./PaxHeaders.82075/openvswitch-2.5.90000644000000000000000000000013213534540120013717 xustar0030 mtime=1567801424.653852483 30 atime=1567801425.621859618 30 ctime=1567801424.653852483 openvswitch-2.5.9/0000755000175000017500000000000013534540120015325 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/PaxHeaders.82075/acinclude.m40000644000000000000000000000013213534540071016035 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.649845081 openvswitch-2.5.9/acinclude.m40000644000175000017500000011025313534540071017525 0ustar00jpettitjpettit00000000000000# -*- autoconf -*- # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. dnl OVS_ENABLE_WERROR AC_DEFUN([OVS_ENABLE_WERROR], [AC_ARG_ENABLE( [Werror], [AC_HELP_STRING([--enable-Werror], [Add -Werror to CFLAGS])], [], [enable_Werror=no]) AC_CONFIG_COMMANDS_PRE( [if test "X$enable_Werror" = Xyes; then OVS_CFLAGS="$OVS_CFLAGS -Werror" fi])]) dnl OVS_CHECK_LINUX dnl dnl Configure linux kernel source tree AC_DEFUN([OVS_CHECK_LINUX], [ AC_ARG_WITH([linux], [AC_HELP_STRING([--with-linux=/path/to/linux], [Specify the Linux kernel build directory])]) AC_ARG_WITH([linux-source], [AC_HELP_STRING([--with-linux-source=/path/to/linux-source], [Specify the Linux kernel source directory (usually figured out automatically from build directory)])]) # Deprecated equivalents to --with-linux, --with-linux-source. AC_ARG_WITH([l26]) AC_ARG_WITH([l26-source]) if test X"$with_linux" != X; then KBUILD=$with_linux elif test X"$with_l26" != X; then KBUILD=$with_l26 AC_MSG_WARN([--with-l26 is deprecated, please use --with-linux instead]) else KBUILD= fi if test X"$KBUILD" != X; then if test X"$with_linux_source" != X; then KSRC=$with_linux_source elif test X"$with_l26_source" != X; then KSRC=$with_l26_source AC_MSG_WARN([--with-l26-source is deprecated, please use --with-linux-source instead]) else KSRC= fi elif test X"$with_linux_source" != X || test X"$with_l26_source" != X; then AC_MSG_ERROR([Linux source directory may not be specified without Linux build directory]) fi if test -n "$KBUILD"; then KBUILD=`eval echo "$KBUILD"` case $KBUILD in /*) ;; *) KBUILD=`pwd`/$KBUILD ;; esac # The build directory is what the user provided. # Make sure that it exists. AC_MSG_CHECKING([for Linux build directory]) if test -d "$KBUILD"; then AC_MSG_RESULT([$KBUILD]) AC_SUBST(KBUILD) else AC_MSG_RESULT([no]) AC_ERROR([source dir $KBUILD doesn't exist]) fi # Debian breaks kernel headers into "source" header and "build" headers. # We want the source headers, but $KBUILD gives us the "build" headers. # Use heuristics to find the source headers. AC_MSG_CHECKING([for Linux source directory]) if test -n "$KSRC"; then KSRC=`eval echo "$KSRC"` case $KSRC in /*) ;; *) KSRC=`pwd`/$KSRC ;; esac if test ! -e $KSRC/include/linux/kernel.h; then AC_MSG_ERROR([$KSRC is not a kernel source directory]) fi else KSRC=$KBUILD if test ! -e $KSRC/include/linux/kernel.h; then # Debian kernel build Makefiles tend to include a line of the form: # MAKEARGS := -C /usr/src/linux-headers-3.2.0-1-common O=/usr/src/linux-headers-3.2.0-1-486 # First try to extract the source directory from this line. KSRC=`sed -n 's/.*-C \([[^ ]]*\).*/\1/p' "$KBUILD"/Makefile` if test ! -e "$KSRC"/include/linux/kernel.h; then # Didn't work. Fall back to name-based heuristics that used to work. case `echo "$KBUILD" | sed 's,/*$,,'` in # ( */build) KSRC=`echo "$KBUILD" | sed 's,/build/*$,/source,'` ;; # ( *) KSRC=`(cd $KBUILD && pwd -P) | sed 's,-[[^-]]*$,-common,'` ;; esac fi fi if test ! -e "$KSRC"/include/linux/kernel.h; then AC_MSG_ERROR([cannot find source directory (please use --with-linux-source)]) fi fi AC_MSG_RESULT([$KSRC]) AC_MSG_CHECKING([for kernel version]) version=`sed -n 's/^VERSION = //p' "$KSRC/Makefile"` patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$KSRC/Makefile"` sublevel=`sed -n 's/^SUBLEVEL = //p' "$KSRC/Makefile"` if test X"$version" = X || test X"$patchlevel" = X; then AC_ERROR([cannot determine kernel version]) elif test X"$sublevel" = X; then kversion=$version.$patchlevel else kversion=$version.$patchlevel.$sublevel fi AC_MSG_RESULT([$kversion]) if test "$version" -ge 4; then if test "$version" = 4 && test "$patchlevel" -le 3; then : # Linux 4.x else AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version newer than 4.3.x is not supported (please refer to the FAQ for advice)]) fi elif test "$version" = 3; then : # Linux 3.x else if test "$version" -le 1 || test "$patchlevel" -le 5 || test "$sublevel" -le 31; then AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version 2.6.32 or later is required]) else : # Linux 2.6.x fi fi if (test ! -e "$KBUILD"/include/linux/version.h && \ test ! -e "$KBUILD"/include/generated/uapi/linux/version.h)|| \ (test ! -e "$KBUILD"/include/linux/autoconf.h && \ test ! -e "$KBUILD"/include/generated/autoconf.h); then AC_MSG_ERROR([Linux kernel source in $KBUILD is not configured]) fi OVS_CHECK_LINUX_COMPAT fi AM_CONDITIONAL(LINUX_ENABLED, test -n "$KBUILD") ]) dnl OVS_CHECK_DPDK dnl dnl Configure DPDK source tree AC_DEFUN([OVS_CHECK_DPDK], [ AC_ARG_WITH([dpdk], [AC_HELP_STRING([--with-dpdk=/path/to/dpdk], [Specify the DPDK build directory])]) if test X"$with_dpdk" != X; then RTE_SDK=$with_dpdk DPDK_INCLUDE=$RTE_SDK/include DPDK_LIB_DIR=$RTE_SDK/lib DPDK_LIB="-ldpdk" DPDK_EXTRA_LIB="" RTE_SDK_FULL=`readlink -f $RTE_SDK` AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([#include <$RTE_SDK_FULL/include/rte_config.h> #if !RTE_LIBRTE_VHOST_USER #error #endif], [])], [], [AC_DEFINE([VHOST_CUSE], [1], [DPDK vhost-cuse support enabled, vhost-user disabled.]) DPDK_EXTRA_LIB="-lfuse"]) ovs_save_CFLAGS="$CFLAGS" ovs_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -L$DPDK_LIB_DIR" CFLAGS="$CFLAGS -I$DPDK_INCLUDE" # On some systems we have to add -ldl to link with dpdk # # This code, at first, tries to link without -ldl (""), # then adds it and tries again. # Before each attempt the search cache must be unset, # otherwise autoconf will stick with the old result found=false save_LIBS=$LIBS for extras in "" "-ldl"; do LIBS="$DPDK_LIB $extras $save_LIBS $DPDK_EXTRA_LIB" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include #include ], [int rte_argc; char ** rte_argv; rte_eal_init(rte_argc, rte_argv);])], [found=true]) if $found; then break fi done if $found; then :; else AC_MSG_ERROR([cannot link with dpdk]) fi CFLAGS="$ovs_save_CFLAGS" LDFLAGS="$ovs_save_LDFLAGS" OVS_LDFLAGS="$OVS_LDFLAGS -L$DPDK_LIB_DIR" OVS_CFLAGS="$OVS_CFLAGS -I$DPDK_INCLUDE" OVS_ENABLE_OPTION([-mssse3]) # DPDK pmd drivers are not linked unless --whole-archive is used. # # This happens because the rest of the DPDK code doesn't use any symbol in # the pmd driver objects, and the drivers register themselves using an # __attribute__((constructor)) function. # # These options are specified inside a single -Wl directive to prevent # autotools from reordering them. DPDK_vswitchd_LDFLAGS=-Wl,--whole-archive,$DPDK_LIB,--no-whole-archive AC_SUBST([DPDK_vswitchd_LDFLAGS]) AC_DEFINE([DPDK_NETDEV], [1], [System uses the DPDK module.]) else RTE_SDK= fi AM_CONDITIONAL([DPDK_NETDEV], test -n "$RTE_SDK") ]) dnl OVS_GREP_IFELSE(FILE, REGEX, [IF-MATCH], [IF-NO-MATCH]) dnl dnl Greps FILE for REGEX. If it matches, runs IF-MATCH, otherwise IF-NO-MATCH. dnl If IF-MATCH is empty then it defines to OVS_DEFINE(HAVE_), with dnl translated to uppercase. AC_DEFUN([OVS_GREP_IFELSE], [ AC_MSG_CHECKING([whether $2 matches in $1]) if test -f $1; then grep '$2' $1 >/dev/null 2>&1 status=$? case $status in 0) AC_MSG_RESULT([yes]) m4_if([$3], [], [OVS_DEFINE([HAVE_]m4_toupper([$2]))], [$3]) ;; 1) AC_MSG_RESULT([no]) $4 ;; *) AC_MSG_ERROR([grep exited with status $status]) ;; esac else AC_MSG_RESULT([file not found]) $4 fi ]) dnl OVS_FIND_FIELD_IFELSE(FILE, STRUCTURE, REGEX, [IF-MATCH], [IF-NO-MATCH]) dnl dnl Looks for STRUCTURE in FILE. If it is found, greps for REGEX within the dnl structure definition. If this is successful, runs IF-MATCH, otherwise dnl IF_NO_MATCH. If IF-MATCH is empty then it defines to dnl OVS_DEFINE(HAVE__WITH_), with and dnl translated to uppercase. AC_DEFUN([OVS_FIND_FIELD_IFELSE], [ AC_MSG_CHECKING([whether $2 has member $3 in $1]) if test -f $1; then awk '/$2.{/,/^}/' $1 2>/dev/null | grep '$3' >/dev/null status=$? case $status in 0) AC_MSG_RESULT([yes]) m4_if([$4], [], [OVS_DEFINE([HAVE_]m4_toupper([$2])[_WITH_]m4_toupper([$3]))], [$4]) ;; 1) AC_MSG_RESULT([no]) $5 ;; *) AC_MSG_ERROR([grep exited with status $status]) ;; esac else AC_MSG_RESULT([file not found]) $5 fi ]) dnl OVS_DEFINE(NAME) dnl dnl Defines NAME to 1 in kcompat.h. AC_DEFUN([OVS_DEFINE], [ echo '#define $1 1' >> datapath/linux/kcompat.h.new ]) dnl OVS_CHECK_LINUX_COMPAT dnl dnl Runs various Autoconf checks on the Linux 2.6 kernel source in dnl the directory in $KBUILD. AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [ rm -f datapath/linux/kcompat.h.new mkdir -p datapath/linux : > datapath/linux/kcompat.h.new echo '#include #ifndef RHEL_RELEASE_CODE #define RHEL_RELEASE_CODE 0 #define RHEL_RELEASE_VERSION(a, b) 0 #endif' >> datapath/linux/kcompat.h.new OVS_GREP_IFELSE([$KSRC/arch/x86/include/asm/checksum_32.h], [src_err,], [OVS_DEFINE([HAVE_CSUM_COPY_DBG])]) OVS_GREP_IFELSE([$KSRC/include/net/addrconf.h], [ipv6_dst_lookup.*net], [OVS_DEFINE([HAVE_IPV6_DST_LOOKUP_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/addrconf.h], [ipv6_stub]) OVS_GREP_IFELSE([$KSRC/include/linux/err.h], [ERR_CAST]) OVS_GREP_IFELSE([$KSRC/include/linux/err.h], [IS_ERR_OR_NULL]) OVS_GREP_IFELSE([$KSRC/include/linux/etherdevice.h], [eth_hw_addr_random]) OVS_GREP_IFELSE([$KSRC/include/linux/etherdevice.h], [ether_addr_copy]) OVS_GREP_IFELSE([$KSRC/include/uapi/linux/if_link.h], [IFLA_GENEVE_TOS]) OVS_GREP_IFELSE([$KSRC/include/uapi/linux/if_link.h], [rtnl_link_stats64]) OVS_GREP_IFELSE([$KSRC/include/linux/if_link.h], [rtnl_link_stats64]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_set_encap_proto]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_hwaccel_push_inside]) OVS_GREP_IFELSE([$KSRC/include/linux/in.h], [ipv4_is_multicast]) OVS_GREP_IFELSE([$KSRC/include/linux/in.h], [proto_ports_offset]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [__ip_select_ident.*dst_entry], [OVS_DEFINE([HAVE_IP_SELECT_IDENT_USING_DST_ENTRY])]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [__ip_select_ident.*net], [OVS_DEFINE([HAVE_IP_SELECT_IDENT_USING_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [inet_get_local_port_range.*net], [OVS_DEFINE([HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_is_fragment]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_do_fragment.*net], [OVS_DEFINE([HAVE_IP_DO_FRAGMENT_TAKES_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_skb_dst_mtu]) OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [IPSKB_FRAG_PMTU], [OVS_DEFINE([HAVE_CORRECT_MRU_HANDLING])]) OVS_GREP_IFELSE([$KSRC/include/net/ip_tunnels.h], [__ip_tunnel_change_mtu]) OVS_GREP_IFELSE([$KSRC/include/net/inet_frag.h], [hashfn.*const], [OVS_DEFINE([HAVE_INET_FRAGS_CONST])]) OVS_GREP_IFELSE([$KSRC/include/net/inet_frag.h], [last_in], [OVS_DEFINE([HAVE_INET_FRAGS_LAST_IN])]) OVS_GREP_IFELSE([$KSRC/include/net/inet_frag.h], [inet_frag_evicting]) OVS_FIND_FIELD_IFELSE([$KSRC/include/net/inet_frag.h], [inet_frags], [frags_work]) OVS_FIND_FIELD_IFELSE([$KSRC/include/net/inet_frag.h], [inet_frags], [rwlock]) OVS_FIND_FIELD_IFELSE([$KSRC/include/net/inet_frag.h], [inet_frag_queue], [list_evictor]) OVS_GREP_IFELSE([$KSRC/include/net/inetpeer.h], [vif], [OVS_DEFINE([HAVE_INETPEER_VIF_SUPPORT])]) OVS_GREP_IFELSE([$KSRC/include/net/dst_metadata.h], [metadata_dst]) OVS_GREP_IFELSE([$KSRC/include/linux/net.h], [sock_create_kern.*net], [OVS_DEFINE([HAVE_SOCK_CREATE_KERN_NET])]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_disable_lro]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_stats]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_stats64]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_get_by_index_rcu]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_recursion_level]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [__skb_gso_segment]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [can_checksum_protocol]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [ndo_get_iflink]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [netdev_features_t]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [pcpu_sw_netstats]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [netdev_rx_handler_register]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [net_device_extended]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [rx_handler_func_t.*pskb], [OVS_DEFINE([HAVE_RX_HANDLER_PSKB])]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [netif_needs_gso.*net_device], [OVS_DEFINE([HAVE_NETIF_NEEDS_GSO_NETDEV])]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [udp_offload]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [udp_offload.*uoff], [OVS_DEFINE([HAVE_UDP_OFFLOAD_ARG_UOFF])]) OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [gro_remcsum]) OVS_GREP_IFELSE([$KSRC/include/linux/netfilter.h], [nf_hook_state]) OVS_GREP_IFELSE([$KSRC/include/linux/netfilter.h], [nf_register_net_hook]) OVS_GREP_IFELSE([$KSRC/include/linux/netfilter.h], [nf_hookfn.*nf_hook_ops], [OVS_DEFINE([HAVE_NF_HOOKFN_ARG_OPS])]) OVS_FIND_FIELD_IFELSE([$KSRC/include/linux/netfilter_ipv6.h], [nf_ipv6_ops], [fragment.*sock], [OVS_DEFINE([HAVE_NF_IPV6_OPS_FRAGMENT])]) OVS_GREP_IFELSE([$KSRC/include/net/netfilter/nf_conntrack.h], [tmpl_alloc.*conntrack_zone], [OVS_DEFINE([HAVE_NF_CT_TMPL_ALLOC_TAKES_STRUCT_ZONE])]) OVS_GREP_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_zones.h], [nf_ct_zone_init]) OVS_GREP_IFELSE([$KSRC/include/net/netfilter/nf_conntrack_labels.h], [nf_connlabels_get]) OVS_GREP_IFELSE([$KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h], [nf_ct_frag6_consume_orig]) OVS_GREP_IFELSE([$KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h], [nf_ct_frag6_output]) OVS_GREP_IFELSE([$KSRC/include/linux/random.h], [prandom_u32]) OVS_GREP_IFELSE([$KSRC/include/linux/random.h], [prandom_u32_max]) OVS_GREP_IFELSE([$KSRC/include/net/rtnetlink.h], [get_link_net]) OVS_GREP_IFELSE([$KSRC/include/net/rtnetlink.h], [name_assign_type]) OVS_GREP_IFELSE([$KSRC/include/net/rtnetlink.h], [rtnl_create_link.*src_net], [OVS_DEFINE([HAVE_RTNL_CREATE_LINK_SRC_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/net_namespace.h], [possible_net_t]) OVS_GREP_IFELSE([$KSRC/include/linux/rcupdate.h], [rcu_read_lock_held], [], [OVS_GREP_IFELSE([$KSRC/include/linux/rtnetlink.h], [rcu_read_lock_held])]) OVS_GREP_IFELSE([$KSRC/include/linux/rtnetlink.h], [lockdep_rtnl_is_held]) # Check for the proto_data_valid member in struct sk_buff. The [^@] # is necessary because some versions of this header remove the # member but retain the kerneldoc comment that describes it (which # starts with @). The brackets must be doubled because of m4 # quoting rules. OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [[[^@]]proto_data_valid], [OVS_DEFINE([HAVE_PROTO_DATA_VALID])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_checksum_start_offset]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [inner_protocol]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [inner_mac_header]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [inner_network_header]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [kfree_skb_list]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [rxhash]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [u16.*rxhash], [OVS_DEFINE([HAVE_U16_RXHASH])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_dst(], [OVS_DEFINE([HAVE_SKB_DST_ACCESSOR_FUNCS])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_copy_from_linear_data_offset]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_reset_tail_pointer]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_cow_head]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_transport_header], [OVS_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])]) OVS_GREP_IFELSE([$KSRC/include/linux/icmpv6.h], [icmp6_hdr], [OVS_DEFINE([HAVE_ICMP6_HDR])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_warn_if_lro], [OVS_DEFINE([HAVE_SKB_WARN_LRO])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_frag_page]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_has_frag_list]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [__skb_fill_page_desc]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_reset_mac_len]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_unclone]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_orphan_frags]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_get_hash(], [OVS_DEFINE([HAVE_SKB_GET_HASH])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_clear_hash]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [int.skb_zerocopy(], [OVS_DEFINE([HAVE_SKB_ZEROCOPY])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [u8.*l4_rxhash], [OVS_DEFINE([HAVE_L4_RXHASH])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_ensure_writable]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_pop]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_push]) OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [bool], [OVS_DEFINE([HAVE_BOOL_TYPE])]) OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [__wsum], [OVS_DEFINE([HAVE_CSUM_TYPES])]) OVS_GREP_IFELSE([$KSRC/include/uapi/linux/types.h], [__wsum], [OVS_DEFINE([HAVE_CSUM_TYPES])]) OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_replace4]) OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_unfold]) OVS_GREP_IFELSE([$KSRC/include/net/dst.h], [dst_discard_sk]) OVS_GREP_IFELSE([$KSRC/include/net/dst.h], [__skb_dst_copy]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [genl_has_listeners]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [mcgrp_offset]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [parallel_ops]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [genlmsg_new_unicast]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [netlink_has_listeners(net->genl_sock], [OVS_DEFINE([HAVE_GENL_HAS_LISTENERS_TAKES_NET])]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [genlmsg_parse]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [genl_notify.*family], [OVS_DEFINE([HAVE_GENL_NOTIFY_TAKES_FAMILY])]) OVS_FIND_FIELD_IFELSE([$KSRC/include/net/genetlink.h], [genl_multicast_group], [id]) OVS_GREP_IFELSE([$KSRC/include/net/geneve.h], [geneve_hdr]) OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_cisco_register]) OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_handle_offloads]) OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [IP6_FH_F_SKIP_RH]) OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [ip6_local_out_sk]) OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [__ipv6_addr_jhash]) OVS_GREP_IFELSE([$KSRC/include/net/ip6_fib.h], [rt6i.*u.dst], [OVS_DEFINE([HAVE_RT6INFO_DST_UNION])]) OVS_GREP_IFELSE([$KSRC/include/net/ip6_route.h], [ip6_frag.*sock], [OVS_DEFINE([HAVE_IP_FRAGMENT_TAKES_SOCK])]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be32]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be64]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_in_addr]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_find_nested]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_is_last]) OVS_GREP_IFELSE([$KSRC/include/linux/netlink.h], [void.*netlink_set_err], [OVS_DEFINE([HAVE_VOID_NETLINK_SET_ERR])]) OVS_GREP_IFELSE([$KSRC/include/net/sctp/checksum.h], [sctp_compute_cksum]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [ADD_ALL_VLANS_CMD], [OVS_DEFINE([HAVE_VLAN_BUG_WORKAROUND])]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_insert_tag_set_proto]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [__vlan_insert_tag]) OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_get_protocol]) OVS_GREP_IFELSE([$KSRC/include/linux/u64_stats_sync.h], [u64_stats_fetch_begin_irq]) OVS_GREP_IFELSE([$KSRC/include/linux/openvswitch.h], [openvswitch_handle_frame_hook], [OVS_DEFINE([HAVE_RHEL_OVS_HOOK])]) OVS_GREP_IFELSE([$KSRC/include/net/vxlan.h], [struct vxlan_metadata], [OVS_DEFINE([HAVE_VXLAN_METADATA])]) OVS_GREP_IFELSE([$KSRC/include/net/vxlan.h], [VXLAN_HF_RCO]) OVS_GREP_IFELSE([$KSRC/include/net/udp.h], [udp_flow_src_port], [OVS_GREP_IFELSE([$KSRC/include/net/udp.h], [inet_get_local_port_range(net], [OVS_DEFINE([HAVE_UDP_FLOW_SRC_PORT])])]) OVS_GREP_IFELSE([$KSRC/include/net/udp.h], [udp_v4_check]) OVS_GREP_IFELSE([$KSRC/include/net/udp_tunnel.h], [udp_tunnel_gro_complete]) OVS_GREP_IFELSE([$KSRC/include/net/udp_tunnel.h], [ipv6_v6only], [OVS_DEFINE([HAVE_UDP_TUNNEL_IPV6])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [ignore_df], [OVS_DEFINE([HAVE_IGNORE_DF_RENAME])]) OVS_GREP_IFELSE([$KSRC/include/uapi/linux/netdevice.h], [NET_NAME_UNKNOWN], [OVS_DEFINE([HAVE_NET_NAME_UNKNOWN])]) OVS_GREP_IFELSE([$KSRC/include/linux/utsrelease.h], [el6], [OVS_DEFINE([HAVE_RHEL6_PER_CPU])]) dnl Conntrack support, and therefore, IP fragment handling backport, should dnl only be enabled on kernels 3.10+. In future when OVS drops support for dnl kernels older than 3.10, this macro could be removed from the codebase. if test "$version" = 4; then OVS_DEFINE([OVS_FRAGMENT_BACKPORT]) elif test "$version" = 3 && test "$patchlevel" -ge 10; then OVS_DEFINE([OVS_FRAGMENT_BACKPORT]) fi if cmp -s datapath/linux/kcompat.h.new \ datapath/linux/kcompat.h >/dev/null 2>&1; then rm datapath/linux/kcompat.h.new else mv datapath/linux/kcompat.h.new datapath/linux/kcompat.h fi ]) dnl Checks for net/if_packet.h. AC_DEFUN([OVS_CHECK_IF_PACKET], [AC_CHECK_HEADER([net/if_packet.h], [HAVE_IF_PACKET=yes], [HAVE_IF_PACKET=no]) AM_CONDITIONAL([HAVE_IF_PACKET], [test "$HAVE_IF_PACKET" = yes]) if test "$HAVE_IF_PACKET" = yes; then AC_DEFINE([HAVE_IF_PACKET], [1], [Define to 1 if net/if_packet.h is available.]) fi]) dnl Checks for net/if_dl.h. dnl dnl (We use this as a proxy for checking whether we're building on FreeBSD dnl or NetBSD.) AC_DEFUN([OVS_CHECK_IF_DL], [AC_CHECK_HEADER([net/if_dl.h], [HAVE_IF_DL=yes], [HAVE_IF_DL=no]) AM_CONDITIONAL([HAVE_IF_DL], [test "$HAVE_IF_DL" = yes]) if test "$HAVE_IF_DL" = yes; then AC_DEFINE([HAVE_IF_DL], [1], [Define to 1 if net/if_dl.h is available.]) # On these platforms we use libpcap to access network devices. AC_SEARCH_LIBS([pcap_open_live], [pcap]) fi]) dnl Checks for buggy strtok_r. dnl dnl Some versions of glibc 2.7 has a bug in strtok_r when compiling dnl with optimization that can cause segfaults: dnl dnl http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614. AC_DEFUN([OVS_CHECK_STRTOK_R], [AC_CACHE_CHECK( [whether strtok_r macro segfaults on some inputs], [ovs_cv_strtok_r_bug], [AC_RUN_IFELSE( [AC_LANG_PROGRAM([#include #include ], [[#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 /* Assume bug is present, because relatively minor changes in compiler settings (e.g. optimization level) can make it crop up. */ return 1; #else char string[] = ":::"; char *save_ptr = (char *) 0xc0ffee; char *token1, *token2; token1 = strtok_r(string, ":", &save_ptr); token2 = strtok_r(NULL, ":", &save_ptr); freopen ("/dev/null", "w", stdout); printf ("%s %s\n", token1, token2); return 0; #endif ]])], [ovs_cv_strtok_r_bug=no], [ovs_cv_strtok_r_bug=yes], [ovs_cv_strtok_r_bug=yes])]) if test $ovs_cv_strtok_r_bug = yes; then AC_DEFINE([HAVE_STRTOK_R_BUG], [1], [Define if strtok_r macro segfaults on some inputs]) fi ]) dnl ---------------------------------------------------------------------- dnl These macros are from GNU PSPP, with the following original license: dnl Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_OVS_CHECK_CC_OPTION], [dnl m4_define([ovs_cv_name], [ovs_cv_[]m4_translit([$1], [-=], [__])])dnl AC_CACHE_CHECK([whether $CC accepts $1], [ovs_cv_name], [ovs_save_CFLAGS="$CFLAGS" dnl Include -Werror in the compiler options, because without -Werror dnl clang's GCC-compatible compiler driver does not return a failure dnl exit status even though it complains about options it does not dnl understand. dnl dnl Also, check stderr as gcc exits with status 0 for options dnl rejected at getopt level. dnl % touch /tmp/a.c dnl % gcc -g -c -Werror -Qunused-arguments /tmp/a.c; echo $? dnl gcc: unrecognized option '-Qunused-arguments' dnl 0 dnl % CFLAGS="$CFLAGS $WERROR $1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,)], [if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv_name[]=no; else ovs_cv_name[]=yes; fi], [ovs_cv_name[]=no]) CFLAGS="$ovs_save_CFLAGS"]) if test $ovs_cv_name = yes; then m4_if([$2], [], [:], [$2]) else m4_if([$3], [], [:], [$3]) fi ]) dnl OVS_CHECK_WERROR dnl dnl Check whether the C compiler accepts -Werror. dnl Sets $WERROR to "-Werror", if so, and otherwise to the empty string. AC_DEFUN([OVS_CHECK_WERROR], [WERROR= _OVS_CHECK_CC_OPTION([-Werror], [WERROR=-Werror])]) dnl OVS_CHECK_CC_OPTION([OPTION], [ACTION-IF-ACCEPTED], [ACTION-IF-REJECTED]) dnl Check whether the given C compiler OPTION is accepted. dnl If so, execute ACTION-IF-ACCEPTED, otherwise ACTION-IF-REJECTED. AC_DEFUN([OVS_CHECK_CC_OPTION], [AC_REQUIRE([OVS_CHECK_WERROR]) _OVS_CHECK_CC_OPTION([$1], [$2], [$3])]) dnl OVS_ENABLE_OPTION([OPTION]) dnl Check whether the given C compiler OPTION is accepted. dnl If so, add it to WARNING_FLAGS. dnl Example: OVS_ENABLE_OPTION([-Wdeclaration-after-statement]) AC_DEFUN([OVS_ENABLE_OPTION], [OVS_CHECK_CC_OPTION([$1], [WARNING_FLAGS="$WARNING_FLAGS $1"]) AC_SUBST([WARNING_FLAGS])]) dnl OVS_CONDITIONAL_CC_OPTION([OPTION], [CONDITIONAL]) dnl Check whether the given C compiler OPTION is accepted. dnl If so, enable the given Automake CONDITIONAL. dnl Example: OVS_CONDITIONAL_CC_OPTION([-Wno-unused], [HAVE_WNO_UNUSED]) AC_DEFUN([OVS_CONDITIONAL_CC_OPTION], [OVS_CHECK_CC_OPTION( [$1], [ovs_have_cc_option=yes], [ovs_have_cc_option=no]) AM_CONDITIONAL([$2], [test $ovs_have_cc_option = yes])]) dnl ---------------------------------------------------------------------- dnl Check for too-old XenServer. AC_DEFUN([OVS_CHECK_XENSERVER_VERSION], [AC_CACHE_CHECK([XenServer release], [ovs_cv_xsversion], [if test -e /etc/redhat-release; then ovs_cv_xsversion=`sed -n 's/^XenServer DDK release \([[^-]]*\)-.*/\1/p' /etc/redhat-release` fi if test -z "$ovs_cv_xsversion"; then ovs_cv_xsversion=none fi]) case $ovs_cv_xsversion in none) ;; [[1-9]][[0-9]]* | dnl XenServer 10 or later [[6-9]]* | dnl XenServer 6 or later 5.[[7-9]]* | dnl XenServer 5.7 or later 5.6.[[1-9]][[0-9]][[0-9]][[0-9]]* | dnl XenServer 5.6.1000 or later 5.6.[[2-9]][[0-9]][[0-9]]* | dnl XenServer 5.6.200 or later 5.6.1[[0-9]][[0-9]]) dnl Xenserver 5.6.100 or later ;; *) AC_MSG_ERROR([This appears to be XenServer $ovs_cv_xsversion, but only XenServer 5.6.100 or later is supported. (If you are really using a supported version of XenServer, you may override this error message by specifying 'ovs_cv_xsversion=5.6.100' on the "configure" command line.)]) ;; esac]) dnl OVS_MAKE_HAS_IF([if-true], [if-false]) dnl dnl Checks whether make has the GNU make $(if condition,then,else) extension. dnl Runs 'if-true' if so, 'if-false' otherwise. AC_DEFUN([OVS_CHECK_MAKE_IF], [AC_CACHE_CHECK( [whether ${MAKE-make} has GNU make \$(if) extension], [ovs_cv_gnu_make_if], [cat <<'EOF' > conftest.mk conftest.out: echo $(if x,y,z) > conftest.out .PHONY: all EOF rm -f conftest.out AS_ECHO(["$as_me:$LINENO: invoking ${MAKE-make} -f conftest.mk all:"]) >&AS_MESSAGE_LOG_FD 2>&1 ${MAKE-make} -f conftest.mk conftest.out >&AS_MESSAGE_LOG_FD 2>&1 AS_ECHO(["$as_me:$LINENO: conftest.out contains:"]) >&AS_MESSAGE_LOG_FD 2>&1 cat conftest.out >&AS_MESSAGE_LOG_FD 2>&1 result=`cat conftest.out` rm -f conftest.mk conftest.out if test "X$result" = "Xy"; then ovs_cv_gnu_make_if=yes else ovs_cv_gnu_make_if=no fi])]) dnl OVS_CHECK_GNU_MAKE dnl dnl Checks whether make is GNU make (because Linux kernel Makefiles dnl only work with GNU make). AC_DEFUN([OVS_CHECK_GNU_MAKE], [AC_CACHE_CHECK( [whether ${MAKE-make} is GNU make], [ovs_cv_gnu_make], [rm -f conftest.out AS_ECHO(["$as_me:$LINENO: invoking ${MAKE-make} --version:"]) >&AS_MESSAGE_LOG_FD 2>&1 ${MAKE-make} --version >conftest.out 2>&1 cat conftest.out >&AS_MESSAGE_LOG_FD 2>&1 result=`cat conftest.out` rm -f conftest.mk conftest.out case $result in # ( GNU*) ovs_cv_gnu_make=yes ;; # ( *) ovs_cv_gnu_make=no ;; esac]) AM_CONDITIONAL([GNU_MAKE], [test $ovs_cv_gnu_make = yes])]) dnl OVS_CHECK_SPARSE_TARGET dnl dnl The "cgcc" script from "sparse" isn't very good at detecting the dnl target for which the code is being built. This helps it out. AC_DEFUN([OVS_CHECK_SPARSE_TARGET], [AC_CACHE_CHECK( [target hint for cgcc], [ac_cv_sparse_target], [AS_CASE([`$CC -dumpmachine 2>/dev/null`], [i?86-* | athlon-*], [ac_cv_sparse_target=x86], [x86_64-*], [ac_cv_sparse_target=x86_64], [ac_cv_sparse_target=other])]) AS_CASE([$ac_cv_sparse_target], [x86], [SPARSEFLAGS= CGCCFLAGS=-target=i86], [x86_64], [SPARSEFLAGS=-m64 CGCCFLAGS=-target=x86_64], [SPARSEFLAGS= CGCCFLAGS=]) AC_SUBST([SPARSEFLAGS]) AC_SUBST([CGCCFLAGS])]) dnl OVS_SPARSE_EXTRA_INCLUDES dnl dnl The cgcc script from "sparse" does not search gcc's default dnl search path. Get the default search path from GCC and pass dnl them to sparse. AC_DEFUN([OVS_SPARSE_EXTRA_INCLUDES], AC_SUBST([SPARSE_EXTRA_INCLUDES], [`$CC -v -E - &1 >/dev/null | sed -n -e '/^#include.*search.*starts.*here:/,/^End.*of.*search.*list\./s/^ \(.*\)/-I \1/p' |grep -v /usr/lib | grep -x -v '\-I /usr/include' | tr \\\n ' ' `] )) dnl OVS_ENABLE_SPARSE AC_DEFUN([OVS_ENABLE_SPARSE], [AC_REQUIRE([OVS_CHECK_SPARSE_TARGET]) AC_REQUIRE([OVS_CHECK_MAKE_IF]) AC_REQUIRE([OVS_SPARSE_EXTRA_INCLUDES]) : ${SPARSE=sparse} AC_SUBST([SPARSE]) AC_CONFIG_COMMANDS_PRE( [if test $ovs_cv_gnu_make_if = yes; then CC='$(if $(C),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')' fi])]) dnl OVS_PTHREAD_SET_NAME dnl dnl This checks for three known variants of pthreads functions for setting dnl the name of the current thread: dnl dnl glibc: int pthread_setname_np(pthread_t, const char *name); dnl NetBSD: int pthread_setname_np(pthread_t, const char *format, void *arg); dnl FreeBSD: int pthread_set_name_np(pthread_t, const char *name); dnl dnl For glibc and FreeBSD, the arguments are just a thread and its name. For dnl NetBSD, 'format' is a printf() format string and 'arg' is an argument to dnl provide to it. dnl dnl This macro defines: dnl dnl glibc: HAVE_GLIBC_PTHREAD_SETNAME_NP dnl NetBSD: HAVE_NETBSD_PTHREAD_SETNAME_NP dnl FreeBSD: HAVE_PTHREAD_SET_NAME_NP AC_DEFUN([OVS_CHECK_PTHREAD_SET_NAME], [AC_CHECK_FUNCS([pthread_set_name_np]) if test $ac_cv_func_pthread_set_name_np != yes; then AC_CACHE_CHECK( [for pthread_setname_np() variant], [ovs_cv_pthread_setname_np], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [pthread_setname_np(pthread_self(), "name");])], [ovs_cv_pthread_setname_np=glibc], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [pthread_setname_np(pthread_self(), "%s", "name");])], [ovs_cv_pthread_setname_np=netbsd], [ovs_cv_pthread_setname_np=none])])]) case $ovs_cv_pthread_setname_np in # ( glibc) AC_DEFINE( [HAVE_GLIBC_PTHREAD_SETNAME_NP], [1], [Define to 1 if pthread_setname_np() is available and takes 2 parameters (like glibc).]) ;; # ( netbsd) AC_DEFINE( [HAVE_NETBSD_PTHREAD_SETNAME_NP], [1], [Define to 1 if pthread_setname_np() is available and takes 3 parameters (like NetBSD).]) ;; esac fi]) dnl OVS_CHECK_LINUX_HOST. dnl dnl Checks whether we're building for a Linux host, based on the presence of dnl the __linux__ preprocessor symbol, and sets up an Automake conditional dnl LINUX based on the result. AC_DEFUN([OVS_CHECK_LINUX_HOST], [AC_CACHE_CHECK( [whether __linux__ is defined], [ovs_cv_linux], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([enum { LINUX = __linux__};], [])], [ovs_cv_linux=true], [ovs_cv_linux=false])]) AM_CONDITIONAL([LINUX], [$ovs_cv_linux])]) openvswitch-2.5.9/PaxHeaders.82075/tutorial0000644000000000000000000000013213534540120015425 xustar0030 mtime=1567801424.621852246 30 atime=1567801425.621859618 30 ctime=1567801424.621852246 openvswitch-2.5.9/tutorial/0000755000175000017500000000000013534540120017170 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-stage40000644000000000000000000000013213534540071017062 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.297849858 openvswitch-2.5.9/tutorial/t-stage40000755000175000017500000000070013534540071020550 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1" ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=2 actions=strip_vlan,2 table=4 reg0=3 actions=strip_vlan,3 table=4 reg0=4 actions=strip_vlan,4 EOF ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2 table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4 table=4 reg0=0 priority=50 actions=1 EOF openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-stage10000644000000000000000000000013213534540071017057 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.293849828 openvswitch-2.5.9/tutorial/t-stage10000755000175000017500000000071513534540071020553 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 "table=1, priority=0, actions=drop" ovs-ofctl add-flow br0 \ "table=1, priority=99, in_port=1, actions=resubmit(,2)" ovs-ofctl add-flows br0 - <<'EOF' table=1, priority=99, in_port=2, vlan_tci=0, actions=mod_vlan_vid:20, resubmit(,2) table=1, priority=99, in_port=3, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) table=1, priority=99, in_port=4, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) EOF openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-stage00000644000000000000000000000013213534540071017056 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.293849828 openvswitch-2.5.9/tutorial/t-stage00000755000175000017500000000042713534540071020552 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 \ "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop" ovs-ofctl add-flow br0 \ "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop" ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)" openvswitch-2.5.9/tutorial/PaxHeaders.82075/ovn0000644000000000000000000000013213534540117016235 xustar0030 mtime=1567801423.625844904 30 atime=1567801425.621859618 30 ctime=1567801423.625844904 openvswitch-2.5.9/tutorial/ovn/0000755000175000017500000000000013534540117020000 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env10000644000000000000000000000013213534540120017100 xustar0030 mtime=1567801424.305849917 30 atime=1567801425.621859618 30 ctime=1567801424.305849917 openvswitch-2.5.9/tutorial/ovn/env1/0000755000175000017500000000000013534540120020643 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env1/PaxHeaders.82075/packet1.sh0000644000000000000000000000013213534540071021046 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.301849888 openvswitch-2.5.9/tutorial/ovn/env1/packet1.sh0000755000175000017500000000132513534540071022540 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # Trace a packet from sw0-port1 to sw0-port2. ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate openvswitch-2.5.9/tutorial/ovn/env1/PaxHeaders.82075/add-third-port.sh0000644000000000000000000000013213534540071022340 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.305849917 openvswitch-2.5.9/tutorial/ovn/env1/add-third-port.sh0000755000175000017500000000146413534540071024036 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovn-nbctl lport-add sw0 sw0-port3 ovn-nbctl lport-set-addresses sw0-port3 00:00:00:00:00:03 ovn-nbctl lport-set-port-security sw0-port3 00:00:00:00:00:03 ovs-vsctl add-port br-int lport3 -- set Interface lport3 external_ids:iface-id=sw0-port3 openvswitch-2.5.9/tutorial/ovn/env1/PaxHeaders.82075/packet2.sh0000644000000000000000000000013213534540071021047 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.301849888 openvswitch-2.5.9/tutorial/ovn/env1/packet2.sh0000755000175000017500000000132113534540071022535 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # Trace a broadcast packet from sw0-port1 ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=ff:ff:ff:ff:ff:ff -generate openvswitch-2.5.9/tutorial/ovn/env1/PaxHeaders.82075/setup.sh0000644000000000000000000000013213534540071020656 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.301849888 openvswitch-2.5.9/tutorial/ovn/env1/setup.sh0000755000175000017500000000347213534540071022355 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # See "Simple two-port setup" in tutorial/OVN-Tutorial.md. # set -o xtrace # Create a logical switch named "sw0" ovn-nbctl lswitch-add sw0 # Create two logical ports on "sw0". ovn-nbctl lport-add sw0 sw0-port1 ovn-nbctl lport-add sw0 sw0-port2 # Set a MAC address for each of the two logical ports. ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02 # Set up port security for the two logical ports. This ensures that # the logical port mac address we have configured is the only allowed # source and destination mac address for these ports. ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02 # Create ports on the local OVS bridge, br-int. When ovn-controller # sees these ports show up with an "iface-id" that matches the OVN # logical port names, it associates these local ports with the OVN # logical ports. ovn-controller will then set up the flows necessary # for these ports to be able to communicate each other as defined by # the OVN logical topology. ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2 openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env50000644000000000000000000000013213534540120017104 xustar0030 mtime=1567801424.325850064 30 atime=1567801425.621859618 30 ctime=1567801424.325850064 openvswitch-2.5.9/tutorial/ovn/env5/0000755000175000017500000000000013534540120020647 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env5/PaxHeaders.82075/packet1.sh0000644000000000000000000000013213534540071021052 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.325850064 openvswitch-2.5.9/tutorial/ovn/env5/packet1.sh0000755000175000017500000000124713534540071022547 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=5,dl_src=00:00:00:00:00:05,dl_dst=00:00:00:00:00:06 -generate openvswitch-2.5.9/tutorial/ovn/env5/PaxHeaders.82075/packet2.sh0000644000000000000000000000013213534540071021053 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.325850064 openvswitch-2.5.9/tutorial/ovn/env5/packet2.sh0000755000175000017500000000126313534540071022546 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:07,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=101 -generate openvswitch-2.5.9/tutorial/ovn/env5/PaxHeaders.82075/setup.sh0000644000000000000000000000013213534540071020662 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.325850064 openvswitch-2.5.9/tutorial/ovn/env5/setup.sh0000755000175000017500000000521113534540071022352 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script simulates 2 chassis connected to a physical switch, # which we call "physnet1". We have two logical ports, one on each hypervisor, # that OVN will connect to physnet1. # # The way to accomplish this in OVN is to create a logical switch for each # logical port. In addition to the normal logical port, each logical switch # has a special "localnet" port, which represents the connection to physnet1. # # In this setup we see the view of this environment from one of the hypervisors. set -o xtrace ovs-vsctl add-br br-eth1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet1:br-eth1 ovn-sbctl chassis-add fakechassis geneve 127.0.0.1 for n in 1 2 3 4 5 6 7 8; do if [ $n -gt 4 ] ; then lswitch_name="provnet1-$n-101" lport_name="$lswitch_name-port1" else lswitch_name="provnet1-$n" fi ovn-nbctl lswitch-add $lswitch_name lport_name="$lswitch_name-port1" ovn-nbctl lport-add $lswitch_name $lport_name ovn-nbctl lport-set-addresses $lport_name 00:00:00:00:00:0$n ovn-nbctl lport-set-port-security $lport_name 00:00:00:00:00:0$n if [ $n -gt 4 ] ; then lport_name="provnet1-$n-physnet1-101" ovn-nbctl lport-add $lswitch_name $lport_name "" 101 else lport_name="provnet1-$n-physnet1" ovn-nbctl lport-add $lswitch_name $lport_name fi ovn-nbctl lport-set-addresses $lport_name unknown ovn-nbctl lport-set-type $lport_name localnet ovn-nbctl lport-set-options $lport_name network_name=physnet1 done ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=provnet1-1-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=provnet1-2-port1 ovs-vsctl add-port br-int lport5 -- set Interface lport5 external_ids:iface-id=provnet1-5-101-port1 ovs-vsctl add-port br-int lport6 -- set Interface lport6 external_ids:iface-id=provnet1-6-101-port1 ovn-sbctl lport-bind provnet1-3-port1 fakechassis ovn-sbctl lport-bind provnet1-4-port1 fakechassis ovn-sbctl lport-bind provnet1-7-101-port1 fakechassis ovn-sbctl lport-bind provnet1-8-101-port1 fakechassis openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env30000644000000000000000000000013213534540120017102 xustar0030 mtime=1567801424.313849976 30 atime=1567801425.621859618 30 ctime=1567801424.313849976 openvswitch-2.5.9/tutorial/ovn/env3/0000755000175000017500000000000013534540120020645 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env3/PaxHeaders.82075/packet1.sh0000644000000000000000000000013213534540071021050 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.309849946 openvswitch-2.5.9/tutorial/ovn/env3/packet1.sh0000755000175000017500000000132513534540071022542 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # Trace a packet from sw0-port1 to sw0-port3. ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:03 -generate openvswitch-2.5.9/tutorial/ovn/env3/PaxHeaders.82075/packet2.sh0000644000000000000000000000013213534540071021051 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.313849976 openvswitch-2.5.9/tutorial/ovn/env3/packet2.sh0000755000175000017500000000224713534540071022547 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # # This trace simulates a packet arriving over a Geneve tunnel from a remote OVN # chassis. The fields are as follows: # # tun_id - # The logical datapath (or logical switch) ID. In this case, we only # have a single logical switch and its ID is 1. # # tun_metadata0 - # This field holds 2 pieces of metadata. The low 16 bits hold the logical # destination port (1 in this case). The upper 16 bits hold the logical # source port (3 in this case. # ovs-appctl ofproto/trace br-int in_port=3,dl_src=00:00:00:00:00:03,dl_dst=00:00:00:00:00:01,tun_id=1,tun_metadata0=$[1 + $[3 << 16]] -generate openvswitch-2.5.9/tutorial/ovn/env3/PaxHeaders.82075/setup.sh0000644000000000000000000000013213534540071020660 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.309849946 openvswitch-2.5.9/tutorial/ovn/env3/setup.sh0000755000175000017500000000322313534540071022351 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovn-nbctl lswitch-add sw0 ovn-nbctl lport-add sw0 sw0-port1 ovn-nbctl lport-add sw0 sw0-port2 ovn-nbctl lport-add sw0 sw0-port3 ovn-nbctl lport-add sw0 sw0-port4 ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02 ovn-nbctl lport-set-addresses sw0-port3 00:00:00:00:00:03 ovn-nbctl lport-set-addresses sw0-port4 00:00:00:00:00:04 ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02 ovn-nbctl lport-set-port-security sw0-port3 00:00:00:00:00:03 ovn-nbctl lport-set-port-security sw0-port4 00:00:00:00:00:04 # Bind sw0-port1 and sw0-port2 to the local chassis ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2 # Create a fake remote chassis. ovn-sbctl chassis-add fakechassis geneve 127.0.0.1 # Bind sw0-port3 and sw0-port4 to the fake remote chassis. ovn-sbctl lport-bind sw0-port3 fakechassis ovn-sbctl lport-bind sw0-port4 fakechassis openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env40000644000000000000000000000013213534540120017103 xustar0030 mtime=1567801424.321850036 30 atime=1567801425.621859618 30 ctime=1567801424.321850036 openvswitch-2.5.9/tutorial/ovn/env4/0000755000175000017500000000000013534540120020646 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/setup2.sh0000644000000000000000000000013213534540071020743 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.317850005 openvswitch-2.5.9/tutorial/ovn/env4/setup2.sh0000755000175000017500000000362513534540071022442 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script simulates 2 chassis connected to a physical switch, # which we call "physnet1". We have two logical ports, one on each hypervisor, # that OVN will connect to physnet1. # # The way to accomplish this in OVN is to create a logical switch for each # logical port. In addition to the normal logical port, each logical switch # has a special "localnet" port, which represents the connection to physnet1. # # In this setup we see the view of this environment from one of the hypervisors. set -o xtrace ovn-sbctl chassis-add fakechassis geneve 127.0.0.1 for n in 1 2 3 4; do ovn-nbctl lswitch-add provnet1-$n ovn-nbctl lport-add provnet1-$n provnet1-$n-port1 ovn-nbctl lport-set-addresses provnet1-$n-port1 00:00:00:00:00:0$n ovn-nbctl lport-set-port-security provnet1-$n-port1 00:00:00:00:00:0$n ovn-nbctl lport-add provnet1-$n provnet1-$n-physnet1 ovn-nbctl lport-set-addresses provnet1-$n-physnet1 unknown ovn-nbctl lport-set-type provnet1-$n-physnet1 localnet ovn-nbctl lport-set-options provnet1-$n-physnet1 network_name=physnet1 done ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=provnet1-1-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=provnet1-2-port1 ovn-sbctl lport-bind provnet1-3-port1 fakechassis ovn-sbctl lport-bind provnet1-4-port1 fakechassis openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/packet3.sh0000644000000000000000000000013213534540071021053 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.321850036 openvswitch-2.5.9/tutorial/ovn/env4/packet3.sh0000755000175000017500000000124713534540071022550 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=3,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:03 -generate openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/packet1.sh0000644000000000000000000000013213534540071021051 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.317850005 openvswitch-2.5.9/tutorial/ovn/env4/packet1.sh0000755000175000017500000000143613534540071022546 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # input from local vif, lport1 (ofport 3) # destination MAC is lport 2 # expect to go out via localnet port (ofport 1) ovs-appctl ofproto/trace br-int in_port=3,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/packet4.sh0000644000000000000000000000013213534540071021054 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.321850036 openvswitch-2.5.9/tutorial/ovn/env4/packet4.sh0000755000175000017500000000124713534540071022551 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=3,dl_src=00:00:00:00:00:01,dl_dst=ff:ff:ff:ff:ff:ff -generate openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/packet2.sh0000644000000000000000000000013213534540071021052 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.317850005 openvswitch-2.5.9/tutorial/ovn/env4/packet2.sh0000755000175000017500000000140613534540071022544 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace # input from localnet port (ofport 1) # expect to be delivered to local vif, lport2 (ofport 4) ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/packet5.sh0000644000000000000000000000013213534540071021055 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.321850036 openvswitch-2.5.9/tutorial/ovn/env4/packet5.sh0000755000175000017500000000124713534540071022552 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:03,dl_dst=ff:ff:ff:ff:ff:ff -generate openvswitch-2.5.9/tutorial/ovn/env4/PaxHeaders.82075/setup1.sh0000644000000000000000000000013213534540071020742 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.313849976 openvswitch-2.5.9/tutorial/ovn/env4/setup1.sh0000755000175000017500000000124213534540071022432 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-vsctl add-br br-eth1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet1:br-eth1 openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env20000644000000000000000000000013213534540120017101 xustar0030 mtime=1567801424.309849946 30 atime=1567801425.621859618 30 ctime=1567801424.309849946 openvswitch-2.5.9/tutorial/ovn/env2/0000755000175000017500000000000013534540120020644 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env2/PaxHeaders.82075/packet1.sh0000644000000000000000000000013213534540071021047 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.305849917 openvswitch-2.5.9/tutorial/ovn/env2/packet1.sh0000755000175000017500000000124713534540071022544 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:02 -generate openvswitch-2.5.9/tutorial/ovn/env2/PaxHeaders.82075/packet2.sh0000644000000000000000000000013213534540071021050 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.309849946 openvswitch-2.5.9/tutorial/ovn/env2/packet2.sh0000755000175000017500000000124713534540071022545 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovs-appctl ofproto/trace br-int in_port=1,dl_src=00:00:00:00:00:01,dl_dst=00:00:00:00:00:03 -generate openvswitch-2.5.9/tutorial/ovn/env2/PaxHeaders.82075/setup.sh0000644000000000000000000000013213534540071020657 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.305849917 openvswitch-2.5.9/tutorial/ovn/env2/setup.sh0000755000175000017500000000310613534540071022350 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovn-nbctl lswitch-add sw0 ovn-nbctl lswitch-add sw1 ovn-nbctl lport-add sw0 sw0-port1 ovn-nbctl lport-add sw0 sw0-port2 ovn-nbctl lport-add sw1 sw1-port1 ovn-nbctl lport-add sw1 sw1-port2 ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02 ovn-nbctl lport-set-addresses sw1-port1 00:00:00:00:00:03 ovn-nbctl lport-set-addresses sw1-port2 00:00:00:00:00:04 ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02 ovn-nbctl lport-set-port-security sw1-port1 00:00:00:00:00:03 ovn-nbctl lport-set-port-security sw1-port2 00:00:00:00:00:04 ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2 ovs-vsctl add-port br-int lport3 -- set Interface lport3 external_ids:iface-id=sw1-port1 ovs-vsctl add-port br-int lport4 -- set Interface lport4 external_ids:iface-id=sw1-port2 openvswitch-2.5.9/tutorial/ovn/PaxHeaders.82075/env60000644000000000000000000000013213534540120017105 xustar0030 mtime=1567801424.329850094 30 atime=1567801425.621859618 30 ctime=1567801424.329850094 openvswitch-2.5.9/tutorial/ovn/env6/0000755000175000017500000000000013534540120020650 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/tutorial/ovn/env6/PaxHeaders.82075/add-acls.sh0000644000000000000000000000013213534540071021173 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.329850094 openvswitch-2.5.9/tutorial/ovn/env6/add-acls.sh0000755000175000017500000000164513534540071022672 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # set -o xtrace ovn-nbctl acl-add sw0 from-lport 1002 "inport == \"sw0-port1\" && ip" allow-related ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip && icmp" allow-related ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip && tcp && tcp.dst == 22" allow-related ovn-nbctl acl-add sw0 to-lport 1001 "outport == \"sw0-port1\" && ip" drop openvswitch-2.5.9/tutorial/ovn/env6/PaxHeaders.82075/setup.sh0000644000000000000000000000013213534540071020663 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.329850094 openvswitch-2.5.9/tutorial/ovn/env6/setup.sh0000755000175000017500000000347213534540071022362 0ustar00jpettitjpettit00000000000000#!/bin/bash # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # See "Simple two-port setup" in tutorial/OVN-Tutorial.md. # set -o xtrace # Create a logical switch named "sw0" ovn-nbctl lswitch-add sw0 # Create two logical ports on "sw0". ovn-nbctl lport-add sw0 sw0-port1 ovn-nbctl lport-add sw0 sw0-port2 # Set a MAC address for each of the two logical ports. ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02 # Set up port security for the two logical ports. This ensures that # the logical port mac address we have configured is the only allowed # source and destination mac address for these ports. ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01 ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02 # Create ports on the local OVS bridge, br-int. When ovn-controller # sees these ports show up with an "iface-id" that matches the OVN # logical port names, it associates these local ports with the OVN # logical ports. ovn-controller will then set up the flows necessary # for these ports to be able to communicate each other as defined by # the OVN logical topology. ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1 ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2 openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-stage30000644000000000000000000000013213534540071017061 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.297849858 openvswitch-2.5.9/tutorial/t-stage30000755000175000017500000000034313534540071020552 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 \ "table=3 priority=50 actions=resubmit(,10), resubmit(,4)" ovs-ofctl add-flow br0 \ "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 \ actions=resubmit(,4)" openvswitch-2.5.9/tutorial/PaxHeaders.82075/OVN-Tutorial.md0000644000000000000000000000013213534540071020274 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801423.705845493 openvswitch-2.5.9/tutorial/OVN-Tutorial.md0000644000175000017500000010302513534540071021763 0ustar00jpettitjpettit00000000000000OVN Tutorial ============ This tutorial is intended to give you a tour of the basic OVN features using `ovs-sandbox` as a simulated test environment. It’s assumed that you have an understanding of OVS before going through this tutorial. Detail about OVN is covered in [ovn-architecture(7)], but this tutorial lets you quickly see it in action. Getting Started --------------- For some general information about `ovs-sandbox`, see the “Getting Started†section of [Tutorial.md]. `ovs-sandbox` does not include OVN support by default. To enable OVN, you must pass the `--ovn` flag. For example, if running it straight from the ovs git tree you would run: $ make sandbox SANDBOXFLAGS=â€--ovn†Running the sandbox with OVN enabled does the following additional steps to the environment: 1. Creates the `OVN_Northbound` and `OVN_Southbound` databases as described in [ovn-nb(5)] and [ovn-sb(5)]. 2. Creates the `hardware_vtep` database as described in [vtep(5)]. 3. Runs the [ovn-northd(8)], [ovn-controller(8)], and [ovn-controller-vtep(8)] daemons. 4. Makes OVN and VTEP utilities available for use in the environment, including [vtep-ctl(8)], [ovn-nbctl(8)], and [ovn-sbctl(8)]. Note that each of these demos assumes you start with a fresh sandbox environment. Re-run `ovs-sandbox` before starting each section. 1) Simple two-port setup ------------------------ This first environment is the simplest OVN example. It demonstrates using OVN with a single logical switch that has two logical ports, both residing on the same hypervisor. Start by running the setup script for this environment. [View ovn/env1/setup.sh][env1setup]. $ ovn/env1/setup.sh You can use the `ovn-nbctl` utility to see an overview of the logical topology. $ ovn-nbctl show lswitch 78687d53-e037-4555-bcd3-f4f8eaf3f2aa (sw0) lport sw0-port1 addresses: 00:00:00:00:00:01 lport sw0-port2 addresses: 00:00:00:00:00:02 The `ovn-sbctl` utility can be used to see into the state stored in the `OVN_Southbound` database. The `show` command shows that there is a single chassis with two logical ports bound to it. In a more realistic multi-hypervisor environment, this would list all hypervisors and where all logical ports are located. $ ovn-sbctl show Chassis “56b18105-5706-46ef-80c4-ff20979ab068†Encap geneve ip: “127.0.0.1†Port_Binding “sw0-port1†Port_Binding “sw0-port2†OVN creates logical flows to describe how the network should behave in logical space. Each chassis then creates OpenFlow flows based on those logical flows that reflect its own local view of the network. The `ovn-sbctl` command can show the logical flows. $ ovn-sbctl lflow-list Datapath: d3466847-2b3a-4f17-8eb2-34f5b0727a70 Pipeline: ingress table=0(port_sec), priority= 100, match=(eth.src[40]), action=(drop;) table=0(port_sec), priority= 100, match=(vlan.present), action=(drop;) table=0(port_sec), priority= 50, match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) table=0(port_sec), priority= 50, match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) table=1( acl), priority= 0, match=(1), action=(next;) table=2( l2_lkup), priority= 100, match=(eth.dst[40]), action=(outport = "_MC_flood"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) Datapath: d3466847-2b3a-4f17-8eb2-34f5b0727a70 Pipeline: egress table=0( acl), priority= 0, match=(1), action=(next;) table=1(port_sec), priority= 100, match=(eth.dst[40]), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) Now we can start taking a closer look at how `ovn-controller` has programmed the local switch. Before looking at the flows, we can use `ovs-ofctl` to verify the OpenFlow port numbers for each of the logical ports on the switch. The output shows that `lport1`, which corresponds with our logical port `sw0-port1`, has an OpenFlow port number of `1`. Similarly, `lport2` has an OpenFlow port number of `2`. $ ovs-ofctl show br-int OFPT_FEATURES_REPLY (xid=0x2): dpid:00003e1ba878364d n_tables:254, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(lport1): addr:aa:55:aa:55:00:07 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max 2(lport2): addr:aa:55:aa:55:00:08 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max LOCAL(br-int): addr:3e:1b:a8:78:36:4d config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 Finally, use `ovs-ofctl` to see the OpenFlow flows for `br-int`. Note that some fields have been omitted for brevity. $ ovs-ofctl -O OpenFlow13 dump-flows br-int OFPST_FLOW reply (OF1.3) (xid=0x2): table=0, priority=100,in_port=1 actions=set_field:0x1->metadata,set_field:0x1->reg6,resubmit(,16) table=0, priority=100,in_port=2 actions=set_field:0x1->metadata,set_field:0x2->reg6,resubmit(,16) table=16, priority=100,metadata=0x1,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop table=16, priority=100,metadata=0x1,vlan_tci=0x1000/0x1000 actions=drop table=16, priority=50,reg6=0x1,metadata=0x1,dl_src=00:00:00:00:00:01 actions=resubmit(,17) table=16, priority=50,reg6=0x2,metadata=0x1,dl_src=00:00:00:00:00:02 actions=resubmit(,17) table=17, priority=0,metadata=0x1 actions=resubmit(,18) table=18, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=set_field:0xffff->reg7,resubmit(,32) table=18, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=set_field:0x1->reg7,resubmit(,32) table=18, priority=50,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=set_field:0x2->reg7,resubmit(,32) table=32, priority=0 actions=resubmit(,33) table=33, priority=100,reg7=0x1,metadata=0x1 actions=resubmit(,34) table=33, priority=100,reg7=0xffff,metadata=0x1 actions=set_field:0x2->reg7,resubmit(,34),set_field:0x1->reg7,resubmit(,34) table=33, priority=100,reg7=0x2,metadata=0x1 actions=resubmit(,34) table=34, priority=100,reg6=0x1,reg7=0x1,metadata=0x1 actions=drop table=34, priority=100,reg6=0x2,reg7=0x2,metadata=0x1 actions=drop table=34, priority=0 actions=set_field:0->reg0,set_field:0->reg1,set_field:0->reg2,set_field:0->reg3,set_field:0->reg4,set_field:0->reg5,resubmit(,48) table=48, priority=0,metadata=0x1 actions=resubmit(,49) table=49, priority=100,metadata=0x1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,64) table=49, priority=50,reg7=0x1,metadata=0x1,dl_dst=00:00:00:00:00:01 actions=resubmit(,64) table=49, priority=50,reg7=0x2,metadata=0x1,dl_dst=00:00:00:00:00:02 actions=resubmit(,64) table=64, priority=100,reg7=0x1,metadata=0x1 actions=output:1 table=64, priority=100,reg7=0x2,metadata=0x1 actions=output:2 The `ovs-appctl` command can be used to generate an OpenFlow trace of how a packet would be processed in this configuration. This first trace shows a packet from `sw0-port1` to `sw0-port2`. The packet arrives from port `1` and should be output to port `2`. [View ovn/env1/packet1.sh][env1packet1]. $ ovn/env1/packet1.sh Trace a broadcast packet from `sw0-port1`. The packet arrives from port `1` and should be output to port `2`. [View ovn/env1/packet2.sh][env1packet2]. $ ovn/env1/packet2.sh You can extend this setup by adding additional ports. For example, to add a third port, run this command: [View ovn/env1/add-third-port.sh][env1thirdport]. $ ovn/env1/add-third-port.sh Now if you do another trace of a broadcast packet from `sw0-port1`, you will see that it is output to both ports `2` and `3`. $ ovn/env1/packet2.sh 2) 2 switches, 4 ports ---------------------- This environment is an extension of the last example. The previous example showed two ports on a single logical switch. In this environment we add a second logical switch that also has two ports. This lets you start to see how `ovn-controller` creates flows for isolated networks to co-exist on the same switch. [View ovn/env2/setup.sh][env2setup]. $ ovn/env2/setup.sh View the logical topology with `ovn-nbctl`. $ ovn-nbctl show lswitch e3190dc2-89d1-44ed-9308-e7077de782b3 (sw0) lport sw0-port1 addresses: 00:00:00:00:00:01 lport sw0-port2 addresses: 00:00:00:00:00:02 lswitch c8ed4c5f-9733-43f6-93da-795b1aabacb1 (sw1) lport sw1-port1 addresses: 00:00:00:00:00:03 lport sw1-port2 addresses: 00:00:00:00:00:04 Physically, all ports reside on the same chassis. $ ovn-sbctl show Chassis “56b18105-5706-46ef-80c4-ff20979ab068†Encap geneve ip: “127.0.0.1†Port_Binding “sw1-port2†Port_Binding “sw0-port2†Port_Binding “sw0-port1†Port_Binding “sw1-port1†OVN creates separate logical flows for each logical switch. $ ovn-sbctl lflow-list Datapath: 5aa8be0b-8369-49e2-a878-f68872a8d211 Pipeline: ingress table=0(port_sec), priority= 100, match=(eth.src[40]), action=(drop;) table=0(port_sec), priority= 100, match=(vlan.present), action=(drop;) table=0(port_sec), priority= 50, match=(inport == "sw0-port1" && eth.src == {00:00:00:00:00:01}), action=(next;) table=0(port_sec), priority= 50, match=(inport == "sw0-port2" && eth.src == {00:00:00:00:00:02}), action=(next;) table=1( acl), priority= 0, match=(1), action=(next;) table=2( l2_lkup), priority= 100, match=(eth.dst[40]), action=(outport = "_MC_flood"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:01), action=(outport = "sw0-port1"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:02), action=(outport = "sw0-port2"; output;) Datapath: 5aa8be0b-8369-49e2-a878-f68872a8d211 Pipeline: egress table=0( acl), priority= 0, match=(1), action=(next;) table=1(port_sec), priority= 100, match=(eth.dst[40]), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw0-port1" && eth.dst == {00:00:00:00:00:01}), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw0-port2" && eth.dst == {00:00:00:00:00:02}), action=(output;) Datapath: 631fb3c9-b0a3-4e56-bac3-1717c8cbb826 Pipeline: ingress table=0(port_sec), priority= 100, match=(eth.src[40]), action=(drop;) table=0(port_sec), priority= 100, match=(vlan.present), action=(drop;) table=0(port_sec), priority= 50, match=(inport == "sw1-port1" && eth.src == {00:00:00:00:00:03}), action=(next;) table=0(port_sec), priority= 50, match=(inport == "sw1-port2" && eth.src == {00:00:00:00:00:04}), action=(next;) table=1( acl), priority= 0, match=(1), action=(next;) table=2( l2_lkup), priority= 100, match=(eth.dst[40]), action=(outport = "_MC_flood"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:03), action=(outport = "sw1-port1"; output;) table=2( l2_lkup), priority= 50, match=(eth.dst == 00:00:00:00:00:04), action=(outport = "sw1-port2"; output;) Datapath: 631fb3c9-b0a3-4e56-bac3-1717c8cbb826 Pipeline: egress table=0( acl), priority= 0, match=(1), action=(next;) table=1(port_sec), priority= 100, match=(eth.dst[40]), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw1-port1" && eth.dst == {00:00:00:00:00:03}), action=(output;) table=1(port_sec), priority= 50, match=(outport == "sw1-port2" && eth.dst == {00:00:00:00:00:04}), action=(output;) In this setup, `sw0-port1` and `sw0-port2` can send packets to each other, but not to either of the ports on `sw1`. This first trace shows a packet from `sw0-port1` to `sw0-port2`. You should see th packet arrive on OpenFlow port `1` and output to OpenFlow port `2`. [View ovn/env2/packet1.sh][env2packet1]. $ ovn/env2/packet1.sh This next example shows a packet from `sw0-port1` with a destination MAC address of `00:00:00:00:00:03`, which is the MAC address for `sw1-port1`. Since these ports are not on the same logical switch, the packet should just be dropped. [View ovn/env2/packet2.sh][env2packet2]. $ ovn/env2/packet2.sh 3) Two Hypervisors ------------------ The first two examples started by showing OVN on a single hypervisor. A more realistic deployment of OVN would span multiple hypervisors. This example creates a single logical switch with 4 logical ports. It then simulates having two hypervisors with two of the logical ports bound to each hypervisor. [View ovn/env3/setup.sh][env3setup]. $ ovn/env3/setup.sh You can start by viewing the logical topology with `ovn-nbctl`. $ ovn-nbctl show lswitch b977dc03-79a5-41ba-9665-341a80e1abfd (sw0) lport sw0-port1 addresses: 00:00:00:00:00:01 lport sw0-port2 addresses: 00:00:00:00:00:02 lport sw0-port4 addresses: 00:00:00:00:00:04 lport sw0-port3 addresses: 00:00:00:00:00:03 Using `ovn-sbctl` to view the state of the system, we can see that there are two chassis: one local that we can interact with, and a fake remote chassis. Two logical ports are bound to each. Both chassis have an IP address of localhost, but in a realistic deployment that would be the IP address used for tunnels to that chassis. $ ovn-sbctl show Chassis “56b18105-5706-46ef-80c4-ff20979ab068†Encap geneve ip: “127.0.0.1†Port_Binding “sw0-port2†Port_Binding “sw0-port1†Chassis fakechassis Encap geneve ip: “127.0.0.1†Port_Binding “sw0-port4†Port_Binding “sw0-port3†Packets between `sw0-port1` and `sw0-port2` behave just like the previous examples. Packets to ports on a remote chassis are the interesting part of this example. You may have noticed before that OVN’s logical flows are broken up into ingress and egress tables. Given a packet from `sw0-port1` on the local chassis to `sw0-port3` on the remote chassis, the ingress pipeline is executed on the local switch. OVN then determines that it must forward the packet over a geneve tunnel. When it arrives at the remote chassis, the egress pipeline will be executed there. This first packet trace shows the first part of this example. It’s a packet from `sw0-port1` to `sw0-port3` from the perspective of the local chassis. `sw0-port1` is OpenFlow port `1`. The tunnel to the fake remote chassis is OpenFlow port `3`. You should see the ingress pipeline being executed and then the packet output to port `3`, the geneve tunnel. [View ovn/env3/packet1.sh][env3packet1]. $ ovn/env3/packet1.sh To simulate what would happen when that packet arrives at the remote chassis we can flip this example around. Consider a packet from `sw0-port3` to `sw0-port1`. This trace shows what would happen when that packet arrives at the local chassis. The packet arrives on OpenFlow port `3` (the tunnel). You should then see the egress pipeline get executed and the packet output to OpenFlow port `1`. [View ovn/env3/packet2.sh][env3packet2]. $ ovn/env3/packet2.sh 4) Locally attached networks ---------------------------- While OVN is generally focused on the implementation of logical networks using overlays, it’s also possible to use OVN as a control plane to manage logically direct connectivity to networks that are locally accessible to each chassis. This example includes two hypervisors. Both hypervisors have two ports on them. We want to use OVN to manage the connectivity of these ports to a network attached to each hypervisor that we will call “physnet1â€. This scenario requires some additional configuration of `ovn-controller`. We must configure a mapping between `physnet1` and a local OVS bridge that provides connectivity to that network. We call these “bridge mappingsâ€. For our example, the following script creates a bridge called `br-eth1` and then configures `ovn-controller` with a bridge mapping from `physnet1` to `br-eth1`. [View ovn/env4/setup1.sh][env4setup1]. $ ovn/env4/setup1.sh At this point we should be able to see that `ovn-controller` has automatically created patch ports between `br-int` and `br-eth1`. $ ovs-vsctl show aea39214-ebec-4210-aa34-1ae7d6921720 Bridge br-int fail_mode: secure Port “patch-br-int-to-br-eth1†Interface “patch-br-int-to-br-eth1†type: patch options: {peer=â€patch-br-eth1-to-br-intâ€} Port br-int Interface br-int type: internal Bridge “br-eth1†Port “br-eth1†Interface “br-eth1†type: internal Port “patch-br-eth1-to-br-int†Interface “patch-br-eth1-to-br-int†type: patch options: {peer=â€patch-br-int-to-br-eth1â€} Now we can move on to the next setup phase for this example. We want to create a fake second chassis and then create the topology that tells OVN we want both ports on both hypervisors connected to `physnet1`. The way this is modeled in OVN is by creating a logical switch for each port. The logical switch has the regular VIF port and a `localnet` port. [View ovn/env4/setup2.sh][env4setup2]. $ ovn/env4/setup2.sh The logical topology from `ovn-nbctl` should look like this. $ ovn-nbctl show lswitch 5a652488-cfba-4f3e-929d-00010cdfde40 (provnet1-2) lport provnet1-2-physnet1 addresses: unknown lport provnet1-2-port1 addresses: 00:00:00:00:00:02 lswitch 5829b60a-eda8-4d78-94f6-7017ff9efcf0 (provnet1-4) lport provnet1-4-port1 addresses: 00:00:00:00:00:04 lport provnet1-4-physnet1 addresses: unknown lswitch 06cbbcb6-38e3-418d-a81e-634ec9b54ad6 (provnet1-1) lport provnet1-1-port1 addresses: 00:00:00:00:00:01 lport provnet1-1-physnet1 addresses: unknown lswitch 9cba3b3b-59ae-4175-95f5-b6f1cd9c2afb (provnet1-3) lport provnet1-3-physnet1 addresses: unknown lport provnet1-3-port1 addresses: 00:00:00:00:00:03 `port1` on each logical switch represents a regular logical port for a VIF on a hypervisor. `physnet1` on each logical switch is the special `localnet` port. You can use `ovn-nbctl` to see that this port has a `type` and `options` set. $ ovn-nbctl lport-get-type provnet1-1-physnet1 localnet $ ovn-nbctl lport-get-options provnet1-1-physnet1 network_name=physnet1 The physical topology should reflect that there are two regular ports on each chassis. $ ovn-sbctl show Chassis fakechassis Encap geneve ip: “127.0.0.1†Port_Binding “provnet1-3-port1†Port_Binding “provnet1-4-port1†Chassis “56b18105-5706-46ef-80c4-ff20979ab068†Encap geneve ip: “127.0.0.1†Port_Binding “provnet1-2-port1†Port_Binding “provnet1-1-port1†All four of our ports should be able to communicate with each other, but they do so through `physnet1`. A packet from any of these ports to any destination should be output to the OpenFlow port number that corresponds to the patch port to `br-eth1`. This example assumes following OpenFlow port number mappings: * 1 = patch port to `br-eth1` * 2 = tunnel to the fake second chassis * 3 = lport1, which is the logical port named `provnet1-1-port1` * 4 = lport2, which is the logical port named `provnet1-2-port1` We get those port numbers using `ovs-ofctl`: $ ovs-ofctl show br-int OFPT_FEATURES_REPLY (xid=0x2): dpid:0000765054700040 n_tables:254, n_buffers:256 capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP actions: output enqueue set_vlan_vid set_vlan_pcp strip_vlan mod_dl_src mod_dl_dst mod_nw_src mod_nw_dst mod_nw_tos mod_tp_src mod_tp_dst 1(patch-br-int-to): addr:de:29:14:95:8a:b8 config: 0 state: 0 speed: 0 Mbps now, 0 Mbps max 2(ovn-fakech-0): addr:aa:55:aa:55:00:08 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max 3(lport1): addr:aa:55:aa:55:00:09 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max 4(lport2): addr:aa:55:aa:55:00:0a config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max LOCAL(br-int): addr:76:50:54:70:00:40 config: PORT_DOWN state: LINK_DOWN speed: 0 Mbps now, 0 Mbps max OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 This first trace shows a packet from `provnet1-1-port1` with a destination MAC address of `provnet1-2-port1`. Despite both of these ports being on the same local switch (`lport1` and `lport2`), we expect all packets to be sent out to `br-eth1` (OpenFlow port 1). We then expect the network to handle getting the packet to its destination. In practice, this will be optimized at `br-eth1` and the packet won’t actually go out and back on the network. [View ovn/env4/packet1.sh][env4packet1]. $ ovn/env4/packet1.sh This next trace is a continuation of the previous one. This shows the packet coming back into `br-int` from `br-eth1`. We now expect the packet to be output to `provnet1-2-port1`, which is OpenFlow port 4. [View ovn/env4/packet2.sh][env4packet2]. $ ovn/env4/packet2.sh This next trace shows an example of a packet being sent to a destination on another hypervisor. The source is `provnet1-2-port1`, but the destination is `provnet1-3-port1`, which is on the other fake chassis. As usual, we expect the output to be to OpenFlow port 1, the patch port to `br-et1`. [View ovn/env4/packet3.sh][env4packet3]. $ ovn/env4/packet3.sh This next test shows a broadcast packet. The destination should still only be OpenFlow port 1. [View ovn/env4/packet4.sh][env4packet4] $ ovn/env4/packet4.sh Finally, this last trace shows what happens when a broadcast packet arrives from the network. In this case, it simulates a broadcast that originated from a port on the remote fake chassis and arrived at the local chassis via `br-eth1`. We should see it output to both local ports that are attached to this network (OpenFlow ports 3 and 4). [View ovn/env4/packet5.sh][env4packet5] $ ovn/env4/packet5.sh 5) Locally attached networks with VLANs --------------------------------------- This example is an extension of the previous one. We take the same setup and add two more ports to each hypervisor. Instead of having the new ports directly connected to `physnet1` as before, we indicate that we want them on VLAN 101 of `physnet1`. This shows how `localnet` ports can be used to provide connectivity to either a flat network or a VLAN on that network. [View ovn/env5/setup.sh][env5setup] $ ovn/env5/setup.sh The logical topology shown by `ovn-nbctl` is similar to `env4`, except we now have 8 regular VIF ports connected to `physnet1` instead of 4. The additional 4 ports we have added are all on VLAN 101 of `physnet1`. Note that the `localnet` ports representing connectivity to VLAN 101 of `physnet1` have the `tag` field set to `101`. $ ovn-nbctl show lswitch 12ea93d0-694b-48e9-adef-d0ddd3ec4ac9 (provnet1-7-101) lport provnet1-7-physnet1-101 parent: , tag:101 addresses: unknown lport provnet1-7-101-port1 addresses: 00:00:00:00:00:07 lswitch c9a5ce3a-15ec-48ea-a898-416013463589 (provnet1-4) lport provnet1-4-port1 addresses: 00:00:00:00:00:04 lport provnet1-4-physnet1 addresses: unknown lswitch e07d4f7a-2085-4fbb-9937-d6192b79a397 (provnet1-1) lport provnet1-1-physnet1 addresses: unknown lport provnet1-1-port1 addresses: 00:00:00:00:00:01 lswitch 6c098474-0509-4219-bc9b-eb4e28dd1aeb (provnet1-2) lport provnet1-2-physnet1 addresses: unknown lport provnet1-2-port1 addresses: 00:00:00:00:00:02 lswitch 723c4684-5d58-4202-b8e3-4ba99ad5ed9e (provnet1-8-101) lport provnet1-8-101-port1 addresses: 00:00:00:00:00:08 lport provnet1-8-physnet1-101 parent: , tag:101 addresses: unknown lswitch 8444e925-ceb2-4b02-ac20-eb2e4cfb954d (provnet1-6-101) lport provnet1-6-physnet1-101 parent: , tag:101 addresses: unknown lport provnet1-6-101-port1 addresses: 00:00:00:00:00:06 lswitch e11e5605-7c46-4395-b28d-cff57451fc7e (provnet1-3) lport provnet1-3-port1 addresses: 00:00:00:00:00:03 lport provnet1-3-physnet1 addresses: unknown lswitch 0706b697-6c92-4d54-bc0a-db5bababb74a (provnet1-5-101) lport provnet1-5-101-port1 addresses: 00:00:00:00:00:05 lport provnet1-5-physnet1-101 parent: , tag:101 addresses: unknown The physical topology shows that we have 4 regular VIF ports on each simulated hypervisor. $ ovn-sbctl show Chassis “56b18105-5706-46ef-80c4-ff20979ab068†Encap geneve ip: “127.0.0.1†Port_Binding “provnet1-6-101-port1†Port_Binding “provnet1-1-port1†Port_Binding “provnet1-2-port1†Port_Binding “provnet1-5-101-port1†Chassis fakechassis Encap geneve ip: “127.0.0.1†Port_Binding “provnet1-4-port1†Port_Binding “provnet1-3-port1†Port_Binding “provnet1-8-101-port1†Port_Binding “provnet1-7-101-port1†All of the traces from the previous example, `env4`, should work in this environment and provide the same result. Now we can show what happens for the ports connected to VLAN 101. This first example shows a packet originating from `provnet1-5-101-port1`, which is OpenFlow port 5. We should see VLAN tag 101 pushed on the packet and then output to OpenFlow port 1, the patch port to `br-eth1` (the bridge providing connectivity to `physnet1`). [View ovn/env5/packet1.sh][env5packet1]. $ ovn/env5/packet1.sh If we look at a broadcast packet arriving on VLAN 101 of `physnet1`, we should see it output to OpenFlow ports 5 and 6 only. [View ovn/env5/packet2.sh][env5packet2]. $ ovn/env5/packet2.sh 6) Stateful ACLs ---------------- ACLs provide a way to do distributed packet filtering for OVN networks. One example use of ACLs is that OpenStack Neutron uses them to implement security groups. ACLs are implemented using conntrack integration with OVS. Start with a simple logical switch with 2 logical ports. [View ovn/env6/setup.sh][env6setup]. $ ovn/env6/setup.sh A common use case would be the following policy applied for `sw0-port1`: * Allow outbound IP traffic and associated return traffic. * Allow incoming ICMP requests and associated return traffic. * Allow incoming SSH connections and associated return traffic. * Drop other incoming IP traffic. The following script applies this policy to our environment. [View ovn/env6/add-acls.sh][env6acls]. $ ovn/env6/add-acls.sh We can view the configured ACLs on this network using the `ovn-nbctl` command. $ ovn-nbctl acl-list sw0 from-lport 1002 (inport == “sw0-port1†&& ip) allow-related to-lport 1002 (outport == “sw0-port1†&& ip && icmp) allow-related to-lport 1002 (outport == “sw0-port1†&& ip && tcp && tcp.dst == 22) allow-related to-lport 1001 (outport == “sw0-port1†&& ip) drop Now that we have ACLs configured, there are new entries in the logical flow table in the stages `switch_in_pre_acl`, switch_in_acl`, `switch_out_pre_acl`, and `switch_out_acl`. $ ovn-sbctl lflow-list Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`. In `switch_out_pre_acl`, we match IP traffic and put it through the connection tracker. This populates the connection state fields so that we can apply policy as appropriate. table=0(switch_out_pre_acl), priority= 100, match=(ip), action=(ct_next;) table=1(switch_out_pre_acl), priority= 0, match=(1), action=(next;) In `switch_out_acl`, we allow packets associated with existing connections. We drop packets that are deemed to be invalid (such as non-SYN TCP packet not associated with an existing connection). table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;) table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;) table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;) For new connections, we apply our configured ACL policy to decide whether to allow the connection or not. In this case, we’ll allow ICMP or SSH. Otherwise, we’ll drop the packet. table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1†&& ip && icmp)), action=(ct_commit; next;) table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1†&& ip && tcp && tcp.dst == 22)), action=(ct_commit; next;) table=1(switch_out_acl), priority= 2001, match=(outport == “sw0-port1†&& ip), action=(drop;) When using ACLs, the default policy is to allow and track IP connections. Based on our above policy, IP traffic directed at `sw0-port1` will never hit this flow at priority 1. table=1(switch_out_acl), priority= 1, match=(ip), action=(ct_commit; next;) table=1(switch_out_acl), priority= 0, match=(1), action=(next;) Note that conntrack integration is not yet supported in ovs-sandbox, so the OpenFlow flows will not represent what you’d see in a real environment. The logical flows described above give a very good idea of what the flows look like, though. [This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an OpenStack perspective and also provides an example of what the resulting OpenFlow flows look like. [ovn-architecture(7)]:http://openvswitch.org/support/dist-docs/ovn-architecture.7.html [Tutorial.md]:https://github.com/openvswitch/ovs/blob/master/tutorial/Tutorial.md [ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html [ovn-sb(5)]:http://openvswitch.org/support/dist-docs/ovn-sb.5.html [vtep(5)]:http://openvswitch.org/support/dist-docs/vtep.5.html [ovn-northd(8)]:http://openvswitch.org/support/dist-docs/ovn-northd [ovn-controller(8)]:http://openvswitch.org/support/dist-docs/ovn-controller.8.html [ovn-controller-vtep(8)]:http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html [vtep-ctl(8)]:http://openvswitch.org/support/dist-docs/vtep-ctl.8.html [ovn-nbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html [ovn-sbctl(8)]:http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html [env1setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/setup.sh [env1packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet1.sh [env1packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/packet2.sh [env1thirdport]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env1/add-third-port.sh [env2setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/setup.sh [env2packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet1.sh [env2packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env2/packet2.sh [env3setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/setup.sh [env3packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet1.sh [env3packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env3/packet2.sh [env4setup1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup1.sh [env4setup2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/setup2.sh [env4packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet1.sh [env4packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet2.sh [env4packet3]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet3.sh [env4packet4]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet4.sh [env4packet5]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env4/packet5.sh [env5setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/setup.sh [env5packet1]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet1.sh [env5packet2]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env5/packet2.sh [env6setup]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/setup.sh [env6acls]:https://github.com/openvswitch/ovs/blob/master/tutorial/ovn/env6/add-acls.sh [openstack-ovn-acl-blog]:http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/ openvswitch-2.5.9/tutorial/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017646 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.621852246 openvswitch-2.5.9/tutorial/automake.mk0000644000175000017500000000200113534540071021325 0ustar00jpettitjpettit00000000000000docs += \ tutorial/Tutorial.md \ tutorial/OVN-Tutorial.md EXTRA_DIST += \ tutorial/ovs-sandbox \ tutorial/t-setup \ tutorial/t-stage0 \ tutorial/t-stage1 \ tutorial/t-stage2 \ tutorial/t-stage3 \ tutorial/t-stage4 \ tutorial/ovn/env1/setup.sh \ tutorial/ovn/env1/packet1.sh \ tutorial/ovn/env1/packet2.sh \ tutorial/ovn/env1/add-third-port.sh \ tutorial/ovn/env2/setup.sh \ tutorial/ovn/env2/packet1.sh \ tutorial/ovn/env2/packet2.sh \ tutorial/ovn/env3/setup.sh \ tutorial/ovn/env3/packet1.sh \ tutorial/ovn/env3/packet2.sh \ tutorial/ovn/env4/setup1.sh \ tutorial/ovn/env4/setup2.sh \ tutorial/ovn/env4/packet1.sh \ tutorial/ovn/env4/packet2.sh \ tutorial/ovn/env4/packet3.sh \ tutorial/ovn/env4/packet4.sh \ tutorial/ovn/env4/packet5.sh \ tutorial/ovn/env5/setup.sh \ tutorial/ovn/env5/packet1.sh \ tutorial/ovn/env5/packet2.sh \ tutorial/ovn/env6/setup.sh \ tutorial/ovn/env6/add-acls.sh sandbox: all cd $(srcdir)/tutorial && MAKE=$(MAKE) ./ovs-sandbox -b $(abs_builddir) $(SANDBOXFLAGS) openvswitch-2.5.9/tutorial/PaxHeaders.82075/Tutorial.md0000644000000000000000000000013213534540071017634 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801423.701845464 openvswitch-2.5.9/tutorial/Tutorial.md0000644000175000017500000010155113534540071021325 0ustar00jpettitjpettit00000000000000Open vSwitch Advanced Features Tutorial ======================================= Many tutorials cover the basics of OpenFlow. This is not such a tutorial. Rather, a knowledge of the basics of OpenFlow is a prerequisite. If you do not already understand how an OpenFlow flow table works, please go read a basic tutorial and then continue reading here afterward. It is also important to understand the basics of Open vSwitch before you begin. If you have never used `ovs-vsctl` or `ovs-ofctl` before, you should learn a little about them before proceeding. Most of the features covered in this tutorial are Open vSwitch extensions to OpenFlow. Also, most of the features in this tutorial are specific to the software Open vSwitch implementation. If you are using an Open vSwitch port to an ASIC-based hardware switch, this tutorial will not help you. This tutorial does not cover every aspect of the features that it mentions. You can find the details elsewhere in the Open vSwitch documentation, especially `ovs-ofctl(8)` and the comments in the `include/openflow/nicira-ext.h` header file. > In this tutorial, paragraphs set off like this designate notes > with additional information that readers may wish to skip on a > first read. Getting Started --------------- This is a hands-on tutorial. To get the most out of it, you will need Open vSwitch binaries. You do not, on the other hand, need any physical networking hardware or even supervisor privilege on your system. Instead, we will use a script called `ovs-sandbox`, which accompanies the tutorial, that constructs a software simulated network environment based on Open vSwitch. You can use `ovs-sandbox` three ways: * If you have already installed Open vSwitch on your system, then you should be able to just run `ovs-sandbox` from this directory without any options. * If you have not installed Open vSwitch (and you do not want to install it), then you can build Open vSwitch according to the instructions in [INSTALL.md], without installing it. Then run `./ovs-sandbox -b DIRECTORY` from this directory, substituting the Open vSwitch build directory for `DIRECTORY`. * As a slight variant on the latter, you can run `make sandbox` from an Open vSwitch build directory. When you run `ovs-sandbox`, it does the following: 1. **CAUTION:** Deletes any subdirectory of the current directory named "sandbox" and any files in that directory. 2. Creates a new directory "sandbox" in the current directory. 3. Sets up special environment variables that ensure that Open vSwitch programs will look inside the "sandbox" directory instead of in the Open vSwitch installation directory. 4. If you are using a built but not installed Open vSwitch, installs the Open vSwitch manpages in a subdirectory of "sandbox" and adjusts the `MANPATH` environment variable to point to this directory. This means that you can use, for example, `man ovs-vsctl` to see a manpage for the `ovs-vsctl` program that you built. 5. Creates an empty Open vSwitch configuration database under "sandbox". 6. Starts `ovsdb-server` running under "sandbox". 7. Starts `ovs-vswitchd` running under "sandbox", passing special options that enable a special "dummy" mode for testing. 8. Starts a nested interactive shell inside "sandbox". At this point, you can run all the usual Open vSwitch utilities from the nested shell environment. You can, for example, use `ovs-vsctl` to create a bridge: ovs-vsctl add-br br0 From Open vSwitch's perspective, the bridge that you create this way is as real as any other. You can, for example, connect it to an OpenFlow controller or use `ovs-ofctl` to examine and modify it and its OpenFlow flow table. On the other hand, the bridge is not visible to the operating system's network stack, so `ifconfig` or `ip` cannot see it or affect it, which means that utilities like `ping` and `tcpdump` will not work either. (That has its good side, too: you can't screw up your computer's network stack by manipulating a sandboxed OVS.) When you're done using OVS from the sandbox, exit the nested shell (by entering the "exit" shell command or pressing Control+D). This will kill the daemons that `ovs-sandbox` started, but it leaves the "sandbox" directory and its contents in place. The sandbox directory contains log files for the Open vSwitch dameons. You can examine them while you're running in the sandboxed environment or after you exit. Using GDB --------- GDB support is not required to go through the tutorial. It is added in case user wants to explore the internals of OVS programs. GDB can already be used to debug any running process, with the usual 'gdb ' command. 'ovs-sandbox' also has a '-g' option for launching ovs-vswitchd under GDB. This option can be handy for setting break points before ovs-vswitchd runs, or for catching early segfaults. Similarly, a '-d' option can be used to run ovsdb-server under GDB. Both options can be specified at the same time. In addition, a '-e' option also launches ovs-vswitchd under GDB. However, instead of displaying a 'gdb>' prompt and waiting for user input, ovs-vswitchd will start to execute immediately. '-r' option is the corresponding option for running ovsdb-server under gdb with immediate execution. To avoid GDB mangling with the sandbox sub shell terminal, 'ovs-sandbox' starts a new xterm to run each GDB session. For systems that do not support X windows, GDB support is effectively disabled. When launching sandbox through the build tree's make file, the '-g' option can be passed via the 'SANDBOXFLAGS' environment variable. 'make sandbox SANDBOXFLAGS=-g' will start the sandbox with ovs-vswitchd running under GDB in its own xterm if X is available. Motivation ---------- The goal of this tutorial is to demonstrate the power of Open vSwitch flow tables. The tutorial works through the implementation of a MAC-learning switch with VLAN trunk and access ports. Outside of the Open vSwitch features that we will discuss, OpenFlow provides at least two ways to implement such a switch: 1. An OpenFlow controller to implement MAC learning in a "reactive" fashion. Whenever a new MAC appears on the switch, or a MAC moves from one switch port to another, the controller adjusts the OpenFlow flow table to match. 2. The "normal" action. OpenFlow defines this action to submit a packet to "the traditional non-OpenFlow pipeline of the switch". That is, if a flow uses this action, then the packets in the flow go through the switch in the same way that they would if OpenFlow was not configured on the switch. Each of these approaches has unfortunate pitfalls. In the first approach, using an OpenFlow controller to implement MAC learning, has a significant cost in terms of network bandwidth and latency. It also makes the controller more difficult to scale to large numbers of switches, which is especially important in environments with thousands of hypervisors (each of which contains a virtual OpenFlow switch). MAC learning at an OpenFlow controller also behaves poorly if the OpenFlow controller fails, slows down, or becomes unavailable due to network problems. The second approach, using the "normal" action, has different problems. First, little about the "normal" action is standardized, so it behaves differently on switches from different vendors, and the available features and how those features are configured (usually not through OpenFlow) varies widely. Second, "normal" does not work well with other OpenFlow actions. It is "all-or-nothing", with little potential to adjust its behavior slightly or to compose it with other features. Scenario -------- We will construct Open vSwitch flow tables for a VLAN-capable, MAC-learning switch that has four ports: * p1, a trunk port that carries all VLANs, on OpenFlow port 1. * p2, an access port for VLAN 20, on OpenFlow port 2. * p3 and p4, both access ports for VLAN 30, on OpenFlow ports 3 and 4, respectively. > The ports' names are not significant. You could call them eth1 > through eth4, or any other names you like. > An OpenFlow switch always has a "local" port as well. This > scenario won't use the local port. Our switch design will consist of five main flow tables, each of which implements one stage in the switch pipeline: Table 0: Admission control. Table 1: VLAN input processing. Table 2: Learn source MAC and VLAN for ingress port. Table 3: Look up learned port for destination MAC and VLAN. Table 4: Output processing. The section below describes how to set up the scenario, followed by a section for each OpenFlow table. You can cut and paste the `ovs-vsctl` and `ovs-ofctl` commands in each of the sections below into your `ovs-sandbox` shell. They are also available as shell scripts in this directory, named `t-setup`, `t-stage0`, `t-stage1`, ..., `t-stage4`. The `ovs-appctl` test commands are intended for cutting and pasting and are not supplied separately. Setup ----- To get started, start `ovs-sandbox`. Inside the interactive shell that it starts, run this command: ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure This command creates a new bridge "br0" and puts "br0" into so-called "fail-secure" mode. For our purpose, this just means that the OpenFlow flow table starts out empty. > If we did not do this, then the flow table would start out with a > single flow that executes the "normal" action. We could use that > feature to yield a switch that behaves the same as the switch we > are currently building, but with the caveats described under > "Motivation" above.) The new bridge has only one port on it so far, the "local port" br0. We need to add p1, p2, p3, and p4. A shell "for" loop is one way to do it: for i in 1 2 3 4; do ovs-vsctl add-port br0 p$i -- set Interface p$i ofport_request=$i ovs-ofctl mod-port br0 p$i up done In addition to adding a port, the `ovs-vsctl` command above sets its "ofport_request" column to ensure that port p1 is assigned OpenFlow port 1, p2 is assigned OpenFlow port 2, and so on. > We could omit setting the ofport_request and let Open vSwitch > choose port numbers for us, but it's convenient for the purposes > of this tutorial because we can talk about OpenFlow port 1 and > know that it corresponds to p1. The `ovs-ofctl` command above brings up the simulated interfaces, which are down initially, using an OpenFlow request. The effect is similar to `ifconfig up`, but the sandbox's interfaces are not visible to the operating system and therefore `ifconfig` would not affect them. We have not configured anything related to VLANs or MAC learning. That's because we're going to implement those features in the flow table. To see what we've done so far to set up the scenario, you can run a command like `ovs-vsctl show` or `ovs-ofctl show br0`. Implementing Table 0: Admission control --------------------------------------- Table 0 is where packets enter the switch. We use this stage to discard packets that for one reason or another are invalid. For example, packets with a multicast source address are not valid, so we can add a flow to drop them at ingress to the switch with: ovs-ofctl add-flow br0 \ "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop" A switch should also not forward IEEE 802.1D Spanning Tree Protocol (STP) packets, so we can also add a flow to drop those and other packets with reserved multicast protocols: ovs-ofctl add-flow br0 \ "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop" We could add flows to drop other protocols, but these demonstrate the pattern. We need one more flow, with a priority lower than the default, so that flows that don't match either of the "drop" flows we added above go on to pipeline stage 1 in OpenFlow table 1: ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)" (The "resubmit" action is an Open vSwitch extension to OpenFlow.) ### Testing Table 0 If we were using Open vSwitch to set up a physical or a virtual switch, then we would naturally test it by sending packets through it one way or another, perhaps with common network testing tools like `ping` and `tcpdump` or more specialized tools like Scapy. That's difficult with our simulated switch, since it's not visible to the operating system. But our simulated switch has a few specialized testing tools. The most powerful of these tools is `ofproto/trace`. Given a switch and the specification of a flow, `ofproto/trace` shows, step-by-step, how such a flow would be treated as it goes through the switch. ### EXAMPLE 1 Try this command: ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:05 The output should look something like this: Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:05,dl_type=0x0000 Rule: table=0 cookie=0 dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0 OpenFlow actions=drop Final flow: unchanged Datapath actions: drop The first block of lines describes an OpenFlow table lookup. The first line shows the fields used for the table lookup (which is mostly zeros because that's the default if we don't specify everything). The second line gives the OpenFlow flow that the fields matched (called a "rule" because that is the name used inside Open vSwitch for an OpenFlow flow). In this case, we see that this packet that has a reserved multicast destination address matches the rule that drops those packets. The third line gives the rule's OpenFlow actions. The second block of lines summarizes the results, which are not very interesting here. ### EXAMPLE 2 Try another command: ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:10 The output should be: Flow: metadata=0,in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:10,dl_type=0x0000 Rule: table=0 cookie=0 priority=0 OpenFlow actions=resubmit(,1) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop No match Final flow: unchanged Datapath actions: drop This time the flow we handed to `ofproto/trace` doesn't match any of our "drop" rules, so it falls through to the low-priority "resubmit" rule, which we see in the rule and the actions selected in the first block. The "resubmit" causes a second lookup in OpenFlow table 1, described by the additional block of indented text in the output. We haven't yet added any flows to OpenFlow table 1, so no flow actually matches in the second lookup. Therefore, the packet is still actually dropped, which means that the externally observable results would be identical to our first example. Implementing Table 1: VLAN Input Processing ------------------------------------------- A packet that enters table 1 has already passed basic validation in table 0. The purpose of table 1 is validate the packet's VLAN, based on the VLAN configuration of the switch port through which the packet entered the switch. We will also use it to attach a VLAN header to packets that arrive on an access port, which allows later processing stages to rely on the packet's VLAN always being part of the VLAN header, reducing special cases. Let's start by adding a low-priority flow that drops all packets, before we add flows that pass through acceptable packets. You can think of this as a "default drop" rule: ovs-ofctl add-flow br0 "table=1, priority=0, actions=drop" Our trunk port p1, on OpenFlow port 1, is an easy case. p1 accepts any packet regardless of whether it has a VLAN header or what the VLAN was, so we can add a flow that resubmits everything on input port 1 to the next table: ovs-ofctl add-flow br0 \ "table=1, priority=99, in_port=1, actions=resubmit(,2)" On the access ports, we want to accept any packet that has no VLAN header, tag it with the access port's VLAN number, and then pass it along to the next stage: ovs-ofctl add-flows br0 - <<'EOF' table=1, priority=99, in_port=2, vlan_tci=0, actions=mod_vlan_vid:20, resubmit(,2) table=1, priority=99, in_port=3, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) table=1, priority=99, in_port=4, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2) EOF We don't write any rules that match packets with 802.1Q that enter this stage on any of the access ports, so the "default drop" rule we added earlier causes them to be dropped, which is ordinarily what we want for access ports. > Another variation of access ports allows ingress of packets tagged > with VLAN 0 (aka 802.1p priority tagged packets). To allow such > packets, replace "vlan_tci=0" by "vlan_tci=0/0xfff" above. ### Testing Table 1 `ofproto/trace` allows us to test the ingress VLAN rules that we added above. ### EXAMPLE 1: Packet on Trunk Port Here's a test of a packet coming in on the trunk port: ovs-appctl ofproto/trace br0 in_port=1,vlan_tci=5 The output shows the lookup in table 0, the resubmit to table 1, and the resubmit to table 2 (which does nothing because we haven't put anything there yet): Flow: metadata=0,in_port=1,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 Rule: table=0 cookie=0 priority=0 OpenFlow actions=resubmit(,1) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=1 cookie=0 priority=99,in_port=1 OpenFlow actions=resubmit(,2) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop No match Final flow: unchanged Datapath actions: drop ### EXAMPLE 2: Valid Packet on Access Port Here's a test of a valid packet (a packet without an 802.1Q header) coming in on access port p2: ovs-appctl ofproto/trace br0 in_port=2 The output is similar to that for the previous case, except that it additionally tags the packet with p2's VLAN 20 before it passes it along to table 2: Flow: metadata=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 Rule: table=0 cookie=0 priority=0 OpenFlow actions=resubmit(,1) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=1 cookie=0 priority=99,in_port=2,vlan_tci=0x0000 OpenFlow actions=mod_vlan_vid:20,resubmit(,2) Resubmitted flow: metadata=0,in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop No match Final flow: unchanged Datapath actions: drop ### EXAMPLE 3: Invalid Packet on Access Port This tests an invalid packet (one that includes an 802.1Q header) coming in on access port p2: ovs-appctl ofproto/trace br0 in_port=2,vlan_tci=5 The output shows the packet matching the default drop rule: Flow: metadata=0,in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000 Rule: table=0 cookie=0 priority=0 OpenFlow actions=resubmit(,1) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=1 cookie=0 priority=0 OpenFlow actions=drop Final flow: unchanged Datapath actions: drop Implementing Table 2: MAC+VLAN Learning for Ingress Port -------------------------------------------------------- This table allows the switch we're implementing to learn that the packet's source MAC is located on the packet's ingress port in the packet's VLAN. > This table is a good example why table 1 added a VLAN tag to > packets that entered the switch through an access port. We want > to associate a MAC+VLAN with a port regardless of whether the VLAN > in question was originally part of the packet or whether it was an > assumed VLAN associated with an access port. It only takes a single flow to do this. The following command adds it: ovs-ofctl add-flow br0 \ "table=2 actions=learn(table=10, NXM_OF_VLAN_TCI[0..11], \ NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]), \ resubmit(,3)" The "learn" action (an Open vSwitch extension to OpenFlow) modifies a flow table based on the content of the flow currently being processed. Here's how you can interpret each part of the "learn" action above: table=10 Modify flow table 10. This will be the MAC learning table. NXM_OF_VLAN_TCI[0..11] Make the flow that we add to flow table 10 match the same VLAN ID that the packet we're currently processing contains. This effectively scopes the MAC learning entry to a single VLAN, which is the ordinary behavior for a VLAN-aware switch. NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] Make the flow that we add to flow table 10 match, as Ethernet destination, the Ethernet source address of the packet we're currently processing. load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15] Whereas the preceding parts specify fields for the new flow to match, this specifies an action for the flow to take when it matches. The action is for the flow to load the ingress port number of the current packet into register 0 (a special field that is an Open vSwitch extension to OpenFlow). > A real use of "learn" for MAC learning would probably involve two > additional elements. First, the "learn" action would specify a > hard_timeout for the new flow, to enable a learned MAC to > eventually expire if no new packets were seen from a given source > within a reasonable interval. Second, one would usually want to > limit resource consumption by using the Flow_Table table in the > Open vSwitch configuration database to specify a maximum number of > flows in table 10. This definitely calls for examples. ### Testing Table 2 ### EXAMPLE 1 Try the following test command: ovs-appctl ofproto/trace br0 in_port=1,vlan_tci=20,dl_src=50:00:00:00:00:01 -generate The output shows that "learn" was executed, but it isn't otherwise informative, so we won't include it here. The `-generate` keyword is new. Ordinarily, `ofproto/trace` has no side effects: "output" actions do not actually output packets, "learn" actions do not actually modify the flow table, and so on. With `-generate`, though, `ofproto/trace` does execute "learn" actions. That's important now, because we want to see the effect of the "learn" action on table 10. You can see that by running: ovs-ofctl dump-flows br0 table=10 which (omitting the "duration" and "idle_age" fields, which will vary based on how soon you ran this command after the previous one, as well as some other uninteresting fields) prints something like: NXST_FLOW reply (xid=0x4): table=10, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15] You can see that the packet coming in on VLAN 20 with source MAC 50:00:00:00:00:01 became a flow that matches VLAN 20 (written in hexadecimal) and destination MAC 50:00:00:00:00:01. The flow loads port number 1, the input port for the flow we tested, into register 0. ### EXAMPLE 2 Here's a second test command: ovs-appctl ofproto/trace br0 in_port=2,dl_src=50:00:00:00:00:01 -generate The flow that this command tests has the same source MAC and VLAN as example 1, although the VLAN comes from an access port VLAN rather than an 802.1Q header. If we again dump the flows for table 10 with: ovs-ofctl dump-flows br0 table=10 then we see that the flow we saw previously has changed to indicate that the learned port is port 2, as we would expect: NXST_FLOW reply (xid=0x4): table=10, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x2->NXM_NX_REG0[0..15] Implementing Table 3: Look Up Destination Port ---------------------------------------------- This table figures out what port we should send the packet to based on the destination MAC and VLAN. That is, if we've learned the location of the destination (from table 2 processing some previous packet with that destination as its source), then we want to send the packet there. We need only one flow to do the lookup: ovs-ofctl add-flow br0 \ "table=3 priority=50 actions=resubmit(,10), resubmit(,4)" The flow's first action resubmits to table 10, the table that the "learn" action modifies. As you saw previously, the learned flows in this table write the learned port into register 0. If the destination for our packet hasn't been learned, then there will be no matching flow, and so the "resubmit" turns into a no-op. Because registers are initialized to 0, we can use a register 0 value of 0 in our next pipeline stage as a signal to flood the packet. The second action resubmits to table 4, continuing to the next pipeline stage. We can add another flow to skip the learning table lookup for multicast and broadcast packets, since those should always be flooded: ovs-ofctl add-flow br0 \ "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 \ actions=resubmit(,4)" > We don't strictly need to add this flow, because multicast > addresses will never show up in our learning table. (In turn, > that's because we put a flow into table 0 to drop packets that > have a multicast source address.) ### Testing Table 3 ### EXAMPLE Here's a command that should cause OVS to learn that f0:00:00:00:00:01 is on p1 in VLAN 20: ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 -generate Here's an excerpt from the output that shows (from the "no match" looking up the resubmit to table 10) that the flow's destination was unknown: Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=3 cookie=0 priority=50 OpenFlow actions=resubmit(,10),resubmit(,4) Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop No match You can verify that the packet's source was learned two ways. The most direct way is to dump the learning table with: ovs-ofctl dump-flows br0 table=10 which ought to show roughly the following, with extraneous details removed: table=10, vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15] > If you tried the examples for the previous step, or if you did > some of your own experiments, then you might see additional flows > there. These additional flows are harmless. If they bother you, > then you can remove them with `ovs-ofctl del-flows br0 table=10`. The other way is to inject a packet to take advantage of the learning entry. For example, we can inject a packet on p2 whose destination is the MAC address that we just learned on p1: ovs-appctl ofproto/trace br0 in_port=2,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01 -generate Here's an interesting excerpt from that command's output. This group of lines traces the "resubmit(,10)", showing that the packet matched the learned flow for the first MAC we used, loading the OpenFlow port number for the learned port p1 into register 0: Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01 OpenFlow actions=load:0x1->NXM_NX_REG0[0..15] If you read the commands above carefully, then you might have noticed that they simply have the Ethernet source and destination addresses exchanged. That means that if we now rerun the first `ovs-appctl` command above, e.g.: ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 -generate then we see in the output that the destination has now been learned: Resubmitted flow: unchanged Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 Resubmitted odp: drop Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=90:00:00:00:00:01 OpenFlow actions=load:0x2->NXM_NX_REG0[0..15] Implementing Table 4: Output Processing --------------------------------------- At entry to stage 4, we know that register 0 contains either the desired output port or is zero if the packet should be flooded. We also know that the packet's VLAN is in its 802.1Q header, even if the VLAN was implicit because the packet came in on an access port. The job of the final pipeline stage is to actually output packets. The job is trivial for output to our trunk port p1: ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1" For output to the access ports, we just have to strip the VLAN header before outputting the packet: ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=2 actions=strip_vlan,2 table=4 reg0=3 actions=strip_vlan,3 table=4 reg0=4 actions=strip_vlan,4 EOF The only slightly tricky part is flooding multicast and broadcast packets and unicast packets with unlearned destinations. For those, we need to make sure that we only output the packets to the ports that carry our packet's VLAN, and that we include the 802.1Q header in the copy output to the trunk port but not in copies output to access ports: ovs-ofctl add-flows br0 - <<'EOF' table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2 table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4 table=4 reg0=0 priority=50 actions=1 EOF > Our rules rely on the standard OpenFlow behavior that an output > action will not forward a packet back out the port it came in on. > That is, if a packet comes in on p1, and we've learned that the > packet's destination MAC is also on p1, so that we end up with > "actions=1" as our actions, the switch will not forward the packet > back out its input port. The multicast/broadcast/unknown > destination cases above also rely on this behavior. ### Testing Table 4 ### EXAMPLE 1: Broadcast, Multicast, and Unknown Destination Try tracing a broadcast packet arriving on p1 in VLAN 30: ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=30 The interesting part of the output is the final line, which shows that the switch would remove the 802.1Q header and then output the packet to p3 and p4, which are access ports for VLAN 30: Datapath actions: pop_vlan,3,4 Similarly, if we trace a broadcast packet arriving on p3: ovs-appctl ofproto/trace br0 in_port=3,dl_dst=ff:ff:ff:ff:ff:ff then we see that it is output to p1 with an 802.1Q tag and then to p4 without one: Datapath actions: push_vlan(vid=30,pcp=0),1,pop_vlan,4 > Open vSwitch could simplify the datapath actions here to just > "4,push_vlan(vid=30,pcp=0),1" but it is not smart enough to do so. The following are also broadcasts, but the result is to drop the packets because the VLAN only belongs to the input port: ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=55 Try some other broadcast cases on your own: ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=20 ovs-appctl ofproto/trace br0 in_port=2,dl_dst=ff:ff:ff:ff:ff:ff ovs-appctl ofproto/trace br0 in_port=4,dl_dst=ff:ff:ff:ff:ff:ff You can see the same behavior with multicast packets and with unicast packets whose destination has not been learned, e.g.: ovs-appctl ofproto/trace br0 in_port=4,dl_dst=01:00:00:00:00:00 ovs-appctl ofproto/trace br0 in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=20 ovs-appctl ofproto/trace br0 in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=30 ### EXAMPLE 2: MAC Learning Let's follow the same pattern as we did for table 3. First learn a MAC on port p1 in VLAN 30: ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 -generate You can see from the last line of output that the packet's destination is unknown, so it gets flooded to both p3 and p4, the other ports in VLAN 30: Datapath actions: pop_vlan,3,4 Then reverse the MACs and learn the first flow's destination on port p4: ovs-appctl ofproto/trace br0 in_port=4,dl_src=20:00:00:00:00:01,dl_dst=10:00:00:00:00:01 -generate The last line of output shows that the this packet's destination is known to be p1, as learned from our previous command: Datapath actions: push_vlan(vid=30,pcp=0),1 Now, if we rerun our first command: ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 -generate we can see that the result is no longer a flood but to the specified learned destination port p4: Datapath actions: pop_vlan,4 Contact ======= bugs@openvswitch.org http://openvswitch.org/ [INSTALL.md]:../INSTALL.md openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-setup0000644000000000000000000000013213534540071017033 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.289849799 openvswitch-2.5.9/tutorial/t-setup0000755000175000017500000000031313534540071020521 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure for i in 1 2 3 4; do ovs-vsctl add-port br0 p$i -- set Interface p$i ofport_request=$i ovs-ofctl mod-port br0 p$i up done openvswitch-2.5.9/tutorial/PaxHeaders.82075/t-stage20000644000000000000000000000013213534540071017060 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.293849828 openvswitch-2.5.9/tutorial/t-stage20000755000175000017500000000042413534540071020551 0ustar00jpettitjpettit00000000000000#! /bin/sh -ve ovs-ofctl add-flow br0 \ "table=2 actions=learn(table=10, NXM_OF_VLAN_TCI[0..11], \ NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]), \ resubmit(,3)" openvswitch-2.5.9/tutorial/PaxHeaders.82075/ovs-sandbox0000644000000000000000000000013213534540071017675 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.289849799 openvswitch-2.5.9/tutorial/ovs-sandbox0000755000175000017500000002520513534540071021372 0ustar00jpettitjpettit00000000000000#! /bin/sh # # Copyright (c) 2013, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e run() { (cd "$sandbox" && "$@") || exit 1 } run_xterm() { title=$1; shift run xterm -T "$title" -e "$@" & } rungdb() { under_gdb=$1 gdb_run=$2 shift shift # Remove the --detach and to put the process under gdb control. # Also remove --vconsole:off to allow error message to show up # on the console. # Use "DISPLAY" variable to determine out if X is supported if $under_gdb && [ "$DISPLAY" ]; then args=`echo $@ |sed s/--detach//g | sed s/--vconsole:off//g` xterm_title=$1 gdb_cmd="" if $gdb_run; then gdb_cmd="-ex run" fi run_xterm $xterm_title gdb $gdb_cmd --args $args else run $@ fi } gdb_vswitchd=false gdb_ovsdb=false gdb_vswitchd_ex=false gdb_ovsdb_ex=false gdb_ovn_northd=false gdb_ovn_northd_ex=false gdb_ovn_controller=false gdb_ovn_controller_ex=false gdb_ovn_controller_vtep=false gdb_ovn_controller_vtep_ex=false builddir= srcdir= schema= installed=false built=false ovn=false ovnsb_schema= ovnnb_schema= for option; do # This option-parsing mechanism borrowed from a Autoconf-generated # configure script under the following license: # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2009, 2013 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # If the previous option needs an argument, assign it. if test -n "$prev"; then eval $prev=\$option prev= continue fi case $option in *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;; *) optarg=yes ;; esac case $dashdash$option in --) dashdash=yes ;; -h|--help) cat <&2 exit 1 ;; *) echo "$option: non-option arguments not supported (use --help for help)" >&2 exit 1 ;; esac shift done if $installed && $built; then echo "sorry, conflicting options (use --help for help)" >&2 exit 1 elif $installed || $built; then : elif test -e vswitchd/ovs-vswitchd; then built=: builddir=. elif (ovs-vswitchd --version) >/dev/null 2>&1; then installed=: else echo "can't find an OVS build or install (use --help for help)" >&2 exit 1 fi if $built; then if test ! -e "$builddir"/vswitchd/ovs-vswitchd; then echo "$builddir does not appear to be an OVS build directory" >&2 exit 1 fi builddir=`cd $builddir && pwd` # Find srcdir. case $srcdir in '') srcdir=$builddir if test ! -e "$srcdir"/WHY-OVS.md; then srcdir=`cd $builddir/.. && pwd` fi ;; /*) ;; *) srcdir=`pwd`/$srcdir ;; esac schema=$srcdir/vswitchd/vswitch.ovsschema if test ! -e "$schema"; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi if $ovn; then ovnsb_schema=$srcdir/ovn/ovn-sb.ovsschema if test ! -e "$ovnsb_schema"; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi ovnnb_schema=$srcdir/ovn/ovn-nb.ovsschema if test ! -e "$ovnnb_schema"; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi vtep_schema=$srcdir/vtep/vtep.ovsschema if test ! -e "$vtep_schema"; then echo >&2 'source directory not found, please use --srcdir' exit 1 fi fi # Put built tools early in $PATH. if test ! -e $builddir/vswitchd/ovs-vswitchd; then echo >&2 'build not found, please change set $builddir or change directory' exit 1 fi PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$builddir/vtep:$PATH if $ovn; then PATH=$builddir/ovn/controller:$builddir/ovn/controller-vtep:$builddir/ovn/northd:$builddir/ovn/utilities:$PATH fi export PATH else case $schema in '') for schema in \ /usr/local/share/openvswitch/vswitch.ovsschema \ /usr/share/openvswitch/vswitch.ovsschema \ none; do if test -r $schema; then break fi done ;; /*) ;; *) schema=`pwd`/$schema ;; esac if test ! -r "$schema"; then echo "can't find vswitch.ovsschema, please specify --schema" >&2 exit 1 fi if $ovn; then echo "running with ovn is only supported from the build dir." >&2 exit 1 fi fi # Create sandbox. rm -rf sandbox mkdir sandbox sandbox=`cd sandbox && pwd` # Set up environment for OVS programs to sandbox themselves. OVS_RUNDIR=$sandbox; export OVS_RUNDIR OVS_LOGDIR=$sandbox; export OVS_LOGDIR OVS_DBDIR=$sandbox; export OVS_DBDIR OVS_SYSCONFDIR=$sandbox; export OVS_SYSCONFDIR if $built; then # Easy access to OVS manpages. (cd "$builddir" && ${MAKE} install-man mandir="$sandbox"/man) MANPATH=$sandbox/man:; export MANPATH fi # Ensure cleanup. trap 'kill `cat "$sandbox"/*.pid`' 0 1 2 3 13 14 15 # Create database and start ovsdb-server. touch "$sandbox"/.conf.db.~lock~ run ovsdb-tool create conf.db "$schema" ovsdb_server_args= if $ovn; then touch "$sandbox"/.ovnsb.db.~lock~ touch "$sandbox"/.ovnnb.db.~lock~ run ovsdb-tool create ovnsb.db "$ovnsb_schema" run ovsdb-tool create ovnnb.db "$ovnnb_schema" run ovsdb-tool create vtep.db "$vtep_schema" ovsdb_server_args="ovnsb.db ovnnb.db vtep.db conf.db" fi rungdb $gdb_ovsdb $gdb_ovsdb_ex ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file \ --remote=punix:"$sandbox"/db.sock $ovsdb_server_args #Add a small delay to allow ovsdb-server to launch. sleep 0.1 #Wait for ovsdb-server to finish launching. if test ! -e "$sandbox"/db.sock; then echo -n "Waiting for ovsdb-server to start..." while test ! -e "$sandbox"/db.sock; do sleep 1; done echo " Done" fi # Initialize database. run ovs-vsctl --no-wait -- init # Start ovs-vswitchd. rungdb $gdb_vswitchd $gdb_vswitchd_ex ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file \ --enable-dummy=override -vvconn -vnetdev_dummy if $ovn; then ovs-vsctl set open . external-ids:system-id=56b18105-5706-46ef-80c4-ff20979ab068 ovs-vsctl set open . external-ids:ovn-remote=unix:"$sandbox"/db.sock ovs-vsctl set open . external-ids:ovn-encap-type=geneve ovs-vsctl set open . external-ids:ovn-encap-ip=127.0.0.1 rungdb $gdb_ovn_northd $gdb_ovn_northd_ex ovn-northd --detach --no-chdir --pidfile -vconsole:off --log-file rungdb $gdb_ovn_controller $gdb_ovn_controller_ex ovn-controller --detach --no-chdir --pidfile -vconsole:off --log-file rungdb $gdb_ovn_controller_vtep $gdb_ovn_controller_vtep_ex ovn-controller-vtep --detach --no-chdir --pidfile -vconsole:off --log-file fi cat </] : ` - `[PATCH /]` indicates that this is the nth of a series of m patches. It helps reviewers to read patches in the correct order. You may omit this prefix if you are sending only one patch. - `:` indicates the area of the Open vSwitch to which the change applies (often the name of a source file or a directory). You may omit it if the change crosses multiple distinct pieces of code. - `` briefly describes the change. The subject, minus the `[PATCH /]` prefix, becomes the first line of the commit's change log message. Description ----------- The body of the email should start with a more thorough description of the change. This becomes the body of the commit message, following the subject. There is no need to duplicate the summary given in the subject. Please limit lines in the description to 79 characters in width. The description should include: - The rationale for the change. - Design description and rationale (but this might be better added as code comments). - Testing that you performed (or testing that should be done but you could not for whatever reason). - Tags (see below). There is no need to describe what the patch actually changed, if the reader can see it for himself. If the patch refers to a commit already in the Open vSwitch repository, please include both the commit number and the subject of the patch, e.g. 'commit 632d136c (vswitch: Remove restriction on datapath names.)'. If you, the person sending the patch, did not write the patch yourself, then the very first line of the body should take the form `From: `, followed by a blank line. This will automatically cause the named author to be credited with authorship in the repository. Tags ---- The description ends with a series of tags, written one to a line as the last paragraph of the email. Each tag indicates some property of the patch in an easily machine-parseable manner. Examples of common tags follow. Signed-off-by: Author Name Informally, this indicates that Author Name is the author or submitter of a patch and has the authority to submit it under the terms of the license. The formal meaning is to agree to the Developer's Certificate of Origin (see below). If the author and submitter are different, each must sign off. If the patch has more than one author, all must sign off. Signed-off-by: Author Name Signed-off-by: Submitter Name Co-authored-by: Author Name Git can only record a single person as the author of a given patch. In the rare event that a patch has multiple authors, one must be given the credit in Git and the others must be credited via Co-authored-by: tags. (All co-authors must also sign off.) Acked-by: Reviewer Name Reviewers will often give an Acked-by: tag to code of which they approve. It is polite for the submitter to add the tag before posting the next version of the patch or applying the patch to the repository. Quality reviewing is hard work, so this gives a small amount of credit to the reviewer. Not all reviewers give Acked-by: tags when they provide positive reviews. It's customary only to add tags from reviewers who actually provide them explicitly. Tested-by: Tester Name When someone tests a patch, it is customary to add a Tested-by: tag indicating that. It's rare for a tester to actually provide the tag; usually the patch submitter makes the tag himself in response to an email indicating successful testing results. Tested-at: When a test report is publicly available, this provides a way to reference it. Typical s would be build logs from autobuilders or references to mailing list archives. Some autobuilders only retain their logs for a limited amount of time. It is less useful to cite these because they may be dead links for a developer reading the commit message months or years later. Reported-by: Reporter Name When a patch fixes a bug reported by some person, please credit the reporter in the commit log in this fashion. Please also add the reporter's name and email address to the list of people who provided helpful bug reports in the AUTHORS file at the top of the source tree. Fairly often, the reporter of a bug also tests the fix. Occasionally one sees a combined "Reported-and-tested-by:" tag used to indicate this. It is also acceptable, and more common, to include both tags separately. (If a bug report is received privately, it might not always be appropriate to publicly credit the reporter. If in doubt, please ask the reporter.) Requested-by: Requester Name Suggested-by: Suggester Name When a patch implements a request or a suggestion made by some person, please credit that person in the commit log in this fashion. For a helpful suggestion, please also add the person's name and email address to the list of people who provided suggestions in the AUTHORS file at the top of the source tree. (If a suggestion or a request is received privately, it might not always be appropriate to publicly give credit. If in doubt, please ask.) Reported-at: If a patch fixes or is otherwise related to a bug reported in a public bug tracker, please include a reference to the bug in the form of a URL to the specific bug, e.g.: Reported-at: https://bugs.debian.org/743635 This is also an appropriate way to refer to bug report emails in public email archives, e.g.: Reported-at: http://openvswitch.org/pipermail/dev/2014-June/040952.html VMware-BZ: #1234567 ONF-JIRA: EXT-12345 If a patch fixes or is otherwise related to a bug reported in a private bug tracker, you may include some tracking ID for the bug for your own reference. Please include some identifier to make the origin clear, e.g. "VMware-BZ" refers to VMware's internal Bugzilla instance and "ONF-JIRA" refers to the Open Networking Foundation's JIRA bug tracker. Bug #1234567. Issue: 1234567 These are obsolete forms of VMware-BZ: that can still be seen in old change log entries. (They are obsolete because they do not tell the reader what bug tracker is referred to.) Developer's Certificate of Origin --------------------------------- To help track the author of a patch as well as the submission chain, and be clear that the developer has authority to submit a patch for inclusion in openvswitch please sign off your work. The sign off certifies the following: Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. Feature Deprecation Guidelines ------------------------------ Open vSwitch is intended to be user friendly. This means that under normal circumstances we don't abruptly remove features from OVS that some users might still be using. Otherwise, if we would, then we would possibly break our user setup when they upgrade and would receive bug reports. Typical process to deprecate a feature in Open vSwitch is to: (a) Mention deprecation of a feature in the NEWS file. Also, mention expected release or absolute time when this feature would be removed from OVS altogether. Don't use relative time (e.g. "in 6 months") because that is not clearly interpretable. (b) If Open vSwitch is configured to use deprecated feature it should print a warning message to the log files clearly indicating that feature is deprecated and that use of it should be avoided. (c) If this feature is mentioned in man pages, then add "Deprecated" keyword to it. Also, if there is alternative feature to the one that is about to be marked as deprecated, then mention it in (a), (b) and (c) as well. Remember to followup and actually remove the feature from OVS codebase once deprecation grace period has expired and users had opportunity to use at least one OVS release that would have informed them about feature deprecation! Comments -------- If you want to include any comments in your email that should not be part of the commit's change log message, put them after the description, separated by a line that contains just `---`. It may be helpful to include a diffstat here for changes that touch multiple files. Patch ----- The patch should be in the body of the email following the description, separated by a blank line. Patches should be in `diff -up` format. We recommend that you use Git to produce your patches, in which case you should use the `-M -C` options to `git diff` (or other Git tools) if your patch renames or copies files. Quilt (http://savannah.nongnu.org/projects/quilt) might be useful if you do not want to use Git. Patches should be inline in the email message. Some email clients corrupt white space or wrap lines in patches. There are hints on how to configure many email clients to avoid this problem at: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/email-clients.txt If you cannot convince your email client not to mangle patches, then sending the patch as an attachment is a second choice. Please follow the style used in the code that you are modifying. The [CodingStyle.md] file describes the coding style used in most of Open vSwitch. Use Linux kernel coding style for Linux kernel code. Example ------- ``` From fa29a1c2c17682879e79a21bb0cdd5bbe67fa7c0 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 8 Dec 2011 13:17:24 -0800 Subject: [PATCH] datapath: Alphabetize include/net/ipv6.h compat header. Signed-off-by: Jesse Gross --- datapath/linux/Modules.mk | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index fdd952e..f6cb88e 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -56,11 +56,11 @@ openvswitch_headers += \ linux/compat/include/net/dst.h \ linux/compat/include/net/genetlink.h \ linux/compat/include/net/ip.h \ + linux/compat/include/net/ipv6.h \ linux/compat/include/net/net_namespace.h \ linux/compat/include/net/netlink.h \ linux/compat/include/net/protocol.h \ linux/compat/include/net/route.h \ - linux/compat/include/net/ipv6.h \ linux/compat/genetlink.inc both_modules += brcompat -- 1.7.7.3 ``` [INSTALL.md]:INSTALL.md [CodingStyle.md]:CodingStyle.md openvswitch-2.5.9/PaxHeaders.82075/INSTALL.KVM.md0000644000000000000000000000013213534540071015730 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.037685841 30 ctime=1567801423.673845258 openvswitch-2.5.9/INSTALL.KVM.md0000644000175000017500000000445413534540071017425 0ustar00jpettitjpettit00000000000000How to Use Open vSwitch with KVM ================================= This document describes how to use Open vSwitch with the Kernel-based Virtual Machine (KVM). This document assumes that you have read and followed [INSTALL.md] to get Open vSwitch setup on your Linux system. Setup ----- First, follow the setup instructions in [INSTALL.md] to get a working Open vSwitch installation. KVM uses tunctl to handle various bridging modes, which you can install with the Debian/Ubuntu package uml-utilities. % apt-get install uml-utilities Next, you will need to modify or create custom versions of the qemu-ifup and qemu-ifdown scripts. In this guide, we'll create custom versions that make use of example Open vSwitch bridges that we'll describe in this guide. Create the following two files and store them in known locations. For example /etc/ovs-ifup and /etc/ovs-ifdown /etc/ovs-ifup ``` #!/bin/sh switch='br0' /sbin/ifconfig $1 0.0.0.0 up ovs-vsctl add-port ${switch} $1 ``` /etc/ovs-ifdown ``` #!/bin/sh switch='br0' /sbin/ifconfig $1 0.0.0.0 down ovs-vsctl del-port ${switch} $1 ``` At the end of [INSTALL.md], it describes basic usage of creating bridges and ports. If you haven't already, create a bridge named br0 with the following command: % ovs-vsctl add-br br0 Then, add a port to the bridge for the NIC that you want your guests to communicate over (e.g. eth0): % ovs-vsctl add-port br0 eth0 Please refer to ovs-vsctl(8) for more details. Next, we'll start a guest that will use our ifup and ifdown scripts. % kvm -m 512 -net nic,macaddr=00:11:22:EE:EE:EE -net \ tap,script=/etc/ovs-ifup,downscript=/etc/ovs-ifdown -drive \ file=/path/to/disk-image,boot=on This will start the guest and associate a tap device with it. The ovs-ifup script will add a port on the br0 bridge so that the guest will be able to communicate over that bridge. To get some more information and for debugging you can use Open vSwitch utilities such as ovs-dpctl and ovs-ofctl, For example: % ovs-dpctl show % ovs-ofctl show br0 You should see tap devices for each KVM guest added as ports to the bridge (e.g. tap0) Please refer to ovs-dpctl(8) and ovs-ofctl(8) for more details. Bug Reporting ------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/python0000644000000000000000000000013213534540120015103 xustar0030 mtime=1567801424.617852217 30 atime=1567801425.625859648 30 ctime=1567801424.617852217 openvswitch-2.5.9/python/0000755000175000017500000000000013534540120016646 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/PaxHeaders.82075/README.rst0000644000000000000000000000013213534540071016654 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801424.237849416 openvswitch-2.5.9/python/README.rst0000644000175000017500000000005513534540071020342 0ustar00jpettitjpettit00000000000000Python library for working with Open vSwitch openvswitch-2.5.9/python/PaxHeaders.82075/setup.py0000644000000000000000000000013213534540071016677 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.241849446 openvswitch-2.5.9/python/setup.py0000644000175000017500000000272713534540071020375 0ustar00jpettitjpettit00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import print_function import sys import setuptools VERSION = "unknown" try: # Try to set the version from the generated ovs/version.py execfile("ovs/version.py") except IOError: print("Ensure version.py is created by running make python/ovs/version.py", file=sys.stderr) sys.exit(-1) setuptools.setup( name='ovs', description='Open vSwitch library', version=VERSION, url='http://www.openvswitch.org/', author='Open vSwitch', author_email='dev@openvswitch.org', packages=['ovs', 'ovs.db', 'ovs.unixctl'], keywords=['openvswitch', 'ovs', 'OVSDB'], license='Apache 2.0', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Topic :: Database :: Front-Ends', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Networking', 'License :: OSI Approved :: Apache Software License' ] ) openvswitch-2.5.9/python/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017324 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801424.617852217 openvswitch-2.5.9/python/automake.mk0000644000175000017500000000624513534540071021021 0ustar00jpettitjpettit00000000000000ovstest_pyfiles = \ python/ovstest/__init__.py \ python/ovstest/args.py \ python/ovstest/rpcserver.py \ python/ovstest/tcp.py \ python/ovstest/tests.py \ python/ovstest/udp.py \ python/ovstest/util.py \ python/ovstest/vswitch.py ovs_pyfiles = \ python/ovs/__init__.py \ python/ovs/daemon.py \ python/ovs/db/__init__.py \ python/ovs/db/data.py \ python/ovs/db/error.py \ python/ovs/db/idl.py \ python/ovs/db/parser.py \ python/ovs/db/schema.py \ python/ovs/db/types.py \ python/ovs/fatal_signal.py \ python/ovs/json.py \ python/ovs/jsonrpc.py \ python/ovs/ovsuuid.py \ python/ovs/poller.py \ python/ovs/process.py \ python/ovs/reconnect.py \ python/ovs/socket_util.py \ python/ovs/stream.py \ python/ovs/timeval.py \ python/ovs/unixctl/__init__.py \ python/ovs/unixctl/client.py \ python/ovs/unixctl/server.py \ python/ovs/util.py \ python/ovs/version.py \ python/ovs/vlog.py # These python files are used at build time but not runtime, # so they are not installed. EXTRA_DIST += \ python/build/__init__.py \ python/build/nroff.py # PyPI support. EXTRA_DIST += \ python/README.rst \ python/setup.py PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) EXTRA_DIST += $(PYFILES) PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover) if HAVE_PYTHON nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles) ovs-install-data-local: $(MKDIR_P) python/ovs sed \ -e '/^##/d' \ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \ -e 's,[@]RUNDIR[@],$(RUNDIR),g' \ -e 's,[@]LOGDIR[@],$(LOGDIR),g' \ -e 's,[@]bindir[@],$(bindir),g' \ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ -e 's,[@]DBDIR[@],$(DBDIR),g' \ < $(srcdir)/python/ovs/dirs.py.template \ > python/ovs/dirs.py.tmp $(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs $(INSTALL_DATA) python/ovs/dirs.py.tmp $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py rm python/ovs/dirs.py.tmp python-sdist: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py (cd python/ && $(PYTHON) setup.py sdist) pypi-upload: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py (cd python/ && $(PYTHON) setup.py sdist upload) else ovs-install-data-local: @: endif install-data-local: ovs-install-data-local UNINSTALL_LOCAL += ovs-uninstall-local ovs-uninstall-local: rm -f $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py ALL_LOCAL += $(srcdir)/python/ovs/version.py $(srcdir)/python/ovs/version.py: config.status $(AM_V_GEN)$(ro_shell) > $(@F).tmp && \ echo 'VERSION = "$(VERSION)"' >> $(@F).tmp && \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi ALL_LOCAL += $(srcdir)/python/ovs/dirs.py $(srcdir)/python/ovs/dirs.py: python/ovs/dirs.py.template $(AM_V_GEN)sed \ -e '/^##/d' \ -e 's,[@]pkgdatadir[@],/usr/local/share/openvswitch,g' \ -e 's,[@]RUNDIR[@],/var/run,g' \ -e 's,[@]LOGDIR[@],/usr/local/var/log,g' \ -e 's,[@]bindir[@],/usr/local/bin,g' \ -e 's,[@]sysconfdir[@],/usr/local/etc,g' \ -e 's,[@]DBDIR[@],/usr/local/etc/openvswitch,g' \ < $? > $@.tmp && \ mv $@.tmp $@ EXTRA_DIST += python/ovs/dirs.py.template openvswitch-2.5.9/python/PaxHeaders.82075/ovstest0000644000000000000000000000013213534540120016612 xustar0030 mtime=1567801424.285849769 30 atime=1567801425.625859648 30 ctime=1567801424.285849769 openvswitch-2.5.9/python/ovstest/0000755000175000017500000000000013534540120020355 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/util.py0000644000000000000000000000013213534540071020223 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.285849769 openvswitch-2.5.9/python/ovstest/util.py0000644000175000017500000001470513534540071021720 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ util module contains some helper function """ import array import exceptions import fcntl import os import select import socket import struct import signal import subprocess import re import xmlrpclib def str_ip(ip_address): """ Converts an IP address from binary format to a string. """ (x1, x2, x3, x4) = struct.unpack("BBBB", ip_address) return ("%u.%u.%u.%u") % (x1, x2, x3, x4) def get_interface_mtu(iface): """ Returns MTU of the given interface. """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) indata = iface + ('\0' * (32 - len(iface))) try: outdata = fcntl.ioctl(s.fileno(), 0x8921, indata) # socket.SIOCGIFMTU mtu = struct.unpack("16si12x", outdata)[1] except: return 0 return mtu def get_interface(address): """ Finds first interface that has given address """ bytes = 256 * 32 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) names = array.array('B', '\0' * bytes) outbytes = struct.unpack('iL', fcntl.ioctl( s.fileno(), 0x8912, # SIOCGIFCONF struct.pack('iL', bytes, names.buffer_info()[0]) ))[0] namestr = names.tostring() for i in range(0, outbytes, 40): name = namestr[i:i + 16].split('\0', 1)[0] if address == str_ip(namestr[i + 20:i + 24]): return name return None # did not find interface we were looking for def uname(): os_info = os.uname() return os_info[2] # return only the kernel version number def start_process(args): try: p = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) out, err = p.communicate() return (p.returncode, out, err) except exceptions.OSError: return (-1, None, None) def get_driver(iface): ret, out, _err = start_process(["ethtool", "-i", iface]) if ret == 0: lines = out.splitlines() driver = "%s(%s)" % (lines[0], lines[1]) # driver name + version else: driver = None return driver def interface_up(iface): """ This function brings given iface up. """ ret, _out, _err = start_process(["ifconfig", iface, "up"]) return ret def interface_assign_ip(iface, ip_addr, mask): """ This function allows to assign IP address to an interface. If mask is an empty string then ifconfig will decide what kind of mask to use. The caller can also specify the mask by using CIDR notation in ip argument by leaving the mask argument as an empty string. In case of success this function returns 0. """ args = ["ifconfig", iface, ip_addr] if mask is not None: args.append("netmask") args.append(mask) ret, _out, _err = start_process(args) return ret def interface_get_ip(iface): """ This function returns tuple - ip and mask that was assigned to the interface. """ args = ["ifconfig", iface] ret, out, _err = start_process(args) if ret == 0: ip = re.search(r'inet addr:(\S+)', out) mask = re.search(r'Mask:(\S+)', out) if ip is not None and mask is not None: return (ip.group(1), mask.group(1)) else: return ret def move_routes(iface1, iface2): """ This function moves routes from iface1 to iface2. """ args = ["ip", "route", "show", "dev", iface1] ret, out, _err = start_process(args) if ret == 0: for route in out.splitlines(): args = ["ip", "route", "replace", "dev", iface2] + route.split() start_process(args) def get_interface_from_routing_decision(ip): """ This function returns the interface through which the given ip address is reachable. """ args = ["ip", "route", "get", ip] ret, out, _err = start_process(args) if ret == 0: iface = re.search(r'dev (\S+)', out) if iface: return iface.group(1) return None def rpc_client(ip, port): return xmlrpclib.Server("http://%s:%u/" % (ip, port), allow_none=True) def sigint_intercept(): """ Intercept SIGINT from child (the local ovs-test server process). """ signal.signal(signal.SIGINT, signal.SIG_IGN) def start_local_server(port): """ This function spawns an ovs-test server that listens on specified port and blocks till the spawned ovs-test server is ready to accept XML RPC connections. """ p = subprocess.Popen(["ovs-test", "-s", str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=sigint_intercept) fcntl.fcntl( p.stdout.fileno(),fcntl.F_SETFL, fcntl.fcntl(p.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK) while p.poll() is None: fd = select.select([p.stdout.fileno()], [], [])[0] if fd: out = p.stdout.readline() if out.startswith("Starting RPC server"): break if p.poll() is not None: raise RuntimeError("Couldn't start local instance of ovs-test server") return p def get_datagram_sizes(mtu1, mtu2): """ This function calculates all the "interesting" datagram sizes so that we test both - receive and send side with different packets sizes. """ s1 = set([8, mtu1 - 100, mtu1 - 28, mtu1]) s2 = set([8, mtu2 - 100, mtu2 - 28, mtu2]) return sorted(s1.union(s2)) def ip_from_cidr(string): """ This function removes the netmask (if present) from the given string and returns the IP address. """ token = string.split("/") return token[0] def bandwidth_to_string(bwidth): """Convert bandwidth from long to string and add units.""" bwidth = bwidth * 8 # Convert back to bits/second if bwidth >= 10000000: return str(int(bwidth / 1000000)) + "Mbps" elif bwidth > 10000: return str(int(bwidth / 1000)) + "Kbps" else: return str(int(bwidth)) + "bps" openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/rpcserver.py0000644000000000000000000000013213534540071021261 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.277849711 openvswitch-2.5.9/python/ovstest/rpcserver.py0000644000175000017500000002647113534540071022761 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ rpcserver is an XML RPC server that allows RPC client to initiate tests """ import exceptions import sys import xmlrpclib from twisted.internet import reactor from twisted.internet.error import CannotListenError from twisted.web import xmlrpc from twisted.web import server import tcp import udp import util import vswitch class TestArena(xmlrpc.XMLRPC): """ This class contains all the functions that ovs-test client will call remotely. The caller is responsible to use designated handleIds for designated methods (e.g. do not mix UDP and TCP handles). """ def __init__(self): xmlrpc.XMLRPC.__init__(self, allowNone=True) self.handle_id = 1 self.handle_map = {} self.bridges = set() self.pbridges = set() self.ports = set() self.request = None def __acquire_handle(self, value): """ Allocates new handle and assigns value object to it """ handle = self.handle_id self.handle_map[handle] = value self.handle_id += 1 return handle def __get_handle_resources(self, handle): """ Return resources that were assigned to handle """ return self.handle_map[handle] def __delete_handle(self, handle): """ Releases handle from handle_map """ del self.handle_map[handle] def cleanup(self): """ Delete all remaining bridges and ports if ovs-test client did not had a chance to remove them. It is necessary to call this function if ovs-test server is abruptly terminated when doing the tests. """ for port in self.ports: # Remove ports that were added to existing bridges vswitch.ovs_vsctl_del_port_from_bridge(port) for bridge in self.bridges: # Remove bridges that were added for L3 tests vswitch.ovs_vsctl_del_bridge(bridge) for pbridge in self.pbridges: # Remove bridges that were added for VLAN tests vswitch.ovs_vsctl_del_pbridge(pbridge[0], pbridge[1]) def render(self, request): """ This method overrides the original XMLRPC.render method so that it would be possible to get the XML RPC client IP address from the request object. """ self.request = request return xmlrpc.XMLRPC.render(self, request) def xmlrpc_get_my_address(self): """ Returns the RPC client's IP address. """ return self.request.getClientIP() def xmlrpc_get_my_address_from(self, his_ip, his_port): """ Returns the ovs-test server IP address that the other ovs-test server with the given ip will see. """ server1 = xmlrpclib.Server("http://%s:%u/" % (his_ip, his_port)) return server1.get_my_address() def xmlrpc_create_udp_listener(self, port): """ Creates a UDP listener that will receive packets from UDP sender """ try: listener = udp.UdpListener() reactor.listenUDP(port, listener) handle_id = self.__acquire_handle(listener) except CannotListenError: return -1 return handle_id def xmlrpc_create_udp_sender(self, host, count, size, duration): """ Send UDP datagrams to UDP listener """ sender = udp.UdpSender(tuple(host), count, size, duration) reactor.listenUDP(0, sender) handle_id = self.__acquire_handle(sender) return handle_id def xmlrpc_get_udp_listener_results(self, handle): """ Returns number of datagrams that were received """ listener = self.__get_handle_resources(handle) return listener.getResults() def xmlrpc_get_udp_sender_results(self, handle): """ Returns number of datagrams that were sent """ sender = self.__get_handle_resources(handle) return sender.getResults() def xmlrpc_close_udp_listener(self, handle): """ Releases UdpListener and all its resources """ listener = self.__get_handle_resources(handle) listener.transport.stopListening() self.__delete_handle(handle) return 0 def xmlrpc_close_udp_sender(self, handle): """ Releases UdpSender and all its resources """ sender = self.__get_handle_resources(handle) sender.transport.stopListening() self.__delete_handle(handle) return 0 def xmlrpc_create_tcp_listener(self, port): """ Creates a TcpListener that will accept connection from TcpSender """ try: listener = tcp.TcpListenerFactory() port = reactor.listenTCP(port, listener) handle_id = self.__acquire_handle((listener, port)) return handle_id except CannotListenError: return -1 def xmlrpc_create_tcp_sender(self, his_ip, his_port, duration): """ Creates a TcpSender that will connect to TcpListener """ sender = tcp.TcpSenderFactory(duration) connector = reactor.connectTCP(his_ip, his_port, sender) handle_id = self.__acquire_handle((sender, connector)) return handle_id def xmlrpc_get_tcp_listener_results(self, handle): """ Returns number of bytes received """ (listener, _) = self.__get_handle_resources(handle) return listener.getResults() def xmlrpc_get_tcp_sender_results(self, handle): """ Returns number of bytes sent """ (sender, _) = self.__get_handle_resources(handle) return sender.getResults() def xmlrpc_close_tcp_listener(self, handle): """ Releases TcpListener and all its resources """ try: (_, port) = self.__get_handle_resources(handle) port.loseConnection() self.__delete_handle(handle) except exceptions.KeyError: return -1 return 0 def xmlrpc_close_tcp_sender(self, handle): """ Releases TcpSender and all its resources """ try: (_, connector) = self.__get_handle_resources(handle) connector.disconnect() self.__delete_handle(handle) except exceptions.KeyError: return -1 return 0 def xmlrpc_create_test_bridge(self, bridge, iface): """ This function creates a physical bridge from iface. It moves the IP configuration from the physical interface to the bridge. """ ret = vswitch.ovs_vsctl_add_bridge(bridge) if ret == 0: self.pbridges.add((bridge, iface)) util.interface_up(bridge) (ip_addr, mask) = util.interface_get_ip(iface) util.interface_assign_ip(bridge, ip_addr, mask) util.move_routes(iface, bridge) util.interface_assign_ip(iface, "0.0.0.0", "255.255.255.255") ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, iface) if ret == 0: self.ports.add(iface) else: util.interface_assign_ip(iface, ip_addr, mask) util.move_routes(bridge, iface) vswitch.ovs_vsctl_del_bridge(bridge) return ret def xmlrpc_del_test_bridge(self, bridge, iface): """ This function deletes the test bridge and moves its IP configuration back to the physical interface. """ ret = vswitch.ovs_vsctl_del_pbridge(bridge, iface) self.pbridges.discard((bridge, iface)) return ret def xmlrpc_get_iface_from_bridge(self, brname): """ Tries to figure out physical interface from bridge. """ return vswitch.ovs_get_physical_interface(brname) def xmlrpc_create_bridge(self, brname): """ Creates an OVS bridge. """ ret = vswitch.ovs_vsctl_add_bridge(brname) if ret == 0: self.bridges.add(brname) return ret def xmlrpc_del_bridge(self, brname): """ Deletes an OVS bridge. """ ret = vswitch.ovs_vsctl_del_bridge(brname) if ret == 0: self.bridges.discard(brname) return ret def xmlrpc_is_ovs_bridge(self, bridge): """ This function verifies whether given interface is an ovs bridge. """ return vswitch.ovs_vsctl_is_ovs_bridge(bridge) def xmlrpc_add_port_to_bridge(self, bridge, port): """ Adds a port to the OVS bridge. """ ret = vswitch.ovs_vsctl_add_port_to_bridge(bridge, port) if ret == 0: self.ports.add(port) return ret def xmlrpc_del_port_from_bridge(self, port): """ Removes a port from OVS bridge. """ ret = vswitch.ovs_vsctl_del_port_from_bridge(port) if ret == 0: self.ports.discard(port) return ret def xmlrpc_ovs_vsctl_set(self, table, record, column, key, value): """ This function allows to alter OVS database. """ return vswitch.ovs_vsctl_set(table, record, column, key, value) def xmlrpc_interface_up(self, iface): """ This function brings up given interface. """ return util.interface_up(iface) def xmlrpc_interface_assign_ip(self, iface, ip_address, mask): """ This function allows to assing ip address to the given interface. """ return util.interface_assign_ip(iface, ip_address, mask) def xmlrpc_get_interface(self, address): """ Finds first interface that has given address """ return util.get_interface(address) def xmlrpc_get_interface_mtu(self, iface): """ Returns MTU of the given interface """ return util.get_interface_mtu(iface) def xmlrpc_uname(self): """ Return information about running kernel """ return util.uname() def xmlrpc_get_driver(self, iface): """ Returns driver version """ return util.get_driver(iface) def xmlrpc_get_interface_from_routing_decision(self, ip): """ Returns driver version """ return util.get_interface_from_routing_decision(ip) def start_rpc_server(port): """ This function creates a RPC server and adds it to the Twisted Reactor. """ rpc_server = TestArena() reactor.listenTCP(port, server.Site(rpc_server)) try: print "Starting RPC server\n" sys.stdout.flush() # If this server was started from ovs-test client then we must flush # STDOUT so that client would know that server is ready to accept # XML RPC connections. reactor.run() finally: rpc_server.cleanup() openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/__init__.py0000644000000000000000000000013213534540071021005 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.273849681 openvswitch-2.5.9/python/ovstest/__init__.py0000644000175000017500000000004613534540071022473 0ustar00jpettitjpettit00000000000000# This file intentionally left blank. openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/tests.py0000644000000000000000000000013213534540071020410 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.281849741 openvswitch-2.5.9/python/ovstest/tests.py0000644000175000017500000002215713534540071022105 0ustar00jpettitjpettit00000000000000import math import time import ovstest.util as util DEFAULT_TEST_BRIDGE = "ovstestbr0" DEFAULT_TEST_PORT = "ovstestport0" DEFAULT_TEST_TUN = "ovstestport1" NO_HANDLE = -1 def do_udp_tests(receiver, sender, tbwidth, duration, port_sizes): """Schedule UDP tests between receiver and sender""" server1 = util.rpc_client(receiver[0], receiver[1]) server2 = util.rpc_client(sender[0], sender[1]) udpformat = '{0:>15} {1:>15} {2:>15} {3:>15} {4:>15}' print ("UDP test from %s:%u to %s:%u with target bandwidth %s" % (sender[0], sender[1], receiver[0], receiver[1], util.bandwidth_to_string(tbwidth))) print udpformat.format("Datagram Size", "Snt Datagrams", "Rcv Datagrams", "Datagram Loss", "Bandwidth") for size in port_sizes: listen_handle = NO_HANDLE send_handle = NO_HANDLE try: packetcnt = (tbwidth * duration) / size listen_handle = server1.create_udp_listener(receiver[3]) if listen_handle == NO_HANDLE: print ("Server could not open UDP listening socket on port" " %u. Try to restart the server.\n" % receiver[3]) return send_handle = server2.create_udp_sender( (util.ip_from_cidr(receiver[2]), receiver[3]), packetcnt, size, duration) # Using sleep here because there is no other synchronization # source that would notify us when all sent packets were received time.sleep(duration + 1) rcv_packets = server1.get_udp_listener_results(listen_handle) snt_packets = server2.get_udp_sender_results(send_handle) loss = math.ceil(((snt_packets - rcv_packets) * 10000.0) / snt_packets) / 100 bwidth = (rcv_packets * size) / duration print udpformat.format(size, snt_packets, rcv_packets, '%.2f%%' % loss, util.bandwidth_to_string(bwidth)) finally: if listen_handle != NO_HANDLE: server1.close_udp_listener(listen_handle) if send_handle != NO_HANDLE: server2.close_udp_sender(send_handle) print "\n" def do_tcp_tests(receiver, sender, duration): """Schedule TCP tests between receiver and sender""" server1 = util.rpc_client(receiver[0], receiver[1]) server2 = util.rpc_client(sender[0], sender[1]) tcpformat = '{0:>15} {1:>15} {2:>15}' print "TCP test from %s:%u to %s:%u (full speed)" % (sender[0], sender[1], receiver[0], receiver[1]) print tcpformat.format("Snt Bytes", "Rcv Bytes", "Bandwidth") listen_handle = NO_HANDLE send_handle = NO_HANDLE try: listen_handle = server1.create_tcp_listener(receiver[3]) if listen_handle == NO_HANDLE: print ("Server was unable to open TCP listening socket on port" " %u. Try to restart the server.\n" % receiver[3]) return send_handle = server2.create_tcp_sender(util.ip_from_cidr(receiver[2]), receiver[3], duration) time.sleep(duration + 1) rcv_bytes = long(server1.get_tcp_listener_results(listen_handle)) snt_bytes = long(server2.get_tcp_sender_results(send_handle)) bwidth = rcv_bytes / duration print tcpformat.format(snt_bytes, rcv_bytes, util.bandwidth_to_string(bwidth)) finally: if listen_handle != NO_HANDLE: server1.close_tcp_listener(listen_handle) if send_handle != NO_HANDLE: server2.close_tcp_sender(send_handle) print "\n" def do_l3_tests(node1, node2, bandwidth, duration, ps, type): """ Do L3 tunneling tests. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. """ server1 = util.rpc_client(node1[0], node1[1]) server2 = util.rpc_client(node2[0], node2[1]) servers_with_bridges = [] try: server1.create_bridge(DEFAULT_TEST_BRIDGE) servers_with_bridges.append(server1) server2.create_bridge(DEFAULT_TEST_BRIDGE) servers_with_bridges.append(server2) server1.interface_up(DEFAULT_TEST_BRIDGE) server2.interface_up(DEFAULT_TEST_BRIDGE) server1.interface_assign_ip(DEFAULT_TEST_BRIDGE, node1[2], None) server2.interface_assign_ip(DEFAULT_TEST_BRIDGE, node2[2], None) server1.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN) server2.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_TUN) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type", None, type) server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "type", None, type) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options", "remote_ip", node2[0]) server2.ovs_vsctl_set("Interface", DEFAULT_TEST_TUN, "options", "remote_ip", node1[0]) do_udp_tests(node1, node2, bandwidth, duration, ps) do_udp_tests(node2, node1, bandwidth, duration, ps) do_tcp_tests(node1, node2, duration) do_tcp_tests(node2, node1, duration) finally: for server in servers_with_bridges: server.del_bridge(DEFAULT_TEST_BRIDGE) def do_vlan_tests(node1, node2, bandwidth, duration, ps, tag): """ Do VLAN tests between node1 and node2. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. """ server1 = util.rpc_client(node1[0], node1[1]) server2 = util.rpc_client(node2[0], node2[1]) br_name1 = None br_name2 = None servers_with_test_ports = [] try: interface_node1 = server1.get_interface(node1[0]) interface_node2 = server2.get_interface(node2[0]) if server1.is_ovs_bridge(interface_node1): br_name1 = interface_node1 else: br_name1 = DEFAULT_TEST_BRIDGE server1.create_test_bridge(br_name1, interface_node1) if server2.is_ovs_bridge(interface_node2): br_name2 = interface_node2 else: br_name2 = DEFAULT_TEST_BRIDGE server2.create_test_bridge(br_name2, interface_node2) server1.add_port_to_bridge(br_name1, DEFAULT_TEST_PORT) servers_with_test_ports.append(server1) server2.add_port_to_bridge(br_name2, DEFAULT_TEST_PORT) servers_with_test_ports.append(server2) server1.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag) server2.ovs_vsctl_set("Port", DEFAULT_TEST_PORT, "tag", None, tag) server1.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, "internal") server2.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, "internal") server1.interface_assign_ip(DEFAULT_TEST_PORT, node1[2], None) server2.interface_assign_ip(DEFAULT_TEST_PORT, node2[2], None) server1.interface_up(DEFAULT_TEST_PORT) server2.interface_up(DEFAULT_TEST_PORT) do_udp_tests(node1, node2, bandwidth, duration, ps) do_udp_tests(node2, node1, bandwidth, duration, ps) do_tcp_tests(node1, node2, duration) do_tcp_tests(node2, node1, duration) finally: for server in servers_with_test_ports: server.del_port_from_bridge(DEFAULT_TEST_PORT) if br_name1 == DEFAULT_TEST_BRIDGE: server1.del_test_bridge(br_name1, interface_node1) if br_name2 == DEFAULT_TEST_BRIDGE: server2.del_test_bridge(br_name2, interface_node2) def do_direct_tests(node1, node2, bandwidth, duration, ps): """ Do tests between outer IPs without involving Open vSwitch. Each node is given as 4 tuple - physical interface IP, control port, test IP and test port. Direct tests will use physical interface IP as the test IP address. """ n1 = (node1[0], node1[1], node1[0], node1[3]) n2 = (node2[0], node2[1], node2[0], node2[3]) do_udp_tests(n1, n2, bandwidth, duration, ps) do_udp_tests(n2, n1, bandwidth, duration, ps) do_tcp_tests(n1, n2, duration) do_tcp_tests(n2, n1, duration) def configure_l3(conf, tunnel_mode): """ This function creates a temporary test bridge and adds an L3 tunnel. """ s = util.start_local_server(conf[1][1]) server = util.rpc_client("127.0.0.1", conf[1][1]) server.create_bridge(DEFAULT_TEST_BRIDGE) server.add_port_to_bridge(DEFAULT_TEST_BRIDGE, DEFAULT_TEST_PORT) server.interface_up(DEFAULT_TEST_BRIDGE) server.interface_assign_ip(DEFAULT_TEST_BRIDGE, conf[1][0], None) server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "type", None, tunnel_mode) server.ovs_vsctl_set("Interface", DEFAULT_TEST_PORT, "options", "remote_ip", conf[0]) return s openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/tcp.py0000644000000000000000000000013213534540071020034 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.281849741 openvswitch-2.5.9/python/ovstest/tcp.py0000644000175000017500000000700713534540071021526 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ tcp module contains listener and sender classes for TCP protocol """ from twisted.internet.protocol import Factory, ClientFactory, Protocol from twisted.internet import interfaces from zope.interface import implements import time class TcpListenerConnection(Protocol): """ This per-connection class is instantiated each time sender connects """ def __init__(self): self.stats = 0 def dataReceived(self, data): self.stats += len(data) def connectionLost(self, reason): self.factory.stats += self.stats class TcpListenerFactory(Factory): """ This per-listening socket class is used to instantiate TcpListenerConnections """ protocol = TcpListenerConnection def __init__(self): self.stats = 0 def getResults(self): """ returns the number of bytes received as string""" # XML RPC does not support 64bit int (http://bugs.python.org/issue2985) # so we have to convert the amount of bytes into a string return str(self.stats) class Producer(object): implements(interfaces.IPushProducer) """ This producer class generates infinite byte stream for a specified time duration """ def __init__(self, proto, duration): self.proto = proto self.start = time.time() self.produced = 0 self.paused = False self.data = "X" * 65535 self.duration = duration def pauseProducing(self): """This function is called whenever write() to socket would block""" self.paused = True def resumeProducing(self): """This function is called whenever socket becomes writable""" self.paused = False current = time.time() while (not self.paused) and (current < self.start + self.duration): self.proto.transport.write(self.data) self.produced += len(self.data) current = time.time() if current >= self.start + self.duration: self.proto.factory.stats += self.produced self.proto.transport.unregisterProducer() self.proto.transport.loseConnection() def stopProducing(self): pass class TcpSenderConnection(Protocol): """ TCP connection instance class that sends all traffic at full speed. """ def connectionMade(self): producer = Producer(self, self.factory.duration) self.transport.registerProducer(producer, True) producer.resumeProducing() def dataReceived(self, data): self.transport.loseConnection() class TcpSenderFactory(ClientFactory): """ This factory is responsible to instantiate TcpSenderConnection classes each time sender initiates connection """ protocol = TcpSenderConnection def __init__(self, duration): self.duration = duration self.stats = 0 def getResults(self): """Returns amount of bytes sent to the Listener (as a string)""" return str(self.stats) openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/args.py0000644000000000000000000000013213534540071020202 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.277849711 openvswitch-2.5.9/python/ovstest/args.py0000644000175000017500000002571213534540071021677 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ ovsargs provide argument parsing for ovs-test utility """ import argparse import re import socket import sys CONTROL_PORT = 15531 DATA_PORT = 15532 def ip_address(string): """Verifies if string is a valid IP address""" try: socket.inet_aton(string) except socket.error: raise argparse.ArgumentTypeError("Not a valid IPv4 address") return string def ip_optional_mask(string): """ Verifies if string contains a valid IP address and an optional mask in CIDR notation. """ token = string.split("/") if len(token) > 2: raise argparse.ArgumentTypeError("IP address and netmask must be " "separated by a single slash") elif len(token) == 2: try: mask = int(token[1]) except ValueError: raise argparse.ArgumentTypeError("Netmask is not a valid integer") if mask < 0 or mask > 31: raise argparse.ArgumentTypeError("Netmask must be in range 0..31") ip_address(token[0]) return string def port(string): """Convert a string into a TCP/UDP Port (integer)""" try: port_number = int(string) if port_number < 1 or port_number > 65535: raise argparse.ArgumentTypeError("Port is out of range") except ValueError: raise argparse.ArgumentTypeError("Port is not an integer") return port_number def ip_optional_port(string, default_port, ip_callback): """Convert a string into IP and Port pair. If port was absent then use default_port as the port. The third argument is a callback that verifies whether IP address is given in correct format.""" value = string.split(':') if len(value) == 1: return (ip_callback(value[0]), default_port) elif len(value) == 2: return (ip_callback(value[0]), port(value[1])) else: raise argparse.ArgumentTypeError("IP address from the optional Port " "must be colon-separated") def ip_optional_port_port(string, default_port1, default_port2, ip_callback): """Convert a string into IP, Port1, Port2 tuple. If any of ports were missing, then default ports will be used. The fourth argument is a callback that verifies whether IP address is given in the expected format.""" value = string.split(':') if len(value) == 1: return (ip_callback(value[0]), default_port1, default_port2) elif len(value) == 2: return (ip_callback(value[0]), port(value[1]), default_port2) elif len(value) == 3: return (ip_callback(value[0]), port(value[1]), port(value[2])) else: raise argparse.ArgumentTypeError("Expected IP address and at most " "two colon-separated ports") def vlan_tag(string): """ This function verifies whether given string is a correct VLAN tag. """ try: value = int(string) except ValueError: raise argparse.ArgumentTypeError("VLAN tag is not a valid integer") if value < 1 or value > 4094: raise argparse.ArgumentTypeError("Not a valid VLAN tag. " "VLAN tag should be in the " "range 1..4094.") return string def server_endpoint(string): """Converts a string OuterIP[:OuterPort],InnerIP[/Mask][:InnerPort] into a 4-tuple, where: 1. First element is OuterIP 2. Second element is OuterPort (if omitted will use default value 15531) 3 Third element is InnerIP with optional mask 4. Fourth element is InnerPort (if omitted will use default value 15532) """ value = string.split(',') if len(value) == 2: ret1 = ip_optional_port(value[0], CONTROL_PORT, ip_address) ret2 = ip_optional_port(value[1], DATA_PORT, ip_optional_mask) return (ret1[0], ret1[1], ret2[0], ret2[1]) else: raise argparse.ArgumentTypeError("OuterIP:OuterPort and InnerIP/Mask:" "InnerPort must be comma separated") class UniqueServerAction(argparse.Action): """ This custom action class will prevent user from entering multiple ovs-test servers with the same OuterIP. If there is an server with 127.0.0.1 outer IP address then it will be inserted in the front of the list. """ def __call__(self, parser, namespace, values, option_string=None): outer_ips = set() endpoints = [] for server in values: try: endpoint = server_endpoint(server) except argparse.ArgumentTypeError: raise argparse.ArgumentError(self, str(sys.exc_info()[1])) if endpoint[0] in outer_ips: raise argparse.ArgumentError(self, "Duplicate OuterIPs found") else: outer_ips.add(endpoint[0]) if endpoint[0] == "127.0.0.1": endpoints.insert(0, endpoint) else: endpoints.append(endpoint) setattr(namespace, self.dest, endpoints) def bandwidth(string): """Convert a string (given in bits/second with optional magnitude for units) into a long (bytes/second)""" if re.match("^[1-9][0-9]*[MK]?$", string) is None: raise argparse.ArgumentTypeError("Not a valid target bandwidth") bwidth = string.replace("M", "000000") bwidth = bwidth.replace("K", "000") return long(bwidth) / 8 # Convert from bits to bytes def tunnel_types(string): """ This function converts a string into a list that contains all tunnel types that user intended to test. """ return string.split(',') def l3_endpoint_client(string): """ This function parses command line argument string in remoteIP,localInnerIP[/mask][:ControlPort[:TestPort]],remoteInnerIP[: ControlPort[:TestPort]] format. """ try: remote_ip, me, he = string.split(',') except ValueError: raise argparse.ArgumentTypeError("All 3 IP addresses must be comma " "separated.") r = (ip_address(remote_ip), ip_optional_port_port(me, CONTROL_PORT, DATA_PORT, ip_optional_mask), ip_optional_port_port(he, CONTROL_PORT, DATA_PORT, ip_address)) return r def l3_endpoint_server(string): """ This function parses a command line argument string in remoteIP,localInnerIP[/mask][:ControlPort] format. """ try: remote_ip, me = string.split(',') except ValueError: raise argparse.ArgumentTypeError("Both IP addresses must be comma " "separated.") return (ip_address(remote_ip), ip_optional_port(me, CONTROL_PORT, ip_optional_mask)) def ovs_initialize_args(): """ Initialize argument parsing for ovs-test utility. """ parser = argparse.ArgumentParser(description='Test connectivity ' 'between two Open vSwitches.') parser.add_argument('-v', '--version', action='version', version='ovs-test (Open vSwitch) @VERSION@') parser.add_argument("-b", "--bandwidth", action='store', dest="targetBandwidth", default="1M", type=bandwidth, help='Target bandwidth for UDP tests in bits/second. Use ' 'postfix M or K to alter unit magnitude.') parser.add_argument("-i", "--interval", action='store', dest="testInterval", default=5, type=int, help='Interval for how long to run each test in seconds.') parser.add_argument("-t", "--tunnel-modes", action='store', dest="tunnelModes", default=(), type=tunnel_types, help='Do L3 tests with the given tunnel modes.') parser.add_argument("-l", "--vlan-tag", action='store', dest="vlanTag", default=None, type=vlan_tag, help='Do VLAN tests and use the given VLAN tag.') parser.add_argument("-d", "--direct", action='store_true', dest="direct", default=None, help='Do direct tests between both ovs-test servers.') group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-s", "--server", action="store", dest="port", type=port, help='Run in server mode and wait for the client to ' 'connect to this port.') group.add_argument('-c', "--client", nargs=2, dest="servers", action=UniqueServerAction, metavar=("SERVER1", "SERVER2"), help='Run in client mode and do tests between these ' 'two ovs-test servers. Each server must be specified in ' 'following format - OuterIP:OuterPort,InnerIP[/mask] ' ':InnerPort. It is possible to start local instance of ' 'ovs-test server in the client mode by using 127.0.0.1 as ' 'OuterIP.') return parser.parse_args() def l3_initialize_args(): """ Initialize argument parsing for ovs-l3ping utility. """ parser = argparse.ArgumentParser(description='Test L3 tunnel ' 'connectivity between two Open vSwitch instances.') parser.add_argument('-v', '--version', action='version', version='ovs-l3ping (Open vSwitch) @VERSION@') parser.add_argument("-b", "--bandwidth", action='store', dest="targetBandwidth", default="1M", type=bandwidth, help='Target bandwidth for UDP tests in bits/second. Use ' 'postfix M or K to alter unit magnitude.') parser.add_argument("-i", "--interval", action='store', dest="testInterval", default=5, type=int, help='Interval for how long to run each test in seconds.') parser.add_argument("-t", "--tunnel-mode", action='store', dest="tunnelMode", required=True, help='Do L3 tests with this tunnel type.') group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-s", "--server", action="store", dest="server", metavar="TUNNELIP,SERVER", type=l3_endpoint_server, help='Run in server mode and wait for the client to ' 'connect.') group.add_argument('-c', "--client", action="store", dest="client", metavar="TUNNELIP,CLIENT,SERVER", type=l3_endpoint_client, help='Run in client mode and connect to the server.') return parser.parse_args() openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/udp.py0000644000000000000000000000013213534540071020036 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.281849741 openvswitch-2.5.9/python/ovstest/udp.py0000644000175000017500000000510613534540071021526 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ ovsudp contains listener and sender classes for UDP protocol """ import array import struct import time from twisted.internet.protocol import DatagramProtocol from twisted.internet.task import LoopingCall class UdpListener(DatagramProtocol): """ Class that will listen for incoming UDP packets """ def __init__(self): self.stats = [] def datagramReceived(self, data, (_1, _2)): """This function is called each time datagram is received""" try: self.stats.append(struct.unpack_from("Q", data, 0)) except struct.error: pass # ignore packets that are less than 8 bytes of size def getResults(self): """Returns number of packets that were actually received""" return len(self.stats) class UdpSender(DatagramProtocol): """ Class that will send UDP packets to UDP Listener """ def __init__(self, host, count, size, duration): # LoopingCall does not know whether UDP socket is actually writable self.looper = None self.host = host self.count = count self.duration = duration self.start = time.time() self.sent = 0 self.data = array.array('c', 'X' * size) def startProtocol(self): self.looper = LoopingCall(self.sendData) period = self.duration / float(self.count) self.looper.start(period , now = False) def stopProtocol(self): if (self.looper is not None): self.looper.stop() self.looper = None def datagramReceived(self, data, (host, port)): pass def sendData(self): """This function is called from LoopingCall""" if self.start + self.duration < time.time(): self.looper.stop() self.looper = None self.sent += 1 struct.pack_into('Q', self.data, 0, self.sent) self.transport.write(self.data, self.host) def getResults(self): """Returns number of packets that were sent""" return self.sent openvswitch-2.5.9/python/ovstest/PaxHeaders.82075/vswitch.py0000644000000000000000000000013213534540071020735 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.285849769 openvswitch-2.5.9/python/ovstest/vswitch.py0000644000175000017500000000651313534540071022430 0ustar00jpettitjpettit00000000000000# Copyright (c) 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ vswitch module allows its callers to interact with OVS DB. """ import exceptions import subprocess import util def ovs_vsctl_add_bridge(bridge): """ This function creates an OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "add-br", bridge]) return ret def ovs_vsctl_del_bridge(bridge): """ This function deletes the OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "del-br", bridge]) return ret def ovs_vsctl_del_pbridge(bridge, iface): """ This function deletes the OVS bridge and assigns the bridge IP address back to the iface. """ (ip_addr, mask) = util.interface_get_ip(bridge) util.interface_assign_ip(iface, ip_addr, mask) util.move_routes(bridge, iface) return ovs_vsctl_del_bridge(bridge) def ovs_vsctl_is_ovs_bridge(bridge): """ This function verifies whether given port is an OVS bridge. If it is an OVS bridge then it will return True. """ ret, _out, _err = util.start_process(["ovs-vsctl", "br-exists", bridge]) return ret == 0 def ovs_vsctl_add_port_to_bridge(bridge, iface): """ This function adds given interface to the bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "add-port", bridge, iface]) return ret def ovs_vsctl_del_port_from_bridge(port): """ This function removes given port from a OVS bridge. """ ret, _out, _err = util.start_process(["ovs-vsctl", "del-port", port]) return ret def ovs_vsctl_set(table, record, column, key, value): """ This function allows to alter the OVS database. If column is a map, then caller should also set the key, otherwise the key should be left as an empty string. """ if key is None: index = column else: index = "%s:%s" % (column, key) index_value = "%s=%s" % (index, value) ret, _out, _err = util.start_process(["ovs-vsctl", "set", table, record, index_value]) return ret def ovs_get_physical_interface(bridge): """ This function tries to figure out which is the physical interface that belongs to the bridge. If there are multiple physical interfaces assigned to this bridge then it will return the first match. """ ret, out, _err = util.start_process(["ovs-vsctl", "list-ifaces", bridge]) if ret == 0: ifaces = out.splitlines() for iface in ifaces: ret, out, _err = util.start_process(["ovs-vsctl", "get", "Interface", iface, "type"]) if ret == 0: if ('""' in out) or ('system' in out): return iface # this should be the physical interface return None openvswitch-2.5.9/python/PaxHeaders.82075/ovs0000644000000000000000000000013213534540120015712 xustar0030 mtime=1567801424.285849769 30 atime=1567801425.625859648 30 ctime=1567801424.285849769 openvswitch-2.5.9/python/ovs/0000755000175000017500000000000013534540120017455 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/ovs/PaxHeaders.82075/util.py0000644000000000000000000000013213534540071017323 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.269849651 openvswitch-2.5.9/python/ovs/util.py0000644000175000017500000000572713534540071021024 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import os.path import sys PROGRAM_NAME = os.path.basename(sys.argv[0]) EOF = -1 def abs_file_name(dir_, file_name): """If 'file_name' starts with '/', returns a copy of 'file_name'. Otherwise, returns an absolute path to 'file_name' considering it relative to 'dir_', which itself must be absolute. 'dir_' may be None or the empty string, in which case the current working directory is used. Returns None if 'dir_' is None and getcwd() fails. This differs from os.path.abspath() in that it will never change the meaning of a file name.""" if file_name.startswith('/'): return file_name else: if dir_ is None or dir_ == "": try: dir_ = os.getcwd() except OSError: return None if dir_.endswith('/'): return dir_ + file_name else: return "%s/%s" % (dir_, file_name) def ovs_retval_to_string(retval): """Many OVS functions return an int which is one of: - 0: no error yet - >0: errno value - EOF: end of file (not necessarily an error; depends on the function called) Returns the appropriate human-readable string.""" if not retval: return "" if retval > 0: return os.strerror(retval) if retval == EOF: return "End of file" return "***unknown return value: %s***" % retval def ovs_error(err_no, message, vlog=None): """Prints 'message' on stderr and emits an ERROR level log message to 'vlog' if supplied. If 'err_no' is nonzero, then it is formatted with ovs_retval_to_string() and appended to the message inside parentheses. 'message' should not end with a new-line, because this function will add one itself.""" err_msg = "%s: %s" % (PROGRAM_NAME, message) if err_no: err_msg += " (%s)" % ovs_retval_to_string(err_no) sys.stderr.write("%s\n" % err_msg) if vlog: vlog.err(err_msg) def ovs_fatal(*args, **kwargs): """Prints 'message' on stderr and emits an ERROR level log message to 'vlog' if supplied. If 'err_no' is nonzero, then it is formatted with ovs_retval_to_string() and appended to the message inside parentheses. Then, terminates with exit code 1 (indicating a failure). 'message' should not end with a new-line, because this function will add one itself.""" ovs_error(*args, **kwargs) sys.exit(1) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/poller.py0000644000000000000000000000013213534540071017643 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.257849563 openvswitch-2.5.9/python/ovs/poller.py0000644000175000017500000001622613534540071021340 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import errno import ovs.timeval import ovs.vlog import select import socket try: import eventlet.patcher def _using_eventlet_green_select(): return eventlet.patcher.is_monkey_patched(select) except: def _using_eventlet_green_select(): return False vlog = ovs.vlog.Vlog("poller") POLLIN = 0x001 POLLOUT = 0x004 POLLERR = 0x008 POLLHUP = 0x010 POLLNVAL = 0x020 # eventlet/gevent doesn't support select.poll. If select.poll is used, # python interpreter is blocked as a whole instead of switching from the # current thread that is about to block to other runnable thread. # So emulate select.poll by select.select because using python means that # performance isn't so important. class _SelectSelect(object): """ select.poll emulation by using select.select. Only register and poll are needed at the moment. """ def __init__(self): self.rlist = [] self.wlist = [] self.xlist = [] def register(self, fd, events): if isinstance(fd, socket.socket): fd = fd.fileno() assert isinstance(fd, int) if events & POLLIN: self.rlist.append(fd) events &= ~POLLIN if events & POLLOUT: self.wlist.append(fd) events &= ~POLLOUT if events: self.xlist.append(fd) def poll(self, timeout): if timeout == -1: # epoll uses -1 for infinite timeout, select uses None. timeout = None else: timeout = float(timeout) / 1000 # XXX workaround a bug in eventlet # see https://github.com/eventlet/eventlet/pull/25 if timeout == 0 and _using_eventlet_green_select(): timeout = 0.1 rlist, wlist, xlist = select.select(self.rlist, self.wlist, self.xlist, timeout) events_dict = {} for fd in rlist: events_dict[fd] = events_dict.get(fd, 0) | POLLIN for fd in wlist: events_dict[fd] = events_dict.get(fd, 0) | POLLOUT for fd in xlist: events_dict[fd] = events_dict.get(fd, 0) | (POLLERR | POLLHUP | POLLNVAL) return events_dict.items() SelectPoll = _SelectSelect # If eventlet/gevent isn't used, we can use select.poll by replacing # _SelectPoll with select.poll class # _SelectPoll = select.poll class Poller(object): """High-level wrapper around the "poll" system call. Intended usage is for the program's main loop to go about its business servicing whatever events it needs to. Then, when it runs out of immediate tasks, it calls each subordinate module or object's "wait" function, which in turn calls one (or more) of the functions Poller.fd_wait(), Poller.immediate_wake(), and Poller.timer_wait() to register to be awakened when the appropriate event occurs. Then the main loop calls Poller.block(), which blocks until one of the registered events happens.""" def __init__(self): self.__reset() def fd_wait(self, fd, events): """Registers 'fd' as waiting for the specified 'events' (which should be select.POLLIN or select.POLLOUT or their bitwise-OR). The following call to self.block() will wake up when 'fd' becomes ready for one or more of the requested events. The event registration is one-shot: only the following call to self.block() is affected. The event will need to be re-registered after self.block() is called if it is to persist. 'fd' may be an integer file descriptor or an object with a fileno() method that returns an integer file descriptor.""" self.poll.register(fd, events) def __timer_wait(self, msec): if self.timeout < 0 or msec < self.timeout: self.timeout = msec def timer_wait(self, msec): """Causes the following call to self.block() to block for no more than 'msec' milliseconds. If 'msec' is nonpositive, the following call to self.block() will not block at all. The timer registration is one-shot: only the following call to self.block() is affected. The timer will need to be re-registered after self.block() is called if it is to persist.""" if msec <= 0: self.immediate_wake() else: self.__timer_wait(msec) def timer_wait_until(self, msec): """Causes the following call to self.block() to wake up when the current time, as returned by ovs.timeval.msec(), reaches 'msec' or later. If 'msec' is earlier than the current time, the following call to self.block() will not block at all. The timer registration is one-shot: only the following call to self.block() is affected. The timer will need to be re-registered after self.block() is called if it is to persist.""" now = ovs.timeval.msec() if msec <= now: self.immediate_wake() else: self.__timer_wait(msec - now) def immediate_wake(self): """Causes the following call to self.block() to wake up immediately, without blocking.""" self.timeout = 0 def block(self): """Blocks until one or more of the events registered with self.fd_wait() occurs, or until the minimum duration registered with self.timer_wait() elapses, or not at all if self.immediate_wake() has been called.""" try: try: events = self.poll.poll(self.timeout) self.__log_wakeup(events) except select.error, e: # XXX rate-limit error, msg = e if error != errno.EINTR: vlog.err("poll: %s" % e[1]) finally: self.__reset() def __log_wakeup(self, events): if not events: vlog.dbg("%d-ms timeout" % self.timeout) else: for fd, revents in events: if revents != 0: s = "" if revents & POLLIN: s += "[POLLIN]" if revents & POLLOUT: s += "[POLLOUT]" if revents & POLLERR: s += "[POLLERR]" if revents & POLLHUP: s += "[POLLHUP]" if revents & POLLNVAL: s += "[POLLNVAL]" vlog.dbg("%s on fd %d" % (s, fd)) def __reset(self): self.poll = SelectPoll() self.timeout = -1 openvswitch-2.5.9/python/ovs/PaxHeaders.82075/socket_util.py0000644000000000000000000000013213534540071020673 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.261849593 openvswitch-2.5.9/python/ovs/socket_util.py0000644000175000017500000002427513534540071022373 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2012, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import errno import os import os.path import random import select import socket import sys import ovs.fatal_signal import ovs.poller import ovs.vlog vlog = ovs.vlog.Vlog("socket_util") def make_short_name(long_name): if long_name is None: return None long_name = os.path.abspath(long_name) long_dirname = os.path.dirname(long_name) tmpdir = os.getenv('TMPDIR', '/tmp') for x in xrange(0, 1000): link_name = \ '%s/ovs-un-py-%d-%d' % (tmpdir, random.randint(0, 10000), x) try: os.symlink(long_dirname, link_name) ovs.fatal_signal.add_file_to_unlink(link_name) return os.path.join(link_name, os.path.basename(long_name)) except OSError, e: if e.errno != errno.EEXIST: break raise Exception("Failed to create temporary symlink") def free_short_name(short_name): if short_name is None: return link_name = os.path.dirname(short_name) ovs.fatal_signal.unlink_file_now(link_name) def make_unix_socket(style, nonblock, bind_path, connect_path, short=False): """Creates a Unix domain socket in the given 'style' (either socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if 'bind_path' is not None) and connected to 'connect_path' (if 'connect_path' is not None). If 'nonblock' is true, the socket is made non-blocking. Returns (error, socket): on success 'error' is 0 and 'socket' is a new socket object, on failure 'error' is a positive errno value and 'socket' is None.""" try: sock = socket.socket(socket.AF_UNIX, style) except socket.error, e: return get_exception_errno(e), None try: if nonblock: set_nonblocking(sock) if bind_path is not None: # Delete bind_path but ignore ENOENT. try: os.unlink(bind_path) except OSError, e: if e.errno != errno.ENOENT: return e.errno, None ovs.fatal_signal.add_file_to_unlink(bind_path) sock.bind(bind_path) try: if sys.hexversion >= 0x02060000: os.fchmod(sock.fileno(), 0700) else: os.chmod("/dev/fd/%d" % sock.fileno(), 0700) except OSError, e: pass if connect_path is not None: try: sock.connect(connect_path) except socket.error, e: if get_exception_errno(e) != errno.EINPROGRESS: raise return 0, sock except socket.error, e: sock.close() if (bind_path is not None and os.path.exists(bind_path)): ovs.fatal_signal.unlink_file_now(bind_path) eno = ovs.socket_util.get_exception_errno(e) if (eno == "AF_UNIX path too long" and os.uname()[0] == "Linux"): short_connect_path = None short_bind_path = None connect_dirfd = None bind_dirfd = None # Try workaround using /proc/self/fd if connect_path is not None: dirname = os.path.dirname(connect_path) basename = os.path.basename(connect_path) try: connect_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) except OSError, err: return get_exception_errno(err), None short_connect_path = "/proc/self/fd/%d/%s" % (connect_dirfd, basename) if bind_path is not None: dirname = os.path.dirname(bind_path) basename = os.path.basename(bind_path) try: bind_dirfd = os.open(dirname, os.O_DIRECTORY | os.O_RDONLY) except OSError, err: return get_exception_errno(err), None short_bind_path = "/proc/self/fd/%d/%s" % (bind_dirfd, basename) try: return make_unix_socket(style, nonblock, short_bind_path, short_connect_path) finally: if connect_dirfd is not None: os.close(connect_dirfd) if bind_dirfd is not None: os.close(bind_dirfd) elif (eno == "AF_UNIX path too long"): if short: return get_exception_errno(e), None short_bind_path = None try: short_bind_path = make_short_name(bind_path) short_connect_path = make_short_name(connect_path) except: free_short_name(short_bind_path) return errno.ENAMETOOLONG, None try: return make_unix_socket(style, nonblock, short_bind_path, short_connect_path, short=True) finally: free_short_name(short_bind_path) free_short_name(short_connect_path) else: return get_exception_errno(e), None def check_connection_completion(sock): p = ovs.poller.SelectPoll() p.register(sock, ovs.poller.POLLOUT) pfds = p.poll(0) if len(pfds) == 1: revents = pfds[0][1] if revents & ovs.poller.POLLERR: try: # The following should raise an exception. socket.send("\0", socket.MSG_DONTWAIT) # (Here's where we end up if it didn't.) # XXX rate-limit vlog.err("poll return POLLERR but send succeeded") return errno.EPROTO except socket.error, e: return get_exception_errno(e) else: return 0 else: return errno.EAGAIN def is_valid_ipv4_address(address): try: socket.inet_pton(socket.AF_INET, address) except AttributeError: try: socket.inet_aton(address) except socket.error: return False except socket.error: return False return True def inet_parse_active(target, default_port): address = target.split(":") if len(address) >= 2: host_name = ":".join(address[0:-1]).lstrip('[').rstrip(']') port = int(address[-1]) else: if default_port: port = default_port else: raise ValueError("%s: port number must be specified" % target) host_name = address[0] if not host_name: raise ValueError("%s: bad peer name format" % target) return (host_name, port) def inet_open_active(style, target, default_port, dscp): address = inet_parse_active(target, default_port) try: is_addr_inet = is_valid_ipv4_address(address[0]) if is_addr_inet: sock = socket.socket(socket.AF_INET, style, 0) family = socket.AF_INET else: sock = socket.socket(socket.AF_INET6, style, 0) family = socket.AF_INET6 except socket.error, e: return get_exception_errno(e), None try: set_nonblocking(sock) set_dscp(sock, family, dscp) try: sock.connect(address) except socket.error, e: if get_exception_errno(e) != errno.EINPROGRESS: raise return 0, sock except socket.error, e: sock.close() return get_exception_errno(e), None def get_exception_errno(e): """A lot of methods on Python socket objects raise socket.error, but that exception is documented as having two completely different forms of arguments: either a string or a (errno, string) tuple. We only want the errno.""" if type(e.args) == tuple: return e.args[0] else: return errno.EPROTO null_fd = -1 def get_null_fd(): """Returns a readable and writable fd for /dev/null, if successful, otherwise a negative errno value. The caller must not close the returned fd (because the same fd will be handed out to subsequent callers).""" global null_fd if null_fd < 0: try: null_fd = os.open("/dev/null", os.O_RDWR) except OSError, e: vlog.err("could not open /dev/null: %s" % os.strerror(e.errno)) return -e.errno return null_fd def write_fully(fd, buf): """Returns an (error, bytes_written) tuple where 'error' is 0 on success, otherwise a positive errno value, and 'bytes_written' is the number of bytes that were written before the error occurred. 'error' is 0 if and only if 'bytes_written' is len(buf).""" bytes_written = 0 if len(buf) == 0: return 0, 0 while True: try: retval = os.write(fd, buf) assert retval >= 0 if retval == len(buf): return 0, bytes_written + len(buf) elif retval == 0: vlog.warn("write returned 0") return errno.EPROTO, bytes_written else: bytes_written += retval buf = buf[:retval] except OSError, e: return e.errno, bytes_written def set_nonblocking(sock): try: sock.setblocking(0) except socket.error, e: vlog.err("could not set nonblocking mode on socket: %s" % os.strerror(get_exception_errno(e))) def set_dscp(sock, family, dscp): if dscp > 63: raise ValueError("Invalid dscp %d" % dscp) val = dscp << 2 if family == socket.AF_INET: try: sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val) except socket.error, e: raise elif family == socket.AF_INET6: try: sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val) except socket.error, e: raise else: raise openvswitch-2.5.9/python/ovs/PaxHeaders.82075/reconnect.py0000644000000000000000000000013213534540071020326 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.261849593 openvswitch-2.5.9/python/ovs/reconnect.py0000644000175000017500000005512113534540071022020 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import ovs.vlog import ovs.util # Values returned by Reconnect.run() CONNECT = 'connect' DISCONNECT = 'disconnect' PROBE = 'probe' EOF = ovs.util.EOF vlog = ovs.vlog.Vlog("reconnect") class Reconnect(object): """A finite-state machine for connecting and reconnecting to a network resource with exponential backoff. It also provides optional support for detecting a connection on which the peer is no longer responding. The library does not implement anything networking related, only an FSM for networking code to use. Many Reconnect methods take a "now" argument. This makes testing easier since there is no hidden state. When not testing, just pass the return value of ovs.time.msec(). (Perhaps this design should be revisited later.)""" class Void(object): name = "VOID" is_connected = False @staticmethod def deadline(fsm): return None @staticmethod def run(fsm, now): return None class Listening(object): name = "LISTENING" is_connected = False @staticmethod def deadline(fsm): return None @staticmethod def run(fsm, now): return None class Backoff(object): name = "BACKOFF" is_connected = False @staticmethod def deadline(fsm): return fsm.state_entered + fsm.backoff @staticmethod def run(fsm, now): return CONNECT class ConnectInProgress(object): name = "CONNECTING" is_connected = False @staticmethod def deadline(fsm): return fsm.state_entered + max(1000, fsm.backoff) @staticmethod def run(fsm, now): return DISCONNECT class Active(object): name = "ACTIVE" is_connected = True @staticmethod def deadline(fsm): if fsm.probe_interval: base = max(fsm.last_activity, fsm.state_entered) return base + fsm.probe_interval return None @staticmethod def run(fsm, now): vlog.dbg("%s: idle %d ms, sending inactivity probe" % (fsm.name, now - max(fsm.last_activity, fsm.state_entered))) fsm._transition(now, Reconnect.Idle) return PROBE class Idle(object): name = "IDLE" is_connected = True @staticmethod def deadline(fsm): if fsm.probe_interval: return fsm.state_entered + fsm.probe_interval return None @staticmethod def run(fsm, now): vlog.err("%s: no response to inactivity probe after %.3g " "seconds, disconnecting" % (fsm.name, (now - fsm.state_entered) / 1000.0)) return DISCONNECT class Reconnect(object): name = "RECONNECT" is_connected = False @staticmethod def deadline(fsm): return fsm.state_entered @staticmethod def run(fsm, now): return DISCONNECT def __init__(self, now): """Creates and returns a new reconnect FSM with default settings. The FSM is initially disabled. The caller will likely want to call self.enable() and self.set_name() on the returned object.""" self.name = "void" self.min_backoff = 1000 self.max_backoff = 8000 self.probe_interval = 5000 self.passive = False self.info_level = vlog.info self.state = Reconnect.Void self.state_entered = now self.backoff = 0 self.last_activity = now self.last_connected = None self.last_disconnected = None self.max_tries = None self.creation_time = now self.n_attempted_connections = 0 self.n_successful_connections = 0 self.total_connected_duration = 0 self.seqno = 0 def set_quiet(self, quiet): """If 'quiet' is true, this object will log informational messages at debug level, by default keeping them out of log files. This is appropriate if the connection is one that is expected to be short-lived, so that the log messages are merely distracting. If 'quiet' is false, this object logs informational messages at info level. This is the default. This setting has no effect on the log level of debugging, warning, or error messages.""" if quiet: self.info_level = vlog.dbg else: self.info_level = vlog.info def get_name(self): return self.name def set_name(self, name): """Sets this object's name to 'name'. If 'name' is None, then "void" is used instead. The name is used in log messages.""" if name is None: self.name = "void" else: self.name = name def get_min_backoff(self): """Return the minimum number of milliseconds to back off between consecutive connection attempts. The default is 1000 ms.""" return self.min_backoff def get_max_backoff(self): """Return the maximum number of milliseconds to back off between consecutive connection attempts. The default is 8000 ms.""" return self.max_backoff def get_probe_interval(self): """Returns the "probe interval" in milliseconds. If this is zero, it disables the connection keepalive feature. If it is nonzero, then if the interval passes while the FSM is connected and without self.activity() being called, self.run() returns ovs.reconnect.PROBE. If the interval passes again without self.activity() being called, self.run() returns ovs.reconnect.DISCONNECT.""" return self.probe_interval def set_max_tries(self, max_tries): """Limits the maximum number of times that this object will ask the client to try to reconnect to 'max_tries'. None (the default) means an unlimited number of tries. After the number of tries has expired, the FSM will disable itself instead of backing off and retrying.""" self.max_tries = max_tries def get_max_tries(self): """Returns the current remaining number of connection attempts, None if the number is unlimited.""" return self.max_tries def set_backoff(self, min_backoff, max_backoff): """Configures the backoff parameters for this FSM. 'min_backoff' is the minimum number of milliseconds, and 'max_backoff' is the maximum, between connection attempts. 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than or equal to 'min_backoff'.""" self.min_backoff = max(min_backoff, 1000) if self.max_backoff: self.max_backoff = max(max_backoff, 1000) else: self.max_backoff = 8000 if self.min_backoff > self.max_backoff: self.max_backoff = self.min_backoff if (self.state == Reconnect.Backoff and self.backoff > self.max_backoff): self.backoff = self.max_backoff def set_probe_interval(self, probe_interval): """Sets the "probe interval" to 'probe_interval', in milliseconds. If this is zero, it disables the connection keepalive feature. If it is nonzero, then if the interval passes while this FSM is connected and without self.activity() being called, self.run() returns ovs.reconnect.PROBE. If the interval passes again without self.activity() being called, self.run() returns ovs.reconnect.DISCONNECT. If 'probe_interval' is nonzero, then it will be forced to a value of at least 1000 ms.""" if probe_interval: self.probe_interval = max(1000, probe_interval) else: self.probe_interval = 0 def is_passive(self): """Returns true if 'fsm' is in passive mode, false if 'fsm' is in active mode (the default).""" return self.passive def set_passive(self, passive, now): """Configures this FSM for active or passive mode. In active mode (the default), the FSM is attempting to connect to a remote host. In passive mode, the FSM is listening for connections from a remote host.""" if self.passive != passive: self.passive = passive if ((passive and self.state in (Reconnect.ConnectInProgress, Reconnect.Reconnect)) or (not passive and self.state == Reconnect.Listening and self.__may_retry())): self._transition(now, Reconnect.Backoff) self.backoff = 0 def is_enabled(self): """Returns true if this FSM has been enabled with self.enable(). Calling another function that indicates a change in connection state, such as self.disconnected() or self.force_reconnect(), will also enable a reconnect FSM.""" return self.state != Reconnect.Void def enable(self, now): """If this FSM is disabled (the default for newly created FSMs), enables it, so that the next call to reconnect_run() for 'fsm' will return ovs.reconnect.CONNECT. If this FSM is not disabled, this function has no effect.""" if self.state == Reconnect.Void and self.__may_retry(): self._transition(now, Reconnect.Backoff) self.backoff = 0 def disable(self, now): """Disables this FSM. Until 'fsm' is enabled again, self.run() will always return 0.""" if self.state != Reconnect.Void: self._transition(now, Reconnect.Void) def force_reconnect(self, now): """If this FSM is enabled and currently connected (or attempting to connect), forces self.run() to return ovs.reconnect.DISCONNECT the next time it is called, which should cause the client to drop the connection (or attempt), back off, and then reconnect.""" if self.state in (Reconnect.ConnectInProgress, Reconnect.Active, Reconnect.Idle): self._transition(now, Reconnect.Reconnect) def disconnected(self, now, error): """Tell this FSM that the connection dropped or that a connection attempt failed. 'error' specifies the reason: a positive value represents an errno value, EOF indicates that the connection was closed by the peer (e.g. read() returned 0), and 0 indicates no specific error. The FSM will back off, then reconnect.""" if self.state not in (Reconnect.Backoff, Reconnect.Void): # Report what happened if self.state in (Reconnect.Active, Reconnect.Idle): if error > 0: vlog.warn("%s: connection dropped (%s)" % (self.name, os.strerror(error))) elif error == EOF: self.info_level("%s: connection closed by peer" % self.name) else: self.info_level("%s: connection dropped" % self.name) elif self.state == Reconnect.Listening: if error > 0: vlog.warn("%s: error listening for connections (%s)" % (self.name, os.strerror(error))) else: self.info_level("%s: error listening for connections" % self.name) else: if self.passive: type_ = "listen" else: type_ = "connection" if error > 0: vlog.warn("%s: %s attempt failed (%s)" % (self.name, type_, os.strerror(error))) else: self.info_level("%s: %s attempt timed out" % (self.name, type_)) if (self.state in (Reconnect.Active, Reconnect.Idle)): self.last_disconnected = now # Back off if (self.state in (Reconnect.Active, Reconnect.Idle) and (self.last_activity - self.last_connected >= self.backoff or self.passive)): if self.passive: self.backoff = 0 else: self.backoff = self.min_backoff else: if self.backoff < self.min_backoff: self.backoff = self.min_backoff elif self.backoff >= self.max_backoff / 2: self.backoff = self.max_backoff else: self.backoff *= 2 if self.passive: self.info_level("%s: waiting %.3g seconds before trying " "to listen again" % (self.name, self.backoff / 1000.0)) else: self.info_level("%s: waiting %.3g seconds before reconnect" % (self.name, self.backoff / 1000.0)) if self.__may_retry(): self._transition(now, Reconnect.Backoff) else: self._transition(now, Reconnect.Void) def connecting(self, now): """Tell this FSM that a connection or listening attempt is in progress. The FSM will start a timer, after which the connection or listening attempt will be aborted (by returning ovs.reconnect.DISCONNECT from self.run()).""" if self.state != Reconnect.ConnectInProgress: if self.passive: self.info_level("%s: listening..." % self.name) else: self.info_level("%s: connecting..." % self.name) self._transition(now, Reconnect.ConnectInProgress) def listening(self, now): """Tell this FSM that the client is listening for connection attempts. This state last indefinitely until the client reports some change. The natural progression from this state is for the client to report that a connection has been accepted or is in progress of being accepted, by calling self.connecting() or self.connected(). The client may also report that listening failed (e.g. accept() returned an unexpected error such as ENOMEM) by calling self.listen_error(), in which case the FSM will back off and eventually return ovs.reconnect.CONNECT from self.run() to tell the client to try listening again.""" if self.state != Reconnect.Listening: self.info_level("%s: listening..." % self.name) self._transition(now, Reconnect.Listening) def listen_error(self, now, error): """Tell this FSM that the client's attempt to accept a connection failed (e.g. accept() returned an unexpected error such as ENOMEM). If the FSM is currently listening (self.listening() was called), it will back off and eventually return ovs.reconnect.CONNECT from self.run() to tell the client to try listening again. If there is an active connection, this will be delayed until that connection drops.""" if self.state == Reconnect.Listening: self.disconnected(now, error) def connected(self, now): """Tell this FSM that the connection was successful. The FSM will start the probe interval timer, which is reset by self.activity(). If the timer expires, a probe will be sent (by returning ovs.reconnect.PROBE from self.run(). If the timer expires again without being reset, the connection will be aborted (by returning ovs.reconnect.DISCONNECT from self.run().""" if not self.state.is_connected: self.connecting(now) self.info_level("%s: connected" % self.name) self._transition(now, Reconnect.Active) self.last_connected = now def connect_failed(self, now, error): """Tell this FSM that the connection attempt failed. The FSM will back off and attempt to reconnect.""" self.connecting(now) self.disconnected(now, error) def activity(self, now): """Tell this FSM that some activity occurred on the connection. This resets the probe interval timer, so that the connection is known not to be idle.""" if self.state != Reconnect.Active: self._transition(now, Reconnect.Active) self.last_activity = now def _transition(self, now, state): if self.state == Reconnect.ConnectInProgress: self.n_attempted_connections += 1 if state == Reconnect.Active: self.n_successful_connections += 1 connected_before = self.state.is_connected connected_now = state.is_connected if connected_before != connected_now: if connected_before: self.total_connected_duration += now - self.last_connected self.seqno += 1 vlog.dbg("%s: entering %s" % (self.name, state.name)) self.state = state self.state_entered = now def run(self, now): """Assesses whether any action should be taken on this FSM. The return value is one of: - None: The client need not take any action. - Active client, ovs.reconnect.CONNECT: The client should start a connection attempt and indicate this by calling self.connecting(). If the connection attempt has definitely succeeded, it should call self.connected(). If the connection attempt has definitely failed, it should call self.connect_failed(). The FSM is smart enough to back off correctly after successful connections that quickly abort, so it is OK to call self.connected() after a low-level successful connection (e.g. connect()) even if the connection might soon abort due to a failure at a high-level (e.g. SSL negotiation failure). - Passive client, ovs.reconnect.CONNECT: The client should try to listen for a connection, if it is not already listening. It should call self.listening() if successful, otherwise self.connecting() or reconnected_connect_failed() if the attempt is in progress or definitely failed, respectively. A listening passive client should constantly attempt to accept a new connection and report an accepted connection with self.connected(). - ovs.reconnect.DISCONNECT: The client should abort the current connection or connection attempt or listen attempt and call self.disconnected() or self.connect_failed() to indicate it. - ovs.reconnect.PROBE: The client should send some kind of request to the peer that will elicit a response, to ensure that the connection is indeed in working order. (This will only be returned if the "probe interval" is nonzero--see self.set_probe_interval()).""" deadline = self.state.deadline(self) if deadline is not None and now >= deadline: return self.state.run(self, now) else: return None def wait(self, poller, now): """Causes the next call to poller.block() to wake up when self.run() should be called.""" timeout = self.timeout(now) if timeout >= 0: poller.timer_wait(timeout) def timeout(self, now): """Returns the number of milliseconds after which self.run() should be called if nothing else notable happens in the meantime, or None if this is currently unnecessary.""" deadline = self.state.deadline(self) if deadline is not None: remaining = deadline - now return max(0, remaining) else: return None def is_connected(self): """Returns True if this FSM is currently believed to be connected, that is, if self.connected() was called more recently than any call to self.connect_failed() or self.disconnected() or self.disable(), and False otherwise.""" return self.state.is_connected def get_last_connect_elapsed(self, now): """Returns the number of milliseconds since 'fsm' was last connected to its peer. Returns None if never connected.""" if self.last_connected: return now - self.last_connected else: return None def get_last_disconnect_elapsed(self, now): """Returns the number of milliseconds since 'fsm' was last disconnected from its peer. Returns None if never disconnected.""" if self.last_disconnected: return now - self.last_disconnected else: return None def get_stats(self, now): class Stats(object): pass stats = Stats() stats.creation_time = self.creation_time stats.last_connected = self.last_connected stats.last_disconnected = self.last_disconnected stats.last_activity = self.last_activity stats.backoff = self.backoff stats.seqno = self.seqno stats.is_connected = self.is_connected() stats.msec_since_connect = self.get_last_connect_elapsed(now) stats.msec_since_disconnect = self.get_last_disconnect_elapsed(now) stats.total_connected_duration = self.total_connected_duration if self.is_connected(): stats.total_connected_duration += ( self.get_last_connect_elapsed(now)) stats.n_attempted_connections = self.n_attempted_connections stats.n_successful_connections = self.n_successful_connections stats.state = self.state.name stats.state_elapsed = now - self.state_entered return stats def __may_retry(self): if self.max_tries is None: return True elif self.max_tries > 0: self.max_tries -= 1 return True else: return False openvswitch-2.5.9/python/ovs/PaxHeaders.82075/__init__.py0000644000000000000000000000013213534540071020105 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.241849446 openvswitch-2.5.9/python/ovs/__init__.py0000644000175000017500000000004613534540071021573 0ustar00jpettitjpettit00000000000000# This file intentionally left blank. openvswitch-2.5.9/python/ovs/PaxHeaders.82075/ovsuuid.py0000644000000000000000000000013213534540071020044 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.257849563 openvswitch-2.5.9/python/ovs/ovsuuid.py0000644000175000017500000000376113534540071021541 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import uuid from ovs.db import error import ovs.db.parser uuidRE = re.compile("^xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx$" .replace('x', '[0-9a-fA-F]')) def zero(): return uuid.UUID(int=0) def is_valid_string(s): return uuidRE.match(s) is not None def from_string(s): if not is_valid_string(s): raise error.Error("%s is not a valid UUID" % s) return uuid.UUID(s) def from_json(json, symtab=None): try: s = ovs.db.parser.unwrap_json(json, "uuid", [str, unicode], "string") if not uuidRE.match(s): raise error.Error("\"%s\" is not a valid UUID" % s, json) return uuid.UUID(s) except error.Error, e: if not symtab: raise e try: name = ovs.db.parser.unwrap_json(json, "named-uuid", [str, unicode], "string") except error.Error: raise e if name not in symtab: symtab[name] = uuid.uuid4() return symtab[name] def to_json(uuid_): return ["uuid", str(uuid_)] def to_c_assignment(uuid_, var): """Returns an array of strings, each of which contain a C statement. The statements assign 'uuid_' to a "struct uuid" as defined in Open vSwitch lib/uuid.h.""" hex_string = uuid_.hex return ["%s.parts[%d] = 0x%s;" % (var, x, hex_string[x * 8:(x + 1) * 8]) for x in range(4)] openvswitch-2.5.9/python/ovs/PaxHeaders.82075/dirs.py.template0000644000000000000000000000013213534540071021121 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.285849769 openvswitch-2.5.9/python/ovs/dirs.py.template0000644000175000017500000000122713534540071022611 0ustar00jpettitjpettit00000000000000## The @variables@ in this file are replaced by default directories for ## use in python/ovs/dirs.py in the source directory and replaced by the ## configured directories for use in the installed python/ovs/dirs.py. ## import os PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """@pkgdatadir@""") RUNDIR = os.environ.get("OVS_RUNDIR", """@RUNDIR@""") LOGDIR = os.environ.get("OVS_LOGDIR", """@LOGDIR@""") BINDIR = os.environ.get("OVS_BINDIR", """@bindir@""") DBDIR = os.environ.get("OVS_DBDIR") if not DBDIR: sysconfdir = os.environ.get("OVS_SYSCONFDIR") if sysconfdir: DBDIR = "%s/openvswitch" % sysconfdir else: DBDIR = """@DBDIR@""" openvswitch-2.5.9/python/ovs/PaxHeaders.82075/db0000644000000000000000000000013213534540120016277 xustar0030 mtime=1567801424.249849504 30 atime=1567801425.625859648 30 ctime=1567801424.249849504 openvswitch-2.5.9/python/ovs/db/0000755000175000017500000000000013534540120020042 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/data.py0000644000000000000000000000013213534540071017644 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.245849474 openvswitch-2.5.9/python/ovs/db/data.py0000644000175000017500000004543213534540071021342 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011, 2014 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import uuid import ovs.poller import ovs.socket_util import ovs.json import ovs.jsonrpc import ovs.ovsuuid import ovs.db.parser from ovs.db import error import ovs.db.types class ConstraintViolation(error.Error): def __init__(self, msg, json=None): error.Error.__init__(self, msg, json, tag="constraint violation") def escapeCString(src): dst = [] for c in src: if c in "\\\"": dst.append("\\" + c) elif ord(c) < 32: if c == '\n': dst.append('\\n') elif c == '\r': dst.append('\\r') elif c == '\a': dst.append('\\a') elif c == '\b': dst.append('\\b') elif c == '\f': dst.append('\\f') elif c == '\t': dst.append('\\t') elif c == '\v': dst.append('\\v') else: dst.append('\\%03o' % ord(c)) else: dst.append(c) return ''.join(dst) def returnUnchanged(x): return x class Atom(object): def __init__(self, type_, value=None): self.type = type_ if value is not None: self.value = value else: self.value = type_.default_atom() def __cmp__(self, other): if not isinstance(other, Atom) or self.type != other.type: return NotImplemented elif self.value < other.value: return -1 elif self.value > other.value: return 1 else: return 0 def __hash__(self): return hash(self.value) @staticmethod def default(type_): """Returns the default value for the given type_, which must be an instance of ovs.db.types.AtomicType. The default value for each atomic type is; - 0, for integer or real atoms. - False, for a boolean atom. - "", for a string atom. - The all-zeros UUID, for a UUID atom.""" return Atom(type_) def is_default(self): return self == self.default(self.type) @staticmethod def from_json(base, json, symtab=None): type_ = base.type json = ovs.db.parser.float_to_int(json) if ((type_ == ovs.db.types.IntegerType and type(json) in [int, long]) or (type_ == ovs.db.types.RealType and type(json) in [int, long, float]) or (type_ == ovs.db.types.BooleanType and type(json) == bool) or (type_ == ovs.db.types.StringType and type(json) in [str, unicode])): atom = Atom(type_, json) elif type_ == ovs.db.types.UuidType: atom = Atom(type_, ovs.ovsuuid.from_json(json, symtab)) else: raise error.Error("expected %s" % type_.to_string(), json) atom.check_constraints(base) return atom @staticmethod def from_python(base, value): value = ovs.db.parser.float_to_int(value) if type(value) in base.type.python_types: atom = Atom(base.type, value) else: raise error.Error("expected %s, got %s" % (base.type, type(value))) atom.check_constraints(base) return atom def check_constraints(self, base): """Checks whether 'atom' meets the constraints (if any) defined in 'base' and raises an ovs.db.error.Error if any constraint is violated. 'base' and 'atom' must have the same type. Checking UUID constraints is deferred to transaction commit time, so this function does nothing for UUID constraints.""" assert base.type == self.type if base.enum is not None and self not in base.enum: raise ConstraintViolation( "%s is not one of the allowed values (%s)" % (self.to_string(), base.enum.to_string())) elif base.type in [ovs.db.types.IntegerType, ovs.db.types.RealType]: if ((base.min is None or self.value >= base.min) and (base.max is None or self.value <= base.max)): pass elif base.min is not None and base.max is not None: raise ConstraintViolation( "%s is not in the valid range %.15g to %.15g (inclusive)" % (self.to_string(), base.min, base.max)) elif base.min is not None: raise ConstraintViolation( "%s is less than minimum allowed value %.15g" % (self.to_string(), base.min)) else: raise ConstraintViolation( "%s is greater than maximum allowed value %.15g" % (self.to_string(), base.max)) elif base.type == ovs.db.types.StringType: # XXX The C version validates that the string is valid UTF-8 here. # Do we need to do that in Python too? s = self.value length = len(s) if length < base.min_length: raise ConstraintViolation( '"%s" length %d is less than minimum allowed length %d' % (s, length, base.min_length)) elif length > base.max_length: raise ConstraintViolation( '"%s" length %d is greater than maximum allowed ' 'length %d' % (s, length, base.max_length)) def to_json(self): if self.type == ovs.db.types.UuidType: return ovs.ovsuuid.to_json(self.value) else: return self.value def cInitAtom(self, var): if self.type == ovs.db.types.IntegerType: return ['%s.integer = %d;' % (var, self.value)] elif self.type == ovs.db.types.RealType: return ['%s.real = %.15g;' % (var, self.value)] elif self.type == ovs.db.types.BooleanType: if self.value: return ['%s.boolean = true;'] else: return ['%s.boolean = false;'] elif self.type == ovs.db.types.StringType: return ['%s.string = xstrdup("%s");' % (var, escapeCString(self.value))] elif self.type == ovs.db.types.UuidType: return ovs.ovsuuid.to_c_assignment(self.value, var) def toEnglish(self, escapeLiteral=returnUnchanged): if self.type == ovs.db.types.IntegerType: return '%d' % self.value elif self.type == ovs.db.types.RealType: return '%.15g' % self.value elif self.type == ovs.db.types.BooleanType: if self.value: return 'true' else: return 'false' elif self.type == ovs.db.types.StringType: return escapeLiteral(self.value) elif self.type == ovs.db.types.UuidType: return self.value.value __need_quotes_re = re.compile("$|true|false|[^_a-zA-Z]|.*[^-._a-zA-Z]") @staticmethod def __string_needs_quotes(s): return Atom.__need_quotes_re.match(s) def to_string(self): if self.type == ovs.db.types.IntegerType: return '%d' % self.value elif self.type == ovs.db.types.RealType: return '%.15g' % self.value elif self.type == ovs.db.types.BooleanType: if self.value: return 'true' else: return 'false' elif self.type == ovs.db.types.StringType: if Atom.__string_needs_quotes(self.value): return ovs.json.to_string(self.value) else: return self.value elif self.type == ovs.db.types.UuidType: return str(self.value) @staticmethod def new(x): if type(x) in [int, long]: t = ovs.db.types.IntegerType elif type(x) == float: t = ovs.db.types.RealType elif x in [False, True]: t = ovs.db.types.BooleanType elif type(x) in [str, unicode]: t = ovs.db.types.StringType elif isinstance(x, uuid): t = ovs.db.types.UuidType else: raise TypeError return Atom(t, x) class Datum(object): def __init__(self, type_, values={}): self.type = type_ self.values = values def __cmp__(self, other): if not isinstance(other, Datum): return NotImplemented elif self.values < other.values: return -1 elif self.values > other.values: return 1 else: return 0 __hash__ = None def __contains__(self, item): return item in self.values def copy(self): return Datum(self.type, dict(self.values)) @staticmethod def default(type_): if type_.n_min == 0: values = {} elif type_.is_map(): values = {type_.key.default(): type_.value.default()} else: values = {type_.key.default(): None} return Datum(type_, values) def is_default(self): return self == Datum.default(self.type) def check_constraints(self): """Checks that each of the atoms in 'datum' conforms to the constraints specified by its 'type' and raises an ovs.db.error.Error. This function is not commonly useful because the most ordinary way to obtain a datum is ultimately via Datum.from_json() or Atom.from_json(), which check constraints themselves.""" for keyAtom, valueAtom in self.values.iteritems(): keyAtom.check_constraints(self.type.key) if valueAtom is not None: valueAtom.check_constraints(self.type.value) @staticmethod def from_json(type_, json, symtab=None): """Parses 'json' as a datum of the type described by 'type'. If successful, returns a new datum. On failure, raises an ovs.db.error.Error. Violations of constraints expressed by 'type' are treated as errors. If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to RFC 7047 for information about this, and for the syntax that this function accepts.""" is_map = type_.is_map() if (is_map or (type(json) == list and len(json) > 0 and json[0] == "set")): if is_map: class_ = "map" else: class_ = "set" inner = ovs.db.parser.unwrap_json(json, class_, [list, tuple], "array") n = len(inner) if n < type_.n_min or n > type_.n_max: raise error.Error("%s must have %d to %d members but %d are " "present" % (class_, type_.n_min, type_.n_max, n), json) values = {} for element in inner: if is_map: key, value = ovs.db.parser.parse_json_pair(element) keyAtom = Atom.from_json(type_.key, key, symtab) valueAtom = Atom.from_json(type_.value, value, symtab) else: keyAtom = Atom.from_json(type_.key, element, symtab) valueAtom = None if keyAtom in values: if is_map: raise error.Error("map contains duplicate key") else: raise error.Error("set contains duplicate") values[keyAtom] = valueAtom return Datum(type_, values) else: keyAtom = Atom.from_json(type_.key, json, symtab) return Datum(type_, {keyAtom: None}) def to_json(self): if self.type.is_map(): return ["map", [[k.to_json(), v.to_json()] for k, v in sorted(self.values.items())]] elif len(self.values) == 1: key = self.values.keys()[0] return key.to_json() else: return ["set", [k.to_json() for k in sorted(self.values.keys())]] def to_string(self): head = tail = None if self.type.n_max > 1 or len(self.values) == 0: if self.type.is_map(): head = "{" tail = "}" else: head = "[" tail = "]" s = [] if head: s.append(head) for i, key in enumerate(sorted(self.values)): if i: s.append(", ") s.append(key.to_string()) if self.type.is_map(): s.append("=") s.append(self.values[key].to_string()) if tail: s.append(tail) return ''.join(s) def as_list(self): if self.type.is_map(): return [[k.value, v.value] for k, v in self.values.iteritems()] else: return [k.value for k in self.values.iterkeys()] def as_dict(self): return dict(self.values) def as_scalar(self): if len(self.values) == 1: if self.type.is_map(): k, v = self.values.iteritems()[0] return [k.value, v.value] else: return self.values.keys()[0].value else: return None def to_python(self, uuid_to_row): """Returns this datum's value converted into a natural Python representation of this datum's type, according to the following rules: - If the type has exactly one value and it is not a map (that is, self.type.is_scalar() returns True), then the value is: * An int or long, for an integer column. * An int or long or float, for a real column. * A bool, for a boolean column. * A str or unicode object, for a string column. * A uuid.UUID object, for a UUID column without a ref_table. * An object represented the referenced row, for a UUID column with a ref_table. (For the Idl, this object will be an ovs.db.idl.Row object.) If some error occurs (e.g. the database server's idea of the column is different from the IDL's idea), then the default value for the scalar type is used (see Atom.default()). - Otherwise, if the type is not a map, then the value is a Python list whose elements have the types described above. - Otherwise, the type is a map, and the value is a Python dict that maps from key to value, with key and value types determined as described above. 'uuid_to_row' must be a function that takes a value and an ovs.db.types.BaseType and translates UUIDs into row objects.""" if self.type.is_scalar(): value = uuid_to_row(self.as_scalar(), self.type.key) if value is None: return self.type.key.default() else: return value elif self.type.is_map(): value = {} for k, v in self.values.iteritems(): dk = uuid_to_row(k.value, self.type.key) dv = uuid_to_row(v.value, self.type.value) if dk is not None and dv is not None: value[dk] = dv return value else: s = set() for k in self.values: dk = uuid_to_row(k.value, self.type.key) if dk is not None: s.add(dk) return sorted(s) @staticmethod def from_python(type_, value, row_to_uuid): """Returns a new Datum with the given ovs.db.types.Type 'type_'. The new datum's value is taken from 'value', which must take the form described as a valid return value from Datum.to_python() for 'type'. Each scalar value within 'value' is initially passed through 'row_to_uuid', which should convert objects that represent rows (if any) into uuid.UUID objects and return other data unchanged. Raises ovs.db.error.Error if 'value' is not in an appropriate form for 'type_'.""" d = {} if type(value) == dict: for k, v in value.iteritems(): ka = Atom.from_python(type_.key, row_to_uuid(k)) va = Atom.from_python(type_.value, row_to_uuid(v)) d[ka] = va elif type(value) in (list, tuple): for k in value: ka = Atom.from_python(type_.key, row_to_uuid(k)) d[ka] = None else: ka = Atom.from_python(type_.key, row_to_uuid(value)) d[ka] = None datum = Datum(type_, d) datum.check_constraints() if not datum.conforms_to_type(): raise error.Error("%d values when type requires between %d and %d" % (len(d), type_.n_min, type_.n_max)) return datum def __getitem__(self, key): if not isinstance(key, Atom): key = Atom.new(key) if not self.type.is_map(): raise IndexError elif key not in self.values: raise KeyError else: return self.values[key].value def get(self, key, default=None): if not isinstance(key, Atom): key = Atom.new(key) if key in self.values: return self.values[key].value else: return default def __str__(self): return self.to_string() def conforms_to_type(self): n = len(self.values) return self.type.n_min <= n <= self.type.n_max def cInitDatum(self, var): if len(self.values) == 0: return ["ovsdb_datum_init_empty(%s);" % var] s = ["%s->n = %d;" % (var, len(self.values))] s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);" % (var, len(self.values), var)] for i, key in enumerate(sorted(self.values)): s += key.cInitAtom("%s->keys[%d]" % (var, i)) if self.type.value: s += ["%s->values = xmalloc(%d * sizeof *%s->values);" % (var, len(self.values), var)] for i, (key, value) in enumerate(sorted(self.values.items())): s += value.cInitAtom("%s->values[%d]" % (var, i)) else: s += ["%s->values = NULL;" % var] if len(self.values) > 1: s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);" % (var, self.type.key.type.to_string().upper())] return s openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/parser.py0000644000000000000000000000013213534540071020227 xustar0030 mtime=1567801401.833684342 30 atime=1567801402.125686488 30 ctime=1567801424.249849504 openvswitch-2.5.9/python/ovs/db/parser.py0000644000175000017500000000640313534540071021720 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re from ovs.db import error class Parser(object): def __init__(self, json, name): self.name = name self.json = json if type(json) != dict: self.__raise_error("Object expected.") self.used = set() def __get(self, name, types, optional, default=None): if name in self.json: self.used.add(name) member = float_to_int(self.json[name]) if is_identifier(member) and "id" in types: return member if len(types) and type(member) not in types: self.__raise_error("Type mismatch for member '%s'." % name) return member else: if not optional: self.__raise_error("Required '%s' member is missing." % name) return default def get(self, name, types): return self.__get(name, types, False) def get_optional(self, name, types, default=None): return self.__get(name, types, True, default) def __raise_error(self, message): raise error.Error("Parsing %s failed: %s" % (self.name, message), self.json) def finish(self): missing = set(self.json) - set(self.used) if missing: name = missing.pop() if len(missing) > 1: present = "and %d other members are" % len(missing) elif missing: present = "and 1 other member are" else: present = "is" self.__raise_error("Member '%s' %s present but not allowed here" % (name, present)) def float_to_int(x): # XXX still needed? if type(x) == float: integer = int(x) if integer == x and -2 ** 53 <= integer < 2 ** 53: return integer return x id_re = re.compile("[_a-zA-Z][_a-zA-Z0-9]*$") def is_identifier(s): return type(s) in [str, unicode] and id_re.match(s) def json_type_to_string(type_): if type_ == None: return "null" elif type_ == bool: return "boolean" elif type_ == dict: return "object" elif type_ == list: return "array" elif type_ in [int, long, float]: return "number" elif type_ in [str, unicode]: return "string" else: return "" def unwrap_json(json, name, types, desc): if (type(json) not in (list, tuple) or len(json) != 2 or json[0] != name or type(json[1]) not in types): raise error.Error('expected ["%s", <%s>]' % (name, desc), json) return json[1] def parse_json_pair(json): if type(json) != list or len(json) != 2: raise error.Error("expected 2-element array", json) return json openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/__init__.py0000644000000000000000000000013213534540071020472 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.241849446 openvswitch-2.5.9/python/ovs/db/__init__.py0000644000175000017500000000004613534540071022160 0ustar00jpettitjpettit00000000000000# This file intentionally left blank. openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/error.py0000644000000000000000000000013213534540071020064 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.245849474 openvswitch-2.5.9/python/ovs/db/error.py0000644000175000017500000000221013534540071021545 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import ovs.json class Error(Exception): def __init__(self, msg, json=None, tag=None): self.msg = msg self.json = json if tag is None: if json is None: self.tag = "ovsdb error" else: self.tag = "syntax error" else: self.tag = tag # Compose message. syntax = "" if self.json is not None: syntax = 'syntax "%s": ' % ovs.json.to_string(self.json) Exception.__init__(self, "%s%s: %s" % (syntax, self.tag, self.msg)) openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/types.py0000644000000000000000000000013213534540071020077 xustar0030 mtime=1567801401.833684342 30 atime=1567801402.125686488 30 ctime=1567801424.249849504 openvswitch-2.5.9/python/ovs/db/types.py0000644000175000017500000005233713534540071021577 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import uuid from ovs.db import error import ovs.db.parser import ovs.db.data import ovs.ovsuuid class AtomicType(object): def __init__(self, name, default, python_types): self.name = name self.default = default self.python_types = python_types @staticmethod def from_string(s): if s != "void": for atomic_type in ATOMIC_TYPES: if s == atomic_type.name: return atomic_type raise error.Error('"%s" is not an atomic-type' % s, s) @staticmethod def from_json(json): if type(json) not in [str, unicode]: raise error.Error("atomic-type expected", json) else: return AtomicType.from_string(json) def __str__(self): return self.name def to_string(self): return self.name def to_json(self): return self.name def default_atom(self): return ovs.db.data.Atom(self, self.default) VoidType = AtomicType("void", None, ()) IntegerType = AtomicType("integer", 0, (int, long)) RealType = AtomicType("real", 0.0, (int, long, float)) BooleanType = AtomicType("boolean", False, (bool,)) StringType = AtomicType("string", "", (str, unicode)) UuidType = AtomicType("uuid", ovs.ovsuuid.zero(), (uuid.UUID,)) ATOMIC_TYPES = [VoidType, IntegerType, RealType, BooleanType, StringType, UuidType] def escapeCString(src): dst = "" for c in src: if c in "\\\"": dst += "\\" + c elif ord(c) < 32: if c == '\n': dst += '\\n' elif c == '\r': dst += '\\r' elif c == '\a': dst += '\\a' elif c == '\b': dst += '\\b' elif c == '\f': dst += '\\f' elif c == '\t': dst += '\\t' elif c == '\v': dst += '\\v' else: dst += '\\%03o' % ord(c) else: dst += c return dst def commafy(x): """Returns integer x formatted in decimal with thousands set off by commas.""" return _commafy("%d" % x) def _commafy(s): if s.startswith('-'): return '-' + _commafy(s[1:]) elif len(s) <= 3: return s else: return _commafy(s[:-3]) + ',' + _commafy(s[-3:]) def returnUnchanged(x): return x class BaseType(object): def __init__(self, type_, enum=None, min=None, max=None, min_length=0, max_length=sys.maxint, ref_table_name=None): assert isinstance(type_, AtomicType) self.type = type_ self.enum = enum self.min = min self.max = max self.min_length = min_length self.max_length = max_length self.ref_table_name = ref_table_name if ref_table_name: self.ref_type = 'strong' else: self.ref_type = None self.ref_table = None def default(self): return ovs.db.data.Atom.default(self.type) def __eq__(self, other): if not isinstance(other, BaseType): return NotImplemented return (self.type == other.type and self.enum == other.enum and self.min == other.min and self.max == other.max and self.min_length == other.min_length and self.max_length == other.max_length and self.ref_table_name == other.ref_table_name) def __ne__(self, other): if not isinstance(other, BaseType): return NotImplemented else: return not (self == other) @staticmethod def __parse_uint(parser, name, default): value = parser.get_optional(name, [int, long]) if value is None: value = default else: max_value = 2 ** 32 - 1 if not (0 <= value <= max_value): raise error.Error("%s out of valid range 0 to %d" % (name, max_value), value) return value @staticmethod def from_json(json): if type(json) in [str, unicode]: return BaseType(AtomicType.from_json(json)) parser = ovs.db.parser.Parser(json, "ovsdb type") atomic_type = AtomicType.from_json(parser.get("type", [str, unicode])) base = BaseType(atomic_type) enum = parser.get_optional("enum", []) if enum is not None: base.enum = ovs.db.data.Datum.from_json( BaseType.get_enum_type(base.type), enum) elif base.type == IntegerType: base.min = parser.get_optional("minInteger", [int, long]) base.max = parser.get_optional("maxInteger", [int, long]) if (base.min is not None and base.max is not None and base.min > base.max): raise error.Error("minInteger exceeds maxInteger", json) elif base.type == RealType: base.min = parser.get_optional("minReal", [int, long, float]) base.max = parser.get_optional("maxReal", [int, long, float]) if (base.min is not None and base.max is not None and base.min > base.max): raise error.Error("minReal exceeds maxReal", json) elif base.type == StringType: base.min_length = BaseType.__parse_uint(parser, "minLength", 0) base.max_length = BaseType.__parse_uint(parser, "maxLength", sys.maxint) if base.min_length > base.max_length: raise error.Error("minLength exceeds maxLength", json) elif base.type == UuidType: base.ref_table_name = parser.get_optional("refTable", ['id']) if base.ref_table_name: base.ref_type = parser.get_optional("refType", [str, unicode], "strong") if base.ref_type not in ['strong', 'weak']: raise error.Error('refType must be "strong" or "weak" ' '(not "%s")' % base.ref_type) parser.finish() return base def to_json(self): if not self.has_constraints(): return self.type.to_json() json = {'type': self.type.to_json()} if self.enum: json['enum'] = self.enum.to_json() if self.type == IntegerType: if self.min is not None: json['minInteger'] = self.min if self.max is not None: json['maxInteger'] = self.max elif self.type == RealType: if self.min is not None: json['minReal'] = self.min if self.max is not None: json['maxReal'] = self.max elif self.type == StringType: if self.min_length != 0: json['minLength'] = self.min_length if self.max_length != sys.maxint: json['maxLength'] = self.max_length elif self.type == UuidType: if self.ref_table_name: json['refTable'] = self.ref_table_name if self.ref_type != 'strong': json['refType'] = self.ref_type return json def copy(self): base = BaseType(self.type, self.enum.copy(), self.min, self.max, self.min_length, self.max_length, self.ref_table_name) base.ref_table = self.ref_table return base def is_valid(self): if self.type in (VoidType, BooleanType, UuidType): return True elif self.type in (IntegerType, RealType): return self.min is None or self.max is None or self.min <= self.max elif self.type == StringType: return self.min_length <= self.max_length else: return False def has_constraints(self): return (self.enum is not None or self.min is not None or self.max is not None or self.min_length != 0 or self.max_length != sys.maxint or self.ref_table_name is not None) def without_constraints(self): return BaseType(self.type) @staticmethod def get_enum_type(atomic_type): """Returns the type of the 'enum' member for a BaseType whose 'type' is 'atomic_type'.""" return Type(BaseType(atomic_type), None, 1, sys.maxint) def is_ref(self): return self.type == UuidType and self.ref_table_name is not None def is_strong_ref(self): return self.is_ref() and self.ref_type == 'strong' def is_weak_ref(self): return self.is_ref() and self.ref_type == 'weak' def toEnglish(self, escapeLiteral=returnUnchanged): if self.type == UuidType and self.ref_table_name: s = escapeLiteral(self.ref_table_name) if self.ref_type == 'weak': s = "weak reference to " + s return s else: return self.type.to_string() def constraintsToEnglish(self, escapeLiteral=returnUnchanged, escapeNumber=returnUnchanged): if self.enum: literals = [value.toEnglish(escapeLiteral) for value in self.enum.values] if len(literals) == 1: english = 'must be %s' % (literals[0]) elif len(literals) == 2: english = 'either %s or %s' % (literals[0], literals[1]) else: english = 'one of %s, %s, or %s' % (literals[0], ', '.join(literals[1:-1]), literals[-1]) elif self.min is not None and self.max is not None: if self.type == IntegerType: english = 'in range %s to %s' % ( escapeNumber(commafy(self.min)), escapeNumber(commafy(self.max))) else: english = 'in range %s to %s' % ( escapeNumber("%g" % self.min), escapeNumber("%g" % self.max)) elif self.min is not None: if self.type == IntegerType: english = 'at least %s' % escapeNumber(commafy(self.min)) else: english = 'at least %s' % escapeNumber("%g" % self.min) elif self.max is not None: if self.type == IntegerType: english = 'at most %s' % escapeNumber(commafy(self.max)) else: english = 'at most %s' % escapeNumber("%g" % self.max) elif self.min_length != 0 and self.max_length != sys.maxint: if self.min_length == self.max_length: english = ('exactly %s characters long' % commafy(self.min_length)) else: english = ('between %s and %s characters long' % (commafy(self.min_length), commafy(self.max_length))) elif self.min_length != 0: return 'at least %s characters long' % commafy(self.min_length) elif self.max_length != sys.maxint: english = 'at most %s characters long' % commafy(self.max_length) else: english = '' return english def toCType(self, prefix): if self.ref_table_name: return "struct %s%s *" % (prefix, self.ref_table_name.lower()) else: return {IntegerType: 'int64_t ', RealType: 'double ', UuidType: 'struct uuid ', BooleanType: 'bool ', StringType: 'char *'}[self.type] def toAtomicType(self): return "OVSDB_TYPE_%s" % self.type.to_string().upper() def copyCValue(self, dst, src): args = {'dst': dst, 'src': src} if self.ref_table_name: return ("%(dst)s = %(src)s->header_.uuid;") % args elif self.type == StringType: return "%(dst)s = xstrdup(%(src)s);" % args else: return "%(dst)s = %(src)s;" % args def assign_c_value_casting_away_const(self, dst, src): args = {'dst': dst, 'src': src} if self.ref_table_name: return ("%(dst)s = %(src)s->header_.uuid;") % args elif self.type == StringType: return "%(dst)s = CONST_CAST(char *, %(src)s);" % args else: return "%(dst)s = %(src)s;" % args def initCDefault(self, var, is_optional): if self.ref_table_name: return "%s = NULL;" % var elif self.type == StringType and not is_optional: return '%s = "";' % var else: pattern = {IntegerType: '%s = 0;', RealType: '%s = 0.0;', UuidType: 'uuid_zero(&%s);', BooleanType: '%s = false;', StringType: '%s = NULL;'}[self.type] return pattern % var def cInitBaseType(self, indent, var): stmts = [] stmts.append('ovsdb_base_type_init(&%s, %s);' % ( var, self.toAtomicType())) if self.enum: stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);" % (var, var)) stmts += self.enum.cInitDatum("%s.enum_" % var) if self.type == IntegerType: if self.min is not None: stmts.append('%s.u.integer.min = INT64_C(%d);' % (var, self.min)) if self.max is not None: stmts.append('%s.u.integer.max = INT64_C(%d);' % (var, self.max)) elif self.type == RealType: if self.min is not None: stmts.append('%s.u.real.min = %d;' % (var, self.min)) if self.max is not None: stmts.append('%s.u.real.max = %d;' % (var, self.max)) elif self.type == StringType: if self.min_length is not None: stmts.append('%s.u.string.minLen = %d;' % (var, self.min_length)) if self.max_length != sys.maxint: stmts.append('%s.u.string.maxLen = %d;' % (var, self.max_length)) elif self.type == UuidType: if self.ref_table_name is not None: stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.ref_table_name))) stmts.append('%s.u.uuid.refType = OVSDB_REF_%s;' % (var, self.ref_type.upper())) return '\n'.join([indent + stmt for stmt in stmts]) class Type(object): DEFAULT_MIN = 1 DEFAULT_MAX = 1 def __init__(self, key, value=None, n_min=DEFAULT_MIN, n_max=DEFAULT_MAX): self.key = key self.value = value self.n_min = n_min self.n_max = n_max def copy(self): if self.value is None: value = None else: value = self.value.copy() return Type(self.key.copy(), value, self.n_min, self.n_max) def __eq__(self, other): if not isinstance(other, Type): return NotImplemented return (self.key == other.key and self.value == other.value and self.n_min == other.n_min and self.n_max == other.n_max) def __ne__(self, other): if not isinstance(other, Type): return NotImplemented else: return not (self == other) def is_valid(self): return (self.key.type != VoidType and self.key.is_valid() and (self.value is None or (self.value.type != VoidType and self.value.is_valid())) and self.n_min <= 1 <= self.n_max) def is_scalar(self): return self.n_min == 1 and self.n_max == 1 and not self.value def is_optional(self): return self.n_min == 0 and self.n_max == 1 def is_composite(self): return self.n_max > 1 def is_set(self): return self.value is None and (self.n_min != 1 or self.n_max != 1) def is_map(self): return self.value is not None def is_smap(self): return (self.is_map() and self.key.type == StringType and self.value.type == StringType) def is_optional_pointer(self): return (self.is_optional() and not self.value and (self.key.type == StringType or self.key.ref_table_name)) @staticmethod def __n_from_json(json, default): if json is None: return default elif type(json) == int and 0 <= json <= sys.maxint: return json else: raise error.Error("bad min or max value", json) @staticmethod def from_json(json): if type(json) in [str, unicode]: return Type(BaseType.from_json(json)) parser = ovs.db.parser.Parser(json, "ovsdb type") key_json = parser.get("key", [dict, str, unicode]) value_json = parser.get_optional("value", [dict, str, unicode]) min_json = parser.get_optional("min", [int]) max_json = parser.get_optional("max", [int, str, unicode]) parser.finish() key = BaseType.from_json(key_json) if value_json: value = BaseType.from_json(value_json) else: value = None n_min = Type.__n_from_json(min_json, Type.DEFAULT_MIN) if max_json == 'unlimited': n_max = sys.maxint else: n_max = Type.__n_from_json(max_json, Type.DEFAULT_MAX) type_ = Type(key, value, n_min, n_max) if not type_.is_valid(): raise error.Error("ovsdb type fails constraint checks", json) return type_ def to_json(self): if self.is_scalar() and not self.key.has_constraints(): return self.key.to_json() json = {"key": self.key.to_json()} if self.value is not None: json["value"] = self.value.to_json() if self.n_min != Type.DEFAULT_MIN: json["min"] = self.n_min if self.n_max == sys.maxint: json["max"] = "unlimited" elif self.n_max != Type.DEFAULT_MAX: json["max"] = self.n_max return json def toEnglish(self, escapeLiteral=returnUnchanged): keyName = self.key.toEnglish(escapeLiteral) if self.value: valueName = self.value.toEnglish(escapeLiteral) if self.is_scalar(): return keyName elif self.is_optional(): if self.value: return "optional %s-%s pair" % (keyName, valueName) else: return "optional %s" % keyName else: if self.n_max == sys.maxint: if self.n_min: quantity = "%s or more " % commafy(self.n_min) else: quantity = "" elif self.n_min: quantity = "%s to %s " % (commafy(self.n_min), commafy(self.n_max)) else: quantity = "up to %s " % commafy(self.n_max) if self.value: return "map of %s%s-%s pairs" % (quantity, keyName, valueName) else: if keyName.endswith('s'): plural = keyName + "es" else: plural = keyName + "s" return "set of %s%s" % (quantity, plural) def constraintsToEnglish(self, escapeLiteral=returnUnchanged, escapeNumber=returnUnchanged): constraints = [] keyConstraints = self.key.constraintsToEnglish(escapeLiteral, escapeNumber) if keyConstraints: if self.value: constraints.append('key %s' % keyConstraints) else: constraints.append(keyConstraints) if self.value: valueConstraints = self.value.constraintsToEnglish(escapeLiteral, escapeNumber) if valueConstraints: constraints.append('value %s' % valueConstraints) return ', '.join(constraints) def cDeclComment(self): if self.n_min == 1 and self.n_max == 1 and self.key.type == StringType: return "\t/* Always nonnull. */" else: return "" def cInitType(self, indent, var): initKey = self.key.cInitBaseType(indent, "%s.key" % var) if self.value: initValue = self.value.cInitBaseType(indent, "%s.value" % var) else: initValue = ('%sovsdb_base_type_init(&%s.value, ' 'OVSDB_TYPE_VOID);' % (indent, var)) initMin = "%s%s.n_min = %s;" % (indent, var, self.n_min) if self.n_max == sys.maxint: n_max = "UINT_MAX" else: n_max = self.n_max initMax = "%s%s.n_max = %s;" % (indent, var, n_max) return "\n".join((initKey, initValue, initMin, initMax)) openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/idl.py0000644000000000000000000000013213534540071017503 xustar0030 mtime=1567801401.833684342 30 atime=1567801402.121686458 30 ctime=1567801424.245849474 openvswitch-2.5.9/python/ovs/db/idl.py0000644000175000017500000016102713534540071021200 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import uuid import ovs.jsonrpc import ovs.db.parser import ovs.db.schema from ovs.db import error import ovs.ovsuuid import ovs.poller import ovs.vlog vlog = ovs.vlog.Vlog("idl") __pychecker__ = 'no-classattr no-objattrs' ROW_CREATE = "create" ROW_UPDATE = "update" ROW_DELETE = "delete" class Idl(object): """Open vSwitch Database Interface Definition Language (OVSDB IDL). The OVSDB IDL maintains an in-memory replica of a database. It issues RPC requests to an OVSDB database server and parses the responses, converting raw JSON into data structures that are easier for clients to digest. The IDL also assists with issuing database transactions. The client creates a transaction, manipulates the IDL data structures, and commits or aborts the transaction. The IDL then composes and issues the necessary JSON-RPC requests and reports to the client whether the transaction completed successfully. The client is allowed to access the following attributes directly, in a read-only fashion: - 'tables': This is the 'tables' map in the ovs.db.schema.DbSchema provided to the Idl constructor. Each ovs.db.schema.TableSchema in the map is annotated with a new attribute 'rows', which is a dict from a uuid.UUID to a Row object. The client may directly read and write the Row objects referenced by the 'rows' map values. Refer to Row for more details. - 'change_seqno': A number that represents the IDL's state. When the IDL is updated (by Idl.run()), its value changes. The sequence number can occasionally change even if the database does not. This happens if the connection to the database drops and reconnects, which causes the database contents to be reloaded even if they didn't change. (It could also happen if the database server sends out a "change" that reflects what the IDL already thought was in the database. The database server is not supposed to do that, but bugs could in theory cause it to do so.) - 'lock_name': The name of the lock configured with Idl.set_lock(), or None if no lock is configured. - 'has_lock': True, if the IDL is configured to obtain a lock and owns that lock, and False otherwise. Locking and unlocking happens asynchronously from the database client's point of view, so the information is only useful for optimization (e.g. if the client doesn't have the lock then there's no point in trying to write to the database). - 'is_lock_contended': True, if the IDL is configured to obtain a lock but the database server has indicated that some other client already owns the requested lock, and False otherwise. - 'txn': The ovs.db.idl.Transaction object for the database transaction currently being constructed, if there is one, or None otherwise. """ def __init__(self, remote, schema): """Creates and returns a connection to the database named 'db_name' on 'remote', which should be in a form acceptable to ovs.jsonrpc.session.open(). The connection will maintain an in-memory replica of the remote database. 'schema' should be the schema for the remote database. The caller may have cut it down by removing tables or columns that are not of interest. The IDL will only replicate the tables and columns that remain. The caller may also add a attribute named 'alert' to selected remaining columns, setting its value to False; if so, then changes to those columns will not be considered changes to the database for the purpose of the return value of Idl.run() and Idl.change_seqno. This is useful for columns that the IDL's client will write but not read. As a convenience to users, 'schema' may also be an instance of the SchemaHelper class. The IDL uses and modifies 'schema' directly.""" assert isinstance(schema, SchemaHelper) schema = schema.get_idl_schema() self.tables = schema.tables self.readonly = schema.readonly self._db = schema self._session = ovs.jsonrpc.Session.open(remote) self._monitor_request_id = None self._last_seqno = None self.change_seqno = 0 # Database locking. self.lock_name = None # Name of lock we need, None if none. self.has_lock = False # Has db server said we have the lock? self.is_lock_contended = False # Has db server said we can't get lock? self._lock_request_id = None # JSON-RPC ID of in-flight lock request. # Transaction support. self.txn = None self._outstanding_txns = {} for table in schema.tables.itervalues(): for column in table.columns.itervalues(): if not hasattr(column, 'alert'): column.alert = True table.need_table = False table.rows = {} table.idl = self def close(self): """Closes the connection to the database. The IDL will no longer update.""" self._session.close() def run(self): """Processes a batch of messages from the database server. Returns True if the database as seen through the IDL changed, False if it did not change. The initial fetch of the entire contents of the remote database is considered to be one kind of change. If the IDL has been configured to acquire a database lock (with Idl.set_lock()), then successfully acquiring the lock is also considered to be a change. This function can return occasional false positives, that is, report that the database changed even though it didn't. This happens if the connection to the database drops and reconnects, which causes the database contents to be reloaded even if they didn't change. (It could also happen if the database server sends out a "change" that reflects what we already thought was in the database, but the database server is not supposed to do that.) As an alternative to checking the return value, the client may check for changes in self.change_seqno.""" assert not self.txn initial_change_seqno = self.change_seqno self._session.run() i = 0 while i < 50: i += 1 if not self._session.is_connected(): break seqno = self._session.get_seqno() if seqno != self._last_seqno: self._last_seqno = seqno self.__txn_abort_all() self.__send_monitor_request() if self.lock_name: self.__send_lock_request() break msg = self._session.recv() if msg is None: break if (msg.type == ovs.jsonrpc.Message.T_NOTIFY and msg.method == "update" and len(msg.params) == 2 and msg.params[0] == None): # Database contents changed. self.__parse_update(msg.params[1]) elif (msg.type == ovs.jsonrpc.Message.T_REPLY and self._monitor_request_id is not None and self._monitor_request_id == msg.id): # Reply to our "monitor" request. try: self.change_seqno += 1 self._monitor_request_id = None self.__clear() self.__parse_update(msg.result) except error.Error, e: vlog.err("%s: parse error in received schema: %s" % (self._session.get_name(), e)) self.__error() elif (msg.type == ovs.jsonrpc.Message.T_REPLY and self._lock_request_id is not None and self._lock_request_id == msg.id): # Reply to our "lock" request. self.__parse_lock_reply(msg.result) elif (msg.type == ovs.jsonrpc.Message.T_NOTIFY and msg.method == "locked"): # We got our lock. self.__parse_lock_notify(msg.params, True) elif (msg.type == ovs.jsonrpc.Message.T_NOTIFY and msg.method == "stolen"): # Someone else stole our lock. self.__parse_lock_notify(msg.params, False) elif msg.type == ovs.jsonrpc.Message.T_NOTIFY and msg.id == "echo": # Reply to our echo request. Ignore it. pass elif (msg.type in (ovs.jsonrpc.Message.T_ERROR, ovs.jsonrpc.Message.T_REPLY) and self.__txn_process_reply(msg)): # __txn_process_reply() did everything needed. pass else: # This can happen if a transaction is destroyed before we # receive the reply, so keep the log level low. vlog.dbg("%s: received unexpected %s message" % (self._session.get_name(), ovs.jsonrpc.Message.type_to_string(msg.type))) return initial_change_seqno != self.change_seqno def wait(self, poller): """Arranges for poller.block() to wake up when self.run() has something to do or when activity occurs on a transaction on 'self'.""" self._session.wait(poller) self._session.recv_wait(poller) def has_ever_connected(self): """Returns True, if the IDL successfully connected to the remote database and retrieved its contents (even if the connection subsequently dropped and is in the process of reconnecting). If so, then the IDL contains an atomic snapshot of the database's contents (but it might be arbitrarily old if the connection dropped). Returns False if the IDL has never connected or retrieved the database's contents. If so, the IDL is empty.""" return self.change_seqno != 0 def force_reconnect(self): """Forces the IDL to drop its connection to the database and reconnect. In the meantime, the contents of the IDL will not change.""" self._session.force_reconnect() def set_lock(self, lock_name): """If 'lock_name' is not None, configures the IDL to obtain the named lock from the database server and to avoid modifying the database when the lock cannot be acquired (that is, when another client has the same lock). If 'lock_name' is None, drops the locking requirement and releases the lock.""" assert not self.txn assert not self._outstanding_txns if self.lock_name and (not lock_name or lock_name != self.lock_name): # Release previous lock. self.__send_unlock_request() self.lock_name = None self.is_lock_contended = False if lock_name and not self.lock_name: # Acquire new lock. self.lock_name = lock_name self.__send_lock_request() def notify(self, event, row, updates=None): """Hook for implementing create/update/delete notifications :param event: The event that was triggered :type event: ROW_CREATE, ROW_UPDATE, or ROW_DELETE :param row: The row as it is after the operation has occured :type row: Row :param updates: For updates, a Row object with just the changed columns :type updates: Row """ def __clear(self): changed = False for table in self.tables.itervalues(): if table.rows: changed = True table.rows = {} if changed: self.change_seqno += 1 def __update_has_lock(self, new_has_lock): if new_has_lock and not self.has_lock: if self._monitor_request_id is None: self.change_seqno += 1 else: # We're waiting for a monitor reply, so don't signal that the # database changed. The monitor reply will increment # change_seqno anyhow. pass self.is_lock_contended = False self.has_lock = new_has_lock def __do_send_lock_request(self, method): self.__update_has_lock(False) self._lock_request_id = None if self._session.is_connected(): msg = ovs.jsonrpc.Message.create_request(method, [self.lock_name]) msg_id = msg.id self._session.send(msg) else: msg_id = None return msg_id def __send_lock_request(self): self._lock_request_id = self.__do_send_lock_request("lock") def __send_unlock_request(self): self.__do_send_lock_request("unlock") def __parse_lock_reply(self, result): self._lock_request_id = None got_lock = type(result) == dict and result.get("locked") is True self.__update_has_lock(got_lock) if not got_lock: self.is_lock_contended = True def __parse_lock_notify(self, params, new_has_lock): if (self.lock_name is not None and type(params) in (list, tuple) and params and params[0] == self.lock_name): self.__update_has_lock(new_has_lock) if not new_has_lock: self.is_lock_contended = True def __send_monitor_request(self): monitor_requests = {} for table in self.tables.itervalues(): columns = [] for column in table.columns.keys(): if ((table.name not in self.readonly) or (table.name in self.readonly) and (column not in self.readonly[table.name])): columns.append(column) monitor_requests[table.name] = {"columns": columns} msg = ovs.jsonrpc.Message.create_request( "monitor", [self._db.name, None, monitor_requests]) self._monitor_request_id = msg.id self._session.send(msg) def __parse_update(self, update): try: self.__do_parse_update(update) except error.Error, e: vlog.err("%s: error parsing update: %s" % (self._session.get_name(), e)) def __do_parse_update(self, table_updates): if type(table_updates) != dict: raise error.Error(" is not an object", table_updates) for table_name, table_update in table_updates.iteritems(): table = self.tables.get(table_name) if not table: raise error.Error(' includes unknown ' 'table "%s"' % table_name) if type(table_update) != dict: raise error.Error(' for table "%s" is not ' 'an object' % table_name, table_update) for uuid_string, row_update in table_update.iteritems(): if not ovs.ovsuuid.is_valid_string(uuid_string): raise error.Error(' for table "%s" ' 'contains bad UUID "%s" as member ' 'name' % (table_name, uuid_string), table_update) uuid = ovs.ovsuuid.from_string(uuid_string) if type(row_update) != dict: raise error.Error(' for table "%s" ' 'contains for %s that ' 'is not an object' % (table_name, uuid_string)) parser = ovs.db.parser.Parser(row_update, "row-update") old = parser.get_optional("old", [dict]) new = parser.get_optional("new", [dict]) parser.finish() if not old and not new: raise error.Error(' missing "old" and ' '"new" members', row_update) if self.__process_update(table, uuid, old, new): self.change_seqno += 1 def __process_update(self, table, uuid, old, new): """Returns True if a column changed, False otherwise.""" row = table.rows.get(uuid) changed = False if not new: # Delete row. if row: del table.rows[uuid] changed = True self.notify(ROW_DELETE, row) else: # XXX rate-limit vlog.warn("cannot delete missing row %s from table %s" % (uuid, table.name)) elif not old: # Insert row. if not row: row = self.__create_row(table, uuid) changed = True else: # XXX rate-limit vlog.warn("cannot add existing row %s to table %s" % (uuid, table.name)) if self.__row_update(table, row, new): changed = True self.notify(ROW_CREATE, row) else: op = ROW_UPDATE if not row: row = self.__create_row(table, uuid) changed = True op = ROW_CREATE # XXX rate-limit vlog.warn("cannot modify missing row %s in table %s" % (uuid, table.name)) if self.__row_update(table, row, new): changed = True self.notify(op, row, Row.from_json(self, table, uuid, old)) return changed def __row_update(self, table, row, row_json): changed = False for column_name, datum_json in row_json.iteritems(): column = table.columns.get(column_name) if not column: # XXX rate-limit vlog.warn("unknown column %s updating table %s" % (column_name, table.name)) continue try: datum = ovs.db.data.Datum.from_json(column.type, datum_json) except error.Error, e: # XXX rate-limit vlog.warn("error parsing column %s in table %s: %s" % (column_name, table.name, e)) continue if datum != row._data[column_name]: row._data[column_name] = datum if column.alert: changed = True else: # Didn't really change but the OVSDB monitor protocol always # includes every value in a row. pass return changed def __create_row(self, table, uuid): data = {} for column in table.columns.itervalues(): data[column.name] = ovs.db.data.Datum.default(column.type) row = table.rows[uuid] = Row(self, table, uuid, data) return row def __error(self): self._session.force_reconnect() def __txn_abort_all(self): while self._outstanding_txns: txn = self._outstanding_txns.popitem()[1] txn._status = Transaction.TRY_AGAIN def __txn_process_reply(self, msg): txn = self._outstanding_txns.pop(msg.id, None) if txn: txn._process_reply(msg) def _uuid_to_row(atom, base): if base.ref_table: return base.ref_table.rows.get(atom) else: return atom def _row_to_uuid(value): if type(value) == Row: return value.uuid else: return value class Row(object): """A row within an IDL. The client may access the following attributes directly: - 'uuid': a uuid.UUID object whose value is the row's database UUID. - An attribute for each column in the Row's table, named for the column, whose values are as returned by Datum.to_python() for the column's type. If some error occurs (e.g. the database server's idea of the column is different from the IDL's idea), then the attribute values is the "default" value return by Datum.default() for the column's type. (It is important to know this because the default value may violate constraints for the column's type, e.g. the default integer value is 0 even if column contraints require the column's value to be positive.) When a transaction is active, column attributes may also be assigned new values. Committing the transaction will then cause the new value to be stored into the database. *NOTE*: In the current implementation, the value of a column is a *copy* of the value in the database. This means that modifying its value directly will have no useful effect. For example, the following: row.mycolumn["a"] = "b" # don't do this will not change anything in the database, even after commit. To modify the column, instead assign the modified column value back to the column: d = row.mycolumn d["a"] = "b" row.mycolumn = d """ def __init__(self, idl, table, uuid, data): # All of the explicit references to self.__dict__ below are required # to set real attributes with invoking self.__getattr__(). self.__dict__["uuid"] = uuid self.__dict__["_idl"] = idl self.__dict__["_table"] = table # _data is the committed data. It takes the following values: # # - A dictionary that maps every column name to a Datum, if the row # exists in the committed form of the database. # # - None, if this row is newly inserted within the active transaction # and thus has no committed form. self.__dict__["_data"] = data # _changes describes changes to this row within the active transaction. # It takes the following values: # # - {}, the empty dictionary, if no transaction is active or if the # row has yet not been changed within this transaction. # # - A dictionary that maps a column name to its new Datum, if an # active transaction changes those columns' values. # # - A dictionary that maps every column name to a Datum, if the row # is newly inserted within the active transaction. # # - None, if this transaction deletes this row. self.__dict__["_changes"] = {} # A dictionary whose keys are the names of columns that must be # verified as prerequisites when the transaction commits. The values # in the dictionary are all None. self.__dict__["_prereqs"] = {} def __getattr__(self, column_name): assert self._changes is not None datum = self._changes.get(column_name) if datum is None: if self._data is None: raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, column_name)) if column_name in self._data: datum = self._data[column_name] else: raise AttributeError("%s instance has no attribute '%s'" % (self.__class__.__name__, column_name)) return datum.to_python(_uuid_to_row) def __setattr__(self, column_name, value): assert self._changes is not None assert self._idl.txn if ((self._table.name in self._idl.readonly) and (column_name in self._idl.readonly[self._table.name])): vlog.warn("attempting to write to readonly column %s" % column_name) return column = self._table.columns[column_name] try: datum = ovs.db.data.Datum.from_python(column.type, value, _row_to_uuid) except error.Error, e: # XXX rate-limit vlog.err("attempting to write bad value to column %s (%s)" % (column_name, e)) return self._idl.txn._write(self, column, datum) @classmethod def from_json(cls, idl, table, uuid, row_json): data = {} for column_name, datum_json in row_json.iteritems(): column = table.columns.get(column_name) if not column: # XXX rate-limit vlog.warn("unknown column %s in table %s" % (column_name, table.name)) continue try: datum = ovs.db.data.Datum.from_json(column.type, datum_json) except error.Error, e: # XXX rate-limit vlog.warn("error parsing column %s in table %s: %s" % (column_name, table.name, e)) continue data[column_name] = datum return cls(idl, table, uuid, data) def verify(self, column_name): """Causes the original contents of column 'column_name' in this row to be verified as a prerequisite to completing the transaction. That is, if 'column_name' changed in this row (or if this row was deleted) between the time that the IDL originally read its contents and the time that the transaction commits, then the transaction aborts and Transaction.commit() returns Transaction.TRY_AGAIN. The intention is that, to ensure that no transaction commits based on dirty reads, an application should call Row.verify() on each data item read as part of a read-modify-write operation. In some cases Row.verify() reduces to a no-op, because the current value of the column is already known: - If this row is a row created by the current transaction (returned by Transaction.insert()). - If the column has already been modified within the current transaction. Because of the latter property, always call Row.verify() *before* modifying the column, for a given read-modify-write. A transaction must be in progress.""" assert self._idl.txn assert self._changes is not None if not self._data or column_name in self._changes: return self._prereqs[column_name] = None def delete(self): """Deletes this row from its table. A transaction must be in progress.""" assert self._idl.txn assert self._changes is not None if self._data is None: del self._idl.txn._txn_rows[self.uuid] else: self._idl.txn._txn_rows[self.uuid] = self self.__dict__["_changes"] = None del self._table.rows[self.uuid] def fetch(self, column_name): self._idl.txn._fetch(self, column_name) def increment(self, column_name): """Causes the transaction, when committed, to increment the value of 'column_name' within this row by 1. 'column_name' must have an integer type. After the transaction commits successfully, the client may retrieve the final (incremented) value of 'column_name' with Transaction.get_increment_new_value(). The client could accomplish something similar by reading and writing and verify()ing columns. However, increment() will never (by itself) cause a transaction to fail because of a verify error. The intended use is for incrementing the "next_cfg" column in the Open_vSwitch table.""" self._idl.txn._increment(self, column_name) def _uuid_name_from_uuid(uuid): return "row%s" % str(uuid).replace("-", "_") def _where_uuid_equals(uuid): return [["_uuid", "==", ["uuid", str(uuid)]]] class _InsertedRow(object): def __init__(self, op_index): self.op_index = op_index self.real = None class Transaction(object): """A transaction may modify the contents of a database by modifying the values of columns, deleting rows, inserting rows, or adding checks that columns in the database have not changed ("verify" operations), through Row methods. Reading and writing columns and inserting and deleting rows are all straightforward. The reasons to verify columns are less obvious. Verification is the key to maintaining transactional integrity. Because OVSDB handles multiple clients, it can happen that between the time that OVSDB client A reads a column and writes a new value, OVSDB client B has written that column. Client A's write should not ordinarily overwrite client B's, especially if the column in question is a "map" column that contains several more or less independent data items. If client A adds a "verify" operation before it writes the column, then the transaction fails in case client B modifies it first. Client A will then see the new value of the column and compose a new transaction based on the new contents written by client B. When a transaction is complete, which must be before the next call to Idl.run(), call Transaction.commit() or Transaction.abort(). The life-cycle of a transaction looks like this: 1. Create the transaction and record the initial sequence number: seqno = idl.change_seqno(idl) txn = Transaction(idl) 2. Modify the database with Row and Transaction methods. 3. Commit the transaction by calling Transaction.commit(). The first call to this function probably returns Transaction.INCOMPLETE. The client must keep calling again along as this remains true, calling Idl.run() in between to let the IDL do protocol processing. (If the client doesn't have anything else to do in the meantime, it can use Transaction.commit_block() to avoid having to loop itself.) 4. If the final status is Transaction.TRY_AGAIN, wait for Idl.change_seqno to change from the saved 'seqno' (it's possible that it's already changed, in which case the client should not wait at all), then start over from step 1. Only a call to Idl.run() will change the return value of Idl.change_seqno. (Transaction.commit_block() calls Idl.run().)""" # Status values that Transaction.commit() can return. UNCOMMITTED = "uncommitted" # Not yet committed or aborted. UNCHANGED = "unchanged" # Transaction didn't include any changes. INCOMPLETE = "incomplete" # Commit in progress, please wait. ABORTED = "aborted" # ovsdb_idl_txn_abort() called. SUCCESS = "success" # Commit successful. TRY_AGAIN = "try again" # Commit failed because a "verify" operation # reported an inconsistency, due to a network # problem, or other transient failure. Wait # for a change, then try again. NOT_LOCKED = "not locked" # Server hasn't given us the lock yet. ERROR = "error" # Commit failed due to a hard error. @staticmethod def status_to_string(status): """Converts one of the status values that Transaction.commit() can return into a human-readable string. (The status values are in fact such strings already, so there's nothing to do.)""" return status def __init__(self, idl): """Starts a new transaction on 'idl' (an instance of ovs.db.idl.Idl). A given Idl may only have a single active transaction at a time. A Transaction may modify the contents of a database by assigning new values to columns (attributes of Row), deleting rows (with Row.delete()), or inserting rows (with Transaction.insert()). It may also check that columns in the database have not changed with Row.verify(). When a transaction is complete (which must be before the next call to Idl.run()), call Transaction.commit() or Transaction.abort().""" assert idl.txn is None idl.txn = self self._request_id = None self.idl = idl self.dry_run = False self._txn_rows = {} self._status = Transaction.UNCOMMITTED self._error = None self._comments = [] self._inc_row = None self._inc_column = None self._fetch_requests = [] self._inserted_rows = {} # Map from UUID to _InsertedRow def add_comment(self, comment): """Appends 'comment' to the comments that will be passed to the OVSDB server when this transaction is committed. (The comment will be committed to the OVSDB log, which "ovsdb-tool show-log" can print in a relatively human-readable form.)""" self._comments.append(comment) def wait(self, poller): """Causes poll_block() to wake up if this transaction has completed committing.""" if self._status not in (Transaction.UNCOMMITTED, Transaction.INCOMPLETE): poller.immediate_wake() def _substitute_uuids(self, json): if type(json) in (list, tuple): if (len(json) == 2 and json[0] == 'uuid' and ovs.ovsuuid.is_valid_string(json[1])): uuid = ovs.ovsuuid.from_string(json[1]) row = self._txn_rows.get(uuid, None) if row and row._data is None: return ["named-uuid", _uuid_name_from_uuid(uuid)] else: return [self._substitute_uuids(elem) for elem in json] return json def __disassemble(self): self.idl.txn = None for row in self._txn_rows.itervalues(): if row._changes is None: row._table.rows[row.uuid] = row elif row._data is None: del row._table.rows[row.uuid] row.__dict__["_changes"] = {} row.__dict__["_prereqs"] = {} self._txn_rows = {} def commit(self): """Attempts to commit 'txn'. Returns the status of the commit operation, one of the following constants: Transaction.INCOMPLETE: The transaction is in progress, but not yet complete. The caller should call again later, after calling Idl.run() to let the IDL do OVSDB protocol processing. Transaction.UNCHANGED: The transaction is complete. (It didn't actually change the database, so the IDL didn't send any request to the database server.) Transaction.ABORTED: The caller previously called Transaction.abort(). Transaction.SUCCESS: The transaction was successful. The update made by the transaction (and possibly other changes made by other database clients) should already be visible in the IDL. Transaction.TRY_AGAIN: The transaction failed for some transient reason, e.g. because a "verify" operation reported an inconsistency or due to a network problem. The caller should wait for a change to the database, then compose a new transaction, and commit the new transaction. Use Idl.change_seqno to wait for a change in the database. It is important to use its value *before* the initial call to Transaction.commit() as the baseline for this purpose, because the change that one should wait for can happen after the initial call but before the call that returns Transaction.TRY_AGAIN, and using some other baseline value in that situation could cause an indefinite wait if the database rarely changes. Transaction.NOT_LOCKED: The transaction failed because the IDL has been configured to require a database lock (with Idl.set_lock()) but didn't get it yet or has already lost it. Committing a transaction rolls back all of the changes that it made to the IDL's copy of the database. If the transaction commits successfully, then the database server will send an update and, thus, the IDL will be updated with the committed changes.""" # The status can only change if we're the active transaction. # (Otherwise, our status will change only in Idl.run().) if self != self.idl.txn: return self._status # If we need a lock but don't have it, give up quickly. if self.idl.lock_name and not self.idl.has_lock: self._status = Transaction.NOT_LOCKED self.__disassemble() return self._status operations = [self.idl._db.name] # Assert that we have the required lock (avoiding a race). if self.idl.lock_name: operations.append({"op": "assert", "lock": self.idl.lock_name}) # Add prerequisites and declarations of new rows. for row in self._txn_rows.itervalues(): if row._prereqs: rows = {} columns = [] for column_name in row._prereqs: columns.append(column_name) rows[column_name] = row._data[column_name].to_json() operations.append({"op": "wait", "table": row._table.name, "timeout": 0, "where": _where_uuid_equals(row.uuid), "until": "==", "columns": columns, "rows": [rows]}) # Add updates. any_updates = False for row in self._txn_rows.itervalues(): if row._changes is None: if row._table.is_root: operations.append({"op": "delete", "table": row._table.name, "where": _where_uuid_equals(row.uuid)}) any_updates = True else: # Let ovsdb-server decide whether to really delete it. pass elif row._changes: op = {"table": row._table.name} if row._data is None: op["op"] = "insert" op["uuid-name"] = _uuid_name_from_uuid(row.uuid) any_updates = True op_index = len(operations) - 1 self._inserted_rows[row.uuid] = _InsertedRow(op_index) else: op["op"] = "update" op["where"] = _where_uuid_equals(row.uuid) row_json = {} op["row"] = row_json for column_name, datum in row._changes.iteritems(): if row._data is not None or not datum.is_default(): row_json[column_name] = ( self._substitute_uuids(datum.to_json())) # If anything really changed, consider it an update. # We can't suppress not-really-changed values earlier # or transactions would become nonatomic (see the big # comment inside Transaction._write()). if (not any_updates and row._data is not None and row._data[column_name] != datum): any_updates = True if row._data is None or row_json: operations.append(op) if self._fetch_requests: for fetch in self._fetch_requests: fetch["index"] = len(operations) - 1 operations.append({"op": "select", "table": fetch["row"]._table.name, "where": self._substitute_uuids( _where_uuid_equals(fetch["row"].uuid)), "columns": [fetch["column_name"]]}) any_updates = True # Add increment. if self._inc_row and any_updates: self._inc_index = len(operations) - 1 operations.append({"op": "mutate", "table": self._inc_row._table.name, "where": self._substitute_uuids( _where_uuid_equals(self._inc_row.uuid)), "mutations": [[self._inc_column, "+=", 1]]}) operations.append({"op": "select", "table": self._inc_row._table.name, "where": self._substitute_uuids( _where_uuid_equals(self._inc_row.uuid)), "columns": [self._inc_column]}) # Add comment. if self._comments: operations.append({"op": "comment", "comment": "\n".join(self._comments)}) # Dry run? if self.dry_run: operations.append({"op": "abort"}) if not any_updates: self._status = Transaction.UNCHANGED else: msg = ovs.jsonrpc.Message.create_request("transact", operations) self._request_id = msg.id if not self.idl._session.send(msg): self.idl._outstanding_txns[self._request_id] = self self._status = Transaction.INCOMPLETE else: self._status = Transaction.TRY_AGAIN self.__disassemble() return self._status def commit_block(self): """Attempts to commit this transaction, blocking until the commit either succeeds or fails. Returns the final commit status, which may be any Transaction.* value other than Transaction.INCOMPLETE. This function calls Idl.run() on this transaction'ss IDL, so it may cause Idl.change_seqno to change.""" while True: status = self.commit() if status != Transaction.INCOMPLETE: return status self.idl.run() poller = ovs.poller.Poller() self.idl.wait(poller) self.wait(poller) poller.block() def get_increment_new_value(self): """Returns the final (incremented) value of the column in this transaction that was set to be incremented by Row.increment. This transaction must have committed successfully.""" assert self._status == Transaction.SUCCESS return self._inc_new_value def abort(self): """Aborts this transaction. If Transaction.commit() has already been called then the transaction might get committed anyhow.""" self.__disassemble() if self._status in (Transaction.UNCOMMITTED, Transaction.INCOMPLETE): self._status = Transaction.ABORTED def get_error(self): """Returns a string representing this transaction's current status, suitable for use in log messages.""" if self._status != Transaction.ERROR: return Transaction.status_to_string(self._status) elif self._error: return self._error else: return "no error details available" def __set_error_json(self, json): if self._error is None: self._error = ovs.json.to_string(json) def get_insert_uuid(self, uuid): """Finds and returns the permanent UUID that the database assigned to a newly inserted row, given the UUID that Transaction.insert() assigned locally to that row. Returns None if 'uuid' is not a UUID assigned by Transaction.insert() or if it was assigned by that function and then deleted by Row.delete() within the same transaction. (Rows that are inserted and then deleted within a single transaction are never sent to the database server, so it never assigns them a permanent UUID.) This transaction must have completed successfully.""" assert self._status in (Transaction.SUCCESS, Transaction.UNCHANGED) inserted_row = self._inserted_rows.get(uuid) if inserted_row: return inserted_row.real return None def _increment(self, row, column): assert not self._inc_row self._inc_row = row self._inc_column = column def _fetch(self, row, column_name): self._fetch_requests.append({"row":row, "column_name":column_name}) def _write(self, row, column, datum): assert row._changes is not None txn = row._idl.txn # If this is a write-only column and the datum being written is the # same as the one already there, just skip the update entirely. This # is worth optimizing because we have a lot of columns that get # periodically refreshed into the database but don't actually change # that often. # # We don't do this for read/write columns because that would break # atomicity of transactions--some other client might have written a # different value in that column since we read it. (But if a whole # transaction only does writes of existing values, without making any # real changes, we will drop the whole transaction later in # ovsdb_idl_txn_commit().) if not column.alert and row._data and row._data.get(column.name) == datum: new_value = row._changes.get(column.name) if new_value is None or new_value == datum: return txn._txn_rows[row.uuid] = row row._changes[column.name] = datum.copy() def insert(self, table, new_uuid=None): """Inserts and returns a new row in 'table', which must be one of the ovs.db.schema.TableSchema objects in the Idl's 'tables' dict. The new row is assigned a provisional UUID. If 'uuid' is None then one is randomly generated; otherwise 'uuid' should specify a randomly generated uuid.UUID not otherwise in use. ovsdb-server will assign a different UUID when 'txn' is committed, but the IDL will replace any uses of the provisional UUID in the data to be to be committed by the UUID assigned by ovsdb-server.""" assert self._status == Transaction.UNCOMMITTED if new_uuid is None: new_uuid = uuid.uuid4() row = Row(self.idl, table, new_uuid, None) table.rows[row.uuid] = row self._txn_rows[row.uuid] = row return row def _process_reply(self, msg): if msg.type == ovs.jsonrpc.Message.T_ERROR: self._status = Transaction.ERROR elif type(msg.result) not in (list, tuple): # XXX rate-limit vlog.warn('reply to "transact" is not JSON array') else: hard_errors = False soft_errors = False lock_errors = False ops = msg.result for op in ops: if op is None: # This isn't an error in itself but indicates that some # prior operation failed, so make sure that we know about # it. soft_errors = True elif type(op) == dict: error = op.get("error") if error is not None: if error == "timed out": soft_errors = True elif error == "not owner": lock_errors = True elif error == "aborted": pass else: hard_errors = True self.__set_error_json(op) else: hard_errors = True self.__set_error_json(op) # XXX rate-limit vlog.warn("operation reply is not JSON null or object") if not soft_errors and not hard_errors and not lock_errors: if self._inc_row and not self.__process_inc_reply(ops): hard_errors = True if self._fetch_requests: if self.__process_fetch_reply(ops): self.idl.change_seqno += 1 else: hard_errors = True for insert in self._inserted_rows.itervalues(): if not self.__process_insert_reply(insert, ops): hard_errors = True if hard_errors: self._status = Transaction.ERROR elif lock_errors: self._status = Transaction.NOT_LOCKED elif soft_errors: self._status = Transaction.TRY_AGAIN else: self._status = Transaction.SUCCESS @staticmethod def __check_json_type(json, types, name): if not json: # XXX rate-limit vlog.warn("%s is missing" % name) return False elif type(json) not in types: # XXX rate-limit vlog.warn("%s has unexpected type %s" % (name, type(json))) return False else: return True def __process_fetch_reply(self, ops): update = False for fetch_request in self._fetch_requests: row = fetch_request["row"] column_name = fetch_request["column_name"] index = fetch_request["index"] table = row._table select = ops[index] fetched_rows = select.get("rows") if not Transaction.__check_json_type(fetched_rows, (list, tuple), '"select" reply "rows"'): return False if len(fetched_rows) != 1: # XXX rate-limit vlog.warn('"select" reply "rows" has %d elements ' 'instead of 1' % len(rows)) continue fetched_row = fetched_rows[0] if not Transaction.__check_json_type(fetched_row, (dict,), '"select" reply row'): continue column = table.columns.get(column_name) datum_json = fetched_row.get(column_name) datum = ovs.db.data.Datum.from_json(column.type, datum_json) row._data[column_name] = datum update = True return update def __process_inc_reply(self, ops): if self._inc_index + 2 > len(ops): # XXX rate-limit vlog.warn("reply does not contain enough operations for " "increment (has %d, needs %d)" % (len(ops), self._inc_index + 2)) # We know that this is a JSON object because the loop in # __process_reply() already checked. mutate = ops[self._inc_index] count = mutate.get("count") if not Transaction.__check_json_type(count, (int, long), '"mutate" reply "count"'): return False if count != 1: # XXX rate-limit vlog.warn('"mutate" reply "count" is %d instead of 1' % count) return False select = ops[self._inc_index + 1] rows = select.get("rows") if not Transaction.__check_json_type(rows, (list, tuple), '"select" reply "rows"'): return False if len(rows) != 1: # XXX rate-limit vlog.warn('"select" reply "rows" has %d elements ' 'instead of 1' % len(rows)) return False row = rows[0] if not Transaction.__check_json_type(row, (dict,), '"select" reply row'): return False column = row.get(self._inc_column) if not Transaction.__check_json_type(column, (int, long), '"select" reply inc column'): return False self._inc_new_value = column return True def __process_insert_reply(self, insert, ops): if insert.op_index >= len(ops): # XXX rate-limit vlog.warn("reply does not contain enough operations " "for insert (has %d, needs %d)" % (len(ops), insert.op_index)) return False # We know that this is a JSON object because the loop in # __process_reply() already checked. reply = ops[insert.op_index] json_uuid = reply.get("uuid") if not Transaction.__check_json_type(json_uuid, (tuple, list), '"insert" reply "uuid"'): return False try: uuid_ = ovs.ovsuuid.from_json(json_uuid) except error.Error: # XXX rate-limit vlog.warn('"insert" reply "uuid" is not a JSON UUID') return False insert.real = uuid_ return True class SchemaHelper(object): """IDL Schema helper. This class encapsulates the logic required to generate schemas suitable for creating 'ovs.db.idl.Idl' objects. Clients should register columns they are interested in using register_columns(). When finished, the get_idl_schema() function may be called. The location on disk of the schema used may be found in the 'schema_location' variable.""" def __init__(self, location=None, schema_json=None): """Creates a new Schema object. 'location' file path to ovs schema. None means default location 'schema_json' schema in json preresentation in memory """ if location and schema_json: raise ValueError("both location and schema_json can't be " "specified. it's ambiguous.") if schema_json is None: if location is None: location = "%s/vswitch.ovsschema" % ovs.dirs.PKGDATADIR schema_json = ovs.json.from_file(location) self.schema_json = schema_json self._tables = {} self._readonly = {} self._all = False def register_columns(self, table, columns, readonly=[]): """Registers interest in the given 'columns' of 'table'. Future calls to get_idl_schema() will include 'table':column for each column in 'columns'. This function automatically avoids adding duplicate entries to the schema. A subset of 'columns' can be specified as 'readonly'. The readonly columns are not replicated but can be fetched on-demand by the user with Row.fetch(). 'table' must be a string. 'columns' must be a list of strings. 'readonly' must be a list of strings. """ assert type(table) is str assert type(columns) is list columns = set(columns) | self._tables.get(table, set()) self._tables[table] = columns self._readonly[table] = readonly def register_table(self, table): """Registers interest in the given all columns of 'table'. Future calls to get_idl_schema() will include all columns of 'table'. 'table' must be a string """ assert type(table) is str self._tables[table] = set() # empty set means all columns in the table def register_all(self): """Registers interest in every column of every table.""" self._all = True def get_idl_schema(self): """Gets a schema appropriate for the creation of an 'ovs.db.id.IDL' object based on columns registered using the register_columns() function.""" schema = ovs.db.schema.DbSchema.from_json(self.schema_json) self.schema_json = None if not self._all: schema_tables = {} for table, columns in self._tables.iteritems(): schema_tables[table] = ( self._keep_table_columns(schema, table, columns)) schema.tables = schema_tables schema.readonly = self._readonly return schema def _keep_table_columns(self, schema, table_name, columns): assert table_name in schema.tables table = schema.tables[table_name] if not columns: # empty set means all columns in the table return table new_columns = {} for column_name in columns: assert type(column_name) is str assert column_name in table.columns new_columns[column_name] = table.columns[column_name] table.columns = new_columns return table openvswitch-2.5.9/python/ovs/db/PaxHeaders.82075/schema.py0000644000000000000000000000013213534540071020173 xustar0030 mtime=1567801401.833684342 30 atime=1567801402.125686488 30 ctime=1567801424.249849504 openvswitch-2.5.9/python/ovs/db/schema.py0000644000175000017500000002446213534540071021671 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import sys from ovs.db import error import ovs.db.parser from ovs.db import types def _check_id(name, json): if name.startswith('_'): raise error.Error('names beginning with "_" are reserved', json) elif not ovs.db.parser.is_identifier(name): raise error.Error("name must be a valid id", json) class DbSchema(object): """Schema for an OVSDB database.""" def __init__(self, name, version, tables): self.name = name self.version = version self.tables = tables # "isRoot" was not part of the original schema definition. Before it # was added, there was no support for garbage collection. So, for # backward compatibility, if the root set is empty then assume that # every table is in the root set. if self.__root_set_size() == 0: for table in self.tables.itervalues(): table.is_root = True # Find the "ref_table"s referenced by "ref_table_name"s. # # Also force certain columns to be persistent, as explained in # __check_ref_table(). This requires 'is_root' to be known, so this # must follow the loop updating 'is_root' above. for table in self.tables.itervalues(): for column in table.columns.itervalues(): self.__follow_ref_table(column, column.type.key, "key") self.__follow_ref_table(column, column.type.value, "value") def __root_set_size(self): """Returns the number of tables in the schema's root set.""" n_root = 0 for table in self.tables.itervalues(): if table.is_root: n_root += 1 return n_root @staticmethod def from_json(json): parser = ovs.db.parser.Parser(json, "database schema") name = parser.get("name", ['id']) version = parser.get_optional("version", [str, unicode]) parser.get_optional("cksum", [str, unicode]) tablesJson = parser.get("tables", [dict]) parser.finish() if (version is not None and not re.match('[0-9]+\.[0-9]+\.[0-9]+$', version)): raise error.Error('schema version "%s" not in format x.y.z' % version) tables = {} for tableName, tableJson in tablesJson.iteritems(): _check_id(tableName, json) tables[tableName] = TableSchema.from_json(tableJson, tableName) return DbSchema(name, version, tables) def to_json(self): # "isRoot" was not part of the original schema definition. Before it # was added, there was no support for garbage collection. So, for # backward compatibility, if every table is in the root set then do not # output "isRoot" in table schemas. default_is_root = self.__root_set_size() == len(self.tables) tables = {} for table in self.tables.itervalues(): tables[table.name] = table.to_json(default_is_root) json = {"name": self.name, "tables": tables} if self.version: json["version"] = self.version return json def copy(self): return DbSchema.from_json(self.to_json()) def __follow_ref_table(self, column, base, base_name): if not base or base.type != types.UuidType or not base.ref_table_name: return base.ref_table = self.tables.get(base.ref_table_name) if not base.ref_table: raise error.Error("column %s %s refers to undefined table %s" % (column.name, base_name, base.ref_table_name), tag="syntax error") if base.is_strong_ref() and not base.ref_table.is_root: # We cannot allow a strong reference to a non-root table to be # ephemeral: if it is the only reference to a row, then replaying # the database log from disk will cause the referenced row to be # deleted, even though it did exist in memory. If there are # references to that row later in the log (to modify it, to delete # it, or just to point to it), then this will yield a transaction # error. column.persistent = True class IdlSchema(DbSchema): def __init__(self, name, version, tables, idlPrefix, idlHeader): DbSchema.__init__(self, name, version, tables) self.idlPrefix = idlPrefix self.idlHeader = idlHeader @staticmethod def from_json(json): parser = ovs.db.parser.Parser(json, "IDL schema") idlPrefix = parser.get("idlPrefix", [str, unicode]) idlHeader = parser.get("idlHeader", [str, unicode]) subjson = dict(json) del subjson["idlPrefix"] del subjson["idlHeader"] schema = DbSchema.from_json(subjson) return IdlSchema(schema.name, schema.version, schema.tables, idlPrefix, idlHeader) def column_set_from_json(json, columns): if json is None: return tuple(columns) elif type(json) != list: raise error.Error("array of distinct column names expected", json) else: for column_name in json: if type(column_name) not in [str, unicode]: raise error.Error("array of distinct column names expected", json) elif column_name not in columns: raise error.Error("%s is not a valid column name" % column_name, json) if len(set(json)) != len(json): # Duplicate. raise error.Error("array of distinct column names expected", json) return tuple([columns[column_name] for column_name in json]) class TableSchema(object): def __init__(self, name, columns, mutable=True, max_rows=sys.maxint, is_root=True, indexes=[]): self.name = name self.columns = columns self.mutable = mutable self.max_rows = max_rows self.is_root = is_root self.indexes = indexes @staticmethod def from_json(json, name): parser = ovs.db.parser.Parser(json, "table schema for table %s" % name) columns_json = parser.get("columns", [dict]) mutable = parser.get_optional("mutable", [bool], True) max_rows = parser.get_optional("maxRows", [int]) is_root = parser.get_optional("isRoot", [bool], False) indexes_json = parser.get_optional("indexes", [list], []) parser.finish() if max_rows == None: max_rows = sys.maxint elif max_rows <= 0: raise error.Error("maxRows must be at least 1", json) if not columns_json: raise error.Error("table must have at least one column", json) columns = {} for column_name, column_json in columns_json.iteritems(): _check_id(column_name, json) columns[column_name] = ColumnSchema.from_json(column_json, column_name) indexes = [] for index_json in indexes_json: index = column_set_from_json(index_json, columns) if not index: raise error.Error("index must have at least one column", json) elif len(index) == 1: index[0].unique = True for column in index: if not column.persistent: raise error.Error("ephemeral columns (such as %s) may " "not be indexed" % column.name, json) indexes.append(index) return TableSchema(name, columns, mutable, max_rows, is_root, indexes) def to_json(self, default_is_root=False): """Returns this table schema serialized into JSON. The "isRoot" member is included in the JSON only if its value would differ from 'default_is_root'. Ordinarily 'default_is_root' should be false, because ordinarily a table would be not be part of the root set if its "isRoot" member is omitted. However, garbage collection was not originally included in OVSDB, so in older schemas that do not include any "isRoot" members, every table is implicitly part of the root set. To serialize such a schema in a way that can be read by older OVSDB tools, specify 'default_is_root' as True. """ json = {} if not self.mutable: json["mutable"] = False if default_is_root != self.is_root: json["isRoot"] = self.is_root json["columns"] = columns = {} for column in self.columns.itervalues(): if not column.name.startswith("_"): columns[column.name] = column.to_json() if self.max_rows != sys.maxint: json["maxRows"] = self.max_rows if self.indexes: json["indexes"] = [] for index in self.indexes: json["indexes"].append([column.name for column in index]) return json class ColumnSchema(object): def __init__(self, name, mutable, persistent, type_): self.name = name self.mutable = mutable self.persistent = persistent self.type = type_ self.unique = False @staticmethod def from_json(json, name): parser = ovs.db.parser.Parser(json, "schema for column %s" % name) mutable = parser.get_optional("mutable", [bool], True) ephemeral = parser.get_optional("ephemeral", [bool], False) type_ = types.Type.from_json(parser.get("type", [dict, str, unicode])) parser.finish() return ColumnSchema(name, mutable, not ephemeral, type_) def to_json(self): json = {"type": self.type.to_json()} if not self.mutable: json["mutable"] = False if not self.persistent: json["ephemeral"] = True return json openvswitch-2.5.9/python/ovs/PaxHeaders.82075/daemon.py0000644000000000000000000000013213534540071017611 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.241849446 openvswitch-2.5.9/python/ovs/daemon.py0000644000175000017500000004053013534540071021301 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import errno import fcntl import os import resource import signal import sys import time import ovs.dirs import ovs.fatal_signal #import ovs.lockfile import ovs.process import ovs.socket_util import ovs.timeval import ovs.util import ovs.vlog vlog = ovs.vlog.Vlog("daemon") # --detach: Should we run in the background? _detach = False # --pidfile: Name of pidfile (null if none). _pidfile = None # Our pidfile's inode and device, if we have created one. _pidfile_dev = None _pidfile_ino = None # --overwrite-pidfile: Create pidfile even if one already exists and is locked? _overwrite_pidfile = False # --no-chdir: Should we chdir to "/"? _chdir = True # --monitor: Should a supervisory process monitor the daemon and restart it if # it dies due to an error signal? _monitor = False # File descriptor used by daemonize_start() and daemonize_complete(). _daemonize_fd = None RESTART_EXIT_CODE = 5 def make_pidfile_name(name): """Returns the file name that would be used for a pidfile if 'name' were provided to set_pidfile().""" if name is None or name == "": return "%s/%s.pid" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME) else: return ovs.util.abs_file_name(ovs.dirs.RUNDIR, name) def set_pidfile(name): """Sets up a following call to daemonize() to create a pidfile named 'name'. If 'name' begins with '/', then it is treated as an absolute path. Otherwise, it is taken relative to ovs.util.RUNDIR, which is $(prefix)/var/run by default. If 'name' is null, then ovs.util.PROGRAM_NAME followed by ".pid" is used.""" global _pidfile _pidfile = make_pidfile_name(name) def set_no_chdir(): """Sets that we do not chdir to "/".""" global _chdir _chdir = False def ignore_existing_pidfile(): """Normally, daemonize() or daemonize_start() will terminate the program with a message if a locked pidfile already exists. If this function is called, an existing pidfile will be replaced, with a warning.""" global _overwrite_pidfile _overwrite_pidfile = True def set_detach(): """Sets up a following call to daemonize() to detach from the foreground session, running this process in the background.""" global _detach _detach = True def get_detach(): """Will daemonize() really detach?""" return _detach def set_monitor(): """Sets up a following call to daemonize() to fork a supervisory process to monitor the daemon and restart it if it dies due to an error signal.""" global _monitor _monitor = True def _fatal(msg): vlog.err(msg) sys.stderr.write("%s\n" % msg) sys.exit(1) def _make_pidfile(): """If a pidfile has been configured, creates it and stores the running process's pid in it. Ensures that the pidfile will be deleted when the process exits.""" pid = os.getpid() # Create a temporary pidfile. tmpfile = "%s.tmp%d" % (_pidfile, pid) ovs.fatal_signal.add_file_to_unlink(tmpfile) try: # This is global to keep Python from garbage-collecting and # therefore closing our file after this function exits. That would # unlock the lock for us, and we don't want that. global file_handle file_handle = open(tmpfile, "w") except IOError, e: _fatal("%s: create failed (%s)" % (tmpfile, e.strerror)) try: s = os.fstat(file_handle.fileno()) except IOError, e: _fatal("%s: fstat failed (%s)" % (tmpfile, e.strerror)) try: file_handle.write("%s\n" % pid) file_handle.flush() except OSError, e: _fatal("%s: write failed: %s" % (tmpfile, e.strerror)) try: fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError, e: _fatal("%s: fcntl failed: %s" % (tmpfile, e.strerror)) # Rename or link it to the correct name. if _overwrite_pidfile: try: os.rename(tmpfile, _pidfile) except OSError, e: _fatal("failed to rename \"%s\" to \"%s\" (%s)" % (tmpfile, _pidfile, e.strerror)) else: while True: try: os.link(tmpfile, _pidfile) error = 0 except OSError, e: error = e.errno if error == errno.EEXIST: _check_already_running() elif error != errno.EINTR: break if error: _fatal("failed to link \"%s\" as \"%s\" (%s)" % (tmpfile, _pidfile, os.strerror(error))) # Ensure that the pidfile will get deleted on exit. ovs.fatal_signal.add_file_to_unlink(_pidfile) # Delete the temporary pidfile if it still exists. if not _overwrite_pidfile: error = ovs.fatal_signal.unlink_file_now(tmpfile) if error: _fatal("%s: unlink failed (%s)" % (tmpfile, os.strerror(error))) global _pidfile_dev global _pidfile_ino _pidfile_dev = s.st_dev _pidfile_ino = s.st_ino def daemonize(): """If configured with set_pidfile() or set_detach(), creates the pid file and detaches from the foreground session.""" daemonize_start() daemonize_complete() def _waitpid(pid, options): while True: try: return os.waitpid(pid, options) except OSError, e: if e.errno == errno.EINTR: pass return -e.errno, 0 def _fork_and_wait_for_startup(): try: rfd, wfd = os.pipe() except OSError, e: sys.stderr.write("pipe failed: %s\n" % os.strerror(e.errno)) sys.exit(1) try: pid = os.fork() except OSError, e: sys.stderr.write("could not fork: %s\n" % os.strerror(e.errno)) sys.exit(1) if pid > 0: # Running in parent process. os.close(wfd) ovs.fatal_signal.fork() while True: try: s = os.read(rfd, 1) error = 0 except OSError, e: s = "" error = e.errno if error != errno.EINTR: break if len(s) != 1: retval, status = _waitpid(pid, 0) if retval == pid: if os.WIFEXITED(status) and os.WEXITSTATUS(status): # Child exited with an error. Convey the same error to # our parent process as a courtesy. sys.exit(os.WEXITSTATUS(status)) else: sys.stderr.write("fork child failed to signal " "startup (%s)\n" % ovs.process.status_msg(status)) else: assert retval < 0 sys.stderr.write("waitpid failed (%s)\n" % os.strerror(-retval)) sys.exit(1) os.close(rfd) else: # Running in parent process. os.close(rfd) ovs.timeval.postfork() #ovs.lockfile.postfork() global _daemonize_fd _daemonize_fd = wfd return pid def _fork_notify_startup(fd): if fd is not None: error, bytes_written = ovs.socket_util.write_fully(fd, "0") if error: sys.stderr.write("could not write to pipe\n") sys.exit(1) os.close(fd) def _should_restart(status): global RESTART_EXIT_CODE if os.WIFEXITED(status) and os.WEXITSTATUS(status) == RESTART_EXIT_CODE: return True if os.WIFSIGNALED(status): for signame in ("SIGABRT", "SIGALRM", "SIGBUS", "SIGFPE", "SIGILL", "SIGPIPE", "SIGSEGV", "SIGXCPU", "SIGXFSZ"): if os.WTERMSIG(status) == getattr(signal, signame, None): return True return False def _monitor_daemon(daemon_pid): # XXX should log daemon's stderr output at startup time # XXX should use setproctitle module if available last_restart = None while True: retval, status = _waitpid(daemon_pid, 0) if retval < 0: sys.stderr.write("waitpid failed\n") sys.exit(1) elif retval == daemon_pid: status_msg = ("pid %d died, %s" % (daemon_pid, ovs.process.status_msg(status))) if _should_restart(status): if os.WCOREDUMP(status): # Disable further core dumps to save disk space. try: resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) except resource.error: vlog.warn("failed to disable core dumps") # Throttle restarts to no more than once every 10 seconds. if (last_restart is not None and ovs.timeval.msec() < last_restart + 10000): vlog.warn("%s, waiting until 10 seconds since last " "restart" % status_msg) while True: now = ovs.timeval.msec() wakeup = last_restart + 10000 if now > wakeup: break print "sleep %f" % ((wakeup - now) / 1000.0) time.sleep((wakeup - now) / 1000.0) last_restart = ovs.timeval.msec() vlog.err("%s, restarting" % status_msg) daemon_pid = _fork_and_wait_for_startup() if not daemon_pid: break else: vlog.info("%s, exiting" % status_msg) sys.exit(0) # Running in new daemon process. def _close_standard_fds(): """Close stdin, stdout, stderr. If we're started from e.g. an SSH session, then this keeps us from holding that session open artificially.""" null_fd = ovs.socket_util.get_null_fd() if null_fd >= 0: os.dup2(null_fd, 0) os.dup2(null_fd, 1) os.dup2(null_fd, 2) def daemonize_start(): """If daemonization is configured, then starts daemonization, by forking and returning in the child process. The parent process hangs around until the child lets it know either that it completed startup successfully (by calling daemon_complete()) or that it failed to start up (by exiting with a nonzero exit code).""" if _detach: if _fork_and_wait_for_startup() > 0: # Running in parent process. sys.exit(0) # Running in daemon or monitor process. os.setsid() if _monitor: saved_daemonize_fd = _daemonize_fd daemon_pid = _fork_and_wait_for_startup() if daemon_pid > 0: # Running in monitor process. _fork_notify_startup(saved_daemonize_fd) _close_standard_fds() _monitor_daemon(daemon_pid) # Running in daemon process if _pidfile: _make_pidfile() def daemonize_complete(): """If daemonization is configured, then this function notifies the parent process that the child process has completed startup successfully.""" _fork_notify_startup(_daemonize_fd) if _detach: if _chdir: os.chdir("/") _close_standard_fds() def usage(): sys.stdout.write(""" Daemon options: --detach run in background as daemon --no-chdir do not chdir to '/' --pidfile[=FILE] create pidfile (default: %s/%s.pid) --overwrite-pidfile with --pidfile, start even if already running """ % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME)) def __read_pidfile(pidfile, delete_if_stale): if _pidfile_dev is not None: try: s = os.stat(pidfile) if s.st_ino == _pidfile_ino and s.st_dev == _pidfile_dev: # It's our own pidfile. We can't afford to open it, # because closing *any* fd for a file that a process # has locked also releases all the locks on that file. # # Fortunately, we know the associated pid anyhow. return os.getpid() except OSError: pass try: file_handle = open(pidfile, "r+") except IOError, e: if e.errno == errno.ENOENT and delete_if_stale: return 0 vlog.warn("%s: open: %s" % (pidfile, e.strerror)) return -e.errno # Python fcntl doesn't directly support F_GETLK so we have to just try # to lock it. try: fcntl.lockf(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB) # pidfile exists but wasn't locked by anyone. Now we have the lock. if not delete_if_stale: file_handle.close() vlog.warn("%s: pid file is stale" % pidfile) return -errno.ESRCH # Is the file we have locked still named 'pidfile'? try: raced = False s = os.stat(pidfile) s2 = os.fstat(file_handle.fileno()) if s.st_ino != s2.st_ino or s.st_dev != s2.st_dev: raced = True except IOError: raced = True if raced: vlog.warn("%s: lost race to delete pidfile" % pidfile) return -errno.EALREADY # We won the right to delete the stale pidfile. try: os.unlink(pidfile) except IOError, e: vlog.warn("%s: failed to delete stale pidfile (%s)" % (pidfile, e.strerror)) return -e.errno else: vlog.dbg("%s: deleted stale pidfile" % pidfile) file_handle.close() return 0 except IOError, e: if e.errno not in [errno.EACCES, errno.EAGAIN]: vlog.warn("%s: fcntl: %s" % (pidfile, e.strerror)) return -e.errno # Someone else has the pidfile locked. try: try: error = int(file_handle.readline()) except IOError, e: vlog.warn("%s: read: %s" % (pidfile, e.strerror)) error = -e.errno except ValueError: vlog.warn("%s does not contain a pid" % pidfile) error = -errno.EINVAL return error finally: try: file_handle.close() except IOError: pass def read_pidfile(pidfile): """Opens and reads a PID from 'pidfile'. Returns the positive PID if successful, otherwise a negative errno value.""" return __read_pidfile(pidfile, False) def _check_already_running(): pid = __read_pidfile(_pidfile, True) if pid > 0: _fatal("%s: already running as pid %d, aborting" % (_pidfile, pid)) elif pid < 0: _fatal("%s: pidfile check failed (%s), aborting" % (_pidfile, os.strerror(pid))) def add_args(parser): """Populates 'parser', an ArgumentParser allocated using the argparse module, with the command line arguments required by the daemon module.""" pidfile = make_pidfile_name(None) group = parser.add_argument_group(title="Daemon Options") group.add_argument("--detach", action="store_true", help="Run in background as a daemon.") group.add_argument("--no-chdir", action="store_true", help="Do not chdir to '/'.") group.add_argument("--monitor", action="store_true", help="Monitor %s process." % ovs.util.PROGRAM_NAME) group.add_argument("--pidfile", nargs="?", const=pidfile, help="Create pidfile (default %s)." % pidfile) group.add_argument("--overwrite-pidfile", action="store_true", help="With --pidfile, start even if already running.") def handle_args(args): """Handles daemon module settings in 'args'. 'args' is an object containing values parsed by the parse_args() method of ArgumentParser. The parent ArgumentParser should have been prepared by add_args() before calling parse_args().""" if args.detach: set_detach() if args.no_chdir: set_no_chdir() if args.pidfile: set_pidfile(args.pidfile) if args.overwrite_pidfile: ignore_existing_pidfile() if args.monitor: set_monitor() openvswitch-2.5.9/python/ovs/PaxHeaders.82075/fatal_signal.py0000644000000000000000000000013213534540071020772 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.249849504 openvswitch-2.5.9/python/ovs/fatal_signal.py0000644000175000017500000000657013534540071022470 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import atexit import os import signal import ovs.vlog _hooks = [] vlog = ovs.vlog.Vlog("fatal-signal") def add_hook(hook, cancel, run_at_exit): _init() _hooks.append((hook, cancel, run_at_exit)) def fork(): """Clears all of the fatal signal hooks without executing them. If any of the hooks passed a 'cancel' function to add_hook(), then those functions will be called, allowing them to free resources, etc. Following a fork, one of the resulting processes can call this function to allow it to terminate without calling the hooks registered before calling this function. New hooks registered after calling this function will take effect normally.""" global _hooks for hook, cancel, run_at_exit in _hooks: if cancel: cancel() _hooks = [] _added_hook = False _files = {} def add_file_to_unlink(file): """Registers 'file' to be unlinked when the program terminates via sys.exit() or a fatal signal.""" global _added_hook if not _added_hook: _added_hook = True add_hook(_unlink_files, _cancel_files, True) _files[file] = None def remove_file_to_unlink(file): """Unregisters 'file' from being unlinked when the program terminates via sys.exit() or a fatal signal.""" if file in _files: del _files[file] def unlink_file_now(file): """Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'. Returns 0 if successful, otherwise a positive errno value.""" error = _unlink(file) if error: vlog.warn("could not unlink \"%s\" (%s)" % (file, os.strerror(error))) remove_file_to_unlink(file) return error def _unlink_files(): for file_ in _files: _unlink(file_) def _cancel_files(): global _added_hook global _files _added_hook = False _files = {} def _unlink(file_): try: os.unlink(file_) return 0 except OSError, e: return e.errno def _signal_handler(signr, _): _call_hooks(signr) # Re-raise the signal with the default handling so that the program # termination status reflects that we were killed by this signal. signal.signal(signr, signal.SIG_DFL) os.kill(os.getpid(), signr) def _atexit_handler(): _call_hooks(0) recurse = False def _call_hooks(signr): global recurse if recurse: return recurse = True for hook, cancel, run_at_exit in _hooks: if signr != 0 or run_at_exit: hook() _inited = False def _init(): global _inited if not _inited: _inited = True for signr in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGALRM): if signal.getsignal(signr) == signal.SIG_DFL: signal.signal(signr, _signal_handler) atexit.register(_atexit_handler) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/stream.py0000644000000000000000000000013213534540071017641 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.261849593 openvswitch-2.5.9/python/ovs/stream.py0000644000175000017500000003043713534540071021336 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import errno import os import socket import ovs.poller import ovs.socket_util import ovs.vlog vlog = ovs.vlog.Vlog("stream") def stream_or_pstream_needs_probes(name): """ 1 if the stream or pstream specified by 'name' needs periodic probes to verify connectivity. For [p]streams which need probes, it can take a long time to notice the connection was dropped. Returns 0 if probes aren't needed, and -1 if 'name' is invalid""" if PassiveStream.is_valid_name(name) or Stream.is_valid_name(name): # Only unix and punix are supported currently. return 0 else: return -1 class Stream(object): """Bidirectional byte stream. Currently only Unix domain sockets are implemented.""" # States. __S_CONNECTING = 0 __S_CONNECTED = 1 __S_DISCONNECTED = 2 # Kinds of events that one might wait for. W_CONNECT = 0 # Connect complete (success or failure). W_RECV = 1 # Data received. W_SEND = 2 # Send buffer room available. _SOCKET_METHODS = {} @staticmethod def register_method(method, cls): Stream._SOCKET_METHODS[method + ":"] = cls @staticmethod def _find_method(name): for method, cls in Stream._SOCKET_METHODS.items(): if name.startswith(method): return cls return None @staticmethod def is_valid_name(name): """Returns True if 'name' is a stream name in the form "TYPE:ARGS" and TYPE is a supported stream type (currently only "unix:" and "tcp:"), otherwise False.""" return bool(Stream._find_method(name)) def __init__(self, socket, name, status): self.socket = socket self.name = name if status == errno.EAGAIN: self.state = Stream.__S_CONNECTING elif status == 0: self.state = Stream.__S_CONNECTED else: self.state = Stream.__S_DISCONNECTED self.error = 0 # Default value of dscp bits for connection between controller and manager. # Value of IPTOS_PREC_INTERNETCONTROL = 0xc0 which is defined # in is used. IPTOS_PREC_INTERNETCONTROL = 0xc0 DSCP_DEFAULT = IPTOS_PREC_INTERNETCONTROL >> 2 @staticmethod def open(name, dscp=DSCP_DEFAULT): """Attempts to connect a stream to a remote peer. 'name' is a connection name in the form "TYPE:ARGS", where TYPE is an active stream class's name and ARGS are stream class-specific. Currently the only supported TYPEs are "unix" and "tcp". Returns (error, stream): on success 'error' is 0 and 'stream' is the new Stream, on failure 'error' is a positive errno value and 'stream' is None. Never returns errno.EAGAIN or errno.EINPROGRESS. Instead, returns 0 and a new Stream. The connect() method can be used to check for successful connection completion.""" cls = Stream._find_method(name) if not cls: return errno.EAFNOSUPPORT, None suffix = name.split(":", 1)[1] if name.startswith("unix:"): suffix = ovs.util.abs_file_name(ovs.dirs.RUNDIR, suffix) error, sock = cls._open(suffix, dscp) if error: return error, None else: status = ovs.socket_util.check_connection_completion(sock) return 0, Stream(sock, name, status) @staticmethod def _open(suffix, dscp): raise NotImplementedError("This method must be overrided by subclass") @staticmethod def open_block((error, stream)): """Blocks until a Stream completes its connection attempt, either succeeding or failing. (error, stream) should be the tuple returned by Stream.open(). Returns a tuple of the same form. Typical usage: error, stream = Stream.open_block(Stream.open("unix:/tmp/socket"))""" if not error: while True: error = stream.connect() if error != errno.EAGAIN: break stream.run() poller = ovs.poller.Poller() stream.run_wait(poller) stream.connect_wait(poller) poller.block() assert error != errno.EINPROGRESS if error and stream: stream.close() stream = None return error, stream def close(self): self.socket.close() def __scs_connecting(self): retval = ovs.socket_util.check_connection_completion(self.socket) assert retval != errno.EINPROGRESS if retval == 0: self.state = Stream.__S_CONNECTED elif retval != errno.EAGAIN: self.state = Stream.__S_DISCONNECTED self.error = retval def connect(self): """Tries to complete the connection on this stream. If the connection is complete, returns 0 if the connection was successful or a positive errno value if it failed. If the connection is still in progress, returns errno.EAGAIN.""" if self.state == Stream.__S_CONNECTING: self.__scs_connecting() if self.state == Stream.__S_CONNECTING: return errno.EAGAIN elif self.state == Stream.__S_CONNECTED: return 0 else: assert self.state == Stream.__S_DISCONNECTED return self.error def recv(self, n): """Tries to receive up to 'n' bytes from this stream. Returns a (error, string) tuple: - If successful, 'error' is zero and 'string' contains between 1 and 'n' bytes of data. - On error, 'error' is a positive errno value. - If the connection has been closed in the normal fashion or if 'n' is 0, the tuple is (0, ""). The recv function will not block waiting for data to arrive. If no data have been received, it returns (errno.EAGAIN, "") immediately.""" retval = self.connect() if retval != 0: return (retval, "") elif n == 0: return (0, "") try: return (0, self.socket.recv(n)) except socket.error, e: return (ovs.socket_util.get_exception_errno(e), "") def send(self, buf): """Tries to send 'buf' on this stream. If successful, returns the number of bytes sent, between 1 and len(buf). 0 is only a valid return value if len(buf) is 0. On error, returns a negative errno value. Will not block. If no bytes can be immediately accepted for transmission, returns -errno.EAGAIN immediately.""" retval = self.connect() if retval != 0: return -retval elif len(buf) == 0: return 0 try: return self.socket.send(buf) except socket.error, e: return -ovs.socket_util.get_exception_errno(e) def run(self): pass def run_wait(self, poller): pass def wait(self, poller, wait): assert wait in (Stream.W_CONNECT, Stream.W_RECV, Stream.W_SEND) if self.state == Stream.__S_DISCONNECTED: poller.immediate_wake() return if self.state == Stream.__S_CONNECTING: wait = Stream.W_CONNECT if wait == Stream.W_RECV: poller.fd_wait(self.socket, ovs.poller.POLLIN) else: poller.fd_wait(self.socket, ovs.poller.POLLOUT) def connect_wait(self, poller): self.wait(poller, Stream.W_CONNECT) def recv_wait(self, poller): self.wait(poller, Stream.W_RECV) def send_wait(self, poller): self.wait(poller, Stream.W_SEND) def __del__(self): # Don't delete the file: we might have forked. self.socket.close() class PassiveStream(object): @staticmethod def is_valid_name(name): """Returns True if 'name' is a passive stream name in the form "TYPE:ARGS" and TYPE is a supported passive stream type (currently only "punix:"), otherwise False.""" return name.startswith("punix:") def __init__(self, sock, name, bind_path): self.name = name self.socket = sock self.bind_path = bind_path @staticmethod def open(name): """Attempts to start listening for remote stream connections. 'name' is a connection name in the form "TYPE:ARGS", where TYPE is an passive stream class's name and ARGS are stream class-specific. Currently the only supported TYPE is "punix". Returns (error, pstream): on success 'error' is 0 and 'pstream' is the new PassiveStream, on failure 'error' is a positive errno value and 'pstream' is None.""" if not PassiveStream.is_valid_name(name): return errno.EAFNOSUPPORT, None bind_path = name[6:] if name.startswith("punix:"): bind_path = ovs.util.abs_file_name(ovs.dirs.RUNDIR, bind_path) error, sock = ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, True, bind_path, None) if error: return error, None try: sock.listen(10) except socket.error, e: vlog.err("%s: listen: %s" % (name, os.strerror(e.error))) sock.close() return e.error, None return 0, PassiveStream(sock, name, bind_path) def close(self): """Closes this PassiveStream.""" self.socket.close() if self.bind_path is not None: ovs.fatal_signal.unlink_file_now(self.bind_path) self.bind_path = None def accept(self): """Tries to accept a new connection on this passive stream. Returns (error, stream): if successful, 'error' is 0 and 'stream' is the new Stream object, and on failure 'error' is a positive errno value and 'stream' is None. Will not block waiting for a connection. If no connection is ready to be accepted, returns (errno.EAGAIN, None) immediately.""" while True: try: sock, addr = self.socket.accept() ovs.socket_util.set_nonblocking(sock) return 0, Stream(sock, "unix:%s" % addr, 0) except socket.error, e: error = ovs.socket_util.get_exception_errno(e) if error != errno.EAGAIN: # XXX rate-limit vlog.dbg("accept: %s" % os.strerror(error)) return error, None def wait(self, poller): poller.fd_wait(self.socket, ovs.poller.POLLIN) def __del__(self): # Don't delete the file: we might have forked. self.socket.close() def usage(name): return """ Active %s connection methods: unix:FILE Unix domain socket named FILE tcp:IP:PORT TCP socket to IP with port no of PORT Passive %s connection methods: punix:FILE Listen on Unix domain socket FILE""" % (name, name) class UnixStream(Stream): @staticmethod def _open(suffix, dscp): connect_path = suffix return ovs.socket_util.make_unix_socket(socket.SOCK_STREAM, True, None, connect_path) Stream.register_method("unix", UnixStream) class TCPStream(Stream): @staticmethod def _open(suffix, dscp): error, sock = ovs.socket_util.inet_open_active(socket.SOCK_STREAM, suffix, 0, dscp) if not error: try: sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) except socket.error as e: sock.close() return ovs.socket_util.get_exception_errno(e), None return error, sock Stream.register_method("tcp", TCPStream) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/process.py0000644000000000000000000000013213534540071020024 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.257849563 openvswitch-2.5.9/python/ovs/process.py0000644000175000017500000000267313534540071021522 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import signal def _signal_status_msg(type_, signr): s = "%s by signal %d" % (type_, signr) for name in signal.__dict__: if name.startswith("SIG") and getattr(signal, name) == signr: return "%s (%s)" % (s, name) return s def status_msg(status): """Given 'status', which is a process status in the form reported by waitpid(2) and returned by process_status(), returns a string describing how the process terminated.""" if os.WIFEXITED(status): s = "exit status %d" % os.WEXITSTATUS(status) elif os.WIFSIGNALED(status): s = _signal_status_msg("killed", os.WTERMSIG(status)) elif os.WIFSTOPPED(status): s = _signal_status_msg("stopped", os.WSTOPSIG(status)) else: s = "terminated abnormally (%x)" % status if os.WCOREDUMP(status): s += ", core dumped" return s openvswitch-2.5.9/python/ovs/PaxHeaders.82075/jsonrpc.py0000644000000000000000000000013213534540071020024 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.253849533 openvswitch-2.5.9/python/ovs/jsonrpc.py0000644000175000017500000004356413534540071021526 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import errno import os import ovs.json import ovs.poller import ovs.reconnect import ovs.stream import ovs.timeval import ovs.util import ovs.vlog EOF = ovs.util.EOF vlog = ovs.vlog.Vlog("jsonrpc") class Message(object): T_REQUEST = 0 # Request. T_NOTIFY = 1 # Notification. T_REPLY = 2 # Successful reply. T_ERROR = 3 # Error reply. __types = {T_REQUEST: "request", T_NOTIFY: "notification", T_REPLY: "reply", T_ERROR: "error"} def __init__(self, type_, method, params, result, error, id): self.type = type_ self.method = method self.params = params self.result = result self.error = error self.id = id _next_id = 0 @staticmethod def _create_id(): this_id = Message._next_id Message._next_id += 1 return this_id @staticmethod def create_request(method, params): return Message(Message.T_REQUEST, method, params, None, None, Message._create_id()) @staticmethod def create_notify(method, params): return Message(Message.T_NOTIFY, method, params, None, None, None) @staticmethod def create_reply(result, id): return Message(Message.T_REPLY, None, None, result, None, id) @staticmethod def create_error(error, id): return Message(Message.T_ERROR, None, None, None, error, id) @staticmethod def type_to_string(type_): return Message.__types[type_] def __validate_arg(self, value, name, must_have): if (value is not None) == (must_have != 0): return None else: type_name = Message.type_to_string(self.type) if must_have: verb = "must" else: verb = "must not" return "%s %s have \"%s\"" % (type_name, verb, name) def is_valid(self): if self.params is not None and type(self.params) != list: return "\"params\" must be JSON array" pattern = {Message.T_REQUEST: 0x11001, Message.T_NOTIFY: 0x11000, Message.T_REPLY: 0x00101, Message.T_ERROR: 0x00011}.get(self.type) if pattern is None: return "invalid JSON-RPC message type %s" % self.type return ( self.__validate_arg(self.method, "method", pattern & 0x10000) or self.__validate_arg(self.params, "params", pattern & 0x1000) or self.__validate_arg(self.result, "result", pattern & 0x100) or self.__validate_arg(self.error, "error", pattern & 0x10) or self.__validate_arg(self.id, "id", pattern & 0x1)) @staticmethod def from_json(json): if type(json) != dict: return "message is not a JSON object" # Make a copy to avoid modifying the caller's dict. json = dict(json) if "method" in json: method = json.pop("method") if type(method) not in [str, unicode]: return "method is not a JSON string" else: method = None params = json.pop("params", None) result = json.pop("result", None) error = json.pop("error", None) id_ = json.pop("id", None) if len(json): return "message has unexpected member \"%s\"" % json.popitem()[0] if result is not None: msg_type = Message.T_REPLY elif error is not None: msg_type = Message.T_ERROR elif id_ is not None: msg_type = Message.T_REQUEST else: msg_type = Message.T_NOTIFY msg = Message(msg_type, method, params, result, error, id_) validation_error = msg.is_valid() if validation_error is not None: return validation_error else: return msg def to_json(self): json = {} if self.method is not None: json["method"] = self.method if self.params is not None: json["params"] = self.params if self.result is not None or self.type == Message.T_ERROR: json["result"] = self.result if self.error is not None or self.type == Message.T_REPLY: json["error"] = self.error if self.id is not None or self.type == Message.T_NOTIFY: json["id"] = self.id return json def __str__(self): s = [Message.type_to_string(self.type)] if self.method is not None: s.append("method=\"%s\"" % self.method) if self.params is not None: s.append("params=" + ovs.json.to_string(self.params)) if self.result is not None: s.append("result=" + ovs.json.to_string(self.result)) if self.error is not None: s.append("error=" + ovs.json.to_string(self.error)) if self.id is not None: s.append("id=" + ovs.json.to_string(self.id)) return ", ".join(s) class Connection(object): def __init__(self, stream): self.name = stream.name self.stream = stream self.status = 0 self.input = "" self.output = "" self.parser = None self.received_bytes = 0 def close(self): self.stream.close() self.stream = None def run(self): if self.status: return while len(self.output): retval = self.stream.send(self.output) if retval >= 0: self.output = self.output[retval:] else: if retval != -errno.EAGAIN: vlog.warn("%s: send error: %s" % (self.name, os.strerror(-retval))) self.error(-retval) break def wait(self, poller): if not self.status: self.stream.run_wait(poller) if len(self.output): self.stream.send_wait(poller) def get_status(self): return self.status def get_backlog(self): if self.status != 0: return 0 else: return len(self.output) def get_received_bytes(self): return self.received_bytes def __log_msg(self, title, msg): if vlog.dbg_is_enabled(): vlog.dbg("%s: %s %s" % (self.name, title, msg)) def send(self, msg): if self.status: return self.status self.__log_msg("send", msg) was_empty = len(self.output) == 0 self.output += ovs.json.to_string(msg.to_json()) if was_empty: self.run() return self.status def send_block(self, msg): error = self.send(msg) if error: return error while True: self.run() if not self.get_backlog() or self.get_status(): return self.status poller = ovs.poller.Poller() self.wait(poller) poller.block() def recv(self): if self.status: return self.status, None while True: if not self.input: error, data = self.stream.recv(4096) if error: if error == errno.EAGAIN: return error, None else: # XXX rate-limit vlog.warn("%s: receive error: %s" % (self.name, os.strerror(error))) self.error(error) return self.status, None elif not data: self.error(EOF) return EOF, None else: self.input += data self.received_bytes += len(data) else: if self.parser is None: self.parser = ovs.json.Parser() self.input = self.input[self.parser.feed(self.input):] if self.parser.is_done(): msg = self.__process_msg() if msg: return 0, msg else: return self.status, None def recv_block(self): while True: error, msg = self.recv() if error != errno.EAGAIN: return error, msg self.run() poller = ovs.poller.Poller() self.wait(poller) self.recv_wait(poller) poller.block() def transact_block(self, request): id_ = request.id error = self.send(request) reply = None while not error: error, reply = self.recv_block() if (reply and (reply.type == Message.T_REPLY or reply.type == Message.T_ERROR) and reply.id == id_): break return error, reply def __process_msg(self): json = self.parser.finish() self.parser = None if type(json) in [str, unicode]: # XXX rate-limit vlog.warn("%s: error parsing stream: %s" % (self.name, json)) self.error(errno.EPROTO) return msg = Message.from_json(json) if not isinstance(msg, Message): # XXX rate-limit vlog.warn("%s: received bad JSON-RPC message: %s" % (self.name, msg)) self.error(errno.EPROTO) return self.__log_msg("received", msg) return msg def recv_wait(self, poller): if self.status or self.input: poller.immediate_wake() else: self.stream.recv_wait(poller) def error(self, error): if self.status == 0: self.status = error self.stream.close() self.output = "" class Session(object): """A JSON-RPC session with reconnection.""" def __init__(self, reconnect, rpc): self.reconnect = reconnect self.rpc = rpc self.stream = None self.pstream = None self.seqno = 0 @staticmethod def open(name): """Creates and returns a Session that maintains a JSON-RPC session to 'name', which should be a string acceptable to ovs.stream.Stream or ovs.stream.PassiveStream's initializer. If 'name' is an active connection method, e.g. "tcp:127.1.2.3", the new session connects and reconnects, with back-off, to 'name'. If 'name' is a passive connection method, e.g. "ptcp:", the new session listens for connections to 'name'. It maintains at most one connection at any given time. Any new connection causes the previous one (if any) to be dropped.""" reconnect = ovs.reconnect.Reconnect(ovs.timeval.msec()) reconnect.set_name(name) reconnect.enable(ovs.timeval.msec()) if ovs.stream.PassiveStream.is_valid_name(name): reconnect.set_passive(True, ovs.timeval.msec()) if not ovs.stream.stream_or_pstream_needs_probes(name): reconnect.set_probe_interval(0) return Session(reconnect, None) @staticmethod def open_unreliably(jsonrpc): reconnect = ovs.reconnect.Reconnect(ovs.timeval.msec()) reconnect.set_quiet(True) reconnect.set_name(jsonrpc.name) reconnect.set_max_tries(0) reconnect.connected(ovs.timeval.msec()) return Session(reconnect, jsonrpc) def close(self): if self.rpc is not None: self.rpc.close() self.rpc = None if self.stream is not None: self.stream.close() self.stream = None if self.pstream is not None: self.pstream.close() self.pstream = None def __disconnect(self): if self.rpc is not None: self.rpc.error(EOF) self.rpc.close() self.rpc = None self.seqno += 1 elif self.stream is not None: self.stream.close() self.stream = None self.seqno += 1 def __connect(self): self.__disconnect() name = self.reconnect.get_name() if not self.reconnect.is_passive(): error, self.stream = ovs.stream.Stream.open(name) if not error: self.reconnect.connecting(ovs.timeval.msec()) else: self.reconnect.connect_failed(ovs.timeval.msec(), error) elif self.pstream is not None: error, self.pstream = ovs.stream.PassiveStream.open(name) if not error: self.reconnect.listening(ovs.timeval.msec()) else: self.reconnect.connect_failed(ovs.timeval.msec(), error) self.seqno += 1 def run(self): if self.pstream is not None: error, stream = self.pstream.accept() if error == 0: if self.rpc or self.stream: # XXX rate-limit vlog.info("%s: new connection replacing active " "connection" % self.reconnect.get_name()) self.__disconnect() self.reconnect.connected(ovs.timeval.msec()) self.rpc = Connection(stream) elif error != errno.EAGAIN: self.reconnect.listen_error(ovs.timeval.msec(), error) self.pstream.close() self.pstream = None if self.rpc: backlog = self.rpc.get_backlog() self.rpc.run() if self.rpc.get_backlog() < backlog: # Data previously caught in a queue was successfully sent (or # there's an error, which we'll catch below). # # We don't count data that is successfully sent immediately as # activity, because there's a lot of queuing downstream from # us, which means that we can push a lot of data into a # connection that has stalled and won't ever recover. self.reconnect.activity(ovs.timeval.msec()) error = self.rpc.get_status() if error != 0: self.reconnect.disconnected(ovs.timeval.msec(), error) self.__disconnect() elif self.stream is not None: self.stream.run() error = self.stream.connect() if error == 0: self.reconnect.connected(ovs.timeval.msec()) self.rpc = Connection(self.stream) self.stream = None elif error != errno.EAGAIN: self.reconnect.connect_failed(ovs.timeval.msec(), error) self.stream.close() self.stream = None action = self.reconnect.run(ovs.timeval.msec()) if action == ovs.reconnect.CONNECT: self.__connect() elif action == ovs.reconnect.DISCONNECT: self.reconnect.disconnected(ovs.timeval.msec(), 0) self.__disconnect() elif action == ovs.reconnect.PROBE: if self.rpc: request = Message.create_request("echo", []) request.id = "echo" self.rpc.send(request) else: assert action == None def wait(self, poller): if self.rpc is not None: self.rpc.wait(poller) elif self.stream is not None: self.stream.run_wait(poller) self.stream.connect_wait(poller) if self.pstream is not None: self.pstream.wait(poller) self.reconnect.wait(poller, ovs.timeval.msec()) def get_backlog(self): if self.rpc is not None: return self.rpc.get_backlog() else: return 0 def get_name(self): return self.reconnect.get_name() def send(self, msg): if self.rpc is not None: return self.rpc.send(msg) else: return errno.ENOTCONN def recv(self): if self.rpc is not None: received_bytes = self.rpc.get_received_bytes() error, msg = self.rpc.recv() if received_bytes != self.rpc.get_received_bytes(): # Data was successfully received. # # Previously we only counted receiving a full message as # activity, but with large messages or a slow connection that # policy could time out the session mid-message. self.reconnect.activity(ovs.timeval.msec()) if not error: if msg.type == Message.T_REQUEST and msg.method == "echo": # Echo request. Send reply. self.send(Message.create_reply(msg.params, msg.id)) elif msg.type == Message.T_REPLY and msg.id == "echo": # It's a reply to our echo request. Suppress it. pass else: return msg return None def recv_wait(self, poller): if self.rpc is not None: self.rpc.recv_wait(poller) def is_alive(self): if self.rpc is not None or self.stream is not None: return True else: max_tries = self.reconnect.get_max_tries() return max_tries is None or max_tries > 0 def is_connected(self): return self.rpc is not None def get_seqno(self): return self.seqno def force_reconnect(self): self.reconnect.force_reconnect(ovs.timeval.msec()) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/unixctl0000644000000000000000000000013213534540120017400 xustar0030 mtime=1567801424.265849622 30 atime=1567801425.625859648 30 ctime=1567801424.265849622 openvswitch-2.5.9/python/ovs/unixctl/0000755000175000017500000000000013534540120021143 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/ovs/unixctl/PaxHeaders.82075/__init__.py0000644000000000000000000000013213534540071021573 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.265849622 openvswitch-2.5.9/python/ovs/unixctl/__init__.py0000644000175000017500000000552613534540071023271 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import types import ovs.util commands = {} strtypes = types.StringTypes class _UnixctlCommand(object): def __init__(self, usage, min_args, max_args, callback, aux): self.usage = usage self.min_args = min_args self.max_args = max_args self.callback = callback self.aux = aux def _unixctl_help(conn, unused_argv, unused_aux): reply = "The available commands are:\n" command_names = sorted(commands.keys()) for name in command_names: reply += " " usage = commands[name].usage if usage: reply += "%-23s %s" % (name, usage) else: reply += name reply += "\n" conn.reply(reply) def command_register(name, usage, min_args, max_args, callback, aux): """ Registers a command with the given 'name' to be exposed by the UnixctlServer. 'usage' describes the arguments to the command; it is used only for presentation to the user in "help" output. 'callback' is called when the command is received. It is passed a UnixctlConnection object, the list of arguments as unicode strings, and 'aux'. Normally 'callback' should reply by calling UnixctlConnection.reply() or UnixctlConnection.reply_error() before it returns, but if the command cannot be handled immediately, then it can defer the reply until later. A given connection can only process a single request at a time, so a reply must be made eventually to avoid blocking that connection.""" assert isinstance(name, strtypes) assert isinstance(usage, strtypes) assert isinstance(min_args, int) assert isinstance(max_args, int) assert isinstance(callback, types.FunctionType) if name not in commands: commands[name] = _UnixctlCommand(usage, min_args, max_args, callback, aux) def socket_name_from_target(target): assert isinstance(target, strtypes) if target.startswith("/"): return 0, target pidfile_name = "%s/%s.pid" % (ovs.dirs.RUNDIR, target) pid = ovs.daemon.read_pidfile(pidfile_name) if pid < 0: return -pid, "cannot read pidfile \"%s\"" % pidfile_name return 0, "%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, target, pid) command_register("help", "", 0, 0, _unixctl_help, None) openvswitch-2.5.9/python/ovs/unixctl/PaxHeaders.82075/client.py0000644000000000000000000000013213534540071021312 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.265849622 openvswitch-2.5.9/python/ovs/unixctl/client.py0000644000175000017500000000403513534540071023002 0ustar00jpettitjpettit00000000000000# Copyright (c) 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import errno import os import types import ovs.jsonrpc import ovs.stream import ovs.util vlog = ovs.vlog.Vlog("unixctl_client") strtypes = types.StringTypes class UnixctlClient(object): def __init__(self, conn): assert isinstance(conn, ovs.jsonrpc.Connection) self._conn = conn def transact(self, command, argv): assert isinstance(command, strtypes) assert isinstance(argv, list) for arg in argv: assert isinstance(arg, strtypes) request = ovs.jsonrpc.Message.create_request(command, argv) error, reply = self._conn.transact_block(request) if error: vlog.warn("error communicating with %s: %s" % (self._conn.name, os.strerror(error))) return error, None, None if reply.error is not None: return 0, str(reply.error), None else: assert reply.result is not None return 0, None, str(reply.result) def close(self): self._conn.close() self.conn = None @staticmethod def create(path): assert isinstance(path, str) unix = "unix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path) error, stream = ovs.stream.Stream.open_block( ovs.stream.Stream.open(unix)) if error: vlog.warn("failed to connect to %s" % path) return error, None return 0, UnixctlClient(ovs.jsonrpc.Connection(stream)) openvswitch-2.5.9/python/ovs/unixctl/PaxHeaders.82075/server.py0000644000000000000000000000013213534540071021342 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.265849622 openvswitch-2.5.9/python/ovs/unixctl/server.py0000644000175000017500000001705113534540071023034 0ustar00jpettitjpettit00000000000000# Copyright (c) 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import errno import os import types import ovs.dirs import ovs.jsonrpc import ovs.stream import ovs.unixctl import ovs.util import ovs.version import ovs.vlog Message = ovs.jsonrpc.Message vlog = ovs.vlog.Vlog("unixctl_server") strtypes = types.StringTypes class UnixctlConnection(object): def __init__(self, rpc): assert isinstance(rpc, ovs.jsonrpc.Connection) self._rpc = rpc self._request_id = None def run(self): self._rpc.run() error = self._rpc.get_status() if error or self._rpc.get_backlog(): return error for _ in range(10): if error or self._request_id: break error, msg = self._rpc.recv() if msg: if msg.type == Message.T_REQUEST: self._process_command(msg) else: # XXX: rate-limit vlog.warn("%s: received unexpected %s message" % (self._rpc.name, Message.type_to_string(msg.type))) error = errno.EINVAL if not error: error = self._rpc.get_status() return error def reply(self, body): self._reply_impl(True, body) def reply_error(self, body): self._reply_impl(False, body) # Called only by unixctl classes. def _close(self): self._rpc.close() self._request_id = None def _wait(self, poller): self._rpc.wait(poller) if not self._rpc.get_backlog(): self._rpc.recv_wait(poller) def _reply_impl(self, success, body): assert isinstance(success, bool) assert body is None or isinstance(body, strtypes) assert self._request_id is not None if body is None: body = "" if body and not body.endswith("\n"): body += "\n" if success: reply = Message.create_reply(body, self._request_id) else: reply = Message.create_error(body, self._request_id) self._rpc.send(reply) self._request_id = None def _process_command(self, request): assert isinstance(request, ovs.jsonrpc.Message) assert request.type == ovs.jsonrpc.Message.T_REQUEST self._request_id = request.id error = None params = request.params method = request.method command = ovs.unixctl.commands.get(method) if command is None: error = '"%s" is not a valid command' % method elif len(params) < command.min_args: error = '"%s" command requires at least %d arguments' \ % (method, command.min_args) elif len(params) > command.max_args: error = '"%s" command takes at most %d arguments' \ % (method, command.max_args) else: for param in params: if not isinstance(param, strtypes): error = '"%s" command has non-string argument' % method break if error is None: unicode_params = [unicode(p) for p in params] command.callback(self, unicode_params, command.aux) if error: self.reply_error(error) def _unixctl_version(conn, unused_argv, version): assert isinstance(conn, UnixctlConnection) version = "%s (Open vSwitch) %s" % (ovs.util.PROGRAM_NAME, version) conn.reply(version) class UnixctlServer(object): def __init__(self, listener): assert isinstance(listener, ovs.stream.PassiveStream) self._listener = listener self._conns = [] def run(self): for _ in range(10): error, stream = self._listener.accept() if not error: rpc = ovs.jsonrpc.Connection(stream) self._conns.append(UnixctlConnection(rpc)) elif error == errno.EAGAIN: break else: # XXX: rate-limit vlog.warn("%s: accept failed: %s" % (self._listener.name, os.strerror(error))) for conn in copy.copy(self._conns): error = conn.run() if error and error != errno.EAGAIN: conn._close() self._conns.remove(conn) def wait(self, poller): self._listener.wait(poller) for conn in self._conns: conn._wait(poller) def close(self): for conn in self._conns: conn._close() self._conns = None self._listener.close() self._listener = None @staticmethod def create(path, version=None): """Creates a new UnixctlServer which listens on a unixctl socket created at 'path'. If 'path' is None, the default path is chosen. 'version' contains the version of the server as reported by the unixctl version command. If None, ovs.version.VERSION is used.""" assert path is None or isinstance(path, strtypes) if path is not None: path = "punix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path) else: path = "punix:%s/%s.%d.ctl" % (ovs.dirs.RUNDIR, ovs.util.PROGRAM_NAME, os.getpid()) if version is None: version = ovs.version.VERSION error, listener = ovs.stream.PassiveStream.open(path) if error: ovs.util.ovs_error(error, "could not initialize control socket %s" % path) return error, None ovs.unixctl.command_register("version", "", 0, 0, _unixctl_version, version) return 0, UnixctlServer(listener) class UnixctlClient(object): def __init__(self, conn): assert isinstance(conn, ovs.jsonrpc.Connection) self._conn = conn def transact(self, command, argv): assert isinstance(command, strtypes) assert isinstance(argv, list) for arg in argv: assert isinstance(arg, strtypes) request = Message.create_request(command, argv) error, reply = self._conn.transact_block(request) if error: vlog.warn("error communicating with %s: %s" % (self._conn.name, os.strerror(error))) return error, None, None if reply.error is not None: return 0, str(reply.error), None else: assert reply.result is not None return 0, None, str(reply.result) def close(self): self._conn.close() self.conn = None @staticmethod def create(path): assert isinstance(path, str) unix = "unix:%s" % ovs.util.abs_file_name(ovs.dirs.RUNDIR, path) error, stream = ovs.stream.Stream.open_block( ovs.stream.Stream.open(unix)) if error: vlog.warn("failed to connect to %s" % path) return error, None return 0, UnixctlClient(ovs.jsonrpc.Connection(stream)) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/json.py0000644000000000000000000000013213534540071017317 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.253849533 openvswitch-2.5.9/python/ovs/json.py0000644000175000017500000004431613534540071021015 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re import StringIO import sys __pychecker__ = 'no-stringiter' escapes = {ord('"'): u"\\\"", ord("\\"): u"\\\\", ord("\b"): u"\\b", ord("\f"): u"\\f", ord("\n"): u"\\n", ord("\r"): u"\\r", ord("\t"): u"\\t"} for esc in range(32): if esc not in escapes: escapes[esc] = u"\\u%04x" % esc SPACES_PER_LEVEL = 2 class _Serializer(object): def __init__(self, stream, pretty, sort_keys): self.stream = stream self.pretty = pretty self.sort_keys = sort_keys self.depth = 0 def __serialize_string(self, s): self.stream.write(u'"%s"' % ''.join(escapes.get(ord(c), c) for c in s)) def __indent_line(self): if self.pretty: self.stream.write('\n') self.stream.write(' ' * (SPACES_PER_LEVEL * self.depth)) def serialize(self, obj): if obj is None: self.stream.write(u"null") elif obj is False: self.stream.write(u"false") elif obj is True: self.stream.write(u"true") elif type(obj) in (int, long): self.stream.write(u"%d" % obj) elif type(obj) == float: self.stream.write("%.15g" % obj) elif type(obj) == unicode: self.__serialize_string(obj) elif type(obj) == str: self.__serialize_string(unicode(obj)) elif type(obj) == dict: self.stream.write(u"{") self.depth += 1 self.__indent_line() if self.sort_keys: items = sorted(obj.items()) else: items = obj.iteritems() for i, (key, value) in enumerate(items): if i > 0: self.stream.write(u",") self.__indent_line() self.__serialize_string(unicode(key)) self.stream.write(u":") if self.pretty: self.stream.write(u' ') self.serialize(value) self.stream.write(u"}") self.depth -= 1 elif type(obj) in (list, tuple): self.stream.write(u"[") self.depth += 1 if obj: self.__indent_line() for i, value in enumerate(obj): if i > 0: self.stream.write(u",") self.__indent_line() self.serialize(value) self.depth -= 1 self.stream.write(u"]") else: raise Exception("can't serialize %s as JSON" % obj) def to_stream(obj, stream, pretty=False, sort_keys=True): _Serializer(stream, pretty, sort_keys).serialize(obj) def to_file(obj, name, pretty=False, sort_keys=True): stream = open(name, "w") try: to_stream(obj, stream, pretty, sort_keys) finally: stream.close() def to_string(obj, pretty=False, sort_keys=True): output = StringIO.StringIO() to_stream(obj, output, pretty, sort_keys) s = output.getvalue() output.close() return s def from_stream(stream): p = Parser(check_trailer=True) while True: buf = stream.read(4096) if buf == "" or p.feed(buf) != len(buf): break return p.finish() def from_file(name): stream = open(name, "r") try: return from_stream(stream) finally: stream.close() def from_string(s): try: s = unicode(s, 'utf-8') except UnicodeDecodeError, e: seq = ' '.join(["0x%2x" % ord(c) for c in e.object[e.start:e.end] if ord(c) >= 0x80]) return ("not a valid UTF-8 string: invalid UTF-8 sequence %s" % seq) p = Parser(check_trailer=True) p.feed(s) return p.finish() class Parser(object): ## Maximum height of parsing stack. ## MAX_HEIGHT = 1000 def __init__(self, check_trailer=False): self.check_trailer = check_trailer # Lexical analysis. self.lex_state = Parser.__lex_start self.buffer = "" self.line_number = 0 self.column_number = 0 self.byte_number = 0 # Parsing. self.parse_state = Parser.__parse_start self.stack = [] self.member_name = None # Parse status. self.done = False self.error = None def __lex_start_space(self, c): pass def __lex_start_alpha(self, c): self.buffer = c self.lex_state = Parser.__lex_keyword def __lex_start_token(self, c): self.__parser_input(c) def __lex_start_number(self, c): self.buffer = c self.lex_state = Parser.__lex_number def __lex_start_string(self, _): self.lex_state = Parser.__lex_string def __lex_start_error(self, c): if ord(c) >= 32 and ord(c) < 128: self.__error("invalid character '%s'" % c) else: self.__error("invalid character U+%04x" % ord(c)) __lex_start_actions = {} for c in " \t\n\r": __lex_start_actions[c] = __lex_start_space for c in "abcdefghijklmnopqrstuvwxyz": __lex_start_actions[c] = __lex_start_alpha for c in "[{]}:,": __lex_start_actions[c] = __lex_start_token for c in "-0123456789": __lex_start_actions[c] = __lex_start_number __lex_start_actions['"'] = __lex_start_string def __lex_start(self, c): Parser.__lex_start_actions.get( c, Parser.__lex_start_error)(self, c) return True __lex_alpha = {} for c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": __lex_alpha[c] = True def __lex_finish_keyword(self): if self.buffer == "false": self.__parser_input(False) elif self.buffer == "true": self.__parser_input(True) elif self.buffer == "null": self.__parser_input(None) else: self.__error("invalid keyword '%s'" % self.buffer) def __lex_keyword(self, c): if c in Parser.__lex_alpha: self.buffer += c return True else: self.__lex_finish_keyword() return False __number_re = re.compile("(-)?(0|[1-9][0-9]*)" "(?:\.([0-9]+))?(?:[eE]([-+]?[0-9]+))?$") def __lex_finish_number(self): s = self.buffer m = Parser.__number_re.match(s) if m: sign, integer, fraction, exp = m.groups() if (exp is not None and (long(exp) > sys.maxint or long(exp) < -sys.maxint - 1)): self.__error("exponent outside valid range") return if fraction is not None and len(fraction.lstrip('0')) == 0: fraction = None sig_string = integer if fraction is not None: sig_string += fraction significand = int(sig_string) pow10 = 0 if fraction is not None: pow10 -= len(fraction) if exp is not None: pow10 += long(exp) if significand == 0: self.__parser_input(0) return elif significand <= 2 ** 63: while pow10 > 0 and significand <= 2 ** 63: significand *= 10 pow10 -= 1 while pow10 < 0 and significand % 10 == 0: significand /= 10 pow10 += 1 if (pow10 == 0 and ((not sign and significand < 2 ** 63) or (sign and significand <= 2 ** 63))): if sign: self.__parser_input(-significand) else: self.__parser_input(significand) return value = float(s) if value == float("inf") or value == float("-inf"): self.__error("number outside valid range") return if value == 0: # Suppress negative zero. value = 0 self.__parser_input(value) elif re.match("-?0[0-9]", s): self.__error("leading zeros not allowed") elif re.match("-([^0-9]|$)", s): self.__error("'-' must be followed by digit") elif re.match("-?(0|[1-9][0-9]*)\.([^0-9]|$)", s): self.__error("decimal point must be followed by digit") elif re.search("e[-+]?([^0-9]|$)", s): self.__error("exponent must contain at least one digit") else: self.__error("syntax error in number") def __lex_number(self, c): if c in ".0123456789eE-+": self.buffer += c return True else: self.__lex_finish_number() return False __4hex_re = re.compile("[0-9a-fA-F]{4}") def __lex_4hex(self, s): if len(s) < 4: self.__error("quoted string ends within \\u escape") elif not Parser.__4hex_re.match(s): self.__error("malformed \\u escape") elif s == "0000": self.__error("null bytes not supported in quoted strings") else: return int(s, 16) @staticmethod def __is_leading_surrogate(c): """Returns true if 'c' is a Unicode code point for a leading surrogate.""" return c >= 0xd800 and c <= 0xdbff @staticmethod def __is_trailing_surrogate(c): """Returns true if 'c' is a Unicode code point for a trailing surrogate.""" return c >= 0xdc00 and c <= 0xdfff @staticmethod def __utf16_decode_surrogate_pair(leading, trailing): """Returns the unicode code point corresponding to leading surrogate 'leading' and trailing surrogate 'trailing'. The return value will not make any sense if 'leading' or 'trailing' are not in the correct ranges for leading or trailing surrogates.""" # Leading surrogate: 110110wwwwxxxxxx # Trailing surrogate: 110111xxxxxxxxxx # Code point: 000uuuuuxxxxxxxxxxxxxxxx w = (leading >> 6) & 0xf u = w + 1 x0 = leading & 0x3f x1 = trailing & 0x3ff return (u << 16) | (x0 << 10) | x1 __unescape = {'"': u'"', "\\": u"\\", "/": u"/", "b": u"\b", "f": u"\f", "n": u"\n", "r": u"\r", "t": u"\t"} def __lex_finish_string(self): inp = self.buffer out = u"" while len(inp): backslash = inp.find('\\') if backslash == -1: out += inp break out += inp[:backslash] inp = inp[backslash + 1:] if inp == "": self.__error("quoted string may not end with backslash") return replacement = Parser.__unescape.get(inp[0]) if replacement is not None: out += replacement inp = inp[1:] continue elif inp[0] != u'u': self.__error("bad escape \\%s" % inp[0]) return c0 = self.__lex_4hex(inp[1:5]) if c0 is None: return inp = inp[5:] if Parser.__is_leading_surrogate(c0): if inp[:2] != u'\\u': self.__error("malformed escaped surrogate pair") return c1 = self.__lex_4hex(inp[2:6]) if c1 is None: return if not Parser.__is_trailing_surrogate(c1): self.__error("second half of escaped surrogate pair is " "not trailing surrogate") return code_point = Parser.__utf16_decode_surrogate_pair(c0, c1) inp = inp[6:] else: code_point = c0 out += unichr(code_point) self.__parser_input('string', out) def __lex_string_escape(self, c): self.buffer += c self.lex_state = Parser.__lex_string return True def __lex_string(self, c): if c == '\\': self.buffer += c self.lex_state = Parser.__lex_string_escape elif c == '"': self.__lex_finish_string() elif ord(c) >= 0x20: self.buffer += c else: self.__error("U+%04X must be escaped in quoted string" % ord(c)) return True def __lex_input(self, c): eat = self.lex_state(self, c) assert eat is True or eat is False return eat def __parse_start(self, token, unused_string): if token == '{': self.__push_object() elif token == '[': self.__push_array() else: self.__error("syntax error at beginning of input") def __parse_end(self, unused_token, unused_string): self.__error("trailing garbage at end of input") def __parse_object_init(self, token, string): if token == '}': self.__parser_pop() else: self.__parse_object_name(token, string) def __parse_object_name(self, token, string): if token == 'string': self.member_name = string self.parse_state = Parser.__parse_object_colon else: self.__error("syntax error parsing object expecting string") def __parse_object_colon(self, token, unused_string): if token == ":": self.parse_state = Parser.__parse_object_value else: self.__error("syntax error parsing object expecting ':'") def __parse_object_value(self, token, string): self.__parse_value(token, string, Parser.__parse_object_next) def __parse_object_next(self, token, unused_string): if token == ",": self.parse_state = Parser.__parse_object_name elif token == "}": self.__parser_pop() else: self.__error("syntax error expecting '}' or ','") def __parse_array_init(self, token, string): if token == ']': self.__parser_pop() else: self.__parse_array_value(token, string) def __parse_array_value(self, token, string): self.__parse_value(token, string, Parser.__parse_array_next) def __parse_array_next(self, token, unused_string): if token == ",": self.parse_state = Parser.__parse_array_value elif token == "]": self.__parser_pop() else: self.__error("syntax error expecting ']' or ','") def __parser_input(self, token, string=None): self.lex_state = Parser.__lex_start self.buffer = "" self.parse_state(self, token, string) def __put_value(self, value): top = self.stack[-1] if type(top) == dict: top[self.member_name] = value else: top.append(value) def __parser_push(self, new_json, next_state): if len(self.stack) < Parser.MAX_HEIGHT: if len(self.stack) > 0: self.__put_value(new_json) self.stack.append(new_json) self.parse_state = next_state else: self.__error("input exceeds maximum nesting depth %d" % Parser.MAX_HEIGHT) def __push_object(self): self.__parser_push({}, Parser.__parse_object_init) def __push_array(self): self.__parser_push([], Parser.__parse_array_init) def __parser_pop(self): if len(self.stack) == 1: self.parse_state = Parser.__parse_end if not self.check_trailer: self.done = True else: self.stack.pop() top = self.stack[-1] if type(top) == list: self.parse_state = Parser.__parse_array_next else: self.parse_state = Parser.__parse_object_next def __parse_value(self, token, string, next_state): if token in [False, None, True] or type(token) in [int, long, float]: self.__put_value(token) elif token == 'string': self.__put_value(string) else: if token == '{': self.__push_object() elif token == '[': self.__push_array() else: self.__error("syntax error expecting value") return self.parse_state = next_state def __error(self, message): if self.error is None: self.error = ("line %d, column %d, byte %d: %s" % (self.line_number, self.column_number, self.byte_number, message)) self.done = True def feed(self, s): i = 0 while True: if self.done or i >= len(s): return i c = s[i] if self.__lex_input(c): self.byte_number += 1 if c == '\n': self.column_number = 0 self.line_number += 1 else: self.column_number += 1 i += 1 def is_done(self): return self.done def finish(self): if self.lex_state == Parser.__lex_start: pass elif self.lex_state in (Parser.__lex_string, Parser.__lex_string_escape): self.__error("unexpected end of input in quoted string") else: self.__lex_input(" ") if self.parse_state == Parser.__parse_start: self.__error("empty input stream") elif self.parse_state != Parser.__parse_end: self.__error("unexpected end of input") if self.error == None: assert len(self.stack) == 1 return self.stack.pop() else: return self.error openvswitch-2.5.9/python/ovs/PaxHeaders.82075/timeval.py0000644000000000000000000000013213534540071020007 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.265849622 openvswitch-2.5.9/python/ovs/timeval.py0000644000175000017500000000457513534540071021510 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009, 2010 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import time try: import ctypes LIBRT = 'librt.so.1' clock_gettime_name = 'clock_gettime' if sys.platform.startswith("linux"): CLOCK_MONOTONIC = 1 time_t = ctypes.c_long elif sys.platform.startswith("netbsd"): # NetBSD uses function renaming for ABI versioning. While the proper # way to get the appropriate version is of course "#include ", # it is difficult with ctypes. The following is appropriate for # recent versions of NetBSD, including NetBSD-6. LIBRT = 'libc.so.12' clock_gettime_name = '__clock_gettime50' CLOCK_MONOTONIC = 3 time_t = ctypes.c_int64 elif sys.platform.startswith("freebsd"): CLOCK_MONOTONIC = 4 time_t = ctypes.c_int64 else: raise Exception class timespec(ctypes.Structure): _fields_ = [ ('tv_sec', time_t), ('tv_nsec', ctypes.c_long), ] librt = ctypes.CDLL(LIBRT) clock_gettime = getattr(librt, clock_gettime_name) clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)] except: # Librt shared library could not be loaded librt = None def monotonic(): if not librt: return time.time() t = timespec() if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) == 0: return t.tv_sec + t.tv_nsec * 1e-9 # Kernel does not support CLOCK_MONOTONIC return time.time() # Use time.monotonic() if Python version >= 3.3 if not hasattr(time, 'monotonic'): time.monotonic = monotonic def msec(): """ Returns the system's monotonic time if possible, otherwise returns the current time as the amount of time since the epoch, in milliseconds, as a float.""" return time.monotonic() * 1000.0 def postfork(): # Just a stub for now pass openvswitch-2.5.9/python/ovs/PaxHeaders.82075/dirs.py0000644000000000000000000000013213534540071017307 xustar0030 mtime=1567801401.837684371 30 atime=1567801402.125686488 30 ctime=1567801424.273849681 openvswitch-2.5.9/python/ovs/dirs.py0000644000175000017500000000075713534540071021006 0ustar00jpettitjpettit00000000000000import os PKGDATADIR = os.environ.get("OVS_PKGDATADIR", """/usr/local/share/openvswitch""") RUNDIR = os.environ.get("OVS_RUNDIR", """/var/run""") LOGDIR = os.environ.get("OVS_LOGDIR", """/usr/local/var/log""") BINDIR = os.environ.get("OVS_BINDIR", """/usr/local/bin""") DBDIR = os.environ.get("OVS_DBDIR") if not DBDIR: sysconfdir = os.environ.get("OVS_SYSCONFDIR") if sysconfdir: DBDIR = "%s/openvswitch" % sysconfdir else: DBDIR = """/usr/local/etc/openvswitch""" openvswitch-2.5.9/python/ovs/PaxHeaders.82075/vlog.py0000644000000000000000000000013213534540071017315 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.273849681 openvswitch-2.5.9/python/ovs/vlog.py0000644000175000017500000003570413534540071021014 0ustar00jpettitjpettit00000000000000 # Copyright (c) 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import logging import logging.handlers import os import re import socket import sys import threading import ovs.dirs import ovs.unixctl import ovs.util DESTINATIONS = {"console": "info", "file": "info", "syslog": "info"} PATTERNS = { "console": "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m", "file": "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m", "syslog": "ovs|%05N|%c%T|%p|%m", } LEVELS = { "dbg": logging.DEBUG, "info": logging.INFO, "warn": logging.WARNING, "err": logging.ERROR, "emer": logging.CRITICAL, "off": logging.CRITICAL } FACILITIES = ['auth', 'authpriv', 'cron', 'daemon', 'ftp', 'kern', 'lpr', 'mail', 'news', 'syslog', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7'] syslog_facility = "daemon" syslog_handler = '' def get_level(level_str): return LEVELS.get(level_str.lower()) class Vlog: __inited = False __msg_num = 0 __start_time = 0 __mfl = {} # Module -> destination -> level __log_file = None __file_handler = None __log_patterns = PATTERNS def __init__(self, name): """Creates a new Vlog object representing a module called 'name'. The created Vlog object will do nothing until the Vlog.init() static method is called. Once called, no more Vlog objects may be created.""" assert not Vlog.__inited self.name = name.lower() if name not in Vlog.__mfl: Vlog.__mfl[self.name] = DESTINATIONS.copy() def __log(self, level, message, **kwargs): if not Vlog.__inited: return level_num = LEVELS.get(level.lower(), logging.DEBUG) msg_num = Vlog.__msg_num Vlog.__msg_num += 1 for f, f_level in Vlog.__mfl[self.name].iteritems(): f_level = LEVELS.get(f_level, logging.CRITICAL) if level_num >= f_level: msg = self._build_message(message, f, level, msg_num) logging.getLogger(f).log(level_num, msg, **kwargs) def _build_message(self, message, destination, level, msg_num): pattern = self.__log_patterns[destination] tmp = pattern tmp = self._format_time(tmp) matches = re.findall("(%-?[0]?[0-9]?[AcmNnpPrtT])", tmp) for m in matches: if "A" in m: tmp = self._format_field(tmp, m, ovs.util.PROGRAM_NAME) elif "c" in m: tmp = self._format_field(tmp, m, self.name) elif "m" in m: tmp = self._format_field(tmp, m, message) elif "N" in m: tmp = self._format_field(tmp, m, str(msg_num)) elif "n" in m: tmp = re.sub(m, "\n", tmp) elif "p" in m: tmp = self._format_field(tmp, m, level.upper()) elif "P" in m: self._format_field(tmp, m, str(os.getpid())) elif "r" in m: now = datetime.datetime.utcnow() delta = now - self.__start_time ms = delta.microseconds / 1000 tmp = self._format_field(tmp, m, str(ms)) elif "t" in m: subprogram = threading.currentThread().getName() if subprogram == "MainThread": subprogram = "main" tmp = self._format_field(tmp, m, subprogram) elif "T" in m: subprogram = threading.currentThread().getName() if not subprogram == "MainThread": subprogram = "({})".format(subprogram) else: subprogram = "" tmp = self._format_field(tmp, m, subprogram) return tmp.strip() def _format_field(self, tmp, match, replace): formatting = re.compile("^%(0)?([1-9])?") matches = formatting.match(match) # Do we need to apply padding? if not matches.group(1) and replace != "": replace = replace.center(len(replace)+2) # Does the field have a minimum width if matches.group(2): min_width = int(matches.group(2)) if len(replace) < min_width: replace = replace.center(min_width) return re.sub(match, replace.replace('\\', r'\\'), tmp) def _format_time(self, tmp): date_regex = re.compile('(%(0?[1-9]?[dD])(\{(.*)\})?)') match = date_regex.search(tmp) if match is None: return tmp # UTC date or Local TZ? if match.group(2) == "d": now = datetime.datetime.now() elif match.group(2) == "D": now = datetime.datetime.utcnow() # Custom format or ISO format? if match.group(3): time = datetime.date.strftime(now, match.group(4)) try: i = len(re.search("#+", match.group(4)).group(0)) msec = '{0:0>{i}.{i}}'.format(str(now.microsecond / 1000), i=i) time = re.sub('#+', msec, time) except AttributeError: pass else: time = datetime.datetime.isoformat(now.replace(microsecond=0)) return self._format_field(tmp, match.group(1), time) def emer(self, message, **kwargs): self.__log("EMER", message, **kwargs) def err(self, message, **kwargs): self.__log("ERR", message, **kwargs) def warn(self, message, **kwargs): self.__log("WARN", message, **kwargs) def info(self, message, **kwargs): self.__log("INFO", message, **kwargs) def dbg(self, message, **kwargs): self.__log("DBG", message, **kwargs) def __is_enabled(self, level): level = LEVELS.get(level.lower(), logging.DEBUG) for f, f_level in Vlog.__mfl[self.name].iteritems(): f_level = LEVELS.get(f_level, logging.CRITICAL) if level >= f_level: return True return False def emer_is_enabled(self): return self.__is_enabled("EMER") def err_is_enabled(self): return self.__is_enabled("ERR") def warn_is_enabled(self): return self.__is_enabled("WARN") def info_is_enabled(self): return self.__is_enabled("INFO") def dbg_is_enabled(self): return self.__is_enabled("DBG") def exception(self, message): """Logs 'message' at ERR log level. Includes a backtrace when in exception context.""" self.err(message, exc_info=True) @staticmethod def init(log_file=None): """Intializes the Vlog module. Causes Vlog to write to 'log_file' if not None. Should be called after all Vlog objects have been created. No logging will occur until this function is called.""" if Vlog.__inited: return Vlog.__inited = True Vlog.__start_time = datetime.datetime.utcnow() logging.raiseExceptions = False Vlog.__log_file = log_file for f in DESTINATIONS: logger = logging.getLogger(f) logger.setLevel(logging.DEBUG) try: if f == "console": logger.addHandler(logging.StreamHandler(sys.stderr)) elif f == "syslog": Vlog.add_syslog_handler() elif f == "file" and Vlog.__log_file: Vlog.__file_handler = logging.FileHandler(Vlog.__log_file) logger.addHandler(Vlog.__file_handler) except (IOError, socket.error): logger.setLevel(logging.CRITICAL) ovs.unixctl.command_register("vlog/reopen", "", 0, 0, Vlog._unixctl_vlog_reopen, None) ovs.unixctl.command_register("vlog/set", "spec", 1, sys.maxint, Vlog._unixctl_vlog_set, None) ovs.unixctl.command_register("vlog/list", "", 0, 0, Vlog._unixctl_vlog_list, None) @staticmethod def set_level(module, destination, level): """ Sets the log level of the 'module'-'destination' tuple to 'level'. All three arguments are strings which are interpreted the same as arguments to the --verbose flag. Should be called after all Vlog objects have already been created.""" module = module.lower() destination = destination.lower() level = level.lower() if destination != "any" and destination not in DESTINATIONS: return if module != "any" and module not in Vlog.__mfl: return if level not in LEVELS: return if module == "any": modules = Vlog.__mfl.keys() else: modules = [module] if destination == "any": destinations = DESTINATIONS.keys() else: destinations = [destination] for m in modules: for f in destinations: Vlog.__mfl[m][f] = level @staticmethod def set_pattern(destination, pattern): """ Sets the log pattern of the 'destination' to 'pattern' """ destination = destination.lower() Vlog.__log_patterns[destination] = pattern @staticmethod def add_syslog_handler(facility=None): global syslog_facility, syslog_handler # If handler is already added and there is no change in 'facility', # there is nothing to do. if (not facility or facility == syslog_facility) and syslog_handler: return logger = logging.getLogger('syslog') # If there is no infrastructure to support python syslog, increase # the logging severity level to avoid repeated errors. if not os.path.exists("/dev/log"): logger.setLevel(logging.CRITICAL) return if syslog_handler: logger.removeHandler(syslog_handler) if facility: syslog_facility = facility syslog_handler = logging.handlers.SysLogHandler(address="/dev/log", facility=syslog_facility) logger.addHandler(syslog_handler) return @staticmethod def set_levels_from_string(s): module = None level = None destination = None words = re.split('[ :]', s) if words[0] == "pattern": try: if words[1] in DESTINATIONS and words[2]: segments = [words[i] for i in range(2, len(words))] pattern = "".join(segments) Vlog.set_pattern(words[1], pattern) return else: return "Destination %s does not exist" % words[1] except IndexError: return "Please supply a valid pattern and destination" elif words[0] == "FACILITY": if words[1] in FACILITIES: Vlog.add_syslog_handler(words[1]) return else: return "Facility %s is invalid" % words[1] for word in [w.lower() for w in words]: if word == "any": pass elif word in DESTINATIONS: if destination: return "cannot specify multiple destinations" destination = word elif word in LEVELS: if level: return "cannot specify multiple levels" level = word elif word in Vlog.__mfl: if module: return "cannot specify multiple modules" module = word else: return "no destination, level, or module \"%s\"" % word Vlog.set_level(module or "any", destination or "any", level or "any") @staticmethod def get_levels(): lines = [" console syslog file\n", " ------- ------ ------\n"] lines.extend(sorted(["%-16s %4s %4s %4s\n" % (m, Vlog.__mfl[m]["console"], Vlog.__mfl[m]["syslog"], Vlog.__mfl[m]["file"]) for m in Vlog.__mfl])) return ''.join(lines) @staticmethod def reopen_log_file(): """Closes and then attempts to re-open the current log file. (This is useful just after log rotation, to ensure that the new log file starts being used.)""" if Vlog.__log_file: logger = logging.getLogger("file") logger.removeHandler(Vlog.__file_handler) Vlog.__file_handler = logging.FileHandler(Vlog.__log_file) logger.addHandler(Vlog.__file_handler) @staticmethod def _unixctl_vlog_reopen(conn, unused_argv, unused_aux): if Vlog.__log_file: Vlog.reopen_log_file() conn.reply(None) else: conn.reply("Logging to file not configured") @staticmethod def _unixctl_vlog_set(conn, argv, unused_aux): for arg in argv: msg = Vlog.set_levels_from_string(arg) if msg: conn.reply(msg) return conn.reply(None) @staticmethod def _unixctl_vlog_list(conn, unused_argv, unused_aux): conn.reply(Vlog.get_levels()) def add_args(parser): """Adds vlog related options to 'parser', an ArgumentParser object. The resulting arguments parsed by 'parser' should be passed to handle_args.""" group = parser.add_argument_group(title="Logging Options") group.add_argument("--log-file", nargs="?", const="default", help="Enables logging to a file. Default log file" " is used if LOG_FILE is omitted.") group.add_argument("-v", "--verbose", nargs="*", help="Sets logging levels, see ovs-vswitchd(8)." " Defaults to dbg.") def handle_args(args): """ Handles command line arguments ('args') parsed by an ArgumentParser. The ArgumentParser should have been primed by add_args(). Also takes care of initializing the Vlog module.""" log_file = args.log_file if log_file == "default": log_file = "%s/%s.log" % (ovs.dirs.LOGDIR, ovs.util.PROGRAM_NAME) if args.verbose is None: args.verbose = [] elif args.verbose == []: args.verbose = ["any:any:dbg"] for verbose in args.verbose: msg = Vlog.set_levels_from_string(verbose) if msg: ovs.util.ovs_fatal(0, "processing \"%s\": %s" % (verbose, msg)) Vlog.init(log_file) openvswitch-2.5.9/python/ovs/PaxHeaders.82075/version.py0000644000000000000000000000013213534540117020034 xustar0030 mtime=1567801423.449843606 30 atime=1567801423.449843606 30 ctime=1567801424.269849651 openvswitch-2.5.9/python/ovs/version.py0000644000175000017500000000013513534540117021521 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- VERSION = "2.5.9" openvswitch-2.5.9/python/PaxHeaders.82075/build0000644000000000000000000000013213534540120016202 xustar0030 mtime=1567801424.237849416 30 atime=1567801425.625859648 30 ctime=1567801424.237849416 openvswitch-2.5.9/python/build/0000755000175000017500000000000013534540120017745 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/python/build/PaxHeaders.82075/nroff.py0000644000000000000000000000013213534540071017750 xustar0030 mtime=1567801401.829684313 30 atime=1567801402.121686458 30 ctime=1567801424.237849416 openvswitch-2.5.9/python/build/nroff.py0000644000175000017500000002551113534540071021442 0ustar00jpettitjpettit00000000000000# Copyright (c) 2010, 2011, 2012, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re from ovs.db import error def text_to_nroff(s, font=r'\fR'): def escape(match): c = match.group(0) # In Roman type, let -- in XML be \- in nroff. That gives us a way to # write minus signs, which is important in some places in manpages. # # Bold in nroff usually represents literal text, where there's no # distinction between hyphens and minus sign. The convention in nroff # appears to be to use a minus sign in such cases, so we follow that # convention. # # Finally, we always output - as a minus sign when it is followed by a # digit. if c.startswith('-'): if c == '--' and font == r'\fR': return r'\-' if c != '-' or font in (r'\fB', r'\fL'): return c.replace('-', r'\-') else: return '-' if c == '\\': return r'\e' elif c == '"': return r'\(dq' elif c == "'": return r'\(cq' elif c == ".": # groff(7) says that . can be escaped by \. but in practice groff # still gives an error with \. at the beginning of a line. return r'\[char46]' else: raise error.Error("bad escape") # Escape - \ " ' . as needed by nroff. s = re.sub('(-[0-9]|--|[-"\'\\\\.])', escape, s) return s def escape_nroff_literal(s, font=r'\fB'): return font + r'%s\fR' % text_to_nroff(s, font) def inline_xml_to_nroff(node, font, to_upper=False, newline='\n'): if node.nodeType == node.TEXT_NODE: if to_upper: s = text_to_nroff(node.data.upper(), font) else: s = text_to_nroff(node.data, font) return s.replace('\n', newline) elif node.nodeType == node.ELEMENT_NODE: if node.tagName in ['code', 'em', 'option', 'env', 'b']: s = r'\fB' for child in node.childNodes: s += inline_xml_to_nroff(child, r'\fB', to_upper, newline) return s + font elif node.tagName == 'ref': s = r'\fB' if node.hasAttribute('column'): s += node.attributes['column'].nodeValue if node.hasAttribute('key'): s += ':' + node.attributes['key'].nodeValue elif node.hasAttribute('table'): s += node.attributes['table'].nodeValue elif node.hasAttribute('group'): s += node.attributes['group'].nodeValue elif node.hasAttribute('db'): s += node.attributes['db'].nodeValue else: raise error.Error("'ref' lacks required attributes: %s" % node.attributes.keys()) return s + font elif node.tagName in ['var', 'dfn', 'i']: s = r'\fI' for child in node.childNodes: s += inline_xml_to_nroff(child, r'\fI', to_upper, newline) return s + font else: raise error.Error("element <%s> unknown or invalid here" % node.tagName) elif node.nodeType == node.COMMENT_NODE: return '' else: raise error.Error("unknown node %s in inline xml" % node) def pre_to_nroff(nodes, para, font): # This puts 'font' at the beginning of each line so that leading and # trailing whitespace stripping later doesn't removed leading spaces # from preformatted text. s = para + '\n.nf\n' + font for node in nodes: s += inline_xml_to_nroff(node, font, False, '\n.br\n' + font) s += '\n.fi\n' return s def diagram_header_to_nroff(header_node): header_fields = [] i = 0 for node in header_node.childNodes: if node.nodeType == node.ELEMENT_NODE and node.tagName == 'bits': name = node.attributes['name'].nodeValue width = node.attributes['width'].nodeValue above = node.getAttribute('above') below = node.getAttribute('below') fill = node.getAttribute('fill') header_fields += [{"name": name, "tag": "B%d" % i, "width": width, "above": above, "below": below, "fill": fill}] i += 1 elif node.nodeType == node.COMMENT_NODE: pass elif node.nodeType == node.TEXT_NODE and node.data.isspace(): pass else: fatal("unknown node %s in diagram
element" % node) pic_s = "" for f in header_fields: pic_s += " %s: box \"%s\" width %s" % (f['tag'], f['name'], f['width']) if f['fill'] == 'yes': pic_s += " fill" pic_s += '\n' for f in header_fields: pic_s += " \"%s\" at %s.n above\n" % (f['above'], f['tag']) pic_s += " \"%s\" at %s.s below\n" % (f['below'], f['tag']) name = header_node.getAttribute('name') if name == "": visible = " invis" else: visible = "" pic_s += "line <->%s \"%s\" above " % (visible, name) pic_s += "from %s.nw + (0,textht) " % header_fields[0]['tag'] pic_s += "to %s.ne + (0,textht)\n" % header_fields[-1]['tag'] text_s = "" for f in header_fields: text_s += """.IP \\(bu %s bits""" % (f['above']) if f['name']: text_s += ": %s" % f['name'] if f['below']: text_s += " (%s)" % f['below'] text_s += "\n" return pic_s, text_s def diagram_to_nroff(nodes, para): pic_s = '' text_s = '' move = False for node in nodes: if node.nodeType == node.ELEMENT_NODE and node.tagName == 'header': if move: pic_s += "move .1\n" text_s += ".sp\n" pic_header, text_header = diagram_header_to_nroff(node) pic_s += "[\n" + pic_header + "]\n" text_s += text_header move = True elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'nospace': move = False elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'dots': pic_s += "move .1\n" pic_s += '". . ." ljust\n' text_s += ".sp\n" elif node.nodeType == node.COMMENT_NODE: pass elif node.nodeType == node.TEXT_NODE and node.data.isspace(): pass else: fatal("unknown node %s in diagram
element" % node) return para + """ .\\" check if in troff mode (TTY) .if t \{ .PS boxht = .2 textht = 1/6 fillval = .2 """ + pic_s + """\ .PE \\} .\\" check if in nroff mode: .if n \{ .RS """ + text_s + """\ .RE \\}""" def block_xml_to_nroff(nodes, para='.PP'): s = '' for node in nodes: if node.nodeType == node.TEXT_NODE: s += text_to_nroff(node.data) s = s.lstrip() elif node.nodeType == node.ELEMENT_NODE: if node.tagName in ['ul', 'ol']: if s != "": s += "\n" s += ".RS\n" i = 0 for li_node in node.childNodes: if (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'li'): i += 1 if node.tagName == 'ul': s += ".IP \\(bu\n" else: s += ".IP %d. .25in\n" % i s += block_xml_to_nroff(li_node.childNodes, ".IP") elif li_node.nodeType == node.COMMENT_NODE: pass elif (li_node.nodeType != node.TEXT_NODE or not li_node.data.isspace()): raise error.Error("<%s> element may only have
  • children" % node.tagName) s += ".RE\n" elif node.tagName == 'dl': if s != "": s += "\n" s += ".RS\n" prev = "dd" for li_node in node.childNodes: if (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'dt'): if prev == 'dd': s += '.TP\n' else: s += '.TQ .5in\n' prev = 'dt' elif (li_node.nodeType == node.ELEMENT_NODE and li_node.tagName == 'dd'): if prev == 'dd': s += '.IP\n' prev = 'dd' elif li_node.nodeType == node.COMMENT_NODE: continue elif (li_node.nodeType != node.TEXT_NODE or not li_node.data.isspace()): raise error.Error("
    element may only have
    and
    children") s += block_xml_to_nroff(li_node.childNodes, ".IP") s += ".RE\n" elif node.tagName == 'p': if s != "": if not s.endswith("\n"): s += "\n" s += para + "\n" s += block_xml_to_nroff(node.childNodes, para) elif node.tagName in ('h1', 'h2', 'h3'): if s != "": if not s.endswith("\n"): s += "\n" nroffTag = {'h1': 'SH', 'h2': 'SS', 'h3': 'ST'}[node.tagName] s += '.%s "' % nroffTag for child_node in node.childNodes: s += inline_xml_to_nroff(child_node, r'\fR', to_upper=(nroffTag == 'SH')) s += '"\n' elif node.tagName == 'pre': fixed = node.getAttribute('fixed') if fixed == 'yes': font = r'\fL' else: font = r'\fB' s += pre_to_nroff(node.childNodes, para, font) elif node.tagName == 'diagram': s += diagram_to_nroff(node.childNodes, para) else: s += inline_xml_to_nroff(node, r'\fR') elif node.nodeType == node.COMMENT_NODE: pass else: raise error.Error("unknown node %s in block xml" % node) if s != "" and not s.endswith('\n'): s += '\n' return s openvswitch-2.5.9/python/build/PaxHeaders.82075/__init__.py0000644000000000000000000000013213534540071020375 xustar0030 mtime=1567801401.825684283 30 atime=1567801401.825684283 30 ctime=1567801424.237849416 openvswitch-2.5.9/python/build/__init__.py0000644000175000017500000000000013534540071022051 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/PaxHeaders.82075/configure.ac0000644000000000000000000000013213534540071016132 xustar0030 mtime=1567801401.197679672 30 atime=1567801401.989685488 30 ctime=1567801423.649845081 openvswitch-2.5.9/configure.ac0000644000175000017500000001420013534540071017615 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. AC_PREREQ(2.63) AC_INIT(openvswitch, 2.5.9, bugs@openvswitch.org) AC_CONFIG_SRCDIR([datapath/datapath.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_TESTDIR([tests]) AM_INIT_AUTOMAKE([tar-pax]) AC_PROG_CC_C99 AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_MKDIR_P AC_PROG_FGREP AC_PROG_EGREP AC_ARG_VAR([PERL], [path to Perl interpreter]) AC_PATH_PROG([PERL], perl, no) if test "$PERL" = no; then AC_MSG_ERROR([Perl interpreter not found in $PATH or $PERL.]) fi AM_MISSING_PROG([AUTOM4TE], [autom4te]) AC_USE_SYSTEM_EXTENSIONS AC_C_BIGENDIAN AC_SYS_LARGEFILE LT_INIT([disable-shared]) m4_pattern_forbid([LT_INIT]) dnl Make autoconf fail if libtool is missing. # The following explanation may help to understand the use of the # version number fields: current, revision, and age. # # Consider that there are three possible kinds of reactions from # users of your library to changes in a shared library: # # 1. Programs using the previous version may use the new version as drop-in # replacement, and programs using the new version can also work with the # previous one. In other words, no recompiling nor relinking is needed. # In short, there are no changes to any symbols, no symbols removed, # and no symbols added. In this case, bump revision only, don't touch # current nor age. # # 2. Programs using the previous version may use the new version as drop-in # replacement, but programs using the new version may use APIs not # present in the previous one. In other words, new symbols have been # added and a program linking against the new version may fail with # "unresolved symbols." If linking against the old version at runtime: # set revision to 0, bump current and age. # # 3. Programs may need to be changed, recompiled, relinked in order to use # the new version. This is the case when symbols have been modified or # deleted. Bump current, set revision and age to 0. m4_define([libopenvswitch_lt_current], [1]) m4_define([libopenvswitch_lt_revision], [0]) m4_define([libopenvswitch_lt_age], [0]) LT_CURRENT=libopenvswitch_lt_current AC_SUBST([LT_CURRENT]) LT_REVISION=libopenvswitch_lt_revision AC_SUBST([LT_REVISION]) LT_AGE=libopenvswitch_lt_age AC_SUBST([LT_AGE]) AC_SEARCH_LIBS([pow], [m]) AC_SEARCH_LIBS([clock_gettime], [rt]) AC_SEARCH_LIBS([timer_create], [rt]) AC_SEARCH_LIBS([pthread_create], [pthread]) AC_FUNC_STRERROR_R OVS_CHECK_ESX OVS_CHECK_WIN64 OVS_CHECK_WIN32 OVS_CHECK_VISUAL_STUDIO_DDK OVS_CHECK_COVERAGE OVS_CHECK_NDEBUG OVS_CHECK_NETLINK OVS_CHECK_OPENSSL OVS_CHECK_LIBCAPNG OVS_CHECK_LOGDIR OVS_CHECK_PYTHON OVS_CHECK_DOT OVS_CHECK_IF_PACKET OVS_CHECK_IF_DL OVS_CHECK_STRTOK_R AC_CHECK_DECLS([sys_siglist], [], [], [[#include ]]) AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec], [], [], [[#include ]]) AC_CHECK_MEMBERS([struct ifreq.ifr_flagshigh], [], [], [[#include ]]) AC_CHECK_FUNCS([mlockall strnlen getloadavg statvfs getmntent_r]) AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h]) AC_CHECK_HEADERS([net/if_mib.h], [], [], [[#include #include ]]) OVS_CHECK_PKIDIR OVS_CHECK_RUNDIR OVS_CHECK_DBDIR OVS_CHECK_BACKTRACE OVS_CHECK_PERF_EVENT OVS_CHECK_VALGRIND OVS_CHECK_SOCKET_LIBS OVS_CHECK_XENSERVER_VERSION OVS_CHECK_GROFF OVS_CHECK_GNU_MAKE OVS_CHECK_TLS OVS_CHECK_ATOMIC_LIBS OVS_CHECK_GCC4_ATOMICS OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(1) OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(2) OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(4) OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(8) OVS_CHECK_POSIX_AIO OVS_CHECK_PTHREAD_SET_NAME OVS_CHECK_LINUX_HOST AX_FUNC_POSIX_MEMALIGN OVS_CHECK_INCLUDE_NEXT([stdio.h string.h]) AC_CONFIG_FILES([ lib/stdio.h lib/string.h ovsdb/libovsdb.sym ofproto/libofproto.sym lib/libsflow.sym lib/libopenvswitch.sym ovn/lib/libovn.sym vtep/libvtep.sym]) OVS_ENABLE_OPTION([-Wall]) OVS_ENABLE_OPTION([-Wextra]) OVS_ENABLE_OPTION([-Wno-sign-compare]) OVS_ENABLE_OPTION([-Wpointer-arith]) OVS_ENABLE_OPTION([-Wformat-security]) OVS_ENABLE_OPTION([-Wswitch-enum]) OVS_ENABLE_OPTION([-Wunused-parameter]) OVS_ENABLE_OPTION([-Wbad-function-cast]) OVS_ENABLE_OPTION([-Wcast-align]) OVS_ENABLE_OPTION([-Wstrict-prototypes]) OVS_ENABLE_OPTION([-Wold-style-definition]) OVS_ENABLE_OPTION([-Wmissing-prototypes]) OVS_ENABLE_OPTION([-Wmissing-field-initializers]) OVS_ENABLE_OPTION([-Wthread-safety]) OVS_ENABLE_OPTION([-fno-strict-aliasing]) OVS_ENABLE_OPTION([-Qunused-arguments]) OVS_CONDITIONAL_CC_OPTION([-Wno-unused], [HAVE_WNO_UNUSED]) OVS_CONDITIONAL_CC_OPTION([-Wno-unused-parameter], [HAVE_WNO_UNUSED_PARAMETER]) OVS_ENABLE_WERROR OVS_ENABLE_SPARSE AC_ARG_VAR(KARCH, [Kernel Architecture String]) AC_SUBST(KARCH) OVS_CHECK_LINUX OVS_CHECK_DPDK OVS_CHECK_PRAGMA_MESSAGE AC_SUBST([OVS_CFLAGS]) AC_SUBST([OVS_LDFLAGS]) AC_CONFIG_FILES(Makefile) AC_CONFIG_FILES(datapath/Makefile) AC_CONFIG_FILES(datapath/linux/Kbuild) AC_CONFIG_FILES(datapath/linux/Makefile) AC_CONFIG_FILES(datapath/linux/Makefile.main) AC_CONFIG_FILES(tests/atlocal) AC_CONFIG_FILES(lib/libopenvswitch.pc) AC_CONFIG_FILES(lib/libsflow.pc) AC_CONFIG_FILES(ofproto/libofproto.pc) AC_CONFIG_FILES(ovsdb/libovsdb.pc) AC_CONFIG_FILES(include/openvswitch/version.h) dnl This makes sure that include/openflow gets created in the build directory. AC_CONFIG_COMMANDS([include/openflow/openflow.h.stamp]) AC_CONFIG_COMMANDS([utilities/bugtool/dummy], [:]) AC_CONFIG_COMMANDS([ovn/dummy], [:]) AC_CONFIG_COMMANDS([ovn/utilities/dummy], [:]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_OUTPUT openvswitch-2.5.9/PaxHeaders.82075/NOTICE0000644000000000000000000000013213534540071014550 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.041685871 30 ctime=1567801423.705845493 openvswitch-2.5.9/NOTICE0000644000175000017500000000242313534540071016237 0ustar00jpettitjpettit00000000000000This file is included in compliance with the Apache 2.0 license, available at http://www.apache.org/licenses/LICENSE-2.0.html Open vSwitch Copyright (c) 2007, 2008, 2009, 2010, 2011, 2013 Nicira, Inc. Open vSwitch BSD port Copyright (c) 2011 Gaetano Catalli Apache Portable Runtime Copyright 2008 The Apache Software Foundation. This product includes software developed by The Apache Software Foundation (http://www.apache.org/). Portions of this software were developed at the National Center for Supercomputing Applications (NCSA) at the University of Illinois at Urbana-Champaign. lib/ovs.tmac includes troff macros written by Eric S. Raymond and Werner Lemberg. m4/include_next.m4 and m4/absolute-header.m4 Copyright (C) 2006-2013 Free Software Foundation, Inc. Rapid Spanning Tree Protocol (RSTP) implementation Copyright (c) 2011-2014 M3S, Srl - Italy LLDP implementation Copyright (c) 2008, 2012 Vincent Bernat LLDP includes code used from the Net::CDP project based on the ISC license Copyright (c) 2014 Michael Chapman LLDP includes code used from the ladvd project based on the ISC license Copyright (c) 2008, 2009, 2010 Sten Spans Auto Attach implementation Copyright (c) 2014, 2015 WindRiver, Inc Copyright (c) 2014, 2015 Avaya, Inc openvswitch-2.5.9/PaxHeaders.82075/INSTALL.md0000644000000000000000000000013213534540071015274 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.665845199 openvswitch-2.5.9/INSTALL.md0000644000175000017500000006712213534540071016772 0ustar00jpettitjpettit00000000000000How to Install Open vSwitch on Linux, FreeBSD and NetBSD ======================================================== This document describes how to build and install Open vSwitch on a generic Linux, FreeBSD, or NetBSD host. For specifics around installation on a specific platform, please see one of these files: - [INSTALL.Debian.md] - [INSTALL.Fedora.md] - [INSTALL.RHEL.md] - [INSTALL.XenServer.md] - [INSTALL.NetBSD.md] - [INSTALL.Windows.md] - [INSTALL.DPDK.md] Build Requirements ------------------ To compile the userspace programs in the Open vSwitch distribution, you will need the following software: - GNU make. - A C compiler, such as: * GCC 4.x. * Clang. Clang 3.4 and later provide useful static semantic analysis and thread-safety checks. For Ubuntu, there are nightly built packages available on clang's website. * MSVC 2013. See [INSTALL.Windows] for additional Windows build instructions. While OVS may be compatible with other compilers, optimal support for atomic operations may be missing, making OVS very slow (see lib/ovs-atomic.h). - libssl, from OpenSSL, is optional but recommended if you plan to connect the Open vSwitch to an OpenFlow controller. libssl is required to establish confidentiality and authenticity in the connections from an Open vSwitch to an OpenFlow controller. If libssl is installed, then Open vSwitch will automatically build with support for it. - libcap-ng, written by Steve Grubb, is optional but recommended. It is required to run OVS daemons as a non-root user with dropped root privileges. If libcap-ng is installed, then Open vSwitch will automatically build with support for it. - Python 2.7. On Linux, you may choose to compile the kernel module that comes with the Open vSwitch distribution or to use the kernel module built into the Linux kernel (version 3.3 or later). See the [FAQ.md] question "What features are not available in the Open vSwitch kernel datapath that ships as part of the upstream Linux kernel?" for more information on this trade-off. You may also use the userspace-only implementation, at some cost in features and performance (see [INSTALL.userspace.md] for details). To compile the kernel module on Linux, you must also install the following: - A supported Linux kernel version. Please refer to [README.md] for a list of supported versions. The Open vSwitch datapath requires bridging support (CONFIG_BRIDGE) to be built as a kernel module. (This is common in kernels provided by Linux distributions.) The bridge module must not be loaded or in use. If the bridge module is running (check with "lsmod | grep bridge"), you must remove it ("rmmod bridge") before starting the datapath. For optional support of ingress policing, you must enable kernel configuration options NET_CLS_BASIC, NET_SCH_INGRESS, and NET_ACT_POLICE, either built-in or as modules. (NET_CLS_POLICE is obsolete and not needed.) To use GRE tunneling on Linux 2.6.37 or newer, kernel support for GRE demultiplexing (CONFIG_NET_IPGRE_DEMUX) must be compiled in or available as a module. Also, on kernels before 3.11, the ip_gre module, for GRE tunnels over IP (NET_IPGRE), must not be loaded or compiled in. To configure HTB or HFSC quality of service with Open vSwitch, you must enable the respective configuration options. To use Open vSwitch support for TAP devices, you must enable CONFIG_TUN. - To build a kernel module, you need the same version of GCC that was used to build that kernel. - A kernel build directory corresponding to the Linux kernel image the module is to run on. Under Debian and Ubuntu, for example, each linux-image package containing a kernel binary has a corresponding linux-headers package with the required build infrastructure. If you are working from a Git tree or snapshot (instead of from a distribution tarball), or if you modify the Open vSwitch build system or the database schema, you will also need the following software: - Autoconf version 2.63 or later. - Automake version 1.10 or later. - libtool version 2.4 or later. (Older versions might work too.) To run the unit tests, you also need: - Perl. Version 5.10.1 is known to work. Earlier versions should also work. The ovs-vswitchd.conf.db(5) manpage will include an E-R diagram, in formats other than plain text, only if you have the following: - "dot" from graphviz (http://www.graphviz.org/). - Perl. Version 5.10.1 is known to work. Earlier versions should also work. If you are going to extensively modify Open vSwitch, please consider installing the following to obtain better warnings: - "sparse" version 0.4.4 or later (http://www.kernel.org/pub/software/devel/sparse/dist/). - GNU make. - clang, version 3.4 or later Also, you may find the ovs-dev script found in utilities/ovs-dev.py useful. Installation Requirements ------------------------- The machine on which Open vSwitch is to be installed must have the following software: - libc compatible with the libc used for build. - libssl compatible with the libssl used for build, if OpenSSL was used for the build. - On Linux, the same kernel version configured as part of the build. - For optional support of ingress policing on Linux, the "tc" program from iproute2 (part of all major distributions and available at http://www.linux-foundation.org/en/Net:Iproute2). On Linux you should ensure that /dev/urandom exists. To support TAP devices, you must also ensure that /dev/net/tun exists. Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD ================================================================= Once you have installed all the prerequisites listed above in the Base Prerequisites section, follow the procedures below to bootstrap, to configure and to build the code. Bootstrapping the Sources ------------------------- This step is not needed if you have downloaded a released tarball. If you pulled the sources directly from an Open vSwitch Git tree or got a Git tree snapshot, then run boot.sh in the top source directory to build the "configure" script. `% ./boot.sh` Configuring the Sources ----------------------- Configure the package by running the configure script. You can usually invoke configure without any arguments. For example: `% ./configure` By default all files are installed under /usr/local. If you want to install into, e.g., /usr and /var instead of /usr/local and /usr/local/var, add options as shown here: `% ./configure --prefix=/usr --localstatedir=/var` By default, static libraries are built and linked against. If you want to use shared libraries instead: % ./configure --enable-shared To use a specific C compiler for compiling Open vSwitch user programs, also specify it on the configure command line, like so: `% ./configure CC=gcc-4.2` To use 'clang' compiler: `% ./configure CC=clang` To supply special flags to the C compiler, specify them as CFLAGS on the configure command line. If you want the default CFLAGS, which include "-g" to build debug symbols and "-O2" to enable optimizations, you must include them yourself. For example, to build with the default CFLAGS plus "-mssse3", you might run configure as follows: `% ./configure CFLAGS="-g -O2 -mssse3"` Note that these CFLAGS are not applied when building the Linux kernel module. Custom CFLAGS for the kernel module are supplied using the EXTRA_CFLAGS variable when running make. So, for example: `% make EXTRA_CFLAGS="-Wno-error=date-time" To build the Linux kernel module, so that you can run the kernel-based switch, pass the location of the kernel build directory on --with-linux. For example, to build for a running instance of Linux: `% ./configure --with-linux=/lib/modules/`uname -r`/build` If --with-linux requests building for an unsupported version of Linux, then "configure" will fail with an error message. Please refer to the [FAQ.md] for advice in that case. If you wish to build the kernel module for an architecture other than the architecture of the machine used for the build, you may specify the kernel architecture string using the KARCH variable when invoking the configure script. For example, to build for MIPS with Linux: `% ./configure --with-linux=/path/to/linux KARCH=mips` If you plan to do much Open vSwitch development, you might want to add --enable-Werror, which adds the -Werror option to the compiler command line, turning warnings into errors. That makes it impossible to miss warnings generated by the build. To build with gcov code coverage support, add --enable-coverage, e.g.: `% ./configure --enable-coverage` The configure script accepts a number of other options and honors additional environment variables. For a full list, invoke configure with the --help option. You can also run configure from a separate build directory. This is helpful if you want to build Open vSwitch in more than one way from a single source directory, e.g. to try out both GCC and Clang builds, or to build kernel modules for more than one Linux version. Here is an example: `% mkdir _gcc && (cd _gcc && ../configure CC=gcc)` `% mkdir _clang && (cd _clang && ../configure CC=clang)` Building the Sources -------------------- 1. Run GNU make in the build directory, e.g.: `% make` or if GNU make is installed as "gmake": `% gmake` If you used a separate build directory, run make or gmake from that directory, e.g.: `% make -C _gcc` `% make -C _clang` For improved warnings if you installed "sparse" (see "Prerequisites"), add C=1 to the command line. Some versions of Clang and ccache are not completely compatible. If you see unusual warnings when you use both together, consider disabling ccache for use with Clang. 2. Consider running the testsuite. Refer to "Running the Testsuite" below, for instructions. 3. Become root by running "su" or another program. 4. Run "make install" to install the executables and manpages into the running system, by default under /usr/local. 5. If you built kernel modules, you may install and load them, e.g.: `% make modules_install` `% /sbin/modprobe openvswitch` To verify that the modules have been loaded, run "/sbin/lsmod" and check that openvswitch is listed. If the `modprobe` operation fails, look at the last few kernel log messages (e.g. with `dmesg | tail`): - The message "openvswitch: exports duplicate symbol br_should_route_hook (owned by bridge)" means that the bridge module is loaded. Run `/sbin/rmmod bridge` to remove it. If `/sbin/rmmod bridge` fails with "ERROR: Module bridge does not exist in /proc/modules", then the bridge is compiled into the kernel, rather than as a module. Open vSwitch does not support this configuration (see "Build Requirements", above). - The message "openvswitch: exports duplicate symbol dp_ioctl_hook (owned by ofdatapath)" means that the ofdatapath module from the OpenFlow reference implementation is loaded. Run `/sbin/rmmod ofdatapath` to remove it. (You might have to delete any existing datapaths beforehand, using the "dpctl" program included with the OpenFlow reference implementation. "ovs-dpctl" will not work.) - Otherwise, the most likely problem is that Open vSwitch was built for a kernel different from the one into which you are trying to load it. Run `modinfo` on openvswitch.ko and on a module built for the running kernel, e.g.: ``` % /sbin/modinfo openvswitch.ko % /sbin/modinfo /lib/modules/`uname -r`/kernel/net/bridge/bridge.ko ``` Compare the "vermagic" lines output by the two commands. If they differ, then Open vSwitch was built for the wrong kernel. - If you decide to report a bug or ask a question related to module loading, please include the output from the `dmesg` and `modinfo` commands mentioned above. There is an optional module parameter to openvswitch.ko called vlan_tso that enables TCP segmentation offload over VLANs on NICs that support it. Many drivers do not expose support for TSO on VLANs in a way that Open vSwitch can use but there is no way to detect whether this is the case. If you know that your particular driver can handle it (for example by testing sending large TCP packets over VLANs) then passing in a value of 1 may improve performance. Modules built for Linux kernels 2.6.37 and later, as well as specially patched versions of earlier kernels, do not need this and do not have this parameter. If you do not understand what this means or do not know if your driver will work, do not set this. 6. Initialize the configuration database using ovsdb-tool, e.g.: `% mkdir -p /usr/local/etc/openvswitch` `% ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema` Startup ======= Before starting ovs-vswitchd itself, you need to start its configuration database, ovsdb-server. Each machine on which Open vSwitch is installed should run its own copy of ovsdb-server. Configure it to use the database you created during installation (as explained above), to listen on a Unix domain socket, to connect to any managers specified in the database itself, and to use the SSL configuration in the database: % ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \ --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ --private-key=db:Open_vSwitch,SSL,private_key \ --certificate=db:Open_vSwitch,SSL,certificate \ --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ --pidfile --detach (If you built Open vSwitch without SSL support, then omit --private-key, --certificate, and --bootstrap-ca-cert.) Then initialize the database using ovs-vsctl. This is only necessary the first time after you create the database with ovsdb-tool (but running it at any time is harmless): % ovs-vsctl --no-wait init Then start the main Open vSwitch daemon, telling it to connect to the same Unix domain socket: % ovs-vswitchd --pidfile --detach Now you may use ovs-vsctl to set up bridges and other Open vSwitch features. For example, to create a bridge named br0 and add ports eth0 and vif1.0 to it: % ovs-vsctl add-br br0 % ovs-vsctl add-port br0 eth0 % ovs-vsctl add-port br0 vif1.0 Please refer to ovs-vsctl(8) for more details. Upgrading ========= When you upgrade Open vSwitch from one version to another, you should also upgrade the database schema: 1. Stop the Open vSwitch daemons, e.g.: ``` % kill `cd /usr/local/var/run/openvswitch && cat ovsdb-server.pid ovs-vswitchd.pid` ``` 2. Install the new Open vSwitch release. 3. Upgrade the database, in one of the following two ways: - If there is no important data in your database, then you may delete the database file and recreate it with ovsdb-tool, following the instructions under "Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD". - If you want to preserve the contents of your database, back it up first, then use "ovsdb-tool convert" to upgrade it, e.g.: `% ovsdb-tool convert /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema` 4. Start the Open vSwitch daemons as described under "Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD" above. Hot Upgrading ============= Upgrading Open vSwitch from one version to the next version with minimum disruption of traffic going through the system that is using that Open vSwitch needs some considerations: 1. If the upgrade only involves upgrading the userspace utilities and daemons of Open vSwitch, make sure that the new userspace version is compatible with the previously loaded kernel module. 2. An upgrade of userspace daemons means that they have to be restarted. Restarting the daemons means that the OpenFlow flows in the ovs-vswitchd daemon will be lost. One way to restore the flows is to let the controller re-populate it. Another way is to save the previous flows using a utility like ovs-ofctl and then re-add them after the restart. Restoring the old flows is accurate only if the new Open vSwitch interfaces retain the old 'ofport' values. 3. When the new userspace daemons get restarted, they automatically flush the old flows setup in the kernel. This can be expensive if there are hundreds of new flows that are entering the kernel but userspace daemons are busy setting up new userspace flows from either the controller or an utility like ovs-ofctl. Open vSwitch database provides an option to solve this problem through the other_config:flow-restore-wait column of the Open_vSwitch table. Refer to the ovs-vswitchd.conf.db(5) manpage for details. 4. If the upgrade also involves upgrading the kernel module, the old kernel module needs to be unloaded and the new kernel module should be loaded. This means that the kernel network devices belonging to Open vSwitch is recreated and the kernel flows are lost. The downtime of the traffic can be reduced if the userspace daemons are restarted immediately and the userspace flows are restored as soon as possible. The ovs-ctl utility's "restart" function only restarts the userspace daemons, makes sure that the 'ofport' values remain consistent across restarts, restores userspace flows using the ovs-ofctl utility and also uses the other_config:flow-restore-wait column to keep the traffic downtime to the minimum. The ovs-ctl utility's "force-reload-kmod" function does all of the above, but also replaces the old kernel module with the new one. Open vSwitch startup scripts for Debian, XenServer and RHEL use ovs-ctl's functions and it is recommended that these functions be used for other software platforms too. Testsuites ========== This section describe Open vSwitch's built-in support for various test suites. You must bootstrap, configure and build Open vSwitch (steps are in "Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD" above) before you run the tests described here. You do not need to install Open vSwitch or to build or load the kernel module to run these test suites. You do not need supervisor privilege to run these test suites. Self-Tests ---------- Open vSwitch includes a suite of self-tests. Before you submit patches upstream, we advise that you run the tests and ensure that they pass. If you add new features to Open vSwitch, then adding tests for those features will ensure your features don't break as developers modify other areas of Open vSwitch. Refer to "Testsuites" above for prerequisites. To run all the unit tests in Open vSwitch, one at a time: `make check` This takes under 5 minutes on a modern desktop system. To run all the unit tests in Open vSwitch, up to 8 in parallel: `make check TESTSUITEFLAGS=-j8` This takes under a minute on a modern 4-core desktop system. To see a list of all the available tests, run: `make check TESTSUITEFLAGS=--list` To run only a subset of tests, e.g. test 123 and tests 477 through 484: `make check TESTSUITEFLAGS='123 477-484'` (Tests do not have inter-dependencies, so you may run any subset.) To run tests matching a keyword, e.g. "ovsdb": `make check TESTSUITEFLAGS='-k ovsdb'` To see a complete list of test options: `make check TESTSUITEFLAGS=--help` The results of a testing run are reported in tests/testsuite.log. Please report test failures as bugs and include the testsuite.log in your report. If you have "valgrind" installed, then you can also run the testsuite under valgrind by using "make check-valgrind" in place of "make check". All the same options are available via TESTSUITEFLAGS. When you do this, the "valgrind" results for test `` are reported in files named `tests/testsuite.dir//valgrind.*`. You may find that the valgrind results are easier to interpret if you put "-q" in ~/.valgrindrc, since that reduces the amount of output. Sometimes a few tests may fail on some runs but not others. This is usually a bug in the testsuite, not a bug in Open vSwitch itself. If you find that a test fails intermittently, please report it, since the developers may not have noticed. You can make the testsuite automatically rerun tests that fail, by adding RECHECK=yes to the "make" command line, e.g.: `make check TESTSUITEFLAGS=-j8 RECHECK=yes` OFTest ------ OFTest is an OpenFlow protocol testing suite. Open vSwitch includes a Makefile target to run OFTest with Open vSwitch in "dummy mode". In this mode of testing, no packets travel across physical or virtual networks. Instead, Unix domain sockets stand in as simulated networks. This simulation is imperfect, but it is much easier to set up, does not require extra physical or virtual hardware, and does not require supervisor privileges. To run OFTest with Open vSwitch, first read and follow the instructions under "Testsuites" above. Second, obtain a copy of OFTest and install its prerequisites. You need a copy of OFTest that includes commit 406614846c5 (make ovs-dummy platform work again). This commit was merged into the OFTest repository on Feb 1, 2013, so any copy of OFTest more recent than that should work. Testing OVS in dummy mode does not require root privilege, so you may ignore that requirement. Optionally, add the top-level OFTest directory (containing the "oft" program) to your $PATH. This slightly simplifies running OFTest later. To run OFTest in dummy mode, run the following command from your Open vSwitch build directory: `make check-oftest OFT=` where `` is the absolute path to the "oft" program in OFTest. If you added "oft" to your $PATH, you may omit the OFT variable assignment: `make check-oftest` By default, "check-oftest" passes "oft" just enough options to enable dummy mode. You can use OFTFLAGS to pass additional options. For example, to run just the basic.Echo test instead of all tests (the default) and enable verbose logging: `make check-oftest OFT= OFTFLAGS='--verbose -T basic.Echo'` If you use OFTest that does not include commit 4d1f3eb2c792 (oft: change default port to 6653), merged into the OFTest repository in October 2013, then you need to add an option to use the IETF-assigned controller port: `make check-oftest OFT= OFTFLAGS='--port=6653'` Please interpret OFTest results cautiously. Open vSwitch can fail a given test in OFTest for many reasons, including bugs in Open vSwitch, bugs in OFTest, bugs in the "dummy mode" integration, and differing interpretations of the OpenFlow standard and other standards. Open vSwitch has not been validated against OFTest. Please do report test failures that you believe to represent bugs in Open vSwitch. Include the precise versions of Open vSwitch and OFTest in your bug report, plus any other information needed to reproduce the problem. Ryu --- Ryu is an OpenFlow controller written in Python that includes an extensive OpenFlow testsuite. Open vSwitch includes a Makefile target to run Ryu in "dummy mode". See "OFTest" above for an explanation of dummy mode. To run Ryu tests with Open vSwitch, first read and follow the instructions under "Testsuites" above. Second, obtain a copy of Ryu, install its prerequisites, and build it. You do not need to install Ryu (some of the tests do not get installed, so it does not help). To run Ryu tests, run the following command from your Open vSwitch build directory: `make check-ryu RYUDIR=` where `` is the absolute path to the root of the Ryu source distribution. The default `` is `$srcdir/../ryu` where $srcdir is your Open vSwitch source directory, so if this default is correct then you make simply run `make check-ryu`. Open vSwitch has not been validated against Ryu. Please do report test failures that you believe to represent bugs in Open vSwitch. Include the precise versions of Open vSwitch and Ryu in your bug report, plus any other information needed to reproduce the problem. Vagrant ------- Requires: Vagrant (version 1.7.0 or later) and a compatible hypervisor You must bootstrap and configure the sources (steps are in "Building and Installing Open vSwitch for Linux, FreeBSD or NetBSD" above) before you run the steps described here. A Vagrantfile is provided allowing to compile and provision the source tree as found locally in a virtual machine using the following commands: vagrant up vagrant ssh This will bring up w Fedora 20 VM by default, alternatively the `Vagrantfile` can be modified to use a different distribution box as base. Also, the VM can be reprovisioned at any time: vagrant provision OVS out-of-tree compilation environment can be set up with: ./boot.sh vagrant provision --provision-with configure_ovs,build_ovs This will set up an out-of-tree build environment in /home/vagrant/build. The source code can be found in /vagrant. Out-of-tree build is preferred to work around limitations of the sync file systems. To recompile and reinstall OVS using RPM: ./boot.sh vagrant provision --provision-with configure_ovs,install_rpm Two provisioners are included to run system tests with the OVS kernel module or with a userspace datapath. This tests are different from the self-tests mentioned above. To run them: ./boot.sh vagrant provision --provision-with configure_ovs,test_ovs_kmod,test_ovs_system_userspace Continuous Integration with Travis-CI ------------------------------------- A .travis.yml file is provided to automatically build Open vSwitch with various build configurations and run the testsuite using travis-ci. Builds will be performed with gcc, sparse and clang with the -Werror compiler flag included, therefore the build will fail if a new warning has been introduced. The CI build is triggered via git push (regardless of the specific branch) or pull request against any Open vSwitch GitHub repository that is linked to travis-ci. Instructions to setup travis-ci for your GitHub repository: 1. Go to http://travis-ci.org/ and sign in using your GitHub ID. 2. Go to the "Repositories" tab and enable the ovs repository. You may disable builds for pushes or pull requests. 3. In order to avoid forks sending build failures to the upstream mailing list, the notification email recipient is encrypted. If you want to receive email notification for build failures, replace the the encrypted string: 3.1) Install the travis-ci CLI (Requires ruby >=2.0): gem install travis 3.2) In your Open vSwitch repository: travis encrypt mylist@mydomain.org 3.3) Add/replace the notifications section in .travis.yml and fill in the secure string as returned by travis encrypt: notifications: email: recipients: - secure: "....." (You may remove/omit the notifications section to fall back to default notification behaviour which is to send an email directly to the author and committer of the failing commit. Note that the email is only sent if the author/committer have commit rights for the particular GitHub repository). 4. Pushing a commit to the repository which breaks the build or the testsuite will now trigger a email sent to mylist@mydomain.org Bug Reporting ============= Please report problems to bugs@openvswitch.org. [README.md]:README.md [INSTALL.Debian.md]:INSTALL.Debian.md [INSTALL.Fedora.md]:INSTALL.Fedora.md [INSTALL.RHEL.md]:INSTALL.RHEL.md [INSTALL.XenServer.md]:INSTALL.XenServer.md [INSTALL.NetBSD.md]:INSTALL.NetBSD.md [INSTALL.DPDK.md]:INSTALL.DPDK.md [INSTALL.userspace.md]:INSTALL.userspace.md [FAQ.md]:FAQ.md openvswitch-2.5.9/PaxHeaders.82075/aclocal.m40000644000000000000000000000013013534540077015510 xustar0029 mtime=1567801407.53772632 30 atime=1567801407.641727086 29 ctime=1567801423.65384511 openvswitch-2.5.9/aclocal.m40000644000175000017500000012300713534540077017203 0ustar00jpettitjpettit00000000000000# generated automatically by aclocal 1.15.1 -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/absolute-header.m4]) m4_include([m4/ax_check_openssl.m4]) m4_include([m4/ax_func_posix_memalign.m4]) m4_include([m4/include_next.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/openvswitch.m4]) m4_include([acinclude.m4]) openvswitch-2.5.9/PaxHeaders.82075/datapath0000644000000000000000000000013213534540121015351 xustar0030 mtime=1567801425.561859176 30 atime=1567801425.625859648 30 ctime=1567801425.561859176 openvswitch-2.5.9/datapath/0000755000175000017500000000000013534540121017114 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/PaxHeaders.82075/flow.c0000644000000000000000000000013213534540071016545 xustar0030 mtime=1567801401.245680025 30 atime=1567801402.053685959 30 ctime=1567801425.517858851 openvswitch-2.5.9/datapath/flow.c0000644000175000017500000004732313534540071020244 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "conntrack.h" #include "flow.h" #include "flow_netlink.h" #include "vport.h" #include "vlan.h" u64 ovs_flow_used_time(unsigned long flow_jiffies) { struct timespec cur_ts; u64 cur_ms, idle_ms; ktime_get_ts(&cur_ts); idle_ms = jiffies_to_msecs(jiffies - flow_jiffies); cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC + cur_ts.tv_nsec / NSEC_PER_MSEC; return cur_ms - idle_ms; } #define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF)) void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, const struct sk_buff *skb) { struct flow_stats *stats; int node = numa_node_id(); int len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); stats = rcu_dereference(flow->stats[node]); /* Check if already have node-specific stats. */ if (likely(stats)) { spin_lock(&stats->lock); /* Mark if we write on the pre-allocated stats. */ if (node == 0 && unlikely(flow->stats_last_writer != node)) flow->stats_last_writer = node; } else { stats = rcu_dereference(flow->stats[0]); /* Pre-allocated. */ spin_lock(&stats->lock); /* If the current NUMA-node is the only writer on the * pre-allocated stats keep using them. */ if (unlikely(flow->stats_last_writer != node)) { /* A previous locker may have already allocated the * stats, so we need to check again. If node-specific * stats were already allocated, we update the pre- * allocated stats as we have already locked them. */ if (likely(flow->stats_last_writer != NUMA_NO_NODE) && likely(!rcu_access_pointer(flow->stats[node]))) { /* Try to allocate node-specific stats. */ struct flow_stats *new_stats; new_stats = kmem_cache_alloc_node(flow_stats_cache, GFP_NOWAIT | __GFP_THISNODE | __GFP_NOWARN | __GFP_NOMEMALLOC, node); if (likely(new_stats)) { new_stats->used = jiffies; new_stats->packet_count = 1; new_stats->byte_count = len; new_stats->tcp_flags = tcp_flags; spin_lock_init(&new_stats->lock); rcu_assign_pointer(flow->stats[node], new_stats); goto unlock; } } flow->stats_last_writer = node; } } stats->used = jiffies; stats->packet_count++; stats->byte_count += len; stats->tcp_flags |= tcp_flags; unlock: spin_unlock(&stats->lock); } /* Must be called with rcu_read_lock or ovs_mutex. */ void ovs_flow_stats_get(const struct sw_flow *flow, struct ovs_flow_stats *ovs_stats, unsigned long *used, __be16 *tcp_flags) { int node; *used = 0; *tcp_flags = 0; memset(ovs_stats, 0, sizeof(*ovs_stats)); for_each_node(node) { struct flow_stats *stats = rcu_dereference_ovsl(flow->stats[node]); if (stats) { /* Local CPU may write on non-local stats, so we must * block bottom-halves here. */ spin_lock_bh(&stats->lock); if (!*used || time_after(stats->used, *used)) *used = stats->used; *tcp_flags |= stats->tcp_flags; ovs_stats->n_packets += stats->packet_count; ovs_stats->n_bytes += stats->byte_count; spin_unlock_bh(&stats->lock); } } } /* Called with ovs_mutex. */ void ovs_flow_stats_clear(struct sw_flow *flow) { int node; for_each_node(node) { struct flow_stats *stats = ovsl_dereference(flow->stats[node]); if (stats) { spin_lock_bh(&stats->lock); stats->used = 0; stats->packet_count = 0; stats->byte_count = 0; stats->tcp_flags = 0; spin_unlock_bh(&stats->lock); } } } static int check_header(struct sk_buff *skb, int len) { if (unlikely(skb->len < len)) return -EINVAL; if (unlikely(!pskb_may_pull(skb, len))) return -ENOMEM; return 0; } static bool arphdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_network_offset(skb) + sizeof(struct arp_eth_header)); } static int check_iphdr(struct sk_buff *skb) { unsigned int nh_ofs = skb_network_offset(skb); unsigned int ip_len; int err; err = check_header(skb, nh_ofs + sizeof(struct iphdr)); if (unlikely(err)) return err; ip_len = ip_hdrlen(skb); if (unlikely(ip_len < sizeof(struct iphdr) || skb->len < nh_ofs + ip_len)) return -EINVAL; skb_set_transport_header(skb, nh_ofs + ip_len); return 0; } static bool tcphdr_ok(struct sk_buff *skb) { int th_ofs = skb_transport_offset(skb); int tcp_len; if (unlikely(!pskb_may_pull(skb, th_ofs + sizeof(struct tcphdr)))) return false; tcp_len = tcp_hdrlen(skb); if (unlikely(tcp_len < sizeof(struct tcphdr) || skb->len < th_ofs + tcp_len)) return false; return true; } static bool udphdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)); } static bool sctphdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct sctphdr)); } static bool icmphdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct icmphdr)); } static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) { unsigned int nh_ofs = skb_network_offset(skb); unsigned int nh_len; int payload_ofs; struct ipv6hdr *nh; uint8_t nexthdr; __be16 frag_off; int err; err = check_header(skb, nh_ofs + sizeof(*nh)); if (unlikely(err)) return err; nh = ipv6_hdr(skb); nexthdr = nh->nexthdr; payload_ofs = (u8 *)(nh + 1) - skb->data; key->ip.proto = NEXTHDR_NONE; key->ip.tos = ipv6_get_dsfield(nh); key->ip.ttl = nh->hop_limit; key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL); key->ipv6.addr.src = nh->saddr; key->ipv6.addr.dst = nh->daddr; payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr, &frag_off); if (unlikely(payload_ofs < 0)) return -EINVAL; if (frag_off) { if (frag_off & htons(~0x7)) key->ip.frag = OVS_FRAG_TYPE_LATER; else key->ip.frag = OVS_FRAG_TYPE_FIRST; } else { key->ip.frag = OVS_FRAG_TYPE_NONE; } nh_len = payload_ofs - nh_ofs; skb_set_transport_header(skb, nh_ofs + nh_len); key->ip.proto = nexthdr; return nh_len; } static bool icmp6hdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct icmp6hdr)); } static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) { struct qtag_prefix { __be16 eth_type; /* ETH_P_8021Q */ __be16 tci; }; struct qtag_prefix *qp; if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16))) return 0; if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + sizeof(__be16)))) return -ENOMEM; qp = (struct qtag_prefix *) skb->data; key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT); __skb_pull(skb, sizeof(struct qtag_prefix)); return 0; } static __be16 parse_ethertype(struct sk_buff *skb) { struct llc_snap_hdr { u8 dsap; /* Always 0xAA */ u8 ssap; /* Always 0xAA */ u8 ctrl; u8 oui[3]; __be16 ethertype; }; struct llc_snap_hdr *llc; __be16 proto; proto = *(__be16 *) skb->data; __skb_pull(skb, sizeof(__be16)); if (eth_proto_is_802_3(proto)) return proto; if (skb->len < sizeof(struct llc_snap_hdr)) return htons(ETH_P_802_2); if (unlikely(!pskb_may_pull(skb, sizeof(struct llc_snap_hdr)))) return htons(0); llc = (struct llc_snap_hdr *) skb->data; if (llc->dsap != LLC_SAP_SNAP || llc->ssap != LLC_SAP_SNAP || (llc->oui[0] | llc->oui[1] | llc->oui[2]) != 0) return htons(ETH_P_802_2); __skb_pull(skb, sizeof(struct llc_snap_hdr)); if (eth_proto_is_802_3(llc->ethertype)) return llc->ethertype; return htons(ETH_P_802_2); } static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, int nh_len) { struct icmp6hdr *icmp = icmp6_hdr(skb); /* The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ key->tp.src = htons(icmp->icmp6_type); key->tp.dst = htons(icmp->icmp6_code); memset(&key->ipv6.nd, 0, sizeof(key->ipv6.nd)); if (icmp->icmp6_code == 0 && (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION || icmp->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT)) { int icmp_len = skb->len - skb_transport_offset(skb); struct nd_msg *nd; int offset; /* In order to process neighbor discovery options, we need the * entire packet. */ if (unlikely(icmp_len < sizeof(*nd))) return 0; if (unlikely(skb_linearize(skb))) return -ENOMEM; nd = (struct nd_msg *)skb_transport_header(skb); key->ipv6.nd.target = nd->target; icmp_len -= sizeof(*nd); offset = 0; while (icmp_len >= 8) { struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd->opt + offset); int opt_len = nd_opt->nd_opt_len * 8; if (unlikely(!opt_len || opt_len > icmp_len)) return 0; /* Store the link layer address if the appropriate * option is provided. It is considered an error if * the same link layer option is specified twice. */ if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LL_ADDR && opt_len == 8) { if (unlikely(!is_zero_ether_addr(key->ipv6.nd.sll))) goto invalid; ether_addr_copy(key->ipv6.nd.sll, &nd->opt[offset+sizeof(*nd_opt)]); } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR && opt_len == 8) { if (unlikely(!is_zero_ether_addr(key->ipv6.nd.tll))) goto invalid; ether_addr_copy(key->ipv6.nd.tll, &nd->opt[offset+sizeof(*nd_opt)]); } icmp_len -= opt_len; offset += opt_len; } } return 0; invalid: memset(&key->ipv6.nd.target, 0, sizeof(key->ipv6.nd.target)); memset(key->ipv6.nd.sll, 0, sizeof(key->ipv6.nd.sll)); memset(key->ipv6.nd.tll, 0, sizeof(key->ipv6.nd.tll)); return 0; } /** * key_extract_l3l4 - extracts L3/L4 header information. * @skb: sk_buff that contains the frame, with skb->data pointing to the * L3 header * @key: output flow key */ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key) { int error; /* Network layer. */ if (key->eth.type == htons(ETH_P_IP)) { struct iphdr *nh; __be16 offset; error = check_iphdr(skb); if (unlikely(error)) { memset(&key->ip, 0, sizeof(key->ip)); memset(&key->ipv4, 0, sizeof(key->ipv4)); if (error == -EINVAL) { skb->transport_header = skb->network_header; error = 0; } return error; } nh = ip_hdr(skb); key->ipv4.addr.src = nh->saddr; key->ipv4.addr.dst = nh->daddr; key->ip.proto = nh->protocol; key->ip.tos = nh->tos; key->ip.ttl = nh->ttl; offset = nh->frag_off & htons(IP_OFFSET); if (offset) { key->ip.frag = OVS_FRAG_TYPE_LATER; memset(&key->tp, 0, sizeof(key->tp)); return 0; } if (nh->frag_off & htons(IP_MF) || skb_shinfo(skb)->gso_type & SKB_GSO_UDP) key->ip.frag = OVS_FRAG_TYPE_FIRST; else key->ip.frag = OVS_FRAG_TYPE_NONE; /* Transport layer. */ if (key->ip.proto == IPPROTO_TCP) { if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); key->tp.src = tcp->source; key->tp.dst = tcp->dest; key->tp.flags = TCP_FLAGS_BE16(tcp); } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == IPPROTO_UDP) { if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); key->tp.src = udp->source; key->tp.dst = udp->dest; } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == IPPROTO_SCTP) { if (sctphdr_ok(skb)) { struct sctphdr *sctp = sctp_hdr(skb); key->tp.src = sctp->source; key->tp.dst = sctp->dest; } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == IPPROTO_ICMP) { if (icmphdr_ok(skb)) { struct icmphdr *icmp = icmp_hdr(skb); /* The ICMP type and code fields use the 16-bit * transport port fields, so we need to store * them in 16-bit network byte order. */ key->tp.src = htons(icmp->type); key->tp.dst = htons(icmp->code); } else { memset(&key->tp, 0, sizeof(key->tp)); } } } else if (key->eth.type == htons(ETH_P_ARP) || key->eth.type == htons(ETH_P_RARP)) { struct arp_eth_header *arp; bool arp_available = arphdr_ok(skb); arp = (struct arp_eth_header *)skb_network_header(skb); if (arp_available && arp->ar_hrd == htons(ARPHRD_ETHER) && arp->ar_pro == htons(ETH_P_IP) && arp->ar_hln == ETH_ALEN && arp->ar_pln == 4) { /* We only match on the lower 8 bits of the opcode. */ if (ntohs(arp->ar_op) <= 0xff) key->ip.proto = ntohs(arp->ar_op); else key->ip.proto = 0; memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src)); memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst)); ether_addr_copy(key->ipv4.arp.sha, arp->ar_sha); ether_addr_copy(key->ipv4.arp.tha, arp->ar_tha); } else { memset(&key->ip, 0, sizeof(key->ip)); memset(&key->ipv4, 0, sizeof(key->ipv4)); } } else if (eth_p_mpls(key->eth.type)) { size_t stack_len = MPLS_HLEN; /* In the presence of an MPLS label stack the end of the L2 * header and the beginning of the L3 header differ. * * Advance network_header to the beginning of the L3 * header. mac_len corresponds to the end of the L2 header. */ while (1) { __be32 lse; error = check_header(skb, skb->mac_len + stack_len); if (unlikely(error)) return 0; memcpy(&lse, skb_network_header(skb), MPLS_HLEN); if (stack_len == MPLS_HLEN) memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN); skb_set_network_header(skb, skb->mac_len + stack_len); if (lse & htonl(MPLS_LS_S_MASK)) break; stack_len += MPLS_HLEN; } } else if (key->eth.type == htons(ETH_P_IPV6)) { int nh_len; /* IPv6 Header + Extensions */ nh_len = parse_ipv6hdr(skb, key); if (unlikely(nh_len < 0)) { memset(&key->ip, 0, sizeof(key->ip)); memset(&key->ipv6.addr, 0, sizeof(key->ipv6.addr)); if (nh_len == -EINVAL) { skb->transport_header = skb->network_header; error = 0; } else { error = nh_len; } return error; } if (key->ip.frag == OVS_FRAG_TYPE_LATER) { memset(&key->tp, 0, sizeof(key->tp)); return 0; } if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) key->ip.frag = OVS_FRAG_TYPE_FIRST; /* Transport layer. */ if (key->ip.proto == NEXTHDR_TCP) { if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); key->tp.src = tcp->source; key->tp.dst = tcp->dest; key->tp.flags = TCP_FLAGS_BE16(tcp); } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == NEXTHDR_UDP) { if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); key->tp.src = udp->source; key->tp.dst = udp->dest; } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == NEXTHDR_SCTP) { if (sctphdr_ok(skb)) { struct sctphdr *sctp = sctp_hdr(skb); key->tp.src = sctp->source; key->tp.dst = sctp->dest; } else { memset(&key->tp, 0, sizeof(key->tp)); } } else if (key->ip.proto == NEXTHDR_ICMP) { if (icmp6hdr_ok(skb)) { error = parse_icmpv6(skb, key, nh_len); if (error) return error; } else { memset(&key->tp, 0, sizeof(key->tp)); } } } return 0; } /** * key_extract - extracts a flow key from an Ethernet frame. * @skb: sk_buff that contains the frame, with skb->data pointing to the * Ethernet header * @key: output flow key * * The caller must ensure that skb->len >= ETH_HLEN. * * Returns 0 if successful, otherwise a negative errno value. * * Initializes @skb header fields as follows: * * - skb->mac_header: the L2 header. * * - skb->network_header: just past the L2 header, or just past the * VLAN header, to the first byte of the L2 payload. * * - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6 * on output, then just past the IP header, if one is present and * of a correct length, otherwise the same as skb->network_header. * For other key->eth.type values it is left untouched. * * - skb->protocol: the type of the data starting at skb->network_header. * Equals to key->eth.type. */ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) { struct ethhdr *eth; /* Flags are always used as part of stats */ key->tp.flags = 0; skb_reset_mac_header(skb); /* Link layer. We are guaranteed to have at least the 14 byte Ethernet * header in the linear data area. */ eth = eth_hdr(skb); ether_addr_copy(key->eth.src, eth->h_source); ether_addr_copy(key->eth.dst, eth->h_dest); __skb_pull(skb, 2 * ETH_ALEN); /* We are going to push all headers that we pull, so no need to * update skb->csum here. */ key->eth.tci = 0; if (skb_vlan_tag_present(skb)) key->eth.tci = htons(skb->vlan_tci); else if (eth->h_proto == htons(ETH_P_8021Q)) if (unlikely(parse_vlan(skb, key))) return -ENOMEM; key->eth.type = parse_ethertype(skb); if (unlikely(key->eth.type == htons(0))) return -ENOMEM; skb_reset_network_header(skb); skb_reset_mac_len(skb); __skb_push(skb, skb->data - skb_mac_header(skb)); /* Fill out L3/L4 key info, if any */ return key_extract_l3l4(skb, key); } /* In the case of conntrack fragment handling it expects L3 headers, * add a helper. */ int ovs_flow_key_update_l3l4(struct sk_buff *skb, struct sw_flow_key *key) { return key_extract_l3l4(skb, key); } int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) { return key_extract(skb, key); } int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key) { /* Extract metadata from packet. */ if (tun_info) { if (ip_tunnel_info_af(tun_info) != AF_INET) return -EINVAL; memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key)); BUILD_BUG_ON(((1 << (sizeof(tun_info->options_len) * 8)) - 1) > sizeof(key->tun_opts)); if (tun_info->options_len) { ip_tunnel_info_opts_get(TUN_METADATA_OPTS(key, tun_info->options_len), tun_info); key->tun_opts_len = tun_info->options_len; } else { key->tun_opts_len = 0; } } else { key->tun_opts_len = 0; memset(&key->tun_key, 0, sizeof(key->tun_key)); } key->phy.priority = skb->priority; key->phy.in_port = OVS_CB(skb)->input_vport->port_no; key->phy.skb_mark = skb->mark; ovs_ct_fill_key(skb, key); key->ovs_flow_hash = 0; key->recirc_id = 0; return key_extract(skb, key); } int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr, struct sk_buff *skb, struct sw_flow_key *key, bool log) { int err; /* Extract metadata from netlink attributes. */ err = ovs_nla_get_flow_metadata(net, attr, key, log); if (err) return err; return key_extract(skb, key); } openvswitch-2.5.9/datapath/PaxHeaders.82075/conntrack.c0000644000000000000000000000013213534540071017560 xustar0030 mtime=1567801401.241679995 30 atime=1567801402.053685959 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/conntrack.c0000644000175000017500000005115313534540071021253 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) && \ IS_ENABLED(CONFIG_NF_CONNTRACK) #include #include #include #include #include #include #include #include #include "datapath.h" #include "conntrack.h" #include "flow.h" #include "flow_netlink.h" #include "gso.h" struct ovs_ct_len_tbl { size_t maxlen; size_t minlen; }; /* Metadata mark for masked write to conntrack mark */ struct md_mark { u32 value; u32 mask; }; /* Metadata label for masked write to conntrack label. */ struct md_labels { struct ovs_key_ct_labels value; struct ovs_key_ct_labels mask; }; /* Conntrack action context for execution. */ struct ovs_conntrack_info { struct nf_conntrack_helper *helper; struct nf_conntrack_zone zone; struct nf_conn *ct; u8 commit : 1; u16 family; struct md_mark mark; struct md_labels labels; }; static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info); static u16 key_to_nfproto(const struct sw_flow_key *key) { switch (ntohs(key->eth.type)) { case ETH_P_IP: return NFPROTO_IPV4; case ETH_P_IPV6: return NFPROTO_IPV6; default: return NFPROTO_UNSPEC; } } /* Map SKB connection state into the values used by flow definition. */ static u8 ovs_ct_get_state(enum ip_conntrack_info ctinfo) { u8 ct_state = OVS_CS_F_TRACKED; switch (ctinfo) { case IP_CT_ESTABLISHED_REPLY: case IP_CT_RELATED_REPLY: case IP_CT_NEW_REPLY: ct_state |= OVS_CS_F_REPLY_DIR; break; default: break; } switch (ctinfo) { case IP_CT_ESTABLISHED: case IP_CT_ESTABLISHED_REPLY: ct_state |= OVS_CS_F_ESTABLISHED; break; case IP_CT_RELATED: case IP_CT_RELATED_REPLY: ct_state |= OVS_CS_F_RELATED; break; case IP_CT_NEW: case IP_CT_NEW_REPLY: ct_state |= OVS_CS_F_NEW; break; default: break; } return ct_state; } static u32 ovs_ct_get_mark(const struct nf_conn *ct) { #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) return ct ? ct->mark : 0; #else return 0; #endif } static void ovs_ct_get_labels(const struct nf_conn *ct, struct ovs_key_ct_labels *labels) { struct nf_conn_labels *cl = ct ? nf_ct_labels_find(ct) : NULL; if (cl) { size_t len = cl->words * sizeof(long); if (len > OVS_CT_LABELS_LEN) len = OVS_CT_LABELS_LEN; else if (len < OVS_CT_LABELS_LEN) memset(labels, 0, OVS_CT_LABELS_LEN); memcpy(labels, cl->bits, len); } else { memset(labels, 0, OVS_CT_LABELS_LEN); } } static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, const struct nf_conntrack_zone *zone, const struct nf_conn *ct) { key->ct.state = state; key->ct.zone = zone->id; key->ct.mark = ovs_ct_get_mark(ct); ovs_ct_get_labels(ct, &key->ct.labels); } /* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has * previously sent the packet to conntrack via the ct action. */ static void ovs_ct_update_key(const struct sk_buff *skb, const struct ovs_conntrack_info *info, struct sw_flow_key *key, bool post_ct) { const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt; enum ip_conntrack_info ctinfo; struct nf_conn *ct; u8 state = 0; ct = nf_ct_get(skb, &ctinfo); if (ct) { state = ovs_ct_get_state(ctinfo); if (!nf_ct_is_confirmed(ct)) state |= OVS_CS_F_NEW; if (ct->master) state |= OVS_CS_F_RELATED; zone = nf_ct_zone(ct); } else if (post_ct) { state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID; if (info) zone = &info->zone; } __ovs_ct_update_key(key, state, zone, ct); } void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) { ovs_ct_update_key(skb, NULL, key, false); } int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) { if (nla_put_u32(skb, OVS_KEY_ATTR_CT_STATE, key->ct.state)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && nla_put_u16(skb, OVS_KEY_ATTR_CT_ZONE, key->ct.zone)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && nla_put_u32(skb, OVS_KEY_ATTR_CT_MARK, key->ct.mark)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && nla_put(skb, OVS_KEY_ATTR_CT_LABELS, sizeof(key->ct.labels), &key->ct.labels)) return -EMSGSIZE; return 0; } static int ovs_ct_set_mark(struct sk_buff *skb, struct sw_flow_key *key, u32 ct_mark, u32 mask) { #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) enum ip_conntrack_info ctinfo; struct nf_conn *ct; u32 new_mark; /* The connection could be invalid, in which case set_mark is no-op. */ ct = nf_ct_get(skb, &ctinfo); if (!ct) return 0; new_mark = ct_mark | (ct->mark & ~(mask)); if (ct->mark != new_mark) { ct->mark = new_mark; nf_conntrack_event_cache(IPCT_MARK, ct); key->ct.mark = new_mark; } return 0; #else return -ENOTSUPP; #endif } static int ovs_ct_set_labels(struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_key_ct_labels *labels, const struct ovs_key_ct_labels *mask) { enum ip_conntrack_info ctinfo; struct nf_conn_labels *cl; struct nf_conn *ct; int err; /* The connection could be invalid, in which case set_label is no-op.*/ ct = nf_ct_get(skb, &ctinfo); if (!ct) return 0; cl = nf_ct_labels_find(ct); if (!cl) { nf_ct_labels_ext_add(ct); cl = nf_ct_labels_find(ct); } if (!cl || cl->words * sizeof(long) < OVS_CT_LABELS_LEN) return -ENOSPC; err = nf_connlabels_replace(ct, (u32 *)labels, (u32 *)mask, OVS_CT_LABELS_LEN / sizeof(u32)); if (err) return err; ovs_ct_get_labels(ct, &key->ct.labels); return 0; } /* 'skb' should already be pulled to nh_ofs. */ static int ovs_ct_helper(struct sk_buff *skb, u16 proto) { const struct nf_conntrack_helper *helper; const struct nf_conn_help *help; enum ip_conntrack_info ctinfo; unsigned int protoff; struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); if (!ct || ctinfo == IP_CT_RELATED_REPLY) return NF_ACCEPT; help = nfct_help(ct); if (!help) return NF_ACCEPT; helper = rcu_dereference(help->helper); if (!helper) return NF_ACCEPT; switch (proto) { case NFPROTO_IPV4: protoff = ip_hdrlen(skb); break; case NFPROTO_IPV6: { u8 nexthdr = ipv6_hdr(skb)->nexthdr; __be16 frag_off; int ofs; ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, &frag_off); if (ofs < 0 || (frag_off & htons(~0x7)) != 0) { pr_debug("proto header not found\n"); return NF_ACCEPT; } protoff = ofs; break; } default: WARN_ONCE(1, "helper invoked on non-IP family!"); return NF_DROP; } return helper->help(skb, protoff, ct, ctinfo); } /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero * value if 'skb' is freed. */ static int handle_fragments(struct net *net, struct sw_flow_key *key, u16 zone, struct sk_buff *skb) { struct ovs_gso_cb ovs_cb = *OVS_GSO_CB(skb); if (!skb->dev) { OVS_NLERR(true, "%s: skb has no dev; dropping", __func__); return -EINVAL; } if (key->eth.type == htons(ETH_P_IP)) { enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone; int err; memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); err = ip_defrag(skb, user); if (err) return err; ovs_cb.dp_cb.mru = IPCB(skb)->frag_max_size; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) } else if (key->eth.type == htons(ETH_P_IPV6)) { enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone; struct sk_buff *reasm; memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); reasm = nf_ct_frag6_gather(skb, user); if (!reasm) return -EINPROGRESS; if (skb == reasm) { kfree_skb(skb); return -EINVAL; } /* Don't free 'skb' even though it is one of the original * fragments, as we're going to morph it into the head. */ skb_get(skb); nf_ct_frag6_consume_orig(reasm); key->ip.proto = ipv6_hdr(reasm)->nexthdr; skb_morph(skb, reasm); skb->next = reasm->next; consume_skb(reasm); ovs_cb.dp_cb.mru = IP6CB(skb)->frag_max_size; #endif /* IP frag support */ } else { kfree_skb(skb); return -EPFNOSUPPORT; } /* The key extracted from the fragment that completed this datagram * likely didn't have an L4 header, so regenerate it. */ ovs_flow_key_update_l3l4(skb, key); key->ip.frag = OVS_FRAG_TYPE_NONE; skb_clear_hash(skb); skb->ignore_df = 1; *OVS_GSO_CB(skb) = ovs_cb; return 0; } static struct nf_conntrack_expect * ovs_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, u16 proto, const struct sk_buff *skb) { struct nf_conntrack_tuple tuple; if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), proto, &tuple)) return NULL; return __nf_ct_expect_find(net, zone, &tuple); } /* Determine whether skb->nfct is equal to the result of conntrack lookup. */ static bool skb_nfct_cached(const struct net *net, const struct sk_buff *skb, const struct ovs_conntrack_info *info) { enum ip_conntrack_info ctinfo; struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; if (!net_eq(net, read_pnet(&ct->ct_net))) return false; if (!nf_ct_zone_equal_any(info->ct, nf_ct_zone(ct))) return false; if (info->helper) { struct nf_conn_help *help; help = nf_ct_ext_find(ct, NF_CT_EXT_HELPER); if (help && rcu_access_pointer(help->helper) != info->helper) return false; } return true; } static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, const struct ovs_conntrack_info *info, struct sk_buff *skb) { /* If we are recirculating packets to match on conntrack fields and * committing with a separate conntrack action, then we don't need to * actually run the packet through conntrack twice unless it's for a * different zone. */ if (!skb_nfct_cached(net, skb, info)) { struct nf_conn *tmpl = info->ct; enum ip_conntrack_info ctinfo; struct nf_conn *ct; int err; /* Associate skb with specified zone. */ if (tmpl) { if (skb->nfct) nf_conntrack_put(skb->nfct); nf_conntrack_get(&tmpl->ct_general); skb->nfct = &tmpl->ct_general; skb->nfctinfo = IP_CT_NEW; } /* Repeat if requested, see nf_iterate(). */ do { err = nf_conntrack_in(net, info->family, NF_INET_FORWARD, skb); } while (err == NF_REPEAT); if (err != NF_ACCEPT) return -ENOENT; ct = nf_ct_get(skb, &ctinfo); if (ct) { /* Userspace may decide to perform a ct lookup without * a helper specified followed by a (recirculate and) * commit with one. Therefore, for unconfirmed * connections which we will commit, we need to attach * the helper here. */ if (!nf_ct_is_confirmed(ct) && info->commit && info->helper && !nfct_help(ct)) { int err = __nf_ct_try_assign_helper(ct, info->ct, GFP_ATOMIC); if (err) return err; } /* Call the helper only if: * - The connection is confirmed, or * - When committing an unconfirmed connection. */ if ((nf_ct_is_confirmed(ct) || info->commit) && ovs_ct_helper(skb, info->family) != NF_ACCEPT) { WARN_ONCE(1, "helper rejected packet"); return -EINVAL; } } } ovs_ct_update_key(skb, info, key, true); return 0; } /* Lookup connection and read fields into key. */ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, const struct ovs_conntrack_info *info, struct sk_buff *skb) { struct nf_conntrack_expect *exp; exp = ovs_ct_expect_find(net, &info->zone, info->family, skb); if (exp) { u8 state; state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED; __ovs_ct_update_key(key, state, &info->zone, exp->master); } else { int err; err = __ovs_ct_lookup(net, key, info, skb); if (err) return err; } return 0; } static bool labels_nonzero(const struct ovs_key_ct_labels *labels) { size_t i; for (i = 0; i < sizeof(*labels); i++) if (labels->ct_labels[i]) return true; return false; } /* Lookup connection and confirm if unconfirmed. */ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, const struct ovs_conntrack_info *info, struct sk_buff *skb) { u8 state; int err; state = key->ct.state; if (key->ct.zone == info->zone.id && ((state & OVS_CS_F_TRACKED) && !(state & OVS_CS_F_NEW))) { /* Previous lookup has shown that this connection is already * tracked and committed. Skip committing. */ return 0; } err = __ovs_ct_lookup(net, key, info, skb); if (err) return err; /* Apply changes before confirming the connection so that the initial * conntrack NEW netlink event carries the values given in the CT * action. */ if (info->mark.mask) { err = ovs_ct_set_mark(skb, key, info->mark.value, info->mark.mask); if (err) return err; } if (labels_nonzero(&info->labels.mask)) { err = ovs_ct_set_labels(skb, key, &info->labels.value, &info->labels.mask); if (err) return err; } /* This will take care of sending queued events even if the connection * is already confirmed. */ if (nf_conntrack_confirm(skb) != NF_ACCEPT) return -EINVAL; return 0; } /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero * value if 'skb' is freed. */ int ovs_ct_execute(struct net *net, struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_conntrack_info *info) { int nh_ofs; int err; /* The conntrack module expects to be working at L3. */ nh_ofs = skb_network_offset(skb); skb_pull(skb, nh_ofs); if (key->ip.frag != OVS_FRAG_TYPE_NONE) { err = handle_fragments(net, key, info->zone.id, skb); if (err) return err; } if (info->commit) err = ovs_ct_commit(net, key, info, skb); else err = ovs_ct_lookup(net, key, info, skb); skb_push(skb, nh_ofs); if (err) kfree_skb(skb); return err; } static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, const struct sw_flow_key *key, bool log) { struct nf_conntrack_helper *helper; struct nf_conn_help *help; helper = nf_conntrack_helper_try_module_get(name, info->family, key->ip.proto); if (!helper) { OVS_NLERR(log, "Unknown helper \"%s\"", name); return -EINVAL; } help = nf_ct_helper_ext_add(info->ct, helper, GFP_KERNEL); if (!help) { module_put(helper->me); return -ENOMEM; } rcu_assign_pointer(help->helper, helper); info->helper = helper; return 0; } static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 }, [OVS_CT_ATTR_ZONE] = { .minlen = sizeof(u16), .maxlen = sizeof(u16) }, [OVS_CT_ATTR_MARK] = { .minlen = sizeof(struct md_mark), .maxlen = sizeof(struct md_mark) }, [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels), .maxlen = sizeof(struct md_labels) }, [OVS_CT_ATTR_HELPER] = { .minlen = 1, .maxlen = NF_CT_HELPER_NAME_LEN } }; static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, const char **helper, bool log) { struct nlattr *a; int rem; nla_for_each_nested(a, attr, rem) { int type = nla_type(a); int maxlen; int minlen; if (type > OVS_CT_ATTR_MAX) { OVS_NLERR(log, "Unknown conntrack attr (type=%d, max=%d)", type, OVS_CT_ATTR_MAX); return -EINVAL; } maxlen = ovs_ct_attr_lens[type].maxlen; minlen = ovs_ct_attr_lens[type].minlen; if (nla_len(a) < minlen || nla_len(a) > maxlen) { OVS_NLERR(log, "Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)", type, nla_len(a), maxlen); return -EINVAL; } switch (type) { case OVS_CT_ATTR_COMMIT: info->commit = true; break; #ifdef CONFIG_NF_CONNTRACK_ZONES case OVS_CT_ATTR_ZONE: info->zone.id = nla_get_u16(a); break; #endif #ifdef CONFIG_NF_CONNTRACK_MARK case OVS_CT_ATTR_MARK: { struct md_mark *mark = nla_data(a); if (!mark->mask) { OVS_NLERR(log, "ct_mark mask cannot be 0"); return -EINVAL; } info->mark = *mark; break; } #endif #ifdef CONFIG_NF_CONNTRACK_LABELS case OVS_CT_ATTR_LABELS: { struct md_labels *labels = nla_data(a); if (!labels_nonzero(&labels->mask)) { OVS_NLERR(log, "ct_labels mask cannot be 0"); return -EINVAL; } info->labels = *labels; break; } #endif case OVS_CT_ATTR_HELPER: *helper = nla_data(a); if (!memchr(*helper, '\0', nla_len(a))) { OVS_NLERR(log, "Invalid conntrack helper"); return -EINVAL; } break; default: OVS_NLERR(log, "Unknown conntrack attr (%d)", type); return -EINVAL; } } #ifdef CONFIG_NF_CONNTRACK_MARK if (!info->commit && info->mark.mask) { OVS_NLERR(log, "Setting conntrack mark requires 'commit' flag."); return -EINVAL; } #endif #ifdef CONFIG_NF_CONNTRACK_LABELS if (!info->commit && labels_nonzero(&info->labels.mask)) { OVS_NLERR(log, "Setting conntrack labels requires 'commit' flag."); return -EINVAL; } #endif if (rem > 0) { OVS_NLERR(log, "Conntrack attr has %d unknown bytes", rem); return -EINVAL; } return 0; } bool ovs_ct_verify(struct net *net, enum ovs_key_attr attr) { if (attr == OVS_KEY_ATTR_CT_STATE) return true; if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && attr == OVS_KEY_ATTR_CT_ZONE) return true; if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && attr == OVS_KEY_ATTR_CT_MARK) return true; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && attr == OVS_KEY_ATTR_CT_LABELS) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); return ovs_net->xt_label; } return false; } int ovs_ct_copy_action(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, struct sw_flow_actions **sfa, bool log) { struct ovs_conntrack_info ct_info; const char *helper = NULL; u16 family; int err; family = key_to_nfproto(key); if (family == NFPROTO_UNSPEC) { OVS_NLERR(log, "ct family unspecified"); return -EINVAL; } memset(&ct_info, 0, sizeof(ct_info)); ct_info.family = family; nf_ct_zone_init(&ct_info.zone, NF_CT_DEFAULT_ZONE_ID, NF_CT_DEFAULT_ZONE_DIR, 0); err = parse_ct(attr, &ct_info, &helper, log); if (err) return err; /* Set up template for tracking connections in specific zones. */ ct_info.ct = nf_ct_tmpl_alloc(net, &ct_info.zone, GFP_KERNEL); if (!ct_info.ct) { OVS_NLERR(log, "Failed to allocate conntrack template"); return -ENOMEM; } if (helper) { err = ovs_ct_add_helper(&ct_info, helper, key, log); if (err) goto err_free_ct; } err = ovs_nla_add_action(sfa, OVS_ACTION_ATTR_CT, &ct_info, sizeof(ct_info), log); if (err) goto err_free_ct; __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status); nf_conntrack_get(&ct_info.ct->ct_general); return 0; err_free_ct: __ovs_ct_free_action(&ct_info); return err; } int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, struct sk_buff *skb) { struct nlattr *start; start = nla_nest_start(skb, OVS_ACTION_ATTR_CT); if (!start) return -EMSGSIZE; if (ct_info->commit && nla_put_flag(skb, OVS_CT_ATTR_COMMIT)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && nla_put_u16(skb, OVS_CT_ATTR_ZONE, ct_info->zone.id)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && ct_info->mark.mask && nla_put(skb, OVS_CT_ATTR_MARK, sizeof(ct_info->mark), &ct_info->mark)) return -EMSGSIZE; if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && labels_nonzero(&ct_info->labels.mask) && nla_put(skb, OVS_CT_ATTR_LABELS, sizeof(ct_info->labels), &ct_info->labels)) return -EMSGSIZE; if (ct_info->helper) { if (nla_put_string(skb, OVS_CT_ATTR_HELPER, ct_info->helper->name)) return -EMSGSIZE; } nla_nest_end(skb, start); return 0; } void ovs_ct_free_action(const struct nlattr *a) { struct ovs_conntrack_info *ct_info = nla_data(a); __ovs_ct_free_action(ct_info); } static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info) { if (ct_info->helper) module_put(ct_info->helper->me); if (ct_info->ct) nf_ct_tmpl_free(ct_info->ct); } void ovs_ct_init(struct net *net) { unsigned int n_bits = sizeof(struct ovs_key_ct_labels) * BITS_PER_BYTE; struct ovs_net *ovs_net = net_generic(net, ovs_net_id); if (nf_connlabels_get(net, n_bits)) { ovs_net->xt_label = false; OVS_NLERR(true, "Failed to set connlabel length"); } else { ovs_net->xt_label = true; } } void ovs_ct_exit(struct net *net) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); if (ovs_net->xt_label) nf_connlabels_put(net); } #endif /* CONFIG_NF_CONNTRACK && LINUX > 3.10 */ openvswitch-2.5.9/datapath/PaxHeaders.82075/compat.h0000644000000000000000000000013213534540071017066 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801425.413858086 openvswitch-2.5.9/datapath/compat.h0000644000175000017500000000443713534540071020564 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef COMPAT_H #define COMPAT_H 1 #include #include #include #include #include #include #include #ifdef HAVE_GENL_MULTICAST_GROUP_WITH_ID #define GROUP_ID(grp) ((grp)->id) #else #define GROUP_ID(grp) 0 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) #define rt_dst(rt) (rt->dst) #else #define rt_dst(rt) (rt->u.dst) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) #define inet_sport(sk) (inet_sk(sk)->sport) #else #define inet_sport(sk) (inet_sk(sk)->inet_sport) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) static inline bool skb_encapsulation(struct sk_buff *skb) { return skb->encapsulation; } #else #define skb_encapsulation(skb) false #endif #ifdef OVS_FRAGMENT_BACKPORT #ifdef HAVE_NF_IPV6_OPS_FRAGMENT static inline int __init ip6_output_init(void) { return 0; } static inline void ip6_output_exit(void) { } #else int __init ip6_output_init(void); void ip6_output_exit(void); #endif static inline int __init compat_init(void) { int err; err = ipfrag_init(); if (err) return err; err = nf_ct_frag6_init(); if (err) goto error_ipfrag_exit; err = ip6_output_init(); if (err) goto error_frag6_exit; return 0; error_frag6_exit: nf_ct_frag6_cleanup(); error_ipfrag_exit: rpl_ipfrag_fini(); return err; } static inline void compat_exit(void) { ip6_output_exit(); nf_ct_frag6_cleanup(); rpl_ipfrag_fini(); } #else static inline int __init compat_init(void) { return 0; } static inline void compat_exit(void) { } #endif #endif /* compat.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/vport.h0000644000000000000000000000013213534540071016755 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.421858144 openvswitch-2.5.9/datapath/vport.h0000644000175000017500000001651213534540071020450 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef VPORT_H #define VPORT_H 1 #include #include #include #include #include #include #include #include #include #include "datapath.h" struct vport; struct vport_parms; /* The following definitions are for users of the vport subsytem: */ int ovs_vport_init(void); void ovs_vport_exit(void); struct vport *ovs_vport_add(const struct vport_parms *); void ovs_vport_del(struct vport *); struct vport *ovs_vport_locate(const struct net *net, const char *name); void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); int ovs_vport_set_options(struct vport *, struct nlattr *options); int ovs_vport_get_options(const struct vport *, struct sk_buff *); int ovs_vport_set_upcall_portids(struct vport *, const struct nlattr *pids); int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *); u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *); int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall, struct net *net, struct sk_buff *, u8 ipproto, __be16 tp_src, __be16 tp_dst); int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall); /** * struct vport_portids - array of netlink portids of a vport. * must be protected by rcu. * @rn_ids: The reciprocal value of @n_ids. * @rcu: RCU callback head for deferred destruction. * @n_ids: Size of @ids array. * @ids: Array storing the Netlink socket pids to be used for packets received * on this port that miss the flow table. */ struct vport_portids { struct reciprocal_value rn_ids; struct rcu_head rcu; u32 n_ids; u32 ids[]; }; /** * struct vport - one port within a datapath * @rcu: RCU callback head for deferred destruction. * @dp: Datapath to which this port belongs. * @upcall_portids: RCU protected 'struct vport_portids'. * @port_no: Index into @dp's @ports array. * @hash_node: Element in @dev_table hash table in vport.c. * @dp_hash_node: Element in @datapath->ports hash table in datapath.c. * @ops: Class structure. * @detach_list: list used for detaching vport in net-exit call. */ struct vport { struct net_device *dev; struct datapath *dp; struct vport_portids __rcu *upcall_portids; u16 port_no; struct hlist_node hash_node; struct hlist_node dp_hash_node; const struct vport_ops *ops; struct list_head detach_list; struct rcu_head rcu; }; /** * struct vport_parms - parameters for creating a new vport * * @name: New vport's name. * @type: New vport's type. * @options: %OVS_VPORT_ATTR_OPTIONS attribute from Netlink message, %NULL if * none was supplied. * @dp: New vport's datapath. * @port_no: New vport's port number. */ struct vport_parms { const char *name; enum ovs_vport_type type; struct nlattr *options; /* For ovs_vport_alloc(). */ struct datapath *dp; u16 port_no; struct nlattr *upcall_portids; }; /** * struct vport_ops - definition of a type of virtual port * * @type: %OVS_VPORT_TYPE_* value for this type of virtual port. * @create: Create a new vport configured as specified. On success returns * a new vport allocated with ovs_vport_alloc(), otherwise an ERR_PTR() value. * @destroy: Destroys a vport. Must call vport_free() on the vport but not * before an RCU grace period has elapsed. * @set_options: Modify the configuration of an existing vport. May be %NULL * if modification is not supported. * @get_options: Appends vport-specific attributes for the configuration of an * existing vport to a &struct sk_buff. May be %NULL for a vport that does not * have any configuration. * @send: Send a packet on the device. * zero for dropped packets or negative for error. * @get_egress_tun_info: Get the egress tunnel 5-tuple and other info for * a packet. */ struct vport_ops { enum ovs_vport_type type; /* Called with ovs_mutex. */ struct vport *(*create)(const struct vport_parms *); void (*destroy)(struct vport *); int (*set_options)(struct vport *, struct nlattr *); int (*get_options)(const struct vport *, struct sk_buff *); int (*get_egress_tun_info)(struct vport *, struct sk_buff *, struct dp_upcall_info *upcall); netdev_tx_t (*send)(struct sk_buff *skb); struct module *owner; struct list_head list; }; struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *, const struct vport_parms *); void ovs_vport_free(struct vport *); void ovs_vport_deferred_free(struct vport *vport); #define VPORT_ALIGN 8 /** * vport_priv - access private data area of vport * * @vport: vport to access * * If a nonzero size was passed in priv_size of vport_alloc() a private data * area was allocated on creation. This allows that area to be accessed and * used for any purpose needed by the vport implementer. */ static inline void *vport_priv(const struct vport *vport) { return (u8 *)(uintptr_t)vport + ALIGN(sizeof(struct vport), VPORT_ALIGN); } /** * vport_from_priv - lookup vport from private data pointer * * @priv: Start of private data area. * * It is sometimes useful to translate from a pointer to the private data * area to the vport, such as in the case where the private data pointer is * the result of a hash table lookup. @priv must point to the start of the * private data area. */ static inline struct vport *vport_from_priv(void *priv) { return (struct vport *)((u8 *)priv - ALIGN(sizeof(struct vport), VPORT_ALIGN)); } int ovs_vport_receive(struct vport *, struct sk_buff *, const struct ip_tunnel_info *); static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, const void *start, unsigned int len) { if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(start, len, 0)); } static inline const char *ovs_vport_name(struct vport *vport) { return vport->dev->name; } int __ovs_vport_ops_register(struct vport_ops *ops); #define ovs_vport_ops_register(ops) \ ({ \ (ops)->owner = THIS_MODULE; \ __ovs_vport_ops_register(ops); \ }) void ovs_vport_ops_unregister(struct vport_ops *ops); static inline struct rtable *ovs_tunnel_route_lookup(struct net *net, const struct ip_tunnel_key *key, u32 mark, struct flowi4 *fl, u8 protocol) { struct rtable *rt; memset(fl, 0, sizeof(*fl)); fl->daddr = key->u.ipv4.dst; fl->saddr = key->u.ipv4.src; fl->flowi4_tos = RT_TOS(key->tos); fl->flowi4_mark = mark; fl->flowi4_proto = protocol; rt = ip_route_output_key(net, fl); return rt; } void ovs_vport_send(struct vport *vport, struct sk_buff *skb); #endif /* vport.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-netdev.c0000644000000000000000000000013213534540071020233 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.525858911 openvswitch-2.5.9/datapath/vport-netdev.c0000644000175000017500000001706613534540071021733 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "gso.h" #include "vport.h" #include "vport-internal_dev.h" #include "vport-netdev.h" static struct vport_ops ovs_netdev_vport_ops; /* Must be called with rcu_read_lock. */ void netdev_port_receive(struct sk_buff *skb, struct ip_tunnel_info *tun_info) { struct vport *vport; vport = ovs_netdev_get_vport(skb->dev); if (unlikely(!vport)) goto error; if (unlikely(skb_warn_if_lro(skb))) goto error; /* Make our own copy of the packet. Otherwise we will mangle the * packet for anyone who came before us (e.g. tcpdump via AF_PACKET). */ skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) return; skb_push(skb, ETH_HLEN); ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN); ovs_vport_receive(vport, skb, tun_info); return; error: kfree_skb(skb); } #ifndef HAVE_METADATA_DST #define port_receive(skb) netdev_port_receive(skb, NULL) #else #define port_receive(skb) netdev_port_receive(skb, skb_tunnel_info(skb)) #endif #if defined HAVE_RX_HANDLER_PSKB /* 2.6.39 and above or backports */ /* Called with rcu_read_lock and bottom-halves disabled. */ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) return RX_HANDLER_PASS; port_receive(skb); return RX_HANDLER_CONSUMED; } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \ defined HAVE_RHEL_OVS_HOOK /* Called with rcu_read_lock and bottom-halves disabled. */ static struct sk_buff *netdev_frame_hook(struct sk_buff *skb) { if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) return skb; port_receive(skb); return NULL; } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) /* * Used as br_handle_frame_hook. (Cannot run bridge at the same time, even on * different set of devices!) */ /* Called with rcu_read_lock and bottom-halves disabled. */ static struct sk_buff *netdev_frame_hook(struct net_bridge_port *p, struct sk_buff *skb) { port_receive(skb); return NULL; } #else #error #endif static struct net_device *get_dpdev(const struct datapath *dp) { struct vport *local; local = ovs_vport_ovsl(dp, OVSP_LOCAL); BUG_ON(!local); return local->dev; } struct vport *ovs_netdev_link(struct vport *vport, const char *name) { int err; vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), name); if (!vport->dev) { err = -ENODEV; goto error_free_vport; } if (vport->dev->flags & IFF_LOOPBACK || vport->dev->type != ARPHRD_ETHER || ovs_is_internal_dev(vport->dev)) { err = -EINVAL; goto error_put; } rtnl_lock(); err = netdev_master_upper_dev_link(vport->dev, get_dpdev(vport->dp)); if (err) goto error_unlock; err = netdev_rx_handler_register(vport->dev, netdev_frame_hook, vport); if (err) goto error_master_upper_dev_unlink; dev_disable_lro(vport->dev); dev_set_promiscuity(vport->dev, 1); vport->dev->priv_flags |= IFF_OVS_DATAPATH; rtnl_unlock(); return vport; error_master_upper_dev_unlink: netdev_upper_dev_unlink(vport->dev, get_dpdev(vport->dp)); error_unlock: rtnl_unlock(); error_put: dev_put(vport->dev); error_free_vport: ovs_vport_free(vport); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ovs_netdev_link); static struct vport *netdev_create(const struct vport_parms *parms) { struct vport *vport; vport = ovs_vport_alloc(0, &ovs_netdev_vport_ops, parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static void vport_netdev_free(struct rcu_head *rcu) { struct vport *vport = container_of(rcu, struct vport, rcu); if (vport->dev) dev_put(vport->dev); ovs_vport_free(vport); } void ovs_netdev_detach_dev(struct vport *vport) { ASSERT_RTNL(); vport->dev->priv_flags &= ~IFF_OVS_DATAPATH; netdev_rx_handler_unregister(vport->dev); netdev_upper_dev_unlink(vport->dev, netdev_master_upper_dev_get(vport->dev)); dev_set_promiscuity(vport->dev, -1); } EXPORT_SYMBOL_GPL(ovs_netdev_detach_dev); static void netdev_destroy(struct vport *vport) { rtnl_lock(); if (vport->dev->priv_flags & IFF_OVS_DATAPATH) ovs_netdev_detach_dev(vport); rtnl_unlock(); call_rcu(&vport->rcu, vport_netdev_free); } void ovs_netdev_tunnel_destroy(struct vport *vport) { rtnl_lock(); if (vport->dev->priv_flags & IFF_OVS_DATAPATH) ovs_netdev_detach_dev(vport); /* We can be invoked by both explicit vport deletion and * underlying netdev deregistration; delete the link only * if it's not already shutting down. */ if (vport->dev->reg_state == NETREG_REGISTERED) rtnl_delete_link(vport->dev); dev_put(vport->dev); vport->dev = NULL; rtnl_unlock(); call_rcu(&vport->rcu, vport_netdev_free); } EXPORT_SYMBOL_GPL(ovs_netdev_tunnel_destroy); /* Returns null if this device is not attached to a datapath. */ struct vport *ovs_netdev_get_vport(struct net_device *dev) { #if defined HAVE_NETDEV_RX_HANDLER_REGISTER || \ defined HAVE_RHEL_OVS_HOOK #ifdef HAVE_OVS_DATAPATH if (likely(dev->priv_flags & IFF_OVS_DATAPATH)) #else if (likely(rcu_access_pointer(dev->rx_handler) == netdev_frame_hook)) #endif #ifdef HAVE_RHEL_OVS_HOOK return (struct vport *)rcu_dereference_rtnl(dev->ax25_ptr); #else #ifdef HAVE_NET_DEVICE_EXTENDED return (struct vport *) rcu_dereference_rtnl(netdev_extended(dev)->rx_handler_data); #else return (struct vport *)rcu_dereference_rtnl(dev->rx_handler_data); #endif #endif else return NULL; #else return (struct vport *)rcu_dereference_rtnl(dev->br_port); #endif } static struct vport_ops ovs_netdev_vport_ops = { .type = OVS_VPORT_TYPE_NETDEV, .create = netdev_create, .destroy = netdev_destroy, .send = dev_queue_xmit, }; int __init ovs_netdev_init(void) { return ovs_vport_ops_register(&ovs_netdev_vport_ops); } void ovs_netdev_exit(void) { ovs_vport_ops_unregister(&ovs_netdev_vport_ops); } #if !defined HAVE_NETDEV_RX_HANDLER_REGISTER && \ !defined HAVE_RHEL_OVS_HOOK /* * Enforces, mutual exclusion with the Linux bridge module, by declaring and * exporting br_should_route_hook. Because the bridge module also exports the * same symbol, the module loader will refuse to load both modules at the same * time (e.g. "bridge: exports duplicate symbol br_should_route_hook (owned by * openvswitch)"). * * Before Linux 2.6.36, Open vSwitch cannot safely coexist with the Linux * bridge module, so openvswitch uses this macro in those versions. In * Linux 2.6.36 and later, Open vSwitch can coexist with the bridge module. * * The use of "typeof" here avoids the need to track changes in the type of * br_should_route_hook over various kernel versions. */ typeof(br_should_route_hook) br_should_route_hook; EXPORT_SYMBOL(br_should_route_hook); #endif openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-internal_dev.h0000644000000000000000000000013213534540071021425 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.425858173 openvswitch-2.5.9/datapath/vport-internal_dev.h0000644000175000017500000000204213534540071023111 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2011 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef VPORT_INTERNAL_DEV_H #define VPORT_INTERNAL_DEV_H 1 #include "datapath.h" #include "vport.h" int ovs_is_internal_dev(const struct net_device *); struct vport *ovs_internal_dev_get_vport(struct net_device *); int ovs_internal_dev_rtnl_link_register(void); void ovs_internal_dev_rtnl_link_unregister(void); #endif /* vport-internal_dev.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-vxlan.c0000644000000000000000000000013213534540071020076 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.561859176 openvswitch-2.5.9/datapath/vport-vxlan.c0000644000175000017500000001126513534540071021571 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2013 Cisco Systems, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport.h" #include "vport-netdev.h" static struct vport_ops ovs_vxlan_netdev_vport_ops; static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb) { struct vxlan_dev *vxlan = netdev_priv(vport->dev); __be16 dst_port = vxlan->cfg.dst_port; if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port))) return -EMSGSIZE; if (vxlan->flags & VXLAN_F_GBP) { struct nlattr *exts; exts = nla_nest_start(skb, OVS_TUNNEL_ATTR_EXTENSION); if (!exts) return -EMSGSIZE; if (vxlan->flags & VXLAN_F_GBP && nla_put_flag(skb, OVS_VXLAN_EXT_GBP)) return -EMSGSIZE; nla_nest_end(skb, exts); } return 0; } static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = { [OVS_VXLAN_EXT_GBP] = { .type = NLA_FLAG, }, }; static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr, struct vxlan_config *conf) { struct nlattr *exts[OVS_VXLAN_EXT_MAX + 1]; int err; if (nla_len(attr) < sizeof(struct nlattr)) return -EINVAL; err = nla_parse_nested(exts, OVS_VXLAN_EXT_MAX, attr, exts_policy); if (err < 0) return err; if (exts[OVS_VXLAN_EXT_GBP]) conf->flags |= VXLAN_F_GBP; return 0; } static struct vport *vxlan_tnl_create(const struct vport_parms *parms) { struct net *net = ovs_dp_get_net(parms->dp); struct nlattr *options = parms->options; struct net_device *dev; struct vport *vport; struct nlattr *a; int err; struct vxlan_config conf = { .no_share = true, .flags = VXLAN_F_COLLECT_METADATA, /* Don't restrict the packets that can be sent by MTU */ .mtu = IP_MAX_MTU, }; if (!options) { err = -EINVAL; goto error; } a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); if (a && nla_len(a) == sizeof(u16)) { conf.dst_port = htons(nla_get_u16(a)); } else { /* Require destination port from userspace. */ err = -EINVAL; goto error; } vport = ovs_vport_alloc(0, &ovs_vxlan_netdev_vport_ops, parms); if (IS_ERR(vport)) return vport; a = nla_find_nested(options, OVS_TUNNEL_ATTR_EXTENSION); if (a) { err = vxlan_configure_exts(vport, a, &conf); if (err) { ovs_vport_free(vport); goto error; } } rtnl_lock(); dev = vxlan_dev_create(net, parms->name, NET_NAME_USER, &conf); if (IS_ERR(dev)) { rtnl_unlock(); ovs_vport_free(vport); return ERR_CAST(dev); } dev_change_flags(dev, dev->flags | IFF_UP); rtnl_unlock(); return vport; error: return ERR_PTR(err); } static struct vport *vxlan_create(const struct vport_parms *parms) { struct vport *vport; vport = vxlan_tnl_create(parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { struct vxlan_dev *vxlan = netdev_priv(vport->dev); struct net *net = ovs_dp_get_net(vport->dp); __be16 dst_port = vxlan_dev_dst_port(vxlan); __be16 src_port; int port_min; int port_max; inet_get_local_port_range(net, &port_min, &port_max); src_port = udp_flow_src_port(net, skb, 0, 0, true); return ovs_tunnel_get_egress_info(upcall, net, skb, IPPROTO_UDP, src_port, dst_port); } static struct vport_ops ovs_vxlan_netdev_vport_ops = { .type = OVS_VPORT_TYPE_VXLAN, .create = vxlan_create, .destroy = ovs_netdev_tunnel_destroy, .get_options = vxlan_get_options, .send = vxlan_xmit, .get_egress_tun_info = vxlan_get_egress_tun_info, }; static int __init ovs_vxlan_tnl_init(void) { return ovs_vport_ops_register(&ovs_vxlan_netdev_vport_ops); } static void __exit ovs_vxlan_tnl_exit(void) { ovs_vport_ops_unregister(&ovs_vxlan_netdev_vport_ops); } module_init(ovs_vxlan_tnl_init); module_exit(ovs_vxlan_tnl_exit); MODULE_DESCRIPTION("OVS: VXLAN switching port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("vport-type-4"); openvswitch-2.5.9/datapath/PaxHeaders.82075/dp_notify.c0000644000000000000000000000013213534540071017571 xustar0030 mtime=1567801401.245680025 30 atime=1567801402.053685959 30 ctime=1567801425.517858851 openvswitch-2.5.9/datapath/dp_notify.c0000644000175000017500000000530413534540071021261 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2012 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include "datapath.h" #include "vport-internal_dev.h" #include "vport-netdev.h" static void dp_detach_port_notify(struct vport *vport) { struct sk_buff *notify; struct datapath *dp; dp = vport->dp; notify = ovs_vport_cmd_build_info(vport, 0, 0, OVS_VPORT_CMD_DEL); ovs_dp_detach_port(vport); if (IS_ERR(notify)) { genl_set_err(&dp_vport_genl_family, ovs_dp_get_net(dp), 0, GROUP_ID(&ovs_dp_vport_multicast_group), PTR_ERR(notify)); return; } genlmsg_multicast_netns(&dp_vport_genl_family, ovs_dp_get_net(dp), notify, 0, GROUP_ID(&ovs_dp_vport_multicast_group), GFP_KERNEL); } void ovs_dp_notify_wq(struct work_struct *work) { struct ovs_net *ovs_net = container_of(work, struct ovs_net, dp_notify_work); struct datapath *dp; ovs_lock(); list_for_each_entry(dp, &ovs_net->dps, list_node) { int i; for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { struct vport *vport; struct hlist_node *n; hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) { if (vport->ops->type == OVS_VPORT_TYPE_INTERNAL) continue; if (!(vport->dev->priv_flags & IFF_OVS_DATAPATH)) dp_detach_port_notify(vport); } } } ovs_unlock(); } static int dp_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct ovs_net *ovs_net; struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vport *vport = NULL; if (!ovs_is_internal_dev(dev)) vport = ovs_netdev_get_vport(dev); if (!vport) return NOTIFY_DONE; if (event == NETDEV_UNREGISTER) { /* upper_dev_unlink and decrement promisc immediately */ ovs_netdev_detach_dev(vport); /* schedule vport destroy, dev_put and genl notification */ ovs_net = net_generic(dev_net(dev), ovs_net_id); queue_work(system_wq, &ovs_net->dp_notify_work); } return NOTIFY_DONE; } struct notifier_block ovs_dp_device_notifier = { .notifier_call = dp_device_event }; openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-netdev.h0000644000000000000000000000013213534540071020240 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.425858173 openvswitch-2.5.9/datapath/vport-netdev.h0000644000175000017500000000242513534540071021731 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef VPORT_NETDEV_H #define VPORT_NETDEV_H 1 #include #include #include "vport.h" struct vport *ovs_netdev_get_vport(struct net_device *dev); struct vport *ovs_netdev_link(struct vport *vport, const char *name); void ovs_netdev_send(struct vport *vport, struct sk_buff *skb); void ovs_netdev_detach_dev(struct vport *); int __init ovs_netdev_init(void); void ovs_netdev_exit(void); void ovs_netdev_tunnel_destroy(struct vport *vport); void netdev_port_receive(struct sk_buff *skb, struct ip_tunnel_info *tun_info); #endif /* vport_netdev.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/flow_netlink.h0000644000000000000000000000013213534540071020276 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.417858114 openvswitch-2.5.9/datapath/flow_netlink.h0000644000175000017500000000536513534540071021775 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef FLOW_NETLINK_H #define FLOW_NETLINK_H 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "flow.h" size_t ovs_tun_key_attr_size(void); size_t ovs_key_attr_size(void); void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key, struct sw_flow_mask *mask); int ovs_nla_put_key(const struct sw_flow_key *, const struct sw_flow_key *, int attr, bool is_mask, struct sk_buff *); int ovs_nla_get_flow_metadata(struct net *, const struct nlattr *, struct sw_flow_key *, bool log); int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb); int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb); int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb); int ovs_nla_get_match(struct net *, struct sw_flow_match *, const struct nlattr *key, const struct nlattr *mask, bool log); int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb, const struct ip_tunnel_info *egress_tun_info, const void *egress_tun_opts); bool ovs_nla_get_ufid(struct sw_flow_id *, const struct nlattr *, bool log); int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid, const struct sw_flow_key *key, bool log); u32 ovs_nla_get_ufid_flags(const struct nlattr *attr); int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, struct sw_flow_actions **sfa, bool log); int ovs_nla_add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len, bool log); int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb); void ovs_nla_free_flow_actions(struct sw_flow_actions *); void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *); #endif /* flow_netlink.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/vlan.h0000644000000000000000000000013213534540071016543 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.057685987 30 ctime=1567801425.421858144 openvswitch-2.5.9/datapath/vlan.h0000644000175000017500000000427013534540071020234 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2011 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef VLAN_H #define VLAN_H 1 #include #include #include /** * DOC: VLAN tag manipulation. * * &struct sk_buff handling of VLAN tags has evolved over time: * * In 2.6.26 and earlier, VLAN tags did not have any generic representation in * an skb, other than as a raw 802.1Q header inside the packet data. * * In 2.6.27 &struct sk_buff added a @vlan_tci member. Between 2.6.27 and * 2.6.32, its value was the raw contents of the 802.1Q TCI field, or zero if * no 802.1Q header was present. This worked OK except for the corner case of * an 802.1Q header with an all-0-bits TCI, which could not be represented. * * In 2.6.33, @vlan_tci semantics changed. Now, if an 802.1Q header is * present, then the VLAN_TAG_PRESENT bit is always set. This fixes the * all-0-bits TCI corner case. * * For compatibility we emulate the 2.6.33+ behavior on earlier kernel * versions. The client must not access @vlan_tci directly. Instead, use * vlan_get_tci() to read it or vlan_set_tci() to write it, with semantics * equivalent to those on 2.6.33+. */ static inline u16 vlan_get_tci(struct sk_buff *skb) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) if (skb->vlan_tci) return skb->vlan_tci | VLAN_TAG_PRESENT; #endif return skb->vlan_tci; } static inline void vlan_set_tci(struct sk_buff *skb, u16 vlan_tci) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) vlan_tci &= ~VLAN_TAG_PRESENT; #endif skb->vlan_tci = vlan_tci; } #endif /* vlan.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/Makefile.in0000644000000000000000000000013213534540101017471 xustar0030 mtime=1567801409.453740437 30 atime=1567801415.789787139 30 ctime=1567801425.409858056 openvswitch-2.5.9/datapath/Makefile.in0000644000175000017500000006612713534540101021173 0ustar00jpettitjpettit00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @LINUX_ENABLED_TRUE@am__append_1 = linux subdir = datapath ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_func_posix_memalign.m4 \ $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/openvswitch.m4 $(top_srcdir)/m4/compat.at \ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = linux am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Modules.mk \ $(srcdir)/linux/Modules.mk DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOM4TE = @AUTOM4TE@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CAPNG_LDADD = @CAPNG_LDADD@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CGCCFLAGS = @CGCCFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBDIR = @DBDIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DPDK_vswitchd_LDFLAGS = @DPDK_vswitchd_LDFLAGS@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ HAVE_LIBCAPNG = @HAVE_LIBCAPNG@ HAVE_OPENSSL = @HAVE_OPENSSL@ HAVE_PYTHON = @HAVE_PYTHON@ INCLUDE_NEXT = @INCLUDE_NEXT@ INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KARCH = @KARCH@ KBUILD = @KBUILD@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LOGDIR = @LOGDIR@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSVC64_LDFLAGS = @MSVC64_LDFLAGS@ MSVC_CFLAGS = @MSVC_CFLAGS@ NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ NEXT_STDIO_H = @NEXT_STDIO_H@ NEXT_STRING_H = @NEXT_STRING_H@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ OVS_CFLAGS = @OVS_CFLAGS@ OVS_LDFLAGS = @OVS_LDFLAGS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKIDIR = @PKIDIR@ PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ PTHREAD_INCLUDES = @PTHREAD_INCLUDES@ PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PTHREAD_WIN32_DIR_DLL = @PTHREAD_WIN32_DIR_DLL@ PTHREAD_WIN32_DIR_DLL_WIN_FORM = @PTHREAD_WIN32_DIR_DLL_WIN_FORM@ PYTHON = @PYTHON@ RANLIB = @RANLIB@ RUNDIR = @RUNDIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SPARSE = @SPARSE@ SPARSEFLAGS = @SPARSEFLAGS@ SPARSE_EXTRA_INCLUDES = @SPARSE_EXTRA_INCLUDES@ SSL_INCLUDES = @SSL_INCLUDES@ SSL_LDFLAGS = @SSL_LDFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VERSION = @VERSION@ VSTUDIO_CONFIG = @VSTUDIO_CONFIG@ WARNING_FLAGS = @WARNING_FLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ SUBDIRS = $(am__append_1) EXTRA_DIST = $(dist_headers) $(dist_sources) $(dist_extras) \ linux/compat/build-aux/export-check-whitelist # Suppress warnings about GNU extensions in Modules.mk files. AUTOMAKE_OPTIONS = -Wno-portability # Some modules should be built and distributed, e.g. openvswitch. # # Some modules should be built but not distributed, e.g. third-party # hwtable modules. build_multi_modules = \ openvswitch both_modules = \ $(build_multi_modules) \ vport_geneve \ vport_gre \ vport_lisp \ vport_stt \ vport_vxlan # When changing the name of 'build_modules', please also update the # print-build-modules in Makefile.am. build_modules = $(both_modules) # Modules to build dist_modules = $(both_modules) # Modules to distribute openvswitch_sources = actions.c conntrack.c datapath.c dp_notify.c \ flow.c flow_netlink.c flow_table.c vport.c \ vport-internal_dev.c vport-netdev.c \ linux/compat/dev-openvswitch.c linux/compat/exthdrs_core.c \ linux/compat/flex_array.c linux/compat/flow_dissector.c \ linux/compat/geneve.c linux/compat/gre.c linux/compat/gso.c \ linux/compat/genetlink-openvswitch.c \ linux/compat/inet_fragment.c linux/compat/ip_gre.c \ linux/compat/ip_fragment.c linux/compat/ip_output.c \ linux/compat/ip_tunnel.c linux/compat/ip_tunnels_core.c \ linux/compat/ip6_output.c linux/compat/lisp.c \ linux/compat/netdevice.c linux/compat/net_namespace.c \ linux/compat/nf_conntrack_core.c \ linux/compat/nf_conntrack_reasm.c linux/compat/reassembly.c \ linux/compat/reciprocal_div.c \ linux/compat/skbuff-openvswitch.c linux/compat/socket.c \ linux/compat/stt.c linux/compat/udp.c \ linux/compat/udp_tunnel.c linux/compat/vxlan.c \ linux/compat/utils.c vport_geneve_sources = vport-geneve.c vport_vxlan_sources = vport-vxlan.c vport_gre_sources = vport-gre.c vport_lisp_sources = vport-lisp.c vport_stt_sources = vport-stt.c openvswitch_headers = compat.h conntrack.h datapath.h flow.h \ flow_netlink.h flow_table.h vlan.h vport.h \ vport-internal_dev.h vport-netdev.h linux/compat/gso.h \ linux/compat/include/linux/percpu.h \ linux/compat/include/linux/bug.h \ linux/compat/include/linux/compiler.h \ linux/compat/include/linux/compiler-gcc.h \ linux/compat/include/linux/cpumask.h \ linux/compat/include/linux/err.h \ linux/compat/include/linux/etherdevice.h \ linux/compat/include/linux/flex_array.h \ linux/compat/include/linux/icmp.h \ linux/compat/include/linux/icmpv6.h \ linux/compat/include/linux/if.h \ linux/compat/include/linux/if_arp.h \ linux/compat/include/linux/if_ether.h \ linux/compat/include/linux/if_link.h \ linux/compat/include/linux/if_vlan.h \ linux/compat/include/linux/in.h \ linux/compat/include/linux/ip.h \ linux/compat/include/linux/ipv6.h \ linux/compat/include/linux/jiffies.h \ linux/compat/include/linux/kconfig.h \ linux/compat/include/linux/kernel.h \ linux/compat/include/net/lisp.h \ linux/compat/include/linux/list.h \ linux/compat/include/linux/mpls.h \ linux/compat/include/linux/net.h \ linux/compat/include/linux/random.h \ linux/compat/include/linux/netdevice.h \ linux/compat/include/linux/netdev_features.h \ linux/compat/include/linux/netfilter_ipv6.h \ linux/compat/include/linux/netlink.h \ linux/compat/include/linux/openvswitch.h \ linux/compat/include/linux/poison.h \ linux/compat/include/linux/rculist.h \ linux/compat/include/linux/rcupdate.h \ linux/compat/include/linux/reciprocal_div.h \ linux/compat/include/linux/rtnetlink.h \ linux/compat/include/linux/sctp.h \ linux/compat/include/linux/skbuff.h \ linux/compat/include/linux/stddef.h \ linux/compat/include/linux/tcp.h \ linux/compat/include/linux/types.h \ linux/compat/include/linux/u64_stats_sync.h \ linux/compat/include/linux/udp.h \ linux/compat/include/linux/workqueue.h \ linux/compat/include/net/checksum.h \ linux/compat/include/net/dst.h \ linux/compat/include/net/dst_metadata.h \ linux/compat/include/net/flow_keys.h \ linux/compat/include/net/genetlink.h \ linux/compat/include/net/geneve.h \ linux/compat/include/net/gre.h \ linux/compat/include/net/inet_ecn.h \ linux/compat/include/net/inet_frag.h \ linux/compat/include/net/inetpeer.h \ linux/compat/include/net/ip.h \ linux/compat/include/net/ip_tunnels.h \ linux/compat/include/net/ip6_route.h \ linux/compat/include/net/ip6_tunnel.h \ linux/compat/include/net/ipv6.h \ linux/compat/include/net/mpls.h \ linux/compat/include/net/net_namespace.h \ linux/compat/include/net/netlink.h \ linux/compat/include/net/route.h \ linux/compat/include/net/rtnetlink.h \ linux/compat/include/net/udp.h \ linux/compat/include/net/udp_tunnel.h \ linux/compat/include/net/sock.h linux/compat/include/net/stt.h \ linux/compat/include/net/vrf.h \ linux/compat/include/net/vxlan.h \ linux/compat/include/net/netfilter/nf_conntrack_core.h \ linux/compat/include/net/netfilter/nf_conntrack_expect.h \ linux/compat/include/net/netfilter/nf_conntrack_labels.h \ linux/compat/include/net/netfilter/nf_conntrack_zones.h \ linux/compat/include/net/netfilter/ipv6/nf_defrag_ipv6.h \ linux/compat/include/net/sctp/checksum.h openvswitch_extras = \ README.md dist_sources = $(foreach module,$(dist_modules),$($(module)_sources)) dist_headers = $(foreach module,$(dist_modules),$($(module)_headers)) dist_extras = $(foreach module,$(dist_modules),$($(module)_extras)) build_sources = $(foreach module,$(build_modules),$($(module)_sources)) build_headers = $(foreach module,$(build_modules),$($(module)_headers)) build_links = $(notdir $(build_sources)) build_objects = $(notdir $(patsubst %.c,%.o,$(build_sources))) CLEANFILES = distfiles COMPAT_GET_FUNCTIONS := find $(top_srcdir)/datapath/linux/compat -name "*.h" \ -exec sed -n '/^[a-z][a-z]* \*\?[A-Za-z0-9_][A-Za-z0-9_]*([a-z]/p; /^struct [a-z0-9_][a-z0-9_]* \*\?[A-Za-z0-9_][A-Za-z0-9_]*([a-z]/p' {} \; | tr -d '*' | cut -d '(' -f1 | rev | cut -d ' ' -f1 | rev COMPAT_GET_EXPORTS := find $(top_srcdir)/datapath/linux/compat -name "*.c" \ -exec sed -n 's/^EXPORT_SYMBOL[A-Z_]*(\([a-z_][a-z_]*\));$$/\1/p' {} \; COMPAT_FUNCTIONS := $(shell $(COMPAT_GET_FUNCTIONS)) COMPAT_EXPORTS := $(shell $(COMPAT_GET_EXPORTS)) all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Modules.mk $(srcdir)/linux/Modules.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu datapath/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu datapath/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(srcdir)/Modules.mk $(srcdir)/linux/Modules.mk $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile all-local installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \ check check-am clean clean-generic clean-libtool cscopelist-am \ ctags ctags-am distclean distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # The following is based on commands for the Automake "distdir" target. distfiles: Makefile @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | sort -u > $@ # Print name of all modules. print-build-modules: @if test -z "$(build_modules)"; \ then \ echo "Could not find any kernel module."; \ exit 1; \ fi @echo "$(build_modules)" | tr '_' '-'; # Checks that all public functions are 'rpl_' or 'ovs_' prefixed. # Checks that all EXPORT_SYMBOL_GPL() export 'rpl_' or 'ovs_' prefixed functions. check-export-symbol: @for fun_ in $(COMPAT_FUNCTIONS); do \ if ! grep -- $${fun_} $(top_srcdir)/datapath/linux/compat/build-aux/export-check-whitelist > /dev/null; then \ if ! echo $${fun_} | grep -q -E '^(rpl|ovs)_'; then \ echo "error: $${fun_}() needs to be prefixed with 'rpl_' or 'ovs_'."; \ exit 1; \ fi; \ fi; \ done @for fun_ in $(COMPAT_EXPORTS); do \ if ! echo $${fun_} | grep -q -E '^(rpl|ovs)_'; then \ echo "error: $${fun_}() needs to be prefixed with 'rpl_' or 'ovs_'."; \ exit 1; \ fi; \ done all-local: check-export-symbol # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-gre.c0000644000000000000000000000013213534540071017523 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.557859146 openvswitch-2.5.9/datapath/vport-gre.c0000644000175000017500000000561513534540071021220 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport.h" #include "vport-netdev.h" static struct vport_ops ovs_gre_vport_ops; static struct vport *gre_tnl_create(const struct vport_parms *parms) { struct net *net = ovs_dp_get_net(parms->dp); struct net_device *dev; struct vport *vport; vport = ovs_vport_alloc(0, &ovs_gre_vport_ops, parms); if (IS_ERR(vport)) return vport; rtnl_lock(); dev = gretap_fb_dev_create(net, parms->name, NET_NAME_USER); if (IS_ERR(dev)) { rtnl_unlock(); ovs_vport_free(vport); return ERR_CAST(dev); } dev_change_flags(dev, dev->flags | IFF_UP); rtnl_unlock(); return vport; } static struct vport *gre_create(const struct vport_parms *parms) { struct vport *vport; vport = gre_tnl_create(parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), skb, IPPROTO_GRE, 0, 0); } static struct vport_ops ovs_gre_vport_ops = { .type = OVS_VPORT_TYPE_GRE, .create = gre_create, .send = gre_fb_xmit, .get_egress_tun_info = gre_get_egress_tun_info, .destroy = ovs_netdev_tunnel_destroy, }; static int __init ovs_gre_tnl_init(void) { return ovs_vport_ops_register(&ovs_gre_vport_ops); } static void __exit ovs_gre_tnl_exit(void) { ovs_vport_ops_unregister(&ovs_gre_vport_ops); } module_init(ovs_gre_tnl_init); module_exit(ovs_gre_tnl_exit); MODULE_DESCRIPTION("OVS: GRE switching port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("vport-type-3"); openvswitch-2.5.9/datapath/PaxHeaders.82075/actions.c0000644000000000000000000000013213534540071017236 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/actions.c0000644000175000017500000007420013534540071020727 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "conntrack.h" #include "gso.h" #include "vlan.h" #include "vport.h" static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, int len); struct deferred_action { struct sk_buff *skb; const struct nlattr *actions; /* Store pkt_key clone when creating deferred action. */ struct sw_flow_key pkt_key; }; #define MAX_L2_LEN (VLAN_ETH_HLEN + 3 * MPLS_HLEN) struct ovs_frag_data { unsigned long dst; struct vport *vport; struct ovs_gso_cb cb; __be16 inner_protocol; __u16 vlan_tci; __be16 vlan_proto; unsigned int l2_len; u8 l2_data[MAX_L2_LEN]; }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) static DEFINE_PER_CPU(struct ovs_frag_data, ovs_frag_data_storage); #endif #define DEFERRED_ACTION_FIFO_SIZE 10 struct action_fifo { int head; int tail; /* Deferred action fifo queue storage. */ struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE]; }; static struct action_fifo __percpu *action_fifos; #define EXEC_ACTIONS_LEVEL_LIMIT 4 /* limit used to detect packet * looping by the network stack */ static DEFINE_PER_CPU(int, exec_actions_level); static void action_fifo_init(struct action_fifo *fifo) { fifo->head = 0; fifo->tail = 0; } static bool action_fifo_is_empty(const struct action_fifo *fifo) { return (fifo->head == fifo->tail); } static struct deferred_action *action_fifo_get(struct action_fifo *fifo) { if (action_fifo_is_empty(fifo)) return NULL; return &fifo->fifo[fifo->tail++]; } static struct deferred_action *action_fifo_put(struct action_fifo *fifo) { if (fifo->head >= DEFERRED_ACTION_FIFO_SIZE - 1) return NULL; return &fifo->fifo[fifo->head++]; } /* Return queue entry if fifo is not full */ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, const struct sw_flow_key *key, const struct nlattr *attr) { struct action_fifo *fifo; struct deferred_action *da; fifo = this_cpu_ptr(action_fifos); da = action_fifo_put(fifo); if (da) { da->skb = skb; da->actions = attr; da->pkt_key = *key; } return da; } static void invalidate_flow_key(struct sw_flow_key *key) { key->eth.type = htons(0); } static bool is_flow_key_valid(const struct sw_flow_key *key) { return !!key->eth.type; } static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_action_push_mpls *mpls) { __be32 *new_mpls_lse; struct ethhdr *hdr; /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */ if (skb_encapsulation(skb)) return -ENOTSUPP; if (skb_cow_head(skb, MPLS_HLEN) < 0) return -ENOMEM; skb_push(skb, MPLS_HLEN); memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), skb->mac_len); skb_reset_mac_header(skb); new_mpls_lse = (__be32 *)skb_mpls_header(skb); *new_mpls_lse = mpls->mpls_lse; if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse, MPLS_HLEN, 0)); hdr = eth_hdr(skb); hdr->h_proto = mpls->mpls_ethertype; if (!ovs_skb_get_inner_protocol(skb)) ovs_skb_set_inner_protocol(skb, skb->protocol); skb->protocol = mpls->mpls_ethertype; invalidate_flow_key(key); return 0; } static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, const __be16 ethertype) { struct ethhdr *hdr; int err; err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); if (unlikely(err)) return err; skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN); memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb), skb->mac_len); __skb_pull(skb, MPLS_HLEN); skb_reset_mac_header(skb); /* skb_mpls_header() is used to locate the ethertype * field correctly in the presence of VLAN tags. */ hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); hdr->h_proto = ethertype; if (eth_p_mpls(skb->protocol)) skb->protocol = ethertype; invalidate_flow_key(key); return 0; } static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key, const __be32 *mpls_lse, const __be32 *mask) { __be32 *stack; __be32 lse; int err; err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); if (unlikely(err)) return err; stack = (__be32 *)skb_mpls_header(skb); lse = OVS_MASKED(*stack, *mpls_lse, *mask); if (skb->ip_summed == CHECKSUM_COMPLETE) { __be32 diff[] = { ~(*stack), lse }; skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum); } *stack = lse; flow_key->mpls.top_lse = lse; return 0; } static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) { int err; err = skb_vlan_pop(skb); if (skb_vlan_tag_present(skb)) invalidate_flow_key(key); else key->eth.tci = 0; return err; } static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_action_push_vlan *vlan) { if (skb_vlan_tag_present(skb)) invalidate_flow_key(key); else key->eth.tci = vlan->vlan_tci; return skb_vlan_push(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); } /* 'src' is already properly masked. */ static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_) { u16 *dst = (u16 *)dst_; const u16 *src = (const u16 *)src_; const u16 *mask = (const u16 *)mask_; OVS_SET_MASKED(dst[0], src[0], mask[0]); OVS_SET_MASKED(dst[1], src[1], mask[1]); OVS_SET_MASKED(dst[2], src[2], mask[2]); } static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_ethernet *key, const struct ovs_key_ethernet *mask) { int err; err = skb_ensure_writable(skb, ETH_HLEN); if (unlikely(err)) return err; skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); ether_addr_copy_masked(eth_hdr(skb)->h_source, key->eth_src, mask->eth_src); ether_addr_copy_masked(eth_hdr(skb)->h_dest, key->eth_dst, mask->eth_dst); ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); ether_addr_copy(flow_key->eth.src, eth_hdr(skb)->h_source); ether_addr_copy(flow_key->eth.dst, eth_hdr(skb)->h_dest); return 0; } static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh, __be32 addr, __be32 new_addr) { int transport_len = skb->len - skb_transport_offset(skb); if (nh->frag_off & htons(IP_OFFSET)) return; if (nh->protocol == IPPROTO_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, addr, new_addr, 1); } else if (nh->protocol == IPPROTO_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) { struct udphdr *uh = udp_hdr(skb); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { inet_proto_csum_replace4(&uh->check, skb, addr, new_addr, 1); if (!uh->check) uh->check = CSUM_MANGLED_0; } } } } static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, __be32 *addr, __be32 new_addr) { update_ip_l4_checksum(skb, nh, *addr, new_addr); csum_replace4(&nh->check, *addr, new_addr); skb_clear_hash(skb); *addr = new_addr; } static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, __be32 addr[4], const __be32 new_addr[4]) { int transport_len = skb->len - skb_transport_offset(skb); if (l4_proto == NEXTHDR_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, addr, new_addr, 1); } else if (l4_proto == NEXTHDR_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) { struct udphdr *uh = udp_hdr(skb); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { inet_proto_csum_replace16(&uh->check, skb, addr, new_addr, 1); if (!uh->check) uh->check = CSUM_MANGLED_0; } } } else if (l4_proto == NEXTHDR_ICMP) { if (likely(transport_len >= sizeof(struct icmp6hdr))) inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum, skb, addr, new_addr, 1); } } static void mask_ipv6_addr(const __be32 old[4], const __be32 addr[4], const __be32 mask[4], __be32 masked[4]) { masked[0] = OVS_MASKED(old[0], addr[0], mask[0]); masked[1] = OVS_MASKED(old[1], addr[1], mask[1]); masked[2] = OVS_MASKED(old[2], addr[2], mask[2]); masked[3] = OVS_MASKED(old[3], addr[3], mask[3]); } static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, __be32 addr[4], const __be32 new_addr[4], bool recalculate_csum) { if (likely(recalculate_csum)) update_ipv6_checksum(skb, l4_proto, addr, new_addr); skb_clear_hash(skb); memcpy(addr, new_addr, sizeof(__be32[4])); } static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl, u32 mask) { /* Bits 21-24 are always unmasked, so this retains their values. */ OVS_SET_MASKED(nh->flow_lbl[0], (u8)(fl >> 16), (u8)(mask >> 16)); OVS_SET_MASKED(nh->flow_lbl[1], (u8)(fl >> 8), (u8)(mask >> 8)); OVS_SET_MASKED(nh->flow_lbl[2], (u8)fl, (u8)mask); } static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl, u8 mask) { new_ttl = OVS_MASKED(nh->ttl, new_ttl, mask); csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); nh->ttl = new_ttl; } static int set_ipv4(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_ipv4 *key, const struct ovs_key_ipv4 *mask) { struct iphdr *nh; __be32 new_addr; int err; err = skb_ensure_writable(skb, skb_network_offset(skb) + sizeof(struct iphdr)); if (unlikely(err)) return err; nh = ip_hdr(skb); /* Setting an IP addresses is typically only a side effect of * matching on them in the current userspace implementation, so it * makes sense to check if the value actually changed. */ if (mask->ipv4_src) { new_addr = OVS_MASKED(nh->saddr, key->ipv4_src, mask->ipv4_src); if (unlikely(new_addr != nh->saddr)) { set_ip_addr(skb, nh, &nh->saddr, new_addr); flow_key->ipv4.addr.src = new_addr; } } if (mask->ipv4_dst) { new_addr = OVS_MASKED(nh->daddr, key->ipv4_dst, mask->ipv4_dst); if (unlikely(new_addr != nh->daddr)) { set_ip_addr(skb, nh, &nh->daddr, new_addr); flow_key->ipv4.addr.dst = new_addr; } } if (mask->ipv4_tos) { ipv4_change_dsfield(nh, ~mask->ipv4_tos, key->ipv4_tos); flow_key->ip.tos = nh->tos; } if (mask->ipv4_ttl) { set_ip_ttl(skb, nh, key->ipv4_ttl, mask->ipv4_ttl); flow_key->ip.ttl = nh->ttl; } return 0; } static bool is_ipv6_mask_nonzero(const __be32 addr[4]) { return !!(addr[0] | addr[1] | addr[2] | addr[3]); } static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_ipv6 *key, const struct ovs_key_ipv6 *mask) { struct ipv6hdr *nh; int err; err = skb_ensure_writable(skb, skb_network_offset(skb) + sizeof(struct ipv6hdr)); if (unlikely(err)) return err; nh = ipv6_hdr(skb); /* Setting an IP addresses is typically only a side effect of * matching on them in the current userspace implementation, so it * makes sense to check if the value actually changed. */ if (is_ipv6_mask_nonzero(mask->ipv6_src)) { __be32 *saddr = (__be32 *)&nh->saddr; __be32 masked[4]; mask_ipv6_addr(saddr, key->ipv6_src, mask->ipv6_src, masked); if (unlikely(memcmp(saddr, masked, sizeof(masked)))) { set_ipv6_addr(skb, flow_key->ip.proto, saddr, masked, true); memcpy(&flow_key->ipv6.addr.src, masked, sizeof(flow_key->ipv6.addr.src)); } } if (is_ipv6_mask_nonzero(mask->ipv6_dst)) { unsigned int offset = 0; int flags = IP6_FH_F_SKIP_RH; bool recalc_csum = true; __be32 *daddr = (__be32 *)&nh->daddr; __be32 masked[4]; mask_ipv6_addr(daddr, key->ipv6_dst, mask->ipv6_dst, masked); if (unlikely(memcmp(daddr, masked, sizeof(masked)))) { if (ipv6_ext_hdr(nh->nexthdr)) recalc_csum = (ipv6_find_hdr(skb, &offset, NEXTHDR_ROUTING, NULL, &flags) != NEXTHDR_ROUTING); set_ipv6_addr(skb, flow_key->ip.proto, daddr, masked, recalc_csum); memcpy(&flow_key->ipv6.addr.dst, masked, sizeof(flow_key->ipv6.addr.dst)); } } if (mask->ipv6_tclass) { ipv6_change_dsfield(nh, ~mask->ipv6_tclass, key->ipv6_tclass); flow_key->ip.tos = ipv6_get_dsfield(nh); } if (mask->ipv6_label) { set_ipv6_fl(nh, ntohl(key->ipv6_label), ntohl(mask->ipv6_label)); flow_key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL); } if (mask->ipv6_hlimit) { OVS_SET_MASKED(nh->hop_limit, key->ipv6_hlimit, mask->ipv6_hlimit); flow_key->ip.ttl = nh->hop_limit; } return 0; } /* Must follow skb_ensure_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) { inet_proto_csum_replace2(check, skb, *port, new_port, 0); *port = new_port; } static int set_udp(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_udp *key, const struct ovs_key_udp *mask) { struct udphdr *uh; __be16 src, dst; int err; err = skb_ensure_writable(skb, skb_transport_offset(skb) + sizeof(struct udphdr)); if (unlikely(err)) return err; uh = udp_hdr(skb); /* Either of the masks is non-zero, so do not bother checking them. */ src = OVS_MASKED(uh->source, key->udp_src, mask->udp_src); dst = OVS_MASKED(uh->dest, key->udp_dst, mask->udp_dst); if (uh->check && skb->ip_summed != CHECKSUM_PARTIAL) { if (likely(src != uh->source)) { set_tp_port(skb, &uh->source, src, &uh->check); flow_key->tp.src = src; } if (likely(dst != uh->dest)) { set_tp_port(skb, &uh->dest, dst, &uh->check); flow_key->tp.dst = dst; } if (unlikely(!uh->check)) uh->check = CSUM_MANGLED_0; } else { uh->source = src; uh->dest = dst; flow_key->tp.src = src; flow_key->tp.dst = dst; } skb_clear_hash(skb); return 0; } static int set_tcp(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_tcp *key, const struct ovs_key_tcp *mask) { struct tcphdr *th; __be16 src, dst; int err; err = skb_ensure_writable(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)); if (unlikely(err)) return err; th = tcp_hdr(skb); src = OVS_MASKED(th->source, key->tcp_src, mask->tcp_src); if (likely(src != th->source)) { set_tp_port(skb, &th->source, src, &th->check); flow_key->tp.src = src; } dst = OVS_MASKED(th->dest, key->tcp_dst, mask->tcp_dst); if (likely(dst != th->dest)) { set_tp_port(skb, &th->dest, dst, &th->check); flow_key->tp.dst = dst; } skb_clear_hash(skb); return 0; } static int set_sctp(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct ovs_key_sctp *key, const struct ovs_key_sctp *mask) { unsigned int sctphoff = skb_transport_offset(skb); struct sctphdr *sh; __le32 old_correct_csum, new_csum, old_csum; int err; err = skb_ensure_writable(skb, sctphoff + sizeof(struct sctphdr)); if (unlikely(err)) return err; sh = sctp_hdr(skb); old_csum = sh->checksum; old_correct_csum = sctp_compute_cksum(skb, sctphoff); sh->source = OVS_MASKED(sh->source, key->sctp_src, mask->sctp_src); sh->dest = OVS_MASKED(sh->dest, key->sctp_dst, mask->sctp_dst); new_csum = sctp_compute_cksum(skb, sctphoff); /* Carry any checksum errors through. */ sh->checksum = old_csum ^ old_correct_csum ^ new_csum; skb_clear_hash(skb); flow_key->tp.src = sh->source; flow_key->tp.dst = sh->dest; return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) static int ovs_vport_output(OVS_VPORT_OUTPUT_PARAMS) { struct ovs_frag_data *data = get_pcpu_ptr(ovs_frag_data_storage); struct vport *vport = data->vport; if (skb_cow_head(skb, data->l2_len) < 0) { kfree_skb(skb); return -ENOMEM; } __skb_dst_copy(skb, data->dst); *OVS_GSO_CB(skb) = data->cb; ovs_skb_set_inner_protocol(skb, data->inner_protocol); skb->vlan_tci = data->vlan_tci; skb->vlan_proto = data->vlan_proto; /* Reconstruct the MAC header. */ skb_push(skb, data->l2_len); memcpy(skb->data, &data->l2_data, data->l2_len); ovs_skb_postpush_rcsum(skb, skb->data, data->l2_len); skb_reset_mac_header(skb); ovs_vport_send(vport, skb); return 0; } static unsigned int ovs_dst_get_mtu(const struct dst_entry *dst) { return dst->dev->mtu; } static struct dst_ops ovs_dst_ops = { .family = AF_UNSPEC, .mtu = ovs_dst_get_mtu, }; /* prepare_frag() is called once per (larger-than-MTU) frame; its inverse is * ovs_vport_output(), which is called once per fragmented packet. */ static void prepare_frag(struct vport *vport, struct sk_buff *skb) { unsigned int hlen = skb_network_offset(skb); struct ovs_frag_data *data; data = get_pcpu_ptr(ovs_frag_data_storage); data->dst = (unsigned long) skb_dst(skb); data->vport = vport; data->cb = *OVS_GSO_CB(skb); data->inner_protocol = ovs_skb_get_inner_protocol(skb); data->vlan_tci = skb->vlan_tci; data->vlan_proto = skb->vlan_proto; data->l2_len = hlen; memcpy(&data->l2_data, skb->data, hlen); memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); skb_pull(skb, hlen); } static void ovs_fragment(struct net *net, struct vport *vport, struct sk_buff *skb, u16 mru, __be16 ethertype) { if (skb_network_offset(skb) > MAX_L2_LEN) { OVS_NLERR(1, "L2 header too long to fragment"); goto err; } if (ethertype == htons(ETH_P_IP)) { struct dst_entry ovs_dst; unsigned long orig_dst; prepare_frag(vport, skb); dst_init(&ovs_dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_dst.dev = vport->dev; orig_dst = (unsigned long) skb_dst(skb); skb_dst_set_noref(skb, &ovs_dst); IPCB(skb)->frag_max_size = mru; ip_do_fragment(net, skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); } else if (ethertype == htons(ETH_P_IPV6)) { const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops(); unsigned long orig_dst; struct rt6_info ovs_rt; if (!v6ops) { goto err; } prepare_frag(vport, skb); memset(&ovs_rt, 0, sizeof(ovs_rt)); dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_rt.dst.dev = vport->dev; orig_dst = (unsigned long) skb_dst(skb); skb_dst_set_noref(skb, &ovs_rt.dst); IP6CB(skb)->frag_max_size = mru; v6ops->fragment(skb->sk, skb, ovs_vport_output); refdst_drop(orig_dst); } else { WARN_ONCE(1, "Failed fragment ->%s: eth=%04x, MRU=%d, MTU=%d.", ovs_vport_name(vport), ntohs(ethertype), mru, vport->dev->mtu); goto err; } return; err: kfree_skb(skb); } #else /* < 3.10 */ static void ovs_fragment(struct net *net, struct vport *vport, struct sk_buff *skb, u16 mru, __be16 ethertype) { WARN_ONCE(1, "Fragment unavailable ->%s: eth=%04x, MRU=%d, MTU=%d.", ovs_vport_name(vport), ntohs(ethertype), mru, vport->dev->mtu); kfree_skb(skb); } #endif static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, struct sw_flow_key *key) { struct vport *vport = ovs_vport_rcu(dp, out_port); if (likely(vport)) { u16 mru = OVS_CB(skb)->mru; if (likely(!mru || (skb->len <= mru + ETH_HLEN))) { ovs_vport_send(vport, skb); } else if (mru <= vport->dev->mtu) { struct net *net = ovs_dp_get_net(dp); __be16 ethertype = key->eth.type; if (!is_flow_key_valid(key)) { if (eth_p_mpls(skb->protocol)) ethertype = ovs_skb_get_inner_protocol(skb); else ethertype = vlan_get_protocol(skb); } ovs_fragment(net, vport, skb, mru, ethertype); } else { OVS_NLERR(true, "Cannot fragment IP frames"); kfree_skb(skb); } } else { kfree_skb(skb); } } static int output_userspace(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, const struct nlattr *actions, int actions_len) { struct ip_tunnel_info info; struct dp_upcall_info upcall; const struct nlattr *a; int rem; memset(&upcall, 0, sizeof(upcall)); upcall.cmd = OVS_PACKET_CMD_ACTION; upcall.mru = OVS_CB(skb)->mru; for (a = nla_data(attr), rem = nla_len(attr); rem > 0; a = nla_next(a, &rem)) { switch (nla_type(a)) { case OVS_USERSPACE_ATTR_USERDATA: upcall.userdata = a; break; case OVS_USERSPACE_ATTR_PID: upcall.portid = nla_get_u32(a); break; case OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: { /* Get out tunnel info. */ struct vport *vport; vport = ovs_vport_rcu(dp, nla_get_u32(a)); if (vport) { int err; upcall.egress_tun_info = &info; err = ovs_vport_get_egress_tun_info(vport, skb, &upcall); if (err) upcall.egress_tun_info = NULL; } break; } case OVS_USERSPACE_ATTR_ACTIONS: { /* Include actions. */ upcall.actions = actions; upcall.actions_len = actions_len; break; } } /* End of switch. */ } return ovs_dp_upcall(dp, skb, key, &upcall); } static int sample(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, const struct nlattr *actions, int actions_len) { const struct nlattr *acts_list = NULL; const struct nlattr *a; int rem; for (a = nla_data(attr), rem = nla_len(attr); rem > 0; a = nla_next(a, &rem)) { u32 probability; switch (nla_type(a)) { case OVS_SAMPLE_ATTR_PROBABILITY: probability = nla_get_u32(a); if (!probability || prandom_u32() > probability) return 0; break; case OVS_SAMPLE_ATTR_ACTIONS: acts_list = a; break; } } rem = nla_len(acts_list); a = nla_data(acts_list); /* Actions list is empty, do nothing */ if (unlikely(!rem)) return 0; /* The only known usage of sample action is having a single user-space * action. Treat this usage as a special case. * The output_userspace() should clone the skb to be sent to the * user space. This skb will be consumed by its caller. */ if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE && nla_is_last(a, rem))) return output_userspace(dp, skb, key, a, actions, actions_len); skb = skb_clone(skb, GFP_ATOMIC); if (!skb) /* Skip the sample action when out of memory. */ return 0; if (!add_deferred_actions(skb, key, a)) { if (net_ratelimit()) pr_warn("%s: deferred actions limit reached, dropping sample action\n", ovs_dp_name(dp)); kfree_skb(skb); } return 0; } static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr) { struct ovs_action_hash *hash_act = nla_data(attr); u32 hash = 0; /* OVS_HASH_ALG_L4 is the only possible hash algorithm. */ hash = skb_get_hash(skb); hash = jhash_1word(hash, hash_act->hash_basis); if (!hash) hash = 0x1; key->ovs_flow_hash = hash; } static int execute_set_action(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct nlattr *a) { /* Only tunnel set execution is supported without a mask. */ if (nla_type(a) == OVS_KEY_ATTR_TUNNEL_INFO) { struct ovs_tunnel_info *tun = nla_data(a); ovs_skb_dst_drop(skb); ovs_dst_hold((struct dst_entry *)tun->tun_dst); ovs_skb_dst_set(skb, (struct dst_entry *)tun->tun_dst); return 0; } return -EINVAL; } /* Mask is at the midpoint of the data. */ #define get_mask(a, type) ((const type)nla_data(a) + 1) static int execute_masked_set_action(struct sk_buff *skb, struct sw_flow_key *flow_key, const struct nlattr *a) { int err = 0; switch (nla_type(a)) { case OVS_KEY_ATTR_PRIORITY: OVS_SET_MASKED(skb->priority, nla_get_u32(a), *get_mask(a, u32 *)); flow_key->phy.priority = skb->priority; break; case OVS_KEY_ATTR_SKB_MARK: OVS_SET_MASKED(skb->mark, nla_get_u32(a), *get_mask(a, u32 *)); flow_key->phy.skb_mark = skb->mark; break; case OVS_KEY_ATTR_TUNNEL_INFO: /* Masked data not supported for tunnel. */ err = -EINVAL; break; case OVS_KEY_ATTR_ETHERNET: err = set_eth_addr(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_ethernet *)); break; case OVS_KEY_ATTR_IPV4: err = set_ipv4(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_ipv4 *)); break; case OVS_KEY_ATTR_IPV6: err = set_ipv6(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_ipv6 *)); break; case OVS_KEY_ATTR_TCP: err = set_tcp(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_tcp *)); break; case OVS_KEY_ATTR_UDP: err = set_udp(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_udp *)); break; case OVS_KEY_ATTR_SCTP: err = set_sctp(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_sctp *)); break; case OVS_KEY_ATTR_MPLS: err = set_mpls(skb, flow_key, nla_data(a), get_mask(a, __be32 *)); break; case OVS_KEY_ATTR_CT_STATE: case OVS_KEY_ATTR_CT_ZONE: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: err = -EINVAL; break; } return err; } static int execute_recirc(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *a, int rem) { struct deferred_action *da; if (!is_flow_key_valid(key)) { int err; err = ovs_flow_key_update(skb, key); if (err) return err; } BUG_ON(!is_flow_key_valid(key)); if (!nla_is_last(a, rem)) { /* Recirc action is the not the last action * of the action list, need to clone the skb. */ skb = skb_clone(skb, GFP_ATOMIC); /* Skip the recirc action when out of memory, but * continue on with the rest of the action list. */ if (!skb) return 0; } da = add_deferred_actions(skb, key, NULL); if (da) { da->pkt_key.recirc_id = nla_get_u32(a); } else { kfree_skb(skb); if (net_ratelimit()) pr_warn("%s: deferred action limit reached, drop recirc action\n", ovs_dp_name(dp)); } return 0; } /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, int len) { /* Every output action needs a separate clone of 'skb', but the common * case is just a single output action, so that doing a clone and * then freeing the original skbuff is wasteful. So the following code * is slightly obscure just to avoid that. */ int prev_port = -1; const struct nlattr *a; int rem; for (a = attr, rem = len; rem > 0; a = nla_next(a, &rem)) { int err = 0; if (unlikely(prev_port != -1)) { struct sk_buff *out_skb = skb_clone(skb, GFP_ATOMIC); if (out_skb) do_output(dp, out_skb, prev_port, key); prev_port = -1; } switch (nla_type(a)) { case OVS_ACTION_ATTR_OUTPUT: prev_port = nla_get_u32(a); break; case OVS_ACTION_ATTR_USERSPACE: output_userspace(dp, skb, key, a, attr, len); break; case OVS_ACTION_ATTR_HASH: execute_hash(skb, key, a); break; case OVS_ACTION_ATTR_PUSH_MPLS: err = push_mpls(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_POP_MPLS: err = pop_mpls(skb, key, nla_get_be16(a)); break; case OVS_ACTION_ATTR_PUSH_VLAN: err = push_vlan(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_POP_VLAN: err = pop_vlan(skb, key); break; case OVS_ACTION_ATTR_RECIRC: err = execute_recirc(dp, skb, key, a, rem); if (nla_is_last(a, rem)) { /* If this is the last action, the skb has * been consumed or freed. * Return immediately. */ return err; } break; case OVS_ACTION_ATTR_SET: err = execute_set_action(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SET_TO_MASKED: err = execute_masked_set_action(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_SAMPLE: err = sample(dp, skb, key, a, attr, len); break; case OVS_ACTION_ATTR_CT: if (!is_flow_key_valid(key)) { err = ovs_flow_key_update(skb, key); if (err) return err; } err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, nla_data(a)); /* Hide stolen IP fragments from user space. */ if (err) return err == -EINPROGRESS ? 0 : err; break; } if (unlikely(err)) { kfree_skb(skb); return err; } } if (prev_port != -1) do_output(dp, skb, prev_port, key); else consume_skb(skb); return 0; } static void process_deferred_actions(struct datapath *dp) { struct action_fifo *fifo = this_cpu_ptr(action_fifos); /* Do not touch the FIFO in case there is no deferred actions. */ if (action_fifo_is_empty(fifo)) return; /* Finishing executing all deferred actions. */ do { struct deferred_action *da = action_fifo_get(fifo); struct sk_buff *skb = da->skb; struct sw_flow_key *key = &da->pkt_key; const struct nlattr *actions = da->actions; if (actions) do_execute_actions(dp, skb, key, actions, nla_len(actions)); else ovs_dp_process_packet(skb, key); } while (!action_fifo_is_empty(fifo)); /* Reset FIFO for the next packet. */ action_fifo_init(fifo); } /* Execute a list of actions against 'skb'. */ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_actions *acts, struct sw_flow_key *key) { int level = this_cpu_read(exec_actions_level); int err; if (unlikely(level >= EXEC_ACTIONS_LEVEL_LIMIT)) { if (net_ratelimit()) pr_warn("%s: packet loop detected, dropping.\n", ovs_dp_name(dp)); kfree_skb(skb); return -ELOOP; } this_cpu_inc(exec_actions_level); err = do_execute_actions(dp, skb, key, acts->actions, acts->actions_len); if (!level) process_deferred_actions(dp); this_cpu_dec(exec_actions_level); /* This return status currently does not reflect the errors * encounted during deferred actions execution. Probably needs to * be fixed in the future. */ return err; } int action_fifos_init(void) { action_fifos = alloc_percpu(struct action_fifo); if (!action_fifos) return -ENOMEM; return 0; } void action_fifos_exit(void) { free_percpu(action_fifos); } openvswitch-2.5.9/datapath/PaxHeaders.82075/flow_table.c0000644000000000000000000000013213534540071017714 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.521858881 openvswitch-2.5.9/datapath/flow_table.c0000644000175000017500000006007413534540071021411 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "flow.h" #include "datapath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vlan.h" #include "flow_netlink.h" #define TBL_MIN_BUCKETS 1024 #define MASK_ARRAY_SIZE_MIN 16 #define REHASH_INTERVAL (10 * 60 * HZ) #define MC_HASH_SHIFT 8 #define MC_HASH_ENTRIES (1u << MC_HASH_SHIFT) #define MC_HASH_SEGS ((sizeof(uint32_t) * 8) / MC_HASH_SHIFT) static struct kmem_cache *flow_cache; struct kmem_cache *flow_stats_cache __read_mostly; static u16 range_n_bytes(const struct sw_flow_key_range *range) { return range->end - range->start; } void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, bool full, const struct sw_flow_mask *mask) { int start = full ? 0 : mask->range.start; int len = full ? sizeof *dst : range_n_bytes(&mask->range); const long *m = (const long *)((const u8 *)&mask->key + start); const long *s = (const long *)((const u8 *)src + start); long *d = (long *)((u8 *)dst + start); int i; /* If 'full' is true then all of 'dst' is fully initialized. Otherwise, * if 'full' is false the memory outside of the 'mask->range' is left * uninitialized. This can be used as an optimization when further * operations on 'dst' only use contents within 'mask->range'. */ for (i = 0; i < len; i += sizeof(long)) *d++ = *s++ & *m++; } struct sw_flow *ovs_flow_alloc(void) { struct sw_flow *flow; struct flow_stats *stats; int node; flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); if (!flow) return ERR_PTR(-ENOMEM); flow->sf_acts = NULL; flow->mask = NULL; flow->id.ufid_len = 0; flow->id.unmasked_key = NULL; flow->stats_last_writer = NUMA_NO_NODE; /* Initialize the default stat node. */ stats = kmem_cache_alloc_node(flow_stats_cache, GFP_KERNEL | __GFP_ZERO, 0); if (!stats) goto err; spin_lock_init(&stats->lock); RCU_INIT_POINTER(flow->stats[0], stats); for_each_node(node) if (node != 0) RCU_INIT_POINTER(flow->stats[node], NULL); return flow; err: kmem_cache_free(flow_cache, flow); return ERR_PTR(-ENOMEM); } int ovs_flow_tbl_count(const struct flow_table *table) { return table->count; } static struct flex_array *alloc_buckets(unsigned int n_buckets) { struct flex_array *buckets; int i, err; buckets = flex_array_alloc(sizeof(struct hlist_head), n_buckets, GFP_KERNEL); if (!buckets) return NULL; err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL); if (err) { flex_array_free(buckets); return NULL; } for (i = 0; i < n_buckets; i++) INIT_HLIST_HEAD((struct hlist_head *) flex_array_get(buckets, i)); return buckets; } static void flow_free(struct sw_flow *flow) { int node; if (ovs_identifier_is_key(&flow->id)) kfree(flow->id.unmasked_key); if (flow->sf_acts) ovs_nla_free_flow_actions((struct sw_flow_actions __force *)flow->sf_acts); for_each_node(node) if (flow->stats[node]) kmem_cache_free(flow_stats_cache, rcu_dereference_raw(flow->stats[node])); kmem_cache_free(flow_cache, flow); } static void rcu_free_flow_callback(struct rcu_head *rcu) { struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); flow_free(flow); } static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu) { struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu); kfree(mask); } void ovs_flow_free(struct sw_flow *flow, bool deferred) { if (!flow) return; if (deferred) call_rcu(&flow->rcu, rcu_free_flow_callback); else flow_free(flow); } static void free_buckets(struct flex_array *buckets) { flex_array_free(buckets); } static void __table_instance_destroy(struct table_instance *ti) { free_buckets(ti->buckets); kfree(ti); } static struct table_instance *table_instance_alloc(int new_size) { struct table_instance *ti = kmalloc(sizeof(*ti), GFP_KERNEL); if (!ti) return NULL; ti->buckets = alloc_buckets(new_size); if (!ti->buckets) { kfree(ti); return NULL; } ti->n_buckets = new_size; ti->node_ver = 0; ti->keep_flows = false; get_random_bytes(&ti->hash_seed, sizeof(u32)); return ti; } static void mask_array_rcu_cb(struct rcu_head *rcu) { struct mask_array *ma = container_of(rcu, struct mask_array, rcu); kfree(ma); } static struct mask_array *tbl_mask_array_alloc(int size) { struct mask_array *new; size = max(MASK_ARRAY_SIZE_MIN, size); new = kzalloc(sizeof(struct mask_array) + sizeof(struct sw_flow_mask *) * size, GFP_KERNEL); if (!new) return NULL; new->count = 0; new->max = size; return new; } static int tbl_mask_array_realloc(struct flow_table *tbl, int size) { struct mask_array *old; struct mask_array *new; new = tbl_mask_array_alloc(size); if (!new) return -ENOMEM; old = ovsl_dereference(tbl->mask_array); if (old) { int i, count = 0; for (i = 0; i < old->max; i++) { if (ovsl_dereference(old->masks[i])) new->masks[count++] = old->masks[i]; } new->count = count; } rcu_assign_pointer(tbl->mask_array, new); if (old) call_rcu(&old->rcu, mask_array_rcu_cb); return 0; } int ovs_flow_tbl_init(struct flow_table *table) { struct table_instance *ti, *ufid_ti; struct mask_array *ma; table->mask_cache = __alloc_percpu(sizeof(struct mask_cache_entry) * MC_HASH_ENTRIES, __alignof__(struct mask_cache_entry)); if (!table->mask_cache) return -ENOMEM; ma = tbl_mask_array_alloc(MASK_ARRAY_SIZE_MIN); if (!ma) goto free_mask_cache; ti = table_instance_alloc(TBL_MIN_BUCKETS); if (!ti) goto free_mask_array; ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS); if (!ufid_ti) goto free_ti; rcu_assign_pointer(table->ti, ti); rcu_assign_pointer(table->ufid_ti, ufid_ti); rcu_assign_pointer(table->mask_array, ma); table->last_rehash = jiffies; table->count = 0; table->ufid_count = 0; return 0; free_ti: __table_instance_destroy(ti); free_mask_array: kfree(ma); free_mask_cache: free_percpu(table->mask_cache); return -ENOMEM; } static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) { struct table_instance *ti = container_of(rcu, struct table_instance, rcu); __table_instance_destroy(ti); } static void table_instance_destroy(struct table_instance *ti, struct table_instance *ufid_ti, bool deferred) { int i; if (!ti) return; BUG_ON(!ufid_ti); if (ti->keep_flows) goto skip_flows; for (i = 0; i < ti->n_buckets; i++) { struct sw_flow *flow; struct hlist_head *head = flex_array_get(ti->buckets, i); struct hlist_node *n; int ver = ti->node_ver; int ufid_ver = ufid_ti->node_ver; hlist_for_each_entry_safe(flow, n, head, flow_table.node[ver]) { hlist_del_rcu(&flow->flow_table.node[ver]); if (ovs_identifier_is_ufid(&flow->id)) hlist_del_rcu(&flow->ufid_table.node[ufid_ver]); ovs_flow_free(flow, deferred); } } skip_flows: if (deferred) { call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); call_rcu(&ufid_ti->rcu, flow_tbl_destroy_rcu_cb); } else { __table_instance_destroy(ti); __table_instance_destroy(ufid_ti); } } /* No need for locking this function is called from RCU callback or * error path. */ void ovs_flow_tbl_destroy(struct flow_table *table) { struct table_instance *ti = rcu_dereference_raw(table->ti); struct table_instance *ufid_ti = rcu_dereference_raw(table->ufid_ti); free_percpu(table->mask_cache); kfree(rcu_dereference_raw(table->mask_array)); table_instance_destroy(ti, ufid_ti, false); } struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, u32 *bucket, u32 *last) { struct sw_flow *flow; struct hlist_head *head; int ver; int i; ver = ti->node_ver; while (*bucket < ti->n_buckets) { i = 0; head = flex_array_get(ti->buckets, *bucket); hlist_for_each_entry_rcu(flow, head, flow_table.node[ver]) { if (i < *last) { i++; continue; } *last = i + 1; return flow; } (*bucket)++; *last = 0; } return NULL; } static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash) { hash = jhash_1word(hash, ti->hash_seed); return flex_array_get(ti->buckets, (hash & (ti->n_buckets - 1))); } static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow) { struct hlist_head *head; head = find_bucket(ti, flow->flow_table.hash); hlist_add_head_rcu(&flow->flow_table.node[ti->node_ver], head); } static void ufid_table_instance_insert(struct table_instance *ti, struct sw_flow *flow) { struct hlist_head *head; head = find_bucket(ti, flow->ufid_table.hash); hlist_add_head_rcu(&flow->ufid_table.node[ti->node_ver], head); } static void flow_table_copy_flows(struct table_instance *old, struct table_instance *new, bool ufid) { int old_ver; int i; old_ver = old->node_ver; new->node_ver = !old_ver; /* Insert in new table. */ for (i = 0; i < old->n_buckets; i++) { struct sw_flow *flow; struct hlist_head *head; head = flex_array_get(old->buckets, i); if (ufid) hlist_for_each_entry(flow, head, ufid_table.node[old_ver]) ufid_table_instance_insert(new, flow); else hlist_for_each_entry(flow, head, flow_table.node[old_ver]) table_instance_insert(new, flow); } old->keep_flows = true; } static struct table_instance *table_instance_rehash(struct table_instance *ti, int n_buckets, bool ufid) { struct table_instance *new_ti; new_ti = table_instance_alloc(n_buckets); if (!new_ti) return NULL; flow_table_copy_flows(ti, new_ti, ufid); return new_ti; } int ovs_flow_tbl_flush(struct flow_table *flow_table) { struct table_instance *old_ti, *new_ti; struct table_instance *old_ufid_ti, *new_ufid_ti; new_ti = table_instance_alloc(TBL_MIN_BUCKETS); if (!new_ti) return -ENOMEM; new_ufid_ti = table_instance_alloc(TBL_MIN_BUCKETS); if (!new_ufid_ti) goto err_free_ti; old_ti = ovsl_dereference(flow_table->ti); old_ufid_ti = ovsl_dereference(flow_table->ufid_ti); rcu_assign_pointer(flow_table->ti, new_ti); rcu_assign_pointer(flow_table->ufid_ti, new_ufid_ti); flow_table->last_rehash = jiffies; flow_table->count = 0; flow_table->ufid_count = 0; table_instance_destroy(old_ti, old_ufid_ti, true); return 0; err_free_ti: __table_instance_destroy(new_ti); return -ENOMEM; } static u32 flow_hash(const struct sw_flow_key *key, const struct sw_flow_key_range *range) { int key_start = range->start; int key_end = range->end; const u32 *hash_key = (const u32 *)((const u8 *)key + key_start); int hash_u32s = (key_end - key_start) >> 2; /* Make sure number of hash bytes are multiple of u32. */ BUILD_BUG_ON(sizeof(long) % sizeof(u32)); return jhash2(hash_key, hash_u32s, 0); } static int flow_key_start(const struct sw_flow_key *key) { if (key->tun_key.u.ipv4.dst) return 0; else return rounddown(offsetof(struct sw_flow_key, phy), sizeof(long)); } static bool cmp_key(const struct sw_flow_key *key1, const struct sw_flow_key *key2, int key_start, int key_end) { const long *cp1 = (const long *)((const u8 *)key1 + key_start); const long *cp2 = (const long *)((const u8 *)key2 + key_start); long diffs = 0; int i; for (i = key_start; i < key_end; i += sizeof(long)) diffs |= *cp1++ ^ *cp2++; return diffs == 0; } static bool flow_cmp_masked_key(const struct sw_flow *flow, const struct sw_flow_key *key, const struct sw_flow_key_range *range) { return cmp_key(&flow->key, key, range->start, range->end); } static bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, const struct sw_flow_match *match) { struct sw_flow_key *key = match->key; int key_start = flow_key_start(key); int key_end = match->range.end; BUG_ON(ovs_identifier_is_ufid(&flow->id)); return cmp_key(flow->id.unmasked_key, key, key_start, key_end); } static struct sw_flow *masked_flow_lookup(struct table_instance *ti, const struct sw_flow_key *unmasked, const struct sw_flow_mask *mask, u32 *n_mask_hit) { struct sw_flow *flow; struct hlist_head *head; u32 hash; struct sw_flow_key masked_key; ovs_flow_mask_key(&masked_key, unmasked, false, mask); hash = flow_hash(&masked_key, &mask->range); head = find_bucket(ti, hash); (*n_mask_hit)++; hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { if (flow->mask == mask && flow->flow_table.hash == hash && flow_cmp_masked_key(flow, &masked_key, &mask->range)) return flow; } return NULL; } /* Flow lookup does full lookup on flow table. It starts with * mask from index passed in *index. */ static struct sw_flow *flow_lookup(struct flow_table *tbl, struct table_instance *ti, const struct mask_array *ma, const struct sw_flow_key *key, u32 *n_mask_hit, u32 *index) { struct sw_flow_mask *mask; struct sw_flow *flow; int i; if (*index < ma->max) { mask = rcu_dereference_ovsl(ma->masks[*index]); if (mask) { flow = masked_flow_lookup(ti, key, mask, n_mask_hit); if (flow) return flow; } } for (i = 0; i < ma->max; i++) { if (i == *index) continue; mask = rcu_dereference_ovsl(ma->masks[i]); if (!mask) continue; flow = masked_flow_lookup(ti, key, mask, n_mask_hit); if (flow) { /* Found */ *index = i; return flow; } } return NULL; } /* * mask_cache maps flow to probable mask. This cache is not tightly * coupled cache, It means updates to mask list can result in inconsistent * cache entry in mask cache. * This is per cpu cache and is divided in MC_HASH_SEGS segments. * In case of a hash collision the entry is hashed in next segment. */ struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl, const struct sw_flow_key *key, u32 skb_hash, u32 *n_mask_hit) { struct mask_array *ma = rcu_dereference(tbl->mask_array); struct table_instance *ti = rcu_dereference(tbl->ti); struct mask_cache_entry *entries, *ce; struct sw_flow *flow; u32 hash; int seg; *n_mask_hit = 0; if (unlikely(!skb_hash)) { u32 mask_index = 0; return flow_lookup(tbl, ti, ma, key, n_mask_hit, &mask_index); } /* Pre and post recirulation flows usually have the same skb_hash * value. To avoid hash collisions, rehash the 'skb_hash' with * 'recirc_id'. */ if (key->recirc_id) skb_hash = jhash_1word(skb_hash, key->recirc_id); ce = NULL; hash = skb_hash; entries = this_cpu_ptr(tbl->mask_cache); /* Find the cache entry 'ce' to operate on. */ for (seg = 0; seg < MC_HASH_SEGS; seg++) { int index = hash & (MC_HASH_ENTRIES - 1); struct mask_cache_entry *e; e = &entries[index]; if (e->skb_hash == skb_hash) { flow = flow_lookup(tbl, ti, ma, key, n_mask_hit, &e->mask_index); if (!flow) e->skb_hash = 0; return flow; } if (!ce || e->skb_hash < ce->skb_hash) ce = e; /* A better replacement cache candidate. */ hash >>= MC_HASH_SHIFT; } /* Cache miss, do full lookup. */ flow = flow_lookup(tbl, ti, ma, key, n_mask_hit, &ce->mask_index); if (flow) ce->skb_hash = skb_hash; return flow; } struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, const struct sw_flow_key *key) { struct table_instance *ti = rcu_dereference_ovsl(tbl->ti); struct mask_array *ma = rcu_dereference_ovsl(tbl->mask_array); u32 __always_unused n_mask_hit; u32 index = 0; return flow_lookup(tbl, ti, ma, key, &n_mask_hit, &index); } struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, const struct sw_flow_match *match) { struct mask_array *ma = ovsl_dereference(tbl->mask_array); int i; /* Always called under ovs-mutex. */ for (i = 0; i < ma->max; i++) { struct table_instance *ti = ovsl_dereference(tbl->ti); u32 __always_unused n_mask_hit; struct sw_flow_mask *mask; struct sw_flow *flow; mask = ovsl_dereference(ma->masks[i]); if (!mask) continue; flow = masked_flow_lookup(ti, match->key, mask, &n_mask_hit); if (flow && ovs_identifier_is_key(&flow->id) && ovs_flow_cmp_unmasked_key(flow, match)) return flow; } return NULL; } static u32 ufid_hash(const struct sw_flow_id *sfid) { return jhash(sfid->ufid, sfid->ufid_len, 0); } static bool ovs_flow_cmp_ufid(const struct sw_flow *flow, const struct sw_flow_id *sfid) { if (flow->id.ufid_len != sfid->ufid_len) return false; return !memcmp(flow->id.ufid, sfid->ufid, sfid->ufid_len); } bool ovs_flow_cmp(const struct sw_flow *flow, const struct sw_flow_match *match) { if (ovs_identifier_is_ufid(&flow->id)) return flow_cmp_masked_key(flow, match->key, &match->range); return ovs_flow_cmp_unmasked_key(flow, match); } struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *tbl, const struct sw_flow_id *ufid) { struct table_instance *ti = rcu_dereference_ovsl(tbl->ufid_ti); struct sw_flow *flow; struct hlist_head *head; u32 hash; hash = ufid_hash(ufid); head = find_bucket(ti, hash); hlist_for_each_entry_rcu(flow, head, ufid_table.node[ti->node_ver]) { if (flow->ufid_table.hash == hash && ovs_flow_cmp_ufid(flow, ufid)) return flow; } return NULL; } int ovs_flow_tbl_num_masks(const struct flow_table *table) { struct mask_array *ma; ma = rcu_dereference_ovsl(table->mask_array); return ma->count; } static struct table_instance *table_instance_expand(struct table_instance *ti, bool ufid) { return table_instance_rehash(ti, ti->n_buckets * 2, ufid); } static void tbl_mask_array_delete_mask(struct mask_array *ma, struct sw_flow_mask *mask) { int i; /* Remove the deleted mask pointers from the array */ for (i = 0; i < ma->max; i++) { if (mask == ovsl_dereference(ma->masks[i])) { RCU_INIT_POINTER(ma->masks[i], NULL); ma->count--; call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb); return; } } BUG(); } /* Remove 'mask' from the mask list, if it is not needed any more. */ static void flow_mask_remove(struct flow_table *tbl, struct sw_flow_mask *mask) { if (mask) { /* ovs-lock is required to protect mask-refcount and * mask list. */ ASSERT_OVSL(); BUG_ON(!mask->ref_count); mask->ref_count--; if (!mask->ref_count) { struct mask_array *ma; ma = ovsl_dereference(tbl->mask_array); tbl_mask_array_delete_mask(ma, mask); /* Shrink the mask array if necessary. */ if (ma->max >= (MASK_ARRAY_SIZE_MIN * 2) && ma->count <= (ma->max / 3)) tbl_mask_array_realloc(tbl, ma->max / 2); } } } /* Must be called with OVS mutex held. */ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) { struct table_instance *ti = ovsl_dereference(table->ti); struct table_instance *ufid_ti = ovsl_dereference(table->ufid_ti); BUG_ON(table->count == 0); hlist_del_rcu(&flow->flow_table.node[ti->node_ver]); table->count--; if (ovs_identifier_is_ufid(&flow->id)) { hlist_del_rcu(&flow->ufid_table.node[ufid_ti->node_ver]); table->ufid_count--; } /* RCU delete the mask. 'flow->mask' is not NULLed, as it should be * accessible as long as the RCU read lock is held. */ flow_mask_remove(table, flow->mask); } static struct sw_flow_mask *mask_alloc(void) { struct sw_flow_mask *mask; mask = kmalloc(sizeof(*mask), GFP_KERNEL); if (mask) mask->ref_count = 1; return mask; } static bool mask_equal(const struct sw_flow_mask *a, const struct sw_flow_mask *b) { const u8 *a_ = (const u8 *)&a->key + a->range.start; const u8 *b_ = (const u8 *)&b->key + b->range.start; return (a->range.end == b->range.end) && (a->range.start == b->range.start) && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0); } static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl, const struct sw_flow_mask *mask) { struct mask_array *ma; int i; ma = ovsl_dereference(tbl->mask_array); for (i = 0; i < ma->max; i++) { struct sw_flow_mask *t; t = ovsl_dereference(ma->masks[i]); if (t && mask_equal(mask, t)) return t; } return NULL; } /* Add 'mask' into the mask list, if it is not already there. */ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, const struct sw_flow_mask *new) { struct sw_flow_mask *mask; mask = flow_mask_find(tbl, new); if (!mask) { struct mask_array *ma; int i; /* Allocate a new mask if none exsits. */ mask = mask_alloc(); if (!mask) return -ENOMEM; mask->key = new->key; mask->range = new->range; /* Add mask to mask-list. */ ma = ovsl_dereference(tbl->mask_array); if (ma->count >= ma->max) { int err; err = tbl_mask_array_realloc(tbl, ma->max + MASK_ARRAY_SIZE_MIN); if (err) { kfree(mask); return err; } ma = ovsl_dereference(tbl->mask_array); } for (i = 0; i < ma->max; i++) { struct sw_flow_mask *t; t = ovsl_dereference(ma->masks[i]); if (!t) { rcu_assign_pointer(ma->masks[i], mask); ma->count++; break; } } } else { BUG_ON(!mask->ref_count); mask->ref_count++; } flow->mask = mask; return 0; } /* Must be called with OVS mutex held. */ static void flow_key_insert(struct flow_table *table, struct sw_flow *flow) { struct table_instance *new_ti = NULL; struct table_instance *ti; flow->flow_table.hash = flow_hash(&flow->key, &flow->mask->range); ti = ovsl_dereference(table->ti); table_instance_insert(ti, flow); table->count++; /* Expand table, if necessary, to make room. */ if (table->count > ti->n_buckets) new_ti = table_instance_expand(ti, false); else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL)) new_ti = table_instance_rehash(ti, ti->n_buckets, false); if (new_ti) { rcu_assign_pointer(table->ti, new_ti); call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); table->last_rehash = jiffies; } } /* Must be called with OVS mutex held. */ static void flow_ufid_insert(struct flow_table *table, struct sw_flow *flow) { struct table_instance *ti; flow->ufid_table.hash = ufid_hash(&flow->id); ti = ovsl_dereference(table->ufid_ti); ufid_table_instance_insert(ti, flow); table->ufid_count++; /* Expand table, if necessary, to make room. */ if (table->ufid_count > ti->n_buckets) { struct table_instance *new_ti; new_ti = table_instance_expand(ti, true); if (new_ti) { rcu_assign_pointer(table->ufid_ti, new_ti); call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); } } } /* Must be called with OVS mutex held. */ int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, const struct sw_flow_mask *mask) { int err; err = flow_mask_insert(table, flow, mask); if (err) return err; flow_key_insert(table, flow); if (ovs_identifier_is_ufid(&flow->id)) flow_ufid_insert(table, flow); return 0; } /* Initializes the flow module. * Returns zero if successful or a negative error code. */ int ovs_flow_init(void) { BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long)); BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long)); flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow) + (nr_node_ids * sizeof(struct flow_stats *)), 0, 0, NULL); if (flow_cache == NULL) return -ENOMEM; flow_stats_cache = kmem_cache_create("sw_flow_stats", sizeof(struct flow_stats), 0, SLAB_HWCACHE_ALIGN, NULL); if (flow_stats_cache == NULL) { kmem_cache_destroy(flow_cache); flow_cache = NULL; return -ENOMEM; } return 0; } /* Uninitializes the flow module. */ void ovs_flow_exit(void) { kmem_cache_destroy(flow_stats_cache); kmem_cache_destroy(flow_cache); } openvswitch-2.5.9/datapath/PaxHeaders.82075/README.md0000644000000000000000000000013213534540071016711 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801425.561859176 openvswitch-2.5.9/datapath/README.md0000644000175000017500000002774513534540071020416 0ustar00jpettitjpettit00000000000000Open vSwitch datapath developer documentation ============================================= The Open vSwitch kernel module allows flexible userspace control over flow-level packet processing on selected network devices. It can be used to implement a plain Ethernet switch, network device bonding, VLAN processing, network access control, flow-based network control, and so on. The kernel module implements multiple "datapaths" (analogous to bridges), each of which can have multiple "vports" (analogous to ports within a bridge). Each datapath also has associated with it a "flow table" that userspace populates with "flows" that map from keys based on packet headers and metadata to sets of actions. The most common action forwards the packet to another vport; other actions are also implemented. When a packet arrives on a vport, the kernel module processes it by extracting its flow key and looking it up in the flow table. If there is a matching flow, it executes the associated actions. If there is no match, it queues the packet to userspace for processing (as part of its processing, userspace will likely set up a flow to handle further packets of the same type entirely in-kernel). Flow key compatibility ---------------------- Network protocols evolve over time. New protocols become important and existing protocols lose their prominence. For the Open vSwitch kernel module to remain relevant, it must be possible for newer versions to parse additional protocols as part of the flow key. It might even be desirable, someday, to drop support for parsing protocols that have become obsolete. Therefore, the Netlink interface to Open vSwitch is designed to allow carefully written userspace applications to work with any version of the flow key, past or future. To support this forward and backward compatibility, whenever the kernel module passes a packet to userspace, it also passes along the flow key that it parsed from the packet. Userspace then extracts its own notion of a flow key from the packet and compares it against the kernel-provided version: - If userspace's notion of the flow key for the packet matches the kernel's, then nothing special is necessary. - If the kernel's flow key includes more fields than the userspace version of the flow key, for example if the kernel decoded IPv6 headers but userspace stopped at the Ethernet type (because it does not understand IPv6), then again nothing special is necessary. Userspace can still set up a flow in the usual way, as long as it uses the kernel-provided flow key to do it. - If the userspace flow key includes more fields than the kernel's, for example if userspace decoded an IPv6 header but the kernel stopped at the Ethernet type, then userspace can forward the packet manually, without setting up a flow in the kernel. This case is bad for performance because every packet that the kernel considers part of the flow must go to userspace, but the forwarding behavior is correct. (If userspace can determine that the values of the extra fields would not affect forwarding behavior, then it could set up a flow anyway.) How flow keys evolve over time is important to making this work, so the following sections go into detail. Flow key format --------------- A flow key is passed over a Netlink socket as a sequence of Netlink attributes. Some attributes represent packet metadata, defined as any information about a packet that cannot be extracted from the packet itself, e.g. the vport on which the packet was received. Most attributes, however, are extracted from headers within the packet, e.g. source and destination addresses from Ethernet, IP, or TCP headers. The header file defines the exact format of the flow key attributes. For informal explanatory purposes here, we write them as comma-separated strings, with parentheses indicating arguments and nesting. For example, the following could represent a flow key corresponding to a TCP packet that arrived on vport 1: in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4), eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0, frag=no), tcp(src=49163, dst=80) Often we ellipsize arguments not important to the discussion, e.g.: in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...) Wildcarded flow key format -------------------------- A wildcarded flow is described with two sequences of Netlink attributes passed over the Netlink socket. A flow key, exactly as described above, and an optional corresponding flow mask. A wildcarded flow can represent a group of exact match flows. Each '1' bit in the mask specifies an exact match with the corresponding bit in the flow key. A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit of an incoming packet. Using a wildcarded flow can improve the flow set up rate by reducing the number of new flows that need to be processed by the user space program. Support for the mask Netlink attribute is optional for both the kernel and user space program. The kernel can ignore the mask attribute, installing an exact match flow, or reduce the number of don't care bits in the kernel to less than what was specified by the user space program. In this case, variations in bits that the kernel does not implement will simply result in additional flow setups. The kernel module will also work with user space programs that neither support nor supply flow mask attributes. Since the kernel may ignore or modify wildcard bits, it can be difficult for the userspace program to know exactly what matches are installed. There are two possible approaches: reactively install flows as they miss the kernel flow table (and therefore not attempt to determine wildcard changes at all) or use the kernel's response messages to determine the installed wildcards. When interacting with userspace, the kernel should maintain the match portion of the key exactly as originally installed. This will provides a handle to identify the flow for all future operations. However, when reporting the mask of an installed flow, the mask should include any restrictions imposed by the kernel. The behavior when using overlapping wildcarded flows is undefined. It is the responsibility of the user space program to ensure that any incoming packet can match at most one flow, wildcarded or not. The current implementation performs best-effort detection of overlapping wildcarded flows and may reject some but not all of them. However, this behavior may change in future versions. Unique flow identifiers ----------------------- An alternative to using the original match portion of a key as the handle for flow identification is a unique flow identifier, or "UFID". UFIDs are optional for both the kernel and user space program. User space programs that support UFID are expected to provide it during flow setup in addition to the flow, then refer to the flow using the UFID for all future operations. The kernel is not required to index flows by the original flow key if a UFID is specified. Basic rule for evolving flow keys --------------------------------- Some care is needed to really maintain forward and backward compatibility for applications that follow the rules listed under "Flow key compatibility" above. The basic rule is obvious: ------------------------------------------------------------------ New network protocol support must only supplement existing flow key attributes. It must not change the meaning of already defined flow key attributes. ------------------------------------------------------------------ This rule does have less-obvious consequences so it is worth working through a few examples. Suppose, for example, that the kernel module did not already implement VLAN parsing. Instead, it just interpreted the 802.1Q TPID (0x8100) as the Ethertype then stopped parsing the packet. The flow key for any packet with an 802.1Q header would look essentially like this, ignoring metadata: eth(...), eth_type(0x8100) Naively, to add VLAN support, it makes sense to add a new "vlan" flow key attribute to contain the VLAN tag, then continue to decode the encapsulated headers beyond the VLAN tag using the existing field definitions. With this change, a TCP packet in VLAN 10 would have a flow key much like this: eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...) But this change would negatively affect a userspace application that has not been updated to understand the new "vlan" flow key attribute. The application could, following the flow compatibility rules above, ignore the "vlan" attribute that it does not understand and therefore assume that the flow contained IP packets. This is a bad assumption (the flow only contains IP packets if one parses and skips over the 802.1Q header) and it could cause the application's behavior to change across kernel versions even though it follows the compatibility rules. The solution is to use a set of nested attributes. This is, for example, why 802.1Q support uses nested attributes. A TCP packet in VLAN 10 is actually expressed as: eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800), ip(proto=6, ...), tcp(...))) Notice how the "eth_type", "ip", and "tcp" flow key attributes are nested inside the "encap" attribute. Thus, an application that does not understand the "vlan" key will not see either of those attributes and therefore will not misinterpret them. (Also, the outer eth_type is still 0x8100, not changed to 0x0800.) Handling malformed packets -------------------------- Don't drop packets in the kernel for malformed protocol headers, bad checksums, etc. This would prevent userspace from implementing a simple Ethernet switch that forwards every packet. Instead, in such a case, include an attribute with "empty" content. It doesn't matter if the empty content could be valid protocol values, as long as those values are rarely seen in practice, because userspace can always forward all packets with those values to userspace and handle them individually. For example, consider a packet that contains an IP header that indicates protocol 6 for TCP, but which is truncated just after the IP header, so that the TCP header is missing. The flow key for this packet would include a tcp attribute with all-zero src and dst, like this: eth(...), eth_type(0x0800), ip(proto=6, ...), tcp(src=0, dst=0) As another example, consider a packet with an Ethernet type of 0x8100, indicating that a VLAN TCI should follow, but which is truncated just after the Ethernet type. The flow key for this packet would include an all-zero-bits vlan and an empty encap attribute, like this: eth(...), eth_type(0x8100), vlan(0), encap() Unlike a TCP packet with source and destination ports 0, an all-zero-bits VLAN TCI is not that rare, so the CFI bit (aka VLAN_TAG_PRESENT inside the kernel) is ordinarily set in a vlan attribute expressly to allow this situation to be distinguished. Thus, the flow key in this second example unambiguously indicates a missing or malformed VLAN TCI. Other rules ----------- The other rules for flow keys are much less subtle: - Duplicate attributes are not allowed at a given nesting level. - Ordering of attributes is not significant. - When the kernel sends a given flow key to userspace, it always composes it the same way. This allows userspace to hash and compare entire flow keys that it may not be able to fully interpret. Coding rules ============ Compatibility ------------- Please implement the headers and codes for compatibility with older kernel in linux/compat/ directory. All public functions should be exported using EXPORT_SYMBOL macro. Public function replacing the same-named kernel function should be prefixed with 'rpl_'. Otherwise, the function should be prefixed with 'ovs_'. For special case when it is not possible to follow this rule (e.g., the pskb_expand_head() function), the function name must be added to linux/compat/build-aux/export-check-whitelist, otherwise, the compilation check 'check-export-symbol' will fail. openvswitch-2.5.9/datapath/PaxHeaders.82075/flow_netlink.c0000644000000000000000000000013213534540071020271 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.521858881 openvswitch-2.5.9/datapath/flow_netlink.c0000644000175000017500000020554313534540071021770 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "conntrack.h" #include "flow.h" #include "flow_netlink.h" #include "gso.h" struct ovs_len_tbl { int len; const struct ovs_len_tbl *next; }; #define OVS_ATTR_NESTED -1 #define OVS_ATTR_VARIABLE -2 static void update_range(struct sw_flow_match *match, size_t offset, size_t size, bool is_mask) { struct sw_flow_key_range *range; size_t start = rounddown(offset, sizeof(long)); size_t end = roundup(offset + size, sizeof(long)); if (!is_mask) range = &match->range; else range = &match->mask->range; if (range->start == range->end) { range->start = start; range->end = end; return; } if (range->start > start) range->start = start; if (range->end < end) range->end = end; } #define SW_FLOW_KEY_PUT(match, field, value, is_mask) \ do { \ update_range(match, offsetof(struct sw_flow_key, field), \ sizeof((match)->key->field), is_mask); \ if (is_mask) \ (match)->mask->key.field = value; \ else \ (match)->key->field = value; \ } while (0) #define SW_FLOW_KEY_MEMCPY_OFFSET(match, offset, value_p, len, is_mask) \ do { \ update_range(match, offset, len, is_mask); \ if (is_mask) \ memcpy((u8 *)&(match)->mask->key + offset, value_p, len);\ else \ memcpy((u8 *)(match)->key + offset, value_p, len); \ } while (0) #define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \ SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \ value_p, len, is_mask) #define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \ do { \ update_range(match, offsetof(struct sw_flow_key, field), \ sizeof((match)->key->field), is_mask); \ if (is_mask) \ memset((u8 *)&(match)->mask->key.field, value, \ sizeof((match)->mask->key.field)); \ else \ memset((u8 *)&(match)->key->field, value, \ sizeof((match)->key->field)); \ } while (0) static bool match_validate(const struct sw_flow_match *match, u64 key_attrs, u64 mask_attrs, bool log) { u64 key_expected = 1ULL << OVS_KEY_ATTR_ETHERNET; u64 mask_allowed = key_attrs; /* At most allow all key attributes */ /* The following mask attributes allowed only if they * pass the validation tests. */ mask_allowed &= ~((1ULL << OVS_KEY_ATTR_IPV4) | (1ULL << OVS_KEY_ATTR_IPV6) | (1ULL << OVS_KEY_ATTR_TCP) | (1ULL << OVS_KEY_ATTR_TCP_FLAGS) | (1ULL << OVS_KEY_ATTR_UDP) | (1ULL << OVS_KEY_ATTR_SCTP) | (1ULL << OVS_KEY_ATTR_ICMP) | (1ULL << OVS_KEY_ATTR_ICMPV6) | (1ULL << OVS_KEY_ATTR_ARP) | (1ULL << OVS_KEY_ATTR_ND) | (1ULL << OVS_KEY_ATTR_MPLS)); /* Always allowed mask fields. */ mask_allowed |= ((1ULL << OVS_KEY_ATTR_TUNNEL) | (1ULL << OVS_KEY_ATTR_IN_PORT) | (1ULL << OVS_KEY_ATTR_ETHERTYPE)); /* Check key attributes. */ if (match->key->eth.type == htons(ETH_P_ARP) || match->key->eth.type == htons(ETH_P_RARP)) { key_expected |= 1ULL << OVS_KEY_ATTR_ARP; if (match->mask && (match->mask->key.eth.type == htons(0xffff))) mask_allowed |= 1ULL << OVS_KEY_ATTR_ARP; } if (eth_p_mpls(match->key->eth.type)) { key_expected |= 1ULL << OVS_KEY_ATTR_MPLS; if (match->mask && (match->mask->key.eth.type == htons(0xffff))) mask_allowed |= 1ULL << OVS_KEY_ATTR_MPLS; } if (match->key->eth.type == htons(ETH_P_IP)) { key_expected |= 1ULL << OVS_KEY_ATTR_IPV4; if (match->mask && (match->mask->key.eth.type == htons(0xffff))) mask_allowed |= 1ULL << OVS_KEY_ATTR_IPV4; if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { if (match->key->ip.proto == IPPROTO_UDP) { key_expected |= 1ULL << OVS_KEY_ATTR_UDP; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP; } if (match->key->ip.proto == IPPROTO_SCTP) { key_expected |= 1ULL << OVS_KEY_ATTR_SCTP; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP; } if (match->key->ip.proto == IPPROTO_TCP) { key_expected |= 1ULL << OVS_KEY_ATTR_TCP; key_expected |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS; if (match->mask && (match->mask->key.ip.proto == 0xff)) { mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP; mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS; } } if (match->key->ip.proto == IPPROTO_ICMP) { key_expected |= 1ULL << OVS_KEY_ATTR_ICMP; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_ICMP; } } } if (match->key->eth.type == htons(ETH_P_IPV6)) { key_expected |= 1ULL << OVS_KEY_ATTR_IPV6; if (match->mask && (match->mask->key.eth.type == htons(0xffff))) mask_allowed |= 1ULL << OVS_KEY_ATTR_IPV6; if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { if (match->key->ip.proto == IPPROTO_UDP) { key_expected |= 1ULL << OVS_KEY_ATTR_UDP; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP; } if (match->key->ip.proto == IPPROTO_SCTP) { key_expected |= 1ULL << OVS_KEY_ATTR_SCTP; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP; } if (match->key->ip.proto == IPPROTO_TCP) { key_expected |= 1ULL << OVS_KEY_ATTR_TCP; key_expected |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS; if (match->mask && (match->mask->key.ip.proto == 0xff)) { mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP; mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS; } } if (match->key->ip.proto == IPPROTO_ICMPV6) { key_expected |= 1ULL << OVS_KEY_ATTR_ICMPV6; if (match->mask && (match->mask->key.ip.proto == 0xff)) mask_allowed |= 1ULL << OVS_KEY_ATTR_ICMPV6; if (match->key->tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { key_expected |= 1ULL << OVS_KEY_ATTR_ND; if (match->mask && (match->mask->key.tp.src == htons(0xff))) mask_allowed |= 1ULL << OVS_KEY_ATTR_ND; } } } } if ((key_attrs & key_expected) != key_expected) { /* Key attributes check failed. */ OVS_NLERR(log, "Missing key (keys=%llx, expected=%llx)", (unsigned long long)key_attrs, (unsigned long long)key_expected); return false; } if ((mask_attrs & mask_allowed) != mask_attrs) { /* Mask attributes check failed. */ OVS_NLERR(log, "Unexpected mask (mask=%llx, allowed=%llx)", (unsigned long long)mask_attrs, (unsigned long long)mask_allowed); return false; } return true; } size_t ovs_tun_key_attr_size(void) { /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider * updating this function. */ return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */ + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */ /* OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS is mutually exclusive with * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it. */ + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */ + nla_total_size(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */ } size_t ovs_key_attr_size(void) { /* Whenever adding new OVS_KEY_ FIELDS, we should consider * updating this function. */ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 26); return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ + ovs_tun_key_attr_size() + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */ + nla_total_size(4) /* OVS_KEY_ATTR_CT_STATE */ + nla_total_size(2) /* OVS_KEY_ATTR_CT_ZONE */ + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */ + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */ + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */ + nla_total_size(28); /* OVS_KEY_ATTR_ND */ } static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = { [OVS_VXLAN_EXT_GBP] = { .len = sizeof(u32) }, }; static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { [OVS_TUNNEL_KEY_ATTR_ID] = { .len = sizeof(u64) }, [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = { .len = sizeof(u32) }, [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = { .len = sizeof(u32) }, [OVS_TUNNEL_KEY_ATTR_TOS] = { .len = 1 }, [OVS_TUNNEL_KEY_ATTR_TTL] = { .len = 1 }, [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_CSUM] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_TP_SRC] = { .len = sizeof(u16) }, [OVS_TUNNEL_KEY_ATTR_TP_DST] = { .len = sizeof(u16) }, [OVS_TUNNEL_KEY_ATTR_OAM] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = OVS_ATTR_VARIABLE }, [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = OVS_ATTR_NESTED, .next = ovs_vxlan_ext_key_lens }, }; /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = { .len = OVS_ATTR_NESTED }, [OVS_KEY_ATTR_PRIORITY] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_IN_PORT] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_SKB_MARK] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) }, [OVS_KEY_ATTR_VLAN] = { .len = sizeof(__be16) }, [OVS_KEY_ATTR_ETHERTYPE] = { .len = sizeof(__be16) }, [OVS_KEY_ATTR_IPV4] = { .len = sizeof(struct ovs_key_ipv4) }, [OVS_KEY_ATTR_IPV6] = { .len = sizeof(struct ovs_key_ipv6) }, [OVS_KEY_ATTR_TCP] = { .len = sizeof(struct ovs_key_tcp) }, [OVS_KEY_ATTR_TCP_FLAGS] = { .len = sizeof(__be16) }, [OVS_KEY_ATTR_UDP] = { .len = sizeof(struct ovs_key_udp) }, [OVS_KEY_ATTR_SCTP] = { .len = sizeof(struct ovs_key_sctp) }, [OVS_KEY_ATTR_ICMP] = { .len = sizeof(struct ovs_key_icmp) }, [OVS_KEY_ATTR_ICMPV6] = { .len = sizeof(struct ovs_key_icmpv6) }, [OVS_KEY_ATTR_ARP] = { .len = sizeof(struct ovs_key_arp) }, [OVS_KEY_ATTR_ND] = { .len = sizeof(struct ovs_key_nd) }, [OVS_KEY_ATTR_RECIRC_ID] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_DP_HASH] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_TUNNEL] = { .len = OVS_ATTR_NESTED, .next = ovs_tunnel_key_lens, }, [OVS_KEY_ATTR_MPLS] = { .len = sizeof(struct ovs_key_mpls) }, [OVS_KEY_ATTR_CT_STATE] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_CT_ZONE] = { .len = sizeof(u16) }, [OVS_KEY_ATTR_CT_MARK] = { .len = sizeof(u32) }, [OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) }, }; static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) { return expected_len == attr_len || expected_len == OVS_ATTR_NESTED || expected_len == OVS_ATTR_VARIABLE; } static bool is_all_zero(const u8 *fp, size_t size) { int i; if (!fp) return false; for (i = 0; i < size; i++) if (fp[i]) return false; return true; } static int __parse_flow_nlattrs(const struct nlattr *attr, const struct nlattr *a[], u64 *attrsp, bool log, bool nz) { const struct nlattr *nla; u64 attrs; int rem; attrs = *attrsp; nla_for_each_nested(nla, attr, rem) { u16 type = nla_type(nla); int expected_len; if (type > OVS_KEY_ATTR_MAX) { OVS_NLERR(log, "Key type %d is out of range max %d", type, OVS_KEY_ATTR_MAX); return -EINVAL; } if (attrs & (1ULL << type)) { OVS_NLERR(log, "Duplicate key (type %d).", type); return -EINVAL; } expected_len = ovs_key_lens[type].len; if (!check_attr_len(nla_len(nla), expected_len)) { OVS_NLERR(log, "Key %d has unexpected len %d expected %d", type, nla_len(nla), expected_len); return -EINVAL; } if (!nz || !is_all_zero(nla_data(nla), expected_len)) { attrs |= 1ULL << type; a[type] = nla; } } if (rem) { OVS_NLERR(log, "Message has %d unknown bytes.", rem); return -EINVAL; } *attrsp = attrs; return 0; } static int parse_flow_mask_nlattrs(const struct nlattr *attr, const struct nlattr *a[], u64 *attrsp, bool log) { return __parse_flow_nlattrs(attr, a, attrsp, log, true); } static int parse_flow_nlattrs(const struct nlattr *attr, const struct nlattr *a[], u64 *attrsp, bool log) { return __parse_flow_nlattrs(attr, a, attrsp, log, false); } static int genev_tun_opt_from_nlattr(const struct nlattr *a, struct sw_flow_match *match, bool is_mask, bool log) { unsigned long opt_key_offset; if (nla_len(a) > sizeof(match->key->tun_opts)) { OVS_NLERR(log, "Geneve option length err (len %d, max %zu).", nla_len(a), sizeof(match->key->tun_opts)); return -EINVAL; } if (nla_len(a) % 4 != 0) { OVS_NLERR(log, "Geneve opt len %d is not a multiple of 4.", nla_len(a)); return -EINVAL; } /* We need to record the length of the options passed * down, otherwise packets with the same format but * additional options will be silently matched. */ if (!is_mask) { SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a), false); } else { /* This is somewhat unusual because it looks at * both the key and mask while parsing the * attributes (and by extension assumes the key * is parsed first). Normally, we would verify * that each is the correct length and that the * attributes line up in the validate function. * However, that is difficult because this is * variable length and we won't have the * information later. */ if (match->key->tun_opts_len != nla_len(a)) { OVS_NLERR(log, "Geneve option len %d != mask len %d", match->key->tun_opts_len, nla_len(a)); return -EINVAL; } SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, true); } opt_key_offset = TUN_METADATA_OFFSET(nla_len(a)); SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, nla_data(a), nla_len(a), is_mask); return 0; } static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr, struct sw_flow_match *match, bool is_mask, bool log) { struct nlattr *a; int rem; unsigned long opt_key_offset; struct vxlan_metadata opts; BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts)); memset(&opts, 0, sizeof(opts)); nla_for_each_nested(a, attr, rem) { int type = nla_type(a); if (type > OVS_VXLAN_EXT_MAX) { OVS_NLERR(log, "VXLAN extension %d out of range max %d", type, OVS_VXLAN_EXT_MAX); return -EINVAL; } if (!check_attr_len(nla_len(a), ovs_vxlan_ext_key_lens[type].len)) { OVS_NLERR(log, "VXLAN extension %d has unexpected len %d expected %d", type, nla_len(a), ovs_vxlan_ext_key_lens[type].len); return -EINVAL; } switch (type) { case OVS_VXLAN_EXT_GBP: opts.gbp = nla_get_u32(a); break; default: OVS_NLERR(log, "Unknown VXLAN extension attribute %d", type); return -EINVAL; } } if (rem) { OVS_NLERR(log, "VXLAN extension message has %d unknown bytes.", rem); return -EINVAL; } if (!is_mask) SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), false); else SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, true); opt_key_offset = TUN_METADATA_OFFSET(sizeof(opts)); SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, &opts, sizeof(opts), is_mask); return 0; } static int ipv4_tun_from_nlattr(const struct nlattr *attr, struct sw_flow_match *match, bool is_mask, bool log) { struct nlattr *a; int rem; bool ttl = false; __be16 tun_flags = 0; int opts_type = 0; nla_for_each_nested(a, attr, rem) { int type = nla_type(a); int err; if (type > OVS_TUNNEL_KEY_ATTR_MAX) { OVS_NLERR(log, "Tunnel attr %d out of range max %d", type, OVS_TUNNEL_KEY_ATTR_MAX); return -EINVAL; } if (!check_attr_len(nla_len(a), ovs_tunnel_key_lens[type].len)) { OVS_NLERR(log, "Tunnel attr %d has unexpected len %d expected %d", type, nla_len(a), ovs_tunnel_key_lens[type].len); return -EINVAL; } switch (type) { case OVS_TUNNEL_KEY_ATTR_ID: SW_FLOW_KEY_PUT(match, tun_key.tun_id, nla_get_be64(a), is_mask); tun_flags |= TUNNEL_KEY; break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.src, nla_get_in_addr(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: SW_FLOW_KEY_PUT(match, tun_key.u.ipv4.dst, nla_get_in_addr(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TOS: SW_FLOW_KEY_PUT(match, tun_key.tos, nla_get_u8(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TTL: SW_FLOW_KEY_PUT(match, tun_key.ttl, nla_get_u8(a), is_mask); ttl = true; break; case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: tun_flags |= TUNNEL_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: tun_flags |= TUNNEL_CSUM; break; case OVS_TUNNEL_KEY_ATTR_TP_SRC: SW_FLOW_KEY_PUT(match, tun_key.tp_src, nla_get_be16(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TP_DST: SW_FLOW_KEY_PUT(match, tun_key.tp_dst, nla_get_be16(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_OAM: tun_flags |= TUNNEL_OAM; break; case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: if (opts_type) { OVS_NLERR(log, "Multiple metadata blocks provided"); return -EINVAL; } err = genev_tun_opt_from_nlattr(a, match, is_mask, log); if (err) return err; tun_flags |= TUNNEL_GENEVE_OPT; opts_type = type; break; case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: if (opts_type) { OVS_NLERR(log, "Multiple metadata blocks provided"); return -EINVAL; } err = vxlan_tun_opt_from_nlattr(a, match, is_mask, log); if (err) return err; tun_flags |= TUNNEL_VXLAN_OPT; opts_type = type; break; default: OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d", type); return -EINVAL; } } SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); if (rem > 0) { OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.", rem); return -EINVAL; } if (!is_mask) { if (!match->key->tun_key.u.ipv4.dst) { OVS_NLERR(log, "IPv4 tunnel dst address is zero"); return -EINVAL; } if (!ttl) { OVS_NLERR(log, "IPv4 tunnel TTL not specified."); return -EINVAL; } } return opts_type; } static int vxlan_opt_to_nlattr(struct sk_buff *skb, const void *tun_opts, int swkey_tun_opts_len) { const struct vxlan_metadata *opts = tun_opts; struct nlattr *nla; nla = nla_nest_start(skb, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS); if (!nla) return -EMSGSIZE; if (nla_put_u32(skb, OVS_VXLAN_EXT_GBP, opts->gbp) < 0) return -EMSGSIZE; nla_nest_end(skb, nla); return 0; } static int __ipv4_tun_to_nlattr(struct sk_buff *skb, const struct ip_tunnel_key *output, const void *tun_opts, int swkey_tun_opts_len) { if (output->tun_flags & TUNNEL_KEY && nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) return -EMSGSIZE; if (output->u.ipv4.src && nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->u.ipv4.src)) return -EMSGSIZE; if (output->u.ipv4.dst && nla_put_in_addr(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->u.ipv4.dst)) return -EMSGSIZE; if (output->tos && nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->tos)) return -EMSGSIZE; if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ttl)) return -EMSGSIZE; if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) return -EMSGSIZE; if ((output->tun_flags & TUNNEL_CSUM) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) return -EMSGSIZE; if (output->tp_src && nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_SRC, output->tp_src)) return -EMSGSIZE; if (output->tp_dst && nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_DST, output->tp_dst)) return -EMSGSIZE; if ((output->tun_flags & TUNNEL_OAM) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM)) return -EMSGSIZE; if (tun_opts) { if (output->tun_flags & TUNNEL_GENEVE_OPT && nla_put(skb, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, swkey_tun_opts_len, tun_opts)) return -EMSGSIZE; else if (output->tun_flags & TUNNEL_VXLAN_OPT && vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len)) return -EMSGSIZE; } return 0; } static int ipv4_tun_to_nlattr(struct sk_buff *skb, const struct ip_tunnel_key *output, const void *tun_opts, int swkey_tun_opts_len) { struct nlattr *nla; int err; nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL); if (!nla) return -EMSGSIZE; err = __ipv4_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len); if (err) return err; nla_nest_end(skb, nla); return 0; } int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb, const struct ip_tunnel_info *egress_tun_info, const void *egress_tun_opts) { return __ipv4_tun_to_nlattr(skb, &egress_tun_info->key, egress_tun_opts, egress_tun_info->options_len); } static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, u64 *attrs, const struct nlattr **a, bool is_mask, bool log) { if (*attrs & (1ULL << OVS_KEY_ATTR_DP_HASH)) { u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]); SW_FLOW_KEY_PUT(match, ovs_flow_hash, hash_val, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_DP_HASH); } if (*attrs & (1ULL << OVS_KEY_ATTR_RECIRC_ID)) { u32 recirc_id = nla_get_u32(a[OVS_KEY_ATTR_RECIRC_ID]); SW_FLOW_KEY_PUT(match, recirc_id, recirc_id, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_RECIRC_ID); } if (*attrs & (1ULL << OVS_KEY_ATTR_PRIORITY)) { SW_FLOW_KEY_PUT(match, phy.priority, nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_PRIORITY); } if (*attrs & (1ULL << OVS_KEY_ATTR_IN_PORT)) { u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); if (is_mask) { in_port = 0xffffffff; /* Always exact match in_port. */ } else if (in_port >= DP_MAX_PORTS) { OVS_NLERR(log, "Port %d exceeds max allowable %d", in_port, DP_MAX_PORTS); return -EINVAL; } SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_IN_PORT); } else if (!is_mask) { SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask); } if (*attrs & (1ULL << OVS_KEY_ATTR_SKB_MARK)) { uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK); } if (*attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) { if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, is_mask, log) < 0) return -EINVAL; *attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL); } if (*attrs & (1 << OVS_KEY_ATTR_CT_STATE) && ovs_ct_verify(net, OVS_KEY_ATTR_CT_STATE)) { u32 ct_state = nla_get_u32(a[OVS_KEY_ATTR_CT_STATE]); if (ct_state & ~CT_SUPPORTED_MASK) { OVS_NLERR(log, "ct_state flags %08x unsupported", ct_state); return -EINVAL; } SW_FLOW_KEY_PUT(match, ct.state, ct_state, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_STATE); } if (*attrs & (1 << OVS_KEY_ATTR_CT_ZONE) && ovs_ct_verify(net, OVS_KEY_ATTR_CT_ZONE)) { u16 ct_zone = nla_get_u16(a[OVS_KEY_ATTR_CT_ZONE]); SW_FLOW_KEY_PUT(match, ct.zone, ct_zone, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_ZONE); } if (*attrs & (1 << OVS_KEY_ATTR_CT_MARK) && ovs_ct_verify(net, OVS_KEY_ATTR_CT_MARK)) { u32 mark = nla_get_u32(a[OVS_KEY_ATTR_CT_MARK]); SW_FLOW_KEY_PUT(match, ct.mark, mark, is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_MARK); } if (*attrs & (1 << OVS_KEY_ATTR_CT_LABELS) && ovs_ct_verify(net, OVS_KEY_ATTR_CT_LABELS)) { const struct ovs_key_ct_labels *cl; cl = nla_data(a[OVS_KEY_ATTR_CT_LABELS]); SW_FLOW_KEY_MEMCPY(match, ct.labels, cl->ct_labels, sizeof(*cl), is_mask); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS); } return 0; } static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, u64 attrs, const struct nlattr **a, bool is_mask, bool log) { int err; err = metadata_from_nlattrs(net, match, &attrs, a, is_mask, log); if (err) return err; if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) { const struct ovs_key_ethernet *eth_key; eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); SW_FLOW_KEY_MEMCPY(match, eth.src, eth_key->eth_src, ETH_ALEN, is_mask); SW_FLOW_KEY_MEMCPY(match, eth.dst, eth_key->eth_dst, ETH_ALEN, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERNET); } if (attrs & (1ULL << OVS_KEY_ATTR_VLAN)) { __be16 tci; tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); if (!(tci & htons(VLAN_TAG_PRESENT))) { if (is_mask) OVS_NLERR(log, "VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit."); else OVS_NLERR(log, "VLAN TCI does not have VLAN_TAG_PRESENT bit set."); return -EINVAL; } SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN); } if (attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) { __be16 eth_type; eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); if (is_mask) { /* Always exact match EtherType. */ eth_type = htons(0xffff); } else if (!eth_proto_is_802_3(eth_type)) { OVS_NLERR(log, "EtherType %x is less than min %x", ntohs(eth_type), ETH_P_802_3_MIN); return -EINVAL; } SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE); } else if (!is_mask) { SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); } if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) { OVS_NLERR(log, "IPv4 frag type %d is out of range max %d", ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; } SW_FLOW_KEY_PUT(match, ip.proto, ipv4_key->ipv4_proto, is_mask); SW_FLOW_KEY_PUT(match, ip.tos, ipv4_key->ipv4_tos, is_mask); SW_FLOW_KEY_PUT(match, ip.ttl, ipv4_key->ipv4_ttl, is_mask); SW_FLOW_KEY_PUT(match, ip.frag, ipv4_key->ipv4_frag, is_mask); SW_FLOW_KEY_PUT(match, ipv4.addr.src, ipv4_key->ipv4_src, is_mask); SW_FLOW_KEY_PUT(match, ipv4.addr.dst, ipv4_key->ipv4_dst, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_IPV4); } if (attrs & (1ULL << OVS_KEY_ATTR_IPV6)) { const struct ovs_key_ipv6 *ipv6_key; ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) { OVS_NLERR(log, "IPv6 frag type %d is out of range max %d", ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; } if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) { OVS_NLERR(log, "Invalid IPv6 flow label value (value=%x, max=%x).", ntohl(ipv6_key->ipv6_label), (1 << 20) - 1); return -EINVAL; } SW_FLOW_KEY_PUT(match, ipv6.label, ipv6_key->ipv6_label, is_mask); SW_FLOW_KEY_PUT(match, ip.proto, ipv6_key->ipv6_proto, is_mask); SW_FLOW_KEY_PUT(match, ip.tos, ipv6_key->ipv6_tclass, is_mask); SW_FLOW_KEY_PUT(match, ip.ttl, ipv6_key->ipv6_hlimit, is_mask); SW_FLOW_KEY_PUT(match, ip.frag, ipv6_key->ipv6_frag, is_mask); SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src, ipv6_key->ipv6_src, sizeof(match->key->ipv6.addr.src), is_mask); SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst, ipv6_key->ipv6_dst, sizeof(match->key->ipv6.addr.dst), is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_IPV6); } if (attrs & (1ULL << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); if (!is_mask && (arp_key->arp_op & htons(0xff00))) { OVS_NLERR(log, "Unknown ARP opcode (opcode=%d).", arp_key->arp_op); return -EINVAL; } SW_FLOW_KEY_PUT(match, ipv4.addr.src, arp_key->arp_sip, is_mask); SW_FLOW_KEY_PUT(match, ipv4.addr.dst, arp_key->arp_tip, is_mask); SW_FLOW_KEY_PUT(match, ip.proto, ntohs(arp_key->arp_op), is_mask); SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN, is_mask); SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ARP); } if (attrs & (1ULL << OVS_KEY_ATTR_MPLS)) { const struct ovs_key_mpls *mpls_key; mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]); SW_FLOW_KEY_PUT(match, mpls.top_lse, mpls_key->mpls_lse, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_MPLS); } if (attrs & (1ULL << OVS_KEY_ATTR_TCP)) { const struct ovs_key_tcp *tcp_key; tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); SW_FLOW_KEY_PUT(match, tp.src, tcp_key->tcp_src, is_mask); SW_FLOW_KEY_PUT(match, tp.dst, tcp_key->tcp_dst, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_TCP); } if (attrs & (1ULL << OVS_KEY_ATTR_TCP_FLAGS)) { SW_FLOW_KEY_PUT(match, tp.flags, nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_TCP_FLAGS); } if (attrs & (1ULL << OVS_KEY_ATTR_UDP)) { const struct ovs_key_udp *udp_key; udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); SW_FLOW_KEY_PUT(match, tp.src, udp_key->udp_src, is_mask); SW_FLOW_KEY_PUT(match, tp.dst, udp_key->udp_dst, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_UDP); } if (attrs & (1ULL << OVS_KEY_ATTR_SCTP)) { const struct ovs_key_sctp *sctp_key; sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); SW_FLOW_KEY_PUT(match, tp.src, sctp_key->sctp_src, is_mask); SW_FLOW_KEY_PUT(match, tp.dst, sctp_key->sctp_dst, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_SCTP); } if (attrs & (1ULL << OVS_KEY_ATTR_ICMP)) { const struct ovs_key_icmp *icmp_key; icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); SW_FLOW_KEY_PUT(match, tp.src, htons(icmp_key->icmp_type), is_mask); SW_FLOW_KEY_PUT(match, tp.dst, htons(icmp_key->icmp_code), is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ICMP); } if (attrs & (1ULL << OVS_KEY_ATTR_ICMPV6)) { const struct ovs_key_icmpv6 *icmpv6_key; icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); SW_FLOW_KEY_PUT(match, tp.src, htons(icmpv6_key->icmpv6_type), is_mask); SW_FLOW_KEY_PUT(match, tp.dst, htons(icmpv6_key->icmpv6_code), is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ICMPV6); } if (attrs & (1ULL << OVS_KEY_ATTR_ND)) { const struct ovs_key_nd *nd_key; nd_key = nla_data(a[OVS_KEY_ATTR_ND]); SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target, nd_key->nd_target, sizeof(match->key->ipv6.nd.target), is_mask); SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN, is_mask); SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN, is_mask); attrs &= ~(1ULL << OVS_KEY_ATTR_ND); } if (attrs != 0) { OVS_NLERR(log, "Unknown key attributes %llx", (unsigned long long)attrs); return -EINVAL; } return 0; } static void nlattr_set(struct nlattr *attr, u8 val, const struct ovs_len_tbl *tbl) { struct nlattr *nla; int rem; /* The nlattr stream should already have been validated */ nla_for_each_nested(nla, attr, rem) { if (tbl[nla_type(nla)].len == OVS_ATTR_NESTED) nlattr_set(nla, val, tbl[nla_type(nla)].next ? : tbl); else memset(nla_data(nla), val, nla_len(nla)); if (nla_type(nla) == OVS_KEY_ATTR_CT_STATE) *(u32 *)nla_data(nla) &= CT_SUPPORTED_MASK; } } static void mask_set_nlattr(struct nlattr *attr, u8 val) { nlattr_set(attr, val, ovs_key_lens); } /** * ovs_nla_get_match - parses Netlink attributes into a flow key and * mask. In case the 'mask' is NULL, the flow is treated as exact match * flow. Otherwise, it is treated as a wildcarded flow, except the mask * does not include any don't care bit. * @net: Used to determine per-namespace field support. * @match: receives the extracted flow match information. * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. The fields should of the packet that triggered the creation * of this flow. * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink * attribute specifies the mask field of the wildcarded flow. * @log: Boolean to allow kernel error logging. Normally true, but when * probing for feature compatibility this should be passed in as false to * suppress unnecessary error logging. */ int ovs_nla_get_match(struct net *net, struct sw_flow_match *match, const struct nlattr *nla_key, const struct nlattr *nla_mask, bool log) { const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; const struct nlattr *encap; struct nlattr *newmask = NULL; u64 key_attrs = 0; u64 mask_attrs = 0; bool encap_valid = false; int err; err = parse_flow_nlattrs(nla_key, a, &key_attrs, log); if (err) return err; if ((key_attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) && (key_attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) && (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) { __be16 tci; if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) && (key_attrs & (1ULL << OVS_KEY_ATTR_ENCAP)))) { OVS_NLERR(log, "Invalid Vlan frame."); return -EINVAL; } key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE); tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); encap = a[OVS_KEY_ATTR_ENCAP]; key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP); encap_valid = true; if (tci & htons(VLAN_TAG_PRESENT)) { err = parse_flow_nlattrs(encap, a, &key_attrs, log); if (err) return err; } else if (!tci) { /* Corner case for truncated 802.1Q header. */ if (nla_len(encap)) { OVS_NLERR(log, "Truncated 802.1Q header has non-zero encap attribute."); return -EINVAL; } } else { OVS_NLERR(log, "Encap attr is set for non-VLAN frame"); return -EINVAL; } } err = ovs_key_from_nlattrs(net, match, key_attrs, a, false, log); if (err) return err; if (match->mask) { if (!nla_mask) { /* Create an exact match mask. We need to set to 0xff * all the 'match->mask' fields that have been touched * in 'match->key'. We cannot simply memset * 'match->mask', because padding bytes and fields not * specified in 'match->key' should be left to 0. * Instead, we use a stream of netlink attributes, * copied from 'key' and set to 0xff. * ovs_key_from_nlattrs() will take care of filling * 'match->mask' appropriately. */ newmask = kmemdup(nla_key, nla_total_size(nla_len(nla_key)), GFP_KERNEL); if (!newmask) return -ENOMEM; mask_set_nlattr(newmask, 0xff); /* The userspace does not send tunnel attributes that * are 0, but we should not wildcard them nonetheless. */ if (match->key->tun_key.u.ipv4.dst) SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true); nla_mask = newmask; } err = parse_flow_mask_nlattrs(nla_mask, a, &mask_attrs, log); if (err) goto free_newmask; /* Always match on tci. */ SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) { __be16 eth_type = 0; __be16 tci = 0; if (!encap_valid) { OVS_NLERR(log, "Encap mask attribute is set for non-VLAN frame."); err = -EINVAL; goto free_newmask; } mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP); if (a[OVS_KEY_ATTR_ETHERTYPE]) eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); if (eth_type == htons(0xffff)) { mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE); encap = a[OVS_KEY_ATTR_ENCAP]; err = parse_flow_mask_nlattrs(encap, a, &mask_attrs, log); if (err) goto free_newmask; } else { OVS_NLERR(log, "VLAN frames must have an exact match on the TPID (mask=%x).", ntohs(eth_type)); err = -EINVAL; goto free_newmask; } if (a[OVS_KEY_ATTR_VLAN]) tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); if (!(tci & htons(VLAN_TAG_PRESENT))) { OVS_NLERR(log, "VLAN tag present bit must have an exact match (tci_mask=%x).", ntohs(tci)); err = -EINVAL; goto free_newmask; } } err = ovs_key_from_nlattrs(net, match, mask_attrs, a, true, log); if (err) goto free_newmask; } if (!match_validate(match, key_attrs, mask_attrs, log)) err = -EINVAL; free_newmask: kfree(newmask); return err; } static size_t get_ufid_len(const struct nlattr *attr, bool log) { size_t len; if (!attr) return 0; len = nla_len(attr); if (len < 1 || len > MAX_UFID_LENGTH) { OVS_NLERR(log, "ufid size %u bytes exceeds the range (1, %d)", nla_len(attr), MAX_UFID_LENGTH); return 0; } return len; } /* Initializes 'flow->ufid', returning true if 'attr' contains a valid UFID, * or false otherwise. */ bool ovs_nla_get_ufid(struct sw_flow_id *sfid, const struct nlattr *attr, bool log) { sfid->ufid_len = get_ufid_len(attr, log); if (sfid->ufid_len) memcpy(sfid->ufid, nla_data(attr), sfid->ufid_len); return sfid->ufid_len; } int ovs_nla_get_identifier(struct sw_flow_id *sfid, const struct nlattr *ufid, const struct sw_flow_key *key, bool log) { struct sw_flow_key *new_key; if (ovs_nla_get_ufid(sfid, ufid, log)) return 0; /* If UFID was not provided, use unmasked key. */ new_key = kmalloc(sizeof(*new_key), GFP_KERNEL); if (!new_key) return -ENOMEM; memcpy(new_key, key, sizeof(*key)); sfid->unmasked_key = new_key; return 0; } u32 ovs_nla_get_ufid_flags(const struct nlattr *attr) { return attr ? nla_get_u32(attr) : 0; } /** * ovs_nla_get_flow_metadata - parses Netlink attributes into a flow key. * @key: Receives extracted in_port, priority, tun_key and skb_mark. * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. * @log: Boolean to allow kernel error logging. Normally true, but when * probing for feature compatibility this should be passed in as false to * suppress unnecessary error logging. * * This parses a series of Netlink attributes that form a flow key, which must * take the same form accepted by flow_from_nlattrs(), but only enough of it to * get the metadata, that is, the parts of the flow key that cannot be * extracted from the packet itself. */ int ovs_nla_get_flow_metadata(struct net *net, const struct nlattr *attr, struct sw_flow_key *key, bool log) { const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; struct sw_flow_match match; u64 attrs = 0; int err; err = parse_flow_nlattrs(attr, a, &attrs, log); if (err) return -EINVAL; memset(&match, 0, sizeof(match)); match.key = key; memset(key, 0, OVS_SW_FLOW_KEY_METADATA_SIZE); memset(&key->ct, 0, sizeof(key->ct)); key->phy.in_port = DP_MAX_PORTS; return metadata_from_nlattrs(net, &match, &attrs, a, false, log); } static int __ovs_nla_put_key(const struct sw_flow_key *swkey, const struct sw_flow_key *output, bool is_mask, struct sk_buff *skb) { struct ovs_key_ethernet *eth_key; struct nlattr *nla, *encap; if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id)) goto nla_put_failure; if (nla_put_u32(skb, OVS_KEY_ATTR_DP_HASH, output->ovs_flow_hash)) goto nla_put_failure; if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) goto nla_put_failure; if ((swkey->tun_key.u.ipv4.dst || is_mask)) { const void *opts = NULL; if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT) opts = TUN_METADATA_OPTS(output, swkey->tun_opts_len); if (ipv4_tun_to_nlattr(skb, &output->tun_key, opts, swkey->tun_opts_len)) goto nla_put_failure; } if (swkey->phy.in_port == DP_MAX_PORTS) { if (is_mask && (output->phy.in_port == 0xffff)) if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff)) goto nla_put_failure; } else { u16 upper_u16; upper_u16 = !is_mask ? 0 : 0xffff; if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, (upper_u16 << 16) | output->phy.in_port)) goto nla_put_failure; } if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark)) goto nla_put_failure; if (ovs_ct_put_key(output, skb)) goto nla_put_failure; nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (!nla) goto nla_put_failure; eth_key = nla_data(nla); ether_addr_copy(eth_key->eth_src, output->eth.src); ether_addr_copy(eth_key->eth_dst, output->eth.dst); if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { __be16 eth_type; eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) goto nla_put_failure; encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); if (!swkey->eth.tci) goto unencap; } else encap = NULL; if (swkey->eth.type == htons(ETH_P_802_2)) { /* * Ethertype 802.2 is represented in the netlink with omitted * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and * 0xffff in the mask attribute. Ethertype can also * be wildcarded. */ if (is_mask && output->eth.type) if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) goto nla_put_failure; goto unencap; } if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) goto nla_put_failure; if (swkey->eth.type == htons(ETH_P_IP)) { struct ovs_key_ipv4 *ipv4_key; nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key)); if (!nla) goto nla_put_failure; ipv4_key = nla_data(nla); ipv4_key->ipv4_src = output->ipv4.addr.src; ipv4_key->ipv4_dst = output->ipv4.addr.dst; ipv4_key->ipv4_proto = output->ip.proto; ipv4_key->ipv4_tos = output->ip.tos; ipv4_key->ipv4_ttl = output->ip.ttl; ipv4_key->ipv4_frag = output->ip.frag; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { struct ovs_key_ipv6 *ipv6_key; nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key)); if (!nla) goto nla_put_failure; ipv6_key = nla_data(nla); memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src, sizeof(ipv6_key->ipv6_src)); memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst, sizeof(ipv6_key->ipv6_dst)); ipv6_key->ipv6_label = output->ipv6.label; ipv6_key->ipv6_proto = output->ip.proto; ipv6_key->ipv6_tclass = output->ip.tos; ipv6_key->ipv6_hlimit = output->ip.ttl; ipv6_key->ipv6_frag = output->ip.frag; } else if (swkey->eth.type == htons(ETH_P_ARP) || swkey->eth.type == htons(ETH_P_RARP)) { struct ovs_key_arp *arp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key)); if (!nla) goto nla_put_failure; arp_key = nla_data(nla); memset(arp_key, 0, sizeof(struct ovs_key_arp)); arp_key->arp_sip = output->ipv4.addr.src; arp_key->arp_tip = output->ipv4.addr.dst; arp_key->arp_op = htons(output->ip.proto); ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha); ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha); } else if (eth_p_mpls(swkey->eth.type)) { struct ovs_key_mpls *mpls_key; nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key)); if (!nla) goto nla_put_failure; mpls_key = nla_data(nla); mpls_key->mpls_lse = output->mpls.top_lse; } if ((swkey->eth.type == htons(ETH_P_IP) || swkey->eth.type == htons(ETH_P_IPV6)) && swkey->ip.frag != OVS_FRAG_TYPE_LATER) { if (swkey->ip.proto == IPPROTO_TCP) { struct ovs_key_tcp *tcp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key)); if (!nla) goto nla_put_failure; tcp_key = nla_data(nla); tcp_key->tcp_src = output->tp.src; tcp_key->tcp_dst = output->tp.dst; if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, output->tp.flags)) goto nla_put_failure; } else if (swkey->ip.proto == IPPROTO_UDP) { struct ovs_key_udp *udp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key)); if (!nla) goto nla_put_failure; udp_key = nla_data(nla); udp_key->udp_src = output->tp.src; udp_key->udp_dst = output->tp.dst; } else if (swkey->ip.proto == IPPROTO_SCTP) { struct ovs_key_sctp *sctp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key)); if (!nla) goto nla_put_failure; sctp_key = nla_data(nla); sctp_key->sctp_src = output->tp.src; sctp_key->sctp_dst = output->tp.dst; } else if (swkey->eth.type == htons(ETH_P_IP) && swkey->ip.proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key)); if (!nla) goto nla_put_failure; icmp_key = nla_data(nla); icmp_key->icmp_type = ntohs(output->tp.src); icmp_key->icmp_code = ntohs(output->tp.dst); } else if (swkey->eth.type == htons(ETH_P_IPV6) && swkey->ip.proto == IPPROTO_ICMPV6) { struct ovs_key_icmpv6 *icmpv6_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6, sizeof(*icmpv6_key)); if (!nla) goto nla_put_failure; icmpv6_key = nla_data(nla); icmpv6_key->icmpv6_type = ntohs(output->tp.src); icmpv6_key->icmpv6_code = ntohs(output->tp.dst); if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { struct ovs_key_nd *nd_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key)); if (!nla) goto nla_put_failure; nd_key = nla_data(nla); memcpy(nd_key->nd_target, &output->ipv6.nd.target, sizeof(nd_key->nd_target)); ether_addr_copy(nd_key->nd_sll, output->ipv6.nd.sll); ether_addr_copy(nd_key->nd_tll, output->ipv6.nd.tll); } } } unencap: if (encap) nla_nest_end(skb, encap); return 0; nla_put_failure: return -EMSGSIZE; } int ovs_nla_put_key(const struct sw_flow_key *swkey, const struct sw_flow_key *output, int attr, bool is_mask, struct sk_buff *skb) { int err; struct nlattr *nla; nla = nla_nest_start(skb, attr); if (!nla) return -EMSGSIZE; err = __ovs_nla_put_key(swkey, output, is_mask, skb); if (err) return err; nla_nest_end(skb, nla); return 0; } /* Called with ovs_mutex or RCU read lock. */ int ovs_nla_put_identifier(const struct sw_flow *flow, struct sk_buff *skb) { if (ovs_identifier_is_ufid(&flow->id)) return nla_put(skb, OVS_FLOW_ATTR_UFID, flow->id.ufid_len, flow->id.ufid); return ovs_nla_put_key(flow->id.unmasked_key, flow->id.unmasked_key, OVS_FLOW_ATTR_KEY, false, skb); } /* Called with ovs_mutex or RCU read lock. */ int ovs_nla_put_masked_key(const struct sw_flow *flow, struct sk_buff *skb) { return ovs_nla_put_key(&flow->key, &flow->key, OVS_FLOW_ATTR_KEY, false, skb); } /* Called with ovs_mutex or RCU read lock. */ int ovs_nla_put_mask(const struct sw_flow *flow, struct sk_buff *skb) { return ovs_nla_put_key(&flow->key, &flow->mask->key, OVS_FLOW_ATTR_MASK, true, skb); } #define MAX_ACTIONS_BUFSIZE (32 * 1024) static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log) { struct sw_flow_actions *sfa; if (size > MAX_ACTIONS_BUFSIZE) { OVS_NLERR(log, "Flow action size %u bytes exceeds max", size); return ERR_PTR(-EINVAL); } sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); if (!sfa) return ERR_PTR(-ENOMEM); sfa->actions_len = 0; return sfa; } static void ovs_nla_free_set_action(const struct nlattr *a) { const struct nlattr *ovs_key = nla_data(a); struct ovs_tunnel_info *ovs_tun; switch (nla_type(ovs_key)) { case OVS_KEY_ATTR_TUNNEL_INFO: ovs_tun = nla_data(ovs_key); ovs_dst_release((struct dst_entry *)ovs_tun->tun_dst); break; } } void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) { const struct nlattr *a; int rem; if (!sf_acts) return; nla_for_each_attr(a, sf_acts->actions, sf_acts->actions_len, rem) { switch (nla_type(a)) { case OVS_ACTION_ATTR_SET: ovs_nla_free_set_action(a); break; case OVS_ACTION_ATTR_CT: ovs_ct_free_action(a); break; } } kfree(sf_acts); } static void __ovs_nla_free_flow_actions(struct rcu_head *head) { ovs_nla_free_flow_actions(container_of(head, struct sw_flow_actions, rcu)); } /* Schedules 'sf_acts' to be freed after the next RCU grace period. * The caller must hold rcu_read_lock for this to be sensible. */ void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *sf_acts) { call_rcu(&sf_acts->rcu, __ovs_nla_free_flow_actions); } static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len, bool log) { struct sw_flow_actions *acts; int new_acts_size; size_t req_size = NLA_ALIGN(attr_len); int next_offset = offsetof(struct sw_flow_actions, actions) + (*sfa)->actions_len; if (req_size <= (ksize(*sfa) - next_offset)) goto out; new_acts_size = max(next_offset + req_size, ksize(*sfa) * 2); if (new_acts_size > MAX_ACTIONS_BUFSIZE) { if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) return ERR_PTR(-EMSGSIZE); new_acts_size = MAX_ACTIONS_BUFSIZE; } acts = nla_alloc_flow_actions(new_acts_size, log); if (IS_ERR(acts)) return (void *)acts; memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len); acts->actions_len = (*sfa)->actions_len; acts->orig_len = (*sfa)->orig_len; kfree(*sfa); *sfa = acts; out: (*sfa)->actions_len += req_size; return (struct nlattr *) ((unsigned char *)(*sfa) + next_offset); } static struct nlattr *__add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len, bool log) { struct nlattr *a; a = reserve_sfa_size(sfa, nla_attr_size(len), log); if (IS_ERR(a)) return a; a->nla_type = attrtype; a->nla_len = nla_attr_size(len); if (data) memcpy(nla_data(a), data, len); memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len)); return a; } int ovs_nla_add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len, bool log) { struct nlattr *a; a = __add_action(sfa, attrtype, data, len, log); if (IS_ERR(a)) return PTR_ERR(a); return 0; } static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype, bool log) { int used = (*sfa)->actions_len; int err; err = ovs_nla_add_action(sfa, attrtype, NULL, 0, log); if (err) return err; return used; } static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset) { struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset); a->nla_len = sfa->actions_len - st_offset; } static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, int depth, struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log); static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, int depth, struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log) { const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; int rem, start, err, st_acts; memset(attrs, 0, sizeof(attrs)); nla_for_each_nested(a, attr, rem) { int type = nla_type(a); if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type]) return -EINVAL; attrs[type] = a; } if (rem) return -EINVAL; probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY]; if (!probability || nla_len(probability) != sizeof(u32)) return -EINVAL; actions = attrs[OVS_SAMPLE_ATTR_ACTIONS]; if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) return -EINVAL; /* validation done, copy sample action. */ start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE, log); if (start < 0) return start; err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32), log); if (err) return err; st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS, log); if (st_acts < 0) return st_acts; err = __ovs_nla_copy_actions(net, actions, key, depth + 1, sfa, eth_type, vlan_tci, log); if (err) return err; add_nested_action_end(*sfa, st_acts); add_nested_action_end(*sfa, start); return 0; } void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key, struct sw_flow_mask *mask) { memset(match, 0, sizeof(*match)); match->key = key; match->mask = mask; memset(key, 0, sizeof(*key)); if (mask) { memset(&mask->key, 0, sizeof(mask->key)); mask->range.start = mask->range.end = 0; } } static int validate_geneve_opts(struct sw_flow_key *key) { struct geneve_opt *option; int opts_len = key->tun_opts_len; bool crit_opt = false; option = (struct geneve_opt *)TUN_METADATA_OPTS(key, key->tun_opts_len); while (opts_len > 0) { int len; if (opts_len < sizeof(*option)) return -EINVAL; len = sizeof(*option) + option->length * 4; if (len > opts_len) return -EINVAL; crit_opt |= !!(option->type & GENEVE_CRIT_OPT_TYPE); option = (struct geneve_opt *)((u8 *)option + len); opts_len -= len; }; key->tun_key.tun_flags |= crit_opt ? TUNNEL_CRIT_OPT : 0; return 0; } static int validate_and_copy_set_tun(const struct nlattr *attr, struct sw_flow_actions **sfa, bool log) { struct sw_flow_match match; struct sw_flow_key key; struct metadata_dst *tun_dst; struct ip_tunnel_info *tun_info; struct ovs_tunnel_info *ovs_tun; struct nlattr *a; int err = 0, start, opts_type; ovs_match_init(&match, &key, NULL); opts_type = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log); if (opts_type < 0) return opts_type; if (key.tun_opts_len) { switch (opts_type) { case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: err = validate_geneve_opts(&key); if (err < 0) return err; break; case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: break; } }; start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET, log); if (start < 0) return start; tun_dst = metadata_dst_alloc(key.tun_opts_len, GFP_KERNEL); if (!tun_dst) return -ENOMEM; a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL, sizeof(*ovs_tun), log); if (IS_ERR(a)) { ovs_dst_release((struct dst_entry *)tun_dst); return PTR_ERR(a); } ovs_tun = nla_data(a); ovs_tun->tun_dst = tun_dst; tun_info = &tun_dst->u.tun_info; tun_info->mode = IP_TUNNEL_INFO_TX; tun_info->key = key.tun_key; /* We need to store the options in the action itself since * everything else will go away after flow setup. We can append * it to tun_info and then point there. */ ip_tunnel_info_opts_set(tun_info, TUN_METADATA_OPTS(&key, key.tun_opts_len), key.tun_opts_len); add_nested_action_end(*sfa, start); return err; } /* Return false if there are any non-masked bits set. * Mask follows data immediately, before any netlink padding. */ static bool validate_masked(u8 *data, int len) { u8 *mask = data + len; while (len--) if (*data++ & ~*mask++) return false; return true; } static int validate_set(const struct nlattr *a, const struct sw_flow_key *flow_key, struct sw_flow_actions **sfa, bool *skip_copy, __be16 eth_type, bool masked, bool log) { const struct nlattr *ovs_key = nla_data(a); int key_type = nla_type(ovs_key); size_t key_len; /* There can be only one key in a action */ if (nla_total_size(nla_len(ovs_key)) != nla_len(a)) return -EINVAL; key_len = nla_len(ovs_key); if (masked) key_len /= 2; if (key_type > OVS_KEY_ATTR_MAX || !check_attr_len(key_len, ovs_key_lens[key_type].len)) return -EINVAL; if (masked && !validate_masked(nla_data(ovs_key), key_len)) return -EINVAL; switch (key_type) { const struct ovs_key_ipv4 *ipv4_key; const struct ovs_key_ipv6 *ipv6_key; int err; case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: case OVS_KEY_ATTR_ETHERNET: break; case OVS_KEY_ATTR_TUNNEL: if (eth_p_mpls(eth_type)) return -EINVAL; if (masked) return -EINVAL; /* Masked tunnel set not supported. */ *skip_copy = true; err = validate_and_copy_set_tun(a, sfa, log); if (err) return err; break; case OVS_KEY_ATTR_IPV4: if (eth_type != htons(ETH_P_IP)) return -EINVAL; ipv4_key = nla_data(ovs_key); if (masked) { const struct ovs_key_ipv4 *mask = ipv4_key + 1; /* Non-writeable fields. */ if (mask->ipv4_proto || mask->ipv4_frag) return -EINVAL; } else { if (ipv4_key->ipv4_proto != flow_key->ip.proto) return -EINVAL; if (ipv4_key->ipv4_frag != flow_key->ip.frag) return -EINVAL; } break; case OVS_KEY_ATTR_IPV6: if (eth_type != htons(ETH_P_IPV6)) return -EINVAL; ipv6_key = nla_data(ovs_key); if (masked) { const struct ovs_key_ipv6 *mask = ipv6_key + 1; /* Non-writeable fields. */ if (mask->ipv6_proto || mask->ipv6_frag) return -EINVAL; /* Invalid bits in the flow label mask? */ if (ntohl(mask->ipv6_label) & 0xFFF00000) return -EINVAL; } else { if (ipv6_key->ipv6_proto != flow_key->ip.proto) return -EINVAL; if (ipv6_key->ipv6_frag != flow_key->ip.frag) return -EINVAL; } if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) return -EINVAL; break; case OVS_KEY_ATTR_TCP: if ((eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) || flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; break; case OVS_KEY_ATTR_UDP: if ((eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) || flow_key->ip.proto != IPPROTO_UDP) return -EINVAL; break; case OVS_KEY_ATTR_MPLS: if (!eth_p_mpls(eth_type)) return -EINVAL; break; case OVS_KEY_ATTR_SCTP: if ((eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6)) || flow_key->ip.proto != IPPROTO_SCTP) return -EINVAL; break; default: return -EINVAL; } /* Convert non-masked non-tunnel set actions to masked set actions. */ if (!masked && key_type != OVS_KEY_ATTR_TUNNEL) { int start, len = key_len * 2; struct nlattr *at; *skip_copy = true; start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET_TO_MASKED, log); if (start < 0) return start; at = __add_action(sfa, key_type, NULL, len, log); if (IS_ERR(at)) return PTR_ERR(at); memcpy(nla_data(at), nla_data(ovs_key), key_len); /* Key. */ memset(nla_data(at) + key_len, 0xff, key_len); /* Mask. */ /* Clear non-writeable bits from otherwise writeable fields. */ if (key_type == OVS_KEY_ATTR_IPV6) { struct ovs_key_ipv6 *mask = nla_data(at) + key_len; mask->ipv6_label &= htonl(0x000FFFFF); } add_nested_action_end(*sfa, start); } return 0; } static int validate_userspace(const struct nlattr *attr) { static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = { [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 }, [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC }, [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = {.type = NLA_U32 }, }; struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; int error; error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, attr, userspace_policy); if (error) return error; if (!a[OVS_USERSPACE_ATTR_PID] || !nla_get_u32(a[OVS_USERSPACE_ATTR_PID])) return -EINVAL; return 0; } static int copy_action(const struct nlattr *from, struct sw_flow_actions **sfa, bool log) { int totlen = NLA_ALIGN(from->nla_len); struct nlattr *to; to = reserve_sfa_size(sfa, from->nla_len, log); if (IS_ERR(to)) return PTR_ERR(to); memcpy(to, from, totlen); return 0; } static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, int depth, struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log) { const struct nlattr *a; int rem, err; if (depth >= SAMPLE_ACTION_DEPTH) return -EOVERFLOW; nla_for_each_nested(a, attr, rem) { /* Expected argument lengths, (u32)-1 for variable length. */ static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), [OVS_ACTION_ATTR_RECIRC] = sizeof(u32), [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, [OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls), [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16), [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), [OVS_ACTION_ATTR_POP_VLAN] = 0, [OVS_ACTION_ATTR_SET] = (u32)-1, [OVS_ACTION_ATTR_SET_MASKED] = (u32)-1, [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), [OVS_ACTION_ATTR_CT] = (u32)-1, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); bool skip_copy; if (type > OVS_ACTION_ATTR_MAX || (action_lens[type] != nla_len(a) && action_lens[type] != (u32)-1)) return -EINVAL; skip_copy = false; switch (type) { case OVS_ACTION_ATTR_UNSPEC: return -EINVAL; case OVS_ACTION_ATTR_USERSPACE: err = validate_userspace(a); if (err) return err; break; case OVS_ACTION_ATTR_OUTPUT: if (nla_get_u32(a) >= DP_MAX_PORTS) return -EINVAL; break; case OVS_ACTION_ATTR_HASH: { const struct ovs_action_hash *act_hash = nla_data(a); switch (act_hash->hash_alg) { case OVS_HASH_ALG_L4: break; default: return -EINVAL; } break; } case OVS_ACTION_ATTR_POP_VLAN: vlan_tci = htons(0); break; case OVS_ACTION_ATTR_PUSH_VLAN: vlan = nla_data(a); if (vlan->vlan_tpid != htons(ETH_P_8021Q)) return -EINVAL; if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT))) return -EINVAL; vlan_tci = vlan->vlan_tci; break; case OVS_ACTION_ATTR_RECIRC: break; case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nla_data(a); if (!eth_p_mpls(mpls->mpls_ethertype)) return -EINVAL; /* Prohibit push MPLS other than to a white list * for packets that have a known tag order. */ if (vlan_tci & htons(VLAN_TAG_PRESENT) || (eth_type != htons(ETH_P_IP) && eth_type != htons(ETH_P_IPV6) && eth_type != htons(ETH_P_ARP) && eth_type != htons(ETH_P_RARP) && !eth_p_mpls(eth_type))) return -EINVAL; eth_type = mpls->mpls_ethertype; break; } case OVS_ACTION_ATTR_POP_MPLS: if (vlan_tci & htons(VLAN_TAG_PRESENT) || !eth_p_mpls(eth_type)) return -EINVAL; /* Disallow subsequent L2.5+ set and mpls_pop actions * as there is no check here to ensure that the new * eth_type is valid and thus set actions could * write off the end of the packet or otherwise * corrupt it. * * Support for these actions is planned using packet * recirculation. */ eth_type = htons(0); break; case OVS_ACTION_ATTR_SET: err = validate_set(a, key, sfa, &skip_copy, eth_type, false, log); if (err) return err; break; case OVS_ACTION_ATTR_SET_MASKED: err = validate_set(a, key, sfa, &skip_copy, eth_type, true, log); if (err) return err; break; case OVS_ACTION_ATTR_SAMPLE: err = validate_and_copy_sample(net, a, key, depth, sfa, eth_type, vlan_tci, log); if (err) return err; skip_copy = true; break; case OVS_ACTION_ATTR_CT: err = ovs_ct_copy_action(net, a, key, sfa, log); if (err) return err; skip_copy = true; break; default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; } if (!skip_copy) { err = copy_action(a, sfa, log); if (err) return err; } } if (rem > 0) return -EINVAL; return 0; } /* 'key' must be the masked key. */ int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, struct sw_flow_actions **sfa, bool log) { int err; *sfa = nla_alloc_flow_actions(nla_len(attr), log); if (IS_ERR(*sfa)) return PTR_ERR(*sfa); (*sfa)->orig_len = nla_len(attr); err = __ovs_nla_copy_actions(net, attr, key, 0, sfa, key->eth.type, key->eth.tci, log); if (err) ovs_nla_free_flow_actions(*sfa); return err; } static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) { const struct nlattr *a; struct nlattr *start; int err = 0, rem; start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE); if (!start) return -EMSGSIZE; nla_for_each_nested(a, attr, rem) { int type = nla_type(a); struct nlattr *st_sample; switch (type) { case OVS_SAMPLE_ATTR_PROBABILITY: if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a))) return -EMSGSIZE; break; case OVS_SAMPLE_ATTR_ACTIONS: st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); if (!st_sample) return -EMSGSIZE; err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb); if (err) return err; nla_nest_end(skb, st_sample); break; } } nla_nest_end(skb, start); return err; } static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) { const struct nlattr *ovs_key = nla_data(a); int key_type = nla_type(ovs_key); struct nlattr *start; int err; switch (key_type) { case OVS_KEY_ATTR_TUNNEL_INFO: { struct ovs_tunnel_info *ovs_tun = nla_data(ovs_key); struct ip_tunnel_info *tun_info = &ovs_tun->tun_dst->u.tun_info; start = nla_nest_start(skb, OVS_ACTION_ATTR_SET); if (!start) return -EMSGSIZE; err = ipv4_tun_to_nlattr(skb, &tun_info->key, tun_info->options_len ? ip_tunnel_info_opts(tun_info) : NULL, tun_info->options_len); if (err) return err; nla_nest_end(skb, start); break; } default: if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key)) return -EMSGSIZE; break; } return 0; } static int masked_set_action_to_set_action_attr(const struct nlattr *a, struct sk_buff *skb) { const struct nlattr *ovs_key = nla_data(a); struct nlattr *nla; size_t key_len = nla_len(ovs_key) / 2; /* Revert the conversion we did from a non-masked set action to * masked set action. */ nla = nla_nest_start(skb, OVS_ACTION_ATTR_SET); if (!nla) return -EMSGSIZE; if (nla_put(skb, nla_type(ovs_key), key_len, nla_data(ovs_key))) return -EMSGSIZE; nla_nest_end(skb, nla); return 0; } int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb) { const struct nlattr *a; int rem, err; nla_for_each_attr(a, attr, len, rem) { int type = nla_type(a); switch (type) { case OVS_ACTION_ATTR_SET: err = set_action_to_attr(a, skb); if (err) return err; break; case OVS_ACTION_ATTR_SET_TO_MASKED: err = masked_set_action_to_set_action_attr(a, skb); if (err) return err; break; case OVS_ACTION_ATTR_SAMPLE: err = sample_action_to_attr(a, skb); if (err) return err; break; case OVS_ACTION_ATTR_CT: err = ovs_ct_action_to_attr(nla_data(a), skb); if (err) return err; break; default: if (nla_put(skb, type, nla_len(a), nla_data(a))) return -EMSGSIZE; break; } } return 0; } openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-stt.c0000644000000000000000000000013213534540071017560 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.561859176 openvswitch-2.5.9/datapath/vport-stt.c0000644000175000017500000000670013534540071021251 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport.h" #include "vport-netdev.h" #ifdef OVS_STT static struct vport_ops ovs_stt_vport_ops; /** * struct stt_port - Keeps track of open UDP ports * @dst_port: destination port. */ struct stt_port { u16 port_no; }; static inline struct stt_port *stt_vport(const struct vport *vport) { return vport_priv(vport); } static int stt_get_options(const struct vport *vport, struct sk_buff *skb) { struct stt_port *stt_port = stt_vport(vport); if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, stt_port->port_no)) return -EMSGSIZE; return 0; } static int stt_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { struct stt_port *stt_port = stt_vport(vport); struct net *net = ovs_dp_get_net(vport->dp); __be16 dport = htons(stt_port->port_no); __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), skb, IPPROTO_UDP, sport, dport); } static struct vport *stt_tnl_create(const struct vport_parms *parms) { struct net *net = ovs_dp_get_net(parms->dp); struct nlattr *options = parms->options; struct stt_port *stt_port; struct net_device *dev; struct vport *vport; struct nlattr *a; u16 dst_port; int err; if (!options) { err = -EINVAL; goto error; } a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); if (a && nla_len(a) == sizeof(u16)) { dst_port = nla_get_u16(a); } else { /* Require destination port from userspace. */ err = -EINVAL; goto error; } vport = ovs_vport_alloc(sizeof(struct stt_port), &ovs_stt_vport_ops, parms); if (IS_ERR(vport)) return vport; stt_port = stt_vport(vport); stt_port->port_no = dst_port; rtnl_lock(); dev = stt_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port); if (IS_ERR(dev)) { rtnl_unlock(); ovs_vport_free(vport); return ERR_CAST(dev); } dev_change_flags(dev, dev->flags | IFF_UP); rtnl_unlock(); return vport; error: return ERR_PTR(err); } static struct vport *stt_create(const struct vport_parms *parms) { struct vport *vport; vport = stt_tnl_create(parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static struct vport_ops ovs_stt_vport_ops = { .type = OVS_VPORT_TYPE_STT, .create = stt_create, .destroy = ovs_netdev_tunnel_destroy, .get_options = stt_get_options, .send = ovs_stt_xmit, .get_egress_tun_info = stt_get_egress_tun_info, }; static int __init ovs_stt_tnl_init(void) { return ovs_vport_ops_register(&ovs_stt_vport_ops); } static void __exit ovs_stt_tnl_exit(void) { ovs_vport_ops_unregister(&ovs_stt_vport_ops); } module_init(ovs_stt_tnl_init); module_exit(ovs_stt_tnl_exit); MODULE_DESCRIPTION("OVS: STT switching port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("vport-type-106"); #endif openvswitch-2.5.9/datapath/PaxHeaders.82075/datapath.h0000644000000000000000000000013213534540071017371 xustar0030 mtime=1567801401.245680025 30 atime=1567801402.053685959 30 ctime=1567801425.417858114 openvswitch-2.5.9/datapath/datapath.h0000644000175000017500000001463013534540071021063 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef DATAPATH_H #define DATAPATH_H 1 #include #include #include #include #include #include #include #include #include "compat.h" #include "flow.h" #include "flow_table.h" #include "vlan.h" #define DP_MAX_PORTS USHRT_MAX #define DP_VPORT_HASH_BUCKETS 1024 #define SAMPLE_ACTION_DEPTH 3 /** * struct dp_stats_percpu - per-cpu packet processing statistics for a given * datapath. * @n_hit: Number of received packets for which a matching flow was found in * the flow table. * @n_miss: Number of received packets that had no matching flow in the flow * table. The sum of @n_hit and @n_miss is the number of packets that have * been received by the datapath. * @n_lost: Number of received packets that had no matching flow in the flow * table that could not be sent to userspace (normally due to an overflow in * one of the datapath's queues). * @n_mask_hit: Number of masks looked up for flow match. * @n_mask_hit / (@n_hit + @n_missed) will be the average masks looked * up per packet. */ struct dp_stats_percpu { u64 n_hit; u64 n_missed; u64 n_lost; u64 n_mask_hit; struct u64_stats_sync syncp; }; /** * struct datapath - datapath for flow-based packet switching * @rcu: RCU callback head for deferred destruction. * @list_node: Element in global 'dps' list. * @table: flow table. * @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by * ovs_mutex and RCU. * @stats_percpu: Per-CPU datapath statistics. * @net: Reference to net namespace. * * Context: See the comment on locking at the top of datapath.c for additional * locking information. */ struct datapath { struct rcu_head rcu; struct list_head list_node; /* Flow table. */ struct flow_table table; /* Switch ports. */ struct hlist_head *ports; /* Stats. */ struct dp_stats_percpu __percpu *stats_percpu; /* Network namespace ref. */ possible_net_t net; u32 user_features; }; /** * struct ovs_skb_cb - OVS data in skb CB * @input_vport: The original vport packet came in on. This value is cached * when a packet is received by OVS. * @mru: The maximum received fragement size; 0 if the packet is not * fragmented. */ struct ovs_skb_cb { struct vport *input_vport; u16 mru; }; #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) /** * struct dp_upcall - metadata to include with a packet to send to userspace * @cmd: One of %OVS_PACKET_CMD_*. * @userdata: If nonnull, its variable-length value is passed to userspace as * %OVS_PACKET_ATTR_USERDATA. * @portid: Netlink portid to which packet should be sent. If @portid is 0 * then no packet is sent and the packet is accounted in the datapath's @n_lost * counter. * @egress_tun_info: If nonnull, becomes %OVS_PACKET_ATTR_EGRESS_TUN_KEY. * @mru: If not zero, Maximum received IP fragment size. */ struct dp_upcall_info { struct ip_tunnel_info *egress_tun_info; const void *egress_tun_opts; const struct nlattr *userdata; const struct nlattr *actions; int actions_len; u32 portid; u8 cmd; u16 mru; }; /** * struct ovs_net - Per net-namespace data for ovs. * @dps: List of datapaths to enable dumping them all out. * Protected by genl_mutex. */ struct ovs_net { struct list_head dps; struct work_struct dp_notify_work; /* Module reference for configuring conntrack. */ bool xt_label; }; extern int ovs_net_id; void ovs_lock(void); void ovs_unlock(void); #ifdef CONFIG_LOCKDEP int lockdep_ovsl_is_held(void); #else #define lockdep_ovsl_is_held() 1 #endif #define ASSERT_OVSL() WARN_ON(!lockdep_ovsl_is_held()) #define ovsl_dereference(p) \ rcu_dereference_protected(p, lockdep_ovsl_is_held()) #define rcu_dereference_ovsl(p) \ rcu_dereference_check(p, lockdep_ovsl_is_held()) static inline struct net *ovs_dp_get_net(const struct datapath *dp) { return rpl_read_pnet(&dp->net); } static inline void ovs_dp_set_net(struct datapath *dp, struct net *net) { rpl_write_pnet(&dp->net, net); } struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no); static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no) { WARN_ON_ONCE(!rcu_read_lock_held()); return ovs_lookup_vport(dp, port_no); } static inline struct vport *ovs_vport_ovsl_rcu(const struct datapath *dp, int port_no) { WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held()); return ovs_lookup_vport(dp, port_no); } static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_no) { ASSERT_OVSL(); return ovs_lookup_vport(dp, port_no); } extern struct notifier_block ovs_dp_device_notifier; extern struct genl_family dp_vport_genl_family; extern struct genl_multicast_group ovs_dp_vport_multicast_group; void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key); void ovs_dp_detach_port(struct vport *); int ovs_dp_upcall(struct datapath *, struct sk_buff *, const struct sw_flow_key *, const struct dp_upcall_info *); const char *ovs_dp_name(const struct datapath *dp); struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, u8 cmd); int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_actions *, struct sw_flow_key *); void ovs_dp_notify_wq(struct work_struct *work); int action_fifos_init(void); void action_fifos_exit(void); /* 'KEY' must not have any bits set outside of the 'MASK' */ #define OVS_MASKED(OLD, KEY, MASK) ((KEY) | ((OLD) & ~(MASK))) #define OVS_SET_MASKED(OLD, KEY, MASK) ((OLD) = OVS_MASKED(OLD, KEY, MASK)) #define OVS_NLERR(logging_allowed, fmt, ...) \ do { \ if (logging_allowed && net_ratelimit()) \ pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \ } while (0) #endif /* datapath.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/datapath.c0000644000000000000000000000013213534540071017364 xustar0030 mtime=1567801401.245680025 30 atime=1567801402.053685959 30 ctime=1567801425.517858851 openvswitch-2.5.9/datapath/datapath.c0000644000175000017500000016347213534540071021067 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "conntrack.h" #include "flow.h" #include "flow_table.h" #include "flow_netlink.h" #include "gso.h" #include "vlan.h" #include "vport-internal_dev.h" #include "vport-netdev.h" int ovs_net_id __read_mostly; EXPORT_SYMBOL_GPL(ovs_net_id); static struct genl_family dp_packet_genl_family; static struct genl_family dp_flow_genl_family; static struct genl_family dp_datapath_genl_family; static const struct nla_policy flow_policy[]; static struct genl_multicast_group ovs_dp_flow_multicast_group = { .name = OVS_FLOW_MCGROUP }; static struct genl_multicast_group ovs_dp_datapath_multicast_group = { .name = OVS_DATAPATH_MCGROUP }; struct genl_multicast_group ovs_dp_vport_multicast_group = { .name = OVS_VPORT_MCGROUP }; /* Check if need to build a reply message. * OVS userspace sets the NLM_F_ECHO flag if it needs the reply. */ static bool ovs_must_notify(struct genl_family *family, struct genl_info *info, unsigned int group) { return info->nlhdr->nlmsg_flags & NLM_F_ECHO || genl_has_listeners(family, genl_info_net(info), group); } static void ovs_notify(struct genl_family *family, struct genl_multicast_group *grp, struct sk_buff *skb, struct genl_info *info) { genl_notify(family, skb, genl_info_net(info), info->snd_portid, GROUP_ID(grp), info->nlhdr, GFP_KERNEL); } /** * DOC: Locking: * * All writes e.g. Writes to device state (add/remove datapath, port, set * operations on vports, etc.), Writes to other state (flow table * modifications, set miscellaneous datapath parameters, etc.) are protected * by ovs_lock. * * Reads are protected by RCU. * * There are a few special cases (mostly stats) that have their own * synchronization but they nest under all of above and don't interact with * each other. * * The RTNL lock nests inside ovs_mutex. */ static DEFINE_MUTEX(ovs_mutex); void ovs_lock(void) { mutex_lock(&ovs_mutex); } void ovs_unlock(void) { mutex_unlock(&ovs_mutex); } #ifdef CONFIG_LOCKDEP int lockdep_ovsl_is_held(void) { if (debug_locks) return lockdep_is_held(&ovs_mutex); else return 1; } EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held); #endif static int queue_gso_packets(struct datapath *dp, struct sk_buff *, const struct sw_flow_key *, const struct dp_upcall_info *); static int queue_userspace_packet(struct datapath *dp, struct sk_buff *, const struct sw_flow_key *, const struct dp_upcall_info *); /* Must be called with rcu_read_lock. */ static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex) { struct net_device *dev = dev_get_by_index_rcu(net, dp_ifindex); if (dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); if (vport) return vport->dp; } return NULL; } /* The caller must hold either ovs_mutex or rcu_read_lock to keep the * returned dp pointer valid. */ static inline struct datapath *get_dp(struct net *net, int dp_ifindex) { struct datapath *dp; WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held()); rcu_read_lock(); dp = get_dp_rcu(net, dp_ifindex); rcu_read_unlock(); return dp; } /* Must be called with rcu_read_lock or ovs_mutex. */ const char *ovs_dp_name(const struct datapath *dp) { struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL); return ovs_vport_name(vport); } static int get_dpifindex(const struct datapath *dp) { struct vport *local; int ifindex; rcu_read_lock(); local = ovs_vport_rcu(dp, OVSP_LOCAL); if (local) ifindex = local->dev->ifindex; else ifindex = 0; rcu_read_unlock(); return ifindex; } static void destroy_dp_rcu(struct rcu_head *rcu) { struct datapath *dp = container_of(rcu, struct datapath, rcu); ovs_flow_tbl_destroy(&dp->table); free_percpu(dp->stats_percpu); kfree(dp->ports); kfree(dp); } static struct hlist_head *vport_hash_bucket(const struct datapath *dp, u16 port_no) { return &dp->ports[port_no & (DP_VPORT_HASH_BUCKETS - 1)]; } /* Called with ovs_mutex or RCU read lock. */ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) { struct vport *vport; struct hlist_head *head; head = vport_hash_bucket(dp, port_no); hlist_for_each_entry_rcu(vport, head, dp_hash_node) { if (vport->port_no == port_no) return vport; } return NULL; } /* Called with ovs_mutex. */ static struct vport *new_vport(const struct vport_parms *parms) { struct vport *vport; vport = ovs_vport_add(parms); if (!IS_ERR(vport)) { struct datapath *dp = parms->dp; struct hlist_head *head = vport_hash_bucket(dp, vport->port_no); hlist_add_head_rcu(&vport->dp_hash_node, head); } return vport; } void ovs_dp_detach_port(struct vport *p) { ASSERT_OVSL(); /* First drop references to device. */ hlist_del_rcu(&p->dp_hash_node); /* Then destroy it. */ ovs_vport_del(p); } /* Must be called with rcu_read_lock. */ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) { const struct vport *p = OVS_CB(skb)->input_vport; struct datapath *dp = p->dp; struct sw_flow *flow; struct sw_flow_actions *sf_acts; struct dp_stats_percpu *stats; u64 *stats_counter; u32 n_mask_hit; stats = this_cpu_ptr(dp->stats_percpu); /* Look up flow. */ flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb), &n_mask_hit); if (unlikely(!flow)) { struct dp_upcall_info upcall; int error; memset(&upcall, 0, sizeof(upcall)); upcall.cmd = OVS_PACKET_CMD_MISS; upcall.portid = ovs_vport_find_upcall_portid(p, skb); upcall.mru = OVS_CB(skb)->mru; error = ovs_dp_upcall(dp, skb, key, &upcall); if (unlikely(error)) kfree_skb(skb); else consume_skb(skb); stats_counter = &stats->n_missed; goto out; } ovs_flow_stats_update(flow, key->tp.flags, skb); sf_acts = rcu_dereference(flow->sf_acts); ovs_execute_actions(dp, skb, sf_acts, key); stats_counter = &stats->n_hit; out: /* Update datapath statistics. */ u64_stats_update_begin(&stats->syncp); (*stats_counter)++; stats->n_mask_hit += n_mask_hit; u64_stats_update_end(&stats->syncp); } int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { struct dp_stats_percpu *stats; int err; if (upcall_info->portid == 0) { err = -ENOTCONN; goto err; } if (!skb_is_gso(skb)) err = queue_userspace_packet(dp, skb, key, upcall_info); else err = queue_gso_packets(dp, skb, key, upcall_info); if (err) goto err; return 0; err: stats = this_cpu_ptr(dp->stats_percpu); u64_stats_update_begin(&stats->syncp); stats->n_lost++; u64_stats_update_end(&stats->syncp); return err; } static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { unsigned short gso_type = skb_shinfo(skb)->gso_type; struct sw_flow_key later_key; struct sk_buff *segs, *nskb; struct ovs_skb_cb ovs_cb; int err; ovs_cb = *OVS_CB(skb); segs = __skb_gso_segment(skb, NETIF_F_SG, false); *OVS_CB(skb) = ovs_cb; if (IS_ERR(segs)) return PTR_ERR(segs); if (segs == NULL) return -EINVAL; if (gso_type & SKB_GSO_UDP) { /* The initial flow key extracted by ovs_flow_key_extract() * in this case is for a first fragment, so we need to * properly mark later fragments. */ later_key = *key; later_key.ip.frag = OVS_FRAG_TYPE_LATER; } /* Queue all of the segments. */ skb = segs; do { *OVS_CB(skb) = ovs_cb; if (gso_type & SKB_GSO_UDP && skb != segs) key = &later_key; err = queue_userspace_packet(dp, skb, key, upcall_info); if (err) break; } while ((skb = skb->next)); /* Free all of the segments. */ skb = segs; do { nskb = skb->next; if (err) kfree_skb(skb); else consume_skb(skb); } while ((skb = nskb)); return err; } static size_t upcall_msg_size(const struct dp_upcall_info *upcall_info, unsigned int hdrlen) { size_t size = NLMSG_ALIGN(sizeof(struct ovs_header)) + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */ + nla_total_size(ovs_key_attr_size()); /* OVS_PACKET_ATTR_KEY */ /* OVS_PACKET_ATTR_USERDATA */ if (upcall_info->userdata) size += NLA_ALIGN(upcall_info->userdata->nla_len); /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */ if (upcall_info->egress_tun_info) size += nla_total_size(ovs_tun_key_attr_size()); /* OVS_PACKET_ATTR_ACTIONS */ if (upcall_info->actions_len) size += nla_total_size(upcall_info->actions_len); /* OVS_PACKET_ATTR_MRU */ if (upcall_info->mru) size += nla_total_size(sizeof(upcall_info->mru)); return size; } static void pad_packet(struct datapath *dp, struct sk_buff *skb) { if (!(dp->user_features & OVS_DP_F_UNALIGNED)) { size_t plen = NLA_ALIGN(skb->len) - skb->len; if (plen > 0) memset(skb_put(skb, plen), 0, plen); } } static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { struct ovs_header *upcall; struct sk_buff *nskb = NULL; struct sk_buff *user_skb = NULL; /* to be queued to userspace */ struct nlattr *nla; struct genl_info info = { #ifdef HAVE_GENLMSG_NEW_UNICAST .dst_sk = ovs_dp_get_net(dp)->genl_sock, #endif .snd_portid = upcall_info->portid, }; size_t len; unsigned int hlen; int err, dp_ifindex; dp_ifindex = get_dpifindex(dp); if (!dp_ifindex) return -ENODEV; if (skb_vlan_tag_present(skb)) { nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) return -ENOMEM; nskb = vlan_insert_tag_set_proto(nskb, nskb->vlan_proto, skb_vlan_tag_get(nskb)); if (!nskb) return -ENOMEM; vlan_set_tci(nskb, 0); skb = nskb; } if (nla_attr_size(skb->len) > USHRT_MAX) { err = -EFBIG; goto out; } /* Complete checksum if needed */ if (skb->ip_summed == CHECKSUM_PARTIAL && (err = skb_checksum_help(skb))) goto out; /* Older versions of OVS user space enforce alignment of the last * Netlink attribute to NLA_ALIGNTO which would require extensive * padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features & OVS_DP_F_UNALIGNED) hlen = skb_zerocopy_headlen(skb); else hlen = skb->len; len = upcall_msg_size(upcall_info, hlen); user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC); if (!user_skb) { err = -ENOMEM; goto out; } upcall = genlmsg_put(user_skb, 0, 0, &dp_packet_genl_family, 0, upcall_info->cmd); upcall->dp_ifindex = dp_ifindex; err = ovs_nla_put_key(key, key, OVS_PACKET_ATTR_KEY, false, user_skb); BUG_ON(err); if (upcall_info->userdata) __nla_put(user_skb, OVS_PACKET_ATTR_USERDATA, nla_len(upcall_info->userdata), nla_data(upcall_info->userdata)); if (upcall_info->egress_tun_info) { nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY); err = ovs_nla_put_egress_tunnel_key(user_skb, upcall_info->egress_tun_info, upcall_info->egress_tun_opts); BUG_ON(err); nla_nest_end(user_skb, nla); } if (upcall_info->actions_len) { nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_ACTIONS); err = ovs_nla_put_actions(upcall_info->actions, upcall_info->actions_len, user_skb); if (!err) nla_nest_end(user_skb, nla); else nla_nest_cancel(user_skb, nla); } /* Add OVS_PACKET_ATTR_MRU */ if (upcall_info->mru) { if (nla_put_u16(user_skb, OVS_PACKET_ATTR_MRU, upcall_info->mru)) { err = -ENOBUFS; goto out; } pad_packet(dp, user_skb); } /* Only reserve room for attribute header, packet data is added * in skb_zerocopy() */ if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) { err = -ENOBUFS; goto out; } nla->nla_len = nla_attr_size(skb->len); err = skb_zerocopy(user_skb, skb, skb->len, hlen); if (err) goto out; /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */ pad_packet(dp, user_skb); ((struct nlmsghdr *) user_skb->data)->nlmsg_len = user_skb->len; err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); user_skb = NULL; out: if (err) skb_tx_error(skb); kfree_skb(user_skb); kfree_skb(nskb); return err; } static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) { struct ovs_header *ovs_header = info->userhdr; struct net *net = sock_net(skb->sk); struct nlattr **a = info->attrs; struct sw_flow_actions *acts; struct sk_buff *packet; struct sw_flow *flow; struct sw_flow_actions *sf_acts; struct datapath *dp; struct ethhdr *eth; struct vport *input_vport; u16 mru = 0; int len; int err; bool log = !a[OVS_PACKET_ATTR_PROBE]; err = -EINVAL; if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] || !a[OVS_PACKET_ATTR_ACTIONS]) goto err; len = nla_len(a[OVS_PACKET_ATTR_PACKET]); packet = __dev_alloc_skb(NET_IP_ALIGN + len, GFP_KERNEL); err = -ENOMEM; if (!packet) goto err; skb_reserve(packet, NET_IP_ALIGN); nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len); skb_reset_mac_header(packet); eth = eth_hdr(packet); /* Normally, setting the skb 'protocol' field would be handled by a * call to eth_type_trans(), but it assumes there's a sending * device, which we may not have. */ if (eth_proto_is_802_3(eth->h_proto)) packet->protocol = eth->h_proto; else packet->protocol = htons(ETH_P_802_2); /* Set packet's mru */ if (a[OVS_PACKET_ATTR_MRU]) { mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]); packet->ignore_df = 1; } OVS_CB(packet)->mru = mru; /* Build an sw_flow for sending this packet. */ flow = ovs_flow_alloc(); err = PTR_ERR(flow); if (IS_ERR(flow)) goto err_kfree_skb; err = ovs_flow_key_extract_userspace(net, a[OVS_PACKET_ATTR_KEY], packet, &flow->key, log); if (err) goto err_flow_free; err = ovs_nla_copy_actions(net, a[OVS_PACKET_ATTR_ACTIONS], &flow->key, &acts, log); if (err) goto err_flow_free; rcu_assign_pointer(flow->sf_acts, acts); packet->priority = flow->key.phy.priority; packet->mark = flow->key.phy.skb_mark; rcu_read_lock(); dp = get_dp_rcu(net, ovs_header->dp_ifindex); err = -ENODEV; if (!dp) goto err_unlock; input_vport = ovs_vport_rcu(dp, flow->key.phy.in_port); if (!input_vport) input_vport = ovs_vport_rcu(dp, OVSP_LOCAL); if (!input_vport) goto err_unlock; packet->dev = input_vport->dev; OVS_CB(packet)->input_vport = input_vport; sf_acts = rcu_dereference(flow->sf_acts); local_bh_disable(); err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); local_bh_enable(); rcu_read_unlock(); ovs_flow_free(flow, false); return err; err_unlock: rcu_read_unlock(); err_flow_free: ovs_flow_free(flow, false); err_kfree_skb: kfree_skb(packet); err: return err; } static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = { [OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN }, [OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED }, [OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED }, [OVS_PACKET_ATTR_PROBE] = { .type = NLA_FLAG }, [OVS_PACKET_ATTR_MRU] = { .type = NLA_U16 }, }; static struct genl_ops dp_packet_genl_ops[] = { { .cmd = OVS_PACKET_CMD_EXECUTE, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = packet_policy, .doit = ovs_packet_cmd_execute } }; static struct genl_family dp_packet_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_PACKET_FAMILY, .version = OVS_PACKET_VERSION, .maxattr = OVS_PACKET_ATTR_MAX, .netnsok = true, .parallel_ops = true, .ops = dp_packet_genl_ops, .n_ops = ARRAY_SIZE(dp_packet_genl_ops), }; static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, struct ovs_dp_megaflow_stats *mega_stats) { int i; memset(mega_stats, 0, sizeof(*mega_stats)); stats->n_flows = ovs_flow_tbl_count(&dp->table); mega_stats->n_masks = ovs_flow_tbl_num_masks(&dp->table); stats->n_hit = stats->n_missed = stats->n_lost = 0; for_each_possible_cpu(i) { const struct dp_stats_percpu *percpu_stats; struct dp_stats_percpu local_stats; unsigned int start; percpu_stats = per_cpu_ptr(dp->stats_percpu, i); do { start = u64_stats_fetch_begin_irq(&percpu_stats->syncp); local_stats = *percpu_stats; } while (u64_stats_fetch_retry_irq(&percpu_stats->syncp, start)); stats->n_hit += local_stats.n_hit; stats->n_missed += local_stats.n_missed; stats->n_lost += local_stats.n_lost; mega_stats->n_mask_hit += local_stats.n_mask_hit; } } static bool should_fill_key(const struct sw_flow_id *sfid, uint32_t ufid_flags) { return ovs_identifier_is_ufid(sfid) && !(ufid_flags & OVS_UFID_F_OMIT_KEY); } static bool should_fill_mask(uint32_t ufid_flags) { return !(ufid_flags & OVS_UFID_F_OMIT_MASK); } static bool should_fill_actions(uint32_t ufid_flags) { return !(ufid_flags & OVS_UFID_F_OMIT_ACTIONS); } static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts, const struct sw_flow_id *sfid, uint32_t ufid_flags) { size_t len = NLMSG_ALIGN(sizeof(struct ovs_header)); /* OVS_FLOW_ATTR_UFID */ if (sfid && ovs_identifier_is_ufid(sfid)) len += nla_total_size(sfid->ufid_len); /* OVS_FLOW_ATTR_KEY */ if (!sfid || should_fill_key(sfid, ufid_flags)) len += nla_total_size(ovs_key_attr_size()); /* OVS_FLOW_ATTR_MASK */ if (should_fill_mask(ufid_flags)) len += nla_total_size(ovs_key_attr_size()); /* OVS_FLOW_ATTR_ACTIONS */ if (should_fill_actions(ufid_flags)) len += nla_total_size(acts->orig_len); return len + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */ + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */ + nla_total_size(8); /* OVS_FLOW_ATTR_USED */ } /* Called with ovs_mutex or RCU read lock. */ static int ovs_flow_cmd_fill_stats(const struct sw_flow *flow, struct sk_buff *skb) { struct ovs_flow_stats stats; __be16 tcp_flags; unsigned long used; ovs_flow_stats_get(flow, &stats, &used, &tcp_flags); if (used && nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used))) return -EMSGSIZE; if (stats.n_packets && nla_put(skb, OVS_FLOW_ATTR_STATS, sizeof(struct ovs_flow_stats), &stats)) return -EMSGSIZE; if ((u8)ntohs(tcp_flags) && nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags))) return -EMSGSIZE; return 0; } /* Called with ovs_mutex or RCU read lock. */ static int ovs_flow_cmd_fill_actions(const struct sw_flow *flow, struct sk_buff *skb, int skb_orig_len) { struct nlattr *start; int err; /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if * this is the first flow to be dumped into 'skb'. This is unusual for * Netlink but individual action lists can be longer than * NLMSG_GOODSIZE and thus entirely undumpable if we didn't do this. * The userspace caller can always fetch the actions separately if it * really wants them. (Most userspace callers in fact don't care.) * * This can only fail for dump operations because the skb is always * properly sized for single flows. */ start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS); if (start) { const struct sw_flow_actions *sf_acts; sf_acts = rcu_dereference_ovsl(flow->sf_acts); err = ovs_nla_put_actions(sf_acts->actions, sf_acts->actions_len, skb); if (!err) nla_nest_end(skb, start); else { if (skb_orig_len) return err; nla_nest_cancel(skb, start); } } else if (skb_orig_len) { return -EMSGSIZE; } return 0; } /* Called with ovs_mutex or RCU read lock. */ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd, u32 ufid_flags) { const int skb_orig_len = skb->len; struct ovs_header *ovs_header; int err; ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); if (!ovs_header) return -EMSGSIZE; ovs_header->dp_ifindex = dp_ifindex; err = ovs_nla_put_identifier(flow, skb); if (err) goto error; if (should_fill_key(&flow->id, ufid_flags)) { err = ovs_nla_put_masked_key(flow, skb); if (err) goto error; } if (should_fill_mask(ufid_flags)) { err = ovs_nla_put_mask(flow, skb); if (err) goto error; } err = ovs_flow_cmd_fill_stats(flow, skb); if (err) goto error; if (should_fill_actions(ufid_flags)) { err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len); if (err) goto error; } genlmsg_end(skb, ovs_header); return 0; error: genlmsg_cancel(skb, ovs_header); return err; } /* May not be called with RCU read lock. */ static struct sk_buff *ovs_flow_cmd_alloc_info(const struct sw_flow_actions *acts, const struct sw_flow_id *sfid, struct genl_info *info, bool always, uint32_t ufid_flags) { struct sk_buff *skb; size_t len; if (!always && !ovs_must_notify(&dp_flow_genl_family, info, GROUP_ID(&ovs_dp_flow_multicast_group))) return NULL; len = ovs_flow_cmd_msg_size(acts, sfid, ufid_flags); skb = genlmsg_new_unicast(len, info, GFP_KERNEL); if (!skb) return ERR_PTR(-ENOMEM); return skb; } /* Called with ovs_mutex. */ static struct sk_buff *ovs_flow_cmd_build_info(const struct sw_flow *flow, int dp_ifindex, struct genl_info *info, u8 cmd, bool always, u32 ufid_flags) { struct sk_buff *skb; int retval; skb = ovs_flow_cmd_alloc_info(ovsl_dereference(flow->sf_acts), &flow->id, info, always, ufid_flags); if (IS_ERR_OR_NULL(skb)) return skb; retval = ovs_flow_cmd_fill_info(flow, dp_ifindex, skb, info->snd_portid, info->snd_seq, 0, cmd, ufid_flags); BUG_ON(retval < 0); return skb; } static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) { struct net *net = sock_net(skb->sk); struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct sw_flow *flow = NULL, *new_flow; struct sw_flow_mask mask; struct sk_buff *reply; struct datapath *dp; struct sw_flow_key key; struct sw_flow_actions *acts; struct sw_flow_match match; u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); int error; bool log = !a[OVS_FLOW_ATTR_PROBE]; /* Must have key and actions. */ error = -EINVAL; if (!a[OVS_FLOW_ATTR_KEY]) { OVS_NLERR(log, "Flow key attr not present in new flow."); goto error; } if (!a[OVS_FLOW_ATTR_ACTIONS]) { OVS_NLERR(log, "Flow actions attr not present in new flow."); goto error; } /* Most of the time we need to allocate a new flow, do it before * locking. */ new_flow = ovs_flow_alloc(); if (IS_ERR(new_flow)) { error = PTR_ERR(new_flow); goto error; } /* Extract key. */ ovs_match_init(&match, &key, &mask); error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK], log); if (error) goto err_kfree_flow; ovs_flow_mask_key(&new_flow->key, &key, true, &mask); /* Extract flow identifier. */ error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], &key, log); if (error) goto err_kfree_flow; /* Validate actions. */ error = ovs_nla_copy_actions(net, a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key, &acts, log); if (error) { OVS_NLERR(log, "Flow actions may not be safe on all matching packets."); goto err_kfree_flow; } reply = ovs_flow_cmd_alloc_info(acts, &new_flow->id, info, false, ufid_flags); if (IS_ERR(reply)) { error = PTR_ERR(reply); goto err_kfree_acts; } ovs_lock(); dp = get_dp(net, ovs_header->dp_ifindex); if (unlikely(!dp)) { error = -ENODEV; goto err_unlock_ovs; } /* Check if this is a duplicate flow */ if (ovs_identifier_is_ufid(&new_flow->id)) flow = ovs_flow_tbl_lookup_ufid(&dp->table, &new_flow->id); if (!flow) flow = ovs_flow_tbl_lookup(&dp->table, &key); if (likely(!flow)) { rcu_assign_pointer(new_flow->sf_acts, acts); /* Put flow in bucket. */ error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask); if (unlikely(error)) { acts = NULL; goto err_unlock_ovs; } if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(new_flow, ovs_header->dp_ifindex, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_NEW, ufid_flags); BUG_ON(error < 0); } ovs_unlock(); } else { struct sw_flow_actions *old_acts; /* Bail out if we're not allowed to modify an existing flow. * We accept NLM_F_CREATE in place of the intended NLM_F_EXCL * because Generic Netlink treats the latter as a dump * request. We also accept NLM_F_EXCL in case that bug ever * gets fixed. */ if (unlikely(info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))) { error = -EEXIST; goto err_unlock_ovs; } /* The flow identifier has to be the same for flow updates. * Look for any overlapping flow. */ if (unlikely(!ovs_flow_cmp(flow, &match))) { if (ovs_identifier_is_key(&flow->id)) flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); else /* UFID matches but key is different */ flow = NULL; if (!flow) { error = -ENOENT; goto err_unlock_ovs; } } /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_NEW, ufid_flags); BUG_ON(error < 0); } ovs_unlock(); ovs_nla_free_flow_actions_rcu(old_acts); ovs_flow_free(new_flow, false); } if (reply) ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info); return 0; err_unlock_ovs: ovs_unlock(); kfree_skb(reply); err_kfree_acts: ovs_nla_free_flow_actions(acts); err_kfree_flow: ovs_flow_free(new_flow, false); error: return error; } /* Factor out action copy to avoid "Wframe-larger-than=1024" warning. */ static struct sw_flow_actions *get_flow_actions(struct net *net, const struct nlattr *a, const struct sw_flow_key *key, const struct sw_flow_mask *mask, bool log) { struct sw_flow_actions *acts; struct sw_flow_key masked_key; int error; ovs_flow_mask_key(&masked_key, key, true, mask); error = ovs_nla_copy_actions(net, a, &masked_key, &acts, log); if (error) { OVS_NLERR(log, "Actions may not be safe on all matching packets"); return ERR_PTR(error); } return acts; } static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) { struct net *net = sock_net(skb->sk); struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct sw_flow_key key; struct sw_flow *flow; struct sw_flow_mask mask; struct sk_buff *reply = NULL; struct datapath *dp; struct sw_flow_actions *old_acts = NULL, *acts = NULL; struct sw_flow_match match; struct sw_flow_id sfid; u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); int error; bool log = !a[OVS_FLOW_ATTR_PROBE]; bool ufid_present; /* Extract key. */ error = -EINVAL; if (!a[OVS_FLOW_ATTR_KEY]) { OVS_NLERR(log, "Flow key attribute not present in set flow."); goto error; } ufid_present = ovs_nla_get_ufid(&sfid, a[OVS_FLOW_ATTR_UFID], log); ovs_match_init(&match, &key, &mask); error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK], log); if (error) goto error; /* Validate actions. */ if (a[OVS_FLOW_ATTR_ACTIONS]) { acts = get_flow_actions(net, a[OVS_FLOW_ATTR_ACTIONS], &key, &mask, log); if (IS_ERR(acts)) { error = PTR_ERR(acts); goto error; } /* Can allocate before locking if have acts. */ reply = ovs_flow_cmd_alloc_info(acts, &sfid, info, false, ufid_flags); if (IS_ERR(reply)) { error = PTR_ERR(reply); goto err_kfree_acts; } } ovs_lock(); dp = get_dp(net, ovs_header->dp_ifindex); if (unlikely(!dp)) { error = -ENODEV; goto err_unlock_ovs; } /* Check that the flow exists. */ if (ufid_present) flow = ovs_flow_tbl_lookup_ufid(&dp->table, &sfid); else flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); if (unlikely(!flow)) { error = -ENOENT; goto err_unlock_ovs; } /* Update actions, if present. */ if (likely(acts)) { old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); if (unlikely(reply)) { error = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_NEW, ufid_flags); BUG_ON(error < 0); } } else { /* Could not alloc without acts before locking. */ reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info, OVS_FLOW_CMD_NEW, false, ufid_flags); if (unlikely(IS_ERR(reply))) { error = PTR_ERR(reply); goto err_unlock_ovs; } } /* Clear stats. */ if (a[OVS_FLOW_ATTR_CLEAR]) ovs_flow_stats_clear(flow); ovs_unlock(); if (reply) ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info); if (old_acts) ovs_nla_free_flow_actions_rcu(old_acts); return 0; err_unlock_ovs: ovs_unlock(); kfree_skb(reply); err_kfree_acts: ovs_nla_free_flow_actions(acts); error: return error; } static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct net *net = sock_net(skb->sk); struct sw_flow_key key; struct sk_buff *reply; struct sw_flow *flow; struct datapath *dp; struct sw_flow_match match; struct sw_flow_id ufid; u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); int err = 0; bool log = !a[OVS_FLOW_ATTR_PROBE]; bool ufid_present; ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log); if (a[OVS_FLOW_ATTR_KEY]) { ovs_match_init(&match, &key, NULL); err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], NULL, log); } else if (!ufid_present) { OVS_NLERR(log, "Flow get message rejected, Key attribute missing."); err = -EINVAL; } if (err) return err; ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); if (!dp) { err = -ENODEV; goto unlock; } if (ufid_present) flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid); else flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); if (!flow) { err = -ENOENT; goto unlock; } reply = ovs_flow_cmd_build_info(flow, ovs_header->dp_ifindex, info, OVS_FLOW_CMD_NEW, true, ufid_flags); if (IS_ERR(reply)) { err = PTR_ERR(reply); goto unlock; } ovs_unlock(); return genlmsg_reply(reply, info); unlock: ovs_unlock(); return err; } static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct net *net = sock_net(skb->sk); struct sw_flow_key key; struct sk_buff *reply; struct sw_flow *flow = NULL; struct datapath *dp; struct sw_flow_match match; struct sw_flow_id ufid; u32 ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); int err; bool log = !a[OVS_FLOW_ATTR_PROBE]; bool ufid_present; ufid_present = ovs_nla_get_ufid(&ufid, a[OVS_FLOW_ATTR_UFID], log); if (a[OVS_FLOW_ATTR_KEY]) { ovs_match_init(&match, &key, NULL); err = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], NULL, log); if (unlikely(err)) return err; } ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); if (unlikely(!dp)) { err = -ENODEV; goto unlock; } if (unlikely(!a[OVS_FLOW_ATTR_KEY] && !ufid_present)) { err = ovs_flow_tbl_flush(&dp->table); goto unlock; } if (ufid_present) flow = ovs_flow_tbl_lookup_ufid(&dp->table, &ufid); else flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); if (unlikely(!flow)) { err = -ENOENT; goto unlock; } ovs_flow_tbl_remove(&dp->table, flow); ovs_unlock(); reply = ovs_flow_cmd_alloc_info(rcu_dereference_raw(flow->sf_acts), &flow->id, info, false, ufid_flags); if (likely(reply)) { if (likely(!IS_ERR(reply))) { rcu_read_lock(); /*To keep RCU checker happy. */ err = ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_DEL, ufid_flags); rcu_read_unlock(); BUG_ON(err < 0); ovs_notify(&dp_flow_genl_family, &ovs_dp_flow_multicast_group, reply, info); } else { genl_set_err(&dp_flow_genl_family, sock_net(skb->sk), 0, GROUP_ID(&ovs_dp_flow_multicast_group), PTR_ERR(reply)); } } ovs_flow_free(flow, true); return 0; unlock: ovs_unlock(); return err; } static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct nlattr *a[__OVS_FLOW_ATTR_MAX]; struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); struct table_instance *ti; struct datapath *dp; u32 ufid_flags; int err; err = genlmsg_parse(cb->nlh, &dp_flow_genl_family, a, OVS_FLOW_ATTR_MAX, flow_policy); if (err) return err; ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); rcu_read_lock(); dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); if (!dp) { rcu_read_unlock(); return -ENODEV; } ti = rcu_dereference(dp->table.ti); for (;;) { struct sw_flow *flow; u32 bucket, obj; bucket = cb->args[0]; obj = cb->args[1]; flow = ovs_flow_tbl_dump_next(ti, &bucket, &obj); if (!flow) break; if (ovs_flow_cmd_fill_info(flow, ovs_header->dp_ifindex, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_FLOW_CMD_NEW, ufid_flags) < 0) break; cb->args[0] = bucket; cb->args[1] = obj; } rcu_read_unlock(); return skb->len; } static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = { [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED }, [OVS_FLOW_ATTR_MASK] = { .type = NLA_NESTED }, [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED }, [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG }, [OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG }, [OVS_FLOW_ATTR_UFID] = { .type = NLA_UNSPEC, .len = 1 }, [OVS_FLOW_ATTR_UFID_FLAGS] = { .type = NLA_U32 }, }; static struct genl_ops dp_flow_genl_ops[] = { { .cmd = OVS_FLOW_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = flow_policy, .doit = ovs_flow_cmd_new }, { .cmd = OVS_FLOW_CMD_DEL, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = flow_policy, .doit = ovs_flow_cmd_del }, { .cmd = OVS_FLOW_CMD_GET, .flags = 0, /* OK for unprivileged users. */ .policy = flow_policy, .doit = ovs_flow_cmd_get, .dumpit = ovs_flow_cmd_dump }, { .cmd = OVS_FLOW_CMD_SET, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = flow_policy, .doit = ovs_flow_cmd_set, }, }; static struct genl_family dp_flow_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_FLOW_FAMILY, .version = OVS_FLOW_VERSION, .maxattr = OVS_FLOW_ATTR_MAX, .netnsok = true, .parallel_ops = true, .ops = dp_flow_genl_ops, .n_ops = ARRAY_SIZE(dp_flow_genl_ops), .mcgrps = &ovs_dp_flow_multicast_group, .n_mcgrps = 1, }; static size_t ovs_dp_cmd_msg_size(void) { size_t msgsize = NLMSG_ALIGN(sizeof(struct ovs_header)); msgsize += nla_total_size(IFNAMSIZ); msgsize += nla_total_size(sizeof(struct ovs_dp_stats)); msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats)); msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */ return msgsize; } /* Called with ovs_mutex. */ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { struct ovs_header *ovs_header; struct ovs_dp_stats dp_stats; struct ovs_dp_megaflow_stats dp_megaflow_stats; int err; ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family, flags, cmd); if (!ovs_header) goto error; ovs_header->dp_ifindex = get_dpifindex(dp); err = nla_put_string(skb, OVS_DP_ATTR_NAME, ovs_dp_name(dp)); if (err) goto nla_put_failure; get_dp_stats(dp, &dp_stats, &dp_megaflow_stats); if (nla_put(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), &dp_stats)) goto nla_put_failure; if (nla_put(skb, OVS_DP_ATTR_MEGAFLOW_STATS, sizeof(struct ovs_dp_megaflow_stats), &dp_megaflow_stats)) goto nla_put_failure; if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features)) goto nla_put_failure; genlmsg_end(skb, ovs_header); return 0; nla_put_failure: genlmsg_cancel(skb, ovs_header); error: return -EMSGSIZE; } static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info) { return genlmsg_new_unicast(ovs_dp_cmd_msg_size(), info, GFP_KERNEL); } /* Called with rcu_read_lock or ovs_mutex. */ static struct datapath *lookup_datapath(struct net *net, const struct ovs_header *ovs_header, struct nlattr *a[OVS_DP_ATTR_MAX + 1]) { struct datapath *dp; if (!a[OVS_DP_ATTR_NAME]) dp = get_dp(net, ovs_header->dp_ifindex); else { struct vport *vport; vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME])); dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL; } return dp ? dp : ERR_PTR(-ENODEV); } static void ovs_dp_reset_user_features(struct sk_buff *skb, struct genl_info *info) { struct datapath *dp; dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); if (IS_ERR(dp)) return; WARN(dp->user_features, "Dropping previously announced user features\n"); dp->user_features = 0; } static void ovs_dp_change(struct datapath *dp, struct nlattr *a[]) { if (a[OVS_DP_ATTR_USER_FEATURES]) dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]); } static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct vport_parms parms; struct sk_buff *reply; struct datapath *dp; struct vport *vport; struct ovs_net *ovs_net; int err, i; err = -EINVAL; if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) goto err; reply = ovs_dp_cmd_alloc_info(info); if (!reply) return -ENOMEM; err = -ENOMEM; dp = kzalloc(sizeof(*dp), GFP_KERNEL); if (dp == NULL) goto err_free_reply; ovs_dp_set_net(dp, sock_net(skb->sk)); /* Allocate table. */ err = ovs_flow_tbl_init(&dp->table); if (err) goto err_free_dp; dp->stats_percpu = netdev_alloc_pcpu_stats(struct dp_stats_percpu); if (!dp->stats_percpu) { err = -ENOMEM; goto err_destroy_table; } dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head), GFP_KERNEL); if (!dp->ports) { err = -ENOMEM; goto err_destroy_percpu; } for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) INIT_HLIST_HEAD(&dp->ports[i]); /* Set up our datapath device. */ parms.name = nla_data(a[OVS_DP_ATTR_NAME]); parms.type = OVS_VPORT_TYPE_INTERNAL; parms.options = NULL; parms.dp = dp; parms.port_no = OVSP_LOCAL; parms.upcall_portids = a[OVS_DP_ATTR_UPCALL_PID]; ovs_dp_change(dp, a); /* So far only local changes have been made, now need the lock. */ ovs_lock(); vport = new_vport(&parms); if (IS_ERR(vport)) { err = PTR_ERR(vport); if (err == -EBUSY) err = -EEXIST; if (err == -EEXIST) { /* An outdated user space instance that does not understand * the concept of user_features has attempted to create a new * datapath and is likely to reuse it. Drop all user features. */ if (info->genlhdr->version < OVS_DP_VER_FEATURES) ovs_dp_reset_user_features(skb, info); } goto err_destroy_ports_array; } err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id); list_add_tail_rcu(&dp->list_node, &ovs_net->dps); ovs_unlock(); ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info); return 0; err_destroy_ports_array: ovs_unlock(); kfree(dp->ports); err_destroy_percpu: free_percpu(dp->stats_percpu); err_destroy_table: ovs_flow_tbl_destroy(&dp->table); err_free_dp: kfree(dp); err_free_reply: kfree_skb(reply); err: return err; } /* Called with ovs_mutex. */ static void __dp_destroy(struct datapath *dp) { int i; for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { struct vport *vport; struct hlist_node *n; hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) if (vport->port_no != OVSP_LOCAL) ovs_dp_detach_port(vport); } list_del_rcu(&dp->list_node); /* OVSP_LOCAL is datapath internal port. We need to make sure that * all ports in datapath are destroyed first before freeing datapath. */ ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); /* RCU destroy the flow table */ call_rcu(&dp->rcu, destroy_dp_rcu); } static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *reply; struct datapath *dp; int err; reply = ovs_dp_cmd_alloc_info(info); if (!reply) return -ENOMEM; ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); err = PTR_ERR(dp); if (IS_ERR(dp)) goto err_unlock_free; err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_DEL); BUG_ON(err < 0); __dp_destroy(dp); ovs_unlock(); ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info); return 0; err_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *reply; struct datapath *dp; int err; reply = ovs_dp_cmd_alloc_info(info); if (!reply) return -ENOMEM; ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); err = PTR_ERR(dp); if (IS_ERR(dp)) goto err_unlock_free; ovs_dp_change(dp, info->attrs); err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); ovs_unlock(); ovs_notify(&dp_datapath_genl_family, &ovs_dp_datapath_multicast_group, reply, info); return 0; err_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *reply; struct datapath *dp; int err; reply = ovs_dp_cmd_alloc_info(info); if (!reply) return -ENOMEM; ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); if (IS_ERR(dp)) { err = PTR_ERR(dp); goto err_unlock_free; } err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); ovs_unlock(); return genlmsg_reply(reply, info); err_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id); struct datapath *dp; int skip = cb->args[0]; int i = 0; ovs_lock(); list_for_each_entry(dp, &ovs_net->dps, list_node) { if (i >= skip && ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_DP_CMD_NEW) < 0) break; i++; } ovs_unlock(); cb->args[0] = i; return skb->len; } static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = { [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 }, [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 }, }; static struct genl_ops dp_datapath_genl_ops[] = { { .cmd = OVS_DP_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = datapath_policy, .doit = ovs_dp_cmd_new }, { .cmd = OVS_DP_CMD_DEL, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = datapath_policy, .doit = ovs_dp_cmd_del }, { .cmd = OVS_DP_CMD_GET, .flags = 0, /* OK for unprivileged users. */ .policy = datapath_policy, .doit = ovs_dp_cmd_get, .dumpit = ovs_dp_cmd_dump }, { .cmd = OVS_DP_CMD_SET, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = datapath_policy, .doit = ovs_dp_cmd_set, }, }; static struct genl_family dp_datapath_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_DATAPATH_FAMILY, .version = OVS_DATAPATH_VERSION, .maxattr = OVS_DP_ATTR_MAX, .netnsok = true, .parallel_ops = true, .ops = dp_datapath_genl_ops, .n_ops = ARRAY_SIZE(dp_datapath_genl_ops), .mcgrps = &ovs_dp_datapath_multicast_group, .n_mcgrps = 1, }; /* Called with ovs_mutex or RCU read lock. */ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { struct ovs_header *ovs_header; struct ovs_vport_stats vport_stats; int err; ovs_header = genlmsg_put(skb, portid, seq, &dp_vport_genl_family, flags, cmd); if (!ovs_header) return -EMSGSIZE; ovs_header->dp_ifindex = get_dpifindex(vport->dp); if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || nla_put_string(skb, OVS_VPORT_ATTR_NAME, ovs_vport_name(vport))) goto nla_put_failure; ovs_vport_get_stats(vport, &vport_stats); if (nla_put(skb, OVS_VPORT_ATTR_STATS, sizeof(struct ovs_vport_stats), &vport_stats)) goto nla_put_failure; if (ovs_vport_get_upcall_portids(vport, skb)) goto nla_put_failure; err = ovs_vport_get_options(vport, skb); if (err == -EMSGSIZE) goto error; genlmsg_end(skb, ovs_header); return 0; nla_put_failure: err = -EMSGSIZE; error: genlmsg_cancel(skb, ovs_header); return err; } static struct sk_buff *ovs_vport_cmd_alloc_info(void) { return nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); } /* Called with ovs_mutex, only via ovs_dp_notify_wq(). */ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, u32 seq, u8 cmd) { struct sk_buff *skb; int retval; skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!skb) return ERR_PTR(-ENOMEM); retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd); BUG_ON(retval < 0); return skb; } /* Called with ovs_mutex or RCU read lock. */ static struct vport *lookup_vport(struct net *net, const struct ovs_header *ovs_header, struct nlattr *a[OVS_VPORT_ATTR_MAX + 1]) { struct datapath *dp; struct vport *vport; if (a[OVS_VPORT_ATTR_NAME]) { vport = ovs_vport_locate(net, nla_data(a[OVS_VPORT_ATTR_NAME])); if (!vport) return ERR_PTR(-ENODEV); if (ovs_header->dp_ifindex && ovs_header->dp_ifindex != get_dpifindex(vport->dp)) return ERR_PTR(-ENODEV); return vport; } else if (a[OVS_VPORT_ATTR_PORT_NO]) { u32 port_no = nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]); if (port_no >= DP_MAX_PORTS) return ERR_PTR(-EFBIG); dp = get_dp(net, ovs_header->dp_ifindex); if (!dp) return ERR_PTR(-ENODEV); vport = ovs_vport_ovsl_rcu(dp, port_no); if (!vport) return ERR_PTR(-ENODEV); return vport; } else return ERR_PTR(-EINVAL); } static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct vport_parms parms; struct sk_buff *reply; struct vport *vport; struct datapath *dp; u32 port_no; int err; if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] || !a[OVS_VPORT_ATTR_UPCALL_PID]) return -EINVAL; port_no = a[OVS_VPORT_ATTR_PORT_NO] ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0; if (port_no >= DP_MAX_PORTS) return -EFBIG; reply = ovs_vport_cmd_alloc_info(); if (!reply) return -ENOMEM; ovs_lock(); restart: dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); err = -ENODEV; if (!dp) goto exit_unlock_free; if (port_no) { vport = ovs_vport_ovsl(dp, port_no); err = -EBUSY; if (vport) goto exit_unlock_free; } else { for (port_no = 1; ; port_no++) { if (port_no >= DP_MAX_PORTS) { err = -EFBIG; goto exit_unlock_free; } vport = ovs_vport_ovsl(dp, port_no); if (!vport) break; } } parms.name = nla_data(a[OVS_VPORT_ATTR_NAME]); parms.type = nla_get_u32(a[OVS_VPORT_ATTR_TYPE]); parms.options = a[OVS_VPORT_ATTR_OPTIONS]; parms.dp = dp; parms.port_no = port_no; parms.upcall_portids = a[OVS_VPORT_ATTR_UPCALL_PID]; vport = new_vport(&parms); err = PTR_ERR(vport); if (IS_ERR(vport)) { if (err == -EAGAIN) goto restart; goto exit_unlock_free; } err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW); BUG_ON(err < 0); ovs_unlock(); ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info); return 0; exit_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct sk_buff *reply; struct vport *vport; int err; reply = ovs_vport_cmd_alloc_info(); if (!reply) return -ENOMEM; ovs_lock(); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); err = PTR_ERR(vport); if (IS_ERR(vport)) goto exit_unlock_free; if (a[OVS_VPORT_ATTR_TYPE] && nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type) { err = -EINVAL; goto exit_unlock_free; } if (a[OVS_VPORT_ATTR_OPTIONS]) { err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]); if (err) goto exit_unlock_free; } if (a[OVS_VPORT_ATTR_UPCALL_PID]) { struct nlattr *ids = a[OVS_VPORT_ATTR_UPCALL_PID]; err = ovs_vport_set_upcall_portids(vport, ids); if (err) goto exit_unlock_free; } err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW); BUG_ON(err < 0); ovs_unlock(); ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info); return 0; exit_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct sk_buff *reply; struct vport *vport; int err; reply = ovs_vport_cmd_alloc_info(); if (!reply) return -ENOMEM; ovs_lock(); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); err = PTR_ERR(vport); if (IS_ERR(vport)) goto exit_unlock_free; if (vport->port_no == OVSP_LOCAL) { err = -EINVAL; goto exit_unlock_free; } err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_DEL); BUG_ON(err < 0); ovs_dp_detach_port(vport); ovs_unlock(); ovs_notify(&dp_vport_genl_family, &ovs_dp_vport_multicast_group, reply, info); return 0; exit_unlock_free: ovs_unlock(); kfree_skb(reply); return err; } static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; struct sk_buff *reply; struct vport *vport; int err; reply = ovs_vport_cmd_alloc_info(); if (!reply) return -ENOMEM; rcu_read_lock(); vport = lookup_vport(sock_net(skb->sk), ovs_header, a); err = PTR_ERR(vport); if (IS_ERR(vport)) goto exit_unlock_free; err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW); BUG_ON(err < 0); rcu_read_unlock(); return genlmsg_reply(reply, info); exit_unlock_free: rcu_read_unlock(); kfree_skb(reply); return err; } static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); struct datapath *dp; int bucket = cb->args[0], skip = cb->args[1]; int i, j = 0; rcu_read_lock(); dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); if (!dp) { rcu_read_unlock(); return -ENODEV; } for (i = bucket; i < DP_VPORT_HASH_BUCKETS; i++) { struct vport *vport; j = 0; hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) { if (j >= skip && ovs_vport_cmd_fill_info(vport, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, OVS_VPORT_CMD_NEW) < 0) goto out; j++; } skip = 0; } out: rcu_read_unlock(); cb->args[0] = i; cb->args[1] = j; return skb->len; } static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 }, [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) }, [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, }; static struct genl_ops dp_vport_genl_ops[] = { { .cmd = OVS_VPORT_CMD_NEW, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = vport_policy, .doit = ovs_vport_cmd_new }, { .cmd = OVS_VPORT_CMD_DEL, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = vport_policy, .doit = ovs_vport_cmd_del }, { .cmd = OVS_VPORT_CMD_GET, .flags = 0, /* OK for unprivileged users. */ .policy = vport_policy, .doit = ovs_vport_cmd_get, .dumpit = ovs_vport_cmd_dump }, { .cmd = OVS_VPORT_CMD_SET, .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */ .policy = vport_policy, .doit = ovs_vport_cmd_set, }, }; struct genl_family dp_vport_genl_family = { .id = GENL_ID_GENERATE, .hdrsize = sizeof(struct ovs_header), .name = OVS_VPORT_FAMILY, .version = OVS_VPORT_VERSION, .maxattr = OVS_VPORT_ATTR_MAX, .netnsok = true, .parallel_ops = true, .ops = dp_vport_genl_ops, .n_ops = ARRAY_SIZE(dp_vport_genl_ops), .mcgrps = &ovs_dp_vport_multicast_group, .n_mcgrps = 1, }; static struct genl_family *dp_genl_families[] = { &dp_datapath_genl_family, &dp_vport_genl_family, &dp_flow_genl_family, &dp_packet_genl_family, }; static void dp_unregister_genl(int n_families) { int i; for (i = 0; i < n_families; i++) genl_unregister_family(dp_genl_families[i]); } static int dp_register_genl(void) { int err; int i; for (i = 0; i < ARRAY_SIZE(dp_genl_families); i++) { err = genl_register_family(dp_genl_families[i]); if (err) goto error; } return 0; error: dp_unregister_genl(i); return err; } static int __net_init ovs_init_net(struct net *net) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); INIT_LIST_HEAD(&ovs_net->dps); INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq); ovs_ct_init(net); return 0; } static void __net_exit list_vports_from_net(struct net *net, struct net *dnet, struct list_head *head) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); struct datapath *dp; list_for_each_entry(dp, &ovs_net->dps, list_node) { int i; for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { struct vport *vport; hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) { if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL) continue; if (dev_net(vport->dev) == dnet) list_add(&vport->detach_list, head); } } } } static void __net_exit ovs_exit_net(struct net *dnet) { struct datapath *dp, *dp_next; struct ovs_net *ovs_net = net_generic(dnet, ovs_net_id); struct vport *vport, *vport_next; struct net *net; LIST_HEAD(head); ovs_ct_exit(dnet); ovs_lock(); list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node) __dp_destroy(dp); rtnl_lock(); for_each_net(net) list_vports_from_net(net, dnet, &head); rtnl_unlock(); /* Detach all vports from given namespace. */ list_for_each_entry_safe(vport, vport_next, &head, detach_list) { list_del(&vport->detach_list); ovs_dp_detach_port(vport); } ovs_unlock(); cancel_work_sync(&ovs_net->dp_notify_work); } static struct pernet_operations ovs_net_ops = { .init = ovs_init_net, .exit = ovs_exit_net, .id = &ovs_net_id, .size = sizeof(struct ovs_net), }; DEFINE_COMPAT_PNET_REG_FUNC(device); static int __init dp_init(void) { int err; BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb)); pr_info("Open vSwitch switching datapath %s\n", VERSION); err = compat_init(); if (err) goto error; err = action_fifos_init(); if (err) goto error_compat_exit; err = ovs_internal_dev_rtnl_link_register(); if (err) goto error_action_fifos_exit; err = ovs_flow_init(); if (err) goto error_unreg_rtnl_link; err = ovs_vport_init(); if (err) goto error_flow_exit; err = register_pernet_device(&ovs_net_ops); if (err) goto error_vport_exit; err = register_netdevice_notifier(&ovs_dp_device_notifier); if (err) goto error_netns_exit; err = ovs_netdev_init(); if (err) goto error_unreg_notifier; err = dp_register_genl(); if (err < 0) goto error_unreg_netdev; return 0; error_unreg_netdev: ovs_netdev_exit(); error_unreg_notifier: unregister_netdevice_notifier(&ovs_dp_device_notifier); error_netns_exit: unregister_pernet_device(&ovs_net_ops); error_vport_exit: ovs_vport_exit(); error_flow_exit: ovs_flow_exit(); error_unreg_rtnl_link: ovs_internal_dev_rtnl_link_unregister(); error_action_fifos_exit: action_fifos_exit(); error_compat_exit: compat_exit(); error: return err; } static void dp_cleanup(void) { dp_unregister_genl(ARRAY_SIZE(dp_genl_families)); ovs_netdev_exit(); unregister_netdevice_notifier(&ovs_dp_device_notifier); unregister_pernet_device(&ovs_net_ops); rcu_barrier(); ovs_vport_exit(); ovs_flow_exit(); ovs_internal_dev_rtnl_link_unregister(); action_fifos_exit(); compat_exit(); } module_init(dp_init); module_exit(dp_cleanup); MODULE_DESCRIPTION("Open vSwitch switching datapath"); MODULE_LICENSE("GPL"); MODULE_VERSION(VERSION); openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-geneve.c0000644000000000000000000000013213534540071020217 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.557859146 openvswitch-2.5.9/datapath/vport-geneve.c0000644000175000017500000000700713534540071021711 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport.h" #include "vport-netdev.h" static struct vport_ops ovs_geneve_vport_ops; /** * struct geneve_port - Keeps track of open UDP ports * @dst_port: destination port. */ struct geneve_port { u16 port_no; }; static inline struct geneve_port *geneve_vport(const struct vport *vport) { return vport_priv(vport); } static int geneve_get_options(const struct vport *vport, struct sk_buff *skb) { struct geneve_port *geneve_port = geneve_vport(vport); if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, geneve_port->port_no)) return -EMSGSIZE; return 0; } static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { struct geneve_port *geneve_port = geneve_vport(vport); struct net *net = ovs_dp_get_net(vport->dp); __be16 dport = htons(geneve_port->port_no); __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), skb, IPPROTO_UDP, sport, dport); } static struct vport *geneve_tnl_create(const struct vport_parms *parms) { struct net *net = ovs_dp_get_net(parms->dp); struct nlattr *options = parms->options; struct geneve_port *geneve_port; struct net_device *dev; struct vport *vport; struct nlattr *a; u16 dst_port; int err; if (!options) { err = -EINVAL; goto error; } a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); if (a && nla_len(a) == sizeof(u16)) { dst_port = nla_get_u16(a); } else { /* Require destination port from userspace. */ err = -EINVAL; goto error; } vport = ovs_vport_alloc(sizeof(struct geneve_port), &ovs_geneve_vport_ops, parms); if (IS_ERR(vport)) return vport; geneve_port = geneve_vport(vport); geneve_port->port_no = dst_port; rtnl_lock(); dev = geneve_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port); if (IS_ERR(dev)) { rtnl_unlock(); ovs_vport_free(vport); return ERR_CAST(dev); } dev_change_flags(dev, dev->flags | IFF_UP); rtnl_unlock(); return vport; error: return ERR_PTR(err); } static struct vport *geneve_create(const struct vport_parms *parms) { struct vport *vport; vport = geneve_tnl_create(parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static struct vport_ops ovs_geneve_vport_ops = { .type = OVS_VPORT_TYPE_GENEVE, .create = geneve_create, .destroy = ovs_netdev_tunnel_destroy, .get_options = geneve_get_options, .send = geneve_xmit, .get_egress_tun_info = geneve_get_egress_tun_info, }; static int __init ovs_geneve_tnl_init(void) { return ovs_vport_ops_register(&ovs_geneve_vport_ops); } static void __exit ovs_geneve_tnl_exit(void) { ovs_vport_ops_unregister(&ovs_geneve_vport_ops); } module_init(ovs_geneve_tnl_init); module_exit(ovs_geneve_tnl_exit); MODULE_DESCRIPTION("OVS: Geneve switching port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("vport-type-5"); openvswitch-2.5.9/datapath/PaxHeaders.82075/linux0000644000000000000000000000013213534540121016510 xustar0030 mtime=1567801425.413858086 30 atime=1567801425.625859648 30 ctime=1567801425.413858086 openvswitch-2.5.9/datapath/linux/0000755000175000017500000000000013534540121020253 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/PaxHeaders.82075/Makefile.in0000644000000000000000000000013213534540071020636 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801424.637852364 openvswitch-2.5.9/datapath/linux/Makefile.in0000644000175000017500000000030413534540071022321 0ustar00jpettitjpettit00000000000000ifeq ($(KERNELRELEASE),) # We're being called directly by running make in this directory. include Makefile.main else # We're being included by the Linux kernel build system include Kbuild endif openvswitch-2.5.9/datapath/linux/PaxHeaders.82075/Kbuild.in0000644000000000000000000000013213534540071020333 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801424.637852364 openvswitch-2.5.9/datapath/linux/Kbuild.in0000644000175000017500000000154313534540071022024 0ustar00jpettitjpettit00000000000000# -*- makefile -*- export builddir = @abs_builddir@ export srcdir = @abs_srcdir@ export top_srcdir = @abs_top_srcdir@ export VERSION = @VERSION@ include $(srcdir)/../Modules.mk include $(srcdir)/Modules.mk ccflags-y := -DVERSION=\"$(VERSION)\" ccflags-y += -I$(srcdir)/.. ccflags-y += -I$(builddir)/.. ccflags-y += -g ccflags-y += -include $(builddir)/kcompat.h # These include directories have to go before -I$(KSRC)/include. # NOSTDINC_FLAGS just happens to be a variable that goes in the # right place, even though it's conceptually incorrect. NOSTDINC_FLAGS += -I$(top_srcdir)/include -I$(srcdir)/compat -I$(srcdir)/compat/include obj-m := $(subst _,-,$(patsubst %,%.o,$(build_modules))) define module_template $(1)-y = $$(notdir $$(patsubst %.c,%.o,$($(1)_sources))) endef $(foreach module,$(build_multi_modules),$(eval $(call module_template,$(module)))) openvswitch-2.5.9/datapath/linux/PaxHeaders.82075/compat0000644000000000000000000000013213534540121017773 xustar0030 mtime=1567801425.557859146 30 atime=1567801425.625859648 30 ctime=1567801425.557859146 openvswitch-2.5.9/datapath/linux/compat/0000755000175000017500000000000013534540121021536 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/reassembly.c0000644000000000000000000000013113534540071022365 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.549859088 openvswitch-2.5.9/datapath/linux/compat/reassembly.c0000644000175000017500000000567313534540071024067 0ustar00jpettitjpettit00000000000000/* * Backported from upstream commit a72a5e2d34ec * ("inet: kill unused skb_free op") * * IPv6 fragment reassembly * Linux INET6 implementation * * Authors: * Pedro Roque * * Based on: net/ipv4/ip_fragment.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ /* * Fixes: * Andi Kleen Make it work with multiple hosts. * More RFC compliance. * * Horst von Brand Add missing #include * Alexey Kuznetsov SMP races, threading, cleanup. * Patrick McHardy LRU queue of frag heads for evictor. * Mitsuru KANDA @USAGI Register inet6_protocol{}. * David Stevens and * YOSHIFUJI,H. @USAGI Always remove fragment header to * calculate ICV correctly. */ #define pr_fmt(fmt) "IPv6: " fmt #if defined(OVS_FRAGMENT_BACKPORT) && \ LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq, struct inet_frags *frags) { struct net_device *dev = NULL; spin_lock(&fq->q.lock); if (qp_flags(fq) & INET_FRAG_COMPLETE) goto out; inet_frag_kill(&fq->q, frags); rcu_read_lock(); dev = dev_get_by_index_rcu(net, fq->iif); if (!dev) goto out_rcu_unlock; IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); if (inet_frag_evicting(&fq->q)) goto out_rcu_unlock; IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); /* Don't send error if the first segment did not arrive. */ if (!(qp_flags(fq) & INET_FRAG_FIRST_IN) || !fq->q.fragments) goto out_rcu_unlock; /* But use as source device on which LAST ARRIVED * segment was received. And do not use fq->dev * pointer directly, device might already disappeared. */ fq->q.fragments->dev = dev; icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); out_rcu_unlock: rcu_read_unlock(); out: spin_unlock(&fq->q.lock); inet_frag_put(&fq->q, frags); } #endif /* OVS_FRAGMENT_BACKPORT */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/flow_dissector.c0000644000000000000000000000013113534540071023245 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 29 ctime=1567801425.52985894 openvswitch-2.5.9/datapath/linux/compat/flow_dissector.c0000644000175000017500000001267013534540071024742 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * * This code is derived from kernel flow_dissector.c */ #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) #include #include #include #include #include #include #include #include #include #include #include #include #include /* copy saddr & daddr, possibly using 64bit load/store * Equivalent to : flow->src = iph->saddr; * flow->dst = iph->daddr; */ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *iph) { BUILD_BUG_ON(offsetof(typeof(*flow), dst) != offsetof(typeof(*flow), src) + sizeof(flow->src)); memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst)); } static __be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto) { int poff = proto_ports_offset(ip_proto); if (poff >= 0) { __be32 *ports, _ports; ports = skb_header_pointer(skb, thoff + poff, sizeof(_ports), &_ports); if (ports) return *ports; } return 0; } static bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) { int nhoff = skb_network_offset(skb); u8 ip_proto; __be16 proto = skb->protocol; memset(flow, 0, sizeof(*flow)); again: switch (proto) { case __constant_htons(ETH_P_IP): { const struct iphdr *iph; struct iphdr _iph; ip: iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (!iph) return false; if (ip_is_fragment(iph)) ip_proto = 0; else ip_proto = iph->protocol; iph_to_flow_copy_addrs(flow, iph); nhoff += iph->ihl * 4; break; } case __constant_htons(ETH_P_IPV6): { const struct ipv6hdr *iph; struct ipv6hdr _iph; ipv6: iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (!iph) return false; ip_proto = iph->nexthdr; flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr); flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr); nhoff += sizeof(struct ipv6hdr); break; } case __constant_htons(ETH_P_8021AD): case __constant_htons(ETH_P_8021Q): { const struct vlan_hdr *vlan; struct vlan_hdr _vlan; vlan = skb_header_pointer(skb, nhoff, sizeof(_vlan), &_vlan); if (!vlan) return false; proto = vlan->h_vlan_encapsulated_proto; nhoff += sizeof(*vlan); goto again; } case __constant_htons(ETH_P_PPP_SES): { struct { struct pppoe_hdr hdr; __be16 proto; } *hdr, _hdr; hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); if (!hdr) return false; proto = hdr->proto; nhoff += PPPOE_SES_HLEN; switch (proto) { case __constant_htons(PPP_IP): goto ip; case __constant_htons(PPP_IPV6): goto ipv6; default: return false; } } default: return false; } switch (ip_proto) { case IPPROTO_GRE: { struct gre_hdr { __be16 flags; __be16 proto; } *hdr, _hdr; hdr = skb_header_pointer(skb, nhoff, sizeof(_hdr), &_hdr); if (!hdr) return false; /* * Only look inside GRE if version zero and no * routing */ if (!(hdr->flags & (GRE_VERSION|GRE_ROUTING))) { proto = hdr->proto; nhoff += 4; if (hdr->flags & GRE_CSUM) nhoff += 4; if (hdr->flags & GRE_KEY) nhoff += 4; if (hdr->flags & GRE_SEQ) nhoff += 4; if (proto == htons(ETH_P_TEB)) { const struct ethhdr *eth; struct ethhdr _eth; eth = skb_header_pointer(skb, nhoff, sizeof(_eth), &_eth); if (!eth) return false; proto = eth->h_proto; nhoff += sizeof(*eth); } goto again; } break; } case IPPROTO_IPIP: goto again; case IPPROTO_IPV6: proto = htons(ETH_P_IPV6); goto ipv6; default: break; } flow->ip_proto = ip_proto; flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto); flow->thoff = (u16) nhoff; return true; } static u32 hashrnd __read_mostly; static __always_inline void __flow_hash_secret_init(void) { net_get_random_once(&hashrnd, sizeof(hashrnd)); } static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c) { __flow_hash_secret_init(); return jhash_3words(a, b, c, hashrnd); } u32 rpl__skb_get_rxhash(struct sk_buff *skb) { struct flow_keys keys; u32 hash; if (!skb_flow_dissect(skb, &keys)) return 0; /* get a consistent hash (same value on both flow directions) */ if (((__force u32)keys.dst < (__force u32)keys.src) || (((__force u32)keys.dst == (__force u32)keys.src) && ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) { swap(keys.dst, keys.src); swap(keys.port16[0], keys.port16[1]); } hash = __flow_hash_3words((__force u32)keys.dst, (__force u32)keys.src, (__force u32)keys.ports); if (!hash) hash = 1; #ifdef HAVE_RXHASH skb->rxhash = hash; #endif return hash; } EXPORT_SYMBOL_GPL(rpl__skb_get_rxhash); #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/genetlink-openvswitch.c0000644000000000000000000000013113534540071024546 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.053685959 29 ctime=1567801425.53385897 openvswitch-2.5.9/datapath/linux/compat/genetlink-openvswitch.c0000644000175000017500000000264613534540071026245 0ustar00jpettitjpettit00000000000000#include #include #ifndef HAVE_GENL_NOTIFY_TAKES_FAMILY #undef genl_notify void rpl_genl_notify(struct rpl_genl_family *family, struct sk_buff *skb, struct net *net, u32 portid, u32 group, struct nlmsghdr *nlh, gfp_t flags) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) struct sock *sk = net->genl_sock; int report = 0; if (nlh) report = nlmsg_report(nlh); nlmsg_notify(sk, skb, portid, group, report, flags); #else genl_notify(skb, net, portid, group, nlh, flags); #endif } EXPORT_SYMBOL_GPL(rpl_genl_notify); int rpl___genl_register_family(struct rpl_genl_family *f) { int err; f->compat_family.id = f->id; f->compat_family.hdrsize = f->hdrsize; strncpy(f->compat_family.name, f->name, GENL_NAMSIZ); f->compat_family.version = f->version; f->compat_family.maxattr = f->maxattr; f->compat_family.netnsok = f->netnsok; #ifdef HAVE_PARALLEL_OPS f->compat_family.parallel_ops = f->parallel_ops; #endif err = genl_register_family_with_ops(&f->compat_family, (struct genl_ops *) f->ops, f->n_ops); if (err) goto error; if (f->mcgrps) { /* Need to Fix GROUP_ID() for more than one group. */ BUG_ON(f->n_mcgrps > 1); err = genl_register_mc_group(&f->compat_family, (struct genl_multicast_group *) f->mcgrps); if (err) goto error; } error: return err; } EXPORT_SYMBOL_GPL(rpl___genl_register_family); #endif /* kernel version < 3.13.0 */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip_tunnel.c0000644000000000000000000000013113534540071022214 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.537858999 openvswitch-2.5.9/datapath/linux/compat/ip_tunnel.c0000644000175000017500000001607413534540071023713 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_IPV6) #include #include #include #endif #include "compat.h" #ifndef HAVE_METADATA_DST static void ip_tunnel_add(struct ip_tunnel_net *itn, struct ip_tunnel *t) { if (t->collect_md) rcu_assign_pointer(itn->collect_md_tun, t); else WARN_ONCE(1, "%s: collect md not set\n", t->dev->name); } static void ip_tunnel_del(struct ip_tunnel_net *itn, struct ip_tunnel *t) { if (t->collect_md) rcu_assign_pointer(itn->collect_md_tun, NULL); } static inline void init_tunnel_flow(struct flowi4 *fl4, int proto, __be32 daddr, __be32 saddr, __be32 key, __u8 tos, int oif) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = oif; fl4->daddr = daddr; fl4->saddr = saddr; fl4->flowi4_tos = tos; fl4->flowi4_proto = proto; fl4->fl4_gre_key = key; } static int ip_tunnel_bind_dev(struct net_device *dev) { struct net_device *tdev = NULL; struct ip_tunnel *tunnel = netdev_priv(dev); const struct iphdr *iph; int hlen = LL_MAX_HEADER; int mtu = ETH_DATA_LEN; int t_hlen = tunnel->hlen + sizeof(struct iphdr); iph = &tunnel->parms.iph; /* Guess output device to choose reasonable mtu and needed_headroom */ if (iph->daddr) { struct flowi4 fl4; struct rtable *rt; init_tunnel_flow(&fl4, iph->protocol, iph->daddr, iph->saddr, tunnel->parms.o_key, RT_TOS(iph->tos), tunnel->parms.link); rt = ip_route_output_key(tunnel->net, &fl4); if (!IS_ERR(rt)) { tdev = rt_dst(rt).dev; ip_rt_put(rt); } if (dev->type != ARPHRD_ETHER) dev->flags |= IFF_POINTOPOINT; } if (!tdev && tunnel->parms.link) tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); if (tdev) { hlen = tdev->hard_header_len + tdev->needed_headroom; mtu = tdev->mtu; } dev->needed_headroom = t_hlen + hlen; mtu -= (dev->hard_header_len + t_hlen); if (mtu < 68) mtu = 68; return mtu; } int rpl___ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict) { struct ip_tunnel *tunnel = netdev_priv(dev); int t_hlen = tunnel->hlen + sizeof(struct iphdr); int max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; if (new_mtu < 68) return -EINVAL; if (new_mtu > max_mtu) { if (strict) return -EINVAL; new_mtu = max_mtu; } dev->mtu = new_mtu; return 0; } int rpl_ip_tunnel_change_mtu(struct net_device *dev, int new_mtu) { return rpl___ip_tunnel_change_mtu(dev, new_mtu, true); } static void ip_tunnel_dev_free(struct net_device *dev) { #ifdef HAVE_DEV_TSTATS free_percpu(dev->tstats); #endif free_netdev(dev); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) void rpl_ip_tunnel_dellink(struct net_device *dev, struct list_head *head) #else void rpl_ip_tunnel_dellink(struct net_device *dev) #endif { struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel_net *itn; itn = net_generic(tunnel->net, tunnel->ip_tnl_net_id); ip_tunnel_del(itn, netdev_priv(dev)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) unregister_netdevice_queue(dev, head); #endif } int rpl_ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname) { struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id); itn->collect_md_tun = NULL; itn->rtnl_ops = ops; return 0; } static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head, struct rtnl_link_ops *ops) { struct ip_tunnel *t; t = rtnl_dereference(itn->collect_md_tun); if (!t) return; unregister_netdevice_queue(t->dev, head); } void rpl_ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops) { LIST_HEAD(list); rtnl_lock(); ip_tunnel_destroy(itn, &list, ops); unregister_netdevice_many(&list); rtnl_unlock(); } int rpl_ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p) { struct ip_tunnel *nt; struct net *net = dev_net(dev); struct ip_tunnel_net *itn; int mtu; int err; nt = netdev_priv(dev); itn = net_generic(net, nt->ip_tnl_net_id); if (nt->collect_md) { if (rtnl_dereference(itn->collect_md_tun)) return -EEXIST; } else { return -EOPNOTSUPP; } nt->net = net; nt->parms = *p; err = register_netdevice(dev); if (err) goto out; if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) eth_hw_addr_random(dev); mtu = ip_tunnel_bind_dev(dev); if (!tb[IFLA_MTU]) dev->mtu = mtu; ip_tunnel_add(itn, nt); out: return err; } int rpl_ip_tunnel_init(struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct iphdr *iph = &tunnel->parms.iph; dev->destructor = ip_tunnel_dev_free; #ifdef HAVE_DEV_TSTATS dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; #endif tunnel->dev = dev; tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); iph->version = 4; iph->ihl = 5; if (tunnel->collect_md) dev->features |= NETIF_F_NETNS_LOCAL; return 0; } void rpl_ip_tunnel_uninit(struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); struct net *net = tunnel->net; struct ip_tunnel_net *itn; itn = net_generic(net, tunnel->ip_tnl_net_id); ip_tunnel_del(itn, netdev_priv(dev)); } /* Do least required initialization, rest of init is done in tunnel_init call */ void rpl_ip_tunnel_setup(struct net_device *dev, int net_id) { struct ip_tunnel *tunnel = netdev_priv(dev); tunnel->ip_tnl_net_id = net_id; } int rpl_ip_tunnel_get_iflink(const struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); return tunnel->parms.link; } struct net *rpl_ip_tunnel_get_link_net(const struct net_device *dev) { struct ip_tunnel *tunnel = netdev_priv(dev); return tunnel->net; } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/geneve.c0000644000000000000000000000013113534540071021470 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.053685959 29 ctime=1567801425.52985894 openvswitch-2.5.9/datapath/linux/compat/geneve.c0000644000175000017500000006577013534540071023176 0ustar00jpettitjpettit00000000000000/* * GENEVE: Generic Network Virtualization Encapsulation * * Copyright (c) 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include "gso.h" #include "vport-netdev.h" #include "compat.h" #ifndef HAVE_METADATA_DST #define GENEVE_NETDEV_VER "0.6" #define GENEVE_UDP_PORT 6081 #define GENEVE_N_VID (1u << 24) #define GENEVE_VID_MASK (GENEVE_N_VID - 1) #define VNI_HASH_BITS 10 #define VNI_HASH_SIZE (1<vni_list[hash]; hlist_for_each_entry_rcu(geneve, vni_list_head, hlist) { if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) && addr == geneve->remote.sin_addr.s_addr) return geneve; } return NULL; } static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb) { return (struct genevehdr *)(udp_hdr(skb) + 1); } /* geneve receive/decap routine */ static void geneve_rx(struct geneve_sock *gs, struct sk_buff *skb) { struct genevehdr *gnvh = geneve_hdr(skb); struct metadata_dst *tun_dst; struct geneve_dev *geneve = NULL; #ifdef HAVE_DEV_TSTATS struct pcpu_sw_netstats *stats; #endif struct iphdr *iph; u8 *vni; __be32 addr; int err; union { struct metadata_dst dst; char buf[sizeof(struct metadata_dst) + 256]; } buf; iph = ip_hdr(skb); /* outer IP header... */ if (gs->collect_md) { static u8 zero_vni[3]; vni = zero_vni; addr = 0; } else { vni = gnvh->vni; addr = iph->saddr; } geneve = geneve_lookup(gs, addr, vni); if (!geneve) goto drop; if (ip_tunnel_collect_metadata() || gs->collect_md) { __be16 flags; flags = TUNNEL_KEY | TUNNEL_GENEVE_OPT | (gnvh->oam ? TUNNEL_OAM : 0) | (gnvh->critical ? TUNNEL_CRIT_OPT : 0); tun_dst = &buf.dst; ovs_udp_tun_rx_dst(&tun_dst->u.tun_info, skb, AF_INET, flags, vni_to_tunnel_id(gnvh->vni), gnvh->opt_len * 4); /* Update tunnel dst according to Geneve options. */ ip_tunnel_info_opts_set(&tun_dst->u.tun_info, gnvh->options, gnvh->opt_len * 4); } else { /* Drop packets w/ critical options, * since we don't support any... */ tun_dst = NULL; if (gnvh->critical) goto drop; } skb_reset_mac_header(skb); skb_scrub_packet(skb, !net_eq(geneve->net, dev_net(geneve->dev))); skb->protocol = eth_type_trans(skb, geneve->dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); if (tun_dst) ovs_skb_dst_set(skb, &tun_dst->dst); else goto drop; /* Ignore packet loops (and multicast echo) */ if (ether_addr_equal(eth_hdr(skb)->h_source, geneve->dev->dev_addr)) goto drop; skb_reset_network_header(skb); err = IP_ECN_decapsulate(iph, skb); if (unlikely(err)) { if (err > 1) { ++geneve->dev->stats.rx_frame_errors; ++geneve->dev->stats.rx_errors; goto drop; } } #ifdef HAVE_DEV_TSTATS stats = this_cpu_ptr((struct pcpu_sw_netstats __percpu *)geneve->dev->tstats); u64_stats_update_begin(&stats->syncp); stats->rx_packets++; stats->rx_bytes += skb->len; u64_stats_update_end(&stats->syncp); #endif netdev_port_receive(skb, &tun_dst->u.tun_info); return; drop: /* Consume bad packet */ kfree_skb(skb); } #ifdef HAVE_DEV_TSTATS /* Setup stats when device is created */ static int geneve_init(struct net_device *dev) { dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; } static void geneve_uninit(struct net_device *dev) { free_percpu(dev->tstats); } #endif /* Callback from net/ipv4/udp.c to receive packets */ static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct genevehdr *geneveh; struct geneve_sock *gs; int opts_len; /* Need Geneve and inner Ethernet header to be present */ if (unlikely(!pskb_may_pull(skb, GENEVE_BASE_HLEN))) goto error; /* Return packets with reserved bits set */ geneveh = geneve_hdr(skb); if (unlikely(geneveh->ver != GENEVE_VER)) goto error; if (unlikely(geneveh->proto_type != htons(ETH_P_TEB))) goto error; opts_len = geneveh->opt_len * 4; if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len, htons(ETH_P_TEB))) goto drop; gs = rcu_dereference_sk_user_data(sk); if (!gs) goto drop; geneve_rx(gs, skb); return 0; drop: /* Consume bad packet */ kfree_skb(skb); return 0; error: /* Let the UDP layer deal with the skb */ return 1; } static struct socket *geneve_create_sock(struct net *net, bool ipv6, __be16 port) { struct socket *sock; struct udp_port_cfg udp_conf; int err; memset(&udp_conf, 0, sizeof(udp_conf)); if (ipv6) { udp_conf.family = AF_INET6; } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = htonl(INADDR_ANY); } udp_conf.local_udp_port = port; /* Open UDP socket */ err = udp_sock_create(net, &udp_conf, &sock); if (err < 0) return ERR_PTR(err); return sock; } #ifdef HAVE_UDP_OFFLOAD static void geneve_notify_add_rx_port(struct geneve_sock *gs) { struct sock *sk = gs->sock->sk; sa_family_t sa_family = sk->sk_family; int err; if (sa_family == AF_INET) { err = udp_add_offload(&gs->udp_offloads); if (err) pr_warn("geneve: udp_add_offload failed with status %d\n", err); } } static int geneve_hlen(struct genevehdr *gh) { return sizeof(*gh) + gh->opt_len * 4; } #ifndef HAVE_UDP_OFFLOAD_ARG_UOFF static struct sk_buff **geneve_gro_receive(struct sk_buff **head, struct sk_buff *skb) #else static struct sk_buff **geneve_gro_receive(struct sk_buff **head, struct sk_buff *skb, struct udp_offload *uoff) #endif { struct sk_buff *p, **pp = NULL; struct genevehdr *gh, *gh2; unsigned int hlen, gh_len, off_gnv; const struct packet_offload *ptype; __be16 type; int flush = 1; off_gnv = skb_gro_offset(skb); hlen = off_gnv + sizeof(*gh); gh = skb_gro_header_fast(skb, off_gnv); if (skb_gro_header_hard(skb, hlen)) { gh = skb_gro_header_slow(skb, hlen, off_gnv); if (unlikely(!gh)) goto out; } if (gh->ver != GENEVE_VER || gh->oam) goto out; gh_len = geneve_hlen(gh); hlen = off_gnv + gh_len; if (skb_gro_header_hard(skb, hlen)) { gh = skb_gro_header_slow(skb, hlen, off_gnv); if (unlikely(!gh)) goto out; } flush = 0; for (p = *head; p; p = p->next) { if (!NAPI_GRO_CB(p)->same_flow) continue; gh2 = (struct genevehdr *)(p->data + off_gnv); if (gh->opt_len != gh2->opt_len || memcmp(gh, gh2, gh_len)) { NAPI_GRO_CB(p)->same_flow = 0; continue; } } type = gh->proto_type; rcu_read_lock(); ptype = gro_find_receive_by_type(type); if (!ptype) { flush = 1; goto out_unlock; } skb_gro_pull(skb, gh_len); skb_gro_postpull_rcsum(skb, gh, gh_len); pp = ptype->callbacks.gro_receive(head, skb); out_unlock: rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; return pp; } #ifndef HAVE_UDP_OFFLOAD_ARG_UOFF static int geneve_gro_complete(struct sk_buff *skb, int nhoff) #else static int geneve_gro_complete(struct sk_buff *skb, int nhoff, struct udp_offload *uoff) #endif { struct genevehdr *gh; struct packet_offload *ptype; __be16 type; int gh_len; int err = -ENOSYS; udp_tunnel_gro_complete(skb, nhoff); gh = (struct genevehdr *)(skb->data + nhoff); gh_len = geneve_hlen(gh); type = gh->proto_type; rcu_read_lock(); ptype = gro_find_complete_by_type(type); if (ptype) err = ptype->callbacks.gro_complete(skb, nhoff + gh_len); rcu_read_unlock(); return err; } #endif /* Create new listen socket if needed */ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, bool ipv6) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; struct socket *sock; struct udp_tunnel_sock_cfg tunnel_cfg; int h; gs = kzalloc(sizeof(*gs), GFP_KERNEL); if (!gs) return ERR_PTR(-ENOMEM); sock = geneve_create_sock(net, ipv6, port); if (IS_ERR(sock)) { kfree(gs); return ERR_CAST(sock); } gs->sock = sock; gs->refcnt = 1; for (h = 0; h < VNI_HASH_SIZE; ++h) INIT_HLIST_HEAD(&gs->vni_list[h]); /* Initialize the geneve udp offloads structure */ #ifdef HAVE_UDP_OFFLOAD gs->udp_offloads.port = port; gs->udp_offloads.callbacks.gro_receive = geneve_gro_receive; gs->udp_offloads.callbacks.gro_complete = geneve_gro_complete; geneve_notify_add_rx_port(gs); #endif /* Mark socket as an encapsulation socket */ tunnel_cfg.sk_user_data = gs; tunnel_cfg.encap_type = 1; tunnel_cfg.encap_rcv = geneve_udp_encap_recv; tunnel_cfg.encap_destroy = NULL; setup_udp_tunnel_sock(net, sock, &tunnel_cfg); list_add(&gs->list, &gn->sock_list); return gs; } static void geneve_notify_del_rx_port(struct geneve_sock *gs) { #ifdef HAVE_UDP_OFFLOAD struct sock *sk = gs->sock->sk; sa_family_t sa_family = sk->sk_family; if (sa_family == AF_INET) udp_del_offload(&gs->udp_offloads); #endif } static void free_gs_rcu(struct rcu_head *rcu) { struct geneve_sock *gs = container_of(rcu, struct geneve_sock, rcu); kfree(gs); } static void geneve_sock_release(struct geneve_sock *gs) { if (--gs->refcnt) return; list_del(&gs->list); geneve_notify_del_rx_port(gs); udp_tunnel_sock_release(gs->sock); call_rcu(&gs->rcu, free_gs_rcu); } static struct geneve_sock *geneve_find_sock(struct geneve_net *gn, __be16 dst_port) { struct geneve_sock *gs; list_for_each_entry(gs, &gn->sock_list, list) { if (inet_sport(gs->sock->sk) == dst_port && inet_sk(gs->sock->sk)->sk.sk_family == AF_INET) { return gs; } } return NULL; } static int geneve_open(struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); struct net *net = geneve->net; struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; __u32 hash; gs = geneve_find_sock(gn, geneve->dst_port); if (gs) { gs->refcnt++; goto out; } gs = geneve_socket_create(net, geneve->dst_port, false); if (IS_ERR(gs)) return PTR_ERR(gs); out: gs->collect_md = geneve->collect_md; geneve->sock = gs; hash = geneve_net_vni_hash(geneve->vni); hlist_add_head_rcu(&geneve->hlist, &gs->vni_list[hash]); return 0; } static int geneve_stop(struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); struct geneve_sock *gs = geneve->sock; if (!hlist_unhashed(&geneve->hlist)) hlist_del_rcu(&geneve->hlist); geneve_sock_release(gs); return 0; } static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb, __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, bool csum) { struct genevehdr *gnvh; int min_headroom; int err; min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr) + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); err = skb_cow_head(skb, min_headroom); if (unlikely(err)) { kfree_skb(skb); goto free_rt; } skb = vlan_hwaccel_push_inside(skb); if (!skb) { err = -ENOMEM; goto free_rt; } skb = udp_tunnel_handle_offloads(skb, csum, 0, false); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto free_rt; } gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len); gnvh->ver = GENEVE_VER; gnvh->opt_len = opt_len / 4; gnvh->oam = !!(tun_flags & TUNNEL_OAM); gnvh->critical = !!(tun_flags & TUNNEL_CRIT_OPT); gnvh->rsvd1 = 0; memcpy(gnvh->vni, vni, 3); gnvh->proto_type = htons(ETH_P_TEB); gnvh->rsvd2 = 0; memcpy(gnvh->options, opt, opt_len); ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB)); return 0; free_rt: ip_rt_put(rt); return err; } static struct rtable *geneve_get_rt(struct sk_buff *skb, struct net_device *dev, struct flowi4 *fl4, struct ip_tunnel_info *info) { struct geneve_dev *geneve = netdev_priv(dev); struct rtable *rt = NULL; __u8 tos; memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_mark = skb->mark; fl4->flowi4_proto = IPPROTO_UDP; if (info) { fl4->daddr = info->key.u.ipv4.dst; fl4->saddr = info->key.u.ipv4.src; fl4->flowi4_tos = RT_TOS(info->key.tos); } else { tos = geneve->tos; if (tos == 1) { const struct iphdr *iip = ip_hdr(skb); tos = ip_tunnel_get_dsfield(iip, skb); } fl4->flowi4_tos = RT_TOS(tos); fl4->daddr = geneve->remote.sin_addr.s_addr; } rt = ip_route_output_key(geneve->net, fl4); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &fl4->daddr); dev->stats.tx_carrier_errors++; return rt; } if (rt_dst(rt).dev == dev) { /* is this necessary? */ netdev_dbg(dev, "circular route to %pI4\n", &fl4->daddr); dev->stats.collisions++; ip_rt_put(rt); return ERR_PTR(-EINVAL); } return rt; } /* Convert 64 bit tunnel ID to 24 bit VNI. */ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni) { #ifdef __BIG_ENDIAN vni[0] = (__force __u8)(tun_id >> 16); vni[1] = (__force __u8)(tun_id >> 8); vni[2] = (__force __u8)tun_id; #else vni[0] = (__force __u8)((__force u64)tun_id >> 40); vni[1] = (__force __u8)((__force u64)tun_id >> 48); vni[2] = (__force __u8)((__force u64)tun_id >> 56); #endif } netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct geneve_dev *geneve = netdev_priv(dev); struct geneve_sock *gs = geneve->sock; struct ip_tunnel_info *info = NULL; struct rtable *rt = NULL; const struct iphdr *iip; /* interior IP header */ struct flowi4 fl4; __u8 tos, ttl; __be16 sport; bool udp_csum; __be16 df; int err; if (geneve->collect_md) { info = skb_tunnel_info(skb); if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) { netdev_dbg(dev, "no tunnel metadata\n"); goto tx_error; } if (info && ip_tunnel_info_af(info) != AF_INET) goto tx_error; } rt = geneve_get_rt(skb, dev, &fl4, info); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr); dev->stats.tx_carrier_errors++; goto tx_error; } sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true); skb_reset_mac_header(skb); iip = ip_hdr(skb); if (info) { const struct ip_tunnel_key *key = &info->key; u8 *opts = NULL; u8 vni[3]; tunnel_id_to_vni(key->tun_id, vni); if (key->tun_flags & TUNNEL_GENEVE_OPT) opts = ip_tunnel_info_opts(info); udp_csum = !!(key->tun_flags & TUNNEL_CSUM); err = geneve_build_skb(rt, skb, key->tun_flags, vni, info->options_len, opts, udp_csum); if (unlikely(err)) goto err; tos = ip_tunnel_ecn_encap(key->tos, iip, skb); ttl = key->ttl; df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { udp_csum = false; err = geneve_build_skb(rt, skb, 0, geneve->vni, 0, NULL, udp_csum); if (unlikely(err)) goto err; tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb); ttl = geneve->ttl; if (!ttl && IN_MULTICAST(ntohl(fl4.daddr))) ttl = 1; ttl = ttl ? : ip4_dst_hoplimit(&rt_dst(rt)); df = 0; } err = udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, fl4.saddr, fl4.daddr, tos, ttl, df, sport, geneve->dst_port, !net_eq(geneve->net, dev_net(geneve->dev)), !udp_csum); iptunnel_xmit_stats(err, &dev->stats, (struct pcpu_sw_netstats __percpu *) dev->tstats); return NETDEV_TX_OK; tx_error: dev_kfree_skb(skb); err: dev->stats.tx_errors++; return NETDEV_TX_OK; } EXPORT_SYMBOL(rpl_geneve_xmit); static netdev_tx_t geneve_dev_xmit(struct sk_buff *skb, struct net_device *dev) { /* Drop All packets coming from networking stack. OVS-CB is * not initialized for these packets. */ dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } static int __geneve_change_mtu(struct net_device *dev, int new_mtu, bool strict) { /* The max_mtu calculation does not take account of GENEVE * options, to avoid excluding potentially valid * configurations. */ int max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - sizeof(struct iphdr) - dev->hard_header_len; if (new_mtu < 68) return -EINVAL; if (new_mtu > max_mtu) { if (strict) return -EINVAL; new_mtu = max_mtu; } dev->mtu = new_mtu; return 0; } static int geneve_change_mtu(struct net_device *dev, int new_mtu) { return __geneve_change_mtu(dev, new_mtu, true); } static const struct net_device_ops geneve_netdev_ops = { #ifdef HAVE_DEV_TSTATS .ndo_init = geneve_init, .ndo_uninit = geneve_uninit, .ndo_get_stats64 = ip_tunnel_get_stats64, #endif .ndo_open = geneve_open, .ndo_stop = geneve_stop, .ndo_start_xmit = geneve_dev_xmit, .ndo_change_mtu = geneve_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; static void geneve_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { strlcpy(drvinfo->version, GENEVE_NETDEV_VER, sizeof(drvinfo->version)); strlcpy(drvinfo->driver, "geneve", sizeof(drvinfo->driver)); } static const struct ethtool_ops geneve_ethtool_ops = { .get_drvinfo = geneve_get_drvinfo, .get_link = ethtool_op_get_link, }; /* Info for udev, that this is a virtual tunnel endpoint */ static struct device_type geneve_type = { .name = "geneve", }; /* Initialize the device structure. */ static void geneve_setup(struct net_device *dev) { ether_setup(dev); dev->netdev_ops = &geneve_netdev_ops; dev->ethtool_ops = &geneve_ethtool_ops; dev->destructor = free_netdev; SET_NETDEV_DEVTYPE(dev, &geneve_type); dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; #endif #if 0 /* Not required */ netif_keep_dst(dev); #endif dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; eth_hw_addr_random(dev); } static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_ID] = { .type = NLA_U32 }, [IFLA_GENEVE_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, [IFLA_GENEVE_TTL] = { .type = NLA_U8 }, [IFLA_GENEVE_TOS] = { .type = NLA_U8 }, [IFLA_GENEVE_PORT] = { .type = NLA_U16 }, [IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG }, }; static int geneve_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } if (!data) return -EINVAL; if (data[IFLA_GENEVE_ID]) { __u32 vni = nla_get_u32(data[IFLA_GENEVE_ID]); if (vni >= GENEVE_VID_MASK) return -ERANGE; } return 0; } static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, __be16 dst_port, __be32 rem_addr, u8 vni[], bool *tun_on_same_port, bool *tun_collect_md) { struct geneve_dev *geneve, *t; *tun_on_same_port = false; *tun_collect_md = false; t = NULL; list_for_each_entry(geneve, &gn->geneve_list, next) { if (geneve->dst_port == dst_port) { *tun_collect_md = geneve->collect_md; *tun_on_same_port = true; } if (!memcmp(vni, geneve->vni, sizeof(geneve->vni)) && rem_addr == geneve->remote.sin_addr.s_addr && dst_port == geneve->dst_port) t = geneve; } return t; } static int geneve_configure(struct net *net, struct net_device *dev, __be32 rem_addr, __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port, bool metadata) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_dev *t, *geneve = netdev_priv(dev); bool tun_collect_md, tun_on_same_port; int err; if (metadata) { if (rem_addr || vni || tos || ttl) return -EINVAL; } geneve->net = net; geneve->dev = dev; geneve->vni[0] = (vni & 0x00ff0000) >> 16; geneve->vni[1] = (vni & 0x0000ff00) >> 8; geneve->vni[2] = vni & 0x000000ff; geneve->remote.sin_addr.s_addr = rem_addr; if (IN_MULTICAST(ntohl(geneve->remote.sin_addr.s_addr))) return -EINVAL; geneve->ttl = ttl; geneve->tos = tos; geneve->dst_port = dst_port; geneve->collect_md = metadata; t = geneve_find_dev(gn, dst_port, rem_addr, geneve->vni, &tun_on_same_port, &tun_collect_md); if (t) return -EBUSY; if (metadata) { if (tun_on_same_port) return -EPERM; } else { if (tun_collect_md) return -EPERM; } err = register_netdevice(dev); if (err) return err; list_add(&geneve->next, &gn->geneve_list); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) static int geneve_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net *net = &init_net; #else static int geneve_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { #endif __be16 dst_port = htons(GENEVE_UDP_PORT); __u8 ttl = 0, tos = 0; bool metadata = false; __be32 rem_addr; __u32 vni; if (!data[IFLA_GENEVE_ID] || !data[IFLA_GENEVE_REMOTE]) return -EINVAL; vni = nla_get_u32(data[IFLA_GENEVE_ID]); rem_addr = nla_get_in_addr(data[IFLA_GENEVE_REMOTE]); if (data[IFLA_GENEVE_TTL]) ttl = nla_get_u8(data[IFLA_GENEVE_TTL]); if (data[IFLA_GENEVE_TOS]) tos = nla_get_u8(data[IFLA_GENEVE_TOS]); if (data[IFLA_GENEVE_PORT]) dst_port = nla_get_be16(data[IFLA_GENEVE_PORT]); if (data[IFLA_GENEVE_COLLECT_METADATA]) metadata = true; return geneve_configure(net, dev, rem_addr, vni, ttl, tos, dst_port, metadata); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) static void geneve_dellink(struct net_device *dev) #else static void geneve_dellink(struct net_device *dev, struct list_head *head) #endif { struct geneve_dev *geneve = netdev_priv(dev); list_del(&geneve->next); unregister_netdevice_queue(dev, head); } static size_t geneve_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__u32)) + /* IFLA_GENEVE_ID */ nla_total_size(sizeof(struct in_addr)) + /* IFLA_GENEVE_REMOTE */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL */ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */ nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */ nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */ 0; } static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct geneve_dev *geneve = netdev_priv(dev); __u32 vni; vni = (geneve->vni[0] << 16) | (geneve->vni[1] << 8) | geneve->vni[2]; if (nla_put_u32(skb, IFLA_GENEVE_ID, vni)) goto nla_put_failure; if (nla_put_in_addr(skb, IFLA_GENEVE_REMOTE, geneve->remote.sin_addr.s_addr)) goto nla_put_failure; if (nla_put_u8(skb, IFLA_GENEVE_TTL, geneve->ttl) || nla_put_u8(skb, IFLA_GENEVE_TOS, geneve->tos)) goto nla_put_failure; if (nla_put_be16(skb, IFLA_GENEVE_PORT, geneve->dst_port)) goto nla_put_failure; if (geneve->collect_md) { if (nla_put_flag(skb, IFLA_GENEVE_COLLECT_METADATA)) goto nla_put_failure; } return 0; nla_put_failure: return -EMSGSIZE; } static struct rtnl_link_ops geneve_link_ops __read_mostly = { .kind = "ovs_geneve", .maxtype = IFLA_GENEVE_MAX, .policy = geneve_policy, .priv_size = sizeof(struct geneve_dev), .setup = geneve_setup, .validate = geneve_validate, .newlink = geneve_newlink, .dellink = geneve_dellink, .get_size = geneve_get_size, .fill_info = geneve_fill_info, }; struct net_device *rpl_geneve_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port) { struct nlattr *tb[IFLA_MAX + 1]; struct net_device *dev; int err; memset(tb, 0, sizeof(tb)); dev = rtnl_create_link(net, (char *) name, name_assign_type, &geneve_link_ops, tb); if (IS_ERR(dev)) return dev; err = geneve_configure(net, dev, 0, 0, 0, 0, htons(dst_port), true); if (err) goto err; /* openvswitch users expect packet sizes to be unrestricted, * so set the largest MTU we can. */ err = __geneve_change_mtu(dev, IP_MAX_MTU, false); if (err) goto err; return dev; err: free_netdev(dev); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(rpl_geneve_dev_create_fb); static __net_init int geneve_init_net(struct net *net) { struct geneve_net *gn = net_generic(net, geneve_net_id); INIT_LIST_HEAD(&gn->geneve_list); INIT_LIST_HEAD(&gn->sock_list); return 0; } static void __net_exit geneve_exit_net(struct net *net) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_dev *geneve, *next; struct net_device *dev, *aux; LIST_HEAD(list); rtnl_lock(); /* gather any geneve devices that were moved into this ns */ for_each_netdev_safe(net, dev, aux) if (dev->rtnl_link_ops == &geneve_link_ops) unregister_netdevice_queue(dev, &list); /* now gather any other geneve devices that were created in this ns */ list_for_each_entry_safe(geneve, next, &gn->geneve_list, next) { /* If geneve->dev is in the same netns, it was already added * to the list by the previous loop. */ if (!net_eq(dev_net(geneve->dev), net)) unregister_netdevice_queue(geneve->dev, &list); } /* unregister the devices gathered above */ unregister_netdevice_many(&list); rtnl_unlock(); } static struct pernet_operations geneve_net_ops = { .init = geneve_init_net, .exit = geneve_exit_net, .id = &geneve_net_id, .size = sizeof(struct geneve_net), }; DEFINE_COMPAT_PNET_REG_FUNC(device) int rpl_geneve_init_module(void) { int rc; rc = register_pernet_subsys(&geneve_net_ops); if (rc) goto out1; rc = rtnl_link_register(&geneve_link_ops); if (rc) goto out2; pr_info("Geneve tunneling driver\n"); return 0; out2: unregister_pernet_subsys(&geneve_net_ops); out1: return rc; } void rpl_geneve_cleanup_module(void) { rtnl_link_unregister(&geneve_link_ops); unregister_pernet_subsys(&geneve_net_ops); } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/gso.c0000644000000000000000000000013113534540071021007 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 29 ctime=1567801425.53385897 openvswitch-2.5.9/datapath/linux/compat/gso.c0000644000175000017500000001665313534540071022511 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gso.h" #include "vlan.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \ !defined(HAVE_VLAN_BUG_WORKAROUND) #include static int vlan_tso __read_mostly; module_param(vlan_tso, int, 0644); MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets"); #else #define vlan_tso true #endif #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION static bool dev_supports_vlan_tx(struct net_device *dev) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) return true; #elif defined(HAVE_VLAN_BUG_WORKAROUND) return dev->features & NETIF_F_HW_VLAN_TX; #else /* Assume that the driver is buggy. */ return false; #endif } /* Strictly this is not needed and will be optimised out * as this code is guarded by if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0). * It is here to make things explicit should the compatibility * code be extended in some way prior extending its life-span * beyond v3.19. */ static bool supports_mpls_gso(void) { /* MPLS GSO was introduced in v3.11, however it was not correctly * activated using mpls_features until v3.19. */ #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION return true; #else return false; #endif } int rpl_dev_queue_xmit(struct sk_buff *skb) { #undef dev_queue_xmit int err = -ENOMEM; bool vlan, mpls; vlan = mpls = false; /* Avoid traversing any VLAN tags that are present to determine if * the ethtype is MPLS. Instead compare the mac_len (end of L2) and * skb_network_offset() (beginning of L3) whose inequality will * indicate the presence of an MPLS label stack. */ if (skb->mac_len != skb_network_offset(skb) && !supports_mpls_gso()) mpls = true; if (skb_vlan_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) vlan = true; if (vlan || mpls) { int features; features = netif_skb_features(skb); if (vlan) { if (!vlan_tso) features &= ~(NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO | NETIF_F_FSO); skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, skb_vlan_tag_get(skb)); if (unlikely(!skb)) return err; vlan_set_tci(skb, 0); } /* As of v3.11 the kernel provides an mpls_features field in * struct net_device which allows devices to advertise which * features its supports for MPLS. This value defaults to * NETIF_F_SG and as of v3.19. * * This compatibility code is intended for kernels older * than v3.19 that do not support MPLS GSO and do not * use mpls_features. Thus this code uses NETIF_F_SG * directly in place of mpls_features. */ if (mpls) features &= NETIF_F_SG; if (netif_needs_gso(skb, features)) { struct sk_buff *nskb; nskb = skb_gso_segment(skb, features); if (!nskb) { if (unlikely(skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) goto drop; skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY; goto xmit; } if (IS_ERR(nskb)) { err = PTR_ERR(nskb); goto drop; } consume_skb(skb); skb = nskb; do { nskb = skb->next; skb->next = NULL; err = dev_queue_xmit(skb); skb = nskb; } while (skb); return err; } } xmit: return dev_queue_xmit(skb); drop: kfree_skb(skb); return err; } EXPORT_SYMBOL_GPL(rpl_dev_queue_xmit); #endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) static __be16 __skb_network_protocol(struct sk_buff *skb) { __be16 type = skb->protocol; int vlan_depth = ETH_HLEN; while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) { struct vlan_hdr *vh; if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) return 0; vh = (struct vlan_hdr *)(skb->data + vlan_depth); type = vh->h_vlan_encapsulated_proto; vlan_depth += VLAN_HLEN; } if (eth_p_mpls(type)) type = ovs_skb_get_inner_protocol(skb); return type; } static struct sk_buff *tnl_skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path) { struct iphdr *iph = ip_hdr(skb); int pkt_hlen = skb_inner_network_offset(skb); /* inner l2 + tunnel hdr. */ int mac_offset = skb_inner_mac_offset(skb); struct sk_buff *skb1 = skb; struct sk_buff *segs; __be16 proto = skb->protocol; char cb[sizeof(skb->cb)]; /* setup whole inner packet to get protocol. */ __skb_pull(skb, mac_offset); skb->protocol = __skb_network_protocol(skb); /* setup l3 packet to gso, to get around segmentation bug on older kernel.*/ __skb_pull(skb, (pkt_hlen - mac_offset)); skb_reset_mac_header(skb); skb_reset_network_header(skb); skb_reset_transport_header(skb); /* From 3.9 kernel skb->cb is used by skb gso. Therefore * make copy of it to restore it back. */ memcpy(cb, skb->cb, sizeof(cb)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) skb->encapsulation = 0; #endif /* We are handling offloads by segmenting l3 packet, so * no need to call OVS compat segmentation function. */ #ifdef HAVE___SKB_GSO_SEGMENT #undef __skb_gso_segment segs = __skb_gso_segment(skb, 0, tx_path); #else #undef skb_gso_segment segs = skb_gso_segment(skb, 0); #endif if (!segs || IS_ERR(segs)) goto free; skb = segs; while (skb) { __skb_push(skb, pkt_hlen); skb_reset_mac_header(skb); skb_reset_network_header(skb); skb_set_transport_header(skb, sizeof(struct iphdr)); skb->mac_len = 0; memcpy(ip_hdr(skb), iph, pkt_hlen); memcpy(skb->cb, cb, sizeof(cb)); OVS_GSO_CB(skb)->fix_segment(skb); skb->protocol = proto; skb = skb->next; } free: consume_skb(skb1); return segs; } static int output_ip(struct sk_buff *skb) { int ret = NETDEV_TX_OK; int err; memset(IPCB(skb), 0, sizeof(*IPCB(skb))); #undef ip_local_out err = ip_local_out(skb); if (unlikely(net_xmit_eval(err))) ret = err; return ret; } int rpl_ip_local_out(struct sk_buff *skb) { int ret = NETDEV_TX_OK; int id = -1; if (!OVS_GSO_CB(skb)->fix_segment) return output_ip(skb); if (skb_is_gso(skb)) { struct iphdr *iph; iph = ip_hdr(skb); id = ntohs(iph->id); skb = tnl_skb_gso_segment(skb, 0, false); if (!skb || IS_ERR(skb)) return 0; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { int err; err = skb_checksum_help(skb); if (unlikely(err)) return 0; } while (skb) { struct sk_buff *next_skb = skb->next; struct iphdr *iph; skb->next = NULL; iph = ip_hdr(skb); if (id >= 0) iph->id = htons(id++); ret = output_ip(skb); skb = next_skb; } return ret; } EXPORT_SYMBOL_GPL(rpl_ip_local_out); #endif /* 3.18 */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/include0000644000000000000000000000013213534540121021416 xustar0030 mtime=1567801425.409858056 30 atime=1567801425.625859648 30 ctime=1567801425.409858056 openvswitch-2.5.9/datapath/linux/compat/include/0000755000175000017500000000000013534540121023161 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/PaxHeaders.82075/net0000644000000000000000000000013213534540121022204 xustar0030 mtime=1567801425.505858763 30 atime=1567801425.625859648 30 ctime=1567801425.505858763 openvswitch-2.5.9/datapath/linux/compat/include/net/0000755000175000017500000000000013534540121023747 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/ip_tunnels.h0000644000000000000000000000013013534540071024614 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.489858645 openvswitch-2.5.9/datapath/linux/compat/include/net/ip_tunnels.h0000644000175000017500000002271613534540071026314 0ustar00jpettitjpettit00000000000000#ifndef __NET_IP_TUNNELS_WRAPPER_H #define __NET_IP_TUNNELS_WRAPPER_H 1 #include #ifdef HAVE_METADATA_DST /* Block all ip_tunnel functions. * Only function that do not depend on ip_tunnel structure can * be used. Those needs to be explicitly defined in this header file. */ #include_next #endif #include #include #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb, bool csum_help, int gso_type_mask, void (*fix_segment)(struct sk_buff *)); #define iptunnel_xmit rpl_iptunnel_xmit int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 proto, __u8 tos, __u8 ttl, __be16 df, bool xnet); #define iptunnel_pull_header rpl_iptunnel_pull_header int rpl_iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto); #else #define ovs_iptunnel_handle_offloads(skb, csum_help, gso_type_mask, fix_segment) \ iptunnel_handle_offloads(skb, csum_help, gso_type_mask) /* This macro is to make OVS build happy about declared functions name. */ #define rpl_iptunnel_pull_header iptunnel_pull_header int rpl_iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto); #define rpl_iptunnel_xmit iptunnel_xmit int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 proto, __u8 tos, __u8 ttl, __be16 df, bool xnet); #endif /* 3.18 */ /* This is not required for OVS on kernel older than 3.18, but gre.h * header file needs this declaration for function gre_handle_offloads(). * So it is defined for all kernel version. */ #define rpl_iptunnel_handle_offloads iptunnel_handle_offloads struct sk_buff *rpl_iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum, int gso_type_mask); #ifndef TUNNEL_CSUM #define TUNNEL_CSUM __cpu_to_be16(0x01) #define TUNNEL_ROUTING __cpu_to_be16(0x02) #define TUNNEL_KEY __cpu_to_be16(0x04) #define TUNNEL_SEQ __cpu_to_be16(0x08) #define TUNNEL_STRICT __cpu_to_be16(0x10) #define TUNNEL_REC __cpu_to_be16(0x20) #define TUNNEL_VERSION __cpu_to_be16(0x40) #define TUNNEL_NO_KEY __cpu_to_be16(0x80) struct tnl_ptk_info { __be16 flags; __be16 proto; __be32 key; __be32 seq; }; #define PACKET_RCVD 0 #define PACKET_REJECT 1 #endif #ifndef TUNNEL_DONT_FRAGMENT #define TUNNEL_DONT_FRAGMENT __cpu_to_be16(0x0100) #endif #ifndef TUNNEL_OAM #define TUNNEL_OAM __cpu_to_be16(0x0200) #define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400) #endif #ifndef TUNNEL_GENEVE_OPT #define TUNNEL_GENEVE_OPT __cpu_to_be16(0x0800) #endif #ifndef TUNNEL_VXLAN_OPT #define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000) #endif /* Older kernels defined TUNNEL_OPTIONS_PRESENT to GENEVE only */ #undef TUNNEL_OPTIONS_PRESENT #define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT) #define skb_is_encapsulated ovs_skb_is_encapsulated bool ovs_skb_is_encapsulated(struct sk_buff *skb); #ifndef HAVE_METADATA_DST /* Used to memset ip_tunnel padding. */ #define IP_TUNNEL_KEY_SIZE offsetofend(struct ip_tunnel_key, tp_dst) /* Used to memset ipv4 address padding. */ #define IP_TUNNEL_KEY_IPV4_PAD offsetofend(struct ip_tunnel_key, u.ipv4.dst) #define IP_TUNNEL_KEY_IPV4_PAD_LEN \ (FIELD_SIZEOF(struct ip_tunnel_key, u) - \ FIELD_SIZEOF(struct ip_tunnel_key, u.ipv4)) struct ip_tunnel_key { __be64 tun_id; union { struct { __be32 src; __be32 dst; } ipv4; struct { struct in6_addr src; struct in6_addr dst; } ipv6; } u; __be16 tun_flags; u8 tos; /* TOS for IPv4, TC for IPv6 */ u8 ttl; /* TTL for IPv4, HL for IPv6 */ __be16 tp_src; __be16 tp_dst; }; /* Flags for ip_tunnel_info mode. */ #define IP_TUNNEL_INFO_TX 0x01 /* represents tx tunnel parameters */ #define IP_TUNNEL_INFO_IPV6 0x02 /* key contains IPv6 addresses */ struct ip_tunnel_info { struct ip_tunnel_key key; u8 options_len; u8 mode; }; static inline unsigned short ip_tunnel_info_af(const struct ip_tunnel_info *tun_info) { return tun_info->mode & IP_TUNNEL_INFO_IPV6 ? AF_INET6 : AF_INET; } static inline void *ip_tunnel_info_opts(struct ip_tunnel_info *info) { return info + 1; } static inline void ip_tunnel_info_opts_get(void *to, const struct ip_tunnel_info *info) { memcpy(to, info + 1, info->options_len); } static inline void ip_tunnel_info_opts_set(struct ip_tunnel_info *info, const void *from, int len) { memcpy(ip_tunnel_info_opts(info), from, len); info->options_len = len; } static inline void ip_tunnel_key_init(struct ip_tunnel_key *key, __be32 saddr, __be32 daddr, u8 tos, u8 ttl, __be16 tp_src, __be16 tp_dst, __be64 tun_id, __be16 tun_flags) { key->tun_id = tun_id; key->u.ipv4.src = saddr; key->u.ipv4.dst = daddr; memset((unsigned char *)key + IP_TUNNEL_KEY_IPV4_PAD, 0, IP_TUNNEL_KEY_IPV4_PAD_LEN); key->tos = tos; key->ttl = ttl; key->tun_flags = tun_flags; /* For the tunnel types on the top of IPsec, the tp_src and tp_dst of * the upper tunnel are used. * E.g: GRE over IPSEC, the tp_src and tp_port are zero. */ key->tp_src = tp_src; key->tp_dst = tp_dst; /* Clear struct padding. */ if (sizeof(*key) != IP_TUNNEL_KEY_SIZE) memset((unsigned char *)key + IP_TUNNEL_KEY_SIZE, 0, sizeof(*key) - IP_TUNNEL_KEY_SIZE); } #define ip_tunnel_collect_metadata() true #define ip_tunnel rpl_ip_tunnel struct ip_tunnel { struct net_device *dev; struct net *net; /* netns for packet i/o */ int err_count; /* Number of arrived ICMP errors */ unsigned long err_time; /* Time when the last ICMP error * arrived */ /* These four fields used only by GRE */ u32 i_seqno; /* The last seen seqno */ u32 o_seqno; /* The last output seqno */ int tun_hlen; /* Precalculated header length */ int mlink; struct ip_tunnel_parm parms; int encap_hlen; /* Encap header length (FOU,GUE) */ int hlen; /* tun_hlen + encap_hlen */ int ip_tnl_net_id; bool collect_md; }; #define ip_tunnel_net rpl_ip_tunnel_net struct ip_tunnel_net { struct ip_tunnel __rcu *collect_md_tun; struct rtnl_link_ops *rtnl_ops; }; #ifndef HAVE_PCPU_SW_NETSTATS #define ip_tunnel_get_stats64 rpl_ip_tunnel_get_stats64 #else #define rpl_ip_tunnel_get_stats64 ip_tunnel_get_stats64 #endif struct rtnl_link_stats64 *rpl_ip_tunnel_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot); #define ip_tunnel_get_dsfield rpl_ip_tunnel_get_dsfield static inline u8 ip_tunnel_get_dsfield(const struct iphdr *iph, const struct sk_buff *skb) { if (skb->protocol == htons(ETH_P_IP)) return iph->tos; else if (skb->protocol == htons(ETH_P_IPV6)) return ipv6_get_dsfield((const struct ipv6hdr *)iph); else return 0; } #define ip_tunnel_ecn_encap rpl_ip_tunnel_ecn_encap static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph, const struct sk_buff *skb) { u8 inner = ip_tunnel_get_dsfield(iph, skb); return INET_ECN_encapsulate(tos, inner); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) #define iptunnel_xmit_stats(err, stats, dummy) \ do { \ if (err > 0) { \ (stats)->tx_bytes += err; \ (stats)->tx_packets++; \ } else if (err < 0) { \ (stats)->tx_errors++; \ (stats)->tx_aborted_errors++; \ } else { \ (stats)->tx_dropped++; \ } \ } while (0) #else #define iptunnel_xmit_stats rpl_iptunnel_xmit_stats static inline void iptunnel_xmit_stats(int err, struct net_device_stats *err_stats, struct pcpu_sw_netstats __percpu *stats) { if (err > 0) { struct pcpu_sw_netstats *tstats = this_cpu_ptr(stats); u64_stats_update_begin(&tstats->syncp); tstats->tx_bytes += err; tstats->tx_packets++; u64_stats_update_end(&tstats->syncp); } else if (err < 0) { err_stats->tx_errors++; err_stats->tx_aborted_errors++; } else { err_stats->tx_dropped++; } } #endif #define ip_tunnel_init rpl_ip_tunnel_init int rpl_ip_tunnel_init(struct net_device *dev); #define ip_tunnel_uninit rpl_ip_tunnel_uninit void rpl_ip_tunnel_uninit(struct net_device *dev); #define ip_tunnel_change_mtu rpl_ip_tunnel_change_mtu int rpl_ip_tunnel_change_mtu(struct net_device *dev, int new_mtu); #define ip_tunnel_newlink rpl_ip_tunnel_newlink int rpl_ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p); #define ip_tunnel_dellink rpl_ip_tunnel_dellink #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) void rpl_ip_tunnel_dellink(struct net_device *dev, struct list_head *head); #else void rpl_ip_tunnel_dellink(struct net_device *dev); #endif #define ip_tunnel_init_net rpl_ip_tunnel_init_net int rpl_ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); #define ip_tunnel_delete_net rpl_ip_tunnel_delete_net void rpl_ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops); #define ip_tunnel_setup rpl_ip_tunnel_setup void rpl_ip_tunnel_setup(struct net_device *dev, int net_id); #define ip_tunnel_get_iflink rpl_ip_tunnel_get_iflink int rpl_ip_tunnel_get_iflink(const struct net_device *dev); #define ip_tunnel_get_link_net rpl_ip_tunnel_get_link_net struct net *rpl_ip_tunnel_get_link_net(const struct net_device *dev); #endif /* HAVE_METADATA_DST */ #ifndef HAVE___IP_TUNNEL_CHANGE_MTU #define __ip_tunnel_change_mtu rpl___ip_tunnel_change_mtu int rpl___ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict); #endif #endif /* __NET_IP_TUNNELS_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/dst.h0000644000000000000000000000013213534540071023230 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.477858557 openvswitch-2.5.9/datapath/linux/compat/include/net/dst.h0000644000175000017500000000506013534540071024717 0ustar00jpettitjpettit00000000000000#ifndef __NET_DST_WRAPPER_H #define __NET_DST_WRAPPER_H 1 #include #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) && \ LINUX_VERSION_CODE > KERNEL_VERSION(3,0,20) #define dst_get_neighbour_noref dst_get_neighbour #endif #ifndef HAVE_SKB_DST_ACCESSOR_FUNCS static inline void skb_dst_drop(struct sk_buff *skb) { if (skb->dst) dst_release(skb_dst(skb)); skb->dst = NULL; } #endif #ifndef DST_OBSOLETE_NONE #define DST_OBSOLETE_NONE 0 #endif #ifndef DST_NOCOUNT #define DST_NOCOUNT 0 #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) static inline void __skb_dst_copy(struct sk_buff *nskb, unsigned long refdst) { nskb->_skb_dst = refdst; dst_clone(skb_dst(nskb)); } static inline void refdst_drop(unsigned long refdst) { } static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) { } static inline void dst_init_metrics(struct dst_entry *dst, const u32 *metrics, bool read_only) { } #elif !defined(HAVE___SKB_DST_COPY) static inline void __skb_dst_copy(struct sk_buff *nskb, unsigned long refdst) { nskb->_skb_refdst = refdst; if (!(nskb->_skb_refdst & SKB_DST_NOREF)) dst_clone(skb_dst(nskb)); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) static inline void dst_entries_add(struct dst_ops *ops, int count) { atomic_add(count, &ops->entries); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) static const u32 rpl_dst_default_metrics[RTAX_MAX + 1] = { /* This initializer is needed to force linker to place this variable * into const section. Otherwise it might end into bss section. * We really want to avoid false sharing on this variable, and catch * any writes on it. */ [RTAX_MAX] = 0xdeadbeef, }; #define dst_default_metrics rpl_dst_default_metrics static inline void rpl_dst_init(struct dst_entry *dst, struct dst_ops *ops, struct net_device *dev, int initial_ref, int initial_obsolete, unsigned short flags) { /* XXX: It's easier to handle compatibility by zeroing, as we can * refer to fewer fields. Do that here. */ memset(dst, 0, sizeof *dst); dst->dev = dev; if (dev) dev_hold(dev); dst->ops = ops; dst_init_metrics(dst, dst_default_metrics, true); dst->path = dst; dst->input = dst_discard; #ifndef HAVE_DST_DISCARD_SK dst->output = dst_discard; #else dst->output = dst_discard_sk; #endif dst->obsolete = initial_obsolete; atomic_set(&dst->__refcnt, initial_ref); dst->lastuse = jiffies; dst->flags = flags; if (!(flags & DST_NOCOUNT)) dst_entries_add(ops, 1); } #define dst_init rpl_dst_init #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/netfilter0000644000000000000000000000013213534540121024200 xustar0030 mtime=1567801425.509858793 30 atime=1567801425.625859648 30 ctime=1567801425.509858793 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/0000755000175000017500000000000013534540121025743 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/PaxHeaders.82075/nf_conntrack_core.h0000644000000000000000000000013013534540071030105 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.509858793 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/nf_conntrack_core.h0000644000175000017500000000173513534540071031603 0ustar00jpettitjpettit00000000000000#ifndef _NF_CONNTRACK_CORE_WRAPPER_H #define _NF_CONNTRACK_CORE_WRAPPER_H #include_next #ifndef HAVE_NF_CT_TMPL_ALLOC_TAKES_STRUCT_ZONE #include #define nf_ct_tmpl_alloc rpl_nf_ct_tmpl_alloc /* Released via destroy_conntrack() */ static inline struct nf_conn * nf_ct_tmpl_alloc(struct net *net, const struct nf_conntrack_zone *zone, gfp_t flags) { struct nf_conn *tmpl; tmpl = kzalloc(sizeof(*tmpl), flags); if (tmpl == NULL) return NULL; tmpl->status = IPS_TEMPLATE; write_pnet(&tmpl->ct_net, net); if (nf_ct_zone_add(tmpl, flags, zone) < 0) goto out_free; atomic_set(&tmpl->ct_general.use, 0); return tmpl; out_free: kfree(tmpl); return NULL; } static void rpl_nf_ct_tmpl_free(struct nf_conn *tmpl) { nf_ct_ext_destroy(tmpl); nf_ct_ext_free(tmpl); kfree(tmpl); } #define nf_ct_tmpl_free rpl_nf_ct_tmpl_free #endif /* HAVE_NF_CT_TMPL_ALLOC */ #endif /* _NF_CONNTRACK_CORE_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/PaxHeaders.82075/nf_conntrack_expect.h0000644000000000000000000000013013534540071030445 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.509858793 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/nf_conntrack_expect.h0000644000175000017500000000113313534540071032133 0ustar00jpettitjpettit00000000000000#ifndef _NF_CONNTRACK_EXPECT_WRAPPER_H #define _NF_CONNTRACK_EXPECT_WRAPPER_H #include_next #ifndef HAVE_NF_CT_ZONE_INIT #include #include static inline struct nf_conntrack_expect * rpl___nf_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, const struct nf_conntrack_tuple *tuple) { return __nf_ct_expect_find(net, zone->id, tuple); } #define __nf_ct_expect_find rpl___nf_ct_expect_find #endif /* HAVE_NF_CT_ZONE_INIT */ #endif /* _NF_CONNTRACK_EXPECT_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/PaxHeaders.82075/ipv60000644000000000000000000000013213534540121025064 xustar0030 mtime=1567801425.513858823 30 atime=1567801425.625859648 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/ipv6/0000755000175000017500000000000013534540121026627 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/ipv6/PaxHeaders.82075/nf_defrag_ipv6.h0000644000000000000000000000013013534540071030173 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/ipv6/nf_defrag_ipv6.h0000644000175000017500000000316613534540071031671 0ustar00jpettitjpettit00000000000000#ifndef _NF_DEFRAG_IPV6_WRAPPER_H #define _NF_DEFRAG_IPV6_WRAPPER_H #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) #include_next #endif /* Upstream commit 029f7f3b8701 ("netfilter: ipv6: nf_defrag: avoid/free clone * operations") changed the semantics of nf_ct_frag6_gather(), so we backport * it for all prior kernels. */ #if defined(HAVE_NF_CT_FRAG6_CONSUME_ORIG) || \ defined(HAVE_NF_CT_FRAG6_OUTPUT) #if defined(OVS_FRAGMENT_BACKPORT) #define OVS_NF_DEFRAG6_BACKPORT 1 struct sk_buff *rpl_nf_ct_frag6_gather(struct sk_buff *skb, u32 user); int __init rpl_nf_ct_frag6_init(void); void rpl_nf_ct_frag6_cleanup(void); void rpl_nf_ct_frag6_consume_orig(struct sk_buff *skb); #else /* !OVS_FRAGMENT_BACKPORT */ static inline struct sk_buff *rpl_nf_ct_frag6_gather(struct sk_buff *skb, u32 user) { return skb; } static inline int __init rpl_nf_ct_frag6_init(void) { return 0; } static inline void rpl_nf_ct_frag6_cleanup(void) { } static inline void rpl_nf_ct_frag6_consume_orig(struct sk_buff *skb) { } #endif /* OVS_FRAGMENT_BACKPORT */ #define nf_ct_frag6_gather rpl_nf_ct_frag6_gather #else /* HAVE_NF_CT_FRAG6_CONSUME_ORIG */ static inline int __init rpl_nf_ct_frag6_init(void) { return 0; } static inline void rpl_nf_ct_frag6_cleanup(void) { } static inline void rpl_nf_ct_frag6_consume_orig(struct sk_buff *skb) { } #endif /* HAVE_NF_CT_FRAG6_CONSUME_ORIG */ #define nf_ct_frag6_init rpl_nf_ct_frag6_init #define nf_ct_frag6_cleanup rpl_nf_ct_frag6_cleanup #define nf_ct_frag6_consume_orig rpl_nf_ct_frag6_consume_orig #endif /* __NF_DEFRAG_IPV6_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/PaxHeaders.82075/nf_conntrack_labels.h0000644000000000000000000000013013534540071030417 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.509858793 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/nf_conntrack_labels.h0000644000175000017500000000230713534540071032111 0ustar00jpettitjpettit00000000000000#ifndef _NF_CONNTRACK_LABELS_WRAPPER_H #define _NF_CONNTRACK_LABELS_WRAPPER_H #include #include #include_next #ifndef HAVE_NF_CONNLABELS_GET #if IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) #ifndef NF_CT_LABELS_MAX_SIZE #define NF_CT_LABELS_MAX_SIZE ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE) #endif /* XXX: This doesn't lock others out from doing the same configuration * simultaneously. */ static inline int nf_connlabels_get(struct net *net, unsigned int n_bits) { size_t words; if (n_bits > (NF_CT_LABELS_MAX_SIZE * BITS_PER_BYTE)) return -ERANGE; words = BITS_TO_LONGS(n_bits); net->ct.labels_used++; if (words > net->ct.label_words) net->ct.label_words = words; return 0; } static inline void nf_connlabels_put(struct net *net) { net->ct.labels_used--; if (net->ct.labels_used == 0) net->ct.label_words = 0; } #else /* CONFIG_NF_CONNTRACK_LABELS */ static inline int nf_connlabels_get(struct net *net, unsigned int n_bits) { return -ERANGE; } static inline void nf_connlabels_put(struct net *net) { } #endif /* CONFIG_NF_CONNTRACK_LABELS */ #endif /* HAVE_NF_CONNLABELS_GET */ #endif /* _NF_CONNTRACK_LABELS_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/PaxHeaders.82075/nf_conntrack_zones.h0000644000000000000000000000013013534540071030313 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.509858793 openvswitch-2.5.9/datapath/linux/compat/include/net/netfilter/nf_conntrack_zones.h0000644000175000017500000000503113534540071032002 0ustar00jpettitjpettit00000000000000#ifndef _NF_CONNTRACK_ZONES_WRAPPER_H #define _NF_CONNTRACK_ZONES_WRAPPER_H #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) #include_next #endif #ifndef HAVE_NF_CT_ZONE_INIT #include #include #include #define NF_CT_DEFAULT_ZONE_ID 0 #define NF_CT_ZONE_DIR_ORIG (1 << IP_CT_DIR_ORIGINAL) #define NF_CT_ZONE_DIR_REPL (1 << IP_CT_DIR_REPLY) #define NF_CT_DEFAULT_ZONE_DIR (NF_CT_ZONE_DIR_ORIG | NF_CT_ZONE_DIR_REPL) #define NF_CT_FLAG_MARK 1 struct rpl_nf_conntrack_zone { u16 id; u8 flags; u8 dir; }; #define nf_conntrack_zone rpl_nf_conntrack_zone extern const struct nf_conntrack_zone nf_ct_zone_dflt; #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include static inline const struct nf_conntrack_zone * rpl_nf_ct_zone(const struct nf_conn *ct) { const struct nf_conntrack_zone *nf_ct_zone = NULL; #ifdef CONFIG_NF_CONNTRACK_ZONES nf_ct_zone = nf_ct_ext_find(ct, NF_CT_EXT_ZONE); #endif return nf_ct_zone ? nf_ct_zone : &nf_ct_zone_dflt; } #define nf_ct_zone rpl_nf_ct_zone static inline const struct nf_conntrack_zone * nf_ct_zone_init(struct nf_conntrack_zone *zone, u16 id, u8 dir, u8 flags) { zone->id = id; zone->flags = flags; zone->dir = dir; return zone; } static inline int nf_ct_zone_add(struct nf_conn *ct, gfp_t flags, const struct nf_conntrack_zone *info) { #ifdef CONFIG_NF_CONNTRACK_ZONES struct nf_conntrack_zone *nf_ct_zone; nf_ct_zone = nf_ct_ext_add(ct, NF_CT_EXT_ZONE, flags); if (!nf_ct_zone) return -ENOMEM; nf_ct_zone_init(nf_ct_zone, info->id, info->dir, info->flags); #endif return 0; } static inline bool nf_ct_zone_matches_dir(const struct nf_conntrack_zone *zone, enum ip_conntrack_dir dir) { return zone->dir & (1 << dir); } static inline u16 nf_ct_zone_id(const struct nf_conntrack_zone *zone, enum ip_conntrack_dir dir) { return nf_ct_zone_matches_dir(zone, dir) ? zone->id : NF_CT_DEFAULT_ZONE_ID; } static inline bool nf_ct_zone_equal(const struct nf_conn *a, const struct nf_conntrack_zone *b, enum ip_conntrack_dir dir) { return nf_ct_zone_id(nf_ct_zone(a), dir) == nf_ct_zone_id(b, dir); } static inline bool nf_ct_zone_equal_any(const struct nf_conn *a, const struct nf_conntrack_zone *b) { return nf_ct_zone(a)->id == b->id; } #endif /* IS_ENABLED(CONFIG_NF_CONNTRACK) */ #endif /* HAVE_NF_CT_ZONE_INIT */ #endif /* _NF_CONNTRACK_ZONES_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/stt.h0000644000000000000000000000013013534540071023246 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.501858734 openvswitch-2.5.9/datapath/linux/compat/include/net/stt.h0000644000175000017500000000306213534540071024737 0ustar00jpettitjpettit00000000000000#ifndef __NET_STT_H #define __NET_STT_H 1 #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) && IS_ENABLED(CONFIG_NETFILTER) #include #define OVS_STT struct stthdr { __u8 version; __u8 flags; __u8 l4_offset; __u8 reserved; __be16 mss; __be16 vlan_tci; __be64 key; }; /* Padding after the end of the tunnel headers to provide alignment * for inner packet IP header after 14 byte Ethernet header. */ #define STT_ETH_PAD 2 #define STT_BASE_HLEN (sizeof(struct stthdr) + STT_ETH_PAD) #define STT_HEADER_LEN (sizeof(struct tcphdr) + STT_BASE_HLEN) static inline struct stthdr *stt_hdr(const struct sk_buff *skb) { return (struct stthdr *)(skb_transport_header(skb) + sizeof(struct tcphdr)); } struct net_device *ovs_stt_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port); netdev_tx_t ovs_stt_xmit(struct sk_buff *skb); int ovs_stt_init_module(void); void ovs_stt_cleanup_module(void); #else static inline int ovs_stt_init_module(void) { return 0; } static inline void ovs_stt_cleanup_module(void) {} static inline struct net_device *ovs_stt_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port) { return ERR_PTR(-EOPNOTSUPP); } static inline netdev_tx_t ovs_stt_xmit(struct sk_buff *skb) { BUG(); return NETDEV_TX_OK; } #endif #define stt_dev_create_fb ovs_stt_dev_create_fb #define stt_init_module ovs_stt_init_module #define stt_cleanup_module ovs_stt_cleanup_module #endif /*ifdef__NET_STT_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/udp.h0000644000000000000000000000013013534540071023224 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.501858734 openvswitch-2.5.9/datapath/linux/compat/include/net/udp.h0000644000175000017500000000326013534540071024715 0ustar00jpettitjpettit00000000000000#ifndef __NET_UDP_WRAPPER_H #define __NET_UDP_WRAPPER_H 1 #include #ifdef inet_get_local_port_range /* Earlier RHEL7 kernels backport udp_flow_src_port() using an older version of * inet_get_local_port_range(). */ #undef inet_get_local_port_range #include_next #define inet_get_local_port_range rpl_inet_get_local_port_range #else #include_next #endif #ifndef HAVE_UDP_FLOW_SRC_PORT static inline __be16 rpl_udp_flow_src_port(struct net *net, struct sk_buff *skb, int min, int max, bool use_eth) { u32 hash; if (min >= max) { /* Use default range */ inet_get_local_port_range(net, &min, &max); } hash = skb_get_hash(skb); if (unlikely(!hash) && use_eth) { /* Can't find a normal hash, caller has indicated an Ethernet * packet so use that to compute a hash. */ hash = jhash(skb->data, 2 * ETH_ALEN, (__force u32) skb->protocol); } /* Since this is being sent on the wire obfuscate hash a bit * to minimize possbility that any useful information to an * attacker is leaked. Only upper 16 bits are relevant in the * computation for 16 bit port value. */ hash ^= hash << 16; return htons((((u64) hash * (max - min)) >> 32) + min); } #define udp_flow_src_port rpl_udp_flow_src_port #endif #ifndef HAVE_UDP_V4_CHECK static inline __sum16 udp_v4_check(int len, __be32 saddr, __be32 daddr, __wsum base) { return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) #define udp_set_csum rpl_udp_set_csum void rpl_udp_set_csum(bool nocheck, struct sk_buff *skb, __be32 saddr, __be32 daddr, int len); #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/vrf.h0000644000000000000000000000013013534540071023231 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.505858763 openvswitch-2.5.9/datapath/linux/compat/include/net/vrf.h0000644000175000017500000000124313534540071024721 0ustar00jpettitjpettit00000000000000/* * include/net/net_vrf.h - adds vrf dev structure definitions * Copyright (c) 2015 Cumulus Networks * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #ifndef __LINUX_NET_VRF_WRAPPER_H #define __LINUX_NET_VRF_WRAPPER_H #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) #include_next #else static inline int vrf_master_ifindex_rcu(const struct net_device *dev) { return 0; } #endif #endif /* __LINUX_NET_VRF_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/inetpeer.h0000644000000000000000000000013213534540071024251 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.489858645 openvswitch-2.5.9/datapath/linux/compat/include/net/inetpeer.h0000644000175000017500000000074413534540071025744 0ustar00jpettitjpettit00000000000000#ifndef _NET_INETPEER_WRAPPER_H #define _NET_INETPEER_WRAPPER_H #include_next #if defined(OVS_FRAGMENT_BACKPORT) && \ !defined(HAVE_INETPEER_VIF_SUPPORT) static inline struct inet_peer *rpl_inet_getpeer_v4(struct inet_peer_base *base, __be32 v4daddr, int vif, int create) { return inet_getpeer_v4(base, v4daddr, create); } #define inet_getpeer_v4 rpl_inet_getpeer_v4 #endif /* OVS_FRAGMENT_BACKPORT */ #endif /* _NET_INETPEER_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/dst_metadata.h0000644000000000000000000000013213534540071025070 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.481858586 openvswitch-2.5.9/datapath/linux/compat/include/net/dst_metadata.h0000644000175000017500000000204113534540071026553 0ustar00jpettitjpettit00000000000000#ifndef __NET_DST_METADATA_WRAPPER_H #define __NET_DST_METADATA_WRAPPER_H 1 #ifdef HAVE_METADATA_DST #include_next #else #include #include #include struct metadata_dst { unsigned long dst; union { struct ip_tunnel_info tun_info; } u; }; static inline struct metadata_dst *metadata_dst_alloc(u8 optslen, gfp_t flags) { struct metadata_dst *md_dst; md_dst = kmalloc(sizeof(*md_dst) + optslen, flags); if (!md_dst) return NULL; return md_dst; } #define skb_tunnel_info ovs_skb_tunnel_info #endif static inline void ovs_ip_tun_rx_dst(struct ip_tunnel_info *tun_info, struct sk_buff *skb, __be16 flags, __be64 tunnel_id, int md_size) { const struct iphdr *iph = ip_hdr(skb); ip_tunnel_key_init(&tun_info->key, iph->saddr, iph->daddr, iph->tos, iph->ttl, 0, 0, tunnel_id, flags); tun_info->mode = 0; } void ovs_ip_tunnel_rcv(struct net_device *dev, struct sk_buff *skb, struct metadata_dst *tun_dst); #endif /* __NET_DST_METADATA_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/checksum.h0000644000000000000000000000013213534540071024240 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.477858557 openvswitch-2.5.9/datapath/linux/compat/include/net/checksum.h0000644000175000017500000000236113534540071025730 0ustar00jpettitjpettit00000000000000#ifndef __NET_CHECKSUM_WRAPPER_H #define __NET_CHECKSUM_WRAPPER_H 1 #include_next #ifndef HAVE_CSUM_UNFOLD static inline __wsum csum_unfold(__sum16 n) { return (__force __wsum)n; } #endif /* !HAVE_CSUM_UNFOLD */ /* Workaround for debugging included in certain versions of XenServer. It only * applies to 32-bit x86. */ #if defined(HAVE_CSUM_COPY_DBG) && defined(CONFIG_X86_32) #define csum_and_copy_to_user(src, dst, len, sum, err_ptr) \ csum_and_copy_to_user(src, dst, len, sum, NULL, err_ptr) #endif #ifndef HAVE_CSUM_REPLACE4 static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to) { __be32 diff[] = { ~from, to }; *sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum))); } static inline void csum_replace2(__sum16 *sum, __be16 from, __be16 to) { csum_replace4(sum, (__force __be32)from, (__force __be32)to); } #endif #ifndef CSUM_MANGLED_0 #define CSUM_MANGLED_0 ((__force __sum16)0xffff) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) #define inet_proto_csum_replace16 rpl_inet_proto_csum_replace16 void rpl_inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, const __be32 *from, const __be32 *to, int pseudohdr); #endif #endif /* checksum.h */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/vxlan.h0000644000000000000000000000013013534540071023564 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.505858763 openvswitch-2.5.9/datapath/linux/compat/include/net/vxlan.h0000644000175000017500000001637313534540071025266 0ustar00jpettitjpettit00000000000000#ifndef __NET_VXLAN_WRAPPER_H #define __NET_VXLAN_WRAPPER_H 1 #ifdef CONFIG_INET #include #endif #ifdef HAVE_METADATA_DST #include_next static inline int rpl_vxlan_init_module(void) { return 0; } static inline void rpl_vxlan_cleanup_module(void) {} #define vxlan_xmit dev_queue_xmit #else #include #include #include #include #include #include #include #include "compat.h" #include "gso.h" #define VNI_HASH_BITS 10 #define VNI_HASH_SIZE (1<" #endif __be16 policy_id; __be32 vx_vni; }; #define VXLAN_GBP_USED_BITS (VXLAN_HF_GBP | 0xFFFFFF) /* skb->mark mapping * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |R|R|R|R|R|R|R|R|R|D|R|R|A|R|R|R| Group Policy ID | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define VXLAN_GBP_DONT_LEARN (BIT(6) << 16) #define VXLAN_GBP_POLICY_APPLIED (BIT(3) << 16) #define VXLAN_GBP_ID_MASK (0xFFFF) /* VXLAN protocol header: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |G|R|R|R|I|R|R|C| Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | VXLAN Network Identifier (VNI) | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * G = 1 Group Policy (VXLAN-GBP) * I = 1 VXLAN Network Identifier (VNI) present * C = 1 Remote checksum offload (RCO) */ struct vxlanhdr { __be32 vx_flags; __be32 vx_vni; }; /* VXLAN header flags. */ #define VXLAN_HF_RCO BIT(21) #define VXLAN_HF_VNI BIT(27) #define VXLAN_HF_GBP BIT(31) /* Remote checksum offload header option */ #define VXLAN_RCO_MASK 0x7f /* Last byte of vni field */ #define VXLAN_RCO_UDP 0x80 /* Indicate UDP RCO (TCP when not set *) */ #define VXLAN_RCO_SHIFT 1 /* Left shift of start */ #define VXLAN_RCO_SHIFT_MASK ((1 << VXLAN_RCO_SHIFT) - 1) #define VXLAN_MAX_REMCSUM_START (VXLAN_RCO_MASK << VXLAN_RCO_SHIFT) #define VXLAN_N_VID (1u << 24) #define VXLAN_VID_MASK (VXLAN_N_VID - 1) #define VXLAN_VNI_MASK (VXLAN_VID_MASK << 8) #define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) struct vxlan_metadata { __be32 vni; u32 gbp; }; #define VNI_HASH_BITS 10 #define VNI_HASH_SIZE (1<vn_sock->sock->sk); } static inline netdev_features_t vxlan_features_check(struct sk_buff *skb, netdev_features_t features) { u8 l4_hdr = 0; if (!skb_encapsulation(skb)) return features; switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): l4_hdr = ip_hdr(skb)->protocol; break; case htons(ETH_P_IPV6): l4_hdr = ipv6_hdr(skb)->nexthdr; break; default: return features; } if ((l4_hdr == IPPROTO_UDP) && ( #ifdef ENCAP_TYPE_ETHER skb->inner_protocol_type != ENCAP_TYPE_ETHER || #endif ovs_skb_get_inner_protocol(skb) != htons(ETH_P_TEB) || (skb_inner_mac_header(skb) - skb_transport_header(skb) != sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); return features; } /* IP header + UDP + VXLAN + Ethernet header */ #define VXLAN_HEADROOM (20 + 8 + 8 + 14) /* IPv6 header + UDP + VXLAN + Ethernet header */ #define VXLAN6_HEADROOM (40 + 8 + 8 + 14) static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) { return vs->sock->sk->sk_family; } int rpl_vxlan_init_module(void); void rpl_vxlan_cleanup_module(void); #define vxlan_xmit rpl_vxlan_xmit netdev_tx_t rpl_vxlan_xmit(struct sk_buff *skb); #endif #define vxlan_init_module rpl_vxlan_init_module #define vxlan_cleanup_module rpl_vxlan_cleanup_module #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/netlink.h0000644000000000000000000000013013534540071024100 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.497858704 openvswitch-2.5.9/datapath/linux/compat/include/net/netlink.h0000644000175000017500000000460013534540071025570 0ustar00jpettitjpettit00000000000000#ifndef __NET_NETLINK_WRAPPER_H #define __NET_NETLINK_WRAPPER_H 1 #include #include_next #include_next #ifndef HAVE_NLA_GET_BE16 /** * nla_get_be16 - return payload of __be16 attribute * @nla: __be16 netlink attribute */ static inline __be16 nla_get_be16(const struct nlattr *nla) { return *(__be16 *) nla_data(nla); } #endif /* !HAVE_NLA_GET_BE16 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) /* This function was introduced in 2.6.31, but initially it performed an * unaligned access, so we replace it up to 2.6.34 where it was fixed. */ #define nla_get_be64 rpl_nla_get_be64 static inline __be64 nla_get_be64(const struct nlattr *nla) { __be64 tmp; /* The additional cast is necessary because */ nla_memcpy(&tmp, (struct nlattr *) nla, sizeof(tmp)); return tmp; } #endif #ifndef HAVE_NLA_PUT_BE16 static inline int nla_put_be16(struct sk_buff *skb, int attrtype, __be16 value) { return nla_put(skb, attrtype, sizeof(__be16), &value); } #endif #ifndef HAVE_NLA_PUT_BE32 static inline int nla_put_be32(struct sk_buff *skb, int attrtype, __be32 value) { return nla_put(skb, attrtype, sizeof(__be32), &value); } #endif #ifndef HAVE_NLA_PUT_BE64 static inline int nla_put_be64(struct sk_buff *skb, int attrtype, __be64 value) { return nla_put(skb, attrtype, sizeof(__be64), &value); } #endif #ifndef nla_for_each_nested #define nla_for_each_nested(pos, nla, rem) \ nla_for_each_attr(pos, nla_data(nla), nla_len(nla), rem) #endif #ifndef HAVE_NLA_FIND_NESTED static inline struct nlattr *nla_find_nested(struct nlattr *nla, int attrtype) { return nla_find(nla_data(nla), nla_len(nla), attrtype); } #endif #ifndef HAVE_NLA_IS_LAST static inline bool nla_is_last(const struct nlattr *nla, int rem) { return nla->nla_len == rem; } #endif #ifndef HAVE_NLA_PUT_IN_ADDR static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype, __be32 addr) { return nla_put_be32(skb, attrtype, addr); } static inline int nla_put_in6_addr(struct sk_buff *skb, int attrtype, const struct in6_addr *addr) { return nla_put(skb, attrtype, sizeof(*addr), addr); } static inline __be32 nla_get_in_addr(const struct nlattr *nla) { return *(__be32 *) nla_data(nla); } static inline struct in6_addr nla_get_in6_addr(const struct nlattr *nla) { struct in6_addr tmp; nla_memcpy(&tmp, nla, sizeof(tmp)); return tmp; } #endif #endif /* net/netlink.h */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/flow_keys.h0000644000000000000000000000013213534540071024440 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.481858586 openvswitch-2.5.9/datapath/linux/compat/include/net/flow_keys.h0000644000175000017500000000060313534540071026125 0ustar00jpettitjpettit00000000000000#ifndef _NET_FLOW_KEYS_WRAPPER_H #define _NET_FLOW_KEYS_WRAPPER_H #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) #include_next #else struct flow_keys { /* (src,dst) must be grouped, in the same way than in IP header */ __be32 src; __be32 dst; union { __be32 ports; __be16 port16[2]; }; u16 thoff; u8 ip_proto; }; #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/ipv6.h0000644000000000000000000000013013534540071023320 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.493858676 openvswitch-2.5.9/datapath/linux/compat/include/net/ipv6.h0000644000175000017500000000302613534540071025011 0ustar00jpettitjpettit00000000000000#ifndef __NET_IPV6_WRAPPER_H #define __NET_IPV6_WRAPPER_H 1 #include #include_next #ifndef NEXTHDR_SCTP #define NEXTHDR_SCTP 132 /* Stream Control Transport Protocol */ #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) #define ipv6_skip_exthdr rpl_ipv6_skip_exthdr extern int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, __be16 *frag_offp); #endif #ifndef HAVE_IP6_FH_F_SKIP_RH enum { IP6_FH_F_FRAG = (1 << 0), IP6_FH_F_AUTH = (1 << 1), IP6_FH_F_SKIP_RH = (1 << 2), }; /* This function is upstream, but not the version which skips routing * headers with 0 segments_left. We fixed it when we introduced * IP6_FH_F_SKIP_RH. */ #define ipv6_find_hdr rpl_ipv6_find_hdr extern int rpl_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff, int *fragflg); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) static inline u32 ipv6_addr_hash(const struct in6_addr *a) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 const unsigned long *ul = (const unsigned long *)a; unsigned long x = ul[0] ^ ul[1]; return (u32)(x ^ (x >> 32)); #else return (__force u32)(a->s6_addr32[0] ^ a->s6_addr32[1] ^ a->s6_addr32[2] ^ a->s6_addr32[3]); #endif } #endif #if defined(OVS_FRAGMENT_BACKPORT) && !defined(HAVE___IPV6_ADDR_JHASH) static inline u32 __ipv6_addr_jhash(const struct in6_addr *a, const u32 unused) { return ipv6_addr_jhash(a); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/ip6_tunnel.h0000644000000000000000000000013013534540071024517 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.493858676 openvswitch-2.5.9/datapath/linux/compat/include/net/ip6_tunnel.h0000644000175000017500000000140413534540071026206 0ustar00jpettitjpettit00000000000000#ifndef _NET_IP6_TUNNEL_WRAPER_H #define _NET_IP6_TUNNEL_WRAPER_H #include #include #include #include #include_next #include "gso.h" #define ip6tunnel_xmit rpl_ip6tunnel_xmit static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, struct net_device *dev) { int pkt_len, err; pkt_len = skb->len - skb_inner_network_offset(skb); /* TODO: Fix GSO for ipv6 */ #ifdef HAVE_IP6_LOCAL_OUT_SK err = ip6_local_out_sk(sk, skb); #else err = ip6_local_out(skb); #endif if (net_xmit_eval(err) != 0) pkt_len = net_xmit_eval(err); else pkt_len = err; iptunnel_xmit_stats(pkt_len, &dev->stats, (struct pcpu_sw_netstats __percpu *)dev->tstats); } #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/genetlink.h0000644000000000000000000000013213534540071024416 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.481858586 openvswitch-2.5.9/datapath/linux/compat/include/net/genetlink.h0000644000175000017500000001057013534540071026107 0ustar00jpettitjpettit00000000000000#ifndef __NET_GENERIC_NETLINK_WRAPPER_H #define __NET_GENERIC_NETLINK_WRAPPER_H 1 #include #include #include #include_next /* * 15e473046cb6e5d18a4d0057e61d76315230382b renames pid to portid * the affected structures are * netlink_skb_parms::pid -> portid * genl_info::snd_pid -> snd_portid */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) #define snd_portid snd_pid #define portid pid #endif #ifndef HAVE_GENL_NOTIFY_TAKES_FAMILY struct rpl_genl_family { struct genl_family compat_family; unsigned int id; unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; bool netnsok; bool parallel_ops; int (*pre_doit)(const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); void (*post_doit)(const struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info); struct nlattr ** attrbuf; /* private */ const struct genl_ops * ops; /* private */ const struct genl_multicast_group *mcgrps; /* private */ unsigned int n_ops; /* private */ unsigned int n_mcgrps; /* private */ unsigned int mcgrp_offset; /* private */ struct list_head family_list; /* private */ struct module *module; }; #define genl_family rpl_genl_family #define genl_notify rpl_genl_notify void rpl_genl_notify(struct genl_family *family, struct sk_buff *skb, struct net *net, u32 portid, u32 group, struct nlmsghdr *nlh, gfp_t flags); static inline void *rpl_genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, struct genl_family *family, int flags, u8 cmd) { return genlmsg_put(skb, portid, seq, &family->compat_family, flags, cmd); } #define genlmsg_put rpl_genlmsg_put static inline int rpl_genl_unregister_family(struct genl_family *family) { return genl_unregister_family(&family->compat_family); } #define genl_unregister_family rpl_genl_unregister_family #define genl_set_err rpl_genl_set_err static inline int genl_set_err(struct genl_family *family, struct net *net, u32 portid, u32 group, int code) { #ifdef HAVE_VOID_NETLINK_SET_ERR netlink_set_err(net->genl_sock, portid, group, code); return 0; #else return netlink_set_err(net->genl_sock, portid, group, code); #endif } #define genlmsg_multicast_netns rpl_genlmsg_multicast_netns static inline int genlmsg_multicast_netns(struct genl_family *family, struct net *net, struct sk_buff *skb, u32 portid, unsigned int group, gfp_t flags) { return nlmsg_multicast(net->genl_sock, skb, portid, group, flags); } #define __genl_register_family rpl___genl_register_family int rpl___genl_register_family(struct genl_family *family); #define genl_register_family rpl_genl_register_family static inline int rpl_genl_register_family(struct genl_family *family) { family->module = THIS_MODULE; return rpl___genl_register_family(family); } #endif #ifndef HAVE_GENLMSG_NEW_UNICAST static inline struct sk_buff *genlmsg_new_unicast(size_t payload, struct genl_info *info, gfp_t flags) { return genlmsg_new(payload, flags); } #endif #ifndef HAVE_GENL_HAS_LISTENERS static inline int genl_has_listeners(struct genl_family *family, struct net *net, unsigned int group) { #ifdef HAVE_MCGRP_OFFSET if (WARN_ON_ONCE(group >= family->n_mcgrps)) return -EINVAL; group = family->mcgrp_offset + group; #endif return netlink_has_listeners(net->genl_sock, group); } #else #ifndef HAVE_GENL_HAS_LISTENERS_TAKES_NET static inline int rpl_genl_has_listeners(struct genl_family *family, struct net *net, unsigned int group) { #ifdef HAVE_GENL_NOTIFY_TAKES_FAMILY return genl_has_listeners(family, net->genl_sock, group); #else return genl_has_listeners(&family->compat_family, net->genl_sock, group); #endif } #define genl_has_listeners rpl_genl_has_listeners #endif #endif /* HAVE_GENL_HAS_LISTENERS */ #ifndef HAVE_GENLMSG_PARSE static inline int genlmsg_parse(const struct nlmsghdr *nlh, const struct genl_family *family, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { return nlmsg_parse(nlh, family->hdrsize + GENL_HDRLEN, tb, maxtype, policy); } #endif #endif /* genetlink.h */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/inet_frag.h0000644000000000000000000000013213534540071024374 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.485858616 openvswitch-2.5.9/datapath/linux/compat/include/net/inet_frag.h0000644000175000017500000000475513534540071026075 0ustar00jpettitjpettit00000000000000#ifndef __NET_INET_FRAG_WRAPPER_H #define __NET_INET_FRAG_WRAPPER_H 1 #include #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) #define inet_frag_evictor(nf, f, force) \ do { \ if (force || atomic_read(&nf->mem) > nf->high_thresh) { \ inet_frag_evictor(nf, f); \ } \ } while (0) #endif #ifdef OVS_FRAGMENT_BACKPORT #ifdef HAVE_INET_FRAGS_LAST_IN #define q_flags(q) (q->last_in) #define qp_flags(qp) (qp->q.last_in) #else #define q_flags(q) (q->flags) #define qp_flags(qp) (qp->q.flags) #endif #ifndef HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR /** * struct ovs_inet_frag_queue - fragment queue * * Wrap the system inet_frag_queue to provide a list evictor. * * @list_evictor: list of queues to forcefully evict (e.g. due to low memory) */ struct ovs_inet_frag_queue { struct inet_frag_queue fq; struct hlist_node list_evictor; }; static inline bool rpl_inet_frag_evicting(struct inet_frag_queue *q) { #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK struct ovs_inet_frag_queue *ofq = (struct ovs_inet_frag_queue *)q; return !hlist_unhashed(&ofq->list_evictor); #else return (q_flags(q) & INET_FRAG_FIRST_IN) && q->fragments != NULL; #endif } #define inet_frag_evicting rpl_inet_frag_evicting #else /* HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR */ #ifndef HAVE_INET_FRAG_EVICTING static inline bool rpl_inet_frag_evicting(struct inet_frag_queue *q) { return !hlist_unhashed(&q->list_evictor); } #define inet_frag_evicting rpl_inet_frag_evicting #endif #endif #ifndef HAVE_CORRECT_MRU_HANDLING static unsigned int rpl_frag_percpu_counter_batch = 130000; #define frag_percpu_counter_batch rpl_frag_percpu_counter_batch static inline void rpl_sub_frag_mem_limit(struct netns_frags *nf, int i) { __percpu_counter_add(&nf->mem, -i, frag_percpu_counter_batch); } #define sub_frag_mem_limit rpl_sub_frag_mem_limit static inline void rpl_add_frag_mem_limit(struct netns_frags *nf, int i) { __percpu_counter_add(&nf->mem, i, frag_percpu_counter_batch); } #define add_frag_mem_limit rpl_add_frag_mem_limit int rpl_inet_frags_init(struct inet_frags *f); #define inet_frags_init rpl_inet_frags_init void rpl_inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); #define inet_frags_exit_net rpl_inet_frags_exit_net void rpl_inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f); #define inet_frag_destroy(q, f, work) rpl_inet_frag_destroy(q, f) #endif /* !HAVE_CORRECT_MRU_HANDLING */ #endif /* OVS_FRAGMENT_BACKPORT */ #endif /* inet_frag.h */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/sctp0000644000000000000000000000013213534540121023155 xustar0030 mtime=1567801425.513858823 30 atime=1567801425.625859648 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/linux/compat/include/net/sctp/0000755000175000017500000000000013534540121024720 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/net/sctp/PaxHeaders.82075/checksum.h0000644000000000000000000000013013534540071025207 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.513858823 openvswitch-2.5.9/datapath/linux/compat/include/net/sctp/checksum.h0000644000175000017500000000130713534540071026700 0ustar00jpettitjpettit00000000000000#ifndef __SCTP_CHECKSUM_WRAPPER_H #define __SCTP_CHECKSUM_WRAPPER_H 1 #include_next #ifndef HAVE_SCTP_COMPUTE_CKSUM static inline __le32 sctp_compute_cksum(const struct sk_buff *skb, unsigned int offset) { const struct sk_buff *iter; __u32 crc32 = sctp_start_cksum(skb->data + offset, skb_headlen(skb) - offset); skb_walk_frags(skb, iter) crc32 = sctp_update_cksum((__u8 *) iter->data, skb_headlen(iter), crc32); /* Open-code sctp_end_cksum() to avoid a sparse warning due to a bug in * sparse annotations in Linux fixed in 3.10 in commit eee1d5a14 (sctp: * Correct type and usage of sctp_end_cksum()). */ return cpu_to_le32(~crc32); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/inet_ecn.h0000644000000000000000000000013213534540071024222 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.485858616 openvswitch-2.5.9/datapath/linux/compat/include/net/inet_ecn.h0000644000175000017500000000245613534540071025717 0ustar00jpettitjpettit00000000000000#ifndef _INET_ECN_WRAPPER_H_ #define _INET_ECN_WRAPPER_H_ #include_next #define INET_ECN_decapsulate rpl_INET_ECN_decapsulate static inline int INET_ECN_decapsulate(struct sk_buff *skb, __u8 outer, __u8 inner) { if (INET_ECN_is_not_ect(inner)) { switch (outer & INET_ECN_MASK) { case INET_ECN_NOT_ECT: return 0; case INET_ECN_ECT_0: case INET_ECN_ECT_1: return 1; case INET_ECN_CE: return 2; } } if (INET_ECN_is_ce(outer)) INET_ECN_set_ce(skb); return 0; } #define IP_ECN_decapsulate rpl_IP_ECN_decapsulate static inline int IP_ECN_decapsulate(const struct iphdr *oiph, struct sk_buff *skb) { __u8 inner; if (skb->protocol == htons(ETH_P_IP)) inner = ip_hdr(skb)->tos; else if (skb->protocol == htons(ETH_P_IPV6)) inner = ipv6_get_dsfield(ipv6_hdr(skb)); else return 0; return INET_ECN_decapsulate(skb, oiph->tos, inner); } #define IP6_ECN_decapsulate rpl_IP6_ECN_decapsulate static inline int IP6_ECN_decapsulate(const struct ipv6hdr *oipv6h, struct sk_buff *skb) { __u8 inner; if (skb->protocol == htons(ETH_P_IP)) inner = ip_hdr(skb)->tos; else if (skb->protocol == htons(ETH_P_IPV6)) inner = ipv6_get_dsfield(ipv6_hdr(skb)); else return 0; return INET_ECN_decapsulate(skb, ipv6_get_dsfield(oipv6h), inner); } #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/rtnetlink.h0000644000000000000000000000013013534540071024446 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.497858704 openvswitch-2.5.9/datapath/linux/compat/include/net/rtnetlink.h0000644000175000017500000000157313534540071026144 0ustar00jpettitjpettit00000000000000#ifndef __NET_RTNETLINK_WRAPPER_H #define __NET_RTNETLINK_WRAPPER_H #include_next #define rtnl_delete_link rpl_rtnl_delete_link int rpl_rtnl_delete_link(struct net_device *dev); #ifndef HAVE_NAME_ASSIGN_TYPE #ifdef HAVE_RTNL_CREATE_LINK_SRC_NET static inline struct net_device *rpl_rtnl_create_link(struct net *net, const char *ifname, unsigned char name_assign_type, const struct rtnl_link_ops *ops, struct nlattr *tb[]) { return rtnl_create_link(net, net, (char *)ifname, ops, tb); } #else static inline struct net_device *rpl_rtnl_create_link(struct net *net, const char *ifname, unsigned char name_assign_type, const struct rtnl_link_ops *ops, struct nlattr *tb[]) { return rtnl_create_link(net, (char *)ifname, ops, tb); } #endif #define rtnl_create_link rpl_rtnl_create_link #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/mpls.h0000644000000000000000000000013013534540071023407 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.493858676 openvswitch-2.5.9/datapath/linux/compat/include/net/mpls.h0000644000175000017500000000227013534540071025100 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #ifndef _NET_MPLS_WRAPPER_H #define _NET_MPLS_WRAPPER_H 1 #include #include #define MPLS_HLEN 4 static inline bool eth_p_mpls(__be16 eth_type) { return eth_type == htons(ETH_P_MPLS_UC) || eth_type == htons(ETH_P_MPLS_MC); } /* * For non-MPLS skbs this will correspond to the network header. * For MPLS skbs it will be before the network_header as the MPLS * label stack lies between the end of the mac header and the network * header. That is, for MPLS skbs the end of the mac header * is the top of the MPLS label stack. */ static inline unsigned char *skb_mpls_header(struct sk_buff *skb) { return skb_mac_header(skb) + skb->mac_len; } #endif /* _NET_MPLS_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/sock.h0000644000000000000000000000013013534540071023373 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.501858734 openvswitch-2.5.9/datapath/linux/compat/include/net/sock.h0000644000175000017500000000056713534540071025073 0ustar00jpettitjpettit00000000000000#ifndef __NET_SOCK_WRAPPER_H #define __NET_SOCK_WRAPPER_H 1 #include_next #ifndef __sk_user_data #define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data))) #define rcu_dereference_sk_user_data(sk) rcu_dereference(__sk_user_data((sk))) #define rcu_assign_sk_user_data(sk, ptr) rcu_assign_pointer(__sk_user_data((sk)), ptr) #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/gre.h0000644000000000000000000000013213534540071023213 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.485858616 openvswitch-2.5.9/datapath/linux/compat/include/net/gre.h0000644000175000017500000000307013534540071024701 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_GRE_WRAPPER_H #define __LINUX_GRE_WRAPPER_H #include #include #include #ifdef HAVE_METADATA_DST #include_next static inline int rpl_ipgre_init(void) { return 0; } static inline void rpl_ipgre_fini(void) {} #define gre_fb_xmit dev_queue_xmit #else #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || \ defined(HAVE_GRE_CISCO_REGISTER) #include_next #endif #ifndef HAVE_GRE_CISCO_REGISTER /* GRE demux not available, implement our own demux. */ #define MAX_GRE_PROTO_PRIORITY 255 struct gre_cisco_protocol { int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi); int (*err_handler)(struct sk_buff *skb, u32 info, const struct tnl_ptk_info *tpi); u8 priority; }; #define gre_cisco_register rpl_gre_cisco_register int rpl_gre_cisco_register(struct gre_cisco_protocol *proto); #define gre_cisco_unregister rpl_gre_cisco_unregister int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto); #ifndef GRE_HEADER_SECTION struct gre_base_hdr { __be16 flags; __be16 protocol; }; #define GRE_HEADER_SECTION 4 #endif #endif /* HAVE_GRE_CISCO_REGISTER */ int rpl_ipgre_init(void); void rpl_ipgre_fini(void); #define gretap_fb_dev_create rpl_gretap_fb_dev_create struct net_device *rpl_gretap_fb_dev_create(struct net *net, const char *name, u8 name_assign_type); #define gre_fb_xmit rpl_gre_fb_xmit netdev_tx_t rpl_gre_fb_xmit(struct sk_buff *skb); #endif /* HAVE_METADATA_DST */ #define ipgre_init rpl_ipgre_init #define ipgre_fini rpl_ipgre_fini #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/lisp.h0000644000000000000000000000013013534540071023403 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.453858381 openvswitch-2.5.9/datapath/linux/compat/include/net/lisp.h0000644000175000017500000000114713534540071025076 0ustar00jpettitjpettit00000000000000#ifndef __NET_LISP_WRAPPER_H #define __NET_LISP_WRAPPER_H 1 #ifdef CONFIG_INET #include #endif #ifdef CONFIG_INET #define lisp_dev_create_fb rpl_lisp_dev_create_fb struct net_device *rpl_lisp_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port); #endif /*ifdef CONFIG_INET */ #define lisp_init_module rpl_lisp_init_module int rpl_lisp_init_module(void); #define lisp_cleanup_module rpl_lisp_cleanup_module void rpl_lisp_cleanup_module(void); #define lisp_xmit rpl_lisp_xmit netdev_tx_t rpl_lisp_xmit(struct sk_buff *skb); #endif /*ifdef__NET_LISP_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/net_namespace.h0000644000000000000000000000013013534540071025236 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.493858676 openvswitch-2.5.9/datapath/linux/compat/include/net/net_namespace.h0000644000175000017500000000455213534540071026734 0ustar00jpettitjpettit00000000000000#ifndef __NET_NET_NAMESPACE_WRAPPER_H #define __NET_NET_NAMESPACE_WRAPPER_H 1 #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) /* for 2.6.32* */ struct rpl_pernet_operations { int (*init)(struct net *net); void (*exit)(struct net *net); int *id; size_t size; struct pernet_operations ops; }; #define pernet_operations rpl_pernet_operations #define register_pernet_device rpl_register_pernet_gen_device #define unregister_pernet_device rpl_unregister_pernet_gen_device #define register_pernet_subsys rpl_register_pernet_gen_device #define unregister_pernet_subsys rpl_unregister_pernet_gen_device #define compat_init_net ovs_compat_init_net int ovs_compat_init_net(struct net *net, struct rpl_pernet_operations *pnet); #define compat_exit_net ovs_compat_exit_net void ovs_compat_exit_net(struct net *net, struct rpl_pernet_operations *pnet); #define DEFINE_COMPAT_PNET_REG_FUNC(TYPE) \ \ static struct rpl_pernet_operations *pnet_gen_##TYPE; \ static int compat_init_net_gen_##TYPE(struct net *net) \ { \ return compat_init_net(net, pnet_gen_##TYPE); \ } \ \ static void compat_exit_net_gen_##TYPE(struct net *net) \ { \ compat_exit_net(net, pnet_gen_##TYPE); \ } \ \ static int rpl_register_pernet_gen_##TYPE(struct rpl_pernet_operations *rpl_pnet) \ { \ pnet_gen_##TYPE = rpl_pnet; \ rpl_pnet->ops.init = compat_init_net_gen_##TYPE; \ rpl_pnet->ops.exit = compat_exit_net_gen_##TYPE; \ return register_pernet_gen_##TYPE(pnet_gen_##TYPE->id, &rpl_pnet->ops); \ } \ \ static void rpl_unregister_pernet_gen_##TYPE(struct rpl_pernet_operations *rpl_pnet) \ { \ unregister_pernet_gen_##TYPE(*pnet_gen_##TYPE->id, &rpl_pnet->ops); \ } #else #define DEFINE_COMPAT_PNET_REG_FUNC(TYPE) #endif /* 2.6.33 */ #ifndef HAVE_POSSIBLE_NET_T typedef struct { #ifdef CONFIG_NET_NS struct net *net; #endif } possible_net_t; static inline void rpl_write_pnet(possible_net_t *pnet, struct net *net) { #ifdef CONFIG_NET_NS pnet->net = net; #endif } static inline struct net *rpl_read_pnet(const possible_net_t *pnet) { #ifdef CONFIG_NET_NS return pnet->net; #else return &init_net; #endif } #else /* Linux >= 4.1 */ #define rpl_read_pnet read_pnet #define rpl_write_pnet write_pnet #endif /* Linux >= 4.1 */ #endif /* net/net_namespace.h wrapper */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/udp_tunnel.h0000644000000000000000000000013013534540071024611 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.501858734 openvswitch-2.5.9/datapath/linux/compat/include/net/udp_tunnel.h0000644000175000017500000001011113534540071026273 0ustar00jpettitjpettit00000000000000#ifndef __NET_UDP_TUNNEL_WRAPPER_H #define __NET_UDP_TUNNEL_WRAPPER_H #include #include #include #include #ifdef HAVE_UDP_TUNNEL_IPV6 #include_next static inline struct sk_buff * rpl_udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum, int type, bool is_vxlan) { if (skb_is_gso(skb) && skb_is_encapsulated(skb)) { kfree_skb(skb); return ERR_PTR(-ENOSYS); } return udp_tunnel_handle_offloads(skb, udp_csum); } #define udp_tunnel_handle_offloads rpl_udp_tunnel_handle_offloads #else #include #include struct udp_port_cfg { u8 family; /* Used only for kernel-created sockets */ union { struct in_addr local_ip; #if IS_ENABLED(CONFIG_IPV6) struct in6_addr local_ip6; #endif }; union { struct in_addr peer_ip; #if IS_ENABLED(CONFIG_IPV6) struct in6_addr peer_ip6; #endif }; __be16 local_udp_port; __be16 peer_udp_port; unsigned int use_udp_checksums:1, use_udp6_tx_checksums:1, use_udp6_rx_checksums:1, ipv6_v6only:1; }; #define udp_sock_create rpl_udp_sock_create int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg, struct socket **sockp); typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb); typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk); struct udp_tunnel_sock_cfg { void *sk_user_data; /* user data used by encap_rcv call back */ /* Used for setting up udp_sock fields, see udp.h for details */ __u8 encap_type; udp_tunnel_encap_rcv_t encap_rcv; udp_tunnel_encap_destroy_t encap_destroy; }; /* Setup the given (UDP) sock to receive UDP encapsulated packets */ #define setup_udp_tunnel_sock rpl_setup_udp_tunnel_sock void rpl_setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *sock_cfg); /* Transmit the skb using UDP encapsulation. */ #define udp_tunnel_xmit_skb rpl_udp_tunnel_xmit_skb int rpl_udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, bool xnet, bool nocheck); #define udp_tunnel_sock_release rpl_udp_tunnel_sock_release void rpl_udp_tunnel_sock_release(struct socket *sock); void ovs_udp_gso(struct sk_buff *skb); void ovs_udp_csum_gso(struct sk_buff *skb); #define udp_tunnel_encap_enable(sock) udp_encap_enable() static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum, int type, bool is_vxlan) { void (*fix_segment)(struct sk_buff *); if (skb_is_gso(skb) && skb_is_encapsulated(skb)) { kfree_skb(skb); return ERR_PTR(-ENOSYS); } type |= udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; if (!udp_csum) fix_segment = ovs_udp_gso; else fix_segment = ovs_udp_csum_gso; #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) if (!is_vxlan) type = 0; #endif return ovs_iptunnel_handle_offloads(skb, udp_csum, type, fix_segment); } #if IS_ENABLED(CONFIG_IPV6) #define udp_tunnel6_xmit_skb rpl_udp_tunnel6_xmit_skb int rpl_udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port, bool nocheck); #endif static inline void udp_tunnel_gro_complete(struct sk_buff *skb, int nhoff) { struct udphdr *uh; uh = (struct udphdr *)(skb->data + nhoff - sizeof(struct udphdr)); skb_shinfo(skb)->gso_type |= uh->check ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; } #endif static inline void ovs_udp_tun_rx_dst(struct ip_tunnel_info *info, struct sk_buff *skb, unsigned short family, __be16 flags, __be64 tunnel_id, int md_size) { if (family == AF_INET) ovs_ip_tun_rx_dst(info, skb, flags, tunnel_id, md_size); info->key.tp_src = udp_hdr(skb)->source; info->key.tp_dst = udp_hdr(skb)->dest; if (udp_hdr(skb)->check) info->key.tun_flags |= TUNNEL_CSUM; } #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/ip6_route.h0000644000000000000000000000013013534540071024350 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.489858645 openvswitch-2.5.9/datapath/linux/compat/include/net/ip6_route.h0000644000175000017500000000232513534540071026042 0ustar00jpettitjpettit00000000000000#ifndef __NET_IP6_ROUTE_WRAPPER #define __NET_IP6_ROUTE_WRAPPER #include #include /* For OVS_VPORT_OUTPUT_PARAMS */ #include #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) static inline struct dst_entry *rpl_ip6_route_output(struct net *net, const struct sock *sk, struct flowi6 *fl6) { struct flowi fl; memset(&fl, 0, sizeof(fl)); fl.oif = fl6->flowi6_oif; fl.fl6_dst = fl6->daddr; fl.fl6_src = fl6->saddr; fl.mark = fl6->flowi6_mark; fl.proto = fl6->flowi6_proto; return ip6_route_output(net, (struct sock *) sk, &fl); } #define ip6_route_output rpl_ip6_route_output #define ip6_dst_hoplimit(dst) dst_metric(dst, RTAX_HOPLIMIT) #endif /* 2.6.39 */ #ifndef HAVE_NF_IPV6_OPS_FRAGMENT #ifdef OVS_FRAGMENT_BACKPORT int rpl_ip6_fragment(struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)); #else static inline int rpl_ip6_fragment(struct sock *sk, struct sk_buff *skb, int (*output)(struct sk_buff *)) { kfree_skb(skb); return -ENOTSUPP; } #endif /* OVS_FRAGMENT_BACKPORT */ #define ip6_fragment rpl_ip6_fragment #endif /* HAVE_NF_IPV6_OPS_FRAGMENT */ #endif /* _NET_IP6_ROUTE_WRAPPER */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/geneve.h0000644000000000000000000000013213534540071023707 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.481858586 openvswitch-2.5.9/datapath/linux/compat/include/net/geneve.h0000644000175000017500000000433013534540071025375 0ustar00jpettitjpettit00000000000000#ifndef __NET_GENEVE_WRAPPER_H #define __NET_GENEVE_WRAPPER_H 1 #ifdef CONFIG_INET #include #endif #ifdef HAVE_METADATA_DST #include_next static inline int rpl_geneve_init_module(void) { return 0; } static inline void rpl_geneve_cleanup_module(void) {} #define geneve_xmit dev_queue_xmit #else /* Geneve Header: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |Ver| Opt Len |O|C| Rsvd. | Protocol Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Virtual Network Identifier (VNI) | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Variable Length Options | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Option Header: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Option Class | Type |R|R|R| Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Variable Option Data | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct geneve_opt { __be16 opt_class; u8 type; #ifdef __LITTLE_ENDIAN_BITFIELD u8 length:5; u8 r3:1; u8 r2:1; u8 r1:1; #else u8 r1:1; u8 r2:1; u8 r3:1; u8 length:5; #endif u8 opt_data[]; }; #define GENEVE_CRIT_OPT_TYPE (1 << 7) struct genevehdr { #ifdef __LITTLE_ENDIAN_BITFIELD u8 opt_len:6; u8 ver:2; u8 rsvd1:6; u8 critical:1; u8 oam:1; #else u8 ver:2; u8 opt_len:6; u8 oam:1; u8 critical:1; u8 rsvd1:6; #endif __be16 proto_type; u8 vni[3]; u8 rsvd2; struct geneve_opt options[]; }; #ifdef CONFIG_INET #define geneve_dev_create_fb rpl_geneve_dev_create_fb struct net_device *rpl_geneve_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port); #endif /*ifdef CONFIG_INET */ int rpl_geneve_init_module(void); void rpl_geneve_cleanup_module(void); #define geneve_xmit rpl_geneve_xmit netdev_tx_t rpl_geneve_xmit(struct sk_buff *skb); #endif #define geneve_init_module rpl_geneve_init_module #define geneve_cleanup_module rpl_geneve_cleanup_module #endif /*ifdef__NET_GENEVE_H */ openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/route.h0000644000000000000000000000013013534540071023572 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 30 ctime=1567801425.497858704 openvswitch-2.5.9/datapath/linux/compat/include/net/route.h0000644000175000017500000000535413534540071025271 0ustar00jpettitjpettit00000000000000#ifndef __NET_ROUTE_H_WRAPPER #define __NET_ROUTE_H_WRAPPER #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) struct flowi_common { int flowic_oif; __u32 flowic_mark; __u8 flowic_tos; __u8 flowic_proto; }; union flowi_uli { struct { __be16 dport; __be16 sport; } ports; struct { __u8 type; __u8 code; } icmpt; struct { __le16 dport; __le16 sport; } dnports; __be32 spi; __be32 gre_key; struct { __u8 type; } mht; }; struct flowi4 { struct flowi_common __fl_common; #define flowi4_oif __fl_common.flowic_oif #define flowi4_iif __fl_common.flowic_iif #define flowi4_mark __fl_common.flowic_mark #define flowi4_tos __fl_common.flowic_tos #define flowi4_scope __fl_common.flowic_scope #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid #define flowi4_tun_key __fl_common.flowic_tun_key union flowi_uli uli; #define fl4_gre_key uli.gre_key /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; __be32 daddr; } __attribute__((__aligned__(BITS_PER_LONG/8))); struct flowi6 { struct flowi_common __fl_common; #define flowi6_oif __fl_common.flowic_oif #define flowi6_iif __fl_common.flowic_iif #define flowi6_mark __fl_common.flowic_mark #define flowi6_tos __fl_common.flowic_tos #define flowi6_scope __fl_common.flowic_scope #define flowi6_proto __fl_common.flowic_proto #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid #define flowi6_tun_key __fl_common.flowic_tun_key struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; union flowi_uli uli; #define fl6_sport uli.ports.sport #define fl6_dport uli.ports.dport #define fl6_icmp_type uli.icmpt.type #define fl6_icmp_code uli.icmpt.code #define fl6_ipsec_spi uli.spi #define fl6_mh_type uli.mht.type #define fl6_gre_key uli.gre_key } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct rtable *rpl_ip_route_output_key(struct net *net, struct flowi4 *flp) { struct rtable *rt; /* Tunnel configuration keeps DSCP part of TOS bits, But Linux * router expect RT_TOS bits only. */ struct flowi fl = { .nl_u = { .ip4_u = { .daddr = flp->daddr, .saddr = flp->saddr, .tos = RT_TOS(flp->flowi4_tos) } }, .mark = flp->flowi4_mark, .proto = flp->flowi4_proto }; if (unlikely(ip_route_output_key(net, &rt, &fl))) return ERR_PTR(-EADDRNOTAVAIL); flp->saddr = fl.nl_u.ip4_u.saddr; return rt; } #define ip_route_output_key rpl_ip_route_output_key #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) static inline int ip4_dst_hoplimit(const struct dst_entry *dst) { return dst_metric(dst, RTAX_HOPLIMIT); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/net/PaxHeaders.82075/ip.h0000644000000000000000000000013213534540071023046 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.489858645 openvswitch-2.5.9/datapath/linux/compat/include/net/ip.h0000644000175000017500000000741313534540071024541 0ustar00jpettitjpettit00000000000000#ifndef __NET_IP_WRAPPER_H #define __NET_IP_WRAPPER_H 1 #include_next #include #include #ifndef HAVE_IP_IS_FRAGMENT static inline bool ip_is_fragment(const struct iphdr *iph) { return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0; } #endif #ifndef HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET static inline void rpl_inet_get_local_port_range(struct net *net, int *low, int *high) { inet_get_local_port_range(low, high); } #define inet_get_local_port_range rpl_inet_get_local_port_range #endif #ifndef IPSKB_FRAG_PMTU #define IPSKB_FRAG_PMTU BIT(6) #endif /* IPv4 datagram length is stored into 16bit field (tot_len) */ #ifndef IP_MAX_MTU #define IP_MAX_MTU 0xFFFFU #endif #ifndef HAVE_IP_SKB_DST_MTU static inline bool rpl_ip_sk_use_pmtu(const struct sock *sk) { return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; } #define ip_sk_use_pmtu rpl_ip_sk_use_pmtu static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst, bool forwarding) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) struct net *net = dev_net(dst->dev); if (net->ipv4.sysctl_ip_fwd_use_pmtu || dst_metric_locked(dst, RTAX_MTU) || !forwarding) return dst_mtu(dst); #endif return min(dst->dev->mtu, IP_MAX_MTU); } static inline unsigned int rpl_ip_skb_dst_mtu(const struct sk_buff *skb) { if (!skb->sk || ip_sk_use_pmtu(skb->sk)) { bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED; return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding); } else { return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU); } } #define ip_skb_dst_mtu rpl_ip_skb_dst_mtu #endif /* HAVE_IP_SKB_DST_MTU */ #ifdef HAVE_IP_FRAGMENT_TAKES_SOCK #define OVS_VPORT_OUTPUT_PARAMS struct sock *sock, struct sk_buff *skb #else #define OVS_VPORT_OUTPUT_PARAMS struct sk_buff *skb #endif /* Prior to upstream commit d6b915e29f4a ("ip_fragment: don't forward * defragmented DF packet"), IPCB(skb)->frag_max_size was not always populated * correctly, which would lead to reassembled packets not being refragmented. * So, we backport all of ip_defrag() in these cases. */ #if !defined(HAVE_CORRECT_MRU_HANDLING) && defined(OVS_FRAGMENT_BACKPORT) #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) static inline bool ip_defrag_user_in_between(u32 user, enum ip_defrag_users lower_bond, enum ip_defrag_users upper_bond) { return user >= lower_bond && user <= upper_bond; } #endif /* < v4.2 */ int rpl_ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)); #define ip_do_fragment rpl_ip_do_fragment int rpl_ip_defrag(struct sk_buff *skb, u32 user); #define ip_defrag rpl_ip_defrag int __init rpl_ipfrag_init(void); void rpl_ipfrag_fini(void); #else /* HAVE_CORRECT_MRU_HANDLING || !OVS_FRAGMENT_BACKPORT */ #if !defined(HAVE_IP_DO_FRAGMENT_TAKES_NET) && defined(OVS_FRAGMENT_BACKPORT) static inline int rpl_ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)) { return ip_do_fragment(sk, skb, output); } #define ip_do_fragment rpl_ip_do_fragment #endif /* IP_DO_FRAGMENT_TAKES_NET */ /* We have no good way to detect the presence of upstream commit 8282f27449bf * ("inet: frag: Always orphan skbs inside ip_defrag()"), but it should be * always included in kernels 4.5+. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) static inline int rpl_ip_defrag(struct sk_buff *skb, u32 user) { skb_orphan(skb); return ip_defrag(skb, user); } #define ip_defrag rpl_ip_defrag #endif static inline int rpl_ipfrag_init(void) { return 0; } static inline void rpl_ipfrag_fini(void) { } #endif /* HAVE_CORRECT_MRU_HANDLING && OVS_FRAGMENT_BACKPORT */ #define ipfrag_init rpl_ipfrag_init #define ipfrag_fini rpl_ipfrag_fini #endif openvswitch-2.5.9/datapath/linux/compat/include/PaxHeaders.82075/linux0000644000000000000000000000013213534540121022555 xustar0030 mtime=1567801425.477858557 30 atime=1567801425.625859648 30 ctime=1567801425.477858557 openvswitch-2.5.9/datapath/linux/compat/include/linux/0000755000175000017500000000000013534540121024320 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/flex_array.h0000644000000000000000000000013213534540071025143 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.437858262 openvswitch-2.5.9/datapath/linux/compat/include/linux/flex_array.h0000644000175000017500000000620513534540071026634 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_FLEX_ARRAY_WRAPPER_H #define __LINUX_FLEX_ARRAY_WRAPPER_H #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) #include_next #else #include #include #include #define FLEX_ARRAY_PART_SIZE PAGE_SIZE #define FLEX_ARRAY_BASE_SIZE PAGE_SIZE struct flex_array_part; /* * This is meant to replace cases where an array-like * structure has gotten too big to fit into kmalloc() * and the developer is getting tempted to use * vmalloc(). */ struct flex_array { union { struct { int element_size; int total_nr_elements; int elems_per_part; struct reciprocal_value reciprocal_elems; struct flex_array_part *parts[]; }; /* * This little trick makes sure that * sizeof(flex_array) == PAGE_SIZE */ char padding[FLEX_ARRAY_BASE_SIZE]; }; }; /* Number of bytes left in base struct flex_array, excluding metadata */ #define FLEX_ARRAY_BASE_BYTES_LEFT \ (FLEX_ARRAY_BASE_SIZE - offsetof(struct flex_array, parts)) /* Number of pointers in base to struct flex_array_part pages */ #define FLEX_ARRAY_NR_BASE_PTRS \ (FLEX_ARRAY_BASE_BYTES_LEFT / sizeof(struct flex_array_part *)) /* Number of elements of size that fit in struct flex_array_part */ #define FLEX_ARRAY_ELEMENTS_PER_PART(size) \ (FLEX_ARRAY_PART_SIZE / size) /* * Defines a statically allocated flex array and ensures its parameters are * valid. */ #define DEFINE_FLEX_ARRAY(__arrayname, __element_size, __total) \ struct flex_array __arrayname = { { { \ .element_size = (__element_size), \ .total_nr_elements = (__total), \ } } }; \ static inline void __arrayname##_invalid_parameter(void) \ { \ BUILD_BUG_ON((__total) > FLEX_ARRAY_NR_BASE_PTRS * \ FLEX_ARRAY_ELEMENTS_PER_PART(__element_size)); \ } #define flex_array_alloc rpl_flex_array_alloc struct flex_array *rpl_flex_array_alloc(int element_size, unsigned int total, gfp_t flags); #define flex_array_prealloc rpl_flex_array_prealloc int rpl_flex_array_prealloc(struct flex_array *fa, unsigned int start, unsigned int nr_elements, gfp_t flags); #define flex_array_free rpl_flex_array_free void rpl_flex_array_free(struct flex_array *fa); #define flex_array_free_parts rpl_flex_array_free_parts void rpl_flex_array_free_parts(struct flex_array *fa); #define flex_array_put rpl_flex_array_put int rpl_flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src, gfp_t flags); #define flex_array_clear rpl_flex_array_clear int rpl_flex_array_clear(struct flex_array *fa, unsigned int element_nr); #define flex_array_get rpl_flex_array_get void *rpl_flex_array_get(struct flex_array *fa, unsigned int element_nr); #define flex_array_shrink rpl_flex_array_shrink int rpl_flex_array_shrink(struct flex_array *fa); #define flex_array_put_ptr rpl_flex_array_put_ptr #define rpl_flex_array_put_ptr(fa, nr, src, gfp) \ flex_array_put(fa, nr, (void *)&(src), gfp) #define flex_array_get_ptr rpl_flex_array_get_ptr void *rpl_flex_array_get_ptr(struct flex_array *fa, unsigned int element_nr); #endif /* Linux version < 3.0.0 */ #endif /* __LINUX_FLEX_ARRAY_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/workqueue.h0000644000000000000000000000013213534540071025036 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.477858557 openvswitch-2.5.9/datapath/linux/compat/include/linux/workqueue.h0000644000175000017500000000033313534540071026523 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_WORKQUEUE_WRAPPER_H #define __LINUX_WORKQUEUE_WRAPPER_H 1 #include_next #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) #define queue_work(wq, dw) schedule_work(dw); #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/in.h0000644000000000000000000000013213534540071023415 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.445858321 openvswitch-2.5.9/datapath/linux/compat/include/linux/in.h0000644000175000017500000000207413534540071025106 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IN_WRAPPER_H #define __LINUX_IN_WRAPPER_H 1 #include_next #include #ifndef HAVE_PROTO_PORTS_OFFSET static inline int proto_ports_offset(int proto) { switch (proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_DCCP: case IPPROTO_ESP: /* SPI */ case IPPROTO_SCTP: case IPPROTO_UDPLITE: return 0; case IPPROTO_AH: /* SPI */ return 4; default: return -EINVAL; } } #endif #ifndef HAVE_IPV4_IS_MULTICAST static inline bool ipv4_is_loopback(__be32 addr) { return (addr & htonl(0xff000000)) == htonl(0x7f000000); } static inline bool ipv4_is_multicast(__be32 addr) { return (addr & htonl(0xf0000000)) == htonl(0xe0000000); } static inline bool ipv4_is_local_multicast(__be32 addr) { return (addr & htonl(0xffffff00)) == htonl(0xe0000000); } static inline bool ipv4_is_lbcast(__be32 addr) { /* limited broadcast */ return addr == htonl(INADDR_BROADCAST); } static inline bool ipv4_is_zeronet(__be32 addr) { return (addr & htonl(0xff000000)) == htonl(0x00000000); } #endif /* !HAVE_IPV4_IS_MULTICAST */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/rculist.h0000644000000000000000000000013213534540071024474 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.465858468 openvswitch-2.5.9/datapath/linux/compat/include/linux/rculist.h0000644000175000017500000000130013534540071026154 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_RCULIST_WRAPPER_H #define __LINUX_RCULIST_WRAPPER_H #include_next #ifndef hlist_first_rcu #define hlist_first_rcu(head) (*((struct hlist_node __rcu **)(&(head)->first))) #define hlist_next_rcu(node) (*((struct hlist_node __rcu **)(&(node)->next))) #define hlist_pprev_rcu(node) (*((struct hlist_node __rcu **)((node)->pprev))) #endif #undef hlist_for_each_entry_rcu #define hlist_for_each_entry_rcu(pos, head, member) \ for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\ typeof(*(pos)), member); \ pos; \ pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(\ &(pos)->member)), typeof(*(pos)), member)) #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/reciprocal_div.h0000644000000000000000000000013213534540071025774 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.465858468 openvswitch-2.5.9/datapath/linux/compat/include/linux/reciprocal_div.h0000644000175000017500000000214113534540071027460 0ustar00jpettitjpettit00000000000000#ifndef _LINUX_RECIPROCAL_DIV_WRAPPER_H #define _LINUX_RECIPROCAL_DIV_WRAPPER_H 1 #include /* * This algorithm is based on the paper "Division by Invariant * Integers Using Multiplication" by Torbjörn Granlund and Peter * L. Montgomery. * * The assembler implementation from Agner Fog, which this code is * based on, can be found here: * http://www.agner.org/optimize/asmlib.zip * * This optimization for A/B is helpful if the divisor B is mostly * runtime invariant. The reciprocal of B is calculated in the * slow-path with reciprocal_value(). The fast-path can then just use * a much faster multiplication operation with a variable dividend A * to calculate the division A/B. */ #define reciprocal_value rpl_reciprocal_value struct reciprocal_value { u32 m; u8 sh1, sh2; }; struct reciprocal_value rpl_reciprocal_value(u32 d); #define reciprocal_divide rpl_reciprocal_divide static inline u32 rpl_reciprocal_divide(u32 a, struct reciprocal_value R) { u32 t = (u32)(((u64)a * R.m) >> 32); return (t + ((a - t) >> R.sh1)) >> R.sh2; } #endif /* _LINUX_RECIPROCAL_DIV_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/kernel.h0000644000000000000000000000013213534540071024267 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.453858381 openvswitch-2.5.9/datapath/linux/compat/include/linux/kernel.h0000644000175000017500000000320013534540071025750 0ustar00jpettitjpettit00000000000000#ifndef __KERNEL_H_WRAPPER #define __KERNEL_H_WRAPPER 1 #include_next #ifndef HAVE_LOG2_H #include #endif #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) #define pr_warn pr_warning #endif /* * Print a one-time message (analogous to WARN_ONCE() et al): */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) #undef printk_once #define printk_once(fmt, ...) \ ({ \ static bool __print_once; \ \ if (!__print_once) { \ __print_once = true; \ printk(fmt, ##__VA_ARGS__); \ } \ }) #define pr_emerg_once(fmt, ...) \ printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert_once(fmt, ...) \ printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit_once(fmt, ...) \ printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err_once(fmt, ...) \ printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn_once(fmt, ...) \ printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define pr_notice_once(fmt, ...) \ printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info_once(fmt, ...) \ printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) #define pr_cont_once(fmt, ...) \ printk_once(KERN_CONT pr_fmt(fmt), ##__VA_ARGS__) #endif #ifndef USHRT_MAX #define USHRT_MAX ((u16)(~0U)) #define SHRT_MAX ((s16)(USHRT_MAX>>1)) #define SHRT_MIN ((s16)(-SHRT_MAX - 1)) #endif #ifndef DIV_ROUND_UP #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #endif #ifndef rounddown #define rounddown(x, y) ( \ { \ typeof(x) __x = (x); \ __x - (__x % (y)); \ } \ ) #endif #endif /* linux/kernel.h */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/cpumask.h0000644000000000000000000000013213534540071024452 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.433858233 openvswitch-2.5.9/datapath/linux/compat/include/linux/cpumask.h0000644000175000017500000000043413534540071026141 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_CPUMASK_WRAPPER_H #define __LINUX_CPUMASK_WRAPPER_H #include_next /* for_each_cpu was renamed for_each_possible_cpu in 2.6.18. */ #ifndef for_each_possible_cpu #define for_each_possible_cpu for_each_cpu #endif #endif /* linux/cpumask.h wrapper */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/udp.h0000644000000000000000000000013213534540071023577 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.477858557 openvswitch-2.5.9/datapath/linux/compat/include/linux/udp.h0000644000175000017500000000061313534540071025265 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_UDP_WRAPPER_H #define __LINUX_UDP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct udphdr *udp_hdr(const struct sk_buff *skb) { return (struct udphdr *)skb_transport_header(skb); } #endif /* HAVE_SKBUFF_HEADER_HELPERS */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) static inline void udp_encap_enable(void) { } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/net.h0000644000000000000000000000013213534540071023575 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.457858409 openvswitch-2.5.9/datapath/linux/compat/include/linux/net.h0000644000175000017500000000370113534540071025264 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_NET_WRAPPER_H #define __LINUX_NET_WRAPPER_H 1 #include_next #include #ifndef net_ratelimited_function #define net_ratelimited_function(function, ...) \ do { \ if (net_ratelimit()) \ function(__VA_ARGS__); \ } while (0) #define net_emerg_ratelimited(fmt, ...) \ net_ratelimited_function(pr_emerg, fmt, ##__VA_ARGS__) #define net_alert_ratelimited(fmt, ...) \ net_ratelimited_function(pr_alert, fmt, ##__VA_ARGS__) #define net_crit_ratelimited(fmt, ...) \ net_ratelimited_function(pr_crit, fmt, ##__VA_ARGS__) #define net_err_ratelimited(fmt, ...) \ net_ratelimited_function(pr_err, fmt, ##__VA_ARGS__) #define net_notice_ratelimited(fmt, ...) \ net_ratelimited_function(pr_notice, fmt, ##__VA_ARGS__) #define net_warn_ratelimited(fmt, ...) \ net_ratelimited_function(pr_warn, fmt, ##__VA_ARGS__) #define net_info_ratelimited(fmt, ...) \ net_ratelimited_function(pr_info, fmt, ##__VA_ARGS__) #define net_dbg_ratelimited(fmt, ...) \ net_ratelimited_function(pr_debug, fmt, ##__VA_ARGS__) #endif #ifndef net_get_random_once #define __net_get_random_once rpl___net_get_random_once bool rpl___net_get_random_once(void *buf, int nbytes, bool *done, atomic_t *done_key); #define ___NET_RANDOM_STATIC_KEY_INIT ATOMIC_INIT(0) #define net_get_random_once(buf, nbytes) \ ({ \ bool ___ret = false; \ static bool ___done = false; \ static atomic_t ___done_key = \ ___NET_RANDOM_STATIC_KEY_INIT; \ if (!atomic_read(&___done_key)) \ ___ret = __net_get_random_once(buf, \ nbytes, \ &___done, \ &___done_key); \ ___ret; \ }) #endif #ifndef HAVE_SOCK_CREATE_KERN_NET int ovs_sock_create_kern(struct net *net, int family, int type, int protocol, struct socket **res); void ovs_sock_release(struct socket *sock); #define sock_create_kern ovs_sock_create_kern #define sock_release ovs_sock_release #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/random.h0000644000000000000000000000013213534540071024267 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.457858409 openvswitch-2.5.9/datapath/linux/compat/include/linux/random.h0000644000175000017500000000047113534540071025757 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_RANDOM_WRAPPER_H #define __LINUX_RANDOM_WRAPPER_H 1 #include_next #ifndef HAVE_PRANDOM_U32 #define prandom_u32() random32() #endif #ifndef HAVE_PRANDOM_U32_MAX static inline u32 prandom_u32_max(u32 ep_ro) { return (u32)(((u64) prandom_u32() * ep_ro) >> 32); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/icmpv6.h0000644000000000000000000000013213534540071024213 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.437858262 openvswitch-2.5.9/datapath/linux/compat/include/linux/icmpv6.h0000644000175000017500000000041113534540071025675 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_ICMPV6_WRAPPER_H #define __LINUX_ICMPV6_WRAPPER_H 1 #include_next #ifndef HAVE_ICMP6_HDR static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb) { return (struct icmp6hdr *)skb_transport_header(skb); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/netfilter_ipv6.h0000644000000000000000000000013213534540071025747 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.461858439 openvswitch-2.5.9/datapath/linux/compat/include/linux/netfilter_ipv6.h0000644000175000017500000000224413534540071027437 0ustar00jpettitjpettit00000000000000#ifndef __NETFILTER_IPV6_WRAPPER_H #define __NETFILTER_IPV6_WRAPPER_H 1 #include_next #include #include /* For OVS_VPORT_OUTPUT_PARAMS */ #include #ifndef HAVE_NF_IPV6_OPS_FRAGMENT /* Try to minimise changes required to the actions.c code for calling IPv6 * fragmentation. We can keep the fragment() API mostly the same, except that * the callback parameter needs to be in the form that older kernels accept. * We don't backport the other ipv6_ops as they're currently unused by OVS. */ struct ovs_nf_ipv6_ops { int (*fragment)(struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)); }; #define nf_ipv6_ops ovs_nf_ipv6_ops #if defined(OVS_FRAGMENT_BACKPORT) static struct ovs_nf_ipv6_ops ovs_ipv6_ops = { .fragment = ip6_fragment, }; static inline struct ovs_nf_ipv6_ops *ovs_nf_get_ipv6_ops(void) { return &ovs_ipv6_ops; } #else /* !OVS_FRAGMENT_BACKPORT */ static inline const struct ovs_nf_ipv6_ops *ovs_nf_get_ipv6_ops(void) { return NULL; } #endif #define nf_get_ipv6_ops ovs_nf_get_ipv6_ops #endif /* HAVE_NF_IPV6_OPS_FRAGMENT */ #endif /* __NETFILTER_IPV6_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/icmp.h0000644000000000000000000000013213534540071023737 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.437858262 openvswitch-2.5.9/datapath/linux/compat/include/linux/icmp.h0000644000175000017500000000041413534540071025424 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_ICMP_WRAPPER_H #define __LINUX_ICMP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct icmphdr *icmp_hdr(const struct sk_buff *skb) { return (struct icmphdr *)skb_transport_header(skb); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/u64_stats_sync.h0000644000000000000000000000013213534540071025677 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.473858528 openvswitch-2.5.9/datapath/linux/compat/include/linux/u64_stats_sync.h0000644000175000017500000001114113534540071027363 0ustar00jpettitjpettit00000000000000#ifndef _LINUX_U64_STATS_SYNC_WRAPPER_H #define _LINUX_U64_STATS_SYNC_WRAPPER_H #include #if defined(HAVE_U64_STATS_FETCH_BEGIN_IRQ) && \ LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) #include_next #else /* * To properly implement 64bits network statistics on 32bit and 64bit hosts, * we provide a synchronization point, that is a noop on 64bit or UP kernels. * * Key points : * 1) Use a seqcount on SMP 32bits, with low overhead. * 2) Whole thing is a noop on 64bit arches or UP kernels. * 3) Write side must ensure mutual exclusion or one seqcount update could * be lost, thus blocking readers forever. * If this synchronization point is not a mutex, but a spinlock or * spinlock_bh() or disable_bh() : * 3.1) Write side should not sleep. * 3.2) Write side should not allow preemption. * 3.3) If applicable, interrupts should be disabled. * * 4) If reader fetches several counters, there is no guarantee the whole values * are consistent (remember point 1) : this is a noop on 64bit arches anyway) * * 5) readers are allowed to sleep or be preempted/interrupted : They perform * pure reads. But if they have to fetch many values, it's better to not allow * preemptions/interruptions to avoid many retries. * * 6) If counter might be written by an interrupt, readers should block interrupts. * (On UP, there is no seqcount_t protection, a reader allowing interrupts could * read partial values) * * 7) For irq or softirq uses, readers can use u64_stats_fetch_begin_irq() and * u64_stats_fetch_retry_irq() helpers * * Usage : * * Stats producer (writer) should use following template granted it already got * an exclusive access to counters (a lock is already taken, or per cpu * data is used [in a non preemptable context]) * * spin_lock_bh(...) or other synchronization to get exclusive access * ... * u64_stats_update_begin(&stats->syncp); * stats->bytes64 += len; // non atomic operation * stats->packets64++; // non atomic operation * u64_stats_update_end(&stats->syncp); * * While a consumer (reader) should use following template to get consistent * snapshot for each variable (but no guarantee on several ones) * * u64 tbytes, tpackets; * unsigned int start; * * do { * start = u64_stats_fetch_begin(&stats->syncp); * tbytes = stats->bytes64; // non atomic operation * tpackets = stats->packets64; // non atomic operation * } while (u64_stats_fetch_retry(&stats->syncp, start)); * * * Example of use in drivers/net/loopback.c, using per_cpu containers, * in BH disabled context. */ #include struct u64_stats_sync { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) seqcount_t seq; #endif }; #if BITS_PER_LONG == 32 && defined(CONFIG_SMP) # define u64_stats_init(syncp) seqcount_init(syncp.seq) #else # define u64_stats_init(syncp) do { } while (0) #endif static inline void u64_stats_update_begin(struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) write_seqcount_begin(&syncp->seq); #endif } static inline void u64_stats_update_end(struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) write_seqcount_end(&syncp->seq); #endif } static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_begin(&syncp->seq); #else #if BITS_PER_LONG==32 preempt_disable(); #endif return 0; #endif } static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp, unsigned int start) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_retry(&syncp->seq, start); #else #if BITS_PER_LONG==32 preempt_enable(); #endif return false; #endif } /* * In case irq handlers can update u64 counters, readers can use following helpers * - SMP 32bit arches use seqcount protection, irq safe. * - UP 32bit must disable irqs. * - 64bit have no problem atomically reading u64 values, irq safe. */ static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_begin(&syncp->seq); #else #if BITS_PER_LONG==32 local_irq_disable(); #endif return 0; #endif } static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp, unsigned int start) { #if BITS_PER_LONG==32 && defined(CONFIG_SMP) return read_seqcount_retry(&syncp->seq, start); #else #if BITS_PER_LONG==32 local_irq_enable(); #endif return false; #endif } #endif /* !HAVE_U64_STATS_FETCH_BEGIN_IRQ || kernel < 3.13 */ #endif /* _LINUX_U64_STATS_SYNC_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/if_ether.h0000644000000000000000000000013213534540071024574 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.441858291 openvswitch-2.5.9/datapath/linux/compat/include/linux/if_ether.h0000644000175000017500000000044013534540071026260 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IF_ETHER_WRAPPER_H #define __LINUX_IF_ETHER_WRAPPER_H 1 #include_next #ifndef ETH_P_802_3_MIN #define ETH_P_802_3_MIN 0x0600 #endif #ifndef ETH_P_8021AD #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/if.h0000644000000000000000000000013213534540071023405 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.441858291 openvswitch-2.5.9/datapath/linux/compat/include/linux/if.h0000644000175000017500000000014013534540071025066 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IF_WRAPPER_H #define __LINUX_IF_WRAPPER_H 1 #include_next #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/netlink.h0000644000000000000000000000013213534540071024453 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.461858439 openvswitch-2.5.9/datapath/linux/compat/include/linux/netlink.h0000644000175000017500000000064413534540071026145 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_NETLINK_WRAPPER_H #define __LINUX_NETLINK_WRAPPER_H 1 #include #include_next #ifndef NLA_TYPE_MASK #define NLA_F_NESTED (1 << 15) #define NLA_F_NET_BYTEORDER (1 << 14) #define NLA_TYPE_MASK (~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)) #endif #include #ifndef NLMSG_DEFAULT_SIZE #define NLMSG_DEFAULT_SIZE (NLMSG_GOODSIZE - NLMSG_HDRLEN) #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/netdev_features.h0000644000000000000000000000013213534540071026172 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.461858439 openvswitch-2.5.9/datapath/linux/compat/include/linux/netdev_features.h0000644000175000017500000000372413534540071027666 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_NETDEV_FEATURES_WRAPPER_H #define __LINUX_NETDEV_FEATURES_WRAPPER_H #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) #include_next #endif #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) /* On RHEL 6, netdev features are defined in netdevice.h header. */ #include #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) #define NETIF_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_TX #endif #ifndef NETIF_F_GSO_GRE #define NETIF_F_GSO_GRE 0 #endif #ifndef NETIF_F_GSO_GRE_CSUM #define NETIF_F_GSO_GRE_CSUM 0 #else #define HAVE_NETIF_F_GSO_GRE_CSUM #endif #ifndef NETIF_F_GSO_IPIP #define NETIF_F_GSO_IPIP 0 #endif #ifndef NETIF_F_GSO_SIT #define NETIF_F_GSO_SIT 0 #endif #ifndef NETIF_F_GSO_UDP_TUNNEL #define NETIF_F_GSO_UDP_TUNNEL 0 #else #define HAVE_NETIF_F_GSO_UDP_TUNNEL 0 #endif #ifndef NETIF_F_GSO_UDP_TUNNEL_CSUM #define NETIF_F_GSO_UDP_TUNNEL_CSUM 0 #define SKB_GSO_UDP_TUNNEL_CSUM 0 #endif #ifndef NETIF_F_GSO_MPLS #define NETIF_F_GSO_MPLS 0 #endif #ifndef NETIF_F_HW_VLAN_STAG_TX #define NETIF_F_HW_VLAN_STAG_TX 0 #endif #ifndef NETIF_F_GSO_TUNNEL_REMCSUM #define NETIF_F_GSO_TUNNEL_REMCSUM 0 #define SKB_GSO_TUNNEL_REMCSUM 0 #else /* support for REM_CSUM is added in 3.19 but API are not defined * till 4.0, so turn on REMSUM support on kernel 4.0 onwards. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) #define HAVE_NETIF_F_GSO_TUNNEL_REMCSUM #endif #endif #ifndef NETIF_F_RXCSUM #define NETIF_F_RXCSUM 0 #endif #ifndef NETIF_F_GSO_ENCAP_ALL #define NETIF_F_GSO_ENCAP_ALL (NETIF_F_GSO_GRE | \ NETIF_F_GSO_GRE_CSUM | \ NETIF_F_GSO_IPIP | \ NETIF_F_GSO_SIT | \ NETIF_F_GSO_UDP_TUNNEL | \ NETIF_F_GSO_UDP_TUNNEL_CSUM | \ NETIF_F_GSO_MPLS) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) #define SKB_GSO_GRE 0 #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) #define SKB_GSO_UDP_TUNNEL 0 #endif #ifndef HAVE_NETIF_F_GSO_GRE_CSUM #define SKB_GSO_GRE_CSUM 0 #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/poison.h0000644000000000000000000000013213534540071024316 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.465858468 openvswitch-2.5.9/datapath/linux/compat/include/linux/poison.h0000644000175000017500000000037013534540071026004 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_POISON_WRAPPER_H #define __LINUX_POISON_WRAPPER_H 1 #include_next #ifndef FLEX_ARRAY_FREE /********** lib/flex_array.c **********/ #define FLEX_ARRAY_FREE 0x6c /* for use-after-free poisoning */ #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/openvswitch.h0000644000000000000000000000013213534540071025360 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.461858439 openvswitch-2.5.9/datapath/linux/compat/include/linux/openvswitch.h0000644000175000017500000007341413534540071027057 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2014 Nicira, Inc. * * This file is offered under your choice of two licenses: Apache 2.0 or GNU * GPL 2.0 or later. The permission statements for each of these licenses is * given below. You may license your modifications to this file under either * of these licenses or both. If you wish to license your modifications under * only one of these licenses, delete the permission text for the other * license. * * ---------------------------------------------------------------------- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ---------------------------------------------------------------------- * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * ---------------------------------------------------------------------- */ #ifndef _LINUX_OPENVSWITCH_H #define _LINUX_OPENVSWITCH_H 1 #include #include /** * struct ovs_header - header for OVS Generic Netlink messages. * @dp_ifindex: ifindex of local port for datapath (0 to make a request not * specific to a datapath). * * Attributes following the header are specific to a particular OVS Generic * Netlink family, but all of the OVS families use this header. */ struct ovs_header { int dp_ifindex; }; /* Datapaths. */ #define OVS_DATAPATH_FAMILY "ovs_datapath" #define OVS_DATAPATH_MCGROUP "ovs_datapath" /* V2: * - API users are expected to provide OVS_DP_ATTR_USER_FEATURES * when creating the datapath. */ #define OVS_DATAPATH_VERSION 2 /* First OVS datapath version to support features */ #define OVS_DP_VER_FEATURES 2 enum ovs_datapath_cmd { OVS_DP_CMD_UNSPEC, OVS_DP_CMD_NEW, OVS_DP_CMD_DEL, OVS_DP_CMD_GET, OVS_DP_CMD_SET }; /** * enum ovs_datapath_attr - attributes for %OVS_DP_* commands. * @OVS_DP_ATTR_NAME: Name of the network device that serves as the "local * port". This is the name of the network device whose dp_ifindex is given in * the &struct ovs_header. Always present in notifications. Required in * %OVS_DP_NEW requests. May be used as an alternative to specifying * dp_ifindex in other requests (with a dp_ifindex of 0). * @OVS_DP_ATTR_UPCALL_PID: The Netlink socket in userspace that is initially * set on the datapath port (for OVS_ACTION_ATTR_MISS). Only valid on * %OVS_DP_CMD_NEW requests. A value of zero indicates that upcalls should * not be sent. * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the * datapath. Always present in notifications. * @OVS_DP_ATTR_MEGAFLOW_STATS: Statistics about mega flow masks usage for the * datapath. Always present in notifications. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_DP_* commands. */ enum ovs_datapath_attr { OVS_DP_ATTR_UNSPEC, OVS_DP_ATTR_NAME, /* name of dp_ifindex netdev */ OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */ OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */ OVS_DP_ATTR_USER_FEATURES, /* OVS_DP_F_* */ __OVS_DP_ATTR_MAX }; #define OVS_DP_ATTR_MAX (__OVS_DP_ATTR_MAX - 1) /* All 64-bit integers within Netlink messages are 4-byte aligned only. */ struct ovs_dp_stats { __u64 n_hit; /* Number of flow table matches. */ __u64 n_missed; /* Number of flow table misses. */ __u64 n_lost; /* Number of misses not sent to userspace. */ __u64 n_flows; /* Number of flows present */ }; struct ovs_dp_megaflow_stats { __u64 n_mask_hit; /* Number of masks used for flow lookups. */ __u32 n_masks; /* Number of masks for the datapath. */ __u32 pad0; /* Pad for future expension. */ __u64 pad1; /* Pad for future expension. */ __u64 pad2; /* Pad for future expension. */ }; struct ovs_vport_stats { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ __u64 rx_bytes; /* total bytes received */ __u64 tx_bytes; /* total bytes transmitted */ __u64 rx_errors; /* bad packets received */ __u64 tx_errors; /* packet transmit problems */ __u64 rx_dropped; /* no space in linux buffers */ __u64 tx_dropped; /* no space available in linux */ }; /* Allow last Netlink attribute to be unaligned */ #define OVS_DP_F_UNALIGNED (1 << 0) /* Allow datapath to associate multiple Netlink PIDs to each vport */ #define OVS_DP_F_VPORT_PIDS (1 << 1) /* Fixed logical ports. */ #define OVSP_LOCAL ((__u32)0) /* Packet transfer. */ #define OVS_PACKET_FAMILY "ovs_packet" #define OVS_PACKET_VERSION 0x1 enum ovs_packet_cmd { OVS_PACKET_CMD_UNSPEC, /* Kernel-to-user notifications. */ OVS_PACKET_CMD_MISS, /* Flow table miss. */ OVS_PACKET_CMD_ACTION, /* OVS_ACTION_ATTR_USERSPACE action. */ /* Userspace commands. */ OVS_PACKET_CMD_EXECUTE /* Apply actions to a packet. */ }; /** * enum ovs_packet_attr - attributes for %OVS_PACKET_* commands. * @OVS_PACKET_ATTR_PACKET: Present for all notifications. Contains the entire * packet as received, from the start of the Ethernet header onward. For * %OVS_PACKET_CMD_ACTION, %OVS_PACKET_ATTR_PACKET reflects changes made by * actions preceding %OVS_ACTION_ATTR_USERSPACE, but %OVS_PACKET_ATTR_KEY is * the flow key extracted from the packet as originally received. * @OVS_PACKET_ATTR_KEY: Present for all notifications. Contains the flow key * extracted from the packet as nested %OVS_KEY_ATTR_* attributes. This allows * userspace to adapt its flow setup strategy by comparing its notion of the * flow key against the kernel's. When used with %OVS_PACKET_CMD_EXECUTE, only * metadata key fields (e.g. priority, skb mark) are honored. All the packet * header fields are parsed from the packet instead. * @OVS_PACKET_ATTR_ACTIONS: Contains actions for the packet. Used * for %OVS_PACKET_CMD_EXECUTE. It has nested %OVS_ACTION_ATTR_* attributes. * Also used in upcall when %OVS_ACTION_ATTR_USERSPACE has optional * %OVS_USERSPACE_ATTR_ACTIONS attribute. * @OVS_PACKET_ATTR_USERDATA: Present for an %OVS_PACKET_CMD_ACTION * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content * specified there. * @OVS_PACKET_ATTR_EGRESS_TUN_KEY: Present for an %OVS_PACKET_CMD_ACTION * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an * %OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute, which is sent only if the * output port is actually a tunnel port. Contains the output tunnel key * extracted from the packet as nested %OVS_TUNNEL_KEY_ATTR_* attributes. * @OVS_PACKET_ATTR_MRU: Present for an %OVS_PACKET_CMD_ACTION and * %OVS_PACKET_ATTR_USERSPACE action specify the Maximum received fragment * size. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_PACKET_* commands. */ enum ovs_packet_attr { OVS_PACKET_ATTR_UNSPEC, OVS_PACKET_ATTR_PACKET, /* Packet data. */ OVS_PACKET_ATTR_KEY, /* Nested OVS_KEY_ATTR_* attributes. */ OVS_PACKET_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ OVS_PACKET_ATTR_USERDATA, /* OVS_ACTION_ATTR_USERSPACE arg. */ OVS_PACKET_ATTR_EGRESS_TUN_KEY, /* Nested OVS_TUNNEL_KEY_ATTR_* attributes. */ OVS_PACKET_ATTR_UNUSED1, OVS_PACKET_ATTR_UNUSED2, OVS_PACKET_ATTR_PROBE, /* Packet operation is a feature probe, error logging should be suppressed. */ OVS_PACKET_ATTR_MRU, /* Maximum received IP fragment size. */ __OVS_PACKET_ATTR_MAX }; #define OVS_PACKET_ATTR_MAX (__OVS_PACKET_ATTR_MAX - 1) /* Virtual ports. */ #define OVS_VPORT_FAMILY "ovs_vport" #define OVS_VPORT_MCGROUP "ovs_vport" #define OVS_VPORT_VERSION 0x1 enum ovs_vport_cmd { OVS_VPORT_CMD_UNSPEC, OVS_VPORT_CMD_NEW, OVS_VPORT_CMD_DEL, OVS_VPORT_CMD_GET, OVS_VPORT_CMD_SET }; enum ovs_vport_type { OVS_VPORT_TYPE_UNSPEC, OVS_VPORT_TYPE_NETDEV, /* network device */ OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */ OVS_VPORT_TYPE_GRE, /* GRE tunnel. */ OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */ OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ __OVS_VPORT_TYPE_MAX }; #define OVS_VPORT_TYPE_MAX (__OVS_VPORT_TYPE_MAX - 1) /** * enum ovs_vport_attr - attributes for %OVS_VPORT_* commands. * @OVS_VPORT_ATTR_PORT_NO: 32-bit port number within datapath. * @OVS_VPORT_ATTR_TYPE: 32-bit %OVS_VPORT_TYPE_* constant describing the type * of vport. * @OVS_VPORT_ATTR_NAME: Name of vport. For a vport based on a network device * this is the name of the network device. Maximum length %IFNAMSIZ-1 bytes * plus a null terminator. * @OVS_VPORT_ATTR_OPTIONS: Vport-specific configuration information. * @OVS_VPORT_ATTR_UPCALL_PID: The array of Netlink socket pids in userspace * among which OVS_PACKET_CMD_MISS upcalls will be distributed for packets * received on this port. If this is a single-element array of value 0, * upcalls should not be sent. * @OVS_VPORT_ATTR_STATS: A &struct ovs_vport_stats giving statistics for * packets sent or received through the vport. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_VPORT_* commands. * * For %OVS_VPORT_CMD_NEW requests, the %OVS_VPORT_ATTR_TYPE and * %OVS_VPORT_ATTR_NAME attributes are required. %OVS_VPORT_ATTR_PORT_NO is * optional; if not specified a free port number is automatically selected. * Whether %OVS_VPORT_ATTR_OPTIONS is required or optional depends on the type * of vport. %OVS_VPORT_ATTR_STATS is optional and other attributes are * ignored. * * For other requests, if %OVS_VPORT_ATTR_NAME is specified then it is used to * look up the vport to operate on; otherwise dp_idx from the &struct * ovs_header plus %OVS_VPORT_ATTR_PORT_NO determine the vport. */ enum ovs_vport_attr { OVS_VPORT_ATTR_UNSPEC, OVS_VPORT_ATTR_PORT_NO, /* u32 port number within datapath */ OVS_VPORT_ATTR_TYPE, /* u32 OVS_VPORT_TYPE_* constant. */ OVS_VPORT_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long */ OVS_VPORT_ATTR_OPTIONS, /* nested attributes, varies by vport type */ OVS_VPORT_ATTR_UPCALL_PID, /* array of u32 Netlink socket PIDs for */ /* receiving upcalls */ OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ __OVS_VPORT_ATTR_MAX }; #define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1) enum { OVS_VXLAN_EXT_UNSPEC, OVS_VXLAN_EXT_GBP, /* Flag or __u32 */ __OVS_VXLAN_EXT_MAX, }; #define OVS_VXLAN_EXT_MAX (__OVS_VXLAN_EXT_MAX - 1) /* OVS_VPORT_ATTR_OPTIONS attributes for tunnels. */ enum { OVS_TUNNEL_ATTR_UNSPEC, OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by L4 tunnels. */ OVS_TUNNEL_ATTR_EXTENSION, __OVS_TUNNEL_ATTR_MAX }; #define OVS_TUNNEL_ATTR_MAX (__OVS_TUNNEL_ATTR_MAX - 1) /* Flows. */ #define OVS_FLOW_FAMILY "ovs_flow" #define OVS_FLOW_MCGROUP "ovs_flow" #define OVS_FLOW_VERSION 0x1 enum ovs_flow_cmd { OVS_FLOW_CMD_UNSPEC, OVS_FLOW_CMD_NEW, OVS_FLOW_CMD_DEL, OVS_FLOW_CMD_GET, OVS_FLOW_CMD_SET }; struct ovs_flow_stats { __u64 n_packets; /* Number of matched packets. */ __u64 n_bytes; /* Number of matched bytes. */ }; enum ovs_key_attr { OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_ENCAP, /* Nested set of encapsulated attributes. */ OVS_KEY_ATTR_PRIORITY, /* u32 skb->priority */ OVS_KEY_ATTR_IN_PORT, /* u32 OVS dp port number */ OVS_KEY_ATTR_ETHERNET, /* struct ovs_key_ethernet */ OVS_KEY_ATTR_VLAN, /* be16 VLAN TCI */ OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */ OVS_KEY_ATTR_IPV4, /* struct ovs_key_ipv4 */ OVS_KEY_ATTR_IPV6, /* struct ovs_key_ipv6 */ OVS_KEY_ATTR_TCP, /* struct ovs_key_tcp */ OVS_KEY_ATTR_UDP, /* struct ovs_key_udp */ OVS_KEY_ATTR_ICMP, /* struct ovs_key_icmp */ OVS_KEY_ATTR_ICMPV6, /* struct ovs_key_icmpv6 */ OVS_KEY_ATTR_ARP, /* struct ovs_key_arp */ OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */ OVS_KEY_ATTR_DP_HASH, /* u32 hash value. Value 0 indicates the hash is not computed by the datapath. */ OVS_KEY_ATTR_RECIRC_ID, /* u32 recirc id */ OVS_KEY_ATTR_MPLS, /* array of struct ovs_key_mpls. * The implementation may restrict * the accepted length of the array. */ OVS_KEY_ATTR_CT_STATE, /* u32 bitmask of OVS_CS_F_* */ OVS_KEY_ATTR_CT_ZONE, /* u16 connection tracking zone. */ OVS_KEY_ATTR_CT_MARK, /* u32 connection tracking mark */ OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking labels */ #ifdef __KERNEL__ /* Only used within kernel data path. */ OVS_KEY_ATTR_TUNNEL_INFO, /* struct ovs_tunnel_info */ #endif __OVS_KEY_ATTR_MAX }; #define OVS_KEY_ATTR_MAX (__OVS_KEY_ATTR_MAX - 1) enum ovs_tunnel_key_attr { OVS_TUNNEL_KEY_ATTR_ID, /* be64 Tunnel ID */ OVS_TUNNEL_KEY_ATTR_IPV4_SRC, /* be32 src IP address. */ OVS_TUNNEL_KEY_ATTR_IPV4_DST, /* be32 dst IP address. */ OVS_TUNNEL_KEY_ATTR_TOS, /* u8 Tunnel IP ToS. */ OVS_TUNNEL_KEY_ATTR_TTL, /* u8 Tunnel IP TTL. */ OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT, /* No argument, set DF. */ OVS_TUNNEL_KEY_ATTR_CSUM, /* No argument. CSUM packet. */ OVS_TUNNEL_KEY_ATTR_OAM, /* No argument. OAM frame. */ OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, /* Array of Geneve options. */ OVS_TUNNEL_KEY_ATTR_TP_SRC, /* be16 src Transport Port. */ OVS_TUNNEL_KEY_ATTR_TP_DST, /* be16 dst Transport Port. */ OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS, /* Nested OVS_VXLAN_EXT_* */ OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ __OVS_TUNNEL_KEY_ATTR_MAX }; #define OVS_TUNNEL_KEY_ATTR_MAX (__OVS_TUNNEL_KEY_ATTR_MAX - 1) /** * enum ovs_frag_type - IPv4 and IPv6 fragment type * @OVS_FRAG_TYPE_NONE: Packet is not a fragment. * @OVS_FRAG_TYPE_FIRST: Packet is a fragment with offset 0. * @OVS_FRAG_TYPE_LATER: Packet is a fragment with nonzero offset. * * Used as the @ipv4_frag in &struct ovs_key_ipv4 and as @ipv6_frag &struct * ovs_key_ipv6. */ enum ovs_frag_type { OVS_FRAG_TYPE_NONE, OVS_FRAG_TYPE_FIRST, OVS_FRAG_TYPE_LATER, __OVS_FRAG_TYPE_MAX }; #define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1) struct ovs_key_ethernet { __u8 eth_src[ETH_ALEN]; __u8 eth_dst[ETH_ALEN]; }; struct ovs_key_mpls { __be32 mpls_lse; }; struct ovs_key_ipv4 { __be32 ipv4_src; __be32 ipv4_dst; __u8 ipv4_proto; __u8 ipv4_tos; __u8 ipv4_ttl; __u8 ipv4_frag; /* One of OVS_FRAG_TYPE_*. */ }; struct ovs_key_ipv6 { __be32 ipv6_src[4]; __be32 ipv6_dst[4]; __be32 ipv6_label; /* 20-bits in least-significant bits. */ __u8 ipv6_proto; __u8 ipv6_tclass; __u8 ipv6_hlimit; __u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */ }; struct ovs_key_tcp { __be16 tcp_src; __be16 tcp_dst; }; struct ovs_key_udp { __be16 udp_src; __be16 udp_dst; }; struct ovs_key_sctp { __be16 sctp_src; __be16 sctp_dst; }; struct ovs_key_icmp { __u8 icmp_type; __u8 icmp_code; }; struct ovs_key_icmpv6 { __u8 icmpv6_type; __u8 icmpv6_code; }; struct ovs_key_arp { __be32 arp_sip; __be32 arp_tip; __be16 arp_op; __u8 arp_sha[ETH_ALEN]; __u8 arp_tha[ETH_ALEN]; }; struct ovs_key_nd { __be32 nd_target[4]; __u8 nd_sll[ETH_ALEN]; __u8 nd_tll[ETH_ALEN]; }; #define OVS_CT_LABELS_LEN 16 struct ovs_key_ct_labels { __u8 ct_labels[OVS_CT_LABELS_LEN]; }; /* OVS_KEY_ATTR_CT_STATE flags */ #define OVS_CS_F_NEW 0x01 /* Beginning of a new connection. */ #define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */ #define OVS_CS_F_RELATED 0x04 /* Related to an established * connection. */ #define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */ #define OVS_CS_F_INVALID 0x10 /* Could not track connection. */ #define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */ /** * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands. * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow * key. Always present in notifications. Required for all requests (except * dumps). * @OVS_FLOW_ATTR_ACTIONS: Nested %OVS_ACTION_ATTR_* attributes specifying * the actions to take for packets that match the key. Always present in * notifications. Required for %OVS_FLOW_CMD_NEW requests, optional for * %OVS_FLOW_CMD_SET requests. An %OVS_FLOW_CMD_SET without * %OVS_FLOW_ATTR_ACTIONS will not modify the actions. To clear the actions, * an %OVS_FLOW_ATTR_ACTIONS without any nested attributes must be given. * @OVS_FLOW_ATTR_STATS: &struct ovs_flow_stats giving statistics for this * flow. Present in notifications if the stats would be nonzero. Ignored in * requests. * @OVS_FLOW_ATTR_TCP_FLAGS: An 8-bit value giving the OR'd value of all of the * TCP flags seen on packets in this flow. Only present in notifications for * TCP flows, and only if it would be nonzero. Ignored in requests. * @OVS_FLOW_ATTR_USED: A 64-bit integer giving the time, in milliseconds on * the system monotonic clock, at which a packet was last processed for this * flow. Only present in notifications if a packet has been processed for this * flow. Ignored in requests. * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the * last-used time, accumulated TCP flags, and statistics for this flow. * Otherwise ignored in requests. Never present in notifications. * @OVS_FLOW_ATTR_MASK: Nested %OVS_KEY_ATTR_* attributes specifying the * mask bits for wildcarded flow match. Mask bit value '1' specifies exact * match with corresponding flow key bit, while mask bit value '0' specifies * a wildcarded match. Omitting attribute is treated as wildcarding all * corresponding fields. Optional for all requests. If not present, * all flow key bits are exact match bits. * @OVS_FLOW_ATTR_UFID: A value between 1-16 octets specifying a unique * identifier for the flow. Causes the flow to be indexed by this value rather * than the value of the %OVS_FLOW_ATTR_KEY attribute. Optional for all * requests. Present in notifications if the flow was created with this * attribute. * @OVS_FLOW_ATTR_UFID_FLAGS: A 32-bit value of OR'd %OVS_UFID_F_* * flags that provide alternative semantics for flow installation and * retrieval. Optional for all requests. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_FLOW_* commands. */ enum ovs_flow_attr { OVS_FLOW_ATTR_UNSPEC, OVS_FLOW_ATTR_KEY, /* Sequence of OVS_KEY_ATTR_* attributes. */ OVS_FLOW_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ OVS_FLOW_ATTR_STATS, /* struct ovs_flow_stats. */ OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */ OVS_FLOW_ATTR_USED, /* u64 msecs last used in monotonic time. */ OVS_FLOW_ATTR_CLEAR, /* Flag to clear stats, tcp_flags, used. */ OVS_FLOW_ATTR_MASK, /* Sequence of OVS_KEY_ATTR_* attributes. */ OVS_FLOW_ATTR_PROBE, /* Flow operation is a feature probe, error * logging should be suppressed. */ OVS_FLOW_ATTR_UFID, /* Variable length unique flow identifier. */ OVS_FLOW_ATTR_UFID_FLAGS,/* u32 of OVS_UFID_F_*. */ __OVS_FLOW_ATTR_MAX }; #define OVS_FLOW_ATTR_MAX (__OVS_FLOW_ATTR_MAX - 1) /** * Omit attributes for notifications. * * If a datapath request contains an OVS_UFID_F_OMIT_* flag, then the datapath * may omit the corresponding 'ovs_flow_attr' from the response. */ #define OVS_UFID_F_OMIT_KEY (1 << 0) #define OVS_UFID_F_OMIT_MASK (1 << 1) #define OVS_UFID_F_OMIT_ACTIONS (1 << 2) /** * enum ovs_sample_attr - Attributes for %OVS_ACTION_ATTR_SAMPLE action. * @OVS_SAMPLE_ATTR_PROBABILITY: 32-bit fraction of packets to sample with * @OVS_ACTION_ATTR_SAMPLE. A value of 0 samples no packets, a value of * %UINT32_MAX samples all packets and intermediate values sample intermediate * fractions of packets. * @OVS_SAMPLE_ATTR_ACTIONS: Set of actions to execute in sampling event. * Actions are passed as nested attributes. * * Executes the specified actions with the given probability on a per-packet * basis. */ enum ovs_sample_attr { OVS_SAMPLE_ATTR_UNSPEC, OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */ OVS_SAMPLE_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ __OVS_SAMPLE_ATTR_MAX, }; #define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1) /** * enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action. * @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION * message should be sent. Required. * @OVS_USERSPACE_ATTR_USERDATA: If present, its variable-length argument is * copied to the %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA. * @OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: If present, u32 output port to get * tunnel info. * @OVS_USERSPACE_ATTR_ACTIONS: If present, send actions with upcall. */ enum ovs_userspace_attr { OVS_USERSPACE_ATTR_UNSPEC, OVS_USERSPACE_ATTR_PID, /* u32 Netlink PID to receive upcalls. */ OVS_USERSPACE_ATTR_USERDATA, /* Optional user-specified cookie. */ OVS_USERSPACE_ATTR_EGRESS_TUN_PORT, /* Optional, u32 output port * to get tunnel info. */ OVS_USERSPACE_ATTR_ACTIONS, /* Optional flag to get actions. */ __OVS_USERSPACE_ATTR_MAX }; #define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1) /** * struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument. * @mpls_lse: MPLS label stack entry to push. * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame. * * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected. */ struct ovs_action_push_mpls { __be32 mpls_lse; __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ }; /** * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument. * @vlan_tpid: Tag protocol identifier (TPID) to push. * @vlan_tci: Tag control identifier (TCI) to push. The CFI bit must be set * (but it will not be set in the 802.1Q header that is pushed). * * The @vlan_tpid value is typically %ETH_P_8021Q. The only acceptable TPID * values are those that the kernel module also parses as 802.1Q headers, to * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN * from having surprising results. */ struct ovs_action_push_vlan { __be16 vlan_tpid; /* 802.1Q TPID. */ __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */ }; /* Data path hash algorithm for computing Datapath hash. * * The algorithm type only specifies the fields in a flow * will be used as part of the hash. Each datapath is free * to use its own hash algorithm. The hash value will be * opaque to the user space daemon. */ enum ovs_hash_alg { OVS_HASH_ALG_L4, }; /* * struct ovs_action_hash - %OVS_ACTION_ATTR_HASH action argument. * @hash_alg: Algorithm used to compute hash prior to recirculation. * @hash_basis: basis used for computing hash. */ struct ovs_action_hash { uint32_t hash_alg; /* One of ovs_hash_alg. */ uint32_t hash_basis; }; #ifndef __KERNEL__ #define TNL_PUSH_HEADER_SIZE 512 /* * struct ovs_action_push_tnl - %OVS_ACTION_ATTR_TUNNEL_PUSH * @tnl_port: To identify tunnel port to pass header info. * @out_port: Physical port to send encapsulated packet. * @header_len: Length of the header to be pushed. * @tnl_type: This is only required to format this header. Otherwise * ODP layer can not parse %header. * @header: Partial header for the tunnel. Tunnel push action can use * this header to build final header according to actual packet parameters. */ struct ovs_action_push_tnl { uint32_t tnl_port; uint32_t out_port; uint32_t header_len; uint32_t tnl_type; /* For logging. */ uint8_t header[TNL_PUSH_HEADER_SIZE]; }; #endif /** * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the conntrack * table. This allows future packets for the same connection to be identified * as 'established' or 'related'. The flow key for the current packet will * retain the pre-commit connection state. * @OVS_CT_ATTR_ZONE: u16 connection tracking zone. * @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the * mask, the corresponding bit in the value is copied to the connection * tracking mark field in the connection. * @OVS_CT_ATTR_LABELS: %OVS_CT_LABELS_LEN value followed by %OVS_CT_LABELS_LEN * mask. For each bit set in the mask, the corresponding bit in the value is * copied to the connection tracking label field in the connection. * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG. */ enum ovs_ct_attr { OVS_CT_ATTR_UNSPEC, OVS_CT_ATTR_COMMIT, /* No argument, commits connection. */ OVS_CT_ATTR_ZONE, /* u16 zone id. */ OVS_CT_ATTR_MARK, /* mark to associate with this connection. */ OVS_CT_ATTR_LABELS, /* label to associate with this connection. */ OVS_CT_ATTR_HELPER, /* netlink helper to assist detection of related connections. */ __OVS_CT_ATTR_MAX }; #define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1) /** * enum ovs_action_attr - Action types. * * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested * %OVS_USERSPACE_ATTR_* attributes. * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the * packet. * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet. * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in * the nested %OVS_SAMPLE_ATTR_* attributes. * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its * value. * @OVS_ACTION_ATTR_SET_MASKED: Replaces the contents of an existing header. A * nested %OVS_KEY_ATTR_* attribute specifies a header to modify, its value, * and a mask. For every bit set in the mask, the corresponding bit value * is copied from the value to the packet header field, rest of the bits are * left unchanged. The non-masked value bits must be passed in as zeroes. * Masking is not supported for the %OVS_KEY_ATTR_TUNNEL attribute. * @OVS_ACTION_RECIRC: Recirculate within the data path. * @OVS_ACTION_HASH: Compute and set flow hash value. * @OVS_ACTION_ATTR_PUSH_MPLS: Push a new MPLS label stack entry onto the * top of the packets MPLS label stack. Set the ethertype of the * encapsulating frame to either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC to * indicate the new packet contents. * @OVS_ACTION_ATTR_POP_MPLS: Pop an MPLS label stack entry off of the * packet's MPLS label stack. Set the encapsulating frame's ethertype to * indicate the new packet contents. This could potentially still be * %ETH_P_MPLS if the resulting MPLS label stack is not empty. If there * is no MPLS label stack, as determined by ethertype, no action is taken. * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related * entries in the flow key. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment * type may not be changed. * * * @OVS_ACTION_ATTR_SET_TO_MASKED: Kernel internal masked set action translated * from the @OVS_ACTION_ATTR_SET. * @OVS_ACTION_ATTR_TUNNEL_PUSH: Push tunnel header described by struct * ovs_action_push_tnl. * @OVS_ACTION_ATTR_TUNNEL_POP: Lookup tunnel port by port-no passed and pop * tunnel header. */ enum ovs_action_attr { OVS_ACTION_ATTR_UNSPEC, OVS_ACTION_ATTR_OUTPUT, /* u32 port number. */ OVS_ACTION_ATTR_USERSPACE, /* Nested OVS_USERSPACE_ATTR_*. */ OVS_ACTION_ATTR_SET, /* One nested OVS_KEY_ATTR_*. */ OVS_ACTION_ATTR_PUSH_VLAN, /* struct ovs_action_push_vlan. */ OVS_ACTION_ATTR_POP_VLAN, /* No argument. */ OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */ OVS_ACTION_ATTR_RECIRC, /* u32 recirc_id. */ OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */ OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */ OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */ OVS_ACTION_ATTR_SET_MASKED, /* One nested OVS_KEY_ATTR_* including * data immediately followed by a mask. * The data must be zero for the unmasked * bits. */ OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */ #endif __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ #ifdef __KERNEL__ OVS_ACTION_ATTR_SET_TO_MASKED, /* Kernel module internal masked * set action converted from * OVS_ACTION_ATTR_SET. */ #endif }; #define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1) #endif /* _LINUX_OPENVSWITCH_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/bug.h0000644000000000000000000000013213534540071023564 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 30 ctime=1567801425.429858203 openvswitch-2.5.9/datapath/linux/compat/include/linux/bug.h0000644000175000017500000000033213534540071025250 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_BUG_WRAPPER_H #define __LINUX_BUG_WRAPPER_H 1 #include_next #ifdef __CHECKER__ #ifndef BUILD_BUG_ON_INVALID #define BUILD_BUG_ON_INVALID(e) (0) #endif #endif /* __CHECKER__ */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/if_arp.h0000644000000000000000000000013213534540071024247 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.441858291 openvswitch-2.5.9/datapath/linux/compat/include/linux/if_arp.h0000644000175000017500000000051213534540071025733 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IF_ARP_WRAPPER_H #define __LINUX_IF_ARP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS #include static inline struct arphdr *arp_hdr(const struct sk_buff *skb) { return (struct arphdr *)skb_network_header(skb); } #endif /* !HAVE_SKBUFF_HEADER_HELPERS */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/ipv6.h0000644000000000000000000000013213534540071023673 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.449858351 openvswitch-2.5.9/datapath/linux/compat/include/linux/ipv6.h0000644000175000017500000000101513534540071025356 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IPV6_WRAPPER_H #define __LINUX_IPV6_WRAPPER_H 1 #include_next struct frag_queue; struct inet_frags; #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) { return (struct ipv6hdr *)skb_network_header(skb); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) void rpl_ip6_expire_frag_queue(struct net *net, struct frag_queue *fq, struct inet_frags *frags); #define ip6_expire_frag_queue rpl_ip6_expire_frag_queue #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/percpu.h0000644000000000000000000000013213534540071024305 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.429858203 openvswitch-2.5.9/datapath/linux/compat/include/linux/percpu.h0000644000175000017500000000132113534540071025770 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_PERCPU_WRAPPER_H #define __LINUX_PERCPU_WRAPPER_H 1 #include_next #if !defined this_cpu_ptr #define this_cpu_ptr(ptr) per_cpu_ptr(ptr, smp_processor_id()) #endif #ifdef HAVE_RHEL6_PER_CPU #undef this_cpu_read #undef this_cpu_inc #undef this_cpu_dec #endif #if !defined this_cpu_read #define this_cpu_read(ptr) percpu_read(ptr) #endif #if !defined this_cpu_inc #define this_cpu_inc(ptr) percpu_add(ptr, 1) #endif #if !defined this_cpu_dec #define this_cpu_dec(ptr) percpu_sub(ptr, 1) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) #define get_pcpu_ptr(name) ((void *)this_cpu_ptr(&__pcpu_unique_##name)) #else #define get_pcpu_ptr(name) (this_cpu_ptr(&name)) #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/sctp.h0000644000000000000000000000013213534540071023760 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.469858498 openvswitch-2.5.9/datapath/linux/compat/include/linux/sctp.h0000644000175000017500000000045513534540071025452 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_SCTP_WRAPPER_H #define __LINUX_SCTP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct sctphdr *sctp_hdr(const struct sk_buff *skb) { return (struct sctphdr *)skb_transport_header(skb); } #endif /* HAVE_SKBUFF_HEADER_HELPERS */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/compiler.h0000644000000000000000000000013213534540071024621 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 30 ctime=1567801425.429858203 openvswitch-2.5.9/datapath/linux/compat/include/linux/compiler.h0000644000175000017500000000030013534540071026300 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_COMPILER_WRAPPER_H #define __LINUX_COMPILER_WRAPPER_H 1 #include_next #ifndef __percpu #define __percpu #endif #ifndef __rcu #define __rcu #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/rtnetlink.h0000644000000000000000000000013213534540071025021 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.469858498 openvswitch-2.5.9/datapath/linux/compat/include/linux/rtnetlink.h0000644000175000017500000000214113534540071026505 0ustar00jpettitjpettit00000000000000#ifndef __RTNETLINK_WRAPPER_H #define __RTNETLINK_WRAPPER_H 1 #include_next #ifndef HAVE_LOCKDEP_RTNL_IS_HELD #ifdef CONFIG_PROVE_LOCKING static inline int lockdep_rtnl_is_held(void) { return 1; } #endif #endif #ifndef rcu_dereference_rtnl /** * rcu_dereference_rtnl - rcu_dereference with debug checking * @p: The pointer to read, prior to dereferencing * * Do an rcu_dereference(p), but check caller either holds rcu_read_lock() * or RTNL. Note : Please prefer rtnl_dereference() or rcu_dereference() */ #define rcu_dereference_rtnl(p) \ rcu_dereference_check(p, rcu_read_lock_held() || \ lockdep_rtnl_is_held()) #endif #ifndef rtnl_dereference /** * rtnl_dereference - fetch RCU pointer when updates are prevented by RTNL * @p: The pointer to read, prior to dereferencing * * Return the value of the specified RCU-protected pointer, but omit * both the smp_read_barrier_depends() and the ACCESS_ONCE(), because * caller holds RTNL. */ #define rtnl_dereference(p) \ rcu_dereference_protected(p, lockdep_rtnl_is_held()) #endif #endif /* linux/rtnetlink.h wrapper */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/mpls.h0000644000000000000000000000013213534540071023762 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.457858409 openvswitch-2.5.9/datapath/linux/compat/include/linux/mpls.h0000644000175000017500000000212613534540071025451 0ustar00jpettitjpettit00000000000000#ifndef _UAPI_MPLS_WRAPPER_H #define _UAPI_MPLS_WRAPPER_H #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0) #include_next #else #include #include /* Reference: RFC 5462, RFC 3032 * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Label | TC |S| TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Label: Label Value, 20 bits * TC: Traffic Class field, 3 bits * S: Bottom of Stack, 1 bit * TTL: Time to Live, 8 bits */ struct mpls_label { __be32 entry; }; #define MPLS_LS_LABEL_MASK 0xFFFFF000 #define MPLS_LS_LABEL_SHIFT 12 #define MPLS_LS_TC_MASK 0x00000E00 #define MPLS_LS_TC_SHIFT 9 #define MPLS_LS_S_MASK 0x00000100 #define MPLS_LS_S_SHIFT 8 #define MPLS_LS_TTL_MASK 0x000000FF #define MPLS_LS_TTL_SHIFT 0 #endif #endif /* _UAPI_MPLS_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/compiler-gcc.h0000644000000000000000000000013213534540071025353 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 30 ctime=1567801425.433858233 openvswitch-2.5.9/datapath/linux/compat/include/linux/compiler-gcc.h0000644000175000017500000000047513534540071027047 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_COMPILER_H #error "Please don't include directly, include instead." #endif #include_next #ifndef __packed #define __packed __attribute__((packed)) #endif #ifndef __always_unused #define __always_unused __attribute__((unused)) #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/if_vlan.h0000644000000000000000000000013213534540071024425 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.445858321 openvswitch-2.5.9/datapath/linux/compat/include/linux/if_vlan.h0000644000175000017500000001425313534540071026120 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IF_VLAN_WRAPPER_H #define __LINUX_IF_VLAN_WRAPPER_H 1 #include #include #include_next #ifndef HAVE_VLAN_INSERT_TAG_SET_PROTO /* * The behavior of __vlan_put_tag()/vlan_insert_tag_set_proto() has changed * over time: * * - In 2.6.26 and earlier, it adjusted both MAC and network header * pointers. (The latter didn't make any sense.) * * - In 2.6.27 and 2.6.28, it did not adjust any header pointers at all. * * - In 2.6.29 and later, it adjusts the MAC header pointer only. * * - In 3.19 and later, it was renamed to vlan_insert_tag_set_proto() * * This is the version from 2.6.33. We unconditionally substitute this version * to avoid the need to guess whether the version in the kernel tree is * acceptable. */ #define vlan_insert_tag_set_proto(skb, proto, vlan_tci) \ rpl_vlan_insert_tag_set_proto(skb, vlan_tci) static inline struct sk_buff *rpl_vlan_insert_tag_set_proto(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; if (skb_cow_head(skb, VLAN_HLEN) < 0) { kfree_skb(skb); return NULL; } veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); skb->mac_header -= VLAN_HLEN; /* first, the ethernet type */ veth->h_vlan_proto = htons(ETH_P_8021Q); /* now, the TCI */ veth->h_vlan_TCI = htons(vlan_tci); skb->protocol = htons(ETH_P_8021Q); return skb; } #endif #ifndef HAVE_VLAN_HWACCEL_PUSH_INSIDE /* * __vlan_hwaccel_push_inside - pushes vlan tag to the payload * @skb: skbuff to tag * * Pushes the VLAN tag from @skb->vlan_tci inside to the payload. * * Following the skb_unshare() example, in case of error, the calling function * doesn't have to worry about freeing the original skb. */ static inline struct sk_buff *__vlan_hwaccel_push_inside(struct sk_buff *skb) { skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, vlan_tx_tag_get(skb)); if (likely(skb)) skb->vlan_tci = 0; return skb; } /* * vlan_hwaccel_push_inside - pushes vlan tag to the payload * @skb: skbuff to tag * * Checks is tag is present in @skb->vlan_tci and if it is, it pushes the * VLAN tag from @skb->vlan_tci inside to the payload. * * Following the skb_unshare() example, in case of error, the calling function * doesn't have to worry about freeing the original skb. */ static inline struct sk_buff *vlan_hwaccel_push_inside(struct sk_buff *skb) { if (vlan_tx_tag_present(skb)) skb = __vlan_hwaccel_push_inside(skb); return skb; } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) static inline struct sk_buff *rpl___vlan_hwaccel_put_tag(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) { return __vlan_hwaccel_put_tag(skb, vlan_tci); } #define __vlan_hwaccel_put_tag rpl___vlan_hwaccel_put_tag #endif /* All of these were introduced in a single commit preceding 2.6.33, so * presumably all of them or none of them are present. */ #ifndef VLAN_PRIO_MASK #define VLAN_PRIO_MASK 0xe000 /* Priority Code Point */ #define VLAN_PRIO_SHIFT 13 #define VLAN_CFI_MASK 0x1000 /* Canonical Format Indicator */ #define VLAN_TAG_PRESENT VLAN_CFI_MASK #endif #ifndef HAVE_VLAN_SET_ENCAP_PROTO static inline void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr) { __be16 proto; unsigned char *rawp; /* * Was a VLAN packet, grab the encapsulated protocol, which the layer * three protocols care about. */ proto = vhdr->h_vlan_encapsulated_proto; if (ntohs(proto) >= 1536) { skb->protocol = proto; return; } rawp = skb->data; if (*(unsigned short *) rawp == 0xFFFF) /* * This is a magic hack to spot IPX packets. Older Novell * breaks the protocol design and runs IPX over 802.3 without * an 802.2 LLC layer. We look for FFFF which isn't a used * 802.2 SSAP/DSAP. This won't work for fault tolerant netware * but does for the rest. */ skb->protocol = htons(ETH_P_802_3); else /* * Real 802.2 LLC */ skb->protocol = htons(ETH_P_802_2); } #endif #ifndef HAVE___VLAN_INSERT_TAG /* Kernels which don't have __vlan_insert_tag() also don't have skb->vlan_proto * so ignore the proto paramter. */ #define __vlan_insert_tag(skb, proto, tci) rpl_vlan_insert_tag(skb, tci) static inline int rpl_vlan_insert_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; if (skb_cow_head(skb, VLAN_HLEN) < 0) return -ENOMEM; veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ memmove(skb->data, skb->data + VLAN_HLEN, 2 * ETH_ALEN); skb->mac_header -= VLAN_HLEN; /* first, the ethernet type */ veth->h_vlan_proto = htons(ETH_P_8021Q); /* now, the TCI */ veth->h_vlan_TCI = htons(vlan_tci); return 0; } #endif #ifndef skb_vlan_tag_present #define skb_vlan_tag_present(skb) vlan_tx_tag_present(skb) #define skb_vlan_tag_get(skb) vlan_tx_tag_get(skb) #endif #ifndef HAVE_VLAN_GET_PROTOCOL static inline __be16 __vlan_get_protocol(struct sk_buff *skb, __be16 type, int *depth) { unsigned int vlan_depth = skb->mac_len; /* if type is 802.1Q/AD then the header should already be * present at mac_len - VLAN_HLEN (if mac_len > 0), or at * ETH_HLEN otherwise */ if (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) { if (vlan_depth) { if (WARN_ON(vlan_depth < VLAN_HLEN)) return 0; vlan_depth -= VLAN_HLEN; } else { vlan_depth = ETH_HLEN; } do { struct vlan_hdr *vh; if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) return 0; vh = (struct vlan_hdr *)(skb->data + vlan_depth); type = vh->h_vlan_encapsulated_proto; vlan_depth += VLAN_HLEN; } while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)); } if (depth) *depth = vlan_depth; return type; } /** * vlan_get_protocol - get protocol EtherType. * @skb: skbuff to query * * Returns the EtherType of the packet, regardless of whether it is * vlan encapsulated (normal or hardware accelerated) or not. */ static inline __be16 vlan_get_protocol(struct sk_buff *skb) { return __vlan_get_protocol(skb, skb->protocol, NULL); } #endif #endif /* linux/if_vlan.h wrapper */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/types.h0000644000000000000000000000013213534540071024153 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.473858528 openvswitch-2.5.9/datapath/linux/compat/include/linux/types.h0000644000175000017500000000042313534540071025640 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_TYPES_WRAPPER_H #define __LINUX_TYPES_WRAPPER_H 1 #include_next #ifndef HAVE_CSUM_TYPES typedef __u16 __bitwise __sum16; typedef __u32 __bitwise __wsum; #endif #ifndef HAVE_BOOL_TYPE typedef _Bool bool; #endif /* !HAVE_BOOL_TYPE */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/etherdevice.h0000644000000000000000000000013213534540071025276 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.437858262 openvswitch-2.5.9/datapath/linux/compat/include/linux/etherdevice.h0000644000175000017500000000455213534540071026772 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_ETHERDEVICE_WRAPPER_H #define __LINUX_ETHERDEVICE_WRAPPER_H 1 #include #include_next #ifndef HAVE_ETH_HW_ADDR_RANDOM #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) static inline void eth_hw_addr_random(struct net_device *dev) { random_ether_addr(dev->dev_addr); } #elif LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0) static inline void eth_hw_addr_random(struct net_device *dev) { dev_hw_addr_random(dev, dev->dev_addr); } #endif #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) #define eth_mac_addr rpl_eth_mac_addr static inline int eth_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; #ifdef NET_ADDR_RANDOM dev->addr_assign_type &= ~NET_ADDR_RANDOM; #endif memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); return 0; } #endif #ifndef HAVE_ETHER_ADDR_COPY static inline void ether_addr_copy(u8 *dst, const u8 *src) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) *(u32 *)dst = *(const u32 *)src; *(u16 *)(dst + 4) = *(const u16 *)(src + 4); #else u16 *a = (u16 *)dst; const u16 *b = (const u16 *)src; a[0] = b[0]; a[1] = b[1]; a[2] = b[2]; #endif } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) #define eth_proto_is_802_3 rpl_eth_proto_is_802_3 static inline bool eth_proto_is_802_3(__be16 proto) { #ifndef __BIG_ENDIAN /* if CPU is little endian mask off bits representing LSB */ proto &= htons(0xFF00); #endif /* cast both to u16 and compare since LSB can be ignored */ return (__force u16)proto >= (__force u16)htons(ETH_P_802_3_MIN); } #endif #define ether_addr_equal rpl_ether_addr_equal static inline bool ether_addr_equal(const u8 *addr1, const u8 *addr2) { #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) u32 fold = ((*(const u32 *)addr1) ^ (*(const u32 *)addr2)) | ((*(const u16 *)(addr1 + 4)) ^ (*(const u16 *)(addr2 + 4))); return fold == 0; #else const u16 *a = (const u16 *)addr1; const u16 *b = (const u16 *)addr2; return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) == 0; #endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) #define eth_gro_receive rpl_eth_gro_receive struct sk_buff **rpl_eth_gro_receive(struct sk_buff **head, struct sk_buff *skb); #define eth_gro_complete rpl_eth_gro_complete int rpl_eth_gro_complete(struct sk_buff *skb, int nhoff); #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/stddef.h0000644000000000000000000000013213534540071024260 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.469858498 openvswitch-2.5.9/datapath/linux/compat/include/linux/stddef.h0000644000175000017500000000055213534540071025750 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_STDDEF_WRAPPER_H #define __LINUX_STDDEF_WRAPPER_H 1 #include_next #ifdef __KERNEL__ #ifndef HAVE_BOOL_TYPE enum { false = 0, true = 1 }; #endif /* !HAVE_BOOL_TYPE */ #ifndef offsetofend #define offsetofend(TYPE, MEMBER) \ (offsetof(TYPE, MEMBER) + sizeof(((TYPE *)0)->MEMBER)) #endif #endif /* __KERNEL__ */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/kconfig.h0000644000000000000000000000013213534540071024427 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.449858351 openvswitch-2.5.9/datapath/linux/compat/include/linux/kconfig.h0000644000175000017500000000336113534540071026120 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_KCONFIG_WRAPPER_H #define __LINUX_KCONFIG_WRAPPER_H #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) #define CONFIG_NET_IPGRE_DEMUX 1 #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0) #include_next #endif #ifndef IS_ENABLED /* * Helper macros to use CONFIG_ options in C/CPP expressions. Note that * these only work with boolean and tristate options. */ /* * Getting something that works in C and CPP for an arg that may or may * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" * we match on the placeholder define, insert the "0," for arg1 and generate * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when * the last step cherry picks the 2nd arg, we get a zero. */ #define __ARG_PLACEHOLDER_1 0, #define config_enabled(cfg) _config_enabled(cfg) #define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) #define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) #define ___config_enabled(__ignored, val, ...) val /* * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm', * 0 otherwise. * */ #define IS_ENABLED(option) \ (config_enabled(option) || config_enabled(option##_MODULE)) /* * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0 * otherwise. For boolean options, this is equivalent to * IS_ENABLED(CONFIG_FOO). */ #define IS_BUILTIN(option) config_enabled(option) /* * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0 * otherwise. */ #define IS_MODULE(option) config_enabled(option##_MODULE) #endif /* IS_ENABLED */ #endif /* __LINUX_KCONFIG_WRAPER_H */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/tcp.h0000644000000000000000000000013213534540071023575 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.473858528 openvswitch-2.5.9/datapath/linux/compat/include/linux/tcp.h0000644000175000017500000000061613534540071025266 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_TCP_WRAPPER_H #define __LINUX_TCP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb) { return (struct tcphdr *)skb_transport_header(skb); } static inline unsigned int tcp_hdrlen(const struct sk_buff *skb) { return tcp_hdr(skb)->doff * 4; } #endif /* !HAVE_SKBUFF_HEADER_HELPERS */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/if_link.h0000644000000000000000000000013213534540071024422 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.445858321 openvswitch-2.5.9/datapath/linux/compat/include/linux/if_link.h0000644000175000017500000001047713534540071026121 0ustar00jpettitjpettit00000000000000#ifndef _LINUX_IF_LINK_WRAPPER_H #define _LINUX_IF_LINK_WRAPPER_H #include_next /* GENEVE section */ enum { #define IFLA_GENEVE_UNSPEC rpl_IFLA_GENEVE_UNSPEC IFLA_GENEVE_UNSPEC, #define IFLA_GENEVE_ID rpl_IFLA_GENEVE_ID IFLA_GENEVE_ID, #define IFLA_GENEVE_REMOTE rpl_IFLA_GENEVE_REMOTE IFLA_GENEVE_REMOTE, #define IFLA_GENEVE_TTL rpl_IFLA_GENEVE_TTL IFLA_GENEVE_TTL, #define IFLA_GENEVE_TOS rpl_IFLA_GENEVE_TOS IFLA_GENEVE_TOS, #define IFLA_GENEVE_PORT rpl_IFLA_GENEVE_PORT IFLA_GENEVE_PORT, /* destination port */ #define IFLA_GENEVE_COLLECT_METADATA rpl_IFLA_GENEVE_COLLECT_METADATA IFLA_GENEVE_COLLECT_METADATA, #define __IFLA_GENEVE_MAX rpl__IFLA_GENEVE_MAX __IFLA_GENEVE_MAX }; #undef IFLA_GENEVE_MAX #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) /* STT section */ enum { IFLA_STT_PORT, /* destination port */ __IFLA_STT_MAX }; #define IFLA_STT_MAX (__IFLA_STT_MAX - 1) /* LISP section */ enum { IFLA_LISP_PORT, /* destination port */ __IFLA_LISP_MAX }; #define IFLA_LISP_MAX (__IFLA_LISP_MAX - 1) /* VXLAN section */ enum { #define IFLA_VXLAN_UNSPEC rpl_IFLA_VXLAN_UNSPEC IFLA_VXLAN_UNSPEC, #define IFLA_VXLAN_ID rpl_IFLA_VXLAN_ID IFLA_VXLAN_ID, #define IFLA_VXLAN_GROUP rpl_IFLA_VXLAN_GROUP IFLA_VXLAN_GROUP, /* group or remote address */ #define IFLA_VXLAN_LINK rpl_IFLA_VXLAN_LINK IFLA_VXLAN_LINK, #define IFLA_VXLAN_LOCAL rpl_IFLA_VXLAN_LOCAL IFLA_VXLAN_LOCAL, #define IFLA_VXLAN_TTL rpl_IFLA_VXLAN_TTL IFLA_VXLAN_TTL, #define IFLA_VXLAN_TOS rpl_IFLA_VXLAN_TOS IFLA_VXLAN_TOS, #define IFLA_VXLAN_LEARNING rpl_IFLA_VXLAN_LEARNING IFLA_VXLAN_LEARNING, #define IFLA_VXLAN_AGEING rpl_IFLA_VXLAN_AGEING IFLA_VXLAN_AGEING, #define IFLA_VXLAN_LIMIT rpl_IFLA_VXLAN_LIMIT IFLA_VXLAN_LIMIT, #define IFLA_VXLAN_PORT_RANGE rpl_IFLA_VXLAN_PORT_RANGE IFLA_VXLAN_PORT_RANGE, /* source port */ #define IFLA_VXLAN_PROXY rpl_IFLA_VXLAN_PROXY IFLA_VXLAN_PROXY, #define IFLA_VXLAN_RSC rpl_IFLA_VXLAN_RSC IFLA_VXLAN_RSC, #define IFLA_VXLAN_L2MISS rpl_IFLA_VXLAN_L2MISS IFLA_VXLAN_L2MISS, #define IFLA_VXLAN_L3MISS rpl_IFLA_VXLAN_L3MISS IFLA_VXLAN_L3MISS, #define IFLA_VXLAN_PORT rpl_IFLA_VXLAN_PORT IFLA_VXLAN_PORT, /* destination port */ #define IFLA_VXLAN_GROUP6 rpl_IFLA_VXLAN_GROUP6 IFLA_VXLAN_GROUP6, #define IFLA_VXLAN_LOCAL6 rpl_IFLA_VXLAN_LOCAL6 IFLA_VXLAN_LOCAL6, #define IFLA_VXLAN_UDP_CSUM rpl_IFLA_VXLAN_UDP_CSUM IFLA_VXLAN_UDP_CSUM, #define IFLA_VXLAN_UDP_ZERO_CSUM6_TX rpl_IFLA_VXLAN_UDP_ZERO_CSUM6_TX IFLA_VXLAN_UDP_ZERO_CSUM6_TX, #define IFLA_VXLAN_UDP_ZERO_CSUM6_RX rpl_IFLA_VXLAN_UDP_ZERO_CSUM6_RX IFLA_VXLAN_UDP_ZERO_CSUM6_RX, #define IFLA_VXLAN_REMCSUM_TX rpl_IFLA_VXLAN_REMCSUM_TX IFLA_VXLAN_REMCSUM_TX, #define IFLA_VXLAN_REMCSUM_RX rpl_IFLA_VXLAN_REMCSUM_RX IFLA_VXLAN_REMCSUM_RX, #define IFLA_VXLAN_GBP rpl_IFLA_VXLAN_GBP IFLA_VXLAN_GBP, #define IFLA_VXLAN_REMCSUM_NOPARTIAL rpl_IFLA_VXLAN_REMCSUM_NOPARTIAL IFLA_VXLAN_REMCSUM_NOPARTIAL, #define IFLA_VXLAN_COLLECT_METADATA rpl_IFLA_VXLAN_COLLECT_METADATA IFLA_VXLAN_COLLECT_METADATA, #define __IFLA_VXLAN_MAX rpl___IFLA_VXLAN_MAX __IFLA_VXLAN_MAX }; #undef IFLA_VXLAN_MAX #define IFLA_VXLAN_MAX (rpl___IFLA_VXLAN_MAX - 1) #define ifla_vxlan_port_range rpl_ifla_vxlan_port_range struct ifla_vxlan_port_range { __be16 low; __be16 high; }; #ifndef HAVE_RTNL_LINK_STATS64 /* The main device statistics structure */ struct rtnl_link_stats64 { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ __u64 rx_bytes; /* total bytes received */ __u64 tx_bytes; /* total bytes transmitted */ __u64 rx_errors; /* bad packets received */ __u64 tx_errors; /* packet transmit problems */ __u64 rx_dropped; /* no space in linux buffers */ __u64 tx_dropped; /* no space available in linux */ __u64 multicast; /* multicast packets received */ __u64 collisions; /* detailed rx_errors: */ __u64 rx_length_errors; __u64 rx_over_errors; /* receiver ring buff overflow */ __u64 rx_crc_errors; /* recved pkt with crc error */ __u64 rx_frame_errors; /* recv'd frame alignment error */ __u64 rx_fifo_errors; /* recv'r fifo overrun */ __u64 rx_missed_errors; /* receiver missed packet */ /* detailed tx_errors */ __u64 tx_aborted_errors; __u64 tx_carrier_errors; __u64 tx_fifo_errors; __u64 tx_heartbeat_errors; __u64 tx_window_errors; /* for cslip etc */ __u64 rx_compressed; __u64 tx_compressed; }; #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/err.h0000644000000000000000000000013213534540071023577 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.433858233 openvswitch-2.5.9/datapath/linux/compat/include/linux/err.h0000644000175000017500000000122113534540071025261 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_ERR_WRAPPER_H #define __LINUX_ERR_WRAPPER_H 1 #include_next #ifndef HAVE_ERR_CAST /** * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type * @ptr: The pointer to cast. * * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ static inline void *ERR_CAST(const void *ptr) { /* cast away the const */ return (void *) ptr; } #endif /* HAVE_ERR_CAST */ #ifndef HAVE_IS_ERR_OR_NULL static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return !ptr || IS_ERR_VALUE((unsigned long)ptr); } #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/rcupdate.h0000644000000000000000000000013213534540071024616 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.465858468 openvswitch-2.5.9/datapath/linux/compat/include/linux/rcupdate.h0000644000175000017500000000147213534540071026310 0ustar00jpettitjpettit00000000000000#ifndef __RCUPDATE_WRAPPER_H #define __RCUPDATE_WRAPPER_H 1 #include_next #ifndef rcu_dereference_check #define rcu_dereference_check(p, c) rcu_dereference(p) #endif #ifndef rcu_dereference_protected #define rcu_dereference_protected(p, c) (p) #endif #ifndef rcu_dereference_raw #define rcu_dereference_raw(p) rcu_dereference_check(p, 1) #endif #ifndef rcu_access_pointer #define rcu_access_pointer(p) rcu_dereference(p) #endif #ifndef HAVE_RCU_READ_LOCK_HELD static inline int rcu_read_lock_held(void) { return 1; } #endif #ifndef RCU_INITIALIZER #define RCU_INITIALIZER(v) (typeof(*(v)) __force __rcu *)(v) #endif #ifndef RCU_INIT_POINTER #define RCU_INIT_POINTER(p, v) \ do { \ p = RCU_INITIALIZER(v); \ } while (0) #endif #endif /* linux/rcupdate.h wrapper */ openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/skbuff.h0000644000000000000000000000013213534540071024267 xustar0030 mtime=1567801401.265680172 30 atime=1567801402.057685987 30 ctime=1567801425.469858498 openvswitch-2.5.9/datapath/linux/compat/include/linux/skbuff.h0000644000175000017500000002303513534540071025760 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_SKBUFF_WRAPPER_H #define __LINUX_SKBUFF_WRAPPER_H 1 #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) /* This should be before skbuff.h to make sure that we rewrite * the calls there. */ struct sk_buff; int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask); #define pskb_expand_head rpl_pskb_expand_head #endif #include_next #include #ifndef HAVE_IGNORE_DF_RENAME #define ignore_df local_df #endif #ifndef HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, const int offset, void *to, const unsigned int len) { memcpy(to, skb->data + offset, len); } static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, const int offset, const void *from, const unsigned int len) { memcpy(skb->data + offset, from, len); } #endif /* !HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET */ #ifndef HAVE_SKB_RESET_TAIL_POINTER static inline void skb_reset_tail_pointer(struct sk_buff *skb) { skb->tail = skb->data; } #endif /* * The networking layer reserves some headroom in skb data (via * dev_alloc_skb). This is used to avoid having to reallocate skb data when * the header has to grow. In the default case, if the header has to grow * 16 bytes or less we avoid the reallocation. * * Unfortunately this headroom changes the DMA alignment of the resulting * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive * on some architectures. An architecture can override this value, * perhaps setting it to a cacheline in size (since that will maintain * cacheline alignment of the DMA). It must be a power of 2. * * Various parts of the networking layer expect at least 16 bytes of * headroom, you should not reduce this. */ #ifndef NET_SKB_PAD #define NET_SKB_PAD 16 #endif #ifndef HAVE_SKB_COW_HEAD static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom, int cloned) { int delta = 0; if (headroom < NET_SKB_PAD) headroom = NET_SKB_PAD; if (headroom > skb_headroom(skb)) delta = headroom - skb_headroom(skb); if (delta || cloned) return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0, GFP_ATOMIC); return 0; } static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom) { return __skb_cow(skb, headroom, skb_header_cloned(skb)); } #endif /* !HAVE_SKB_COW_HEAD */ #ifndef HAVE_SKB_DST_ACCESSOR_FUNCS static inline struct dst_entry *skb_dst(const struct sk_buff *skb) { return (struct dst_entry *)skb->dst; } static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) { skb->dst = dst; } static inline struct rtable *skb_rtable(const struct sk_buff *skb) { return (struct rtable *)skb->dst; } #endif #ifndef CHECKSUM_PARTIAL #define CHECKSUM_PARTIAL CHECKSUM_HW #endif #ifndef CHECKSUM_COMPLETE #define CHECKSUM_COMPLETE CHECKSUM_HW #endif #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline unsigned char *skb_transport_header(const struct sk_buff *skb) { return skb->h.raw; } static inline void skb_reset_transport_header(struct sk_buff *skb) { skb->h.raw = skb->data; } static inline void skb_set_transport_header(struct sk_buff *skb, const int offset) { skb->h.raw = skb->data + offset; } static inline unsigned char *skb_network_header(const struct sk_buff *skb) { return skb->nh.raw; } static inline void skb_reset_network_header(struct sk_buff *skb) { skb->nh.raw = skb->data; } static inline void skb_set_network_header(struct sk_buff *skb, const int offset) { skb->nh.raw = skb->data + offset; } static inline unsigned char *skb_mac_header(const struct sk_buff *skb) { return skb->mac.raw; } static inline void skb_reset_mac_header(struct sk_buff *skb) { skb->mac_header = skb->data; } static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) { skb->mac.raw = skb->data + offset; } static inline int skb_transport_offset(const struct sk_buff *skb) { return skb_transport_header(skb) - skb->data; } static inline int skb_network_offset(const struct sk_buff *skb) { return skb_network_header(skb) - skb->data; } static inline void skb_copy_to_linear_data(struct sk_buff *skb, const void *from, const unsigned int len) { memcpy(skb->data, from, len); } #endif /* !HAVE_SKBUFF_HEADER_HELPERS */ #ifndef HAVE_SKB_WARN_LRO #ifndef NETIF_F_LRO static inline bool skb_warn_if_lro(const struct sk_buff *skb) { return false; } #else extern void __skb_warn_lro_forwarding(const struct sk_buff *skb); static inline bool skb_warn_if_lro(const struct sk_buff *skb) { /* LRO sets gso_size but not gso_type, whereas if GSO is really * wanted then gso_type will be set. */ struct skb_shared_info *shinfo = skb_shinfo(skb); if (shinfo->gso_size != 0 && unlikely(shinfo->gso_type == 0)) { __skb_warn_lro_forwarding(skb); return true; } return false; } #endif /* NETIF_F_LRO */ #endif /* HAVE_SKB_WARN_LRO */ #ifndef HAVE_CONSUME_SKB #define consume_skb kfree_skb #endif #ifndef HAVE_SKB_FRAG_PAGE #include static inline struct page *skb_frag_page(const skb_frag_t *frag) { return frag->page; } static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page) { frag->page = page; } static inline void skb_frag_size_set(skb_frag_t *frag, unsigned int size) { frag->size = size; } static inline void __skb_frag_ref(skb_frag_t *frag) { get_page(skb_frag_page(frag)); } static inline void __skb_frag_unref(skb_frag_t *frag) { put_page(skb_frag_page(frag)); } static inline void skb_frag_ref(struct sk_buff *skb, int f) { __skb_frag_ref(&skb_shinfo(skb)->frags[f]); } static inline void skb_frag_unref(struct sk_buff *skb, int f) { __skb_frag_unref(&skb_shinfo(skb)->frags[f]); } #endif #ifndef HAVE_SKB_RESET_MAC_LEN static inline void skb_reset_mac_len(struct sk_buff *skb) { skb->mac_len = skb->network_header - skb->mac_header; } #endif #ifndef HAVE_SKB_UNCLONE static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) { might_sleep_if(pri & __GFP_WAIT); if (skb_cloned(skb)) return pskb_expand_head(skb, 0, 0, pri); return 0; } #endif #ifndef HAVE_SKB_ORPHAN_FRAGS static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) { return 0; } #endif #ifndef HAVE_SKB_GET_HASH #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) #define __skb_get_hash rpl__skb_get_rxhash #define skb_get_hash rpl_skb_get_rxhash extern u32 __skb_get_hash(struct sk_buff *skb); static inline __u32 skb_get_hash(struct sk_buff *skb) { #ifdef HAVE_RXHASH if (skb->rxhash) #ifndef HAVE_U16_RXHASH return skb->rxhash; #else return jhash_1word(skb->rxhash, 0); #endif #endif return __skb_get_hash(skb); } #else #define skb_get_hash skb_get_rxhash #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) */ #endif /* HAVE_SKB_GET_HASH */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) static inline void skb_tx_error(struct sk_buff *skb) { return; } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) #define skb_zerocopy_headlen rpl_skb_zerocopy_headlen unsigned int rpl_skb_zerocopy_headlen(const struct sk_buff *from); #endif #ifndef HAVE_SKB_ZEROCOPY #define skb_zerocopy rpl_skb_zerocopy int rpl_skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen); #endif #ifndef HAVE_SKB_CLEAR_HASH static inline void skb_clear_hash(struct sk_buff *skb) { #ifdef HAVE_RXHASH skb->rxhash = 0; #endif #if defined(HAVE_L4_RXHASH) && !defined(HAVE_RHEL_OVS_HOOK) skb->l4_rxhash = 0; #endif } #endif #ifndef HAVE_SKB_HAS_FRAG_LIST #define skb_has_frag_list skb_has_frags #endif #ifndef HAVE___SKB_FILL_PAGE_DESC static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; __skb_frag_set_page(frag, page); frag->page_offset = off; skb_frag_size_set(frag, size); } #endif #ifndef HAVE_SKB_ENSURE_WRITABLE #define skb_ensure_writable rpl_skb_ensure_writable int rpl_skb_ensure_writable(struct sk_buff *skb, int write_len); #endif #ifndef HAVE_SKB_VLAN_POP #define skb_vlan_pop rpl_skb_vlan_pop int rpl_skb_vlan_pop(struct sk_buff *skb); #endif #ifndef HAVE_SKB_VLAN_PUSH #define skb_vlan_push rpl_skb_vlan_push int rpl_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci); #endif #ifndef HAVE_KFREE_SKB_LIST void rpl_kfree_skb_list(struct sk_buff *segs); #define kfree_skb_list rpl_kfree_skb_list #endif #ifndef HAVE_SKB_CHECKSUM_START_OFFSET static inline int skb_checksum_start_offset(const struct sk_buff *skb) { return skb->csum_start - skb_headroom(skb); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) #define skb_postpull_rcsum rpl_skb_postpull_rcsum static inline void skb_postpull_rcsum(struct sk_buff *skb, const void *start, unsigned int len) { if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_sub(skb->csum, csum_partial(start, len, 0)); else if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_start_offset(skb) < 0) skb->ip_summed = CHECKSUM_NONE; } #define skb_pull_rcsum rpl_skb_pull_rcsum static inline unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) { unsigned char *data = skb->data; BUG_ON(len > skb->len); __skb_pull(skb, len); skb_postpull_rcsum(skb, data, len); return skb->data; } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) #define skb_scrub_packet rpl_skb_scrub_packet void rpl_skb_scrub_packet(struct sk_buff *skb, bool xnet); #endif #define skb_pop_mac_header rpl_skb_pop_mac_header static inline void skb_pop_mac_header(struct sk_buff *skb) { skb->mac_header = skb->network_header; } #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/jiffies.h0000644000000000000000000000013213534540071024426 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.449858351 openvswitch-2.5.9/datapath/linux/compat/include/linux/jiffies.h0000644000175000017500000000140613534540071026115 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_JIFFIES_WRAPPER_H #define __LINUX_JIFFIES_WRAPPER_H 1 #include_next #include /* Same as above, but does so with platform independent 64bit types. * These must be used when utilizing jiffies_64 (i.e. return value of * get_jiffies_64() */ #ifndef time_after64 #define time_after64(a, b) \ (typecheck(__u64, a) && \ typecheck(__u64, b) && \ ((__s64)(b) - (__s64)(a) < 0)) #endif #ifndef time_before64 #define time_before64(a, b) time_after64(b, a) #endif #ifndef time_after_eq64 #define time_after_eq64(a, b) \ (typecheck(__u64, a) && \ typecheck(__u64, b) && \ ((__s64)(a) - (__s64)(b) >= 0)) #endif #ifndef time_before_eq64 #define time_before_eq64(a, b) time_after_eq64(b, a) #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/ip.h0000644000000000000000000000013213534540071023417 xustar0030 mtime=1567801401.257680113 30 atime=1567801402.057685987 30 ctime=1567801425.449858351 openvswitch-2.5.9/datapath/linux/compat/include/linux/ip.h0000644000175000017500000000063613534540071025112 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_IP_WRAPPER_H #define __LINUX_IP_WRAPPER_H 1 #include_next #ifndef HAVE_SKBUFF_HEADER_HELPERS #include static inline struct iphdr *ip_hdr(const struct sk_buff *skb) { return (struct iphdr *)skb_network_header(skb); } static inline unsigned int ip_hdrlen(const struct sk_buff *skb) { return ip_hdr(skb)->ihl * 4; } #endif /* !HAVE_SKBUFF_HEADER_HELPERS */ #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/list.h0000644000000000000000000000013213534540071023762 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.453858381 openvswitch-2.5.9/datapath/linux/compat/include/linux/list.h0000644000175000017500000000166613534540071025461 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_LIST_WRAPPER_H #define __LINUX_LIST_WRAPPER_H 1 #include_next #ifndef hlist_entry_safe #define hlist_entry_safe(ptr, type, member) \ ({ typeof(ptr) ____ptr = (ptr); \ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ }) #undef hlist_for_each_entry #define hlist_for_each_entry(pos, head, member) \ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\ pos; \ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member)) #undef hlist_for_each_entry_safe #define hlist_for_each_entry_safe(pos, n, head, member) \ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\ pos && ({ n = pos->member.next; 1; }); \ pos = hlist_entry_safe(n, typeof(*pos), member)) #endif #ifndef list_first_entry_or_null #define list_first_entry_or_null(ptr, type, member) \ (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) #endif #endif openvswitch-2.5.9/datapath/linux/compat/include/linux/PaxHeaders.82075/netdevice.h0000644000000000000000000000013213534540071024755 xustar0030 mtime=1567801401.261680141 30 atime=1567801402.057685987 30 ctime=1567801425.457858409 openvswitch-2.5.9/datapath/linux/compat/include/linux/netdevice.h0000644000175000017500000001617113534540071026451 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_NETDEVICE_WRAPPER_H #define __LINUX_NETDEVICE_WRAPPER_H 1 #include_next #include struct net; #include #ifndef IFF_TX_SKB_SHARING #define IFF_TX_SKB_SHARING 0 #endif #ifndef IFF_OVS_DATAPATH #define IFF_OVS_DATAPATH 0 #else #define HAVE_OVS_DATAPATH #endif #ifndef IFF_LIVE_ADDR_CHANGE #define IFF_LIVE_ADDR_CHANGE 0 #endif #ifndef IFF_NO_QUEUE #define IFF_NO_QUEUE 0 #endif #ifndef IFF_OPENVSWITCH #define IFF_OPENVSWITCH 0 #endif #ifndef to_net_dev #define to_net_dev(class) container_of(class, struct net_device, NETDEV_DEV_MEMBER) #endif #ifndef HAVE_NET_NAME_UNKNOWN #undef alloc_netdev #define NET_NAME_UNKNOWN 0 #define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \ alloc_netdev_mq(sizeof_priv, name, setup, 1) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) #define unregister_netdevice_queue(dev, head) unregister_netdevice(dev) #define unregister_netdevice_many(head) #endif #ifndef HAVE_DEV_DISABLE_LRO extern void dev_disable_lro(struct net_device *dev); #endif #if !defined HAVE_NETDEV_RX_HANDLER_REGISTER || \ defined HAVE_RHEL_OVS_HOOK #ifdef HAVE_RHEL_OVS_HOOK typedef struct sk_buff *(openvswitch_handle_frame_hook_t)(struct sk_buff *skb); extern openvswitch_handle_frame_hook_t *openvswitch_handle_frame_hook; #define netdev_rx_handler_register rpl_netdev_rx_handler_register int rpl_netdev_rx_handler_register(struct net_device *dev, openvswitch_handle_frame_hook_t *hook, void *rx_handler_data); #else #define netdev_rx_handler_register rpl_netdev_rx_handler_register int rpl_netdev_rx_handler_register(struct net_device *dev, struct sk_buff *(*netdev_hook)(struct net_bridge_port *p, struct sk_buff *skb), void *rx_handler_data); #endif #define netdev_rx_handler_unregister rpl_netdev_rx_handler_unregister void rpl_netdev_rx_handler_unregister(struct net_device *dev); #endif #ifndef HAVE_DEV_GET_BY_INDEX_RCU static inline struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex) { struct net_device *dev; read_lock(&dev_base_lock); dev = __dev_get_by_index(net, ifindex); read_unlock(&dev_base_lock); return dev; } #endif #ifndef NETIF_F_FSO #define NETIF_F_FSO 0 #endif #ifndef HAVE_NETDEV_FEATURES_T typedef u32 netdev_features_t; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) #define OVS_USE_COMPAT_GSO_SEGMENTATION #endif #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION /* define compat version to handle MPLS segmentation offload. */ #define __skb_gso_segment rpl__skb_gso_segment struct sk_buff *rpl__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path); #define skb_gso_segment rpl_skb_gso_segment static inline struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, netdev_features_t features) { return rpl__skb_gso_segment(skb, features, true); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) #define netif_skb_features rpl_netif_skb_features netdev_features_t rpl_netif_skb_features(struct sk_buff *skb); #endif #ifdef HAVE_NETIF_NEEDS_GSO_NETDEV #define netif_needs_gso rpl_netif_needs_gso static inline bool netif_needs_gso(struct sk_buff *skb, netdev_features_t features) { return skb_is_gso(skb) && (!skb_gso_ok(skb, features) || unlikely((skb->ip_summed != CHECKSUM_PARTIAL) && (skb->ip_summed != CHECKSUM_UNNECESSARY))); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) /* XEN dom0 networking assumes dev->master is bond device * and it tries to access bond private structure from dev->master * ptr on receive path. This causes panic. Therefore it is better * not to backport this API. **/ static inline int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev) { return 0; } static inline void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { } static inline struct net_device *netdev_master_upper_dev_get(struct net_device *dev) { return NULL; } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) #define dev_queue_xmit rpl_dev_queue_xmit int rpl_dev_queue_xmit(struct sk_buff *skb); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) static inline struct net_device *netdev_notifier_info_to_dev(void *info) { return info; } #endif #ifndef HAVE_PCPU_SW_NETSTATS #include struct pcpu_sw_netstats { u64 rx_packets; u64 rx_bytes; u64 tx_packets; u64 tx_bytes; struct u64_stats_sync syncp; }; #endif #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0) /* Use compat version for all redhas releases */ #undef netdev_alloc_pcpu_stats #endif #ifndef netdev_alloc_pcpu_stats #define netdev_alloc_pcpu_stats(type) \ ({ \ typeof(type) __percpu *pcpu_stats = alloc_percpu(type); \ if (pcpu_stats) { \ int ____i; \ for_each_possible_cpu(____i) { \ typeof(type) *stat; \ stat = per_cpu_ptr(pcpu_stats, ____i); \ u64_stats_init(&stat->syncp); \ } \ } \ pcpu_stats; \ }) #endif #ifndef HAVE_DEV_RECURSION_LEVEL static inline bool dev_recursion_level(void) { return false; } #endif #ifndef NET_NAME_USER #define NET_NAME_USER 3 #endif #ifndef HAVE_GRO_REMCSUM struct gro_remcsum { }; #define skb_gro_remcsum_init(grc) #define skb_gro_remcsum_cleanup(a1, a2) #else #if LINUX_VERSION_CODE < KERNEL_VERSION(4,3,0) #define skb_gro_remcsum_process rpl_skb_gro_remcsum_process static inline void *skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, unsigned int off, size_t hdrlen, int start, int offset, struct gro_remcsum *grc, bool nopartial) { __wsum delta; size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); BUG_ON(!NAPI_GRO_CB(skb)->csum_valid); if (!nopartial) { NAPI_GRO_CB(skb)->gro_remcsum_start = off + hdrlen + start; return ptr; } ptr = skb_gro_header_fast(skb, off); if (skb_gro_header_hard(skb, off + plen)) { ptr = skb_gro_header_slow(skb, off + plen, off); if (!ptr) return NULL; } delta = remcsum_adjust(ptr + hdrlen, NAPI_GRO_CB(skb)->csum, start, offset); /* Adjust skb->csum since we changed the packet */ NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); grc->offset = off + hdrlen + offset; grc->delta = delta; return ptr; } #endif #endif #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) /* Only required on RHEL 6. */ #ifdef HAVE_DEV_GET_STATS64 #define dev_get_stats dev_get_stats64 #else #undef HAVE_RTNL_LINK_STATS64 #endif #endif #ifndef HAVE_RTNL_LINK_STATS64 #define dev_get_stats rpl_dev_get_stats struct rtnl_link_stats64 *rpl_dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage); #else #define HAVE_DEV_TSTATS #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7,0) #undef HAVE_DEV_TSTATS #endif #endif #ifndef netdev_dbg #define netdev_dbg(__dev, format, args...) \ do { \ printk(KERN_DEBUG "%s ", __dev->name); \ printk(KERN_DEBUG format, ##args); \ } while (0) #endif #ifndef netdev_info #define netdev_info(__dev, format, args...) \ do { \ printk(KERN_INFO "%s ", __dev->name); \ printk(KERN_INFO format, ##args); \ } while (0) #endif #endif /* __LINUX_NETDEVICE_WRAPPER_H */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/gso.h0000644000000000000000000000013213534540071021015 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 30 ctime=1567801425.425858173 openvswitch-2.5.9/datapath/linux/compat/gso.h0000644000175000017500000001150013534540071022500 0ustar00jpettitjpettit00000000000000#ifndef __LINUX_GSO_WRAPPER_H #define __LINUX_GSO_WRAPPER_H #include #include "datapath.h" typedef void (*gso_fix_segment_t)(struct sk_buff *); struct ovs_gso_cb { struct ovs_skb_cb dp_cb; #ifndef HAVE_METADATA_DST struct metadata_dst *tun_dst; #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) gso_fix_segment_t fix_segment; #endif #ifndef HAVE_INNER_PROTOCOL __be16 inner_protocol; #endif #ifndef HAVE_INNER_MAC_HEADER unsigned int inner_mac_header; #endif #ifndef HAVE_INNER_NETWORK_HEADER unsigned int inner_network_header; #endif }; #define OVS_GSO_CB(skb) ((struct ovs_gso_cb *)(skb)->cb) #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) #include #include #include static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb) { OVS_GSO_CB(skb)->fix_segment = NULL; } #else static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb) { } #endif #ifndef HAVE_INNER_MAC_HEADER static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) { return skb->head + OVS_GSO_CB(skb)->inner_mac_header; } static inline void skb_set_inner_mac_header(const struct sk_buff *skb, int offset) { OVS_GSO_CB(skb)->inner_mac_header = (skb->data - skb->head) + offset; } #endif /* HAVE_INNER_MAC_HEADER */ #ifndef HAVE_INNER_NETWORK_HEADER static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb) { return skb->head + OVS_GSO_CB(skb)->inner_network_header; } static inline int skb_inner_network_offset(const struct sk_buff *skb) { return skb_inner_network_header(skb) - skb->data; } /* We don't actually store the transport offset on backports because * we don't use it anywhere. Slightly rename this version to avoid * future users from picking it up accidentially. */ static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb) { return 0; } static inline void skb_set_inner_network_header(const struct sk_buff *skb, int offset) { OVS_GSO_CB(skb)->inner_network_header = (skb->data - skb->head) + offset; } static inline void skb_set_inner_transport_header(const struct sk_buff *skb, int offset) { } #else static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb) { return skb_inner_transport_header(skb) - skb->data; } #endif /* HAVE_INNER_NETWORK_HEADER */ #ifndef HAVE_INNER_PROTOCOL static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) { OVS_GSO_CB(skb)->inner_protocol = htons(0); } static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb, __be16 ethertype) { OVS_GSO_CB(skb)->inner_protocol = ethertype; } static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb) { return OVS_GSO_CB(skb)->inner_protocol; } #else static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) { /* Nothing to do. The inner_protocol is either zero or * has been set to a value by another user. * Either way it may be considered initialised. */ } static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb) { return skb->inner_protocol; } #ifdef ENCAP_TYPE_ETHER #define ovs_skb_set_inner_protocol skb_set_inner_protocol #else static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb, __be16 ethertype) { skb->inner_protocol = ethertype; } #endif /* ENCAP_TYPE_ETHER */ #endif /* 3.11 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) #define ip_local_out rpl_ip_local_out int rpl_ip_local_out(struct sk_buff *skb); static inline int skb_inner_mac_offset(const struct sk_buff *skb) { return skb_inner_mac_header(skb) - skb->data; } #define skb_reset_inner_headers rpl_skb_reset_inner_headers static inline void skb_reset_inner_headers(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct ovs_gso_cb) > FIELD_SIZEOF(struct sk_buff, cb)); skb_set_inner_mac_header(skb, skb_mac_header(skb) - skb->data); skb_set_inner_network_header(skb, skb_network_offset(skb)); skb_set_inner_transport_header(skb, skb_transport_offset(skb)); } #endif /* 3.18 */ #ifndef HAVE_METADATA_DST /* We need two separate functions to manage different dst in this case. * First is dst_entry and second is tunnel-dst. * So define ovs_* separate functions for tun_dst. */ static inline void ovs_skb_dst_set(struct sk_buff *skb, void *dst) { OVS_GSO_CB(skb)->tun_dst = (void *)dst; } static inline struct ip_tunnel_info *ovs_skb_tunnel_info(struct sk_buff *skb) { if (likely(OVS_GSO_CB(skb)->tun_dst)) return &OVS_GSO_CB(skb)->tun_dst->u.tun_info; else return NULL; } static inline void ovs_skb_dst_drop(struct sk_buff *skb) { OVS_GSO_CB(skb)->tun_dst = NULL; } static inline void ovs_dst_hold(void *dst) { } static inline void ovs_dst_release(struct dst_entry *dst) { kfree(dst); } #else #define ovs_skb_dst_set skb_dst_set #define ovs_skb_dst_drop skb_dst_drop #define ovs_dst_hold dst_hold #define ovs_dst_release dst_release #endif #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/nf_conntrack_reasm.c0000644000000000000000000000013113534540071024053 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.545859058 openvswitch-2.5.9/datapath/linux/compat/nf_conntrack_reasm.c0000644000175000017500000004016713534540071025552 0ustar00jpettitjpettit00000000000000/* * Backported from upstream commit 5b490047240f * ("ipv6: Export nf_ct_frag6_gather()") * * IPv6 fragment reassembly for connection tracking * * Copyright (C)2004 USAGI/WIDE Project * * Author: * Yasuyuki Kozakai @USAGI * * Based on: net/ipv6/reassembly.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #define pr_fmt(fmt) "IPv6-nf: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef OVS_NF_DEFRAG6_BACKPORT static const char nf_frags_cache_name[] = "ovs-frag6"; struct nf_ct_frag6_skb_cb { struct inet6_skb_parm h; int offset; struct sk_buff *orig; }; #define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb)) static struct inet_frags nf_frags; static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) { return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK); } static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr, const struct in6_addr *daddr) { net_get_random_once(&nf_frags.rnd, sizeof(nf_frags.rnd)); return jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), (__force u32)id, nf_frags.rnd); } #ifdef HAVE_INET_FRAGS_CONST static unsigned int nf_hashfn(const struct inet_frag_queue *q) #else static unsigned int nf_hashfn(struct inet_frag_queue *q) #endif { const struct frag_queue *nq; nq = container_of(q, struct frag_queue, q); return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); } static void nf_skb_free(struct sk_buff *skb) { if (NFCT_FRAG6_CB(skb)->orig) kfree_skb(NFCT_FRAG6_CB(skb)->orig); } static void nf_ct_frag6_expire(unsigned long data) { struct frag_queue *fq; struct net *net; fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); net = container_of(fq->q.net, struct net, nf_frag.frags); ip6_expire_frag_queue(net, fq, &nf_frags); } /* Creation primitives. */ static inline struct frag_queue *fq_find(struct net *net, __be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst, u8 ecn) { struct inet_frag_queue *q; struct ip6_create_arg arg; unsigned int hash; arg.id = id; arg.user = user; arg.src = src; arg.dst = dst; arg.ecn = ecn; local_bh_disable(); hash = nf_hash_frag(id, src, dst); q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash); local_bh_enable(); if (IS_ERR_OR_NULL(q)) { inet_frag_maybe_warn_overflow(q, pr_fmt()); return NULL; } return container_of(q, struct frag_queue, q); } static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb, const struct frag_hdr *fhdr, int nhoff) { struct sk_buff *prev, *next; unsigned int payload_len; int offset, end; u8 ecn; if (qp_flags(fq) & INET_FRAG_COMPLETE) { pr_debug("Already completed\n"); goto err; } payload_len = ntohs(ipv6_hdr(skb)->payload_len); offset = ntohs(fhdr->frag_off) & ~0x7; end = offset + (payload_len - ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); if ((unsigned int)end > IPV6_MAXPLEN) { pr_debug("offset is too large.\n"); return -1; } ecn = ip6_frag_ecn(ipv6_hdr(skb)); if (skb->ip_summed == CHECKSUM_COMPLETE) { const unsigned char *nh = skb_network_header(skb); skb->csum = csum_sub(skb->csum, csum_partial(nh, (u8 *)(fhdr + 1) - nh, 0)); } /* Is this the final fragment? */ if (!(fhdr->frag_off & htons(IP6_MF))) { /* If we already have some bits beyond end * or have different end, the segment is corrupted. */ if (end < fq->q.len || ((qp_flags(fq) & INET_FRAG_LAST_IN) && end != fq->q.len)) { pr_debug("already received last fragment\n"); goto err; } qp_flags(fq) |= INET_FRAG_LAST_IN; fq->q.len = end; } else { /* Check if the fragment is rounded to 8 bytes. * Required by the RFC. */ if (end & 0x7) { /* RFC2460 says always send parameter problem in * this case. -DaveM */ pr_debug("end of fragment not rounded to 8 bytes.\n"); return -1; } if (end > fq->q.len) { /* Some bits beyond end -> corruption. */ if (qp_flags(fq) & INET_FRAG_LAST_IN) { pr_debug("last packet already reached.\n"); goto err; } fq->q.len = end; } } if (end == offset) goto err; /* Point into the IP datagram 'data' part. */ if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) { pr_debug("queue: message is too short.\n"); goto err; } if (pskb_trim_rcsum(skb, end - offset)) { pr_debug("Can't trim\n"); goto err; } /* Find out which fragments are in front and at the back of us * in the chain of fragments so far. We must know where to put * this fragment, right? */ prev = fq->q.fragments_tail; if (!prev || NFCT_FRAG6_CB(prev)->offset < offset) { next = NULL; goto found; } prev = NULL; for (next = fq->q.fragments; next != NULL; next = next->next) { if (NFCT_FRAG6_CB(next)->offset >= offset) break; /* bingo! */ prev = next; } found: /* RFC5722, Section 4: * When reassembling an IPv6 datagram, if * one or more its constituent fragments is determined to be an * overlapping fragment, the entire datagram (and any constituent * fragments, including those not yet received) MUST be silently * discarded. */ /* Check for overlap with preceding fragment. */ if (prev && (NFCT_FRAG6_CB(prev)->offset + prev->len) > offset) goto discard_fq; /* Look for overlap with succeeding segment. */ if (next && NFCT_FRAG6_CB(next)->offset < end) goto discard_fq; NFCT_FRAG6_CB(skb)->offset = offset; /* Insert this fragment in the chain of fragments. */ skb->next = next; if (!next) fq->q.fragments_tail = skb; if (prev) prev->next = skb; else fq->q.fragments = skb; if (skb->dev) { fq->iif = skb->dev->ifindex; skb->dev = NULL; } fq->q.stamp = skb->tstamp; fq->q.meat += skb->len; fq->ecn |= ecn; if (payload_len > fq->q.max_size) fq->q.max_size = payload_len; add_frag_mem_limit(fq->q.net, skb->truesize); /* The first fragment. * nhoffset is obtained from the first fragment, of course. */ if (offset == 0) { fq->nhoffset = nhoff; qp_flags(fq) |= INET_FRAG_FIRST_IN; } return 0; discard_fq: inet_frag_kill(&fq->q, &nf_frags); err: return -1; } /* * Check if this packet is complete. * Returns NULL on failure by any reason, and pointer * to current nexthdr field in reassembled frame. * * It is called with locked fq, and caller must check that * queue is eligible for reassembly i.e. it is not COMPLETE, * the last and the first frames arrived and all the bits are here. */ static struct sk_buff * nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev) { struct sk_buff *fp, *op, *head = fq->q.fragments; int payload_len; u8 ecn; inet_frag_kill(&fq->q, &nf_frags); WARN_ON(head == NULL); WARN_ON(NFCT_FRAG6_CB(head)->offset != 0); ecn = ip_frag_ecn_table[fq->ecn]; if (unlikely(ecn == 0xff)) goto out_fail; /* Unfragmented part is taken from the first segment. */ payload_len = ((head->data - skb_network_header(head)) - sizeof(struct ipv6hdr) + fq->q.len - sizeof(struct frag_hdr)); if (payload_len > IPV6_MAXPLEN) { pr_debug("payload len is too large.\n"); goto out_oversize; } /* Head of list must not be cloned. */ if (skb_unclone(head, GFP_ATOMIC)) { pr_debug("skb is cloned but can't expand head"); goto out_oom; } /* If the first fragment is fragmented itself, we split * it to two chunks: the first with data and paged part * and the second, holding only fragments. */ if (skb_has_frag_list(head)) { struct sk_buff *clone; int i, plen = 0; clone = alloc_skb(0, GFP_ATOMIC); if (clone == NULL) goto out_oom; clone->next = head->next; head->next = clone; skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; skb_frag_list_init(head); for (i = 0; i < skb_shinfo(head)->nr_frags; i++) plen += skb_frag_size(&skb_shinfo(head)->frags[i]); clone->len = clone->data_len = head->data_len - plen; head->data_len -= clone->len; head->len -= clone->len; clone->csum = 0; clone->ip_summed = head->ip_summed; NFCT_FRAG6_CB(clone)->orig = NULL; add_frag_mem_limit(fq->q.net, clone->truesize); } /* We have to remove fragment header from datagram and to relocate * header in order to calculate ICV correctly. */ skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0]; memmove(head->head + sizeof(struct frag_hdr), head->head, (head->data - head->head) - sizeof(struct frag_hdr)); head->mac_header += sizeof(struct frag_hdr); head->network_header += sizeof(struct frag_hdr); skb_shinfo(head)->frag_list = head->next; skb_reset_transport_header(head); skb_push(head, head->data - skb_network_header(head)); for (fp=head->next; fp; fp = fp->next) { head->data_len += fp->len; head->len += fp->len; if (head->ip_summed != fp->ip_summed) head->ip_summed = CHECKSUM_NONE; else if (head->ip_summed == CHECKSUM_COMPLETE) head->csum = csum_add(head->csum, fp->csum); head->truesize += fp->truesize; } sub_frag_mem_limit(fq->q.net, head->truesize); head->ignore_df = 1; head->next = NULL; head->dev = dev; head->tstamp = fq->q.stamp; ipv6_hdr(head)->payload_len = htons(payload_len); ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size; /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) head->csum = csum_partial(skb_network_header(head), skb_network_header_len(head), head->csum); fq->q.fragments = NULL; fq->q.fragments_tail = NULL; /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */ fp = skb_shinfo(head)->frag_list; if (fp && NFCT_FRAG6_CB(fp)->orig == NULL) /* at above code, head skb is divided into two skbs. */ fp = fp->next; op = NFCT_FRAG6_CB(head)->orig; for (; fp; fp = fp->next) { struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig; op->next = orig; op = orig; NFCT_FRAG6_CB(fp)->orig = NULL; } return head; out_oversize: net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n", payload_len); goto out_fail; out_oom: net_dbg_ratelimited("nf_ct_frag6_reasm: no memory for reassembly\n"); out_fail: return NULL; } /* * find the header just before Fragment Header. * * if success return 0 and set ... * (*prevhdrp): the value of "Next Header Field" in the header * just before Fragment Header. * (*prevhoff): the offset of "Next Header Field" in the header * just before Fragment Header. * (*fhoff) : the offset of Fragment Header. * * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c * */ static int find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff) { u8 nexthdr = ipv6_hdr(skb)->nexthdr; const int netoff = skb_network_offset(skb); u8 prev_nhoff = netoff + offsetof(struct ipv6hdr, nexthdr); int start = netoff + sizeof(struct ipv6hdr); int len = skb->len - start; u8 prevhdr = NEXTHDR_IPV6; while (nexthdr != NEXTHDR_FRAGMENT) { struct ipv6_opt_hdr hdr; int hdrlen; if (!ipv6_ext_hdr(nexthdr)) { return -1; } if (nexthdr == NEXTHDR_NONE) { pr_debug("next header is none\n"); return -1; } if (len < (int)sizeof(struct ipv6_opt_hdr)) { pr_debug("too short\n"); return -1; } if (skb_copy_bits(skb, start, &hdr, sizeof(hdr))) BUG(); if (nexthdr == NEXTHDR_AUTH) hdrlen = (hdr.hdrlen+2)<<2; else hdrlen = ipv6_optlen(&hdr); prevhdr = nexthdr; prev_nhoff = start; nexthdr = hdr.nexthdr; len -= hdrlen; start += hdrlen; } if (len < 0) return -1; *prevhdrp = prevhdr; *prevhoff = prev_nhoff; *fhoff = start; return 0; } struct sk_buff *rpl_nf_ct_frag6_gather(struct sk_buff *skb, u32 user) { struct sk_buff *clone; struct net_device *dev = skb->dev; struct net *net = skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev); struct frag_hdr *fhdr; struct frag_queue *fq; struct ipv6hdr *hdr; int fhoff, nhoff; u8 prevhdr; struct sk_buff *ret_skb = NULL; /* Jumbo payload inhibits frag. header */ if (ipv6_hdr(skb)->payload_len == 0) { pr_debug("payload len = 0\n"); return skb; } if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0) return skb; clone = skb_clone(skb, GFP_ATOMIC); if (clone == NULL) { pr_debug("Can't clone skb\n"); return skb; } NFCT_FRAG6_CB(clone)->orig = skb; if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) { pr_debug("message is too short.\n"); goto ret_orig; } skb_set_transport_header(clone, fhoff); hdr = ipv6_hdr(clone); fhdr = (struct frag_hdr *)skb_transport_header(clone); fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr, ip6_frag_ecn(hdr)); if (fq == NULL) { pr_debug("Can't find and can't create new queue\n"); goto ret_orig; } spin_lock_bh(&fq->q.lock); if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { spin_unlock_bh(&fq->q.lock); pr_debug("Can't insert skb to queue\n"); inet_frag_put(&fq->q, &nf_frags); goto ret_orig; } if (qp_flags(fq) == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && fq->q.meat == fq->q.len) { ret_skb = nf_ct_frag6_reasm(fq, dev); if (ret_skb == NULL) pr_debug("Can't reassemble fragmented packets\n"); } spin_unlock_bh(&fq->q.lock); inet_frag_put(&fq->q, &nf_frags); return ret_skb; ret_orig: kfree_skb(clone); return skb; } EXPORT_SYMBOL_GPL(rpl_nf_ct_frag6_gather); #ifdef HAVE_INET_FRAGS_CONST static void rpl_ip6_frag_init(struct inet_frag_queue *q, const void *a) #else static void rpl_ip6_frag_init(struct inet_frag_queue *q, void *a) #endif { struct frag_queue *fq = container_of(q, struct frag_queue, q); const struct ip6_create_arg *arg = a; fq->id = arg->id; fq->user = arg->user; fq->saddr = *arg->src; fq->daddr = *arg->dst; fq->ecn = arg->ecn; } #ifdef HAVE_INET_FRAGS_CONST static bool rpl_ip6_frag_match(const struct inet_frag_queue *q, const void *a) #else static bool rpl_ip6_frag_match(struct inet_frag_queue *q, void *a) #endif { const struct frag_queue *fq; const struct ip6_create_arg *arg = a; fq = container_of(q, struct frag_queue, q); return fq->id == arg->id && fq->user == arg->user && ipv6_addr_equal(&fq->saddr, arg->src) && ipv6_addr_equal(&fq->daddr, arg->dst); } void nf_ct_frag6_consume_orig(struct sk_buff *skb) { struct sk_buff *s, *s2; for (s = NFCT_FRAG6_CB(skb)->orig; s;) { s2 = s->next; s->next = NULL; consume_skb(s); s = s2; } } static int nf_ct_net_init(struct net *net) { nf_defrag_ipv6_enable(); return 0; } static void nf_ct_net_exit(struct net *net) { inet_frags_exit_net(&net->ipv6.frags, &nf_frags); } static struct pernet_operations nf_ct_net_ops = { .init = nf_ct_net_init, .exit = nf_ct_net_exit, }; int rpl_nf_ct_frag6_init(void) { int ret = 0; nf_frags.hashfn = nf_hashfn; nf_frags.constructor = rpl_ip6_frag_init; nf_frags.destructor = NULL; nf_frags.skb_free = nf_skb_free; nf_frags.qsize = sizeof(struct frag_queue); nf_frags.match = rpl_ip6_frag_match; nf_frags.frag_expire = nf_ct_frag6_expire; #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK nf_frags.frags_cache_name = nf_frags_cache_name; #endif #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0) nf_frags.secret_interval = 10 * 60 * HZ; #endif ret = inet_frags_init(&nf_frags); if (ret) goto out; ret = register_pernet_subsys(&nf_ct_net_ops); if (ret) inet_frags_fini(&nf_frags); out: return ret; } void rpl_nf_ct_frag6_cleanup(void) { unregister_pernet_subsys(&nf_ct_net_ops); inet_frags_fini(&nf_frags); } #endif /* OVS_NF_DEFRAG6_BACKPORT */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/udp_tunnel.c0000644000000000000000000000013113534540071022374 xustar0029 mtime=1567801401.27768026 30 atime=1567801402.057685987 30 ctime=1567801425.553859118 openvswitch-2.5.9/datapath/linux/compat/udp_tunnel.c0000644000175000017500000001473313534540071024073 0ustar00jpettitjpettit00000000000000#include #ifndef HAVE_METADATA_DST #include #include #include #include #include #include #include #include #include #include #include #include int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg, struct socket **sockp) { int err; struct socket *sock = NULL; #if IS_ENABLED(CONFIG_IPV6) if (cfg->family == AF_INET6) { struct sockaddr_in6 udp6_addr; err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock); if (err < 0) goto error; udp6_addr.sin6_family = AF_INET6; memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6, sizeof(udp6_addr.sin6_addr)); udp6_addr.sin6_port = cfg->local_udp_port; err = kernel_bind(sock, (struct sockaddr *)&udp6_addr, sizeof(udp6_addr)); if (err < 0) goto error; if (cfg->peer_udp_port) { udp6_addr.sin6_family = AF_INET6; memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6, sizeof(udp6_addr.sin6_addr)); udp6_addr.sin6_port = cfg->peer_udp_port; err = kernel_connect(sock, (struct sockaddr *)&udp6_addr, sizeof(udp6_addr), 0); } if (err < 0) goto error; } else #endif if (cfg->family == AF_INET) { struct sockaddr_in udp_addr; err = sock_create_kern(net, AF_INET, SOCK_DGRAM, 0, &sock); if (err < 0) goto error; udp_addr.sin_family = AF_INET; udp_addr.sin_addr = cfg->local_ip; udp_addr.sin_port = cfg->local_udp_port; err = kernel_bind(sock, (struct sockaddr *)&udp_addr, sizeof(udp_addr)); if (err < 0) goto error; if (cfg->peer_udp_port) { udp_addr.sin_family = AF_INET; udp_addr.sin_addr = cfg->peer_ip; udp_addr.sin_port = cfg->peer_udp_port; err = kernel_connect(sock, (struct sockaddr *)&udp_addr, sizeof(udp_addr), 0); if (err < 0) goto error; } } else { return -EPFNOSUPPORT; } *sockp = sock; return 0; error: if (sock) { kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); } *sockp = NULL; return err; } EXPORT_SYMBOL_GPL(rpl_udp_sock_create); void rpl_setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *cfg) { struct sock *sk = sock->sk; /* Disable multicast loopback */ inet_sk(sk)->mc_loop = 0; rcu_assign_sk_user_data(sk, cfg->sk_user_data); udp_sk(sk)->encap_type = cfg->encap_type; udp_sk(sk)->encap_rcv = cfg->encap_rcv; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) udp_sk(sk)->encap_destroy = cfg->encap_destroy; #endif udp_tunnel_encap_enable(sock); } EXPORT_SYMBOL_GPL(rpl_setup_udp_tunnel_sock); void ovs_udp_gso(struct sk_buff *skb) { int udp_offset = skb_transport_offset(skb); struct udphdr *uh; uh = udp_hdr(skb); uh->len = htons(skb->len - udp_offset); } EXPORT_SYMBOL_GPL(ovs_udp_gso); void ovs_udp_csum_gso(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); int udp_offset = skb_transport_offset(skb); ovs_udp_gso(skb); /* csum segment if tunnel sets skb with csum. The cleanest way * to do this just to set it up from scratch. */ skb->ip_summed = CHECKSUM_NONE; udp_set_csum(false, skb, iph->saddr, iph->daddr, skb->len - udp_offset); } EXPORT_SYMBOL_GPL(ovs_udp_csum_gso); int rpl_udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, bool xnet, bool nocheck) { struct udphdr *uh; __skb_push(skb, sizeof(*uh)); skb_reset_transport_header(skb); uh = udp_hdr(skb); uh->dest = dst_port; uh->source = src_port; uh->len = htons(skb->len); udp_set_csum(nocheck, skb, src, dst, skb->len); return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, xnet); } EXPORT_SYMBOL_GPL(rpl_udp_tunnel_xmit_skb); void rpl_udp_tunnel_sock_release(struct socket *sock) { rcu_assign_sk_user_data(sock->sk, NULL); kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); } EXPORT_SYMBOL_GPL(rpl_udp_tunnel_sock_release); #if IS_ENABLED(CONFIG_IPV6) #define udp_v6_check rpl_udp_v6_check static __sum16 udp_v6_check(int len, const struct in6_addr *saddr, const struct in6_addr *daddr, __wsum base) { return csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, base); } #define udp6_set_csum rpl_udp6_set_csum static void udp6_set_csum(bool nocheck, struct sk_buff *skb, const struct in6_addr *saddr, const struct in6_addr *daddr, int len) { struct udphdr *uh = udp_hdr(skb); if (nocheck) uh->check = 0; else if (skb_is_gso(skb)) uh->check = ~udp_v6_check(len, saddr, daddr, 0); else if (skb_dst(skb) && skb_dst(skb)->dev && (skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL); skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); uh->check = ~udp_v6_check(len, saddr, daddr, 0); } else { __wsum csum; BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL); uh->check = 0; csum = skb_checksum(skb, 0, len, 0); uh->check = udp_v6_check(len, saddr, daddr, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; skb->ip_summed = CHECKSUM_UNNECESSARY; } } #define ip6_flow_hdr rpl_ip6_flow_hdr static inline void ip6_flow_hdr(struct ipv6hdr *hdr, unsigned int tclass, __be32 flowlabel) { *(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | flowlabel; } int rpl_udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port, bool nocheck) { struct udphdr *uh; struct ipv6hdr *ip6h; __skb_push(skb, sizeof(*uh)); skb_reset_transport_header(skb); uh = udp_hdr(skb); uh->dest = dst_port; uh->source = src_port; uh->len = htons(skb->len); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); skb_dst_set(skb, dst); udp6_set_csum(nocheck, skb, saddr, daddr, skb->len); __skb_push(skb, sizeof(*ip6h)); skb_reset_network_header(skb); ip6h = ipv6_hdr(skb); ip6_flow_hdr(ip6h, prio, htonl(0)); ip6h->payload_len = htons(skb->len); ip6h->nexthdr = IPPROTO_UDP; ip6h->hop_limit = ttl; ip6h->daddr = *daddr; ip6h->saddr = *saddr; ip6tunnel_xmit(sk, skb, dev); return 0; } #endif #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/utils.c0000644000000000000000000000013113534540071021357 xustar0029 mtime=1567801401.27768026 30 atime=1567801402.057685987 30 ctime=1567801425.557859146 openvswitch-2.5.9/datapath/linux/compat/utils.c0000644000175000017500000000312113534540071023043 0ustar00jpettitjpettit00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) void rpl_inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, const __be32 *from, const __be32 *to, int pseudohdr) { __be32 diff[] = { ~from[0], ~from[1], ~from[2], ~from[3], to[0], to[1], to[2], to[3], }; if (skb->ip_summed != CHECKSUM_PARTIAL) { *sum = csum_fold(csum_partial(diff, sizeof(diff), ~csum_unfold(*sum))); if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) skb->csum = ~csum_partial(diff, sizeof(diff), ~skb->csum); } else if (pseudohdr) *sum = ~csum_fold(csum_partial(diff, sizeof(diff), csum_unfold(*sum))); } EXPORT_SYMBOL_GPL(rpl_inet_proto_csum_replace16); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0) bool rpl___net_get_random_once(void *buf, int nbytes, bool *done, atomic_t *done_key) { static DEFINE_SPINLOCK(lock); unsigned long flags; spin_lock_irqsave(&lock, flags); if (*done) { spin_unlock_irqrestore(&lock, flags); return false; } get_random_bytes(buf, nbytes); *done = true; spin_unlock_irqrestore(&lock, flags); atomic_set(done_key, 1); return true; } EXPORT_SYMBOL_GPL(rpl___net_get_random_once); #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip_gre.c0000644000000000000000000000013113534540071021464 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.537858999 openvswitch-2.5.9/datapath/linux/compat/ip_gre.c0000644000175000017500000004032313534540071023155 0ustar00jpettitjpettit00000000000000/* * Linux NET3: GRE over IP protocol decoder. * * Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_METADATA_DST #if IS_ENABLED(CONFIG_IPV6) #include #include #include #endif #include "gso.h" #include "vport-netdev.h" static int gre_tap_net_id __read_mostly; #define ip_gre_calc_hlen rpl_ip_gre_calc_hlen static int ip_gre_calc_hlen(__be16 o_flags) { int addend = 4; if (o_flags & TUNNEL_CSUM) addend += 4; if (o_flags & TUNNEL_KEY) addend += 4; if (o_flags & TUNNEL_SEQ) addend += 4; return addend; } #define tnl_flags_to_gre_flags rpl_tnl_flags_to_gre_flags static __be16 tnl_flags_to_gre_flags(__be16 tflags) { __be16 flags = 0; if (tflags & TUNNEL_CSUM) flags |= GRE_CSUM; if (tflags & TUNNEL_ROUTING) flags |= GRE_ROUTING; if (tflags & TUNNEL_KEY) flags |= GRE_KEY; if (tflags & TUNNEL_SEQ) flags |= GRE_SEQ; if (tflags & TUNNEL_STRICT) flags |= GRE_STRICT; if (tflags & TUNNEL_REC) flags |= GRE_REC; if (tflags & TUNNEL_VERSION) flags |= GRE_VERSION; return flags; } static __be64 key_to_tunnel_id(__be32 key) { #ifdef __BIG_ENDIAN return (__force __be64)((__force u32)key); #else return (__force __be64)((__force u64)key << 32); #endif } /* Returns the least-significant 32 bits of a __be64. */ static __be32 tunnel_id_to_key(__be64 x) { #ifdef __BIG_ENDIAN return (__force __be32)x; #else return (__force __be32)((__force u64)x >> 32); #endif } static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi) { struct net *net = dev_net(skb->dev); struct metadata_dst tun_dst; struct ip_tunnel_net *itn; const struct iphdr *iph; struct ip_tunnel *tunnel; if (tpi->proto != htons(ETH_P_TEB)) return PACKET_REJECT; itn = net_generic(net, gre_tap_net_id); iph = ip_hdr(skb); tunnel = rcu_dereference(itn->collect_md_tun); if (tunnel) { __be16 flags; __be64 tun_id; int err; skb_pop_mac_header(skb); flags = tpi->flags & (TUNNEL_CSUM | TUNNEL_KEY); tun_id = key_to_tunnel_id(tpi->key); ovs_ip_tun_rx_dst(&tun_dst.u.tun_info, skb, flags, tun_id, 0); skb_reset_network_header(skb); err = IP_ECN_decapsulate(iph, skb); if (unlikely(err)) { if (err > 1) { ++tunnel->dev->stats.rx_frame_errors; ++tunnel->dev->stats.rx_errors; return PACKET_REJECT; } } ovs_ip_tunnel_rcv(tunnel->dev, skb, &tun_dst); return PACKET_RCVD; } return PACKET_REJECT; } static int gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi) { if (ipgre_rcv(skb, tpi) == PACKET_RCVD) return 0; kfree_skb(skb); return 0; } #ifndef HAVE_GRE_HANDLE_OFFLOADS static void gre_nop_fix(struct sk_buff *skb) { } static void gre_csum_fix(struct sk_buff *skb) { struct gre_base_hdr *greh; __be32 *options; int gre_offset = skb_transport_offset(skb); greh = (struct gre_base_hdr *)skb_transport_header(skb); options = ((__be32 *)greh + 1); *options = 0; *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset, skb->len - gre_offset, 0)); } static bool is_gre_gso(struct sk_buff *skb) { return skb_is_gso(skb); } static struct sk_buff *rpl_gre_handle_offloads(struct sk_buff *skb, bool gre_csum) { int type = gre_csum ? SKB_GSO_GRE_CSUM : SKB_GSO_GRE; gso_fix_segment_t fix_segment; if (gre_csum) fix_segment = gre_csum_fix; else fix_segment = gre_nop_fix; return ovs_iptunnel_handle_offloads(skb, gre_csum, type, fix_segment); } #else static bool is_gre_gso(struct sk_buff *skb) { return skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | SKB_GSO_GRE_CSUM); } static struct sk_buff *rpl_gre_handle_offloads(struct sk_buff *skb, bool gre_csum) { if (skb_is_gso(skb) && skb_is_encapsulated(skb)) { kfree_skb(skb); return ERR_PTR(-ENOSYS); } #undef gre_handle_offloads return gre_handle_offloads(skb, gre_csum); } #endif static void build_header(struct sk_buff *skb, int hdr_len, __be16 flags, __be16 proto, __be32 key, __be32 seq) { struct gre_base_hdr *greh; skb_push(skb, hdr_len); skb_reset_transport_header(skb); greh = (struct gre_base_hdr *)skb->data; greh->flags = tnl_flags_to_gre_flags(flags); greh->protocol = proto; if (flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) { __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); if (flags & TUNNEL_SEQ) { *ptr = seq; ptr--; } if (flags & TUNNEL_KEY) { *ptr = key; ptr--; } if (flags & TUNNEL_CSUM && !is_gre_gso(skb)) { *ptr = 0; *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0, skb->len, 0)); } } ovs_skb_set_inner_protocol(skb, proto); } netdev_tx_t rpl_gre_fb_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct net *net = dev_net(dev); struct ip_tunnel_info *tun_info; const struct ip_tunnel_key *key; struct flowi4 fl; struct rtable *rt; int min_headroom; int tunnel_hlen; __be16 df, flags; int err; tun_info = skb_tunnel_info(skb); if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || ip_tunnel_info_af(tun_info) != AF_INET)) goto err_free_skb; key = &tun_info->key; memset(&fl, 0, sizeof(fl)); fl.daddr = key->u.ipv4.dst; fl.saddr = key->u.ipv4.src; fl.flowi4_tos = RT_TOS(key->tos); fl.flowi4_mark = skb->mark; fl.flowi4_proto = IPPROTO_GRE; rt = ip_route_output_key(net, &fl); if (IS_ERR(rt)) goto err_free_skb; tunnel_hlen = ip_gre_calc_hlen(key->tun_flags); min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + tunnel_hlen + sizeof(struct iphdr) + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { int head_delta = SKB_DATA_ALIGN(min_headroom - skb_headroom(skb) + 16); err = pskb_expand_head(skb, max_t(int, head_delta, 0), 0, GFP_ATOMIC); if (unlikely(err)) goto err_free_rt; } skb = vlan_hwaccel_push_inside(skb); if (unlikely(!skb)) { err = -ENOMEM; goto err_free_rt; } /* Push Tunnel header. */ skb = rpl_gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM)); if (IS_ERR(skb)) { skb = NULL; goto err_free_rt; } flags = tun_info->key.tun_flags & (TUNNEL_CSUM | TUNNEL_KEY); build_header(skb, tunnel_hlen, flags, htons(ETH_P_TEB), tunnel_id_to_key(tun_info->key.tun_id), 0); df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; err = iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE, key->tos, key->ttl, df, false); iptunnel_xmit_stats(err, &dev->stats, (struct pcpu_sw_netstats __percpu *)dev->tstats); return NETDEV_TX_OK; err_free_rt: ip_rt_put(rt); err_free_skb: kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } EXPORT_SYMBOL(rpl_gre_fb_xmit); #define GRE_FEATURES (NETIF_F_SG | \ NETIF_F_FRAGLIST | \ NETIF_F_HIGHDMA | \ NETIF_F_HW_CSUM | \ NETIF_F_NETNS_LOCAL) static void __gre_tunnel_init(struct net_device *dev) { struct ip_tunnel *tunnel; int t_hlen; tunnel = netdev_priv(dev); tunnel->parms.iph.protocol = IPPROTO_GRE; tunnel->tun_hlen = ip_gre_calc_hlen(tunnel->parms.o_flags); tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen; t_hlen = tunnel->hlen + sizeof(struct iphdr); dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4; dev->mtu = ETH_DATA_LEN - t_hlen - 4; dev->features |= GRE_FEATURES; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) dev->hw_features |= GRE_FEATURES; #endif if (!(tunnel->parms.o_flags & TUNNEL_SEQ)) { /* TCP offload with GRE SEQ is not supported. */ dev->features |= NETIF_F_GSO_SOFTWARE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) dev->hw_features |= NETIF_F_GSO_SOFTWARE; #endif /* Can use a lockless transmit, unless we generate * output sequences */ dev->features |= NETIF_F_LLTX; } } /* Called with rcu_read_lock and BH disabled. */ static int gre_err(struct sk_buff *skb, u32 info, const struct tnl_ptk_info *tpi) { return PACKET_REJECT; } static struct gre_cisco_protocol ipgre_protocol = { .handler = gre_rcv, .err_handler = gre_err, .priority = 1, }; static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) { __be16 flags; if (!data) return 0; flags = 0; if (data[IFLA_GRE_IFLAGS]) flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); if (data[IFLA_GRE_OFLAGS]) flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); if (flags & (GRE_VERSION|GRE_ROUTING)) return -EINVAL; return 0; } static int ipgre_tap_validate(struct nlattr *tb[], struct nlattr *data[]) { __be32 daddr; if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } if (!data) goto out; if (data[IFLA_GRE_REMOTE]) { memcpy(&daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); if (!daddr) return -EINVAL; } out: return ipgre_tunnel_validate(tb, data); } static void ipgre_netlink_parms(struct net_device *dev, struct nlattr *data[], struct nlattr *tb[], struct ip_tunnel_parm *parms) { memset(parms, 0, sizeof(*parms)); parms->iph.protocol = IPPROTO_GRE; } static int gre_tap_init(struct net_device *dev) { __gre_tunnel_init(dev); dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; return ip_tunnel_init(dev); } static netdev_tx_t gre_dev_xmit(struct sk_buff *skb, struct net_device *dev) { /* Drop All packets coming from networking stack. OVS-CB is * not initialized for these packets. */ dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } static const struct net_device_ops gre_tap_netdev_ops = { .ndo_init = gre_tap_init, .ndo_uninit = ip_tunnel_uninit, .ndo_start_xmit = gre_dev_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = ip_tunnel_change_mtu, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) .ndo_get_stats64 = ip_tunnel_get_stats64, #endif #ifdef HAVE_NDO_GET_IFLINK .ndo_get_iflink = ip_tunnel_get_iflink, #endif }; static void ipgre_tap_setup(struct net_device *dev) { ether_setup(dev); dev->netdev_ops = &gre_tap_netdev_ops; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ip_tunnel_setup(dev, gre_tap_net_id); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) #else static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) #endif { struct ip_tunnel_parm p; int err; ipgre_netlink_parms(dev, data, tb, &p); err = ip_tunnel_newlink(dev, tb, &p); return err; } static size_t ipgre_get_size(const struct net_device *dev) { return /* IFLA_GRE_LINK */ nla_total_size(4) + /* IFLA_GRE_IFLAGS */ nla_total_size(2) + /* IFLA_GRE_OFLAGS */ nla_total_size(2) + /* IFLA_GRE_IKEY */ nla_total_size(4) + /* IFLA_GRE_OKEY */ nla_total_size(4) + /* IFLA_GRE_LOCAL */ nla_total_size(4) + /* IFLA_GRE_REMOTE */ nla_total_size(4) + /* IFLA_GRE_TTL */ nla_total_size(1) + /* IFLA_GRE_TOS */ nla_total_size(1) + /* IFLA_GRE_PMTUDISC */ nla_total_size(1) + /* IFLA_GRE_ENCAP_TYPE */ nla_total_size(2) + /* IFLA_GRE_ENCAP_FLAGS */ nla_total_size(2) + /* IFLA_GRE_ENCAP_SPORT */ nla_total_size(2) + /* IFLA_GRE_ENCAP_DPORT */ nla_total_size(2) + /* IFLA_GRE_COLLECT_METADATA */ nla_total_size(0) + 0; } static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel_parm *p = &t->parms; if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) || nla_put_be16(skb, IFLA_GRE_IFLAGS, tnl_flags_to_gre_flags(p->i_flags)) || nla_put_be16(skb, IFLA_GRE_OFLAGS, tnl_flags_to_gre_flags(p->o_flags)) || nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) || nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) || nla_put_in_addr(skb, IFLA_GRE_LOCAL, p->iph.saddr) || nla_put_in_addr(skb, IFLA_GRE_REMOTE, p->iph.daddr) || nla_put_u8(skb, IFLA_GRE_TTL, p->iph.ttl) || nla_put_u8(skb, IFLA_GRE_TOS, p->iph.tos) || nla_put_u8(skb, IFLA_GRE_PMTUDISC, !!(p->iph.frag_off & htons(IP_DF)))) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_LINK] = { .type = NLA_U32 }, [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_IKEY] = { .type = NLA_U32 }, [IFLA_GRE_OKEY] = { .type = NLA_U32 }, [IFLA_GRE_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, [IFLA_GRE_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, [IFLA_GRE_TTL] = { .type = NLA_U8 }, [IFLA_GRE_TOS] = { .type = NLA_U8 }, [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, }; static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { .kind = "ovs_gretap", .maxtype = IFLA_GRE_MAX, .policy = ipgre_policy, .priv_size = sizeof(struct ip_tunnel), .setup = ipgre_tap_setup, .validate = ipgre_tap_validate, .newlink = ipgre_newlink, .dellink = ip_tunnel_dellink, .get_size = ipgre_get_size, .fill_info = ipgre_fill_info, #ifdef HAVE_GET_LINK_NET .get_link_net = ip_tunnel_get_link_net, #endif }; struct net_device *rpl_gretap_fb_dev_create(struct net *net, const char *name, u8 name_assign_type) { struct nlattr *tb[IFLA_MAX + 1]; struct net_device *dev; struct ip_tunnel *t; int err; memset(&tb, 0, sizeof(tb)); dev = rtnl_create_link(net, (char *)name, name_assign_type, &ipgre_tap_ops, tb); if (IS_ERR(dev)) return dev; t = netdev_priv(dev); t->collect_md = true; /* Configure flow based GRE device. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) err = ipgre_newlink(net, dev, tb, NULL); #else err = ipgre_newlink(dev, tb, NULL); #endif if (err < 0) goto out; /* openvswitch users expect packet sizes to be unrestricted, * so set the largest MTU we can. */ err = __ip_tunnel_change_mtu(dev, IP_MAX_MTU, false); if (err) goto out; return dev; out: free_netdev(dev); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(rpl_gretap_fb_dev_create); static int __net_init ipgre_tap_init_net(struct net *net) { return ip_tunnel_init_net(net, gre_tap_net_id, &ipgre_tap_ops, "gretap0"); } static void __net_exit ipgre_tap_exit_net(struct net *net) { struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id); ip_tunnel_delete_net(itn, &ipgre_tap_ops); } static struct pernet_operations ipgre_tap_net_ops = { .init = ipgre_tap_init_net, .exit = ipgre_tap_exit_net, .id = &gre_tap_net_id, .size = sizeof(struct ip_tunnel_net), }; DEFINE_COMPAT_PNET_REG_FUNC(device); int rpl_ipgre_init(void) { int err; err = register_pernet_device(&ipgre_tap_net_ops); if (err < 0) goto pnet_tap_faied; err = gre_cisco_register(&ipgre_protocol); if (err < 0) { pr_info("%s: can't add protocol\n", __func__); goto add_proto_failed; } err = rtnl_link_register(&ipgre_tap_ops); if (err < 0) goto tap_ops_failed; pr_info("GRE over IPv4 tunneling driver\n"); return 0; tap_ops_failed: gre_cisco_unregister(&ipgre_protocol); add_proto_failed: unregister_pernet_device(&ipgre_tap_net_ops); pnet_tap_faied: return err; } void rpl_ipgre_fini(void) { rtnl_link_unregister(&ipgre_tap_ops); gre_cisco_unregister(&ipgre_protocol); unregister_pernet_device(&ipgre_tap_net_ops); } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip6_output.c0000644000000000000000000000013113534540071022335 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.541859029 openvswitch-2.5.9/datapath/linux/compat/ip6_output.c0000644000175000017500000002616313534540071024034 0ustar00jpettitjpettit00000000000000/* * Backported from upstream commit 9ef2e965e554 * ("ipv6: drop frames with attached skb->sk in forwarding") * * IPv6 output functions * Linux INET6 implementation * * Authors: * Pedro Roque * * Based on linux/net/ipv4/ip_output.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Changes: * A.N.Kuznetsov : airthmetics in fragmentation. * extension headers are implemented. * route changes now work. * ip6_forward does not confuse sniffers. * etc. * * H. von Brand : Added missing #include * Imran Patel : frag id should be in NBO * Kazunori MIYAZAWA @USAGI * : add ip6_append_data and related functions * for datagram xmit */ #include #if !defined(HAVE_NF_IPV6_OPS_FRAGMENT) && defined(OVS_FRAGMENT_BACKPORT) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IP_IDENTS_SZ 2048u static atomic_t *ip_idents __read_mostly; static u32 *ip_tstamps __read_mostly; int __init ip6_output_init(void); void ip6_output_exit(void); /* In order to protect privacy, we add a perturbation to identifiers * if one generator is seldom used. This makes hard for an attacker * to infer how many packets were sent between two points in time. */ static u32 rpl_ip_idents_reserve(u32 hash, int segs) { u32 *p_tstamp = ip_tstamps + hash % IP_IDENTS_SZ; atomic_t *p_id = ip_idents + hash % IP_IDENTS_SZ; u32 old = ACCESS_ONCE(*p_tstamp); u32 now = (u32)jiffies; u32 delta = 0; if (old != now && cmpxchg(p_tstamp, old, now) == old) delta = prandom_u32_max(now - old); return atomic_add_return(segs + delta, p_id) - segs; } static u32 rpl___ipv6_select_ident(struct net *net, u32 hashrnd, const struct in6_addr *dst, const struct in6_addr *src) { u32 hash, id; hash = __ipv6_addr_jhash(dst, hashrnd); hash = __ipv6_addr_jhash(src, hash); hash ^= net_hash_mix(net); /* Treat id of 0 as unset and if we get 0 back from ip_idents_reserve, * set the hight order instead thus minimizing possible future * collisions. */ id = rpl_ip_idents_reserve(hash, 1); if (unlikely(!id)) id = 1 << 31; return id; } static __be32 rpl_ipv6_select_ident(struct net *net, const struct in6_addr *daddr, const struct in6_addr *saddr) { static u32 ip6_idents_hashrnd __read_mostly; u32 id; net_get_random_once(&ip6_idents_hashrnd, sizeof(ip6_idents_hashrnd)); id = rpl___ipv6_select_ident(net, ip6_idents_hashrnd, daddr, saddr); return htonl(id); } static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from) { to->pkt_type = from->pkt_type; to->priority = from->priority; to->protocol = from->protocol; skb_dst_drop(to); skb_dst_set(to, dst_clone(skb_dst(from))); to->dev = from->dev; to->mark = from->mark; #ifdef CONFIG_NET_SCHED to->tc_index = from->tc_index; #endif nf_copy(to, from); skb_copy_secmark(to, from); } #ifdef HAVE_IP_FRAGMENT_TAKES_SOCK #define OUTPUT(skb) output(skb->sk, skb) #else #define OUTPUT(skb) output(skb) #endif int ip6_fragment(struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)) { struct sk_buff *frag; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ? inet6_sk(skb->sk) : NULL; struct ipv6hdr *tmp_hdr; struct frag_hdr *fh; unsigned int mtu, hlen, left, len; int hroom, troom; __be32 frag_id; int ptr, offset = 0, err = 0; u8 *prevhdr, nexthdr = 0; struct net *net = dev_net(skb_dst(skb)->dev); hlen = ip6_find_1stfragopt(skb, &prevhdr); nexthdr = *prevhdr; mtu = ip6_skb_dst_mtu(skb); /* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. */ if (unlikely(!skb->ignore_df && skb->len > mtu)) goto fail_toobig; if (IP6CB(skb)->frag_max_size) { if (IP6CB(skb)->frag_max_size > mtu) goto fail_toobig; /* don't send fragments larger than what we received */ mtu = IP6CB(skb)->frag_max_size; if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; } if (np && np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; } mtu -= hlen + sizeof(struct frag_hdr); frag_id = rpl_ipv6_select_ident(net, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr); hroom = LL_RESERVED_SPACE(rt->dst.dev); if (skb_has_frag_list(skb)) { int first_len = skb_pagelen(skb); struct sk_buff *frag2; if (first_len - hlen > mtu || ((first_len - hlen) & 7) || skb_cloned(skb) || skb_headroom(skb) < (hroom + sizeof(struct frag_hdr))) goto slow_path; skb_walk_frags(skb, frag) { /* Correct geometry. */ if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < (hlen + hroom + sizeof(struct frag_hdr))) goto slow_path_clean; /* Partially cloned skb? */ if (skb_shared(frag)) goto slow_path_clean; BUG_ON(frag->sk); if (skb->sk) { frag->sk = skb->sk; frag->destructor = sock_wfree; } skb->truesize -= frag->truesize; } err = 0; offset = 0; /* BUILD HEADER */ *prevhdr = NEXTHDR_FRAGMENT; tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC); if (!tmp_hdr) { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); err = -ENOMEM; goto fail; } frag = skb_shinfo(skb)->frag_list; skb_frag_list_init(skb); __skb_pull(skb, hlen); fh = (struct frag_hdr *)__skb_push(skb, sizeof(struct frag_hdr)); __skb_push(skb, hlen); skb_reset_network_header(skb); memcpy(skb_network_header(skb), tmp_hdr, hlen); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(IP6_MF); fh->identification = frag_id; first_len = skb_pagelen(skb); skb->data_len = first_len - skb_headlen(skb); skb->len = first_len; ipv6_hdr(skb)->payload_len = htons(first_len - sizeof(struct ipv6hdr)); dst_hold(&rt->dst); for (;;) { /* Prepare header of the next frame, * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; skb_reset_transport_header(frag); fh = (struct frag_hdr *)__skb_push(frag, sizeof(struct frag_hdr)); __skb_push(frag, hlen); skb_reset_network_header(frag); memcpy(skb_network_header(frag), tmp_hdr, hlen); offset += skb->len - hlen - sizeof(struct frag_hdr); fh->nexthdr = nexthdr; fh->reserved = 0; fh->frag_off = htons(offset); if (frag->next) fh->frag_off |= htons(IP6_MF); fh->identification = frag_id; ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ip6_copy_metadata(frag, skb); } err = OUTPUT(skb); if (!err) IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGCREATES); if (err || !frag) break; skb = frag; frag = skb->next; skb->next = NULL; } kfree(tmp_hdr); if (err == 0) { IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGOKS); ip6_rt_put(rt); return 0; } kfree_skb_list(frag); IP6_INC_STATS(net, ip6_dst_idev(&rt->dst), IPSTATS_MIB_FRAGFAILS); ip6_rt_put(rt); return err; slow_path_clean: skb_walk_frags(skb, frag2) { if (frag2 == frag) break; frag2->sk = NULL; frag2->destructor = NULL; skb->truesize += frag2->truesize; } } slow_path: if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb)) goto fail; left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ /* * Fragment the datagram. */ *prevhdr = NEXTHDR_FRAGMENT; troom = rt->dst.dev->needed_tailroom; /* * Keep copying data until we run out. */ while (left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) len = mtu; /* IF: we are not sending up to and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len &= ~7; } /* Allocate buffer */ frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) + hroom + troom, GFP_ATOMIC); if (!frag) { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); err = -ENOMEM; goto fail; } /* * Set up data on packet */ ip6_copy_metadata(frag, skb); skb_reserve(frag, hroom); skb_put(frag, len + hlen + sizeof(struct frag_hdr)); skb_reset_network_header(frag); fh = (struct frag_hdr *)(skb_network_header(frag) + hlen); frag->transport_header = (frag->network_header + hlen + sizeof(struct frag_hdr)); /* * Charge the memory for the fragment to any owner * it might possess */ if (skb->sk) skb_set_owner_w(frag, skb->sk); /* * Copy the packet header into the new buffer. */ skb_copy_from_linear_data(skb, skb_network_header(frag), hlen); /* * Build fragment header. */ fh->nexthdr = nexthdr; fh->reserved = 0; fh->identification = frag_id; /* * Copy a block of the IP datagram. */ BUG_ON(skb_copy_bits(skb, ptr, skb_transport_header(frag), len)); left -= len; fh->frag_off = htons(offset); if (left > 0) fh->frag_off |= htons(IP6_MF); ipv6_hdr(frag)->payload_len = htons(frag->len - sizeof(struct ipv6hdr)); ptr += len; offset += len; /* * Put this fragment into the sending queue. */ err = OUTPUT(frag); if (err) goto fail; IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGCREATES); } IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGOKS); consume_skb(skb); return err; fail_toobig: if (skb->sk && dst_allfrag(skb_dst(skb))) sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK); skb->dev = skb_dst(skb)->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); err = -EMSGSIZE; fail: IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); kfree_skb(skb); return err; } #undef OUTPUT int __init ip6_output_init(void) { ip_idents = kmalloc(IP_IDENTS_SZ * sizeof(*ip_idents), GFP_KERNEL); if (!ip_idents) { pr_warn("IP: failed to allocate ip_idents\n"); goto error; } prandom_bytes(ip_idents, IP_IDENTS_SZ * sizeof(*ip_idents)); ip_tstamps = kcalloc(IP_IDENTS_SZ, sizeof(*ip_tstamps), GFP_KERNEL); if (!ip_tstamps) { pr_warn("IP: failed to allocate ip_tstamps\n"); goto error_ip_idents_free; } return 0; error_ip_idents_free: kfree(ip_idents); error: return -ENOMEM; } void ip6_output_exit(void) { kfree(ip_tstamps); kfree(ip_idents); } #endif /* OVS_FRAGMENT_BACKPORT */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/socket.c0000644000000000000000000000013113534540071021507 xustar0029 mtime=1567801401.27768026 30 atime=1567801402.057685987 30 ctime=1567801425.553859118 openvswitch-2.5.9/datapath/linux/compat/socket.c0000644000175000017500000000124013534540071023173 0ustar00jpettitjpettit00000000000000#include #include #include #include #include #include #include #include #include #include #ifndef HAVE_SOCK_CREATE_KERN_NET #undef sock_create_kern int ovs_sock_create_kern(struct net *net, int family, int type, int protocol, struct socket **res) { int err; err = sock_create_kern(family, type, protocol, res); if (err < 0) return err; sk_change_net((*res)->sk, net); return err; } #undef sk_release_kernel void ovs_sock_release(struct socket *sock) { sk_release_kernel(sock->sk); } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/exthdrs_core.c0000644000000000000000000000013213534540071022711 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.525858911 openvswitch-2.5.9/datapath/linux/compat/exthdrs_core.c0000644000175000017500000001063513534540071024404 0ustar00jpettitjpettit00000000000000#include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, __be16 *frag_offp) { u8 nexthdr = *nexthdrp; *frag_offp = 0; while (ipv6_ext_hdr(nexthdr)) { struct ipv6_opt_hdr _hdr, *hp; int hdrlen; if (nexthdr == NEXTHDR_NONE) return -1; hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) return -1; if (nexthdr == NEXTHDR_FRAGMENT) { __be16 _frag_off, *fp; fp = skb_header_pointer(skb, start+offsetof(struct frag_hdr, frag_off), sizeof(_frag_off), &_frag_off); if (fp == NULL) return -1; *frag_offp = *fp; if (ntohs(*frag_offp) & ~0x7) break; hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) hdrlen = (hp->hdrlen+2)<<2; else hdrlen = ipv6_optlen(hp); nexthdr = hp->nexthdr; start += hdrlen; } *nexthdrp = nexthdr; return start; } EXPORT_SYMBOL_GPL(rpl_ipv6_skip_exthdr); #endif /* Kernel version < 3.3 */ #ifndef HAVE_IP6_FH_F_SKIP_RH /* * find the offset to specified header or the protocol number of last header * if target < 0. "last header" is transport protocol header, ESP, or * "No next header". * * Note that *offset is used as input/output parameter. an if it is not zero, * then it must be a valid offset to an inner IPv6 header. This can be used * to explore inner IPv6 header, eg. ICMPv6 error messages. * * If target header is found, its offset is set in *offset and return protocol * number. Otherwise, return -1. * * If the first fragment doesn't contain the final protocol header or * NEXTHDR_NONE it is considered invalid. * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, * *offset is meaningless and fragment offset is stored in *fragoff if fragoff * isn't NULL. * * if flags is not NULL and it's a fragment, then the frag flag * IP6_FH_F_FRAG will be set. If it's an AH header, the * IP6_FH_F_AUTH flag is set and target < 0, then this function will * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this * function will skip all those routing headers, where segements_left was 0. */ int rpl_ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff, int *flags) { unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); u8 nexthdr = ipv6_hdr(skb)->nexthdr; unsigned int len; bool found; if (fragoff) *fragoff = 0; if (*offset) { struct ipv6hdr _ip6, *ip6; ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); if (!ip6 || (ip6->version != 6)) { printk(KERN_ERR "IPv6 header not found\n"); return -EBADMSG; } start = *offset + sizeof(struct ipv6hdr); nexthdr = ip6->nexthdr; } len = skb->len - start; do { struct ipv6_opt_hdr _hdr, *hp; unsigned int hdrlen; found = (nexthdr == target); if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { if (target < 0 || found) break; return -ENOENT; } hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) return -EBADMSG; if (nexthdr == NEXTHDR_ROUTING) { struct ipv6_rt_hdr _rh, *rh; rh = skb_header_pointer(skb, start, sizeof(_rh), &_rh); if (rh == NULL) return -EBADMSG; if (flags && (*flags & IP6_FH_F_SKIP_RH) && rh->segments_left == 0) found = false; } if (nexthdr == NEXTHDR_FRAGMENT) { unsigned short _frag_off; __be16 *fp; if (flags) /* Indicate that this is a fragment */ *flags |= IP6_FH_F_FRAG; fp = skb_header_pointer(skb, start+offsetof(struct frag_hdr, frag_off), sizeof(_frag_off), &_frag_off); if (fp == NULL) return -EBADMSG; _frag_off = ntohs(*fp) & ~0x7; if (_frag_off) { if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { if (fragoff) *fragoff = _frag_off; return hp->nexthdr; } return -ENOENT; } hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) { if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) break; hdrlen = (hp->hdrlen + 2) << 2; } else hdrlen = ipv6_optlen(hp); if (!found) { nexthdr = hp->nexthdr; len -= hdrlen; start += hdrlen; } } while (!found); *offset = start; return nexthdr; } EXPORT_SYMBOL_GPL(rpl_ipv6_find_hdr); #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/lisp.c0000644000000000000000000000013113534540071021166 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.541859029 openvswitch-2.5.9/datapath/linux/compat/lisp.c0000644000175000017500000004302613534540071022662 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2013 Cisco Systems, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "gso.h" #include "vport.h" #include "gso.h" #include "vport-netdev.h" #define LISP_UDP_PORT 4341 #define LISP_NETDEV_VER "0.1" static int lisp_net_id; /* Pseudo network device */ struct lisp_dev { struct net *net; /* netns for packet i/o */ struct net_device *dev; /* netdev for lisp tunnel */ struct socket *sock; __be16 dst_port; struct list_head next; }; /* per-network namespace private data for this module */ struct lisp_net { struct list_head lisp_list; }; /* * LISP encapsulation header: * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |N|L|E|V|I|flags| Nonce/Map-Version | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Instance ID/Locator Status Bits | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ /** * struct lisphdr - LISP header * @nonce_present: Flag indicating the presence of a 24 bit nonce value. * @locator_status_bits_present: Flag indicating the presence of Locator Status * Bits (LSB). * @solicit_echo_nonce: Flag indicating the use of the echo noncing mechanism. * @map_version_present: Flag indicating the use of mapping versioning. * @instance_id_present: Flag indicating the presence of a 24 bit Instance ID. * @reserved_flags: 3 bits reserved for future flags. * @nonce: 24 bit nonce value. * @map_version: 24 bit mapping version. * @locator_status_bits: Locator Status Bits: 32 bits when instance_id_present * is not set, 8 bits when it is. * @instance_id: 24 bit Instance ID */ struct lisphdr { #ifdef __LITTLE_ENDIAN_BITFIELD __u8 reserved_flags:3; __u8 instance_id_present:1; __u8 map_version_present:1; __u8 solicit_echo_nonce:1; __u8 locator_status_bits_present:1; __u8 nonce_present:1; #else __u8 nonce_present:1; __u8 locator_status_bits_present:1; __u8 solicit_echo_nonce:1; __u8 map_version_present:1; __u8 instance_id_present:1; __u8 reserved_flags:3; #endif union { __u8 nonce[3]; __u8 map_version[3]; } u1; union { __be32 locator_status_bits; struct { __u8 instance_id[3]; __u8 locator_status_bits; } word2; } u2; }; #define LISP_HLEN (sizeof(struct udphdr) + sizeof(struct lisphdr)) #define LISP_MAX_MTU (IP_MAX_MTU - LISP_HLEN - sizeof(struct iphdr)) static inline struct lisphdr *lisp_hdr(const struct sk_buff *skb) { return (struct lisphdr *)(udp_hdr(skb) + 1); } /* Convert 64 bit tunnel ID to 24 bit Instance ID. */ static void tunnel_id_to_instance_id(__be64 tun_id, __u8 *iid) { #ifdef __BIG_ENDIAN iid[0] = (__force __u8)(tun_id >> 16); iid[1] = (__force __u8)(tun_id >> 8); iid[2] = (__force __u8)tun_id; #else iid[0] = (__force __u8)((__force u64)tun_id >> 40); iid[1] = (__force __u8)((__force u64)tun_id >> 48); iid[2] = (__force __u8)((__force u64)tun_id >> 56); #endif } /* Convert 24 bit Instance ID to 64 bit tunnel ID. */ static __be64 instance_id_to_tunnel_id(__u8 *iid) { #ifdef __BIG_ENDIAN return (iid[0] << 16) | (iid[1] << 8) | iid[2]; #else return (__force __be64)(((__force u64)iid[0] << 40) | ((__force u64)iid[1] << 48) | ((__force u64)iid[2] << 56)); #endif } /* Compute source UDP port for outgoing packet. * Currently we use the flow hash. */ static u16 get_src_port(struct net *net, struct sk_buff *skb) { u32 hash = skb_get_hash(skb); unsigned int range; int high; int low; if (!hash) { if (skb->protocol == htons(ETH_P_IP)) { struct iphdr *iph; int size = (sizeof(iph->saddr) * 2) / sizeof(u32); iph = (struct iphdr *) skb_network_header(skb); hash = jhash2((const u32 *)&iph->saddr, size, 0); } else if (skb->protocol == htons(ETH_P_IPV6)) { struct ipv6hdr *ipv6hdr; ipv6hdr = (struct ipv6hdr *) skb_network_header(skb); hash = jhash2((const u32 *)&ipv6hdr->saddr, (sizeof(struct in6_addr) * 2) / sizeof(u32), 0); } else { pr_warn_once("LISP inner protocol is not IP when " "calculating hash.\n"); } } inet_get_local_port_range(net, &low, &high); range = (high - low) + 1; return (((u64) hash * range) >> 32) + low; } static void lisp_build_header(struct sk_buff *skb, const struct ip_tunnel_key *tun_key) { struct lisphdr *lisph; lisph = (struct lisphdr *)__skb_push(skb, sizeof(struct lisphdr)); lisph->nonce_present = 0; /* We don't support echo nonce algorithm */ lisph->locator_status_bits_present = 1; /* Set LSB */ lisph->solicit_echo_nonce = 0; /* No echo noncing */ lisph->map_version_present = 0; /* No mapping versioning, nonce instead */ lisph->instance_id_present = 1; /* Store the tun_id as Instance ID */ lisph->reserved_flags = 0; /* Reserved flags, set to 0 */ lisph->u1.nonce[0] = 0; lisph->u1.nonce[1] = 0; lisph->u1.nonce[2] = 0; tunnel_id_to_instance_id(tun_key->tun_id, &lisph->u2.word2.instance_id[0]); lisph->u2.word2.locator_status_bits = 1; } /* Called with rcu_read_lock and BH disabled. */ static int lisp_rcv(struct sock *sk, struct sk_buff *skb) { struct net_device *dev; struct lisphdr *lisph; struct iphdr *inner_iph; struct metadata_dst *tun_dst; #ifndef HAVE_METADATA_DST struct metadata_dst temp; #endif __be64 key; struct ethhdr *ethh; __be16 protocol; dev = rcu_dereference_sk_user_data(sk); if (unlikely(!dev)) goto error; if (iptunnel_pull_header(skb, LISP_HLEN, 0)) goto error; lisph = lisp_hdr(skb); if (lisph->instance_id_present != 1) key = 0; else key = instance_id_to_tunnel_id(&lisph->u2.word2.instance_id[0]); /* Save outer tunnel values */ #ifndef HAVE_METADATA_DST tun_dst = &temp; ovs_udp_tun_rx_dst(&tun_dst->u.tun_info, skb, AF_INET, TUNNEL_KEY, key, 0); #else tun_dst = udp_tun_rx_dst(skb, AF_INET, TUNNEL_KEY, key, 0); #endif /* Drop non-IP inner packets */ inner_iph = (struct iphdr *)(lisph + 1); switch (inner_iph->version) { case 4: protocol = htons(ETH_P_IP); break; case 6: protocol = htons(ETH_P_IPV6); break; default: goto error; } skb->protocol = protocol; /* Add Ethernet header */ ethh = (struct ethhdr *)skb_push(skb, ETH_HLEN); memset(ethh, 0, ETH_HLEN); ethh->h_dest[0] = 0x02; ethh->h_source[0] = 0x02; ethh->h_proto = protocol; ovs_ip_tunnel_rcv(dev, skb, tun_dst); goto out; error: kfree_skb(skb); out: return 0; } netdev_tx_t rpl_lisp_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct lisp_dev *lisp_dev = netdev_priv(dev); struct net *net = lisp_dev->net; int network_offset = skb_network_offset(skb); struct ip_tunnel_info *info; struct ip_tunnel_key *tun_key; struct rtable *rt; int min_headroom; __be16 src_port, dst_port; struct flowi4 fl; __be16 df; int err; info = skb_tunnel_info(skb); if (unlikely(!info)) { err = -EINVAL; goto error; } if (skb->protocol != htons(ETH_P_IP) && skb->protocol != htons(ETH_P_IPV6)) { err = 0; goto error; } tun_key = &info->key; /* Route lookup */ memset(&fl, 0, sizeof(fl)); fl.daddr = tun_key->u.ipv4.dst; fl.saddr = tun_key->u.ipv4.src; fl.flowi4_tos = RT_TOS(tun_key->tos); fl.flowi4_mark = skb->mark; fl.flowi4_proto = IPPROTO_UDP; rt = ip_route_output_key(net, &fl); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto error; } min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + sizeof(struct iphdr) + LISP_HLEN; if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { int head_delta = SKB_DATA_ALIGN(min_headroom - skb_headroom(skb) + 16); err = pskb_expand_head(skb, max_t(int, head_delta, 0), 0, GFP_ATOMIC); if (unlikely(err)) goto err_free_rt; } /* Reset l2 headers. */ skb_pull(skb, network_offset); skb_reset_mac_header(skb); vlan_set_tci(skb, 0); skb = udp_tunnel_handle_offloads(skb, false, 0, false); if (IS_ERR(skb)) { err = PTR_ERR(skb); skb = NULL; goto err_free_rt; } src_port = htons(get_src_port(net, skb)); dst_port = lisp_dev->dst_port; lisp_build_header(skb, tun_key); skb->ignore_df = 1; ovs_skb_set_inner_protocol(skb, skb->protocol); df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; err = udp_tunnel_xmit_skb(rt, lisp_dev->sock->sk, skb, fl.saddr, tun_key->u.ipv4.dst, tun_key->tos, tun_key->ttl, df, src_port, dst_port, false, true); iptunnel_xmit_stats(err, &dev->stats, (struct pcpu_sw_netstats __percpu *)dev->tstats); return NETDEV_TX_OK; err_free_rt: ip_rt_put(rt); error: kfree_skb(skb); return NETDEV_TX_OK; } EXPORT_SYMBOL(rpl_lisp_xmit); #ifdef HAVE_DEV_TSTATS /* Setup stats when device is created */ static int lisp_init(struct net_device *dev) { dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; } static void lisp_uninit(struct net_device *dev) { free_percpu(dev->tstats); } #endif static struct socket *create_sock(struct net *net, bool ipv6, __be16 port) { struct socket *sock; struct udp_port_cfg udp_conf; int err; memset(&udp_conf, 0, sizeof(udp_conf)); if (ipv6) { udp_conf.family = AF_INET6; } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = htonl(INADDR_ANY); } udp_conf.local_udp_port = port; /* Open UDP socket */ err = udp_sock_create(net, &udp_conf, &sock); if (err < 0) return ERR_PTR(err); return sock; } static int lisp_open(struct net_device *dev) { struct lisp_dev *lisp = netdev_priv(dev); struct udp_tunnel_sock_cfg tunnel_cfg; struct net *net = lisp->net; lisp->sock = create_sock(net, false, lisp->dst_port); if (IS_ERR(lisp->sock)) return PTR_ERR(lisp->sock); /* Mark socket as an encapsulation socket */ memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); tunnel_cfg.sk_user_data = dev; tunnel_cfg.encap_type = 1; tunnel_cfg.encap_rcv = lisp_rcv; tunnel_cfg.encap_destroy = NULL; setup_udp_tunnel_sock(net, lisp->sock, &tunnel_cfg); return 0; } static int lisp_stop(struct net_device *dev) { struct lisp_dev *lisp = netdev_priv(dev); udp_tunnel_sock_release(lisp->sock); lisp->sock = NULL; return 0; } static netdev_tx_t lisp_dev_xmit(struct sk_buff *skb, struct net_device *dev) { #ifdef HAVE_METADATA_DST return rpl_lisp_xmit(skb); #else /* Drop All packets coming from networking stack. OVS-CB is * not initialized for these packets. */ dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; #endif } static int lisp_change_mtu(struct net_device *dev, int new_mtu) { if (new_mtu < 68 || new_mtu > LISP_MAX_MTU) return -EINVAL; dev->mtu = new_mtu; return 0; } static const struct net_device_ops lisp_netdev_ops = { #ifdef HAVE_DEV_TSTATS .ndo_init = lisp_init, .ndo_uninit = lisp_uninit, .ndo_get_stats64 = ip_tunnel_get_stats64, #endif .ndo_open = lisp_open, .ndo_stop = lisp_stop, .ndo_start_xmit = lisp_dev_xmit, .ndo_change_mtu = lisp_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; static void lisp_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { strlcpy(drvinfo->version, LISP_NETDEV_VER, sizeof(drvinfo->version)); strlcpy(drvinfo->driver, "lisp", sizeof(drvinfo->driver)); } static const struct ethtool_ops lisp_ethtool_ops = { .get_drvinfo = lisp_get_drvinfo, .get_link = ethtool_op_get_link, }; /* Info for udev, that this is a virtual tunnel endpoint */ static struct device_type lisp_type = { .name = "lisp", }; /* Initialize the device structure. */ static void lisp_setup(struct net_device *dev) { ether_setup(dev); dev->netdev_ops = &lisp_netdev_ops; dev->ethtool_ops = &lisp_ethtool_ops; dev->destructor = free_netdev; SET_NETDEV_DEVTYPE(dev, &lisp_type); dev->features |= NETIF_F_LLTX | NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; #endif #ifdef HAVE_METADATA_DST netif_keep_dst(dev); #endif dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; eth_hw_addr_random(dev); } static const struct nla_policy lisp_policy[IFLA_LISP_MAX + 1] = { [IFLA_LISP_PORT] = { .type = NLA_U16 }, }; static int lisp_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } return 0; } static struct lisp_dev *find_dev(struct net *net, __be16 dst_port) { struct lisp_net *ln = net_generic(net, lisp_net_id); struct lisp_dev *dev; list_for_each_entry(dev, &ln->lisp_list, next) { if (dev->dst_port == dst_port) return dev; } return NULL; } static int lisp_configure(struct net *net, struct net_device *dev, __be16 dst_port) { struct lisp_net *ln = net_generic(net, lisp_net_id); struct lisp_dev *lisp = netdev_priv(dev); int err; lisp->net = net; lisp->dev = dev; lisp->dst_port = dst_port; if (find_dev(net, dst_port)) return -EBUSY; err = lisp_change_mtu(dev, LISP_MAX_MTU); if (err) return err; err = register_netdevice(dev); if (err) return err; list_add(&lisp->next, &ln->lisp_list); return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int lisp_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { #else static int lisp_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net *net = &init_net; #endif __be16 dst_port = htons(LISP_UDP_PORT); if (data[IFLA_LISP_PORT]) dst_port = nla_get_be16(data[IFLA_LISP_PORT]); return lisp_configure(net, dev, dst_port); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static void lisp_dellink(struct net_device *dev, struct list_head *head) #else static void lisp_dellink(struct net_device *dev) #endif { struct lisp_dev *lisp = netdev_priv(dev); list_del(&lisp->next); unregister_netdevice_queue(dev, head); } static size_t lisp_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__be32)); /* IFLA_LISP_PORT */ } static int lisp_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct lisp_dev *lisp = netdev_priv(dev); if (nla_put_be16(skb, IFLA_LISP_PORT, lisp->dst_port)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } static struct rtnl_link_ops lisp_link_ops __read_mostly = { .kind = "lisp", .maxtype = IFLA_LISP_MAX, .policy = lisp_policy, .priv_size = sizeof(struct lisp_dev), .setup = lisp_setup, .validate = lisp_validate, .newlink = lisp_newlink, .dellink = lisp_dellink, .get_size = lisp_get_size, .fill_info = lisp_fill_info, }; struct net_device *rpl_lisp_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port) { struct nlattr *tb[IFLA_MAX + 1]; struct net_device *dev; int err; memset(tb, 0, sizeof(tb)); dev = rtnl_create_link(net, (char *) name, name_assign_type, &lisp_link_ops, tb); if (IS_ERR(dev)) return dev; err = lisp_configure(net, dev, htons(dst_port)); if (err) { free_netdev(dev); return ERR_PTR(err); } return dev; } EXPORT_SYMBOL_GPL(rpl_lisp_dev_create_fb); static int lisp_init_net(struct net *net) { struct lisp_net *ln = net_generic(net, lisp_net_id); INIT_LIST_HEAD(&ln->lisp_list); return 0; } static void lisp_exit_net(struct net *net) { struct lisp_net *ln = net_generic(net, lisp_net_id); struct lisp_dev *lisp, *next; struct net_device *dev, *aux; LIST_HEAD(list); rtnl_lock(); /* gather any lisp devices that were moved into this ns */ for_each_netdev_safe(net, dev, aux) if (dev->rtnl_link_ops == &lisp_link_ops) unregister_netdevice_queue(dev, &list); list_for_each_entry_safe(lisp, next, &ln->lisp_list, next) { /* If lisp->dev is in the same netns, it was already added * to the lisp by the previous loop. */ if (!net_eq(dev_net(lisp->dev), net)) unregister_netdevice_queue(lisp->dev, &list); } /* unregister the devices gathered above */ unregister_netdevice_many(&list); rtnl_unlock(); } static struct pernet_operations lisp_net_ops = { .init = lisp_init_net, .exit = lisp_exit_net, .id = &lisp_net_id, .size = sizeof(struct lisp_net), }; DEFINE_COMPAT_PNET_REG_FUNC(device) int rpl_lisp_init_module(void) { int rc; rc = register_pernet_subsys(&lisp_net_ops); if (rc) goto out1; rc = rtnl_link_register(&lisp_link_ops); if (rc) goto out2; pr_info("LISP tunneling driver\n"); return 0; out2: unregister_pernet_subsys(&lisp_net_ops); out1: return rc; } void rpl_lisp_cleanup_module(void) { rtnl_link_unregister(&lisp_link_ops); unregister_pernet_subsys(&lisp_net_ops); } openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip_tunnels_core.c0000644000000000000000000000013113534540071023407 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.541859029 openvswitch-2.5.9/datapath/linux/compat/ip_tunnels_core.c0000644000175000017500000001556013534540071025105 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include "compat.h" #include "gso.h" #include "vport-netdev.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 proto, __u8 tos, __u8 ttl, __be16 df, bool xnet) { int pkt_len = skb->len; struct iphdr *iph; int err; skb_scrub_packet(skb, xnet); skb_clear_hash(skb); skb_dst_set(skb, &rt_dst(rt)); #if 0 /* Do not clear ovs_skb_cb. It will be done in gso code. */ memset(IPCB(skb), 0, sizeof(*IPCB(skb))); #endif /* Push down and install the IP header. */ __skb_push(skb, sizeof(struct iphdr)); skb_reset_network_header(skb); iph = ip_hdr(skb); iph->version = 4; iph->ihl = sizeof(struct iphdr) >> 2; iph->frag_off = df; iph->protocol = proto; iph->tos = tos; iph->daddr = dst; iph->saddr = src; iph->ttl = ttl; #ifdef HAVE_IP_SELECT_IDENT_USING_DST_ENTRY __ip_select_ident(iph, &rt_dst(rt), (skb_shinfo(skb)->gso_segs ?: 1) - 1); #elif defined(HAVE_IP_SELECT_IDENT_USING_NET) __ip_select_ident(dev_net(rt->dst.dev), iph, skb_shinfo(skb)->gso_segs ?: 1); #else __ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1); #endif err = ip_local_out(skb); if (unlikely(net_xmit_eval(err))) pkt_len = 0; return pkt_len; } EXPORT_SYMBOL_GPL(rpl_iptunnel_xmit); struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb, bool csum_help, int gso_type_mask, void (*fix_segment)(struct sk_buff *)) { int err; if (likely(!skb_is_encapsulated(skb))) { skb_reset_inner_headers(skb); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) skb->encapsulation = 1; #endif } else if (skb_is_gso(skb)) { err = -ENOSYS; goto error; } if (gso_type_mask) fix_segment = NULL; OVS_GSO_CB(skb)->fix_segment = fix_segment; if (skb_is_gso(skb)) { err = skb_unclone(skb, GFP_ATOMIC); if (unlikely(err)) goto error; skb_shinfo(skb)->gso_type |= gso_type_mask; return skb; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) /* If packet is not gso and we are resolving any partial checksum, * clear encapsulation flag. This allows setting CHECKSUM_PARTIAL * on the outer header without confusing devices that implement * NETIF_F_IP_CSUM with encapsulation. */ if (csum_help) skb->encapsulation = 0; #endif if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) { err = skb_checksum_help(skb); if (unlikely(err)) goto error; } else if (skb->ip_summed != CHECKSUM_PARTIAL) skb->ip_summed = CHECKSUM_NONE; return skb; error: kfree_skb(skb); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ovs_iptunnel_handle_offloads); int rpl_iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto) { if (unlikely(!pskb_may_pull(skb, hdr_len))) return -ENOMEM; skb_pull_rcsum(skb, hdr_len); if (inner_proto == htons(ETH_P_TEB)) { struct ethhdr *eh; if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) return -ENOMEM; eh = (struct ethhdr *)skb->data; if (likely(ntohs(eh->h_proto) >= ETH_P_802_3_MIN)) skb->protocol = eh->h_proto; else skb->protocol = htons(ETH_P_802_2); } else { skb->protocol = inner_proto; } nf_reset(skb); secpath_reset(skb); skb_clear_hash(skb); skb_dst_drop(skb); vlan_set_tci(skb, 0); skb_set_queue_mapping(skb, 0); skb->pkt_type = PACKET_HOST; return 0; } EXPORT_SYMBOL_GPL(rpl_iptunnel_pull_header); #endif bool ovs_skb_is_encapsulated(struct sk_buff *skb) { /* checking for inner protocol should be sufficient on newer kernel, but * old kernel just set encapsulation bit. */ return ovs_skb_get_inner_protocol(skb) || skb_encapsulation(skb); } EXPORT_SYMBOL_GPL(ovs_skb_is_encapsulated); /* derived from ip_tunnel_rcv(). */ void ovs_ip_tunnel_rcv(struct net_device *dev, struct sk_buff *skb, struct metadata_dst *tun_dst) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) struct pcpu_sw_netstats *tstats; tstats = this_cpu_ptr((struct pcpu_sw_netstats __percpu *)dev->tstats); u64_stats_update_begin(&tstats->syncp); tstats->rx_packets++; tstats->rx_bytes += skb->len; u64_stats_update_end(&tstats->syncp); #endif skb_reset_mac_header(skb); skb_scrub_packet(skb, false); skb->protocol = eth_type_trans(skb, dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); ovs_skb_dst_set(skb, (struct dst_entry *)tun_dst); #ifndef HAVE_METADATA_DST netdev_port_receive(skb, &tun_dst->u.tun_info); #else netif_rx(skb); #endif } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) #ifndef HAVE_PCPU_SW_NETSTATS #define netdev_stats_to_stats64 rpl_netdev_stats_to_stats64 static void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, const struct net_device_stats *netdev_stats) { #if BITS_PER_LONG == 64 BUILD_BUG_ON(sizeof(*stats64) != sizeof(*netdev_stats)); memcpy(stats64, netdev_stats, sizeof(*stats64)); #else size_t i, n = sizeof(*stats64) / sizeof(u64); const unsigned long *src = (const unsigned long *)netdev_stats; u64 *dst = (u64 *)stats64; BUILD_BUG_ON(sizeof(*netdev_stats) / sizeof(unsigned long) != sizeof(*stats64) / sizeof(u64)); for (i = 0; i < n; i++) dst[i] = src[i]; #endif } struct rtnl_link_stats64 *rpl_ip_tunnel_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot) { int i; netdev_stats_to_stats64(tot, &dev->stats); for_each_possible_cpu(i) { const struct pcpu_sw_netstats *tstats = per_cpu_ptr((struct pcpu_sw_netstats __percpu *)dev->tstats, i); u64 rx_packets, rx_bytes, tx_packets, tx_bytes; unsigned int start; do { start = u64_stats_fetch_begin_irq(&tstats->syncp); rx_packets = tstats->rx_packets; tx_packets = tstats->tx_packets; rx_bytes = tstats->rx_bytes; tx_bytes = tstats->tx_bytes; } while (u64_stats_fetch_retry_irq(&tstats->syncp, start)); tot->rx_packets += rx_packets; tot->tx_packets += tx_packets; tot->rx_bytes += rx_bytes; tot->tx_bytes += tx_bytes; } return tot; } #endif #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/udp.c0000644000000000000000000000013113534540071021007 xustar0029 mtime=1567801401.27768026 30 atime=1567801402.057685987 30 ctime=1567801425.553859118 openvswitch-2.5.9/datapath/linux/compat/udp.c0000644000175000017500000000234513534540071022502 0ustar00jpettitjpettit00000000000000#include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) #include /* Function to set UDP checksum for an IPv4 UDP packet. This is intended * for the simple case like when setting the checksum for a UDP tunnel. */ void rpl_udp_set_csum(bool nocheck, struct sk_buff *skb, __be32 saddr, __be32 daddr, int len) { struct udphdr *uh = udp_hdr(skb); if (nocheck) uh->check = 0; else if (skb_is_gso(skb)) uh->check = ~udp_v4_check(len, saddr, daddr, 0); else if (skb_dst(skb) && skb_dst(skb)->dev && (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL); skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); uh->check = ~udp_v4_check(len, saddr, daddr, 0); } else { int l4_offset = skb_transport_offset(skb); __wsum csum; BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL); uh->check = 0; csum = skb_checksum(skb, l4_offset, len, 0); uh->check = udp_v4_check(len, saddr, daddr, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; skb->ip_summed = CHECKSUM_UNNECESSARY; } } EXPORT_SYMBOL_GPL(rpl_udp_set_csum); #endif /* Linux version < 3.16 */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip_output.c0000644000000000000000000000013113534540071022247 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.537858999 openvswitch-2.5.9/datapath/linux/compat/ip_output.c0000644000175000017500000002432013534540071023737 0ustar00jpettitjpettit00000000000000/* * IP fragmentation backport, heavily based on linux/net/ipv4/ip_output.c, * copied from Linux ae7ef81ef000 ("skbuff: introduce skb_gso_validate_mtu") * * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * The Internet Protocol (IP) output module. * * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, * Alan Cox, * Richard Underwood * Stefan Becker, * Jorge Cwik, * Arnt Gulbrandsen, * Hirokazu Takahashi, * * See ip_input.c for original log * * Fixes: * Alan Cox : Missing nonblock feature in ip_build_xmit. * Mike Kilburn : htons() missing in ip_build_xmit. * Bradford Johnson: Fix faulty handling of some frames when * no route is found. * Alexander Demenshin: Missing sk/skb free in ip_queue_xmit * (in case if packet not accepted by * output firewall rules) * Mike McLagan : Routing by source * Alexey Kuznetsov: use new route cache * Andi Kleen: Fix broken PMTU recovery and remove * some redundant tests. * Vitaly E. Lavrov : Transparent proxy revived after year coma. * Andi Kleen : Replace ip_reply with ip_send_reply. * Andi Kleen : Split fast and slow ip_build_xmit path * for decreased register pressure on x86 * and more readibility. * Marc Boucher : When call_out_firewall returns FW_QUEUE, * silently drop skb instead of failing with -EPERM. * Detlev Wengorz : Copy protocol for fragments. * Hirokazu Takahashi: HW checksumming for outgoing UDP * datagrams. * Hirokazu Takahashi: sendfile() on UDP works now. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(OVS_FRAGMENT_BACKPORT) && !defined(HAVE_CORRECT_MRU_HANDLING) static inline void rpl_ip_options_fragment(struct sk_buff *skb) { unsigned char *optptr = skb_network_header(skb) + sizeof(struct iphdr); struct ip_options *opt = &(IPCB(skb)->opt); int l = opt->optlen; int optlen; while (l > 0) { switch (*optptr) { case IPOPT_END: return; case IPOPT_NOOP: l--; optptr++; continue; } optlen = optptr[1]; if (optlen < 2 || optlen > l) return; if (!IPOPT_COPIED(*optptr)) memset(optptr, IPOPT_NOOP, optlen); l -= optlen; optptr += optlen; } opt->ts = 0; opt->rr = 0; opt->rr_needaddr = 0; opt->ts_needaddr = 0; opt->ts_needtime = 0; } #define ip_options_fragment rpl_ip_options_fragment static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from) { to->pkt_type = from->pkt_type; to->priority = from->priority; to->protocol = from->protocol; skb_dst_drop(to); skb_dst_copy(to, from); to->dev = from->dev; to->mark = from->mark; /* Copy the flags to each fragment. */ IPCB(to)->flags = IPCB(from)->flags; #ifdef CONFIG_NET_SCHED to->tc_index = from->tc_index; #endif nf_copy(to, from); #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) to->ipvs_property = from->ipvs_property; #endif skb_copy_secmark(to, from); } #ifdef HAVE_IP_DO_FRAGMENT_USING_NET #define OUTPUT(net, sk, skb) output(net, sk, skb) #elif defined(HAVE_IP_FRAGMENT_TAKES_SOCK) #define OUTPUT(net, sk, skb) output(sk, skb) #else #define OUTPUT(net, sk, skb) output(skb) #endif /* * This IP datagram is too large to be sent in one piece. Break it up into * smaller pieces (each of size equal to IP header plus * a block of the data of the original IP data part) that will yet fit in a * single device frame, and queue such a frame for sending. */ int rpl_ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, int (*output)(OVS_VPORT_OUTPUT_PARAMS)) { struct iphdr *iph; int ptr; struct net_device *dev; struct sk_buff *skb2; unsigned int mtu, hlen, left, len, ll_rs; int offset; __be16 not_last_frag; struct rtable *rt = skb_rtable(skb); int err = 0; dev = rt->dst.dev; /* for offloaded checksums cleanup checksum before fragmentation */ if (skb->ip_summed == CHECKSUM_PARTIAL && (err = skb_checksum_help(skb))) goto fail; /* * Point into the IP datagram header. */ iph = ip_hdr(skb); mtu = ip_skb_dst_mtu(skb); if (IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size < mtu) mtu = IPCB(skb)->frag_max_size; /* * Setup starting values. */ hlen = iph->ihl * 4; mtu = mtu - hlen; /* Size of data space */ IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE; /* When frag_list is given, use it. First, check its validity: * some transformers could create wrong frag_list or break existing * one, it is not prohibited. In this case fall back to copying. * * LATER: this step can be merged to real generation of fragments, * we can switch to copy when see the first bad fragment. */ if (skb_has_frag_list(skb)) { struct sk_buff *frag, *frag2; int first_len = skb_pagelen(skb); if (first_len - hlen > mtu || ((first_len - hlen) & 7) || ip_is_fragment(iph) || skb_cloned(skb)) goto slow_path; skb_walk_frags(skb, frag) { /* Correct geometry. */ if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < hlen) goto slow_path_clean; /* Partially cloned skb? */ if (skb_shared(frag)) goto slow_path_clean; BUG_ON(frag->sk); if (skb->sk) { frag->sk = skb->sk; frag->destructor = sock_wfree; } skb->truesize -= frag->truesize; } /* Everything is OK. Generate! */ err = 0; offset = 0; frag = skb_shinfo(skb)->frag_list; skb_frag_list_init(skb); skb->data_len = first_len - skb_headlen(skb); skb->len = first_len; iph->tot_len = htons(first_len); iph->frag_off = htons(IP_MF); ip_send_check(iph); for (;;) { /* Prepare header of the next frame, * before previous one went down. */ if (frag) { frag->ip_summed = CHECKSUM_NONE; skb_reset_transport_header(frag); __skb_push(frag, hlen); skb_reset_network_header(frag); memcpy(skb_network_header(frag), iph, hlen); iph = ip_hdr(frag); iph->tot_len = htons(frag->len); ip_copy_metadata(frag, skb); if (offset == 0) ip_options_fragment(frag); offset += skb->len - hlen; iph->frag_off = htons(offset>>3); if (frag->next) iph->frag_off |= htons(IP_MF); /* Ready, complete checksum */ ip_send_check(iph); } err = OUTPUT(net, sk, skb); if (!err) IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); if (err || !frag) break; skb = frag; frag = skb->next; skb->next = NULL; } if (err == 0) { IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS); return 0; } while (frag) { skb = frag->next; kfree_skb(frag); frag = skb; } IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); return err; slow_path_clean: skb_walk_frags(skb, frag2) { if (frag2 == frag) break; frag2->sk = NULL; frag2->destructor = NULL; skb->truesize += frag2->truesize; } } slow_path: iph = ip_hdr(skb); left = skb->len - hlen; /* Space per frame */ ptr = hlen; /* Where to start from */ ll_rs = LL_RESERVED_SPACE(rt->dst.dev); /* * Fragment the datagram. */ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; not_last_frag = iph->frag_off & htons(IP_MF); /* * Keep copying data until we run out. */ while (left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) len = mtu; /* IF: we are not sending up to and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len &= ~7; } /* Allocate buffer */ skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC); if (!skb2) { err = -ENOMEM; goto fail; } /* * Set up data on packet */ ip_copy_metadata(skb2, skb); skb_reserve(skb2, ll_rs); skb_put(skb2, len + hlen); skb_reset_network_header(skb2); skb2->transport_header = skb2->network_header + hlen; /* * Charge the memory for the fragment to any owner * it might possess */ if (skb->sk) skb_set_owner_w(skb2, skb->sk); /* * Copy the packet header into the new buffer. */ skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen); /* * Copy a block of the IP datagram. */ if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len)) BUG(); left -= len; /* * Fill in the new header fields. */ iph = ip_hdr(skb2); iph->frag_off = htons((offset >> 3)); if (IPCB(skb)->flags & IPSKB_FRAG_PMTU) iph->frag_off |= htons(IP_DF); /* ANK: dirty, but effective trick. Upgrade options only if * the segment to be fragmented was THE FIRST (otherwise, * options are already fixed) and make it ONCE * on the initial skb, so that all the following fragments * will inherit fixed options. */ if (offset == 0) ip_options_fragment(skb); /* * Added AC : If we are fragmenting a fragment that's not the * last fragment then keep MF on each bit */ if (left > 0 || not_last_frag) iph->frag_off |= htons(IP_MF); ptr += len; offset += len; /* * Put this fragment into the sending queue. */ iph->tot_len = htons(len + hlen); ip_send_check(iph); err = OUTPUT(net, sk, skb2); if (err) goto fail; IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES); } consume_skb(skb); IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS); return err; fail: kfree_skb(skb); IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); return err; } EXPORT_SYMBOL(rpl_ip_do_fragment); #endif /* HAVE_CORRECT_MRU_HANDLING */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/skbuff-openvswitch.c0000644000000000000000000000013113534540071024046 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.549859088 openvswitch-2.5.9/datapath/linux/compat/skbuff-openvswitch.c0000644000175000017500000001641113534540071025540 0ustar00jpettitjpettit00000000000000#include #include #include #include #include #include "gso.h" #if !defined(HAVE_SKB_WARN_LRO) && defined(NETIF_F_LRO) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt void __skb_warn_lro_forwarding(const struct sk_buff *skb) { if (net_ratelimit()) pr_warn("%s: received packets cannot be forwarded while LRO is enabled\n", skb->dev->name); } #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) static inline bool head_frag(const struct sk_buff *skb) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) return skb->head_frag; #else return false; #endif } /** * skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy() * @from: source buffer * * Calculates the amount of linear headroom needed in the 'to' skb passed * into skb_zerocopy(). */ unsigned int rpl_skb_zerocopy_headlen(const struct sk_buff *from) { unsigned int hlen = 0; if (!head_frag(from) || skb_headlen(from) < L1_CACHE_BYTES || skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) hlen = skb_headlen(from); if (skb_has_frag_list(from)) hlen = from->len; return hlen; } EXPORT_SYMBOL_GPL(rpl_skb_zerocopy_headlen); #ifndef HAVE_SKB_ZEROCOPY /** * skb_zerocopy - Zero copy skb to skb * @to: destination buffer * @source: source buffer * @len: number of bytes to copy from source buffer * @hlen: size of linear headroom in destination buffer * * Copies up to `len` bytes from `from` to `to` by creating references * to the frags in the source buffer. * * The `hlen` as calculated by skb_zerocopy_headlen() specifies the * headroom in the `to` buffer. * * Return value: * 0: everything is OK * -ENOMEM: couldn't orphan frags of @from due to lack of memory * -EFAULT: skb_copy_bits() found some problem with skb geometry */ int rpl_skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) { int i, j = 0; int plen = 0; /* length of skb->head fragment */ int ret; struct page *page; unsigned int offset; BUG_ON(!head_frag(from) && !hlen); /* dont bother with small payloads */ if (len <= skb_tailroom(to)) return skb_copy_bits(from, 0, skb_put(to, len), len); if (hlen) { ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen); if (unlikely(ret)) return ret; len -= hlen; } else { plen = min_t(int, skb_headlen(from), len); if (plen) { page = virt_to_head_page(from->head); offset = from->data - (unsigned char *)page_address(page); __skb_fill_page_desc(to, 0, page, offset, plen); get_page(page); j = 1; len -= plen; } } to->truesize += len + plen; to->len += len + plen; to->data_len += len + plen; if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { skb_tx_error(from); return -ENOMEM; } for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { if (!len) break; skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); len -= skb_shinfo(to)->frags[j].size; skb_frag_ref(to, j); j++; } skb_shinfo(to)->nr_frags = j; return 0; } EXPORT_SYMBOL_GPL(rpl_skb_zerocopy); #endif #endif #ifndef HAVE_SKB_ENSURE_WRITABLE int rpl_skb_ensure_writable(struct sk_buff *skb, int write_len) { if (!pskb_may_pull(skb, write_len)) return -ENOMEM; if (!skb_cloned(skb) || skb_clone_writable(skb, write_len)) return 0; return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); } EXPORT_SYMBOL_GPL(rpl_skb_ensure_writable); #endif #ifndef HAVE_SKB_VLAN_POP /* remove VLAN header from packet and update csum accordingly. */ static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci) { struct vlan_hdr *vhdr; unsigned int offset = skb->data - skb_mac_header(skb); int err; __skb_push(skb, offset); err = skb_ensure_writable(skb, VLAN_ETH_HLEN); if (unlikely(err)) goto pull; skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN); vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); *vlan_tci = ntohs(vhdr->h_vlan_TCI); memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); __skb_pull(skb, VLAN_HLEN); vlan_set_encap_proto(skb, vhdr); skb->mac_header += VLAN_HLEN; if (skb_network_offset(skb) < ETH_HLEN) skb_set_network_header(skb, ETH_HLEN); skb_reset_mac_len(skb); pull: __skb_pull(skb, offset); return err; } int rpl_skb_vlan_pop(struct sk_buff *skb) { u16 vlan_tci; __be16 vlan_proto; int err; if (likely(skb_vlan_tag_present(skb))) { skb->vlan_tci = 0; } else { if (unlikely((skb->protocol != htons(ETH_P_8021Q) && skb->protocol != htons(ETH_P_8021AD)) || skb->len < VLAN_ETH_HLEN)) return 0; err = __skb_vlan_pop(skb, &vlan_tci); if (err) return err; } /* move next vlan tag to hw accel tag */ if (likely((skb->protocol != htons(ETH_P_8021Q) && skb->protocol != htons(ETH_P_8021AD)) || skb->len < VLAN_ETH_HLEN)) return 0; vlan_proto = htons(ETH_P_8021Q); err = __skb_vlan_pop(skb, &vlan_tci); if (unlikely(err)) return err; __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); return 0; } EXPORT_SYMBOL_GPL(rpl_skb_vlan_pop); #endif #ifndef HAVE_SKB_VLAN_PUSH int rpl_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) { if (skb_vlan_tag_present(skb)) { unsigned int offset = skb->data - skb_mac_header(skb); int err; /* __vlan_insert_tag expect skb->data pointing to mac header. * So change skb->data before calling it and change back to * original position later */ __skb_push(skb, offset); err = __vlan_insert_tag(skb, skb->vlan_proto, skb_vlan_tag_get(skb)); if (err) return err; skb->mac_len += VLAN_HLEN; __skb_pull(skb, offset); if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + (2 * ETH_ALEN), VLAN_HLEN, 0)); } __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); return 0; } EXPORT_SYMBOL_GPL(rpl_skb_vlan_push); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) int rpl_pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask) { int err; int inner_mac_offset, inner_nw_offset, inner_transport_offset; inner_mac_offset = skb_inner_mac_offset(skb); inner_nw_offset = skb_inner_network_offset(skb); inner_transport_offset = ovs_skb_inner_transport_offset(skb); #undef pskb_expand_head err = pskb_expand_head(skb, nhead, ntail, gfp_mask); if (err) return err; skb_set_inner_mac_header(skb, inner_mac_offset); skb_set_inner_network_header(skb, inner_nw_offset); skb_set_inner_transport_header(skb, inner_transport_offset); return 0; } EXPORT_SYMBOL(rpl_pskb_expand_head); #endif #ifndef HAVE_KFREE_SKB_LIST void rpl_kfree_skb_list(struct sk_buff *segs) { while (segs) { struct sk_buff *next = segs->next; kfree_skb(segs); segs = next; } } EXPORT_SYMBOL(rpl_kfree_skb_list); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,1,0) #define nf_reset_trace rpl_nf_reset_trace static void nf_reset_trace(struct sk_buff *skb) { #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) skb->nf_trace = 0; #endif } void rpl_skb_scrub_packet(struct sk_buff *skb, bool xnet) { skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) skb->skb_iif = 0; #endif skb->ignore_df = 0; skb_dst_drop(skb); secpath_reset(skb); nf_reset(skb); nf_reset_trace(skb); if (!xnet) return; skb_orphan(skb); skb->mark = 0; } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/dev-openvswitch.c0000644000000000000000000000013213534540071023345 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.525858911 openvswitch-2.5.9/datapath/linux/compat/dev-openvswitch.c0000644000175000017500000000501413534540071025033 0ustar00jpettitjpettit00000000000000#include #include #include #include #ifndef HAVE_DEV_DISABLE_LRO #ifdef NETIF_F_LRO #include /** * dev_disable_lro - disable Large Receive Offload on a device * @dev: device * * Disable Large Receive Offload (LRO) on a net device. Must be * called under RTNL. This is needed if received packets may be * forwarded to another interface. */ void dev_disable_lro(struct net_device *dev) { if (dev->ethtool_ops && dev->ethtool_ops->get_flags && dev->ethtool_ops->set_flags) { u32 flags = dev->ethtool_ops->get_flags(dev); if (flags & ETH_FLAG_LRO) { flags &= ~ETH_FLAG_LRO; dev->ethtool_ops->set_flags(dev, flags); } } WARN_ON(dev->features & NETIF_F_LRO); } #else void dev_disable_lro(struct net_device *dev) { } #endif /* NETIF_F_LRO */ #endif /* HAVE_DEV_DISABLE_LRO */ #if !defined HAVE_NETDEV_RX_HANDLER_REGISTER || \ defined HAVE_RHEL_OVS_HOOK static int nr_bridges; #ifdef HAVE_RHEL_OVS_HOOK int rpl_netdev_rx_handler_register(struct net_device *dev, openvswitch_handle_frame_hook_t *hook, void *rx_handler_data) { nr_bridges++; rcu_assign_pointer(dev->ax25_ptr, rx_handler_data); if (nr_bridges == 1) rcu_assign_pointer(openvswitch_handle_frame_hook, hook); return 0; } EXPORT_SYMBOL_GPL(rpl_netdev_rx_handler_register); #else int rpl_netdev_rx_handler_register(struct net_device *dev, struct sk_buff *(*hook)(struct net_bridge_port *p, struct sk_buff *skb), void *rx_handler_data) { nr_bridges++; if (dev->br_port) return -EBUSY; rcu_assign_pointer(dev->br_port, rx_handler_data); if (nr_bridges == 1) br_handle_frame_hook = hook; return 0; } EXPORT_SYMBOL_GPL(rpl_netdev_rx_handler_register); #endif void rpl_netdev_rx_handler_unregister(struct net_device *dev) { nr_bridges--; #ifdef HAVE_RHEL_OVS_HOOK rcu_assign_pointer(dev->ax25_ptr, NULL); if (nr_bridges) return; rcu_assign_pointer(openvswitch_handle_frame_hook, NULL); #else rcu_assign_pointer(dev->br_port, NULL); if (nr_bridges) return; br_handle_frame_hook = NULL; #endif } EXPORT_SYMBOL_GPL(rpl_netdev_rx_handler_unregister); #endif int rpl_rtnl_delete_link(struct net_device *dev) { const struct rtnl_link_ops *ops; ops = dev->rtnl_link_ops; if (!ops || !ops->dellink) return -EOPNOTSUPP; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34) ops->dellink(dev); #else { LIST_HEAD(list_kill); ops->dellink(dev, &list_kill); unregister_netdevice_many(&list_kill); } #endif return 0; } openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/nf_conntrack_core.c0000644000000000000000000000013113534540071023674 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.545859058 openvswitch-2.5.9/datapath/linux/compat/nf_conntrack_core.c0000644000175000017500000000046213534540071025365 0ustar00jpettitjpettit00000000000000#include #ifndef HAVE_NF_CT_ZONE_INIT #include /* Built-in default zone used e.g. by modules. */ const struct nf_conntrack_zone nf_ct_zone_dflt = { .id = NF_CT_DEFAULT_ZONE_ID, .dir = NF_CT_DEFAULT_ZONE_DIR, }; #endif /* HAVE_NF_CT_ZONE_INIT */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/stt.c0000644000000000000000000000013113534540071021031 xustar0029 mtime=1567801401.27768026 30 atime=1567801402.057685987 30 ctime=1567801425.553859118 openvswitch-2.5.9/datapath/linux/compat/stt.c0000644000175000017500000013443713534540071022534 0ustar00jpettitjpettit00000000000000/* * Stateless TCP Tunnel (STT) vport. * * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gso.h" #include "compat.h" #define STT_NETDEV_VER "0.1" #define STT_DST_PORT 7471 #ifdef OVS_STT #define STT_VER 0 /* @list: Per-net list of STT ports. * @rcv: The callback is called on STT packet recv, STT reassembly can generate * multiple packets, in this case first packet has tunnel outer header, rest * of the packets are inner packet segments with no stt header. * @rcv_data: user data. * @sock: Fake TCP socket for the STT port. */ struct stt_dev { struct net_device *dev; struct net *net; struct list_head next; struct list_head up_next; struct socket *sock; __be16 dst_port; }; #define STT_CSUM_VERIFIED BIT(0) #define STT_CSUM_PARTIAL BIT(1) #define STT_PROTO_IPV4 BIT(2) #define STT_PROTO_TCP BIT(3) #define STT_PROTO_TYPES (STT_PROTO_IPV4 | STT_PROTO_TCP) #define SUPPORTED_GSO_TYPES (SKB_GSO_TCPV4 | SKB_GSO_UDP | SKB_GSO_DODGY | \ SKB_GSO_TCPV6) /* The length and offset of a fragment are encoded in the sequence number. * STT_SEQ_LEN_SHIFT is the left shift needed to store the length. * STT_SEQ_OFFSET_MASK is the mask to extract the offset. */ #define STT_SEQ_LEN_SHIFT 16 #define STT_SEQ_OFFSET_MASK (BIT(STT_SEQ_LEN_SHIFT) - 1) /* The maximum amount of memory used to store packets waiting to be reassembled * on a given CPU. Once this threshold is exceeded we will begin freeing the * least recently used fragments. */ #define REASM_HI_THRESH (4 * 1024 * 1024) /* The target for the high memory evictor. Once we have exceeded * REASM_HI_THRESH, we will continue freeing fragments until we hit * this limit. */ #define REASM_LO_THRESH (3 * 1024 * 1024) /* The length of time a given packet has to be reassembled from the time the * first fragment arrives. Once this limit is exceeded it becomes available * for cleaning. */ #define FRAG_EXP_TIME (30 * HZ) /* Number of hash entries. Each entry has only a single slot to hold a packet * so if there are collisions, we will drop packets. This is allocated * per-cpu and each entry consists of struct pkt_frag. */ #define FRAG_HASH_SHIFT 8 #define FRAG_HASH_ENTRIES BIT(FRAG_HASH_SHIFT) #define FRAG_HASH_SEGS ((sizeof(u32) * 8) / FRAG_HASH_SHIFT) #define CLEAN_PERCPU_INTERVAL (30 * HZ) struct pkt_key { __be32 saddr; __be32 daddr; __be32 pkt_seq; u32 mark; }; struct pkt_frag { struct sk_buff *skbs; unsigned long timestamp; struct list_head lru_node; struct pkt_key key; }; struct stt_percpu { struct flex_array *frag_hash; struct list_head frag_lru; unsigned int frag_mem_used; /* Protect frags table. */ spinlock_t lock; }; struct first_frag { struct sk_buff *last_skb; unsigned int mem_used; u16 tot_len; u16 rcvd_len; bool set_ecn_ce; }; struct frag_skb_cb { u16 offset; /* Only valid for the first skb in the chain. */ struct first_frag first; }; #define FRAG_CB(skb) ((struct frag_skb_cb *)(skb)->cb) /* per-network namespace private data for this module */ struct stt_net { struct list_head stt_list; struct list_head stt_up_list; /* Devices which are in IFF_UP state. */ int n_tunnels; #ifdef HAVE_NF_REGISTER_NET_HOOK bool nf_hook_reg_done; #endif }; static int stt_net_id; static struct stt_percpu __percpu *stt_percpu_data __read_mostly; static u32 frag_hash_seed __read_mostly; /* Protects sock-hash and refcounts. */ static DEFINE_MUTEX(stt_mutex); static int n_tunnels; static DEFINE_PER_CPU(u32, pkt_seq_counter); static void clean_percpu(struct work_struct *work); static DECLARE_DELAYED_WORK(clean_percpu_wq, clean_percpu); static struct stt_dev *stt_find_up_dev(struct net *net, __be16 port) { struct stt_net *sn = net_generic(net, stt_net_id); struct stt_dev *stt_dev; list_for_each_entry_rcu(stt_dev, &sn->stt_up_list, up_next) { if (stt_dev->dst_port == port) return stt_dev; } return NULL; } static __be32 ack_seq(void) { #if NR_CPUS <= 65536 u32 pkt_seq, ack; pkt_seq = this_cpu_read(pkt_seq_counter); ack = pkt_seq << ilog2(NR_CPUS) | smp_processor_id(); this_cpu_inc(pkt_seq_counter); return (__force __be32)ack; #else #error "Support for greater than 64k CPUs not implemented" #endif } static int clear_gso(struct sk_buff *skb) { struct skb_shared_info *shinfo = skb_shinfo(skb); int err; if (shinfo->gso_type == 0 && shinfo->gso_size == 0 && shinfo->gso_segs == 0) return 0; err = skb_unclone(skb, GFP_ATOMIC); if (unlikely(err)) return err; shinfo = skb_shinfo(skb); shinfo->gso_type = 0; shinfo->gso_size = 0; shinfo->gso_segs = 0; return 0; } static struct sk_buff *normalize_frag_list(struct sk_buff *head, struct sk_buff **skbp) { struct sk_buff *skb = *skbp; struct sk_buff *last; do { struct sk_buff *frags; if (skb_shared(skb)) { struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); if (unlikely(!nskb)) return ERR_PTR(-ENOMEM); nskb->next = skb->next; consume_skb(skb); skb = nskb; *skbp = skb; } if (head) { head->len -= skb->len; head->data_len -= skb->len; head->truesize -= skb->truesize; } frags = skb_shinfo(skb)->frag_list; if (frags) { int err; err = skb_unclone(skb, GFP_ATOMIC); if (unlikely(err)) return ERR_PTR(err); last = normalize_frag_list(skb, &frags); if (IS_ERR(last)) return last; skb_shinfo(skb)->frag_list = NULL; last->next = skb->next; skb->next = frags; } else { last = skb; } skbp = &skb->next; } while ((skb = skb->next)); return last; } /* Takes a linked list of skbs, which potentially contain frag_list * (whose members in turn potentially contain frag_lists, etc.) and * converts them into a single linear linked list. */ static int straighten_frag_list(struct sk_buff **skbp) { struct sk_buff *err_skb; err_skb = normalize_frag_list(NULL, skbp); if (IS_ERR(err_skb)) return PTR_ERR(err_skb); return 0; } static void copy_skb_metadata(struct sk_buff *to, struct sk_buff *from) { to->protocol = from->protocol; to->tstamp = from->tstamp; to->priority = from->priority; to->mark = from->mark; to->vlan_tci = from->vlan_tci; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) to->vlan_proto = from->vlan_proto; #endif skb_copy_secmark(to, from); } static void update_headers(struct sk_buff *skb, bool head, unsigned int l4_offset, unsigned int hdr_len, bool ipv4, u32 tcp_seq) { u16 old_len, new_len; __be32 delta; struct tcphdr *tcph; int gso_size; if (ipv4) { struct iphdr *iph = (struct iphdr *)(skb->data + ETH_HLEN); old_len = ntohs(iph->tot_len); new_len = skb->len - ETH_HLEN; iph->tot_len = htons(new_len); ip_send_check(iph); } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + ETH_HLEN); old_len = ntohs(ip6h->payload_len); new_len = skb->len - ETH_HLEN - sizeof(struct ipv6hdr); ip6h->payload_len = htons(new_len); } tcph = (struct tcphdr *)(skb->data + l4_offset); if (!head) { tcph->seq = htonl(tcp_seq); tcph->cwr = 0; } if (skb->next) { tcph->fin = 0; tcph->psh = 0; } delta = htonl(~old_len + new_len); tcph->check = ~csum_fold((__force __wsum)((__force u32)tcph->check + (__force u32)delta)); gso_size = skb_shinfo(skb)->gso_size; if (gso_size && skb->len - hdr_len <= gso_size) BUG_ON(clear_gso(skb)); } static bool can_segment(struct sk_buff *head, bool ipv4, bool tcp, bool csum_partial) { /* If no offloading is in use then we don't have enough information * to process the headers. */ if (!csum_partial) goto linearize; /* Handling UDP packets requires IP fragmentation, which means that * the L4 checksum can no longer be calculated by hardware (since the * fragments are in different packets. If we have to compute the * checksum it's faster just to linearize and large UDP packets are * pretty uncommon anyways, so it's not worth dealing with for now. */ if (!tcp) goto linearize; if (ipv4) { struct iphdr *iph = (struct iphdr *)(head->data + ETH_HLEN); /* It's difficult to get the IP IDs exactly right here due to * varying segment sizes and potentially multiple layers of * segmentation. IP ID isn't important when DF is set and DF * is generally set for TCP packets, so just linearize if it's * not. */ if (!(iph->frag_off & htons(IP_DF))) goto linearize; } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)(head->data + ETH_HLEN); /* Jumbograms require more processing to update and we'll * probably never see them, so just linearize. */ if (ip6h->payload_len == 0) goto linearize; } return true; linearize: return false; } static int copy_headers(struct sk_buff *head, struct sk_buff *frag, int hdr_len) { u16 csum_start; if (skb_cloned(frag) || skb_headroom(frag) < hdr_len) { int extra_head = hdr_len - skb_headroom(frag); extra_head = extra_head > 0 ? extra_head : 0; if (unlikely(pskb_expand_head(frag, extra_head, 0, GFP_ATOMIC))) return -ENOMEM; } memcpy(__skb_push(frag, hdr_len), head->data, hdr_len); csum_start = head->csum_start - skb_headroom(head); frag->csum_start = skb_headroom(frag) + csum_start; frag->csum_offset = head->csum_offset; frag->ip_summed = head->ip_summed; skb_shinfo(frag)->gso_size = skb_shinfo(head)->gso_size; skb_shinfo(frag)->gso_type = skb_shinfo(head)->gso_type; skb_shinfo(frag)->gso_segs = 0; copy_skb_metadata(frag, head); return 0; } static int skb_list_segment(struct sk_buff *head, bool ipv4, int l4_offset) { struct sk_buff *skb; struct tcphdr *tcph; int seg_len; int hdr_len; int tcp_len; u32 seq; if (unlikely(!pskb_may_pull(head, l4_offset + sizeof(*tcph)))) return -ENOMEM; tcph = (struct tcphdr *)(head->data + l4_offset); tcp_len = tcph->doff * 4; hdr_len = l4_offset + tcp_len; if (unlikely((tcp_len < sizeof(struct tcphdr)) || (head->len < hdr_len))) return -EINVAL; if (unlikely(!pskb_may_pull(head, hdr_len))) return -ENOMEM; tcph = (struct tcphdr *)(head->data + l4_offset); /* Update header of each segment. */ seq = ntohl(tcph->seq); seg_len = skb_pagelen(head) - hdr_len; skb = skb_shinfo(head)->frag_list; skb_shinfo(head)->frag_list = NULL; head->next = skb; for (; skb; skb = skb->next) { int err; head->len -= skb->len; head->data_len -= skb->len; head->truesize -= skb->truesize; seq += seg_len; seg_len = skb->len; err = copy_headers(head, skb, hdr_len); if (err) return err; update_headers(skb, false, l4_offset, hdr_len, ipv4, seq); } update_headers(head, true, l4_offset, hdr_len, ipv4, 0); return 0; } static int coalesce_skb(struct sk_buff **headp) { struct sk_buff *frag, *head, *prev; int err; err = straighten_frag_list(headp); if (unlikely(err)) return err; head = *headp; /* Coalesce frag list. */ prev = head; for (frag = head->next; frag; frag = frag->next) { bool headstolen; int delta; if (unlikely(skb_unclone(prev, GFP_ATOMIC))) return -ENOMEM; if (!skb_try_coalesce(prev, frag, &headstolen, &delta)) { prev = frag; continue; } prev->next = frag->next; frag->len = 0; frag->data_len = 0; frag->truesize -= delta; kfree_skb_partial(frag, headstolen); frag = prev; } if (!head->next) return 0; for (frag = head->next; frag; frag = frag->next) { head->len += frag->len; head->data_len += frag->len; head->truesize += frag->truesize; } skb_shinfo(head)->frag_list = head->next; head->next = NULL; return 0; } static int __try_to_segment(struct sk_buff *skb, bool csum_partial, bool ipv4, bool tcp, int l4_offset) { if (can_segment(skb, ipv4, tcp, csum_partial)) return skb_list_segment(skb, ipv4, l4_offset); else return skb_linearize(skb); } static int try_to_segment(struct sk_buff *skb) { struct stthdr *stth = stt_hdr(skb); bool csum_partial = !!(stth->flags & STT_CSUM_PARTIAL); bool ipv4 = !!(stth->flags & STT_PROTO_IPV4); bool tcp = !!(stth->flags & STT_PROTO_TCP); int l4_offset = stth->l4_offset; return __try_to_segment(skb, csum_partial, ipv4, tcp, l4_offset); } static int segment_skb(struct sk_buff **headp, bool csum_partial, bool ipv4, bool tcp, int l4_offset) { int err; err = coalesce_skb(headp); if (err) return err; if (skb_shinfo(*headp)->frag_list) return __try_to_segment(*headp, csum_partial, ipv4, tcp, l4_offset); return 0; } static int __push_stt_header(struct sk_buff *skb, __be64 tun_id, __be16 s_port, __be16 d_port, __be32 saddr, __be32 dst, __be16 l3_proto, u8 l4_proto, int dst_mtu) { int data_len = skb->len + sizeof(struct stthdr) + STT_ETH_PAD; unsigned short encap_mss; struct tcphdr *tcph; struct stthdr *stth; skb_push(skb, STT_HEADER_LEN); skb_reset_transport_header(skb); tcph = tcp_hdr(skb); memset(tcph, 0, STT_HEADER_LEN); stth = stt_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { stth->flags |= STT_CSUM_PARTIAL; stth->l4_offset = skb->csum_start - (skb_headroom(skb) + STT_HEADER_LEN); if (l3_proto == htons(ETH_P_IP)) stth->flags |= STT_PROTO_IPV4; if (l4_proto == IPPROTO_TCP) stth->flags |= STT_PROTO_TCP; stth->mss = htons(skb_shinfo(skb)->gso_size); } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { stth->flags |= STT_CSUM_VERIFIED; } stth->vlan_tci = htons(skb->vlan_tci); skb->vlan_tci = 0; put_unaligned(tun_id, &stth->key); tcph->source = s_port; tcph->dest = d_port; tcph->doff = sizeof(struct tcphdr) / 4; tcph->ack = 1; tcph->psh = 1; tcph->window = htons(USHRT_MAX); tcph->seq = htonl(data_len << STT_SEQ_LEN_SHIFT); tcph->ack_seq = ack_seq(); tcph->check = ~tcp_v4_check(skb->len, saddr, dst, 0); skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct tcphdr, check); skb->ip_summed = CHECKSUM_PARTIAL; encap_mss = dst_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr); if (data_len > encap_mss) { if (unlikely(skb_unclone(skb, GFP_ATOMIC))) return -EINVAL; skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; skb_shinfo(skb)->gso_size = encap_mss; skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(data_len, encap_mss); } else { if (unlikely(clear_gso(skb))) return -EINVAL; } return 0; } static struct sk_buff *push_stt_header(struct sk_buff *head, __be64 tun_id, __be16 s_port, __be16 d_port, __be32 saddr, __be32 dst, __be16 l3_proto, u8 l4_proto, int dst_mtu) { struct sk_buff *skb; if (skb_shinfo(head)->frag_list) { bool ipv4 = (l3_proto == htons(ETH_P_IP)); bool tcp = (l4_proto == IPPROTO_TCP); bool csum_partial = (head->ip_summed == CHECKSUM_PARTIAL); int l4_offset = skb_transport_offset(head); /* Need to call skb_orphan() to report currect true-size. * calling skb_orphan() in this layer is odd but SKB with * frag-list should not be associated with any socket, so * skb-orphan should be no-op. */ skb_orphan(head); if (unlikely(segment_skb(&head, csum_partial, ipv4, tcp, l4_offset))) goto error; } for (skb = head; skb; skb = skb->next) { if (__push_stt_header(skb, tun_id, s_port, d_port, saddr, dst, l3_proto, l4_proto, dst_mtu)) goto error; } return head; error: kfree_skb_list(head); return NULL; } static int stt_can_offload(struct sk_buff *skb, __be16 l3_proto, u8 l4_proto) { if (skb_is_gso(skb) && skb->ip_summed != CHECKSUM_PARTIAL) { int csum_offset; __sum16 *csum; int len; if (l4_proto == IPPROTO_TCP) csum_offset = offsetof(struct tcphdr, check); else if (l4_proto == IPPROTO_UDP) csum_offset = offsetof(struct udphdr, check); else return 0; len = skb->len - skb_transport_offset(skb); csum = (__sum16 *)(skb_transport_header(skb) + csum_offset); if (unlikely(!pskb_may_pull(skb, skb_transport_offset(skb) + csum_offset + sizeof(*csum)))) return -EINVAL; if (l3_proto == htons(ETH_P_IP)) { struct iphdr *iph = ip_hdr(skb); *csum = ~csum_tcpudp_magic(iph->saddr, iph->daddr, len, l4_proto, 0); } else if (l3_proto == htons(ETH_P_IPV6)) { struct ipv6hdr *ip6h = ipv6_hdr(skb); *csum = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, len, l4_proto, 0); } else { return 0; } skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = csum_offset; skb->ip_summed = CHECKSUM_PARTIAL; } if (skb->ip_summed == CHECKSUM_PARTIAL) { /* Assume receiver can only offload TCP/UDP over IPv4/6, * and require 802.1Q VLANs to be accelerated. */ if (l3_proto != htons(ETH_P_IP) && l3_proto != htons(ETH_P_IPV6)) return 0; if (l4_proto != IPPROTO_TCP && l4_proto != IPPROTO_UDP) return 0; /* L4 offset must fit in a 1-byte field. */ if (skb->csum_start - skb_headroom(skb) > 255) return 0; if (skb_shinfo(skb)->gso_type & ~SUPPORTED_GSO_TYPES) return 0; } /* Total size of encapsulated packet must fit in 16 bits. */ if (skb->len + STT_HEADER_LEN + sizeof(struct iphdr) > 65535) return 0; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) if (skb_vlan_tag_present(skb) && skb->vlan_proto != htons(ETH_P_8021Q)) return 0; #endif return 1; } static bool need_linearize(const struct sk_buff *skb) { struct skb_shared_info *shinfo = skb_shinfo(skb); int i; if (unlikely(shinfo->frag_list)) return true; /* Generally speaking we should linearize if there are paged frags. * However, if all of the refcounts are 1 we know nobody else can * change them from underneath us and we can skip the linearization. */ for (i = 0; i < shinfo->nr_frags; i++) if (unlikely(page_count(skb_frag_page(&shinfo->frags[i])) > 1)) return true; return false; } static struct sk_buff *handle_offloads(struct sk_buff *skb, int min_headroom) { int err; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) if (skb_vlan_tag_present(skb) && skb->vlan_proto != htons(ETH_P_8021Q)) { min_headroom += VLAN_HLEN; if (skb_headroom(skb) < min_headroom) { int head_delta = SKB_DATA_ALIGN(min_headroom - skb_headroom(skb) + 16); err = pskb_expand_head(skb, max_t(int, head_delta, 0), 0, GFP_ATOMIC); if (unlikely(err)) goto error; } skb = __vlan_hwaccel_push_inside(skb); if (!skb) { err = -ENOMEM; goto error; } } #endif if (skb_is_gso(skb)) { struct sk_buff *nskb; char cb[sizeof(skb->cb)]; memcpy(cb, skb->cb, sizeof(cb)); nskb = __skb_gso_segment(skb, 0, false); if (IS_ERR(nskb)) { err = PTR_ERR(nskb); goto error; } consume_skb(skb); skb = nskb; while (nskb) { memcpy(nskb->cb, cb, sizeof(cb)); nskb = nskb->next; } } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* Pages aren't locked and could change at any time. * If this happens after we compute the checksum, the * checksum will be wrong. We linearize now to avoid * this problem. */ if (unlikely(need_linearize(skb))) { err = __skb_linearize(skb); if (unlikely(err)) goto error; } err = skb_checksum_help(skb); if (unlikely(err)) goto error; } skb->ip_summed = CHECKSUM_NONE; return skb; error: kfree_skb(skb); return ERR_PTR(err); } static int skb_list_xmit(struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df) { int len = 0; while (skb) { struct sk_buff *next = skb->next; if (next) dst_clone(&rt->dst); skb->next = NULL; len += iptunnel_xmit(NULL, rt, skb, src, dst, IPPROTO_TCP, tos, ttl, df, false); skb = next; } return len; } static u8 parse_ipv6_l4_proto(struct sk_buff *skb) { unsigned int nh_ofs = skb_network_offset(skb); int payload_ofs; struct ipv6hdr *nh; uint8_t nexthdr; __be16 frag_off; if (unlikely(!pskb_may_pull(skb, nh_ofs + sizeof(struct ipv6hdr)))) return 0; nh = ipv6_hdr(skb); nexthdr = nh->nexthdr; payload_ofs = (u8 *)(nh + 1) - skb->data; payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr, &frag_off); if (unlikely(payload_ofs < 0)) return 0; return nexthdr; } static u8 skb_get_l4_proto(struct sk_buff *skb, __be16 l3_proto) { if (l3_proto == htons(ETH_P_IP)) { unsigned int nh_ofs = skb_network_offset(skb); if (unlikely(!pskb_may_pull(skb, nh_ofs + sizeof(struct iphdr)))) return 0; return ip_hdr(skb)->protocol; } else if (l3_proto == htons(ETH_P_IPV6)) { return parse_ipv6_l4_proto(skb); } return 0; } static int stt_xmit_skb(struct sk_buff *skb, struct rtable *rt, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be64 tun_id) { struct ethhdr *eh = eth_hdr(skb); int ret = 0, min_headroom; __be16 inner_l3_proto; u8 inner_l4_proto; inner_l3_proto = eh->h_proto; inner_l4_proto = skb_get_l4_proto(skb, inner_l3_proto); min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + STT_HEADER_LEN + sizeof(struct iphdr); if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { int head_delta = SKB_DATA_ALIGN(min_headroom - skb_headroom(skb) + 16); ret = pskb_expand_head(skb, max_t(int, head_delta, 0), 0, GFP_ATOMIC); if (unlikely(ret)) goto err_free_rt; } ret = stt_can_offload(skb, inner_l3_proto, inner_l4_proto); if (ret < 0) goto err_free_rt; if (!ret) { skb = handle_offloads(skb, min_headroom); if (IS_ERR(skb)) { ret = PTR_ERR(skb); skb = NULL; goto err_free_rt; } } ret = 0; while (skb) { struct sk_buff *next_skb = skb->next; skb->next = NULL; if (next_skb) dst_clone(&rt->dst); /* Push STT and TCP header. */ skb = push_stt_header(skb, tun_id, src_port, dst_port, src, dst, inner_l3_proto, inner_l4_proto, dst_mtu(&rt->dst)); if (unlikely(!skb)) { ip_rt_put(rt); goto next; } /* Push IP header. */ ret += skb_list_xmit(rt, skb, src, dst, tos, ttl, df); next: skb = next_skb; } return ret; err_free_rt: ip_rt_put(rt); kfree_skb(skb); return ret; } netdev_tx_t ovs_stt_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct stt_dev *stt_dev = netdev_priv(dev); struct net *net = stt_dev->net; __be16 dport = stt_dev->dst_port; struct ip_tunnel_key *tun_key; struct ip_tunnel_info *tun_info; struct rtable *rt; struct flowi4 fl; __be16 sport; __be16 df; int err; tun_info = skb_tunnel_info(skb); if (unlikely(!tun_info)) { err = -EINVAL; goto error; } tun_key = &tun_info->key; /* Route lookup */ memset(&fl, 0, sizeof(fl)); fl.daddr = tun_key->u.ipv4.dst; fl.saddr = tun_key->u.ipv4.src; fl.flowi4_tos = RT_TOS(tun_key->tos); fl.flowi4_mark = skb->mark; fl.flowi4_proto = IPPROTO_TCP; rt = ip_route_output_key(net, &fl); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto error; } df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); skb->ignore_df = 1; err = stt_xmit_skb(skb, rt, fl.saddr, tun_key->u.ipv4.dst, tun_key->tos, tun_key->ttl, df, sport, dport, tun_key->tun_id); iptunnel_xmit_stats(err, &dev->stats, (struct pcpu_sw_netstats __percpu *)dev->tstats); return NETDEV_TX_OK; error: kfree_skb(skb); dev->stats.tx_errors++; return err; } EXPORT_SYMBOL(ovs_stt_xmit); static void free_frag(struct stt_percpu *stt_percpu, struct pkt_frag *frag) { stt_percpu->frag_mem_used -= FRAG_CB(frag->skbs)->first.mem_used; kfree_skb_list(frag->skbs); list_del(&frag->lru_node); frag->skbs = NULL; } static void evict_frags(struct stt_percpu *stt_percpu) { while (!list_empty(&stt_percpu->frag_lru) && stt_percpu->frag_mem_used > REASM_LO_THRESH) { struct pkt_frag *frag; frag = list_first_entry(&stt_percpu->frag_lru, struct pkt_frag, lru_node); free_frag(stt_percpu, frag); } } static bool pkt_key_match(struct net *net, const struct pkt_frag *a, const struct pkt_key *b) { return a->key.saddr == b->saddr && a->key.daddr == b->daddr && a->key.pkt_seq == b->pkt_seq && a->key.mark == b->mark && net_eq(dev_net(a->skbs->dev), net); } static u32 pkt_key_hash(const struct net *net, const struct pkt_key *key) { u32 initval = frag_hash_seed ^ (u32)(unsigned long)net ^ key->mark; return jhash_3words((__force u32)key->saddr, (__force u32)key->daddr, (__force u32)key->pkt_seq, initval); } static struct pkt_frag *lookup_frag(struct net *net, struct stt_percpu *stt_percpu, const struct pkt_key *key, u32 hash) { struct pkt_frag *frag, *victim_frag = NULL; int i; for (i = 0; i < FRAG_HASH_SEGS; i++) { frag = flex_array_get(stt_percpu->frag_hash, hash & (FRAG_HASH_ENTRIES - 1)); if (frag->skbs && time_before(jiffies, frag->timestamp + FRAG_EXP_TIME) && pkt_key_match(net, frag, key)) return frag; if (!victim_frag || (victim_frag->skbs && (!frag->skbs || time_before(frag->timestamp, victim_frag->timestamp)))) victim_frag = frag; hash >>= FRAG_HASH_SHIFT; } if (victim_frag->skbs) free_frag(stt_percpu, victim_frag); return victim_frag; } static struct sk_buff *reassemble(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); struct tcphdr *tcph = tcp_hdr(skb); u32 seq = ntohl(tcph->seq); struct stt_percpu *stt_percpu; struct sk_buff *last_skb; struct pkt_frag *frag; struct pkt_key key; int tot_len; u32 hash; tot_len = seq >> STT_SEQ_LEN_SHIFT; FRAG_CB(skb)->offset = seq & STT_SEQ_OFFSET_MASK; if (unlikely(skb->len == 0)) goto out_free; if (unlikely(FRAG_CB(skb)->offset + skb->len > tot_len)) goto out_free; if (tot_len == skb->len) goto out; key.saddr = iph->saddr; key.daddr = iph->daddr; key.pkt_seq = tcph->ack_seq; key.mark = skb->mark; hash = pkt_key_hash(dev_net(skb->dev), &key); stt_percpu = per_cpu_ptr(stt_percpu_data, smp_processor_id()); spin_lock(&stt_percpu->lock); if (unlikely(stt_percpu->frag_mem_used + skb->truesize > REASM_HI_THRESH)) evict_frags(stt_percpu); frag = lookup_frag(dev_net(skb->dev), stt_percpu, &key, hash); if (!frag->skbs) { frag->skbs = skb; frag->key = key; frag->timestamp = jiffies; FRAG_CB(skb)->first.last_skb = skb; FRAG_CB(skb)->first.mem_used = skb->truesize; FRAG_CB(skb)->first.tot_len = tot_len; FRAG_CB(skb)->first.rcvd_len = skb->len; FRAG_CB(skb)->first.set_ecn_ce = false; list_add_tail(&frag->lru_node, &stt_percpu->frag_lru); stt_percpu->frag_mem_used += skb->truesize; skb = NULL; goto unlock; } /* Optimize for the common case where fragments are received in-order * and not overlapping. */ last_skb = FRAG_CB(frag->skbs)->first.last_skb; if (likely(FRAG_CB(last_skb)->offset + last_skb->len == FRAG_CB(skb)->offset)) { last_skb->next = skb; FRAG_CB(frag->skbs)->first.last_skb = skb; } else { struct sk_buff *prev = NULL, *next; for (next = frag->skbs; next; next = next->next) { if (FRAG_CB(next)->offset >= FRAG_CB(skb)->offset) break; prev = next; } /* Overlapping fragments aren't allowed. We shouldn't start * before the end of the previous fragment. */ if (prev && FRAG_CB(prev)->offset + prev->len > FRAG_CB(skb)->offset) goto unlock_free; /* We also shouldn't end after the beginning of the next * fragment. */ if (next && FRAG_CB(skb)->offset + skb->len > FRAG_CB(next)->offset) goto unlock_free; if (prev) { prev->next = skb; } else { FRAG_CB(skb)->first = FRAG_CB(frag->skbs)->first; frag->skbs = skb; } if (next) skb->next = next; else FRAG_CB(frag->skbs)->first.last_skb = skb; } FRAG_CB(frag->skbs)->first.set_ecn_ce |= INET_ECN_is_ce(iph->tos); FRAG_CB(frag->skbs)->first.rcvd_len += skb->len; FRAG_CB(frag->skbs)->first.mem_used += skb->truesize; stt_percpu->frag_mem_used += skb->truesize; if (FRAG_CB(frag->skbs)->first.tot_len == FRAG_CB(frag->skbs)->first.rcvd_len) { struct sk_buff *frag_head = frag->skbs; frag_head->tstamp = skb->tstamp; if (FRAG_CB(frag_head)->first.set_ecn_ce) INET_ECN_set_ce(frag_head); list_del(&frag->lru_node); stt_percpu->frag_mem_used -= FRAG_CB(frag_head)->first.mem_used; frag->skbs = NULL; skb = frag_head; } else { list_move_tail(&frag->lru_node, &stt_percpu->frag_lru); skb = NULL; } goto unlock; unlock_free: kfree_skb(skb); skb = NULL; unlock: spin_unlock(&stt_percpu->lock); return skb; out_free: kfree_skb(skb); skb = NULL; out: return skb; } static bool validate_checksum(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); if (skb_csum_unnecessary(skb)) return true; if (skb->ip_summed == CHECKSUM_COMPLETE && !tcp_v4_check(skb->len, iph->saddr, iph->daddr, skb->csum)) return true; skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, skb->len, IPPROTO_TCP, 0); return __tcp_checksum_complete(skb) == 0; } static bool set_offloads(struct sk_buff *skb) { struct stthdr *stth = stt_hdr(skb); unsigned short gso_type; int l3_header_size; int l4_header_size; u16 csum_offset; u8 proto_type; if (stth->vlan_tci) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(stth->vlan_tci)); if (!(stth->flags & STT_CSUM_PARTIAL)) { if (stth->flags & STT_CSUM_VERIFIED) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; return clear_gso(skb) == 0; } proto_type = stth->flags & STT_PROTO_TYPES; switch (proto_type) { case (STT_PROTO_IPV4 | STT_PROTO_TCP): /* TCP/IPv4 */ csum_offset = offsetof(struct tcphdr, check); gso_type = SKB_GSO_TCPV4; l3_header_size = sizeof(struct iphdr); l4_header_size = sizeof(struct tcphdr); skb->protocol = htons(ETH_P_IP); break; case STT_PROTO_TCP: /* TCP/IPv6 */ csum_offset = offsetof(struct tcphdr, check); gso_type = SKB_GSO_TCPV6; l3_header_size = sizeof(struct ipv6hdr); l4_header_size = sizeof(struct tcphdr); skb->protocol = htons(ETH_P_IPV6); break; case STT_PROTO_IPV4: /* UDP/IPv4 */ csum_offset = offsetof(struct udphdr, check); gso_type = SKB_GSO_UDP; l3_header_size = sizeof(struct iphdr); l4_header_size = sizeof(struct udphdr); skb->protocol = htons(ETH_P_IP); break; default: /* UDP/IPv6 */ csum_offset = offsetof(struct udphdr, check); gso_type = SKB_GSO_UDP; l3_header_size = sizeof(struct ipv6hdr); l4_header_size = sizeof(struct udphdr); skb->protocol = htons(ETH_P_IPV6); } if (unlikely(stth->l4_offset < ETH_HLEN + l3_header_size)) return false; if (unlikely(!pskb_may_pull(skb, stth->l4_offset + l4_header_size))) return false; stth = stt_hdr(skb); skb->csum_start = skb_headroom(skb) + stth->l4_offset; skb->csum_offset = csum_offset; skb->ip_summed = CHECKSUM_PARTIAL; if (stth->mss) { if (unlikely(skb_unclone(skb, GFP_ATOMIC))) return false; skb_shinfo(skb)->gso_type = gso_type | SKB_GSO_DODGY; skb_shinfo(skb)->gso_size = ntohs(stth->mss); skb_shinfo(skb)->gso_segs = 0; } else { if (unlikely(clear_gso(skb))) return false; } return true; } static void rcv_list(struct net_device *dev, struct sk_buff *skb, struct metadata_dst *tun_dst) { struct sk_buff *next; do { next = skb->next; skb->next = NULL; if (next) { ovs_dst_hold((struct dst_entry *)tun_dst); ovs_skb_dst_set(next, (struct dst_entry *)tun_dst); } ovs_ip_tunnel_rcv(dev, skb, tun_dst); } while ((skb = next)); } #ifndef HAVE_METADATA_DST static int __stt_rcv(struct stt_dev *stt_dev, struct sk_buff *skb) { struct metadata_dst tun_dst; ovs_ip_tun_rx_dst(&tun_dst.u.tun_info, skb, TUNNEL_KEY | TUNNEL_CSUM, get_unaligned(&stt_hdr(skb)->key), 0); tun_dst.u.tun_info.key.tp_src = tcp_hdr(skb)->source; tun_dst.u.tun_info.key.tp_dst = tcp_hdr(skb)->dest; rcv_list(stt_dev->dev, skb, &tun_dst); return 0; } #else static int __stt_rcv(struct stt_dev *stt_dev, struct sk_buff *skb) { struct metadata_dst *tun_dst; __be16 flags; __be64 tun_id; flags = TUNNEL_KEY | TUNNEL_CSUM; tun_id = get_unaligned(&stt_hdr(skb)->key); tun_dst = ip_tun_rx_dst(skb, flags, tun_id, 0); if (!tun_dst) return -ENOMEM; tun_dst->u.tun_info.key.tp_src = tcp_hdr(skb)->source; tun_dst->u.tun_info.key.tp_dst = tcp_hdr(skb)->dest; rcv_list(stt_dev->dev, skb, tun_dst); return 0; } #endif static void stt_rcv(struct stt_dev *stt_dev, struct sk_buff *skb) { int err; if (unlikely(!validate_checksum(skb))) goto drop; __skb_pull(skb, sizeof(struct tcphdr)); skb = reassemble(skb); if (!skb) return; if (skb->next && coalesce_skb(&skb)) goto drop; err = iptunnel_pull_header(skb, sizeof(struct stthdr) + STT_ETH_PAD, htons(ETH_P_TEB)); if (unlikely(err)) goto drop; if (unlikely(stt_hdr(skb)->version != 0)) goto drop; if (unlikely(!set_offloads(skb))) goto drop; if (skb_shinfo(skb)->frag_list && try_to_segment(skb)) goto drop; err = __stt_rcv(stt_dev, skb); if (err) goto drop; return; drop: /* Consume bad packet */ kfree_skb_list(skb); stt_dev->dev->stats.rx_errors++; } static void tcp_sock_release(struct socket *sock) { kernel_sock_shutdown(sock, SHUT_RDWR); sock_release(sock); } static int tcp_sock_create4(struct net *net, __be16 port, struct socket **sockp) { struct sockaddr_in tcp_addr; struct socket *sock = NULL; int err; err = sock_create_kern(net, AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); if (err < 0) goto error; memset(&tcp_addr, 0, sizeof(tcp_addr)); tcp_addr.sin_family = AF_INET; tcp_addr.sin_addr.s_addr = htonl(INADDR_ANY); tcp_addr.sin_port = port; err = kernel_bind(sock, (struct sockaddr *)&tcp_addr, sizeof(tcp_addr)); if (err < 0) goto error; *sockp = sock; return 0; error: if (sock) tcp_sock_release(sock); *sockp = NULL; return err; } static void schedule_clean_percpu(void) { schedule_delayed_work(&clean_percpu_wq, CLEAN_PERCPU_INTERVAL); } static void clean_percpu(struct work_struct *work) { int i; for_each_possible_cpu(i) { struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i); int j; for (j = 0; j < FRAG_HASH_ENTRIES; j++) { struct pkt_frag *frag; frag = flex_array_get(stt_percpu->frag_hash, j); if (!frag->skbs || time_before(jiffies, frag->timestamp + FRAG_EXP_TIME)) continue; spin_lock_bh(&stt_percpu->lock); if (frag->skbs && time_after(jiffies, frag->timestamp + FRAG_EXP_TIME)) free_frag(stt_percpu, frag); spin_unlock_bh(&stt_percpu->lock); } } schedule_clean_percpu(); } #ifdef HAVE_NF_HOOKFN_ARG_OPS #define FIRST_PARAM const struct nf_hook_ops *ops #else #define FIRST_PARAM unsigned int hooknum #endif #ifdef HAVE_NF_HOOK_STATE #if RHEL_RELEASE_CODE > RHEL_RELEASE_VERSION(7,0) /* RHEL nfhook hacks. */ #ifndef __GENKSYMS__ #define LAST_PARAM const struct net_device *in, const struct net_device *out, \ const struct nf_hook_state *state #else #define LAST_PARAM const struct net_device *in, const struct net_device *out, \ int (*okfn)(struct sk_buff *) #endif #else #define LAST_PARAM const struct nf_hook_state *state #endif #else #define LAST_PARAM const struct net_device *in, const struct net_device *out, \ int (*okfn)(struct sk_buff *) #endif static unsigned int nf_ip_hook(FIRST_PARAM, struct sk_buff *skb, LAST_PARAM) { struct stt_dev *stt_dev; int ip_hdr_len; if (ip_hdr(skb)->protocol != IPPROTO_TCP) return NF_ACCEPT; ip_hdr_len = ip_hdrlen(skb); if (unlikely(!pskb_may_pull(skb, ip_hdr_len + sizeof(struct tcphdr)))) return NF_ACCEPT; skb_set_transport_header(skb, ip_hdr_len); stt_dev = stt_find_up_dev(dev_net(skb->dev), tcp_hdr(skb)->dest); if (!stt_dev) return NF_ACCEPT; __skb_pull(skb, ip_hdr_len); stt_rcv(stt_dev, skb); return NF_STOLEN; } static struct nf_hook_ops nf_hook_ops __read_mostly = { .hook = nf_ip_hook, .owner = THIS_MODULE, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN, .priority = INT_MAX, }; static int stt_start(struct net *net) { struct stt_net *sn = net_generic(net, stt_net_id); int err; int i; if (n_tunnels) { n_tunnels++; return 0; } get_random_bytes(&frag_hash_seed, sizeof(u32)); stt_percpu_data = alloc_percpu(struct stt_percpu); if (!stt_percpu_data) { err = -ENOMEM; goto error; } for_each_possible_cpu(i) { struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i); struct flex_array *frag_hash; spin_lock_init(&stt_percpu->lock); INIT_LIST_HEAD(&stt_percpu->frag_lru); get_random_bytes(&per_cpu(pkt_seq_counter, i), sizeof(u32)); frag_hash = flex_array_alloc(sizeof(struct pkt_frag), FRAG_HASH_ENTRIES, GFP_KERNEL | __GFP_ZERO); if (!frag_hash) { err = -ENOMEM; goto free_percpu; } stt_percpu->frag_hash = frag_hash; err = flex_array_prealloc(stt_percpu->frag_hash, 0, FRAG_HASH_ENTRIES, GFP_KERNEL | __GFP_ZERO); if (err) goto free_percpu; } schedule_clean_percpu(); n_tunnels++; if (sn->n_tunnels) { sn->n_tunnels++; return 0; } #ifdef HAVE_NF_REGISTER_NET_HOOK /* On kernel which support per net nf-hook, nf_register_hook() takes * rtnl-lock, which results in dead lock in stt-dev-create. Therefore * use this new API. */ if (sn->nf_hook_reg_done) goto out; err = nf_register_net_hook(net, &nf_hook_ops); if (!err) sn->nf_hook_reg_done = true; #else /* Register STT only on very first STT device addition. */ if (!list_empty(&nf_hook_ops.list)) goto out; err = nf_register_hook(&nf_hook_ops); #endif if (err) goto dec_n_tunnel; out: sn->n_tunnels++; return 0; dec_n_tunnel: n_tunnels--; free_percpu: for_each_possible_cpu(i) { struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i); if (stt_percpu->frag_hash) flex_array_free(stt_percpu->frag_hash); } free_percpu(stt_percpu_data); error: return err; } static void stt_cleanup(struct net *net) { struct stt_net *sn = net_generic(net, stt_net_id); int i; sn->n_tunnels--; if (sn->n_tunnels) goto out; out: n_tunnels--; if (n_tunnels) return; cancel_delayed_work_sync(&clean_percpu_wq); for_each_possible_cpu(i) { struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i); int j; for (j = 0; j < FRAG_HASH_ENTRIES; j++) { struct pkt_frag *frag; frag = flex_array_get(stt_percpu->frag_hash, j); kfree_skb_list(frag->skbs); } flex_array_free(stt_percpu->frag_hash); } free_percpu(stt_percpu_data); } static netdev_tx_t stt_dev_xmit(struct sk_buff *skb, struct net_device *dev) { #ifdef HAVE_METADATA_DST return ovs_stt_xmit(skb); #else /* Drop All packets coming from networking stack. OVS-CB is * not initialized for these packets. */ dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; #endif } /* Setup stats when device is created */ static int stt_init(struct net_device *dev) { dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; } static void stt_uninit(struct net_device *dev) { free_percpu(dev->tstats); } static int stt_open(struct net_device *dev) { struct stt_dev *stt = netdev_priv(dev); struct net *net = stt->net; struct stt_net *sn = net_generic(net, stt_net_id); int err; err = stt_start(net); if (err) return err; err = tcp_sock_create4(net, stt->dst_port, &stt->sock); if (err) return err; list_add_rcu(&stt->up_next, &sn->stt_up_list); return 0; } static int stt_stop(struct net_device *dev) { struct stt_dev *stt_dev = netdev_priv(dev); struct net *net = stt_dev->net; list_del_rcu(&stt_dev->up_next); synchronize_net(); tcp_sock_release(stt_dev->sock); stt_dev->sock = NULL; stt_cleanup(net); return 0; } static int __stt_change_mtu(struct net_device *dev, int new_mtu, bool strict) { int max_mtu = IP_MAX_MTU - STT_HEADER_LEN - sizeof(struct iphdr) - dev->hard_header_len; if (new_mtu < 68) return -EINVAL; if (new_mtu > max_mtu) { if (strict) return -EINVAL; new_mtu = max_mtu; } dev->mtu = new_mtu; return 0; } static int stt_change_mtu(struct net_device *dev, int new_mtu) { return __stt_change_mtu(dev, new_mtu, true); } static const struct net_device_ops stt_netdev_ops = { .ndo_init = stt_init, .ndo_uninit = stt_uninit, .ndo_open = stt_open, .ndo_stop = stt_stop, .ndo_start_xmit = stt_dev_xmit, .ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_change_mtu = stt_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; static void stt_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { strlcpy(drvinfo->version, STT_NETDEV_VER, sizeof(drvinfo->version)); strlcpy(drvinfo->driver, "stt", sizeof(drvinfo->driver)); } static const struct ethtool_ops stt_ethtool_ops = { .get_drvinfo = stt_get_drvinfo, .get_link = ethtool_op_get_link, }; /* Info for udev, that this is a virtual tunnel endpoint */ static struct device_type stt_type = { .name = "stt", }; /* Initialize the device structure. */ static void stt_setup(struct net_device *dev) { ether_setup(dev); dev->netdev_ops = &stt_netdev_ops; dev->ethtool_ops = &stt_ethtool_ops; dev->destructor = free_netdev; SET_NETDEV_DEVTYPE(dev, &stt_type); dev->features |= NETIF_F_LLTX | NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; #ifdef HAVE_METADATA_DST netif_keep_dst(dev); #endif dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; eth_hw_addr_random(dev); } static const struct nla_policy stt_policy[IFLA_STT_MAX + 1] = { [IFLA_STT_PORT] = { .type = NLA_U16 }, }; static int stt_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) return -EINVAL; if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) return -EADDRNOTAVAIL; } return 0; } static struct stt_dev *find_dev(struct net *net, __be16 dst_port) { struct stt_net *sn = net_generic(net, stt_net_id); struct stt_dev *dev; list_for_each_entry(dev, &sn->stt_list, next) { if (dev->dst_port == dst_port) return dev; } return NULL; } static int stt_configure(struct net *net, struct net_device *dev, __be16 dst_port) { struct stt_net *sn = net_generic(net, stt_net_id); struct stt_dev *stt = netdev_priv(dev); int err; stt->net = net; stt->dev = dev; stt->dst_port = dst_port; if (find_dev(net, dst_port)) return -EBUSY; err = __stt_change_mtu(dev, IP_MAX_MTU, false); if (err) return err; err = register_netdevice(dev); if (err) return err; list_add(&stt->next, &sn->stt_list); return 0; } static int stt_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { __be16 dst_port = htons(STT_DST_PORT); if (data[IFLA_STT_PORT]) dst_port = nla_get_be16(data[IFLA_STT_PORT]); return stt_configure(net, dev, dst_port); } static void stt_dellink(struct net_device *dev, struct list_head *head) { struct stt_dev *stt = netdev_priv(dev); list_del(&stt->next); unregister_netdevice_queue(dev, head); } static size_t stt_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__be32)); /* IFLA_STT_PORT */ } static int stt_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct stt_dev *stt = netdev_priv(dev); if (nla_put_be16(skb, IFLA_STT_PORT, stt->dst_port)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } static struct rtnl_link_ops stt_link_ops __read_mostly = { .kind = "stt", .maxtype = IFLA_STT_MAX, .policy = stt_policy, .priv_size = sizeof(struct stt_dev), .setup = stt_setup, .validate = stt_validate, .newlink = stt_newlink, .dellink = stt_dellink, .get_size = stt_get_size, .fill_info = stt_fill_info, }; struct net_device *ovs_stt_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port) { struct nlattr *tb[IFLA_MAX + 1]; struct net_device *dev; int err; memset(tb, 0, sizeof(tb)); dev = rtnl_create_link(net, (char *) name, name_assign_type, &stt_link_ops, tb); if (IS_ERR(dev)) return dev; err = stt_configure(net, dev, htons(dst_port)); if (err) { free_netdev(dev); return ERR_PTR(err); } return dev; } EXPORT_SYMBOL_GPL(ovs_stt_dev_create_fb); static int stt_init_net(struct net *net) { struct stt_net *sn = net_generic(net, stt_net_id); INIT_LIST_HEAD(&sn->stt_list); INIT_LIST_HEAD(&sn->stt_up_list); #ifdef HAVE_NF_REGISTER_NET_HOOK sn->nf_hook_reg_done = false; #endif return 0; } static void stt_exit_net(struct net *net) { struct stt_net *sn = net_generic(net, stt_net_id); struct stt_dev *stt, *next; struct net_device *dev, *aux; LIST_HEAD(list); #ifdef HAVE_NF_REGISTER_NET_HOOK /* Ideally this should be done from stt_stop(), But on some kernels * nf-unreg operation needs RTNL-lock, which can cause deallock. * So it is done from here. */ if (sn->nf_hook_reg_done) nf_unregister_net_hook(net, &nf_hook_ops); #endif rtnl_lock(); /* gather any stt devices that were moved into this ns */ for_each_netdev_safe(net, dev, aux) if (dev->rtnl_link_ops == &stt_link_ops) unregister_netdevice_queue(dev, &list); list_for_each_entry_safe(stt, next, &sn->stt_list, next) { /* If stt->dev is in the same netns, it was already added * to the stt by the previous loop. */ if (!net_eq(dev_net(stt->dev), net)) unregister_netdevice_queue(stt->dev, &list); } /* unregister the devices gathered above */ unregister_netdevice_many(&list); rtnl_unlock(); } static struct pernet_operations stt_net_ops = { .init = stt_init_net, .exit = stt_exit_net, .id = &stt_net_id, .size = sizeof(struct stt_net), }; int stt_init_module(void) { int rc; rc = register_pernet_subsys(&stt_net_ops); if (rc) goto out1; rc = rtnl_link_register(&stt_link_ops); if (rc) goto out2; INIT_LIST_HEAD(&nf_hook_ops.list); pr_info("STT tunneling driver\n"); return 0; out2: unregister_pernet_subsys(&stt_net_ops); out1: return rc; } void stt_cleanup_module(void) { #ifndef HAVE_NF_REGISTER_NET_HOOK if (!list_empty(&nf_hook_ops.list)) nf_unregister_hook(&nf_hook_ops); #endif rtnl_link_unregister(&stt_link_ops); unregister_pernet_subsys(&stt_net_ops); } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/netdevice.c0000644000000000000000000000013113534540071022165 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.541859029 openvswitch-2.5.9/datapath/linux/compat/netdevice.c0000644000175000017500000001256713534540071023667 0ustar00jpettitjpettit00000000000000#include #include #include #include "gso.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) #ifndef HAVE_CAN_CHECKSUM_PROTOCOL static bool can_checksum_protocol(netdev_features_t features, __be16 protocol) { return ((features & NETIF_F_GEN_CSUM) || ((features & NETIF_F_V4_CSUM) && protocol == htons(ETH_P_IP)) || ((features & NETIF_F_V6_CSUM) && protocol == htons(ETH_P_IPV6)) || ((features & NETIF_F_FCOE_CRC) && protocol == htons(ETH_P_FCOE))); } #endif static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb) { #ifdef CONFIG_HIGHMEM int i; if (dev->features & NETIF_F_HIGHDMA) return 0; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) if (PageHighMem(skb_shinfo(skb)->frags[i].page)) return 1; #endif return 0; } static netdev_features_t harmonize_features(struct sk_buff *skb, __be16 protocol, netdev_features_t features) { if (!can_checksum_protocol(features, protocol)) { features &= ~NETIF_F_ALL_CSUM; features &= ~NETIF_F_SG; } else if (illegal_highdma(skb->dev, skb)) { features &= ~NETIF_F_SG; } return features; } netdev_features_t rpl_netif_skb_features(struct sk_buff *skb) { unsigned long vlan_features = skb->dev->vlan_features; __be16 protocol = skb->protocol; netdev_features_t features = skb->dev->features; if (protocol == htons(ETH_P_8021Q)) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; protocol = veh->h_vlan_encapsulated_proto; } else if (!skb_vlan_tag_present(skb)) { return harmonize_features(skb, protocol, features); } features &= (vlan_features | NETIF_F_HW_VLAN_TX); if (protocol != htons(ETH_P_8021Q)) { return harmonize_features(skb, protocol, features); } else { features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_TX; return harmonize_features(skb, protocol, features); } } EXPORT_SYMBOL_GPL(rpl_netif_skb_features); #endif /* kernel version < 2.6.38 */ #ifdef OVS_USE_COMPAT_GSO_SEGMENTATION struct sk_buff *rpl__skb_gso_segment(struct sk_buff *skb, netdev_features_t features, bool tx_path) { int vlan_depth = ETH_HLEN; __be16 type = skb->protocol; __be16 skb_proto; struct sk_buff *skb_gso; while (type == htons(ETH_P_8021Q)) { struct vlan_hdr *vh; if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) return ERR_PTR(-EINVAL); vh = (struct vlan_hdr *)(skb->data + vlan_depth); type = vh->h_vlan_encapsulated_proto; vlan_depth += VLAN_HLEN; } if (eth_p_mpls(type)) type = ovs_skb_get_inner_protocol(skb); /* this hack needed to get regular skb_gso_segment() */ skb_proto = skb->protocol; skb->protocol = type; #ifdef HAVE___SKB_GSO_SEGMENT #undef __skb_gso_segment skb_gso = __skb_gso_segment(skb, features, tx_path); #else #undef skb_gso_segment skb_gso = skb_gso_segment(skb, features); #endif skb->protocol = skb_proto; return skb_gso; } EXPORT_SYMBOL_GPL(rpl__skb_gso_segment); #endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */ #ifdef HAVE_UDP_OFFLOAD #if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) struct sk_buff **rpl_eth_gro_receive(struct sk_buff **head, struct sk_buff *skb) { struct sk_buff *p, **pp = NULL; struct ethhdr *eh, *eh2; unsigned int hlen, off_eth; const struct packet_offload *ptype; __be16 type; int flush = 1; off_eth = skb_gro_offset(skb); hlen = off_eth + sizeof(*eh); eh = skb_gro_header_fast(skb, off_eth); if (skb_gro_header_hard(skb, hlen)) { eh = skb_gro_header_slow(skb, hlen, off_eth); if (unlikely(!eh)) goto out; } flush = 0; for (p = *head; p; p = p->next) { if (!NAPI_GRO_CB(p)->same_flow) continue; eh2 = (struct ethhdr *)(p->data + off_eth); if (compare_ether_header(eh, eh2)) { NAPI_GRO_CB(p)->same_flow = 0; continue; } } type = eh->h_proto; rcu_read_lock(); ptype = gro_find_receive_by_type(type); if (ptype == NULL) { flush = 1; goto out_unlock; } skb_gro_pull(skb, sizeof(*eh)); skb_gro_postpull_rcsum(skb, eh, sizeof(*eh)); pp = ptype->callbacks.gro_receive(head, skb); out_unlock: rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; return pp; } int rpl_eth_gro_complete(struct sk_buff *skb, int nhoff) { struct ethhdr *eh = (struct ethhdr *)(skb->data + nhoff); __be16 type = eh->h_proto; struct packet_offload *ptype; int err = -ENOSYS; if (skb->encapsulation) skb_set_inner_mac_header(skb, nhoff); rcu_read_lock(); ptype = gro_find_complete_by_type(type); if (ptype != NULL) err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(struct ethhdr)); rcu_read_unlock(); return err; } #endif #endif /* HAVE_UDP_OFFLOAD */ #ifndef HAVE_RTNL_LINK_STATS64 #undef dev_get_stats struct rtnl_link_stats64 *rpl_dev_get_stats(struct net_device *dev, struct rtnl_link_stats64 *storage) { const struct net_device_stats *stats = dev_get_stats(dev); #define copy(s) storage->s = stats->s copy(rx_packets); copy(tx_packets); copy(rx_bytes); copy(tx_bytes); copy(rx_errors); copy(tx_errors); copy(rx_dropped); copy(tx_dropped); copy(multicast); copy(collisions); copy(rx_length_errors); copy(rx_over_errors); copy(rx_crc_errors); copy(rx_frame_errors); copy(rx_fifo_errors); copy(rx_missed_errors); copy(tx_aborted_errors); copy(tx_carrier_errors); copy(tx_fifo_errors); copy(tx_heartbeat_errors); copy(tx_window_errors); copy(rx_compressed); copy(tx_compressed); #undef copy return storage; } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/gre.c0000644000000000000000000000013113534540071020774 xustar0030 mtime=1567801401.253680083 30 atime=1567801402.057685987 29 ctime=1567801425.52985894 openvswitch-2.5.9/datapath/linux/compat/gre.c0000644000175000017500000001524013534540071022465 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gso.h" #ifndef HAVE_METADATA_DST #if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX) #ifndef HAVE_GRE_HANDLE_OFFLOADS #ifndef HAVE_GRE_CISCO_REGISTER #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) #define GREPROTO_CISCO 0 #define GREPROTO_MAX 1 struct gre_protocol { int (*handler)(struct sk_buff *skb); }; static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; static int gre_rcv(struct sk_buff *skb) { const struct gre_protocol *proto; u8 ver; int ret; if (!pskb_may_pull(skb, 12)) goto drop; ver = skb->data[1] & 0x7f; if (ver >= GREPROTO_MAX) goto drop; rcu_read_lock(); proto = rcu_dereference(gre_proto[ver]); if (!proto || !proto->handler) goto drop_unlock; ret = proto->handler(skb); rcu_read_unlock(); return ret; drop_unlock: rcu_read_unlock(); drop: kfree_skb(skb); return NET_RX_DROP; } static const struct net_protocol net_gre_protocol = { .handler = gre_rcv, .netns_ok = 1, }; static int gre_add_protocol(const struct gre_protocol *proto, u8 version) { if (version >= GREPROTO_MAX) return -EINVAL; if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { pr_err("%s: cannot register gre protocol handler\n", __func__); return -EAGAIN; } return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? 0 : -EBUSY; } static int gre_del_protocol(const struct gre_protocol *proto, u8 version) { int ret; if (version >= GREPROTO_MAX) return -EINVAL; ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? 0 : -EBUSY; if (ret) return ret; synchronize_net(); ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); if (ret) return ret; return 0; } #endif static __sum16 check_checksum(struct sk_buff *skb) { __sum16 csum = 0; switch (skb->ip_summed) { case CHECKSUM_COMPLETE: csum = csum_fold(skb->csum); if (!csum) break; /* Fall through. */ case CHECKSUM_NONE: skb->csum = 0; csum = __skb_checksum_complete(skb); skb->ip_summed = CHECKSUM_COMPLETE; break; } return csum; } #define ip_gre_calc_hlen rpl_ip_gre_calc_hlen static int ip_gre_calc_hlen(__be16 o_flags) { int addend = 4; if (o_flags & TUNNEL_CSUM) addend += 4; if (o_flags & TUNNEL_KEY) addend += 4; if (o_flags & TUNNEL_SEQ) addend += 4; return addend; } #define gre_flags_to_tnl_flags rpl_gre_flags_to_tnl_flags static __be16 gre_flags_to_tnl_flags(__be16 flags) { __be16 tflags = 0; if (flags & GRE_CSUM) tflags |= TUNNEL_CSUM; if (flags & GRE_ROUTING) tflags |= TUNNEL_ROUTING; if (flags & GRE_KEY) tflags |= TUNNEL_KEY; if (flags & GRE_SEQ) tflags |= TUNNEL_SEQ; if (flags & GRE_STRICT) tflags |= TUNNEL_STRICT; if (flags & GRE_REC) tflags |= TUNNEL_REC; if (flags & GRE_VERSION) tflags |= TUNNEL_VERSION; return tflags; } static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, bool *csum_err) { unsigned int ip_hlen = ip_hdrlen(skb); struct gre_base_hdr *greh; __be32 *options; int hdr_len; if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr)))) return -EINVAL; greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) return -EINVAL; tpi->flags = gre_flags_to_tnl_flags(greh->flags); hdr_len = ip_gre_calc_hlen(tpi->flags); if (!pskb_may_pull(skb, hdr_len)) return -EINVAL; greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); tpi->proto = greh->protocol; options = (__be32 *)(greh + 1); if (greh->flags & GRE_CSUM) { if (check_checksum(skb)) { *csum_err = true; return -EINVAL; } options++; } if (greh->flags & GRE_KEY) { tpi->key = *options; options++; } else tpi->key = 0; if (unlikely(greh->flags & GRE_SEQ)) { tpi->seq = *options; options++; } else tpi->seq = 0; /* WCCP version 1 and 2 protocol decoding. * - Change protocol to IP * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header */ if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) { tpi->proto = htons(ETH_P_IP); if ((*(u8 *)options & 0xF0) != 0x40) { hdr_len += 4; if (!pskb_may_pull(skb, hdr_len)) return -EINVAL; } } return iptunnel_pull_header(skb, hdr_len, tpi->proto); } static struct gre_cisco_protocol __rcu *gre_cisco_proto; static int gre_cisco_rcv(struct sk_buff *skb) { struct tnl_ptk_info tpi; bool csum_err = false; struct gre_cisco_protocol *proto; rcu_read_lock(); proto = rcu_dereference(gre_cisco_proto); if (!proto) goto drop; if (parse_gre_header(skb, &tpi, &csum_err) < 0) goto drop; proto->handler(skb, &tpi); rcu_read_unlock(); return 0; drop: rcu_read_unlock(); kfree_skb(skb); return 0; } static const struct gre_protocol ipgre_protocol = { .handler = gre_cisco_rcv, }; int rpl_gre_cisco_register(struct gre_cisco_protocol *newp) { int err; err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO); if (err) { pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__); return err; } return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ? 0 : -EBUSY; } EXPORT_SYMBOL_GPL(rpl_gre_cisco_register); int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto) { int ret; ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ? 0 : -EINVAL; if (ret) return ret; synchronize_net(); ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); return ret; } EXPORT_SYMBOL_GPL(rpl_gre_cisco_unregister); #endif /* !HAVE_GRE_CISCO_REGISTER */ #endif #endif /* CONFIG_NET_IPGRE_DEMUX */ #endif /* HAVE_METADATA_DST */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/reciprocal_div.c0000644000000000000000000000013113534540071023204 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.549859088 openvswitch-2.5.9/datapath/linux/compat/reciprocal_div.c0000644000175000017500000000077013534540071024677 0ustar00jpettitjpettit00000000000000#include #include #include #include /* * For a description of the algorithm please have a look at * include/linux/reciprocal_div.h */ struct reciprocal_value rpl_reciprocal_value(u32 d) { struct reciprocal_value R; u64 m; int l; l = fls(d - 1); m = ((1ULL << 32) * ((1ULL << l) - d)); do_div(m, d); ++m; R.m = (u32)m; R.sh1 = min(l, 1); R.sh2 = max(l - 1, 0); return R; } EXPORT_SYMBOL_GPL(rpl_reciprocal_value); openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/build-aux0000644000000000000000000000013213534540121021665 xustar0030 mtime=1567801425.565859206 30 atime=1567801425.625859648 30 ctime=1567801425.565859206 openvswitch-2.5.9/datapath/linux/compat/build-aux/0000755000175000017500000000000013534540121023430 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath/linux/compat/build-aux/PaxHeaders.82075/export-check-whitelist0000644000000000000000000000013213534540071026277 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.565859206 openvswitch-2.5.9/datapath/linux/compat/build-aux/export-check-whitelist0000644000175000017500000000002013534540071027755 0ustar00jpettitjpettit00000000000000pskb_expand_headopenvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/net_namespace.c0000644000000000000000000000013113534540071023021 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.545859058 openvswitch-2.5.9/datapath/linux/compat/net_namespace.c0000644000175000017500000000145013534540071024510 0ustar00jpettitjpettit00000000000000#include #include #include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) int ovs_compat_init_net(struct net *net, struct rpl_pernet_operations *pnet) { int err; void *ovs_net = kzalloc(pnet->size, GFP_KERNEL); if (!ovs_net) return -ENOMEM; err = net_assign_generic(net, *pnet->id, ovs_net); if (err) goto err; if (pnet->init) { err = pnet->init(net); if (err) goto err; } return 0; err: kfree(ovs_net); return err; } EXPORT_SYMBOL_GPL(ovs_compat_init_net); void ovs_compat_exit_net(struct net *net, struct rpl_pernet_operations *pnet) { void *ovs_net = net_generic(net, *pnet->id); if (pnet->exit) pnet->exit(net); kfree(ovs_net); } EXPORT_SYMBOL_GPL(ovs_compat_exit_net); #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/inet_fragment.c0000644000000000000000000000012713534540071023046 xustar0028 mtime=1567801401.2696802 30 atime=1567801402.057685987 29 ctime=1567801425.53385897 openvswitch-2.5.9/datapath/linux/compat/inet_fragment.c0000644000175000017500000003176313534540071024542 0ustar00jpettitjpettit00000000000000/* * inet fragments management * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Pavel Emelyanov * Started as consolidation of ipv4/ip_fragment.c, * ipv6/reassembly. and ipv6 nf conntrack reassembly */ #include #if !defined(HAVE_CORRECT_MRU_HANDLING) && defined(OVS_FRAGMENT_BACKPORT) #include #include #include #include #include #include #include #include #include #include #include #include #define INETFRAGS_EVICT_BUCKETS 128 #define INETFRAGS_EVICT_MAX 512 /* don't rebuild inetfrag table with new secret more often than this */ #define INETFRAGS_MIN_REBUILD_INTERVAL (5 * HZ) /* Given the OR values of all fragments, apply RFC 3168 5.3 requirements * Value : 0xff if frame should be dropped. * 0 or INET_ECN_CE value, to be ORed in to final iph->tos field */ const u8 ip_frag_ecn_table[16] = { /* at least one fragment had CE, and others ECT_0 or ECT_1 */ [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = INET_ECN_CE, [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = INET_ECN_CE, [IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = INET_ECN_CE, /* invalid combinations : drop frame */ [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_1] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_1] = 0xff, [IPFRAG_ECN_NOT_ECT | IPFRAG_ECN_CE | IPFRAG_ECN_ECT_0 | IPFRAG_ECN_ECT_1] = 0xff, }; static unsigned int inet_frag_hashfn(const struct inet_frags *f, struct inet_frag_queue *q) { return f->hashfn(q) & (INETFRAGS_HASHSZ - 1); } #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK static bool inet_frag_may_rebuild(struct inet_frags *f) { return time_after(jiffies, f->last_rebuild_jiffies + INETFRAGS_MIN_REBUILD_INTERVAL); } static void inet_frag_secret_rebuild(struct inet_frags *f) { int i; write_seqlock_bh(&f->rnd_seqlock); if (!inet_frag_may_rebuild(f)) goto out; get_random_bytes(&f->rnd, sizeof(u32)); for (i = 0; i < INETFRAGS_HASHSZ; i++) { struct inet_frag_bucket *hb; struct inet_frag_queue *q; struct hlist_node *n; hb = &f->hash[i]; spin_lock(&hb->chain_lock); hlist_for_each_entry_safe(q, n, &hb->chain, list) { unsigned int hval = inet_frag_hashfn(f, q); if (hval != i) { struct inet_frag_bucket *hb_dest; hlist_del(&q->list); /* Relink to new hash chain. */ hb_dest = &f->hash[hval]; /* This is the only place where we take * another chain_lock while already holding * one. As this will not run concurrently, * we cannot deadlock on hb_dest lock below, if its * already locked it will be released soon since * other caller cannot be waiting for hb lock * that we've taken above. */ spin_lock_nested(&hb_dest->chain_lock, SINGLE_DEPTH_NESTING); hlist_add_head(&q->list, &hb_dest->chain); spin_unlock(&hb_dest->chain_lock); } } spin_unlock(&hb->chain_lock); } f->rebuild = false; f->last_rebuild_jiffies = jiffies; out: write_sequnlock_bh(&f->rnd_seqlock); } static bool inet_fragq_should_evict(const struct inet_frag_queue *q) { return q->net->low_thresh == 0 || frag_mem_limit(q->net) >= q->net->low_thresh; } static unsigned int inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb) { #ifndef HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR struct ovs_inet_frag_queue *ofq; #endif struct inet_frag_queue *fq; struct hlist_node *n; unsigned int evicted = 0; HLIST_HEAD(expired); spin_lock(&hb->chain_lock); hlist_for_each_entry_safe(fq, n, &hb->chain, list) { if (!inet_fragq_should_evict(fq)) continue; if (!del_timer(&fq->timer)) continue; #ifdef HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR hlist_add_head(&fq->list_evictor, &expired); #else ofq = (struct ovs_inet_frag_queue *)fq; hlist_add_head(&ofq->list_evictor, &expired); #endif ++evicted; } spin_unlock(&hb->chain_lock); #ifdef HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR hlist_for_each_entry_safe(fq, n, &expired, list_evictor) f->frag_expire((unsigned long) fq); #else hlist_for_each_entry_safe(ofq, n, &expired, list_evictor) f->frag_expire((unsigned long) &ofq->fq); #endif return evicted; } static void inet_frag_worker(struct work_struct *work) { unsigned int budget = INETFRAGS_EVICT_BUCKETS; unsigned int i, evicted = 0; struct inet_frags *f; f = container_of(work, struct inet_frags, frags_work); BUILD_BUG_ON(INETFRAGS_EVICT_BUCKETS >= INETFRAGS_HASHSZ); local_bh_disable(); for (i = ACCESS_ONCE(f->next_bucket); budget; --budget) { evicted += inet_evict_bucket(f, &f->hash[i]); i = (i + 1) & (INETFRAGS_HASHSZ - 1); if (evicted > INETFRAGS_EVICT_MAX) break; } f->next_bucket = i; local_bh_enable(); if (f->rebuild && inet_frag_may_rebuild(f)) inet_frag_secret_rebuild(f); } static void inet_frag_schedule_worker(struct inet_frags *f) { if (unlikely(!work_pending(&f->frags_work))) schedule_work(&f->frags_work); } #endif /* >= 3.17 */ int inet_frags_init(struct inet_frags *f) { int i; #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK INIT_WORK(&f->frags_work, inet_frag_worker); #endif for (i = 0; i < INETFRAGS_HASHSZ; i++) { struct inet_frag_bucket *hb = &f->hash[i]; spin_lock_init(&hb->chain_lock); INIT_HLIST_HEAD(&hb->chain); } #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK seqlock_init(&f->rnd_seqlock); f->last_rebuild_jiffies = 0; f->frags_cachep = kmem_cache_create(f->frags_cache_name, f->qsize, 0, 0, NULL); if (!f->frags_cachep) return -ENOMEM; #else rwlock_init(&f->lock); f->secret_timer.expires = jiffies + f->secret_interval; #endif return 0; } void inet_frags_fini(struct inet_frags *f) { #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK cancel_work_sync(&f->frags_work); kmem_cache_destroy(f->frags_cachep); #endif } int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force); #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) { unsigned int seq; evict_again: local_bh_disable(); seq = read_seqbegin(&f->rnd_seqlock); inet_frag_evictor(nf, f, true); local_bh_enable(); cond_resched(); if (read_seqretry(&f->rnd_seqlock, seq) || percpu_counter_sum(&nf->mem)) goto evict_again; } #else void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) { read_lock_bh(&f->lock); inet_frag_evictor(nf, f, true); read_unlock_bh(&f->lock); } #endif static struct inet_frag_bucket * get_frag_bucket_locked(struct inet_frag_queue *fq, struct inet_frags *f) #ifdef HAVE_INET_FRAGS_WITH_RWLOCK __acquires(f->lock) #endif __acquires(hb->chain_lock) { struct inet_frag_bucket *hb; unsigned int hash; #ifdef HAVE_INET_FRAGS_WITH_RWLOCK read_lock(&f->lock); #else unsigned int seq; restart: seq = read_seqbegin(&f->rnd_seqlock); #endif hash = inet_frag_hashfn(f, fq); hb = &f->hash[hash]; spin_lock(&hb->chain_lock); #ifndef HAVE_INET_FRAGS_WITH_RWLOCK if (read_seqretry(&f->rnd_seqlock, seq)) { spin_unlock(&hb->chain_lock); goto restart; } #endif return hb; } static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) #ifdef HAVE_INET_FRAGS_WITH_RWLOCK __releases(f->lock) #endif __releases(hb->chain_lock) { struct inet_frag_bucket *hb; hb = get_frag_bucket_locked(fq, f); hlist_del(&fq->list); q_flags(fq) |= INET_FRAG_COMPLETE; spin_unlock(&hb->chain_lock); #ifdef HAVE_INET_FRAGS_WITH_RWLOCK read_unlock(&f->lock); #endif } void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) { if (del_timer(&fq->timer)) atomic_dec(&fq->refcnt); if (!(q_flags(fq) & INET_FRAG_COMPLETE)) { fq_unlink(fq, f); atomic_dec(&fq->refcnt); } } static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f, struct sk_buff *skb) { if (f->skb_free) f->skb_free(skb); kfree_skb(skb); } void rpl_inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f) { struct sk_buff *fp; struct netns_frags *nf; unsigned int sum, sum_truesize = 0; WARN_ON(!(q_flags(q) & INET_FRAG_COMPLETE)); WARN_ON(del_timer(&q->timer) != 0); /* Release all fragment data. */ fp = q->fragments; nf = q->net; while (fp) { struct sk_buff *xp = fp->next; sum_truesize += fp->truesize; frag_kfree_skb(nf, f, fp); fp = xp; } sum = sum_truesize + f->qsize; if (f->destructor) f->destructor(q); #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK kmem_cache_free(f->frags_cachep, q); #else kfree(q); #endif sub_frag_mem_limit(nf, sum); } int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f, bool force) { #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK int i; for (i = 0; i < INETFRAGS_HASHSZ ; i++) inet_evict_bucket(f, &f->hash[i]); return 0; #else struct inet_frag_queue *q; int work, evicted = 0; work = frag_mem_limit(nf) - nf->low_thresh; while (work > 0 || force) { spin_lock(&nf->lru_lock); if (list_empty(&nf->lru_list)) { spin_unlock(&nf->lru_lock); break; } q = list_first_entry(&nf->lru_list, struct inet_frag_queue, lru_list); atomic_inc(&q->refcnt); /* Remove q from list to avoid several CPUs grabbing it */ list_del_init(&q->lru_list); spin_unlock(&nf->lru_lock); spin_lock(&q->lock); if (!(q->last_in & INET_FRAG_COMPLETE)) inet_frag_kill(q, f); spin_unlock(&q->lock); if (atomic_dec_and_test(&q->refcnt)) inet_frag_destroy(q, f, &work); evicted++; } return evicted; #endif } static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, void *arg) { struct inet_frag_bucket *hb = get_frag_bucket_locked(qp_in, f); struct inet_frag_queue *qp; #ifdef CONFIG_SMP /* With SMP race we have to recheck hash table, because * such entry could have been created on other cpu before * we acquired hash bucket lock. */ hlist_for_each_entry(qp, &hb->chain, list) { if (qp->net == nf && f->match(qp, arg)) { atomic_inc(&qp->refcnt); spin_unlock(&hb->chain_lock); #ifdef HAVE_INET_FRAGS_WITH_RWLOCK read_unlock(&f->lock); #endif q_flags(qp_in) |= INET_FRAG_COMPLETE; inet_frag_put(qp_in, f); return qp; } } #endif /* CONFIG_SMP */ qp = qp_in; if (!mod_timer(&qp->timer, jiffies + nf->timeout)) atomic_inc(&qp->refcnt); atomic_inc(&qp->refcnt); hlist_add_head(&qp->list, &hb->chain); spin_unlock(&hb->chain_lock); #ifdef HAVE_INET_FRAGS_WITH_RWLOCK read_unlock(&f->lock); #endif return qp; } static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, struct inet_frags *f, void *arg) { struct inet_frag_queue *q; if (frag_mem_limit(nf) > nf->high_thresh) { #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK inet_frag_schedule_worker(f); #endif return NULL; } #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK q = kmem_cache_zalloc(f->frags_cachep, GFP_ATOMIC); #else q = kzalloc(f->qsize, GFP_ATOMIC); #endif if (!q) return NULL; q->net = nf; f->constructor(q, arg); add_frag_mem_limit(nf, f->qsize); setup_timer(&q->timer, f->frag_expire, (unsigned long)q); spin_lock_init(&q->lock); atomic_set(&q->refcnt, 1); return q; } static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, struct inet_frags *f, void *arg) { struct inet_frag_queue *q; q = inet_frag_alloc(nf, f, arg); if (!q) return NULL; return inet_frag_intern(nf, q, f, arg); } struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) { struct inet_frag_bucket *hb; struct inet_frag_queue *q; int depth = 0; #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK if (frag_mem_limit(nf) > nf->low_thresh) inet_frag_schedule_worker(f); #else if (frag_mem_limit(nf) > nf->high_thresh) inet_frag_evictor(nf, f, false); #endif hash &= (INETFRAGS_HASHSZ - 1); hb = &f->hash[hash]; spin_lock(&hb->chain_lock); hlist_for_each_entry(q, &hb->chain, list) { if (q->net == nf && f->match(q, key)) { atomic_inc(&q->refcnt); spin_unlock(&hb->chain_lock); return q; } depth++; } spin_unlock(&hb->chain_lock); if (depth <= INETFRAGS_MAXDEPTH) return inet_frag_create(nf, f, key); #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK if (inet_frag_may_rebuild(f)) { if (!f->rebuild) f->rebuild = true; inet_frag_schedule_worker(f); } #endif return ERR_PTR(-ENOBUFS); } void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q, const char *prefix) { static const char msg[] = "inet_frag_find: Fragment hash bucket" " list length grew over limit " __stringify(INETFRAGS_MAXDEPTH) ". Dropping fragment.\n"; if (PTR_ERR(q) == -ENOBUFS) net_dbg_ratelimited("%s%s", prefix, msg); } #endif /* !HAVE_CORRECT_MRU_HANDLING && OVS_FRAGMENT_BACKPORT */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/vxlan.c0000644000000000000000000000013213534540071021350 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.057685987 30 ctime=1567801425.557859146 openvswitch-2.5.9/datapath/linux/compat/vxlan.c0000644000175000017500000015675613534540071023062 0ustar00jpettitjpettit00000000000000/* * VXLAN: Virtual eXtensible Local Area Network * * Copyright (c) 2012-2013 Vyatta Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_IPV6) #include #include #include #include #endif #include #ifndef HAVE_METADATA_DST #include "gso.h" #include "vport-netdev.h" #define VXLAN_VERSION "0.1" #define PORT_HASH_BITS 8 #define PORT_HASH_SIZE (1<flags & VXLAN_F_COLLECT_METADATA || ip_tunnel_collect_metadata(); } #if IS_ENABLED(CONFIG_IPV6) static inline bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) { if (a->sa.sa_family != b->sa.sa_family) return false; if (a->sa.sa_family == AF_INET6) return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr); else return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; } static inline bool vxlan_addr_any(const union vxlan_addr *ipa) { if (ipa->sa.sa_family == AF_INET6) return ipv6_addr_any(&ipa->sin6.sin6_addr); else return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY); } static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa) { if (ipa->sa.sa_family == AF_INET6) return ipv6_addr_is_multicast(&ipa->sin6.sin6_addr); else return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr)); } #else /* !CONFIG_IPV6 */ static inline bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b) { return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr; } static inline bool vxlan_addr_any(const union vxlan_addr *ipa) { return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY); } static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa) { return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr)); } #endif /* Virtual Network hash table head */ static inline struct hlist_head *vni_head(struct vxlan_sock *vs, u32 id) { return &vs->vni_list[hash_32(id, VNI_HASH_BITS)]; } /* Socket hash table head */ static inline struct hlist_head *vs_head(struct net *net, __be16 port) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; } /* First remote destination for a forwarding entry. * Guaranteed to be non-NULL because remotes are never deleted. */ static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb) { return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list); } static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb) { return list_first_entry(&fdb->remotes, struct vxlan_rdst, list); } /* Find VXLAN socket based on network namespace, address family and UDP port * and enabled unshareable flags. */ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family, __be16 port, u32 flags) { struct vxlan_sock *vs; flags &= VXLAN_F_RCV_FLAGS; hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { if (inet_sport(vs->sock->sk) == port && vxlan_get_sk_family(vs) == family && vs->flags == flags) return vs; } return NULL; } static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id) { struct vxlan_dev *vxlan; hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) { if (vxlan->default_dst.remote_vni == id) return vxlan; } return NULL; } /* Look up VNI in a per net namespace table */ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, sa_family_t family, __be16 port, u32 flags) { struct vxlan_sock *vs; vs = vxlan_find_sock(net, family, port, flags); if (!vs) return NULL; return vxlan_vs_find_vni(vs, id); } /* Fill in neighbour message in skbuff. */ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, const struct vxlan_fdb *fdb, u32 portid, u32 seq, int type, unsigned int flags, const struct vxlan_rdst *rdst) { return -EINVAL; } static inline size_t vxlan_nlmsg_size(void) { return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN) /* NDA_LLADDR */ + nla_total_size(sizeof(struct in6_addr)) /* NDA_DST */ + nla_total_size(sizeof(__be16)) /* NDA_PORT */ + nla_total_size(sizeof(__be32)) /* NDA_VNI */ + nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */ + nla_total_size(sizeof(__s32)) /* NDA_LINK_NETNSID */ + nla_total_size(sizeof(struct nda_cacheinfo)); } static void vxlan_fdb_notify(struct vxlan_dev *vxlan, struct vxlan_fdb *fdb, struct vxlan_rdst *rd, int type) { struct net *net = dev_net(vxlan->dev); struct sk_buff *skb; int err = -ENOBUFS; skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC); if (skb == NULL) goto errout; err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0, rd); if (err < 0) { /* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); } /* Hash Ethernet address */ static u32 eth_hash(const unsigned char *addr) { u64 value = get_unaligned((u64 *)addr); /* only want 6 bytes */ #ifdef __BIG_ENDIAN value >>= 16; #else value <<= 16; #endif return hash_64(value, FDB_HASH_BITS); } /* Hash chain to use given mac address */ static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan, const u8 *mac) { return &vxlan->fdb_head[eth_hash(mac)]; } /* Look up Ethernet address in forwarding table */ static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan, const u8 *mac) { struct hlist_head *head = vxlan_fdb_head(vxlan, mac); struct vxlan_fdb *f; hlist_for_each_entry_rcu(f, head, hlist) { if (ether_addr_equal(mac, f->eth_addr)) return f; } return NULL; } static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan, const u8 *mac) { struct vxlan_fdb *f; f = __vxlan_find_mac(vxlan, mac); if (f) f->used = jiffies; return f; } /* caller should hold vxlan->hash_lock */ static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, union vxlan_addr *ip, __be16 port, __u32 vni, __u32 ifindex) { struct vxlan_rdst *rd; list_for_each_entry(rd, &f->remotes, list) { if (vxlan_addr_equal(&rd->remote_ip, ip) && rd->remote_port == port && rd->remote_vni == vni && rd->remote_ifindex == ifindex) return rd; } return NULL; } /* Replace destination of unicast mac */ static int vxlan_fdb_replace(struct vxlan_fdb *f, union vxlan_addr *ip, __be16 port, __u32 vni, __u32 ifindex) { struct vxlan_rdst *rd; rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex); if (rd) return 0; rd = list_first_entry_or_null(&f->remotes, struct vxlan_rdst, list); if (!rd) return 0; rd->remote_ip = *ip; rd->remote_port = port; rd->remote_vni = vni; rd->remote_ifindex = ifindex; return 1; } /* Add/update destinations for multicast */ static int vxlan_fdb_append(struct vxlan_fdb *f, union vxlan_addr *ip, __be16 port, __u32 vni, __u32 ifindex, struct vxlan_rdst **rdp) { struct vxlan_rdst *rd; rd = vxlan_fdb_find_rdst(f, ip, port, vni, ifindex); if (rd) return 0; rd = kmalloc(sizeof(*rd), GFP_ATOMIC); if (rd == NULL) return -ENOBUFS; rd->remote_ip = *ip; rd->remote_port = port; rd->remote_vni = vni; rd->remote_ifindex = ifindex; list_add_tail_rcu(&rd->list, &f->remotes); *rdp = rd; return 1; } #ifdef HAVE_UDP_OFFLOAD #ifdef HAVE_NETIF_F_GSO_TUNNEL_REMCSUM static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, unsigned int off, struct vxlanhdr *vh, size_t hdrlen, u32 data, struct gro_remcsum *grc, bool nopartial) { size_t start, offset; if (skb->remcsum_offload) return vh; if (!NAPI_GRO_CB(skb)->csum_valid) return NULL; start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; offset = start + ((data & VXLAN_RCO_UDP) ? offsetof(struct udphdr, check) : offsetof(struct tcphdr, check)); vh = skb_gro_remcsum_process(skb, (void *)vh, off, hdrlen, start, offset, grc, nopartial); skb->remcsum_offload = 1; return vh; } #else static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, unsigned int off, struct vxlanhdr *vh, size_t hdrlen, u32 data, struct gro_remcsum *grc, bool nopartial) { return NULL; } #endif #ifndef HAVE_UDP_OFFLOAD_ARG_UOFF static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff *skb) #else static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff *skb, struct udp_offload *uoff) #endif { #ifdef HAVE_UDP_OFFLOAD_ARG_UOFF struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, udp_offloads); #else struct vxlan_sock *vs = NULL; #endif struct sk_buff *p, **pp = NULL; struct vxlanhdr *vh, *vh2; unsigned int hlen, off_vx; int flush = 1; u32 flags; struct gro_remcsum grc; skb_gro_remcsum_init(&grc); off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); vh = skb_gro_header_fast(skb, off_vx); if (skb_gro_header_hard(skb, hlen)) { vh = skb_gro_header_slow(skb, hlen, off_vx); if (unlikely(!vh)) goto out; } skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); flags = ntohl(vh->vx_flags); if ((flags & VXLAN_HF_RCO) && vs && (vs->flags & VXLAN_F_REMCSUM_RX)) { vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), ntohl(vh->vx_vni), &grc, !!(vs->flags & VXLAN_F_REMCSUM_NOPARTIAL)); if (!vh) goto out; } skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */ flush = 0; for (p = *head; p; p = p->next) { if (!NAPI_GRO_CB(p)->same_flow) continue; vh2 = (struct vxlanhdr *)(p->data + off_vx); if (vh->vx_flags != vh2->vx_flags || vh->vx_vni != vh2->vx_vni) { NAPI_GRO_CB(p)->same_flow = 0; continue; } } pp = eth_gro_receive(head, skb); out: skb_gro_remcsum_cleanup(skb, &grc); NAPI_GRO_CB(skb)->flush |= flush; return pp; } #ifndef HAVE_UDP_OFFLOAD_ARG_UOFF static int vxlan_gro_complete(struct sk_buff *skb, int nhoff) #else static int vxlan_gro_complete(struct sk_buff *skb, int nhoff, struct udp_offload *uoff) #endif { udp_tunnel_gro_complete(skb, nhoff); return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr)); } /* Notify netdevs that UDP port started listening */ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs) { struct net_device *dev; struct sock *sk = vs->sock->sk; struct net *net = sock_net(sk); sa_family_t sa_family = vxlan_get_sk_family(vs); __be16 port = inet_sk(sk)->inet_sport; int err; if (sa_family == AF_INET) { err = udp_add_offload(&vs->udp_offloads); if (err) pr_warn("vxlan: udp_add_offload failed with status %d\n", err); } rcu_read_lock(); for_each_netdev_rcu(net, dev) { if (dev->netdev_ops->ndo_add_vxlan_port) dev->netdev_ops->ndo_add_vxlan_port(dev, sa_family, port); } rcu_read_unlock(); } /* Notify netdevs that UDP port is no more listening */ static void vxlan_notify_del_rx_port(struct vxlan_sock *vs) { struct net_device *dev; struct sock *sk = vs->sock->sk; struct net *net = sock_net(sk); sa_family_t sa_family = vxlan_get_sk_family(vs); __be16 port = inet_sk(sk)->inet_sport; rcu_read_lock(); for_each_netdev_rcu(net, dev) { if (dev->netdev_ops->ndo_del_vxlan_port) dev->netdev_ops->ndo_del_vxlan_port(dev, sa_family, port); } rcu_read_unlock(); if (sa_family == AF_INET) udp_del_offload(&vs->udp_offloads); } #endif /* Add new entry to forwarding table -- assumes lock held */ static int vxlan_fdb_create(struct vxlan_dev *vxlan, const u8 *mac, union vxlan_addr *ip, __u16 state, __u16 flags, __be16 port, __u32 vni, __u32 ifindex, __u8 ndm_flags) { struct vxlan_rdst *rd = NULL; struct vxlan_fdb *f; int notify = 0; f = __vxlan_find_mac(vxlan, mac); if (f) { if (flags & NLM_F_EXCL) { netdev_dbg(vxlan->dev, "lost race to create %pM\n", mac); return -EEXIST; } if (f->state != state) { f->state = state; f->updated = jiffies; notify = 1; } if (f->flags != ndm_flags) { f->flags = ndm_flags; f->updated = jiffies; notify = 1; } if ((flags & NLM_F_REPLACE)) { /* Only change unicasts */ if (!(is_multicast_ether_addr(f->eth_addr) || is_zero_ether_addr(f->eth_addr))) { notify |= vxlan_fdb_replace(f, ip, port, vni, ifindex); } else return -EOPNOTSUPP; } if ((flags & NLM_F_APPEND) && (is_multicast_ether_addr(f->eth_addr) || is_zero_ether_addr(f->eth_addr))) { int rc = vxlan_fdb_append(f, ip, port, vni, ifindex, &rd); if (rc < 0) return rc; notify |= rc; } } else { if (!(flags & NLM_F_CREATE)) return -ENOENT; if (vxlan->cfg.addrmax && vxlan->addrcnt >= vxlan->cfg.addrmax) return -ENOSPC; /* Disallow replace to add a multicast entry */ if ((flags & NLM_F_REPLACE) && (is_multicast_ether_addr(mac) || is_zero_ether_addr(mac))) return -EOPNOTSUPP; netdev_dbg(vxlan->dev, "add %pM -> %pIS\n", mac, ip); f = kmalloc(sizeof(*f), GFP_ATOMIC); if (!f) return -ENOMEM; notify = 1; f->state = state; f->flags = ndm_flags; f->updated = f->used = jiffies; INIT_LIST_HEAD(&f->remotes); memcpy(f->eth_addr, mac, ETH_ALEN); vxlan_fdb_append(f, ip, port, vni, ifindex, &rd); ++vxlan->addrcnt; hlist_add_head_rcu(&f->hlist, vxlan_fdb_head(vxlan, mac)); } if (notify) { if (rd == NULL) rd = first_remote_rtnl(f); vxlan_fdb_notify(vxlan, f, rd, RTM_NEWNEIGH); } return 0; } static void vxlan_fdb_free(struct rcu_head *head) { struct vxlan_fdb *f = container_of(head, struct vxlan_fdb, rcu); struct vxlan_rdst *rd, *nd; list_for_each_entry_safe(rd, nd, &f->remotes, list) kfree(rd); kfree(f); } static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f) { netdev_dbg(vxlan->dev, "delete %pM\n", f->eth_addr); --vxlan->addrcnt; vxlan_fdb_notify(vxlan, f, first_remote_rtnl(f), RTM_DELNEIGH); hlist_del_rcu(&f->hlist); call_rcu(&f->rcu, vxlan_fdb_free); } /* Watch incoming packets to learn mapping between Ethernet address * and Tunnel endpoint. * Return true if packet is bogus and should be dropped. */ static bool vxlan_snoop(struct net_device *dev, union vxlan_addr *src_ip, const u8 *src_mac) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_fdb *f; f = vxlan_find_mac(vxlan, src_mac); if (likely(f)) { struct vxlan_rdst *rdst = first_remote_rcu(f); if (likely(vxlan_addr_equal(&rdst->remote_ip, src_ip))) return false; /* Don't migrate static entries, drop packets */ if (f->state & NUD_NOARP) return true; if (net_ratelimit()) netdev_info(dev, "%pM migrated from %pIS to %pIS\n", src_mac, &rdst->remote_ip.sa, &src_ip->sa); rdst->remote_ip = *src_ip; f->updated = jiffies; vxlan_fdb_notify(vxlan, f, rdst, RTM_NEWNEIGH); } else { /* learned new entry */ spin_lock(&vxlan->hash_lock); /* close off race between vxlan_flush and incoming packets */ if (netif_running(dev)) vxlan_fdb_create(vxlan, src_mac, src_ip, NUD_REACHABLE, NLM_F_EXCL|NLM_F_CREATE, vxlan->cfg.dst_port, vxlan->default_dst.remote_vni, 0, NTF_SELF); spin_unlock(&vxlan->hash_lock); } return false; } /* See if multicast group is already in use by other ID */ static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) { struct vxlan_dev *vxlan; /* The vxlan_sock is only used by dev, leaving group has * no effect on other vxlan devices. */ if (atomic_read(&dev->vn_sock->refcnt) == 1) return false; list_for_each_entry(vxlan, &vn->vxlan_list, next) { if (!netif_running(vxlan->dev) || vxlan == dev) continue; if (vxlan->vn_sock != dev->vn_sock) continue; if (!vxlan_addr_equal(&vxlan->default_dst.remote_ip, &dev->default_dst.remote_ip)) continue; if (vxlan->default_dst.remote_ifindex != dev->default_dst.remote_ifindex) continue; return true; } return false; } static void vxlan_sock_release(struct vxlan_sock *vs) { struct sock *sk = vs->sock->sk; struct net *net = sock_net(sk); struct vxlan_net *vn = net_generic(net, vxlan_net_id); if (!atomic_dec_and_test(&vs->refcnt)) return; spin_lock(&vn->sock_lock); hlist_del_rcu(&vs->hlist); #ifdef HAVE_UDP_OFFLOAD vxlan_notify_del_rx_port(vs); #endif spin_unlock(&vn->sock_lock); queue_work(vxlan_wq, &vs->del_work); } /* Update multicast group membership when first VNI on * multicast address is brought up */ static int vxlan_igmp_join(struct vxlan_dev *vxlan) { return -EINVAL; } /* Inverse of vxlan_igmp_join when last VNI is brought down */ static int vxlan_igmp_leave(struct vxlan_dev *vxlan) { return -EINVAL; } #ifdef HAVE_VXLAN_HF_RCO static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, size_t hdrlen, u32 data, bool nopartial) { size_t start, offset, plen; if (skb->remcsum_offload) return vh; start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; offset = start + ((data & VXLAN_RCO_UDP) ? offsetof(struct udphdr, check) : offsetof(struct tcphdr, check)); plen = hdrlen + offset + sizeof(u16); if (!pskb_may_pull(skb, plen)) return NULL; vh = (struct vxlanhdr *)(udp_hdr(skb) + 1); skb_remcsum_process(skb, (void *)vh + hdrlen, start, offset, nopartial); return vh; } #endif static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, struct vxlan_metadata *md, u32 vni, struct metadata_dst *tun_dst) { struct iphdr *oip = NULL; struct ipv6hdr *oip6 = NULL; struct vxlan_dev *vxlan; #ifdef HAVE_DEV_TSTATS struct pcpu_sw_netstats *stats; #endif union vxlan_addr saddr; int err = 0; /* For flow based devices, map all packets to VNI 0 */ if (vs->flags & VXLAN_F_COLLECT_METADATA) vni = 0; /* Is this VNI defined? */ vxlan = vxlan_vs_find_vni(vs, vni); if (!vxlan) goto drop; skb_reset_mac_header(skb); skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev))); skb->protocol = eth_type_trans(skb, vxlan->dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); /* Ignore packet loops (and multicast echo) */ if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr)) goto drop; /* Get data from the outer IP header */ if (vxlan_get_sk_family(vs) == AF_INET) { oip = ip_hdr(skb); saddr.sin.sin_addr.s_addr = oip->saddr; saddr.sa.sa_family = AF_INET; #if IS_ENABLED(CONFIG_IPV6) } else { oip6 = ipv6_hdr(skb); saddr.sin6.sin6_addr = oip6->saddr; saddr.sa.sa_family = AF_INET6; #endif } if (tun_dst) { ovs_skb_dst_set(skb, (struct dst_entry *)tun_dst); tun_dst = NULL; } else { goto drop; } if ((vxlan->flags & VXLAN_F_LEARN) && vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source)) goto drop; skb_reset_network_header(skb); /* In flow-based mode, GBP is carried in dst_metadata */ if (!(vs->flags & VXLAN_F_COLLECT_METADATA)) skb->mark = md->gbp; if (oip6) err = IP6_ECN_decapsulate(oip6, skb); if (oip) err = IP_ECN_decapsulate(oip, skb); if (unlikely(err)) { if (err > 1) { ++vxlan->dev->stats.rx_frame_errors; ++vxlan->dev->stats.rx_errors; goto drop; } } #ifdef HAVE_DEV_TSTATS stats = this_cpu_ptr((struct pcpu_sw_netstats __percpu *)vxlan->dev->tstats); u64_stats_update_begin(&stats->syncp); stats->rx_packets++; stats->rx_bytes += skb->len; u64_stats_update_end(&stats->syncp); #endif netdev_port_receive(skb, skb_tunnel_info(skb)); return; drop: /* Consume bad packet */ kfree_skb(skb); } /* Callback from net/ipv4/udp.c to receive packets */ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct vxlan_sock *vs; struct vxlanhdr *vxh; u32 flags, vni; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; union { struct metadata_dst dst; char buf[sizeof(struct metadata_dst) + sizeof(*md)]; } buf; /* Need Vxlan and inner Ethernet header to be present */ if (!pskb_may_pull(skb, VXLAN_HLEN)) goto error; vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); flags = ntohl(vxh->vx_flags); vni = ntohl(vxh->vx_vni); if (flags & VXLAN_HF_VNI) { flags &= ~VXLAN_HF_VNI; } else { /* VNI flag always required to be set */ goto bad_flags; } if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB))) goto drop; vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); vs = rcu_dereference_sk_user_data(sk); if (!vs) goto drop; #ifdef HAVE_VXLAN_HF_RCO if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { vxh = vxlan_remcsum(skb, vxh, sizeof(struct vxlanhdr), vni, !!(vs->flags & VXLAN_F_REMCSUM_NOPARTIAL)); if (!vxh) goto drop; flags &= ~VXLAN_HF_RCO; vni &= VXLAN_VNI_MASK; } #endif if (vxlan_collect_metadata(vs)) { ovs_udp_tun_rx_dst(&buf.dst.u.tun_info, skb, AF_INET, TUNNEL_KEY, cpu_to_be64(vni >> 8), sizeof(*md)); md = ip_tunnel_info_opts(&buf.dst.u.tun_info); } else { memset(md, 0, sizeof(*md)); } /* For backwards compatibility, only allow reserved fields to be * used by VXLAN extensions if explicitly requested. */ if ((flags & VXLAN_HF_GBP) && (vs->flags & VXLAN_F_GBP)) { struct vxlanhdr_gbp *gbp; gbp = (struct vxlanhdr_gbp *)vxh; md->gbp = ntohs(gbp->policy_id); buf.dst.u.tun_info.key.tun_flags |= TUNNEL_VXLAN_OPT; if (gbp->dont_learn) md->gbp |= VXLAN_GBP_DONT_LEARN; if (gbp->policy_applied) md->gbp |= VXLAN_GBP_POLICY_APPLIED; flags &= ~VXLAN_GBP_USED_BITS; } if (flags || vni & ~VXLAN_VNI_MASK) { /* If there are any unprocessed flags remaining treat * this as a malformed packet. This behavior diverges from * VXLAN RFC (RFC7348) which stipulates that bits in reserved * in reserved fields are to be ignored. The approach here * maintains compatibility with previous stack code, and also * is more robust and provides a little more security in * adding extensions to VXLAN. */ goto bad_flags; } vxlan_rcv(vs, skb, md, vni >> 8, &buf.dst); return 0; drop: /* Consume bad packet */ kfree_skb(skb); return 0; bad_flags: netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n", ntohl(vxh->vx_flags), ntohl(vxh->vx_vni)); error: /* Return non vxlan pkt */ return 1; } static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, struct vxlan_metadata *md) { struct vxlanhdr_gbp *gbp; if (!md->gbp) return; gbp = (struct vxlanhdr_gbp *)vxh; vxh->vx_flags |= htonl(VXLAN_HF_GBP); if (md->gbp & VXLAN_GBP_DONT_LEARN) gbp->dont_learn = 1; if (md->gbp & VXLAN_GBP_POLICY_APPLIED) gbp->policy_applied = 1; gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK); } #if IS_ENABLED(CONFIG_IPV6) static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port, __be32 vni, struct vxlan_metadata *md, bool xnet, u32 vxflags) { struct vxlanhdr *vxh; int min_headroom; int err; bool udp_sum = !(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX); int type = 0; if ((vxflags & VXLAN_F_REMCSUM_TX) && skb->ip_summed == CHECKSUM_PARTIAL) { int csum_start = skb_checksum_start_offset(skb); if (csum_start <= VXLAN_MAX_REMCSUM_START && !(csum_start & VXLAN_RCO_SHIFT_MASK) && (skb->csum_offset == offsetof(struct udphdr, check) || skb->csum_offset == offsetof(struct tcphdr, check))) { udp_sum = false; type |= SKB_GSO_TUNNEL_REMCSUM; /* Add support for remote csum. */ if (!SKB_GSO_TUNNEL_REMCSUM) { kfree_skb(skb); err = -EOPNOTSUPP; goto err; } } } skb_scrub_packet(skb, xnet); min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + VXLAN_HLEN + sizeof(struct ipv6hdr) + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); if (unlikely(err)) { kfree_skb(skb); goto err; } skb = vlan_hwaccel_push_inside(skb); if (WARN_ON(!skb)) { err = -ENOMEM; goto err; } skb = udp_tunnel_handle_offloads(skb, udp_sum, type, true); if (IS_ERR(skb)) { err = -EINVAL; goto err; } vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_HF_VNI); vxh->vx_vni = vni; if (type & SKB_GSO_TUNNEL_REMCSUM) { u16 hdrlen = sizeof(struct vxlanhdr); u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> VXLAN_RCO_SHIFT; if (skb->csum_offset == offsetof(struct udphdr, check)) data |= VXLAN_RCO_UDP; vxh->vx_vni |= htonl(data); vxh->vx_flags |= htonl(VXLAN_HF_RCO); if (!skb_is_gso(skb)) { skb->ip_summed = CHECKSUM_NONE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) skb->encapsulation = 0; #endif } } if (vxflags & VXLAN_F_GBP) vxlan_build_gbp_hdr(vxh, vxflags, md); ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB)); udp_tunnel6_xmit_skb(dst, sk, skb, dev, saddr, daddr, prio, ttl, src_port, dst_port, !!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX)); return 0; err: dst_release(dst); return err; } #endif static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port, __be32 vni, struct vxlan_metadata *md, bool xnet, u32 vxflags) { struct vxlanhdr *vxh; int min_headroom; int err; bool udp_sum = !!(vxflags & VXLAN_F_UDP_CSUM); int type = 0; if ((vxflags & VXLAN_F_REMCSUM_TX) && skb->ip_summed == CHECKSUM_PARTIAL) { int csum_start = skb_checksum_start_offset(skb); if (csum_start <= VXLAN_MAX_REMCSUM_START && !(csum_start & VXLAN_RCO_SHIFT_MASK) && (skb->csum_offset == offsetof(struct udphdr, check) || skb->csum_offset == offsetof(struct tcphdr, check))) { udp_sum = false; type |= SKB_GSO_TUNNEL_REMCSUM; if (!SKB_GSO_TUNNEL_REMCSUM) { kfree_skb(skb); return -EOPNOTSUPP; } } } min_headroom = LL_RESERVED_SPACE(rt_dst(rt).dev) + rt_dst(rt).header_len + VXLAN_HLEN + sizeof(struct iphdr) + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); if (unlikely(err)) { kfree_skb(skb); return err; } skb = vlan_hwaccel_push_inside(skb); if (WARN_ON(!skb)) return -ENOMEM; skb = udp_tunnel_handle_offloads(skb, udp_sum, type, true); if (IS_ERR(skb)) return PTR_ERR(skb); vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); vxh->vx_flags = htonl(VXLAN_HF_VNI); vxh->vx_vni = vni; if (type & SKB_GSO_TUNNEL_REMCSUM) { u16 hdrlen = sizeof(struct vxlanhdr); u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> VXLAN_RCO_SHIFT; if (skb->csum_offset == offsetof(struct udphdr, check)) data |= VXLAN_RCO_UDP; vxh->vx_vni |= htonl(data); vxh->vx_flags |= htonl(VXLAN_HF_RCO); if (!skb_is_gso(skb)) { skb->ip_summed = CHECKSUM_NONE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) skb->encapsulation = 0; #endif } } if (vxflags & VXLAN_F_GBP) vxlan_build_gbp_hdr(vxh, vxflags, md); ovs_skb_set_inner_protocol(skb, htons(ETH_P_TEB)); return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df, src_port, dst_port, xnet, !(vxflags & VXLAN_F_UDP_CSUM)); } static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_rdst *rdst, bool did_rsc) { struct ip_tunnel_info *info; struct vxlan_dev *vxlan = netdev_priv(dev); struct sock *sk = vxlan->vn_sock->sock->sk; unsigned short family = vxlan_get_sk_family(vxlan->vn_sock); struct rtable *rt = NULL; const struct iphdr *old_iph; struct flowi4 fl4; union vxlan_addr *dst, *src; union vxlan_addr remote_ip, local_ip; struct vxlan_metadata _md; struct vxlan_metadata *md = &_md; __be16 src_port = 0, dst_port; u32 vni; __be16 df = 0; __u8 tos, ttl; int err; u32 flags = vxlan->flags; info = skb_tunnel_info(skb); if (rdst) { dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port; vni = rdst->remote_vni; dst = &rdst->remote_ip; src = &vxlan->cfg.saddr; } else { if (!info) { WARN_ONCE(1, "%s: Missing encapsulation instructions\n", dev->name); goto drop; } if (family != ip_tunnel_info_af(info)) goto drop; dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port; vni = be64_to_cpu(info->key.tun_id); remote_ip.sa.sa_family = family; /* memset is called to avoid compiler warning.*/ memset(&local_ip, 0, sizeof(local_ip)); if (family == AF_INET) { remote_ip.sin.sin_addr.s_addr = info->key.u.ipv4.dst; local_ip.sin.sin_addr.s_addr = info->key.u.ipv4.src; } else { remote_ip.sin6.sin6_addr = info->key.u.ipv6.dst; local_ip.sin6.sin6_addr = info->key.u.ipv6.src; } dst = &remote_ip; src = &local_ip; } if (vxlan_addr_any(dst)) { if (did_rsc) { /* short-circuited back to local bridge */ WARN_ONCE(1, "%s: vxlan_encap_bypass not supported\n", dev->name); } goto drop; } old_iph = ip_hdr(skb); ttl = vxlan->cfg.ttl; if (!ttl && vxlan_addr_multicast(dst)) ttl = 1; tos = vxlan->cfg.tos; if (tos == 1) tos = ip_tunnel_get_dsfield(old_iph, skb); src_port = udp_flow_src_port(dev_net(dev), skb, vxlan->cfg.port_min, vxlan->cfg.port_max, true); if (info) { if (info->key.tun_flags & TUNNEL_CSUM) flags |= VXLAN_F_UDP_CSUM; else flags &= ~VXLAN_F_UDP_CSUM; ttl = info->key.ttl; tos = info->key.tos; if (info->options_len) md = ip_tunnel_info_opts(info); } else { md->gbp = skb->mark; } if (dst->sa.sa_family == AF_INET) { if (info && (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)) df = htons(IP_DF); memset(&fl4, 0, sizeof(fl4)); fl4.flowi4_oif = rdst ? rdst->remote_ifindex : 0; fl4.flowi4_tos = RT_TOS(tos); fl4.flowi4_mark = skb->mark; fl4.flowi4_proto = IPPROTO_UDP; fl4.daddr = dst->sin.sin_addr.s_addr; fl4.saddr = src->sin.sin_addr.s_addr; rt = ip_route_output_key(vxlan->net, &fl4); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &dst->sin.sin_addr.s_addr); dev->stats.tx_carrier_errors++; goto tx_error; } if (rt_dst(rt).dev == dev) { netdev_dbg(dev, "circular route to %pI4\n", &dst->sin.sin_addr.s_addr); dev->stats.collisions++; goto rt_tx_error; } /* Bypass encapsulation if the destination is local */ if (!info && rt->rt_flags & RTCF_LOCAL && !(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { struct vxlan_dev *dst_vxlan; ip_rt_put(rt); dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst->sa.sa_family, dst_port, vxlan->flags); if (!dst_vxlan) goto tx_error; WARN_ONCE(1, "%s: vxlan_encap_bypass not supported\n", dev->name); goto tx_error; } tos = ip_tunnel_ecn_encap(tos, old_iph, skb); ttl = ttl ? : ip4_dst_hoplimit(&rt_dst(rt)); err = vxlan_xmit_skb(rt, sk, skb, fl4.saddr, dst->sin.sin_addr.s_addr, tos, ttl, df, src_port, dst_port, htonl(vni << 8), md, !net_eq(vxlan->net, dev_net(vxlan->dev)), flags); if (err < 0) { /* skb is already freed. */ skb = NULL; goto rt_tx_error; } iptunnel_xmit_stats(err, &dev->stats, (struct pcpu_sw_netstats __percpu *)dev->tstats); #if IS_ENABLED(CONFIG_IPV6) } else { struct dst_entry *ndst; struct flowi6 fl6; u32 rt6i_flags; memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = rdst ? rdst->remote_ifindex : 0; fl6.daddr = dst->sin6.sin6_addr; fl6.saddr = src->sin6.sin6_addr; fl6.flowi6_mark = skb->mark; fl6.flowi6_proto = IPPROTO_UDP; #ifdef HAVE_IPV6_DST_LOOKUP_NET if (ipv6_stub->ipv6_dst_lookup(vxlan->net, sk, &ndst, &fl6)) { #else #ifdef HAVE_IPV6_STUB if (ipv6_stub->ipv6_dst_lookup(sk, &ndst, &fl6)) { #else ndst = ip6_route_output(vxlan->net, sk, &fl6); if (ndst->error) { #endif #endif netdev_dbg(dev, "no route to %pI6\n", &dst->sin6.sin6_addr); dev->stats.tx_carrier_errors++; goto tx_error; } if (ndst->dev == dev) { netdev_dbg(dev, "circular route to %pI6\n", &dst->sin6.sin6_addr); dst_release(ndst); dev->stats.collisions++; goto tx_error; } /* Bypass encapsulation if the destination is local */ rt6i_flags = ((struct rt6_info *)ndst)->rt6i_flags; if (!info && rt6i_flags & RTF_LOCAL && !(rt6i_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) { struct vxlan_dev *dst_vxlan; dst_release(ndst); dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst->sa.sa_family, dst_port, vxlan->flags); if (!dst_vxlan) goto tx_error; WARN_ONCE(1, "%s: vxlan_encap_bypass not supported\n", dev->name); goto tx_error; } ttl = ttl ? : ip6_dst_hoplimit(ndst); err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr, 0, ttl, src_port, dst_port, htonl(vni << 8), md, !net_eq(vxlan->net, dev_net(vxlan->dev)), flags); #endif } return; drop: dev->stats.tx_dropped++; goto tx_free; rt_tx_error: ip_rt_put(rt); tx_error: dev->stats.tx_errors++; tx_free: dev_kfree_skb(skb); } /* Transmit local packets over Vxlan * * Outer IP header inherits ECN and DF from inner header. * Outer UDP destination is the VXLAN assigned port. * source port is based on hash of flow */ netdev_tx_t rpl_vxlan_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; struct vxlan_dev *vxlan = netdev_priv(dev); const struct ip_tunnel_info *info; info = skb_tunnel_info(skb); skb_reset_mac_header(skb); if ((vxlan->flags & VXLAN_F_PROXY)) goto out; if (vxlan->flags & VXLAN_F_COLLECT_METADATA && info && info->mode & IP_TUNNEL_INFO_TX) { vxlan_xmit_one(skb, dev, NULL, false); return NETDEV_TX_OK; } out: pr_warn("vxlan: unsupported flag set %x", vxlan->flags); kfree_skb(skb); return NETDEV_TX_OK; } EXPORT_SYMBOL(rpl_vxlan_xmit); /* Walk the forwarding table and purge stale entries */ static void vxlan_cleanup(unsigned long arg) { struct vxlan_dev *vxlan = (struct vxlan_dev *) arg; unsigned long next_timer = jiffies + FDB_AGE_INTERVAL; unsigned int h; if (!netif_running(vxlan->dev)) return; for (h = 0; h < FDB_HASH_SIZE; ++h) { struct hlist_node *p, *n; spin_lock_bh(&vxlan->hash_lock); hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) { struct vxlan_fdb *f = container_of(p, struct vxlan_fdb, hlist); unsigned long timeout; if (f->state & NUD_PERMANENT) continue; timeout = f->used + vxlan->cfg.age_interval * HZ; if (time_before_eq(timeout, jiffies)) { netdev_dbg(vxlan->dev, "garbage collect %pM\n", f->eth_addr); f->state = NUD_STALE; vxlan_fdb_destroy(vxlan, f); } else if (time_before(timeout, next_timer)) next_timer = timeout; } spin_unlock_bh(&vxlan->hash_lock); } mod_timer(&vxlan->age_timer, next_timer); } static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); __u32 vni = vxlan->default_dst.remote_vni; vxlan->vn_sock = vs; spin_lock(&vn->sock_lock); hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni)); spin_unlock(&vn->sock_lock); } /* Setup stats when device is created */ #ifdef HAVE_DEV_TSTATS static int vxlan_init(struct net_device *dev) { dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; } #endif static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan) { struct vxlan_fdb *f; spin_lock_bh(&vxlan->hash_lock); f = __vxlan_find_mac(vxlan, all_zeros_mac); if (f) vxlan_fdb_destroy(vxlan, f); spin_unlock_bh(&vxlan->hash_lock); } #ifdef HAVE_DEV_TSTATS static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); vxlan_fdb_delete_default(vxlan); free_percpu(dev->tstats); } #endif /* Start ageing timer and join group when device is brought up */ static int vxlan_open(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_sock *vs; int ret = 0; vs = vxlan_sock_add(vxlan->net, vxlan->cfg.dst_port, vxlan->cfg.no_share, vxlan->flags); if (IS_ERR(vs)) return PTR_ERR(vs); vxlan_vs_add_dev(vs, vxlan); if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { ret = vxlan_igmp_join(vxlan); if (ret == -EADDRINUSE) ret = 0; if (ret) { vxlan_sock_release(vs); return ret; } } if (vxlan->cfg.age_interval) mod_timer(&vxlan->age_timer, jiffies + FDB_AGE_INTERVAL); return ret; } /* Purge the forwarding table */ static void vxlan_flush(struct vxlan_dev *vxlan) { unsigned int h; spin_lock_bh(&vxlan->hash_lock); for (h = 0; h < FDB_HASH_SIZE; ++h) { struct hlist_node *p, *n; hlist_for_each_safe(p, n, &vxlan->fdb_head[h]) { struct vxlan_fdb *f = container_of(p, struct vxlan_fdb, hlist); /* the all_zeros_mac entry is deleted at vxlan_uninit */ if (!is_zero_ether_addr(f->eth_addr)) vxlan_fdb_destroy(vxlan, f); } } spin_unlock_bh(&vxlan->hash_lock); } /* Cleanup timer and forwarding table on shutdown */ static int vxlan_stop(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); struct vxlan_sock *vs = vxlan->vn_sock; int ret = 0; if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) && !vxlan_group_used(vn, vxlan)) ret = vxlan_igmp_leave(vxlan); del_timer_sync(&vxlan->age_timer); vxlan_flush(vxlan); vxlan_sock_release(vs); return ret; } /* Stub, nothing needs to be done. */ static void vxlan_set_multicast_list(struct net_device *dev) { } static int __vxlan_change_mtu(struct net_device *dev, struct net_device *lowerdev, struct vxlan_rdst *dst, int new_mtu, bool strict) { int max_mtu = IP_MAX_MTU; if (lowerdev) max_mtu = lowerdev->mtu; if (dst->remote_ip.sa.sa_family == AF_INET6) max_mtu -= VXLAN6_HEADROOM; else max_mtu -= VXLAN_HEADROOM; if (new_mtu < 68) return -EINVAL; if (new_mtu > max_mtu) { if (strict) return -EINVAL; new_mtu = max_mtu; } dev->mtu = new_mtu; return 0; } static int vxlan_change_mtu(struct net_device *dev, int new_mtu) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; struct net_device *lowerdev = __dev_get_by_index(vxlan->net, dst->remote_ifindex); return __vxlan_change_mtu(dev, lowerdev, dst, new_mtu, true); } static netdev_tx_t vxlan_dev_xmit(struct sk_buff *skb, struct net_device *dev) { /* Drop All packets coming from networking stack. OVS-CB is * not initialized for these packets. */ dev_kfree_skb(skb); dev->stats.tx_dropped++; return NETDEV_TX_OK; } static const struct net_device_ops vxlan_netdev_ops = { #ifdef HAVE_DEV_TSTATS .ndo_init = vxlan_init, .ndo_uninit = vxlan_uninit, .ndo_get_stats64 = ip_tunnel_get_stats64, #endif .ndo_open = vxlan_open, .ndo_stop = vxlan_stop, .ndo_start_xmit = vxlan_dev_xmit, .ndo_set_rx_mode = vxlan_set_multicast_list, .ndo_change_mtu = vxlan_change_mtu, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, }; /* Info for udev, that this is a virtual tunnel endpoint */ static struct device_type vxlan_type = { .name = "vxlan", }; /* Initialize the device structure. */ static void vxlan_setup(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); unsigned int h; eth_hw_addr_random(dev); ether_setup(dev); dev->netdev_ops = &vxlan_netdev_ops; dev->destructor = free_netdev; SET_NETDEV_DEVTYPE(dev, &vxlan_type); dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; dev->features |= NETIF_F_RXCSUM; dev->features |= NETIF_F_GSO_SOFTWARE; dev->vlan_features = dev->features; dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; dev->hw_features |= NETIF_F_GSO_SOFTWARE; dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; #endif #if 0 netif_keep_dst(dev); #endif dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; INIT_LIST_HEAD(&vxlan->next); spin_lock_init(&vxlan->hash_lock); init_timer_deferrable(&vxlan->age_timer); vxlan->age_timer.function = vxlan_cleanup; vxlan->age_timer.data = (unsigned long) vxlan; vxlan->cfg.dst_port = htons(vxlan_port); vxlan->dev = dev; for (h = 0; h < FDB_HASH_SIZE; ++h) INIT_HLIST_HEAD(&vxlan->fdb_head[h]); } static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) { if (tb[IFLA_ADDRESS]) { if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) { pr_debug("invalid link address (not ethernet)\n"); return -EINVAL; } if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) { pr_debug("invalid all zero ethernet address\n"); return -EADDRNOTAVAIL; } } if (!data) return -EINVAL; if (data[IFLA_VXLAN_ID]) { __u32 id = nla_get_u32(data[IFLA_VXLAN_ID]); if (id >= VXLAN_VID_MASK) return -ERANGE; } if (data[IFLA_VXLAN_PORT_RANGE]) { const struct ifla_vxlan_port_range *p = nla_data(data[IFLA_VXLAN_PORT_RANGE]); if (ntohs(p->high) < ntohs(p->low)) { pr_debug("port range %u .. %u not valid\n", ntohs(p->low), ntohs(p->high)); return -EINVAL; } } return 0; } static void vxlan_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { strlcpy(drvinfo->version, VXLAN_VERSION, sizeof(drvinfo->version)); strlcpy(drvinfo->driver, "vxlan", sizeof(drvinfo->driver)); } static const struct ethtool_ops vxlan_ethtool_ops = { .get_drvinfo = vxlan_get_drvinfo, .get_link = ethtool_op_get_link, }; static void free_vs_rcu(struct rcu_head *rcu) { struct vxlan_sock *vs = container_of(rcu, struct vxlan_sock, rcu); kfree(vs); } static void vxlan_del_work(struct work_struct *work) { struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work); udp_tunnel_sock_release(vs->sock); call_rcu(&vs->rcu, free_vs_rcu); } static struct socket *vxlan_create_sock(struct net *net, bool ipv6, __be16 port, u32 flags) { struct socket *sock; struct udp_port_cfg udp_conf; int err; memset(&udp_conf, 0, sizeof(udp_conf)); if (ipv6) { udp_conf.family = AF_INET6; udp_conf.use_udp6_rx_checksums = !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); udp_conf.ipv6_v6only = 1; } else { udp_conf.family = AF_INET; } udp_conf.local_udp_port = port; /* Open UDP socket */ err = udp_sock_create(net, &udp_conf, &sock); if (err < 0) return ERR_PTR(err); return sock; } /* Create new listen socket if needed */ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, u32 flags) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_sock *vs; struct socket *sock; unsigned int h; bool ipv6 = !!(flags & VXLAN_F_IPV6); struct udp_tunnel_sock_cfg tunnel_cfg; vs = kzalloc(sizeof(*vs), GFP_KERNEL); if (!vs) return ERR_PTR(-ENOMEM); for (h = 0; h < VNI_HASH_SIZE; ++h) INIT_HLIST_HEAD(&vs->vni_list[h]); INIT_WORK(&vs->del_work, vxlan_del_work); sock = vxlan_create_sock(net, ipv6, port, flags); if (IS_ERR(sock)) { pr_info("Cannot bind port %d, err=%ld\n", ntohs(port), PTR_ERR(sock)); kfree(vs); return ERR_CAST(sock); } vs->sock = sock; atomic_set(&vs->refcnt, 1); vs->flags = (flags & VXLAN_F_RCV_FLAGS); /* Initialize the vxlan udp offloads structure */ #ifdef HAVE_UDP_OFFLOAD vs->udp_offloads.port = port; vs->udp_offloads.callbacks.gro_receive = vxlan_gro_receive; vs->udp_offloads.callbacks.gro_complete = vxlan_gro_complete; vxlan_notify_add_rx_port(vs); #endif spin_lock(&vn->sock_lock); hlist_add_head_rcu(&vs->hlist, vs_head(net, port)); spin_unlock(&vn->sock_lock); /* Mark socket as an encapsulation socket. */ tunnel_cfg.sk_user_data = vs; tunnel_cfg.encap_type = 1; tunnel_cfg.encap_rcv = vxlan_udp_encap_recv; tunnel_cfg.encap_destroy = NULL; setup_udp_tunnel_sock(net, sock, &tunnel_cfg); return vs; } static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, bool no_share, u32 flags) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_sock *vs; bool ipv6 = flags & VXLAN_F_IPV6; if (!no_share) { spin_lock(&vn->sock_lock); vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port, flags); if (vs) { if (!atomic_add_unless(&vs->refcnt, 1, 0)) vs = ERR_PTR(-EBUSY); spin_unlock(&vn->sock_lock); return vs; } spin_unlock(&vn->sock_lock); } return vxlan_socket_create(net, port, flags); } static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, struct vxlan_config *conf) { struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; int err; bool use_ipv6 = false; __be16 default_port = vxlan->cfg.dst_port; struct net_device *lowerdev = NULL; vxlan->net = src_net; dst->remote_vni = conf->vni; memcpy(&dst->remote_ip, &conf->remote_ip, sizeof(conf->remote_ip)); /* Unless IPv6 is explicitly requested, assume IPv4 */ if (!dst->remote_ip.sa.sa_family) dst->remote_ip.sa.sa_family = AF_INET; if (dst->remote_ip.sa.sa_family == AF_INET6 || vxlan->cfg.saddr.sa.sa_family == AF_INET6) { if (!IS_ENABLED(CONFIG_IPV6)) return -EPFNOSUPPORT; use_ipv6 = true; } if (conf->remote_ifindex) { lowerdev = __dev_get_by_index(src_net, conf->remote_ifindex); dst->remote_ifindex = conf->remote_ifindex; if (!lowerdev) { pr_info("ifindex %d does not exist\n", dst->remote_ifindex); return -ENODEV; } #if IS_ENABLED(CONFIG_IPV6) if (use_ipv6) { struct inet6_dev *idev = __in6_dev_get(lowerdev); if (idev && idev->cnf.disable_ipv6) { pr_info("IPv6 is disabled via sysctl\n"); return -EPERM; } vxlan->flags |= VXLAN_F_IPV6; } #endif if (!conf->mtu) dev->mtu = lowerdev->mtu - (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); dev->needed_headroom = lowerdev->hard_header_len + (use_ipv6 ? VXLAN6_HEADROOM : VXLAN_HEADROOM); } else if (use_ipv6) { vxlan->flags |= VXLAN_F_IPV6; dev->needed_headroom = ETH_HLEN + VXLAN6_HEADROOM; } else { dev->needed_headroom = ETH_HLEN + VXLAN_HEADROOM; } if (conf->mtu) { err = __vxlan_change_mtu(dev, lowerdev, dst, conf->mtu, false); if (err) return err; } memcpy(&vxlan->cfg, conf, sizeof(*conf)); if (!vxlan->cfg.dst_port) vxlan->cfg.dst_port = default_port; vxlan->flags |= conf->flags; if (!vxlan->cfg.age_interval) vxlan->cfg.age_interval = FDB_AGE_DEFAULT; if (vxlan_find_vni(src_net, conf->vni, use_ipv6 ? AF_INET6 : AF_INET, vxlan->cfg.dst_port, vxlan->flags)) return -EEXIST; dev->ethtool_ops = &vxlan_ethtool_ops; /* create an fdb entry for a valid default destination */ if (!vxlan_addr_any(&vxlan->default_dst.remote_ip)) { err = vxlan_fdb_create(vxlan, all_zeros_mac, &vxlan->default_dst.remote_ip, NUD_REACHABLE|NUD_PERMANENT, NLM_F_EXCL|NLM_F_CREATE, vxlan->cfg.dst_port, vxlan->default_dst.remote_vni, vxlan->default_dst.remote_ifindex, NTF_SELF); if (err) return err; } err = register_netdevice(dev); if (err) { vxlan_fdb_delete_default(vxlan); return err; } list_add(&vxlan->next, &vn->vxlan_list); return 0; } struct net_device *rpl_vxlan_dev_create(struct net *net, const char *name, u8 name_assign_type, struct vxlan_config *conf) { struct nlattr *tb[IFLA_MAX+1]; struct net_device *dev; int err; memset(&tb, 0, sizeof(tb)); dev = rtnl_create_link(net, (char *)name, name_assign_type, &vxlan_link_ops, tb); if (IS_ERR(dev)) return dev; err = vxlan_dev_configure(net, dev, conf); if (err < 0) { free_netdev(dev); return ERR_PTR(err); } return dev; } EXPORT_SYMBOL_GPL(rpl_vxlan_dev_create); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static int vxlan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) #else static int vxlan_newlink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) #endif { return -EINVAL; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) static void vxlan_dellink(struct net_device *dev, struct list_head *head) #else static void vxlan_dellink(struct net_device *dev) #endif { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); spin_lock(&vn->sock_lock); if (!hlist_unhashed(&vxlan->hlist)) hlist_del_rcu(&vxlan->hlist); spin_unlock(&vn->sock_lock); list_del(&vxlan->next); unregister_netdevice_queue(dev, head); } static size_t vxlan_get_size(const struct net_device *dev) { return nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_ID */ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_GROUP{6} */ nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LINK */ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_VXLAN_LOCAL{6} */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TTL */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_TOS */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_PROXY */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_RSC */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_L2MISS */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_L3MISS */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_COLLECT_METADATA */ nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */ nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */ nla_total_size(sizeof(struct ifla_vxlan_port_range)) + nla_total_size(sizeof(__be16)) + /* IFLA_VXLAN_PORT */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */ 0; } static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) { const struct vxlan_dev *vxlan = netdev_priv(dev); if (nla_put_be16(skb, IFLA_VXLAN_PORT, vxlan->cfg.dst_port)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; } #ifdef HAVE_GET_LINK_NET static struct net *vxlan_get_link_net(const struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); return vxlan->net; } #endif static struct rtnl_link_ops vxlan_link_ops __read_mostly = { .kind = "ovs_vxlan", .maxtype = IFLA_VXLAN_MAX, .policy = vxlan_policy, .priv_size = sizeof(struct vxlan_dev), .setup = vxlan_setup, .validate = vxlan_validate, .newlink = vxlan_newlink, .dellink = vxlan_dellink, .get_size = vxlan_get_size, .fill_info = vxlan_fill_info, #ifdef HAVE_GET_LINK_NET .get_link_net = vxlan_get_link_net, #endif }; static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn, struct net_device *dev) { struct vxlan_dev *vxlan, *next; LIST_HEAD(list_kill); list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) { struct vxlan_rdst *dst = &vxlan->default_dst; /* In case we created vxlan device with carrier * and we loose the carrier due to module unload * we also need to remove vxlan device. In other * cases, it's not necessary and remote_ifindex * is 0 here, so no matches. */ if (dst->remote_ifindex == dev->ifindex) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) vxlan_dellink(vxlan->dev, &list_kill); #else vxlan_dellink(vxlan->dev); #endif } unregister_netdevice_many(&list_kill); } static int vxlan_lowerdev_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); if (event == NETDEV_UNREGISTER) vxlan_handle_lowerdev_unregister(vn, dev); return NOTIFY_DONE; } static struct notifier_block vxlan_notifier_block __read_mostly = { .notifier_call = vxlan_lowerdev_event, }; static __net_init int vxlan_init_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); unsigned int h; INIT_LIST_HEAD(&vn->vxlan_list); spin_lock_init(&vn->sock_lock); for (h = 0; h < PORT_HASH_SIZE; ++h) INIT_HLIST_HEAD(&vn->sock_list[h]); return 0; } static void __net_exit vxlan_exit_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan, *next; struct net_device *dev, *aux; LIST_HEAD(list); rtnl_lock(); for_each_netdev_safe(net, dev, aux) if (dev->rtnl_link_ops == &vxlan_link_ops) unregister_netdevice_queue(dev, &list); list_for_each_entry_safe(vxlan, next, &vn->vxlan_list, next) { /* If vxlan->dev is in the same netns, it has already been added * to the list by the previous loop. */ if (!net_eq(dev_net(vxlan->dev), net)) unregister_netdevice_queue(vxlan->dev, &list); } unregister_netdevice_many(&list); rtnl_unlock(); } static struct pernet_operations vxlan_net_ops = { .init = vxlan_init_net, .exit = vxlan_exit_net, .id = &vxlan_net_id, .size = sizeof(struct vxlan_net), }; DEFINE_COMPAT_PNET_REG_FUNC(device) int rpl_vxlan_init_module(void) { int rc; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) vxlan_wq = create_workqueue("vxlan"); #else vxlan_wq = alloc_workqueue("vxlan", 0, 0); #endif if (!vxlan_wq) return -ENOMEM; get_random_bytes(&vxlan_salt, sizeof(vxlan_salt)); rc = register_pernet_subsys(&vxlan_net_ops); if (rc) goto out1; rc = register_netdevice_notifier(&vxlan_notifier_block); if (rc) goto out2; rc = rtnl_link_register(&vxlan_link_ops); if (rc) goto out3; pr_info("VxLAN tunneling driver\n"); return 0; out3: unregister_netdevice_notifier(&vxlan_notifier_block); out2: unregister_pernet_subsys(&vxlan_net_ops); out1: destroy_workqueue(vxlan_wq); return rc; } void rpl_vxlan_cleanup_module(void) { rtnl_link_unregister(&vxlan_link_ops); unregister_netdevice_notifier(&vxlan_notifier_block); destroy_workqueue(vxlan_wq); unregister_pernet_subsys(&vxlan_net_ops); /* rcu_barrier() is called by netns */ } #endif openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/flex_array.c0000644000000000000000000000013113534540071022353 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 29 ctime=1567801425.52985894 openvswitch-2.5.9/datapath/linux/compat/flex_array.c0000644000175000017500000002616413534540071024053 0ustar00jpettitjpettit00000000000000#include #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) /* * Flexible array managed in PAGE_SIZE parts * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Copyright IBM Corporation, 2009 * * Author: Dave Hansen */ #include #include #include #include #include struct flex_array_part { char elements[FLEX_ARRAY_PART_SIZE]; }; /* * If a user requests an allocation which is small * enough, we may simply use the space in the * flex_array->parts[] array to store the user * data. */ static inline int elements_fit_in_base(struct flex_array *fa) { int data_size = fa->element_size * fa->total_nr_elements; if (data_size <= FLEX_ARRAY_BASE_BYTES_LEFT) return 1; return 0; } /** * flex_array_alloc - allocate a new flexible array * @element_size: the size of individual elements in the array * @total: total number of elements that this should hold * @flags: page allocation flags to use for base array * * Note: all locking must be provided by the caller. * * @total is used to size internal structures. If the user ever * accesses any array indexes >=@total, it will produce errors. * * The maximum number of elements is defined as: the number of * elements that can be stored in a page times the number of * page pointers that we can fit in the base structure or (using * integer math): * * (PAGE_SIZE/element_size) * (PAGE_SIZE-8)/sizeof(void *) * * Here's a table showing example capacities. Note that the maximum * index that the get/put() functions is just nr_objects-1. This * basically means that you get 4MB of storage on 32-bit and 2MB on * 64-bit. * * * Element size | Objects | Objects | * PAGE_SIZE=4k | 32-bit | 64-bit | * ---------------------------------| * 1 bytes | 4177920 | 2088960 | * 2 bytes | 2088960 | 1044480 | * 3 bytes | 1392300 | 696150 | * 4 bytes | 1044480 | 522240 | * 32 bytes | 130560 | 65408 | * 33 bytes | 126480 | 63240 | * 2048 bytes | 2040 | 1020 | * 2049 bytes | 1020 | 510 | * void * | 1044480 | 261120 | * * Since 64-bit pointers are twice the size, we lose half the * capacity in the base structure. Also note that no effort is made * to efficiently pack objects across page boundaries. */ struct flex_array *rpl_flex_array_alloc(int element_size, unsigned int total, gfp_t flags) { struct flex_array *ret; struct reciprocal_value reciprocal_elems = {0}; int elems_per_part = 0; int max_size = 0; if (element_size) { elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size); reciprocal_elems = reciprocal_value(elems_per_part); max_size = FLEX_ARRAY_NR_BASE_PTRS * elems_per_part; } /* max_size will end up 0 if element_size > PAGE_SIZE */ if (total > max_size) return NULL; ret = kzalloc(sizeof(struct flex_array), flags); if (!ret) return NULL; ret->element_size = element_size; ret->total_nr_elements = total; ret->elems_per_part = elems_per_part; ret->reciprocal_elems = reciprocal_elems; if (elements_fit_in_base(ret) && !(flags & __GFP_ZERO)) memset(&ret->parts[0], FLEX_ARRAY_FREE, FLEX_ARRAY_BASE_BYTES_LEFT); return ret; } EXPORT_SYMBOL_GPL(rpl_flex_array_alloc); static int fa_element_to_part_nr(struct flex_array *fa, unsigned int element_nr) { return reciprocal_divide(element_nr, fa->reciprocal_elems); } /** * flex_array_free_parts - just free the second-level pages * @fa: the flex array from which to free parts * * This is to be used in cases where the base 'struct flex_array' * has been statically allocated and should not be free. */ void rpl_flex_array_free_parts(struct flex_array *fa) { int part_nr; if (elements_fit_in_base(fa)) return; for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) kfree(fa->parts[part_nr]); } EXPORT_SYMBOL_GPL(rpl_flex_array_free_parts); void rpl_flex_array_free(struct flex_array *fa) { flex_array_free_parts(fa); kfree(fa); } EXPORT_SYMBOL_GPL(rpl_flex_array_free); static unsigned int index_inside_part(struct flex_array *fa, unsigned int element_nr, unsigned int part_nr) { unsigned int part_offset; part_offset = element_nr - part_nr * fa->elems_per_part; return part_offset * fa->element_size; } static struct flex_array_part * __fa_get_part(struct flex_array *fa, int part_nr, gfp_t flags) { struct flex_array_part *part = fa->parts[part_nr]; if (!part) { part = kmalloc(sizeof(struct flex_array_part), flags); if (!part) return NULL; if (!(flags & __GFP_ZERO)) memset(part, FLEX_ARRAY_FREE, sizeof(struct flex_array_part)); fa->parts[part_nr] = part; } return part; } /** * flex_array_put - copy data into the array at @element_nr * @fa: the flex array to copy data into * @element_nr: index of the position in which to insert * the new element. * @src: address of data to copy into the array * @flags: page allocation flags to use for array expansion * * * Note that this *copies* the contents of @src into * the array. If you are trying to store an array of * pointers, make sure to pass in &ptr instead of ptr. * You may instead wish to use the flex_array_put_ptr() * helper function. * * Locking must be provided by the caller. */ int rpl_flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src, gfp_t flags) { int part_nr = 0; struct flex_array_part *part; void *dst; if (element_nr >= fa->total_nr_elements) return -ENOSPC; if (!fa->element_size) return 0; if (elements_fit_in_base(fa)) part = (struct flex_array_part *)&fa->parts[0]; else { part_nr = fa_element_to_part_nr(fa, element_nr); part = __fa_get_part(fa, part_nr, flags); if (!part) return -ENOMEM; } dst = &part->elements[index_inside_part(fa, element_nr, part_nr)]; memcpy(dst, src, fa->element_size); return 0; } EXPORT_SYMBOL_GPL(rpl_flex_array_put); /** * flex_array_clear - clear element in array at @element_nr * @fa: the flex array of the element. * @element_nr: index of the position to clear. * * Locking must be provided by the caller. */ int rpl_flex_array_clear(struct flex_array *fa, unsigned int element_nr) { int part_nr = 0; struct flex_array_part *part; void *dst; if (element_nr >= fa->total_nr_elements) return -ENOSPC; if (!fa->element_size) return 0; if (elements_fit_in_base(fa)) part = (struct flex_array_part *)&fa->parts[0]; else { part_nr = fa_element_to_part_nr(fa, element_nr); part = fa->parts[part_nr]; if (!part) return -EINVAL; } dst = &part->elements[index_inside_part(fa, element_nr, part_nr)]; memset(dst, FLEX_ARRAY_FREE, fa->element_size); return 0; } EXPORT_SYMBOL_GPL(rpl_flex_array_clear); /** * flex_array_prealloc - guarantee that array space exists * @fa: the flex array for which to preallocate parts * @start: index of first array element for which space is * allocated * @nr_elements: number of elements for which space is allocated * @flags: page allocation flags * * This will guarantee that no future calls to flex_array_put() * will allocate memory. It can be used if you are expecting to * be holding a lock or in some atomic context while writing * data into the array. * * Locking must be provided by the caller. */ int rpl_flex_array_prealloc(struct flex_array *fa, unsigned int start, unsigned int nr_elements, gfp_t flags) { int start_part; int end_part; int part_nr; unsigned int end; struct flex_array_part *part; if (!start && !nr_elements) return 0; if (start >= fa->total_nr_elements) return -ENOSPC; if (!nr_elements) return 0; end = start + nr_elements - 1; if (end >= fa->total_nr_elements) return -ENOSPC; if (!fa->element_size) return 0; if (elements_fit_in_base(fa)) return 0; start_part = fa_element_to_part_nr(fa, start); end_part = fa_element_to_part_nr(fa, end); for (part_nr = start_part; part_nr <= end_part; part_nr++) { part = __fa_get_part(fa, part_nr, flags); if (!part) return -ENOMEM; } return 0; } EXPORT_SYMBOL_GPL(rpl_flex_array_prealloc); /** * flex_array_get - pull data back out of the array * @fa: the flex array from which to extract data * @element_nr: index of the element to fetch from the array * * Returns a pointer to the data at index @element_nr. Note * that this is a copy of the data that was passed in. If you * are using this to store pointers, you'll get back &ptr. You * may instead wish to use the flex_array_get_ptr helper. * * Locking must be provided by the caller. */ void *rpl_flex_array_get(struct flex_array *fa, unsigned int element_nr) { int part_nr = 0; struct flex_array_part *part; if (!fa->element_size) return NULL; if (element_nr >= fa->total_nr_elements) return NULL; if (elements_fit_in_base(fa)) part = (struct flex_array_part *)&fa->parts[0]; else { part_nr = fa_element_to_part_nr(fa, element_nr); part = fa->parts[part_nr]; if (!part) return NULL; } return &part->elements[index_inside_part(fa, element_nr, part_nr)]; } EXPORT_SYMBOL_GPL(rpl_flex_array_get); /** * flex_array_get_ptr - pull a ptr back out of the array * @fa: the flex array from which to extract data * @element_nr: index of the element to fetch from the array * * Returns the pointer placed in the flex array at element_nr using * flex_array_put_ptr(). This function should not be called if the * element in question was not set using the _put_ptr() helper. */ void *rpl_flex_array_get_ptr(struct flex_array *fa, unsigned int element_nr) { void **tmp; tmp = flex_array_get(fa, element_nr); if (!tmp) return NULL; return *tmp; } EXPORT_SYMBOL_GPL(rpl_flex_array_get_ptr); static int part_is_free(struct flex_array_part *part) { int i; for (i = 0; i < sizeof(struct flex_array_part); i++) if (part->elements[i] != FLEX_ARRAY_FREE) return 0; return 1; } /** * flex_array_shrink - free unused second-level pages * @fa: the flex array to shrink * * Frees all second-level pages that consist solely of unused * elements. Returns the number of pages freed. * * Locking must be provided by the caller. */ int rpl_flex_array_shrink(struct flex_array *fa) { struct flex_array_part *part; int part_nr; int ret = 0; if (!fa->total_nr_elements || !fa->element_size) return 0; if (elements_fit_in_base(fa)) return ret; for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) { part = fa->parts[part_nr]; if (!part) continue; if (part_is_free(part)) { fa->parts[part_nr] = NULL; kfree(part); ret++; } } return ret; } EXPORT_SYMBOL_GPL(rpl_flex_array_shrink); #endif /* Linux version < 3.0.0 */ openvswitch-2.5.9/datapath/linux/compat/PaxHeaders.82075/ip_fragment.c0000644000000000000000000000013113534540071022512 xustar0029 mtime=1567801401.27368023 30 atime=1567801402.057685987 30 ctime=1567801425.537858999 openvswitch-2.5.9/datapath/linux/compat/ip_fragment.c0000644000175000017500000004345713534540071024216 0ustar00jpettitjpettit00000000000000/* * IP fragmentation backport, heavily based on linux/net/ipv4/ip_fragment.c, * copied from Linux 192132b9a034 net: Add support for VRFs to inetpeer cache * * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * The IP fragmentation functionality. * * Authors: Fred N. van Kempen * Alan Cox * * Fixes: * Alan Cox : Split from ip.c , see ip_input.c for history. * David S. Miller : Begin massive cleanup... * Andi Kleen : Add sysctls. * xxxx : Overlapfrag bug. * Ultima : ip_expire() kernel panic. * Bill Hawes : Frag accounting and evictor fixes. * John McDonald : 0 length frag bug. * Alexey Kuznetsov: SMP races, threading, cleanup. * Patrick McHardy : LRU queue of frag heads for evictor. */ #include #if !defined(HAVE_CORRECT_MRU_HANDLING) && defined(OVS_FRAGMENT_BACKPORT) #define pr_fmt(fmt) "IPv4: " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c * as well. Or notify me, at least. --ANK */ static int sysctl_ipfrag_max_dist __read_mostly = 64; static const char ip_frag_cache_name[] = "ovs-frag4"; struct ipfrag_skb_cb { struct inet_skb_parm h; int offset; }; #define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb)) /* Describe an entry in the "incomplete datagrams" queue. */ struct ipq { union { struct inet_frag_queue q; #ifndef HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR struct ovs_inet_frag_queue oq; #endif }; u32 user; __be32 saddr; __be32 daddr; __be16 id; u8 protocol; u8 ecn; /* RFC3168 support */ u16 max_df_size; /* largest frag with DF set seen */ int iif; int vif; /* VRF device index */ unsigned int rid; struct inet_peer *peer; }; static u8 ip4_frag_ecn(u8 tos) { return 1 << (tos & INET_ECN_MASK); } static struct inet_frags ip4_frags; static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, struct net_device *dev); struct ip4_create_arg { struct iphdr *iph; u32 user; int vif; }; static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) { net_get_random_once(&ip4_frags.rnd, sizeof(ip4_frags.rnd)); return jhash_3words((__force u32)id << 16 | prot, (__force u32)saddr, (__force u32)daddr, ip4_frags.rnd); } #ifdef HAVE_INET_FRAGS_CONST static unsigned int ip4_hashfn(const struct inet_frag_queue *q) #else static unsigned int ip4_hashfn(struct inet_frag_queue *q) #endif { const struct ipq *ipq; ipq = container_of(q, struct ipq, q); return ipqhashfn(ipq->id, ipq->saddr, ipq->daddr, ipq->protocol); } #ifdef HAVE_INET_FRAGS_CONST static bool ip4_frag_match(const struct inet_frag_queue *q, const void *a) #else static bool ip4_frag_match(struct inet_frag_queue *q, void *a) #endif { const struct ipq *qp; const struct ip4_create_arg *arg = a; qp = container_of(q, struct ipq, q); return qp->id == arg->iph->id && qp->saddr == arg->iph->saddr && qp->daddr == arg->iph->daddr && qp->protocol == arg->iph->protocol && qp->user == arg->user && qp->vif == arg->vif; } #ifdef HAVE_INET_FRAGS_CONST static void ip4_frag_init(struct inet_frag_queue *q, const void *a) #else static void ip4_frag_init(struct inet_frag_queue *q, void *a) #endif { struct ipq *qp = container_of(q, struct ipq, q); struct netns_ipv4 *ipv4 = container_of(q->net, struct netns_ipv4, frags); struct net *net = container_of(ipv4, struct net, ipv4); const struct ip4_create_arg *arg = a; qp->protocol = arg->iph->protocol; qp->id = arg->iph->id; qp->ecn = ip4_frag_ecn(arg->iph->tos); qp->saddr = arg->iph->saddr; qp->daddr = arg->iph->daddr; qp->vif = arg->vif; qp->user = arg->user; qp->peer = sysctl_ipfrag_max_dist ? inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, arg->vif, 1) : NULL; } static void ip4_frag_free(struct inet_frag_queue *q) { struct ipq *qp; qp = container_of(q, struct ipq, q); if (qp->peer) inet_putpeer(qp->peer); } /* Destruction primitives. */ static void ipq_put(struct ipq *ipq) { inet_frag_put(&ipq->q, &ip4_frags); } /* Kill ipq entry. It is not destroyed immediately, * because caller (and someone more) holds reference count. */ static void ipq_kill(struct ipq *ipq) { inet_frag_kill(&ipq->q, &ip4_frags); } static bool frag_expire_skip_icmp(u32 user) { return user == IP_DEFRAG_AF_PACKET || ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_IN, __IP_DEFRAG_CONNTRACK_IN_END) || ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_BRIDGE_IN, __IP_DEFRAG_CONNTRACK_BRIDGE_IN); } /* * Oops, a fragment queue timed out. Kill it and send an ICMP reply. */ static void ip_expire(unsigned long arg) { struct ipq *qp; struct net *net; qp = container_of((struct inet_frag_queue *) arg, struct ipq, q); net = container_of(qp->q.net, struct net, ipv4.frags); spin_lock(&qp->q.lock); if (qp_flags(qp) & INET_FRAG_COMPLETE) goto out; ipq_kill(qp); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); if (!inet_frag_evicting(&qp->q)) { struct sk_buff *head = qp->q.fragments; const struct iphdr *iph; int err; IP_INC_STATS_BH(net, IPSTATS_MIB_REASMTIMEOUT); if (!(qp_flags(qp) & INET_FRAG_FIRST_IN) || !qp->q.fragments) goto out; rcu_read_lock(); head->dev = dev_get_by_index_rcu(net, qp->iif); if (!head->dev) goto out_rcu_unlock; /* skb has no dst, perform route lookup again */ iph = ip_hdr(head); err = ip_route_input_noref(head, iph->daddr, iph->saddr, iph->tos, head->dev); if (err) goto out_rcu_unlock; /* Only an end host needs to send an ICMP * "Fragment Reassembly Timeout" message, per RFC792. */ if (frag_expire_skip_icmp(qp->user) && (skb_rtable(head)->rt_type != RTN_LOCAL)) goto out_rcu_unlock; /* Send an ICMP "Fragment Reassembly Timeout" message. */ icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); out_rcu_unlock: rcu_read_unlock(); } out: spin_unlock(&qp->q.lock); ipq_put(qp); } /* Find the correct entry in the "incomplete datagrams" queue for * this IP datagram, and create new one, if nothing is found. */ static struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user, int vif) { struct inet_frag_queue *q; struct ip4_create_arg arg; unsigned int hash; arg.iph = iph; arg.user = user; arg.vif = vif; hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); q = inet_frag_find(&net->ipv4.frags, &ip4_frags, &arg, hash); if (IS_ERR_OR_NULL(q)) { inet_frag_maybe_warn_overflow(q, pr_fmt()); return NULL; } return container_of(q, struct ipq, q); } /* Is the fragment too far ahead to be part of ipq? */ static int ip_frag_too_far(struct ipq *qp) { struct inet_peer *peer = qp->peer; unsigned int max = sysctl_ipfrag_max_dist; unsigned int start, end; int rc; if (!peer || !max) return 0; start = qp->rid; end = atomic_inc_return(&peer->rid); qp->rid = end; rc = qp->q.fragments && (end - start) > max; if (rc) { struct net *net; net = container_of(qp->q.net, struct net, ipv4.frags); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); } return rc; } static int ip_frag_reinit(struct ipq *qp) { struct sk_buff *fp; unsigned int sum_truesize = 0; if (!mod_timer(&qp->q.timer, jiffies + qp->q.net->timeout)) { atomic_inc(&qp->q.refcnt); return -ETIMEDOUT; } fp = qp->q.fragments; do { struct sk_buff *xp = fp->next; sum_truesize += fp->truesize; kfree_skb(fp); fp = xp; } while (fp); sub_frag_mem_limit(qp->q.net, sum_truesize); qp_flags(qp) = 0; qp->q.len = 0; qp->q.meat = 0; qp->q.fragments = NULL; qp->q.fragments_tail = NULL; qp->iif = 0; qp->ecn = 0; return 0; } /* Add new segment to existing queue. */ static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) { struct sk_buff *prev, *next; struct net_device *dev; unsigned int fragsize; int flags, offset; int ihl, end; int err = -ENOENT; u8 ecn; if (qp_flags(qp) & INET_FRAG_COMPLETE) goto err; if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && unlikely(ip_frag_too_far(qp)) && unlikely(err = ip_frag_reinit(qp))) { ipq_kill(qp); goto err; } ecn = ip4_frag_ecn(ip_hdr(skb)->tos); offset = ntohs(ip_hdr(skb)->frag_off); flags = offset & ~IP_OFFSET; offset &= IP_OFFSET; offset <<= 3; /* offset is in 8-byte chunks */ ihl = ip_hdrlen(skb); /* Determine the position of this fragment. */ end = offset + skb->len - skb_network_offset(skb) - ihl; err = -EINVAL; /* Is this the final fragment? */ if ((flags & IP_MF) == 0) { /* If we already have some bits beyond end * or have different end, the segment is corrupted. */ if (end < qp->q.len || ((qp_flags(qp) & INET_FRAG_LAST_IN) && end != qp->q.len)) goto err; qp_flags(qp) |= INET_FRAG_LAST_IN; qp->q.len = end; } else { if (end&7) { end &= ~7; if (skb->ip_summed != CHECKSUM_UNNECESSARY) skb->ip_summed = CHECKSUM_NONE; } if (end > qp->q.len) { /* Some bits beyond end -> corruption. */ if (qp_flags(qp) & INET_FRAG_LAST_IN) goto err; qp->q.len = end; } } if (end == offset) goto err; err = -ENOMEM; if (!pskb_pull(skb, skb_network_offset(skb) + ihl)) goto err; err = pskb_trim_rcsum(skb, end - offset); if (err) goto err; /* Find out which fragments are in front and at the back of us * in the chain of fragments so far. We must know where to put * this fragment, right? */ prev = qp->q.fragments_tail; if (!prev || FRAG_CB(prev)->offset < offset) { next = NULL; goto found; } prev = NULL; for (next = qp->q.fragments; next != NULL; next = next->next) { if (FRAG_CB(next)->offset >= offset) break; /* bingo! */ prev = next; } found: /* We found where to put this one. Check for overlap with * preceding fragment, and, if needed, align things so that * any overlaps are eliminated. */ if (prev) { int i = (FRAG_CB(prev)->offset + prev->len) - offset; if (i > 0) { offset += i; err = -EINVAL; if (end <= offset) goto err; err = -ENOMEM; if (!pskb_pull(skb, i)) goto err; if (skb->ip_summed != CHECKSUM_UNNECESSARY) skb->ip_summed = CHECKSUM_NONE; } } err = -ENOMEM; while (next && FRAG_CB(next)->offset < end) { int i = end - FRAG_CB(next)->offset; /* overlap is 'i' bytes */ if (i < next->len) { /* Eat head of the next overlapped fragment * and leave the loop. The next ones cannot overlap. */ if (!pskb_pull(next, i)) goto err; FRAG_CB(next)->offset += i; qp->q.meat -= i; if (next->ip_summed != CHECKSUM_UNNECESSARY) next->ip_summed = CHECKSUM_NONE; break; } else { struct sk_buff *free_it = next; /* Old fragment is completely overridden with * new one drop it. */ next = next->next; if (prev) prev->next = next; else qp->q.fragments = next; qp->q.meat -= free_it->len; sub_frag_mem_limit(qp->q.net, free_it->truesize); kfree_skb(free_it); } } FRAG_CB(skb)->offset = offset; /* Insert this fragment in the chain of fragments. */ skb->next = next; if (!next) qp->q.fragments_tail = skb; if (prev) prev->next = skb; else qp->q.fragments = skb; dev = skb->dev; if (dev) { qp->iif = dev->ifindex; skb->dev = NULL; } qp->q.stamp = skb->tstamp; qp->q.meat += skb->len; qp->ecn |= ecn; add_frag_mem_limit(qp->q.net, skb->truesize); if (offset == 0) qp_flags(qp) |= INET_FRAG_FIRST_IN; fragsize = skb->len + ihl; if (fragsize > qp->q.max_size) qp->q.max_size = fragsize; if (ip_hdr(skb)->frag_off & htons(IP_DF) && fragsize > qp->max_df_size) qp->max_df_size = fragsize; if (qp_flags(qp) == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && qp->q.meat == qp->q.len) { unsigned long orefdst = skb->_skb_refdst; skb->_skb_refdst = 0UL; err = ip_frag_reasm(qp, prev, dev); skb->_skb_refdst = orefdst; return err; } skb_dst_drop(skb); return -EINPROGRESS; err: kfree_skb(skb); return err; } /* Build a new IP datagram from all its fragments. */ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, struct net_device *dev) { struct net *net = container_of(qp->q.net, struct net, ipv4.frags); struct iphdr *iph; struct sk_buff *fp, *head = qp->q.fragments; int len; int ihlen; int err; u8 ecn; ipq_kill(qp); ecn = ip_frag_ecn_table[qp->ecn]; if (unlikely(ecn == 0xff)) { err = -EINVAL; goto out_fail; } /* Make the one we just received the head. */ if (prev) { head = prev->next; fp = skb_clone(head, GFP_ATOMIC); if (!fp) goto out_nomem; fp->next = head->next; if (!fp->next) qp->q.fragments_tail = fp; prev->next = fp; skb_morph(head, qp->q.fragments); head->next = qp->q.fragments->next; consume_skb(qp->q.fragments); qp->q.fragments = head; } WARN_ON(!head); WARN_ON(FRAG_CB(head)->offset != 0); /* Allocate a new buffer for the datagram. */ ihlen = ip_hdrlen(head); len = ihlen + qp->q.len; err = -E2BIG; if (len > 65535) goto out_oversize; /* Head of list must not be cloned. */ if (skb_unclone(head, GFP_ATOMIC)) goto out_nomem; /* If the first fragment is fragmented itself, we split * it to two chunks: the first with data and paged part * and the second, holding only fragments. */ if (skb_has_frag_list(head)) { struct sk_buff *clone; int i, plen = 0; clone = alloc_skb(0, GFP_ATOMIC); if (!clone) goto out_nomem; clone->next = head->next; head->next = clone; skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; skb_frag_list_init(head); for (i = 0; i < skb_shinfo(head)->nr_frags; i++) plen += skb_frag_size(&skb_shinfo(head)->frags[i]); clone->len = clone->data_len = head->data_len - plen; head->data_len -= clone->len; head->len -= clone->len; clone->csum = 0; clone->ip_summed = head->ip_summed; add_frag_mem_limit(qp->q.net, clone->truesize); } skb_shinfo(head)->frag_list = head->next; skb_push(head, head->data - skb_network_header(head)); for (fp=head->next; fp; fp = fp->next) { head->data_len += fp->len; head->len += fp->len; if (head->ip_summed != fp->ip_summed) head->ip_summed = CHECKSUM_NONE; else if (head->ip_summed == CHECKSUM_COMPLETE) head->csum = csum_add(head->csum, fp->csum); head->truesize += fp->truesize; } sub_frag_mem_limit(qp->q.net, head->truesize); head->next = NULL; head->dev = dev; head->tstamp = qp->q.stamp; IPCB(head)->frag_max_size = max(qp->max_df_size, qp->q.max_size); iph = ip_hdr(head); iph->tot_len = htons(len); iph->tos |= ecn; /* When we set IP_DF on a refragmented skb we must also force a * call to ip_fragment to avoid forwarding a DF-skb of size s while * original sender only sent fragments of size f (where f < s). * * We only set DF/IPSKB_FRAG_PMTU if such DF fragment was the largest * frag seen to avoid sending tiny DF-fragments in case skb was built * from one very small df-fragment and one large non-df frag. */ if (qp->max_df_size == qp->q.max_size) { IPCB(head)->flags |= IPSKB_FRAG_PMTU; iph->frag_off = htons(IP_DF); } else { iph->frag_off = 0; } ip_send_check(iph); IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS); qp->q.fragments = NULL; qp->q.fragments_tail = NULL; return 0; out_nomem: net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp); err = -ENOMEM; goto out_fail; out_oversize: net_info_ratelimited("Oversized IP packet from %pI4\n", &qp->saddr); out_fail: IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); return err; } /* Process an incoming IP datagram fragment. */ int rpl_ip_defrag(struct sk_buff *skb, u32 user) { struct net_device *dev = skb->dev ? : skb_dst(skb)->dev; int vif = vrf_master_ifindex_rcu(dev); struct net *net = dev_net(dev); struct ipq *qp; IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS); skb_orphan(skb); /* Lookup (or create) queue header */ qp = ip_find(net, ip_hdr(skb), user, vif); if (qp) { int ret; spin_lock(&qp->q.lock); ret = ip_frag_queue(qp, skb); spin_unlock(&qp->q.lock); ipq_put(qp); return ret; } IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); kfree_skb(skb); return -ENOMEM; } EXPORT_SYMBOL_GPL(rpl_ip_defrag); static int __net_init ipv4_frags_init_net(struct net *net) { nf_defrag_ipv4_enable(); return 0; } static void __net_exit ipv4_frags_exit_net(struct net *net) { inet_frags_exit_net(&net->ipv4.frags, &ip4_frags); } static struct pernet_operations ip4_frags_ops = { .init = ipv4_frags_init_net, .exit = ipv4_frags_exit_net, }; int __init rpl_ipfrag_init(void) { register_pernet_subsys(&ip4_frags_ops); ip4_frags.hashfn = ip4_hashfn; ip4_frags.constructor = ip4_frag_init; ip4_frags.destructor = ip4_frag_free; ip4_frags.skb_free = NULL; ip4_frags.qsize = sizeof(struct ipq); ip4_frags.match = ip4_frag_match; ip4_frags.frag_expire = ip_expire; #ifdef HAVE_INET_FRAGS_WITH_FRAGS_WORK ip4_frags.frags_cache_name = ip_frag_cache_name; #endif #if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8,0) ip4_frags.secret_interval = 10 * 60 * HZ; #endif if (inet_frags_init(&ip4_frags)) { pr_warn("IP: failed to allocate ip4_frags cache\n"); return -ENOMEM; } return 0; } void rpl_ipfrag_fini(void) { inet_frags_fini(&ip4_frags); unregister_pernet_subsys(&ip4_frags_ops); } #endif /* !HAVE_CORRECT_MRU_HANDLING && OVS_FRAGMENT_BACKPORT */ openvswitch-2.5.9/datapath/linux/PaxHeaders.82075/Makefile.main.in0000644000000000000000000000013213534540071021561 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801424.637852364 openvswitch-2.5.9/datapath/linux/Makefile.main.in0000644000175000017500000000601413534540071023250 0ustar00jpettitjpettit00000000000000# -*- makefile -*- export builddir = @abs_builddir@ export srcdir = @abs_srcdir@ export top_srcdir = @abs_top_srcdir@ export KSRC = @KBUILD@ export VERSION = @VERSION@ include $(srcdir)/../Modules.mk include $(srcdir)/Modules.mk default: $(build_links) $(foreach s,$(sort $(foreach m,$(build_modules),$($(m)_sources))), \ $(eval $(notdir $(s)): ; ln -s $(srcdir)/../$(s) $@)) all: default distdir: clean install: install-data: install-exec: uninstall: install-dvi: install-html: install-info: install-ps: install-pdf: installdirs: check: all installcheck: mostlyclean: clean: rm -f *.o *.ko *.mod.* Module.symvers .*.cmd kcompat.h.new \ modules.order .tmp_versions/openvswitch.mod for d in $(build_links); do if test -h $$d; then rm $$d; fi; done distclean: clean rm -f kcompat.h maintainer-clean: distclean dvi: pdf: ps: info: html: tags: TAGS: ifneq ($(KSRC),) ifeq (/lib/modules/$(shell uname -r)/source, $(KSRC)) KOBJ := /lib/modules/$(shell uname -r)/build else KOBJ := $(KSRC) endif VERSION_FILE := $(KOBJ)/include/linux/version.h ifeq (,$(wildcard $(VERSION_FILE))) VERSION_FILE := $(KOBJ)/include/generated/uapi/linux/version.h ifeq (,$(wildcard $(VERSION_FILE))) $(error Linux kernel source not configured - missing version.h) endif endif CONFIG_FILE := $(KSRC)/include/generated/autoconf.h ifeq (,$(wildcard $(CONFIG_FILE))) CONFIG_FILE := $(KSRC)/include/linux/autoconf.h ifeq (,$(wildcard $(CONFIG_FILE))) $(error Linux kernel source not configured - missing autoconf.h) endif endif default: $(MAKE) -C $(KSRC) M=$(builddir) modules modules_install: $(MAKE) -C $(KSRC) M=$(builddir) modules_install depmod `sed -n 's/#define UTS_RELEASE "\([^"]*\)"/\1/p' $(KSRC)/include/generated/utsrelease.h` endif # Much of the kernel build system in this file is derived from Intel's # e1000 distribution, with the following license: ################################################################################ # # Intel PRO/1000 Linux driver # Copyright(c) 1999 - 2007, 2009 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, # version 2, as published by the Free Software Foundation. # # This program is distributed in the hope it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. # # The full GNU General Public License is included in this distribution in # the file called "COPYING". # # Contact Information: # Linux NICS # e1000-devel Mailing List # Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 # ################################################################################ openvswitch-2.5.9/datapath/linux/PaxHeaders.82075/Modules.mk0000644000000000000000000000013213534540071020532 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.413858086 openvswitch-2.5.9/datapath/linux/Modules.mk0000644000175000017500000000771313534540071022230 0ustar00jpettitjpettit00000000000000openvswitch_sources += \ linux/compat/dev-openvswitch.c \ linux/compat/exthdrs_core.c \ linux/compat/flex_array.c \ linux/compat/flow_dissector.c \ linux/compat/geneve.c \ linux/compat/gre.c \ linux/compat/gso.c \ linux/compat/genetlink-openvswitch.c \ linux/compat/inet_fragment.c \ linux/compat/ip_gre.c \ linux/compat/ip_fragment.c \ linux/compat/ip_output.c \ linux/compat/ip_tunnel.c \ linux/compat/ip_tunnels_core.c \ linux/compat/ip6_output.c \ linux/compat/lisp.c \ linux/compat/netdevice.c \ linux/compat/net_namespace.c \ linux/compat/nf_conntrack_core.c \ linux/compat/nf_conntrack_reasm.c \ linux/compat/reassembly.c \ linux/compat/reciprocal_div.c \ linux/compat/skbuff-openvswitch.c \ linux/compat/socket.c \ linux/compat/stt.c \ linux/compat/udp.c \ linux/compat/udp_tunnel.c \ linux/compat/vxlan.c \ linux/compat/utils.c openvswitch_headers += \ linux/compat/gso.h \ linux/compat/include/linux/percpu.h \ linux/compat/include/linux/bug.h \ linux/compat/include/linux/compiler.h \ linux/compat/include/linux/compiler-gcc.h \ linux/compat/include/linux/cpumask.h \ linux/compat/include/linux/err.h \ linux/compat/include/linux/etherdevice.h \ linux/compat/include/linux/flex_array.h \ linux/compat/include/linux/icmp.h \ linux/compat/include/linux/icmpv6.h \ linux/compat/include/linux/if.h \ linux/compat/include/linux/if_arp.h \ linux/compat/include/linux/if_ether.h \ linux/compat/include/linux/if_link.h \ linux/compat/include/linux/if_vlan.h \ linux/compat/include/linux/in.h \ linux/compat/include/linux/ip.h \ linux/compat/include/linux/ipv6.h \ linux/compat/include/linux/jiffies.h \ linux/compat/include/linux/kconfig.h \ linux/compat/include/linux/kernel.h \ linux/compat/include/net/lisp.h \ linux/compat/include/linux/list.h \ linux/compat/include/linux/mpls.h \ linux/compat/include/linux/net.h \ linux/compat/include/linux/random.h \ linux/compat/include/linux/netdevice.h \ linux/compat/include/linux/netdev_features.h \ linux/compat/include/linux/netfilter_ipv6.h \ linux/compat/include/linux/netlink.h \ linux/compat/include/linux/openvswitch.h \ linux/compat/include/linux/poison.h \ linux/compat/include/linux/rculist.h \ linux/compat/include/linux/rcupdate.h \ linux/compat/include/linux/reciprocal_div.h \ linux/compat/include/linux/rtnetlink.h \ linux/compat/include/linux/sctp.h \ linux/compat/include/linux/skbuff.h \ linux/compat/include/linux/stddef.h \ linux/compat/include/linux/tcp.h \ linux/compat/include/linux/types.h \ linux/compat/include/linux/u64_stats_sync.h \ linux/compat/include/linux/udp.h \ linux/compat/include/linux/workqueue.h \ linux/compat/include/net/checksum.h \ linux/compat/include/net/dst.h \ linux/compat/include/net/dst_metadata.h \ linux/compat/include/net/flow_keys.h \ linux/compat/include/net/genetlink.h \ linux/compat/include/net/geneve.h \ linux/compat/include/net/gre.h \ linux/compat/include/net/inet_ecn.h \ linux/compat/include/net/inet_frag.h \ linux/compat/include/net/inetpeer.h \ linux/compat/include/net/ip.h \ linux/compat/include/net/ip_tunnels.h \ linux/compat/include/net/ip6_route.h \ linux/compat/include/net/ip6_tunnel.h \ linux/compat/include/net/ipv6.h \ linux/compat/include/net/mpls.h \ linux/compat/include/net/net_namespace.h \ linux/compat/include/net/netlink.h \ linux/compat/include/net/route.h \ linux/compat/include/net/rtnetlink.h \ linux/compat/include/net/udp.h \ linux/compat/include/net/udp_tunnel.h \ linux/compat/include/net/sock.h \ linux/compat/include/net/stt.h \ linux/compat/include/net/vrf.h \ linux/compat/include/net/vxlan.h \ linux/compat/include/net/netfilter/nf_conntrack_core.h \ linux/compat/include/net/netfilter/nf_conntrack_expect.h \ linux/compat/include/net/netfilter/nf_conntrack_labels.h \ linux/compat/include/net/netfilter/nf_conntrack_zones.h \ linux/compat/include/net/netfilter/ipv6/nf_defrag_ipv6.h \ linux/compat/include/net/sctp/checksum.h EXTRA_DIST += linux/compat/build-aux/export-check-whitelist openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-internal_dev.c0000644000000000000000000000013213534540071021420 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.521858881 openvswitch-2.5.9/datapath/vport-internal_dev.c0000644000175000017500000001760213534540071023114 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport-internal_dev.h" #include "vport-netdev.h" struct internal_dev { struct vport *vport; }; static struct vport_ops ovs_internal_vport_ops; static struct internal_dev *internal_dev_priv(struct net_device *netdev) { return netdev_priv(netdev); } /* Called with rcu_read_lock_bh. */ static int internal_dev_xmit(struct sk_buff *skb, struct net_device *netdev) { int len, err; len = skb->len; rcu_read_lock(); err = ovs_vport_receive(internal_dev_priv(netdev)->vport, skb, NULL); rcu_read_unlock(); if (likely(!err)) { #ifdef HAVE_DEV_TSTATS struct pcpu_sw_netstats *tstats; tstats = this_cpu_ptr((struct pcpu_sw_netstats __percpu *)netdev->tstats); u64_stats_update_begin(&tstats->syncp); tstats->tx_bytes += len; tstats->tx_packets++; u64_stats_update_end(&tstats->syncp); #endif } else { netdev->stats.tx_errors++; } return 0; } static int internal_dev_open(struct net_device *netdev) { netif_start_queue(netdev); return 0; } static int internal_dev_stop(struct net_device *netdev) { netif_stop_queue(netdev); return 0; } static void internal_dev_getinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { strlcpy(info->driver, "openvswitch", sizeof(info->driver)); } static const struct ethtool_ops internal_dev_ethtool_ops = { .get_drvinfo = internal_dev_getinfo, .get_link = ethtool_op_get_link, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tx_csum = ethtool_op_get_tx_csum, .set_tx_csum = ethtool_op_set_tx_hw_csum, .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso, #endif }; static int internal_dev_change_mtu(struct net_device *netdev, int new_mtu) { if (new_mtu < 68) return -EINVAL; netdev->mtu = new_mtu; return 0; } static void internal_dev_destructor(struct net_device *dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); ovs_vport_free(vport); free_netdev(dev); } #ifdef HAVE_DEV_TSTATS static int internal_dev_init(struct net_device *dev) { dev->tstats = (typeof(dev->tstats)) netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; return 0; } static void internal_dev_uninit(struct net_device *dev) { free_percpu(dev->tstats); } #endif static const struct net_device_ops internal_dev_netdev_ops = { #ifdef HAVE_DEV_TSTATS .ndo_init = internal_dev_init, .ndo_uninit = internal_dev_uninit, .ndo_get_stats64 = ip_tunnel_get_stats64, #endif .ndo_open = internal_dev_open, .ndo_stop = internal_dev_stop, .ndo_start_xmit = internal_dev_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = internal_dev_change_mtu, }; static struct rtnl_link_ops internal_dev_link_ops __read_mostly = { .kind = "openvswitch", }; static void do_setup(struct net_device *netdev) { ether_setup(netdev); netdev->netdev_ops = &internal_dev_netdev_ops; netdev->priv_flags &= ~IFF_TX_SKB_SHARING; netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_OPENVSWITCH; netdev->destructor = internal_dev_destructor; netdev->ethtool_ops = &internal_dev_ethtool_ops; netdev->rtnl_link_ops = &internal_dev_link_ops; netdev->tx_queue_len = 0; netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ENCAP_ALL; netdev->vlan_features = netdev->features; netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0) netdev->hw_enc_features = netdev->features; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) netdev->hw_features = netdev->features & ~NETIF_F_LLTX; #endif eth_hw_addr_random(netdev); } static struct vport *internal_dev_create(const struct vport_parms *parms) { struct vport *vport; struct internal_dev *internal_dev; int err; vport = ovs_vport_alloc(0, &ovs_internal_vport_ops, parms); if (IS_ERR(vport)) { err = PTR_ERR(vport); goto error; } vport->dev = alloc_netdev(sizeof(struct internal_dev), parms->name, NET_NAME_UNKNOWN, do_setup); if (!vport->dev) { err = -ENOMEM; goto error_free_vport; } dev_net_set(vport->dev, ovs_dp_get_net(vport->dp)); internal_dev = internal_dev_priv(vport->dev); internal_dev->vport = vport; /* Restrict bridge port to current netns. */ if (vport->port_no == OVSP_LOCAL) vport->dev->features |= NETIF_F_NETNS_LOCAL; rtnl_lock(); err = register_netdevice(vport->dev); if (err) goto error_free_netdev; dev_set_promiscuity(vport->dev, 1); rtnl_unlock(); netif_start_queue(vport->dev); return vport; error_free_netdev: rtnl_unlock(); free_netdev(vport->dev); error_free_vport: ovs_vport_free(vport); error: return ERR_PTR(err); } static void internal_dev_destroy(struct vport *vport) { netif_stop_queue(vport->dev); rtnl_lock(); dev_set_promiscuity(vport->dev, -1); /* unregister_netdevice() waits for an RCU grace period. */ unregister_netdevice(vport->dev); rtnl_unlock(); } static netdev_tx_t internal_dev_recv(struct sk_buff *skb) { struct net_device *netdev = skb->dev; #ifdef HAVE_DEV_TSTATS struct pcpu_sw_netstats *stats; #endif if (unlikely(!(netdev->flags & IFF_UP))) { kfree_skb(skb); netdev->stats.rx_dropped++; return NETDEV_TX_OK; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) if (skb_vlan_tag_present(skb)) { if (unlikely(!vlan_insert_tag_set_proto(skb, skb->vlan_proto, skb_vlan_tag_get(skb)))) return NETDEV_TX_OK; if (skb->ip_summed == CHECKSUM_COMPLETE) skb->csum = csum_add(skb->csum, csum_partial(skb->data + (2 * ETH_ALEN), VLAN_HLEN, 0)); vlan_set_tci(skb, 0); } #endif skb_dst_drop(skb); nf_reset(skb); secpath_reset(skb); skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, netdev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); #ifdef HAVE_DEV_TSTATS stats = this_cpu_ptr((struct pcpu_sw_netstats __percpu *)netdev->tstats); u64_stats_update_begin(&stats->syncp); stats->rx_packets++; stats->rx_bytes += skb->len; u64_stats_update_end(&stats->syncp); #endif netif_rx(skb); return NETDEV_TX_OK; } static struct vport_ops ovs_internal_vport_ops = { .type = OVS_VPORT_TYPE_INTERNAL, .create = internal_dev_create, .destroy = internal_dev_destroy, .send = internal_dev_recv, }; int ovs_is_internal_dev(const struct net_device *netdev) { return netdev->netdev_ops == &internal_dev_netdev_ops; } struct vport *ovs_internal_dev_get_vport(struct net_device *netdev) { if (!ovs_is_internal_dev(netdev)) return NULL; return internal_dev_priv(netdev)->vport; } int ovs_internal_dev_rtnl_link_register(void) { int err; err = rtnl_link_register(&internal_dev_link_ops); if (err < 0) return err; err = ovs_vport_ops_register(&ovs_internal_vport_ops); if (err < 0) rtnl_link_unregister(&internal_dev_link_ops); return err; } void ovs_internal_dev_rtnl_link_unregister(void) { ovs_vport_ops_unregister(&ovs_internal_vport_ops); rtnl_link_unregister(&internal_dev_link_ops); } openvswitch-2.5.9/datapath/PaxHeaders.82075/flow_table.h0000644000000000000000000000013213534540071017721 xustar0030 mtime=1567801401.249680053 30 atime=1567801402.053685959 30 ctime=1567801425.421858144 openvswitch-2.5.9/datapath/flow_table.h0000644000175000017500000000606013534540071021411 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef FLOW_TABLE_H #define FLOW_TABLE_H 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "flow.h" struct mask_cache_entry { u32 skb_hash; u32 mask_index; }; struct mask_array { struct rcu_head rcu; int count, max; struct sw_flow_mask __rcu *masks[]; }; struct table_instance { struct flex_array *buckets; unsigned int n_buckets; struct rcu_head rcu; int node_ver; u32 hash_seed; bool keep_flows; }; struct flow_table { struct table_instance __rcu *ti; struct table_instance __rcu *ufid_ti; struct mask_cache_entry __percpu *mask_cache; struct mask_array __rcu *mask_array; unsigned long last_rehash; unsigned int count; unsigned int ufid_count; }; extern struct kmem_cache *flow_stats_cache; int ovs_flow_init(void); void ovs_flow_exit(void); struct sw_flow *ovs_flow_alloc(void); void ovs_flow_free(struct sw_flow *, bool deferred); int ovs_flow_tbl_init(struct flow_table *); int ovs_flow_tbl_count(const struct flow_table *table); void ovs_flow_tbl_destroy(struct flow_table *table); int ovs_flow_tbl_flush(struct flow_table *flow_table); int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, const struct sw_flow_mask *mask); void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); int ovs_flow_tbl_num_masks(const struct flow_table *table); struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table, u32 *bucket, u32 *idx); struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *, const struct sw_flow_key *, u32 skb_hash, u32 *n_mask_hit); struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, const struct sw_flow_key *); struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, const struct sw_flow_match *match); struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *, const struct sw_flow_id *); bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, bool full, const struct sw_flow_mask *mask); #endif /* flow_table.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/flow.h0000644000000000000000000000013213534540071016552 xustar0030 mtime=1567801401.245680025 30 atime=1567801402.053685959 30 ctime=1567801425.417858114 openvswitch-2.5.9/datapath/flow.h0000644000175000017500000001537013534540071020246 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #ifndef FLOW_H #define FLOW_H 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sk_buff; /* Store options at the end of the array if they are less than the * maximum size. This allows us to get the benefits of variable length * matching for small options. */ #define TUN_METADATA_OFFSET(opt_len) \ (FIELD_SIZEOF(struct sw_flow_key, tun_opts) - opt_len) #define TUN_METADATA_OPTS(flow_key, opt_len) \ ((void *)((flow_key)->tun_opts + TUN_METADATA_OFFSET(opt_len))) struct ovs_tunnel_info { struct metadata_dst *tun_dst; }; #define OVS_SW_FLOW_KEY_METADATA_SIZE \ (offsetof(struct sw_flow_key, recirc_id) + \ FIELD_SIZEOF(struct sw_flow_key, recirc_id)) struct sw_flow_key { u8 tun_opts[255]; u8 tun_opts_len; struct ip_tunnel_key tun_key; /* Encapsulating tunnel key. */ struct { u32 priority; /* Packet QoS priority. */ u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ } __packed phy; /* Safe when right after 'tun_key'. */ u32 ovs_flow_hash; /* Datapath computed hash value. */ u32 recirc_id; /* Recirculation ID. */ struct { u8 src[ETH_ALEN]; /* Ethernet source address. */ u8 dst[ETH_ALEN]; /* Ethernet destination address. */ __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ __be16 type; /* Ethernet frame type. */ } eth; union { struct { __be32 top_lse; /* top label stack entry */ } mpls; struct { u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */ u8 tos; /* IP ToS. */ u8 ttl; /* IP TTL/hop limit. */ u8 frag; /* One of OVS_FRAG_TYPE_*. */ } ip; }; struct { __be16 src; /* TCP/UDP/SCTP source port. */ __be16 dst; /* TCP/UDP/SCTP destination port. */ __be16 flags; /* TCP flags. */ } tp; union { struct { struct { __be32 src; /* IP source address. */ __be32 dst; /* IP destination address. */ } addr; struct { u8 sha[ETH_ALEN]; /* ARP source hardware address. */ u8 tha[ETH_ALEN]; /* ARP target hardware address. */ } arp; } ipv4; struct { struct { struct in6_addr src; /* IPv6 source address. */ struct in6_addr dst; /* IPv6 destination address. */ } addr; __be32 label; /* IPv6 flow label. */ struct { struct in6_addr target; /* ND target address. */ u8 sll[ETH_ALEN]; /* ND source link layer address. */ u8 tll[ETH_ALEN]; /* ND target link layer address. */ } nd; } ipv6; }; struct { /* Connection tracking fields. */ u16 zone; u32 mark; u8 state; struct ovs_key_ct_labels labels; } ct; } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */ struct sw_flow_key_range { unsigned short int start; unsigned short int end; }; struct sw_flow_mask { int ref_count; struct rcu_head rcu; struct sw_flow_key_range range; struct sw_flow_key key; }; struct sw_flow_match { struct sw_flow_key *key; struct sw_flow_key_range range; struct sw_flow_mask *mask; }; #define MAX_UFID_LENGTH 16 /* 128 bits */ struct sw_flow_id { u32 ufid_len; union { u32 ufid[MAX_UFID_LENGTH / 4]; struct sw_flow_key *unmasked_key; }; }; struct sw_flow_actions { struct rcu_head rcu; size_t orig_len; /* From flow_cmd_new netlink actions size */ u32 actions_len; struct nlattr actions[]; }; struct flow_stats { u64 packet_count; /* Number of packets matched. */ u64 byte_count; /* Number of bytes matched. */ unsigned long used; /* Last used time (in jiffies). */ spinlock_t lock; /* Lock for atomic stats update. */ __be16 tcp_flags; /* Union of seen TCP flags. */ }; struct sw_flow { struct rcu_head rcu; struct { struct hlist_node node[2]; u32 hash; } flow_table, ufid_table; int stats_last_writer; /* NUMA-node id of the last writer on * 'stats[0]'. */ struct sw_flow_key key; struct sw_flow_id id; struct sw_flow_mask *mask; struct sw_flow_actions __rcu *sf_acts; struct flow_stats __rcu *stats[]; /* One for each NUMA node. First one * is allocated at flow creation time, * the rest are allocated on demand * while holding the 'stats[0].lock'. */ }; struct arp_eth_header { __be16 ar_hrd; /* format of hardware address */ __be16 ar_pro; /* format of protocol address */ unsigned char ar_hln; /* length of hardware address */ unsigned char ar_pln; /* length of protocol address */ __be16 ar_op; /* ARP opcode (command) */ /* Ethernet+IPv4 specific members. */ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ unsigned char ar_sip[4]; /* sender IP address */ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ unsigned char ar_tip[4]; /* target IP address */ } __packed; static inline bool ovs_identifier_is_ufid(const struct sw_flow_id *sfid) { return sfid->ufid_len; } static inline bool ovs_identifier_is_key(const struct sw_flow_id *sfid) { return !ovs_identifier_is_ufid(sfid); } void ovs_flow_stats_update(struct sw_flow *, __be16 tcp_flags, const struct sk_buff *); void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *, unsigned long *used, __be16 *tcp_flags); void ovs_flow_stats_clear(struct sw_flow *); u64 ovs_flow_used_time(unsigned long flow_jiffies); /* Update the non-metadata part of the flow key using skb. */ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key); int ovs_flow_key_update_l3l4(struct sk_buff *skb, struct sw_flow_key *key); int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key); /* Extract key from packet coming from userspace. */ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr, struct sk_buff *skb, struct sw_flow_key *key, bool log); #endif /* flow.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/vport-lisp.c0000644000000000000000000000013213534540071017715 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.561859176 openvswitch-2.5.9/datapath/vport-lisp.c0000644000175000017500000000667113534540071021415 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "vport.h" #include "vport-netdev.h" static struct vport_ops ovs_lisp_vport_ops; /** * struct lisp_port - Keeps track of open UDP ports * @dst_port: destination port. */ struct lisp_port { u16 port_no; }; static inline struct lisp_port *lisp_vport(const struct vport *vport) { return vport_priv(vport); } static int lisp_get_options(const struct vport *vport, struct sk_buff *skb) { struct lisp_port *lisp_port = lisp_vport(vport); if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, lisp_port->port_no)) return -EMSGSIZE; return 0; } static int lisp_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { struct lisp_port *lisp_port = lisp_vport(vport); struct net *net = ovs_dp_get_net(vport->dp); __be16 dport = htons(lisp_port->port_no); __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); return ovs_tunnel_get_egress_info(upcall, ovs_dp_get_net(vport->dp), skb, IPPROTO_UDP, sport, dport); } static struct vport *lisp_tnl_create(const struct vport_parms *parms) { struct net *net = ovs_dp_get_net(parms->dp); struct nlattr *options = parms->options; struct lisp_port *lisp_port; struct net_device *dev; struct vport *vport; struct nlattr *a; u16 dst_port; int err; if (!options) { err = -EINVAL; goto error; } a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); if (a && nla_len(a) == sizeof(u16)) { dst_port = nla_get_u16(a); } else { /* Require destination port from userspace. */ err = -EINVAL; goto error; } vport = ovs_vport_alloc(sizeof(struct lisp_port), &ovs_lisp_vport_ops, parms); if (IS_ERR(vport)) return vport; lisp_port = lisp_vport(vport); lisp_port->port_no = dst_port; rtnl_lock(); dev = lisp_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port); if (IS_ERR(dev)) { rtnl_unlock(); ovs_vport_free(vport); return ERR_CAST(dev); } dev_change_flags(dev, dev->flags | IFF_UP); rtnl_unlock(); return vport; error: return ERR_PTR(err); } static struct vport *lisp_create(const struct vport_parms *parms) { struct vport *vport; vport = lisp_tnl_create(parms); if (IS_ERR(vport)) return vport; return ovs_netdev_link(vport, parms->name); } static struct vport_ops ovs_lisp_vport_ops = { .type = OVS_VPORT_TYPE_LISP, .create = lisp_create, .destroy = ovs_netdev_tunnel_destroy, .get_options = lisp_get_options, .send = lisp_xmit, .get_egress_tun_info = lisp_get_egress_tun_info, }; static int __init ovs_lisp_tnl_init(void) { return ovs_vport_ops_register(&ovs_lisp_vport_ops); } static void __exit ovs_lisp_tnl_exit(void) { ovs_vport_ops_unregister(&ovs_lisp_vport_ops); } module_init(ovs_lisp_tnl_init); module_exit(ovs_lisp_tnl_exit); MODULE_DESCRIPTION("OVS: Lisp switching port"); MODULE_LICENSE("GPL"); MODULE_ALIAS("vport-type-105"); openvswitch-2.5.9/datapath/PaxHeaders.82075/Makefile.am0000644000000000000000000000013213534540071017466 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801425.409858056 openvswitch-2.5.9/datapath/Makefile.am0000644000175000017500000000417413534540071021162 0ustar00jpettitjpettit00000000000000SUBDIRS = if LINUX_ENABLED SUBDIRS += linux endif EXTRA_DIST = $(dist_headers) $(dist_sources) $(dist_extras) # Suppress warnings about GNU extensions in Modules.mk files. AUTOMAKE_OPTIONS = -Wno-portability include Modules.mk include linux/Modules.mk # The following is based on commands for the Automake "distdir" target. distfiles: Makefile @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | sort -u > $@ CLEANFILES = distfiles # Print name of all modules. print-build-modules: @if test -z "$(build_modules)"; \ then \ echo "Could not find any kernel module."; \ exit 1; \ fi @echo "$(build_modules)" | tr '_' '-'; COMPAT_GET_FUNCTIONS := find $(top_srcdir)/datapath/linux/compat -name "*.h" \ -exec sed -n '/^[a-z][a-z]* \*\?[A-Za-z0-9_][A-Za-z0-9_]*([a-z]/p; /^struct [a-z0-9_][a-z0-9_]* \*\?[A-Za-z0-9_][A-Za-z0-9_]*([a-z]/p' {} \; | tr -d '*' | cut -d '(' -f1 | rev | cut -d ' ' -f1 | rev COMPAT_GET_EXPORTS := find $(top_srcdir)/datapath/linux/compat -name "*.c" \ -exec sed -n 's/^EXPORT_SYMBOL[A-Z_]*(\([a-z_][a-z_]*\));$$/\1/p' {} \; COMPAT_FUNCTIONS := $(shell $(COMPAT_GET_FUNCTIONS)) COMPAT_EXPORTS := $(shell $(COMPAT_GET_EXPORTS)) # Checks that all public functions are 'rpl_' or 'ovs_' prefixed. # Checks that all EXPORT_SYMBOL_GPL() export 'rpl_' or 'ovs_' prefixed functions. check-export-symbol: @for fun_ in $(COMPAT_FUNCTIONS); do \ if ! grep -- $${fun_} $(top_srcdir)/datapath/linux/compat/build-aux/export-check-whitelist > /dev/null; then \ if ! echo $${fun_} | grep -q -E '^(rpl|ovs)_'; then \ echo "error: $${fun_}() needs to be prefixed with 'rpl_' or 'ovs_'."; \ exit 1; \ fi; \ fi; \ done @for fun_ in $(COMPAT_EXPORTS); do \ if ! echo $${fun_} | grep -q -E '^(rpl|ovs)_'; then \ echo "error: $${fun_}() needs to be prefixed with 'rpl_' or 'ovs_'."; \ exit 1; \ fi; \ done all-local: check-export-symbol openvswitch-2.5.9/datapath/PaxHeaders.82075/vport.c0000644000000000000000000000013213534540071016750 xustar0030 mtime=1567801401.281680288 30 atime=1567801402.061686017 30 ctime=1567801425.521858881 openvswitch-2.5.9/datapath/vport.c0000644000175000017500000003625413534540071020450 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2007-2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datapath.h" #include "gso.h" #include "vport.h" #include "vport-internal_dev.h" static LIST_HEAD(vport_ops_list); /* Protected by RCU read lock for reading, ovs_mutex for writing. */ static struct hlist_head *dev_table; #define VPORT_HASH_BUCKETS 1024 /** * ovs_vport_init - initialize vport subsystem * * Called at module load time to initialize the vport subsystem. */ int ovs_vport_init(void) { int err; dev_table = kzalloc(VPORT_HASH_BUCKETS * sizeof(struct hlist_head), GFP_KERNEL); if (!dev_table) return -ENOMEM; err = lisp_init_module(); if (err) goto err_lisp; err = ipgre_init(); if (err) goto err_gre; err = geneve_init_module(); if (err) goto err_geneve; err = vxlan_init_module(); if (err) goto err_vxlan; err = ovs_stt_init_module(); if (err) goto err_stt; return 0; err_stt: vxlan_cleanup_module(); err_vxlan: geneve_cleanup_module(); err_geneve: ipgre_fini(); err_gre: lisp_cleanup_module(); err_lisp: kfree(dev_table); return err; } /** * ovs_vport_exit - shutdown vport subsystem * * Called at module exit time to shutdown the vport subsystem. */ void ovs_vport_exit(void) { ovs_stt_cleanup_module(); vxlan_cleanup_module(); geneve_cleanup_module(); ipgre_fini(); lisp_cleanup_module(); kfree(dev_table); } static struct hlist_head *hash_bucket(const struct net *net, const char *name) { unsigned int hash = jhash(name, strlen(name), (unsigned long) net); return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)]; } int __ovs_vport_ops_register(struct vport_ops *ops) { int err = -EEXIST; struct vport_ops *o; ovs_lock(); list_for_each_entry(o, &vport_ops_list, list) if (ops->type == o->type) goto errout; list_add_tail(&ops->list, &vport_ops_list); err = 0; errout: ovs_unlock(); return err; } EXPORT_SYMBOL_GPL(__ovs_vport_ops_register); void ovs_vport_ops_unregister(struct vport_ops *ops) { ovs_lock(); list_del(&ops->list); ovs_unlock(); } EXPORT_SYMBOL_GPL(ovs_vport_ops_unregister); /** * ovs_vport_locate - find a port that has already been created * * @name: name of port to find * * Must be called with ovs or RCU read lock. */ struct vport *ovs_vport_locate(const struct net *net, const char *name) { struct hlist_head *bucket = hash_bucket(net, name); struct vport *vport; hlist_for_each_entry_rcu(vport, bucket, hash_node) if (!strcmp(name, ovs_vport_name(vport)) && net_eq(ovs_dp_get_net(vport->dp), net)) return vport; return NULL; } /** * ovs_vport_alloc - allocate and initialize new vport * * @priv_size: Size of private data area to allocate. * @ops: vport device ops * * Allocate and initialize a new vport defined by @ops. The vport will contain * a private data area of size @priv_size that can be accessed using * vport_priv(). vports that are no longer needed should be released with * vport_free(). */ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, const struct vport_parms *parms) { struct vport *vport; size_t alloc_size; alloc_size = sizeof(struct vport); if (priv_size) { alloc_size = ALIGN(alloc_size, VPORT_ALIGN); alloc_size += priv_size; } vport = kzalloc(alloc_size, GFP_KERNEL); if (!vport) return ERR_PTR(-ENOMEM); vport->dp = parms->dp; vport->port_no = parms->port_no; vport->ops = ops; INIT_HLIST_NODE(&vport->dp_hash_node); if (ovs_vport_set_upcall_portids(vport, parms->upcall_portids)) { kfree(vport); return ERR_PTR(-EINVAL); } return vport; } EXPORT_SYMBOL_GPL(ovs_vport_alloc); /** * ovs_vport_free - uninitialize and free vport * * @vport: vport to free * * Frees a vport allocated with vport_alloc() when it is no longer needed. * * The caller must ensure that an RCU grace period has passed since the last * time @vport was in a datapath. */ void ovs_vport_free(struct vport *vport) { /* vport is freed from RCU callback or error path, Therefore * it is safe to use raw dereference. */ kfree(rcu_dereference_raw(vport->upcall_portids)); kfree(vport); } EXPORT_SYMBOL_GPL(ovs_vport_free); static struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms) { struct vport_ops *ops; list_for_each_entry(ops, &vport_ops_list, list) if (ops->type == parms->type) return ops; return NULL; } /** * ovs_vport_add - add vport device (for kernel callers) * * @parms: Information about new vport. * * Creates a new vport with the specified configuration (which is dependent on * device type). ovs_mutex must be held. */ struct vport *ovs_vport_add(const struct vport_parms *parms) { struct vport_ops *ops; struct vport *vport; ops = ovs_vport_lookup(parms); if (ops) { struct hlist_head *bucket; if (!try_module_get(ops->owner)) return ERR_PTR(-EAFNOSUPPORT); vport = ops->create(parms); if (IS_ERR(vport)) { module_put(ops->owner); return vport; } bucket = hash_bucket(ovs_dp_get_net(vport->dp), ovs_vport_name(vport)); hlist_add_head_rcu(&vport->hash_node, bucket); return vport; } /* Unlock to attempt module load and return -EAGAIN if load * was successful as we need to restart the port addition * workflow. */ ovs_unlock(); request_module("vport-type-%d", parms->type); ovs_lock(); if (!ovs_vport_lookup(parms)) return ERR_PTR(-EAFNOSUPPORT); else return ERR_PTR(-EAGAIN); } /** * ovs_vport_set_options - modify existing vport device (for kernel callers) * * @vport: vport to modify. * @options: New configuration. * * Modifies an existing device with the specified configuration (which is * dependent on device type). ovs_mutex must be held. */ int ovs_vport_set_options(struct vport *vport, struct nlattr *options) { if (!vport->ops->set_options) return -EOPNOTSUPP; return vport->ops->set_options(vport, options); } /** * ovs_vport_del - delete existing vport device * * @vport: vport to delete. * * Detaches @vport from its datapath and destroys it. It is possible to fail * for reasons such as lack of memory. ovs_mutex must be held. */ void ovs_vport_del(struct vport *vport) { ASSERT_OVSL(); hlist_del_rcu(&vport->hash_node); module_put(vport->ops->owner); vport->ops->destroy(vport); } /** * ovs_vport_get_stats - retrieve device stats * * @vport: vport from which to retrieve the stats * @stats: location to store stats * * Retrieves transmit, receive, and error stats for the given device. * * Must be called with ovs_mutex or rcu_read_lock. */ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) { const struct rtnl_link_stats64 *dev_stats; struct rtnl_link_stats64 temp; dev_stats = dev_get_stats(vport->dev, &temp); stats->rx_errors = dev_stats->rx_errors; stats->tx_errors = dev_stats->tx_errors; stats->tx_dropped = dev_stats->tx_dropped; stats->rx_dropped = dev_stats->rx_dropped; stats->rx_bytes = dev_stats->rx_bytes; stats->rx_packets = dev_stats->rx_packets; stats->tx_bytes = dev_stats->tx_bytes; stats->tx_packets = dev_stats->tx_packets; } /** * ovs_vport_get_options - retrieve device options * * @vport: vport from which to retrieve the options. * @skb: sk_buff where options should be appended. * * Retrieves the configuration of the given device, appending an * %OVS_VPORT_ATTR_OPTIONS attribute that in turn contains nested * vport-specific attributes to @skb. * * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room, or another * negative error code if a real error occurred. If an error occurs, @skb is * left unmodified. * * Must be called with ovs_mutex or rcu_read_lock. */ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) { struct nlattr *nla; int err; if (!vport->ops->get_options) return 0; nla = nla_nest_start(skb, OVS_VPORT_ATTR_OPTIONS); if (!nla) return -EMSGSIZE; err = vport->ops->get_options(vport, skb); if (err) { nla_nest_cancel(skb, nla); return err; } nla_nest_end(skb, nla); return 0; } static void vport_portids_destroy_rcu_cb(struct rcu_head *rcu) { struct vport_portids *ids = container_of(rcu, struct vport_portids, rcu); kfree(ids); } /** * ovs_vport_set_upcall_portids - set upcall portids of @vport. * * @vport: vport to modify. * @ids: new configuration, an array of port ids. * * Sets the vport's upcall_portids to @ids. * * Returns 0 if successful, -EINVAL if @ids is zero length or cannot be parsed * as an array of U32. * * Must be called with ovs_mutex. */ int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids) { struct vport_portids *old, *vport_portids; if (!nla_len(ids) || nla_len(ids) % sizeof(u32)) return -EINVAL; old = ovsl_dereference(vport->upcall_portids); vport_portids = kmalloc(sizeof(*vport_portids) + nla_len(ids), GFP_KERNEL); if (!vport_portids) return -ENOMEM; vport_portids->n_ids = nla_len(ids) / sizeof(u32); vport_portids->rn_ids = reciprocal_value(vport_portids->n_ids); nla_memcpy(vport_portids->ids, ids, nla_len(ids)); rcu_assign_pointer(vport->upcall_portids, vport_portids); if (old) call_rcu(&old->rcu, vport_portids_destroy_rcu_cb); return 0; } /** * ovs_vport_get_upcall_portids - get the upcall_portids of @vport. * * @vport: vport from which to retrieve the portids. * @skb: sk_buff where portids should be appended. * * Retrieves the configuration of the given vport, appending the * %OVS_VPORT_ATTR_UPCALL_PID attribute which is the array of upcall * portids to @skb. * * Returns 0 if successful, -EMSGSIZE if @skb has insufficient room. * If an error occurs, @skb is left unmodified. Must be called with * ovs_mutex or rcu_read_lock. */ int ovs_vport_get_upcall_portids(const struct vport *vport, struct sk_buff *skb) { struct vport_portids *ids; ids = rcu_dereference_ovsl(vport->upcall_portids); if (vport->dp->user_features & OVS_DP_F_VPORT_PIDS) return nla_put(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->n_ids * sizeof(u32), (void *)ids->ids); else return nla_put_u32(skb, OVS_VPORT_ATTR_UPCALL_PID, ids->ids[0]); } /** * ovs_vport_find_upcall_portid - find the upcall portid to send upcall. * * @vport: vport from which the missed packet is received. * @skb: skb that the missed packet was received. * * Uses the skb_get_hash() to select the upcall portid to send the * upcall. * * Returns the portid of the target socket. Must be called with rcu_read_lock. */ u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb) { struct vport_portids *ids; u32 ids_index; u32 hash; ids = rcu_dereference(vport->upcall_portids); if (ids->n_ids == 1 && ids->ids[0] == 0) return 0; hash = skb_get_hash(skb); ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids); return ids->ids[ids_index]; } /** * ovs_vport_receive - pass up received packet to the datapath for processing * * @vport: vport that received the packet * @skb: skb that was received * @tun_key: tunnel (if any) that carried packet * * Must be called with rcu_read_lock. The packet cannot be shared and * skb->data should point to the Ethernet header. */ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, const struct ip_tunnel_info *tun_info) { struct sw_flow_key key; int error; OVS_CB(skb)->input_vport = vport; OVS_CB(skb)->mru = 0; if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { u32 mark; mark = skb->mark; skb_scrub_packet(skb, true); skb->mark = mark; tun_info = NULL; } ovs_skb_init_inner_protocol(skb); skb_clear_ovs_gso_cb(skb); /* Extract flow from 'skb' into 'key'. */ error = ovs_flow_key_extract(tun_info, skb, &key); if (unlikely(error)) { kfree_skb(skb); return error; } ovs_dp_process_packet(skb, &key); return 0; } EXPORT_SYMBOL_GPL(ovs_vport_receive); static void free_vport_rcu(struct rcu_head *rcu) { struct vport *vport = container_of(rcu, struct vport, rcu); ovs_vport_free(vport); } void ovs_vport_deferred_free(struct vport *vport) { if (!vport) return; call_rcu(&vport->rcu, free_vport_rcu); } EXPORT_SYMBOL_GPL(ovs_vport_deferred_free); int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall, struct net *net, struct sk_buff *skb, u8 ipproto, __be16 tp_src, __be16 tp_dst) { struct ip_tunnel_info *egress_tun_info = upcall->egress_tun_info; struct ip_tunnel_info *tun_info = skb_tunnel_info(skb); const struct ip_tunnel_key *tun_key; u32 skb_mark = skb->mark; struct rtable *rt; struct flowi4 fl; if (unlikely(!tun_info)) return -EINVAL; if (ip_tunnel_info_af(tun_info) != AF_INET) return -EINVAL; tun_key = &tun_info->key; /* Route lookup to get srouce IP address. * The process may need to be changed if the corresponding process * in vports ops changed. */ rt = ovs_tunnel_route_lookup(net, tun_key, skb_mark, &fl, ipproto); if (IS_ERR(rt)) return PTR_ERR(rt); ip_rt_put(rt); /* Generate egress_tun_info based on tun_info, * saddr, tp_src and tp_dst */ ip_tunnel_key_init(&egress_tun_info->key, fl.saddr, tun_key->u.ipv4.dst, tun_key->tos, tun_key->ttl, tp_src, tp_dst, tun_key->tun_id, tun_key->tun_flags); egress_tun_info->options_len = tun_info->options_len; egress_tun_info->mode = tun_info->mode; upcall->egress_tun_opts = ip_tunnel_info_opts(tun_info); return 0; } EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info); int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, struct dp_upcall_info *upcall) { /* get_egress_tun_info() is only implemented on tunnel ports. */ if (unlikely(!vport->ops->get_egress_tun_info)) return -EINVAL; return vport->ops->get_egress_tun_info(vport, skb, upcall); } static unsigned int packet_length(const struct sk_buff *skb) { unsigned int length = skb->len - ETH_HLEN; if (skb->protocol == htons(ETH_P_8021Q)) length -= VLAN_HLEN; return length; } void ovs_vport_send(struct vport *vport, struct sk_buff *skb) { int mtu = vport->dev->mtu; if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", vport->dev->name, packet_length(skb), mtu); vport->dev->stats.tx_errors++; goto drop; } skb->dev = vport->dev; vport->ops->send(skb); return; drop: kfree_skb(skb); } openvswitch-2.5.9/datapath/PaxHeaders.82075/conntrack.h0000644000000000000000000000013213534540071017565 xustar0030 mtime=1567801401.241679995 30 atime=1567801402.053685959 30 ctime=1567801425.413858086 openvswitch-2.5.9/datapath/conntrack.h0000644000175000017500000000521613534540071021257 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #ifndef OVS_CONNTRACK_H #define OVS_CONNTRACK_H 1 #include #include "flow.h" struct ovs_conntrack_info; enum ovs_key_attr; #if IS_ENABLED(CONFIG_NF_CONNTRACK) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) void ovs_ct_init(struct net *); void ovs_ct_exit(struct net *); bool ovs_ct_verify(struct net *, enum ovs_key_attr attr); int ovs_ct_copy_action(struct net *, const struct nlattr *, const struct sw_flow_key *, struct sw_flow_actions **, bool log); int ovs_ct_action_to_attr(const struct ovs_conntrack_info *, struct sk_buff *); int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *, const struct ovs_conntrack_info *); void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key); int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb); void ovs_ct_free_action(const struct nlattr *a); #define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \ OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | \ OVS_CS_F_INVALID | OVS_CS_F_TRACKED) #else #include static inline void ovs_ct_init(struct net *net) { } static inline void ovs_ct_exit(struct net *net) { } static inline bool ovs_ct_verify(struct net *net, int attr) { return false; } static inline int ovs_ct_copy_action(struct net *net, const struct nlattr *nla, const struct sw_flow_key *key, struct sw_flow_actions **acts, bool log) { return -ENOTSUPP; } static inline int ovs_ct_action_to_attr(const struct ovs_conntrack_info *info, struct sk_buff *skb) { return -ENOTSUPP; } static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_conntrack_info *info) { kfree_skb(skb); return -ENOTSUPP; } static inline void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) { key->ct.state = 0; key->ct.zone = 0; key->ct.mark = 0; memset(&key->ct.labels, 0, sizeof(key->ct.labels)); } static inline int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb) { return 0; } static inline void ovs_ct_free_action(const struct nlattr *a) { } #define CT_SUPPORTED_MASK 0 #endif #endif /* ovs_conntrack.h */ openvswitch-2.5.9/datapath/PaxHeaders.82075/Modules.mk0000644000000000000000000000013213534540071017373 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801425.409858056 openvswitch-2.5.9/datapath/Modules.mk0000644000175000017500000000277213534540071021071 0ustar00jpettitjpettit00000000000000# Some modules should be built and distributed, e.g. openvswitch. # # Some modules should be built but not distributed, e.g. third-party # hwtable modules. build_multi_modules = \ openvswitch both_modules = \ $(build_multi_modules) \ vport_geneve \ vport_gre \ vport_lisp \ vport_stt \ vport_vxlan # When changing the name of 'build_modules', please also update the # print-build-modules in Makefile.am. build_modules = $(both_modules) # Modules to build dist_modules = $(both_modules) # Modules to distribute openvswitch_sources = \ actions.c \ conntrack.c \ datapath.c \ dp_notify.c \ flow.c \ flow_netlink.c \ flow_table.c \ vport.c \ vport-internal_dev.c \ vport-netdev.c vport_geneve_sources = vport-geneve.c vport_vxlan_sources = vport-vxlan.c vport_gre_sources = vport-gre.c vport_lisp_sources = vport-lisp.c vport_stt_sources = vport-stt.c openvswitch_headers = \ compat.h \ conntrack.h \ datapath.h \ flow.h \ flow_netlink.h \ flow_table.h \ vlan.h \ vport.h \ vport-internal_dev.h \ vport-netdev.h openvswitch_extras = \ README.md dist_sources = $(foreach module,$(dist_modules),$($(module)_sources)) dist_headers = $(foreach module,$(dist_modules),$($(module)_headers)) dist_extras = $(foreach module,$(dist_modules),$($(module)_extras)) build_sources = $(foreach module,$(build_modules),$($(module)_sources)) build_headers = $(foreach module,$(build_modules),$($(module)_headers)) build_links = $(notdir $(build_sources)) build_objects = $(notdir $(patsubst %.c,%.o,$(build_sources))) openvswitch-2.5.9/PaxHeaders.82075/ovsdb0000644000000000000000000000013013534540121014676 xustar0029 mtime=1567801425.13785605 30 atime=1567801425.625859648 29 ctime=1567801425.13785605 openvswitch-2.5.9/ovsdb/0000755000175000017500000000000013534540121016443 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovsdb/PaxHeaders.82075/condition.c0000644000000000000000000000013213534540071017113 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.117686428 30 ctime=1567801425.073855578 openvswitch-2.5.9/ovsdb/condition.c0000644000175000017500000002227213534540071020606 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "condition.h" #include #include "column.h" #include "json.h" #include "ovsdb-error.h" #include "row.h" #include "table.h" struct ovsdb_error * ovsdb_function_from_string(const char *name, enum ovsdb_function *function) { #define OVSDB_FUNCTION(ENUM, NAME) \ if (!strcmp(name, NAME)) { \ *function = ENUM; \ return NULL; \ } OVSDB_FUNCTIONS; #undef OVSDB_FUNCTION return ovsdb_syntax_error(NULL, "unknown function", "No function named %s.", name); } const char * ovsdb_function_to_string(enum ovsdb_function function) { switch (function) { #define OVSDB_FUNCTION(ENUM, NAME) case ENUM: return NAME; OVSDB_FUNCTIONS; #undef OVSDB_FUNCTION } return NULL; } static OVS_WARN_UNUSED_RESULT struct ovsdb_error * ovsdb_clause_from_json(const struct ovsdb_table_schema *ts, const struct json *json, struct ovsdb_symbol_table *symtab, struct ovsdb_clause *clause) { const struct json_array *array; struct ovsdb_error *error; const char *function_name; const char *column_name; struct ovsdb_type type; if (json->type != JSON_ARRAY || json->u.array.n != 3 || json->u.array.elems[0]->type != JSON_STRING || json->u.array.elems[1]->type != JSON_STRING) { return ovsdb_syntax_error(json, NULL, "Parse error in condition."); } array = json_array(json); column_name = json_string(array->elems[0]); clause->column = ovsdb_table_schema_get_column(ts, column_name); if (!clause->column) { return ovsdb_syntax_error(json, "unknown column", "No column %s in table %s.", column_name, ts->name); } type = clause->column->type; function_name = json_string(array->elems[1]); error = ovsdb_function_from_string(function_name, &clause->function); if (error) { return error; } /* Type-check and relax restrictions on 'type' if appropriate. */ switch (clause->function) { case OVSDB_F_LT: case OVSDB_F_LE: case OVSDB_F_GT: case OVSDB_F_GE: /* Allow these operators for types with n_min == 0, n_max == 1. * (They will always be "false" if the value is missing.) */ if (!(ovsdb_type_is_scalar(&type) || ovsdb_type_is_optional_scalar(&type)) || (type.key.type != OVSDB_TYPE_INTEGER && type.key.type != OVSDB_TYPE_REAL)) { char *s = ovsdb_type_to_english(&type); error = ovsdb_syntax_error( json, NULL, "Type mismatch: \"%s\" operator may not be " "applied to column %s of type %s.", ovsdb_function_to_string(clause->function), clause->column->name, s); free(s); return error; } break; case OVSDB_F_EQ: case OVSDB_F_NE: break; case OVSDB_F_EXCLUDES: if (!ovsdb_type_is_scalar(&type)) { type.n_min = 0; type.n_max = UINT_MAX; } break; case OVSDB_F_INCLUDES: if (!ovsdb_type_is_scalar(&type)) { type.n_min = 0; } break; } return ovsdb_datum_from_json(&clause->arg, &type, array->elems[2], symtab); } static void ovsdb_clause_free(struct ovsdb_clause *clause) { ovsdb_datum_destroy(&clause->arg, &clause->column->type); } static int compare_clauses_3way(const void *a_, const void *b_) { const struct ovsdb_clause *a = a_; const struct ovsdb_clause *b = b_; if (a->function != b->function) { /* Bring functions to the front based on the fraction of table rows * that they are (heuristically) expected to leave in the query * results. Note that "enum ovsdb_function" is intentionally ordered * to make this trivial. */ return a->function < b->function ? -1 : 1; } else if (a->column->index != b->column->index) { if (a->column->index < OVSDB_N_STD_COLUMNS || b->column->index < OVSDB_N_STD_COLUMNS) { /* Bring the standard columns and in particular the UUID column * (since OVSDB_COL_UUID has value 0) to the front. We have an * index on the UUID column, so that makes our queries cheaper. */ return a->column->index < b->column->index ? -1 : 1; } else { /* Order clauses predictably to make testing easier. */ return strcmp(a->column->name, b->column->name); } } else { return 0; } } struct ovsdb_error * ovsdb_condition_from_json(const struct ovsdb_table_schema *ts, const struct json *json, struct ovsdb_symbol_table *symtab, struct ovsdb_condition *cnd) { const struct json_array *array = json_array(json); size_t i; cnd->clauses = xmalloc(array->n * sizeof *cnd->clauses); cnd->n_clauses = 0; for (i = 0; i < array->n; i++) { struct ovsdb_error *error; error = ovsdb_clause_from_json(ts, array->elems[i], symtab, &cnd->clauses[i]); if (error) { ovsdb_condition_destroy(cnd); cnd->clauses = NULL; cnd->n_clauses = 0; return error; } cnd->n_clauses++; } /* A real database would have a query optimizer here. */ qsort(cnd->clauses, cnd->n_clauses, sizeof *cnd->clauses, compare_clauses_3way); return NULL; } static struct json * ovsdb_clause_to_json(const struct ovsdb_clause *clause) { return json_array_create_3( json_string_create(clause->column->name), json_string_create(ovsdb_function_to_string(clause->function)), ovsdb_datum_to_json(&clause->arg, &clause->column->type)); } struct json * ovsdb_condition_to_json(const struct ovsdb_condition *cnd) { struct json **clauses; size_t i; clauses = xmalloc(cnd->n_clauses * sizeof *clauses); for (i = 0; i < cnd->n_clauses; i++) { clauses[i] = ovsdb_clause_to_json(&cnd->clauses[i]); } return json_array_create(clauses, cnd->n_clauses); } static bool ovsdb_clause_evaluate(const struct ovsdb_row *row, const struct ovsdb_clause *c) { const struct ovsdb_datum *field = &row->fields[c->column->index]; const struct ovsdb_datum *arg = &c->arg; const struct ovsdb_type *type = &c->column->type; if (ovsdb_type_is_optional_scalar(type) && field->n == 0) { switch (c->function) { case OVSDB_F_LT: case OVSDB_F_LE: case OVSDB_F_EQ: case OVSDB_F_GE: case OVSDB_F_GT: case OVSDB_F_INCLUDES: return false; case OVSDB_F_NE: case OVSDB_F_EXCLUDES: return true; } } else if (ovsdb_type_is_scalar(type) || ovsdb_type_is_optional_scalar(type)) { int cmp = ovsdb_atom_compare_3way(&field->keys[0], &arg->keys[0], type->key.type); switch (c->function) { case OVSDB_F_LT: return cmp < 0; case OVSDB_F_LE: return cmp <= 0; case OVSDB_F_EQ: case OVSDB_F_INCLUDES: return cmp == 0; case OVSDB_F_NE: case OVSDB_F_EXCLUDES: return cmp != 0; case OVSDB_F_GE: return cmp >= 0; case OVSDB_F_GT: return cmp > 0; } } else { switch (c->function) { case OVSDB_F_EQ: return ovsdb_datum_equals(field, arg, type); case OVSDB_F_NE: return !ovsdb_datum_equals(field, arg, type); case OVSDB_F_INCLUDES: return ovsdb_datum_includes_all(arg, field, type); case OVSDB_F_EXCLUDES: return ovsdb_datum_excludes_all(arg, field, type); case OVSDB_F_LT: case OVSDB_F_LE: case OVSDB_F_GE: case OVSDB_F_GT: OVS_NOT_REACHED(); } } OVS_NOT_REACHED(); } bool ovsdb_condition_evaluate(const struct ovsdb_row *row, const struct ovsdb_condition *cnd) { size_t i; for (i = 0; i < cnd->n_clauses; i++) { if (!ovsdb_clause_evaluate(row, &cnd->clauses[i])) { return false; } } return true; } void ovsdb_condition_destroy(struct ovsdb_condition *cnd) { size_t i; for (i = 0; i < cnd->n_clauses; i++) { ovsdb_clause_free(&cnd->clauses[i]); } free(cnd->clauses); } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-server.c0000644000000000000000000000013113534540071017545 xustar0030 mtime=1567801401.821684255 30 atime=1567801402.121686458 29 ctime=1567801425.13785605 openvswitch-2.5.9/ovsdb/ovsdb-server.c0000644000175000017500000012527713534540071021252 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "column.h" #include "command-line.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "file.h" #include "hash.h" #include "json.h" #include "jsonrpc.h" #include "jsonrpc-server.h" #include "list.h" #include "memory.h" #include "ovsdb.h" #include "ovsdb-data.h" #include "ovsdb-types.h" #include "ovsdb-error.h" #include "poll-loop.h" #include "process.h" #include "row.h" #include "simap.h" #include "shash.h" #include "stream-ssl.h" #include "stream.h" #include "sset.h" #include "table.h" #include "timeval.h" #include "transaction.h" #include "trigger.h" #include "util.h" #include "unixctl.h" #include "perf-counter.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_server); struct db { /* Initialized in main(). */ char *filename; struct ovsdb_file *file; struct ovsdb *db; /* Only used by update_remote_status(). */ struct ovsdb_txn *txn; }; /* SSL configuration. */ static char *private_key_file; static char *certificate_file; static char *ca_cert_file; static bool bootstrap_ca_cert; static unixctl_cb_func ovsdb_server_exit; static unixctl_cb_func ovsdb_server_compact; static unixctl_cb_func ovsdb_server_reconnect; static unixctl_cb_func ovsdb_server_perf_counters_clear; static unixctl_cb_func ovsdb_server_perf_counters_show; struct server_config { struct sset *remotes; struct shash *all_dbs; FILE *config_tmpfile; struct ovsdb_jsonrpc_server *jsonrpc; }; static unixctl_cb_func ovsdb_server_add_remote; static unixctl_cb_func ovsdb_server_remove_remote; static unixctl_cb_func ovsdb_server_list_remotes; static unixctl_cb_func ovsdb_server_add_database; static unixctl_cb_func ovsdb_server_remove_database; static unixctl_cb_func ovsdb_server_list_databases; static char *open_db(struct server_config *config, const char *filename); static void close_db(struct db *db); static void parse_options(int *argc, char **argvp[], struct sset *remotes, char **unixctl_pathp, char **run_command); OVS_NO_RETURN static void usage(void); static char *reconfigure_remotes(struct ovsdb_jsonrpc_server *, const struct shash *all_dbs, struct sset *remotes); static char *reconfigure_ssl(const struct shash *all_dbs); static void report_error_if_changed(char *error, char **last_errorp); static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, const struct sset *remotes, struct shash *all_dbs); static void save_config__(FILE *config_file, const struct sset *remotes, const struct sset *db_filenames); static void save_config(struct server_config *); static void load_config(FILE *config_file, struct sset *remotes, struct sset *db_filenames); static void main_loop(struct ovsdb_jsonrpc_server *jsonrpc, struct shash *all_dbs, struct unixctl_server *unixctl, struct sset *remotes, struct process *run_process, bool *exiting) { char *remotes_error, *ssl_error; struct shash_node *node; long long int status_timer = LLONG_MIN; *exiting = false; ssl_error = NULL; remotes_error = NULL; while (!*exiting) { memory_run(); if (memory_should_report()) { struct simap usage; simap_init(&usage); ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage); SHASH_FOR_EACH(node, all_dbs) { struct db *db = node->data; ovsdb_get_memory_usage(db->db, &usage); } memory_report(&usage); simap_destroy(&usage); } /* Run unixctl_server_run() before reconfigure_remotes() because * ovsdb-server/add-remote and ovsdb-server/remove-remote can change * the set of remotes that reconfigure_remotes() uses. */ unixctl_server_run(unixctl); report_error_if_changed( reconfigure_remotes(jsonrpc, all_dbs, remotes), &remotes_error); report_error_if_changed(reconfigure_ssl(all_dbs), &ssl_error); ovsdb_jsonrpc_server_run(jsonrpc); SHASH_FOR_EACH(node, all_dbs) { struct db *db = node->data; ovsdb_trigger_run(db->db, time_msec()); } if (run_process) { process_run(); if (process_exited(run_process)) { *exiting = true; } } /* update Manager status(es) every 5 seconds */ if (time_msec() >= status_timer) { status_timer = time_msec() + 5000; update_remote_status(jsonrpc, remotes, all_dbs); } memory_wait(); ovsdb_jsonrpc_server_wait(jsonrpc); unixctl_server_wait(unixctl); SHASH_FOR_EACH(node, all_dbs) { struct db *db = node->data; ovsdb_trigger_wait(db->db, time_msec()); } if (run_process) { process_wait(run_process); } if (*exiting) { poll_immediate_wake(); } poll_timer_wait_until(status_timer); poll_block(); if (should_service_stop()) { *exiting = true; } } } int main(int argc, char *argv[]) { char *unixctl_path = NULL; char *run_command = NULL; struct unixctl_server *unixctl; struct ovsdb_jsonrpc_server *jsonrpc; struct sset remotes, db_filenames; const char *db_filename; struct process *run_process; bool exiting; int retval; FILE *config_tmpfile; struct server_config server_config; struct shash all_dbs; struct shash_node *node, *next; char *error; int i; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); fatal_ignore_sigpipe(); process_init(); parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command); daemon_become_new_user(false); /* Create and initialize 'config_tmpfile' as a temporary file to hold * ovsdb-server's most basic configuration, and then save our initial * configuration to it. When --monitor is used, this preserves the effects * of ovs-appctl commands such as ovsdb-server/add-remote (which saves the * new configuration) across crashes. */ config_tmpfile = tmpfile(); if (!config_tmpfile) { ovs_fatal(errno, "failed to create temporary file"); } sset_init(&db_filenames); if (argc > 0) { for (i = 0; i < argc; i++) { sset_add(&db_filenames, argv[i]); } } else { char *default_db = xasprintf("%s/conf.db", ovs_dbdir()); sset_add(&db_filenames, default_db); free(default_db); } server_config.remotes = &remotes; server_config.config_tmpfile = config_tmpfile; save_config__(config_tmpfile, &remotes, &db_filenames); daemonize_start(false); /* Load the saved config. */ load_config(config_tmpfile, &remotes, &db_filenames); jsonrpc = ovsdb_jsonrpc_server_create(); shash_init(&all_dbs); server_config.all_dbs = &all_dbs; server_config.jsonrpc = jsonrpc; perf_counters_init(); SSET_FOR_EACH (db_filename, &db_filenames) { error = open_db(&server_config, db_filename); if (error) { ovs_fatal(0, "%s", error); } } error = reconfigure_remotes(jsonrpc, &all_dbs, &remotes); if (!error) { error = reconfigure_ssl(&all_dbs); } if (error) { ovs_fatal(0, "%s", error); } retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } if (run_command) { char *run_argv[4]; run_argv[0] = "/bin/sh"; run_argv[1] = "-c"; run_argv[2] = run_command; run_argv[3] = NULL; retval = process_start(run_argv, &run_process); if (retval) { ovs_fatal(retval, "%s: process failed to start", run_command); } } else { run_process = NULL; } daemonize_complete(); if (!run_command) { /* ovsdb-server is usually a long-running process, in which case it * makes plenty of sense to log the version, but --run makes * ovsdb-server more like a command-line tool, so skip it. */ VLOG_INFO("%s (Open vSwitch) %s", program_name, VERSION); } unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting); unixctl_command_register("ovsdb-server/compact", "", 0, 1, ovsdb_server_compact, &all_dbs); unixctl_command_register("ovsdb-server/reconnect", "", 0, 0, ovsdb_server_reconnect, jsonrpc); unixctl_command_register("ovsdb-server/add-remote", "REMOTE", 1, 1, ovsdb_server_add_remote, &server_config); unixctl_command_register("ovsdb-server/remove-remote", "REMOTE", 1, 1, ovsdb_server_remove_remote, &server_config); unixctl_command_register("ovsdb-server/list-remotes", "", 0, 0, ovsdb_server_list_remotes, &remotes); unixctl_command_register("ovsdb-server/add-db", "DB", 1, 1, ovsdb_server_add_database, &server_config); unixctl_command_register("ovsdb-server/remove-db", "DB", 1, 1, ovsdb_server_remove_database, &server_config); unixctl_command_register("ovsdb-server/list-dbs", "", 0, 0, ovsdb_server_list_databases, &all_dbs); unixctl_command_register("ovsdb-server/perf-counters-show", "", 0, 0, ovsdb_server_perf_counters_show, NULL); unixctl_command_register("ovsdb-server/perf-counters-clear", "", 0, 0, ovsdb_server_perf_counters_clear, NULL); main_loop(jsonrpc, &all_dbs, unixctl, &remotes, run_process, &exiting); ovsdb_jsonrpc_server_destroy(jsonrpc); SHASH_FOR_EACH_SAFE(node, next, &all_dbs) { struct db *db = node->data; close_db(db); shash_delete(&all_dbs, node); } shash_destroy(&all_dbs); sset_destroy(&remotes); sset_destroy(&db_filenames); unixctl_server_destroy(unixctl); if (run_process && process_exited(run_process)) { int status = process_status(run_process); if (status) { ovs_fatal(0, "%s: child exited, %s", run_command, process_status_msg(status)); } } perf_counters_destroy(); service_stop(); return 0; } /* Returns true if 'filename' is known to be already open as a database, * false if not. * * "False negatives" are possible. */ static bool is_already_open(struct server_config *config OVS_UNUSED, const char *filename OVS_UNUSED) { #ifndef _WIN32 struct stat s; if (!stat(filename, &s)) { struct shash_node *node; SHASH_FOR_EACH (node, config->all_dbs) { struct db *db = node->data; struct stat s2; if (!stat(db->filename, &s2) && s.st_dev == s2.st_dev && s.st_ino == s2.st_ino) { return true; } } } #endif /* !_WIN32 */ return false; } static void close_db(struct db *db) { ovsdb_destroy(db->db); free(db->filename); free(db); } static char * open_db(struct server_config *config, const char *filename) { struct ovsdb_error *db_error; struct db *db; char *error; /* If we know that the file is already open, return a good error message. * Otherwise, if the file is open, we'll fail later on with a harder to * interpret file locking error. */ if (is_already_open(config, filename)) { return xasprintf("%s: already open", filename); } db = xzalloc(sizeof *db); db->filename = xstrdup(filename); db_error = ovsdb_file_open(db->filename, false, &db->db, &db->file); if (db_error) { error = ovsdb_error_to_string(db_error); } else if (!ovsdb_jsonrpc_server_add_db(config->jsonrpc, db->db)) { error = xasprintf("%s: duplicate database name", db->db->schema->name); } else { shash_add_assert(config->all_dbs, db->db->schema->name, db); return NULL; } ovsdb_error_destroy(db_error); close_db(db); return error; } static const struct db * find_db(const struct shash *all_dbs, const char *db_name) { struct shash_node *node; SHASH_FOR_EACH(node, all_dbs) { struct db *db = node->data; if (!strcmp(db->db->schema->name, db_name)) { return db; } } return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_db_column__(const struct shash *all_dbs, const char *name_, char *name, const struct db **dbp, const struct ovsdb_table **tablep, const struct ovsdb_column **columnp) { const char *db_name, *table_name, *column_name; const struct ovsdb_column *column; const struct ovsdb_table *table; const char *tokens[3]; char *save_ptr = NULL; const struct db *db; *dbp = NULL; *tablep = NULL; *columnp = NULL; strtok_r(name, ":", &save_ptr); /* "db:" */ tokens[0] = strtok_r(NULL, ",", &save_ptr); tokens[1] = strtok_r(NULL, ",", &save_ptr); tokens[2] = strtok_r(NULL, ",", &save_ptr); if (!tokens[0] || !tokens[1] || !tokens[2]) { return xasprintf("\"%s\": invalid syntax", name_); } db_name = tokens[0]; table_name = tokens[1]; column_name = tokens[2]; db = find_db(all_dbs, tokens[0]); if (!db) { return xasprintf("\"%s\": no database named %s", name_, db_name); } table = ovsdb_get_table(db->db, table_name); if (!table) { return xasprintf("\"%s\": no table named %s", name_, table_name); } column = ovsdb_table_schema_get_column(table->schema, column_name); if (!column) { return xasprintf("\"%s\": table \"%s\" has no column \"%s\"", name_, table_name, column_name); } *dbp = db; *columnp = column; *tablep = table; return NULL; } /* Returns NULL if successful, otherwise a malloc()'d string describing the * error. */ static char * OVS_WARN_UNUSED_RESULT parse_db_column(const struct shash *all_dbs, const char *name_, const struct db **dbp, const struct ovsdb_table **tablep, const struct ovsdb_column **columnp) { char *name = xstrdup(name_); char *retval = parse_db_column__(all_dbs, name_, name, dbp, tablep, columnp); free(name); return retval; } /* Returns NULL if successful, otherwise a malloc()'d string describing the * error. */ static char * OVS_WARN_UNUSED_RESULT parse_db_string_column(const struct shash *all_dbs, const char *name, const struct db **dbp, const struct ovsdb_table **tablep, const struct ovsdb_column **columnp) { char *retval; retval = parse_db_column(all_dbs, name, dbp, tablep, columnp); if (retval) { return retval; } if ((*columnp)->type.key.type != OVSDB_TYPE_STRING || (*columnp)->type.value.type != OVSDB_TYPE_VOID) { return xasprintf("\"%s\": table \"%s\" column \"%s\" is " "not string or set of strings", name, (*tablep)->schema->name, (*columnp)->name); } return NULL; } static const char * query_db_string(const struct shash *all_dbs, const char *name, struct ds *errors) { if (!name || strncmp(name, "db:", 3)) { return name; } else { const struct ovsdb_column *column; const struct ovsdb_table *table; const struct ovsdb_row *row; const struct db *db; char *retval; retval = parse_db_string_column(all_dbs, name, &db, &table, &column); if (retval) { ds_put_format(errors, "%s\n", retval); free(retval); return NULL; } HMAP_FOR_EACH (row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; size_t i; datum = &row->fields[column->index]; for (i = 0; i < datum->n; i++) { if (datum->keys[i].string[0]) { return datum->keys[i].string; } } } return NULL; } } static struct ovsdb_jsonrpc_options * add_remote(struct shash *remotes, const char *target) { struct ovsdb_jsonrpc_options *options; options = shash_find_data(remotes, target); if (!options) { options = ovsdb_jsonrpc_default_options(target); shash_add(remotes, target, options); } return options; } static struct ovsdb_datum * get_datum(struct ovsdb_row *row, const char *column_name, const enum ovsdb_atomic_type key_type, const enum ovsdb_atomic_type value_type, const size_t n_max) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); const struct ovsdb_table_schema *schema = row->table->schema; const struct ovsdb_column *column; column = ovsdb_table_schema_get_column(schema, column_name); if (!column) { VLOG_DBG_RL(&rl, "Table `%s' has no `%s' column", schema->name, column_name); return NULL; } if (column->type.key.type != key_type || column->type.value.type != value_type || column->type.n_max != n_max) { if (!VLOG_DROP_DBG(&rl)) { char *type_name = ovsdb_type_to_english(&column->type); VLOG_DBG("Table `%s' column `%s' has type %s, not expected " "key type %s, value type %s, max elements %"PRIuSIZE".", schema->name, column_name, type_name, ovsdb_atomic_type_to_string(key_type), ovsdb_atomic_type_to_string(value_type), n_max); free(type_name); } return NULL; } return &row->fields[column->index]; } /* Read string-string key-values from a map. Returns the value associated with * 'key', if found, or NULL */ static const char * read_map_string_column(const struct ovsdb_row *row, const char *column_name, const char *key) { const struct ovsdb_datum *datum; union ovsdb_atom *atom_key = NULL, *atom_value = NULL; size_t i; datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX); if (!datum) { return NULL; } for (i = 0; i < datum->n; i++) { atom_key = &datum->keys[i]; if (!strcmp(atom_key->string, key)){ atom_value = &datum->values[i]; break; } } return atom_value ? atom_value->string : NULL; } static const union ovsdb_atom * read_column(const struct ovsdb_row *row, const char *column_name, enum ovsdb_atomic_type type) { const struct ovsdb_datum *datum; datum = get_datum(CONST_CAST(struct ovsdb_row *, row), column_name, type, OVSDB_TYPE_VOID, 1); return datum && datum->n ? datum->keys : NULL; } static bool read_integer_column(const struct ovsdb_row *row, const char *column_name, long long int *integerp) { const union ovsdb_atom *atom; atom = read_column(row, column_name, OVSDB_TYPE_INTEGER); *integerp = atom ? atom->integer : 0; return atom != NULL; } static bool read_string_column(const struct ovsdb_row *row, const char *column_name, const char **stringp) { const union ovsdb_atom *atom; atom = read_column(row, column_name, OVSDB_TYPE_STRING); *stringp = atom ? atom->string : NULL; return atom != NULL; } static void write_bool_column(struct ovsdb_row *row, const char *column_name, bool value) { const struct ovsdb_column *column; struct ovsdb_datum *datum; column = ovsdb_table_schema_get_column(row->table->schema, column_name); datum = get_datum(row, column_name, OVSDB_TYPE_BOOLEAN, OVSDB_TYPE_VOID, 1); if (!datum) { return; } if (datum->n != 1) { ovsdb_datum_destroy(datum, &column->type); datum->n = 1; datum->keys = xmalloc(sizeof *datum->keys); datum->values = NULL; } datum->keys[0].boolean = value; } static void write_string_string_column(struct ovsdb_row *row, const char *column_name, char **keys, char **values, size_t n) { const struct ovsdb_column *column; struct ovsdb_datum *datum; size_t i; column = ovsdb_table_schema_get_column(row->table->schema, column_name); datum = get_datum(row, column_name, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING, UINT_MAX); if (!datum) { for (i = 0; i < n; i++) { free(keys[i]); free(values[i]); } return; } /* Free existing data. */ ovsdb_datum_destroy(datum, &column->type); /* Allocate space for new values. */ datum->n = n; datum->keys = xmalloc(n * sizeof *datum->keys); datum->values = xmalloc(n * sizeof *datum->values); for (i = 0; i < n; ++i) { datum->keys[i].string = keys[i]; datum->values[i].string = values[i]; } /* Sort and check constraints. */ ovsdb_datum_sort_assert(datum, column->type.key.type); } /* Adds a remote and options to 'remotes', based on the Manager table row in * 'row'. */ static void add_manager_options(struct shash *remotes, const struct ovsdb_row *row) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); struct ovsdb_jsonrpc_options *options; long long int max_backoff, probe_interval; const char *target, *dscp_string; if (!read_string_column(row, "target", &target) || !target) { VLOG_INFO_RL(&rl, "Table `%s' has missing or invalid `target' column", row->table->schema->name); return; } options = add_remote(remotes, target); if (read_integer_column(row, "max_backoff", &max_backoff)) { options->max_backoff = max_backoff; } if (read_integer_column(row, "inactivity_probe", &probe_interval)) { options->probe_interval = probe_interval; } options->dscp = DSCP_DEFAULT; dscp_string = read_map_string_column(row, "other_config", "dscp"); if (dscp_string) { int dscp = atoi(dscp_string); if (dscp >= 0 && dscp <= 63) { options->dscp = dscp; } } } static void query_db_remotes(const char *name, const struct shash *all_dbs, struct shash *remotes, struct ds *errors) { const struct ovsdb_column *column; const struct ovsdb_table *table; const struct ovsdb_row *row; const struct db *db; char *retval; retval = parse_db_column(all_dbs, name, &db, &table, &column); if (retval) { ds_put_format(errors, "%s\n", retval); free(retval); return; } if (column->type.key.type == OVSDB_TYPE_STRING && column->type.value.type == OVSDB_TYPE_VOID) { HMAP_FOR_EACH (row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; size_t i; datum = &row->fields[column->index]; for (i = 0; i < datum->n; i++) { add_remote(remotes, datum->keys[i].string); } } } else if (column->type.key.type == OVSDB_TYPE_UUID && column->type.key.u.uuid.refTable && column->type.value.type == OVSDB_TYPE_VOID) { const struct ovsdb_table *ref_table = column->type.key.u.uuid.refTable; HMAP_FOR_EACH (row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; size_t i; datum = &row->fields[column->index]; for (i = 0; i < datum->n; i++) { const struct ovsdb_row *ref_row; ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); if (ref_row) { add_manager_options(remotes, ref_row); } } } } } static void update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, const struct ovsdb_jsonrpc_server *jsonrpc) { struct ovsdb_jsonrpc_remote_status status; struct ovsdb_row *rw_row; const char *target; char *keys[9], *values[9]; size_t n = 0; /* Get the "target" (protocol/host/port) spec. */ if (!read_string_column(row, "target", &target)) { /* Bad remote spec or incorrect schema. */ return; } rw_row = ovsdb_txn_row_modify(txn, row); ovsdb_jsonrpc_server_get_remote_status(jsonrpc, target, &status); /* Update status information columns. */ write_bool_column(rw_row, "is_connected", status.is_connected); if (status.state) { keys[n] = xstrdup("state"); values[n++] = xstrdup(status.state); } if (status.sec_since_connect != UINT_MAX) { keys[n] = xstrdup("sec_since_connect"); values[n++] = xasprintf("%u", status.sec_since_connect); } if (status.sec_since_disconnect != UINT_MAX) { keys[n] = xstrdup("sec_since_disconnect"); values[n++] = xasprintf("%u", status.sec_since_disconnect); } if (status.last_error) { keys[n] = xstrdup("last_error"); values[n++] = xstrdup(ovs_retval_to_string(status.last_error)); } if (status.locks_held && status.locks_held[0]) { keys[n] = xstrdup("locks_held"); values[n++] = xstrdup(status.locks_held); } if (status.locks_waiting && status.locks_waiting[0]) { keys[n] = xstrdup("locks_waiting"); values[n++] = xstrdup(status.locks_waiting); } if (status.locks_lost && status.locks_lost[0]) { keys[n] = xstrdup("locks_lost"); values[n++] = xstrdup(status.locks_lost); } if (status.n_connections > 1) { keys[n] = xstrdup("n_connections"); values[n++] = xasprintf("%d", status.n_connections); } if (status.bound_port != htons(0)) { keys[n] = xstrdup("bound_port"); values[n++] = xasprintf("%"PRIu16, ntohs(status.bound_port)); } write_string_string_column(rw_row, "status", keys, values, n); ovsdb_jsonrpc_server_free_remote_status(&status); } static void update_remote_rows(const struct shash *all_dbs, const char *remote_name, const struct ovsdb_jsonrpc_server *jsonrpc) { const struct ovsdb_table *table, *ref_table; const struct ovsdb_column *column; const struct ovsdb_row *row; const struct db *db; char *retval; if (strncmp("db:", remote_name, 3)) { return; } retval = parse_db_column(all_dbs, remote_name, &db, &table, &column); if (retval) { free(retval); return; } if (column->type.key.type != OVSDB_TYPE_UUID || !column->type.key.u.uuid.refTable || column->type.value.type != OVSDB_TYPE_VOID) { return; } ref_table = column->type.key.u.uuid.refTable; HMAP_FOR_EACH (row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; size_t i; datum = &row->fields[column->index]; for (i = 0; i < datum->n; i++) { const struct ovsdb_row *ref_row; ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid); if (ref_row) { update_remote_row(ref_row, db->txn, jsonrpc); } } } } static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, const struct sset *remotes, struct shash *all_dbs) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); const char *remote; struct db *db; struct shash_node *node; SHASH_FOR_EACH(node, all_dbs) { db = node->data; db->txn = ovsdb_txn_create(db->db); } /* Iterate over --remote arguments given on command line. */ SSET_FOR_EACH (remote, remotes) { update_remote_rows(all_dbs, remote, jsonrpc); } SHASH_FOR_EACH(node, all_dbs) { struct ovsdb_error *error; db = node->data; error = ovsdb_txn_commit(db->txn, false); if (error) { VLOG_ERR_RL(&rl, "Failed to update remote status: %s", ovsdb_error_to_string(error)); ovsdb_error_destroy(error); } } } /* Reconfigures ovsdb-server's remotes based on information in the database. */ static char * reconfigure_remotes(struct ovsdb_jsonrpc_server *jsonrpc, const struct shash *all_dbs, struct sset *remotes) { struct ds errors = DS_EMPTY_INITIALIZER; struct shash resolved_remotes; const char *name; /* Configure remotes. */ shash_init(&resolved_remotes); SSET_FOR_EACH (name, remotes) { if (!strncmp(name, "db:", 3)) { query_db_remotes(name, all_dbs, &resolved_remotes, &errors); } else { add_remote(&resolved_remotes, name); } } ovsdb_jsonrpc_server_set_remotes(jsonrpc, &resolved_remotes); shash_destroy_free_data(&resolved_remotes); return errors.string; } static char * reconfigure_ssl(const struct shash *all_dbs) { struct ds errors = DS_EMPTY_INITIALIZER; const char *resolved_private_key; const char *resolved_certificate; const char *resolved_ca_cert; resolved_private_key = query_db_string(all_dbs, private_key_file, &errors); resolved_certificate = query_db_string(all_dbs, certificate_file, &errors); resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors); stream_ssl_set_key_and_cert(resolved_private_key, resolved_certificate); stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert); return errors.string; } static void report_error_if_changed(char *error, char **last_errorp) { if (error) { if (!*last_errorp || strcmp(error, *last_errorp)) { VLOG_WARN("%s", error); free(*last_errorp); *last_errorp = error; return; } free(error); } else { free(*last_errorp); *last_errorp = NULL; } } static void ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void ovsdb_server_perf_counters_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *arg_ OVS_UNUSED) { char *s = perf_counters_to_string(); unixctl_command_reply(conn, s); free(s); } static void ovsdb_server_perf_counters_clear(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *arg_ OVS_UNUSED) { perf_counters_clear(); unixctl_command_reply(conn, NULL); } static void ovsdb_server_compact(struct unixctl_conn *conn, int argc, const char *argv[], void *dbs_) { struct shash *all_dbs = dbs_; struct ds reply; struct db *db; struct shash_node *node; int n = 0; ds_init(&reply); SHASH_FOR_EACH(node, all_dbs) { const char *name; db = node->data; name = db->db->schema->name; if (argc < 2 || !strcmp(argv[1], name)) { struct ovsdb_error *error; VLOG_INFO("compacting %s database by user request", name); error = ovsdb_file_compact(db->file); if (error) { char *s = ovsdb_error_to_string(error); ds_put_format(&reply, "%s\n", s); free(s); ovsdb_error_destroy(error); } n++; } } if (!n) { unixctl_command_reply_error(conn, "no database by that name"); } else if (reply.length) { unixctl_command_reply_error(conn, ds_cstr(&reply)); } else { unixctl_command_reply(conn, NULL); } ds_destroy(&reply); } /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC * connections and reconnect. */ static void ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *jsonrpc_) { struct ovsdb_jsonrpc_server *jsonrpc = jsonrpc_; ovsdb_jsonrpc_server_reconnect(jsonrpc); unixctl_command_reply(conn, NULL); } /* "ovsdb-server/add-remote REMOTE": adds REMOTE to the set of remotes that * ovsdb-server services. */ static void ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *config_) { struct server_config *config = config_; const char *remote = argv[1]; const struct ovsdb_column *column; const struct ovsdb_table *table; const struct db *db; char *retval; retval = (strncmp("db:", remote, 3) ? NULL : parse_db_column(config->all_dbs, remote, &db, &table, &column)); if (!retval) { if (sset_add(config->remotes, remote)) { save_config(config); } unixctl_command_reply(conn, NULL); } else { unixctl_command_reply_error(conn, retval); free(retval); } } /* "ovsdb-server/remove-remote REMOTE": removes REMOTE frmo the set of remotes * that ovsdb-server services. */ static void ovsdb_server_remove_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *config_) { struct server_config *config = config_; struct sset_node *node; node = sset_find(config->remotes, argv[1]); if (node) { sset_delete(config->remotes, node); save_config(config); unixctl_command_reply(conn, NULL); } else { unixctl_command_reply_error(conn, "no such remote"); } } /* "ovsdb-server/list-remotes": outputs a list of configured rmeotes. */ static void ovsdb_server_list_remotes(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *remotes_) { struct sset *remotes = remotes_; const char **list, **p; struct ds s; ds_init(&s); list = sset_sort(remotes); for (p = list; *p; p++) { ds_put_format(&s, "%s\n", *p); } free(list); unixctl_command_reply(conn, ds_cstr(&s)); ds_destroy(&s); } /* "ovsdb-server/add-db DB": adds the DB to ovsdb-server. */ static void ovsdb_server_add_database(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *config_) { struct server_config *config = config_; const char *filename = argv[1]; char *error; error = open_db(config, filename); if (!error) { save_config(config); unixctl_command_reply(conn, NULL); } else { unixctl_command_reply_error(conn, error); free(error); } } static void ovsdb_server_remove_database(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *config_) { struct server_config *config = config_; struct shash_node *node; struct db *db; bool ok; node = shash_find(config->all_dbs, argv[1]); if (!node) { unixctl_command_reply_error(conn, "Failed to find the database."); return; } db = node->data; ok = ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db); ovs_assert(ok); close_db(db); shash_delete(config->all_dbs, node); save_config(config); unixctl_command_reply(conn, NULL); } static void ovsdb_server_list_databases(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *all_dbs_) { struct shash *all_dbs = all_dbs_; const struct shash_node **nodes; struct ds s; size_t i; ds_init(&s); nodes = shash_sort(all_dbs); for (i = 0; i < shash_count(all_dbs); i++) { struct db *db = nodes[i]->data; ds_put_format(&s, "%s\n", db->db->schema->name); } free(nodes); unixctl_command_reply(conn, ds_cstr(&s)); ds_destroy(&s); } static void parse_options(int *argcp, char **argvp[], struct sset *remotes, char **unixctl_pathp, char **run_command) { enum { OPT_REMOTE = UCHAR_MAX + 1, OPT_UNIXCTL, OPT_RUN, OPT_BOOTSTRAP_CA_CERT, OPT_PEER_CA_CERT, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; static const struct option long_options[] = { {"remote", required_argument, NULL, OPT_REMOTE}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, #ifndef _WIN32 {"run", required_argument, NULL, OPT_RUN}, #endif {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"private-key", required_argument, NULL, 'p'}, {"certificate", required_argument, NULL, 'c'}, {"ca-cert", required_argument, NULL, 'C'}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); int argc = *argcp; char **argv = *argvp; sset_init(remotes); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case OPT_REMOTE: sset_add(remotes, optarg); break; case OPT_UNIXCTL: *unixctl_pathp = optarg; break; case OPT_RUN: *run_command = optarg; break; case 'h': usage(); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS case 'p': private_key_file = optarg; break; case 'c': certificate_file = optarg; break; case 'C': ca_cert_file = optarg; bootstrap_ca_cert = false; break; case OPT_BOOTSTRAP_CA_CERT: ca_cert_file = optarg; bootstrap_ca_cert = true; break; case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); *argcp -= optind; *argvp += optind; } static void usage(void) { printf("%s: Open vSwitch database server\n" "usage: %s [OPTIONS] [DATABASE...]\n" "where each DATABASE is a database file in ovsdb format.\n" "The default DATABASE, if none is given, is\n%s/conf.db.\n", program_name, program_name, ovs_dbdir()); printf("\nJSON-RPC options (may be specified any number of times):\n" " --remote=REMOTE connect or listen to REMOTE\n"); stream_usage("JSON-RPC", true, true, true); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " --run COMMAND run COMMAND as subprocess then exit\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static struct json * sset_to_json(const struct sset *sset) { struct json *array; const char *s; array = json_array_create_empty(); SSET_FOR_EACH (s, sset) { json_array_add(array, json_string_create(s)); } return array; } /* Truncates and replaces the contents of 'config_file' by a representation of * 'remotes' and 'db_filenames'. */ static void save_config__(FILE *config_file, const struct sset *remotes, const struct sset *db_filenames) { struct json *obj; char *s; if (ftruncate(fileno(config_file), 0) == -1) { VLOG_FATAL("failed to truncate temporary file (%s)", ovs_strerror(errno)); } obj = json_object_create(); json_object_put(obj, "remotes", sset_to_json(remotes)); json_object_put(obj, "db_filenames", sset_to_json(db_filenames)); s = json_to_string(obj, 0); json_destroy(obj); if (fseek(config_file, 0, SEEK_SET) != 0 || fputs(s, config_file) == EOF || fflush(config_file) == EOF) { VLOG_FATAL("failed to write temporary file (%s)", ovs_strerror(errno)); } free(s); } /* Truncates and replaces the contents of 'config_file' by a representation of * 'config'. */ static void save_config(struct server_config *config) { struct sset db_filenames; struct shash_node *node; sset_init(&db_filenames); SHASH_FOR_EACH (node, config->all_dbs) { struct db *db = node->data; sset_add(&db_filenames, db->filename); } save_config__(config->config_tmpfile, config->remotes, &db_filenames); sset_destroy(&db_filenames); } static void sset_from_json(struct sset *sset, const struct json *array) { size_t i; sset_clear(sset); ovs_assert(array->type == JSON_ARRAY); for (i = 0; i < array->u.array.n; i++) { const struct json *elem = array->u.array.elems[i]; sset_add(sset, json_string(elem)); } } /* Clears and replaces 'remotes' and 'dbnames' by a configuration read from * 'config_file', which must have been previously written by save_config(). */ static void load_config(FILE *config_file, struct sset *remotes, struct sset *db_filenames) { struct json *json; if (fseek(config_file, 0, SEEK_SET) != 0) { VLOG_FATAL("seek failed in temporary file (%s)", ovs_strerror(errno)); } json = json_from_stream(config_file); if (json->type == JSON_STRING) { VLOG_FATAL("reading json failed (%s)", json_string(json)); } ovs_assert(json->type == JSON_OBJECT); sset_from_json(remotes, shash_find_data(json_object(json), "remotes")); sset_from_json(db_filenames, shash_find_data(json_object(json), "db_filenames")); json_destroy(json); } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-tool.c0000644000000000000000000000013113534540071017214 xustar0030 mtime=1567801401.821684255 30 atime=1567801402.121686458 29 ctime=1567801425.13785605 openvswitch-2.5.9/ovsdb/ovsdb-tool.c0000644000175000017500000004371313534540071020713 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "column.h" #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "file.h" #include "lockfile.h" #include "log.h" #include "json.h" #include "ovsdb.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "socket-util.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" /* -m, --more: Verbosity level for "show-log" command output. */ static int show_log_verbosity; static const struct ovs_cmdl_command *get_all_commands(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static const char *default_db(void); static const char *default_schema(void); int main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); ctx.argc = argc - optind; ctx.argv = argv + optind; ovs_cmdl_run_command(&ctx, get_all_commands()); return 0; } static void parse_options(int argc, char *argv[]) { static const struct option long_options[] = { {"more", no_argument, NULL, 'm'}, {"verbose", optional_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'm': show_log_verbosity++; break; case 'h': usage(); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); case 'v': vlog_set_verbosity(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void) { printf("%s: Open vSwitch database management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" " create [DB [SCHEMA]] create DB with the given SCHEMA\n" " compact [DB [DST]] compact DB in-place (or to DST)\n" " convert [DB [SCHEMA [DST]]] convert DB to SCHEMA (to DST)\n" " db-version [DB] report version of schema used by DB\n" " db-cksum [DB] report checksum of schema used by DB\n" " schema-version [SCHEMA] report SCHEMA's schema version\n" " schema-cksum [SCHEMA] report SCHEMA's checksum\n" " query [DB] TRNS execute read-only transaction on DB\n" " transact [DB] TRNS execute read/write transaction on DB\n" " [-m]... show-log [DB] print DB's log entries\n" "The default DB is %s.\n" "The default SCHEMA is %s.\n", program_name, program_name, default_db(), default_schema()); vlog_usage(); printf("\nOther options:\n" " -m, --more increase show-log verbosity\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static const char * default_db(void) { static char *db; if (!db) { db = xasprintf("%s/conf.db", ovs_dbdir()); } return db; } static const char * default_schema(void) { static char *schema; if (!schema) { schema = xasprintf("%s/vswitch.ovsschema", ovs_pkgdatadir()); } return schema; } static struct json * parse_json(const char *s) { struct json *json = json_from_string(s); if (json->type == JSON_STRING) { ovs_fatal(0, "\"%s\": %s", s, json->u.string); } return json; } static void print_and_free_json(struct json *json) { char *string = json_to_string(json, JSSF_SORT); json_destroy(json); puts(string); free(string); } static void check_ovsdb_error(struct ovsdb_error *error) { if (error) { ovs_fatal(0, "%s", ovsdb_error_to_string(error)); } } static void do_create(struct ovs_cmdl_context *ctx) { const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db(); const char *schema_file_name = ctx->argc >= 3 ? ctx->argv[2] : default_schema(); struct ovsdb_schema *schema; struct ovsdb_log *log; struct json *json; /* Read schema from file and convert to JSON. */ check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); json = ovsdb_schema_to_json(schema); ovsdb_schema_destroy(schema); /* Create database file. */ check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_CREATE, -1, &log)); check_ovsdb_error(ovsdb_log_write(log, json)); check_ovsdb_error(ovsdb_log_commit(log)); ovsdb_log_close(log); json_destroy(json); } static void compact_or_convert(const char *src_name_, const char *dst_name_, const struct ovsdb_schema *new_schema, const char *comment) { char *src_name, *dst_name; struct lockfile *src_lock; struct lockfile *dst_lock; bool in_place = dst_name_ == NULL; struct ovsdb *db; int retval; /* Dereference symlinks for source and destination names. In the in-place * case this ensures that, if the source name is a symlink, we replace its * target instead of replacing the symlink by a regular file. In the * non-in-place, this has the same effect for the destination name. */ src_name = follow_symlinks(src_name_); dst_name = (in_place ? xasprintf("%s.tmp", src_name) : follow_symlinks(dst_name_)); /* Lock the source, if we will be replacing it. */ if (in_place) { retval = lockfile_lock(src_name, &src_lock); if (retval) { ovs_fatal(retval, "%s: failed to lock lockfile", src_name); } } /* Get (temporary) destination and lock it. */ retval = lockfile_lock(dst_name, &dst_lock); if (retval) { ovs_fatal(retval, "%s: failed to lock lockfile", dst_name); } /* Save a copy. */ check_ovsdb_error(new_schema ? ovsdb_file_open_as_schema(src_name, new_schema, &db) : ovsdb_file_open(src_name, true, &db, NULL)); check_ovsdb_error(ovsdb_file_save_copy(dst_name, false, comment, db)); ovsdb_destroy(db); /* Replace source. */ if (in_place) { #ifdef _WIN32 unlink(src_name); #endif if (rename(dst_name, src_name)) { ovs_fatal(errno, "failed to rename \"%s\" to \"%s\"", dst_name, src_name); } fsync_parent_dir(dst_name); lockfile_unlock(src_lock); } lockfile_unlock(dst_lock); free(src_name); free(dst_name); } static void do_compact(struct ovs_cmdl_context *ctx) { const char *db = ctx->argc >= 2 ? ctx->argv[1] : default_db(); const char *target = ctx->argc >= 3 ? ctx->argv[2] : NULL; compact_or_convert(db, target, NULL, "compacted by ovsdb-tool "VERSION); } static void do_convert(struct ovs_cmdl_context *ctx) { const char *db = ctx->argc >= 2 ? ctx->argv[1] : default_db(); const char *schema = ctx->argc >= 3 ? ctx->argv[2] : default_schema(); const char *target = ctx->argc >= 4 ? ctx->argv[3] : NULL; struct ovsdb_schema *new_schema; check_ovsdb_error(ovsdb_schema_from_file(schema, &new_schema)); compact_or_convert(db, target, new_schema, "converted by ovsdb-tool "VERSION); ovsdb_schema_destroy(new_schema); } static void do_needs_conversion(struct ovs_cmdl_context *ctx) { const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db(); const char *schema_file_name = ctx->argc >= 3 ? ctx->argv[2] : default_schema(); struct ovsdb_schema *schema1, *schema2; check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema1)); check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema2)); puts(ovsdb_schema_equal(schema1, schema2) ? "no" : "yes"); ovsdb_schema_destroy(schema1); ovsdb_schema_destroy(schema2); } static void do_db_version(struct ovs_cmdl_context *ctx) { const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db(); struct ovsdb_schema *schema; check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema)); puts(schema->version); ovsdb_schema_destroy(schema); } static void do_db_cksum(struct ovs_cmdl_context *ctx) { const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db(); struct ovsdb_schema *schema; check_ovsdb_error(ovsdb_file_read_schema(db_file_name, &schema)); puts(schema->cksum); ovsdb_schema_destroy(schema); } static void do_schema_version(struct ovs_cmdl_context *ctx) { const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema(); struct ovsdb_schema *schema; check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); puts(schema->version); ovsdb_schema_destroy(schema); } static void do_schema_cksum(struct ovs_cmdl_context *ctx) { const char *schema_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_schema(); struct ovsdb_schema *schema; check_ovsdb_error(ovsdb_schema_from_file(schema_file_name, &schema)); puts(schema->cksum); ovsdb_schema_destroy(schema); } static void transact(bool read_only, int argc, char *argv[]) { const char *db_file_name = argc >= 3 ? argv[1] : default_db(); const char *transaction = argv[argc - 1]; struct json *request, *result; struct ovsdb *db; check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db, NULL)); request = parse_json(transaction); result = ovsdb_execute(db, NULL, request, 0, NULL); json_destroy(request); print_and_free_json(result); ovsdb_destroy(db); } static void do_query(struct ovs_cmdl_context *ctx) { transact(true, ctx->argc, ctx->argv); } static void do_transact(struct ovs_cmdl_context *ctx) { transact(false, ctx->argc, ctx->argv); } static void print_db_changes(struct shash *tables, struct shash *names, const struct ovsdb_schema *schema) { struct shash_node *n1; SHASH_FOR_EACH (n1, tables) { const char *table = n1->name; struct ovsdb_table_schema *table_schema; struct json *rows = n1->data; struct shash_node *n2; if (n1->name[0] == '_' || rows->type != JSON_OBJECT) { continue; } table_schema = shash_find_data(&schema->tables, table); SHASH_FOR_EACH (n2, json_object(rows)) { const char *row_uuid = n2->name; struct json *columns = n2->data; struct shash_node *n3; char *old_name, *new_name; bool free_new_name = false; old_name = new_name = shash_find_data(names, row_uuid); if (columns->type == JSON_OBJECT) { struct json *new_name_json; new_name_json = shash_find_data(json_object(columns), "name"); if (new_name_json) { new_name = json_to_string(new_name_json, JSSF_SORT); free_new_name = true; } } printf("\ttable %s", table); if (!old_name) { if (new_name) { printf(" insert row %s (%.8s):\n", new_name, row_uuid); } else { printf(" insert row %.8s:\n", row_uuid); } } else { printf(" row %s (%.8s):\n", old_name, row_uuid); } if (columns->type == JSON_OBJECT) { if (show_log_verbosity > 1) { SHASH_FOR_EACH (n3, json_object(columns)) { const char *column = n3->name; const struct ovsdb_column *column_schema; struct json *value = n3->data; char *value_string = NULL; column_schema = (table_schema ? shash_find_data(&table_schema->columns, column) : NULL); if (column_schema) { const struct ovsdb_type *type; struct ovsdb_error *error; struct ovsdb_datum datum; type = &column_schema->type; error = ovsdb_datum_from_json(&datum, type, value, NULL); if (!error) { struct ds s; ds_init(&s); ovsdb_datum_to_string(&datum, type, &s); value_string = ds_steal_cstr(&s); } else { ovsdb_error_destroy(error); } } if (!value_string) { value_string = json_to_string(value, JSSF_SORT); } printf("\t\t%s=%s\n", column, value_string); free(value_string); } } if (!old_name || (new_name != old_name && strcmp(old_name, new_name))) { if (old_name) { shash_delete(names, shash_find(names, row_uuid)); free(old_name); } shash_add(names, row_uuid, (new_name ? xstrdup(new_name) : xmemdup0(row_uuid, 8))); } } else if (columns->type == JSON_NULL) { struct shash_node *node; printf("\t\tdelete row\n"); node = shash_find(names, row_uuid); if (node) { shash_delete(names, node); } free(old_name); } if (free_new_name) { free(new_name); } } } } static void do_show_log(struct ovs_cmdl_context *ctx) { const char *db_file_name = ctx->argc >= 2 ? ctx->argv[1] : default_db(); struct shash names; struct ovsdb_log *log; struct ovsdb_schema *schema; unsigned int i; check_ovsdb_error(ovsdb_log_open(db_file_name, OVSDB_LOG_READ_ONLY, -1, &log)); shash_init(&names); schema = NULL; for (i = 0; ; i++) { struct json *json; check_ovsdb_error(ovsdb_log_read(log, &json)); if (!json) { break; } printf("record %u:", i); if (i == 0) { check_ovsdb_error(ovsdb_schema_from_json(json, &schema)); printf(" \"%s\" schema, version=\"%s\", cksum=\"%s\"\n", schema->name, schema->version, schema->cksum); } else if (json->type == JSON_OBJECT) { struct json *date, *comment; date = shash_find_data(json_object(json), "_date"); if (date && date->type == JSON_INTEGER) { long long int t = json_integer(date); char *s; if (t < INT32_MAX) { /* Older versions of ovsdb wrote timestamps in seconds. */ t *= 1000; } s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true); fputs(s, stdout); free(s); } comment = shash_find_data(json_object(json), "_comment"); if (comment && comment->type == JSON_STRING) { printf(" \"%s\"", json_string(comment)); } if (i > 0 && show_log_verbosity > 0) { putchar('\n'); print_db_changes(json_object(json), &names, schema); } } json_destroy(json); putchar('\n'); } ovsdb_log_close(log); ovsdb_schema_destroy(schema); /* XXX free 'names'. */ } static void do_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } static void do_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovs_cmdl_print_commands(get_all_commands()); } static const struct ovs_cmdl_command all_commands[] = { { "create", "[db [schema]]", 0, 2, do_create }, { "compact", "[db [dst]]", 0, 2, do_compact }, { "convert", "[db [schema [dst]]]", 0, 3, do_convert }, { "needs-conversion", NULL, 0, 2, do_needs_conversion }, { "db-version", "[db]", 0, 1, do_db_version }, { "db-cksum", "[db]", 0, 1, do_db_cksum }, { "schema-version", "[schema]", 0, 1, do_schema_version }, { "schema-cksum", "[schema]", 0, 1, do_schema_cksum }, { "query", "[db] trns", 1, 2, do_query }, { "transact", "[db] trns", 1, 2, do_transact }, { "show-log", "[db]", 0, 1, do_show_log }, { "help", NULL, 0, INT_MAX, do_help }, { "list-commands", NULL, 0, INT_MAX, do_list_commands }, { NULL, NULL, 0, 0, NULL }, }; static const struct ovs_cmdl_command *get_all_commands(void) { return all_commands; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-doc0000644000000000000000000000013213534540071016564 xustar0030 mtime=1567801401.813684195 30 atime=1567801402.121686458 30 ctime=1567801424.181849003 openvswitch-2.5.9/ovsdb/ovsdb-doc0000755000175000017500000002524513534540071020265 0ustar00jpettitjpettit00000000000000#! /usr/bin/python # Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from datetime import date import getopt import os import sys import xml.dom.minidom import ovs.json from ovs.db import error import ovs.db.schema from build.nroff import * argv0 = sys.argv[0] def typeAndConstraintsToNroff(column): type = column.type.toEnglish(escape_nroff_literal) constraints = column.type.constraintsToEnglish(escape_nroff_literal, text_to_nroff) if constraints: type += ", " + constraints if column.unique: type += " (must be unique within table)" return type def columnGroupToNroff(table, groupXml, documented_columns): introNodes = [] columnNodes = [] for node in groupXml.childNodes: if (node.nodeType == node.ELEMENT_NODE and node.tagName in ('column', 'group')): columnNodes += [node] else: if (columnNodes and not (node.nodeType == node.TEXT_NODE and node.data.isspace())): raise error.Error("text follows or inside : %s" % node) introNodes += [node] summary = [] intro = block_xml_to_nroff(introNodes) body = '' for node in columnNodes: if node.tagName == 'column': name = node.attributes['name'].nodeValue documented_columns.add(name) column = table.columns[name] if node.hasAttribute('key'): key = node.attributes['key'].nodeValue if node.hasAttribute('type'): type_string = node.attributes['type'].nodeValue type_json = ovs.json.from_string(str(type_string)) if type(type_json) in (str, unicode): raise error.Error("%s %s:%s has invalid 'type': %s" % (table.name, name, key, type_json)) type_ = ovs.db.types.BaseType.from_json(type_json) else: type_ = column.type.value nameNroff = "%s : %s" % (name, key) if column.type.value: typeNroff = "optional %s" % column.type.value.toEnglish( escape_nroff_literal) if (column.type.value.type == ovs.db.types.StringType and type_.type == ovs.db.types.BooleanType): # This is a little more explicit and helpful than # "containing a boolean" typeNroff += r", either \fBtrue\fR or \fBfalse\fR" else: if type_.type != column.type.value.type: type_english = type_.toEnglish() if type_english[0] in 'aeiou': typeNroff += ", containing an %s" % type_english else: typeNroff += ", containing a %s" % type_english constraints = ( type_.constraintsToEnglish(escape_nroff_literal, text_to_nroff)) if constraints: typeNroff += ", %s" % constraints else: typeNroff = "none" else: nameNroff = name typeNroff = typeAndConstraintsToNroff(column) if not column.mutable: typeNroff = "immutable %s" % typeNroff body += '.IP "\\fB%s\\fR: %s"\n' % (nameNroff, typeNroff) body += block_xml_to_nroff(node.childNodes, '.IP') + "\n" summary += [('column', nameNroff, typeNroff)] elif node.tagName == 'group': title = node.attributes["title"].nodeValue subSummary, subIntro, subBody = columnGroupToNroff( table, node, documented_columns) summary += [('group', title, subSummary)] body += '.ST "%s:"\n' % text_to_nroff(title) body += subIntro + subBody else: raise error.Error("unknown element %s in " % node.tagName) return summary, intro, body def tableSummaryToNroff(summary, level=0): s = "" for type, name, arg in summary: if type == 'column': s += ".TQ %.2fin\n\\fB%s\\fR\n%s\n" % (3 - level * .25, name, arg) else: s += ".TQ .25in\n\\fI%s:\\fR\n.RS .25in\n" % name s += tableSummaryToNroff(arg, level + 1) s += ".RE\n" return s def tableToNroff(schema, tableXml): tableName = tableXml.attributes['name'].nodeValue table = schema.tables[tableName] documented_columns = set() s = """.bp .SH "%s TABLE" """ % tableName summary, intro, body = columnGroupToNroff(table, tableXml, documented_columns) s += intro s += '.SS "Summary:\n' s += tableSummaryToNroff(summary) s += '.SS "Details:\n' s += body schema_columns = set(table.columns.keys()) undocumented_columns = schema_columns - documented_columns for column in undocumented_columns: raise error.Error("table %s has undocumented column %s" % (tableName, column)) return s def docsToNroff(schemaFile, xmlFile, erFile, version=None): schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile)) doc = xml.dom.minidom.parse(xmlFile).documentElement schemaDate = os.stat(schemaFile).st_mtime xmlDate = os.stat(xmlFile).st_mtime d = date.fromtimestamp(max(schemaDate, xmlDate)) if doc.hasAttribute('name'): manpage = doc.attributes['name'].nodeValue else: manpage = schema.name if version == None: version = "UNKNOWN" # Putting '\" p as the first line tells "man" that the manpage # needs to be preprocessed by "pic". s = r''''\" p .\" -*- nroff -*- .TH "%s" 5 " DB Schema %s" "Open vSwitch %s" "Open vSwitch Manual" .fp 5 L CR \\" Make fixed-width font available as \\fL. .de TQ . br . ns . TP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .SH NAME %s \- %s database schema .PP ''' % (manpage, schema.version, version, text_to_nroff(manpage), schema.name) tables = "" introNodes = [] tableNodes = [] summary = [] for dbNode in doc.childNodes: if (dbNode.nodeType == dbNode.ELEMENT_NODE and dbNode.tagName == "table"): tableNodes += [dbNode] name = dbNode.attributes['name'].nodeValue if dbNode.hasAttribute("title"): title = dbNode.attributes['title'].nodeValue else: title = name + " configuration." summary += [(name, title)] else: introNodes += [dbNode] documented_tables = set((name for (name, title) in summary)) schema_tables = set(schema.tables.keys()) undocumented_tables = schema_tables - documented_tables for table in undocumented_tables: raise error.Error("undocumented table %s" % table) s += block_xml_to_nroff(introNodes) + "\n" s += r""" .SH "TABLE SUMMARY" .PP The following list summarizes the purpose of each of the tables in the \fB%s\fR database. Each table is described in more detail on a later page. .IP "Table" 1in Purpose """ % schema.name for name, title in summary: s += r""" .TQ 1in \fB%s\fR %s """ % (name, text_to_nroff(title)) if erFile: s += """ .\\" check if in troff mode (TTY) .if t \{ .bp .SH "TABLE RELATIONSHIPS" .PP The following diagram shows the relationship among tables in the database. Each node represents a table. Tables that are part of the ``root set'' are shown with double borders. Each edge leads from the table that contains it and points to the table that its value represents. Edges are labeled with their column names, followed by a constraint on the number of allowed values: \\fB?\\fR for zero or one, \\fB*\\fR for zero or more, \\fB+\\fR for one or more. Thick lines represent strong references; thin lines represent weak references. .RS -1in """ erStream = open(erFile, "r") for line in erStream: s += line + '\n' erStream.close() s += ".RE\\}\n" for node in tableNodes: s += tableToNroff(schema, node) + "\n" return s def usage(): print """\ %(argv0)s: ovsdb schema documentation generator Prints documentation for an OVSDB schema as an nroff-formatted manpage. usage: %(argv0)s [OPTIONS] SCHEMA XML where SCHEMA is an OVSDB schema in JSON format and XML is OVSDB documentation in XML format. The following options are also available: --er-diagram=DIAGRAM.PIC include E-R diagram from DIAGRAM.PIC --version=VERSION use VERSION to display on document footer -h, --help display this help message\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['er-diagram=', 'version=', 'help']) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) er_diagram = None version = None for key, value in options: if key == '--er-diagram': er_diagram = value elif key == '--version': version = value elif key in ['-h', '--help']: usage() else: sys.exit(0) if len(args) != 2: sys.stderr.write("%s: exactly 2 non-option arguments required " "(use --help for help)\n" % argv0) sys.exit(1) # XXX we should warn about undocumented tables or columns s = docsToNroff(args[0], args[1], er_diagram, version) for line in s.split("\n"): line = line.strip() if len(line): print line except error.Error, e: sys.stderr.write("%s: %s\n" % (argv0, e.msg)) sys.exit(1) # Local variables: # mode: python # End: openvswitch-2.5.9/ovsdb/PaxHeaders.82075/dot2pic0000644000000000000000000000013213534540071016250 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.121686458 30 ctime=1567801424.181849003 openvswitch-2.5.9/ovsdb/dot2pic0000755000175000017500000000475213534540071017751 0ustar00jpettitjpettit00000000000000#! /usr/bin/perl # Copyright (c) 2009, 2010, 2011, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; use Getopt::Long; my $font_scale = 0; GetOptions("f=i" => \$font_scale) || exit 1; my ($scale) = 1; printf ".ps %+d\n", -$font_scale if $font_scale; print ".PS\n"; print "linethick = 1;\n"; while (<>) { if (/^graph/) { (undef, $scale) = split; } elsif (/^node/) { my (undef, $name, $x, $y, $width, $height, $label, $style, $shape, $color, $fillcolor) = split; $x *= $scale; $y *= $scale; $width *= $scale; $height *= $scale; print "linethick = ", ($style eq 'bold' ? 0.5 : 1.0), ";\n"; print "box at $x,$y wid $width height $height \"$name\"\n"; if ($style eq 'bold') { my $inset = 2.0 / 72.0; $width -= $inset * 2; $height -= $inset * 2; print "box at $x,$y wid $width height $height\n"; } } elsif (/edge/) { my (undef, $tail, $head, $n, $rest) = split(' ', $_, 5); my @xy; for (1...$n) { my ($x, $y); ($x, $y, $rest) = split(' ', $rest, 3); push(@xy, [$x * $scale, $y * $scale]); } my ($label, $xl, $yl); if (scalar(my @junk = split(' ', $rest)) > 2) { if ($rest =~ s/^"([^"]*)"\s+//) { $label = $1; } else { ($label, $rest) = split(' ', $rest, 2); } ($xl, $yl, $rest) = split(' ', $rest, 3); $xl *= $scale; $yl *= $scale; } my ($style, $color) = split(' ', $rest); print "linethick = ", ($style eq 'dotted' ? 0.5 : 1), ";\n"; print "spline -> from $xy[0][0],$xy[0][1]"; for (my ($i) = 0; $i <= $#xy; $i++) { print " to $xy[$i][0],$xy[$i][1]"; } print "\n"; print "\"$label\" at $xl,$yl\n" if defined($label); } } printf ".ps %+d\n", $font_scale if $font_scale; print ".PE\n"; openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-server.1.in0000644000000000000000000000013213534540071020071 xustar0030 mtime=1567801401.817684225 30 atime=1567801402.121686458 30 ctime=1567801423.781846054 openvswitch-2.5.9/ovsdb/ovsdb-server.1.in0000644000175000017500000002567413534540071021575 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovsdb\-server 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovsdb\-server . .SH NAME ovsdb\-server \- Open vSwitch database server . .SH SYNOPSIS \fBovsdb\-server\fR [\fIdatabase\fR]\&... [\fB\-\-remote=\fIremote\fR]\&... [\fB\-\-run=\fIcommand\fR] .so lib/daemon-syn.man .so lib/service-syn.man .so lib/vlog-syn.man .so lib/ssl-syn.man .so lib/ssl-bootstrap-syn.man .so lib/ssl-peer-ca-cert-syn.man .so lib/unixctl-syn.man .so lib/common-syn.man . .SH DESCRIPTION The \fBovsdb\-server\fR program provides RPC interfaces to one or more Open vSwitch databases (OVSDBs). It supports JSON-RPC client connections over active or passive TCP/IP or Unix domain sockets. .PP Each OVSDB file may be specified on the command line as \fIdatabase\fR. If none is specified, the default is \fB@DBDIR@/conf.db\fR. The database files must already have been created and initialized using, for example, \fBovsdb\-tool create\fR. . .SH OPTIONS . .IP "\fB\-\-remote=\fIremote\fR" Adds \fIremote\fR as a connection method used by \fBovsdb\-server\fR. \fIremote\fR must take one of the following forms: . .RS .so ovsdb/remote-passive.man .so ovsdb/remote-active.man . .IP "\fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR" Reads additional connection methods from \fIcolumn\fR in all of the rows in \fItable\fR within \fIdb\fR. As the contents of \fIcolumn\fR changes, \fBovsdb\-server\fR also adds and drops connection methods accordingly. .IP If \fIcolumn\fR's type is string or set of strings, then the connection methods are taken directly from the column. The connection methods in the column must have one of the forms described above. .IP If \fIcolumn\fR's type is UUID or set of UUIDs and references a table, then each UUID is looked up in the referenced table to obtain a row. The following columns in the row, if present and of the correct type, configure a connection method. Any additional columns are ignored. .RS .IP "\fBtarget\fR (string)" Connection method, in one of the forms described above. This column is mandatory: if it is missing or empty then no connection method can be configured. .IP "\fBmax_backoff\fR (integer)" Maximum number of milliseconds to wait between connection attempts. .IP "\fBinactivity_probe\fR (integer)" Maximum number of milliseconds of idle time on connection to client before sending an inactivity probe message. .RE .IP It is an error for \fIcolumn\fR to have another type. .RE . .IP To connect or listen on multiple connection methods, use multiple \fB\-\-remote\fR options. . .IP "\fB\-\-run=\fIcommand\fR]" Ordinarily \fBovsdb\-server\fR runs forever, or until it is told to exit (see \fBRUNTIME MANAGEMENT COMMANDS\fR below). With this option, \fBovsdb\-server\fR instead starts a shell subprocess running \fIcommand\fR. When the subprocess terminates, \fBovsdb\-server\fR also exits gracefully. If the subprocess exits normally with exit code 0, then \fBovsdb\-server\fR exits with exit code 0 also; otherwise, it exits with exit code 1. .IP This option can be useful where a database server is needed only to run a single command, e.g.: .B "ovsdb\-server \-\-remote=punix:socket \-\-run='ovsdb\-client dump unix:socket Open_vSwitch'" .IP This option is not supported on Windows platform. .SS "Daemon Options" .ds DD \ \fBovsdb\-server\fR detaches only after it starts listening on all \ configured remotes. .so lib/daemon.man .SS "Service Options" .so lib/service.man .SS "Logging Options" .so lib/vlog.man .SS "Public Key Infrastructure Options" The options described below for configuring the SSL public key infrastructure accept a special syntax for obtaining their configuration from the database. If any of these options is given \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR as its argument, then the actual file name is read from the specified \fIcolumn\fR in \fItable\fR within the \fIdb\fR database. The \fIcolumn\fR must have type string or set of strings. The first nonempty string in the table is taken as the file name. (This means that ordinarily there should be at most one row in \fItable\fR.) .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .SS "Other Options" .so lib/unixctl.man .so lib/common.man .SH "RUNTIME MANAGEMENT COMMANDS" \fBovs\-appctl\fR(8) can send commands to a running \fBovsdb\-server\fR process. The currently supported commands are described below. .SS "OVSDB\-SERVER COMMANDS" These commands are specific to \fBovsdb\-server\fR. .IP "\fBexit\fR" Causes \fBovsdb\-server\fR to gracefully terminate. .IP "\fBovsdb\-server/compact\fR [\fIdb\fR]\&..." Compacts each database \fIdb\fR in-place. If no \fIdb\fR is specified, compacts every database in-place. Databases are also automatically compacted occasionally. . .IP "\fBovsdb\-server/reconnect\fR" Makes \fBovsdb\-server\fR drop all of the JSON\-RPC connections to database clients and reconnect. .IP This command might be useful for debugging issues with database clients. . .IP "\fBovsdb\-server/add\-remote \fIremote\fR" Adds a remote, as if \fB\-\-remote=\fIremote\fR had been specified on the \fBovsdb\-server\fR command line. (If \fIremote\fR is already a remote, this command succeeds without changing the configuration.) . .IP "\fBovsdb\-server/remove\-remote \fIremote\fR" Removes the specified \fIremote\fR from the configuration, failing with an error if \fIremote\fR is not configured as a remote. This command only works with remotes that were named on \fB\-\-remote\fR or \fBovsdb\-server/add\-remote\fR, that is, it will not remove remotes added indirectly because they were read from the database by configuring a \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR remote. (You can remove a database source with \fBovsdb\-server/remove\-remote \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR, but not individual remotes found indirectly through the database.) . .IP "\fBovsdb\-server/list\-remotes" Outputs a list of the currently configured remotes named on \fB\-\-remote\fR or \fBovsdb\-server/add\-remote\fR, that is, it does not list remotes added indirectly because they were read from the database by configuring a \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR remote. . .IP "\fBovsdb\-server/add\-db \fIdatabase\fR" Adds the \fIdatabase\fR to the running \fBovsdb\-server\fR. The database file must already have been created and initialized using, for example, \fBovsdb\-tool create\fR. . .IP "\fBovsdb\-server/remove\-db \fIdatabase\fR" Removes \fIdatabase\fR from the running \fBovsdb\-server\fR. \fIdatabase\fR must be a database name as listed by \fBovsdb-server/list\-dbs\fR. .IP If a remote has been configured that points to the specified \fIdatabase\fR (e.g. \fB\-\-remote=db:\fIdatabase\fB,\fR... on the command line), then it will be disabled until another database with the same name is added again (with \fBovsdb\-server/add\-db\fR). .IP Any public key infrastructure options specified through this database (e.g. \fB\-\-private\-key=db:\fIdatabase,\fR... on the command line) will be disabled until another database with the same name is added again (with \fBovsdb\-server/add\-db\fR). . .IP "\fBovsdb\-server/list\-dbs" Outputs a list of the currently configured databases added either through the command line or through the \fBovsdb\-server/add\-db\fR command. . .so lib/vlog-unixctl.man .so lib/memory-unixctl.man .so lib/coverage-unixctl.man .SH "SPECIFICATIONS" . .PP \fBovsdb\-server\fR implements the Open vSwitch Database (OVSDB) protocol specified in RFC 7047, with the following clarifications: . .IP "3.1. JSON Usage" RFC 4627 says that names within a JSON object should be unique. The Open vSwitch JSON parser discards all but the last value for a name that is specified more than once. . .IP The definition of allows for implementation extensions. Currently \fBovsdb\-server\fR uses the following additional "error" strings which might change in later releases): . .RS .IP "\fBsyntax error\fR or \fBunknown column\fR" The request could not be parsed as an OVSDB request. An additional "syntax" member, whose value is a string that contains JSON, may narrow down the particular syntax that could not be parsed. .IP "\fBinternal error\fR" The request triggered a bug in \fBovsdb\-server\fR. .IP "\fBovsdb error\fR" A map or set contains a duplicate key. .RE . .IP "3.2. Schema Format" RFC 7047 requires the "version" field in . Current versions of \fBovsdb\-server\fR allow it to be omitted (future versions are likely to require it). . .IP "4. Wire Protocol" The original OVSDB specifications included the following reason, omitted from RFC 7047, to operate JSON-RPC directly over a stream instead of over HTTP: . .RS .IP \(bu JSON-RPC is a peer-to-peer protocol, but HTTP is a client-server protocol, which is a poor match. Thus, JSON-RPC over HTTP requires the client to periodically poll the server to receive server requests. .IP \(bu HTTP is more complicated than stream connections and doesn't provide any corresponding advantage. .IP \(bu The JSON-RPC specification for HTTP transport is incomplete. .RE . .IP "4.1.5. Monitor" For backward compatibility, \fBovsdb\-server\fR currently permits a single to be used instead of an array; it is treated as a single-element array. Future versions of \fBovsdb\-server\fR might remove this compatibility feature. .IP Because the parameter is used to match subsequent update notifications (see below) to the request, it must be unique among all active monitors. \fBovsdb\-server\fR rejects attempt to create two monitors with the same identifier. . .IP "5.1. Notation" For , RFC 7047 only allows the use of \fB!=\fR, \fB==\fR, \fBincludes\fR, and \fBexcludes\fR operators with set types. Open vSwitch 2.4 and later extend to allow the use of \fB<\fR, \fB<=\fR, \fB>=\fR, and \fB>\fR operators with columns with type ``set of 0 or 1 integer'' and ``set of 0 or 1 real''. These conditions evaluate to false when the column is empty, and otherwise as described in RFC 7047 for integer and real types. . .SH "BUGS" . In Open vSwitch before version 2.4, when \fBovsdb\-server\fR sent JSON-RPC error responses to some requests, it incorrectly formulated them with the \fBresult\fR and \fBerror\fR swapped, so that the response appeared to indicate success (with a nonsensical result) rather than an error. The requests that suffered from this problem were: . .IP \fBtransact\fR .IQ \fBget_schema\fR Only if the request names a nonexistent database. .IP \fBmonitor\fR .IQ \fBlock\fR .IQ \fBunlock\fR In all error cases. . .PP Of these cases, the only error that a well-written application is likely to encounter in practice is \fBmonitor\fR of tables or columns that do not exist, in an situation where the application has been upgraded but the old database schema is still temporarily in use. To handle this situation gracefully, we recommend that clients should treat a \fBmonitor\fR response with a \fBresult\fR that contains an \fBerror\fR key-value pair as an error (assuming that the database being monitored does not contain a table named \fBerror\fR). . .SH "SEE ALSO" . .BR ovsdb\-tool (1). openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-idlc.10000644000000000000000000000013213534540071017071 xustar0030 mtime=1567801401.813684195 30 atime=1567801402.121686458 30 ctime=1567801423.781846054 openvswitch-2.5.9/ovsdb/ovsdb-idlc.10000644000175000017500000000511313534540071020557 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .TH ovsdb\-idlc 1 "November 2009" "Open vSwitch" "Open vSwitch Manual" .ds PN ovsdb\-idlc . .SH NAME ovsdb\-idlc \- Open vSwitch IDL (Interface Definition Language) compiler . .SH SYNOPSIS \fBovsdb\-idlc \fBannotate\fI schema annotations\fR .br \fBovsdb\-idlc \fBc\-idl\-header\fI idl\fR .br \fBovsdb\-idlc \fBc\-idl\-source\fI idl\fR .br \fBovsdb\-idlc \-\-help\fR .br \fBovsdb\-idlc \-\-version\fR . .SH DESCRIPTION The \fBovsdb\-idlc\fR program is a command-line tool for translating Open vSwitch database interface definition language (IDL) schemas into other formats. It is used while building Open vSwitch, not at installation or configuration time. Thus, it is not normally installed as part of Open vSwitch. . .PP The \fIidl\fR files used as input for most \fBovsdb\-idlc\fR commands have the same format as the OVSDB schemas, specified in the OVSDB specification, with a few additions: . .IP "\fB""\fBidlPrefix\fR"" member of " This member, which is required, specifies a string that is prefixed to top-level names in C bindings. It should probably end in an underscore. . .IP "\fB""\fBidlHeader\fR"" member of " This member, which is required, specifies the name of the IDL header. It will be output on an \fB#include\fR line in the source file generated by the C bindings. It should include the bracketing \fB""\fR or \fB<>\fR. . .SS "Commands" .IP "\fBannotate\fI schema annotations\fR" Reads \fIschema\fR, which should be a file in JSON format (ordinarily an OVSDB schema file), then reads and executes the Python syntax fragment in \fIannotations\fR. The Python syntax fragment is passed the JSON object as a local variable named \fBs\fR. It may modify this data in any way. After the Python code returns, the object as modified is re-serialized as JSON on standard output. . .IP "\fBc\-idl\-header\fI idl\fR" Reads \fIidl\fR and prints on standard output a C header file that defines a structure for each table defined by the schema. . .IP "\fBc\-idl\-source\fI idl\fR" Reads \fIidl\fR and prints on standard output a C source file that implements C bindings for the database defined by the schema. . .IP "\fBdoc\fI idl\fR" Reads \fIidl\fR and prints on standard output a text file that documents the schema. The output may have very long lines, so it makes sense to pipe it through, e.g. \fBfmt \-s\fR. . .SS "Options" .so lib/common.man . .SH "BUGS" \fBovsdb\-idlc\fR is more lenient about the format of OVSDB schemas than other OVSDB tools. It may successfully parse schemas that, e.g., \fBovsdb\-tool\fR rejects. . .SH "SEE ALSO" The OVSDB specification. openvswitch-2.5.9/ovsdb/PaxHeaders.82075/trigger.c0000644000000000000000000000013213534540071016570 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.097855756 openvswitch-2.5.9/ovsdb/trigger.c0000644000175000017500000000654213534540071020265 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "trigger.h" #include #include "json.h" #include "jsonrpc.h" #include "ovsdb.h" #include "poll-loop.h" #include "server.h" static bool ovsdb_trigger_try(struct ovsdb_trigger *, long long int now); static void ovsdb_trigger_complete(struct ovsdb_trigger *); void ovsdb_trigger_init(struct ovsdb_session *session, struct ovsdb *db, struct ovsdb_trigger *trigger, struct json *request, long long int now) { trigger->session = session; trigger->db = db; list_push_back(&trigger->db->triggers, &trigger->node); trigger->request = request; trigger->result = NULL; trigger->created = now; trigger->timeout_msec = LLONG_MAX; ovsdb_trigger_try(trigger, now); } void ovsdb_trigger_destroy(struct ovsdb_trigger *trigger) { list_remove(&trigger->node); json_destroy(trigger->request); json_destroy(trigger->result); } bool ovsdb_trigger_is_complete(const struct ovsdb_trigger *trigger) { return trigger->result != NULL; } struct json * ovsdb_trigger_steal_result(struct ovsdb_trigger *trigger) { struct json *result = trigger->result; trigger->result = NULL; return result; } void ovsdb_trigger_run(struct ovsdb *db, long long int now) { struct ovsdb_trigger *t, *next; bool run_triggers; run_triggers = db->run_triggers; db->run_triggers = false; LIST_FOR_EACH_SAFE (t, next, node, &db->triggers) { if (run_triggers || now - t->created >= t->timeout_msec) { ovsdb_trigger_try(t, now); } } } void ovsdb_trigger_wait(struct ovsdb *db, long long int now) { if (db->run_triggers) { poll_immediate_wake(); } else { long long int deadline = LLONG_MAX; struct ovsdb_trigger *t; LIST_FOR_EACH (t, node, &db->triggers) { if (t->created < LLONG_MAX - t->timeout_msec) { long long int t_deadline = t->created + t->timeout_msec; if (deadline > t_deadline) { deadline = t_deadline; if (now >= deadline) { break; } } } } if (deadline < LLONG_MAX) { poll_timer_wait_until(deadline); } } } static bool ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now) { t->result = ovsdb_execute(t->db, t->session, t->request, now - t->created, &t->timeout_msec); if (t->result) { ovsdb_trigger_complete(t); return true; } else { return false; } } static void ovsdb_trigger_complete(struct ovsdb_trigger *t) { ovs_assert(t->result != NULL); list_remove(&t->node); list_push_back(&t->session->completions, &t->node); } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/libovsdb.sym.in0000644000000000000000000000013213534540071017724 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801424.649852453 openvswitch-2.5.9/ovsdb/libovsdb.sym.in0000644000175000017500000000005613534540071021413 0ustar00jpettitjpettit00000000000000libovsdb_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-tool.1.in0000644000000000000000000000013213534540071017540 xustar0030 mtime=1567801401.821684255 30 atime=1567801402.121686458 30 ctime=1567801423.777846025 openvswitch-2.5.9/ovsdb/ovsdb-tool.1.in0000644000175000017500000001377213534540071021240 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .\" -*- nroff -*- .TH ovsdb\-tool 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovsdb\-tool . .SH NAME ovsdb\-tool \- Open vSwitch database management utility . .SH SYNOPSIS \fBovsdb\-tool \fR[\fIoptions\fR] \fBcreate \fR[\fIdb\fR [\fIschema\fR]] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBcompact \fR[\fIdb\fR [\fItarget\fR]] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBconvert \fR[\fIdb\fR [\fIschema \fR[\fItarget\fR]]] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBneeds\-conversion \fR[\fIdb\fR [\fIschema\fR]] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBdb\-version \fR[\fIdb\fR] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBschema\-version \fR[\fIschema\fR] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBdb\-cksum \fR[\fIdb\fR] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBschema\-cksum \fR[\fIschema\fR] .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBquery \fR[\fIdb\fR] \fItransaction\fR .br \fBovsdb\-tool \fR[\fIoptions\fR] \fBtransact \fR[\fIdb\fR] \fItransaction\fR .br \fBovsdb\-tool \fR[\fIoptions\fR] [\fB\-m\fR | \fB\-\-more\fR]... \fBshow\-log \fR[\fIdb\fR] .br \fBovsdb\-tool help\fR .so lib/vlog-syn.man .so lib/common-syn.man . .SH DESCRIPTION The \fBovsdb\-tool\fR program is a command-line tool for managing Open vSwitch database (OVSDB) files. It does not interact directly with running Open vSwitch database servers (instead, use \fBovsdb\-client\fR). . .SS "Basic Commands" .IP "\fBcreate\fI db schema\fR" Reads an OVSDB schema from the file named \fIschema\fR and creates a new OVSDB database file named \fIdb\fR using that schema. The new database is initially empty. This command will not overwrite an existing \fIdb\fR. .IP \fIschema\fR must contain an OVSDB schema in JSON format. Refer to the OVSDB specification for details. . .IP "\fBcompact\fI db \fR[\fItarget\fR]" Reads \fIdb\fR and writes a compacted version. If \fItarget\fR is specified, the compacted version is written as a new file named \fItarget\fR, which must not already exist. If \fItarget\fR is omitted, then the compacted version of the database replaces \fIdb\fR in-place. . .IP "\fBconvert\fI db schema \fR[\fItarget\fR]" Reads \fIdb\fR, translating it into to the schema specified in \fIschema\fR, and writes out the new interpretation. If \fItarget\fR is specified, the translated version is written as a new file named \fItarget\fR, which must not already exist. If \fItarget\fR is omitted, then the translated version of the database replaces \fIdb\fR in-place. .IP This command can do simple ``upgrades'' and ``downgrades'' on a database's schema. The data in \fIdb\fR must be valid when interpreted under \fIschema\fR, with only one exception: data in \fIdb\fR for tables and columns that do not exist in \fIschema\fR are ignored. Columns that exist in \fIschema\fR but not in \fIdb\fR are set to their default values. All of \fIschema\fR's constraints apply in full. . .IP "\fBneeds\-conversion\fI db schema\fR" Reads the schema embedded in \fIdb\fR and the standalone schema in \fIschema\fR and compares them. If the schemas are the same, prints \fBno\fR on stdout; if they differ, print \fByes\fR. . .IP "\fBdb\-version\fI db\fR" .IQ "\fBschema\-version\fI schema\fR" Prints the version number in the schema embedded within the database \fIdb\fR or in the standalone schema \fIschema\fR on stdout. A schema version number has the form \fIx\fB.\fIy\fB.\fIz\fR. See \fBovs\-vswitchd.conf.db\fR(5) for details. .IP Schema version numbers and Open vSwitch version numbers are independent. .IP If \fIschema\fR or \fIdb\fR was created before schema versioning was introduced, then it will not have a version number and this command will print a blank line. . .IP "\fBdb\-cksum\fI db\fR" .IQ "\fBschema\-cksum\fI schema\fR" Prints the checksum in the schema embedded within the database \fIdb\fR or of the standalone schema \fIschema\fR on stdout. .IP If \fIschema\fR or \fIdb\fR was created before schema checksums were introduced, then it will not have a checksum and this command will print a blank line. . .IP "\fBquery\fI db transaction\fR" Opens \fIdb\fR, executes \fItransaction\fR on it, and prints the results. The \fItransaction\fR must be a JSON array in the format of the \fBparams\fR array for the JSON-RPC \fBtransact\fR method, as described in the OVSDB specification. .IP The \fIdb\fR is opened for read-only access, so this command may safely run concurrently with other database activity, including \fBovsdb\-server\fR and other database writers. The \fItransaction\fR may specify database modifications, but these will have no effect on \fIdb\fR. . .IP "\fBtransact\fI db transaction\fR" Opens \fIdb\fR, executes \fItransaction\fR on it, prints the results, and commits any changes to \fIdb\fR. The \fItransaction\fR must be a JSON array in the format of the \fBparams\fR array for the JSON-RPC \fBtransact\fR method, as described in the OVSDB specification. .IP The \fIdb\fR is opened and locked for read/write access, so this command will fail if the database is opened for writing by any other process, including \fBovsdb\-server\fR(1). Use \fBovsdb\-client\fR(1), instead, to write to a database that is served by \fBovsdb\-server\fR(1). . .IP "\fBshow\-log\fI db\fR" Prints a summary of the records in \fIdb\fR's log, including the time and date at which each database change occurred and any associated comment. This may be useful for debugging. .IP To increase the verbosity of output, add \fB\-m\fR (or \fB\-\-more\fR) one or more times to the command line. With one \fB\-m\fR, \fBshow\-log\fR prints a summary of the records added, deleted, or modified by each transaction. With two \fB\-m\fRs, \fBshow\-log\fR also prints the values of the columns modified by each change to a record. . .SH OPTIONS .SS "Logging Options" .so lib/vlog.man .SS "Other Options" .so lib/common.man .SH "FILES" The default \fIdb\fR is \fB@DBDIR@/conf.db\fR. The default \fIschema\fR is \fB@pkgdatadir@/vswitch.ovsschema\fR. The \fBhelp\fR command also displays these defaults. .SH "SEE ALSO" . \fBovsdb\-server\fR(1), \fBovsdb\-client\fR(1), and the OVSDB specification. openvswitch-2.5.9/ovsdb/PaxHeaders.82075/trigger.h0000644000000000000000000000013213534540071016575 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.101855786 openvswitch-2.5.9/ovsdb/trigger.h0000644000175000017500000000331013534540071020260 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_TRIGGER_H #define OVSDB_TRIGGER_H 1 #include "list.h" struct ovsdb; struct ovsdb_trigger { struct ovsdb_session *session; /* Session that owns this trigger. */ struct ovsdb *db; /* Database on which trigger acts. */ struct ovs_list node; /* !result: in db->triggers; * result: in session->completions. */ struct json *request; /* Database request. */ struct json *result; /* Result (null if none yet). */ long long int created; /* Time created. */ long long int timeout_msec; /* Max wait duration. */ }; void ovsdb_trigger_init(struct ovsdb_session *, struct ovsdb *, struct ovsdb_trigger *, struct json *request, long long int now); void ovsdb_trigger_destroy(struct ovsdb_trigger *); bool ovsdb_trigger_is_complete(const struct ovsdb_trigger *); struct json *ovsdb_trigger_steal_result(struct ovsdb_trigger *); void ovsdb_trigger_run(struct ovsdb *, long long int now); void ovsdb_trigger_wait(struct ovsdb *, long long int now); #endif /* ovsdb/trigger.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/row.c0000644000000000000000000000013213534540071015734 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.093855726 openvswitch-2.5.9/ovsdb/row.c0000644000175000017500000003003613534540071017424 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "row.h" #include #include "dynamic-string.h" #include "json.h" #include "ovsdb-error.h" #include "shash.h" #include "sort.h" #include "table.h" static struct ovsdb_row * allocate_row(const struct ovsdb_table *table) { size_t n_fields = shash_count(&table->schema->columns); size_t n_indexes = table->schema->n_indexes; size_t row_size = (offsetof(struct ovsdb_row, fields) + sizeof(struct ovsdb_datum) * n_fields + sizeof(struct hmap_node) * n_indexes); struct ovsdb_row *row = xmalloc(row_size); row->table = CONST_CAST(struct ovsdb_table *, table); row->txn_row = NULL; list_init(&row->src_refs); list_init(&row->dst_refs); row->n_refs = 0; return row; } struct ovsdb_row * ovsdb_row_create(const struct ovsdb_table *table) { struct shash_node *node; struct ovsdb_row *row; row = allocate_row(table); SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; ovsdb_datum_init_default(&row->fields[column->index], &column->type); } return row; } struct ovsdb_row * ovsdb_row_clone(const struct ovsdb_row *old) { const struct ovsdb_table *table = old->table; const struct shash_node *node; struct ovsdb_row *new; new = allocate_row(table); SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; ovsdb_datum_clone(&new->fields[column->index], &old->fields[column->index], &column->type); } return new; } /* The caller is responsible for ensuring that 'row' has been removed from its * table and that it is not participating in a transaction. */ void ovsdb_row_destroy(struct ovsdb_row *row) { if (row) { const struct ovsdb_table *table = row->table; struct ovsdb_weak_ref *weak, *next; const struct shash_node *node; LIST_FOR_EACH_SAFE (weak, next, dst_node, &row->dst_refs) { list_remove(&weak->src_node); list_remove(&weak->dst_node); free(weak); } LIST_FOR_EACH_SAFE (weak, next, src_node, &row->src_refs) { list_remove(&weak->src_node); list_remove(&weak->dst_node); free(weak); } SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; ovsdb_datum_destroy(&row->fields[column->index], &column->type); } free(row); } } uint32_t ovsdb_row_hash_columns(const struct ovsdb_row *row, const struct ovsdb_column_set *columns, uint32_t basis) { size_t i; for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; basis = ovsdb_datum_hash(&row->fields[column->index], &column->type, basis); } return basis; } int ovsdb_row_compare_columns_3way(const struct ovsdb_row *a, const struct ovsdb_row *b, const struct ovsdb_column_set *columns) { size_t i; for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; int cmp = ovsdb_datum_compare_3way(&a->fields[column->index], &b->fields[column->index], &column->type); if (cmp) { return cmp; } } return 0; } bool ovsdb_row_equal_columns(const struct ovsdb_row *a, const struct ovsdb_row *b, const struct ovsdb_column_set *columns) { size_t i; for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; if (!ovsdb_datum_equals(&a->fields[column->index], &b->fields[column->index], &column->type)) { return false; } } return true; } void ovsdb_row_update_columns(struct ovsdb_row *dst, const struct ovsdb_row *src, const struct ovsdb_column_set *columns) { size_t i; for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; ovsdb_datum_destroy(&dst->fields[column->index], &column->type); ovsdb_datum_clone(&dst->fields[column->index], &src->fields[column->index], &column->type); } } /* Appends the string form of the value in 'row' of each of the columns in * 'columns' to 'out', e.g. "1, \"xyz\", and [1, 2, 3]". */ void ovsdb_row_columns_to_string(const struct ovsdb_row *row, const struct ovsdb_column_set *columns, struct ds *out) { size_t i; for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; ds_put_cstr(out, english_list_delimiter(i, columns->n_columns)); ovsdb_datum_to_string(&row->fields[column->index], &column->type, out); } } struct ovsdb_error * ovsdb_row_from_json(struct ovsdb_row *row, const struct json *json, struct ovsdb_symbol_table *symtab, struct ovsdb_column_set *included) { struct ovsdb_table_schema *schema = row->table->schema; struct ovsdb_error *error; struct shash_node *node; if (json->type != JSON_OBJECT) { return ovsdb_syntax_error(json, NULL, "row must be JSON object"); } SHASH_FOR_EACH (node, json_object(json)) { const char *column_name = node->name; const struct ovsdb_column *column; struct ovsdb_datum datum; column = ovsdb_table_schema_get_column(schema, column_name); if (!column) { return ovsdb_syntax_error(json, "unknown column", "No column %s in table %s.", column_name, schema->name); } error = ovsdb_datum_from_json(&datum, &column->type, node->data, symtab); if (error) { return error; } ovsdb_datum_swap(&row->fields[column->index], &datum); ovsdb_datum_destroy(&datum, &column->type); if (included) { ovsdb_column_set_add(included, column); } } return NULL; } static void put_json_column(struct json *object, const struct ovsdb_row *row, const struct ovsdb_column *column) { json_object_put(object, column->name, ovsdb_datum_to_json(&row->fields[column->index], &column->type)); } struct json * ovsdb_row_to_json(const struct ovsdb_row *row, const struct ovsdb_column_set *columns) { struct json *json; size_t i; json = json_object_create(); for (i = 0; i < columns->n_columns; i++) { put_json_column(json, row, columns->columns[i]); } return json; } void ovsdb_row_set_init(struct ovsdb_row_set *set) { set->rows = NULL; set->n_rows = set->allocated_rows = 0; } void ovsdb_row_set_destroy(struct ovsdb_row_set *set) { free(set->rows); } void ovsdb_row_set_add_row(struct ovsdb_row_set *set, const struct ovsdb_row *row) { if (set->n_rows >= set->allocated_rows) { set->rows = x2nrealloc(set->rows, &set->allocated_rows, sizeof *set->rows); } set->rows[set->n_rows++] = row; } struct json * ovsdb_row_set_to_json(const struct ovsdb_row_set *rows, const struct ovsdb_column_set *columns) { struct json **json_rows; size_t i; json_rows = xmalloc(rows->n_rows * sizeof *json_rows); for (i = 0; i < rows->n_rows; i++) { json_rows[i] = ovsdb_row_to_json(rows->rows[i], columns); } return json_array_create(json_rows, rows->n_rows); } struct ovsdb_row_set_sort_cbdata { struct ovsdb_row_set *set; const struct ovsdb_column_set *columns; }; static int ovsdb_row_set_sort_compare_cb(size_t a, size_t b, void *cbdata_) { struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_; return ovsdb_row_compare_columns_3way(cbdata->set->rows[a], cbdata->set->rows[b], cbdata->columns); } static void ovsdb_row_set_sort_swap_cb(size_t a, size_t b, void *cbdata_) { struct ovsdb_row_set_sort_cbdata *cbdata = cbdata_; const struct ovsdb_row *tmp = cbdata->set->rows[a]; cbdata->set->rows[a] = cbdata->set->rows[b]; cbdata->set->rows[b] = tmp; } void ovsdb_row_set_sort(struct ovsdb_row_set *set, const struct ovsdb_column_set *columns) { if (columns && columns->n_columns && set->n_rows > 1) { struct ovsdb_row_set_sort_cbdata cbdata; cbdata.set = set; cbdata.columns = columns; sort(set->n_rows, ovsdb_row_set_sort_compare_cb, ovsdb_row_set_sort_swap_cb, &cbdata); } } void ovsdb_row_hash_init(struct ovsdb_row_hash *rh, const struct ovsdb_column_set *columns) { hmap_init(&rh->rows); ovsdb_column_set_clone(&rh->columns, columns); } void ovsdb_row_hash_destroy(struct ovsdb_row_hash *rh, bool destroy_rows) { struct ovsdb_row_hash_node *node, *next; HMAP_FOR_EACH_SAFE (node, next, hmap_node, &rh->rows) { hmap_remove(&rh->rows, &node->hmap_node); if (destroy_rows) { ovsdb_row_destroy(CONST_CAST(struct ovsdb_row *, node->row)); } free(node); } hmap_destroy(&rh->rows); ovsdb_column_set_destroy(&rh->columns); } size_t ovsdb_row_hash_count(const struct ovsdb_row_hash *rh) { return hmap_count(&rh->rows); } bool ovsdb_row_hash_contains(const struct ovsdb_row_hash *rh, const struct ovsdb_row *row) { size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0); return ovsdb_row_hash_contains__(rh, row, hash); } /* Returns true if every row in 'b' has an equal row in 'a'. */ bool ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *a, const struct ovsdb_row_hash *b) { struct ovsdb_row_hash_node *node; ovs_assert(ovsdb_column_set_equals(&a->columns, &b->columns)); HMAP_FOR_EACH (node, hmap_node, &b->rows) { if (!ovsdb_row_hash_contains__(a, node->row, node->hmap_node.hash)) { return false; } } return true; } bool ovsdb_row_hash_insert(struct ovsdb_row_hash *rh, const struct ovsdb_row *row) { size_t hash = ovsdb_row_hash_columns(row, &rh->columns, 0); return ovsdb_row_hash_insert__(rh, row, hash); } bool ovsdb_row_hash_contains__(const struct ovsdb_row_hash *rh, const struct ovsdb_row *row, size_t hash) { struct ovsdb_row_hash_node *node; HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash, &rh->rows) { if (ovsdb_row_equal_columns(row, node->row, &rh->columns)) { return true; } } return false; } bool ovsdb_row_hash_insert__(struct ovsdb_row_hash *rh, const struct ovsdb_row *row, size_t hash) { if (!ovsdb_row_hash_contains__(rh, row, hash)) { struct ovsdb_row_hash_node *node = xmalloc(sizeof *node); node->row = row; hmap_insert(&rh->rows, &node->hmap_node, hash); return true; } else { return false; } } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/query.c0000644000000000000000000000013213534540071016272 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.089855696 openvswitch-2.5.9/ovsdb/query.c0000644000175000017500000000611513534540071017763 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "query.h" #include "column.h" #include "condition.h" #include "row.h" #include "table.h" void ovsdb_query(struct ovsdb_table *table, const struct ovsdb_condition *cnd, bool (*output_row)(const struct ovsdb_row *, void *aux), void *aux) { if (cnd->n_clauses > 0 && cnd->clauses[0].column->index == OVSDB_COL_UUID && cnd->clauses[0].function == OVSDB_F_EQ) { /* Optimize the case where the query has a clause of the form "uuid == * ", since we have an index on UUID. */ const struct ovsdb_row *row; row = ovsdb_table_get_row(table, &cnd->clauses[0].arg.keys[0].uuid); if (row && row->table == table && ovsdb_condition_evaluate(row, cnd)) { output_row(row, aux); } } else { /* Linear scan. */ const struct ovsdb_row *row, *next; HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) { if (ovsdb_condition_evaluate(row, cnd) && !output_row(row, aux)) { break; } } } } static bool query_row_set_cb(const struct ovsdb_row *row, void *results_) { struct ovsdb_row_set *results = results_; ovsdb_row_set_add_row(results, row); return true; } void ovsdb_query_row_set(struct ovsdb_table *table, const struct ovsdb_condition *condition, struct ovsdb_row_set *results) { ovsdb_query(table, condition, query_row_set_cb, results); } static bool query_distinct_cb(const struct ovsdb_row *row, void *hash_) { struct ovsdb_row_hash *hash = hash_; ovsdb_row_hash_insert(hash, row); return true; } void ovsdb_query_distinct(struct ovsdb_table *table, const struct ovsdb_condition *condition, const struct ovsdb_column_set *columns, struct ovsdb_row_set *results) { if (!columns || ovsdb_column_set_contains(columns, OVSDB_COL_UUID)) { /* All the result rows are guaranteed to be distinct anyway. */ ovsdb_query_row_set(table, condition, results); return; } else { /* Use hash table to drop duplicates. */ struct ovsdb_row_hash_node *node; struct ovsdb_row_hash hash; ovsdb_row_hash_init(&hash, columns); ovsdb_query(table, condition, query_distinct_cb, &hash); HMAP_FOR_EACH (node, hmap_node, &hash.rows) { ovsdb_row_set_add_row(results, node->row); } ovsdb_row_hash_destroy(&hash, false); } } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/file.h0000644000000000000000000000013213534540071016051 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.121686458 30 ctime=1567801425.077855608 openvswitch-2.5.9/ovsdb/file.h0000644000175000017500000000320413534540071017536 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_FILE_H #define OVSDB_FILE_H 1 #include #include "compiler.h" #include "log.h" struct ovsdb; struct ovsdb_file; struct ovsdb_schema; struct ovsdb_error *ovsdb_file_open(const char *file_name, bool read_only, struct ovsdb **, struct ovsdb_file **) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_file_open_as_schema(const char *file_name, const struct ovsdb_schema *, struct ovsdb **) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_file_save_copy(const char *file_name, int locking, const char *comment, const struct ovsdb *) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_file_compact(struct ovsdb_file *); struct ovsdb_error *ovsdb_file_read_schema(const char *file_name, struct ovsdb_schema **) OVS_WARN_UNUSED_RESULT; #endif /* ovsdb/file.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/monitor.h0000644000000000000000000000013213534540071016621 xustar0030 mtime=1567801401.809684167 30 atime=1567801402.121686458 30 ctime=1567801425.089855696 openvswitch-2.5.9/ovsdb/monitor.h0000644000175000017500000000507013534540071020311 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_MONITOR_H #define OVSDB_MONITOR_H struct ovsdb_monitor; enum ovsdb_monitor_selection { OJMS_INITIAL = 1 << 0, /* All rows when monitor is created. */ OJMS_INSERT = 1 << 1, /* New rows. */ OJMS_DELETE = 1 << 2, /* Deleted rows. */ OJMS_MODIFY = 1 << 3 /* Modified rows. */ }; struct ovsdb_monitor *ovsdb_monitor_create(struct ovsdb *db, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor); struct ovsdb_monitor *ovsdb_monitor_add(struct ovsdb_monitor *dbmon); void ovsdb_monitor_add_jsonrpc_monitor(struct ovsdb_monitor *dbmon, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor); void ovsdb_monitor_remove_jsonrpc_monitor(struct ovsdb_monitor *dbmon, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor, uint64_t unflushed); void ovsdb_monitor_add_table(struct ovsdb_monitor *m, const struct ovsdb_table *table); void ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, const struct ovsdb_column *column, enum ovsdb_monitor_selection select, size_t *allocated_columns); const char * OVS_WARN_UNUSED_RESULT ovsdb_monitor_table_check_duplicates(struct ovsdb_monitor *, const struct ovsdb_table *); struct json *ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, bool initial, uint64_t *unflushed_transaction); void ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, enum ovsdb_monitor_selection select); bool ovsdb_monitor_needs_flush(struct ovsdb_monitor *dbmon, uint64_t next_transaction); void ovsdb_monitor_get_initial(const struct ovsdb_monitor *dbmon); #endif openvswitch-2.5.9/ovsdb/PaxHeaders.82075/column.c0000644000000000000000000000013213534540071016422 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.117686428 30 ctime=1567801425.073855578 openvswitch-2.5.9/ovsdb/column.c0000644000175000017500000001757413534540071020126 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb/column.h" #include #include "column.h" #include "dynamic-string.h" #include "json.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "table.h" #include "util.h" struct ovsdb_column * ovsdb_column_create(const char *name, bool mutable, bool persistent, const struct ovsdb_type *type) { /* Doesn't set the new column's 'index': the caller must do that. */ struct ovsdb_column *column; column = xzalloc(sizeof *column); column->name = xstrdup(name); column->mutable = mutable; column->persistent = persistent; ovsdb_type_clone(&column->type, type); return column; } struct ovsdb_column * ovsdb_column_clone(const struct ovsdb_column *old) { /* Doesn't copy the column's 'index': the caller must do that. */ return ovsdb_column_create(old->name, old->mutable, old->persistent, &old->type); } void ovsdb_column_destroy(struct ovsdb_column *column) { ovsdb_type_destroy(&column->type); free(column->name); free(column); } struct ovsdb_error * ovsdb_column_from_json(const struct json *json, const char *name, struct ovsdb_column **columnp) { const struct json *mutable, *ephemeral, *type_json; struct ovsdb_error *error; struct ovsdb_type type; struct ovsdb_parser parser; bool persistent; *columnp = NULL; ovsdb_parser_init(&parser, json, "schema for column %s", name); mutable = ovsdb_parser_member(&parser, "mutable", OP_TRUE | OP_FALSE | OP_OPTIONAL); ephemeral = ovsdb_parser_member(&parser, "ephemeral", OP_TRUE | OP_FALSE | OP_OPTIONAL); type_json = ovsdb_parser_member(&parser, "type", OP_STRING | OP_OBJECT); error = ovsdb_parser_finish(&parser); if (error) { return error; } error = ovsdb_type_from_json(&type, type_json); if (error) { return error; } persistent = ephemeral ? !json_boolean(ephemeral) : true; *columnp = ovsdb_column_create(name, mutable ? json_boolean(mutable) : true, persistent, &type); ovsdb_type_destroy(&type); return NULL; } struct json * ovsdb_column_to_json(const struct ovsdb_column *column) { struct json *json = json_object_create(); if (!column->mutable) { json_object_put(json, "mutable", json_boolean_create(false)); } if (!column->persistent) { json_object_put(json, "ephemeral", json_boolean_create(true)); } json_object_put(json, "type", ovsdb_type_to_json(&column->type)); return json; } void ovsdb_column_set_init(struct ovsdb_column_set *set) { set->columns = NULL; set->n_columns = set->allocated_columns = 0; } void ovsdb_column_set_destroy(struct ovsdb_column_set *set) { free(set->columns); } void ovsdb_column_set_clone(struct ovsdb_column_set *new, const struct ovsdb_column_set *old) { new->columns = xmemdup(old->columns, old->n_columns * sizeof *old->columns); new->n_columns = new->allocated_columns = old->n_columns; } struct ovsdb_error * ovsdb_column_set_from_json(const struct json *json, const struct ovsdb_table_schema *schema, struct ovsdb_column_set *set) { ovsdb_column_set_init(set); if (!json) { struct shash_node *node; SHASH_FOR_EACH (node, &schema->columns) { const struct ovsdb_column *column = node->data; ovsdb_column_set_add(set, column); } return NULL; } else { struct ovsdb_error *error = NULL; size_t i; if (json->type != JSON_ARRAY) { goto error; } /* XXX this is O(n**2) */ for (i = 0; i < json->u.array.n; i++) { const struct ovsdb_column *column; const char *s; if (json->u.array.elems[i]->type != JSON_STRING) { goto error; } s = json->u.array.elems[i]->u.string; column = shash_find_data(&schema->columns, s); if (!column) { error = ovsdb_syntax_error(json, NULL, "%s is not a valid " "column name", s); goto error; } else if (ovsdb_column_set_contains(set, column->index)) { goto error; } ovsdb_column_set_add(set, column); } return NULL; error: ovsdb_column_set_destroy(set); ovsdb_column_set_init(set); if (!error) { error = ovsdb_syntax_error(json, NULL, "array of distinct column " "names expected"); } return error; } } struct json * ovsdb_column_set_to_json(const struct ovsdb_column_set *set) { struct json *json; size_t i; json = json_array_create_empty(); for (i = 0; i < set->n_columns; i++) { json_array_add(json, json_string_create(set->columns[i]->name)); } return json; } /* Returns an English string listing the contents of 'set', e.g. "columns * \"a\", \"b\", and \"c\"". The caller must free the string. */ char * ovsdb_column_set_to_string(const struct ovsdb_column_set *set) { if (!set->n_columns) { return xstrdup("no columns"); } else { struct ds s; size_t i; ds_init(&s); ds_put_format(&s, "column%s ", set->n_columns > 1 ? "s" : ""); for (i = 0; i < set->n_columns; i++) { const char *delimiter = english_list_delimiter(i, set->n_columns); ds_put_format(&s, "%s\"%s\"", delimiter, set->columns[i]->name); } return ds_steal_cstr(&s); } } void ovsdb_column_set_add(struct ovsdb_column_set *set, const struct ovsdb_column *column) { if (set->n_columns >= set->allocated_columns) { set->columns = x2nrealloc(set->columns, &set->allocated_columns, sizeof *set->columns); } set->columns[set->n_columns++] = column; } void ovsdb_column_set_add_all(struct ovsdb_column_set *set, const struct ovsdb_table *table) { struct shash_node *node; SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; ovsdb_column_set_add(set, column); } } bool ovsdb_column_set_contains(const struct ovsdb_column_set *set, unsigned int column_index) { size_t i; for (i = 0; i < set->n_columns; i++) { if (set->columns[i]->index == column_index) { return true; } } return false; } /* This comparison is sensitive to ordering of columns within a set, but that's * good: the only existing caller wants to make sure that hash values are * comparable, which is only true if column ordering is the same. */ bool ovsdb_column_set_equals(const struct ovsdb_column_set *a, const struct ovsdb_column_set *b) { size_t i; if (a->n_columns != b->n_columns) { return false; } for (i = 0; i < a->n_columns; i++) { if (a->columns[i] != b->columns[i]) { return false; } } return true; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/transaction.c0000644000000000000000000000013213534540071017452 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.101855786 openvswitch-2.5.9/ovsdb/transaction.c0000644000175000017500000010365613534540071021153 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "transaction.h" #include "bitmap.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "json.h" #include "list.h" #include "ovsdb-error.h" #include "ovsdb.h" #include "row.h" #include "table.h" #include "perf-counter.h" #include "uuid.h" struct ovsdb_txn { struct ovsdb *db; struct ovs_list txn_tables; /* Contains "struct ovsdb_txn_table"s. */ struct ds comment; }; /* A table modified by a transaction. */ struct ovsdb_txn_table { struct ovs_list node; /* Element in ovsdb_txn's txn_tables list. */ struct ovsdb_table *table; struct hmap txn_rows; /* Contains "struct ovsdb_txn_row"s. */ /* This has the same form as the 'indexes' member of struct ovsdb_table, * but it is only used or updated at transaction commit time, from * check_index_uniqueness(). */ struct hmap *txn_indexes; /* Used by for_each_txn_row(). */ unsigned int serial; /* Serial number of in-progress iteration. */ unsigned int n_processed; /* Number of rows processed. */ }; /* A row modified by the transaction: * * - A row added by a transaction will have null 'old' and non-null 'new'. * * - A row deleted by a transaction will have non-null 'old' and null * 'new'. * * - A row modified by a transaction will have non-null 'old' and 'new'. * * - 'old' and 'new' both null indicates that a row was added then deleted * within a single transaction. Most of the time we instead delete the * ovsdb_txn_row entirely, but inside a for_each_txn_row() callback * there are restrictions that sometimes mean we have to leave the * ovsdb_txn_row in place. */ struct ovsdb_txn_row { struct hmap_node hmap_node; /* In ovsdb_txn_table's txn_rows hmap. */ struct ovsdb_row *old; /* The old row. */ struct ovsdb_row *new; /* The new row. */ size_t n_refs; /* Number of remaining references. */ /* These members are the same as the corresponding members of 'old' or * 'new'. They are present here for convenience and because occasionally * there can be an ovsdb_txn_row where both 'old' and 'new' are NULL. */ struct uuid uuid; struct ovsdb_table *table; /* Used by for_each_txn_row(). */ unsigned int serial; /* Serial number of in-progress commit. */ unsigned long changed[]; /* Bits set to 1 for columns that changed. */ }; static struct ovsdb_error * OVS_WARN_UNUSED_RESULT delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *r); static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *); static struct ovsdb_error * OVS_WARN_UNUSED_RESULT for_each_txn_row(struct ovsdb_txn *txn, struct ovsdb_error *(*)(struct ovsdb_txn *, struct ovsdb_txn_row *)); /* Used by for_each_txn_row() to track tables and rows that have been * processed. */ static unsigned int serial; struct ovsdb_txn * ovsdb_txn_create(struct ovsdb *db) { struct ovsdb_txn *txn = xmalloc(sizeof *txn); txn->db = db; list_init(&txn->txn_tables); ds_init(&txn->comment); return txn; } static void ovsdb_txn_free(struct ovsdb_txn *txn) { ovs_assert(list_is_empty(&txn->txn_tables)); ds_destroy(&txn->comment); free(txn); } static struct ovsdb_error * ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row) { struct ovsdb_row *old = txn_row->old; struct ovsdb_row *new = txn_row->new; ovsdb_txn_row_prefree(txn_row); if (!old) { if (new) { hmap_remove(&new->table->rows, &new->hmap_node); } } else if (!new) { hmap_insert(&old->table->rows, &old->hmap_node, ovsdb_row_hash(old)); } else { hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node); } ovsdb_row_destroy(new); free(txn_row); return NULL; } /* Returns the offset in bytes from the start of an ovsdb_row for 'table' to * the hmap_node for the index numbered 'i'. */ static size_t ovsdb_row_index_offset__(const struct ovsdb_table *table, size_t i) { size_t n_fields = shash_count(&table->schema->columns); return (offsetof(struct ovsdb_row, fields) + n_fields * sizeof(struct ovsdb_datum) + i * sizeof(struct hmap_node)); } /* Returns the hmap_node in 'row' for the index numbered 'i'. */ static struct hmap_node * ovsdb_row_get_index_node(struct ovsdb_row *row, size_t i) { return (void *) ((char *) row + ovsdb_row_index_offset__(row->table, i)); } /* Returns the ovsdb_row given 'index_node', which is a pointer to that row's * hmap_node for the index numbered 'i' within 'table'. */ static struct ovsdb_row * ovsdb_row_from_index_node(struct hmap_node *index_node, const struct ovsdb_table *table, size_t i) { return (void *) ((char *) index_node - ovsdb_row_index_offset__(table, i)); } void ovsdb_txn_abort(struct ovsdb_txn *txn) { ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_row_abort)); ovsdb_txn_free(txn); } static struct ovsdb_txn_row * find_txn_row(const struct ovsdb_table *table, const struct uuid *uuid) { struct ovsdb_txn_row *txn_row; if (!table->txn_table) { return NULL; } HMAP_FOR_EACH_WITH_HASH (txn_row, hmap_node, uuid_hash(uuid), &table->txn_table->txn_rows) { if (uuid_equals(uuid, &txn_row->uuid)) { return txn_row; } } return NULL; } static struct ovsdb_txn_row * find_or_make_txn_row(struct ovsdb_txn *txn, const struct ovsdb_table *table, const struct uuid *uuid) { struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid); if (!txn_row) { const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid); if (row) { txn_row = ovsdb_txn_row_modify(txn, row)->txn_row; } } return txn_row; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, const struct ovsdb_column *c, const struct ovsdb_base_type *base, const union ovsdb_atom *atoms, unsigned int n, int delta) { const struct ovsdb_table *table; unsigned int i; if (!ovsdb_base_type_is_strong_ref(base)) { return NULL; } table = base->u.uuid.refTable; for (i = 0; i < n; i++) { const struct uuid *uuid = &atoms[i].uuid; struct ovsdb_txn_row *txn_row; if (uuid_equals(uuid, ovsdb_row_get_uuid(r))) { /* Self-references don't count. */ continue; } txn_row = find_or_make_txn_row(txn, table, uuid); if (!txn_row) { return ovsdb_error("referential integrity violation", "Table %s column %s row "UUID_FMT" " "references nonexistent row "UUID_FMT" in " "table %s.", r->table->schema->name, c->name, UUID_ARGS(ovsdb_row_get_uuid(r)), UUID_ARGS(uuid), table->schema->name); } txn_row->n_refs += delta; } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_txn_adjust_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r, const struct ovsdb_column *column, int delta) { const struct ovsdb_datum *field = &r->fields[column->index]; struct ovsdb_error *error; error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.key, field->keys, field->n, delta); if (!error) { error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.value, field->values, field->n, delta); } return error; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT update_row_ref_count(struct ovsdb_txn *txn, struct ovsdb_txn_row *r) { struct ovsdb_table *table = r->table; struct shash_node *node; SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; struct ovsdb_error *error; if (r->old) { error = ovsdb_txn_adjust_row_refs(txn, r->old, column, -1); if (error) { return OVSDB_WRAP_BUG("error decreasing refcount", error); } } if (r->new) { error = ovsdb_txn_adjust_row_refs(txn, r->new, column, 1); if (error) { return error; } } } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT check_ref_count(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *r) { if (r->new || !r->n_refs) { return NULL; } else { return ovsdb_error("referential integrity violation", "cannot delete %s row "UUID_FMT" because " "of %"PRIuSIZE" remaining reference(s)", r->table->schema->name, UUID_ARGS(&r->uuid), r->n_refs); } } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT delete_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *row, const struct ovsdb_base_type *base, const union ovsdb_atom *atoms, unsigned int n) { const struct ovsdb_table *table; unsigned int i; if (!ovsdb_base_type_is_strong_ref(base)) { return NULL; } table = base->u.uuid.refTable; for (i = 0; i < n; i++) { const struct uuid *uuid = &atoms[i].uuid; struct ovsdb_txn_row *txn_row; if (uuid_equals(uuid, ovsdb_row_get_uuid(row))) { /* Self-references don't count. */ continue; } txn_row = find_or_make_txn_row(txn, table, uuid); if (!txn_row) { return OVSDB_BUG("strong ref target missing"); } else if (!txn_row->n_refs) { return OVSDB_BUG("strong ref target has zero n_refs"); } else if (!txn_row->new) { return OVSDB_BUG("deleted strong ref target"); } if (--txn_row->n_refs == 0) { struct ovsdb_error *error = delete_garbage_row(txn, txn_row); if (error) { return error; } } } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) { struct shash_node *node; struct ovsdb_row *row; if (txn_row->table->schema->is_root) { return NULL; } row = txn_row->new; txn_row->new = NULL; hmap_remove(&txn_row->table->rows, &row->hmap_node); SHASH_FOR_EACH (node, &txn_row->table->schema->columns) { const struct ovsdb_column *column = node->data; const struct ovsdb_datum *field = &row->fields[column->index]; struct ovsdb_error *error; error = delete_row_refs(txn, row, &column->type.key, field->keys, field->n); if (error) { return error; } error = delete_row_refs(txn, row, &column->type.value, field->values, field->n); if (error) { return error; } } ovsdb_row_destroy(row); return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT collect_garbage(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) { if (txn_row->new && !txn_row->n_refs) { return delete_garbage_row(txn, txn_row); } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT update_ref_counts(struct ovsdb_txn *txn) { struct ovsdb_error *error; error = for_each_txn_row(txn, update_row_ref_count); if (error) { return error; } return for_each_txn_row(txn, check_ref_count); } static struct ovsdb_error * ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row) { size_t n_indexes = txn_row->table->schema->n_indexes; if (txn_row->old) { size_t i; for (i = 0; i < n_indexes; i++) { struct hmap_node *node = ovsdb_row_get_index_node(txn_row->old, i); hmap_remove(&txn_row->table->indexes[i], node); } } if (txn_row->new) { size_t i; for (i = 0; i < n_indexes; i++) { struct hmap_node *node = ovsdb_row_get_index_node(txn_row->new, i); hmap_insert(&txn_row->table->indexes[i], node, node->hash); } } ovsdb_txn_row_prefree(txn_row); if (txn_row->new) { txn_row->new->n_refs = txn_row->n_refs; } ovsdb_row_destroy(txn_row->old); free(txn_row); return NULL; } static void add_weak_ref(struct ovsdb_txn *txn, const struct ovsdb_row *src_, const struct ovsdb_row *dst_) { struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_); struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_); struct ovsdb_weak_ref *weak; if (src == dst) { return; } dst = ovsdb_txn_row_modify(txn, dst); if (!list_is_empty(&dst->dst_refs)) { /* Omit duplicates. */ weak = CONTAINER_OF(list_back(&dst->dst_refs), struct ovsdb_weak_ref, dst_node); if (weak->src == src) { return; } } weak = xmalloc(sizeof *weak); weak->src = src; list_push_back(&dst->dst_refs, &weak->dst_node); list_push_back(&src->src_refs, &weak->src_node); } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) { struct ovsdb_table *table; struct shash_node *node; if (txn_row->old) { /* Mark rows that have weak references to 'txn_row' as modified, so * that their weak references will get reassessed. */ struct ovsdb_weak_ref *weak, *next; LIST_FOR_EACH_SAFE (weak, next, dst_node, &txn_row->old->dst_refs) { if (!weak->src->txn_row) { ovsdb_txn_row_modify(txn, weak->src); } } } if (!txn_row->new) { /* We don't have to do anything about references that originate at * 'txn_row', because ovsdb_row_destroy() will remove those weak * references. */ return NULL; } table = txn_row->table; SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; struct ovsdb_datum *datum = &txn_row->new->fields[column->index]; unsigned int orig_n, i; bool zero = false; orig_n = datum->n; if (ovsdb_base_type_is_weak_ref(&column->type.key)) { for (i = 0; i < datum->n; ) { const struct ovsdb_row *row; row = ovsdb_table_get_row(column->type.key.u.uuid.refTable, &datum->keys[i].uuid); if (row) { add_weak_ref(txn, txn_row->new, row); i++; } else { if (uuid_is_zero(&datum->keys[i].uuid)) { zero = true; } ovsdb_datum_remove_unsafe(datum, i, &column->type); } } } if (ovsdb_base_type_is_weak_ref(&column->type.value)) { for (i = 0; i < datum->n; ) { const struct ovsdb_row *row; row = ovsdb_table_get_row(column->type.value.u.uuid.refTable, &datum->values[i].uuid); if (row) { add_weak_ref(txn, txn_row->new, row); i++; } else { if (uuid_is_zero(&datum->values[i].uuid)) { zero = true; } ovsdb_datum_remove_unsafe(datum, i, &column->type); } } } if (datum->n != orig_n) { bitmap_set1(txn_row->changed, column->index); ovsdb_datum_sort_assert(datum, column->type.key.type); if (datum->n < column->type.n_min) { const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new); if (zero && !txn_row->old) { return ovsdb_error( "constraint violation", "Weak reference column \"%s\" in \"%s\" row "UUID_FMT " (inserted within this transaction) contained " "all-zeros UUID (probably as the default value for " "this column) but deleting this value caused a " "constraint volation because this column is not " "allowed to be empty.", column->name, table->schema->name, UUID_ARGS(row_uuid)); } else { return ovsdb_error( "constraint violation", "Deletion of %u weak reference(s) to deleted (or " "never-existing) rows from column \"%s\" in \"%s\" " "row "UUID_FMT" %scaused this column to become empty, " "but constraints on this column disallow an " "empty column.", orig_n - datum->n, column->name, table->schema->name, UUID_ARGS(row_uuid), (txn_row->old ? "" : "(inserted within this transaction) ")); } } } } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT determine_changes(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row) { struct ovsdb_table *table = txn_row->table; if (txn_row->old && txn_row->new) { struct shash_node *node; bool changed = false; SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; const struct ovsdb_type *type = &column->type; unsigned int idx = column->index; if (!ovsdb_datum_equals(&txn_row->old->fields[idx], &txn_row->new->fields[idx], type)) { bitmap_set1(txn_row->changed, idx); changed = true; } } if (!changed) { /* Nothing actually changed in this row, so drop it. */ ovsdb_txn_row_abort(txn, txn_row); } } else { bitmap_set_multiple(txn_row->changed, 0, shash_count(&table->schema->columns), 1); } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT check_max_rows(struct ovsdb_txn *txn) { struct ovsdb_txn_table *t; LIST_FOR_EACH (t, node, &txn->txn_tables) { size_t n_rows = hmap_count(&t->table->rows); unsigned int max_rows = t->table->schema->max_rows; if (n_rows > max_rows) { return ovsdb_error("constraint violation", "transaction causes \"%s\" table to contain " "%"PRIuSIZE" rows, greater than the schema-defined " "limit of %u row(s)", t->table->schema->name, n_rows, max_rows); } } return NULL; } static struct ovsdb_row * ovsdb_index_search(struct hmap *index, struct ovsdb_row *row, size_t i, uint32_t hash) { const struct ovsdb_table *table = row->table; const struct ovsdb_column_set *columns = &table->schema->indexes[i]; struct hmap_node *node; for (node = hmap_first_with_hash(index, hash); node; node = hmap_next_with_hash(node)) { struct ovsdb_row *irow = ovsdb_row_from_index_node(node, table, i); if (ovsdb_row_equal_columns(row, irow, columns)) { return irow; } } return NULL; } static void duplicate_index_row__(const struct ovsdb_column_set *index, const struct ovsdb_row *row, const char *title, struct ds *out) { size_t n_columns = shash_count(&row->table->schema->columns); ds_put_format(out, "%s row, with UUID "UUID_FMT", ", title, UUID_ARGS(ovsdb_row_get_uuid(row))); if (!row->txn_row || bitmap_scan(row->txn_row->changed, 1, 0, n_columns) == n_columns) { ds_put_cstr(out, "existed in the database before this " "transaction and was not modified by the transaction."); } else if (!row->txn_row->old) { ds_put_cstr(out, "was inserted by this transaction."); } else if (ovsdb_row_equal_columns(row->txn_row->old, row->txn_row->new, index)) { ds_put_cstr(out, "existed in the database before this " "transaction, which modified some of the row's columns " "but not any columns in this index."); } else { ds_put_cstr(out, "had the following index values before the " "transaction: "); ovsdb_row_columns_to_string(row->txn_row->old, index, out); ds_put_char(out, '.'); } } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT duplicate_index_row(const struct ovsdb_column_set *index, const struct ovsdb_row *a, const struct ovsdb_row *b) { struct ovsdb_column_set all_columns; struct ovsdb_error *error; char *index_s; struct ds s; /* Put 'a' and 'b' in a predictable order to make error messages * reproducible for testing. */ ovsdb_column_set_init(&all_columns); ovsdb_column_set_add_all(&all_columns, a->table); if (ovsdb_row_compare_columns_3way(a, b, &all_columns) < 0) { const struct ovsdb_row *tmp = a; a = b; b = tmp; } ovsdb_column_set_destroy(&all_columns); index_s = ovsdb_column_set_to_string(index); ds_init(&s); ds_put_format(&s, "Transaction causes multiple rows in \"%s\" table to " "have identical values (", a->table->schema->name); ovsdb_row_columns_to_string(a, index, &s); ds_put_format(&s, ") for index on %s. ", index_s); duplicate_index_row__(index, a, "First", &s); ds_put_cstr(&s, " "); duplicate_index_row__(index, b, "Second", &s); free(index_s); error = ovsdb_error("constraint violation", "%s", ds_cstr(&s)); ds_destroy(&s); return error; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT check_index_uniqueness(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row) { struct ovsdb_txn_table *txn_table = txn_row->table->txn_table; struct ovsdb_table *table = txn_row->table; struct ovsdb_row *row = txn_row->new; size_t i; if (!row) { return NULL; } for (i = 0; i < table->schema->n_indexes; i++) { const struct ovsdb_column_set *index = &table->schema->indexes[i]; struct ovsdb_row *irow; uint32_t hash; hash = ovsdb_row_hash_columns(row, index, 0); irow = ovsdb_index_search(&txn_table->txn_indexes[i], row, i, hash); if (irow) { return duplicate_index_row(index, irow, row); } irow = ovsdb_index_search(&table->indexes[i], row, i, hash); if (irow && !irow->txn_row) { return duplicate_index_row(index, irow, row); } hmap_insert(&txn_table->txn_indexes[i], ovsdb_row_get_index_node(row, i), hash); } return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT update_version(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row) { struct ovsdb_table *table = txn_row->table; size_t n_columns = shash_count(&table->schema->columns); if (txn_row->old && txn_row->new && !bitmap_is_all_zeros(txn_row->changed, n_columns)) { bitmap_set1(txn_row->changed, OVSDB_COL_VERSION); uuid_generate(ovsdb_row_get_version_rw(txn_row->new)); } return NULL; } static struct ovsdb_error * ovsdb_txn_commit_(struct ovsdb_txn *txn, bool durable) { struct ovsdb_replica *replica; struct ovsdb_error *error; /* Figure out what actually changed, and abort early if the transaction * was really a no-op. */ error = for_each_txn_row(txn, determine_changes); if (error) { return OVSDB_WRAP_BUG("can't happen", error); } if (list_is_empty(&txn->txn_tables)) { ovsdb_txn_abort(txn); return NULL; } /* Update reference counts and check referential integrity. */ error = update_ref_counts(txn); if (error) { ovsdb_txn_abort(txn); return error; } /* Delete unreferenced, non-root rows. */ error = for_each_txn_row(txn, collect_garbage); if (error) { ovsdb_txn_abort(txn); return OVSDB_WRAP_BUG("can't happen", error); } /* Check maximum rows table constraints. */ error = check_max_rows(txn); if (error) { ovsdb_txn_abort(txn); return error; } /* Check reference counts and remove bad references for "weak" referential * integrity. */ error = for_each_txn_row(txn, assess_weak_refs); if (error) { ovsdb_txn_abort(txn); return error; } /* Verify that the indexes will still be unique post-transaction. */ error = for_each_txn_row(txn, check_index_uniqueness); if (error) { ovsdb_txn_abort(txn); return error; } /* Update _version for rows that changed. */ error = for_each_txn_row(txn, update_version); if (error) { return OVSDB_WRAP_BUG("can't happen", error); } /* Send the commit to each replica. */ LIST_FOR_EACH (replica, node, &txn->db->replicas) { error = (replica->class->commit)(replica, txn, durable); if (error) { /* We don't support two-phase commit so only the first replica is * allowed to report an error. */ ovs_assert(&replica->node == txn->db->replicas.next); ovsdb_txn_abort(txn); return error; } } /* Finalize commit. */ txn->db->run_triggers = true; ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_row_commit)); ovsdb_txn_free(txn); return NULL; } struct ovsdb_error * ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable) { struct ovsdb_error *err; PERF(__func__, err = ovsdb_txn_commit_(txn, durable)); return err; } void ovsdb_txn_for_each_change(const struct ovsdb_txn *txn, ovsdb_txn_row_cb_func *cb, void *aux) { struct ovsdb_txn_table *t; struct ovsdb_txn_row *r; LIST_FOR_EACH (t, node, &txn->txn_tables) { HMAP_FOR_EACH (r, hmap_node, &t->txn_rows) { if ((r->old || r->new) && !cb(r->old, r->new, r->changed, aux)) { break; } } } } static struct ovsdb_txn_table * ovsdb_txn_create_txn_table(struct ovsdb_txn *txn, struct ovsdb_table *table) { if (!table->txn_table) { struct ovsdb_txn_table *txn_table; size_t i; table->txn_table = txn_table = xmalloc(sizeof *table->txn_table); txn_table->table = table; hmap_init(&txn_table->txn_rows); txn_table->serial = serial - 1; txn_table->txn_indexes = xmalloc(table->schema->n_indexes * sizeof *txn_table->txn_indexes); for (i = 0; i < table->schema->n_indexes; i++) { hmap_init(&txn_table->txn_indexes[i]); } list_push_back(&txn->txn_tables, &txn_table->node); } return table->txn_table; } static struct ovsdb_txn_row * ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table, const struct ovsdb_row *old_, struct ovsdb_row *new) { const struct ovsdb_row *row = old_ ? old_ : new; struct ovsdb_row *old = CONST_CAST(struct ovsdb_row *, old_); size_t n_columns = shash_count(&table->schema->columns); struct ovsdb_txn_table *txn_table; struct ovsdb_txn_row *txn_row; txn_row = xzalloc(offsetof(struct ovsdb_txn_row, changed) + bitmap_n_bytes(n_columns)); txn_row->uuid = *ovsdb_row_get_uuid(row); txn_row->table = row->table; txn_row->old = old; txn_row->new = new; txn_row->n_refs = old ? old->n_refs : 0; txn_row->serial = serial - 1; if (old) { old->txn_row = txn_row; } if (new) { new->txn_row = txn_row; } txn_table = ovsdb_txn_create_txn_table(txn, table); hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node, ovsdb_row_hash(old ? old : new)); return txn_row; } struct ovsdb_row * ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_) { struct ovsdb_row *ro_row = CONST_CAST(struct ovsdb_row *, ro_row_); if (ro_row->txn_row) { ovs_assert(ro_row == ro_row->txn_row->new); return ro_row; } else { struct ovsdb_table *table = ro_row->table; struct ovsdb_row *rw_row; rw_row = ovsdb_row_clone(ro_row); rw_row->n_refs = ro_row->n_refs; ovsdb_txn_row_create(txn, table, ro_row, rw_row); hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node); return rw_row; } } void ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row) { uint32_t hash = ovsdb_row_hash(row); struct ovsdb_table *table = row->table; uuid_generate(ovsdb_row_get_version_rw(row)); ovsdb_txn_row_create(txn, table, NULL, row); hmap_insert(&table->rows, &row->hmap_node, hash); } /* 'row' must be assumed destroyed upon return; the caller must not reference * it again. */ void ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_) { struct ovsdb_row *row = CONST_CAST(struct ovsdb_row *, row_); struct ovsdb_table *table = row->table; struct ovsdb_txn_row *txn_row = row->txn_row; hmap_remove(&table->rows, &row->hmap_node); if (!txn_row) { ovsdb_txn_row_create(txn, table, row, NULL); } else { ovs_assert(txn_row->new == row); if (txn_row->old) { txn_row->new = NULL; } else { hmap_remove(&table->txn_table->txn_rows, &txn_row->hmap_node); free(txn_row); } ovsdb_row_destroy(row); } } void ovsdb_txn_add_comment(struct ovsdb_txn *txn, const char *s) { if (txn->comment.length) { ds_put_char(&txn->comment, '\n'); } ds_put_cstr(&txn->comment, s); } const char * ovsdb_txn_get_comment(const struct ovsdb_txn *txn) { return txn->comment.length ? ds_cstr_ro(&txn->comment) : NULL; } static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *txn_row) { struct ovsdb_txn_table *txn_table = txn_row->table->txn_table; txn_table->n_processed--; hmap_remove(&txn_table->txn_rows, &txn_row->hmap_node); if (txn_row->old) { txn_row->old->txn_row = NULL; } if (txn_row->new) { txn_row->new->txn_row = NULL; } } static void ovsdb_txn_table_destroy(struct ovsdb_txn_table *txn_table) { size_t i; ovs_assert(hmap_is_empty(&txn_table->txn_rows)); for (i = 0; i < txn_table->table->schema->n_indexes; i++) { hmap_destroy(&txn_table->txn_indexes[i]); } free(txn_table->txn_indexes); txn_table->table->txn_table = NULL; hmap_destroy(&txn_table->txn_rows); list_remove(&txn_table->node); free(txn_table); } /* Calls 'cb' for every txn_row within 'txn'. If 'cb' returns nonnull, this * aborts the iteration and for_each_txn_row() passes the error up. Otherwise, * returns a null pointer after iteration is complete. * * 'cb' may insert new txn_rows and new txn_tables into 'txn'. It may delete * the txn_row that it is passed in, or txn_rows in txn_tables other than the * one passed to 'cb'. It may *not* delete txn_rows other than the one passed * in within the same txn_table. It may *not* delete any txn_tables. As long * as these rules are followed, 'cb' will be called exactly once for each * txn_row in 'txn', even those added by 'cb'. * * (Even though 'cb' is not allowed to delete some txn_rows, it can still * delete any actual row by clearing a txn_row's 'new' member.) */ static struct ovsdb_error * OVS_WARN_UNUSED_RESULT for_each_txn_row(struct ovsdb_txn *txn, struct ovsdb_error *(*cb)(struct ovsdb_txn *, struct ovsdb_txn_row *)) { bool any_work; serial++; do { struct ovsdb_txn_table *t, *next_txn_table; any_work = false; LIST_FOR_EACH_SAFE (t, next_txn_table, node, &txn->txn_tables) { if (t->serial != serial) { t->serial = serial; t->n_processed = 0; } while (t->n_processed < hmap_count(&t->txn_rows)) { struct ovsdb_txn_row *r, *next_txn_row; HMAP_FOR_EACH_SAFE (r, next_txn_row, hmap_node, &t->txn_rows) { if (r->serial != serial) { struct ovsdb_error *error; r->serial = serial; t->n_processed++; any_work = true; error = cb(txn, r); if (error) { return error; } } } } if (hmap_is_empty(&t->txn_rows)) { /* Table is empty. Drop it. */ ovsdb_txn_table_destroy(t); } } } while (any_work); return NULL; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/table.c0000644000000000000000000000013213534540071016214 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.097855756 openvswitch-2.5.9/ovsdb/table.c0000644000175000017500000002437713534540071017717 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "table.h" #include #include "json.h" #include "column.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb-types.h" #include "row.h" static void add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column) { ovs_assert(!shash_find(&ts->columns, column->name)); column->index = shash_count(&ts->columns); shash_add(&ts->columns, column->name, column); } struct ovsdb_table_schema * ovsdb_table_schema_create(const char *name, bool mutable, unsigned int max_rows, bool is_root) { struct ovsdb_column *uuid, *version; struct ovsdb_table_schema *ts; ts = xzalloc(sizeof *ts); ts->name = xstrdup(name); ts->mutable = mutable; shash_init(&ts->columns); ts->max_rows = max_rows; ts->is_root = is_root; uuid = ovsdb_column_create("_uuid", false, true, &ovsdb_type_uuid); add_column(ts, uuid); ovs_assert(uuid->index == OVSDB_COL_UUID); version = ovsdb_column_create("_version", false, false, &ovsdb_type_uuid); add_column(ts, version); ovs_assert(version->index == OVSDB_COL_VERSION); ts->n_indexes = 0; ts->indexes = NULL; return ts; } struct ovsdb_table_schema * ovsdb_table_schema_clone(const struct ovsdb_table_schema *old) { struct ovsdb_table_schema *new; struct shash_node *node; size_t i; new = ovsdb_table_schema_create(old->name, old->mutable, old->max_rows, old->is_root); SHASH_FOR_EACH (node, &old->columns) { const struct ovsdb_column *column = node->data; if (column->name[0] == '_') { /* Added automatically by ovsdb_table_schema_create(). */ continue; } add_column(new, ovsdb_column_clone(column)); } new->n_indexes = old->n_indexes; new->indexes = xmalloc(new->n_indexes * sizeof *new->indexes); for (i = 0; i < new->n_indexes; i++) { const struct ovsdb_column_set *old_index = &old->indexes[i]; struct ovsdb_column_set *new_index = &new->indexes[i]; size_t j; ovsdb_column_set_init(new_index); for (j = 0; j < old_index->n_columns; j++) { const struct ovsdb_column *old_column = old_index->columns[j]; const struct ovsdb_column *new_column; new_column = ovsdb_table_schema_get_column(new, old_column->name); ovsdb_column_set_add(new_index, new_column); } } return new; } void ovsdb_table_schema_destroy(struct ovsdb_table_schema *ts) { struct shash_node *node; size_t i; for (i = 0; i < ts->n_indexes; i++) { ovsdb_column_set_destroy(&ts->indexes[i]); } free(ts->indexes); SHASH_FOR_EACH (node, &ts->columns) { ovsdb_column_destroy(node->data); } shash_destroy(&ts->columns); free(ts->name); free(ts); } struct ovsdb_error * ovsdb_table_schema_from_json(const struct json *json, const char *name, struct ovsdb_table_schema **tsp) { struct ovsdb_table_schema *ts; const struct json *columns, *mutable, *max_rows, *is_root, *indexes; struct shash_node *node; struct ovsdb_parser parser; struct ovsdb_error *error; long long int n_max_rows; *tsp = NULL; ovsdb_parser_init(&parser, json, "table schema for table %s", name); columns = ovsdb_parser_member(&parser, "columns", OP_OBJECT); mutable = ovsdb_parser_member(&parser, "mutable", OP_TRUE | OP_FALSE | OP_OPTIONAL); max_rows = ovsdb_parser_member(&parser, "maxRows", OP_INTEGER | OP_OPTIONAL); is_root = ovsdb_parser_member(&parser, "isRoot", OP_BOOLEAN | OP_OPTIONAL); indexes = ovsdb_parser_member(&parser, "indexes", OP_ARRAY | OP_OPTIONAL); error = ovsdb_parser_finish(&parser); if (error) { return error; } if (max_rows) { if (json_integer(max_rows) <= 0) { return ovsdb_syntax_error(json, NULL, "maxRows must be at least 1"); } n_max_rows = max_rows->u.integer; } else { n_max_rows = UINT_MAX; } if (shash_is_empty(json_object(columns))) { return ovsdb_syntax_error(json, NULL, "table must have at least one column"); } ts = ovsdb_table_schema_create(name, mutable ? json_boolean(mutable) : true, MIN(n_max_rows, UINT_MAX), is_root ? json_boolean(is_root) : false); SHASH_FOR_EACH (node, json_object(columns)) { struct ovsdb_column *column; if (node->name[0] == '_') { error = ovsdb_syntax_error(json, NULL, "names beginning with " "\"_\" are reserved"); } else if (!ovsdb_parser_is_id(node->name)) { error = ovsdb_syntax_error(json, NULL, "name must be a valid id"); } else { error = ovsdb_column_from_json(node->data, node->name, &column); } if (error) { goto error; } add_column(ts, column); } if (indexes) { size_t i; ts->indexes = xmalloc(indexes->u.array.n * sizeof *ts->indexes); for (i = 0; i < indexes->u.array.n; i++) { struct ovsdb_column_set *index = &ts->indexes[i]; size_t j; error = ovsdb_column_set_from_json(indexes->u.array.elems[i], ts, index); if (error) { goto error; } if (index->n_columns == 0) { error = ovsdb_syntax_error(json, NULL, "index must have " "at least one column"); goto error; } ts->n_indexes++; for (j = 0; j < index->n_columns; j++) { const struct ovsdb_column *column = index->columns[j]; if (!column->persistent) { error = ovsdb_syntax_error(json, NULL, "ephemeral columns " "(such as %s) may not be " "indexed", column->name); goto error; } } } } *tsp = ts; return NULL; error: ovsdb_table_schema_destroy(ts); return error; } /* Returns table schema 'ts' serialized into JSON. * * The "isRoot" member is included in the JSON only if its value would differ * from 'default_is_root'. Ordinarily 'default_is_root' should be false, * because ordinarily a table would be not be part of the root set if its * "isRoot" member is omitted. However, garbage collection was not originally * included in OVSDB, so in older schemas that do not include any "isRoot" * members, every table is implicitly part of the root set. To serialize such * a schema in a way that can be read by older OVSDB tools, specify * 'default_is_root' as true. */ struct json * ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts, bool default_is_root) { struct json *json, *columns; struct shash_node *node; json = json_object_create(); if (!ts->mutable) { json_object_put(json, "mutable", json_boolean_create(false)); } if (default_is_root != ts->is_root) { json_object_put(json, "isRoot", json_boolean_create(ts->is_root)); } columns = json_object_create(); SHASH_FOR_EACH (node, &ts->columns) { const struct ovsdb_column *column = node->data; if (node->name[0] != '_') { json_object_put(columns, column->name, ovsdb_column_to_json(column)); } } json_object_put(json, "columns", columns); if (ts->max_rows != UINT_MAX) { json_object_put(json, "maxRows", json_integer_create(ts->max_rows)); } if (ts->n_indexes) { struct json **indexes; size_t i; indexes = xmalloc(ts->n_indexes * sizeof *indexes); for (i = 0; i < ts->n_indexes; i++) { indexes[i] = ovsdb_column_set_to_json(&ts->indexes[i]); } json_object_put(json, "indexes", json_array_create(indexes, ts->n_indexes)); } return json; } const struct ovsdb_column * ovsdb_table_schema_get_column(const struct ovsdb_table_schema *ts, const char *name) { return shash_find_data(&ts->columns, name); } struct ovsdb_table * ovsdb_table_create(struct ovsdb_table_schema *ts) { struct ovsdb_table *table; size_t i; table = xmalloc(sizeof *table); table->schema = ts; table->txn_table = NULL; table->indexes = xmalloc(ts->n_indexes * sizeof *table->indexes); for (i = 0; i < ts->n_indexes; i++) { hmap_init(&table->indexes[i]); } hmap_init(&table->rows); return table; } void ovsdb_table_destroy(struct ovsdb_table *table) { if (table) { struct ovsdb_row *row, *next; size_t i; HMAP_FOR_EACH_SAFE (row, next, hmap_node, &table->rows) { ovsdb_row_destroy(row); } hmap_destroy(&table->rows); for (i = 0; i < table->schema->n_indexes; i++) { hmap_destroy(&table->indexes[i]); } free(table->indexes); ovsdb_table_schema_destroy(table->schema); free(table); } } const struct ovsdb_row * ovsdb_table_get_row(const struct ovsdb_table *table, const struct uuid *uuid) { struct ovsdb_row *row; HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { if (uuid_equals(ovsdb_row_get_uuid(row), uuid)) { return row; } } return NULL; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-dot.in0000644000000000000000000000013213534540071017212 xustar0030 mtime=1567801401.813684195 30 atime=1567801402.121686458 30 ctime=1567801424.181849003 openvswitch-2.5.9/ovsdb/ovsdb-dot.in0000755000175000017500000000677713534540071020724 0ustar00jpettitjpettit00000000000000#! @PYTHON@ from datetime import date import ovs.db.error import ovs.db.schema import getopt import os import re import sys argv0 = sys.argv[0] def printEdge(tableName, type, baseType, label): if baseType.ref_table_name: if type.n_min == 0: if type.n_max == 1: arity = "?" elif type.n_max == sys.maxint: arity = "*" else: arity = "{,%d}" % type.n_max elif type.n_min == 1: if type.n_max == 1: arity = "" elif type.n_max == sys.maxint: arity = "+" else: arity = "{1,%d}" % type.n_max options = {} options['label'] = '"%s%s"' % (label, arity) if baseType.ref_type == 'weak': options['style'] = 'dotted' print "\t%s -> %s [%s];" % ( tableName, baseType.ref_table_name, ', '.join(['%s=%s' % (k,v) for k,v in options.items()])) def schemaToDot(schemaFile, arrows): schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile)) print "digraph %s {" % schema.name print '\trankdir=LR;' print '\tsize="6.5,4";' print '\tmargin="0";' print "\tnode [shape=box];" if not arrows: print "\tedge [dir=none, arrowhead=none, arrowtail=none];" for tableName, table in schema.tables.iteritems(): options = {} if table.is_root: options['style'] = 'bold' print "\t%s [%s];" % ( tableName, ', '.join(['%s=%s' % (k,v) for k,v in options.items()])) for columnName, column in table.columns.iteritems(): if column.type.value: printEdge(tableName, column.type, column.type.key, "%s key" % columnName) printEdge(tableName, column.type, column.type.value, "%s value" % columnName) else: printEdge(tableName, column.type, column.type.key, columnName) print "}"; def usage(): print """\ %(argv0)s: compiles ovsdb schemas to graphviz format Prints a .dot file that "dot" can render to an entity-relationship diagram usage: %(argv0)s [OPTIONS] SCHEMA where SCHEMA is an OVSDB schema in JSON format The following options are also available: --no-arrows omit arrows from diagram -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['no-arrows', 'help', 'version',]) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) arrows = True for key, value in options: if key == '--no-arrows': arrows = False elif key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print "ovsdb-dot (Open vSwitch) @VERSION@" else: sys.exit(0) if len(args) != 1: sys.stderr.write("%s: exactly 1 non-option argument required " "(use --help for help)\n" % argv0) sys.exit(1) schemaToDot(args[0], arrows) except ovs.db.error.Error, e: sys.stderr.write("%s: %s\n" % (argv0, e.msg)) sys.exit(1) # Local variables: # mode: python # End: openvswitch-2.5.9/ovsdb/PaxHeaders.82075/file.c0000644000000000000000000000013213534540071016044 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.121686458 30 ctime=1567801425.077855608 openvswitch-2.5.9/ovsdb/file.c0000644000175000017500000006005013534540071017533 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "file.h" #include #include #include #include "bitmap.h" #include "column.h" #include "log.h" #include "json.h" #include "lockfile.h" #include "ovsdb.h" #include "ovsdb-error.h" #include "row.h" #include "socket-util.h" #include "table.h" #include "timeval.h" #include "transaction.h" #include "uuid.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_file); /* Minimum number of milliseconds between database compactions. */ #define COMPACT_MIN_MSEC (10 * 60 * 1000) /* 10 minutes. */ /* Minimum number of milliseconds between trying to compact the database if * compacting fails. */ #define COMPACT_RETRY_MSEC (60 * 1000) /* 1 minute. */ /* A transaction being converted to JSON for writing to a file. */ struct ovsdb_file_txn { struct json *json; /* JSON for the whole transaction. */ struct json *table_json; /* JSON for 'table''s transaction. */ struct ovsdb_table *table; /* Table described in 'table_json'. */ }; static void ovsdb_file_txn_init(struct ovsdb_file_txn *); static void ovsdb_file_txn_add_row(struct ovsdb_file_txn *, const struct ovsdb_row *old, const struct ovsdb_row *new, const unsigned long int *changed); static struct ovsdb_error *ovsdb_file_txn_commit(struct json *, const char *comment, bool durable, struct ovsdb_log *); static struct ovsdb_error *ovsdb_file_open__(const char *file_name, const struct ovsdb_schema *, bool read_only, struct ovsdb **, struct ovsdb_file **); static struct ovsdb_error *ovsdb_file_txn_from_json( struct ovsdb *, const struct json *, bool converting, struct ovsdb_txn **); static struct ovsdb_error *ovsdb_file_create(struct ovsdb *, struct ovsdb_log *, const char *file_name, unsigned int n_transactions, struct ovsdb_file **filep); /* Opens database 'file_name' and stores a pointer to the new database in * '*dbp'. If 'read_only' is false, then the database will be locked and * changes to the database will be written to disk. If 'read_only' is true, * the database will not be locked and changes to the database will persist * only as long as the "struct ovsdb". * * If 'filep' is nonnull and 'read_only' is false, then on success sets * '*filep' to an ovsdb_file that represents the open file. This ovsdb_file * persists until '*dbp' is destroyed. * * On success, returns NULL. On failure, returns an ovsdb_error (which the * caller must destroy) and sets '*dbp' and '*filep' to NULL. */ struct ovsdb_error * ovsdb_file_open(const char *file_name, bool read_only, struct ovsdb **dbp, struct ovsdb_file **filep) { return ovsdb_file_open__(file_name, NULL, read_only, dbp, filep); } /* Opens database 'file_name' with an alternate schema. The specified 'schema' * is used to interpret the data in 'file_name', ignoring the schema actually * stored in the file. Data in the file for tables or columns that do not * exist in 'schema' are ignored, but the ovsdb file format must otherwise be * observed, including column constraints. * * This function can be useful for upgrading or downgrading databases to * "almost-compatible" formats. * * The database will not be locked. Changes to the database will persist only * as long as the "struct ovsdb". * * On success, stores a pointer to the new database in '*dbp' and returns a * null pointer. On failure, returns an ovsdb_error (which the caller must * destroy) and sets '*dbp' to NULL. */ struct ovsdb_error * ovsdb_file_open_as_schema(const char *file_name, const struct ovsdb_schema *schema, struct ovsdb **dbp) { return ovsdb_file_open__(file_name, schema, true, dbp, NULL); } static struct ovsdb_error * ovsdb_file_open_log(const char *file_name, enum ovsdb_log_open_mode open_mode, struct ovsdb_log **logp, struct ovsdb_schema **schemap) { struct ovsdb_schema *schema = NULL; struct ovsdb_log *log = NULL; struct ovsdb_error *error; struct json *json = NULL; ovs_assert(logp || schemap); error = ovsdb_log_open(file_name, open_mode, -1, &log); if (error) { goto error; } error = ovsdb_log_read(log, &json); if (error) { goto error; } else if (!json) { error = ovsdb_io_error(EOF, "%s: database file contains no schema", file_name); goto error; } if (schemap) { error = ovsdb_schema_from_json(json, &schema); if (error) { error = ovsdb_wrap_error(error, "failed to parse \"%s\" as ovsdb schema", file_name); goto error; } } json_destroy(json); if (logp) { *logp = log; } else { ovsdb_log_close(log); } if (schemap) { *schemap = schema; } return NULL; error: ovsdb_log_close(log); json_destroy(json); if (logp) { *logp = NULL; } if (schemap) { *schemap = NULL; } return error; } static struct ovsdb_error * ovsdb_file_open__(const char *file_name, const struct ovsdb_schema *alternate_schema, bool read_only, struct ovsdb **dbp, struct ovsdb_file **filep) { enum ovsdb_log_open_mode open_mode; unsigned int n_transactions; struct ovsdb_schema *schema = NULL; struct ovsdb_error *error; struct ovsdb_log *log; struct json *json; struct ovsdb *db = NULL; /* In read-only mode there is no ovsdb_file so 'filep' must be null. */ ovs_assert(!(read_only && filep)); open_mode = read_only ? OVSDB_LOG_READ_ONLY : OVSDB_LOG_READ_WRITE; error = ovsdb_file_open_log(file_name, open_mode, &log, alternate_schema ? NULL : &schema); if (error) { goto error; } db = ovsdb_create(schema ? schema : ovsdb_schema_clone(alternate_schema)); n_transactions = 0; while ((error = ovsdb_log_read(log, &json)) == NULL && json) { struct ovsdb_txn *txn; error = ovsdb_file_txn_from_json(db, json, alternate_schema != NULL, &txn); json_destroy(json); if (error) { ovsdb_log_unread(log); break; } n_transactions++; error = ovsdb_txn_commit(txn, false); if (error) { ovsdb_log_unread(log); break; } } if (error) { /* Log error but otherwise ignore it. Probably the database just got * truncated due to power failure etc. and we should use its current * contents. */ char *msg = ovsdb_error_to_string(error); VLOG_ERR("%s", msg); free(msg); ovsdb_error_destroy(error); } if (!read_only) { struct ovsdb_file *file; error = ovsdb_file_create(db, log, file_name, n_transactions, &file); if (error) { goto error; } if (filep) { *filep = file; } } else { ovsdb_log_close(log); } *dbp = db; return NULL; error: *dbp = NULL; if (filep) { *filep = NULL; } ovsdb_destroy(db); ovsdb_log_close(log); return error; } static struct ovsdb_error * ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting, const struct json *json) { struct ovsdb_table_schema *schema = row->table->schema; struct ovsdb_error *error; struct shash_node *node; if (json->type != JSON_OBJECT) { return ovsdb_syntax_error(json, NULL, "row must be JSON object"); } SHASH_FOR_EACH (node, json_object(json)) { const char *column_name = node->name; const struct ovsdb_column *column; struct ovsdb_datum datum; column = ovsdb_table_schema_get_column(schema, column_name); if (!column) { if (converting) { continue; } return ovsdb_syntax_error(json, "unknown column", "No column %s in table %s.", column_name, schema->name); } error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL); if (error) { return error; } ovsdb_datum_swap(&row->fields[column->index], &datum); ovsdb_datum_destroy(&datum, &column->type); } return NULL; } static struct ovsdb_error * ovsdb_file_txn_row_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table, bool converting, const struct uuid *row_uuid, struct json *json) { const struct ovsdb_row *row = ovsdb_table_get_row(table, row_uuid); if (json->type == JSON_NULL) { if (!row) { return ovsdb_syntax_error(NULL, NULL, "transaction deletes " "row "UUID_FMT" that does not exist", UUID_ARGS(row_uuid)); } ovsdb_txn_row_delete(txn, row); return NULL; } else if (row) { return ovsdb_file_update_row_from_json(ovsdb_txn_row_modify(txn, row), converting, json); } else { struct ovsdb_error *error; struct ovsdb_row *new; new = ovsdb_row_create(table); *ovsdb_row_get_uuid_rw(new) = *row_uuid; error = ovsdb_file_update_row_from_json(new, converting, json); if (error) { ovsdb_row_destroy(new); } else { ovsdb_txn_row_insert(txn, new); } return error; } } static struct ovsdb_error * ovsdb_file_txn_table_from_json(struct ovsdb_txn *txn, struct ovsdb_table *table, bool converting, struct json *json) { struct shash_node *node; if (json->type != JSON_OBJECT) { return ovsdb_syntax_error(json, NULL, "object expected"); } SHASH_FOR_EACH (node, json->u.object) { const char *uuid_string = node->name; struct json *txn_row_json = node->data; struct ovsdb_error *error; struct uuid row_uuid; if (!uuid_from_string(&row_uuid, uuid_string)) { return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID", uuid_string); } error = ovsdb_file_txn_row_from_json(txn, table, converting, &row_uuid, txn_row_json); if (error) { return error; } } return NULL; } /* Converts 'json' to an ovsdb_txn for 'db', storing the new transaction in * '*txnp'. Returns NULL if successful, otherwise an error. * * If 'converting' is true, then unknown table and column names are ignored * (which can ease upgrading and downgrading schemas); otherwise, they are * treated as errors. */ static struct ovsdb_error * ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json, bool converting, struct ovsdb_txn **txnp) { struct ovsdb_error *error; struct shash_node *node; struct ovsdb_txn *txn; *txnp = NULL; if (json->type != JSON_OBJECT) { return ovsdb_syntax_error(json, NULL, "object expected"); } txn = ovsdb_txn_create(db); SHASH_FOR_EACH (node, json->u.object) { const char *table_name = node->name; struct json *node_json = node->data; struct ovsdb_table *table; table = shash_find_data(&db->tables, table_name); if (!table) { if (!strcmp(table_name, "_date") && node_json->type == JSON_INTEGER) { continue; } else if (!strcmp(table_name, "_comment") || converting) { continue; } error = ovsdb_syntax_error(json, "unknown table", "No table named %s.", table_name); goto error; } error = ovsdb_file_txn_table_from_json(txn, table, converting, node_json); if (error) { goto error; } } *txnp = txn; return NULL; error: ovsdb_txn_abort(txn); return error; } static struct ovsdb_error * ovsdb_file_save_copy__(const char *file_name, int locking, const char *comment, const struct ovsdb *db, struct ovsdb_log **logp) { const struct shash_node *node; struct ovsdb_file_txn ftxn; struct ovsdb_error *error; struct ovsdb_log *log; struct json *json; error = ovsdb_log_open(file_name, OVSDB_LOG_CREATE, locking, &log); if (error) { return error; } /* Write schema. */ json = ovsdb_schema_to_json(db->schema); error = ovsdb_log_write(log, json); json_destroy(json); if (error) { goto exit; } /* Write data. */ ovsdb_file_txn_init(&ftxn); SHASH_FOR_EACH (node, &db->tables) { const struct ovsdb_table *table = node->data; const struct ovsdb_row *row; HMAP_FOR_EACH (row, hmap_node, &table->rows) { ovsdb_file_txn_add_row(&ftxn, NULL, row, NULL); } } error = ovsdb_file_txn_commit(ftxn.json, comment, true, log); exit: if (logp) { if (!error) { *logp = log; log = NULL; } else { *logp = NULL; } } ovsdb_log_close(log); if (error) { remove(file_name); } return error; } /* Saves a snapshot of 'db''s current contents as 'file_name'. If 'comment' is * nonnull, then it is added along with the data contents and can be viewed * with "ovsdb-tool show-log". * * 'locking' is passed along to ovsdb_log_open() untouched. */ struct ovsdb_error * ovsdb_file_save_copy(const char *file_name, int locking, const char *comment, const struct ovsdb *db) { return ovsdb_file_save_copy__(file_name, locking, comment, db, NULL); } /* Opens database 'file_name', reads its schema, and closes it. On success, * stores the schema into '*schemap' and returns NULL; the caller then owns the * schema. On failure, returns an ovsdb_error (which the caller must destroy) * and sets '*dbp' to NULL. */ struct ovsdb_error * ovsdb_file_read_schema(const char *file_name, struct ovsdb_schema **schemap) { ovs_assert(schemap != NULL); return ovsdb_file_open_log(file_name, OVSDB_LOG_READ_ONLY, NULL, schemap); } /* Replica implementation. */ struct ovsdb_file { struct ovsdb_replica replica; struct ovsdb *db; struct ovsdb_log *log; char *file_name; long long int last_compact; long long int next_compact; unsigned int n_transactions; }; static const struct ovsdb_replica_class ovsdb_file_class; static struct ovsdb_error * ovsdb_file_create(struct ovsdb *db, struct ovsdb_log *log, const char *file_name, unsigned int n_transactions, struct ovsdb_file **filep) { struct ovsdb_file *file; char *deref_name; char *abs_name; /* Use the absolute name of the file because ovsdb-server opens its * database before daemonize() chdirs to "/". */ deref_name = follow_symlinks(file_name); abs_name = abs_file_name(NULL, deref_name); free(deref_name); if (!abs_name) { *filep = NULL; return ovsdb_io_error(0, "could not determine current " "working directory"); } file = xmalloc(sizeof *file); ovsdb_replica_init(&file->replica, &ovsdb_file_class); file->db = db; file->log = log; file->file_name = abs_name; file->last_compact = time_msec(); file->next_compact = file->last_compact + COMPACT_MIN_MSEC; file->n_transactions = n_transactions; ovsdb_add_replica(db, &file->replica); *filep = file; return NULL; } static struct ovsdb_file * ovsdb_file_cast(struct ovsdb_replica *replica) { ovs_assert(replica->class == &ovsdb_file_class); return CONTAINER_OF(replica, struct ovsdb_file, replica); } static bool ovsdb_file_change_cb(const struct ovsdb_row *old, const struct ovsdb_row *new, const unsigned long int *changed, void *ftxn_) { struct ovsdb_file_txn *ftxn = ftxn_; ovsdb_file_txn_add_row(ftxn, old, new, changed); return true; } static struct ovsdb_error * ovsdb_file_commit(struct ovsdb_replica *replica, const struct ovsdb_txn *txn, bool durable) { struct ovsdb_file *file = ovsdb_file_cast(replica); struct ovsdb_file_txn ftxn; struct ovsdb_error *error; ovsdb_file_txn_init(&ftxn); ovsdb_txn_for_each_change(txn, ovsdb_file_change_cb, &ftxn); if (!ftxn.json) { /* Nothing to commit. */ return NULL; } error = ovsdb_file_txn_commit(ftxn.json, ovsdb_txn_get_comment(txn), durable, file->log); if (error) { return error; } file->n_transactions++; /* If it has been at least COMPACT_MIN_MSEC ms since the last time we * compacted (or at least COMPACT_RETRY_MSEC ms since the last time we * tried), and if there are at least 100 transactions in the database, and * if the database is at least 10 MB, then compact the database. */ if (time_msec() >= file->next_compact && file->n_transactions >= 100 && ovsdb_log_get_offset(file->log) >= 10 * 1024 * 1024) { error = ovsdb_file_compact(file); if (error) { char *s = ovsdb_error_to_string(error); ovsdb_error_destroy(error); VLOG_WARN("%s: compacting database failed (%s), retrying in " "%d seconds", file->file_name, s, COMPACT_RETRY_MSEC / 1000); free(s); file->next_compact = time_msec() + COMPACT_RETRY_MSEC; } } return NULL; } struct ovsdb_error * ovsdb_file_compact(struct ovsdb_file *file) { struct ovsdb_log *new_log = NULL; struct lockfile *tmp_lock = NULL; struct ovsdb_error *error; char *tmp_name = NULL; char *comment = NULL; int retval; comment = xasprintf("compacting database online " "(%.3f seconds old, %u transactions, %llu bytes)", (time_wall_msec() - file->last_compact) / 1000.0, file->n_transactions, (unsigned long long) ovsdb_log_get_offset(file->log)); VLOG_INFO("%s: %s", file->file_name, comment); /* Commit the old version, so that we can be assured that we'll eventually * have either the old or the new version. */ error = ovsdb_log_commit(file->log); if (error) { goto exit; } /* Lock temporary file. */ tmp_name = xasprintf("%s.tmp", file->file_name); retval = lockfile_lock(tmp_name, &tmp_lock); if (retval) { error = ovsdb_io_error(retval, "could not get lock on %s", tmp_name); goto exit; } /* Remove temporary file. (It might not exist.) */ if (unlink(tmp_name) < 0 && errno != ENOENT) { error = ovsdb_io_error(errno, "failed to remove %s", tmp_name); goto exit; } /* Save a copy. */ error = ovsdb_file_save_copy__(tmp_name, false, comment, file->db, &new_log); if (error) { goto exit; } /* Replace original by temporary. */ if (rename(tmp_name, file->file_name)) { error = ovsdb_io_error(errno, "failed to rename \"%s\" to \"%s\"", tmp_name, file->file_name); goto exit; } fsync_parent_dir(file->file_name); exit: if (!error) { ovsdb_log_close(file->log); file->log = new_log; file->last_compact = time_msec(); file->next_compact = file->last_compact + COMPACT_MIN_MSEC; file->n_transactions = 1; } else { ovsdb_log_close(new_log); if (tmp_lock) { unlink(tmp_name); } } lockfile_unlock(tmp_lock); free(tmp_name); free(comment); return error; } static void ovsdb_file_destroy(struct ovsdb_replica *replica) { struct ovsdb_file *file = ovsdb_file_cast(replica); ovsdb_log_close(file->log); free(file->file_name); free(file); } static const struct ovsdb_replica_class ovsdb_file_class = { ovsdb_file_commit, ovsdb_file_destroy }; static void ovsdb_file_txn_init(struct ovsdb_file_txn *ftxn) { ftxn->json = NULL; ftxn->table_json = NULL; ftxn->table = NULL; } static void ovsdb_file_txn_add_row(struct ovsdb_file_txn *ftxn, const struct ovsdb_row *old, const struct ovsdb_row *new, const unsigned long int *changed) { struct json *row; if (!new) { row = json_null_create(); } else { struct shash_node *node; row = old ? NULL : json_object_create(); SHASH_FOR_EACH (node, &new->table->schema->columns) { const struct ovsdb_column *column = node->data; const struct ovsdb_type *type = &column->type; unsigned int idx = column->index; if (idx != OVSDB_COL_UUID && column->persistent && (old ? bitmap_is_set(changed, idx) : !ovsdb_datum_is_default(&new->fields[idx], type))) { if (!row) { row = json_object_create(); } json_object_put(row, column->name, ovsdb_datum_to_json(&new->fields[idx], type)); } } } if (row) { struct ovsdb_table *table = new ? new->table : old->table; char uuid[UUID_LEN + 1]; if (table != ftxn->table) { /* Create JSON object for transaction overall. */ if (!ftxn->json) { ftxn->json = json_object_create(); } /* Create JSON object for transaction on this table. */ ftxn->table_json = json_object_create(); ftxn->table = table; json_object_put(ftxn->json, table->schema->name, ftxn->table_json); } /* Add row to transaction for this table. */ snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old))); json_object_put(ftxn->table_json, uuid, row); } } static struct ovsdb_error * ovsdb_file_txn_commit(struct json *json, const char *comment, bool durable, struct ovsdb_log *log) { struct ovsdb_error *error; if (!json) { json = json_object_create(); } if (comment) { json_object_put_string(json, "_comment", comment); } json_object_put(json, "_date", json_integer_create(time_wall_msec())); error = ovsdb_log_write(log, json); json_destroy(json); if (error) { return ovsdb_wrap_error(error, "writing transaction failed"); } if (durable) { error = ovsdb_log_commit(log); if (error) { return ovsdb_wrap_error(error, "committing transaction failed"); } } return NULL; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/column.h0000644000000000000000000000013213534540071016427 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.117686428 30 ctime=1567801425.073855578 openvswitch-2.5.9/ovsdb/column.h0000644000175000017500000000605013534540071020116 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_COLUMN_H #define OVSDB_COLUMN_H 1 #include #include "compiler.h" #include "ovsdb-types.h" struct ovsdb_table; struct ovsdb_table_schema; /* A column or a column schema (currently there is no distinction). */ struct ovsdb_column { unsigned int index; char *name; bool mutable; bool persistent; struct ovsdb_type type; }; /* A few columns appear in every table with standardized column indexes. * These macros define those columns' indexes. * * Don't change these values, because ovsdb_query() depends on OVSDB_COL_UUID * having value 0. */ enum { OVSDB_COL_UUID = 0, /* UUID for the row. */ OVSDB_COL_VERSION = 1, /* Version number for the row. */ OVSDB_N_STD_COLUMNS }; struct ovsdb_column *ovsdb_column_create( const char *name, bool mutable, bool persistent, const struct ovsdb_type *); struct ovsdb_column *ovsdb_column_clone(const struct ovsdb_column *); void ovsdb_column_destroy(struct ovsdb_column *); struct ovsdb_error *ovsdb_column_from_json(const struct json *, const char *name, struct ovsdb_column **) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_column_to_json(const struct ovsdb_column *); /* An unordered set of distinct columns. */ struct ovsdb_column_set { const struct ovsdb_column **columns; size_t n_columns, allocated_columns; }; #define OVSDB_COLUMN_SET_INITIALIZER { NULL, 0, 0 } void ovsdb_column_set_init(struct ovsdb_column_set *); void ovsdb_column_set_destroy(struct ovsdb_column_set *); void ovsdb_column_set_clone(struct ovsdb_column_set *, const struct ovsdb_column_set *); struct ovsdb_error *ovsdb_column_set_from_json( const struct json *, const struct ovsdb_table_schema *, struct ovsdb_column_set *); struct json *ovsdb_column_set_to_json(const struct ovsdb_column_set *); char *ovsdb_column_set_to_string(const struct ovsdb_column_set *); void ovsdb_column_set_add(struct ovsdb_column_set *, const struct ovsdb_column *); void ovsdb_column_set_add_all(struct ovsdb_column_set *, const struct ovsdb_table *); bool ovsdb_column_set_contains(const struct ovsdb_column_set *, unsigned int column_index); bool ovsdb_column_set_equals(const struct ovsdb_column_set *, const struct ovsdb_column_set *); #endif /* column.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017120 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.117686428 30 ctime=1567801424.617852217 openvswitch-2.5.9/ovsdb/automake.mk0000644000175000017500000000573013534540071020613 0ustar00jpettitjpettit00000000000000# libovsdb lib_LTLIBRARIES += ovsdb/libovsdb.la ovsdb_libovsdb_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ovsdb/libovsdb.sym \ $(AM_LDFLAGS) ovsdb_libovsdb_la_SOURCES = \ ovsdb/column.c \ ovsdb/column.h \ ovsdb/condition.c \ ovsdb/condition.h \ ovsdb/execution.c \ ovsdb/file.c \ ovsdb/file.h \ ovsdb/jsonrpc-server.c \ ovsdb/jsonrpc-server.h \ ovsdb/log.c \ ovsdb/log.h \ ovsdb/mutation.c \ ovsdb/mutation.h \ ovsdb/ovsdb.c \ ovsdb/ovsdb.h \ ovsdb/monitor.c \ ovsdb/monitor.h \ ovsdb/query.c \ ovsdb/query.h \ ovsdb/row.c \ ovsdb/row.h \ ovsdb/server.c \ ovsdb/server.h \ ovsdb/table.c \ ovsdb/table.h \ ovsdb/trigger.c \ ovsdb/trigger.h \ ovsdb/transaction.c \ ovsdb/transaction.h ovsdb_libovsdb_la_CFLAGS = $(AM_CFLAGS) ovsdb_libovsdb_la_CPPFLAGS = $(AM_CPPFLAGS) pkgconfig_DATA += \ $(srcdir)/ovsdb/libovsdb.pc MAN_FRAGMENTS += \ ovsdb/remote-active.man \ ovsdb/remote-passive.man # ovsdb-tool bin_PROGRAMS += ovsdb/ovsdb-tool ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la # ovsdb-tool.1 man_MANS += ovsdb/ovsdb-tool.1 DISTCLEANFILES += ovsdb/ovsdb-tool.1 MAN_ROOTS += ovsdb/ovsdb-tool.1.in # ovsdb-client bin_PROGRAMS += ovsdb/ovsdb-client ovsdb_ovsdb_client_SOURCES = ovsdb/ovsdb-client.c ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la # ovsdb-client.1 man_MANS += ovsdb/ovsdb-client.1 DISTCLEANFILES += ovsdb/ovsdb-client.1 MAN_ROOTS += ovsdb/ovsdb-client.1.in # ovsdb-server sbin_PROGRAMS += ovsdb/ovsdb-server ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la # ovsdb-server.1 man_MANS += ovsdb/ovsdb-server.1 DISTCLEANFILES += ovsdb/ovsdb-server.1 MAN_ROOTS += ovsdb/ovsdb-server.1.in # ovsdb-idlc noinst_SCRIPTS += ovsdb/ovsdb-idlc EXTRA_DIST += ovsdb/ovsdb-idlc.in MAN_ROOTS += ovsdb/ovsdb-idlc.1 DISTCLEANFILES += ovsdb/ovsdb-idlc SUFFIXES += .ovsidl .ovsschema OVSDB_IDLC = $(run_python) $(srcdir)/ovsdb/ovsdb-idlc.in .ovsidl.c: $(AM_V_GEN)$(OVSDB_IDLC) c-idl-source $< > $@.tmp && mv $@.tmp $@ .ovsidl.h: $(AM_V_GEN)$(OVSDB_IDLC) c-idl-header $< > $@.tmp && mv $@.tmp $@ BUILT_SOURCES += $(OVSIDL_BUILT) CLEANFILES += $(OVSIDL_BUILT) # This must be done late: macros in targets are expanded when the # target line is read, so if this file were to be included before some # other file that added to OVSIDL_BUILT, then those files wouldn't get # the dependency. # # However, current versions of Automake seem to output all variable # assignments before any targets, so it doesn't seem to be a problem, # at least for now. $(OVSIDL_BUILT): ovsdb/ovsdb-idlc.in # ovsdb-doc EXTRA_DIST += ovsdb/ovsdb-doc OVSDB_DOC = $(run_python) $(srcdir)/ovsdb/ovsdb-doc # ovsdb-dot EXTRA_DIST += ovsdb/ovsdb-dot.in ovsdb/dot2pic noinst_SCRIPTS += ovsdb/ovsdb-dot DISTCLEANFILES += ovsdb/ovsdb-dot OVSDB_DOT = $(run_python) $(srcdir)/ovsdb/ovsdb-dot.in openvswitch-2.5.9/ovsdb/PaxHeaders.82075/table.h0000644000000000000000000000013213534540071016221 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.097855756 openvswitch-2.5.9/ovsdb/table.h0000644000175000017500000000520313534540071017707 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_TABLE_H #define OVSDB_TABLE_H 1 #include #include "compiler.h" #include "hmap.h" #include "shash.h" struct json; struct uuid; /* Schema for a database table. */ struct ovsdb_table_schema { char *name; bool mutable; struct shash columns; /* Contains "struct ovsdb_column *"s. */ unsigned int max_rows; /* Maximum number of rows. */ bool is_root; /* Part of garbage collection root set? */ struct ovsdb_column_set *indexes; size_t n_indexes; }; struct ovsdb_table_schema *ovsdb_table_schema_create( const char *name, bool mutable, unsigned int max_rows, bool is_root); struct ovsdb_table_schema *ovsdb_table_schema_clone( const struct ovsdb_table_schema *); void ovsdb_table_schema_destroy(struct ovsdb_table_schema *); struct ovsdb_error *ovsdb_table_schema_from_json(const struct json *, const char *name, struct ovsdb_table_schema **) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *, bool default_is_root); const struct ovsdb_column *ovsdb_table_schema_get_column( const struct ovsdb_table_schema *, const char *name); /* Database table. */ struct ovsdb_table { struct ovsdb_table_schema *schema; struct ovsdb_txn_table *txn_table; /* Only if table is in a transaction. */ struct hmap rows; /* Contains "struct ovsdb_row"s. */ /* An array of schema->n_indexes hmaps, each of which contains "struct * ovsdb_row"s. Each of the hmap_nodes in indexes[i] are at index 'i' at * the end of struct ovsdb_row, following the 'fields' member. */ struct hmap *indexes; }; struct ovsdb_table *ovsdb_table_create(struct ovsdb_table_schema *); void ovsdb_table_destroy(struct ovsdb_table *); const struct ovsdb_row *ovsdb_table_get_row(const struct ovsdb_table *, const struct uuid *); #endif /* ovsdb/table.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/mutation.c0000644000000000000000000000013213534540071016765 xustar0030 mtime=1567801401.809684167 30 atime=1567801402.121686458 30 ctime=1567801425.085855667 openvswitch-2.5.9/ovsdb/mutation.c0000644000175000017500000003402213534540071020454 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "mutation.h" #include #include #include "column.h" #include "ovsdb-error.h" #include "json.h" #include "row.h" #include "table.h" struct ovsdb_error * ovsdb_mutator_from_string(const char *name, enum ovsdb_mutator *mutator) { #define OVSDB_MUTATOR(ENUM, NAME) \ if (!strcmp(name, NAME)) { \ *mutator = ENUM; \ return NULL; \ } OVSDB_MUTATORS; #undef OVSDB_MUTATOR return ovsdb_syntax_error(NULL, "unknown mutator", "No mutator named %s.", name); } const char * ovsdb_mutator_to_string(enum ovsdb_mutator mutator) { switch (mutator) { #define OVSDB_MUTATOR(ENUM, NAME) case ENUM: return NAME; OVSDB_MUTATORS; #undef OVSDB_MUTATOR } return NULL; } static OVS_WARN_UNUSED_RESULT struct ovsdb_error * type_mismatch(const struct ovsdb_mutation *m, const struct json *json) { struct ovsdb_error *error; char *s; s = ovsdb_type_to_english(&m->column->type); error = ovsdb_syntax_error( json, NULL, "Type mismatch: \"%s\" operator may not be " "applied to column %s of type %s.", ovsdb_mutator_to_string(m->mutator), m->column->name, s); free(s); return error; } static OVS_WARN_UNUSED_RESULT struct ovsdb_error * ovsdb_mutation_from_json(const struct ovsdb_table_schema *ts, const struct json *json, struct ovsdb_symbol_table *symtab, struct ovsdb_mutation *m) { const struct json_array *array; struct ovsdb_error *error; const char *mutator_name; const char *column_name; if (json->type != JSON_ARRAY || json->u.array.n != 3 || json->u.array.elems[0]->type != JSON_STRING || json->u.array.elems[1]->type != JSON_STRING) { return ovsdb_syntax_error(json, NULL, "Parse error in mutation."); } array = json_array(json); column_name = json_string(array->elems[0]); m->column = ovsdb_table_schema_get_column(ts, column_name); if (!m->column) { return ovsdb_syntax_error(json, "unknown column", "No column %s in table %s.", column_name, ts->name); } if (!m->column->mutable) { return ovsdb_syntax_error(json, "constraint violation", "Cannot mutate immutable column %s in " "table %s.", column_name, ts->name); } ovsdb_type_clone(&m->type, &m->column->type); mutator_name = json_string(array->elems[1]); error = ovsdb_mutator_from_string(mutator_name, &m->mutator); if (error) { goto exit; } /* Type-check and relax restrictions on 'type' if appropriate. */ switch (m->mutator) { case OVSDB_M_ADD: case OVSDB_M_SUB: case OVSDB_M_MUL: case OVSDB_M_DIV: case OVSDB_M_MOD: if ((!ovsdb_type_is_scalar(&m->type) && !ovsdb_type_is_set(&m->type)) || (m->type.key.type != OVSDB_TYPE_INTEGER && m->type.key.type != OVSDB_TYPE_REAL) || (m->mutator == OVSDB_M_MOD && m->type.key.type == OVSDB_TYPE_REAL)) { return type_mismatch(m, json); } ovsdb_base_type_clear_constraints(&m->type.key); m->type.n_min = m->type.n_max = 1; error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2], symtab); break; case OVSDB_M_INSERT: case OVSDB_M_DELETE: if (!ovsdb_type_is_set(&m->type) && !ovsdb_type_is_map(&m->type)) { return type_mismatch(m, json); } m->type.n_min = 0; if (m->mutator == OVSDB_M_DELETE) { m->type.n_max = UINT_MAX; } error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2], symtab); if (error && ovsdb_type_is_map(&m->type) && m->mutator == OVSDB_M_DELETE) { ovsdb_error_destroy(error); m->type.value.type = OVSDB_TYPE_VOID; error = ovsdb_datum_from_json(&m->arg, &m->type, array->elems[2], symtab); } break; default: OVS_NOT_REACHED(); } exit: if (error) { ovsdb_type_destroy(&m->type); } return error; } static void ovsdb_mutation_free(struct ovsdb_mutation *m) { ovsdb_datum_destroy(&m->arg, &m->type); ovsdb_type_destroy(&m->type); } struct ovsdb_error * ovsdb_mutation_set_from_json(const struct ovsdb_table_schema *ts, const struct json *json, struct ovsdb_symbol_table *symtab, struct ovsdb_mutation_set *set) { const struct json_array *array = json_array(json); size_t i; set->mutations = xmalloc(array->n * sizeof *set->mutations); set->n_mutations = 0; for (i = 0; i < array->n; i++) { struct ovsdb_error *error; error = ovsdb_mutation_from_json(ts, array->elems[i], symtab, &set->mutations[i]); if (error) { ovsdb_mutation_set_destroy(set); set->mutations = NULL; set->n_mutations = 0; return error; } set->n_mutations++; } return NULL; } static struct json * ovsdb_mutation_to_json(const struct ovsdb_mutation *m) { return json_array_create_3( json_string_create(m->column->name), json_string_create(ovsdb_mutator_to_string(m->mutator)), ovsdb_datum_to_json(&m->arg, &m->type)); } struct json * ovsdb_mutation_set_to_json(const struct ovsdb_mutation_set *set) { struct json **mutations; size_t i; mutations = xmalloc(set->n_mutations * sizeof *mutations); for (i = 0; i < set->n_mutations; i++) { mutations[i] = ovsdb_mutation_to_json(&set->mutations[i]); } return json_array_create(mutations, set->n_mutations); } void ovsdb_mutation_set_destroy(struct ovsdb_mutation_set *set) { size_t i; for (i = 0; i < set->n_mutations; i++) { ovsdb_mutation_free(&set->mutations[i]); } free(set->mutations); } enum ovsdb_mutation_scalar_error { ME_OK, ME_DOM, ME_RANGE }; struct ovsdb_scalar_mutation { int (*mutate_integer)(int64_t *x, int64_t y); int (*mutate_real)(double *x, double y); enum ovsdb_mutator mutator; }; static const struct ovsdb_scalar_mutation add_mutation; static const struct ovsdb_scalar_mutation sub_mutation; static const struct ovsdb_scalar_mutation mul_mutation; static const struct ovsdb_scalar_mutation div_mutation; static const struct ovsdb_scalar_mutation mod_mutation; static struct ovsdb_error * ovsdb_mutation_scalar_error(enum ovsdb_mutation_scalar_error error, enum ovsdb_mutator mutator) { switch (error) { case ME_OK: return OVSDB_BUG("unexpected success"); case ME_DOM: return ovsdb_error("domain error", "Division by zero."); case ME_RANGE: return ovsdb_error("range error", "Result of \"%s\" operation is out of range.", ovsdb_mutator_to_string(mutator)); default: return OVSDB_BUG("unexpected error"); } } static int check_real_range(double x) { return x >= -DBL_MAX && x <= DBL_MAX ? 0 : ME_RANGE; } static struct ovsdb_error * mutate_scalar(const struct ovsdb_type *dst_type, struct ovsdb_datum *dst, const union ovsdb_atom *arg, const struct ovsdb_scalar_mutation *mutation) { const struct ovsdb_base_type *base = &dst_type->key; struct ovsdb_error *error; unsigned int i; if (base->type == OVSDB_TYPE_INTEGER) { int64_t y = arg->integer; for (i = 0; i < dst->n; i++) { enum ovsdb_mutation_scalar_error me; me = (mutation->mutate_integer)(&dst->keys[i].integer, y); if (me != ME_OK) { return ovsdb_mutation_scalar_error(me, mutation->mutator); } } } else if (base->type == OVSDB_TYPE_REAL) { double y = arg->real; for (i = 0; i < dst->n; i++) { double *x = &dst->keys[i].real; enum ovsdb_mutation_scalar_error me; me = (mutation->mutate_real)(x, y); if (me == ME_OK) { me = check_real_range(*x); } if (me != ME_OK) { return ovsdb_mutation_scalar_error(me, mutation->mutator); } } } else { OVS_NOT_REACHED(); } for (i = 0; i < dst->n; i++) { error = ovsdb_atom_check_constraints(&dst->keys[i], base); if (error) { return error; } } error = ovsdb_datum_sort(dst, dst_type->key.type); if (error) { ovsdb_error_destroy(error); return ovsdb_error("constraint violation", "Result of \"%s\" operation contains duplicates.", ovsdb_mutator_to_string(mutation->mutator)); } return NULL; } static struct ovsdb_error * ovsdb_mutation_check_count(struct ovsdb_datum *dst, const struct ovsdb_type *dst_type) { if (!ovsdb_datum_conforms_to_type(dst, dst_type)) { char *s = ovsdb_type_to_english(dst_type); struct ovsdb_error *e = ovsdb_error( "constraint violation", "Attempted to store %u elements in %s.", dst->n, s); free(s); return e; } return NULL; } struct ovsdb_error * ovsdb_mutation_set_execute(struct ovsdb_row *row, const struct ovsdb_mutation_set *set) { size_t i; for (i = 0; i < set->n_mutations; i++) { const struct ovsdb_mutation *m = &set->mutations[i]; struct ovsdb_datum *dst = &row->fields[m->column->index]; const struct ovsdb_type *dst_type = &m->column->type; const struct ovsdb_datum *arg = &set->mutations[i].arg; const struct ovsdb_type *arg_type = &m->type; struct ovsdb_error *error; switch (m->mutator) { case OVSDB_M_ADD: error = mutate_scalar(dst_type, dst, &arg->keys[0], &add_mutation); break; case OVSDB_M_SUB: error = mutate_scalar(dst_type, dst, &arg->keys[0], &sub_mutation); break; case OVSDB_M_MUL: error = mutate_scalar(dst_type, dst, &arg->keys[0], &mul_mutation); break; case OVSDB_M_DIV: error = mutate_scalar(dst_type, dst, &arg->keys[0], &div_mutation); break; case OVSDB_M_MOD: error = mutate_scalar(dst_type, dst, &arg->keys[0], &mod_mutation); break; case OVSDB_M_INSERT: ovsdb_datum_union(dst, arg, dst_type, false); error = ovsdb_mutation_check_count(dst, dst_type); break; case OVSDB_M_DELETE: ovsdb_datum_subtract(dst, dst_type, arg, arg_type); error = ovsdb_mutation_check_count(dst, dst_type); break; default: OVS_NOT_REACHED(); } if (error) { return error; } } return NULL; } static int add_int(int64_t *x, int64_t y) { /* Check for overflow. See _Hacker's Delight_ pp. 27. */ int64_t z = ~(*x ^ y) & INT64_MIN; if ((~(*x ^ y) & ~(((*x ^ z) + y) ^ y)) >> 63) { return ME_RANGE; } else { *x += y; return 0; } } static int sub_int(int64_t *x, int64_t y) { /* Check for overflow. See _Hacker's Delight_ pp. 27. */ int64_t z = (*x ^ y) & INT64_MIN; if (((*x ^ y) & (((*x ^ z) - y) ^ y)) >> 63) { return ME_RANGE; } else { *x -= y; return 0; } } static int mul_int(int64_t *x, int64_t y) { /* Check for overflow. See _Hacker's Delight_ pp. 30. */ if (*x > 0 ? (y > 0 ? *x >= INT64_MAX / y : y < INT64_MIN / *x) : (y > 0 ? *x < INT64_MIN / y : *x != 0 && y < INT64_MAX / y)) { return ME_RANGE; } else { *x *= y; return 0; } } static int check_int_div(int64_t x, int64_t y) { /* Check for overflow. See _Hacker's Delight_ pp. 32. */ if (!y) { return ME_DOM; } else if (x == INT64_MIN && y == -1) { return ME_RANGE; } else { return 0; } } static int div_int(int64_t *x, int64_t y) { int error = check_int_div(*x, y); if (!error) { *x /= y; } return error; } static int mod_int(int64_t *x, int64_t y) { int error = check_int_div(*x, y); if (!error) { *x %= y; } return error; } static int add_double(double *x, double y) { *x += y; return 0; } static int sub_double(double *x, double y) { *x -= y; return 0; } static int mul_double(double *x, double y) { *x *= y; return 0; } static int div_double(double *x, double y) { if (y == 0) { return ME_DOM; } else { *x /= y; return 0; } } static const struct ovsdb_scalar_mutation add_mutation = { add_int, add_double, OVSDB_M_ADD }; static const struct ovsdb_scalar_mutation sub_mutation = { sub_int, sub_double, OVSDB_M_SUB }; static const struct ovsdb_scalar_mutation mul_mutation = { mul_int, mul_double, OVSDB_M_MUL }; static const struct ovsdb_scalar_mutation div_mutation = { div_int, div_double, OVSDB_M_DIV }; static const struct ovsdb_scalar_mutation mod_mutation = { mod_int, NULL, OVSDB_M_MOD }; openvswitch-2.5.9/ovsdb/PaxHeaders.82075/remote-passive.man0000644000000000000000000000013213534540071020421 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801423.757845878 openvswitch-2.5.9/ovsdb/remote-passive.man0000644000175000017500000000231113534540071022104 0ustar00jpettitjpettit00000000000000.IP "\fBpssl:\fIport\fR[\fB:\fIip\fR]" Listen on the given SSL \fIport\fR for a connection. By default, connections are not bound to a particular local IP address and it listens only on IPv4 (but not IPv6) addresses, but specifying \fIip\fR limits connections to those from the given \fIip\fR, either IPv4 or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square brackets, e.g.: \fBpssl:6640:[::1]\fR. The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory when this form is used. . .IP "\fBptcp:\fIport\fR[\fB:\fIip\fR]" Listen on the given TCP \fIport\fR for a connection. By default, connections are not bound to a particular local IP address and it listens only on IPv4 (but not IPv6) addresses, but \fIip\fR may be specified to listen only for connections to the given \fIip\fR, either IPv4 or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square brackets, e.g.: \fBptcp:6640:[::1]\fR. . .IP "\fBpunix:\fIfile\fR" On POSIX, listen on the Unix domain server socket named \fIfile\fR for a connection. .IP On Windows, listen on a kernel chosen TCP port on the localhost. The kernel chosen TCP port value is written in \fIfile\fR. openvswitch-2.5.9/ovsdb/PaxHeaders.82075/log.h0000644000000000000000000000013213534540071015713 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801425.085855667 openvswitch-2.5.9/ovsdb/log.h0000644000175000017500000000315013534540071017400 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_LOG_H #define OVSDB_LOG_H 1 #include #include "compiler.h" struct json; struct ovsdb_log; /* Access mode for opening an OVSDB log. */ enum ovsdb_log_open_mode { OVSDB_LOG_READ_ONLY, /* Open existing file, read-only. */ OVSDB_LOG_READ_WRITE, /* Open existing file, read/write. */ OVSDB_LOG_CREATE /* Create new file, read/write. */ }; struct ovsdb_error *ovsdb_log_open(const char *name, enum ovsdb_log_open_mode, int locking, struct ovsdb_log **) OVS_WARN_UNUSED_RESULT; void ovsdb_log_close(struct ovsdb_log *); struct ovsdb_error *ovsdb_log_read(struct ovsdb_log *, struct json **) OVS_WARN_UNUSED_RESULT; void ovsdb_log_unread(struct ovsdb_log *); struct ovsdb_error *ovsdb_log_write(struct ovsdb_log *, struct json *) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_log_commit(struct ovsdb_log *) OVS_WARN_UNUSED_RESULT; off_t ovsdb_log_get_offset(const struct ovsdb_log *); #endif /* ovsdb/log.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/jsonrpc-server.c0000644000000000000000000000013213534540071020107 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801425.081855638 openvswitch-2.5.9/ovsdb/jsonrpc-server.c0000644000175000017500000012546413534540071021611 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "jsonrpc-server.h" #include #include "bitmap.h" #include "column.h" #include "dynamic-string.h" #include "json.h" #include "jsonrpc.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb.h" #include "poll-loop.h" #include "reconnect.h" #include "row.h" #include "server.h" #include "simap.h" #include "stream.h" #include "table.h" #include "timeval.h" #include "transaction.h" #include "trigger.h" #include "monitor.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_jsonrpc_server); struct ovsdb_jsonrpc_remote; struct ovsdb_jsonrpc_session; /* Message rate-limiting. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); /* Sessions. */ static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create( struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *); static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_get_memory_usage_all( const struct ovsdb_jsonrpc_remote *, struct simap *usage); static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *); static void ovsdb_jsonrpc_session_set_all_options( struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *); static bool ovsdb_jsonrpc_session_get_status( const struct ovsdb_jsonrpc_remote *, struct ovsdb_jsonrpc_remote_status *); static void ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *); static void ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *); static void ovsdb_jsonrpc_session_send(struct ovsdb_jsonrpc_session *, struct jsonrpc_msg *); /* Triggers. */ static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *, struct ovsdb *, struct json *id, struct json *params); static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find( struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash); static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *); static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *); static void ovsdb_jsonrpc_trigger_complete_done( struct ovsdb_jsonrpc_session *); /* Monitors. */ static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_create( struct ovsdb_jsonrpc_session *, struct ovsdb *, struct json *params, const struct json *request_id); static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel( struct ovsdb_jsonrpc_session *, struct json_array *params, const struct json *request_id); static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *); static void ovsdb_jsonrpc_monitor_flush_all(struct ovsdb_jsonrpc_session *); static bool ovsdb_jsonrpc_monitor_needs_flush(struct ovsdb_jsonrpc_session *); static struct json *ovsdb_jsonrpc_monitor_compose_update( struct ovsdb_jsonrpc_monitor *monitor, bool initial); /* JSON-RPC database server. */ struct ovsdb_jsonrpc_server { struct ovsdb_server up; unsigned int n_sessions, max_sessions; struct shash remotes; /* Contains "struct ovsdb_jsonrpc_remote *"s. */ }; /* A configured remote. This is either a passive stream listener plus a list * of the currently connected sessions, or a list of exactly one active * session. */ struct ovsdb_jsonrpc_remote { struct ovsdb_jsonrpc_server *server; struct pstream *listener; /* Listener, if passive. */ struct ovs_list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */ uint8_t dscp; }; static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote( struct ovsdb_jsonrpc_server *, const char *name, const struct ovsdb_jsonrpc_options *options ); static void ovsdb_jsonrpc_server_del_remote(struct shash_node *); /* Creates and returns a new server to provide JSON-RPC access to an OVSDB. * * The caller must call ovsdb_jsonrpc_server_add_db() for each database to * which 'server' should provide access. */ struct ovsdb_jsonrpc_server * ovsdb_jsonrpc_server_create(void) { struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server); ovsdb_server_init(&server->up); server->max_sessions = 330; /* Random limit. */ shash_init(&server->remotes); return server; } /* Adds 'db' to the set of databases served out by 'svr'. Returns true if * successful, false if 'db''s name is the same as some database already in * 'server'. */ bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db) { /* The OVSDB protocol doesn't have a way to notify a client that a * database has been added. If some client tried to use the database * that we're adding and failed, then forcing it to reconnect seems like * a reasonable way to make it try again. * * If this is too big of a hammer in practice, we could be more selective, * e.g. disconnect only connections that actually tried to use a database * with 'db''s name. */ ovsdb_jsonrpc_server_reconnect(svr); return ovsdb_server_add_db(&svr->up, db); } /* Removes 'db' from the set of databases served out by 'svr'. Returns * true if successful, false if there is no database associated with 'db'. */ bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db) { /* There might be pointers to 'db' from 'svr', such as monitors or * outstanding transactions. Disconnect all JSON-RPC connections to avoid * accesses to freed memory. * * If this is too big of a hammer in practice, we could be more selective, * e.g. disconnect only connections that actually reference 'db'. */ ovsdb_jsonrpc_server_reconnect(svr); return ovsdb_server_remove_db(&svr->up, db); } void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { ovsdb_jsonrpc_server_del_remote(node); } shash_destroy(&svr->remotes); ovsdb_server_destroy(&svr->up); free(svr); } struct ovsdb_jsonrpc_options * ovsdb_jsonrpc_default_options(const char *target) { struct ovsdb_jsonrpc_options *options = xzalloc(sizeof *options); options->max_backoff = RECONNECT_DEFAULT_MAX_BACKOFF; options->probe_interval = (stream_or_pstream_needs_probes(target) ? RECONNECT_DEFAULT_PROBE_INTERVAL : 0); return options; } /* Sets 'svr''s current set of remotes to the names in 'new_remotes', with * options in the struct ovsdb_jsonrpc_options supplied as the data values. * * A remote is an active or passive stream connection method, e.g. "pssl:" or * "tcp:1.2.3.4". */ void ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr, const struct shash *new_remotes) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { struct ovsdb_jsonrpc_remote *remote = node->data; struct ovsdb_jsonrpc_options *options = shash_find_data(new_remotes, node->name); if (!options) { VLOG_INFO("%s: remote deconfigured", node->name); ovsdb_jsonrpc_server_del_remote(node); } else if (options->dscp != remote->dscp) { ovsdb_jsonrpc_server_del_remote(node); } } SHASH_FOR_EACH (node, new_remotes) { const struct ovsdb_jsonrpc_options *options = node->data; struct ovsdb_jsonrpc_remote *remote; remote = shash_find_data(&svr->remotes, node->name); if (!remote) { remote = ovsdb_jsonrpc_server_add_remote(svr, node->name, options); if (!remote) { continue; } } ovsdb_jsonrpc_session_set_all_options(remote, options); } } static struct ovsdb_jsonrpc_remote * ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr, const char *name, const struct ovsdb_jsonrpc_options *options) { struct ovsdb_jsonrpc_remote *remote; struct pstream *listener; int error; error = jsonrpc_pstream_open(name, &listener, options->dscp); if (error && error != EAFNOSUPPORT) { VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, ovs_strerror(error)); return NULL; } remote = xmalloc(sizeof *remote); remote->server = svr; remote->listener = listener; list_init(&remote->sessions); remote->dscp = options->dscp; shash_add(&svr->remotes, name, remote); if (!listener) { ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name, true)); } return remote; } static void ovsdb_jsonrpc_server_del_remote(struct shash_node *node) { struct ovsdb_jsonrpc_remote *remote = node->data; ovsdb_jsonrpc_session_close_all(remote); pstream_close(remote->listener); shash_delete(&remote->server->remotes, node); free(remote); } /* Stores status information for the remote named 'target', which should have * been configured on 'svr' with a call to ovsdb_jsonrpc_server_set_remotes(), * into '*status'. On success returns true, on failure (if 'svr' doesn't have * a remote named 'target' or if that remote is an inbound remote that has no * active connections) returns false. On failure, 'status' will be zeroed. */ bool ovsdb_jsonrpc_server_get_remote_status( const struct ovsdb_jsonrpc_server *svr, const char *target, struct ovsdb_jsonrpc_remote_status *status) { const struct ovsdb_jsonrpc_remote *remote; memset(status, 0, sizeof *status); remote = shash_find_data(&svr->remotes, target); return remote && ovsdb_jsonrpc_session_get_status(remote, status); } void ovsdb_jsonrpc_server_free_remote_status( struct ovsdb_jsonrpc_remote_status *status) { free(status->locks_held); free(status->locks_waiting); free(status->locks_lost); } /* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and * reconnect. */ void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr) { struct shash_node *node; SHASH_FOR_EACH (node, &svr->remotes) { struct ovsdb_jsonrpc_remote *remote = node->data; ovsdb_jsonrpc_session_reconnect_all(remote); } } void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr) { struct shash_node *node; SHASH_FOR_EACH (node, &svr->remotes) { struct ovsdb_jsonrpc_remote *remote = node->data; if (remote->listener) { if (svr->n_sessions < svr->max_sessions) { struct stream *stream; int error; error = pstream_accept(remote->listener, &stream); if (!error) { struct jsonrpc_session *js; js = jsonrpc_session_open_unreliably(jsonrpc_open(stream), remote->dscp); ovsdb_jsonrpc_session_create(remote, js); } else if (error != EAGAIN) { VLOG_WARN_RL(&rl, "%s: accept failed: %s", pstream_get_name(remote->listener), ovs_strerror(error)); } } else { VLOG_WARN_RL(&rl, "%s: connection exceeded maximum (%d)", pstream_get_name(remote->listener), svr->max_sessions); } } ovsdb_jsonrpc_session_run_all(remote); } } void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr) { struct shash_node *node; SHASH_FOR_EACH (node, &svr->remotes) { struct ovsdb_jsonrpc_remote *remote = node->data; if (remote->listener && svr->n_sessions < svr->max_sessions) { pstream_wait(remote->listener); } ovsdb_jsonrpc_session_wait_all(remote); } } /* Adds some memory usage statistics for 'svr' into 'usage', for use with * memory_report(). */ void ovsdb_jsonrpc_server_get_memory_usage(const struct ovsdb_jsonrpc_server *svr, struct simap *usage) { struct shash_node *node; simap_increase(usage, "sessions", svr->n_sessions); SHASH_FOR_EACH (node, &svr->remotes) { struct ovsdb_jsonrpc_remote *remote = node->data; ovsdb_jsonrpc_session_get_memory_usage_all(remote, usage); } } /* JSON-RPC database server session. */ struct ovsdb_jsonrpc_session { struct ovs_list node; /* Element in remote's sessions list. */ struct ovsdb_session up; struct ovsdb_jsonrpc_remote *remote; /* Triggers. */ struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */ /* Monitors. */ struct hmap monitors; /* Hmap of "struct ovsdb_jsonrpc_monitor"s. */ /* Network connectivity. */ struct jsonrpc_session *js; /* JSON-RPC session. */ unsigned int js_seqno; /* Last jsonrpc_session_get_seqno() value. */ }; static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *); static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *); static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *); static void ovsdb_jsonrpc_session_get_memory_usage( const struct ovsdb_jsonrpc_session *, struct simap *usage); static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *, struct jsonrpc_msg *); static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *, struct jsonrpc_msg *); static struct ovsdb_jsonrpc_session * ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote, struct jsonrpc_session *js) { struct ovsdb_jsonrpc_session *s; s = xzalloc(sizeof *s); ovsdb_session_init(&s->up, &remote->server->up); s->remote = remote; list_push_back(&remote->sessions, &s->node); hmap_init(&s->triggers); hmap_init(&s->monitors); s->js = js; s->js_seqno = jsonrpc_session_get_seqno(js); remote->server->n_sessions++; return s; } static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) { ovsdb_jsonrpc_monitor_remove_all(s); ovsdb_jsonrpc_session_unlock_all(s); ovsdb_jsonrpc_trigger_complete_all(s); hmap_destroy(&s->monitors); hmap_destroy(&s->triggers); jsonrpc_session_close(s->js); list_remove(&s->node); s->remote->server->n_sessions--; ovsdb_session_destroy(&s->up); free(s); } static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) { jsonrpc_session_run(s->js); if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) { s->js_seqno = jsonrpc_session_get_seqno(s->js); ovsdb_jsonrpc_trigger_complete_all(s); ovsdb_jsonrpc_monitor_remove_all(s); ovsdb_jsonrpc_session_unlock_all(s); } ovsdb_jsonrpc_trigger_complete_done(s); if (!jsonrpc_session_get_backlog(s->js)) { struct jsonrpc_msg *msg; ovsdb_jsonrpc_monitor_flush_all(s); msg = jsonrpc_session_recv(s->js); if (msg) { if (msg->type == JSONRPC_REQUEST) { ovsdb_jsonrpc_session_got_request(s, msg); } else if (msg->type == JSONRPC_NOTIFY) { ovsdb_jsonrpc_session_got_notify(s, msg); } else { VLOG_WARN("%s: received unexpected %s message", jsonrpc_session_get_name(s->js), jsonrpc_msg_type_to_string(msg->type)); jsonrpc_session_force_reconnect(s->js); jsonrpc_msg_destroy(msg); } } } return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT; } static void ovsdb_jsonrpc_session_set_options(struct ovsdb_jsonrpc_session *session, const struct ovsdb_jsonrpc_options *options) { jsonrpc_session_set_max_backoff(session->js, options->max_backoff); jsonrpc_session_set_probe_interval(session->js, options->probe_interval); jsonrpc_session_set_dscp(session->js, options->dscp); } static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *remote) { struct ovsdb_jsonrpc_session *s, *next; LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) { int error = ovsdb_jsonrpc_session_run(s); if (error) { ovsdb_jsonrpc_session_close(s); } } } static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *s) { jsonrpc_session_wait(s->js); if (!jsonrpc_session_get_backlog(s->js)) { if (ovsdb_jsonrpc_monitor_needs_flush(s)) { poll_immediate_wake(); } else { jsonrpc_session_recv_wait(s->js); } } } static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *remote) { struct ovsdb_jsonrpc_session *s; LIST_FOR_EACH (s, node, &remote->sessions) { ovsdb_jsonrpc_session_wait(s); } } static void ovsdb_jsonrpc_session_get_memory_usage(const struct ovsdb_jsonrpc_session *s, struct simap *usage) { simap_increase(usage, "triggers", hmap_count(&s->triggers)); simap_increase(usage, "monitors", hmap_count(&s->monitors)); simap_increase(usage, "backlog", jsonrpc_session_get_backlog(s->js)); } static void ovsdb_jsonrpc_session_get_memory_usage_all( const struct ovsdb_jsonrpc_remote *remote, struct simap *usage) { struct ovsdb_jsonrpc_session *s; LIST_FOR_EACH (s, node, &remote->sessions) { ovsdb_jsonrpc_session_get_memory_usage(s, usage); } } static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote) { struct ovsdb_jsonrpc_session *s, *next; LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) { ovsdb_jsonrpc_session_close(s); } } /* Forces all of the JSON-RPC sessions managed by 'remote' to disconnect and * reconnect. */ static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote) { struct ovsdb_jsonrpc_session *s, *next; LIST_FOR_EACH_SAFE (s, next, node, &remote->sessions) { jsonrpc_session_force_reconnect(s->js); if (!jsonrpc_session_is_alive(s->js)) { ovsdb_jsonrpc_session_close(s); } } } /* Sets the options for all of the JSON-RPC sessions managed by 'remote' to * 'options'. * * (The dscp value can't be changed directly; the caller must instead close and * re-open the session.) */ static void ovsdb_jsonrpc_session_set_all_options( struct ovsdb_jsonrpc_remote *remote, const struct ovsdb_jsonrpc_options *options) { struct ovsdb_jsonrpc_session *s; LIST_FOR_EACH (s, node, &remote->sessions) { ovsdb_jsonrpc_session_set_options(s, options); } } static bool ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote, struct ovsdb_jsonrpc_remote_status *status) { const struct ovsdb_jsonrpc_session *s; const struct jsonrpc_session *js; struct ovsdb_lock_waiter *waiter; struct reconnect_stats rstats; struct ds locks_held, locks_waiting, locks_lost; status->bound_port = (remote->listener ? pstream_get_bound_port(remote->listener) : htons(0)); if (list_is_empty(&remote->sessions)) { return false; } s = CONTAINER_OF(remote->sessions.next, struct ovsdb_jsonrpc_session, node); js = s->js; status->is_connected = jsonrpc_session_is_connected(js); status->last_error = jsonrpc_session_get_status(js); jsonrpc_session_get_reconnect_stats(js, &rstats); status->state = rstats.state; status->sec_since_connect = rstats.msec_since_connect == UINT_MAX ? UINT_MAX : rstats.msec_since_connect / 1000; status->sec_since_disconnect = rstats.msec_since_disconnect == UINT_MAX ? UINT_MAX : rstats.msec_since_disconnect / 1000; ds_init(&locks_held); ds_init(&locks_waiting); ds_init(&locks_lost); HMAP_FOR_EACH (waiter, session_node, &s->up.waiters) { struct ds *string; string = (ovsdb_lock_waiter_is_owner(waiter) ? &locks_held : waiter->mode == OVSDB_LOCK_WAIT ? &locks_waiting : &locks_lost); if (string->length) { ds_put_char(string, ' '); } ds_put_cstr(string, waiter->lock_name); } status->locks_held = ds_steal_cstr(&locks_held); status->locks_waiting = ds_steal_cstr(&locks_waiting); status->locks_lost = ds_steal_cstr(&locks_lost); status->n_connections = list_size(&remote->sessions); return true; } /* Examines 'request' to determine the database to which it relates, and then * searches 's' to find that database: * * - If successful, returns the database and sets '*replyp' to NULL. * * - If no such database exists, returns NULL and sets '*replyp' to an * appropriate JSON-RPC error reply, owned by the caller. */ static struct ovsdb * ovsdb_jsonrpc_lookup_db(const struct ovsdb_jsonrpc_session *s, const struct jsonrpc_msg *request, struct jsonrpc_msg **replyp) { struct json_array *params; struct ovsdb_error *error; const char *db_name; struct ovsdb *db; params = json_array(request->params); if (!params->n || params->elems[0]->type != JSON_STRING) { error = ovsdb_syntax_error( request->params, NULL, "%s request params must begin with ", request->method); goto error; } db_name = params->elems[0]->u.string; db = shash_find_data(&s->up.server->dbs, db_name); if (!db) { error = ovsdb_syntax_error( request->params, "unknown database", "%s request specifies unknown database %s", request->method, db_name); goto error; } *replyp = NULL; return db; error: *replyp = jsonrpc_create_error(ovsdb_error_to_json(error), request->id); ovsdb_error_destroy(error); return NULL; } static struct ovsdb_error * ovsdb_jsonrpc_session_parse_lock_name(const struct jsonrpc_msg *request, const char **lock_namep) { const struct json_array *params; params = json_array(request->params); if (params->n != 1 || params->elems[0]->type != JSON_STRING || !ovsdb_parser_is_id(json_string(params->elems[0]))) { *lock_namep = NULL; return ovsdb_syntax_error(request->params, NULL, "%s request params must be ", request->method); } *lock_namep = json_string(params->elems[0]); return NULL; } static void ovsdb_jsonrpc_session_notify(struct ovsdb_session *session, const char *lock_name, const char *method) { struct ovsdb_jsonrpc_session *s; struct json *params; s = CONTAINER_OF(session, struct ovsdb_jsonrpc_session, up); params = json_array_create_1(json_string_create(lock_name)); ovsdb_jsonrpc_session_send(s, jsonrpc_create_notify(method, params)); } static struct jsonrpc_msg * ovsdb_jsonrpc_session_lock(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request, enum ovsdb_lock_mode mode) { struct ovsdb_lock_waiter *waiter; struct jsonrpc_msg *reply; struct ovsdb_error *error; struct ovsdb_session *victim; const char *lock_name; struct json *result; error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name); if (error) { goto error; } /* Report error if this session has issued a "lock" or "steal" without a * matching "unlock" for this lock. */ waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name); if (waiter) { error = ovsdb_syntax_error( request->params, NULL, "must issue \"unlock\" before new \"%s\"", request->method); goto error; } /* Get the lock, add us as a waiter. */ waiter = ovsdb_server_lock(&s->remote->server->up, &s->up, lock_name, mode, &victim); if (victim) { ovsdb_jsonrpc_session_notify(victim, lock_name, "stolen"); } result = json_object_create(); json_object_put(result, "locked", json_boolean_create(ovsdb_lock_waiter_is_owner(waiter))); return jsonrpc_create_reply(result, request->id); error: reply = jsonrpc_create_error(ovsdb_error_to_json(error), request->id); ovsdb_error_destroy(error); return reply; } static void ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *s) { struct ovsdb_lock_waiter *waiter, *next; HMAP_FOR_EACH_SAFE (waiter, next, session_node, &s->up.waiters) { ovsdb_jsonrpc_session_unlock__(waiter); } } static void ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *waiter) { struct ovsdb_lock *lock = waiter->lock; if (lock) { struct ovsdb_session *new_owner = ovsdb_lock_waiter_remove(waiter); if (new_owner) { ovsdb_jsonrpc_session_notify(new_owner, lock->name, "locked"); } else { /* ovsdb_server_lock() might have freed 'lock'. */ } } ovsdb_lock_waiter_destroy(waiter); } static struct jsonrpc_msg * ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) { struct ovsdb_lock_waiter *waiter; struct jsonrpc_msg *reply; struct ovsdb_error *error; const char *lock_name; error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name); if (error) { goto error; } /* Report error if this session has not issued a "lock" or "steal" for this * lock. */ waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name); if (!waiter) { error = ovsdb_syntax_error( request->params, NULL, "\"unlock\" without \"lock\" or \"steal\""); goto error; } ovsdb_jsonrpc_session_unlock__(waiter); return jsonrpc_create_reply(json_object_create(), request->id); error: reply = jsonrpc_create_error(ovsdb_error_to_json(error), request->id); ovsdb_error_destroy(error); return reply; } static struct jsonrpc_msg * execute_transaction(struct ovsdb_jsonrpc_session *s, struct ovsdb *db, struct jsonrpc_msg *request) { ovsdb_jsonrpc_trigger_create(s, db, request->id, request->params); request->id = NULL; request->params = NULL; jsonrpc_msg_destroy(request); return NULL; } static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) { struct jsonrpc_msg *reply; if (!strcmp(request->method, "transact")) { struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply); if (!reply) { reply = execute_transaction(s, db, request); } } else if (!strcmp(request->method, "monitor")) { struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply); if (!reply) { reply = ovsdb_jsonrpc_monitor_create(s, db, request->params, request->id); } } else if (!strcmp(request->method, "monitor_cancel")) { reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params), request->id); } else if (!strcmp(request->method, "get_schema")) { struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply); if (!reply) { reply = jsonrpc_create_reply(ovsdb_schema_to_json(db->schema), request->id); } } else if (!strcmp(request->method, "list_dbs")) { size_t n_dbs = shash_count(&s->up.server->dbs); struct shash_node *node; struct json **dbs; size_t i; dbs = xmalloc(n_dbs * sizeof *dbs); i = 0; SHASH_FOR_EACH (node, &s->up.server->dbs) { dbs[i++] = json_string_create(node->name); } reply = jsonrpc_create_reply(json_array_create(dbs, n_dbs), request->id); } else if (!strcmp(request->method, "lock")) { reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT); } else if (!strcmp(request->method, "steal")) { reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_STEAL); } else if (!strcmp(request->method, "unlock")) { reply = ovsdb_jsonrpc_session_unlock(s, request); } else if (!strcmp(request->method, "echo")) { reply = jsonrpc_create_reply(json_clone(request->params), request->id); } else { reply = jsonrpc_create_error(json_string_create("unknown method"), request->id); } if (reply) { jsonrpc_msg_destroy(request); ovsdb_jsonrpc_session_send(s, reply); } } static void execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) { if (json_array(request->params)->n == 1) { struct ovsdb_jsonrpc_trigger *t; struct json *id; id = request->params->u.array.elems[0]; t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0)); if (t) { ovsdb_jsonrpc_trigger_complete(t); } } } static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) { if (!strcmp(request->method, "cancel")) { execute_cancel(s, request); } jsonrpc_msg_destroy(request); } static void ovsdb_jsonrpc_session_send(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *msg) { ovsdb_jsonrpc_monitor_flush_all(s); jsonrpc_session_send(s->js, msg); } /* JSON-RPC database server triggers. * * (Every transaction is treated as a trigger even if it doesn't actually have * any "wait" operations.) */ struct ovsdb_jsonrpc_trigger { struct ovsdb_trigger trigger; struct hmap_node hmap_node; /* In session's "triggers" hmap. */ struct json *id; }; static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db, struct json *id, struct json *params) { struct ovsdb_jsonrpc_trigger *t; size_t hash; /* Check for duplicate ID. */ hash = json_hash(id, 0); t = ovsdb_jsonrpc_trigger_find(s, id, hash); if (t) { struct jsonrpc_msg *msg; msg = jsonrpc_create_error(json_string_create("duplicate request ID"), id); ovsdb_jsonrpc_session_send(s, msg); json_destroy(id); json_destroy(params); return; } /* Insert into trigger table. */ t = xmalloc(sizeof *t); ovsdb_trigger_init(&s->up, db, &t->trigger, params, time_msec()); t->id = id; hmap_insert(&s->triggers, &t->hmap_node, hash); /* Complete early if possible. */ if (ovsdb_trigger_is_complete(&t->trigger)) { ovsdb_jsonrpc_trigger_complete(t); } } static struct ovsdb_jsonrpc_trigger * ovsdb_jsonrpc_trigger_find(struct ovsdb_jsonrpc_session *s, const struct json *id, size_t hash) { struct ovsdb_jsonrpc_trigger *t; HMAP_FOR_EACH_WITH_HASH (t, hmap_node, hash, &s->triggers) { if (json_equal(t->id, id)) { return t; } } return NULL; } static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t) { struct ovsdb_jsonrpc_session *s; s = CONTAINER_OF(t->trigger.session, struct ovsdb_jsonrpc_session, up); if (jsonrpc_session_is_connected(s->js)) { struct jsonrpc_msg *reply; struct json *result; result = ovsdb_trigger_steal_result(&t->trigger); if (result) { reply = jsonrpc_create_reply(result, t->id); } else { reply = jsonrpc_create_error(json_string_create("canceled"), t->id); } ovsdb_jsonrpc_session_send(s, reply); } json_destroy(t->id); ovsdb_trigger_destroy(&t->trigger); hmap_remove(&s->triggers, &t->hmap_node); free(t); } static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s) { struct ovsdb_jsonrpc_trigger *t, *next; HMAP_FOR_EACH_SAFE (t, next, hmap_node, &s->triggers) { ovsdb_jsonrpc_trigger_complete(t); } } static void ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s) { while (!list_is_empty(&s->up.completions)) { struct ovsdb_jsonrpc_trigger *t = CONTAINER_OF(s->up.completions.next, struct ovsdb_jsonrpc_trigger, trigger.node); ovsdb_jsonrpc_trigger_complete(t); } } /* Jsonrpc front end monitor. */ struct ovsdb_jsonrpc_monitor { struct ovsdb_jsonrpc_session *session; struct ovsdb *db; struct hmap_node node; /* In ovsdb_jsonrpc_session's "monitors". */ struct json *monitor_id; struct ovsdb_monitor *dbmon; uint64_t unflushed; /* The first transaction that has not been flushed to the jsonrpc remote client. */ }; static struct ovsdb_jsonrpc_monitor * ovsdb_jsonrpc_monitor_find(struct ovsdb_jsonrpc_session *s, const struct json *monitor_id) { struct ovsdb_jsonrpc_monitor *m; HMAP_FOR_EACH_WITH_HASH (m, node, json_hash(monitor_id, 0), &s->monitors) { if (json_equal(m->monitor_id, monitor_id)) { return m; } } return NULL; } static bool parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value) { const struct json *json; json = ovsdb_parser_member(parser, name, OP_BOOLEAN | OP_OPTIONAL); return json ? json_boolean(json) : default_value; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, const struct json *monitor_request, size_t *allocated_columns) { const struct ovsdb_table_schema *ts = table->schema; enum ovsdb_monitor_selection select; const struct json *columns, *select_json; struct ovsdb_parser parser; struct ovsdb_error *error; ovsdb_parser_init(&parser, monitor_request, "table %s", ts->name); columns = ovsdb_parser_member(&parser, "columns", OP_ARRAY | OP_OPTIONAL); select_json = ovsdb_parser_member(&parser, "select", OP_OBJECT | OP_OPTIONAL); error = ovsdb_parser_finish(&parser); if (error) { return error; } if (select_json) { select = 0; ovsdb_parser_init(&parser, select_json, "table %s select", ts->name); if (parse_bool(&parser, "initial", true)) { select |= OJMS_INITIAL; } if (parse_bool(&parser, "insert", true)) { select |= OJMS_INSERT; } if (parse_bool(&parser, "delete", true)) { select |= OJMS_DELETE; } if (parse_bool(&parser, "modify", true)) { select |= OJMS_MODIFY; } error = ovsdb_parser_finish(&parser); if (error) { return error; } } else { select = OJMS_INITIAL | OJMS_INSERT | OJMS_DELETE | OJMS_MODIFY; } ovsdb_monitor_table_add_select(dbmon, table, select); if (columns) { size_t i; if (columns->type != JSON_ARRAY) { return ovsdb_syntax_error(columns, NULL, "array of column names expected"); } for (i = 0; i < columns->u.array.n; i++) { const struct ovsdb_column *column; const char *s; if (columns->u.array.elems[i]->type != JSON_STRING) { return ovsdb_syntax_error(columns, NULL, "array of column names expected"); } s = columns->u.array.elems[i]->u.string; column = shash_find_data(&table->schema->columns, s); if (!column) { return ovsdb_syntax_error(columns, NULL, "%s is not a valid " "column name", s); } ovsdb_monitor_add_column(dbmon, table, column, select, allocated_columns); } } else { struct shash_node *node; SHASH_FOR_EACH (node, &ts->columns) { const struct ovsdb_column *column = node->data; if (column->index != OVSDB_COL_UUID) { ovsdb_monitor_add_column(dbmon, table, column, select, allocated_columns); } } } return NULL; } static struct jsonrpc_msg * ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db, struct json *params, const struct json *request_id) { struct ovsdb_jsonrpc_monitor *m = NULL; struct ovsdb_monitor *dbmon = NULL; struct json *monitor_id, *monitor_requests; struct ovsdb_error *error = NULL; struct shash_node *node; struct json *json; if (json_array(params)->n != 3) { error = ovsdb_syntax_error(params, NULL, "invalid parameters"); goto error; } monitor_id = params->u.array.elems[1]; monitor_requests = params->u.array.elems[2]; if (monitor_requests->type != JSON_OBJECT) { error = ovsdb_syntax_error(monitor_requests, NULL, "monitor-requests must be object"); goto error; } if (ovsdb_jsonrpc_monitor_find(s, monitor_id)) { error = ovsdb_syntax_error(monitor_id, NULL, "duplicate monitor ID"); goto error; } m = xzalloc(sizeof *m); m->session = s; m->db = db; m->dbmon = ovsdb_monitor_create(db, m); m->unflushed = 0; hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0)); m->monitor_id = json_clone(monitor_id); SHASH_FOR_EACH (node, json_object(monitor_requests)) { const struct ovsdb_table *table; const char *column_name; size_t allocated_columns; const struct json *mr_value; size_t i; table = ovsdb_get_table(m->db, node->name); if (!table) { error = ovsdb_syntax_error(NULL, NULL, "no table named %s", node->name); goto error; } ovsdb_monitor_add_table(m->dbmon, table); /* Parse columns. */ mr_value = node->data; allocated_columns = 0; if (mr_value->type == JSON_ARRAY) { const struct json_array *array = &mr_value->u.array; for (i = 0; i < array->n; i++) { error = ovsdb_jsonrpc_parse_monitor_request( m->dbmon, table, array->elems[i], &allocated_columns); if (error) { goto error; } } } else { error = ovsdb_jsonrpc_parse_monitor_request( m->dbmon, table, mr_value, &allocated_columns); if (error) { goto error; } } column_name = ovsdb_monitor_table_check_duplicates(m->dbmon, table); if (column_name) { error = ovsdb_syntax_error(mr_value, NULL, "column %s " "mentioned more than once", column_name); goto error; } } dbmon = ovsdb_monitor_add(m->dbmon); if (dbmon != m->dbmon) { /* Found an exisiting dbmon, reuse the current one. */ ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m, m->unflushed); ovsdb_monitor_add_jsonrpc_monitor(dbmon, m); m->dbmon = dbmon; } ovsdb_monitor_get_initial(m->dbmon); json = ovsdb_jsonrpc_monitor_compose_update(m, true); json = json ? json : json_object_create(); return jsonrpc_create_reply(json, request_id); error: if (m) { ovsdb_jsonrpc_monitor_destroy(m); } json = ovsdb_error_to_json(error); ovsdb_error_destroy(error); return jsonrpc_create_error(json, request_id); } static struct jsonrpc_msg * ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s, struct json_array *params, const struct json *request_id) { if (params->n != 1) { return jsonrpc_create_error(json_string_create("invalid parameters"), request_id); } else { struct ovsdb_jsonrpc_monitor *m; m = ovsdb_jsonrpc_monitor_find(s, params->elems[0]); if (!m) { return jsonrpc_create_error(json_string_create("unknown monitor"), request_id); } else { ovsdb_jsonrpc_monitor_destroy(m); return jsonrpc_create_reply(json_object_create(), request_id); } } } static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s) { struct ovsdb_jsonrpc_monitor *m, *next; HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) { ovsdb_jsonrpc_monitor_destroy(m); } } static struct json * ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m, bool initial) { if (!ovsdb_monitor_needs_flush(m->dbmon, m->unflushed)) { return NULL; } return ovsdb_monitor_get_update(m->dbmon, initial, &m->unflushed); } static bool ovsdb_jsonrpc_monitor_needs_flush(struct ovsdb_jsonrpc_session *s) { struct ovsdb_jsonrpc_monitor *m; HMAP_FOR_EACH (m, node, &s->monitors) { if (ovsdb_monitor_needs_flush(m->dbmon, m->unflushed)) { return true; } } return false; } void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_jsonrpc_monitor *m) { json_destroy(m->monitor_id); hmap_remove(&m->session->monitors, &m->node); ovsdb_monitor_remove_jsonrpc_monitor(m->dbmon, m, m->unflushed); free(m); } static void ovsdb_jsonrpc_monitor_flush_all(struct ovsdb_jsonrpc_session *s) { struct ovsdb_jsonrpc_monitor *m; HMAP_FOR_EACH (m, node, &s->monitors) { struct json *json; json = ovsdb_jsonrpc_monitor_compose_update(m, false); if (json) { struct jsonrpc_msg *msg; struct json *params; params = json_array_create_2(json_clone(m->monitor_id), json); msg = jsonrpc_create_notify("update", params); jsonrpc_session_send(s->js, msg); } } } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/row.h0000644000000000000000000000013213534540071015741 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.093855726 openvswitch-2.5.9/ovsdb/row.h0000644000175000017500000001464713534540071017443 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_ROW_H #define OVSDB_ROW_H 1 #include #include #include "column.h" #include "hmap.h" #include "list.h" #include "ovsdb-data.h" struct ovsdb_column_set; /* A weak reference. * * When a column in row A contains a weak reference to UUID of a row B this * constitutes a weak reference from A (the source) to B (the destination). * * Rows A and B may be in the same table or different tables. * * Weak references from a row to itself are allowed, but no "struct * ovsdb_weak_ref" structures are created for them. */ struct ovsdb_weak_ref { struct ovs_list src_node; /* In src->src_refs list. */ struct ovs_list dst_node; /* In destination row's dst_refs list. */ struct ovsdb_row *src; /* Source row. */ }; /* A row in a database table. */ struct ovsdb_row { struct hmap_node hmap_node; /* Element in ovsdb_table's 'rows' hmap. */ struct ovsdb_table *table; /* Table to which this belongs. */ struct ovsdb_txn_row *txn_row; /* Transaction that row is in, if any. */ /* Weak references. */ struct ovs_list src_refs; /* Weak references from this row. */ struct ovs_list dst_refs; /* Weak references to this row. */ /* Number of strong refs to this row from other rows, in this table or * other tables, through 'uuid' columns that have a 'refTable' constraint * pointing to this table and a 'refType' of "strong". A row with nonzero * 'n_refs' cannot be deleted. Updated and checked only at transaction * commit. */ size_t n_refs; /* One datum for each column (shash_count(&table->schema->columns) * elements). */ struct ovsdb_datum fields[]; /* Followed by table->schema->n_indexes "struct hmap_node"s. In rows that * have have been committed as part of the database, the hmap_node with * index 'i' is contained in hmap table->indexes[i]. */ }; struct ovsdb_row *ovsdb_row_create(const struct ovsdb_table *); struct ovsdb_row *ovsdb_row_clone(const struct ovsdb_row *); void ovsdb_row_destroy(struct ovsdb_row *); uint32_t ovsdb_row_hash_columns(const struct ovsdb_row *, const struct ovsdb_column_set *, uint32_t basis); bool ovsdb_row_equal_columns(const struct ovsdb_row *, const struct ovsdb_row *, const struct ovsdb_column_set *); int ovsdb_row_compare_columns_3way(const struct ovsdb_row *, const struct ovsdb_row *, const struct ovsdb_column_set *); void ovsdb_row_update_columns(struct ovsdb_row *, const struct ovsdb_row *, const struct ovsdb_column_set *); void ovsdb_row_columns_to_string(const struct ovsdb_row *, const struct ovsdb_column_set *, struct ds *); struct ovsdb_error *ovsdb_row_from_json(struct ovsdb_row *, const struct json *, struct ovsdb_symbol_table *, struct ovsdb_column_set *included) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_row_to_json(const struct ovsdb_row *, const struct ovsdb_column_set *include); static inline const struct uuid * ovsdb_row_get_uuid(const struct ovsdb_row *row) { return &row->fields[OVSDB_COL_UUID].keys[0].uuid; } static inline struct uuid * ovsdb_row_get_uuid_rw(struct ovsdb_row *row) { return &row->fields[OVSDB_COL_UUID].keys[0].uuid; } static inline const struct uuid * ovsdb_row_get_version(const struct ovsdb_row *row) { return &row->fields[OVSDB_COL_VERSION].keys[0].uuid; } static inline struct uuid * ovsdb_row_get_version_rw(struct ovsdb_row *row) { return &row->fields[OVSDB_COL_VERSION].keys[0].uuid; } static inline uint32_t ovsdb_row_hash(const struct ovsdb_row *row) { return uuid_hash(ovsdb_row_get_uuid(row)); } /* An unordered collection of rows. */ struct ovsdb_row_set { const struct ovsdb_row **rows; size_t n_rows, allocated_rows; }; #define OVSDB_ROW_SET_INITIALIZER { NULL, 0, 0 } void ovsdb_row_set_init(struct ovsdb_row_set *); void ovsdb_row_set_destroy(struct ovsdb_row_set *); void ovsdb_row_set_add_row(struct ovsdb_row_set *, const struct ovsdb_row *); struct json *ovsdb_row_set_to_json(const struct ovsdb_row_set *, const struct ovsdb_column_set *); void ovsdb_row_set_sort(struct ovsdb_row_set *, const struct ovsdb_column_set *); /* A hash table of rows. A specified set of columns is used for hashing and * comparing rows. * * The row hash doesn't necessarily own its rows. They may be owned by, for * example, an ovsdb_table. */ struct ovsdb_row_hash { struct hmap rows; struct ovsdb_column_set columns; }; #define OVSDB_ROW_HASH_INITIALIZER(RH) \ { HMAP_INITIALIZER(&(RH).rows), OVSDB_COLUMN_SET_INITIALIZER } struct ovsdb_row_hash_node { struct hmap_node hmap_node; const struct ovsdb_row *row; }; void ovsdb_row_hash_init(struct ovsdb_row_hash *, const struct ovsdb_column_set *); void ovsdb_row_hash_destroy(struct ovsdb_row_hash *, bool destroy_rows); size_t ovsdb_row_hash_count(const struct ovsdb_row_hash *); bool ovsdb_row_hash_contains(const struct ovsdb_row_hash *, const struct ovsdb_row *); bool ovsdb_row_hash_contains_all(const struct ovsdb_row_hash *, const struct ovsdb_row_hash *); bool ovsdb_row_hash_insert(struct ovsdb_row_hash *, const struct ovsdb_row *); bool ovsdb_row_hash_contains__(const struct ovsdb_row_hash *, const struct ovsdb_row *, size_t hash); bool ovsdb_row_hash_insert__(struct ovsdb_row_hash *, const struct ovsdb_row *, size_t hash); #endif /* ovsdb/row.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/query.h0000644000000000000000000000013213534540071016277 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.093855726 openvswitch-2.5.9/ovsdb/query.h0000644000175000017500000000244513534540071017772 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_QUERY_H #define OVSDB_QUERY_H 1 #include struct ovsdb_column_set; struct ovsdb_condition; struct ovsdb_row; struct ovsdb_row_set; struct ovsdb_table; struct ovsdb_txn; void ovsdb_query(struct ovsdb_table *, const struct ovsdb_condition *, bool (*output_row)(const struct ovsdb_row *, void *aux), void *aux); void ovsdb_query_row_set(struct ovsdb_table *, const struct ovsdb_condition *, struct ovsdb_row_set *); void ovsdb_query_distinct(struct ovsdb_table *, const struct ovsdb_condition *, const struct ovsdb_column_set *, struct ovsdb_row_set *); #endif /* ovsdb/query.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/execution.c0000644000000000000000000000013213534540071017130 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.121686458 30 ctime=1567801425.077855608 openvswitch-2.5.9/ovsdb/execution.c0000644000175000017500000005755413534540071020636 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "column.h" #include "condition.h" #include "file.h" #include "json.h" #include "mutation.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb.h" #include "query.h" #include "row.h" #include "server.h" #include "table.h" #include "timeval.h" #include "transaction.h" struct ovsdb_execution { struct ovsdb *db; const struct ovsdb_session *session; struct ovsdb_txn *txn; struct ovsdb_symbol_table *symtab; bool durable; /* Triggers. */ long long int elapsed_msec; long long int timeout_msec; }; typedef struct ovsdb_error *ovsdb_operation_executor(struct ovsdb_execution *, struct ovsdb_parser *, struct json *result); static ovsdb_operation_executor ovsdb_execute_insert; static ovsdb_operation_executor ovsdb_execute_select; static ovsdb_operation_executor ovsdb_execute_update; static ovsdb_operation_executor ovsdb_execute_mutate; static ovsdb_operation_executor ovsdb_execute_delete; static ovsdb_operation_executor ovsdb_execute_wait; static ovsdb_operation_executor ovsdb_execute_commit; static ovsdb_operation_executor ovsdb_execute_abort; static ovsdb_operation_executor ovsdb_execute_comment; static ovsdb_operation_executor ovsdb_execute_assert; static ovsdb_operation_executor * lookup_executor(const char *name) { struct ovsdb_operation { const char *name; ovsdb_operation_executor *executor; }; static const struct ovsdb_operation operations[] = { { "insert", ovsdb_execute_insert }, { "select", ovsdb_execute_select }, { "update", ovsdb_execute_update }, { "mutate", ovsdb_execute_mutate }, { "delete", ovsdb_execute_delete }, { "wait", ovsdb_execute_wait }, { "commit", ovsdb_execute_commit }, { "abort", ovsdb_execute_abort }, { "comment", ovsdb_execute_comment }, { "assert", ovsdb_execute_assert }, }; size_t i; for (i = 0; i < ARRAY_SIZE(operations); i++) { const struct ovsdb_operation *c = &operations[i]; if (!strcmp(c->name, name)) { return c->executor; } } return NULL; } struct json * ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session, const struct json *params, long long int elapsed_msec, long long int *timeout_msec) { struct ovsdb_execution x; struct ovsdb_error *error; struct json *results; size_t n_operations; size_t i; if (params->type != JSON_ARRAY || !params->u.array.n || params->u.array.elems[0]->type != JSON_STRING || strcmp(params->u.array.elems[0]->u.string, db->schema->name)) { if (params->type != JSON_ARRAY) { error = ovsdb_syntax_error(params, NULL, "array expected"); } else { error = ovsdb_syntax_error(params, NULL, "database name expected " "as first parameter"); } results = ovsdb_error_to_json(error); ovsdb_error_destroy(error); return results; } x.db = db; x.session = session; x.txn = ovsdb_txn_create(db); x.symtab = ovsdb_symbol_table_create(); x.durable = false; x.elapsed_msec = elapsed_msec; x.timeout_msec = LLONG_MAX; results = NULL; results = json_array_create_empty(); n_operations = params->u.array.n - 1; error = NULL; for (i = 1; i <= n_operations; i++) { struct json *operation = params->u.array.elems[i]; struct ovsdb_error *parse_error; struct ovsdb_parser parser; struct json *result; const struct json *op; /* Parse and execute operation. */ ovsdb_parser_init(&parser, operation, "ovsdb operation %"PRIuSIZE" of %"PRIuSIZE, i, n_operations); op = ovsdb_parser_member(&parser, "op", OP_ID); result = json_object_create(); if (op) { const char *op_name = json_string(op); ovsdb_operation_executor *executor = lookup_executor(op_name); if (executor) { error = executor(&x, &parser, result); } else { ovsdb_parser_raise_error(&parser, "No operation \"%s\"", op_name); } } else { ovs_assert(ovsdb_parser_has_error(&parser)); } /* A parse error overrides any other error. * An error overrides any other result. */ parse_error = ovsdb_parser_finish(&parser); if (parse_error) { ovsdb_error_destroy(error); error = parse_error; } if (error) { json_destroy(result); result = ovsdb_error_to_json(error); } if (error && !strcmp(ovsdb_error_get_tag(error), "not supported") && timeout_msec) { ovsdb_txn_abort(x.txn); *timeout_msec = x.timeout_msec; json_destroy(result); json_destroy(results); results = NULL; goto exit; } /* Add result to array. */ json_array_add(results, result); if (error) { break; } } if (!error) { error = ovsdb_txn_commit(x.txn, x.durable); if (error) { json_array_add(results, ovsdb_error_to_json(error)); } } else { ovsdb_txn_abort(x.txn); } while (json_array(results)->n < n_operations) { json_array_add(results, json_null_create()); } exit: ovsdb_error_destroy(error); ovsdb_symbol_table_destroy(x.symtab); return results; } static struct ovsdb_error * ovsdb_execute_commit(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result OVS_UNUSED) { const struct json *durable; durable = ovsdb_parser_member(parser, "durable", OP_BOOLEAN); if (durable && json_boolean(durable)) { x->durable = true; } return NULL; } static struct ovsdb_error * ovsdb_execute_abort(struct ovsdb_execution *x OVS_UNUSED, struct ovsdb_parser *parser OVS_UNUSED, struct json *result OVS_UNUSED) { return ovsdb_error("aborted", "aborted by request"); } static struct ovsdb_table * parse_table(struct ovsdb_execution *x, struct ovsdb_parser *parser, const char *member) { struct ovsdb_table *table; const char *table_name; const struct json *json; json = ovsdb_parser_member(parser, member, OP_ID); if (!json) { return NULL; } table_name = json_string(json); table = shash_find_data(&x->db->tables, table_name); if (!table) { ovsdb_parser_raise_error(parser, "No table named %s.", table_name); } return table; } static OVS_WARN_UNUSED_RESULT struct ovsdb_error * parse_row(const struct json *json, const struct ovsdb_table *table, struct ovsdb_symbol_table *symtab, struct ovsdb_row **rowp, struct ovsdb_column_set *columns) { struct ovsdb_error *error; struct ovsdb_row *row; *rowp = NULL; if (!table) { return OVSDB_BUG("null table"); } if (!json) { return OVSDB_BUG("null row"); } row = ovsdb_row_create(table); error = ovsdb_row_from_json(row, json, symtab, columns); if (error) { ovsdb_row_destroy(row); return error; } else { *rowp = row; return NULL; } } static struct ovsdb_error * ovsdb_execute_insert(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result) { struct ovsdb_table *table; struct ovsdb_row *row = NULL; const struct json *uuid_name, *row_json; struct ovsdb_error *error; struct uuid row_uuid; table = parse_table(x, parser, "table"); uuid_name = ovsdb_parser_member(parser, "uuid-name", OP_ID | OP_OPTIONAL); row_json = ovsdb_parser_member(parser, "row", OP_OBJECT); error = ovsdb_parser_get_error(parser); if (error) { return error; } if (uuid_name) { struct ovsdb_symbol *symbol; symbol = ovsdb_symbol_table_insert(x->symtab, json_string(uuid_name)); if (symbol->created) { return ovsdb_syntax_error(uuid_name, "duplicate uuid-name", "This \"uuid-name\" appeared on an " "earlier \"insert\" operation."); } row_uuid = symbol->uuid; symbol->created = true; } else { uuid_generate(&row_uuid); } if (!error) { error = parse_row(row_json, table, x->symtab, &row, NULL); } if (!error) { /* Check constraints for columns not included in "row", in case the * default values do not satisfy the constraints. We could check only * the columns that have their default values by supplying an * ovsdb_column_set to parse_row() above, but I suspect that this is * cheaper. */ const struct shash_node *node; SHASH_FOR_EACH (node, &table->schema->columns) { const struct ovsdb_column *column = node->data; const struct ovsdb_datum *datum = &row->fields[column->index]; /* If there are 0 keys or pairs, there's nothing to check. * If there is 1, it might be a default value. * If there are more, it can't be a default value, so the value has * already been checked. */ if (datum->n == 1) { error = ovsdb_datum_check_constraints(datum, &column->type); if (error) { ovsdb_row_destroy(row); break; } } } } if (!error) { *ovsdb_row_get_uuid_rw(row) = row_uuid; ovsdb_txn_row_insert(x->txn, row); json_object_put(result, "uuid", ovsdb_datum_to_json(&row->fields[OVSDB_COL_UUID], &ovsdb_type_uuid)); } return error; } static struct ovsdb_error * ovsdb_execute_select(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result) { struct ovsdb_table *table; const struct json *where, *columns_json, *sort_json; struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; struct ovsdb_column_set sort = OVSDB_COLUMN_SET_INITIALIZER; struct ovsdb_error *error; table = parse_table(x, parser, "table"); where = ovsdb_parser_member(parser, "where", OP_ARRAY); columns_json = ovsdb_parser_member(parser, "columns", OP_ARRAY | OP_OPTIONAL); sort_json = ovsdb_parser_member(parser, "sort", OP_ARRAY | OP_OPTIONAL); error = ovsdb_parser_get_error(parser); if (!error) { error = ovsdb_condition_from_json(table->schema, where, x->symtab, &condition); } if (!error) { error = ovsdb_column_set_from_json(columns_json, table->schema, &columns); } if (!error) { error = ovsdb_column_set_from_json(sort_json, table->schema, &sort); } if (!error) { struct ovsdb_row_set rows = OVSDB_ROW_SET_INITIALIZER; ovsdb_query_distinct(table, &condition, &columns, &rows); ovsdb_row_set_sort(&rows, &sort); json_object_put(result, "rows", ovsdb_row_set_to_json(&rows, &columns)); ovsdb_row_set_destroy(&rows); } ovsdb_column_set_destroy(&columns); ovsdb_column_set_destroy(&sort); ovsdb_condition_destroy(&condition); return error; } struct update_row_cbdata { size_t n_matches; struct ovsdb_txn *txn; const struct ovsdb_row *row; const struct ovsdb_column_set *columns; }; static bool update_row_cb(const struct ovsdb_row *row, void *ur_) { struct update_row_cbdata *ur = ur_; ur->n_matches++; if (!ovsdb_row_equal_columns(row, ur->row, ur->columns)) { ovsdb_row_update_columns(ovsdb_txn_row_modify(ur->txn, row), ur->row, ur->columns); } return true; } static struct ovsdb_error * ovsdb_execute_update(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result) { struct ovsdb_table *table; const struct json *where, *row_json; struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; struct ovsdb_row *row = NULL; struct update_row_cbdata ur; struct ovsdb_error *error; table = parse_table(x, parser, "table"); where = ovsdb_parser_member(parser, "where", OP_ARRAY); row_json = ovsdb_parser_member(parser, "row", OP_OBJECT); error = ovsdb_parser_get_error(parser); if (!error) { error = parse_row(row_json, table, x->symtab, &row, &columns); } if (!error) { size_t i; for (i = 0; i < columns.n_columns; i++) { const struct ovsdb_column *column = columns.columns[i]; if (!column->mutable) { error = ovsdb_syntax_error(parser->json, "constraint violation", "Cannot update immutable column %s " "in table %s.", column->name, table->schema->name); break; } } } if (!error) { error = ovsdb_condition_from_json(table->schema, where, x->symtab, &condition); } if (!error) { ur.n_matches = 0; ur.txn = x->txn; ur.row = row; ur.columns = &columns; ovsdb_query(table, &condition, update_row_cb, &ur); json_object_put(result, "count", json_integer_create(ur.n_matches)); } ovsdb_row_destroy(row); ovsdb_column_set_destroy(&columns); ovsdb_condition_destroy(&condition); return error; } struct mutate_row_cbdata { size_t n_matches; struct ovsdb_txn *txn; const struct ovsdb_mutation_set *mutations; struct ovsdb_error **error; }; static bool mutate_row_cb(const struct ovsdb_row *row, void *mr_) { struct mutate_row_cbdata *mr = mr_; mr->n_matches++; *mr->error = ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn, row), mr->mutations); return *mr->error == NULL; } static struct ovsdb_error * ovsdb_execute_mutate(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result) { struct ovsdb_table *table; const struct json *where; const struct json *mutations_json; struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; struct ovsdb_mutation_set mutations = OVSDB_MUTATION_SET_INITIALIZER; struct ovsdb_row *row = NULL; struct mutate_row_cbdata mr; struct ovsdb_error *error; table = parse_table(x, parser, "table"); where = ovsdb_parser_member(parser, "where", OP_ARRAY); mutations_json = ovsdb_parser_member(parser, "mutations", OP_ARRAY); error = ovsdb_parser_get_error(parser); if (!error) { error = ovsdb_mutation_set_from_json(table->schema, mutations_json, x->symtab, &mutations); } if (!error) { error = ovsdb_condition_from_json(table->schema, where, x->symtab, &condition); } if (!error) { mr.n_matches = 0; mr.txn = x->txn; mr.mutations = &mutations; mr.error = &error; ovsdb_query(table, &condition, mutate_row_cb, &mr); json_object_put(result, "count", json_integer_create(mr.n_matches)); } ovsdb_row_destroy(row); ovsdb_mutation_set_destroy(&mutations); ovsdb_condition_destroy(&condition); return error; } struct delete_row_cbdata { size_t n_matches; const struct ovsdb_table *table; struct ovsdb_txn *txn; }; static bool delete_row_cb(const struct ovsdb_row *row, void *dr_) { struct delete_row_cbdata *dr = dr_; dr->n_matches++; ovsdb_txn_row_delete(dr->txn, row); return true; } static struct ovsdb_error * ovsdb_execute_delete(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result) { struct ovsdb_table *table; const struct json *where; struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; struct ovsdb_error *error; where = ovsdb_parser_member(parser, "where", OP_ARRAY); table = parse_table(x, parser, "table"); error = ovsdb_parser_get_error(parser); if (!error) { error = ovsdb_condition_from_json(table->schema, where, x->symtab, &condition); } if (!error) { struct delete_row_cbdata dr; dr.n_matches = 0; dr.table = table; dr.txn = x->txn; ovsdb_query(table, &condition, delete_row_cb, &dr); json_object_put(result, "count", json_integer_create(dr.n_matches)); } ovsdb_condition_destroy(&condition); return error; } struct wait_auxdata { struct ovsdb_row_hash *actual; struct ovsdb_row_hash *expected; bool *equal; }; static bool ovsdb_execute_wait_query_cb(const struct ovsdb_row *row, void *aux_) { struct wait_auxdata *aux = aux_; if (ovsdb_row_hash_contains(aux->expected, row)) { ovsdb_row_hash_insert(aux->actual, row); return true; } else { /* The query row isn't in the expected result set, so the actual and * expected results sets definitely differ and we can short-circuit the * rest of the query. */ *aux->equal = false; return false; } } static struct ovsdb_error * ovsdb_execute_wait(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result OVS_UNUSED) { struct ovsdb_table *table; const struct json *timeout, *where, *columns_json, *until, *rows; struct ovsdb_condition condition = OVSDB_CONDITION_INITIALIZER; struct ovsdb_column_set columns = OVSDB_COLUMN_SET_INITIALIZER; struct ovsdb_row_hash expected = OVSDB_ROW_HASH_INITIALIZER(expected); struct ovsdb_row_hash actual = OVSDB_ROW_HASH_INITIALIZER(actual); struct ovsdb_error *error; struct wait_auxdata aux; long long int timeout_msec = 0; size_t i; timeout = ovsdb_parser_member(parser, "timeout", OP_NUMBER | OP_OPTIONAL); where = ovsdb_parser_member(parser, "where", OP_ARRAY); columns_json = ovsdb_parser_member(parser, "columns", OP_ARRAY | OP_OPTIONAL); until = ovsdb_parser_member(parser, "until", OP_STRING); rows = ovsdb_parser_member(parser, "rows", OP_ARRAY); table = parse_table(x, parser, "table"); error = ovsdb_parser_get_error(parser); if (!error) { error = ovsdb_condition_from_json(table->schema, where, x->symtab, &condition); } if (!error) { error = ovsdb_column_set_from_json(columns_json, table->schema, &columns); } if (!error) { if (timeout) { timeout_msec = MIN(LLONG_MAX, json_real(timeout)); if (timeout_msec < 0) { error = ovsdb_syntax_error(timeout, NULL, "timeout must be nonnegative"); } else if (timeout_msec < x->timeout_msec) { x->timeout_msec = timeout_msec; } } else { timeout_msec = LLONG_MAX; } } if (!error) { if (strcmp(json_string(until), "==") && strcmp(json_string(until), "!=")) { error = ovsdb_syntax_error(until, NULL, "\"until\" must be \"==\" or \"!=\""); } } if (!error) { /* Parse "rows" into 'expected'. */ ovsdb_row_hash_init(&expected, &columns); for (i = 0; i < rows->u.array.n; i++) { struct ovsdb_row *row; row = ovsdb_row_create(table); error = ovsdb_row_from_json(row, rows->u.array.elems[i], x->symtab, NULL); if (error) { ovsdb_row_destroy(row); break; } if (!ovsdb_row_hash_insert(&expected, row)) { /* XXX Perhaps we should abort with an error or log a * warning. */ ovsdb_row_destroy(row); } } } if (!error) { /* Execute query. */ bool equal = true; ovsdb_row_hash_init(&actual, &columns); aux.actual = &actual; aux.expected = &expected; aux.equal = &equal; ovsdb_query(table, &condition, ovsdb_execute_wait_query_cb, &aux); if (equal) { /* We know that every row in 'actual' is also in 'expected'. We * also know that all of the rows in 'actual' are distinct and that * all of the rows in 'expected' are distinct. Therefore, if * 'actual' and 'expected' have the same number of rows, then they * have the same content. */ size_t n_actual = ovsdb_row_hash_count(&actual); size_t n_expected = ovsdb_row_hash_count(&expected); equal = n_actual == n_expected; } if (!strcmp(json_string(until), "==") != equal) { if (timeout && x->elapsed_msec >= timeout_msec) { if (x->elapsed_msec) { error = ovsdb_error("timed out", "\"wait\" timed out after %lld ms", x->elapsed_msec); } else { error = ovsdb_error("timed out", "\"wait\" timed out"); } } else { /* ovsdb_execute() will change this, if triggers really are * supported. */ error = ovsdb_error("not supported", "triggers not supported"); } } } ovsdb_row_hash_destroy(&expected, true); ovsdb_row_hash_destroy(&actual, false); ovsdb_column_set_destroy(&columns); ovsdb_condition_destroy(&condition); return error; } static struct ovsdb_error * ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result OVS_UNUSED) { const struct json *comment; comment = ovsdb_parser_member(parser, "comment", OP_STRING); if (!comment) { return NULL; } ovsdb_txn_add_comment(x->txn, json_string(comment)); return NULL; } static struct ovsdb_error * ovsdb_execute_assert(struct ovsdb_execution *x, struct ovsdb_parser *parser, struct json *result OVS_UNUSED) { const struct json *lock_name; lock_name = ovsdb_parser_member(parser, "lock", OP_ID); if (!lock_name) { return NULL; } if (x->session) { const struct ovsdb_lock_waiter *waiter; waiter = ovsdb_session_get_lock_waiter(x->session, json_string(lock_name)); if (waiter && ovsdb_lock_waiter_is_owner(waiter)) { return NULL; } } return ovsdb_error("not owner", "Asserted lock %s not held.", json_string(lock_name)); } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-idlc.in0000644000000000000000000000013213534540071017337 xustar0030 mtime=1567801401.817684225 30 atime=1567801402.121686458 30 ctime=1567801424.177848973 openvswitch-2.5.9/ovsdb/ovsdb-idlc.in0000755000175000017500000010270613534540071021036 0ustar00jpettitjpettit00000000000000#! @PYTHON@ import getopt import os import re import sys import ovs.json import ovs.db.error import ovs.db.schema argv0 = sys.argv[0] def parseSchema(filename): return ovs.db.schema.IdlSchema.from_json(ovs.json.from_file(filename)) def annotateSchema(schemaFile, annotationFile): schemaJson = ovs.json.from_file(schemaFile) execfile(annotationFile, globals(), {"s": schemaJson}) ovs.json.to_stream(schemaJson, sys.stdout) sys.stdout.write('\n') def constify(cType, const): if (const and cType.endswith('*') and (cType == 'char **' or not cType.endswith('**'))): return 'const %s' % cType else: return cType def cMembers(prefix, tableName, columnName, column, const): comment = "" type = column.type if type.is_smap(): comment = """ /* Sets the "%(c)s" column's value from the "%(t)s" table in 'row' * to '%(c)s'. * * The caller retains ownership of '%(c)s' and everything in it. */""" \ % {'c': columnName, 't': tableName} return (comment, [{'name': columnName, 'type': 'struct smap ', 'comment': ''}]) comment = """\n/* Sets the "%s" column from the "%s" table in """\ """'row' to\n""" % (columnName, tableName) if type.n_min == 1 and type.n_max == 1: singleton = True pointer = '' else: singleton = False if type.is_optional_pointer(): pointer = '' else: pointer = '*' if type.value: keyName = "key_%s" % columnName valueName = "value_%s" % columnName key = {'name': keyName, 'type': constify(type.key.toCType(prefix) + pointer, const), 'comment': ''} value = {'name': valueName, 'type': constify(type.value.toCType(prefix) + pointer, const), 'comment': ''} if singleton: comment += " * the map with key '%s' and value '%s'\n *" \ % (keyName, valueName) else: comment += " * the map with keys '%s' and values '%s'\n *" \ % (keyName, valueName) members = [key, value] else: m = {'name': columnName, 'type': constify(type.key.toCType(prefix) + pointer, const), 'comment': type.cDeclComment()} if singleton: comment += " * '%s'" % columnName else: comment += " * the '%s' set" % columnName members = [m] if not singleton and not type.is_optional_pointer(): sizeName = "n_%s" % columnName comment += " with '%s' entries" % sizeName members.append({'name': sizeName, 'type': 'size_t ', 'comment': ''}) comment += ".\n" if type.is_optional() and not type.is_optional_pointer(): comment += """ * * '%s' may be 0 or 1; if it is 0, then '%s' * may be NULL.\n""" \ % ("n_%s" % columnName, columnName) if type.is_optional_pointer(): comment += """ * * If "%s" is null, the column will be the empty set, * otherwise it will contain the specified value.\n""" % columnName if type.constraintsToEnglish(): comment += """ * * Argument constraints: %s\n""" \ % type.constraintsToEnglish(lambda s : '"%s"' % s) comment += " *\n * The caller retains ownership of the arguments. */" return (comment, members) def printCIDLHeader(schemaFile): schema = parseSchema(schemaFile) prefix = schema.idlPrefix print '''\ /* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */ #ifndef %(prefix)sIDL_HEADER #define %(prefix)sIDL_HEADER 1 #include #include #include #include "ovsdb-data.h" #include "ovsdb-idl-provider.h" #include "smap.h" #include "uuid.h"''' % {'prefix': prefix.upper()} for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) print " " print "/* %s table. */" % tableName print "struct %s {" % structName print "\tstruct ovsdb_idl_row header_;" for columnName, column in sorted(table.columns.iteritems()): print "\n\t/* %s column. */" % columnName comment, members = cMembers(prefix, tableName, columnName, column, False) for member in members: print "\t%(type)s%(name)s;%(comment)s" % member print "};" # Column indexes. printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper()) for columnName in sorted(table.columns)] + ["%s_N_COLUMNS" % structName.upper()]) print for columnName in table.columns: print "#define %(s)s_col_%(c)s (%(s)s_columns[%(S)s_COL_%(C)s])" % { 's': structName, 'S': structName.upper(), 'c': columnName, 'C': columnName.upper()} print "\nextern struct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (structName, structName.upper()) print ''' const struct %(s)s *%(s)s_get_for_uuid(const struct ovsdb_idl *, const struct uuid *); const struct %(s)s *%(s)s_first(const struct ovsdb_idl *); const struct %(s)s *%(s)s_next(const struct %(s)s *); #define %(S)s_FOR_EACH(ROW, IDL) \\ for ((ROW) = %(s)s_first(IDL); \\ (ROW); \\ (ROW) = %(s)s_next(ROW)) #define %(S)s_FOR_EACH_SAFE(ROW, NEXT, IDL) \\ for ((ROW) = %(s)s_first(IDL); \\ (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\ (ROW) = (NEXT)) unsigned int %(s)s_get_seqno(const struct ovsdb_idl *); unsigned int %(s)s_row_get_seqno(const struct %(s)s *row, enum ovsdb_idl_change change); const struct %(s)s *%(s)s_track_get_first(const struct ovsdb_idl *); const struct %(s)s *%(s)s_track_get_next(const struct %(s)s *); #define %(S)s_FOR_EACH_TRACKED(ROW, IDL) \\ for ((ROW) = %(s)s_track_get_first(IDL); \\ (ROW); \\ (ROW) = %(s)s_track_get_next(ROW)) void %(s)s_init(struct %(s)s *); void %(s)s_delete(const struct %(s)s *); struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *); ''' % {'s': structName, 'S': structName.upper()} for columnName, column in sorted(table.columns.iteritems()): print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName} print for columnName, column in sorted(table.columns.iteritems()): if column.type.value: valueParam = ', enum ovsdb_atomic_type value_type' else: valueParam = '' print 'const struct ovsdb_datum *%(s)s_get_%(c)s(const struct %(s)s *, enum ovsdb_atomic_type key_type%(v)s);' % { 's': structName, 'c': columnName, 'v': valueParam} print for columnName, column in sorted(table.columns.iteritems()): print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName}, if column.type.is_smap(): args = ['const struct smap *'] else: comment, members = cMembers(prefix, tableName, columnName, column, True) args = ['%(type)s%(name)s' % member for member in members] print '%s);' % ', '.join(args) print # Table indexes. printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()]) print for tableName in schema.tables: print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % { 'p': prefix, 'P': prefix.upper(), 't': tableName.lower(), 'T': tableName.upper()} print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper()) print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix print "\nvoid %sinit(void);" % prefix print "\nconst char * %sget_db_version(void);" % prefix print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()} def printEnum(members): if len(members) == 0: return print "\nenum {"; for member in members[:-1]: print " %s," % member print " %s" % members[-1] print "};" def printCIDLSource(schemaFile): schema = parseSchema(schemaFile) prefix = schema.idlPrefix print '''\ /* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */ #include #include %s #include #include "ovs-thread.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "util.h" #ifdef __CHECKER__ /* Sparse dislikes sizeof(bool) ("warning: expression using sizeof bool"). */ enum { sizeof_bool = 1 }; #else enum { sizeof_bool = sizeof(bool) }; #endif static bool inited; ''' % schema.idlHeader # Cast functions. for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) print ''' static struct %(s)s * %(s)s_cast(const struct ovsdb_idl_row *row) { return row ? CONTAINER_OF(row, struct %(s)s, header_) : NULL; }\ ''' % {'s': structName} for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) print " " print "/* %s table. */" % (tableName) # Parse functions. for columnName, column in sorted(table.columns.iteritems()): print ''' static void %(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum) { struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName, 'c': columnName} type = column.type if type.value: keyVar = "row->key_%s" % columnName valueVar = "row->value_%s" % columnName else: keyVar = "row->%s" % columnName valueVar = None if type.is_smap(): print " size_t i;" print print " ovs_assert(inited);" print " smap_init(&row->%s);" % columnName print " for (i = 0; i < datum->n; i++) {" print " smap_add(&row->%s," % columnName print " datum->keys[i].string," print " datum->values[i].string);" print " }" elif (type.n_min == 1 and type.n_max == 1) or type.is_optional_pointer(): print print " ovs_assert(inited);" print " if (datum->n >= 1) {" if not type.key.ref_table: print " %s = datum->keys[0].%s;" % (keyVar, type.key.type.to_string()) else: print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.ref_table.name.lower(), prefix, prefix.upper(), type.key.ref_table.name.upper()) if valueVar: if type.value.ref_table: print " %s = datum->values[0].%s;" % (valueVar, type.value.type.to_string()) else: print " %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.ref_table.name.lower(), prefix, prefix.upper(), type.value.ref_table.name.upper()) print " } else {" print " %s" % type.key.initCDefault(keyVar, type.n_min == 0) if valueVar: print " %s" % type.value.initCDefault(valueVar, type.n_min == 0) print " }" else: if type.n_max != sys.maxint: print " size_t n = MIN(%d, datum->n);" % type.n_max nMax = "n" else: nMax = "datum->n" print " size_t i;" print print " ovs_assert(inited);" print " %s = NULL;" % keyVar if valueVar: print " %s = NULL;" % valueVar print " row->n_%s = 0;" % columnName print " for (i = 0; i < %s; i++) {" % nMax refs = [] if type.key.ref_table: print " struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.ref_table.name.lower(), prefix, type.key.ref_table.name.lower(), prefix, prefix.upper(), type.key.ref_table.name.upper()) keySrc = "keyRow" refs.append('keyRow') else: keySrc = "datum->keys[i].%s" % type.key.type.to_string() if type.value and type.value.ref_table: print " struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.ref_table.name.lower(), prefix, type.value.ref_table.name.lower(), prefix, prefix.upper(), type.value.ref_table.name.upper()) valueSrc = "valueRow" refs.append('valueRow') elif valueVar: valueSrc = "datum->values[i].%s" % type.value.type.to_string() if refs: print " if (%s) {" % ' && '.join(refs) indent = " " else: indent = " " print "%sif (!row->n_%s) {" % (indent, columnName) # Special case for boolean types. This is only here because # sparse does not like the "normal" case ("warning: expression # using sizeof bool"). if type.key.type == ovs.db.types.BooleanType: sizeof = "sizeof_bool" else: sizeof = "sizeof *%s" % keyVar print "%s %s = xmalloc(%s * %s);" % (indent, keyVar, nMax, sizeof) if valueVar: # Special case for boolean types (see above). if type.value.type == ovs.db.types.BooleanType: sizeof = " * sizeof_bool" else: sizeof = "sizeof *%s" % valueVar print "%s %s = xmalloc(%s * %s);" % (indent, valueVar, nMax, sizeof) print "%s}" % indent print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc) if valueVar: print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc) print "%srow->n_%s++;" % (indent, columnName) if refs: print " }" print " }" print "}" # Unparse functions. for columnName, column in sorted(table.columns.iteritems()): type = column.type if type.is_smap() or (type.n_min != 1 or type.n_max != 1) and not type.is_optional_pointer(): print ''' static void %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_) { struct %(s)s *row = %(s)s_cast(row_); ovs_assert(inited);''' % {'s': structName, 'c': columnName} if type.is_smap(): print " smap_destroy(&row->%s);" % columnName else: if type.value: keyVar = "row->key_%s" % columnName valueVar = "row->value_%s" % columnName else: keyVar = "row->%s" % columnName valueVar = None print " free(%s);" % keyVar if valueVar: print " free(%s);" % valueVar print '}' else: print ''' static void %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED) { /* Nothing to do. */ }''' % {'s': structName, 'c': columnName} # Generic Row Initialization function. print """ static void %(s)s_init__(struct ovsdb_idl_row *row) { %(s)s_init(%(s)s_cast(row)); }""" % {'s': structName} # Row Initialization function. print """ /* Clears the contents of 'row' in table "%(t)s". */ void %(s)s_init(struct %(s)s *row) { memset(row, 0, sizeof *row); """ % {'s': structName, 't': tableName} for columnName, column in sorted(table.columns.iteritems()): if column.type.is_smap(): print " smap_init(&row->%s);" % columnName print "}" # First, next functions. print ''' /* Searches table "%(t)s" in 'idl' for a row with UUID 'uuid'. Returns * a pointer to the row if there is one, otherwise a null pointer. */ const struct %(s)s * %(s)s_get_for_uuid(const struct ovsdb_idl *idl, const struct uuid *uuid) { return %(s)s_cast(ovsdb_idl_get_row_for_uuid(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s], uuid)); } /* Returns a row in table "%(t)s" in 'idl', or a null pointer if that * table is empty. * * Database tables are internally maintained as hash tables, so adding or * removing rows while traversing the same table can cause some rows to be * visited twice or not at apply. */ const struct %(s)s * %(s)s_first(const struct ovsdb_idl *idl) { return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s])); } /* Returns a row following 'row' within its table, or a null pointer if 'row' * is the last row in its table. */ const struct %(s)s * %(s)s_next(const struct %(s)s *row) { return %(s)s_cast(ovsdb_idl_next_row(&row->header_)); } unsigned int %(s)s_get_seqno(const struct ovsdb_idl *idl) { return ovsdb_idl_table_get_seqno(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]); } unsigned int %(s)s_row_get_seqno(const struct %(s)s *row, enum ovsdb_idl_change change) { return ovsdb_idl_row_get_seqno(&row->header_, change); } const struct %(s)s * %(s)s_track_get_first(const struct ovsdb_idl *idl) { return %(s)s_cast(ovsdb_idl_track_get_first(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s])); } const struct %(s)s *%(s)s_track_get_next(const struct %(s)s *row) { return %(s)s_cast(ovsdb_idl_track_get_next(&row->header_)); }''' % {'s': structName, 'p': prefix, 'P': prefix.upper(), 't': tableName, 'T': tableName.upper()} print ''' /* Deletes 'row' from table "%(t)s". 'row' may be freed, so it must not be * accessed afterward. * * The caller must have started a transaction with ovsdb_idl_txn_create(). */ void %(s)s_delete(const struct %(s)s *row) { ovsdb_idl_txn_delete(&row->header_); } /* Inserts and returns a new row in the table "%(t)s" in the database * with open transaction 'txn'. * * The new row is assigned a randomly generated provisional UUID. * ovsdb-server will assign a different UUID when 'txn' is committed, * but the IDL will replace any uses of the provisional UUID in the * data to be to be committed by the UUID assigned by ovsdb-server. */ struct %(s)s * %(s)s_insert(struct ovsdb_idl_txn *txn) { return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s], NULL)); }''' % {'s': structName, 'p': prefix, 'P': prefix.upper(), 't': tableName, 'T': tableName.upper()} # Verify functions. for columnName, column in sorted(table.columns.iteritems()): print ''' /* Causes the original contents of column "%(c)s" in 'row' to be * verified as a prerequisite to completing the transaction. That is, if * "%(c)s" in 'row' changed (or if 'row' was deleted) between the * time that the IDL originally read its contents and the time that the * transaction commits, then the transaction aborts and ovsdb_idl_txn_commit() * returns TXN_AGAIN_WAIT or TXN_AGAIN_NOW (depending on whether the database * change has already been received). * * The intention is that, to ensure that no transaction commits based on dirty * reads, an application should call this function any time "%(c)s" is * read as part of a read-modify-write operation. * * In some cases this function reduces to a no-op, because the current value * of "%(c)s" is already known: * * - If 'row' is a row created by the current transaction (returned by * %(s)s_insert()). * * - If "%(c)s" has already been modified (with * %(s)s_set_%(c)s()) within the current transaction. * * Because of the latter property, always call this function *before* * %(s)s_set_%(c)s() for a given read-modify-write. * * The caller must have started a transaction with ovsdb_idl_txn_create(). */ void %(s)s_verify_%(c)s(const struct %(s)s *row) { ovs_assert(inited); ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]); }''' % {'s': structName, 'S': structName.upper(), 'c': columnName, 'C': columnName.upper()} # Get functions. for columnName, column in sorted(table.columns.iteritems()): if column.type.value: valueParam = ',\n\tenum ovsdb_atomic_type value_type OVS_UNUSED' valueType = '\n ovs_assert(value_type == %s);' % column.type.value.toAtomicType() valueComment = "\n * 'value_type' must be %s." % column.type.value.toAtomicType() else: valueParam = '' valueType = '' valueComment = '' print """ /* Returns the "%(c)s" column's value from the "%(t)s" table in 'row' * as a struct ovsdb_datum. This is useful occasionally: for example, * ovsdb_datum_find_key() is an easier and more efficient way to search * for a given key than implementing the same operation on the "cooked" * form in 'row'. * * 'key_type' must be %(kt)s.%(vc)s * (This helps to avoid silent bugs if someone changes %(c)s's * type without updating the caller.) * * The caller must not modify or free the returned value. * * Various kinds of changes can invalidate the returned value: modifying * 'column' within 'row', deleting 'row', or completing an ongoing transaction. * If the returned value is needed for a long time, it is best to make a copy * of it with ovsdb_datum_clone(). * * This function is rarely useful, since it is easier to access the value * directly through the "%(c)s" member in %(s)s. */ const struct ovsdb_datum * %(s)s_get_%(c)s(const struct %(s)s *row, \tenum ovsdb_atomic_type key_type OVS_UNUSED%(v)s) { ovs_assert(key_type == %(kt)s);%(vt)s return ovsdb_idl_read(&row->header_, &%(s)s_col_%(c)s); }""" % {'t': tableName, 's': structName, 'c': columnName, 'kt': column.type.key.toAtomicType(), 'v': valueParam, 'vt': valueType, 'vc': valueComment} # Set functions. for columnName, column in sorted(table.columns.iteritems()): type = column.type comment, members = cMembers(prefix, tableName, columnName, column, True) if type.is_smap(): print comment print """void %(s)s_set_%(c)s(const struct %(s)s *row, const struct smap *%(c)s) { struct ovsdb_datum datum; ovs_assert(inited); if (%(c)s) { struct smap_node *node; size_t i; datum.n = smap_count(%(c)s); datum.keys = xmalloc(datum.n * sizeof *datum.keys); datum.values = xmalloc(datum.n * sizeof *datum.values); i = 0; SMAP_FOR_EACH (node, %(c)s) { datum.keys[i].string = xstrdup(node->key); datum.values[i].string = xstrdup(node->value); i++; } ovsdb_datum_sort_unique(&datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); } else { ovsdb_datum_init_empty(&datum); } ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum); } """ % {'t': tableName, 's': structName, 'S': structName.upper(), 'c': columnName, 'C': columnName.upper()} continue keyVar = members[0]['name'] nVar = None valueVar = None if type.value: valueVar = members[1]['name'] if len(members) > 2: nVar = members[2]['name'] else: if len(members) > 1: nVar = members[1]['name'] print comment print 'void' print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \ {'s': structName, 'c': columnName, 'args': ', '.join(['%(type)s%(name)s' % m for m in members])} print "{" print " struct ovsdb_datum datum;" if type.n_min == 1 and type.n_max == 1: print " union ovsdb_atom key;" if type.value: print " union ovsdb_atom value;" print print " ovs_assert(inited);" print " datum.n = 1;" print " datum.keys = &key;" print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar) if type.value: print " datum.values = &value;" print " "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar) else: print " datum.values = NULL;" txn_write_func = "ovsdb_idl_txn_write_clone" elif type.is_optional_pointer(): print " union ovsdb_atom key;" print print " ovs_assert(inited);" print " if (%s) {" % keyVar print " datum.n = 1;" print " datum.keys = &key;" print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar) print " } else {" print " datum.n = 0;" print " datum.keys = NULL;" print " }" print " datum.values = NULL;" txn_write_func = "ovsdb_idl_txn_write_clone" elif type.n_max == 1: print " union ovsdb_atom key;" print print " ovs_assert(inited);" print " if (%s) {" % nVar print " datum.n = 1;" print " datum.keys = &key;" print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar) print " } else {" print " datum.n = 0;" print " datum.keys = NULL;" print " }" print " datum.values = NULL;" txn_write_func = "ovsdb_idl_txn_write_clone" else: print " size_t i;" print print " ovs_assert(inited);" print " datum.n = %s;" % nVar print " datum.keys = %s ? xmalloc(%s * sizeof *datum.keys) : NULL;" % (nVar, nVar) if type.value: print " datum.values = xmalloc(%s * sizeof *datum.values);" % nVar else: print " datum.values = NULL;" print " for (i = 0; i < %s; i++) {" % nVar print " " + type.key.copyCValue("datum.keys[i].%s" % type.key.type.to_string(), "%s[i]" % keyVar) if type.value: print " " + type.value.copyCValue("datum.values[i].%s" % type.value.type.to_string(), "%s[i]" % valueVar) print " }" if type.value: valueType = type.value.toAtomicType() else: valueType = "OVSDB_TYPE_VOID" print " ovsdb_datum_sort_unique(&datum, %s, %s);" % ( type.key.toAtomicType(), valueType) txn_write_func = "ovsdb_idl_txn_write" print " %(f)s(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \ % {'f': txn_write_func, 's': structName, 'S': structName.upper(), 'C': columnName.upper()} print "}" # Table columns. print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % ( structName, structName.upper()) print """ static void\n%s_columns_init(void) { struct ovsdb_idl_column *c;\ """ % structName for columnName, column in sorted(table.columns.iteritems()): cs = "%s_col_%s" % (structName, columnName) d = {'cs': cs, 'c': columnName, 's': structName} if column.mutable: mutable = "true" else: mutable = "false" print print " /* Initialize %(cs)s. */" % d print " c = &%(cs)s;" % d print " c->name = \"%(c)s\";" % d print column.type.cInitType(" ", "c->type") print " c->mutable = %s;" % mutable print " c->parse = %(s)s_parse_%(c)s;" % d print " c->unparse = %(s)s_unparse_%(c)s;" % d print "}" # Table classes. print " " print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper()) for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) if table.is_root: is_root = "true" else: is_root = "false" print " {\"%s\", %s," % (tableName, is_root) print " %s_columns, ARRAY_SIZE(%s_columns)," % ( structName, structName) print " sizeof(struct %s), %s_init__}," % (structName, structName) print "};" # IDL class. print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix print " \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % ( schema.name, prefix, prefix) print "};" # global init function print """ void %sinit(void) { if (inited) { return; } assert_single_threaded(); inited = true; """ % prefix for tableName, table in sorted(schema.tables.iteritems()): structName = "%s%s" % (prefix, tableName.lower()) print " %s_columns_init();" % structName print "}" print """ /* Return the schema version. The caller must not free the returned value. */ const char * %sget_db_version(void) { return "%s"; } """ % (prefix, schema.version) def ovsdb_escape(string): def escape(match): c = match.group(0) if c == '\0': raise ovs.db.error.Error("strings may not contain null bytes") elif c == '\\': return '\\\\' elif c == '\n': return '\\n' elif c == '\r': return '\\r' elif c == '\t': return '\\t' elif c == '\b': return '\\b' elif c == '\a': return '\\a' else: return '\\x%02x' % ord(c) return re.sub(r'["\\\000-\037]', escape, string) def usage(): print """\ %(argv0)s: ovsdb schema compiler usage: %(argv0)s [OPTIONS] COMMAND ARG... The following commands are supported: annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS c-idl-header IDL print C header file for IDL c-idl-source IDL print C source file for IDL implementation nroff IDL print schema documentation in nroff format The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV', ['directory', 'help', 'version']) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print "ovsdb-idlc (Open vSwitch) @VERSION@" elif key in ['-C', '--directory']: os.chdir(value) else: sys.exit(0) optKeys = [key for key, value in options] if not args: sys.stderr.write("%s: missing command argument " "(use --help for help)\n" % argv0) sys.exit(1) commands = {"annotate": (annotateSchema, 2), "c-idl-header": (printCIDLHeader, 1), "c-idl-source": (printCIDLSource, 1)} if not args[0] in commands: sys.stderr.write("%s: unknown command \"%s\" " "(use --help for help)\n" % (argv0, args[0])) sys.exit(1) func, n_args = commands[args[0]] if len(args) - 1 != n_args: sys.stderr.write("%s: \"%s\" requires %d arguments but %d " "provided\n" % (argv0, args[0], n_args, len(args) - 1)) sys.exit(1) func(*args[1:]) except ovs.db.error.Error, e: sys.stderr.write("%s: %s\n" % (argv0, e)) sys.exit(1) # Local variables: # mode: python # End: openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-client.1.in0000644000000000000000000000013213534540071020041 xustar0030 mtime=1567801401.813684195 30 atime=1567801402.121686458 30 ctime=1567801423.777846025 openvswitch-2.5.9/ovsdb/ovsdb-client.1.in0000644000175000017500000001447713534540071021544 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .\" -*- nroff -*- .TH ovsdb\-client 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovsdb\-client . .SH NAME ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1) . .SH SYNOPSIS \fBovsdb\-client \fR[\fIoptions\fR] \fBlist\-dbs \fR[\fIserver\fR] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBget\-schema \fR[\fIserver\fR] \fR[\fIdatabase\fR] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBget\-schema\-version\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBlist\-tables\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBlist\-columns\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] [\fItable\fR] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBtransact\fI \fR[\fIserver\fR] \fItransaction\fR .br \fBovsdb\-client \fR[\fIoptions\fR] \fBdump\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]\fR [\fItable\fR [\fIcolumn\fR...]] .br \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]... .br \fBovsdb\-client \fR[\fIoptions\fR] \fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR .br \fBovsdb\-client help\fR .IP "Output formatting options:" [\fB\-\-format=\fIformat\fR] [\fB\-\-data=\fIformat\fR] [\fB\-\-no-heading\fR] [\fB\-\-pretty\fR] [\fB\-\-bare\fR] [\fB\-\-no\-heading\fR] [\fB\-\-timestamp\fR] .so lib/daemon-syn.man .so lib/vlog-syn.man .so lib/ssl-syn.man .so lib/ssl-bootstrap-syn.man .so lib/common-syn.man . .SH DESCRIPTION The \fBovsdb\-client\fR program is a command-line client for interacting with a running \fBovsdb\-server\fR process. Each command connects to an OVSDB server, which is \fBunix:@RUNDIR@/db.sock\fR by default, or may be specified as \fIserver\fR in one of the following forms: .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE .PP The default \fIdatabase\fR is \fBOpen_vSwitch\fR. . .SS "Commands" The following commands are implemented: .IP "\fBlist\-dbs \fR[\fIserver\fR]" Connects to \fIserver\fR, retrieves the list of known databases, and prints them one per line. These database names are the ones that may be used for \fIdatabase\fR in the following commands. . .IP "\fBget\-schema \fR[\fIserver\fR] \fR[\fIdatabase\fR]" Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and prints it in JSON format. . .IP "\fBget\-schema\-version\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]" Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and prints its version number on stdout. A schema version number has the form \fIx\fB.\fIy\fB.\fIz\fR. See \fBovs\-vswitchd.conf.db\fR(5) for details. .IP Schema version numbers and Open vSwitch version numbers are independent. .IP If \fIdatabase\fR was created before schema versioning was introduced, then it will not have a version number and this command will print a blank line. . .IP "\fBlist\-tables\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]" Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and prints a table listing the name of each table within the database. . .IP "\fBlist\-columns\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR" Connects to \fIserver\fR, retrieves the schema for \fIdatabase\fR, and prints a table listing the name and type of each column. If \fItable\fR is specified, only columns in that table are listed; otherwise, the tables include columns in all tables. . .IP "\fBtransact\fI \fR[\fIserver\fR] \fItransaction\fR" Connects to \fIserver\fR, sends it the specified \fItransaction\fR, which must be a JSON array containing one or more valid OVSDB operations, and prints the received reply on stdout. . .IP "\fBdump\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR]\fR [\fItable \fR[\fIcolumn\fR...]]" Connects to \fIserver\fR, retrieves all of the data in \fIdatabase\fR, and prints it on stdout as a series of tables. If \fItable\fR is specified, only that table is retrieved. If at least one \fIcolumn\fR is specified, only those columns are retrieved. . .IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fItable\fR [\fIcolumn\fR[\fB,\fIcolumn\fR]...]..." Connects to \fIserver\fR and monitors the contents of \fItable\fR in \fIdatabase\fR. By default, the initial contents of \fItable\fR are printed, followed by each change as it occurs. If at least one \fIcolumn\fR is specified, only those columns are monitored. The following \fIcolumn\fR names have special meanings: .RS .IP "\fB!initial\fR" Do not print the initial contents of the specified columns. .IP "\fB!insert\fR" Do not print newly inserted rows. .IP "\fB!delete\fR" Do not print deleted rows. .IP "\fB!modify\fR" Do not print modifications to existing rows. .RE .IP Multiple [\fIcolumn\fR[\fB,\fIcolumn\fR]...] groups may be specified as separate arguments, e.g. to apply different reporting parameters to each group. Whether multiple groups or only a single group is specified, any given column may only be mentioned once on the command line. .IP If \fB\-\-detach\fR is used with \fBmonitor\fR, then \fBovsdb\-client\fR detaches after it has successfully received and printed the initial contents of \fItable\fR. . .IP "\fBmonitor\fI \fR[\fIserver\fR] \fR[\fIdatabase\fR] \fBALL\fR" Connects to \fIserver\fR and monitors the contents of all tables in \fIdatabase\fR. Prints initial values and all kinds of changes to all columns in the database. The \fB\-\-detach\fR option causes \fBovsdb\-client\fR to detach after it successfully receives and prints the initial database contents. . .SH OPTIONS .SS "Output Formatting Options" Much of the output from \fBovsdb\-client\fR is in the form of tables. The following options controlling output formatting: . .ds TD (default) .so lib/table.man . .IP "\fB\-\-timestamp\fR" For the \fBmonitor\fR command, adds a timestamp to each table update. Most output formats add the timestamp on a line of its own just above the table. The JSON output format puts the timestamp in a member of the top-level JSON object named \fBtime\fR. . .SS "Daemon Options" The daemon options apply only to the \fBmonitor\fR command. With any other command, they have no effect. .ds DD .so lib/daemon.man .SS "Logging Options" .so lib/vlog.man .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .SS "Other Options" .so lib/common.man .SH "SEE ALSO" . \fBovsdb\-server\fR(1), \fBovsdb\-client\fR(1), and the OVSDB specification. openvswitch-2.5.9/ovsdb/PaxHeaders.82075/remote-active.man0000644000000000000000000000013213534540071020222 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801423.757845878 openvswitch-2.5.9/ovsdb/remote-active.man0000644000175000017500000000146413534540071021715 0ustar00jpettitjpettit00000000000000.IP "\fBssl:\fIip\fB:\fIport\fR" The specified SSL \fIport\fR on the host at the given \fIip\fR, which must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square brackets, e.g.: \fBssl:[::1]:6640\fR. The \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory when this form is used. . .IP "\fBtcp:\fIip\fB:\fIport\fR" Connect to the given TCP \fIport\fR on \fIip\fR, where \fIip\fR can be IPv4 or IPv6 address. If \fIip\fR is an IPv6 address, then wrap \fIip\fR with square brackets, e.g.: \fBtcp:[::1]:6640\fR. . .IP "\fBunix:\fIfile\fR" On POSIX, connect to the Unix domain server socket named \fIfile\fR. .IP On Windows, connect to a localhost TCP port whose value is written in \fIfile\fR. openvswitch-2.5.9/ovsdb/PaxHeaders.82075/server.h0000644000000000000000000000013213534540071016440 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.097855756 openvswitch-2.5.9/ovsdb/server.h0000644000175000017500000000705013534540071020130 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SERVER_H #define SERVER_H 1 #include "hmap.h" #include "list.h" #include "shash.h" struct ovsdb; struct ovsdb_server; /* Abstract representation of an OVSDB client connection, not tied to any * particular network protocol. Protocol implementations * (e.g. jsonrpc-server.c) embed this in a larger data structure. */ struct ovsdb_session { struct ovsdb_server *server; struct ovs_list completions;/* Completed triggers. */ struct hmap waiters; /* "ovsdb_lock_waiter *"s by lock name. */ }; void ovsdb_session_init(struct ovsdb_session *, struct ovsdb_server *); void ovsdb_session_destroy(struct ovsdb_session *); struct ovsdb_lock_waiter *ovsdb_session_get_lock_waiter( const struct ovsdb_session *, const char *lock_name); /* A database lock. * * A lock always has one or more "lock waiters" kept on a list. The waiter at * the head of the list owns the lock. */ struct ovsdb_lock { struct hmap_node hmap_node; /* In ovsdb_server's "locks" hmap. */ struct ovsdb_server *server; /* The containing server. */ char *name; /* Unique name. */ struct ovs_list waiters; /* Contains "struct ovsdb_lock_waiter"s. */ }; struct ovsdb_lock_waiter *ovsdb_lock_get_owner(const struct ovsdb_lock *); /* How to obtain a lock. */ enum ovsdb_lock_mode { OVSDB_LOCK_WAIT, /* By waiting for it to become available. */ OVSDB_LOCK_STEAL /* By stealing it from the owner. */ }; /* A session's request for a database lock. */ struct ovsdb_lock_waiter { struct hmap_node session_node; /* In ->session->locks's hmap. */ struct ovsdb_lock *lock; /* The lock being waited for. */ enum ovsdb_lock_mode mode; char *lock_name; struct ovsdb_session *session; struct ovs_list lock_node; /* In ->lock->waiters's list. */ }; struct ovsdb_session *ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *); void ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *); bool ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *); /* Abstract representation of an OVSDB server not tied to any particular * network protocol. Protocol implementations (e.g. jsonrpc-server.c) embed * this in a larger data structure. */ struct ovsdb_server { struct shash dbs; /* Maps from a db name to a "struct ovsdb *". */ struct hmap locks; /* Contains "struct ovsdb_lock"s indexed by name. */ }; void ovsdb_server_init(struct ovsdb_server *); bool ovsdb_server_add_db(struct ovsdb_server *, struct ovsdb *); bool ovsdb_server_remove_db(struct ovsdb_server *, struct ovsdb *); void ovsdb_server_destroy(struct ovsdb_server *); struct ovsdb_lock_waiter *ovsdb_server_lock(struct ovsdb_server *, struct ovsdb_session *, const char *lock_name, enum ovsdb_lock_mode, struct ovsdb_session **victimp); #endif /* ovsdb/server.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/transaction.h0000644000000000000000000000013213534540071017457 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.101855786 openvswitch-2.5.9/ovsdb/transaction.h0000644000175000017500000000333413534540071021150 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_TRANSACTION_H #define OVSDB_TRANSACTION_H 1 #include #include "compiler.h" struct json; struct ovsdb; struct ovsdb_table; struct uuid; struct ovsdb_txn *ovsdb_txn_create(struct ovsdb *); void ovsdb_txn_abort(struct ovsdb_txn *); struct ovsdb_error *ovsdb_txn_commit(struct ovsdb_txn *, bool durable) OVS_WARN_UNUSED_RESULT; struct ovsdb_row *ovsdb_txn_row_modify(struct ovsdb_txn *, const struct ovsdb_row *); void ovsdb_txn_row_insert(struct ovsdb_txn *, struct ovsdb_row *); void ovsdb_txn_row_delete(struct ovsdb_txn *, const struct ovsdb_row *); typedef bool ovsdb_txn_row_cb_func(const struct ovsdb_row *old, const struct ovsdb_row *new, const unsigned long int *changed, void *aux); void ovsdb_txn_for_each_change(const struct ovsdb_txn *, ovsdb_txn_row_cb_func *, void *aux); void ovsdb_txn_add_comment(struct ovsdb_txn *, const char *); const char *ovsdb_txn_get_comment(const struct ovsdb_txn *); #endif /* ovsdb/transaction.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb.c0000644000000000000000000000013213534540071016242 xustar0030 mtime=1567801401.821684255 30 atime=1567801402.121686458 30 ctime=1567801425.085855667 openvswitch-2.5.9/ovsdb/ovsdb.c0000644000175000017500000003150213534540071017731 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb.h" #include "column.h" #include "json.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb-types.h" #include "simap.h" #include "table.h" #include "transaction.h" struct ovsdb_schema * ovsdb_schema_create(const char *name, const char *version, const char *cksum) { struct ovsdb_schema *schema; schema = xzalloc(sizeof *schema); schema->name = xstrdup(name); schema->version = xstrdup(version); schema->cksum = xstrdup(cksum); shash_init(&schema->tables); return schema; } struct ovsdb_schema * ovsdb_schema_clone(const struct ovsdb_schema *old) { struct ovsdb_schema *new; struct shash_node *node; new = ovsdb_schema_create(old->name, old->version, old->cksum); SHASH_FOR_EACH (node, &old->tables) { const struct ovsdb_table_schema *ts = node->data; shash_add(&new->tables, node->name, ovsdb_table_schema_clone(ts)); } return new; } void ovsdb_schema_destroy(struct ovsdb_schema *schema) { struct shash_node *node; if (!schema) { return; } SHASH_FOR_EACH (node, &schema->tables) { ovsdb_table_schema_destroy(node->data); } shash_destroy(&schema->tables); free(schema->name); free(schema->version); free(schema->cksum); free(schema); } struct ovsdb_error * ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **schemap) { struct ovsdb_schema *schema; struct ovsdb_error *error; struct json *json; *schemap = NULL; json = json_from_file(file_name); if (json->type == JSON_STRING) { error = ovsdb_error("failed to read schema", "\"%s\" could not be read as JSON (%s)", file_name, json_string(json)); json_destroy(json); return error; } error = ovsdb_schema_from_json(json, &schema); json_destroy(json); if (error) { return ovsdb_wrap_error(error, "failed to parse \"%s\" as ovsdb schema", file_name); } *schemap = schema; return NULL; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_schema_check_ref_table(struct ovsdb_column *column, const struct shash *tables, const struct ovsdb_base_type *base, const char *base_name) { struct ovsdb_table_schema *refTable; if (base->type != OVSDB_TYPE_UUID || !base->u.uuid.refTableName) { return NULL; } refTable = shash_find_data(tables, base->u.uuid.refTableName); if (!refTable) { return ovsdb_syntax_error(NULL, NULL, "column %s %s refers to undefined table %s", column->name, base_name, base->u.uuid.refTableName); } if (ovsdb_base_type_is_strong_ref(base) && !refTable->is_root) { /* We cannot allow a strong reference to a non-root table to be * ephemeral: if it is the only reference to a row, then replaying the * database log from disk will cause the referenced row to be deleted, * even though it did exist in memory. If there are references to that * row later in the log (to modify it, to delete it, or just to point * to it), then this will yield a transaction error. */ column->persistent = true; } return NULL; } static bool is_valid_version(const char *s) { int n = -1; ignore(ovs_scan(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n)); return n != -1 && s[n] == '\0'; } /* Returns the number of tables in 'schema''s root set. */ static size_t root_set_size(const struct ovsdb_schema *schema) { struct shash_node *node; size_t n_root = 0; SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; n_root += table->is_root; } return n_root; } struct ovsdb_error * ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap) { struct ovsdb_schema *schema; const struct json *name, *tables, *version_json, *cksum; struct ovsdb_error *error; struct shash_node *node; struct ovsdb_parser parser; const char *version; *schemap = NULL; ovsdb_parser_init(&parser, json, "database schema"); name = ovsdb_parser_member(&parser, "name", OP_ID); version_json = ovsdb_parser_member(&parser, "version", OP_STRING | OP_OPTIONAL); cksum = ovsdb_parser_member(&parser, "cksum", OP_STRING | OP_OPTIONAL); tables = ovsdb_parser_member(&parser, "tables", OP_OBJECT); error = ovsdb_parser_finish(&parser); if (error) { return error; } if (version_json) { version = json_string(version_json); if (!is_valid_version(version)) { return ovsdb_syntax_error(json, NULL, "schema version \"%s\" not " "in format x.y.z", version); } } else { /* Backward compatibility with old databases. */ version = ""; } schema = ovsdb_schema_create(json_string(name), version, cksum ? json_string(cksum) : ""); SHASH_FOR_EACH (node, json_object(tables)) { struct ovsdb_table_schema *table; if (node->name[0] == '_') { error = ovsdb_syntax_error(json, NULL, "names beginning with " "\"_\" are reserved"); } else if (!ovsdb_parser_is_id(node->name)) { error = ovsdb_syntax_error(json, NULL, "name must be a valid id"); } else { error = ovsdb_table_schema_from_json(node->data, node->name, &table); } if (error) { ovsdb_schema_destroy(schema); return error; } shash_add(&schema->tables, table->name, table); } /* "isRoot" was not part of the original schema definition. Before it was * added, there was no support for garbage collection. So, for backward * compatibility, if the root set is empty then assume that every table is * in the root set. */ if (root_set_size(schema) == 0) { SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; table->is_root = true; } } /* Validate that all refTables refer to the names of tables that exist. * * Also force certain columns to be persistent, as explained in * ovsdb_schema_check_ref_table(). This requires 'is_root' to be known, so * this must follow the loop updating 'is_root' above. */ SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; struct shash_node *node2; SHASH_FOR_EACH (node2, &table->columns) { struct ovsdb_column *column = node2->data; error = ovsdb_schema_check_ref_table(column, &schema->tables, &column->type.key, "key"); if (!error) { error = ovsdb_schema_check_ref_table(column, &schema->tables, &column->type.value, "value"); } if (error) { ovsdb_schema_destroy(schema); return error; } } } *schemap = schema; return NULL; } struct json * ovsdb_schema_to_json(const struct ovsdb_schema *schema) { struct json *json, *tables; struct shash_node *node; bool default_is_root; json = json_object_create(); json_object_put_string(json, "name", schema->name); if (schema->version[0]) { json_object_put_string(json, "version", schema->version); } if (schema->cksum[0]) { json_object_put_string(json, "cksum", schema->cksum); } /* "isRoot" was not part of the original schema definition. Before it was * added, there was no support for garbage collection. So, for backward * compatibility, if every table is in the root set then do not output * "isRoot" in table schemas. */ default_is_root = root_set_size(schema) == shash_count(&schema->tables); tables = json_object_create(); SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; json_object_put(tables, table->name, ovsdb_table_schema_to_json(table, default_is_root)); } json_object_put(json, "tables", tables); return json; } /* Returns true if 'a' and 'b' specify equivalent schemas, false if they * differ. */ bool ovsdb_schema_equal(const struct ovsdb_schema *a, const struct ovsdb_schema *b) { /* This implementation is simple, stupid, and slow, but I doubt that it * will ever require much maintenance. */ struct json *ja = ovsdb_schema_to_json(a); struct json *jb = ovsdb_schema_to_json(b); bool equals = json_equal(ja, jb); json_destroy(ja); json_destroy(jb); return equals; } static void ovsdb_set_ref_table(const struct shash *tables, struct ovsdb_base_type *base) { if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { struct ovsdb_table *table; table = shash_find_data(tables, base->u.uuid.refTableName); base->u.uuid.refTable = table; } } struct ovsdb * ovsdb_create(struct ovsdb_schema *schema) { struct shash_node *node; struct ovsdb *db; db = xmalloc(sizeof *db); db->schema = schema; list_init(&db->replicas); list_init(&db->triggers); db->run_triggers = false; shash_init(&db->tables); SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *ts = node->data; shash_add(&db->tables, node->name, ovsdb_table_create(ts)); } /* Set all the refTables. */ SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *table = node->data; struct shash_node *node2; SHASH_FOR_EACH (node2, &table->columns) { struct ovsdb_column *column = node2->data; ovsdb_set_ref_table(&db->tables, &column->type.key); ovsdb_set_ref_table(&db->tables, &column->type.value); } } return db; } void ovsdb_destroy(struct ovsdb *db) { if (db) { struct shash_node *node; /* Remove all the replicas. */ while (!list_is_empty(&db->replicas)) { struct ovsdb_replica *r = CONTAINER_OF(list_pop_back(&db->replicas), struct ovsdb_replica, node); ovsdb_remove_replica(db, r); } /* Delete all the tables. This also deletes their schemas. */ SHASH_FOR_EACH (node, &db->tables) { struct ovsdb_table *table = node->data; ovsdb_table_destroy(table); } shash_destroy(&db->tables); /* The schemas, but not the table that points to them, were deleted in * the previous step, so we need to clear out the table. We can't * destroy the table, because ovsdb_schema_destroy() will do that. */ shash_clear(&db->schema->tables); ovsdb_schema_destroy(db->schema); free(db); } } /* Adds some memory usage statistics for 'db' into 'usage', for use with * memory_report(). */ void ovsdb_get_memory_usage(const struct ovsdb *db, struct simap *usage) { const struct shash_node *node; unsigned int cells = 0; SHASH_FOR_EACH (node, &db->tables) { const struct ovsdb_table *table = node->data; unsigned int n_columns = shash_count(&table->schema->columns); unsigned int n_rows = hmap_count(&table->rows); cells += n_rows * n_columns; } simap_increase(usage, "cells", cells); } struct ovsdb_table * ovsdb_get_table(const struct ovsdb *db, const char *name) { return shash_find_data(&db->tables, name); } void ovsdb_replica_init(struct ovsdb_replica *r, const struct ovsdb_replica_class *class) { r->class = class; } void ovsdb_add_replica(struct ovsdb *db, struct ovsdb_replica *r) { list_push_back(&db->replicas, &r->node); } void ovsdb_remove_replica(struct ovsdb *db OVS_UNUSED, struct ovsdb_replica *r) { list_remove(&r->node); (r->class->destroy)(r); } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb.h0000644000000000000000000000013213534540071016247 xustar0030 mtime=1567801401.821684255 30 atime=1567801402.121686458 30 ctime=1567801425.089855696 openvswitch-2.5.9/ovsdb/ovsdb.h0000644000175000017500000000632213534540071017740 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_OVSDB_H #define OVSDB_OVSDB_H 1 #include "compiler.h" #include "hmap.h" #include "list.h" #include "shash.h" struct json; struct ovsdb_log; struct ovsdb_session; struct ovsdb_txn; struct simap; struct uuid; /* Database schema. */ struct ovsdb_schema { char *name; char *version; char *cksum; struct shash tables; /* Contains "struct ovsdb_table_schema *"s. */ }; struct ovsdb_schema *ovsdb_schema_create(const char *name, const char *version, const char *cksum); struct ovsdb_schema *ovsdb_schema_clone(const struct ovsdb_schema *); void ovsdb_schema_destroy(struct ovsdb_schema *); struct ovsdb_error *ovsdb_schema_from_file(const char *file_name, struct ovsdb_schema **) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_schema_from_json(struct json *, struct ovsdb_schema **) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_schema_to_json(const struct ovsdb_schema *); bool ovsdb_schema_equal(const struct ovsdb_schema *, const struct ovsdb_schema *); /* Database. */ struct ovsdb { struct ovsdb_schema *schema; struct ovs_list replicas; /* Contains "struct ovsdb_replica"s. */ struct shash tables; /* Contains "struct ovsdb_table *"s. */ /* Triggers. */ struct ovs_list triggers; /* Contains "struct ovsdb_trigger"s. */ bool run_triggers; }; struct ovsdb *ovsdb_create(struct ovsdb_schema *); void ovsdb_destroy(struct ovsdb *); void ovsdb_get_memory_usage(const struct ovsdb *, struct simap *usage); struct ovsdb_table *ovsdb_get_table(const struct ovsdb *, const char *); struct json *ovsdb_execute(struct ovsdb *, const struct ovsdb_session *, const struct json *params, long long int elapsed_msec, long long int *timeout_msec); /* Database replication. */ struct ovsdb_replica { struct ovs_list node; /* Element in "struct ovsdb" replicas list. */ const struct ovsdb_replica_class *class; }; struct ovsdb_replica_class { struct ovsdb_error *(*commit)(struct ovsdb_replica *, const struct ovsdb_txn *, bool durable); void (*destroy)(struct ovsdb_replica *); }; void ovsdb_replica_init(struct ovsdb_replica *, const struct ovsdb_replica_class *); void ovsdb_add_replica(struct ovsdb *, struct ovsdb_replica *); void ovsdb_remove_replica(struct ovsdb *, struct ovsdb_replica *); #endif /* ovsdb/ovsdb.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/monitor.c0000644000000000000000000000013213534540071016614 xustar0030 mtime=1567801401.809684167 30 atime=1567801402.121686458 30 ctime=1567801425.089855696 openvswitch-2.5.9/ovsdb/monitor.c0000644000175000017500000010032113534540071020277 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "bitmap.h" #include "column.h" #include "dynamic-string.h" #include "json.h" #include "jsonrpc.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "ovsdb.h" #include "row.h" #include "simap.h" #include "hash.h" #include "table.h" #include "hash.h" #include "timeval.h" #include "transaction.h" #include "jsonrpc-server.h" #include "monitor.h" #include "openvswitch/vlog.h" static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class; static struct hmap ovsdb_monitors = HMAP_INITIALIZER(&ovsdb_monitors); /* Backend monitor. * * ovsdb_monitor keep track of the ovsdb changes. */ /* A collection of tables being monitored. */ struct ovsdb_monitor { struct ovsdb_replica replica; struct shash tables; /* Holds "struct ovsdb_monitor_table"s. */ struct ovs_list jsonrpc_monitors; /* Contains "jsonrpc_monitor_node"s. */ struct ovsdb *db; uint64_t n_transactions; /* Count number of committed transactions. */ struct hmap_node hmap_node; /* Elements within ovsdb_monitors. */ struct hmap json_cache; /* Contains "ovsdb_monitor_json_cache_node"s.*/ }; /* A json object of updates between 'from_txn' and 'dbmon->n_transactions' * inclusive. */ struct ovsdb_monitor_json_cache_node { struct hmap_node hmap_node; /* Elements in json cache. */ uint64_t from_txn; struct json *json; /* Null, or a cloned of json */ }; struct jsonrpc_monitor_node { struct ovsdb_jsonrpc_monitor *jsonrpc_monitor; struct ovs_list node; }; /* A particular column being monitored. */ struct ovsdb_monitor_column { const struct ovsdb_column *column; enum ovsdb_monitor_selection select; }; /* A row that has changed in a monitored table. */ struct ovsdb_monitor_row { struct hmap_node hmap_node; /* In ovsdb_jsonrpc_monitor_table.changes. */ struct uuid uuid; /* UUID of row that changed. */ struct ovsdb_datum *old; /* Old data, NULL for an inserted row. */ struct ovsdb_datum *new; /* New data, NULL for a deleted row. */ }; /* Contains 'struct ovsdb_monitor_row's for rows that have been * updated but not yet flushed to all the jsonrpc connection. * * 'n_refs' represent the number of jsonrpc connections that have * not received updates. Generate the update for the last jsonprc * connection will also destroy the whole "struct ovsdb_monitor_changes" * object. * * 'transaction' stores the first update's transaction id. * */ struct ovsdb_monitor_changes { struct ovsdb_monitor_table *mt; struct hmap rows; int n_refs; uint64_t transaction; struct hmap_node hmap_node; /* Element in ovsdb_monitor_tables' changes hmap. */ }; /* A particular table being monitored. */ struct ovsdb_monitor_table { const struct ovsdb_table *table; /* This is the union (bitwise-OR) of the 'select' values in all of the * members of 'columns' below. */ enum ovsdb_monitor_selection select; /* Columns being monitored. */ struct ovsdb_monitor_column *columns; size_t n_columns; /* Contains 'ovsdb_monitor_changes' indexed by 'transaction'. */ struct hmap changes; }; static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon); static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes( struct ovsdb_monitor_table *mt, uint64_t next_txn); static struct ovsdb_monitor_changes *ovsdb_monitor_table_find_changes( struct ovsdb_monitor_table *mt, uint64_t unflushed); static void ovsdb_monitor_changes_destroy( struct ovsdb_monitor_changes *changes); static void ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt, uint64_t unflushed); static struct ovsdb_monitor_json_cache_node * ovsdb_monitor_json_cache_search(const struct ovsdb_monitor *dbmon, uint64_t from_txn) { struct ovsdb_monitor_json_cache_node *node; uint32_t hash = hash_uint64(from_txn); HMAP_FOR_EACH_WITH_HASH(node, hmap_node, hash, &dbmon->json_cache) { if (node->from_txn == from_txn) { return node; } } return NULL; } static void ovsdb_monitor_json_cache_insert(struct ovsdb_monitor *dbmon, uint64_t from_txn, struct json *json) { struct ovsdb_monitor_json_cache_node *node; uint32_t hash; node = xmalloc(sizeof *node); hash = hash_uint64(from_txn); node->from_txn = from_txn; node->json = json ? json_clone(json) : NULL; hmap_insert(&dbmon->json_cache, &node->hmap_node, hash); } static void ovsdb_monitor_json_cache_flush(struct ovsdb_monitor *dbmon) { struct ovsdb_monitor_json_cache_node *node, *next; HMAP_FOR_EACH_SAFE(node, next, hmap_node, &dbmon->json_cache) { hmap_remove(&dbmon->json_cache, &node->hmap_node); json_destroy(node->json); free(node); } } static int compare_ovsdb_monitor_column(const void *a_, const void *b_) { const struct ovsdb_monitor_column *a = a_; const struct ovsdb_monitor_column *b = b_; return a->column < b->column ? -1 : a->column > b->column; } static struct ovsdb_monitor * ovsdb_monitor_cast(struct ovsdb_replica *replica) { ovs_assert(replica->class == &ovsdb_jsonrpc_replica_class); return CONTAINER_OF(replica, struct ovsdb_monitor, replica); } /* Finds and returns the ovsdb_monitor_row in 'mt->changes->rows' for the * given 'uuid', or NULL if there is no such row. */ static struct ovsdb_monitor_row * ovsdb_monitor_changes_row_find(const struct ovsdb_monitor_changes *changes, const struct uuid *uuid) { struct ovsdb_monitor_row *row; HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &changes->rows) { if (uuid_equals(uuid, &row->uuid)) { return row; } } return NULL; } /* Allocates an array of 'mt->n_columns' ovsdb_datums and initializes them as * copies of the data in 'row' drawn from the columns represented by * mt->columns[]. Returns the array. * * If 'row' is NULL, returns NULL. */ static struct ovsdb_datum * clone_monitor_row_data(const struct ovsdb_monitor_table *mt, const struct ovsdb_row *row) { struct ovsdb_datum *data; size_t i; if (!row) { return NULL; } data = xmalloc(mt->n_columns * sizeof *data); for (i = 0; i < mt->n_columns; i++) { const struct ovsdb_column *c = mt->columns[i].column; const struct ovsdb_datum *src = &row->fields[c->index]; struct ovsdb_datum *dst = &data[i]; const struct ovsdb_type *type = &c->type; ovsdb_datum_clone(dst, src, type); } return data; } /* Replaces the mt->n_columns ovsdb_datums in row[] by copies of the data from * in 'row' drawn from the columns represented by mt->columns[]. */ static void update_monitor_row_data(const struct ovsdb_monitor_table *mt, const struct ovsdb_row *row, struct ovsdb_datum *data) { size_t i; for (i = 0; i < mt->n_columns; i++) { const struct ovsdb_column *c = mt->columns[i].column; const struct ovsdb_datum *src = &row->fields[c->index]; struct ovsdb_datum *dst = &data[i]; const struct ovsdb_type *type = &c->type; if (!ovsdb_datum_equals(src, dst, type)) { ovsdb_datum_destroy(dst, type); ovsdb_datum_clone(dst, src, type); } } } /* Frees all of the mt->n_columns ovsdb_datums in data[], using the types taken * from mt->columns[], plus 'data' itself. */ static void free_monitor_row_data(const struct ovsdb_monitor_table *mt, struct ovsdb_datum *data) { if (data) { size_t i; for (i = 0; i < mt->n_columns; i++) { const struct ovsdb_column *c = mt->columns[i].column; ovsdb_datum_destroy(&data[i], &c->type); } free(data); } } /* Frees 'row', which must have been created from 'mt'. */ static void ovsdb_monitor_row_destroy(const struct ovsdb_monitor_table *mt, struct ovsdb_monitor_row *row) { if (row) { free_monitor_row_data(mt, row->old); free_monitor_row_data(mt, row->new); free(row); } } void ovsdb_monitor_add_jsonrpc_monitor(struct ovsdb_monitor *dbmon, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor) { struct jsonrpc_monitor_node *jm; jm = xzalloc(sizeof *jm); jm->jsonrpc_monitor = jsonrpc_monitor; list_push_back(&dbmon->jsonrpc_monitors, &jm->node); } struct ovsdb_monitor * ovsdb_monitor_create(struct ovsdb *db, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor) { struct ovsdb_monitor *dbmon; dbmon = xzalloc(sizeof *dbmon); ovsdb_replica_init(&dbmon->replica, &ovsdb_jsonrpc_replica_class); ovsdb_add_replica(db, &dbmon->replica); list_init(&dbmon->jsonrpc_monitors); dbmon->db = db; dbmon->n_transactions = 0; shash_init(&dbmon->tables); hmap_node_nullify(&dbmon->hmap_node); hmap_init(&dbmon->json_cache); ovsdb_monitor_add_jsonrpc_monitor(dbmon, jsonrpc_monitor); return dbmon; } void ovsdb_monitor_add_table(struct ovsdb_monitor *m, const struct ovsdb_table *table) { struct ovsdb_monitor_table *mt; mt = xzalloc(sizeof *mt); mt->table = table; shash_add(&m->tables, table->schema->name, mt); hmap_init(&mt->changes); } void ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, const struct ovsdb_column *column, enum ovsdb_monitor_selection select, size_t *allocated_columns) { struct ovsdb_monitor_table *mt; struct ovsdb_monitor_column *c; mt = shash_find_data(&dbmon->tables, table->schema->name); if (mt->n_columns >= *allocated_columns) { mt->columns = x2nrealloc(mt->columns, allocated_columns, sizeof *mt->columns); } mt->select |= select; c = &mt->columns[mt->n_columns++]; c->column = column; c->select = select; } /* Check for duplicated column names. Return the first * duplicated column's name if found. Otherwise return * NULL. */ const char * OVS_WARN_UNUSED_RESULT ovsdb_monitor_table_check_duplicates(struct ovsdb_monitor *m, const struct ovsdb_table *table) { struct ovsdb_monitor_table *mt; int i; mt = shash_find_data(&m->tables, table->schema->name); if (mt) { /* Check for duplicate columns. */ qsort(mt->columns, mt->n_columns, sizeof *mt->columns, compare_ovsdb_monitor_column); for (i = 1; i < mt->n_columns; i++) { if (mt->columns[i].column == mt->columns[i - 1].column) { return mt->columns[i].column->name; } } } return NULL; } static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes(struct ovsdb_monitor_table *mt, uint64_t next_txn) { struct ovsdb_monitor_changes *changes; changes = xzalloc(sizeof *changes); changes->transaction = next_txn; changes->mt = mt; changes->n_refs = 1; hmap_init(&changes->rows); hmap_insert(&mt->changes, &changes->hmap_node, hash_uint64(next_txn)); return changes; }; static struct ovsdb_monitor_changes * ovsdb_monitor_table_find_changes(struct ovsdb_monitor_table *mt, uint64_t transaction) { struct ovsdb_monitor_changes *changes; size_t hash = hash_uint64(transaction); HMAP_FOR_EACH_WITH_HASH(changes, hmap_node, hash, &mt->changes) { if (changes->transaction == transaction) { return changes; } } return NULL; } /* Stop currently tracking changes to table 'mt' since 'transaction'. */ static void ovsdb_monitor_table_untrack_changes(struct ovsdb_monitor_table *mt, uint64_t transaction) { struct ovsdb_monitor_changes *changes = ovsdb_monitor_table_find_changes(mt, transaction); if (changes) { if (--changes->n_refs == 0) { hmap_remove(&mt->changes, &changes->hmap_node); ovsdb_monitor_changes_destroy(changes); } } } /* Start tracking changes to table 'mt' begins from 'transaction' inclusive. */ static void ovsdb_monitor_table_track_changes(struct ovsdb_monitor_table *mt, uint64_t transaction) { struct ovsdb_monitor_changes *changes; changes = ovsdb_monitor_table_find_changes(mt, transaction); if (changes) { changes->n_refs++; } else { ovsdb_monitor_table_add_changes(mt, transaction); } } static void ovsdb_monitor_changes_destroy(struct ovsdb_monitor_changes *changes) { struct ovsdb_monitor_row *row, *next; HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { hmap_remove(&changes->rows, &row->hmap_node); ovsdb_monitor_row_destroy(changes->mt, row); } hmap_destroy(&changes->rows); free(changes); } static enum ovsdb_monitor_selection ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new) { return initial ? OJMS_INITIAL : !old ? OJMS_INSERT : !new ? OJMS_DELETE : OJMS_MODIFY; } /* Returns JSON for a (as described in RFC 7047) for 'row' within * 'mt', or NULL if no row update should be sent. * * The caller should specify 'initial' as true if the returned JSON is going to * be used as part of the initial reply to a "monitor" request, false if it is * going to be used as part of an "update" notification. * * 'changed' must be a scratch buffer for internal use that is at least * bitmap_n_bytes(mt->n_columns) bytes long. */ static struct json * ovsdb_monitor_compose_row_update( const struct ovsdb_monitor_table *mt, const struct ovsdb_monitor_row *row, bool initial, unsigned long int *changed) { enum ovsdb_monitor_selection type; struct json *old_json, *new_json; struct json *row_json; size_t i; type = ovsdb_monitor_row_update_type(initial, row->old, row->new); if (!(mt->select & type)) { return NULL; } if (type == OJMS_MODIFY) { size_t n_changes; n_changes = 0; memset(changed, 0, bitmap_n_bytes(mt->n_columns)); for (i = 0; i < mt->n_columns; i++) { const struct ovsdb_column *c = mt->columns[i].column; if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) { bitmap_set1(changed, i); n_changes++; } } if (!n_changes) { /* No actual changes: presumably a row changed and then * changed back later. */ return NULL; } } row_json = json_object_create(); old_json = new_json = NULL; if (type & (OJMS_DELETE | OJMS_MODIFY)) { old_json = json_object_create(); json_object_put(row_json, "old", old_json); } if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { new_json = json_object_create(); json_object_put(row_json, "new", new_json); } for (i = 0; i < mt->n_columns; i++) { const struct ovsdb_monitor_column *c = &mt->columns[i]; if (!(type & c->select)) { /* We don't care about this type of change for this * particular column (but we will care about it for some * other column). */ continue; } if ((type == OJMS_MODIFY && bitmap_is_set(changed, i)) || type == OJMS_DELETE) { json_object_put(old_json, c->column->name, ovsdb_datum_to_json(&row->old[i], &c->column->type)); } if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { json_object_put(new_json, c->column->name, ovsdb_datum_to_json(&row->new[i], &c->column->type)); } } return row_json; } /* Constructs and returns JSON for a object (as described in * RFC 7047) for all the outstanding changes within 'monitor', starting from * 'transaction'. */ static struct json* ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, bool initial, uint64_t transaction) { struct shash_node *node; unsigned long int *changed; struct json *json; size_t max_columns; max_columns = 0; SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; max_columns = MAX(max_columns, mt->n_columns); } changed = xmalloc(bitmap_n_bytes(max_columns)); json = NULL; SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; struct ovsdb_monitor_row *row, *next; struct ovsdb_monitor_changes *changes; struct json *table_json = NULL; changes = ovsdb_monitor_table_find_changes(mt, transaction); if (!changes) { continue; } HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { struct json *row_json; row_json = ovsdb_monitor_compose_row_update( mt, row, initial, changed); if (row_json) { char uuid[UUID_LEN + 1]; /* Create JSON object for transaction overall. */ if (!json) { json = json_object_create(); } /* Create JSON object for transaction on this table. */ if (!table_json) { table_json = json_object_create(); json_object_put(json, mt->table->schema->name, table_json); } /* Add JSON row to JSON table. */ snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&row->uuid)); json_object_put(table_json, uuid, row_json); } } } free(changed); return json; } /* Returns JSON for a object (as described in RFC 7047) * for all the outstanding changes within 'monitor' that starts from * '*unflushed' transaction id. * * The caller should specify 'initial' as true if the returned JSON is going to * be used as part of the initial reply to a "monitor" request, false if it is * going to be used as part of an "update" notification. */ struct json * ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, bool initial, uint64_t *unflushed_) { struct ovsdb_monitor_json_cache_node *cache_node; struct shash_node *node; struct json *json; const uint64_t unflushed = *unflushed_; const uint64_t next_unflushed = dbmon->n_transactions + 1; /* Return a clone of cached json if one exists. Otherwise, * generate a new one and add it to the cache. */ cache_node = ovsdb_monitor_json_cache_search(dbmon, unflushed); if (cache_node) { json = cache_node->json ? json_clone(cache_node->json) : NULL; } else { json = ovsdb_monitor_compose_update(dbmon, initial, unflushed); ovsdb_monitor_json_cache_insert(dbmon, unflushed, json); } /* Maintain transaction id of 'changes'. */ SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; ovsdb_monitor_table_untrack_changes(mt, unflushed); ovsdb_monitor_table_track_changes(mt, next_unflushed); } *unflushed_ = next_unflushed; return json; } bool ovsdb_monitor_needs_flush(struct ovsdb_monitor *dbmon, uint64_t next_transaction) { ovs_assert(next_transaction <= dbmon->n_transactions + 1); return (next_transaction <= dbmon->n_transactions); } void ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, enum ovsdb_monitor_selection select) { struct ovsdb_monitor_table * mt; mt = shash_find_data(&dbmon->tables, table->schema->name); mt->select |= select; } /* * If a row's change type (insert, delete or modify) matches that of * the monitor, they should be sent to the monitor's clients as updates. * Of cause, the monitor should also internally update with this change. * * When a change type does not require client side update, the monitor * may still need to keep track of certain changes in order to generate * correct future updates. For example, the monitor internal state should * be updated whenever a new row is inserted, in order to generate the * correct initial state, regardless if a insert change type is being * monitored. * * On the other hand, if a transaction only contains changes to columns * that are not monitored, this transaction can be safely ignored by the * monitor. * * Thus, the order of the declaration is important: * 'OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE' always implies * 'OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE', but not vice versa. */ enum ovsdb_monitor_changes_efficacy { OVSDB_CHANGES_NO_EFFECT, /* Monitor does not care about this change. */ OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE, /* Monitor internal updates. */ OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE, /* Client needs to be updated. */ }; struct ovsdb_monitor_aux { const struct ovsdb_monitor *monitor; struct ovsdb_monitor_table *mt; enum ovsdb_monitor_changes_efficacy efficacy; }; static void ovsdb_monitor_init_aux(struct ovsdb_monitor_aux *aux, const struct ovsdb_monitor *m) { aux->monitor = m; aux->mt = NULL; aux->efficacy = OVSDB_CHANGES_NO_EFFECT; } static void ovsdb_monitor_changes_update(const struct ovsdb_row *old, const struct ovsdb_row *new, const struct ovsdb_monitor_table *mt, struct ovsdb_monitor_changes *changes) { const struct uuid *uuid = ovsdb_row_get_uuid(new ? new : old); struct ovsdb_monitor_row *change; change = ovsdb_monitor_changes_row_find(changes, uuid); if (!change) { change = xzalloc(sizeof *change); hmap_insert(&changes->rows, &change->hmap_node, uuid_hash(uuid)); change->uuid = *uuid; change->old = clone_monitor_row_data(mt, old); change->new = clone_monitor_row_data(mt, new); } else { if (new) { update_monitor_row_data(mt, new, change->new); } else { free_monitor_row_data(mt, change->new); change->new = NULL; if (!change->old) { /* This row was added then deleted. Forget about it. */ hmap_remove(&changes->rows, &change->hmap_node); free(change); } } } } static bool ovsdb_monitor_columns_changed(const struct ovsdb_monitor_table *mt, const unsigned long int *changed) { size_t i; for (i = 0; i < mt->n_columns; i++) { size_t column_index = mt->columns[i].column->index; if (bitmap_is_set(changed, column_index)) { return true; } } return false; } /* Return the efficacy of a row's change to a monitor table. * * Please see the block comment above 'ovsdb_monitor_changes_efficacy' * definition form more information. */ static enum ovsdb_monitor_changes_efficacy ovsdb_monitor_changes_classify(enum ovsdb_monitor_selection type, const struct ovsdb_monitor_table *mt, const unsigned long int *changed) { if (type == OJMS_MODIFY && !ovsdb_monitor_columns_changed(mt, changed)) { return OVSDB_CHANGES_NO_EFFECT; } return (mt->select & type) ? OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE : OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE; } static bool ovsdb_monitor_change_cb(const struct ovsdb_row *old, const struct ovsdb_row *new, const unsigned long int *changed, void *aux_) { struct ovsdb_monitor_aux *aux = aux_; const struct ovsdb_monitor *m = aux->monitor; struct ovsdb_table *table = new ? new->table : old->table; struct ovsdb_monitor_table *mt; struct ovsdb_monitor_changes *changes; if (!aux->mt || table != aux->mt->table) { aux->mt = shash_find_data(&m->tables, table->schema->name); if (!aux->mt) { /* We don't care about rows in this table at all. Tell the caller * to skip it. */ return false; } } mt = aux->mt; HMAP_FOR_EACH(changes, hmap_node, &mt->changes) { enum ovsdb_monitor_changes_efficacy efficacy; enum ovsdb_monitor_selection type; type = ovsdb_monitor_row_update_type(false, old, new); efficacy = ovsdb_monitor_changes_classify(type, mt, changed); if (efficacy > OVSDB_CHANGES_NO_EFFECT) { ovsdb_monitor_changes_update(old, new, mt, changes); } if (aux->efficacy < efficacy) { aux->efficacy = efficacy; } } return true; } void ovsdb_monitor_get_initial(const struct ovsdb_monitor *dbmon) { struct ovsdb_monitor_aux aux; struct shash_node *node; ovsdb_monitor_init_aux(&aux, dbmon); SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; if (mt->select & OJMS_INITIAL) { struct ovsdb_row *row; struct ovsdb_monitor_changes *changes; changes = ovsdb_monitor_table_find_changes(mt, 0); if (!changes) { changes = ovsdb_monitor_table_add_changes(mt, 0); HMAP_FOR_EACH (row, hmap_node, &mt->table->rows) { ovsdb_monitor_changes_update(NULL, row, mt, changes); } } else { changes->n_refs++; } } } } void ovsdb_monitor_remove_jsonrpc_monitor(struct ovsdb_monitor *dbmon, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor, uint64_t unflushed) { struct jsonrpc_monitor_node *jm; if (list_is_empty(&dbmon->jsonrpc_monitors)) { ovsdb_monitor_destroy(dbmon); return; } /* Find and remove the jsonrpc monitor from the list. */ LIST_FOR_EACH(jm, node, &dbmon->jsonrpc_monitors) { if (jm->jsonrpc_monitor == jsonrpc_monitor) { /* Release the tracked changes. */ struct shash_node *node; SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; ovsdb_monitor_table_untrack_changes(mt, unflushed); } list_remove(&jm->node); free(jm); /* Destroy ovsdb monitor if this is the last user. */ if (list_is_empty(&dbmon->jsonrpc_monitors)) { ovsdb_monitor_destroy(dbmon); } return; }; } /* Should never reach here. jsonrpc_monitor should be on the list. */ OVS_NOT_REACHED(); } static bool ovsdb_monitor_table_equal(const struct ovsdb_monitor_table *a, const struct ovsdb_monitor_table *b) { size_t i; if ((a->table != b->table) || (a->select != b->select) || (a->n_columns != b->n_columns)) { return false; } for (i = 0; i < a->n_columns; i++) { if ((a->columns[i].column != b->columns[i].column) || (a->columns[i].select != b->columns[i].select)) { return false; } } return true; } static bool ovsdb_monitor_equal(const struct ovsdb_monitor *a, const struct ovsdb_monitor *b) { struct shash_node *node; if (shash_count(&a->tables) != shash_count(&b->tables)) { return false; } SHASH_FOR_EACH(node, &a->tables) { const struct ovsdb_monitor_table *mta = node->data; const struct ovsdb_monitor_table *mtb; mtb = shash_find_data(&b->tables, node->name); if (!mtb) { return false; } if (!ovsdb_monitor_table_equal(mta, mtb)) { return false; } } return true; } static size_t ovsdb_monitor_hash(const struct ovsdb_monitor *dbmon, size_t basis) { const struct shash_node **nodes; size_t i, j, n; nodes = shash_sort(&dbmon->tables); n = shash_count(&dbmon->tables); for (i = 0; i < n; i++) { struct ovsdb_monitor_table *mt = nodes[i]->data; basis = hash_pointer(mt->table, basis); basis = hash_3words(mt->select, mt->n_columns, basis); for (j = 0; j < mt->n_columns; j++) { basis = hash_pointer(mt->columns[j].column, basis); basis = hash_2words(mt->columns[j].select, basis); } } free(nodes); return basis; } struct ovsdb_monitor * ovsdb_monitor_add(struct ovsdb_monitor *new_dbmon) { struct ovsdb_monitor *dbmon; size_t hash; /* New_dbmon should be associated with only one jsonrpc * connections. */ ovs_assert(list_is_singleton(&new_dbmon->jsonrpc_monitors)); hash = ovsdb_monitor_hash(new_dbmon, 0); HMAP_FOR_EACH_WITH_HASH(dbmon, hmap_node, hash, &ovsdb_monitors) { if (ovsdb_monitor_equal(dbmon, new_dbmon)) { return dbmon; } } hmap_insert(&ovsdb_monitors, &new_dbmon->hmap_node, hash); return new_dbmon; } static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon) { struct shash_node *node; list_remove(&dbmon->replica.node); if (!hmap_node_is_null(&dbmon->hmap_node)) { hmap_remove(&ovsdb_monitors, &dbmon->hmap_node); } ovsdb_monitor_json_cache_flush(dbmon); hmap_destroy(&dbmon->json_cache); SHASH_FOR_EACH (node, &dbmon->tables) { struct ovsdb_monitor_table *mt = node->data; struct ovsdb_monitor_changes *changes, *next; HMAP_FOR_EACH_SAFE (changes, next, hmap_node, &mt->changes) { hmap_remove(&mt->changes, &changes->hmap_node); ovsdb_monitor_changes_destroy(changes); } hmap_destroy(&mt->changes); free(mt->columns); free(mt); } shash_destroy(&dbmon->tables); free(dbmon); } static struct ovsdb_error * ovsdb_monitor_commit(struct ovsdb_replica *replica, const struct ovsdb_txn *txn, bool durable OVS_UNUSED) { struct ovsdb_monitor *m = ovsdb_monitor_cast(replica); struct ovsdb_monitor_aux aux; ovsdb_monitor_init_aux(&aux, m); /* Update ovsdb_monitor's transaction number for * each transaction, before calling ovsdb_monitor_change_cb(). */ m->n_transactions++; ovsdb_txn_for_each_change(txn, ovsdb_monitor_change_cb, &aux); switch(aux.efficacy) { case OVSDB_CHANGES_NO_EFFECT: /* The transaction is ignored by the monitor. * Roll back the 'n_transactions' as if the transaction * has never happened. */ m->n_transactions--; break; case OVSDB_CHANGES_REQUIRE_INTERNAL_UPDATE: /* Nothing. */ break; case OVSDB_CHANGES_REQUIRE_EXTERNAL_UPDATE: ovsdb_monitor_json_cache_flush(m); break; } return NULL; } static void ovsdb_monitor_destroy_callback(struct ovsdb_replica *replica) { struct ovsdb_monitor *dbmon = ovsdb_monitor_cast(replica); struct jsonrpc_monitor_node *jm, *next; /* Delete all front end monitors. Removing the last front * end monitor will also destroy the corresponding 'ovsdb_monitor'. * ovsdb monitor will also be destroied. */ LIST_FOR_EACH_SAFE(jm, next, node, &dbmon->jsonrpc_monitors) { ovsdb_jsonrpc_monitor_destroy(jm->jsonrpc_monitor); } } static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = { ovsdb_monitor_commit, ovsdb_monitor_destroy_callback, }; openvswitch-2.5.9/ovsdb/PaxHeaders.82075/jsonrpc-server.h0000644000000000000000000000013213534540071020114 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801425.081855638 openvswitch-2.5.9/ovsdb/jsonrpc-server.h0000644000175000017500000000522613534540071021607 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_JSONRPC_SERVER_H #define OVSDB_JSONRPC_SERVER_H 1 #include #include "openvswitch/types.h" struct ovsdb; struct shash; struct simap; struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(void); bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *, struct ovsdb *); bool ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *, struct ovsdb *); void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *); /* Options for a remote. */ struct ovsdb_jsonrpc_options { int max_backoff; /* Maximum reconnection backoff, in msec. */ int probe_interval; /* Max idle time before probing, in msec. */ int dscp; /* Dscp value for manager connections */ }; struct ovsdb_jsonrpc_options * ovsdb_jsonrpc_default_options(const char *target); void ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *, const struct shash *); /* Status of a single remote connection. */ struct ovsdb_jsonrpc_remote_status { const char *state; int last_error; unsigned int sec_since_connect; unsigned int sec_since_disconnect; bool is_connected; char *locks_held; char *locks_waiting; char *locks_lost; int n_connections; ovs_be16 bound_port; }; bool ovsdb_jsonrpc_server_get_remote_status( const struct ovsdb_jsonrpc_server *, const char *target, struct ovsdb_jsonrpc_remote_status *); void ovsdb_jsonrpc_server_free_remote_status( struct ovsdb_jsonrpc_remote_status *); void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *); void ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *); void ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *); void ovsdb_jsonrpc_server_get_memory_usage(const struct ovsdb_jsonrpc_server *, struct simap *usage); struct ovsdb_jsonrpc_monitor; void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_jsonrpc_monitor *); #endif /* ovsdb/jsonrpc-server.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/server.c0000644000000000000000000000013213534540071016433 xustar0030 mtime=1567801401.825684283 30 atime=1567801402.121686458 30 ctime=1567801425.093855726 openvswitch-2.5.9/ovsdb/server.c0000644000175000017500000001574713534540071020137 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "server.h" #include "hash.h" #include "ovsdb.h" /* Initializes 'session' as a session within 'server'. */ void ovsdb_session_init(struct ovsdb_session *session, struct ovsdb_server *server) { session->server = server; list_init(&session->completions); hmap_init(&session->waiters); } /* Destroys 'session'. */ void ovsdb_session_destroy(struct ovsdb_session *session) { ovs_assert(hmap_is_empty(&session->waiters)); hmap_destroy(&session->waiters); } /* Searches 'session' for an ovsdb_lock_waiter named 'lock_name' and returns * it if it finds one, otherwise NULL. */ struct ovsdb_lock_waiter * ovsdb_session_get_lock_waiter(const struct ovsdb_session *session, const char *lock_name) { struct ovsdb_lock_waiter *waiter; HMAP_FOR_EACH_WITH_HASH (waiter, session_node, hash_string(lock_name, 0), &session->waiters) { if (!strcmp(lock_name, waiter->lock_name)) { return waiter; } } return NULL; } /* Returns the waiter that owns 'lock'. * * A lock always has an owner, so this function will never return NULL. */ struct ovsdb_lock_waiter * ovsdb_lock_get_owner(const struct ovsdb_lock *lock) { return CONTAINER_OF(list_front(&lock->waiters), struct ovsdb_lock_waiter, lock_node); } /* Removes 'waiter' from its lock's list. This means that, if 'waiter' was * formerly the owner of its lock, then it no longer owns it. * * Returns the session that now owns 'waiter'. This is NULL if 'waiter' was * the lock's owner and no other sessions were waiting for the lock. In this * case, the lock has been destroyed, so the caller must be sure not to refer * to it again. A nonnull return value reflects a change in the lock's * ownership if and only if 'waiter' formerly owned the lock. */ struct ovsdb_session * ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *waiter) { struct ovsdb_lock *lock = waiter->lock; list_remove(&waiter->lock_node); waiter->lock = NULL; if (list_is_empty(&lock->waiters)) { hmap_remove(&lock->server->locks, &lock->hmap_node); free(lock->name); free(lock); return NULL; } return ovsdb_lock_get_owner(lock)->session; } /* Destroys 'waiter', which must have already been removed from its lock's * waiting list with ovsdb_lock_waiter_remove(). * * Removing and destroying locks are decoupled because a lock initially created * by the "steal" request, that is later stolen by another client, remains in * the database session until the database client sends an "unlock" request. */ void ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *waiter) { ovs_assert(!waiter->lock); hmap_remove(&waiter->session->waiters, &waiter->session_node); free(waiter->lock_name); free(waiter); } /* Returns true if 'waiter' owns its associated lock. */ bool ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *waiter) { return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock); } /* Initializes 'server'. * * The caller must call ovsdb_server_add_db() for each database to which * 'server' should provide access. */ void ovsdb_server_init(struct ovsdb_server *server) { shash_init(&server->dbs); hmap_init(&server->locks); } /* Adds 'db' to the set of databases served out by 'server'. Returns true if * successful, false if 'db''s name is the same as some database already in * 'server'. */ bool ovsdb_server_add_db(struct ovsdb_server *server, struct ovsdb *db) { return shash_add_once(&server->dbs, db->schema->name, db); } /* Removes 'db' from the set of databases served out by 'server'. Returns * true if successful, false if there is no db associated with * db->schema->name. */ bool ovsdb_server_remove_db(struct ovsdb_server *server, struct ovsdb *db) { void *data = shash_find_and_delete(&server->dbs, db->schema->name); if (data) { return true; } return false; } /* Destroys 'server'. */ void ovsdb_server_destroy(struct ovsdb_server *server) { shash_destroy(&server->dbs); hmap_destroy(&server->locks); } static struct ovsdb_lock * ovsdb_server_create_lock__(struct ovsdb_server *server, const char *lock_name, uint32_t hash) { struct ovsdb_lock *lock; HMAP_FOR_EACH_WITH_HASH (lock, hmap_node, hash, &server->locks) { if (!strcmp(lock->name, lock_name)) { return lock; } } lock = xzalloc(sizeof *lock); lock->server = server; lock->name = xstrdup(lock_name); hmap_insert(&server->locks, &lock->hmap_node, hash); list_init(&lock->waiters); return lock; } /* Attempts to acquire the lock named 'lock_name' for 'session' within * 'server'. Returns the new lock waiter. * * If 'mode' is OVSDB_LOCK_STEAL, then the new lock waiter is always the owner * of the lock. '*victimp' receives the session of the previous owner or NULL * if the lock was previously unowned. (If the victim itself originally * obtained the lock through a "steal" operation, then this function also * removes the victim from the lock's waiting list.) * * If 'mode' is OVSDB_LOCK_WAIT, then the new lock waiter is the owner of the * lock only if this lock had no existing owner. '*victimp' is set to NULL. */ struct ovsdb_lock_waiter * ovsdb_server_lock(struct ovsdb_server *server, struct ovsdb_session *session, const char *lock_name, enum ovsdb_lock_mode mode, struct ovsdb_session **victimp) { uint32_t hash = hash_string(lock_name, 0); struct ovsdb_lock_waiter *waiter, *victim; struct ovsdb_lock *lock; lock = ovsdb_server_create_lock__(server, lock_name, hash); victim = (mode == OVSDB_LOCK_STEAL && !list_is_empty(&lock->waiters) ? ovsdb_lock_get_owner(lock) : NULL); waiter = xmalloc(sizeof *waiter); waiter->mode = mode; waiter->lock_name = xstrdup(lock_name); waiter->lock = lock; if (mode == OVSDB_LOCK_STEAL) { list_push_front(&lock->waiters, &waiter->lock_node); } else { list_push_back(&lock->waiters, &waiter->lock_node); } waiter->session = session; hmap_insert(&waiter->session->waiters, &waiter->session_node, hash); if (victim && victim->mode == OVSDB_LOCK_STEAL) { ovsdb_lock_waiter_remove(victim); } *victimp = victim ? victim->session : NULL; return waiter; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/ovsdb-client.c0000644000000000000000000000013213534540071017516 xustar0030 mtime=1567801401.813684195 30 atime=1567801402.121686458 30 ctime=1567801425.133856021 openvswitch-2.5.9/ovsdb/ovsdb-client.c0000644000175000017500000011001013534540071021175 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "column.h" #include "compiler.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "json.h" #include "jsonrpc.h" #include "lib/table.h" #include "ovsdb.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "poll-loop.h" #include "sort.h" #include "svec.h" #include "stream.h" #include "stream-ssl.h" #include "table.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_client); enum args_needed { NEED_NONE, /* No JSON-RPC connection or database name needed. */ NEED_RPC, /* JSON-RPC connection needed. */ NEED_DATABASE /* JSON-RPC connection and database name needed. */ }; struct ovsdb_client_command { const char *name; enum args_needed need; int min_args; int max_args; void (*handler)(struct jsonrpc *rpc, const char *database, int argc, char *argv[]); }; /* --timestamp: Print a timestamp before each update on "monitor" command? */ static bool timestamp; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; static const struct ovsdb_client_command *get_all_commands(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static struct jsonrpc *open_jsonrpc(const char *server); static void fetch_dbs(struct jsonrpc *, struct svec *dbs); int main(int argc, char *argv[]) { const struct ovsdb_client_command *command; const char *database; struct jsonrpc *rpc; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); daemon_become_new_user(false); if (optind >= argc) { ovs_fatal(0, "missing command name; use --help for help"); } for (command = get_all_commands(); ; command++) { if (!command->name) { VLOG_FATAL("unknown command '%s'; use --help for help", argv[optind]); } else if (!strcmp(command->name, argv[optind])) { break; } } optind++; if (command->need != NEED_NONE) { if (argc - optind > command->min_args && (isalpha((unsigned char) argv[optind][0]) && strchr(argv[optind], ':'))) { rpc = open_jsonrpc(argv[optind++]); } else { char *sock = xasprintf("unix:%s/db.sock", ovs_rundir()); rpc = open_jsonrpc(sock); free(sock); } } else { rpc = NULL; } if (command->need == NEED_DATABASE) { struct svec dbs; svec_init(&dbs); fetch_dbs(rpc, &dbs); if (argc - optind > command->min_args && svec_contains(&dbs, argv[optind])) { database = argv[optind++]; } else if (dbs.n == 1) { database = xstrdup(dbs.names[0]); } else if (svec_contains(&dbs, "Open_vSwitch")) { database = "Open_vSwitch"; } else { ovs_fatal(0, "no default database for `%s' command, please " "specify a database name", command->name); } svec_destroy(&dbs); } else { database = NULL; } if (argc - optind < command->min_args || argc - optind > command->max_args) { VLOG_FATAL("invalid syntax for '%s' (use --help for help)", command->name); } command->handler(rpc, database, argc - optind, argv + optind); jsonrpc_close(rpc); if (ferror(stdout)) { VLOG_FATAL("write to stdout failed"); } if (ferror(stderr)) { VLOG_FATAL("write to stderr failed"); } return 0; } static void parse_options(int argc, char *argv[]) { enum { OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1, OPT_TIMESTAMP, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS, TABLE_OPTION_ENUMS }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, VLOG_LONG_OPTIONS, DAEMON_LONG_OPTIONS, #ifdef HAVE_OPENSSL {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, STREAM_SSL_LONG_OPTIONS, #endif TABLE_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case OPT_TIMESTAMP: timestamp = true; break; case '?': exit(EXIT_FAILURE); case 0: /* getopt_long() already set the value for us. */ break; default: abort(); } } free(short_options); } static void usage(void) { printf("%s: Open vSwitch database JSON-RPC client\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" "\nValid commands are:\n" "\n list-dbs [SERVER]\n" " list databases available on SERVER\n" "\n get-schema [SERVER] [DATABASE]\n" " retrieve schema for DATABASE from SERVER\n" "\n get-schema-version [SERVER] [DATABASE]\n" " retrieve schema for DATABASE from SERVER and report only its\n" " version number on stdout\n" "\n list-tables [SERVER] [DATABASE]\n" " list tables for DATABASE on SERVER\n" "\n list-columns [SERVER] [DATABASE] [TABLE]\n" " list columns in TABLE (or all tables) in DATABASE on SERVER\n" "\n transact [SERVER] TRANSACTION\n" " run TRANSACTION (a JSON array of operations) on SERVER\n" " and print the results as JSON on stdout\n" "\n monitor [SERVER] [DATABASE] TABLE [COLUMN,...]...\n" " monitor contents of COLUMNs in TABLE in DATABASE on SERVER.\n" " COLUMNs may include !initial, !insert, !delete, !modify\n" " to avoid seeing the specified kinds of changes.\n" "\n monitor [SERVER] [DATABASE] ALL\n" " monitor all changes to all columns in all tables\n" " in DATBASE on SERVER.\n" "\n dump [SERVER] [DATABASE] [TABLE [COLUMN]...]\n" " dump contents of DATABASE on SERVER to stdout\n" "\nThe default SERVER is unix:%s/db.sock.\n" "The default DATABASE is Open_vSwitch.\n", program_name, program_name, ovs_rundir()); stream_usage("SERVER", true, true, true); printf("\nOutput formatting options:\n" " -f, --format=FORMAT set output formatting to FORMAT\n" " (\"table\", \"html\", \"csv\", " "or \"json\")\n" " --no-headings omit table heading row\n" " --pretty pretty-print JSON in output\n" " --timestamp timestamp \"monitor\" output"); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void check_txn(int error, struct jsonrpc_msg **reply_) { struct jsonrpc_msg *reply = *reply_; if (error) { ovs_fatal(error, "transaction failed"); } if (reply->error) { ovs_fatal(error, "transaction returned error: %s", json_to_string(reply->error, table_style.json_flags)); } } static struct json * parse_json(const char *s) { struct json *json = json_from_string(s); if (json->type == JSON_STRING) { ovs_fatal(0, "\"%s\": %s", s, json->u.string); } return json; } static struct jsonrpc * open_jsonrpc(const char *server) { struct stream *stream; int error; error = stream_open_block(jsonrpc_stream_open(server, &stream, DSCP_DEFAULT), &stream); if (error == EAFNOSUPPORT) { struct pstream *pstream; error = jsonrpc_pstream_open(server, &pstream, DSCP_DEFAULT); if (error) { ovs_fatal(error, "failed to connect or listen to \"%s\"", server); } VLOG_INFO("%s: waiting for connection...", server); error = pstream_accept_block(pstream, &stream); if (error) { ovs_fatal(error, "failed to accept connection on \"%s\"", server); } pstream_close(pstream); } else if (error) { ovs_fatal(error, "failed to connect to \"%s\"", server); } return jsonrpc_open(stream); } static void print_json(struct json *json) { char *string = json_to_string(json, table_style.json_flags); fputs(string, stdout); free(string); } static void print_and_free_json(struct json *json) { print_json(json); json_destroy(json); } static void check_ovsdb_error(struct ovsdb_error *error) { if (error) { ovs_fatal(0, "%s", ovsdb_error_to_string(error)); } } static struct ovsdb_schema * fetch_schema(struct jsonrpc *rpc, const char *database) { struct jsonrpc_msg *request, *reply; struct ovsdb_schema *schema; request = jsonrpc_create_request("get_schema", json_array_create_1( json_string_create(database)), NULL); check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply); check_ovsdb_error(ovsdb_schema_from_json(reply->result, &schema)); jsonrpc_msg_destroy(reply); return schema; } static void fetch_dbs(struct jsonrpc *rpc, struct svec *dbs) { struct jsonrpc_msg *request, *reply; size_t i; request = jsonrpc_create_request("list_dbs", json_array_create_empty(), NULL); check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply); if (reply->result->type != JSON_ARRAY) { ovs_fatal(0, "list_dbs response is not array"); } for (i = 0; i < reply->result->u.array.n; i++) { const struct json *name = reply->result->u.array.elems[i]; if (name->type != JSON_STRING) { ovs_fatal(0, "list_dbs response %"PRIuSIZE" is not string", i); } svec_add(dbs, name->u.string); } jsonrpc_msg_destroy(reply); svec_sort(dbs); } static void do_list_dbs(struct jsonrpc *rpc, const char *database OVS_UNUSED, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { const char *db_name; struct svec dbs; size_t i; svec_init(&dbs); fetch_dbs(rpc, &dbs); SVEC_FOR_EACH (i, db_name, &dbs) { puts(db_name); } svec_destroy(&dbs); } static void do_get_schema(struct jsonrpc *rpc, const char *database, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ovsdb_schema *schema = fetch_schema(rpc, database); print_and_free_json(ovsdb_schema_to_json(schema)); ovsdb_schema_destroy(schema); } static void do_get_schema_version(struct jsonrpc *rpc, const char *database, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ovsdb_schema *schema = fetch_schema(rpc, database); puts(schema->version); ovsdb_schema_destroy(schema); } static void do_list_tables(struct jsonrpc *rpc, const char *database, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ovsdb_schema *schema; struct shash_node *node; struct table t; schema = fetch_schema(rpc, database); table_init(&t); table_add_column(&t, "Table"); SHASH_FOR_EACH (node, &schema->tables) { struct ovsdb_table_schema *ts = node->data; table_add_row(&t); table_add_cell(&t)->text = xstrdup(ts->name); } ovsdb_schema_destroy(schema); table_print(&t, &table_style); } static void do_list_columns(struct jsonrpc *rpc, const char *database, int argc OVS_UNUSED, char *argv[]) { const char *table_name = argv[0]; struct ovsdb_schema *schema; struct shash_node *table_node; struct table t; schema = fetch_schema(rpc, database); table_init(&t); if (!table_name) { table_add_column(&t, "Table"); } table_add_column(&t, "Column"); table_add_column(&t, "Type"); SHASH_FOR_EACH (table_node, &schema->tables) { struct ovsdb_table_schema *ts = table_node->data; if (!table_name || !strcmp(table_name, ts->name)) { struct shash_node *column_node; SHASH_FOR_EACH (column_node, &ts->columns) { const struct ovsdb_column *column = column_node->data; table_add_row(&t); if (!table_name) { table_add_cell(&t)->text = xstrdup(ts->name); } table_add_cell(&t)->text = xstrdup(column->name); table_add_cell(&t)->json = ovsdb_type_to_json(&column->type); } } } ovsdb_schema_destroy(schema); table_print(&t, &table_style); } static void do_transact(struct jsonrpc *rpc, const char *database OVS_UNUSED, int argc OVS_UNUSED, char *argv[]) { struct jsonrpc_msg *request, *reply; struct json *transaction; transaction = parse_json(argv[0]); request = jsonrpc_create_request("transact", transaction, NULL); check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply); print_json(reply->result); putchar('\n'); jsonrpc_msg_destroy(reply); } /* "monitor" command. */ struct monitored_table { struct ovsdb_table_schema *table; struct ovsdb_column_set columns; }; static void monitor_print_row(struct json *row, const char *type, const char *uuid, const struct ovsdb_column_set *columns, struct table *t) { size_t i; if (!row) { ovs_error(0, "missing %s row", type); return; } else if (row->type != JSON_OBJECT) { ovs_error(0, " is not object"); return; } table_add_row(t); table_add_cell(t)->text = xstrdup(uuid); table_add_cell(t)->text = xstrdup(type); for (i = 0; i < columns->n_columns; i++) { const struct ovsdb_column *column = columns->columns[i]; struct json *value = shash_find_data(json_object(row), column->name); struct cell *cell = table_add_cell(t); if (value) { cell->json = json_clone(value); cell->type = &column->type; } } } static void monitor_print_table(struct json *table_update, const struct monitored_table *mt, char *caption, bool initial) { const struct ovsdb_table_schema *table = mt->table; const struct ovsdb_column_set *columns = &mt->columns; struct shash_node *node; struct table t; size_t i; if (table_update->type != JSON_OBJECT) { ovs_error(0, " for table %s is not object", table->name); return; } table_init(&t); table_set_timestamp(&t, timestamp); table_set_caption(&t, caption); table_add_column(&t, "row"); table_add_column(&t, "action"); for (i = 0; i < columns->n_columns; i++) { table_add_column(&t, "%s", columns->columns[i]->name); } SHASH_FOR_EACH (node, json_object(table_update)) { struct json *row_update = node->data; struct json *old, *new; if (row_update->type != JSON_OBJECT) { ovs_error(0, " is not object"); continue; } old = shash_find_data(json_object(row_update), "old"); new = shash_find_data(json_object(row_update), "new"); if (initial) { monitor_print_row(new, "initial", node->name, columns, &t); } else if (!old) { monitor_print_row(new, "insert", node->name, columns, &t); } else if (!new) { monitor_print_row(old, "delete", node->name, columns, &t); } else { monitor_print_row(old, "old", node->name, columns, &t); monitor_print_row(new, "new", "", columns, &t); } } table_print(&t, &table_style); table_destroy(&t); } static void monitor_print(struct json *table_updates, const struct monitored_table *mts, size_t n_mts, bool initial) { size_t i; if (table_updates->type != JSON_OBJECT) { ovs_error(0, " is not object"); return; } for (i = 0; i < n_mts; i++) { const struct monitored_table *mt = &mts[i]; struct json *table_update = shash_find_data(json_object(table_updates), mt->table->name); if (table_update) { monitor_print_table(table_update, mt, n_mts > 1 ? xstrdup(mt->table->name) : NULL, initial); } } } static void add_column(const char *server, const struct ovsdb_column *column, struct ovsdb_column_set *columns, struct json *columns_json) { if (ovsdb_column_set_contains(columns, column->index)) { ovs_fatal(0, "%s: column \"%s\" mentioned multiple times", server, column->name); } ovsdb_column_set_add(columns, column); json_array_add(columns_json, json_string_create(column->name)); } static struct json * parse_monitor_columns(char *arg, const char *server, const char *database, const struct ovsdb_table_schema *table, struct ovsdb_column_set *columns) { bool initial, insert, delete, modify; struct json *mr, *columns_json; char *save_ptr = NULL; char *token; mr = json_object_create(); columns_json = json_array_create_empty(); json_object_put(mr, "columns", columns_json); initial = insert = delete = modify = true; for (token = strtok_r(arg, ",", &save_ptr); token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { if (!strcmp(token, "!initial")) { initial = false; } else if (!strcmp(token, "!insert")) { insert = false; } else if (!strcmp(token, "!delete")) { delete = false; } else if (!strcmp(token, "!modify")) { modify = false; } else { const struct ovsdb_column *column; column = ovsdb_table_schema_get_column(table, token); if (!column) { ovs_fatal(0, "%s: table \"%s\" in %s does not have a " "column named \"%s\"", server, table->name, database, token); } add_column(server, column, columns, columns_json); } } if (columns_json->u.array.n == 0) { const struct shash_node **nodes; size_t i, n; n = shash_count(&table->columns); nodes = shash_sort(&table->columns); for (i = 0; i < n; i++) { const struct ovsdb_column *column = nodes[i]->data; if (column->index != OVSDB_COL_UUID && column->index != OVSDB_COL_VERSION) { add_column(server, column, columns, columns_json); } } free(nodes); add_column(server, ovsdb_table_schema_get_column(table, "_version"), columns, columns_json); } if (!initial || !insert || !delete || !modify) { struct json *select = json_object_create(); json_object_put(select, "initial", json_boolean_create(initial)); json_object_put(select, "insert", json_boolean_create(insert)); json_object_put(select, "delete", json_boolean_create(delete)); json_object_put(select, "modify", json_boolean_create(modify)); json_object_put(mr, "select", select); } return mr; } static void ovsdb_client_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void ovsdb_client_block(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (!*blocked) { *blocked = true; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already blocking"); } } static void ovsdb_client_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (*blocked) { *blocked = false; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already unblocked"); } } static void add_monitored_table(int argc, char *argv[], const char *server, const char *database, struct ovsdb_table_schema *table, struct json *monitor_requests, struct monitored_table **mts, size_t *n_mts, size_t *allocated_mts) { struct json *monitor_request_array; struct monitored_table *mt; if (*n_mts >= *allocated_mts) { *mts = x2nrealloc(*mts, allocated_mts, sizeof **mts); } mt = &(*mts)[(*n_mts)++]; mt->table = table; ovsdb_column_set_init(&mt->columns); monitor_request_array = json_array_create_empty(); if (argc > 1) { int i; for (i = 1; i < argc; i++) { json_array_add( monitor_request_array, parse_monitor_columns(argv[i], server, database, table, &mt->columns)); } } else { /* Allocate a writable empty string since parse_monitor_columns() * is going to strtok() it and that's risky with literal "". */ char empty[] = ""; json_array_add( monitor_request_array, parse_monitor_columns(empty, server, database, table, &mt->columns)); } json_object_put(monitor_requests, table->name, monitor_request_array); } static void do_monitor(struct jsonrpc *rpc, const char *database, int argc, char *argv[]) { const char *server = jsonrpc_get_name(rpc); const char *table_name = argv[0]; struct unixctl_server *unixctl; struct ovsdb_schema *schema; struct jsonrpc_msg *request; struct json *monitor, *monitor_requests, *request_id; bool exiting = false; bool blocked = false; struct monitored_table *mts; size_t n_mts, allocated_mts; daemon_save_fd(STDOUT_FILENO); daemonize_start(false); if (get_detach()) { int error; error = unixctl_server_create(NULL, &unixctl); if (error) { ovs_fatal(error, "failed to create unixctl server"); } unixctl_command_register("exit", "", 0, 0, ovsdb_client_exit, &exiting); unixctl_command_register("ovsdb-client/block", "", 0, 0, ovsdb_client_block, &blocked); unixctl_command_register("ovsdb-client/unblock", "", 0, 0, ovsdb_client_unblock, &blocked); } else { unixctl = NULL; } schema = fetch_schema(rpc, database); monitor_requests = json_object_create(); mts = NULL; n_mts = allocated_mts = 0; if (strcmp(table_name, "ALL")) { struct ovsdb_table_schema *table; table = shash_find_data(&schema->tables, table_name); if (!table) { ovs_fatal(0, "%s: %s does not have a table named \"%s\"", server, database, table_name); } add_monitored_table(argc, argv, server, database, table, monitor_requests, &mts, &n_mts, &allocated_mts); } else { size_t n = shash_count(&schema->tables); const struct shash_node **nodes = shash_sort(&schema->tables); size_t i; for (i = 0; i < n; i++) { struct ovsdb_table_schema *table = nodes[i]->data; add_monitored_table(argc, argv, server, database, table, monitor_requests, &mts, &n_mts, &allocated_mts); } free(nodes); } monitor = json_array_create_3(json_string_create(database), json_null_create(), monitor_requests); request = jsonrpc_create_request("monitor", monitor, NULL); request_id = json_clone(request->id); jsonrpc_send(rpc, request); for (;;) { unixctl_server_run(unixctl); while (!blocked) { struct jsonrpc_msg *msg; int error; error = jsonrpc_recv(rpc, &msg); if (error == EAGAIN) { break; } else if (error) { ovs_fatal(error, "%s: receive failed", server); } if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) { jsonrpc_send(rpc, jsonrpc_create_reply(json_clone(msg->params), msg->id)); } else if (msg->type == JSONRPC_REPLY && json_equal(msg->id, request_id)) { monitor_print(msg->result, mts, n_mts, true); fflush(stdout); daemonize_complete(); } else if (msg->type == JSONRPC_NOTIFY && !strcmp(msg->method, "update")) { struct json *params = msg->params; if (params->type == JSON_ARRAY && params->u.array.n == 2 && params->u.array.elems[0]->type == JSON_NULL) { monitor_print(params->u.array.elems[1], mts, n_mts, false); fflush(stdout); } } jsonrpc_msg_destroy(msg); } if (exiting) { break; } jsonrpc_run(rpc); jsonrpc_wait(rpc); if (!blocked) { jsonrpc_recv_wait(rpc); } unixctl_server_wait(unixctl); poll_block(); } } struct dump_table_aux { struct ovsdb_datum **data; const struct ovsdb_column **columns; size_t n_columns; }; static int compare_data(size_t a_y, size_t b_y, size_t x, const struct dump_table_aux *aux) { return ovsdb_datum_compare_3way(&aux->data[a_y][x], &aux->data[b_y][x], &aux->columns[x]->type); } static int compare_rows(size_t a_y, size_t b_y, void *aux_) { struct dump_table_aux *aux = aux_; size_t x; /* Skip UUID columns on the first pass, since their values tend to be * random and make our results less reproducible. */ for (x = 0; x < aux->n_columns; x++) { if (aux->columns[x]->type.key.type != OVSDB_TYPE_UUID) { int cmp = compare_data(a_y, b_y, x, aux); if (cmp) { return cmp; } } } /* Use UUID columns as tie-breakers. */ for (x = 0; x < aux->n_columns; x++) { if (aux->columns[x]->type.key.type == OVSDB_TYPE_UUID) { int cmp = compare_data(a_y, b_y, x, aux); if (cmp) { return cmp; } } } return 0; } static void swap_rows(size_t a_y, size_t b_y, void *aux_) { struct dump_table_aux *aux = aux_; struct ovsdb_datum *tmp = aux->data[a_y]; aux->data[a_y] = aux->data[b_y]; aux->data[b_y] = tmp; } static int compare_columns(const void *a_, const void *b_) { const struct ovsdb_column *const *ap = a_; const struct ovsdb_column *const *bp = b_; const struct ovsdb_column *a = *ap; const struct ovsdb_column *b = *bp; return strcmp(a->name, b->name); } static void dump_table(const char *table_name, const struct shash *cols, struct json_array *rows) { const struct ovsdb_column **columns; size_t n_columns; struct ovsdb_datum **data; struct dump_table_aux aux; struct shash_node *node; struct table t; size_t x, y; /* Sort columns by name, for reproducibility. */ columns = xmalloc(shash_count(cols) * sizeof *columns); n_columns = 0; SHASH_FOR_EACH (node, cols) { struct ovsdb_column *column = node->data; if (strcmp(column->name, "_version")) { columns[n_columns++] = column; } } qsort(columns, n_columns, sizeof *columns, compare_columns); /* Extract data from table. */ data = xmalloc(rows->n * sizeof *data); for (y = 0; y < rows->n; y++) { struct shash *row; if (rows->elems[y]->type != JSON_OBJECT) { ovs_fatal(0, "row %"PRIuSIZE" in table %s response is not a JSON object: " "%s", y, table_name, json_to_string(rows->elems[y], 0)); } row = json_object(rows->elems[y]); data[y] = xmalloc(n_columns * sizeof **data); for (x = 0; x < n_columns; x++) { const struct json *json = shash_find_data(row, columns[x]->name); if (!json) { ovs_fatal(0, "row %"PRIuSIZE" in table %s response lacks %s column", y, table_name, columns[x]->name); } check_ovsdb_error(ovsdb_datum_from_json(&data[y][x], &columns[x]->type, json, NULL)); } } /* Sort rows by column values, for reproducibility. */ aux.data = data; aux.columns = columns; aux.n_columns = n_columns; sort(rows->n, compare_rows, swap_rows, &aux); /* Add column headings. */ table_init(&t); table_set_caption(&t, xasprintf("%s table", table_name)); for (x = 0; x < n_columns; x++) { table_add_column(&t, "%s", columns[x]->name); } /* Print rows. */ for (y = 0; y < rows->n; y++) { table_add_row(&t); for (x = 0; x < n_columns; x++) { struct cell *cell = table_add_cell(&t); cell->json = ovsdb_datum_to_json(&data[y][x], &columns[x]->type); cell->type = &columns[x]->type; ovsdb_datum_destroy(&data[y][x], &columns[x]->type); } free(data[y]); } table_print(&t, &table_style); table_destroy(&t); free(data); free(columns); } static void do_dump(struct jsonrpc *rpc, const char *database, int argc, char *argv[]) { struct jsonrpc_msg *request, *reply; struct ovsdb_schema *schema; struct json *transaction; const struct shash_node *node, **tables; size_t n_tables; struct ovsdb_table_schema *tschema; const struct shash *columns; struct shash custom_columns; size_t i; shash_init(&custom_columns); schema = fetch_schema(rpc, database); if (argc) { node = shash_find(&schema->tables, argv[0]); if (!node) { ovs_fatal(0, "No table \"%s\" found.", argv[0]); } tables = xmemdup(&node, sizeof(&node)); n_tables = 1; tschema = tables[0]->data; for (i = 1; i < argc; i++) { node = shash_find(&tschema->columns, argv[i]); if (!node) { ovs_fatal(0, "Table \"%s\" has no column %s.", argv[0], argv[i]); } shash_add(&custom_columns, argv[i], node->data); } } else { tables = shash_sort(&schema->tables); n_tables = shash_count(&schema->tables); } /* Construct transaction to retrieve entire database. */ transaction = json_array_create_1(json_string_create(database)); for (i = 0; i < n_tables; i++) { const struct ovsdb_table_schema *ts = tables[i]->data; struct json *op, *jcolumns; if (argc > 1) { columns = &custom_columns; } else { columns = &ts->columns; } jcolumns = json_array_create_empty(); SHASH_FOR_EACH (node, columns) { const struct ovsdb_column *column = node->data; if (strcmp(column->name, "_version")) { json_array_add(jcolumns, json_string_create(column->name)); } } op = json_object_create(); json_object_put_string(op, "op", "select"); json_object_put_string(op, "table", tables[i]->name); json_object_put(op, "where", json_array_create_empty()); json_object_put(op, "columns", jcolumns); json_array_add(transaction, op); } /* Send request, get reply. */ request = jsonrpc_create_request("transact", transaction, NULL); check_txn(jsonrpc_transact_block(rpc, request, &reply), &reply); /* Print database contents. */ if (reply->result->type != JSON_ARRAY || reply->result->u.array.n != n_tables) { ovs_fatal(0, "reply is not array of %"PRIuSIZE" elements: %s", n_tables, json_to_string(reply->result, 0)); } for (i = 0; i < n_tables; i++) { const struct ovsdb_table_schema *ts = tables[i]->data; const struct json *op_result = reply->result->u.array.elems[i]; struct json *rows; if (op_result->type != JSON_OBJECT || !(rows = shash_find_data(json_object(op_result), "rows")) || rows->type != JSON_ARRAY) { ovs_fatal(0, "%s table reply is not an object with a \"rows\" " "member array: %s", ts->name, json_to_string(op_result, 0)); } if (argc > 1) { dump_table(tables[i]->name, &custom_columns, &rows->u.array); } else { dump_table(tables[i]->name, &ts->columns, &rows->u.array); } } jsonrpc_msg_destroy(reply); shash_destroy(&custom_columns); free(tables); ovsdb_schema_destroy(schema); } static void do_help(struct jsonrpc *rpc OVS_UNUSED, const char *database OVS_UNUSED, int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { usage(); } /* All command handlers (except for "help") are expected to take an optional * server socket name (e.g. "unix:...") as their first argument. The socket * name argument must be included in max_args (but left out of min_args). The * command name and socket name are not included in the arguments passed to the * handler: the argv[0] passed to the handler is the first argument after the * optional server socket name. The connection to the server is available as * global variable 'rpc'. */ static const struct ovsdb_client_command all_commands[] = { { "list-dbs", NEED_RPC, 0, 0, do_list_dbs }, { "get-schema", NEED_DATABASE, 0, 0, do_get_schema }, { "get-schema-version", NEED_DATABASE, 0, 0, do_get_schema_version }, { "list-tables", NEED_DATABASE, 0, 0, do_list_tables }, { "list-columns", NEED_DATABASE, 0, 1, do_list_columns }, { "transact", NEED_RPC, 1, 1, do_transact }, { "monitor", NEED_DATABASE, 1, INT_MAX, do_monitor }, { "dump", NEED_DATABASE, 0, INT_MAX, do_dump }, { "help", NEED_NONE, 0, INT_MAX, do_help }, { NULL, 0, 0, 0, NULL }, }; static const struct ovsdb_client_command *get_all_commands(void) { return all_commands; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/log.c0000644000000000000000000000013213534540071015706 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801425.081855638 openvswitch-2.5.9/ovsdb/log.c0000644000175000017500000002746013534540071017405 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "log.h" #include #include #include #include #include #include #include "json.h" #include "lockfile.h" #include "ovsdb.h" #include "ovsdb-error.h" #include "sha1.h" #include "socket-util.h" #include "transaction.h" #include "util.h" enum ovsdb_log_mode { OVSDB_LOG_READ, OVSDB_LOG_WRITE }; struct ovsdb_log { off_t prev_offset; off_t offset; char *name; struct lockfile *lockfile; FILE *stream; struct ovsdb_error *read_error; bool write_error; enum ovsdb_log_mode mode; }; /* Attempts to open 'name' with the specified 'open_mode'. On success, stores * the new log into '*filep' and returns NULL; otherwise returns NULL and * stores NULL into '*filep'. * * Whether the file will be locked using lockfile_lock() depends on 'locking': * use true to lock it, false not to lock it, or -1 to lock it only if * 'open_mode' is a mode that allows writing. */ struct ovsdb_error * ovsdb_log_open(const char *name, enum ovsdb_log_open_mode open_mode, int locking, struct ovsdb_log **filep) { struct lockfile *lockfile; struct ovsdb_error *error; struct ovsdb_log *file; struct stat s; FILE *stream; int flags; int fd; *filep = NULL; ovs_assert(locking == -1 || locking == false || locking == true); if (locking < 0) { locking = open_mode != OVSDB_LOG_READ_ONLY; } if (locking) { int retval = lockfile_lock(name, &lockfile); if (retval) { error = ovsdb_io_error(retval, "%s: failed to lock lockfile", name); goto error; } } else { lockfile = NULL; } if (open_mode == OVSDB_LOG_READ_ONLY) { flags = O_RDONLY; } else if (open_mode == OVSDB_LOG_READ_WRITE) { flags = O_RDWR; } else if (open_mode == OVSDB_LOG_CREATE) { #ifndef _WIN32 if (stat(name, &s) == -1 && errno == ENOENT && lstat(name, &s) == 0 && S_ISLNK(s.st_mode)) { /* 'name' is a dangling symlink. We want to create the file that * the symlink points to, but POSIX says that open() with O_EXCL * must fail with EEXIST if the named file is a symlink. So, we * have to leave off O_EXCL and accept the race. */ flags = O_RDWR | O_CREAT; } else { flags = O_RDWR | O_CREAT | O_EXCL; } #else flags = O_RDWR | O_CREAT | O_EXCL; #endif } else { OVS_NOT_REACHED(); } #ifdef _WIN32 flags = flags | O_BINARY; #endif fd = open(name, flags, 0666); if (fd < 0) { const char *op = open_mode == OVSDB_LOG_CREATE ? "create" : "open"; error = ovsdb_io_error(errno, "%s: %s failed", op, name); goto error_unlock; } if (!fstat(fd, &s) && s.st_size == 0) { /* It's (probably) a new file so fsync() its parent directory to ensure * that its directory entry is committed to disk. */ fsync_parent_dir(name); } stream = fdopen(fd, open_mode == OVSDB_LOG_READ_ONLY ? "rb" : "w+b"); if (!stream) { error = ovsdb_io_error(errno, "%s: fdopen failed", name); goto error_close; } file = xmalloc(sizeof *file); file->name = xstrdup(name); file->lockfile = lockfile; file->stream = stream; file->prev_offset = 0; file->offset = 0; file->read_error = NULL; file->write_error = false; file->mode = OVSDB_LOG_READ; *filep = file; return NULL; error_close: close(fd); error_unlock: lockfile_unlock(lockfile); error: return error; } void ovsdb_log_close(struct ovsdb_log *file) { if (file) { free(file->name); fclose(file->stream); lockfile_unlock(file->lockfile); ovsdb_error_destroy(file->read_error); free(file); } } static const char magic[] = "OVSDB JSON "; static bool parse_header(char *header, unsigned long int *length, uint8_t sha1[SHA1_DIGEST_SIZE]) { char *p; /* 'header' must consist of a magic string... */ if (strncmp(header, magic, strlen(magic))) { return false; } /* ...followed by a length in bytes... */ *length = strtoul(header + strlen(magic), &p, 10); if (!*length || *length == ULONG_MAX || *p != ' ') { return false; } p++; /* ...followed by a SHA-1 hash... */ if (!sha1_from_hex(sha1, p)) { return false; } p += SHA1_HEX_DIGEST_LEN; /* ...and ended by a new-line. */ if (*p != '\n') { return false; } return true; } struct ovsdb_log_read_cbdata { char input[4096]; struct ovsdb_log *file; int error; unsigned long length; }; static struct ovsdb_error * parse_body(struct ovsdb_log *file, off_t offset, unsigned long int length, uint8_t sha1[SHA1_DIGEST_SIZE], struct json **jsonp) { struct json_parser *parser; struct sha1_ctx ctx; sha1_init(&ctx); parser = json_parser_create(JSPF_TRAILER); while (length > 0) { char input[BUFSIZ]; int chunk; chunk = MIN(length, sizeof input); if (fread(input, 1, chunk, file->stream) != chunk) { json_parser_abort(parser); return ovsdb_io_error(ferror(file->stream) ? errno : EOF, "%s: error reading %lu bytes " "starting at offset %lld", file->name, length, (long long int) offset); } sha1_update(&ctx, input, chunk); json_parser_feed(parser, input, chunk); length -= chunk; } sha1_final(&ctx, sha1); *jsonp = json_parser_finish(parser); return NULL; } struct ovsdb_error * ovsdb_log_read(struct ovsdb_log *file, struct json **jsonp) { uint8_t expected_sha1[SHA1_DIGEST_SIZE]; uint8_t actual_sha1[SHA1_DIGEST_SIZE]; struct ovsdb_error *error; off_t data_offset; unsigned long data_length; struct json *json; char header[128]; *jsonp = json = NULL; if (file->read_error) { return ovsdb_error_clone(file->read_error); } else if (file->mode == OVSDB_LOG_WRITE) { return OVSDB_BUG("reading file in write mode"); } if (!fgets(header, sizeof header, file->stream)) { if (feof(file->stream)) { error = NULL; } else { error = ovsdb_io_error(errno, "%s: read failed", file->name); } goto error; } if (!parse_header(header, &data_length, expected_sha1)) { error = ovsdb_syntax_error(NULL, NULL, "%s: parse error at offset " "%lld in header line \"%.*s\"", file->name, (long long int) file->offset, (int) strcspn(header, "\n"), header); goto error; } data_offset = file->offset + strlen(header); error = parse_body(file, data_offset, data_length, actual_sha1, &json); if (error) { goto error; } if (memcmp(expected_sha1, actual_sha1, SHA1_DIGEST_SIZE)) { error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at " "offset %lld have SHA-1 hash "SHA1_FMT" " "but should have hash "SHA1_FMT, file->name, data_length, (long long int) data_offset, SHA1_ARGS(actual_sha1), SHA1_ARGS(expected_sha1)); goto error; } if (json->type == JSON_STRING) { error = ovsdb_syntax_error(NULL, NULL, "%s: %lu bytes starting at " "offset %lld are not valid JSON (%s)", file->name, data_length, (long long int) data_offset, json->u.string); goto error; } file->prev_offset = file->offset; file->offset = data_offset + data_length; *jsonp = json; return NULL; error: file->read_error = ovsdb_error_clone(error); json_destroy(json); return error; } /* Causes the log record read by the previous call to ovsdb_log_read() to be * effectively discarded. The next call to ovsdb_log_write() will overwrite * that previously read record. * * Calling this function more than once has no additional effect. * * This function is useful when ovsdb_log_read() successfully reads a record * but that record does not make sense at a higher level (e.g. it specifies an * invalid transaction). */ void ovsdb_log_unread(struct ovsdb_log *file) { ovs_assert(file->mode == OVSDB_LOG_READ); file->offset = file->prev_offset; } struct ovsdb_error * ovsdb_log_write(struct ovsdb_log *file, struct json *json) { uint8_t sha1[SHA1_DIGEST_SIZE]; struct ovsdb_error *error; char *json_string; char header[128]; size_t length; json_string = NULL; if (file->mode == OVSDB_LOG_READ || file->write_error) { file->mode = OVSDB_LOG_WRITE; file->write_error = false; if (fseeko(file->stream, file->offset, SEEK_SET)) { error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld", file->name, (long long int) file->offset); goto error; } if (ftruncate(fileno(file->stream), file->offset)) { error = ovsdb_io_error(errno, "%s: cannot truncate to length %lld", file->name, (long long int) file->offset); goto error; } } if (json->type != JSON_OBJECT && json->type != JSON_ARRAY) { error = OVSDB_BUG("bad JSON type"); goto error; } /* Compose content. Add a new-line (replacing the null terminator) to make * the file easier to read, even though it has no semantic value. */ json_string = json_to_string(json, 0); length = strlen(json_string) + 1; json_string[length - 1] = '\n'; /* Compose header. */ sha1_bytes(json_string, length, sha1); snprintf(header, sizeof header, "%s%"PRIuSIZE" "SHA1_FMT"\n", magic, length, SHA1_ARGS(sha1)); /* Write. */ if (fwrite(header, strlen(header), 1, file->stream) != 1 || fwrite(json_string, length, 1, file->stream) != 1 || fflush(file->stream)) { error = ovsdb_io_error(errno, "%s: write failed", file->name); /* Remove any partially written data, ignoring errors since there is * nothing further we can do. */ ignore(ftruncate(fileno(file->stream), file->offset)); goto error; } file->offset += strlen(header) + length; free(json_string); return NULL; error: file->write_error = true; free(json_string); return error; } struct ovsdb_error * ovsdb_log_commit(struct ovsdb_log *file) { if (fsync(fileno(file->stream))) { return ovsdb_io_error(errno, "%s: fsync failed", file->name); } return NULL; } /* Returns the current offset into the file backing 'log', in bytes. This * reflects the number of bytes that have been read or written in the file. If * the whole file has been read, this is the file size. */ off_t ovsdb_log_get_offset(const struct ovsdb_log *log) { return log->offset; } openvswitch-2.5.9/ovsdb/PaxHeaders.82075/condition.h0000644000000000000000000000013213534540071017120 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.121686458 30 ctime=1567801425.077855608 openvswitch-2.5.9/ovsdb/condition.h0000644000175000017500000000465013534540071020613 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_CONDITION_H #define OVSDB_CONDITION_H 1 #include #include "compiler.h" #include "ovsdb-data.h" struct json; struct ovsdb_table_schema; struct ovsdb_row; /* These list is ordered in ascending order of the fraction of tables row that * they are (heuristically) expected to leave in query results. */ #define OVSDB_FUNCTIONS \ OVSDB_FUNCTION(OVSDB_F_EQ, "==") \ OVSDB_FUNCTION(OVSDB_F_INCLUDES, "includes") \ OVSDB_FUNCTION(OVSDB_F_LE, "<=") \ OVSDB_FUNCTION(OVSDB_F_LT, "<") \ OVSDB_FUNCTION(OVSDB_F_GE, ">=") \ OVSDB_FUNCTION(OVSDB_F_GT, ">") \ OVSDB_FUNCTION(OVSDB_F_EXCLUDES, "excludes") \ OVSDB_FUNCTION(OVSDB_F_NE, "!=") enum ovsdb_function { #define OVSDB_FUNCTION(ENUM, NAME) ENUM, OVSDB_FUNCTIONS #undef OVSDB_FUNCTION }; struct ovsdb_error *ovsdb_function_from_string(const char *, enum ovsdb_function *) OVS_WARN_UNUSED_RESULT; const char *ovsdb_function_to_string(enum ovsdb_function); struct ovsdb_clause { enum ovsdb_function function; const struct ovsdb_column *column; struct ovsdb_datum arg; }; struct ovsdb_condition { struct ovsdb_clause *clauses; size_t n_clauses; }; #define OVSDB_CONDITION_INITIALIZER { NULL, 0 } struct ovsdb_error *ovsdb_condition_from_json( const struct ovsdb_table_schema *, const struct json *, struct ovsdb_symbol_table *, struct ovsdb_condition *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_condition_to_json(const struct ovsdb_condition *); void ovsdb_condition_destroy(struct ovsdb_condition *); bool ovsdb_condition_evaluate(const struct ovsdb_row *, const struct ovsdb_condition *); #endif /* ovsdb/condition.h */ openvswitch-2.5.9/ovsdb/PaxHeaders.82075/libovsdb.pc.in0000644000000000000000000000013213534540071017516 xustar0030 mtime=1567801401.805684137 30 atime=1567801402.121686458 30 ctime=1567801424.649852453 openvswitch-2.5.9/ovsdb/libovsdb.pc.in0000644000175000017500000000034713534540071021210 0ustar00jpettitjpettit00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libovsdb Description: OVSDB library of Open vSwitch Version: @VERSION@ Libs: -L${libdir} -lovsdb Libs.private: @LIBS@ Cflags: -I${includedir} openvswitch-2.5.9/ovsdb/PaxHeaders.82075/mutation.h0000644000000000000000000000013213534540071016772 xustar0030 mtime=1567801401.809684167 30 atime=1567801402.121686458 30 ctime=1567801425.085855667 openvswitch-2.5.9/ovsdb/mutation.h0000644000175000017500000000465713534540071020474 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_MUTATION_H #define OVSDB_MUTATION_H 1 #include #include "compiler.h" #include "ovsdb-data.h" struct json; struct ovsdb_table_schema; struct ovsdb_row; /* These list is ordered in ascending order of the fraction of tables row that * they are (heuristically) expected to leave in query results. */ #define OVSDB_MUTATORS \ OVSDB_MUTATOR(OVSDB_M_ADD, "+=") \ OVSDB_MUTATOR(OVSDB_M_SUB, "-=") \ OVSDB_MUTATOR(OVSDB_M_MUL, "*=") \ OVSDB_MUTATOR(OVSDB_M_DIV, "/=") \ OVSDB_MUTATOR(OVSDB_M_MOD, "%=") \ OVSDB_MUTATOR(OVSDB_M_INSERT, "insert") \ OVSDB_MUTATOR(OVSDB_M_DELETE, "delete") enum ovsdb_mutator { #define OVSDB_MUTATOR(ENUM, NAME) ENUM, OVSDB_MUTATORS #undef OVSDB_MUTATOR }; struct ovsdb_error *ovsdb_mutator_from_string(const char *, enum ovsdb_mutator *) OVS_WARN_UNUSED_RESULT; const char *ovsdb_mutator_to_string(enum ovsdb_mutator); struct ovsdb_mutation { enum ovsdb_mutator mutator; const struct ovsdb_column *column; struct ovsdb_datum arg; struct ovsdb_type type; }; struct ovsdb_mutation_set { struct ovsdb_mutation *mutations; size_t n_mutations; }; #define OVSDB_MUTATION_SET_INITIALIZER { NULL, 0 } struct ovsdb_error *ovsdb_mutation_set_from_json( const struct ovsdb_table_schema *, const struct json *, struct ovsdb_symbol_table *, struct ovsdb_mutation_set *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_mutation_set_to_json(const struct ovsdb_mutation_set *); void ovsdb_mutation_set_destroy(struct ovsdb_mutation_set *); struct ovsdb_error *ovsdb_mutation_set_execute( struct ovsdb_row *, const struct ovsdb_mutation_set *) OVS_WARN_UNUSED_RESULT; #endif /* ovsdb/mutation.h */ openvswitch-2.5.9/PaxHeaders.82075/.travis0000644000000000000000000000013213534540117015156 xustar0030 mtime=1567801423.709845523 30 atime=1567801425.625859648 30 ctime=1567801423.709845523 openvswitch-2.5.9/.travis/0000755000175000017500000000000013534540117016721 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/.travis/PaxHeaders.82075/prepare.sh0000644000000000000000000000013213534540071017224 xustar0030 mtime=1567801401.177679525 30 atime=1567801402.037685841 30 ctime=1567801423.709845523 openvswitch-2.5.9/.travis/prepare.sh0000755000175000017500000000001613534540071020712 0ustar00jpettitjpettit00000000000000#!/bin/bash openvswitch-2.5.9/.travis/PaxHeaders.82075/build.sh0000644000000000000000000000013213534540071016665 xustar0030 mtime=1567801401.177679525 30 atime=1567801402.037685841 30 ctime=1567801423.709845523 openvswitch-2.5.9/.travis/build.sh0000755000175000017500000000501513534540071020357 0ustar00jpettitjpettit00000000000000#!/bin/bash set -o errexit KERNELSRC="" CFLAGS="-Werror" EXTRA_OPTS="" function install_kernel() { if [[ "$1" =~ ^4.* ]]; then PREFIX="v4.x" elif [[ "$1" =~ ^3.* ]]; then PREFIX="v3.x" else PREFIX="v2.6/longterm/v2.6.32" fi wget https://www.kernel.org/pub/linux/kernel/${PREFIX}/linux-${1}.tar.gz tar xzvf linux-${1}.tar.gz > /dev/null cd linux-${1} make allmodconfig # Older kernels do not include openvswitch if [ -d "net/openvswitch" ]; then make net/openvswitch/ else make net/bridge/ fi KERNELSRC=$(pwd) if [ ! "$DPDK" ]; then EXTRA_OPTS="--with-linux=$(pwd)" fi echo "Installed kernel source in $(pwd)" cd .. } function install_dpdk() { if [ -n "$DPDK_GIT" ]; then git clone $DPDK_GIT dpdk-$1 cd dpdk-$1 git checkout v$1 else wget http://www.dpdk.org/browse/dpdk/snapshot/dpdk-$1.tar.gz tar xzvf dpdk-$1.tar.gz > /dev/null cd dpdk-$1 fi find ./ -type f | xargs sed -i 's/max-inline-insns-single=100/max-inline-insns-single=400/' sed -ri 's,(CONFIG_RTE_BUILD_COMBINE_LIBS=).*,\1y,' config/common_linuxapp echo 'CONFIG_RTE_BUILD_FPIC=y' >>config/common_linuxapp sed -ri '/EXECENV_CFLAGS = -pthread -fPIC/{s/$/\nelse ifeq ($(CONFIG_RTE_BUILD_FPIC),y)/;s/$/\nEXECENV_CFLAGS = -pthread -fPIC/}' mk/exec-env/linuxapp/rte.vars.mk make config CC=gcc T=x86_64-native-linuxapp-gcc make CC=gcc RTE_KERNELDIR=$KERNELSRC echo "Installed DPDK source in $(pwd)" cd .. } function configure_ovs() { ./boot.sh && ./configure $* } if [ "$KERNEL" ] || [ "$DPDK" ]; then install_kernel $KERNEL fi if [ "$DPDK" ]; then if [ -z "$DPDK_VER" ]; then DPDK_VER="2.2.0" fi install_dpdk $DPDK_VER if [ "$CC" = "clang" ]; then # Disregard cast alignment errors until DPDK is fixed CFLAGS="$CFLAGS -Wno-cast-align" fi EXTRA_OPTS="$EXTRA_OPTS --with-dpdk=./dpdk-$DPDK_VER/build" fi configure_ovs $EXTRA_OPTS $* # Only build datapath if we are testing kernel w/o running testsuite if [ "$KERNEL" ] && [ ! "$TESTSUITE" ] && [ ! "$DPDK" ]; then cd datapath fi if [ "$CC" = "clang" ]; then make CFLAGS="$CFLAGS -Wno-error=unused-command-line-argument" else make CFLAGS="$CFLAGS $BUILD_ENV" fi if [ "$TESTSUITE" ] && [ "$CC" != "clang" ]; then if ! make distcheck RECHECK=yes; then # testsuite.log is necessary for debugging. cat */_build/tests/testsuite.log exit 1 fi fi exit 0 openvswitch-2.5.9/PaxHeaders.82075/INSTALL.NetBSD.md0000644000000000000000000000013213534540071016352 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.677845288 openvswitch-2.5.9/INSTALL.NetBSD.md0000644000175000017500000000231713534540071020043 0ustar00jpettitjpettit00000000000000How to Install Open vSwitch on NetBSD ===================================== On NetBSD, you might want to install requirements from pkgsrc. In that case, you need at least the following packages. * automake * libtool-base * gmake * python27 * py27-xml * pkg_alternatives Some components have additional requirements. (See [INSTALL.md]) Assuming you are running NetBSD/amd64 6.1.2, you can download and install pre-built binary packages as the following. (You might get some warnings about minor version mismatch. Don't care.) ``` # PKG_PATH=http://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/6.1.2/All/ # export PKG_PATH # pkg_add automake libtool-base gmake python27 py27-xml pkg_alternatives ``` NetBSD's `/usr/bin/make` is not GNU make. GNU make is installed as `/usr/pkg/bin/gmake` by the above mentioned `gmake` package. As all executables installed with pkgsrc are placed in `/usr/pkg/bin/` directory, it might be a good idea to add it to your PATH. Open vSwitch on NetBSD is currently "userspace switch" implementation in the sense described in [INSTALL.userspace.md] and [PORTING.md]. [INSTALL.md]:INSTALL.md [INSTALL.userspace.md]:INSTALL.userspace.md [PORTING.md]:PORTING.md openvswitch-2.5.9/PaxHeaders.82075/include0000644000000000000000000000013213534540120015205 xustar0030 mtime=1567801424.597852069 30 atime=1567801425.625859648 30 ctime=1567801424.597852069 openvswitch-2.5.9/include/0000755000175000017500000000000013534540120016750 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/PaxHeaders.82075/openflow0000644000000000000000000000013213534540120017036 xustar0030 mtime=1567801424.601852099 30 atime=1567801425.625859648 30 ctime=1567801424.601852099 openvswitch-2.5.9/include/openflow/0000755000175000017500000000000013534540120020601 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/openflow/PaxHeaders.82075/netronome-ext.h0000644000000000000000000000013213534540071022075 xustar0030 mtime=1567801401.293680376 30 atime=1567801402.065686047 30 ctime=1567801424.573851893 openvswitch-2.5.9/include/openflow/netronome-ext.h0000644000175000017500000000512013534540071023561 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Netronome. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENFLOW_NETRONOME_EXT_H #define OPENFLOW_NETRONOME_EXT_H 1 #include "openflow/openflow.h" #include "openvswitch/types.h" /* The following vendor extension, proposed by Netronome, is not yet * standardized, so they are not included in openflow.h. It may * be suitable for standardization */ /* Netronome enhanced select group */ enum ntr_group_mod_subtype { NTRT_SELECTION_METHOD = 1, }; #define NTR_MAX_SELECTION_METHOD_LEN 16 struct ntr_group_prop_selection_method { ovs_be16 type; /* OFPGPT15_EXPERIMENTER. */ ovs_be16 length; /* Length in bytes of this property * excluding trailing padding. */ ovs_be32 experimenter; /* NTR_VENDOR_ID. */ ovs_be32 exp_type; /* NTRT_SELECTION_METHOD. */ ovs_be32 pad; char selection_method[NTR_MAX_SELECTION_METHOD_LEN]; /* Null-terminated */ ovs_be64 selection_method_param; /* Non-Field parameter for * bucket selection. */ /* Followed by: * - Exactly (length - 40) (possibly 0) bytes containing OXM TLVs, then * - Exactly ((length + 7)/8*8 - length) (between 0 and 7) bytes of * all-zero bytes * In summary, ntr_group_prop_selection_method is padded as needed, * to make its overall size a multiple of 8, to preserve alignment * in structures using it. */ /* uint8_t field_array[0]; */ /* Zero or more fields encoded as * OXM TLVs where the has_mask bit must * be zero and the value it specifies is * a mask to apply to packet fields and * then input them to the selection * method of a select group. */ /* uint8_t pad2[0]; */ }; OFP_ASSERT(sizeof(struct ntr_group_prop_selection_method) == 40); #endif /* openflow/netronome-ext.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/nicira-ext.h0000644000000000000000000000013213534540071021334 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.573851893 openvswitch-2.5.9/include/openflow/nicira-ext.h0000644000175000017500000013014313534540071023024 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENFLOW_NICIRA_EXT_H #define OPENFLOW_NICIRA_EXT_H 1 #include #include /* The following vendor extensions, proposed by Nicira, are not yet * standardized, so they are not included in openflow.h. Some of them may be * suitable for standardization; others we never expect to standardize. */ /* Nicira vendor-specific error messages extension. * * OpenFlow 1.0 has a set of predefined error types (OFPET_*) and codes (which * are specific to each type). It does not have any provision for * vendor-specific error codes, and it does not even provide "generic" error * codes that can apply to problems not anticipated by the OpenFlow * specification authors. * * This extension attempts to address the problem by adding a generic "error * vendor extension". The extension works as follows: use NXET_VENDOR as type * and NXVC_VENDOR_ERROR as code, followed by struct nx_vendor_error with * vendor-specific details, followed by at least 64 bytes of the failed * request. * * It would be better to have a type-specific vendor extension, e.g. so that * OFPET_BAD_ACTION could be used with vendor-specific code values. But * OFPET_BAD_ACTION and most other standardized types already specify that * their 'data' values are (the start of) the OpenFlow message being replied * to, so there is no room to insert a vendor ID. * * Currently this extension is only implemented by Open vSwitch, but it seems * like a reasonable candidate for future standardization. */ /* This is a random number to avoid accidental collision with any other * vendor's extension. */ #define NXET_VENDOR 0xb0c2 /* ofp_error msg 'code' values for NXET_VENDOR. */ enum nx_vendor_code { NXVC_VENDOR_ERROR /* 'data' contains struct nx_vendor_error. */ }; /* 'data' for 'type' == NXET_VENDOR, 'code' == NXVC_VENDOR_ERROR. */ struct nx_vendor_error { ovs_be32 vendor; /* Vendor ID as in struct ofp_vendor_header. */ ovs_be16 type; /* Vendor-defined type. */ ovs_be16 code; /* Vendor-defined subtype. */ /* Followed by at least the first 64 bytes of the failed request. */ }; /* Nicira vendor requests and replies. */ /* Header for Nicira vendor requests and replies. */ struct nicira_header { struct ofp_header header; ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be32 subtype; /* See the NXT numbers in ofp-msgs.h. */ }; OFP_ASSERT(sizeof(struct nicira_header) == 16); /* Header for Nicira vendor stats request and reply messages in OpenFlow * 1.0. */ struct nicira10_stats_msg { struct ofp10_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */ ovs_be32 subtype; /* One of NXST_* below. */ uint8_t pad[4]; /* Align to 64-bits. */ }; OFP_ASSERT(sizeof(struct nicira10_stats_msg) == 24); /* Header for Nicira vendor stats request and reply messages in OpenFlow * 1.1. */ struct nicira11_stats_msg { struct ofp11_vendor_stats_msg vsm; /* Vendor NX_VENDOR_ID. */ ovs_be32 subtype; /* One of NXST_* below. */ }; OFP_ASSERT(sizeof(struct nicira11_stats_msg) == 24); /* Fields to use when hashing flows. */ enum nx_hash_fields { /* Ethernet source address (NXM_OF_ETH_SRC) only. */ NX_HASH_FIELDS_ETH_SRC, /* L2 through L4, symmetric across src/dst. Specifically, each of the * following fields, if present, is hashed (slashes separate symmetric * pairs): * * - NXM_OF_ETH_DST / NXM_OF_ETH_SRC * - NXM_OF_ETH_TYPE * - The VID bits from NXM_OF_VLAN_TCI, ignoring PCP and CFI. * - NXM_OF_IP_PROTO * - NXM_OF_IP_SRC / NXM_OF_IP_DST * - NXM_OF_TCP_SRC / NXM_OF_TCP_DST */ NX_HASH_FIELDS_SYMMETRIC_L4, /* L3+L4 only, including the following fields: * * - NXM_OF_IP_PROTO * - NXM_OF_IP_SRC / NXM_OF_IP_DST * - NXM_OF_SCTP_SRC / NXM_OF_SCTP_DST * - NXM_OF_TCP_SRC / NXM_OF_TCP_DST */ NX_HASH_FIELDS_SYMMETRIC_L3L4, /* L3+L4 only with UDP ports, including the following fields: * * - NXM_OF_IP_PROTO * - NXM_OF_IP_SRC / NXM_OF_IP_DST * - NXM_OF_SCTP_SRC / NXM_OF_SCTP_DST * - NXM_OF_TCP_SRC / NXM_OF_TCP_DST * - NXM_OF_UDP_SRC / NXM_OF_UDP_DST */ NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP }; /* This command enables or disables an Open vSwitch extension that allows a * controller to specify the OpenFlow table to which a flow should be added, * instead of having the switch decide which table is most appropriate as * required by OpenFlow 1.0. Because NXM was designed as an extension to * OpenFlow 1.0, the extension applies equally to ofp10_flow_mod and * nx_flow_mod. By default, the extension is disabled. * * When this feature is enabled, Open vSwitch treats struct ofp10_flow_mod's * and struct nx_flow_mod's 16-bit 'command' member as two separate fields. * The upper 8 bits are used as the table ID, the lower 8 bits specify the * command as usual. A table ID of 0xff is treated like a wildcarded table ID. * * The specific treatment of the table ID depends on the type of flow mod: * * - OFPFC_ADD: Given a specific table ID, the flow is always placed in that * table. If an identical flow already exists in that table only, then it * is replaced. If the flow cannot be placed in the specified table, * either because the table is full or because the table cannot support * flows of the given type, the switch replies with an OFPFMFC_TABLE_FULL * error. (A controller can distinguish these cases by comparing the * current and maximum number of entries reported in ofp_table_stats.) * * If the table ID is wildcarded, the switch picks an appropriate table * itself. If an identical flow already exist in the selected flow table, * then it is replaced. The choice of table might depend on the flows * that are already in the switch; for example, if one table fills up then * the switch might fall back to another one. * * - OFPFC_MODIFY, OFPFC_DELETE: Given a specific table ID, only flows * within that table are matched and modified or deleted. If the table ID * is wildcarded, flows within any table may be matched and modified or * deleted. * * - OFPFC_MODIFY_STRICT, OFPFC_DELETE_STRICT: Given a specific table ID, * only a flow within that table may be matched and modified or deleted. * If the table ID is wildcarded and exactly one flow within any table * matches, then it is modified or deleted; if flows in more than one * table match, then none is modified or deleted. */ struct nx_flow_mod_table_id { uint8_t set; /* Nonzero to enable, zero to disable. */ uint8_t pad[7]; }; OFP_ASSERT(sizeof(struct nx_flow_mod_table_id) == 8); enum nx_packet_in_format { NXPIF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */ NXPIF_NXM = 1 /* Nicira Extended. */ }; /* NXT_SET_PACKET_IN_FORMAT request. */ struct nx_set_packet_in_format { ovs_be32 format; /* One of NXPIF_*. */ }; OFP_ASSERT(sizeof(struct nx_set_packet_in_format) == 4); /* NXT_PACKET_IN (analogous to OFPT_PACKET_IN). * * NXT_PACKET_IN is similar to the OpenFlow 1.2 OFPT_PACKET_IN. The * differences are: * * - NXT_PACKET_IN includes the cookie of the rule that triggered the * message. (OpenFlow 1.3 OFPT_PACKET_IN also includes the cookie.) * * - The metadata fields use NXM (instead of OXM) field numbers. * * Open vSwitch 1.9.0 and later omits metadata fields that are zero (as allowed * by OpenFlow 1.2). Earlier versions included all implemented metadata * fields. * * Open vSwitch does not include non-metadata in the nx_match, because by * definition that information can be found in the packet itself. The format * and the standards allow this, however, so controllers should be prepared to * tolerate future changes. * * The NXM format is convenient for reporting metadata values, but it is * important not to interpret the format as matching against a flow, because it * does not. Nothing is being matched; arbitrary metadata masks would not be * meaningful. * * Whereas in most cases a controller can expect to only get back NXM fields * that it set up itself (e.g. flow dumps will ordinarily report only NXM * fields from flows that the controller added), NXT_PACKET_IN messages might * contain fields that the controller does not understand, because the switch * might support fields (new registers, new protocols, etc.) that the * controller does not. The controller must prepared to tolerate these. * * The 'cookie' field has no meaning when 'reason' is OFPR_NO_MATCH. In this * case it should be UINT64_MAX. */ struct nx_packet_in { ovs_be32 buffer_id; /* ID assigned by datapath. */ ovs_be16 total_len; /* Full length of frame. */ uint8_t reason; /* Reason packet is sent (one of OFPR_*). */ uint8_t table_id; /* ID of the table that was looked up. */ ovs_be64 cookie; /* Cookie of the rule that was looked up. */ ovs_be16 match_len; /* Size of nx_match. */ uint8_t pad[6]; /* Align to 64-bits. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, then * - Exactly 2 all-zero padding bytes, then * - An Ethernet frame whose length is inferred from nxh.header.length. * * The padding bytes preceding the Ethernet frame ensure that the IP * header (if any) following the Ethernet header is 32-bit aligned. */ /* uint8_t nxm_fields[...]; */ /* NXM headers. */ /* uint8_t pad[2]; */ /* Align to 64 bit + 16 bit. */ /* uint8_t data[0]; */ /* Ethernet frame. */ }; OFP_ASSERT(sizeof(struct nx_packet_in) == 24); /* Configures the "role" of the sending controller. The default role is: * * - Other (NX_ROLE_OTHER), which allows the controller access to all * OpenFlow features. * * The other possible roles are a related pair: * * - Master (NX_ROLE_MASTER) is equivalent to Other, except that there may * be at most one Master controller at a time: when a controller * configures itself as Master, any existing Master is demoted to the * Slave role. * * - Slave (NX_ROLE_SLAVE) allows the controller read-only access to * OpenFlow features. In particular attempts to modify the flow table * will be rejected with an OFPBRC_EPERM error. * * Slave controllers do not receive OFPT_PACKET_IN or OFPT_FLOW_REMOVED * messages, but they do receive OFPT_PORT_STATUS messages. */ struct nx_role_request { ovs_be32 role; /* One of NX_ROLE_*. */ }; OFP_ASSERT(sizeof(struct nx_role_request) == 4); enum nx_role { NX_ROLE_OTHER, /* Default role, full access. */ NX_ROLE_MASTER, /* Full access, at most one. */ NX_ROLE_SLAVE /* Read-only access. */ }; /* NXT_SET_ASYNC_CONFIG. * * Sent by a controller, this message configures the asynchronous messages that * the controller wants to receive. Element 0 in each array specifies messages * of interest when the controller has an "other" or "master" role; element 1, * when the controller has a "slave" role. * * Each array element is a bitmask in which a 0-bit disables receiving a * particular message and a 1-bit enables receiving it. Each bit controls the * message whose 'reason' corresponds to the bit index. For example, the bit * with value 1<<2 == 4 in port_status_mask[1] determines whether the * controller will receive OFPT_PORT_STATUS messages with reason OFPPR_MODIFY * (value 2) when the controller has a "slave" role. * * As a side effect, for service controllers, this message changes the * miss_send_len from default of zero to OFP_DEFAULT_MISS_SEND_LEN (128). */ struct nx_async_config { ovs_be32 packet_in_mask[2]; /* Bitmasks of OFPR_* values. */ ovs_be32 port_status_mask[2]; /* Bitmasks of OFPRR_* values. */ ovs_be32 flow_removed_mask[2]; /* Bitmasks of OFPPR_* values. */ }; OFP_ASSERT(sizeof(struct nx_async_config) == 24); /* Flexible flow specifications (aka NXM = Nicira Extended Match). * * OpenFlow 1.0 has "struct ofp10_match" for specifying flow matches. This * structure is fixed-length and hence difficult to extend. This section * describes a more flexible, variable-length flow match, called "nx_match" for * short, that is also supported by Open vSwitch. This section also defines a * replacement for each OpenFlow message that includes struct ofp10_match. * * OpenFlow 1.2+ introduced OpenFlow Extensible Match (OXM), adapting * the design of NXM. The format of NXM and OXM are compatible. * * * Format * ====== * * An nx_match is a sequence of zero or more "nxm_entry"s, which are * type-length-value (TLV) entries, each 5 to 259 (inclusive) bytes long. * "nxm_entry"s are not aligned on or padded to any multibyte boundary. The * first 4 bytes of an nxm_entry are its "header", followed by the entry's * "body". * * An nxm_entry's header is interpreted as a 32-bit word in network byte order: * * |<-------------------- nxm_type ------------------>| * | | * |31 16 15 9| 8 7 0 * +----------------------------------+---------------+--+------------------+ * | nxm_vendor | nxm_field |hm| nxm_length | * +----------------------------------+---------------+--+------------------+ * * The most-significant 23 bits of the header are collectively "nxm_type". * Bits 16...31 are "nxm_vendor", one of OFPXMC12_* values. In case of * NXM, it's either OFPXMC12_NXM_0 or OFPXMC12_NXM_1. * Bits 9...15 are "nxm_field", which is a vendor-specific value. nxm_type * normally designates a protocol header, such as the Ethernet type, but it * can also refer to packet metadata, such as the switch port on which a packet * arrived. * * Bit 8 is "nxm_hasmask" (labeled "hm" above for space reasons). The meaning * of this bit is explained later. * * The least-significant 8 bits are "nxm_length", a positive integer. The * length of the nxm_entry, including the header, is exactly 4 + nxm_length * bytes. * * For a given nxm_vendor, nxm_field, and nxm_hasmask value, nxm_length is a * constant. It is included only to allow software to minimally parse * "nxm_entry"s of unknown types. (Similarly, for a given nxm_vendor, * nxm_field, and nxm_length, nxm_hasmask is a constant.) * * * Semantics * ========= * * A zero-length nx_match (one with no "nxm_entry"s) matches every packet. * * An nxm_entry places a constraint on the packets matched by the nx_match: * * - If nxm_hasmask is 0, the nxm_entry's body contains a value for the * field, called "nxm_value". The nx_match matches only packets in which * the field equals nxm_value. * * - If nxm_hasmask is 1, then the nxm_entry's body contains a value for the * field (nxm_value), followed by a bitmask of the same length as the * value, called "nxm_mask". For each 1-bit in position J in nxm_mask, the * nx_match matches only packets for which bit J in the given field's value * matches bit J in nxm_value. A 0-bit in nxm_mask causes the * corresponding bit in nxm_value is ignored (it should be 0; Open vSwitch * may enforce this someday), as is the corresponding bit in the field's * value. (The sense of the nxm_mask bits is the opposite of that used by * the "wildcards" member of struct ofp10_match.) * * When nxm_hasmask is 1, nxm_length is always even. * * An all-zero-bits nxm_mask is equivalent to omitting the nxm_entry * entirely. An all-one-bits nxm_mask is equivalent to specifying 0 for * nxm_hasmask. * * When there are multiple "nxm_entry"s, all of the constraints must be met. * * * Mask Restrictions * ================= * * Masks may be restricted: * * - Some nxm_types may not support masked wildcards, that is, nxm_hasmask * must always be 0 when these fields are specified. For example, the * field that identifies the port on which a packet was received may not be * masked. * * - Some nxm_types that do support masked wildcards may only support certain * nxm_mask patterns. For example, fields that have IPv4 address values * may be restricted to CIDR masks. * * These restrictions should be noted in specifications for individual fields. * A switch may accept an nxm_hasmask or nxm_mask value that the specification * disallows, if the switch correctly implements support for that nxm_hasmask * or nxm_mask value. A switch must reject an attempt to set up a flow that * contains a nxm_hasmask or nxm_mask value that it does not support. * * * Prerequisite Restrictions * ========================= * * The presence of an nxm_entry with a given nxm_type may be restricted based * on the presence of or values of other "nxm_entry"s. For example: * * - An nxm_entry for nxm_type=NXM_OF_IP_TOS is allowed only if it is * preceded by another entry with nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, * and nxm_value=0x0800. That is, matching on the IP source address is * allowed only if the Ethernet type is explicitly set to IP. * * - An nxm_entry for nxm_type=NXM_OF_TCP_SRC is allowed only if it is * preceded by an entry with nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and * nxm_value either 0x0800 or 0x86dd, and another with * nxm_type=NXM_OF_IP_PROTO, nxm_hasmask=0, nxm_value=6, in that order. * That is, matching on the TCP source port is allowed only if the Ethernet * type is IP or IPv6 and the IP protocol is TCP. * * These restrictions should be noted in specifications for individual fields. * A switch may implement relaxed versions of these restrictions. A switch * must reject an attempt to set up a flow that violates its restrictions. * * * Ordering Restrictions * ===================== * * An nxm_entry that has prerequisite restrictions must appear after the * "nxm_entry"s for its prerequisites. Ordering of "nxm_entry"s within an * nx_match is not otherwise constrained. * * Any given nxm_type may appear in an nx_match at most once. * * * nxm_entry Examples * ================== * * These examples show the format of a single nxm_entry with particular * nxm_hasmask and nxm_length values. The diagrams are labeled with field * numbers and byte indexes. * * * 8-bit nxm_value, nxm_hasmask=1, nxm_length=2: * * 0 3 4 5 * +------------+---+---+ * | header | v | m | * +------------+---+---+ * * * 16-bit nxm_value, nxm_hasmask=0, nxm_length=2: * * 0 3 4 5 * +------------+------+ * | header | value| * +------------+------+ * * * 32-bit nxm_value, nxm_hasmask=0, nxm_length=4: * * 0 3 4 7 * +------------+-------------+ * | header | nxm_value | * +------------+-------------+ * * * 48-bit nxm_value, nxm_hasmask=0, nxm_length=6: * * 0 3 4 9 * +------------+------------------+ * | header | nxm_value | * +------------+------------------+ * * * 48-bit nxm_value, nxm_hasmask=1, nxm_length=12: * * 0 3 4 9 10 15 * +------------+------------------+------------------+ * | header | nxm_value | nxm_mask | * +------------+------------------+------------------+ * * * Error Reporting * =============== * * A switch should report an error in an nx_match using error type * OFPET_BAD_REQUEST and one of the NXBRC_NXM_* codes. Ideally the switch * should report a specific error code, if one is assigned for the particular * problem, but NXBRC_NXM_INVALID is also available to report a generic * nx_match error. */ /* Number of registers allocated NXM field IDs. */ #define NXM_NX_MAX_REGS 16 /* Bits in the value of NXM_NX_IP_FRAG. */ #define NX_IP_FRAG_ANY (1 << 0) /* Is this a fragment? */ #define NX_IP_FRAG_LATER (1 << 1) /* Is this a fragment with nonzero offset? */ /* Bits in the value of NXM_NX_TUN_FLAGS. */ #define NX_TUN_FLAG_OAM (1 << 0) /* Is this an OAM packet? */ /* ## --------------------- ## */ /* ## Requests and replies. ## */ /* ## --------------------- ## */ enum nx_flow_format { NXFF_OPENFLOW10 = 0, /* Standard OpenFlow 1.0 compatible. */ NXFF_NXM = 2 /* Nicira extended match. */ }; /* NXT_SET_FLOW_FORMAT request. */ struct nx_set_flow_format { ovs_be32 format; /* One of NXFF_*. */ }; OFP_ASSERT(sizeof(struct nx_set_flow_format) == 4); /* NXT_FLOW_MOD (analogous to OFPT_FLOW_MOD). * * It is possible to limit flow deletions and modifications to certain * cookies by using the NXM_NX_COOKIE(_W) matches. The "cookie" field * is used only to add or modify flow cookies. */ struct nx_flow_mod { ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be16 command; /* OFPFC_* + possibly a table ID (see comment * on struct nx_flow_mod_table_id). */ ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ ovs_be16 priority; /* Priority level of flow entry. */ ovs_be32 buffer_id; /* Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. */ ovs_be16 out_port; /* For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */ ovs_be16 flags; /* One of OFPFF_*. */ ovs_be16 match_len; /* Size of nx_match. */ uint8_t pad[6]; /* Align to 64-bits. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, then * - Actions to fill out the remainder of the message length (always a * multiple of 8). */ }; OFP_ASSERT(sizeof(struct nx_flow_mod) == 32); /* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED). * * 'table_id' is present only in Open vSwitch 1.11 and later. In earlier * versions of Open vSwitch, this is a padding byte that is always zeroed. * Therefore, a 'table_id' value of 0 indicates that the table ID is not known, * and other values may be interpreted as one more than the flow's former table * ID. */ struct nx_flow_removed { ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be16 priority; /* Priority level of flow entry. */ uint8_t reason; /* One of OFPRR_*. */ uint8_t table_id; /* Flow's former table ID, plus one. */ ovs_be32 duration_sec; /* Time flow was alive in seconds. */ ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond duration_sec. */ ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */ ovs_be16 match_len; /* Size of nx_match. */ ovs_be64 packet_count; ovs_be64 byte_count; /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes. */ }; OFP_ASSERT(sizeof(struct nx_flow_removed) == 40); /* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW * request). * * It is possible to limit matches to certain cookies by using the * NXM_NX_COOKIE and NXM_NX_COOKIE_W matches. */ struct nx_flow_stats_request { ovs_be16 out_port; /* Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */ ovs_be16 match_len; /* Length of nx_match. */ uint8_t table_id; /* ID of table to read (from ofp_table_stats) or 0xff for all tables. */ uint8_t pad[3]; /* Align to 64 bits. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, which must also exactly fill out the length of the * message. */ }; OFP_ASSERT(sizeof(struct nx_flow_stats_request) == 8); /* Body for Nicira vendor stats reply of type NXST_FLOW (analogous to * OFPST_FLOW reply). * * The values of 'idle_age' and 'hard_age' are only meaningful when talking to * a switch that implements the NXT_FLOW_AGE extension. Zero means that the * true value is unknown, perhaps because hardware does not track the value. * (Zero is also the value that one should ordinarily expect to see talking to * a switch that does not implement NXT_FLOW_AGE, since those switches zero the * padding bytes that these fields replaced.) A nonzero value X represents X-1 * seconds. A value of 65535 represents 65534 or more seconds. * * 'idle_age' is the number of seconds that the flow has been idle, that is, * the number of seconds since a packet passed through the flow. 'hard_age' is * the number of seconds since the flow was last modified (e.g. OFPFC_MODIFY or * OFPFC_MODIFY_STRICT). (The 'duration_*' fields are the elapsed time since * the flow was added, regardless of subsequent modifications.) * * For a flow with an idle or hard timeout, 'idle_age' or 'hard_age', * respectively, will ordinarily be smaller than the timeout, but flow * expiration times are only approximate and so one must be prepared to * tolerate expirations that occur somewhat early or late. */ struct nx_flow_stats { ovs_be16 length; /* Length of this entry. */ uint8_t table_id; /* ID of table flow came from. */ uint8_t pad; ovs_be32 duration_sec; /* Time flow has been alive in seconds. */ ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds beyond duration_sec. */ ovs_be16 priority; /* Priority of the entry. */ ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */ ovs_be16 hard_timeout; /* Number of seconds before expiration. */ ovs_be16 match_len; /* Length of nx_match. */ ovs_be16 idle_age; /* Seconds since last packet, plus one. */ ovs_be16 hard_age; /* Seconds since last modification, plus one. */ ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be64 packet_count; /* Number of packets, UINT64_MAX if unknown. */ ovs_be64 byte_count; /* Number of bytes, UINT64_MAX if unknown. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, then * - Actions to fill out the remainder 'length' bytes (always a multiple * of 8). */ }; OFP_ASSERT(sizeof(struct nx_flow_stats) == 48); /* Nicira vendor stats request of type NXST_AGGREGATE (analogous to * OFPST_AGGREGATE request). * * The reply format is identical to the reply format for OFPST_AGGREGATE, * except for the header. */ struct nx_aggregate_stats_request { ovs_be16 out_port; /* Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */ ovs_be16 match_len; /* Length of nx_match. */ uint8_t table_id; /* ID of table to read (from ofp_table_stats) or 0xff for all tables. */ uint8_t pad[3]; /* Align to 64 bits. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, which must also exactly fill out the length of the * message. */ }; OFP_ASSERT(sizeof(struct nx_aggregate_stats_request) == 8); /* NXT_SET_CONTROLLER_ID. * * Each OpenFlow controller connection has a 16-bit identifier that is * initially 0. This message changes the connection's ID to 'id'. * * Controller connection IDs need not be unique. * * The NXAST_CONTROLLER action is the only current user of controller * connection IDs. */ struct nx_controller_id { uint8_t zero[6]; /* Must be zero. */ ovs_be16 controller_id; /* New controller connection ID. */ }; OFP_ASSERT(sizeof(struct nx_controller_id) == 8); /* Flow Table Monitoring * ===================== * * NXST_FLOW_MONITOR allows a controller to keep track of changes to OpenFlow * flow table(s) or subsets of them, with the following workflow: * * 1. The controller sends an NXST_FLOW_MONITOR request to begin monitoring * flows. The 'id' in the request must be unique among all monitors that * the controller has started and not yet canceled on this OpenFlow * connection. * * 2. The switch responds with an NXST_FLOW_MONITOR reply. If the request's * 'flags' included NXFMF_INITIAL, the reply includes all the flows that * matched the request at the time of the request (with event NXFME_ADDED). * If 'flags' did not include NXFMF_INITIAL, the reply is empty. * * The reply uses the xid of the request (as do all replies to OpenFlow * requests). * * 3. Whenever a change to a flow table entry matches some outstanding monitor * request's criteria and flags, the switch sends a notification to the * controller as an additional NXST_FLOW_MONITOR reply with xid 0. * * When multiple outstanding monitors match a single change, only a single * notification is sent. This merged notification includes the information * requested in any of the individual monitors. That is, if any of the * matching monitors requests actions (NXFMF_ACTIONS), the notification * includes actions, and if any of the monitors request full changes for the * controller's own changes (NXFMF_OWN), the controller's own changes will * be included in full. * * 4. The controller may cancel a monitor with NXT_FLOW_MONITOR_CANCEL. No * further notifications will be sent on the basis of the canceled monitor * afterward. * * * Buffer Management * ================= * * OpenFlow messages for flow monitor notifications can overflow the buffer * space available to the switch, either temporarily (e.g. due to network * conditions slowing OpenFlow traffic) or more permanently (e.g. the sustained * rate of flow table change exceeds the network bandwidth between switch and * controller). * * When Open vSwitch's notification buffer space reaches a limiting threshold, * OVS reacts as follows: * * 1. OVS sends an NXT_FLOW_MONITOR_PAUSED message to the controller, following * all the already queued notifications. After it receives this message, * the controller knows that its view of the flow table, as represented by * flow monitor notifications, is incomplete. * * 2. As long as the notification buffer is not empty: * * - NXMFE_ADD and NXFME_MODIFIED notifications will not be sent. * * - NXFME_DELETED notifications will still be sent, but only for flows * that existed before OVS sent NXT_FLOW_MONITOR_PAUSED. * * - NXFME_ABBREV notifications will not be sent. They are treated as * the expanded version (and therefore only the NXFME_DELETED * components, if any, are sent). * * 3. When the notification buffer empties, OVS sends NXFME_ADD notifications * for flows added since the buffer reached its limit and NXFME_MODIFIED * notifications for flows that existed before the limit was reached and * changed after the limit was reached. * * 4. OVS sends an NXT_FLOW_MONITOR_RESUMED message to the controller. After * it receives this message, the controller knows that its view of the flow * table, as represented by flow monitor notifications, is again complete. * * This allows the maximum buffer space requirement for notifications to be * bounded by the limit plus the maximum number of supported flows. * * * "Flow Removed" messages * ======================= * * The flow monitor mechanism is independent of OFPT_FLOW_REMOVED and * NXT_FLOW_REMOVED. Flow monitor updates for deletion are sent if * NXFMF_DELETE is set on a monitor, regardless of whether the * OFPFF_SEND_FLOW_REM flag was set when the flow was added. */ /* NXST_FLOW_MONITOR request. * * The NXST_FLOW_MONITOR request's body consists of an array of zero or more * instances of this structure. The request arranges to monitor the flows * that match the specified criteria, which are interpreted in the same way as * for NXST_FLOW. * * 'id' identifies a particular monitor for the purpose of allowing it to be * canceled later with NXT_FLOW_MONITOR_CANCEL. 'id' must be unique among * existing monitors that have not already been canceled. * * The reply includes the initial flow matches for monitors that have the * NXFMF_INITIAL flag set. No single flow will be included in the reply more * than once, even if more than one requested monitor matches that flow. The * reply will be empty if none of the monitors has NXFMF_INITIAL set or if none * of the monitors initially matches any flows. * * For NXFMF_ADD, an event will be reported if 'out_port' matches against the * actions of the flow being added or, for a flow that is replacing an existing * flow, if 'out_port' matches against the actions of the flow being replaced. * For NXFMF_DELETE, 'out_port' matches against the actions of a flow being * deleted. For NXFMF_MODIFY, an event will be reported if 'out_port' matches * either the old or the new actions. */ struct nx_flow_monitor_request { ovs_be32 id; /* Controller-assigned ID for this monitor. */ ovs_be16 flags; /* NXFMF_*. */ ovs_be16 out_port; /* Required output port, if not OFPP_NONE. */ ovs_be16 match_len; /* Length of nx_match. */ uint8_t table_id; /* One table's ID or 0xff for all tables. */ uint8_t zeros[5]; /* Align to 64 bits (must be zero). */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes. */ }; OFP_ASSERT(sizeof(struct nx_flow_monitor_request) == 16); /* 'flags' bits in struct nx_flow_monitor_request. */ enum nx_flow_monitor_flags { /* When to send updates. */ NXFMF_INITIAL = 1 << 0, /* Initially matching flows. */ NXFMF_ADD = 1 << 1, /* New matching flows as they are added. */ NXFMF_DELETE = 1 << 2, /* Old matching flows as they are removed. */ NXFMF_MODIFY = 1 << 3, /* Matching flows as they are changed. */ /* What to include in updates. */ NXFMF_ACTIONS = 1 << 4, /* If set, actions are included. */ NXFMF_OWN = 1 << 5, /* If set, include own changes in full. */ }; /* NXST_FLOW_MONITOR reply header. * * The body of an NXST_FLOW_MONITOR reply is an array of variable-length * structures, each of which begins with this header. The 'length' member may * be used to traverse the array, and the 'event' member may be used to * determine the particular structure. * * Every instance is a multiple of 8 bytes long. */ struct nx_flow_update_header { ovs_be16 length; /* Length of this entry. */ ovs_be16 event; /* One of NXFME_*. */ /* ...other data depending on 'event'... */ }; OFP_ASSERT(sizeof(struct nx_flow_update_header) == 4); /* 'event' values in struct nx_flow_update_header. */ enum nx_flow_update_event { /* struct nx_flow_update_full. */ NXFME_ADDED = 0, /* Flow was added. */ NXFME_DELETED = 1, /* Flow was deleted. */ NXFME_MODIFIED = 2, /* Flow (generally its actions) was changed. */ /* struct nx_flow_update_abbrev. */ NXFME_ABBREV = 3, /* Abbreviated reply. */ }; /* NXST_FLOW_MONITOR reply for NXFME_ADDED, NXFME_DELETED, and * NXFME_MODIFIED. */ struct nx_flow_update_full { ovs_be16 length; /* Length is 24. */ ovs_be16 event; /* One of NXFME_*. */ ovs_be16 reason; /* OFPRR_* for NXFME_DELETED, else zero. */ ovs_be16 priority; /* Priority of the entry. */ ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */ ovs_be16 hard_timeout; /* Number of seconds before expiration. */ ovs_be16 match_len; /* Length of nx_match. */ uint8_t table_id; /* ID of flow's table. */ uint8_t pad; /* Reserved, currently zeroed. */ ovs_be64 cookie; /* Opaque controller-issued identifier. */ /* Followed by: * - Exactly match_len (possibly 0) bytes containing the nx_match, then * - Exactly (match_len + 7)/8*8 - match_len (between 0 and 7) bytes of * all-zero bytes, then * - Actions to fill out the remainder 'length' bytes (always a multiple * of 8). If NXFMF_ACTIONS was not specified, or 'event' is * NXFME_DELETED, no actions are included. */ }; OFP_ASSERT(sizeof(struct nx_flow_update_full) == 24); /* NXST_FLOW_MONITOR reply for NXFME_ABBREV. * * When the controller does not specify NXFMF_OWN in a monitor request, any * flow tables changes due to the controller's own requests (on the same * OpenFlow channel) will be abbreviated, when possible, to this form, which * simply specifies the 'xid' of the OpenFlow request (e.g. an OFPT_FLOW_MOD or * NXT_FLOW_MOD) that caused the change. * * Some changes cannot be abbreviated and will be sent in full: * * - Changes that only partially succeed. This can happen if, for example, * a flow_mod with type OFPFC_MODIFY affects multiple flows, but only some * of those modifications succeed (e.g. due to hardware limitations). * * This cannot occur with the Open vSwitch software datapath. This also * cannot occur in Open vSwitch 2.4 and later, because these versions only * execute any flow modifications if all of them will succeed. * * - Changes that race with conflicting changes made by other controllers or * other flow_mods (not separated by barriers) by the same controller. * * This cannot occur with the current Open vSwitch implementation * (regardless of datapath) because Open vSwitch internally serializes * potentially conflicting changes. * * - Changes that occur when flow notification is paused (see "Buffer * Management" above). * * A flow_mod that does not change the flow table will not trigger any * notification, even an abbreviated one. For example, a "modify" or "delete" * flow_mod that does not match any flows will not trigger a notification. * Whether an "add" or "modify" that specifies all the same parameters that a * flow already has triggers a notification is unspecified and subject to * change in future versions of Open vSwitch. * * OVS will always send the notifications for a given flow table change before * the reply to a OFPT_BARRIER_REQUEST request that follows the flow table * change. Thus, if the controller does not receive an abbreviated (or * unabbreviated) notification for a flow_mod before the next * OFPT_BARRIER_REPLY, it will never receive one. */ struct nx_flow_update_abbrev { ovs_be16 length; /* Length is 8. */ ovs_be16 event; /* NXFME_ABBREV. */ ovs_be32 xid; /* Controller-specified xid from flow_mod. */ }; OFP_ASSERT(sizeof(struct nx_flow_update_abbrev) == 8); /* NXT_FLOW_MONITOR_CANCEL. * * Used by a controller to cancel an outstanding monitor. */ struct nx_flow_monitor_cancel { ovs_be32 id; /* 'id' from nx_flow_monitor_request. */ }; OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4); /* Variable-length option TLV table maintenance commands. * * The option in Type-Length-Value format is widely used in tunnel options, * e.g., the base Geneve header is followed by zero or more options in TLV * format. Each option consists of a four byte option header and a variable * amount of option data interpreted according to the type. The generic TLV * format in tunnel options is as following: * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Option Class | Type |R|R|R| Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Variable Option Data | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * In order to work with this variable-length options in TLV format in * tunnel options, we need to maintain a mapping table between an option * TLV (defined by ) and an NXM field that can be * operated on for the purposes of matches, actions, etc. This mapping * must be explicitly specified by the user. * * There are two primary groups of OpenFlow messages that are introduced * as Nicira extensions: modification commands (add, delete, clear mappings) * and table status request/reply to dump the current table along with switch * information. * * Note that mappings should not be changed while they are in active use by * a flow. The result of doing so is undefined. */ /* TLV table commands */ enum nx_tlv_table_mod_command { NXTTMC_ADD, /* New mappings (fails if an option is already mapped). */ NXTTMC_DELETE, /* Delete mappings, identified by index * (unmapped options are ignored). */ NXTTMC_CLEAR, /* Clear all mappings. Additional information in this command is ignored. */ }; /* Map between an option TLV and an NXM field. */ struct nx_tlv_map { ovs_be16 option_class; /* TLV class. */ uint8_t option_type; /* TLV type. */ uint8_t option_len; /* TLV length (multiple of 4). */ ovs_be16 index; /* NXM_NX_TUN_METADATA index */ uint8_t pad[2]; }; OFP_ASSERT(sizeof(struct nx_tlv_map) == 8); /* NXT_TLV_TABLE_MOD. * * Use to configure a mapping between option TLVs (class, type, length) * and NXM fields (NXM_NX_TUN_METADATA where 'index' is ). * * This command is atomic: all operations on different options will * either succeed or fail. */ struct nx_tlv_table_mod { ovs_be16 command; /* One of NTTTMC_* */ uint8_t pad[6]; /* struct nx_tlv_map[0]; Array of maps between indicies and option TLVs. The number of elements is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct nx_tlv_table_mod) == 8); /* NXT_TLV_TABLE_REPLY. * * Issued in reponse to an NXT_TLV_TABLE_REQUEST to give information * about the current status of the TLV table in the switch. Provides * both static information about the switch's capabilities as well as * the configured TLV table. */ struct nx_tlv_table_reply { ovs_be32 max_option_space; /* Maximum total of option sizes supported. */ ovs_be16 max_fields; /* Maximum number of match fields supported. */ uint8_t reserved[10]; /* struct nx_tlv_map[0]; Array of maps between indicies and option TLVs. The number of elements is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct nx_tlv_table_reply) == 16); #endif /* openflow/nicira-ext.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.3.h0000644000000000000000000000013213534540071021421 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.577851922 openvswitch-2.5.9/include/openflow/openflow-1.3.h0000644000175000017500000004672413534540071023124 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford * Junior University * Copyright (c) 2011, 2012 Open Networking Foundation * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_13_H #define OPENFLOW_13_H 1 #include /* * OpenFlow 1.3 modifies the syntax of the following message types: * * OFPT_FEATURES_REPLY = 6 (opf13_switch_features) * - new field: auxiliary_id * - removed: ofp_ports at the end * * OFPT_PACKET_IN = 10 (ofp13_packet_in) new field: cookie * * OpenFlow 1.3 adds following new message types: * * * Asynchronous message configuration. * * OFPT13_GET_ASYNC_REQUEST = 26 (void) * OFPT13_GET_ASYNC_REPLY = 27 (ofp13_async_config) * OFPT13_SET_ASYNC = 28 (ofp13_async_config) * * * Meters and rate limiters configuration messages. * * OFPT13_METER_MOD = 29 (ofp13_meter_mod) * * OpenFlow 1.3 modifies the syntax of the following statistics message types * (now called multipart message types): * * OFPMP13_FLOW_REPLY = 1 (struct ofp13_flow_stats[]) * OFPMP13_TABLE_REPLY = 3 (struct ofp13_table_stats[]) * OFPMP13_PORT_REPLY = 4 (struct ofp13_port_stats[]) * OFPMP13_QUEUE_REPLY = 5, (struct ofp13_queue_stats[]) * OFPMP13_GROUP_REPLY = 6, (struct ofp13_group_stats[]) * * OpenFlow 1.3 adds the following multipart message types * * Meter statistics: * OFPMP13_METER_REQUEST = 9, (struct ofp13_meter_multipart_request) * OFPMP13_METER_REPLY = 9, (struct ofp13_meter_stats[]) * * Meter configuration: * OFPMP13_METER_CONFIG_REQUEST = 10, (struct ofp13_meter_multipart_request) * OFPMP13_METER_CONFIG_REPLY = 10, (struct ofp13_meter_config[]) * * Meter features: * OFPMP13_METER_FEATURES_REQUEST = 11 (void) * OFPMP13_METER_FEATURES_REPLY = 11 (struct ofp13_meter_features) * * Table features: * OFPMP13_TABLE_FEATURES_REQUEST = 12, (struct ofp13_table_features[]) * OFPMP13_TABLE_FEATURES_REPLY = 12, (struct ofp13_table_features[]) * */ enum ofp13_instruction_type { OFPIT13_METER = 6 /* Apply meter (rate limiter) */ }; /* Instruction structure for OFPIT_METER */ struct ofp13_instruction_meter { ovs_be16 type; /* OFPIT13_METER */ ovs_be16 len; /* Length is 8. */ ovs_be32 meter_id; /* Meter instance. */ }; OFP_ASSERT(sizeof(struct ofp13_instruction_meter) == 8); /* enum ofp_config_flags value OFPC_INVALID_TTL_TO_CONTROLLER * is deprecated in OpenFlow 1.3 */ /* Flags to configure the table. Reserved for future use. */ enum ofp13_table_config { OFPTC13_DEPRECATED_MASK = 3 /* Deprecated bits */ }; /* OpenFlow 1.3 specific flags for flow_mod messages. */ enum ofp13_flow_mod_flags { OFPFF13_NO_PKT_COUNTS = 1 << 3, /* Don't keep track of packet count. */ OFPFF13_NO_BYT_COUNTS = 1 << 4 /* Don't keep track of byte count. */ }; /* Common header for all meter bands */ struct ofp13_meter_band_header { ovs_be16 type; /* One of OFPMBT_*. */ ovs_be16 len; /* Length in bytes of this band. */ ovs_be32 rate; /* Rate for this band. */ ovs_be32 burst_size; /* Size of bursts. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_band_header) == 12); /* Meter configuration. OFPT_METER_MOD. */ struct ofp13_meter_mod { ovs_be16 command; /* One of OFPMC_*. */ ovs_be16 flags; /* Set of OFPMF_*. */ ovs_be32 meter_id; /* Meter instance. */ /* struct ofp13_meter_band_header bands[0]; The bands length is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_mod) == 8); /* Meter numbering. Flow meters can use any number up to OFPM_MAX. */ enum ofp13_meter { /* Last usable meter. */ OFPM13_MAX = 0xffff0000, /* Virtual meters. */ OFPM13_SLOWPATH = 0xfffffffd, /* Meter for slow datapath. */ OFPM13_CONTROLLER = 0xfffffffe, /* Meter for controller connection. */ OFPM13_ALL = 0xffffffff, /* Represents all meters for stat requests commands. */ }; /* Meter commands */ enum ofp13_meter_mod_command { OFPMC13_ADD, /* New meter. */ OFPMC13_MODIFY, /* Modify specified meter. */ OFPMC13_DELETE /* Delete specified meter. */ }; /* Meter configuration flags */ enum ofp13_meter_flags { OFPMF13_KBPS = 1 << 0, /* Rate value in kb/s (kilo-bit per second). */ OFPMF13_PKTPS = 1 << 1, /* Rate value in packet/sec. */ OFPMF13_BURST = 1 << 2, /* Do burst size. */ OFPMF13_STATS = 1 << 3 /* Collect statistics. */ }; /* Meter band types */ enum ofp13_meter_band_type { OFPMBT13_DROP = 1, /* Drop packet. */ OFPMBT13_DSCP_REMARK = 2, /* Remark DSCP in the IP header. */ OFPMBT13_EXPERIMENTER = 0xFFFF /* Experimenter meter band. */ }; /* OFPMBT_DROP band - drop packets */ struct ofp13_meter_band_drop { ovs_be16 type; /* OFPMBT_DROP. */ ovs_be16 len; /* Length in bytes of this band. */ ovs_be32 rate; /* Rate for dropping packets. */ ovs_be32 burst_size; /* Size of bursts. */ uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp13_meter_band_drop) == 16); /* OFPMBT_DSCP_REMARK band - Remark DSCP in the IP header */ struct ofp13_meter_band_dscp_remark { ovs_be16 type; /* OFPMBT_DSCP_REMARK. */ ovs_be16 len; /* Length in bytes of this band. */ ovs_be32 rate; /* Rate for remarking packets. */ ovs_be32 burst_size; /* Size of bursts. */ uint8_t prec_level; /* Number of drop precedence level to add. */ uint8_t pad[3]; }; OFP_ASSERT(sizeof(struct ofp13_meter_band_dscp_remark) == 16); /* OFPMBT_EXPERIMENTER band - Write actions in action set */ struct ofp13_meter_band_experimenter { ovs_be16 type; /* OFPMBT_EXPERIMENTER. */ ovs_be16 len; /* Length in bytes of this band. */ ovs_be32 rate; /* Rate for dropping packets. */ ovs_be32 burst_size; /* Size of bursts. */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_experimenter_header. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_band_experimenter) == 16); /* OF 1.3 adds MORE flag also for requests */ enum ofp13_multipart_request_flags { OFPMPF13_REQ_MORE = 1 << 0 /* More requests to follow. */ }; /* OF 1.3 splits table features off the ofp_table_stats */ /* Body of reply to OFPMP13_TABLE request. */ struct ofp13_table_stats { uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[3]; /* Align to 32-bits. */ ovs_be32 active_count; /* Number of active entries. */ ovs_be64 lookup_count; /* Number of packets looked up in table. */ ovs_be64 matched_count; /* Number of packets that hit table. */ }; OFP_ASSERT(sizeof(struct ofp13_table_stats) == 24); /* Common header for all Table Feature Properties */ struct ofp13_table_feature_prop_header { ovs_be16 type; /* One of OFPTFPT_*. */ ovs_be16 length; /* Length in bytes of this property. */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_header) == 4); /* Body for ofp_multipart_request of type OFPMP_TABLE_FEATURES./ * Body of reply to OFPMP_TABLE_FEATURES request. */ struct ofp13_table_features { ovs_be16 length; /* Length is padded to 64 bits. */ uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[5]; /* Align to 64-bits. */ char name[OFP_MAX_TABLE_NAME_LEN]; ovs_be64 metadata_match; /* Bits of metadata table can match. */ ovs_be64 metadata_write; /* Bits of metadata table can write. */ /* In OF1.3 this field was named 'config' and it was useless because OF1.3 * did not define any OFPTC_* bits. * * OF1.4 renamed this field to 'capabilities' and added OFPTC14_EVICTION * and OFPTC14_VACANCY_EVENTS. */ ovs_be32 capabilities; /* Bitmap of OFPTC_* values */ ovs_be32 max_entries; /* Max number of entries supported. */ /* Table Feature Property list */ /* struct ofp13_table_feature_prop_header properties[0]; */ }; OFP_ASSERT(sizeof(struct ofp13_table_features) == 64); /* Table Feature property types. * Low order bit cleared indicates a property for a regular Flow Entry. * Low order bit set indicates a property for the Table-Miss Flow Entry. */ enum ofp13_table_feature_prop_type { OFPTFPT13_INSTRUCTIONS = 0, /* Instructions property. */ OFPTFPT13_INSTRUCTIONS_MISS = 1, /* Instructions for table-miss. */ OFPTFPT13_NEXT_TABLES = 2, /* Next Table property. */ OFPTFPT13_NEXT_TABLES_MISS = 3, /* Next Table for table-miss. */ OFPTFPT13_WRITE_ACTIONS = 4, /* Write Actions property. */ OFPTFPT13_WRITE_ACTIONS_MISS = 5, /* Write Actions for table-miss. */ OFPTFPT13_APPLY_ACTIONS = 6, /* Apply Actions property. */ OFPTFPT13_APPLY_ACTIONS_MISS = 7, /* Apply Actions for table-miss. */ OFPTFPT13_MATCH = 8, /* Match property. */ OFPTFPT13_WILDCARDS = 10, /* Wildcards property. */ OFPTFPT13_WRITE_SETFIELD = 12, /* Write Set-Field property. */ OFPTFPT13_WRITE_SETFIELD_MISS = 13, /* Write Set-Field for table-miss. */ OFPTFPT13_APPLY_SETFIELD = 14, /* Apply Set-Field property. */ OFPTFPT13_APPLY_SETFIELD_MISS = 15, /* Apply Set-Field for table-miss. */ OFPTFPT13_EXPERIMENTER = 0xFFFE, /* Experimenter property. */ OFPTFPT13_EXPERIMENTER_MISS = 0xFFFF, /* Experimenter for table-miss. */ }; /* Instructions property */ struct ofp13_table_feature_prop_instructions { ovs_be16 type; /* One of OFPTFPT13_INSTRUCTIONS, OFPTFPT13_INSTRUCTIONS_MISS. */ ovs_be16 length; /* Length in bytes of this property. */ /* Followed by: * - Exactly (length - 4) bytes containing the instruction ids, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ /* struct ofp11_instruction instruction_ids[0]; List of instructions without any data */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_instructions) == 4); /* Next Tables property */ struct ofp13_table_feature_prop_next_tables { ovs_be16 type; /* One of OFPTFPT13_NEXT_TABLES, OFPTFPT13_NEXT_TABLES_MISS. */ ovs_be16 length; /* Length in bytes of this property. */ /* Followed by: * - Exactly (length - 4) bytes containing the table_ids, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ /* uint8_t next_table_ids[0]; */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_next_tables) == 4); /* Actions property */ struct ofp13_table_feature_prop_actions { ovs_be16 type; /* One of OFPTFPT13_WRITE_ACTIONS, OFPTFPT13_WRITE_ACTIONS_MISS, OFPTFPT13_APPLY_ACTIONS, OFPTFPT13_APPLY_ACTIONS_MISS. */ ovs_be16 length; /* Length in bytes of this property. */ /* Followed by: * - Exactly (length - 4) bytes containing the action_ids, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ /* struct ofp_action_header action_ids[0]; List of actions without any data */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_actions) == 4); /* Match, Wildcard or Set-Field property */ struct ofp13_table_feature_prop_oxm { ovs_be16 type; /* One of OFPTFPT13_MATCH, OFPTFPT13_WILDCARDS, OFPTFPT13_WRITE_SETFIELD, OFPTFPT13_WRITE_SETFIELD_MISS, OFPTFPT13_APPLY_SETFIELD, OFPTFPT13_APPLY_SETFIELD_MISS. */ ovs_be16 length; /* Length in bytes of this property. */ /* Followed by: * - Exactly (length - 4) bytes containing the oxm_ids, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ /* ovs_be32 oxm_ids[0]; Array of OXM headers */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_oxm) == 4); /* Experimenter table feature property */ struct ofp13_table_feature_prop_experimenter { ovs_be16 type; /* One of OFPTFPT13_EXPERIMENTER, OFPTFPT13_EXPERIMENTER_MISS. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_experimenter_header. */ ovs_be32 exp_type; /* Experimenter defined. */ /* Followed by: * - Exactly (length - 12) bytes containing the experimenter data, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ /* ovs_be32 experimenter_data[0]; */ }; OFP_ASSERT(sizeof(struct ofp13_table_feature_prop_experimenter) == 12); /* Body of reply to OFPMP13_PORT request. If a counter is unsupported, set * the field to all ones. */ struct ofp13_port_stats { struct ofp11_port_stats ps; ovs_be32 duration_sec; /* Time port has been alive in seconds. */ ovs_be32 duration_nsec; /* Time port has been alive in nanoseconds beyond duration_sec. */ }; OFP_ASSERT(sizeof(struct ofp13_port_stats) == 112); /* Body of reply to OFPMP13_QUEUE request */ struct ofp13_queue_stats { struct ofp11_queue_stats qs; ovs_be32 duration_sec; /* Time queue has been alive in seconds. */ ovs_be32 duration_nsec; /* Time queue has been alive in nanoseconds beyond duration_sec. */ }; OFP_ASSERT(sizeof(struct ofp13_queue_stats) == 40); /* Body of reply to OFPMP13_GROUP request */ struct ofp13_group_stats { struct ofp11_group_stats gs; ovs_be32 duration_sec; /* Time group has been alive in seconds. */ ovs_be32 duration_nsec; /* Time group has been alive in nanoseconds beyond duration_sec. */ /* struct ofp11_bucket_counter bucket_stats[]; */ }; OFP_ASSERT(sizeof(struct ofp13_group_stats) == 40); /* Body of OFPMP13_METER and OFPMP13_METER_CONFIG requests. */ struct ofp13_meter_multipart_request { ovs_be32 meter_id; /* Meter instance, or OFPM_ALL. */ uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_multipart_request) == 8); /* Statistics for each meter band */ struct ofp13_meter_band_stats { ovs_be64 packet_band_count; /* Number of packets in band. */ ovs_be64 byte_band_count; /* Number of bytes in band. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_band_stats) == 16); /* Body of reply to OFPMP13_METER request. Meter statistics. */ struct ofp13_meter_stats { ovs_be32 meter_id; /* Meter instance. */ ovs_be16 len; /* Length in bytes of this stats. */ uint8_t pad[6]; ovs_be32 flow_count; /* Number of flows bound to meter. */ ovs_be64 packet_in_count; /* Number of packets in input. */ ovs_be64 byte_in_count; /* Number of bytes in input. */ ovs_be32 duration_sec; /* Time meter has been alive in seconds. */ ovs_be32 duration_nsec; /* Time meter has been alive in nanoseconds beyond duration_sec. */ struct ofp13_meter_band_stats band_stats[0]; /* The band_stats length is inferred from the length field. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_stats) == 40); /* Body of reply to OFPMP13_METER_CONFIG request. Meter configuration. */ struct ofp13_meter_config { ovs_be16 length; /* Length of this entry. */ ovs_be16 flags; /* Set of OFPMC_* that apply. */ ovs_be32 meter_id; /* Meter instance. */ /* struct ofp13_meter_band_header bands[0]; The bands length is inferred from the length field. */ }; OFP_ASSERT(sizeof(struct ofp13_meter_config) == 8); /* Body of reply to OFPMP13_METER_FEATURES request. Meter features. */ struct ofp13_meter_features { ovs_be32 max_meter; /* Maximum number of meters. */ ovs_be32 band_types; /* Bitmaps of OFPMBT13_* values supported. */ ovs_be32 capabilities; /* Bitmaps of "ofp13_meter_flags". */ uint8_t max_bands; /* Maximum bands per meters */ uint8_t max_color; /* Maximum color value */ uint8_t pad[2]; }; OFP_ASSERT(sizeof(struct ofp13_meter_features) == 16); /* Asynchronous message configuration. */ /* The body of this is the same as nx_async_config */ /* OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC. */ struct ofp13_async_config { ovs_be32 packet_in_mask[2]; /* Bitmasks of OFPR_* values. */ ovs_be32 port_status_mask[2]; /* Bitmasks of OFPPR_* values. */ ovs_be32 flow_removed_mask[2];/* Bitmasks of OFPRR_* values. */ }; OFP_ASSERT(sizeof(struct ofp13_async_config) == 24); /* Packet received on port (datapath -> controller). */ struct ofp13_packet_in { struct ofp12_packet_in pi; ovs_be64 cookie; /* Cookie of the flow entry that was looked up */ /* Followed by: * - Match * - Exactly 2 all-zero padding bytes, then * - An Ethernet frame whose length is inferred from header.length. * The padding bytes preceding the Ethernet frame ensure that the IP * header (if any) following the Ethernet header is 32-bit aligned. */ /* struct ofp12_match match; */ /* uint8_t pad[2]; Align to 64 bit + 16 bit */ /* uint8_t data[0]; Ethernet frame */ }; OFP_ASSERT(sizeof(struct ofp13_packet_in) == 16); #endif /* openflow/openflow-1.3.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071021257 xustar0030 mtime=1567801401.293680376 30 atime=1567801402.065686047 30 ctime=1567801424.601852099 openvswitch-2.5.9/include/openflow/automake.mk0000644000175000017500000000143213534540071022745 0ustar00jpettitjpettit00000000000000openflowincludedir = $(includedir)/openflow openflowinclude_HEADERS = \ include/openflow/netronome-ext.h \ include/openflow/nicira-ext.h \ include/openflow/openflow-1.0.h \ include/openflow/openflow-1.1.h \ include/openflow/openflow-1.2.h \ include/openflow/openflow-1.3.h \ include/openflow/openflow-1.4.h \ include/openflow/openflow-1.5.h \ include/openflow/openflow-common.h \ include/openflow/openflow.h if HAVE_PYTHON SUFFIXES += .h .hstamp .h.hstamp: $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/check-structs -I$(srcdir)/include $< && \ touch $@ HSTAMP_FILES = $(openflowinclude_HEADERS:.h=.hstamp) CLEANFILES += $(HSTAMP_FILES) ALL_LOCAL += $(HSTAMP_FILES) $(HSTAMP_FILES): build-aux/check-structs $(openflowinclude_HEADERS) endif EXTRA_DIST += build-aux/check-structs openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.1.h0000644000000000000000000000013213534540071021417 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.577851922 openvswitch-2.5.9/include/openflow/openflow-1.1.h0000644000175000017500000007060513534540071023115 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2011, 2012, 2013, 2014 The Board of Trustees of The Leland Stanford * Junior University * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_11_H #define OPENFLOW_11_H 1 #include /* OpenFlow 1.1 uses 32-bit port numbers. Open vSwitch, for now, uses OpenFlow * 1.0 port numbers internally. We map them to OpenFlow 1.0 as follows: * * OF1.1 <=> OF1.0 * ----------------------- --------------- * 0x00000000...0x0000feff <=> 0x0000...0xfeff "physical" ports * 0x0000ff00...0xfffffeff <=> not supported * 0xffffff00...0xffffffff <=> 0xff00...0xffff "reserved" OFPP_* ports * * OFPP11_OFFSET is the value that must be added or subtracted to convert * an OpenFlow 1.0 reserved port number to or from, respectively, the * corresponding OpenFlow 1.1 reserved port number. */ #define OFPP11_MAX OFP11_PORT_C(0xffffff00) #define OFPP11_OFFSET 0xffff0000 /* OFPP11_MAX - OFPP_MAX */ /* Reserved wildcard port used only for flow mod (delete) and flow stats * requests. Selects all flows regardless of output port * (including flows with no output port) * * Define it via OFPP_NONE (0xFFFF) so that OFPP_ANY is still an enum ofp_port */ #define OFPP_ANY OFPP_NONE /* OpenFlow 1.1 port config flags are just the common flags. */ #define OFPPC11_ALL \ (OFPPC_PORT_DOWN | OFPPC_NO_RECV | OFPPC_NO_FWD | OFPPC_NO_PACKET_IN) /* OpenFlow 1.1 specific current state of the physical port. These are not * configurable from the controller. */ enum ofp11_port_state { OFPPS11_BLOCKED = 1 << 1, /* Port is blocked */ OFPPS11_LIVE = 1 << 2, /* Live for Fast Failover Group. */ #define OFPPS11_ALL (OFPPS_LINK_DOWN | OFPPS11_BLOCKED | OFPPS11_LIVE) }; /* OpenFlow 1.1 specific features of ports available in a datapath. */ enum ofp11_port_features { OFPPF11_40GB_FD = 1 << 7, /* 40 Gb full-duplex rate support. */ OFPPF11_100GB_FD = 1 << 8, /* 100 Gb full-duplex rate support. */ OFPPF11_1TB_FD = 1 << 9, /* 1 Tb full-duplex rate support. */ OFPPF11_OTHER = 1 << 10, /* Other rate, not in the list. */ OFPPF11_COPPER = 1 << 11, /* Copper medium. */ OFPPF11_FIBER = 1 << 12, /* Fiber medium. */ OFPPF11_AUTONEG = 1 << 13, /* Auto-negotiation. */ OFPPF11_PAUSE = 1 << 14, /* Pause. */ OFPPF11_PAUSE_ASYM = 1 << 15 /* Asymmetric pause. */ #define OFPPF11_ALL ((1 << 16) - 1) }; /* Description of a port */ struct ofp11_port { ovs_be32 port_no; uint8_t pad[4]; struct eth_addr hw_addr; uint8_t pad2[2]; /* Align to 64 bits. */ char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */ ovs_be32 config; /* Bitmap of OFPPC_* flags. */ ovs_be32 state; /* Bitmap of OFPPS_* and OFPPS11_* flags. */ /* Bitmaps of OFPPF_* and OFPPF11_* that describe features. All bits * zeroed if unsupported or unavailable. */ ovs_be32 curr; /* Current features. */ ovs_be32 advertised; /* Features being advertised by the port. */ ovs_be32 supported; /* Features supported by the port. */ ovs_be32 peer; /* Features advertised by peer. */ ovs_be32 curr_speed; /* Current port bitrate in kbps. */ ovs_be32 max_speed; /* Max port bitrate in kbps */ }; OFP_ASSERT(sizeof(struct ofp11_port) == 64); /* Modify behavior of the physical port */ struct ofp11_port_mod { ovs_be32 port_no; uint8_t pad[4]; struct eth_addr hw_addr; /* The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp11_port struct. */ uint8_t pad2[2]; /* Pad to 64 bits. */ ovs_be32 config; /* Bitmap of OFPPC_* flags. */ ovs_be32 mask; /* Bitmap of OFPPC_* flags to be changed. */ ovs_be32 advertise; /* Bitmap of OFPPF_* and OFPPF11_*. Zero all bits to prevent any action taking place. */ uint8_t pad3[4]; /* Pad to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp11_port_mod) == 32); /* Group setup and teardown (controller -> datapath). */ struct ofp11_group_mod { ovs_be16 command; /* One of OFPGC11_*. */ uint8_t type; /* One of OFPGT11_*. */ uint8_t pad; /* Pad to 64 bits. */ ovs_be32 group_id; /* Group identifier. */ /* struct ofp11_bucket buckets[0]; The bucket length is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct ofp11_group_mod) == 8); /* Query for port queue configuration. */ struct ofp11_queue_get_config_request { ovs_be32 port; /* Port to be queried. Should refer to a valid physical port (i.e. < OFPP_MAX) */ uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp11_queue_get_config_request) == 8); /* Group commands */ enum ofp11_group_mod_command { OFPGC11_ADD, /* New group. */ OFPGC11_MODIFY, /* Modify all matching groups. */ OFPGC11_DELETE, /* Delete all matching groups. */ }; /* OpenFlow 1.1 specific capabilities supported by the datapath (struct * ofp_switch_features, member capabilities). */ enum ofp11_capabilities { OFPC11_GROUP_STATS = 1 << 3, /* Group statistics. */ }; #define OFPMT11_STANDARD_LENGTH 88 struct ofp11_match_header { ovs_be16 type; /* One of OFPMT_* */ ovs_be16 length; /* Length of match */ }; OFP_ASSERT(sizeof(struct ofp11_match_header) == 4); /* Fields to match against flows */ struct ofp11_match { struct ofp11_match_header omh; ovs_be32 in_port; /* Input switch port. */ ovs_be32 wildcards; /* Wildcard fields. */ struct eth_addr dl_src; /* Ethernet source address. */ struct eth_addr dl_src_mask; /* Ethernet source address mask. */ struct eth_addr dl_dst; /* Ethernet destination address. */ struct eth_addr dl_dst_mask; /* Ethernet destination address mask. */ ovs_be16 dl_vlan; /* Input VLAN id. */ uint8_t dl_vlan_pcp; /* Input VLAN priority. */ uint8_t pad1[1]; /* Align to 32-bits */ ovs_be16 dl_type; /* Ethernet frame type. */ uint8_t nw_tos; /* IP ToS (actually DSCP field, 6 bits). */ uint8_t nw_proto; /* IP protocol or lower 8 bits of ARP opcode. */ ovs_be32 nw_src; /* IP source address. */ ovs_be32 nw_src_mask; /* IP source address mask. */ ovs_be32 nw_dst; /* IP destination address. */ ovs_be32 nw_dst_mask; /* IP destination address mask. */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */ ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port. */ ovs_be32 mpls_label; /* MPLS label. */ uint8_t mpls_tc; /* MPLS TC. */ uint8_t pad2[3]; /* Align to 64-bits */ ovs_be64 metadata; /* Metadata passed between tables. */ ovs_be64 metadata_mask; /* Mask for metadata. */ }; OFP_ASSERT(sizeof(struct ofp11_match) == OFPMT11_STANDARD_LENGTH); /* Flow wildcards. */ enum ofp11_flow_wildcards { OFPFW11_IN_PORT = 1 << 0, /* Switch input port. */ OFPFW11_DL_VLAN = 1 << 1, /* VLAN id. */ OFPFW11_DL_VLAN_PCP = 1 << 2, /* VLAN priority. */ OFPFW11_DL_TYPE = 1 << 3, /* Ethernet frame type. */ OFPFW11_NW_TOS = 1 << 4, /* IP ToS (DSCP field, 6 bits). */ OFPFW11_NW_PROTO = 1 << 5, /* IP protocol. */ OFPFW11_TP_SRC = 1 << 6, /* TCP/UDP/SCTP source port. */ OFPFW11_TP_DST = 1 << 7, /* TCP/UDP/SCTP destination port. */ OFPFW11_MPLS_LABEL = 1 << 8, /* MPLS label. */ OFPFW11_MPLS_TC = 1 << 9, /* MPLS TC. */ /* Wildcard all fields. */ OFPFW11_ALL = ((1 << 10) - 1) }; /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate * special conditions. */ enum ofp11_vlan_id { OFPVID11_ANY = 0xfffe, /* Indicate that a VLAN id is set but don't care about it's value. Note: only valid when specifying the VLAN id in a match */ OFPVID11_NONE = 0xffff, /* No VLAN id was set. */ }; enum ofp11_instruction_type { OFPIT11_GOTO_TABLE = 1, /* Setup the next table in the lookup pipeline */ OFPIT11_WRITE_METADATA = 2, /* Setup the metadata field for use later in pipeline */ OFPIT11_WRITE_ACTIONS = 3, /* Write the action(s) onto the datapath action set */ OFPIT11_APPLY_ACTIONS = 4, /* Applies the action(s) immediately */ OFPIT11_CLEAR_ACTIONS = 5, /* Clears all actions from the datapath action set */ OFPIT11_EXPERIMENTER = 0xFFFF /* Experimenter instruction */ }; #define OFP11_INSTRUCTION_ALIGN 8 /* Generic ofp_instruction structure. */ struct ofp11_instruction { ovs_be16 type; /* Instruction type */ ovs_be16 len; /* Length of this struct in bytes. */ uint8_t pad[4]; /* Align to 64-bits */ }; OFP_ASSERT(sizeof(struct ofp11_instruction) == 8); /* Instruction structure for OFPIT_GOTO_TABLE */ struct ofp11_instruction_goto_table { ovs_be16 type; /* OFPIT_GOTO_TABLE */ ovs_be16 len; /* Length of this struct in bytes. */ uint8_t table_id; /* Set next table in the lookup pipeline */ uint8_t pad[3]; /* Pad to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp11_instruction_goto_table) == 8); /* Instruction structure for OFPIT_WRITE_METADATA */ struct ofp11_instruction_write_metadata { ovs_be16 type; /* OFPIT_WRITE_METADATA */ ovs_be16 len; /* Length of this struct in bytes. */ uint8_t pad[4]; /* Align to 64-bits */ ovs_be64 metadata; /* Metadata value to write */ ovs_be64 metadata_mask; /* Metadata write bitmask */ }; OFP_ASSERT(sizeof(struct ofp11_instruction_write_metadata) == 24); /* Instruction structure for OFPIT_WRITE/APPLY/CLEAR_ACTIONS */ struct ofp11_instruction_actions { ovs_be16 type; /* One of OFPIT_*_ACTIONS */ ovs_be16 len; /* Length of this struct in bytes. */ uint8_t pad[4]; /* Align to 64-bits */ /* struct ofp_action_header actions[0]; Actions associated with OFPIT_WRITE_ACTIONS and OFPIT_APPLY_ACTIONS */ }; OFP_ASSERT(sizeof(struct ofp11_instruction_actions) == 8); /* Instruction structure for experimental instructions */ struct ofp11_instruction_experimenter { ovs_be16 type; /* OFPIT11_EXPERIMENTER */ ovs_be16 len; /* Length of this struct in bytes */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_vendor_header. */ /* Experimenter-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp11_instruction_experimenter) == 8); /* Configure/Modify behavior of a flow table */ struct ofp11_table_mod { uint8_t table_id; /* ID of the table, 0xFF indicates all tables */ uint8_t pad[3]; /* Pad to 32 bits */ ovs_be32 config; /* Bitmap of OFPTC_* flags */ }; OFP_ASSERT(sizeof(struct ofp11_table_mod) == 8); /* Flow setup and teardown (controller -> datapath). */ struct ofp11_flow_mod { ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be64 cookie_mask; /* Mask used to restrict the cookie bits that must match when the command is OFPFC_MODIFY* or OFPFC_DELETE*. A value of 0 indicates no restriction. */ /* Flow actions. */ uint8_t table_id; /* ID of the table to put the flow in */ uint8_t command; /* One of OFPFC_*. */ ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ ovs_be16 priority; /* Priority level of flow entry. */ ovs_be32 buffer_id; /* Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. */ ovs_be32 out_port; /* For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_ANY indicates no restriction. */ ovs_be32 out_group; /* For OFPFC_DELETE* commands, require matching entries to include this as an output group. A value of OFPG_ANY indicates no restriction. */ ovs_be16 flags; /* One of OFPFF_*. */ ovs_be16 importance; /* Eviction precedence (OF1.4+). */ /* Followed by an ofp11_match structure. */ /* Followed by an instruction set. */ }; OFP_ASSERT(sizeof(struct ofp11_flow_mod) == 40); /* Group types. Values in the range [128, 255] are reserved for experimental * use. */ enum ofp11_group_type { OFPGT11_ALL, /* All (multicast/broadcast) group. */ OFPGT11_SELECT, /* Select group. */ OFPGT11_INDIRECT, /* Indirect group. */ OFPGT11_FF /* Fast failover group. */ }; /* Bucket for use in groups. */ struct ofp11_bucket { ovs_be16 len; /* Length the bucket in bytes, including this header and any padding to make it 64-bit aligned. */ ovs_be16 weight; /* Relative weight of bucket. Only defined for select groups. */ ovs_be32 watch_port; /* Port whose state affects whether this bucket is live. Only required for fast failover groups. */ ovs_be32 watch_group; /* Group whose state affects whether this bucket is live. Only required for fast failover groups. */ uint8_t pad[4]; /* struct ofp_action_header actions[0]; The action length is inferred from the length field in the header. */ }; OFP_ASSERT(sizeof(struct ofp11_bucket) == 16); /* Queue configuration for a given port. */ struct ofp11_queue_get_config_reply { ovs_be32 port; uint8_t pad[4]; /* struct ofp_packet_queue queues[0]; List of configured queues. */ }; OFP_ASSERT(sizeof(struct ofp11_queue_get_config_reply) == 8); struct ofp11_stats_msg { struct ofp_header header; ovs_be16 type; /* One of the OFPST_* constants. */ ovs_be16 flags; /* OFPSF_REQ_* flags (none yet defined). */ uint8_t pad[4]; /* Followed by the body of the request. */ }; OFP_ASSERT(sizeof(struct ofp11_stats_msg) == 16); /* Vendor extension stats message. */ struct ofp11_vendor_stats_msg { struct ofp11_stats_msg osm; /* Type OFPST_VENDOR. */ ovs_be32 vendor; /* Vendor ID: * - MSB 0: low-order bytes are IEEE OUI. * - MSB != 0: defined by OpenFlow * consortium. */ /* Followed by vendor-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp11_vendor_stats_msg) == 20); /* Stats request of type OFPST_FLOW. */ struct ofp11_flow_stats_request { uint8_t table_id; /* ID of table to read (from ofp_table_stats), 0xff for all tables. */ uint8_t pad[3]; /* Align to 64 bits. */ ovs_be32 out_port; /* Require matching entries to include this as an output port. A value of OFPP_ANY indicates no restriction. */ ovs_be32 out_group; /* Require matching entries to include this as an output group. A value of OFPG_ANY indicates no restriction. */ uint8_t pad2[4]; /* Align to 64 bits. */ ovs_be64 cookie; /* Require matching entries to contain this cookie value */ ovs_be64 cookie_mask; /* Mask used to restrict the cookie bits that must match. A value of 0 indicates no restriction. */ /* Followed by an ofp11_match structure. */ }; OFP_ASSERT(sizeof(struct ofp11_flow_stats_request) == 32); /* Body of reply to OFPST_FLOW request. */ struct ofp11_flow_stats { ovs_be16 length; /* Length of this entry. */ uint8_t table_id; /* ID of table flow came from. */ uint8_t pad; ovs_be32 duration_sec; /* Time flow has been alive in seconds. */ ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds beyond duration_sec. */ ovs_be16 priority; /* Priority of the entry. Only meaningful when this is not an exact-match entry. */ ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */ ovs_be16 hard_timeout; /* Number of seconds before expiration. */ ovs_be16 flags; /* OF 1.3: Set of OFPFF*. */ ovs_be16 importance; /* Eviction precedence (OF1.4+). */ uint8_t pad2[2]; /* Align to 64-bits. */ ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be64 packet_count; /* Number of packets in flow. */ ovs_be64 byte_count; /* Number of bytes in flow. */ /* OpenFlow version specific match */ /* struct ofp11_instruction instructions[0]; Instruction set. */ }; OFP_ASSERT(sizeof(struct ofp11_flow_stats) == 48); /* Body for ofp_stats_request of type OFPST_AGGREGATE. */ /* Identical to ofp11_flow_stats_request */ /* Flow match fields. */ enum ofp11_flow_match_fields { OFPFMF11_IN_PORT = 1 << 0, /* Switch input port. */ OFPFMF11_DL_VLAN = 1 << 1, /* VLAN id. */ OFPFMF11_DL_VLAN_PCP = 1 << 2, /* VLAN priority. */ OFPFMF11_DL_TYPE = 1 << 3, /* Ethernet frame type. */ OFPFMF11_NW_TOS = 1 << 4, /* IP ToS (DSCP field, 6 bits). */ OFPFMF11_NW_PROTO = 1 << 5, /* IP protocol. */ OFPFMF11_TP_SRC = 1 << 6, /* TCP/UDP/SCTP source port. */ OFPFMF11_TP_DST = 1 << 7, /* TCP/UDP/SCTP destination port. */ OFPFMF11_MPLS_LABEL = 1 << 8, /* MPLS label. */ OFPFMF11_MPLS_TC = 1 << 9, /* MPLS TC. */ OFPFMF11_TYPE = 1 << 10, /* Match type. */ OFPFMF11_DL_SRC = 1 << 11, /* Ethernet source address. */ OFPFMF11_DL_DST = 1 << 12, /* Ethernet destination address. */ OFPFMF11_NW_SRC = 1 << 13, /* IP source address. */ OFPFMF11_NW_DST = 1 << 14, /* IP destination address. */ OFPFMF11_METADATA = 1 << 15, /* Metadata passed between tables. */ }; /* Body of reply to OFPST_TABLE request. */ struct ofp11_table_stats { uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[7]; /* Align to 64-bits. */ char name[OFP_MAX_TABLE_NAME_LEN]; ovs_be32 wildcards; /* Bitmap of OFPFMF_* wildcards that are supported by the table. */ ovs_be32 match; /* Bitmap of OFPFMF_* that indicate the fields the table can match on. */ ovs_be32 instructions; /* Bitmap of OFPIT_* values supported. */ ovs_be32 write_actions; /* Bitmap of OFPAT_* that are supported by the table with OFPIT_WRITE_ACTIONS. */ ovs_be32 apply_actions; /* Bitmap of OFPAT_* that are supported by the table with OFPIT_APPLY_ACTIONS. */ ovs_be32 config; /* Bitmap of OFPTC_* values */ ovs_be32 max_entries; /* Max number of entries supported. */ ovs_be32 active_count; /* Number of active entries. */ ovs_be64 lookup_count; /* Number of packets looked up in table. */ ovs_be64 matched_count; /* Number of packets that hit table. */ }; OFP_ASSERT(sizeof(struct ofp11_table_stats) == 88); /* Body for ofp_stats_request of type OFPST_PORT. */ struct ofp11_port_stats_request { ovs_be32 port_no; /* OFPST_PORT message must request statistics * either for a single port (specified in * port_no) or for all ports (if port_no == * OFPP_ANY). */ uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp11_port_stats_request) == 8); /* Body of reply to OFPST_PORT request. If a counter is unsupported, set * the field to all ones. */ struct ofp11_port_stats { ovs_be32 port_no; uint8_t pad[4]; /* Align to 64-bits. */ ovs_be64 rx_packets; /* Number of received packets. */ ovs_be64 tx_packets; /* Number of transmitted packets. */ ovs_be64 rx_bytes; /* Number of received bytes. */ ovs_be64 tx_bytes; /* Number of transmitted bytes. */ ovs_be64 rx_dropped; /* Number of packets dropped by RX. */ ovs_be64 tx_dropped; /* Number of packets dropped by TX. */ ovs_be64 rx_errors; /* Number of receive errors. This is a super-set of receive errors and should be great than or equal to the sum of all rx_*_err values. */ ovs_be64 tx_errors; /* Number of transmit errors. This is a super-set of transmit errors. */ ovs_be64 rx_frame_err; /* Number of frame alignment errors. */ ovs_be64 rx_over_err; /* Number of packets with RX overrun. */ ovs_be64 rx_crc_err; /* Number of CRC errors. */ ovs_be64 collisions; /* Number of collisions. */ }; OFP_ASSERT(sizeof(struct ofp11_port_stats) == 104); struct ofp11_queue_stats_request { ovs_be32 port_no; /* All ports if OFPP_ANY. */ ovs_be32 queue_id; /* All queues if OFPQ_ALL. */ }; OFP_ASSERT(sizeof(struct ofp11_queue_stats_request) == 8); struct ofp11_queue_stats { ovs_be32 port_no; ovs_be32 queue_id; /* Queue id. */ ovs_be64 tx_bytes; /* Number of transmitted bytes. */ ovs_be64 tx_packets; /* Number of transmitted packets. */ ovs_be64 tx_errors; /* # of packets dropped due to overrun. */ }; OFP_ASSERT(sizeof(struct ofp11_queue_stats) == 32); struct ofp11_group_stats_request { ovs_be32 group_id; /* All groups if OFPG_ALL. */ uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp11_group_stats_request) == 8); /* Used in group stats replies. */ struct ofp11_bucket_counter { ovs_be64 packet_count; /* Number of packets processed by bucket. */ ovs_be64 byte_count; /* Number of bytes processed by bucket. */ }; OFP_ASSERT(sizeof(struct ofp11_bucket_counter) == 16); /* Body of reply to OFPST11_GROUP request */ struct ofp11_group_stats { ovs_be16 length; /* Length of this entry. */ uint8_t pad[2]; /* Align to 64 bits. */ ovs_be32 group_id; /* Group identifier. */ ovs_be32 ref_count; /* Number of flows or groups that directly forward to this group. */ uint8_t pad2[4]; /* Align to 64 bits. */ ovs_be64 packet_count; /* Number of packets processed by group. */ ovs_be64 byte_count; /* Number of bytes processed by group. */ /* struct ofp11_bucket_counter bucket_stats[]; */ }; OFP_ASSERT(sizeof(struct ofp11_group_stats) == 32); /* Body of reply to OFPST11_GROUP_DESC request. */ struct ofp11_group_desc_stats { ovs_be16 length; /* Length of this entry. */ uint8_t type; /* One of OFPGT11_*. */ uint8_t pad; /* Pad to 64 bits. */ ovs_be32 group_id; /* Group identifier. */ /* struct ofp11_bucket buckets[0]; */ }; OFP_ASSERT(sizeof(struct ofp11_group_desc_stats) == 8); /* Send packet (controller -> datapath). */ struct ofp11_packet_out { ovs_be32 buffer_id; /* ID assigned by datapath (-1 if none). */ ovs_be32 in_port; /* Packet's input port or OFPP_CONTROLLER. */ ovs_be16 actions_len; /* Size of action array in bytes. */ uint8_t pad[6]; /* struct ofp_action_header actions[0]; Action list. */ /* uint8_t data[0]; */ /* Packet data. The length is inferred from the length field in the header. (Only meaningful if buffer_id == -1.) */ }; OFP_ASSERT(sizeof(struct ofp11_packet_out) == 16); /* Packet received on port (datapath -> controller). */ struct ofp11_packet_in { ovs_be32 buffer_id; /* ID assigned by datapath. */ ovs_be32 in_port; /* Port on which frame was received. */ ovs_be32 in_phy_port; /* Physical Port on which frame was received. */ ovs_be16 total_len; /* Full length of frame. */ uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t table_id; /* ID of the table that was looked up */ /* Followed by Ethernet frame. */ }; OFP_ASSERT(sizeof(struct ofp11_packet_in) == 16); /* Flow removed (datapath -> controller). */ struct ofp11_flow_removed { ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be16 priority; /* Priority level of flow entry. */ uint8_t reason; /* One of OFPRR_*. */ uint8_t table_id; /* ID of the table */ ovs_be32 duration_sec; /* Time flow was alive in seconds. */ ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond duration_sec. */ ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */ uint8_t pad2[2]; /* Align to 64-bits. */ ovs_be64 packet_count; ovs_be64 byte_count; /* Followed by an ofp11_match structure. */ }; OFP_ASSERT(sizeof(struct ofp11_flow_removed) == 40); #endif /* openflow/openflow-1.1.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.2.h0000644000000000000000000000013213534540071021420 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.577851922 openvswitch-2.5.9/include/openflow/openflow-1.2.h0000644000175000017500000002536113534540071023115 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2011, 2012, 2013, 2014 The Board of Trustees of The Leland Stanford * Junior University * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * Copyright (c) 2012 Horms Solutions Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_12_H #define OPENFLOW_12_H 1 #include /* Error type for experimenter error messages. */ #define OFPET12_EXPERIMENTER 0xffff /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate * special conditions. */ enum ofp12_vlan_id { OFPVID12_PRESENT = 0x1000, /* Bit that indicate that a VLAN id is set */ OFPVID12_NONE = 0x0000, /* No VLAN id was set. */ }; /* Bit definitions for IPv6 Extension Header pseudo-field. */ enum ofp12_ipv6exthdr_flags { OFPIEH12_NONEXT = 1 << 0, /* "No next header" encountered. */ OFPIEH12_ESP = 1 << 1, /* Encrypted Sec Payload header present. */ OFPIEH12_AUTH = 1 << 2, /* Authentication header present. */ OFPIEH12_DEST = 1 << 3, /* 1 or 2 dest headers present. */ OFPIEH12_FRAG = 1 << 4, /* Fragment header present. */ OFPIEH12_ROUTER = 1 << 5, /* Router header present. */ OFPIEH12_HOP = 1 << 6, /* Hop-by-hop header present. */ OFPIEH12_UNREP = 1 << 7, /* Unexpected repeats encountered. */ OFPIEH12_UNSEQ = 1 << 8 /* Unexpected sequencing encountered. */ }; /* Header for OXM experimenter match fields. */ struct ofp12_oxm_experimenter_header { ovs_be32 oxm_header; /* oxm_class = OFPXMC_EXPERIMENTER */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp11_experimenter_header. */ }; OFP_ASSERT(sizeof(struct ofp12_oxm_experimenter_header) == 8); enum ofp12_controller_max_len { OFPCML12_MAX = 0xffe5, /* maximum max_len value which can be used * to request a specific byte length. */ OFPCML12_NO_BUFFER = 0xffff /* indicates that no buffering should be * applied and the whole packet is to be * sent to the controller. */ }; /* OpenFlow 1.2 specific flags * (struct ofp12_flow_mod, member flags). */ enum ofp12_flow_mod_flags { OFPFF12_RESET_COUNTS = 1 << 2 /* Reset flow packet and byte counts. */ }; /* OpenFlow 1.2 specific capabilities * (struct ofp_switch_features, member capabilities). */ enum ofp12_capabilities { OFPC12_PORT_BLOCKED = 1 << 8 /* Switch will block looping ports. */ }; /* Full description for a queue. */ struct ofp12_packet_queue { ovs_be32 queue_id; /* id for the specific queue. */ ovs_be32 port; /* Port this queue is attached to. */ ovs_be16 len; /* Length in bytes of this queue desc. */ uint8_t pad[6]; /* 64-bit alignment. */ /* Followed by any number of queue properties expressed using * ofp_queue_prop_header, to fill out a total of 'len' bytes. */ }; OFP_ASSERT(sizeof(struct ofp12_packet_queue) == 16); /* Body of reply to OFPST_TABLE request. */ struct ofp12_table_stats { uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[7]; /* Align to 64-bits. */ char name[OFP_MAX_TABLE_NAME_LEN]; ovs_be64 match; /* Bitmap of (1 << OFPXMT_*) that indicate the fields the table can match on. */ ovs_be64 wildcards; /* Bitmap of (1 << OFPXMT_*) wildcards that are supported by the table. */ ovs_be32 write_actions; /* Bitmap of OFPAT_* that are supported by the table with OFPIT_WRITE_ACTIONS. */ ovs_be32 apply_actions; /* Bitmap of OFPAT_* that are supported by the table with OFPIT_APPLY_ACTIONS. */ ovs_be64 write_setfields;/* Bitmap of (1 << OFPXMT_*) header fields that can be set with OFPIT_WRITE_ACTIONS. */ ovs_be64 apply_setfields;/* Bitmap of (1 << OFPXMT_*) header fields that can be set with OFPIT_APPLY_ACTIONS. */ ovs_be64 metadata_match; /* Bits of metadata table can match. */ ovs_be64 metadata_write; /* Bits of metadata table can write. */ ovs_be32 instructions; /* Bitmap of OFPIT_* values supported. */ ovs_be32 config; /* Bitmap of OFPTC_* values */ ovs_be32 max_entries; /* Max number of entries supported. */ ovs_be32 active_count; /* Number of active entries. */ ovs_be64 lookup_count; /* Number of packets looked up in table. */ ovs_be64 matched_count; /* Number of packets that hit table. */ }; OFP_ASSERT(sizeof(struct ofp12_table_stats) == 128); /* Number of types of groups supported by ofp12_group_features_stats. */ #define OFPGT12_N_TYPES 4 /* Body of reply to OFPST12_GROUP_FEATURES request. Group features. */ struct ofp12_group_features_stats { ovs_be32 types; /* Bitmap of OFPGT11_* values supported. */ ovs_be32 capabilities; /* Bitmap of OFPGFC12_* capability supported. */ /* Each element in the following arrays corresponds to the group type with * the same number, e.g. max_groups[0] is the maximum number of OFPGT11_ALL * groups, actions[2] is the actions supported by OFPGT11_INDIRECT * groups. */ ovs_be32 max_groups[OFPGT12_N_TYPES]; /* Max number of groups. */ ovs_be32 actions[OFPGT12_N_TYPES]; /* Bitmaps of supported OFPAT_*. */ }; OFP_ASSERT(sizeof(struct ofp12_group_features_stats) == 40); /* Group configuration flags */ enum ofp12_group_capabilities { OFPGFC12_SELECT_WEIGHT = 1 << 0, /* Support weight for select groups */ OFPGFC12_SELECT_LIVENESS = 1 << 1, /* Support liveness for select groups */ OFPGFC12_CHAINING = 1 << 2, /* Support chaining groups */ OFPGFC12_CHAINING_CHECKS = 1 << 3, /* Check chaining for loops and delete */ }; /* Body for ofp12_stats_request/reply of type OFPST_EXPERIMENTER. */ struct ofp12_experimenter_stats_header { ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_experimenter_header. */ ovs_be32 exp_type; /* Experimenter defined. */ /* Experimenter-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp12_experimenter_stats_header) == 8); /* Role request and reply message. */ struct ofp12_role_request { ovs_be32 role; /* One of OFPCR12_ROLE_*. */ uint8_t pad[4]; /* Align to 64 bits. */ ovs_be64 generation_id; /* Master Election Generation Id */ }; OFP_ASSERT(sizeof(struct ofp12_role_request) == 16); /* Controller roles. */ enum ofp12_controller_role { OFPCR12_ROLE_NOCHANGE, /* Don't change current role. */ OFPCR12_ROLE_EQUAL, /* Default role, full access. */ OFPCR12_ROLE_MASTER, /* Full access, at most one master. */ OFPCR12_ROLE_SLAVE, /* Read-only access. */ }; /* Packet received on port (datapath -> controller). */ struct ofp12_packet_in { ovs_be32 buffer_id; /* ID assigned by datapath. */ ovs_be16 total_len; /* Full length of frame. */ uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t table_id; /* ID of the table that was looked up */ /* Followed by: * - Match * - Exactly 2 all-zero padding bytes, then * - An Ethernet frame whose length is inferred from header.length. * The padding bytes preceding the Ethernet frame ensure that the IP * header (if any) following the Ethernet header is 32-bit aligned. */ /* struct ofp12_match match; */ /* uint8_t pad[2]; Align to 64 bit + 16 bit */ /* uint8_t data[0]; Ethernet frame */ }; OFP_ASSERT(sizeof(struct ofp12_packet_in) == 8); /* Flow removed (datapath -> controller). */ struct ofp12_flow_removed { ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be16 priority; /* Priority level of flow entry. */ uint8_t reason; /* One of OFPRR_*. */ uint8_t table_id; /* ID of the table */ ovs_be32 duration_sec; /* Time flow was alive in seconds. */ ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond duration_sec. */ ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */ ovs_be16 hard_timeout; /* Hard timeout from original flow mod. */ ovs_be64 packet_count; ovs_be64 byte_count; /* struct ofp12_match match; Description of fields. Variable size. */ }; OFP_ASSERT(sizeof(struct ofp12_flow_removed) == 40); #endif /* openflow/openflow-1.2.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.4.h0000644000000000000000000000013213534540071021422 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.577851922 openvswitch-2.5.9/include/openflow/openflow-1.4.h0000644000175000017500000004540613534540071023121 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2014 The Board of Trustees of The Leland Stanford * Junior University * Copyright (c) 2011, 2012 Open Networking Foundation * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_14_H #define OPENFLOW_14_H 1 #include /* OpenFlow 1.4.1+ specific capabilities * (struct ofp_switch_features, member capabilities). */ enum ofp14_capabilities { OFPC14_BUNDLES = 1 << 9, /* Switch supports bundles. */ OFPC14_FLOW_MONITORING = 1 << 10, /* Switch supports flow monitoring. */ }; /* ## ---------- ## */ /* ## ofp14_port ## */ /* ## ---------- ## */ /* Port description property types. */ enum ofp_port_desc_prop_type { OFPPDPT14_ETHERNET = 0, /* Ethernet property. */ OFPPDPT14_OPTICAL = 1, /* Optical property. */ OFPPDPT14_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ }; /* Ethernet port description property. */ struct ofp14_port_desc_prop_ethernet { ovs_be16 type; /* OFPPDPT14_ETHERNET. */ ovs_be16 length; /* Length in bytes of this property. */ uint8_t pad[4]; /* Align to 64 bits. */ /* Bitmaps of OFPPF_* that describe features. All bits zeroed if * unsupported or unavailable. */ ovs_be32 curr; /* Current features. */ ovs_be32 advertised; /* Features being advertised by the port. */ ovs_be32 supported; /* Features supported by the port. */ ovs_be32 peer; /* Features advertised by peer. */ ovs_be32 curr_speed; /* Current port bitrate in kbps. */ ovs_be32 max_speed; /* Max port bitrate in kbps */ }; OFP_ASSERT(sizeof(struct ofp14_port_desc_prop_ethernet) == 32); struct ofp14_port { ovs_be32 port_no; ovs_be16 length; uint8_t pad[2]; struct eth_addr hw_addr; uint8_t pad2[2]; /* Align to 64 bits. */ char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */ ovs_be32 config; /* Bitmap of OFPPC_* flags. */ ovs_be32 state; /* Bitmap of OFPPS_* flags. */ /* Followed by 0 or more OFPPDPT14_* properties. */ }; OFP_ASSERT(sizeof(struct ofp14_port) == 40); /* ## -------------- ## */ /* ## ofp14_port_mod ## */ /* ## -------------- ## */ enum ofp14_port_mod_prop_type { OFPPMPT14_ETHERNET = 0, /* Ethernet property. */ OFPPMPT14_OPTICAL = 1, /* Optical property. */ OFPPMPT14_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ }; /* Ethernet port mod property. */ struct ofp14_port_mod_prop_ethernet { ovs_be16 type; /* OFPPMPT14_ETHERNET. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 advertise; /* Bitmap of OFPPF_*. Zero all bits to prevent any action taking place. */ }; OFP_ASSERT(sizeof(struct ofp14_port_mod_prop_ethernet) == 8); struct ofp14_port_mod { ovs_be32 port_no; uint8_t pad[4]; struct eth_addr hw_addr; uint8_t pad2[2]; ovs_be32 config; /* Bitmap of OFPPC_* flags. */ ovs_be32 mask; /* Bitmap of OFPPC_* flags to be changed. */ /* Followed by 0 or more OFPPMPT14_* properties. */ }; OFP_ASSERT(sizeof(struct ofp14_port_mod) == 24); /* ## --------------- ## */ /* ## ofp14_table_mod ## */ /* ## --------------- ## */ enum ofp14_table_mod_prop_type { OFPTMPT14_EVICTION = 0x2, /* Eviction property. */ OFPTMPT14_VACANCY = 0x3, /* Vacancy property. */ OFPTMPT14_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ }; enum ofp14_table_mod_prop_eviction_flag { OFPTMPEF14_OTHER = 1 << 0, /* Using other factors. */ OFPTMPEF14_IMPORTANCE = 1 << 1, /* Using flow entry importance. */ OFPTMPEF14_LIFETIME = 1 << 2, /* Using flow entry lifetime. */ }; /* What changed about the table */ enum ofp14_table_reason { OFPTR_VACANCY_DOWN = 3, /* Vacancy down threshold event. */ OFPTR_VACANCY_UP = 4, /* Vacancy up threshold event. */ OFPTR_N_REASONS /* Denotes number of reasons. */ }; struct ofp14_table_mod_prop_eviction { ovs_be16 type; /* OFPTMPT14_EVICTION. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 flags; /* Bitmap of OFPTMPEF14_* flags */ }; OFP_ASSERT(sizeof(struct ofp14_table_mod_prop_eviction) == 8); struct ofp14_table_mod_prop_vacancy { ovs_be16 type; /* OFPTMPT14_VACANCY. */ ovs_be16 length; /* Length in bytes of this property. */ uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ uint8_t vacancy; /* Current vacancy (%) - only in ofp14_table_desc. */ uint8_t pad[1]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp14_table_mod_prop_vacancy) == 8); struct ofp14_table_mod { uint8_t table_id; /* ID of the table, OFPTT_ALL indicates all tables */ uint8_t pad[3]; /* Pad to 32 bits */ ovs_be32 config; /* Bitmap of OFPTC_* flags */ /* Followed by 0 or more OFPTMPT14_* properties. */ }; OFP_ASSERT(sizeof(struct ofp14_table_mod) == 8); /* Body of reply to OFPMP_TABLE_DESC request. */ struct ofp14_table_desc { ovs_be16 length; /* Length is padded to 64 bits. */ uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[1]; /* Align to 32-bits. */ ovs_be32 config; /* Bitmap of OFPTC_* values. */ /* Followed by 0 or more OFPTMPT14_* properties. */ }; OFP_ASSERT(sizeof(struct ofp14_table_desc) == 8); /* ## ---------------- ## */ /* ## ofp14_port_stats ## */ /* ## ---------------- ## */ enum ofp14_port_stats_prop_type { OFPPSPT14_ETHERNET = 0, /* Ethernet property. */ OFPPSPT14_OPTICAL = 1, /* Optical property. */ OFPPSPT14_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ }; struct ofp14_port_stats_prop_ethernet { ovs_be16 type; /* OFPPSPT14_ETHERNET. */ ovs_be16 length; /* Length in bytes of this property. */ uint8_t pad[4]; /* Align to 64 bits. */ ovs_be64 rx_frame_err; /* Number of frame alignment errors. */ ovs_be64 rx_over_err; /* Number of packets with RX overrun. */ ovs_be64 rx_crc_err; /* Number of CRC errors. */ ovs_be64 collisions; /* Number of collisions. */ }; OFP_ASSERT(sizeof(struct ofp14_port_stats_prop_ethernet) == 40); struct ofp14_port_stats { ovs_be16 length; /* Length of this entry. */ uint8_t pad[2]; /* Align to 64 bits. */ ovs_be32 port_no; ovs_be32 duration_sec; /* Time port has been alive in seconds. */ ovs_be32 duration_nsec; /* Time port has been alive in nanoseconds beyond duration_sec. */ ovs_be64 rx_packets; /* Number of received packets. */ ovs_be64 tx_packets; /* Number of transmitted packets. */ ovs_be64 rx_bytes; /* Number of received bytes. */ ovs_be64 tx_bytes; /* Number of transmitted bytes. */ ovs_be64 rx_dropped; /* Number of packets dropped by RX. */ ovs_be64 tx_dropped; /* Number of packets dropped by TX. */ ovs_be64 rx_errors; /* Number of receive errors. This is a super-set of more specific receive errors and should be greater than or equal to the sum of all rx_*_err values in properties. */ ovs_be64 tx_errors; /* Number of transmit errors. This is a super-set of more specific transmit errors and should be greater than or equal to the sum of all tx_*_err values (none currently defined.) */ /* Followed by 0 or more OFPPSPT14_* properties. */ }; OFP_ASSERT(sizeof(struct ofp14_port_stats) == 80); /* ## ----------------- ## */ /* ## ofp14_queue_stats ## */ /* ## ----------------- ## */ struct ofp14_queue_stats { ovs_be16 length; /* Length of this entry. */ uint8_t pad[6]; /* Align to 64 bits. */ struct ofp13_queue_stats qs; /* Followed by 0 or more properties (none yet defined). */ }; OFP_ASSERT(sizeof(struct ofp14_queue_stats) == 48); /* ## -------------- ## */ /* ## Miscellaneous. ## */ /* ## -------------- ## */ /* Common header for all async config Properties */ struct ofp14_async_config_prop_header { ovs_be16 type; /* One of OFPACPT_*. */ ovs_be16 length; /* Length in bytes of this property. */ }; OFP_ASSERT(sizeof(struct ofp14_async_config_prop_header) == 4); /* Asynchronous message configuration. * OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC. */ struct ofp14_async_config { struct ofp_header header; /* Async config Property list - 0 or more */ struct ofp14_async_config_prop_header properties[0]; }; OFP_ASSERT(sizeof(struct ofp14_async_config) == 8); /* Request forward reason */ enum ofp14_requestforward_reason { OFPRFR_GROUP_MOD = 0, /* Forward group mod requests. */ OFPRFR_METER_MOD = 1, /* Forward meter mod requests. */ OFPRFR_N_REASONS /* Denotes number of reasons. */ }; /* Async Config property types. * Low order bit cleared indicates a property for the slave role. * Low order bit set indicates a property for the master/equal role. */ enum ofp14_async_config_prop_type { OFPACPT_PACKET_IN_SLAVE = 0, /* Packet-in mask for slave. */ OFPACPT_PACKET_IN_MASTER = 1, /* Packet-in mask for master. */ OFPACPT_PORT_STATUS_SLAVE = 2, /* Port-status mask for slave. */ OFPACPT_PORT_STATUS_MASTER = 3, /* Port-status mask for master. */ OFPACPT_FLOW_REMOVED_SLAVE = 4, /* Flow removed mask for slave. */ OFPACPT_FLOW_REMOVED_MASTER = 5, /* Flow removed mask for master. */ OFPACPT_ROLE_STATUS_SLAVE = 6, /* Role status mask for slave. */ OFPACPT_ROLE_STATUS_MASTER = 7, /* Role status mask for master. */ OFPACPT_TABLE_STATUS_SLAVE = 8, /* Table status mask for slave. */ OFPACPT_TABLE_STATUS_MASTER = 9, /* Table status mask for master. */ OFPACPT_REQUESTFORWARD_SLAVE = 10, /* RequestForward mask for slave. */ OFPACPT_REQUESTFORWARD_MASTER = 11, /* RequestForward mask for master. */ OFPTFPT_EXPERIMENTER_SLAVE = 0xFFFE, /* Experimenter for slave. */ OFPTFPT_EXPERIMENTER_MASTER = 0xFFFF, /* Experimenter for master. */ }; /* Various reason based properties */ struct ofp14_async_config_prop_reasons { /* 'type' is one of OFPACPT_PACKET_IN_*, OFPACPT_PORT_STATUS_*, * OFPACPT_FLOW_REMOVED_*, OFPACPT_ROLE_STATUS_*, * OFPACPT_TABLE_STATUS_*, OFPACPT_REQUESTFORWARD_*. */ ovs_be16 type; ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 mask; /* Bitmasks of reason values. */ }; OFP_ASSERT(sizeof(struct ofp14_async_config_prop_reasons) == 8); /* Experimenter async config property */ struct ofp14_async_config_prop_experimenter { ovs_be16 type; /* One of OFPTFPT_EXPERIMENTER_SLAVE, OFPTFPT_EXPERIMENTER_MASTER. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_experimenter_header. */ ovs_be32 exp_type; /* Experimenter defined. */ /* Followed by: * - Exactly (length - 12) bytes containing the experimenter data, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ }; OFP_ASSERT(sizeof(struct ofp14_async_config_prop_experimenter) == 12); /* Common header for all Role Properties */ struct ofp14_role_prop_header { ovs_be16 type; /* One of OFPRPT_*. */ ovs_be16 length; /* Length in bytes of this property. */ }; OFP_ASSERT(sizeof(struct ofp14_role_prop_header) == 4); /* Role status event message. */ struct ofp14_role_status { ovs_be32 role; /* One of OFPCR_ROLE_*. */ uint8_t reason; /* One of OFPCRR_*. */ uint8_t pad[3]; /* Align to 64 bits. */ ovs_be64 generation_id; /* Master Election Generation Id */ /* Followed by a list of struct ofp14_role_prop_header */ }; OFP_ASSERT(sizeof(struct ofp14_role_status) == 16); /* What changed about the controller role */ enum ofp14_controller_role_reason { OFPCRR_MASTER_REQUEST = 0, /* Another controller asked to be master. */ OFPCRR_CONFIG = 1, /* Configuration changed on the switch. */ OFPCRR_EXPERIMENTER = 2, /* Experimenter data changed. */ OFPCRR_N_REASONS /* Denotes number of reasons. */ }; /* Role property types. */ enum ofp14_role_prop_type { OFPRPT_EXPERIMENTER = 0xFFFF, /* Experimenter property. */ }; /* Experimenter role property */ struct ofp14_role_prop_experimenter { ovs_be16 type; /* One of OFPRPT_EXPERIMENTER. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as in struct ofp_experimenter_header. */ ovs_be32 exp_type; /* Experimenter defined. */ /* Followed by: * - Exactly (length - 12) bytes containing the experimenter data, then * - Exactly (length + 7)/8*8 - (length) (between 0 and 7) * bytes of all-zero bytes */ }; OFP_ASSERT(sizeof(struct ofp14_role_prop_experimenter) == 12); /* Group/Meter request forwarding. */ struct ofp14_requestforward { struct ofp_header request; /* Request being forwarded. */ }; OFP_ASSERT(sizeof(struct ofp14_requestforward) == 8); /* Bundle control message types */ enum ofp14_bundle_ctrl_type { OFPBCT_OPEN_REQUEST = 0, OFPBCT_OPEN_REPLY = 1, OFPBCT_CLOSE_REQUEST = 2, OFPBCT_CLOSE_REPLY = 3, OFPBCT_COMMIT_REQUEST = 4, OFPBCT_COMMIT_REPLY = 5, OFPBCT_DISCARD_REQUEST = 6, OFPBCT_DISCARD_REPLY = 7, }; /* Bundle configuration flags. */ enum ofp14_bundle_flags { OFPBF_ATOMIC = 1 << 0, /* Execute atomically. */ OFPBF_ORDERED = 1 << 1, /* Execute in specified order. */ }; /* Message structure for OFPT_BUNDLE_CONTROL and OFPT_BUNDLE_ADD_MESSAGE. */ struct ofp14_bundle_ctrl_msg { ovs_be32 bundle_id; /* Identify the bundle. */ ovs_be16 type; /* OFPT_BUNDLE_CONTROL: one of OFPBCT_*. * OFPT_BUNDLE_ADD_MESSAGE: not used. */ ovs_be16 flags; /* Bitmap of OFPBF_* flags. */ /* Followed by: * - For OFPT_BUNDLE_ADD_MESSAGE only, an encapsulated OpenFlow message, * beginning with an ofp_header whose xid is identical to this message's * outer xid. * - For OFPT_BUNDLE_ADD_MESSAGE only, and only if at least one property is * present, 0 to 7 bytes of padding to align on a 64-bit boundary. * - Zero or more properties (see struct ofp14_bundle_prop_header). */ }; OFP_ASSERT(sizeof(struct ofp14_bundle_ctrl_msg) == 8); /* Body for ofp14_multipart_request of type OFPMP_FLOW_MONITOR. * * The OFPMP_FLOW_MONITOR request's body consists of an array of zero or more * instances of this structure. The request arranges to monitor the flows * that match the specified criteria, which are interpreted in the same way as * for OFPMP_FLOW. * * 'id' identifies a particular monitor for the purpose of allowing it to be * canceled later with OFPFMC_DELETE. 'id' must be unique among * existing monitors that have not already been canceled. */ struct ofp14_flow_monitor_request { ovs_be32 monitor_id; /* Controller-assigned ID for this monitor. */ ovs_be32 out_port; /* Required output port, if not OFPP_ANY. */ ovs_be32 out_group; /* Required output port, if not OFPG_ANY. */ ovs_be16 flags; /* OFPMF14_*. */ uint8_t table_id; /* One table's ID or OFPTT_ALL (all tables). */ uint8_t command; /* One of OFPFMC14_*. */ /* Followed by an ofp11_match structure. */ }; OFP_ASSERT(sizeof(struct ofp14_flow_monitor_request) == 16); /* Flow monitor commands */ enum ofp14_flow_monitor_command { OFPFMC14_ADD = 0, /* New flow monitor. */ OFPFMC14_MODIFY = 1, /* Modify existing flow monitor. */ OFPFMC14_DELETE = 2, /* Delete/cancel existing flow monitor. */ }; /* 'flags' bits in struct of_flow_monitor_request. */ enum ofp14_flow_monitor_flags { /* When to send updates. */ /* Common to NX and OpenFlow 1.4 */ OFPFMF14_INITIAL = 1 << 0, /* Initially matching flows. */ OFPFMF14_ADD = 1 << 1, /* New matching flows as they are added. */ OFPFMF14_REMOVED = 1 << 2, /* Old matching flows as they are removed. */ OFPFMF14_MODIFY = 1 << 3, /* Matching flows as they are changed. */ /* What to include in updates. */ /* Common to NX and OpenFlow 1.4 */ OFPFMF14_INSTRUCTIONS = 1 << 4, /* If set, instructions are included. */ OFPFMF14_NO_ABBREV = 1 << 5, /* If set, include own changes in full. */ /* OpenFlow 1.4 */ OFPFMF14_ONLY_OWN = 1 << 6, /* If set, don't include other controllers. */ }; #endif /* openflow/openflow-1.4.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.5.h0000644000000000000000000000013213534540071021423 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.581851951 openvswitch-2.5.9/include/openflow/openflow-1.5.h0000644000175000017500000001726213534540071023121 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2014 The Board of Trustees of The Leland Stanford * Junior University * Copyright (c) 2011, 2014 Open Networking Foundation * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_15_H #define OPENFLOW_15_H 1 #include /* Body for ofp15_multipart_request of type OFPMP_PORT_DESC. */ struct ofp15_port_desc_request { ovs_be32 port_no; /* All ports if OFPP_ANY. */ uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp15_port_desc_request) == 8); /* Group commands */ enum ofp15_group_mod_command { /* Present since OpenFlow 1.1 - 1.4 */ OFPGC15_ADD = 0, /* New group. */ OFPGC15_MODIFY = 1, /* Modify all matching groups. */ OFPGC15_DELETE = 2, /* Delete all matching groups. */ /* New in OpenFlow 1.5 */ OFPGC15_INSERT_BUCKET = 3,/* Insert action buckets to the already available list of action buckets in a matching group */ /* OFPGCXX_YYY = 4, */ /* Reserved for future use. */ OFPGC15_REMOVE_BUCKET = 5,/* Remove all action buckets or any specific action bucket from matching group */ }; /* Group bucket property types. */ enum ofp15_group_bucket_prop_type { OFPGBPT15_WEIGHT = 0, /* Select groups only. */ OFPGBPT15_WATCH_PORT = 1, /* Fast failover groups only. */ OFPGBPT15_WATCH_GROUP = 2, /* Fast failover groups only. */ OFPGBPT15_EXPERIMENTER = 0xFFFF, /* Experimenter defined. */ }; /* Group bucket weight property, for select groups only. */ struct ofp15_group_bucket_prop_weight { ovs_be16 type; /* OFPGBPT15_WEIGHT. */ ovs_be16 length; /* 8. */ ovs_be16 weight; /* Relative weight of bucket. */ uint8_t pad[2]; /* Pad to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp15_group_bucket_prop_weight) == 8); /* Group bucket watch port or watch group property, for fast failover groups * only. */ struct ofp15_group_bucket_prop_watch { ovs_be16 type; /* OFPGBPT15_WATCH_PORT or OFPGBPT15_WATCH_GROUP. */ ovs_be16 length; /* 8. */ ovs_be32 watch; /* The port or the group. */ }; OFP_ASSERT(sizeof(struct ofp15_group_bucket_prop_watch) == 8); /* Bucket for use in groups. */ struct ofp15_bucket { ovs_be16 len; /* Length the bucket in bytes, including this header and any padding to make it 64-bit aligned. */ ovs_be16 action_array_len; /* Length of all actions in bytes. */ ovs_be32 bucket_id; /* Bucket Id used to identify bucket*/ /* Followed by exactly len - 8 bytes of group bucket properties. */ /* Followed by: * - Exactly 'action_array_len' bytes containing an array of * struct ofp_action_*. * - Zero or more bytes of group bucket properties to fill out the * overall length in header.length. */ }; OFP_ASSERT(sizeof(struct ofp15_bucket) == 8); /* Bucket Id can be any value between 0 and OFPG_BUCKET_MAX */ enum ofp15_group_bucket { OFPG15_BUCKET_MAX = 0xffffff00, /* Last usable bucket ID */ OFPG15_BUCKET_FIRST = 0xfffffffd, /* First bucket ID in the list of action buckets of a group. This is applicable for OFPGC15_INSERT_BUCKET and OFPGC15_REMOVE_BUCKET commands */ OFPG15_BUCKET_LAST = 0xfffffffe, /* Last bucket ID in the list of action buckets of a group. This is applicable for OFPGC15_INSERT_BUCKET and OFPGC15_REMOVE_BUCKET commands */ OFPG15_BUCKET_ALL = 0xffffffff /* All action buckets in a group, This is applicable for only OFPGC15_REMOVE_BUCKET command */ }; /* Group property types. */ enum ofp_group_prop_type { OFPGPT15_EXPERIMENTER = 0xFFFF, /* Experimenter defined. */ }; /* Group setup and teardown (controller -> datapath). */ struct ofp15_group_mod { ovs_be16 command; /* One of OFPGC15_*. */ uint8_t type; /* One of OFPGT11_*. */ uint8_t pad; /* Pad to 64 bits. */ ovs_be32 group_id; /* Group identifier. */ ovs_be16 bucket_array_len; /* Length of action buckets data. */ uint8_t pad1[2]; /* Pad to 64 bits. */ ovs_be32 command_bucket_id; /* Bucket Id used as part of * OFPGC15_INSERT_BUCKET and * OFPGC15_REMOVE_BUCKET commands * execution.*/ /* Followed by: * - Exactly 'bucket_array_len' bytes containing an array of * struct ofp15_bucket. * - Zero or more bytes of group properties to fill out the overall * length in header.length. */ }; OFP_ASSERT(sizeof(struct ofp15_group_mod) == 16); /* Body for ofp15_multipart_request of type OFPMP_GROUP_DESC. */ struct ofp15_group_desc_request { ovs_be32 group_id; /* All groups if OFPG_ALL. */ uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp15_group_desc_request) == 8); /* Body of reply to OFPMP_GROUP_DESC request. */ struct ofp15_group_desc_stats { ovs_be16 length; /* Length of this entry. */ uint8_t type; /* One of OFPGT11_*. */ uint8_t pad; /* Pad to 64 bits. */ ovs_be32 group_id; /* Group identifier. */ ovs_be16 bucket_list_len; /* Length of action buckets data. */ uint8_t pad2[6]; /* Pad to 64 bits. */ /* Followed by: * - Exactly 'bucket_list_len' bytes containing an array of * struct ofp_bucket. * - Zero or more bytes of group properties to fill out the overall * length in header.length. */ }; OFP_ASSERT(sizeof(struct ofp15_group_desc_stats) == 16); #endif /* openflow/openflow-1.5.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow.h0000644000000000000000000000013213534540071021122 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.581851951 openvswitch-2.5.9/include/openflow/openflow.h0000644000175000017500000000164113534540071022612 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENFLOW_OPENFLOW_H #define OPENFLOW_OPENFLOW_H 1 #include #include #include #include #include #include #endif /* openflow/openflow.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-common.h0000644000000000000000000000013213534540071022410 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.581851951 openvswitch-2.5.9/include/openflow/openflow-common.h0000644000175000017500000004155213534540071024105 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2011, 2012, 2013, 2014 The Board of Trustees of The Leland Stanford * Junior University * * We are making the OpenFlow specification and associated documentation * (Software) available for public use and benefit with the expectation * that others will use, modify and enhance the Software and contribute * those enhancements back to the community. However, since we would * like to make the Software available for broadest use, with as few * restrictions as possible permission is hereby granted, free of * charge, to any person obtaining a copy of this Software to deal in * the Software under the copyrights without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * The name and trademarks of copyright holder(s) may NOT be used in * advertising or publicity pertaining to the Software or any * derivatives without specific, written prior permission. */ /* * Copyright (c) 2008-2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENFLOW_COMMON_H #define OPENFLOW_COMMON_H 1 #include #ifdef SWIG #define OFP_ASSERT(EXPR) /* SWIG can't handle OFP_ASSERT. */ #elif !defined(__cplusplus) /* Build-time assertion for use in a declaration context. */ #define OFP_ASSERT(EXPR) \ extern int (*build_assert(void))[ sizeof(struct { \ unsigned int build_assert_failed : (EXPR) ? 1 : -1; })] #else /* __cplusplus */ #include #define OFP_ASSERT BOOST_STATIC_ASSERT #endif /* __cplusplus */ /* Version number: * Non-experimental versions released: 0x01 0x02 * Experimental versions released: 0x81 -- 0x99 */ /* The most significant bit being set in the version field indicates an * experimental OpenFlow version. */ enum ofp_version { OFP10_VERSION = 0x01, OFP11_VERSION = 0x02, OFP12_VERSION = 0x03, OFP13_VERSION = 0x04, OFP14_VERSION = 0x05, OFP15_VERSION = 0x06 }; /* Vendor (aka experimenter) IDs. * * These are used in various places in OpenFlow to identify an extension * defined by some vendor, as opposed to a standardized part of the core * OpenFlow protocol. * * Vendor IDs whose top 8 bits are 0 hold an Ethernet OUI in their low 24 bits. * The Open Networking Foundation assigns vendor IDs whose top 8 bits are * nonzero. * * A few vendor IDs are special: * * - OF_VENDOR_ID is not a real vendor ID and does not appear in the * OpenFlow protocol itself. It can occasionally be useful within Open * vSwitch to identify a standardized part of OpenFlow. * * - ONF_VENDOR_ID is being used within the ONF "extensibility" working * group to identify extensions being proposed for standardization. * * The list is sorted numerically. */ #define OF_VENDOR_ID 0 #define HPL_VENDOR_ID 0x000004EA /* HP Labs. */ #define NTR_VENDOR_ID 0x0000154d /* Netronome. */ #define NTR_COMPAT_VENDOR_ID 0x00001540 /* Incorrect value used in v2.4. */ #define NX_VENDOR_ID 0x00002320 /* Nicira. */ #define ONF_VENDOR_ID 0x4f4e4600 /* Open Networking Foundation. */ #define OFP_MAX_TABLE_NAME_LEN 32 #define OFP_MAX_PORT_NAME_LEN 16 #define OFP_OLD_PORT 6633 #define OFP_PORT 6653 #define OFP_DEFAULT_MISS_SEND_LEN 128 /* Values below this cutoff are 802.3 packets and the two bytes * following MAC addresses are used as a frame length. Otherwise, the * two bytes are used as the Ethernet type. */ #define OFP_DL_TYPE_ETH2_CUTOFF 0x0600 /* Value of dl_type to indicate that the frame does not include an * Ethernet type. */ #define OFP_DL_TYPE_NOT_ETH_TYPE 0x05ff /* Value used in "idle_timeout" and "hard_timeout" to indicate that the entry * is permanent. */ #define OFP_FLOW_PERMANENT 0 /* By default, choose a priority in the middle. */ #define OFP_DEFAULT_PRIORITY 0x8000 /* Header on all OpenFlow packets. */ struct ofp_header { uint8_t version; /* An OpenFlow version number, e.g. OFP10_VERSION. */ uint8_t type; /* One of the OFPT_ constants. */ ovs_be16 length; /* Length including this ofp_header. */ ovs_be32 xid; /* Transaction id associated with this packet. Replies use the same id as was in the request to facilitate pairing. */ }; OFP_ASSERT(sizeof(struct ofp_header) == 8); /* OFPT_ERROR: Error message (datapath -> controller). */ struct ofp_error_msg { ovs_be16 type; ovs_be16 code; uint8_t data[0]; /* Variable-length data. Interpreted based on the type and code. */ }; OFP_ASSERT(sizeof(struct ofp_error_msg) == 4); enum ofp_config_flags { /* Handling of IP fragments. */ OFPC_FRAG_NORMAL = 0, /* No special handling for fragments. */ OFPC_FRAG_DROP = 1, /* Drop fragments. */ OFPC_FRAG_REASM = 2, /* Reassemble (only if OFPC_IP_REASM set). */ OFPC_FRAG_NX_MATCH = 3, /* Make first fragments available for matching. */ OFPC_FRAG_MASK = 3, /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OpenFlow 1.3 */ /* TTL processing - applicable for IP and MPLS packets. */ OFPC_INVALID_TTL_TO_CONTROLLER = 1 << 2, /* Send packets with invalid TTL to the controller. */ }; /* Switch configuration. */ struct ofp_switch_config { ovs_be16 flags; /* OFPC_* flags. */ ovs_be16 miss_send_len; /* Max bytes of new flow that datapath should send to the controller. */ }; OFP_ASSERT(sizeof(struct ofp_switch_config) == 4); /* Common flags to indicate behavior of the physical port. These flags are * used in ofp_port to describe the current configuration. They are used in * the ofp_port_mod message to configure the port's behavior. */ enum ofp_port_config { OFPPC_PORT_DOWN = 1 << 0, /* Port is administratively down. */ OFPPC_NO_RECV = 1 << 2, /* Drop all packets received by port. */ OFPPC_NO_FWD = 1 << 5, /* Drop packets forwarded to port. */ OFPPC_NO_PACKET_IN = 1 << 6 /* Do not send packet-in msgs for port. */ }; /* Common current state of the physical port. These are not configurable from * the controller. */ enum ofp_port_state { OFPPS_LINK_DOWN = 1 << 0, /* No physical link present. */ }; /* Common features of physical ports available in a datapath. */ enum ofp_port_features { OFPPF_10MB_HD = 1 << 0, /* 10 Mb half-duplex rate support. */ OFPPF_10MB_FD = 1 << 1, /* 10 Mb full-duplex rate support. */ OFPPF_100MB_HD = 1 << 2, /* 100 Mb half-duplex rate support. */ OFPPF_100MB_FD = 1 << 3, /* 100 Mb full-duplex rate support. */ OFPPF_1GB_HD = 1 << 4, /* 1 Gb half-duplex rate support. */ OFPPF_1GB_FD = 1 << 5, /* 1 Gb full-duplex rate support. */ OFPPF_10GB_FD = 1 << 6, /* 10 Gb full-duplex rate support. */ }; enum ofp_queue_properties { OFPQT_MIN_RATE = 1, /* Minimum datarate guaranteed. */ OFPQT_MAX_RATE = 2, /* Maximum guaranteed rate. */ OFPQT_EXPERIMENTER = 0xffff, /* Experimenter defined property. */ }; /* Common description for a queue. */ struct ofp_queue_prop_header { ovs_be16 property; /* One of OFPQT_. */ ovs_be16 len; /* Length of property, including this header. */ uint8_t pad[4]; /* 64-bit alignemnt. */ }; OFP_ASSERT(sizeof(struct ofp_queue_prop_header) == 8); /* Min-Rate and Max-Rate queue property description (OFPQT_MIN and * OFPQT_MAX). */ struct ofp_queue_prop_rate { struct ofp_queue_prop_header prop_header; ovs_be16 rate; /* In 1/10 of a percent; >1000 -> disabled. */ uint8_t pad[6]; /* 64-bit alignment */ }; OFP_ASSERT(sizeof(struct ofp_queue_prop_rate) == 16); /* Switch features. */ struct ofp_switch_features { ovs_be64 datapath_id; /* Datapath unique ID. The lower 48-bits are for a MAC address, while the upper 16-bits are implementer-defined. */ ovs_be32 n_buffers; /* Max packets buffered at once. */ uint8_t n_tables; /* Number of tables supported by datapath. */ uint8_t auxiliary_id; /* OF 1.3: Identify auxiliary connections */ uint8_t pad[2]; /* Align to 64-bits. */ /* Features. */ ovs_be32 capabilities; /* OFPC_*, OFPC10_*, OFPC11_*, OFPC12_*. */ ovs_be32 actions; /* Bitmap of supported "ofp_action_type"s. * DEPRECATED in OpenFlow 1.1 */ /* Followed by an array of struct ofp10_phy_port or struct ofp11_port * structures. The number is inferred from header.length. * REMOVED in OpenFlow 1.3 */ }; OFP_ASSERT(sizeof(struct ofp_switch_features) == 24); /* Common capabilities supported by the datapath (struct ofp_switch_features, * member capabilities). */ enum ofp_capabilities { OFPC_FLOW_STATS = 1 << 0, /* Flow statistics. */ OFPC_TABLE_STATS = 1 << 1, /* Table statistics. */ OFPC_PORT_STATS = 1 << 2, /* Port statistics. */ OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */ OFPC_QUEUE_STATS = 1 << 6, /* Queue statistics. */ OFPC_ARP_MATCH_IP = 1 << 7 /* Match IP addresses in ARP pkts. */ }; /* Why is this packet being sent to the controller? */ enum ofp_packet_in_reason { OFPR_NO_MATCH, /* No matching flow. */ OFPR_ACTION, /* Action explicitly output to controller. */ OFPR_INVALID_TTL, /* Packet has invalid TTL. */ OFPR_ACTION_SET, /* Output to controller in action set */ OFPR_GROUP, /* Output to controller in group bucket */ OFPR_PACKET_OUT, /* Output to controller in packet-out */ OFPR_N_REASONS }; enum ofp_flow_mod_command { OFPFC_ADD, /* New flow. */ OFPFC_MODIFY, /* Modify all matching flows. */ OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */ OFPFC_DELETE, /* Delete all matching flows. */ OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */ }; enum ofp_flow_mod_flags { OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow * expires or is deleted. */ OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */ }; /* Why was this flow removed? */ enum ofp_flow_removed_reason { OFPRR_IDLE_TIMEOUT, /* Flow idle time exceeded idle_timeout. */ OFPRR_HARD_TIMEOUT, /* Time exceeded hard_timeout. */ OFPRR_DELETE, /* Evicted by a DELETE flow mod. */ OFPRR_GROUP_DELETE, /* Group was removed. */ OFPRR_METER_DELETE, /* Meter was removed. */ OFPRR_EVICTION, /* Switch eviction to free resources. */ OVS_OFPRR_NONE /* OVS internal_use only, keep last!. */ }; /* What changed about the physical port */ enum ofp_port_reason { OFPPR_ADD, /* The port was added. */ OFPPR_DELETE, /* The port was removed. */ OFPPR_MODIFY, /* Some attribute of the port has changed. */ OFPPR_N_REASONS /* Denotes number of reasons. */ }; /* A physical port has changed in the datapath */ struct ofp_port_status { uint8_t reason; /* One of OFPPR_*. */ uint8_t pad[7]; /* Align to 64-bits. */ /* Followed by struct ofp10_phy_port, struct ofp11_port, or struct * ofp14_port. */ }; OFP_ASSERT(sizeof(struct ofp_port_status) == 8); enum ofp_stats_reply_flags { OFPSF_REPLY_MORE = 1 << 0 /* More replies to follow. */ }; #define DESC_STR_LEN 256 #define SERIAL_NUM_LEN 32 /* Body of reply to OFPST_DESC request. Each entry is a NULL-terminated ASCII * string. */ struct ofp_desc_stats { char mfr_desc[DESC_STR_LEN]; /* Manufacturer description. */ char hw_desc[DESC_STR_LEN]; /* Hardware description. */ char sw_desc[DESC_STR_LEN]; /* Software description. */ char serial_num[SERIAL_NUM_LEN]; /* Serial number. */ char dp_desc[DESC_STR_LEN]; /* Human readable description of the datapath. */ }; OFP_ASSERT(sizeof(struct ofp_desc_stats) == 1056); /* Reply to OFPST_AGGREGATE request. */ struct ofp_aggregate_stats_reply { ovs_32aligned_be64 packet_count; /* Number of packets in flows. */ ovs_32aligned_be64 byte_count; /* Number of bytes in flows. */ ovs_be32 flow_count; /* Number of flows. */ uint8_t pad[4]; /* Align to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 24); /* The match type indicates the match structure (set of fields that compose the * match) in use. The match type is placed in the type field at the beginning * of all match structures. The "OpenFlow Extensible Match" type corresponds * to OXM TLV format described below and must be supported by all OpenFlow * switches. Extensions that define other match types may be published on the * ONF wiki. Support for extensions is optional. */ enum ofp_match_type { OFPMT_STANDARD = 0, /* The match fields defined in the ofp11_match structure apply */ OFPMT_OXM = 1, /* OpenFlow Extensible Match */ }; /* Group numbering. Groups can use any number up to OFPG_MAX. */ enum ofp_group { /* Last usable group number. */ OFPG_MAX = 0xffffff00, /* Fake groups. */ OFPG_ALL = 0xfffffffc, /* All groups, for group delete commands. */ OFPG_ANY = 0xffffffff /* Wildcard, for flow stats requests. */ }; /* Group configuration flags */ enum ofp_group_capabilities { OFPGFC_SELECT_WEIGHT = 1 << 0, /* Support weight for select groups */ OFPGFC_SELECT_LIVENESS = 1 << 1, /* Support liveness for select groups */ OFPGFC_CHAINING = 1 << 2, /* Support chaining groups */ OFPGFC_CHAINING_CHECKS = 1 << 3, /* Check chaining for loops and delete */ }; enum ofp_hello_elem_type { OFPHET_VERSIONBITMAP = 1, /* Bitmap of version supported. */ }; /* Common header for all Hello Elements */ struct ofp_hello_elem_header { ovs_be16 type; /* One of OFPHET_*. */ ovs_be16 length; /* Length in bytes of this element. */ }; OFP_ASSERT(sizeof(struct ofp_hello_elem_header) == 4); /* Vendor extension. */ struct ofp_vendor_header { struct ofp_header header; /* Type OFPT_VENDOR or OFPT_EXPERIMENTER. */ ovs_be32 vendor; /* Vendor ID: * - MSB 0: low-order bytes are IEEE OUI. * - MSB != 0: defined by OpenFlow * consortium. */ /* Vendor-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp_vendor_header) == 12); /* Table numbering. Tables can use any number up to OFPT_MAX. */ enum ofp_table { /* Last usable table number. */ OFPTT_MAX = 0xfe, /* Fake tables. */ OFPTT_ALL = 0xff /* Wildcard table used for table config, flow stats and flow deletes. */ }; enum ofp_table_config { /* OpenFlow 1.1 and 1.2 defined this field as shown. * OpenFlow 1.3 and later mark this field as deprecated, but have not * reused it for any new purpose. */ OFPTC11_TABLE_MISS_CONTROLLER = 0 << 0, /* Send to controller. */ OFPTC11_TABLE_MISS_CONTINUE = 1 << 0, /* Go to next table, like OF1.0. */ OFPTC11_TABLE_MISS_DROP = 2 << 0, /* Drop the packet. */ OFPTC11_TABLE_MISS_MASK = 3 << 0, /* OpenFlow 1.4. */ OFPTC14_EVICTION = 1 << 2, /* Allow table to evict flows. */ OFPTC14_VACANCY_EVENTS = 1 << 3, /* Enable vacancy events. */ }; #endif /* openflow/openflow-common.h */ openvswitch-2.5.9/include/openflow/PaxHeaders.82075/openflow-1.0.h0000644000000000000000000000013213534540071021416 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.573851893 openvswitch-2.5.9/include/openflow/openflow-1.0.h0000644000175000017500000004615213534540071023114 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenFlow: protocol between controller and datapath. */ #ifndef OPENFLOW_OPENFLOW10_H #define OPENFLOW_OPENFLOW10_H 1 #include /* Port number(s) meaning * --------------- -------------------------------------- * 0x0000 not assigned a meaning by OpenFlow 1.0 * 0x0001...0xfeff "physical" ports * 0xff00...0xfff6 "reserved" but not assigned a meaning by OpenFlow 1.x * 0xfff7...0xffff "reserved" OFPP_* ports with assigned meanings */ /* Ranges. */ #define OFPP_MAX OFP_PORT_C(0xff00) /* Max # of switch ports. */ #define OFPP_FIRST_RESV OFP_PORT_C(0xfff7) /* First assigned reserved port. */ #define OFPP_LAST_RESV OFP_PORT_C(0xffff) /* Last assigned reserved port. */ /* Reserved output "ports". */ #define OFPP_UNSET OFP_PORT_C(0xfff7) /* For OXM_OF_ACTSET_OUTPUT only. */ #define OFPP_IN_PORT OFP_PORT_C(0xfff8) /* Where the packet came in. */ #define OFPP_TABLE OFP_PORT_C(0xfff9) /* Perform actions in flow table. */ #define OFPP_NORMAL OFP_PORT_C(0xfffa) /* Process with normal L2/L3. */ #define OFPP_FLOOD OFP_PORT_C(0xfffb) /* All ports except input port and * ports disabled by STP. */ #define OFPP_ALL OFP_PORT_C(0xfffc) /* All ports except input port. */ #define OFPP_CONTROLLER OFP_PORT_C(0xfffd) /* Send to controller. */ #define OFPP_LOCAL OFP_PORT_C(0xfffe) /* Local openflow "port". */ #define OFPP_NONE OFP_PORT_C(0xffff) /* Not associated with any port. */ /* OpenFlow 1.0 specific capabilities supported by the datapath (struct * ofp_switch_features, member capabilities). */ enum ofp10_capabilities { OFPC10_STP = 1 << 3, /* 802.1d spanning tree. */ OFPC10_RESERVED = 1 << 4, /* Reserved, must not be set. */ }; /* OpenFlow 1.0 specific flags to indicate behavior of the physical port. * These flags are used in ofp10_phy_port to describe the current * configuration. They are used in the ofp10_port_mod message to configure the * port's behavior. */ enum ofp10_port_config { OFPPC10_NO_STP = 1 << 1, /* Disable 802.1D spanning tree on port. */ OFPPC10_NO_RECV_STP = 1 << 3, /* Drop received 802.1D STP packets. */ OFPPC10_NO_FLOOD = 1 << 4, /* Do not include port when flooding. */ #define OFPPC10_ALL (OFPPC_PORT_DOWN | OFPPC10_NO_STP | OFPPC_NO_RECV | \ OFPPC10_NO_RECV_STP | OFPPC10_NO_FLOOD | OFPPC_NO_FWD | \ OFPPC_NO_PACKET_IN) }; /* OpenFlow 1.0 specific current state of the physical port. These are not * configurable from the controller. */ enum ofp10_port_state { /* The OFPPS10_STP_* bits have no effect on switch operation. The * controller must adjust OFPPC_NO_RECV, OFPPC_NO_FWD, and * OFPPC_NO_PACKET_IN appropriately to fully implement an 802.1D spanning * tree. */ OFPPS10_STP_LISTEN = 0 << 8, /* Not learning or relaying frames. */ OFPPS10_STP_LEARN = 1 << 8, /* Learning but not relaying frames. */ OFPPS10_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */ OFPPS10_STP_BLOCK = 3 << 8, /* Not part of spanning tree. */ OFPPS10_STP_MASK = 3 << 8 /* Bit mask for OFPPS10_STP_* values. */ #define OFPPS10_ALL (OFPPS_LINK_DOWN | OFPPS10_STP_MASK) }; /* OpenFlow 1.0 specific features of physical ports available in a datapath. */ enum ofp10_port_features { OFPPF10_COPPER = 1 << 7, /* Copper medium. */ OFPPF10_FIBER = 1 << 8, /* Fiber medium. */ OFPPF10_AUTONEG = 1 << 9, /* Auto-negotiation. */ OFPPF10_PAUSE = 1 << 10, /* Pause. */ OFPPF10_PAUSE_ASYM = 1 << 11 /* Asymmetric pause. */ }; /* Description of a physical port */ struct ofp10_phy_port { ovs_be16 port_no; struct eth_addr hw_addr; char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */ ovs_be32 config; /* Bitmap of OFPPC_* and OFPPC10_* flags. */ ovs_be32 state; /* Bitmap of OFPPS_* and OFPPS10_* flags. */ /* Bitmaps of OFPPF_* and OFPPF10_* that describe features. All bits * zeroed if unsupported or unavailable. */ ovs_be32 curr; /* Current features. */ ovs_be32 advertised; /* Features being advertised by the port. */ ovs_be32 supported; /* Features supported by the port. */ ovs_be32 peer; /* Features advertised by peer. */ }; OFP_ASSERT(sizeof(struct ofp10_phy_port) == 48); /* Modify behavior of the physical port */ struct ofp10_port_mod { ovs_be16 port_no; struct eth_addr hw_addr; /* The hardware address is not configurable. This is used to sanity-check the request, so it must be the same as returned in an ofp10_phy_port struct. */ ovs_be32 config; /* Bitmap of OFPPC_* flags. */ ovs_be32 mask; /* Bitmap of OFPPC_* flags to be changed. */ ovs_be32 advertise; /* Bitmap of "ofp_port_features"s. Zero all bits to prevent any action taking place. */ uint8_t pad[4]; /* Pad to 64-bits. */ }; OFP_ASSERT(sizeof(struct ofp10_port_mod) == 24); struct ofp10_packet_queue { ovs_be32 queue_id; /* id for the specific queue. */ ovs_be16 len; /* Length in bytes of this queue desc. */ uint8_t pad[2]; /* 64-bit alignment. */ /* Followed by any number of queue properties expressed using * ofp_queue_prop_header, to fill out a total of 'len' bytes. */ }; OFP_ASSERT(sizeof(struct ofp10_packet_queue) == 8); /* Query for port queue configuration. */ struct ofp10_queue_get_config_request { ovs_be16 port; /* Port to be queried. Should refer to a valid physical port (i.e. < OFPP_MAX) */ uint8_t pad[2]; /* 32-bit alignment. */ }; OFP_ASSERT(sizeof(struct ofp10_queue_get_config_request) == 4); /* Queue configuration for a given port. */ struct ofp10_queue_get_config_reply { ovs_be16 port; uint8_t pad[6]; /* struct ofp10_packet_queue queues[0]; List of configured queues. */ }; OFP_ASSERT(sizeof(struct ofp10_queue_get_config_reply) == 8); /* Packet received on port (datapath -> controller). */ struct ofp10_packet_in { ovs_be32 buffer_id; /* ID assigned by datapath. */ ovs_be16 total_len; /* Full length of frame. */ ovs_be16 in_port; /* Port on which frame was received. */ uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t pad; uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word, so the IP header is 32-bit aligned. The amount of data is inferred from the length field in the header. Because of padding, offsetof(struct ofp_packet_in, data) == sizeof(struct ofp_packet_in) - 2. */ }; OFP_ASSERT(sizeof(struct ofp10_packet_in) == 12); /* Send packet (controller -> datapath). */ struct ofp10_packet_out { ovs_be32 buffer_id; /* ID assigned by datapath or UINT32_MAX. */ ovs_be16 in_port; /* Packet's input port (OFPP_NONE if none). */ ovs_be16 actions_len; /* Size of action array in bytes. */ /* Followed by: * - Exactly 'actions_len' bytes (possibly 0 bytes, and always a multiple * of 8) containing actions. * - If 'buffer_id' == UINT32_MAX, packet data to fill out the remainder * of the message length. */ }; OFP_ASSERT(sizeof(struct ofp10_packet_out) == 8); /* Flow wildcards. */ enum ofp10_flow_wildcards { OFPFW10_IN_PORT = 1 << 0, /* Switch input port. */ OFPFW10_DL_VLAN = 1 << 1, /* VLAN vid. */ OFPFW10_DL_SRC = 1 << 2, /* Ethernet source address. */ OFPFW10_DL_DST = 1 << 3, /* Ethernet destination address. */ OFPFW10_DL_TYPE = 1 << 4, /* Ethernet frame type. */ OFPFW10_NW_PROTO = 1 << 5, /* IP protocol. */ OFPFW10_TP_SRC = 1 << 6, /* TCP/UDP source port. */ OFPFW10_TP_DST = 1 << 7, /* TCP/UDP destination port. */ /* IP source address wildcard bit count. 0 is exact match, 1 ignores the * LSB, 2 ignores the 2 least-significant bits, ..., 32 and higher wildcard * the entire field. This is the *opposite* of the usual convention where * e.g. /24 indicates that 8 bits (not 24 bits) are wildcarded. */ OFPFW10_NW_SRC_SHIFT = 8, OFPFW10_NW_SRC_BITS = 6, OFPFW10_NW_SRC_MASK = (((1 << OFPFW10_NW_SRC_BITS) - 1) << OFPFW10_NW_SRC_SHIFT), OFPFW10_NW_SRC_ALL = 32 << OFPFW10_NW_SRC_SHIFT, /* IP destination address wildcard bit count. Same format as source. */ OFPFW10_NW_DST_SHIFT = 14, OFPFW10_NW_DST_BITS = 6, OFPFW10_NW_DST_MASK = (((1 << OFPFW10_NW_DST_BITS) - 1) << OFPFW10_NW_DST_SHIFT), OFPFW10_NW_DST_ALL = 32 << OFPFW10_NW_DST_SHIFT, OFPFW10_DL_VLAN_PCP = 1 << 20, /* VLAN priority. */ OFPFW10_NW_TOS = 1 << 21, /* IP ToS (DSCP field, 6 bits). */ /* Wildcard all fields. */ OFPFW10_ALL = ((1 << 22) - 1) }; /* The wildcards for ICMP type and code fields use the transport source * and destination port fields, respectively. */ #define OFPFW10_ICMP_TYPE OFPFW10_TP_SRC #define OFPFW10_ICMP_CODE OFPFW10_TP_DST /* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate * special conditions. All ones indicates that 802.1Q header is not present. */ #define OFP10_VLAN_NONE 0xffff /* Fields to match against flows */ struct ofp10_match { ovs_be32 wildcards; /* Wildcard fields. */ ovs_be16 in_port; /* Input switch port. */ struct eth_addr dl_src; /* Ethernet source address. */ struct eth_addr dl_dst; /* Ethernet destination address. */ ovs_be16 dl_vlan; /* Input VLAN. */ uint8_t dl_vlan_pcp; /* Input VLAN priority. */ uint8_t pad1[1]; /* Align to 64-bits. */ ovs_be16 dl_type; /* Ethernet frame type. */ uint8_t nw_tos; /* IP ToS (DSCP field, 6 bits). */ uint8_t nw_proto; /* IP protocol or lower 8 bits of ARP opcode. */ uint8_t pad2[2]; /* Align to 64-bits. */ ovs_be32 nw_src; /* IP source address. */ ovs_be32 nw_dst; /* IP destination address. */ ovs_be16 tp_src; /* TCP/UDP source port. */ ovs_be16 tp_dst; /* TCP/UDP destination port. */ }; OFP_ASSERT(sizeof(struct ofp10_match) == 40); enum ofp10_flow_mod_flags { OFPFF10_EMERG = 1 << 2 /* Part of "emergency flow cache". */ }; /* Flow setup and teardown (controller -> datapath). */ struct ofp10_flow_mod { struct ofp10_match match; /* Fields to match */ ovs_be64 cookie; /* Opaque controller-issued identifier. */ /* Flow actions. */ ovs_be16 command; /* One of OFPFC_*. */ ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ ovs_be16 priority; /* Priority level of flow entry. */ ovs_be32 buffer_id; /* Buffered packet to apply to (or -1). Not meaningful for OFPFC_DELETE*. */ ovs_be16 out_port; /* For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */ ovs_be16 flags; /* One of OFPFF_*. */ /* Followed by OpenFlow actions whose length is inferred from the length * field in the OpenFlow header. */ }; OFP_ASSERT(sizeof(struct ofp10_flow_mod) == 64); /* Flow removed (datapath -> controller). */ struct ofp10_flow_removed { struct ofp10_match match; /* Description of fields. */ ovs_be64 cookie; /* Opaque controller-issued identifier. */ ovs_be16 priority; /* Priority level of flow entry. */ uint8_t reason; /* One of OFPRR_*. */ uint8_t pad[1]; /* Align to 32-bits. */ ovs_be32 duration_sec; /* Time flow was alive in seconds. */ ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond duration_sec. */ ovs_be16 idle_timeout; /* Idle timeout from original flow mod. */ uint8_t pad2[2]; /* Align to 64-bits. */ ovs_be64 packet_count; ovs_be64 byte_count; }; OFP_ASSERT(sizeof(struct ofp10_flow_removed) == 80); /* Statistics request or reply message. */ struct ofp10_stats_msg { struct ofp_header header; ovs_be16 type; /* One of the OFPST_* constants. */ ovs_be16 flags; /* Requests: always 0. * Replies: 0 or OFPSF_REPLY_MORE. */ }; OFP_ASSERT(sizeof(struct ofp10_stats_msg) == 12); /* Stats request of type OFPST_AGGREGATE or OFPST_FLOW. */ struct ofp10_flow_stats_request { struct ofp10_match match; /* Fields to match. */ uint8_t table_id; /* ID of table to read (from ofp_table_stats) or 0xff for all tables. */ uint8_t pad; /* Align to 32 bits. */ ovs_be16 out_port; /* Require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */ }; OFP_ASSERT(sizeof(struct ofp10_flow_stats_request) == 44); /* Body of reply to OFPST_FLOW request. */ struct ofp10_flow_stats { ovs_be16 length; /* Length of this entry. */ uint8_t table_id; /* ID of table flow came from. */ uint8_t pad; struct ofp10_match match; /* Description of fields. */ ovs_be32 duration_sec; /* Time flow has been alive in seconds. */ ovs_be32 duration_nsec; /* Time flow has been alive in nanoseconds beyond duration_sec. */ ovs_be16 priority; /* Priority of the entry. Only meaningful when this is not an exact-match entry. */ ovs_be16 idle_timeout; /* Number of seconds idle before expiration. */ ovs_be16 hard_timeout; /* Number of seconds before expiration. */ uint8_t pad2[6]; /* Align to 64 bits. */ ovs_32aligned_be64 cookie; /* Opaque controller-issued identifier. */ ovs_32aligned_be64 packet_count; /* Number of packets in flow. */ ovs_32aligned_be64 byte_count; /* Number of bytes in flow. */ /* Followed by OpenFlow actions whose length is inferred from 'length'. */ }; OFP_ASSERT(sizeof(struct ofp10_flow_stats) == 88); /* Body of reply to OFPST_TABLE request. */ struct ofp10_table_stats { uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ uint8_t pad[3]; /* Align to 32-bits. */ char name[OFP_MAX_TABLE_NAME_LEN]; ovs_be32 wildcards; /* Bitmap of OFPFW10_* wildcards that are supported by the table. */ ovs_be32 max_entries; /* Max number of entries supported. */ ovs_be32 active_count; /* Number of active entries. */ ovs_32aligned_be64 lookup_count; /* # of packets looked up in table. */ ovs_32aligned_be64 matched_count; /* Number of packets that hit table. */ }; OFP_ASSERT(sizeof(struct ofp10_table_stats) == 64); /* Stats request of type OFPST_PORT. */ struct ofp10_port_stats_request { ovs_be16 port_no; /* OFPST_PORT message may request statistics for a single port (specified with port_no) or for all ports (port_no == OFPP_NONE). */ uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ofp10_port_stats_request) == 8); /* Body of reply to OFPST_PORT request. If a counter is unsupported, set * the field to all ones. */ struct ofp10_port_stats { ovs_be16 port_no; uint8_t pad[6]; /* Align to 64-bits. */ ovs_32aligned_be64 rx_packets; /* Number of received packets. */ ovs_32aligned_be64 tx_packets; /* Number of transmitted packets. */ ovs_32aligned_be64 rx_bytes; /* Number of received bytes. */ ovs_32aligned_be64 tx_bytes; /* Number of transmitted bytes. */ ovs_32aligned_be64 rx_dropped; /* Number of packets dropped by RX. */ ovs_32aligned_be64 tx_dropped; /* Number of packets dropped by TX. */ ovs_32aligned_be64 rx_errors; /* Number of receive errors. This is a super-set of receive errors and should be great than or equal to the sum of all rx_*_err values. */ ovs_32aligned_be64 tx_errors; /* Number of transmit errors. This is a super-set of transmit errors. */ ovs_32aligned_be64 rx_frame_err; /* Number of frame alignment errors. */ ovs_32aligned_be64 rx_over_err; /* Number of packets with RX overrun. */ ovs_32aligned_be64 rx_crc_err; /* Number of CRC errors. */ ovs_32aligned_be64 collisions; /* Number of collisions. */ }; OFP_ASSERT(sizeof(struct ofp10_port_stats) == 104); /* All ones is used to indicate all queues in a port (for stats retrieval). */ #define OFPQ_ALL 0xffffffff /* Body for stats request of type OFPST_QUEUE. */ struct ofp10_queue_stats_request { ovs_be16 port_no; /* All ports if OFPP_ALL. */ uint8_t pad[2]; /* Align to 32-bits. */ ovs_be32 queue_id; /* All queues if OFPQ_ALL. */ }; OFP_ASSERT(sizeof(struct ofp10_queue_stats_request) == 8); /* Body for stats reply of type OFPST_QUEUE consists of an array of this * structure type. */ struct ofp10_queue_stats { ovs_be16 port_no; uint8_t pad[2]; /* Align to 32-bits. */ ovs_be32 queue_id; /* Queue id. */ ovs_32aligned_be64 tx_bytes; /* Number of transmitted bytes. */ ovs_32aligned_be64 tx_packets; /* Number of transmitted packets. */ ovs_32aligned_be64 tx_errors; /* # of packets dropped due to overrun. */ }; OFP_ASSERT(sizeof(struct ofp10_queue_stats) == 32); /* Vendor extension stats message. */ struct ofp10_vendor_stats_msg { struct ofp10_stats_msg osm; /* Type OFPST_VENDOR. */ ovs_be32 vendor; /* Vendor ID: * - MSB 0: low-order bytes are IEEE OUI. * - MSB != 0: defined by OpenFlow * consortium. */ /* Followed by vendor-defined arbitrary additional data. */ }; OFP_ASSERT(sizeof(struct ofp10_vendor_stats_msg) == 16); #endif /* openflow/openflow-1.0.h */ openvswitch-2.5.9/include/PaxHeaders.82075/openvswitch0000644000000000000000000000013213534540120017556 xustar0030 mtime=1567801424.637852364 30 atime=1567801425.625859648 30 ctime=1567801424.637852364 openvswitch-2.5.9/include/openvswitch/0000755000175000017500000000000013534540120021321 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/token-bucket.h0000644000000000000000000000013213534540071022404 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.585851981 openvswitch-2.5.9/include/openvswitch/token-bucket.h0000644000175000017500000000275013534540071024076 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_TOKEN_BUCKET_H #define OPENVSWITCH_TOKEN_BUCKET_H 1 #include #include struct token_bucket { /* Configuration settings. */ unsigned int rate; /* Tokens added per millisecond. */ unsigned int burst; /* Max cumulative tokens credit. */ /* Current status. */ unsigned int tokens; /* Current number of tokens. */ long long int last_fill; /* Last time tokens added. */ }; #define TOKEN_BUCKET_INIT(RATE, BURST) { RATE, BURST, 0, LLONG_MIN } void token_bucket_init(struct token_bucket *, unsigned int rate, unsigned int burst); void token_bucket_set(struct token_bucket *, unsigned int rate, unsigned int burst); bool token_bucket_withdraw(struct token_bucket *, unsigned int n); void token_bucket_wait(struct token_bucket *, unsigned int n); #endif /* token-bucket.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/util.h0000644000000000000000000000013213534540071020766 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.589852011 openvswitch-2.5.9/include/openvswitch/util.h0000644000175000017500000000336413534540071022462 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_UTIL_H #define OPENVSWITCH_UTIL_H 1 #include #ifdef __cplusplus extern "C" { #endif void ovs_set_program_name__(const char *name, const char *version, const char *date, const char *time); #define ovs_set_program_name(name, version) \ ovs_set_program_name__(name, version, __DATE__, __TIME__) const char *ovs_get_program_name(void); const char *ovs_get_program_version(void); /* Expands to a string that looks like ":", e.g. "tmp.c:10". * * See http://c-faq.com/ansi/stringize.html for an explanation of OVS_STRINGIZE * and OVS_STRINGIZE2. */ #define OVS_SOURCE_LOCATOR __FILE__ ":" OVS_STRINGIZE(__LINE__) #define OVS_STRINGIZE(ARG) OVS_STRINGIZE2(ARG) #define OVS_STRINGIZE2(ARG) #ARG /* Saturating multiplication of "unsigned int"s: overflow yields UINT_MAX. */ #define OVS_SAT_MUL(X, Y) \ ((Y) == 0 ? 0 \ : (X) <= UINT_MAX / (Y) ? (unsigned int) (X) * (unsigned int) (Y) \ : UINT_MAX) #ifdef __cplusplus } #endif #endif openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071021777 xustar0030 mtime=1567801401.297680407 30 atime=1567801402.065686047 30 ctime=1567801424.601852099 openvswitch-2.5.9/include/openvswitch/automake.mk0000644000175000017500000000056113534540071023467 0ustar00jpettitjpettit00000000000000openvswitchincludedir = $(includedir)/openvswitch openvswitchinclude_HEADERS = \ include/openvswitch/compiler.h \ include/openvswitch/list.h \ include/openvswitch/thread.h \ include/openvswitch/token-bucket.h \ include/openvswitch/types.h \ include/openvswitch/util.h \ include/openvswitch/version.h \ include/openvswitch/vconn.h \ include/openvswitch/vlog.h openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/version.h0000644000000000000000000000013213534540107021476 xustar0030 mtime=1567801415.965788435 30 atime=1567801416.517792505 30 ctime=1567801424.589852011 openvswitch-2.5.9/include/openvswitch/version.h0000644000175000017500000000162613534540107023171 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * Copyright (c) 2014 Cisco Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_VERSION_H #define OPENVSWITCH_VERSION_H 1 #define OVS_PACKAGE_STRING "openvswitch 2.5.9" #define OVS_PACKAGE_VERSION "2.5.9" #define OVS_LIB_VERSION 1 #define OVS_LIB_REVISION 0 #define OVS_LIB_AGE 0 #endif /* openvswitch/version.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/compiler.h0000644000000000000000000000013213534540071021623 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.581851951 openvswitch-2.5.9/include/openvswitch/compiler.h0000644000175000017500000002064013534540071023313 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_COMPILER_H #define OPENVSWITCH_COMPILER_H 1 #ifndef __has_feature #define __has_feature(x) 0 #endif #ifndef __has_extension #define __has_extension(x) 0 #endif /* To make OVS_NO_RETURN portable across gcc/clang and MSVC, it should be * added at the beginning of the function declaration. */ #if __GNUC__ && !__CHECKER__ #define OVS_NO_RETURN __attribute__((__noreturn__)) #elif _MSC_VER #define OVS_NO_RETURN __declspec(noreturn) #else #define OVS_NO_RETURN #endif #if __GNUC__ && !__CHECKER__ #define OVS_UNUSED __attribute__((__unused__)) #define OVS_PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1))) #define OVS_SCANF_FORMAT(FMT, ARG1) __attribute__((__format__(scanf, FMT, ARG1))) #define OVS_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) #define OVS_LIKELY(CONDITION) __builtin_expect(!!(CONDITION), 1) #define OVS_UNLIKELY(CONDITION) __builtin_expect(!!(CONDITION), 0) #else #define OVS_UNUSED #define OVS_PRINTF_FORMAT(FMT, ARG1) #define OVS_SCANF_FORMAT(FMT, ARG1) #define OVS_WARN_UNUSED_RESULT #define OVS_LIKELY(CONDITION) (!!(CONDITION)) #define OVS_UNLIKELY(CONDITION) (!!(CONDITION)) #endif #if __has_feature(c_thread_safety_attributes) /* "clang" annotations for thread safety check. * * OVS_LOCKABLE indicates that the struct contains mutex element * which can be locked by functions like ovs_mutex_lock(). * * Below, the word MUTEX stands for the name of an object with an OVS_LOCKABLE * struct type. It can also be a comma-separated list of multiple structs, * e.g. to require a function to hold multiple locks while invoked. * * * On a variable: * * - OVS_GUARDED indicates that the variable may only be accessed some mutex * is held. * * - OVS_GUARDED_BY(MUTEX) indicates that the variable may only be accessed * while the specific MUTEX is held. * * * On a variable A of mutex type: * * - OVS_ACQ_BEFORE(B), where B is a mutex or a comma-separated list of * mutexes, declare that if both A and B are acquired at the same time, * then A must be acquired before B. That is, B nests inside A. * * - OVS_ACQ_AFTER(B) is the opposite of OVS_ACQ_BEFORE(B), that is, it * declares that A nests inside B. * * * On a function, the following attributes apply to mutexes: * * - OVS_ACQUIRES(MUTEX) indicate that the function must be called without * holding MUTEX and that it returns holding MUTEX. * * - OVS_RELEASES(MUTEX) indicates that the function may only be called with * MUTEX held and that it returns with MUTEX released. It can be used for * all types of MUTEX. * * - OVS_TRY_LOCK(RETVAL, MUTEX) indicate that the function will try to * acquire MUTEX. RETVAL is an integer or boolean value specifying the * return value of a successful lock acquisition. * * - OVS_REQUIRES(MUTEX) indicate that the function may only be called with * MUTEX held and that the function does not release MUTEX. * * - OVS_EXCLUDED(MUTEX) indicates that the function may only be called when * MUTEX is not held. * * * The following variants, with the same syntax, apply to reader-writer locks: * * mutex rwlock, for reading rwlock, for writing * ------------------- ------------------- ------------------- * OVS_ACQUIRES OVS_ACQ_RDLOCK OVS_ACQ_WRLOCK * OVS_RELEASES OVS_RELEASES OVS_RELEASES * OVS_TRY_LOCK OVS_TRY_RDLOCK OVS_TRY_WRLOCK * OVS_REQUIRES OVS_REQ_RDLOCK OVS_REQ_WRLOCK * OVS_EXCLUDED OVS_EXCLUDED OVS_EXCLUDED */ #define OVS_LOCKABLE __attribute__((lockable)) #define OVS_REQ_RDLOCK(...) __attribute__((shared_locks_required(__VA_ARGS__))) #define OVS_ACQ_RDLOCK(...) __attribute__((shared_lock_function(__VA_ARGS__))) #define OVS_REQ_WRLOCK(...) \ __attribute__((exclusive_locks_required(__VA_ARGS__))) #define OVS_ACQ_WRLOCK(...) \ __attribute__((exclusive_lock_function(__VA_ARGS__))) #define OVS_REQUIRES(...) \ __attribute__((exclusive_locks_required(__VA_ARGS__))) #define OVS_ACQUIRES(...) \ __attribute__((exclusive_lock_function(__VA_ARGS__))) #define OVS_TRY_WRLOCK(RETVAL, ...) \ __attribute__((exclusive_trylock_function(RETVAL, __VA_ARGS__))) #define OVS_TRY_RDLOCK(RETVAL, ...) \ __attribute__((shared_trylock_function(RETVAL, __VA_ARGS__))) #define OVS_TRY_LOCK(RETVAL, ...) \ __attribute__((exclusive_trylock_function(RETVAL, __VA_ARGS__))) #define OVS_GUARDED __attribute__((guarded_var)) #define OVS_GUARDED_BY(...) __attribute__((guarded_by(__VA_ARGS__))) #define OVS_RELEASES(...) __attribute__((unlock_function(__VA_ARGS__))) #define OVS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__))) #define OVS_ACQ_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__))) #define OVS_ACQ_AFTER(...) __attribute__((acquired_after(__VA_ARGS__))) #define OVS_NO_THREAD_SAFETY_ANALYSIS \ __attribute__((no_thread_safety_analysis)) #else /* not Clang */ #define OVS_LOCKABLE #define OVS_REQ_RDLOCK(...) #define OVS_ACQ_RDLOCK(...) #define OVS_REQ_WRLOCK(...) #define OVS_ACQ_WRLOCK(...) #define OVS_REQUIRES(...) #define OVS_ACQUIRES(...) #define OVS_TRY_WRLOCK(...) #define OVS_TRY_RDLOCK(...) #define OVS_TRY_LOCK(...) #define OVS_GUARDED #define OVS_GUARDED_BY(...) #define OVS_EXCLUDED(...) #define OVS_RELEASES(...) #define OVS_ACQ_BEFORE(...) #define OVS_ACQ_AFTER(...) #define OVS_NO_THREAD_SAFETY_ANALYSIS #endif /* ISO C says that a C implementation may choose any integer type for an enum * that is sufficient to hold all of its values. Common ABIs (such as the * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even * when a smaller type would suffice. * * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an * enum compatible with a type that is no bigger than necessary. This is the * intended use of OVS_PACKED_ENUM. * * OVS_PACKED_ENUM is intended for use only as a space optimization, since it * only works with GCC. That means that it must not be used in wire protocols * or otherwise exposed outside of a single process. */ #if __GNUC__ && !__CHECKER__ #define OVS_PACKED_ENUM __attribute__((__packed__)) #define HAVE_PACKED_ENUM #else #define OVS_PACKED_ENUM #endif #ifndef _MSC_VER #define OVS_PACKED(DECL) DECL __attribute__((__packed__)) #else #define OVS_PACKED(DECL) __pragma(pack(push, 1)) DECL __pragma(pack(pop)) #endif /* For defining a structure whose instances should aligned on an N-byte * boundary. * * e.g. The following: * OVS_ALIGNED_STRUCT(64, mystruct) { ... }; * is equivalent to the following except that it specifies 64-byte alignment: * struct mystruct { ... }; */ #ifndef _MSC_VER #define OVS_ALIGNED_STRUCT(N, TAG) struct __attribute__((aligned(N))) TAG #else #define OVS_ALIGNED_STRUCT(N, TAG) __declspec(align(N)) struct TAG #endif #ifdef _MSC_VER #define CCALL __cdecl #pragma section(".CRT$XCU",read) #define OVS_CONSTRUCTOR(f) \ static void __cdecl f(void); \ __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \ static void __cdecl f(void) #else #define OVS_CONSTRUCTOR(f) \ static void f(void) __attribute__((constructor)); \ static void f(void) #endif /* OVS_PREFETCH() can be used to instruct the CPU to fetch the cache * line containing the given address to a CPU cache. * OVS_PREFETCH_WRITE() should be used when the memory is going to be * written to. Depending on the target CPU, this can generate the same * instruction as OVS_PREFETCH(), or bring the data into the cache in an * exclusive state. */ #if __GNUC__ #define OVS_PREFETCH(addr) __builtin_prefetch((addr)) #define OVS_PREFETCH_WRITE(addr) __builtin_prefetch((addr), 1) #else #define OVS_PREFETCH(addr) #define OVS_PREFETCH_WRITE(addr) #endif #endif /* compiler.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/types.h0000644000000000000000000000013213534540071021155 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.585851981 openvswitch-2.5.9/include/openvswitch/types.h0000644000175000017500000001134713534540071022651 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2013, 2014, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_TYPES_H #define OPENVSWITCH_TYPES_H 1 #include #include #include "openvswitch/compiler.h" #ifdef __CHECKER__ #define OVS_BITWISE __attribute__((bitwise)) #define OVS_FORCE __attribute__((force)) #else #define OVS_BITWISE #define OVS_FORCE #endif /* The ovs_be types indicate that an object is in big-endian, not * native-endian, byte order. They are otherwise equivalent to uint_t. */ typedef uint16_t OVS_BITWISE ovs_be16; typedef uint32_t OVS_BITWISE ovs_be32; typedef uint64_t OVS_BITWISE ovs_be64; #define OVS_BE16_MAX ((OVS_FORCE ovs_be16) 0xffff) #define OVS_BE32_MAX ((OVS_FORCE ovs_be32) 0xffffffff) #define OVS_BE64_MAX ((OVS_FORCE ovs_be64) 0xffffffffffffffffULL) /* These types help with a few funny situations: * * - The Ethernet header is 14 bytes long, which misaligns everything after * that. One can put 2 "shim" bytes before the Ethernet header, but this * helps only if there is exactly one Ethernet header. If there are two, * as with GRE and VXLAN (and if the inner header doesn't use this * trick--GRE and VXLAN don't) then you have the choice of aligning the * inner data or the outer data. So it seems better to treat 32-bit fields * in protocol headers as aligned only on 16-bit boundaries. * * - ARP headers contain misaligned 32-bit fields. * * - Netlink and OpenFlow contain 64-bit values that are only guaranteed to * be aligned on 32-bit boundaries. * * lib/unaligned.h has helper functions for accessing these. */ /* A 32-bit value, in host byte order, that is only aligned on a 16-bit * boundary. */ typedef struct { #ifdef WORDS_BIGENDIAN uint16_t hi, lo; #else uint16_t lo, hi; #endif } ovs_16aligned_u32; /* A 32-bit value, in network byte order, that is only aligned on a 16-bit * boundary. */ typedef struct { ovs_be16 hi, lo; } ovs_16aligned_be32; /* A 64-bit value, in host byte order, that is only aligned on a 32-bit * boundary. */ typedef struct { #ifdef WORDS_BIGENDIAN uint32_t hi, lo; #else uint32_t lo, hi; #endif } ovs_32aligned_u64; typedef union { uint32_t u32[4]; struct { #ifdef WORDS_BIGENDIAN uint64_t hi, lo; #else uint64_t lo, hi; #endif } u64; } ovs_u128; typedef union { ovs_be32 be32[4]; struct { ovs_be64 hi, lo; } be64; } ovs_be128; /* MSVC2015 doesn't support designated initializers when compiling C++, * and doesn't support ternary operators with non-designated initializers. * So we use these static definitions rather than using initializer macros. */ static const ovs_u128 OVS_U128_MAX = { { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX } }; static const ovs_be128 OVS_BE128_MAX OVS_UNUSED = { { OVS_BE32_MAX, OVS_BE32_MAX, OVS_BE32_MAX, OVS_BE32_MAX } }; /* A 64-bit value, in network byte order, that is only aligned on a 32-bit * boundary. */ typedef struct { ovs_be32 hi, lo; } ovs_32aligned_be64; /* Port numbers * ------------ * * None of these types are directly interchangeable, hence the OVS_BITWISE * annotation. * * ofp_port_t is an OpenFlow 1.0 port number. It uses a 16-bit range, even * though it is a 32-bit type. This allows it to be overlaid on an odp_port_t * for a few situations where this is useful, e.g. in union flow_in_port. * * ofp11_port_t is an OpenFlow-1.1 port number. * * odp_port_t is a port number within a datapath (e.g. see lib/dpif.h). */ typedef uint32_t OVS_BITWISE ofp_port_t; typedef uint32_t OVS_BITWISE odp_port_t; typedef uint32_t OVS_BITWISE ofp11_port_t; /* Macro functions that cast int types to ofp/odp/ofp11 types. */ #define OFP_PORT_C(X) ((OVS_FORCE ofp_port_t) (X)) #define ODP_PORT_C(X) ((OVS_FORCE odp_port_t) (X)) #define OFP11_PORT_C(X) ((OVS_FORCE ofp11_port_t) (X)) /* Using this struct instead of a bare array makes an ethernet address field * assignable. The size of the array is also part of the type, so it is easier * to deal with. */ struct eth_addr { union { uint8_t ea[6]; ovs_be16 be16[3]; }; }; #endif /* openvswitch/types.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/thread.h0000644000000000000000000000013213534540071021260 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.585851981 openvswitch-2.5.9/include/openvswitch/thread.h0000644000175000017500000001127213534540071022751 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_THREAD_H #define OPENVSWITCH_THREAD_H 1 #include #include #include #include #include "util.h" /* Mutex. */ struct OVS_LOCKABLE ovs_mutex { pthread_mutex_t lock; const char *where; /* NULL if and only if uninitialized. */ }; /* "struct ovs_mutex" initializer. */ #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP #define OVS_MUTEX_INITIALIZER { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, \ "" } #else #define OVS_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, "" } #endif #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP #define OVS_ADAPTIVE_MUTEX_INITIALIZER \ { PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, "" } #else #define OVS_ADAPTIVE_MUTEX_INITIALIZER OVS_MUTEX_INITIALIZER #endif /* ovs_mutex functions analogous to pthread_mutex_*() functions. * * Most of these functions abort the process with an error message on any * error. ovs_mutex_trylock() is an exception: it passes through a 0 or EBUSY * return value to the caller and aborts on any other error. */ void ovs_mutex_init(const struct ovs_mutex *); void ovs_mutex_init_recursive(const struct ovs_mutex *); void ovs_mutex_init_adaptive(const struct ovs_mutex *); void ovs_mutex_destroy(const struct ovs_mutex *); void ovs_mutex_unlock(const struct ovs_mutex *mutex) OVS_RELEASES(mutex); void ovs_mutex_lock_at(const struct ovs_mutex *mutex, const char *where) OVS_ACQUIRES(mutex); #define ovs_mutex_lock(mutex) \ ovs_mutex_lock_at(mutex, OVS_SOURCE_LOCATOR) int ovs_mutex_trylock_at(const struct ovs_mutex *mutex, const char *where) OVS_TRY_LOCK(0, mutex); #define ovs_mutex_trylock(mutex) \ ovs_mutex_trylock_at(mutex, OVS_SOURCE_LOCATOR) void ovs_mutex_cond_wait(pthread_cond_t *, const struct ovs_mutex *); /* Convenient once-only execution. * * * Problem * ======= * * POSIX provides pthread_once_t and pthread_once() as primitives for running a * set of code only once per process execution. They are used like this: * * static void run_once(void) { ...initialization... } * static pthread_once_t once = PTHREAD_ONCE_INIT; * ... * pthread_once(&once, run_once); * * pthread_once() does not allow passing any parameters to the initialization * function, which is often inconvenient, because it means that the function * can only access data declared at file scope. * * * Solution * ======== * * Use ovsthread_once, like this, instead: * * static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; * * if (ovsthread_once_start(&once)) { * ...initialization... * ovsthread_once_done(&once); * } */ struct ovsthread_once { bool done; /* Non-atomic, false negatives possible. */ struct ovs_mutex mutex; }; #define OVSTHREAD_ONCE_INITIALIZER \ { \ false, \ OVS_MUTEX_INITIALIZER, \ } static inline bool ovsthread_once_start(struct ovsthread_once *once) OVS_TRY_LOCK(true, once->mutex); void ovsthread_once_done(struct ovsthread_once *once) OVS_RELEASES(once->mutex); bool ovsthread_once_start__(struct ovsthread_once *once) OVS_TRY_LOCK(true, once->mutex); /* Returns true if this is the first call to ovsthread_once_start() for * 'once'. In this case, the caller should perform whatever initialization * actions it needs to do, then call ovsthread_once_done() for 'once'. * * Returns false if this is not the first call to ovsthread_once_start() for * 'once'. In this case, the call will not return until after * ovsthread_once_done() has been called. */ static inline bool ovsthread_once_start(struct ovsthread_once *once) { /* We may be reading 'done' at the same time as the first thread * is writing on it, or we can be using a stale copy of it. The * worst that can happen is that we call ovsthread_once_start__() * once when strictly not necessary. */ return OVS_UNLIKELY(!once->done && ovsthread_once_start__(once)); } #endif /* ovs-thread.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/vconn.h0000644000000000000000000000013213534540071021134 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.589852011 openvswitch-2.5.9/include/openvswitch/vconn.h0000644000175000017500000000644113534540071022627 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_VCONN_H #define OPENVSWITCH_VCONN_H 1 #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct ofpbuf; struct pvconn; struct pvconn_class; struct vconn; struct vconn_class; void vconn_usage(bool active, bool passive, bool bootstrap); /* Active vconns: virtual connections to OpenFlow devices. */ int vconn_verify_name(const char *name); int vconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp, struct vconn **vconnp); void vconn_close(struct vconn *); const char *vconn_get_name(const struct vconn *); uint32_t vconn_get_allowed_versions(const struct vconn *vconn); void vconn_set_allowed_versions(struct vconn *vconn, uint32_t allowed_versions); int vconn_get_version(const struct vconn *); void vconn_set_recv_any_version(struct vconn *); int vconn_connect(struct vconn *); int vconn_recv(struct vconn *, struct ofpbuf **); int vconn_send(struct vconn *, struct ofpbuf *); int vconn_recv_xid(struct vconn *, ovs_be32 xid, struct ofpbuf **); int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **); int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf **); int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list *requests, struct ofpbuf **replyp); int vconn_bundle_transact(struct vconn *, struct ovs_list *requests, uint16_t bundle_flags, void (*error_reporter)(const struct ofp_header *)); void vconn_run(struct vconn *); void vconn_run_wait(struct vconn *); int vconn_get_status(const struct vconn *); int vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp, struct vconn **); int vconn_connect_block(struct vconn *); int vconn_send_block(struct vconn *, struct ofpbuf *); int vconn_recv_block(struct vconn *, struct ofpbuf **); enum vconn_wait_type { WAIT_CONNECT, WAIT_RECV, WAIT_SEND }; void vconn_wait(struct vconn *, enum vconn_wait_type); void vconn_connect_wait(struct vconn *); void vconn_recv_wait(struct vconn *); void vconn_send_wait(struct vconn *); /* Passive vconns: virtual listeners for incoming OpenFlow connections. */ int pvconn_verify_name(const char *name); int pvconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp, struct pvconn **pvconnp); const char *pvconn_get_name(const struct pvconn *); void pvconn_close(struct pvconn *); int pvconn_accept(struct pvconn *, struct vconn **); void pvconn_wait(struct pvconn *); #ifdef __cplusplus } #endif #endif /* vconn.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/vlog.h0000644000000000000000000000013213534540071020760 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.593852041 openvswitch-2.5.9/include/openvswitch/vlog.h0000644000175000017500000003200013534540071022441 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_VLOG_H #define OPENVSWITCH_VLOG_H 1 /* Logging. * * * Thread-safety * ============= * * Fully thread safe. */ #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* Logging severity levels. * * ovs-appctl(8) defines each of the log levels. */ #define VLOG_LEVELS \ VLOG_LEVEL(OFF, LOG_ALERT, 1) \ VLOG_LEVEL(EMER, LOG_ALERT, 1) \ VLOG_LEVEL(ERR, LOG_ERR, 3) \ VLOG_LEVEL(WARN, LOG_WARNING, 4) \ VLOG_LEVEL(INFO, LOG_NOTICE, 5) \ VLOG_LEVEL(DBG, LOG_DEBUG, 7) enum vlog_level { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424_LEVEL) VLL_##NAME, VLOG_LEVELS #undef VLOG_LEVEL VLL_N_LEVELS }; const char *vlog_get_level_name(enum vlog_level); enum vlog_level vlog_get_level_val(const char *name); /* Destinations that we can log to. */ #define VLOG_DESTINATIONS \ VLOG_DESTINATION(SYSLOG, "ovs|%05N|%c%T|%p|%m") \ VLOG_DESTINATION(CONSOLE, "%D{%Y-%m-%dT%H:%M:%SZ}|%05N|%c%T|%p|%m") \ VLOG_DESTINATION(FILE, "%D{%Y-%m-%dT%H:%M:%S.###Z}|%05N|%c%T|%p|%m") enum vlog_destination { #define VLOG_DESTINATION(NAME, PATTERN) VLF_##NAME, VLOG_DESTINATIONS #undef VLOG_DESTINATION VLF_N_DESTINATIONS, VLF_ANY_DESTINATION = -1 }; const char *vlog_get_destination_name(enum vlog_destination); enum vlog_destination vlog_get_destination_val(const char *name); /* A log module. */ struct vlog_module { struct ovs_list list; const char *name; /* User-visible name. */ int levels[VLF_N_DESTINATIONS]; /* Minimum log level for each destination. */ int min_level; /* Minimum log level for any destination. */ bool honor_rate_limits; /* Set false to ignore rate limits. */ }; /* Global list of all logging modules */ extern struct ovs_list vlog_modules; void vlog_insert_module(struct ovs_list *); /* Creates and initializes a global instance of a module named MODULE. */ #define VLOG_DEFINE_MODULE(MODULE) \ VLOG_DEFINE_MODULE__(MODULE) \ OVS_CONSTRUCTOR(init_##MODULE) { \ vlog_insert_module(&VLM_##MODULE.list); \ } \ const char *vlog_get_module_name(const struct vlog_module *); struct vlog_module *vlog_module_from_name(const char *name); /* Rate-limiter for log messages. */ struct vlog_rate_limit { struct token_bucket token_bucket; time_t first_dropped; /* Time first message was dropped. */ time_t last_dropped; /* Time of most recent message drop. */ unsigned int n_dropped; /* Number of messages dropped. */ struct ovs_mutex mutex; /* Mutual exclusion for rate limit. */ }; /* Number of tokens to emit a message. We add 'rate' tokens per millisecond, * thus 60,000 tokens are required to emit one message per minute. */ #define VLOG_MSG_TOKENS (60 * 1000) /* Initializer for a struct vlog_rate_limit, to set up a maximum rate of RATE * messages per minute and a maximum burst size of BURST messages. */ #define VLOG_RATE_LIMIT_INIT(RATE, BURST) \ { \ TOKEN_BUCKET_INIT(RATE, OVS_SAT_MUL(BURST, VLOG_MSG_TOKENS)), \ 0, /* first_dropped */ \ 0, /* last_dropped */ \ 0, /* n_dropped */ \ OVS_MUTEX_INITIALIZER /* mutex */ \ } /* Configuring how each module logs messages. */ enum vlog_level vlog_get_level(const struct vlog_module *, enum vlog_destination); void vlog_set_levels(struct vlog_module *, enum vlog_destination, enum vlog_level); char *vlog_set_levels_from_string(const char *) OVS_WARN_UNUSED_RESULT; void vlog_set_levels_from_string_assert(const char *); char *vlog_get_levels(void); char *vlog_get_patterns(void); bool vlog_is_enabled(const struct vlog_module *, enum vlog_level); bool vlog_should_drop(const struct vlog_module *, enum vlog_level, struct vlog_rate_limit *); void vlog_set_verbosity(const char *arg); /* Configuring log destinations. */ void vlog_set_pattern(enum vlog_destination, const char *pattern); int vlog_set_log_file(const char *file_name); int vlog_reopen_log_file(void); #ifndef _WIN32 void vlog_change_owner_unix(uid_t, gid_t); #endif /* Configure method how vlog should send messages to syslog server. */ void vlog_set_syslog_method(const char *method); /* Configure syslog target. */ void vlog_set_syslog_target(const char *target); /* Initialization. */ void vlog_init(void); void vlog_enable_async(void); /* Functions for actual logging. */ void vlog(const struct vlog_module *, enum vlog_level, const char *format, ...) OVS_PRINTF_FORMAT (3, 4); void vlog_valist(const struct vlog_module *, enum vlog_level, const char *, va_list) OVS_PRINTF_FORMAT (3, 0); OVS_NO_RETURN void vlog_fatal(const struct vlog_module *, const char *format, ...) OVS_PRINTF_FORMAT (2, 3); OVS_NO_RETURN void vlog_fatal_valist(const struct vlog_module *, const char *format, va_list) OVS_PRINTF_FORMAT (2, 0); OVS_NO_RETURN void vlog_abort(const struct vlog_module *, const char *format, ...) OVS_PRINTF_FORMAT (2, 3); OVS_NO_RETURN void vlog_abort_valist(const struct vlog_module *, const char *format, va_list) OVS_PRINTF_FORMAT (2, 0); void vlog_rate_limit(const struct vlog_module *, enum vlog_level, struct vlog_rate_limit *, const char *, ...) OVS_PRINTF_FORMAT (4, 5); /* Creates and initializes a global instance of a module named MODULE, and * defines a static variable named THIS_MODULE that points to it, for use with * the convenience macros below. */ #define VLOG_DEFINE_THIS_MODULE(MODULE) \ VLOG_DEFINE_MODULE(MODULE); \ static struct vlog_module *const THIS_MODULE = &VLM_##MODULE /* Convenience macros. These assume that THIS_MODULE points to a "struct * vlog_module" for the current module, as set up by e.g. the * VLOG_DEFINE_MODULE macro above. * * Guaranteed to preserve errno. */ #define VLOG_FATAL(...) vlog_fatal(THIS_MODULE, __VA_ARGS__) #define VLOG_ABORT(...) vlog_abort(THIS_MODULE, __VA_ARGS__) #define VLOG_EMER(...) VLOG(VLL_EMER, __VA_ARGS__) #define VLOG_ERR(...) VLOG(VLL_ERR, __VA_ARGS__) #define VLOG_WARN(...) VLOG(VLL_WARN, __VA_ARGS__) #define VLOG_INFO(...) VLOG(VLL_INFO, __VA_ARGS__) #define VLOG_DBG(...) VLOG(VLL_DBG, __VA_ARGS__) /* More convenience macros, for testing whether a given level is enabled in * THIS_MODULE. When constructing a log message is expensive, this enables it * to be skipped. */ #define VLOG_IS_ERR_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_ERR) #define VLOG_IS_WARN_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_WARN) #define VLOG_IS_INFO_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_INFO) #define VLOG_IS_DBG_ENABLED() vlog_is_enabled(THIS_MODULE, VLL_DBG) /* Convenience macros for rate-limiting. * Guaranteed to preserve errno. */ #define VLOG_ERR_RL(RL, ...) VLOG_RL(RL, VLL_ERR, __VA_ARGS__) #define VLOG_WARN_RL(RL, ...) VLOG_RL(RL, VLL_WARN, __VA_ARGS__) #define VLOG_INFO_RL(RL, ...) VLOG_RL(RL, VLL_INFO, __VA_ARGS__) #define VLOG_DBG_RL(RL, ...) VLOG_RL(RL, VLL_DBG, __VA_ARGS__) /* Convenience macros to additionally store log message in buffer * Caller is responsible for freeing *ERRP afterwards */ #define VLOG_ERR_BUF(ERRP, ...) VLOG_ERRP(ERRP, VLL_ERR, __VA_ARGS__) #define VLOG_WARN_BUF(ERRP, ...) VLOG_ERRP(ERRP, VLL_WARN, __VA_ARGS__) #define VLOG_DROP_ERR(RL) vlog_should_drop(THIS_MODULE, VLL_ERR, RL) #define VLOG_DROP_WARN(RL) vlog_should_drop(THIS_MODULE, VLL_WARN, RL) #define VLOG_DROP_INFO(RL) vlog_should_drop(THIS_MODULE, VLL_INFO, RL) #define VLOG_DROP_DBG(RL) vlog_should_drop(THIS_MODULE, VLL_DBG, RL) /* Macros for logging at most once per execution. */ #define VLOG_ERR_ONCE(...) VLOG_ONCE(VLL_ERR, __VA_ARGS__) #define VLOG_WARN_ONCE(...) VLOG_ONCE(VLL_WARN, __VA_ARGS__) #define VLOG_INFO_ONCE(...) VLOG_ONCE(VLL_INFO, __VA_ARGS__) #define VLOG_DBG_ONCE(...) VLOG_ONCE(VLL_DBG, __VA_ARGS__) /* Command line processing. */ #define VLOG_OPTION_ENUMS \ OPT_LOG_FILE, \ OPT_SYSLOG_IMPL, \ OPT_SYSLOG_TARGET #define VLOG_LONG_OPTIONS \ {"verbose", optional_argument, NULL, 'v'}, \ {"log-file", optional_argument, NULL, OPT_LOG_FILE}, \ {"syslog-method", required_argument, NULL, OPT_SYSLOG_IMPL}, \ {"syslog-target", required_argument, NULL, OPT_SYSLOG_TARGET} #define VLOG_OPTION_HANDLERS \ case 'v': \ vlog_set_verbosity(optarg); \ break; \ case OPT_LOG_FILE: \ vlog_set_log_file(optarg); \ break; \ case OPT_SYSLOG_IMPL: \ vlog_set_syslog_method(optarg); \ break; \ case OPT_SYSLOG_TARGET: \ vlog_set_syslog_target(optarg); \ break; void vlog_usage(void); /* Implementation details. */ #define VLOG(LEVEL, ...) \ do { \ enum vlog_level level__ = LEVEL; \ if (THIS_MODULE->min_level >= level__) { \ vlog(THIS_MODULE, level__, __VA_ARGS__); \ } \ } while (0) #define VLOG_RL(RL, LEVEL, ...) \ do { \ enum vlog_level level__ = LEVEL; \ if (THIS_MODULE->min_level >= level__) { \ vlog_rate_limit(THIS_MODULE, level__, RL, __VA_ARGS__); \ } \ } while (0) #define VLOG_ONCE(LEVEL, ...) \ do { \ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; \ if (ovsthread_once_start(&once)) { \ vlog(THIS_MODULE, LEVEL, __VA_ARGS__); \ ovsthread_once_done(&once); \ } \ } while (0) #define VLOG_ERRP(ERRP, LEVEL, ...) \ do { \ VLOG(LEVEL, __VA_ARGS__); \ if (ERRP) { \ *(ERRP) = xasprintf(__VA_ARGS__); \ } \ } while (0) #define VLOG_DEFINE_MODULE__(MODULE) \ extern struct vlog_module VLM_##MODULE; \ struct vlog_module VLM_##MODULE = \ { \ OVS_LIST_INITIALIZER(&VLM_##MODULE.list), \ #MODULE, /* name */ \ { VLL_INFO, VLL_INFO, VLL_INFO }, /* levels */ \ VLL_INFO, /* min_level */ \ true /* honor_rate_limits */ \ }; #ifdef __cplusplus } #endif #endif /* vlog.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/version.h.in0000644000000000000000000000013213534540071022103 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.637852364 openvswitch-2.5.9/include/openvswitch/version.h.in0000644000175000017500000000167713534540071023604 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * Copyright (c) 2014 Cisco Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_VERSION_H #define OPENVSWITCH_VERSION_H 1 #define OVS_PACKAGE_STRING "@PACKAGE_STRING@" #define OVS_PACKAGE_VERSION "@PACKAGE_VERSION@" #define OVS_LIB_VERSION @LT_CURRENT@ #define OVS_LIB_REVISION @LT_REVISION@ #define OVS_LIB_AGE @LT_AGE@ #endif /* openvswitch/version.h */ openvswitch-2.5.9/include/openvswitch/PaxHeaders.82075/list.h0000644000000000000000000000013213534540071020764 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.585851981 openvswitch-2.5.9/include/openvswitch/list.h0000644000175000017500000000164513534540071022460 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OPENVSWITCH_LIST_H #define OPENVSWITCH_LIST_H 1 /* Doubly linked list head or element. */ struct ovs_list { struct ovs_list *prev; /* Previous list element. */ struct ovs_list *next; /* Next list element. */ }; #define OVS_LIST_INITIALIZER(LIST) { LIST, LIST } #endif /* list.h */ openvswitch-2.5.9/include/PaxHeaders.82075/sparse0000644000000000000000000000013213534540120016502 xustar0030 mtime=1567801424.601852099 30 atime=1567801425.625859648 30 ctime=1567801424.601852099 openvswitch-2.5.9/include/sparse/0000755000175000017500000000000013534540120020245 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/sparse/PaxHeaders.82075/arpa0000644000000000000000000000013213534540120017425 xustar0030 mtime=1567801424.525851539 30 atime=1567801425.625859648 30 ctime=1567801424.525851539 openvswitch-2.5.9/include/sparse/arpa/0000755000175000017500000000000013534540120021170 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/sparse/arpa/PaxHeaders.82075/inet.h0000644000000000000000000000013213534540071020617 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.525851539 openvswitch-2.5.9/include/sparse/arpa/inet.h0000644000175000017500000000133613534540071022310 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #include openvswitch-2.5.9/include/sparse/PaxHeaders.82075/rte_atomic.h0000644000000000000000000000013213534540071021063 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.533851598 openvswitch-2.5.9/include/sparse/rte_atomic.h0000644000175000017500000000166413534540071022560 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif /* Fix sparse technicality about types in one of the function calls by just * ignoring it. */ #define __sync_add_and_fetch(a, b) (0) /* Get actual definitions for us to annotate and build on. */ #include_next openvswitch-2.5.9/include/sparse/PaxHeaders.82075/sys0000644000000000000000000000013213534540120017320 xustar0030 mtime=1567801424.537851628 30 atime=1567801425.625859648 30 ctime=1567801424.537851628 openvswitch-2.5.9/include/sparse/sys/0000755000175000017500000000000013534540120021063 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/sparse/sys/PaxHeaders.82075/wait.h0000644000000000000000000000013213534540071020517 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.537851628 openvswitch-2.5.9/include/sparse/sys/wait.h0000644000175000017500000000174213534540071022211 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef __SYS_WAIT_SPARSE #define __SYS_WAIT_SPARSE 1 #include_next #undef wait #define wait(a) rpl_wait(a) pid_t rpl_wait(int *); #undef waitpid #define waitpid(a, b, c) rpl_waitpid(a, b, c) pid_t rpl_waitpid(pid_t, int *, int); #endif /* for sparse */ openvswitch-2.5.9/include/sparse/sys/PaxHeaders.82075/socket.h0000644000000000000000000000013213534540071021043 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.537851628 openvswitch-2.5.9/include/sparse/sys/socket.h0000644000175000017500000000765513534540071022546 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef __SYS_SOCKET_SPARSE #define __SYS_SOCKET_SPARSE 1 #include "openvswitch/types.h" #include #include typedef unsigned short int sa_family_t; typedef __socklen_t socklen_t; struct sockaddr { sa_family_t sa_family; char sa_data[64]; }; struct sockaddr_storage { sa_family_t ss_family; char sa_data[64]; }; struct msghdr { void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov; int msg_iovlen; void *msg_control; socklen_t msg_controllen; int msg_flags; }; struct cmsghdr { size_t cmsg_len; int cmsg_level; int cmsg_type; unsigned char cmsg_data[]; }; #define __CMSG_ALIGNTO sizeof(size_t) #define CMSG_ALIGN(LEN) \ (((LEN) + __CMSG_ALIGNTO - 1) / __CMSG_ALIGNTO * __CMSG_ALIGNTO) #define CMSG_DATA(CMSG) ((CMSG)->cmsg_data) #define CMSG_LEN(LEN) (sizeof(struct cmsghdr) + (LEN)) #define CMSG_SPACE(LEN) CMSG_ALIGN(CMSG_LEN(LEN)) #define CMSG_FIRSTHDR(MSG) \ ((MSG)->msg_controllen ? (struct cmsghdr *) (MSG)->msg_control : NULL) #define CMSG_NXTHDR(MSG, CMSG) __cmsg_nxthdr(MSG, CMSG) static inline struct cmsghdr * __cmsg_nxthdr(struct msghdr *msg, struct cmsghdr *cmsg) { size_t ofs = (char *) cmsg - (char *) msg->msg_control; size_t next_ofs = ofs + CMSG_ALIGN(cmsg->cmsg_len); return (next_ofs < msg->msg_controllen ? (void *) ((char *) msg->msg_control + next_ofs) : NULL); } enum { SCM_RIGHTS = 1 }; enum { SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET, SOCK_STREAM }; enum { SOL_PACKET, SOL_SOCKET }; enum { SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE, SO_ERROR, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVLOWAT, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, SO_SNDLOWAT, SO_SNDTIMEO, SO_TYPE, SO_RCVBUFFORCE, SO_ATTACH_FILTER }; enum { MSG_CTRUNC, MSG_DONTROUTE, MSG_EOR, MSG_OOB, MSG_NOSIGNAL, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, MSG_DONTWAIT }; enum { AF_UNSPEC, PF_UNSPEC = AF_UNSPEC, AF_INET, PF_INET = AF_INET, AF_INET6, PF_INET6 = AF_INET6, AF_UNIX, PF_UNIX = AF_UNIX, AF_NETLINK, PF_NETLINK = AF_NETLINK, AF_PACKET, PF_PACKET = AF_PACKET }; enum { SHUT_RD, SHUT_RDWR, SHUT_WR }; int accept(int, struct sockaddr *, socklen_t *); int bind(int, const struct sockaddr *, socklen_t); int connect(int, const struct sockaddr *, socklen_t); int getpeername(int, struct sockaddr *, socklen_t *); int getsockname(int, struct sockaddr *, socklen_t *); int getsockopt(int, int, int, void *, socklen_t *); int listen(int, int); ssize_t recv(int, void *, size_t, int); ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *); ssize_t recvmsg(int, struct msghdr *, int); ssize_t send(int, const void *, size_t, int); ssize_t sendmsg(int, const struct msghdr *, int); ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); int setsockopt(int, int, int, const void *, socklen_t); int shutdown(int, int); int sockatmark(int); int socket(int, int, int); int socketpair(int, int, int, int[2]); #endif /* for sparse */ openvswitch-2.5.9/include/sparse/PaxHeaders.82075/netpacket0000644000000000000000000000013213534540120020460 xustar0030 mtime=1567801424.533851598 30 atime=1567801425.625859648 30 ctime=1567801424.533851598 openvswitch-2.5.9/include/sparse/netpacket/0000755000175000017500000000000013534540120022223 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/sparse/netpacket/PaxHeaders.82075/packet.h0000644000000000000000000000013213534540071022162 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.533851598 openvswitch-2.5.9/include/sparse/netpacket/packet.h0000644000175000017500000000210413534540071023645 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef __NETPACKET_PACKET_SPARSE #define __NETPACKET_PACKET_SPARSE 1 #include "openvswitch/types.h" struct sockaddr_ll { unsigned short int sll_family; ovs_be16 sll_protocol; int sll_ifindex; unsigned short int sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[8]; }; #endif /* sparse */ openvswitch-2.5.9/include/sparse/PaxHeaders.82075/netinet0000644000000000000000000000013213534540120020150 xustar0030 mtime=1567801424.529851568 30 atime=1567801425.625859648 30 ctime=1567801424.529851568 openvswitch-2.5.9/include/sparse/netinet/0000755000175000017500000000000013534540120021713 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/sparse/netinet/PaxHeaders.82075/in.h0000644000000000000000000000013213534540071021011 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.529851568 openvswitch-2.5.9/include/sparse/netinet/in.h0000644000175000017500000000734713534540071022512 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef _NETINET_IN_H #define _NETINET_IN_H 1 #include "openvswitch/types.h" #include #include typedef ovs_be16 in_port_t; typedef ovs_be32 in_addr_t; struct in_addr { in_addr_t s_addr; }; struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; }; struct in6_addr { union { uint8_t u_s6_addr[16]; } u; }; #define s6_addr u.u_s6_addr extern const struct in6_addr in6addr_any; /* Ditto, for IPv6. */ struct sockaddr_in6 { sa_family_t sin6_family; in_port_t sin6_port; /* Transport layer port # */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* IPv6 scope-id */ }; #define IPPROTO_IP 0 #define IPPROTO_IPV6 41 #define IPPROTO_HOPOPTS 0 #define IPPROTO_ICMP 1 #define IPPROTO_IGMP 2 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 #define IPPROTO_ROUTING 43 #define IPPROTO_FRAGMENT 44 #define IPPROTO_GRE 47 #define IPPROTO_ESP 50 #define IPPROTO_AH 51 #define IPPROTO_ICMPV6 58 #define IPPROTO_NONE 59 #define IPPROTO_DSTOPTS 60 #define IPPROTO_SCTP 132 #define IPPORT_FTP 21 /* All the IP options documented in Linux ip(7). */ #define IP_ADD_MEMBERSHIP 35 #define IP_DROP_MEMBERSHIP 36 #define IP_HDRINCL 3 #define IP_MTU 14 #define IP_MTU_DISCOVER 10 #define IP_MULTICAST_IF 32 #define IP_MULTICAST_LOOP 34 #define IP_MULTICAST_TTL 33 #define IP_NODEFRAG 22 #define IP_OPTIONS 4 #define IP_PKTINFO 8 #define IP_RECVERR 11 #define IP_RECVOPTS 6 #define IP_RECVTOS 13 #define IP_RECVTTL 12 #define IP_RETOPTS 7 #define IP_ROUTER_ALERT 5 #define IP_TOS 1 #define IP_TTL 2 #define INADDR_ANY 0x00000000 #define INADDR_BROADCAST 0xffffffff #define INADDR_LOOPBACK 0x7f000001 #define INADDR_NONE 0xffffffff #define IN6_IS_ADDR_V4MAPPED(X) \ ((X)->s6_addr[0] == 0 && \ (X)->s6_addr[1] == 0 && \ (X)->s6_addr[2] == 0 && \ (X)->s6_addr[3] == 0 && \ (X)->s6_addr[4] == 0 && \ (X)->s6_addr[5] == 0 && \ (X)->s6_addr[6] == 0 && \ (X)->s6_addr[7] == 0 && \ (X)->s6_addr[8] == 0 && \ (X)->s6_addr[9] == 0 && \ (X)->s6_addr[10] == 0xff && \ (X)->s6_addr[11] == 0xff) #define INET6_ADDRSTRLEN 46 #define IPV6_TCLASS 67 static inline ovs_be32 htonl(uint32_t x) { return (OVS_FORCE ovs_be32) x; } static inline ovs_be16 htons(uint16_t x) { return (OVS_FORCE ovs_be16) x; } static inline uint32_t ntohl(ovs_be32 x) { return (OVS_FORCE uint32_t) x; } static inline uint16_t ntohs(ovs_be16 x) { return (OVS_FORCE uint16_t) x; } in_addr_t inet_addr(const char *); int inet_aton (const char *, struct in_addr *); const char *inet_ntop(int, const void *, char *, socklen_t); int inet_pton(int, const char *, void *); #endif /* */ openvswitch-2.5.9/include/sparse/netinet/PaxHeaders.82075/ip6.h0000644000000000000000000000013213534540071021101 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.529851568 openvswitch-2.5.9/include/sparse/netinet/ip6.h0000644000175000017500000000337713534540071022601 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef __NETINET_IP6_SPARSE #define __NETINET_IP6_SPARSE 1 #include struct ip6_hdr { union { struct ip6_hdrctl { ovs_be32 ip6_un1_flow; ovs_be16 ip6_un1_plen; uint8_t ip6_un1_nxt; uint8_t ip6_un1_hlim; } ip6_un1; uint8_t ip6_un2_vfc; } ip6_ctlun; struct in6_addr ip6_src; struct in6_addr ip6_dst; }; #define ip6_vfc ip6_ctlun.ip6_un2_vfc #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen #define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt #define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim #define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim struct ip6_rthdr { uint8_t ip6r_nxt; uint8_t ip6r_len; uint8_t ip6r_type; uint8_t ip6r_segleft; }; struct ip6_ext { uint8_t ip6e_nxt; uint8_t ip6e_len; }; struct ip6_frag { uint8_t ip6f_nxt; uint8_t ip6f_reserved; ovs_be16 ip6f_offlg; ovs_be32 ip6f_ident; }; #define IP6F_OFF_MASK ((OVS_FORCE ovs_be16) 0xfff8) #endif /* netinet/ip6.h sparse */ openvswitch-2.5.9/include/sparse/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020723 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.601852099 openvswitch-2.5.9/include/sparse/automake.mk0000644000175000017500000000103113534540071022404 0ustar00jpettitjpettit00000000000000noinst_HEADERS += \ include/sparse/arpa/inet.h \ include/sparse/assert.h \ include/sparse/bmi2intrin.h \ include/sparse/emmintrin.h \ include/sparse/math.h \ include/sparse/netinet/in.h \ include/sparse/netinet/ip6.h \ include/sparse/netpacket/packet.h \ include/sparse/pthread.h \ include/sparse/rte_atomic.h \ include/sparse/rte_lcore.h \ include/sparse/rte_vect.h \ include/sparse/sys/socket.h \ include/sparse/sys/wait.h openvswitch-2.5.9/include/sparse/PaxHeaders.82075/emmintrin.h0000644000000000000000000000013213534540071020737 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.525851539 openvswitch-2.5.9/include/sparse/emmintrin.h0000644000175000017500000000160313534540071022425 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif /* GCC 4.8 *intrin.h headers do not work if these are not defined */ #define __SSE2__ #define __SSE__ #define __MMX__ #include_next #undef __MMX__ #undef __SSE__ #undef __SSE2__ openvswitch-2.5.9/include/sparse/PaxHeaders.82075/rte_lcore.h0000644000000000000000000000013213534540071020713 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.537851628 openvswitch-2.5.9/include/sparse/rte_lcore.h0000644000175000017500000000150613534540071022403 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif typedef int rte_cpuset_t; /* Get actual definitions for us to annotate and build on. */ #include_next openvswitch-2.5.9/include/sparse/PaxHeaders.82075/pthread.h0000644000000000000000000000013213534540071020364 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.533851598 openvswitch-2.5.9/include/sparse/pthread.h0000644000175000017500000000227613534540071022061 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif /* Get actual definitions for us to annotate and build on. */ #include_next /* Sparse complains about the proper PTHREAD_*_INITIALIZER definitions. * Luckily, it's not a real compiler so we can overwrite it with something * simple. */ #undef PTHREAD_MUTEX_INITIALIZER #define PTHREAD_MUTEX_INITIALIZER {} #undef PTHREAD_RWLOCK_INITIALIZER #define PTHREAD_RWLOCK_INITIALIZER {} #undef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP #define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP {} openvswitch-2.5.9/include/sparse/PaxHeaders.82075/bmi2intrin.h0000644000000000000000000000013213534540071021012 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.525851539 openvswitch-2.5.9/include/sparse/bmi2intrin.h0000644000175000017500000000166213534540071022505 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif /* Sparse doesn't know the __int128 type used by GCC 4.9 *intrin.h headers. * We cannot use a typedef because the type is used with a qualifier * ('unsigned __int128') */ #define __int128 int #include_next #undef __int128 openvswitch-2.5.9/include/sparse/PaxHeaders.82075/assert.h0000644000000000000000000000013213534540071020236 xustar0030 mtime=1567801401.301680435 30 atime=1567801402.065686047 30 ctime=1567801424.525851539 openvswitch-2.5.9/include/sparse/assert.h0000644000175000017500000000143313534540071021725 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif extern void __ovs_assert(_Bool); #define assert(EXPRESSION) __ovs_assert(EXPRESSION) openvswitch-2.5.9/include/sparse/PaxHeaders.82075/rte_vect.h0000644000000000000000000000013213534540071020550 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.537851628 openvswitch-2.5.9/include/sparse/rte_vect.h0000644000175000017500000000147713534540071022247 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif typedef int __m128i; /* Get actual definitions for us to annotate and build on. */ #include_next openvswitch-2.5.9/include/sparse/PaxHeaders.82075/math.h0000644000000000000000000000013213534540071017666 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.529851568 openvswitch-2.5.9/include/sparse/math.h0000644000175000017500000001313213534540071021354 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKER__ #error "Use this header only with sparse. It is not a correct implementation." #endif #ifndef __SYS_MATH_SPARSE #define __SYS_MATH_SPARSE 1 double acos(double); float acosf(float); double acosh(double); float acoshf(float); long double acoshl(long double); long double acosl(long double); double asin(double); float asinf(float); double asinh(double); float asinhf(float); long double asinhl(long double); long double asinl(long double); double atan(double); double atan2(double, double); float atan2f(float, float); long double atan2l(long double, long double); float atanf(float); double atanh(double); float atanhf(float); long double atanhl(long double); long double atanl(long double); double cbrt(double); float cbrtf(float); long double cbrtl(long double); double ceil(double); float ceilf(float); long double ceill(long double); double copysign(double, double); float copysignf(float, float); long double copysignl(long double, long double); double cos(double); float cosf(float); double cosh(double); float coshf(float); long double coshl(long double); long double cosl(long double); double erf(double); double erfc(double); float erfcf(float); long double erfcl(long double); float erff(float); long double erfl(long double); double exp(double); double exp2(double); float exp2f(float); long double exp2l(long double); float expf(float); long double expl(long double); double expm1(double); float expm1f(float); long double expm1l(long double); double fabs(double); float fabsf(float); long double fabsl(long double); double fdim(double, double); float fdimf(float, float); long double fdiml(long double, long double); double floor(double); float floorf(float); long double floorl(long double); double fma(double, double, double); float fmaf(float, float, float); long double fmal(long double, long double, long double); double fmax(double, double); float fmaxf(float, float); long double fmaxl(long double, long double); double fmin(double, double); float fminf(float, float); long double fminl(long double, long double); double fmod(double, double); float fmodf(float, float); long double fmodl(long double, long double); double frexp(double, int *); float frexpf(float value, int *); long double frexpl(long double value, int *); double hypot(double, double); float hypotf(float, float); long double hypotl(long double, long double); int ilogb(double); int ilogbf(float); int ilogbl(long double); double j0(double); double j1(double); double jn(int, double); double ldexp(double, int); float ldexpf(float, int); long double ldexpl(long double, int); long long llrint(double); long long llrintf(float); long long llrintl(long double); long long llround(double); long long llroundf(float); long long llroundl(long double); double log(double); double log10(double); float log10f(float); long double log10l(long double); double log1p(double); float log1pf(float); long double log1pl(long double); double log2(double); float log2f(float); long double log2l(long double); double logb(double); float logbf(float); long double logbl(long double); float logf(float); long double logl(long double); long lrint(double); long lrintf(float); long lrintl(long double); long lround(double); long lroundf(float); long lroundl(long double); double modf(double, double *); float modff(float, float *); long double modfl(long double, long double *); double nan(const char *); float nanf(const char *); long double nanl(const char *); double nearbyint(double); float nearbyintf(float); long double nearbyintl(long double); double nextafter(double, double); float nextafterf(float, float); long double nextafterl(long double, long double); double nexttoward(double, long double); float nexttowardf(float, long double); long double nexttowardl(long double, long double); double pow(double, double); float powf(float, float); long double powl(long double, long double); double remainder(double, double); float remainderf(float, float); long double remainderl(long double, long double); double remquo(double, double, int *); float remquof(float, float, int *); long double remquol(long double, long double, int *); double rint(double); float rintf(float); long double rintl(long double); double round(double); float roundf(float); long double roundl(long double); double scalb(double, double); double scalbln(double, long); float scalblnf(float, long); long double scalblnl(long double, long); double scalbn(double, int); float scalbnf(float, int); long double scalbnl(long double, int); double sin(double); float sinf(float); double sinh(double); float sinhf(float); long double sinhl(long double); long double sinl(long double); double sqrt(double); float sqrtf(float); long double sqrtl(long double); double tan(double); float tanf(float); double tanh(double); float tanhf(float); long double tanhl(long double); long double tanl(long double); double tgamma(double); float tgammaf(float); long double tgammal(long double); double trunc(double); float truncf(float); long double truncl(long double); double y0(double); double y1(double); double yn(int, double); #endif /* for sparse */ openvswitch-2.5.9/include/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017426 xustar0030 mtime=1567801401.293680376 30 atime=1567801402.065686047 30 ctime=1567801424.597852069 openvswitch-2.5.9/include/automake.mk0000644000175000017500000000073013534540071021114 0ustar00jpettitjpettit00000000000000BUILT_SOURCES += include/odp-netlink.h include/odp-netlink.h: datapath/linux/compat/include/linux/openvswitch.h \ build-aux/extract-odp-netlink-h $(AM_V_GEN)sed -f $(srcdir)/build-aux/extract-odp-netlink-h < $< > $@ EXTRA_DIST += build-aux/extract-odp-netlink-h CLEANFILES += include/odp-netlink.h include include/openflow/automake.mk include include/openvswitch/automake.mk include include/sparse/automake.mk include include/windows/automake.mk openvswitch-2.5.9/include/PaxHeaders.82075/windows0000644000000000000000000000013213534540120016677 xustar0030 mtime=1567801424.605852128 30 atime=1567801425.625859648 30 ctime=1567801424.605852128 openvswitch-2.5.9/include/windows/0000755000175000017500000000000013534540120020442 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/unistd.h0000644000000000000000000000013213534540071020440 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.569851863 openvswitch-2.5.9/include/windows/unistd.h0000644000175000017500000000371413534540071022133 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _UNISTD_H #define _UNISTD_H 1 #define WIN32_LEAN_AND_MEAN #include #define fsync _commit /* Standard file descriptors. */ #define STDIN_FILENO 0 /* Standard input. */ #define STDOUT_FILENO 1 /* Standard output. */ #define STDERR_FILENO 2 /* Standard error output. */ #define _SC_UIO_MAXIOV 2 #define _XOPEN_IOV_MAX 16 #define _SC_PAGESIZE 0x1 #define _SC_NPROCESSORS_ONLN 0x2 #define _SC_PHYS_PAGES 0x4 __inline int GetNumLogicalProcessors(void) { SYSTEM_INFO info_temp; GetSystemInfo(&info_temp); long int n_cores = info_temp.dwNumberOfProcessors; return n_cores; } __inline long sysconf(int type) { long value = -1; long page_size = -1; SYSTEM_INFO sys_info; MEMORYSTATUSEX status; switch (type) { case _SC_NPROCESSORS_ONLN: value = GetNumLogicalProcessors(); break; case _SC_PAGESIZE: GetSystemInfo(&sys_info); value = sys_info.dwPageSize; break; case _SC_PHYS_PAGES: status.dwLength = sizeof(status); page_size = sysconf(_SC_PAGESIZE); if (GlobalMemoryStatusEx(&status) && page_size != -1) { value = status.ullTotalPhys / page_size; } break; default: break; } return value; } #endif /* unistd.h */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/strings.h0000644000000000000000000000013213534540071020623 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.557851775 openvswitch-2.5.9/include/windows/strings.h0000644000175000017500000000000013534540071022277 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/arpa0000644000000000000000000000013213534540120017622 xustar0030 mtime=1567801424.541851656 30 atime=1567801425.625859648 30 ctime=1567801424.541851656 openvswitch-2.5.9/include/windows/arpa/0000755000175000017500000000000013534540120021365 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/arpa/PaxHeaders.82075/inet.h0000644000000000000000000000013213534540071021014 xustar0030 mtime=1567801401.305680465 30 atime=1567801401.305680465 30 ctime=1567801424.541851656 openvswitch-2.5.9/include/windows/arpa/inet.h0000644000175000017500000000000013534540071022470 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/windefs.h0000644000000000000000000000013213534540071020571 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.569851863 openvswitch-2.5.9/include/windows/windefs.h0000644000175000017500000000225313534540071022261 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef WINDEFS_H #define WINDEFS_H 1 #include #include #include #include #include #include #include #pragma comment(lib, "advapi32") #undef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #define inline __inline #define __func__ __FUNCTION__ #define ssize_t SSIZE_T #define u_int8_t uint8_t #define u_int16_t uint16_t #define u_int32_t uint32_t #define u_int64_t uint64_t typedef int pid_t; char *strsep(char **stringp, const char *delim); #define srandom srand #define random rand #endif /* windefs.h */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/sys0000644000000000000000000000013213534540120017515 xustar0030 mtime=1567801424.569851863 30 atime=1567801425.625859648 30 ctime=1567801424.569851863 openvswitch-2.5.9/include/windows/sys/0000755000175000017500000000000013534540120021260 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/wait.h0000644000000000000000000000013213534540071020714 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.569851863 openvswitch-2.5.9/include/windows/sys/wait.h0000644000175000017500000000000013534540071022370 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/time.h0000644000000000000000000000013213534540071020706 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.565851833 openvswitch-2.5.9/include/windows/sys/time.h0000644000175000017500000000000013534540071022362 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/uio.h0000644000000000000000000000013213534540071020544 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.565851833 openvswitch-2.5.9/include/windows/sys/uio.h0000644000175000017500000000130113534540071022225 0ustar00jpettitjpettit00000000000000/* * Copyright 2014 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SYS_UIO_H #define __SYS_UIO_H 1 #include #endif /* sys/uio.h */ openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/epoll.h0000644000000000000000000000013213534540071021063 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.561851804 openvswitch-2.5.9/include/windows/sys/epoll.h0000644000175000017500000000146213534540071022554 0ustar00jpettitjpettit00000000000000/* * Copyright 2014 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SYS_EPOLL_H #define __SYS_EPOLL_H 1 #define EPOLLIN 0x00001 typedef union data { uint32_t u32; } data_t; struct epoll_event { uint32_t events; data_t data; }; #endif /* sys/epoll.h */ openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/un.h0000644000000000000000000000013213534540071020372 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.569851863 openvswitch-2.5.9/include/windows/sys/un.h0000644000175000017500000000000013534540071022046 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/socket.h0000644000000000000000000000013213534540071021240 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.561851804 openvswitch-2.5.9/include/windows/sys/socket.h0000644000175000017500000000131613534540071022727 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SYS_SOCKET_H #define __SYS_SOCKET_H 1 typedef unsigned short int sa_family_t; #endif /* sys/socket.h */ openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/resource.h0000644000000000000000000000013213534540071021577 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.561851804 openvswitch-2.5.9/include/windows/sys/resource.h0000644000175000017500000000341213534540071023265 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SYS_RESOURCE_H #define SYS_RESOURCE_H 1 struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims (soft page faults) */ long ru_majflt; /* page faults (hard page faults) */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* IPC messages sent */ long ru_msgrcv; /* IPC messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ }; #ifndef RUSAGE_SELF #define RUSAGE_SELF 1 #endif #ifndef RUSAGE_CHILDREN #define RUSAGE_CHILDREN 2 #endif #ifndef RUSAGE_THREAD #define RUSAGE_THREAD 3 #endif #endif /* sys/resource.h */ openvswitch-2.5.9/include/windows/sys/PaxHeaders.82075/ioctl.h0000644000000000000000000000013213534540071021062 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.561851804 openvswitch-2.5.9/include/windows/sys/ioctl.h0000644000175000017500000000000013534540071022536 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/getopt.h0000644000000000000000000000013213534540071020434 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.541851656 openvswitch-2.5.9/include/windows/getopt.h0000644000175000017500000000425513534540071022130 0ustar00jpettitjpettit00000000000000/*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _GETOPT_H_ #define _GETOPT_H_ #include extern char *optarg; extern int optind; /* * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 struct option { /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; }; int getopt_long(int, char * const *, const char *, const struct option *, int *); #endif /* !_GETOPT_H_ */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/net0000644000000000000000000000013213534540120017465 xustar0030 mtime=1567801424.545851686 30 atime=1567801425.625859648 30 ctime=1567801424.545851686 openvswitch-2.5.9/include/windows/net/0000755000175000017500000000000013534540120021230 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/net/PaxHeaders.82075/if.h0000644000000000000000000000013213534540071020316 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.545851686 openvswitch-2.5.9/include/windows/net/if.h0000644000175000017500000000334113534540071022005 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NET_IF_H #define __NET_IF_H 1 #include #define IFNAMSIZ IF_NAMESIZE enum { IFLA_UNSPEC, IFLA_ADDRESS, IFLA_BROADCAST, IFLA_IFNAME, IFLA_MTU, IFLA_LINK, IFLA_QDISC, IFLA_STATS, IFLA_COST, #define IFLA_COST IFLA_COST IFLA_PRIORITY, #define IFLA_PRIORITY IFLA_PRIORITY IFLA_MASTER, #define IFLA_MASTER IFLA_MASTER IFLA_WIRELESS, #define IFLA_WIRELESS IFLA_WIRELESS IFLA_PROTINFO, #define IFLA_PROTINFO IFLA_PROTINFO IFLA_TXQLEN, #define IFLA_TXQLEN IFLA_TXQLEN IFLA_MAP, #define IFLA_MAP IFLA_MAP IFLA_WEIGHT, #define IFLA_WEIGHT IFLA_WEIGHT IFLA_OPERSTATE, IFLA_LINKMODE, IFLA_LINKINFO, #define IFLA_LINKINFO IFLA_LINKINFO IFLA_NET_NS_PID, IFLA_IFALIAS, IFLA_NUM_VF, IFLA_VFINFO_LIST, IFLA_STATS64, IFLA_VF_PORTS, IFLA_PORT_SELF, IFLA_AF_SPEC, IFLA_GROUP, IFLA_NET_NS_FD, IFLA_EXT_MASK, IFLA_PROMISCUITY, #define IFLA_PROMISCUITY IFLA_PROMISCUITY IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, IFLA_CARRIER, IFLA_PHYS_PORT_ID, __IFLA_MAX }; #define IFLA_MAX (__IFLA_MAX - 1) #endif /* net/if.h */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/syslog.h0000644000000000000000000000013213534540071020452 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.557851775 openvswitch-2.5.9/include/windows/syslog.h0000644000175000017500000000474113534540071022146 0ustar00jpettitjpettit00000000000000/* * Copyright 2013, 2015 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License.You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the * License for the specific language governing permissions and limitations * under the License. */ #ifndef SYSLOG_H #define SYSLOG_H 1 #define LOG_EMERG 0 /* system is unusable */ #define LOG_ALERT 1 /* action must be taken immediately */ #define LOG_CRIT 2 /* critical conditions */ #define LOG_ERR 3 /* error conditions */ #define LOG_WARNING 4 /* warning conditions */ #define LOG_NOTICE 5 /* normal but significant condition */ #define LOG_INFO 6 /* informational */ #define LOG_DEBUG 7 /* debug-level messages */ #define LOG_NDELAY 8 /* don't delay open */ #define LOG_KERN (0<<3) /* kernel messages */ #define LOG_USER (1<<3) /* user-level messages */ #define LOG_MAIL (2<<3) /* mail system */ #define LOG_DAEMON (3<<3) /* system daemons */ #define LOG_AUTH (4<<3) /* security/authorization messages */ #define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ #define LOG_LPR (6<<3) /* line printer subsystem */ #define LOG_NEWS (7<<3) /* network news subsystem */ #define LOG_UUCP (8<<3) /* UUCP subsystem */ #define LOG_CRON (9<<3) /* clock daemon */ #define LOG_AUTHPRIV (10<<3) /* security/authorization messages */ #define LOG_FTP (11<<3) /* FTP daemon */ #define LOG_LOCAL0 (16<<3) /* reserved for local use */ #define LOG_LOCAL1 (17<<3) /* reserved for local use */ #define LOG_LOCAL2 (18<<3) /* reserved for local use */ #define LOG_LOCAL3 (19<<3) /* reserved for local use */ #define LOG_LOCAL4 (20<<3) /* reserved for local use */ #define LOG_LOCAL5 (21<<3) /* reserved for local use */ #define LOG_LOCAL6 (22<<3) /* reserved for local use */ #define LOG_LOCAL7 (23<<3) /* reserved for local use */ static inline void openlog(const char *ident, int option, int facility) { } static inline void syslog(int priority, const char *format, ...) { } #endif /* syslog.h */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/netpacket0000644000000000000000000000013213534540120020655 xustar0030 mtime=1567801424.549851716 30 atime=1567801425.625859648 30 ctime=1567801424.549851716 openvswitch-2.5.9/include/windows/netpacket/0000755000175000017500000000000013534540120022420 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/netpacket/PaxHeaders.82075/packet.h0000644000000000000000000000013213534540071022357 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.549851716 openvswitch-2.5.9/include/windows/netpacket/packet.h0000644000175000017500000000167713534540071024060 0ustar00jpettitjpettit00000000000000/* * Copyright 2014 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETPACKET_PACKET_H #define __NETPACKET_PACKET_H 1 struct iovec { void *iov_base; unsigned int iov_len; }; struct msghdr { void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov; size_t msg_iovlen; void *msg_control; size_t msg_controllen; int msg_flags; }; #endif /* netpacket/packet.h */ openvswitch-2.5.9/include/windows/PaxHeaders.82075/netinet0000644000000000000000000000013213534540120020345 xustar0030 mtime=1567801424.553851745 30 atime=1567801425.625859648 30 ctime=1567801424.553851745 openvswitch-2.5.9/include/windows/netinet/0000755000175000017500000000000013534540120022110 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/in.h0000644000000000000000000000013213534540071021206 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.549851716 openvswitch-2.5.9/include/windows/netinet/in.h0000644000175000017500000000132313534540071022673 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETINET_IN_H #define __NETINET_IN_H 1 #define IPPROTO_GRE 47 #define IPPORT_FTP 21 #endif /* netinet/in.h */ openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/icmp6.h0000644000000000000000000000013213534540071021616 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.549851716 openvswitch-2.5.9/include/windows/netinet/icmp6.h0000644000175000017500000005201613534540071023310 0ustar00jpettitjpettit00000000000000/* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET_ICMP6_H_ #define _NETINET_ICMP6_H_ #include "byte-order.h" #define ICMPV6_PLD_MAXLEN 1232 /* IPV6_MMTU - sizeof(struct ip6_hdr) - sizeof(struct icmp6_hdr) */ struct icmp6_hdr { u_int8_t icmp6_type; /* type field */ u_int8_t icmp6_code; /* code field */ u_int16_t icmp6_cksum; /* checksum field */ union { u_int32_t icmp6_un_data32[1]; /* type-specific field */ u_int16_t icmp6_un_data16[2]; /* type-specific field */ u_int8_t icmp6_un_data8[4]; /* type-specific field */ } icmp6_dataun; }; #define icmp6_data32 icmp6_dataun.icmp6_un_data32 #define icmp6_data16 icmp6_dataun.icmp6_un_data16 #define icmp6_data8 icmp6_dataun.icmp6_un_data8 #define icmp6_pptr icmp6_data32[0] /* parameter prob */ #define icmp6_mtu icmp6_data32[0] /* packet too big */ #define icmp6_id icmp6_data16[0] /* echo request/reply */ #define icmp6_seq icmp6_data16[1] /* echo request/reply */ #define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */ #define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */ #define ICMP6_PACKET_TOO_BIG 2 /* packet too big */ #define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */ #define ICMP6_PARAM_PROB 4 /* ip6 header bad */ #define ICMP6_ECHO_REQUEST 128 /* echo service */ #define ICMP6_ECHO_REPLY 129 /* echo reply */ #define MLD_LISTENER_QUERY 130 /* multicast listener query */ #define MLD_LISTENER_REPORT 131 /* multicast listener report */ #define MLD_LISTENER_DONE 132 /* multicast listener done */ #define MLD_LISTENER_REDUCTION MLD_LISTENER_DONE /* RFC3542 definition */ /* RFC2292 decls */ #define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */ #define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */ #define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */ /* the followings are for backward compatibility to old KAME apps. */ #define MLD6_LISTENER_QUERY MLD_LISTENER_QUERY #define MLD6_LISTENER_REPORT MLD_LISTENER_REPORT #define MLD6_LISTENER_DONE MLD_LISTENER_DONE #define ND_ROUTER_SOLICIT 133 /* router solicitation */ #define ND_ROUTER_ADVERT 134 /* router advertisement */ #define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ #define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisement */ #define ND_REDIRECT 137 /* redirect */ #define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */ #define ICMP6_WRUREQUEST 139 /* who are you request */ #define ICMP6_WRUREPLY 140 /* who are you reply */ #define ICMP6_FQDN_QUERY 139 /* FQDN query */ #define ICMP6_FQDN_REPLY 140 /* FQDN reply */ #define ICMP6_NI_QUERY 139 /* node information request */ #define ICMP6_NI_REPLY 140 /* node information reply */ #define MLDV2_LISTENER_REPORT 143 /* RFC3810 listener report */ /* The definitions below are experimental. TBA */ #define MLD_MTRACE_RESP 200 /* mtrace response(to sender) */ #define MLD_MTRACE 201 /* mtrace messages */ /* the followings are for backward compatibility to old KAME apps. */ #define MLD6_MTRACE_RESP MLD_MTRACE_RESP #define MLD6_MTRACE MLD_MTRACE #define ICMP6_MAXTYPE 201 #define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */ #define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */ #define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */ #define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */ #define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */ #define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */ #define ICMP6_DST_UNREACH_POLICY 5 /* source address failed ingress/egress policy */ #define ICMP6_DST_UNREACH_REJROUTE 6 /* reject route to destination */ #define ICMP6_DST_UNREACH_SOURCERT 7 /* error in source routing header */ #define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */ #define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */ #define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */ #define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */ #define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */ #define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */ #define ICMP6_NI_SUBJ_IPV6 0 /* Query Subject is an IPv6 address */ #define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */ #define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an IPv4 address */ #define ICMP6_NI_SUCCESS 0 /* node information successful reply */ #define ICMP6_NI_REFUSED 1 /* node information request is refused */ #define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */ #define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */ #define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */ #define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */ /* Used in kernel only */ #define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */ #define ND_REDIRECT_ROUTER 1 /* redirect to a better router */ /* * Multicast Listener Discovery */ struct mld_hdr { struct icmp6_hdr mld_icmp6_hdr; struct in6_addr mld_addr; /* multicast address */ }; /* definitions to provide backward compatibility to old KAME applications */ #define mld6_hdr mld_hdr #define mld6_type mld_type #define mld6_code mld_code #define mld6_cksum mld_cksum #define mld6_maxdelay mld_maxdelay #define mld6_reserved mld_reserved #define mld6_addr mld_addr /* shortcut macro definitions */ #define mld_type mld_icmp6_hdr.icmp6_type #define mld_code mld_icmp6_hdr.icmp6_code #define mld_cksum mld_icmp6_hdr.icmp6_cksum #define mld_maxdelay mld_icmp6_hdr.icmp6_data16[0] #define mld_reserved mld_icmp6_hdr.icmp6_data16[1] #define MLD_MINLEN 24 /* * Neighbor Discovery */ struct nd_router_solicit { /* router solicitation */ struct icmp6_hdr nd_rs_hdr; /* could be followed by options */ }; #define nd_rs_type nd_rs_hdr.icmp6_type #define nd_rs_code nd_rs_hdr.icmp6_code #define nd_rs_cksum nd_rs_hdr.icmp6_cksum #define nd_rs_reserved nd_rs_hdr.icmp6_data32[0] struct nd_router_advert { /* router advertisement */ struct icmp6_hdr nd_ra_hdr; u_int32_t nd_ra_reachable; /* reachable time */ u_int32_t nd_ra_retransmit; /* retransmit timer */ /* could be followed by options */ }; #define nd_ra_type nd_ra_hdr.icmp6_type #define nd_ra_code nd_ra_hdr.icmp6_code #define nd_ra_cksum nd_ra_hdr.icmp6_cksum #define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0] #define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1] #define ND_RA_FLAG_MANAGED 0x80 #define ND_RA_FLAG_OTHER 0x40 #define ND_RA_FLAG_HOME_AGENT 0x20 /* * Router preference values based on RFC4191. */ #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_HIGH 0x08 /* 00001000 */ #define ND_RA_FLAG_RTPREF_MEDIUM 0x00 /* 00000000 */ #define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */ #define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ #define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1] struct nd_neighbor_solicit { /* neighbor solicitation */ struct icmp6_hdr nd_ns_hdr; struct in6_addr nd_ns_target; /*target address */ /* could be followed by options */ }; #define nd_ns_type nd_ns_hdr.icmp6_type #define nd_ns_code nd_ns_hdr.icmp6_code #define nd_ns_cksum nd_ns_hdr.icmp6_cksum #define nd_ns_reserved nd_ns_hdr.icmp6_data32[0] struct nd_neighbor_advert { /* neighbor advertisement */ struct icmp6_hdr nd_na_hdr; struct in6_addr nd_na_target; /* target address */ /* could be followed by options */ }; #define nd_na_type nd_na_hdr.icmp6_type #define nd_na_code nd_na_hdr.icmp6_code #define nd_na_cksum nd_na_hdr.icmp6_cksum #define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0] #define ND_NA_FLAG_ROUTER CONSTANT_HTONL(0x80000000) #define ND_NA_FLAG_SOLICITED CONSTANT_HTONL(0x40000000) #define ND_NA_FLAG_OVERRIDE CONSTANT_HTONL(0x20000000) struct nd_redirect { /* redirect */ struct icmp6_hdr nd_rd_hdr; struct in6_addr nd_rd_target; /* target address */ struct in6_addr nd_rd_dst; /* destination address */ /* could be followed by options */ }; #define nd_rd_type nd_rd_hdr.icmp6_type #define nd_rd_code nd_rd_hdr.icmp6_code #define nd_rd_cksum nd_rd_hdr.icmp6_cksum #define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] struct nd_opt_hdr { /* Neighbor discovery option header */ u_int8_t nd_opt_type; u_int8_t nd_opt_len; /* followed by option specific data*/ }; #define ND_OPT_SOURCE_LINKADDR 1 #define ND_OPT_TARGET_LINKADDR 2 #define ND_OPT_PREFIX_INFORMATION 3 #define ND_OPT_REDIRECTED_HEADER 4 #define ND_OPT_MTU 5 #define ND_OPT_ADVINTERVAL 7 #define ND_OPT_HOMEAGENT_INFO 8 #define ND_OPT_SOURCE_ADDRLIST 9 #define ND_OPT_TARGET_ADDRLIST 10 #define ND_OPT_MAP 23 /* RFC 5380 */ #define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */ #define ND_OPT_RDNSS 25 /* RFC 6016 */ #define ND_OPT_DNSSL 31 /* RFC 6016 */ struct nd_opt_route_info { /* route info */ u_int8_t nd_opt_rti_type; u_int8_t nd_opt_rti_len; u_int8_t nd_opt_rti_prefixlen; u_int8_t nd_opt_rti_flags; u_int32_t nd_opt_rti_lifetime; /* prefix follows */ }; struct nd_opt_prefix_info { /* prefix information */ u_int8_t nd_opt_pi_type; u_int8_t nd_opt_pi_len; u_int8_t nd_opt_pi_prefix_len; u_int8_t nd_opt_pi_flags_reserved; u_int32_t nd_opt_pi_valid_time; u_int32_t nd_opt_pi_preferred_time; u_int32_t nd_opt_pi_reserved2; struct in6_addr nd_opt_pi_prefix; }; #define ND_OPT_PI_FLAG_ONLINK 0x80 #define ND_OPT_PI_FLAG_AUTO 0x40 struct nd_opt_rd_hdr { /* redirected header */ u_int8_t nd_opt_rh_type; u_int8_t nd_opt_rh_len; u_int16_t nd_opt_rh_reserved1; u_int32_t nd_opt_rh_reserved2; /* followed by IP header and data */ }; struct nd_opt_mtu { /* MTU option */ u_int8_t nd_opt_mtu_type; u_int8_t nd_opt_mtu_len; u_int16_t nd_opt_mtu_reserved; u_int32_t nd_opt_mtu_mtu; }; struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ u_int8_t nd_opt_rdnss_type; u_int8_t nd_opt_rdnss_len; u_int16_t nd_opt_rdnss_reserved; u_int32_t nd_opt_rdnss_lifetime; /* followed by list of IP prefixes */ }; struct nd_opt_dnssl { /* DNSSL option RFC 6106 */ u_int8_t nd_opt_dnssl_type; u_int8_t nd_opt_dnssl_len; u_int16_t nd_opt_dnssl_reserved; u_int32_t nd_opt_dnssl_lifetime; /* followed by list of IP prefixes */ }; /* * icmp6 namelookup */ struct icmp6_namelookup { struct icmp6_hdr icmp6_nl_hdr; u_int8_t icmp6_nl_nonce[8]; int32_t icmp6_nl_ttl; #if 0 u_int8_t icmp6_nl_len; u_int8_t icmp6_nl_name[3]; #endif /* could be followed by options */ }; /* * icmp6 node information */ struct icmp6_nodeinfo { struct icmp6_hdr icmp6_ni_hdr; u_int8_t icmp6_ni_nonce[8]; /* could be followed by reply data */ }; #define ni_type icmp6_ni_hdr.icmp6_type #define ni_code icmp6_ni_hdr.icmp6_code #define ni_cksum icmp6_ni_hdr.icmp6_cksum #define ni_qtype icmp6_ni_hdr.icmp6_data16[0] #define ni_flags icmp6_ni_hdr.icmp6_data16[1] #define NI_QTYPE_NOOP 0 /* NOOP */ #define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes */ #define NI_QTYPE_FQDN 2 /* FQDN (draft 04) */ #define NI_QTYPE_DNSNAME 2 /* DNS Name */ #define NI_QTYPE_NODEADDR 3 /* Node Addresses */ #define NI_QTYPE_IPV4ADDR 4 /* IPv4 Addresses */ #define NI_SUPTYPE_FLAG_COMPRESS CONSTANT_HTONS(0x1) #define NI_FQDN_FLAG_VALIDTTL CONSTANT_HTONS(0x1) #ifdef NAME_LOOKUPS_04 #define NI_NODEADDR_FLAG_LINKLOCAL CONSTANT_HTONS(0x1) #define NI_NODEADDR_FLAG_SITELOCAL CONSTANT_HTONS(0x2) #define NI_NODEADDR_FLAG_GLOBAL CONSTANT_HTONS(0x4) #define NI_NODEADDR_FLAG_ALL CONSTANT_HTONS(0x8) #define NI_NODEADDR_FLAG_TRUNCATE CONSTANT_HTONS(0x10) #define NI_NODEADDR_FLAG_ANYCAST CONSTANT_HTONS(0x20) /* just experimental. not in spec */ #else /* draft-ietf-ipngwg-icmp-name-lookups-05 (and later?) */ #define NI_NODEADDR_FLAG_TRUNCATE CONSTANT_HTONS(0x1) #define NI_NODEADDR_FLAG_ALL CONSTANT_HTONS(0x2) #define NI_NODEADDR_FLAG_COMPAT CONSTANT_HTONS(0x4) #define NI_NODEADDR_FLAG_LINKLOCAL CONSTANT_HTONS(0x8) #define NI_NODEADDR_FLAG_SITELOCAL CONSTANT_HTONS(0x10) #define NI_NODEADDR_FLAG_GLOBAL CONSTANT_HTONS(0x20) #define NI_NODEADDR_FLAG_ANYCAST CONSTANT_HTONS(0x40) /* just experimental. not in spec */ #endif struct ni_reply_fqdn { u_int32_t ni_fqdn_ttl; /* TTL */ u_int8_t ni_fqdn_namelen; /* length in octets of the FQDN */ u_int8_t ni_fqdn_name[3]; /* XXX: alignment */ }; /* * Router Renumbering. as router-renum-08.txt */ struct icmp6_router_renum { /* router renumbering header */ struct icmp6_hdr rr_hdr; u_int8_t rr_segnum; u_int8_t rr_flags; u_int16_t rr_maxdelay; u_int32_t rr_reserved; }; #define ICMP6_RR_FLAGS_TEST 0x80 #define ICMP6_RR_FLAGS_REQRESULT 0x40 #define ICMP6_RR_FLAGS_FORCEAPPLY 0x20 #define ICMP6_RR_FLAGS_SPECSITE 0x10 #define ICMP6_RR_FLAGS_PREVDONE 0x08 #define rr_type rr_hdr.icmp6_type #define rr_code rr_hdr.icmp6_code #define rr_cksum rr_hdr.icmp6_cksum #define rr_seqnum rr_hdr.icmp6_data32[0] struct rr_pco_match { /* match prefix part */ u_int8_t rpm_code; u_int8_t rpm_len; u_int8_t rpm_ordinal; u_int8_t rpm_matchlen; u_int8_t rpm_minlen; u_int8_t rpm_maxlen; u_int16_t rpm_reserved; struct in6_addr rpm_prefix; }; #define RPM_PCO_ADD 1 #define RPM_PCO_CHANGE 2 #define RPM_PCO_SETGLOBAL 3 #define RPM_PCO_MAX 4 struct rr_pco_use { /* use prefix part */ u_int8_t rpu_uselen; u_int8_t rpu_keeplen; u_int8_t rpu_ramask; u_int8_t rpu_raflags; u_int32_t rpu_vltime; u_int32_t rpu_pltime; u_int32_t rpu_flags; struct in6_addr rpu_prefix; }; #define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80 #define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40 #define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME CONSTANT_HTONL(0x80000000) #define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME CONSTANT_HTONL(0x40000000) struct rr_result { /* router renumbering result message */ u_int16_t rrr_flags; u_int8_t rrr_ordinal; u_int8_t rrr_matchedlen; u_int32_t rrr_ifid; struct in6_addr rrr_prefix; }; #define ICMP6_RR_RESULT_FLAGS_OOB CONSTANT_HTONS(0x0002) #define ICMP6_RR_RESULT_FLAGS_FORBIDDEN CONSTANT_HTONS(0x0001) /* * icmp6 filter structures. */ struct icmp6_filter { u_int32_t icmp6_filt[8]; }; #define ICMP6_FILTER_SETPASSALL(filterp) \ (void)memset(filterp, 0xff, sizeof(struct icmp6_filter)) #define ICMP6_FILTER_SETBLOCKALL(filterp) \ (void)memset(filterp, 0x00, sizeof(struct icmp6_filter)) #define ICMP6_FILTER_SETPASS(type, filterp) \ (((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))) #define ICMP6_FILTER_SETBLOCK(type, filterp) \ (((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))) #define ICMP6_FILTER_WILLPASS(type, filterp) \ ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0) #define ICMP6_FILTER_WILLBLOCK(type, filterp) \ ((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0) /* * Variables related to this implementation * of the internet control message protocol version 6. */ /* * IPv6 ICMP statistics. * Each counter is an unsigned 64-bit value. */ #define ICMP6_STAT_ERROR 0 /* # of calls to icmp6_error */ #define ICMP6_STAT_CANTERROR 1 /* no error (old was icmp) */ #define ICMP6_STAT_TOOFREQ 2 /* no error (rate limitation) */ #define ICMP6_STAT_OUTHIST 3 /* # of output messages */ /* space for 256 counters */ #define ICMP6_STAT_BADCODE 259 /* icmp6_code out of range */ #define ICMP6_STAT_TOOSHORT 260 /* packet < sizeof(struct icmp6_hdr) */ #define ICMP6_STAT_CHECKSUM 261 /* bad checksum */ #define ICMP6_STAT_BADLEN 262 /* calculated bound mismatch */ /* * number of responses; this member is inherited from the netinet code, * but for netinet6 code, it is already available in outhist[]. */ #define ICMP6_STAT_REFLECT 263 #define ICMP6_STAT_INHIST 264 /* # of input messages */ /* space for 256 counters */ #define ICMP6_STAT_ND_TOOMANYOPT 520 /* too many ND options */ #define ICMP6_STAT_OUTERRHIST 521 /* space for 13 counters */ #define ICMP6_STAT_PMTUCHG 534 /* path MTU changes */ #define ICMP6_STAT_ND_BADOPT 535 /* bad ND options */ #define ICMP6_STAT_BADNS 536 /* bad neighbor solicititation */ #define ICMP6_STAT_BADNA 537 /* bad neighbor advertisement */ #define ICMP6_STAT_BADRS 538 /* bad router solicitiation */ #define ICMP6_STAT_BADRA 539 /* bad router advertisement */ #define ICMP6_STAT_BADREDIRECT 540 /* bad redirect message */ #define ICMP6_STAT_DROPPED_RAROUTE 541 /* discarded routes from router advertisement */ #define ICMP6_NSTATS 542 #define ICMP6_ERRSTAT_DST_UNREACH_NOROUTE 0 #define ICMP6_ERRSTAT_DST_UNREACH_ADMIN 1 #define ICMP6_ERRSTAT_DST_UNREACH_BEYONDSCOPE 2 #define ICMP6_ERRSTAT_DST_UNREACH_ADDR 3 #define ICMP6_ERRSTAT_DST_UNREACH_NOPORT 4 #define ICMP6_ERRSTAT_PACKET_TOO_BIG 5 #define ICMP6_ERRSTAT_TIME_EXCEED_TRANSIT 6 #define ICMP6_ERRSTAT_TIME_EXCEED_REASSEMBLY 7 #define ICMP6_ERRSTAT_PARAMPROB_HEADER 8 #define ICMP6_ERRSTAT_PARAMPROB_NEXTHEADER 9 #define ICMP6_ERRSTAT_PARAMPROB_OPTION 10 #define ICMP6_ERRSTAT_REDIRECT 11 #define ICMP6_ERRSTAT_UNKNOWN 12 /* * Names for ICMP sysctl objects */ #define ICMPV6CTL_STATS 1 #define ICMPV6CTL_REDIRACCEPT 2 /* accept/process redirects */ #define ICMPV6CTL_REDIRTIMEOUT 3 /* redirect cache time */ #if 0 /*obsoleted*/ #define ICMPV6CTL_ERRRATELIMIT 5 /* ICMPv6 error rate limitation */ #endif #define ICMPV6CTL_ND6_PRUNE 6 #define ICMPV6CTL_ND6_DELAY 8 #define ICMPV6CTL_ND6_UMAXTRIES 9 #define ICMPV6CTL_ND6_MMAXTRIES 10 #define ICMPV6CTL_ND6_USELOOPBACK 11 /*#define ICMPV6CTL_ND6_PROXYALL 12 obsoleted, do not reuse here */ #define ICMPV6CTL_NODEINFO 13 #define ICMPV6CTL_ERRPPSLIMIT 14 /* ICMPv6 error pps limitation */ #define ICMPV6CTL_ND6_MAXNUDHINT 15 #define ICMPV6CTL_MTUDISC_HIWAT 16 #define ICMPV6CTL_MTUDISC_LOWAT 17 #define ICMPV6CTL_ND6_DEBUG 18 #define ICMPV6CTL_ND6_DRLIST 19 #define ICMPV6CTL_ND6_PRLIST 20 #define ICMPV6CTL_ND6_MAXQLEN 24 #define ICMPV6CTL_MAXID 25 #define ICMPV6CTL_NAMES { \ { 0, 0 }, \ { 0, 0 }, \ { "rediraccept", CTLTYPE_INT }, \ { "redirtimeout", CTLTYPE_INT }, \ { 0, 0 }, \ { 0, 0 }, \ { "nd6_prune", CTLTYPE_INT }, \ { 0, 0 }, \ { "nd6_delay", CTLTYPE_INT }, \ { "nd6_umaxtries", CTLTYPE_INT }, \ { "nd6_mmaxtries", CTLTYPE_INT }, \ { "nd6_useloopback", CTLTYPE_INT }, \ { 0, 0 }, \ { "nodeinfo", CTLTYPE_INT }, \ { "errppslimit", CTLTYPE_INT }, \ { "nd6_maxnudhint", CTLTYPE_INT }, \ { "mtudisc_hiwat", CTLTYPE_INT }, \ { "mtudisc_lowat", CTLTYPE_INT }, \ { "nd6_debug", CTLTYPE_INT }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { 0, 0 }, \ { "nd6_maxqueuelen", CTLTYPE_INT }, \ } #endif /* !_NETINET_ICMP6_H_ */ openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/ip6.h0000644000000000000000000000013213534540071021276 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.553851745 openvswitch-2.5.9/include/windows/netinet/ip6.h0000644000175000017500000002015313534540071022765 0ustar00jpettitjpettit00000000000000/* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET_IP6_H_ #define _NETINET_IP6_H_ #include #include #include "byte-order.h" /* * Definition for internet protocol version 6. * RFC 2460 */ struct ip6_hdr { union { struct ip6_hdrctl { u_int32_t ip6_un1_flow; /* 20 bits of flow-ID */ u_int16_t ip6_un1_plen; /* payload length */ u_int8_t ip6_un1_nxt; /* next header */ u_int8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; u_int8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ }; #define ip6_vfc ip6_ctlun.ip6_un2_vfc #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen #define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt #define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim #define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim #define IPV6_VERSION 0x60 #define IPV6_VERSION_MASK 0xf0 #define IPV6_FLOWINFO_MASK CONSTANT_HTONL(0x0fffffff) /* flow info (28 bits) */ #define IPV6_FLOWLABEL_MASK CONSTANT_HTONL(0x000fffff) /* flow label (20 bits) */ #if 1 /* ECN bits proposed by Sally Floyd */ #define IP6TOS_CE 0x01 /* congestion experienced */ #define IP6TOS_ECT 0x02 /* ECN-capable transport */ #endif /* * Extension Headers */ struct ip6_ext { u_int8_t ip6e_nxt; u_int8_t ip6e_len; }; /* Hop-by-Hop options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ struct ip6_hbh { u_int8_t ip6h_nxt; /* next header */ u_int8_t ip6h_len; /* length in units of 8 octets */ /* followed by options */ }; /* Destination options header */ /* XXX should we pad it to force alignment on an 8-byte boundary? */ struct ip6_dest { u_int8_t ip6d_nxt; /* next header */ u_int8_t ip6d_len; /* length in units of 8 octets */ /* followed by options */ }; /* Option types and related macros */ #define IP6OPT_PAD1 0x00 /* 00 0 00000 */ #define IP6OPT_PADN 0x01 /* 00 0 00001 */ #define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ #define IP6OPT_NSAP_ADDR 0xC3 /* 11 0 00011 */ #define IP6OPT_TUNNEL_LIMIT 0x04 /* 00 0 00100 */ #define IP6OPT_RTALERT 0x05 /* 00 0 00101 (KAME definition) */ #define IP6OPT_ROUTER_ALERT 0x05 /* (RFC3542 def, recommended) */ #define IP6OPT_RTALERT_LEN 4 #define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ #define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ #define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ #define IP6OPT_MINLEN 2 #define IP6OPT_TYPE(o) ((o) & 0xC0) #define IP6OPT_TYPE_SKIP 0x00 #define IP6OPT_TYPE_DISCARD 0x40 #define IP6OPT_TYPE_FORCEICMP 0x80 #define IP6OPT_TYPE_ICMP 0xC0 #define IP6OPT_MUTABLE 0x20 /* IPv6 options: common part */ struct ip6_opt { u_int8_t ip6o_type; u_int8_t ip6o_len; }; /* Jumbo Payload Option */ struct ip6_opt_jumbo { u_int8_t ip6oj_type; u_int8_t ip6oj_len; u_int8_t ip6oj_jumbo_len[4]; }; #define IP6OPT_JUMBO_LEN 6 /* NSAP Address Option */ struct ip6_opt_nsap { u_int8_t ip6on_type; u_int8_t ip6on_len; u_int8_t ip6on_src_nsap_len; u_int8_t ip6on_dst_nsap_len; /* followed by source NSAP */ /* followed by destination NSAP */ }; /* Tunnel Limit Option */ struct ip6_opt_tunnel { u_int8_t ip6ot_type; u_int8_t ip6ot_len; u_int8_t ip6ot_encap_limit; }; /* Router Alert Option */ struct ip6_opt_router { u_int8_t ip6or_type; u_int8_t ip6or_len; u_int8_t ip6or_value[2]; }; /* Router alert values (in network byte order) */ #define IP6_ALERT_MLD CONSTANT_HTONS(0x0000) #define IP6_ALERT_RSVP CONSTANT_HTONS(0x0001) #define IP6_ALERT_AN CONSTANT_HTONS(0x0002) /* Routing header */ struct ip6_rthdr { u_int8_t ip6r_nxt; /* next header */ u_int8_t ip6r_len; /* length in units of 8 octets */ u_int8_t ip6r_type; /* routing type */ u_int8_t ip6r_segleft; /* segments left */ /* followed by routing type specific data */ }; /* Type 0 Routing header */ struct ip6_rthdr0 { u_int8_t ip6r0_nxt; /* next header */ u_int8_t ip6r0_len; /* length in units of 8 octets */ u_int8_t ip6r0_type; /* always zero */ u_int8_t ip6r0_segleft; /* segments left */ u_int32_t ip6r0_reserved; /* reserved field */ }; /* Fragment header */ struct ip6_frag { u_int8_t ip6f_nxt; /* next header */ u_int8_t ip6f_reserved; /* reserved field */ u_int16_t ip6f_offlg; /* offset, reserved, and flag */ u_int32_t ip6f_ident; /* identification */ }; #define IP6F_OFF_MASK CONSTANT_HTONS(0xfff8) /* mask out offset from _offlg */ #define IP6F_RESERVED_MASK CONSTANT_HTONS(0x0006) /* reserved bits in ip6f_offlg */ #define IP6F_MORE_FRAG CONSTANT_HTONS(0x0001) /* more-fragments flag */ /* * Internet implementation parameters. */ #define IPV6_MAXHLIM 255 /* maximum hoplimit */ #define IPV6_DEFHLIM 64 /* default hlim */ #define IPV6_FRAGTTL 120 /* ttl for fragment packets, in slowtimo tick */ #define IPV6_HLIMDEC 1 /* subtracted when forwarding */ #define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ #define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/ #endif /* !_NETINET_IP6_H_ */ openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/tcp.h0000644000000000000000000000013213534540071021366 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.557851775 openvswitch-2.5.9/include/windows/netinet/tcp.h0000644000175000017500000000000013534540071023042 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/ip.h0000644000000000000000000000013213534540071021210 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.553851745 openvswitch-2.5.9/include/windows/netinet/ip.h0000644000175000017500000000153413534540071022701 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETINET_IP_H #define __NETINET_IP_H 1 #define IPTOS_PREC_INTERNETCONTROL 0xc0 #define MAXTTL 255 #define IPTOS_LOWDELAY 0x10 #define IPTOS_THROUGHPUT 0x08 #define IP_DF 0x4000 /* dont fragment flag */ #endif /* netinet/ip.h */ openvswitch-2.5.9/include/windows/netinet/PaxHeaders.82075/in_systm.h0000644000000000000000000000013213534540071022445 xustar0030 mtime=1567801401.305680465 30 atime=1567801401.305680465 30 ctime=1567801424.553851745 openvswitch-2.5.9/include/windows/netinet/in_systm.h0000644000175000017500000000000013534540071024121 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071021120 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.605852128 openvswitch-2.5.9/include/windows/automake.mk0000644000175000017500000000215513534540071022611 0ustar00jpettitjpettit00000000000000# Copyright (C) 2013 Nicira, Inc. # # Copying and distribution of this file, with or without modification # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. noinst_HEADERS += \ include/windows/arpa/inet.h \ include/windows/dirent.h \ include/windows/getopt.h \ include/windows/linux/pkt_sched.h \ include/windows/linux/types.h \ include/windows/net/if.h \ include/windows/netdb.h \ include/windows/netpacket/packet.h \ include/windows/netinet/icmp6.h \ include/windows/netinet/in.h \ include/windows/netinet/in_systm.h \ include/windows/netinet/ip.h \ include/windows/netinet/ip6.h \ include/windows/netinet/tcp.h \ include/windows/poll.h \ include/windows/strings.h \ include/windows/syslog.h \ include/windows/sys/epoll.h \ include/windows/sys/ioctl.h \ include/windows/sys/resource.h \ include/windows/sys/socket.h \ include/windows/sys/time.h \ include/windows/sys/uio.h \ include/windows/sys/un.h \ include/windows/sys/wait.h \ include/windows/unistd.h \ include/windows/windefs.h openvswitch-2.5.9/include/windows/PaxHeaders.82075/netdb.h0000644000000000000000000000013213534540071020226 xustar0030 mtime=1567801401.305680465 30 atime=1567801401.305680465 30 ctime=1567801424.545851686 openvswitch-2.5.9/include/windows/netdb.h0000644000175000017500000000000013534540071021702 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/linux0000644000000000000000000000013213534540120020036 xustar0030 mtime=1567801424.545851686 30 atime=1567801425.625859648 30 ctime=1567801424.545851686 openvswitch-2.5.9/include/windows/linux/0000755000175000017500000000000013534540120021601 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/linux/PaxHeaders.82075/pkt_sched.h0000644000000000000000000000013213534540071022235 xustar0030 mtime=1567801401.305680465 30 atime=1567801402.065686047 30 ctime=1567801424.541851656 openvswitch-2.5.9/include/windows/linux/pkt_sched.h0000644000175000017500000000151613534540071023726 0ustar00jpettitjpettit00000000000000/* * Copyright 2014 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __LINUX_PKT_SCHED_H #define __LINUX_PKT_SCHED_H 1 #define TC_H_MAJ_MASK (0xFFFF0000U) #define TC_H_MIN_MASK (0x0000FFFFU) #define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) #endif /* linux/pkt_sched.h */ openvswitch-2.5.9/include/windows/linux/PaxHeaders.82075/types.h0000644000000000000000000000013213534540071021435 xustar0030 mtime=1567801401.305680465 30 atime=1567801401.305680465 30 ctime=1567801424.545851686 openvswitch-2.5.9/include/windows/linux/types.h0000644000175000017500000000000013534540071023111 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/dirent.h0000644000000000000000000000013213534540071020417 xustar0030 mtime=1567801401.305680465 30 atime=1567801401.305680465 30 ctime=1567801424.541851656 openvswitch-2.5.9/include/windows/dirent.h0000644000175000017500000000000013534540071022073 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/include/windows/PaxHeaders.82075/poll.h0000644000000000000000000000013213534540071020100 xustar0030 mtime=1567801401.309680495 30 atime=1567801401.309680495 30 ctime=1567801424.557851775 openvswitch-2.5.9/include/windows/poll.h0000644000175000017500000000000013534540071021554 0ustar00jpettitjpettit00000000000000openvswitch-2.5.9/PaxHeaders.82075/lib0000644000000000000000000000013213534540121014331 xustar0030 mtime=1567801425.013855136 30 atime=1567801425.625859648 30 ctime=1567801425.013855136 openvswitch-2.5.9/lib/0000755000175000017500000000000013534540121016074 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/lib/PaxHeaders.82075/coverage.c0000644000000000000000000000013213534540071016351 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.681852689 openvswitch-2.5.9/lib/coverage.c0000644000175000017500000002672013534540071020046 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "coverage.h" #include #include #include "dynamic-string.h" #include "hash.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(coverage); /* The coverage counters. */ static struct coverage_counter **coverage_counters = NULL; static size_t n_coverage_counters = 0; static size_t allocated_coverage_counters = 0; static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER; DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN); static long long int coverage_run_time = LLONG_MIN; /* Index counter used to compute the moving average array's index. */ static unsigned int idx_count = 0; static void coverage_read(struct svec *); static unsigned int coverage_array_sum(const unsigned int *arr, const unsigned int len); /* Registers a coverage counter with the coverage core */ void coverage_counter_register(struct coverage_counter* counter) { if (n_coverage_counters >= allocated_coverage_counters) { coverage_counters = x2nrealloc(coverage_counters, &allocated_coverage_counters, sizeof(struct coverage_counter*)); } coverage_counters[n_coverage_counters++] = counter; } static void coverage_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct svec lines; char *reply; svec_init(&lines); coverage_read(&lines); reply = svec_join(&lines, "\n", "\n"); unixctl_command_reply(conn, reply); free(reply); svec_destroy(&lines); } void coverage_init(void) { unixctl_command_register("coverage/show", "", 0, 0, coverage_unixctl_show, NULL); } /* Sorts coverage counters in descending order by total, within equal * totals alphabetically by name. */ static int compare_coverage_counters(const void *a_, const void *b_) { const struct coverage_counter *const *ap = a_; const struct coverage_counter *const *bp = b_; const struct coverage_counter *a = *ap; const struct coverage_counter *b = *bp; if (a->total != b->total) { return a->total < b->total ? 1 : -1; } else { return strcmp(a->name, b->name); } } static uint32_t coverage_hash(void) { struct coverage_counter **c; uint32_t hash = 0; int n_groups, i; /* Sort coverage counters into groups with equal totals. */ c = xmalloc(n_coverage_counters * sizeof *c); ovs_mutex_lock(&coverage_mutex); for (i = 0; i < n_coverage_counters; i++) { c[i] = coverage_counters[i]; } ovs_mutex_unlock(&coverage_mutex); qsort(c, n_coverage_counters, sizeof *c, compare_coverage_counters); /* Hash the names in each group along with the rank. */ n_groups = 0; for (i = 0; i < n_coverage_counters; ) { int j; if (!c[i]->total) { break; } n_groups++; hash = hash_int(i, hash); for (j = i; j < n_coverage_counters; j++) { if (c[j]->total != c[i]->total) { break; } hash = hash_string(c[j]->name, hash); } i = j; } free(c); return hash_int(n_groups, hash); } static bool coverage_hit(uint32_t hash) { enum { HIT_BITS = 1024, BITS_PER_WORD = 32 }; static uint32_t hit[HIT_BITS / BITS_PER_WORD]; BUILD_ASSERT_DECL(IS_POW2(HIT_BITS)); static long long int next_clear = LLONG_MIN; unsigned int bit_index = hash & (HIT_BITS - 1); unsigned int word_index = bit_index / BITS_PER_WORD; unsigned int word_mask = 1u << (bit_index % BITS_PER_WORD); /* Expire coverage hash suppression once a day. */ if (time_msec() >= next_clear) { memset(hit, 0, sizeof hit); next_clear = time_msec() + 60 * 60 * 24 * 1000LL; } if (hit[word_index] & word_mask) { return true; } else { hit[word_index] |= word_mask; return false; } } /* Logs the coverage counters, unless a similar set of events has already been * logged. * * This function logs at log level VLL_INFO. Use care before adjusting this * level, because depending on its configuration, syslogd can write changes * synchronously, which can cause the coverage messages to take several seconds * to write. */ void coverage_log(void) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 3); if (!VLOG_DROP_INFO(&rl)) { uint32_t hash = coverage_hash(); if (coverage_hit(hash)) { VLOG_INFO("Skipping details of duplicate event coverage for " "hash=%08"PRIx32, hash); } else { struct svec lines; const char *line; size_t i; svec_init(&lines); coverage_read(&lines); SVEC_FOR_EACH (i, line, &lines) { VLOG_INFO("%s", line); } svec_destroy(&lines); } } } /* Adds coverage counter information to 'lines'. */ static void coverage_read(struct svec *lines) { struct coverage_counter **c = coverage_counters; unsigned long long int *totals; size_t n_never_hit; uint32_t hash; size_t i; hash = coverage_hash(); n_never_hit = 0; svec_add_nocopy(lines, xasprintf("Event coverage, avg rate over last: %d " "seconds, last minute, last hour, " "hash=%08"PRIx32":", COVERAGE_RUN_INTERVAL/1000, hash)); totals = xmalloc(n_coverage_counters * sizeof *totals); ovs_mutex_lock(&coverage_mutex); for (i = 0; i < n_coverage_counters; i++) { totals[i] = c[i]->total; } ovs_mutex_unlock(&coverage_mutex); for (i = 0; i < n_coverage_counters; i++) { if (totals[i]) { /* Shows the averaged per-second rates for the last * COVERAGE_RUN_INTERVAL interval, the last minute and * the last hour. */ svec_add_nocopy(lines, xasprintf("%-24s %5.1f/sec %9.3f/sec " "%13.4f/sec total: %llu", c[i]->name, (c[i]->min[(idx_count - 1) % MIN_AVG_LEN] * 1000.0 / COVERAGE_RUN_INTERVAL), coverage_array_sum(c[i]->min, MIN_AVG_LEN) / 60.0, coverage_array_sum(c[i]->hr, HR_AVG_LEN) / 3600.0, totals[i])); } else { n_never_hit++; } } svec_add_nocopy(lines, xasprintf("%"PRIuSIZE" events never hit", n_never_hit)); free(totals); } /* Runs approximately every COVERAGE_CLEAR_INTERVAL amount of time to * synchronize per-thread counters with global counters. Every thread maintains * a separate timer to ensure all counters are periodically aggregated. * * Uses 'ovs_mutex_trylock()' if 'trylock' is true. This is to prevent * multiple performance-critical threads contending over the 'coverage_mutex'. * * */ static void coverage_clear__(bool trylock) { long long int now, *thread_time; now = time_msec(); thread_time = coverage_clear_time_get(); /* Initialize the coverage_clear_time. */ if (*thread_time == LLONG_MIN) { *thread_time = now + COVERAGE_CLEAR_INTERVAL; } if (now >= *thread_time) { size_t i; if (trylock) { /* Returns if cannot acquire lock. */ if (ovs_mutex_trylock(&coverage_mutex)) { return; } } else { ovs_mutex_lock(&coverage_mutex); } for (i = 0; i < n_coverage_counters; i++) { struct coverage_counter *c = coverage_counters[i]; c->total += c->count(); } ovs_mutex_unlock(&coverage_mutex); *thread_time = now + COVERAGE_CLEAR_INTERVAL; } } void coverage_clear(void) { coverage_clear__(false); } void coverage_try_clear(void) { coverage_clear__(true); } /* Runs approximately every COVERAGE_RUN_INTERVAL amount of time to update the * coverage counters' 'min' and 'hr' array. 'min' array is for cumulating * per second counts into per minute count. 'hr' array is for cumulating per * minute counts into per hour count. Every thread may call this function. */ void coverage_run(void) { struct coverage_counter **c = coverage_counters; long long int now; ovs_mutex_lock(&coverage_mutex); now = time_msec(); /* Initialize the coverage_run_time. */ if (coverage_run_time == LLONG_MIN) { coverage_run_time = now + COVERAGE_RUN_INTERVAL; } if (now >= coverage_run_time) { size_t i, j; /* Computes the number of COVERAGE_RUN_INTERVAL slots, since * it is possible that the actual run interval is multiple of * COVERAGE_RUN_INTERVAL. */ int slots = (now - coverage_run_time) / COVERAGE_RUN_INTERVAL + 1; for (i = 0; i < n_coverage_counters; i++) { unsigned int count, portion; unsigned int idx = idx_count; /* Computes the differences between the current total and the one * recorded in last invocation of coverage_run(). */ count = c[i]->total - c[i]->last_total; c[i]->last_total = c[i]->total; /* The count over the time interval is evenly distributed * among slots by calculating the portion. */ portion = count / slots; for (j = 0; j < slots; j++) { /* Updates the index variables. */ /* The m_idx is increased from 0 to MIN_AVG_LEN - 1. Every * time the m_idx finishes a cycle (a cycle is one minute), * the h_idx is incremented by 1. */ unsigned int m_idx = idx % MIN_AVG_LEN; unsigned int h_idx = idx / MIN_AVG_LEN; c[i]->min[m_idx] = portion + (j == (slots - 1) ? count % slots : 0); c[i]->hr[h_idx] = m_idx == 0 ? c[i]->min[m_idx] : (c[i]->hr[h_idx] + c[i]->min[m_idx]); /* This is to guarantee that h_idx ranges from 0 to 59. */ idx = (idx + 1) % (MIN_AVG_LEN * HR_AVG_LEN); } } /* Updates the global index variables. */ idx_count = (idx_count + slots) % (MIN_AVG_LEN * HR_AVG_LEN); /* Updates the run time. */ coverage_run_time = now + COVERAGE_RUN_INTERVAL; } ovs_mutex_unlock(&coverage_mutex); } static unsigned int coverage_array_sum(const unsigned int *arr, const unsigned int len) { unsigned int sum = 0; size_t i; ovs_mutex_lock(&coverage_mutex); for (i = 0; i < len; i++) { sum += arr[i]; } ovs_mutex_unlock(&coverage_mutex); return sum; } openvswitch-2.5.9/lib/PaxHeaders.82075/cmap.c0000644000000000000000000000013213534540071015476 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.673852631 openvswitch-2.5.9/lib/cmap.c0000644000175000017500000010171213534540071017166 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "cmap.h" #include "coverage.h" #include "bitmap.h" #include "hash.h" #include "ovs-rcu.h" #include "random.h" #include "util.h" COVERAGE_DEFINE(cmap_expand); COVERAGE_DEFINE(cmap_shrink); /* Optimistic Concurrent Cuckoo Hash * ================================= * * A "cuckoo hash" is an open addressing hash table schema, designed such that * a given element can be in one of only a small number of buckets 'd', each of * which holds up to a small number 'k' elements. Thus, the expected and * worst-case lookup times are O(1) because they require comparing no more than * a fixed number of elements (k * d). Inserting a new element can require * moving around existing elements, but it is also O(1) amortized expected * time. * * An optimistic concurrent hash table goes one step further, making it * possible for a single writer to execute concurrently with any number of * readers without requiring the readers to take any locks. * * This cuckoo hash implementation uses: * * - Two hash functions (d=2). More hash functions allow for a higher load * factor, but increasing 'k' is easier and the benefits of increasing 'd' * quickly fall off with the 'k' values used here. Also, the method of * generating hashes used in this implementation is hard to reasonably * extend beyond d=2. Finally, each additional hash function means that a * lookup has to look at least one extra cache line. * * - 5 or 7 elements per bucket (k=5 or k=7), chosen to make buckets * exactly one cache line in size. * * According to Erlingsson [4], these parameters suggest a maximum load factor * of about 93%. The current implementation is conservative, expanding the * hash table when it is over 85% full. * * When the load factor is below 20%, the hash table will be shrinked by half. * This is to reduce the memory utilization of the hash table and to avoid * the hash table occupying the top of heap chunk which prevents the trimming * of heap. * * Hash Functions * ============== * * A cuckoo hash requires multiple hash functions. When reorganizing the hash * becomes too difficult, it also requires the ability to change the hash * functions. Requiring the client to provide multiple hashes and to be able * to change them to new hashes upon insertion is inconvenient. * * This implementation takes another approach. The client provides a single, * fixed hash. The cuckoo hash internally "rehashes" this hash against a * randomly selected basis value (see rehash()). This rehashed value is one of * the two hashes. The other hash is computed by 16-bit circular rotation of * the rehashed value. Updating the basis changes the hash functions. * * To work properly, the hash functions used by a cuckoo hash must be * independent. If one hash function is a function of the other (e.g. h2(x) = * h1(x) + 1, or h2(x) = hash(h1(x))), then insertion will eventually fail * catastrophically (loop forever) because of collisions. With this rehashing * technique, the two hashes are completely independent for masks up to 16 bits * wide. For masks wider than 16 bits, only 32-n bits are independent between * the two hashes. Thus, it becomes risky to grow a cuckoo hash table beyond * about 2**24 buckets (about 71 million elements with k=5 and maximum load * 85%). Fortunately, Open vSwitch does not normally deal with hash tables * this large. * * * Handling Duplicates * =================== * * This cuckoo hash table implementation deals with duplicate client-provided * hash values by chaining: the second and subsequent cmap_nodes with a given * hash are chained off the initially inserted node's 'next' member. The hash * table maintains the invariant that a single client-provided hash value * exists in only a single chain in a single bucket (even though that hash * could be stored in two buckets). * * * References * ========== * * [1] D. Zhou, B. Fan, H. Lim, M. Kaminsky, D. G. Andersen, "Scalable, High * Performance Ethernet Forwarding with CuckooSwitch". In Proc. 9th * CoNEXT, Dec. 2013. * * [2] B. Fan, D. G. Andersen, and M. Kaminsky. "MemC3: Compact and concurrent * memcache with dumber caching and smarter hashing". In Proc. 10th USENIX * NSDI, Apr. 2013 * * [3] R. Pagh and F. Rodler. "Cuckoo hashing". Journal of Algorithms, 51(2): * 122-144, May 2004. * * [4] U. Erlingsson, M. Manasse, F. McSherry, "A Cool and Practical * Alternative to Traditional Hash Tables". In Proc. 7th Workshop on * Distributed Data and Structures (WDAS'06), 2006. */ /* An entry is an int and a pointer: 8 bytes on 32-bit, 12 bytes on 64-bit. */ #define CMAP_ENTRY_SIZE (4 + (UINTPTR_MAX == UINT32_MAX ? 4 : 8)) /* Number of entries per bucket: 7 on 32-bit, 5 on 64-bit. */ #define CMAP_K ((CACHE_LINE_SIZE - 4) / CMAP_ENTRY_SIZE) /* Pad to make a bucket a full cache line in size: 4 on 32-bit, 0 on 64-bit. */ #define CMAP_PADDING ((CACHE_LINE_SIZE - 4) - (CMAP_K * CMAP_ENTRY_SIZE)) /* A cuckoo hash bucket. Designed to be cache-aligned and exactly one cache * line long. */ struct cmap_bucket { /* Allows readers to track in-progress changes. Initially zero, each * writer increments this value just before and just after each change (see * cmap_set_bucket()). Thus, a reader can ensure that it gets a consistent * snapshot by waiting for the counter to become even (see * read_even_counter()), then checking that its value does not change while * examining the bucket (see cmap_find()). */ atomic_uint32_t counter; /* (hash, node) slots. They are parallel arrays instead of an array of * structs to reduce the amount of space lost to padding. * * The slots are in no particular order. A null pointer indicates that a * pair is unused. In-use slots are not necessarily in the earliest * slots. */ uint32_t hashes[CMAP_K]; struct cmap_node nodes[CMAP_K]; /* Padding to make cmap_bucket exactly one cache line long. */ #if CMAP_PADDING > 0 uint8_t pad[CMAP_PADDING]; #endif }; BUILD_ASSERT_DECL(sizeof(struct cmap_bucket) == CACHE_LINE_SIZE); /* Default maximum load factor (as a fraction of UINT32_MAX + 1) before * enlarging a cmap. Reasonable values lie between about 75% and 93%. Smaller * values waste memory; larger values increase the average insertion time. */ #define CMAP_MAX_LOAD ((uint32_t) (UINT32_MAX * .85)) /* Default minimum load factor (as a fraction of UINT32_MAX + 1) before * shrinking a cmap. Currently, the value is chosen to be 20%, this * means cmap will have a 40% load factor after shrink. */ #define CMAP_MIN_LOAD ((uint32_t) (UINT32_MAX * .20)) /* The implementation of a concurrent hash map. */ struct cmap_impl { unsigned int n; /* Number of in-use elements. */ unsigned int max_n; /* Max elements before enlarging. */ unsigned int min_n; /* Min elements before shrinking. */ uint32_t mask; /* Number of 'buckets', minus one. */ uint32_t basis; /* Basis for rehashing client's hash values. */ /* Padding to make cmap_impl exactly one cache line long. */ uint8_t pad[CACHE_LINE_SIZE - sizeof(unsigned int) * 5]; struct cmap_bucket buckets[]; }; BUILD_ASSERT_DECL(sizeof(struct cmap_impl) == CACHE_LINE_SIZE); static struct cmap_impl *cmap_rehash(struct cmap *, uint32_t mask); /* Explicit inline keywords in utility functions seem to be necessary * to prevent performance regression on cmap_find(). */ /* Given a rehashed value 'hash', returns the other hash for that rehashed * value. This is symmetric: other_hash(other_hash(x)) == x. (See also "Hash * Functions" at the top of this file.) */ static inline uint32_t other_hash(uint32_t hash) { return (hash << 16) | (hash >> 16); } /* Returns the rehashed value for 'hash' within 'impl'. (See also "Hash * Functions" at the top of this file.) */ static inline uint32_t rehash(const struct cmap_impl *impl, uint32_t hash) { return hash_finish(impl->basis, hash); } /* Not always without the inline keyword. */ static inline struct cmap_impl * cmap_get_impl(const struct cmap *cmap) { return ovsrcu_get(struct cmap_impl *, &cmap->impl); } static uint32_t calc_max_n(uint32_t mask) { return ((uint64_t) (mask + 1) * CMAP_K * CMAP_MAX_LOAD) >> 32; } static uint32_t calc_min_n(uint32_t mask) { return ((uint64_t) (mask + 1) * CMAP_K * CMAP_MIN_LOAD) >> 32; } static struct cmap_impl * cmap_impl_create(uint32_t mask) { struct cmap_impl *impl; ovs_assert(is_pow2(mask + 1)); impl = xzalloc_cacheline(sizeof *impl + (mask + 1) * sizeof *impl->buckets); impl->n = 0; impl->max_n = calc_max_n(mask); impl->min_n = calc_min_n(mask); impl->mask = mask; impl->basis = random_uint32(); return impl; } /* Initializes 'cmap' as an empty concurrent hash map. */ void cmap_init(struct cmap *cmap) { ovsrcu_set(&cmap->impl, cmap_impl_create(0)); } /* Destroys 'cmap'. * * The client is responsible for destroying any data previously held in * 'cmap'. */ void cmap_destroy(struct cmap *cmap) { if (cmap) { ovsrcu_postpone(free_cacheline, cmap_get_impl(cmap)); } } /* Returns the number of elements in 'cmap'. */ size_t cmap_count(const struct cmap *cmap) { return cmap_get_impl(cmap)->n; } /* Returns true if 'cmap' is empty, false otherwise. */ bool cmap_is_empty(const struct cmap *cmap) { return cmap_count(cmap) == 0; } static inline uint32_t read_counter(const struct cmap_bucket *bucket_) { struct cmap_bucket *bucket = CONST_CAST(struct cmap_bucket *, bucket_); uint32_t counter; atomic_read_explicit(&bucket->counter, &counter, memory_order_acquire); return counter; } static inline uint32_t read_even_counter(const struct cmap_bucket *bucket) { uint32_t counter; do { counter = read_counter(bucket); } while (OVS_UNLIKELY(counter & 1)); return counter; } static inline bool counter_changed(const struct cmap_bucket *b_, uint32_t c) { struct cmap_bucket *b = CONST_CAST(struct cmap_bucket *, b_); uint32_t counter; /* Need to make sure the counter read is not moved up, before the hash and * cmap_node_next(). Using atomic_read_explicit with memory_order_acquire * would allow prior reads to be moved after the barrier. * atomic_thread_fence prevents all following memory accesses from moving * prior to preceding loads. */ atomic_thread_fence(memory_order_acquire); atomic_read_relaxed(&b->counter, &counter); return OVS_UNLIKELY(counter != c); } static inline const struct cmap_node * cmap_find_in_bucket(const struct cmap_bucket *bucket, uint32_t hash) { for (int i = 0; i < CMAP_K; i++) { if (bucket->hashes[i] == hash) { return cmap_node_next(&bucket->nodes[i]); } } return NULL; } static inline const struct cmap_node * cmap_find__(const struct cmap_bucket *b1, const struct cmap_bucket *b2, uint32_t hash) { uint32_t c1, c2; const struct cmap_node *node; do { do { c1 = read_even_counter(b1); node = cmap_find_in_bucket(b1, hash); } while (OVS_UNLIKELY(counter_changed(b1, c1))); if (node) { break; } do { c2 = read_even_counter(b2); node = cmap_find_in_bucket(b2, hash); } while (OVS_UNLIKELY(counter_changed(b2, c2))); if (node) { break; } } while (OVS_UNLIKELY(counter_changed(b1, c1))); return node; } /* Searches 'cmap' for an element with the specified 'hash'. If one or more is * found, returns a pointer to the first one, otherwise a null pointer. All of * the nodes on the returned list are guaranteed to have exactly the given * 'hash'. * * This function works even if 'cmap' is changing concurrently. If 'cmap' is * not changing, then cmap_find_protected() is slightly faster. * * CMAP_FOR_EACH_WITH_HASH is usually more convenient. */ const struct cmap_node * cmap_find(const struct cmap *cmap, uint32_t hash) { const struct cmap_impl *impl = cmap_get_impl(cmap); uint32_t h1 = rehash(impl, hash); uint32_t h2 = other_hash(h1); return cmap_find__(&impl->buckets[h1 & impl->mask], &impl->buckets[h2 & impl->mask], hash); } /* Looks up multiple 'hashes', when the corresponding bit in 'map' is 1, * and sets the corresponding pointer in 'nodes', if the hash value was * found from the 'cmap'. In other cases the 'nodes' values are not changed, * i.e., no NULL pointers are stored there. * Returns a map where a bit is set to 1 if the corresponding 'nodes' pointer * was stored, 0 otherwise. * Generally, the caller wants to use CMAP_NODE_FOR_EACH to verify for * hash collisions. */ unsigned long cmap_find_batch(const struct cmap *cmap, unsigned long map, uint32_t hashes[], const struct cmap_node *nodes[]) { const struct cmap_impl *impl = cmap_get_impl(cmap); unsigned long result = map; int i; uint32_t h1s[sizeof map * CHAR_BIT]; const struct cmap_bucket *b1s[sizeof map * CHAR_BIT]; const struct cmap_bucket *b2s[sizeof map * CHAR_BIT]; uint32_t c1s[sizeof map * CHAR_BIT]; /* Compute hashes and prefetch 1st buckets. */ ULLONG_FOR_EACH_1(i, map) { h1s[i] = rehash(impl, hashes[i]); b1s[i] = &impl->buckets[h1s[i] & impl->mask]; OVS_PREFETCH(b1s[i]); } /* Lookups, Round 1. Only look up at the first bucket. */ ULLONG_FOR_EACH_1(i, map) { uint32_t c1; const struct cmap_bucket *b1 = b1s[i]; const struct cmap_node *node; do { c1 = read_even_counter(b1); node = cmap_find_in_bucket(b1, hashes[i]); } while (OVS_UNLIKELY(counter_changed(b1, c1))); if (!node) { /* Not found (yet); Prefetch the 2nd bucket. */ b2s[i] = &impl->buckets[other_hash(h1s[i]) & impl->mask]; OVS_PREFETCH(b2s[i]); c1s[i] = c1; /* We may need to check this after Round 2. */ continue; } /* Found. */ ULLONG_SET0(map, i); /* Ignore this on round 2. */ OVS_PREFETCH(node); nodes[i] = node; } /* Round 2. Look into the 2nd bucket, if needed. */ ULLONG_FOR_EACH_1(i, map) { uint32_t c2; const struct cmap_bucket *b2 = b2s[i]; const struct cmap_node *node; do { c2 = read_even_counter(b2); node = cmap_find_in_bucket(b2, hashes[i]); } while (OVS_UNLIKELY(counter_changed(b2, c2))); if (!node) { /* Not found, but the node may have been moved from b2 to b1 right * after we finished with b1 earlier. We just got a clean reading * of the 2nd bucket, so we check the counter of the 1st bucket * only. However, we need to check both buckets again, as the * entry may be moved again to the 2nd bucket. Basically, we * need to loop as long as it takes to get stable readings of * both buckets. cmap_find__() does that, and now that we have * fetched both buckets we can just use it. */ if (OVS_UNLIKELY(counter_changed(b1s[i], c1s[i]))) { node = cmap_find__(b1s[i], b2s[i], hashes[i]); if (node) { goto found; } } /* Not found. */ ULLONG_SET0(result, i); /* Fix the result. */ continue; } found: OVS_PREFETCH(node); nodes[i] = node; } return result; } static int cmap_find_slot_protected(struct cmap_bucket *b, uint32_t hash) { int i; for (i = 0; i < CMAP_K; i++) { if (b->hashes[i] == hash && cmap_node_next_protected(&b->nodes[i])) { return i; } } return -1; } static struct cmap_node * cmap_find_bucket_protected(struct cmap_impl *impl, uint32_t hash, uint32_t h) { struct cmap_bucket *b = &impl->buckets[h & impl->mask]; int i; for (i = 0; i < CMAP_K; i++) { if (b->hashes[i] == hash) { return cmap_node_next_protected(&b->nodes[i]); } } return NULL; } /* Like cmap_find(), but only for use if 'cmap' cannot change concurrently. * * CMAP_FOR_EACH_WITH_HASH_PROTECTED is usually more convenient. */ struct cmap_node * cmap_find_protected(const struct cmap *cmap, uint32_t hash) { struct cmap_impl *impl = cmap_get_impl(cmap); uint32_t h1 = rehash(impl, hash); uint32_t h2 = other_hash(h1); struct cmap_node *node; node = cmap_find_bucket_protected(impl, hash, h1); if (node) { return node; } return cmap_find_bucket_protected(impl, hash, h2); } static int cmap_find_empty_slot_protected(const struct cmap_bucket *b) { int i; for (i = 0; i < CMAP_K; i++) { if (!cmap_node_next_protected(&b->nodes[i])) { return i; } } return -1; } static void cmap_set_bucket(struct cmap_bucket *b, int i, struct cmap_node *node, uint32_t hash) { uint32_t c; atomic_read_explicit(&b->counter, &c, memory_order_acquire); atomic_store_explicit(&b->counter, c + 1, memory_order_release); ovsrcu_set(&b->nodes[i].next, node); /* Also atomic. */ b->hashes[i] = hash; atomic_store_explicit(&b->counter, c + 2, memory_order_release); } /* Searches 'b' for a node with the given 'hash'. If it finds one, adds * 'new_node' to the node's linked list and returns true. If it does not find * one, returns false. */ static bool cmap_insert_dup(struct cmap_node *new_node, uint32_t hash, struct cmap_bucket *b) { int i; for (i = 0; i < CMAP_K; i++) { if (b->hashes[i] == hash) { struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]); if (node) { struct cmap_node *p; /* The common case is that 'new_node' is a singleton, * with a null 'next' pointer. Rehashing can add a * longer chain, but due to our invariant of always * having all nodes with the same (user) hash value at * a single chain, rehashing will always insert the * chain to an empty node. The only way we can end up * here is by the user inserting a chain of nodes at * once. Find the end of the chain starting at * 'new_node', then splice 'node' to the end of that * chain. */ p = new_node; for (;;) { struct cmap_node *next = cmap_node_next_protected(p); if (!next) { break; } p = next; } ovsrcu_set_hidden(&p->next, node); } else { /* The hash value is there from some previous insertion, but * the associated node has been removed. We're not really * inserting a duplicate, but we can still reuse the slot. * Carry on. */ } /* Change the bucket to point to 'new_node'. This is a degenerate * form of cmap_set_bucket() that doesn't update the counter since * we're only touching one field and in a way that doesn't change * the bucket's meaning for readers. */ ovsrcu_set(&b->nodes[i].next, new_node); return true; } } return false; } /* Searches 'b' for an empty slot. If successful, stores 'node' and 'hash' in * the slot and returns true. Otherwise, returns false. */ static bool cmap_insert_bucket(struct cmap_node *node, uint32_t hash, struct cmap_bucket *b) { int i; for (i = 0; i < CMAP_K; i++) { if (!cmap_node_next_protected(&b->nodes[i])) { cmap_set_bucket(b, i, node, hash); return true; } } return false; } /* Returns the other bucket that b->nodes[slot] could occupy in 'impl'. (This * might be the same as 'b'.) */ static struct cmap_bucket * other_bucket_protected(struct cmap_impl *impl, struct cmap_bucket *b, int slot) { uint32_t h1 = rehash(impl, b->hashes[slot]); uint32_t h2 = other_hash(h1); uint32_t b_idx = b - impl->buckets; uint32_t other_h = (h1 & impl->mask) == b_idx ? h2 : h1; return &impl->buckets[other_h & impl->mask]; } /* 'new_node' is to be inserted into 'impl', but both candidate buckets 'b1' * and 'b2' are full. This function attempts to rearrange buckets within * 'impl' to make room for 'new_node'. * * The implementation is a general-purpose breadth-first search. At first * glance, this is more complex than a random walk through 'impl' (suggested by * some references), but random walks have a tendency to loop back through a * single bucket. We have to move nodes backward along the path that we find, * so that no node actually disappears from the hash table, which means a * random walk would have to be careful to deal with loops. By contrast, a * successful breadth-first search always finds a *shortest* path through the * hash table, and a shortest path will never contain loops, so it avoids that * problem entirely. */ static bool cmap_insert_bfs(struct cmap_impl *impl, struct cmap_node *new_node, uint32_t hash, struct cmap_bucket *b1, struct cmap_bucket *b2) { enum { MAX_DEPTH = 4 }; /* A path from 'start' to 'end' via the 'n' steps in 'slots[]'. * * One can follow the path via: * * struct cmap_bucket *b; * int i; * * b = path->start; * for (i = 0; i < path->n; i++) { * b = other_bucket_protected(impl, b, path->slots[i]); * } * ovs_assert(b == path->end); */ struct cmap_path { struct cmap_bucket *start; /* First bucket along the path. */ struct cmap_bucket *end; /* Last bucket on the path. */ uint8_t slots[MAX_DEPTH]; /* Slots used for each hop. */ int n; /* Number of slots[]. */ }; /* We need to limit the amount of work we do trying to find a path. It * might actually be impossible to rearrange the cmap, and after some time * it is likely to be easier to rehash the entire cmap. * * This value of MAX_QUEUE is an arbitrary limit suggested by one of the * references. Empirically, it seems to work OK. */ enum { MAX_QUEUE = 500 }; struct cmap_path queue[MAX_QUEUE]; int head = 0; int tail = 0; /* Add 'b1' and 'b2' as starting points for the search. */ queue[head].start = b1; queue[head].end = b1; queue[head].n = 0; head++; if (b1 != b2) { queue[head].start = b2; queue[head].end = b2; queue[head].n = 0; head++; } while (tail < head) { const struct cmap_path *path = &queue[tail++]; struct cmap_bucket *this = path->end; int i; for (i = 0; i < CMAP_K; i++) { struct cmap_bucket *next = other_bucket_protected(impl, this, i); int j; if (this == next) { continue; } j = cmap_find_empty_slot_protected(next); if (j >= 0) { /* We've found a path along which we can rearrange the hash * table: Start at path->start, follow all the slots in * path->slots[], then follow slot 'i', then the bucket you * arrive at has slot 'j' empty. */ struct cmap_bucket *buckets[MAX_DEPTH + 2]; int slots[MAX_DEPTH + 2]; int k; /* Figure out the full sequence of slots. */ for (k = 0; k < path->n; k++) { slots[k] = path->slots[k]; } slots[path->n] = i; slots[path->n + 1] = j; /* Figure out the full sequence of buckets. */ buckets[0] = path->start; for (k = 0; k <= path->n; k++) { buckets[k + 1] = other_bucket_protected(impl, buckets[k], slots[k]); } /* Now the path is fully expressed. One can start from * buckets[0], go via slots[0] to buckets[1], via slots[1] to * buckets[2], and so on. * * Move all the nodes across the path "backward". After each * step some node appears in two buckets. Thus, every node is * always visible to a concurrent search. */ for (k = path->n + 1; k > 0; k--) { int slot = slots[k - 1]; cmap_set_bucket( buckets[k], slots[k], cmap_node_next_protected(&buckets[k - 1]->nodes[slot]), buckets[k - 1]->hashes[slot]); } /* Finally, replace the first node on the path by * 'new_node'. */ cmap_set_bucket(buckets[0], slots[0], new_node, hash); return true; } if (path->n < MAX_DEPTH && head < MAX_QUEUE) { struct cmap_path *new_path = &queue[head++]; *new_path = *path; new_path->end = next; new_path->slots[new_path->n++] = i; } } } return false; } /* Adds 'node', with the given 'hash', to 'impl'. * * 'node' is ordinarily a single node, with a null 'next' pointer. When * rehashing, however, it may be a longer chain of nodes. */ static bool cmap_try_insert(struct cmap_impl *impl, struct cmap_node *node, uint32_t hash) { uint32_t h1 = rehash(impl, hash); uint32_t h2 = other_hash(h1); struct cmap_bucket *b1 = &impl->buckets[h1 & impl->mask]; struct cmap_bucket *b2 = &impl->buckets[h2 & impl->mask]; return (OVS_UNLIKELY(cmap_insert_dup(node, hash, b1) || cmap_insert_dup(node, hash, b2)) || OVS_LIKELY(cmap_insert_bucket(node, hash, b1) || cmap_insert_bucket(node, hash, b2)) || cmap_insert_bfs(impl, node, hash, b1, b2)); } /* Inserts 'node', with the given 'hash', into 'cmap'. The caller must ensure * that 'cmap' cannot change concurrently (from another thread). If duplicates * are undesirable, the caller must have already verified that 'cmap' does not * contain a duplicate of 'node'. * * Returns the current number of nodes in the cmap after the insertion. */ size_t cmap_insert(struct cmap *cmap, struct cmap_node *node, uint32_t hash) { struct cmap_impl *impl = cmap_get_impl(cmap); ovsrcu_set_hidden(&node->next, NULL); if (OVS_UNLIKELY(impl->n >= impl->max_n)) { COVERAGE_INC(cmap_expand); impl = cmap_rehash(cmap, (impl->mask << 1) | 1); } while (OVS_UNLIKELY(!cmap_try_insert(impl, node, hash))) { impl = cmap_rehash(cmap, impl->mask); } return ++impl->n; } static bool cmap_replace__(struct cmap_impl *impl, struct cmap_node *node, struct cmap_node *replacement, uint32_t hash, uint32_t h) { struct cmap_bucket *b = &impl->buckets[h & impl->mask]; int slot; slot = cmap_find_slot_protected(b, hash); if (slot < 0) { return false; } /* The pointer to 'node' is changed to point to 'replacement', * which is the next node if no replacement node is given. */ if (!replacement) { replacement = cmap_node_next_protected(node); } else { /* 'replacement' takes the position of 'node' in the list. */ ovsrcu_set_hidden(&replacement->next, cmap_node_next_protected(node)); } struct cmap_node *iter = &b->nodes[slot]; for (;;) { struct cmap_node *next = cmap_node_next_protected(iter); if (next == node) { ovsrcu_set(&iter->next, replacement); return true; } iter = next; } } /* Replaces 'old_node' in 'cmap' with 'new_node'. The caller must * ensure that 'cmap' cannot change concurrently (from another thread). * * 'old_node' must not be destroyed or modified or inserted back into 'cmap' or * into any other concurrent hash map while any other thread might be accessing * it. One correct way to do this is to free it from an RCU callback with * ovsrcu_postpone(). * * Returns the current number of nodes in the cmap after the replacement. The * number of nodes decreases by one if 'new_node' is NULL. */ size_t cmap_replace(struct cmap *cmap, struct cmap_node *old_node, struct cmap_node *new_node, uint32_t hash) { struct cmap_impl *impl = cmap_get_impl(cmap); uint32_t h1 = rehash(impl, hash); uint32_t h2 = other_hash(h1); bool ok; ok = cmap_replace__(impl, old_node, new_node, hash, h1) || cmap_replace__(impl, old_node, new_node, hash, h2); ovs_assert(ok); if (!new_node) { impl->n--; if (OVS_UNLIKELY(impl->n < impl->min_n)) { COVERAGE_INC(cmap_shrink); impl = cmap_rehash(cmap, impl->mask >> 1); } } return impl->n; } static bool cmap_try_rehash(const struct cmap_impl *old, struct cmap_impl *new) { const struct cmap_bucket *b; for (b = old->buckets; b <= &old->buckets[old->mask]; b++) { int i; for (i = 0; i < CMAP_K; i++) { /* possible optimization here because we know the hashes are * unique */ struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]); if (node && !cmap_try_insert(new, node, b->hashes[i])) { return false; } } } return true; } static struct cmap_impl * cmap_rehash(struct cmap *cmap, uint32_t mask) { struct cmap_impl *old = cmap_get_impl(cmap); struct cmap_impl *new; new = cmap_impl_create(mask); ovs_assert(old->n < new->max_n); while (!cmap_try_rehash(old, new)) { memset(new->buckets, 0, (mask + 1) * sizeof *new->buckets); new->basis = random_uint32(); } new->n = old->n; ovsrcu_set(&cmap->impl, new); ovsrcu_postpone(free_cacheline, old); return new; } struct cmap_cursor cmap_cursor_start(const struct cmap *cmap) { struct cmap_cursor cursor; cursor.impl = cmap_get_impl(cmap); cursor.bucket_idx = 0; cursor.entry_idx = 0; cursor.node = NULL; cmap_cursor_advance(&cursor); return cursor; } void cmap_cursor_advance(struct cmap_cursor *cursor) { const struct cmap_impl *impl = cursor->impl; if (cursor->node) { cursor->node = cmap_node_next(cursor->node); if (cursor->node) { return; } } while (cursor->bucket_idx <= impl->mask) { const struct cmap_bucket *b = &impl->buckets[cursor->bucket_idx]; while (cursor->entry_idx < CMAP_K) { cursor->node = cmap_node_next(&b->nodes[cursor->entry_idx++]); if (cursor->node) { return; } } cursor->bucket_idx++; cursor->entry_idx = 0; } } /* Returns the next node in 'cmap' in hash order, or NULL if no nodes remain in * 'cmap'. Uses '*pos' to determine where to begin iteration, and updates * '*pos' to pass on the next iteration into them before returning. * * It's better to use plain CMAP_FOR_EACH and related functions, since they are * faster and better at dealing with cmaps that change during iteration. * * Before beginning iteration, set '*pos' to all zeros. */ struct cmap_node * cmap_next_position(const struct cmap *cmap, struct cmap_position *pos) { struct cmap_impl *impl = cmap_get_impl(cmap); unsigned int bucket = pos->bucket; unsigned int entry = pos->entry; unsigned int offset = pos->offset; while (bucket <= impl->mask) { const struct cmap_bucket *b = &impl->buckets[bucket]; while (entry < CMAP_K) { const struct cmap_node *node = cmap_node_next(&b->nodes[entry]); unsigned int i; for (i = 0; node; i++, node = cmap_node_next(node)) { if (i == offset) { if (cmap_node_next(node)) { offset++; } else { entry++; offset = 0; } pos->bucket = bucket; pos->entry = entry; pos->offset = offset; return CONST_CAST(struct cmap_node *, node); } } entry++; offset = 0; } bucket++; entry = offset = 0; } pos->bucket = pos->entry = pos->offset = 0; return NULL; } openvswitch-2.5.9/lib/PaxHeaders.82075/odp-execute.h0000644000000000000000000000013213534540071017005 xustar0030 mtime=1567801401.477681728 30 atime=1567801402.081686164 30 ctime=1567801424.785853456 openvswitch-2.5.9/lib/odp-execute.h0000644000175000017500000000275713534540071020506 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * Copyright (c) 2013 Simon Horman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef EXECUTE_ACTIONS_H #define EXECUTE_ACTIONS_H 1 #include #include #include #include "openvswitch/types.h" struct nlattr; struct dp_packet; struct pkt_metadata; typedef void (*odp_execute_cb)(void *dp, struct dp_packet **packets, int cnt, const struct nlattr *action, bool may_steal); /* Actions that need to be executed in the context of a datapath are handed * to 'dp_execute_action', if non-NULL. Currently this is called only for * actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so * 'dp_execute_action' needs to handle only these. */ void odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal, const struct nlattr *actions, size_t actions_len, odp_execute_cb dp_execute_action); #endif openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-protocol.h0000644000000000000000000000013013534540071020064 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 28 ctime=1567801424.9818549 openvswitch-2.5.9/lib/netlink-protocol.h0000644000175000017500000001136413534540071021561 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2010, 2011, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETLINK_PROTOCOL_H #define NETLINK_PROTOCOL_H 1 /* Netlink protocol definitions. * * Netlink is a message framing format described in RFC 3549 and used heavily * in Linux to access the network stack. Open vSwitch uses AF_NETLINK sockets * for this purpose on Linux. But on all platforms, Open vSwitch uses Netlink * message framing internally for certain purposes. * * This header provides access to the Netlink message framing definitions * regardless of platform. On Linux, it includes the proper headers directly; * on other platforms it directly defines the structures and macros itself. */ #include #include #include "util.h" #ifdef HAVE_NETLINK #include #include #else #define NETLINK_GENERIC 16 /* nlmsg_flags bits. */ #define NLM_F_REQUEST 0x001 #define NLM_F_MULTI 0x002 #define NLM_F_ACK 0x004 #define NLM_F_ECHO 0x008 #define NLM_F_ROOT 0x100 #define NLM_F_MATCH 0x200 #define NLM_F_EXCL 0x200 #define NLM_F_ATOMIC 0x400 #define NLM_F_CREATE 0x400 #define NLM_F_DUMP (NLM_F_ROOT | NLM_F_MATCH) /* nlmsg_type values. */ #define NLMSG_NOOP 1 #define NLMSG_ERROR 2 #define NLMSG_DONE 3 #define NLMSG_OVERRUN 4 #define NLMSG_MIN_TYPE 0x10 #define MAX_LINKS 32 struct nlmsghdr { uint32_t nlmsg_len; uint16_t nlmsg_type; uint16_t nlmsg_flags; uint32_t nlmsg_seq; uint32_t nlmsg_pid; }; BUILD_ASSERT_DECL(sizeof(struct nlmsghdr) == 16); #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(SIZE) ROUND_UP(SIZE, NLMSG_ALIGNTO) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) struct nlmsgerr { int error; struct nlmsghdr msg; }; BUILD_ASSERT_DECL(sizeof(struct nlmsgerr) == 20); struct genlmsghdr { uint8_t cmd; uint8_t version; uint16_t reserved; }; BUILD_ASSERT_DECL(sizeof(struct genlmsghdr) == 4); #define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) struct nlattr { uint16_t nla_len; uint16_t nla_type; }; BUILD_ASSERT_DECL(sizeof(struct nlattr) == 4); #define NLA_ALIGNTO 4 #define NLA_ALIGN(SIZE) ROUND_UP(SIZE, NLA_ALIGNTO) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) #define GENL_MIN_ID NLMSG_MIN_TYPE #define GENL_MAX_ID 1023 #define GENL_ID_CTRL NLMSG_MIN_TYPE enum { CTRL_CMD_UNSPEC, CTRL_CMD_NEWFAMILY, CTRL_CMD_DELFAMILY, CTRL_CMD_GETFAMILY, CTRL_CMD_NEWOPS, CTRL_CMD_DELOPS, CTRL_CMD_GETOPS, __CTRL_CMD_MAX, }; #define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) enum { CTRL_ATTR_UNSPEC, CTRL_ATTR_FAMILY_ID, CTRL_ATTR_FAMILY_NAME, CTRL_ATTR_VERSION, CTRL_ATTR_HDRSIZE, CTRL_ATTR_MAXATTR, CTRL_ATTR_OPS, __CTRL_ATTR_MAX, }; #define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) enum { CTRL_ATTR_OP_UNSPEC, CTRL_ATTR_OP_ID, CTRL_ATTR_OP_FLAGS, __CTRL_ATTR_OP_MAX, }; #define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) #endif /* !HAVE_NETLINK */ /* These were introduced all together in 2.6.24. */ #ifndef NLA_TYPE_MASK #define NLA_F_NESTED (1 << 15) #define NLA_F_NET_BYTEORDER (1 << 14) #define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) #endif /* These were introduced all together in 2.6.14. (We want our programs to * support the newer kernel features even if compiled with older headers.) */ #ifndef NETLINK_ADD_MEMBERSHIP #define NETLINK_ADD_MEMBERSHIP 1 #define NETLINK_DROP_MEMBERSHIP 2 #endif /* These were introduced all together in 2.6.23. (We want our programs to * support the newer kernel features even if compiled with older headers.) */ #ifndef CTRL_ATTR_MCAST_GRP_MAX #undef CTRL_ATTR_MAX #define CTRL_ATTR_MAX 7 #define CTRL_ATTR_MCAST_GROUPS 7 enum { CTRL_ATTR_MCAST_GRP_UNSPEC, CTRL_ATTR_MCAST_GRP_NAME, CTRL_ATTR_MCAST_GRP_ID, __CTRL_ATTR_MCAST_GRP_MAX, }; #define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1) #endif /* CTRL_ATTR_MCAST_GRP_MAX */ #endif /* netlink-protocol.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ssl-bootstrap.man0000644000000000000000000000013113534540071017722 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 29 ctime=1567801423.73784573 openvswitch-2.5.9/lib/ssl-bootstrap.man0000644000175000017500000000163613534540071021417 0ustar00jpettitjpettit00000000000000.IP "\fB\-\-bootstrap\-ca\-cert=\fIcacert.pem\fR" When \fIcacert.pem\fR exists, this option has the same effect as \fB\-C\fR or \fB\-\-ca\-cert\fR. If it does not exist, then \fB\*(PN\fR will attempt to obtain the CA certificate from the SSL peer on its first SSL connection and save it to the named PEM file. If it is successful, it will immediately drop the connection and reconnect, and from then on all SSL connections must be authenticated by a certificate signed by the CA certificate thus obtained. .IP \fBThis option exposes the SSL connection to a man-in-the-middle attack obtaining the initial CA certificate\fR, but it may be useful for bootstrapping. .IP This option is only useful if the SSL peer sends its CA certificate as part of the SSL certificate chain. The SSL protocol does not require the server to send the CA certificate. .IP This option is mutually exclusive with \fB\-C\fR and \fB\-\-ca\-cert\fR. openvswitch-2.5.9/lib/PaxHeaders.82075/process.c0000644000000000000000000000013213534540071016234 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.853853956 openvswitch-2.5.9/lib/process.c0000644000175000017500000002530613534540071017730 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "process.h" #include #include #include #include #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "list.h" #include "ovs-thread.h" #include "poll-loop.h" #include "signals.h" #include "socket-util.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(process); COVERAGE_DEFINE(process_start); struct process { struct ovs_list node; char *name; pid_t pid; /* State. */ bool exited; int status; }; /* Pipe used to signal child termination. */ static int fds[2]; /* All processes. */ static struct ovs_list all_processes = OVS_LIST_INITIALIZER(&all_processes); static void sigchld_handler(int signr OVS_UNUSED); /* Initializes the process subsystem (if it is not already initialized). Calls * exit() if initialization fails. * * This function may not be called after creating any additional threads. * * Calling this function is optional; it will be called automatically by * process_start() if necessary. Calling it explicitly allows the client to * prevent the process from exiting at an unexpected time. */ void process_init(void) { #ifndef _WIN32 static bool inited; struct sigaction sa; assert_single_threaded(); if (inited) { return; } inited = true; /* Create notification pipe. */ xpipe_nonblocking(fds); /* Set up child termination signal handler. */ memset(&sa, 0, sizeof sa); sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; xsigaction(SIGCHLD, &sa, NULL); #endif } char * process_escape_args(char **argv) { struct ds ds = DS_EMPTY_INITIALIZER; char **argp; for (argp = argv; *argp; argp++) { const char *arg = *argp; const char *p; if (argp != argv) { ds_put_char(&ds, ' '); } if (arg[strcspn(arg, " \t\r\n\v\\\'\"")]) { ds_put_char(&ds, '"'); for (p = arg; *p; p++) { if (*p == '\\' || *p == '\"') { ds_put_char(&ds, '\\'); } ds_put_char(&ds, *p); } ds_put_char(&ds, '"'); } else { ds_put_cstr(&ds, arg); } } return ds_cstr(&ds); } /* Prepare to start a process whose command-line arguments are given by the * null-terminated 'argv' array. Returns 0 if successful, otherwise a * positive errno value. */ static int process_prestart(char **argv) { char *binary; process_init(); /* Log the process to be started. */ if (VLOG_IS_DBG_ENABLED()) { char *args = process_escape_args(argv); VLOG_DBG("starting subprocess: %s", args); free(args); } /* execvp() will search PATH too, but the error in that case is more * obscure, since it is only reported post-fork. */ binary = process_search_path(argv[0]); if (!binary) { VLOG_ERR("%s not found in PATH", argv[0]); return ENOENT; } free(binary); return 0; } /* Creates and returns a new struct process with the specified 'name' and * 'pid'. */ static struct process * process_register(const char *name, pid_t pid) { struct process *p; const char *slash; p = xzalloc(sizeof *p); p->pid = pid; slash = strrchr(name, '/'); p->name = xstrdup(slash ? slash + 1 : name); p->exited = false; list_push_back(&all_processes, &p->node); return p; } #ifndef _WIN32 static bool rlim_is_finite(rlim_t limit) { if (limit == RLIM_INFINITY) { return false; } #ifdef RLIM_SAVED_CUR /* FreeBSD 8.0 lacks RLIM_SAVED_CUR. */ if (limit == RLIM_SAVED_CUR) { return false; } #endif #ifdef RLIM_SAVED_MAX /* FreeBSD 8.0 lacks RLIM_SAVED_MAX. */ if (limit == RLIM_SAVED_MAX) { return false; } #endif return true; } /* Returns the maximum valid FD value, plus 1. */ static int get_max_fds(void) { static int max_fds; if (!max_fds) { struct rlimit r; if (!getrlimit(RLIMIT_NOFILE, &r) && rlim_is_finite(r.rlim_cur)) { max_fds = r.rlim_cur; } else { VLOG_WARN("failed to obtain fd limit, defaulting to 1024"); max_fds = 1024; } } return max_fds; } #endif /* _WIN32 */ /* Starts a subprocess with the arguments in the null-terminated argv[] array. * argv[0] is used as the name of the process. Searches the PATH environment * variable to find the program to execute. * * This function may not be called after creating any additional threads. * * All file descriptors are closed before executing the subprocess, except for * fds 0, 1, and 2. * * Returns 0 if successful, otherwise a positive errno value indicating the * error. If successful, '*pp' is assigned a new struct process that may be * used to query the process's status. On failure, '*pp' is set to NULL. */ int process_start(char **argv, struct process **pp) { #ifndef _WIN32 pid_t pid; int error; sigset_t prev_mask; assert_single_threaded(); *pp = NULL; COVERAGE_INC(process_start); error = process_prestart(argv); if (error) { return error; } fatal_signal_block(&prev_mask); pid = fork(); if (pid < 0) { VLOG_WARN("fork failed: %s", ovs_strerror(errno)); error = errno; } else if (pid) { /* Running in parent process. */ *pp = process_register(argv[0], pid); error = 0; } else { /* Running in child process. */ int fd_max = get_max_fds(); int fd; fatal_signal_fork(); for (fd = 3; fd < fd_max; fd++) { close(fd); } xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); execvp(argv[0], argv); fprintf(stderr, "execvp(\"%s\") failed: %s\n", argv[0], ovs_strerror(errno)); _exit(1); } xpthread_sigmask(SIG_SETMASK, &prev_mask, NULL); return error; #else *pp = NULL; return ENOSYS; #endif } /* Destroys process 'p'. */ void process_destroy(struct process *p) { if (p) { list_remove(&p->node); free(p->name); free(p); } } /* Sends signal 'signr' to process 'p'. Returns 0 if successful, otherwise a * positive errno value. */ int process_kill(const struct process *p, int signr) { #ifndef _WIN32 return (p->exited ? ESRCH : !kill(p->pid, signr) ? 0 : errno); #else return ENOSYS; #endif } /* Returns the pid of process 'p'. */ pid_t process_pid(const struct process *p) { return p->pid; } /* Returns the name of process 'p' (the name passed to process_start() with any * leading directories stripped). */ const char * process_name(const struct process *p) { return p->name; } /* Returns true if process 'p' has exited, false otherwise. */ bool process_exited(struct process *p) { return p->exited; } /* Returns process 'p''s exit status, as reported by waitpid(2). * process_status(p) may be called only after process_exited(p) has returned * true. */ int process_status(const struct process *p) { ovs_assert(p->exited); return p->status; } /* Given 'status', which is a process status in the form reported by waitpid(2) * and returned by process_status(), returns a string describing how the * process terminated. The caller is responsible for freeing the string when * it is no longer needed. */ char * process_status_msg(int status) { struct ds ds = DS_EMPTY_INITIALIZER; #ifndef _WIN32 if (WIFEXITED(status)) { ds_put_format(&ds, "exit status %d", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { char namebuf[SIGNAL_NAME_BUFSIZE]; ds_put_format(&ds, "killed (%s)", signal_name(WTERMSIG(status), namebuf, sizeof namebuf)); } else if (WIFSTOPPED(status)) { char namebuf[SIGNAL_NAME_BUFSIZE]; ds_put_format(&ds, "stopped (%s)", signal_name(WSTOPSIG(status), namebuf, sizeof namebuf)); } else { ds_put_format(&ds, "terminated abnormally (%x)", status); } if (WCOREDUMP(status)) { ds_put_cstr(&ds, ", core dumped"); } #else ds_put_cstr(&ds, "function not supported."); #endif return ds_cstr(&ds); } /* Executes periodic maintenance activities required by the process module. */ void process_run(void) { #ifndef _WIN32 char buf[_POSIX_PIPE_BUF]; if (!list_is_empty(&all_processes) && read(fds[0], buf, sizeof buf) > 0) { struct process *p; LIST_FOR_EACH (p, node, &all_processes) { if (!p->exited) { int retval, status; do { retval = waitpid(p->pid, &status, WNOHANG); } while (retval == -1 && errno == EINTR); if (retval == p->pid) { p->exited = true; p->status = status; } else if (retval < 0) { VLOG_WARN("waitpid: %s", ovs_strerror(errno)); p->exited = true; p->status = -1; } } } } #endif } /* Causes the next call to poll_block() to wake up when process 'p' has * exited. */ void process_wait(struct process *p) { #ifndef _WIN32 if (p->exited) { poll_immediate_wake(); } else { poll_fd_wait(fds[0], POLLIN); } #else OVS_NOT_REACHED(); #endif } char * process_search_path(const char *name) { char *save_ptr = NULL; char *path, *dir; struct stat s; if (strchr(name, '/') || !getenv("PATH")) { return stat(name, &s) == 0 ? xstrdup(name) : NULL; } path = xstrdup(getenv("PATH")); for (dir = strtok_r(path, ":", &save_ptr); dir; dir = strtok_r(NULL, ":", &save_ptr)) { char *file = xasprintf("%s/%s", dir, name); if (stat(file, &s) == 0) { free(path); return file; } free(file); } free(path); return NULL; } static void sigchld_handler(int signr OVS_UNUSED) { ignore(write(fds[1], "", 1)); } openvswitch-2.5.9/lib/PaxHeaders.82075/route-table.c0000644000000000000000000000013213534540071017001 xustar0030 mtime=1567801401.581682492 30 atime=1567801402.093686252 30 ctime=1567801424.989854959 openvswitch-2.5.9/lib/route-table.c0000644000175000017500000002277513534540071020504 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "route-table.h" #include #include #include #include #include #include "hash.h" #include "netlink.h" #include "netlink-notifier.h" #include "netlink-socket.h" #include "ofpbuf.h" #include "ovs-router.h" #include "packets.h" #include "rtnetlink.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(route_table); struct route_data { /* Copied from struct rtmsg. */ unsigned char rtm_dst_len; /* Extracted from Netlink attributes. */ struct in6_addr rta_dst; /* 0 if missing. */ struct in6_addr rta_gw; char ifname[IFNAMSIZ]; /* Interface name. */ }; /* A digested version of a route message sent down by the kernel to indicate * that a route has changed. */ struct route_table_msg { bool relevant; /* Should this message be processed? */ int nlmsg_type; /* e.g. RTM_NEWROUTE, RTM_DELROUTE. */ struct route_data rd; /* Data parsed from this message. */ }; static struct ovs_mutex route_table_mutex = OVS_MUTEX_INITIALIZER; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); /* Global change number for route-table, which should be incremented * every time route_table_reset() is called. */ static uint64_t rt_change_seq; static struct nln *nln = NULL; static struct nln *nln6 = NULL; static struct route_table_msg rtmsg; static struct nln_notifier *route_notifier = NULL; static struct nln_notifier *route6_notifier = NULL; static struct nln_notifier *name_notifier = NULL; static bool route_table_valid = false; static int route_table_reset(void); static void route_table_handle_msg(const struct route_table_msg *); static bool route_table_parse(struct ofpbuf *, struct route_table_msg *); static void route_table_change(const struct route_table_msg *, void *); static void route_map_clear(void); static void name_table_init(void); static void name_table_change(const struct rtnetlink_change *, void *); uint64_t route_table_get_change_seq(void) { return rt_change_seq; } /* Users of the route_table module should register themselves with this * function before making any other route_table function calls. */ void route_table_init(void) OVS_EXCLUDED(route_table_mutex) { ovs_mutex_lock(&route_table_mutex); ovs_assert(!nln); ovs_assert(!nln6); ovs_assert(!route_notifier); ovs_assert(!route6_notifier); ovs_router_init(); nln = nln_create(NETLINK_ROUTE, RTNLGRP_IPV4_ROUTE, (nln_parse_func *) route_table_parse, &rtmsg); nln6 = nln_create(NETLINK_ROUTE, RTNLGRP_IPV6_ROUTE, (nln_parse_func *) route_table_parse, &rtmsg); route_notifier = nln_notifier_create(nln, (nln_notify_func *) route_table_change, NULL); route6_notifier = nln_notifier_create(nln6, (nln_notify_func *) route_table_change, NULL); route_table_reset(); name_table_init(); ovs_mutex_unlock(&route_table_mutex); } /* Run periodically to update the locally maintained routing table. */ void route_table_run(void) OVS_EXCLUDED(route_table_mutex) { ovs_mutex_lock(&route_table_mutex); if (nln || nln6) { rtnetlink_run(); if (nln) { nln_run(nln); } if (nln6) { nln_run(nln6); } if (!route_table_valid) { route_table_reset(); } } ovs_mutex_unlock(&route_table_mutex); } /* Causes poll_block() to wake up when route_table updates are required. */ void route_table_wait(void) OVS_EXCLUDED(route_table_mutex) { ovs_mutex_lock(&route_table_mutex); if (nln || nln6) { rtnetlink_wait(); if (nln) { nln_wait(nln); } if (nln6) { nln_wait(nln6); } } ovs_mutex_unlock(&route_table_mutex); } static int route_table_reset(void) { struct nl_dump dump; struct rtgenmsg *rtmsg; uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; struct ofpbuf request, reply, buf; route_map_clear(); route_table_valid = true; rt_change_seq++; ofpbuf_init(&request, 0); nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST); rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg); rtmsg->rtgen_family = AF_UNSPEC; nl_dump_start(&dump, NETLINK_ROUTE, &request); ofpbuf_uninit(&request); ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); while (nl_dump_next(&dump, &reply, &buf)) { struct route_table_msg msg; if (route_table_parse(&reply, &msg)) { route_table_handle_msg(&msg); } } ofpbuf_uninit(&buf); return nl_dump_done(&dump); } static bool route_table_parse(struct ofpbuf *buf, struct route_table_msg *change) { bool parsed, ipv4 = false; static const struct nl_policy policy[] = { [RTA_DST] = { .type = NL_A_U32, .optional = true }, [RTA_OIF] = { .type = NL_A_U32, .optional = false }, [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true }, }; static const struct nl_policy policy6[] = { [RTA_DST] = { .type = NL_A_IPV6, .optional = true }, [RTA_OIF] = { .type = NL_A_U32, .optional = true }, [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; const struct rtmsg *rtm; rtm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *rtm); if (rtm->rtm_family == AF_INET) { parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg), policy, attrs, ARRAY_SIZE(policy)); ipv4 = true; } else if (rtm->rtm_family == AF_INET6) { parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct rtmsg), policy6, attrs, ARRAY_SIZE(policy6)); } else { VLOG_DBG_RL(&rl, "received non AF_INET rtnetlink route message"); return false; } if (parsed) { const struct nlmsghdr *nlmsg; int rta_oif; /* Output interface index. */ nlmsg = buf->data; memset(change, 0, sizeof *change); change->relevant = true; if (rtm->rtm_scope == RT_SCOPE_NOWHERE) { change->relevant = false; } if (rtm->rtm_type != RTN_UNICAST && rtm->rtm_type != RTN_LOCAL) { change->relevant = false; } change->nlmsg_type = nlmsg->nlmsg_type; change->rd.rtm_dst_len = rtm->rtm_dst_len + (ipv4 ? 96 : 0); if (attrs[RTA_OIF]) { rta_oif = nl_attr_get_u32(attrs[RTA_OIF]); if (!if_indextoname(rta_oif, change->rd.ifname)) { int error = errno; VLOG_DBG_RL(&rl, "Could not find interface name[%u]: %s", rta_oif, ovs_strerror(error)); if (error == ENXIO) { change->relevant = false; } else { return false; } } } if (attrs[RTA_DST]) { if (ipv4) { ovs_be32 dst; dst = nl_attr_get_be32(attrs[RTA_DST]); in6_addr_set_mapped_ipv4(&change->rd.rta_dst, dst); } else { change->rd.rta_dst = nl_attr_get_in6_addr(attrs[RTA_DST]); } } else if (ipv4) { in6_addr_set_mapped_ipv4(&change->rd.rta_dst, 0); } if (attrs[RTA_GATEWAY]) { if (ipv4) { ovs_be32 gw; gw = nl_attr_get_be32(attrs[RTA_GATEWAY]); in6_addr_set_mapped_ipv4(&change->rd.rta_gw, gw); } else { change->rd.rta_gw = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]); } } } else { VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message"); } return parsed; } static void route_table_change(const struct route_table_msg *change OVS_UNUSED, void *aux OVS_UNUSED) { route_table_valid = false; } static void route_table_handle_msg(const struct route_table_msg *change) { if (change->relevant && change->nlmsg_type == RTM_NEWROUTE) { const struct route_data *rd = &change->rd; ovs_router_insert(&rd->rta_dst, rd->rtm_dst_len, rd->ifname, &rd->rta_gw); } } static void route_map_clear(void) { ovs_router_flush(); } bool route_table_fallback_lookup(ovs_be32 ip_dst OVS_UNUSED, char output_bridge[] OVS_UNUSED, ovs_be32 *gw) { *gw = 0; return false; } /* name_table . */ static void name_table_init(void) { name_notifier = rtnetlink_notifier_create(name_table_change, NULL); } static void name_table_change(const struct rtnetlink_change *change OVS_UNUSED, void *aux OVS_UNUSED) { /* Changes to interface status can cause routing table changes that some * versions of the linux kernel do not advertise for some reason. */ route_table_valid = false; } openvswitch-2.5.9/lib/PaxHeaders.82075/simap.c0000644000000000000000000000013113534540071015666 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.877854134 openvswitch-2.5.9/lib/simap.c0000644000175000017500000001643713534540071017370 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "simap.h" #include "hash.h" static size_t hash_name(const char *, size_t length); static struct simap_node *simap_find__(const struct simap *, const char *name, size_t name_len, size_t hash); static struct simap_node *simap_add_nocopy__(struct simap *, char *name, unsigned int data, size_t hash); static int compare_nodes_by_name(const void *a_, const void *b_); /* Initializes 'simap' as an empty string-to-integer map. */ void simap_init(struct simap *simap) { hmap_init(&simap->map); } /* Frees all the data that 'simap' contains. */ void simap_destroy(struct simap *simap) { if (simap) { simap_clear(simap); hmap_destroy(&simap->map); } } /* Exchanges the contents of 'a' and 'b'. */ void simap_swap(struct simap *a, struct simap *b) { hmap_swap(&a->map, &b->map); } /* Adjusts 'simap' so that it is still valid after it has been moved around in * memory (e.g. due to realloc()). */ void simap_moved(struct simap *simap) { hmap_moved(&simap->map); } /* Removes all of the mappings from 'simap' and frees them. */ void simap_clear(struct simap *simap) { struct simap_node *node, *next; SIMAP_FOR_EACH_SAFE (node, next, simap) { hmap_remove(&simap->map, &node->node); free(node->name); free(node); } } /* Returns true if 'simap' contains no mappings, false if it contains at least * one. */ bool simap_is_empty(const struct simap *simap) { return hmap_is_empty(&simap->map); } /* Returns the number of mappings in 'simap'. */ size_t simap_count(const struct simap *simap) { return hmap_count(&simap->map); } /* Inserts a mapping from 'name' to 'data' into 'simap', replacing any * existing mapping for 'name'. Returns true if a new mapping was added, * false if an existing mapping's value was replaced. * * The caller retains ownership of 'name'. */ bool simap_put(struct simap *simap, const char *name, unsigned int data) { size_t length = strlen(name); size_t hash = hash_name(name, length); struct simap_node *node; node = simap_find__(simap, name, length, hash); if (node) { node->data = data; return false; } else { simap_add_nocopy__(simap, xmemdup0(name, length), data, hash); return true; } } /* Increases the data value in the mapping for 'name' by 'amt', or inserts a * mapping from 'name' to 'amt' if no such mapping exists. Returns the * new total data value for the mapping. * * If 'amt' is zero, this function does nothing and returns 0. That is, this * function won't create a mapping with a initial value of 0. * * The caller retains ownership of 'name'. */ unsigned int simap_increase(struct simap *simap, const char *name, unsigned int amt) { if (amt) { size_t length = strlen(name); size_t hash = hash_name(name, length); struct simap_node *node; node = simap_find__(simap, name, length, hash); if (node) { node->data += amt; } else { node = simap_add_nocopy__(simap, xmemdup0(name, length), amt, hash); } return node->data; } else { return 0; } } /* Deletes 'node' from 'simap' and frees its associated memory. */ void simap_delete(struct simap *simap, struct simap_node *node) { hmap_remove(&simap->map, &node->node); free(node->name); free(node); } /* Searches for 'name' in 'simap'. If found, deletes it and returns true. If * not found, returns false without modifying 'simap'. */ bool simap_find_and_delete(struct simap *simap, const char *name) { struct simap_node *node = simap_find(simap, name); if (node) { simap_delete(simap, node); return true; } return false; } /* Searches 'simap' for a mapping with the given 'name'. Returns it, if found, * or a null pointer if not. */ struct simap_node * simap_find(const struct simap *simap, const char *name) { return simap_find_len(simap, name, strlen(name)); } /* Searches 'simap' for a mapping whose name is the first 'name_len' bytes * starting at 'name'. Returns it, if found, or a null pointer if not. */ struct simap_node * simap_find_len(const struct simap *simap, const char *name, size_t len) { return simap_find__(simap, name, len, hash_name(name, len)); } /* Searches 'simap' for a mapping with the given 'name'. Returns the * associated data value, if found, otherwise zero. */ unsigned int simap_get(const struct simap *simap, const char *name) { struct simap_node *node = simap_find(simap, name); return node ? node->data : 0; } /* Returns true if 'simap' contains a copy of 'name', false otherwise. */ bool simap_contains(const struct simap *simap, const char *name) { return simap_find(simap, name) != NULL; } /* Returns an array that contains a pointer to each mapping in 'simap', * ordered alphabetically by name. The returned array has simap_count(simap) * elements. * * The caller is responsible for freeing the returned array (with free()). It * should not free the individual "simap_node"s in the array, because they are * still part of 'simap'. */ const struct simap_node ** simap_sort(const struct simap *simap) { if (simap_is_empty(simap)) { return NULL; } else { const struct simap_node **nodes; struct simap_node *node; size_t i, n; n = simap_count(simap); nodes = xmalloc(n * sizeof *nodes); i = 0; SIMAP_FOR_EACH (node, simap) { nodes[i++] = node; } ovs_assert(i == n); qsort(nodes, n, sizeof *nodes, compare_nodes_by_name); return nodes; } } static size_t hash_name(const char *name, size_t length) { return hash_bytes(name, length, 0); } static struct simap_node * simap_find__(const struct simap *simap, const char *name, size_t name_len, size_t hash) { struct simap_node *node; HMAP_FOR_EACH_WITH_HASH (node, node, hash, &simap->map) { if (!strncmp(node->name, name, name_len) && !node->name[name_len]) { return node; } } return NULL; } static struct simap_node * simap_add_nocopy__(struct simap *simap, char *name, unsigned int data, size_t hash) { struct simap_node *node = xmalloc(sizeof *node); node->name = name; node->data = data; hmap_insert(&simap->map, &node->node, hash); return node; } static int compare_nodes_by_name(const void *a_, const void *b_) { const struct simap_node *const *a = a_; const struct simap_node *const *b = b_; return strcmp((*a)->name, (*b)->name); } openvswitch-2.5.9/lib/PaxHeaders.82075/vconn-stream.c0000644000000000000000000000013213534540071017172 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.937854576 openvswitch-2.5.9/lib/vconn-stream.c0000644000175000017500000002504213534540071020663 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "fatal-signal.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "socket-util.h" #include "stream.h" #include "util.h" #include "vconn-provider.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(vconn_stream); /* Active stream socket vconn. */ struct vconn_stream { struct vconn vconn; struct stream *stream; struct ofpbuf *rxbuf; struct ofpbuf *txbuf; int n_packets; }; static const struct vconn_class stream_vconn_class; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); static void vconn_stream_clear_txbuf(struct vconn_stream *); static struct vconn * vconn_stream_new(struct stream *stream, int connect_status, uint32_t allowed_versions) { struct vconn_stream *s; s = xmalloc(sizeof *s); vconn_init(&s->vconn, &stream_vconn_class, connect_status, stream_get_name(stream), allowed_versions); s->stream = stream; s->txbuf = NULL; s->rxbuf = NULL; s->n_packets = 0; return &s->vconn; } /* Creates a new vconn that will send and receive data on a stream named 'name' * and stores a pointer to the vconn in '*vconnp'. * * Returns 0 if successful, otherwise a positive errno value. */ static int vconn_stream_open(const char *name, uint32_t allowed_versions, char *suffix OVS_UNUSED, struct vconn **vconnp, uint8_t dscp) { struct stream *stream; int error; error = stream_open_with_default_port(name, OFP_PORT, &stream, dscp); if (!error) { error = stream_connect(stream); if (!error || error == EAGAIN) { *vconnp = vconn_stream_new(stream, error, allowed_versions); return 0; } } stream_close(stream); return error; } static struct vconn_stream * vconn_stream_cast(struct vconn *vconn) { return CONTAINER_OF(vconn, struct vconn_stream, vconn); } static void vconn_stream_close(struct vconn *vconn) { struct vconn_stream *s = vconn_stream_cast(vconn); if ((vconn->error == EPROTO || s->n_packets < 1) && s->rxbuf) { stream_report_content(s->rxbuf->data, s->rxbuf->size, STREAM_OPENFLOW, THIS_MODULE, vconn_get_name(vconn)); } stream_close(s->stream); vconn_stream_clear_txbuf(s); ofpbuf_delete(s->rxbuf); free(s); } static int vconn_stream_connect(struct vconn *vconn) { struct vconn_stream *s = vconn_stream_cast(vconn); return stream_connect(s->stream); } static int vconn_stream_recv__(struct vconn_stream *s, int rx_len) { struct ofpbuf *rx = s->rxbuf; int want_bytes, retval; want_bytes = rx_len - rx->size; ofpbuf_prealloc_tailroom(rx, want_bytes); retval = stream_recv(s->stream, ofpbuf_tail(rx), want_bytes); if (retval > 0) { rx->size += retval; return retval == want_bytes ? 0 : EAGAIN; } else if (retval == 0) { if (rx->size) { VLOG_ERR_RL(&rl, "connection dropped mid-packet"); return EPROTO; } return EOF; } else { return -retval; } } static int vconn_stream_recv(struct vconn *vconn, struct ofpbuf **bufferp) { struct vconn_stream *s = vconn_stream_cast(vconn); const struct ofp_header *oh; int rx_len; /* Allocate new receive buffer if we don't have one. */ if (s->rxbuf == NULL) { s->rxbuf = ofpbuf_new(1564); } /* Read ofp_header. */ if (s->rxbuf->size < sizeof(struct ofp_header)) { int retval = vconn_stream_recv__(s, sizeof(struct ofp_header)); if (retval) { return retval; } } /* Read payload. */ oh = s->rxbuf->data; rx_len = ntohs(oh->length); if (rx_len < sizeof(struct ofp_header)) { VLOG_ERR_RL(&rl, "received too-short ofp_header (%d bytes)", rx_len); return EPROTO; } else if (s->rxbuf->size < rx_len) { int retval = vconn_stream_recv__(s, rx_len); if (retval) { return retval; } } s->n_packets++; *bufferp = s->rxbuf; s->rxbuf = NULL; return 0; } static void vconn_stream_clear_txbuf(struct vconn_stream *s) { ofpbuf_delete(s->txbuf); s->txbuf = NULL; } static int vconn_stream_send(struct vconn *vconn, struct ofpbuf *buffer) { struct vconn_stream *s = vconn_stream_cast(vconn); ssize_t retval; if (s->txbuf) { return EAGAIN; } retval = stream_send(s->stream, buffer->data, buffer->size); if (retval == buffer->size) { ofpbuf_delete(buffer); return 0; } else if (retval >= 0 || retval == -EAGAIN) { s->txbuf = buffer; if (retval > 0) { ofpbuf_pull(buffer, retval); } return 0; } else { return -retval; } } static void vconn_stream_run(struct vconn *vconn) { struct vconn_stream *s = vconn_stream_cast(vconn); ssize_t retval; stream_run(s->stream); if (!s->txbuf) { return; } retval = stream_send(s->stream, s->txbuf->data, s->txbuf->size); if (retval < 0) { if (retval != -EAGAIN) { VLOG_ERR_RL(&rl, "send: %s", ovs_strerror(-retval)); vconn_stream_clear_txbuf(s); return; } } else if (retval > 0) { ofpbuf_pull(s->txbuf, retval); if (!s->txbuf->size) { vconn_stream_clear_txbuf(s); return; } } } static void vconn_stream_run_wait(struct vconn *vconn) { struct vconn_stream *s = vconn_stream_cast(vconn); stream_run_wait(s->stream); if (s->txbuf) { stream_send_wait(s->stream); } } static void vconn_stream_wait(struct vconn *vconn, enum vconn_wait_type wait) { struct vconn_stream *s = vconn_stream_cast(vconn); switch (wait) { case WAIT_CONNECT: stream_connect_wait(s->stream); break; case WAIT_SEND: if (!s->txbuf) { stream_send_wait(s->stream); } else { /* Nothing to do: need to drain txbuf first. * vconn_stream_run_wait() will arrange to wake up when there room * to send data, so there's no point in calling poll_fd_wait() * redundantly here. */ } break; case WAIT_RECV: stream_recv_wait(s->stream); break; default: OVS_NOT_REACHED(); } } /* Passive stream socket vconn. */ struct pvconn_pstream { struct pvconn pvconn; struct pstream *pstream; }; static const struct pvconn_class pstream_pvconn_class; static struct pvconn_pstream * pvconn_pstream_cast(struct pvconn *pvconn) { return CONTAINER_OF(pvconn, struct pvconn_pstream, pvconn); } /* Creates a new pvconn named 'name' that will accept new connections using * pstream_accept() and stores a pointer to the pvconn in '*pvconnp'. * * Returns 0 if successful, otherwise a positive errno value. (The current * implementation never fails.) */ static int pvconn_pstream_listen(const char *name, uint32_t allowed_versions, char *suffix OVS_UNUSED, struct pvconn **pvconnp, uint8_t dscp) { struct pvconn_pstream *ps; struct pstream *pstream; int error; error = pstream_open_with_default_port(name, OFP_PORT, &pstream, dscp); if (error) { return error; } ps = xmalloc(sizeof *ps); pvconn_init(&ps->pvconn, &pstream_pvconn_class, name, allowed_versions); ps->pstream = pstream; *pvconnp = &ps->pvconn; return 0; } static void pvconn_pstream_close(struct pvconn *pvconn) { struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); pstream_close(ps->pstream); free(ps); } static int pvconn_pstream_accept(struct pvconn *pvconn, struct vconn **new_vconnp) { struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); struct stream *stream; int error; error = pstream_accept(ps->pstream, &stream); if (error) { if (error != EAGAIN) { VLOG_DBG_RL(&rl, "%s: accept: %s", pstream_get_name(ps->pstream), ovs_strerror(error)); } return error; } *new_vconnp = vconn_stream_new(stream, 0, pvconn->allowed_versions); return 0; } static void pvconn_pstream_wait(struct pvconn *pvconn) { struct pvconn_pstream *ps = pvconn_pstream_cast(pvconn); pstream_wait(ps->pstream); } /* Stream-based vconns and pvconns. */ #define STREAM_INIT(NAME) \ { \ NAME, \ vconn_stream_open, \ vconn_stream_close, \ vconn_stream_connect, \ vconn_stream_recv, \ vconn_stream_send, \ vconn_stream_run, \ vconn_stream_run_wait, \ vconn_stream_wait, \ } #define PSTREAM_INIT(NAME) \ { \ NAME, \ pvconn_pstream_listen, \ pvconn_pstream_close, \ pvconn_pstream_accept, \ pvconn_pstream_wait \ } static const struct vconn_class stream_vconn_class = STREAM_INIT("stream"); static const struct pvconn_class pstream_pvconn_class = PSTREAM_INIT("pstream"); const struct vconn_class tcp_vconn_class = STREAM_INIT("tcp"); const struct pvconn_class ptcp_pvconn_class = PSTREAM_INIT("ptcp"); const struct vconn_class unix_vconn_class = STREAM_INIT("unix"); const struct pvconn_class punix_pvconn_class = PSTREAM_INIT("punix"); #ifdef HAVE_OPENSSL const struct vconn_class ssl_vconn_class = STREAM_INIT("ssl"); const struct pvconn_class pssl_pvconn_class = PSTREAM_INIT("pssl"); #endif openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-util.c0000644000000000000000000000013213534540071016315 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.801853573 openvswitch-2.5.9/lib/ofp-util.c0000644000175000017500000115762613534540071020025 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofp-print.h" #include #include #include #include #include #include #include #include "bundle.h" #include "byte-order.h" #include "classifier.h" #include "dynamic-string.h" #include "learn.h" #include "meta-flow.h" #include "multipath.h" #include "netdev.h" #include "nx-match.h" #include "id-pool.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/netronome-ext.h" #include "packets.h" #include "random.h" #include "tun-metadata.h" #include "unaligned.h" #include "type-props.h" #include "openvswitch/vlog.h" #include "bitmap.h" VLOG_DEFINE_THIS_MODULE(ofp_util); /* Rate limit for OpenFlow message parse errors. These always indicate a bug * in the peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); static enum ofputil_table_vacancy ofputil_decode_table_vacancy( ovs_be32 config, enum ofp_version); static enum ofputil_table_eviction ofputil_decode_table_eviction( ovs_be32 config, enum ofp_version); static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss, enum ofputil_table_eviction, enum ofputil_table_vacancy, enum ofp_version); struct ofp_prop_header { ovs_be16 type; ovs_be16 len; }; struct ofp_prop_experimenter { ovs_be16 type; /* OFP*_EXPERIMENTER. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* Experimenter ID which takes the same form as * in struct ofp_experimenter_header. */ ovs_be32 exp_type; /* Experimenter defined. */ }; /* Pulls a property, beginning with struct ofp_prop_header, from the beginning * of 'msg'. Stores the type of the property in '*typep' and, if 'property' is * nonnull, the entire property, including the header, in '*property'. Returns * 0 if successful, otherwise an error code. * * This function pulls the property's stated size padded out to a multiple of * 'alignment' bytes. The common case in OpenFlow is an 'alignment' of 8, so * you can use ofputil_pull_property() for that case. */ static enum ofperr ofputil_pull_property__(struct ofpbuf *msg, struct ofpbuf *property, unsigned int alignment, uint16_t *typep) { struct ofp_prop_header *oph; unsigned int padded_len; unsigned int len; if (msg->size < sizeof *oph) { return OFPERR_OFPBPC_BAD_LEN; } oph = msg->data; len = ntohs(oph->len); padded_len = ROUND_UP(len, alignment); if (len < sizeof *oph || padded_len > msg->size) { return OFPERR_OFPBPC_BAD_LEN; } *typep = ntohs(oph->type); if (property) { ofpbuf_use_const(property, msg->data, len); } ofpbuf_pull(msg, padded_len); return 0; } /* Pulls a property, beginning with struct ofp_prop_header, from the beginning * of 'msg'. Stores the type of the property in '*typep' and, if 'property' is * nonnull, the entire property, including the header, in '*property'. Returns * 0 if successful, otherwise an error code. * * This function pulls the property's stated size padded out to a multiple of * 8 bytes, which is the common case for OpenFlow properties. */ static enum ofperr ofputil_pull_property(struct ofpbuf *msg, struct ofpbuf *property, uint16_t *typep) { return ofputil_pull_property__(msg, property, 8, typep); } static void OVS_PRINTF_FORMAT(2, 3) log_property(bool loose, const char *message, ...) { enum vlog_level level = loose ? VLL_DBG : VLL_WARN; if (!vlog_should_drop(THIS_MODULE, level, &bad_ofmsg_rl)) { va_list args; va_start(args, message); vlog_valist(THIS_MODULE, level, message, args); va_end(args); } } static enum ofperr ofputil_check_mask(uint16_t type, uint32_t mask) { switch (type) { case OFPACPT_PACKET_IN_SLAVE: case OFPACPT_PACKET_IN_MASTER: if (mask > MAXIMUM_MASK_PACKET_IN) { return OFPERR_OFPACFC_INVALID; } break; case OFPACPT_FLOW_REMOVED_SLAVE: case OFPACPT_FLOW_REMOVED_MASTER: if (mask > MAXIMUM_MASK_FLOW_REMOVED) { return OFPERR_OFPACFC_INVALID; } break; case OFPACPT_PORT_STATUS_SLAVE: case OFPACPT_PORT_STATUS_MASTER: if (mask > MAXIMUM_MASK_PORT_STATUS) { return OFPERR_OFPACFC_INVALID; } break; case OFPACPT_ROLE_STATUS_SLAVE: case OFPACPT_ROLE_STATUS_MASTER: if (mask > MAXIMUM_MASK_ROLE_STATUS) { return OFPERR_OFPACFC_INVALID; } break; case OFPACPT_TABLE_STATUS_SLAVE: case OFPACPT_TABLE_STATUS_MASTER: if ((mask < MINIMUM_MASK_TABLE_STATUS && mask != 0) | (mask > MAXIMUM_MASK_TABLE_STATUS)) { return OFPERR_OFPACFC_INVALID; } break; case OFPACPT_REQUESTFORWARD_SLAVE: case OFPACPT_REQUESTFORWARD_MASTER: if (mask > MAXIMUM_MASK_REQUESTFORWARD) { return OFPERR_OFPACFC_INVALID; } break; } return 0; } static size_t start_property(struct ofpbuf *msg, uint16_t type) { size_t start_ofs = msg->size; struct ofp_prop_header *oph; oph = ofpbuf_put_uninit(msg, sizeof *oph); oph->type = htons(type); oph->len = htons(4); /* May be updated later by end_property(). */ return start_ofs; } static void end_property(struct ofpbuf *msg, size_t start_ofs) { struct ofp_prop_header *oph; oph = ofpbuf_at_assert(msg, start_ofs, sizeof *oph); oph->len = htons(msg->size - start_ofs); ofpbuf_padto(msg, ROUND_UP(msg->size, 8)); } static void put_bitmap_properties(struct ofpbuf *msg, uint64_t bitmap) { for (; bitmap; bitmap = zero_rightmost_1bit(bitmap)) { start_property(msg, rightmost_1bit_idx(bitmap)); } } /* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns * an IP netmask with a 1 in each bit that must match and a 0 in each bit that * is wildcarded. * * The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0 * is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits, * ..., 32 and higher wildcard the entire field. This is the *opposite* of the * usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are * wildcarded. */ ovs_be32 ofputil_wcbits_to_netmask(int wcbits) { wcbits &= 0x3f; return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0; } /* Given the IP netmask 'netmask', returns the number of bits of the IP address * that it wildcards, that is, the number of 0-bits in 'netmask', a number * between 0 and 32 inclusive. * * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will * still be in the valid range but isn't otherwise meaningful. */ int ofputil_netmask_to_wcbits(ovs_be32 netmask) { return 32 - ip_count_cidr_bits(netmask); } /* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a * flow_wildcards in 'wc' for use in struct match. It is the caller's * responsibility to handle the special case where the flow match's dl_vlan is * set to OFP_VLAN_NONE. */ void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); if (!(ofpfw & OFPFW10_IN_PORT)) { wc->masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX); } if (!(ofpfw & OFPFW10_NW_TOS)) { wc->masks.nw_tos |= IP_DSCP_MASK; } if (!(ofpfw & OFPFW10_NW_PROTO)) { wc->masks.nw_proto = UINT8_MAX; } wc->masks.nw_src = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT); wc->masks.nw_dst = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT); if (!(ofpfw & OFPFW10_TP_SRC)) { wc->masks.tp_src = OVS_BE16_MAX; } if (!(ofpfw & OFPFW10_TP_DST)) { wc->masks.tp_dst = OVS_BE16_MAX; } if (!(ofpfw & OFPFW10_DL_SRC)) { WC_MASK_FIELD(wc, dl_src); } if (!(ofpfw & OFPFW10_DL_DST)) { WC_MASK_FIELD(wc, dl_dst); } if (!(ofpfw & OFPFW10_DL_TYPE)) { wc->masks.dl_type = OVS_BE16_MAX; } /* VLAN TCI mask. */ if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) { wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI); } if (!(ofpfw & OFPFW10_DL_VLAN)) { wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); } } /* Converts the ofp10_match in 'ofmatch' into a struct match in 'match'. */ void ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch, struct match *match) { uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL; /* Initialize match->wc. */ memset(&match->flow, 0, sizeof match->flow); ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc); memset(&match->tun_md, 0, sizeof match->tun_md); /* Initialize most of match->flow. */ match->flow.nw_src = ofmatch->nw_src; match->flow.nw_dst = ofmatch->nw_dst; match->flow.in_port.ofp_port = u16_to_ofp(ntohs(ofmatch->in_port)); match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type); match->flow.tp_src = ofmatch->tp_src; match->flow.tp_dst = ofmatch->tp_dst; match->flow.dl_src = ofmatch->dl_src; match->flow.dl_dst = ofmatch->dl_dst; match->flow.nw_tos = ofmatch->nw_tos & IP_DSCP_MASK; match->flow.nw_proto = ofmatch->nw_proto; /* Translate VLANs. */ if (!(ofpfw & OFPFW10_DL_VLAN) && ofmatch->dl_vlan == htons(OFP10_VLAN_NONE)) { /* Match only packets without 802.1Q header. * * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct. * * If OFPFW10_DL_VLAN_PCP is matched, the flow match is contradictory, * because we can't have a specific PCP without an 802.1Q header. * However, older versions of OVS treated this as matching packets * withut an 802.1Q header, so we do here too. */ match->flow.vlan_tci = htons(0); match->wc.masks.vlan_tci = htons(0xffff); } else { ovs_be16 vid, pcp, tci; uint16_t hpcp; vid = ofmatch->dl_vlan & htons(VLAN_VID_MASK); hpcp = (ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK; pcp = htons(hpcp); tci = vid | pcp | htons(VLAN_CFI); match->flow.vlan_tci = tci & match->wc.masks.vlan_tci; } /* Clean up. */ match_zero_wildcarded_fields(match); } /* Convert 'match' into the OpenFlow 1.0 match structure 'ofmatch'. */ void ofputil_match_to_ofp10_match(const struct match *match, struct ofp10_match *ofmatch) { const struct flow_wildcards *wc = &match->wc; uint32_t ofpfw; /* Figure out most OpenFlow wildcards. */ ofpfw = 0; if (!wc->masks.in_port.ofp_port) { ofpfw |= OFPFW10_IN_PORT; } if (!wc->masks.dl_type) { ofpfw |= OFPFW10_DL_TYPE; } if (!wc->masks.nw_proto) { ofpfw |= OFPFW10_NW_PROTO; } ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_src) << OFPFW10_NW_SRC_SHIFT); ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_dst) << OFPFW10_NW_DST_SHIFT); if (!(wc->masks.nw_tos & IP_DSCP_MASK)) { ofpfw |= OFPFW10_NW_TOS; } if (!wc->masks.tp_src) { ofpfw |= OFPFW10_TP_SRC; } if (!wc->masks.tp_dst) { ofpfw |= OFPFW10_TP_DST; } if (eth_addr_is_zero(wc->masks.dl_src)) { ofpfw |= OFPFW10_DL_SRC; } if (eth_addr_is_zero(wc->masks.dl_dst)) { ofpfw |= OFPFW10_DL_DST; } /* Translate VLANs. */ ofmatch->dl_vlan = htons(0); ofmatch->dl_vlan_pcp = 0; if (match->wc.masks.vlan_tci == htons(0)) { ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP; } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI) && !(match->flow.vlan_tci & htons(VLAN_CFI))) { ofmatch->dl_vlan = htons(OFP10_VLAN_NONE); } else { if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) { ofpfw |= OFPFW10_DL_VLAN; } else { ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci)); } if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) { ofpfw |= OFPFW10_DL_VLAN_PCP; } else { ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci); } } /* Compose most of the match structure. */ ofmatch->wildcards = htonl(ofpfw); ofmatch->in_port = htons(ofp_to_u16(match->flow.in_port.ofp_port)); ofmatch->dl_src = match->flow.dl_src; ofmatch->dl_dst = match->flow.dl_dst; ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type); ofmatch->nw_src = match->flow.nw_src; ofmatch->nw_dst = match->flow.nw_dst; ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK; ofmatch->nw_proto = match->flow.nw_proto; ofmatch->tp_src = match->flow.tp_src; ofmatch->tp_dst = match->flow.tp_dst; memset(ofmatch->pad1, '\0', sizeof ofmatch->pad1); memset(ofmatch->pad2, '\0', sizeof ofmatch->pad2); } enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match, uint16_t *padded_match_len) { struct ofp11_match_header *omh = buf->data; uint16_t match_len; if (buf->size < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } match_len = ntohs(omh->length); switch (ntohs(omh->type)) { case OFPMT_STANDARD: { struct ofp11_match *om; if (match_len != sizeof *om || buf->size < sizeof *om) { return OFPERR_OFPBMC_BAD_LEN; } om = ofpbuf_pull(buf, sizeof *om); if (padded_match_len) { *padded_match_len = match_len; } return ofputil_match_from_ofp11_match(om, match); } case OFPMT_OXM: if (padded_match_len) { *padded_match_len = ROUND_UP(match_len, 8); } return oxm_pull_match(buf, match); default: return OFPERR_OFPBMC_BAD_TYPE; } } /* Converts the ofp11_match in 'ofmatch' into a struct match in 'match'. * Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, struct match *match) { uint16_t wc = ntohl(ofmatch->wildcards); bool ipv4, arp, rarp; match_init_catchall(match); if (!(wc & OFPFW11_IN_PORT)) { ofp_port_t ofp_port; enum ofperr error; error = ofputil_port_from_ofp11(ofmatch->in_port, &ofp_port); if (error) { return OFPERR_OFPBMC_BAD_VALUE; } match_set_in_port(match, ofp_port); } match_set_dl_src_masked(match, ofmatch->dl_src, eth_addr_invert(ofmatch->dl_src_mask)); match_set_dl_dst_masked(match, ofmatch->dl_dst, eth_addr_invert(ofmatch->dl_dst_mask)); if (!(wc & OFPFW11_DL_VLAN)) { if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) { /* Match only packets without a VLAN tag. */ match->flow.vlan_tci = htons(0); match->wc.masks.vlan_tci = OVS_BE16_MAX; } else { if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) { /* Match any packet with a VLAN tag regardless of VID. */ match->flow.vlan_tci = htons(VLAN_CFI); match->wc.masks.vlan_tci = htons(VLAN_CFI); } else if (ntohs(ofmatch->dl_vlan) < 4096) { /* Match only packets with the specified VLAN VID. */ match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan; match->wc.masks.vlan_tci = htons(VLAN_CFI | VLAN_VID_MASK); } else { /* Invalid VID. */ return OFPERR_OFPBMC_BAD_VALUE; } if (!(wc & OFPFW11_DL_VLAN_PCP)) { if (ofmatch->dl_vlan_pcp <= 7) { match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT); match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK); } else { /* Invalid PCP. */ return OFPERR_OFPBMC_BAD_VALUE; } } } } if (!(wc & OFPFW11_DL_TYPE)) { match_set_dl_type(match, ofputil_dl_type_from_openflow(ofmatch->dl_type)); } ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP); arp = match->flow.dl_type == htons(ETH_TYPE_ARP); rarp = match->flow.dl_type == htons(ETH_TYPE_RARP); if (ipv4 && !(wc & OFPFW11_NW_TOS)) { if (ofmatch->nw_tos & ~IP_DSCP_MASK) { /* Invalid TOS. */ return OFPERR_OFPBMC_BAD_VALUE; } match_set_nw_dscp(match, ofmatch->nw_tos); } if (ipv4 || arp || rarp) { if (!(wc & OFPFW11_NW_PROTO)) { match_set_nw_proto(match, ofmatch->nw_proto); } match_set_nw_src_masked(match, ofmatch->nw_src, ~ofmatch->nw_src_mask); match_set_nw_dst_masked(match, ofmatch->nw_dst, ~ofmatch->nw_dst_mask); } #define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST) if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) { switch (match->flow.nw_proto) { case IPPROTO_ICMP: /* "A.2.3 Flow Match Structures" in OF1.1 says: * * The tp_src and tp_dst fields will be ignored unless the * network protocol specified is as TCP, UDP or SCTP. * * but I'm pretty sure we should support ICMP too, otherwise * that's a regression from OF1.0. */ if (!(wc & OFPFW11_TP_SRC)) { uint16_t icmp_type = ntohs(ofmatch->tp_src); if (icmp_type < 0x100) { match_set_icmp_type(match, icmp_type); } else { return OFPERR_OFPBMC_BAD_FIELD; } } if (!(wc & OFPFW11_TP_DST)) { uint16_t icmp_code = ntohs(ofmatch->tp_dst); if (icmp_code < 0x100) { match_set_icmp_code(match, icmp_code); } else { return OFPERR_OFPBMC_BAD_FIELD; } } break; case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_SCTP: if (!(wc & (OFPFW11_TP_SRC))) { match_set_tp_src(match, ofmatch->tp_src); } if (!(wc & (OFPFW11_TP_DST))) { match_set_tp_dst(match, ofmatch->tp_dst); } break; default: /* OF1.1 says explicitly to ignore this. */ break; } } if (eth_type_mpls(match->flow.dl_type)) { if (!(wc & OFPFW11_MPLS_LABEL)) { match_set_mpls_label(match, 0, ofmatch->mpls_label); } if (!(wc & OFPFW11_MPLS_TC)) { match_set_mpls_tc(match, 0, ofmatch->mpls_tc); } } match_set_metadata_masked(match, ofmatch->metadata, ~ofmatch->metadata_mask); return 0; } /* Convert 'match' into the OpenFlow 1.1 match structure 'ofmatch'. */ void ofputil_match_to_ofp11_match(const struct match *match, struct ofp11_match *ofmatch) { uint32_t wc = 0; memset(ofmatch, 0, sizeof *ofmatch); ofmatch->omh.type = htons(OFPMT_STANDARD); ofmatch->omh.length = htons(OFPMT11_STANDARD_LENGTH); if (!match->wc.masks.in_port.ofp_port) { wc |= OFPFW11_IN_PORT; } else { ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port.ofp_port); } ofmatch->dl_src = match->flow.dl_src; ofmatch->dl_src_mask = eth_addr_invert(match->wc.masks.dl_src); ofmatch->dl_dst = match->flow.dl_dst; ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst); if (match->wc.masks.vlan_tci == htons(0)) { wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP; } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI) && !(match->flow.vlan_tci & htons(VLAN_CFI))) { ofmatch->dl_vlan = htons(OFPVID11_NONE); wc |= OFPFW11_DL_VLAN_PCP; } else { if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) { ofmatch->dl_vlan = htons(OFPVID11_ANY); } else { ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci)); } if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) { wc |= OFPFW11_DL_VLAN_PCP; } else { ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci); } } if (!match->wc.masks.dl_type) { wc |= OFPFW11_DL_TYPE; } else { ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type); } if (!(match->wc.masks.nw_tos & IP_DSCP_MASK)) { wc |= OFPFW11_NW_TOS; } else { ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK; } if (!match->wc.masks.nw_proto) { wc |= OFPFW11_NW_PROTO; } else { ofmatch->nw_proto = match->flow.nw_proto; } ofmatch->nw_src = match->flow.nw_src; ofmatch->nw_src_mask = ~match->wc.masks.nw_src; ofmatch->nw_dst = match->flow.nw_dst; ofmatch->nw_dst_mask = ~match->wc.masks.nw_dst; if (!match->wc.masks.tp_src) { wc |= OFPFW11_TP_SRC; } else { ofmatch->tp_src = match->flow.tp_src; } if (!match->wc.masks.tp_dst) { wc |= OFPFW11_TP_DST; } else { ofmatch->tp_dst = match->flow.tp_dst; } if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK))) { wc |= OFPFW11_MPLS_LABEL; } else { ofmatch->mpls_label = htonl(mpls_lse_to_label( match->flow.mpls_lse[0])); } if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK))) { wc |= OFPFW11_MPLS_TC; } else { ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse[0]); } ofmatch->metadata = match->flow.metadata; ofmatch->metadata_mask = ~match->wc.masks.metadata; ofmatch->wildcards = htonl(wc); } /* Returns the "typical" length of a match for 'protocol', for use in * estimating space to preallocate. */ int ofputil_match_typical_len(enum ofputil_protocol protocol) { switch (protocol) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: return sizeof(struct ofp10_match); case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: return NXM_TYPICAL_LEN; case OFPUTIL_P_OF11_STD: return sizeof(struct ofp11_match); case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: return NXM_TYPICAL_LEN; default: OVS_NOT_REACHED(); } } /* Appends to 'b' an struct ofp11_match_header followed by a match that * expresses 'match' properly for 'protocol', plus enough zero bytes to pad the * data appended out to a multiple of 8. 'protocol' must be one that is usable * in OpenFlow 1.1 or later. * * This function can cause 'b''s data to be reallocated. * * Returns the number of bytes appended to 'b', excluding the padding. Never * returns zero. */ int ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match, enum ofputil_protocol protocol) { switch (protocol) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: OVS_NOT_REACHED(); case OFPUTIL_P_OF11_STD: { struct ofp11_match *om; /* Make sure that no padding is needed. */ BUILD_ASSERT_DECL(sizeof *om % 8 == 0); om = ofpbuf_put_uninit(b, sizeof *om); ofputil_match_to_ofp11_match(match, om); return sizeof *om; } case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: return oxm_put_match(b, match, ofputil_protocol_to_ofp_version(protocol)); } OVS_NOT_REACHED(); } /* Given a 'dl_type' value in the format used in struct flow, returns the * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match * structure. */ ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type) { return (flow_dl_type == htons(FLOW_DL_TYPE_NONE) ? htons(OFP_DL_TYPE_NOT_ETH_TYPE) : flow_dl_type); } /* Given a 'dl_type' value in the format used in an ofp10_match or ofp11_match * structure, returns the corresponding 'dl_type' value for use in struct * flow. */ ovs_be16 ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type) { return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE) ? htons(FLOW_DL_TYPE_NONE) : ofp_dl_type); } /* Protocols. */ struct proto_abbrev { enum ofputil_protocol protocol; const char *name; }; /* Most users really don't care about some of the differences between * protocols. These abbreviations help with that. */ static const struct proto_abbrev proto_abbrevs[] = { { OFPUTIL_P_ANY, "any" }, { OFPUTIL_P_OF10_STD_ANY, "OpenFlow10" }, { OFPUTIL_P_OF10_NXM_ANY, "NXM" }, { OFPUTIL_P_ANY_OXM, "OXM" }, }; #define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs) enum ofputil_protocol ofputil_flow_dump_protocols[] = { OFPUTIL_P_OF15_OXM, OFPUTIL_P_OF14_OXM, OFPUTIL_P_OF13_OXM, OFPUTIL_P_OF12_OXM, OFPUTIL_P_OF11_STD, OFPUTIL_P_OF10_NXM, OFPUTIL_P_OF10_STD, }; size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols); /* Returns the set of ofputil_protocols that are supported with the given * OpenFlow 'version'. 'version' should normally be an 8-bit OpenFlow version * identifier (e.g. 0x01 for OpenFlow 1.0, 0x02 for OpenFlow 1.1). Returns 0 * if 'version' is not supported or outside the valid range. */ enum ofputil_protocol ofputil_protocols_from_ofp_version(enum ofp_version version) { switch (version) { case OFP10_VERSION: return OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY; case OFP11_VERSION: return OFPUTIL_P_OF11_STD; case OFP12_VERSION: return OFPUTIL_P_OF12_OXM; case OFP13_VERSION: return OFPUTIL_P_OF13_OXM; case OFP14_VERSION: return OFPUTIL_P_OF14_OXM; case OFP15_VERSION: return OFPUTIL_P_OF15_OXM; default: return 0; } } /* Returns the ofputil_protocol that is initially in effect on an OpenFlow * connection that has negotiated the given 'version'. 'version' should * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow * 1.0, 0x02 for OpenFlow 1.1). Returns 0 if 'version' is not supported or * outside the valid range. */ enum ofputil_protocol ofputil_protocol_from_ofp_version(enum ofp_version version) { return rightmost_1bit(ofputil_protocols_from_ofp_version(version)); } /* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION, * etc.) that corresponds to 'protocol'. */ enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) { switch (protocol) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: return OFP10_VERSION; case OFPUTIL_P_OF11_STD: return OFP11_VERSION; case OFPUTIL_P_OF12_OXM: return OFP12_VERSION; case OFPUTIL_P_OF13_OXM: return OFP13_VERSION; case OFPUTIL_P_OF14_OXM: return OFP14_VERSION; case OFPUTIL_P_OF15_OXM: return OFP15_VERSION; } OVS_NOT_REACHED(); } /* Returns a bitmap of OpenFlow versions that are supported by at * least one of the 'protocols'. */ uint32_t ofputil_protocols_to_version_bitmap(enum ofputil_protocol protocols) { uint32_t bitmap = 0; for (; protocols; protocols = zero_rightmost_1bit(protocols)) { enum ofputil_protocol protocol = rightmost_1bit(protocols); bitmap |= 1u << ofputil_protocol_to_ofp_version(protocol); } return bitmap; } /* Returns the set of protocols that are supported on top of the * OpenFlow versions included in 'bitmap'. */ enum ofputil_protocol ofputil_protocols_from_version_bitmap(uint32_t bitmap) { enum ofputil_protocol protocols = 0; for (; bitmap; bitmap = zero_rightmost_1bit(bitmap)) { enum ofp_version version = rightmost_1bit_idx(bitmap); protocols |= ofputil_protocols_from_ofp_version(version); } return protocols; } /* Returns true if 'protocol' is a single OFPUTIL_P_* value, false * otherwise. */ bool ofputil_protocol_is_valid(enum ofputil_protocol protocol) { return protocol & OFPUTIL_P_ANY && is_pow2(protocol); } /* Returns the equivalent of 'protocol' with the Nicira flow_mod_table_id * extension turned on or off if 'enable' is true or false, respectively. * * This extension is only useful for protocols whose "standard" version does * not allow specific tables to be modified. In particular, this is true of * OpenFlow 1.0. In later versions of OpenFlow, a flow_mod request always * specifies a table ID and so there is no need for such an extension. When * 'protocol' is such a protocol that doesn't need a flow_mod_table_id * extension, this function just returns its 'protocol' argument unchanged * regardless of the value of 'enable'. */ enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable) { switch (protocol) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: return enable ? OFPUTIL_P_OF10_STD_TID : OFPUTIL_P_OF10_STD; case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM; case OFPUTIL_P_OF11_STD: return OFPUTIL_P_OF11_STD; case OFPUTIL_P_OF12_OXM: return OFPUTIL_P_OF12_OXM; case OFPUTIL_P_OF13_OXM: return OFPUTIL_P_OF13_OXM; case OFPUTIL_P_OF14_OXM: return OFPUTIL_P_OF14_OXM; case OFPUTIL_P_OF15_OXM: return OFPUTIL_P_OF15_OXM; default: OVS_NOT_REACHED(); } } /* Returns the "base" version of 'protocol'. That is, if 'protocol' includes * some extension to a standard protocol version, the return value is the * standard version of that protocol without any extension. If 'protocol' is a * standard protocol version, returns 'protocol' unchanged. */ enum ofputil_protocol ofputil_protocol_to_base(enum ofputil_protocol protocol) { return ofputil_protocol_set_tid(protocol, false); } /* Returns 'new_base' with any extensions taken from 'cur'. */ enum ofputil_protocol ofputil_protocol_set_base(enum ofputil_protocol cur, enum ofputil_protocol new_base) { bool tid = (cur & OFPUTIL_P_TID) != 0; switch (new_base) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: return ofputil_protocol_set_tid(OFPUTIL_P_OF10_STD, tid); case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid); case OFPUTIL_P_OF11_STD: return ofputil_protocol_set_tid(OFPUTIL_P_OF11_STD, tid); case OFPUTIL_P_OF12_OXM: return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid); case OFPUTIL_P_OF13_OXM: return ofputil_protocol_set_tid(OFPUTIL_P_OF13_OXM, tid); case OFPUTIL_P_OF14_OXM: return ofputil_protocol_set_tid(OFPUTIL_P_OF14_OXM, tid); case OFPUTIL_P_OF15_OXM: return ofputil_protocol_set_tid(OFPUTIL_P_OF15_OXM, tid); default: OVS_NOT_REACHED(); } } /* Returns a string form of 'protocol', if a simple form exists (that is, if * 'protocol' is either a single protocol or it is a combination of protocols * that have a single abbreviation). Otherwise, returns NULL. */ const char * ofputil_protocol_to_string(enum ofputil_protocol protocol) { const struct proto_abbrev *p; /* Use a "switch" statement for single-bit names so that we get a compiler * warning if we forget any. */ switch (protocol) { case OFPUTIL_P_OF10_NXM: return "NXM-table_id"; case OFPUTIL_P_OF10_NXM_TID: return "NXM+table_id"; case OFPUTIL_P_OF10_STD: return "OpenFlow10-table_id"; case OFPUTIL_P_OF10_STD_TID: return "OpenFlow10+table_id"; case OFPUTIL_P_OF11_STD: return "OpenFlow11"; case OFPUTIL_P_OF12_OXM: return "OXM-OpenFlow12"; case OFPUTIL_P_OF13_OXM: return "OXM-OpenFlow13"; case OFPUTIL_P_OF14_OXM: return "OXM-OpenFlow14"; case OFPUTIL_P_OF15_OXM: return "OXM-OpenFlow15"; } /* Check abbreviations. */ for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) { if (protocol == p->protocol) { return p->name; } } return NULL; } /* Returns a string that represents 'protocols'. The return value might be a * comma-separated list if 'protocols' doesn't have a simple name. The return * value is "none" if 'protocols' is 0. * * The caller must free the returned string (with free()). */ char * ofputil_protocols_to_string(enum ofputil_protocol protocols) { struct ds s; ovs_assert(!(protocols & ~OFPUTIL_P_ANY)); if (protocols == 0) { return xstrdup("none"); } ds_init(&s); while (protocols) { const struct proto_abbrev *p; int i; if (s.length) { ds_put_char(&s, ','); } for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) { if ((protocols & p->protocol) == p->protocol) { ds_put_cstr(&s, p->name); protocols &= ~p->protocol; goto match; } } for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) { enum ofputil_protocol bit = 1u << i; if (protocols & bit) { ds_put_cstr(&s, ofputil_protocol_to_string(bit)); protocols &= ~bit; goto match; } } OVS_NOT_REACHED(); match: ; } return ds_steal_cstr(&s); } static enum ofputil_protocol ofputil_protocol_from_string__(const char *s, size_t n) { const struct proto_abbrev *p; int i; for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) { enum ofputil_protocol bit = 1u << i; const char *name = ofputil_protocol_to_string(bit); if (name && n == strlen(name) && !strncasecmp(s, name, n)) { return bit; } } for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) { if (n == strlen(p->name) && !strncasecmp(s, p->name, n)) { return p->protocol; } } return 0; } /* Returns the nonempty set of protocols represented by 's', which can be a * single protocol name or abbreviation or a comma-separated list of them. * * Aborts the program with an error message if 's' is invalid. */ enum ofputil_protocol ofputil_protocols_from_string(const char *s) { const char *orig_s = s; enum ofputil_protocol protocols; protocols = 0; while (*s) { enum ofputil_protocol p; size_t n; n = strcspn(s, ","); if (n == 0) { s++; continue; } p = ofputil_protocol_from_string__(s, n); if (!p) { ovs_fatal(0, "%.*s: unknown flow protocol", (int) n, s); } protocols |= p; s += n; } if (!protocols) { ovs_fatal(0, "%s: no flow protocol specified", orig_s); } return protocols; } enum ofp_version ofputil_version_from_string(const char *s) { if (!strcasecmp(s, "OpenFlow10")) { return OFP10_VERSION; } if (!strcasecmp(s, "OpenFlow11")) { return OFP11_VERSION; } if (!strcasecmp(s, "OpenFlow12")) { return OFP12_VERSION; } if (!strcasecmp(s, "OpenFlow13")) { return OFP13_VERSION; } if (!strcasecmp(s, "OpenFlow14")) { return OFP14_VERSION; } if (!strcasecmp(s, "OpenFlow15")) { return OFP15_VERSION; } return 0; } static bool is_delimiter(unsigned char c) { return isspace(c) || c == ','; } uint32_t ofputil_versions_from_string(const char *s) { size_t i = 0; uint32_t bitmap = 0; while (s[i]) { size_t j; int version; char *key; if (is_delimiter(s[i])) { i++; continue; } j = 0; while (s[i + j] && !is_delimiter(s[i + j])) { j++; } key = xmemdup0(s + i, j); version = ofputil_version_from_string(key); if (!version) { VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key); } free(key); bitmap |= 1u << version; i += j; } return bitmap; } uint32_t ofputil_versions_from_strings(char ** const s, size_t count) { uint32_t bitmap = 0; while (count--) { int version = ofputil_version_from_string(s[count]); if (!version) { VLOG_WARN("Unknown OpenFlow version: \"%s\"", s[count]); } else { bitmap |= 1u << version; } } return bitmap; } const char * ofputil_version_to_string(enum ofp_version ofp_version) { switch (ofp_version) { case OFP10_VERSION: return "OpenFlow10"; case OFP11_VERSION: return "OpenFlow11"; case OFP12_VERSION: return "OpenFlow12"; case OFP13_VERSION: return "OpenFlow13"; case OFP14_VERSION: return "OpenFlow14"; case OFP15_VERSION: return "OpenFlow15"; default: OVS_NOT_REACHED(); } } bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format) { switch (packet_in_format) { case NXPIF_OPENFLOW10: case NXPIF_NXM: return true; } return false; } const char * ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format) { switch (packet_in_format) { case NXPIF_OPENFLOW10: return "openflow10"; case NXPIF_NXM: return "nxm"; default: OVS_NOT_REACHED(); } } int ofputil_packet_in_format_from_string(const char *s) { return (!strcmp(s, "openflow10") ? NXPIF_OPENFLOW10 : !strcmp(s, "nxm") ? NXPIF_NXM : -1); } void ofputil_format_version(struct ds *msg, enum ofp_version version) { ds_put_format(msg, "0x%02x", version); } void ofputil_format_version_name(struct ds *msg, enum ofp_version version) { ds_put_cstr(msg, ofputil_version_to_string(version)); } static void ofputil_format_version_bitmap__(struct ds *msg, uint32_t bitmap, void (*format_version)(struct ds *msg, enum ofp_version)) { while (bitmap) { format_version(msg, raw_ctz(bitmap)); bitmap = zero_rightmost_1bit(bitmap); if (bitmap) { ds_put_cstr(msg, ", "); } } } void ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap) { ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version); } void ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap) { ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name); } static bool ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh, uint32_t *allowed_versionsp) { uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh; const ovs_be32 *bitmap = ALIGNED_CAST(const ovs_be32 *, oheh + 1); uint32_t allowed_versions; if (!bitmap_len || bitmap_len % sizeof *bitmap) { return false; } /* Only use the first 32-bit element of the bitmap as that is all the * current implementation supports. Subsequent elements are ignored which * should have no effect on session negotiation until Open vSwitch supports * wire-protocol versions greater than 31. */ allowed_versions = ntohl(bitmap[0]); if (allowed_versions & 1) { /* There's no OpenFlow version 0. */ VLOG_WARN_RL(&bad_ofmsg_rl, "peer claims to support invalid OpenFlow " "version 0x00"); allowed_versions &= ~1u; } if (!allowed_versions) { VLOG_WARN_RL(&bad_ofmsg_rl, "peer does not support any OpenFlow " "version (between 0x01 and 0x1f)"); return false; } *allowed_versionsp = allowed_versions; return true; } static uint32_t version_bitmap_from_version(uint8_t ofp_version) { return ((ofp_version < 32 ? 1u << ofp_version : 0) - 1) << 1; } /* Decodes OpenFlow OFPT_HELLO message 'oh', storing into '*allowed_versions' * the set of OpenFlow versions for which 'oh' announces support. * * Because of how OpenFlow defines OFPT_HELLO messages, this function is always * successful, and thus '*allowed_versions' is always initialized. However, it * returns false if 'oh' contains some data that could not be fully understood, * true if 'oh' was completely parsed. */ bool ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions) { struct ofpbuf msg; bool ok = true; ofpbuf_use_const(&msg, oh, ntohs(oh->length)); ofpbuf_pull(&msg, sizeof *oh); *allowed_versions = version_bitmap_from_version(oh->version); while (msg.size) { const struct ofp_hello_elem_header *oheh; unsigned int len; if (msg.size < sizeof *oheh) { return false; } oheh = msg.data; len = ntohs(oheh->length); if (len < sizeof *oheh || !ofpbuf_try_pull(&msg, ROUND_UP(len, 8))) { return false; } if (oheh->type != htons(OFPHET_VERSIONBITMAP) || !ofputil_decode_hello_bitmap(oheh, allowed_versions)) { ok = false; } } return ok; } /* Returns true if 'allowed_versions' needs to be accompanied by a version * bitmap to be correctly expressed in an OFPT_HELLO message. */ static bool should_send_version_bitmap(uint32_t allowed_versions) { return !is_pow2((allowed_versions >> 1) + 1); } /* Create an OFPT_HELLO message that expresses support for the OpenFlow * versions in the 'allowed_versions' bitmaps and returns the message. */ struct ofpbuf * ofputil_encode_hello(uint32_t allowed_versions) { enum ofp_version ofp_version; struct ofpbuf *msg; ofp_version = leftmost_1bit_idx(allowed_versions); msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0); if (should_send_version_bitmap(allowed_versions)) { struct ofp_hello_elem_header *oheh; uint16_t map_len; map_len = sizeof allowed_versions; oheh = ofpbuf_put_zeros(msg, ROUND_UP(map_len + sizeof *oheh, 8)); oheh->type = htons(OFPHET_VERSIONBITMAP); oheh->length = htons(map_len + sizeof *oheh); *ALIGNED_CAST(ovs_be32 *, oheh + 1) = htonl(allowed_versions); ofpmsg_update_length(msg); } return msg; } /* Returns an OpenFlow message that, sent on an OpenFlow connection whose * protocol is 'current', at least partly transitions the protocol to 'want'. * Stores in '*next' the protocol that will be in effect on the OpenFlow * connection if the switch processes the returned message correctly. (If * '*next != want' then the caller will have to iterate.) * * If 'current == want', or if it is not possible to transition from 'current' * to 'want' (because, for example, 'current' and 'want' use different OpenFlow * protocol versions), returns NULL and stores 'current' in '*next'. */ struct ofpbuf * ofputil_encode_set_protocol(enum ofputil_protocol current, enum ofputil_protocol want, enum ofputil_protocol *next) { enum ofp_version cur_version, want_version; enum ofputil_protocol cur_base, want_base; bool cur_tid, want_tid; cur_version = ofputil_protocol_to_ofp_version(current); want_version = ofputil_protocol_to_ofp_version(want); if (cur_version != want_version) { *next = current; return NULL; } cur_base = ofputil_protocol_to_base(current); want_base = ofputil_protocol_to_base(want); if (cur_base != want_base) { *next = ofputil_protocol_set_base(current, want_base); switch (want_base) { case OFPUTIL_P_OF10_NXM: return ofputil_encode_nx_set_flow_format(NXFF_NXM); case OFPUTIL_P_OF10_STD: return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10); case OFPUTIL_P_OF11_STD: case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: /* There is only one variant of each OpenFlow 1.1+ protocol, and we * verified above that we're not trying to change versions. */ OVS_NOT_REACHED(); case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM_TID: OVS_NOT_REACHED(); } } cur_tid = (current & OFPUTIL_P_TID) != 0; want_tid = (want & OFPUTIL_P_TID) != 0; if (cur_tid != want_tid) { *next = ofputil_protocol_set_tid(current, want_tid); return ofputil_make_flow_mod_table_id(want_tid); } ovs_assert(current == want); *next = current; return NULL; } /* Returns an NXT_SET_FLOW_FORMAT message that can be used to set the flow * format to 'nxff'. */ struct ofpbuf * ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff) { struct nx_set_flow_format *sff; struct ofpbuf *msg; ovs_assert(ofputil_nx_flow_format_is_valid(nxff)); msg = ofpraw_alloc(OFPRAW_NXT_SET_FLOW_FORMAT, OFP10_VERSION, 0); sff = ofpbuf_put_zeros(msg, sizeof *sff); sff->format = htonl(nxff); return msg; } /* Returns the base protocol if 'flow_format' is a valid NXFF_* value, false * otherwise. */ enum ofputil_protocol ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format) { switch (flow_format) { case NXFF_OPENFLOW10: return OFPUTIL_P_OF10_STD; case NXFF_NXM: return OFPUTIL_P_OF10_NXM; default: return 0; } } /* Returns true if 'flow_format' is a valid NXFF_* value, false otherwise. */ bool ofputil_nx_flow_format_is_valid(enum nx_flow_format flow_format) { return ofputil_nx_flow_format_to_protocol(flow_format) != 0; } /* Returns a string version of 'flow_format', which must be a valid NXFF_* * value. */ const char * ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format) { switch (flow_format) { case NXFF_OPENFLOW10: return "openflow10"; case NXFF_NXM: return "nxm"; default: OVS_NOT_REACHED(); } } struct ofpbuf * ofputil_make_set_packet_in_format(enum ofp_version ofp_version, enum nx_packet_in_format packet_in_format) { struct nx_set_packet_in_format *spif; struct ofpbuf *msg; msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, ofp_version, 0); spif = ofpbuf_put_zeros(msg, sizeof *spif); spif->format = htonl(packet_in_format); return msg; } /* Returns an OpenFlow message that can be used to turn the flow_mod_table_id * extension on or off (according to 'flow_mod_table_id'). */ struct ofpbuf * ofputil_make_flow_mod_table_id(bool flow_mod_table_id) { struct nx_flow_mod_table_id *nfmti; struct ofpbuf *msg; msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, 0); nfmti = ofpbuf_put_zeros(msg, sizeof *nfmti); nfmti->set = flow_mod_table_id; return msg; } struct ofputil_flow_mod_flag { uint16_t raw_flag; enum ofp_version min_version, max_version; enum ofputil_flow_mod_flags flag; }; static const struct ofputil_flow_mod_flag ofputil_flow_mod_flags[] = { { OFPFF_SEND_FLOW_REM, OFP10_VERSION, 0, OFPUTIL_FF_SEND_FLOW_REM }, { OFPFF_CHECK_OVERLAP, OFP10_VERSION, 0, OFPUTIL_FF_CHECK_OVERLAP }, { OFPFF10_EMERG, OFP10_VERSION, OFP10_VERSION, OFPUTIL_FF_EMERG }, { OFPFF12_RESET_COUNTS, OFP12_VERSION, 0, OFPUTIL_FF_RESET_COUNTS }, { OFPFF13_NO_PKT_COUNTS, OFP13_VERSION, 0, OFPUTIL_FF_NO_PKT_COUNTS }, { OFPFF13_NO_BYT_COUNTS, OFP13_VERSION, 0, OFPUTIL_FF_NO_BYT_COUNTS }, { 0, 0, 0, 0 }, }; static enum ofperr ofputil_decode_flow_mod_flags(ovs_be16 raw_flags_, enum ofp_flow_mod_command command, enum ofp_version version, enum ofputil_flow_mod_flags *flagsp) { uint16_t raw_flags = ntohs(raw_flags_); const struct ofputil_flow_mod_flag *f; *flagsp = 0; for (f = ofputil_flow_mod_flags; f->raw_flag; f++) { if (raw_flags & f->raw_flag && version >= f->min_version && (!f->max_version || version <= f->max_version)) { raw_flags &= ~f->raw_flag; *flagsp |= f->flag; } } /* In OF1.0 and OF1.1, "add" always resets counters, and other commands * never do. * * In OF1.2 and later, OFPFF12_RESET_COUNTS controls whether each command * resets counters. */ if ((version == OFP10_VERSION || version == OFP11_VERSION) && command == OFPFC_ADD) { *flagsp |= OFPUTIL_FF_RESET_COUNTS; } return raw_flags ? OFPERR_OFPFMFC_BAD_FLAGS : 0; } static ovs_be16 ofputil_encode_flow_mod_flags(enum ofputil_flow_mod_flags flags, enum ofp_version version) { const struct ofputil_flow_mod_flag *f; uint16_t raw_flags; raw_flags = 0; for (f = ofputil_flow_mod_flags; f->raw_flag; f++) { if (f->flag & flags && version >= f->min_version && (!f->max_version || version <= f->max_version)) { raw_flags |= f->raw_flag; } } return htons(raw_flags); } /* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. * * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions. * The caller must initialize 'ofpacts' and retains ownership of it. * 'fm->ofpacts' will point into the 'ofpacts' buffer. * * Does not validate the flow_mod actions. The caller should do that, with * ofpacts_check(). */ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, const struct ofp_header *oh, enum ofputil_protocol protocol, struct ofpbuf *ofpacts, ofp_port_t max_port, uint8_t max_table) { ovs_be16 raw_flags; enum ofperr error; struct ofpbuf b; enum ofpraw raw; /* Ignored for non-delete actions */ fm->delete_reason = OFPRR_DELETE; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_FLOW_MOD) { /* Standard OpenFlow 1.1+ flow_mod. */ const struct ofp11_flow_mod *ofm; ofm = ofpbuf_pull(&b, sizeof *ofm); error = ofputil_pull_ofp11_match(&b, &fm->match, NULL); if (error) { return error; } /* Translate the message. */ fm->priority = ntohs(ofm->priority); if (ofm->command == OFPFC_ADD || (oh->version == OFP11_VERSION && (ofm->command == OFPFC_MODIFY || ofm->command == OFPFC_MODIFY_STRICT) && ofm->cookie_mask == htonll(0))) { /* In OpenFlow 1.1 only, a "modify" or "modify-strict" that does * not match on the cookie is treated as an "add" if there is no * match. */ fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = ofm->cookie; } else { fm->cookie = ofm->cookie; fm->cookie_mask = ofm->cookie_mask; fm->new_cookie = OVS_BE64_MAX; } fm->modify_cookie = false; fm->command = ofm->command; /* Get table ID. * * OF1.1 entirely forbids table_id == OFPTT_ALL. * OF1.2+ allows table_id == OFPTT_ALL only for deletes. */ fm->table_id = ofm->table_id; if (fm->table_id == OFPTT_ALL && (oh->version == OFP11_VERSION || (ofm->command != OFPFC_DELETE && ofm->command != OFPFC_DELETE_STRICT))) { return OFPERR_OFPFMFC_BAD_TABLE_ID; } fm->idle_timeout = ntohs(ofm->idle_timeout); fm->hard_timeout = ntohs(ofm->hard_timeout); if (oh->version >= OFP14_VERSION && ofm->command == OFPFC_ADD) { fm->importance = ntohs(ofm->importance); } else { fm->importance = 0; } fm->buffer_id = ntohl(ofm->buffer_id); error = ofputil_port_from_ofp11(ofm->out_port, &fm->out_port); if (error) { return error; } fm->out_group = (ofm->command == OFPFC_DELETE || ofm->command == OFPFC_DELETE_STRICT ? ntohl(ofm->out_group) : OFPG_ANY); raw_flags = ofm->flags; } else { uint16_t command; if (raw == OFPRAW_OFPT10_FLOW_MOD) { /* Standard OpenFlow 1.0 flow_mod. */ const struct ofp10_flow_mod *ofm; /* Get the ofp10_flow_mod. */ ofm = ofpbuf_pull(&b, sizeof *ofm); /* Translate the rule. */ ofputil_match_from_ofp10_match(&ofm->match, &fm->match); ofputil_normalize_match(&fm->match); /* OpenFlow 1.0 says that exact-match rules have to have the * highest possible priority. */ fm->priority = (ofm->match.wildcards & htonl(OFPFW10_ALL) ? ntohs(ofm->priority) : UINT16_MAX); /* Translate the message. */ command = ntohs(ofm->command); fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = ofm->cookie; fm->idle_timeout = ntohs(ofm->idle_timeout); fm->hard_timeout = ntohs(ofm->hard_timeout); fm->importance = 0; fm->buffer_id = ntohl(ofm->buffer_id); fm->out_port = u16_to_ofp(ntohs(ofm->out_port)); fm->out_group = OFPG_ANY; raw_flags = ofm->flags; } else if (raw == OFPRAW_NXT_FLOW_MOD) { /* Nicira extended flow_mod. */ const struct nx_flow_mod *nfm; /* Dissect the message. */ nfm = ofpbuf_pull(&b, sizeof *nfm); error = nx_pull_match(&b, ntohs(nfm->match_len), &fm->match, &fm->cookie, &fm->cookie_mask); if (error) { return error; } /* Translate the message. */ command = ntohs(nfm->command); if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) { /* Flow additions may only set a new cookie, not match an * existing cookie. */ return OFPERR_NXBRC_NXM_INVALID; } fm->priority = ntohs(nfm->priority); fm->new_cookie = nfm->cookie; fm->idle_timeout = ntohs(nfm->idle_timeout); fm->hard_timeout = ntohs(nfm->hard_timeout); fm->importance = 0; fm->buffer_id = ntohl(nfm->buffer_id); fm->out_port = u16_to_ofp(ntohs(nfm->out_port)); fm->out_group = OFPG_ANY; raw_flags = nfm->flags; } else { OVS_NOT_REACHED(); } fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; } else { if (command > 0xff) { VLOG_WARN_RL(&bad_ofmsg_rl, "flow_mod has explicit table_id " "but flow_mod_table_id extension is not enabled"); } fm->command = command; fm->table_id = 0xff; } } if (fm->command > OFPFC_DELETE_STRICT) { return OFPERR_OFPFMFC_BAD_COMMAND; } error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version, ofpacts); if (error) { return error; } fm->ofpacts = ofpacts->data; fm->ofpacts_len = ofpacts->size; error = ofputil_decode_flow_mod_flags(raw_flags, fm->command, oh->version, &fm->flags); if (error) { return error; } if (fm->flags & OFPUTIL_FF_EMERG) { /* We do not support the OpenFlow 1.0 emergency flow cache, which * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. * * OpenFlow 1.0 specifies the error code to use when idle_timeout * or hard_timeout is nonzero. Otherwise, there is no good error * code, so just state that the flow table is full. */ return (fm->hard_timeout || fm->idle_timeout ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT : OFPERR_OFPFMFC_TABLE_FULL); } return ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len, &fm->match.flow, max_port, fm->table_id, max_table, protocol); } static enum ofperr ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands, struct ofpbuf *bands) { const struct ofp13_meter_band_header *ombh; struct ofputil_meter_band *mb; uint16_t n = 0; ombh = ofpbuf_try_pull(msg, len); if (!ombh) { return OFPERR_OFPBRC_BAD_LEN; } while (len >= sizeof (struct ofp13_meter_band_drop)) { size_t ombh_len = ntohs(ombh->len); /* All supported band types have the same length. */ if (ombh_len != sizeof (struct ofp13_meter_band_drop)) { return OFPERR_OFPBRC_BAD_LEN; } mb = ofpbuf_put_uninit(bands, sizeof *mb); mb->type = ntohs(ombh->type); if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) { return OFPERR_OFPMMFC_BAD_BAND; } mb->rate = ntohl(ombh->rate); mb->burst_size = ntohl(ombh->burst_size); mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ? ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0; n++; len -= ombh_len; ombh = ALIGNED_CAST(struct ofp13_meter_band_header *, (char *) ombh + ombh_len); } if (len) { return OFPERR_OFPBRC_BAD_LEN; } *n_bands = n; return 0; } enum ofperr ofputil_decode_meter_mod(const struct ofp_header *oh, struct ofputil_meter_mod *mm, struct ofpbuf *bands) { const struct ofp13_meter_mod *omm; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); omm = ofpbuf_pull(&b, sizeof *omm); /* Translate the message. */ mm->command = ntohs(omm->command); if (mm->command != OFPMC13_ADD && mm->command != OFPMC13_MODIFY && mm->command != OFPMC13_DELETE) { return OFPERR_OFPMMFC_BAD_COMMAND; } mm->meter.meter_id = ntohl(omm->meter_id); if (mm->command == OFPMC13_DELETE) { mm->meter.flags = 0; mm->meter.n_bands = 0; mm->meter.bands = NULL; } else { enum ofperr error; mm->meter.flags = ntohs(omm->flags); if (mm->meter.flags & OFPMF13_KBPS && mm->meter.flags & OFPMF13_PKTPS) { return OFPERR_OFPMMFC_BAD_FLAGS; } error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands); if (error) { return error; } mm->meter.bands = bands->data; } return 0; } void ofputil_decode_meter_request(const struct ofp_header *oh, uint32_t *meter_id) { const struct ofp13_meter_multipart_request *omr = ofpmsg_body(oh); *meter_id = ntohl(omr->meter_id); } struct ofpbuf * ofputil_encode_meter_request(enum ofp_version ofp_version, enum ofputil_meter_request_type type, uint32_t meter_id) { struct ofpbuf *msg; enum ofpraw raw; switch (type) { case OFPUTIL_METER_CONFIG: raw = OFPRAW_OFPST13_METER_CONFIG_REQUEST; break; case OFPUTIL_METER_STATS: raw = OFPRAW_OFPST13_METER_REQUEST; break; default: case OFPUTIL_METER_FEATURES: raw = OFPRAW_OFPST13_METER_FEATURES_REQUEST; break; } msg = ofpraw_alloc(raw, ofp_version, 0); if (type != OFPUTIL_METER_FEATURES) { struct ofp13_meter_multipart_request *omr; omr = ofpbuf_put_zeros(msg, sizeof *omr); omr->meter_id = htonl(meter_id); } return msg; } static void ofputil_put_bands(uint16_t n_bands, const struct ofputil_meter_band *mb, struct ofpbuf *msg) { uint16_t n = 0; for (n = 0; n < n_bands; ++n) { /* Currently all band types have same size. */ struct ofp13_meter_band_dscp_remark *ombh; size_t ombh_len = sizeof *ombh; ombh = ofpbuf_put_zeros(msg, ombh_len); ombh->type = htons(mb->type); ombh->len = htons(ombh_len); ombh->rate = htonl(mb->rate); ombh->burst_size = htonl(mb->burst_size); ombh->prec_level = mb->prec_level; mb++; } } /* Encode a meter stat for 'mc' and append it to 'replies'. */ void ofputil_append_meter_config(struct ovs_list *replies, const struct ofputil_meter_config *mc) { struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); size_t start_ofs = msg->size; struct ofp13_meter_config *reply = ofpbuf_put_uninit(msg, sizeof *reply); reply->flags = htons(mc->flags); reply->meter_id = htonl(mc->meter_id); ofputil_put_bands(mc->n_bands, mc->bands, msg); reply = ofpbuf_at_assert(msg, start_ofs, sizeof *reply); reply->length = htons(msg->size - start_ofs); ofpmp_postappend(replies, start_ofs); } /* Encode a meter stat for 'ms' and append it to 'replies'. */ void ofputil_append_meter_stats(struct ovs_list *replies, const struct ofputil_meter_stats *ms) { struct ofp13_meter_stats *reply; uint16_t n = 0; uint16_t len; len = sizeof *reply + ms->n_bands * sizeof(struct ofp13_meter_band_stats); reply = ofpmp_append(replies, len); reply->meter_id = htonl(ms->meter_id); reply->len = htons(len); memset(reply->pad, 0, sizeof reply->pad); reply->flow_count = htonl(ms->flow_count); reply->packet_in_count = htonll(ms->packet_in_count); reply->byte_in_count = htonll(ms->byte_in_count); reply->duration_sec = htonl(ms->duration_sec); reply->duration_nsec = htonl(ms->duration_nsec); for (n = 0; n < ms->n_bands; ++n) { const struct ofputil_meter_band_stats *src = &ms->bands[n]; struct ofp13_meter_band_stats *dst = &reply->band_stats[n]; dst->packet_band_count = htonll(src->packet_count); dst->byte_band_count = htonll(src->byte_count); } } /* Converts an OFPMP_METER_CONFIG reply in 'msg' into an abstract * ofputil_meter_config in 'mc', with mc->bands pointing to bands decoded into * 'bands'. The caller must have initialized 'bands' and retains ownership of * it across the call. * * Multiple OFPST13_METER_CONFIG replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. 'bands' is cleared for each reply. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_meter_config(struct ofpbuf *msg, struct ofputil_meter_config *mc, struct ofpbuf *bands) { const struct ofp13_meter_config *omc; enum ofperr err; /* Pull OpenFlow headers for the first call. */ if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } omc = ofpbuf_try_pull(msg, sizeof *omc); if (!omc) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPMP_METER_CONFIG reply has %"PRIu32" leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } ofpbuf_clear(bands); err = ofputil_pull_bands(msg, ntohs(omc->length) - sizeof *omc, &mc->n_bands, bands); if (err) { return err; } mc->meter_id = ntohl(omc->meter_id); mc->flags = ntohs(omc->flags); mc->bands = bands->data; return 0; } static enum ofperr ofputil_pull_band_stats(struct ofpbuf *msg, size_t len, uint16_t *n_bands, struct ofpbuf *bands) { const struct ofp13_meter_band_stats *ombs; struct ofputil_meter_band_stats *mbs; uint16_t n, i; ombs = ofpbuf_try_pull(msg, len); if (!ombs) { return OFPERR_OFPBRC_BAD_LEN; } n = len / sizeof *ombs; if (len != n * sizeof *ombs) { return OFPERR_OFPBRC_BAD_LEN; } mbs = ofpbuf_put_uninit(bands, len); for (i = 0; i < n; ++i) { mbs[i].packet_count = ntohll(ombs[i].packet_band_count); mbs[i].byte_count = ntohll(ombs[i].byte_band_count); } *n_bands = n; return 0; } /* Converts an OFPMP_METER reply in 'msg' into an abstract * ofputil_meter_stats in 'ms', with ms->bands pointing to band stats * decoded into 'bands'. * * Multiple OFPMP_METER replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. 'bands' is cleared for each reply. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_meter_stats(struct ofpbuf *msg, struct ofputil_meter_stats *ms, struct ofpbuf *bands) { const struct ofp13_meter_stats *oms; enum ofperr err; /* Pull OpenFlow headers for the first call. */ if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } oms = ofpbuf_try_pull(msg, sizeof *oms); if (!oms) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPMP_METER reply has %"PRIu32" leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } ofpbuf_clear(bands); err = ofputil_pull_band_stats(msg, ntohs(oms->len) - sizeof *oms, &ms->n_bands, bands); if (err) { return err; } ms->meter_id = ntohl(oms->meter_id); ms->flow_count = ntohl(oms->flow_count); ms->packet_in_count = ntohll(oms->packet_in_count); ms->byte_in_count = ntohll(oms->byte_in_count); ms->duration_sec = ntohl(oms->duration_sec); ms->duration_nsec = ntohl(oms->duration_nsec); ms->bands = bands->data; return 0; } void ofputil_decode_meter_features(const struct ofp_header *oh, struct ofputil_meter_features *mf) { const struct ofp13_meter_features *omf = ofpmsg_body(oh); mf->max_meters = ntohl(omf->max_meter); mf->band_types = ntohl(omf->band_types); mf->capabilities = ntohl(omf->capabilities); mf->max_bands = omf->max_bands; mf->max_color = omf->max_color; } struct ofpbuf * ofputil_encode_meter_features_reply(const struct ofputil_meter_features *mf, const struct ofp_header *request) { struct ofpbuf *reply; struct ofp13_meter_features *omf; reply = ofpraw_alloc_stats_reply(request, 0); omf = ofpbuf_put_zeros(reply, sizeof *omf); omf->max_meter = htonl(mf->max_meters); omf->band_types = htonl(mf->band_types); omf->capabilities = htonl(mf->capabilities); omf->max_bands = mf->max_bands; omf->max_color = mf->max_color; return reply; } struct ofpbuf * ofputil_encode_meter_mod(enum ofp_version ofp_version, const struct ofputil_meter_mod *mm) { struct ofpbuf *msg; struct ofp13_meter_mod *omm; msg = ofpraw_alloc(OFPRAW_OFPT13_METER_MOD, ofp_version, NXM_TYPICAL_LEN + mm->meter.n_bands * 16); omm = ofpbuf_put_zeros(msg, sizeof *omm); omm->command = htons(mm->command); if (mm->command != OFPMC13_DELETE) { omm->flags = htons(mm->meter.flags); } omm->meter_id = htonl(mm->meter.meter_id); ofputil_put_bands(mm->meter.n_bands, mm->meter.bands, msg); ofpmsg_update_length(msg); return msg; } static ovs_be16 ofputil_tid_command(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol) { return htons(protocol & OFPUTIL_P_TID ? (fm->command & 0xff) | (fm->table_id << 8) : fm->command); } /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to * 'protocol' and returns the message. */ struct ofpbuf * ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol) { enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); ovs_be16 raw_flags = ofputil_encode_flow_mod_flags(fm->flags, version); struct ofpbuf *msg; switch (protocol) { case OFPUTIL_P_OF11_STD: case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: { struct ofp11_flow_mod *ofm; int tailroom; tailroom = ofputil_match_typical_len(protocol) + fm->ofpacts_len; msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, version, tailroom); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); if ((protocol == OFPUTIL_P_OF11_STD && (fm->command == OFPFC_MODIFY || fm->command == OFPFC_MODIFY_STRICT) && fm->cookie_mask == htonll(0)) || fm->command == OFPFC_ADD) { ofm->cookie = fm->new_cookie; } else { ofm->cookie = fm->cookie & fm->cookie_mask; } ofm->cookie_mask = fm->cookie_mask; if (fm->table_id != OFPTT_ALL || (protocol != OFPUTIL_P_OF11_STD && (fm->command == OFPFC_DELETE || fm->command == OFPFC_DELETE_STRICT))) { ofm->table_id = fm->table_id; } else { ofm->table_id = 0; } ofm->command = fm->command; ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = ofputil_port_to_ofp11(fm->out_port); ofm->out_group = htonl(fm->out_group); ofm->flags = raw_flags; if (version >= OFP14_VERSION && fm->command == OFPFC_ADD) { ofm->importance = htons(fm->importance); } else { ofm->importance = 0; } ofputil_put_ofp11_match(msg, &fm->match, protocol); ofpacts_put_openflow_instructions(fm->ofpacts, fm->ofpacts_len, msg, version); break; } case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_mod *ofm; msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION, fm->ofpacts_len); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); ofputil_match_to_ofp10_match(&fm->match, &ofm->match); ofm->cookie = fm->new_cookie; ofm->command = ofputil_tid_command(fm, protocol); ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = htons(ofp_to_u16(fm->out_port)); ofm->flags = raw_flags; ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, version); break; } case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_mod *nfm; int match_len; msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD, OFP10_VERSION, NXM_TYPICAL_LEN + fm->ofpacts_len); nfm = ofpbuf_put_zeros(msg, sizeof *nfm); nfm->command = ofputil_tid_command(fm, protocol); nfm->cookie = fm->new_cookie; match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask); nfm = msg->msg; nfm->idle_timeout = htons(fm->idle_timeout); nfm->hard_timeout = htons(fm->hard_timeout); nfm->priority = htons(fm->priority); nfm->buffer_id = htonl(fm->buffer_id); nfm->out_port = htons(ofp_to_u16(fm->out_port)); nfm->flags = raw_flags; nfm->match_len = htons(match_len); ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, version); break; } default: OVS_NOT_REACHED(); } ofpmsg_update_length(msg); return msg; } static enum ofperr ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr, const struct ofp10_flow_stats_request *ofsr, bool aggregate) { fsr->aggregate = aggregate; ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match); fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port)); fsr->out_group = OFPG_ANY; fsr->table_id = ofsr->table_id; fsr->cookie = fsr->cookie_mask = htonll(0); return 0; } static enum ofperr ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr, struct ofpbuf *b, bool aggregate) { const struct ofp11_flow_stats_request *ofsr; enum ofperr error; ofsr = ofpbuf_pull(b, sizeof *ofsr); fsr->aggregate = aggregate; fsr->table_id = ofsr->table_id; error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port); if (error) { return error; } fsr->out_group = ntohl(ofsr->out_group); fsr->cookie = ofsr->cookie; fsr->cookie_mask = ofsr->cookie_mask; error = ofputil_pull_ofp11_match(b, &fsr->match, NULL); if (error) { return error; } return 0; } static enum ofperr ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, struct ofpbuf *b, bool aggregate) { const struct nx_flow_stats_request *nfsr; enum ofperr error; nfsr = ofpbuf_pull(b, sizeof *nfsr); error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match, &fsr->cookie, &fsr->cookie_mask); if (error) { return error; } if (b->size) { return OFPERR_OFPBRC_BAD_LEN; } fsr->aggregate = aggregate; fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port)); fsr->out_group = OFPG_ANY; fsr->table_id = nfsr->table_id; return 0; } /* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified * 'port', suitable for OpenFlow version 'version'. */ struct ofpbuf * ofputil_encode_queue_get_config_request(enum ofp_version version, ofp_port_t port) { struct ofpbuf *request; if (version == OFP10_VERSION) { struct ofp10_queue_get_config_request *qgcr10; request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST, version, 0); qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10); qgcr10->port = htons(ofp_to_u16(port)); } else { struct ofp11_queue_get_config_request *qgcr11; request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST, version, 0); qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11); qgcr11->port = ofputil_port_to_ofp11(port); } return request; } /* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the * request into '*port'. Returns 0 if successful, otherwise an OpenFlow error * code. */ enum ofperr ofputil_decode_queue_get_config_request(const struct ofp_header *oh, ofp_port_t *port) { const struct ofp10_queue_get_config_request *qgcr10; const struct ofp11_queue_get_config_request *qgcr11; enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); switch ((int) raw) { case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: qgcr10 = b.data; *port = u16_to_ofp(ntohs(qgcr10->port)); return 0; case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: qgcr11 = b.data; return ofputil_port_from_ofp11(qgcr11->port, port); } OVS_NOT_REACHED(); } /* Constructs and returns the beginning of a reply to * OFPT_QUEUE_GET_CONFIG_REQUEST 'oh'. The caller may append information about * individual queues with ofputil_append_queue_get_config_reply(). */ struct ofpbuf * ofputil_encode_queue_get_config_reply(const struct ofp_header *oh) { struct ofp10_queue_get_config_reply *qgcr10; struct ofp11_queue_get_config_reply *qgcr11; struct ofpbuf *reply; enum ofperr error; struct ofpbuf b; enum ofpraw raw; ofp_port_t port; error = ofputil_decode_queue_get_config_request(oh, &port); ovs_assert(!error); ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); switch ((int) raw) { case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY, oh, 0); qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10); qgcr10->port = htons(ofp_to_u16(port)); break; case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY, oh, 0); qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11); qgcr11->port = ofputil_port_to_ofp11(port); break; default: OVS_NOT_REACHED(); } return reply; } static void put_queue_rate(struct ofpbuf *reply, enum ofp_queue_properties property, uint16_t rate) { if (rate != UINT16_MAX) { struct ofp_queue_prop_rate *oqpr; oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr); oqpr->prop_header.property = htons(property); oqpr->prop_header.len = htons(sizeof *oqpr); oqpr->rate = htons(rate); } } /* Appends a queue description for 'queue_id' to the * OFPT_QUEUE_GET_CONFIG_REPLY already in 'oh'. */ void ofputil_append_queue_get_config_reply(struct ofpbuf *reply, const struct ofputil_queue_config *oqc) { const struct ofp_header *oh = reply->data; size_t start_ofs, len_ofs; ovs_be16 *len; start_ofs = reply->size; if (oh->version < OFP12_VERSION) { struct ofp10_packet_queue *opq10; opq10 = ofpbuf_put_zeros(reply, sizeof *opq10); opq10->queue_id = htonl(oqc->queue_id); len_ofs = (char *) &opq10->len - (char *) reply->data; } else { struct ofp11_queue_get_config_reply *qgcr11; struct ofp12_packet_queue *opq12; ovs_be32 port; qgcr11 = reply->msg; port = qgcr11->port; opq12 = ofpbuf_put_zeros(reply, sizeof *opq12); opq12->port = port; opq12->queue_id = htonl(oqc->queue_id); len_ofs = (char *) &opq12->len - (char *) reply->data; } put_queue_rate(reply, OFPQT_MIN_RATE, oqc->min_rate); put_queue_rate(reply, OFPQT_MAX_RATE, oqc->max_rate); len = ofpbuf_at(reply, len_ofs, sizeof *len); *len = htons(reply->size - start_ofs); } /* Decodes the initial part of an OFPT_QUEUE_GET_CONFIG_REPLY from 'reply' and * stores in '*port' the port that the reply is about. The caller may call * ofputil_pull_queue_get_config_reply() to obtain information about individual * queues included in the reply. Returns 0 if successful, otherwise an * ofperr.*/ enum ofperr ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port) { const struct ofp10_queue_get_config_reply *qgcr10; const struct ofp11_queue_get_config_reply *qgcr11; enum ofpraw raw; raw = ofpraw_pull_assert(reply); switch ((int) raw) { case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY: qgcr10 = ofpbuf_pull(reply, sizeof *qgcr10); *port = u16_to_ofp(ntohs(qgcr10->port)); return 0; case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY: qgcr11 = ofpbuf_pull(reply, sizeof *qgcr11); return ofputil_port_from_ofp11(qgcr11->port, port); } OVS_NOT_REACHED(); } static enum ofperr parse_queue_rate(const struct ofp_queue_prop_header *hdr, uint16_t *rate) { const struct ofp_queue_prop_rate *oqpr; if (hdr->len == htons(sizeof *oqpr)) { oqpr = (const struct ofp_queue_prop_rate *) hdr; *rate = ntohs(oqpr->rate); return 0; } else { return OFPERR_OFPBRC_BAD_LEN; } } /* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in * 'reply' and stores it in '*queue'. ofputil_decode_queue_get_config_reply() * must already have pulled off the main header. * * This function returns EOF if the last queue has already been decoded, 0 if a * queue was successfully decoded into '*queue', or an ofperr if there was a * problem decoding 'reply'. */ int ofputil_pull_queue_get_config_reply(struct ofpbuf *reply, struct ofputil_queue_config *queue) { const struct ofp_header *oh; unsigned int opq_len; unsigned int len; if (!reply->size) { return EOF; } queue->min_rate = UINT16_MAX; queue->max_rate = UINT16_MAX; oh = reply->header; if (oh->version < OFP12_VERSION) { const struct ofp10_packet_queue *opq10; opq10 = ofpbuf_try_pull(reply, sizeof *opq10); if (!opq10) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue_id = ntohl(opq10->queue_id); len = ntohs(opq10->len); opq_len = sizeof *opq10; } else { const struct ofp12_packet_queue *opq12; opq12 = ofpbuf_try_pull(reply, sizeof *opq12); if (!opq12) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue_id = ntohl(opq12->queue_id); len = ntohs(opq12->len); opq_len = sizeof *opq12; } if (len < opq_len || len > reply->size + opq_len || len % 8) { return OFPERR_OFPBRC_BAD_LEN; } len -= opq_len; while (len > 0) { const struct ofp_queue_prop_header *hdr; unsigned int property; unsigned int prop_len; enum ofperr error = 0; hdr = ofpbuf_at_assert(reply, 0, sizeof *hdr); prop_len = ntohs(hdr->len); if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) { return OFPERR_OFPBRC_BAD_LEN; } property = ntohs(hdr->property); switch (property) { case OFPQT_MIN_RATE: error = parse_queue_rate(hdr, &queue->min_rate); break; case OFPQT_MAX_RATE: error = parse_queue_rate(hdr, &queue->max_rate); break; default: VLOG_INFO_RL(&bad_ofmsg_rl, "unknown queue property %u", property); break; } if (error) { return error; } ofpbuf_pull(reply, prop_len); len -= prop_len; } return 0; } /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE * request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if * successful, otherwise an OpenFlow error code. */ enum ofperr ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, const struct ofp_header *oh) { enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); switch ((int) raw) { case OFPRAW_OFPST10_FLOW_REQUEST: return ofputil_decode_ofpst10_flow_request(fsr, b.data, false); case OFPRAW_OFPST10_AGGREGATE_REQUEST: return ofputil_decode_ofpst10_flow_request(fsr, b.data, true); case OFPRAW_OFPST11_FLOW_REQUEST: return ofputil_decode_ofpst11_flow_request(fsr, &b, false); case OFPRAW_OFPST11_AGGREGATE_REQUEST: return ofputil_decode_ofpst11_flow_request(fsr, &b, true); case OFPRAW_NXST_FLOW_REQUEST: return ofputil_decode_nxst_flow_request(fsr, &b, false); case OFPRAW_NXST_AGGREGATE_REQUEST: return ofputil_decode_nxst_flow_request(fsr, &b, true); default: /* Hey, the caller lied. */ OVS_NOT_REACHED(); } } /* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW, * OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to * 'protocol', and returns the message. */ struct ofpbuf * ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, enum ofputil_protocol protocol) { struct ofpbuf *msg; enum ofpraw raw; switch (protocol) { case OFPUTIL_P_OF11_STD: case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: { struct ofp11_flow_stats_request *ofsr; raw = (fsr->aggregate ? OFPRAW_OFPST11_AGGREGATE_REQUEST : OFPRAW_OFPST11_FLOW_REQUEST); msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol), ofputil_match_typical_len(protocol)); ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr); ofsr->table_id = fsr->table_id; ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port); ofsr->out_group = htonl(fsr->out_group); ofsr->cookie = fsr->cookie; ofsr->cookie_mask = fsr->cookie_mask; ofputil_put_ofp11_match(msg, &fsr->match, protocol); break; } case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_stats_request *ofsr; raw = (fsr->aggregate ? OFPRAW_OFPST10_AGGREGATE_REQUEST : OFPRAW_OFPST10_FLOW_REQUEST); msg = ofpraw_alloc(raw, OFP10_VERSION, 0); ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr); ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match); ofsr->table_id = fsr->table_id; ofsr->out_port = htons(ofp_to_u16(fsr->out_port)); break; } case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_stats_request *nfsr; int match_len; raw = (fsr->aggregate ? OFPRAW_NXST_AGGREGATE_REQUEST : OFPRAW_NXST_FLOW_REQUEST); msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN); ofpbuf_put_zeros(msg, sizeof *nfsr); match_len = nx_put_match(msg, &fsr->match, fsr->cookie, fsr->cookie_mask); nfsr = msg->msg; nfsr->out_port = htons(ofp_to_u16(fsr->out_port)); nfsr->match_len = htons(match_len); nfsr->table_id = fsr->table_id; break; } default: OVS_NOT_REACHED(); } return msg; } /* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract * ofputil_flow_stats in 'fs'. * * Multiple OFPST_FLOW or NXST_FLOW replies can be packed into a single * OpenFlow message. Calling this function multiple times for a single 'msg' * iterates through the replies. The caller must initially leave 'msg''s layer * pointers null and not modify them between calls. * * Most switches don't send the values needed to populate fs->idle_age and * fs->hard_age, so those members will usually be set to 0. If the switch from * which 'msg' originated is known to implement NXT_FLOW_AGE, then pass * 'flow_age_extension' as true so that the contents of 'msg' determine the * 'idle_age' and 'hard_age' members in 'fs'. * * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats * reply's actions. The caller must initialize 'ofpacts' and retains ownership * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, struct ofpbuf *msg, bool flow_age_extension, struct ofpbuf *ofpacts) { const struct ofp_header *oh; size_t instructions_len; enum ofperr error; enum ofpraw raw; error = (msg->header ? ofpraw_decode(&raw, msg->header) : ofpraw_pull(&raw, msg)); if (error) { return error; } oh = msg->header; if (!msg->size) { return EOF; } else if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) { const struct ofp11_flow_stats *ofs; size_t length; uint16_t padded_match_len; ofs = ofpbuf_try_pull(msg, sizeof *ofs); if (!ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %"PRIu32" leftover " "bytes at end", msg->size); return EINVAL; } length = ntohs(ofs->length); if (length < sizeof *ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid " "length %"PRIuSIZE, length); return EINVAL; } if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match"); return EINVAL; } instructions_len = length - sizeof *ofs - padded_match_len; fs->priority = ntohs(ofs->priority); fs->table_id = ofs->table_id; fs->duration_sec = ntohl(ofs->duration_sec); fs->duration_nsec = ntohl(ofs->duration_nsec); fs->idle_timeout = ntohs(ofs->idle_timeout); fs->hard_timeout = ntohs(ofs->hard_timeout); if (oh->version >= OFP14_VERSION) { fs->importance = ntohs(ofs->importance); } else { fs->importance = 0; } if (raw == OFPRAW_OFPST13_FLOW_REPLY) { error = ofputil_decode_flow_mod_flags(ofs->flags, -1, oh->version, &fs->flags); if (error) { return error; } } else { fs->flags = 0; } fs->idle_age = -1; fs->hard_age = -1; fs->cookie = ofs->cookie; fs->packet_count = ntohll(ofs->packet_count); fs->byte_count = ntohll(ofs->byte_count); } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) { const struct ofp10_flow_stats *ofs; size_t length; ofs = ofpbuf_try_pull(msg, sizeof *ofs); if (!ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %"PRIu32" leftover " "bytes at end", msg->size); return EINVAL; } length = ntohs(ofs->length); if (length < sizeof *ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid " "length %"PRIuSIZE, length); return EINVAL; } instructions_len = length - sizeof *ofs; fs->cookie = get_32aligned_be64(&ofs->cookie); ofputil_match_from_ofp10_match(&ofs->match, &fs->match); fs->priority = ntohs(ofs->priority); fs->table_id = ofs->table_id; fs->duration_sec = ntohl(ofs->duration_sec); fs->duration_nsec = ntohl(ofs->duration_nsec); fs->idle_timeout = ntohs(ofs->idle_timeout); fs->hard_timeout = ntohs(ofs->hard_timeout); fs->importance = 0; fs->idle_age = -1; fs->hard_age = -1; fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count)); fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count)); fs->flags = 0; } else if (raw == OFPRAW_NXST_FLOW_REPLY) { const struct nx_flow_stats *nfs; size_t match_len, length; nfs = ofpbuf_try_pull(msg, sizeof *nfs); if (!nfs) { VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply has %"PRIu32" leftover " "bytes at end", msg->size); return EINVAL; } length = ntohs(nfs->length); match_len = ntohs(nfs->match_len); if (length < sizeof *nfs + ROUND_UP(match_len, 8)) { VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply with match_len=%"PRIuSIZE" " "claims invalid length %"PRIuSIZE, match_len, length); return EINVAL; } if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) { return EINVAL; } instructions_len = length - sizeof *nfs - ROUND_UP(match_len, 8); fs->cookie = nfs->cookie; fs->table_id = nfs->table_id; fs->duration_sec = ntohl(nfs->duration_sec); fs->duration_nsec = ntohl(nfs->duration_nsec); fs->priority = ntohs(nfs->priority); fs->idle_timeout = ntohs(nfs->idle_timeout); fs->hard_timeout = ntohs(nfs->hard_timeout); fs->importance = 0; fs->idle_age = -1; fs->hard_age = -1; if (flow_age_extension) { if (nfs->idle_age) { fs->idle_age = ntohs(nfs->idle_age) - 1; } if (nfs->hard_age) { fs->hard_age = ntohs(nfs->hard_age) - 1; } } fs->packet_count = ntohll(nfs->packet_count); fs->byte_count = ntohll(nfs->byte_count); fs->flags = 0; } else { OVS_NOT_REACHED(); } if (ofpacts_pull_openflow_instructions(msg, instructions_len, oh->version, ofpacts)) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions"); return EINVAL; } fs->ofpacts = ofpacts->data; fs->ofpacts_len = ofpacts->size; return 0; } /* Returns 'count' unchanged except that UINT64_MAX becomes 0. * * We use this in situations where OVS internally uses UINT64_MAX to mean * "value unknown" but OpenFlow 1.0 does not define any unknown value. */ static uint64_t unknown_to_zero(uint64_t count) { return count != UINT64_MAX ? count : 0; } /* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to * those already present in the list of ofpbufs in 'replies'. 'replies' should * have been initialized with ofpmp_init(). */ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct ovs_list *replies) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); size_t start_ofs = reply->size; enum ofp_version version = ofpmp_version(replies); enum ofpraw raw = ofpmp_decode_raw(replies); if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) { struct ofp11_flow_stats *ofs; ofpbuf_put_uninit(reply, sizeof *ofs); oxm_put_match(reply, &fs->match, version); ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply, version); ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); ofs->length = htons(reply->size - start_ofs); ofs->table_id = fs->table_id; ofs->pad = 0; ofs->duration_sec = htonl(fs->duration_sec); ofs->duration_nsec = htonl(fs->duration_nsec); ofs->priority = htons(fs->priority); ofs->idle_timeout = htons(fs->idle_timeout); ofs->hard_timeout = htons(fs->hard_timeout); if (version >= OFP14_VERSION) { ofs->importance = htons(fs->importance); } else { ofs->importance = 0; } if (raw == OFPRAW_OFPST13_FLOW_REPLY) { ofs->flags = ofputil_encode_flow_mod_flags(fs->flags, version); } else { ofs->flags = 0; } memset(ofs->pad2, 0, sizeof ofs->pad2); ofs->cookie = fs->cookie; ofs->packet_count = htonll(unknown_to_zero(fs->packet_count)); ofs->byte_count = htonll(unknown_to_zero(fs->byte_count)); } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) { struct ofp10_flow_stats *ofs; ofpbuf_put_uninit(reply, sizeof *ofs); ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply, version); ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); ofs->length = htons(reply->size - start_ofs); ofs->table_id = fs->table_id; ofs->pad = 0; ofputil_match_to_ofp10_match(&fs->match, &ofs->match); ofs->duration_sec = htonl(fs->duration_sec); ofs->duration_nsec = htonl(fs->duration_nsec); ofs->priority = htons(fs->priority); ofs->idle_timeout = htons(fs->idle_timeout); ofs->hard_timeout = htons(fs->hard_timeout); memset(ofs->pad2, 0, sizeof ofs->pad2); put_32aligned_be64(&ofs->cookie, fs->cookie); put_32aligned_be64(&ofs->packet_count, htonll(unknown_to_zero(fs->packet_count))); put_32aligned_be64(&ofs->byte_count, htonll(unknown_to_zero(fs->byte_count))); } else if (raw == OFPRAW_NXST_FLOW_REPLY) { struct nx_flow_stats *nfs; int match_len; ofpbuf_put_uninit(reply, sizeof *nfs); match_len = nx_put_match(reply, &fs->match, 0, 0); ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply, version); nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs); nfs->length = htons(reply->size - start_ofs); nfs->table_id = fs->table_id; nfs->pad = 0; nfs->duration_sec = htonl(fs->duration_sec); nfs->duration_nsec = htonl(fs->duration_nsec); nfs->priority = htons(fs->priority); nfs->idle_timeout = htons(fs->idle_timeout); nfs->hard_timeout = htons(fs->hard_timeout); nfs->idle_age = htons(fs->idle_age < 0 ? 0 : fs->idle_age < UINT16_MAX ? fs->idle_age + 1 : UINT16_MAX); nfs->hard_age = htons(fs->hard_age < 0 ? 0 : fs->hard_age < UINT16_MAX ? fs->hard_age + 1 : UINT16_MAX); nfs->match_len = htons(match_len); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); } else { OVS_NOT_REACHED(); } ofpmp_postappend(replies, start_ofs); } /* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or * NXST_AGGREGATE reply matching 'request', and returns the message. */ struct ofpbuf * ofputil_encode_aggregate_stats_reply( const struct ofputil_aggregate_stats *stats, const struct ofp_header *request) { struct ofp_aggregate_stats_reply *asr; uint64_t packet_count; uint64_t byte_count; struct ofpbuf *msg; enum ofpraw raw; ofpraw_decode(&raw, request); if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) { packet_count = unknown_to_zero(stats->packet_count); byte_count = unknown_to_zero(stats->byte_count); } else { packet_count = stats->packet_count; byte_count = stats->byte_count; } msg = ofpraw_alloc_stats_reply(request, 0); asr = ofpbuf_put_zeros(msg, sizeof *asr); put_32aligned_be64(&asr->packet_count, htonll(packet_count)); put_32aligned_be64(&asr->byte_count, htonll(byte_count)); asr->flow_count = htonl(stats->flow_count); return msg; } enum ofperr ofputil_decode_aggregate_stats_reply(struct ofputil_aggregate_stats *stats, const struct ofp_header *reply) { struct ofp_aggregate_stats_reply *asr; struct ofpbuf msg; ofpbuf_use_const(&msg, reply, ntohs(reply->length)); ofpraw_pull_assert(&msg); asr = msg.msg; stats->packet_count = ntohll(get_32aligned_be64(&asr->packet_count)); stats->byte_count = ntohll(get_32aligned_be64(&asr->byte_count)); stats->flow_count = ntohl(asr->flow_count); return 0; } /* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an * abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise * an OpenFlow error code. */ enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, const struct ofp_header *oh) { enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_FLOW_REMOVED) { const struct ofp12_flow_removed *ofr; enum ofperr error; ofr = ofpbuf_pull(&b, sizeof *ofr); error = ofputil_pull_ofp11_match(&b, &fr->match, NULL); if (error) { return error; } fr->priority = ntohs(ofr->priority); fr->cookie = ofr->cookie; fr->reason = ofr->reason; fr->table_id = ofr->table_id; fr->duration_sec = ntohl(ofr->duration_sec); fr->duration_nsec = ntohl(ofr->duration_nsec); fr->idle_timeout = ntohs(ofr->idle_timeout); fr->hard_timeout = ntohs(ofr->hard_timeout); fr->packet_count = ntohll(ofr->packet_count); fr->byte_count = ntohll(ofr->byte_count); } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) { const struct ofp10_flow_removed *ofr; ofr = ofpbuf_pull(&b, sizeof *ofr); ofputil_match_from_ofp10_match(&ofr->match, &fr->match); fr->priority = ntohs(ofr->priority); fr->cookie = ofr->cookie; fr->reason = ofr->reason; fr->table_id = 255; fr->duration_sec = ntohl(ofr->duration_sec); fr->duration_nsec = ntohl(ofr->duration_nsec); fr->idle_timeout = ntohs(ofr->idle_timeout); fr->hard_timeout = 0; fr->packet_count = ntohll(ofr->packet_count); fr->byte_count = ntohll(ofr->byte_count); } else if (raw == OFPRAW_NXT_FLOW_REMOVED) { struct nx_flow_removed *nfr; enum ofperr error; nfr = ofpbuf_pull(&b, sizeof *nfr); error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, NULL, NULL); if (error) { return error; } if (b.size) { return OFPERR_OFPBRC_BAD_LEN; } fr->priority = ntohs(nfr->priority); fr->cookie = nfr->cookie; fr->reason = nfr->reason; fr->table_id = nfr->table_id ? nfr->table_id - 1 : 255; fr->duration_sec = ntohl(nfr->duration_sec); fr->duration_nsec = ntohl(nfr->duration_nsec); fr->idle_timeout = ntohs(nfr->idle_timeout); fr->hard_timeout = 0; fr->packet_count = ntohll(nfr->packet_count); fr->byte_count = ntohll(nfr->byte_count); } else { OVS_NOT_REACHED(); } return 0; } /* Converts abstract ofputil_flow_removed 'fr' into an OFPT_FLOW_REMOVED or * NXT_FLOW_REMOVED message 'oh' according to 'protocol', and returns the * message. */ struct ofpbuf * ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, enum ofputil_protocol protocol) { struct ofpbuf *msg; enum ofp_flow_removed_reason reason = fr->reason; if (reason == OFPRR_METER_DELETE && !(protocol & OFPUTIL_P_OF14_UP)) { reason = OFPRR_DELETE; } switch (protocol) { case OFPUTIL_P_OF11_STD: case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: { struct ofp12_flow_removed *ofr; msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED, ofputil_protocol_to_ofp_version(protocol), htonl(0), ofputil_match_typical_len(protocol)); ofr = ofpbuf_put_zeros(msg, sizeof *ofr); ofr->cookie = fr->cookie; ofr->priority = htons(fr->priority); ofr->reason = reason; ofr->table_id = fr->table_id; ofr->duration_sec = htonl(fr->duration_sec); ofr->duration_nsec = htonl(fr->duration_nsec); ofr->idle_timeout = htons(fr->idle_timeout); ofr->hard_timeout = htons(fr->hard_timeout); ofr->packet_count = htonll(fr->packet_count); ofr->byte_count = htonll(fr->byte_count); ofputil_put_ofp11_match(msg, &fr->match, protocol); break; } case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_removed *ofr; msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION, htonl(0), 0); ofr = ofpbuf_put_zeros(msg, sizeof *ofr); ofputil_match_to_ofp10_match(&fr->match, &ofr->match); ofr->cookie = fr->cookie; ofr->priority = htons(fr->priority); ofr->reason = reason; ofr->duration_sec = htonl(fr->duration_sec); ofr->duration_nsec = htonl(fr->duration_nsec); ofr->idle_timeout = htons(fr->idle_timeout); ofr->packet_count = htonll(unknown_to_zero(fr->packet_count)); ofr->byte_count = htonll(unknown_to_zero(fr->byte_count)); break; } case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_removed *nfr; int match_len; msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION, htonl(0), NXM_TYPICAL_LEN); ofpbuf_put_zeros(msg, sizeof *nfr); match_len = nx_put_match(msg, &fr->match, 0, 0); nfr = msg->msg; nfr->cookie = fr->cookie; nfr->priority = htons(fr->priority); nfr->reason = reason; nfr->table_id = fr->table_id + 1; nfr->duration_sec = htonl(fr->duration_sec); nfr->duration_nsec = htonl(fr->duration_nsec); nfr->idle_timeout = htons(fr->idle_timeout); nfr->match_len = htons(match_len); nfr->packet_count = htonll(fr->packet_count); nfr->byte_count = htonll(fr->byte_count); break; } default: OVS_NOT_REACHED(); } return msg; } enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *pin, const struct ofp_header *oh) { enum ofpraw raw; struct ofpbuf b; memset(pin, 0, sizeof *pin); pin->cookie = OVS_BE64_MAX; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) { const struct ofp13_packet_in *opi; int error; size_t packet_in_size; if (raw == OFPRAW_OFPT12_PACKET_IN) { packet_in_size = sizeof (struct ofp12_packet_in); } else { packet_in_size = sizeof (struct ofp13_packet_in); } opi = ofpbuf_pull(&b, packet_in_size); error = oxm_pull_match_loose(&b, &pin->flow_metadata); if (error) { return error; } if (!ofpbuf_try_pull(&b, 2)) { return OFPERR_OFPBRC_BAD_LEN; } pin->reason = opi->pi.reason; pin->table_id = opi->pi.table_id; pin->buffer_id = ntohl(opi->pi.buffer_id); pin->total_len = ntohs(opi->pi.total_len); if (raw == OFPRAW_OFPT13_PACKET_IN) { pin->cookie = opi->cookie; } pin->packet = b.data; pin->packet_len = b.size; } else if (raw == OFPRAW_OFPT10_PACKET_IN) { const struct ofp10_packet_in *opi; opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data)); pin->packet = opi->data; pin->packet_len = b.size; match_init_catchall(&pin->flow_metadata); match_set_in_port(&pin->flow_metadata, u16_to_ofp(ntohs(opi->in_port))); pin->reason = opi->reason; pin->buffer_id = ntohl(opi->buffer_id); pin->total_len = ntohs(opi->total_len); } else if (raw == OFPRAW_OFPT11_PACKET_IN) { const struct ofp11_packet_in *opi; ofp_port_t in_port; enum ofperr error; opi = ofpbuf_pull(&b, sizeof *opi); pin->packet = b.data; pin->packet_len = b.size; pin->buffer_id = ntohl(opi->buffer_id); error = ofputil_port_from_ofp11(opi->in_port, &in_port); if (error) { return error; } match_init_catchall(&pin->flow_metadata); match_set_in_port(&pin->flow_metadata, in_port); pin->total_len = ntohs(opi->total_len); pin->reason = opi->reason; pin->table_id = opi->table_id; } else if (raw == OFPRAW_NXT_PACKET_IN) { const struct nx_packet_in *npi; int error; npi = ofpbuf_pull(&b, sizeof *npi); error = nx_pull_match_loose(&b, ntohs(npi->match_len), &pin->flow_metadata, NULL, NULL); if (error) { return error; } if (!ofpbuf_try_pull(&b, 2)) { return OFPERR_OFPBRC_BAD_LEN; } pin->reason = npi->reason; pin->table_id = npi->table_id; pin->cookie = npi->cookie; pin->buffer_id = ntohl(npi->buffer_id); pin->total_len = ntohs(npi->total_len); pin->packet = b.data; pin->packet_len = b.size; } else { OVS_NOT_REACHED(); } return 0; } static struct ofpbuf * ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin) { struct ofp10_packet_in *opi; struct ofpbuf *packet; packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, htonl(0), pin->packet_len); opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data)); opi->total_len = htons(pin->total_len); opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port)); opi->reason = pin->reason; opi->buffer_id = htonl(pin->buffer_id); ofpbuf_put(packet, pin->packet, pin->packet_len); return packet; } static struct ofpbuf * ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin) { struct nx_packet_in *npi; struct ofpbuf *packet; size_t match_len; /* The final argument is just an estimate of the space required. */ packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION, htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); ofpbuf_put_zeros(packet, sizeof *npi); match_len = nx_put_match(packet, &pin->flow_metadata, 0, 0); ofpbuf_put_zeros(packet, 2); ofpbuf_put(packet, pin->packet, pin->packet_len); npi = packet->msg; npi->buffer_id = htonl(pin->buffer_id); npi->total_len = htons(pin->total_len); npi->reason = pin->reason; npi->table_id = pin->table_id; npi->cookie = pin->cookie; npi->match_len = htons(match_len); return packet; } static struct ofpbuf * ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin) { struct ofp11_packet_in *opi; struct ofpbuf *packet; packet = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION, htonl(0), pin->packet_len); opi = ofpbuf_put_zeros(packet, sizeof *opi); opi->buffer_id = htonl(pin->buffer_id); opi->in_port = ofputil_port_to_ofp11(pin->flow_metadata.flow.in_port.ofp_port); opi->in_phy_port = opi->in_port; opi->total_len = htons(pin->total_len); opi->reason = pin->reason; opi->table_id = pin->table_id; ofpbuf_put(packet, pin->packet, pin->packet_len); return packet; } static struct ofpbuf * ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin, enum ofputil_protocol protocol) { struct ofp13_packet_in *opi; enum ofpraw packet_in_raw; enum ofp_version packet_in_version; size_t packet_in_size; struct ofpbuf *packet; if (protocol == OFPUTIL_P_OF12_OXM) { packet_in_raw = OFPRAW_OFPT12_PACKET_IN; packet_in_version = OFP12_VERSION; packet_in_size = sizeof (struct ofp12_packet_in); } else { packet_in_raw = OFPRAW_OFPT13_PACKET_IN; packet_in_version = ofputil_protocol_to_ofp_version(protocol); packet_in_size = sizeof (struct ofp13_packet_in); } /* The final argument is just an estimate of the space required. */ packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version, htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len); ofpbuf_put_zeros(packet, packet_in_size); oxm_put_match(packet, &pin->flow_metadata, ofputil_protocol_to_ofp_version(protocol)); ofpbuf_put_zeros(packet, 2); ofpbuf_put(packet, pin->packet, pin->packet_len); opi = packet->msg; opi->pi.buffer_id = htonl(pin->buffer_id); opi->pi.total_len = htons(pin->total_len); opi->pi.reason = pin->reason; opi->pi.table_id = pin->table_id; if (protocol != OFPUTIL_P_OF12_OXM) { opi->cookie = pin->cookie; } return packet; } /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message * in the format specified by 'packet_in_format'. */ struct ofpbuf * ofputil_encode_packet_in(const struct ofputil_packet_in *pin, enum ofputil_protocol protocol, enum nx_packet_in_format packet_in_format) { struct ofpbuf *packet; switch (protocol) { case OFPUTIL_P_OF10_STD: case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: packet = (packet_in_format == NXPIF_NXM ? ofputil_encode_nx_packet_in(pin) : ofputil_encode_ofp10_packet_in(pin)); break; case OFPUTIL_P_OF11_STD: packet = ofputil_encode_ofp11_packet_in(pin); break; case OFPUTIL_P_OF12_OXM: case OFPUTIL_P_OF13_OXM: case OFPUTIL_P_OF14_OXM: case OFPUTIL_P_OF15_OXM: packet = ofputil_encode_ofp12_packet_in(pin, protocol); break; default: OVS_NOT_REACHED(); } ofpmsg_update_length(packet); return packet; } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFPUTIL_PACKET_IN_REASON_BUFSIZE. */ const char * ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPR_NO_MATCH: return "no_match"; case OFPR_ACTION: return "action"; case OFPR_INVALID_TTL: return "invalid_ttl"; case OFPR_ACTION_SET: return "action_set"; case OFPR_GROUP: return "group"; case OFPR_PACKET_OUT: return "packet_out"; case OFPR_N_REASONS: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } bool ofputil_packet_in_reason_from_string(const char *s, enum ofp_packet_in_reason *reason) { int i; for (i = 0; i < OFPR_N_REASONS; i++) { char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; const char *reason_s; reason_s = ofputil_packet_in_reason_to_string(i, reasonbuf, sizeof reasonbuf); if (!strcasecmp(s, reason_s)) { *reason = i; return true; } } return false; } /* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in * 'po'. * * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out * message's actions. The caller must initialize 'ofpacts' and retains * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer. * * Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *po, const struct ofp_header *oh, struct ofpbuf *ofpacts) { enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_PACKET_OUT) { enum ofperr error; const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); po->buffer_id = ntohl(opo->buffer_id); error = ofputil_port_from_ofp11(opo->in_port, &po->in_port); if (error) { return error; } error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), oh->version, ofpacts); if (error) { return error; } } else if (raw == OFPRAW_OFPT10_PACKET_OUT) { enum ofperr error; const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); po->buffer_id = ntohl(opo->buffer_id); po->in_port = u16_to_ofp(ntohs(opo->in_port)); error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), oh->version, ofpacts); if (error) { return error; } } else { OVS_NOT_REACHED(); } if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX) && po->in_port != OFPP_LOCAL && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) { VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16, po->in_port); return OFPERR_OFPBRC_BAD_PORT; } po->ofpacts = ofpacts->data; po->ofpacts_len = ofpacts->size; if (po->buffer_id == UINT32_MAX) { po->packet = b.data; po->packet_len = b.size; } else { po->packet = NULL; po->packet_len = 0; } return 0; } /* ofputil_phy_port */ /* NETDEV_F_* to and from OFPPF_* and OFPPF10_*. */ BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */ BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */ BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */ BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */ BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */ BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */ BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */ /* NETDEV_F_ bits 11...15 are OFPPF10_ bits 7...11: */ BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF10_COPPER << 4)); BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF10_FIBER << 4)); BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF10_AUTONEG << 4)); BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF10_PAUSE << 4)); BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF10_PAUSE_ASYM << 4)); static enum netdev_features netdev_port_features_from_ofp10(ovs_be32 ofp10_) { uint32_t ofp10 = ntohl(ofp10_); return (ofp10 & 0x7f) | ((ofp10 & 0xf80) << 4); } static ovs_be32 netdev_port_features_to_ofp10(enum netdev_features features) { return htonl((features & 0x7f) | ((features & 0xf800) >> 4)); } BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */ BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */ BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */ BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */ BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */ BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */ BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */ BUILD_ASSERT_DECL((int) NETDEV_F_40GB_FD == OFPPF11_40GB_FD); /* bit 7 */ BUILD_ASSERT_DECL((int) NETDEV_F_100GB_FD == OFPPF11_100GB_FD); /* bit 8 */ BUILD_ASSERT_DECL((int) NETDEV_F_1TB_FD == OFPPF11_1TB_FD); /* bit 9 */ BUILD_ASSERT_DECL((int) NETDEV_F_OTHER == OFPPF11_OTHER); /* bit 10 */ BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == OFPPF11_COPPER); /* bit 11 */ BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == OFPPF11_FIBER); /* bit 12 */ BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == OFPPF11_AUTONEG); /* bit 13 */ BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == OFPPF11_PAUSE); /* bit 14 */ BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == OFPPF11_PAUSE_ASYM);/* bit 15 */ static enum netdev_features netdev_port_features_from_ofp11(ovs_be32 ofp11) { return ntohl(ofp11) & 0xffff; } static ovs_be32 netdev_port_features_to_ofp11(enum netdev_features features) { return htonl(features & 0xffff); } static enum ofperr ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp, const struct ofp10_phy_port *opp) { pp->port_no = u16_to_ofp(ntohs(opp->port_no)); pp->hw_addr = opp->hw_addr; ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN); pp->config = ntohl(opp->config) & OFPPC10_ALL; pp->state = ntohl(opp->state) & OFPPS10_ALL; pp->curr = netdev_port_features_from_ofp10(opp->curr); pp->advertised = netdev_port_features_from_ofp10(opp->advertised); pp->supported = netdev_port_features_from_ofp10(opp->supported); pp->peer = netdev_port_features_from_ofp10(opp->peer); pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000; pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000; return 0; } static enum ofperr ofputil_decode_ofp11_port(struct ofputil_phy_port *pp, const struct ofp11_port *op) { enum ofperr error; error = ofputil_port_from_ofp11(op->port_no, &pp->port_no); if (error) { return error; } pp->hw_addr = op->hw_addr; ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN); pp->config = ntohl(op->config) & OFPPC11_ALL; pp->state = ntohl(op->state) & OFPPS11_ALL; pp->curr = netdev_port_features_from_ofp11(op->curr); pp->advertised = netdev_port_features_from_ofp11(op->advertised); pp->supported = netdev_port_features_from_ofp11(op->supported); pp->peer = netdev_port_features_from_ofp11(op->peer); pp->curr_speed = ntohl(op->curr_speed); pp->max_speed = ntohl(op->max_speed); return 0; } static enum ofperr parse_ofp14_port_ethernet_property(const struct ofpbuf *payload, struct ofputil_phy_port *pp) { struct ofp14_port_desc_prop_ethernet *eth = payload->data; if (payload->size != sizeof *eth) { return OFPERR_OFPBPC_BAD_LEN; } pp->curr = netdev_port_features_from_ofp11(eth->curr); pp->advertised = netdev_port_features_from_ofp11(eth->advertised); pp->supported = netdev_port_features_from_ofp11(eth->supported); pp->peer = netdev_port_features_from_ofp11(eth->peer); pp->curr_speed = ntohl(eth->curr_speed); pp->max_speed = ntohl(eth->max_speed); return 0; } static enum ofperr ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg) { struct ofpbuf properties; struct ofp14_port *op; enum ofperr error; size_t len; op = ofpbuf_try_pull(msg, sizeof *op); if (!op) { return OFPERR_OFPBRC_BAD_LEN; } len = ntohs(op->length); if (len < sizeof *op || len - sizeof *op > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } len -= sizeof *op; ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); error = ofputil_port_from_ofp11(op->port_no, &pp->port_no); if (error) { return error; } pp->hw_addr = op->hw_addr; ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN); pp->config = ntohl(op->config) & OFPPC11_ALL; pp->state = ntohl(op->state) & OFPPS11_ALL; while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; uint16_t type; error = ofputil_pull_property(&properties, &payload, &type); if (error) { return error; } switch (type) { case OFPPDPT14_ETHERNET: error = parse_ofp14_port_ethernet_property(&payload, pp); break; default: log_property(true, "unknown port property %"PRIu16, type); error = 0; break; } if (error) { return error; } } return 0; } static void ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp, struct ofp10_phy_port *opp) { memset(opp, 0, sizeof *opp); opp->port_no = htons(ofp_to_u16(pp->port_no)); opp->hw_addr = pp->hw_addr; ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN); opp->config = htonl(pp->config & OFPPC10_ALL); opp->state = htonl(pp->state & OFPPS10_ALL); opp->curr = netdev_port_features_to_ofp10(pp->curr); opp->advertised = netdev_port_features_to_ofp10(pp->advertised); opp->supported = netdev_port_features_to_ofp10(pp->supported); opp->peer = netdev_port_features_to_ofp10(pp->peer); } static void ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp, struct ofp11_port *op) { memset(op, 0, sizeof *op); op->port_no = ofputil_port_to_ofp11(pp->port_no); op->hw_addr = pp->hw_addr; ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN); op->config = htonl(pp->config & OFPPC11_ALL); op->state = htonl(pp->state & OFPPS11_ALL); op->curr = netdev_port_features_to_ofp11(pp->curr); op->advertised = netdev_port_features_to_ofp11(pp->advertised); op->supported = netdev_port_features_to_ofp11(pp->supported); op->peer = netdev_port_features_to_ofp11(pp->peer); op->curr_speed = htonl(pp->curr_speed); op->max_speed = htonl(pp->max_speed); } static void ofputil_put_ofp14_port(const struct ofputil_phy_port *pp, struct ofpbuf *b) { struct ofp14_port *op; struct ofp14_port_desc_prop_ethernet *eth; ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth); op = ofpbuf_put_zeros(b, sizeof *op); op->port_no = ofputil_port_to_ofp11(pp->port_no); op->length = htons(sizeof *op + sizeof *eth); op->hw_addr = pp->hw_addr; ovs_strlcpy(op->name, pp->name, sizeof op->name); op->config = htonl(pp->config & OFPPC11_ALL); op->state = htonl(pp->state & OFPPS11_ALL); eth = ofpbuf_put_zeros(b, sizeof *eth); eth->type = htons(OFPPDPT14_ETHERNET); eth->length = htons(sizeof *eth); eth->curr = netdev_port_features_to_ofp11(pp->curr); eth->advertised = netdev_port_features_to_ofp11(pp->advertised); eth->supported = netdev_port_features_to_ofp11(pp->supported); eth->peer = netdev_port_features_to_ofp11(pp->peer); eth->curr_speed = htonl(pp->curr_speed); eth->max_speed = htonl(pp->max_speed); } static void ofputil_put_phy_port(enum ofp_version ofp_version, const struct ofputil_phy_port *pp, struct ofpbuf *b) { switch (ofp_version) { case OFP10_VERSION: { struct ofp10_phy_port *opp = ofpbuf_put_uninit(b, sizeof *opp); ofputil_encode_ofp10_phy_port(pp, opp); break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: { struct ofp11_port *op = ofpbuf_put_uninit(b, sizeof *op); ofputil_encode_ofp11_port(pp, op); break; } case OFP14_VERSION: case OFP15_VERSION: ofputil_put_ofp14_port(pp, b); break; default: OVS_NOT_REACHED(); } } enum ofperr ofputil_decode_port_desc_stats_request(const struct ofp_header *request, ofp_port_t *port) { struct ofpbuf b; enum ofpraw raw; ofpbuf_use_const(&b, request, ntohs(request->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPST10_PORT_DESC_REQUEST) { *port = OFPP_ANY; return 0; } else if (raw == OFPRAW_OFPST15_PORT_DESC_REQUEST) { ovs_be32 *ofp11_port; ofp11_port = ofpbuf_pull(&b, sizeof *ofp11_port); return ofputil_port_from_ofp11(*ofp11_port, port); } else { OVS_NOT_REACHED(); } } struct ofpbuf * ofputil_encode_port_desc_stats_request(enum ofp_version ofp_version, ofp_port_t port) { struct ofpbuf *request; switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: request = ofpraw_alloc(OFPRAW_OFPST10_PORT_DESC_REQUEST, ofp_version, 0); break; case OFP15_VERSION:{ struct ofp15_port_desc_request *req; request = ofpraw_alloc(OFPRAW_OFPST15_PORT_DESC_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->port_no = ofputil_port_to_ofp11(port); break; } default: OVS_NOT_REACHED(); } return request; } void ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp, struct ovs_list *replies) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); size_t start_ofs = reply->size; ofputil_put_phy_port(ofpmp_version(replies), pp, reply); ofpmp_postappend(replies, start_ofs); } /* ofputil_switch_features */ #define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \ OFPC_IP_REASM | OFPC_QUEUE_STATS) BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS); BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS); BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS); BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM); BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS); BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP); BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_BLOCKED == OFPC12_PORT_BLOCKED); BUILD_ASSERT_DECL((int) OFPUTIL_C_BUNDLES == OFPC14_BUNDLES); BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_MONITORING == OFPC14_FLOW_MONITORING); static uint32_t ofputil_capabilities_mask(enum ofp_version ofp_version) { /* Handle capabilities whose bit is unique for all OpenFlow versions */ switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: return OFPC_COMMON | OFPC_ARP_MATCH_IP; case OFP12_VERSION: case OFP13_VERSION: return OFPC_COMMON | OFPC12_PORT_BLOCKED; case OFP14_VERSION: case OFP15_VERSION: return OFPC_COMMON | OFPC12_PORT_BLOCKED | OFPC14_BUNDLES | OFPC14_FLOW_MONITORING; default: /* Caller needs to check osf->header.version itself */ return 0; } } /* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an * abstract representation in '*features'. Initializes '*b' to iterate over * the OpenFlow port structures following 'osf' with later calls to * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an * OFPERR_* value. */ enum ofperr ofputil_decode_switch_features(const struct ofp_header *oh, struct ofputil_switch_features *features, struct ofpbuf *b) { const struct ofp_switch_features *osf; enum ofpraw raw; ofpbuf_use_const(b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(b); osf = ofpbuf_pull(b, sizeof *osf); features->datapath_id = ntohll(osf->datapath_id); features->n_buffers = ntohl(osf->n_buffers); features->n_tables = osf->n_tables; features->auxiliary_id = 0; features->capabilities = ntohl(osf->capabilities) & ofputil_capabilities_mask(oh->version); if (raw == OFPRAW_OFPT10_FEATURES_REPLY) { if (osf->capabilities & htonl(OFPC10_STP)) { features->capabilities |= OFPUTIL_C_STP; } features->ofpacts = ofpact_bitmap_from_openflow(osf->actions, OFP10_VERSION); } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY || raw == OFPRAW_OFPT13_FEATURES_REPLY) { if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) { features->capabilities |= OFPUTIL_C_GROUP_STATS; } features->ofpacts = 0; if (raw == OFPRAW_OFPT13_FEATURES_REPLY) { features->auxiliary_id = osf->auxiliary_id; } } else { return OFPERR_OFPBRC_BAD_VERSION; } return 0; } /* In OpenFlow 1.0, 1.1, and 1.2, an OFPT_FEATURES_REPLY message lists all the * switch's ports, unless there are too many to fit. In OpenFlow 1.3 and * later, an OFPT_FEATURES_REPLY does not list ports at all. * * Given a buffer 'b' that contains a Features Reply message, this message * checks if it contains a complete list of the switch's ports. Returns true, * if so. Returns false if the list is missing (OF1.3+) or incomplete * (OF1.0/1.1/1.2), and in the latter case removes all of the ports from the * message. * * When this function returns false, the caller should send an OFPST_PORT_DESC * stats request to get the ports. */ bool ofputil_switch_features_has_ports(struct ofpbuf *b) { struct ofp_header *oh = b->data; size_t phy_port_size; if (oh->version >= OFP13_VERSION) { /* OpenFlow 1.3+ never has ports in the feature reply. */ return false; } phy_port_size = (oh->version == OFP10_VERSION ? sizeof(struct ofp10_phy_port) : sizeof(struct ofp11_port)); if (ntohs(oh->length) + phy_port_size <= UINT16_MAX) { /* There's room for additional ports in the feature reply. * Assume that the list is complete. */ return true; } /* The feature reply has no room for more ports. Probably the list is * truncated. Drop the ports and tell the caller to retrieve them with * OFPST_PORT_DESC. */ b->size = sizeof *oh + sizeof(struct ofp_switch_features); ofpmsg_update_length(b); return false; } /* Returns a buffer owned by the caller that encodes 'features' in the format * required by 'protocol' with the given 'xid'. The caller should append port * information to the buffer with subsequent calls to * ofputil_put_switch_features_port(). */ struct ofpbuf * ofputil_encode_switch_features(const struct ofputil_switch_features *features, enum ofputil_protocol protocol, ovs_be32 xid) { struct ofp_switch_features *osf; struct ofpbuf *b; enum ofp_version version; enum ofpraw raw; version = ofputil_protocol_to_ofp_version(protocol); switch (version) { case OFP10_VERSION: raw = OFPRAW_OFPT10_FEATURES_REPLY; break; case OFP11_VERSION: case OFP12_VERSION: raw = OFPRAW_OFPT11_FEATURES_REPLY; break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: raw = OFPRAW_OFPT13_FEATURES_REPLY; break; default: OVS_NOT_REACHED(); } b = ofpraw_alloc_xid(raw, version, xid, 0); osf = ofpbuf_put_zeros(b, sizeof *osf); osf->datapath_id = htonll(features->datapath_id); osf->n_buffers = htonl(features->n_buffers); osf->n_tables = features->n_tables; osf->capabilities = htonl(features->capabilities & ofputil_capabilities_mask(version)); switch (version) { case OFP10_VERSION: if (features->capabilities & OFPUTIL_C_STP) { osf->capabilities |= htonl(OFPC10_STP); } osf->actions = ofpact_bitmap_to_openflow(features->ofpacts, OFP10_VERSION); break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: osf->auxiliary_id = features->auxiliary_id; /* fall through */ case OFP11_VERSION: case OFP12_VERSION: if (features->capabilities & OFPUTIL_C_GROUP_STATS) { osf->capabilities |= htonl(OFPC11_GROUP_STATS); } break; default: OVS_NOT_REACHED(); } return b; } /* Encodes 'pp' into the format required by the switch_features message already * in 'b', which should have been returned by ofputil_encode_switch_features(), * and appends the encoded version to 'b'. */ void ofputil_put_switch_features_port(const struct ofputil_phy_port *pp, struct ofpbuf *b) { const struct ofp_header *oh = b->data; if (oh->version < OFP13_VERSION) { /* Try adding a port description to the message, but drop it again if * the buffer overflows. (This possibility for overflow is why * OpenFlow 1.3+ moved port descriptions into a multipart message.) */ size_t start_ofs = b->size; ofputil_put_phy_port(oh->version, pp, b); if (b->size > UINT16_MAX) { b->size = start_ofs; } } } /* ofputil_port_status */ /* Decodes the OpenFlow "port status" message in '*ops' into an abstract form * in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_port_status(const struct ofp_header *oh, struct ofputil_port_status *ps) { const struct ofp_port_status *ops; struct ofpbuf b; int retval; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); ops = ofpbuf_pull(&b, sizeof *ops); if (ops->reason != OFPPR_ADD && ops->reason != OFPPR_DELETE && ops->reason != OFPPR_MODIFY) { return OFPERR_NXBRC_BAD_REASON; } ps->reason = ops->reason; retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc); ovs_assert(retval != EOF); return retval; } /* Converts the abstract form of a "port status" message in '*ps' into an * OpenFlow message suitable for 'protocol', and returns that encoded form in * a buffer owned by the caller. */ struct ofpbuf * ofputil_encode_port_status(const struct ofputil_port_status *ps, enum ofputil_protocol protocol) { struct ofp_port_status *ops; struct ofpbuf *b; enum ofp_version version; enum ofpraw raw; version = ofputil_protocol_to_ofp_version(protocol); switch (version) { case OFP10_VERSION: raw = OFPRAW_OFPT10_PORT_STATUS; break; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: raw = OFPRAW_OFPT11_PORT_STATUS; break; case OFP14_VERSION: case OFP15_VERSION: raw = OFPRAW_OFPT14_PORT_STATUS; break; default: OVS_NOT_REACHED(); } b = ofpraw_alloc_xid(raw, version, htonl(0), 0); ops = ofpbuf_put_zeros(b, sizeof *ops); ops->reason = ps->reason; ofputil_put_phy_port(version, &ps->desc, b); ofpmsg_update_length(b); return b; } /* ofputil_port_mod */ static enum ofperr parse_port_mod_ethernet_property(struct ofpbuf *property, struct ofputil_port_mod *pm) { struct ofp14_port_mod_prop_ethernet *eth = property->data; if (property->size != sizeof *eth) { return OFPERR_OFPBRC_BAD_LEN; } pm->advertise = netdev_port_features_from_ofp11(eth->advertise); return 0; } /* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_port_mod(const struct ofp_header *oh, struct ofputil_port_mod *pm, bool loose) { enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT10_PORT_MOD) { const struct ofp10_port_mod *opm = b.data; pm->port_no = u16_to_ofp(ntohs(opm->port_no)); pm->hw_addr = opm->hw_addr; pm->config = ntohl(opm->config) & OFPPC10_ALL; pm->mask = ntohl(opm->mask) & OFPPC10_ALL; pm->advertise = netdev_port_features_from_ofp10(opm->advertise); } else if (raw == OFPRAW_OFPT11_PORT_MOD) { const struct ofp11_port_mod *opm = b.data; enum ofperr error; error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no); if (error) { return error; } pm->hw_addr = opm->hw_addr; pm->config = ntohl(opm->config) & OFPPC11_ALL; pm->mask = ntohl(opm->mask) & OFPPC11_ALL; pm->advertise = netdev_port_features_from_ofp11(opm->advertise); } else if (raw == OFPRAW_OFPT14_PORT_MOD) { const struct ofp14_port_mod *opm = ofpbuf_pull(&b, sizeof *opm); enum ofperr error; memset(pm, 0, sizeof *pm); error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no); if (error) { return error; } pm->hw_addr = opm->hw_addr; pm->config = ntohl(opm->config) & OFPPC11_ALL; pm->mask = ntohl(opm->mask) & OFPPC11_ALL; while (b.size > 0) { struct ofpbuf property; enum ofperr error; uint16_t type; error = ofputil_pull_property(&b, &property, &type); if (error) { return error; } switch (type) { case OFPPMPT14_ETHERNET: error = parse_port_mod_ethernet_property(&property, pm); break; default: log_property(loose, "unknown port_mod property %"PRIu16, type); if (loose) { error = 0; } else if (type == OFPPMPT14_EXPERIMENTER) { error = OFPERR_OFPBPC_BAD_EXPERIMENTER; } else { error = OFPERR_OFPBRC_BAD_TYPE; } break; } if (error) { return error; } } } else { return OFPERR_OFPBRC_BAD_TYPE; } pm->config &= pm->mask; return 0; } /* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow * message suitable for 'protocol', and returns that encoded form in a buffer * owned by the caller. */ struct ofpbuf * ofputil_encode_port_mod(const struct ofputil_port_mod *pm, enum ofputil_protocol protocol) { enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); struct ofpbuf *b; switch (ofp_version) { case OFP10_VERSION: { struct ofp10_port_mod *opm; b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0); opm = ofpbuf_put_zeros(b, sizeof *opm); opm->port_no = htons(ofp_to_u16(pm->port_no)); opm->hw_addr = pm->hw_addr; opm->config = htonl(pm->config & OFPPC10_ALL); opm->mask = htonl(pm->mask & OFPPC10_ALL); opm->advertise = netdev_port_features_to_ofp10(pm->advertise); break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: { struct ofp11_port_mod *opm; b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0); opm = ofpbuf_put_zeros(b, sizeof *opm); opm->port_no = ofputil_port_to_ofp11(pm->port_no); opm->hw_addr = pm->hw_addr; opm->config = htonl(pm->config & OFPPC11_ALL); opm->mask = htonl(pm->mask & OFPPC11_ALL); opm->advertise = netdev_port_features_to_ofp11(pm->advertise); break; } case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_port_mod_prop_ethernet *eth; struct ofp14_port_mod *opm; b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, sizeof *eth); opm = ofpbuf_put_zeros(b, sizeof *opm); opm->port_no = ofputil_port_to_ofp11(pm->port_no); opm->hw_addr = pm->hw_addr; opm->config = htonl(pm->config & OFPPC11_ALL); opm->mask = htonl(pm->mask & OFPPC11_ALL); if (pm->advertise) { eth = ofpbuf_put_zeros(b, sizeof *eth); eth->type = htons(OFPPMPT14_ETHERNET); eth->length = htons(sizeof *eth); eth->advertise = netdev_port_features_to_ofp11(pm->advertise); } break; } default: OVS_NOT_REACHED(); } return b; } /* Table features. */ static enum ofperr pull_table_feature_property(struct ofpbuf *msg, struct ofpbuf *payload, uint16_t *typep) { enum ofperr error; error = ofputil_pull_property(msg, payload, typep); if (payload && !error) { ofpbuf_pull(payload, sizeof(struct ofp_prop_header)); } return error; } static enum ofperr parse_action_bitmap(struct ofpbuf *payload, enum ofp_version ofp_version, uint64_t *ofpacts) { uint32_t types = 0; while (payload->size > 0) { uint16_t type; enum ofperr error; error = ofputil_pull_property__(payload, NULL, 1, &type); if (error) { return error; } if (type < CHAR_BIT * sizeof types) { types |= 1u << type; } } *ofpacts = ofpact_bitmap_from_openflow(htonl(types), ofp_version); return 0; } static enum ofperr parse_instruction_ids(struct ofpbuf *payload, bool loose, uint32_t *insts) { *insts = 0; while (payload->size > 0) { enum ovs_instruction_type inst; enum ofperr error; uint16_t ofpit; /* OF1.3 and OF1.4 aren't clear about padding in the instruction IDs. * It seems clear that they aren't padded to 8 bytes, though, because * both standards say that "non-experimenter instructions are 4 bytes" * and do not mention any padding before the first instruction ID. * (There wouldn't be any point in padding to 8 bytes if the IDs were * aligned on an odd 4-byte boundary.) * * Anyway, we just assume they're all glommed together on byte * boundaries. */ error = ofputil_pull_property__(payload, NULL, 1, &ofpit); if (error) { return error; } error = ovs_instruction_type_from_inst_type(&inst, ofpit); if (!error) { *insts |= 1u << inst; } else if (!loose) { return error; } } return 0; } static enum ofperr parse_table_features_next_table(struct ofpbuf *payload, unsigned long int *next_tables) { size_t i; memset(next_tables, 0, bitmap_n_bytes(255)); for (i = 0; i < payload->size; i++) { uint8_t id = ((const uint8_t *) payload->data)[i]; if (id >= 255) { return OFPERR_OFPBPC_BAD_VALUE; } bitmap_set1(next_tables, id); } return 0; } static enum ofperr parse_oxms(struct ofpbuf *payload, bool loose, struct mf_bitmap *exactp, struct mf_bitmap *maskedp) { struct mf_bitmap exact = MF_BITMAP_INITIALIZER; struct mf_bitmap masked = MF_BITMAP_INITIALIZER; while (payload->size > 0) { const struct mf_field *field; enum ofperr error; bool hasmask; error = nx_pull_header(payload, &field, &hasmask); if (!error) { bitmap_set1(hasmask ? masked.bm : exact.bm, field->id); } else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) { return error; } } if (exactp) { *exactp = exact; } else if (!bitmap_is_all_zeros(exact.bm, MFF_N_IDS)) { return OFPERR_OFPBMC_BAD_MASK; } if (maskedp) { *maskedp = masked; } else if (!bitmap_is_all_zeros(masked.bm, MFF_N_IDS)) { return OFPERR_OFPBMC_BAD_MASK; } return 0; } /* Converts an OFPMP_TABLE_FEATURES request or reply in 'msg' into an abstract * ofputil_table_features in 'tf'. * * If 'loose' is true, this function ignores properties and values that it does * not understand, as a controller would want to do when interpreting * capabilities provided by a switch. If 'loose' is false, this function * treats unknown properties and values as an error, as a switch would want to * do when interpreting a configuration request made by a controller. * * A single OpenFlow message can specify features for multiple tables. Calling * this function multiple times for a single 'msg' iterates through the tables * in the message. The caller must initially leave 'msg''s layer pointers null * and not modify them between calls. * * Returns 0 if successful, EOF if no tables were left in this 'msg', otherwise * a positive "enum ofperr" value. */ int ofputil_decode_table_features(struct ofpbuf *msg, struct ofputil_table_features *tf, bool loose) { const struct ofp_header *oh; struct ofp13_table_features *otf; struct ofpbuf properties; unsigned int len; memset(tf, 0, sizeof *tf); if (!msg->header) { ofpraw_pull_assert(msg); } oh = msg->header; if (!msg->size) { return EOF; } if (msg->size < sizeof *otf) { return OFPERR_OFPBPC_BAD_LEN; } otf = msg->data; len = ntohs(otf->length); if (len < sizeof *otf || len % 8 || len > msg->size) { return OFPERR_OFPBPC_BAD_LEN; } ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); ofpbuf_pull(&properties, sizeof *otf); tf->table_id = otf->table_id; if (tf->table_id == OFPTT_ALL) { return OFPERR_OFPTFFC_BAD_TABLE; } ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN); tf->metadata_match = otf->metadata_match; tf->metadata_write = otf->metadata_write; tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT; if (oh->version >= OFP14_VERSION) { uint32_t caps = ntohl(otf->capabilities); tf->supports_eviction = (caps & OFPTC14_EVICTION) != 0; tf->supports_vacancy_events = (caps & OFPTC14_VACANCY_EVENTS) != 0; } else { tf->supports_eviction = -1; tf->supports_vacancy_events = -1; } tf->max_entries = ntohl(otf->max_entries); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; uint16_t type; error = pull_table_feature_property(&properties, &payload, &type); if (error) { return error; } switch ((enum ofp13_table_feature_prop_type) type) { case OFPTFPT13_INSTRUCTIONS: error = parse_instruction_ids(&payload, loose, &tf->nonmiss.instructions); break; case OFPTFPT13_INSTRUCTIONS_MISS: error = parse_instruction_ids(&payload, loose, &tf->miss.instructions); break; case OFPTFPT13_NEXT_TABLES: error = parse_table_features_next_table(&payload, tf->nonmiss.next); break; case OFPTFPT13_NEXT_TABLES_MISS: error = parse_table_features_next_table(&payload, tf->miss.next); break; case OFPTFPT13_WRITE_ACTIONS: error = parse_action_bitmap(&payload, oh->version, &tf->nonmiss.write.ofpacts); break; case OFPTFPT13_WRITE_ACTIONS_MISS: error = parse_action_bitmap(&payload, oh->version, &tf->miss.write.ofpacts); break; case OFPTFPT13_APPLY_ACTIONS: error = parse_action_bitmap(&payload, oh->version, &tf->nonmiss.apply.ofpacts); break; case OFPTFPT13_APPLY_ACTIONS_MISS: error = parse_action_bitmap(&payload, oh->version, &tf->miss.apply.ofpacts); break; case OFPTFPT13_MATCH: error = parse_oxms(&payload, loose, &tf->match, &tf->mask); break; case OFPTFPT13_WILDCARDS: error = parse_oxms(&payload, loose, &tf->wildcard, NULL); break; case OFPTFPT13_WRITE_SETFIELD: error = parse_oxms(&payload, loose, &tf->nonmiss.write.set_fields, NULL); break; case OFPTFPT13_WRITE_SETFIELD_MISS: error = parse_oxms(&payload, loose, &tf->miss.write.set_fields, NULL); break; case OFPTFPT13_APPLY_SETFIELD: error = parse_oxms(&payload, loose, &tf->nonmiss.apply.set_fields, NULL); break; case OFPTFPT13_APPLY_SETFIELD_MISS: error = parse_oxms(&payload, loose, &tf->miss.apply.set_fields, NULL); break; case OFPTFPT13_EXPERIMENTER: case OFPTFPT13_EXPERIMENTER_MISS: default: log_property(loose, "unknown table features property %"PRIu16, type); error = loose ? 0 : OFPERR_OFPBPC_BAD_TYPE; break; } if (error) { return error; } } /* Fix inconsistencies: * * - Turn on 'match' bits that are set in 'mask', because maskable * fields are matchable. * * - Turn on 'wildcard' bits that are set in 'mask', because a field * that is arbitrarily maskable can be wildcarded entirely. * * - Turn off 'wildcard' bits that are not in 'match', because a field * must be matchable for it to be meaningfully wildcarded. */ bitmap_or(tf->match.bm, tf->mask.bm, MFF_N_IDS); bitmap_or(tf->wildcard.bm, tf->mask.bm, MFF_N_IDS); bitmap_and(tf->wildcard.bm, tf->match.bm, MFF_N_IDS); return 0; } /* Encodes and returns a request to obtain the table features of a switch. * The message is encoded for OpenFlow version 'ofp_version'. */ struct ofpbuf * ofputil_encode_table_features_request(enum ofp_version ofp_version) { struct ofpbuf *request = NULL; switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: case OFP12_VERSION: ovs_fatal(0, "dump-table-features needs OpenFlow 1.3 or later " "(\'-O OpenFlow13\')"); case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST, ofp_version, 0); break; default: OVS_NOT_REACHED(); } return request; } static void put_fields_property(struct ofpbuf *reply, const struct mf_bitmap *fields, const struct mf_bitmap *masks, enum ofp13_table_feature_prop_type property, enum ofp_version version) { size_t start_ofs; int field; start_ofs = start_property(reply, property); BITMAP_FOR_EACH_1 (field, MFF_N_IDS, fields->bm) { nx_put_header(reply, field, version, masks && bitmap_is_set(masks->bm, field)); } end_property(reply, start_ofs); } static void put_table_action_features(struct ofpbuf *reply, const struct ofputil_table_action_features *taf, enum ofp13_table_feature_prop_type actions_type, enum ofp13_table_feature_prop_type set_fields_type, int miss_offset, enum ofp_version version) { size_t start_ofs; start_ofs = start_property(reply, actions_type + miss_offset); put_bitmap_properties(reply, ntohl(ofpact_bitmap_to_openflow(taf->ofpacts, version))); end_property(reply, start_ofs); put_fields_property(reply, &taf->set_fields, NULL, set_fields_type + miss_offset, version); } static void put_table_instruction_features( struct ofpbuf *reply, const struct ofputil_table_instruction_features *tif, int miss_offset, enum ofp_version version) { size_t start_ofs; uint8_t table_id; start_ofs = start_property(reply, OFPTFPT13_INSTRUCTIONS + miss_offset); put_bitmap_properties(reply, ntohl(ovsinst_bitmap_to_openflow(tif->instructions, version))); end_property(reply, start_ofs); start_ofs = start_property(reply, OFPTFPT13_NEXT_TABLES + miss_offset); BITMAP_FOR_EACH_1 (table_id, 255, tif->next) { ofpbuf_put(reply, &table_id, 1); } end_property(reply, start_ofs); put_table_action_features(reply, &tif->write, OFPTFPT13_WRITE_ACTIONS, OFPTFPT13_WRITE_SETFIELD, miss_offset, version); put_table_action_features(reply, &tif->apply, OFPTFPT13_APPLY_ACTIONS, OFPTFPT13_APPLY_SETFIELD, miss_offset, version); } void ofputil_append_table_features_reply(const struct ofputil_table_features *tf, struct ovs_list *replies) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); enum ofp_version version = ofpmp_version(replies); size_t start_ofs = reply->size; struct ofp13_table_features *otf; otf = ofpbuf_put_zeros(reply, sizeof *otf); otf->table_id = tf->table_id; ovs_strlcpy(otf->name, tf->name, sizeof otf->name); otf->metadata_match = tf->metadata_match; otf->metadata_write = tf->metadata_write; if (version >= OFP14_VERSION) { if (tf->supports_eviction) { otf->capabilities |= htonl(OFPTC14_EVICTION); } if (tf->supports_vacancy_events) { otf->capabilities |= htonl(OFPTC14_VACANCY_EVENTS); } } otf->max_entries = htonl(tf->max_entries); put_table_instruction_features(reply, &tf->nonmiss, 0, version); put_table_instruction_features(reply, &tf->miss, 1, version); put_fields_property(reply, &tf->match, &tf->mask, OFPTFPT13_MATCH, version); put_fields_property(reply, &tf->wildcard, NULL, OFPTFPT13_WILDCARDS, version); otf = ofpbuf_at_assert(reply, start_ofs, sizeof *otf); otf->length = htons(reply->size - start_ofs); ofpmp_postappend(replies, start_ofs); } static enum ofperr parse_table_desc_eviction_property(struct ofpbuf *property, struct ofputil_table_desc *td) { struct ofp14_table_mod_prop_eviction *ote = property->data; if (property->size != sizeof *ote) { return OFPERR_OFPBPC_BAD_LEN; } td->eviction_flags = ntohl(ote->flags); return 0; } static enum ofperr parse_table_desc_vacancy_property(struct ofpbuf *property, struct ofputil_table_desc *td) { struct ofp14_table_mod_prop_vacancy *otv = property->data; if (property->size != sizeof *otv) { return OFPERR_OFPBPC_BAD_LEN; } td->table_vacancy.vacancy_down = otv->vacancy_down; td->table_vacancy.vacancy_up = otv->vacancy_up; td->table_vacancy.vacancy = otv->vacancy; return 0; } /* Decodes the next OpenFlow "table desc" message (of possibly several) from * 'msg' into an abstract form in '*td'. Returns 0 if successful, EOF if the * last "table desc" in 'msg' was already decoded, otherwise an OFPERR_* * value. */ int ofputil_decode_table_desc(struct ofpbuf *msg, struct ofputil_table_desc *td, enum ofp_version version) { struct ofp14_table_desc *otd; struct ofpbuf properties; size_t length; memset(td, 0, sizeof *td); if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } otd = ofpbuf_try_pull(msg, sizeof *otd); if (!otd) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply has %"PRIu32" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } td->table_id = otd->table_id; length = ntohs(otd->length); if (length < sizeof *otd || length - sizeof *otd > msg->size) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFP14_TABLE_DESC reply claims invalid " "length %"PRIuSIZE, length); return OFPERR_OFPBRC_BAD_LEN; } length -= sizeof *otd; ofpbuf_use_const(&properties, ofpbuf_pull(msg, length), length); td->eviction = ofputil_decode_table_eviction(otd->config, version); td->vacancy = ofputil_decode_table_vacancy(otd->config, version); td->eviction_flags = UINT32_MAX; while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; uint16_t type; error = ofputil_pull_property(&properties, &payload, &type); if (error) { return error; } switch (type) { case OFPTMPT14_EVICTION: error = parse_table_desc_eviction_property(&payload, td); break; case OFPTMPT14_VACANCY: error = parse_table_desc_vacancy_property(&payload, td); break; default: log_property(true, "unknown table_desc property %"PRIu16, type); error = 0; break; } if (error) { return error; } } return 0; } /* Encodes and returns a request to obtain description of tables of a switch. * The message is encoded for OpenFlow version 'ofp_version'. */ struct ofpbuf * ofputil_encode_table_desc_request(enum ofp_version ofp_version) { struct ofpbuf *request = NULL; if (ofp_version >= OFP14_VERSION) { request = ofpraw_alloc(OFPRAW_OFPST14_TABLE_DESC_REQUEST, ofp_version, 0); } else { ovs_fatal(0, "dump-table-desc needs OpenFlow 1.4 or later " "(\'-O OpenFlow14\')"); } return request; } /* Function to append Table desc information in a reply list. */ void ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, struct ovs_list *replies, enum ofp_version version) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); size_t start_otd; struct ofp14_table_desc *otd; start_otd = reply->size; ofpbuf_put_zeros(reply, sizeof *otd); if (td->eviction_flags != UINT32_MAX) { struct ofp14_table_mod_prop_eviction *ote; ote = ofpbuf_put_zeros(reply, sizeof *ote); ote->type = htons(OFPTMPT14_EVICTION); ote->length = htons(sizeof *ote); ote->flags = htonl(td->eviction_flags); } if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) { struct ofp14_table_mod_prop_vacancy *otv; otv = ofpbuf_put_zeros(reply, sizeof *otv); otv->type = htons(OFPTMPT14_VACANCY); otv->length = htons(sizeof *otv); otv->vacancy_down = td->table_vacancy.vacancy_down; otv->vacancy_up = td->table_vacancy.vacancy_up; otv->vacancy = td->table_vacancy.vacancy; } otd = ofpbuf_at_assert(reply, start_otd, sizeof *otd); otd->length = htons(reply->size - start_otd); otd->table_id = td->table_id; otd->config = ofputil_encode_table_config(OFPUTIL_TABLE_MISS_DEFAULT, td->eviction, td->vacancy, version); ofpmp_postappend(replies, start_otd); } /* This function parses Vacancy property, and decodes the * ofp14_table_mod_prop_vacancy in ofputil_table_mod. * Returns OFPERR_OFPBPC_BAD_VALUE error code when vacancy_down is * greater than vacancy_up and also when current vacancy has non-zero * value. Returns 0 on success. */ static enum ofperr parse_table_mod_vacancy_property(struct ofpbuf *property, struct ofputil_table_mod *tm) { struct ofp14_table_mod_prop_vacancy *otv = property->data; if (property->size != sizeof *otv) { return OFPERR_OFPBPC_BAD_LEN; } tm->table_vacancy.vacancy_down = otv->vacancy_down; tm->table_vacancy.vacancy_up = otv->vacancy_up; if (tm->table_vacancy.vacancy_down > tm->table_vacancy.vacancy_up) { log_property(false, "Value of vacancy_down is greater than " "vacancy_up"); return OFPERR_OFPBPC_BAD_VALUE; } if (tm->table_vacancy.vacancy_down > 100 || tm->table_vacancy.vacancy_up > 100) { log_property(false, "Vacancy threshold percentage should not be" "greater than 100"); return OFPERR_OFPBPC_BAD_VALUE; } tm->table_vacancy.vacancy = otv->vacancy; if (tm->table_vacancy.vacancy) { log_property(false, "Vacancy value should be zero for table-mod " "messages"); return OFPERR_OFPBPC_BAD_VALUE; } return 0; } /* Given 'config', taken from an OpenFlow 'version' message that specifies * table configuration (a table mod, table stats, or table features message), * returns the table vacancy configuration that it specifies. * * Only OpenFlow 1.4 and later specify table vacancy configuration this way, * so for other 'version' this function always returns * OFPUTIL_TABLE_VACANCY_DEFAULT. */ static enum ofputil_table_vacancy ofputil_decode_table_vacancy(ovs_be32 config, enum ofp_version version) { return (version < OFP14_VERSION ? OFPUTIL_TABLE_VACANCY_DEFAULT : config & htonl(OFPTC14_VACANCY_EVENTS) ? OFPUTIL_TABLE_VACANCY_ON : OFPUTIL_TABLE_VACANCY_OFF); } static enum ofperr parse_table_mod_eviction_property(struct ofpbuf *property, struct ofputil_table_mod *tm) { struct ofp14_table_mod_prop_eviction *ote = property->data; if (property->size != sizeof *ote) { return OFPERR_OFPBPC_BAD_LEN; } tm->eviction_flags = ntohl(ote->flags); return 0; } /* Given 'config', taken from an OpenFlow 'version' message that specifies * table configuration (a table mod, table stats, or table features message), * returns the table eviction configuration that it specifies. * * Only OpenFlow 1.4 and later specify table eviction configuration this way, * so for other 'version' values this function always returns * OFPUTIL_TABLE_EVICTION_DEFAULT. */ static enum ofputil_table_eviction ofputil_decode_table_eviction(ovs_be32 config, enum ofp_version version) { return (version < OFP14_VERSION ? OFPUTIL_TABLE_EVICTION_DEFAULT : config & htonl(OFPTC14_EVICTION) ? OFPUTIL_TABLE_EVICTION_ON : OFPUTIL_TABLE_EVICTION_OFF); } /* Returns a bitmap of OFPTC* values suitable for 'config' fields in various * OpenFlow messages of the given 'version', based on the provided 'miss' and * 'eviction' values. */ static ovs_be32 ofputil_encode_table_config(enum ofputil_table_miss miss, enum ofputil_table_eviction eviction, enum ofputil_table_vacancy vacancy, enum ofp_version version) { uint32_t config = 0; /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more * information on the crazy evolution of this field. */ switch (version) { case OFP10_VERSION: /* OpenFlow 1.0 didn't have such a field, any value ought to do. */ return htonl(0); case OFP11_VERSION: case OFP12_VERSION: /* OpenFlow 1.1 and 1.2 define only OFPTC11_TABLE_MISS_*. */ switch (miss) { case OFPUTIL_TABLE_MISS_DEFAULT: /* Really this shouldn't be used for encoding (the caller should * provide a specific value) but I can't imagine that defaulting to * the fall-through case here will hurt. */ case OFPUTIL_TABLE_MISS_CONTROLLER: default: return htonl(OFPTC11_TABLE_MISS_CONTROLLER); case OFPUTIL_TABLE_MISS_CONTINUE: return htonl(OFPTC11_TABLE_MISS_CONTINUE); case OFPUTIL_TABLE_MISS_DROP: return htonl(OFPTC11_TABLE_MISS_DROP); } OVS_NOT_REACHED(); case OFP13_VERSION: /* OpenFlow 1.3 removed OFPTC11_TABLE_MISS_* and didn't define any new * flags, so this is correct. */ return htonl(0); case OFP14_VERSION: case OFP15_VERSION: /* OpenFlow 1.4 introduced OFPTC14_EVICTION and * OFPTC14_VACANCY_EVENTS. */ if (eviction == OFPUTIL_TABLE_EVICTION_ON) { config |= OFPTC14_EVICTION; } if (vacancy == OFPUTIL_TABLE_VACANCY_ON) { config |= OFPTC14_VACANCY_EVENTS; } return htonl(config); } OVS_NOT_REACHED(); } /* Given 'config', taken from an OpenFlow 'version' message that specifies * table configuration (a table mod, table stats, or table features message), * returns the table miss configuration that it specifies. * * Only OpenFlow 1.1 and 1.2 specify table miss configurations this way, so for * other 'version' values this function always returns * OFPUTIL_TABLE_MISS_DEFAULT. */ static enum ofputil_table_miss ofputil_decode_table_miss(ovs_be32 config_, enum ofp_version version) { uint32_t config = ntohl(config_); if (version == OFP11_VERSION || version == OFP12_VERSION) { switch (config & OFPTC11_TABLE_MISS_MASK) { case OFPTC11_TABLE_MISS_CONTROLLER: return OFPUTIL_TABLE_MISS_CONTROLLER; case OFPTC11_TABLE_MISS_CONTINUE: return OFPUTIL_TABLE_MISS_CONTINUE; case OFPTC11_TABLE_MISS_DROP: return OFPUTIL_TABLE_MISS_DROP; default: VLOG_WARN_RL(&bad_ofmsg_rl, "bad table miss config %d", config); return OFPUTIL_TABLE_MISS_CONTROLLER; } } else { return OFPUTIL_TABLE_MISS_DEFAULT; } } /* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_table_mod(const struct ofp_header *oh, struct ofputil_table_mod *pm) { enum ofpraw raw; struct ofpbuf b; memset(pm, 0, sizeof *pm); pm->miss = OFPUTIL_TABLE_MISS_DEFAULT; pm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; pm->eviction_flags = UINT32_MAX; pm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_TABLE_MOD) { const struct ofp11_table_mod *otm = b.data; pm->table_id = otm->table_id; pm->miss = ofputil_decode_table_miss(otm->config, oh->version); } else if (raw == OFPRAW_OFPT14_TABLE_MOD) { const struct ofp14_table_mod *otm = ofpbuf_pull(&b, sizeof *otm); pm->table_id = otm->table_id; pm->miss = ofputil_decode_table_miss(otm->config, oh->version); pm->eviction = ofputil_decode_table_eviction(otm->config, oh->version); pm->vacancy = ofputil_decode_table_vacancy(otm->config, oh->version); while (b.size > 0) { struct ofpbuf property; enum ofperr error; uint16_t type; error = ofputil_pull_property(&b, &property, &type); if (error) { return error; } switch (type) { case OFPTMPT14_EVICTION: error = parse_table_mod_eviction_property(&property, pm); break; case OFPTMPT14_VACANCY: error = parse_table_mod_vacancy_property(&property, pm); break; default: error = OFPERR_OFPBRC_BAD_TYPE; break; } if (error) { return error; } } } else { return OFPERR_OFPBRC_BAD_TYPE; } return 0; } /* Converts the abstract form of a "table mod" message in '*tm' into an * OpenFlow message suitable for 'protocol', and returns that encoded form in a * buffer owned by the caller. */ struct ofpbuf * ofputil_encode_table_mod(const struct ofputil_table_mod *tm, enum ofputil_protocol protocol) { enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); struct ofpbuf *b; switch (ofp_version) { case OFP10_VERSION: { ovs_fatal(0, "table mod needs OpenFlow 1.1 or later " "(\'-O OpenFlow11\')"); break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: { struct ofp11_table_mod *otm; b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0); otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, tm->vacancy, ofp_version); break; } case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_table_mod *otm; struct ofp14_table_mod_prop_eviction *ote; struct ofp14_table_mod_prop_vacancy *otv; b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0); otm = ofpbuf_put_zeros(b, sizeof *otm); otm->table_id = tm->table_id; otm->config = ofputil_encode_table_config(tm->miss, tm->eviction, tm->vacancy, ofp_version); if (tm->eviction_flags != UINT32_MAX) { ote = ofpbuf_put_zeros(b, sizeof *ote); ote->type = htons(OFPTMPT14_EVICTION); ote->length = htons(sizeof *ote); ote->flags = htonl(tm->eviction_flags); } if (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON) { otv = ofpbuf_put_zeros(b, sizeof *otv); otv->type = htons(OFPTMPT14_VACANCY); otv->length = htons(sizeof *otv); otv->vacancy_down = tm->table_vacancy.vacancy_down; otv->vacancy_up = tm->table_vacancy.vacancy_up; } break; } default: OVS_NOT_REACHED(); } return b; } /* ofputil_role_request */ /* Decodes the OpenFlow "role request" or "role reply" message in '*oh' into * an abstract form in '*rr'. Returns 0 if successful, otherwise an * OFPERR_* value. */ enum ofperr ofputil_decode_role_message(const struct ofp_header *oh, struct ofputil_role_request *rr) { struct ofpbuf b; enum ofpraw raw; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT12_ROLE_REQUEST || raw == OFPRAW_OFPT12_ROLE_REPLY) { const struct ofp12_role_request *orr = b.msg; if (orr->role != htonl(OFPCR12_ROLE_NOCHANGE) && orr->role != htonl(OFPCR12_ROLE_EQUAL) && orr->role != htonl(OFPCR12_ROLE_MASTER) && orr->role != htonl(OFPCR12_ROLE_SLAVE)) { return OFPERR_OFPRRFC_BAD_ROLE; } rr->role = ntohl(orr->role); if (raw == OFPRAW_OFPT12_ROLE_REQUEST ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE) : orr->generation_id == OVS_BE64_MAX) { rr->have_generation_id = false; rr->generation_id = 0; } else { rr->have_generation_id = true; rr->generation_id = ntohll(orr->generation_id); } } else if (raw == OFPRAW_NXT_ROLE_REQUEST || raw == OFPRAW_NXT_ROLE_REPLY) { const struct nx_role_request *nrr = b.msg; BUILD_ASSERT(NX_ROLE_OTHER + 1 == OFPCR12_ROLE_EQUAL); BUILD_ASSERT(NX_ROLE_MASTER + 1 == OFPCR12_ROLE_MASTER); BUILD_ASSERT(NX_ROLE_SLAVE + 1 == OFPCR12_ROLE_SLAVE); if (nrr->role != htonl(NX_ROLE_OTHER) && nrr->role != htonl(NX_ROLE_MASTER) && nrr->role != htonl(NX_ROLE_SLAVE)) { return OFPERR_OFPRRFC_BAD_ROLE; } rr->role = ntohl(nrr->role) + 1; rr->have_generation_id = false; rr->generation_id = 0; } else { OVS_NOT_REACHED(); } return 0; } /* Returns an encoded form of a role reply suitable for the "request" in a * buffer owned by the caller. */ struct ofpbuf * ofputil_encode_role_reply(const struct ofp_header *request, const struct ofputil_role_request *rr) { struct ofpbuf *buf; enum ofpraw raw; raw = ofpraw_decode_assert(request); if (raw == OFPRAW_OFPT12_ROLE_REQUEST) { struct ofp12_role_request *orr; buf = ofpraw_alloc_reply(OFPRAW_OFPT12_ROLE_REPLY, request, 0); orr = ofpbuf_put_zeros(buf, sizeof *orr); orr->role = htonl(rr->role); orr->generation_id = htonll(rr->have_generation_id ? rr->generation_id : UINT64_MAX); } else if (raw == OFPRAW_NXT_ROLE_REQUEST) { struct nx_role_request *nrr; BUILD_ASSERT(NX_ROLE_OTHER == OFPCR12_ROLE_EQUAL - 1); BUILD_ASSERT(NX_ROLE_MASTER == OFPCR12_ROLE_MASTER - 1); BUILD_ASSERT(NX_ROLE_SLAVE == OFPCR12_ROLE_SLAVE - 1); buf = ofpraw_alloc_reply(OFPRAW_NXT_ROLE_REPLY, request, 0); nrr = ofpbuf_put_zeros(buf, sizeof *nrr); nrr->role = htonl(rr->role - 1); } else { OVS_NOT_REACHED(); } return buf; } /* Encodes "role status" message 'status' for sending in the given * 'protocol'. Returns the role status message, if 'protocol' supports them, * otherwise a null pointer. */ struct ofpbuf * ofputil_encode_role_status(const struct ofputil_role_status *status, enum ofputil_protocol protocol) { enum ofp_version version; version = ofputil_protocol_to_ofp_version(protocol); if (version >= OFP14_VERSION) { struct ofp14_role_status *rstatus; struct ofpbuf *buf; buf = ofpraw_alloc_xid(OFPRAW_OFPT14_ROLE_STATUS, version, htonl(0), 0); rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus); rstatus->role = htonl(status->role); rstatus->reason = status->reason; rstatus->generation_id = htonll(status->generation_id); return buf; } else { return NULL; } } enum ofperr ofputil_decode_role_status(const struct ofp_header *oh, struct ofputil_role_status *rs) { struct ofpbuf b; enum ofpraw raw; const struct ofp14_role_status *r; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS); r = b.msg; if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) && r->role != htonl(OFPCR12_ROLE_EQUAL) && r->role != htonl(OFPCR12_ROLE_MASTER) && r->role != htonl(OFPCR12_ROLE_SLAVE)) { return OFPERR_OFPRRFC_BAD_ROLE; } rs->role = ntohl(r->role); rs->generation_id = ntohll(r->generation_id); rs->reason = r->reason; return 0; } /* Encodes 'rf' according to 'protocol', and returns the encoded message. * 'protocol' must be for OpenFlow 1.4 or later. */ struct ofpbuf * ofputil_encode_requestforward(const struct ofputil_requestforward *rf, enum ofputil_protocol protocol) { enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); struct ofpbuf *inner; switch (rf->reason) { case OFPRFR_GROUP_MOD: inner = ofputil_encode_group_mod(ofp_version, rf->group_mod); break; case OFPRFR_METER_MOD: inner = ofputil_encode_meter_mod(ofp_version, rf->meter_mod); break; case OFPRFR_N_REASONS: default: OVS_NOT_REACHED(); } struct ofp_header *inner_oh = inner->data; inner_oh->xid = rf->xid; inner_oh->length = htons(inner->size); struct ofpbuf *outer = ofpraw_alloc_xid(OFPRAW_OFPT14_REQUESTFORWARD, ofp_version, htonl(0), inner->size); ofpbuf_put(outer, inner->data, inner->size); ofpbuf_delete(inner); return outer; } /* Decodes OFPT_REQUESTFORWARD message 'outer'. On success, puts the decoded * form into '*rf' and returns 0, and the caller is later responsible for * freeing the content of 'rf', with ofputil_destroy_requestforward(rf). On * failure, returns an ofperr and '*rf' is indeterminate. */ enum ofperr ofputil_decode_requestforward(const struct ofp_header *outer, struct ofputil_requestforward *rf) { struct ofpbuf b; enum ofperr error; ofpbuf_use_const(&b, outer, ntohs(outer->length)); /* Skip past outer message. */ enum ofpraw outer_raw = ofpraw_pull_assert(&b); ovs_assert(outer_raw == OFPRAW_OFPT14_REQUESTFORWARD); /* Validate inner message. */ if (b.size < sizeof(struct ofp_header)) { return OFPERR_OFPBFC_MSG_BAD_LEN; } const struct ofp_header *inner = b.data; unsigned int inner_len = ntohs(inner->length); if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) { return OFPERR_OFPBFC_MSG_BAD_LEN; } if (inner->version != outer->version) { return OFPERR_OFPBRC_BAD_VERSION; } /* Parse inner message. */ enum ofptype type; error = ofptype_decode(&type, inner); if (error) { return error; } rf->xid = inner->xid; if (type == OFPTYPE_GROUP_MOD) { rf->reason = OFPRFR_GROUP_MOD; rf->group_mod = xmalloc(sizeof *rf->group_mod); error = ofputil_decode_group_mod(inner, rf->group_mod); if (error) { free(rf->group_mod); return error; } } else if (type == OFPTYPE_METER_MOD) { rf->reason = OFPRFR_METER_MOD; rf->meter_mod = xmalloc(sizeof *rf->meter_mod); ofpbuf_init(&rf->bands, 64); error = ofputil_decode_meter_mod(inner, rf->meter_mod, &rf->bands); if (error) { free(rf->meter_mod); ofpbuf_uninit(&rf->bands); return error; } } else { return OFPERR_OFPBFC_MSG_UNSUP; } return 0; } /* Frees the content of 'rf', which should have been initialized through a * successful call to ofputil_decode_requestforward(). */ void ofputil_destroy_requestforward(struct ofputil_requestforward *rf) { if (!rf) { return; } switch (rf->reason) { case OFPRFR_GROUP_MOD: ofputil_uninit_group_mod(rf->group_mod); free(rf->group_mod); break; case OFPRFR_METER_MOD: ofpbuf_uninit(&rf->bands); free(rf->meter_mod); break; case OFPRFR_N_REASONS: OVS_NOT_REACHED(); } } /* Table stats. */ /* OpenFlow 1.0 and 1.1 don't distinguish between a field that cannot be * matched and a field that must be wildcarded. This function returns a bitmap * that contains both kinds of fields. */ static struct mf_bitmap wild_or_nonmatchable_fields(const struct ofputil_table_features *features) { struct mf_bitmap wc = features->match; bitmap_not(wc.bm, MFF_N_IDS); bitmap_or(wc.bm, features->wildcard.bm, MFF_N_IDS); return wc; } struct ofp10_wc_map { enum ofp10_flow_wildcards wc10; enum mf_field_id mf; }; static const struct ofp10_wc_map ofp10_wc_map[] = { { OFPFW10_IN_PORT, MFF_IN_PORT }, { OFPFW10_DL_VLAN, MFF_VLAN_VID }, { OFPFW10_DL_SRC, MFF_ETH_SRC }, { OFPFW10_DL_DST, MFF_ETH_DST}, { OFPFW10_DL_TYPE, MFF_ETH_TYPE }, { OFPFW10_NW_PROTO, MFF_IP_PROTO }, { OFPFW10_TP_SRC, MFF_TCP_SRC }, { OFPFW10_TP_DST, MFF_TCP_DST }, { OFPFW10_NW_SRC_MASK, MFF_IPV4_SRC }, { OFPFW10_NW_DST_MASK, MFF_IPV4_DST }, { OFPFW10_DL_VLAN_PCP, MFF_VLAN_PCP }, { OFPFW10_NW_TOS, MFF_IP_DSCP }, }; static ovs_be32 mf_bitmap_to_of10(const struct mf_bitmap *fields) { const struct ofp10_wc_map *p; uint32_t wc10 = 0; for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) { if (bitmap_is_set(fields->bm, p->mf)) { wc10 |= p->wc10; } } return htonl(wc10); } static struct mf_bitmap mf_bitmap_from_of10(ovs_be32 wc10_) { struct mf_bitmap fields = MF_BITMAP_INITIALIZER; const struct ofp10_wc_map *p; uint32_t wc10 = ntohl(wc10_); for (p = ofp10_wc_map; p < &ofp10_wc_map[ARRAY_SIZE(ofp10_wc_map)]; p++) { if (wc10 & p->wc10) { bitmap_set1(fields.bm, p->mf); } } return fields; } static void ofputil_put_ofp10_table_stats(const struct ofputil_table_stats *stats, const struct ofputil_table_features *features, struct ofpbuf *buf) { struct mf_bitmap wc = wild_or_nonmatchable_fields(features); struct ofp10_table_stats *out; out = ofpbuf_put_zeros(buf, sizeof *out); out->table_id = features->table_id; ovs_strlcpy(out->name, features->name, sizeof out->name); out->wildcards = mf_bitmap_to_of10(&wc); out->max_entries = htonl(features->max_entries); out->active_count = htonl(stats->active_count); put_32aligned_be64(&out->lookup_count, htonll(stats->lookup_count)); put_32aligned_be64(&out->matched_count, htonll(stats->matched_count)); } struct ofp11_wc_map { enum ofp11_flow_match_fields wc11; enum mf_field_id mf; }; static const struct ofp11_wc_map ofp11_wc_map[] = { { OFPFMF11_IN_PORT, MFF_IN_PORT }, { OFPFMF11_DL_VLAN, MFF_VLAN_VID }, { OFPFMF11_DL_VLAN_PCP, MFF_VLAN_PCP }, { OFPFMF11_DL_TYPE, MFF_ETH_TYPE }, { OFPFMF11_NW_TOS, MFF_IP_DSCP }, { OFPFMF11_NW_PROTO, MFF_IP_PROTO }, { OFPFMF11_TP_SRC, MFF_TCP_SRC }, { OFPFMF11_TP_DST, MFF_TCP_DST }, { OFPFMF11_MPLS_LABEL, MFF_MPLS_LABEL }, { OFPFMF11_MPLS_TC, MFF_MPLS_TC }, /* I don't know what OFPFMF11_TYPE means. */ { OFPFMF11_DL_SRC, MFF_ETH_SRC }, { OFPFMF11_DL_DST, MFF_ETH_DST }, { OFPFMF11_NW_SRC, MFF_IPV4_SRC }, { OFPFMF11_NW_DST, MFF_IPV4_DST }, { OFPFMF11_METADATA, MFF_METADATA }, }; static ovs_be32 mf_bitmap_to_of11(const struct mf_bitmap *fields) { const struct ofp11_wc_map *p; uint32_t wc11 = 0; for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) { if (bitmap_is_set(fields->bm, p->mf)) { wc11 |= p->wc11; } } return htonl(wc11); } static struct mf_bitmap mf_bitmap_from_of11(ovs_be32 wc11_) { struct mf_bitmap fields = MF_BITMAP_INITIALIZER; const struct ofp11_wc_map *p; uint32_t wc11 = ntohl(wc11_); for (p = ofp11_wc_map; p < &ofp11_wc_map[ARRAY_SIZE(ofp11_wc_map)]; p++) { if (wc11 & p->wc11) { bitmap_set1(fields.bm, p->mf); } } return fields; } static void ofputil_put_ofp11_table_stats(const struct ofputil_table_stats *stats, const struct ofputil_table_features *features, struct ofpbuf *buf) { struct mf_bitmap wc = wild_or_nonmatchable_fields(features); struct ofp11_table_stats *out; out = ofpbuf_put_zeros(buf, sizeof *out); out->table_id = features->table_id; ovs_strlcpy(out->name, features->name, sizeof out->name); out->wildcards = mf_bitmap_to_of11(&wc); out->match = mf_bitmap_to_of11(&features->match); out->instructions = ovsinst_bitmap_to_openflow( features->nonmiss.instructions, OFP11_VERSION); out->write_actions = ofpact_bitmap_to_openflow( features->nonmiss.write.ofpacts, OFP11_VERSION); out->apply_actions = ofpact_bitmap_to_openflow( features->nonmiss.apply.ofpacts, OFP11_VERSION); out->config = htonl(features->miss_config); out->max_entries = htonl(features->max_entries); out->active_count = htonl(stats->active_count); out->lookup_count = htonll(stats->lookup_count); out->matched_count = htonll(stats->matched_count); } static void ofputil_put_ofp12_table_stats(const struct ofputil_table_stats *stats, const struct ofputil_table_features *features, struct ofpbuf *buf) { struct ofp12_table_stats *out; out = ofpbuf_put_zeros(buf, sizeof *out); out->table_id = features->table_id; ovs_strlcpy(out->name, features->name, sizeof out->name); out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION); out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard, OFP12_VERSION); out->write_actions = ofpact_bitmap_to_openflow( features->nonmiss.write.ofpacts, OFP12_VERSION); out->apply_actions = ofpact_bitmap_to_openflow( features->nonmiss.apply.ofpacts, OFP12_VERSION); out->write_setfields = oxm_bitmap_from_mf_bitmap( &features->nonmiss.write.set_fields, OFP12_VERSION); out->apply_setfields = oxm_bitmap_from_mf_bitmap( &features->nonmiss.apply.set_fields, OFP12_VERSION); out->metadata_match = features->metadata_match; out->metadata_write = features->metadata_write; out->instructions = ovsinst_bitmap_to_openflow( features->nonmiss.instructions, OFP12_VERSION); out->config = ofputil_encode_table_config(features->miss_config, OFPUTIL_TABLE_EVICTION_DEFAULT, OFPUTIL_TABLE_VACANCY_DEFAULT, OFP12_VERSION); out->max_entries = htonl(features->max_entries); out->active_count = htonl(stats->active_count); out->lookup_count = htonll(stats->lookup_count); out->matched_count = htonll(stats->matched_count); } static void ofputil_put_ofp13_table_stats(const struct ofputil_table_stats *stats, struct ofpbuf *buf) { struct ofp13_table_stats *out; out = ofpbuf_put_zeros(buf, sizeof *out); out->table_id = stats->table_id; out->active_count = htonl(stats->active_count); out->lookup_count = htonll(stats->lookup_count); out->matched_count = htonll(stats->matched_count); } struct ofpbuf * ofputil_encode_table_stats_reply(const struct ofp_header *request) { return ofpraw_alloc_stats_reply(request, 0); } void ofputil_append_table_stats_reply(struct ofpbuf *reply, const struct ofputil_table_stats *stats, const struct ofputil_table_features *features) { struct ofp_header *oh = reply->header; ovs_assert(stats->table_id == features->table_id); switch ((enum ofp_version) oh->version) { case OFP10_VERSION: ofputil_put_ofp10_table_stats(stats, features, reply); break; case OFP11_VERSION: ofputil_put_ofp11_table_stats(stats, features, reply); break; case OFP12_VERSION: ofputil_put_ofp12_table_stats(stats, features, reply); break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: ofputil_put_ofp13_table_stats(stats, reply); break; default: OVS_NOT_REACHED(); } } static int ofputil_decode_ofp10_table_stats(struct ofpbuf *msg, struct ofputil_table_stats *stats, struct ofputil_table_features *features) { struct ofp10_table_stats *ots; ots = ofpbuf_try_pull(msg, sizeof *ots); if (!ots) { return OFPERR_OFPBRC_BAD_LEN; } features->table_id = ots->table_id; ovs_strlcpy(features->name, ots->name, sizeof features->name); features->max_entries = ntohl(ots->max_entries); features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards); stats->table_id = ots->table_id; stats->active_count = ntohl(ots->active_count); stats->lookup_count = ntohll(get_32aligned_be64(&ots->lookup_count)); stats->matched_count = ntohll(get_32aligned_be64(&ots->matched_count)); return 0; } static int ofputil_decode_ofp11_table_stats(struct ofpbuf *msg, struct ofputil_table_stats *stats, struct ofputil_table_features *features) { struct ofp11_table_stats *ots; ots = ofpbuf_try_pull(msg, sizeof *ots); if (!ots) { return OFPERR_OFPBRC_BAD_LEN; } features->table_id = ots->table_id; ovs_strlcpy(features->name, ots->name, sizeof features->name); features->max_entries = ntohl(ots->max_entries); features->nonmiss.instructions = ovsinst_bitmap_from_openflow( ots->instructions, OFP11_VERSION); features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow( ots->write_actions, OFP11_VERSION); features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow( ots->write_actions, OFP11_VERSION); features->miss = features->nonmiss; features->miss_config = ofputil_decode_table_miss(ots->config, OFP11_VERSION); features->match = mf_bitmap_from_of11(ots->match); features->wildcard = mf_bitmap_from_of11(ots->wildcards); bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS); stats->table_id = ots->table_id; stats->active_count = ntohl(ots->active_count); stats->lookup_count = ntohll(ots->lookup_count); stats->matched_count = ntohll(ots->matched_count); return 0; } static int ofputil_decode_ofp12_table_stats(struct ofpbuf *msg, struct ofputil_table_stats *stats, struct ofputil_table_features *features) { struct ofp12_table_stats *ots; ots = ofpbuf_try_pull(msg, sizeof *ots); if (!ots) { return OFPERR_OFPBRC_BAD_LEN; } features->table_id = ots->table_id; ovs_strlcpy(features->name, ots->name, sizeof features->name); features->metadata_match = ots->metadata_match; features->metadata_write = ots->metadata_write; features->miss_config = ofputil_decode_table_miss(ots->config, OFP12_VERSION); features->max_entries = ntohl(ots->max_entries); features->nonmiss.instructions = ovsinst_bitmap_from_openflow( ots->instructions, OFP12_VERSION); features->nonmiss.write.ofpacts = ofpact_bitmap_from_openflow( ots->write_actions, OFP12_VERSION); features->nonmiss.apply.ofpacts = ofpact_bitmap_from_openflow( ots->apply_actions, OFP12_VERSION); features->nonmiss.write.set_fields = oxm_bitmap_to_mf_bitmap( ots->write_setfields, OFP12_VERSION); features->nonmiss.apply.set_fields = oxm_bitmap_to_mf_bitmap( ots->apply_setfields, OFP12_VERSION); features->miss = features->nonmiss; features->match = oxm_bitmap_to_mf_bitmap(ots->match, OFP12_VERSION); features->wildcard = oxm_bitmap_to_mf_bitmap(ots->wildcards, OFP12_VERSION); bitmap_or(features->match.bm, features->wildcard.bm, MFF_N_IDS); stats->table_id = ots->table_id; stats->active_count = ntohl(ots->active_count); stats->lookup_count = ntohll(ots->lookup_count); stats->matched_count = ntohll(ots->matched_count); return 0; } static int ofputil_decode_ofp13_table_stats(struct ofpbuf *msg, struct ofputil_table_stats *stats, struct ofputil_table_features *features) { struct ofp13_table_stats *ots; ots = ofpbuf_try_pull(msg, sizeof *ots); if (!ots) { return OFPERR_OFPBRC_BAD_LEN; } features->table_id = ots->table_id; stats->table_id = ots->table_id; stats->active_count = ntohl(ots->active_count); stats->lookup_count = ntohll(ots->lookup_count); stats->matched_count = ntohll(ots->matched_count); return 0; } int ofputil_decode_table_stats_reply(struct ofpbuf *msg, struct ofputil_table_stats *stats, struct ofputil_table_features *features) { const struct ofp_header *oh; if (!msg->header) { ofpraw_pull_assert(msg); } oh = msg->header; if (!msg->size) { return EOF; } memset(stats, 0, sizeof *stats); memset(features, 0, sizeof *features); features->supports_eviction = -1; features->supports_vacancy_events = -1; switch ((enum ofp_version) oh->version) { case OFP10_VERSION: return ofputil_decode_ofp10_table_stats(msg, stats, features); case OFP11_VERSION: return ofputil_decode_ofp11_table_stats(msg, stats, features); case OFP12_VERSION: return ofputil_decode_ofp12_table_stats(msg, stats, features); case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: return ofputil_decode_ofp13_table_stats(msg, stats, features); default: OVS_NOT_REACHED(); } } /* ofputil_flow_monitor_request */ /* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract * ofputil_flow_monitor_request in 'rq'. * * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the requests. The caller must initially leave 'msg''s layer * pointers null and not modify them between calls. * * Returns 0 if successful, EOF if no requests were left in this 'msg', * otherwise an OFPERR_* value. */ int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg) { struct nx_flow_monitor_request *nfmr; uint16_t flags; if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } nfmr = ofpbuf_try_pull(msg, sizeof *nfmr); if (!nfmr) { VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %"PRIu32" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } flags = ntohs(nfmr->flags); if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY)) || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) { VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16, flags); return OFPERR_OFPMOFC_BAD_FLAGS; } if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) { return OFPERR_NXBRC_MUST_BE_ZERO; } rq->id = ntohl(nfmr->id); rq->flags = flags; rq->out_port = u16_to_ofp(ntohs(nfmr->out_port)); rq->table_id = nfmr->table_id; return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL); } void ofputil_append_flow_monitor_request( const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg) { struct nx_flow_monitor_request *nfmr; size_t start_ofs; int match_len; if (!msg->size) { ofpraw_put(OFPRAW_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, msg); } start_ofs = msg->size; ofpbuf_put_zeros(msg, sizeof *nfmr); match_len = nx_put_match(msg, &rq->match, htonll(0), htonll(0)); nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr); nfmr->id = htonl(rq->id); nfmr->flags = htons(rq->flags); nfmr->out_port = htons(ofp_to_u16(rq->out_port)); nfmr->match_len = htons(match_len); nfmr->table_id = rq->table_id; } /* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg' * into an abstract ofputil_flow_update in 'update'. The caller must have * initialized update->match to point to space allocated for a match. * * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's * actions (except for NXFME_ABBREV, which never includes actions). The caller * must initialize 'ofpacts' and retains ownership of it. 'update->ofpacts' * will point into the 'ofpacts' buffer. * * Multiple flow updates can be packed into a single OpenFlow message. Calling * this function multiple times for a single 'msg' iterates through the * updates. The caller must initially leave 'msg''s layer pointers null and * not modify them between calls. * * Returns 0 if successful, EOF if no updates were left in this 'msg', * otherwise an OFPERR_* value. */ int ofputil_decode_flow_update(struct ofputil_flow_update *update, struct ofpbuf *msg, struct ofpbuf *ofpacts) { struct nx_flow_update_header *nfuh; unsigned int length; struct ofp_header *oh; if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } if (msg->size < sizeof(struct nx_flow_update_header)) { goto bad_len; } oh = msg->header; nfuh = msg->data; update->event = ntohs(nfuh->event); length = ntohs(nfuh->length); if (length > msg->size || length % 8) { goto bad_len; } if (update->event == NXFME_ABBREV) { struct nx_flow_update_abbrev *nfua; if (length != sizeof *nfua) { goto bad_len; } nfua = ofpbuf_pull(msg, sizeof *nfua); update->xid = nfua->xid; return 0; } else if (update->event == NXFME_ADDED || update->event == NXFME_DELETED || update->event == NXFME_MODIFIED) { struct nx_flow_update_full *nfuf; unsigned int actions_len; unsigned int match_len; enum ofperr error; if (length < sizeof *nfuf) { goto bad_len; } nfuf = ofpbuf_pull(msg, sizeof *nfuf); match_len = ntohs(nfuf->match_len); if (sizeof *nfuf + match_len > length) { goto bad_len; } update->reason = ntohs(nfuf->reason); update->idle_timeout = ntohs(nfuf->idle_timeout); update->hard_timeout = ntohs(nfuf->hard_timeout); update->table_id = nfuf->table_id; update->cookie = nfuf->cookie; update->priority = ntohs(nfuf->priority); error = nx_pull_match(msg, match_len, update->match, NULL, NULL); if (error) { return error; } actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8); error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version, ofpacts); if (error) { return error; } update->ofpacts = ofpacts->data; update->ofpacts_len = ofpacts->size; return 0; } else { VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16, ntohs(nfuh->event)); return OFPERR_NXBRC_FM_BAD_EVENT; } bad_len: VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %"PRIu32" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh) { const struct nx_flow_monitor_cancel *cancel = ofpmsg_body(oh); return ntohl(cancel->id); } struct ofpbuf * ofputil_encode_flow_monitor_cancel(uint32_t id) { struct nx_flow_monitor_cancel *nfmc; struct ofpbuf *msg; msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, 0); nfmc = ofpbuf_put_uninit(msg, sizeof *nfmc); nfmc->id = htonl(id); return msg; } void ofputil_start_flow_update(struct ovs_list *replies) { struct ofpbuf *msg; msg = ofpraw_alloc_xid(OFPRAW_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION, htonl(0), 1024); list_init(replies); list_push_back(replies, &msg->list_node); } void ofputil_append_flow_update(const struct ofputil_flow_update *update, struct ovs_list *replies) { enum ofp_version version = ofpmp_version(replies); struct nx_flow_update_header *nfuh; struct ofpbuf *msg; size_t start_ofs; msg = ofpbuf_from_list(list_back(replies)); start_ofs = msg->size; if (update->event == NXFME_ABBREV) { struct nx_flow_update_abbrev *nfua; nfua = ofpbuf_put_zeros(msg, sizeof *nfua); nfua->xid = update->xid; } else { struct nx_flow_update_full *nfuf; int match_len; ofpbuf_put_zeros(msg, sizeof *nfuf); match_len = nx_put_match(msg, update->match, htonll(0), htonll(0)); ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg, version); nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf); nfuf->reason = htons(update->reason); nfuf->priority = htons(update->priority); nfuf->idle_timeout = htons(update->idle_timeout); nfuf->hard_timeout = htons(update->hard_timeout); nfuf->match_len = htons(match_len); nfuf->table_id = update->table_id; nfuf->cookie = update->cookie; } nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh); nfuh->length = htons(msg->size - start_ofs); nfuh->event = htons(update->event); ofpmp_postappend(replies, start_ofs); } struct ofpbuf * ofputil_encode_packet_out(const struct ofputil_packet_out *po, enum ofputil_protocol protocol) { enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); struct ofpbuf *msg; size_t size; size = po->ofpacts_len; if (po->buffer_id == UINT32_MAX) { size += po->packet_len; } switch (ofp_version) { case OFP10_VERSION: { struct ofp10_packet_out *opo; size_t actions_ofs; msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size); ofpbuf_put_zeros(msg, sizeof *opo); actions_ofs = msg->size; ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, ofp_version); opo = msg->msg; opo->buffer_id = htonl(po->buffer_id); opo->in_port = htons(ofp_to_u16(po->in_port)); opo->actions_len = htons(msg->size - actions_ofs); break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: { struct ofp11_packet_out *opo; size_t len; msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size); ofpbuf_put_zeros(msg, sizeof *opo); len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, ofp_version); opo = msg->msg; opo->buffer_id = htonl(po->buffer_id); opo->in_port = ofputil_port_to_ofp11(po->in_port); opo->actions_len = htons(len); break; } default: OVS_NOT_REACHED(); } if (po->buffer_id == UINT32_MAX) { ofpbuf_put(msg, po->packet, po->packet_len); } ofpmsg_update_length(msg); return msg; } /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */ struct ofpbuf * make_echo_request(enum ofp_version ofp_version) { return ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, ofp_version, htonl(0), 0); } /* Creates and returns an OFPT_ECHO_REPLY message matching the * OFPT_ECHO_REQUEST message in 'rq'. */ struct ofpbuf * make_echo_reply(const struct ofp_header *rq) { struct ofpbuf rq_buf; struct ofpbuf *reply; ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length)); ofpraw_pull_assert(&rq_buf); reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size); ofpbuf_put(reply, rq_buf.data, rq_buf.size); return reply; } struct ofpbuf * ofputil_encode_barrier_request(enum ofp_version ofp_version) { enum ofpraw type; switch (ofp_version) { case OFP15_VERSION: case OFP14_VERSION: case OFP13_VERSION: case OFP12_VERSION: case OFP11_VERSION: type = OFPRAW_OFPT11_BARRIER_REQUEST; break; case OFP10_VERSION: type = OFPRAW_OFPT10_BARRIER_REQUEST; break; default: OVS_NOT_REACHED(); } return ofpraw_alloc(type, ofp_version, 0); } const char * ofputil_frag_handling_to_string(enum ofp_config_flags flags) { switch (flags & OFPC_FRAG_MASK) { case OFPC_FRAG_NORMAL: return "normal"; case OFPC_FRAG_DROP: return "drop"; case OFPC_FRAG_REASM: return "reassemble"; case OFPC_FRAG_NX_MATCH: return "nx-match"; } OVS_NOT_REACHED(); } bool ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags) { if (!strcasecmp(s, "normal")) { *flags = OFPC_FRAG_NORMAL; } else if (!strcasecmp(s, "drop")) { *flags = OFPC_FRAG_DROP; } else if (!strcasecmp(s, "reassemble")) { *flags = OFPC_FRAG_REASM; } else if (!strcasecmp(s, "nx-match")) { *flags = OFPC_FRAG_NX_MATCH; } else { return false; } return true; } /* Converts the OpenFlow 1.1+ port number 'ofp11_port' into an OpenFlow 1.0 * port number and stores the latter in '*ofp10_port', for the purpose of * decoding OpenFlow 1.1+ protocol messages. Returns 0 if successful, * otherwise an OFPERR_* number. On error, stores OFPP_NONE in '*ofp10_port'. * * See the definition of OFP11_MAX for an explanation of the mapping. */ enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port, ofp_port_t *ofp10_port) { uint32_t ofp11_port_h = ntohl(ofp11_port); if (ofp11_port_h < ofp_to_u16(OFPP_MAX)) { *ofp10_port = u16_to_ofp(ofp11_port_h); return 0; } else if (ofp11_port_h >= ofp11_to_u32(OFPP11_MAX)) { *ofp10_port = u16_to_ofp(ofp11_port_h - OFPP11_OFFSET); return 0; } else { *ofp10_port = OFPP_NONE; VLOG_WARN_RL(&bad_ofmsg_rl, "port %"PRIu32" is outside the supported " "range 0 through %d or 0x%"PRIx32" through 0x%"PRIx32, ofp11_port_h, ofp_to_u16(OFPP_MAX) - 1, ofp11_to_u32(OFPP11_MAX), UINT32_MAX); return OFPERR_OFPBAC_BAD_OUT_PORT; } } /* Returns the OpenFlow 1.1+ port number equivalent to the OpenFlow 1.0 port * number 'ofp10_port', for encoding OpenFlow 1.1+ protocol messages. * * See the definition of OFP11_MAX for an explanation of the mapping. */ ovs_be32 ofputil_port_to_ofp11(ofp_port_t ofp10_port) { return htonl(ofp_to_u16(ofp10_port) < ofp_to_u16(OFPP_MAX) ? ofp_to_u16(ofp10_port) : ofp_to_u16(ofp10_port) + OFPP11_OFFSET); } #define OFPUTIL_NAMED_PORTS \ OFPUTIL_NAMED_PORT(IN_PORT) \ OFPUTIL_NAMED_PORT(TABLE) \ OFPUTIL_NAMED_PORT(NORMAL) \ OFPUTIL_NAMED_PORT(FLOOD) \ OFPUTIL_NAMED_PORT(ALL) \ OFPUTIL_NAMED_PORT(CONTROLLER) \ OFPUTIL_NAMED_PORT(LOCAL) \ OFPUTIL_NAMED_PORT(ANY) \ OFPUTIL_NAMED_PORT(UNSET) /* For backwards compatibility, so that "none" is recognized as OFPP_ANY */ #define OFPUTIL_NAMED_PORTS_WITH_NONE \ OFPUTIL_NAMED_PORTS \ OFPUTIL_NAMED_PORT(NONE) /* Stores the port number represented by 's' into '*portp'. 's' may be an * integer or, for reserved ports, the standard OpenFlow name for the port * (e.g. "LOCAL"). * * Returns true if successful, false if 's' is not a valid OpenFlow port number * or name. The caller should issue an error message in this case, because * this function usually does not. (This gives the caller an opportunity to * look up the port name another way, e.g. by contacting the switch and listing * the names of all its ports). * * This function accepts OpenFlow 1.0 port numbers. It also accepts a subset * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit * range as described in include/openflow/openflow-1.1.h. */ bool ofputil_port_from_string(const char *s, ofp_port_t *portp) { unsigned int port32; /* int is at least 32 bits wide. */ if (*s == '-') { VLOG_WARN("Negative value %s is not a valid port number.", s); return false; } *portp = 0; if (str_to_uint(s, 10, &port32)) { if (port32 < ofp_to_u16(OFPP_MAX)) { /* Pass. */ } else if (port32 < ofp_to_u16(OFPP_FIRST_RESV)) { VLOG_WARN("port %u is a reserved OF1.0 port number that will " "be translated to %u when talking to an OF1.1 or " "later controller", port32, port32 + OFPP11_OFFSET); } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) { char name[OFP_MAX_PORT_NAME_LEN]; ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name); VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated " "for compatibility with OpenFlow 1.1 and later", name, port32); } else if (port32 < ofp11_to_u32(OFPP11_MAX)) { VLOG_WARN("port %u is outside the supported range 0 through " "%"PRIx16" or 0x%x through 0x%"PRIx32, port32, UINT16_MAX, ofp11_to_u32(OFPP11_MAX), UINT32_MAX); return false; } else { port32 -= OFPP11_OFFSET; } *portp = u16_to_ofp(port32); return true; } else { struct pair { const char *name; ofp_port_t value; }; static const struct pair pairs[] = { #define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME}, OFPUTIL_NAMED_PORTS_WITH_NONE #undef OFPUTIL_NAMED_PORT }; const struct pair *p; for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) { if (!strcasecmp(s, p->name)) { *portp = p->value; return true; } } return false; } } /* Appends to 's' a string representation of the OpenFlow port number 'port'. * Most ports' string representation is just the port number, but for special * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */ void ofputil_format_port(ofp_port_t port, struct ds *s) { char name[OFP_MAX_PORT_NAME_LEN]; ofputil_port_to_string(port, name, sizeof name); ds_put_cstr(s, name); } /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string * representation of OpenFlow port number 'port'. Most ports are represented * as just the port number, but special ports, e.g. OFPP_LOCAL, are represented * by name, e.g. "LOCAL". */ void ofputil_port_to_string(ofp_port_t port, char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize) { switch (port) { #define OFPUTIL_NAMED_PORT(NAME) \ case OFPP_##NAME: \ ovs_strlcpy(namebuf, #NAME, bufsize); \ break; OFPUTIL_NAMED_PORTS #undef OFPUTIL_NAMED_PORT default: snprintf(namebuf, bufsize, "%"PRIu16, port); break; } } /* Stores the group id represented by 's' into '*group_idp'. 's' may be an * integer or, for reserved group IDs, the standard OpenFlow name for the group * (either "ANY" or "ALL"). * * Returns true if successful, false if 's' is not a valid OpenFlow group ID or * name. */ bool ofputil_group_from_string(const char *s, uint32_t *group_idp) { if (!strcasecmp(s, "any")) { *group_idp = OFPG_ANY; } else if (!strcasecmp(s, "all")) { *group_idp = OFPG_ALL; } else if (!str_to_uint(s, 10, group_idp)) { VLOG_WARN("%s is not a valid group ID. (Valid group IDs are " "32-bit nonnegative integers or the keywords ANY or " "ALL.)", s); return false; } return true; } /* Appends to 's' a string representation of the OpenFlow group ID 'group_id'. * Most groups' string representation is just the number, but for special * groups, e.g. OFPG_ALL, it is the name, e.g. "ALL". */ void ofputil_format_group(uint32_t group_id, struct ds *s) { char name[MAX_GROUP_NAME_LEN]; ofputil_group_to_string(group_id, name, sizeof name); ds_put_cstr(s, name); } /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string * representation of OpenFlow group ID 'group_id'. Most group are represented * as just their number, but special groups, e.g. OFPG_ALL, are represented * by name, e.g. "ALL". */ void ofputil_group_to_string(uint32_t group_id, char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize) { switch (group_id) { case OFPG_ALL: ovs_strlcpy(namebuf, "ALL", bufsize); break; case OFPG_ANY: ovs_strlcpy(namebuf, "ANY", bufsize); break; default: snprintf(namebuf, bufsize, "%"PRIu32, group_id); break; } } /* Given a buffer 'b' that contains an array of OpenFlow ports of type * 'ofp_version', tries to pull the first element from the array. If * successful, initializes '*pp' with an abstract representation of the * port and returns 0. If no ports remain to be decoded, returns EOF. * On an error, returns a positive OFPERR_* value. */ int ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b, struct ofputil_phy_port *pp) { memset(pp, 0, sizeof *pp); switch (ofp_version) { case OFP10_VERSION: { const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp); return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: { const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op); return op ? ofputil_decode_ofp11_port(pp, op) : EOF; } case OFP14_VERSION: case OFP15_VERSION: return b->size ? ofputil_pull_ofp14_port(pp, b) : EOF; default: OVS_NOT_REACHED(); } } static void ofputil_normalize_match__(struct match *match, bool may_log) { enum { MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */ MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */ MAY_NW_PROTO = 1 << 2, /* nw_proto */ MAY_IPVx = 1 << 3, /* tos, frag, ttl */ MAY_ARP_SHA = 1 << 4, /* arp_sha */ MAY_ARP_THA = 1 << 5, /* arp_tha */ MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */ MAY_ND_TARGET = 1 << 7, /* nd_target */ MAY_MPLS = 1 << 8, /* mpls label and tc */ } may_match; struct flow_wildcards wc; /* Figure out what fields may be matched. */ if (match->flow.dl_type == htons(ETH_TYPE_IP)) { may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR; if (match->flow.nw_proto == IPPROTO_TCP || match->flow.nw_proto == IPPROTO_UDP || match->flow.nw_proto == IPPROTO_SCTP || match->flow.nw_proto == IPPROTO_ICMP) { may_match |= MAY_TP_ADDR; } } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) { may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6; if (match->flow.nw_proto == IPPROTO_TCP || match->flow.nw_proto == IPPROTO_UDP || match->flow.nw_proto == IPPROTO_SCTP) { may_match |= MAY_TP_ADDR; } else if (match->flow.nw_proto == IPPROTO_ICMPV6) { may_match |= MAY_TP_ADDR; if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) { may_match |= MAY_ND_TARGET | MAY_ARP_SHA; } else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) { may_match |= MAY_ND_TARGET | MAY_ARP_THA; } } } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) || match->flow.dl_type == htons(ETH_TYPE_RARP)) { may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA; } else if (eth_type_mpls(match->flow.dl_type)) { may_match = MAY_MPLS; } else { may_match = 0; } /* Clear the fields that may not be matched. */ wc = match->wc; if (!(may_match & MAY_NW_ADDR)) { wc.masks.nw_src = wc.masks.nw_dst = htonl(0); } if (!(may_match & MAY_TP_ADDR)) { wc.masks.tp_src = wc.masks.tp_dst = htons(0); } if (!(may_match & MAY_NW_PROTO)) { wc.masks.nw_proto = 0; } if (!(may_match & MAY_IPVx)) { wc.masks.nw_tos = 0; wc.masks.nw_ttl = 0; } if (!(may_match & MAY_ARP_SHA)) { WC_UNMASK_FIELD(&wc, arp_sha); } if (!(may_match & MAY_ARP_THA)) { WC_UNMASK_FIELD(&wc, arp_tha); } if (!(may_match & MAY_IPV6)) { wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any; wc.masks.ipv6_label = htonl(0); } if (!(may_match & MAY_ND_TARGET)) { wc.masks.nd_target = in6addr_any; } if (!(may_match & MAY_MPLS)) { memset(wc.masks.mpls_lse, 0, sizeof wc.masks.mpls_lse); } /* Log any changes. */ if (!flow_wildcards_equal(&wc, &match->wc)) { bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl); char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL; match->wc = wc; match_zero_wildcarded_fields(match); if (log) { char *post = match_to_string(match, OFP_DEFAULT_PRIORITY); VLOG_INFO("normalization changed ofp_match, details:"); VLOG_INFO(" pre: %s", pre); VLOG_INFO("post: %s", post); free(pre); free(post); } } } /* "Normalizes" the wildcards in 'match'. That means: * * 1. If the type of level N is known, then only the valid fields for that * level may be specified. For example, ARP does not have a TOS field, * so nw_tos must be wildcarded if 'match' specifies an ARP flow. * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and * ipv6_dst (and other fields) must be wildcarded if 'match' specifies an * IPv4 flow. * * 2. If the type of level N is not known (or not understood by Open * vSwitch), then no fields at all for that level may be specified. For * example, Open vSwitch does not understand SCTP, an L4 protocol, so the * L4 fields tp_src and tp_dst must be wildcarded if 'match' specifies an * SCTP flow. * * If this function changes 'match', it logs a rate-limited informational * message. */ void ofputil_normalize_match(struct match *match) { ofputil_normalize_match__(match, true); } /* Same as ofputil_normalize_match() without the logging. Thus, this function * is suitable for a program's internal use, whereas ofputil_normalize_match() * sense for use on flows received from elsewhere (so that a bug in the program * that sent them can be reported and corrected). */ void ofputil_normalize_match_quiet(struct match *match) { ofputil_normalize_match__(match, false); } static size_t parse_value(const char *s, const char *delimiters) { size_t n = 0; /* Iterate until we reach a delimiter. * * strchr(s, '\0') returns s+strlen(s), so this test handles the null * terminator at the end of 's'. */ while (!strchr(delimiters, s[n])) { if (s[n] == '(') { int level = 0; do { switch (s[n]) { case '\0': return n; case '(': level++; break; case ')': level--; break; } n++; } while (level > 0); } else { n++; } } return n; } /* Parses a key or a key-value pair from '*stringp'. * * On success: Stores the key into '*keyp'. Stores the value, if present, into * '*valuep', otherwise an empty string. Advances '*stringp' past the end of * the key-value pair, preparing it for another call. '*keyp' and '*valuep' * are substrings of '*stringp' created by replacing some of its bytes by null * terminators. Returns true. * * If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to * NULL and returns false. */ bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep) { /* Skip white space and delimiters. If that brings us to the end of the * input string, we are done and there are no more key-value pairs. */ *stringp += strspn(*stringp, ", \t\r\n"); if (**stringp == '\0') { *keyp = *valuep = NULL; return false; } /* Extract the key and the delimiter that ends the key-value pair or begins * the value. Advance the input position past the key and delimiter. */ char *key = *stringp; size_t key_len = strcspn(key, ":=(, \t\r\n"); char key_delim = key[key_len]; key[key_len] = '\0'; *stringp += key_len + (key_delim != '\0'); /* Figure out what delimiter ends the value: * * - If key_delim is ":" or "=", the value extends until white space * or a comma. * * - If key_delim is "(", the value extends until ")". * * If there is no value, we are done. */ const char *value_delims; if (key_delim == ':' || key_delim == '=') { value_delims = ", \t\r\n"; } else if (key_delim == '(') { value_delims = ")"; } else { *keyp = key; *valuep = key + key_len; /* Empty string. */ return true; } /* Extract the value. Advance the input position past the value and * delimiter. */ char *value = *stringp; size_t value_len = parse_value(value, value_delims); char value_delim = value[value_len]; value[value_len] = '\0'; *stringp += value_len + (value_delim != '\0'); *keyp = key; *valuep = value; return true; } /* Encode a dump ports request for 'port', the encoded message * will be for OpenFlow version 'ofp_version'. Returns message * as a struct ofpbuf. Returns encoded message on success, NULL on error */ struct ofpbuf * ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port) { struct ofpbuf *request; switch (ofp_version) { case OFP10_VERSION: { struct ofp10_port_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->port_no = htons(ofp_to_u16(port)); break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: { struct ofp11_port_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->port_no = ofputil_port_to_ofp11(port); break; } default: OVS_NOT_REACHED(); } return request; } static void ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops, struct ofp10_port_stats *ps10) { ps10->port_no = htons(ofp_to_u16(ops->port_no)); memset(ps10->pad, 0, sizeof ps10->pad); put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets)); put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets)); put_32aligned_be64(&ps10->rx_bytes, htonll(ops->stats.rx_bytes)); put_32aligned_be64(&ps10->tx_bytes, htonll(ops->stats.tx_bytes)); put_32aligned_be64(&ps10->rx_dropped, htonll(ops->stats.rx_dropped)); put_32aligned_be64(&ps10->tx_dropped, htonll(ops->stats.tx_dropped)); put_32aligned_be64(&ps10->rx_errors, htonll(ops->stats.rx_errors)); put_32aligned_be64(&ps10->tx_errors, htonll(ops->stats.tx_errors)); put_32aligned_be64(&ps10->rx_frame_err, htonll(ops->stats.rx_frame_errors)); put_32aligned_be64(&ps10->rx_over_err, htonll(ops->stats.rx_over_errors)); put_32aligned_be64(&ps10->rx_crc_err, htonll(ops->stats.rx_crc_errors)); put_32aligned_be64(&ps10->collisions, htonll(ops->stats.collisions)); } static void ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops, struct ofp11_port_stats *ps11) { ps11->port_no = ofputil_port_to_ofp11(ops->port_no); memset(ps11->pad, 0, sizeof ps11->pad); ps11->rx_packets = htonll(ops->stats.rx_packets); ps11->tx_packets = htonll(ops->stats.tx_packets); ps11->rx_bytes = htonll(ops->stats.rx_bytes); ps11->tx_bytes = htonll(ops->stats.tx_bytes); ps11->rx_dropped = htonll(ops->stats.rx_dropped); ps11->tx_dropped = htonll(ops->stats.tx_dropped); ps11->rx_errors = htonll(ops->stats.rx_errors); ps11->tx_errors = htonll(ops->stats.tx_errors); ps11->rx_frame_err = htonll(ops->stats.rx_frame_errors); ps11->rx_over_err = htonll(ops->stats.rx_over_errors); ps11->rx_crc_err = htonll(ops->stats.rx_crc_errors); ps11->collisions = htonll(ops->stats.collisions); } static void ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops, struct ofp13_port_stats *ps13) { ofputil_port_stats_to_ofp11(ops, &ps13->ps); ps13->duration_sec = htonl(ops->duration_sec); ps13->duration_nsec = htonl(ops->duration_nsec); } static void ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops, struct ovs_list *replies) { struct ofp14_port_stats_prop_ethernet *eth; struct ofp14_port_stats *ps14; struct ofpbuf *reply; reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth); ps14 = ofpbuf_put_uninit(reply, sizeof *ps14); ps14->length = htons(sizeof *ps14 + sizeof *eth); memset(ps14->pad, 0, sizeof ps14->pad); ps14->port_no = ofputil_port_to_ofp11(ops->port_no); ps14->duration_sec = htonl(ops->duration_sec); ps14->duration_nsec = htonl(ops->duration_nsec); ps14->rx_packets = htonll(ops->stats.rx_packets); ps14->tx_packets = htonll(ops->stats.tx_packets); ps14->rx_bytes = htonll(ops->stats.rx_bytes); ps14->tx_bytes = htonll(ops->stats.tx_bytes); ps14->rx_dropped = htonll(ops->stats.rx_dropped); ps14->tx_dropped = htonll(ops->stats.tx_dropped); ps14->rx_errors = htonll(ops->stats.rx_errors); ps14->tx_errors = htonll(ops->stats.tx_errors); eth = ofpbuf_put_uninit(reply, sizeof *eth); eth->type = htons(OFPPSPT14_ETHERNET); eth->length = htons(sizeof *eth); memset(eth->pad, 0, sizeof eth->pad); eth->rx_frame_err = htonll(ops->stats.rx_frame_errors); eth->rx_over_err = htonll(ops->stats.rx_over_errors); eth->rx_crc_err = htonll(ops->stats.rx_crc_errors); eth->collisions = htonll(ops->stats.collisions); } /* Encode a ports stat for 'ops' and append it to 'replies'. */ void ofputil_append_port_stat(struct ovs_list *replies, const struct ofputil_port_stats *ops) { switch (ofpmp_version(replies)) { case OFP13_VERSION: { struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_port_stats_to_ofp13(ops, reply); break; } case OFP12_VERSION: case OFP11_VERSION: { struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_port_stats_to_ofp11(ops, reply); break; } case OFP10_VERSION: { struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_port_stats_to_ofp10(ops, reply); break; } case OFP14_VERSION: case OFP15_VERSION: ofputil_append_ofp14_port_stats(ops, replies); break; default: OVS_NOT_REACHED(); } } static enum ofperr ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops, const struct ofp10_port_stats *ps10) { memset(ops, 0, sizeof *ops); ops->port_no = u16_to_ofp(ntohs(ps10->port_no)); ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets)); ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets)); ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes)); ops->stats.tx_bytes = ntohll(get_32aligned_be64(&ps10->tx_bytes)); ops->stats.rx_dropped = ntohll(get_32aligned_be64(&ps10->rx_dropped)); ops->stats.tx_dropped = ntohll(get_32aligned_be64(&ps10->tx_dropped)); ops->stats.rx_errors = ntohll(get_32aligned_be64(&ps10->rx_errors)); ops->stats.tx_errors = ntohll(get_32aligned_be64(&ps10->tx_errors)); ops->stats.rx_frame_errors = ntohll(get_32aligned_be64(&ps10->rx_frame_err)); ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err)); ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err)); ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions)); ops->duration_sec = ops->duration_nsec = UINT32_MAX; return 0; } static enum ofperr ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops, const struct ofp11_port_stats *ps11) { enum ofperr error; memset(ops, 0, sizeof *ops); error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no); if (error) { return error; } ops->stats.rx_packets = ntohll(ps11->rx_packets); ops->stats.tx_packets = ntohll(ps11->tx_packets); ops->stats.rx_bytes = ntohll(ps11->rx_bytes); ops->stats.tx_bytes = ntohll(ps11->tx_bytes); ops->stats.rx_dropped = ntohll(ps11->rx_dropped); ops->stats.tx_dropped = ntohll(ps11->tx_dropped); ops->stats.rx_errors = ntohll(ps11->rx_errors); ops->stats.tx_errors = ntohll(ps11->tx_errors); ops->stats.rx_frame_errors = ntohll(ps11->rx_frame_err); ops->stats.rx_over_errors = ntohll(ps11->rx_over_err); ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err); ops->stats.collisions = ntohll(ps11->collisions); ops->duration_sec = ops->duration_nsec = UINT32_MAX; return 0; } static enum ofperr ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops, const struct ofp13_port_stats *ps13) { enum ofperr error = ofputil_port_stats_from_ofp11(ops, &ps13->ps); if (!error) { ops->duration_sec = ntohl(ps13->duration_sec); ops->duration_nsec = ntohl(ps13->duration_nsec); } return error; } static enum ofperr parse_ofp14_port_stats_ethernet_property(const struct ofpbuf *payload, struct ofputil_port_stats *ops) { const struct ofp14_port_stats_prop_ethernet *eth = payload->data; if (payload->size != sizeof *eth) { return OFPERR_OFPBPC_BAD_LEN; } ops->stats.rx_frame_errors = ntohll(eth->rx_frame_err); ops->stats.rx_over_errors = ntohll(eth->rx_over_err); ops->stats.rx_crc_errors = ntohll(eth->rx_crc_err); ops->stats.collisions = ntohll(eth->collisions); return 0; } static enum ofperr ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops, struct ofpbuf *msg) { const struct ofp14_port_stats *ps14; struct ofpbuf properties; enum ofperr error; size_t len; ps14 = ofpbuf_try_pull(msg, sizeof *ps14); if (!ps14) { return OFPERR_OFPBRC_BAD_LEN; } len = ntohs(ps14->length); if (len < sizeof *ps14 || len - sizeof *ps14 > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } len -= sizeof *ps14; ofpbuf_use_const(&properties, ofpbuf_pull(msg, len), len); error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no); if (error) { return error; } ops->duration_sec = ntohl(ps14->duration_sec); ops->duration_nsec = ntohl(ps14->duration_nsec); ops->stats.rx_packets = ntohll(ps14->rx_packets); ops->stats.tx_packets = ntohll(ps14->tx_packets); ops->stats.rx_bytes = ntohll(ps14->rx_bytes); ops->stats.tx_bytes = ntohll(ps14->tx_bytes); ops->stats.rx_dropped = ntohll(ps14->rx_dropped); ops->stats.tx_dropped = ntohll(ps14->tx_dropped); ops->stats.rx_errors = ntohll(ps14->rx_errors); ops->stats.tx_errors = ntohll(ps14->tx_errors); ops->stats.rx_frame_errors = UINT64_MAX; ops->stats.rx_over_errors = UINT64_MAX; ops->stats.rx_crc_errors = UINT64_MAX; ops->stats.collisions = UINT64_MAX; while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; uint16_t type; error = ofputil_pull_property(&properties, &payload, &type); if (error) { return error; } switch (type) { case OFPPSPT14_ETHERNET: error = parse_ofp14_port_stats_ethernet_property(&payload, ops); break; default: log_property(true, "unknown port stats property %"PRIu16, type); error = 0; break; } if (error) { return error; } } return 0; } /* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY * message 'oh'. */ size_t ofputil_count_port_stats(const struct ofp_header *oh) { struct ofputil_port_stats ps; struct ofpbuf b; size_t n = 0; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); while (!ofputil_decode_port_stats(&ps, &b)) { n++; } return n; } /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract * ofputil_port_stats in 'ps'. * * Multiple OFPST_PORT_STATS replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. The caller must initially leave 'msg''s layer pointers * null and not modify them between calls. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg) { enum ofperr error; enum ofpraw raw; error = (msg->header ? ofpraw_decode(&raw, msg->header) : ofpraw_pull(&raw, msg)); if (error) { return error; } if (!msg->size) { return EOF; } else if (raw == OFPRAW_OFPST14_PORT_REPLY) { return ofputil_pull_ofp14_port_stats(ps, msg); } else if (raw == OFPRAW_OFPST13_PORT_REPLY) { const struct ofp13_port_stats *ps13; ps13 = ofpbuf_try_pull(msg, sizeof *ps13); if (!ps13) { goto bad_len; } return ofputil_port_stats_from_ofp13(ps, ps13); } else if (raw == OFPRAW_OFPST11_PORT_REPLY) { const struct ofp11_port_stats *ps11; ps11 = ofpbuf_try_pull(msg, sizeof *ps11); if (!ps11) { goto bad_len; } return ofputil_port_stats_from_ofp11(ps, ps11); } else if (raw == OFPRAW_OFPST10_PORT_REPLY) { const struct ofp10_port_stats *ps10; ps10 = ofpbuf_try_pull(msg, sizeof *ps10); if (!ps10) { goto bad_len; } return ofputil_port_stats_from_ofp10(ps, ps10); } else { OVS_NOT_REACHED(); } bad_len: VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %"PRIu32" leftover " "bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } /* Parse a port status request message into a 16 bit OpenFlow 1.0 * port number and stores the latter in '*ofp10_port'. * Returns 0 if successful, otherwise an OFPERR_* number. */ enum ofperr ofputil_decode_port_stats_request(const struct ofp_header *request, ofp_port_t *ofp10_port) { switch ((enum ofp_version)request->version) { case OFP15_VERSION: case OFP14_VERSION: case OFP13_VERSION: case OFP12_VERSION: case OFP11_VERSION: { const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request); return ofputil_port_from_ofp11(psr11->port_no, ofp10_port); } case OFP10_VERSION: { const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request); *ofp10_port = u16_to_ofp(ntohs(psr10->port_no)); return 0; } default: OVS_NOT_REACHED(); } } /* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */ void ofputil_bucket_list_destroy(struct ovs_list *buckets) { struct ofputil_bucket *bucket; LIST_FOR_EACH_POP (bucket, list_node, buckets) { free(bucket->ofpacts); free(bucket); } } /* Clones 'bucket' and its ofpacts data */ static struct ofputil_bucket * ofputil_bucket_clone_data(const struct ofputil_bucket *bucket) { struct ofputil_bucket *new; new = xmemdup(bucket, sizeof *bucket); new->ofpacts = xmemdup(bucket->ofpacts, bucket->ofpacts_len); return new; } /* Clones each of the buckets in the list 'src' appending them * in turn to 'dest' which should be an initialised list. * An exception is that if the pointer value of a bucket in 'src' * matches 'skip' then it is not cloned or appended to 'dest'. * This allows all of 'src' or 'all of 'src' except 'skip' to * be cloned and appended to 'dest'. */ void ofputil_bucket_clone_list(struct ovs_list *dest, const struct ovs_list *src, const struct ofputil_bucket *skip) { struct ofputil_bucket *bucket; LIST_FOR_EACH (bucket, list_node, src) { struct ofputil_bucket *new_bucket; if (bucket == skip) { continue; } new_bucket = ofputil_bucket_clone_data(bucket); list_push_back(dest, &new_bucket->list_node); } } /* Find a bucket in the list 'buckets' whose bucket id is 'bucket_id' * Returns the first bucket found or NULL if no buckets are found. */ struct ofputil_bucket * ofputil_bucket_find(const struct ovs_list *buckets, uint32_t bucket_id) { struct ofputil_bucket *bucket; if (bucket_id > OFPG15_BUCKET_MAX) { return NULL; } LIST_FOR_EACH (bucket, list_node, buckets) { if (bucket->bucket_id == bucket_id) { return bucket; } } return NULL; } /* Returns true if more than one bucket in the list 'buckets' * have the same bucket id. Returns false otherwise. */ bool ofputil_bucket_check_duplicate_id(const struct ovs_list *buckets) { struct ofputil_bucket *i, *j; LIST_FOR_EACH (i, list_node, buckets) { LIST_FOR_EACH_REVERSE (j, list_node, buckets) { if (i == j) { break; } if (i->bucket_id == j->bucket_id) { return true; } } } return false; } /* Returns the bucket at the front of the list 'buckets'. * Undefined if 'buckets is empty. */ struct ofputil_bucket * ofputil_bucket_list_front(const struct ovs_list *buckets) { static struct ofputil_bucket *bucket; ASSIGN_CONTAINER(bucket, list_front(buckets), list_node); return bucket; } /* Returns the bucket at the back of the list 'buckets'. * Undefined if 'buckets is empty. */ struct ofputil_bucket * ofputil_bucket_list_back(const struct ovs_list *buckets) { static struct ofputil_bucket *bucket; ASSIGN_CONTAINER(bucket, list_back(buckets), list_node); return bucket; } /* Returns an OpenFlow group stats request for OpenFlow version 'ofp_version', * that requests stats for group 'group_id'. (Use OFPG_ALL to request stats * for all groups.) * * Group statistics include packet and byte counts for each group. */ struct ofpbuf * ofputil_encode_group_stats_request(enum ofp_version ofp_version, uint32_t group_id) { struct ofpbuf *request; switch (ofp_version) { case OFP10_VERSION: ovs_fatal(0, "dump-group-stats needs OpenFlow 1.1 or later " "(\'-O OpenFlow11\')"); case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: { struct ofp11_group_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->group_id = htonl(group_id); break; } default: OVS_NOT_REACHED(); } return request; } void ofputil_uninit_group_desc(struct ofputil_group_desc *gd) { ofputil_bucket_list_destroy(&gd->buckets); free(&gd->props.fields); } /* Decodes the OpenFlow group description request in 'oh', returning the group * whose description is requested, or OFPG_ALL if stats for all groups was * requested. */ uint32_t ofputil_decode_group_desc_request(const struct ofp_header *oh) { struct ofpbuf request; enum ofpraw raw; ofpbuf_use_const(&request, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&request); if (raw == OFPRAW_OFPST11_GROUP_DESC_REQUEST) { return OFPG_ALL; } else if (raw == OFPRAW_OFPST15_GROUP_DESC_REQUEST) { ovs_be32 *group_id = ofpbuf_pull(&request, sizeof *group_id); return ntohl(*group_id); } else { OVS_NOT_REACHED(); } } /* Returns an OpenFlow group description request for OpenFlow version * 'ofp_version', that requests stats for group 'group_id'. Use OFPG_ALL to * request stats for all groups (OpenFlow 1.4 and earlier always request all * groups). * * Group descriptions include the bucket and action configuration for each * group. */ struct ofpbuf * ofputil_encode_group_desc_request(enum ofp_version ofp_version, uint32_t group_id) { struct ofpbuf *request; switch (ofp_version) { case OFP10_VERSION: ovs_fatal(0, "dump-groups needs OpenFlow 1.1 or later " "(\'-O OpenFlow11\')"); case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST, ofp_version, 0); break; case OFP15_VERSION:{ struct ofp15_group_desc_request *req; request = ofpraw_alloc(OFPRAW_OFPST15_GROUP_DESC_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->group_id = htonl(group_id); break; } default: OVS_NOT_REACHED(); } return request; } static void ofputil_group_bucket_counters_to_ofp11(const struct ofputil_group_stats *gs, struct ofp11_bucket_counter bucket_cnts[]) { int i; for (i = 0; i < gs->n_buckets; i++) { bucket_cnts[i].packet_count = htonll(gs->bucket_stats[i].packet_count); bucket_cnts[i].byte_count = htonll(gs->bucket_stats[i].byte_count); } } static void ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *gs, struct ofp11_group_stats *gs11, size_t length, struct ofp11_bucket_counter bucket_cnts[]) { memset(gs11, 0, sizeof *gs11); gs11->length = htons(length); gs11->group_id = htonl(gs->group_id); gs11->ref_count = htonl(gs->ref_count); gs11->packet_count = htonll(gs->packet_count); gs11->byte_count = htonll(gs->byte_count); ofputil_group_bucket_counters_to_ofp11(gs, bucket_cnts); } static void ofputil_group_stats_to_ofp13(const struct ofputil_group_stats *gs, struct ofp13_group_stats *gs13, size_t length, struct ofp11_bucket_counter bucket_cnts[]) { ofputil_group_stats_to_ofp11(gs, &gs13->gs, length, bucket_cnts); gs13->duration_sec = htonl(gs->duration_sec); gs13->duration_nsec = htonl(gs->duration_nsec); } /* Encodes 'gs' properly for the format of the list of group statistics * replies already begun in 'replies' and appends it to the list. 'replies' * must have originally been initialized with ofpmp_init(). */ void ofputil_append_group_stats(struct ovs_list *replies, const struct ofputil_group_stats *gs) { size_t bucket_counter_size; struct ofp11_bucket_counter *bucket_counters; size_t length; bucket_counter_size = gs->n_buckets * sizeof(struct ofp11_bucket_counter); switch (ofpmp_version(replies)) { case OFP11_VERSION: case OFP12_VERSION:{ struct ofp11_group_stats *gs11; length = sizeof *gs11 + bucket_counter_size; gs11 = ofpmp_append(replies, length); bucket_counters = (struct ofp11_bucket_counter *)(gs11 + 1); ofputil_group_stats_to_ofp11(gs, gs11, length, bucket_counters); break; } case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: { struct ofp13_group_stats *gs13; length = sizeof *gs13 + bucket_counter_size; gs13 = ofpmp_append(replies, length); bucket_counters = (struct ofp11_bucket_counter *)(gs13 + 1); ofputil_group_stats_to_ofp13(gs, gs13, length, bucket_counters); break; } case OFP10_VERSION: default: OVS_NOT_REACHED(); } } /* Returns an OpenFlow group features request for OpenFlow version * 'ofp_version'. */ struct ofpbuf * ofputil_encode_group_features_request(enum ofp_version ofp_version) { struct ofpbuf *request = NULL; switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: ovs_fatal(0, "dump-group-features needs OpenFlow 1.2 or later " "(\'-O OpenFlow12\')"); case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST, ofp_version, 0); break; default: OVS_NOT_REACHED(); } return request; } /* Returns a OpenFlow message that encodes 'features' properly as a reply to * group features request 'request'. */ struct ofpbuf * ofputil_encode_group_features_reply( const struct ofputil_group_features *features, const struct ofp_header *request) { struct ofp12_group_features_stats *ogf; struct ofpbuf *reply; int i; reply = ofpraw_alloc_xid(OFPRAW_OFPST12_GROUP_FEATURES_REPLY, request->version, request->xid, 0); ogf = ofpbuf_put_zeros(reply, sizeof *ogf); ogf->types = htonl(features->types); ogf->capabilities = htonl(features->capabilities); for (i = 0; i < OFPGT12_N_TYPES; i++) { ogf->max_groups[i] = htonl(features->max_groups[i]); ogf->actions[i] = ofpact_bitmap_to_openflow(features->ofpacts[i], request->version); } return reply; } /* Decodes group features reply 'oh' into 'features'. */ void ofputil_decode_group_features_reply(const struct ofp_header *oh, struct ofputil_group_features *features) { const struct ofp12_group_features_stats *ogf = ofpmsg_body(oh); int i; features->types = ntohl(ogf->types); features->capabilities = ntohl(ogf->capabilities); for (i = 0; i < OFPGT12_N_TYPES; i++) { features->max_groups[i] = ntohl(ogf->max_groups[i]); features->ofpacts[i] = ofpact_bitmap_from_openflow( ogf->actions[i], oh->version); } } /* Parse a group status request message into a 32 bit OpenFlow 1.1 * group ID and stores the latter in '*group_id'. * Returns 0 if successful, otherwise an OFPERR_* number. */ enum ofperr ofputil_decode_group_stats_request(const struct ofp_header *request, uint32_t *group_id) { const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request); *group_id = ntohl(gsr11->group_id); return 0; } /* Converts a group stats reply in 'msg' into an abstract ofputil_group_stats * in 'gs'. Assigns freshly allocated memory to gs->bucket_stats for the * caller to eventually free. * * Multiple group stats replies can be packed into a single OpenFlow message. * Calling this function multiple times for a single 'msg' iterates through the * replies. The caller must initially leave 'msg''s layer pointers null and * not modify them between calls. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_group_stats_reply(struct ofpbuf *msg, struct ofputil_group_stats *gs) { struct ofp11_bucket_counter *obc; struct ofp11_group_stats *ogs11; enum ofpraw raw; enum ofperr error; size_t base_len; size_t length; size_t i; gs->bucket_stats = NULL; error = (msg->header ? ofpraw_decode(&raw, msg->header) : ofpraw_pull(&raw, msg)); if (error) { return error; } if (!msg->size) { return EOF; } if (raw == OFPRAW_OFPST11_GROUP_REPLY) { base_len = sizeof *ogs11; ogs11 = ofpbuf_try_pull(msg, sizeof *ogs11); gs->duration_sec = gs->duration_nsec = UINT32_MAX; } else if (raw == OFPRAW_OFPST13_GROUP_REPLY) { struct ofp13_group_stats *ogs13; base_len = sizeof *ogs13; ogs13 = ofpbuf_try_pull(msg, sizeof *ogs13); if (ogs13) { ogs11 = &ogs13->gs; gs->duration_sec = ntohl(ogs13->duration_sec); gs->duration_nsec = ntohl(ogs13->duration_nsec); } else { ogs11 = NULL; } } else { OVS_NOT_REACHED(); } if (!ogs11) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %"PRIu32" leftover bytes at end", ofpraw_get_name(raw), msg->size); return OFPERR_OFPBRC_BAD_LEN; } length = ntohs(ogs11->length); if (length < sizeof base_len) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply claims invalid length %"PRIuSIZE, ofpraw_get_name(raw), length); return OFPERR_OFPBRC_BAD_LEN; } gs->group_id = ntohl(ogs11->group_id); gs->ref_count = ntohl(ogs11->ref_count); gs->packet_count = ntohll(ogs11->packet_count); gs->byte_count = ntohll(ogs11->byte_count); gs->n_buckets = (length - base_len) / sizeof *obc; obc = ofpbuf_try_pull(msg, gs->n_buckets * sizeof *obc); if (!obc) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %"PRIu32" leftover bytes at end", ofpraw_get_name(raw), msg->size); return OFPERR_OFPBRC_BAD_LEN; } gs->bucket_stats = xmalloc(gs->n_buckets * sizeof *gs->bucket_stats); for (i = 0; i < gs->n_buckets; i++) { gs->bucket_stats[i].packet_count = ntohll(obc[i].packet_count); gs->bucket_stats[i].byte_count = ntohll(obc[i].byte_count); } return 0; } static void ofputil_put_ofp11_bucket(const struct ofputil_bucket *bucket, struct ofpbuf *openflow, enum ofp_version ofp_version) { struct ofp11_bucket *ob; size_t start; start = openflow->size; ofpbuf_put_zeros(openflow, sizeof *ob); ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len, openflow, ofp_version); ob = ofpbuf_at_assert(openflow, start, sizeof *ob); ob->len = htons(openflow->size - start); ob->weight = htons(bucket->weight); ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port); ob->watch_group = htonl(bucket->watch_group); } static void ofputil_put_ofp15_group_bucket_prop_weight(ovs_be16 weight, struct ofpbuf *openflow) { size_t start_ofs; struct ofp15_group_bucket_prop_weight *prop; start_ofs = start_property(openflow, OFPGBPT15_WEIGHT); ofpbuf_put_zeros(openflow, sizeof *prop - sizeof(struct ofp_prop_header)); prop = ofpbuf_at_assert(openflow, start_ofs, sizeof *prop); prop->weight = weight; end_property(openflow, start_ofs); } static void ofputil_put_ofp15_group_bucket_prop_watch(ovs_be32 watch, uint16_t type, struct ofpbuf *openflow) { size_t start_ofs; struct ofp15_group_bucket_prop_watch *prop; start_ofs = start_property(openflow, type); ofpbuf_put_zeros(openflow, sizeof *prop - sizeof(struct ofp_prop_header)); prop = ofpbuf_at_assert(openflow, start_ofs, sizeof *prop); prop->watch = watch; end_property(openflow, start_ofs); } static void ofputil_put_ofp15_bucket(const struct ofputil_bucket *bucket, uint32_t bucket_id, enum ofp11_group_type group_type, struct ofpbuf *openflow, enum ofp_version ofp_version) { struct ofp15_bucket *ob; size_t start, actions_start, actions_len; start = openflow->size; ofpbuf_put_zeros(openflow, sizeof *ob); actions_start = openflow->size; ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len, openflow, ofp_version); actions_len = openflow->size - actions_start; if (group_type == OFPGT11_SELECT) { ofputil_put_ofp15_group_bucket_prop_weight(htons(bucket->weight), openflow); } if (bucket->watch_port != OFPP_ANY) { ovs_be32 port = ofputil_port_to_ofp11(bucket->watch_port); ofputil_put_ofp15_group_bucket_prop_watch(port, OFPGBPT15_WATCH_PORT, openflow); } if (bucket->watch_group != OFPG_ANY) { ovs_be32 group = htonl(bucket->watch_group); ofputil_put_ofp15_group_bucket_prop_watch(group, OFPGBPT15_WATCH_GROUP, openflow); } ob = ofpbuf_at_assert(openflow, start, sizeof *ob); ob->len = htons(openflow->size - start); ob->action_array_len = htons(actions_len); ob->bucket_id = htonl(bucket_id); } static void ofputil_put_group_prop_ntr_selection_method(enum ofp_version ofp_version, const struct ofputil_group_props *gp, struct ofpbuf *openflow) { struct ntr_group_prop_selection_method *prop; size_t start; start = openflow->size; ofpbuf_put_zeros(openflow, sizeof *prop); oxm_put_field_array(openflow, &gp->fields, ofp_version); prop = ofpbuf_at_assert(openflow, start, sizeof *prop); prop->type = htons(OFPGPT15_EXPERIMENTER); prop->experimenter = htonl(NTR_VENDOR_ID); prop->exp_type = htonl(NTRT_SELECTION_METHOD); strcpy(prop->selection_method, gp->selection_method); prop->selection_method_param = htonll(gp->selection_method_param); end_property(openflow, start); } static void ofputil_append_ofp11_group_desc_reply(const struct ofputil_group_desc *gds, const struct ovs_list *buckets, struct ovs_list *replies, enum ofp_version version) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); struct ofp11_group_desc_stats *ogds; struct ofputil_bucket *bucket; size_t start_ogds; start_ogds = reply->size; ofpbuf_put_zeros(reply, sizeof *ogds); LIST_FOR_EACH (bucket, list_node, buckets) { ofputil_put_ofp11_bucket(bucket, reply, version); } ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds); ogds->length = htons(reply->size - start_ogds); ogds->type = gds->type; ogds->group_id = htonl(gds->group_id); ofpmp_postappend(replies, start_ogds); } static void ofputil_append_ofp15_group_desc_reply(const struct ofputil_group_desc *gds, const struct ovs_list *buckets, struct ovs_list *replies, enum ofp_version version) { struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); struct ofp15_group_desc_stats *ogds; struct ofputil_bucket *bucket; size_t start_ogds, start_buckets; start_ogds = reply->size; ofpbuf_put_zeros(reply, sizeof *ogds); start_buckets = reply->size; LIST_FOR_EACH (bucket, list_node, buckets) { ofputil_put_ofp15_bucket(bucket, bucket->bucket_id, gds->type, reply, version); } ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds); ogds->type = gds->type; ogds->group_id = htonl(gds->group_id); ogds->bucket_list_len = htons(reply->size - start_buckets); /* Add group properties */ if (gds->props.selection_method[0]) { ofputil_put_group_prop_ntr_selection_method(version, &gds->props, reply); } ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds); ogds->length = htons(reply->size - start_ogds); ofpmp_postappend(replies, start_ogds); } /* Appends a group stats reply that contains the data in 'gds' to those already * present in the list of ofpbufs in 'replies'. 'replies' should have been * initialized with ofpmp_init(). */ void ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds, const struct ovs_list *buckets, struct ovs_list *replies) { enum ofp_version version = ofpmp_version(replies); switch (version) { case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: ofputil_append_ofp11_group_desc_reply(gds, buckets, replies, version); break; case OFP15_VERSION: ofputil_append_ofp15_group_desc_reply(gds, buckets, replies, version); break; case OFP10_VERSION: default: OVS_NOT_REACHED(); } } static enum ofperr ofputil_pull_ofp11_buckets(struct ofpbuf *msg, size_t buckets_length, enum ofp_version version, struct ovs_list *buckets) { struct ofp11_bucket *ob; uint32_t bucket_id = 0; list_init(buckets); while (buckets_length > 0) { struct ofputil_bucket *bucket; struct ofpbuf ofpacts; enum ofperr error; size_t ob_len; ob = (buckets_length >= sizeof *ob ? ofpbuf_try_pull(msg, sizeof *ob) : NULL); if (!ob) { VLOG_WARN_RL(&bad_ofmsg_rl, "buckets end with %"PRIuSIZE" leftover bytes", buckets_length); ofputil_bucket_list_destroy(buckets); return OFPERR_OFPGMFC_BAD_BUCKET; } ob_len = ntohs(ob->len); if (ob_len < sizeof *ob) { VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " "%"PRIuSIZE" is not valid", ob_len); ofputil_bucket_list_destroy(buckets); return OFPERR_OFPGMFC_BAD_BUCKET; } else if (ob_len > buckets_length) { VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " "%"PRIuSIZE" exceeds remaining buckets data size %"PRIuSIZE, ob_len, buckets_length); ofputil_bucket_list_destroy(buckets); return OFPERR_OFPGMFC_BAD_BUCKET; } buckets_length -= ob_len; ofpbuf_init(&ofpacts, 0); error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob, version, &ofpacts); if (error) { ofpbuf_uninit(&ofpacts); ofputil_bucket_list_destroy(buckets); return error; } bucket = xzalloc(sizeof *bucket); bucket->weight = ntohs(ob->weight); error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port); if (error) { ofpbuf_uninit(&ofpacts); ofputil_bucket_list_destroy(buckets); free(bucket); return OFPERR_OFPGMFC_BAD_WATCH; } bucket->watch_group = ntohl(ob->watch_group); bucket->bucket_id = bucket_id++; bucket->ofpacts = ofpbuf_steal_data(&ofpacts); bucket->ofpacts_len = ofpacts.size; list_push_back(buckets, &bucket->list_node); } return 0; } static enum ofperr parse_ofp15_group_bucket_prop_weight(const struct ofpbuf *payload, ovs_be16 *weight) { struct ofp15_group_bucket_prop_weight *prop = payload->data; if (payload->size != sizeof *prop) { log_property(false, "OpenFlow bucket weight property length " "%u is not valid", payload->size); return OFPERR_OFPBPC_BAD_LEN; } *weight = prop->weight; return 0; } static enum ofperr parse_ofp15_group_bucket_prop_watch(const struct ofpbuf *payload, ovs_be32 *watch) { struct ofp15_group_bucket_prop_watch *prop = payload->data; if (payload->size != sizeof *prop) { log_property(false, "OpenFlow bucket watch port or group " "property length %u is not valid", payload->size); return OFPERR_OFPBPC_BAD_LEN; } *watch = prop->watch; return 0; } static enum ofperr ofputil_pull_ofp15_buckets(struct ofpbuf *msg, size_t buckets_length, enum ofp_version version, uint8_t group_type, struct ovs_list *buckets) { struct ofp15_bucket *ob; list_init(buckets); while (buckets_length > 0) { struct ofputil_bucket *bucket = NULL; struct ofpbuf ofpacts; enum ofperr err = OFPERR_OFPGMFC_BAD_BUCKET; struct ofpbuf properties; size_t ob_len, actions_len, properties_len; ovs_be32 watch_port = ofputil_port_to_ofp11(OFPP_ANY); ovs_be32 watch_group = htonl(OFPG_ANY); ovs_be16 weight = htons(group_type == OFPGT11_SELECT ? 1 : 0); ofpbuf_init(&ofpacts, 0); ob = ofpbuf_try_pull(msg, sizeof *ob); if (!ob) { VLOG_WARN_RL(&bad_ofmsg_rl, "buckets end with %"PRIuSIZE " leftover bytes", buckets_length); goto err; } ob_len = ntohs(ob->len); actions_len = ntohs(ob->action_array_len); if (ob_len < sizeof *ob) { VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " "%"PRIuSIZE" is not valid", ob_len); goto err; } else if (ob_len > buckets_length) { VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " "%"PRIuSIZE" exceeds remaining buckets data size %" PRIuSIZE, ob_len, buckets_length); goto err; } else if (actions_len > ob_len - sizeof *ob) { VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket actions " "length %"PRIuSIZE" exceeds remaining bucket " "data size %"PRIuSIZE, actions_len, ob_len - sizeof *ob); goto err; } buckets_length -= ob_len; err = ofpacts_pull_openflow_actions(msg, actions_len, version, &ofpacts); if (err) { goto err; } properties_len = ob_len - sizeof *ob - actions_len; ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len), properties_len); while (properties.size > 0) { struct ofpbuf payload; uint16_t type; err = ofputil_pull_property(&properties, &payload, &type); if (err) { goto err; } switch (type) { case OFPGBPT15_WEIGHT: err = parse_ofp15_group_bucket_prop_weight(&payload, &weight); break; case OFPGBPT15_WATCH_PORT: err = parse_ofp15_group_bucket_prop_watch(&payload, &watch_port); break; case OFPGBPT15_WATCH_GROUP: err = parse_ofp15_group_bucket_prop_watch(&payload, &watch_group); break; default: log_property(false, "unknown group bucket property %"PRIu16, type); err = OFPERR_OFPBPC_BAD_TYPE; break; } if (err) { goto err; } } bucket = xzalloc(sizeof *bucket); bucket->weight = ntohs(weight); err = ofputil_port_from_ofp11(watch_port, &bucket->watch_port); if (err) { err = OFPERR_OFPGMFC_BAD_WATCH; goto err; } bucket->watch_group = ntohl(watch_group); bucket->bucket_id = ntohl(ob->bucket_id); if (bucket->bucket_id > OFPG15_BUCKET_MAX) { VLOG_WARN_RL(&bad_ofmsg_rl, "bucket id (%u) is out of range", bucket->bucket_id); err = OFPERR_OFPGMFC_BAD_BUCKET; goto err; } bucket->ofpacts = ofpbuf_steal_data(&ofpacts); bucket->ofpacts_len = ofpacts.size; list_push_back(buckets, &bucket->list_node); continue; err: free(bucket); ofpbuf_uninit(&ofpacts); ofputil_bucket_list_destroy(buckets); return err; } if (ofputil_bucket_check_duplicate_id(buckets)) { VLOG_WARN_RL(&bad_ofmsg_rl, "Duplicate bucket id"); ofputil_bucket_list_destroy(buckets); return OFPERR_OFPGMFC_BAD_BUCKET; } return 0; } static void ofputil_init_group_properties(struct ofputil_group_props *gp) { memset(gp, 0, sizeof *gp); } static enum ofperr parse_group_prop_ntr_selection_method(struct ofpbuf *payload, enum ofp11_group_type group_type, enum ofp15_group_mod_command group_cmd, struct ofputil_group_props *gp) { struct ntr_group_prop_selection_method *prop = payload->data; size_t fields_len, method_len; enum ofperr error; switch (group_type) { case OFPGT11_SELECT: break; case OFPGT11_ALL: case OFPGT11_INDIRECT: case OFPGT11_FF: log_property(false, "ntr selection method property is only allowed " "for select groups"); return OFPERR_OFPBPC_BAD_VALUE; default: return OFPERR_OFPGMFC_BAD_TYPE; } switch (group_cmd) { case OFPGC15_ADD: case OFPGC15_MODIFY: break; case OFPGC15_DELETE: case OFPGC15_INSERT_BUCKET: case OFPGC15_REMOVE_BUCKET: log_property(false, "ntr selection method property is only allowed " "for add and delete group modifications"); return OFPERR_OFPBPC_BAD_VALUE; default: return OFPERR_OFPGMFC_BAD_COMMAND; } if (payload->size < sizeof *prop) { log_property(false, "ntr selection method property length " "%u is not valid", payload->size); return OFPERR_OFPBPC_BAD_LEN; } method_len = strnlen(prop->selection_method, NTR_MAX_SELECTION_METHOD_LEN); if (method_len == NTR_MAX_SELECTION_METHOD_LEN) { log_property(false, "ntr selection method is not null terminated"); return OFPERR_OFPBPC_BAD_VALUE; } if (strcmp("hash", prop->selection_method)) { log_property(false, "ntr selection method '%s' is not supported", prop->selection_method); return OFPERR_OFPBPC_BAD_VALUE; } strcpy(gp->selection_method, prop->selection_method); gp->selection_method_param = ntohll(prop->selection_method_param); if (!method_len && gp->selection_method_param) { log_property(false, "ntr selection method parameter is non-zero but " "selection method is empty"); return OFPERR_OFPBPC_BAD_VALUE; } ofpbuf_pull(payload, sizeof *prop); fields_len = ntohs(prop->length) - sizeof *prop; if (!method_len && fields_len) { log_property(false, "ntr selection method parameter is zero " "but fields are provided"); return OFPERR_OFPBPC_BAD_VALUE; } error = oxm_pull_field_array(payload->data, fields_len, &gp->fields); if (error) { log_property(false, "ntr selection method fields are invalid"); return error; } return 0; } static enum ofperr parse_group_prop_ntr(struct ofpbuf *payload, uint32_t exp_type, enum ofp11_group_type group_type, enum ofp15_group_mod_command group_cmd, struct ofputil_group_props *gp) { enum ofperr error; switch (exp_type) { case NTRT_SELECTION_METHOD: error = parse_group_prop_ntr_selection_method(payload, group_type, group_cmd, gp); break; default: log_property(false, "unknown group property ntr experimenter type " "%"PRIu32, exp_type); error = OFPERR_OFPBPC_BAD_TYPE; break; } return error; } static enum ofperr parse_ofp15_group_prop_exp(struct ofpbuf *payload, enum ofp11_group_type group_type, enum ofp15_group_mod_command group_cmd, struct ofputil_group_props *gp) { struct ofp_prop_experimenter *prop = payload->data; uint16_t experimenter; uint32_t exp_type; enum ofperr error; if (payload->size < sizeof *prop) { return OFPERR_OFPBPC_BAD_LEN; } experimenter = ntohl(prop->experimenter); exp_type = ntohl(prop->exp_type); switch (experimenter) { case NTR_VENDOR_ID: case NTR_COMPAT_VENDOR_ID: error = parse_group_prop_ntr(payload, exp_type, group_type, group_cmd, gp); break; default: log_property(false, "unknown group property experimenter %"PRIu16, experimenter); error = OFPERR_OFPBPC_BAD_EXPERIMENTER; break; } return error; } static enum ofperr parse_ofp15_group_properties(struct ofpbuf *msg, enum ofp11_group_type group_type, enum ofp15_group_mod_command group_cmd, struct ofputil_group_props *gp, size_t properties_len) { struct ofpbuf properties; ofpbuf_use_const(&properties, ofpbuf_pull(msg, properties_len), properties_len); while (properties.size > 0) { struct ofpbuf payload; enum ofperr error; uint16_t type; error = ofputil_pull_property(&properties, &payload, &type); if (error) { return error; } switch (type) { case OFPGPT15_EXPERIMENTER: error = parse_ofp15_group_prop_exp(&payload, group_type, group_cmd, gp); break; default: log_property(false, "unknown group property %"PRIu16, type); error = OFPERR_OFPBPC_BAD_TYPE; break; } if (error) { return error; } } return 0; } static int ofputil_decode_ofp11_group_desc_reply(struct ofputil_group_desc *gd, struct ofpbuf *msg, enum ofp_version version) { struct ofp11_group_desc_stats *ogds; size_t length; if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } ogds = ofpbuf_try_pull(msg, sizeof *ogds); if (!ogds) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %"PRIu32" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } gd->type = ogds->type; gd->group_id = ntohl(ogds->group_id); length = ntohs(ogds->length); if (length < sizeof *ogds || length - sizeof *ogds > msg->size) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid " "length %"PRIuSIZE, length); return OFPERR_OFPBRC_BAD_LEN; } return ofputil_pull_ofp11_buckets(msg, length - sizeof *ogds, version, &gd->buckets); } static int ofputil_decode_ofp15_group_desc_reply(struct ofputil_group_desc *gd, struct ofpbuf *msg, enum ofp_version version) { struct ofp15_group_desc_stats *ogds; uint16_t length, bucket_list_len; int error; if (!msg->header) { ofpraw_pull_assert(msg); } if (!msg->size) { return EOF; } ogds = ofpbuf_try_pull(msg, sizeof *ogds); if (!ogds) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %"PRIu32" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } gd->type = ogds->type; gd->group_id = ntohl(ogds->group_id); length = ntohs(ogds->length); if (length < sizeof *ogds || length - sizeof *ogds > msg->size) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid " "length %u", length); return OFPERR_OFPBRC_BAD_LEN; } bucket_list_len = ntohs(ogds->bucket_list_len); if (length < bucket_list_len + sizeof *ogds) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid " "bucket list length %u", bucket_list_len); return OFPERR_OFPBRC_BAD_LEN; } error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, version, gd->type, &gd->buckets); if (error) { return error; } /* By definition group desc messages don't have a group mod command. * However, parse_group_prop_ntr_selection_method() checks to make sure * that the command is OFPGC15_ADD or OFPGC15_DELETE to guard * against group mod messages with other commands supplying * a NTR selection method group experimenter property. * Such properties are valid for group desc replies so * claim that the group mod command is OFPGC15_ADD to * satisfy the check in parse_group_prop_ntr_selection_method() */ error = parse_ofp15_group_properties( msg, gd->type, OFPGC15_ADD, &gd->props, length - sizeof *ogds - bucket_list_len); if (error) { ofputil_bucket_list_destroy(&gd->buckets); } return error; } /* Converts a group description reply in 'msg' into an abstract * ofputil_group_desc in 'gd'. * * Multiple group description replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. The caller must initially leave 'msg''s layer pointers * null and not modify them between calls. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd, struct ofpbuf *msg, enum ofp_version version) { ofputil_init_group_properties(&gd->props); switch (version) { case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: return ofputil_decode_ofp11_group_desc_reply(gd, msg, version); case OFP15_VERSION: return ofputil_decode_ofp15_group_desc_reply(gd, msg, version); case OFP10_VERSION: default: OVS_NOT_REACHED(); } } void ofputil_uninit_group_mod(struct ofputil_group_mod *gm) { ofputil_bucket_list_destroy(&gm->buckets); } static struct ofpbuf * ofputil_encode_ofp11_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm) { struct ofpbuf *b; struct ofp11_group_mod *ogm; size_t start_ogm; struct ofputil_bucket *bucket; b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0); start_ogm = b->size; ofpbuf_put_zeros(b, sizeof *ogm); LIST_FOR_EACH (bucket, list_node, &gm->buckets) { ofputil_put_ofp11_bucket(bucket, b, ofp_version); } ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm); ogm->command = htons(gm->command); ogm->type = gm->type; ogm->group_id = htonl(gm->group_id); return b; } static struct ofpbuf * ofputil_encode_ofp15_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm) { struct ofpbuf *b; struct ofp15_group_mod *ogm; size_t start_ogm; struct ofputil_bucket *bucket; struct id_pool *bucket_ids = NULL; b = ofpraw_alloc(OFPRAW_OFPT15_GROUP_MOD, ofp_version, 0); start_ogm = b->size; ofpbuf_put_zeros(b, sizeof *ogm); LIST_FOR_EACH (bucket, list_node, &gm->buckets) { uint32_t bucket_id; /* Generate a bucket id if none was supplied */ if (bucket->bucket_id > OFPG15_BUCKET_MAX) { if (!bucket_ids) { const struct ofputil_bucket *bkt; bucket_ids = id_pool_create(0, OFPG15_BUCKET_MAX + 1); /* Mark all bucket_ids that are present in gm * as used in the pool. */ LIST_FOR_EACH_REVERSE (bkt, list_node, &gm->buckets) { if (bkt == bucket) { break; } if (bkt->bucket_id <= OFPG15_BUCKET_MAX) { id_pool_add(bucket_ids, bkt->bucket_id); } } } if (!id_pool_alloc_id(bucket_ids, &bucket_id)) { OVS_NOT_REACHED(); } } else { bucket_id = bucket->bucket_id; } ofputil_put_ofp15_bucket(bucket, bucket_id, gm->type, b, ofp_version); } ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm); ogm->command = htons(gm->command); ogm->type = gm->type; ogm->group_id = htonl(gm->group_id); ogm->command_bucket_id = htonl(gm->command_bucket_id); ogm->bucket_array_len = htons(b->size - start_ogm - sizeof *ogm); /* Add group properties */ if (gm->props.selection_method[0]) { ofputil_put_group_prop_ntr_selection_method(ofp_version, &gm->props, b); } id_pool_destroy(bucket_ids); return b; } static void bad_group_cmd(enum ofp15_group_mod_command cmd) { const char *opt_version; const char *version; const char *cmd_str; switch (cmd) { case OFPGC15_ADD: case OFPGC15_MODIFY: case OFPGC15_DELETE: version = "1.1"; opt_version = "11"; break; case OFPGC15_INSERT_BUCKET: case OFPGC15_REMOVE_BUCKET: version = "1.5"; opt_version = "15"; break; default: OVS_NOT_REACHED(); } switch (cmd) { case OFPGC15_ADD: cmd_str = "add-group"; break; case OFPGC15_MODIFY: cmd_str = "mod-group"; break; case OFPGC15_DELETE: cmd_str = "del-group"; break; case OFPGC15_INSERT_BUCKET: cmd_str = "insert-bucket"; break; case OFPGC15_REMOVE_BUCKET: cmd_str = "remove-bucket"; break; default: OVS_NOT_REACHED(); } ovs_fatal(0, "%s needs OpenFlow %s or later (\'-O OpenFlow%s\')", cmd_str, version, opt_version); } /* Converts abstract group mod 'gm' into a message for OpenFlow version * 'ofp_version' and returns the message. */ struct ofpbuf * ofputil_encode_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm) { switch (ofp_version) { case OFP10_VERSION: bad_group_cmd(gm->command); case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: if (gm->command > OFPGC11_DELETE) { bad_group_cmd(gm->command); } return ofputil_encode_ofp11_group_mod(ofp_version, gm); case OFP15_VERSION: return ofputil_encode_ofp15_group_mod(ofp_version, gm); default: OVS_NOT_REACHED(); } } static enum ofperr ofputil_pull_ofp11_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version, struct ofputil_group_mod *gm) { const struct ofp11_group_mod *ogm; enum ofperr error; ogm = ofpbuf_pull(msg, sizeof *ogm); gm->command = ntohs(ogm->command); gm->type = ogm->type; gm->group_id = ntohl(ogm->group_id); gm->command_bucket_id = OFPG15_BUCKET_ALL; error = ofputil_pull_ofp11_buckets(msg, msg->size, ofp_version, &gm->buckets); /* OF1.3.5+ prescribes an error when an OFPGC_DELETE includes buckets. */ if (!error && ofp_version >= OFP13_VERSION && gm->command == OFPGC11_DELETE && !list_is_empty(&gm->buckets)) { error = OFPERR_OFPGMFC_INVALID_GROUP; ofputil_bucket_list_destroy(&gm->buckets); } return error; } static enum ofperr ofputil_pull_ofp15_group_mod(struct ofpbuf *msg, enum ofp_version ofp_version, struct ofputil_group_mod *gm) { const struct ofp15_group_mod *ogm; uint16_t bucket_list_len; enum ofperr error = OFPERR_OFPGMFC_BAD_BUCKET; ogm = ofpbuf_pull(msg, sizeof *ogm); gm->command = ntohs(ogm->command); gm->type = ogm->type; gm->group_id = ntohl(ogm->group_id); gm->command_bucket_id = ntohl(ogm->command_bucket_id); switch (gm->command) { case OFPGC15_REMOVE_BUCKET: if (gm->command_bucket_id == OFPG15_BUCKET_ALL) { error = 0; } /* Fall through */ case OFPGC15_INSERT_BUCKET: if (gm->command_bucket_id <= OFPG15_BUCKET_MAX || gm->command_bucket_id == OFPG15_BUCKET_FIRST || gm->command_bucket_id == OFPG15_BUCKET_LAST) { error = 0; } break; case OFPGC11_ADD: case OFPGC11_MODIFY: case OFPGC11_DELETE: default: if (gm->command_bucket_id == OFPG15_BUCKET_ALL) { error = 0; } break; } if (error) { VLOG_WARN_RL(&bad_ofmsg_rl, "group command bucket id (%u) is out of range", gm->command_bucket_id); return OFPERR_OFPGMFC_BAD_BUCKET; } bucket_list_len = ntohs(ogm->bucket_array_len); if (bucket_list_len > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } error = ofputil_pull_ofp15_buckets(msg, bucket_list_len, ofp_version, gm->type, &gm->buckets); if (error) { return error; } error = parse_ofp15_group_properties(msg, gm->type, gm->command, &gm->props, msg->size); if (error) { ofputil_bucket_list_destroy(&gm->buckets); } return error; } static enum ofperr ofputil_check_group_mod(const struct ofputil_group_mod *gm) { switch (gm->type) { case OFPGT11_INDIRECT: if (!list_is_singleton(&gm->buckets)) { return OFPERR_OFPGMFC_INVALID_GROUP; } break; case OFPGT11_ALL: case OFPGT11_SELECT: case OFPGT11_FF: break; default: return OFPERR_OFPGMFC_BAD_TYPE; } switch (gm->command) { case OFPGC11_ADD: case OFPGC11_MODIFY: case OFPGC11_DELETE: case OFPGC15_INSERT_BUCKET: break; case OFPGC15_REMOVE_BUCKET: if (!list_is_empty(&gm->buckets)) { return OFPERR_OFPGMFC_BAD_BUCKET; } break; default: return OFPERR_OFPGMFC_BAD_COMMAND; } struct ofputil_bucket *bucket; LIST_FOR_EACH (bucket, list_node, &gm->buckets) { if (bucket->weight && gm->type != OFPGT11_SELECT) { return OFPERR_OFPGMFC_INVALID_GROUP; } switch (gm->type) { case OFPGT11_ALL: case OFPGT11_INDIRECT: if (ofputil_bucket_has_liveness(bucket)) { return OFPERR_OFPGMFC_WATCH_UNSUPPORTED; } break; case OFPGT11_SELECT: break; case OFPGT11_FF: if (!ofputil_bucket_has_liveness(bucket)) { return OFPERR_OFPGMFC_INVALID_GROUP; } break; default: OVS_NOT_REACHED(); } } return 0; } /* Converts OpenFlow group mod message 'oh' into an abstract group mod in * 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr ofputil_decode_group_mod(const struct ofp_header *oh, struct ofputil_group_mod *gm) { enum ofp_version ofp_version = oh->version; struct ofpbuf msg; enum ofperr err; ofpbuf_use_const(&msg, oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); ofputil_init_group_properties(&gm->props); switch (ofp_version) { case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: err = ofputil_pull_ofp11_group_mod(&msg, ofp_version, gm); break; case OFP15_VERSION: err = ofputil_pull_ofp15_group_mod(&msg, ofp_version, gm); break; case OFP10_VERSION: default: OVS_NOT_REACHED(); } if (err) { return err; } err = ofputil_check_group_mod(gm); if (err) { ofputil_uninit_group_mod(gm); } return err; } /* Parse a queue status request message into 'oqsr'. * Returns 0 if successful, otherwise an OFPERR_* number. */ enum ofperr ofputil_decode_queue_stats_request(const struct ofp_header *request, struct ofputil_queue_stats_request *oqsr) { switch ((enum ofp_version)request->version) { case OFP15_VERSION: case OFP14_VERSION: case OFP13_VERSION: case OFP12_VERSION: case OFP11_VERSION: { const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request); oqsr->queue_id = ntohl(qsr11->queue_id); return ofputil_port_from_ofp11(qsr11->port_no, &oqsr->port_no); } case OFP10_VERSION: { const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request); oqsr->queue_id = ntohl(qsr10->queue_id); oqsr->port_no = u16_to_ofp(ntohs(qsr10->port_no)); /* OF 1.0 uses OFPP_ALL for OFPP_ANY */ if (oqsr->port_no == OFPP_ALL) { oqsr->port_no = OFPP_ANY; } return 0; } default: OVS_NOT_REACHED(); } } /* Encode a queue stats request for 'oqsr', the encoded message * will be for OpenFlow version 'ofp_version'. Returns message * as a struct ofpbuf. Returns encoded message on success, NULL on error. */ struct ofpbuf * ofputil_encode_queue_stats_request(enum ofp_version ofp_version, const struct ofputil_queue_stats_request *oqsr) { struct ofpbuf *request; switch (ofp_version) { case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: { struct ofp11_queue_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->port_no = ofputil_port_to_ofp11(oqsr->port_no); req->queue_id = htonl(oqsr->queue_id); break; } case OFP10_VERSION: { struct ofp10_queue_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */ req->port_no = htons(ofp_to_u16(oqsr->port_no == OFPP_ANY ? OFPP_ALL : oqsr->port_no)); req->queue_id = htonl(oqsr->queue_id); break; } default: OVS_NOT_REACHED(); } return request; } /* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY * message 'oh'. */ size_t ofputil_count_queue_stats(const struct ofp_header *oh) { struct ofputil_queue_stats qs; struct ofpbuf b; size_t n = 0; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); while (!ofputil_decode_queue_stats(&qs, &b)) { n++; } return n; } static enum ofperr ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs, const struct ofp10_queue_stats *qs10) { oqs->port_no = u16_to_ofp(ntohs(qs10->port_no)); oqs->queue_id = ntohl(qs10->queue_id); oqs->tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes)); oqs->tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets)); oqs->tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors)); oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; return 0; } static enum ofperr ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs, const struct ofp11_queue_stats *qs11) { enum ofperr error; error = ofputil_port_from_ofp11(qs11->port_no, &oqs->port_no); if (error) { return error; } oqs->queue_id = ntohl(qs11->queue_id); oqs->tx_bytes = ntohll(qs11->tx_bytes); oqs->tx_packets = ntohll(qs11->tx_packets); oqs->tx_errors = ntohll(qs11->tx_errors); oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; return 0; } static enum ofperr ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs, const struct ofp13_queue_stats *qs13) { enum ofperr error = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs); if (!error) { oqs->duration_sec = ntohl(qs13->duration_sec); oqs->duration_nsec = ntohl(qs13->duration_nsec); } return error; } static enum ofperr ofputil_pull_ofp14_queue_stats(struct ofputil_queue_stats *oqs, struct ofpbuf *msg) { const struct ofp14_queue_stats *qs14; size_t len; qs14 = ofpbuf_try_pull(msg, sizeof *qs14); if (!qs14) { return OFPERR_OFPBRC_BAD_LEN; } len = ntohs(qs14->length); if (len < sizeof *qs14 || len - sizeof *qs14 > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } ofpbuf_pull(msg, len - sizeof *qs14); /* No properties yet defined, so ignore them for now. */ return ofputil_queue_stats_from_ofp13(oqs, &qs14->qs); } /* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract * ofputil_queue_stats in 'qs'. * * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. The caller must initially leave 'msg''s layer pointers * null and not modify them between calls. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) { enum ofperr error; enum ofpraw raw; error = (msg->header ? ofpraw_decode(&raw, msg->header) : ofpraw_pull(&raw, msg)); if (error) { return error; } if (!msg->size) { return EOF; } else if (raw == OFPRAW_OFPST14_QUEUE_REPLY) { return ofputil_pull_ofp14_queue_stats(qs, msg); } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) { const struct ofp13_queue_stats *qs13; qs13 = ofpbuf_try_pull(msg, sizeof *qs13); if (!qs13) { goto bad_len; } return ofputil_queue_stats_from_ofp13(qs, qs13); } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) { const struct ofp11_queue_stats *qs11; qs11 = ofpbuf_try_pull(msg, sizeof *qs11); if (!qs11) { goto bad_len; } return ofputil_queue_stats_from_ofp11(qs, qs11); } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) { const struct ofp10_queue_stats *qs10; qs10 = ofpbuf_try_pull(msg, sizeof *qs10); if (!qs10) { goto bad_len; } return ofputil_queue_stats_from_ofp10(qs, qs10); } else { OVS_NOT_REACHED(); } bad_len: VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %"PRIu32" leftover " "bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } static void ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs, struct ofp10_queue_stats *qs10) { qs10->port_no = htons(ofp_to_u16(oqs->port_no)); memset(qs10->pad, 0, sizeof qs10->pad); qs10->queue_id = htonl(oqs->queue_id); put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->tx_bytes)); put_32aligned_be64(&qs10->tx_packets, htonll(oqs->tx_packets)); put_32aligned_be64(&qs10->tx_errors, htonll(oqs->tx_errors)); } static void ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs, struct ofp11_queue_stats *qs11) { qs11->port_no = ofputil_port_to_ofp11(oqs->port_no); qs11->queue_id = htonl(oqs->queue_id); qs11->tx_bytes = htonll(oqs->tx_bytes); qs11->tx_packets = htonll(oqs->tx_packets); qs11->tx_errors = htonll(oqs->tx_errors); } static void ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs, struct ofp13_queue_stats *qs13) { ofputil_queue_stats_to_ofp11(oqs, &qs13->qs); if (oqs->duration_sec != UINT32_MAX) { qs13->duration_sec = htonl(oqs->duration_sec); qs13->duration_nsec = htonl(oqs->duration_nsec); } else { qs13->duration_sec = OVS_BE32_MAX; qs13->duration_nsec = OVS_BE32_MAX; } } static void ofputil_queue_stats_to_ofp14(const struct ofputil_queue_stats *oqs, struct ofp14_queue_stats *qs14) { qs14->length = htons(sizeof *qs14); memset(qs14->pad, 0, sizeof qs14->pad); ofputil_queue_stats_to_ofp13(oqs, &qs14->qs); } /* Encode a queue stat for 'oqs' and append it to 'replies'. */ void ofputil_append_queue_stat(struct ovs_list *replies, const struct ofputil_queue_stats *oqs) { switch (ofpmp_version(replies)) { case OFP13_VERSION: { struct ofp13_queue_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_queue_stats_to_ofp13(oqs, reply); break; } case OFP12_VERSION: case OFP11_VERSION: { struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_queue_stats_to_ofp11(oqs, reply); break; } case OFP10_VERSION: { struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_queue_stats_to_ofp10(oqs, reply); break; } case OFP14_VERSION: case OFP15_VERSION: { struct ofp14_queue_stats *reply = ofpmp_append(replies, sizeof *reply); ofputil_queue_stats_to_ofp14(oqs, reply); break; } default: OVS_NOT_REACHED(); } } enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *oh, struct ofputil_bundle_ctrl_msg *msg) { struct ofpbuf b; enum ofpraw raw; const struct ofp14_bundle_ctrl_msg *m; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_CONTROL); m = b.msg; msg->bundle_id = ntohl(m->bundle_id); msg->type = ntohs(m->type); msg->flags = ntohs(m->flags); return 0; } struct ofpbuf * ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version, struct ofputil_bundle_ctrl_msg *bc) { struct ofpbuf *request; struct ofp14_bundle_ctrl_msg *m; switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: ovs_fatal(0, "bundles need OpenFlow 1.4 or later " "(\'-O OpenFlow14\')"); case OFP14_VERSION: case OFP15_VERSION: request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0); m = ofpbuf_put_zeros(request, sizeof *m); m->bundle_id = htonl(bc->bundle_id); m->type = htons(bc->type); m->flags = htons(bc->flags); break; default: OVS_NOT_REACHED(); } return request; } struct ofpbuf * ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh, struct ofputil_bundle_ctrl_msg *msg) { struct ofpbuf *buf; struct ofp14_bundle_ctrl_msg *m; buf = ofpraw_alloc_reply(OFPRAW_OFPT14_BUNDLE_CONTROL, oh, 0); m = ofpbuf_put_zeros(buf, sizeof *m); m->bundle_id = htonl(msg->bundle_id); m->type = htons(msg->type); m->flags = htons(msg->flags); return buf; } /* Return true for bundlable state change requests, false for other messages. */ static bool ofputil_is_bundlable(enum ofptype type) { switch (type) { /* Minimum required by OpenFlow 1.4. */ case OFPTYPE_PORT_MOD: case OFPTYPE_FLOW_MOD: return true; /* Nice to have later. */ case OFPTYPE_FLOW_MOD_TABLE_ID: case OFPTYPE_GROUP_MOD: case OFPTYPE_TABLE_MOD: case OFPTYPE_METER_MOD: case OFPTYPE_PACKET_OUT: case OFPTYPE_NXT_TLV_TABLE_MOD: /* Not to be bundlable. */ case OFPTYPE_ECHO_REQUEST: case OFPTYPE_FEATURES_REQUEST: case OFPTYPE_GET_CONFIG_REQUEST: case OFPTYPE_SET_CONFIG: case OFPTYPE_BARRIER_REQUEST: case OFPTYPE_ROLE_REQUEST: case OFPTYPE_ECHO_REPLY: case OFPTYPE_SET_FLOW_FORMAT: case OFPTYPE_SET_PACKET_IN_FORMAT: case OFPTYPE_SET_CONTROLLER_ID: case OFPTYPE_FLOW_AGE: case OFPTYPE_FLOW_MONITOR_CANCEL: case OFPTYPE_SET_ASYNC_CONFIG: case OFPTYPE_GET_ASYNC_REQUEST: case OFPTYPE_DESC_STATS_REQUEST: case OFPTYPE_FLOW_STATS_REQUEST: case OFPTYPE_AGGREGATE_STATS_REQUEST: case OFPTYPE_TABLE_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_DESC_REQUEST: case OFPTYPE_PORT_STATS_REQUEST: case OFPTYPE_QUEUE_STATS_REQUEST: case OFPTYPE_PORT_DESC_STATS_REQUEST: case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REQUEST: case OFPTYPE_METER_FEATURES_STATS_REQUEST: case OFPTYPE_GROUP_STATS_REQUEST: case OFPTYPE_GROUP_DESC_STATS_REQUEST: case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: case OFPTYPE_BUNDLE_CONTROL: case OFPTYPE_BUNDLE_ADD_MESSAGE: case OFPTYPE_HELLO: case OFPTYPE_ERROR: case OFPTYPE_FEATURES_REPLY: case OFPTYPE_GET_CONFIG_REPLY: case OFPTYPE_PACKET_IN: case OFPTYPE_FLOW_REMOVED: case OFPTYPE_PORT_STATUS: case OFPTYPE_BARRIER_REPLY: case OFPTYPE_QUEUE_GET_CONFIG_REPLY: case OFPTYPE_DESC_STATS_REPLY: case OFPTYPE_FLOW_STATS_REPLY: case OFPTYPE_QUEUE_STATS_REPLY: case OFPTYPE_PORT_STATS_REPLY: case OFPTYPE_TABLE_STATS_REPLY: case OFPTYPE_AGGREGATE_STATS_REPLY: case OFPTYPE_PORT_DESC_STATS_REPLY: case OFPTYPE_ROLE_REPLY: case OFPTYPE_FLOW_MONITOR_PAUSED: case OFPTYPE_FLOW_MONITOR_RESUMED: case OFPTYPE_FLOW_MONITOR_STATS_REPLY: case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_GROUP_STATS_REPLY: case OFPTYPE_GROUP_DESC_STATS_REPLY: case OFPTYPE_GROUP_FEATURES_STATS_REPLY: case OFPTYPE_METER_STATS_REPLY: case OFPTYPE_METER_CONFIG_STATS_REPLY: case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: case OFPTYPE_NXT_TLV_TABLE_REQUEST: case OFPTYPE_NXT_TLV_TABLE_REPLY: break; } return false; } enum ofperr ofputil_decode_bundle_add(const struct ofp_header *oh, struct ofputil_bundle_add_msg *msg, enum ofptype *type_ptr) { const struct ofp14_bundle_ctrl_msg *m; struct ofpbuf b; enum ofpraw raw; size_t inner_len; enum ofperr error; enum ofptype type; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); ovs_assert(raw == OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE); m = ofpbuf_pull(&b, sizeof *m); msg->bundle_id = ntohl(m->bundle_id); msg->flags = ntohs(m->flags); /* Pull the inner ofp_header. */ if (b.size < sizeof(struct ofp_header)) { return OFPERR_OFPBFC_MSG_BAD_LEN; } msg->msg = b.data; if (msg->msg->version != oh->version) { return OFPERR_NXBFC_BAD_VERSION; } inner_len = ntohs(msg->msg->length); if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) { return OFPERR_OFPBFC_MSG_BAD_LEN; } if (msg->msg->xid != oh->xid) { return OFPERR_OFPBFC_MSG_BAD_XID; } /* Reject unbundlable messages. */ if (!type_ptr) { type_ptr = &type; } error = ofptype_decode(type_ptr, msg->msg); if (error) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPT14_BUNDLE_ADD_MESSAGE contained " "message is unparsable (%s)", ofperr_get_name(error)); return OFPERR_OFPBFC_MSG_UNSUP; /* 'error' would be confusing. */ } if (!ofputil_is_bundlable(*type_ptr)) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s message not allowed inside " "OFPT14_BUNDLE_ADD_MESSAGE", ofptype_get_name(*type_ptr)); return OFPERR_OFPBFC_MSG_UNSUP; } return 0; } struct ofpbuf * ofputil_encode_bundle_add(enum ofp_version ofp_version, struct ofputil_bundle_add_msg *msg) { struct ofpbuf *request; struct ofp14_bundle_ctrl_msg *m; /* Must use the same xid as the embedded message. */ request = ofpraw_alloc_xid(OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version, msg->msg->xid, 0); m = ofpbuf_put_zeros(request, sizeof *m); m->bundle_id = htonl(msg->bundle_id); m->flags = htons(msg->flags); ofpbuf_put(request, msg->msg, ntohs(msg->msg->length)); return request; } static void encode_tlv_table_mappings(struct ofpbuf *b, struct ovs_list *mappings) { struct ofputil_tlv_map *map; LIST_FOR_EACH (map, list_node, mappings) { struct nx_tlv_map *nx_map; nx_map = ofpbuf_put_zeros(b, sizeof *nx_map); nx_map->option_class = htons(map->option_class); nx_map->option_type = map->option_type; nx_map->option_len = map->option_len; nx_map->index = htons(map->index); } } struct ofpbuf * ofputil_encode_tlv_table_mod(enum ofp_version ofp_version, struct ofputil_tlv_table_mod *ttm) { struct ofpbuf *b; struct nx_tlv_table_mod *nx_ttm; b = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_MOD, ofp_version, 0); nx_ttm = ofpbuf_put_zeros(b, sizeof *nx_ttm); nx_ttm->command = htons(ttm->command); encode_tlv_table_mappings(b, &ttm->mappings); return b; } static enum ofperr decode_tlv_table_mappings(struct ofpbuf *msg, unsigned int max_fields, struct ovs_list *mappings) { list_init(mappings); while (msg->size) { struct nx_tlv_map *nx_map; struct ofputil_tlv_map *map; nx_map = ofpbuf_pull(msg, sizeof *nx_map); map = xmalloc(sizeof *map); list_push_back(mappings, &map->list_node); map->option_class = ntohs(nx_map->option_class); map->option_type = nx_map->option_type; map->option_len = nx_map->option_len; if (map->option_len % 4 || map->option_len > TLV_MAX_OPT_SIZE) { VLOG_WARN_RL(&bad_ofmsg_rl, "tlv table option length (%u) is not a valid option size", map->option_len); ofputil_uninit_tlv_table(mappings); return OFPERR_NXTTMFC_BAD_OPT_LEN; } map->index = ntohs(nx_map->index); if (map->index >= max_fields) { VLOG_WARN_RL(&bad_ofmsg_rl, "tlv table field index (%u) is too large (max %u)", map->index, max_fields - 1); ofputil_uninit_tlv_table(mappings); return OFPERR_NXTTMFC_BAD_FIELD_IDX; } } return 0; } enum ofperr ofputil_decode_tlv_table_mod(const struct ofp_header *oh, struct ofputil_tlv_table_mod *ttm) { struct ofpbuf msg; struct nx_tlv_table_mod *nx_ttm; ofpbuf_use_const(&msg, oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm); ttm->command = ntohs(nx_ttm->command); if (ttm->command > NXTTMC_CLEAR) { VLOG_WARN_RL(&bad_ofmsg_rl, "tlv table mod command (%u) is out of range", ttm->command); return OFPERR_NXTTMFC_BAD_COMMAND; } return decode_tlv_table_mappings(&msg, TUN_METADATA_NUM_OPTS, &ttm->mappings); } struct ofpbuf * ofputil_encode_tlv_table_reply(const struct ofp_header *oh, struct ofputil_tlv_table_reply *ttr) { struct ofpbuf *b; struct nx_tlv_table_reply *nx_ttr; b = ofpraw_alloc_reply(OFPRAW_NXT_TLV_TABLE_REPLY, oh, 0); nx_ttr = ofpbuf_put_zeros(b, sizeof *nx_ttr); nx_ttr->max_option_space = htonl(ttr->max_option_space); nx_ttr->max_fields = htons(ttr->max_fields); encode_tlv_table_mappings(b, &ttr->mappings); return b; } /* Decodes the NXT_TLV_TABLE_REPLY message in 'oh' into '*ttr'. Returns 0 * if successful, otherwise an ofperr. * * The decoder verifies that the indexes in 'ttr->mappings' are less than * 'ttr->max_fields', but the caller must ensure, if necessary, that they are * less than TUN_METADATA_NUM_OPTS. */ enum ofperr ofputil_decode_tlv_table_reply(const struct ofp_header *oh, struct ofputil_tlv_table_reply *ttr) { struct ofpbuf msg; struct nx_tlv_table_reply *nx_ttr; ofpbuf_use_const(&msg, oh, ntohs(oh->length)); ofpraw_pull_assert(&msg); nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr); ttr->max_option_space = ntohl(nx_ttr->max_option_space); ttr->max_fields = ntohs(nx_ttr->max_fields); return decode_tlv_table_mappings(&msg, ttr->max_fields, &ttr->mappings); } void ofputil_uninit_tlv_table(struct ovs_list *mappings) { struct ofputil_tlv_map *map; LIST_FOR_EACH_POP (map, list_node, mappings) { free(map); } } /* Decodes the OpenFlow "set async config" request and "get async config * reply" message in '*oh' into an abstract form in 'master' and 'slave'. * * If 'loose' is true, this function ignores properties and values that it does * not understand, as a controller would want to do when interpreting * capabilities provided by a switch. If 'loose' is false, this function * treats unknown properties and values as an error, as a switch would want to * do when interpreting a configuration request made by a controller. * * Returns 0 if successful, otherwise an OFPERR_* value. * * Returns error code OFPERR_OFPACFC_INVALID if the value of mask is not in * the valid range of mask. * * Returns error code OFPERR_OFPACFC_UNSUPPORTED if the configuration is not * supported.*/ enum ofperr ofputil_decode_set_async_config(const struct ofp_header *oh, uint32_t master[OAM_N_TYPES], uint32_t slave[OAM_N_TYPES], bool loose) { enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT13_SET_ASYNC || raw == OFPRAW_NXT_SET_ASYNC_CONFIG || raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { const struct nx_async_config *msg = ofpmsg_body(oh); master[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[0]); master[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[0]); master[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[0]); slave[OAM_PACKET_IN] = ntohl(msg->packet_in_mask[1]); slave[OAM_PORT_STATUS] = ntohl(msg->port_status_mask[1]); slave[OAM_FLOW_REMOVED] = ntohl(msg->flow_removed_mask[1]); } else if (raw == OFPRAW_OFPT14_SET_ASYNC || raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { while (b.size > 0) { struct ofp14_async_config_prop_reasons *msg; struct ofpbuf property; enum ofperr error; uint16_t type; error = ofputil_pull_property(&b, &property, &type); if (error) { return error; } msg = property.data; if (property.size != sizeof *msg) { return OFPERR_OFPBRC_BAD_LEN; } if (!loose) { error = ofputil_check_mask(type, ntohl(msg->mask)); if (error) { return error; } } switch (type) { case OFPACPT_PACKET_IN_SLAVE: slave[OAM_PACKET_IN] = ntohl(msg->mask); break; case OFPACPT_PACKET_IN_MASTER: master[OAM_PACKET_IN] = ntohl(msg->mask); break; case OFPACPT_PORT_STATUS_SLAVE: slave[OAM_PORT_STATUS] = ntohl(msg->mask); break; case OFPACPT_PORT_STATUS_MASTER: master[OAM_PORT_STATUS] = ntohl(msg->mask); break; case OFPACPT_FLOW_REMOVED_SLAVE: slave[OAM_FLOW_REMOVED] = ntohl(msg->mask); break; case OFPACPT_FLOW_REMOVED_MASTER: master[OAM_FLOW_REMOVED] = ntohl(msg->mask); break; case OFPACPT_ROLE_STATUS_SLAVE: slave[OAM_ROLE_STATUS] = ntohl(msg->mask); break; case OFPACPT_ROLE_STATUS_MASTER: master[OAM_ROLE_STATUS] = ntohl(msg->mask); break; case OFPACPT_TABLE_STATUS_SLAVE: slave[OAM_TABLE_STATUS] = ntohl(msg->mask); break; case OFPACPT_TABLE_STATUS_MASTER: master[OAM_TABLE_STATUS] = ntohl(msg->mask); break; case OFPACPT_REQUESTFORWARD_SLAVE: slave[OAM_REQUESTFORWARD] = ntohl(msg->mask); break; case OFPACPT_REQUESTFORWARD_MASTER: master[OAM_REQUESTFORWARD] = ntohl(msg->mask); break; default: error = loose ? 0 : OFPERR_OFPACFC_UNSUPPORTED; return error; } } } else { return OFPERR_OFPBRC_BAD_VERSION; } return 0; } /* Append all asynchronous configuration properties in GET_ASYNC_REPLY * message, describing if various set of asynchronous messages are enabled * or not. */ static enum ofperr ofputil_get_async_reply(struct ofpbuf *buf, const uint32_t master_mask, const uint32_t slave_mask, const uint32_t type) { int role; for (role = 0; role < 2; role++) { struct ofp14_async_config_prop_reasons *msg; msg = ofpbuf_put_zeros(buf, sizeof *msg); switch (type) { case OAM_PACKET_IN: msg->type = (role ? htons(OFPACPT_PACKET_IN_SLAVE) : htons(OFPACPT_PACKET_IN_MASTER)); break; case OAM_PORT_STATUS: msg->type = (role ? htons(OFPACPT_PORT_STATUS_SLAVE) : htons(OFPACPT_PORT_STATUS_MASTER)); break; case OAM_FLOW_REMOVED: msg->type = (role ? htons(OFPACPT_FLOW_REMOVED_SLAVE) : htons(OFPACPT_FLOW_REMOVED_MASTER)); break; case OAM_ROLE_STATUS: msg->type = (role ? htons(OFPACPT_ROLE_STATUS_SLAVE) : htons(OFPACPT_ROLE_STATUS_MASTER)); break; case OAM_TABLE_STATUS: msg->type = (role ? htons(OFPACPT_TABLE_STATUS_SLAVE) : htons(OFPACPT_TABLE_STATUS_MASTER)); break; case OAM_REQUESTFORWARD: msg->type = (role ? htons(OFPACPT_REQUESTFORWARD_SLAVE) : htons(OFPACPT_REQUESTFORWARD_MASTER)); break; default: return OFPERR_OFPBRC_BAD_TYPE; } msg->length = htons(sizeof *msg); msg->mask = (role ? htonl(slave_mask) : htonl(master_mask)); } return 0; } /* Returns a OpenFlow message that encodes 'asynchronous configuration' properly * as a reply to get async config request. */ struct ofpbuf * ofputil_encode_get_async_config(const struct ofp_header *oh, uint32_t master[OAM_N_TYPES], uint32_t slave[OAM_N_TYPES]) { struct ofpbuf *buf; uint32_t type; buf = ofpraw_alloc_reply((oh->version < OFP14_VERSION ? OFPRAW_OFPT13_GET_ASYNC_REPLY : OFPRAW_OFPT14_GET_ASYNC_REPLY), oh, 0); if (oh->version < OFP14_VERSION) { struct nx_async_config *msg; msg = ofpbuf_put_zeros(buf, sizeof *msg); msg->packet_in_mask[0] = htonl(master[OAM_PACKET_IN]); msg->port_status_mask[0] = htonl(master[OAM_PORT_STATUS]); msg->flow_removed_mask[0] = htonl(master[OAM_FLOW_REMOVED]); msg->packet_in_mask[1] = htonl(slave[OAM_PACKET_IN]); msg->port_status_mask[1] = htonl(slave[OAM_PORT_STATUS]); msg->flow_removed_mask[1] = htonl(slave[OAM_FLOW_REMOVED]); } else if (oh->version == OFP14_VERSION) { for (type = 0; type < OAM_N_TYPES; type++) { ofputil_get_async_reply(buf, master[type], slave[type], type); } } return buf; } openvswitch-2.5.9/lib/PaxHeaders.82075/dummy.h0000644000000000000000000000013213534540071015716 xustar0030 mtime=1567801401.381681023 30 atime=1567801402.073686105 30 ctime=1567801424.701852836 openvswitch-2.5.9/lib/dummy.h0000644000175000017500000000272313534540071017410 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DUMMY_H #define DUMMY_H 1 #include /* Degree of dummy support. * * Beyond enabling support for dummies, it can be useful to replace some kinds * of bridges and netdevs, or all kinds, by dummies. This enum expresses the * degree to which this should happen. */ enum dummy_level { DUMMY_OVERRIDE_NONE, /* Support dummy but don't force its use. */ DUMMY_OVERRIDE_SYSTEM, /* Replace "system" by dummy. */ DUMMY_OVERRIDE_ALL, /* Replace all types by dummy. */ }; /* For client programs to call directly to enable dummy support. */ void dummy_enable(const char *arg); /* Implementation details. */ void dpif_dummy_register(enum dummy_level); void netdev_dummy_register(enum dummy_level); void timeval_dummy_register(void); void vlandev_dummy_enable(void); void ofpact_dummy_enable(void); #endif /* dummy.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stp.c0000644000000000000000000000013213534540071015364 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.889854223 openvswitch-2.5.9/lib/stp.c0000644000175000017500000014220213534540071017053 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Based on sample implementation in 802.1D-1998. Above copyright and license * applies to all modifications. */ #include #include "stp.h" #include #include #include #include #include #include "byte-order.h" #include "connectivity.h" #include "ofpbuf.h" #include "ovs-atomic.h" #include "dp-packet.h" #include "packets.h" #include "seq.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stp); static struct vlog_rate_limit stp_rl = VLOG_RATE_LIMIT_INIT(60, 60); #define STP_PROTOCOL_ID 0x0000 #define STP_PROTOCOL_VERSION 0x00 #define STP_TYPE_CONFIG 0x00 #define STP_TYPE_TCN 0x80 OVS_PACKED( struct stp_bpdu_header { ovs_be16 protocol_id; /* STP_PROTOCOL_ID. */ uint8_t protocol_version; /* STP_PROTOCOL_VERSION. */ uint8_t bpdu_type; /* One of STP_TYPE_*. */ }); BUILD_ASSERT_DECL(sizeof(struct stp_bpdu_header) == 4); enum stp_config_bpdu_flags { STP_CONFIG_TOPOLOGY_CHANGE_ACK = 0x80, STP_CONFIG_TOPOLOGY_CHANGE = 0x01 }; OVS_PACKED( struct stp_config_bpdu { struct stp_bpdu_header header; /* Type STP_TYPE_CONFIG. */ uint8_t flags; /* STP_CONFIG_* flags. */ ovs_be64 root_id; /* 8.5.1.1: Bridge believed to be root. */ ovs_be32 root_path_cost; /* 8.5.1.2: Cost of path to root. */ ovs_be64 bridge_id; /* 8.5.1.3: ID of transmitting bridge. */ ovs_be16 port_id; /* 8.5.1.4: Port transmitting the BPDU. */ ovs_be16 message_age; /* 8.5.1.5: Age of BPDU at tx time. */ ovs_be16 max_age; /* 8.5.1.6: Timeout for received data. */ ovs_be16 hello_time; /* 8.5.1.7: Time between BPDU generation. */ ovs_be16 forward_delay; /* 8.5.1.8: State progression delay. */ }); BUILD_ASSERT_DECL(sizeof(struct stp_config_bpdu) == 35); OVS_PACKED( struct stp_tcn_bpdu { struct stp_bpdu_header header; /* Type STP_TYPE_TCN. */ }); BUILD_ASSERT_DECL(sizeof(struct stp_tcn_bpdu) == 4); struct stp_timer { bool active; /* Timer in use? */ int value; /* Current value of timer, counting up. */ }; struct stp_port { struct stp *stp; char *port_name; /* Human-readable name for log messages. */ void *aux; /* Auxiliary data the user may retrieve. */ int port_id; /* 8.5.5.1: Unique port identifier. */ enum stp_state state; /* 8.5.5.2: Current state. */ int path_cost; /* 8.5.5.3: Cost of tx/rx on this port. */ stp_identifier designated_root; /* 8.5.5.4. */ int designated_cost; /* 8.5.5.5: Path cost to root on port. */ stp_identifier designated_bridge; /* 8.5.5.6. */ int designated_port; /* 8.5.5.7: Port to send config msgs on. */ bool topology_change_ack; /* 8.5.5.8: Flag for next config BPDU. */ bool config_pending; /* 8.5.5.9: Send BPDU when hold expires? */ bool change_detection_enabled; /* 8.5.5.10: Detect topology changes? */ struct stp_timer message_age_timer; /* 8.5.6.1: Age of received info. */ struct stp_timer forward_delay_timer; /* 8.5.6.2: State change timer. */ struct stp_timer hold_timer; /* 8.5.6.3: BPDU rate limit timer. */ int tx_count; /* Number of BPDUs transmitted. */ int rx_count; /* Number of valid BPDUs received. */ int error_count; /* Number of bad BPDUs received. */ bool state_changed; }; struct stp { struct ovs_list node; /* Node in all_stps list. */ /* Static bridge data. */ char *name; /* Human-readable name for log messages. */ stp_identifier bridge_id; /* 8.5.3.7: This bridge. */ int max_age; /* 8.5.3.4: Time to drop received data. */ int hello_time; /* 8.5.3.5: Time between sending BPDUs. */ int forward_delay; /* 8.5.3.6: Delay between state changes. */ int bridge_max_age; /* 8.5.3.8: max_age when we're root. */ int bridge_hello_time; /* 8.5.3.9: hello_time as root. */ int bridge_forward_delay; /* 8.5.3.10: forward_delay as root. */ int rq_max_age; /* User-requested max age, in ms. */ int rq_hello_time; /* User-requested hello time, in ms. */ int rq_forward_delay; /* User-requested forward delay, in ms. */ int elapsed_remainder; /* Left-over msecs from last stp_tick(). */ /* Dynamic bridge data. */ stp_identifier designated_root; /* 8.5.3.1: Bridge believed to be root. */ unsigned int root_path_cost; /* 8.5.3.2: Cost of path to root. */ struct stp_port *root_port; /* 8.5.3.3: Lowest cost port to root. */ bool topology_change_detected; /* 8.5.3.11: Detected a topology change? */ bool topology_change; /* 8.5.3.12: Received topology change? */ /* Bridge timers. */ struct stp_timer hello_timer; /* 8.5.4.1: Hello timer. */ struct stp_timer tcn_timer; /* 8.5.4.2: Topology change timer. */ struct stp_timer topology_change_timer; /* 8.5.4.3. */ /* Ports. */ struct stp_port ports[STP_MAX_PORTS]; /* Interface to client. */ bool fdb_needs_flush; /* MAC learning tables needs flushing. */ struct stp_port *first_changed_port; void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux); void *aux; struct ovs_refcount ref_cnt; }; static struct ovs_mutex mutex; static struct ovs_list all_stps__ = OVS_LIST_INITIALIZER(&all_stps__); static struct ovs_list *const all_stps OVS_GUARDED_BY(mutex) = &all_stps__; #define FOR_EACH_ENABLED_PORT(PORT, STP) \ for ((PORT) = stp_next_enabled_port((STP), (STP)->ports); \ (PORT); \ (PORT) = stp_next_enabled_port((STP), (PORT) + 1)) static struct stp_port * stp_next_enabled_port(const struct stp *stp, const struct stp_port *port) OVS_REQUIRES(mutex) { for (; port < &stp->ports[ARRAY_SIZE(stp->ports)]; port++) { if (port->state != STP_DISABLED) { return CONST_CAST(struct stp_port *, port); } } return NULL; } #define MESSAGE_AGE_INCREMENT 1 static void stp_transmit_config(struct stp_port *) OVS_REQUIRES(mutex); static bool stp_supersedes_port_info(const struct stp_port *, const struct stp_config_bpdu *) OVS_REQUIRES(mutex); static void stp_record_config_information(struct stp_port *, const struct stp_config_bpdu *) OVS_REQUIRES(mutex); static void stp_record_config_timeout_values(struct stp *, const struct stp_config_bpdu *) OVS_REQUIRES(mutex); static bool stp_is_designated_port(const struct stp_port *) OVS_REQUIRES(mutex); static void stp_config_bpdu_generation(struct stp *) OVS_REQUIRES(mutex); static void stp_transmit_tcn(struct stp *) OVS_REQUIRES(mutex); static void stp_configuration_update(struct stp *) OVS_REQUIRES(mutex); static bool stp_supersedes_root(const struct stp_port *root, const struct stp_port *) OVS_REQUIRES(mutex); static void stp_root_selection(struct stp *) OVS_REQUIRES(mutex); static void stp_designated_port_selection(struct stp *) OVS_REQUIRES(mutex); static void stp_become_designated_port(struct stp_port *) OVS_REQUIRES(mutex); static void stp_port_state_selection(struct stp *) OVS_REQUIRES(mutex); static void stp_make_forwarding(struct stp_port *) OVS_REQUIRES(mutex); static void stp_make_blocking(struct stp_port *) OVS_REQUIRES(mutex); static void stp_set_port_state(struct stp_port *, enum stp_state) OVS_REQUIRES(mutex); static void stp_topology_change_detection(struct stp *) OVS_REQUIRES(mutex); static void stp_topology_change_acknowledged(struct stp *) OVS_REQUIRES(mutex); static void stp_acknowledge_topology_change(struct stp_port *) OVS_REQUIRES(mutex); static void stp_received_config_bpdu(struct stp *, struct stp_port *, const struct stp_config_bpdu *) OVS_REQUIRES(mutex); static void stp_received_tcn_bpdu(struct stp *, struct stp_port *) OVS_REQUIRES(mutex); static void stp_hello_timer_expiry(struct stp *) OVS_REQUIRES(mutex); static void stp_message_age_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex); static bool stp_is_designated_for_some_port(const struct stp *) OVS_REQUIRES(mutex); static void stp_forward_delay_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex); static void stp_tcn_timer_expiry(struct stp *) OVS_REQUIRES(mutex); static void stp_topology_change_timer_expiry(struct stp *) OVS_REQUIRES(mutex); static void stp_hold_timer_expiry(struct stp_port *) OVS_REQUIRES(mutex); static void stp_initialize_port(struct stp_port *, enum stp_state) OVS_REQUIRES(mutex); static void stp_become_root_bridge(struct stp *) OVS_REQUIRES(mutex); static void stp_update_bridge_timers(struct stp *) OVS_REQUIRES(mutex); static int clamp(int x, int min, int max); static int ms_to_timer(int ms); static int timer_to_ms(int timer); static void stp_start_timer(struct stp_timer *, int value); static void stp_stop_timer(struct stp_timer *); static bool stp_timer_expired(struct stp_timer *, int elapsed, int timeout); static void stp_send_bpdu(struct stp_port *, const void *, size_t) OVS_REQUIRES(mutex); static void stp_unixctl_tcn(struct unixctl_conn *, int argc, const char *argv[], void *aux); void stp_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { /* We need a recursive mutex because stp_send_bpdu() could loop back * into the stp module through a patch port. This happens * intentionally as part of the unit tests. Ideally we'd ditch * the call back function, but for now this is what we have. */ ovs_mutex_init_recursive(&mutex); unixctl_command_register("stp/tcn", "[bridge]", 0, 1, stp_unixctl_tcn, NULL); ovsthread_once_done(&once); } } /* Creates and returns a new STP instance that initially has no ports enabled. * * 'bridge_id' should be a 48-bit MAC address as returned by * eth_addr_to_uint64(). 'bridge_id' may also have a priority value in its top * 16 bits; if those bits are set to 0, STP_DEFAULT_BRIDGE_PRIORITY is used. * (This priority may be changed with stp_set_bridge_priority().) * * When the bridge needs to send out a BPDU, it calls 'send_bpdu'. This * callback may be called from stp_tick() or stp_received_bpdu(). The * arguments to 'send_bpdu' are an STP BPDU encapsulated in 'bpdu', * the spanning tree port number 'port_no' that should transmit the * packet, and auxiliary data to be passed to the callback in 'aux'. */ struct stp * stp_create(const char *name, stp_identifier bridge_id, void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux), void *aux) { struct stp *stp; struct stp_port *p; stp_init(); ovs_mutex_lock(&mutex); stp = xzalloc(sizeof *stp); stp->name = xstrdup(name); stp->bridge_id = bridge_id; if (!(stp->bridge_id >> 48)) { stp->bridge_id |= (uint64_t) STP_DEFAULT_BRIDGE_PRIORITY << 48; } stp->rq_max_age = STP_DEFAULT_MAX_AGE; stp->rq_hello_time = STP_DEFAULT_HELLO_TIME; stp->rq_forward_delay = STP_DEFAULT_FWD_DELAY; stp_update_bridge_timers(stp); stp->max_age = stp->bridge_max_age; stp->hello_time = stp->bridge_hello_time; stp->forward_delay = stp->bridge_forward_delay; stp->designated_root = stp->bridge_id; stp->root_path_cost = 0; stp->root_port = NULL; stp->topology_change_detected = false; stp->topology_change = false; stp_stop_timer(&stp->tcn_timer); stp_stop_timer(&stp->topology_change_timer); stp_start_timer(&stp->hello_timer, 0); stp->send_bpdu = send_bpdu; stp->aux = aux; stp->first_changed_port = &stp->ports[ARRAY_SIZE(stp->ports)]; for (p = stp->ports; p < &stp->ports[ARRAY_SIZE(stp->ports)]; p++) { p->stp = stp; p->port_id = (stp_port_no(p) + 1) | (STP_DEFAULT_PORT_PRIORITY << 8); p->path_cost = 19; /* Recommended default for 100 Mb/s link. */ stp_initialize_port(p, STP_DISABLED); } ovs_refcount_init(&stp->ref_cnt); list_push_back(all_stps, &stp->node); ovs_mutex_unlock(&mutex); return stp; } struct stp * stp_ref(const struct stp *stp_) { struct stp *stp = CONST_CAST(struct stp *, stp_); if (stp) { ovs_refcount_ref(&stp->ref_cnt); } return stp; } /* Destroys 'stp'. */ void stp_unref(struct stp *stp) { if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) { size_t i; ovs_mutex_lock(&mutex); list_remove(&stp->node); ovs_mutex_unlock(&mutex); free(stp->name); for (i = 0; i < STP_MAX_PORTS; i++) { free(stp->ports[i].port_name); } free(stp); } } /* Runs 'stp' given that 'ms' milliseconds have passed. */ void stp_tick(struct stp *stp, int ms) { struct stp_port *p; int elapsed; ovs_mutex_lock(&mutex); /* Convert 'ms' to STP timer ticks. Preserve any leftover milliseconds * from previous stp_tick() calls so that we don't lose STP ticks when we * are called too frequently. */ ms = clamp(ms, 0, INT_MAX - 1000) + stp->elapsed_remainder; elapsed = ms_to_timer(ms); stp->elapsed_remainder = ms - timer_to_ms(elapsed); if (!elapsed) { goto out; } if (stp_timer_expired(&stp->hello_timer, elapsed, stp->hello_time)) { stp_hello_timer_expiry(stp); } if (stp_timer_expired(&stp->tcn_timer, elapsed, stp->bridge_hello_time)) { stp_tcn_timer_expiry(stp); } if (stp_timer_expired(&stp->topology_change_timer, elapsed, stp->max_age + stp->forward_delay)) { stp_topology_change_timer_expiry(stp); } FOR_EACH_ENABLED_PORT (p, stp) { if (stp_timer_expired(&p->message_age_timer, elapsed, stp->max_age)) { stp_message_age_timer_expiry(p); } } FOR_EACH_ENABLED_PORT (p, stp) { if (stp_timer_expired(&p->forward_delay_timer, elapsed, stp->forward_delay)) { stp_forward_delay_timer_expiry(p); } if (stp_timer_expired(&p->hold_timer, elapsed, ms_to_timer(1000))) { stp_hold_timer_expiry(p); } } out: ovs_mutex_unlock(&mutex); } static void set_bridge_id(struct stp *stp, stp_identifier new_bridge_id) OVS_REQUIRES(mutex) { if (new_bridge_id != stp->bridge_id) { bool root; struct stp_port *p; root = stp_is_root_bridge(stp); FOR_EACH_ENABLED_PORT (p, stp) { if (stp_is_designated_port(p)) { p->designated_bridge = new_bridge_id; } } stp->bridge_id = new_bridge_id; stp_configuration_update(stp); stp_port_state_selection(stp); if (stp_is_root_bridge(stp) && !root) { stp_become_root_bridge(stp); } } } void stp_set_bridge_id(struct stp *stp, stp_identifier bridge_id) { const uint64_t mac_bits = (UINT64_C(1) << 48) - 1; const uint64_t pri_bits = ~mac_bits; ovs_mutex_lock(&mutex); set_bridge_id(stp, (stp->bridge_id & pri_bits) | (bridge_id & mac_bits)); ovs_mutex_unlock(&mutex); } void stp_set_bridge_priority(struct stp *stp, uint16_t new_priority) { const uint64_t mac_bits = (UINT64_C(1) << 48) - 1; ovs_mutex_lock(&mutex); set_bridge_id(stp, ((stp->bridge_id & mac_bits) | ((uint64_t) new_priority << 48))); ovs_mutex_unlock(&mutex); } /* Sets the desired hello time for 'stp' to 'ms', in milliseconds. The actual * hello time is clamped to the range of 1 to 10 seconds and subject to the * relationship (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge * hello time is only used when 'stp' is the root bridge. */ void stp_set_hello_time(struct stp *stp, int ms) { ovs_mutex_lock(&mutex); stp->rq_hello_time = ms; stp_update_bridge_timers(stp); ovs_mutex_unlock(&mutex); } /* Sets the desired max age for 'stp' to 'ms', in milliseconds. The actual max * age is clamped to the range of 6 to 40 seconds and subject to the * relationships (2 * (bridge_forward_delay - 1 s) >= bridge_max_age) and * (bridge_max_age >= 2 * (bridge_hello_time + 1 s)). The bridge max age is * only used when 'stp' is the root bridge. */ void stp_set_max_age(struct stp *stp, int ms) { ovs_mutex_lock(&mutex); stp->rq_max_age = ms; stp_update_bridge_timers(stp); ovs_mutex_unlock(&mutex); } /* Sets the desired forward delay for 'stp' to 'ms', in milliseconds. The * actual forward delay is clamped to the range of 4 to 30 seconds and subject * to the relationship (2 * (bridge_forward_delay - 1 s) >= bridge_max_age). * The bridge forward delay is only used when 'stp' is the root bridge. */ void stp_set_forward_delay(struct stp *stp, int ms) { ovs_mutex_lock(&mutex); stp->rq_forward_delay = ms; stp_update_bridge_timers(stp); ovs_mutex_unlock(&mutex); } /* Returns the name given to 'stp' in the call to stp_create(). */ const char * stp_get_name(const struct stp *stp) { char *name; ovs_mutex_lock(&mutex); name = stp->name; ovs_mutex_unlock(&mutex); return name; } /* Returns the bridge ID for 'stp'. */ stp_identifier stp_get_bridge_id(const struct stp *stp) { stp_identifier bridge_id; ovs_mutex_lock(&mutex); bridge_id = stp->bridge_id; ovs_mutex_unlock(&mutex); return bridge_id; } /* Returns the bridge ID of the bridge currently believed to be the root. */ stp_identifier stp_get_designated_root(const struct stp *stp) { stp_identifier designated_root; ovs_mutex_lock(&mutex); designated_root = stp->designated_root; ovs_mutex_unlock(&mutex); return designated_root; } /* Returns true if 'stp' believes itself to the be root of the spanning tree, * false otherwise. */ bool stp_is_root_bridge(const struct stp *stp) { bool is_root; ovs_mutex_lock(&mutex); is_root = stp->bridge_id == stp->designated_root; ovs_mutex_unlock(&mutex); return is_root; } /* Returns the cost of the path from 'stp' to the root of the spanning tree. */ int stp_get_root_path_cost(const struct stp *stp) { int cost; ovs_mutex_lock(&mutex); cost = stp->root_path_cost; ovs_mutex_unlock(&mutex); return cost; } /* Returns the bridge hello time, in ms. The returned value is not necessarily * the value passed to stp_set_hello_time(): it is clamped to the valid range * and quantized to the STP timer resolution. */ int stp_get_hello_time(const struct stp *stp) { int time; ovs_mutex_lock(&mutex); time = timer_to_ms(stp->bridge_hello_time); ovs_mutex_unlock(&mutex); return time; } /* Returns the bridge max age, in ms. The returned value is not necessarily * the value passed to stp_set_max_age(): it is clamped to the valid range, * quantized to the STP timer resolution, and adjusted to match the constraints * due to the hello time. */ int stp_get_max_age(const struct stp *stp) { int time; ovs_mutex_lock(&mutex); time = timer_to_ms(stp->bridge_max_age); ovs_mutex_unlock(&mutex); return time; } /* Returns the bridge forward delay, in ms. The returned value is not * necessarily the value passed to stp_set_forward_delay(): it is clamped to * the valid range, quantized to the STP timer resolution, and adjusted to * match the constraints due to the forward delay. */ int stp_get_forward_delay(const struct stp *stp) { int time; ovs_mutex_lock(&mutex); time = timer_to_ms(stp->bridge_forward_delay); ovs_mutex_unlock(&mutex); return time; } /* Returns true if something has happened to 'stp' which necessitates flushing * the client's MAC learning table. Calling this function resets 'stp' so that * future calls will return false until flushing is required again. */ bool stp_check_and_reset_fdb_flush(struct stp *stp) { bool needs_flush; ovs_mutex_lock(&mutex); needs_flush = stp->fdb_needs_flush; stp->fdb_needs_flush = false; ovs_mutex_unlock(&mutex); return needs_flush; } /* Returns the port in 'stp' with index 'port_no', which must be between 0 and * STP_MAX_PORTS. */ struct stp_port * stp_get_port(struct stp *stp, int port_no) { struct stp_port *port; ovs_mutex_lock(&mutex); ovs_assert(port_no >= 0 && port_no < ARRAY_SIZE(stp->ports)); port = &stp->ports[port_no]; ovs_mutex_unlock(&mutex); return port; } /* Returns the port connecting 'stp' to the root bridge, or a null pointer if * there is no such port. */ struct stp_port * stp_get_root_port(struct stp *stp) { struct stp_port *port; ovs_mutex_lock(&mutex); port = stp->root_port; ovs_mutex_unlock(&mutex); return port; } /* Finds a port whose state has changed. If successful, stores the port whose * state changed in '*portp' and returns true. If no port has changed, stores * NULL in '*portp' and returns false. */ bool stp_get_changed_port(struct stp *stp, struct stp_port **portp) { struct stp_port *end, *p; bool changed = false; ovs_mutex_lock(&mutex); end = &stp->ports[ARRAY_SIZE(stp->ports)]; for (p = stp->first_changed_port; p < end; p++) { if (p->state_changed) { p->state_changed = false; stp->first_changed_port = p + 1; *portp = p; changed = true; goto out; } } stp->first_changed_port = end; *portp = NULL; out: ovs_mutex_unlock(&mutex); return changed; } /* Returns the name for the given 'state' (for use in debugging and log * messages). */ const char * stp_state_name(enum stp_state state) { switch (state) { case STP_DISABLED: return "disabled"; case STP_LISTENING: return "listening"; case STP_LEARNING: return "learning"; case STP_FORWARDING: return "forwarding"; case STP_BLOCKING: return "blocking"; default: OVS_NOT_REACHED(); } } /* Returns true if 'state' is one in which packets received on a port should * be forwarded, false otherwise. */ bool stp_forward_in_state(enum stp_state state) { return (state & STP_FORWARDING) != 0; } /* Returns true if 'state' is one in which MAC learning should be done on * packets received on a port, false otherwise. */ bool stp_learn_in_state(enum stp_state state) { return (state & (STP_LEARNING | STP_FORWARDING)) != 0; } /* Returns true if 'state' is one in which bpdus should be forwarded on a * port, false otherwise. * * Returns true if 'state' is STP_DISABLED, since in that case the port does * not generate the bpdu and should just forward it (e.g. patch port on pif * bridge). */ bool stp_should_forward_bpdu(enum stp_state state) { return (state & ( STP_DISABLED | STP_LISTENING | STP_LEARNING | STP_FORWARDING)) != 0; } /* Returns the name for the given 'role' (for use in debugging and log * messages). */ const char * stp_role_name(enum stp_role role) { switch (role) { case STP_ROLE_ROOT: return "root"; case STP_ROLE_DESIGNATED: return "designated"; case STP_ROLE_ALTERNATE: return "alternate"; case STP_ROLE_DISABLED: return "disabled"; default: OVS_NOT_REACHED(); } } /* Notifies the STP entity that bridge protocol data unit 'bpdu', which is * 'bpdu_size' bytes in length, was received on port 'p'. * * This function may call the 'send_bpdu' function provided to stp_create(). */ void stp_received_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size) { struct stp *stp = p->stp; const struct stp_bpdu_header *header; ovs_mutex_lock(&mutex); if (p->state == STP_DISABLED) { goto out; } if (bpdu_size < sizeof(struct stp_bpdu_header)) { VLOG_WARN("%s: received runt %"PRIuSIZE"-byte BPDU", stp->name, bpdu_size); p->error_count++; goto out; } header = bpdu; if (header->protocol_id != htons(STP_PROTOCOL_ID)) { VLOG_WARN("%s: received BPDU with unexpected protocol ID %"PRIu16, stp->name, ntohs(header->protocol_id)); p->error_count++; goto out; } if (header->protocol_version != STP_PROTOCOL_VERSION) { VLOG_DBG("%s: received BPDU with unexpected protocol version %"PRIu8, stp->name, header->protocol_version); } switch (header->bpdu_type) { case STP_TYPE_CONFIG: if (bpdu_size < sizeof(struct stp_config_bpdu)) { VLOG_WARN("%s: received config BPDU with invalid size %"PRIuSIZE, stp->name, bpdu_size); p->error_count++; goto out; } stp_received_config_bpdu(stp, p, bpdu); break; case STP_TYPE_TCN: if (bpdu_size != sizeof(struct stp_tcn_bpdu)) { VLOG_WARN("%s: received TCN BPDU with invalid size %"PRIuSIZE, stp->name, bpdu_size); p->error_count++; goto out; } stp_received_tcn_bpdu(stp, p); break; default: VLOG_WARN("%s: received BPDU of unexpected type %"PRIu8, stp->name, header->bpdu_type); p->error_count++; goto out; } p->rx_count++; out: ovs_mutex_unlock(&mutex); } /* Returns the STP entity in which 'p' is nested. */ struct stp * stp_port_get_stp(struct stp_port *p) { struct stp *stp; ovs_mutex_lock(&mutex); stp = p->stp; ovs_mutex_unlock(&mutex); return stp; } void stp_port_set_name(struct stp_port *p, const char *name) { char *old; ovs_mutex_lock(&mutex); old = p->port_name; p->port_name = xstrdup(name); free(old); ovs_mutex_unlock(&mutex); } /* Sets the 'aux' member of 'p'. * * The 'aux' member will be reset to NULL when stp_port_disable() is * called or stp_port_enable() is called when the port is in a Disabled * state. */ void stp_port_set_aux(struct stp_port *p, void *aux) { ovs_mutex_lock(&mutex); p->aux = aux; ovs_mutex_unlock(&mutex); } /* Returns the 'aux' member of 'p'. */ void * stp_port_get_aux(struct stp_port *p) { void *aux; ovs_mutex_lock(&mutex); aux = p->aux; ovs_mutex_unlock(&mutex); return aux; } /* Returns the index of port 'p' within its bridge. */ int stp_port_no(const struct stp_port *p) { struct stp *stp; int index; ovs_mutex_lock(&mutex); stp = p->stp; ovs_assert(p >= stp->ports && p < &stp->ports[ARRAY_SIZE(stp->ports)]); index = p - p->stp->ports; ovs_mutex_unlock(&mutex); return index; } /* Returns the port ID for 'p'. */ int stp_port_get_id(const struct stp_port *p) { int port_id; ovs_mutex_lock(&mutex); port_id = p->port_id; ovs_mutex_unlock(&mutex); return port_id; } /* Returns the state of port 'p'. */ enum stp_state stp_port_get_state(const struct stp_port *p) { enum stp_state state; ovs_mutex_lock(&mutex); state = p->state; ovs_mutex_unlock(&mutex); return state; } /* Returns the role of port 'p'. */ enum stp_role stp_port_get_role(const struct stp_port *p) { struct stp_port *root_port; enum stp_role role; ovs_mutex_lock(&mutex); root_port = p->stp->root_port; if (root_port && root_port->port_id == p->port_id) { role = STP_ROLE_ROOT; } else if (stp_is_designated_port(p)) { role = STP_ROLE_DESIGNATED; } else if (p->state == STP_DISABLED) { role = STP_ROLE_DISABLED; } else { role = STP_ROLE_ALTERNATE; } ovs_mutex_unlock(&mutex); return role; } /* Retrieves BPDU transmit and receive counts for 'p'. */ void stp_port_get_counts(const struct stp_port *p, int *tx_count, int *rx_count, int *error_count) { ovs_mutex_lock(&mutex); *tx_count = p->tx_count; *rx_count = p->rx_count; *error_count = p->error_count; ovs_mutex_unlock(&mutex); } /* Disables STP on port 'p'. */ void stp_port_disable(struct stp_port *p) { struct stp *stp; ovs_mutex_lock(&mutex); stp = p->stp; if (p->state != STP_DISABLED) { bool root = stp_is_root_bridge(stp); stp_become_designated_port(p); stp_set_port_state(p, STP_DISABLED); p->topology_change_ack = false; p->config_pending = false; stp_stop_timer(&p->message_age_timer); stp_stop_timer(&p->forward_delay_timer); stp_configuration_update(stp); stp_port_state_selection(stp); if (stp_is_root_bridge(stp) && !root) { stp_become_root_bridge(stp); } p->aux = NULL; } ovs_mutex_unlock(&mutex); } /* Enables STP on port 'p'. The port will initially be in "blocking" state. */ void stp_port_enable(struct stp_port *p) { ovs_mutex_lock(&mutex); if (p->state == STP_DISABLED) { stp_initialize_port(p, STP_BLOCKING); stp_port_state_selection(p->stp); } ovs_mutex_unlock(&mutex); } /* Sets the priority of port 'p' to 'new_priority'. Lower numerical values * are interpreted as higher priorities. */ void stp_port_set_priority(struct stp_port *p, uint8_t new_priority) { uint16_t new_port_id; ovs_mutex_lock(&mutex); new_port_id = (p->port_id & 0xff) | (new_priority << 8); if (p->port_id != new_port_id) { struct stp *stp = p->stp; if (stp_is_designated_port(p)) { p->designated_port = new_port_id; } p->port_id = new_port_id; if (stp->bridge_id == p->designated_bridge && p->port_id < p->designated_port) { stp_become_designated_port(p); stp_port_state_selection(stp); } } ovs_mutex_unlock(&mutex); } /* Convert 'speed' (measured in Mb/s) into the path cost. */ uint16_t stp_convert_speed_to_cost(unsigned int speed) { uint16_t ret; ovs_mutex_lock(&mutex); ret = speed >= 10000 ? 2 /* 10 Gb/s. */ : speed >= 1000 ? 4 /* 1 Gb/s. */ : speed >= 100 ? 19 /* 100 Mb/s. */ : speed >= 16 ? 62 /* 16 Mb/s. */ : speed >= 10 ? 100 /* 10 Mb/s. */ : speed >= 4 ? 250 /* 4 Mb/s. */ : 19; /* 100 Mb/s (guess). */ ovs_mutex_unlock(&mutex); return ret; } /* Sets the path cost of port 'p' to 'path_cost'. Lower values are generally * used to indicate faster links. Use stp_port_set_speed() to automatically * generate a default path cost from a link speed. */ void stp_port_set_path_cost(struct stp_port *p, uint16_t path_cost) { ovs_mutex_lock(&mutex); if (p->path_cost != path_cost) { struct stp *stp = p->stp; p->path_cost = path_cost; stp_configuration_update(stp); stp_port_state_selection(stp); } ovs_mutex_unlock(&mutex); } /* Sets the path cost of port 'p' based on 'speed' (measured in Mb/s). */ void stp_port_set_speed(struct stp_port *p, unsigned int speed) { stp_port_set_path_cost(p, stp_convert_speed_to_cost(speed)); } /* Enables topology change detection on port 'p'. */ void stp_port_enable_change_detection(struct stp_port *p) { p->change_detection_enabled = true; } /* Disables topology change detection on port 'p'. */ void stp_port_disable_change_detection(struct stp_port *p) { p->change_detection_enabled = false; } static void stp_transmit_config(struct stp_port *p) OVS_REQUIRES(mutex) { struct stp *stp = p->stp; bool root = stp_is_root_bridge(stp); if (!root && !stp->root_port) { return; } if (p->hold_timer.active) { VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu pending", stp->name, p->port_name); p->config_pending = true; } else { struct stp_config_bpdu config; memset(&config, 0, sizeof config); config.header.protocol_id = htons(STP_PROTOCOL_ID); config.header.protocol_version = STP_PROTOCOL_VERSION; config.header.bpdu_type = STP_TYPE_CONFIG; config.flags = 0; if (p->topology_change_ack) { config.flags |= STP_CONFIG_TOPOLOGY_CHANGE_ACK; } if (stp->topology_change) { config.flags |= STP_CONFIG_TOPOLOGY_CHANGE; } config.root_id = htonll(stp->designated_root); config.root_path_cost = htonl(stp->root_path_cost); config.bridge_id = htonll(stp->bridge_id); config.port_id = htons(p->port_id); if (root) { config.message_age = htons(0); } else { config.message_age = htons(stp->root_port->message_age_timer.value + MESSAGE_AGE_INCREMENT); } config.max_age = htons(stp->max_age); config.hello_time = htons(stp->hello_time); config.forward_delay = htons(stp->forward_delay); if (ntohs(config.message_age) < stp->max_age) { p->topology_change_ack = false; p->config_pending = false; VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, transmit config bpdu", stp->name, p->port_name); stp_send_bpdu(p, &config, sizeof config); stp_start_timer(&p->hold_timer, 0); } } } static bool stp_supersedes_port_info(const struct stp_port *p, const struct stp_config_bpdu *config) OVS_REQUIRES(mutex) { if (ntohll(config->root_id) != p->designated_root) { return ntohll(config->root_id) < p->designated_root; } else if (ntohl(config->root_path_cost) != p->designated_cost) { return ntohl(config->root_path_cost) < p->designated_cost; } else if (ntohll(config->bridge_id) != p->designated_bridge) { return ntohll(config->bridge_id) < p->designated_bridge; } else { return (ntohll(config->bridge_id) != p->stp->bridge_id || ntohs(config->port_id) <= p->designated_port); } } static void stp_record_config_information(struct stp_port *p, const struct stp_config_bpdu *config) OVS_REQUIRES(mutex) { p->designated_root = ntohll(config->root_id); p->designated_cost = ntohl(config->root_path_cost); p->designated_bridge = ntohll(config->bridge_id); p->designated_port = ntohs(config->port_id); stp_start_timer(&p->message_age_timer, ntohs(config->message_age)); } static void stp_record_config_timeout_values(struct stp *stp, const struct stp_config_bpdu *config) OVS_REQUIRES(mutex) { stp->max_age = ntohs(config->max_age); stp->hello_time = ntohs(config->hello_time); stp->forward_delay = ntohs(config->forward_delay); stp->topology_change = config->flags & STP_CONFIG_TOPOLOGY_CHANGE; } static bool stp_is_designated_port(const struct stp_port *p) OVS_REQUIRES(mutex) { return (p->designated_bridge == p->stp->bridge_id && p->designated_port == p->port_id); } static void stp_config_bpdu_generation(struct stp *stp) OVS_REQUIRES(mutex) { struct stp_port *p; FOR_EACH_ENABLED_PORT (p, stp) { if (stp_is_designated_port(p)) { stp_transmit_config(p); } } } static void stp_transmit_tcn(struct stp *stp) OVS_REQUIRES(mutex) { struct stp_port *p = stp->root_port; struct stp_tcn_bpdu tcn_bpdu; if (!p) { return; } VLOG_DBG_RL(&stp_rl, "bridge: %s, root port: %s, transmit tcn", stp->name, p->port_name); tcn_bpdu.header.protocol_id = htons(STP_PROTOCOL_ID); tcn_bpdu.header.protocol_version = STP_PROTOCOL_VERSION; tcn_bpdu.header.bpdu_type = STP_TYPE_TCN; stp_send_bpdu(p, &tcn_bpdu, sizeof tcn_bpdu); } static void stp_configuration_update(struct stp *stp) OVS_REQUIRES(mutex) { stp_root_selection(stp); stp_designated_port_selection(stp); seq_change(connectivity_seq_get()); } static bool stp_supersedes_root(const struct stp_port *root, const struct stp_port *p) OVS_REQUIRES(mutex) { int p_cost = p->designated_cost + p->path_cost; int root_cost = root->designated_cost + root->path_cost; if (p->designated_root != root->designated_root) { return p->designated_root < root->designated_root; } else if (p_cost != root_cost) { return p_cost < root_cost; } else if (p->designated_bridge != root->designated_bridge) { return p->designated_bridge < root->designated_bridge; } else if (p->designated_port != root->designated_port) { return p->designated_port < root->designated_port; } else { return p->port_id < root->port_id; } } static void stp_root_selection(struct stp *stp) OVS_REQUIRES(mutex) { struct stp_port *p, *root; root = NULL; FOR_EACH_ENABLED_PORT (p, stp) { if (stp_is_designated_port(p) || p->designated_root >= stp->bridge_id) { continue; } if (root && !stp_supersedes_root(root, p)) { continue; } root = p; } stp->root_port = root; if (!root) { stp->designated_root = stp->bridge_id; stp->root_path_cost = 0; } else { stp->designated_root = root->designated_root; stp->root_path_cost = root->designated_cost + root->path_cost; } } static void stp_designated_port_selection(struct stp *stp) OVS_REQUIRES(mutex) { struct stp_port *p; FOR_EACH_ENABLED_PORT (p, stp) { if (stp_is_designated_port(p) || p->designated_root != stp->designated_root || stp->root_path_cost < p->designated_cost || (stp->root_path_cost == p->designated_cost && (stp->bridge_id < p->designated_bridge || (stp->bridge_id == p->designated_bridge && p->port_id <= p->designated_port)))) { stp_become_designated_port(p); } } } static void stp_become_designated_port(struct stp_port *p) OVS_REQUIRES(mutex) { struct stp *stp = p->stp; p->designated_root = stp->designated_root; p->designated_cost = stp->root_path_cost; p->designated_bridge = stp->bridge_id; p->designated_port = p->port_id; } static void stp_port_state_selection(struct stp *stp) OVS_REQUIRES(mutex) { struct stp_port *p; FOR_EACH_ENABLED_PORT (p, stp) { if (p == stp->root_port) { p->config_pending = false; p->topology_change_ack = false; stp_make_forwarding(p); } else if (stp_is_designated_port(p)) { stp_stop_timer(&p->message_age_timer); stp_make_forwarding(p); } else { p->config_pending = false; p->topology_change_ack = false; stp_make_blocking(p); } } } static void stp_make_forwarding(struct stp_port *p) OVS_REQUIRES(mutex) { if (p->state == STP_BLOCKING) { stp_set_port_state(p, STP_LISTENING); stp_start_timer(&p->forward_delay_timer, 0); } } static void stp_make_blocking(struct stp_port *p) OVS_REQUIRES(mutex) { if (!(p->state & (STP_DISABLED | STP_BLOCKING))) { if (p->state & (STP_FORWARDING | STP_LEARNING)) { if (p->change_detection_enabled) { stp_topology_change_detection(p->stp); } } stp_set_port_state(p, STP_BLOCKING); stp_stop_timer(&p->forward_delay_timer); } } static void stp_set_port_state(struct stp_port *p, enum stp_state state) OVS_REQUIRES(mutex) { if (state != p->state && !p->state_changed) { p->state_changed = true; if (p < p->stp->first_changed_port) { p->stp->first_changed_port = p; } seq_change(connectivity_seq_get()); } p->state = state; } static void stp_topology_change_detection(struct stp *stp) OVS_REQUIRES(mutex) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (stp_is_root_bridge(stp)) { stp->topology_change = true; stp_start_timer(&stp->topology_change_timer, 0); } else if (!stp->topology_change_detected) { stp_transmit_tcn(stp); stp_start_timer(&stp->tcn_timer, 0); } stp->fdb_needs_flush = true; stp->topology_change_detected = true; seq_change(connectivity_seq_get()); VLOG_INFO_RL(&rl, "%s: detected topology change.", stp->name); } static void stp_topology_change_acknowledged(struct stp *stp) OVS_REQUIRES(mutex) { stp->topology_change_detected = false; stp_stop_timer(&stp->tcn_timer); } static void stp_acknowledge_topology_change(struct stp_port *p) OVS_REQUIRES(mutex) { p->topology_change_ack = true; stp_transmit_config(p); } static void stp_received_config_bpdu(struct stp *stp, struct stp_port *p, const struct stp_config_bpdu *config) OVS_REQUIRES(mutex) { if (ntohs(config->message_age) >= ntohs(config->max_age)) { VLOG_WARN("%s: received config BPDU with message age (%u) greater " "than max age (%u)", stp->name, ntohs(config->message_age), ntohs(config->max_age)); return; } if (p->state != STP_DISABLED) { bool root = stp_is_root_bridge(stp); if (stp_supersedes_port_info(p, config)) { stp_record_config_information(p, config); stp_configuration_update(stp); stp_port_state_selection(stp); if (!stp_is_root_bridge(stp) && root) { stp_stop_timer(&stp->hello_timer); if (stp->topology_change_detected) { stp_stop_timer(&stp->topology_change_timer); stp_transmit_tcn(stp); stp_start_timer(&stp->tcn_timer, 0); } } if (p == stp->root_port) { stp_record_config_timeout_values(stp, config); stp_config_bpdu_generation(stp); if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE_ACK) { stp_topology_change_acknowledged(stp); } if (config->flags & STP_CONFIG_TOPOLOGY_CHANGE) { stp->fdb_needs_flush = true; } } } else if (stp_is_designated_port(p)) { stp_transmit_config(p); } } } static void stp_received_tcn_bpdu(struct stp *stp, struct stp_port *p) OVS_REQUIRES(mutex) { if (p->state != STP_DISABLED) { if (stp_is_designated_port(p)) { stp_topology_change_detection(stp); stp_acknowledge_topology_change(p); } } } static void stp_hello_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex) { stp_config_bpdu_generation(stp); stp_start_timer(&stp->hello_timer, 0); } static void stp_message_age_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex) { struct stp *stp = p->stp; bool root = stp_is_root_bridge(stp); VLOG_DBG_RL(&stp_rl, "bridge: %s, port: %s, message age timer expired", stp->name, p->port_name); stp_become_designated_port(p); stp_configuration_update(stp); stp_port_state_selection(stp); if (stp_is_root_bridge(stp) && !root) { stp->max_age = stp->bridge_max_age; stp->hello_time = stp->bridge_hello_time; stp->forward_delay = stp->bridge_forward_delay; stp_topology_change_detection(stp); stp_stop_timer(&stp->tcn_timer); stp_config_bpdu_generation(stp); stp_start_timer(&stp->hello_timer, 0); } } static bool stp_is_designated_for_some_port(const struct stp *stp) OVS_REQUIRES(mutex) { const struct stp_port *p; FOR_EACH_ENABLED_PORT (p, stp) { if (p->designated_bridge == stp->bridge_id) { return true; } } return false; } static void stp_forward_delay_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex) { if (p->state == STP_LISTENING) { stp_set_port_state(p, STP_LEARNING); stp_start_timer(&p->forward_delay_timer, 0); } else if (p->state == STP_LEARNING) { stp_set_port_state(p, STP_FORWARDING); if (stp_is_designated_for_some_port(p->stp)) { if (p->change_detection_enabled) { stp_topology_change_detection(p->stp); } } } } static void stp_tcn_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex) { stp_transmit_tcn(stp); stp_start_timer(&stp->tcn_timer, 0); } static void stp_topology_change_timer_expiry(struct stp *stp) OVS_REQUIRES(mutex) { stp->topology_change_detected = false; stp->topology_change = false; } static void stp_hold_timer_expiry(struct stp_port *p) OVS_REQUIRES(mutex) { if (p->config_pending) { stp_transmit_config(p); } } static void stp_initialize_port(struct stp_port *p, enum stp_state state) OVS_REQUIRES(mutex) { ovs_assert(state & (STP_DISABLED | STP_BLOCKING)); stp_become_designated_port(p); if (!p->state && state == STP_DISABLED) { p->state = state; /* Do not trigger state change when initializing. */ } else { stp_set_port_state(p, state); } p->topology_change_ack = false; p->config_pending = false; p->change_detection_enabled = true; p->aux = NULL; stp_stop_timer(&p->message_age_timer); stp_stop_timer(&p->forward_delay_timer); stp_stop_timer(&p->hold_timer); p->tx_count = p->rx_count = p->error_count = 0; } static void stp_become_root_bridge(struct stp *stp) OVS_REQUIRES(mutex) { stp->max_age = stp->bridge_max_age; stp->hello_time = stp->bridge_hello_time; stp->forward_delay = stp->bridge_forward_delay; stp_topology_change_detection(stp); stp_stop_timer(&stp->tcn_timer); stp_config_bpdu_generation(stp); stp_start_timer(&stp->hello_timer, 0); } static void stp_start_timer(struct stp_timer *timer, int value) OVS_REQUIRES(mutex) { timer->value = value; timer->active = true; } static void stp_stop_timer(struct stp_timer *timer) OVS_REQUIRES(mutex) { timer->active = false; } static bool stp_timer_expired(struct stp_timer *timer, int elapsed, int timeout) OVS_REQUIRES(mutex) { if (timer->active) { timer->value += elapsed; if (timer->value >= timeout) { timer->active = false; return true; } } return false; } /* Returns the number of whole STP timer ticks in 'ms' milliseconds. There * are 256 STP timer ticks per second. */ static int ms_to_timer(int ms) { return ms * 0x100 / 1000; } /* Returns the number of whole milliseconds in 'timer' STP timer ticks. There * are 256 STP timer ticks per second. */ static int timer_to_ms(int timer) { return timer * 1000 / 0x100; } static int clamp(int x, int min, int max) { return x < min ? min : x > max ? max : x; } static void stp_update_bridge_timers(struct stp *stp) OVS_REQUIRES(mutex) { int ht, ma, fd; ht = clamp(stp->rq_hello_time, 1000, 10000); ma = clamp(stp->rq_max_age, MAX(2 * (ht + 1000), 6000), 40000); fd = clamp(stp->rq_forward_delay, ma / 2 + 1000, 30000); stp->bridge_hello_time = ms_to_timer(ht); stp->bridge_max_age = ms_to_timer(ma); stp->bridge_forward_delay = ms_to_timer(fd); if (stp_is_root_bridge(stp)) { stp->max_age = stp->bridge_max_age; stp->hello_time = stp->bridge_hello_time; stp->forward_delay = stp->bridge_forward_delay; } } static void stp_send_bpdu(struct stp_port *p, const void *bpdu, size_t bpdu_size) OVS_REQUIRES(mutex) { struct eth_header *eth; struct llc_header *llc; struct dp_packet *pkt; /* Skeleton. */ pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size); eth = dp_packet_put_zeros(pkt, sizeof *eth); llc = dp_packet_put_zeros(pkt, sizeof *llc); dp_packet_reset_offsets(pkt); dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size)); /* 802.2 header. */ eth->eth_dst = eth_addr_stp; /* p->stp->send_bpdu() must fill in source address. */ eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN); /* LLC header. */ llc->llc_dsap = STP_LLC_DSAP; llc->llc_ssap = STP_LLC_SSAP; llc->llc_cntl = STP_LLC_CNTL; p->stp->send_bpdu(pkt, stp_port_no(p), p->stp->aux); p->tx_count++; } /* Unixctl. */ static struct stp * stp_find(const char *name) OVS_REQUIRES(mutex) { struct stp *stp; LIST_FOR_EACH (stp, node, all_stps) { if (!strcmp(stp->name, name)) { return stp; } } return NULL; } static void stp_unixctl_tcn(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { ovs_mutex_lock(&mutex); if (argc > 1) { struct stp *stp = stp_find(argv[1]); if (!stp) { unixctl_command_reply_error(conn, "no such stp object"); goto out; } stp_topology_change_detection(stp); } else { struct stp *stp; LIST_FOR_EACH (stp, node, all_stps) { stp_topology_change_detection(stp); } } unixctl_command_reply(conn, "OK"); out: ovs_mutex_unlock(&mutex); } openvswitch-2.5.9/lib/PaxHeaders.82075/memory.h0000644000000000000000000000013213534540071016073 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.765853308 openvswitch-2.5.9/lib/memory.h0000644000175000017500000000317413534540071017566 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MEMORY_H #define MEMORY_H 1 /* Memory usage monitor. * * This is intended to be called as part of a daemon's main loop. After some * time to allow the daemon to allocate an initial memory usage, it logs some * memory usage information (most of which must actually be provided by the * client). At intervals, if the daemon's memory usage has grown * significantly, it again logs information. * * The monitor also has a unixctl interface. * * Intended usage in the program's main loop is like this: * * for (;;) { * memory_run(); * if (memory_should_report()) { * struct simap usage; * * simap_init(&usage); * ...fill in 'usage' with meaningful statistics... * memory_report(&usage); * simap_destroy(&usage); * } * * ... * * memory_wait(); * poll_block(); * } */ #include struct simap; void memory_run(void); void memory_wait(void); bool memory_should_report(void); void memory_report(const struct simap *usage); #endif /* memory.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/flow.c0000644000000000000000000000013213534540071015525 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.725853013 openvswitch-2.5.9/lib/flow.c0000644000175000017500000024603213534540071017222 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "flow.h" #include #include #include #include #include #include #include #include #include #include "byte-order.h" #include "coverage.h" #include "csum.h" #include "dynamic-string.h" #include "hash.h" #include "jhash.h" #include "match.h" #include "dp-packet.h" #include "openflow/openflow.h" #include "packets.h" #include "odp-util.h" #include "random.h" #include "unaligned.h" COVERAGE_DEFINE(flow_extract); COVERAGE_DEFINE(miniflow_malloc); /* U64 indices for segmented flow classification. */ const uint8_t flow_segment_u64s[4] = { FLOW_SEGMENT_1_ENDS_AT / sizeof(uint64_t), FLOW_SEGMENT_2_ENDS_AT / sizeof(uint64_t), FLOW_SEGMENT_3_ENDS_AT / sizeof(uint64_t), FLOW_U64S }; /* Asserts that field 'f1' follows immediately after 'f0' in struct flow, * without any intervening padding. */ #define ASSERT_SEQUENTIAL(f0, f1) \ BUILD_ASSERT_DECL(offsetof(struct flow, f0) \ + MEMBER_SIZEOF(struct flow, f0) \ == offsetof(struct flow, f1)) /* Asserts that fields 'f0' and 'f1' are in the same 32-bit aligned word within * struct flow. */ #define ASSERT_SAME_WORD(f0, f1) \ BUILD_ASSERT_DECL(offsetof(struct flow, f0) / 4 \ == offsetof(struct flow, f1) / 4) /* Asserts that 'f0' and 'f1' are both sequential and within the same 32-bit * aligned word in struct flow. */ #define ASSERT_SEQUENTIAL_SAME_WORD(f0, f1) \ ASSERT_SEQUENTIAL(f0, f1); \ ASSERT_SAME_WORD(f0, f1) /* miniflow_extract() assumes the following to be true to optimize the * extraction process. */ ASSERT_SEQUENTIAL_SAME_WORD(dl_type, vlan_tci); ASSERT_SEQUENTIAL_SAME_WORD(nw_frag, nw_tos); ASSERT_SEQUENTIAL_SAME_WORD(nw_tos, nw_ttl); ASSERT_SEQUENTIAL_SAME_WORD(nw_ttl, nw_proto); /* TCP flags in the middle of a BE64, zeroes in the other half. */ BUILD_ASSERT_DECL(offsetof(struct flow, tcp_flags) % 8 == 4); #if WORDS_BIGENDIAN #define TCP_FLAGS_BE32(tcp_ctl) ((OVS_FORCE ovs_be32)TCP_FLAGS_BE16(tcp_ctl) \ << 16) #else #define TCP_FLAGS_BE32(tcp_ctl) ((OVS_FORCE ovs_be32)TCP_FLAGS_BE16(tcp_ctl)) #endif ASSERT_SEQUENTIAL_SAME_WORD(tp_src, tp_dst); /* Removes 'size' bytes from the head end of '*datap', of size '*sizep', which * must contain at least 'size' bytes of data. Returns the first byte of data * removed. */ static inline const void * data_pull(const void **datap, size_t *sizep, size_t size) { const char *data = *datap; *datap = data + size; *sizep -= size; return data; } /* If '*datap' has at least 'size' bytes of data, removes that many bytes from * the head end of '*datap' and returns the first byte removed. Otherwise, * returns a null pointer without modifying '*datap'. */ static inline const void * data_try_pull(const void **datap, size_t *sizep, size_t size) { return OVS_LIKELY(*sizep >= size) ? data_pull(datap, sizep, size) : NULL; } /* Context for pushing data to a miniflow. */ struct mf_ctx { struct flowmap map; uint64_t *data; uint64_t * const end; }; /* miniflow_push_* macros allow filling in a miniflow data values in order. * Assertions are needed only when the layout of the struct flow is modified. * 'ofs' is a compile-time constant, which allows most of the code be optimized * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ #if (FLOW_WC_SEQ != 35) #define MINIFLOW_ASSERT(X) ovs_assert(X) BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime " "assertions enabled. Consider updating FLOW_WC_SEQ after " "testing") #else #define MINIFLOW_ASSERT(X) #endif /* True if 'IDX' and higher bits are not set. */ #define ASSERT_FLOWMAP_NOT_SET(FM, IDX) \ { \ MINIFLOW_ASSERT(!((FM)->bits[(IDX) / MAP_T_BITS] & \ (FLOWMAP_MAX << ((IDX) % MAP_T_BITS)))); \ for (size_t i = (IDX) / MAP_T_BITS + 1; i < FLOWMAP_UNITS; i++) { \ MINIFLOW_ASSERT(!(FM)->bits[i]); \ } \ } #define miniflow_set_map(MF, OFS) \ { \ ASSERT_FLOWMAP_NOT_SET(&MF.map, (OFS)); \ flowmap_set(&MF.map, (OFS), 1); \ } #define miniflow_assert_in_map(MF, OFS) \ MINIFLOW_ASSERT(FLOWMAP_IS_SET(MF.map, (OFS))); \ ASSERT_FLOWMAP_NOT_SET(&MF.map, (OFS) + 1) #define miniflow_push_uint64_(MF, OFS, VALUE) \ { \ MINIFLOW_ASSERT(MF.data < MF.end && (OFS) % 8 == 0); \ *MF.data++ = VALUE; \ miniflow_set_map(MF, OFS / 8); \ } #define miniflow_push_be64_(MF, OFS, VALUE) \ miniflow_push_uint64_(MF, OFS, (OVS_FORCE uint64_t)(VALUE)) #define miniflow_push_uint32_(MF, OFS, VALUE) \ { \ MINIFLOW_ASSERT(MF.data < MF.end); \ \ if ((OFS) % 8 == 0) { \ miniflow_set_map(MF, OFS / 8); \ *(uint32_t *)MF.data = VALUE; \ } else if ((OFS) % 8 == 4) { \ miniflow_assert_in_map(MF, OFS / 8); \ *((uint32_t *)MF.data + 1) = VALUE; \ MF.data++; \ } \ } #define miniflow_push_be32_(MF, OFS, VALUE) \ miniflow_push_uint32_(MF, OFS, (OVS_FORCE uint32_t)(VALUE)) #define miniflow_push_uint16_(MF, OFS, VALUE) \ { \ MINIFLOW_ASSERT(MF.data < MF.end); \ \ if ((OFS) % 8 == 0) { \ miniflow_set_map(MF, OFS / 8); \ *(uint16_t *)MF.data = VALUE; \ } else if ((OFS) % 8 == 2) { \ miniflow_assert_in_map(MF, OFS / 8); \ *((uint16_t *)MF.data + 1) = VALUE; \ } else if ((OFS) % 8 == 4) { \ miniflow_assert_in_map(MF, OFS / 8); \ *((uint16_t *)MF.data + 2) = VALUE; \ } else if ((OFS) % 8 == 6) { \ miniflow_assert_in_map(MF, OFS / 8); \ *((uint16_t *)MF.data + 3) = VALUE; \ MF.data++; \ } \ } #define miniflow_pad_to_64_(MF, OFS) \ { \ MINIFLOW_ASSERT((OFS) % 8 != 0); \ miniflow_assert_in_map(MF, OFS / 8); \ \ memset((uint8_t *)MF.data + (OFS) % 8, 0, 8 - (OFS) % 8); \ MF.data++; \ } #define miniflow_push_be16_(MF, OFS, VALUE) \ miniflow_push_uint16_(MF, OFS, (OVS_FORCE uint16_t)VALUE); #define miniflow_set_maps(MF, OFS, N_WORDS) \ { \ size_t ofs = (OFS); \ size_t n_words = (N_WORDS); \ \ MINIFLOW_ASSERT(n_words && MF.data + n_words <= MF.end); \ ASSERT_FLOWMAP_NOT_SET(&MF.map, ofs); \ flowmap_set(&MF.map, ofs, n_words); \ } /* Data at 'valuep' may be unaligned. */ #define miniflow_push_words_(MF, OFS, VALUEP, N_WORDS) \ { \ MINIFLOW_ASSERT((OFS) % 8 == 0); \ miniflow_set_maps(MF, (OFS) / 8, (N_WORDS)); \ memcpy(MF.data, (VALUEP), (N_WORDS) * sizeof *MF.data); \ MF.data += (N_WORDS); \ } /* Push 32-bit words padded to 64-bits. */ #define miniflow_push_words_32_(MF, OFS, VALUEP, N_WORDS) \ { \ miniflow_set_maps(MF, (OFS) / 8, DIV_ROUND_UP(N_WORDS, 2)); \ memcpy(MF.data, (VALUEP), (N_WORDS) * sizeof(uint32_t)); \ MF.data += DIV_ROUND_UP(N_WORDS, 2); \ if ((N_WORDS) & 1) { \ *((uint32_t *)MF.data - 1) = 0; \ } \ } /* Data at 'valuep' may be unaligned. */ /* MACs start 64-aligned, and must be followed by other data or padding. */ #define miniflow_push_macs_(MF, OFS, VALUEP) \ { \ miniflow_set_maps(MF, (OFS) / 8, 2); \ memcpy(MF.data, (VALUEP), 2 * ETH_ADDR_LEN); \ MF.data += 1; /* First word only. */ \ } #define miniflow_push_uint32(MF, FIELD, VALUE) \ miniflow_push_uint32_(MF, offsetof(struct flow, FIELD), VALUE) #define miniflow_push_be32(MF, FIELD, VALUE) \ miniflow_push_be32_(MF, offsetof(struct flow, FIELD), VALUE) #define miniflow_push_uint16(MF, FIELD, VALUE) \ miniflow_push_uint16_(MF, offsetof(struct flow, FIELD), VALUE) #define miniflow_push_be16(MF, FIELD, VALUE) \ miniflow_push_be16_(MF, offsetof(struct flow, FIELD), VALUE) #define miniflow_pad_to_64(MF, FIELD) \ miniflow_pad_to_64_(MF, offsetof(struct flow, FIELD)) #define miniflow_push_words(MF, FIELD, VALUEP, N_WORDS) \ miniflow_push_words_(MF, offsetof(struct flow, FIELD), VALUEP, N_WORDS) #define miniflow_push_words_32(MF, FIELD, VALUEP, N_WORDS) \ miniflow_push_words_32_(MF, offsetof(struct flow, FIELD), VALUEP, N_WORDS) #define miniflow_push_macs(MF, FIELD, VALUEP) \ miniflow_push_macs_(MF, offsetof(struct flow, FIELD), VALUEP) /* Pulls the MPLS headers at '*datap' and returns the count of them. */ static inline int parse_mpls(const void **datap, size_t *sizep) { const struct mpls_hdr *mh; int count = 0; while ((mh = data_try_pull(datap, sizep, sizeof *mh))) { count++; if (mh->mpls_lse.lo & htons(1 << MPLS_BOS_SHIFT)) { break; } } return MIN(count, FLOW_MAX_MPLS_LABELS); } static inline ovs_be16 parse_vlan(const void **datap, size_t *sizep) { const struct eth_header *eth = *datap; struct qtag_prefix { ovs_be16 eth_type; /* ETH_TYPE_VLAN */ ovs_be16 tci; }; data_pull(datap, sizep, ETH_ADDR_LEN * 2); if (eth->eth_type == htons(ETH_TYPE_VLAN)) { if (OVS_LIKELY(*sizep >= sizeof(struct qtag_prefix) + sizeof(ovs_be16))) { const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp); return qp->tci | htons(VLAN_CFI); } } return 0; } static inline ovs_be16 parse_ethertype(const void **datap, size_t *sizep) { const struct llc_snap_header *llc; ovs_be16 proto; proto = *(ovs_be16 *) data_pull(datap, sizep, sizeof proto); if (OVS_LIKELY(ntohs(proto) >= ETH_TYPE_MIN)) { return proto; } if (OVS_UNLIKELY(*sizep < sizeof *llc)) { return htons(FLOW_DL_TYPE_NONE); } llc = *datap; if (OVS_UNLIKELY(llc->llc.llc_dsap != LLC_DSAP_SNAP || llc->llc.llc_ssap != LLC_SSAP_SNAP || llc->llc.llc_cntl != LLC_CNTL_SNAP || memcmp(llc->snap.snap_org, SNAP_ORG_ETHERNET, sizeof llc->snap.snap_org))) { return htons(FLOW_DL_TYPE_NONE); } data_pull(datap, sizep, sizeof *llc); if (OVS_LIKELY(ntohs(llc->snap.snap_type) >= ETH_TYPE_MIN)) { return llc->snap.snap_type; } return htons(FLOW_DL_TYPE_NONE); } static inline void parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp, const struct in6_addr **nd_target, struct eth_addr arp_buf[2]) { if (icmp->icmp6_code == 0 && (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { *nd_target = data_try_pull(datap, sizep, sizeof **nd_target); if (OVS_UNLIKELY(!*nd_target)) { return; } while (*sizep >= 8) { /* The minimum size of an option is 8 bytes, which also is * the size of Ethernet link-layer options. */ const struct ovs_nd_opt *nd_opt = *datap; int opt_len = nd_opt->nd_opt_len * ND_OPT_LEN; if (!opt_len || opt_len > *sizep) { return; } /* Store the link layer address if the appropriate option is * provided. It is considered an error if the same link * layer option is specified twice. */ if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) { if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) { arp_buf[0] = nd_opt->nd_opt_mac; } else { goto invalid; } } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR && opt_len == 8) { if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) { arp_buf[1] = nd_opt->nd_opt_mac; } else { goto invalid; } } if (OVS_UNLIKELY(!data_try_pull(datap, sizep, opt_len))) { return; } } } return; invalid: *nd_target = NULL; arp_buf[0] = eth_addr_zero; arp_buf[1] = eth_addr_zero; } /* Initializes 'flow' members from 'packet' and 'md' * * Initializes 'packet' header l2 pointer to the start of the Ethernet * header, and the layer offsets as follows: * * - packet->l2_5_ofs to the start of the MPLS shim header, or UINT16_MAX * when there is no MPLS shim header. * * - packet->l3_ofs to just past the Ethernet header, or just past the * vlan_header if one is present, to the first byte of the payload of the * Ethernet frame. UINT16_MAX if the frame is too short to contain an * Ethernet header. * * - packet->l4_ofs to just past the IPv4 header, if one is present and * has at least the content used for the fields of interest for the flow, * otherwise UINT16_MAX. */ void flow_extract(struct dp_packet *packet, struct flow *flow) { struct { struct miniflow mf; uint64_t buf[FLOW_U64S]; } m; COVERAGE_INC(flow_extract); miniflow_extract(packet, &m.mf); miniflow_expand(&m.mf, flow); } /* Caller is responsible for initializing 'dst' with enough storage for * FLOW_U64S * 8 bytes. */ void miniflow_extract(struct dp_packet *packet, struct miniflow *dst) { const struct pkt_metadata *md = &packet->md; const void *data = dp_packet_data(packet); size_t size = dp_packet_size(packet); uint64_t *values = miniflow_values(dst); struct mf_ctx mf = { FLOWMAP_EMPTY_INITIALIZER, values, values + FLOW_U64S }; const char *l2; ovs_be16 dl_type; uint8_t nw_frag, nw_tos, nw_ttl, nw_proto; /* Metadata. */ if (flow_tnl_dst_is_set(&md->tunnel)) { miniflow_push_words(mf, tunnel, &md->tunnel, offsetof(struct flow_tnl, metadata) / sizeof(uint64_t)); if (!(md->tunnel.flags & FLOW_TNL_F_UDPIF)) { if (md->tunnel.metadata.present.map) { miniflow_push_words(mf, tunnel.metadata, &md->tunnel.metadata, sizeof md->tunnel.metadata / sizeof(uint64_t)); } } else { if (md->tunnel.metadata.present.len) { miniflow_push_words(mf, tunnel.metadata.present, &md->tunnel.metadata.present, 1); miniflow_push_words(mf, tunnel.metadata.opts.gnv, md->tunnel.metadata.opts.gnv, DIV_ROUND_UP(md->tunnel.metadata.present.len, sizeof(uint64_t))); } } } if (md->skb_priority || md->pkt_mark) { miniflow_push_uint32(mf, skb_priority, md->skb_priority); miniflow_push_uint32(mf, pkt_mark, md->pkt_mark); } miniflow_push_uint32(mf, dp_hash, md->dp_hash); miniflow_push_uint32(mf, in_port, odp_to_u32(md->in_port.odp_port)); if (md->recirc_id || md->ct_state) { miniflow_push_uint32(mf, recirc_id, md->recirc_id); miniflow_push_uint16(mf, ct_state, md->ct_state); miniflow_push_uint16(mf, ct_zone, md->ct_zone); } if (md->ct_state) { miniflow_push_uint32(mf, ct_mark, md->ct_mark); miniflow_pad_to_64(mf, pad1); if (!ovs_u128_is_zero(&md->ct_label)) { miniflow_push_words(mf, ct_label, &md->ct_label, sizeof md->ct_label / sizeof(uint64_t)); } } /* Initialize packet's layer pointer and offsets. */ l2 = data; dp_packet_reset_offsets(packet); /* Must have full Ethernet header to proceed. */ if (OVS_UNLIKELY(size < sizeof(struct eth_header))) { goto out; } else { ovs_be16 vlan_tci; /* Link layer. */ ASSERT_SEQUENTIAL(dl_dst, dl_src); miniflow_push_macs(mf, dl_dst, data); /* dl_type, vlan_tci. */ vlan_tci = parse_vlan(&data, &size); dl_type = parse_ethertype(&data, &size); miniflow_push_be16(mf, dl_type, dl_type); miniflow_push_be16(mf, vlan_tci, vlan_tci); } /* Parse mpls. */ if (OVS_UNLIKELY(eth_type_mpls(dl_type))) { int count; const void *mpls = data; packet->l2_5_ofs = (char *)data - l2; count = parse_mpls(&data, &size); miniflow_push_words_32(mf, mpls_lse, mpls, count); } /* Network layer. */ packet->l3_ofs = (char *)data - l2; nw_frag = 0; if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) { const struct ip_header *nh = data; int ip_len; uint16_t tot_len; if (OVS_UNLIKELY(size < IP_HEADER_LEN)) { goto out; } ip_len = IP_IHL(nh->ip_ihl_ver) * 4; if (OVS_UNLIKELY(ip_len < IP_HEADER_LEN)) { goto out; } if (OVS_UNLIKELY(size < ip_len)) { goto out; } tot_len = ntohs(nh->ip_tot_len); if (OVS_UNLIKELY(tot_len > size || ip_len > tot_len)) { goto out; } if (OVS_UNLIKELY(size - tot_len > UINT8_MAX)) { goto out; } dp_packet_set_l2_pad_size(packet, size - tot_len); size = tot_len; /* Never pull padding. */ /* Push both source and destination address at once. */ miniflow_push_words(mf, nw_src, &nh->ip_src, 1); miniflow_push_be32(mf, ipv6_label, 0); /* Padding for IPv4. */ nw_tos = nh->ip_tos; nw_ttl = nh->ip_ttl; nw_proto = nh->ip_proto; if (OVS_UNLIKELY(IP_IS_FRAGMENT(nh->ip_frag_off))) { nw_frag = FLOW_NW_FRAG_ANY; if (nh->ip_frag_off & htons(IP_FRAG_OFF_MASK)) { nw_frag |= FLOW_NW_FRAG_LATER; } } data_pull(&data, &size, ip_len); } else if (dl_type == htons(ETH_TYPE_IPV6)) { const struct ovs_16aligned_ip6_hdr *nh; ovs_be32 tc_flow; uint16_t plen; if (OVS_UNLIKELY(size < sizeof *nh)) { goto out; } nh = data_pull(&data, &size, sizeof *nh); plen = ntohs(nh->ip6_plen); if (OVS_UNLIKELY(plen > size)) { goto out; } /* Jumbo Payload option not supported yet. */ if (OVS_UNLIKELY(size - plen > UINT8_MAX)) { goto out; } dp_packet_set_l2_pad_size(packet, size - plen); size = plen; /* Never pull padding. */ miniflow_push_words(mf, ipv6_src, &nh->ip6_src, sizeof nh->ip6_src / 8); miniflow_push_words(mf, ipv6_dst, &nh->ip6_dst, sizeof nh->ip6_dst / 8); tc_flow = get_16aligned_be32(&nh->ip6_flow); nw_tos = ntohl(tc_flow) >> 20; nw_ttl = nh->ip6_hlim; nw_proto = nh->ip6_nxt; while (1) { if (OVS_LIKELY((nw_proto != IPPROTO_HOPOPTS) && (nw_proto != IPPROTO_ROUTING) && (nw_proto != IPPROTO_DSTOPTS) && (nw_proto != IPPROTO_AH) && (nw_proto != IPPROTO_FRAGMENT))) { /* It's either a terminal header (e.g., TCP, UDP) or one we * don't understand. In either case, we're done with the * packet, so use it to fill in 'nw_proto'. */ break; } /* We only verify that at least 8 bytes of the next header are * available, but many of these headers are longer. Ensure that * accesses within the extension header are within those first 8 * bytes. All extension headers are required to be at least 8 * bytes. */ if (OVS_UNLIKELY(size < 8)) { goto out; } if ((nw_proto == IPPROTO_HOPOPTS) || (nw_proto == IPPROTO_ROUTING) || (nw_proto == IPPROTO_DSTOPTS)) { /* These headers, while different, have the fields we care * about in the same location and with the same * interpretation. */ const struct ip6_ext *ext_hdr = data; nw_proto = ext_hdr->ip6e_nxt; if (OVS_UNLIKELY(!data_try_pull(&data, &size, (ext_hdr->ip6e_len + 1) * 8))) { goto out; } } else if (nw_proto == IPPROTO_AH) { /* A standard AH definition isn't available, but the fields * we care about are in the same location as the generic * option header--only the header length is calculated * differently. */ const struct ip6_ext *ext_hdr = data; nw_proto = ext_hdr->ip6e_nxt; if (OVS_UNLIKELY(!data_try_pull(&data, &size, (ext_hdr->ip6e_len + 2) * 4))) { goto out; } } else if (nw_proto == IPPROTO_FRAGMENT) { const struct ovs_16aligned_ip6_frag *frag_hdr = data; nw_proto = frag_hdr->ip6f_nxt; if (!data_try_pull(&data, &size, sizeof *frag_hdr)) { goto out; } /* We only process the first fragment. */ if (frag_hdr->ip6f_offlg != htons(0)) { nw_frag = FLOW_NW_FRAG_ANY; if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) { nw_frag |= FLOW_NW_FRAG_LATER; nw_proto = IPPROTO_FRAGMENT; break; } } } } /* This needs to be after the parse_ipv6_ext_hdrs__() call because it * leaves the nw_frag word uninitialized. */ ASSERT_SEQUENTIAL(ipv6_label, nw_frag); ovs_be32 label = tc_flow & htonl(IPV6_LABEL_MASK); miniflow_push_be32(mf, ipv6_label, label); } else { if (dl_type == htons(ETH_TYPE_ARP) || dl_type == htons(ETH_TYPE_RARP)) { struct eth_addr arp_buf[2]; const struct arp_eth_header *arp = (const struct arp_eth_header *) data_try_pull(&data, &size, ARP_ETH_HEADER_LEN); if (OVS_LIKELY(arp) && OVS_LIKELY(arp->ar_hrd == htons(1)) && OVS_LIKELY(arp->ar_pro == htons(ETH_TYPE_IP)) && OVS_LIKELY(arp->ar_hln == ETH_ADDR_LEN) && OVS_LIKELY(arp->ar_pln == 4)) { miniflow_push_be32(mf, nw_src, get_16aligned_be32(&arp->ar_spa)); miniflow_push_be32(mf, nw_dst, get_16aligned_be32(&arp->ar_tpa)); /* We only match on the lower 8 bits of the opcode. */ if (OVS_LIKELY(ntohs(arp->ar_op) <= 0xff)) { miniflow_push_be32(mf, ipv6_label, 0); /* Pad with ARP. */ miniflow_push_be32(mf, nw_frag, htonl(ntohs(arp->ar_op))); } /* Must be adjacent. */ ASSERT_SEQUENTIAL(arp_sha, arp_tha); arp_buf[0] = arp->ar_sha; arp_buf[1] = arp->ar_tha; miniflow_push_macs(mf, arp_sha, arp_buf); miniflow_pad_to_64(mf, tcp_flags); } } goto out; } packet->l4_ofs = (char *)data - l2; miniflow_push_be32(mf, nw_frag, BYTES_TO_BE32(nw_frag, nw_tos, nw_ttl, nw_proto)); if (OVS_LIKELY(!(nw_frag & FLOW_NW_FRAG_LATER))) { if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) { if (OVS_LIKELY(size >= TCP_HEADER_LEN)) { const struct tcp_header *tcp = data; miniflow_push_be32(mf, arp_tha.ea[2], 0); miniflow_push_be32(mf, tcp_flags, TCP_FLAGS_BE32(tcp->tcp_ctl)); miniflow_push_be16(mf, tp_src, tcp->tcp_src); miniflow_push_be16(mf, tp_dst, tcp->tcp_dst); miniflow_pad_to_64(mf, igmp_group_ip4); } } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) { if (OVS_LIKELY(size >= UDP_HEADER_LEN)) { const struct udp_header *udp = data; miniflow_push_be16(mf, tp_src, udp->udp_src); miniflow_push_be16(mf, tp_dst, udp->udp_dst); miniflow_pad_to_64(mf, igmp_group_ip4); } } else if (OVS_LIKELY(nw_proto == IPPROTO_SCTP)) { if (OVS_LIKELY(size >= SCTP_HEADER_LEN)) { const struct sctp_header *sctp = data; miniflow_push_be16(mf, tp_src, sctp->sctp_src); miniflow_push_be16(mf, tp_dst, sctp->sctp_dst); miniflow_pad_to_64(mf, igmp_group_ip4); } } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMP)) { if (OVS_LIKELY(size >= ICMP_HEADER_LEN)) { const struct icmp_header *icmp = data; miniflow_push_be16(mf, tp_src, htons(icmp->icmp_type)); miniflow_push_be16(mf, tp_dst, htons(icmp->icmp_code)); miniflow_pad_to_64(mf, igmp_group_ip4); } } else if (OVS_LIKELY(nw_proto == IPPROTO_IGMP)) { if (OVS_LIKELY(size >= IGMP_HEADER_LEN)) { const struct igmp_header *igmp = data; miniflow_push_be16(mf, tp_src, htons(igmp->igmp_type)); miniflow_push_be16(mf, tp_dst, htons(igmp->igmp_code)); miniflow_push_be32(mf, igmp_group_ip4, get_16aligned_be32(&igmp->group)); } } else if (OVS_LIKELY(nw_proto == IPPROTO_ICMPV6)) { if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) { const struct in6_addr *nd_target = NULL; struct eth_addr arp_buf[2] = { { { { 0 } } } }; const struct icmp6_hdr *icmp = data_pull(&data, &size, sizeof *icmp); parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf); if (nd_target) { miniflow_push_words(mf, nd_target, nd_target, sizeof *nd_target / sizeof(uint64_t)); } miniflow_push_macs(mf, arp_sha, arp_buf); miniflow_pad_to_64(mf, tcp_flags); miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type)); miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code)); miniflow_pad_to_64(mf, igmp_group_ip4); } } } out: dst->map = mf.map; } /* For every bit of a field that is wildcarded in 'wildcards', sets the * corresponding bit in 'flow' to zero. */ void flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) { uint64_t *flow_u64 = (uint64_t *) flow; const uint64_t *wc_u64 = (const uint64_t *) &wildcards->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { flow_u64[i] &= wc_u64[i]; } } void flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc) { if (flow->nw_proto != IPPROTO_ICMP) { memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); } else { wc->masks.tp_src = htons(0xff); wc->masks.tp_dst = htons(0xff); } } /* Initializes 'flow_metadata' with the metadata found in 'flow'. */ void flow_get_metadata(const struct flow *flow, struct match *flow_metadata) { int i; BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); match_init_catchall(flow_metadata); if (flow->tunnel.tun_id != htonll(0)) { match_set_tun_id(flow_metadata, flow->tunnel.tun_id); } if (flow->tunnel.flags & FLOW_TNL_PUB_F_MASK) { match_set_tun_flags(flow_metadata, flow->tunnel.flags & FLOW_TNL_PUB_F_MASK); } if (flow->tunnel.ip_src) { match_set_tun_src(flow_metadata, flow->tunnel.ip_src); } if (flow->tunnel.ip_dst) { match_set_tun_dst(flow_metadata, flow->tunnel.ip_dst); } if (ipv6_addr_is_set(&flow->tunnel.ipv6_src)) { match_set_tun_ipv6_src(flow_metadata, &flow->tunnel.ipv6_src); } if (ipv6_addr_is_set(&flow->tunnel.ipv6_dst)) { match_set_tun_ipv6_dst(flow_metadata, &flow->tunnel.ipv6_dst); } if (flow->tunnel.gbp_id != htons(0)) { match_set_tun_gbp_id(flow_metadata, flow->tunnel.gbp_id); } if (flow->tunnel.gbp_flags) { match_set_tun_gbp_flags(flow_metadata, flow->tunnel.gbp_flags); } tun_metadata_get_fmd(&flow->tunnel, flow_metadata); if (flow->metadata != htonll(0)) { match_set_metadata(flow_metadata, flow->metadata); } for (i = 0; i < FLOW_N_REGS; i++) { if (flow->regs[i]) { match_set_reg(flow_metadata, i, flow->regs[i]); } } if (flow->pkt_mark != 0) { match_set_pkt_mark(flow_metadata, flow->pkt_mark); } match_set_in_port(flow_metadata, flow->in_port.ofp_port); if (flow->ct_state != 0) { match_set_ct_state(flow_metadata, flow->ct_state); } if (flow->ct_zone != 0) { match_set_ct_zone(flow_metadata, flow->ct_zone); } if (flow->ct_mark != 0) { match_set_ct_mark(flow_metadata, flow->ct_mark); } if (!ovs_u128_is_zero(&flow->ct_label)) { match_set_ct_label(flow_metadata, flow->ct_label); } } const char *ct_state_to_string(uint32_t state) { switch (state) { case CS_REPLY_DIR: return "rpl"; case CS_TRACKED: return "trk"; case CS_NEW: return "new"; case CS_ESTABLISHED: return "est"; case CS_RELATED: return "rel"; case CS_INVALID: return "inv"; default: return NULL; } } char * flow_to_string(const struct flow *flow) { struct ds ds = DS_EMPTY_INITIALIZER; flow_format(&ds, flow); return ds_cstr(&ds); } const char * flow_tun_flag_to_string(uint32_t flags) { switch (flags) { case FLOW_TNL_F_DONT_FRAGMENT: return "df"; case FLOW_TNL_F_CSUM: return "csum"; case FLOW_TNL_F_KEY: return "key"; case FLOW_TNL_F_OAM: return "oam"; default: return NULL; } } void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t), uint32_t flags, char del) { uint32_t bad = 0; if (!flags) { ds_put_char(ds, '0'); return; } while (flags) { uint32_t bit = rightmost_1bit(flags); const char *s; s = bit_to_string(bit); if (s) { ds_put_format(ds, "%s%c", s, del); } else { bad |= bit; } flags &= ~bit; } if (bad) { ds_put_format(ds, "0x%"PRIx32"%c", bad, del); } ds_chomp(ds, del); } void format_flags_masked(struct ds *ds, const char *name, const char *(*bit_to_string)(uint32_t), uint32_t flags, uint32_t mask, uint32_t max_mask) { if (name) { ds_put_format(ds, "%s=", name); } if (mask == max_mask) { format_flags(ds, bit_to_string, flags, '|'); return; } if (!mask) { ds_put_cstr(ds, "0/0"); return; } while (mask) { uint32_t bit = rightmost_1bit(mask); const char *s = bit_to_string(bit); ds_put_format(ds, "%s%s", (flags & bit) ? "+" : "-", s ? s : "[Unknown]"); mask &= ~bit; } } /* Scans a string 's' of flags to determine their numerical value and * returns the number of characters parsed using 'bit_to_string' to * lookup flag names. Scanning continues until the character 'end' is * reached. * * In the event of a failure, a negative error code will be returned. In * addition, if 'res_string' is non-NULL then a descriptive string will * be returned incorporating the identifying string 'field_name'. This * error string must be freed by the caller. * * Upon success, the flag values will be stored in 'res_flags' and * optionally 'res_mask', if it is non-NULL (if it is NULL then any masks * present in the original string will be considered an error). The * caller may restrict the acceptable set of values through the mask * 'allowed'. */ int parse_flags(const char *s, const char *(*bit_to_string)(uint32_t), char end, const char *field_name, char **res_string, uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask) { uint32_t result = 0; int n; /* Parse masked flags in numeric format? */ if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n", res_flags, res_mask, &n) && n > 0) { if (*res_flags & ~allowed || *res_mask & ~allowed) { goto unknown; } return n; } n = 0; if (res_mask && (*s == '+' || *s == '-')) { uint32_t flags = 0, mask = 0; /* Parse masked flags. */ while (s[0] != end) { bool set; uint32_t bit; size_t len; if (s[0] == '+') { set = true; } else if (s[0] == '-') { set = false; } else { if (res_string) { *res_string = xasprintf("%s: %s must be preceded by '+' " "(for SET) or '-' (NOT SET)", s, field_name); } return -EINVAL; } s++; n++; for (bit = 1; bit; bit <<= 1) { const char *fname = bit_to_string(bit); if (!fname) { continue; } len = strlen(fname); if (strncmp(s, fname, len) || (s[len] != '+' && s[len] != '-' && s[len] != end)) { continue; } if (mask & bit) { /* bit already set. */ if (res_string) { *res_string = xasprintf("%s: Each %s flag can be " "specified only once", s, field_name); } return -EINVAL; } if (!(bit & allowed)) { goto unknown; } if (set) { flags |= bit; } mask |= bit; break; } if (!bit) { goto unknown; } s += len; n += len; } *res_flags = flags; *res_mask = mask; return n; } /* Parse unmasked flags. If a flag is present, it is set, otherwise * it is not set. */ while (s[n] != end) { unsigned long long int flags; uint32_t bit; int n0; if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) { if (flags & ~allowed) { goto unknown; } n += n0 + (s[n + n0] == '|'); result |= flags; continue; } for (bit = 1; bit; bit <<= 1) { const char *name = bit_to_string(bit); size_t len; if (!name) { continue; } len = strlen(name); if (!strncmp(s + n, name, len) && (s[n + len] == '|' || s[n + len] == end)) { if (!(bit & allowed)) { goto unknown; } result |= bit; n += len + (s[n + len] == '|'); break; } } if (!bit) { goto unknown; } } *res_flags = result; if (res_mask) { *res_mask = UINT32_MAX; } if (res_string) { *res_string = NULL; } return n; unknown: if (res_string) { *res_string = xasprintf("%s: unknown %s flag(s)", s, field_name); } return -EINVAL; } void flow_format(struct ds *ds, const struct flow *flow) { struct match match; struct flow_wildcards *wc = &match.wc; match_wc_init(&match, flow); /* As this function is most often used for formatting a packet in a * packet-in message, skip formatting the packet context fields that are * all-zeroes to make the print-out easier on the eyes. This means that a * missing context field implies a zero value for that field. This is * similar to OpenFlow encoding of these fields, as the specification * states that all-zeroes context fields should not be encoded in the * packet-in messages. */ if (!flow->in_port.ofp_port) { WC_UNMASK_FIELD(wc, in_port); } if (!flow->skb_priority) { WC_UNMASK_FIELD(wc, skb_priority); } if (!flow->pkt_mark) { WC_UNMASK_FIELD(wc, pkt_mark); } if (!flow->recirc_id) { WC_UNMASK_FIELD(wc, recirc_id); } if (!flow->dp_hash) { WC_UNMASK_FIELD(wc, dp_hash); } if (!flow->ct_state) { WC_UNMASK_FIELD(wc, ct_state); } if (!flow->ct_zone) { WC_UNMASK_FIELD(wc, ct_zone); } if (!flow->ct_mark) { WC_UNMASK_FIELD(wc, ct_mark); } if (ovs_u128_is_zero(&flow->ct_label)) { WC_UNMASK_FIELD(wc, ct_label); } for (int i = 0; i < FLOW_N_REGS; i++) { if (!flow->regs[i]) { WC_UNMASK_FIELD(wc, regs[i]); } } if (!flow->metadata) { WC_UNMASK_FIELD(wc, metadata); } match_format(&match, ds, OFP_DEFAULT_PRIORITY); } void flow_print(FILE *stream, const struct flow *flow) { char *s = flow_to_string(flow); fputs(s, stream); free(s); } /* flow_wildcards functions. */ /* Initializes 'wc' as a set of wildcards that matches every packet. */ void flow_wildcards_init_catchall(struct flow_wildcards *wc) { memset(&wc->masks, 0, sizeof wc->masks); } /* Converts a flow into flow wildcards. It sets the wildcard masks based on * the packet headers extracted to 'flow'. It will not set the mask for fields * that do not make sense for the packet type. OpenFlow-only metadata is * wildcarded, but other metadata is unconditionally exact-matched. */ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, const struct flow *flow) { memset(&wc->masks, 0x0, sizeof wc->masks); /* Update this function whenever struct flow changes. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); if (flow_tnl_dst_is_set(&flow->tunnel)) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { WC_MASK_FIELD(wc, tunnel.tun_id); } WC_MASK_FIELD(wc, tunnel.ip_src); WC_MASK_FIELD(wc, tunnel.ip_dst); WC_MASK_FIELD(wc, tunnel.ipv6_src); WC_MASK_FIELD(wc, tunnel.ipv6_dst); WC_MASK_FIELD(wc, tunnel.flags); WC_MASK_FIELD(wc, tunnel.ip_tos); WC_MASK_FIELD(wc, tunnel.ip_ttl); WC_MASK_FIELD(wc, tunnel.tp_src); WC_MASK_FIELD(wc, tunnel.tp_dst); WC_MASK_FIELD(wc, tunnel.gbp_id); WC_MASK_FIELD(wc, tunnel.gbp_flags); if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) { if (flow->tunnel.metadata.present.map) { wc->masks.tunnel.metadata.present.map = flow->tunnel.metadata.present.map; WC_MASK_FIELD(wc, tunnel.metadata.opts.u8); } } else { WC_MASK_FIELD(wc, tunnel.metadata.present.len); memset(wc->masks.tunnel.metadata.opts.gnv, 0xff, flow->tunnel.metadata.present.len); } } else if (flow->tunnel.tun_id) { WC_MASK_FIELD(wc, tunnel.tun_id); } /* metadata, regs, and conj_id wildcarded. */ WC_MASK_FIELD(wc, skb_priority); WC_MASK_FIELD(wc, pkt_mark); WC_MASK_FIELD(wc, ct_state); WC_MASK_FIELD(wc, ct_zone); WC_MASK_FIELD(wc, ct_mark); WC_MASK_FIELD(wc, ct_label); WC_MASK_FIELD(wc, recirc_id); WC_MASK_FIELD(wc, dp_hash); WC_MASK_FIELD(wc, in_port); /* actset_output wildcarded. */ WC_MASK_FIELD(wc, dl_dst); WC_MASK_FIELD(wc, dl_src); WC_MASK_FIELD(wc, dl_type); WC_MASK_FIELD(wc, vlan_tci); if (flow->dl_type == htons(ETH_TYPE_IP)) { WC_MASK_FIELD(wc, nw_src); WC_MASK_FIELD(wc, nw_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { WC_MASK_FIELD(wc, ipv6_src); WC_MASK_FIELD(wc, ipv6_dst); WC_MASK_FIELD(wc, ipv6_label); } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { WC_MASK_FIELD(wc, nw_src); WC_MASK_FIELD(wc, nw_dst); WC_MASK_FIELD(wc, nw_proto); WC_MASK_FIELD(wc, arp_sha); WC_MASK_FIELD(wc, arp_tha); return; } else if (eth_type_mpls(flow->dl_type)) { for (int i = 0; i < FLOW_MAX_MPLS_LABELS; i++) { WC_MASK_FIELD(wc, mpls_lse[i]); if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) { break; } } return; } else { return; /* Unknown ethertype. */ } /* IPv4 or IPv6. */ WC_MASK_FIELD(wc, nw_frag); WC_MASK_FIELD(wc, nw_tos); WC_MASK_FIELD(wc, nw_ttl); WC_MASK_FIELD(wc, nw_proto); /* No transport layer header in later fragments. */ if (!(flow->nw_frag & FLOW_NW_FRAG_LATER) && (flow->nw_proto == IPPROTO_ICMP || flow->nw_proto == IPPROTO_ICMPV6 || flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_UDP || flow->nw_proto == IPPROTO_SCTP || flow->nw_proto == IPPROTO_IGMP)) { WC_MASK_FIELD(wc, tp_src); WC_MASK_FIELD(wc, tp_dst); if (flow->nw_proto == IPPROTO_TCP) { WC_MASK_FIELD(wc, tcp_flags); } else if (flow->nw_proto == IPPROTO_ICMPV6) { WC_MASK_FIELD(wc, arp_sha); WC_MASK_FIELD(wc, arp_tha); WC_MASK_FIELD(wc, nd_target); } else if (flow->nw_proto == IPPROTO_IGMP) { WC_MASK_FIELD(wc, igmp_group_ip4); } } } /* Return a map of possible fields for a packet of the same type as 'flow'. * Including extra bits in the returned mask is not wrong, it is just less * optimal. * * This is a less precise version of flow_wildcards_init_for_packet() above. */ void flow_wc_map(const struct flow *flow, struct flowmap *map) { /* Update this function whenever struct flow changes. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); flowmap_init(map); if (flow_tnl_dst_is_set(&flow->tunnel)) { FLOWMAP_SET__(map, tunnel, offsetof(struct flow_tnl, metadata)); if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) { if (flow->tunnel.metadata.present.map) { FLOWMAP_SET(map, tunnel.metadata); } } else { FLOWMAP_SET(map, tunnel.metadata.present.len); FLOWMAP_SET__(map, tunnel.metadata.opts.gnv, flow->tunnel.metadata.present.len); } } /* Metadata fields that can appear on packet input. */ FLOWMAP_SET(map, skb_priority); FLOWMAP_SET(map, pkt_mark); FLOWMAP_SET(map, recirc_id); FLOWMAP_SET(map, dp_hash); FLOWMAP_SET(map, in_port); FLOWMAP_SET(map, dl_dst); FLOWMAP_SET(map, dl_src); FLOWMAP_SET(map, dl_type); FLOWMAP_SET(map, vlan_tci); FLOWMAP_SET(map, ct_state); FLOWMAP_SET(map, ct_zone); FLOWMAP_SET(map, ct_mark); FLOWMAP_SET(map, ct_label); /* Ethertype-dependent fields. */ if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) { FLOWMAP_SET(map, nw_src); FLOWMAP_SET(map, nw_dst); FLOWMAP_SET(map, nw_proto); FLOWMAP_SET(map, nw_frag); FLOWMAP_SET(map, nw_tos); FLOWMAP_SET(map, nw_ttl); FLOWMAP_SET(map, tp_src); FLOWMAP_SET(map, tp_dst); if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) { FLOWMAP_SET(map, igmp_group_ip4); } else { FLOWMAP_SET(map, tcp_flags); } } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { FLOWMAP_SET(map, ipv6_src); FLOWMAP_SET(map, ipv6_dst); FLOWMAP_SET(map, ipv6_label); FLOWMAP_SET(map, nw_proto); FLOWMAP_SET(map, nw_frag); FLOWMAP_SET(map, nw_tos); FLOWMAP_SET(map, nw_ttl); FLOWMAP_SET(map, tp_src); FLOWMAP_SET(map, tp_dst); if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_ICMPV6)) { FLOWMAP_SET(map, nd_target); FLOWMAP_SET(map, arp_sha); FLOWMAP_SET(map, arp_tha); } else { FLOWMAP_SET(map, tcp_flags); } } else if (eth_type_mpls(flow->dl_type)) { FLOWMAP_SET(map, mpls_lse); } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { FLOWMAP_SET(map, nw_src); FLOWMAP_SET(map, nw_dst); FLOWMAP_SET(map, nw_proto); FLOWMAP_SET(map, arp_sha); FLOWMAP_SET(map, arp_tha); } } /* Clear the metadata and register wildcard masks. They are not packet * header fields. */ void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc) { /* Update this function whenever struct flow changes. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); memset(&wc->masks.regs, 0, sizeof wc->masks.regs); wc->masks.actset_output = 0; wc->masks.conj_id = 0; } /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or * fields. */ bool flow_wildcards_is_catchall(const struct flow_wildcards *wc) { const uint64_t *wc_u64 = (const uint64_t *) &wc->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { if (wc_u64[i]) { return false; } } return true; } /* Sets 'dst' as the bitwise AND of wildcards in 'src1' and 'src2'. * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded * in 'src1' or 'src2' or both. */ void flow_wildcards_and(struct flow_wildcards *dst, const struct flow_wildcards *src1, const struct flow_wildcards *src2) { uint64_t *dst_u64 = (uint64_t *) &dst->masks; const uint64_t *src1_u64 = (const uint64_t *) &src1->masks; const uint64_t *src2_u64 = (const uint64_t *) &src2->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { dst_u64[i] = src1_u64[i] & src2_u64[i]; } } /* Sets 'dst' as the bitwise OR of wildcards in 'src1' and 'src2'. That * is, a bit or a field is wildcarded in 'dst' if it is neither * wildcarded in 'src1' nor 'src2'. */ void flow_wildcards_or(struct flow_wildcards *dst, const struct flow_wildcards *src1, const struct flow_wildcards *src2) { uint64_t *dst_u64 = (uint64_t *) &dst->masks; const uint64_t *src1_u64 = (const uint64_t *) &src1->masks; const uint64_t *src2_u64 = (const uint64_t *) &src2->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { dst_u64[i] = src1_u64[i] | src2_u64[i]; } } /* Returns a hash of the wildcards in 'wc'. */ uint32_t flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis) { return flow_hash(&wc->masks, basis); } /* Returns true if 'a' and 'b' represent the same wildcards, false if they are * different. */ bool flow_wildcards_equal(const struct flow_wildcards *a, const struct flow_wildcards *b) { return flow_equal(&a->masks, &b->masks); } /* Returns true if at least one bit or field is wildcarded in 'a' but not in * 'b', false otherwise. */ bool flow_wildcards_has_extra(const struct flow_wildcards *a, const struct flow_wildcards *b) { const uint64_t *a_u64 = (const uint64_t *) &a->masks; const uint64_t *b_u64 = (const uint64_t *) &b->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { if ((a_u64[i] & b_u64[i]) != b_u64[i]) { return true; } } return false; } /* Returns true if 'a' and 'b' are equal, except that 0-bits (wildcarded bits) * in 'wc' do not need to be equal in 'a' and 'b'. */ bool flow_equal_except(const struct flow *a, const struct flow *b, const struct flow_wildcards *wc) { const uint64_t *a_u64 = (const uint64_t *) a; const uint64_t *b_u64 = (const uint64_t *) b; const uint64_t *wc_u64 = (const uint64_t *) &wc->masks; size_t i; for (i = 0; i < FLOW_U64S; i++) { if ((a_u64[i] ^ b_u64[i]) & wc_u64[i]) { return false; } } return true; } /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'. * (A 0-bit indicates a wildcard bit.) */ void flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask) { wc->masks.regs[idx] = mask; } /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'. * (A 0-bit indicates a wildcard bit.) */ void flow_wildcards_set_xreg_mask(struct flow_wildcards *wc, int idx, uint64_t mask) { flow_set_xreg(&wc->masks, idx, mask); } /* Calculates the 5-tuple hash from the given miniflow. * This returns the same value as flow_hash_5tuple for the corresponding * flow. */ uint32_t miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis) { uint32_t hash = basis; if (flow) { ovs_be16 dl_type = MINIFLOW_GET_BE16(flow, dl_type); hash = hash_add(hash, MINIFLOW_GET_U8(flow, nw_proto)); /* Separate loops for better optimization. */ if (dl_type == htons(ETH_TYPE_IPV6)) { struct flowmap map = FLOWMAP_EMPTY_INITIALIZER; uint64_t value; FLOWMAP_SET(&map, ipv6_src); FLOWMAP_SET(&map, ipv6_dst); MINIFLOW_FOR_EACH_IN_FLOWMAP(value, flow, map) { hash = hash_add64(hash, value); } } else { hash = hash_add(hash, MINIFLOW_GET_U32(flow, nw_src)); hash = hash_add(hash, MINIFLOW_GET_U32(flow, nw_dst)); } /* Add both ports at once. */ hash = hash_add(hash, MINIFLOW_GET_U32(flow, tp_src)); hash = hash_finish(hash, 42); /* Arbitrary number. */ } return hash; } ASSERT_SEQUENTIAL_SAME_WORD(tp_src, tp_dst); ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst); /* Calculates the 5-tuple hash from the given flow. */ uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis) { uint32_t hash = basis; if (flow) { hash = hash_add(hash, flow->nw_proto); if (flow->dl_type == htons(ETH_TYPE_IPV6)) { const uint64_t *flow_u64 = (const uint64_t *)flow; int ofs = offsetof(struct flow, ipv6_src) / 8; int end = ofs + 2 * sizeof flow->ipv6_src / 8; for (;ofs < end; ofs++) { hash = hash_add64(hash, flow_u64[ofs]); } } else { hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_src); hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst); } /* Add both ports at once. */ hash = hash_add(hash, ((const uint32_t *)flow)[offsetof(struct flow, tp_src) / sizeof(uint32_t)]); hash = hash_finish(hash, 42); /* Arbitrary number. */ } return hash; } /* Hashes 'flow' based on its L2 through L4 protocol information. */ uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis) { struct { union { ovs_be32 ipv4_addr; struct in6_addr ipv6_addr; }; ovs_be16 eth_type; ovs_be16 vlan_tci; ovs_be16 tp_port; struct eth_addr eth_addr; uint8_t ip_proto; } fields; int i; memset(&fields, 0, sizeof fields); for (i = 0; i < ARRAY_SIZE(fields.eth_addr.be16); i++) { fields.eth_addr.be16[i] = flow->dl_src.be16[i] ^ flow->dl_dst.be16[i]; } fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK); fields.eth_type = flow->dl_type; /* UDP source and destination port are not taken into account because they * will not necessarily be symmetric in a bidirectional flow. */ if (fields.eth_type == htons(ETH_TYPE_IP)) { fields.ipv4_addr = flow->nw_src ^ flow->nw_dst; fields.ip_proto = flow->nw_proto; if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) { fields.tp_port = flow->tp_src ^ flow->tp_dst; } } else if (fields.eth_type == htons(ETH_TYPE_IPV6)) { const uint8_t *a = &flow->ipv6_src.s6_addr[0]; const uint8_t *b = &flow->ipv6_dst.s6_addr[0]; uint8_t *ipv6_addr = &fields.ipv6_addr.s6_addr[0]; for (i=0; i<16; i++) { ipv6_addr[i] = a[i] ^ b[i]; } fields.ip_proto = flow->nw_proto; if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) { fields.tp_port = flow->tp_src ^ flow->tp_dst; } } return jhash_bytes(&fields, sizeof fields, basis); } /* Hashes 'flow' based on its L3 through L4 protocol information */ uint32_t flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis, bool inc_udp_ports) { uint32_t hash = basis; /* UDP source and destination port are also taken into account. */ if (flow->dl_type == htons(ETH_TYPE_IP)) { hash = hash_add(hash, (OVS_FORCE uint32_t) (flow->nw_src ^ flow->nw_dst)); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { /* IPv6 addresses are 64-bit aligned inside struct flow. */ const uint64_t *a = ALIGNED_CAST(uint64_t *, flow->ipv6_src.s6_addr); const uint64_t *b = ALIGNED_CAST(uint64_t *, flow->ipv6_dst.s6_addr); for (int i = 0; i < sizeof flow->ipv6_src / sizeof *a; i++) { hash = hash_add64(hash, a[i] ^ b[i]); } } else { /* Cannot hash non-IP flows */ return 0; } hash = hash_add(hash, flow->nw_proto); if (flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_SCTP || (inc_udp_ports && flow->nw_proto == IPPROTO_UDP)) { hash = hash_add(hash, (OVS_FORCE uint16_t) (flow->tp_src ^ flow->tp_dst)); } return hash_finish(hash, basis); } /* Initialize a flow with random fields that matter for nx_hash_fields. */ void flow_random_hash_fields(struct flow *flow) { uint16_t rnd = random_uint16(); /* Initialize to all zeros. */ memset(flow, 0, sizeof *flow); eth_addr_random(&flow->dl_src); eth_addr_random(&flow->dl_dst); flow->vlan_tci = (OVS_FORCE ovs_be16) (random_uint16() & VLAN_VID_MASK); /* Make most of the random flows IPv4, some IPv6, and rest random. */ flow->dl_type = rnd < 0x8000 ? htons(ETH_TYPE_IP) : rnd < 0xc000 ? htons(ETH_TYPE_IPV6) : (OVS_FORCE ovs_be16)rnd; if (dl_type_is_ip_any(flow->dl_type)) { if (flow->dl_type == htons(ETH_TYPE_IP)) { flow->nw_src = (OVS_FORCE ovs_be32)random_uint32(); flow->nw_dst = (OVS_FORCE ovs_be32)random_uint32(); } else { random_bytes(&flow->ipv6_src, sizeof flow->ipv6_src); random_bytes(&flow->ipv6_dst, sizeof flow->ipv6_dst); } /* Make most of IP flows TCP, some UDP or SCTP, and rest random. */ rnd = random_uint16(); flow->nw_proto = rnd < 0x8000 ? IPPROTO_TCP : rnd < 0xc000 ? IPPROTO_UDP : rnd < 0xd000 ? IPPROTO_SCTP : (uint8_t)rnd; if (flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_UDP || flow->nw_proto == IPPROTO_SCTP) { flow->tp_src = (OVS_FORCE ovs_be16)random_uint16(); flow->tp_dst = (OVS_FORCE ovs_be16)random_uint16(); } } } /* Masks the fields in 'wc' that are used by the flow hash 'fields'. */ void flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc, enum nx_hash_fields fields) { switch (fields) { case NX_HASH_FIELDS_ETH_SRC: memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src); break; case NX_HASH_FIELDS_SYMMETRIC_L4: memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src); memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src); memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst); } if (is_ip_any(flow)) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); flow_unwildcard_tp_ports(flow, wc); } wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); break; case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP: if (is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP) { memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); } /* no break */ case NX_HASH_FIELDS_SYMMETRIC_L3L4: if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src); memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst); } else { break; /* non-IP flow */ } memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); if (flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_SCTP) { memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); } break; default: OVS_NOT_REACHED(); } } /* Hashes the portions of 'flow' designated by 'fields'. */ uint32_t flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields, uint16_t basis) { switch (fields) { case NX_HASH_FIELDS_ETH_SRC: return jhash_bytes(&flow->dl_src, sizeof flow->dl_src, basis); case NX_HASH_FIELDS_SYMMETRIC_L4: return flow_hash_symmetric_l4(flow, basis); case NX_HASH_FIELDS_SYMMETRIC_L3L4: return flow_hash_symmetric_l3l4(flow, basis, false); case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP: return flow_hash_symmetric_l3l4(flow, basis, true); } OVS_NOT_REACHED(); } /* Returns a string representation of 'fields'. */ const char * flow_hash_fields_to_str(enum nx_hash_fields fields) { switch (fields) { case NX_HASH_FIELDS_ETH_SRC: return "eth_src"; case NX_HASH_FIELDS_SYMMETRIC_L4: return "symmetric_l4"; case NX_HASH_FIELDS_SYMMETRIC_L3L4: return "symmetric_l3l4"; case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP: return "symmetric_l3l4+udp"; default: return ""; } } /* Returns true if the value of 'fields' is supported. Otherwise false. */ bool flow_hash_fields_valid(enum nx_hash_fields fields) { return fields == NX_HASH_FIELDS_ETH_SRC || fields == NX_HASH_FIELDS_SYMMETRIC_L4 || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4 || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP; } /* Returns a hash value for the bits of 'flow' that are active based on * 'wc', given 'basis'. */ uint32_t flow_hash_in_wildcards(const struct flow *flow, const struct flow_wildcards *wc, uint32_t basis) { const uint64_t *wc_u64 = (const uint64_t *) &wc->masks; const uint64_t *flow_u64 = (const uint64_t *) flow; uint32_t hash; size_t i; hash = basis; for (i = 0; i < FLOW_U64S; i++) { hash = hash_add64(hash, flow_u64[i] & wc_u64[i]); } return hash_finish(hash, 8 * FLOW_U64S); } /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an * OpenFlow 1.0 "dl_vlan" value: * * - If it is in the range 0...4095, 'flow->vlan_tci' is set to match * that VLAN. Any existing PCP match is unchanged (it becomes 0 if * 'flow' previously matched packets without a VLAN header). * * - If it is OFP_VLAN_NONE, 'flow->vlan_tci' is set to match a packet * without a VLAN tag. * * - Other values of 'vid' should not be used. */ void flow_set_dl_vlan(struct flow *flow, ovs_be16 vid) { if (vid == htons(OFP10_VLAN_NONE)) { flow->vlan_tci = htons(0); } else { vid &= htons(VLAN_VID_MASK); flow->vlan_tci &= ~htons(VLAN_VID_MASK); flow->vlan_tci |= htons(VLAN_CFI) | vid; } } /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID * plus CFI). */ void flow_set_vlan_vid(struct flow *flow, ovs_be16 vid) { ovs_be16 mask = htons(VLAN_VID_MASK | VLAN_CFI); flow->vlan_tci &= ~mask; flow->vlan_tci |= vid & mask; } /* Sets the VLAN PCP that 'flow' matches to 'pcp', which should be in the * range 0...7. * * This function has no effect on the VLAN ID that 'flow' matches. * * After calling this function, 'flow' will not match packets without a VLAN * header. */ void flow_set_vlan_pcp(struct flow *flow, uint8_t pcp) { pcp &= 0x07; flow->vlan_tci &= ~htons(VLAN_PCP_MASK); flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI); } /* Returns the number of MPLS LSEs present in 'flow' * * Returns 0 if the 'dl_type' of 'flow' is not an MPLS ethernet type. * Otherwise traverses 'flow''s MPLS label stack stopping at the * first entry that has the BoS bit set. If no such entry exists then * the maximum number of LSEs that can be stored in 'flow' is returned. */ int flow_count_mpls_labels(const struct flow *flow, struct flow_wildcards *wc) { /* dl_type is always masked. */ if (eth_type_mpls(flow->dl_type)) { int i; int cnt; cnt = 0; for (i = 0; i < FLOW_MAX_MPLS_LABELS; i++) { if (wc) { wc->masks.mpls_lse[i] |= htonl(MPLS_BOS_MASK); } if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) { return i + 1; } if (flow->mpls_lse[i]) { cnt++; } } return cnt; } else { return 0; } } /* Returns the number consecutive of MPLS LSEs, starting at the * innermost LSE, that are common in 'a' and 'b'. * * 'an' must be flow_count_mpls_labels(a). * 'bn' must be flow_count_mpls_labels(b). */ int flow_count_common_mpls_labels(const struct flow *a, int an, const struct flow *b, int bn, struct flow_wildcards *wc) { int min_n = MIN(an, bn); if (min_n == 0) { return 0; } else { int common_n = 0; int a_last = an - 1; int b_last = bn - 1; int i; for (i = 0; i < min_n; i++) { if (wc) { wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX; wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX; } if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) { break; } else { common_n++; } } return common_n; } } /* Adds a new outermost MPLS label to 'flow' and changes 'flow''s Ethernet type * to 'mpls_eth_type', which must be an MPLS Ethertype. * * If the new label is the first MPLS label in 'flow', it is generated as; * * - label: 2, if 'flow' is IPv6, otherwise 0. * * - TTL: IPv4 or IPv6 TTL, if present and nonzero, otherwise 64. * * - TC: IPv4 or IPv6 TOS, if present, otherwise 0. * * - BoS: 1. * * If the new label is the second or later label MPLS label in 'flow', it is * generated as; * * - label: Copied from outer label. * * - TTL: Copied from outer label. * * - TC: Copied from outer label. * * - BoS: 0. * * 'n' must be flow_count_mpls_labels(flow). 'n' must be less than * FLOW_MAX_MPLS_LABELS (because otherwise flow->mpls_lse[] would overflow). */ void flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, struct flow_wildcards *wc, bool clear_flow_L3) { ovs_assert(eth_type_mpls(mpls_eth_type)); ovs_assert(n < FLOW_MAX_MPLS_LABELS); if (n) { int i; if (wc) { memset(&wc->masks.mpls_lse, 0xff, sizeof *wc->masks.mpls_lse * n); } for (i = n; i >= 1; i--) { flow->mpls_lse[i] = flow->mpls_lse[i - 1]; } flow->mpls_lse[0] = (flow->mpls_lse[1] & htonl(~MPLS_BOS_MASK)); } else { int label = 0; /* IPv4 Explicit Null. */ int tc = 0; int ttl = 64; if (flow->dl_type == htons(ETH_TYPE_IPV6)) { label = 2; } if (is_ip_any(flow)) { tc = (flow->nw_tos & IP_DSCP_MASK) >> 2; if (wc) { wc->masks.nw_tos |= IP_DSCP_MASK; wc->masks.nw_ttl = 0xff; } if (flow->nw_ttl) { ttl = flow->nw_ttl; } } flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); if (clear_flow_L3) { /* Clear all L3 and L4 fields and dp_hash. */ BUILD_ASSERT(FLOW_WC_SEQ == 35); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); flow->dp_hash = 0; } } flow->dl_type = mpls_eth_type; } /* Tries to remove the outermost MPLS label from 'flow'. Returns true if * successful, false otherwise. On success, sets 'flow''s Ethernet type to * 'eth_type'. * * 'n' must be flow_count_mpls_labels(flow). */ bool flow_pop_mpls(struct flow *flow, int n, ovs_be16 eth_type, struct flow_wildcards *wc) { int i; if (n == 0) { /* Nothing to pop. */ return false; } else if (n == FLOW_MAX_MPLS_LABELS) { if (wc) { wc->masks.mpls_lse[n - 1] |= htonl(MPLS_BOS_MASK); } if (!(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) { /* Can't pop because don't know what to fill in mpls_lse[n - 1]. */ return false; } } if (wc) { memset(&wc->masks.mpls_lse[1], 0xff, sizeof *wc->masks.mpls_lse * (n - 1)); } for (i = 1; i < n; i++) { flow->mpls_lse[i - 1] = flow->mpls_lse[i]; } flow->mpls_lse[n - 1] = 0; flow->dl_type = eth_type; return true; } /* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted * as an OpenFlow 1.1 "mpls_label" value. */ void flow_set_mpls_label(struct flow *flow, int idx, ovs_be32 label) { set_mpls_lse_label(&flow->mpls_lse[idx], label); } /* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the * range 0...255. */ void flow_set_mpls_ttl(struct flow *flow, int idx, uint8_t ttl) { set_mpls_lse_ttl(&flow->mpls_lse[idx], ttl); } /* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the * range 0...7. */ void flow_set_mpls_tc(struct flow *flow, int idx, uint8_t tc) { set_mpls_lse_tc(&flow->mpls_lse[idx], tc); } /* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */ void flow_set_mpls_bos(struct flow *flow, int idx, uint8_t bos) { set_mpls_lse_bos(&flow->mpls_lse[idx], bos); } /* Sets the entire MPLS LSE. */ void flow_set_mpls_lse(struct flow *flow, int idx, ovs_be32 lse) { flow->mpls_lse[idx] = lse; } static size_t flow_compose_l4(struct dp_packet *p, const struct flow *flow) { size_t l4_len = 0; if (!(flow->nw_frag & FLOW_NW_FRAG_ANY) || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (flow->nw_proto == IPPROTO_TCP) { struct tcp_header *tcp; l4_len = sizeof *tcp; tcp = dp_packet_put_zeros(p, l4_len); tcp->tcp_src = flow->tp_src; tcp->tcp_dst = flow->tp_dst; tcp->tcp_ctl = TCP_CTL(ntohs(flow->tcp_flags), 5); } else if (flow->nw_proto == IPPROTO_UDP) { struct udp_header *udp; l4_len = sizeof *udp; udp = dp_packet_put_zeros(p, l4_len); udp->udp_src = flow->tp_src; udp->udp_dst = flow->tp_dst; } else if (flow->nw_proto == IPPROTO_SCTP) { struct sctp_header *sctp; l4_len = sizeof *sctp; sctp = dp_packet_put_zeros(p, l4_len); sctp->sctp_src = flow->tp_src; sctp->sctp_dst = flow->tp_dst; } else if (flow->nw_proto == IPPROTO_ICMP) { struct icmp_header *icmp; l4_len = sizeof *icmp; icmp = dp_packet_put_zeros(p, l4_len); icmp->icmp_type = ntohs(flow->tp_src); icmp->icmp_code = ntohs(flow->tp_dst); icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN); } else if (flow->nw_proto == IPPROTO_IGMP) { struct igmp_header *igmp; l4_len = sizeof *igmp; igmp = dp_packet_put_zeros(p, l4_len); igmp->igmp_type = ntohs(flow->tp_src); igmp->igmp_code = ntohs(flow->tp_dst); put_16aligned_be32(&igmp->group, flow->igmp_group_ip4); igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN); } else if (flow->nw_proto == IPPROTO_ICMPV6) { struct icmp6_hdr *icmp; l4_len = sizeof *icmp; icmp = dp_packet_put_zeros(p, l4_len); icmp->icmp6_type = ntohs(flow->tp_src); icmp->icmp6_code = ntohs(flow->tp_dst); if (icmp->icmp6_code == 0 && (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { struct in6_addr *nd_target; struct ovs_nd_opt *nd_opt; l4_len += sizeof *nd_target; nd_target = dp_packet_put_zeros(p, sizeof *nd_target); *nd_target = flow->nd_target; if (!eth_addr_is_zero(flow->arp_sha)) { l4_len += 8; nd_opt = dp_packet_put_zeros(p, 8); nd_opt->nd_opt_len = 1; nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; nd_opt->nd_opt_mac = flow->arp_sha; } if (!eth_addr_is_zero(flow->arp_tha)) { l4_len += 8; nd_opt = dp_packet_put_zeros(p, 8); nd_opt->nd_opt_len = 1; nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_mac = flow->arp_tha; } } icmp->icmp6_cksum = (OVS_FORCE uint16_t) csum(icmp, (char *)dp_packet_tail(p) - (char *)icmp); } } return l4_len; } /* Puts into 'b' a packet that flow_extract() would parse as having the given * 'flow'. * * (This is useful only for testing, obviously, and the packet isn't really * valid. It hasn't got some checksums filled in, for one, and lots of fields * are just zeroed.) */ void flow_compose(struct dp_packet *p, const struct flow *flow) { size_t l4_len; /* eth_compose() sets l3 pointer and makes sure it is 32-bit aligned. */ eth_compose(p, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0); if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) { struct eth_header *eth = dp_packet_l2(p); eth->eth_type = htons(dp_packet_size(p)); return; } if (flow->vlan_tci & htons(VLAN_CFI)) { eth_push_vlan(p, htons(ETH_TYPE_VLAN), flow->vlan_tci); } if (flow->dl_type == htons(ETH_TYPE_IP)) { struct ip_header *ip; ip = dp_packet_put_zeros(p, sizeof *ip); ip->ip_ihl_ver = IP_IHL_VER(5, 4); ip->ip_tos = flow->nw_tos; ip->ip_ttl = flow->nw_ttl; ip->ip_proto = flow->nw_proto; put_16aligned_be32(&ip->ip_src, flow->nw_src); put_16aligned_be32(&ip->ip_dst, flow->nw_dst); if (flow->nw_frag & FLOW_NW_FRAG_ANY) { ip->ip_frag_off |= htons(IP_MORE_FRAGMENTS); if (flow->nw_frag & FLOW_NW_FRAG_LATER) { ip->ip_frag_off |= htons(100); } } dp_packet_set_l4(p, dp_packet_tail(p)); l4_len = flow_compose_l4(p, flow); ip = dp_packet_l3(p); ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len); ip->ip_csum = csum(ip, sizeof *ip); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { struct ovs_16aligned_ip6_hdr *nh; nh = dp_packet_put_zeros(p, sizeof *nh); put_16aligned_be32(&nh->ip6_flow, htonl(6 << 28) | htonl(flow->nw_tos << 20) | flow->ipv6_label); nh->ip6_hlim = flow->nw_ttl; nh->ip6_nxt = flow->nw_proto; memcpy(&nh->ip6_src, &flow->ipv6_src, sizeof(nh->ip6_src)); memcpy(&nh->ip6_dst, &flow->ipv6_dst, sizeof(nh->ip6_dst)); dp_packet_set_l4(p, dp_packet_tail(p)); l4_len = flow_compose_l4(p, flow); nh = dp_packet_l3(p); nh->ip6_plen = htons(l4_len); } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { struct arp_eth_header *arp; arp = dp_packet_put_zeros(p, sizeof *arp); dp_packet_set_l3(p, arp); arp->ar_hrd = htons(1); arp->ar_pro = htons(ETH_TYPE_IP); arp->ar_hln = ETH_ADDR_LEN; arp->ar_pln = 4; arp->ar_op = htons(flow->nw_proto); if (flow->nw_proto == ARP_OP_REQUEST || flow->nw_proto == ARP_OP_REPLY) { put_16aligned_be32(&arp->ar_spa, flow->nw_src); put_16aligned_be32(&arp->ar_tpa, flow->nw_dst); arp->ar_sha = flow->arp_sha; arp->ar_tha = flow->arp_tha; } } if (eth_type_mpls(flow->dl_type)) { int n; p->l2_5_ofs = p->l3_ofs; for (n = 1; n < FLOW_MAX_MPLS_LABELS; n++) { if (flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK)) { break; } } while (n > 0) { push_mpls(p, flow->dl_type, flow->mpls_lse[--n]); } } } /* Compressed flow. */ /* Completes an initialization of 'dst' as a miniflow copy of 'src' begun by * the caller. The caller must have already computed 'dst->map' properly to * indicate the significant uint64_t elements of 'src'. * * Normally the significant elements are the ones that are non-zero. However, * when a miniflow is initialized from a (mini)mask, the values can be zeroes, * so that the flow and mask always have the same maps. */ void miniflow_init(struct miniflow *dst, const struct flow *src) { uint64_t *dst_u64 = miniflow_values(dst); size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, dst->map) { *dst_u64++ = flow_u64_value(src, idx); } } /* Initialize the maps of 'flow' from 'src'. */ void miniflow_map_init(struct miniflow *flow, const struct flow *src) { /* Initialize map, counting the number of nonzero elements. */ flowmap_init(&flow->map); for (size_t i = 0; i < FLOW_U64S; i++) { if (flow_u64_value(src, i)) { flowmap_set(&flow->map, i, 1); } } } /* Allocates 'n' count of miniflows, consecutive in memory, initializing the * map of each from 'src'. * Returns the size of the miniflow data. */ size_t miniflow_alloc(struct miniflow *dsts[], size_t n, const struct miniflow *src) { size_t n_values = miniflow_n_values(src); size_t data_size = MINIFLOW_VALUES_SIZE(n_values); struct miniflow *dst = xmalloc(n * (sizeof *src + data_size)); size_t i; COVERAGE_INC(miniflow_malloc); for (i = 0; i < n; i++) { *dst = *src; /* Copy maps. */ dsts[i] = dst; dst += 1; /* Just past the maps. */ dst = (struct miniflow *)((uint64_t *)dst + n_values); /* Skip data. */ } return data_size; } /* Returns a miniflow copy of 'src'. The caller must eventually free() the * returned miniflow. */ struct miniflow * miniflow_create(const struct flow *src) { struct miniflow tmp; struct miniflow *dst; miniflow_map_init(&tmp, src); miniflow_alloc(&dst, 1, &tmp); miniflow_init(dst, src); return dst; } /* Initializes 'dst' as a copy of 'src'. The caller must have allocated * 'dst' to have inline space for 'n_values' data in 'src'. */ void miniflow_clone(struct miniflow *dst, const struct miniflow *src, size_t n_values) { *dst = *src; /* Copy maps. */ memcpy(miniflow_values(dst), miniflow_get_values(src), MINIFLOW_VALUES_SIZE(n_values)); } /* Initializes 'dst' as a copy of 'src'. */ void miniflow_expand(const struct miniflow *src, struct flow *dst) { memset(dst, 0, sizeof *dst); flow_union_with_miniflow(dst, src); } /* Returns true if 'a' and 'b' are equal miniflows, false otherwise. */ bool miniflow_equal(const struct miniflow *a, const struct miniflow *b) { const uint64_t *ap = miniflow_get_values(a); const uint64_t *bp = miniflow_get_values(b); /* This is mostly called after a matching hash, so it is highly likely that * the maps are equal as well. */ if (OVS_LIKELY(flowmap_equal(a->map, b->map))) { return !memcmp(ap, bp, miniflow_n_values(a) * sizeof *ap); } else { size_t idx; FLOWMAP_FOR_EACH_INDEX (idx, flowmap_or(a->map, b->map)) { if ((flowmap_is_set(&a->map, idx) ? *ap++ : 0) != (flowmap_is_set(&b->map, idx) ? *bp++ : 0)) { return false; } } } return true; } /* Returns false if 'a' and 'b' differ at the places where there are 1-bits * in 'mask', true otherwise. */ bool miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b, const struct minimask *mask) { const uint64_t *p = miniflow_get_values(&mask->masks); size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, mask->masks.map) { if ((miniflow_get(a, idx) ^ miniflow_get(b, idx)) & *p++) { return false; } } return true; } /* Returns true if 'a' and 'b' are equal at the places where there are 1-bits * in 'mask', false if they differ. */ bool miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b, const struct minimask *mask) { const uint64_t *p = miniflow_get_values(&mask->masks); size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, mask->masks.map) { if ((miniflow_get(a, idx) ^ flow_u64_value(b, idx)) & *p++) { return false; } } return true; } void minimask_init(struct minimask *mask, const struct flow_wildcards *wc) { miniflow_init(&mask->masks, &wc->masks); } /* Returns a minimask copy of 'wc'. The caller must eventually free the * returned minimask with free(). */ struct minimask * minimask_create(const struct flow_wildcards *wc) { return (struct minimask *)miniflow_create(&wc->masks); } /* Initializes 'dst_' as the bit-wise "and" of 'a_' and 'b_'. * * The caller must provide room for FLOW_U64S "uint64_t"s in 'storage', which * must follow '*dst_' in memory, for use by 'dst_'. The caller must *not* * free 'dst_' free(). */ void minimask_combine(struct minimask *dst_, const struct minimask *a_, const struct minimask *b_, uint64_t storage[FLOW_U64S]) { struct miniflow *dst = &dst_->masks; uint64_t *dst_values = storage; const struct miniflow *a = &a_->masks; const struct miniflow *b = &b_->masks; size_t idx; flowmap_init(&dst->map); FLOWMAP_FOR_EACH_INDEX(idx, flowmap_and(a->map, b->map)) { /* Both 'a' and 'b' have non-zero data at 'idx'. */ uint64_t mask = *miniflow_get__(a, idx) & *miniflow_get__(b, idx); if (mask) { flowmap_set(&dst->map, idx, 1); *dst_values++ = mask; } } } /* Initializes 'wc' as a copy of 'mask'. */ void minimask_expand(const struct minimask *mask, struct flow_wildcards *wc) { miniflow_expand(&mask->masks, &wc->masks); } /* Returns true if 'a' and 'b' are the same flow mask, false otherwise. * Minimasks may not have zero data values, so for the minimasks to be the * same, they need to have the same map and the same data values. */ bool minimask_equal(const struct minimask *a, const struct minimask *b) { return !memcmp(a, b, sizeof *a + MINIFLOW_VALUES_SIZE(miniflow_n_values(&a->masks))); } /* Returns true if at least one bit matched by 'b' is wildcarded by 'a', * false otherwise. */ bool minimask_has_extra(const struct minimask *a, const struct minimask *b) { const uint64_t *bp = miniflow_get_values(&b->masks); size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, b->masks.map) { uint64_t b_u64 = *bp++; /* 'b_u64' is non-zero, check if the data in 'a' is either zero * or misses some of the bits in 'b_u64'. */ if (!MINIFLOW_IN_MAP(&a->masks, idx) || ((*miniflow_get__(&a->masks, idx) & b_u64) != b_u64)) { return true; /* 'a' wildcards some bits 'b' doesn't. */ } } return false; } openvswitch-2.5.9/lib/PaxHeaders.82075/rtbsd.c0000644000000000000000000000013213534540071015674 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801425.001855048 openvswitch-2.5.9/lib/rtbsd.c0000644000175000017500000001327313534540071017370 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013 Gaetano Catalli. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "rtbsd.h" #include #include #include #include #include #include #include "coverage.h" #include "socket-util.h" #include "poll-loop.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(rtbsd); COVERAGE_DEFINE(rtbsd_changed); static struct ovs_mutex rtbsd_mutex = OVS_MUTEX_INITIALIZER; /* PF_ROUTE socket. */ static int notify_sock = -1; /* All registered notifiers. */ static struct ovs_list all_notifiers = OVS_LIST_INITIALIZER(&all_notifiers); static void rtbsd_report_change(const struct if_msghdr *) OVS_REQUIRES(rtbsd_mutex); static void rtbsd_report_notify_error(void) OVS_REQUIRES(rtbsd_mutex); /* Registers 'cb' to be called with auxiliary data 'aux' with network device * change notifications. The notifier is stored in 'notifier', which the * caller must not modify or free. * * Returns 0 if successful, otherwise a positive errno value. */ int rtbsd_notifier_register(struct rtbsd_notifier *notifier, rtbsd_notify_func *cb, void *aux) OVS_EXCLUDED(rtbsd_mutex) { int error = 0; ovs_mutex_lock(&rtbsd_mutex); if (notify_sock < 0) { notify_sock = socket(PF_ROUTE, SOCK_RAW, 0); if (notify_sock < 0) { VLOG_WARN("could not create PF_ROUTE socket: %s", ovs_strerror(errno)); error = errno; goto out; } error = set_nonblocking(notify_sock); if (error) { VLOG_WARN("error set_nonblocking PF_ROUTE socket: %s", ovs_strerror(error)); goto out; } } list_push_back(&all_notifiers, ¬ifier->node); notifier->cb = cb; notifier->aux = aux; out: ovs_mutex_unlock(&rtbsd_mutex); return error; } /* Cancels notification on 'notifier', which must have previously been * registered with rtbsd_notifier_register(). */ void rtbsd_notifier_unregister(struct rtbsd_notifier *notifier) OVS_EXCLUDED(rtbsd_mutex) { ovs_mutex_lock(&rtbsd_mutex); list_remove(¬ifier->node); if (list_is_empty(&all_notifiers)) { close(notify_sock); notify_sock = -1; } ovs_mutex_unlock(&rtbsd_mutex); } /* Calls all of the registered notifiers, passing along any as-yet-unreported * netdev change events. */ void rtbsd_notifier_run(void) OVS_EXCLUDED(rtbsd_mutex) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); struct if_msghdr msg; ovs_mutex_lock(&rtbsd_mutex); if (notify_sock < 0) { ovs_mutex_unlock(&rtbsd_mutex); return; } for (;;) { int retval; msg.ifm_type = RTM_IFINFO; msg.ifm_version = RTM_VERSION; //XXX check if necessary /* read from PF_ROUTE socket */ retval = read(notify_sock, (char *)&msg, sizeof(msg)); if (retval >= 0) { /* received packet from PF_ROUTE socket * XXX check for bad packets */ switch (msg.ifm_type) { case RTM_IFINFO: /* Since RTM_IFANNOUNCE messages are smaller than RTM_IFINFO * messages, the same buffer may be used. */ case RTM_IFANNOUNCE: rtbsd_report_change(&msg); break; default: break; } } else if (errno == EAGAIN) { ovs_mutex_unlock(&rtbsd_mutex); return; } else { if (errno == ENOBUFS) { VLOG_WARN_RL(&rl, "PF_ROUTE receive buffer overflowed"); } else { VLOG_WARN_RL(&rl, "error reading PF_ROUTE socket: %s", ovs_strerror(errno)); } rtbsd_report_notify_error(); } } } /* Causes poll_block() to wake up when network device change notifications are * ready. */ void rtbsd_notifier_wait(void) OVS_EXCLUDED(rtbsd_mutex) { ovs_mutex_lock(&rtbsd_mutex); if (notify_sock >= 0) { poll_fd_wait(notify_sock, POLLIN); } ovs_mutex_unlock(&rtbsd_mutex); } static void rtbsd_report_change(const struct if_msghdr *msg) OVS_REQUIRES(rtbsd_mutex) { struct rtbsd_notifier *notifier; struct rtbsd_change change; const struct if_announcemsghdr *ahdr; COVERAGE_INC(rtbsd_changed); change.msg_type = msg->ifm_type; //XXX change.master_ifindex = 0; //XXX switch (msg->ifm_type) { case RTM_IFINFO: change.if_index = msg->ifm_index; if_indextoname(msg->ifm_index, change.if_name); break; case RTM_IFANNOUNCE: ahdr = (const struct if_announcemsghdr *) msg; change.if_index = ahdr->ifan_index; strncpy(change.if_name, ahdr->ifan_name, IF_NAMESIZE); break; } LIST_FOR_EACH (notifier, node, &all_notifiers) { notifier->cb(&change, notifier->aux); } } /* If an error occurs the notifiers' callbacks are called with NULL changes */ static void rtbsd_report_notify_error(void) OVS_REQUIRES(rtbsd_mutex) { struct rtbsd_notifier *notifier; LIST_FOR_EACH (notifier, node, &all_notifiers) { notifier->cb(NULL, notifier->aux); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-data.h0000644000000000000000000000013213534540071016607 xustar0030 mtime=1567801401.549682257 30 atime=1567801402.093686252 30 ctime=1567801424.833853809 openvswitch-2.5.9/lib/ovsdb-data.h0000644000175000017500000002550213534540071020301 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_DATA_H #define OVSDB_DATA_H 1 #include #include "compiler.h" #include "ovsdb-types.h" #include "shash.h" struct ds; struct ovsdb_symbol_table; struct smap; /* One value of an atomic type (given by enum ovs_atomic_type). */ union ovsdb_atom { int64_t integer; double real; bool boolean; char *string; struct uuid uuid; }; void ovsdb_atom_init_default(union ovsdb_atom *, enum ovsdb_atomic_type); const union ovsdb_atom *ovsdb_atom_default(enum ovsdb_atomic_type); bool ovsdb_atom_is_default(const union ovsdb_atom *, enum ovsdb_atomic_type); void ovsdb_atom_clone(union ovsdb_atom *, const union ovsdb_atom *, enum ovsdb_atomic_type); void ovsdb_atom_swap(union ovsdb_atom *, union ovsdb_atom *); /* Returns false if ovsdb_atom_destroy() is a no-op when it is applied to an * initialized atom of the given 'type', true if ovsdb_atom_destroy() actually * does something. * * This can be used to avoid calling ovsdb_atom_destroy() for each element in * an array of homogeneous atoms. (It's not worthwhile for a single atom.) */ static inline bool ovsdb_atom_needs_destruction(enum ovsdb_atomic_type type) { return type == OVSDB_TYPE_STRING; } /* Frees the contents of 'atom', which must have the specified 'type'. * * This does not actually call free(atom). If necessary, the caller must be * responsible for that. */ static inline void ovsdb_atom_destroy(union ovsdb_atom *atom, enum ovsdb_atomic_type type) { if (type == OVSDB_TYPE_STRING) { free(atom->string); } } uint32_t ovsdb_atom_hash(const union ovsdb_atom *, enum ovsdb_atomic_type, uint32_t basis); int ovsdb_atom_compare_3way(const union ovsdb_atom *, const union ovsdb_atom *, enum ovsdb_atomic_type); /* Returns true if 'a' and 'b', which are both of type 'type', has the same * contents, false if their contents differ. */ static inline bool ovsdb_atom_equals(const union ovsdb_atom *a, const union ovsdb_atom *b, enum ovsdb_atomic_type type) { return !ovsdb_atom_compare_3way(a, b, type); } struct ovsdb_error *ovsdb_atom_from_json(union ovsdb_atom *, const struct ovsdb_base_type *, const struct json *, struct ovsdb_symbol_table *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_atom_to_json(const union ovsdb_atom *, enum ovsdb_atomic_type); char *ovsdb_atom_from_string(union ovsdb_atom *, const struct ovsdb_base_type *, const char *, struct ovsdb_symbol_table *) OVS_WARN_UNUSED_RESULT; void ovsdb_atom_to_string(const union ovsdb_atom *, enum ovsdb_atomic_type, struct ds *); void ovsdb_atom_to_bare(const union ovsdb_atom *, enum ovsdb_atomic_type, struct ds *); struct ovsdb_error *ovsdb_atom_check_constraints( const union ovsdb_atom *, const struct ovsdb_base_type *) OVS_WARN_UNUSED_RESULT; /* An instance of an OVSDB type (given by struct ovsdb_type). * * - The 'keys' must be unique and in sorted order. Most functions that modify * an ovsdb_datum maintain these invariants. Functions that don't maintain * the invariants have names that end in "_unsafe". Use ovsdb_datum_sort() * to check and restore these invariants. * * - 'n' is constrained by the ovsdb_type's 'n_min' and 'n_max'. * * If 'n' is nonzero, then 'keys' points to an array of 'n' atoms of the type * specified by the ovsdb_type's 'key_type'. (Otherwise, 'keys' should be * null.) * * If 'n' is nonzero and the ovsdb_type's 'value_type' is not * OVSDB_TYPE_VOID, then 'values' points to an array of 'n' atoms of the type * specified by the 'value_type'. (Otherwise, 'values' should be null.) * * Thus, for 'n' > 0, 'keys' will always be nonnull and 'values' will be * nonnull only for "map" types. */ struct ovsdb_datum { unsigned int n; /* Number of 'keys' and 'values'. */ union ovsdb_atom *keys; /* Each of the ovsdb_type's 'key_type'. */ union ovsdb_atom *values; /* Each of the ovsdb_type's 'value_type'. */ }; /* Basics. */ void ovsdb_datum_init_empty(struct ovsdb_datum *); void ovsdb_datum_init_default(struct ovsdb_datum *, const struct ovsdb_type *); bool ovsdb_datum_is_default(const struct ovsdb_datum *, const struct ovsdb_type *); const struct ovsdb_datum *ovsdb_datum_default(const struct ovsdb_type *); void ovsdb_datum_clone(struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *); void ovsdb_datum_destroy(struct ovsdb_datum *, const struct ovsdb_type *); void ovsdb_datum_swap(struct ovsdb_datum *, struct ovsdb_datum *); /* Checking and maintaining invariants. */ struct ovsdb_error *ovsdb_datum_sort(struct ovsdb_datum *, enum ovsdb_atomic_type key_type) OVS_WARN_UNUSED_RESULT; void ovsdb_datum_sort_assert(struct ovsdb_datum *, enum ovsdb_atomic_type key_type); size_t ovsdb_datum_sort_unique(struct ovsdb_datum *, enum ovsdb_atomic_type key_type, enum ovsdb_atomic_type value_type); struct ovsdb_error *ovsdb_datum_check_constraints( const struct ovsdb_datum *, const struct ovsdb_type *) OVS_WARN_UNUSED_RESULT; /* Type conversion. */ struct ovsdb_error *ovsdb_datum_from_json(struct ovsdb_datum *, const struct ovsdb_type *, const struct json *, struct ovsdb_symbol_table *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_datum_to_json(const struct ovsdb_datum *, const struct ovsdb_type *); char *ovsdb_datum_from_string(struct ovsdb_datum *, const struct ovsdb_type *, const char *, struct ovsdb_symbol_table *) OVS_WARN_UNUSED_RESULT; void ovsdb_datum_to_string(const struct ovsdb_datum *, const struct ovsdb_type *, struct ds *); void ovsdb_datum_to_bare(const struct ovsdb_datum *, const struct ovsdb_type *, struct ds *); void ovsdb_datum_from_smap(struct ovsdb_datum *, struct smap *); /* Comparison. */ uint32_t ovsdb_datum_hash(const struct ovsdb_datum *, const struct ovsdb_type *, uint32_t basis); int ovsdb_datum_compare_3way(const struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *); bool ovsdb_datum_equals(const struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *); /* Search. */ unsigned int ovsdb_datum_find_key(const struct ovsdb_datum *, const union ovsdb_atom *key, enum ovsdb_atomic_type key_type); unsigned int ovsdb_datum_find_key_value(const struct ovsdb_datum *, const union ovsdb_atom *key, enum ovsdb_atomic_type key_type, const union ovsdb_atom *value, enum ovsdb_atomic_type value_type); /* Set operations. */ bool ovsdb_datum_includes_all(const struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *); bool ovsdb_datum_excludes_all(const struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *); void ovsdb_datum_union(struct ovsdb_datum *, const struct ovsdb_datum *, const struct ovsdb_type *, bool replace); void ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type, const struct ovsdb_datum *b, const struct ovsdb_type *b_type); /* Raw operations that may not maintain the invariants. */ void ovsdb_datum_remove_unsafe(struct ovsdb_datum *, size_t idx, const struct ovsdb_type *); void ovsdb_datum_add_unsafe(struct ovsdb_datum *, const union ovsdb_atom *key, const union ovsdb_atom *value, const struct ovsdb_type *); /* Type checking. */ static inline bool ovsdb_datum_conforms_to_type(const struct ovsdb_datum *datum, const struct ovsdb_type *type) { return datum->n >= type->n_min && datum->n <= type->n_max; } /* A table mapping from names to data items. Currently the data items are * always UUIDs; perhaps this will be expanded in the future. */ struct ovsdb_symbol_table { struct shash sh; /* Maps from name to struct ovsdb_symbol *. */ }; struct ovsdb_symbol { struct uuid uuid; /* The UUID that the symbol represents. */ bool created; /* Already used to create row? */ bool strong_ref; /* Parsed a strong reference to this row? */ bool weak_ref; /* Parsed a weak reference to this row? */ }; struct ovsdb_symbol_table *ovsdb_symbol_table_create(void); void ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *); struct ovsdb_symbol *ovsdb_symbol_table_get(const struct ovsdb_symbol_table *, const char *name); struct ovsdb_symbol *ovsdb_symbol_table_put(struct ovsdb_symbol_table *, const char *name, const struct uuid *, bool used); struct ovsdb_symbol *ovsdb_symbol_table_insert(struct ovsdb_symbol_table *, const char *name); /* Tokenization * * Used by ovsdb_atom_from_string() and ovsdb_datum_from_string(). */ char *ovsdb_token_parse(const char **, char **outp) OVS_WARN_UNUSED_RESULT; bool ovsdb_token_is_delim(unsigned char); #endif /* ovsdb-data.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/match.c0000644000000000000000000000013213534540071015652 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.761853278 openvswitch-2.5.9/lib/match.c0000644000175000017500000012433413534540071017347 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "match.h" #include #include "byte-order.h" #include "dynamic-string.h" #include "ofp-util.h" #include "packets.h" #include "tun-metadata.h" /* Converts the flow in 'flow' into a match in 'match', with the given * 'wildcards'. */ void match_init(struct match *match, const struct flow *flow, const struct flow_wildcards *wc) { match->flow = *flow; match->wc = *wc; match_zero_wildcarded_fields(match); memset(&match->tun_md, 0, sizeof match->tun_md); } /* Converts a flow into a match. It sets the wildcard masks based on * the packet contents. It will not set the mask for fields that do not * make sense for the packet type. */ void match_wc_init(struct match *match, const struct flow *flow) { match->flow = *flow; flow_wildcards_init_for_packet(&match->wc, flow); WC_MASK_FIELD(&match->wc, regs); WC_MASK_FIELD(&match->wc, metadata); memset(&match->tun_md, 0, sizeof match->tun_md); } /* Initializes 'match' as a "catch-all" match that matches every packet. */ void match_init_catchall(struct match *match) { memset(&match->flow, 0, sizeof match->flow); flow_wildcards_init_catchall(&match->wc); memset(&match->tun_md, 0, sizeof match->tun_md); } /* For each bit or field wildcarded in 'match', sets the corresponding bit or * field in 'flow' to all-0-bits. It is important to maintain this invariant * in a match that might be inserted into a classifier. * * It is never necessary to call this function directly for a match that is * initialized or modified only by match_*() functions. It is useful to * restore the invariant in a match whose 'wc' member is modified by hand. */ void match_zero_wildcarded_fields(struct match *match) { flow_zero_wildcards(&match->flow, &match->wc); } void match_set_dp_hash(struct match *match, uint32_t value) { match_set_dp_hash_masked(match, value, UINT32_MAX); } void match_set_dp_hash_masked(struct match *match, uint32_t value, uint32_t mask) { match->wc.masks.dp_hash = mask; match->flow.dp_hash = value & mask; } void match_set_recirc_id(struct match *match, uint32_t value) { match->flow.recirc_id = value; match->wc.masks.recirc_id = UINT32_MAX; } void match_set_conj_id(struct match *match, uint32_t value) { match->flow.conj_id = value; match->wc.masks.conj_id = UINT32_MAX; } void match_set_reg(struct match *match, unsigned int reg_idx, uint32_t value) { match_set_reg_masked(match, reg_idx, value, UINT32_MAX); } void match_set_reg_masked(struct match *match, unsigned int reg_idx, uint32_t value, uint32_t mask) { ovs_assert(reg_idx < FLOW_N_REGS); flow_wildcards_set_reg_mask(&match->wc, reg_idx, mask); match->flow.regs[reg_idx] = value & mask; } void match_set_xreg(struct match *match, unsigned int xreg_idx, uint64_t value) { match_set_xreg_masked(match, xreg_idx, value, UINT64_MAX); } void match_set_xreg_masked(struct match *match, unsigned int xreg_idx, uint64_t value, uint64_t mask) { ovs_assert(xreg_idx < FLOW_N_XREGS); flow_wildcards_set_xreg_mask(&match->wc, xreg_idx, mask); flow_set_xreg(&match->flow, xreg_idx, value & mask); } void match_set_actset_output(struct match *match, ofp_port_t actset_output) { match->wc.masks.actset_output = u16_to_ofp(UINT16_MAX); match->flow.actset_output = actset_output; } void match_set_metadata(struct match *match, ovs_be64 metadata) { match_set_metadata_masked(match, metadata, OVS_BE64_MAX); } void match_set_metadata_masked(struct match *match, ovs_be64 metadata, ovs_be64 mask) { match->wc.masks.metadata = mask; match->flow.metadata = metadata & mask; } void match_set_tun_id(struct match *match, ovs_be64 tun_id) { match_set_tun_id_masked(match, tun_id, OVS_BE64_MAX); } void match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask) { match->wc.masks.tunnel.tun_id = mask; match->flow.tunnel.tun_id = tun_id & mask; } void match_set_tun_src(struct match *match, ovs_be32 src) { match_set_tun_src_masked(match, src, OVS_BE32_MAX); } void match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask) { match->wc.masks.tunnel.ip_src = mask; match->flow.tunnel.ip_src = src & mask; } void match_set_tun_dst(struct match *match, ovs_be32 dst) { match_set_tun_dst_masked(match, dst, OVS_BE32_MAX); } void match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask) { match->wc.masks.tunnel.ip_dst = mask; match->flow.tunnel.ip_dst = dst & mask; } void match_set_tun_ipv6_src(struct match *match, const struct in6_addr *src) { match->flow.tunnel.ipv6_src = *src; match->wc.masks.tunnel.ipv6_src = in6addr_exact; } void match_set_tun_ipv6_src_masked(struct match *match, const struct in6_addr *src, const struct in6_addr *mask) { match->flow.tunnel.ipv6_src = ipv6_addr_bitand(src, mask); match->wc.masks.tunnel.ipv6_src = *mask; } void match_set_tun_ipv6_dst(struct match *match, const struct in6_addr *dst) { match->flow.tunnel.ipv6_dst = *dst; match->wc.masks.tunnel.ipv6_dst = in6addr_exact; } void match_set_tun_ipv6_dst_masked(struct match *match, const struct in6_addr *dst, const struct in6_addr *mask) { match->flow.tunnel.ipv6_dst = ipv6_addr_bitand(dst, mask); match->wc.masks.tunnel.ipv6_dst = *mask; } void match_set_tun_ttl(struct match *match, uint8_t ttl) { match_set_tun_ttl_masked(match, ttl, UINT8_MAX); } void match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask) { match->wc.masks.tunnel.ip_ttl = mask; match->flow.tunnel.ip_ttl = ttl & mask; } void match_set_tun_tos(struct match *match, uint8_t tos) { match_set_tun_tos_masked(match, tos, UINT8_MAX); } void match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask) { match->wc.masks.tunnel.ip_tos = mask; match->flow.tunnel.ip_tos = tos & mask; } void match_set_tun_flags(struct match *match, uint16_t flags) { match_set_tun_flags_masked(match, flags, UINT16_MAX); } void match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask) { mask &= FLOW_TNL_PUB_F_MASK; match->wc.masks.tunnel.flags = mask; match->flow.tunnel.flags = flags & mask; } void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 mask) { match->wc.masks.tunnel.gbp_id = mask; match->flow.tunnel.gbp_id = gbp_id & mask; } void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id) { match_set_tun_gbp_id_masked(match, gbp_id, OVS_BE16_MAX); } void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask) { match->wc.masks.tunnel.gbp_flags = mask; match->flow.tunnel.gbp_flags = flags & mask; } void match_set_tun_gbp_flags(struct match *match, uint8_t flags) { match_set_tun_gbp_flags_masked(match, flags, UINT8_MAX); } void match_set_in_port(struct match *match, ofp_port_t ofp_port) { match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX); match->flow.in_port.ofp_port = ofp_port; } void match_set_skb_priority(struct match *match, uint32_t skb_priority) { match->wc.masks.skb_priority = UINT32_MAX; match->flow.skb_priority = skb_priority; } void match_set_pkt_mark(struct match *match, uint32_t pkt_mark) { match_set_pkt_mark_masked(match, pkt_mark, UINT32_MAX); } void match_set_pkt_mark_masked(struct match *match, uint32_t pkt_mark, uint32_t mask) { match->flow.pkt_mark = pkt_mark & mask; match->wc.masks.pkt_mark = mask; } void match_set_ct_state(struct match *match, uint32_t ct_state) { match_set_ct_state_masked(match, ct_state, UINT32_MAX); } void match_set_ct_state_masked(struct match *match, uint32_t ct_state, uint32_t mask) { match->flow.ct_state = ct_state & mask & UINT16_MAX; match->wc.masks.ct_state = mask & UINT16_MAX; } void match_set_ct_zone(struct match *match, uint16_t ct_zone) { match->flow.ct_zone = ct_zone; match->wc.masks.ct_zone = UINT16_MAX; } void match_set_ct_mark(struct match *match, uint32_t ct_mark) { match_set_ct_mark_masked(match, ct_mark, UINT32_MAX); } void match_set_ct_mark_masked(struct match *match, uint32_t ct_mark, uint32_t mask) { match->flow.ct_mark = ct_mark & mask; match->wc.masks.ct_mark = mask; } void match_set_ct_label(struct match *match, ovs_u128 ct_label) { ovs_u128 mask; mask.u64.lo = UINT64_MAX; mask.u64.hi = UINT64_MAX; match_set_ct_label_masked(match, ct_label, mask); } void match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask) { match->flow.ct_label.u64.lo = value.u64.lo & mask.u64.lo; match->flow.ct_label.u64.hi = value.u64.hi & mask.u64.hi; match->wc.masks.ct_label = mask; } void match_set_dl_type(struct match *match, ovs_be16 dl_type) { match->wc.masks.dl_type = OVS_BE16_MAX; match->flow.dl_type = dl_type; } /* Modifies 'value_src' so that the Ethernet address must match 'value_dst' * exactly. 'mask_dst' is set to all 1s. */ static void set_eth(const struct eth_addr value_src, struct eth_addr *value_dst, struct eth_addr *mask_dst) { *value_dst = value_src; *mask_dst = eth_addr_exact; } /* Modifies 'value_src' so that the Ethernet address must match 'value_src' * after each byte is ANDed with the appropriate byte in 'mask_src'. * 'mask_dst' is set to 'mask_src' */ static void set_eth_masked(const struct eth_addr value_src, const struct eth_addr mask_src, struct eth_addr *value_dst, struct eth_addr *mask_dst) { size_t i; for (i = 0; i < ARRAY_SIZE(value_dst->be16); i++) { value_dst->be16[i] = value_src.be16[i] & mask_src.be16[i]; } *mask_dst = mask_src; } /* Modifies 'rule' so that the source Ethernet address must match 'dl_src' * exactly. */ void match_set_dl_src(struct match *match, const struct eth_addr dl_src) { set_eth(dl_src, &match->flow.dl_src, &match->wc.masks.dl_src); } /* Modifies 'rule' so that the source Ethernet address must match 'dl_src' * after each byte is ANDed with the appropriate byte in 'mask'. */ void match_set_dl_src_masked(struct match *match, const struct eth_addr dl_src, const struct eth_addr mask) { set_eth_masked(dl_src, mask, &match->flow.dl_src, &match->wc.masks.dl_src); } /* Modifies 'match' so that the Ethernet address must match 'dl_dst' * exactly. */ void match_set_dl_dst(struct match *match, const struct eth_addr dl_dst) { set_eth(dl_dst, &match->flow.dl_dst, &match->wc.masks.dl_dst); } /* Modifies 'match' so that the Ethernet address must match 'dl_dst' after each * byte is ANDed with the appropriate byte in 'mask'. * * This function will assert-fail if 'mask' is invalid. Only 'mask' values * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */ void match_set_dl_dst_masked(struct match *match, const struct eth_addr dl_dst, const struct eth_addr mask) { set_eth_masked(dl_dst, mask, &match->flow.dl_dst, &match->wc.masks.dl_dst); } void match_set_dl_tci(struct match *match, ovs_be16 tci) { match_set_dl_tci_masked(match, tci, htons(0xffff)); } void match_set_dl_tci_masked(struct match *match, ovs_be16 tci, ovs_be16 mask) { match->flow.vlan_tci = tci & mask; match->wc.masks.vlan_tci = mask; } /* Modifies 'match' so that the VLAN VID is wildcarded. If the PCP is already * wildcarded, then 'match' will match a packet regardless of whether it has an * 802.1Q header or not. */ void match_set_any_vid(struct match *match) { if (match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK)) { match->wc.masks.vlan_tci &= ~htons(VLAN_VID_MASK); match->flow.vlan_tci &= ~htons(VLAN_VID_MASK); } else { match_set_dl_tci_masked(match, htons(0), htons(0)); } } /* Modifies 'match' depending on 'dl_vlan': * * - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'match' match only packets * without an 802.1Q header. * * - Otherwise, makes 'match' match only packets with an 802.1Q header whose * VID equals the low 12 bits of 'dl_vlan'. */ void match_set_dl_vlan(struct match *match, ovs_be16 dl_vlan) { flow_set_dl_vlan(&match->flow, dl_vlan); if (dl_vlan == htons(OFP10_VLAN_NONE)) { match->wc.masks.vlan_tci = OVS_BE16_MAX; } else { match->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); } } /* Sets the VLAN VID that 'match' matches to 'vid', which is interpreted as an * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID * plus CFI). */ void match_set_vlan_vid(struct match *match, ovs_be16 vid) { match_set_vlan_vid_masked(match, vid, htons(VLAN_VID_MASK | VLAN_CFI)); } /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID * plus CFI), with the corresponding 'mask'. */ void match_set_vlan_vid_masked(struct match *match, ovs_be16 vid, ovs_be16 mask) { ovs_be16 pcp_mask = htons(VLAN_PCP_MASK); ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI); mask &= vid_mask; flow_set_vlan_vid(&match->flow, vid & mask); match->wc.masks.vlan_tci = mask | (match->wc.masks.vlan_tci & pcp_mask); } /* Modifies 'match' so that the VLAN PCP is wildcarded. If the VID is already * wildcarded, then 'match' will match a packet regardless of whether it has an * 802.1Q header or not. */ void match_set_any_pcp(struct match *match) { if (match->wc.masks.vlan_tci & htons(VLAN_VID_MASK)) { match->wc.masks.vlan_tci &= ~htons(VLAN_PCP_MASK); match->flow.vlan_tci &= ~htons(VLAN_PCP_MASK); } else { match_set_dl_tci_masked(match, htons(0), htons(0)); } } /* Modifies 'match' so that it matches only packets with an 802.1Q header whose * PCP equals the low 3 bits of 'dl_vlan_pcp'. */ void match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp) { flow_set_vlan_pcp(&match->flow, dl_vlan_pcp); match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK); } /* Modifies 'match' so that the MPLS label 'idx' matches 'lse' exactly. */ void match_set_mpls_lse(struct match *match, int idx, ovs_be32 lse) { match->wc.masks.mpls_lse[idx] = OVS_BE32_MAX; match->flow.mpls_lse[idx] = lse; } /* Modifies 'match' so that the MPLS label is wildcarded. */ void match_set_any_mpls_label(struct match *match, int idx) { match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_LABEL_MASK); flow_set_mpls_label(&match->flow, idx, htonl(0)); } /* Modifies 'match' so that it matches only packets with an MPLS header whose * label equals the low 20 bits of 'mpls_label'. */ void match_set_mpls_label(struct match *match, int idx, ovs_be32 mpls_label) { match->wc.masks.mpls_lse[idx] |= htonl(MPLS_LABEL_MASK); flow_set_mpls_label(&match->flow, idx, mpls_label); } /* Modifies 'match' so that the MPLS TC is wildcarded. */ void match_set_any_mpls_tc(struct match *match, int idx) { match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_TC_MASK); flow_set_mpls_tc(&match->flow, idx, 0); } /* Modifies 'match' so that it matches only packets with an MPLS header whose * Traffic Class equals the low 3 bits of 'mpls_tc'. */ void match_set_mpls_tc(struct match *match, int idx, uint8_t mpls_tc) { match->wc.masks.mpls_lse[idx] |= htonl(MPLS_TC_MASK); flow_set_mpls_tc(&match->flow, idx, mpls_tc); } /* Modifies 'match' so that the MPLS stack flag is wildcarded. */ void match_set_any_mpls_bos(struct match *match, int idx) { match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_BOS_MASK); flow_set_mpls_bos(&match->flow, idx, 0); } /* Modifies 'match' so that it matches only packets with an MPLS header whose * Stack Flag equals the lower bit of 'mpls_bos' */ void match_set_mpls_bos(struct match *match, int idx, uint8_t mpls_bos) { match->wc.masks.mpls_lse[idx] |= htonl(MPLS_BOS_MASK); flow_set_mpls_bos(&match->flow, idx, mpls_bos); } /* Modifies 'match' so that the MPLS LSE is wildcarded. */ void match_set_any_mpls_lse(struct match *match, int idx) { match->wc.masks.mpls_lse[idx] = htonl(0); flow_set_mpls_lse(&match->flow, idx, htonl(0)); } void match_set_tp_src(struct match *match, ovs_be16 tp_src) { match_set_tp_src_masked(match, tp_src, OVS_BE16_MAX); } void match_set_tp_src_masked(struct match *match, ovs_be16 port, ovs_be16 mask) { match->flow.tp_src = port & mask; match->wc.masks.tp_src = mask; } void match_set_tp_dst(struct match *match, ovs_be16 tp_dst) { match_set_tp_dst_masked(match, tp_dst, OVS_BE16_MAX); } void match_set_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask) { match->flow.tp_dst = port & mask; match->wc.masks.tp_dst = mask; } void match_set_tcp_flags(struct match *match, ovs_be16 flags) { match_set_tcp_flags_masked(match, flags, OVS_BE16_MAX); } void match_set_tcp_flags_masked(struct match *match, ovs_be16 flags, ovs_be16 mask) { match->flow.tcp_flags = flags & mask; match->wc.masks.tcp_flags = mask; } void match_set_nw_proto(struct match *match, uint8_t nw_proto) { match->flow.nw_proto = nw_proto; match->wc.masks.nw_proto = UINT8_MAX; } void match_set_nw_src(struct match *match, ovs_be32 nw_src) { match->flow.nw_src = nw_src; match->wc.masks.nw_src = OVS_BE32_MAX; } void match_set_nw_src_masked(struct match *match, ovs_be32 nw_src, ovs_be32 mask) { match->flow.nw_src = nw_src & mask; match->wc.masks.nw_src = mask; } void match_set_nw_dst(struct match *match, ovs_be32 nw_dst) { match->flow.nw_dst = nw_dst; match->wc.masks.nw_dst = OVS_BE32_MAX; } void match_set_nw_dst_masked(struct match *match, ovs_be32 ip, ovs_be32 mask) { match->flow.nw_dst = ip & mask; match->wc.masks.nw_dst = mask; } void match_set_nw_dscp(struct match *match, uint8_t nw_dscp) { match->wc.masks.nw_tos |= IP_DSCP_MASK; match->flow.nw_tos &= ~IP_DSCP_MASK; match->flow.nw_tos |= nw_dscp & IP_DSCP_MASK; } void match_set_nw_ecn(struct match *match, uint8_t nw_ecn) { match->wc.masks.nw_tos |= IP_ECN_MASK; match->flow.nw_tos &= ~IP_ECN_MASK; match->flow.nw_tos |= nw_ecn & IP_ECN_MASK; } void match_set_nw_ttl(struct match *match, uint8_t nw_ttl) { match->wc.masks.nw_ttl = UINT8_MAX; match->flow.nw_ttl = nw_ttl; } void match_set_nw_frag(struct match *match, uint8_t nw_frag) { match->wc.masks.nw_frag |= FLOW_NW_FRAG_MASK; match->flow.nw_frag = nw_frag; } void match_set_nw_frag_masked(struct match *match, uint8_t nw_frag, uint8_t mask) { match->flow.nw_frag = nw_frag & mask; match->wc.masks.nw_frag = mask; } void match_set_icmp_type(struct match *match, uint8_t icmp_type) { match_set_tp_src(match, htons(icmp_type)); } void match_set_icmp_code(struct match *match, uint8_t icmp_code) { match_set_tp_dst(match, htons(icmp_code)); } void match_set_arp_sha(struct match *match, const struct eth_addr sha) { match->flow.arp_sha = sha; match->wc.masks.arp_sha = eth_addr_exact; } void match_set_arp_sha_masked(struct match *match, const struct eth_addr arp_sha, const struct eth_addr mask) { set_eth_masked(arp_sha, mask, &match->flow.arp_sha, &match->wc.masks.arp_sha); } void match_set_arp_tha(struct match *match, const struct eth_addr tha) { match->flow.arp_tha = tha; match->wc.masks.arp_tha = eth_addr_exact; } void match_set_arp_tha_masked(struct match *match, const struct eth_addr arp_tha, const struct eth_addr mask) { set_eth_masked(arp_tha, mask, &match->flow.arp_tha, &match->wc.masks.arp_tha); } void match_set_ipv6_src(struct match *match, const struct in6_addr *src) { match->flow.ipv6_src = *src; match->wc.masks.ipv6_src = in6addr_exact; } void match_set_ipv6_src_masked(struct match *match, const struct in6_addr *src, const struct in6_addr *mask) { match->flow.ipv6_src = ipv6_addr_bitand(src, mask); match->wc.masks.ipv6_src = *mask; } void match_set_ipv6_dst(struct match *match, const struct in6_addr *dst) { match->flow.ipv6_dst = *dst; match->wc.masks.ipv6_dst = in6addr_exact; } void match_set_ipv6_dst_masked(struct match *match, const struct in6_addr *dst, const struct in6_addr *mask) { match->flow.ipv6_dst = ipv6_addr_bitand(dst, mask); match->wc.masks.ipv6_dst = *mask; } void match_set_ipv6_label(struct match *match, ovs_be32 ipv6_label) { match->wc.masks.ipv6_label = OVS_BE32_MAX; match->flow.ipv6_label = ipv6_label; } void match_set_ipv6_label_masked(struct match *match, ovs_be32 ipv6_label, ovs_be32 mask) { match->flow.ipv6_label = ipv6_label & mask; match->wc.masks.ipv6_label = mask; } void match_set_nd_target(struct match *match, const struct in6_addr *target) { match->flow.nd_target = *target; match->wc.masks.nd_target = in6addr_exact; } void match_set_nd_target_masked(struct match *match, const struct in6_addr *target, const struct in6_addr *mask) { match->flow.nd_target = ipv6_addr_bitand(target, mask); match->wc.masks.nd_target = *mask; } /* Returns true if 'a' and 'b' wildcard the same fields and have the same * values for fixed fields, otherwise false. */ bool match_equal(const struct match *a, const struct match *b) { return (flow_wildcards_equal(&a->wc, &b->wc) && flow_equal(&a->flow, &b->flow)); } /* Returns a hash value for the flow and wildcards in 'match', starting from * 'basis'. */ uint32_t match_hash(const struct match *match, uint32_t basis) { return flow_wildcards_hash(&match->wc, flow_hash(&match->flow, basis)); } static bool match_has_default_recirc_id(const struct match *m) { return m->flow.recirc_id == 0 && (m->wc.masks.recirc_id == UINT32_MAX || m->wc.masks.recirc_id == 0); } static bool match_has_default_dp_hash(const struct match *m) { return ((m->flow.dp_hash | m->wc.masks.dp_hash) == 0); } /* Return true if the hidden fields of the match are set to the default values. * The default values equals to those set up by match_init_hidden_fields(). */ bool match_has_default_hidden_fields(const struct match *m) { return match_has_default_recirc_id(m) && match_has_default_dp_hash(m); } void match_init_hidden_fields(struct match *m) { match_set_recirc_id(m, 0); match_set_dp_hash_masked(m, 0, 0); } static void format_eth_masked(struct ds *s, const char *name, const struct eth_addr eth, const struct eth_addr mask) { if (!eth_addr_is_zero(mask)) { ds_put_format(s, "%s=", name); eth_format_masked(eth, &mask, s); ds_put_char(s, ','); } } static void format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip, ovs_be32 netmask) { if (netmask) { ds_put_format(s, "%s=", name); ip_format_masked(ip, netmask, s); ds_put_char(s, ','); } } static void format_ipv6_netmask(struct ds *s, const char *name, const struct in6_addr *addr, const struct in6_addr *netmask) { if (!ipv6_mask_is_any(netmask)) { ds_put_format(s, "%s=", name); ipv6_format_masked(addr, netmask, s); ds_put_char(s, ','); } } static void format_uint16_masked(struct ds *s, const char *name, uint16_t value, uint16_t mask) { if (mask != 0) { ds_put_format(s, "%s=", name); if (mask == UINT16_MAX) { ds_put_format(s, "%"PRIu16, value); } else { ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16, value, mask); } ds_put_char(s, ','); } } static void format_be16_masked(struct ds *s, const char *name, ovs_be16 value, ovs_be16 mask) { if (mask != htons(0)) { ds_put_format(s, "%s=", name); if (mask == OVS_BE16_MAX) { ds_put_format(s, "%"PRIu16, ntohs(value)); } else { ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16, ntohs(value), ntohs(mask)); } ds_put_char(s, ','); } } static void format_be32_masked(struct ds *s, const char *name, ovs_be32 value, ovs_be32 mask) { if (mask != htonl(0)) { ds_put_format(s, "%s=", name); if (mask == OVS_BE32_MAX) { ds_put_format(s, "%"PRIu32, ntohl(value)); } else { ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32, ntohl(value), ntohl(mask)); } ds_put_char(s, ','); } } static void format_uint32_masked(struct ds *s, const char *name, uint32_t value, uint32_t mask) { if (mask) { ds_put_format(s, "%s=%#"PRIx32, name, value); if (mask != UINT32_MAX) { ds_put_format(s, "/%#"PRIx32, mask); } ds_put_char(s, ','); } } static void format_be64_masked(struct ds *s, const char *name, ovs_be64 value, ovs_be64 mask) { if (mask != htonll(0)) { ds_put_format(s, "%s=%#"PRIx64, name, ntohll(value)); if (mask != OVS_BE64_MAX) { ds_put_format(s, "/%#"PRIx64, ntohll(mask)); } ds_put_char(s, ','); } } static void format_flow_tunnel(struct ds *s, const struct match *match) { const struct flow_wildcards *wc = &match->wc; const struct flow_tnl *tnl = &match->flow.tunnel; format_be64_masked(s, "tun_id", tnl->tun_id, wc->masks.tunnel.tun_id); format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src); format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst); format_ipv6_netmask(s, "tun_ipv6_src", &tnl->ipv6_src, &wc->masks.tunnel.ipv6_src); format_ipv6_netmask(s, "tun_ipv6_dst", &tnl->ipv6_dst, &wc->masks.tunnel.ipv6_dst); if (wc->masks.tunnel.gbp_id) { format_be16_masked(s, "tun_gbp_id", tnl->gbp_id, wc->masks.tunnel.gbp_id); } if (wc->masks.tunnel.gbp_flags) { ds_put_format(s, "tun_gbp_flags=%#"PRIx8",", tnl->gbp_flags); } if (wc->masks.tunnel.ip_tos) { ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos); } if (wc->masks.tunnel.ip_ttl) { ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl); } if (wc->masks.tunnel.flags & FLOW_TNL_F_MASK) { format_flags_masked(s, "tun_flags", flow_tun_flag_to_string, tnl->flags & FLOW_TNL_F_MASK, wc->masks.tunnel.flags & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK); ds_put_char(s, ','); } tun_metadata_match_format(s, match); } static void format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask) { if (!ovs_u128_is_zero(mask)) { ovs_be128 value = hton128(*key); ds_put_format(s, "ct_label="); ds_put_hex(s, &value, sizeof value); if (!is_all_ones(mask, sizeof(*mask))) { value = hton128(*mask); ds_put_char(s, '/'); ds_put_hex(s, &value, sizeof value); } ds_put_char(s, ','); } } /* Appends a string representation of 'match' to 's'. If 'priority' is * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */ void match_format(const struct match *match, struct ds *s, int priority) { const struct flow_wildcards *wc = &match->wc; size_t start_len = s->length; const struct flow *f = &match->flow; bool skip_type = false; bool skip_proto = false; int i; BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%d,", priority); } format_uint32_masked(s, "pkt_mark", f->pkt_mark, wc->masks.pkt_mark); if (wc->masks.recirc_id) { format_uint32_masked(s, "recirc_id", f->recirc_id, wc->masks.recirc_id); } if (wc->masks.dp_hash) { format_uint32_masked(s, "dp_hash", f->dp_hash, wc->masks.dp_hash); } if (wc->masks.conj_id) { ds_put_format(s, "conj_id=%"PRIu32",", f->conj_id); } if (wc->masks.skb_priority) { ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority); } if (wc->masks.actset_output) { ds_put_cstr(s, "actset_output="); ofputil_format_port(f->actset_output, s); ds_put_char(s, ','); } if (wc->masks.ct_state) { if (wc->masks.ct_state == UINT16_MAX) { ds_put_cstr(s, "ct_state="); if (f->ct_state) { format_flags(s, ct_state_to_string, f->ct_state, '|'); } else { ds_put_cstr(s, "0"); /* No state. */ } } else { format_flags_masked(s, "ct_state", ct_state_to_string, f->ct_state, wc->masks.ct_state, UINT16_MAX); } ds_put_char(s, ','); } if (wc->masks.ct_zone) { format_uint16_masked(s, "ct_zone", f->ct_zone, wc->masks.ct_zone); } if (wc->masks.ct_mark) { format_uint32_masked(s, "ct_mark", f->ct_mark, wc->masks.ct_mark); } if (!ovs_u128_is_zero(&wc->masks.ct_label)) { format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label); } if (wc->masks.dl_type) { skip_type = true; if (f->dl_type == htons(ETH_TYPE_IP)) { if (wc->masks.nw_proto) { skip_proto = true; if (f->nw_proto == IPPROTO_ICMP) { ds_put_cstr(s, "icmp,"); } else if (f->nw_proto == IPPROTO_IGMP) { ds_put_cstr(s, "igmp,"); } else if (f->nw_proto == IPPROTO_TCP) { ds_put_cstr(s, "tcp,"); } else if (f->nw_proto == IPPROTO_UDP) { ds_put_cstr(s, "udp,"); } else if (f->nw_proto == IPPROTO_SCTP) { ds_put_cstr(s, "sctp,"); } else { ds_put_cstr(s, "ip,"); skip_proto = false; } } else { ds_put_cstr(s, "ip,"); } } else if (f->dl_type == htons(ETH_TYPE_IPV6)) { if (wc->masks.nw_proto) { skip_proto = true; if (f->nw_proto == IPPROTO_ICMPV6) { ds_put_cstr(s, "icmp6,"); } else if (f->nw_proto == IPPROTO_TCP) { ds_put_cstr(s, "tcp6,"); } else if (f->nw_proto == IPPROTO_UDP) { ds_put_cstr(s, "udp6,"); } else if (f->nw_proto == IPPROTO_SCTP) { ds_put_cstr(s, "sctp6,"); } else { ds_put_cstr(s, "ipv6,"); skip_proto = false; } } else { ds_put_cstr(s, "ipv6,"); } } else if (f->dl_type == htons(ETH_TYPE_ARP)) { ds_put_cstr(s, "arp,"); } else if (f->dl_type == htons(ETH_TYPE_RARP)) { ds_put_cstr(s, "rarp,"); } else if (f->dl_type == htons(ETH_TYPE_MPLS)) { ds_put_cstr(s, "mpls,"); } else if (f->dl_type == htons(ETH_TYPE_MPLS_MCAST)) { ds_put_cstr(s, "mplsm,"); } else { skip_type = false; } } for (i = 0; i < FLOW_N_REGS; i++) { #define REGNAME_LEN 20 char regname[REGNAME_LEN]; if (snprintf(regname, REGNAME_LEN, "reg%d", i) >= REGNAME_LEN) { strcpy(regname, "reg?"); } format_uint32_masked(s, regname, f->regs[i], wc->masks.regs[i]); } format_flow_tunnel(s, match); format_be64_masked(s, "metadata", f->metadata, wc->masks.metadata); if (wc->masks.in_port.ofp_port) { ds_put_cstr(s, "in_port="); ofputil_format_port(f->in_port.ofp_port, s); ds_put_char(s, ','); } if (wc->masks.vlan_tci) { ovs_be16 vid_mask = wc->masks.vlan_tci & htons(VLAN_VID_MASK); ovs_be16 pcp_mask = wc->masks.vlan_tci & htons(VLAN_PCP_MASK); ovs_be16 cfi = wc->masks.vlan_tci & htons(VLAN_CFI); if (cfi && f->vlan_tci & htons(VLAN_CFI) && (!vid_mask || vid_mask == htons(VLAN_VID_MASK)) && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK)) && (vid_mask || pcp_mask)) { if (vid_mask) { ds_put_format(s, "dl_vlan=%"PRIu16",", vlan_tci_to_vid(f->vlan_tci)); } if (pcp_mask) { ds_put_format(s, "dl_vlan_pcp=%d,", vlan_tci_to_pcp(f->vlan_tci)); } } else if (wc->masks.vlan_tci == htons(0xffff)) { ds_put_format(s, "vlan_tci=0x%04"PRIx16",", ntohs(f->vlan_tci)); } else { ds_put_format(s, "vlan_tci=0x%04"PRIx16"/0x%04"PRIx16",", ntohs(f->vlan_tci), ntohs(wc->masks.vlan_tci)); } } format_eth_masked(s, "dl_src", f->dl_src, wc->masks.dl_src); format_eth_masked(s, "dl_dst", f->dl_dst, wc->masks.dl_dst); if (!skip_type && wc->masks.dl_type) { ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type)); } if (f->dl_type == htons(ETH_TYPE_IPV6)) { format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->masks.ipv6_src); format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->masks.ipv6_dst); if (wc->masks.ipv6_label) { if (wc->masks.ipv6_label == OVS_BE32_MAX) { ds_put_format(s, "ipv6_label=0x%05"PRIx32",", ntohl(f->ipv6_label)); } else { ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",", ntohl(f->ipv6_label), ntohl(wc->masks.ipv6_label)); } } } else if (f->dl_type == htons(ETH_TYPE_ARP) || f->dl_type == htons(ETH_TYPE_RARP)) { format_ip_netmask(s, "arp_spa", f->nw_src, wc->masks.nw_src); format_ip_netmask(s, "arp_tpa", f->nw_dst, wc->masks.nw_dst); } else { format_ip_netmask(s, "nw_src", f->nw_src, wc->masks.nw_src); format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst); } if (!skip_proto && wc->masks.nw_proto) { if (f->dl_type == htons(ETH_TYPE_ARP) || f->dl_type == htons(ETH_TYPE_RARP)) { ds_put_format(s, "arp_op=%"PRIu8",", f->nw_proto); } else { ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto); } } if (f->dl_type == htons(ETH_TYPE_ARP) || f->dl_type == htons(ETH_TYPE_RARP)) { format_eth_masked(s, "arp_sha", f->arp_sha, wc->masks.arp_sha); format_eth_masked(s, "arp_tha", f->arp_tha, wc->masks.arp_tha); } if (wc->masks.nw_tos & IP_DSCP_MASK) { ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK); } if (wc->masks.nw_tos & IP_ECN_MASK) { ds_put_format(s, "nw_ecn=%"PRIu8",", f->nw_tos & IP_ECN_MASK); } if (wc->masks.nw_ttl) { ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl); } if (wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) { ds_put_format(s, "mpls_label=%"PRIu32",", mpls_lse_to_label(f->mpls_lse[0])); } if (wc->masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) { ds_put_format(s, "mpls_tc=%"PRIu8",", mpls_lse_to_tc(f->mpls_lse[0])); } if (wc->masks.mpls_lse[0] & htonl(MPLS_TTL_MASK)) { ds_put_format(s, "mpls_ttl=%"PRIu8",", mpls_lse_to_ttl(f->mpls_lse[0])); } if (wc->masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) { ds_put_format(s, "mpls_bos=%"PRIu8",", mpls_lse_to_bos(f->mpls_lse[0])); } format_be32_masked(s, "mpls_lse1", f->mpls_lse[1], wc->masks.mpls_lse[1]); format_be32_masked(s, "mpls_lse2", f->mpls_lse[2], wc->masks.mpls_lse[2]); switch (wc->masks.nw_frag) { case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER: ds_put_format(s, "nw_frag=%s,", f->nw_frag & FLOW_NW_FRAG_ANY ? (f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "first") : (f->nw_frag & FLOW_NW_FRAG_LATER ? "" : "no")); break; case FLOW_NW_FRAG_ANY: ds_put_format(s, "nw_frag=%s,", f->nw_frag & FLOW_NW_FRAG_ANY ? "yes" : "no"); break; case FLOW_NW_FRAG_LATER: ds_put_format(s, "nw_frag=%s,", f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "not_later"); break; } if (f->dl_type == htons(ETH_TYPE_IP) && f->nw_proto == IPPROTO_ICMP) { format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src); format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst); } else if (f->dl_type == htons(ETH_TYPE_IP) && f->nw_proto == IPPROTO_IGMP) { format_be16_masked(s, "igmp_type", f->tp_src, wc->masks.tp_src); format_be16_masked(s, "igmp_code", f->tp_dst, wc->masks.tp_dst); } else if (f->dl_type == htons(ETH_TYPE_IPV6) && f->nw_proto == IPPROTO_ICMPV6) { format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src); format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst); format_ipv6_netmask(s, "nd_target", &f->nd_target, &wc->masks.nd_target); format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha); format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha); } else { format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src); format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst); } if (is_ip_any(f) && f->nw_proto == IPPROTO_TCP && wc->masks.tcp_flags) { format_flags_masked(s, "tcp_flags", packet_tcp_flag_to_string, ntohs(f->tcp_flags), TCP_FLAGS(wc->masks.tcp_flags), TCP_FLAGS(OVS_BE16_MAX)); } if (s->length > start_len) { ds_chomp(s, ','); } } /* Converts 'match' to a string and returns the string. If 'priority' is * different from OFP_DEFAULT_PRIORITY, includes it in the string. The caller * must free the string (with free()). */ char * match_to_string(const struct match *match, int priority) { struct ds s = DS_EMPTY_INITIALIZER; match_format(match, &s, priority); return ds_steal_cstr(&s); } void match_print(const struct match *match) { char *s = match_to_string(match, OFP_DEFAULT_PRIORITY); puts(s); free(s); } /* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' * with minimatch_destroy(). */ void minimatch_init(struct minimatch *dst, const struct match *src) { struct miniflow tmp; miniflow_map_init(&tmp, &src->wc.masks); /* Allocate two consecutive miniflows. */ miniflow_alloc(dst->flows, 2, &tmp); miniflow_init(dst->flow, &src->flow); minimask_init(dst->mask, &src->wc); } /* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' * with minimatch_destroy(). */ void minimatch_clone(struct minimatch *dst, const struct minimatch *src) { /* Allocate two consecutive miniflows. */ size_t data_size = miniflow_alloc(dst->flows, 2, &src->mask->masks); memcpy(miniflow_values(dst->flow), miniflow_get_values(src->flow), data_size); memcpy(miniflow_values(&dst->mask->masks), miniflow_get_values(&src->mask->masks), data_size); } /* Initializes 'dst' with the data in 'src', destroying 'src'. The caller must * eventually free 'dst' with minimatch_destroy(). */ void minimatch_move(struct minimatch *dst, struct minimatch *src) { dst->flow = src->flow; dst->mask = src->mask; } /* Frees any memory owned by 'match'. Does not free the storage in which * 'match' itself resides; the caller is responsible for that. */ void minimatch_destroy(struct minimatch *match) { free(match->flow); } /* Initializes 'dst' as a copy of 'src'. */ void minimatch_expand(const struct minimatch *src, struct match *dst) { miniflow_expand(src->flow, &dst->flow); minimask_expand(src->mask, &dst->wc); memset(&dst->tun_md, 0, sizeof dst->tun_md); } /* Returns true if 'a' and 'b' match the same packets, false otherwise. */ bool minimatch_equal(const struct minimatch *a, const struct minimatch *b) { return minimask_equal(a->mask, b->mask) && miniflow_equal(a->flow, b->flow); } /* Returns true if 'target' satisifies 'match', that is, if each bit for which * 'match' specifies a particular value has the correct value in 'target'. * * This function is equivalent to miniflow_equal_flow_in_minimask(&match->flow, * target, &match->mask) but it is faster because of the invariant that * match->flow.map and match->mask.map are the same. */ bool minimatch_matches_flow(const struct minimatch *match, const struct flow *target) { const uint64_t *flowp = miniflow_get_values(match->flow); const uint64_t *maskp = miniflow_get_values(&match->mask->masks); size_t idx; FLOWMAP_FOR_EACH_INDEX(idx, match->flow->map) { if ((*flowp++ ^ flow_u64_value(target, idx)) & *maskp++) { return false; } } return true; } /* Appends a string representation of 'match' to 's'. If 'priority' is * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */ void minimatch_format(const struct minimatch *match, struct ds *s, int priority) { struct match megamatch; minimatch_expand(match, &megamatch); match_format(&megamatch, s, priority); } /* Converts 'match' to a string and returns the string. If 'priority' is * different from OFP_DEFAULT_PRIORITY, includes it in the string. The caller * must free the string (with free()). */ char * minimatch_to_string(const struct minimatch *match, int priority) { struct match megamatch; minimatch_expand(match, &megamatch); return match_to_string(&megamatch, priority); } openvswitch-2.5.9/lib/PaxHeaders.82075/dpctl.h0000644000000000000000000000013113534540071015670 xustar0029 mtime=1567801401.34168073 30 atime=1567801402.073686105 30 ctime=1567801424.705852866 openvswitch-2.5.9/lib/dpctl.h0000644000175000017500000000326613534540071017366 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DPCTL_H #define DPCTL_H 1 #include #include "compiler.h" struct dpctl_params { /* True if it is called by ovs-appctl command. */ bool is_appctl; /* -s, --statistics: Print port/flow statistics? */ bool print_statistics; /* --clear: Reset existing statistics to zero when modifying a flow? */ bool zero_statistics; /* --may-create: Allow mod-flows command to create a new flow? */ bool may_create; /* -m, --more: Increase output verbosity. */ int verbosity; /* Callback for printing. This function is called from dpctl_run_command() * to output data. The 'aux' parameter is set to the 'aux' * member. The 'error' parameter is true if 'string' is an error * message, false otherwise */ void (*output)(void *aux, bool error, const char *string); void *aux; /* 'usage' (if != NULL) gets called for the "help" command. */ void (*usage)(void *aux); }; int dpctl_run_command(int argc, const char *argv[], struct dpctl_params *dpctl_p); void dpctl_unixctl_register(void); #endif /* dpctl.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-bsd.c0000644000000000000000000000013213534540071016611 xustar0030 mtime=1567801401.425681346 30 atime=1567801402.077686135 30 ctime=1567801425.001855048 openvswitch-2.5.9/lib/netdev-bsd.c0000644000175000017500000014601013534540071020301 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013, 2014 Gaetano Catalli. * Copyright (c) 2013, 2014 YAMAMOTO Takashi. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netdev-provider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NET_IF_MIB_H #include #endif #include #include #include #include #if defined(__NetBSD__) #include #include #endif #include "rtbsd.h" #include "coverage.h" #include "dp-packet.h" #include "dpif-netdev.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "openflow/openflow.h" #include "ovs-thread.h" #include "packets.h" #include "poll-loop.h" #include "shash.h" #include "socket-util.h" #include "svec.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_bsd); struct netdev_rxq_bsd { struct netdev_rxq up; /* Packet capture descriptor for a system network device. * For a tap device this is NULL. */ pcap_t *pcap_handle; /* Selectable file descriptor for the network device. * This descriptor will be used for polling operations. */ int fd; }; struct netdev_bsd { struct netdev up; /* Never changes after initialization. */ char *kernel_name; /* Protects all members below. */ struct ovs_mutex mutex; unsigned int cache_valid; int ifindex; struct eth_addr etheraddr; struct in_addr in4; struct in_addr netmask; struct in6_addr in6; int mtu; int carrier; int tap_fd; /* TAP character device, if any, otherwise -1. */ /* Used for sending packets on non-tap devices. */ pcap_t *pcap; int fd; }; enum { VALID_IFINDEX = 1 << 0, VALID_ETHERADDR = 1 << 1, VALID_IN4 = 1 << 2, VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, VALID_CARRIER = 1 << 5 }; #define PCAP_SNAPLEN 2048 /* * Notifier used to invalidate device informations in case of status change. * * It will be registered with a 'rtbsd_notifier_register()' when the first * device will be created with the call of either 'netdev_bsd_tap_create()' or * 'netdev_bsd_system_create()'. * * The callback associated with this notifier ('netdev_bsd_cache_cb()') will * invalidate cached information about the device. */ static struct rtbsd_notifier netdev_bsd_cache_notifier; static int cache_notifier_refcount; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); static void destroy_tap(int fd, const char *name); static int get_flags(const struct netdev *, int *flagsp); static int set_flags(const char *, int flags); static int do_set_addr(struct netdev *netdev, unsigned long ioctl_nr, const char *ioctl_name, struct in_addr addr); static int get_etheraddr(const char *netdev_name, struct eth_addr *ea); static int set_etheraddr(const char *netdev_name, int hwaddr_family, int hwaddr_len, const struct eth_addr); static int get_ifindex(const struct netdev *, int *ifindexp); static int ifr_get_flags(const struct ifreq *); static void ifr_set_flags(struct ifreq *, int flags); #ifdef __NetBSD__ static int af_link_ioctl(unsigned long command, const void *arg); #endif static void netdev_bsd_run(void); static int netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup); static bool is_netdev_bsd_class(const struct netdev_class *netdev_class) { return netdev_class->run == netdev_bsd_run; } static struct netdev_bsd * netdev_bsd_cast(const struct netdev *netdev) { ovs_assert(is_netdev_bsd_class(netdev_get_class(netdev))); return CONTAINER_OF(netdev, struct netdev_bsd, up); } static struct netdev_rxq_bsd * netdev_rxq_bsd_cast(const struct netdev_rxq *rxq) { ovs_assert(is_netdev_bsd_class(netdev_get_class(rxq->netdev))); return CONTAINER_OF(rxq, struct netdev_rxq_bsd, up); } static const char * netdev_get_kernel_name(const struct netdev *netdev) { return netdev_bsd_cast(netdev)->kernel_name; } /* * Perform periodic work needed by netdev. In BSD netdevs it checks for any * interface status changes, and eventually calls all the user callbacks. */ static void netdev_bsd_run(void) { rtbsd_notifier_run(); } /* * Arranges for poll_block() to wake up if the "run" member function needs to * be called. */ static void netdev_bsd_wait(void) { rtbsd_notifier_wait(); } /* Invalidate cache in case of interface status change. */ static void netdev_bsd_cache_cb(const struct rtbsd_change *change, void *aux OVS_UNUSED) { struct netdev_bsd *dev; if (change) { struct netdev *base_dev = netdev_from_name(change->if_name); if (base_dev) { const struct netdev_class *netdev_class = netdev_get_class(base_dev); if (is_netdev_bsd_class(netdev_class)) { dev = netdev_bsd_cast(base_dev); dev->cache_valid = 0; netdev_change_seq_changed(base_dev); } netdev_close(base_dev); } } else { /* * XXX the API is lacking, we should be able to iterate on the list of * netdevs without having to store the info in a temp shash. */ struct shash device_shash; struct shash_node *node; shash_init(&device_shash); netdev_get_devices(&netdev_bsd_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { struct netdev *netdev = node->data; dev = netdev_bsd_cast(netdev); dev->cache_valid = 0; netdev_change_seq_changed(netdev); netdev_close(netdev); } shash_destroy(&device_shash); } } static int cache_notifier_ref(void) { int ret = 0; if (!cache_notifier_refcount) { ret = rtbsd_notifier_register(&netdev_bsd_cache_notifier, netdev_bsd_cache_cb, NULL); if (ret) { return ret; } } cache_notifier_refcount++; return 0; } static int cache_notifier_unref(void) { cache_notifier_refcount--; if (cache_notifier_refcount == 0) { rtbsd_notifier_unregister(&netdev_bsd_cache_notifier); } return 0; } static struct netdev * netdev_bsd_alloc(void) { struct netdev_bsd *netdev = xzalloc(sizeof *netdev); return &netdev->up; } static int netdev_bsd_construct_system(struct netdev *netdev_) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); enum netdev_flags flags; int error; error = cache_notifier_ref(); if (error) { return error; } ovs_mutex_init(&netdev->mutex); netdev->tap_fd = -1; netdev->kernel_name = xstrdup(netdev_->name); /* Verify that the netdev really exists by attempting to read its flags */ error = netdev_get_flags(netdev_, &flags); if (error == ENXIO) { free(netdev->kernel_name); cache_notifier_unref(); ovs_mutex_destroy(&netdev->mutex); return error; } return 0; } static int netdev_bsd_construct_tap(struct netdev *netdev_) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); const char *name = netdev_->name; int error = 0; struct ifreq ifr; char *kernel_name = NULL; error = cache_notifier_ref(); if (error) { goto error; } memset(&ifr, 0, sizeof(ifr)); /* Create a tap device by opening /dev/tap. The TAPGIFNAME ioctl is used * to retrieve the name of the tap device. */ ovs_mutex_init(&netdev->mutex); netdev->tap_fd = open("/dev/tap", O_RDWR); if (netdev->tap_fd < 0) { error = errno; VLOG_WARN("opening \"/dev/tap\" failed: %s", ovs_strerror(error)); goto error_unref_notifier; } /* Retrieve tap name (e.g. tap0) */ if (ioctl(netdev->tap_fd, TAPGIFNAME, &ifr) == -1) { /* XXX Need to destroy the device? */ error = errno; close(netdev->tap_fd); goto error_unref_notifier; } /* Change the name of the tap device */ #if defined(SIOCSIFNAME) ifr.ifr_data = (void *)name; error = af_inet_ioctl(SIOCSIFNAME, &ifr); if (error) { destroy_tap(netdev->tap_fd, ifr.ifr_name); goto error_unref_notifier; } kernel_name = xstrdup(name); #else /* * NetBSD doesn't support inteface renaming. */ VLOG_INFO("tap %s is created for bridge %s", ifr.ifr_name, name); kernel_name = xstrdup(ifr.ifr_name); #endif /* set non-blocking. */ error = set_nonblocking(netdev->tap_fd); if (error) { destroy_tap(netdev->tap_fd, kernel_name); goto error_unref_notifier; } /* Turn device UP */ ifr_set_flags(&ifr, IFF_UP); ovs_strlcpy(ifr.ifr_name, kernel_name, sizeof ifr.ifr_name); error = af_inet_ioctl(SIOCSIFFLAGS, &ifr); if (error) { destroy_tap(netdev->tap_fd, kernel_name); goto error_unref_notifier; } netdev->kernel_name = kernel_name; return 0; error_unref_notifier: ovs_mutex_destroy(&netdev->mutex); cache_notifier_unref(); error: free(kernel_name); return error; } static void netdev_bsd_destruct(struct netdev *netdev_) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); cache_notifier_unref(); if (netdev->tap_fd >= 0) { destroy_tap(netdev->tap_fd, netdev_get_kernel_name(netdev_)); } if (netdev->pcap) { pcap_close(netdev->pcap); } free(netdev->kernel_name); ovs_mutex_destroy(&netdev->mutex); } static void netdev_bsd_dealloc(struct netdev *netdev_) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); free(netdev); } static int netdev_bsd_open_pcap(const char *name, pcap_t **pcapp, int *fdp) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pcap = NULL; int one = 1; int error; int fd; /* Open the pcap device. The device is opened in non-promiscuous mode * because the interface flags are manually set by the caller. */ errbuf[0] = '\0'; pcap = pcap_open_live(name, PCAP_SNAPLEN, 0, 1000, errbuf); if (!pcap) { VLOG_ERR_RL(&rl, "%s: pcap_open_live failed: %s", name, errbuf); error = EIO; goto error; } if (errbuf[0] != '\0') { VLOG_WARN_RL(&rl, "%s: pcap_open_live: %s", name, errbuf); } /* Get the underlying fd. */ fd = pcap_get_selectable_fd(pcap); if (fd == -1) { VLOG_WARN_RL(&rl, "%s: no selectable file descriptor", name); error = errno; goto error; } /* Set non-blocking mode. Also the BIOCIMMEDIATE ioctl must be called * on the file descriptor returned by pcap_get_selectable_fd to achieve * a real non-blocking behaviour.*/ error = pcap_setnonblock(pcap, 1, errbuf); if (error == -1) { error = errno; goto error; } /* This call assure that reads return immediately upon packet * reception. Otherwise, a read will block until either the kernel * buffer becomes full or a timeout occurs. */ if (ioctl(fd, BIOCIMMEDIATE, &one) < 0 ) { VLOG_ERR_RL(&rl, "ioctl(BIOCIMMEDIATE) on %s device failed: %s", name, ovs_strerror(errno)); error = errno; goto error; } /* Capture only incoming packets. */ error = pcap_setdirection(pcap, PCAP_D_IN); if (error == -1) { error = errno; goto error; } *pcapp = pcap; *fdp = fd; return 0; error: if (pcap) { pcap_close(pcap); } *pcapp = NULL; *fdp = -1; return error; } static struct netdev_rxq * netdev_bsd_rxq_alloc(void) { struct netdev_rxq_bsd *rxq = xzalloc(sizeof *rxq); return &rxq->up; } static int netdev_bsd_rxq_construct(struct netdev_rxq *rxq_) { struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); struct netdev *netdev_ = rxq->up.netdev; struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error; if (!strcmp(netdev_get_type(netdev_), "tap")) { rxq->pcap_handle = NULL; rxq->fd = netdev->tap_fd; error = 0; } else { ovs_mutex_lock(&netdev->mutex); error = netdev_bsd_open_pcap(netdev_get_kernel_name(netdev_), &rxq->pcap_handle, &rxq->fd); ovs_mutex_unlock(&netdev->mutex); } return error; } static void netdev_bsd_rxq_destruct(struct netdev_rxq *rxq_) { struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); if (rxq->pcap_handle) { pcap_close(rxq->pcap_handle); } } static void netdev_bsd_rxq_dealloc(struct netdev_rxq *rxq_) { struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); free(rxq); } /* The recv callback of the netdev class returns the number of bytes of the * received packet. * * This can be done by the pcap_next() function. Unfortunately pcap_next() does * not make difference between a missing packet on the capture interface and * an error during the file capture. We can use the pcap_dispatch() function * instead, which is able to distinguish between errors and null packet. * * To make pcap_dispatch() returns the number of bytes read from the interface * we need to define the following callback and argument. */ struct pcap_arg { void *data; int size; int retval; }; /* * This callback will be executed on every captured packet. * * If the packet captured by pcap_dispatch() does not fit the pcap buffer, * pcap returns a truncated packet and we follow this behavior. * * The argument args->retval is the packet size in bytes. */ static void proc_pkt(u_char *args_, const struct pcap_pkthdr *hdr, const u_char *packet) { struct pcap_arg *args = ALIGNED_CAST(struct pcap_arg *, args_); if (args->size < hdr->len) { VLOG_WARN_RL(&rl, "packet truncated"); args->retval = args->size; } else { args->retval = hdr->len; } /* copy the packet to our buffer */ memcpy(args->data, packet, args->retval); } /* * This function attempts to receive a packet from the specified network * device. It is assumed that the network device is a system device or a tap * device opened as a system one. In this case the read operation is performed * from rxq->pcap. */ static int netdev_rxq_bsd_recv_pcap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer) { struct pcap_arg arg; int ret; /* prepare the pcap argument to store the packet */ arg.size = dp_packet_tailroom(buffer); arg.data = dp_packet_data(buffer); for (;;) { ret = pcap_dispatch(rxq->pcap_handle, 1, proc_pkt, (u_char *) &arg); if (ret > 0) { dp_packet_set_size(buffer, dp_packet_size(buffer) + arg.retval); return 0; } if (ret == -1) { if (errno == EINTR) { continue; } } return EAGAIN; } } /* * This function attempts to receive a packet from the specified network * device. It is assumed that the network device is a tap device and * 'rxq->fd' is initialized with the tap file descriptor. */ static int netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer) { size_t size = dp_packet_tailroom(buffer); for (;;) { ssize_t retval = read(rxq->fd, dp_packet_data(buffer), size); if (retval >= 0) { dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); return 0; } else if (errno != EINTR) { if (errno != EAGAIN) { VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", ovs_strerror(errno), netdev_rxq_get_name(&rxq->up)); } return errno; } } } static int netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **packets, int *c) { struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); struct netdev *netdev = rxq->up.netdev; struct dp_packet *packet; ssize_t retval; int mtu; if (netdev_bsd_get_mtu(netdev, &mtu)) { mtu = ETH_PAYLOAD_MAX; } packet = dp_packet_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu, DP_NETDEV_HEADROOM); retval = (rxq->pcap_handle ? netdev_rxq_bsd_recv_pcap(rxq, packet) : netdev_rxq_bsd_recv_tap(rxq, packet)); if (retval) { dp_packet_delete(packet); } else { dp_packet_pad(packet); packets[0] = packet; *c = 1; } return retval; } /* * Registers with the poll loop to wake up from the next call to poll_block() * when a packet is ready to be received with netdev_rxq_recv() on 'rxq'. */ static void netdev_bsd_rxq_wait(struct netdev_rxq *rxq_) { struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); poll_fd_wait(rxq->fd, POLLIN); } /* Discards all packets waiting to be received from 'rxq'. */ static int netdev_bsd_rxq_drain(struct netdev_rxq *rxq_) { struct ifreq ifr; struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_); strcpy(ifr.ifr_name, netdev_get_kernel_name(netdev_rxq_get_netdev(rxq_))); if (ioctl(rxq->fd, BIOCFLUSH, &ifr) == -1) { VLOG_DBG_RL(&rl, "%s: ioctl(BIOCFLUSH) failed: %s", netdev_rxq_get_name(rxq_), ovs_strerror(errno)); return errno; } return 0; } /* * Send a packet on the specified network device. The device could be either a * system or a tap device. */ static int netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED, struct dp_packet **pkts, int cnt, bool may_steal) { struct netdev_bsd *dev = netdev_bsd_cast(netdev_); const char *name = netdev_get_name(netdev_); int error; int i; ovs_mutex_lock(&dev->mutex); if (dev->tap_fd < 0 && !dev->pcap) { error = netdev_bsd_open_pcap(name, &dev->pcap, &dev->fd); } else { error = 0; } for (i = 0; i < cnt; i++) { const void *data = dp_packet_data(pkts[i]); size_t size = dp_packet_size(pkts[i]); while (!error) { ssize_t retval; if (dev->tap_fd >= 0) { retval = write(dev->tap_fd, data, size); } else { retval = pcap_inject(dev->pcap, data, size); } if (retval < 0) { if (errno == EINTR) { continue; } else { error = errno; if (error != EAGAIN) { VLOG_WARN_RL(&rl, "error sending Ethernet packet on" " %s: %s", name, ovs_strerror(error)); } } } else if (retval != size) { VLOG_WARN_RL(&rl, "sent partial Ethernet packet " "(%"PRIuSIZE" bytes of " "%"PRIuSIZE") on %s", retval, size, name); error = EMSGSIZE; } else { break; } } } ovs_mutex_unlock(&dev->mutex); if (may_steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(pkts[i]); } } return error; } /* * Registers with the poll loop to wake up from the next call to poll_block() * when the packet transmission queue has sufficient room to transmit a packet * with netdev_send(). */ static void netdev_bsd_send_wait(struct netdev *netdev_, int qid OVS_UNUSED) { struct netdev_bsd *dev = netdev_bsd_cast(netdev_); ovs_mutex_lock(&dev->mutex); if (dev->tap_fd >= 0) { /* TAP device always accepts packets. */ poll_immediate_wake(); } else if (dev->pcap) { poll_fd_wait(dev->fd, POLLOUT); } else { /* We haven't even tried to send a packet yet. */ poll_immediate_wake(); } ovs_mutex_unlock(&dev->mutex); } /* * Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, * otherwise a positive errno value. */ static int netdev_bsd_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_ETHERADDR) || !eth_addr_equals(netdev->etheraddr, mac)) { error = set_etheraddr(netdev_get_kernel_name(netdev_), AF_LINK, ETH_ADDR_LEN, mac); if (!error) { netdev->cache_valid |= VALID_ETHERADDR; netdev->etheraddr = mac; netdev_change_seq_changed(netdev_); } } ovs_mutex_unlock(&netdev->mutex); return error; } /* * Returns a pointer to 'netdev''s MAC address. The caller must not modify or * free the returned buffer. */ static int netdev_bsd_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_ETHERADDR)) { error = get_etheraddr(netdev_get_kernel_name(netdev_), &netdev->etheraddr); if (!error) { netdev->cache_valid |= VALID_ETHERADDR; } } if (!error) { *mac = netdev->etheraddr; } ovs_mutex_unlock(&netdev->mutex); return error; } /* * Returns the maximum size of transmitted (and received) packets on 'netdev', * in bytes, not including the hardware header; thus, this is typically 1500 * bytes for Ethernet devices. */ static int netdev_bsd_get_mtu(const struct netdev *netdev_, int *mtup) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_MTU)) { struct ifreq ifr; error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr, SIOCGIFMTU, "SIOCGIFMTU"); if (!error) { netdev->mtu = ifr.ifr_mtu; netdev->cache_valid |= VALID_MTU; } } if (!error) { *mtup = netdev->mtu; } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_bsd_get_ifindex(const struct netdev *netdev_) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int ifindex, error; ovs_mutex_lock(&netdev->mutex); error = get_ifindex(netdev_, &ifindex); ovs_mutex_unlock(&netdev->mutex); return error ? -error : ifindex; } static int netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_CARRIER)) { struct ifmediareq ifmr; memset(&ifmr, 0, sizeof(ifmr)); ovs_strlcpy(ifmr.ifm_name, netdev_get_kernel_name(netdev_), sizeof ifmr.ifm_name); error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr); if (!error) { netdev->carrier = (ifmr.ifm_status & IFM_ACTIVE) == IFM_ACTIVE; netdev->cache_valid |= VALID_CARRIER; /* If the interface doesn't report whether the media is active, * just assume it is active. */ if ((ifmr.ifm_status & IFM_AVALID) == 0) { netdev->carrier = true; } } else { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s", netdev_get_name(netdev_), ovs_strerror(error)); } } if (!error) { *carrier = netdev->carrier; } ovs_mutex_unlock(&netdev->mutex); return error; } static void convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd) { /* * note: UINT64_MAX means unsupported */ stats->rx_packets = ifd->ifi_ipackets; stats->tx_packets = ifd->ifi_opackets; stats->rx_bytes = ifd->ifi_obytes; stats->tx_bytes = ifd->ifi_ibytes; stats->rx_errors = ifd->ifi_ierrors; stats->tx_errors = ifd->ifi_oerrors; stats->rx_dropped = ifd->ifi_iqdrops; stats->tx_dropped = UINT64_MAX; stats->multicast = ifd->ifi_imcasts; stats->collisions = ifd->ifi_collisions; stats->rx_length_errors = UINT64_MAX; stats->rx_over_errors = UINT64_MAX; stats->rx_crc_errors = UINT64_MAX; stats->rx_frame_errors = UINT64_MAX; stats->rx_fifo_errors = UINT64_MAX; stats->rx_missed_errors = UINT64_MAX; stats->tx_aborted_errors = UINT64_MAX; stats->tx_carrier_errors = UINT64_MAX; stats->tx_fifo_errors = UINT64_MAX; stats->tx_heartbeat_errors = UINT64_MAX; stats->tx_window_errors = UINT64_MAX; } static void convert_stats_tap(struct netdev_stats *stats, const struct if_data *ifd) { /* * Similar to convert_stats_system but swapping rxq and tx * because 'ifd' is stats for the network interface side of the * tap device and what the caller wants is one for the character * device side. * * note: UINT64_MAX means unsupported */ stats->rx_packets = ifd->ifi_opackets; stats->tx_packets = ifd->ifi_ipackets; stats->rx_bytes = ifd->ifi_ibytes; stats->tx_bytes = ifd->ifi_obytes; stats->rx_errors = ifd->ifi_oerrors; stats->tx_errors = ifd->ifi_ierrors; stats->rx_dropped = UINT64_MAX; stats->tx_dropped = ifd->ifi_iqdrops; stats->multicast = ifd->ifi_omcasts; stats->collisions = UINT64_MAX; stats->rx_length_errors = UINT64_MAX; stats->rx_over_errors = UINT64_MAX; stats->rx_crc_errors = UINT64_MAX; stats->rx_frame_errors = UINT64_MAX; stats->rx_fifo_errors = UINT64_MAX; stats->rx_missed_errors = UINT64_MAX; stats->tx_aborted_errors = UINT64_MAX; stats->tx_carrier_errors = UINT64_MAX; stats->tx_fifo_errors = UINT64_MAX; stats->tx_heartbeat_errors = UINT64_MAX; stats->tx_window_errors = UINT64_MAX; } static void convert_stats(const struct netdev *netdev, struct netdev_stats *stats, const struct if_data *ifd) { if (netdev_bsd_cast(netdev)->tap_fd == -1) { convert_stats_system(stats, ifd); } else { convert_stats_tap(stats, ifd); } } /* Retrieves current device stats for 'netdev'. */ static int netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { #if defined(__FreeBSD__) int if_count, i; int mib[6]; size_t len; struct ifmibdata ifmd; mib[0] = CTL_NET; mib[1] = PF_LINK; mib[2] = NETLINK_GENERIC; mib[3] = IFMIB_SYSTEM; mib[4] = IFMIB_IFCOUNT; len = sizeof(if_count); if (sysctl(mib, 5, &if_count, &len, (void *)0, 0) == -1) { VLOG_DBG_RL(&rl, "%s: sysctl failed: %s", netdev_get_name(netdev_), ovs_strerror(errno)); return errno; } mib[5] = IFDATA_GENERAL; mib[3] = IFMIB_IFDATA; len = sizeof(ifmd); for (i = 1; i <= if_count; i++) { mib[4] = i; //row if (sysctl(mib, 6, &ifmd, &len, (void *)0, 0) == -1) { VLOG_DBG_RL(&rl, "%s: sysctl failed: %s", netdev_get_name(netdev_), ovs_strerror(errno)); return errno; } else if (!strcmp(ifmd.ifmd_name, netdev_get_name(netdev_))) { convert_stats(netdev_, stats, &ifmd.ifmd_data); break; } } return 0; #elif defined(__NetBSD__) struct ifdatareq ifdr; int error; memset(&ifdr, 0, sizeof(ifdr)); ovs_strlcpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_), sizeof(ifdr.ifdr_name)); error = af_link_ioctl(SIOCGIFDATA, &ifdr); if (!error) { convert_stats(netdev_, stats, &ifdr.ifdr_data); } return error; #else #error not implemented #endif } static uint32_t netdev_bsd_parse_media(int media) { uint32_t supported = 0; bool half_duplex = media & IFM_HDX ? true : false; switch (IFM_SUBTYPE(media)) { case IFM_10_2: case IFM_10_5: case IFM_10_STP: case IFM_10_T: supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD; supported |= NETDEV_F_COPPER; break; case IFM_10_FL: supported |= half_duplex ? NETDEV_F_10MB_HD : NETDEV_F_10MB_FD; supported |= NETDEV_F_FIBER; break; case IFM_100_T2: case IFM_100_T4: case IFM_100_TX: case IFM_100_VG: supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD; supported |= NETDEV_F_COPPER; break; case IFM_100_FX: supported |= half_duplex ? NETDEV_F_100MB_HD : NETDEV_F_100MB_FD; supported |= NETDEV_F_FIBER; break; case IFM_1000_CX: case IFM_1000_T: supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD; supported |= NETDEV_F_COPPER; break; case IFM_1000_LX: case IFM_1000_SX: supported |= half_duplex ? NETDEV_F_1GB_HD : NETDEV_F_1GB_FD; supported |= NETDEV_F_FIBER; break; case IFM_10G_CX4: supported |= NETDEV_F_10GB_FD; supported |= NETDEV_F_COPPER; break; case IFM_10G_LR: case IFM_10G_SR: supported |= NETDEV_F_10GB_FD; supported |= NETDEV_F_FIBER; break; default: return 0; } if (IFM_SUBTYPE(media) == IFM_AUTO) { supported |= NETDEV_F_AUTONEG; } /* if (media & IFM_ETH_FMASK) { supported |= NETDEV_F_PAUSE; } */ return supported; } /* * Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value is a * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if * successful, otherwise a positive errno value. On failure, all of the * passed-in values are set to 0. */ static int netdev_bsd_get_features(const struct netdev *netdev, enum netdev_features *current, uint32_t *advertised, enum netdev_features *supported, uint32_t *peer) { struct ifmediareq ifmr; int *media_list; int i; int error; /* XXX Look into SIOCGIFCAP instead of SIOCGIFMEDIA */ memset(&ifmr, 0, sizeof(ifmr)); ovs_strlcpy(ifmr.ifm_name, netdev_get_name(netdev), sizeof ifmr.ifm_name); /* We make two SIOCGIFMEDIA ioctl calls. The first to determine the * number of supported modes, and a second with a buffer to retrieve * them. */ error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr); if (error) { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s", netdev_get_name(netdev), ovs_strerror(error)); return error; } media_list = xcalloc(ifmr.ifm_count, sizeof(int)); ifmr.ifm_ulist = media_list; if (IFM_TYPE(ifmr.ifm_current) != IFM_ETHER) { VLOG_DBG_RL(&rl, "%s: doesn't appear to be ethernet", netdev_get_name(netdev)); error = EINVAL; goto cleanup; } error = af_inet_ioctl(SIOCGIFMEDIA, &ifmr); if (error) { VLOG_DBG_RL(&rl, "%s: ioctl(SIOCGIFMEDIA) failed: %s", netdev_get_name(netdev), ovs_strerror(error)); goto cleanup; } /* Current settings. */ *current = netdev_bsd_parse_media(ifmr.ifm_active); /* Advertised features. */ *advertised = netdev_bsd_parse_media(ifmr.ifm_current); /* Supported features. */ *supported = 0; for (i = 0; i < ifmr.ifm_count; i++) { *supported |= netdev_bsd_parse_media(ifmr.ifm_ulist[i]); } /* Peer advertisements. */ *peer = 0; /* XXX */ error = 0; cleanup: free(media_list); return error; } /* * If 'netdev' has an assigned IPv4 address, sets '*in4' to that address and * '*netmask' to its netmask and returns true. Otherwise, returns false. */ static int netdev_bsd_get_in4(const struct netdev *netdev_, struct in_addr *in4, struct in_addr *netmask) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_IN4)) { struct ifreq ifr; ifr.ifr_addr.sa_family = AF_INET; error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr, SIOCGIFADDR, "SIOCGIFADDR"); if (!error) { const struct sockaddr_in *sin; sin = ALIGNED_CAST(struct sockaddr_in *, &ifr.ifr_addr); netdev->in4 = sin->sin_addr; netdev->cache_valid |= VALID_IN4; error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev_), &ifr, SIOCGIFNETMASK, "SIOCGIFNETMASK"); if (!error) { *netmask = sin->sin_addr; } } } if (!error) { *in4 = netdev->in4; *netmask = netdev->netmask; } ovs_mutex_unlock(&netdev->mutex); return error ? error : in4->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; } /* * Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a * positive errno value. */ static int netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr, struct in_addr mask) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", addr); if (!error) { if (addr.s_addr != INADDR_ANY) { error = do_set_addr(netdev_, SIOCSIFNETMASK, "SIOCSIFNETMASK", mask); if (!error) { netdev->cache_valid |= VALID_IN4; netdev->in4 = addr; netdev->netmask = mask; } } netdev_change_seq_changed(netdev_); } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); if (!(netdev->cache_valid & VALID_IN6)) { struct ifaddrs *ifa, *head; struct sockaddr_in6 *sin6; const char *netdev_name = netdev_get_name(netdev_); if (getifaddrs(&head) != 0) { VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name, ovs_strerror(errno)); return errno; } for (ifa = head; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_INET6 && !strcmp(ifa->ifa_name, netdev_name)) { sin6 = ALIGNED_CAST(struct sockaddr_in6 *, ifa->ifa_addr); if (sin6) { memcpy(&netdev->in6, &sin6->sin6_addr, sin6->sin6_len); netdev->cache_valid |= VALID_IN6; *in6 = netdev->in6; freeifaddrs(head); return 0; } } } return EADDRNOTAVAIL; } *in6 = netdev->in6; return 0; } #if defined(__NetBSD__) static char * netdev_bsd_kernel_name_to_ovs_name(const char *kernel_name) { char *ovs_name = NULL; struct shash device_shash; struct shash_node *node; shash_init(&device_shash); netdev_get_devices(&netdev_tap_class, &device_shash); SHASH_FOR_EACH(node, &device_shash) { struct netdev *netdev = node->data; struct netdev_bsd * const dev = netdev_bsd_cast(netdev); if (!strcmp(dev->kernel_name, kernel_name)) { free(ovs_name); ovs_name = xstrdup(netdev_get_name(&dev->up)); } netdev_close(netdev); } shash_destroy(&device_shash); return ovs_name ? ovs_name : xstrdup(kernel_name); } #endif static int netdev_bsd_get_next_hop(const struct in_addr *host OVS_UNUSED, struct in_addr *next_hop OVS_UNUSED, char **netdev_name OVS_UNUSED) { #if defined(__NetBSD__) static int seq = 0; struct sockaddr_in sin; struct sockaddr_dl sdl; int s; int i; struct { struct rt_msghdr h; char space[512]; } buf; struct rt_msghdr *rtm = &buf.h; const pid_t pid = getpid(); char *cp; ssize_t ssz; bool gateway = false; char *ifname = NULL; int saved_errno; memset(next_hop, 0, sizeof(*next_hop)); *netdev_name = NULL; memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; sin.sin_port = 0; sin.sin_addr = *host; memset(&sdl, 0, sizeof(sdl)); sdl.sdl_len = sizeof(sdl); sdl.sdl_family = AF_LINK; s = socket(PF_ROUTE, SOCK_RAW, 0); memset(&buf, 0, sizeof(buf)); rtm->rtm_flags = RTF_HOST|RTF_UP; rtm->rtm_version = RTM_VERSION; rtm->rtm_addrs = RTA_DST|RTA_IFP; cp = (void *)&buf.space; memcpy(cp, &sin, sizeof(sin)); RT_ADVANCE(cp, (struct sockaddr *)(void *)&sin); memcpy(cp, &sdl, sizeof(sdl)); RT_ADVANCE(cp, (struct sockaddr *)(void *)&sdl); rtm->rtm_msglen = cp - (char *)(void *)rtm; rtm->rtm_seq = ++seq; rtm->rtm_type = RTM_GET; rtm->rtm_pid = pid; write(s, rtm, rtm->rtm_msglen); memset(&buf, 0, sizeof(buf)); do { ssz = read(s, &buf, sizeof(buf)); } while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); saved_errno = errno; close(s); if (ssz <= 0) { if (ssz < 0) { return saved_errno; } return EPIPE; /* XXX */ } cp = (void *)&buf.space; for (i = 1; i; i <<= 1) { if ((rtm->rtm_addrs & i) != 0) { const struct sockaddr *sa = (const void *)cp; if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) { const struct sockaddr_in * const sin = ALIGNED_CAST(const struct sockaddr_in *, sa); *next_hop = sin->sin_addr; gateway = true; } if ((i == RTA_IFP) && sa->sa_family == AF_LINK) { const struct sockaddr_dl * const sdl = ALIGNED_CAST(const struct sockaddr_dl *, sa); char *kernel_name; kernel_name = xmemdup0(sdl->sdl_data, sdl->sdl_nlen); ifname = netdev_bsd_kernel_name_to_ovs_name(kernel_name); free(kernel_name); } RT_ADVANCE(cp, sa); } } if (ifname == NULL) { return ENXIO; } if (!gateway) { *next_hop = *host; } *netdev_name = ifname; VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s", IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name); return 0; #else return EOPNOTSUPP; #endif } static int netdev_bsd_arp_lookup(const struct netdev *netdev OVS_UNUSED, ovs_be32 ip OVS_UNUSED, struct eth_addr *mac OVS_UNUSED) { #if defined(__NetBSD__) const struct rt_msghdr *rtm; size_t needed; char *buf; const char *cp; const char *ep; int mib[6]; int error; buf = NULL; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; mib[5] = RTF_LLINFO; if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) { error = errno; goto error; } buf = xmalloc(needed); if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) { error = errno; goto error; } ep = buf + needed; for (cp = buf; cp < ep; cp += rtm->rtm_msglen) { const struct sockaddr_inarp *sina; const struct sockaddr_dl *sdl; rtm = (const void *)cp; sina = (const void *)(rtm + 1); if (ip != sina->sin_addr.s_addr) { continue; } sdl = (const void *) ((const char *)(const void *)sina + RT_ROUNDUP(sina->sin_len)); if (sdl->sdl_alen == ETH_ADDR_LEN) { memcpy(mac, &sdl->sdl_data[sdl->sdl_nlen], ETH_ADDR_LEN); error = 0; goto error; } } error = ENXIO; error: free(buf); return error; #else return EOPNOTSUPP; #endif } static void make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr) { struct sockaddr_in sin; memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr = addr; sin.sin_port = 0; memset(sa, 0, sizeof *sa); memcpy(sa, &sin, sizeof sin); } static int do_set_addr(struct netdev *netdev, unsigned long ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; make_in4_sockaddr(&ifr.ifr_addr, addr); return af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, ioctl_nr, ioctl_name); } static int nd_to_iff_flags(enum netdev_flags nd) { int iff = 0; if (nd & NETDEV_UP) { iff |= IFF_UP; } if (nd & NETDEV_PROMISC) { iff |= IFF_PROMISC; #if defined(IFF_PPROMISC) iff |= IFF_PPROMISC; #endif } if (nd & NETDEV_LOOPBACK) { iff |= IFF_LOOPBACK; } return iff; } static int iff_to_nd_flags(int iff) { enum netdev_flags nd = 0; if (iff & IFF_UP) { nd |= NETDEV_UP; } if (iff & IFF_PROMISC) { nd |= NETDEV_PROMISC; } if (iff & IFF_LOOPBACK) { nd |= NETDEV_LOOPBACK; } return nd; } static int netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { int old_flags, new_flags; int error; error = get_flags(netdev_, &old_flags); if (!error) { *old_flagsp = iff_to_nd_flags(old_flags); new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on); if (new_flags != old_flags) { error = set_flags(netdev_get_kernel_name(netdev_), new_flags); netdev_change_seq_changed(netdev_); } } return error; } /* Linux has also different GET_STATS, SET_STATS, * GET_STATUS) */ #define NETDEV_BSD_CLASS(NAME, CONSTRUCT, \ GET_FEATURES) \ { \ NAME, \ \ NULL, /* init */ \ netdev_bsd_run, \ netdev_bsd_wait, \ netdev_bsd_alloc, \ CONSTRUCT, \ netdev_bsd_destruct, \ netdev_bsd_dealloc, \ NULL, /* get_config */ \ NULL, /* set_config */ \ NULL, /* get_tunnel_config */ \ NULL, /* build header */ \ NULL, /* push header */ \ NULL, /* pop header */ \ NULL, /* get_numa_id */ \ NULL, /* set_multiq */ \ \ netdev_bsd_send, \ netdev_bsd_send_wait, \ \ netdev_bsd_set_etheraddr, \ netdev_bsd_get_etheraddr, \ netdev_bsd_get_mtu, \ NULL, /* set_mtu */ \ netdev_bsd_get_ifindex, \ netdev_bsd_get_carrier, \ NULL, /* get_carrier_resets */ \ NULL, /* set_miimon_interval */ \ netdev_bsd_get_stats, \ \ GET_FEATURES, \ NULL, /* set_advertisement */ \ NULL, /* set_policing */ \ NULL, /* get_qos_type */ \ NULL, /* get_qos_capabilities */ \ NULL, /* get_qos */ \ NULL, /* set_qos */ \ NULL, /* get_queue */ \ NULL, /* set_queue */ \ NULL, /* delete_queue */ \ NULL, /* get_queue_stats */ \ NULL, /* queue_dump_start */ \ NULL, /* queue_dump_next */ \ NULL, /* queue_dump_done */ \ NULL, /* dump_queue_stats */ \ \ netdev_bsd_get_in4, \ netdev_bsd_set_in4, \ netdev_bsd_get_in6, \ NULL, /* add_router */ \ netdev_bsd_get_next_hop, \ NULL, /* get_status */ \ netdev_bsd_arp_lookup, /* arp_lookup */ \ \ netdev_bsd_update_flags, \ \ netdev_bsd_rxq_alloc, \ netdev_bsd_rxq_construct, \ netdev_bsd_rxq_destruct, \ netdev_bsd_rxq_dealloc, \ netdev_bsd_rxq_recv, \ netdev_bsd_rxq_wait, \ netdev_bsd_rxq_drain, \ } const struct netdev_class netdev_bsd_class = NETDEV_BSD_CLASS( "system", netdev_bsd_construct_system, netdev_bsd_get_features); const struct netdev_class netdev_tap_class = NETDEV_BSD_CLASS( "tap", netdev_bsd_construct_tap, netdev_bsd_get_features); static void destroy_tap(int fd, const char *name) { struct ifreq ifr; close(fd); strcpy(ifr.ifr_name, name); /* XXX What to do if this call fails? */ af_inet_ioctl(SIOCIFDESTROY, &ifr); } static int get_flags(const struct netdev *netdev, int *flags) { struct ifreq ifr; int error; error = af_inet_ifreq_ioctl(netdev_get_kernel_name(netdev), &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); *flags = ifr_get_flags(&ifr); return error; } static int set_flags(const char *name, int flags) { struct ifreq ifr; ifr_set_flags(&ifr, flags); return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); } static int get_ifindex(const struct netdev *netdev_, int *ifindexp) { struct netdev_bsd *netdev = netdev_bsd_cast(netdev_); *ifindexp = 0; if (!(netdev->cache_valid & VALID_IFINDEX)) { int ifindex = if_nametoindex(netdev_get_name(netdev_)); if (ifindex <= 0) { return errno; } netdev->cache_valid |= VALID_IFINDEX; netdev->ifindex = ifindex; } *ifindexp = netdev->ifindex; return 0; } static int get_etheraddr(const char *netdev_name, struct eth_addr *ea) { struct ifaddrs *head; struct ifaddrs *ifa; struct sockaddr_dl *sdl; if (getifaddrs(&head) != 0) { VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name, ovs_strerror(errno)); return errno; } for (ifa = head; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_LINK) { if (!strcmp(ifa->ifa_name, netdev_name)) { sdl = ALIGNED_CAST(struct sockaddr_dl *, ifa->ifa_addr); if (sdl) { memcpy(ea, LLADDR(sdl), sdl->sdl_alen); freeifaddrs(head); return 0; } } } } VLOG_ERR("could not find ethernet address for %s device", netdev_name); freeifaddrs(head); return ENODEV; } static int set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED, int hwaddr_len OVS_UNUSED, const struct eth_addr mac OVS_UNUSED) { #if defined(__FreeBSD__) struct ifreq ifr; int error; memset(&ifr, 0, sizeof ifr); ovs_strlcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); ifr.ifr_addr.sa_family = hwaddr_family; ifr.ifr_addr.sa_len = hwaddr_len; memcpy(ifr.ifr_addr.sa_data, &mac, hwaddr_len); error = af_inet_ioctl(SIOCSIFLLADDR, &ifr); if (error) { VLOG_ERR("ioctl(SIOCSIFLLADDR) on %s device failed: %s", netdev_name, ovs_strerror(error)); return error; } return 0; #elif defined(__NetBSD__) struct if_laddrreq req; struct sockaddr_dl *sdl; struct sockaddr_storage oldaddr; int error; /* * get the old address, add new one, and then remove old one. */ if (hwaddr_len != ETH_ADDR_LEN) { /* just to be safe about sockaddr storage size */ return EOPNOTSUPP; } memset(&req, 0, sizeof(req)); ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name)); req.addr.ss_len = sizeof(req.addr); req.addr.ss_family = hwaddr_family; sdl = (struct sockaddr_dl *)&req.addr; sdl->sdl_alen = hwaddr_len; error = af_link_ioctl(SIOCGLIFADDR, &req); if (error) { return error; } if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], &mac, hwaddr_len)) { return 0; } oldaddr = req.addr; memset(&req, 0, sizeof(req)); ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name)); req.flags = IFLR_ACTIVE; sdl = (struct sockaddr_dl *)&req.addr; sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data) + hwaddr_len; sdl->sdl_alen = hwaddr_len; sdl->sdl_family = hwaddr_family; memcpy(sdl->sdl_data, &mac, hwaddr_len); error = af_link_ioctl(SIOCALIFADDR, &req); if (error) { return error; } memset(&req, 0, sizeof(req)); ovs_strlcpy(req.iflr_name, netdev_name, sizeof(req.iflr_name)); req.addr = oldaddr; return af_link_ioctl(SIOCDLIFADDR, &req); #else #error not implemented #endif } static int ifr_get_flags(const struct ifreq *ifr) { #ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH return (ifr->ifr_flagshigh << 16) | (ifr->ifr_flags & 0xffff); #else return ifr->ifr_flags; #endif } static void ifr_set_flags(struct ifreq *ifr, int flags) { #ifdef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH ifr->ifr_flags = flags & 0xffff; ifr->ifr_flagshigh = flags >> 16; #else ifr->ifr_flags = flags; #endif } #if defined(__NetBSD__) /* Calls ioctl() on an AF_LINK sock, passing the specified 'command' and * 'arg'. Returns 0 if successful, otherwise a positive errno value. */ int af_link_ioctl(unsigned long command, const void *arg) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int sock; if (ovsthread_once_start(&once)) { sock = socket(AF_LINK, SOCK_DGRAM, 0); if (sock < 0) { sock = -errno; VLOG_ERR("failed to create link socket: %s", ovs_strerror(errno)); } ovsthread_once_done(&once); } return (sock < 0 ? -sock : ioctl(sock, command, arg) == -1 ? errno : 0); } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/dpif-netlink.h0000644000000000000000000000013213534540071017147 xustar0030 mtime=1567801401.377680993 30 atime=1567801402.073686105 30 ctime=1567801424.973854841 openvswitch-2.5.9/lib/dpif-netlink.h0000644000175000017500000000374213534540071020643 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DPIF_NETLINK_H #define DPIF_NETLINK_H 1 #include #include #include #include "odp-netlink.h" #include "flow.h" struct ofpbuf; struct dpif_netlink_vport { /* Generic Netlink header. */ uint8_t cmd; /* ovs_vport header. */ int dp_ifindex; odp_port_t port_no; /* ODPP_NONE if unknown. */ enum ovs_vport_type type; /* Attributes. * * The 'stats' member points to 64-bit data that might only be aligned on * 32-bit boundaries, so use get_unaligned_u64() to access its values. */ const char *name; /* OVS_VPORT_ATTR_NAME. */ uint32_t n_upcall_pids; const uint32_t *upcall_pids; /* OVS_VPORT_ATTR_UPCALL_PID. */ const struct ovs_vport_stats *stats; /* OVS_VPORT_ATTR_STATS. */ const struct nlattr *options; /* OVS_VPORT_ATTR_OPTIONS. */ size_t options_len; }; void dpif_netlink_vport_init(struct dpif_netlink_vport *); int dpif_netlink_vport_transact(const struct dpif_netlink_vport *request, struct dpif_netlink_vport *reply, struct ofpbuf **bufp); int dpif_netlink_vport_get(const char *name, struct dpif_netlink_vport *reply, struct ofpbuf **bufp); bool dpif_netlink_is_internal_device(const char *name); #endif /* dpif-netlink.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/mcast-snooping.c0000644000000000000000000000013213534540071017517 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.761853278 openvswitch-2.5.9/lib/mcast-snooping.c0000644000175000017500000006233613534540071021217 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Red Hat, Inc. * * Based on mac-learning implementation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "mcast-snooping.h" #include #include #include "bitmap.h" #include "byte-order.h" #include "coverage.h" #include "hash.h" #include "list.h" #include "poll-loop.h" #include "timeval.h" #include "entropy.h" #include "unaligned.h" #include "util.h" #include "vlan-bitmap.h" #include "openvswitch/vlog.h" COVERAGE_DEFINE(mcast_snooping_learned); COVERAGE_DEFINE(mcast_snooping_expired); static struct mcast_port_bundle * mcast_snooping_port_lookup(struct ovs_list *list, void *port); static struct mcast_mrouter_bundle * mcast_snooping_mrouter_lookup(struct mcast_snooping *ms, uint16_t vlan, void *port) OVS_REQ_RDLOCK(ms->rwlock); bool mcast_snooping_enabled(const struct mcast_snooping *ms) { return !!ms; } bool mcast_snooping_flood_unreg(const struct mcast_snooping *ms) { return ms->flood_unreg; } bool mcast_snooping_is_query(ovs_be16 igmp_type) { return igmp_type == htons(IGMP_HOST_MEMBERSHIP_QUERY); } bool mcast_snooping_is_membership(ovs_be16 igmp_type) { switch (ntohs(igmp_type)) { case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: case IGMPV3_HOST_MEMBERSHIP_REPORT: case IGMP_HOST_LEAVE_MESSAGE: return true; } return false; } /* Returns the number of seconds since multicast group 'b' was learned in a * port on 'ms'. */ int mcast_bundle_age(const struct mcast_snooping *ms, const struct mcast_group_bundle *b) { time_t remaining = b->expires - time_now(); return ms->idle_time - remaining; } static uint32_t mcast_table_hash(const struct mcast_snooping *ms, const struct in6_addr *grp_addr, uint16_t vlan) { return hash_bytes(grp_addr->s6_addr, 16, hash_2words(ms->secret, vlan)); } static struct mcast_group_bundle * mcast_group_bundle_from_lru_node(struct ovs_list *list) { return CONTAINER_OF(list, struct mcast_group_bundle, bundle_node); } static struct mcast_group * mcast_group_from_lru_node(struct ovs_list *list) { return CONTAINER_OF(list, struct mcast_group, group_node); } /* Searches 'ms' for and returns an mcast group for destination address * 'dip' in 'vlan'. */ struct mcast_group * mcast_snooping_lookup(const struct mcast_snooping *ms, const struct in6_addr *dip, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct mcast_group *grp; uint32_t hash; hash = mcast_table_hash(ms, dip, vlan); HMAP_FOR_EACH_WITH_HASH (grp, hmap_node, hash, &ms->table) { if (grp->vlan == vlan && ipv6_addr_equals(&grp->addr, dip)) { return grp; } } return NULL; } struct mcast_group * mcast_snooping_lookup4(const struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct in6_addr addr = in6_addr_mapped_ipv4(ip4); return mcast_snooping_lookup(ms, &addr, vlan); } /* If the LRU list is not empty, stores the least-recently-used entry * in '*e' and returns true. Otherwise, if the LRU list is empty, * stores NULL in '*e' and return false. */ static bool group_get_lru(const struct mcast_snooping *ms, struct mcast_group **grp) OVS_REQ_RDLOCK(ms->rwlock) { if (!list_is_empty(&ms->group_lru)) { *grp = mcast_group_from_lru_node(ms->group_lru.next); return true; } else { *grp = NULL; return false; } } static unsigned int normalize_idle_time(unsigned int idle_time) { return (idle_time < 15 ? 15 : idle_time > 3600 ? 3600 : idle_time); } /* Creates and returns a new mcast table with an initial mcast aging * timeout of MCAST_ENTRY_DEFAULT_IDLE_TIME seconds and an initial maximum of * MCAST_DEFAULT_MAX entries. */ struct mcast_snooping * mcast_snooping_create(void) { struct mcast_snooping *ms; ms = xmalloc(sizeof *ms); hmap_init(&ms->table); list_init(&ms->group_lru); list_init(&ms->mrouter_lru); list_init(&ms->fport_list); list_init(&ms->rport_list); ms->secret = random_uint32(); ms->idle_time = MCAST_ENTRY_DEFAULT_IDLE_TIME; ms->max_entries = MCAST_DEFAULT_MAX_ENTRIES; ms->need_revalidate = false; ms->flood_unreg = true; ovs_refcount_init(&ms->ref_cnt); ovs_rwlock_init(&ms->rwlock); return ms; } struct mcast_snooping * mcast_snooping_ref(const struct mcast_snooping *ms_) { struct mcast_snooping *ms = CONST_CAST(struct mcast_snooping *, ms_); if (ms) { ovs_refcount_ref(&ms->ref_cnt); } return ms; } /* Unreferences (and possibly destroys) mcast snooping table 'ms'. */ void mcast_snooping_unref(struct mcast_snooping *ms) { if (!mcast_snooping_enabled(ms)) { return; } if (ovs_refcount_unref_relaxed(&ms->ref_cnt) == 1) { mcast_snooping_flush(ms); hmap_destroy(&ms->table); ovs_rwlock_destroy(&ms->rwlock); free(ms); } } /* Changes the mcast aging timeout of 'ms' to 'idle_time' seconds. */ void mcast_snooping_set_idle_time(struct mcast_snooping *ms, unsigned int idle_time) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group *grp; struct mcast_group_bundle *b; int delta; idle_time = normalize_idle_time(idle_time); if (idle_time != ms->idle_time) { delta = (int) idle_time - (int) ms->idle_time; LIST_FOR_EACH (grp, group_node, &ms->group_lru) { LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) { b->expires += delta; } } ms->idle_time = idle_time; } } /* Sets the maximum number of entries in 'ms' to 'max_entries', adjusting it * to be within a reasonable range. */ void mcast_snooping_set_max_entries(struct mcast_snooping *ms, size_t max_entries) OVS_REQ_WRLOCK(ms->rwlock) { ms->max_entries = (max_entries < 10 ? 10 : max_entries > 1000 * 1000 ? 1000 * 1000 : max_entries); } /* Sets if unregistered multicast packets should be flooded to * all ports or only to ports connected to multicast routers * * Returns true if previous state differs from current state, * false otherwise. */ bool mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, bool enable) OVS_REQ_WRLOCK(ms->rwlock) { bool prev = ms->flood_unreg; ms->flood_unreg = enable; return prev != enable; } static struct mcast_group_bundle * mcast_group_bundle_lookup(struct mcast_snooping *ms OVS_UNUSED, struct mcast_group *grp, void *port) OVS_REQ_RDLOCK(ms->rwlock) { struct mcast_group_bundle *b; LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) { if (b->port == port) { return b; } } return NULL; } /* Insert a new bundle to the mcast group or update its * position and expiration if it is already there. */ static struct mcast_group_bundle * mcast_group_insert_bundle(struct mcast_snooping *ms OVS_UNUSED, struct mcast_group *grp, void *port, int idle_time) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group_bundle *b; b = mcast_group_bundle_lookup(ms, grp, port); if (b) { list_remove(&b->bundle_node); } else { b = xmalloc(sizeof *b); list_init(&b->bundle_node); b->port = port; ms->need_revalidate = true; } b->expires = time_now() + idle_time; list_push_back(&grp->bundle_lru, &b->bundle_node); return b; } /* Return true if multicast still has bundles associated. * Return false if there is no bundles. */ static bool mcast_group_has_bundles(struct mcast_group *grp) { return !list_is_empty(&grp->bundle_lru); } /* Delete 'grp' from the 'ms' hash table. * Caller is responsible to clean bundle lru first. */ static void mcast_snooping_flush_group__(struct mcast_snooping *ms, struct mcast_group *grp) { ovs_assert(list_is_empty(&grp->bundle_lru)); hmap_remove(&ms->table, &grp->hmap_node); list_remove(&grp->group_node); free(grp); } /* Flush out mcast group and its bundles */ static void mcast_snooping_flush_group(struct mcast_snooping *ms, struct mcast_group *grp) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group_bundle *b; LIST_FOR_EACH_POP (b, bundle_node, &grp->bundle_lru) { free(b); } mcast_snooping_flush_group__(ms, grp); ms->need_revalidate = true; } /* Delete bundle returning true if it succeeds, * false if it didn't find the group. */ static bool mcast_group_delete_bundle(struct mcast_snooping *ms OVS_UNUSED, struct mcast_group *grp, void *port) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group_bundle *b; LIST_FOR_EACH (b, bundle_node, &grp->bundle_lru) { if (b->port == port) { list_remove(&b->bundle_node); free(b); return true; } } return false; } /* If any bundle has expired, delete it. Returns the number of deleted * bundles. */ static int mcast_snooping_prune_expired(struct mcast_snooping *ms, struct mcast_group *grp) OVS_REQ_WRLOCK(ms->rwlock) { int expired; struct mcast_group_bundle *b, *next_b; time_t timenow = time_now(); expired = 0; LIST_FOR_EACH_SAFE (b, next_b, bundle_node, &grp->bundle_lru) { /* This list is sorted on expiration time. */ if (b->expires > timenow) { break; } list_remove(&b->bundle_node); free(b); expired++; } if (!mcast_group_has_bundles(grp)) { mcast_snooping_flush_group__(ms, grp); expired++; } if (expired) { ms->need_revalidate = true; COVERAGE_ADD(mcast_snooping_expired, expired); } return expired; } /* Add a multicast group to the mdb. If it exists, then * move to the last position in the LRU list. */ bool mcast_snooping_add_group(struct mcast_snooping *ms, const struct in6_addr *addr, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock) { bool learned; struct mcast_group *grp; /* Avoid duplicate packets. */ if (mcast_snooping_mrouter_lookup(ms, vlan, port) || mcast_snooping_port_lookup(&ms->fport_list, port)) { return false; } learned = false; grp = mcast_snooping_lookup(ms, addr, vlan); if (!grp) { uint32_t hash = mcast_table_hash(ms, addr, vlan); if (hmap_count(&ms->table) >= ms->max_entries) { group_get_lru(ms, &grp); mcast_snooping_flush_group(ms, grp); } grp = xmalloc(sizeof *grp); hmap_insert(&ms->table, &grp->hmap_node, hash); grp->addr = *addr; grp->vlan = vlan; list_init(&grp->bundle_lru); learned = true; ms->need_revalidate = true; COVERAGE_INC(mcast_snooping_learned); } else { list_remove(&grp->group_node); } mcast_group_insert_bundle(ms, grp, port, ms->idle_time); /* Mark 'grp' as recently used. */ list_push_back(&ms->group_lru, &grp->group_node); return learned; } bool mcast_snooping_add_group4(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock) { struct in6_addr addr = in6_addr_mapped_ipv4(ip4); return mcast_snooping_add_group(ms, &addr, vlan, port); } int mcast_snooping_add_report(struct mcast_snooping *ms, const struct dp_packet *p, uint16_t vlan, void *port) { ovs_be32 ip4; size_t offset; const struct igmpv3_header *igmpv3; const struct igmpv3_record *record; int count = 0; int ngrp; offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p); igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN); if (!igmpv3) { return 0; } ngrp = ntohs(igmpv3->ngrp); offset += IGMPV3_HEADER_LEN; while (ngrp--) { bool ret; record = dp_packet_at(p, offset, sizeof(struct igmpv3_record)); if (!record) { break; } /* Only consider known record types. */ if (record->type < IGMPV3_MODE_IS_INCLUDE || record->type > IGMPV3_BLOCK_OLD_SOURCES) { continue; } ip4 = get_16aligned_be32(&record->maddr); /* * If record is INCLUDE MODE and there are no sources, it's equivalent * to a LEAVE. */ if (ntohs(record->nsrcs) == 0 && (record->type == IGMPV3_MODE_IS_INCLUDE || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) { ret = mcast_snooping_leave_group4(ms, ip4, vlan, port); } else { ret = mcast_snooping_add_group4(ms, ip4, vlan, port); } if (ret) { count++; } offset += sizeof(*record) + ntohs(record->nsrcs) * sizeof(ovs_be32) + record->aux_len; } return count; } int mcast_snooping_add_mld(struct mcast_snooping *ms, const struct dp_packet *p, uint16_t vlan, void *port) { const struct in6_addr *addr; size_t offset; const struct mld_header *mld; const struct mld2_record *record; int count = 0; int ngrp; bool ret; offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p); mld = dp_packet_at(p, offset, MLD_HEADER_LEN); if (!mld) { return 0; } ngrp = ntohs(mld->ngrp); offset += MLD_HEADER_LEN; addr = dp_packet_at(p, offset, sizeof(struct in6_addr)); switch (mld->type) { case MLD_REPORT: ret = mcast_snooping_add_group(ms, addr, vlan, port); if (ret) { count++; } break; case MLD_DONE: ret = mcast_snooping_leave_group(ms, addr, vlan, port); if (ret) { count++; } break; case MLD2_REPORT: while (ngrp--) { record = dp_packet_at(p, offset, sizeof(struct mld2_record)); if (!record) { break; } /* Only consider known record types. */ if (record->type >= IGMPV3_MODE_IS_INCLUDE && record->type <= IGMPV3_BLOCK_OLD_SOURCES) { struct in6_addr maddr; memcpy(maddr.s6_addr, record->maddr.be16, 16); addr = &maddr; /* * If record is INCLUDE MODE and there are no sources, it's * equivalent to a LEAVE. */ if (record->nsrcs == htons(0) && (record->type == IGMPV3_MODE_IS_INCLUDE || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) { ret = mcast_snooping_leave_group(ms, addr, vlan, port); } else { ret = mcast_snooping_add_group(ms, addr, vlan, port); } if (ret) { count++; } } offset += sizeof(*record) + ntohs(record->nsrcs) * sizeof(struct in6_addr) + record->aux_len; } } return count; } bool mcast_snooping_leave_group(struct mcast_snooping *ms, const struct in6_addr *addr, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group *grp; /* Ports flagged to forward Reports usually have more * than one host behind it, so don't leave the group * on the first message and just let it expire */ if (mcast_snooping_port_lookup(&ms->rport_list, port)) { return false; } grp = mcast_snooping_lookup(ms, addr, vlan); if (grp && mcast_group_delete_bundle(ms, grp, port)) { ms->need_revalidate = true; return true; } return false; } bool mcast_snooping_leave_group4(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) { struct in6_addr addr = in6_addr_mapped_ipv4(ip4); return mcast_snooping_leave_group(ms, &addr, vlan, port); } /* Router ports. */ /* Returns the number of seconds since the multicast router * was learned in a port. */ int mcast_mrouter_age(const struct mcast_snooping *ms OVS_UNUSED, const struct mcast_mrouter_bundle *mrouter) { time_t remaining = mrouter->expires - time_now(); return MCAST_MROUTER_PORT_IDLE_TIME - remaining; } static struct mcast_mrouter_bundle * mcast_mrouter_from_lru_node(struct ovs_list *list) { return CONTAINER_OF(list, struct mcast_mrouter_bundle, mrouter_node); } /* If the LRU list is not empty, stores the least-recently-used mrouter * in '*m' and returns true. Otherwise, if the LRU list is empty, * stores NULL in '*m' and return false. */ static bool mrouter_get_lru(const struct mcast_snooping *ms, struct mcast_mrouter_bundle **m) OVS_REQ_RDLOCK(ms->rwlock) { if (!list_is_empty(&ms->mrouter_lru)) { *m = mcast_mrouter_from_lru_node(ms->mrouter_lru.next); return true; } else { *m = NULL; return false; } } static struct mcast_mrouter_bundle * mcast_snooping_mrouter_lookup(struct mcast_snooping *ms, uint16_t vlan, void *port) OVS_REQ_RDLOCK(ms->rwlock) { struct mcast_mrouter_bundle *mrouter; LIST_FOR_EACH (mrouter, mrouter_node, &ms->mrouter_lru) { if (mrouter->vlan == vlan && mrouter->port == port) { return mrouter; } } return NULL; } bool mcast_snooping_add_mrouter(struct mcast_snooping *ms, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_mrouter_bundle *mrouter; /* Avoid duplicate packets. */ if (mcast_snooping_port_lookup(&ms->fport_list, port)) { return false; } mrouter = mcast_snooping_mrouter_lookup(ms, vlan, port); if (mrouter) { list_remove(&mrouter->mrouter_node); } else { mrouter = xmalloc(sizeof *mrouter); mrouter->vlan = vlan; mrouter->port = port; COVERAGE_INC(mcast_snooping_learned); ms->need_revalidate = true; } mrouter->expires = time_now() + MCAST_MROUTER_PORT_IDLE_TIME; list_push_back(&ms->mrouter_lru, &mrouter->mrouter_node); return ms->need_revalidate; } static void mcast_snooping_flush_mrouter(struct mcast_mrouter_bundle *mrouter) { list_remove(&mrouter->mrouter_node); free(mrouter); } /* Ports */ static struct mcast_port_bundle * mcast_port_from_list_node(struct ovs_list *list) { return CONTAINER_OF(list, struct mcast_port_bundle, node); } /* If the list is not empty, stores the fport in '*f' and returns true. * Otherwise, if the list is empty, stores NULL in '*f' and return false. */ static bool mcast_snooping_port_get(const struct ovs_list *list, struct mcast_port_bundle **f) { if (!list_is_empty(list)) { *f = mcast_port_from_list_node(list->next); return true; } else { *f = NULL; return false; } } static struct mcast_port_bundle * mcast_snooping_port_lookup(struct ovs_list *list, void *port) { struct mcast_port_bundle *pbundle; LIST_FOR_EACH (pbundle, node, list) { if (pbundle->port == port) { return pbundle; } } return NULL; } static void mcast_snooping_add_port(struct ovs_list *list, void *port) { struct mcast_port_bundle *pbundle; pbundle = xmalloc(sizeof *pbundle); pbundle->port = port; list_insert(list, &pbundle->node); } static void mcast_snooping_flush_port(struct mcast_port_bundle *pbundle) { list_remove(&pbundle->node); free(pbundle); } /* Flood ports. */ void mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port, bool flood) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_port_bundle *fbundle; fbundle = mcast_snooping_port_lookup(&ms->fport_list, port); if (flood && !fbundle) { mcast_snooping_add_port(&ms->fport_list, port); ms->need_revalidate = true; } else if (!flood && fbundle) { mcast_snooping_flush_port(fbundle); ms->need_revalidate = true; } } /* Flood Reports ports. */ void mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms, void *port, bool flood) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_port_bundle *pbundle; pbundle = mcast_snooping_port_lookup(&ms->rport_list, port); if (flood && !pbundle) { mcast_snooping_add_port(&ms->rport_list, port); ms->need_revalidate = true; } else if (!flood && pbundle) { mcast_snooping_flush_port(pbundle); ms->need_revalidate = true; } } /* Run and flush. */ static void mcast_snooping_mdb_flush__(struct mcast_snooping *ms) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group *grp; struct mcast_mrouter_bundle *mrouter; while (group_get_lru(ms, &grp)) { mcast_snooping_flush_group(ms, grp); } hmap_shrink(&ms->table); while (mrouter_get_lru(ms, &mrouter)) { mcast_snooping_flush_mrouter(mrouter); } } void mcast_snooping_mdb_flush(struct mcast_snooping *ms) { if (!mcast_snooping_enabled(ms)) { return; } ovs_rwlock_wrlock(&ms->rwlock); mcast_snooping_mdb_flush__(ms); ovs_rwlock_unlock(&ms->rwlock); } /* Flushes mdb and flood ports. */ static void mcast_snooping_flush__(struct mcast_snooping *ms) OVS_REQ_WRLOCK(ms->rwlock) { struct mcast_group *grp; struct mcast_mrouter_bundle *mrouter; struct mcast_port_bundle *pbundle; while (group_get_lru(ms, &grp)) { mcast_snooping_flush_group(ms, grp); } hmap_shrink(&ms->table); /* flush multicast routers */ while (mrouter_get_lru(ms, &mrouter)) { mcast_snooping_flush_mrouter(mrouter); } /* flush flood ports */ while (mcast_snooping_port_get(&ms->fport_list, &pbundle)) { mcast_snooping_flush_port(pbundle); } /* flush flood report ports */ while (mcast_snooping_port_get(&ms->rport_list, &pbundle)) { mcast_snooping_flush_port(pbundle); } } void mcast_snooping_flush(struct mcast_snooping *ms) { if (!mcast_snooping_enabled(ms)) { return; } ovs_rwlock_wrlock(&ms->rwlock); mcast_snooping_flush__(ms); ovs_rwlock_unlock(&ms->rwlock); } static bool mcast_snooping_run__(struct mcast_snooping *ms) OVS_REQ_WRLOCK(ms->rwlock) { bool need_revalidate; struct mcast_group *grp; struct mcast_mrouter_bundle *mrouter; int mrouter_expired; while (group_get_lru(ms, &grp)) { if (hmap_count(&ms->table) > ms->max_entries) { mcast_snooping_flush_group(ms, grp); } else { if (!mcast_snooping_prune_expired(ms, grp)) { break; } } } hmap_shrink(&ms->table); mrouter_expired = 0; while (mrouter_get_lru(ms, &mrouter) && time_now() >= mrouter->expires) { mcast_snooping_flush_mrouter(mrouter); mrouter_expired++; } if (mrouter_expired) { ms->need_revalidate = true; COVERAGE_ADD(mcast_snooping_expired, mrouter_expired); } need_revalidate = ms->need_revalidate; ms->need_revalidate = false; return need_revalidate; } /* Does periodic work required by 'ms'. Returns true if something changed * that may require flow revalidation. */ bool mcast_snooping_run(struct mcast_snooping *ms) { bool need_revalidate; if (!mcast_snooping_enabled(ms)) { return false; } ovs_rwlock_wrlock(&ms->rwlock); need_revalidate = mcast_snooping_run__(ms); ovs_rwlock_unlock(&ms->rwlock); return need_revalidate; } static void mcast_snooping_wait__(struct mcast_snooping *ms) OVS_REQ_RDLOCK(ms->rwlock) { if (hmap_count(&ms->table) > ms->max_entries || ms->need_revalidate) { poll_immediate_wake(); } else { struct mcast_group *grp; struct mcast_group_bundle *bundle; struct mcast_mrouter_bundle *mrouter; long long int mrouter_msec; long long int msec = 0; if (!list_is_empty(&ms->group_lru)) { grp = mcast_group_from_lru_node(ms->group_lru.next); bundle = mcast_group_bundle_from_lru_node(grp->bundle_lru.next); msec = bundle->expires * 1000LL; } if (!list_is_empty(&ms->mrouter_lru)) { mrouter = mcast_mrouter_from_lru_node(ms->mrouter_lru.next); mrouter_msec = mrouter->expires * 1000LL; msec = msec ? MIN(msec, mrouter_msec) : mrouter_msec; } if (msec) { poll_timer_wait_until(msec); } } } void mcast_snooping_wait(struct mcast_snooping *ms) { if (!mcast_snooping_enabled(ms)) { return; } ovs_rwlock_rdlock(&ms->rwlock); mcast_snooping_wait__(ms); ovs_rwlock_unlock(&ms->rwlock); } openvswitch-2.5.9/lib/PaxHeaders.82075/svec.c0000644000000000000000000000013213534540071015516 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.901854311 openvswitch-2.5.9/lib/svec.c0000644000175000017500000001761113534540071017212 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "svec.h" #include #include #include #include "dynamic-string.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(svec); void svec_init(struct svec *svec) { svec->names = NULL; svec->n = 0; svec->allocated = 0; } void svec_clone(struct svec *svec, const struct svec *other) { svec_init(svec); svec_append(svec, other); } void svec_destroy(struct svec *svec) { svec_clear(svec); free(svec->names); } void svec_clear(struct svec *svec) { size_t i; for (i = 0; i < svec->n; i++) { free(svec->names[i]); } svec->n = 0; } bool svec_is_empty(const struct svec *svec) { return svec->n == 0; } void svec_add(struct svec *svec, const char *name) { svec_add_nocopy(svec, xstrdup(name)); } void svec_del(struct svec *svec, const char *name) { size_t offset; offset = svec_find(svec, name); if (offset != SIZE_MAX) { free(svec->names[offset]); memmove(&svec->names[offset], &svec->names[offset + 1], sizeof *svec->names * (svec->n - offset - 1)); svec->n--; } } static void svec_expand(struct svec *svec) { if (svec->n >= svec->allocated) { svec->names = x2nrealloc(svec->names, &svec->allocated, sizeof *svec->names); } } void svec_add_nocopy(struct svec *svec, char *name) { svec_expand(svec); svec->names[svec->n++] = name; } void svec_append(struct svec *svec, const struct svec *other) { size_t i; for (i = 0; i < other->n; i++) { svec_add(svec, other->names[i]); } } void svec_terminate(struct svec *svec) { svec_expand(svec); svec->names[svec->n] = NULL; } static int compare_strings(const void *a_, const void *b_) { char *const *a = a_; char *const *b = b_; return strcmp(*a, *b); } void svec_sort(struct svec *svec) { qsort(svec->names, svec->n, sizeof *svec->names, compare_strings); } void svec_sort_unique(struct svec *svec) { svec_sort(svec); svec_unique(svec); } void svec_unique(struct svec *svec) { ovs_assert(svec_is_sorted(svec)); if (svec->n > 1) { /* This algorithm is lazy and sub-optimal, but it's "obviously correct" * and asymptotically optimal . */ struct svec tmp; size_t i; svec_init(&tmp); svec_add(&tmp, svec->names[0]); for (i = 1; i < svec->n; i++) { if (strcmp(svec->names[i - 1], svec->names[i])) { svec_add(&tmp, svec->names[i]); } } svec_swap(&tmp, svec); svec_destroy(&tmp); } } void svec_compact(struct svec *svec) { size_t i, j; for (i = j = 0; i < svec->n; i++) { if (svec->names[i] != NULL) { svec->names[j++] = svec->names[i]; } } svec->n = j; } void svec_diff(const struct svec *a, const struct svec *b, struct svec *a_only, struct svec *both, struct svec *b_only) { size_t i, j; ovs_assert(svec_is_sorted(a)); ovs_assert(svec_is_sorted(b)); if (a_only) { svec_init(a_only); } if (both) { svec_init(both); } if (b_only) { svec_init(b_only); } for (i = j = 0; i < a->n && j < b->n; ) { int cmp = strcmp(a->names[i], b->names[j]); if (cmp < 0) { if (a_only) { svec_add(a_only, a->names[i]); } i++; } else if (cmp > 0) { if (b_only) { svec_add(b_only, b->names[j]); } j++; } else { if (both) { svec_add(both, a->names[i]); } i++; j++; } } if (a_only) { for (; i < a->n; i++) { svec_add(a_only, a->names[i]); } } if (b_only) { for (; j < b->n; j++) { svec_add(b_only, b->names[j]); } } } bool svec_contains(const struct svec *svec, const char *name) { return svec_find(svec, name) != SIZE_MAX; } size_t svec_find(const struct svec *svec, const char *name) { char **p; ovs_assert(svec_is_sorted(svec)); p = bsearch(&name, svec->names, svec->n, sizeof *svec->names, compare_strings); return p ? p - svec->names : SIZE_MAX; } bool svec_is_sorted(const struct svec *svec) { size_t i; for (i = 1; i < svec->n; i++) { if (strcmp(svec->names[i - 1], svec->names[i]) > 0) { return false; } } return true; } bool svec_is_unique(const struct svec *svec) { return svec_get_duplicate(svec) == NULL; } const char * svec_get_duplicate(const struct svec *svec) { ovs_assert(svec_is_sorted(svec)); if (svec->n > 1) { size_t i; for (i = 1; i < svec->n; i++) { if (!strcmp(svec->names[i - 1], svec->names[i])) { return svec->names[i]; } } } return NULL; } void svec_swap(struct svec *a, struct svec *b) { struct svec tmp = *a; *a = *b; *b = tmp; } void svec_print(const struct svec *svec, const char *title) { size_t i; printf("%s:\n", title); for (i = 0; i < svec->n; i++) { printf("\"%s\"\n", svec->names[i]); } } /* Breaks 'words' into words at white space, respecting shell-like quoting * conventions, and appends the words to 'svec'. */ void svec_parse_words(struct svec *svec, const char *words) { struct ds word = DS_EMPTY_INITIALIZER; const char *p, *q; for (p = words; *p != '\0'; p = q) { int quote = 0; while (isspace((unsigned char) *p)) { p++; } if (*p == '\0') { break; } ds_clear(&word); for (q = p; *q != '\0'; q++) { if (*q == quote) { quote = 0; } else if (*q == '\'' || *q == '"') { quote = *q; } else if (*q == '\\' && (!quote || quote == '"')) { q++; if (*q == '\0') { VLOG_WARN("%s: ends in trailing backslash", words); break; } ds_put_char(&word, *q); } else if (isspace((unsigned char) *q) && !quote) { q++; break; } else { ds_put_char(&word, *q); } } svec_add(svec, ds_cstr(&word)); if (quote) { VLOG_WARN("%s: word ends inside quoted string", words); } } ds_destroy(&word); } bool svec_equal(const struct svec *a, const struct svec *b) { size_t i; if (a->n != b->n) { return false; } for (i = 0; i < a->n; i++) { if (strcmp(a->names[i], b->names[i])) { return false; } } return true; } char * svec_join(const struct svec *svec, const char *delimiter, const char *terminator) { struct ds ds; size_t i; ds_init(&ds); for (i = 0; i < svec->n; i++) { if (i) { ds_put_cstr(&ds, delimiter); } ds_put_cstr(&ds, svec->names[i]); } ds_put_cstr(&ds, terminator); return ds_cstr(&ds); } const char * svec_back(const struct svec *svec) { ovs_assert(svec->n); return svec->names[svec->n - 1]; } void svec_pop_back(struct svec *svec) { ovs_assert(svec->n); free(svec->names[--svec->n]); } openvswitch-2.5.9/lib/PaxHeaders.82075/daemon-private.h0000644000000000000000000000013213534540071017476 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.693852778 openvswitch-2.5.9/lib/daemon-private.h0000644000175000017500000000140413534540071021163 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DAEMON_PRIVATE_H #define DAEMON_PRIVATE_H 1 extern bool detach; extern char *pidfile; char *make_pidfile_name(const char *name); #endif /* daemon-private.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/hash.c0000644000000000000000000000013213534540071015501 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.729853043 openvswitch-2.5.9/lib/hash.c0000644000175000017500000001634713534540071017202 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "hash.h" #include #include "unaligned.h" /* Returns the hash of 'a', 'b', and 'c'. */ uint32_t hash_3words(uint32_t a, uint32_t b, uint32_t c) { return hash_finish(hash_add(hash_add(hash_add(a, 0), b), c), 12); } /* Returns the hash of the 'n' bytes at 'p', starting from 'basis'. */ uint32_t hash_bytes(const void *p_, size_t n, uint32_t basis) { const uint32_t *p = p_; size_t orig_n = n; uint32_t hash; hash = basis; while (n >= 4) { hash = hash_add(hash, get_unaligned_u32(p)); n -= 4; p += 1; } if (n) { uint32_t tmp = 0; memcpy(&tmp, p, n); hash = hash_add(hash, tmp); } return hash_finish(hash, orig_n); } uint32_t hash_double(double x, uint32_t basis) { uint32_t value[2]; BUILD_ASSERT_DECL(sizeof x == sizeof value); memcpy(value, &x, sizeof value); return hash_3words(value[0], value[1], basis); } uint32_t hash_words__(const uint32_t p[], size_t n_words, uint32_t basis) { return hash_words_inline(p, n_words, basis); } uint32_t hash_words64__(const uint64_t p[], size_t n_words, uint32_t basis) { return hash_words64_inline(p, n_words, basis); } #if !(defined(__x86_64__)) void hash_bytes128(const void *p_, size_t len, uint32_t basis, ovs_u128 *out) { const uint32_t c1 = 0x239b961b; const uint32_t c2 = 0xab0e9789; const uint32_t c3 = 0x38b34ae5; const uint32_t c4 = 0xa1e38b93; const uint8_t *tail, *data = (const uint8_t *)p_; const uint32_t *blocks = (const uint32_t *)p_; const int nblocks = len / 16; uint32_t h1 = basis; uint32_t h2 = basis; uint32_t h3 = basis; uint32_t h4 = basis; uint32_t k1, k2, k3, k4; /* Body */ for (int i = 0; i < nblocks; i++) { uint32_t k1 = get_unaligned_u32(&blocks[i * 4 + 0]); uint32_t k2 = get_unaligned_u32(&blocks[i * 4 + 1]); uint32_t k3 = get_unaligned_u32(&blocks[i * 4 + 2]); uint32_t k4 = get_unaligned_u32(&blocks[i * 4 + 3]); k1 *= c1; k1 = hash_rot(k1, 15); k1 *= c2; h1 ^= k1; h1 = hash_rot(h1, 19); h1 += h2; h1 = h1 * 5 + 0x561ccd1b; k2 *= c2; k2 = hash_rot(k2, 16); k2 *= c3; h2 ^= k2; h2 = hash_rot(h2, 17); h2 += h3; h2 = h2 * 5 + 0x0bcaa747; k3 *= c3; k3 = hash_rot(k3, 17); k3 *= c4; h3 ^= k3; h3 = hash_rot(h3, 15); h3 += h4; h3 = h3 * 5 + 0x96cd1c35; k4 *= c4; k4 = hash_rot(k4, 18); k4 *= c1; h4 ^= k4; h4 = hash_rot(h4, 13); h4 += h1; h4 = h4 * 5 + 0x32ac3b17; } /* Tail */ k1 = k2 = k3 = k4 = 0; tail = data + nblocks * 16; switch (len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = hash_rot(k4, 18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[9] << 8; case 9: k3 ^= tail[8] << 0; k3 *= c3; k3 = hash_rot(k3, 17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[7] << 24; case 7: k2 ^= tail[6] << 16; case 6: k2 ^= tail[5] << 8; case 5: k2 ^= tail[4] << 0; k2 *= c2; k2 = hash_rot(k2, 16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[3] << 24; case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0] << 0; k1 *= c1; k1 = hash_rot(k1, 15); k1 *= c2; h1 ^= k1; }; /* Finalization */ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = mhash_finish(h1); h2 = mhash_finish(h2); h3 = mhash_finish(h3); h4 = mhash_finish(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; out->u32[0] = h1; out->u32[1] = h2; out->u32[2] = h3; out->u32[3] = h4; } #else /* __x86_64__ */ static inline uint64_t hash_rot64(uint64_t x, int8_t r) { return (x << r) | (x >> (64 - r)); } static inline uint64_t fmix64(uint64_t k) { k ^= k >> 33; k *= 0xff51afd7ed558ccdULL; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53ULL; k ^= k >> 33; return k; } void hash_bytes128(const void *p_, size_t len, uint32_t basis, ovs_u128 *out) { const uint64_t c1 = 0x87c37b91114253d5ULL; const uint64_t c2 = 0x4cf5ad432745937fULL; const uint8_t *tail, *data = (const uint8_t *)p_; const uint64_t *blocks = (const uint64_t *)p_; const int nblocks = len / 16; uint64_t h1 = basis; uint64_t h2 = basis; uint64_t k1, k2; /* Body */ for (int i = 0; i < nblocks; i++) { k1 = get_unaligned_u64(&blocks[i * 2 + 0]); k2 = get_unaligned_u64(&blocks[i * 2 + 1]); k1 *= c1; k1 = hash_rot64(k1, 31); k1 *= c2; h1 ^= k1; h1 = hash_rot64(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; k2 *= c2; k2 = hash_rot64(k2, 33); k2 *= c1; h2 ^= k2; h2 = hash_rot64(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } /* Tail */ k1 = 0; k2 = 0; tail = data + nblocks * 16; switch (len & 15) { case 15: k2 ^= ((uint64_t) tail[14]) << 48; case 14: k2 ^= ((uint64_t) tail[13]) << 40; case 13: k2 ^= ((uint64_t) tail[12]) << 32; case 12: k2 ^= ((uint64_t) tail[11]) << 24; case 11: k2 ^= ((uint64_t) tail[10]) << 16; case 10: k2 ^= ((uint64_t) tail[9]) << 8; case 9: k2 ^= ((uint64_t) tail[8]) << 0; k2 *= c2; k2 = hash_rot64(k2, 33); k2 *= c1; h2 ^= k2; case 8: k1 ^= ((uint64_t) tail[7]) << 56; case 7: k1 ^= ((uint64_t) tail[6]) << 48; case 6: k1 ^= ((uint64_t) tail[5]) << 40; case 5: k1 ^= ((uint64_t) tail[4]) << 32; case 4: k1 ^= ((uint64_t) tail[3]) << 24; case 3: k1 ^= ((uint64_t) tail[2]) << 16; case 2: k1 ^= ((uint64_t) tail[1]) << 8; case 1: k1 ^= ((uint64_t) tail[0]) << 0; k1 *= c1; k1 = hash_rot64(k1, 31); k1 *= c2; h1 ^= k1; }; /* Finalization */ h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; out->u64.lo = h1; out->u64.hi = h2; } #endif /* __x86_64__ */ openvswitch-2.5.9/lib/PaxHeaders.82075/valgrind.h0000644000000000000000000000013213534540071016371 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.937854576 openvswitch-2.5.9/lib/valgrind.h0000644000175000017500000000141413534540071020057 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VALGRIND_H #define VALGRIND_H 1 #ifdef HAVE_VALGRIND_VALGRIND_H #include #else #define RUNNING_ON_VALGRIND 0 #endif #endif /* valgrind.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/rculist.h0000644000000000000000000000013213534540071016250 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.861854016 openvswitch-2.5.9/lib/rculist.h0000644000175000017500000003430213534540071017740 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RCULIST_H #define RCULIST_H 1 /* A single writer multiple RCU-reader doubly linked list. * * RCU readers may iterate over the list at the same time as a writer is * modifying the list. Multiple writers can be supported by use of mutual * exclusion, but rculist does not provide that, as the user of rculist * typically does that already. * * To be RCU-friendly, the struct rculist instances must be freed via * ovsrcu_postpone(). * * The API is almost the same as for struct ovs_list, with the following * exeptions: * * - The 'prev' pointer may not be accessed by the user. * - The 'next' pointer should be accessed via rculist_next() by readers, and * rculist_next_protected() by the writer. * - No rculist_moved(): due to the memory management limitation stated above, * rculist instances may not be reallocated, as realloc may instantly free * the old memory. * - rculist_front() returns a const pointer to accommodate for an RCU reader. * - rculist_splice_hidden(): Spliced elements may not have been visible to * RCU readers before the operation. * - rculist_poison(): Only poisons the 'prev' pointer. * * The following functions are variations of the struct ovs_list functions with * similar names, but are now restricted to the writer use: * * - rculist_back_protected() * - rculist_is_short_protected() * - rculist_is_singleton_protected() */ #include #include #include "ovs-rcu.h" #include "util.h" /* A non-existing mutex to make it more difficult for an user to accidentally * keep using the 'prev' pointer. This may be helpful when porting code from * struct ovs_list to rculist. */ extern struct ovs_mutex rculist_fake_mutex; /* Doubly linked list head or element. */ struct rculist { /* Previous list element. */ struct rculist *prev OVS_GUARDED_BY(rculist_fake_mutex); /* Next list element. */ OVSRCU_TYPE(struct rculist *) next; }; /* Easier access to 'next' member. */ static inline const struct rculist *rculist_next(const struct rculist *); static inline struct rculist *rculist_next_protected(const struct rculist *); /* List initialization. */ #define RCUOVS_LIST_INITIALIZER(LIST) { LIST, OVSRCU_INITIALIZER(LIST) } static inline void rculist_init(struct rculist *list); static inline void rculist_poison(struct rculist *elem); /* List insertion. */ static inline void rculist_insert(struct rculist *list, struct rculist *elem); static inline void rculist_splice_hidden(struct rculist *before, struct rculist *first, struct rculist *last); static inline void rculist_push_front(struct rculist *list, struct rculist *elem); static inline void rculist_push_back(struct rculist *list, struct rculist *elem); static inline void rculist_replace(struct rculist *replacement, struct rculist *replaced); static inline void rculist_move(struct rculist *dst, struct rculist *src); /* List removal. */ static inline struct rculist *rculist_remove(struct rculist *elem); static inline struct rculist *rculist_pop_front(struct rculist *list); static inline struct rculist *rculist_pop_back(struct rculist *list); /* List elements. */ static inline const struct rculist *rculist_front(const struct rculist *); static inline struct rculist *rculist_back_protected(const struct rculist *); /* List properties. */ static inline size_t rculist_size(const struct rculist *); static inline bool rculist_is_empty(const struct rculist *); static inline bool rculist_is_singleton_protected(const struct rculist *); static inline bool rculist_is_short_protected(const struct rculist *); /* Inline implementations. */ static inline const struct rculist * rculist_next(const struct rculist *list) { return ovsrcu_get(struct rculist *, &list->next); } static inline struct rculist * rculist_next_protected(const struct rculist *list) { return ovsrcu_get_protected(struct rculist *, &list->next); } static inline void rculist_init(struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { list->prev = list; ovsrcu_init(&list->next, list); } #define RCULIST_POISON (struct rculist *)(UINTPTR_MAX / 0xf * 0xc) /* Initializes 'list' with pointers that will (probably) cause segfaults if * dereferenced and, better yet, show up clearly in a debugger. */ static inline void rculist_poison(struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { list->prev = RCULIST_POISON; } /* Initializes 'list' with pointers that will (probably) cause segfaults if * dereferenced and, better yet, show up clearly in a debugger. * * This variant poisons also the next pointer, so this may not be called if * this list element is still visible to RCU readers. */ static inline void rculist_poison__(struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { rculist_poison(list); ovsrcu_set_hidden(&list->next, RCULIST_POISON); } /* rculist insertion. */ static inline void rculist_insert(struct rculist *before, struct rculist *elem) OVS_NO_THREAD_SAFETY_ANALYSIS { elem->prev = before->prev; ovsrcu_set_hidden(&elem->next, before); ovsrcu_set(&before->prev->next, elem); before->prev = elem; } /* Removes elements 'first' though 'last' (exclusive) from their current list, * which may NOT be visible to any other threads (== be hidden from them), * then inserts them just before 'before'. */ static inline void rculist_splice_hidden(struct rculist *before, struct rculist *first, struct rculist *last) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rculist *last_next; if (first == last) { return; } last = last->prev; /* Cleanly remove 'first'...'last' from its current list. */ last_next = rculist_next_protected(last); last_next->prev = first->prev; ovsrcu_set_hidden(&first->prev->next, last_next); /* Splice 'first'...'last' into new list. */ first->prev = before->prev; ovsrcu_set(&last->next, before); ovsrcu_set(&before->prev->next, first); before->prev = last; } /* Inserts 'elem' at the beginning of 'list', so that it becomes the front in * 'list'. */ static inline void rculist_push_front(struct rculist *list, struct rculist *elem) { rculist_insert(rculist_next_protected(list), elem); } /* Inserts 'elem' at the end of 'list', so that it becomes the back in * 'list'. */ static inline void rculist_push_back(struct rculist *list, struct rculist *elem) { rculist_insert(list, elem); } /* Puts 'element' in the position currently occupied by 'position'. * * Afterward, 'position' is not linked to from the list any more, but still * links to the nodes in the list, and may still be referenced by other threads * until all other threads quiesce. The replaced node ('position') may not be * re-inserted, re-initialized, or deleted until after all other threads have * quiesced (use ovsrcu_postpone). */ static inline void rculist_replace(struct rculist *element, struct rculist *position) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rculist *position_next = rculist_next_protected(position); ovsrcu_set_hidden(&element->next, position_next); position_next->prev = element; element->prev = position->prev; ovsrcu_set(&element->prev->next, element); rculist_poison(position); } /* Initializes 'dst' with the contents of 'src', compensating for moving it * around in memory. The effect is that, if 'src' was the head of a list, now * 'dst' is the head of a list containing the same elements. * * Memory for 'src' must be kept around until the next RCU quiescent period. * rculist cannot be simply reallocated, so there is no rculist_moved(). */ static inline void rculist_move(struct rculist *dst, struct rculist *src) OVS_NO_THREAD_SAFETY_ANALYSIS { if (!rculist_is_empty(src)) { struct rculist *src_next = rculist_next_protected(src); dst->prev = src->prev; ovsrcu_set_hidden(&dst->next, src_next); src_next->prev = dst; ovsrcu_set(&src->prev->next, dst); } else { rculist_init(dst); } rculist_poison(src); } /* Removes 'elem' from its list and returns the element that followed it. * Has no effect when 'elem' is initialized, but not in a list. * Undefined behavior if 'elem' is not initialized. * * Afterward, 'elem' is not linked to from the list any more, but still links * to the nodes in the list, and may still be referenced by other threads until * all other threads quiesce. The removed node ('elem') may not be * re-inserted, re-initialized, or deleted until after all other threads have * quiesced (use ovsrcu_postpone). */ static inline struct rculist * rculist_remove(struct rculist *elem) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rculist *elem_next = rculist_next_protected(elem); elem_next->prev = elem->prev; ovsrcu_set(&elem->prev->next, elem_next); rculist_poison(elem); return elem_next; } /* Removes the front element from 'list' and returns it. Undefined behavior if * 'list' is empty before removal. * * Afterward, teh returned former first node is not linked to from the list any * more, but still links to the nodes in the list, and may still be referenced * by other threads until all other threads quiesce. The returned node may not * be re-inserted, re-initialized, or deleted until after all other threads * have quiesced (use ovsrcu_postpone). */ static inline struct rculist * rculist_pop_front(struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rculist *front = rculist_next_protected(list); rculist_remove(front); return front; } /* Removes the back element from 'list' and returns it. * Undefined behavior if 'list' is empty before removal. * * Afterward, teh returned former last node is not linked to from the list any * more, but still links to the nodes in the list, and may still be referenced * by other threads until all other threads quiesce. The returned node may not * be re-inserted, re-initialized, or deleted until after all other threads * have quiesced (use ovsrcu_postpone). */ static inline struct rculist * rculist_pop_back(struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rculist *back = list->prev; rculist_remove(back); return back; } /* Returns the front element in 'list_'. * Undefined behavior if 'list_' is empty. */ static inline const struct rculist * rculist_front(const struct rculist *list) { ovs_assert(!rculist_is_empty(list)); return rculist_next(list); } /* Returns the back element in 'list_'. * Returns the 'list_' itself, if 'list_' is empty. */ static inline struct rculist * rculist_back_protected(const struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { return CONST_CAST(struct rculist *, list)->prev; } /* Returns the number of elements in 'list'. * Runs in O(n) in the number of elements. */ static inline size_t rculist_size(const struct rculist *list) { const struct rculist *e; size_t cnt = 0; for (e = rculist_next(list); e != list; e = rculist_next(e)) { cnt++; } return cnt; } /* Returns true if 'list' is empty, false otherwise. */ static inline bool rculist_is_empty(const struct rculist *list) { return rculist_next(list) == list; } /* Returns true if 'list' has 0 or 1 elements, false otherwise. */ static inline bool rculist_is_short_protected(const struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { return rculist_next_protected(list) == list->prev; } /* Returns true if 'list' has exactly 1 element, false otherwise. */ static inline bool rculist_is_singleton_protected(const struct rculist *list) OVS_NO_THREAD_SAFETY_ANALYSIS { const struct rculist *list_next = rculist_next_protected(list); return list_next == list->prev && list_next != list; } #define RCULIST_FOR_EACH(ITER, MEMBER, RCULIST) \ for (INIT_CONTAINER(ITER, rculist_next(RCULIST), MEMBER); \ &(ITER)->MEMBER != (RCULIST); \ ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER)) #define RCULIST_FOR_EACH_CONTINUE(ITER, MEMBER, RCULIST) \ for (ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER); \ &(ITER)->MEMBER != (RCULIST); \ ASSIGN_CONTAINER(ITER, rculist_next(&(ITER)->MEMBER), MEMBER)) #define RCULIST_FOR_EACH_REVERSE_PROTECTED(ITER, MEMBER, RCULIST) \ for (INIT_CONTAINER(ITER, (RCULIST)->prev, MEMBER); \ &(ITER)->MEMBER != (RCULIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) #define RCULIST_FOR_EACH_REVERSE_PROTECTED_CONTINUE(ITER, MEMBER, RCULIST) \ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \ &(ITER)->MEMBER != (RCULIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) #define RCULIST_FOR_EACH_PROTECTED(ITER, MEMBER, RCULIST) \ for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \ &(ITER)->MEMBER != (RCULIST); \ ASSIGN_CONTAINER(ITER, rculist_next_protected(&(ITER)->MEMBER), \ MEMBER)) #define RCULIST_FOR_EACH_SAFE_PROTECTED(ITER, NEXT, MEMBER, RCULIST) \ for (INIT_CONTAINER(ITER, rculist_next_protected(RCULIST), MEMBER); \ (&(ITER)->MEMBER != (RCULIST) \ ? INIT_CONTAINER(NEXT, rculist_next_protected(&(ITER)->MEMBER), \ MEMBER), 1 : 0); \ (ITER) = (NEXT)) #endif /* rculist.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/simap.h0000644000000000000000000000013113534540071015673 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.877854134 openvswitch-2.5.9/lib/simap.h0000644000175000017500000000423313534540071017364 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SIMAP_H #define SIMAP_H 1 #include "hmap.h" #ifdef __cplusplus extern "C" { #endif /* A map from strings to unsigned integers. */ struct simap { struct hmap map; /* Contains "struct simap_node"s. */ }; struct simap_node { struct hmap_node node; /* In struct simap's 'map' hmap. */ char *name; unsigned int data; }; #define SIMAP_INITIALIZER(SIMAP) { HMAP_INITIALIZER(&(SIMAP)->map) } #define SIMAP_FOR_EACH(SIMAP_NODE, SIMAP) \ HMAP_FOR_EACH (SIMAP_NODE, node, &(SIMAP)->map) #define SIMAP_FOR_EACH_SAFE(SIMAP_NODE, NEXT, SIMAP) \ HMAP_FOR_EACH_SAFE (SIMAP_NODE, NEXT, node, &(SIMAP)->map) void simap_init(struct simap *); void simap_destroy(struct simap *); void simap_swap(struct simap *, struct simap *); void simap_moved(struct simap *); void simap_clear(struct simap *); bool simap_is_empty(const struct simap *); size_t simap_count(const struct simap *); bool simap_put(struct simap *, const char *, unsigned int); unsigned int simap_increase(struct simap *, const char *, unsigned int); unsigned int simap_get(const struct simap *, const char *); struct simap_node *simap_find(const struct simap *, const char *); struct simap_node *simap_find_len(const struct simap *, const char *, size_t len); bool simap_contains(const struct simap *, const char *); void simap_delete(struct simap *, struct simap_node *); bool simap_find_and_delete(struct simap *, const char *); const struct simap_node **simap_sort(const struct simap *); #ifdef __cplusplus } #endif #endif /* simap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/fatal-signal.c0000644000000000000000000000013213534540071017120 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.721852984 openvswitch-2.5.9/lib/fatal-signal.c0000644000175000017500000002467013534540071020617 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "fatal-signal.h" #include #include #include #include #include #include #include #include #include "ovs-thread.h" #include "poll-loop.h" #include "shash.h" #include "sset.h" #include "signals.h" #include "socket-util.h" #include "util.h" #include "openvswitch/vlog.h" #include "type-props.h" #ifndef SIG_ATOMIC_MAX #define SIG_ATOMIC_MAX TYPE_MAXIMUM(sig_atomic_t) #endif VLOG_DEFINE_THIS_MODULE(fatal_signal); /* Signals to catch. */ #ifndef _WIN32 static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM }; #else static const int fatal_signals[] = { SIGTERM }; #endif /* Hooks to call upon catching a signal */ struct hook { void (*hook_cb)(void *aux); void (*cancel_cb)(void *aux); void *aux; bool run_at_exit; }; #define MAX_HOOKS 32 static struct hook hooks[MAX_HOOKS]; static size_t n_hooks; static int signal_fds[2]; static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX; #ifdef _WIN32 static HANDLE wevent; #endif static struct ovs_mutex mutex; static void call_hooks(int sig_nr); #ifdef _WIN32 static BOOL WINAPI ConsoleHandlerRoutine(DWORD dwCtrlType); #endif /* Initializes the fatal signal handling module. Calling this function is * optional, because calling any other function in the module will also * initialize it. However, in a multithreaded program, the module must be * initialized while the process is still single-threaded. */ void fatal_signal_init(void) { static bool inited = false; if (!inited) { size_t i; assert_single_threaded(); inited = true; ovs_mutex_init_recursive(&mutex); #ifndef _WIN32 xpipe_nonblocking(signal_fds); #else wevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!wevent) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to create a event (%s).", msg_buf); } /* Register a function to handle Ctrl+C. */ SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); #endif for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; #ifndef _WIN32 struct sigaction old_sa; xsigaction(sig_nr, NULL, &old_sa); if (old_sa.sa_handler == SIG_DFL && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); } #else if (signal(sig_nr, fatal_signal_handler) == SIG_ERR) { VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); } #endif } atexit(fatal_signal_atexit_handler); } } /* Registers 'hook_cb' to be called from inside poll_block() following a fatal * signal. 'hook_cb' does not need to be async-signal-safe. In a * multithreaded program 'hook_cb' might be called from any thread, with * threads other than the one running 'hook_cb' in unknown states. * * If 'run_at_exit' is true, 'hook_cb' is also called during normal process * termination, e.g. when exit() is called or when main() returns. * * If the current process forks, fatal_signal_fork() may be called to clear the * parent process's fatal signal hooks, so that 'hook_cb' is only called when * the child terminates, not when the parent does. When fatal_signal_fork() is * called, it calls the 'cancel_cb' function if it is nonnull, passing 'aux', * to notify that the hook has been canceled. This allows the hook to free * memory, etc. */ void fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux), void *aux, bool run_at_exit) { fatal_signal_init(); ovs_mutex_lock(&mutex); ovs_assert(n_hooks < MAX_HOOKS); hooks[n_hooks].hook_cb = hook_cb; hooks[n_hooks].cancel_cb = cancel_cb; hooks[n_hooks].aux = aux; hooks[n_hooks].run_at_exit = run_at_exit; n_hooks++; ovs_mutex_unlock(&mutex); } /* Handles fatal signal number 'sig_nr'. * * Ordinarily this is the actual signal handler. When other code needs to * handle one of our signals, however, it can register for that signal and, if * and when necessary, call this function to do fatal signal processing for it * and terminate the process. Currently only timeval.c does this, for SIGALRM. * (It is not important whether the other code sets up its signal handler * before or after this file, because this file will only set up a signal * handler in the case where the signal has its default handling.) */ void fatal_signal_handler(int sig_nr) { #ifndef _WIN32 ignore(write(signal_fds[1], "", 1)); #else SetEvent(wevent); #endif stored_sig_nr = sig_nr; } /* Check whether a fatal signal has occurred and, if so, call the fatal signal * hooks and exit. * * This function is called automatically by poll_block(), but specialized * programs that may not always call poll_block() on a regular basis should * also call it periodically. (Therefore, any function with "block" in its * name should call fatal_signal_run() each time it is called, either directly * or through poll_block(), because such functions can only used by specialized * programs that can afford to block outside their main loop around * poll_block().) */ void fatal_signal_run(void) { sig_atomic_t sig_nr; fatal_signal_init(); sig_nr = stored_sig_nr; if (sig_nr != SIG_ATOMIC_MAX) { char namebuf[SIGNAL_NAME_BUFSIZE]; ovs_mutex_lock(&mutex); #ifndef _WIN32 VLOG_WARN("terminating with signal %d (%s)", (int)sig_nr, signal_name(sig_nr, namebuf, sizeof namebuf)); #else VLOG_WARN("terminating with signal %d", (int)sig_nr); #endif call_hooks(sig_nr); fflush(stderr); /* Re-raise the signal with the default handling so that the program * termination status reflects that we were killed by this signal */ signal(sig_nr, SIG_DFL); raise(sig_nr); ovs_mutex_unlock(&mutex); OVS_NOT_REACHED(); } } void fatal_signal_wait(void) { fatal_signal_init(); #ifdef _WIN32 poll_wevent_wait(wevent); #else poll_fd_wait(signal_fds[0], POLLIN); #endif } void fatal_ignore_sigpipe(void) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); #endif } void fatal_signal_atexit_handler(void) { call_hooks(0); } static void call_hooks(int sig_nr) { static volatile sig_atomic_t recurse = 0; if (!recurse) { size_t i; recurse = 1; for (i = 0; i < n_hooks; i++) { struct hook *h = &hooks[i]; if (sig_nr || h->run_at_exit) { h->hook_cb(h->aux); } } } } #ifdef _WIN32 BOOL WINAPI ConsoleHandlerRoutine(DWORD dwCtrlType) { stored_sig_nr = SIGINT; SetEvent(wevent); return true; } #endif /* Files to delete on exit. */ static struct sset files = SSET_INITIALIZER(&files); /* Has a hook function been registered with fatal_signal_add_hook() (and not * cleared by fatal_signal_fork())? */ static bool added_hook; static void unlink_files(void *aux); static void cancel_files(void *aux); static void do_unlink_files(void); /* Registers 'file' to be unlinked when the program terminates via exit() or a * fatal signal. */ void fatal_signal_add_file_to_unlink(const char *file) { fatal_signal_init(); ovs_mutex_lock(&mutex); if (!added_hook) { added_hook = true; fatal_signal_add_hook(unlink_files, cancel_files, NULL, true); } sset_add(&files, file); ovs_mutex_unlock(&mutex); } /* Unregisters 'file' from being unlinked when the program terminates via * exit() or a fatal signal. */ void fatal_signal_remove_file_to_unlink(const char *file) { fatal_signal_init(); ovs_mutex_lock(&mutex); sset_find_and_delete(&files, file); ovs_mutex_unlock(&mutex); } /* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'. * Returns 0 if successful, otherwise a positive errno value. */ int fatal_signal_unlink_file_now(const char *file) { int error; fatal_signal_init(); ovs_mutex_lock(&mutex); error = unlink(file) ? errno : 0; if (error) { VLOG_WARN("could not unlink \"%s\" (%s)", file, ovs_strerror(error)); } fatal_signal_remove_file_to_unlink(file); ovs_mutex_unlock(&mutex); return error; } static void unlink_files(void *aux OVS_UNUSED) { do_unlink_files(); } static void cancel_files(void *aux OVS_UNUSED) { sset_clear(&files); added_hook = false; } static void do_unlink_files(void) { const char *file; SSET_FOR_EACH (file, &files) { unlink(file); } } /* Clears all of the fatal signal hooks without executing them. If any of the * hooks passed a 'cancel_cb' function to fatal_signal_add_hook(), then those * functions will be called, allowing them to free resources, etc. * * Following a fork, one of the resulting processes can call this function to * allow it to terminate without calling the hooks registered before calling * this function. New hooks registered after calling this function will take * effect normally. */ void fatal_signal_fork(void) { size_t i; assert_single_threaded(); for (i = 0; i < n_hooks; i++) { struct hook *h = &hooks[i]; if (h->cancel_cb) { h->cancel_cb(h->aux); } } n_hooks = 0; /* Raise any signals that we have already received with the default * handler. */ if (stored_sig_nr != SIG_ATOMIC_MAX) { raise(stored_sig_nr); } } #ifndef _WIN32 /* Blocks all fatal signals and returns previous signal mask into * 'prev_mask'. */ void fatal_signal_block(sigset_t *prev_mask) { int i; sigset_t block_mask; sigemptyset(&block_mask); for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { int sig_nr = fatal_signals[i]; sigaddset(&block_mask, sig_nr); } xpthread_sigmask(SIG_BLOCK, &block_mask, prev_mask); } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/bfd.c0000644000000000000000000000013213534540071015311 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.661852541 openvswitch-2.5.9/lib/bfd.c0000644000175000017500000012745513534540071017015 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "bfd.h" #include #include #include #include #include #include "byte-order.h" #include "connectivity.h" #include "csum.h" #include "dp-packet.h" #include "dpif.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" #include "hmap.h" #include "list.h" #include "netdev.h" #include "odp-util.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "openvswitch/types.h" #include "packets.h" #include "poll-loop.h" #include "random.h" #include "seq.h" #include "smap.h" #include "timeval.h" #include "unaligned.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(bfd); /* XXX Finish BFD. * * The goal of this module is to replace CFM with something both more flexible * and standards compliant. In service of this goal, the following needs to be * done. * * - Compliance * * Implement Demand mode. * * Go through the RFC line by line and verify we comply. * * Test against a hardware implementation. Preferably a popular one. * * Delete BFD packets with nw_ttl != 255 in the datapath to prevent DOS * attacks. * * - Unit tests. * * - Set TOS/PCP on the outer tunnel header when encapped. * * - Sending BFD messages should be in its own thread/process. * * - Scale testing. How does it operate when there are large number of bfd * sessions? Do we ever have random flaps? What's the CPU utilization? * * - Rely on data traffic for liveness by using BFD demand mode. * If we're receiving traffic on a port, we can safely assume it's up (modulo * unidrectional failures). BFD has a demand mode in which it can stay quiet * unless it feels the need to check the status of the port. Using this, we * can implement a strategy in which BFD only sends control messages on dark * interfaces. * * - Depending on how one interprets the spec, it appears that a BFD session * can never change bfd.LocalDiag to "No Diagnostic". We should verify that * this is what hardware implementations actually do. Seems like "No * Diagnostic" should be set once a BFD session state goes UP. */ #define BFD_VERSION 1 enum flags { FLAG_MULTIPOINT = 1 << 0, FLAG_DEMAND = 1 << 1, FLAG_AUTH = 1 << 2, FLAG_CTL = 1 << 3, FLAG_FINAL = 1 << 4, FLAG_POLL = 1 << 5 }; enum state { STATE_ADMIN_DOWN = 0 << 6, STATE_DOWN = 1 << 6, STATE_INIT = 2 << 6, STATE_UP = 3 << 6 }; enum diag { DIAG_NONE = 0, /* No Diagnostic. */ DIAG_EXPIRED = 1, /* Control Detection Time Expired. */ DIAG_ECHO_FAILED = 2, /* Echo Function Failed. */ DIAG_RMT_DOWN = 3, /* Neighbor Signaled Session Down. */ DIAG_FWD_RESET = 4, /* Forwarding Plane Reset. */ DIAG_PATH_DOWN = 5, /* Path Down. */ DIAG_CPATH_DOWN = 6, /* Concatenated Path Down. */ DIAG_ADMIN_DOWN = 7, /* Administratively Down. */ DIAG_RCPATH_DOWN = 8 /* Reverse Concatenated Path Down. */ }; /* RFC 5880 Section 4.1 * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | My Discriminator | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Your Discriminator | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Desired Min TX Interval | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Required Min RX Interval | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Required Min Echo RX Interval | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct msg { uint8_t vers_diag; /* Version and diagnostic. */ uint8_t flags; /* 2bit State field followed by flags. */ uint8_t mult; /* Fault detection multiplier. */ uint8_t length; /* Length of this BFD message. */ ovs_be32 my_disc; /* My discriminator. */ ovs_be32 your_disc; /* Your discriminator. */ ovs_be32 min_tx; /* Desired minimum tx interval. */ ovs_be32 min_rx; /* Required minimum rx interval. */ ovs_be32 min_rx_echo; /* Required minimum echo rx interval. */ }; BUILD_ASSERT_DECL(BFD_PACKET_LEN == sizeof(struct msg)); #define DIAG_MASK 0x1f #define VERS_SHIFT 5 #define STATE_MASK 0xC0 #define FLAGS_MASK 0x3f struct bfd { struct hmap_node node; /* In 'all_bfds'. */ uint32_t disc; /* bfd.LocalDiscr. Key in 'all_bfds' hmap. */ char *name; /* Name used for logging. */ bool cpath_down; /* Concatenated Path Down. */ uint8_t mult; /* bfd.DetectMult. */ struct netdev *netdev; uint64_t rx_packets; /* Packets received by 'netdev'. */ enum state state; /* bfd.SessionState. */ enum state rmt_state; /* bfd.RemoteSessionState. */ enum diag diag; /* bfd.LocalDiag. */ enum diag rmt_diag; /* Remote diagnostic. */ enum flags flags; /* Flags sent on messages. */ enum flags rmt_flags; /* Flags last received. */ uint32_t rmt_disc; /* bfd.RemoteDiscr. */ struct eth_addr local_eth_src; /* Local eth src address. */ struct eth_addr local_eth_dst; /* Local eth dst address. */ struct eth_addr rmt_eth_dst; /* Remote eth dst address. */ ovs_be32 ip_src; /* IPv4 source address. */ ovs_be32 ip_dst; /* IPv4 destination address. */ uint16_t udp_src; /* UDP source port. */ /* All timers in milliseconds. */ long long int rmt_min_rx; /* bfd.RemoteMinRxInterval. */ long long int rmt_min_tx; /* Remote minimum TX interval. */ long long int cfg_min_tx; /* Configured minimum TX rate. */ long long int cfg_min_rx; /* Configured required minimum RX rate. */ long long int poll_min_tx; /* Min TX negotating in a poll sequence. */ long long int poll_min_rx; /* Min RX negotating in a poll sequence. */ long long int min_tx; /* bfd.DesiredMinTxInterval. */ long long int min_rx; /* bfd.RequiredMinRxInterval. */ long long int last_tx; /* Last TX time. */ long long int next_tx; /* Next TX time. */ long long int detect_time; /* RFC 5880 6.8.4 Detection time. */ bool last_forwarding; /* Last calculation of forwarding flag. */ int forwarding_override; /* Manual override of 'forwarding' status. */ atomic_bool check_tnl_key; /* Verify tunnel key of inbound packets? */ struct ovs_refcount ref_cnt; /* When forward_if_rx is true, bfd_forwarding() will return * true as long as there are incoming packets received. * Note, forwarding_override still has higher priority. */ bool forwarding_if_rx; long long int forwarding_if_rx_detect_time; /* When 'bfd->forwarding_if_rx' is set, at least one bfd control packet * is required to be received every 100 * bfd->cfg_min_rx. If bfd * control packet is not received within this interval, even if data * packets are received, the bfd->forwarding will still be false. */ long long int demand_rx_bfd_time; /* BFD decay related variables. */ bool in_decay; /* True when bfd is in decay. */ int decay_min_rx; /* min_rx is set to decay_min_rx when */ /* in decay. */ int decay_rx_ctl; /* Count bfd packets received within decay */ /* detect interval. */ uint64_t decay_rx_packets; /* Packets received by 'netdev'. */ long long int decay_detect_time; /* Decay detection time. */ uint64_t flap_count; /* Counts bfd forwarding flaps. */ /* True when the variables returned by bfd_get_status() are changed * since last check. */ bool status_changed; }; static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__); static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__; static bool bfd_lookup_ip(const char *host_name, struct in_addr *) OVS_REQUIRES(mutex); static bool bfd_forwarding__(struct bfd *) OVS_REQUIRES(mutex); static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex); static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex); static const char *bfd_diag_str(enum diag) OVS_REQUIRES(mutex); static const char *bfd_state_str(enum state) OVS_REQUIRES(mutex); static long long int bfd_min_tx(const struct bfd *) OVS_REQUIRES(mutex); static long long int bfd_tx_interval(const struct bfd *) OVS_REQUIRES(mutex); static long long int bfd_rx_interval(const struct bfd *) OVS_REQUIRES(mutex); static void bfd_set_next_tx(struct bfd *) OVS_REQUIRES(mutex); static void bfd_set_state(struct bfd *, enum state, enum diag) OVS_REQUIRES(mutex); static uint32_t generate_discriminator(void) OVS_REQUIRES(mutex); static void bfd_put_details(struct ds *, const struct bfd *) OVS_REQUIRES(mutex); static uint64_t bfd_rx_packets(const struct bfd *) OVS_REQUIRES(mutex); static void bfd_try_decay(struct bfd *) OVS_REQUIRES(mutex); static void bfd_decay_update(struct bfd *) OVS_REQUIRES(mutex); static void bfd_status_changed(struct bfd *) OVS_REQUIRES(mutex); static void bfd_forwarding_if_rx_update(struct bfd *) OVS_REQUIRES(mutex); static void bfd_unixctl_show(struct unixctl_conn *, int argc, const char *argv[], void *aux OVS_UNUSED); static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *, int argc, const char *argv[], void *aux OVS_UNUSED); static void log_msg(enum vlog_level, const struct msg *, const char *message, const struct bfd *) OVS_REQUIRES(mutex); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 20); /* Returns true if the interface on which 'bfd' is running may be used to * forward traffic according to the BFD session state. */ bool bfd_forwarding(struct bfd *bfd) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = bfd_forwarding__(bfd); ovs_mutex_unlock(&mutex); return ret; } /* When forwarding_if_rx is enabled, if there are packets received, * updates forwarding_if_rx_detect_time. */ void bfd_account_rx(struct bfd *bfd, const struct dpif_flow_stats *stats) { if (stats->n_packets && bfd->forwarding_if_rx) { ovs_mutex_lock(&mutex); bfd_forwarding__(bfd); bfd_forwarding_if_rx_update(bfd); bfd_forwarding__(bfd); ovs_mutex_unlock(&mutex); } } /* Returns and resets the 'bfd->status_changed'. */ bool bfd_check_status_change(struct bfd *bfd) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = bfd->status_changed; bfd->status_changed = false; ovs_mutex_unlock(&mutex); return ret; } /* Returns a 'smap' of key value pairs representing the status of 'bfd' * intended for the OVS database. */ void bfd_get_status(const struct bfd *bfd, struct smap *smap) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); smap_add(smap, "forwarding", bfd_forwarding__(CONST_CAST(struct bfd *, bfd)) ? "true" : "false"); smap_add(smap, "state", bfd_state_str(bfd->state)); smap_add(smap, "diagnostic", bfd_diag_str(bfd->diag)); smap_add_format(smap, "flap_count", "%"PRIu64, bfd->flap_count); smap_add(smap, "remote_state", bfd_state_str(bfd->rmt_state)); smap_add(smap, "remote_diagnostic", bfd_diag_str(bfd->rmt_diag)); ovs_mutex_unlock(&mutex); } void bfd_init(void) { unixctl_command_register("bfd/show", "[interface]", 0, 1, bfd_unixctl_show, NULL); unixctl_command_register("bfd/set-forwarding", "[interface] normal|false|true", 1, 2, bfd_unixctl_set_forwarding_override, NULL); } /* Initializes, destroys, or reconfigures the BFD session 'bfd' (named 'name'), * according to the database configuration contained in 'cfg'. Takes ownership * of 'bfd', which may be NULL. Returns a BFD object which may be used as a * handle for the session, or NULL if BFD is not enabled according to 'cfg'. * Also returns NULL if cfg is NULL. */ struct bfd * bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg, struct netdev *netdev) OVS_EXCLUDED(mutex) { static atomic_count udp_src = ATOMIC_COUNT_INIT(0); int decay_min_rx; long long int min_tx, min_rx; bool need_poll = false; bool cfg_min_rx_changed = false; bool cpath_down, forwarding_if_rx; const char *hwaddr, *ip_src, *ip_dst; struct in_addr in_addr; struct eth_addr ea; if (!cfg || !smap_get_bool(cfg, "enable", false)) { bfd_unref(bfd); return NULL; } ovs_mutex_lock(&mutex); if (!bfd) { bfd = xzalloc(sizeof *bfd); bfd->name = xstrdup(name); bfd->forwarding_override = -1; bfd->disc = generate_discriminator(); hmap_insert(all_bfds, &bfd->node, bfd->disc); bfd->diag = DIAG_NONE; bfd->min_tx = 1000; bfd->mult = 3; ovs_refcount_init(&bfd->ref_cnt); bfd->netdev = netdev_ref(netdev); bfd->rx_packets = bfd_rx_packets(bfd); bfd->in_decay = false; bfd->flap_count = 0; /* RFC 5881 section 4 * The source port MUST be in the range 49152 through 65535. The same * UDP source port number MUST be used for all BFD Control packets * associated with a particular session. The source port number SHOULD * be unique among all BFD sessions on the system. */ bfd->udp_src = (atomic_count_inc(&udp_src) % 16384) + 49152; bfd_set_state(bfd, STATE_DOWN, DIAG_NONE); bfd_status_changed(bfd); } atomic_store_relaxed(&bfd->check_tnl_key, smap_get_bool(cfg, "check_tnl_key", false)); min_tx = smap_get_int(cfg, "min_tx", 100); min_tx = MAX(min_tx, 1); if (bfd->cfg_min_tx != min_tx) { bfd->cfg_min_tx = min_tx; if (bfd->state != STATE_UP || (!bfd_in_poll(bfd) && bfd->cfg_min_tx < bfd->min_tx)) { bfd->min_tx = bfd->cfg_min_tx; } need_poll = true; } min_rx = smap_get_int(cfg, "min_rx", 1000); min_rx = MAX(min_rx, 1); if (bfd->cfg_min_rx != min_rx) { bfd->cfg_min_rx = min_rx; if (bfd->state != STATE_UP || (!bfd_in_poll(bfd) && bfd->cfg_min_rx > bfd->min_rx)) { bfd->min_rx = bfd->cfg_min_rx; } cfg_min_rx_changed = true; need_poll = true; } decay_min_rx = smap_get_int(cfg, "decay_min_rx", 0); if (bfd->decay_min_rx != decay_min_rx || cfg_min_rx_changed) { if (decay_min_rx > 0 && decay_min_rx < bfd->cfg_min_rx) { VLOG_WARN("%s: decay_min_rx cannot be less than %lld ms", bfd->name, bfd->cfg_min_rx); bfd->decay_min_rx = 0; } else { bfd->decay_min_rx = decay_min_rx; } /* Resets decay. */ bfd->in_decay = false; bfd_decay_update(bfd); need_poll = true; } cpath_down = smap_get_bool(cfg, "cpath_down", false); if (bfd->cpath_down != cpath_down) { bfd->cpath_down = cpath_down; bfd_set_state(bfd, bfd->state, DIAG_NONE); need_poll = true; } hwaddr = smap_get(cfg, "bfd_local_src_mac"); if (hwaddr && eth_addr_from_string(hwaddr, &ea)) { bfd->local_eth_src = ea; } else { bfd->local_eth_src = eth_addr_zero; } hwaddr = smap_get(cfg, "bfd_local_dst_mac"); if (hwaddr && eth_addr_from_string(hwaddr, &ea)) { bfd->local_eth_dst = ea; } else { bfd->local_eth_dst = eth_addr_zero; } hwaddr = smap_get(cfg, "bfd_remote_dst_mac"); if (hwaddr && eth_addr_from_string(hwaddr, &ea)) { bfd->rmt_eth_dst = ea; } else { bfd->rmt_eth_dst = eth_addr_zero; } ip_src = smap_get(cfg, "bfd_src_ip"); if (ip_src && bfd_lookup_ip(ip_src, &in_addr)) { memcpy(&bfd->ip_src, &in_addr, sizeof in_addr); } else { bfd->ip_src = htonl(0xA9FE0101); /* 169.254.1.1. */ } ip_dst = smap_get(cfg, "bfd_dst_ip"); if (ip_dst && bfd_lookup_ip(ip_dst, &in_addr)) { memcpy(&bfd->ip_dst, &in_addr, sizeof in_addr); } else { bfd->ip_dst = htonl(0xA9FE0100); /* 169.254.1.0. */ } forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false); if (bfd->forwarding_if_rx != forwarding_if_rx) { bfd->forwarding_if_rx = forwarding_if_rx; if (bfd->state == STATE_UP && bfd->forwarding_if_rx) { bfd_forwarding_if_rx_update(bfd); } else { bfd->forwarding_if_rx_detect_time = 0; } } if (need_poll) { bfd_poll(bfd); } ovs_mutex_unlock(&mutex); return bfd; } struct bfd * bfd_ref(const struct bfd *bfd_) { struct bfd *bfd = CONST_CAST(struct bfd *, bfd_); if (bfd) { ovs_refcount_ref(&bfd->ref_cnt); } return bfd; } void bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex) { if (bfd && ovs_refcount_unref_relaxed(&bfd->ref_cnt) == 1) { ovs_mutex_lock(&mutex); bfd_status_changed(bfd); hmap_remove(all_bfds, &bfd->node); netdev_close(bfd->netdev); free(bfd->name); free(bfd); ovs_mutex_unlock(&mutex); } } long long int bfd_wait(const struct bfd *bfd) OVS_EXCLUDED(mutex) { long long int wake_time = bfd_wake_time(bfd); poll_timer_wait_until(wake_time); return wake_time; } /* Returns the next wake up time. */ long long int bfd_wake_time(const struct bfd *bfd) OVS_EXCLUDED(mutex) { long long int retval; if (!bfd) { return LLONG_MAX; } ovs_mutex_lock(&mutex); if (bfd->flags & FLAG_FINAL) { retval = 0; } else { retval = bfd->next_tx; if (bfd->state > STATE_DOWN) { retval = MIN(bfd->detect_time, retval); } } ovs_mutex_unlock(&mutex); return retval; } void bfd_run(struct bfd *bfd) OVS_EXCLUDED(mutex) { long long int now; bool old_in_decay; ovs_mutex_lock(&mutex); now = time_msec(); old_in_decay = bfd->in_decay; if (bfd->state > STATE_DOWN && now >= bfd->detect_time) { bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED); } bfd_forwarding__(bfd); /* Decay may only happen when state is STATE_UP, bfd->decay_min_rx is * configured, and decay_detect_time is reached. */ if (bfd->state == STATE_UP && bfd->decay_min_rx > 0 && now >= bfd->decay_detect_time) { bfd_try_decay(bfd); } if (bfd->min_tx != bfd->cfg_min_tx || (bfd->min_rx != bfd->cfg_min_rx && bfd->min_rx != bfd->decay_min_rx) || bfd->in_decay != old_in_decay) { bfd_poll(bfd); } ovs_mutex_unlock(&mutex); } bool bfd_should_send_packet(const struct bfd *bfd) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = bfd->flags & FLAG_FINAL || time_msec() >= bfd->next_tx; ovs_mutex_unlock(&mutex); return ret; } void bfd_put_packet(struct bfd *bfd, struct dp_packet *p, const struct eth_addr eth_src) OVS_EXCLUDED(mutex) { long long int min_tx, min_rx; struct udp_header *udp; struct eth_header *eth; struct ip_header *ip; struct msg *msg; ovs_mutex_lock(&mutex); if (bfd->next_tx) { long long int delay = time_msec() - bfd->next_tx; long long int interval = bfd_tx_interval(bfd); if (delay > interval * 3 / 2) { VLOG_INFO("%s: long delay of %lldms (expected %lldms) sending BFD" " control message", bfd->name, delay, interval); } } /* RFC 5880 Section 6.5 * A BFD Control packet MUST NOT have both the Poll (P) and Final (F) bits * set. */ ovs_assert(!(bfd->flags & FLAG_POLL) || !(bfd->flags & FLAG_FINAL)); dp_packet_reserve(p, 2); /* Properly align after the ethernet header. */ eth = dp_packet_put_uninit(p, sizeof *eth); eth->eth_src = eth_addr_is_zero(bfd->local_eth_src) ? eth_src : bfd->local_eth_src; eth->eth_dst = eth_addr_is_zero(bfd->local_eth_dst) ? eth_addr_bfd : bfd->local_eth_dst; eth->eth_type = htons(ETH_TYPE_IP); ip = dp_packet_put_zeros(p, sizeof *ip); ip->ip_ihl_ver = IP_IHL_VER(5, 4); ip->ip_tot_len = htons(sizeof *ip + sizeof *udp + sizeof *msg); ip->ip_ttl = MAXTTL; ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT; ip->ip_proto = IPPROTO_UDP; put_16aligned_be32(&ip->ip_src, bfd->ip_src); put_16aligned_be32(&ip->ip_dst, bfd->ip_dst); ip->ip_csum = csum(ip, sizeof *ip); udp = dp_packet_put_zeros(p, sizeof *udp); udp->udp_src = htons(bfd->udp_src); udp->udp_dst = htons(BFD_DEST_PORT); udp->udp_len = htons(sizeof *udp + sizeof *msg); msg = dp_packet_put_uninit(p, sizeof *msg); msg->vers_diag = (BFD_VERSION << 5) | bfd->diag; msg->flags = (bfd->state & STATE_MASK) | bfd->flags; msg->mult = bfd->mult; msg->length = BFD_PACKET_LEN; msg->my_disc = htonl(bfd->disc); msg->your_disc = htonl(bfd->rmt_disc); msg->min_rx_echo = htonl(0); if (bfd_in_poll(bfd)) { min_tx = bfd->poll_min_tx; min_rx = bfd->poll_min_rx; } else { min_tx = bfd_min_tx(bfd); min_rx = bfd->min_rx; } msg->min_tx = htonl(min_tx * 1000); msg->min_rx = htonl(min_rx * 1000); bfd->flags &= ~FLAG_FINAL; log_msg(VLL_DBG, msg, "Sending BFD Message", bfd); bfd->last_tx = time_msec(); bfd_set_next_tx(bfd); ovs_mutex_unlock(&mutex); } bool bfd_should_process_flow(const struct bfd *bfd_, const struct flow *flow, struct flow_wildcards *wc) { struct bfd *bfd = CONST_CAST(struct bfd *, bfd_); if (!eth_addr_is_zero(bfd->rmt_eth_dst)) { memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); if (!eth_addr_equals(bfd->rmt_eth_dst, flow->dl_dst)) { return false; } } if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); if (flow->nw_proto == IPPROTO_UDP && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); if (flow->tp_dst == htons(BFD_DEST_PORT)) { bool check_tnl_key; atomic_read_relaxed(&bfd->check_tnl_key, &check_tnl_key); if (check_tnl_key) { memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id); return flow->tunnel.tun_id == htonll(0); } return true; } } } return false; } void bfd_process_packet(struct bfd *bfd, const struct flow *flow, const struct dp_packet *p) OVS_EXCLUDED(mutex) { uint32_t rmt_min_rx, pkt_your_disc; enum state rmt_state; enum flags flags; uint8_t version; struct msg *msg; const uint8_t *l7 = dp_packet_get_udp_payload(p); if (!l7) { return; /* No UDP payload. */ } /* This function is designed to follow section RFC 5880 6.8.6 closely. */ ovs_mutex_lock(&mutex); /* Increments the decay rx counter. */ bfd->decay_rx_ctl++; bfd_forwarding__(bfd); if (flow->nw_ttl != 255) { /* XXX Should drop in the kernel to prevent DOS. */ goto out; } msg = dp_packet_at(p, l7 - (uint8_t *)dp_packet_data(p), BFD_PACKET_LEN); if (!msg) { VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only " "%"PRIdPTR" bytes long, at least %d required).", bfd->name, (uint8_t *) dp_packet_tail(p) - l7, BFD_PACKET_LEN); goto out; } /* RFC 5880 Section 6.8.6 * If the Length field is greater than the payload of the encapsulating * protocol, the packet MUST be discarded. * * Note that we make this check implicitly. Above we use dp_packet_at() to * ensure that there are at least BFD_PACKET_LEN bytes in the payload of * the encapsulating protocol. Below we require msg->length to be exactly * BFD_PACKET_LEN bytes. */ flags = msg->flags & FLAGS_MASK; rmt_state = msg->flags & STATE_MASK; version = msg->vers_diag >> VERS_SHIFT; log_msg(VLL_DBG, msg, "Received BFD control message", bfd); if (version != BFD_VERSION) { log_msg(VLL_WARN, msg, "Incorrect version", bfd); goto out; } /* Technically this should happen after the length check. We don't support * authentication however, so it's simpler to do the check first. */ if (flags & FLAG_AUTH) { log_msg(VLL_WARN, msg, "Authenticated control message with" " authentication disabled", bfd); goto out; } if (msg->length != BFD_PACKET_LEN) { log_msg(VLL_WARN, msg, "Unexpected length", bfd); if (msg->length < BFD_PACKET_LEN) { goto out; } } if (!msg->mult) { log_msg(VLL_WARN, msg, "Zero multiplier", bfd); goto out; } if (flags & FLAG_MULTIPOINT) { log_msg(VLL_WARN, msg, "Unsupported multipoint flag", bfd); goto out; } if (!msg->my_disc) { log_msg(VLL_WARN, msg, "NULL my_disc", bfd); goto out; } pkt_your_disc = ntohl(msg->your_disc); if (pkt_your_disc) { /* Technically, we should use the your discriminator field to figure * out which 'struct bfd' this packet is destined towards. That way a * bfd session could migrate from one interface to another * transparently. This doesn't fit in with the OVS structure very * well, so in this respect, we are not compliant. */ if (pkt_your_disc != bfd->disc) { log_msg(VLL_WARN, msg, "Incorrect your_disc", bfd); goto out; } } else if (rmt_state > STATE_DOWN) { log_msg(VLL_WARN, msg, "Null your_disc", bfd); goto out; } if (bfd->rmt_state != rmt_state) { bfd_status_changed(bfd); } bfd->rmt_disc = ntohl(msg->my_disc); bfd->rmt_state = rmt_state; bfd->rmt_flags = flags; bfd->rmt_diag = msg->vers_diag & DIAG_MASK; if (flags & FLAG_FINAL && bfd_in_poll(bfd)) { bfd->min_tx = bfd->poll_min_tx; bfd->min_rx = bfd->poll_min_rx; bfd->flags &= ~FLAG_POLL; log_msg(VLL_INFO, msg, "Poll sequence terminated", bfd); } if (flags & FLAG_POLL) { /* RFC 5880 Section 6.5 * When the other system receives a Poll, it immediately transmits a * BFD Control packet with the Final (F) bit set, independent of any * periodic BFD Control packets it may be sending * (see section 6.8.7). */ bfd->flags &= ~FLAG_POLL; bfd->flags |= FLAG_FINAL; } rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1); if (bfd->rmt_min_rx != rmt_min_rx) { bfd->rmt_min_rx = rmt_min_rx; if (bfd->next_tx) { bfd_set_next_tx(bfd); } log_msg(VLL_INFO, msg, "New remote min_rx", bfd); } bfd->rmt_min_tx = MAX(ntohl(msg->min_tx) / 1000, 1); bfd->detect_time = bfd_rx_interval(bfd) * bfd->mult + time_msec(); if (bfd->state == STATE_ADMIN_DOWN) { VLOG_DBG_RL(&rl, "Administratively down, dropping control message."); goto out; } if (rmt_state == STATE_ADMIN_DOWN) { if (bfd->state != STATE_DOWN) { bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN); } } else { switch (bfd->state) { case STATE_DOWN: if (rmt_state == STATE_DOWN) { bfd_set_state(bfd, STATE_INIT, bfd->diag); } else if (rmt_state == STATE_INIT) { bfd_set_state(bfd, STATE_UP, bfd->diag); } break; case STATE_INIT: if (rmt_state > STATE_DOWN) { bfd_set_state(bfd, STATE_UP, bfd->diag); } break; case STATE_UP: if (rmt_state <= STATE_DOWN) { bfd_set_state(bfd, STATE_DOWN, DIAG_RMT_DOWN); log_msg(VLL_INFO, msg, "Remote signaled STATE_DOWN", bfd); } break; case STATE_ADMIN_DOWN: default: OVS_NOT_REACHED(); } } /* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */ if (bfd->forwarding_if_rx) { bfd->demand_rx_bfd_time = time_msec() + 100 * bfd->cfg_min_rx; } out: bfd_forwarding__(bfd); ovs_mutex_unlock(&mutex); } /* Must be called when the netdev owned by 'bfd' should change. */ void bfd_set_netdev(struct bfd *bfd, const struct netdev *netdev) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (bfd->netdev != netdev) { netdev_close(bfd->netdev); bfd->netdev = netdev_ref(netdev); if (bfd->decay_min_rx && bfd->state == STATE_UP) { bfd_decay_update(bfd); } if (bfd->forwarding_if_rx && bfd->state == STATE_UP) { bfd_forwarding_if_rx_update(bfd); } bfd->rx_packets = bfd_rx_packets(bfd); } ovs_mutex_unlock(&mutex); } /* Updates the forwarding flag. If override is not configured and * the forwarding flag value changes, increments the flap count. * * Note this function may be called multiple times in a function * (e.g. bfd_account_rx) before and after the bfd state or status * change. This is to capture any forwarding flag flap. */ static bool bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex) { long long int now = time_msec(); bool forwarding_if_rx; bool last_forwarding = bfd->last_forwarding; if (bfd->forwarding_override != -1) { return bfd->forwarding_override == 1; } forwarding_if_rx = bfd->forwarding_if_rx && bfd->forwarding_if_rx_detect_time > now && bfd->demand_rx_bfd_time > now; bfd->last_forwarding = (bfd->state == STATE_UP || forwarding_if_rx) && bfd->rmt_diag != DIAG_PATH_DOWN && bfd->rmt_diag != DIAG_CPATH_DOWN && bfd->rmt_diag != DIAG_RCPATH_DOWN; if (bfd->last_forwarding != last_forwarding) { bfd->flap_count++; bfd_status_changed(bfd); } return bfd->last_forwarding; } /* Helpers. */ static bool bfd_lookup_ip(const char *host_name, struct in_addr *addr) { if (!inet_pton(AF_INET, host_name, addr)) { VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name); return false; } return true; } static bool bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex) { return (bfd->flags & FLAG_POLL) != 0; } static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex) { if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd) && !(bfd->flags & FLAG_FINAL)) { bfd->poll_min_tx = bfd->cfg_min_tx; bfd->poll_min_rx = bfd->in_decay ? bfd->decay_min_rx : bfd->cfg_min_rx; bfd->flags |= FLAG_POLL; bfd->next_tx = 0; VLOG_INFO_RL(&rl, "%s: Initiating poll sequence", bfd->name); } } static long long int bfd_min_tx(const struct bfd *bfd) OVS_REQUIRES(mutex) { /* RFC 5880 Section 6.8.3 * When bfd.SessionState is not Up, the system MUST set * bfd.DesiredMinTxInterval to a value of not less than one second * (1,000,000 microseconds). This is intended to ensure that the * bandwidth consumed by BFD sessions that are not Up is negligible, * particularly in the case where a neighbor may not be running BFD. */ return (bfd->state == STATE_UP ? bfd->min_tx : MAX(bfd->min_tx, 1000)); } static long long int bfd_tx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex) { long long int interval = bfd_min_tx(bfd); return MAX(interval, bfd->rmt_min_rx); } static long long int bfd_rx_interval(const struct bfd *bfd) OVS_REQUIRES(mutex) { return MAX(bfd->min_rx, bfd->rmt_min_tx); } static void bfd_set_next_tx(struct bfd *bfd) OVS_REQUIRES(mutex) { long long int interval = bfd_tx_interval(bfd); interval -= interval * random_range(26) / 100; bfd->next_tx = bfd->last_tx + interval; } static const char * bfd_flag_str(enum flags flags) { struct ds ds = DS_EMPTY_INITIALIZER; static char flag_str[128]; if (!flags) { return "none"; } if (flags & FLAG_MULTIPOINT) { ds_put_cstr(&ds, "multipoint "); } if (flags & FLAG_DEMAND) { ds_put_cstr(&ds, "demand "); } if (flags & FLAG_AUTH) { ds_put_cstr(&ds, "auth "); } if (flags & FLAG_CTL) { ds_put_cstr(&ds, "ctl "); } if (flags & FLAG_FINAL) { ds_put_cstr(&ds, "final "); } if (flags & FLAG_POLL) { ds_put_cstr(&ds, "poll "); } /* Do not copy the trailing whitespace. */ ds_chomp(&ds, ' '); ovs_strlcpy(flag_str, ds_cstr(&ds), sizeof flag_str); ds_destroy(&ds); return flag_str; } static const char * bfd_state_str(enum state state) { switch (state) { case STATE_ADMIN_DOWN: return "admin_down"; case STATE_DOWN: return "down"; case STATE_INIT: return "init"; case STATE_UP: return "up"; default: return "invalid"; } } static const char * bfd_diag_str(enum diag diag) { switch (diag) { case DIAG_NONE: return "No Diagnostic"; case DIAG_EXPIRED: return "Control Detection Time Expired"; case DIAG_ECHO_FAILED: return "Echo Function Failed"; case DIAG_RMT_DOWN: return "Neighbor Signaled Session Down"; case DIAG_FWD_RESET: return "Forwarding Plane Reset"; case DIAG_PATH_DOWN: return "Path Down"; case DIAG_CPATH_DOWN: return "Concatenated Path Down"; case DIAG_ADMIN_DOWN: return "Administratively Down"; case DIAG_RCPATH_DOWN: return "Reverse Concatenated Path Down"; default: return "Invalid Diagnostic"; } }; static void log_msg(enum vlog_level level, const struct msg *p, const char *message, const struct bfd *bfd) OVS_REQUIRES(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; if (vlog_should_drop(THIS_MODULE, level, &rl)) { return; } ds_put_format(&ds, "%s: %s." "\n\tvers:%"PRIu8" diag:\"%s\" state:%s mult:%"PRIu8 " length:%"PRIu8 "\n\tflags: %s" "\n\tmy_disc:0x%"PRIx32" your_disc:0x%"PRIx32 "\n\tmin_tx:%"PRIu32"us (%"PRIu32"ms)" "\n\tmin_rx:%"PRIu32"us (%"PRIu32"ms)" "\n\tmin_rx_echo:%"PRIu32"us (%"PRIu32"ms)", bfd->name, message, p->vers_diag >> VERS_SHIFT, bfd_diag_str(p->vers_diag & DIAG_MASK), bfd_state_str(p->flags & STATE_MASK), p->mult, p->length, bfd_flag_str(p->flags & FLAGS_MASK), ntohl(p->my_disc), ntohl(p->your_disc), ntohl(p->min_tx), ntohl(p->min_tx) / 1000, ntohl(p->min_rx), ntohl(p->min_rx) / 1000, ntohl(p->min_rx_echo), ntohl(p->min_rx_echo) / 1000); bfd_put_details(&ds, bfd); VLOG(level, "%s", ds_cstr(&ds)); ds_destroy(&ds); } static void bfd_set_state(struct bfd *bfd, enum state state, enum diag diag) OVS_REQUIRES(mutex) { if (bfd->cpath_down) { diag = DIAG_CPATH_DOWN; } if (bfd->state != state || bfd->diag != diag) { if (!VLOG_DROP_INFO(&rl)) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "%s: BFD state change: %s->%s" " \"%s\"->\"%s\".\n", bfd->name, bfd_state_str(bfd->state), bfd_state_str(state), bfd_diag_str(bfd->diag), bfd_diag_str(diag)); bfd_put_details(&ds, bfd); VLOG_INFO("%s", ds_cstr(&ds)); ds_destroy(&ds); } bfd->state = state; bfd->diag = diag; if (bfd->state <= STATE_DOWN) { bfd->rmt_state = STATE_DOWN; bfd->rmt_diag = DIAG_NONE; bfd->rmt_min_rx = 1; bfd->rmt_flags = 0; bfd->rmt_disc = 0; bfd->rmt_min_tx = 0; /* Resets the min_rx if in_decay. */ if (bfd->in_decay) { bfd->min_rx = bfd->cfg_min_rx; bfd->in_decay = false; } } /* Resets the decay when state changes to STATE_UP * and decay_min_rx is configured. */ if (bfd->state == STATE_UP && bfd->decay_min_rx) { bfd_decay_update(bfd); } bfd_status_changed(bfd); } } static uint64_t bfd_rx_packets(const struct bfd *bfd) OVS_REQUIRES(mutex) { struct netdev_stats stats; if (!netdev_get_stats(bfd->netdev, &stats)) { return stats.rx_packets; } else { return 0; } } /* Decays the bfd->min_rx to bfd->decay_min_rx when 'diff' is less than * the 'expect' value. */ static void bfd_try_decay(struct bfd *bfd) OVS_REQUIRES(mutex) { int64_t diff, expect; /* The 'diff' is the difference between current interface rx_packets * stats and last-time check. The 'expect' is the recorded number of * bfd control packets received within an approximately decay_min_rx * (2000 ms if decay_min_rx is less than 2000 ms) interval. * * Since the update of rx_packets stats at interface happens * asynchronously to the bfd_rx_packets() function, the 'diff' value * can be jittered. Thusly, we double the decay_rx_ctl to provide * more wiggle room. */ diff = bfd_rx_packets(bfd) - bfd->decay_rx_packets; expect = 2 * MAX(bfd->decay_rx_ctl, 1); bfd->in_decay = diff <= expect ? true : false; bfd_decay_update(bfd); } /* Updates the rx_packets, decay_rx_ctl and decay_detect_time. */ static void bfd_decay_update(struct bfd * bfd) OVS_REQUIRES(mutex) { bfd->decay_rx_packets = bfd_rx_packets(bfd); bfd->decay_rx_ctl = 0; bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec(); } /* Records the status change and changes the global connectivity seq. */ static void bfd_status_changed(struct bfd *bfd) OVS_REQUIRES(mutex) { seq_change(connectivity_seq_get()); bfd->status_changed = true; } static void bfd_forwarding_if_rx_update(struct bfd *bfd) OVS_REQUIRES(mutex) { int64_t incr = bfd_rx_interval(bfd) * bfd->mult; bfd->forwarding_if_rx_detect_time = MAX(incr, 2000) + time_msec(); } static uint32_t generate_discriminator(void) { uint32_t disc = 0; /* RFC 5880 Section 6.8.1 * It SHOULD be set to a random (but still unique) value to improve * security. The value is otherwise outside the scope of this * specification. */ while (!disc) { struct bfd *bfd; /* 'disc' is by definition random, so there's no reason to waste time * hashing it. */ disc = random_uint32(); HMAP_FOR_EACH_IN_BUCKET (bfd, node, disc, all_bfds) { if (bfd->disc == disc) { disc = 0; break; } } } return disc; } static struct bfd * bfd_find_by_name(const char *name) OVS_REQUIRES(mutex) { struct bfd *bfd; HMAP_FOR_EACH (bfd, node, all_bfds) { if (!strcmp(bfd->name, name)) { return bfd; } } return NULL; } static void bfd_put_details(struct ds *ds, const struct bfd *bfd) OVS_REQUIRES(mutex) { ds_put_format(ds, "\tForwarding: %s\n", bfd_forwarding__(CONST_CAST(struct bfd *, bfd)) ? "true" : "false"); ds_put_format(ds, "\tDetect Multiplier: %d\n", bfd->mult); ds_put_format(ds, "\tConcatenated Path Down: %s\n", bfd->cpath_down ? "true" : "false"); ds_put_format(ds, "\tTX Interval: Approx %lldms\n", bfd_tx_interval(bfd)); ds_put_format(ds, "\tRX Interval: Approx %lldms\n", bfd_rx_interval(bfd)); ds_put_format(ds, "\tDetect Time: now %+lldms\n", time_msec() - bfd->detect_time); ds_put_format(ds, "\tNext TX Time: now %+lldms\n", time_msec() - bfd->next_tx); ds_put_format(ds, "\tLast TX Time: now %+lldms\n", time_msec() - bfd->last_tx); ds_put_cstr(ds, "\n"); ds_put_format(ds, "\tLocal Flags: %s\n", bfd_flag_str(bfd->flags)); ds_put_format(ds, "\tLocal Session State: %s\n", bfd_state_str(bfd->state)); ds_put_format(ds, "\tLocal Diagnostic: %s\n", bfd_diag_str(bfd->diag)); ds_put_format(ds, "\tLocal Discriminator: 0x%"PRIx32"\n", bfd->disc); ds_put_format(ds, "\tLocal Minimum TX Interval: %lldms\n", bfd_min_tx(bfd)); ds_put_format(ds, "\tLocal Minimum RX Interval: %lldms\n", bfd->min_rx); ds_put_cstr(ds, "\n"); ds_put_format(ds, "\tRemote Flags: %s\n", bfd_flag_str(bfd->rmt_flags)); ds_put_format(ds, "\tRemote Session State: %s\n", bfd_state_str(bfd->rmt_state)); ds_put_format(ds, "\tRemote Diagnostic: %s\n", bfd_diag_str(bfd->rmt_diag)); ds_put_format(ds, "\tRemote Discriminator: 0x%"PRIx32"\n", bfd->rmt_disc); ds_put_format(ds, "\tRemote Minimum TX Interval: %lldms\n", bfd->rmt_min_tx); ds_put_format(ds, "\tRemote Minimum RX Interval: %lldms\n", bfd->rmt_min_rx); } static void bfd_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; struct bfd *bfd; ovs_mutex_lock(&mutex); if (argc > 1) { bfd = bfd_find_by_name(argv[1]); if (!bfd) { unixctl_command_reply_error(conn, "no such bfd object"); goto out; } bfd_put_details(&ds, bfd); } else { HMAP_FOR_EACH (bfd, node, all_bfds) { ds_put_format(&ds, "---- %s ----\n", bfd->name); bfd_put_details(&ds, bfd); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); out: ovs_mutex_unlock(&mutex); } static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { const char *forward_str = argv[argc - 1]; int forwarding_override; struct bfd *bfd; ovs_mutex_lock(&mutex); if (!strcasecmp("true", forward_str)) { forwarding_override = 1; } else if (!strcasecmp("false", forward_str)) { forwarding_override = 0; } else if (!strcasecmp("normal", forward_str)) { forwarding_override = -1; } else { unixctl_command_reply_error(conn, "unknown fault string"); goto out; } if (argc > 2) { bfd = bfd_find_by_name(argv[1]); if (!bfd) { unixctl_command_reply_error(conn, "no such BFD object"); goto out; } bfd->forwarding_override = forwarding_override; bfd_status_changed(bfd); } else { HMAP_FOR_EACH (bfd, node, all_bfds) { bfd->forwarding_override = forwarding_override; bfd_status_changed(bfd); } } unixctl_command_reply(conn, "OK"); out: ovs_mutex_unlock(&mutex); } openvswitch-2.5.9/lib/PaxHeaders.82075/lacp.h0000644000000000000000000000013113534540071015501 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.749853191 openvswitch-2.5.9/lib/lacp.h0000644000175000017500000000703413534540071017174 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LACP_H #define LACP_H 1 #include #include #include "packets.h" /* LACP Protocol Implementation. */ enum lacp_status { LACP_NEGOTIATED, /* Successful LACP negotations. */ LACP_CONFIGURED, /* LACP is enabled but not negotiated. */ LACP_DISABLED /* LACP is not enabled. */ }; struct lacp_settings { char *name; /* Name (for debugging). */ struct eth_addr id; /* System ID. Must be nonzero. */ uint16_t priority; /* System priority. */ bool active; /* Active or passive mode? */ bool fast; /* Fast or slow probe interval. */ bool fallback_ab_cfg; /* Fallback to BM_SLB on LACP failure. */ }; void lacp_init(void); struct lacp *lacp_create(void); void lacp_unref(struct lacp *); struct lacp *lacp_ref(const struct lacp *); void lacp_configure(struct lacp *, const struct lacp_settings *); bool lacp_is_active(const struct lacp *); void lacp_process_packet(struct lacp *, const void *slave, const struct dp_packet *packet); enum lacp_status lacp_status(const struct lacp *); struct lacp_slave_settings { char *name; /* Name (for debugging). */ uint16_t id; /* Port ID. */ uint16_t priority; /* Port priority. */ uint16_t key; /* Aggregation key. */ }; void lacp_slave_register(struct lacp *, void *slave_, const struct lacp_slave_settings *); void lacp_slave_unregister(struct lacp *, const void *slave); void lacp_slave_carrier_changed(const struct lacp *, const void *slave); bool lacp_slave_may_enable(const struct lacp *, const void *slave); bool lacp_slave_is_current(const struct lacp *, const void *slave_); /* Callback function for lacp_run() for sending a LACP PDU. */ typedef void lacp_send_pdu(void *slave, const void *pdu, size_t pdu_size); void lacp_run(struct lacp *, lacp_send_pdu *); void lacp_wait(struct lacp *); struct lacp_slave_stats { /* id */ struct eth_addr dot3adAggPortActorSystemID; struct eth_addr dot3adAggPortPartnerOperSystemID; uint32_t dot3adAggPortAttachedAggID; /* state */ uint8_t dot3adAggPortActorAdminState; uint8_t dot3adAggPortActorOperState; uint8_t dot3adAggPortPartnerAdminState; uint8_t dot3adAggPortPartnerOperState; /* counters */ uint32_t dot3adAggPortStatsLACPDUsRx; /* uint32_t dot3adAggPortStatsMarkerPDUsRx; */ /* uint32_t dot3adAggPortStatsMarkerResponsePDUsRx; */ /* uint32_t dot3adAggPortStatsUnknownRx; */ uint32_t dot3adAggPortStatsIllegalRx; uint32_t dot3adAggPortStatsLACPDUsTx; /* uint32_t dot3adAggPortStatsMarkerPDUsTx; */ /* uint32_t dot3adAggPortStatsMarkerResponsePDUsTx; */ }; bool lacp_get_slave_stats(const struct lacp *, const void *slave_, struct lacp_slave_stats *); #endif /* lacp.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vconn-provider.h0000644000000000000000000000013213534540071017536 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.937854576 openvswitch-2.5.9/lib/vconn-provider.h0000644000175000017500000001772713534540071021242 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VCONN_PROVIDER_H #define VCONN_PROVIDER_H 1 /* Provider interface to vconns, which provide a virtual connection to an * OpenFlow device. */ #include "openvswitch/vconn.h" #include "util.h" #include "openflow/openflow-common.h" /* Active virtual connection to an OpenFlow device. */ /* This structure should be treated as opaque by vconn implementations. */ struct vconn { const struct vconn_class *vclass; int state; int error; /* OpenFlow versions. */ uint32_t allowed_versions; /* Bitmap of versions we will accept. */ uint32_t peer_versions; /* Peer's bitmap of versions it will accept. */ enum ofp_version version; /* Negotiated version (or 0). */ bool recv_any_version; /* True to receive a message of any version. */ char *name; }; void vconn_init(struct vconn *, const struct vconn_class *, int connect_status, const char *name, uint32_t allowed_versions); void vconn_free_data(struct vconn *vconn); static inline void vconn_assert_class(const struct vconn *vconn, const struct vconn_class *vclass) { ovs_assert(vconn->vclass == vclass); } struct vconn_class { /* Prefix for connection names, e.g. "nl", "tcp". */ const char *name; /* Attempts to connect to an OpenFlow device. 'name' is the full * connection name provided by the user, e.g. "tcp:1.2.3.4". This name is * useful for error messages but must not be modified. * * 'allowed_versions' is the OpenFlow versions that may be * negotiated for a connection. * * 'suffix' is a copy of 'name' following the colon and may be modified. * 'dscp' is the DSCP value that the new connection should use in the IP * packets it sends. * * Returns 0 if successful, otherwise a positive errno value. If * successful, stores a pointer to the new connection in '*vconnp'. * * The open function must not block waiting for a connection to complete. * If the connection cannot be completed immediately, it should return * EAGAIN (not EINPROGRESS, as returned by the connect system call) and * continue the connection in the background. */ int (*open)(const char *name, uint32_t allowed_versions, char *suffix, struct vconn **vconnp, uint8_t dscp); /* Closes 'vconn' and frees associated memory. */ void (*close)(struct vconn *vconn); /* Tries to complete the connection on 'vconn'. If 'vconn''s connection is * complete, returns 0 if the connection was successful or a positive errno * value if it failed. If the connection is still in progress, returns * EAGAIN. * * The connect function must not block waiting for the connection to * complete; instead, it should return EAGAIN immediately. */ int (*connect)(struct vconn *vconn); /* Tries to receive an OpenFlow message from 'vconn'. If successful, * stores the received message into '*msgp' and returns 0. The caller is * responsible for destroying the message with ofpbuf_delete(). On * failure, returns a positive errno value and stores a null pointer into * '*msgp'. * * If the connection has been closed in the normal fashion, returns EOF. * * The recv function must not block waiting for a packet to arrive. If no * packets have been received, it should return EAGAIN. */ int (*recv)(struct vconn *vconn, struct ofpbuf **msgp); /* Tries to queue 'msg' for transmission on 'vconn'. If successful, * returns 0, in which case ownership of 'msg' is transferred to the vconn. * Success does not guarantee that 'msg' has been or ever will be delivered * to the peer, only that it has been queued for transmission. * * Returns a positive errno value on failure, in which case the caller * retains ownership of 'msg'. * * The send function must not block. If 'msg' cannot be immediately * accepted for transmission, it should return EAGAIN. */ int (*send)(struct vconn *vconn, struct ofpbuf *msg); /* Allows 'vconn' to perform maintenance activities, such as flushing * output buffers. * * May be null if 'vconn' doesn't have anything to do here. */ void (*run)(struct vconn *vconn); /* Arranges for the poll loop to wake up when 'vconn' needs to perform * maintenance activities. * * May be null if 'vconn' doesn't have anything to do here. */ void (*run_wait)(struct vconn *vconn); /* Arranges for the poll loop to wake up when 'vconn' is ready to take an * action of the given 'type'. */ void (*wait)(struct vconn *vconn, enum vconn_wait_type type); }; /* Passive virtual connection to an OpenFlow device. */ /* This structure should be treated as opaque by vconn implementations. */ struct pvconn { const struct pvconn_class *pvclass; char *name; uint32_t allowed_versions; }; void pvconn_init(struct pvconn *pvconn, const struct pvconn_class *pvclass, const char *name, uint32_t allowed_versions); static inline void pvconn_assert_class(const struct pvconn *pvconn, const struct pvconn_class *pvclass) { ovs_assert(pvconn->pvclass == pvclass); } struct pvconn_class { /* Prefix for connection names, e.g. "ptcp", "pssl". */ const char *name; /* Attempts to start listening for OpenFlow connections. 'name' is the * full connection name provided by the user, e.g. "ptcp:1234". This name * is useful for error messages but must not be modified. * * 'allowed_versions' is the OpenFlow protocol versions that may * be negotiated for a session. * * 'suffix' is a copy of 'name' following the colon and may be modified. * 'dscp' is the DSCP value that the new connection should use in the IP * packets it sends. * * Returns 0 if successful, otherwise a positive errno value. If * successful, stores a pointer to the new connection in '*pvconnp'. * * The listen function must not block. If the connection cannot be * completed immediately, it should return EAGAIN (not EINPROGRESS, as * returned by the connect system call) and continue the connection in the * background. */ int (*listen)(const char *name, uint32_t allowed_versions, char *suffix, struct pvconn **pvconnp, uint8_t dscp); /* Closes 'pvconn' and frees associated memory. */ void (*close)(struct pvconn *pvconn); /* Tries to accept a new connection on 'pvconn'. If successful, stores the * new connection in '*new_vconnp' and returns 0. Otherwise, returns a * positive errno value. * * The accept function must not block waiting for a connection. If no * connection is ready to be accepted, it should return EAGAIN. */ int (*accept)(struct pvconn *pvconn, struct vconn **new_vconnp); /* Arranges for the poll loop to wake up when a connection is ready to be * accepted on 'pvconn'. */ void (*wait)(struct pvconn *pvconn); }; /* Active and passive vconn classes. */ extern const struct vconn_class tcp_vconn_class; extern const struct pvconn_class ptcp_pvconn_class; extern const struct vconn_class unix_vconn_class; extern const struct pvconn_class punix_pvconn_class; #ifdef HAVE_OPENSSL extern const struct vconn_class ssl_vconn_class; extern const struct pvconn_class pssl_pvconn_class; #endif #endif /* vconn-provider.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlandev.h0000644000000000000000000000013213534540071016222 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.945854636 openvswitch-2.5.9/lib/vlandev.h0000644000175000017500000000343613534540071017716 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VLANDEV_H #define VLANDEV_H 1 #include "hmap.h" /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device * drivers in old versions of Linux that do not properly support VLANs when * VLAN devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ /* A VLAN device (e.g. "eth0.10" for VLAN 10 on eth0). */ struct vlan_dev { struct vlan_real_dev *real_dev; /* Parent, e.g. "eth0". */ struct hmap_node hmap_node; /* In vlan_real_dev's "vlan_devs" map. */ char *name; /* VLAN device name, e.g. "eth0.10". */ int vid; /* VLAN ID, e.g. 10. */ }; /* A device that has VLAN devices broken out of it. */ struct vlan_real_dev { char *name; /* Name, e.g. "eth0". */ struct hmap vlan_devs; /* All child VLAN devices, hashed by VID. */ }; int vlandev_add(const char *real_dev, int vid); int vlandev_del(const char *vlan_dev); int vlandev_refresh(void); struct shash *vlandev_get_real_devs(void); const char *vlandev_get_name(const char *real_dev_name, int vid); #endif /* vlandev.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/util.h0000644000000000000000000000013213534540071015540 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.933854546 openvswitch-2.5.9/lib/util.h0000644000175000017500000004577713534540071017252 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UTIL_H #define UTIL_H 1 #include #include #include #include #include #include #include #include #include #include #include "compiler.h" #include "openvswitch/types.h" #include "openvswitch/util.h" #ifndef va_copy #ifdef __va_copy #define va_copy __va_copy #else #define va_copy(dst, src) ((dst) = (src)) #endif #endif #ifdef __CHECKER__ #define BUILD_ASSERT(EXPR) ((void) 0) #define BUILD_ASSERT_DECL(EXPR) extern int (*build_assert(void))[1] #elif !defined(__cplusplus) /* Build-time assertion building block. */ #define BUILD_ASSERT__(EXPR) \ sizeof(struct { unsigned int build_assert_failed : (EXPR) ? 1 : -1; }) /* Build-time assertion for use in a statement context. */ #define BUILD_ASSERT(EXPR) (void) BUILD_ASSERT__(EXPR) /* Build-time assertion for use in a declaration context. */ #define BUILD_ASSERT_DECL(EXPR) \ extern int (*build_assert(void))[BUILD_ASSERT__(EXPR)] #else /* __cplusplus */ #include #define BUILD_ASSERT BOOST_STATIC_ASSERT #define BUILD_ASSERT_DECL BOOST_STATIC_ASSERT #endif /* __cplusplus */ #ifdef __GNUC__ #define BUILD_ASSERT_GCCONLY(EXPR) BUILD_ASSERT(EXPR) #define BUILD_ASSERT_DECL_GCCONLY(EXPR) BUILD_ASSERT_DECL(EXPR) #else #define BUILD_ASSERT_GCCONLY(EXPR) ((void) 0) #define BUILD_ASSERT_DECL_GCCONLY(EXPR) ((void) 0) #endif /* Like the standard assert macro, except writes the failure message to the * log. */ #ifndef NDEBUG #define ovs_assert(CONDITION) \ if (!OVS_LIKELY(CONDITION)) { \ ovs_assert_failure(OVS_SOURCE_LOCATOR, __func__, #CONDITION); \ } #else #define ovs_assert(CONDITION) ((void) (CONDITION)) #endif OVS_NO_RETURN void ovs_assert_failure(const char *, const char *, const char *); /* Casts 'pointer' to 'type' and issues a compiler warning if the cast changes * anything other than an outermost "const" or "volatile" qualifier. * * The cast to int is present only to suppress an "expression using sizeof * bool" warning from "sparse" (see * http://permalink.gmane.org/gmane.comp.parsers.sparse/2967). */ #define CONST_CAST(TYPE, POINTER) \ ((void) sizeof ((int) ((POINTER) == (TYPE) (POINTER))), \ (TYPE) (POINTER)) extern char *program_name; #define __ARRAY_SIZE_NOCHECK(ARRAY) (sizeof(ARRAY) / sizeof((ARRAY)[0])) #ifdef __GNUC__ /* return 0 for array types, 1 otherwise */ #define __ARRAY_CHECK(ARRAY) \ !__builtin_types_compatible_p(typeof(ARRAY), typeof(&ARRAY[0])) /* compile-time fail if not array */ #define __ARRAY_FAIL(ARRAY) (sizeof(char[-2*!__ARRAY_CHECK(ARRAY)])) #define __ARRAY_SIZE(ARRAY) \ __builtin_choose_expr(__ARRAY_CHECK(ARRAY), \ __ARRAY_SIZE_NOCHECK(ARRAY), __ARRAY_FAIL(ARRAY)) #else #define __ARRAY_SIZE(ARRAY) __ARRAY_SIZE_NOCHECK(ARRAY) #endif /* Returns the number of elements in ARRAY. */ #define ARRAY_SIZE(ARRAY) __ARRAY_SIZE(ARRAY) /* Returns X / Y, rounding up. X must be nonnegative to round correctly. */ #define DIV_ROUND_UP(X, Y) (((X) + ((Y) - 1)) / (Y)) /* Returns X rounded up to the nearest multiple of Y. */ #define ROUND_UP(X, Y) (DIV_ROUND_UP(X, Y) * (Y)) /* Returns the least number that, when added to X, yields a multiple of Y. */ #define PAD_SIZE(X, Y) (ROUND_UP(X, Y) - (X)) /* Returns X rounded down to the nearest multiple of Y. */ #define ROUND_DOWN(X, Y) ((X) / (Y) * (Y)) /* Returns true if X is a power of 2, otherwise false. */ #define IS_POW2(X) ((X) && !((X) & ((X) - 1))) static inline bool is_pow2(uintmax_t x) { return IS_POW2(x); } /* Returns X rounded up to a power of 2. X must be a constant expression. */ #define ROUND_UP_POW2(X) RUP2__(X) #define RUP2__(X) (RUP2_1(X) + 1) #define RUP2_1(X) (RUP2_2(X) | (RUP2_2(X) >> 16)) #define RUP2_2(X) (RUP2_3(X) | (RUP2_3(X) >> 8)) #define RUP2_3(X) (RUP2_4(X) | (RUP2_4(X) >> 4)) #define RUP2_4(X) (RUP2_5(X) | (RUP2_5(X) >> 2)) #define RUP2_5(X) (RUP2_6(X) | (RUP2_6(X) >> 1)) #define RUP2_6(X) ((X) - 1) /* Returns X rounded down to a power of 2. X must be a constant expression. */ #define ROUND_DOWN_POW2(X) RDP2__(X) #define RDP2__(X) (RDP2_1(X) - (RDP2_1(X) >> 1)) #define RDP2_1(X) (RDP2_2(X) | (RDP2_2(X) >> 16)) #define RDP2_2(X) (RDP2_3(X) | (RDP2_3(X) >> 8)) #define RDP2_3(X) (RDP2_4(X) | (RDP2_4(X) >> 4)) #define RDP2_4(X) (RDP2_5(X) | (RDP2_5(X) >> 2)) #define RDP2_5(X) ( (X) | ( (X) >> 1)) /* This system's cache line size, in bytes. * Being wrong hurts performance but not correctness. */ #define CACHE_LINE_SIZE 64 BUILD_ASSERT_DECL(IS_POW2(CACHE_LINE_SIZE)); static inline void ovs_prefetch_range(const void *start, size_t size) { const char *addr = (const char *)start; size_t ofs; for (ofs = 0; ofs < size; ofs += CACHE_LINE_SIZE) { OVS_PREFETCH(addr + ofs); } } #ifndef MIN #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif #ifndef MAX #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif #define OVS_NOT_REACHED() abort() /* Given a pointer-typed lvalue OBJECT, expands to a pointer type that may be * assigned to OBJECT. */ #ifdef __GNUC__ #define OVS_TYPEOF(OBJECT) typeof(OBJECT) #else #define OVS_TYPEOF(OBJECT) void * #endif /* Given OBJECT of type pointer-to-structure, expands to the offset of MEMBER * within an instance of the structure. * * The GCC-specific version avoids the technicality of undefined behavior if * OBJECT is null, invalid, or not yet initialized. This makes some static * checkers (like Coverity) happier. But the non-GCC version does not actually * dereference any pointer, so it would be surprising for it to cause any * problems in practice. */ #ifdef __GNUC__ #define OBJECT_OFFSETOF(OBJECT, MEMBER) offsetof(typeof(*(OBJECT)), MEMBER) #else #define OBJECT_OFFSETOF(OBJECT, MEMBER) \ ((char *) &(OBJECT)->MEMBER - (char *) (OBJECT)) #endif /* Yields the size of MEMBER within STRUCT. */ #define MEMBER_SIZEOF(STRUCT, MEMBER) (sizeof(((STRUCT *) NULL)->MEMBER)) /* Given POINTER, the address of the given MEMBER in a STRUCT object, returns the STRUCT object. */ #define CONTAINER_OF(POINTER, STRUCT, MEMBER) \ ((STRUCT *) (void *) ((char *) (POINTER) - offsetof (STRUCT, MEMBER))) /* Given POINTER, the address of the given MEMBER within an object of the type * that that OBJECT points to, returns OBJECT as an assignment-compatible * pointer type (either the correct pointer type or "void *"). OBJECT must be * an lvalue. * * This is the same as CONTAINER_OF except that it infers the structure type * from the type of '*OBJECT'. */ #define OBJECT_CONTAINING(POINTER, OBJECT, MEMBER) \ ((OVS_TYPEOF(OBJECT)) (void *) \ ((char *) (POINTER) - OBJECT_OFFSETOF(OBJECT, MEMBER))) /* Given POINTER, the address of the given MEMBER within an object of the type * that that OBJECT points to, assigns the address of the outer object to * OBJECT, which must be an lvalue. * * Evaluates to (void) 0 as the result is not to be used. */ #define ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER) \ ((OBJECT) = OBJECT_CONTAINING(POINTER, OBJECT, MEMBER), (void) 0) /* As explained in the comment above OBJECT_OFFSETOF(), non-GNUC compilers * like MSVC will complain about un-initialized variables if OBJECT * hasn't already been initialized. To prevent such warnings, INIT_CONTAINER() * can be used as a wrapper around ASSIGN_CONTAINER. */ #define INIT_CONTAINER(OBJECT, POINTER, MEMBER) \ ((OBJECT) = NULL, ASSIGN_CONTAINER(OBJECT, POINTER, MEMBER)) /* Given ATTR, and TYPE, cast the ATTR to TYPE by first casting ATTR to * (void *). This is to suppress the alignment warning issued by clang. */ #define ALIGNED_CAST(TYPE, ATTR) ((TYPE) (void *) (ATTR)) /* Use "%"PRIuSIZE to format size_t with printf(). */ #ifdef _WIN32 #define PRIdSIZE "Id" #define PRIiSIZE "Ii" #define PRIoSIZE "Io" #define PRIuSIZE "Iu" #define PRIxSIZE "Ix" #define PRIXSIZE "IX" #else #define PRIdSIZE "zd" #define PRIiSIZE "zi" #define PRIoSIZE "zo" #define PRIuSIZE "zu" #define PRIxSIZE "zx" #define PRIXSIZE "zX" #endif #ifndef _WIN32 typedef uint32_t HANDLE; #endif #ifdef __cplusplus extern "C" { #endif #define set_program_name(name) \ ovs_set_program_name(name, OVS_PACKAGE_VERSION) const char *get_subprogram_name(void); void set_subprogram_name(const char *); void ovs_print_version(uint8_t min_ofp, uint8_t max_ofp); OVS_NO_RETURN void out_of_memory(void); void *xmalloc(size_t) MALLOC_LIKE; void *xcalloc(size_t, size_t) MALLOC_LIKE; void *xzalloc(size_t) MALLOC_LIKE; void *xrealloc(void *, size_t); void *xmemdup(const void *, size_t) MALLOC_LIKE; char *xmemdup0(const char *, size_t) MALLOC_LIKE; char *xstrdup(const char *) MALLOC_LIKE; char *xasprintf(const char *format, ...) OVS_PRINTF_FORMAT(1, 2) MALLOC_LIKE; char *xvasprintf(const char *format, va_list) OVS_PRINTF_FORMAT(1, 0) MALLOC_LIKE; void *x2nrealloc(void *p, size_t *n, size_t s); void *xmalloc_cacheline(size_t) MALLOC_LIKE; void *xzalloc_cacheline(size_t) MALLOC_LIKE; void free_cacheline(void *); void ovs_strlcpy(char *dst, const char *src, size_t size); void ovs_strzcpy(char *dst, const char *src, size_t size); OVS_NO_RETURN void ovs_abort(int err_no, const char *format, ...) OVS_PRINTF_FORMAT(2, 3); OVS_NO_RETURN void ovs_abort_valist(int err_no, const char *format, va_list) OVS_PRINTF_FORMAT(2, 0); OVS_NO_RETURN void ovs_fatal(int err_no, const char *format, ...) OVS_PRINTF_FORMAT(2, 3); OVS_NO_RETURN void ovs_fatal_valist(int err_no, const char *format, va_list) OVS_PRINTF_FORMAT(2, 0); void ovs_error(int err_no, const char *format, ...) OVS_PRINTF_FORMAT(2, 3); void ovs_error_valist(int err_no, const char *format, va_list) OVS_PRINTF_FORMAT(2, 0); const char *ovs_retval_to_string(int); const char *ovs_strerror(int); void ovs_hex_dump(FILE *, const void *, size_t, uintptr_t offset, bool ascii); bool str_to_int(const char *, int base, int *); bool str_to_long(const char *, int base, long *); bool str_to_llong(const char *, int base, long long *); bool str_to_uint(const char *, int base, unsigned int *); bool ovs_scan(const char *s, const char *format, ...) OVS_SCANF_FORMAT(2, 3); bool ovs_scan_len(const char *s, int *n, const char *format, ...); bool str_to_double(const char *, double *); int hexit_value(int c); uintmax_t hexits_value(const char *s, size_t n, bool *ok); int parse_int_string(const char *s, uint8_t *valuep, int field_width, char **tail); const char *english_list_delimiter(size_t index, size_t total); char *get_cwd(void); #ifndef _WIN32 char *dir_name(const char *file_name); char *base_name(const char *file_name); #endif char *abs_file_name(const char *dir, const char *file_name); char *follow_symlinks(const char *filename); void ignore(bool x OVS_UNUSED); /* Bitwise tests. */ /* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ #if __GNUC__ >= 4 static inline int raw_ctz(uint64_t n) { /* With GCC 4.7 on 32-bit x86, if a 32-bit integer is passed as 'n', using * a plain __builtin_ctzll() here always generates an out-of-line function * call. The test below helps it to emit a single 'bsf' instruction. */ return (__builtin_constant_p(n <= UINT32_MAX) && n <= UINT32_MAX ? __builtin_ctz(n) : __builtin_ctzll(n)); } static inline int raw_clz64(uint64_t n) { return __builtin_clzll(n); } #elif _MSC_VER static inline int raw_ctz(uint64_t n) { #ifdef _WIN64 unsigned long r = 0; _BitScanForward64(&r, n); return r; #else unsigned long low = n, high, r = 0; if (_BitScanForward(&r, low)) { return r; } high = n >> 32; _BitScanForward(&r, high); return r + 32; #endif } static inline int raw_clz64(uint64_t n) { #ifdef _WIN64 unsigned long r = 0; _BitScanReverse64(&r, n); return 63 - r; #else unsigned long low, high = n >> 32, r = 0; if (_BitScanReverse(&r, high)) { return 31 - r; } low = n; _BitScanReverse(&r, low); return 63 - r; #endif } #else /* Defined in util.c. */ int raw_ctz(uint64_t n); int raw_clz64(uint64_t n); #endif /* Returns the number of trailing 0-bits in 'n', or 32 if 'n' is 0. */ static inline int ctz32(uint32_t n) { return n ? raw_ctz(n) : 32; } /* Returns the number of trailing 0-bits in 'n', or 64 if 'n' is 0. */ static inline int ctz64(uint64_t n) { return n ? raw_ctz(n) : 64; } /* Returns the number of leading 0-bits in 'n', or 32 if 'n' is 0. */ static inline int clz32(uint32_t n) { return n ? raw_clz64(n) - 32 : 32; } /* Returns the number of leading 0-bits in 'n', or 64 if 'n' is 0. */ static inline int clz64(uint64_t n) { return n ? raw_clz64(n) : 64; } /* Given a word 'n', calculates floor(log_2('n')). This is equivalent * to finding the bit position of the most significant one bit in 'n'. It is * an error to call this function with 'n' == 0. */ static inline int log_2_floor(uint64_t n) { return 63 - raw_clz64(n); } /* Given a word 'n', calculates ceil(log_2('n')). It is an error to * call this function with 'n' == 0. */ static inline int log_2_ceil(uint64_t n) { return log_2_floor(n) + !is_pow2(n); } /* unsigned int count_1bits(uint64_t x): * * Returns the number of 1-bits in 'x', between 0 and 64 inclusive. */ #if UINTPTR_MAX == UINT64_MAX static inline unsigned int count_1bits(uint64_t x) { #if __GNUC__ >= 4 && __POPCNT__ return __builtin_popcountll(x); #else /* This portable implementation is the fastest one we know of for 64 * bits, and about 3x faster than GCC 4.7 __builtin_popcountll(). */ const uint64_t h55 = UINT64_C(0x5555555555555555); const uint64_t h33 = UINT64_C(0x3333333333333333); const uint64_t h0F = UINT64_C(0x0F0F0F0F0F0F0F0F); const uint64_t h01 = UINT64_C(0x0101010101010101); x -= (x >> 1) & h55; /* Count of each 2 bits in-place. */ x = (x & h33) + ((x >> 2) & h33); /* Count of each 4 bits in-place. */ x = (x + (x >> 4)) & h0F; /* Count of each 8 bits in-place. */ return (x * h01) >> 56; /* Sum of all bytes. */ #endif } #else /* Not 64-bit. */ #if __GNUC__ >= 4 && __POPCNT__ static inline unsigned int count_1bits_32__(uint32_t x) { return __builtin_popcount(x); } #else #define NEED_COUNT_1BITS_8 1 extern const uint8_t count_1bits_8[256]; static inline unsigned int count_1bits_32__(uint32_t x) { /* This portable implementation is the fastest one we know of for 32 bits, * and faster than GCC __builtin_popcount(). */ return (count_1bits_8[x & 0xff] + count_1bits_8[(x >> 8) & 0xff] + count_1bits_8[(x >> 16) & 0xff] + count_1bits_8[x >> 24]); } #endif static inline unsigned int count_1bits(uint64_t x) { return count_1bits_32__(x) + count_1bits_32__(x >> 32); } #endif /* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x' * is 0. */ static inline uintmax_t rightmost_1bit(uintmax_t x) { return x & -x; } /* Returns 'x' with its rightmost 1-bit changed to a zero (e.g. 01011000 => * 01010000), or 0 if 'x' is 0. */ static inline uintmax_t zero_rightmost_1bit(uintmax_t x) { return x & (x - 1); } /* Returns the index of the rightmost 1-bit in 'x' (e.g. 01011000 => 3), or an * undefined value if 'x' is 0. */ static inline int rightmost_1bit_idx(uint64_t x) { return ctz64(x); } /* Returns the index of the leftmost 1-bit in 'x' (e.g. 01011000 => 6), or an * undefined value if 'x' is 0. */ static inline uint32_t leftmost_1bit_idx(uint64_t x) { return log_2_floor(x); } /* Return a ovs_be32 prefix in network byte order with 'plen' highest bits set. * Shift with 32 is undefined behavior, but we rather use 64-bit shift than * compare. */ static inline ovs_be32 be32_prefix_mask(int plen) { return htonl((uint64_t)UINT32_MAX << (32 - plen)); } bool is_all_zeros(const void *, size_t); bool is_all_ones(const void *, size_t); void bitwise_copy(const void *src, unsigned int src_len, unsigned int src_ofs, void *dst, unsigned int dst_len, unsigned int dst_ofs, unsigned int n_bits); void bitwise_zero(void *dst_, unsigned int dst_len, unsigned dst_ofs, unsigned int n_bits); void bitwise_one(void *dst_, unsigned int dst_len, unsigned dst_ofs, unsigned int n_bits); bool bitwise_is_all_zeros(const void *, unsigned int len, unsigned int ofs, unsigned int n_bits); unsigned int bitwise_scan(const void *, unsigned int len, bool target, unsigned int start, unsigned int end); int bitwise_rscan(const void *, unsigned int len, bool target, int start, int end); void bitwise_put(uint64_t value, void *dst, unsigned int dst_len, unsigned int dst_ofs, unsigned int n_bits); uint64_t bitwise_get(const void *src, unsigned int src_len, unsigned int src_ofs, unsigned int n_bits); bool bitwise_get_bit(const void *src, unsigned int len, unsigned int ofs); void bitwise_put0(void *dst, unsigned int len, unsigned int ofs); void bitwise_put1(void *dst, unsigned int len, unsigned int ofs); void bitwise_put_bit(void *dst, unsigned int len, unsigned int ofs, bool); void bitwise_toggle_bit(void *dst, unsigned int len, unsigned int ofs); /* Returns non-zero if the parameters have equal value. */ static inline int ovs_u128_equals(const ovs_u128 *a, const ovs_u128 *b) { return (a->u64.hi == b->u64.hi) && (a->u64.lo == b->u64.lo); } /* Returns true if 'val' is 0. */ static inline bool ovs_u128_is_zero(const ovs_u128 *val) { return !(val->u64.hi || val->u64.lo); } /* Returns true if 'val' is all ones. */ static inline bool ovs_u128_is_ones(const ovs_u128 *val) { return ovs_u128_equals(val, &OVS_U128_MAX); } /* Returns non-zero if the parameters have equal value. */ static inline int ovs_be128_equals(const ovs_be128 *a, const ovs_be128 *b) { return (a->be64.hi == b->be64.hi) && (a->be64.lo == b->be64.lo); } /* Returns true if 'val' is 0. */ static inline bool ovs_be128_is_zero(const ovs_be128 *val) { return !(val->be64.hi || val->be64.lo); } static inline ovs_u128 ovs_u128_and(const ovs_u128 a, const ovs_u128 b) { ovs_u128 dst; dst.u64.hi = a.u64.hi & b.u64.hi; dst.u64.lo = a.u64.lo & b.u64.lo; return dst; } void xsleep(unsigned int seconds); #ifdef _WIN32 char *ovs_format_message(int error); char *ovs_lasterror_to_string(void); int ftruncate(int fd, off_t length); #endif #ifdef __cplusplus } #endif #endif /* util.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/guarded-list.h0000644000000000000000000000013213534540071017147 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.729853043 openvswitch-2.5.9/lib/guarded-list.h0000644000175000017500000000261113534540071020635 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef GUARDED_LIST_H #define GUARDED_LIST_H 1 #include #include "compiler.h" #include "list.h" #include "ovs-thread.h" struct guarded_list { struct ovs_mutex mutex; struct ovs_list list; size_t n; }; #define GUARDED_OVS_LIST_INITIALIZER(LIST) { \ .mutex = OVS_MUTEX_INITIALIZER, \ .list = OVS_LIST_INITIALIZER(&((LIST)->list)), \ .n = 0 } void guarded_list_init(struct guarded_list *); void guarded_list_destroy(struct guarded_list *); bool guarded_list_is_empty(const struct guarded_list *); size_t guarded_list_push_back(struct guarded_list *, struct ovs_list *, size_t max); struct ovs_list *guarded_list_pop_front(struct guarded_list *); size_t guarded_list_pop_all(struct guarded_list *, struct ovs_list *); #endif /* guarded-list.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/id-pool.c0000644000000000000000000000013213534540071016121 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.737853101 openvswitch-2.5.9/lib/id-pool.c0000644000175000017500000000667713534540071017627 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * Copyright (c) 2014 Netronome. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "id-pool.h" #include "hmap.h" #include "hash.h" struct id_node { struct hmap_node node; uint32_t id; }; struct id_pool { struct hmap map; uint32_t base; /* IDs in the range of [base, base + n_ids). */ uint32_t n_ids; /* Total number of ids in the pool. */ uint32_t next_free_id; /* Possible next free id. */ }; static void id_pool_init(struct id_pool *pool, uint32_t base, uint32_t n_ids); static void id_pool_uninit(struct id_pool *pool); static struct id_node *id_pool_find(struct id_pool *pool, uint32_t id); struct id_pool * id_pool_create(uint32_t base, uint32_t n_ids) { struct id_pool *pool; pool = xmalloc(sizeof *pool); id_pool_init(pool, base, n_ids); return pool; } void id_pool_destroy(struct id_pool *pool) { if (pool) { id_pool_uninit(pool); free(pool); } } static void id_pool_init(struct id_pool *pool, uint32_t base, uint32_t n_ids) { pool->base = base; pool->n_ids = n_ids; pool->next_free_id = base; hmap_init(&pool->map); } static void id_pool_uninit(struct id_pool *pool) { struct id_node *id_node, *next; HMAP_FOR_EACH_SAFE(id_node, next, node, &pool->map) { hmap_remove(&pool->map, &id_node->node); free(id_node); } hmap_destroy(&pool->map); } static struct id_node * id_pool_find(struct id_pool *pool, uint32_t id) { size_t hash; struct id_node *id_node; hash = hash_int(id, 0); HMAP_FOR_EACH_WITH_HASH(id_node, node, hash, &pool->map) { if (id == id_node->id) { return id_node; } } return NULL; } void id_pool_add(struct id_pool *pool, uint32_t id) { struct id_node *id_node = xmalloc(sizeof *id_node); size_t hash; id_node->id = id; hash = hash_int(id, 0); hmap_insert(&pool->map, &id_node->node, hash); } bool id_pool_alloc_id(struct id_pool *pool, uint32_t *id_) { uint32_t id; if (pool->n_ids == 0) { return false; } if (!(id_pool_find(pool, pool->next_free_id))) { id = pool->next_free_id; goto found_free_id; } for(id = pool->base; id < pool->base + pool->n_ids; id++) { if (!id_pool_find(pool, id)) { goto found_free_id; } } /* Not available. */ return false; found_free_id: id_pool_add(pool, id); if (id < pool->base + pool->n_ids) { pool->next_free_id = id + 1; } else { pool->next_free_id = pool->base; } *id_ = id; return true; } void id_pool_free_id(struct id_pool *pool, uint32_t id) { struct id_node *id_node; if (id > pool->base && (id <= pool->base + pool->n_ids)) { id_node = id_pool_find(pool, id); if (id_node) { hmap_remove(&pool->map, &id_node->node); free(id_node); } } } openvswitch-2.5.9/lib/PaxHeaders.82075/tnl-ports.c0000644000000000000000000000013213534540071016520 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.917854428 openvswitch-2.5.9/lib/tnl-ports.c0000644000175000017500000003017713534540071020216 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "tnl-ports.h" #include #include #include #include "classifier.h" #include "dynamic-string.h" #include "hash.h" #include "list.h" #include "netdev.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "odp-util.h" #include "ovs-thread.h" #include "unixctl.h" #include "util.h" static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct classifier cls; /* Tunnel ports. */ struct ip_device { struct netdev *dev; struct eth_addr mac; ovs_be32 addr4; struct in6_addr addr6; uint64_t change_seq; struct ovs_list node; char dev_name[IFNAMSIZ]; }; static struct ovs_list addr_list; struct tnl_port { odp_port_t port; ovs_be16 udp_port; char dev_name[IFNAMSIZ]; struct ovs_list node; }; static struct ovs_list port_list; struct tnl_port_in { struct cls_rule cr; odp_port_t portno; struct ovs_refcount ref_cnt; char dev_name[IFNAMSIZ]; }; static struct tnl_port_in * tnl_port_cast(const struct cls_rule *cr) { BUILD_ASSERT_DECL(offsetof(struct tnl_port_in, cr) == 0); return CONTAINER_OF(cr, struct tnl_port_in, cr); } static void tnl_port_free(struct tnl_port_in *p) { cls_rule_destroy(&p->cr); free(p); } static void tnl_port_init_flow(struct flow *flow, struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port) { memset(flow, 0, sizeof *flow); flow->dl_dst = mac; if (IN6_IS_ADDR_V4MAPPED(addr)) { flow->dl_type = htons(ETH_TYPE_IP); flow->nw_dst = in6_addr_get_mapped_ipv4(addr); } else { flow->dl_type = htons(ETH_TYPE_IPV6); flow->ipv6_dst = *addr; } if (udp_port) { flow->nw_proto = IPPROTO_UDP; } else { flow->nw_proto = IPPROTO_GRE; } flow->tp_dst = udp_port; } static void map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port, const char dev_name[]) { const struct cls_rule *cr; struct tnl_port_in *p; struct match match; memset(&match, 0, sizeof match); tnl_port_init_flow(&match.flow, mac, addr, udp_port); do { cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL); p = tnl_port_cast(cr); /* Try again if the rule was released before we get the reference. */ } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt)); if (!p) { p = xzalloc(sizeof *p); p->portno = port; match.wc.masks.dl_type = OVS_BE16_MAX; match.wc.masks.nw_proto = 0xff; /* XXX: No fragments support. */ match.wc.masks.nw_frag = FLOW_NW_FRAG_MASK; /* 'udp_port' is zero for non-UDP tunnels (e.g. GRE). In this case it * doesn't make sense to match on UDP port numbers. */ if (udp_port) { match.wc.masks.tp_dst = OVS_BE16_MAX; } if (IN6_IS_ADDR_V4MAPPED(addr)) { match.wc.masks.nw_dst = OVS_BE32_MAX; } else { match.wc.masks.ipv6_dst = in6addr_exact; } match.wc.masks.vlan_tci = OVS_BE16_MAX; memset(&match.wc.masks.dl_dst, 0xff, sizeof (struct eth_addr)); cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */ ovs_refcount_init(&p->ref_cnt); ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name); classifier_insert(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0); } } void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[]) { struct tnl_port *p; struct ip_device *ip_dev; ovs_mutex_lock(&mutex); LIST_FOR_EACH(p, node, &port_list) { if (udp_port == p->udp_port) { goto out; } } p = xzalloc(sizeof *p); p->port = port; p->udp_port = udp_port; ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name); list_insert(&port_list, &p->node); LIST_FOR_EACH(ip_dev, node, &addr_list) { if (ip_dev->addr4 != INADDR_ANY) { struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4); map_insert(p->port, ip_dev->mac, &addr4, p->udp_port, p->dev_name); } if (ipv6_addr_is_set(&ip_dev->addr6)) { map_insert(p->port, ip_dev->mac, &ip_dev->addr6, p->udp_port, p->dev_name); } } out: ovs_mutex_unlock(&mutex); } static void tnl_port_unref(const struct cls_rule *cr) { struct tnl_port_in *p = tnl_port_cast(cr); if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) { if (classifier_remove(&cls, cr)) { ovsrcu_postpone(tnl_port_free, p); } } } static void map_delete(struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port) { const struct cls_rule *cr; struct flow flow; tnl_port_init_flow(&flow, mac, addr, udp_port); cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL); tnl_port_unref(cr); } void tnl_port_map_delete(ovs_be16 udp_port) { struct tnl_port *p, *next; struct ip_device *ip_dev; bool found = false; ovs_mutex_lock(&mutex); LIST_FOR_EACH_SAFE(p, next, node, &port_list) { if (p->udp_port == udp_port) { list_remove(&p->node); found = true; break; } } if (!found) { goto out; } LIST_FOR_EACH(ip_dev, node, &addr_list) { if (ip_dev->addr4 != INADDR_ANY) { struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4); map_delete(ip_dev->mac, &addr4, udp_port); } if (ipv6_addr_is_set(&ip_dev->addr6)) { map_delete(ip_dev->mac, &ip_dev->addr6, udp_port); } } free(p); out: ovs_mutex_unlock(&mutex); } /* 'flow' is non-const to allow for temporary modifications during the lookup. * Any changes are restored before returning. */ odp_port_t tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc) { const struct cls_rule *cr = classifier_lookup(&cls, CLS_MAX_VERSION, flow, wc); return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE; } static void tnl_port_show_v(struct ds *ds) { const struct tnl_port_in *p; CLS_FOR_EACH(p, cr, &cls) { struct odputil_keybuf keybuf; struct odputil_keybuf maskbuf; struct flow flow; const struct nlattr *key, *mask; size_t key_len, mask_len; struct flow_wildcards wc; struct ofpbuf buf; struct odp_flow_key_parms odp_parms = { .flow = &flow, .mask = &wc.masks, }; ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno); minimask_expand(p->cr.match.mask, &wc); miniflow_expand(p->cr.match.flow, &flow); /* Key. */ odp_parms.odp_in_port = flow.in_port.odp_port; odp_parms.support.recirc = true; ofpbuf_use_stack(&buf, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&odp_parms, &buf); key = buf.data; key_len = buf.size; /* mask*/ odp_parms.odp_in_port = wc.masks.in_port.odp_port; odp_parms.support.recirc = false; ofpbuf_use_stack(&buf, &maskbuf, sizeof maskbuf); odp_flow_key_from_mask(&odp_parms, &buf); mask = buf.data; mask_len = buf.size; /* build string. */ odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false); ds_put_format(ds, "\n"); } } static void tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct tnl_port *p; ds_put_format(&ds, "Listening ports:\n"); ovs_mutex_lock(&mutex); if (argc > 1) { if (!strcasecmp(argv[1], "-v")) { tnl_port_show_v(&ds); goto out; } } LIST_FOR_EACH(p, node, &port_list) { ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port); } out: ovs_mutex_unlock(&mutex); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void map_insert_ipdev(struct ip_device *ip_dev) { struct tnl_port *p; LIST_FOR_EACH(p, node, &port_list) { if (ip_dev->addr4 != INADDR_ANY) { struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4); map_insert(p->port, ip_dev->mac, &addr4, p->udp_port, p->dev_name); } if (ipv6_addr_is_set(&ip_dev->addr6)) { map_insert(p->port, ip_dev->mac, &ip_dev->addr6, p->udp_port, p->dev_name); } } } static void insert_ipdev(const char dev_name[]) { struct ip_device *ip_dev; enum netdev_flags flags; struct netdev *dev; int error; int error4, error6; error = netdev_open(dev_name, NULL, &dev); if (error) { return; } error = netdev_get_flags(dev, &flags); if (error || (flags & NETDEV_LOOPBACK)) { netdev_close(dev); return; } ip_dev = xzalloc(sizeof *ip_dev); ip_dev->dev = dev; ip_dev->change_seq = netdev_get_change_seq(dev); error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac); if (error) { netdev_close(dev); free(ip_dev); return; } error4 = netdev_get_in4(ip_dev->dev, (struct in_addr *)&ip_dev->addr4, NULL); error6 = netdev_get_in6(ip_dev->dev, &ip_dev->addr6); if (error4 && error6) { netdev_close(dev); free(ip_dev); return; } ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name); list_insert(&addr_list, &ip_dev->node); map_insert_ipdev(ip_dev); } static void delete_ipdev(struct ip_device *ip_dev) { struct tnl_port *p; LIST_FOR_EACH(p, node, &port_list) { if (ip_dev->addr4 != INADDR_ANY) { struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4); map_delete(ip_dev->mac, &addr4, p->udp_port); } if (ipv6_addr_is_set(&ip_dev->addr6)) { map_delete(ip_dev->mac, &ip_dev->addr6, p->udp_port); } } list_remove(&ip_dev->node); netdev_close(ip_dev->dev); free(ip_dev); } void tnl_port_map_insert_ipdev(const char dev_name[]) { struct ip_device *ip_dev, *next; ovs_mutex_lock(&mutex); LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) { if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) { if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) { goto out; } /* Address changed. */ delete_ipdev(ip_dev); break; } } insert_ipdev(dev_name); out: ovs_mutex_unlock(&mutex); } void tnl_port_map_delete_ipdev(const char dev_name[]) { struct ip_device *ip_dev, *next; ovs_mutex_lock(&mutex); LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) { if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) { delete_ipdev(ip_dev); } } ovs_mutex_unlock(&mutex); } void tnl_port_map_run(void) { struct ip_device *ip_dev, *next; ovs_mutex_lock(&mutex); LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) { char dev_name[IFNAMSIZ]; if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) { continue; } /* Address changed. */ ovs_strlcpy(dev_name, ip_dev->dev_name, sizeof dev_name); delete_ipdev(ip_dev); insert_ipdev(dev_name); } ovs_mutex_unlock(&mutex); } void tnl_port_map_init(void) { classifier_init(&cls, flow_segment_u64s); list_init(&addr_list); list_init(&port_list); unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL); } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-errors.c0000644000000000000000000000013213534540071016654 xustar0030 mtime=1567801401.513681992 30 atime=1567801402.085686193 30 ctime=1567801424.793853514 openvswitch-2.5.9/lib/ofp-errors.c0000644000175000017500000002622513534540071020351 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofp-errors.h" #include #include "byte-order.h" #include "dynamic-string.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_errors); struct triplet { uint32_t vendor; int type, code; }; #include "ofp-errors.inc" /* Returns an ofperr_domain that corresponds to the OpenFlow version number * 'version' (one of the possible values of struct ofp_header's 'version' * member). Returns NULL if the version isn't defined or isn't understood by * OVS. */ static const struct ofperr_domain * ofperr_domain_from_version(enum ofp_version version) { switch (version) { case OFP10_VERSION: return &ofperr_of10; case OFP11_VERSION: return &ofperr_of11; case OFP12_VERSION: return &ofperr_of12; case OFP13_VERSION: return &ofperr_of13; case OFP14_VERSION: return &ofperr_of14; case OFP15_VERSION: return &ofperr_of15; default: return NULL; } } /* Returns the name (e.g. "OpenFlow 1.0") of OpenFlow version 'version'. */ const char * ofperr_domain_get_name(enum ofp_version version) { const struct ofperr_domain *domain = ofperr_domain_from_version(version); return domain ? domain->name : NULL; } /* Returns true if 'error' is a valid OFPERR_* value, false otherwise. */ bool ofperr_is_valid(enum ofperr error) { return error >= OFPERR_OFS && error < OFPERR_OFS + OFPERR_N_ERRORS; } /* Returns the OFPERR_* value that corresponds to 'type' and 'code' within * 'version', or 0 if either no such OFPERR_* value exists or 'version' is * unknown. */ static enum ofperr ofperr_decode(enum ofp_version version, uint32_t vendor, uint16_t type, uint16_t code) { const struct ofperr_domain *domain = ofperr_domain_from_version(version); return domain ? domain->decode(vendor, type, code) : 0; } /* Returns the name of 'error', e.g. "OFPBRC_BAD_TYPE" if 'error' is * OFPBRC_BAD_TYPE, or "" if 'error' is not a valid OFPERR_* value. * * Consider ofperr_to_string() instead, if the error code might be an errno * value. */ const char * ofperr_get_name(enum ofperr error) { return (ofperr_is_valid(error) ? error_names[error - OFPERR_OFS] : ""); } /* Returns the OFPERR_* value that corresponds for 'name', 0 if none exists. * For example, returns OFPERR_OFPHFC_INCOMPATIBLE if 'name' is * "OFPHFC_INCOMPATIBLE". * * This is probably useful only for debugging and testing. */ enum ofperr ofperr_from_name(const char *name) { int i; for (i = 0; i < OFPERR_N_ERRORS; i++) { if (!strcmp(name, error_names[i])) { return i + OFPERR_OFS; } } return 0; } /* Returns an extended description name of 'error', e.g. "ofp_header.type not * supported." if 'error' is OFPBRC_BAD_TYPE, or "" if 'error' is not * a valid OFPERR_* value. */ const char * ofperr_get_description(enum ofperr error) { return (ofperr_is_valid(error) ? error_comments[error - OFPERR_OFS] : ""); } static const struct triplet * ofperr_get_triplet__(enum ofperr error, const struct ofperr_domain *domain) { size_t ofs = error - OFPERR_OFS; ovs_assert(ofperr_is_valid(error)); return &domain->errors[ofs]; } static struct ofpbuf * ofperr_encode_msg__(enum ofperr error, enum ofp_version ofp_version, ovs_be32 xid, const void *data, size_t data_len) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct ofperr_domain *domain; const struct triplet *triplet; struct ofp_error_msg *oem; struct ofpbuf *buf; /* Get the error domain for 'ofp_version', or fall back to OF1.0. */ domain = ofperr_domain_from_version(ofp_version); if (!domain) { VLOG_ERR_RL(&rl, "cannot encode error for unknown OpenFlow " "version 0x%02x", ofp_version); domain = &ofperr_of10; } /* Make sure 'error' is valid in 'domain', or use a fallback error. */ if (!ofperr_is_valid(error)) { /* 'error' seems likely to be a system errno value. */ VLOG_ERR_RL(&rl, "invalid OpenFlow error code %d (%s)", error, ovs_strerror(error)); error = OFPERR_NXBRC_UNENCODABLE_ERROR; } else if (domain->errors[error - OFPERR_OFS].code < 0) { VLOG_ERR_RL(&rl, "cannot encode %s for %s", ofperr_get_name(error), domain->name); error = OFPERR_NXBRC_UNENCODABLE_ERROR; } triplet = ofperr_get_triplet__(error, domain); if (!triplet->vendor) { buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid, sizeof *oem + data_len); oem = ofpbuf_put_uninit(buf, sizeof *oem); oem->type = htons(triplet->type); oem->code = htons(triplet->code); } else if (ofp_version <= OFP11_VERSION) { struct nx_vendor_error *nve; buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid, sizeof *oem + sizeof *nve + data_len); oem = ofpbuf_put_uninit(buf, sizeof *oem); oem->type = htons(NXET_VENDOR); oem->code = htons(NXVC_VENDOR_ERROR); nve = ofpbuf_put_uninit(buf, sizeof *nve); nve->vendor = htonl(triplet->vendor); nve->type = htons(triplet->type); nve->code = htons(triplet->code); } else { ovs_be32 vendor = htonl(triplet->vendor); buf = ofpraw_alloc_xid(OFPRAW_OFPT_ERROR, domain->version, xid, sizeof *oem + sizeof(uint32_t) + data_len); oem = ofpbuf_put_uninit(buf, sizeof *oem); oem->type = htons(OFPET12_EXPERIMENTER); oem->code = htons(triplet->type); ofpbuf_put(buf, &vendor, sizeof vendor); } ofpbuf_put(buf, data, data_len); ofpmsg_update_length(buf); return buf; } /* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the * given 'error'. * * 'oh->version' determines the OpenFlow version of the error reply. * 'oh->xid' determines the xid of the error reply. * The error reply will contain an initial subsequence of 'oh', up to * 'oh->length' or 64 bytes, whichever is shorter. * * This function isn't appropriate for encoding OFPET_HELLO_FAILED error * messages. Use ofperr_encode_hello() instead. */ struct ofpbuf * ofperr_encode_reply(enum ofperr error, const struct ofp_header *oh) { uint16_t len = ntohs(oh->length); return ofperr_encode_msg__(error, oh->version, oh->xid, oh, MIN(len, 64)); } /* Creates and returns an OpenFlow message of type OFPT_ERROR that conveys the * given 'error', in the error domain 'domain'. The error message will include * the additional null-terminated text string 's'. * * If 'version' is an unknown version then OFP10_VERSION is used. * OFPET_HELLO_FAILED error messages are supposed to be backward-compatible, * so in theory this should work. */ struct ofpbuf * ofperr_encode_hello(enum ofperr error, enum ofp_version ofp_version, const char *s) { return ofperr_encode_msg__(error, ofp_version, htonl(0), s, strlen(s)); } int ofperr_get_vendor(enum ofperr error, enum ofp_version version) { const struct ofperr_domain *domain = ofperr_domain_from_version(version); return domain ? ofperr_get_triplet__(error, domain)->vendor : -1; } /* Returns the value that would go into an OFPT_ERROR message's 'type' for * encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in * 'version' or 'version' is unknown. * * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */ int ofperr_get_type(enum ofperr error, enum ofp_version version) { const struct ofperr_domain *domain = ofperr_domain_from_version(version); return domain ? ofperr_get_triplet__(error, domain)->type : -1; } /* Returns the value that would go into an OFPT_ERROR message's 'code' for * encoding 'error' in 'domain'. Returns -1 if 'error' is not encodable in * 'version', 'version' is unknown or if 'error' represents a category * rather than a specific error. * * * 'error' must be a valid OFPERR_* code, as checked by ofperr_is_valid(). */ int ofperr_get_code(enum ofperr error, enum ofp_version version) { const struct ofperr_domain *domain = ofperr_domain_from_version(version); return domain ? ofperr_get_triplet__(error, domain)->code : -1; } /* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message. * Returns an OFPERR_* constant on success, 0 on failure. * * If 'payload' is nonnull, on success '*payload' is initialized with a copy of * the error's payload (copying is required because the payload is not properly * aligned). The caller must free the payload (with ofpbuf_uninit()) when it * is no longer needed. On failure, '*payload' is cleared. */ enum ofperr ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload) { const struct ofp_error_msg *oem; enum ofpraw raw; uint16_t type, code; enum ofperr error; uint32_t vendor; struct ofpbuf b; if (payload) { memset(payload, 0, sizeof *payload); } /* Pull off the error message. */ ofpbuf_use_const(&b, oh, ntohs(oh->length)); error = ofpraw_pull(&raw, &b); if (error) { return 0; } oem = ofpbuf_pull(&b, sizeof *oem); /* Get the error type and code. */ vendor = 0; type = ntohs(oem->type); code = ntohs(oem->code); if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) { const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve); if (!nve) { return 0; } vendor = ntohl(nve->vendor); type = ntohs(nve->type); code = ntohs(nve->code); } else if (type == OFPET12_EXPERIMENTER) { const ovs_be32 *vendorp = ofpbuf_try_pull(&b, sizeof *vendorp); if (!vendorp) { return 0; } vendor = ntohl(*vendorp); type = code; code = 0; } /* Translate the error type and code into an ofperr. */ error = ofperr_decode(oh->version, vendor, type, code); if (error && payload) { ofpbuf_init(payload, b.size); ofpbuf_push(payload, b.data, b.size); } return error; } /* If 'error' is a valid OFPERR_* value, returns its name * (e.g. "OFPBRC_BAD_TYPE" for OFPBRC_BAD_TYPE). Otherwise, assumes that * 'error' is a positive errno value and returns what ovs_strerror() produces * for 'error'. */ const char * ofperr_to_string(enum ofperr error) { return (ofperr_is_valid(error) ? ofperr_get_name(error) : ovs_strerror(error)); } openvswitch-2.5.9/lib/PaxHeaders.82075/jhash.h0000644000000000000000000000013213534540071015660 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.741853131 openvswitch-2.5.9/lib/jhash.h0000644000175000017500000000240613534540071017350 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JHASH_H #define JHASH_H 1 #include #include #include #include #include "util.h" #ifdef __cplusplus extern "C" { #endif /* This is the public domain lookup3 hash by Bob Jenkins from * http://burtleburtle.net/bob/c/lookup3.c, modified for style. * * Use the functions in hash.h instead if you can. These are here just for * places where we've exposed a hash function "on the wire" and don't want it * to change. */ uint32_t jhash_words(const uint32_t *, size_t n_word, uint32_t basis); uint32_t jhash_bytes(const void *, size_t n_bytes, uint32_t basis); #ifdef __cplusplus } #endif #endif /* jhash.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/mcast-snooping.h0000644000000000000000000000013213534540071017524 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.761853278 openvswitch-2.5.9/lib/mcast-snooping.h0000644000175000017500000001637013534540071021221 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Red Hat, Inc. * * Based on mac-learning implementation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MCAST_SNOOPING_H #define MCAST_SNOOPING_H 1 #include #include "dp-packet.h" #include "hmap.h" #include "list.h" #include "ovs-atomic.h" #include "ovs-thread.h" #include "packets.h" #include "timeval.h" struct mcast_snooping; /* Default maximum size of a mcast snooping table, in entries. */ #define MCAST_DEFAULT_MAX_ENTRIES 2048 /* Time, in seconds, before expiring a mcast_group due to inactivity. */ #define MCAST_ENTRY_DEFAULT_IDLE_TIME 300 /* Time, in seconds, before expiring a mrouter_port due to inactivity. */ #define MCAST_MROUTER_PORT_IDLE_TIME 180 /* Multicast group entry. * Guarded by owning 'mcast_snooping''s rwlock. */ struct mcast_group { /* Node in parent struct mcast_snooping hmap. */ struct hmap_node hmap_node; /* Multicast group IPv6/IPv4 address. */ struct in6_addr addr; /* VLAN tag. */ uint16_t vlan; /* Node in parent struct mcast_snooping group_lru. */ struct ovs_list group_node OVS_GUARDED; /* Contains struct mcast_group_bundle (ports), least recently used * at the front, most recently used at the back. */ struct ovs_list bundle_lru OVS_GUARDED; }; /* The bundle associated to the multicast group. * Guarded by owning 'mcast_snooping''s rwlock. */ struct mcast_group_bundle { /* Node in parent struct mcast_group bundle_lru list. */ struct ovs_list bundle_node OVS_GUARDED; /* When this node expires. */ time_t expires; /* Learned port. */ void *port OVS_GUARDED; }; /* The bundle connected to a multicast router. * Guarded by owning 'mcast_snooping''s rwlock. */ struct mcast_mrouter_bundle { /* Node in parent struct mcast_group mrouter_lru list. */ struct ovs_list mrouter_node OVS_GUARDED; /* When this node expires. */ time_t expires; /* VLAN tag. */ uint16_t vlan; /* Learned port. */ void *port OVS_GUARDED; }; /* The bundle to send multicast traffic or Reports. * Guarded by owning 'mcast_snooping''s rwlock */ struct mcast_port_bundle { /* Node in parent struct mcast_snooping. */ struct ovs_list node; /* VLAN tag. */ uint16_t vlan; /* Learned port. */ void *port; }; /* Multicast snooping table. */ struct mcast_snooping { /* Snooping/learning table. */ struct hmap table; /* Contains struct mcast_group, least recently used at the front, * most recently used at the back. */ struct ovs_list group_lru OVS_GUARDED; /* Contains struct mcast_mrouter_bundle, least recently used at the * front, most recently used at the back. */ struct ovs_list mrouter_lru OVS_GUARDED; /* Contains struct mcast_port_bundle to be flooded with multicast * packets in no special order. */ struct ovs_list fport_list OVS_GUARDED; /* Contains struct mcast_port_bundle to forward Reports in * no special order. */ struct ovs_list rport_list OVS_GUARDED; /* Secret for randomizing hash table. */ uint32_t secret; /* Maximum age before deleting an entry. */ unsigned int idle_time; /* Maximum number of multicast groups learned. */ size_t max_entries; /* True if flow revalidation is needed. */ bool need_revalidate; /* True if unregistered multicast packets should be flooded to all * ports, otherwise send them to ports connected to multicast routers. */ bool flood_unreg; struct ovs_refcount ref_cnt; struct ovs_rwlock rwlock; }; /* Basics. */ bool mcast_snooping_enabled(const struct mcast_snooping *ms); bool mcast_snooping_flood_unreg(const struct mcast_snooping *ms); int mcast_mrouter_age(const struct mcast_snooping *ms, const struct mcast_mrouter_bundle *m); int mcast_bundle_age(const struct mcast_snooping *ms, const struct mcast_group_bundle *b); struct mcast_snooping *mcast_snooping_create(void); struct mcast_snooping *mcast_snooping_ref(const struct mcast_snooping *); void mcast_snooping_unref(struct mcast_snooping *); bool mcast_snooping_run(struct mcast_snooping *ms); void mcast_snooping_wait(struct mcast_snooping *ms); /* Configuration. */ void mcast_snooping_set_idle_time(struct mcast_snooping *ms, unsigned int idle_time) OVS_REQ_WRLOCK(ms->rwlock); void mcast_snooping_set_max_entries(struct mcast_snooping *ms, size_t max_entries) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, bool enable) OVS_REQ_WRLOCK(ms->rwlock); void mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port, bool flood) OVS_REQ_WRLOCK(ms->rwlock); void mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms, void *port, bool flood) OVS_REQ_WRLOCK(ms->rwlock); /* Lookup. */ struct mcast_group * mcast_snooping_lookup(const struct mcast_snooping *ms, const struct in6_addr *dip, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock); struct mcast_group * mcast_snooping_lookup4(const struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock); /* Learning. */ bool mcast_snooping_add_group(struct mcast_snooping *ms, const struct in6_addr *addr, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_add_group4(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); int mcast_snooping_add_report(struct mcast_snooping *ms, const struct dp_packet *p, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); int mcast_snooping_add_mld(struct mcast_snooping *ms, const struct dp_packet *p, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_leave_group(struct mcast_snooping *ms, const struct in6_addr *addr, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_leave_group4(struct mcast_snooping *ms, ovs_be32 ip4, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_add_mrouter(struct mcast_snooping *ms, uint16_t vlan, void *port) OVS_REQ_WRLOCK(ms->rwlock); bool mcast_snooping_is_query(ovs_be16 igmp_type); bool mcast_snooping_is_membership(ovs_be16 igmp_type); /* Flush. */ void mcast_snooping_mdb_flush(struct mcast_snooping *ms); void mcast_snooping_flush(struct mcast_snooping *ms); #endif /* mcast-snooping.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/libsflow.pc.in0000644000000000000000000000013113534540071017163 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.641852394 openvswitch-2.5.9/lib/libsflow.pc.in0000644000175000017500000000035113534540071020651 0ustar00jpettitjpettit00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libofproto Description: sFlow library of Open vSwitch Version: @VERSION@ Libs: -L${libdir} -lsflow Libs.private: @LIBS@ Cflags: -I${includedir} openvswitch-2.5.9/lib/PaxHeaders.82075/coverage.h0000644000000000000000000000013213534540071016356 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.685852718 openvswitch-2.5.9/lib/coverage.h0000644000175000017500000000762713534540071020060 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef COVERAGE_H #define COVERAGE_H 1 /* This file implements a simple form of coverage instrumentation. Points in * source code that are of interest must be explicitly annotated with * COVERAGE_INC. The coverage counters may be logged at any time with * coverage_log(). * * This form of coverage instrumentation is intended to be so lightweight that * it can be enabled in production builds. It is obviously not a substitute * for traditional coverage instrumentation with e.g. "gcov", but it is still * a useful debugging tool. */ #include "ovs-thread.h" #include "compiler.h" /* Makes coverage_run run every 5000 ms (5 seconds). * If this value is redefined, the new value must * divide 60000 (1 minute). */ #define COVERAGE_RUN_INTERVAL 5000 BUILD_ASSERT_DECL(60000 % COVERAGE_RUN_INTERVAL == 0); #define COVERAGE_CLEAR_INTERVAL 1000 BUILD_ASSERT_DECL(COVERAGE_RUN_INTERVAL % COVERAGE_CLEAR_INTERVAL == 0); /* Defines the moving average array length. */ #define MIN_AVG_LEN (60000/COVERAGE_RUN_INTERVAL) #define HR_AVG_LEN 60 /* A coverage counter. */ struct coverage_counter { const char *const name; /* Textual name. */ unsigned int (*const count)(void); /* Gets, zeros this thread's count. */ unsigned long long int total; /* Total count. */ unsigned long long int last_total; /* The moving average arrays. */ unsigned int min[MIN_AVG_LEN]; unsigned int hr[HR_AVG_LEN]; }; void coverage_counter_register(struct coverage_counter*); /* Defines COUNTER. There must be exactly one such definition at file scope * within a program. */ #define COVERAGE_DEFINE(COUNTER) \ DEFINE_STATIC_PER_THREAD_DATA(unsigned int, \ counter_##COUNTER, 0); \ static unsigned int COUNTER##_count(void) \ { \ unsigned int *countp = counter_##COUNTER##_get(); \ unsigned int count = *countp; \ *countp = 0; \ return count; \ } \ static inline void COUNTER##_add(unsigned int n) \ { \ *counter_##COUNTER##_get() += n; \ } \ extern struct coverage_counter counter_##COUNTER; \ struct coverage_counter counter_##COUNTER \ = { #COUNTER, COUNTER##_count, 0, 0, {0}, {0} }; \ OVS_CONSTRUCTOR(COUNTER##_init) { \ coverage_counter_register(&counter_##COUNTER); \ } /* Adds 1 to COUNTER. */ #define COVERAGE_INC(COUNTER) COVERAGE_ADD(COUNTER, 1) /* Adds AMOUNT to COUNTER. */ #define COVERAGE_ADD(COUNTER, AMOUNT) COUNTER##_add(AMOUNT) void coverage_init(void); void coverage_log(void); void coverage_clear(void); void coverage_try_clear(void); void coverage_run(void); #endif /* coverage.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-conntrack.h0000644000000000000000000000013213534540071020207 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 30 ctime=1567801424.977854871 openvswitch-2.5.9/lib/netlink-conntrack.h0000644000175000017500000000307413534540071021701 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETLINK_CONNTRACK_H #define NETLINK_CONNTRACK_H #include "byte-order.h" #include "compiler.h" #include "ct-dpif.h" #include "dynamic-string.h" #include "hmap.h" #include "ofpbuf.h" #include "timeval.h" #include "unixctl.h" #include "util.h" enum nl_ct_event_type { NL_CT_EVENT_NEW = 1 << 0, NL_CT_EVENT_UPDATE = 1 << 1, NL_CT_EVENT_DELETE = 1 << 2, }; struct nl_ct_dump_state; int nl_ct_dump_start(struct nl_ct_dump_state **, const uint16_t *zone); int nl_ct_dump_next(struct nl_ct_dump_state *, struct ct_dpif_entry *); int nl_ct_dump_done(struct nl_ct_dump_state *); int nl_ct_flush(void); int nl_ct_flush_zone(uint16_t zone); bool nl_ct_parse_entry(struct ofpbuf *, struct ct_dpif_entry *, enum nl_ct_event_type *); void nl_ct_format_event_entry(const struct ct_dpif_entry *, enum nl_ct_event_type, struct ds *, bool verbose, bool print_stats); #endif /* NETLINK_CONNTRACK_H */ openvswitch-2.5.9/lib/PaxHeaders.82075/csum.h0000644000000000000000000000013213534540071015532 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.689852748 openvswitch-2.5.9/lib/csum.h0000644000175000017500000000407513534540071017226 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2011, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CSUM_H #define CSUM_H 1 #include #include #include "openvswitch/types.h" ovs_be16 csum(const void *, size_t); uint32_t csum_continue(uint32_t partial, const void *, size_t); ovs_be16 csum_finish(uint32_t partial); ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16); ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32); ovs_be16 recalc_csum48(ovs_be16 old_csum, const struct eth_addr old_mac, const struct eth_addr new_mac); ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4], const ovs_be32 new_u32[4]); #ifndef __CHECKER__ /* Adds the 16 bits in 'new' to the partial IP checksum 'partial' and returns * the updated checksum. (To start a new checksum, pass 0 for 'partial'. To * obtain the finished checksum, pass the return value to csum_finish().) */ static inline uint32_t csum_add16(uint32_t partial, ovs_be16 new) { return partial + new; } /* Adds the 32 bits in 'new' to the partial IP checksum 'partial' and returns * the updated checksum. (To start a new checksum, pass 0 for 'partial'. To * obtain the finished checksum, pass the return value to csum_finish().) */ static inline uint32_t csum_add32(uint32_t partial, ovs_be32 new) { return partial + (new >> 16) + (new & 0xffff); } #else uint32_t csum_add16(uint32_t partial, ovs_be16); uint32_t csum_add32(uint32_t partial, ovs_be32); #endif #endif /* csum.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpif-netdev.c0000644000000000000000000000013213534540071016763 xustar0030 mtime=1567801401.361680876 30 atime=1567801402.073686105 30 ctime=1567801424.705852866 openvswitch-2.5.9/lib/dpif-netdev.c0000644000175000017500000040307313534540071020460 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dpif-netdev.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bitmap.h" #include "cmap.h" #include "csum.h" #include "dp-packet.h" #include "dpif.h" #include "dpif-provider.h" #include "dummy.h" #include "dynamic-string.h" #include "fat-rwlock.h" #include "flow.h" #include "cmap.h" #include "coverage.h" #include "latch.h" #include "list.h" #include "match.h" #include "netdev.h" #include "netdev-dpdk.h" #include "netdev-vport.h" #include "netlink.h" #include "odp-execute.h" #include "odp-util.h" #include "ofp-print.h" #include "ofpbuf.h" #include "ovs-numa.h" #include "ovs-rcu.h" #include "packets.h" #include "poll-loop.h" #include "pvector.h" #include "random.h" #include "seq.h" #include "shash.h" #include "sset.h" #include "timeval.h" #include "tnl-neigh-cache.h" #include "tnl-ports.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(dpif_netdev); #define FLOW_DUMP_MAX_BATCH 50 /* Use per thread recirc_depth to prevent recirculation loop. */ #define MAX_RECIRC_DEPTH 5 DEFINE_STATIC_PER_THREAD_DATA(uint32_t, recirc_depth, 0) /* Configuration parameters. */ enum { MAX_FLOWS = 65536 }; /* Maximum number of flows in flow table. */ /* Protects against changes to 'dp_netdevs'. */ static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER; /* Contains all 'struct dp_netdev's. */ static struct shash dp_netdevs OVS_GUARDED_BY(dp_netdev_mutex) = SHASH_INITIALIZER(&dp_netdevs); static struct vlog_rate_limit upcall_rl = VLOG_RATE_LIMIT_INIT(600, 600); static struct odp_support dp_netdev_support = { .max_mpls_depth = SIZE_MAX, .recirc = true, }; /* Stores a miniflow with inline values */ struct netdev_flow_key { uint32_t hash; /* Hash function differs for different users. */ uint32_t len; /* Length of the following miniflow (incl. map). */ struct miniflow mf; uint64_t buf[FLOW_MAX_PACKET_U64S]; }; /* Exact match cache for frequently used flows * * The cache uses a 32-bit hash of the packet (which can be the RSS hash) to * search its entries for a miniflow that matches exactly the miniflow of the * packet. It stores the 'dpcls_rule' (rule) that matches the miniflow. * * A cache entry holds a reference to its 'dp_netdev_flow'. * * A miniflow with a given hash can be in one of EM_FLOW_HASH_SEGS different * entries. The 32-bit hash is split into EM_FLOW_HASH_SEGS values (each of * them is EM_FLOW_HASH_SHIFT bits wide and the remainder is thrown away). Each * value is the index of a cache entry where the miniflow could be. * * * Thread-safety * ============= * * Each pmd_thread has its own private exact match cache. * If dp_netdev_input is not called from a pmd thread, a mutex is used. */ #define EM_FLOW_HASH_SHIFT 13 #define EM_FLOW_HASH_ENTRIES (1u << EM_FLOW_HASH_SHIFT) #define EM_FLOW_HASH_MASK (EM_FLOW_HASH_ENTRIES - 1) #define EM_FLOW_HASH_SEGS 2 struct emc_entry { struct dp_netdev_flow *flow; struct netdev_flow_key key; /* key.hash used for emc hash value. */ }; struct emc_cache { struct emc_entry entries[EM_FLOW_HASH_ENTRIES]; int sweep_idx; /* For emc_cache_slow_sweep(). */ }; /* Iterate in the exact match cache through every entry that might contain a * miniflow with hash 'HASH'. */ #define EMC_FOR_EACH_POS_WITH_HASH(EMC, CURRENT_ENTRY, HASH) \ for (uint32_t i__ = 0, srch_hash__ = (HASH); \ (CURRENT_ENTRY) = &(EMC)->entries[srch_hash__ & EM_FLOW_HASH_MASK], \ i__ < EM_FLOW_HASH_SEGS; \ i__++, srch_hash__ >>= EM_FLOW_HASH_SHIFT) /* Simple non-wildcarding single-priority classifier. */ struct dpcls { struct cmap subtables_map; struct pvector subtables; }; /* A rule to be inserted to the classifier. */ struct dpcls_rule { struct cmap_node cmap_node; /* Within struct dpcls_subtable 'rules'. */ struct netdev_flow_key *mask; /* Subtable's mask. */ struct netdev_flow_key flow; /* Matching key. */ /* 'flow' must be the last field, additional space is allocated here. */ }; static void dpcls_init(struct dpcls *); static void dpcls_destroy(struct dpcls *); static void dpcls_insert(struct dpcls *, struct dpcls_rule *, const struct netdev_flow_key *mask); static void dpcls_remove(struct dpcls *, struct dpcls_rule *); static bool dpcls_lookup(const struct dpcls *cls, const struct netdev_flow_key keys[], struct dpcls_rule **rules, size_t cnt); /* Datapath based on the network device interface from netdev.h. * * * Thread-safety * ============= * * Some members, marked 'const', are immutable. Accessing other members * requires synchronization, as noted in more detail below. * * Acquisition order is, from outermost to innermost: * * dp_netdev_mutex (global) * port_mutex */ struct dp_netdev { const struct dpif_class *const class; const char *const name; struct dpif *dpif; struct ovs_refcount ref_cnt; atomic_flag destroyed; /* Ports. * * Protected by RCU. Take the mutex to add or remove ports. */ struct ovs_mutex port_mutex; struct cmap ports; struct seq *port_seq; /* Incremented whenever a port changes. */ /* Protects access to ofproto-dpif-upcall interface during revalidator * thread synchronization. */ struct fat_rwlock upcall_rwlock; upcall_callback *upcall_cb; /* Callback function for executing upcalls. */ void *upcall_aux; /* Callback function for notifying the purging of dp flows (during * reseting pmd deletion). */ dp_purge_callback *dp_purge_cb; void *dp_purge_aux; /* Stores all 'struct dp_netdev_pmd_thread's. */ struct cmap poll_threads; /* Protects the access of the 'struct dp_netdev_pmd_thread' * instance for non-pmd thread. */ struct ovs_mutex non_pmd_mutex; /* Each pmd thread will store its pointer to * 'struct dp_netdev_pmd_thread' in 'per_pmd_key'. */ ovsthread_key_t per_pmd_key; /* Number of rx queues for each dpdk interface and the cpu mask * for pin of pmd threads. */ size_t n_dpdk_rxqs; char *pmd_cmask; uint64_t last_tnl_conf_seq; }; static struct dp_netdev_port *dp_netdev_lookup_port(const struct dp_netdev *dp, odp_port_t); enum dp_stat_type { DP_STAT_EXACT_HIT, /* Packets that had an exact match (emc). */ DP_STAT_MASKED_HIT, /* Packets that matched in the flow table. */ DP_STAT_MISS, /* Packets that did not match. */ DP_STAT_LOST, /* Packets not passed up to the client. */ DP_N_STATS }; enum pmd_cycles_counter_type { PMD_CYCLES_POLLING, /* Cycles spent polling NICs. */ PMD_CYCLES_PROCESSING, /* Cycles spent processing packets */ PMD_N_CYCLES }; /* A port in a netdev-based datapath. */ struct dp_netdev_port { odp_port_t port_no; struct netdev *netdev; struct cmap_node node; /* Node in dp_netdev's 'ports'. */ struct netdev_saved_flags *sf; struct netdev_rxq **rxq; struct ovs_refcount ref_cnt; char *type; /* Port type as requested by user. */ }; /* Contained by struct dp_netdev_flow's 'stats' member. */ struct dp_netdev_flow_stats { atomic_llong used; /* Last used time, in monotonic msecs. */ atomic_ullong packet_count; /* Number of packets matched. */ atomic_ullong byte_count; /* Number of bytes matched. */ atomic_uint16_t tcp_flags; /* Bitwise-OR of seen tcp_flags values. */ }; /* A flow in 'dp_netdev_pmd_thread's 'flow_table'. * * * Thread-safety * ============= * * Except near the beginning or ending of its lifespan, rule 'rule' belongs to * its pmd thread's classifier. The text below calls this classifier 'cls'. * * Motivation * ---------- * * The thread safety rules described here for "struct dp_netdev_flow" are * motivated by two goals: * * - Prevent threads that read members of "struct dp_netdev_flow" from * reading bad data due to changes by some thread concurrently modifying * those members. * * - Prevent two threads making changes to members of a given "struct * dp_netdev_flow" from interfering with each other. * * * Rules * ----- * * A flow 'flow' may be accessed without a risk of being freed during an RCU * grace period. Code that needs to hold onto a flow for a while * should try incrementing 'flow->ref_cnt' with dp_netdev_flow_ref(). * * 'flow->ref_cnt' protects 'flow' from being freed. It doesn't protect the * flow from being deleted from 'cls' and it doesn't protect members of 'flow' * from modification. * * Some members, marked 'const', are immutable. Accessing other members * requires synchronization, as noted in more detail below. */ struct dp_netdev_flow { const struct flow flow; /* Unmasked flow that created this entry. */ /* Hash table index by unmasked flow. */ const struct cmap_node node; /* In owning dp_netdev_pmd_thread's */ /* 'flow_table'. */ const ovs_u128 ufid; /* Unique flow identifier. */ const unsigned pmd_id; /* The 'core_id' of pmd thread owning this */ /* flow. */ /* Number of references. * The classifier owns one reference. * Any thread trying to keep a rule from being freed should hold its own * reference. */ struct ovs_refcount ref_cnt; bool dead; /* Statistics. */ struct dp_netdev_flow_stats stats; /* Actions. */ OVSRCU_TYPE(struct dp_netdev_actions *) actions; /* While processing a group of input packets, the datapath uses the next * member to store a pointer to the output batch for the flow. It is * reset after the batch has been sent out (See dp_netdev_queue_batches(), * packet_batch_init() and packet_batch_execute()). */ struct packet_batch *batch; /* Packet classification. */ struct dpcls_rule cr; /* In owning dp_netdev's 'cls'. */ /* 'cr' must be the last member. */ }; static void dp_netdev_flow_unref(struct dp_netdev_flow *); static bool dp_netdev_flow_ref(struct dp_netdev_flow *); static int dpif_netdev_flow_from_nlattrs(const struct nlattr *, uint32_t, struct flow *); /* A set of datapath actions within a "struct dp_netdev_flow". * * * Thread-safety * ============= * * A struct dp_netdev_actions 'actions' is protected with RCU. */ struct dp_netdev_actions { /* These members are immutable: they do not change during the struct's * lifetime. */ unsigned int size; /* Size of 'actions', in bytes. */ struct nlattr actions[]; /* Sequence of OVS_ACTION_ATTR_* attributes. */ }; struct dp_netdev_actions *dp_netdev_actions_create(const struct nlattr *, size_t); struct dp_netdev_actions *dp_netdev_flow_get_actions( const struct dp_netdev_flow *); static void dp_netdev_actions_free(struct dp_netdev_actions *); /* Contained by struct dp_netdev_pmd_thread's 'stats' member. */ struct dp_netdev_pmd_stats { /* Indexed by DP_STAT_*. */ atomic_ullong n[DP_N_STATS]; }; /* Contained by struct dp_netdev_pmd_thread's 'cycle' member. */ struct dp_netdev_pmd_cycles { /* Indexed by PMD_CYCLES_*. */ atomic_ullong n[PMD_N_CYCLES]; }; /* Contained by struct dp_netdev_pmd_thread's 'poll_list' member. */ struct rxq_poll { struct dp_netdev_port *port; struct netdev_rxq *rx; struct ovs_list node; }; /* PMD: Poll modes drivers. PMD accesses devices via polling to eliminate * the performance overhead of interrupt processing. Therefore netdev can * not implement rx-wait for these devices. dpif-netdev needs to poll * these device to check for recv buffer. pmd-thread does polling for * devices assigned to itself. * * DPDK used PMD for accessing NIC. * * Note, instance with cpu core id NON_PMD_CORE_ID will be reserved for * I/O of all non-pmd threads. There will be no actual thread created * for the instance. * * Each struct has its own flow table and classifier. Packets received * from managed ports are looked up in the corresponding pmd thread's * flow table, and are executed with the found actions. * */ struct dp_netdev_pmd_thread { struct dp_netdev *dp; struct ovs_refcount ref_cnt; /* Every reference must be refcount'ed. */ struct cmap_node node; /* In 'dp->poll_threads'. */ pthread_cond_t cond; /* For synchronizing pmd thread reload. */ struct ovs_mutex cond_mutex; /* Mutex for condition variable. */ /* Per thread exact-match cache. Note, the instance for cpu core * NON_PMD_CORE_ID can be accessed by multiple threads, and thusly * need to be protected (e.g. by 'dp_netdev_mutex'). All other * instances will only be accessed by its own pmd thread. */ struct emc_cache flow_cache; /* Classifier and Flow-Table. * * Writers of 'flow_table' must take the 'flow_mutex'. Corresponding * changes to 'cls' must be made while still holding the 'flow_mutex'. */ struct ovs_mutex flow_mutex; struct dpcls cls; struct cmap flow_table OVS_GUARDED; /* Flow table. */ /* Statistics. */ struct dp_netdev_pmd_stats stats; /* Cycles counters */ struct dp_netdev_pmd_cycles cycles; /* Used to count cicles. See 'cycles_counter_end()' */ unsigned long long last_cycles; struct latch exit_latch; /* For terminating the pmd thread. */ atomic_uint change_seq; /* For reloading pmd ports. */ pthread_t thread; int index; /* Idx of this pmd thread among pmd*/ /* threads on same numa node. */ unsigned core_id; /* CPU core id of this pmd thread. */ int numa_id; /* numa node id of this pmd thread. */ atomic_int tx_qid; /* Queue id used by this pmd thread to * send packets on all netdevs */ struct ovs_mutex poll_mutex; /* Mutex for poll_list. */ /* List of rx queues to poll. */ struct ovs_list poll_list OVS_GUARDED; int poll_cnt; /* Number of elemints in poll_list. */ /* Only a pmd thread can write on its own 'cycles' and 'stats'. * The main thread keeps 'stats_zero' and 'cycles_zero' as base * values and subtracts them from 'stats' and 'cycles' before * reporting to the user */ unsigned long long stats_zero[DP_N_STATS]; uint64_t cycles_zero[PMD_N_CYCLES]; }; #define PMD_INITIAL_SEQ 1 /* Interface to netdev-based datapath. */ struct dpif_netdev { struct dpif dpif; struct dp_netdev *dp; uint64_t last_port_seq; }; static int get_port_by_number(struct dp_netdev *dp, odp_port_t port_no, struct dp_netdev_port **portp); static int get_port_by_name(struct dp_netdev *dp, const char *devname, struct dp_netdev_port **portp); static void dp_netdev_free(struct dp_netdev *) OVS_REQUIRES(dp_netdev_mutex); static int do_add_port(struct dp_netdev *dp, const char *devname, const char *type, odp_port_t port_no) OVS_REQUIRES(dp->port_mutex); static void do_del_port(struct dp_netdev *dp, struct dp_netdev_port *) OVS_REQUIRES(dp->port_mutex); static int dpif_netdev_open(const struct dpif_class *, const char *name, bool create, struct dpif **); static void dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd, struct dp_packet **, int c, bool may_steal, const struct nlattr *actions, size_t actions_len); static void dp_netdev_input(struct dp_netdev_pmd_thread *, struct dp_packet **, int cnt, odp_port_t port_no); static void dp_netdev_recirculate(struct dp_netdev_pmd_thread *, struct dp_packet **, int cnt); static void dp_netdev_disable_upcall(struct dp_netdev *); static void dp_netdev_pmd_reload_done(struct dp_netdev_pmd_thread *pmd); static void dp_netdev_configure_pmd(struct dp_netdev_pmd_thread *pmd, struct dp_netdev *dp, int index, unsigned core_id, int numa_id); static void dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd); static void dp_netdev_set_nonpmd(struct dp_netdev *dp); static struct dp_netdev_pmd_thread *dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id); static struct dp_netdev_pmd_thread * dp_netdev_pmd_get_next(struct dp_netdev *dp, struct cmap_position *pos); static void dp_netdev_destroy_all_pmds(struct dp_netdev *dp); static void dp_netdev_del_pmds_on_numa(struct dp_netdev *dp, int numa_id); static void dp_netdev_set_pmds_on_numa(struct dp_netdev *dp, int numa_id); static void dp_netdev_add_rxq_to_pmd(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_port *port, struct netdev_rxq *rx); static struct dp_netdev_pmd_thread * dp_netdev_less_loaded_pmd_on_numa(struct dp_netdev *dp, int numa_id); static void dp_netdev_reset_pmd_threads(struct dp_netdev *dp); static bool dp_netdev_pmd_try_ref(struct dp_netdev_pmd_thread *pmd); static void dp_netdev_pmd_unref(struct dp_netdev_pmd_thread *pmd); static void dp_netdev_pmd_flow_flush(struct dp_netdev_pmd_thread *pmd); static inline bool emc_entry_alive(struct emc_entry *ce); static void emc_clear_entry(struct emc_entry *ce); static void emc_cache_init(struct emc_cache *flow_cache) { int i; flow_cache->sweep_idx = 0; for (i = 0; i < ARRAY_SIZE(flow_cache->entries); i++) { flow_cache->entries[i].flow = NULL; flow_cache->entries[i].key.hash = 0; flow_cache->entries[i].key.len = sizeof(struct miniflow); flowmap_init(&flow_cache->entries[i].key.mf.map); } } static void emc_cache_uninit(struct emc_cache *flow_cache) { int i; for (i = 0; i < ARRAY_SIZE(flow_cache->entries); i++) { emc_clear_entry(&flow_cache->entries[i]); } } /* Check and clear dead flow references slowly (one entry at each * invocation). */ static void emc_cache_slow_sweep(struct emc_cache *flow_cache) { struct emc_entry *entry = &flow_cache->entries[flow_cache->sweep_idx]; if (!emc_entry_alive(entry)) { emc_clear_entry(entry); } flow_cache->sweep_idx = (flow_cache->sweep_idx + 1) & EM_FLOW_HASH_MASK; } /* Returns true if 'dpif' is a netdev or dummy dpif, false otherwise. */ bool dpif_is_netdev(const struct dpif *dpif) { return dpif->dpif_class->open == dpif_netdev_open; } static struct dpif_netdev * dpif_netdev_cast(const struct dpif *dpif) { ovs_assert(dpif_is_netdev(dpif)); return CONTAINER_OF(dpif, struct dpif_netdev, dpif); } static struct dp_netdev * get_dp_netdev(const struct dpif *dpif) { return dpif_netdev_cast(dpif)->dp; } enum pmd_info_type { PMD_INFO_SHOW_STATS, /* Show how cpu cycles are spent. */ PMD_INFO_CLEAR_STATS, /* Set the cycles count to 0. */ PMD_INFO_SHOW_RXQ /* Show poll-lists of pmd threads. */ }; static void pmd_info_show_stats(struct ds *reply, struct dp_netdev_pmd_thread *pmd, unsigned long long stats[DP_N_STATS], uint64_t cycles[PMD_N_CYCLES]) { unsigned long long total_packets = 0; uint64_t total_cycles = 0; int i; /* These loops subtracts reference values ('*_zero') from the counters. * Since loads and stores are relaxed, it might be possible for a '*_zero' * value to be more recent than the current value we're reading from the * counter. This is not a big problem, since these numbers are not * supposed to be too accurate, but we should at least make sure that * the result is not negative. */ for (i = 0; i < DP_N_STATS; i++) { if (stats[i] > pmd->stats_zero[i]) { stats[i] -= pmd->stats_zero[i]; } else { stats[i] = 0; } if (i != DP_STAT_LOST) { /* Lost packets are already included in DP_STAT_MISS */ total_packets += stats[i]; } } for (i = 0; i < PMD_N_CYCLES; i++) { if (cycles[i] > pmd->cycles_zero[i]) { cycles[i] -= pmd->cycles_zero[i]; } else { cycles[i] = 0; } total_cycles += cycles[i]; } ds_put_cstr(reply, (pmd->core_id == NON_PMD_CORE_ID) ? "main thread" : "pmd thread"); if (pmd->numa_id != OVS_NUMA_UNSPEC) { ds_put_format(reply, " numa_id %d", pmd->numa_id); } if (pmd->core_id != OVS_CORE_UNSPEC && pmd->core_id != NON_PMD_CORE_ID) { ds_put_format(reply, " core_id %u", pmd->core_id); } ds_put_cstr(reply, ":\n"); ds_put_format(reply, "\temc hits:%llu\n\tmegaflow hits:%llu\n" "\tmiss:%llu\n\tlost:%llu\n", stats[DP_STAT_EXACT_HIT], stats[DP_STAT_MASKED_HIT], stats[DP_STAT_MISS], stats[DP_STAT_LOST]); if (total_cycles == 0) { return; } ds_put_format(reply, "\tpolling cycles:%"PRIu64" (%.02f%%)\n" "\tprocessing cycles:%"PRIu64" (%.02f%%)\n", cycles[PMD_CYCLES_POLLING], cycles[PMD_CYCLES_POLLING] / (double)total_cycles * 100, cycles[PMD_CYCLES_PROCESSING], cycles[PMD_CYCLES_PROCESSING] / (double)total_cycles * 100); if (total_packets == 0) { return; } ds_put_format(reply, "\tavg cycles per packet: %.02f (%"PRIu64"/%llu)\n", total_cycles / (double)total_packets, total_cycles, total_packets); ds_put_format(reply, "\tavg processing cycles per packet: " "%.02f (%"PRIu64"/%llu)\n", cycles[PMD_CYCLES_PROCESSING] / (double)total_packets, cycles[PMD_CYCLES_PROCESSING], total_packets); } static void pmd_info_clear_stats(struct ds *reply OVS_UNUSED, struct dp_netdev_pmd_thread *pmd, unsigned long long stats[DP_N_STATS], uint64_t cycles[PMD_N_CYCLES]) { int i; /* We cannot write 'stats' and 'cycles' (because they're written by other * threads) and we shouldn't change 'stats' (because they're used to count * datapath stats, which must not be cleared here). Instead, we save the * current values and subtract them from the values to be displayed in the * future */ for (i = 0; i < DP_N_STATS; i++) { pmd->stats_zero[i] = stats[i]; } for (i = 0; i < PMD_N_CYCLES; i++) { pmd->cycles_zero[i] = cycles[i]; } } static void pmd_info_show_rxq(struct ds *reply, struct dp_netdev_pmd_thread *pmd) { if (pmd->core_id != NON_PMD_CORE_ID) { struct rxq_poll *poll; const char *prev_name = NULL; ds_put_format(reply, "pmd thread numa_id %d core_id %u:\n", pmd->numa_id, pmd->core_id); ovs_mutex_lock(&pmd->poll_mutex); LIST_FOR_EACH (poll, node, &pmd->poll_list) { const char *name = netdev_get_name(poll->port->netdev); if (!prev_name || strcmp(name, prev_name)) { if (prev_name) { ds_put_cstr(reply, "\n"); } ds_put_format(reply, "\tport: %s\tqueue-id:", netdev_get_name(poll->port->netdev)); } ds_put_format(reply, " %d", netdev_rxq_get_queue_id(poll->rx)); prev_name = name; } ovs_mutex_unlock(&pmd->poll_mutex); ds_put_cstr(reply, "\n"); } } static void dpif_netdev_pmd_info(struct unixctl_conn *conn, int argc, const char *argv[], void *aux) { struct ds reply = DS_EMPTY_INITIALIZER; struct dp_netdev_pmd_thread *pmd; struct dp_netdev *dp = NULL; enum pmd_info_type type = *(enum pmd_info_type *) aux; ovs_mutex_lock(&dp_netdev_mutex); if (argc == 2) { dp = shash_find_data(&dp_netdevs, argv[1]); } else if (shash_count(&dp_netdevs) == 1) { /* There's only one datapath */ dp = shash_first(&dp_netdevs)->data; } if (!dp) { ovs_mutex_unlock(&dp_netdev_mutex); unixctl_command_reply_error(conn, "please specify an existing datapath"); return; } CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { if (type == PMD_INFO_SHOW_RXQ) { pmd_info_show_rxq(&reply, pmd); } else { unsigned long long stats[DP_N_STATS]; uint64_t cycles[PMD_N_CYCLES]; int i; /* Read current stats and cycle counters */ for (i = 0; i < ARRAY_SIZE(stats); i++) { atomic_read_relaxed(&pmd->stats.n[i], &stats[i]); } for (i = 0; i < ARRAY_SIZE(cycles); i++) { atomic_read_relaxed(&pmd->cycles.n[i], &cycles[i]); } if (type == PMD_INFO_CLEAR_STATS) { pmd_info_clear_stats(&reply, pmd, stats, cycles); } else if (type == PMD_INFO_SHOW_STATS) { pmd_info_show_stats(&reply, pmd, stats, cycles); } } } ovs_mutex_unlock(&dp_netdev_mutex); unixctl_command_reply(conn, ds_cstr(&reply)); ds_destroy(&reply); } static int dpif_netdev_init(void) { static enum pmd_info_type show_aux = PMD_INFO_SHOW_STATS, clear_aux = PMD_INFO_CLEAR_STATS, poll_aux = PMD_INFO_SHOW_RXQ; unixctl_command_register("dpif-netdev/pmd-stats-show", "[dp]", 0, 1, dpif_netdev_pmd_info, (void *)&show_aux); unixctl_command_register("dpif-netdev/pmd-stats-clear", "[dp]", 0, 1, dpif_netdev_pmd_info, (void *)&clear_aux); unixctl_command_register("dpif-netdev/pmd-rxq-show", "[dp]", 0, 1, dpif_netdev_pmd_info, (void *)&poll_aux); return 0; } static int dpif_netdev_enumerate(struct sset *all_dps, const struct dpif_class *dpif_class) { struct shash_node *node; ovs_mutex_lock(&dp_netdev_mutex); SHASH_FOR_EACH(node, &dp_netdevs) { struct dp_netdev *dp = node->data; if (dpif_class != dp->class) { /* 'dp_netdevs' contains both "netdev" and "dummy" dpifs. * If the class doesn't match, skip this dpif. */ continue; } sset_add(all_dps, node->name); } ovs_mutex_unlock(&dp_netdev_mutex); return 0; } static bool dpif_netdev_class_is_dummy(const struct dpif_class *class) { return class != &dpif_netdev_class; } static const char * dpif_netdev_port_open_type(const struct dpif_class *class, const char *type) { return strcmp(type, "internal") ? type : dpif_netdev_class_is_dummy(class) ? "dummy" : "tap"; } static struct dpif * create_dpif_netdev(struct dp_netdev *dp) { uint16_t netflow_id = hash_string(dp->name, 0); struct dpif_netdev *dpif; ovs_refcount_ref(&dp->ref_cnt); dpif = xmalloc(sizeof *dpif); dpif_init(&dpif->dpif, dp->class, dp->name, netflow_id >> 8, netflow_id); dpif->dp = dp; dpif->last_port_seq = seq_read(dp->port_seq); return &dpif->dpif; } /* Choose an unused, non-zero port number and return it on success. * Return ODPP_NONE on failure. */ static odp_port_t choose_port(struct dp_netdev *dp, const char *name) OVS_REQUIRES(dp->port_mutex) { uint32_t port_no; if (dp->class != &dpif_netdev_class) { const char *p; int start_no = 0; /* If the port name begins with "br", start the number search at * 100 to make writing tests easier. */ if (!strncmp(name, "br", 2)) { start_no = 100; } /* If the port name contains a number, try to assign that port number. * This can make writing unit tests easier because port numbers are * predictable. */ for (p = name; *p != '\0'; p++) { if (isdigit((unsigned char) *p)) { port_no = start_no + strtol(p, NULL, 10); if (port_no > 0 && port_no != odp_to_u32(ODPP_NONE) && !dp_netdev_lookup_port(dp, u32_to_odp(port_no))) { return u32_to_odp(port_no); } break; } } } for (port_no = 1; port_no <= UINT16_MAX; port_no++) { if (!dp_netdev_lookup_port(dp, u32_to_odp(port_no))) { return u32_to_odp(port_no); } } return ODPP_NONE; } static int create_dp_netdev(const char *name, const struct dpif_class *class, struct dp_netdev **dpp) OVS_REQUIRES(dp_netdev_mutex) { struct dp_netdev *dp; int error; dp = xzalloc(sizeof *dp); shash_add(&dp_netdevs, name, dp); *CONST_CAST(const struct dpif_class **, &dp->class) = class; *CONST_CAST(const char **, &dp->name) = xstrdup(name); ovs_refcount_init(&dp->ref_cnt); atomic_flag_clear(&dp->destroyed); ovs_mutex_init(&dp->port_mutex); cmap_init(&dp->ports); dp->port_seq = seq_create(); fat_rwlock_init(&dp->upcall_rwlock); /* Disable upcalls by default. */ dp_netdev_disable_upcall(dp); dp->upcall_aux = NULL; dp->upcall_cb = NULL; cmap_init(&dp->poll_threads); ovs_mutex_init_recursive(&dp->non_pmd_mutex); ovsthread_key_create(&dp->per_pmd_key, NULL); dp_netdev_set_nonpmd(dp); dp->n_dpdk_rxqs = NR_QUEUE; ovs_mutex_lock(&dp->port_mutex); error = do_add_port(dp, name, "internal", ODPP_LOCAL); ovs_mutex_unlock(&dp->port_mutex); if (error) { dp_netdev_free(dp); return error; } dp->last_tnl_conf_seq = seq_read(tnl_conf_seq); *dpp = dp; return 0; } static int dpif_netdev_open(const struct dpif_class *class, const char *name, bool create, struct dpif **dpifp) { struct dp_netdev *dp; int error; ovs_mutex_lock(&dp_netdev_mutex); dp = shash_find_data(&dp_netdevs, name); if (!dp) { error = create ? create_dp_netdev(name, class, &dp) : ENODEV; } else { error = (dp->class != class ? EINVAL : create ? EEXIST : 0); } if (!error) { *dpifp = create_dpif_netdev(dp); dp->dpif = *dpifp; } ovs_mutex_unlock(&dp_netdev_mutex); return error; } static void dp_netdev_destroy_upcall_lock(struct dp_netdev *dp) OVS_NO_THREAD_SAFETY_ANALYSIS { /* Check that upcalls are disabled, i.e. that the rwlock is taken */ ovs_assert(fat_rwlock_tryrdlock(&dp->upcall_rwlock)); /* Before freeing a lock we should release it */ fat_rwlock_unlock(&dp->upcall_rwlock); fat_rwlock_destroy(&dp->upcall_rwlock); } /* Requires dp_netdev_mutex so that we can't get a new reference to 'dp' * through the 'dp_netdevs' shash while freeing 'dp'. */ static void dp_netdev_free(struct dp_netdev *dp) OVS_REQUIRES(dp_netdev_mutex) { struct dp_netdev_port *port; shash_find_and_delete(&dp_netdevs, dp->name); dp_netdev_destroy_all_pmds(dp); ovs_mutex_destroy(&dp->non_pmd_mutex); ovsthread_key_delete(dp->per_pmd_key); ovs_mutex_lock(&dp->port_mutex); CMAP_FOR_EACH (port, node, &dp->ports) { /* PMD threads are destroyed here. do_del_port() cannot quiesce */ do_del_port(dp, port); } ovs_mutex_unlock(&dp->port_mutex); cmap_destroy(&dp->poll_threads); seq_destroy(dp->port_seq); cmap_destroy(&dp->ports); /* Upcalls must be disabled at this point */ dp_netdev_destroy_upcall_lock(dp); free(dp->pmd_cmask); free(CONST_CAST(char *, dp->name)); free(dp); } static void dp_netdev_unref(struct dp_netdev *dp) { if (dp) { /* Take dp_netdev_mutex so that, if dp->ref_cnt falls to zero, we can't * get a new reference to 'dp' through the 'dp_netdevs' shash. */ ovs_mutex_lock(&dp_netdev_mutex); if (ovs_refcount_unref_relaxed(&dp->ref_cnt) == 1) { dp_netdev_free(dp); } ovs_mutex_unlock(&dp_netdev_mutex); } } static void dpif_netdev_close(struct dpif *dpif) { struct dp_netdev *dp = get_dp_netdev(dpif); dp_netdev_unref(dp); free(dpif); } static int dpif_netdev_destroy(struct dpif *dpif) { struct dp_netdev *dp = get_dp_netdev(dpif); if (!atomic_flag_test_and_set(&dp->destroyed)) { if (ovs_refcount_unref_relaxed(&dp->ref_cnt) == 1) { /* Can't happen: 'dpif' still owns a reference to 'dp'. */ OVS_NOT_REACHED(); } } return 0; } /* Add 'n' to the atomic variable 'var' non-atomically and using relaxed * load/store semantics. While the increment is not atomic, the load and * store operations are, making it impossible to read inconsistent values. * * This is used to update thread local stats counters. */ static void non_atomic_ullong_add(atomic_ullong *var, unsigned long long n) { unsigned long long tmp; atomic_read_relaxed(var, &tmp); tmp += n; atomic_store_relaxed(var, tmp); } static int dpif_netdev_get_stats(const struct dpif *dpif, struct dpif_dp_stats *stats) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_pmd_thread *pmd; stats->n_flows = stats->n_hit = stats->n_missed = stats->n_lost = 0; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { unsigned long long n; stats->n_flows += cmap_count(&pmd->flow_table); atomic_read_relaxed(&pmd->stats.n[DP_STAT_MASKED_HIT], &n); stats->n_hit += n; atomic_read_relaxed(&pmd->stats.n[DP_STAT_EXACT_HIT], &n); stats->n_hit += n; atomic_read_relaxed(&pmd->stats.n[DP_STAT_MISS], &n); stats->n_missed += n; atomic_read_relaxed(&pmd->stats.n[DP_STAT_LOST], &n); stats->n_lost += n; } stats->n_masks = UINT32_MAX; stats->n_mask_hit = UINT64_MAX; return 0; } static void dp_netdev_reload_pmd__(struct dp_netdev_pmd_thread *pmd) { int old_seq; if (pmd->core_id == NON_PMD_CORE_ID) { return; } ovs_mutex_lock(&pmd->cond_mutex); atomic_add_relaxed(&pmd->change_seq, 1, &old_seq); ovs_mutex_cond_wait(&pmd->cond, &pmd->cond_mutex); ovs_mutex_unlock(&pmd->cond_mutex); } static uint32_t hash_port_no(odp_port_t port_no) { return hash_int(odp_to_u32(port_no), 0); } static int do_add_port(struct dp_netdev *dp, const char *devname, const char *type, odp_port_t port_no) OVS_REQUIRES(dp->port_mutex) { struct netdev_saved_flags *sf; struct dp_netdev_port *port; struct netdev *netdev; enum netdev_flags flags; const char *open_type; int error; int i; /* Reject devices already in 'dp'. */ if (!get_port_by_name(dp, devname, &port)) { return EEXIST; } /* Open and validate network device. */ open_type = dpif_netdev_port_open_type(dp->class, type); error = netdev_open(devname, open_type, &netdev); if (error) { return error; } /* XXX reject non-Ethernet devices */ netdev_get_flags(netdev, &flags); if (flags & NETDEV_LOOPBACK) { VLOG_ERR("%s: cannot add a loopback device", devname); netdev_close(netdev); return EINVAL; } if (netdev_is_pmd(netdev)) { int n_cores = ovs_numa_get_n_cores(); if (n_cores == OVS_CORE_UNSPEC) { VLOG_ERR("%s, cannot get cpu core info", devname); return ENOENT; } /* There can only be ovs_numa_get_n_cores() pmd threads, * so creates a txq for each, and one extra for the non * pmd threads. */ error = netdev_set_multiq(netdev, n_cores + 1, dp->n_dpdk_rxqs); if (error && (error != EOPNOTSUPP)) { VLOG_ERR("%s, cannot set multiq", devname); return errno; } } port = xzalloc(sizeof *port); port->port_no = port_no; port->netdev = netdev; port->rxq = xmalloc(sizeof *port->rxq * netdev_n_rxq(netdev)); port->type = xstrdup(type); for (i = 0; i < netdev_n_rxq(netdev); i++) { error = netdev_rxq_open(netdev, &port->rxq[i], i); if (error && !(error == EOPNOTSUPP && dpif_netdev_class_is_dummy(dp->class))) { VLOG_ERR("%s: cannot receive packets on this network device (%s)", devname, ovs_strerror(errno)); netdev_close(netdev); free(port->type); free(port->rxq); free(port); return error; } } error = netdev_turn_flags_on(netdev, NETDEV_PROMISC, &sf); if (error) { for (i = 0; i < netdev_n_rxq(netdev); i++) { netdev_rxq_close(port->rxq[i]); } netdev_close(netdev); free(port->type); free(port->rxq); free(port); return error; } port->sf = sf; ovs_refcount_init(&port->ref_cnt); cmap_insert(&dp->ports, &port->node, hash_port_no(port_no)); if (netdev_is_pmd(netdev)) { int numa_id = netdev_get_numa_id(netdev); struct dp_netdev_pmd_thread *pmd; /* Cannot create pmd threads for invalid numa node. */ ovs_assert(ovs_numa_numa_id_is_valid(numa_id)); for (i = 0; i < netdev_n_rxq(netdev); i++) { pmd = dp_netdev_less_loaded_pmd_on_numa(dp, numa_id); if (!pmd) { /* There is no pmd threads on this numa node. */ dp_netdev_set_pmds_on_numa(dp, numa_id); /* Assigning of rx queues done. */ break; } ovs_mutex_lock(&pmd->poll_mutex); dp_netdev_add_rxq_to_pmd(pmd, port, port->rxq[i]); ovs_mutex_unlock(&pmd->poll_mutex); dp_netdev_reload_pmd__(pmd); } } seq_change(dp->port_seq); return 0; } static int dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) { struct dp_netdev *dp = get_dp_netdev(dpif); char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dpif_port; odp_port_t port_no; int error; ovs_mutex_lock(&dp->port_mutex); dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); if (*port_nop != ODPP_NONE) { port_no = *port_nop; error = dp_netdev_lookup_port(dp, *port_nop) ? EBUSY : 0; } else { port_no = choose_port(dp, dpif_port); error = port_no == ODPP_NONE ? EFBIG : 0; } if (!error) { *port_nop = port_no; error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no); } ovs_mutex_unlock(&dp->port_mutex); return error; } static int dpif_netdev_port_del(struct dpif *dpif, odp_port_t port_no) { struct dp_netdev *dp = get_dp_netdev(dpif); int error; ovs_mutex_lock(&dp->port_mutex); if (port_no == ODPP_LOCAL) { error = EINVAL; } else { struct dp_netdev_port *port; error = get_port_by_number(dp, port_no, &port); if (!error) { do_del_port(dp, port); } } ovs_mutex_unlock(&dp->port_mutex); return error; } static bool is_valid_port_number(odp_port_t port_no) { return port_no != ODPP_NONE; } static struct dp_netdev_port * dp_netdev_lookup_port(const struct dp_netdev *dp, odp_port_t port_no) { struct dp_netdev_port *port; CMAP_FOR_EACH_WITH_HASH (port, node, hash_port_no(port_no), &dp->ports) { if (port->port_no == port_no) { return port; } } return NULL; } static int get_port_by_number(struct dp_netdev *dp, odp_port_t port_no, struct dp_netdev_port **portp) { if (!is_valid_port_number(port_no)) { *portp = NULL; return EINVAL; } else { *portp = dp_netdev_lookup_port(dp, port_no); return *portp ? 0 : ENOENT; } } static void port_ref(struct dp_netdev_port *port) { if (port) { ovs_refcount_ref(&port->ref_cnt); } } static void port_unref(struct dp_netdev_port *port) { if (port && ovs_refcount_unref_relaxed(&port->ref_cnt) == 1) { int n_rxq = netdev_n_rxq(port->netdev); int i; netdev_close(port->netdev); netdev_restore_flags(port->sf); for (i = 0; i < n_rxq; i++) { netdev_rxq_close(port->rxq[i]); } free(port->rxq); free(port->type); free(port); } } static int get_port_by_name(struct dp_netdev *dp, const char *devname, struct dp_netdev_port **portp) OVS_REQUIRES(dp->port_mutex) { struct dp_netdev_port *port; CMAP_FOR_EACH (port, node, &dp->ports) { if (!strcmp(netdev_get_name(port->netdev), devname)) { *portp = port; return 0; } } return ENOENT; } static int get_n_pmd_threads(struct dp_netdev *dp) { /* There is one non pmd thread in dp->poll_threads */ return cmap_count(&dp->poll_threads) - 1; } static int get_n_pmd_threads_on_numa(struct dp_netdev *dp, int numa_id) { struct dp_netdev_pmd_thread *pmd; int n_pmds = 0; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { if (pmd->numa_id == numa_id) { n_pmds++; } } return n_pmds; } /* Returns 'true' if there is a port with pmd netdev and the netdev * is on numa node 'numa_id'. */ static bool has_pmd_port_for_numa(struct dp_netdev *dp, int numa_id) { struct dp_netdev_port *port; CMAP_FOR_EACH (port, node, &dp->ports) { if (netdev_is_pmd(port->netdev) && netdev_get_numa_id(port->netdev) == numa_id) { return true; } } return false; } static void do_del_port(struct dp_netdev *dp, struct dp_netdev_port *port) OVS_REQUIRES(dp->port_mutex) { cmap_remove(&dp->ports, &port->node, hash_odp_port(port->port_no)); seq_change(dp->port_seq); if (netdev_is_pmd(port->netdev)) { int numa_id = netdev_get_numa_id(port->netdev); /* PMD threads can not be on invalid numa node. */ ovs_assert(ovs_numa_numa_id_is_valid(numa_id)); /* If there is no netdev on the numa node, deletes the pmd threads * for that numa. Else, deletes the queues from polling lists. */ if (!has_pmd_port_for_numa(dp, numa_id)) { dp_netdev_del_pmds_on_numa(dp, numa_id); } else { struct dp_netdev_pmd_thread *pmd; struct rxq_poll *poll, *next; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { if (pmd->numa_id == numa_id) { bool found = false; ovs_mutex_lock(&pmd->poll_mutex); LIST_FOR_EACH_SAFE (poll, next, node, &pmd->poll_list) { if (poll->port == port) { found = true; port_unref(poll->port); list_remove(&poll->node); pmd->poll_cnt--; free(poll); } } ovs_mutex_unlock(&pmd->poll_mutex); if (found) { dp_netdev_reload_pmd__(pmd); } } } } } port_unref(port); } static void answer_port_query(const struct dp_netdev_port *port, struct dpif_port *dpif_port) { dpif_port->name = xstrdup(netdev_get_name(port->netdev)); dpif_port->type = xstrdup(port->type); dpif_port->port_no = port->port_no; } static int dpif_netdev_port_query_by_number(const struct dpif *dpif, odp_port_t port_no, struct dpif_port *dpif_port) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_port *port; int error; error = get_port_by_number(dp, port_no, &port); if (!error && dpif_port) { answer_port_query(port, dpif_port); } return error; } static int dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname, struct dpif_port *dpif_port) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_port *port; int error; ovs_mutex_lock(&dp->port_mutex); error = get_port_by_name(dp, devname, &port); if (!error && dpif_port) { answer_port_query(port, dpif_port); } ovs_mutex_unlock(&dp->port_mutex); return error; } static void dp_netdev_flow_free(struct dp_netdev_flow *flow) { dp_netdev_actions_free(dp_netdev_flow_get_actions(flow)); free(flow); } static void dp_netdev_flow_unref(struct dp_netdev_flow *flow) { if (ovs_refcount_unref_relaxed(&flow->ref_cnt) == 1) { ovsrcu_postpone(dp_netdev_flow_free, flow); } } static uint32_t dp_netdev_flow_hash(const ovs_u128 *ufid) { return ufid->u32[0]; } static void dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_flow *flow) OVS_REQUIRES(pmd->flow_mutex) { struct cmap_node *node = CONST_CAST(struct cmap_node *, &flow->node); dpcls_remove(&pmd->cls, &flow->cr); cmap_remove(&pmd->flow_table, node, dp_netdev_flow_hash(&flow->ufid)); flow->dead = true; dp_netdev_flow_unref(flow); } static void dp_netdev_pmd_flow_flush(struct dp_netdev_pmd_thread *pmd) { struct dp_netdev_flow *netdev_flow; ovs_mutex_lock(&pmd->flow_mutex); CMAP_FOR_EACH (netdev_flow, node, &pmd->flow_table) { dp_netdev_pmd_remove_flow(pmd, netdev_flow); } ovs_mutex_unlock(&pmd->flow_mutex); } static int dpif_netdev_flow_flush(struct dpif *dpif) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_pmd_thread *pmd; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { dp_netdev_pmd_flow_flush(pmd); } return 0; } struct dp_netdev_port_state { struct cmap_position position; char *name; }; static int dpif_netdev_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep) { *statep = xzalloc(sizeof(struct dp_netdev_port_state)); return 0; } static int dpif_netdev_port_dump_next(const struct dpif *dpif, void *state_, struct dpif_port *dpif_port) { struct dp_netdev_port_state *state = state_; struct dp_netdev *dp = get_dp_netdev(dpif); struct cmap_node *node; int retval; node = cmap_next_position(&dp->ports, &state->position); if (node) { struct dp_netdev_port *port; port = CONTAINER_OF(node, struct dp_netdev_port, node); free(state->name); state->name = xstrdup(netdev_get_name(port->netdev)); dpif_port->name = state->name; dpif_port->type = port->type; dpif_port->port_no = port->port_no; retval = 0; } else { retval = EOF; } return retval; } static int dpif_netdev_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_) { struct dp_netdev_port_state *state = state_; free(state->name); free(state); return 0; } static int dpif_netdev_port_poll(const struct dpif *dpif_, char **devnamep OVS_UNUSED) { struct dpif_netdev *dpif = dpif_netdev_cast(dpif_); uint64_t new_port_seq; int error; new_port_seq = seq_read(dpif->dp->port_seq); if (dpif->last_port_seq != new_port_seq) { dpif->last_port_seq = new_port_seq; error = ENOBUFS; } else { error = EAGAIN; } return error; } static void dpif_netdev_port_poll_wait(const struct dpif *dpif_) { struct dpif_netdev *dpif = dpif_netdev_cast(dpif_); seq_wait(dpif->dp->port_seq, dpif->last_port_seq); } static struct dp_netdev_flow * dp_netdev_flow_cast(const struct dpcls_rule *cr) { return cr ? CONTAINER_OF(cr, struct dp_netdev_flow, cr) : NULL; } static bool dp_netdev_flow_ref(struct dp_netdev_flow *flow) { return ovs_refcount_try_ref_rcu(&flow->ref_cnt); } /* netdev_flow_key utilities. * * netdev_flow_key is basically a miniflow. We use these functions * (netdev_flow_key_clone, netdev_flow_key_equal, ...) instead of the miniflow * functions (miniflow_clone_inline, miniflow_equal, ...), because: * * - Since we are dealing exclusively with miniflows created by * miniflow_extract(), if the map is different the miniflow is different. * Therefore we can be faster by comparing the map and the miniflow in a * single memcmp(). * - These functions can be inlined by the compiler. */ /* Given the number of bits set in miniflow's maps, returns the size of the * 'netdev_flow_key.mf' */ static inline size_t netdev_flow_key_size(size_t flow_u64s) { return sizeof(struct miniflow) + MINIFLOW_VALUES_SIZE(flow_u64s); } static inline bool netdev_flow_key_equal(const struct netdev_flow_key *a, const struct netdev_flow_key *b) { /* 'b->len' may be not set yet. */ return a->hash == b->hash && !memcmp(&a->mf, &b->mf, a->len); } /* Used to compare 'netdev_flow_key' in the exact match cache to a miniflow. * The maps are compared bitwise, so both 'key->mf' 'mf' must have been * generated by miniflow_extract. */ static inline bool netdev_flow_key_equal_mf(const struct netdev_flow_key *key, const struct miniflow *mf) { return !memcmp(&key->mf, mf, key->len); } static inline void netdev_flow_key_clone(struct netdev_flow_key *dst, const struct netdev_flow_key *src) { memcpy(dst, src, offsetof(struct netdev_flow_key, mf) + src->len); } /* Slow. */ static void netdev_flow_key_from_flow(struct netdev_flow_key *dst, const struct flow *src) { struct dp_packet packet; uint64_t buf_stub[512 / 8]; dp_packet_use_stub(&packet, buf_stub, sizeof buf_stub); pkt_metadata_from_flow(&packet.md, src); flow_compose(&packet, src); miniflow_extract(&packet, &dst->mf); dp_packet_uninit(&packet); dst->len = netdev_flow_key_size(miniflow_n_values(&dst->mf)); dst->hash = 0; /* Not computed yet. */ } /* Initialize a netdev_flow_key 'mask' from 'match'. */ static inline void netdev_flow_mask_init(struct netdev_flow_key *mask, const struct match *match) { uint64_t *dst = miniflow_values(&mask->mf); struct flowmap fmap; uint32_t hash = 0; size_t idx; /* Only check masks that make sense for the flow. */ flow_wc_map(&match->flow, &fmap); flowmap_init(&mask->mf.map); FLOWMAP_FOR_EACH_INDEX(idx, fmap) { uint64_t mask_u64 = flow_u64_value(&match->wc.masks, idx); if (mask_u64) { flowmap_set(&mask->mf.map, idx, 1); *dst++ = mask_u64; hash = hash_add64(hash, mask_u64); } } map_t map; FLOWMAP_FOR_EACH_MAP (map, mask->mf.map) { hash = hash_add64(hash, map); } size_t n = dst - miniflow_get_values(&mask->mf); mask->hash = hash_finish(hash, n * 8); mask->len = netdev_flow_key_size(n); } /* Initializes 'dst' as a copy of 'flow' masked with 'mask'. */ static inline void netdev_flow_key_init_masked(struct netdev_flow_key *dst, const struct flow *flow, const struct netdev_flow_key *mask) { uint64_t *dst_u64 = miniflow_values(&dst->mf); const uint64_t *mask_u64 = miniflow_get_values(&mask->mf); uint32_t hash = 0; uint64_t value; dst->len = mask->len; dst->mf = mask->mf; /* Copy maps. */ FLOW_FOR_EACH_IN_MAPS(value, flow, mask->mf.map) { *dst_u64 = value & *mask_u64++; hash = hash_add64(hash, *dst_u64++); } dst->hash = hash_finish(hash, (dst_u64 - miniflow_get_values(&dst->mf)) * 8); } /* Iterate through netdev_flow_key TNL u64 values specified by 'FLOWMAP'. */ #define NETDEV_FLOW_KEY_FOR_EACH_IN_FLOWMAP(VALUE, KEY, FLOWMAP) \ MINIFLOW_FOR_EACH_IN_FLOWMAP(VALUE, &(KEY)->mf, FLOWMAP) /* Returns a hash value for the bits of 'key' where there are 1-bits in * 'mask'. */ static inline uint32_t netdev_flow_key_hash_in_mask(const struct netdev_flow_key *key, const struct netdev_flow_key *mask) { const uint64_t *p = miniflow_get_values(&mask->mf); uint32_t hash = 0; uint64_t value; NETDEV_FLOW_KEY_FOR_EACH_IN_FLOWMAP(value, key, mask->mf.map) { hash = hash_add64(hash, value & *p++); } return hash_finish(hash, (p - miniflow_get_values(&mask->mf)) * 8); } static inline bool emc_entry_alive(struct emc_entry *ce) { return ce->flow && !ce->flow->dead; } static void emc_clear_entry(struct emc_entry *ce) { if (ce->flow) { dp_netdev_flow_unref(ce->flow); ce->flow = NULL; } } static inline void emc_change_entry(struct emc_entry *ce, struct dp_netdev_flow *flow, const struct netdev_flow_key *key) { if (ce->flow != flow) { if (ce->flow) { dp_netdev_flow_unref(ce->flow); } if (dp_netdev_flow_ref(flow)) { ce->flow = flow; } else { ce->flow = NULL; } } if (key) { netdev_flow_key_clone(&ce->key, key); } } static inline void emc_insert(struct emc_cache *cache, const struct netdev_flow_key *key, struct dp_netdev_flow *flow) { struct emc_entry *to_be_replaced = NULL; struct emc_entry *current_entry; EMC_FOR_EACH_POS_WITH_HASH(cache, current_entry, key->hash) { if (netdev_flow_key_equal(¤t_entry->key, key)) { /* We found the entry with the 'mf' miniflow */ emc_change_entry(current_entry, flow, NULL); return; } /* Replacement policy: put the flow in an empty (not alive) entry, or * in the first entry where it can be */ if (!to_be_replaced || (emc_entry_alive(to_be_replaced) && !emc_entry_alive(current_entry)) || current_entry->key.hash < to_be_replaced->key.hash) { to_be_replaced = current_entry; } } /* We didn't find the miniflow in the cache. * The 'to_be_replaced' entry is where the new flow will be stored */ emc_change_entry(to_be_replaced, flow, key); } static inline struct dp_netdev_flow * emc_lookup(struct emc_cache *cache, const struct netdev_flow_key *key) { struct emc_entry *current_entry; EMC_FOR_EACH_POS_WITH_HASH(cache, current_entry, key->hash) { if (current_entry->key.hash == key->hash && emc_entry_alive(current_entry) && netdev_flow_key_equal_mf(¤t_entry->key, &key->mf)) { /* We found the entry with the 'key->mf' miniflow */ return current_entry->flow; } } return NULL; } static struct dp_netdev_flow * dp_netdev_pmd_lookup_flow(const struct dp_netdev_pmd_thread *pmd, const struct netdev_flow_key *key) { struct dp_netdev_flow *netdev_flow; struct dpcls_rule *rule; dpcls_lookup(&pmd->cls, key, &rule, 1); netdev_flow = dp_netdev_flow_cast(rule); return netdev_flow; } static struct dp_netdev_flow * dp_netdev_pmd_find_flow(const struct dp_netdev_pmd_thread *pmd, const ovs_u128 *ufidp, const struct nlattr *key, size_t key_len) { struct dp_netdev_flow *netdev_flow; struct flow flow; ovs_u128 ufid; /* If a UFID is not provided, determine one based on the key. */ if (!ufidp && key && key_len && !dpif_netdev_flow_from_nlattrs(key, key_len, &flow)) { dpif_flow_hash(pmd->dp->dpif, &flow, sizeof flow, &ufid); ufidp = &ufid; } if (ufidp) { CMAP_FOR_EACH_WITH_HASH (netdev_flow, node, dp_netdev_flow_hash(ufidp), &pmd->flow_table) { if (ovs_u128_equals(&netdev_flow->ufid, ufidp)) { return netdev_flow; } } } return NULL; } static void get_dpif_flow_stats(const struct dp_netdev_flow *netdev_flow_, struct dpif_flow_stats *stats) { struct dp_netdev_flow *netdev_flow; unsigned long long n; long long used; uint16_t flags; netdev_flow = CONST_CAST(struct dp_netdev_flow *, netdev_flow_); atomic_read_relaxed(&netdev_flow->stats.packet_count, &n); stats->n_packets = n; atomic_read_relaxed(&netdev_flow->stats.byte_count, &n); stats->n_bytes = n; atomic_read_relaxed(&netdev_flow->stats.used, &used); stats->used = used; atomic_read_relaxed(&netdev_flow->stats.tcp_flags, &flags); stats->tcp_flags = flags; } /* Converts to the dpif_flow format, using 'key_buf' and 'mask_buf' for * storing the netlink-formatted key/mask. 'key_buf' may be the same as * 'mask_buf'. Actions will be returned without copying, by relying on RCU to * protect them. */ static void dp_netdev_flow_to_dpif_flow(const struct dp_netdev_flow *netdev_flow, struct ofpbuf *key_buf, struct ofpbuf *mask_buf, struct dpif_flow *flow, bool terse) { if (terse) { memset(flow, 0, sizeof *flow); } else { struct flow_wildcards wc; struct dp_netdev_actions *actions; size_t offset; struct odp_flow_key_parms odp_parms = { .flow = &netdev_flow->flow, .mask = &wc.masks, .support = dp_netdev_support, }; miniflow_expand(&netdev_flow->cr.mask->mf, &wc.masks); /* Key */ offset = key_buf->size; flow->key = ofpbuf_tail(key_buf); odp_parms.odp_in_port = netdev_flow->flow.in_port.odp_port; odp_flow_key_from_flow(&odp_parms, key_buf); flow->key_len = key_buf->size - offset; /* Mask */ offset = mask_buf->size; flow->mask = ofpbuf_tail(mask_buf); odp_parms.odp_in_port = wc.masks.in_port.odp_port; odp_parms.key_buf = key_buf; odp_flow_key_from_mask(&odp_parms, mask_buf); flow->mask_len = mask_buf->size - offset; /* Actions */ actions = dp_netdev_flow_get_actions(netdev_flow); flow->actions = actions->actions; flow->actions_len = actions->size; } flow->ufid = netdev_flow->ufid; flow->ufid_present = true; flow->pmd_id = netdev_flow->pmd_id; get_dpif_flow_stats(netdev_flow, &flow->stats); } static int dpif_netdev_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len, const struct nlattr *mask_key, uint32_t mask_key_len, const struct flow *flow, struct flow_wildcards *wc) { enum odp_key_fitness fitness; fitness = odp_flow_key_to_mask_udpif(mask_key, mask_key_len, key, key_len, wc, flow); if (fitness) { /* This should not happen: it indicates that * odp_flow_key_from_mask() and odp_flow_key_to_mask() * disagree on the acceptable form of a mask. Log the problem * as an error, with enough details to enable debugging. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (!VLOG_DROP_ERR(&rl)) { struct ds s; ds_init(&s); odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s, true); VLOG_ERR("internal error parsing flow mask %s (%s)", ds_cstr(&s), odp_key_fitness_to_string(fitness)); ds_destroy(&s); } return EINVAL; } return 0; } static int dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len, struct flow *flow) { odp_port_t in_port; if (odp_flow_key_to_flow_udpif(key, key_len, flow)) { /* This should not happen: it indicates that odp_flow_key_from_flow() * and odp_flow_key_to_flow() disagree on the acceptable form of a * flow. Log the problem as an error, with enough details to enable * debugging. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (!VLOG_DROP_ERR(&rl)) { struct ds s; ds_init(&s); odp_flow_format(key, key_len, NULL, 0, NULL, &s, true); VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s)); ds_destroy(&s); } return EINVAL; } in_port = flow->in_port.odp_port; if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) { return EINVAL; } /* Userspace datapath doesn't support conntrack. */ if (flow->ct_state || flow->ct_zone || flow->ct_mark || !ovs_u128_is_zero(&flow->ct_label)) { return EINVAL; } return 0; } static int dpif_netdev_flow_get(const struct dpif *dpif, const struct dpif_flow_get *get) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_flow *netdev_flow; struct dp_netdev_pmd_thread *pmd; unsigned pmd_id = get->pmd_id == PMD_ID_NULL ? NON_PMD_CORE_ID : get->pmd_id; int error = 0; pmd = dp_netdev_get_pmd(dp, pmd_id); if (!pmd) { return EINVAL; } netdev_flow = dp_netdev_pmd_find_flow(pmd, get->ufid, get->key, get->key_len); if (netdev_flow) { dp_netdev_flow_to_dpif_flow(netdev_flow, get->buffer, get->buffer, get->flow, false); } else { error = ENOENT; } dp_netdev_pmd_unref(pmd); return error; } static struct dp_netdev_flow * dp_netdev_flow_add(struct dp_netdev_pmd_thread *pmd, struct match *match, const ovs_u128 *ufid, const struct nlattr *actions, size_t actions_len) OVS_REQUIRES(pmd->flow_mutex) { struct dp_netdev_flow *flow; struct netdev_flow_key mask; netdev_flow_mask_init(&mask, match); /* Make sure wc does not have metadata. */ ovs_assert(!FLOWMAP_HAS_FIELD(&mask.mf.map, metadata) && !FLOWMAP_HAS_FIELD(&mask.mf.map, regs)); /* Do not allocate extra space. */ flow = xmalloc(sizeof *flow - sizeof flow->cr.flow.mf + mask.len); memset(&flow->stats, 0, sizeof flow->stats); flow->dead = false; flow->batch = NULL; *CONST_CAST(unsigned *, &flow->pmd_id) = pmd->core_id; *CONST_CAST(struct flow *, &flow->flow) = match->flow; *CONST_CAST(ovs_u128 *, &flow->ufid) = *ufid; ovs_refcount_init(&flow->ref_cnt); ovsrcu_set(&flow->actions, dp_netdev_actions_create(actions, actions_len)); netdev_flow_key_init_masked(&flow->cr.flow, &match->flow, &mask); dpcls_insert(&pmd->cls, &flow->cr, &mask); cmap_insert(&pmd->flow_table, CONST_CAST(struct cmap_node *, &flow->node), dp_netdev_flow_hash(&flow->ufid)); if (OVS_UNLIKELY(VLOG_IS_DBG_ENABLED())) { struct match match; struct ds ds = DS_EMPTY_INITIALIZER; match.tun_md.valid = false; match.flow = flow->flow; miniflow_expand(&flow->cr.mask->mf, &match.wc.masks); ds_put_cstr(&ds, "flow_add: "); odp_format_ufid(ufid, &ds); ds_put_cstr(&ds, " "); match_format(&match, &ds, OFP_DEFAULT_PRIORITY); ds_put_cstr(&ds, ", actions:"); format_odp_actions(&ds, actions, actions_len); VLOG_DBG_RL(&upcall_rl, "%s", ds_cstr(&ds)); ds_destroy(&ds); } return flow; } static int dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_flow *netdev_flow; struct netdev_flow_key key; struct dp_netdev_pmd_thread *pmd; struct match match; ovs_u128 ufid; unsigned pmd_id = put->pmd_id == PMD_ID_NULL ? NON_PMD_CORE_ID : put->pmd_id; int error; error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &match.flow); if (error) { return error; } error = dpif_netdev_mask_from_nlattrs(put->key, put->key_len, put->mask, put->mask_len, &match.flow, &match.wc); if (error) { return error; } pmd = dp_netdev_get_pmd(dp, pmd_id); if (!pmd) { return EINVAL; } /* Must produce a netdev_flow_key for lookup. * This interface is no longer performance critical, since it is not used * for upcall processing any more. */ netdev_flow_key_from_flow(&key, &match.flow); if (put->ufid) { ufid = *put->ufid; } else { dpif_flow_hash(dpif, &match.flow, sizeof match.flow, &ufid); } ovs_mutex_lock(&pmd->flow_mutex); netdev_flow = dp_netdev_pmd_lookup_flow(pmd, &key); if (!netdev_flow) { if (put->flags & DPIF_FP_CREATE) { if (cmap_count(&pmd->flow_table) < MAX_FLOWS) { if (put->stats) { memset(put->stats, 0, sizeof *put->stats); } dp_netdev_flow_add(pmd, &match, &ufid, put->actions, put->actions_len); error = 0; } else { error = EFBIG; } } else { error = ENOENT; } } else { if (put->flags & DPIF_FP_MODIFY && flow_equal(&match.flow, &netdev_flow->flow)) { struct dp_netdev_actions *new_actions; struct dp_netdev_actions *old_actions; new_actions = dp_netdev_actions_create(put->actions, put->actions_len); old_actions = dp_netdev_flow_get_actions(netdev_flow); ovsrcu_set(&netdev_flow->actions, new_actions); if (put->stats) { get_dpif_flow_stats(netdev_flow, put->stats); } if (put->flags & DPIF_FP_ZERO_STATS) { /* XXX: The userspace datapath uses thread local statistics * (for flows), which should be updated only by the owning * thread. Since we cannot write on stats memory here, * we choose not to support this flag. Please note: * - This feature is currently used only by dpctl commands with * option --clear. * - Should the need arise, this operation can be implemented * by keeping a base value (to be update here) for each * counter, and subtracting it before outputting the stats */ error = EOPNOTSUPP; } ovsrcu_postpone(dp_netdev_actions_free, old_actions); } else if (put->flags & DPIF_FP_CREATE) { error = EEXIST; } else { /* Overlapping flow. */ error = EINVAL; } } ovs_mutex_unlock(&pmd->flow_mutex); dp_netdev_pmd_unref(pmd); return error; } static int dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del) { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_flow *netdev_flow; struct dp_netdev_pmd_thread *pmd; unsigned pmd_id = del->pmd_id == PMD_ID_NULL ? NON_PMD_CORE_ID : del->pmd_id; int error = 0; pmd = dp_netdev_get_pmd(dp, pmd_id); if (!pmd) { return EINVAL; } ovs_mutex_lock(&pmd->flow_mutex); netdev_flow = dp_netdev_pmd_find_flow(pmd, del->ufid, del->key, del->key_len); if (netdev_flow) { if (del->stats) { get_dpif_flow_stats(netdev_flow, del->stats); } dp_netdev_pmd_remove_flow(pmd, netdev_flow); } else { error = ENOENT; } ovs_mutex_unlock(&pmd->flow_mutex); dp_netdev_pmd_unref(pmd); return error; } struct dpif_netdev_flow_dump { struct dpif_flow_dump up; struct cmap_position poll_thread_pos; struct cmap_position flow_pos; struct dp_netdev_pmd_thread *cur_pmd; int status; struct ovs_mutex mutex; }; static struct dpif_netdev_flow_dump * dpif_netdev_flow_dump_cast(struct dpif_flow_dump *dump) { return CONTAINER_OF(dump, struct dpif_netdev_flow_dump, up); } static struct dpif_flow_dump * dpif_netdev_flow_dump_create(const struct dpif *dpif_, bool terse) { struct dpif_netdev_flow_dump *dump; dump = xzalloc(sizeof *dump); dpif_flow_dump_init(&dump->up, dpif_); dump->up.terse = terse; ovs_mutex_init(&dump->mutex); return &dump->up; } static int dpif_netdev_flow_dump_destroy(struct dpif_flow_dump *dump_) { struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_); ovs_mutex_destroy(&dump->mutex); free(dump); return 0; } struct dpif_netdev_flow_dump_thread { struct dpif_flow_dump_thread up; struct dpif_netdev_flow_dump *dump; struct odputil_keybuf keybuf[FLOW_DUMP_MAX_BATCH]; struct odputil_keybuf maskbuf[FLOW_DUMP_MAX_BATCH]; }; static struct dpif_netdev_flow_dump_thread * dpif_netdev_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread) { return CONTAINER_OF(thread, struct dpif_netdev_flow_dump_thread, up); } static struct dpif_flow_dump_thread * dpif_netdev_flow_dump_thread_create(struct dpif_flow_dump *dump_) { struct dpif_netdev_flow_dump *dump = dpif_netdev_flow_dump_cast(dump_); struct dpif_netdev_flow_dump_thread *thread; thread = xmalloc(sizeof *thread); dpif_flow_dump_thread_init(&thread->up, &dump->up); thread->dump = dump; return &thread->up; } static void dpif_netdev_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_) { struct dpif_netdev_flow_dump_thread *thread = dpif_netdev_flow_dump_thread_cast(thread_); free(thread); } static int dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread *thread_, struct dpif_flow *flows, int max_flows) { struct dpif_netdev_flow_dump_thread *thread = dpif_netdev_flow_dump_thread_cast(thread_); struct dpif_netdev_flow_dump *dump = thread->dump; struct dp_netdev_flow *netdev_flows[FLOW_DUMP_MAX_BATCH]; int n_flows = 0; int i; ovs_mutex_lock(&dump->mutex); if (!dump->status) { struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dpif); struct dp_netdev *dp = get_dp_netdev(&dpif->dpif); struct dp_netdev_pmd_thread *pmd = dump->cur_pmd; int flow_limit = MIN(max_flows, FLOW_DUMP_MAX_BATCH); /* First call to dump_next(), extracts the first pmd thread. * If there is no pmd thread, returns immediately. */ if (!pmd) { pmd = dp_netdev_pmd_get_next(dp, &dump->poll_thread_pos); if (!pmd) { ovs_mutex_unlock(&dump->mutex); return n_flows; } } do { for (n_flows = 0; n_flows < flow_limit; n_flows++) { struct cmap_node *node; node = cmap_next_position(&pmd->flow_table, &dump->flow_pos); if (!node) { break; } netdev_flows[n_flows] = CONTAINER_OF(node, struct dp_netdev_flow, node); } /* When finishing dumping the current pmd thread, moves to * the next. */ if (n_flows < flow_limit) { memset(&dump->flow_pos, 0, sizeof dump->flow_pos); dp_netdev_pmd_unref(pmd); pmd = dp_netdev_pmd_get_next(dp, &dump->poll_thread_pos); if (!pmd) { dump->status = EOF; break; } } /* Keeps the reference to next caller. */ dump->cur_pmd = pmd; /* If the current dump is empty, do not exit the loop, since the * remaining pmds could have flows to be dumped. Just dumps again * on the new 'pmd'. */ } while (!n_flows); } ovs_mutex_unlock(&dump->mutex); for (i = 0; i < n_flows; i++) { struct odputil_keybuf *maskbuf = &thread->maskbuf[i]; struct odputil_keybuf *keybuf = &thread->keybuf[i]; struct dp_netdev_flow *netdev_flow = netdev_flows[i]; struct dpif_flow *f = &flows[i]; struct ofpbuf key, mask; ofpbuf_use_stack(&key, keybuf, sizeof *keybuf); ofpbuf_use_stack(&mask, maskbuf, sizeof *maskbuf); dp_netdev_flow_to_dpif_flow(netdev_flow, &key, &mask, f, dump->up.terse); } return n_flows; } static int dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) OVS_NO_THREAD_SAFETY_ANALYSIS { struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_pmd_thread *pmd; struct dp_packet *pp; if (dp_packet_size(execute->packet) < ETH_HEADER_LEN || dp_packet_size(execute->packet) > UINT16_MAX) { return EINVAL; } /* Tries finding the 'pmd'. If NULL is returned, that means * the current thread is a non-pmd thread and should use * dp_netdev_get_pmd(dp, NON_PMD_CORE_ID). */ pmd = ovsthread_getspecific(dp->per_pmd_key); if (!pmd) { pmd = dp_netdev_get_pmd(dp, NON_PMD_CORE_ID); if (!pmd) { return EBUSY; } } /* If the current thread is non-pmd thread, acquires * the 'non_pmd_mutex'. */ if (pmd->core_id == NON_PMD_CORE_ID) { ovs_mutex_lock(&dp->non_pmd_mutex); ovs_mutex_lock(&dp->port_mutex); } pp = execute->packet; dp_netdev_execute_actions(pmd, &pp, 1, false, execute->actions, execute->actions_len); if (pmd->core_id == NON_PMD_CORE_ID) { dp_netdev_pmd_unref(pmd); ovs_mutex_unlock(&dp->port_mutex); ovs_mutex_unlock(&dp->non_pmd_mutex); } return 0; } static void dpif_netdev_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops) { size_t i; for (i = 0; i < n_ops; i++) { struct dpif_op *op = ops[i]; switch (op->type) { case DPIF_OP_FLOW_PUT: op->error = dpif_netdev_flow_put(dpif, &op->u.flow_put); break; case DPIF_OP_FLOW_DEL: op->error = dpif_netdev_flow_del(dpif, &op->u.flow_del); break; case DPIF_OP_EXECUTE: op->error = dpif_netdev_execute(dpif, &op->u.execute); break; case DPIF_OP_FLOW_GET: op->error = dpif_netdev_flow_get(dpif, &op->u.flow_get); break; } } } /* Returns true if the configuration for rx queues or cpu mask * is changed. */ static bool pmd_config_changed(const struct dp_netdev *dp, size_t rxqs, const char *cmask) { if (dp->n_dpdk_rxqs != rxqs) { return true; } else { if (dp->pmd_cmask != NULL && cmask != NULL) { return strcmp(dp->pmd_cmask, cmask); } else { return (dp->pmd_cmask != NULL || cmask != NULL); } } } /* Resets pmd threads if the configuration for 'rxq's or cpu mask changes. */ static int dpif_netdev_pmd_set(struct dpif *dpif, unsigned int n_rxqs, const char *cmask) { struct dp_netdev *dp = get_dp_netdev(dpif); if (pmd_config_changed(dp, n_rxqs, cmask)) { struct dp_netdev_port *port; dp_netdev_destroy_all_pmds(dp); CMAP_FOR_EACH (port, node, &dp->ports) { if (netdev_is_pmd(port->netdev)) { int i, err; /* Closes the existing 'rxq's. */ for (i = 0; i < netdev_n_rxq(port->netdev); i++) { netdev_rxq_close(port->rxq[i]); port->rxq[i] = NULL; } /* Sets the new rx queue config. */ err = netdev_set_multiq(port->netdev, ovs_numa_get_n_cores() + 1, n_rxqs); if (err && (err != EOPNOTSUPP)) { VLOG_ERR("Failed to set dpdk interface %s rx_queue to:" " %u", netdev_get_name(port->netdev), n_rxqs); return err; } /* If the set_multiq() above succeeds, reopens the 'rxq's. */ port->rxq = xrealloc(port->rxq, sizeof *port->rxq * netdev_n_rxq(port->netdev)); for (i = 0; i < netdev_n_rxq(port->netdev); i++) { netdev_rxq_open(port->netdev, &port->rxq[i], i); } } } dp->n_dpdk_rxqs = n_rxqs; /* Reconfigures the cpu mask. */ ovs_numa_set_cpu_mask(cmask); free(dp->pmd_cmask); dp->pmd_cmask = cmask ? xstrdup(cmask) : NULL; /* Restores the non-pmd. */ dp_netdev_set_nonpmd(dp); /* Restores all pmd threads. */ dp_netdev_reset_pmd_threads(dp); } return 0; } static int dpif_netdev_queue_to_priority(const struct dpif *dpif OVS_UNUSED, uint32_t queue_id, uint32_t *priority) { *priority = queue_id; return 0; } /* Creates and returns a new 'struct dp_netdev_actions', whose actions are * a copy of the 'ofpacts_len' bytes of 'ofpacts'. */ struct dp_netdev_actions * dp_netdev_actions_create(const struct nlattr *actions, size_t size) { struct dp_netdev_actions *netdev_actions; netdev_actions = xmalloc(sizeof *netdev_actions + size); memcpy(netdev_actions->actions, actions, size); netdev_actions->size = size; return netdev_actions; } struct dp_netdev_actions * dp_netdev_flow_get_actions(const struct dp_netdev_flow *flow) { return ovsrcu_get(struct dp_netdev_actions *, &flow->actions); } static void dp_netdev_actions_free(struct dp_netdev_actions *actions) { free(actions); } static inline unsigned long long cycles_counter(void) { #ifdef DPDK_NETDEV return rte_get_tsc_cycles(); #else return 0; #endif } /* Fake mutex to make sure that the calls to cycles_count_* are balanced */ extern struct ovs_mutex cycles_counter_fake_mutex; /* Start counting cycles. Must be followed by 'cycles_count_end()' */ static inline void cycles_count_start(struct dp_netdev_pmd_thread *pmd) OVS_ACQUIRES(&cycles_counter_fake_mutex) OVS_NO_THREAD_SAFETY_ANALYSIS { pmd->last_cycles = cycles_counter(); } /* Stop counting cycles and add them to the counter 'type' */ static inline void cycles_count_end(struct dp_netdev_pmd_thread *pmd, enum pmd_cycles_counter_type type) OVS_RELEASES(&cycles_counter_fake_mutex) OVS_NO_THREAD_SAFETY_ANALYSIS { unsigned long long interval = cycles_counter() - pmd->last_cycles; non_atomic_ullong_add(&pmd->cycles.n[type], interval); } static void dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_port *port, struct netdev_rxq *rxq) { struct dp_packet *packets[NETDEV_MAX_BURST]; int error, cnt; cycles_count_start(pmd); error = netdev_rxq_recv(rxq, packets, &cnt); cycles_count_end(pmd, PMD_CYCLES_POLLING); if (!error) { *recirc_depth_get() = 0; cycles_count_start(pmd); dp_netdev_input(pmd, packets, cnt, port->port_no); cycles_count_end(pmd, PMD_CYCLES_PROCESSING); } else if (error != EAGAIN && error != EOPNOTSUPP) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "error receiving data from %s: %s", netdev_get_name(port->netdev), ovs_strerror(error)); } } /* Return true if needs to revalidate datapath flows. */ static bool dpif_netdev_run(struct dpif *dpif) { struct dp_netdev_port *port; struct dp_netdev *dp = get_dp_netdev(dpif); struct dp_netdev_pmd_thread *non_pmd = dp_netdev_get_pmd(dp, NON_PMD_CORE_ID); uint64_t new_tnl_seq; ovs_mutex_lock(&dp->non_pmd_mutex); CMAP_FOR_EACH (port, node, &dp->ports) { if (!netdev_is_pmd(port->netdev)) { int i; for (i = 0; i < netdev_n_rxq(port->netdev); i++) { dp_netdev_process_rxq_port(non_pmd, port, port->rxq[i]); } } } ovs_mutex_unlock(&dp->non_pmd_mutex); dp_netdev_pmd_unref(non_pmd); tnl_neigh_cache_run(); tnl_port_map_run(); new_tnl_seq = seq_read(tnl_conf_seq); if (dp->last_tnl_conf_seq != new_tnl_seq) { dp->last_tnl_conf_seq = new_tnl_seq; return true; } return false; } static void dpif_netdev_wait(struct dpif *dpif) { struct dp_netdev_port *port; struct dp_netdev *dp = get_dp_netdev(dpif); ovs_mutex_lock(&dp_netdev_mutex); CMAP_FOR_EACH (port, node, &dp->ports) { if (!netdev_is_pmd(port->netdev)) { int i; for (i = 0; i < netdev_n_rxq(port->netdev); i++) { netdev_rxq_wait(port->rxq[i]); } } } ovs_mutex_unlock(&dp_netdev_mutex); seq_wait(tnl_conf_seq, dp->last_tnl_conf_seq); } static int pmd_load_queues(struct dp_netdev_pmd_thread *pmd, struct rxq_poll **ppoll_list, int poll_cnt) OVS_REQUIRES(pmd->poll_mutex) { struct rxq_poll *poll_list = *ppoll_list; struct rxq_poll *poll; int i; for (i = 0; i < poll_cnt; i++) { port_unref(poll_list[i].port); } poll_list = xrealloc(poll_list, pmd->poll_cnt * sizeof *poll_list); i = 0; LIST_FOR_EACH (poll, node, &pmd->poll_list) { port_ref(poll->port); poll_list[i++] = *poll; } *ppoll_list = poll_list; return pmd->poll_cnt; } static void * pmd_thread_main(void *f_) { struct dp_netdev_pmd_thread *pmd = f_; unsigned int lc = 0; struct rxq_poll *poll_list; unsigned int port_seq = PMD_INITIAL_SEQ; int poll_cnt; int i; poll_cnt = 0; poll_list = NULL; /* Stores the pmd thread's 'pmd' to 'per_pmd_key'. */ ovsthread_setspecific(pmd->dp->per_pmd_key, pmd); pmd_thread_setaffinity_cpu(pmd->core_id); reload: emc_cache_init(&pmd->flow_cache); ovs_mutex_lock(&pmd->poll_mutex); poll_cnt = pmd_load_queues(pmd, &poll_list, poll_cnt); ovs_mutex_unlock(&pmd->poll_mutex); /* List port/core affinity */ for (i = 0; i < poll_cnt; i++) { VLOG_DBG("Core %d processing port \'%s\' with queue-id %d\n", pmd->core_id, netdev_get_name(poll_list[i].port->netdev), netdev_rxq_get_queue_id(poll_list[i].rx)); } /* Signal here to make sure the pmd finishes * reloading the updated configuration. */ dp_netdev_pmd_reload_done(pmd); for (;;) { for (i = 0; i < poll_cnt; i++) { dp_netdev_process_rxq_port(pmd, poll_list[i].port, poll_list[i].rx); } if (lc++ > 1024) { unsigned int seq; lc = 0; coverage_try_clear(); if (!ovsrcu_try_quiesce()) { emc_cache_slow_sweep(&pmd->flow_cache); } atomic_read_relaxed(&pmd->change_seq, &seq); if (seq != port_seq) { port_seq = seq; break; } } } emc_cache_uninit(&pmd->flow_cache); if (!latch_is_set(&pmd->exit_latch)){ goto reload; } for (i = 0; i < poll_cnt; i++) { port_unref(poll_list[i].port); } dp_netdev_pmd_reload_done(pmd); free(poll_list); return NULL; } static void dp_netdev_disable_upcall(struct dp_netdev *dp) OVS_ACQUIRES(dp->upcall_rwlock) { fat_rwlock_wrlock(&dp->upcall_rwlock); } static void dpif_netdev_disable_upcall(struct dpif *dpif) OVS_NO_THREAD_SAFETY_ANALYSIS { struct dp_netdev *dp = get_dp_netdev(dpif); dp_netdev_disable_upcall(dp); } static void dp_netdev_enable_upcall(struct dp_netdev *dp) OVS_RELEASES(dp->upcall_rwlock) { fat_rwlock_unlock(&dp->upcall_rwlock); } static void dpif_netdev_enable_upcall(struct dpif *dpif) OVS_NO_THREAD_SAFETY_ANALYSIS { struct dp_netdev *dp = get_dp_netdev(dpif); dp_netdev_enable_upcall(dp); } static void dp_netdev_pmd_reload_done(struct dp_netdev_pmd_thread *pmd) { ovs_mutex_lock(&pmd->cond_mutex); xpthread_cond_signal(&pmd->cond); ovs_mutex_unlock(&pmd->cond_mutex); } /* Finds and refs the dp_netdev_pmd_thread on core 'core_id'. Returns * the pointer if succeeds, otherwise, NULL (it can return NULL even if * 'core_id' is NON_PMD_CORE_ID). * * Caller must unrefs the returned reference. */ static struct dp_netdev_pmd_thread * dp_netdev_get_pmd(struct dp_netdev *dp, unsigned core_id) { struct dp_netdev_pmd_thread *pmd; const struct cmap_node *pnode; pnode = cmap_find(&dp->poll_threads, hash_int(core_id, 0)); if (!pnode) { return NULL; } pmd = CONTAINER_OF(pnode, struct dp_netdev_pmd_thread, node); return dp_netdev_pmd_try_ref(pmd) ? pmd : NULL; } /* Sets the 'struct dp_netdev_pmd_thread' for non-pmd threads. */ static void dp_netdev_set_nonpmd(struct dp_netdev *dp) { struct dp_netdev_pmd_thread *non_pmd; non_pmd = xzalloc(sizeof *non_pmd); dp_netdev_configure_pmd(non_pmd, dp, 0, NON_PMD_CORE_ID, OVS_NUMA_UNSPEC); } /* Caller must have valid pointer to 'pmd'. */ static bool dp_netdev_pmd_try_ref(struct dp_netdev_pmd_thread *pmd) { return ovs_refcount_try_ref_rcu(&pmd->ref_cnt); } static void dp_netdev_pmd_unref(struct dp_netdev_pmd_thread *pmd) { if (pmd && ovs_refcount_unref(&pmd->ref_cnt) == 1) { ovsrcu_postpone(dp_netdev_destroy_pmd, pmd); } } /* Given cmap position 'pos', tries to ref the next node. If try_ref() * fails, keeps checking for next node until reaching the end of cmap. * * Caller must unrefs the returned reference. */ static struct dp_netdev_pmd_thread * dp_netdev_pmd_get_next(struct dp_netdev *dp, struct cmap_position *pos) { struct dp_netdev_pmd_thread *next; do { struct cmap_node *node; node = cmap_next_position(&dp->poll_threads, pos); next = node ? CONTAINER_OF(node, struct dp_netdev_pmd_thread, node) : NULL; } while (next && !dp_netdev_pmd_try_ref(next)); return next; } /* Configures the 'pmd' based on the input argument. */ static void dp_netdev_configure_pmd(struct dp_netdev_pmd_thread *pmd, struct dp_netdev *dp, int index, unsigned core_id, int numa_id) { pmd->dp = dp; pmd->index = index; pmd->core_id = core_id; pmd->numa_id = numa_id; pmd->poll_cnt = 0; atomic_init(&pmd->tx_qid, (core_id == NON_PMD_CORE_ID) ? ovs_numa_get_n_cores() : get_n_pmd_threads(dp)); ovs_refcount_init(&pmd->ref_cnt); latch_init(&pmd->exit_latch); atomic_init(&pmd->change_seq, PMD_INITIAL_SEQ); xpthread_cond_init(&pmd->cond, NULL); ovs_mutex_init(&pmd->cond_mutex); ovs_mutex_init(&pmd->flow_mutex); ovs_mutex_init(&pmd->poll_mutex); dpcls_init(&pmd->cls); cmap_init(&pmd->flow_table); list_init(&pmd->poll_list); /* init the 'flow_cache' since there is no * actual thread created for NON_PMD_CORE_ID. */ if (core_id == NON_PMD_CORE_ID) { emc_cache_init(&pmd->flow_cache); } cmap_insert(&dp->poll_threads, CONST_CAST(struct cmap_node *, &pmd->node), hash_int(core_id, 0)); } static void dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread *pmd) { dp_netdev_pmd_flow_flush(pmd); dpcls_destroy(&pmd->cls); cmap_destroy(&pmd->flow_table); ovs_mutex_destroy(&pmd->flow_mutex); latch_destroy(&pmd->exit_latch); xpthread_cond_destroy(&pmd->cond); ovs_mutex_destroy(&pmd->cond_mutex); ovs_mutex_destroy(&pmd->poll_mutex); free(pmd); } /* Stops the pmd thread, removes it from the 'dp->poll_threads', * and unrefs the struct. */ static void dp_netdev_del_pmd(struct dp_netdev *dp, struct dp_netdev_pmd_thread *pmd) { struct rxq_poll *poll; /* Uninit the 'flow_cache' since there is * no actual thread uninit it for NON_PMD_CORE_ID. */ if (pmd->core_id == NON_PMD_CORE_ID) { emc_cache_uninit(&pmd->flow_cache); } else { latch_set(&pmd->exit_latch); dp_netdev_reload_pmd__(pmd); ovs_numa_unpin_core(pmd->core_id); xpthread_join(pmd->thread, NULL); } /* Unref all ports and free poll_list. */ LIST_FOR_EACH_POP (poll, node, &pmd->poll_list) { port_unref(poll->port); free(poll); } /* Purges the 'pmd''s flows after stopping the thread, but before * destroying the flows, so that the flow stats can be collected. */ if (dp->dp_purge_cb) { dp->dp_purge_cb(dp->dp_purge_aux, pmd->core_id); } cmap_remove(&pmd->dp->poll_threads, &pmd->node, hash_int(pmd->core_id, 0)); dp_netdev_pmd_unref(pmd); } /* Destroys all pmd threads. */ static void dp_netdev_destroy_all_pmds(struct dp_netdev *dp) { struct dp_netdev_pmd_thread *pmd; struct dp_netdev_pmd_thread **pmd_list; size_t k = 0, n_pmds; n_pmds = cmap_count(&dp->poll_threads); pmd_list = xcalloc(n_pmds, sizeof *pmd_list); CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { /* We cannot call dp_netdev_del_pmd(), since it alters * 'dp->poll_threads' (while we're iterating it) and it * might quiesce. */ ovs_assert(k < n_pmds); pmd_list[k++] = pmd; } for (size_t i = 0; i < k; i++) { dp_netdev_del_pmd(dp, pmd_list[i]); } free(pmd_list); } /* Deletes all pmd threads on numa node 'numa_id' and * fixes tx_qids of other threads to keep them sequential. */ static void dp_netdev_del_pmds_on_numa(struct dp_netdev *dp, int numa_id) { struct dp_netdev_pmd_thread *pmd; int n_pmds_on_numa, n_pmds; int *free_idx, k = 0; struct dp_netdev_pmd_thread **pmd_list; n_pmds_on_numa = get_n_pmd_threads_on_numa(dp, numa_id); free_idx = xcalloc(n_pmds_on_numa, sizeof *free_idx); pmd_list = xcalloc(n_pmds_on_numa, sizeof *pmd_list); CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { /* We cannot call dp_netdev_del_pmd(), since it alters * 'dp->poll_threads' (while we're iterating it) and it * might quiesce. */ if (pmd->numa_id == numa_id) { atomic_read_relaxed(&pmd->tx_qid, &free_idx[k]); pmd_list[k] = pmd; ovs_assert(k < n_pmds_on_numa); k++; } } for (int i = 0; i < k; i++) { dp_netdev_del_pmd(dp, pmd_list[i]); } n_pmds = get_n_pmd_threads(dp); CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { int old_tx_qid; atomic_read_relaxed(&pmd->tx_qid, &old_tx_qid); if (old_tx_qid >= n_pmds) { int new_tx_qid = free_idx[--k]; atomic_store_relaxed(&pmd->tx_qid, new_tx_qid); } } free(pmd_list); free(free_idx); } /* Returns PMD thread from this numa node with fewer rx queues to poll. * Returns NULL if there is no PMD threads on this numa node. * Can be called safely only by main thread. */ static struct dp_netdev_pmd_thread * dp_netdev_less_loaded_pmd_on_numa(struct dp_netdev *dp, int numa_id) { int min_cnt = -1; struct dp_netdev_pmd_thread *pmd, *res = NULL; CMAP_FOR_EACH (pmd, node, &dp->poll_threads) { if (pmd->numa_id == numa_id && (min_cnt > pmd->poll_cnt || res == NULL)) { min_cnt = pmd->poll_cnt; res = pmd; } } return res; } /* Adds rx queue to poll_list of PMD thread. */ static void dp_netdev_add_rxq_to_pmd(struct dp_netdev_pmd_thread *pmd, struct dp_netdev_port *port, struct netdev_rxq *rx) OVS_REQUIRES(pmd->poll_mutex) { struct rxq_poll *poll = xmalloc(sizeof *poll); port_ref(port); poll->port = port; poll->rx = rx; list_push_back(&pmd->poll_list, &poll->node); pmd->poll_cnt++; } /* Checks the numa node id of 'netdev' and starts pmd threads for * the numa node. */ static void dp_netdev_set_pmds_on_numa(struct dp_netdev *dp, int numa_id) { int n_pmds; if (!ovs_numa_numa_id_is_valid(numa_id)) { VLOG_ERR("Cannot create pmd threads due to numa id (%d)" "invalid", numa_id); return ; } n_pmds = get_n_pmd_threads_on_numa(dp, numa_id); /* If there are already pmd threads created for the numa node * in which 'netdev' is on, do nothing. Else, creates the * pmd threads for the numa node. */ if (!n_pmds) { int can_have, n_unpinned, i, index = 0; struct dp_netdev_pmd_thread **pmds; struct dp_netdev_port *port; n_unpinned = ovs_numa_get_n_unpinned_cores_on_numa(numa_id); if (!n_unpinned) { VLOG_ERR("Cannot create pmd threads due to out of unpinned " "cores on numa node %d", numa_id); return; } /* If cpu mask is specified, uses all unpinned cores, otherwise * tries creating NR_PMD_THREADS pmd threads. */ can_have = dp->pmd_cmask ? n_unpinned : MIN(n_unpinned, NR_PMD_THREADS); pmds = xzalloc(can_have * sizeof *pmds); for (i = 0; i < can_have; i++) { unsigned core_id = ovs_numa_get_unpinned_core_on_numa(numa_id); pmds[i] = xzalloc(sizeof **pmds); dp_netdev_configure_pmd(pmds[i], dp, i, core_id, numa_id); } /* Distributes rx queues of this numa node between new pmd threads. */ CMAP_FOR_EACH (port, node, &dp->ports) { if (netdev_is_pmd(port->netdev) && netdev_get_numa_id(port->netdev) == numa_id) { for (i = 0; i < netdev_n_rxq(port->netdev); i++) { /* Make thread-safety analyser happy. */ ovs_mutex_lock(&pmds[index]->poll_mutex); dp_netdev_add_rxq_to_pmd(pmds[index], port, port->rxq[i]); ovs_mutex_unlock(&pmds[index]->poll_mutex); index = (index + 1) % can_have; } } } /* Actual start of pmd threads. */ for (i = 0; i < can_have; i++) { pmds[i]->thread = ovs_thread_create("pmd", pmd_thread_main, pmds[i]); } free(pmds); VLOG_INFO("Created %d pmd threads on numa node %d", can_have, numa_id); } } /* Called after pmd threads config change. Restarts pmd threads with * new configuration. */ static void dp_netdev_reset_pmd_threads(struct dp_netdev *dp) { struct dp_netdev_port *port; CMAP_FOR_EACH (port, node, &dp->ports) { if (netdev_is_pmd(port->netdev)) { int numa_id = netdev_get_numa_id(port->netdev); dp_netdev_set_pmds_on_numa(dp, numa_id); } } } static char * dpif_netdev_get_datapath_version(void) { return xstrdup(""); } static void dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow, int cnt, int size, uint16_t tcp_flags, long long now) { uint16_t flags; atomic_store_relaxed(&netdev_flow->stats.used, now); non_atomic_ullong_add(&netdev_flow->stats.packet_count, cnt); non_atomic_ullong_add(&netdev_flow->stats.byte_count, size); atomic_read_relaxed(&netdev_flow->stats.tcp_flags, &flags); flags |= tcp_flags; atomic_store_relaxed(&netdev_flow->stats.tcp_flags, flags); } static void dp_netdev_count_packet(struct dp_netdev_pmd_thread *pmd, enum dp_stat_type type, int cnt) { non_atomic_ullong_add(&pmd->stats.n[type], cnt); } static int dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_, struct flow *flow, struct flow_wildcards *wc, ovs_u128 *ufid, enum dpif_upcall_type type, const struct nlattr *userdata, struct ofpbuf *actions, struct ofpbuf *put_actions) { struct dp_netdev *dp = pmd->dp; struct flow_tnl orig_tunnel; int err; if (OVS_UNLIKELY(!dp->upcall_cb)) { return ENODEV; } /* Upcall processing expects the Geneve options to be in the translated * format but we need to retain the raw format for datapath use. */ orig_tunnel.flags = flow->tunnel.flags; if (flow->tunnel.flags & FLOW_TNL_F_UDPIF) { orig_tunnel.metadata.present.len = flow->tunnel.metadata.present.len; memcpy(orig_tunnel.metadata.opts.gnv, flow->tunnel.metadata.opts.gnv, flow->tunnel.metadata.present.len); err = tun_metadata_from_geneve_udpif(&orig_tunnel, &orig_tunnel, &flow->tunnel); if (err) { return err; } } if (OVS_UNLIKELY(!VLOG_DROP_DBG(&upcall_rl))) { struct ds ds = DS_EMPTY_INITIALIZER; char *packet_str; struct ofpbuf key; struct odp_flow_key_parms odp_parms = { .flow = flow, .mask = wc ? &wc->masks : NULL, .odp_in_port = flow->in_port.odp_port, .support = dp_netdev_support, }; ofpbuf_init(&key, 0); odp_flow_key_from_flow(&odp_parms, &key); packet_str = ofp_packet_to_string(dp_packet_data(packet_), dp_packet_size(packet_)); odp_flow_key_format(key.data, key.size, &ds); VLOG_DBG("%s: %s upcall:\n%s\n%s", dp->name, dpif_upcall_type_to_string(type), ds_cstr(&ds), packet_str); ofpbuf_uninit(&key); free(packet_str); ds_destroy(&ds); } err = dp->upcall_cb(packet_, flow, ufid, pmd->core_id, type, userdata, actions, wc, put_actions, dp->upcall_aux); if (err && err != ENOSPC) { return err; } /* Translate tunnel metadata masks to datapath format. */ if (wc) { if (wc->masks.tunnel.metadata.present.map) { struct geneve_opt opts[TLV_TOT_OPT_SIZE / sizeof(struct geneve_opt)]; if (orig_tunnel.flags & FLOW_TNL_F_UDPIF) { tun_metadata_to_geneve_udpif_mask(&flow->tunnel, &wc->masks.tunnel, orig_tunnel.metadata.opts.gnv, orig_tunnel.metadata.present.len, opts); } else { orig_tunnel.metadata.present.len = 0; } memset(&wc->masks.tunnel.metadata, 0, sizeof wc->masks.tunnel.metadata); memcpy(&wc->masks.tunnel.metadata.opts.gnv, opts, orig_tunnel.metadata.present.len); } wc->masks.tunnel.metadata.present.len = 0xff; } /* Restore tunnel metadata. We need to use the saved options to ensure * that any unknown options are not lost. The generated mask will have * the same structure, matching on types and lengths but wildcarding * option data we don't care about. */ if (orig_tunnel.flags & FLOW_TNL_F_UDPIF) { memcpy(&flow->tunnel.metadata.opts.gnv, orig_tunnel.metadata.opts.gnv, orig_tunnel.metadata.present.len); flow->tunnel.metadata.present.len = orig_tunnel.metadata.present.len; flow->tunnel.flags |= FLOW_TNL_F_UDPIF; } return err; } static inline uint32_t dpif_netdev_packet_get_rss_hash(struct dp_packet *packet, const struct miniflow *mf) { uint32_t hash, recirc_depth; if (OVS_LIKELY(dp_packet_rss_valid(packet))) { hash = dp_packet_get_rss_hash(packet); } else { hash = miniflow_hash_5tuple(mf, 0); dp_packet_set_rss_hash(packet, hash); } /* The RSS hash must account for the recirculation depth to avoid * collisions in the exact match cache */ recirc_depth = *recirc_depth_get_unsafe(); if (OVS_UNLIKELY(recirc_depth)) { hash = hash_finish(hash, recirc_depth); dp_packet_set_rss_hash(packet, hash); } return hash; } struct packet_batch { unsigned int packet_count; unsigned int byte_count; uint16_t tcp_flags; struct dp_netdev_flow *flow; struct dp_packet *packets[NETDEV_MAX_BURST]; }; static inline void packet_batch_update(struct packet_batch *batch, struct dp_packet *packet, const struct miniflow *mf) { batch->tcp_flags |= miniflow_get_tcp_flags(mf); batch->packets[batch->packet_count++] = packet; batch->byte_count += dp_packet_size(packet); } static inline void packet_batch_init(struct packet_batch *batch, struct dp_netdev_flow *flow) { flow->batch = batch; batch->flow = flow; batch->packet_count = 0; batch->byte_count = 0; batch->tcp_flags = 0; } static inline void packet_batch_execute(struct packet_batch *batch, struct dp_netdev_pmd_thread *pmd, long long now) { struct dp_netdev_actions *actions; struct dp_netdev_flow *flow = batch->flow; dp_netdev_flow_used(flow, batch->packet_count, batch->byte_count, batch->tcp_flags, now); actions = dp_netdev_flow_get_actions(flow); dp_netdev_execute_actions(pmd, batch->packets, batch->packet_count, true, actions->actions, actions->size); } static inline void dp_netdev_queue_batches(struct dp_packet *pkt, struct dp_netdev_flow *flow, const struct miniflow *mf, struct packet_batch *batches, size_t *n_batches) { struct packet_batch *batch = flow->batch; if (OVS_LIKELY(batch)) { packet_batch_update(batch, pkt, mf); return; } batch = &batches[(*n_batches)++]; packet_batch_init(batch, flow); packet_batch_update(batch, pkt, mf); } static inline void dp_packet_swap(struct dp_packet **a, struct dp_packet **b) { struct dp_packet *tmp = *a; *a = *b; *b = tmp; } /* Try to process all ('cnt') the 'packets' using only the exact match cache * 'pmd->flow_cache'. If a flow is not found for a packet 'packets[i]', the * miniflow is copied into 'keys' and the packet pointer is moved at the * beginning of the 'packets' array. * * The function returns the number of packets that needs to be processed in the * 'packets' array (they have been moved to the beginning of the vector). * * If 'md_is_valid' is false, the metadata in 'packets' is not valid and must be * initialized by this function using 'port_no'. */ static inline size_t emc_processing(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, size_t cnt, struct netdev_flow_key *keys, struct packet_batch batches[], size_t *n_batches, bool md_is_valid, odp_port_t port_no) { struct emc_cache *flow_cache = &pmd->flow_cache; struct netdev_flow_key key; size_t i, notfound_cnt = 0; for (i = 0; i < cnt; i++) { struct dp_netdev_flow *flow; if (OVS_UNLIKELY(dp_packet_size(packets[i]) < ETH_HEADER_LEN)) { dp_packet_delete(packets[i]); continue; } if (i != cnt - 1) { /* Prefetch next packet data and metadata. */ OVS_PREFETCH(dp_packet_data(packets[i+1])); pkt_metadata_prefetch_init(&packets[i+1]->md); } if (!md_is_valid) { pkt_metadata_init(&packets[i]->md, port_no); } miniflow_extract(packets[i], &key.mf); key.len = 0; /* Not computed yet. */ key.hash = dpif_netdev_packet_get_rss_hash(packets[i], &key.mf); flow = emc_lookup(flow_cache, &key); if (OVS_LIKELY(flow)) { dp_netdev_queue_batches(packets[i], flow, &key.mf, batches, n_batches); } else { if (i != notfound_cnt) { dp_packet_swap(&packets[i], &packets[notfound_cnt]); } keys[notfound_cnt++] = key; } } dp_netdev_count_packet(pmd, DP_STAT_EXACT_HIT, cnt - notfound_cnt); return notfound_cnt; } static inline void fast_path_processing(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, size_t cnt, struct netdev_flow_key *keys, struct packet_batch batches[], size_t *n_batches) { #if !defined(__CHECKER__) && !defined(_WIN32) const size_t PKT_ARRAY_SIZE = cnt; #else /* Sparse or MSVC doesn't like variable length array. */ enum { PKT_ARRAY_SIZE = NETDEV_MAX_BURST }; #endif struct dpcls_rule *rules[PKT_ARRAY_SIZE]; struct dp_netdev *dp = pmd->dp; struct emc_cache *flow_cache = &pmd->flow_cache; int miss_cnt = 0, lost_cnt = 0; bool any_miss; size_t i; for (i = 0; i < cnt; i++) { /* Key length is needed in all the cases, hash computed on demand. */ keys[i].len = netdev_flow_key_size(miniflow_n_values(&keys[i].mf)); } any_miss = !dpcls_lookup(&pmd->cls, keys, rules, cnt); if (OVS_UNLIKELY(any_miss) && !fat_rwlock_tryrdlock(&dp->upcall_rwlock)) { uint64_t actions_stub[512 / 8], slow_stub[512 / 8]; struct ofpbuf actions, put_actions; ovs_u128 ufid; ofpbuf_use_stub(&actions, actions_stub, sizeof actions_stub); ofpbuf_use_stub(&put_actions, slow_stub, sizeof slow_stub); for (i = 0; i < cnt; i++) { struct dp_netdev_flow *netdev_flow; struct ofpbuf *add_actions; struct match match; int error; if (OVS_LIKELY(rules[i])) { continue; } /* It's possible that an earlier slow path execution installed * a rule covering this flow. In this case, it's a lot cheaper * to catch it here than execute a miss. */ netdev_flow = dp_netdev_pmd_lookup_flow(pmd, &keys[i]); if (netdev_flow) { rules[i] = &netdev_flow->cr; continue; } miss_cnt++; match.tun_md.valid = false; miniflow_expand(&keys[i].mf, &match.flow); ofpbuf_clear(&actions); ofpbuf_clear(&put_actions); dpif_flow_hash(dp->dpif, &match.flow, sizeof match.flow, &ufid); error = dp_netdev_upcall(pmd, packets[i], &match.flow, &match.wc, &ufid, DPIF_UC_MISS, NULL, &actions, &put_actions); if (OVS_UNLIKELY(error && error != ENOSPC)) { dp_packet_delete(packets[i]); lost_cnt++; continue; } /* The Netlink encoding of datapath flow keys cannot express * wildcarding the presence of a VLAN tag. Instead, a missing VLAN * tag is interpreted as exact match on the fact that there is no * VLAN. Unless we refactor a lot of code that translates between * Netlink and struct flow representations, we have to do the same * here. */ if (!match.wc.masks.vlan_tci) { match.wc.masks.vlan_tci = htons(0xffff); } /* We can't allow the packet batching in the next loop to execute * the actions. Otherwise, if there are any slow path actions, * we'll send the packet up twice. */ dp_netdev_execute_actions(pmd, &packets[i], 1, true, actions.data, actions.size); add_actions = put_actions.size ? &put_actions : &actions; if (OVS_LIKELY(error != ENOSPC)) { /* XXX: There's a race window where a flow covering this packet * could have already been installed since we last did the flow * lookup before upcall. This could be solved by moving the * mutex lock outside the loop, but that's an awful long time * to be locking everyone out of making flow installs. If we * move to a per-core classifier, it would be reasonable. */ ovs_mutex_lock(&pmd->flow_mutex); netdev_flow = dp_netdev_pmd_lookup_flow(pmd, &keys[i]); if (OVS_LIKELY(!netdev_flow)) { netdev_flow = dp_netdev_flow_add(pmd, &match, &ufid, add_actions->data, add_actions->size); } ovs_mutex_unlock(&pmd->flow_mutex); emc_insert(flow_cache, &keys[i], netdev_flow); } } ofpbuf_uninit(&actions); ofpbuf_uninit(&put_actions); fat_rwlock_unlock(&dp->upcall_rwlock); } else if (OVS_UNLIKELY(any_miss)) { for (i = 0; i < cnt; i++) { if (OVS_UNLIKELY(!rules[i])) { dp_packet_delete(packets[i]); lost_cnt++; miss_cnt++; } } } for (i = 0; i < cnt; i++) { struct dp_packet *packet = packets[i]; struct dp_netdev_flow *flow; if (OVS_UNLIKELY(!rules[i])) { continue; } flow = dp_netdev_flow_cast(rules[i]); emc_insert(flow_cache, &keys[i], flow); dp_netdev_queue_batches(packet, flow, &keys[i].mf, batches, n_batches); } dp_netdev_count_packet(pmd, DP_STAT_MASKED_HIT, cnt - miss_cnt); dp_netdev_count_packet(pmd, DP_STAT_MISS, miss_cnt); dp_netdev_count_packet(pmd, DP_STAT_LOST, lost_cnt); } /* Packets enter the datapath from a port (or from recirculation) here. * * For performance reasons a caller may choose not to initialize the metadata * in 'packets': in this case 'mdinit' is false and this function needs to * initialize it using 'port_no'. If the metadata in 'packets' is already * valid, 'md_is_valid' must be true and 'port_no' will be ignored. */ static void dp_netdev_input__(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, int cnt, bool md_is_valid, odp_port_t port_no) { #if !defined(__CHECKER__) && !defined(_WIN32) const size_t PKT_ARRAY_SIZE = cnt; #else /* Sparse or MSVC doesn't like variable length array. */ enum { PKT_ARRAY_SIZE = NETDEV_MAX_BURST }; #endif struct netdev_flow_key keys[PKT_ARRAY_SIZE]; struct packet_batch batches[PKT_ARRAY_SIZE]; long long now = time_msec(); size_t newcnt, n_batches, i; n_batches = 0; newcnt = emc_processing(pmd, packets, cnt, keys, batches, &n_batches, md_is_valid, port_no); if (OVS_UNLIKELY(newcnt)) { fast_path_processing(pmd, packets, newcnt, keys, batches, &n_batches); } for (i = 0; i < n_batches; i++) { batches[i].flow->batch = NULL; } for (i = 0; i < n_batches; i++) { packet_batch_execute(&batches[i], pmd, now); } } static void dp_netdev_input(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, int cnt, odp_port_t port_no) { dp_netdev_input__(pmd, packets, cnt, false, port_no); } static void dp_netdev_recirculate(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, int cnt) { dp_netdev_input__(pmd, packets, cnt, true, 0); } struct dp_netdev_execute_aux { struct dp_netdev_pmd_thread *pmd; }; static void dpif_netdev_register_dp_purge_cb(struct dpif *dpif, dp_purge_callback *cb, void *aux) { struct dp_netdev *dp = get_dp_netdev(dpif); dp->dp_purge_aux = aux; dp->dp_purge_cb = cb; } static void dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, void *aux) { struct dp_netdev *dp = get_dp_netdev(dpif); dp->upcall_aux = aux; dp->upcall_cb = cb; } static void dp_netdev_drop_packets(struct dp_packet **packets, int cnt, bool may_steal) { if (may_steal) { int i; for (i = 0; i < cnt; i++) { dp_packet_delete(packets[i]); } } } static int push_tnl_action(const struct dp_netdev *dp, const struct nlattr *attr, struct dp_packet **packets, int cnt) { struct dp_netdev_port *tun_port; const struct ovs_action_push_tnl *data; data = nl_attr_get(attr); tun_port = dp_netdev_lookup_port(dp, u32_to_odp(data->tnl_port)); if (!tun_port) { return -EINVAL; } netdev_push_header(tun_port->netdev, packets, cnt, data); return 0; } static void dp_netdev_clone_pkt_batch(struct dp_packet **dst_pkts, struct dp_packet **src_pkts, int cnt) { int i; for (i = 0; i < cnt; i++) { dst_pkts[i] = dp_packet_clone(src_pkts[i]); } } static void dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, const struct nlattr *a, bool may_steal) OVS_NO_THREAD_SAFETY_ANALYSIS { struct dp_netdev_execute_aux *aux = aux_; uint32_t *depth = recirc_depth_get(); struct dp_netdev_pmd_thread *pmd = aux->pmd; struct dp_netdev *dp = pmd->dp; int type = nl_attr_type(a); struct dp_netdev_port *p; int i; switch ((enum ovs_action_attr)type) { case OVS_ACTION_ATTR_OUTPUT: p = dp_netdev_lookup_port(dp, u32_to_odp(nl_attr_get_u32(a))); if (OVS_LIKELY(p)) { int tx_qid; atomic_read_relaxed(&pmd->tx_qid, &tx_qid); netdev_send(p->netdev, tx_qid, packets, cnt, may_steal); return; } break; case OVS_ACTION_ATTR_TUNNEL_PUSH: if (*depth < MAX_RECIRC_DEPTH) { struct dp_packet *tnl_pkt[NETDEV_MAX_BURST]; int err; if (!may_steal) { dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt); packets = tnl_pkt; } err = push_tnl_action(dp, a, packets, cnt); if (!err) { (*depth)++; dp_netdev_recirculate(pmd, packets, cnt); (*depth)--; } else { dp_netdev_drop_packets(tnl_pkt, cnt, !may_steal); } return; } break; case OVS_ACTION_ATTR_TUNNEL_POP: if (*depth < MAX_RECIRC_DEPTH) { odp_port_t portno = u32_to_odp(nl_attr_get_u32(a)); p = dp_netdev_lookup_port(dp, portno); if (p) { struct dp_packet *tnl_pkt[NETDEV_MAX_BURST]; int err; if (!may_steal) { dp_netdev_clone_pkt_batch(tnl_pkt, packets, cnt); packets = tnl_pkt; } err = netdev_pop_header(p->netdev, packets, cnt); if (!err) { for (i = 0; i < cnt; i++) { packets[i]->md.in_port.odp_port = portno; } (*depth)++; dp_netdev_recirculate(pmd, packets, cnt); (*depth)--; } else { dp_netdev_drop_packets(tnl_pkt, cnt, !may_steal); } return; } } break; case OVS_ACTION_ATTR_USERSPACE: if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) { const struct nlattr *userdata; struct ofpbuf actions; struct flow flow; ovs_u128 ufid; userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA); ofpbuf_init(&actions, 0); for (i = 0; i < cnt; i++) { int error; ofpbuf_clear(&actions); flow_extract(packets[i], &flow); dpif_flow_hash(dp->dpif, &flow, sizeof flow, &ufid); error = dp_netdev_upcall(pmd, packets[i], &flow, NULL, &ufid, DPIF_UC_ACTION, userdata,&actions, NULL); if (!error || error == ENOSPC) { dp_netdev_execute_actions(pmd, &packets[i], 1, may_steal, actions.data, actions.size); } else if (may_steal) { dp_packet_delete(packets[i]); } } ofpbuf_uninit(&actions); fat_rwlock_unlock(&dp->upcall_rwlock); return; } break; case OVS_ACTION_ATTR_RECIRC: if (*depth < MAX_RECIRC_DEPTH) { struct dp_packet *recirc_pkts[NETDEV_MAX_BURST]; if (!may_steal) { dp_netdev_clone_pkt_batch(recirc_pkts, packets, cnt); packets = recirc_pkts; } for (i = 0; i < cnt; i++) { packets[i]->md.recirc_id = nl_attr_get_u32(a); } (*depth)++; dp_netdev_recirculate(pmd, packets, cnt); (*depth)--; return; } VLOG_WARN("Packet dropped. Max recirculation depth exceeded."); break; case OVS_ACTION_ATTR_CT: /* If a flow with this action is slow-pathed, datapath assistance is * required to implement it. However, we don't support this action * in the userspace datapath. */ VLOG_WARN("Cannot execute conntrack action in userspace."); break; case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } dp_netdev_drop_packets(packets, cnt, may_steal); } static void dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd, struct dp_packet **packets, int cnt, bool may_steal, const struct nlattr *actions, size_t actions_len) { struct dp_netdev_execute_aux aux = { pmd }; odp_execute_actions(&aux, packets, cnt, may_steal, actions, actions_len, dp_execute_cb); } const struct dpif_class dpif_netdev_class = { "netdev", dpif_netdev_init, dpif_netdev_enumerate, dpif_netdev_port_open_type, dpif_netdev_open, dpif_netdev_close, dpif_netdev_destroy, dpif_netdev_run, dpif_netdev_wait, dpif_netdev_get_stats, dpif_netdev_port_add, dpif_netdev_port_del, dpif_netdev_port_query_by_number, dpif_netdev_port_query_by_name, NULL, /* port_get_pid */ dpif_netdev_port_dump_start, dpif_netdev_port_dump_next, dpif_netdev_port_dump_done, dpif_netdev_port_poll, dpif_netdev_port_poll_wait, dpif_netdev_flow_flush, dpif_netdev_flow_dump_create, dpif_netdev_flow_dump_destroy, dpif_netdev_flow_dump_thread_create, dpif_netdev_flow_dump_thread_destroy, dpif_netdev_flow_dump_next, dpif_netdev_operate, NULL, /* recv_set */ NULL, /* handlers_set */ dpif_netdev_pmd_set, dpif_netdev_queue_to_priority, NULL, /* recv */ NULL, /* recv_wait */ NULL, /* recv_purge */ dpif_netdev_register_dp_purge_cb, dpif_netdev_register_upcall_cb, dpif_netdev_enable_upcall, dpif_netdev_disable_upcall, dpif_netdev_get_datapath_version, NULL, /* ct_dump_start */ NULL, /* ct_dump_next */ NULL, /* ct_dump_done */ NULL, /* ct_flush */ }; static void dpif_dummy_change_port_number(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct dp_netdev_port *old_port; struct dp_netdev_port *new_port; struct dp_netdev *dp; odp_port_t port_no; ovs_mutex_lock(&dp_netdev_mutex); dp = shash_find_data(&dp_netdevs, argv[1]); if (!dp || !dpif_netdev_class_is_dummy(dp->class)) { ovs_mutex_unlock(&dp_netdev_mutex); unixctl_command_reply_error(conn, "unknown datapath or not a dummy"); return; } ovs_refcount_ref(&dp->ref_cnt); ovs_mutex_unlock(&dp_netdev_mutex); ovs_mutex_lock(&dp->port_mutex); if (get_port_by_name(dp, argv[2], &old_port)) { unixctl_command_reply_error(conn, "unknown port"); goto exit; } port_no = u32_to_odp(atoi(argv[3])); if (!port_no || port_no == ODPP_NONE) { unixctl_command_reply_error(conn, "bad port number"); goto exit; } if (dp_netdev_lookup_port(dp, port_no)) { unixctl_command_reply_error(conn, "port number already in use"); goto exit; } /* Remove old port. */ cmap_remove(&dp->ports, &old_port->node, hash_port_no(old_port->port_no)); ovsrcu_postpone(free, old_port); /* Insert new port (cmap semantics mean we cannot re-insert 'old_port'). */ new_port = xmemdup(old_port, sizeof *old_port); new_port->port_no = port_no; cmap_insert(&dp->ports, &new_port->node, hash_port_no(port_no)); seq_change(dp->port_seq); unixctl_command_reply(conn, NULL); exit: ovs_mutex_unlock(&dp->port_mutex); dp_netdev_unref(dp); } static void dpif_dummy_delete_port(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct dp_netdev_port *port; struct dp_netdev *dp; ovs_mutex_lock(&dp_netdev_mutex); dp = shash_find_data(&dp_netdevs, argv[1]); if (!dp || !dpif_netdev_class_is_dummy(dp->class)) { ovs_mutex_unlock(&dp_netdev_mutex); unixctl_command_reply_error(conn, "unknown datapath or not a dummy"); return; } ovs_refcount_ref(&dp->ref_cnt); ovs_mutex_unlock(&dp_netdev_mutex); ovs_mutex_lock(&dp->port_mutex); if (get_port_by_name(dp, argv[2], &port)) { unixctl_command_reply_error(conn, "unknown port"); } else if (port->port_no == ODPP_LOCAL) { unixctl_command_reply_error(conn, "can't delete local port"); } else { do_del_port(dp, port); unixctl_command_reply(conn, NULL); } ovs_mutex_unlock(&dp->port_mutex); dp_netdev_unref(dp); } static void dpif_dummy_register__(const char *type) { struct dpif_class *class; class = xmalloc(sizeof *class); *class = dpif_netdev_class; class->type = xstrdup(type); dp_register_provider(class); } static void dpif_dummy_override(const char *type) { int error; /* * Ignore EAFNOSUPPORT to allow --enable-dummy=system with * a userland-only build. It's useful for testsuite. */ error = dp_unregister_provider(type); if (error == 0 || error == EAFNOSUPPORT) { dpif_dummy_register__(type); } } void dpif_dummy_register(enum dummy_level level) { if (level == DUMMY_OVERRIDE_ALL) { struct sset types; const char *type; sset_init(&types); dp_enumerate_types(&types); SSET_FOR_EACH (type, &types) { dpif_dummy_override(type); } sset_destroy(&types); } else if (level == DUMMY_OVERRIDE_SYSTEM) { dpif_dummy_override("system"); } dpif_dummy_register__("dummy"); unixctl_command_register("dpif-dummy/change-port-number", "dp port new-number", 3, 3, dpif_dummy_change_port_number, NULL); unixctl_command_register("dpif-dummy/delete-port", "dp port", 2, 2, dpif_dummy_delete_port, NULL); } /* Datapath Classifier. */ /* A set of rules that all have the same fields wildcarded. */ struct dpcls_subtable { /* The fields are only used by writers. */ struct cmap_node cmap_node OVS_GUARDED; /* Within dpcls 'subtables_map'. */ /* These fields are accessed by readers. */ struct cmap rules; /* Contains "struct dpcls_rule"s. */ struct netdev_flow_key mask; /* Wildcards for fields (const). */ /* 'mask' must be the last field, additional space is allocated here. */ }; /* Initializes 'cls' as a classifier that initially contains no classification * rules. */ static void dpcls_init(struct dpcls *cls) { cmap_init(&cls->subtables_map); pvector_init(&cls->subtables); } static void dpcls_destroy_subtable(struct dpcls *cls, struct dpcls_subtable *subtable) { pvector_remove(&cls->subtables, subtable); cmap_remove(&cls->subtables_map, &subtable->cmap_node, subtable->mask.hash); cmap_destroy(&subtable->rules); ovsrcu_postpone(free, subtable); } /* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the * caller's responsibility. * May only be called after all the readers have been terminated. */ static void dpcls_destroy(struct dpcls *cls) { if (cls) { struct dpcls_subtable *subtable; CMAP_FOR_EACH (subtable, cmap_node, &cls->subtables_map) { ovs_assert(cmap_count(&subtable->rules) == 0); dpcls_destroy_subtable(cls, subtable); } cmap_destroy(&cls->subtables_map); pvector_destroy(&cls->subtables); } } static struct dpcls_subtable * dpcls_create_subtable(struct dpcls *cls, const struct netdev_flow_key *mask) { struct dpcls_subtable *subtable; /* Need to add one. */ subtable = xmalloc(sizeof *subtable - sizeof subtable->mask.mf + mask->len); cmap_init(&subtable->rules); netdev_flow_key_clone(&subtable->mask, mask); cmap_insert(&cls->subtables_map, &subtable->cmap_node, mask->hash); pvector_insert(&cls->subtables, subtable, 0); pvector_publish(&cls->subtables); return subtable; } static inline struct dpcls_subtable * dpcls_find_subtable(struct dpcls *cls, const struct netdev_flow_key *mask) { struct dpcls_subtable *subtable; CMAP_FOR_EACH_WITH_HASH (subtable, cmap_node, mask->hash, &cls->subtables_map) { if (netdev_flow_key_equal(&subtable->mask, mask)) { return subtable; } } return dpcls_create_subtable(cls, mask); } /* Insert 'rule' into 'cls'. */ static void dpcls_insert(struct dpcls *cls, struct dpcls_rule *rule, const struct netdev_flow_key *mask) { struct dpcls_subtable *subtable = dpcls_find_subtable(cls, mask); rule->mask = &subtable->mask; cmap_insert(&subtable->rules, &rule->cmap_node, rule->flow.hash); } /* Removes 'rule' from 'cls', also destructing the 'rule'. */ static void dpcls_remove(struct dpcls *cls, struct dpcls_rule *rule) { struct dpcls_subtable *subtable; ovs_assert(rule->mask); INIT_CONTAINER(subtable, rule->mask, mask); if (cmap_remove(&subtable->rules, &rule->cmap_node, rule->flow.hash) == 0) { dpcls_destroy_subtable(cls, subtable); pvector_publish(&cls->subtables); } } /* Returns true if 'target' satisfies 'key' in 'mask', that is, if each 1-bit * in 'mask' the values in 'key' and 'target' are the same. */ static inline bool dpcls_rule_matches_key(const struct dpcls_rule *rule, const struct netdev_flow_key *target) { const uint64_t *keyp = miniflow_get_values(&rule->flow.mf); const uint64_t *maskp = miniflow_get_values(&rule->mask->mf); uint64_t value; NETDEV_FLOW_KEY_FOR_EACH_IN_FLOWMAP(value, target, rule->flow.mf.map) { if (OVS_UNLIKELY((value & *maskp++) != *keyp++)) { return false; } } return true; } /* For each miniflow in 'flows' performs a classifier lookup writing the result * into the corresponding slot in 'rules'. If a particular entry in 'flows' is * NULL it is skipped. * * This function is optimized for use in the userspace datapath and therefore * does not implement a lot of features available in the standard * classifier_lookup() function. Specifically, it does not implement * priorities, instead returning any rule which matches the flow. * * Returns true if all flows found a corresponding rule. */ static bool dpcls_lookup(const struct dpcls *cls, const struct netdev_flow_key keys[], struct dpcls_rule **rules, const size_t cnt) { /* The batch size 16 was experimentally found faster than 8 or 32. */ typedef uint16_t map_type; #define MAP_BITS (sizeof(map_type) * CHAR_BIT) #if !defined(__CHECKER__) && !defined(_WIN32) const int N_MAPS = DIV_ROUND_UP(cnt, MAP_BITS); #else enum { N_MAPS = DIV_ROUND_UP(NETDEV_MAX_BURST, MAP_BITS) }; #endif map_type maps[N_MAPS]; struct dpcls_subtable *subtable; memset(maps, 0xff, sizeof maps); if (cnt % MAP_BITS) { maps[N_MAPS - 1] >>= MAP_BITS - cnt % MAP_BITS; /* Clear extra bits. */ } memset(rules, 0, cnt * sizeof *rules); PVECTOR_FOR_EACH (subtable, &cls->subtables) { const struct netdev_flow_key *mkeys = keys; struct dpcls_rule **mrules = rules; map_type remains = 0; int m; BUILD_ASSERT_DECL(sizeof remains == sizeof *maps); for (m = 0; m < N_MAPS; m++, mkeys += MAP_BITS, mrules += MAP_BITS) { uint32_t hashes[MAP_BITS]; const struct cmap_node *nodes[MAP_BITS]; unsigned long map = maps[m]; int i; if (!map) { continue; /* Skip empty maps. */ } /* Compute hashes for the remaining keys. */ ULLONG_FOR_EACH_1(i, map) { hashes[i] = netdev_flow_key_hash_in_mask(&mkeys[i], &subtable->mask); } /* Lookup. */ map = cmap_find_batch(&subtable->rules, map, hashes, nodes); /* Check results. */ ULLONG_FOR_EACH_1(i, map) { struct dpcls_rule *rule; CMAP_NODE_FOR_EACH (rule, cmap_node, nodes[i]) { if (OVS_LIKELY(dpcls_rule_matches_key(rule, &mkeys[i]))) { mrules[i] = rule; goto next; } } ULLONG_SET0(map, i); /* Did not match. */ next: ; /* Keep Sparse happy. */ } maps[m] &= ~map; /* Clear the found rules. */ remains |= maps[m]; } if (!remains) { return true; /* All found. */ } } return false; /* Some misses. */ } openvswitch-2.5.9/lib/PaxHeaders.82075/rstp-state-machines.c0000644000000000000000000000013213534540071020451 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.093686252 30 ctime=1567801424.869854075 openvswitch-2.5.9/lib/rstp-state-machines.c0000644000175000017500000023054313534540071022146 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011-2014 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines * implementation. * * Authors: * Martino Fornasa * Daniele Venturino * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. * */ #include #include "rstp.h" #include "rstp-state-machines.h" #include #include #include #include #include #include "byte-order.h" #include "connectivity.h" #include "ofpbuf.h" #include "dp-packet.h" #include "packets.h" #include "seq.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(rstp_sm); #define ROLE_FLAG_MASK 0xC #define ROLE_FLAG_SHIFT 2 enum port_flag { PORT_UNKN = 0, PORT_ALT_BACK = 1, PORT_ROOT = 2, PORT_DES = 3 }; enum bpdu_size { CONFIGURATION_BPDU_SIZE = 35, TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE = 4, RAPID_SPANNING_TREE_BPDU_SIZE = 36 }; /* Same is a subset of SUPERIOR, so can be used as a boolean when the * distinction is not significant. */ enum vector_comparison { INFERIOR = 0, SUPERIOR = 1, SAME = 2 }; static void decrement_timer(uint16_t *); static void rstp_send_bpdu(struct rstp_port *, const void *, size_t) OVS_REQUIRES(rstp_mutex); static int validate_received_bpdu(struct rstp_port *, const void *, size_t) OVS_REQUIRES(rstp_mutex); static ovs_be16 time_encode(uint8_t); static uint8_t time_decode(ovs_be16); static enum vector_comparison compare_rstp_priority_vectors(const struct rstp_priority_vector *, const struct rstp_priority_vector *); static bool rstp_times_equal(struct rstp_times *, struct rstp_times *); /* Per-Bridge State Machine */ static int port_role_selection_sm(struct rstp *) OVS_REQUIRES(rstp_mutex); /* Per-Port State Machines */ static int port_receive_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int port_protocol_migration_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int bridge_detection_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int port_transmit_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int port_information_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int port_role_transition_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int port_state_transition_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static int topology_change_sm(struct rstp_port *) OVS_REQUIRES(rstp_mutex); /* port_timers_sm() not defined as a state machine */ void process_received_bpdu__(struct rstp_port *p, const void *bpdu_, size_t bpdu_size) OVS_REQUIRES(rstp_mutex) { struct rstp *rstp = p->rstp; struct rstp_bpdu *bpdu = (struct rstp_bpdu *)bpdu_; if (!p->port_enabled) { return; } if (p->rcvd_bpdu) { return; } /* [9.2.9 Encoding of Port Role values] * NOTE. If the Unknown value of the Port Role parameter is received, the * state machines will effectively treat the RST BPDU as if it were a * Configuration BPDU. */ if (bpdu->bpdu_type == RAPID_SPANNING_TREE_BPDU) { uint8_t role = (bpdu->flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; if (role == PORT_UNKN) { bpdu->bpdu_type = CONFIGURATION_BPDU; } } if (validate_received_bpdu(p, bpdu, bpdu_size) == 0) { p->rcvd_bpdu = true; p->rx_rstp_bpdu_cnt++; memcpy(&p->received_bpdu_buffer, bpdu, sizeof(struct rstp_bpdu)); rstp->changes = true; move_rstp__(rstp); } else { VLOG_DBG("%s, port %u: Bad STP or RSTP BPDU received", p->rstp->name, p->port_number); p->error_count++; } } /* Returns 0 on success. */ static int validate_received_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size) OVS_REQUIRES(rstp_mutex) { /* Validation of received BPDU, see [9.3.4]. */ const struct rstp_bpdu *temp; temp = bpdu; if (bpdu_size < TOPOLOGY_CHANGE_NOTIFICATION_BPDU_SIZE || ntohs(temp->protocol_identifier) != 0) { return -1; } else { if (temp->bpdu_type == CONFIGURATION_BPDU && bpdu_size >= CONFIGURATION_BPDU_SIZE && (time_decode(temp->message_age) < time_decode(temp->max_age))) { if ((ntohll(temp->designated_bridge_id) != p->rstp->bridge_identifier) || ((ntohll(temp->designated_bridge_id) == p->rstp->bridge_identifier) && (ntohs(temp->designated_port_id) != p->port_id))) { return 0; } else { return -1; } } else if (temp->bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { return 0; } else if (temp->bpdu_type == RAPID_SPANNING_TREE_BPDU && bpdu_size >= RAPID_SPANNING_TREE_BPDU_SIZE) { return 0; } else { return -1; } } } /* * move_rstp__() * This method is invoked to move the State Machines. The SMs move only if the * boolean 'changes' is true, meaning that something changed and the SMs need * to work to process this change. * The boolean 'changes' is set every time a SM modifies its state, a BPDU is * received, a timer expires or port down event is detected. If a parameter is * set by management, then 'changes' is set. */ #define MAX_RSTP_ITERATIONS 1000 /* safeguard */ int move_rstp__(struct rstp *rstp) OVS_REQUIRES(rstp_mutex) { int num_iterations; num_iterations = 0; while (rstp->changes == true && num_iterations < MAX_RSTP_ITERATIONS) { struct rstp_port *p; VLOG_DBG("%s: move_rstp()", rstp->name); rstp->changes = false; port_role_selection_sm(rstp); HMAP_FOR_EACH (p, node, &rstp->ports) { if (p->rstp_state != RSTP_DISABLED) { port_receive_sm(p); bridge_detection_sm(p); port_information_sm(p); port_role_transition_sm(p); port_state_transition_sm(p); topology_change_sm(p); port_transmit_sm(p); port_protocol_migration_sm(p); } } num_iterations++; seq_change(connectivity_seq_get()); } if (num_iterations >= MAX_RSTP_ITERATIONS) { VLOG_ERR("%s: move_rstp() reached the iteration safeguard limit!", rstp->name); } return 0; } void decrease_rstp_port_timers__(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &r->ports) { decrement_timer(&p->hello_when); decrement_timer(&p->tc_while); decrement_timer(&p->fd_while); decrement_timer(&p->rcvd_info_while); decrement_timer(&p->rr_while); decrement_timer(&p->rb_while); decrement_timer(&p->mdelay_while); decrement_timer(&p->edge_delay_while); decrement_timer(&p->tx_count); p->uptime += 1; } r->changes = true; move_rstp__(r); } static void decrement_timer(uint16_t *timer) { if (*timer != 0) { *timer -= 1; } } /* Bridge State Machine. */ /* [17.28] Port Role Selection state machine. */ static void updt_role_disabled_tree(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &r->ports) { p->selected_role = ROLE_DISABLED; } } static void clear_reselect_tree(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &r->ports) { p->reselect = false; } } void updt_roles_tree__(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; int vsel; struct rstp_priority_vector best_vector, candidate_vector; enum rstp_port_role new_root_old_role = ROLE_DESIGNATED; uint16_t old_root_port_number = 0; uint16_t new_root_port_number = 0; old_root_port_number = r->root_port_id & 0x00ff; if (old_root_port_number) { r->old_root_aux = rstp_get_port_aux__(r, old_root_port_number); } vsel = -1; best_vector = r->bridge_priority; /* Letter c1) */ r->root_times = r->bridge_times; /* Letters a) b) c) */ HMAP_FOR_EACH (p, node, &r->ports) { uint32_t old_root_path_cost; uint32_t root_path_cost; if (p->info_is != INFO_IS_RECEIVED) { continue; } /* [17.6] */ candidate_vector = p->port_priority; candidate_vector.bridge_port_id = p->port_id; old_root_path_cost = candidate_vector.root_path_cost; root_path_cost = old_root_path_cost + p->port_path_cost; candidate_vector.root_path_cost = root_path_cost; if ((candidate_vector.designated_bridge_id & 0xffffffffffffULL) == (r->bridge_priority.designated_bridge_id & 0xffffffffffffULL)) { continue; } if (compare_rstp_priority_vectors(&candidate_vector, &best_vector) == SUPERIOR) { best_vector = candidate_vector; r->root_times = p->port_times; r->root_times.message_age++; vsel = p->port_number; new_root_old_role = p->role; } } r->root_priority = best_vector; r->root_port_id = best_vector.bridge_port_id; VLOG_DBG("%s: new Root is "RSTP_ID_FMT, r->name, RSTP_ID_ARGS(r->root_priority.root_bridge_id)); new_root_port_number = r->root_port_id & 0x00ff; if (new_root_port_number) { r->new_root_aux = rstp_get_port_aux__(r, new_root_port_number); } /* Shift learned MAC addresses from an old Root Port to an existing * Alternate Port. */ if (!r->root_changed && new_root_old_role == ROLE_ALTERNATE && new_root_port_number && old_root_port_number && new_root_port_number != old_root_port_number) { r->root_changed = true; } /* Letters d) e) */ HMAP_FOR_EACH (p, node, &r->ports) { p->designated_priority_vector.root_bridge_id = r->root_priority.root_bridge_id; p->designated_priority_vector.root_path_cost = r->root_priority.root_path_cost; p->designated_priority_vector.designated_bridge_id = r->bridge_identifier; p->designated_priority_vector.designated_port_id = p->port_id; p->designated_times = r->root_times; p->designated_times.hello_time = r->bridge_times.hello_time; } HMAP_FOR_EACH (p, node, &r->ports) { switch (p->info_is) { case INFO_IS_DISABLED: p->selected_role = ROLE_DISABLED; break; case INFO_IS_AGED: p->updt_info = true; p->selected_role = ROLE_DESIGNATED; break; case INFO_IS_MINE: p->selected_role = ROLE_DESIGNATED; if (compare_rstp_priority_vectors( &p->port_priority, &p->designated_priority_vector) != SAME || !rstp_times_equal(&p->designated_times, &r->root_times)) { p->updt_info = true; } break; case INFO_IS_RECEIVED: if (vsel == p->port_number) { /* Letter i) */ p->selected_role = ROLE_ROOT; p->updt_info = false; } else if (compare_rstp_priority_vectors( &p->designated_priority_vector, &p->port_priority) == INFERIOR) { if (p->port_priority.designated_bridge_id != r->bridge_identifier) { p->selected_role = ROLE_ALTERNATE; p->updt_info = false; } else { p->selected_role = ROLE_BACKUP; p->updt_info = false; } } else { p->selected_role = ROLE_DESIGNATED; p->updt_info = true; } break; default: OVS_NOT_REACHED(); /* no break */ } } seq_change(connectivity_seq_get()); } static void set_selected_tree(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &r->ports) { if (p->reselect) { return; } } HMAP_FOR_EACH (p, node, &r->ports) { p->selected = true; } } static int port_role_selection_sm(struct rstp *r) OVS_REQUIRES(rstp_mutex) { enum port_role_selection_state_machine old_state; struct rstp_port *p; old_state = r->port_role_selection_sm_state; switch (r->port_role_selection_sm_state) { case PORT_ROLE_SELECTION_SM_INIT: if (r->begin) { r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC; } break; case PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC: updt_role_disabled_tree(r); r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_INIT_BRIDGE; /* no break */ case PORT_ROLE_SELECTION_SM_INIT_BRIDGE: r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC; break; case PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC: clear_reselect_tree(r); updt_roles_tree__(r); set_selected_tree(r); r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION; /* no break */ case PORT_ROLE_SELECTION_SM_ROLE_SELECTION: HMAP_FOR_EACH (p, node, &r->ports) { if (p->reselect) { r->port_role_selection_sm_state = PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC; break; } } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != r->port_role_selection_sm_state) { r->changes = true; VLOG_DBG("%s: Port_role_selection_sm %d -> %d", r->name, old_state, r->port_role_selection_sm_state); } return 0; } /* Port State Machines */ /* [17.23 - Port receive state machine] */ static void updt_bpdu_version(struct rstp_port *p) /* [17.21.22] */ OVS_REQUIRES(rstp_mutex) { switch (p->received_bpdu_buffer.bpdu_type) { case CONFIGURATION_BPDU: case TOPOLOGY_CHANGE_NOTIFICATION_BPDU: p->rcvd_rstp = false; p->rcvd_stp = true; break; case RAPID_SPANNING_TREE_BPDU: p->rcvd_rstp = true; p->rcvd_stp = false; break; default: OVS_NOT_REACHED(); /* no break */ } } static int port_receive_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_receive_state_machine old_state; struct rstp *r; old_state = p->port_receive_sm_state; r = p->rstp; switch (p->port_receive_sm_state) { case PORT_RECEIVE_SM_INIT: if (r->begin || ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) && !p->port_enabled)) { p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; } break; case PORT_RECEIVE_SM_DISCARD_EXEC: p->rcvd_bpdu = p->rcvd_rstp = p->rcvd_stp = false; p->rcvd_msg = false; p->edge_delay_while = r->migrate_time; p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD; /* no break */ case PORT_RECEIVE_SM_DISCARD: if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) && !p->port_enabled) { /* Global transition. */ p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; } else if (p->rcvd_bpdu && p->port_enabled) { p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC; } break; case PORT_RECEIVE_SM_RECEIVE_EXEC: updt_bpdu_version(p); p->oper_edge = p->rcvd_bpdu = false; p->rcvd_msg = true; p->edge_delay_while = r->migrate_time; p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE; /* no break */ case PORT_RECEIVE_SM_RECEIVE: if ((p->rcvd_bpdu || (p->edge_delay_while != r->migrate_time)) && !p->port_enabled) { /* Global transition. */ p->port_receive_sm_state = PORT_RECEIVE_SM_DISCARD_EXEC; } else if (p->rcvd_bpdu && p->port_enabled && !p->rcvd_msg) { p->port_receive_sm_state = PORT_RECEIVE_SM_RECEIVE_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_receive_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: Port_receive_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_receive_sm_state); } return 0; } /* [17.24 - Port Protocol Migration state machine] */ static int port_protocol_migration_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_protocol_migration_state_machine old_state; struct rstp *r; old_state = p->port_protocol_migration_sm_state; r = p->rstp; switch (p->port_protocol_migration_sm_state) { case PORT_PROTOCOL_MIGRATION_SM_INIT: p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; /* no break */ case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC: p->mcheck = false; p->send_rstp = r->rstp_version; p->mdelay_while = r->migrate_time; p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP; /* no break */ case PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP: if (p->mdelay_while == 0) { p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC; } else if ((p->mdelay_while != r->migrate_time) && !p->port_enabled) { p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; } break; case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC: p->send_rstp = false; p->mdelay_while = r->migrate_time; p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP; /* no break */ case PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP: if ((p->mdelay_while == 0) || (!p->port_enabled) || p->mcheck) { p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC; } break; case PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC: p->rcvd_rstp = false; p->rcvd_stp = false; p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SENSING; /* no break */ case PORT_PROTOCOL_MIGRATION_SM_SENSING: if (!p->port_enabled || p->mcheck || ((r->rstp_version) && !p->send_rstp && p->rcvd_rstp)) { p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC; } else if (p->send_rstp && p->rcvd_stp) { p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_protocol_migration_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: port_protocol_migration_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_protocol_migration_sm_state); } return 0; } /* [17.25 - Bridge Detection state machine] */ static int bridge_detection_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum bridge_detection_state_machine old_state; struct rstp *r; old_state = p->bridge_detection_sm_state; r = p->rstp; switch (p->bridge_detection_sm_state) { case BRIDGE_DETECTION_SM_INIT: if (r->begin && p->admin_edge) { p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC; } else if (r->begin && !p->admin_edge) { p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC; } break; case BRIDGE_DETECTION_SM_EDGE_EXEC: p->oper_edge = true; p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE; /* no break */ case BRIDGE_DETECTION_SM_EDGE: if ((!p->port_enabled && !p->admin_edge) || !p->oper_edge) { p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE_EXEC; } break; case BRIDGE_DETECTION_SM_NOT_EDGE_EXEC: p->oper_edge = false; p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_NOT_EDGE; /* no break */ case BRIDGE_DETECTION_SM_NOT_EDGE: if ((!p->port_enabled && p->admin_edge) || ((p->edge_delay_while == 0) && p->auto_edge && p->send_rstp && p->proposing)) { p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_EDGE_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->bridge_detection_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: bridge_detection_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->bridge_detection_sm_state); } return 0; } /* [17.26 - Port Transmit state machine] */ static void rstp_send_bpdu(struct rstp_port *p, const void *bpdu, size_t bpdu_size) OVS_REQUIRES(rstp_mutex) { struct eth_header *eth; struct llc_header *llc; struct dp_packet *pkt; /* Skeleton. */ pkt = dp_packet_new(ETH_HEADER_LEN + LLC_HEADER_LEN + bpdu_size); eth = dp_packet_put_zeros(pkt, sizeof *eth); llc = dp_packet_put_zeros(pkt, sizeof *llc); dp_packet_reset_offsets(pkt); dp_packet_set_l3(pkt, dp_packet_put(pkt, bpdu, bpdu_size)); /* 802.2 header. */ eth->eth_dst = eth_addr_stp; /* p->rstp->send_bpdu() must fill in source address. */ eth->eth_type = htons(dp_packet_size(pkt) - ETH_HEADER_LEN); /* LLC header. */ llc->llc_dsap = STP_LLC_DSAP; llc->llc_ssap = STP_LLC_SSAP; llc->llc_cntl = STP_LLC_CNTL; p->rstp->send_bpdu(pkt, p->aux, p->rstp->aux); } static void record_agreement(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; r = p->rstp; if (r->rstp_version && p->oper_point_to_point_mac && ((p->received_bpdu_buffer.flags & BPDU_FLAG_AGREEMENT))) { p->agreed = true; p->proposing = false; } else { p->agreed = false; } } static void set_tc_flags(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* Sets rcvd_tc and/or rcvd_tc_ack if the Topology Change and/or Topology * Change Acknowledgment flags, respectively, are set in a ConfigBPDU or * RST BPDU. */ if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU || p->received_bpdu_buffer.bpdu_type == RAPID_SPANNING_TREE_BPDU) { if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGE) != 0) { p->rcvd_tc = true; } if ((p->received_bpdu_buffer.flags & BPDU_FLAG_TOPCHANGEACK) != 0) { p->rcvd_tc_ack = true; } } /* Sets rcvd_tcn true if the BPDU is a TCN BPDU. */ if (p->received_bpdu_buffer.bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { p->rcvd_tcn = true; } } static void record_dispute(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { if ((p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) != 0) { /* 802.1D-2004 says to set the agreed flag and to clear the proposing * flag. 802.1q-2008 instead says to set the disputed variable and to * clear the agreed variable. */ p->disputed = true; p->agreed = false; } } static void record_proposal(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_flag role = ((p->received_bpdu_buffer.flags) & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; if ((role == PORT_DES) && ((p->received_bpdu_buffer.flags & BPDU_FLAG_PROPOSAL) != 0)) { p->proposed = true; } } static void record_priority(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { p->port_priority.root_bridge_id = p->msg_priority.root_bridge_id; p->port_priority.root_path_cost = p->msg_priority.root_path_cost; p->port_priority.designated_bridge_id = p->msg_priority.designated_bridge_id; p->port_priority.designated_port_id = p->msg_priority.designated_port_id; } static void record_times(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { p->port_times = p->msg_times; if (p->msg_times.hello_time == 0) { p->port_times.hello_time = 1; } } static void updt_rcvd_info_while(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* [17.21.23] * The value assigned to rcvdInfoWhile is the three times the Hello Time, * if Message Age, incremented by 1 second and rounded to the nearest whole * second, does not exceed Max Age, and is zero otherwise. */ if (p->port_times.message_age < p->port_times.max_age) { p->rcvd_info_while = p->port_times.hello_time * 3; } else { p->rcvd_info_while = 0; } } /* Times are internally held in seconds, while the protocol uses 1/256 seconds. * time_encode() is used to convert time values sent in bpdus, while * time_decode() is used to convert time values received in bpdus. */ static ovs_be16 time_encode(uint8_t value) { return htons(value * 256); } static uint8_t time_decode(ovs_be16 encoded) { return ntohs(encoded) / 256; } /* [17.21.19] */ static void tx_config(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp_bpdu bpdu; bpdu.protocol_identifier = htons(0); bpdu.protocol_version_identifier = 0; bpdu.bpdu_type = CONFIGURATION_BPDU; bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id); bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost); bpdu.designated_bridge_id = htonll(p->designated_priority_vector.designated_bridge_id); bpdu.designated_port_id = htons(p->designated_priority_vector.designated_port_id); bpdu.message_age = time_encode(p->designated_times.message_age); bpdu.max_age = time_encode(p->designated_times.max_age); bpdu.hello_time = time_encode(p->designated_times.hello_time); bpdu.forward_delay = time_encode(p->designated_times.forward_delay); bpdu.flags = 0; if (p->tc_while != 0) { bpdu.flags |= BPDU_FLAG_TOPCHANGE; } if (p->tc_ack != 0) { bpdu.flags |= BPDU_FLAG_TOPCHANGEACK; } rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); } /* [17.21.20] */ static void tx_rstp(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp_bpdu bpdu; bpdu.protocol_identifier = htons(0); bpdu.protocol_version_identifier = 2; bpdu.bpdu_type = RAPID_SPANNING_TREE_BPDU; bpdu.root_bridge_id = htonll(p->designated_priority_vector.root_bridge_id); bpdu.root_path_cost = htonl(p->designated_priority_vector.root_path_cost); bpdu.designated_bridge_id = htonll(p->designated_priority_vector.designated_bridge_id); bpdu.designated_port_id = htons(p->designated_priority_vector.designated_port_id); bpdu.message_age = time_encode(p->designated_times.message_age); bpdu.max_age = time_encode(p->designated_times.max_age); bpdu.hello_time = time_encode(p->designated_times.hello_time); bpdu.forward_delay = time_encode(p->designated_times.forward_delay); bpdu.flags = 0; switch (p->role) { case ROLE_ROOT: bpdu.flags = PORT_ROOT << ROLE_FLAG_SHIFT; break; case ROLE_DESIGNATED: bpdu.flags = PORT_DES << ROLE_FLAG_SHIFT; break; case ROLE_ALTERNATE: case ROLE_BACKUP: bpdu.flags = PORT_ALT_BACK << ROLE_FLAG_SHIFT; break; case ROLE_DISABLED: /* Should not happen! */ VLOG_ERR("%s transmitting bpdu in disabled role on port " RSTP_PORT_ID_FMT, p->rstp->name, p->port_id); break; } if (p->agree) { bpdu.flags |= BPDU_FLAG_AGREEMENT; } if (p->proposing) { bpdu.flags |= BPDU_FLAG_PROPOSAL; } if (p->tc_while != 0) { bpdu.flags |= BPDU_FLAG_TOPCHANGE; } if (p->learning) { bpdu.flags |= BPDU_FLAG_LEARNING; } if (p->forwarding) { bpdu.flags |= BPDU_FLAG_FORWARDING; } bpdu.version1_length = 0; rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); } /* [17.21.21] */ static void tx_tcn(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp_bpdu bpdu; memset(&bpdu, 0, sizeof(struct rstp_bpdu)); bpdu.protocol_identifier = htons(0); bpdu.protocol_version_identifier = 0; bpdu.bpdu_type = TOPOLOGY_CHANGE_NOTIFICATION_BPDU; rstp_send_bpdu(p, &bpdu, sizeof(struct rstp_bpdu)); } static int port_transmit_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_transmit_state_machine old_state; struct rstp *r; old_state = p->port_transmit_sm_state; r = p->rstp; switch (p->port_transmit_sm_state) { case PORT_TRANSMIT_SM_INIT: if (r->begin) { p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC; } break; case PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC: p->new_info = true; p->tx_count = 0; p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_INIT; /* no break */ case PORT_TRANSMIT_SM_TRANSMIT_INIT: p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; break; case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC: p->new_info = p->new_info || (p->role == ROLE_DESIGNATED || (p->role == ROLE_ROOT && p->tc_while != 0)); p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC; /* no break */ case PORT_TRANSMIT_SM_TRANSMIT_PERIODIC: p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; break; case PORT_TRANSMIT_SM_IDLE_EXEC: p->hello_when = r->bridge_hello_time; p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE; /* no break */ case PORT_TRANSMIT_SM_IDLE: if (p->role == ROLE_DISABLED) { VLOG_DBG("%s, port %u: port_transmit_sm ROLE == DISABLED.", p->rstp->name, p->port_number); break; } else if (p->send_rstp && p->new_info && p->tx_count < r->transmit_hold_count && p->hello_when != 0 && p->selected && !p->updt_info) { p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC; } else if (p->hello_when == 0 && p->selected && !p->updt_info) { p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC; } else if (!p->send_rstp && p->new_info && p->role == ROLE_ROOT && p->tx_count < r->transmit_hold_count && p->hello_when != 0 && p->selected && !p->updt_info) { p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC; } else if (!p->send_rstp && p->new_info && p->role == ROLE_DESIGNATED && p->tx_count < r->transmit_hold_count && p->hello_when != 0 && p->selected && !p->updt_info) { p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC; } break; case PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC: p->new_info = false; tx_config(p); p->tx_count += 1; p->tc_ack = false; p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_CONFIG; /* no break */ case PORT_TRANSMIT_SM_TRANSMIT_CONFIG: p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; break; case PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC: p->new_info = false; tx_tcn(p); p->tx_count += 1; p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_TCN; /* no break */ case PORT_TRANSMIT_SM_TRANSMIT_TCN: p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; break; case PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC: p->new_info = false; tx_rstp(p); p->tx_count += 1; p->tc_ack = false; p->port_transmit_sm_state = PORT_TRANSMIT_SM_TRANSMIT_RSTP; /* no break */ case PORT_TRANSMIT_SM_TRANSMIT_RSTP: p->port_transmit_sm_state = PORT_TRANSMIT_SM_IDLE_EXEC; break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_transmit_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: port_transmit_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_transmit_sm_state); } return 0; } /* [17.27 Port Information state machine] */ #define RECEIVED 0 #define MINE 1 static int rcv_info(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum vector_comparison cp; bool ct; enum port_flag role; p->msg_priority.root_bridge_id = ntohll(p->received_bpdu_buffer.root_bridge_id); p->msg_priority.root_path_cost = ntohl(p->received_bpdu_buffer.root_path_cost); p->msg_priority.designated_bridge_id = ntohll(p->received_bpdu_buffer.designated_bridge_id); p->msg_priority.designated_port_id = ntohs(p->received_bpdu_buffer.designated_port_id); p->msg_times.forward_delay = time_decode(p->received_bpdu_buffer.forward_delay); p->msg_times.hello_time = time_decode(p->received_bpdu_buffer.hello_time); p->msg_times.max_age = time_decode(p->received_bpdu_buffer.max_age); p->msg_times.message_age = time_decode(p->received_bpdu_buffer.message_age); cp = compare_rstp_priority_vectors(&p->msg_priority, &p->port_priority); ct = rstp_times_equal(&p->port_times, &p->msg_times); /* Configuration BPDU conveys a Designated Port Role. */ if (p->received_bpdu_buffer.bpdu_type == CONFIGURATION_BPDU) { role = PORT_DES; } else { role = (p->received_bpdu_buffer.flags & ROLE_FLAG_MASK) >> ROLE_FLAG_SHIFT; } /* 802.1D-2004 does not report this behaviour. * 802.1Q-2008 says set rcvdTcn. */ if (p->received_bpdu_buffer.bpdu_type == TOPOLOGY_CHANGE_NOTIFICATION_BPDU) { p->rcvd_tcn = true; return OTHER_INFO; } /* Returns SuperiorDesignatedInfo if: * a) The received message conveys a Designated Port Role, and * 1) The message priority is superior (17.6) to the Port.s port priority * vector, or * 2) The message priority vector is the same as the Port.s port priority * vector, and any of the received timer parameter values (msg_times. * 17.19.15) differ from those already held for the Port (port_times * 17.19.22). * NOTE: Configuration BPDU explicitly conveys a Designated Port Role. */ if (role == PORT_DES && (cp == SUPERIOR || (cp == SAME && ct == false))) { return SUPERIOR_DESIGNATED_INFO; /* Returns RepeatedDesignatedInfo if: * b) The received message conveys Designated Port Role, and a message * priority vector and timer parameters that are the same as the * Port's port priority vector or timer values. */ } else if (role == PORT_DES && cp == SAME && ct == true) { return REPEATED_DESIGNATED_INFO; /* Returns InferiorDesignatedInfo if: * c) The received message conveys a Designated Port Role, and a * message priority vector that is worse than the Port's port * priority vector. */ } else if (role == PORT_DES && cp == INFERIOR) { return INFERIOR_DESIGNATED_INFO; /* Returns InferiorRootAlternateInfo if: * d) The received message conveys a Root Port, Alternate Port, or * Backup Port Role and a message priority that is the same as or * worse than the port priority vector. */ } else if ((role == PORT_ROOT || role == PORT_ALT_BACK) && (cp == INFERIOR || cp == SAME)) { return INFERIOR_ROOT_ALTERNATE_INFO; /* Otherwise, returns OtherInfo. */ } else { return OTHER_INFO; } } static int better_or_same_info(struct rstp_port *p, int new_info_is) OVS_REQUIRES(rstp_mutex) { return (new_info_is == RECEIVED && p->info_is == INFO_IS_RECEIVED && compare_rstp_priority_vectors(&p->msg_priority, &p->port_priority)) || (new_info_is == MINE && p->info_is == INFO_IS_MINE && compare_rstp_priority_vectors(&p->designated_priority_vector, &p->port_priority)); } static int port_information_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_information_state_machine old_state; struct rstp *r; struct rstp_port *p1; old_state = p->port_information_sm_state; r = p->rstp; switch (p->port_information_sm_state) { case PORT_INFORMATION_SM_INIT: if (r->begin || (!p->port_enabled && p->info_is != INFO_IS_DISABLED)) { p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } break; case PORT_INFORMATION_SM_DISABLED_EXEC: p->rcvd_msg = false; p->proposing = p->proposed = p->agree = p->agreed = false; p->rcvd_info_while = 0; p->info_is = INFO_IS_DISABLED; p->reselect = true; p->selected = false; p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED; /* no break */ case PORT_INFORMATION_SM_DISABLED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else if (p->port_enabled) { p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC; } else if (p->rcvd_msg) { p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } break; case PORT_INFORMATION_SM_AGED_EXEC: p->info_is = INFO_IS_AGED; p->reselect = true; p->selected = false; p->port_information_sm_state = PORT_INFORMATION_SM_AGED; /* no break */ case PORT_INFORMATION_SM_AGED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else if (p->selected && p->updt_info) { p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC; } break; case PORT_INFORMATION_SM_UPDATE_EXEC: p->proposing = p->proposed = false; /* MINE is not specified in Standard 802.1D-2004. */ p->agreed = p->agreed && better_or_same_info(p, MINE); p->synced = p->synced && p->agreed; p->port_priority.root_bridge_id = p->designated_priority_vector.root_bridge_id; p->port_priority.root_path_cost = p->designated_priority_vector.root_path_cost; p->port_priority.designated_bridge_id = p->designated_priority_vector.designated_bridge_id; p->port_priority.designated_port_id = p->designated_priority_vector.designated_port_id; p->port_times = p->designated_times; p->updt_info = false; p->info_is = INFO_IS_MINE; p->new_info = true; p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE; /* no break */ case PORT_INFORMATION_SM_UPDATE: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; case PORT_INFORMATION_SM_CURRENT_EXEC: p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT; /* no break */ case PORT_INFORMATION_SM_CURRENT: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else if (p->rcvd_msg && !p->updt_info) { p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE_EXEC; } else if (p->selected && p->updt_info) { p->port_information_sm_state = PORT_INFORMATION_SM_UPDATE_EXEC; } else if ((p->info_is == INFO_IS_RECEIVED) && (p->rcvd_info_while == 0) && !p->updt_info && !p->rcvd_msg) { p->port_information_sm_state = PORT_INFORMATION_SM_AGED_EXEC; } break; case PORT_INFORMATION_SM_RECEIVE_EXEC: p->rcvd_info = rcv_info(p); p->port_information_sm_state = PORT_INFORMATION_SM_RECEIVE; /* no break */ case PORT_INFORMATION_SM_RECEIVE: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { switch (p->rcvd_info) { case SUPERIOR_DESIGNATED_INFO: /* 802.1q-2008 has a checkBPDUConsistency() function, called on * a BPDU reception. checkBPDUConsistency() clears the agreed * variable if the received message priority vector is superior * to the port priority vector, the BPDU is an ST BPDU or an * RST BPDU, its port role is Designated and its Learning flag * is set. */ if (p->received_bpdu_buffer.flags & BPDU_FLAG_LEARNING) { HMAP_FOR_EACH (p1, node, &r->ports) { if (p1->port_number != p->port_number) { p1->agreed = false; } } } p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC; break; case REPEATED_DESIGNATED_INFO: p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC; break; case INFERIOR_DESIGNATED_INFO: p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC; break; case INFERIOR_ROOT_ALTERNATE_INFO: p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC; break; case OTHER_INFO: p->port_information_sm_state = PORT_INFORMATION_SM_OTHER_EXEC; break; default: OVS_NOT_REACHED(); /* no break */ } } break; case PORT_INFORMATION_SM_OTHER_EXEC: p->rcvd_msg = false; p->port_information_sm_state = PORT_INFORMATION_SM_OTHER; /* no break */ case PORT_INFORMATION_SM_OTHER: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; case PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC: record_agreement(p); set_tc_flags(p); p->rcvd_msg = false; p->port_information_sm_state = PORT_INFORMATION_SM_NOT_DESIGNATED; /* no break */ case PORT_INFORMATION_SM_NOT_DESIGNATED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; case PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC: record_dispute(p); p->rcvd_msg = false; p->port_information_sm_state = PORT_INFORMATION_SM_INFERIOR_DESIGNATED; /* no break */ case PORT_INFORMATION_SM_INFERIOR_DESIGNATED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; case PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC: record_proposal(p); set_tc_flags(p); /* This record_agreement() is missing in 802.1D-2004, but it's present * in 802.1q-2008. */ record_agreement(p); updt_rcvd_info_while(p); p->rcvd_msg = false; p->port_information_sm_state = PORT_INFORMATION_SM_REPEATED_DESIGNATED; /* no break */ case PORT_INFORMATION_SM_REPEATED_DESIGNATED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC: p->agreed = p->proposing = false; record_proposal(p); set_tc_flags(p); /* RECEIVED is not specified in Standard 802.1D-2004. */ p->agree = p->agree && better_or_same_info(p, RECEIVED); /* This record_agreement() and the synced assignment are missing in * 802.1D-2004, but they're present in 802.1q-2008. */ record_agreement(p); p->synced = p->synced && p->agreed; record_priority(p); record_times(p); updt_rcvd_info_while(p); p->info_is = INFO_IS_RECEIVED; p->reselect = true; p->selected = false; p->rcvd_msg = false; p->port_information_sm_state = PORT_INFORMATION_SM_SUPERIOR_DESIGNATED; /* no break */ case PORT_INFORMATION_SM_SUPERIOR_DESIGNATED: if (!p->port_enabled && p->info_is != INFO_IS_DISABLED) { /* Global transition. */ p->port_information_sm_state = PORT_INFORMATION_SM_DISABLED_EXEC; } else { p->port_information_sm_state = PORT_INFORMATION_SM_CURRENT_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_information_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: Port_information_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_information_sm_state); } return 0; } /* [17.29 Port Role Transitions state machine] */ static void set_re_root_tree(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; struct rstp_port *p1; r = p->rstp; HMAP_FOR_EACH (p1, node, &r->ports) { p1->re_root = true; } } static void set_sync_tree(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; struct rstp_port *p1; r = p->rstp; HMAP_FOR_EACH (p1, node, &r->ports) { p1->sync = true; } } static int hello_time(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { return p->designated_times.hello_time; } static int fwd_delay(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { return p->designated_times.forward_delay; } static int forward_delay(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { if (p->send_rstp) { return hello_time(p); } else { return fwd_delay(p); } } static int edge_delay(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; r = p->rstp; if (p->oper_point_to_point_mac == 1) { return r->migrate_time; } else { return p->designated_times.max_age; } } static int check_selected_role_change(struct rstp_port *p, int current_role_state) OVS_REQUIRES(rstp_mutex) { if (p->selected && !p->updt_info && p->role != p->selected_role && p->selected_role != current_role_state) { VLOG_DBG("%s, port %u: case: current = %s role = %s selected = %d", p->rstp->name, p->port_number, rstp_port_role_name(current_role_state), rstp_port_role_name(p->role), p->selected_role); switch (p->selected_role) { case ROLE_ROOT: p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; return true; case ROLE_DESIGNATED: p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; return true; case ROLE_ALTERNATE: p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC; return true; case ROLE_BACKUP: p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC; return true; case ROLE_DISABLED: p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC; return true; } } return false; } static int re_rooted(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; struct rstp_port *p1; r = p->rstp; HMAP_FOR_EACH (p1, node, &r->ports) { if ((p1 != p) && (p1->rr_while != 0)) { return false; } } return true; } static int all_synced(struct rstp *r) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &r->ports) { if (!(p->selected && p->role == p->selected_role && (p->role == ROLE_ROOT || p->synced == true))) { return false; } } return true; } static int port_role_transition_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_role_transition_state_machine old_state; struct rstp *r; enum rstp_port_role last_role; old_state = p->port_role_transition_sm_state; r = p->rstp; last_role = p->role; switch (p->port_role_transition_sm_state) { case PORT_ROLE_TRANSITION_SM_INIT: if (r->begin) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC: p->role = ROLE_DISABLED; p->learn = p->forward = false; p->synced = false; p->sync = p->re_root = true; p->rr_while = p->designated_times.forward_delay; p->fd_while = p->designated_times.max_age; p->rb_while = 0; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC; break; case PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC: p->role = p->selected_role; p->learn = p->forward = false; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLE_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_DISABLE_PORT: if (check_selected_role_change(p, ROLE_DISABLED)) { /* Global transition. */ } else if (p->selected && !p->updt_info && !p->learning && !p->forwarding) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC: p->fd_while = p->designated_times.max_age; p->synced = true; p->rr_while = 0; p->sync = p->re_root = false; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_DISABLED_PORT: if (check_selected_role_change(p, ROLE_DISABLED)) { /* Global transition. */ } else if (p->selected && !p->updt_info && (p->fd_while != p->designated_times.max_age || p->sync || p->re_root || !p->synced)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC: p->role = ROLE_ROOT; p->rr_while = p->designated_times.forward_delay; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_ROOT_PORT: if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else if (p->selected && !p->updt_info) { if (p->rr_while != p->designated_times.forward_delay) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; break; } else if (p->re_root && p->forward) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_REROOTED_EXEC; break; } else if ((p->fd_while == 0 || ((re_rooted(p) && p->rb_while == 0) && r->rstp_version)) && !p->learn) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC; break; } else if ((p->fd_while == 0 || ((re_rooted(p) && p->rb_while == 0) && r->rstp_version)) && p->learn && !p->forward) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC; break; } else if (p->proposed && !p->agree) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC; break; } else if ((all_synced(r) && !p->agree) || (p->proposed && p->agree)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC; break; } else if (!p->forward && !p->re_root) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_REROOT_EXEC; break; } } break; case PORT_ROLE_TRANSITION_SM_REROOT_EXEC: if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { set_re_root_tree(p); p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC: if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { p->proposed = p->sync = false; p->agree = p->new_info = true; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC: set_sync_tree(p); p->proposed = false; if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC: p->fd_while = 0; p->forward = true; if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC: p->fd_while = forward_delay(p); p->learn = true; if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_REROOTED_EXEC: p->re_root = false; if (check_selected_role_change(p, ROLE_ROOT)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC: p->role = ROLE_DESIGNATED; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT: if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else if (p->selected && !p->updt_info) { if (((p->sync && !p->synced) || (p->re_root && p->rr_while != 0) || p->disputed) && !p->oper_edge && (p->learn || p->forward)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC; } else if ((p->fd_while == 0 || p->agreed || p->oper_edge) && (p->rr_while == 0 || !p->re_root) && !p->sync && !p->learn) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC; } else if ((p->fd_while == 0 || p->agreed || p->oper_edge) && (p->rr_while == 0 || !p->re_root) && !p->sync && (p->learn && !p->forward)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC; } else if (!p->forward && !p->agreed && !p->proposing && !p->oper_edge) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC; } else if ((!p->learning && !p->forwarding && !p->synced) || (p->agreed && !p->synced) || (p->oper_edge && !p->synced) || (p->sync && p->synced)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC; } else if (p->rr_while == 0 && p->re_root) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC; } } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC: p->re_root = false; if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC: p->rr_while = 0; p->synced = true; p->sync = false; if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC: p->proposing = true; p->edge_delay_while = edge_delay(p); p->new_info = true; if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC: p->forward = true; p->fd_while = 0; p->agreed = p->send_rstp; if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC: p->learn = true; p->fd_while = forward_delay(p); if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC: p->learn = p->forward = p->disputed = false; p->fd_while = forward_delay(p); if (check_selected_role_change(p, ROLE_DESIGNATED)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC: p->fd_while = p->designated_times.forward_delay; p->synced = true; p->rr_while = 0; p->sync = p->re_root = false; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT: if (check_selected_role_change(p, ROLE_ALTERNATE)) { /* Global transition. */ } else if (p->selected && !p->updt_info) { if (p->rb_while != 2 * p->designated_times.hello_time && p->role == ROLE_BACKUP) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC; } else if ((p->fd_while != forward_delay(p)) || p->sync || p->re_root || !p->synced) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; } else if (p->proposed && !p->agree) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC; } else if ((all_synced(r) && !p->agree) || (p->proposed && p->agree)) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC; } } break; case PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC: p->proposed = false; p->agree = true; p->new_info = true; if (check_selected_role_change(p, ROLE_ALTERNATE)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC: set_sync_tree(p); p->proposed = false; if (check_selected_role_change(p, ROLE_ALTERNATE)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC: p->role = p->selected_role; p->learn = p->forward = false; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_BLOCK_PORT; /* no break */ case PORT_ROLE_TRANSITION_SM_BLOCK_PORT: if (check_selected_role_change(p, ROLE_ALTERNATE)) { /* Global transition. */ } else if (p->selected && !p->updt_info && !p->learning && !p->forwarding) { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; } break; case PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC: p->rb_while = 2 * p->designated_times.hello_time; if (check_selected_role_change(p, ROLE_ALTERNATE)) { /* Global transition. */ } else { p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_role_transition_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: Port_role_transition_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_role_transition_sm_state); } if (last_role != p->role) { VLOG_DBG("%s, port %u, port role ["RSTP_PORT_ID_FMT"] = %s", p->rstp->name, p->port_number, p->port_id, rstp_port_role_name(p->role)); } return 0; } /* [17.30 - Port state transition state machine] */ static void enable_learning(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* [17.21.6 enableLearning()] An implementation dependent procedure that * causes the Learning Process (7.8) to start learning from frames received * on the Port. The procedure does not complete until learning has been * enabled. */ rstp_port_set_state__(p, RSTP_LEARNING); } static void enable_forwarding(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* [17.21.5 enableForwarding()] An implementation dependent procedure that * causes the Forwarding Process (7.7) to start forwarding frames through * the Port. The procedure does not complete until forwarding has been * enabled. */ rstp_port_set_state__(p, RSTP_FORWARDING); } static void disable_learning(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* [17.21.4 - disableLearning()] An implementation dependent procedure that * causes the Learning Process (7.8) to stop learning from the source * address of frames received on the Port. The procedure does not complete * until learning has stopped. */ rstp_port_set_state__(p, RSTP_DISCARDING); } static void disable_forwarding(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { /* [17.21.3 - disableForwarding()] An implementation dependent procedure * that causes the Forwarding Process (7.7) to stop forwarding frames * through the Port. The procedure does not complete until forwarding has * stopped. */ rstp_port_set_state__(p, RSTP_DISCARDING); } static int port_state_transition_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum port_state_transition_state_machine old_state; struct rstp *r; old_state = p->port_state_transition_sm_state; r = p->rstp; switch (p->port_state_transition_sm_state) { case PORT_STATE_TRANSITION_SM_INIT: if (r->begin) { p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; } break; case PORT_STATE_TRANSITION_SM_DISCARDING_EXEC: disable_learning(p); p->learning = false; disable_forwarding(p); p->forwarding = false; p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING; /* no break */ case PORT_STATE_TRANSITION_SM_DISCARDING: if (p->learn) { p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING_EXEC; } break; case PORT_STATE_TRANSITION_SM_LEARNING_EXEC: enable_learning(p); p->learning = true; p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_LEARNING; /* no break */ case PORT_STATE_TRANSITION_SM_LEARNING: if (!p->learn) { p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; } else if (p->forward) { p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_FORWARDING_EXEC; } break; case PORT_STATE_TRANSITION_SM_FORWARDING_EXEC: enable_forwarding(p); p->forwarding = true; p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_FORWARDING; /* no break */ case PORT_STATE_TRANSITION_SM_FORWARDING: if (!p->forward) { p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_DISCARDING_EXEC; } break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->port_state_transition_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: Port_state_transition_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->port_state_transition_sm_state); } return 0; } /* [17.31 - Topology Change state machine] */ static void new_tc_while(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; r = p->rstp; if (p->tc_while == 0 && p->send_rstp == true) { p->tc_while = r->bridge_hello_time + 1; p->new_info = true; } else if (p->tc_while == 0 && p->send_rstp == false) { p->tc_while = r->bridge_max_age + r->bridge_forward_delay; } } /* [17.21.18 setTcPropTree()] * Sets tcprop for all Ports except the Port that called the procedure. */ static void set_tc_prop_tree(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *r; struct rstp_port *p1; r = p->rstp; HMAP_FOR_EACH (p1, node, &r->ports) { /* Set tc_prop on every port, except the one calling this * function. */ if (p1->port_number != p->port_number) { p1->tc_prop = true; } } } static void set_tc_prop_bridge(struct rstp_port *p) /* not specified in 802.1D-2004. */ OVS_REQUIRES(rstp_mutex) { set_tc_prop_tree(p); /* see 802.1w-2001. */ } static int topology_change_sm(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { enum topology_change_state_machine old_state; struct rstp *r; old_state = p->topology_change_sm_state; r = p->rstp; switch (p->topology_change_sm_state) { case TOPOLOGY_CHANGE_SM_INIT: if (r->begin) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC; } break; case TOPOLOGY_CHANGE_SM_INACTIVE_EXEC: p->fdb_flush = true; p->tc_while = 0; p->tc_ack = false; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE; /* no break */ case TOPOLOGY_CHANGE_SM_INACTIVE: if (p->learn && !p->fdb_flush) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; } break; case TOPOLOGY_CHANGE_SM_LEARNING_EXEC: p->rcvd_tc = p->rcvd_tcn = p->rcvd_tc_ack = false; p->tc_prop = p->rcvd_tc_ack = false; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING; /* no break */ case TOPOLOGY_CHANGE_SM_LEARNING: if (p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED && !(p->learn || p->learning) && !(p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop)) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INACTIVE_EXEC; } else if (p->rcvd_tc || p->rcvd_tcn || p->rcvd_tc_ack || p->tc_prop) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; } else if ((p->role == ROLE_ROOT || p->role == ROLE_DESIGNATED) && p->forward && !p->oper_edge) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_DETECTED_EXEC; } break; case TOPOLOGY_CHANGE_SM_DETECTED_EXEC: new_tc_while(p); set_tc_prop_tree(p); p->new_info = true; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE_EXEC; /* no break */ case TOPOLOGY_CHANGE_SM_ACTIVE_EXEC: p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; /* no break */ case TOPOLOGY_CHANGE_SM_ACTIVE: if ((p->role != ROLE_ROOT && p->role != ROLE_DESIGNATED) || p->oper_edge) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_LEARNING_EXEC; } else if (p->rcvd_tcn) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC; } else if (p->rcvd_tc) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC; } else if (p->tc_prop && !p->oper_edge) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC; } else if (p->rcvd_tc_ack) { p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC; } break; case TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC: p->tc_while = 0; p->rcvd_tc_ack = false; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; break; case TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC: new_tc_while(p); p->fdb_flush = true; p->tc_prop = false; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; break; case TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC: p->rcvd_tcn = p->rcvd_tc = false; if (p->role == ROLE_DESIGNATED) { p->tc_ack = true; } set_tc_prop_bridge(p); p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_ACTIVE; break; case TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC: new_tc_while(p); p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC; break; default: OVS_NOT_REACHED(); /* no break */ } if (old_state != p->topology_change_sm_state) { r->changes = true; VLOG_DBG("%s, port %u: Topology_change_sm %d -> %d", p->rstp->name, p->port_number, old_state, p->topology_change_sm_state); } return 0; } /**************************************************************************** * [17.6] Priority vector calculation helper functions ****************************************************************************/ /* compare_rstp_priority_vectors() compares two struct rstp_priority_vectors * and returns a value indicating if the first rstp_priority_vector is * superior, same or inferior to the second one. * * Zero return value indicates INFERIOR, a non-zero return value indicates * SUPERIOR. When it makes a difference the non-zero return value SAME * indicates the priority vectors are identical (a subset of SUPERIOR). */ static enum vector_comparison compare_rstp_priority_vectors(const struct rstp_priority_vector *v1, const struct rstp_priority_vector *v2) { VLOG_DBG("v1: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d", RSTP_ID_ARGS(v1->root_bridge_id), v1->root_path_cost, RSTP_ID_ARGS(v1->designated_bridge_id), v1->designated_port_id, v1->bridge_port_id); VLOG_DBG("v2: "RSTP_ID_FMT", %u, "RSTP_ID_FMT", %d, %d", RSTP_ID_ARGS(v2->root_bridge_id), v2->root_path_cost, RSTP_ID_ARGS(v2->designated_bridge_id), v2->designated_port_id, v2->bridge_port_id); /* [17.6] * This message priority vector is superior to the port priority vector and * will replace it if, and only if, the message priority vector is better * than the port priority vector, or the message has been transmitted from * the same Designated Bridge and Designated Port as the port priority * vector, i.e., if the following is true: * * ((RD < RootBridgeID)) || * ((RD == RootBridgeID) && (RPCD < RootPathCost)) || * ((RD == RootBridgeID) && (RPCD == RootPathCost) && * (D < designated_bridge_id)) || * ((RD == RootBridgeID) && (RPCD == RootPathCost) && * (D == designated_bridge_id) && (PD < designated_port_id)) || * ((D == designated_bridge_id.BridgeAddress) && * (PD == designated_port_id.PortNumber)) */ if ((v1->root_bridge_id < v2->root_bridge_id) || (v1->root_bridge_id == v2->root_bridge_id && v1->root_path_cost < v2->root_path_cost) || (v1->root_bridge_id == v2->root_bridge_id && v1->root_path_cost == v2->root_path_cost && v1->designated_bridge_id < v2->designated_bridge_id) || (v1->root_bridge_id == v2->root_bridge_id && v1->root_path_cost == v2->root_path_cost && v1->designated_bridge_id == v2->designated_bridge_id && v1->designated_port_id < v2->designated_port_id) || (v1->designated_bridge_id == v2->designated_bridge_id && v1->designated_port_id == v2->designated_port_id)) { /* SAME is a subset of SUPERIOR. */ if (v1->root_bridge_id == v2->root_bridge_id && v1->root_path_cost == v2->root_path_cost && v1->designated_bridge_id == v2->designated_bridge_id && v1->designated_port_id == v2->designated_port_id) { if (v1->bridge_port_id < v2->bridge_port_id) { VLOG_DBG("superior"); return SUPERIOR; } else if (v1->bridge_port_id > v2->bridge_port_id) { VLOG_DBG("inferior"); return INFERIOR; } VLOG_DBG("superior_same"); return SAME; } VLOG_DBG("superior"); return SUPERIOR; } VLOG_DBG("inferior"); return INFERIOR; } static bool rstp_times_equal(struct rstp_times *t1, struct rstp_times *t2) { return t1->forward_delay == t2->forward_delay && t1->hello_time == t2->hello_time && t1->max_age == t2->max_age && t1->message_age == t2->message_age; } openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-dpdk.h0000644000000000000000000000013213534540071016770 xustar0030 mtime=1567801401.441681464 30 atime=1567801402.081686164 30 ctime=1567801424.993854989 openvswitch-2.5.9/lib/netdev-dpdk.h0000644000175000017500000000333113534540071020456 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETDEV_DPDK_H #define NETDEV_DPDK_H #include struct dp_packet; #ifdef DPDK_NETDEV #include #include #include #include #include #include #include #include #include #include #include #include #define NON_PMD_CORE_ID LCORE_ID_ANY int dpdk_init(int argc, char **argv); void netdev_dpdk_register(void); void free_dpdk_buf(struct dp_packet *); int pmd_thread_setaffinity_cpu(unsigned cpu); #else #define NON_PMD_CORE_ID UINT32_MAX #include "util.h" static inline int dpdk_init(int argc, char **argv) { if (argc >= 2 && !strcmp(argv[1], "--dpdk")) { ovs_fatal(0, "DPDK support not built into this copy of Open vSwitch."); } return 0; } static inline void netdev_dpdk_register(void) { /* Nothing */ } static inline void free_dpdk_buf(struct dp_packet *buf OVS_UNUSED) { /* Nothing */ } static inline int pmd_thread_setaffinity_cpu(unsigned cpu OVS_UNUSED) { return 0; } #endif /* DPDK_NETDEV */ #endif openvswitch-2.5.9/lib/PaxHeaders.82075/service-syn.man0000644000000000000000000000013113534540071017355 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 29 ctime=1567801423.73784573 openvswitch-2.5.9/lib/service-syn.man0000644000175000017500000000011013534540071021034 0ustar00jpettitjpettit00000000000000.IP "Service options:" [\fB\-\-service\fR] [\fB\-\-service\-monitor\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/sset.c0000644000000000000000000000013213534540071015534 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.885854193 openvswitch-2.5.9/lib/sset.c0000644000175000017500000002037413534540071017230 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "sset.h" #include "hash.h" static uint32_t hash_name__(const char *name, size_t length) { return hash_bytes(name, length, 0); } static uint32_t hash_name(const char *name) { return hash_name__(name, strlen(name)); } static struct sset_node * sset_find__(const struct sset *set, const char *name, size_t hash) { struct sset_node *node; HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash, &set->map) { if (!strcmp(node->name, name)) { return node; } } return NULL; } static struct sset_node * sset_add__(struct sset *set, const char *name, size_t length, size_t hash) { struct sset_node *node = xmalloc(length + sizeof *node); memcpy(node->name, name, length + 1); hmap_insert(&set->map, &node->hmap_node, hash); return node; } /* Initializes 'set' as an empty set of strings. */ void sset_init(struct sset *set) { hmap_init(&set->map); } /* Destroys 'sets'. */ void sset_destroy(struct sset *set) { if (set) { sset_clear(set); hmap_destroy(&set->map); } } /* Initializes 'set' to contain the same strings as 'orig'. */ void sset_clone(struct sset *set, const struct sset *orig) { struct sset_node *node; sset_init(set); HMAP_FOR_EACH (node, hmap_node, &orig->map) { sset_add__(set, node->name, strlen(node->name), node->hmap_node.hash); } } /* Exchanges the contents of 'a' and 'b'. */ void sset_swap(struct sset *a, struct sset *b) { hmap_swap(&a->map, &b->map); } /* Adjusts 'set' so that it is still valid after it has been moved around in * memory (e.g. due to realloc()). */ void sset_moved(struct sset *set) { hmap_moved(&set->map); } /* Returns true if 'set' contains no strings, false if it contains at least one * string. */ bool sset_is_empty(const struct sset *set) { return hmap_is_empty(&set->map); } /* Returns the number of strings in 'set'. */ size_t sset_count(const struct sset *set) { return hmap_count(&set->map); } /* Adds 'name' to 'set'. If 'name' is new, returns the new sset_node; * otherwise (if a copy of 'name' already existed in 'set'), returns NULL. */ struct sset_node * sset_add(struct sset *set, const char *name) { size_t length = strlen(name); uint32_t hash = hash_name__(name, length); return (sset_find__(set, name, hash) ? NULL : sset_add__(set, name, length, hash)); } /* Adds a copy of 'name' to 'set' and frees 'name'. * * If 'name' is new, returns the new sset_node; otherwise (if a copy of 'name' * already existed in 'set'), returns NULL. */ struct sset_node * sset_add_and_free(struct sset *set, char *name) { struct sset_node *node = sset_add(set, name); free(name); return node; } /* Adds 'name' to 'set'. Assert-fails if a copy of 'name' was already in * 'set'. */ void sset_add_assert(struct sset *set, const char *name) { bool added OVS_UNUSED = sset_add(set, name); ovs_assert(added); } /* Adds a copy of each of the 'n' names in 'names' to 'set'. */ void sset_add_array(struct sset *set, char **names, size_t n) { size_t i; for (i = 0; i < n; i++) { sset_add(set, names[i]); } } /* Removes all of the strings from 'set'. */ void sset_clear(struct sset *set) { const char *name, *next; SSET_FOR_EACH_SAFE (name, next, set) { sset_delete(set, SSET_NODE_FROM_NAME(name)); } } /* Deletes 'node' from 'set' and frees 'node'. */ void sset_delete(struct sset *set, struct sset_node *node) { hmap_remove(&set->map, &node->hmap_node); free(node); } /* Searches for 'name' in 'set'. If found, deletes it and returns true. If * not found, returns false without modifying 'set'. */ bool sset_find_and_delete(struct sset *set, const char *name) { struct sset_node *node = sset_find(set, name); if (node) { sset_delete(set, node); } return node != NULL; } /* Searches for 'name' in 'set' and deletes it. Assert-fails if 'name' is not * in 'set'. */ void sset_find_and_delete_assert(struct sset *set, const char *name) { bool deleted OVS_UNUSED = sset_find_and_delete(set, name); ovs_assert(deleted); } /* Removes a string from 'set' and returns a copy of it. The caller must free * the returned string (with free()). * * 'set' must not be empty. * * This is not a very good way to iterate through an sset: it copies each name * and it takes O(n**2) time to remove all the names. Use SSET_FOR_EACH_SAFE * instead, if you can. */ char * sset_pop(struct sset *set) { const char *name = SSET_FIRST(set); char *copy = xstrdup(name); sset_delete(set, SSET_NODE_FROM_NAME(name)); return copy; } /* Searches for 'name' in 'set'. Returns its node, if found, otherwise a null * pointer. */ struct sset_node * sset_find(const struct sset *set, const char *name) { return sset_find__(set, name, hash_name(name)); } /* Returns true if 'set' contains a copy of 'name', false otherwise. */ bool sset_contains(const struct sset *set, const char *name) { return sset_find(set, name) != NULL; } /* Returns true if 'a' and 'b' contain the same strings, false otherwise. */ bool sset_equals(const struct sset *a, const struct sset *b) { struct sset_node *node; if (sset_count(a) != sset_count(b)) { return false; } HMAP_FOR_EACH (node, hmap_node, &a->map) { if (!sset_find__(b, node->name, node->hmap_node.hash)) { return false; } } return true; } /* Returns the next node in 'set' in hash order, or NULL if no nodes remain in * 'set'. Uses '*bucketp' and '*offsetp' to determine where to begin * iteration, and stores new values to pass on the next iteration into them * before returning. * * It's better to use plain SSET_FOR_EACH and related functions, since they are * faster and better at dealing with ssets that change during iteration. * * Before beginning iteration, store 0 into '*bucketp' and '*offsetp'. */ struct sset_node * sset_at_position(const struct sset *set, uint32_t *bucketp, uint32_t *offsetp) { struct hmap_node *hmap_node; hmap_node = hmap_at_position(&set->map, bucketp, offsetp); return SSET_NODE_FROM_HMAP_NODE(hmap_node); } /* Replaces 'a' by the intersection of 'a' and 'b'. That is, removes from 'a' * all of the strings that are not also in 'b'. */ void sset_intersect(struct sset *a, const struct sset *b) { const char *name, *next; SSET_FOR_EACH_SAFE (name, next, a) { if (!sset_contains(b, name)) { sset_delete(a, SSET_NODE_FROM_NAME(name)); } } } /* Returns a null-terminated array of pointers to the strings in 'set', in no * particular order. The caller must free the returned array when it is no * longer needed, but the strings in the array belong to 'set' and thus must * not be modified or freed. */ const char ** sset_array(const struct sset *set) { size_t n = sset_count(set); const char **array; const char *s; size_t i; array = xmalloc(sizeof *array * (n + 1)); i = 0; SSET_FOR_EACH (s, set) { array[i++] = s; } ovs_assert(i == n); array[n] = NULL; return array; } static int compare_string_pointers(const void *a_, const void *b_) { const char *const *a = a_; const char *const *b = b_; return strcmp(*a, *b); } /* Returns a null-terminated array of pointers to the strings in 'set', sorted * alphabetically. The caller must free the returned array when it is no * longer needed, but the strings in the array belong to 'set' and thus must * not be modified or freed. */ const char ** sset_sort(const struct sset *set) { const char **array = sset_array(set); qsort(array, sset_count(set), sizeof *array, compare_string_pointers); return array; } openvswitch-2.5.9/lib/PaxHeaders.82075/stdio.c0000644000000000000000000000013213534540071015700 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.897854281 openvswitch-2.5.9/lib/stdio.c0000644000175000017500000000250113534540071017364 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #ifdef _WIN32 #undef snprintf #undef vsnprintf int ovs_snprintf(char *s, size_t n, const char *format, ... ) { va_list args; int len; va_start(args, format); len = ovs_vsnprintf(s, n, format, args); va_end(args); return len; } int ovs_vsnprintf(char *s, size_t n, const char *format, va_list args) { int needed = _vscprintf(format, args); if (s && n) { vsnprintf(s, n, format, args); s[n - 1] = '\0'; } return needed; } int fseeko(FILE *stream, off_t offset, int whence) { int error; error = _fseeki64(stream, offset, whence); if (error) { return -1; } return error; } #endif /* _WIN32 */ openvswitch-2.5.9/lib/PaxHeaders.82075/multipath.h0000644000000000000000000000013213534540071016572 xustar0030 mtime=1567801401.421681316 30 atime=1567801402.077686135 30 ctime=1567801424.769853338 openvswitch-2.5.9/lib/multipath.h0000644000175000017500000000244013534540071020260 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MULTIPATH_H #define MULTIPATH_H 1 #include #include "compiler.h" #include "ofp-errors.h" struct ds; struct flow; struct flow_wildcards; struct nx_action_multipath; struct ofpact_multipath; struct ofpbuf; /* NXAST_MULTIPATH helper functions. */ enum ofperr multipath_check(const struct ofpact_multipath *, const struct flow *); void multipath_execute(const struct ofpact_multipath *, struct flow *, struct flow_wildcards *); char *multipath_parse(struct ofpact_multipath *, const char *) OVS_WARN_UNUSED_RESULT; void multipath_format(const struct ofpact_multipath *, struct ds *); #endif /* multipath.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/cfm.h0000644000000000000000000000013013534540071015326 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 28 ctime=1567801424.6698526 openvswitch-2.5.9/lib/cfm.h0000644000175000017500000000753713534540071017032 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2010, 2011, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CFM_H #define CFM_H 1 #include #include "hmap.h" #include "openvswitch/types.h" #include "packets.h" struct flow; struct dp_packet; struct netdev; struct flow_wildcards; #define CFM_RANDOM_VLAN UINT16_MAX #define CFM_FAULT_REASONS \ CFM_FAULT_REASON(RECV, recv) \ CFM_FAULT_REASON(RDI, rdi) \ CFM_FAULT_REASON(MAID, maid) \ CFM_FAULT_REASON(LOOPBACK, loopback) \ CFM_FAULT_REASON(OVERFLOW, overflow) \ CFM_FAULT_REASON(OVERRIDE, override) enum cfm_fault_bit_index { #define CFM_FAULT_REASON(NAME, STR) CFM_FAULT_INDEX_##NAME, CFM_FAULT_REASONS #undef CFM_FAULT_REASON CFM_FAULT_N_REASONS }; enum cfm_fault_reason { #define CFM_FAULT_REASON(NAME, STR) \ CFM_FAULT_##NAME = 1 << CFM_FAULT_INDEX_##NAME, CFM_FAULT_REASONS #undef CFM_FAULT_REASON }; struct cfm_settings { uint64_t mpid; /* The MPID of this CFM. */ int interval; /* The requested transmission interval. */ bool extended; /* Run in extended mode. */ bool demand; /* Run in demand mode. */ bool opup; /* Operational State. */ uint16_t ccm_vlan; /* CCM Vlan tag. Zero if none. CFM_RANDOM_VLAN if random. */ uint8_t ccm_pcp; /* CCM Priority. Zero if none. */ bool check_tnl_key; /* Verify inbound packet key? */ }; /* CFM status query. */ struct cfm_status { /* 0 if not faulted, otherwise a combination of one or more reasons. */ enum cfm_fault_reason faults; /* 0 if the remote CFM endpoint is operationally down, * 1 if the remote CFM endpoint is operationally up, * -1 if we don't know because the remote CFM endpoint is not in extended * mode. */ int remote_opstate; uint64_t flap_count; /* Ordinarily a "health status" in the range 0...100 inclusive, with 0 * being worst and 100 being best, or -1 if the health status is not * well-defined. */ int health; /* MPIDs of remote maintenance points whose CCMs have been received. */ uint64_t *rmps; size_t n_rmps; }; void cfm_init(void); struct cfm *cfm_create(const struct netdev *); struct cfm *cfm_ref(const struct cfm *); void cfm_unref(struct cfm *); void cfm_run(struct cfm *); bool cfm_should_send_ccm(struct cfm *); void cfm_compose_ccm(struct cfm *, struct dp_packet *, const struct eth_addr eth_src); long long int cfm_wait(struct cfm *); bool cfm_configure(struct cfm *, const struct cfm_settings *); void cfm_set_netdev(struct cfm *, const struct netdev *); bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *, struct flow_wildcards *); void cfm_process_heartbeat(struct cfm *, const struct dp_packet *packet); bool cfm_check_status_change(struct cfm *); int cfm_get_fault(const struct cfm *); uint64_t cfm_get_flap_count(const struct cfm *); int cfm_get_health(const struct cfm *); int cfm_get_opup(const struct cfm *); void cfm_get_remote_mpids(const struct cfm *, uint64_t **rmps, size_t *n_rmps); void cfm_get_status(const struct cfm *, struct cfm_status *); const char *cfm_fault_reason_to_str(int fault); long long int cfm_wake_time(struct cfm*); #endif /* cfm.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/random.h0000644000000000000000000000013213534540071016043 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.857853986 openvswitch-2.5.9/lib/random.h0000644000175000017500000000211613534540071017531 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RANDOM_H #define RANDOM_H 1 #include #include void random_init(void); void random_set_seed(uint32_t); void random_bytes(void *, size_t); uint32_t random_uint32(void); uint64_t random_uint64(void); static inline int random_range(int max) { return random_uint32() % max; } static inline uint8_t random_uint8(void) { return random_uint32(); } static inline uint16_t random_uint16(void) { return random_uint32(); } #endif /* random.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/lockfile.h0000644000000000000000000000013013534540071016351 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.757853249 openvswitch-2.5.9/lib/lockfile.h0000644000175000017500000000153413534540071020044 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LOCKFILE_H #define LOCKFILE_H 1 struct lockfile; char *lockfile_name(const char *file); int lockfile_lock(const char *file, struct lockfile **); void lockfile_unlock(struct lockfile *); void lockfile_postfork(void); #endif /* lib/lockfile.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/daemon.h0000644000000000000000000000013213534540071016026 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.693852778 openvswitch-2.5.9/lib/daemon.h0000644000175000017500000001452113534540071017517 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DAEMON_H #define DAEMON_H 1 #include #include #include /* This file provides an interface for utilities to run in the background * as daemons on POSIX platforms like Linux or as services on Windows platform. * Some of the functionalities defined in this file are only applicable to * POSIX platforms and some are applicable only on Windows. As such, the * function definitions unique to each platform are separated out with * ifdef macros. More descriptive comments on individual functions are provided * in daemon-unix.c (for POSIX platforms) and daemon-windows.c (for Windows). * The DAEMON_OPTION_ENUMS, DAEMON_LONG_OPTIONS and DAEMON_OPTION_HANDLERS * macros are useful for parsing command-line options in individual utilities. * For e.g., the command-line option "--monitor" is recognized on Linux * and results in calling the daemon_set_monitor() function. The same option is * not recognized on Windows platform. */ #ifndef _WIN32 #define DAEMON_OPTION_ENUMS \ OPT_DETACH, \ OPT_NO_CHDIR, \ OPT_OVERWRITE_PIDFILE, \ OPT_PIDFILE, \ OPT_MONITOR, \ OPT_USER_GROUP #define DAEMON_LONG_OPTIONS \ {"detach", no_argument, NULL, OPT_DETACH}, \ {"no-chdir", no_argument, NULL, OPT_NO_CHDIR}, \ {"pidfile", optional_argument, NULL, OPT_PIDFILE}, \ {"overwrite-pidfile", no_argument, NULL, OPT_OVERWRITE_PIDFILE}, \ {"monitor", no_argument, NULL, OPT_MONITOR}, \ {"user", required_argument, NULL, OPT_USER_GROUP} #define DAEMON_OPTION_HANDLERS \ case OPT_DETACH: \ set_detach(); \ break; \ \ case OPT_NO_CHDIR: \ set_no_chdir(); \ break; \ \ case OPT_PIDFILE: \ set_pidfile(optarg); \ break; \ \ case OPT_OVERWRITE_PIDFILE: \ ignore_existing_pidfile(); \ break; \ \ case OPT_MONITOR: \ daemon_set_monitor(); \ break; \ \ case OPT_USER_GROUP: \ daemon_set_new_user(optarg); \ break; void set_detach(void); void daemon_set_monitor(void); void set_no_chdir(void); void ignore_existing_pidfile(void); pid_t read_pidfile(const char *name); #else #define DAEMON_OPTION_ENUMS \ OPT_DETACH, \ OPT_NO_CHDIR, \ OPT_PIDFILE, \ OPT_PIPE_HANDLE, \ OPT_SERVICE, \ OPT_SERVICE_MONITOR, \ OPT_USER_GROUP #define DAEMON_LONG_OPTIONS \ {"detach", no_argument, NULL, OPT_DETACH}, \ {"no-chdir", no_argument, NULL, OPT_NO_CHDIR}, \ {"pidfile", optional_argument, NULL, OPT_PIDFILE}, \ {"pipe-handle", required_argument, NULL, OPT_PIPE_HANDLE}, \ {"service", no_argument, NULL, OPT_SERVICE}, \ {"service-monitor", no_argument, NULL, OPT_SERVICE_MONITOR}, \ {"user", required_argument, NULL, OPT_USER_GROUP} #define DAEMON_OPTION_HANDLERS \ case OPT_DETACH: \ break; \ \ case OPT_NO_CHDIR: \ break; \ \ case OPT_PIDFILE: \ set_pidfile(optarg); \ break; \ \ case OPT_PIPE_HANDLE: \ set_pipe_handle(optarg); \ break; \ \ case OPT_SERVICE: \ break; \ \ case OPT_SERVICE_MONITOR: \ break; \ \ case OPT_USER_GROUP: \ daemon_set_new_user(optarg); void control_handler(DWORD request); void set_pipe_handle(const char *pipe_handle); #endif /* _WIN32 */ bool get_detach(void); void daemon_save_fd(int fd); void daemonize(void); void daemonize_start(bool access_datapath); void daemonize_complete(void); void daemon_set_new_user(const char * user_spec); void daemon_become_new_user(bool access_datapath); void daemon_usage(void); void service_start(int *argcp, char **argvp[]); void service_stop(void); bool should_service_stop(void); void set_pidfile(const char *name); void close_standard_fds(void); #endif /* daemon.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/unixctl.man0000644000000000000000000000013213534540071016575 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801423.745845788 openvswitch-2.5.9/lib/unixctl.man0000644000175000017500000000150213534540071020261 0ustar00jpettitjpettit00000000000000.IP "\fB\-\-unixctl=\fIsocket\fR" Sets the name of the control socket on which \fB\*(PN\fR listens for runtime management commands (see \fBRUNTIME MANAGEMENT COMMANDS\fR, below). If \fIsocket\fR does not begin with \fB/\fR, it is interpreted as relative to \fB@RUNDIR@\fR. If \fB\-\-unixctl\fR is not used at all, the default socket is \fB@RUNDIR@/\*(PN.\fIpid\fB.ctl\fR, where \fIpid\fR is \fB\*(PN\fR's process ID. .IP On Windows, uses a kernel chosen TCP port on the localhost to listen for runtime management commands. The kernel chosen TCP port value is written in a file whose absolute path is pointed by \fIsocket\fR. If \fB\-\-unixctl\fR is not used at all, the file is created as \fB\*(PN.ctl\fR in the configured \fIOVS_RUNDIR\fR directory. .IP Specifying \fBnone\fR for \fIsocket\fR disables the control socket feature. openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-router.h0000644000000000000000000000013113534540071016707 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 29 ctime=1567801424.82585375 openvswitch-2.5.9/lib/ovs-router.h0000644000175000017500000000222113534540071020373 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_TNL_ROUTER_H #define OVS_TNL_ROUTER_H 1 #include #include "util.h" #ifdef __cplusplus extern "C" { #endif bool ovs_router_lookup(const struct in6_addr *ip_dst, char out_dev[], struct in6_addr *gw); bool ovs_router_lookup4(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw); void ovs_router_init(void); void ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen, const char output_bridge[], const struct in6_addr *gw); void ovs_router_flush(void); #ifdef __cplusplus } #endif #endif openvswitch-2.5.9/lib/PaxHeaders.82075/route-table-bsd.c0000644000000000000000000000013213534540071017547 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801425.005855078 openvswitch-2.5.9/lib/route-table-bsd.c0000644000175000017500000001232613534540071021241 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Ed Maste. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "route-table.h" #include #include #include #include #include #include #include #include #include #include #include "ovs-router.h" #include "packets.h" #include "openvswitch/vlog.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(route_table_bsd); bool route_table_fallback_lookup(ovs_be32 ip, char name[], ovs_be32 *gw) { struct { struct rt_msghdr rtm; char space[512]; } rtmsg; struct rt_msghdr *rtm = &rtmsg.rtm; struct sockaddr_dl *ifp = NULL; struct sockaddr_in *sin; struct sockaddr *sa; static int seq; int i, namelen, rtsock; ssize_t len; const pid_t pid = getpid(); bool got_ifp = false; unsigned int retry_count = 5; /* arbitrary */ VLOG_DBG("looking route up for " IP_FMT " pid %" PRIuMAX, IP_ARGS(ip), (uintmax_t)pid); rtsock = socket(PF_ROUTE, SOCK_RAW, 0); if (rtsock < 0) return false; retry: memset(&rtmsg, 0, sizeof(rtmsg)); rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in); rtm->rtm_version = RTM_VERSION; rtm->rtm_type = RTM_GET; rtm->rtm_addrs = RTA_DST | RTA_IFP; rtm->rtm_seq = ++seq; sin = (struct sockaddr_in *)(rtm + 1); sin->sin_len = len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_addr.s_addr = ip; len = write(rtsock, (char *)&rtmsg, rtm->rtm_msglen); if (len == -1) { if (errno == ENOBUFS && retry_count-- > 0) { VLOG_INFO("Recoverable error writing to routing socket: %s", ovs_strerror(errno)); usleep(500 * 1000); /* arbitrary */ goto retry; } VLOG_ERR("Error writing to routing socket: %s", ovs_strerror(errno)); close(rtsock); return false; } if (len != rtm->rtm_msglen) { VLOG_ERR("Short write to routing socket"); close(rtsock); return false; } do { struct pollfd pfd; int ret; memset(&pfd, 0, sizeof(pfd)); pfd.fd = rtsock; pfd.events = POLLIN; /* * The timeout value below is somehow arbitrary. * It's to detect the lost of routing messages due to * buffer exhaustion etc. The routing socket is not * reliable. */ ret = poll(&pfd, 1, 500); if (ret == -1) { VLOG_ERR("Error polling on routing socket: %s", ovs_strerror(errno)); close(rtsock); return false; } if (ret == 0) { if (retry_count-- > 0) { VLOG_INFO("Timeout; resending routing message"); goto retry; } close(rtsock); return false; } len = read(rtsock, (char *)&rtmsg, sizeof(rtmsg)); if (len > 0) { VLOG_DBG("got rtmsg pid %" PRIuMAX " seq %d", (uintmax_t)rtmsg.rtm.rtm_pid, rtmsg.rtm.rtm_seq); } } while (len > 0 && (rtmsg.rtm.rtm_seq != seq || rtmsg.rtm.rtm_pid != pid)); close(rtsock); if (len == -1) { VLOG_ERR("Error reading from routing socket: %s", ovs_strerror(errno)); return false; } *gw = 0; sa = (struct sockaddr *)(rtm + 1); for (i = 1; i; i <<= 1) { if (rtm->rtm_addrs & i) { if (i == RTA_IFP && sa->sa_family == AF_LINK && ALIGNED_CAST(struct sockaddr_dl *, sa)->sdl_nlen) { ifp = ALIGNED_CAST(struct sockaddr_dl *, sa); namelen = ifp->sdl_nlen; if (namelen > IFNAMSIZ - 1) namelen = IFNAMSIZ - 1; memcpy(name, ifp->sdl_data, namelen); name[namelen] = '\0'; VLOG_DBG("got ifp %s", name); got_ifp = true; } else if (i == RTA_GATEWAY && sa->sa_family == AF_INET) { const struct sockaddr_in *sin_dst = ALIGNED_CAST(struct sockaddr_in *, sa); *gw = sin_dst->sin_addr.s_addr; VLOG_DBG("got gateway " IP_FMT, IP_ARGS(*gw)); } #if defined(__FreeBSD__) sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); #elif defined(__NetBSD__) sa = (struct sockaddr *)((char *)sa + RT_ROUNDUP(sa->sa_len)); #else #error unimplemented #endif } } return got_ifp; } uint64_t route_table_get_change_seq(void) { return 0; } void route_table_init(void) { ovs_router_init(); } void route_table_run(void) { } void route_table_wait(void) { } openvswitch-2.5.9/lib/PaxHeaders.82075/vlog-unixctl.man0000644000000000000000000000013213534540071017542 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.749845818 openvswitch-2.5.9/lib/vlog-unixctl.man0000644000175000017500000000567513534540071021245 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .SS "VLOG COMMANDS" These commands manage \fB\*(PN\fR's logging settings. .IP "\fBvlog/set\fR [\fIspec\fR]" Sets logging levels. Without any \fIspec\fR, sets the log level for every module and destination to \fBdbg\fR. Otherwise, \fIspec\fR is a list of words separated by spaces or commas or colons, up to one from each category below: . .RS .IP \(bu A valid module name, as displayed by the \fBvlog/list\fR command on \fBovs\-appctl\fR(8), limits the log level change to the specified module. . .IP \(bu \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level change to only to the system log, to the console, or to a file, respectively. .IP On Windows platform, \fBsyslog\fR is accepted as a word and is only useful along with the \fB\-\-syslog\-target\fR option (the word has no effect otherwise). . .IP \(bu \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or \fBdbg\fR, to control the log level. Messages of the given severity or higher will be logged, and messages of lower severity will be filtered out. \fBoff\fR filters out all messages. See \fBovs\-appctl\fR(8) for a definition of each log level. .RE . .IP Case is not significant within \fIspec\fR. .IP Regardless of the log levels set for \fBfile\fR, logging to a file will not take place unless \fB\*(PN\fR was invoked with the \fB\-\-log\-file\fR option. .IP For compatibility with older versions of OVS, \fBany\fR is accepted as a word but has no effect. .RE .IP "\fBvlog/set PATTERN:\fIdestination\fB:\fIpattern\fR" Sets the log pattern for \fIdestination\fR to \fIpattern\fR. Refer to \fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR. . .IP "\fBvlog/list\fR" Lists the supported logging modules and their current levels. . .IP "\fBvlog/list-pattern\fR" Lists logging patterns used for each destination. . .IP "\fBvlog/reopen\fR" Causes \fB\*(PN\fR to close and reopen its log file. (This is useful after rotating log files, to cause a new log file to be used.) .IP This has no effect unless \fB\*(PN\fR was invoked with the \fB\-\-log\-file\fR option. . .IP "\fBvlog/disable\-rate\-limit \fR[\fImodule\fR]..." .IQ "\fBvlog/enable\-rate\-limit \fR[\fImodule\fR]..." By default, \fB\*(PN\fR limits the rate at which certain messages can be logged. When a message would appear more frequently than the limit, it is suppressed. This saves disk space, makes logs easier to read, and speeds up execution, but occasionally troubleshooting requires more detail. Therefore, \fBvlog/disable\-rate\-limit\fR allows rate limits to be disabled at the level of an individual log module. Specify one or more module names, as displayed by the \fBvlog/list\fR command. Specifying either no module names at all or the keyword \fBany\fR disables rate limits for every log module. . .IP The \fBvlog/enable\-rate\-limit\fR command, whose syntax is the same as \fBvlog/disable\-rate\-limit\fR, can be used to re-enable a rate limit that was previously disabled. openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-vport.c0000644000000000000000000000013213534540071017213 xustar0030 mtime=1567801401.457681581 30 atime=1567801402.081686164 30 ctime=1567801424.773853367 openvswitch-2.5.9/lib/netdev-vport.c0000644000175000017500000013555713534540071020721 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netdev-vport.h" #include #include #include #include #include #include #include #include "byte-order.h" #include "csum.h" #include "daemon.h" #include "dirs.h" #include "dpif.h" #include "dp-packet.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" #include "hmap.h" #include "list.h" #include "netdev-provider.h" #include "odp-netlink.h" #include "dp-packet.h" #include "ovs-router.h" #include "packets.h" #include "poll-loop.h" #include "route-table.h" #include "shash.h" #include "socket-util.h" #include "openvswitch/vlog.h" #include "unaligned.h" #include "unixctl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(netdev_vport); static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5); #define GENEVE_DST_PORT 6081 #define VXLAN_DST_PORT 4789 #define LISP_DST_PORT 4341 #define STT_DST_PORT 7471 #define VXLAN_HLEN (sizeof(struct udp_header) + \ sizeof(struct vxlanhdr)) #define GENEVE_BASE_HLEN (sizeof(struct udp_header) + \ sizeof(struct genevehdr)) #define DEFAULT_TTL 64 struct netdev_vport { struct netdev up; /* Protects all members below. */ struct ovs_mutex mutex; struct eth_addr etheraddr; struct netdev_stats stats; /* Tunnels. */ struct netdev_tunnel_config tnl_cfg; char egress_iface[IFNAMSIZ]; bool carrier_status; /* Patch Ports. */ char *peer; }; struct vport_class { const char *dpif_port; struct netdev_class netdev_class; }; /* Last read of the route-table's change number. */ static uint64_t rt_change_seqno; static int netdev_vport_construct(struct netdev *); static int get_patch_config(const struct netdev *netdev, struct smap *args); static int get_tunnel_config(const struct netdev *, struct smap *args); static bool tunnel_check_status_change__(struct netdev_vport *); static uint16_t tnl_udp_port_min = 32768; static uint16_t tnl_udp_port_max = 61000; static bool is_vport_class(const struct netdev_class *class) { return class->construct == netdev_vport_construct; } bool netdev_vport_is_vport_class(const struct netdev_class *class) { return is_vport_class(class); } static const struct vport_class * vport_class_cast(const struct netdev_class *class) { ovs_assert(is_vport_class(class)); return CONTAINER_OF(class, struct vport_class, netdev_class); } static struct netdev_vport * netdev_vport_cast(const struct netdev *netdev) { ovs_assert(is_vport_class(netdev_get_class(netdev))); return CONTAINER_OF(netdev, struct netdev_vport, up); } static const struct netdev_tunnel_config * get_netdev_tunnel_config(const struct netdev *netdev) { return &netdev_vport_cast(netdev)->tnl_cfg; } bool netdev_vport_is_patch(const struct netdev *netdev) { const struct netdev_class *class = netdev_get_class(netdev); return class->get_config == get_patch_config; } bool netdev_vport_is_layer3(const struct netdev *dev) { const char *type = netdev_get_type(dev); return (!strcmp("lisp", type)); } static bool netdev_vport_needs_dst_port(const struct netdev *dev) { const struct netdev_class *class = netdev_get_class(dev); const char *type = netdev_get_type(dev); return (class->get_config == get_tunnel_config && (!strcmp("geneve", type) || !strcmp("vxlan", type) || !strcmp("lisp", type) || !strcmp("stt", type)) ); } const char * netdev_vport_class_get_dpif_port(const struct netdev_class *class) { return is_vport_class(class) ? vport_class_cast(class)->dpif_port : NULL; } const char * netdev_vport_get_dpif_port(const struct netdev *netdev, char namebuf[], size_t bufsize) { const struct netdev_class *class = netdev_get_class(netdev); const char *dpif_port = netdev_vport_class_get_dpif_port(class); if (!dpif_port) { return netdev_get_name(netdev); } if (netdev_vport_needs_dst_port(netdev)) { const struct netdev_vport *vport = netdev_vport_cast(netdev); /* * Note: IFNAMSIZ is 16 bytes long. Implementations should choose * a dpif port name that is short enough to fit including any * port numbers but assert just in case. */ BUILD_ASSERT(NETDEV_VPORT_NAME_BUFSIZE >= IFNAMSIZ); ovs_assert(strlen(dpif_port) + 6 < IFNAMSIZ); snprintf(namebuf, bufsize, "%s_%d", dpif_port, ntohs(vport->tnl_cfg.dst_port)); return namebuf; } else { return dpif_port; } } char * netdev_vport_get_dpif_port_strdup(const struct netdev *netdev) { char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; return xstrdup(netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf)); } /* Whenever the route-table change number is incremented, * netdev_vport_route_changed() should be called to update * the corresponding tunnel interface status. */ static void netdev_vport_route_changed(void) { struct netdev **vports; size_t i, n_vports; vports = netdev_get_vports(&n_vports); for (i = 0; i < n_vports; i++) { struct netdev *netdev_ = vports[i]; struct netdev_vport *netdev = netdev_vport_cast(netdev_); ovs_mutex_lock(&netdev->mutex); /* Finds all tunnel vports. */ if (ipv6_addr_is_set(&netdev->tnl_cfg.ipv6_dst)) { if (tunnel_check_status_change__(netdev)) { netdev_change_seq_changed(netdev_); } } ovs_mutex_unlock(&netdev->mutex); netdev_close(netdev_); } free(vports); } static struct netdev * netdev_vport_alloc(void) { struct netdev_vport *netdev = xzalloc(sizeof *netdev); return &netdev->up; } static int netdev_vport_construct(struct netdev *netdev_) { struct netdev_vport *dev = netdev_vport_cast(netdev_); const char *type = netdev_get_type(netdev_); ovs_mutex_init(&dev->mutex); eth_addr_random(&dev->etheraddr); /* Add a default destination port for tunnel ports if none specified. */ if (!strcmp(type, "geneve")) { dev->tnl_cfg.dst_port = htons(GENEVE_DST_PORT); } else if (!strcmp(type, "vxlan")) { dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT); } else if (!strcmp(type, "lisp")) { dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); } else if (!strcmp(type, "stt")) { dev->tnl_cfg.dst_port = htons(STT_DST_PORT); } dev->tnl_cfg.dont_fragment = true; dev->tnl_cfg.ttl = DEFAULT_TTL; return 0; } static void netdev_vport_destruct(struct netdev *netdev_) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); free(netdev->peer); ovs_mutex_destroy(&netdev->mutex); } static void netdev_vport_dealloc(struct netdev *netdev_) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); free(netdev); } static int netdev_vport_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); ovs_mutex_lock(&netdev->mutex); netdev->etheraddr = mac; ovs_mutex_unlock(&netdev->mutex); netdev_change_seq_changed(netdev_); return 0; } static int netdev_vport_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); ovs_mutex_lock(&netdev->mutex); *mac = netdev->etheraddr; ovs_mutex_unlock(&netdev->mutex); return 0; } /* Checks if the tunnel status has changed and returns a boolean. * Updates the tunnel status if it has changed. */ static bool tunnel_check_status_change__(struct netdev_vport *netdev) OVS_REQUIRES(netdev->mutex) { char iface[IFNAMSIZ]; bool status = false; struct in6_addr *route; struct in6_addr gw; iface[0] = '\0'; route = &netdev->tnl_cfg.ipv6_dst; if (ovs_router_lookup(route, iface, &gw)) { struct netdev *egress_netdev; if (!netdev_open(iface, "system", &egress_netdev)) { status = netdev_get_carrier(egress_netdev); netdev_close(egress_netdev); } } if (strcmp(netdev->egress_iface, iface) || netdev->carrier_status != status) { ovs_strlcpy(netdev->egress_iface, iface, IFNAMSIZ); netdev->carrier_status = status; return true; } return false; } static int tunnel_get_status(const struct netdev *netdev_, struct smap *smap) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); if (netdev->egress_iface[0]) { smap_add(smap, "tunnel_egress_iface", netdev->egress_iface); smap_add(smap, "tunnel_egress_iface_carrier", netdev->carrier_status ? "up" : "down"); } return 0; } static int netdev_vport_update_flags(struct netdev *netdev OVS_UNUSED, enum netdev_flags off, enum netdev_flags on OVS_UNUSED, enum netdev_flags *old_flagsp) { if (off & (NETDEV_UP | NETDEV_PROMISC)) { return EOPNOTSUPP; } *old_flagsp = NETDEV_UP | NETDEV_PROMISC; return 0; } static void netdev_vport_run(void) { uint64_t seq; route_table_run(); seq = route_table_get_change_seq(); if (rt_change_seqno != seq) { rt_change_seqno = seq; netdev_vport_route_changed(); } } static void netdev_vport_wait(void) { uint64_t seq; route_table_wait(); seq = route_table_get_change_seq(); if (rt_change_seqno != seq) { poll_immediate_wake(); } } /* Code specific to tunnel types. */ static ovs_be64 parse_key(const struct smap *args, const char *name, bool *present, bool *flow) { const char *s; *present = false; *flow = false; s = smap_get(args, name); if (!s) { s = smap_get(args, "key"); if (!s) { return 0; } } *present = true; if (!strcmp(s, "flow")) { *flow = true; return 0; } else { return htonll(strtoull(s, NULL, 0)); } } static int parse_tunnel_ip(const char *value, bool accept_mcast, bool *flow, struct in6_addr *ipv6, uint16_t *protocol) { if (!strcmp(value, "flow")) { *flow = true; *protocol = 0; return 0; } if (addr_is_ipv6(value)) { if (lookup_ipv6(value, ipv6)) { return ENOENT; } if (!accept_mcast && ipv6_addr_is_multicast(ipv6)) { return EINVAL; } *protocol = ETH_TYPE_IPV6; } else { struct in_addr ip; if (lookup_ip(value, &ip)) { return ENOENT; } if (!accept_mcast && ip_is_multicast(ip.s_addr)) { return EINVAL; } in6_addr_set_mapped_ipv4(ipv6, ip.s_addr); *protocol = ETH_TYPE_IP; } return 0; } static int set_tunnel_config(struct netdev *dev_, const struct smap *args) { struct netdev_vport *dev = netdev_vport_cast(dev_); const char *name = netdev_get_name(dev_); const char *type = netdev_get_type(dev_); bool ipsec_mech_set, needs_dst_port, has_csum; uint16_t dst_proto = 0, src_proto = 0; struct netdev_tunnel_config tnl_cfg; struct smap_node *node; has_csum = strstr(type, "gre") || strstr(type, "geneve") || strstr(type, "stt") || strstr(type, "vxlan"); ipsec_mech_set = false; memset(&tnl_cfg, 0, sizeof tnl_cfg); /* Add a default destination port for tunnel ports if none specified. */ if (!strcmp(type, "geneve")) { tnl_cfg.dst_port = htons(GENEVE_DST_PORT); } if (!strcmp(type, "vxlan")) { tnl_cfg.dst_port = htons(VXLAN_DST_PORT); } if (!strcmp(type, "lisp")) { tnl_cfg.dst_port = htons(LISP_DST_PORT); } if (!strcmp(type, "stt")) { tnl_cfg.dst_port = htons(STT_DST_PORT); } needs_dst_port = netdev_vport_needs_dst_port(dev_); tnl_cfg.ipsec = strstr(type, "ipsec"); tnl_cfg.dont_fragment = true; SMAP_FOR_EACH (node, args) { if (!strcmp(node->key, "remote_ip")) { int err; err = parse_tunnel_ip(node->value, false, &tnl_cfg.ip_dst_flow, &tnl_cfg.ipv6_dst, &dst_proto); switch (err) { case ENOENT: VLOG_WARN("%s: bad %s 'remote_ip'", name, type); break; case EINVAL: VLOG_WARN("%s: multicast remote_ip=%s not allowed", name, node->value); return EINVAL; } if (dst_proto == ETH_TYPE_IPV6) { VLOG_WARN("%s: IPv6 'remote_ip' is not supported", name); return EOPNOTSUPP; } } else if (!strcmp(node->key, "local_ip")) { int err; err = parse_tunnel_ip(node->value, true, &tnl_cfg.ip_src_flow, &tnl_cfg.ipv6_src, &src_proto); switch (err) { case ENOENT: VLOG_WARN("%s: bad %s 'local_ip'", name, type); break; } if (src_proto == ETH_TYPE_IPV6) { VLOG_WARN("%s: IPv6 'local_ip' is not supported", name); return EOPNOTSUPP; } } else if (!strcmp(node->key, "tos")) { if (!strcmp(node->value, "inherit")) { tnl_cfg.tos_inherit = true; } else { char *endptr; int tos; tos = strtol(node->value, &endptr, 0); if (*endptr == '\0' && tos == (tos & IP_DSCP_MASK)) { tnl_cfg.tos = tos; } else { VLOG_WARN("%s: invalid TOS %s", name, node->value); } } } else if (!strcmp(node->key, "ttl")) { if (!strcmp(node->value, "inherit")) { tnl_cfg.ttl_inherit = true; } else { tnl_cfg.ttl = atoi(node->value); } } else if (!strcmp(node->key, "dst_port") && needs_dst_port) { tnl_cfg.dst_port = htons(atoi(node->value)); } else if (!strcmp(node->key, "csum") && has_csum) { if (!strcmp(node->value, "true")) { tnl_cfg.csum = true; } } else if (!strcmp(node->key, "df_default")) { if (!strcmp(node->value, "false")) { tnl_cfg.dont_fragment = false; } } else if (!strcmp(node->key, "peer_cert") && tnl_cfg.ipsec) { if (smap_get(args, "certificate")) { ipsec_mech_set = true; } else { const char *use_ssl_cert; /* If the "use_ssl_cert" is true, then "certificate" and * "private_key" will be pulled from the SSL table. The * use of this option is strongly discouraged, since it * will like be removed when multiple SSL configurations * are supported by OVS. */ use_ssl_cert = smap_get(args, "use_ssl_cert"); if (!use_ssl_cert || strcmp(use_ssl_cert, "true")) { VLOG_ERR("%s: 'peer_cert' requires 'certificate' argument", name); return EINVAL; } ipsec_mech_set = true; } } else if (!strcmp(node->key, "psk") && tnl_cfg.ipsec) { ipsec_mech_set = true; } else if (tnl_cfg.ipsec && (!strcmp(node->key, "certificate") || !strcmp(node->key, "private_key") || !strcmp(node->key, "use_ssl_cert"))) { /* Ignore options not used by the netdev. */ } else if (!strcmp(node->key, "key") || !strcmp(node->key, "in_key") || !strcmp(node->key, "out_key")) { /* Handled separately below. */ } else if (!strcmp(node->key, "exts")) { char *str = xstrdup(node->value); char *ext, *save_ptr = NULL; tnl_cfg.exts = 0; ext = strtok_r(str, ",", &save_ptr); while (ext) { if (!strcmp(type, "vxlan") && !strcmp(ext, "gbp")) { tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP); } else { VLOG_WARN("%s: unknown extension '%s'", name, ext); } ext = strtok_r(NULL, ",", &save_ptr); } free(str); } else { VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key); } } if (tnl_cfg.ipsec) { static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static pid_t pid = 0; #ifndef _WIN32 ovs_mutex_lock(&mutex); if (pid <= 0) { char *file_name = xasprintf("%s/%s", ovs_rundir(), "ovs-monitor-ipsec.pid"); pid = read_pidfile(file_name); free(file_name); } ovs_mutex_unlock(&mutex); #endif if (pid < 0) { VLOG_ERR("%s: IPsec requires the ovs-monitor-ipsec daemon", name); return EINVAL; } if (smap_get(args, "peer_cert") && smap_get(args, "psk")) { VLOG_ERR("%s: cannot define both 'peer_cert' and 'psk'", name); return EINVAL; } if (!ipsec_mech_set) { VLOG_ERR("%s: IPsec requires an 'peer_cert' or psk' argument", name); return EINVAL; } } if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) { VLOG_ERR("%s: %s type requires valid 'remote_ip' argument", name, type); return EINVAL; } if (tnl_cfg.ip_src_flow && !tnl_cfg.ip_dst_flow) { VLOG_ERR("%s: %s type requires 'remote_ip=flow' with 'local_ip=flow'", name, type); return EINVAL; } if (src_proto && dst_proto && src_proto != dst_proto) { VLOG_ERR("%s: 'remote_ip' and 'local_ip' has to be of the same address family", name); return EINVAL; } if (!tnl_cfg.ttl) { tnl_cfg.ttl = DEFAULT_TTL; } tnl_cfg.in_key = parse_key(args, "in_key", &tnl_cfg.in_key_present, &tnl_cfg.in_key_flow); tnl_cfg.out_key = parse_key(args, "out_key", &tnl_cfg.out_key_present, &tnl_cfg.out_key_flow); ovs_mutex_lock(&dev->mutex); if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) { dev->tnl_cfg = tnl_cfg; tunnel_check_status_change__(dev); netdev_change_seq_changed(dev_); } ovs_mutex_unlock(&dev->mutex); return 0; } static int get_tunnel_config(const struct netdev *dev, struct smap *args) { struct netdev_vport *netdev = netdev_vport_cast(dev); struct netdev_tunnel_config tnl_cfg; ovs_mutex_lock(&netdev->mutex); tnl_cfg = netdev->tnl_cfg; ovs_mutex_unlock(&netdev->mutex); if (ipv6_addr_is_set(&tnl_cfg.ipv6_dst)) { smap_add_ipv6(args, "remote_ip", &tnl_cfg.ipv6_dst); } else if (tnl_cfg.ip_dst_flow) { smap_add(args, "remote_ip", "flow"); } if (ipv6_addr_is_set(&tnl_cfg.ipv6_src)) { smap_add_ipv6(args, "local_ip", &tnl_cfg.ipv6_src); } else if (tnl_cfg.ip_src_flow) { smap_add(args, "local_ip", "flow"); } if (tnl_cfg.in_key_flow && tnl_cfg.out_key_flow) { smap_add(args, "key", "flow"); } else if (tnl_cfg.in_key_present && tnl_cfg.out_key_present && tnl_cfg.in_key == tnl_cfg.out_key) { smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg.in_key)); } else { if (tnl_cfg.in_key_flow) { smap_add(args, "in_key", "flow"); } else if (tnl_cfg.in_key_present) { smap_add_format(args, "in_key", "%"PRIu64, ntohll(tnl_cfg.in_key)); } if (tnl_cfg.out_key_flow) { smap_add(args, "out_key", "flow"); } else if (tnl_cfg.out_key_present) { smap_add_format(args, "out_key", "%"PRIu64, ntohll(tnl_cfg.out_key)); } } if (tnl_cfg.ttl_inherit) { smap_add(args, "ttl", "inherit"); } else if (tnl_cfg.ttl != DEFAULT_TTL) { smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg.ttl); } if (tnl_cfg.tos_inherit) { smap_add(args, "tos", "inherit"); } else if (tnl_cfg.tos) { smap_add_format(args, "tos", "0x%x", tnl_cfg.tos); } if (tnl_cfg.dst_port) { uint16_t dst_port = ntohs(tnl_cfg.dst_port); const char *type = netdev_get_type(dev); if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) || (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) || (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) || (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { smap_add_format(args, "dst_port", "%d", dst_port); } } if (tnl_cfg.csum) { smap_add(args, "csum", "true"); } if (!tnl_cfg.dont_fragment) { smap_add(args, "df_default", "false"); } return 0; } /* Code specific to patch ports. */ /* If 'netdev' is a patch port, returns the name of its peer as a malloc()'d * string that the caller must free. * * If 'netdev' is not a patch port, returns NULL. */ char * netdev_vport_patch_peer(const struct netdev *netdev_) { char *peer = NULL; if (netdev_vport_is_patch(netdev_)) { struct netdev_vport *netdev = netdev_vport_cast(netdev_); ovs_mutex_lock(&netdev->mutex); if (netdev->peer) { peer = xstrdup(netdev->peer); } ovs_mutex_unlock(&netdev->mutex); } return peer; } void netdev_vport_inc_rx(const struct netdev *netdev, const struct dpif_flow_stats *stats) { if (is_vport_class(netdev_get_class(netdev))) { struct netdev_vport *dev = netdev_vport_cast(netdev); ovs_mutex_lock(&dev->mutex); dev->stats.rx_packets += stats->n_packets; dev->stats.rx_bytes += stats->n_bytes; ovs_mutex_unlock(&dev->mutex); } } void netdev_vport_inc_tx(const struct netdev *netdev, const struct dpif_flow_stats *stats) { if (is_vport_class(netdev_get_class(netdev))) { struct netdev_vport *dev = netdev_vport_cast(netdev); ovs_mutex_lock(&dev->mutex); dev->stats.tx_packets += stats->n_packets; dev->stats.tx_bytes += stats->n_bytes; ovs_mutex_unlock(&dev->mutex); } } static int get_patch_config(const struct netdev *dev_, struct smap *args) { struct netdev_vport *dev = netdev_vport_cast(dev_); ovs_mutex_lock(&dev->mutex); if (dev->peer) { smap_add(args, "peer", dev->peer); } ovs_mutex_unlock(&dev->mutex); return 0; } static int set_patch_config(struct netdev *dev_, const struct smap *args) { struct netdev_vport *dev = netdev_vport_cast(dev_); const char *name = netdev_get_name(dev_); const char *peer; peer = smap_get(args, "peer"); if (!peer) { VLOG_ERR("%s: patch type requires valid 'peer' argument", name); return EINVAL; } if (smap_count(args) > 1) { VLOG_ERR("%s: patch type takes only a 'peer' argument", name); return EINVAL; } if (!strcmp(name, peer)) { VLOG_ERR("%s: patch peer must not be self", name); return EINVAL; } ovs_mutex_lock(&dev->mutex); if (!dev->peer || strcmp(dev->peer, peer)) { free(dev->peer); dev->peer = xstrdup(peer); netdev_change_seq_changed(dev_); } ovs_mutex_unlock(&dev->mutex); return 0; } static int get_stats(const struct netdev *netdev, struct netdev_stats *stats) { struct netdev_vport *dev = netdev_vport_cast(netdev); ovs_mutex_lock(&dev->mutex); *stats = dev->stats; ovs_mutex_unlock(&dev->mutex); return 0; } /* Tunnel push pop ops. */ static struct ip_header * ip_hdr(void *eth) { return (void *)((char *)eth + sizeof (struct eth_header)); } static struct ovs_16aligned_ip6_hdr * ipv6_hdr(void *eth) { return (void *)((char *)eth + sizeof (struct eth_header)); } static void * ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl, unsigned int *hlen) { void *nh; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; void *l4; int l3_size; nh = dp_packet_l3(packet); ip = nh; ip6 = nh; l4 = dp_packet_l4(packet); if (!nh || !l4) { return NULL; } *hlen = sizeof(struct eth_header); l3_size = dp_packet_size(packet) - ((char *)nh - (char *)dp_packet_data(packet)); if (IP_VER(ip->ip_ihl_ver) == 4) { ovs_be32 ip_src, ip_dst; if (csum(ip, IP_IHL(ip->ip_ihl_ver) * 4)) { VLOG_WARN_RL(&err_rl, "ip packet has invalid checksum"); return NULL; } if (ntohs(ip->ip_tot_len) > l3_size) { VLOG_WARN_RL(&err_rl, "ip packet is truncated (IP length %d, actual %d)", ntohs(ip->ip_tot_len), l3_size); return NULL; } if (IP_IHL(ip->ip_ihl_ver) * 4 > sizeof(struct ip_header)) { VLOG_WARN_RL(&err_rl, "ip options not supported on tunnel packets " "(%d bytes)", IP_IHL(ip->ip_ihl_ver) * 4); return NULL; } ip_src = get_16aligned_be32(&ip->ip_src); ip_dst = get_16aligned_be32(&ip->ip_dst); tnl->ip_src = ip_src; tnl->ip_dst = ip_dst; tnl->ip_tos = ip->ip_tos; tnl->ip_ttl = ip->ip_ttl; *hlen += IP_HEADER_LEN; } else if (IP_VER(ip->ip_ihl_ver) == 6) { memcpy(tnl->ipv6_src.s6_addr, ip6->ip6_src.be16, sizeof ip6->ip6_src); memcpy(tnl->ipv6_dst.s6_addr, ip6->ip6_dst.be16, sizeof ip6->ip6_dst); tnl->ip_tos = 0; tnl->ip_ttl = ip6->ip6_hlim; *hlen += IPV6_HEADER_LEN; } else { VLOG_WARN_RL(&err_rl, "ipv4 packet has invalid version (%d)", IP_VER(ip->ip_ihl_ver)); return NULL; } return l4; } static bool is_header_ipv6(const void *header) { const struct eth_header *eth; eth = header; return eth->eth_type == htons(ETH_TYPE_IPV6); } /* Pushes the 'size' bytes of 'header' into the headroom of 'packet', * reallocating the packet if necessary. 'header' should contain an Ethernet * header, followed by an IPv4 header (without options), and an L4 header. * * This function sets the IP header's ip_tot_len field (which should be zeroed * as part of 'header') and puts its value into '*ip_tot_size' as well. Also * updates IP header checksum. * * Return pointer to the L4 header added to 'packet'. */ static void * push_ip_header(struct dp_packet *packet, const void *header, int size, int *ip_tot_size) { struct eth_header *eth; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; eth = dp_packet_push_uninit(packet, size); *ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header); memcpy(eth, header, size); if (is_header_ipv6(header)) { ip6 = ipv6_hdr(eth); *ip_tot_size -= IPV6_HEADER_LEN; ip6->ip6_plen = htons(*ip_tot_size); return ip6 + 1; } else { ip = ip_hdr(eth); ip->ip_tot_len = htons(*ip_tot_size); ip->ip_csum = recalc_csum16(ip->ip_csum, 0, ip->ip_tot_len); *ip_tot_size -= IP_HEADER_LEN; return ip + 1; } } static void * udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl, unsigned int *hlen) { struct udp_header *udp; udp = ip_extract_tnl_md(packet, tnl, hlen); if (!udp) { return NULL; } if (udp->udp_csum) { uint32_t csum; if (is_header_ipv6(dp_packet_data(packet))) { csum = packet_csum_pseudoheader6(dp_packet_l3(packet)); } else { csum = packet_csum_pseudoheader(dp_packet_l3(packet)); } csum = csum_continue(csum, udp, dp_packet_size(packet) - ((const unsigned char *)udp - (const unsigned char *)dp_packet_l2(packet))); if (csum_finish(csum)) { return NULL; } tnl->flags |= FLOW_TNL_F_CSUM; } tnl->tp_src = udp->udp_src; tnl->tp_dst = udp->udp_dst; return udp + 1; } static ovs_be16 get_src_port(struct dp_packet *packet) { uint32_t hash; hash = dp_packet_get_rss_hash(packet); return htons((((uint64_t) hash * (tnl_udp_port_max - tnl_udp_port_min)) >> 32) + tnl_udp_port_min); } static void push_udp_header(struct dp_packet *packet, const struct ovs_action_push_tnl *data) { struct udp_header *udp; int ip_tot_size; udp = push_ip_header(packet, data->header, data->header_len, &ip_tot_size); /* set udp src port */ udp->udp_src = get_src_port(packet); udp->udp_len = htons(ip_tot_size); if (udp->udp_csum) { uint32_t csum; if (is_header_ipv6(dp_packet_data(packet))) { csum = packet_csum_pseudoheader6(ipv6_hdr(dp_packet_data(packet))); } else { csum = packet_csum_pseudoheader(ip_hdr(dp_packet_data(packet))); } csum = csum_continue(csum, udp, ip_tot_size); udp->udp_csum = csum_finish(csum); if (!udp->udp_csum) { udp->udp_csum = htons(0xffff); } } } static void * udp_build_header(struct netdev_tunnel_config *tnl_cfg, const struct flow *tnl_flow, struct ovs_action_push_tnl *data, unsigned int *hlen) { struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; struct udp_header *udp; bool is_ipv6; *hlen = sizeof(struct eth_header); is_ipv6 = is_header_ipv6(data->header); if (is_ipv6) { ip6 = ipv6_hdr(data->header); ip6->ip6_nxt = IPPROTO_UDP; udp = (struct udp_header *) (ip6 + 1); *hlen += IPV6_HEADER_LEN; } else { ip = ip_hdr(data->header); ip->ip_proto = IPPROTO_UDP; udp = (struct udp_header *) (ip + 1); *hlen += IP_HEADER_LEN; } udp->udp_dst = tnl_cfg->dst_port; if (is_ipv6 || tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) { /* Write a value in now to mark that we should compute the checksum * later. 0xffff is handy because it is transparent to the * calculation. */ udp->udp_csum = htons(0xffff); } return udp + 1; } static int gre_header_len(ovs_be16 flags) { int hlen = 4; if (flags & htons(GRE_CSUM)) { hlen += 4; } if (flags & htons(GRE_KEY)) { hlen += 4; } if (flags & htons(GRE_SEQ)) { hlen += 4; } return hlen; } static int parse_gre_header(struct dp_packet *packet, struct flow_tnl *tnl) { const struct gre_base_hdr *greh; ovs_16aligned_be32 *options; int hlen; unsigned int ulen; greh = ip_extract_tnl_md(packet, tnl, &ulen); if (!greh) { return -EINVAL; } if (greh->flags & ~(htons(GRE_CSUM | GRE_KEY | GRE_SEQ))) { return -EINVAL; } if (greh->protocol != htons(ETH_TYPE_TEB)) { return -EINVAL; } hlen = ulen + gre_header_len(greh->flags); if (hlen > dp_packet_size(packet)) { return -EINVAL; } options = (ovs_16aligned_be32 *)(greh + 1); if (greh->flags & htons(GRE_CSUM)) { ovs_be16 pkt_csum; pkt_csum = csum(greh, dp_packet_size(packet) - ((const unsigned char *)greh - (const unsigned char *)dp_packet_l2(packet))); if (pkt_csum) { return -EINVAL; } tnl->flags = FLOW_TNL_F_CSUM; options++; } if (greh->flags & htons(GRE_KEY)) { tnl->tun_id = be32_to_be64(get_16aligned_be32(options)); tnl->flags |= FLOW_TNL_F_KEY; options++; } if (greh->flags & htons(GRE_SEQ)) { options++; } return hlen; } static void pkt_metadata_init_tnl(struct pkt_metadata *md) { /* Zero up through the tunnel metadata options. The length and table * are before this and as long as they are empty, the options won't * be looked at. */ memset(md, 0, offsetof(struct pkt_metadata, tunnel.metadata.opts)); } static int netdev_gre_pop_header(struct dp_packet *packet) { struct pkt_metadata *md = &packet->md; struct flow_tnl *tnl = &md->tunnel; int hlen = sizeof(struct eth_header) + 4; hlen += is_header_ipv6(dp_packet_data(packet)) ? IPV6_HEADER_LEN : IP_HEADER_LEN; pkt_metadata_init_tnl(md); if (hlen > dp_packet_size(packet)) { return EINVAL; } hlen = parse_gre_header(packet, tnl); if (hlen < 0) { return -hlen; } dp_packet_reset_packet(packet, hlen); return 0; } static void netdev_gre_push_header(struct dp_packet *packet, const struct ovs_action_push_tnl *data) { struct gre_base_hdr *greh; int ip_tot_size; greh = push_ip_header(packet, data->header, data->header_len, &ip_tot_size); if (greh->flags & htons(GRE_CSUM)) { ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1); *csum_opt = csum(greh, ip_tot_size); } } static int netdev_gre_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct flow *tnl_flow) { struct netdev_vport *dev = netdev_vport_cast(netdev); struct netdev_tunnel_config *tnl_cfg; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; struct gre_base_hdr *greh; ovs_16aligned_be32 *options; int hlen; bool is_ipv6; is_ipv6 = is_header_ipv6(data->header); /* XXX: RCUfy tnl_cfg. */ ovs_mutex_lock(&dev->mutex); tnl_cfg = &dev->tnl_cfg; if (is_ipv6) { ip6 = ipv6_hdr(data->header); ip6->ip6_nxt = IPPROTO_GRE; greh = (struct gre_base_hdr *) (ip6 + 1); } else { ip = ip_hdr(data->header); ip->ip_proto = IPPROTO_GRE; greh = (struct gre_base_hdr *) (ip + 1); } greh->protocol = htons(ETH_TYPE_TEB); greh->flags = 0; options = (ovs_16aligned_be32 *) (greh + 1); if (tnl_flow->tunnel.flags & FLOW_TNL_F_CSUM) { greh->flags |= htons(GRE_CSUM); put_16aligned_be32(options, 0); options++; } if (tnl_cfg->out_key_present) { greh->flags |= htons(GRE_KEY); put_16aligned_be32(options, be64_to_be32(tnl_flow->tunnel.tun_id)); options++; } ovs_mutex_unlock(&dev->mutex); hlen = (uint8_t *) options - (uint8_t *) greh; data->header_len = sizeof(struct eth_header) + hlen + (is_ipv6 ? IPV6_HEADER_LEN : IP_HEADER_LEN); data->tnl_type = OVS_VPORT_TYPE_GRE; return 0; } static int netdev_vxlan_pop_header(struct dp_packet *packet) { struct pkt_metadata *md = &packet->md; struct flow_tnl *tnl = &md->tunnel; struct vxlanhdr *vxh; unsigned int hlen; pkt_metadata_init_tnl(md); if (VXLAN_HLEN > dp_packet_l4_size(packet)) { return EINVAL; } vxh = udp_extract_tnl_md(packet, tnl, &hlen); if (!vxh) { return EINVAL; } if (get_16aligned_be32(&vxh->vx_flags) != htonl(VXLAN_FLAGS) || (get_16aligned_be32(&vxh->vx_vni) & htonl(0xff))) { VLOG_WARN_RL(&err_rl, "invalid vxlan flags=%#x vni=%#x\n", ntohl(get_16aligned_be32(&vxh->vx_flags)), ntohl(get_16aligned_be32(&vxh->vx_vni))); return EINVAL; } tnl->tun_id = htonll(ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8); tnl->flags |= FLOW_TNL_F_KEY; dp_packet_reset_packet(packet, hlen + VXLAN_HLEN); return 0; } static int netdev_vxlan_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct flow *tnl_flow) { struct netdev_vport *dev = netdev_vport_cast(netdev); struct netdev_tunnel_config *tnl_cfg; struct vxlanhdr *vxh; unsigned int hlen; /* XXX: RCUfy tnl_cfg. */ ovs_mutex_lock(&dev->mutex); tnl_cfg = &dev->tnl_cfg; vxh = udp_build_header(tnl_cfg, tnl_flow, data, &hlen); put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS)); put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8)); ovs_mutex_unlock(&dev->mutex); data->header_len = hlen + VXLAN_HLEN; data->tnl_type = OVS_VPORT_TYPE_VXLAN; return 0; } static int netdev_geneve_pop_header(struct dp_packet *packet) { struct pkt_metadata *md = &packet->md; struct flow_tnl *tnl = &md->tunnel; struct genevehdr *gnh; unsigned int hlen, opts_len, ulen; pkt_metadata_init_tnl(md); if (GENEVE_BASE_HLEN > dp_packet_l4_size(packet)) { VLOG_WARN_RL(&err_rl, "geneve packet too small: min header=%u packet size=%"PRIuSIZE"\n", (unsigned int)GENEVE_BASE_HLEN, dp_packet_l4_size(packet)); return EINVAL; } gnh = udp_extract_tnl_md(packet, tnl, &ulen); if (!gnh) { return EINVAL; } opts_len = gnh->opt_len * 4; hlen = ulen + GENEVE_BASE_HLEN + opts_len; if (hlen > dp_packet_size(packet)) { VLOG_WARN_RL(&err_rl, "geneve packet too small: header len=%u packet size=%u\n", hlen, dp_packet_size(packet)); return EINVAL; } if (gnh->ver != 0) { VLOG_WARN_RL(&err_rl, "unknown geneve version: %"PRIu8"\n", gnh->ver); return EINVAL; } if (gnh->proto_type != htons(ETH_TYPE_TEB)) { VLOG_WARN_RL(&err_rl, "unknown geneve encapsulated protocol: %#x\n", ntohs(gnh->proto_type)); return EINVAL; } tnl->flags |= gnh->oam ? FLOW_TNL_F_OAM : 0; tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gnh->vni)) >> 8); tnl->flags |= FLOW_TNL_F_KEY; memcpy(tnl->metadata.opts.gnv, gnh->options, opts_len); tnl->metadata.present.len = opts_len; tnl->flags |= FLOW_TNL_F_UDPIF; dp_packet_reset_packet(packet, hlen); return 0; } static int netdev_geneve_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct flow *tnl_flow) { struct netdev_vport *dev = netdev_vport_cast(netdev); struct netdev_tunnel_config *tnl_cfg; struct genevehdr *gnh; int opt_len; bool crit_opt; unsigned int hlen; /* XXX: RCUfy tnl_cfg. */ ovs_mutex_lock(&dev->mutex); tnl_cfg = &dev->tnl_cfg; gnh = udp_build_header(tnl_cfg, tnl_flow, data, &hlen); put_16aligned_be32(&gnh->vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8)); ovs_mutex_unlock(&dev->mutex); opt_len = tun_metadata_to_geneve_header(&tnl_flow->tunnel, gnh->options, &crit_opt); gnh->opt_len = opt_len / 4; gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM); gnh->critical = crit_opt ? 1 : 0; gnh->proto_type = htons(ETH_TYPE_TEB); data->header_len = hlen + GENEVE_BASE_HLEN + opt_len; data->tnl_type = OVS_VPORT_TYPE_GENEVE; return 0; } static void netdev_vport_range(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { int val1, val2; if (argc < 3) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "Tunnel UDP source port range: %"PRIu16"-%"PRIu16"\n", tnl_udp_port_min, tnl_udp_port_max); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); return; } if (argc != 3) { return; } val1 = atoi(argv[1]); if (val1 <= 0 || val1 > UINT16_MAX) { unixctl_command_reply(conn, "Invalid min."); return; } val2 = atoi(argv[2]); if (val2 <= 0 || val2 > UINT16_MAX) { unixctl_command_reply(conn, "Invalid max."); return; } if (val1 > val2) { tnl_udp_port_min = val2; tnl_udp_port_max = val1; } else { tnl_udp_port_min = val1; tnl_udp_port_max = val2; } seq_change(tnl_conf_seq); unixctl_command_reply(conn, "OK"); } #define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG, \ GET_TUNNEL_CONFIG, GET_STATUS, \ BUILD_HEADER, \ PUSH_HEADER, POP_HEADER) \ NULL, \ netdev_vport_run, \ netdev_vport_wait, \ \ netdev_vport_alloc, \ netdev_vport_construct, \ netdev_vport_destruct, \ netdev_vport_dealloc, \ GET_CONFIG, \ SET_CONFIG, \ GET_TUNNEL_CONFIG, \ BUILD_HEADER, \ PUSH_HEADER, \ POP_HEADER, \ NULL, /* get_numa_id */ \ NULL, /* set_multiq */ \ \ NULL, /* send */ \ NULL, /* send_wait */ \ \ netdev_vport_set_etheraddr, \ netdev_vport_get_etheraddr, \ NULL, /* get_mtu */ \ NULL, /* set_mtu */ \ NULL, /* get_ifindex */ \ NULL, /* get_carrier */ \ NULL, /* get_carrier_resets */ \ NULL, /* get_miimon */ \ get_stats, \ \ NULL, /* get_features */ \ NULL, /* set_advertisements */ \ \ NULL, /* set_policing */ \ NULL, /* get_qos_types */ \ NULL, /* get_qos_capabilities */ \ NULL, /* get_qos */ \ NULL, /* set_qos */ \ NULL, /* get_queue */ \ NULL, /* set_queue */ \ NULL, /* delete_queue */ \ NULL, /* get_queue_stats */ \ NULL, /* queue_dump_start */ \ NULL, /* queue_dump_next */ \ NULL, /* queue_dump_done */ \ NULL, /* dump_queue_stats */ \ \ NULL, /* get_in4 */ \ NULL, /* set_in4 */ \ NULL, /* get_in6 */ \ NULL, /* add_router */ \ NULL, /* get_next_hop */ \ GET_STATUS, \ NULL, /* arp_lookup */ \ \ netdev_vport_update_flags, \ \ NULL, /* rx_alloc */ \ NULL, /* rx_construct */ \ NULL, /* rx_destruct */ \ NULL, /* rx_dealloc */ \ NULL, /* rx_recv */ \ NULL, /* rx_wait */ \ NULL, /* rx_drain */ #define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER) \ { DPIF_PORT, \ { NAME, VPORT_FUNCTIONS(get_tunnel_config, \ set_tunnel_config, \ get_netdev_tunnel_config, \ tunnel_get_status, \ BUILD_HEADER, PUSH_HEADER, POP_HEADER) }} void netdev_vport_tunnel_register(void) { /* The name of the dpif_port should be short enough to accomodate adding * a port number to the end if one is necessary. */ static const struct vport_class vport_classes[] = { TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header, push_udp_header, netdev_geneve_pop_header), TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header, netdev_gre_push_header, netdev_gre_pop_header), TUNNEL_CLASS("ipsec_gre", "gre_sys", NULL, NULL, NULL), TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header, push_udp_header, netdev_vxlan_pop_header), TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL), TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL), }; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { int i; for (i = 0; i < ARRAY_SIZE(vport_classes); i++) { netdev_register_provider(&vport_classes[i].netdev_class); } unixctl_command_register("tnl/egress_port_range", "min max", 0, 2, netdev_vport_range, NULL); ovsthread_once_done(&once); } } void netdev_vport_patch_register(void) { static const struct vport_class patch_class = { NULL, { "patch", VPORT_FUNCTIONS(get_patch_config, set_patch_config, NULL, NULL, NULL, NULL, NULL) }}; netdev_register_provider(&patch_class.netdev_class); } openvswitch-2.5.9/lib/PaxHeaders.82075/cmap.h0000644000000000000000000000013213534540071015503 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.673852631 openvswitch-2.5.9/lib/cmap.h0000644000175000017500000002326013534540071017174 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CMAP_H #define CMAP_H 1 #include #include #include "ovs-rcu.h" #include "util.h" /* Concurrent hash map * =================== * * A single-writer, multiple-reader hash table that efficiently supports * duplicates. * * * Thread-safety * ============= * * The general rules are: * * - Only a single thread may safely call into cmap_insert(), * cmap_remove(), or cmap_replace() at any given time. * * - Any number of threads may use functions and macros that search or * iterate through a given cmap, even in parallel with other threads * calling cmap_insert(), cmap_remove(), or cmap_replace(). * * There is one exception: cmap_find_protected() is only safe if no thread * is currently calling cmap_insert(), cmap_remove(), or cmap_replace(). * (Use ordinary cmap_find() if that is not guaranteed.) * * - See "Iteration" below for additional thread safety rules. * * Writers must use special care to ensure that any elements that they remove * do not get freed or reused until readers have finished with them. This * includes inserting the element back into its original cmap or a different * one. One correct way to do this is to free them from an RCU callback with * ovsrcu_postpone(). */ /* A concurrent hash map node, to be embedded inside the data structure being * mapped. * * All nodes linked together on a chain have exactly the same hash value. */ struct cmap_node { OVSRCU_TYPE(struct cmap_node *) next; /* Next node with same hash. */ }; static inline struct cmap_node * cmap_node_next(const struct cmap_node *node) { return ovsrcu_get(struct cmap_node *, &node->next); } static inline struct cmap_node * cmap_node_next_protected(const struct cmap_node *node) { return ovsrcu_get_protected(struct cmap_node *, &node->next); } /* Concurrent hash map. */ struct cmap { OVSRCU_TYPE(struct cmap_impl *) impl; }; /* Initialization. */ void cmap_init(struct cmap *); void cmap_destroy(struct cmap *); /* Count. */ size_t cmap_count(const struct cmap *); bool cmap_is_empty(const struct cmap *); /* Insertion and deletion. Return the current count after the operation. */ size_t cmap_insert(struct cmap *, struct cmap_node *, uint32_t hash); static inline size_t cmap_remove(struct cmap *, struct cmap_node *, uint32_t hash); size_t cmap_replace(struct cmap *, struct cmap_node *old_node, struct cmap_node *new_node, uint32_t hash); /* Search. * * These macros iterate NODE over all of the nodes in CMAP that have hash value * equal to HASH. MEMBER must be the name of the 'struct cmap_node' member * within NODE. * * CMAP and HASH are evaluated only once. NODE is evaluated many times. * * * Thread-safety * ============= * * CMAP_NODE_FOR_EACH will reliably visit each of the nodes starting with * CMAP_NODE, even with concurrent insertions and deletions. (Of * course, if nodes are being inserted or deleted, it might or might not visit * the nodes actually being inserted or deleted.) * * CMAP_NODE_FOR_EACH_PROTECTED may only be used if the containing CMAP is * guaranteed not to change during iteration. It may be only slightly faster. * * CMAP_FOR_EACH_WITH_HASH will reliably visit each of the nodes with the * specified hash in CMAP, even with concurrent insertions and deletions. (Of * course, if nodes with the given HASH are being inserted or deleted, it might * or might not visit the nodes actually being inserted or deleted.) * * CMAP_FOR_EACH_WITH_HASH_PROTECTED may only be used if CMAP is guaranteed not * to change during iteration. It may be very slightly faster. */ #define CMAP_NODE_FOR_EACH(NODE, MEMBER, CMAP_NODE) \ for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER); \ (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ ASSIGN_CONTAINER(NODE, cmap_node_next(&(NODE)->MEMBER), MEMBER)) #define CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, CMAP_NODE) \ for (INIT_CONTAINER(NODE, CMAP_NODE, MEMBER); \ (NODE) != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ ASSIGN_CONTAINER(NODE, cmap_node_next_protected(&(NODE)->MEMBER), \ MEMBER)) #define CMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, CMAP) \ CMAP_NODE_FOR_EACH(NODE, MEMBER, cmap_find(CMAP, HASH)) #define CMAP_FOR_EACH_WITH_HASH_PROTECTED(NODE, MEMBER, HASH, CMAP) \ CMAP_NODE_FOR_EACH_PROTECTED(NODE, MEMBER, cmap_find_locked(CMAP, HASH)) const struct cmap_node *cmap_find(const struct cmap *, uint32_t hash); struct cmap_node *cmap_find_protected(const struct cmap *, uint32_t hash); /* Looks up multiple 'hashes', when the corresponding bit in 'map' is 1, * and sets the corresponding pointer in 'nodes', if the hash value was * found from the 'cmap'. In other cases the 'nodes' values are not changed, * i.e., no NULL pointers are stored there. * Returns a map where a bit is set to 1 if the corresponding 'nodes' pointer * was stored, 0 otherwise. * Generally, the caller wants to use CMAP_NODE_FOR_EACH to verify for * hash collisions. */ unsigned long cmap_find_batch(const struct cmap *cmap, unsigned long map, uint32_t hashes[], const struct cmap_node *nodes[]); /* Iteration. * * * Thread-safety * ============= * * Iteration is safe even in a cmap that is changing concurrently. However: * * - In the presence of concurrent calls to cmap_insert(), any given * iteration might skip some nodes and might visit some nodes more than * once. If this is a problem, then the iterating code should lock the * data structure (a rwlock can be used to allow multiple threads to * iterate in parallel). * * - Concurrent calls to cmap_remove() don't have the same problem. (A * node being deleted may be visited once or not at all. Other nodes * will be visited once.) * * * Example * ======= * * struct my_node { * struct cmap_node cmap_node; * int extra_data; * }; * * struct cmap_cursor cursor; * struct my_node *iter; * struct cmap my_map; * * cmap_init(&cmap); * ...add data... * CMAP_FOR_EACH (my_node, cmap_node, &cursor, &cmap) { * ...operate on my_node... * } * * CMAP_FOR_EACH is "safe" in the sense of HMAP_FOR_EACH_SAFE. That is, it is * safe to free the current node before going on to the next iteration. Most * of the time, though, this doesn't matter for a cmap because node * deallocation has to be postponed until the next grace period. This means * that this guarantee is useful only in deallocation code already executing at * postponed time, when it is known that the RCU grace period has already * expired. */ #define CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER) \ ((CURSOR)->node \ ? (INIT_CONTAINER(NODE, (CURSOR)->node, MEMBER), \ cmap_cursor_advance(CURSOR), \ true) \ : false) #define CMAP_CURSOR_FOR_EACH(NODE, MEMBER, CURSOR, CMAP) \ for (*(CURSOR) = cmap_cursor_start(CMAP); \ CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER); \ ) #define CMAP_CURSOR_FOR_EACH_CONTINUE(NODE, MEMBER, CURSOR) \ while (CMAP_CURSOR_FOR_EACH__(NODE, CURSOR, MEMBER)) struct cmap_cursor { const struct cmap_impl *impl; uint32_t bucket_idx; int entry_idx; struct cmap_node *node; }; struct cmap_cursor cmap_cursor_start(const struct cmap *); void cmap_cursor_advance(struct cmap_cursor *); #define CMAP_FOR_EACH(NODE, MEMBER, CMAP) \ for (struct cmap_cursor cursor__ = cmap_cursor_start(CMAP); \ CMAP_CURSOR_FOR_EACH__(NODE, &cursor__, MEMBER); \ ) static inline struct cmap_node *cmap_first(const struct cmap *); /* Another, less preferred, form of iteration, for use in situations where it * is difficult to maintain a pointer to a cmap_node. */ struct cmap_position { unsigned int bucket; unsigned int entry; unsigned int offset; }; struct cmap_node *cmap_next_position(const struct cmap *, struct cmap_position *); /* Returns the first node in 'cmap', in arbitrary order, or a null pointer if * 'cmap' is empty. */ static inline struct cmap_node * cmap_first(const struct cmap *cmap) { struct cmap_position pos = { 0, 0, 0 }; return cmap_next_position(cmap, &pos); } /* Removes 'node' from 'cmap'. The caller must ensure that 'cmap' cannot * change concurrently (from another thread). * * 'node' must not be destroyed or modified or inserted back into 'cmap' or * into any other concurrent hash map while any other thread might be accessing * it. One correct way to do this is to free it from an RCU callback with * ovsrcu_postpone(). * * Returns the current number of nodes in the cmap after the removal. */ static inline size_t cmap_remove(struct cmap *cmap, struct cmap_node *node, uint32_t hash) { return cmap_replace(cmap, node, NULL, hash); } #endif /* cmap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sflow_receiver.c0000644000000000000000000000013113534540071017573 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801425.013855136 openvswitch-2.5.9/lib/sflow_receiver.c0000644000175000017500000010347313534540071021272 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #ifndef __CHECKER__ /* Don't run sparse on anything in this file. */ #include #include "sflow_api.h" static void resetSampleCollector(SFLReceiver *receiver); static void sendSample(SFLReceiver *receiver); static void sflError(SFLReceiver *receiver, char *errm); inline static void putNet32(SFLReceiver *receiver, u_int32_t val); inline static void putAddress(SFLReceiver *receiver, SFLAddress *addr); #ifdef SFLOW_DO_SOCKET static void initSocket(SFLReceiver *receiver); #endif /*_________________--------------------------__________________ _________________ sfl_receiver_init __________________ -----------------__________________________------------------ */ void sfl_receiver_init(SFLReceiver *receiver, SFLAgent *agent) { /* first clear everything */ memset(receiver, 0, sizeof(*receiver)); /* now copy in the parameters */ receiver->agent = agent; /* set defaults */ receiver->sFlowRcvrMaximumDatagramSize = SFL_DEFAULT_DATAGRAM_SIZE; receiver->sFlowRcvrPort = SFL_DEFAULT_COLLECTOR_PORT; #ifdef SFLOW_DO_SOCKET /* initialize the socket address */ initSocket(receiver); #endif /* preset some of the header fields */ receiver->sampleCollector.datap = receiver->sampleCollector.data; putNet32(receiver, SFLDATAGRAM_VERSION5); putAddress(receiver, &agent->myIP); putNet32(receiver, agent->subId); /* prepare to receive the first sample */ resetSampleCollector(receiver); } /*_________________---------------------------__________________ _________________ reset __________________ -----------------___________________________------------------ called on timeout, or when owner string is cleared */ static void reset(SFLReceiver *receiver) { // ask agent to tell samplers and pollers to stop sending samples sfl_agent_resetReceiver(receiver->agent, receiver); // reinitialize sfl_receiver_init(receiver, receiver->agent); } #ifdef SFLOW_DO_SOCKET /*_________________---------------------------__________________ _________________ initSocket __________________ -----------------___________________________------------------ */ static void initSocket(SFLReceiver *receiver) { if(receiver->sFlowRcvrAddress.type == SFLADDRESSTYPE_IP_V6) { struct sockaddr_in6 *sa6 = &receiver->receiver6; sa6->sin6_port = htons((u_int16_t)receiver->sFlowRcvrPort); sa6->sin6_family = AF_INET6; sa6->sin6_addr = receiver->sFlowRcvrAddress.address.ip_v6; } else { struct sockaddr_in *sa4 = &receiver->receiver4; sa4->sin_port = htons((u_int16_t)receiver->sFlowRcvrPort); sa4->sin_family = AF_INET; sa4->sin_addr = receiver->sFlowRcvrAddress.address.ip_v4; } } #endif /*_________________----------------------------------------_____________ _________________ MIB Vars _____________ -----------------________________________________________------------- */ char * sfl_receiver_get_sFlowRcvrOwner(SFLReceiver *receiver) { return receiver->sFlowRcvrOwner; } void sfl_receiver_set_sFlowRcvrOwner(SFLReceiver *receiver, char *sFlowRcvrOwner) { receiver->sFlowRcvrOwner = sFlowRcvrOwner; if(sFlowRcvrOwner == NULL || sFlowRcvrOwner[0] == '\0') { // reset condition! owner string was cleared reset(receiver); } } time_t sfl_receiver_get_sFlowRcvrTimeout(SFLReceiver *receiver) { return receiver->sFlowRcvrTimeout; } void sfl_receiver_set_sFlowRcvrTimeout(SFLReceiver *receiver, time_t sFlowRcvrTimeout) { receiver->sFlowRcvrTimeout =sFlowRcvrTimeout; } u_int32_t sfl_receiver_get_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver) { return receiver->sFlowRcvrMaximumDatagramSize; } void sfl_receiver_set_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver, u_int32_t sFlowRcvrMaximumDatagramSize) { u_int32_t mdz = sFlowRcvrMaximumDatagramSize; if(mdz < SFL_MIN_DATAGRAM_SIZE) mdz = SFL_MIN_DATAGRAM_SIZE; receiver->sFlowRcvrMaximumDatagramSize = mdz; } SFLAddress *sfl_receiver_get_sFlowRcvrAddress(SFLReceiver *receiver) { return &receiver->sFlowRcvrAddress; } void sfl_receiver_set_sFlowRcvrAddress(SFLReceiver *receiver, SFLAddress *sFlowRcvrAddress) { if(sFlowRcvrAddress) receiver->sFlowRcvrAddress = *sFlowRcvrAddress; // structure copy #ifdef SFLOW_DO_SOCKET initSocket(receiver); #endif } u_int32_t sfl_receiver_get_sFlowRcvrPort(SFLReceiver *receiver) { return receiver->sFlowRcvrPort; } void sfl_receiver_set_sFlowRcvrPort(SFLReceiver *receiver, u_int32_t sFlowRcvrPort) { receiver->sFlowRcvrPort = sFlowRcvrPort; // update the socket structure #ifdef SFLOW_DO_SOCKET initSocket(receiver); #endif } /*_________________---------------------------__________________ _________________ sfl_receiver_tick __________________ -----------------___________________________------------------ */ void sfl_receiver_tick(SFLReceiver *receiver, time_t now) { // if there are any samples to send, flush them now if(receiver->sampleCollector.numSamples > 0) sendSample(receiver); // check the timeout if(receiver->sFlowRcvrTimeout && (u_int32_t)receiver->sFlowRcvrTimeout != 0xFFFFFFFF) { // count down one tick and reset if we reach 0 if(--receiver->sFlowRcvrTimeout == 0) reset(receiver); } } /*_________________-----------------------------__________________ _________________ receiver write utilities __________________ -----------------_____________________________------------------ */ inline static void put32(SFLReceiver *receiver, u_int32_t val) { *receiver->sampleCollector.datap++ = val; } inline static void putNet32(SFLReceiver *receiver, u_int32_t val) { *receiver->sampleCollector.datap++ = htonl(val); } inline static void putNet32_run(SFLReceiver *receiver, void *obj, size_t quads) { u_int32_t *from = (u_int32_t *)obj; while(quads--) putNet32(receiver, *from++); } inline static void putNet64(SFLReceiver *receiver, u_int64_t val64) { u_int32_t *firstQuadPtr = receiver->sampleCollector.datap; // first copy the bytes in memcpy((u_char *)firstQuadPtr, &val64, 8); if(htonl(1) != 1) { // swap the bytes, and reverse the quads too u_int32_t tmp = *receiver->sampleCollector.datap++; *firstQuadPtr = htonl(*receiver->sampleCollector.datap); *receiver->sampleCollector.datap++ = htonl(tmp); } else receiver->sampleCollector.datap += 2; } inline static void put128(SFLReceiver *receiver, u_char *val) { memcpy(receiver->sampleCollector.datap, val, 16); receiver->sampleCollector.datap += 4; } inline static void putString(SFLReceiver *receiver, SFLString *s) { putNet32(receiver, s->len); memcpy(receiver->sampleCollector.datap, s->str, s->len); receiver->sampleCollector.datap += (s->len + 3) / 4; /* pad to 4-byte boundary */ if ((s->len % 4) != 0){ u_int8_t padding = 4 - (s->len % 4); memset(((u_int8_t*)receiver->sampleCollector.datap)-padding, 0, padding); } } inline static u_int32_t stringEncodingLength(SFLString *s) { // answer in bytes, so remember to mulitply by 4 after rounding up to nearest 4-byte boundary return 4 + (((s->len + 3) / 4) * 4); } inline static void putAddress(SFLReceiver *receiver, SFLAddress *addr) { // encode unspecified addresses as IPV4:0.0.0.0 - or should we flag this as an error? if(addr->type == 0) { putNet32(receiver, SFLADDRESSTYPE_IP_V4); put32(receiver, 0); } else { putNet32(receiver, addr->type); if(addr->type == SFLADDRESSTYPE_IP_V4) put32(receiver, addr->address.ip_v4.addr); else put128(receiver, addr->address.ip_v6.addr); } } inline static u_int32_t addressEncodingLength(SFLAddress *addr) { return (addr->type == SFLADDRESSTYPE_IP_V6) ? 20 : 8; // type + address (unspecified == IPV4) } inline static void putMACAddress(SFLReceiver *receiver, const struct eth_addr mac) { memcpy(receiver->sampleCollector.datap, &mac, 6); receiver->sampleCollector.datap += 2; } inline static void putSwitch(SFLReceiver *receiver, SFLExtended_switch *sw) { putNet32(receiver, sw->src_vlan); putNet32(receiver, sw->src_priority); putNet32(receiver, sw->dst_vlan); putNet32(receiver, sw->dst_priority); } inline static void putRouter(SFLReceiver *receiver, SFLExtended_router *router) { putAddress(receiver, &router->nexthop); putNet32(receiver, router->src_mask); putNet32(receiver, router->dst_mask); } inline static u_int32_t routerEncodingLength(SFLExtended_router *router) { return addressEncodingLength(&router->nexthop) + 8; } inline static void putGateway(SFLReceiver *receiver, SFLExtended_gateway *gw) { putAddress(receiver, &gw->nexthop); putNet32(receiver, gw->as); putNet32(receiver, gw->src_as); putNet32(receiver, gw->src_peer_as); putNet32(receiver, gw->dst_as_path_segments); { u_int32_t seg = 0; for(; seg < gw->dst_as_path_segments; seg++) { putNet32(receiver, gw->dst_as_path[seg].type); putNet32(receiver, gw->dst_as_path[seg].length); putNet32_run(receiver, gw->dst_as_path[seg].as.seq, gw->dst_as_path[seg].length); } } putNet32(receiver, gw->communities_length); putNet32_run(receiver, gw->communities, gw->communities_length); putNet32(receiver, gw->localpref); } inline static u_int32_t gatewayEncodingLength(SFLExtended_gateway *gw) { u_int32_t elemSiz = addressEncodingLength(&gw->nexthop); u_int32_t seg = 0; elemSiz += 16; // as, src_as, src_peer_as, dst_as_path_segments for(; seg < gw->dst_as_path_segments; seg++) { elemSiz += 8; // type, length elemSiz += 4 * gw->dst_as_path[seg].length; // set/seq bytes } elemSiz += 4; // communities_length elemSiz += 4 * gw->communities_length; // communities elemSiz += 4; // localpref return elemSiz; } inline static void putUser(SFLReceiver *receiver, SFLExtended_user *user) { putNet32(receiver, user->src_charset); putString(receiver, &user->src_user); putNet32(receiver, user->dst_charset); putString(receiver, &user->dst_user); } inline static u_int32_t userEncodingLength(SFLExtended_user *user) { return 4 + stringEncodingLength(&user->src_user) + 4 + stringEncodingLength(&user->dst_user); } inline static void putUrl(SFLReceiver *receiver, SFLExtended_url *url) { putNet32(receiver, url->direction); putString(receiver, &url->url); putString(receiver, &url->host); } inline static u_int32_t urlEncodingLength(SFLExtended_url *url) { return 4 + stringEncodingLength(&url->url) + stringEncodingLength(&url->host); } inline static void putLabelStack(SFLReceiver *receiver, SFLLabelStack *labelStack) { putNet32(receiver, labelStack->depth); putNet32_run(receiver, labelStack->stack, labelStack->depth); } inline static u_int32_t labelStackEncodingLength(SFLLabelStack *labelStack) { return 4 + (4 * labelStack->depth); } inline static void putMpls(SFLReceiver *receiver, SFLExtended_mpls *mpls) { putAddress(receiver, &mpls->nextHop); putLabelStack(receiver, &mpls->in_stack); putLabelStack(receiver, &mpls->out_stack); } inline static u_int32_t mplsEncodingLength(SFLExtended_mpls *mpls) { return addressEncodingLength(&mpls->nextHop) + labelStackEncodingLength(&mpls->in_stack) + labelStackEncodingLength(&mpls->out_stack); } inline static void putNat(SFLReceiver *receiver, SFLExtended_nat *nat) { putAddress(receiver, &nat->src); putAddress(receiver, &nat->dst); } inline static u_int32_t natEncodingLength(SFLExtended_nat *nat) { return addressEncodingLength(&nat->src) + addressEncodingLength(&nat->dst); } inline static void putMplsTunnel(SFLReceiver *receiver, SFLExtended_mpls_tunnel *tunnel) { putString(receiver, &tunnel->tunnel_lsp_name); putNet32(receiver, tunnel->tunnel_id); putNet32(receiver, tunnel->tunnel_cos); } inline static u_int32_t mplsTunnelEncodingLength(SFLExtended_mpls_tunnel *tunnel) { return stringEncodingLength(&tunnel->tunnel_lsp_name) + 8; } inline static void putMplsVc(SFLReceiver *receiver, SFLExtended_mpls_vc *vc) { putString(receiver, &vc->vc_instance_name); putNet32(receiver, vc->vll_vc_id); putNet32(receiver, vc->vc_label_cos); } inline static u_int32_t mplsVcEncodingLength(SFLExtended_mpls_vc *vc) { return stringEncodingLength( &vc->vc_instance_name) + 8; } inline static void putMplsFtn(SFLReceiver *receiver, SFLExtended_mpls_FTN *ftn) { putString(receiver, &ftn->mplsFTNDescr); putNet32(receiver, ftn->mplsFTNMask); } inline static u_int32_t mplsFtnEncodingLength(SFLExtended_mpls_FTN *ftn) { return stringEncodingLength( &ftn->mplsFTNDescr) + 4; } inline static void putMplsLdpFec(SFLReceiver *receiver, SFLExtended_mpls_LDP_FEC *ldpfec) { putNet32(receiver, ldpfec->mplsFecAddrPrefixLength); } inline static u_int32_t mplsLdpFecEncodingLength(SFLExtended_mpls_LDP_FEC *ldpfec) { return 4; } inline static void putVlanTunnel(SFLReceiver *receiver, SFLExtended_vlan_tunnel *vlanTunnel) { putLabelStack(receiver, &vlanTunnel->stack); } inline static u_int32_t vlanTunnelEncodingLength(SFLExtended_vlan_tunnel *vlanTunnel) { return labelStackEncodingLength(&vlanTunnel->stack); } inline static void putGenericCounters(SFLReceiver *receiver, SFLIf_counters *counters) { putNet32(receiver, counters->ifIndex); putNet32(receiver, counters->ifType); putNet64(receiver, counters->ifSpeed); putNet32(receiver, counters->ifDirection); putNet32(receiver, counters->ifStatus); putNet64(receiver, counters->ifInOctets); putNet32(receiver, counters->ifInUcastPkts); putNet32(receiver, counters->ifInMulticastPkts); putNet32(receiver, counters->ifInBroadcastPkts); putNet32(receiver, counters->ifInDiscards); putNet32(receiver, counters->ifInErrors); putNet32(receiver, counters->ifInUnknownProtos); putNet64(receiver, counters->ifOutOctets); putNet32(receiver, counters->ifOutUcastPkts); putNet32(receiver, counters->ifOutMulticastPkts); putNet32(receiver, counters->ifOutBroadcastPkts); putNet32(receiver, counters->ifOutDiscards); putNet32(receiver, counters->ifOutErrors); putNet32(receiver, counters->ifPromiscuousMode); } /*_________________-----------------------------__________________ _________________ computeFlowSampleSize __________________ -----------------_____________________________------------------ */ static int computeFlowSampleSize(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs) { SFLFlow_sample_element *elem = fs->elements; #ifdef SFL_USE_32BIT_INDEX u_int siz = 52; /* tag, length, sequence_number, ds_class, ds_index, sampling_rate, sample_pool, drops, inputFormat, input, outputFormat, output, number of elements */ #else u_int siz = 40; /* tag, length, sequence_number, source_id, sampling_rate, sample_pool, drops, input, output, number of elements */ #endif fs->num_elements = 0; /* we're going to count them again even if this was set by the client */ for(; elem != NULL; elem = elem->nxt) { u_int elemSiz = 0; fs->num_elements++; siz += 8; /* tag, length */ switch(elem->tag) { case SFLFLOW_HEADER: elemSiz = 16; /* header_protocol, frame_length, stripped, header_length */ elemSiz += ((elem->flowType.header.header_length + 3) / 4) * 4; /* header, rounded up to nearest 4 bytes */ break; case SFLFLOW_ETHERNET: elemSiz = sizeof(SFLSampled_ethernet); break; case SFLFLOW_IPV4: elemSiz = sizeof(SFLSampled_ipv4); break; case SFLFLOW_IPV6: elemSiz = sizeof(SFLSampled_ipv6); break; case SFLFLOW_EX_SWITCH: elemSiz = sizeof(SFLExtended_switch); break; case SFLFLOW_EX_ROUTER: elemSiz = routerEncodingLength(&elem->flowType.router); break; case SFLFLOW_EX_GATEWAY: elemSiz = gatewayEncodingLength(&elem->flowType.gateway); break; case SFLFLOW_EX_USER: elemSiz = userEncodingLength(&elem->flowType.user); break; case SFLFLOW_EX_URL: elemSiz = urlEncodingLength(&elem->flowType.url); break; case SFLFLOW_EX_MPLS: elemSiz = mplsEncodingLength(&elem->flowType.mpls); break; case SFLFLOW_EX_NAT: elemSiz = natEncodingLength(&elem->flowType.nat); break; case SFLFLOW_EX_MPLS_TUNNEL: elemSiz = mplsTunnelEncodingLength(&elem->flowType.mpls_tunnel); break; case SFLFLOW_EX_MPLS_VC: elemSiz = mplsVcEncodingLength(&elem->flowType.mpls_vc); break; case SFLFLOW_EX_MPLS_FTN: elemSiz = mplsFtnEncodingLength(&elem->flowType.mpls_ftn); break; case SFLFLOW_EX_MPLS_LDP_FEC: elemSiz = mplsLdpFecEncodingLength(&elem->flowType.mpls_ldp_fec); break; case SFLFLOW_EX_VLAN_TUNNEL: elemSiz = vlanTunnelEncodingLength(&elem->flowType.vlan_tunnel); break; case SFLFLOW_EX_IPV4_TUNNEL_EGRESS: case SFLFLOW_EX_IPV4_TUNNEL_INGRESS: elemSiz = sizeof(SFLSampled_ipv4); break; case SFLFLOW_EX_VNI_EGRESS: case SFLFLOW_EX_VNI_INGRESS: elemSiz = sizeof(SFLExtended_vni); break; default: sflError(receiver, "unexpected packet_data_tag"); return -1; break; } // cache the element size, and accumulate it into the overall FlowSample size elem->length = elemSiz; siz += elemSiz; } return siz; } /*_________________-------------------------------__________________ _________________ sfl_receiver_writeFlowSample __________________ -----------------_______________________________------------------ */ int sfl_receiver_writeFlowSample(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs) { int packedSize; if(fs == NULL) return -1; if((packedSize = computeFlowSampleSize(receiver, fs)) == -1) return -1; // check in case this one sample alone is too big for the datagram // in fact - if it is even half as big then we should ditch it. Very // important to avoid overruning the packet buffer. if(packedSize > (int)(receiver->sFlowRcvrMaximumDatagramSize / 2)) { sflError(receiver, "flow sample too big for datagram"); return -1; } // if the sample pkt is full enough so that this sample might put // it over the limit, then we should send it now before going on. if((receiver->sampleCollector.pktlen + packedSize) >= receiver->sFlowRcvrMaximumDatagramSize) sendSample(receiver); receiver->sampleCollector.numSamples++; #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, SFLFLOW_SAMPLE_EXPANDED); #else putNet32(receiver, SFLFLOW_SAMPLE); #endif putNet32(receiver, packedSize - 8); // don't include tag and len putNet32(receiver, fs->sequence_number); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, fs->ds_class); putNet32(receiver, fs->ds_index); #else putNet32(receiver, fs->source_id); #endif putNet32(receiver, fs->sampling_rate); putNet32(receiver, fs->sample_pool); putNet32(receiver, fs->drops); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, fs->inputFormat); putNet32(receiver, fs->input); putNet32(receiver, fs->outputFormat); putNet32(receiver, fs->output); #else putNet32(receiver, fs->input); putNet32(receiver, fs->output); #endif putNet32(receiver, fs->num_elements); { SFLFlow_sample_element *elem = fs->elements; for(; elem != NULL; elem = elem->nxt) { putNet32(receiver, elem->tag); putNet32(receiver, elem->length); // length cached in computeFlowSampleSize() switch(elem->tag) { case SFLFLOW_HEADER: putNet32(receiver, elem->flowType.header.header_protocol); putNet32(receiver, elem->flowType.header.frame_length); putNet32(receiver, elem->flowType.header.stripped); putNet32(receiver, elem->flowType.header.header_length); /* the header */ memcpy(receiver->sampleCollector.datap, elem->flowType.header.header_bytes, elem->flowType.header.header_length); /* round up to multiple of 4 to preserve alignment */ receiver->sampleCollector.datap += ((elem->flowType.header.header_length + 3) / 4); break; case SFLFLOW_ETHERNET: putNet32(receiver, elem->flowType.ethernet.eth_len); putMACAddress(receiver, elem->flowType.ethernet.src_mac); putMACAddress(receiver, elem->flowType.ethernet.dst_mac); putNet32(receiver, elem->flowType.ethernet.eth_type); break; case SFLFLOW_IPV4: case SFLFLOW_EX_IPV4_TUNNEL_EGRESS: case SFLFLOW_EX_IPV4_TUNNEL_INGRESS: putNet32(receiver, elem->flowType.ipv4.length); putNet32(receiver, elem->flowType.ipv4.protocol); put32(receiver, elem->flowType.ipv4.src_ip.addr); put32(receiver, elem->flowType.ipv4.dst_ip.addr); putNet32(receiver, elem->flowType.ipv4.src_port); putNet32(receiver, elem->flowType.ipv4.dst_port); putNet32(receiver, elem->flowType.ipv4.tcp_flags); putNet32(receiver, elem->flowType.ipv4.tos); break; case SFLFLOW_IPV6: putNet32(receiver, elem->flowType.ipv6.length); putNet32(receiver, elem->flowType.ipv6.protocol); put128(receiver, elem->flowType.ipv6.src_ip.addr); put128(receiver, elem->flowType.ipv6.dst_ip.addr); putNet32(receiver, elem->flowType.ipv6.src_port); putNet32(receiver, elem->flowType.ipv6.dst_port); putNet32(receiver, elem->flowType.ipv6.tcp_flags); putNet32(receiver, elem->flowType.ipv6.priority); break; case SFLFLOW_EX_SWITCH: putSwitch(receiver, &elem->flowType.sw); break; case SFLFLOW_EX_ROUTER: putRouter(receiver, &elem->flowType.router); break; case SFLFLOW_EX_GATEWAY: putGateway(receiver, &elem->flowType.gateway); break; case SFLFLOW_EX_USER: putUser(receiver, &elem->flowType.user); break; case SFLFLOW_EX_URL: putUrl(receiver, &elem->flowType.url); break; case SFLFLOW_EX_MPLS: putMpls(receiver, &elem->flowType.mpls); break; case SFLFLOW_EX_NAT: putNat(receiver, &elem->flowType.nat); break; case SFLFLOW_EX_MPLS_TUNNEL: putMplsTunnel(receiver, &elem->flowType.mpls_tunnel); break; case SFLFLOW_EX_MPLS_VC: putMplsVc(receiver, &elem->flowType.mpls_vc); break; case SFLFLOW_EX_MPLS_FTN: putMplsFtn(receiver, &elem->flowType.mpls_ftn); break; case SFLFLOW_EX_MPLS_LDP_FEC: putMplsLdpFec(receiver, &elem->flowType.mpls_ldp_fec); break; case SFLFLOW_EX_VLAN_TUNNEL: putVlanTunnel(receiver, &elem->flowType.vlan_tunnel); break; case SFLFLOW_EX_VNI_EGRESS: case SFLFLOW_EX_VNI_INGRESS: putNet32(receiver, elem->flowType.tunnel_vni.vni); break; default: sflError(receiver, "unexpected packet_data_tag"); return -1; break; } } } // sanity check assert(((u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data - receiver->sampleCollector.pktlen) == (u_int32_t)packedSize); // update the pktlen receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; return packedSize; } /*_________________-----------------------------__________________ _________________ computeCountersSampleSize __________________ -----------------_____________________________------------------ */ static int computeCountersSampleSize(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs) { SFLCounters_sample_element *elem = cs->elements; #ifdef SFL_USE_32BIT_INDEX u_int siz = 24; /* tag, length, sequence_number, ds_class, ds_index, number of elements */ #else u_int siz = 20; /* tag, length, sequence_number, source_id, number of elements */ #endif cs->num_elements = 0; /* we're going to count them again even if this was set by the client */ for(; elem != NULL; elem = elem->nxt) { u_int elemSiz = 0; cs->num_elements++; siz += 8; /* tag, length */ switch(elem->tag) { case SFLCOUNTERS_GENERIC: elemSiz = SFL_CTR_GENERIC_XDR_SIZE; break; case SFLCOUNTERS_ETHERNET: elemSiz = SFL_CTR_ETHERNET_XDR_SIZE; break; case SFLCOUNTERS_TOKENRING: elemSiz = sizeof(elem->counterBlock.tokenring); break; case SFLCOUNTERS_VG: elemSiz = sizeof(elem->counterBlock.vg); break; case SFLCOUNTERS_VLAN: elemSiz = sizeof(elem->counterBlock.vlan); break; case SFLCOUNTERS_LACP: elemSiz = SFL_CTR_LACP_XDR_SIZE; break; case SFLCOUNTERS_OPENFLOWPORT: elemSiz = SFL_CTR_OPENFLOWPORT_XDR_SIZE; break; case SFLCOUNTERS_PORTNAME: elemSiz = stringEncodingLength(&elem->counterBlock.portName.portName); break; case SFLCOUNTERS_APP_RESOURCES: elemSiz = SFL_CTR_APP_RESOURCES_XDR_SIZE; break; case SFLCOUNTERS_OVSDP: elemSiz = SFL_CTR_OVSDP_XDR_SIZE; break; default: sflError(receiver, "unexpected counters_tag"); return -1; break; } // cache the element size, and accumulate it into the overall FlowSample size elem->length = elemSiz; siz += elemSiz; } return siz; } /*_________________----------------------------------__________________ _________________ sfl_receiver_writeCountersSample __________________ -----------------__________________________________------------------ */ int sfl_receiver_writeCountersSample(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs) { int packedSize; if(cs == NULL) return -1; // if the sample pkt is full enough so that this sample might put // it over the limit, then we should send it now. if((packedSize = computeCountersSampleSize(receiver, cs)) == -1) return -1; // check in case this one sample alone is too big for the datagram // in fact - if it is even half as big then we should ditch it. Very // important to avoid overruning the packet buffer. if(packedSize > (int)(receiver->sFlowRcvrMaximumDatagramSize / 2)) { sflError(receiver, "counters sample too big for datagram"); return -1; } if((receiver->sampleCollector.pktlen + packedSize) >= receiver->sFlowRcvrMaximumDatagramSize) sendSample(receiver); receiver->sampleCollector.numSamples++; #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, SFLCOUNTERS_SAMPLE_EXPANDED); #else putNet32(receiver, SFLCOUNTERS_SAMPLE); #endif putNet32(receiver, packedSize - 8); // tag and length not included putNet32(receiver, cs->sequence_number); #ifdef SFL_USE_32BIT_INDEX putNet32(receiver, cs->ds_class); putNet32(receiver, cs->ds_index); #else putNet32(receiver, cs->source_id); #endif putNet32(receiver, cs->num_elements); { SFLCounters_sample_element *elem = cs->elements; for(; elem != NULL; elem = elem->nxt) { putNet32(receiver, elem->tag); putNet32(receiver, elem->length); // length cached in computeCountersSampleSize() switch(elem->tag) { case SFLCOUNTERS_GENERIC: putGenericCounters(receiver, &(elem->counterBlock.generic)); break; case SFLCOUNTERS_ETHERNET: // all these counters are 32-bit putNet32_run(receiver, &elem->counterBlock.ethernet, sizeof(elem->counterBlock.ethernet) / 4); break; case SFLCOUNTERS_TOKENRING: // all these counters are 32-bit putNet32_run(receiver, &elem->counterBlock.tokenring, sizeof(elem->counterBlock.tokenring) / 4); break; case SFLCOUNTERS_VG: // mixed sizes putNet32(receiver, elem->counterBlock.vg.dot12InHighPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12InHighPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12InNormPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12InNormPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12InIPMErrors); putNet32(receiver, elem->counterBlock.vg.dot12InOversizeFrameErrors); putNet32(receiver, elem->counterBlock.vg.dot12InDataErrors); putNet32(receiver, elem->counterBlock.vg.dot12InNullAddressedFrames); putNet32(receiver, elem->counterBlock.vg.dot12OutHighPriorityFrames); putNet64(receiver, elem->counterBlock.vg.dot12OutHighPriorityOctets); putNet32(receiver, elem->counterBlock.vg.dot12TransitionIntoTrainings); putNet64(receiver, elem->counterBlock.vg.dot12HCInHighPriorityOctets); putNet64(receiver, elem->counterBlock.vg.dot12HCInNormPriorityOctets); putNet64(receiver, elem->counterBlock.vg.dot12HCOutHighPriorityOctets); break; case SFLCOUNTERS_VLAN: // mixed sizes putNet32(receiver, elem->counterBlock.vlan.vlan_id); putNet64(receiver, elem->counterBlock.vlan.octets); putNet32(receiver, elem->counterBlock.vlan.ucastPkts); putNet32(receiver, elem->counterBlock.vlan.multicastPkts); putNet32(receiver, elem->counterBlock.vlan.broadcastPkts); putNet32(receiver, elem->counterBlock.vlan.discards); break; case SFLCOUNTERS_LACP: putMACAddress(receiver, elem->counterBlock.lacp.actorSystemID); putMACAddress(receiver, elem->counterBlock.lacp.partnerSystemID); putNet32(receiver, elem->counterBlock.lacp.attachedAggID); put32(receiver, elem->counterBlock.lacp.portState.all); putNet32(receiver, elem->counterBlock.lacp.LACPDUsRx); putNet32(receiver, elem->counterBlock.lacp.markerPDUsRx); putNet32(receiver, elem->counterBlock.lacp.markerResponsePDUsRx); putNet32(receiver, elem->counterBlock.lacp.unknownRx); putNet32(receiver, elem->counterBlock.lacp.illegalRx); putNet32(receiver, elem->counterBlock.lacp.LACPDUsTx); putNet32(receiver, elem->counterBlock.lacp.markerPDUsTx); putNet32(receiver, elem->counterBlock.lacp.markerResponsePDUsTx); break; case SFLCOUNTERS_OPENFLOWPORT: putNet64(receiver, elem->counterBlock.ofPort.datapath_id); putNet32(receiver, elem->counterBlock.ofPort.port_no); break; case SFLCOUNTERS_PORTNAME: putString(receiver, &elem->counterBlock.portName.portName); break; case SFLCOUNTERS_APP_RESOURCES: putNet32(receiver, elem->counterBlock.appResources.user_time); putNet32(receiver, elem->counterBlock.appResources.system_time); putNet64(receiver, elem->counterBlock.appResources.mem_used); putNet64(receiver, elem->counterBlock.appResources.mem_max); putNet32(receiver, elem->counterBlock.appResources.fd_open); putNet32(receiver, elem->counterBlock.appResources.fd_max); putNet32(receiver, elem->counterBlock.appResources.conn_open); putNet32(receiver, elem->counterBlock.appResources.conn_max); break; case SFLCOUNTERS_OVSDP: putNet32(receiver, elem->counterBlock.ovsdp.n_hit); putNet32(receiver, elem->counterBlock.ovsdp.n_missed); putNet32(receiver, elem->counterBlock.ovsdp.n_lost); putNet32(receiver, elem->counterBlock.ovsdp.n_mask_hit); putNet32(receiver, elem->counterBlock.ovsdp.n_flows); putNet32(receiver, elem->counterBlock.ovsdp.n_masks); break; default: sflError(receiver, "unexpected counters_tag"); return -1; break; } } } // sanity check assert(((u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data - receiver->sampleCollector.pktlen) == (u_int32_t)packedSize); // update the pktlen receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; return packedSize; } /*_________________---------------------------------__________________ _________________ sfl_receiver_samplePacketsSent __________________ -----------------_________________________________------------------ */ u_int32_t sfl_receiver_samplePacketsSent(SFLReceiver *receiver) { return receiver->sampleCollector.packetSeqNo; } /*_________________---------------------------__________________ _________________ sendSample __________________ -----------------___________________________------------------ */ static void sendSample(SFLReceiver *receiver) { /* construct and send out the sample, then reset for the next one... */ /* first fill in the header with the latest values */ /* version, agent_address and sub_agent_id were pre-set. */ u_int32_t hdrIdx = (receiver->agent->myIP.type == SFLADDRESSTYPE_IP_V6) ? 7 : 4; receiver->sampleCollector.data[hdrIdx++] = htonl(++receiver->sampleCollector.packetSeqNo); /* seq no */ receiver->sampleCollector.data[hdrIdx++] = htonl((receiver->agent->now - receiver->agent->bootTime) * 1000); /* uptime */ receiver->sampleCollector.data[hdrIdx++] = htonl(receiver->sampleCollector.numSamples); /* num samples */ /* send */ if(receiver->agent->sendFn) (*receiver->agent->sendFn)(receiver->agent->magic, receiver->agent, receiver, (u_char *)receiver->sampleCollector.data, receiver->sampleCollector.pktlen); else { #ifdef SFLOW_DO_SOCKET /* send it myself */ if (receiver->sFlowRcvrAddress.type == SFLADDRESSTYPE_IP_V6) { u_int32_t soclen = sizeof(struct sockaddr_in6); int result = sendto(receiver->agent->receiverSocket6, receiver->sampleCollector.data, receiver->sampleCollector.pktlen, 0, (struct sockaddr *)&receiver->receiver6, soclen); if(result == -1 && errno != EINTR) sfl_agent_sysError(receiver->agent, "receiver", "IPv6 socket sendto error"); if(result == 0) sfl_agent_error(receiver->agent, "receiver", "IPv6 socket sendto returned 0"); } else { u_int32_t soclen = sizeof(struct sockaddr_in); int result = sendto(receiver->agent->receiverSocket4, receiver->sampleCollector.data, receiver->sampleCollector.pktlen, 0, (struct sockaddr *)&receiver->receiver4, soclen); if(result == -1 && errno != EINTR) sfl_agent_sysError(receiver->agent, "receiver", "socket sendto error"); if(result == 0) sfl_agent_error(receiver->agent, "receiver", "socket sendto returned 0"); } #endif } /* reset for the next time */ resetSampleCollector(receiver); } /*_________________---------------------------__________________ _________________ resetSampleCollector __________________ -----------------___________________________------------------ */ static void resetSampleCollector(SFLReceiver *receiver) { receiver->sampleCollector.pktlen = 0; receiver->sampleCollector.numSamples = 0; /* point the datap to just after the header */ receiver->sampleCollector.datap = (receiver->agent->myIP.type == SFLADDRESSTYPE_IP_V6) ? (receiver->sampleCollector.data + 10) : (receiver->sampleCollector.data + 7); receiver->sampleCollector.pktlen = (u_char *)receiver->sampleCollector.datap - (u_char *)receiver->sampleCollector.data; } /*_________________---------------------------__________________ _________________ sflError __________________ -----------------___________________________------------------ */ static void sflError(SFLReceiver *receiver, char *msg) { sfl_agent_error(receiver->agent, "receiver", msg); resetSampleCollector(receiver); } #endif /* !__CHECKER__ */ openvswitch-2.5.9/lib/PaxHeaders.82075/getrusage-windows.c0000644000000000000000000000013213534540071020234 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.957854723 openvswitch-2.5.9/lib/getrusage-windows.c0000644000175000017500000000467713534540071021740 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(getrusage_windows); static void usage_to_timeval(FILETIME *ft, struct timeval *tv) { ULARGE_INTEGER time; time.LowPart = ft->dwLowDateTime; time.HighPart = ft->dwHighDateTime; tv->tv_sec = time.QuadPart / 10000000; tv->tv_usec = (time.QuadPart % 10000000) / 10; } int getrusage(int who, struct rusage *usage) { FILETIME creation_time, exit_time, kernel_time, user_time; PROCESS_MEMORY_COUNTERS pmc; memset(usage, 0, sizeof(struct rusage)); if (who == RUSAGE_SELF) { if (!GetProcessTimes(GetCurrentProcess(), &creation_time, &exit_time, &kernel_time, &user_time)) { VLOG_ERR("failed at GetProcessTimes: %s", ovs_lasterror_to_string()); return -1; } if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { VLOG_ERR("failed at GetProcessMemoryInfo: %s", ovs_lasterror_to_string()); return -1; } usage_to_timeval(&kernel_time, &usage->ru_stime); usage_to_timeval(&user_time, &usage->ru_utime); usage->ru_majflt = pmc.PageFaultCount; usage->ru_maxrss = pmc.PeakWorkingSetSize / 1024; return 0; } else if (who == RUSAGE_THREAD) { if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time, &kernel_time, &user_time)) { VLOG_ERR("failed at GetThreadTimes: %s", ovs_lasterror_to_string()); return -1; } usage_to_timeval(&kernel_time, &usage->ru_stime); usage_to_timeval(&user_time, &usage->ru_utime); return 0; } else { return -1; } } openvswitch-2.5.9/lib/PaxHeaders.82075/db-ctl-base.c0000644000000000000000000000013213534540071016633 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.693852778 openvswitch-2.5.9/lib/db-ctl-base.c0000644000175000017500000020170613534540071020327 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "json.h" #include "openvswitch/vlog.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "ovsdb-idl-provider.h" #include "shash.h" #include "sset.h" #include "string.h" #include "table.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(db_ctl_base); /* This array defines the 'show' command output format. User can check the * definition in utilities/ovs-vsctl.c as reference. * * Particularly, if an element in 'columns[]' represents a reference to * another table, the referred table must also be defined as an entry in * in 'cmd_show_tables[]'. * * The definition must end with an all-NULL entry. It is initalized once * when ctl_init() is called. * * */ static const struct cmd_show_table *cmd_show_tables; /* ctl_exit() is called by ctl_fatal(). User can optionally supply an exit * function ctl_exit_func() via ctl_init. If supplied, this function will * be called by ctl_exit() */ static void (*ctl_exit_func)(int status) = NULL; OVS_NO_RETURN static void ctl_exit(int status); /* Represents all tables in the schema. User must define 'tables' * in implementation and supply via clt_init(). The definition must end * with an all-NULL entry. */ static const struct ctl_table_class *tables; static struct shash all_commands = SHASH_INITIALIZER(&all_commands); static const struct ctl_table_class *get_table(const char *table_name); static void set_column(const struct ctl_table_class *, const struct ovsdb_idl_row *, const char *, struct ovsdb_symbol_table *); static struct option * find_option(const char *name, struct option *options, size_t n_options) { size_t i; for (i = 0; i < n_options; i++) { if (!strcmp(options[i].name, name)) { return &options[i]; } } return NULL; } static struct option * add_option(struct option **optionsp, size_t *n_optionsp, size_t *allocated_optionsp) { if (*n_optionsp >= *allocated_optionsp) { *optionsp = x2nrealloc(*optionsp, allocated_optionsp, sizeof **optionsp); } return &(*optionsp)[(*n_optionsp)++]; } /* Converts the command arguments into format that can be parsed by * bash completion script. * * Therein, arguments will be attached with following prefixes: * * !argument :: The argument is required * ?argument :: The argument is optional * *argument :: The argument may appear any number (0 or more) times * +argument :: The argument may appear one or more times * */ static void print_command_arguments(const struct ctl_command_syntax *command) { /* * The argument string is parsed in reverse. We use a stack 'oew_stack' to * keep track of nested optionals. Whenever a ']' is encountered, we push * a bit to 'oew_stack'. The bit is set to 1 if the ']' is not nested. * Subsequently, we pop an entry everytime '[' is met. * * We use 'whole_word_is_optional' value to decide whether or not a ! or + * should be added on encountering a space: if the optional surrounds the * whole word then it shouldn't be, but if it is only a part of the word * (i.e. [key=]value), it should be. */ uint32_t oew_stack = 0; const char *arguments = command->arguments; int length = strlen(arguments); if (!length) { return; } /* Output buffer, written backward from end. */ char *output = xmalloc(2 * length); char *outp = output + 2 * length; *--outp = '\0'; bool in_repeated = false; bool whole_word_is_optional = false; for (const char *inp = arguments + length; inp > arguments; ) { switch (*--inp) { case ']': oew_stack <<= 1; if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') { oew_stack |= 1; } break; case '[': /* Checks if the whole word is optional, and sets the * 'whole_word_is_optional' accordingly. */ if ((inp == arguments || inp[-1] == ' ') && oew_stack & 1) { *--outp = in_repeated ? '*' : '?'; whole_word_is_optional = true; } else { *--outp = '?'; whole_word_is_optional = false; } oew_stack >>= 1; break; case ' ': if (!whole_word_is_optional) { *--outp = in_repeated ? '+' : '!'; } *--outp = ' '; in_repeated = false; whole_word_is_optional = false; break; case '.': in_repeated = true; break; default: *--outp = *inp; break; } } if (arguments[0] != '[' && outp != output + 2 * length - 1) { *--outp = in_repeated ? '+' : '!'; } printf("%s", outp); free(output); } static void die_if_error(char *error) { if (error) { ctl_fatal("%s", error); } } static int to_lower_and_underscores(unsigned c) { return c == '-' ? '_' : tolower(c); } static unsigned int score_partial_match(const char *name, const char *s) { int score; if (!strcmp(name, s)) { return UINT_MAX; } for (score = 0; ; score++, name++, s++) { if (to_lower_and_underscores(*name) != to_lower_and_underscores(*s)) { break; } else if (*name == '\0') { return UINT_MAX - 1; } } return *s == '\0' ? score : 0; } static struct ovsdb_symbol * create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp) { struct ovsdb_symbol *symbol; if (id[0] != '@') { ctl_fatal("row id \"%s\" does not begin with \"@\"", id); } if (newp) { *newp = ovsdb_symbol_table_get(symtab, id) == NULL; } symbol = ovsdb_symbol_table_insert(symtab, id); if (symbol->created) { ctl_fatal("row id \"%s\" may only be specified on one --id option", id); } symbol->created = true; return symbol; } static const struct ovsdb_idl_row * get_row_by_id(struct ctl_context *ctx, const struct ctl_table_class *table, const struct ctl_row_id *id, const char *record_id) { const struct ovsdb_idl_row *referrer, *final; if (!id->table) { return NULL; } if (!id->name_column) { if (strcmp(record_id, ".")) { return NULL; } referrer = ovsdb_idl_first_row(ctx->idl, id->table); if (!referrer || ovsdb_idl_next_row(referrer)) { return NULL; } } else { const struct ovsdb_idl_row *row; referrer = NULL; for (row = ovsdb_idl_first_row(ctx->idl, id->table); row != NULL; row = ovsdb_idl_next_row(row)) { const struct ovsdb_datum *name; name = ovsdb_idl_get(row, id->name_column, OVSDB_TYPE_STRING, OVSDB_TYPE_VOID); if (name->n == 1 && !strcmp(name->keys[0].string, record_id)) { if (referrer) { ctl_fatal("multiple rows in %s match \"%s\"", table->class->name, record_id); } referrer = row; } } } if (!referrer) { return NULL; } final = NULL; if (id->uuid_column) { const struct ovsdb_datum *uuid; ovsdb_idl_txn_verify(referrer, id->uuid_column); uuid = ovsdb_idl_get(referrer, id->uuid_column, OVSDB_TYPE_UUID, OVSDB_TYPE_VOID); if (uuid->n == 1) { final = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid->keys[0].uuid); } else { final = NULL; } } else { final = referrer; } return final; } static const struct ovsdb_idl_row * get_row(struct ctl_context *ctx, const struct ctl_table_class *table, const char *record_id, bool must_exist) { const struct ovsdb_idl_row *row; struct uuid uuid; row = NULL; if (uuid_from_string(&uuid, record_id)) { row = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid); } if (!row) { int i; for (i = 0; i < ARRAY_SIZE(table->row_ids); i++) { row = get_row_by_id(ctx, table, &table->row_ids[i], record_id); if (row) { break; } } } if (must_exist && !row) { ctl_fatal("no row \"%s\" in table %s", record_id, table->class->name); } return row; } static char * get_column(const struct ctl_table_class *table, const char *column_name, const struct ovsdb_idl_column **columnp) { const struct ovsdb_idl_column *best_match = NULL; unsigned int best_score = 0; size_t i; for (i = 0; i < table->class->n_columns; i++) { const struct ovsdb_idl_column *column = &table->class->columns[i]; unsigned int score = score_partial_match(column->name, column_name); if (score > best_score) { best_match = column; best_score = score; } else if (score == best_score) { best_match = NULL; } } *columnp = best_match; if (best_match) { return NULL; } else if (best_score) { return xasprintf("%s contains more than one column whose name " "matches \"%s\"", table->class->name, column_name); } else { return xasprintf("%s does not contain a column whose name matches " "\"%s\"", table->class->name, column_name); } } static void pre_get_column(struct ctl_context *ctx, const struct ctl_table_class *table, const char *column_name, const struct ovsdb_idl_column **columnp) { die_if_error(get_column(table, column_name, columnp)); ovsdb_idl_add_column(ctx->idl, *columnp); } static const struct ctl_table_class * pre_get_table(struct ctl_context *ctx, const char *table_name) { const struct ctl_table_class *table_class; int i; table_class = get_table(table_name); ovsdb_idl_add_table(ctx->idl, table_class->class); for (i = 0; i < ARRAY_SIZE(table_class->row_ids); i++) { const struct ctl_row_id *id = &table_class->row_ids[i]; if (id->table) { ovsdb_idl_add_table(ctx->idl, id->table); } if (id->name_column) { ovsdb_idl_add_column(ctx->idl, id->name_column); } if (id->uuid_column) { ovsdb_idl_add_column(ctx->idl, id->uuid_column); } } return table_class; } static char * missing_operator_error(const char *arg, const char **allowed_operators, size_t n_allowed) { struct ds s; ds_init(&s); ds_put_format(&s, "%s: argument does not end in ", arg); ds_put_format(&s, "\"%s\"", allowed_operators[0]); if (n_allowed == 2) { ds_put_format(&s, " or \"%s\"", allowed_operators[1]); } else if (n_allowed > 2) { size_t i; for (i = 1; i < n_allowed - 1; i++) { ds_put_format(&s, ", \"%s\"", allowed_operators[i]); } ds_put_format(&s, ", or \"%s\"", allowed_operators[i]); } ds_put_format(&s, " followed by a value."); return ds_steal_cstr(&s); } /* Breaks 'arg' apart into a number of fields in the following order: * * - The name of a column in 'table', stored into '*columnp'. The column * name may be abbreviated. * * - Optionally ':' followed by a key string. The key is stored as a * malloc()'d string into '*keyp', or NULL if no key is present in * 'arg'. * * - If 'valuep' is nonnull, an operator followed by a value string. The * allowed operators are the 'n_allowed' string in 'allowed_operators', * or just "=" if 'n_allowed' is 0. If 'operatorp' is nonnull, then the * index of the operator within 'allowed_operators' is stored into * '*operatorp'. The value is stored as a malloc()'d string into * '*valuep', or NULL if no value is present in 'arg'. * * On success, returns NULL. On failure, returned a malloc()'d string error * message and stores NULL into all of the nonnull output arguments. */ static char * OVS_WARN_UNUSED_RESULT parse_column_key_value(const char *arg, const struct ctl_table_class *table, const struct ovsdb_idl_column **columnp, char **keyp, int *operatorp, const char **allowed_operators, size_t n_allowed, char **valuep) { const char *p = arg; char *column_name; char *error; ovs_assert(!(operatorp && !valuep)); *keyp = NULL; if (valuep) { *valuep = NULL; } /* Parse column name. */ error = ovsdb_token_parse(&p, &column_name); if (error) { goto error; } if (column_name[0] == '\0') { free(column_name); error = xasprintf("%s: missing column name", arg); goto error; } error = get_column(table, column_name, columnp); free(column_name); if (error) { goto error; } /* Parse key string. */ if (*p == ':') { p++; error = ovsdb_token_parse(&p, keyp); if (error) { goto error; } } /* Parse value string. */ if (valuep) { size_t best_len; size_t i; int best; if (!allowed_operators) { static const char *equals = "="; allowed_operators = = n_allowed = 1; } best = -1; best_len = 0; for (i = 0; i < n_allowed; i++) { const char *op = allowed_operators[i]; size_t op_len = strlen(op); if (op_len > best_len && !strncmp(op, p, op_len) && p[op_len]) { best_len = op_len; best = i; } } if (best < 0) { error = missing_operator_error(arg, allowed_operators, n_allowed); goto error; } if (operatorp) { *operatorp = best; } *valuep = xstrdup(p + best_len); } else { if (*p != '\0') { error = xasprintf("%s: trailing garbage \"%s\" in argument", arg, p); goto error; } } return NULL; error: *columnp = NULL; free(*keyp); *keyp = NULL; if (valuep) { free(*valuep); *valuep = NULL; if (operatorp) { *operatorp = -1; } } return error; } static const struct ovsdb_idl_column * pre_parse_column_key_value(struct ctl_context *ctx, const char *arg, const struct ctl_table_class *table) { const struct ovsdb_idl_column *column; const char *p; char *column_name; p = arg; die_if_error(ovsdb_token_parse(&p, &column_name)); if (column_name[0] == '\0') { ctl_fatal("%s: missing column name", arg); } pre_get_column(ctx, table, column_name, &column); free(column_name); return column; } static void check_mutable(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column) { if (!ovsdb_idl_is_mutable(row, column)) { ctl_fatal("cannot modify read-only column %s in table %s", column->name, row->table->class->name); } } #define RELOPS \ RELOP(RELOP_EQ, "=") \ RELOP(RELOP_NE, "!=") \ RELOP(RELOP_LT, "<") \ RELOP(RELOP_GT, ">") \ RELOP(RELOP_LE, "<=") \ RELOP(RELOP_GE, ">=") \ RELOP(RELOP_SET_EQ, "{=}") \ RELOP(RELOP_SET_NE, "{!=}") \ RELOP(RELOP_SET_LT, "{<}") \ RELOP(RELOP_SET_GT, "{>}") \ RELOP(RELOP_SET_LE, "{<=}") \ RELOP(RELOP_SET_GE, "{>=}") enum relop { #define RELOP(ENUM, STRING) ENUM, RELOPS #undef RELOP }; static bool is_set_operator(enum relop op) { return (op == RELOP_SET_EQ || op == RELOP_SET_NE || op == RELOP_SET_LT || op == RELOP_SET_GT || op == RELOP_SET_LE || op == RELOP_SET_GE); } static bool evaluate_relop(const struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type, enum relop op) { switch (op) { case RELOP_EQ: case RELOP_SET_EQ: return ovsdb_datum_compare_3way(a, b, type) == 0; case RELOP_NE: case RELOP_SET_NE: return ovsdb_datum_compare_3way(a, b, type) != 0; case RELOP_LT: return ovsdb_datum_compare_3way(a, b, type) < 0; case RELOP_GT: return ovsdb_datum_compare_3way(a, b, type) > 0; case RELOP_LE: return ovsdb_datum_compare_3way(a, b, type) <= 0; case RELOP_GE: return ovsdb_datum_compare_3way(a, b, type) >= 0; case RELOP_SET_LT: return b->n > a->n && ovsdb_datum_includes_all(a, b, type); case RELOP_SET_GT: return a->n > b->n && ovsdb_datum_includes_all(b, a, type); case RELOP_SET_LE: return ovsdb_datum_includes_all(a, b, type); case RELOP_SET_GE: return ovsdb_datum_includes_all(b, a, type); default: OVS_NOT_REACHED(); } } static bool is_condition_satisfied(const struct ctl_table_class *table, const struct ovsdb_idl_row *row, const char *arg, struct ovsdb_symbol_table *symtab) { static const char *operators[] = { #define RELOP(ENUM, STRING) STRING, RELOPS #undef RELOP }; const struct ovsdb_idl_column *column; const struct ovsdb_datum *have_datum; char *key_string, *value_string; struct ovsdb_type type; int operator; bool retval; char *error; error = parse_column_key_value(arg, table, &column, &key_string, &operator, operators, ARRAY_SIZE(operators), &value_string); die_if_error(error); if (!value_string) { ctl_fatal("%s: missing value", arg); } type = column->type; type.n_max = UINT_MAX; have_datum = ovsdb_idl_read(row, column); if (key_string) { union ovsdb_atom want_key; struct ovsdb_datum b; unsigned int idx; if (column->type.value.type == OVSDB_TYPE_VOID) { ctl_fatal("cannot specify key to check for non-map column %s", column->name); } die_if_error(ovsdb_atom_from_string(&want_key, &column->type.key, key_string, symtab)); type.key = type.value; type.value.type = OVSDB_TYPE_VOID; die_if_error(ovsdb_datum_from_string(&b, &type, value_string, symtab)); idx = ovsdb_datum_find_key(have_datum, &want_key, column->type.key.type); if (idx == UINT_MAX && !is_set_operator(operator)) { retval = false; } else { struct ovsdb_datum a; if (idx != UINT_MAX) { a.n = 1; a.keys = &have_datum->values[idx]; a.values = NULL; } else { a.n = 0; a.keys = NULL; a.values = NULL; } retval = evaluate_relop(&a, &b, &type, operator); } ovsdb_atom_destroy(&want_key, column->type.key.type); ovsdb_datum_destroy(&b, &type); } else { struct ovsdb_datum want_datum; die_if_error(ovsdb_datum_from_string(&want_datum, &column->type, value_string, symtab)); retval = evaluate_relop(have_datum, &want_datum, &type, operator); ovsdb_datum_destroy(&want_datum, &column->type); } free(key_string); free(value_string); return retval; } static void invalidate_cache(struct ctl_context *ctx) { if (ctx->invalidate_cache) { (ctx->invalidate_cache)(ctx); } } static void pre_cmd_get(struct ctl_context *ctx) { const char *id = shash_find_data(&ctx->options, "--id"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; /* Using "get" without --id or a column name could possibly make sense. * Maybe, for example, a *ctl command run wants to assert that a row * exists. But it is unlikely that an interactive user would want to do * that, so issue a warning if we're running on a terminal. */ if (!id && ctx->argc <= 3 && isatty(STDOUT_FILENO)) { VLOG_WARN("\"get\" command without row arguments or \"--id\" is " "possibly erroneous"); } table = pre_get_table(ctx, table_name); for (i = 3; i < ctx->argc; i++) { if (!strcasecmp(ctx->argv[i], "_uuid") || !strcasecmp(ctx->argv[i], "-uuid")) { continue; } pre_parse_column_key_value(ctx, ctx->argv[i], table); } } static void cmd_get(struct ctl_context *ctx) { const char *id = shash_find_data(&ctx->options, "--id"); bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const struct ctl_table_class *table; const struct ovsdb_idl_row *row; struct ds *out = &ctx->output; int i; if (id && !must_exist) { ctl_fatal("--if-exists and --id may not be specified together"); } table = get_table(table_name); row = get_row(ctx, table, record_id, must_exist); if (!row) { return; } if (id) { struct ovsdb_symbol *symbol; bool new; symbol = create_symbol(ctx->symtab, id, &new); if (!new) { ctl_fatal("row id \"%s\" specified on \"get\" command was used " "before it was defined", id); } symbol->uuid = row->uuid; /* This symbol refers to a row that already exists, so disable warnings * about it being unreferenced. */ symbol->strong_ref = true; } for (i = 3; i < ctx->argc; i++) { const struct ovsdb_idl_column *column; const struct ovsdb_datum *datum; char *key_string; /* Special case for obtaining the UUID of a row. We can't just do this * through parse_column_key_value() below since it returns a "struct * ovsdb_idl_column" and the UUID column doesn't have one. */ if (!strcasecmp(ctx->argv[i], "_uuid") || !strcasecmp(ctx->argv[i], "-uuid")) { ds_put_format(out, UUID_FMT"\n", UUID_ARGS(&row->uuid)); continue; } die_if_error(parse_column_key_value(ctx->argv[i], table, &column, &key_string, NULL, NULL, 0, NULL)); ovsdb_idl_txn_verify(row, column); datum = ovsdb_idl_read(row, column); if (key_string) { union ovsdb_atom key; unsigned int idx; if (column->type.value.type == OVSDB_TYPE_VOID) { ctl_fatal("cannot specify key to get for non-map column %s", column->name); } die_if_error(ovsdb_atom_from_string(&key, &column->type.key, key_string, ctx->symtab)); idx = ovsdb_datum_find_key(datum, &key, column->type.key.type); if (idx == UINT_MAX) { if (must_exist) { ctl_fatal("no key \"%s\" in %s record \"%s\" column %s", key_string, table->class->name, record_id, column->name); } } else { ovsdb_atom_to_string(&datum->values[idx], column->type.value.type, out); } ovsdb_atom_destroy(&key, column->type.key.type); } else { ovsdb_datum_to_string(datum, &column->type, out); } ds_put_char(out, '\n'); free(key_string); } } static void parse_column_names(const char *column_names, const struct ctl_table_class *table, const struct ovsdb_idl_column ***columnsp, size_t *n_columnsp) { const struct ovsdb_idl_column **columns; size_t n_columns; if (!column_names) { size_t i; n_columns = table->class->n_columns + 1; columns = xmalloc(n_columns * sizeof *columns); columns[0] = NULL; for (i = 0; i < table->class->n_columns; i++) { columns[i + 1] = &table->class->columns[i]; } } else { char *s = xstrdup(column_names); size_t allocated_columns; char *save_ptr = NULL; char *column_name; columns = NULL; allocated_columns = n_columns = 0; for (column_name = strtok_r(s, ", ", &save_ptr); column_name; column_name = strtok_r(NULL, ", ", &save_ptr)) { const struct ovsdb_idl_column *column; if (!strcasecmp(column_name, "_uuid")) { column = NULL; } else { die_if_error(get_column(table, column_name, &column)); } if (n_columns >= allocated_columns) { columns = x2nrealloc(columns, &allocated_columns, sizeof *columns); } columns[n_columns++] = column; } free(s); if (!n_columns) { ctl_fatal("must specify at least one column name"); } } *columnsp = columns; *n_columnsp = n_columns; } static void pre_list_columns(struct ctl_context *ctx, const struct ctl_table_class *table, const char *column_names) { const struct ovsdb_idl_column **columns; size_t n_columns; size_t i; parse_column_names(column_names, table, &columns, &n_columns); for (i = 0; i < n_columns; i++) { if (columns[i]) { ovsdb_idl_add_column(ctx->idl, columns[i]); } } free(columns); } static void pre_cmd_list(struct ctl_context *ctx) { const char *column_names = shash_find_data(&ctx->options, "--columns"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; table = pre_get_table(ctx, table_name); pre_list_columns(ctx, table, column_names); } static struct table * list_make_table(const struct ovsdb_idl_column **columns, size_t n_columns) { struct table *out; size_t i; out = xmalloc(sizeof *out); table_init(out); for (i = 0; i < n_columns; i++) { const struct ovsdb_idl_column *column = columns[i]; const char *column_name = column ? column->name : "_uuid"; table_add_column(out, "%s", column_name); } return out; } static void list_record(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column **columns, size_t n_columns, struct table *out) { size_t i; if (!row) { return; } table_add_row(out); for (i = 0; i < n_columns; i++) { const struct ovsdb_idl_column *column = columns[i]; struct cell *cell = table_add_cell(out); if (!column) { struct ovsdb_datum datum; union ovsdb_atom atom; atom.uuid = row->uuid; datum.keys = &atom; datum.values = NULL; datum.n = 1; cell->json = ovsdb_datum_to_json(&datum, &ovsdb_type_uuid); cell->type = &ovsdb_type_uuid; } else { const struct ovsdb_datum *datum = ovsdb_idl_read(row, column); cell->json = ovsdb_datum_to_json(datum, &column->type); cell->type = &column->type; } } } static void cmd_list(struct ctl_context *ctx) { const char *column_names = shash_find_data(&ctx->options, "--columns"); bool must_exist = !shash_find(&ctx->options, "--if-exists"); const struct ovsdb_idl_column **columns; const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; struct table *out; size_t n_columns; int i; table = get_table(table_name); parse_column_names(column_names, table, &columns, &n_columns); out = ctx->table = list_make_table(columns, n_columns); if (ctx->argc > 2) { for (i = 2; i < ctx->argc; i++) { list_record(get_row(ctx, table, ctx->argv[i], must_exist), columns, n_columns, out); } } else { const struct ovsdb_idl_row *row; for (row = ovsdb_idl_first_row(ctx->idl, table->class); row != NULL; row = ovsdb_idl_next_row(row)) { list_record(row, columns, n_columns, out); } } free(columns); } /* Finds and returns the "struct ctl_table_class *" with 'table_name' by * searching the 'tables'. */ static const struct ctl_table_class * get_table(const char *table_name) { const struct ctl_table_class *table; const struct ctl_table_class *best_match = NULL; unsigned int best_score = 0; for (table = tables; table->class; table++) { unsigned int score = score_partial_match(table->class->name, table_name); if (score > best_score) { best_match = table; best_score = score; } else if (score == best_score) { best_match = NULL; } } if (best_match) { return best_match; } else if (best_score) { ctl_fatal("multiple table names match \"%s\"", table_name); } else { ctl_fatal("unknown table \"%s\"", table_name); } return NULL; } static void pre_cmd_find(struct ctl_context *ctx) { const char *column_names = shash_find_data(&ctx->options, "--columns"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; table = pre_get_table(ctx, table_name); pre_list_columns(ctx, table, column_names); for (i = 2; i < ctx->argc; i++) { pre_parse_column_key_value(ctx, ctx->argv[i], table); } } static void cmd_find(struct ctl_context *ctx) { const char *column_names = shash_find_data(&ctx->options, "--columns"); const struct ovsdb_idl_column **columns; const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; const struct ovsdb_idl_row *row; struct table *out; size_t n_columns; table = get_table(table_name); parse_column_names(column_names, table, &columns, &n_columns); out = ctx->table = list_make_table(columns, n_columns); for (row = ovsdb_idl_first_row(ctx->idl, table->class); row; row = ovsdb_idl_next_row(row)) { int i; for (i = 2; i < ctx->argc; i++) { if (!is_condition_satisfied(table, row, ctx->argv[i], ctx->symtab)) { goto next_row; } } list_record(row, columns, n_columns, out); next_row: ; } free(columns); } /* Sets the column of 'row' in 'table'. */ static void set_column(const struct ctl_table_class *table, const struct ovsdb_idl_row *row, const char *arg, struct ovsdb_symbol_table *symtab) { const struct ovsdb_idl_column *column; char *key_string, *value_string; char *error; error = parse_column_key_value(arg, table, &column, &key_string, NULL, NULL, 0, &value_string); die_if_error(error); if (!value_string) { ctl_fatal("%s: missing value", arg); } check_mutable(row, column); if (key_string) { union ovsdb_atom key, value; struct ovsdb_datum datum; if (column->type.value.type == OVSDB_TYPE_VOID) { ctl_fatal("cannot specify key to set for non-map column %s", column->name); } die_if_error(ovsdb_atom_from_string(&key, &column->type.key, key_string, symtab)); die_if_error(ovsdb_atom_from_string(&value, &column->type.value, value_string, symtab)); ovsdb_datum_init_empty(&datum); ovsdb_datum_add_unsafe(&datum, &key, &value, &column->type); ovsdb_atom_destroy(&key, column->type.key.type); ovsdb_atom_destroy(&value, column->type.value.type); ovsdb_datum_union(&datum, ovsdb_idl_read(row, column), &column->type, false); ovsdb_idl_txn_verify(row, column); ovsdb_idl_txn_write(row, column, &datum); } else { struct ovsdb_datum datum; die_if_error(ovsdb_datum_from_string(&datum, &column->type, value_string, symtab)); ovsdb_idl_txn_write(row, column, &datum); } free(key_string); free(value_string); } static void pre_cmd_set(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; table = pre_get_table(ctx, table_name); for (i = 3; i < ctx->argc; i++) { pre_parse_column_key_value(ctx, ctx->argv[i], table); } } static void cmd_set(struct ctl_context *ctx) { bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const struct ctl_table_class *table; const struct ovsdb_idl_row *row; int i; table = get_table(table_name); row = get_row(ctx, table, record_id, must_exist); if (!row) { return; } for (i = 3; i < ctx->argc; i++) { set_column(table, row, ctx->argv[i], ctx->symtab); } invalidate_cache(ctx); } static void pre_cmd_add(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const char *column_name = ctx->argv[3]; const struct ctl_table_class *table; const struct ovsdb_idl_column *column; table = pre_get_table(ctx, table_name); pre_get_column(ctx, table, column_name, &column); } static void cmd_add(struct ctl_context *ctx) { bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const char *column_name = ctx->argv[3]; const struct ctl_table_class *table; const struct ovsdb_idl_column *column; const struct ovsdb_idl_row *row; const struct ovsdb_type *type; struct ovsdb_datum old; int i; table = get_table(table_name); die_if_error(get_column(table, column_name, &column)); row = get_row(ctx, table, record_id, must_exist); if (!row) { return; } check_mutable(row, column); type = &column->type; ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type); for (i = 4; i < ctx->argc; i++) { struct ovsdb_type add_type; struct ovsdb_datum add; add_type = *type; add_type.n_min = 1; add_type.n_max = UINT_MAX; die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i], ctx->symtab)); ovsdb_datum_union(&old, &add, type, false); ovsdb_datum_destroy(&add, type); } if (old.n > type->n_max) { ctl_fatal("\"add\" operation would put %u %s in column %s of " "table %s but the maximum number is %u", old.n, type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs", column->name, table->class->name, type->n_max); } ovsdb_idl_txn_verify(row, column); ovsdb_idl_txn_write(row, column, &old); invalidate_cache(ctx); } static void pre_cmd_remove(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const char *column_name = ctx->argv[3]; const struct ctl_table_class *table; const struct ovsdb_idl_column *column; table = pre_get_table(ctx, table_name); pre_get_column(ctx, table, column_name, &column); } static void cmd_remove(struct ctl_context *ctx) { bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const char *column_name = ctx->argv[3]; const struct ctl_table_class *table; const struct ovsdb_idl_column *column; const struct ovsdb_idl_row *row; const struct ovsdb_type *type; struct ovsdb_datum old; int i; table = get_table(table_name); die_if_error(get_column(table, column_name, &column)); row = get_row(ctx, table, record_id, must_exist); if (!row) { return; } check_mutable(row, column); type = &column->type; ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type); for (i = 4; i < ctx->argc; i++) { struct ovsdb_type rm_type; struct ovsdb_datum rm; char *error; rm_type = *type; rm_type.n_min = 1; rm_type.n_max = UINT_MAX; error = ovsdb_datum_from_string(&rm, &rm_type, ctx->argv[i], ctx->symtab); if (error) { if (ovsdb_type_is_map(&rm_type)) { rm_type.value.type = OVSDB_TYPE_VOID; free(error); die_if_error(ovsdb_datum_from_string( &rm, &rm_type, ctx->argv[i], ctx->symtab)); } else { ctl_fatal("%s", error); } } ovsdb_datum_subtract(&old, type, &rm, &rm_type); ovsdb_datum_destroy(&rm, &rm_type); } if (old.n < type->n_min) { ctl_fatal("\"remove\" operation would put %u %s in column %s of " "table %s but the minimum number is %u", old.n, type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs", column->name, table->class->name, type->n_min); } ovsdb_idl_txn_verify(row, column); ovsdb_idl_txn_write(row, column, &old); invalidate_cache(ctx); } static void pre_cmd_clear(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; table = pre_get_table(ctx, table_name); for (i = 3; i < ctx->argc; i++) { const struct ovsdb_idl_column *column; pre_get_column(ctx, table, ctx->argv[i], &column); } } static void cmd_clear(struct ctl_context *ctx) { bool must_exist = !shash_find(&ctx->options, "--if-exists"); const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const struct ctl_table_class *table; const struct ovsdb_idl_row *row; int i; table = get_table(table_name); row = get_row(ctx, table, record_id, must_exist); if (!row) { return; } for (i = 3; i < ctx->argc; i++) { const struct ovsdb_idl_column *column; const struct ovsdb_type *type; struct ovsdb_datum datum; die_if_error(get_column(table, ctx->argv[i], &column)); check_mutable(row, column); type = &column->type; if (type->n_min > 0) { ctl_fatal("\"clear\" operation cannot be applied to column %s " "of table %s, which is not allowed to be empty", column->name, table->class->name); } ovsdb_datum_init_empty(&datum); ovsdb_idl_txn_write(row, column, &datum); } invalidate_cache(ctx); } static void pre_create(struct ctl_context *ctx) { const char *id = shash_find_data(&ctx->options, "--id"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; table = get_table(table_name); if (!id && !table->class->is_root) { VLOG_WARN("applying \"create\" command to table %s without --id " "option will have no effect", table->class->name); } } static void cmd_create(struct ctl_context *ctx) { const char *id = shash_find_data(&ctx->options, "--id"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table = get_table(table_name); const struct ovsdb_idl_row *row; const struct uuid *uuid; int i; if (id) { struct ovsdb_symbol *symbol = create_symbol(ctx->symtab, id, NULL); if (table->class->is_root) { /* This table is in the root set, meaning that rows created in it * won't disappear even if they are unreferenced, so disable * warnings about that by pretending that there is a reference. */ symbol->strong_ref = true; } uuid = &symbol->uuid; } else { uuid = NULL; } row = ovsdb_idl_txn_insert(ctx->txn, table->class, uuid); for (i = 2; i < ctx->argc; i++) { set_column(table, row, ctx->argv[i], ctx->symtab); } ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); } /* This function may be used as the 'postprocess' function for commands that * insert new rows into the database. It expects that the command's 'run' * function prints the UUID reported by ovsdb_idl_txn_insert() as the command's * sole output. It replaces that output by the row's permanent UUID assigned * by the database server and appends a new-line. * * Currently we use this only for "create", because the higher-level commands * are supposed to be independent of the actual structure of the vswitch * configuration. */ static void post_create(struct ctl_context *ctx) { const struct uuid *real; struct uuid dummy; if (!uuid_from_string(&dummy, ds_cstr(&ctx->output))) { OVS_NOT_REACHED(); } real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy); if (real) { ds_clear(&ctx->output); ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(real)); } ds_put_char(&ctx->output, '\n'); } static void pre_cmd_destroy(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; pre_get_table(ctx, table_name); } static void cmd_destroy(struct ctl_context *ctx) { bool must_exist = !shash_find(&ctx->options, "--if-exists"); bool delete_all = shash_find(&ctx->options, "--all"); const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; table = get_table(table_name); if (delete_all && ctx->argc > 2) { ctl_fatal("--all and records argument should not be specified together"); } if (delete_all && !must_exist) { ctl_fatal("--all and --if-exists should not be specified together"); } if (delete_all) { const struct ovsdb_idl_row *row; const struct ovsdb_idl_row *next_row; for (row = ovsdb_idl_first_row(ctx->idl, table->class); row;) { next_row = ovsdb_idl_next_row(row); ovsdb_idl_txn_delete(row); row = next_row; } } else { for (i = 2; i < ctx->argc; i++) { const struct ovsdb_idl_row *row; row = get_row(ctx, table, ctx->argv[i], must_exist); if (row) { ovsdb_idl_txn_delete(row); } } } invalidate_cache(ctx); } static void pre_cmd_wait_until(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const struct ctl_table_class *table; int i; table = pre_get_table(ctx, table_name); for (i = 3; i < ctx->argc; i++) { pre_parse_column_key_value(ctx, ctx->argv[i], table); } } static void cmd_wait_until(struct ctl_context *ctx) { const char *table_name = ctx->argv[1]; const char *record_id = ctx->argv[2]; const struct ctl_table_class *table; const struct ovsdb_idl_row *row; int i; table = get_table(table_name); row = get_row(ctx, table, record_id, false); if (!row) { ctx->try_again = true; return; } for (i = 3; i < ctx->argc; i++) { if (!is_condition_satisfied(table, row, ctx->argv[i], ctx->symtab)) { ctx->try_again = true; return; } } } /* Parses one command. */ static void parse_command(int argc, char *argv[], struct shash *local_options, struct ctl_command *command) { const struct ctl_command_syntax *p; struct shash_node *node; int n_arg; int i; shash_init(&command->options); shash_swap(local_options, &command->options); for (i = 0; i < argc; i++) { const char *option = argv[i]; const char *equals; char *key, *value; if (option[0] != '-') { break; } equals = strchr(option, '='); if (equals) { key = xmemdup0(option, equals - option); value = xstrdup(equals + 1); } else { key = xstrdup(option); value = NULL; } if (shash_find(&command->options, key)) { ctl_fatal("'%s' option specified multiple times", argv[i]); } shash_add_nocopy(&command->options, key, value); } if (i == argc) { ctl_fatal("missing command name (use --help for help)"); } p = shash_find_data(&all_commands, argv[i]); if (!p) { ctl_fatal("unknown command '%s'; use --help for help", argv[i]); } SHASH_FOR_EACH (node, &command->options) { const char *s = strstr(p->options, node->name); int end = s ? s[strlen(node->name)] : EOF; if (end != '=' && end != ',' && end != ' ' && end != '\0') { ctl_fatal("'%s' command has no '%s' option", argv[i], node->name); } if ((end == '=') != (node->data != NULL)) { if (end == '=') { ctl_fatal("missing argument to '%s' option on '%s' " "command", node->name, argv[i]); } else { ctl_fatal("'%s' option on '%s' does not accept an " "argument", node->name, argv[i]); } } } n_arg = argc - i - 1; if (n_arg < p->min_args) { ctl_fatal("'%s' command requires at least %d arguments", p->name, p->min_args); } else if (n_arg > p->max_args) { int j; for (j = i + 1; j < argc; j++) { if (argv[j][0] == '-') { ctl_fatal("'%s' command takes at most %d arguments " "(note that options must precede command " "names and follow a \"--\" argument)", p->name, p->max_args); } } ctl_fatal("'%s' command takes at most %d arguments", p->name, p->max_args); } command->syntax = p; command->argc = n_arg + 1; command->argv = &argv[i]; } static void pre_cmd_show(struct ctl_context *ctx) { const struct cmd_show_table *show; for (show = cmd_show_tables; show->table; show++) { size_t i; ovsdb_idl_add_table(ctx->idl, show->table); if (show->name_column) { ovsdb_idl_add_column(ctx->idl, show->name_column); } for (i = 0; i < ARRAY_SIZE(show->columns); i++) { const struct ovsdb_idl_column *column = show->columns[i]; if (column) { ovsdb_idl_add_column(ctx->idl, column); } } if (show->wref_table.table) { ovsdb_idl_add_table(ctx->idl, show->wref_table.table); } if (show->wref_table.name_column) { ovsdb_idl_add_column(ctx->idl, show->wref_table.name_column); } if (show->wref_table.wref_column) { ovsdb_idl_add_column(ctx->idl, show->wref_table.wref_column); } } } static const struct cmd_show_table * cmd_show_find_table_by_row(const struct ovsdb_idl_row *row) { const struct cmd_show_table *show; for (show = cmd_show_tables; show->table; show++) { if (show->table == row->table->class) { return show; } } return NULL; } static const struct cmd_show_table * cmd_show_find_table_by_name(const char *name) { const struct cmd_show_table *show; for (show = cmd_show_tables; show->table; show++) { if (!strcmp(show->table->name, name)) { return show; } } return NULL; } /* Prints table entries that weak reference the 'cur_row'. */ static void cmd_show_weak_ref(struct ctl_context *ctx, const struct cmd_show_table *show, const struct ovsdb_idl_row *cur_row, int level) { const struct ovsdb_idl_row *row_wref; const struct ovsdb_idl_table_class *table = show->wref_table.table; const struct ovsdb_idl_column *name_column = show->wref_table.name_column; const struct ovsdb_idl_column *wref_column = show->wref_table.wref_column; if (!table || !name_column || !wref_column) { return; } for (row_wref = ovsdb_idl_first_row(ctx->idl, table); row_wref; row_wref = ovsdb_idl_next_row(row_wref)) { const struct ovsdb_datum *wref_datum = ovsdb_idl_read(row_wref, wref_column); /* If weak reference refers to the 'cur_row', prints it. */ if (wref_datum->n && uuid_equals(&cur_row->uuid, &wref_datum->keys[0].uuid)) { const struct ovsdb_datum *name_datum = ovsdb_idl_read(row_wref, name_column); ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4); ds_put_format(&ctx->output, "%s ", table->name); ovsdb_datum_to_string(name_datum, &name_column->type, &ctx->output); ds_put_char(&ctx->output, '\n'); } } } /* 'shown' records the tables that has been displayed by the current * command to avoid duplicated prints. */ static void cmd_show_row(struct ctl_context *ctx, const struct ovsdb_idl_row *row, int level, struct sset *shown) { const struct cmd_show_table *show = cmd_show_find_table_by_row(row); size_t i; ds_put_char_multiple(&ctx->output, ' ', level * 4); if (show && show->name_column) { const struct ovsdb_datum *datum; ds_put_format(&ctx->output, "%s ", show->table->name); datum = ovsdb_idl_read(row, show->name_column); ovsdb_datum_to_string(datum, &show->name_column->type, &ctx->output); } else { ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid)); } ds_put_char(&ctx->output, '\n'); if (!show || sset_find(shown, show->table->name)) { return; } sset_add(shown, show->table->name); for (i = 0; i < ARRAY_SIZE(show->columns); i++) { const struct ovsdb_idl_column *column = show->columns[i]; const struct ovsdb_datum *datum; if (!column) { break; } datum = ovsdb_idl_read(row, column); if (column->type.key.type == OVSDB_TYPE_UUID && column->type.key.u.uuid.refTableName) { const struct cmd_show_table *ref_show; size_t j; ref_show = cmd_show_find_table_by_name( column->type.key.u.uuid.refTableName); if (ref_show) { for (j = 0; j < datum->n; j++) { const struct ovsdb_idl_row *ref_row; ref_row = ovsdb_idl_get_row_for_uuid(ctx->idl, ref_show->table, &datum->keys[j].uuid); if (ref_row) { cmd_show_row(ctx, ref_row, level + 1, shown); } } continue; } } else if (ovsdb_type_is_map(&column->type) && column->type.value.type == OVSDB_TYPE_UUID && column->type.value.u.uuid.refTableName) { const struct cmd_show_table *ref_show; size_t j; /* Prints the key to ref'ed table name map if the ref'ed table * is also defined in 'cmd_show_tables'. */ ref_show = cmd_show_find_table_by_name( column->type.value.u.uuid.refTableName); if (ref_show && ref_show->name_column) { ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4); ds_put_format(&ctx->output, "%s:\n", column->name); for (j = 0; j < datum->n; j++) { const struct ovsdb_idl_row *ref_row; ref_row = ovsdb_idl_get_row_for_uuid(ctx->idl, ref_show->table, &datum->values[j].uuid); ds_put_char_multiple(&ctx->output, ' ', (level + 2) * 4); ovsdb_atom_to_string(&datum->keys[j], column->type.key.type, &ctx->output); ds_put_char(&ctx->output, '='); if (ref_row) { const struct ovsdb_datum *ref_datum; ref_datum = ovsdb_idl_read(ref_row, ref_show->name_column); ovsdb_datum_to_string(ref_datum, &ref_show->name_column->type, &ctx->output); } else { ds_put_cstr(&ctx->output, "\"\""); } ds_put_char(&ctx->output, '\n'); } continue; } } if (!ovsdb_datum_is_default(datum, &column->type)) { ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4); ds_put_format(&ctx->output, "%s: ", column->name); ovsdb_datum_to_string(datum, &column->type, &ctx->output); ds_put_char(&ctx->output, '\n'); } } cmd_show_weak_ref(ctx, show, row, level); sset_find_and_delete_assert(shown, show->table->name); } static void cmd_show(struct ctl_context *ctx) { const struct ovsdb_idl_row *row; struct sset shown = SSET_INITIALIZER(&shown); for (row = ovsdb_idl_first_row(ctx->idl, cmd_show_tables[0].table); row; row = ovsdb_idl_next_row(row)) { cmd_show_row(ctx, row, 0, &shown); } ovs_assert(sset_is_empty(&shown)); sset_destroy(&shown); } /* Given pointer to dynamic array 'options_p', array's current size * 'allocated_options_p' and number of added options 'n_options_p', * adds all command options to the array. Enlarges the array if * necessary. */ void ctl_add_cmd_options(struct option **options_p, size_t *n_options_p, size_t *allocated_options_p, int opt_val) { struct option *o; const struct shash_node *node; size_t n_existing_options = *n_options_p; SHASH_FOR_EACH (node, &all_commands) { const struct ctl_command_syntax *p = node->data; if (p->options[0]) { char *save_ptr = NULL; char *name; char *s; s = xstrdup(p->options); for (name = strtok_r(s, ",", &save_ptr); name != NULL; name = strtok_r(NULL, ",", &save_ptr)) { char *equals; int has_arg; ovs_assert(name[0] == '-' && name[1] == '-' && name[2]); name += 2; equals = strchr(name, '='); if (equals) { has_arg = required_argument; *equals = '\0'; } else { has_arg = no_argument; } o = find_option(name, *options_p, *n_options_p); if (o) { ovs_assert(o - *options_p >= n_existing_options); ovs_assert(o->has_arg == has_arg); } else { o = add_option(options_p, n_options_p, allocated_options_p); o->name = xstrdup(name); o->has_arg = has_arg; o->flag = NULL; o->val = opt_val; } } free(s); } } o = add_option(options_p, n_options_p, allocated_options_p); memset(o, 0, sizeof *o); } /* Parses command-line input for commands. */ struct ctl_command * ctl_parse_commands(int argc, char *argv[], struct shash *local_options, size_t *n_commandsp) { struct ctl_command *commands; size_t n_commands, allocated_commands; int i, start; commands = NULL; n_commands = allocated_commands = 0; for (start = i = 0; i <= argc; i++) { if (i == argc || !strcmp(argv[i], "--")) { if (i > start) { if (n_commands >= allocated_commands) { struct ctl_command *c; commands = x2nrealloc(commands, &allocated_commands, sizeof *commands); for (c = commands; c < &commands[n_commands]; c++) { shash_moved(&c->options); } } parse_command(i - start, &argv[start], local_options, &commands[n_commands++]); } else if (!shash_is_empty(local_options)) { ctl_fatal("missing command name (use --help for help)"); } start = i + 1; } } if (!n_commands) { ctl_fatal("missing command name (use --help for help)"); } *n_commandsp = n_commands; return commands; } /* Prints all registered commands. */ void ctl_print_commands(void) { const struct shash_node *node; SHASH_FOR_EACH (node, &all_commands) { const struct ctl_command_syntax *p = node->data; char *options = xstrdup(p->options); char *options_begin = options; char *item; for (item = strsep(&options, ","); item != NULL; item = strsep(&options, ",")) { if (item[0] != '\0') { printf("[%s] ", item); } } printf(",%s,", p->name); print_command_arguments(p); printf("\n"); free(options_begin); } exit(EXIT_SUCCESS); } /* Given array of options 'options', prints them. */ void ctl_print_options(const struct option *options) { for (; options->name; options++) { const struct option *o = options; printf("--%s%s\n", o->name, o->has_arg ? "=ARG" : ""); if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) { printf("-%c%s\n", o->val, o->has_arg ? " ARG" : ""); } } exit(EXIT_SUCCESS); } /* Returns the default local database path. */ char * ctl_default_db(void) { static char *def; if (!def) { def = xasprintf("unix:%s/db.sock", ovs_rundir()); } return def; } /* Returns true if it looks like this set of arguments might modify the * database, otherwise false. (Not very smart, so it's prone to false * positives.) */ bool ctl_might_write_to_db(char **argv) { for (; *argv; argv++) { const struct ctl_command_syntax *p = shash_find_data(&all_commands, *argv); if (p && p->mode == RW) { return true; } } return false; } void ctl_fatal(const char *format, ...) { char *message; va_list args; va_start(args, format); message = xvasprintf(format, args); va_end(args); vlog_set_levels(&VLM_db_ctl_base, VLF_CONSOLE, VLL_OFF); VLOG_ERR("%s", message); ovs_error(0, "%s", message); ctl_exit(EXIT_FAILURE); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void ctl_exit(int status) { if (ctl_exit_func) { ctl_exit_func(status); } exit(status); } /* Comman database commands to be registered. */ static const struct ctl_command_syntax db_ctl_commands[] = { {"comment", 0, INT_MAX, "[ARG]...", NULL, NULL, NULL, "", RO}, {"get", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]]...",pre_cmd_get, cmd_get, NULL, "--if-exists,--id=", RO}, {"list", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_list, cmd_list, NULL, "--if-exists,--columns=", RO}, {"find", 1, INT_MAX, "TABLE [COLUMN[:KEY]=VALUE]...", pre_cmd_find, cmd_find, NULL, "--columns=", RO}, {"set", 3, INT_MAX, "TABLE RECORD COLUMN[:KEY]=VALUE...", pre_cmd_set, cmd_set, NULL, "--if-exists", RW}, {"add", 4, INT_MAX, "TABLE RECORD COLUMN [KEY=]VALUE...", pre_cmd_add, cmd_add, NULL, "--if-exists", RW}, {"remove", 4, INT_MAX, "TABLE RECORD COLUMN KEY|VALUE|KEY=VALUE...", pre_cmd_remove, cmd_remove, NULL, "--if-exists", RW}, {"clear", 3, INT_MAX, "TABLE RECORD COLUMN...", pre_cmd_clear, cmd_clear, NULL, "--if-exists", RW}, {"create", 2, INT_MAX, "TABLE COLUMN[:KEY]=VALUE...", pre_create, cmd_create, post_create, "--id=", RW}, {"destroy", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_destroy, cmd_destroy, NULL, "--if-exists,--all", RW}, {"wait-until", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]=VALUE]...", pre_cmd_wait_until, cmd_wait_until, NULL, "", RO}, {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; static void ctl_register_command(const struct ctl_command_syntax *command) { shash_add_assert(&all_commands, command->name, command); } /* Registers commands represented by 'struct ctl_command_syntax's to * 'all_commands'. The last element of 'commands' must be an all-NULL * element. */ void ctl_register_commands(const struct ctl_command_syntax *commands) { const struct ctl_command_syntax *p; for (p = commands; p->name; p++) { ctl_register_command(p); } } /* Registers the 'db_ctl_commands' to 'all_commands'. */ void ctl_init(const struct ctl_table_class tables_[], const struct cmd_show_table cmd_show_tables_[], void (*ctl_exit_func_)(int status)) { tables = tables_; ctl_exit_func = ctl_exit_func_; ctl_register_commands(db_ctl_commands); cmd_show_tables = cmd_show_tables_; if (cmd_show_tables) { static const struct ctl_command_syntax show = {"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO}; ctl_register_command(&show); } } /* Returns the text for the database commands usage. */ const char * ctl_get_db_cmd_usage(void) { return "Database commands:\n\ list TBL [REC] list RECord (or all records) in TBL\n\ find TBL CONDITION... list records satisfying CONDITION in TBL\n\ get TBL REC COL[:KEY] print values of COLumns in RECord in TBL\n\ set TBL REC COL[:KEY]=VALUE set COLumn values in RECord in TBL\n\ add TBL REC COL [KEY=]VALUE add (KEY=)VALUE to COLumn in RECord in TBL\n\ remove TBL REC COL [KEY=]VALUE remove (KEY=)VALUE from COLumn\n\ clear TBL REC COL clear values from COLumn in RECord in TBL\n\ create TBL COL[:KEY]=VALUE create and initialize new record\n\ destroy TBL REC delete RECord from TBL\n\ wait-until TBL REC [COL[:KEY]=VALUE] wait until condition is true\n\ Potentially unsafe database commands require --force option.\n"; } /* Initializes 'ctx' from 'command'. */ void ctl_context_init_command(struct ctl_context *ctx, struct ctl_command *command) { ctx->argc = command->argc; ctx->argv = command->argv; ctx->options = command->options; ds_swap(&ctx->output, &command->output); ctx->table = command->table; ctx->try_again = false; } /* Initializes the entire 'ctx'. */ void ctl_context_init(struct ctl_context *ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, struct ovsdb_symbol_table *symtab, void (*invalidate_cache)(struct ctl_context *)) { if (command) { ctl_context_init_command(ctx, command); } ctx->idl = idl; ctx->txn = txn; ctx->symtab = symtab; ctx->invalidate_cache = invalidate_cache; } /* Completes processing of 'command' within 'ctx'. */ void ctl_context_done_command(struct ctl_context *ctx, struct ctl_command *command) { ds_swap(&ctx->output, &command->output); command->table = ctx->table; } /* Finishes up with 'ctx'. * * If command is nonnull, first calls ctl_context_done_command() to complete * processing that command within 'ctx'. */ void ctl_context_done(struct ctl_context *ctx, struct ctl_command *command) { if (command) { ctl_context_done_command(ctx, command); } invalidate_cache(ctx); } void ctl_set_column(const char *table_name, const struct ovsdb_idl_row *row, const char *arg, struct ovsdb_symbol_table *symtab) { set_column(get_table(table_name), row, arg, symtab); } openvswitch-2.5.9/lib/PaxHeaders.82075/smap.c0000644000000000000000000000013113534540071015515 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.881854163 openvswitch-2.5.9/lib/smap.c0000644000175000017500000002370113534540071017207 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2012, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "smap.h" #include #include "hash.h" #include "json.h" #include "packets.h" #include "uuid.h" static struct smap_node *smap_add__(struct smap *, char *, void *, size_t hash); static struct smap_node *smap_find__(const struct smap *, const char *key, size_t key_len, size_t hash); static int compare_nodes_by_key(const void *, const void *); /* Public Functions. */ void smap_init(struct smap *smap) { hmap_init(&smap->map); } void smap_destroy(struct smap *smap) { if (smap) { smap_clear(smap); hmap_destroy(&smap->map); } } /* Adds 'key' paired with 'value' to 'smap'. It is the caller's responsibility * to avoid duplicate keys if desirable. */ struct smap_node * smap_add(struct smap *smap, const char *key, const char *value) { size_t key_len = strlen(key); return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value), hash_bytes(key, key_len, 0)); } /* Adds 'key' paired with 'value' to 'smap'. Takes ownership of 'key' and * 'value' (which will eventually be freed with free()). It is the caller's * responsibility to avoid duplicate keys if desirable. */ struct smap_node * smap_add_nocopy(struct smap *smap, char *key, char *value) { return smap_add__(smap, key, value, hash_bytes(key, strlen(key), 0)); } /* Attempts to add 'key' to 'smap' associated with 'value'. If 'key' already * exists in 'smap', does nothing and returns false. Otherwise, performs the * addition and returns true. */ bool smap_add_once(struct smap *smap, const char *key, const char *value) { if (!smap_get(smap, key)) { smap_add(smap, key, value); return true; } else { return false; } } /* Adds 'key' paired with a value derived from 'format' (similar to printf). * It is the caller's responsibility to avoid duplicate keys if desirable. */ void smap_add_format(struct smap *smap, const char *key, const char *format, ...) { size_t key_len; va_list args; char *value; va_start(args, format); value = xvasprintf(format, args); va_end(args); key_len = strlen(key); smap_add__(smap, xmemdup0(key, key_len), value, hash_bytes(key, key_len, 0)); } /* Adds 'key' paired with a string representation of 'addr'. It is the * caller's responsibility to avoid duplicate keys if desirable. */ void smap_add_ipv6(struct smap *smap, const char *key, struct in6_addr *addr) { char buf[INET6_ADDRSTRLEN]; ipv6_string_mapped(buf, addr); smap_add(smap, key, buf); } /* Searches for 'key' in 'smap'. If it does not already exists, adds it. * Otherwise, changes its value to 'value'. */ void smap_replace(struct smap *smap, const char *key, const char *value) { size_t key_len = strlen(key); size_t hash = hash_bytes(key, key_len, 0); struct smap_node *node; node = smap_find__(smap, key, key_len, hash); if (node) { free(node->value); node->value = xstrdup(value); } else { smap_add__(smap, xmemdup0(key, key_len), xstrdup(value), hash); } } /* If 'key' is in 'smap', removes it. Otherwise does nothing. */ void smap_remove(struct smap *smap, const char *key) { struct smap_node *node = smap_get_node(smap, key); if (node) { smap_remove_node(smap, node); } } /* Removes 'node' from 'smap'. */ void smap_remove_node(struct smap *smap, struct smap_node *node) { hmap_remove(&smap->map, &node->node); free(node->key); free(node->value); free(node); } /* Deletes 'node' from 'smap'. * * If 'keyp' is nonnull, stores the node's key in '*keyp' and transfers * ownership to the caller. Otherwise, frees the node's key. Similarly for * 'valuep' and the node's value. */ void smap_steal(struct smap *smap, struct smap_node *node, char **keyp, char **valuep) { if (keyp) { *keyp = node->key; } else { free(node->key); } if (valuep) { *valuep = node->value; } else { free(node->value); } hmap_remove(&smap->map, &node->node); free(node); } /* Removes all key-value pairs from 'smap'. */ void smap_clear(struct smap *smap) { struct smap_node *node, *next; SMAP_FOR_EACH_SAFE (node, next, smap) { smap_remove_node(smap, node); } } /* Returns the value associated with 'key' in 'smap', or NULL. */ const char * smap_get(const struct smap *smap, const char *key) { struct smap_node *node = smap_get_node(smap, key); return node ? node->value : NULL; } /* Returns the node associated with 'key' in 'smap', or NULL. */ struct smap_node * smap_get_node(const struct smap *smap, const char *key) { size_t key_len = strlen(key); return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0)); } /* Gets the value associated with 'key' in 'smap' and converts it to a boolean. * If 'key' is not in 'smap', or its value is neither "true" nor "false", * returns 'def'. */ bool smap_get_bool(const struct smap *smap, const char *key, bool def) { const char *value = smap_get(smap, key); if (!value) { return def; } if (def) { return strcasecmp("false", value) != 0; } else { return !strcasecmp("true", value); } } /* Gets the value associated with 'key' in 'smap' and converts it to an int * using atoi(). If 'key' is not in 'smap', returns 'def'. */ int smap_get_int(const struct smap *smap, const char *key, int def) { const char *value = smap_get(smap, key); return value ? atoi(value) : def; } /* Gets the value associated with 'key' in 'smap' and converts it to a UUID * using uuid_from_string(). Returns true if successful, false if 'key' is not * in 'smap' or if 'key' does not have the correct syntax for a UUID. */ bool smap_get_uuid(const struct smap *smap, const char *key, struct uuid *uuid) { const char *value = smap_get(smap, key); return value && uuid_from_string(uuid, value); } /* Returns true of there are no elements in 'smap'. */ bool smap_is_empty(const struct smap *smap) { return hmap_is_empty(&smap->map); } /* Returns the number of elements in 'smap'. */ size_t smap_count(const struct smap *smap) { return hmap_count(&smap->map); } /* Initializes 'dst' as a clone of 'src. */ void smap_clone(struct smap *dst, const struct smap *src) { const struct smap_node *node; smap_init(dst); SMAP_FOR_EACH (node, src) { smap_add__(dst, xstrdup(node->key), xstrdup(node->value), node->node.hash); } } /* Returns an array of nodes sorted on key or NULL if 'smap' is empty. The * caller is responsible for freeing this array. */ const struct smap_node ** smap_sort(const struct smap *smap) { if (smap_is_empty(smap)) { return NULL; } else { const struct smap_node **nodes; struct smap_node *node; size_t i, n; n = smap_count(smap); nodes = xmalloc(n * sizeof *nodes); i = 0; SMAP_FOR_EACH (node, smap) { nodes[i++] = node; } ovs_assert(i == n); qsort(nodes, n, sizeof *nodes, compare_nodes_by_key); return nodes; } } /* Adds each of the key-value pairs from 'json' (which must be a JSON object * whose values are strings) to 'smap'. * * The caller must have initialized 'smap'. * * The caller retains ownership of 'json' and everything in it. */ void smap_from_json(struct smap *smap, const struct json *json) { const struct shash_node *node; SHASH_FOR_EACH (node, json_object(json)) { const struct json *value = node->data; smap_add(smap, node->name, json_string(value)); } } /* Returns a JSON object that maps from the keys in 'smap' to their values. * * The caller owns the returned value and must eventually json_destroy() it. * * The caller retains ownership of 'smap' and everything in it. */ struct json * smap_to_json(const struct smap *smap) { const struct smap_node *node; struct json *json; json = json_object_create(); SMAP_FOR_EACH (node, smap) { json_object_put_string(json, node->key, node->value); } return json; } /* Returns true if the two maps are equal, meaning that they have the same set * of key-value pairs. */ bool smap_equal(const struct smap *smap1, const struct smap *smap2) { if (smap_count(smap1) != smap_count(smap2)) { return false; } const struct smap_node *node; SMAP_FOR_EACH (node, smap1) { const char *value2 = smap_get(smap2, node->key); if (!value2 || strcmp(node->value, value2)) { return false; } } return true; } /* Private Helpers. */ static struct smap_node * smap_add__(struct smap *smap, char *key, void *value, size_t hash) { struct smap_node *node = xmalloc(sizeof *node); node->key = key; node->value = value; hmap_insert(&smap->map, &node->node, hash); return node; } static struct smap_node * smap_find__(const struct smap *smap, const char *key, size_t key_len, size_t hash) { struct smap_node *node; HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) { if (!strncmp(node->key, key, key_len) && !node->key[key_len]) { return node; } } return NULL; } static int compare_nodes_by_key(const void *a_, const void *b_) { const struct smap_node *const *a = a_; const struct smap_node *const *b = b_; return strcmp((*a)->key, (*b)->key); } openvswitch-2.5.9/lib/PaxHeaders.82075/meta-flow.h0000644000000000000000000000013213534540071016456 xustar0030 mtime=1567801401.421681316 30 atime=1567801402.077686135 30 ctime=1567801424.769853338 openvswitch-2.5.9/lib/meta-flow.h0000644000175000017500000020242513534540071020151 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef META_FLOW_H #define META_FLOW_H 1 #include #include #include #include "bitmap.h" #include "flow.h" #include "ofp-errors.h" #include "packets.h" #include "util.h" struct ds; struct match; /* Open vSwitch fields * =================== * * A "field" is a property of a packet. Most familiarly, "data fields" are * fields that can be extracted from a packet. * * Some data fields are always present as a consequence of the basic networking * technology in use. Ethernet is the assumed base technology for current * versions of OpenFlow and Open vSwitch, so Ethernet header fields are always * available. * * Other data fields are not always present. A packet contains ARP fields, for * example, only when its Ethernet header indicates the Ethertype for ARP, * 0x0806. We say that a field is "applicable" when it is it present in a * packet, and "inapplicable" when it is not, and refer to the conditions that * determine whether a field is applicable as "prerequisites". Some * VLAN-related fields are a special case: these fields are always applicable, * but have a designated value or bit that indicates whether a VLAN header is * present, with the remaining values or bits indicating the VLAN header's * content (if it is present). See MFF_VLAN_TCI for an example. * * Conceptually, an inapplicable field does not have a value, not even a * nominal ``value'' such as all-zero-bits. In many circumstances, OpenFlow * and Open vSwitch allow references only to applicable fields. For example, * one may match a given field only if the match includes the field's * prerequisite, e.g. matching an ARP field is only allowed if one also matches * on Ethertype 0x0806. * * (Practically, however, OVS represents a field's value as some fixed member * in its "struct flow", so accessing that member will obtain some value. Some * members are used for more than one purpose, e.g. the "tp_src" member * represents the TCP, UDP, and SCTP source port, so the value read may not * even make sense. For this reason, it is important to know whether a field's * prerequisites are satisfied before attempting to read it.) * * Sometimes a packet may contain multiple instances of a header. For example, * a packet may contain multiple VLAN or MPLS headers, and tunnels can cause * any data field to recur. OpenFlow and Open vSwitch do not address these * cases uniformly. For VLAN and MPLS headers, only the outermost header is * accessible, so that inner headers may be accessed only by ``popping'' * (removing) the outer header. (Open vSwitch supports only a single VLAN * header in any case.) For tunnels, e.g. GRE or VXLAN, the outer header and * inner headers are treated as different data fields. * * OpenFlow and Open vSwitch support some fields other than data fields. * "Metadata fields" relate to the origin or treatment of a packet, but they * are not extracted from the packet data itself. One example is the physical * port on which a packet arrived at the switch. "Register fields" act like * variables: they give an OpenFlow switch space for temporary storage while * processing a packet. Existing metadata and register fields have no * prerequisites. * * A field's value consists of an integral number of bytes. Most data fields * are copied directly from protocol headers, e.g. at layer 2, MFF_ETH_SRC is * copied from the Ethernet source address and MFF_ETH_DST from the destination * address. Other data fields are copied from a packet with padding, usually * with zeros and in the most significant positions (see e.g. MFF_MPLS_LABEL) * but not always (see e.g. MFF_IP_DSCP). A final category of data fields is * transformed in other ways as they are copied from the packets, to make them * more useful for matching, e.g. MFF_IP_FRAG describes whether a packet is a * fragment but it is not copied directly from the IP header. * * * Field specifications * ==================== * * Each of the enumeration values below represents a field. The comments * preceding each enum must be in a stylized form that is parsed at compile * time by the extract-ofp-fields program. The comment itself consists of a * series of paragraphs separate by blank lines. The paragraphs consist of: * * - The first paragraph gives the user-visible name of the field as a * quoted string. This is the name used for parsing and formatting the * field. * * For historical reasons, some fields have an additional name that is * accepted as an alternative in parsing. This name, when there is one, * is given as a quoted string in parentheses along with "aka". For * example: * * "tun_id" (aka "tunnel_id"). * * New fields should have only one name. * * - Any number of paragraphs of free text that describe the field. This * is meant for human readers, so extract-ofp-fields ignores it. * * - A final paragraph that consists of a series of key-value pairs, one * per line, in the form "key: value." where the period at the end of the * line is a mandatory part of the syntax. * * Every field must specify the following key-value pairs: * * Type: * * The format and size of the field's value. Some possible values are * generic: * * u8: A one-byte field. * be16: A two-byte field. * be32: A four-byte field. * be64: An eight-byte field. * * The remaining values imply more about the value's semantics, though OVS * does not currently take advantage of this additional information: * * MAC: A six-byte field whose value is an Ethernet address. * IPv6: A 16-byte field whose value is an IPv6 address. * tunnelMD: A variable length field, up to 124 bytes, that carries * tunnel metadata. * * Maskable: * * Either "bitwise", if OVS supports matching any subset of bits in the * field, or "no", if OVS only supports matching or wildcarding the entire * field. * * Formatting: * * Explains how a field's value is formatted and parsed for human * consumption. Some of the options are fairly generally useful: * * decimal: Formats the value as a decimal number. On parsing, accepts * decimal (with no prefix), hexadecimal with 0x prefix, or octal * with 0 prefix. * * hexadecimal: Same as decimal except nonzero values are formatted in * hex with 0x prefix. The default for parsing is *not* hexadecimal: * only with a 0x prefix is the input in hexadecimal. * * Ethernet: Formats and accepts the common format xx:xx:xx:xx:xx:xx. * 6-byte fields only. * * IPv4: Formats and accepts the common format w.x.y.z. 4-byte fields * only. * * IPv6: Formats and accepts the common IPv6 formats. 16-byte fields * only. * * OpenFlow 1.0 port: Accepts an OpenFlow well-known port name * (e.g. "IN_PORT") in uppercase or lowercase, or a 16-bit port * number in decimal. Formats ports using their well-known names in * uppercase, or in decimal otherwise. 2-byte fields only. * * OpenFlow 1.1+ port: Same syntax as for OpenFlow 1.0 ports but for * 4-byte OpenFlow 1.1+ port number fields. * * Others are very specific to particular fields: * * frag: One of the strings "no", "first", "later", "yes", "not_later" * describing which IPv4/v6 fragments are matched. * * tunnel flags: Any number of the strings "df", "csum", "key", or * "oam" separated by "|". * * TCP flags: See the description of tcp_flags in ovs-ofctl(8). * * Prerequisites: * * The field's prerequisites. The values should be straightfoward. * * Access: * * Either "read-only", for a field that cannot be changed via OpenFlow, or * "read/write" for a modifiable field. * * NXM: * * If the field has an NXM field assignment, then this specifies the NXM * name of the field (e.g. "NXM_OF_ETH_SRC"), followed by its nxm_type in * parentheses, followed by "since v." specifying the version of Open * vSwitch that first supported this field in NXM (e.g. "since v1.1" if it * was introduced in Open vSwitch 1.1). * * The NXM name must begin with NXM_OF_ or NXM_NX_. This allows OVS to * determine the correct NXM class. * * If the field does not have an NXM field assignment, specify "none". * * OXM: * * If the field has an OXM field assignment, then this specifies the OXM * name of the field (e.g. "OXM_OF_ETH_SRC"), followed by its nxm_type in * parentheses, followed by "since OF. v." specifying the * versions of OpenFlow and Open vSwitch that first supported this field in * OXM (e.g. "since OF1.3 and v1.10" if it was introduced in OpenFlow 1.3 * and first supported by Open vSwitch in version 1.10). * * Some fields have more than one OXM field assignment. For example, * actset_output has an experimenter OXM assignment in OpenFlow 1.3 and a * standard OXM assignment in OpenFlow 1.5. In such a case, specify both, * separated by commas. * * OVS uses the start of the OXM field name to determine the correct OXM * class. To support a new OXM class, edit the mapping table in * build-aux/extract-ofp-fields. * * If the field does not have an OXM field assignment, specify "none". * * The following key-value pairs are optional. Open vSwitch already supports * all the fields to which they apply, so new fields should probably not * include these pairs: * * OF1.0: * * Specify this as "exact match" if OpenFlow 1.0 can match or wildcard the * entire field, or as "CIDR mask" if OpenFlow 1.0 can match any CIDR * prefix of the field. (OpenFlow 1.0 did not support bitwise matching.) * Omit, if OpenFlow 1.0 did not support this field. * * OF1.1: * * Specify this as "exact match" if OpenFlow 1.1 can match or wildcard the * entire field, or as "bitwise" if OpenFlow 1.1 can match any subset of * bits in the field. Omit, if OpenFlow 1.1 did not support this field. * * The following key-value pair is optional: * * Prefix lookup member: * * If this field makes sense for use with classifier_set_prefix_fields(), * specify the name of the "struct flow" member that corresponds to the * field. * * Finally, a few "register" fields have very similar names and purposes, * e.g. MFF_REG0 through MFF_REG7. For these, the comments may be merged * together using as a metasyntactic variable for the numeric suffix. * Lines in the comment that are specific to one of the particular fields by * writing, e.g. <1>, to consider that line only for e.g. MFF_REG1. */ enum OVS_PACKED_ENUM mf_field_id { /* ## -------- ## */ /* ## Metadata ## */ /* ## -------- ## */ /* "dp_hash". * * Flow hash computed in the datapath. Internal use only, not programmable * from controller. * * The OXM code point for this is an attempt to test OXM experimenter * support, which is otherwise difficult to test due to the dearth of use * out in the wild. Because controllers can't add flows that match on * dp_hash, this doesn't commit OVS to supporting this OXM experimenter * code point in the future. * * Type: be32. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read-only. * NXM: NXM_NX_DP_HASH(35) since v2.2. * OXM: NXOXM_ET_DP_HASH(0) since OF1.5 and v2.4. */ MFF_DP_HASH, /* "recirc_id". * * ID for recirculation. The value 0 is reserved for initially received * packets. Internal use only, not programmable from controller. * * Type: be32. * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read-only. * NXM: NXM_NX_RECIRC_ID(36) since v2.2. * OXM: none. */ MFF_RECIRC_ID, /* "conj_id". * * ID for "conjunction" actions. Please refer to ovs-ofctl(8) * documentation of "conjunction" for details. * * Type: be32. * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read-only. * NXM: NXM_NX_CONJ_ID(37) since v2.4. * OXM: none. */ MFF_CONJ_ID, /* "tun_id" (aka "tunnel_id"). * * The "key" or "tunnel ID" or "VNI" in a packet received via a keyed * tunnel. For protocols in which the key is shorter than 64 bits, the key * is stored in the low bits and the high bits are zeroed. For non-keyed * tunnels and packets not received via a tunnel, the value is 0. * * Type: be64. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_ID(16) since v1.1. * OXM: OXM_OF_TUNNEL_ID(38) since OF1.3 and v1.10. * Prefix lookup member: tunnel.tun_id. */ MFF_TUN_ID, /* "tun_src". * * The IPv4 source address in the outer IP header of a tunneled packet. * * For non-tunneled packets, the value is 0. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_IPV4_SRC(31) since v2.0. * OXM: none. * Prefix lookup member: tunnel.ip_src. */ MFF_TUN_SRC, /* "tun_dst". * * The IPv4 destination address in the outer IP header of a tunneled * packet. * * For non-tunneled packets, the value is 0. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_IPV4_DST(32) since v2.0. * OXM: none. * Prefix lookup member: tunnel.ip_dst. */ MFF_TUN_DST, /* "tun_ipv6_src". * * The IPv6 source address in the outer IP header of a tunneled packet. * * For non-tunneled packets, the value is 0. * * Type: be128. * Maskable: bitwise. * Formatting: IPv6. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_IPV6_SRC(109) since v2.5. * OXM: none. * Prefix lookup member: tunnel.ipv6_src. */ MFF_TUN_IPV6_SRC, /* "tun_ipv6_dst". * * The IPv6 destination address in the outer IP header of a tunneled * packet. * * For non-tunneled packets, the value is 0. * * Type: be128. * Maskable: bitwise. * Formatting: IPv6. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_IPV6_DST(110) since v2.5. * OXM: none. * Prefix lookup member: tunnel.ipv6_dst. */ MFF_TUN_IPV6_DST, /* "tun_flags". * * Flags representing aspects of tunnel behavior. * * This field currently only has a single flag defined: * * - NX_TUN_FLAG_OAM: The tunnel protocol indicated that this is an * OAM control packet. * * The switch may reject matches against values that it is not aware of. * * Note that it is possible for newer version of Open vSwitch to * introduce additional flags with varying meaning. It is therefore not * recommended to use an exact match on this field since the behavior of * these new flags is unknown and should be ignored. * * For non-tunneled packets, the value is 0. * * Type: be16 (low 1 bits). * Maskable: bitwise. * Formatting: tunnel flags. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_FLAGS(104) since v2.5. * OXM: none. */ MFF_TUN_FLAGS, /* "tun_ttl". * * The TTL in the outer IP header of a tunneled packet. Internal use only, * not programmable from controller. * * For non-tunneled packets, the value is 0. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read-only. * NXM: none. * OXM: none. */ MFF_TUN_TTL, /* "tun_tos". * * The ToS value in the outer IP header of a tunneled packet. Internal use * only, not programmable from controller. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read-only. * NXM: none. * OXM: none. */ MFF_TUN_TOS, /* "tun_gbp_id". * * VXLAN Group Policy ID * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_GBP_ID(38) since v2.4. * OXM: none. */ MFF_TUN_GBP_ID, /* "tun_gbp_flags". * * VXLAN Group Policy flags * * Type: u8. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_GBP_FLAGS(39) since v2.4. * OXM: none. */ MFF_TUN_GBP_FLAGS, #if TUN_METADATA_NUM_OPTS == 64 /* "tun_metadata". * * Encapsulation metadata for tunnels. * * Each NXM can be dynamically mapped onto a particular tunnel field using * OpenFlow commands. The individual NXMs can each carry up to 124 bytes * of data and a combined total of 256 across all allocated fields. * * Type: tunnelMD. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_TUN_METADATA0(40) since v2.5. <0> * NXM: NXM_NX_TUN_METADATA1(41) since v2.5. <1> * NXM: NXM_NX_TUN_METADATA2(42) since v2.5. <2> * NXM: NXM_NX_TUN_METADATA3(43) since v2.5. <3> * NXM: NXM_NX_TUN_METADATA4(44) since v2.5. <4> * NXM: NXM_NX_TUN_METADATA5(45) since v2.5. <5> * NXM: NXM_NX_TUN_METADATA6(46) since v2.5. <6> * NXM: NXM_NX_TUN_METADATA7(47) since v2.5. <7> * NXM: NXM_NX_TUN_METADATA8(48) since v2.5. <8> * NXM: NXM_NX_TUN_METADATA9(49) since v2.5. <9> * NXM: NXM_NX_TUN_METADATA10(50) since v2.5. <10> * NXM: NXM_NX_TUN_METADATA11(51) since v2.5. <11> * NXM: NXM_NX_TUN_METADATA12(52) since v2.5. <12> * NXM: NXM_NX_TUN_METADATA13(53) since v2.5. <13> * NXM: NXM_NX_TUN_METADATA14(54) since v2.5. <14> * NXM: NXM_NX_TUN_METADATA15(55) since v2.5. <15> * NXM: NXM_NX_TUN_METADATA16(56) since v2.5. <16> * NXM: NXM_NX_TUN_METADATA17(57) since v2.5. <17> * NXM: NXM_NX_TUN_METADATA18(58) since v2.5. <18> * NXM: NXM_NX_TUN_METADATA19(59) since v2.5. <19> * NXM: NXM_NX_TUN_METADATA20(60) since v2.5. <20> * NXM: NXM_NX_TUN_METADATA21(61) since v2.5. <21> * NXM: NXM_NX_TUN_METADATA22(62) since v2.5. <22> * NXM: NXM_NX_TUN_METADATA23(63) since v2.5. <23> * NXM: NXM_NX_TUN_METADATA24(64) since v2.5. <24> * NXM: NXM_NX_TUN_METADATA25(65) since v2.5. <25> * NXM: NXM_NX_TUN_METADATA26(66) since v2.5. <26> * NXM: NXM_NX_TUN_METADATA27(67) since v2.5. <27> * NXM: NXM_NX_TUN_METADATA28(68) since v2.5. <28> * NXM: NXM_NX_TUN_METADATA29(69) since v2.5. <29> * NXM: NXM_NX_TUN_METADATA30(70) since v2.5. <30> * NXM: NXM_NX_TUN_METADATA31(71) since v2.5. <31> * NXM: NXM_NX_TUN_METADATA32(72) since v2.5. <32> * NXM: NXM_NX_TUN_METADATA33(73) since v2.5. <33> * NXM: NXM_NX_TUN_METADATA34(74) since v2.5. <34> * NXM: NXM_NX_TUN_METADATA35(75) since v2.5. <35> * NXM: NXM_NX_TUN_METADATA36(76) since v2.5. <36> * NXM: NXM_NX_TUN_METADATA37(77) since v2.5. <37> * NXM: NXM_NX_TUN_METADATA38(78) since v2.5. <38> * NXM: NXM_NX_TUN_METADATA39(79) since v2.5. <39> * NXM: NXM_NX_TUN_METADATA40(80) since v2.5. <40> * NXM: NXM_NX_TUN_METADATA41(81) since v2.5. <41> * NXM: NXM_NX_TUN_METADATA42(82) since v2.5. <42> * NXM: NXM_NX_TUN_METADATA43(83) since v2.5. <43> * NXM: NXM_NX_TUN_METADATA44(84) since v2.5. <44> * NXM: NXM_NX_TUN_METADATA45(85) since v2.5. <45> * NXM: NXM_NX_TUN_METADATA46(86) since v2.5. <46> * NXM: NXM_NX_TUN_METADATA47(87) since v2.5. <47> * NXM: NXM_NX_TUN_METADATA48(88) since v2.5. <48> * NXM: NXM_NX_TUN_METADATA49(89) since v2.5. <49> * NXM: NXM_NX_TUN_METADATA50(90) since v2.5. <50> * NXM: NXM_NX_TUN_METADATA51(91) since v2.5. <51> * NXM: NXM_NX_TUN_METADATA52(92) since v2.5. <52> * NXM: NXM_NX_TUN_METADATA53(93) since v2.5. <53> * NXM: NXM_NX_TUN_METADATA54(94) since v2.5. <54> * NXM: NXM_NX_TUN_METADATA55(95) since v2.5. <55> * NXM: NXM_NX_TUN_METADATA56(96) since v2.5. <56> * NXM: NXM_NX_TUN_METADATA57(97) since v2.5. <57> * NXM: NXM_NX_TUN_METADATA58(98) since v2.5. <58> * NXM: NXM_NX_TUN_METADATA59(99) since v2.5. <59> * NXM: NXM_NX_TUN_METADATA60(100) since v2.5. <60> * NXM: NXM_NX_TUN_METADATA61(101) since v2.5. <61> * NXM: NXM_NX_TUN_METADATA62(102) since v2.5. <62> * NXM: NXM_NX_TUN_METADATA63(103) since v2.5. <63> * OXM: none. */ MFF_TUN_METADATA0, MFF_TUN_METADATA1, MFF_TUN_METADATA2, MFF_TUN_METADATA3, MFF_TUN_METADATA4, MFF_TUN_METADATA5, MFF_TUN_METADATA6, MFF_TUN_METADATA7, MFF_TUN_METADATA8, MFF_TUN_METADATA9, MFF_TUN_METADATA10, MFF_TUN_METADATA11, MFF_TUN_METADATA12, MFF_TUN_METADATA13, MFF_TUN_METADATA14, MFF_TUN_METADATA15, MFF_TUN_METADATA16, MFF_TUN_METADATA17, MFF_TUN_METADATA18, MFF_TUN_METADATA19, MFF_TUN_METADATA20, MFF_TUN_METADATA21, MFF_TUN_METADATA22, MFF_TUN_METADATA23, MFF_TUN_METADATA24, MFF_TUN_METADATA25, MFF_TUN_METADATA26, MFF_TUN_METADATA27, MFF_TUN_METADATA28, MFF_TUN_METADATA29, MFF_TUN_METADATA30, MFF_TUN_METADATA31, MFF_TUN_METADATA32, MFF_TUN_METADATA33, MFF_TUN_METADATA34, MFF_TUN_METADATA35, MFF_TUN_METADATA36, MFF_TUN_METADATA37, MFF_TUN_METADATA38, MFF_TUN_METADATA39, MFF_TUN_METADATA40, MFF_TUN_METADATA41, MFF_TUN_METADATA42, MFF_TUN_METADATA43, MFF_TUN_METADATA44, MFF_TUN_METADATA45, MFF_TUN_METADATA46, MFF_TUN_METADATA47, MFF_TUN_METADATA48, MFF_TUN_METADATA49, MFF_TUN_METADATA50, MFF_TUN_METADATA51, MFF_TUN_METADATA52, MFF_TUN_METADATA53, MFF_TUN_METADATA54, MFF_TUN_METADATA55, MFF_TUN_METADATA56, MFF_TUN_METADATA57, MFF_TUN_METADATA58, MFF_TUN_METADATA59, MFF_TUN_METADATA60, MFF_TUN_METADATA61, MFF_TUN_METADATA62, MFF_TUN_METADATA63, #else #error "Need to update MFF_TUN_METADATA* to match TUN_METADATA_NUM_OPTS" #endif /* "metadata". * * A scratch pad value standardized in OpenFlow 1.1+. Initially zero, at * the beginning of the pipeline. * * Type: be64. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: OXM_OF_METADATA(2) since OF1.2 and v1.8. * OF1.1: bitwise mask. */ MFF_METADATA, /* "in_port". * * 16-bit (OpenFlow 1.0) view of the physical or virtual port on which the * packet was received. * * Type: be16. * Maskable: no. * Formatting: OpenFlow 1.0 port. * Prerequisites: none. * Access: read/write. * NXM: NXM_OF_IN_PORT(0) since v1.1. * OXM: none. * OF1.0: exact match. * OF1.1: exact match. */ MFF_IN_PORT, /* "in_port_oxm". * * 32-bit (OpenFlow 1.1+) view of the physical or virtual port on which the * packet was received. * * Type: be32. * Maskable: no. * Formatting: OpenFlow 1.1+ port. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: OXM_OF_IN_PORT(0) since OF1.2 and v1.7. * OF1.1: exact match. */ MFF_IN_PORT_OXM, /* "actset_output". * * Type: be32. * Maskable: no. * Formatting: OpenFlow 1.1+ port. * Prerequisites: none. * Access: read-only. * NXM: none. * OXM: ONFOXM_ET_ACTSET_OUTPUT(43) since OF1.3 and v2.4, * OXM_OF_ACTSET_OUTPUT(43) since OF1.5 and v2.4. */ MFF_ACTSET_OUTPUT, /* "skb_priority". * * Designates the queue to which output will be directed. The value in * this field is not necessarily the OpenFlow queue number; with the Linux * kernel switch, it instead has a pair of subfields designating the * "major" and "minor" numbers of a Linux kernel qdisc handle. * * This field is "semi-internal" in that it can be set with the "set_queue" * action but not matched or read or written other ways. * * Type: be32. * Maskable: no. * Formatting: hexadecimal. * Prerequisites: none. * Access: read-only. * NXM: none. * OXM: none. */ MFF_SKB_PRIORITY, /* "pkt_mark". * * Packet metadata mark. The mark may be passed into other system * components in order to facilitate interaction between subsystems. On * Linux this corresponds to struct sk_buff's "skb_mark" member but the * exact implementation is platform-dependent. * * Type: be32. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_PKT_MARK(33) since v2.0. * OXM: none. */ MFF_PKT_MARK, /* "ct_state". * * Connection tracking state. The field is populated by the NXAST_CT * action. The following bit values describe the state of the connection: * * - New (0x01): This is the beginning of a new connection. * - Established (0x02): This is part of an already existing connection. * - Related (0x04): This is a separate connection that is related to an * existing connection. * - Reply (0x08): This flow is in the reply direction, ie it did not * initiate the connection. * - Invalid (0x10): This flow could not be associated with a connection. * This could be set for a variety of reasons, * including (but not limited to): * - L3/L4 protocol handler is not loaded/unavailable. * - L3/L4 protocol handler determines that the packet * is malformed or invalid for the current FSM stage. * - Packets are unexpected length for protocol. * - Tracked (0x20): Connection tracking has occurred. * * The "Tracked" bit corresponds to the packet_state as described in the * description of NXAST_CT action. The remaining bits correspond to * connection state. The "New" bit implies that the connection state * is uncommitted, while "Established" implies that it has previously been * committed. * * There are additional constraints on the ct_state bits, listed in order * of precedence below: * * - If "Tracked" is unset, no other bits may be set. * - If "Tracked" is set, one or more other bits may be set. * - If "Invalid" is set, only the "Tracked" bit is also set. * - The "New" and "Established" bits are mutually exclusive. * - The "New" and "Reply" bits are mutually exclusive. * - The "Related" bit may be set in conjunction with any other bits. * Connections that are identified as "Related" are separate * connections from the originating connection, so must be committed * separately. All packets for a related connection will have the * "Related" bit set (not just the initial packet). * * Type: be32. * Maskable: bitwise. * Formatting: ct state. * Prerequisites: none. * Access: read-only. * NXM: NXM_NX_CT_STATE(105) since v2.5. * OXM: none. */ MFF_CT_STATE, /* "ct_zone". * * Connection tracking zone. The field is populated by the * NXAST_CT action. * * Type: be16. * Maskable: no. * Formatting: hexadecimal. * Prerequisites: none. * Access: read-only. * NXM: NXM_NX_CT_ZONE(106) since v2.5. * OXM: none. */ MFF_CT_ZONE, /* "ct_mark". * * Connection tracking mark. The mark is carried with the * connection tracking state. On Linux this corresponds to the * nf_conn's "mark" member but the exact implementation is * platform-dependent. * * Writable only from nested actions within the NXAST_CT action. * * Type: be32. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_CT_MARK(107) since v2.5. * OXM: none. */ MFF_CT_MARK, /* "ct_label". * * Connection tracking label. The label is carried with the * connection tracking state. On Linux this is held in the * conntrack label extension but the exact implementation is * platform-dependent. * * Writable only from nested actions within the NXAST_CT action. * * Type: be128. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_CT_LABEL(108) since v2.5. * OXM: none. */ MFF_CT_LABEL, #if FLOW_N_REGS == 8 /* "reg". * * Nicira extension scratch pad register with initial value 0. * * Type: be32. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_NX_REG0(0) since v1.1. <0> * NXM: NXM_NX_REG1(1) since v1.1. <1> * NXM: NXM_NX_REG2(2) since v1.1. <2> * NXM: NXM_NX_REG3(3) since v1.1. <3> * NXM: NXM_NX_REG4(4) since v1.3. <4> * NXM: NXM_NX_REG5(5) since v1.7. <5> * NXM: NXM_NX_REG6(6) since v1.7. <6> * NXM: NXM_NX_REG7(7) since v1.7. <7> * OXM: none. */ MFF_REG0, MFF_REG1, MFF_REG2, MFF_REG3, MFF_REG4, MFF_REG5, MFF_REG6, MFF_REG7, #else #error "Need to update MFF_REG* to match FLOW_N_REGS" #endif #if FLOW_N_XREGS == 4 /* "xreg". * * OpenFlow 1.5 ``extended register". Each extended register * overlays two of the Nicira extension 32-bit registers: xreg0 overlays * reg0 and reg1, with reg0 supplying the most-significant bits of xreg0 * and reg1 the least-significant. xreg1 similarly overlays reg2 and reg3, * and so on. * * These registers were introduced in OpenFlow 1.5, but EXT-244 in the ONF * JIRA also publishes them as a (draft) OpenFlow extension to OpenFlow * 1.3. * * Type: be64. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: OXM_OF_PKT_REG() since OF1.3 and v2.4. */ MFF_XREG0, MFF_XREG1, MFF_XREG2, MFF_XREG3, #else #error "Need to update MFF_REG* to match FLOW_N_XREGS" #endif /* ## -------- ## */ /* ## Ethernet ## */ /* ## -------- ## */ /* "eth_src" (aka "dl_src"). * * Source address in Ethernet header. * * This field was not maskable before Open vSwitch 1.8. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: none. * Access: read/write. * NXM: NXM_OF_ETH_SRC(2) since v1.1. * OXM: OXM_OF_ETH_SRC(4) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: bitwise mask. */ MFF_ETH_SRC, /* "eth_dst" (aka "dl_dst"). * * Destination address in Ethernet header. * * Before Open vSwitch 1.8, the allowed masks were restricted to * 00:00:00:00:00:00, fe:ff:ff:ff:ff:ff, 01:00:00:00:00:00, * ff:ff:ff:ff:ff:ff. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: none. * Access: read/write. * NXM: NXM_OF_ETH_DST(1) since v1.1. * OXM: OXM_OF_ETH_DST(3) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: bitwise mask. */ MFF_ETH_DST, /* "eth_type" (aka "dl_type"). * * Packet's Ethernet type. * * For an Ethernet II packet this is taken from the Ethernet header. For * an 802.2 LLC+SNAP header with OUI 00-00-00 this is taken from the SNAP * header. A packet that has neither format has value 0x05ff * (OFP_DL_TYPE_NOT_ETH_TYPE). * * For a packet with an 802.1Q header, this is the type of the encapsulated * frame. * * Type: be16. * Maskable: no. * Formatting: hexadecimal. * Prerequisites: none. * Access: read-only. * NXM: NXM_OF_ETH_TYPE(3) since v1.1. * OXM: OXM_OF_ETH_TYPE(5) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_ETH_TYPE, /* ## ---- ## */ /* ## VLAN ## */ /* ## ---- ## */ /* It looks odd for vlan_tci, vlan_vid, and vlan_pcp to say that they are * supported in OF1.0 and OF1.1, since the detailed semantics of these fields * only apply to NXM or OXM. They are marked as supported for exact matches in * OF1.0 and OF1.1 because exact matches on those fields can be successfully * translated into the OF1.0 and OF1.1 flow formats. */ /* "vlan_tci". * * 802.1Q TCI. * * For a packet with an 802.1Q header, this is the Tag Control Information * (TCI) field, with the CFI bit forced to 1. For a packet with no 802.1Q * header, this has value 0. * * This field can be used in various ways: * * - If it is not constrained at all, the nx_match matches packets * without an 802.1Q header or with an 802.1Q header that has any TCI * value. * * - Testing for an exact match with 0 matches only packets without an * 802.1Q header. * * - Testing for an exact match with a TCI value with CFI=1 matches * packets that have an 802.1Q header with a specified VID and PCP. * * - Testing for an exact match with a nonzero TCI value with CFI=0 does * not make sense. The switch may reject this combination. * * - Testing with a specific VID and CFI=1, with nxm_mask=0x1fff, matches * packets that have an 802.1Q header with that VID (and any PCP). * * - Testing with a specific PCP and CFI=1, with nxm_mask=0xf000, matches * packets that have an 802.1Q header with that PCP (and any VID). * * - Testing with nxm_value=0, nxm_mask=0x0fff matches packets with no * 802.1Q header or with an 802.1Q header with a VID of 0. * * - Testing with nxm_value=0, nxm_mask=0xe000 matches packets with no * 802.1Q header or with an 802.1Q header with a PCP of 0. * * - Testing with nxm_value=0, nxm_mask=0xefff matches packets with no * 802.1Q header or with an 802.1Q header with both VID and PCP of 0. * * Type: be16. * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: none. * Access: read/write. * NXM: NXM_OF_VLAN_TCI(4) since v1.1. * OXM: none. * OF1.0: exact match. * OF1.1: exact match. */ MFF_VLAN_TCI, /* "dl_vlan" (OpenFlow 1.0). * * VLAN ID field. Zero if no 802.1Q header is present. * * Type: be16 (low 12 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: none. * OF1.0: exact match. * OF1.1: exact match. */ MFF_DL_VLAN, /* "vlan_vid" (OpenFlow 1.2+). * * If an 802.1Q header is present, this field's value is 0x1000 * bitwise-or'd with the VLAN ID. If no 802.1Q is present, this field's * value is 0. * * Type: be16 (low 12 bits). * Maskable: bitwise. * Formatting: decimal. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: OXM_OF_VLAN_VID(6) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_VLAN_VID, /* "dl_vlan_pcp" (OpenFlow 1.0). * * VLAN priority (PCP) field. Zero if no 802.1Q header is present. * * Type: u8 (low 3 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: none. * Access: read/write. * NXM: none. * OXM: none. * OF1.0: exact match. * OF1.1: exact match. */ MFF_DL_VLAN_PCP, /* "vlan_pcp" (OpenFlow 1.2+). * * VLAN priority (PCP) field. Zero if no 802.1Q header is present. * * Type: u8 (low 3 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: VLAN VID. * Access: read/write. * NXM: none. * OXM: OXM_OF_VLAN_PCP(7) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_VLAN_PCP, /* ## ---- ## */ /* ## MPLS ## */ /* ## ---- ## */ /* "mpls_label". * * The outermost MPLS label, or 0 if no MPLS labels are present. * * Type: be32 (low 20 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: MPLS. * Access: read/write. * NXM: none. * OXM: OXM_OF_MPLS_LABEL(34) since OF1.2 and v1.11. * OF1.1: exact match. */ MFF_MPLS_LABEL, /* "mpls_tc". * * The outermost MPLS label's traffic control (TC) field, or 0 if no MPLS * labels are present. * * Type: u8 (low 3 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: MPLS. * Access: read/write. * NXM: none. * OXM: OXM_OF_MPLS_TC(35) since OF1.2 and v1.11. * OF1.1: exact match. */ MFF_MPLS_TC, /* "mpls_bos". * * The outermost MPLS label's bottom of stack (BoS) field, or 0 if no MPLS * labels are present. * * Type: u8 (low 1 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: MPLS. * Access: read-only. * NXM: none. * OXM: OXM_OF_MPLS_BOS(36) since OF1.3 and v1.11. */ MFF_MPLS_BOS, /* ## ---- ## */ /* ## IPv4 ## */ /* ## ---- ## */ /* Update mf_is_l3_or_higher() if MFF_IPV4_SRC is no longer the first element * for a field of layer 3 or higher */ /* "ip_src" (aka "nw_src"). * * The source address in the IPv4 header. * * Before Open vSwitch 1.8, only CIDR masks were supported. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: IPv4. * Access: read/write. * NXM: NXM_OF_IP_SRC(7) since v1.1. * OXM: OXM_OF_IPV4_SRC(11) since OF1.2 and v1.7. * OF1.0: CIDR mask. * OF1.1: bitwise mask. * Prefix lookup member: nw_src. */ MFF_IPV4_SRC, /* "ip_dst" (aka "nw_dst"). * * The destination address in the IPv4 header. * * Before Open vSwitch 1.8, only CIDR masks were supported. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: IPv4. * Access: read/write. * NXM: NXM_OF_IP_DST(8) since v1.1. * OXM: OXM_OF_IPV4_DST(12) since OF1.2 and v1.7. * OF1.0: CIDR mask. * OF1.1: bitwise mask. * Prefix lookup member: nw_dst. */ MFF_IPV4_DST, /* ## ---- ## */ /* ## IPv6 ## */ /* ## ---- ## */ /* "ipv6_src". * * The source address in the IPv6 header. * * Type: be128. * Maskable: bitwise. * Formatting: IPv6. * Prerequisites: IPv6. * Access: read/write. * NXM: NXM_NX_IPV6_SRC(19) since v1.1. * OXM: OXM_OF_IPV6_SRC(26) since OF1.2 and v1.1. * Prefix lookup member: ipv6_src. */ MFF_IPV6_SRC, /* "ipv6_dst". * * The destination address in the IPv6 header. * * Type: be128. * Maskable: bitwise. * Formatting: IPv6. * Prerequisites: IPv6. * Access: read/write. * NXM: NXM_NX_IPV6_DST(20) since v1.1. * OXM: OXM_OF_IPV6_DST(27) since OF1.2 and v1.1. * Prefix lookup member: ipv6_dst. */ MFF_IPV6_DST, /* "ipv6_label". * * The flow label in the IPv6 header. * * Type: be32 (low 20 bits). * Maskable: bitwise. * Formatting: hexadecimal. * Prerequisites: IPv6. * Access: read/write. * NXM: NXM_NX_IPV6_LABEL(27) since v1.4. * OXM: OXM_OF_IPV6_FLABEL(28) since OF1.2 and v1.7. */ MFF_IPV6_LABEL, /* ## ----------------------- ## */ /* ## IPv4/IPv6 common fields ## */ /* ## ----------------------- ## */ /* "nw_proto" (aka "ip_proto"). * * The "protocol" byte in the IPv4 or IPv6 header. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: IPv4/IPv6. * Access: read-only. * NXM: NXM_OF_IP_PROTO(6) since v1.1. * OXM: OXM_OF_IP_PROTO(10) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_IP_PROTO, /* Both views of the DSCP below are marked as supported in all of the versions * of OpenFlow because a match on either view can be successfully translated * into every OpenFlow flow format. */ /* "nw_tos" (OpenFlow 1.0/1.1). * * The DSCP byte in the IPv4 header or the traffic class byte from the IPv6 * header, with the ECN bits forced to 0. (That is, bits 2-7 contain the * type of service and bits 0-1 are zero.) * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: IPv4/IPv6. * Access: read/write. * NXM: NXM_OF_IP_TOS(5) since v1.1. * OXM: none. * OF1.0: exact match. * OF1.1: exact match. */ MFF_IP_DSCP, /* "ip_dscp" (OpenFlow 1.2+). * * The DSCP byte in the IPv4 header or the traffic class byte from the IPv6 * header, shifted right 2 bits. (That is, bits 0-5 contain the type of * service and bits 6-7 are zero.) * * Type: u8 (low 6 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: IPv4/IPv6. * Access: read/write. * NXM: none. * OXM: OXM_OF_IP_DSCP(8) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_IP_DSCP_SHIFTED, /* "nw_ecn" (aka "ip_ecn"). * * The ECN bits in the IPv4 or IPv6 header. * * Type: u8 (low 2 bits). * Maskable: no. * Formatting: decimal. * Prerequisites: IPv4/IPv6. * Access: read/write. * NXM: NXM_NX_IP_ECN(28) since v1.4. * OXM: OXM_OF_IP_ECN(9) since OF1.2 and v1.7. */ MFF_IP_ECN, /* "nw_ttl". * * The time-to-live (TTL) in the IPv4 header or hop limit in the IPv6 * header. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: IPv4/IPv6. * Access: read/write. * NXM: NXM_NX_IP_TTL(29) since v1.4. * OXM: none. */ MFF_IP_TTL, /* "ip_frag" (aka "nw_frag"). * * IP fragment information. * * This field has three possible values: * * - A packet that is not an IP fragment has value 0. * * - A packet that is an IP fragment with offset 0 (the first fragment) * has bit 0 set and thus value 1. * * - A packet that is an IP fragment with nonzero offset has bits 0 and 1 * set and thus value 3. * * NX_IP_FRAG_ANY and NX_IP_FRAG_LATER are declared to symbolically * represent the meanings of bits 0 and 1. * * The switch may reject matches against values that can never appear. * * It is important to understand how this field interacts with the OpenFlow * IP fragment handling mode: * * - In OFPC_FRAG_DROP mode, the OpenFlow switch drops all IP fragments * before they reach the flow table, so every packet that is available * for matching will have value 0 in this field. * * - Open vSwitch does not implement OFPC_FRAG_REASM mode, but if it did * then IP fragments would be reassembled before they reached the flow * table and again every packet available for matching would always * have value 0. * * - In OFPC_FRAG_NORMAL mode, all three values are possible, but * OpenFlow 1.0 says that fragments' transport ports are always 0, even * for the first fragment, so this does not provide much extra * information. * * - In OFPC_FRAG_NX_MATCH mode, all three values are possible. For * fragments with offset 0, Open vSwitch makes L4 header information * available. * * Type: u8 (low 2 bits). * Maskable: bitwise. * Formatting: frag. * Prerequisites: IPv4/IPv6. * Access: read-only. * NXM: NXM_NX_IP_FRAG(26) since v1.3. * OXM: none. */ MFF_IP_FRAG, /* ## --- ## */ /* ## ARP ## */ /* ## --- ## */ /* "arp_op". * * ARP opcode. * * For an Ethernet+IP ARP packet, the opcode in the ARP header. Always 0 * otherwise. Only ARP opcodes between 1 and 255 should be specified for * matching. * * Type: be16. * Maskable: no. * Formatting: decimal. * Prerequisites: ARP. * Access: read/write. * NXM: NXM_OF_ARP_OP(15) since v1.1. * OXM: OXM_OF_ARP_OP(21) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_ARP_OP, /* "arp_spa". * * For an Ethernet+IP ARP packet, the source protocol (IPv4) address in the * ARP header. Always 0 otherwise. * * Before Open vSwitch 1.8, only CIDR masks were supported. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: ARP. * Access: read/write. * NXM: NXM_OF_ARP_SPA(16) since v1.1. * OXM: OXM_OF_ARP_SPA(22) since OF1.2 and v1.7. * OF1.0: CIDR mask. * OF1.1: bitwise mask. */ MFF_ARP_SPA, /* "arp_tpa". * * For an Ethernet+IP ARP packet, the target protocol (IPv4) address in the * ARP header. Always 0 otherwise. * * Before Open vSwitch 1.8, only CIDR masks were supported. * * Type: be32. * Maskable: bitwise. * Formatting: IPv4. * Prerequisites: ARP. * Access: read/write. * NXM: NXM_OF_ARP_TPA(17) since v1.1. * OXM: OXM_OF_ARP_TPA(23) since OF1.2 and v1.7. * OF1.0: CIDR mask. * OF1.1: bitwise mask. */ MFF_ARP_TPA, /* "arp_sha". * * For an Ethernet+IP ARP packet, the source hardware (Ethernet) address in * the ARP header. Always 0 otherwise. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: ARP. * Access: read/write. * NXM: NXM_NX_ARP_SHA(17) since v1.1. * OXM: OXM_OF_ARP_SHA(24) since OF1.2 and v1.7. */ MFF_ARP_SHA, /* "arp_tha". * * For an Ethernet+IP ARP packet, the target hardware (Ethernet) address in * the ARP header. Always 0 otherwise. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: ARP. * Access: read/write. * NXM: NXM_NX_ARP_THA(18) since v1.1. * OXM: OXM_OF_ARP_THA(25) since OF1.2 and v1.7. */ MFF_ARP_THA, /* ## --- ## */ /* ## TCP ## */ /* ## --- ## */ /* "tcp_src" (aka "tp_src"). * * TCP source port. * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: TCP. * Access: read/write. * NXM: NXM_OF_TCP_SRC(9) since v1.1. * OXM: OXM_OF_TCP_SRC(13) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_TCP_SRC, /* "tcp_dst" (aka "tp_dst"). * * TCP destination port. * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: TCP. * Access: read/write. * NXM: NXM_OF_TCP_DST(10) since v1.1. * OXM: OXM_OF_TCP_DST(14) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_TCP_DST, /* "tcp_flags". * * Flags in the TCP header. * * TCP currently defines 9 flag bits, and additional 3 bits are reserved * (must be transmitted as zero). See RFCs 793, 3168, and 3540. * * Type: be16 (low 12 bits). * Maskable: bitwise. * Formatting: TCP flags. * Prerequisites: TCP. * Access: read-only. * NXM: NXM_NX_TCP_FLAGS(34) since v2.1. * OXM: ONFOXM_ET_TCP_FLAGS(42) since OF1.3 and v2.4, * OXM_OF_TCP_FLAGS(42) since OF1.5 and v2.3. */ MFF_TCP_FLAGS, /* ## --- ## */ /* ## UDP ## */ /* ## --- ## */ /* "udp_src". * * UDP source port. * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: UDP. * Access: read/write. * NXM: NXM_OF_UDP_SRC(11) since v1.1. * OXM: OXM_OF_UDP_SRC(15) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_UDP_SRC, /* "udp_dst". * * UDP destination port * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: UDP. * Access: read/write. * NXM: NXM_OF_UDP_DST(12) since v1.1. * OXM: OXM_OF_UDP_DST(16) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_UDP_DST, /* ## ---- ## */ /* ## SCTP ## */ /* ## ---- ## */ /* "sctp_src". * * SCTP source port. * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: SCTP. * Access: read/write. * NXM: none. * OXM: OXM_OF_SCTP_SRC(17) since OF1.2 and v2.0. * OF1.1: exact match. */ MFF_SCTP_SRC, /* "sctp_dst". * * SCTP destination port. * * Type: be16. * Maskable: bitwise. * Formatting: decimal. * Prerequisites: SCTP. * Access: read/write. * NXM: none. * OXM: OXM_OF_SCTP_DST(18) since OF1.2 and v2.0. * OF1.1: exact match. */ MFF_SCTP_DST, /* ## ---- ## */ /* ## ICMP ## */ /* ## ---- ## */ /* "icmp_type". * * ICMPv4 type. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: ICMPv4. * Access: read/write. * NXM: NXM_OF_ICMP_TYPE(13) since v1.1. * OXM: OXM_OF_ICMPV4_TYPE(19) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_ICMPV4_TYPE, /* "icmp_code". * * ICMPv4 code. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: ICMPv4. * Access: read/write. * NXM: NXM_OF_ICMP_CODE(14) since v1.1. * OXM: OXM_OF_ICMPV4_CODE(20) since OF1.2 and v1.7. * OF1.0: exact match. * OF1.1: exact match. */ MFF_ICMPV4_CODE, /* "icmpv6_type". * * ICMPv6 type. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: ICMPv6. * Access: read/write. * NXM: NXM_NX_ICMPV6_TYPE(21) since v1.1. * OXM: OXM_OF_ICMPV6_TYPE(29) since OF1.2 and v1.7. */ MFF_ICMPV6_TYPE, /* "icmpv6_code". * * ICMPv6 code. * * Type: u8. * Maskable: no. * Formatting: decimal. * Prerequisites: ICMPv6. * Access: read/write. * NXM: NXM_NX_ICMPV6_CODE(22) since v1.1. * OXM: OXM_OF_ICMPV6_CODE(30) since OF1.2 and v1.7. */ MFF_ICMPV6_CODE, /* ## ------------------------- ## */ /* ## ICMPv6 Neighbor Discovery ## */ /* ## ------------------------- ## */ /* "nd_target". * * The target address in an IPv6 Neighbor Discovery message. * * Before Open vSwitch 1.8, only CIDR masks were supported. * * Type: be128. * Maskable: bitwise. * Formatting: IPv6. * Prerequisites: ND. * Access: read/write. * NXM: NXM_NX_ND_TARGET(23) since v1.1. * OXM: OXM_OF_IPV6_ND_TARGET(31) since OF1.2 and v1.7. */ MFF_ND_TARGET, /* "nd_sll". * * The source link layer address in an IPv6 Neighbor Discovery message. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: ND solicit. * Access: read/write. * NXM: NXM_NX_ND_SLL(24) since v1.1. * OXM: OXM_OF_IPV6_ND_SLL(32) since OF1.2 and v1.7. */ MFF_ND_SLL, /* "nd_tll". * * The target link layer address in an IPv6 Neighbor Discovery message. * * Type: MAC. * Maskable: bitwise. * Formatting: Ethernet. * Prerequisites: ND advert. * Access: read/write. * NXM: NXM_NX_ND_TLL(25) since v1.1. * OXM: OXM_OF_IPV6_ND_TLL(33) since OF1.2 and v1.7. */ MFF_ND_TLL, MFF_N_IDS }; /* A set of mf_field_ids. */ struct mf_bitmap { unsigned long bm[BITMAP_N_LONGS(MFF_N_IDS)]; }; #define MF_BITMAP_INITIALIZER { { [0] = 0 } } /* Use this macro as CASE_MFF_REGS: in a switch statement to choose all of the * MFF_REGn cases. */ #if FLOW_N_REGS == 8 #define CASE_MFF_REGS \ case MFF_REG0: case MFF_REG1: case MFF_REG2: case MFF_REG3: \ case MFF_REG4: case MFF_REG5: case MFF_REG6: case MFF_REG7 #else #error "Need to update CASE_MFF_REGS to match FLOW_N_REGS" #endif /* Use this macro as CASE_MFF_XREGS: in a switch statement to choose all of the * MFF_REGn cases. */ #if FLOW_N_XREGS == 4 #define CASE_MFF_XREGS \ case MFF_XREG0: case MFF_XREG1: case MFF_XREG2: case MFF_XREG3 #else #error "Need to update CASE_MFF_XREGS to match FLOW_N_XREGS" #endif /* Use this macro as CASE_MFF_TUN_METADATA: in a switch statement to choose * all of the MFF_TUN_METADATAn cases. */ #define CASE_MFF_TUN_METADATA \ case MFF_TUN_METADATA0: case MFF_TUN_METADATA1: \ case MFF_TUN_METADATA2: case MFF_TUN_METADATA3: \ case MFF_TUN_METADATA4: case MFF_TUN_METADATA5: \ case MFF_TUN_METADATA6: case MFF_TUN_METADATA7: \ case MFF_TUN_METADATA8: case MFF_TUN_METADATA9: \ case MFF_TUN_METADATA10: case MFF_TUN_METADATA11: \ case MFF_TUN_METADATA12: case MFF_TUN_METADATA13: \ case MFF_TUN_METADATA14: case MFF_TUN_METADATA15: \ case MFF_TUN_METADATA16: case MFF_TUN_METADATA17: \ case MFF_TUN_METADATA18: case MFF_TUN_METADATA19: \ case MFF_TUN_METADATA20: case MFF_TUN_METADATA21: \ case MFF_TUN_METADATA22: case MFF_TUN_METADATA23: \ case MFF_TUN_METADATA24: case MFF_TUN_METADATA25: \ case MFF_TUN_METADATA26: case MFF_TUN_METADATA27: \ case MFF_TUN_METADATA28: case MFF_TUN_METADATA29: \ case MFF_TUN_METADATA30: case MFF_TUN_METADATA31: \ case MFF_TUN_METADATA32: case MFF_TUN_METADATA33: \ case MFF_TUN_METADATA34: case MFF_TUN_METADATA35: \ case MFF_TUN_METADATA36: case MFF_TUN_METADATA37: \ case MFF_TUN_METADATA38: case MFF_TUN_METADATA39: \ case MFF_TUN_METADATA40: case MFF_TUN_METADATA41: \ case MFF_TUN_METADATA42: case MFF_TUN_METADATA43: \ case MFF_TUN_METADATA44: case MFF_TUN_METADATA45: \ case MFF_TUN_METADATA46: case MFF_TUN_METADATA47: \ case MFF_TUN_METADATA48: case MFF_TUN_METADATA49: \ case MFF_TUN_METADATA50: case MFF_TUN_METADATA51: \ case MFF_TUN_METADATA52: case MFF_TUN_METADATA53: \ case MFF_TUN_METADATA54: case MFF_TUN_METADATA55: \ case MFF_TUN_METADATA56: case MFF_TUN_METADATA57: \ case MFF_TUN_METADATA58: case MFF_TUN_METADATA59: \ case MFF_TUN_METADATA60: case MFF_TUN_METADATA61: \ case MFF_TUN_METADATA62: case MFF_TUN_METADATA63 /* Prerequisites for matching a field. * * A field may only be matched if the correct lower-level protocols are also * matched. For example, the TCP port may be matched only if the Ethernet type * matches ETH_TYPE_IP and the IP protocol matches IPPROTO_TCP. */ enum OVS_PACKED_ENUM mf_prereqs { MFP_NONE, /* L2 requirements. */ MFP_ARP, MFP_VLAN_VID, MFP_IPV4, MFP_IPV6, MFP_IP_ANY, /* L2.5 requirements. */ MFP_MPLS, /* L2+L3 requirements. */ MFP_TCP, /* On IPv4 or IPv6. */ MFP_UDP, /* On IPv4 or IPv6. */ MFP_SCTP, /* On IPv4 or IPv6. */ MFP_ICMPV4, MFP_ICMPV6, /* L2+L3+L4 requirements. */ MFP_ND, MFP_ND_SOLICIT, MFP_ND_ADVERT }; /* Forms of partial-field masking allowed for a field. * * Every field may be masked as a whole. */ enum OVS_PACKED_ENUM mf_maskable { MFM_NONE, /* No sub-field masking. */ MFM_FULLY, /* Every bit is individually maskable. */ }; /* How to format or parse a field's value. */ enum OVS_PACKED_ENUM mf_string { /* Integer formats. * * The particular MFS_* constant sets the output format. On input, either * decimal or hexadecimal (prefixed with 0x) is accepted. */ MFS_DECIMAL, MFS_HEXADECIMAL, /* Other formats. */ MFS_CT_STATE, /* Connection tracking state */ MFS_ETHERNET, MFS_IPV4, MFS_IPV6, MFS_OFP_PORT, /* 16-bit OpenFlow 1.0 port number or name. */ MFS_OFP_PORT_OXM, /* 32-bit OpenFlow 1.1+ port number or name. */ MFS_FRAG, /* no, yes, first, later, not_later */ MFS_TNL_FLAGS, /* FLOW_TNL_F_* flags */ MFS_TCP_FLAGS, /* TCP_* flags */ }; struct mf_field { /* Identification. */ enum mf_field_id id; /* MFF_*. */ const char *name; /* Name of this field, e.g. "eth_type". */ const char *extra_name; /* Alternate name, e.g. "dl_type", or NULL. */ /* Size. * * Most fields have n_bytes * 8 == n_bits. There are a few exceptions: * * - "dl_vlan" is 2 bytes but only 12 bits. * - "dl_vlan_pcp" is 1 byte but only 3 bits. * - "is_frag" is 1 byte but only 2 bits. * - "ipv6_label" is 4 bytes but only 20 bits. * - "mpls_label" is 4 bytes but only 20 bits. * - "mpls_tc" is 1 byte but only 3 bits. * - "mpls_bos" is 1 byte but only 1 bit. */ unsigned int n_bytes; /* Width of the field in bytes. */ unsigned int n_bits; /* Number of significant bits in field. */ bool variable_len; /* Length is variable, if so width is max. */ /* Properties. */ enum mf_maskable maskable; enum mf_string string; enum mf_prereqs prereqs; bool writable; /* May be written by actions? */ /* Usable protocols. * * NXM and OXM are extensible, allowing later extensions to be sent in * earlier protocol versions, so this does not necessarily correspond to * the OpenFlow protocol version the field was introduced in. * Also, some field types are tranparently mapped to each other via the * struct flow (like vlan and dscp/tos fields), so each variant supports * all protocols. * * These are combinations of OFPUTIL_P_*. (They are not declared as type * enum ofputil_protocol because that would give meta-flow.h and ofp-util.h * a circular dependency.) */ uint32_t usable_protocols_exact; /* Matching or setting whole field. */ uint32_t usable_protocols_cidr; /* Matching a CIDR mask in field. */ uint32_t usable_protocols_bitwise; /* Matching arbitrary bits in field. */ int flow_be32ofs; /* Field's be32 offset in "struct flow", if prefix tree * lookup is supported for the field, or -1. */ }; /* The representation of a field's value. */ union mf_value { uint8_t tun_metadata[128]; struct in6_addr ipv6; struct eth_addr mac; ovs_be128 be128; ovs_be64 be64; ovs_be32 be32; ovs_be16 be16; uint8_t u8; }; BUILD_ASSERT_DECL(sizeof(union mf_value) == 128); BUILD_ASSERT_DECL(sizeof(union mf_value) >= TLV_MAX_OPT_SIZE); /* A const mf_value with all bits initialized to ones. */ extern const union mf_value exact_match_mask; /* Part of a field. */ struct mf_subfield { const struct mf_field *field; unsigned int ofs; /* Bit offset. */ unsigned int n_bits; /* Number of bits. */ }; /* Data for some part of an mf_field. * * The data is stored "right-justified". For example, if "union mf_subvalue * value" contains NXM_OF_VLAN_TCI[0..11], then one could access the * corresponding data in value.be16[7] as the bits in the mask htons(0xfff). */ union mf_subvalue { /* Access to full data. */ uint8_t u8[128]; ovs_be16 be16[64]; ovs_be32 be32[32]; ovs_be64 be64[16]; /* Convenient access to just least-significant bits in various forms. */ struct { ovs_be64 dummy_integer[15]; ovs_be64 integer; }; struct { uint8_t dummy_mac[122]; struct eth_addr mac; }; struct { ovs_be32 dummy_ipv4[31]; ovs_be32 ipv4; }; struct { struct in6_addr dummy_ipv6[7]; struct in6_addr ipv6; }; }; BUILD_ASSERT_DECL(sizeof(union mf_value) == sizeof (union mf_subvalue)); bool mf_subvalue_intersect(const union mf_subvalue *a_value, const union mf_subvalue *a_mask, const union mf_subvalue *b_value, const union mf_subvalue *b_mask, union mf_subvalue *dst_value, union mf_subvalue *dst_mask); int mf_subvalue_width(const union mf_subvalue *); void mf_subvalue_shift(union mf_subvalue *, int n); /* An array of fields with values */ struct field_array { struct mf_bitmap used; union mf_value value[MFF_N_IDS]; }; /* Finding mf_fields. */ const struct mf_field *mf_from_name(const char *name); static inline const struct mf_field * mf_from_id(enum mf_field_id id) { extern const struct mf_field mf_fields[MFF_N_IDS]; ovs_assert((unsigned int) id < MFF_N_IDS); return &mf_fields[id]; } /* Inspecting wildcarded bits. */ bool mf_is_all_wild(const struct mf_field *, const struct flow_wildcards *); bool mf_is_mask_valid(const struct mf_field *, const union mf_value *mask); void mf_get_mask(const struct mf_field *, const struct flow_wildcards *, union mf_value *mask); /* Prerequisites. */ bool mf_are_prereqs_ok(const struct mf_field *, const struct flow *); void mf_mask_field_and_prereqs(const struct mf_field *, struct flow_wildcards *); void mf_mask_field_and_prereqs__(const struct mf_field *, const union mf_value *, struct flow_wildcards *); void mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap *bm); static inline bool mf_is_l3_or_higher(const struct mf_field *mf) { return mf->id >= MFF_IPV4_SRC; } /* Field values. */ bool mf_is_value_valid(const struct mf_field *, const union mf_value *value); void mf_get_value(const struct mf_field *, const struct flow *, union mf_value *value); void mf_set_value(const struct mf_field *, const union mf_value *value, struct match *, char **err_str); void mf_set_flow_value(const struct mf_field *, const union mf_value *value, struct flow *); void mf_set_flow_value_masked(const struct mf_field *, const union mf_value *value, const union mf_value *mask, struct flow *); bool mf_is_tun_metadata(const struct mf_field *); bool mf_is_set(const struct mf_field *, const struct flow *); void mf_mask_field(const struct mf_field *, struct flow *); int mf_field_len(const struct mf_field *, const union mf_value *value, const union mf_value *mask, bool *is_masked); void mf_get(const struct mf_field *, const struct match *, union mf_value *value, union mf_value *mask); /* Returns the set of usable protocols. */ uint32_t mf_set(const struct mf_field *, const union mf_value *value, const union mf_value *mask, struct match *, char **err_str); void mf_set_wild(const struct mf_field *, struct match *, char **err_str); /* Subfields. */ void mf_write_subfield_flow(const struct mf_subfield *, const union mf_subvalue *, struct flow *); void mf_write_subfield(const struct mf_subfield *, const union mf_subvalue *, struct match *); void mf_mask_subfield(const struct mf_field *, const union mf_subvalue *value, const union mf_subvalue *mask, struct match *); void mf_read_subfield(const struct mf_subfield *, const struct flow *, union mf_subvalue *); uint64_t mf_get_subfield(const struct mf_subfield *, const struct flow *); enum ofperr mf_check_src(const struct mf_subfield *, const struct flow *); enum ofperr mf_check_dst(const struct mf_subfield *, const struct flow *); /* Parsing and formatting. */ char *mf_parse(const struct mf_field *, const char *, union mf_value *value, union mf_value *mask); char *mf_parse_value(const struct mf_field *, const char *, union mf_value *); void mf_format(const struct mf_field *, const union mf_value *value, const union mf_value *mask, struct ds *); void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s); /* Field Arrays. */ void field_array_set(enum mf_field_id id, const union mf_value *, struct field_array *); #endif /* meta-flow.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/reconnect.c0000644000000000000000000000013213534540071016536 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.861854016 openvswitch-2.5.9/lib/reconnect.c0000644000175000017500000005650713534540071020241 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "reconnect.h" #include #include "poll-loop.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(reconnect); #define STATES \ STATE(VOID, 1 << 0) \ STATE(BACKOFF, 1 << 1) \ STATE(CONNECTING, 1 << 3) \ STATE(ACTIVE, 1 << 4) \ STATE(IDLE, 1 << 5) \ STATE(RECONNECT, 1 << 6) \ STATE(LISTENING, 1 << 7) enum state { #define STATE(NAME, VALUE) S_##NAME = VALUE, STATES #undef STATE }; static bool is_connected_state(enum state state) { return (state & (S_ACTIVE | S_IDLE)) != 0; } struct reconnect { /* Configuration. */ char *name; int min_backoff; int max_backoff; int probe_interval; bool passive; enum vlog_level info; /* Used for informational messages. */ /* State. */ enum state state; long long int state_entered; int backoff; long long int last_activity; long long int last_connected; long long int last_disconnected; unsigned int max_tries; /* These values are simply for statistics reporting, not otherwise used * directly by anything internal. */ long long int creation_time; unsigned int n_attempted_connections, n_successful_connections; unsigned int total_connected_duration; unsigned int seqno; }; static void reconnect_transition__(struct reconnect *, long long int now, enum state state); static long long int reconnect_deadline__(const struct reconnect *); static bool reconnect_may_retry(struct reconnect *); static const char * reconnect_state_name__(enum state state) { switch (state) { #define STATE(NAME, VALUE) case S_##NAME: return #NAME; STATES #undef STATE } return "***ERROR***"; } /* Creates and returns a new reconnect FSM with default settings. The FSM is * initially disabled. The caller will likely want to call reconnect_enable() * and reconnect_set_name() on the returned object. */ struct reconnect * reconnect_create(long long int now) { struct reconnect *fsm = xzalloc(sizeof *fsm); fsm->name = xstrdup("void"); fsm->min_backoff = RECONNECT_DEFAULT_MIN_BACKOFF; fsm->max_backoff = RECONNECT_DEFAULT_MAX_BACKOFF; fsm->probe_interval = RECONNECT_DEFAULT_PROBE_INTERVAL; fsm->passive = false; fsm->info = VLL_INFO; fsm->state = S_VOID; fsm->state_entered = now; fsm->backoff = 0; fsm->last_activity = now; fsm->last_connected = LLONG_MAX; fsm->last_disconnected = LLONG_MAX; fsm->max_tries = UINT_MAX; fsm->creation_time = now; return fsm; } /* Frees 'fsm'. */ void reconnect_destroy(struct reconnect *fsm) { if (fsm) { free(fsm->name); free(fsm); } } /* If 'quiet' is true, 'fsm' will log informational messages at level VLL_DBG, * by default keeping them out of log files. This is appropriate if the * connection is one that is expected to be short-lived, so that the log * messages are merely distracting. * * If 'quiet' is false, 'fsm' logs informational messages at level VLL_INFO. * This is the default. * * This setting has no effect on the log level of debugging, warning, or error * messages. */ void reconnect_set_quiet(struct reconnect *fsm, bool quiet) { fsm->info = quiet ? VLL_DBG : VLL_INFO; } /* Returns 'fsm''s name. */ const char * reconnect_get_name(const struct reconnect *fsm) { return fsm->name; } /* Sets 'fsm''s name to 'name'. If 'name' is null, then "void" is used * instead. * * The name set for 'fsm' is used in log messages. */ void reconnect_set_name(struct reconnect *fsm, const char *name) { free(fsm->name); fsm->name = xstrdup(name ? name : "void"); } /* Return the minimum number of milliseconds to back off between consecutive * connection attempts. The default is RECONNECT_DEFAULT_MIN_BACKOFF. */ int reconnect_get_min_backoff(const struct reconnect *fsm) { return fsm->min_backoff; } /* Return the maximum number of milliseconds to back off between consecutive * connection attempts. The default is RECONNECT_DEFAULT_MAX_BACKOFF. */ int reconnect_get_max_backoff(const struct reconnect *fsm) { return fsm->max_backoff; } /* Returns the "probe interval" for 'fsm' in milliseconds. If this is zero, it * disables the connection keepalive feature. If it is nonzero, then if the * interval passes while 'fsm' is connected and without reconnect_activity() * being called for 'fsm', reconnect_run() returns RECONNECT_PROBE. If the * interval passes again without reconnect_activity() being called, * reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'. */ int reconnect_get_probe_interval(const struct reconnect *fsm) { return fsm->probe_interval; } /* Limits the maximum number of times that 'fsm' will ask the client to try to * reconnect to 'max_tries'. UINT_MAX (the default) means an unlimited number * of tries. * * After the number of tries has expired, the 'fsm' will disable itself * instead of backing off and retrying. */ void reconnect_set_max_tries(struct reconnect *fsm, unsigned int max_tries) { fsm->max_tries = max_tries; } /* Returns the current remaining number of connection attempts, UINT_MAX if * the number is unlimited. */ unsigned int reconnect_get_max_tries(struct reconnect *fsm) { return fsm->max_tries; } /* Configures the backoff parameters for 'fsm'. 'min_backoff' is the minimum * number of milliseconds, and 'max_backoff' is the maximum, between connection * attempts. The current backoff is also the duration that 'fsm' is willing to * wait for a given connection to succeed or fail. * * 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than * or equal to 'min_backoff'. * * Pass 0 for 'min_backoff' or 'max_backoff' or both to use the defaults. */ void reconnect_set_backoff(struct reconnect *fsm, int min_backoff, int max_backoff) { fsm->min_backoff = MAX(min_backoff, 1000); fsm->max_backoff = (max_backoff ? MAX(max_backoff, 1000) : RECONNECT_DEFAULT_MAX_BACKOFF); if (fsm->min_backoff > fsm->max_backoff) { fsm->max_backoff = fsm->min_backoff; } if (fsm->state == S_BACKOFF && fsm->backoff > max_backoff) { fsm->backoff = max_backoff; } } /* Sets the "probe interval" for 'fsm' to 'probe_interval', in milliseconds. * If this is zero, it disables the connection keepalive feature. If it is * nonzero, then if the interval passes while 'fsm' is connected and without * reconnect_activity() being called for 'fsm', reconnect_run() returns * RECONNECT_PROBE. If the interval passes again without reconnect_activity() * being called, reconnect_run() returns RECONNECT_DISCONNECT for 'fsm'. * * If 'probe_interval' is nonzero, then it will be forced to a value of at * least 1000 ms. */ void reconnect_set_probe_interval(struct reconnect *fsm, int probe_interval) { fsm->probe_interval = probe_interval ? MAX(1000, probe_interval) : 0; } /* Returns true if 'fsm' is in passive mode, false if 'fsm' is in active mode * (the default). */ bool reconnect_is_passive(const struct reconnect *fsm) { return fsm->passive; } /* Configures 'fsm' for active or passive mode. In active mode (the default), * the FSM is attempting to connect to a remote host. In passive mode, the FSM * is listening for connections from a remote host. */ void reconnect_set_passive(struct reconnect *fsm, bool passive, long long int now) { if (fsm->passive != passive) { fsm->passive = passive; if (passive ? fsm->state & (S_CONNECTING | S_RECONNECT) : fsm->state == S_LISTENING && reconnect_may_retry(fsm)) { reconnect_transition__(fsm, now, S_BACKOFF); fsm->backoff = 0; } } } /* Returns true if 'fsm' has been enabled with reconnect_enable(). Calling * another function that indicates a change in connection state, such as * reconnect_disconnected() or reconnect_force_reconnect(), will also enable * a reconnect FSM. */ bool reconnect_is_enabled(const struct reconnect *fsm) { return fsm->state != S_VOID; } /* If 'fsm' is disabled (the default for newly created FSMs), enables it, so * that the next call to reconnect_run() for 'fsm' will return * RECONNECT_CONNECT. * * If 'fsm' is not disabled, this function has no effect. */ void reconnect_enable(struct reconnect *fsm, long long int now) { if (fsm->state == S_VOID && reconnect_may_retry(fsm)) { reconnect_transition__(fsm, now, S_BACKOFF); fsm->backoff = 0; } } /* Disables 'fsm'. Until 'fsm' is enabled again, reconnect_run() will always * return 0. */ void reconnect_disable(struct reconnect *fsm, long long int now) { if (fsm->state != S_VOID) { reconnect_transition__(fsm, now, S_VOID); } } /* If 'fsm' is enabled and currently connected (or attempting to connect), * forces reconnect_run() for 'fsm' to return RECONNECT_DISCONNECT the next * time it is called, which should cause the client to drop the connection (or * attempt), back off, and then reconnect. */ void reconnect_force_reconnect(struct reconnect *fsm, long long int now) { if (fsm->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) { reconnect_transition__(fsm, now, S_RECONNECT); } } /* Tell 'fsm' that the connection dropped or that a connection attempt failed. * 'error' specifies the reason: a positive value represents an errno value, * EOF indicates that the connection was closed by the peer (e.g. read() * returned 0), and 0 indicates no specific error. * * The FSM will back off, then reconnect. */ void reconnect_disconnected(struct reconnect *fsm, long long int now, int error) { if (!(fsm->state & (S_BACKOFF | S_VOID))) { /* Report what happened. */ if (fsm->state & (S_ACTIVE | S_IDLE)) { if (error > 0) { VLOG_WARN("%s: connection dropped (%s)", fsm->name, ovs_strerror(error)); } else if (error == EOF) { VLOG(fsm->info, "%s: connection closed by peer", fsm->name); } else { VLOG(fsm->info, "%s: connection dropped", fsm->name); } } else if (fsm->state == S_LISTENING) { if (error > 0) { VLOG_WARN("%s: error listening for connections (%s)", fsm->name, ovs_strerror(error)); } else { VLOG(fsm->info, "%s: error listening for connections", fsm->name); } } else { const char *type = fsm->passive ? "listen" : "connection"; if (error > 0) { VLOG_INFO("%s: %s attempt failed (%s)", fsm->name, type, ovs_strerror(error)); } else { VLOG(fsm->info, "%s: %s attempt timed out", fsm->name, type); } } if (fsm->state & (S_ACTIVE | S_IDLE)) { fsm->last_disconnected = now; } /* Back off. */ if (fsm->state & (S_ACTIVE | S_IDLE) && (fsm->last_activity - fsm->last_connected >= fsm->backoff || fsm->passive)) { fsm->backoff = fsm->passive ? 0 : fsm->min_backoff; } else { if (fsm->backoff < fsm->min_backoff) { fsm->backoff = fsm->min_backoff; } else if (fsm->backoff >= fsm->max_backoff / 2) { fsm->backoff = fsm->max_backoff; } else { fsm->backoff *= 2; } if (fsm->passive) { VLOG(fsm->info, "%s: waiting %.3g seconds before trying to " "listen again", fsm->name, fsm->backoff / 1000.0); } else { VLOG(fsm->info, "%s: waiting %.3g seconds before reconnect", fsm->name, fsm->backoff / 1000.0); } } reconnect_transition__(fsm, now, reconnect_may_retry(fsm) ? S_BACKOFF : S_VOID); } } /* Tell 'fsm' that a connection or listening attempt is in progress. * * The FSM will start a timer, after which the connection or listening attempt * will be aborted (by returning RECONNECT_DISCONNECT from * reconnect_run()). */ void reconnect_connecting(struct reconnect *fsm, long long int now) { if (fsm->state != S_CONNECTING) { if (fsm->passive) { VLOG(fsm->info, "%s: listening...", fsm->name); } else { VLOG(fsm->info, "%s: connecting...", fsm->name); } reconnect_transition__(fsm, now, S_CONNECTING); } } /* Tell 'fsm' that the client is listening for connection attempts. This state * last indefinitely until the client reports some change. * * The natural progression from this state is for the client to report that a * connection has been accepted or is in progress of being accepted, by calling * reconnect_connecting() or reconnect_connected(). * * The client may also report that listening failed (e.g. accept() returned an * unexpected error such as ENOMEM) by calling reconnect_listen_error(), in * which case the FSM will back off and eventually return RECONNECT_CONNECT * from reconnect_run() to tell the client to try listening again. */ void reconnect_listening(struct reconnect *fsm, long long int now) { if (fsm->state != S_LISTENING) { VLOG(fsm->info, "%s: listening...", fsm->name); reconnect_transition__(fsm, now, S_LISTENING); } } /* Tell 'fsm' that the client's attempt to accept a connection failed * (e.g. accept() returned an unexpected error such as ENOMEM). * * If the FSM is currently listening (reconnect_listening() was called), it * will back off and eventually return RECONNECT_CONNECT from reconnect_run() * to tell the client to try listening again. If there is an active * connection, this will be delayed until that connection drops. */ void reconnect_listen_error(struct reconnect *fsm, long long int now, int error) { if (fsm->state == S_LISTENING) { reconnect_disconnected(fsm, now, error); } } /* Tell 'fsm' that the connection was successful. * * The FSM will start the probe interval timer, which is reset by * reconnect_activity(). If the timer expires, a probe will be sent (by * returning RECONNECT_PROBE from reconnect_run()). If the timer expires * again without being reset, the connection will be aborted (by returning * RECONNECT_DISCONNECT from reconnect_run()). */ void reconnect_connected(struct reconnect *fsm, long long int now) { if (!is_connected_state(fsm->state)) { reconnect_connecting(fsm, now); VLOG(fsm->info, "%s: connected", fsm->name); reconnect_transition__(fsm, now, S_ACTIVE); fsm->last_connected = now; } } /* Tell 'fsm' that the connection attempt failed. * * The FSM will back off and attempt to reconnect. */ void reconnect_connect_failed(struct reconnect *fsm, long long int now, int error) { reconnect_connecting(fsm, now); reconnect_disconnected(fsm, now, error); } /* Tell 'fsm' that some activity has occurred on the connection. This resets * the probe interval timer, so that the connection is known not to be idle. */ void reconnect_activity(struct reconnect *fsm, long long int now) { if (fsm->state != S_ACTIVE) { reconnect_transition__(fsm, now, S_ACTIVE); } fsm->last_activity = now; } static void reconnect_transition__(struct reconnect *fsm, long long int now, enum state state) { if (fsm->state == S_CONNECTING) { fsm->n_attempted_connections++; if (state == S_ACTIVE) { fsm->n_successful_connections++; } } if (is_connected_state(fsm->state) != is_connected_state(state)) { if (is_connected_state(fsm->state)) { fsm->total_connected_duration += now - fsm->last_connected; } fsm->seqno++; } VLOG_DBG("%s: entering %s", fsm->name, reconnect_state_name__(state)); fsm->state = state; fsm->state_entered = now; } static long long int reconnect_deadline__(const struct reconnect *fsm) { ovs_assert(fsm->state_entered != LLONG_MIN); switch (fsm->state) { case S_VOID: case S_LISTENING: return LLONG_MAX; case S_BACKOFF: return fsm->state_entered + fsm->backoff; case S_CONNECTING: return fsm->state_entered + MAX(1000, fsm->backoff); case S_ACTIVE: if (fsm->probe_interval) { long long int base = MAX(fsm->last_activity, fsm->state_entered); return base + fsm->probe_interval; } return LLONG_MAX; case S_IDLE: if (fsm->probe_interval) { return fsm->state_entered + fsm->probe_interval; } return LLONG_MAX; case S_RECONNECT: return fsm->state_entered; } OVS_NOT_REACHED(); } /* Assesses whether any action should be taken on 'fsm'. The return value is * one of: * * - 0: The client need not take any action. * * - Active client, RECONNECT_CONNECT: The client should start a connection * attempt and indicate this by calling reconnect_connecting(). If the * connection attempt has definitely succeeded, it should call * reconnect_connected(). If the connection attempt has definitely * failed, it should call reconnect_connect_failed(). * * The FSM is smart enough to back off correctly after successful * connections that quickly abort, so it is OK to call * reconnect_connected() after a low-level successful connection * (e.g. connect()) even if the connection might soon abort due to a * failure at a high-level (e.g. SSL negotiation failure). * * - Passive client, RECONNECT_CONNECT: The client should try to listen for * a connection, if it is not already listening. It should call * reconnect_listening() if successful, otherwise reconnect_connecting() * or reconnected_connect_failed() if the attempt is in progress or * definitely failed, respectively. * * A listening passive client should constantly attempt to accept a new * connection and report an accepted connection with * reconnect_connected(). * * - RECONNECT_DISCONNECT: The client should abort the current connection * or connection attempt or listen attempt and call * reconnect_disconnected() or reconnect_connect_failed() to indicate it. * * - RECONNECT_PROBE: The client should send some kind of request to the * peer that will elicit a response, to ensure that the connection is * indeed in working order. (This will only be returned if the "probe * interval" is nonzero--see reconnect_set_probe_interval()). */ enum reconnect_action reconnect_run(struct reconnect *fsm, long long int now) { if (now >= reconnect_deadline__(fsm)) { switch (fsm->state) { case S_VOID: return 0; case S_BACKOFF: return RECONNECT_CONNECT; case S_CONNECTING: return RECONNECT_DISCONNECT; case S_ACTIVE: VLOG_DBG("%s: idle %lld ms, sending inactivity probe", fsm->name, now - MAX(fsm->last_activity, fsm->state_entered)); reconnect_transition__(fsm, now, S_IDLE); return RECONNECT_PROBE; case S_IDLE: VLOG_ERR("%s: no response to inactivity probe after %.3g " "seconds, disconnecting", fsm->name, (now - fsm->state_entered) / 1000.0); return RECONNECT_DISCONNECT; case S_RECONNECT: return RECONNECT_DISCONNECT; case S_LISTENING: return 0; } OVS_NOT_REACHED(); } else { return 0; } } /* Causes the next call to poll_block() to wake up when reconnect_run() should * be called on 'fsm'. */ void reconnect_wait(struct reconnect *fsm, long long int now) { int timeout = reconnect_timeout(fsm, now); if (timeout >= 0) { poll_timer_wait(timeout); } } /* Returns the number of milliseconds after which reconnect_run() should be * called on 'fsm' if nothing else notable happens in the meantime, or a * negative number if this is currently unnecessary. */ int reconnect_timeout(struct reconnect *fsm, long long int now) { long long int deadline = reconnect_deadline__(fsm); if (deadline != LLONG_MAX) { long long int remaining = deadline - now; return MAX(0, MIN(INT_MAX, remaining)); } return -1; } /* Returns true if 'fsm' is currently believed to be connected, that is, if * reconnect_connected() was called more recently than any call to * reconnect_connect_failed() or reconnect_disconnected() or * reconnect_disable(), and false otherwise. */ bool reconnect_is_connected(const struct reconnect *fsm) { return is_connected_state(fsm->state); } /* Returns the number of milliseconds since 'fsm' last successfully connected * to its peer (even if it has since disconnected). Returns UINT_MAX if never * connected. */ unsigned int reconnect_get_last_connect_elapsed(const struct reconnect *fsm, long long int now) { return fsm->last_connected == LLONG_MAX ? UINT_MAX : now - fsm->last_connected; } /* Returns the number of milliseconds since 'fsm' last disconnected * from its peer (even if it has since reconnected). Returns UINT_MAX if never * disconnected. */ unsigned int reconnect_get_last_disconnect_elapsed(const struct reconnect *fsm, long long int now) { return fsm->last_disconnected == LLONG_MAX ? UINT_MAX : now - fsm->last_disconnected; } /* Copies various statistics for 'fsm' into '*stats'. */ void reconnect_get_stats(const struct reconnect *fsm, long long int now, struct reconnect_stats *stats) { stats->creation_time = fsm->creation_time; stats->last_activity = fsm->last_activity; stats->last_connected = fsm->last_connected; stats->last_disconnected = fsm->last_disconnected; stats->backoff = fsm->backoff; stats->seqno = fsm->seqno; stats->is_connected = reconnect_is_connected(fsm); stats->msec_since_connect = reconnect_get_last_connect_elapsed(fsm, now); stats->msec_since_disconnect = reconnect_get_last_disconnect_elapsed(fsm, now); stats->total_connected_duration = fsm->total_connected_duration + (is_connected_state(fsm->state) ? reconnect_get_last_connect_elapsed(fsm, now) : 0); stats->n_attempted_connections = fsm->n_attempted_connections; stats->n_successful_connections = fsm->n_successful_connections; stats->state = reconnect_state_name__(fsm->state); stats->state_elapsed = now - fsm->state_entered; } static bool reconnect_may_retry(struct reconnect *fsm) { bool may_retry = fsm->max_tries > 0; if (may_retry && fsm->max_tries != UINT_MAX) { fsm->max_tries--; } return may_retry; } openvswitch-2.5.9/lib/PaxHeaders.82075/rstp.h0000644000000000000000000000013213534540071015553 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.865854045 openvswitch-2.5.9/lib/rstp.h0000644000175000017500000002336613534540071017253 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011-2014 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) public interface (header * file). * * Authors: * Martino Fornasa * Daniele Venturino * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. * */ #ifndef RSTP_H #define RSTP_H 1 #include #include #include "compiler.h" #include "util.h" /* Thread Safety: Callers passing in RSTP and RSTP port object * pointers must hold a reference to the passed object to ensure that * the object does not become stale while it is being accessed. */ extern struct ovs_mutex rstp_mutex; #define RSTP_MAX_PORTS 4095 struct dp_packet; /* Bridge priority defaults [Table 17-2] */ #define RSTP_MIN_PRIORITY 0 #define RSTP_MAX_PRIORITY 61440 #define RSTP_PRIORITY_STEP 4096 #define RSTP_DEFAULT_PRIORITY 32768 /* Port priority defaults [Table 17-2] */ #define RSTP_MIN_PORT_PRIORITY 0 #define RSTP_MAX_PORT_PRIORITY 240 #define RSTP_STEP_PORT_PRIORITY 16 #define RSTP_DEFAULT_PORT_PRIORITY 128 /* Performance parameters defaults. [Table 7-5] and [Table 17-1] * These values are expressed in seconds. */ #define RSTP_DEFAULT_AGEING_TIME 300 #define RSTP_MIN_AGEING_TIME 10 #define RSTP_MAX_AGEING_TIME 1000000 #define RSTP_DEFAULT_BRIDGE_MAX_AGE 20 #define RSTP_MIN_BRIDGE_MAX_AGE 6 #define RSTP_MAX_BRIDGE_MAX_AGE 40 #define RSTP_DEFAULT_BRIDGE_FORWARD_DELAY 15 #define RSTP_MIN_BRIDGE_FORWARD_DELAY 4 #define RSTP_MAX_BRIDGE_FORWARD_DELAY 30 #define RSTP_DEFAULT_TRANSMIT_HOLD_COUNT 6 #define RSTP_MIN_TRANSMIT_HOLD_COUNT 1 #define RSTP_MAX_TRANSMIT_HOLD_COUNT 10 #define RSTP_BRIDGE_HELLO_TIME 2 /* Value is fixed [Table 17-1] */ #define RSTP_MIGRATE_TIME 3 /* Value is fixed [Table 17-1] */ /* Port path cost [Table 17-3] */ #define RSTP_MIN_PORT_PATH_COST 1 #define RSTP_MAX_PORT_PATH_COST 200000000 #define RSTP_DEFAULT_PORT_PATH_COST 200000 /* RSTP Bridge identifier [9.2.5]. Top four most significant bits are a * priority value. The next most significant twelve bits are a locally * assigned system ID extension. Bottom 48 bits are MAC address of bridge. */ typedef uint64_t rstp_identifier; #define RSTP_ID_FMT "%01"PRIx8".%03"PRIx16".%012"PRIx64 #define RSTP_ID_ARGS(rstp_id) \ (uint8_t)((rstp_id) >> 60), \ (uint16_t)(((rstp_id) & 0x0fff000000000000ULL) >> 48), \ (uint64_t)((rstp_id) & 0xffffffffffffULL) #define RSTP_PORT_ID_FMT "%04"PRIx16 enum rstp_state { RSTP_DISABLED, RSTP_LEARNING, RSTP_FORWARDING, RSTP_DISCARDING }; /* Force Protocol Version [17.13.4] */ enum rstp_force_protocol_version { FPV_STP_COMPATIBILITY = 0, FPV_DEFAULT = 2 }; enum rstp_port_role { ROLE_ROOT, ROLE_DESIGNATED, ROLE_ALTERNATE, ROLE_BACKUP, ROLE_DISABLED }; enum rstp_admin_point_to_point_mac_state { RSTP_ADMIN_P2P_MAC_FORCE_FALSE, RSTP_ADMIN_P2P_MAC_FORCE_TRUE, RSTP_ADMIN_P2P_MAC_AUTO }; struct rstp; struct rstp_port; struct ofproto_rstp_settings; const char *rstp_state_name(enum rstp_state); const char *rstp_port_role_name(enum rstp_port_role); static inline bool rstp_forward_in_state(enum rstp_state); static inline bool rstp_learn_in_state(enum rstp_state); static inline bool rstp_should_manage_bpdu(enum rstp_state state); void rstp_init(void) OVS_EXCLUDED(rstp_mutex); struct rstp * rstp_create(const char *, rstp_identifier bridge_id, void (*send_bpdu)(struct dp_packet *, void *port_aux, void *rstp_aux), void *aux) OVS_EXCLUDED(rstp_mutex); struct rstp *rstp_ref(struct rstp *) OVS_EXCLUDED(rstp_mutex); void rstp_unref(struct rstp *) OVS_EXCLUDED(rstp_mutex); /* Functions used outside RSTP, to call functions defined in rstp-state-machines.h */ void rstp_tick_timers(struct rstp *) OVS_EXCLUDED(rstp_mutex); void rstp_port_received_bpdu(struct rstp_port *, const void *bpdu, size_t bpdu_size) OVS_EXCLUDED(rstp_mutex); void *rstp_check_and_reset_fdb_flush(struct rstp *, struct rstp_port **) OVS_EXCLUDED(rstp_mutex); void *rstp_get_next_changed_port_aux(struct rstp *, struct rstp_port **) OVS_EXCLUDED(rstp_mutex); void rstp_port_set_mac_operational(struct rstp_port *, bool new_mac_operational) OVS_EXCLUDED(rstp_mutex); bool rstp_shift_root_learned_address(struct rstp *) OVS_EXCLUDED(rstp_mutex); void *rstp_get_old_root_aux(struct rstp *) OVS_EXCLUDED(rstp_mutex); void *rstp_get_new_root_aux(struct rstp *) OVS_EXCLUDED(rstp_mutex); void rstp_reset_root_changed(struct rstp *) OVS_EXCLUDED(rstp_mutex); /* Bridge setters */ void rstp_set_bridge_address(struct rstp *, rstp_identifier bridge_address) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_priority(struct rstp *, int new_priority) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_ageing_time(struct rstp *, int new_ageing_time) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_force_protocol_version(struct rstp *, enum rstp_force_protocol_version) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_max_age(struct rstp *, int new_max_age) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_forward_delay(struct rstp *, int new_forward_delay) OVS_EXCLUDED(rstp_mutex); void rstp_set_bridge_transmit_hold_count(struct rstp *, int new_transmit_hold_count) OVS_EXCLUDED(rstp_mutex); /* Bridge getters */ const char * rstp_get_name(const struct rstp *) OVS_EXCLUDED(rstp_mutex); rstp_identifier rstp_get_root_id(const struct rstp *) OVS_EXCLUDED(rstp_mutex); rstp_identifier rstp_get_bridge_id(const struct rstp *) OVS_EXCLUDED(rstp_mutex); rstp_identifier rstp_get_designated_id(const struct rstp *) OVS_EXCLUDED(rstp_mutex); uint32_t rstp_get_root_path_cost(const struct rstp *) OVS_EXCLUDED(rstp_mutex); uint16_t rstp_get_designated_port_id(const struct rstp *) OVS_EXCLUDED(rstp_mutex); uint16_t rstp_get_bridge_port_id(const struct rstp *) OVS_EXCLUDED(rstp_mutex); struct rstp_port * rstp_get_root_port(struct rstp *) OVS_EXCLUDED(rstp_mutex); rstp_identifier rstp_get_designated_root(const struct rstp *) OVS_EXCLUDED(rstp_mutex); bool rstp_is_root_bridge(const struct rstp *) OVS_EXCLUDED(rstp_mutex); /* RSTP ports */ struct rstp_port * rstp_add_port(struct rstp *) OVS_EXCLUDED(rstp_mutex); struct rstp_port *rstp_port_ref(const struct rstp_port *) OVS_EXCLUDED(rstp_mutex); void rstp_port_unref(struct rstp_port *) OVS_EXCLUDED(rstp_mutex); uint32_t rstp_convert_speed_to_cost(unsigned int speed); void rstp_port_set(struct rstp_port *, uint16_t port_num, int priority, uint32_t path_cost, bool is_admin_edge, bool is_auto_edge, enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state, bool admin_port_state, bool do_mcheck, void *aux) OVS_EXCLUDED(rstp_mutex); enum rstp_state rstp_port_get_state(const struct rstp_port *) OVS_EXCLUDED(rstp_mutex); void rstp_port_get_status(const struct rstp_port *, uint16_t *id, enum rstp_state *state, enum rstp_port_role *role, rstp_identifier *designated_bridge_id, uint16_t *designated_port_id, uint32_t *designated_path_cost, int *tx_count, int *rx_count, int *error_count, int *uptime) OVS_EXCLUDED(rstp_mutex); void * rstp_get_port_aux__(struct rstp *rstp, uint16_t port_number) OVS_REQUIRES(rstp_mutex); /* Internal API for rstp-state-machines.c */ void rstp_port_set_state__(struct rstp_port *, enum rstp_state state) OVS_REQUIRES(rstp_mutex); /* Internal API for test-rstp.c */ struct rstp_port *rstp_get_port(struct rstp *rstp, uint16_t port_number) OVS_EXCLUDED(rstp_mutex); void reinitialize_port(struct rstp_port *p) OVS_EXCLUDED(rstp_mutex); int rstp_port_get_number(const struct rstp_port *) OVS_EXCLUDED(rstp_mutex); void rstp_port_set_priority(struct rstp_port *port, int priority) OVS_EXCLUDED(rstp_mutex); void rstp_port_set_aux(struct rstp_port *p, void *aux) OVS_EXCLUDED(rstp_mutex); void rstp_port_set_path_cost(struct rstp_port *port, uint32_t path_cost) OVS_EXCLUDED(rstp_mutex); void rstp_port_set_state(struct rstp_port *p, enum rstp_state state) OVS_EXCLUDED(rstp_mutex); /* Inline functions. */ /* Returns true if 'state' is one in which BPDU packets should be received * and transmitted on a port, false otherwise. */ static inline bool rstp_should_manage_bpdu(enum rstp_state state) { return (state == RSTP_DISCARDING || state == RSTP_LEARNING || state == RSTP_FORWARDING); } /* Returns true if 'state' is one in which packets received on a port should * be forwarded, false otherwise. */ static inline bool rstp_forward_in_state(enum rstp_state state) { return (state == RSTP_FORWARDING); } /* Returns true if 'state' is one in which MAC learning should be done on * packets received on a port, false otherwise. */ static inline bool rstp_learn_in_state(enum rstp_state state) { return (state == RSTP_LEARNING || state == RSTP_FORWARDING); } #endif /* rstp.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-print.c0000644000000000000000000000013213534540071016474 xustar0030 mtime=1567801401.525682081 30 atime=1567801402.085686193 30 ctime=1567801424.797853544 openvswitch-2.5.9/lib/ofp-print.c0000644000175000017500000031526513534540071020176 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofp-print.h" #include #include #include #include #include #include #include #include #include "bundle.h" #include "byte-order.h" #include "compiler.h" #include "dynamic-string.h" #include "flow.h" #include "learn.h" #include "multipath.h" #include "meta-flow.h" #include "netdev.h" #include "nx-match.h" #include "ofp-actions.h" #include "ofpbuf.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "openflow/openflow.h" #include "openflow/nicira-ext.h" #include "packets.h" #include "dp-packet.h" #include "type-props.h" #include "unaligned.h" #include "odp-util.h" #include "util.h" static void ofp_print_queue_name(struct ds *string, uint32_t port); static void ofp_print_error(struct ds *, enum ofperr); /* Returns a string that represents the contents of the Ethernet frame in the * 'len' bytes starting at 'data'. The caller must free the returned string.*/ char * ofp_packet_to_string(const void *data, size_t len) { struct ds ds = DS_EMPTY_INITIALIZER; struct dp_packet buf; struct flow flow; size_t l4_size; dp_packet_use_const(&buf, data, len); flow_extract(&buf, &flow); flow_format(&ds, &flow); l4_size = dp_packet_l4_size(&buf); if (flow.nw_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) { struct tcp_header *th = dp_packet_l4(&buf); ds_put_format(&ds, " tcp_csum:%"PRIx16, ntohs(th->tcp_csum)); } else if (flow.nw_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) { struct udp_header *uh = dp_packet_l4(&buf); ds_put_format(&ds, " udp_csum:%"PRIx16, ntohs(uh->udp_csum)); } else if (flow.nw_proto == IPPROTO_SCTP && l4_size >= SCTP_HEADER_LEN) { struct sctp_header *sh = dp_packet_l4(&buf); ds_put_format(&ds, " sctp_csum:%"PRIx32, ntohl(get_16aligned_be32(&sh->sctp_csum))); } else if (flow.nw_proto == IPPROTO_ICMP && l4_size >= ICMP_HEADER_LEN) { struct icmp_header *icmph = dp_packet_l4(&buf); ds_put_format(&ds, " icmp_csum:%"PRIx16, ntohs(icmph->icmp_csum)); } else if (flow.nw_proto == IPPROTO_ICMPV6 && l4_size >= ICMP6_HEADER_LEN) { struct icmp6_header *icmp6h = dp_packet_l4(&buf); ds_put_format(&ds, " icmp6_csum:%"PRIx16, ntohs(icmp6h->icmp6_cksum)); } ds_put_char(&ds, '\n'); return ds_cstr(&ds); } static void ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, int verbosity) { char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; struct ofputil_packet_in pin; int error; error = ofputil_decode_packet_in(&pin, oh); if (error) { ofp_print_error(string, error); return; } if (pin.table_id) { ds_put_format(string, " table_id=%"PRIu8, pin.table_id); } if (pin.cookie != OVS_BE64_MAX) { ds_put_format(string, " cookie=0x%"PRIx64, ntohll(pin.cookie)); } ds_put_format(string, " total_len=%"PRIuSIZE" ", pin.total_len); match_format(&pin.flow_metadata, string, OFP_DEFAULT_PRIORITY); ds_put_format(string, " (via %s)", ofputil_packet_in_reason_to_string(pin.reason, reasonbuf, sizeof reasonbuf)); ds_put_format(string, " data_len=%"PRIuSIZE, pin.packet_len); if (pin.buffer_id == UINT32_MAX) { ds_put_format(string, " (unbuffered)"); if (pin.total_len != pin.packet_len) { ds_put_format(string, " (***total_len != data_len***)"); } } else { ds_put_format(string, " buffer=0x%08"PRIx32, pin.buffer_id); if (pin.total_len < pin.packet_len) { ds_put_format(string, " (***total_len < data_len***)"); } } ds_put_char(string, '\n'); if (verbosity > 0) { char *packet = ofp_packet_to_string(pin.packet, pin.packet_len); ds_put_cstr(string, packet); free(packet); } if (verbosity > 2) { ds_put_hex_dump(string, pin.packet, pin.packet_len, 0, false); } } static void ofp_print_packet_out(struct ds *string, const struct ofp_header *oh, int verbosity) { struct ofputil_packet_out po; struct ofpbuf ofpacts; enum ofperr error; ofpbuf_init(&ofpacts, 64); error = ofputil_decode_packet_out(&po, oh, &ofpacts); if (error) { ofpbuf_uninit(&ofpacts); ofp_print_error(string, error); return; } ds_put_cstr(string, " in_port="); ofputil_format_port(po.in_port, string); ds_put_cstr(string, " actions="); ofpacts_format(po.ofpacts, po.ofpacts_len, string); if (po.buffer_id == UINT32_MAX) { ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len); if (verbosity > 0 && po.packet_len > 0) { char *packet = ofp_packet_to_string(po.packet, po.packet_len); ds_put_char(string, '\n'); ds_put_cstr(string, packet); free(packet); } if (verbosity > 2) { ds_put_hex_dump(string, po.packet, po.packet_len, 0, false); } } else { ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id); } ofpbuf_uninit(&ofpacts); } /* qsort comparison function. */ static int compare_ports(const void *a_, const void *b_) { const struct ofputil_phy_port *a = a_; const struct ofputil_phy_port *b = b_; uint16_t ap = ofp_to_u16(a->port_no); uint16_t bp = ofp_to_u16(b->port_no); return ap < bp ? -1 : ap > bp; } static void ofp_print_bit_names(struct ds *string, uint32_t bits, const char *(*bit_to_name)(uint32_t bit), char separator) { int n = 0; int i; if (!bits) { ds_put_cstr(string, "0"); return; } for (i = 0; i < 32; i++) { uint32_t bit = UINT32_C(1) << i; if (bits & bit) { const char *name = bit_to_name(bit); if (name) { if (n++) { ds_put_char(string, separator); } ds_put_cstr(string, name); bits &= ~bit; } } } if (bits) { if (n) { ds_put_char(string, separator); } ds_put_format(string, "0x%"PRIx32, bits); } } static const char * netdev_feature_to_name(uint32_t bit) { enum netdev_features f = bit; switch (f) { case NETDEV_F_10MB_HD: return "10MB-HD"; case NETDEV_F_10MB_FD: return "10MB-FD"; case NETDEV_F_100MB_HD: return "100MB-HD"; case NETDEV_F_100MB_FD: return "100MB-FD"; case NETDEV_F_1GB_HD: return "1GB-HD"; case NETDEV_F_1GB_FD: return "1GB-FD"; case NETDEV_F_10GB_FD: return "10GB-FD"; case NETDEV_F_40GB_FD: return "40GB-FD"; case NETDEV_F_100GB_FD: return "100GB-FD"; case NETDEV_F_1TB_FD: return "1TB-FD"; case NETDEV_F_OTHER: return "OTHER"; case NETDEV_F_COPPER: return "COPPER"; case NETDEV_F_FIBER: return "FIBER"; case NETDEV_F_AUTONEG: return "AUTO_NEG"; case NETDEV_F_PAUSE: return "AUTO_PAUSE"; case NETDEV_F_PAUSE_ASYM: return "AUTO_PAUSE_ASYM"; } return NULL; } static void ofp_print_port_features(struct ds *string, enum netdev_features features) { ofp_print_bit_names(string, features, netdev_feature_to_name, ' '); ds_put_char(string, '\n'); } static const char * ofputil_port_config_to_name(uint32_t bit) { enum ofputil_port_config pc = bit; switch (pc) { case OFPUTIL_PC_PORT_DOWN: return "PORT_DOWN"; case OFPUTIL_PC_NO_STP: return "NO_STP"; case OFPUTIL_PC_NO_RECV: return "NO_RECV"; case OFPUTIL_PC_NO_RECV_STP: return "NO_RECV_STP"; case OFPUTIL_PC_NO_FLOOD: return "NO_FLOOD"; case OFPUTIL_PC_NO_FWD: return "NO_FWD"; case OFPUTIL_PC_NO_PACKET_IN: return "NO_PACKET_IN"; } return NULL; } static void ofp_print_port_config(struct ds *string, enum ofputil_port_config config) { ofp_print_bit_names(string, config, ofputil_port_config_to_name, ' '); ds_put_char(string, '\n'); } static const char * ofputil_port_state_to_name(uint32_t bit) { enum ofputil_port_state ps = bit; switch (ps) { case OFPUTIL_PS_LINK_DOWN: return "LINK_DOWN"; case OFPUTIL_PS_BLOCKED: return "BLOCKED"; case OFPUTIL_PS_LIVE: return "LIVE"; case OFPUTIL_PS_STP_LISTEN: case OFPUTIL_PS_STP_LEARN: case OFPUTIL_PS_STP_FORWARD: case OFPUTIL_PS_STP_BLOCK: /* Handled elsewhere. */ return NULL; } return NULL; } static void ofp_print_port_state(struct ds *string, enum ofputil_port_state state) { enum ofputil_port_state stp_state; /* The STP state is a 2-bit field so it doesn't fit in with the bitmask * pattern. We have to special case it. * * OVS doesn't support STP, so this field will always be 0 if we are * talking to OVS, so we'd always print STP_LISTEN in that case. * Therefore, we don't print anything at all if the value is STP_LISTEN, to * avoid confusing users. */ stp_state = state & OFPUTIL_PS_STP_MASK; if (stp_state) { ds_put_cstr(string, (stp_state == OFPUTIL_PS_STP_LEARN ? "STP_LEARN" : stp_state == OFPUTIL_PS_STP_FORWARD ? "STP_FORWARD" : "STP_BLOCK")); state &= ~OFPUTIL_PS_STP_MASK; if (state) { ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' '); } } else { ofp_print_bit_names(string, state, ofputil_port_state_to_name, ' '); } ds_put_char(string, '\n'); } static void ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port) { char name[sizeof port->name]; int j; memcpy(name, port->name, sizeof name); for (j = 0; j < sizeof name - 1; j++) { if (!isprint((unsigned char) name[j])) { break; } } name[j] = '\0'; ds_put_char(string, ' '); ofputil_format_port(port->port_no, string); ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n", name, ETH_ADDR_ARGS(port->hw_addr)); ds_put_cstr(string, " config: "); ofp_print_port_config(string, port->config); ds_put_cstr(string, " state: "); ofp_print_port_state(string, port->state); if (port->curr) { ds_put_format(string, " current: "); ofp_print_port_features(string, port->curr); } if (port->advertised) { ds_put_format(string, " advertised: "); ofp_print_port_features(string, port->advertised); } if (port->supported) { ds_put_format(string, " supported: "); ofp_print_port_features(string, port->supported); } if (port->peer) { ds_put_format(string, " peer: "); ofp_print_port_features(string, port->peer); } ds_put_format(string, " speed: %"PRIu32" Mbps now, " "%"PRIu32" Mbps max\n", port->curr_speed / UINT32_C(1000), port->max_speed / UINT32_C(1000)); } /* Given a buffer 'b' that contains an array of OpenFlow ports of type * 'ofp_version', writes a detailed description of each port into * 'string'. */ static void ofp_print_phy_ports(struct ds *string, uint8_t ofp_version, struct ofpbuf *b) { struct ofputil_phy_port *ports; size_t allocated_ports, n_ports; int retval; size_t i; ports = NULL; allocated_ports = 0; for (n_ports = 0; ; n_ports++) { if (n_ports >= allocated_ports) { ports = x2nrealloc(ports, &allocated_ports, sizeof *ports); } retval = ofputil_pull_phy_port(ofp_version, b, &ports[n_ports]); if (retval) { break; } } qsort(ports, n_ports, sizeof *ports, compare_ports); for (i = 0; i < n_ports; i++) { ofp_print_phy_port(string, &ports[i]); } free(ports); if (retval != EOF) { ofp_print_error(string, retval); } } static const char * ofputil_capabilities_to_name(uint32_t bit) { enum ofputil_capabilities capabilities = bit; switch (capabilities) { case OFPUTIL_C_FLOW_STATS: return "FLOW_STATS"; case OFPUTIL_C_TABLE_STATS: return "TABLE_STATS"; case OFPUTIL_C_PORT_STATS: return "PORT_STATS"; case OFPUTIL_C_IP_REASM: return "IP_REASM"; case OFPUTIL_C_QUEUE_STATS: return "QUEUE_STATS"; case OFPUTIL_C_ARP_MATCH_IP: return "ARP_MATCH_IP"; case OFPUTIL_C_STP: return "STP"; case OFPUTIL_C_GROUP_STATS: return "GROUP_STATS"; case OFPUTIL_C_PORT_BLOCKED: return "PORT_BLOCKED"; case OFPUTIL_C_BUNDLES: return "BUNDLES"; case OFPUTIL_C_FLOW_MONITORING: return "FLOW_MONITORING"; } return NULL; } static void ofp_print_switch_features(struct ds *string, const struct ofp_header *oh) { struct ofputil_switch_features features; enum ofperr error; struct ofpbuf b; error = ofputil_decode_switch_features(oh, &features, &b); if (error) { ofp_print_error(string, error); return; } ds_put_format(string, " dpid:%016"PRIx64"\n", features.datapath_id); ds_put_format(string, "n_tables:%"PRIu8", n_buffers:%"PRIu32, features.n_tables, features.n_buffers); if (features.auxiliary_id) { ds_put_format(string, ", auxiliary_id:%"PRIu8, features.auxiliary_id); } ds_put_char(string, '\n'); ds_put_cstr(string, "capabilities: "); ofp_print_bit_names(string, features.capabilities, ofputil_capabilities_to_name, ' '); ds_put_char(string, '\n'); switch ((enum ofp_version)oh->version) { case OFP10_VERSION: ds_put_cstr(string, "actions: "); ofpact_bitmap_format(features.ofpacts, string); ds_put_char(string, '\n'); break; case OFP11_VERSION: case OFP12_VERSION: break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: return; /* no ports in ofp13_switch_features */ default: OVS_NOT_REACHED(); } ofp_print_phy_ports(string, oh->version, &b); } static void ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc) { enum ofp_config_flags flags; flags = ntohs(osc->flags); ds_put_format(string, " frags=%s", ofputil_frag_handling_to_string(flags)); flags &= ~OFPC_FRAG_MASK; if (flags & OFPC_INVALID_TTL_TO_CONTROLLER) { ds_put_format(string, " invalid_ttl_to_controller"); flags &= ~OFPC_INVALID_TTL_TO_CONTROLLER; } if (flags) { ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags); } ds_put_format(string, " miss_send_len=%"PRIu16"\n", ntohs(osc->miss_send_len)); } static void print_wild(struct ds *string, const char *leader, int is_wild, int verbosity, const char *format, ...) OVS_PRINTF_FORMAT(5, 6); static void print_wild(struct ds *string, const char *leader, int is_wild, int verbosity, const char *format, ...) { if (is_wild && verbosity < 2) { return; } ds_put_cstr(string, leader); if (!is_wild) { va_list args; va_start(args, format); ds_put_format_valist(string, format, args); va_end(args); } else { ds_put_char(string, '*'); } ds_put_char(string, ','); } static void print_wild_port(struct ds *string, const char *leader, int is_wild, int verbosity, ofp_port_t port) { if (is_wild && verbosity < 2) { return; } ds_put_cstr(string, leader); if (!is_wild) { ofputil_format_port(port, string); } else { ds_put_char(string, '*'); } ds_put_char(string, ','); } static void print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip, uint32_t wild_bits, int verbosity) { if (wild_bits >= 32 && verbosity < 2) { return; } ds_put_cstr(string, leader); if (wild_bits < 32) { ds_put_format(string, IP_FMT, IP_ARGS(ip)); if (wild_bits) { ds_put_format(string, "/%d", 32 - wild_bits); } } else { ds_put_char(string, '*'); } ds_put_char(string, ','); } void ofp10_match_print(struct ds *f, const struct ofp10_match *om, int verbosity) { char *s = ofp10_match_to_string(om, verbosity); ds_put_cstr(f, s); free(s); } char * ofp10_match_to_string(const struct ofp10_match *om, int verbosity) { struct ds f = DS_EMPTY_INITIALIZER; uint32_t w = ntohl(om->wildcards); bool skip_type = false; bool skip_proto = false; if (!(w & OFPFW10_DL_TYPE)) { skip_type = true; if (om->dl_type == htons(ETH_TYPE_IP)) { if (!(w & OFPFW10_NW_PROTO)) { skip_proto = true; if (om->nw_proto == IPPROTO_ICMP) { ds_put_cstr(&f, "icmp,"); } else if (om->nw_proto == IPPROTO_TCP) { ds_put_cstr(&f, "tcp,"); } else if (om->nw_proto == IPPROTO_UDP) { ds_put_cstr(&f, "udp,"); } else if (om->nw_proto == IPPROTO_SCTP) { ds_put_cstr(&f, "sctp,"); } else { ds_put_cstr(&f, "ip,"); skip_proto = false; } } else { ds_put_cstr(&f, "ip,"); } } else if (om->dl_type == htons(ETH_TYPE_ARP)) { ds_put_cstr(&f, "arp,"); } else if (om->dl_type == htons(ETH_TYPE_RARP)){ ds_put_cstr(&f, "rarp,"); } else if (om->dl_type == htons(ETH_TYPE_MPLS)) { ds_put_cstr(&f, "mpls,"); } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) { ds_put_cstr(&f, "mplsm,"); } else { skip_type = false; } } print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity, u16_to_ofp(ntohs(om->in_port))); print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity, "%d", ntohs(om->dl_vlan)); print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity, "%d", om->dl_vlan_pcp); print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity, ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src)); print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity, ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst)); if (!skip_type) { print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity, "0x%04x", ntohs(om->dl_type)); } print_ip_netmask(&f, "nw_src=", om->nw_src, (w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT, verbosity); print_ip_netmask(&f, "nw_dst=", om->nw_dst, (w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT, verbosity); if (!skip_proto) { if (om->dl_type == htons(ETH_TYPE_ARP) || om->dl_type == htons(ETH_TYPE_RARP)) { print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity, "%u", om->nw_proto); } else { print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity, "%u", om->nw_proto); } } print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity, "%u", om->nw_tos); if (om->nw_proto == IPPROTO_ICMP) { print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity, "%d", ntohs(om->tp_src)); print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity, "%d", ntohs(om->tp_dst)); } else { print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity, "%d", ntohs(om->tp_src)); print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity, "%d", ntohs(om->tp_dst)); } ds_chomp(&f, ','); return ds_cstr(&f); } static void ofp_print_flow_flags(struct ds *s, enum ofputil_flow_mod_flags flags) { if (flags & OFPUTIL_FF_SEND_FLOW_REM) { ds_put_cstr(s, "send_flow_rem "); } if (flags & OFPUTIL_FF_CHECK_OVERLAP) { ds_put_cstr(s, "check_overlap "); } if (flags & OFPUTIL_FF_RESET_COUNTS) { ds_put_cstr(s, "reset_counts "); } if (flags & OFPUTIL_FF_NO_PKT_COUNTS) { ds_put_cstr(s, "no_packet_counts "); } if (flags & OFPUTIL_FF_NO_BYT_COUNTS) { ds_put_cstr(s, "no_byte_counts "); } if (flags & OFPUTIL_FF_HIDDEN_FIELDS) { ds_put_cstr(s, "allow_hidden_fields "); } if (flags & OFPUTIL_FF_NO_READONLY) { ds_put_cstr(s, "no_readonly_table "); } } static void ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity) { struct ofputil_flow_mod fm; struct ofpbuf ofpacts; bool need_priority; enum ofperr error; enum ofpraw raw; enum ofputil_protocol protocol; protocol = ofputil_protocol_from_ofp_version(oh->version); protocol = ofputil_protocol_set_tid(protocol, true); ofpbuf_init(&ofpacts, 64); error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts, OFPP_MAX, 255); if (error) { ofpbuf_uninit(&ofpacts); ofp_print_error(s, error); return; } ds_put_char(s, ' '); switch (fm.command) { case OFPFC_ADD: ds_put_cstr(s, "ADD"); break; case OFPFC_MODIFY: ds_put_cstr(s, "MOD"); break; case OFPFC_MODIFY_STRICT: ds_put_cstr(s, "MOD_STRICT"); break; case OFPFC_DELETE: ds_put_cstr(s, "DEL"); break; case OFPFC_DELETE_STRICT: ds_put_cstr(s, "DEL_STRICT"); break; default: ds_put_format(s, "cmd:%d", fm.command); } if (fm.table_id != 0) { ds_put_format(s, " table:%d", fm.table_id); } ds_put_char(s, ' '); ofpraw_decode(&raw, oh); if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) { const struct ofp10_flow_mod *ofm = ofpmsg_body(oh); ofp10_match_print(s, &ofm->match, verbosity); /* ofp_print_match() doesn't print priority. */ need_priority = true; } else if (verbosity >= 3 && raw == OFPRAW_NXT_FLOW_MOD) { const struct nx_flow_mod *nfm = ofpmsg_body(oh); const void *nxm = nfm + 1; char *nxm_s; nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len)); ds_put_cstr(s, nxm_s); free(nxm_s); /* nx_match_to_string() doesn't print priority. */ need_priority = true; } else { match_format(&fm.match, s, fm.priority); /* match_format() does print priority. */ need_priority = false; } if (ds_last(s) != ' ') { ds_put_char(s, ' '); } if (fm.new_cookie != htonll(0) && fm.new_cookie != OVS_BE64_MAX) { ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie)); } if (fm.cookie_mask != htonll(0)) { ds_put_format(s, "cookie:0x%"PRIx64"/0x%"PRIx64" ", ntohll(fm.cookie), ntohll(fm.cookie_mask)); } if (fm.idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout); } if (fm.hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout); } if (fm.importance != 0) { ds_put_format(s, "importance:%"PRIu16" ", fm.importance); } if (fm.priority != OFP_DEFAULT_PRIORITY && need_priority) { ds_put_format(s, "pri:%"PRIu16" ", fm.priority); } if (fm.buffer_id != UINT32_MAX) { ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id); } if (fm.out_port != OFPP_ANY) { ds_put_format(s, "out_port:"); ofputil_format_port(fm.out_port, s); ds_put_char(s, ' '); } if (oh->version == OFP10_VERSION || oh->version == OFP11_VERSION) { /* Don't print the reset_counts flag for OF1.0 and OF1.1 because those * versions don't really have such a flag and printing one is likely to * confuse people. */ fm.flags &= ~OFPUTIL_FF_RESET_COUNTS; } ofp_print_flow_flags(s, fm.flags); ds_put_cstr(s, "actions="); ofpacts_format(fm.ofpacts, fm.ofpacts_len, s); ofpbuf_uninit(&ofpacts); } static void ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec) { ds_put_format(string, "%u", sec); /* If there are no fractional seconds, don't print any decimals. * * If the fractional seconds can be expressed exactly as milliseconds, * print 3 decimals. Open vSwitch provides millisecond precision for most * time measurements, so printing 3 decimals every time makes it easier to * spot real changes in flow dumps that refresh themselves quickly. * * If the fractional seconds are more precise than milliseconds, print the * number of decimals needed to express them exactly. */ if (nsec > 0) { unsigned int msec = nsec / 1000000; if (msec * 1000000 == nsec) { ds_put_format(string, ".%03u", msec); } else { ds_put_format(string, ".%09u", nsec); while (string->string[string->length - 1] == '0') { string->length--; } } } ds_put_char(string, 's'); } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFP_FLOW_REMOVED_REASON_BUFSIZE. */ #define OFP_FLOW_REMOVED_REASON_BUFSIZE (INT_STRLEN(int) + 1) static const char * ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPRR_IDLE_TIMEOUT: return "idle"; case OFPRR_HARD_TIMEOUT: return "hard"; case OFPRR_DELETE: return "delete"; case OFPRR_GROUP_DELETE: return "group_delete"; case OFPRR_EVICTION: return "eviction"; case OFPRR_METER_DELETE: return "meter_delete"; case OVS_OFPRR_NONE: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } static void ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh) { char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; struct ofputil_flow_removed fr; enum ofperr error; error = ofputil_decode_flow_removed(&fr, oh); if (error) { ofp_print_error(string, error); return; } ds_put_char(string, ' '); match_format(&fr.match, string, fr.priority); ds_put_format(string, " reason=%s", ofp_flow_removed_reason_to_string(fr.reason, reasonbuf, sizeof reasonbuf)); if (fr.table_id != 255) { ds_put_format(string, " table_id=%"PRIu8, fr.table_id); } if (fr.cookie != htonll(0)) { ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie)); } ds_put_cstr(string, " duration"); ofp_print_duration(string, fr.duration_sec, fr.duration_nsec); ds_put_format(string, " idle%"PRIu16, fr.idle_timeout); if (fr.hard_timeout) { /* The hard timeout was only added in OF1.2, so only print it if it is * actually in use to avoid gratuitous change to the formatting. */ ds_put_format(string, " hard%"PRIu16, fr.hard_timeout); } ds_put_format(string, " pkts%"PRIu64" bytes%"PRIu64"\n", fr.packet_count, fr.byte_count); } static void ofp_print_port_mod(struct ds *string, const struct ofp_header *oh) { struct ofputil_port_mod pm; enum ofperr error; error = ofputil_decode_port_mod(oh, &pm, true); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, "port: "); ofputil_format_port(pm.port_no, string); ds_put_format(string, ": addr:"ETH_ADDR_FMT"\n", ETH_ADDR_ARGS(pm.hw_addr)); ds_put_cstr(string, " config: "); ofp_print_port_config(string, pm.config); ds_put_cstr(string, " mask: "); ofp_print_port_config(string, pm.mask); ds_put_cstr(string, " advertise: "); if (pm.advertise) { ofp_print_port_features(string, pm.advertise); } else { ds_put_cstr(string, "UNCHANGED\n"); } } static const char * ofputil_table_miss_to_string(enum ofputil_table_miss miss) { switch (miss) { case OFPUTIL_TABLE_MISS_DEFAULT: return "default"; case OFPUTIL_TABLE_MISS_CONTROLLER: return "controller"; case OFPUTIL_TABLE_MISS_CONTINUE: return "continue"; case OFPUTIL_TABLE_MISS_DROP: return "drop"; default: return "***error***"; } } static const char * ofputil_table_eviction_to_string(enum ofputil_table_eviction eviction) { switch (eviction) { case OFPUTIL_TABLE_EVICTION_DEFAULT: return "default"; case OFPUTIL_TABLE_EVICTION_ON: return "on"; case OFPUTIL_TABLE_EVICTION_OFF: return "off"; default: return "***error***"; } } static const char * ofputil_eviction_flag_to_string(uint32_t bit) { enum ofp14_table_mod_prop_eviction_flag eviction_flag = bit; switch (eviction_flag) { case OFPTMPEF14_OTHER: return "OTHER"; case OFPTMPEF14_IMPORTANCE: return "IMPORTANCE"; case OFPTMPEF14_LIFETIME: return "LIFETIME"; } return NULL; } /* Appends to 'string' a description of the bitmap of OFPTMPEF14_* values in * 'eviction_flags'. */ static void ofputil_put_eviction_flags(struct ds *string, uint32_t eviction_flags) { if (eviction_flags != UINT32_MAX) { ofp_print_bit_names(string, eviction_flags, ofputil_eviction_flag_to_string, '|'); } else { ds_put_cstr(string, "(default)"); } } static const char * ofputil_table_vacancy_to_string(enum ofputil_table_vacancy vacancy) { switch (vacancy) { case OFPUTIL_TABLE_VACANCY_DEFAULT: return "default"; case OFPUTIL_TABLE_VACANCY_ON: return "on"; case OFPUTIL_TABLE_VACANCY_OFF: return "off"; default: return "***error***"; } } static void ofp_print_table_mod(struct ds *string, const struct ofp_header *oh) { struct ofputil_table_mod pm; enum ofperr error; error = ofputil_decode_table_mod(oh, &pm); if (error) { ofp_print_error(string, error); return; } if (pm.table_id == 0xff) { ds_put_cstr(string, " table_id: ALL_TABLES"); } else { ds_put_format(string, " table_id=%"PRIu8, pm.table_id); } if (pm.miss != OFPUTIL_TABLE_MISS_DEFAULT) { ds_put_format(string, ", flow_miss_config=%s", ofputil_table_miss_to_string(pm.miss)); } if (pm.eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { ds_put_format(string, ", eviction=%s", ofputil_table_eviction_to_string(pm.eviction)); } if (pm.eviction_flags != UINT32_MAX) { ds_put_cstr(string, "eviction_flags="); ofputil_put_eviction_flags(string, pm.eviction_flags); } if (pm.vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { ds_put_format(string, ", vacancy=%s", ofputil_table_vacancy_to_string(pm.vacancy)); if (pm.vacancy == OFPUTIL_TABLE_VACANCY_ON) { ds_put_format(string, " vacancy:%"PRIu8"" ",%"PRIu8"", pm.table_vacancy.vacancy_down, pm.table_vacancy.vacancy_up); } } } /* This function will print the Table description properties. */ static void ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc *td) { ds_put_format(string, "\n table %"PRIu8, td->table_id); ds_put_cstr(string, ":\n"); ds_put_format(string, " eviction=%s eviction_flags=", ofputil_table_eviction_to_string(td->eviction)); ofputil_put_eviction_flags(string, td->eviction_flags); ds_put_char(string, '\n'); ds_put_format(string, " vacancy=%s", ofputil_table_vacancy_to_string(td->vacancy)); if (td->vacancy == OFPUTIL_TABLE_VACANCY_ON) { ds_put_format(string, " vacancy_down=%"PRIu8"%%", td->table_vacancy.vacancy_down); ds_put_format(string, " vacancy_up=%"PRIu8"%%", td->table_vacancy.vacancy_up); ds_put_format(string, " vacancy=%"PRIu8"%%", td->table_vacancy.vacancy); } ds_put_char(string, '\n'); } static void ofp_print_queue_get_config_request(struct ds *string, const struct ofp_header *oh) { enum ofperr error; ofp_port_t port; error = ofputil_decode_queue_get_config_request(oh, &port); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " port="); ofputil_format_port(port, string); } static void print_queue_rate(struct ds *string, const char *name, unsigned int rate) { if (rate <= 1000) { ds_put_format(string, " %s:%u.%u%%", name, rate / 10, rate % 10); } else if (rate < UINT16_MAX) { ds_put_format(string, " %s:(disabled)", name); } } static int compare_queues(const void *a_, const void *b_) { const struct ofputil_queue_config *a = a_; const struct ofputil_queue_config *b = b_; uint32_t aq = a->queue_id; uint32_t bq = b->queue_id; return aq < bq ? -1 : aq > bq; } static void ofp_print_queue_get_config_reply(struct ds *string, const struct ofp_header *oh) { enum ofperr error; struct ofpbuf b; ofp_port_t port; ofpbuf_use_const(&b, oh, ntohs(oh->length)); error = ofputil_decode_queue_get_config_reply(&b, &port); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " port="); ofputil_format_port(port, string); ds_put_char(string, '\n'); struct ofputil_queue_config *queues = NULL; size_t allocated_queues = 0; size_t n = 0; int retval = 0; for (;;) { if (n >= allocated_queues) { queues = x2nrealloc(queues, &allocated_queues, sizeof *queues); } retval = ofputil_pull_queue_get_config_reply(&b, &queues[n]); if (retval) { break; } n++; } qsort(queues, n, sizeof *queues, compare_queues); for (const struct ofputil_queue_config *q = queues; q < &queues[n]; q++) { ds_put_format(string, "queue %"PRIu32":", q->queue_id); print_queue_rate(string, "min_rate", q->min_rate); print_queue_rate(string, "max_rate", q->max_rate); ds_put_char(string, '\n'); } if (retval != EOF) { ofp_print_error(string, retval); } } static void ofp_print_meter_flags(struct ds *s, uint16_t flags) { if (flags & OFPMF13_KBPS) { ds_put_cstr(s, "kbps "); } if (flags & OFPMF13_PKTPS) { ds_put_cstr(s, "pktps "); } if (flags & OFPMF13_BURST) { ds_put_cstr(s, "burst "); } if (flags & OFPMF13_STATS) { ds_put_cstr(s, "stats "); } flags &= ~(OFPMF13_KBPS | OFPMF13_PKTPS | OFPMF13_BURST | OFPMF13_STATS); if (flags) { ds_put_format(s, "flags:0x%"PRIx16" ", flags); } } static void ofp_print_meter_band(struct ds *s, uint16_t flags, const struct ofputil_meter_band *mb) { ds_put_cstr(s, "\ntype="); switch (mb->type) { case OFPMBT13_DROP: ds_put_cstr(s, "drop"); break; case OFPMBT13_DSCP_REMARK: ds_put_cstr(s, "dscp_remark"); break; default: ds_put_format(s, "%u", mb->type); } ds_put_format(s, " rate=%"PRIu32, mb->rate); if (flags & OFPMF13_BURST) { ds_put_format(s, " burst_size=%"PRIu32, mb->burst_size); } if (mb->type == OFPMBT13_DSCP_REMARK) { ds_put_format(s, " prec_level=%"PRIu8, mb->prec_level); } } static void ofp_print_meter_stats(struct ds *s, const struct ofputil_meter_stats *ms) { uint16_t i; ds_put_format(s, "meter:%"PRIu32" ", ms->meter_id); ds_put_format(s, "flow_count:%"PRIu32" ", ms->flow_count); ds_put_format(s, "packet_in_count:%"PRIu64" ", ms->packet_in_count); ds_put_format(s, "byte_in_count:%"PRIu64" ", ms->byte_in_count); ds_put_cstr(s, "duration:"); ofp_print_duration(s, ms->duration_sec, ms->duration_nsec); ds_put_char(s, ' '); ds_put_cstr(s, "bands:\n"); for (i = 0; i < ms->n_bands; ++i) { ds_put_format(s, "%d: ", i); ds_put_format(s, "packet_count:%"PRIu64" ", ms->bands[i].packet_count); ds_put_format(s, "byte_count:%"PRIu64"\n", ms->bands[i].byte_count); } } static void ofp_print_meter_config(struct ds *s, const struct ofputil_meter_config *mc) { uint16_t i; ds_put_format(s, "meter=%"PRIu32" ", mc->meter_id); ofp_print_meter_flags(s, mc->flags); ds_put_cstr(s, "bands="); for (i = 0; i < mc->n_bands; ++i) { ofp_print_meter_band(s, mc->flags, &mc->bands[i]); } ds_put_char(s, '\n'); } static void ofp_print_meter_mod__(struct ds *s, const struct ofputil_meter_mod *mm) { switch (mm->command) { case OFPMC13_ADD: ds_put_cstr(s, " ADD "); break; case OFPMC13_MODIFY: ds_put_cstr(s, " MOD "); break; case OFPMC13_DELETE: ds_put_cstr(s, " DEL "); break; default: ds_put_format(s, " cmd:%d ", mm->command); } ofp_print_meter_config(s, &mm->meter); } static void ofp_print_meter_mod(struct ds *s, const struct ofp_header *oh) { struct ofputil_meter_mod mm; struct ofpbuf bands; enum ofperr error; ofpbuf_init(&bands, 64); error = ofputil_decode_meter_mod(oh, &mm, &bands); if (error) { ofp_print_error(s, error); } else { ofp_print_meter_mod__(s, &mm); } ofpbuf_uninit(&bands); } static void ofp_print_meter_stats_request(struct ds *s, const struct ofp_header *oh) { uint32_t meter_id; ofputil_decode_meter_request(oh, &meter_id); ds_put_format(s, " meter=%"PRIu32, meter_id); } static const char * ofputil_meter_capabilities_to_name(uint32_t bit) { enum ofp13_meter_flags flag = bit; switch (flag) { case OFPMF13_KBPS: return "kbps"; case OFPMF13_PKTPS: return "pktps"; case OFPMF13_BURST: return "burst"; case OFPMF13_STATS: return "stats"; } return NULL; } static const char * ofputil_meter_band_types_to_name(uint32_t bit) { switch (bit) { case 1 << OFPMBT13_DROP: return "drop"; case 1 << OFPMBT13_DSCP_REMARK: return "dscp_remark"; } return NULL; } static void ofp_print_meter_features_reply(struct ds *s, const struct ofp_header *oh) { struct ofputil_meter_features mf; ofputil_decode_meter_features(oh, &mf); ds_put_format(s, "\nmax_meter:%"PRIu32, mf.max_meters); ds_put_format(s, " max_bands:%"PRIu8, mf.max_bands); ds_put_format(s, " max_color:%"PRIu8"\n", mf.max_color); ds_put_cstr(s, "band_types: "); ofp_print_bit_names(s, mf.band_types, ofputil_meter_band_types_to_name, ' '); ds_put_char(s, '\n'); ds_put_cstr(s, "capabilities: "); ofp_print_bit_names(s, mf.capabilities, ofputil_meter_capabilities_to_name, ' '); ds_put_char(s, '\n'); } static void ofp_print_meter_config_reply(struct ds *s, const struct ofp_header *oh) { struct ofpbuf bands; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpbuf_init(&bands, 64); for (;;) { struct ofputil_meter_config mc; int retval; retval = ofputil_decode_meter_config(&b, &mc, &bands); if (retval) { if (retval != EOF) { ofp_print_error(s, retval); } break; } ds_put_char(s, '\n'); ofp_print_meter_config(s, &mc); } ofpbuf_uninit(&bands); } static void ofp_print_meter_stats_reply(struct ds *s, const struct ofp_header *oh) { struct ofpbuf bands; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpbuf_init(&bands, 64); for (;;) { struct ofputil_meter_stats ms; int retval; retval = ofputil_decode_meter_stats(&b, &ms, &bands); if (retval) { if (retval != EOF) { ofp_print_error(s, retval); } break; } ds_put_char(s, '\n'); ofp_print_meter_stats(s, &ms); } ofpbuf_uninit(&bands); } static void ofp_print_error(struct ds *string, enum ofperr error) { if (string->length) { ds_put_char(string, ' '); } ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error)); } static void ofp_print_hello(struct ds *string, const struct ofp_header *oh) { uint32_t allowed_versions; bool ok; ok = ofputil_decode_hello(oh, &allowed_versions); ds_put_cstr(string, "\n version bitmap: "); ofputil_format_version_bitmap(string, allowed_versions); if (!ok) { ds_put_cstr(string, "\n unknown data in hello:\n"); ds_put_hex_dump(string, oh, ntohs(oh->length), 0, true); } } static void ofp_print_error_msg(struct ds *string, const struct ofp_header *oh) { size_t len = ntohs(oh->length); struct ofpbuf payload; enum ofperr error; char *s; error = ofperr_decode_msg(oh, &payload); if (!error) { ds_put_cstr(string, "***decode error***"); ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true); return; } ds_put_format(string, " %s\n", ofperr_get_name(error)); if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) { ds_put_printable(string, payload.data, payload.size); } else { s = ofp_to_string(payload.data, payload.size, 1); ds_put_cstr(string, s); free(s); } ofpbuf_uninit(&payload); } static void ofp_print_port_status(struct ds *string, const struct ofp_header *oh) { struct ofputil_port_status ps; enum ofperr error; error = ofputil_decode_port_status(oh, &ps); if (error) { ofp_print_error(string, error); return; } if (ps.reason == OFPPR_ADD) { ds_put_format(string, " ADD:"); } else if (ps.reason == OFPPR_DELETE) { ds_put_format(string, " DEL:"); } else if (ps.reason == OFPPR_MODIFY) { ds_put_format(string, " MOD:"); } ofp_print_phy_port(string, &ps.desc); } static void ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh) { const struct ofp_desc_stats *ods = ofpmsg_body(oh); ds_put_char(string, '\n'); ds_put_format(string, "Manufacturer: %.*s\n", (int) sizeof ods->mfr_desc, ods->mfr_desc); ds_put_format(string, "Hardware: %.*s\n", (int) sizeof ods->hw_desc, ods->hw_desc); ds_put_format(string, "Software: %.*s\n", (int) sizeof ods->sw_desc, ods->sw_desc); ds_put_format(string, "Serial Num: %.*s\n", (int) sizeof ods->serial_num, ods->serial_num); ds_put_format(string, "DP Description: %.*s\n", (int) sizeof ods->dp_desc, ods->dp_desc); } static void ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh) { struct ofputil_flow_stats_request fsr; enum ofperr error; error = ofputil_decode_flow_stats_request(&fsr, oh); if (error) { ofp_print_error(string, error); return; } if (fsr.table_id != 0xff) { ds_put_format(string, " table=%"PRIu8, fsr.table_id); } if (fsr.out_port != OFPP_ANY) { ds_put_cstr(string, " out_port="); ofputil_format_port(fsr.out_port, string); } ds_put_char(string, ' '); match_format(&fsr.match, string, OFP_DEFAULT_PRIORITY); } void ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs) { ds_put_format(string, " cookie=0x%"PRIx64", duration=", ntohll(fs->cookie)); ofp_print_duration(string, fs->duration_sec, fs->duration_nsec); ds_put_format(string, ", table=%"PRIu8", ", fs->table_id); ds_put_format(string, "n_packets=%"PRIu64", ", fs->packet_count); ds_put_format(string, "n_bytes=%"PRIu64", ", fs->byte_count); if (fs->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(string, "idle_timeout=%"PRIu16", ", fs->idle_timeout); } if (fs->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(string, "hard_timeout=%"PRIu16", ", fs->hard_timeout); } if (fs->flags) { ofp_print_flow_flags(string, fs->flags); } if (fs->importance != 0) { ds_put_format(string, "importance=%"PRIu16", ", fs->importance); } if (fs->idle_age >= 0) { ds_put_format(string, "idle_age=%d, ", fs->idle_age); } if (fs->hard_age >= 0 && fs->hard_age != fs->duration_sec) { ds_put_format(string, "hard_age=%d, ", fs->hard_age); } match_format(&fs->match, string, fs->priority); if (string->string[string->length - 1] != ' ') { ds_put_char(string, ' '); } ds_put_cstr(string, "actions="); ofpacts_format(fs->ofpacts, fs->ofpacts_len, string); } static void ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh) { struct ofpbuf ofpacts; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpbuf_init(&ofpacts, 64); for (;;) { struct ofputil_flow_stats fs; int retval; retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts); if (retval) { if (retval != EOF) { ds_put_cstr(string, " ***parse error***"); } break; } ds_put_char(string, '\n'); ofp_print_flow_stats(string, &fs); } ofpbuf_uninit(&ofpacts); } static void ofp_print_aggregate_stats_reply(struct ds *string, const struct ofp_header *oh) { struct ofputil_aggregate_stats as; enum ofperr error; error = ofputil_decode_aggregate_stats_reply(&as, oh); if (error) { ofp_print_error(string, error); return; } ds_put_format(string, " packet_count=%"PRIu64, as.packet_count); ds_put_format(string, " byte_count=%"PRIu64, as.byte_count); ds_put_format(string, " flow_count=%"PRIu32, as.flow_count); } static void print_port_stat(struct ds *string, const char *leader, uint64_t stat, int more) { ds_put_cstr(string, leader); if (stat != UINT64_MAX) { ds_put_format(string, "%"PRIu64, stat); } else { ds_put_char(string, '?'); } if (more) { ds_put_cstr(string, ", "); } else { ds_put_cstr(string, "\n"); } } static void ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh) { ofp_port_t ofp10_port; enum ofperr error; error = ofputil_decode_port_stats_request(oh, &ofp10_port); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " port_no="); ofputil_format_port(ofp10_port, string); } static void ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh, int verbosity) { struct ofpbuf b; ds_put_format(string, " %"PRIuSIZE" ports\n", ofputil_count_port_stats(oh)); if (verbosity < 1) { return; } ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_port_stats ps; int retval; retval = ofputil_decode_port_stats(&ps, &b); if (retval) { if (retval != EOF) { ds_put_cstr(string, " ***parse error***"); } return; } ds_put_cstr(string, " port "); if (ofp_to_u16(ps.port_no) < 10) { ds_put_char(string, ' '); } ofputil_format_port(ps.port_no, string); ds_put_cstr(string, ": rx "); print_port_stat(string, "pkts=", ps.stats.rx_packets, 1); print_port_stat(string, "bytes=", ps.stats.rx_bytes, 1); print_port_stat(string, "drop=", ps.stats.rx_dropped, 1); print_port_stat(string, "errs=", ps.stats.rx_errors, 1); print_port_stat(string, "frame=", ps.stats.rx_frame_errors, 1); print_port_stat(string, "over=", ps.stats.rx_over_errors, 1); print_port_stat(string, "crc=", ps.stats.rx_crc_errors, 0); ds_put_cstr(string, " tx "); print_port_stat(string, "pkts=", ps.stats.tx_packets, 1); print_port_stat(string, "bytes=", ps.stats.tx_bytes, 1); print_port_stat(string, "drop=", ps.stats.tx_dropped, 1); print_port_stat(string, "errs=", ps.stats.tx_errors, 1); print_port_stat(string, "coll=", ps.stats.collisions, 0); if (ps.duration_sec != UINT32_MAX) { ds_put_cstr(string, " duration="); ofp_print_duration(string, ps.duration_sec, ps.duration_nsec); ds_put_char(string, '\n'); } } } static void ofp_print_table_stats_reply(struct ds *string, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); struct ofputil_table_features prev_features; struct ofputil_table_stats prev_stats; for (int i = 0;; i++) { struct ofputil_table_features features; struct ofputil_table_stats stats; int retval; retval = ofputil_decode_table_stats_reply(&b, &stats, &features); if (retval) { if (retval != EOF) { ofp_print_error(string, retval); } return; } ds_put_char(string, '\n'); ofp_print_table_features(string, &features, i ? &prev_features : NULL, &stats, i ? &prev_stats : NULL); prev_features = features; prev_stats = stats; } } static void ofp_print_queue_name(struct ds *string, uint32_t queue_id) { if (queue_id == OFPQ_ALL) { ds_put_cstr(string, "ALL"); } else { ds_put_format(string, "%"PRIu32, queue_id); } } static void ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh) { struct ofputil_queue_stats_request oqsr; enum ofperr error; error = ofputil_decode_queue_stats_request(oh, &oqsr); if (error) { ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error)); return; } ds_put_cstr(string, "port="); ofputil_format_port(oqsr.port_no, string); ds_put_cstr(string, " queue="); ofp_print_queue_name(string, oqsr.queue_id); } static void ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh, int verbosity) { struct ofpbuf b; ds_put_format(string, " %"PRIuSIZE" queues\n", ofputil_count_queue_stats(oh)); if (verbosity < 1) { return; } ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_queue_stats qs; int retval; retval = ofputil_decode_queue_stats(&qs, &b); if (retval) { if (retval != EOF) { ds_put_cstr(string, " ***parse error***"); } return; } ds_put_cstr(string, " port "); ofputil_format_port(qs.port_no, string); ds_put_cstr(string, " queue "); ofp_print_queue_name(string, qs.queue_id); ds_put_cstr(string, ": "); print_port_stat(string, "bytes=", qs.tx_bytes, 1); print_port_stat(string, "pkts=", qs.tx_packets, 1); print_port_stat(string, "errors=", qs.tx_errors, 1); ds_put_cstr(string, "duration="); if (qs.duration_sec != UINT32_MAX) { ofp_print_duration(string, qs.duration_sec, qs.duration_nsec); } else { ds_put_char(string, '?'); } ds_put_char(string, '\n'); } } static void ofp_print_ofpst_port_desc_request(struct ds *string, const struct ofp_header *oh) { enum ofperr error; ofp_port_t port; error = ofputil_decode_port_desc_stats_request(oh, &port); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " port="); ofputil_format_port(port, string); } static void ofp_print_ofpst_port_desc_reply(struct ds *string, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpraw_pull_assert(&b); ds_put_char(string, '\n'); ofp_print_phy_ports(string, oh->version, &b); } static void ofp_print_stats(struct ds *string, const struct ofp_header *oh) { uint16_t flags = ofpmp_flags(oh); if (flags) { ds_put_cstr(string, " flags="); if ((!ofpmsg_is_stat_request(oh) || oh->version >= OFP13_VERSION) && (flags & OFPSF_REPLY_MORE)) { ds_put_cstr(string, "[more]"); flags &= ~OFPSF_REPLY_MORE; } if (flags) { ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", flags); } } } static void ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity) { size_t len = ntohs(oh->length); ds_put_format(string, " %"PRIuSIZE" bytes of payload\n", len - sizeof *oh); if (verbosity > 1) { ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true); } } static void ofp_print_role_generic(struct ds *string, enum ofp12_controller_role role, uint64_t generation_id) { ds_put_cstr(string, " role="); switch (role) { case OFPCR12_ROLE_NOCHANGE: ds_put_cstr(string, "nochange"); break; case OFPCR12_ROLE_EQUAL: ds_put_cstr(string, "equal"); /* OF 1.2 wording */ break; case OFPCR12_ROLE_MASTER: ds_put_cstr(string, "master"); break; case OFPCR12_ROLE_SLAVE: ds_put_cstr(string, "slave"); break; default: OVS_NOT_REACHED(); } if (generation_id != UINT64_MAX) { ds_put_format(string, " generation_id=%"PRIu64, generation_id); } } static void ofp_print_role_message(struct ds *string, const struct ofp_header *oh) { struct ofputil_role_request rr; enum ofperr error; error = ofputil_decode_role_message(oh, &rr); if (error) { ofp_print_error(string, error); return; } ofp_print_role_generic(string, rr.role, rr.have_generation_id ? rr.generation_id : UINT64_MAX); } static void ofp_print_role_status_message(struct ds *string, const struct ofp_header *oh) { struct ofputil_role_status rs; enum ofperr error; error = ofputil_decode_role_status(oh, &rs); if (error) { ofp_print_error(string, error); return; } ofp_print_role_generic(string, rs.role, rs.generation_id); ds_put_cstr(string, " reason="); switch (rs.reason) { case OFPCRR_MASTER_REQUEST: ds_put_cstr(string, "master_request"); break; case OFPCRR_CONFIG: ds_put_cstr(string, "configuration_changed"); break; case OFPCRR_EXPERIMENTER: ds_put_cstr(string, "experimenter_data_changed"); break; case OFPCRR_N_REASONS: default: ds_put_cstr(string, "(unknown)"); break; } } static void ofp_print_nxt_flow_mod_table_id(struct ds *string, const struct nx_flow_mod_table_id *nfmti) { ds_put_format(string, " %s", nfmti->set ? "enable" : "disable"); } static void ofp_print_nxt_set_flow_format(struct ds *string, const struct nx_set_flow_format *nsff) { uint32_t format = ntohl(nsff->format); ds_put_cstr(string, " format="); if (ofputil_nx_flow_format_is_valid(format)) { ds_put_cstr(string, ofputil_nx_flow_format_to_string(format)); } else { ds_put_format(string, "%"PRIu32, format); } } static void ofp_print_nxt_set_packet_in_format(struct ds *string, const struct nx_set_packet_in_format *nspf) { uint32_t format = ntohl(nspf->format); ds_put_cstr(string, " format="); if (ofputil_packet_in_format_is_valid(format)) { ds_put_cstr(string, ofputil_packet_in_format_to_string(format)); } else { ds_put_format(string, "%"PRIu32, format); } } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFP_PORT_REASON_BUFSIZE. */ #define OFP_PORT_REASON_BUFSIZE (INT_STRLEN(int) + 1) static const char * ofp_port_reason_to_string(enum ofp_port_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPPR_ADD: return "add"; case OFPPR_DELETE: return "delete"; case OFPPR_MODIFY: return "modify"; case OFPPR_N_REASONS: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ static const char* ofp_role_reason_to_string(enum ofp14_controller_role_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPCRR_MASTER_REQUEST: return "master_request"; case OFPCRR_CONFIG: return "configuration_changed"; case OFPCRR_EXPERIMENTER: return "experimenter_data_changed"; case OFPCRR_N_REASONS: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ static const char* ofp_table_reason_to_string(enum ofp14_table_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPTR_VACANCY_DOWN: return "vacancy_down"; case OFPTR_VACANCY_UP: return "vacancy_up"; case OFPTR_N_REASONS: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } /* Returns a string form of 'reason'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'. * 'bufsize' should be at least OFP_ASYNC_CONFIG_REASON_BUFSIZE. */ static const char* ofp_requestforward_reason_to_string(enum ofp14_requestforward_reason reason, char *reasonbuf, size_t bufsize) { switch (reason) { case OFPRFR_GROUP_MOD: return "group_mod_request"; case OFPRFR_METER_MOD: return "meter_mod_request"; case OFPRFR_N_REASONS: default: snprintf(reasonbuf, bufsize, "%d", (int) reason); return reasonbuf; } } static const char * ofp_async_config_reason_to_string(uint32_t reason, enum ofputil_async_msg_type type, char *reasonbuf, size_t bufsize) { switch (type) { case OAM_PACKET_IN: return ofputil_packet_in_reason_to_string(reason, reasonbuf, bufsize); case OAM_PORT_STATUS: return ofp_port_reason_to_string(reason, reasonbuf, bufsize); case OAM_FLOW_REMOVED: return ofp_flow_removed_reason_to_string(reason, reasonbuf, bufsize); case OAM_ROLE_STATUS: return ofp_role_reason_to_string(reason, reasonbuf, bufsize); case OAM_TABLE_STATUS: return ofp_table_reason_to_string(reason, reasonbuf, bufsize); case OAM_REQUESTFORWARD: return ofp_requestforward_reason_to_string(reason, reasonbuf, bufsize); case OAM_N_TYPES: default: return "Unknown asynchronous configuration message type"; } } #define OFP_ASYNC_CONFIG_REASON_BUFSIZE (INT_STRLEN(int) + 1) static void ofp_print_nxt_set_async_config(struct ds *string, const struct ofp_header *oh) { int i, j; enum ofpraw raw; ofpraw_decode(&raw, oh); if (raw == OFPRAW_OFPT13_SET_ASYNC || raw == OFPRAW_NXT_SET_ASYNC_CONFIG || raw == OFPRAW_OFPT13_GET_ASYNC_REPLY) { const struct nx_async_config *nac = ofpmsg_body(oh); for (i = 0; i < 2; i++) { ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); ds_put_cstr(string, " PACKET_IN:"); for (j = 0; j < 32; j++) { if (nac->packet_in_mask[i] & htonl(1u << j)) { char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; const char *reason; reason = ofputil_packet_in_reason_to_string(j, reasonbuf, sizeof reasonbuf); ds_put_format(string, " %s", reason); } } if (!nac->packet_in_mask[i]) { ds_put_cstr(string, " (off)"); } ds_put_char(string, '\n'); ds_put_cstr(string, " PORT_STATUS:"); for (j = 0; j < 32; j++) { if (nac->port_status_mask[i] & htonl(1u << j)) { char reasonbuf[OFP_PORT_REASON_BUFSIZE]; const char *reason; reason = ofp_port_reason_to_string(j, reasonbuf, sizeof reasonbuf); ds_put_format(string, " %s", reason); } } if (!nac->port_status_mask[i]) { ds_put_cstr(string, " (off)"); } ds_put_char(string, '\n'); ds_put_cstr(string, " FLOW_REMOVED:"); for (j = 0; j < 32; j++) { if (nac->flow_removed_mask[i] & htonl(1u << j)) { char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; const char *reason; reason = ofp_flow_removed_reason_to_string(j, reasonbuf, sizeof reasonbuf); ds_put_format(string, " %s", reason); } } if (!nac->flow_removed_mask[i]) { ds_put_cstr(string, " (off)"); } ds_put_char(string, '\n'); } } else if (raw == OFPRAW_OFPT14_SET_ASYNC || raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { enum ofperr error = 0; uint32_t role[2][OAM_N_TYPES] = {{0}}; uint32_t type; if (raw == OFPRAW_OFPT14_GET_ASYNC_REPLY) { error = ofputil_decode_set_async_config(oh, role[0], role[1], true); } else if (raw == OFPRAW_OFPT14_SET_ASYNC) { error = ofputil_decode_set_async_config(oh, role[0], role[1], false); } if (error) { ofp_print_error(string, error); return; } for (i = 0; i < 2; i++) { ds_put_format(string, "\n %s:\n", i == 0 ? "master" : "slave"); for (type = 0; type < OAM_N_TYPES; type++) { switch (type) { case OAM_PACKET_IN: ds_put_cstr(string, " PACKET_IN:"); break; case OAM_PORT_STATUS: ds_put_cstr(string, " PORT_STATUS:"); break; case OAM_FLOW_REMOVED: ds_put_cstr(string, " FLOW_REMOVED:"); break; case OAM_ROLE_STATUS: ds_put_cstr(string, " ROLE_STATUS:"); break; case OAM_TABLE_STATUS: ds_put_cstr(string, " TABLE_STATUS:"); break; case OAM_REQUESTFORWARD: ds_put_cstr(string, " REQUESTFORWARD:"); break; } for (j = 0; j < 32; j++) { if (role[i][type] & (1u << j)) { char reasonbuf[OFP_ASYNC_CONFIG_REASON_BUFSIZE]; const char *reason; reason = ofp_async_config_reason_to_string(j, type, reasonbuf, sizeof reasonbuf); ds_put_format(string, " %s", reason); } } if (!role[i][type]) { ds_put_cstr(string, " (off)"); } ds_put_char(string, '\n'); } } } } static void ofp_print_nxt_set_controller_id(struct ds *string, const struct nx_controller_id *nci) { ds_put_format(string, " id=%"PRIu16, ntohs(nci->controller_id)); } static void ofp_print_nxt_flow_monitor_cancel(struct ds *string, const struct ofp_header *oh) { ds_put_format(string, " id=%"PRIu32, ofputil_decode_flow_monitor_cancel(oh)); } static const char * nx_flow_monitor_flags_to_name(uint32_t bit) { enum nx_flow_monitor_flags fmf = bit; switch (fmf) { case NXFMF_INITIAL: return "initial"; case NXFMF_ADD: return "add"; case NXFMF_DELETE: return "delete"; case NXFMF_MODIFY: return "modify"; case NXFMF_ACTIONS: return "actions"; case NXFMF_OWN: return "own"; } return NULL; } static void ofp_print_nxst_flow_monitor_request(struct ds *string, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_flow_monitor_request request; int retval; retval = ofputil_decode_flow_monitor_request(&request, &b); if (retval) { if (retval != EOF) { ofp_print_error(string, retval); } return; } ds_put_format(string, "\n id=%"PRIu32" flags=", request.id); ofp_print_bit_names(string, request.flags, nx_flow_monitor_flags_to_name, ','); if (request.out_port != OFPP_NONE) { ds_put_cstr(string, " out_port="); ofputil_format_port(request.out_port, string); } if (request.table_id != 0xff) { ds_put_format(string, " table=%"PRIu8, request.table_id); } ds_put_char(string, ' '); match_format(&request.match, string, OFP_DEFAULT_PRIORITY); ds_chomp(string, ' '); } } static void ofp_print_nxst_flow_monitor_reply(struct ds *string, const struct ofp_header *oh) { uint64_t ofpacts_stub[1024 / 8]; struct ofpbuf ofpacts; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); for (;;) { char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE]; struct ofputil_flow_update update; struct match match; int retval; update.match = &match; retval = ofputil_decode_flow_update(&update, &b, &ofpacts); if (retval) { if (retval != EOF) { ofp_print_error(string, retval); } ofpbuf_uninit(&ofpacts); return; } ds_put_cstr(string, "\n event="); switch (update.event) { case NXFME_ADDED: ds_put_cstr(string, "ADDED"); break; case NXFME_DELETED: ds_put_format(string, "DELETED reason=%s", ofp_flow_removed_reason_to_string(update.reason, reasonbuf, sizeof reasonbuf)); break; case NXFME_MODIFIED: ds_put_cstr(string, "MODIFIED"); break; case NXFME_ABBREV: ds_put_format(string, "ABBREV xid=0x%"PRIx32, ntohl(update.xid)); continue; } ds_put_format(string, " table=%"PRIu8, update.table_id); if (update.idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(string, " idle_timeout=%"PRIu16, update.idle_timeout); } if (update.hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(string, " hard_timeout=%"PRIu16, update.hard_timeout); } ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie)); ds_put_char(string, ' '); match_format(update.match, string, OFP_DEFAULT_PRIORITY); if (update.ofpacts_len) { if (string->string[string->length - 1] != ' ') { ds_put_char(string, ' '); } ds_put_cstr(string, "actions="); ofpacts_format(update.ofpacts, update.ofpacts_len, string); } } } void ofp_print_version(const struct ofp_header *oh, struct ds *string) { switch (oh->version) { case OFP10_VERSION: break; case OFP11_VERSION: ds_put_cstr(string, " (OF1.1)"); break; case OFP12_VERSION: ds_put_cstr(string, " (OF1.2)"); break; case OFP13_VERSION: ds_put_cstr(string, " (OF1.3)"); break; case OFP14_VERSION: ds_put_cstr(string, " (OF1.4)"); break; case OFP15_VERSION: ds_put_cstr(string, " (OF1.5)"); break; default: ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version); break; } ds_put_format(string, " (xid=0x%"PRIx32"):", ntohl(oh->xid)); } static void ofp_header_to_string__(const struct ofp_header *oh, enum ofpraw raw, struct ds *string) { ds_put_cstr(string, ofpraw_get_name(raw)); ofp_print_version(oh, string); } static void ofp_print_bucket_id(struct ds *s, const char *label, uint32_t bucket_id, enum ofp_version ofp_version) { if (ofp_version < OFP15_VERSION) { return; } ds_put_cstr(s, label); switch (bucket_id) { case OFPG15_BUCKET_FIRST: ds_put_cstr(s, "first"); break; case OFPG15_BUCKET_LAST: ds_put_cstr(s, "last"); break; case OFPG15_BUCKET_ALL: ds_put_cstr(s, "all"); break; default: ds_put_format(s, "%"PRIu32, bucket_id); break; } ds_put_char(s, ','); } static void ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type, const struct ovs_list *p_buckets, const struct ofputil_group_props *props, enum ofp_version ofp_version, bool suppress_type) { struct ofputil_bucket *bucket; ds_put_format(s, "group_id=%"PRIu32, group_id); if (!suppress_type) { static const char *type_str[] = { "all", "select", "indirect", "ff", "unknown" }; ds_put_format(s, ",type=%s", type_str[type > 4 ? 4 : type]); } if (props->selection_method[0]) { ds_put_format(s, ",selection_method=%s", props->selection_method); if (props->selection_method_param) { ds_put_format(s, ",selection_method_param=%"PRIu64, props->selection_method_param); } size_t n = bitmap_count1(props->fields.used.bm, MFF_N_IDS); if (n == 1) { ds_put_cstr(s, ",fields="); oxm_format_field_array(s, &props->fields); } else if (n > 1) { ds_put_cstr(s, ",fields("); oxm_format_field_array(s, &props->fields); ds_put_char(s, ')'); } } if (!p_buckets) { return; } ds_put_char(s, ','); LIST_FOR_EACH (bucket, list_node, p_buckets) { ds_put_cstr(s, "bucket="); ofp_print_bucket_id(s, "bucket_id:", bucket->bucket_id, ofp_version); if (bucket->weight != (type == OFPGT11_SELECT ? 1 : 0)) { ds_put_format(s, "weight:%"PRIu16",", bucket->weight); } if (bucket->watch_port != OFPP_NONE) { ds_put_format(s, "watch_port:%"PRIu32",", bucket->watch_port); } if (bucket->watch_group != OFPG_ANY) { ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group); } ds_put_cstr(s, "actions="); ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, s); ds_put_char(s, ','); } ds_chomp(s, ','); } static void ofp_print_ofpst_group_desc_request(struct ds *string, const struct ofp_header *oh) { uint32_t group_id = ofputil_decode_group_desc_request(oh); ds_put_cstr(string, " group_id="); ofputil_format_group(group_id, string); } static void ofp_print_group_desc(struct ds *s, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_group_desc gd; int retval; retval = ofputil_decode_group_desc_reply(&gd, &b, oh->version); if (retval) { if (retval != EOF) { ds_put_cstr(s, " ***parse error***"); } break; } ds_put_char(s, '\n'); ds_put_char(s, ' '); ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props, oh->version, false); ofputil_bucket_list_destroy(&gd.buckets); } } static void ofp_print_ofpst_group_request(struct ds *string, const struct ofp_header *oh) { enum ofperr error; uint32_t group_id; error = ofputil_decode_group_stats_request(oh, &group_id); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " group_id="); ofputil_format_group(group_id, string); } static void ofp_print_group_stats(struct ds *s, const struct ofp_header *oh) { struct ofpbuf b; uint32_t bucket_i; ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_group_stats gs; int retval; retval = ofputil_decode_group_stats_reply(&b, &gs); if (retval) { if (retval != EOF) { ds_put_cstr(s, " ***parse error***"); } break; } ds_put_char(s, '\n'); ds_put_char(s, ' '); ds_put_format(s, "group_id=%"PRIu32",", gs.group_id); if (gs.duration_sec != UINT32_MAX) { ds_put_cstr(s, "duration="); ofp_print_duration(s, gs.duration_sec, gs.duration_nsec); ds_put_char(s, ','); } ds_put_format(s, "ref_count=%"PRIu32",", gs.ref_count); ds_put_format(s, "packet_count=%"PRIu64",", gs.packet_count); ds_put_format(s, "byte_count=%"PRIu64"", gs.byte_count); for (bucket_i = 0; bucket_i < gs.n_buckets; bucket_i++) { if (gs.bucket_stats[bucket_i].packet_count != UINT64_MAX) { ds_put_format(s, ",bucket%"PRIu32":", bucket_i); ds_put_format(s, "packet_count=%"PRIu64",", gs.bucket_stats[bucket_i].packet_count); ds_put_format(s, "byte_count=%"PRIu64"", gs.bucket_stats[bucket_i].byte_count); } } free(gs.bucket_stats); } } static const char * group_type_to_string(enum ofp11_group_type type) { switch (type) { case OFPGT11_ALL: return "all"; case OFPGT11_SELECT: return "select"; case OFPGT11_INDIRECT: return "indirect"; case OFPGT11_FF: return "fast failover"; default: OVS_NOT_REACHED(); } } static void ofp_print_group_features(struct ds *string, const struct ofp_header *oh) { struct ofputil_group_features features; int i; ofputil_decode_group_features_reply(oh, &features); ds_put_format(string, "\n Group table:\n"); ds_put_format(string, " Types: 0x%"PRIx32"\n", features.types); ds_put_format(string, " Capabilities: 0x%"PRIx32"\n", features.capabilities); for (i = 0; i < OFPGT12_N_TYPES; i++) { if (features.types & (1u << i)) { ds_put_format(string, " %s group:\n", group_type_to_string(i)); ds_put_format(string, " max_groups=%#"PRIx32"\n", features.max_groups[i]); ds_put_format(string, " actions: "); ofpact_bitmap_format(features.ofpacts[i], string); ds_put_char(string, '\n'); } } } static void ofp_print_group_mod__(struct ds *s, enum ofp_version ofp_version, const struct ofputil_group_mod *gm) { bool bucket_command = false; ds_put_char(s, '\n'); ds_put_char(s, ' '); switch (gm->command) { case OFPGC11_ADD: ds_put_cstr(s, "ADD"); break; case OFPGC11_MODIFY: ds_put_cstr(s, "MOD"); break; case OFPGC11_DELETE: ds_put_cstr(s, "DEL"); break; case OFPGC15_INSERT_BUCKET: ds_put_cstr(s, "INSERT_BUCKET"); bucket_command = true; break; case OFPGC15_REMOVE_BUCKET: ds_put_cstr(s, "REMOVE_BUCKET"); bucket_command = true; break; default: ds_put_format(s, "cmd:%"PRIu16"", gm->command); } ds_put_char(s, ' '); if (bucket_command) { ofp_print_bucket_id(s, "command_bucket_id:", gm->command_bucket_id, ofp_version); } ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props, ofp_version, bucket_command); } static void ofp_print_group_mod(struct ds *s, const struct ofp_header *oh) { struct ofputil_group_mod gm; int error; error = ofputil_decode_group_mod(oh, &gm); if (error) { ofp_print_error(s, error); return; } ofp_print_group_mod__(s, oh->version, &gm); ofputil_bucket_list_destroy(&gm.buckets); } static void print_table_action_features(struct ds *s, const struct ofputil_table_action_features *taf) { if (taf->ofpacts) { ds_put_cstr(s, " actions: "); ofpact_bitmap_format(taf->ofpacts, s); ds_put_char(s, '\n'); } if (!bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS)) { int i; ds_put_cstr(s, " supported on Set-Field:"); BITMAP_FOR_EACH_1 (i, MFF_N_IDS, taf->set_fields.bm) { ds_put_format(s, " %s", mf_from_id(i)->name); } ds_put_char(s, '\n'); } } static bool table_action_features_equal(const struct ofputil_table_action_features *a, const struct ofputil_table_action_features *b) { return (a->ofpacts == b->ofpacts && bitmap_equal(a->set_fields.bm, b->set_fields.bm, MFF_N_IDS)); } static bool table_action_features_empty(const struct ofputil_table_action_features *taf) { return !taf->ofpacts && bitmap_is_all_zeros(taf->set_fields.bm, MFF_N_IDS); } static void print_table_instruction_features( struct ds *s, const struct ofputil_table_instruction_features *tif, const struct ofputil_table_instruction_features *prev_tif) { int start, end; if (!bitmap_is_all_zeros(tif->next, 255)) { ds_put_cstr(s, " next tables: "); for (start = bitmap_scan(tif->next, 1, 0, 255); start < 255; start = bitmap_scan(tif->next, 1, end, 255)) { end = bitmap_scan(tif->next, 0, start + 1, 255); if (end == start + 1) { ds_put_format(s, "%d,", start); } else { ds_put_format(s, "%d-%d,", start, end - 1); } } ds_chomp(s, ','); if (ds_last(s) == ' ') { ds_put_cstr(s, "none"); } ds_put_char(s, '\n'); } if (tif->instructions) { if (prev_tif && tif->instructions == prev_tif->instructions) { ds_put_cstr(s, " (same instructions)\n"); } else { ds_put_cstr(s, " instructions: "); int i; for (i = 0; i < 32; i++) { if (tif->instructions & (1u << i)) { const char *name = ovs_instruction_name_from_type(i); if (name) { ds_put_cstr(s, name); } else { ds_put_format(s, "%d", i); } ds_put_char(s, ','); } } ds_chomp(s, ','); ds_put_char(s, '\n'); } } if (prev_tif && table_action_features_equal(&tif->write, &prev_tif->write) && table_action_features_equal(&tif->apply, &prev_tif->apply) && !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) { ds_put_cstr(s, " (same actions)\n"); } else if (!table_action_features_equal(&tif->write, &tif->apply)) { ds_put_cstr(s, " Write-Actions features:\n"); print_table_action_features(s, &tif->write); ds_put_cstr(s, " Apply-Actions features:\n"); print_table_action_features(s, &tif->apply); } else if (tif->write.ofpacts || !bitmap_is_all_zeros(tif->write.set_fields.bm, MFF_N_IDS)) { ds_put_cstr(s, " Write-Actions and Apply-Actions features:\n"); print_table_action_features(s, &tif->write); } } static bool table_instruction_features_equal( const struct ofputil_table_instruction_features *a, const struct ofputil_table_instruction_features *b) { return (bitmap_equal(a->next, b->next, 255) && a->instructions == b->instructions && table_action_features_equal(&a->write, &b->write) && table_action_features_equal(&a->apply, &b->apply)); } static bool table_instruction_features_empty( const struct ofputil_table_instruction_features *tif) { return (bitmap_is_all_zeros(tif->next, 255) && !tif->instructions && table_action_features_empty(&tif->write) && table_action_features_empty(&tif->apply)); } static bool table_features_equal(const struct ofputil_table_features *a, const struct ofputil_table_features *b) { return (a->metadata_match == b->metadata_match && a->metadata_write == b->metadata_write && a->miss_config == b->miss_config && a->supports_eviction == b->supports_eviction && a->supports_vacancy_events == b->supports_vacancy_events && a->max_entries == b->max_entries && table_instruction_features_equal(&a->nonmiss, &b->nonmiss) && table_instruction_features_equal(&a->miss, &b->miss) && bitmap_equal(a->match.bm, b->match.bm, MFF_N_IDS)); } static bool table_features_empty(const struct ofputil_table_features *tf) { return (!tf->metadata_match && !tf->metadata_write && tf->miss_config == OFPUTIL_TABLE_MISS_DEFAULT && tf->supports_eviction < 0 && tf->supports_vacancy_events < 0 && !tf->max_entries && table_instruction_features_empty(&tf->nonmiss) && table_instruction_features_empty(&tf->miss) && bitmap_is_all_zeros(tf->match.bm, MFF_N_IDS)); } static bool table_stats_equal(const struct ofputil_table_stats *a, const struct ofputil_table_stats *b) { return (a->active_count == b->active_count && a->lookup_count == b->lookup_count && a->matched_count == b->matched_count); } void ofp_print_table_features(struct ds *s, const struct ofputil_table_features *features, const struct ofputil_table_features *prev_features, const struct ofputil_table_stats *stats, const struct ofputil_table_stats *prev_stats) { int i; ds_put_format(s, " table %"PRIu8, features->table_id); if (features->name[0]) { ds_put_format(s, " (\"%s\")", features->name); } ds_put_char(s, ':'); bool same_stats = prev_stats && table_stats_equal(stats, prev_stats); bool same_features = prev_features && table_features_equal(features, prev_features); if ((!stats || same_stats) && same_features) { ds_put_cstr(s, " ditto"); return; } ds_put_char(s, '\n'); if (stats) { ds_put_format(s, " active=%"PRIu32", ", stats->active_count); ds_put_format(s, "lookup=%"PRIu64", ", stats->lookup_count); ds_put_format(s, "matched=%"PRIu64"\n", stats->matched_count); } if (same_features) { if (!table_features_empty(features)) { ds_put_cstr(s, " (same features)\n"); } return; } if (features->metadata_match || features->metadata_write) { ds_put_format(s, " metadata: match=%#"PRIx64" write=%#"PRIx64"\n", ntohll(features->metadata_match), ntohll(features->metadata_write)); } if (features->miss_config != OFPUTIL_TABLE_MISS_DEFAULT) { ds_put_format(s, " config=%s\n", ofputil_table_miss_to_string(features->miss_config)); } if (features->supports_eviction >= 0) { ds_put_format(s, " eviction: %ssupported\n", features->supports_eviction ? "" : "not "); } if (features->supports_vacancy_events >= 0) { ds_put_format(s, " vacancy events: %ssupported\n", features->supports_vacancy_events ? "" : "not "); } if (features->max_entries) { ds_put_format(s, " max_entries=%"PRIu32"\n", features->max_entries); } const struct ofputil_table_instruction_features *prev_nonmiss = prev_features ? &prev_features->nonmiss : NULL; const struct ofputil_table_instruction_features *prev_miss = prev_features ? &prev_features->miss : NULL; if (prev_features && table_instruction_features_equal(&features->nonmiss, prev_nonmiss) && table_instruction_features_equal(&features->miss, prev_miss)) { if (!table_instruction_features_empty(&features->nonmiss)) { ds_put_cstr(s, " (same instructions)\n"); } } else if (!table_instruction_features_equal(&features->nonmiss, &features->miss)) { ds_put_cstr(s, " instructions (other than table miss):\n"); print_table_instruction_features(s, &features->nonmiss, prev_nonmiss); ds_put_cstr(s, " instructions (table miss):\n"); print_table_instruction_features(s, &features->miss, prev_miss); } else if (!table_instruction_features_empty(&features->nonmiss)) { ds_put_cstr(s, " instructions (table miss and others):\n"); print_table_instruction_features(s, &features->nonmiss, prev_nonmiss); } if (!bitmap_is_all_zeros(features->match.bm, MFF_N_IDS)) { if (prev_features && bitmap_equal(features->match.bm, prev_features->match.bm, MFF_N_IDS)) { ds_put_cstr(s, " (same matching)\n"); } else { ds_put_cstr(s, " matching:\n"); BITMAP_FOR_EACH_1 (i, MFF_N_IDS, features->match.bm) { const struct mf_field *f = mf_from_id(i); bool mask = bitmap_is_set(features->mask.bm, i); bool wildcard = bitmap_is_set(features->wildcard.bm, i); ds_put_format(s, " %s: %s\n", f->name, (mask ? "arbitrary mask" : wildcard ? "exact match or wildcard" : "must exact match")); } } } } static void ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); struct ofputil_table_features prev; for (int i = 0; ; i++) { struct ofputil_table_features tf; int retval; retval = ofputil_decode_table_features(&b, &tf, true); if (retval) { if (retval != EOF) { ofp_print_error(s, retval); } return; } ds_put_char(s, '\n'); ofp_print_table_features(s, &tf, i ? &prev : NULL, NULL, NULL); prev = tf; } } static void ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh) { struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); for (;;) { struct ofputil_table_desc td; int retval; retval = ofputil_decode_table_desc(&b, &td, oh->version); if (retval) { if (retval != EOF) { ofp_print_error(s, retval); } return; } ofp_print_table_desc(s, &td); } } static const char * bundle_flags_to_name(uint32_t bit) { switch (bit) { case OFPBF_ATOMIC: return "atomic"; case OFPBF_ORDERED: return "ordered"; default: return NULL; } } static void ofp_print_bundle_ctrl(struct ds *s, const struct ofp_header *oh) { int error; struct ofputil_bundle_ctrl_msg bctrl; error = ofputil_decode_bundle_ctrl(oh, &bctrl); if (error) { ofp_print_error(s, error); return; } ds_put_char(s, '\n'); ds_put_format(s, " bundle_id=%#"PRIx32" type=", bctrl.bundle_id); switch (bctrl.type) { case OFPBCT_OPEN_REQUEST: ds_put_cstr(s, "OPEN_REQUEST"); break; case OFPBCT_OPEN_REPLY: ds_put_cstr(s, "OPEN_REPLY"); break; case OFPBCT_CLOSE_REQUEST: ds_put_cstr(s, "CLOSE_REQUEST"); break; case OFPBCT_CLOSE_REPLY: ds_put_cstr(s, "CLOSE_REPLY"); break; case OFPBCT_COMMIT_REQUEST: ds_put_cstr(s, "COMMIT_REQUEST"); break; case OFPBCT_COMMIT_REPLY: ds_put_cstr(s, "COMMIT_REPLY"); break; case OFPBCT_DISCARD_REQUEST: ds_put_cstr(s, "DISCARD_REQUEST"); break; case OFPBCT_DISCARD_REPLY: ds_put_cstr(s, "DISCARD_REPLY"); break; } ds_put_cstr(s, " flags="); ofp_print_bit_names(s, bctrl.flags, bundle_flags_to_name, ' '); } static void ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh, int verbosity) { int error; struct ofputil_bundle_add_msg badd; error = ofputil_decode_bundle_add(oh, &badd, NULL); if (error) { ofp_print_error(s, error); return; } ds_put_char(s, '\n'); ds_put_format(s, " bundle_id=%#"PRIx32, badd.bundle_id); ds_put_cstr(s, " flags="); ofp_print_bit_names(s, badd.flags, bundle_flags_to_name, ' '); ds_put_char(s, '\n'); char *msg = ofp_to_string(badd.msg, ntohs(badd.msg->length), verbosity); ds_put_and_free_cstr(s, msg); } static void print_tlv_table(struct ds *s, struct ovs_list *mappings) { struct ofputil_tlv_map *map; ds_put_cstr(s, " mapping table:\n"); ds_put_cstr(s, " class\ttype\tlength\tmatch field\n"); ds_put_cstr(s, " -----\t----\t------\t-----------"); LIST_FOR_EACH (map, list_node, mappings) { ds_put_char(s, '\n'); ds_put_format(s, " 0x%"PRIx16"\t0x%"PRIx8"\t%"PRIu8"\ttun_metadata%"PRIu16, map->option_class, map->option_type, map->option_len, map->index); } } static void ofp_print_tlv_table_mod(struct ds *s, const struct ofp_header *oh) { int error; struct ofputil_tlv_table_mod ttm; error = ofputil_decode_tlv_table_mod(oh, &ttm); if (error) { ofp_print_error(s, error); return; } ds_put_cstr(s, "\n "); switch (ttm.command) { case NXTTMC_ADD: ds_put_cstr(s, "ADD"); break; case NXTTMC_DELETE: ds_put_cstr(s, "DEL"); break; case NXTTMC_CLEAR: ds_put_cstr(s, "CLEAR"); break; } if (ttm.command != NXTTMC_CLEAR) { print_tlv_table(s, &ttm.mappings); } ofputil_uninit_tlv_table(&ttm.mappings); } static void ofp_print_tlv_table_reply(struct ds *s, const struct ofp_header *oh) { int error; struct ofputil_tlv_table_reply ttr; struct ofputil_tlv_map *map; int allocated_space = 0; error = ofputil_decode_tlv_table_reply(oh, &ttr); if (error) { ofp_print_error(s, error); return; } ds_put_char(s, '\n'); LIST_FOR_EACH (map, list_node, &ttr.mappings) { allocated_space += map->option_len; } ds_put_format(s, " max option space=%"PRIu32" max fields=%"PRIu16"\n", ttr.max_option_space, ttr.max_fields); ds_put_format(s, " allocated option space=%d\n", allocated_space); ds_put_char(s, '\n'); print_tlv_table(s, &ttr.mappings); ofputil_uninit_tlv_table(&ttr.mappings); } /* This function will print the request forward message. The reason for * request forward is taken from rf.request.type */ static void ofp_print_requestforward(struct ds *string, const struct ofp_header *oh) { struct ofputil_requestforward rf; enum ofperr error; error = ofputil_decode_requestforward(oh, &rf); if (error) { ofp_print_error(string, error); return; } ds_put_cstr(string, " reason="); switch (rf.reason) { case OFPRFR_GROUP_MOD: ds_put_cstr(string, "group_mod"); ofp_print_group_mod__(string, oh->version, rf.group_mod); break; case OFPRFR_METER_MOD: ds_put_cstr(string, "meter_mod"); ofp_print_meter_mod__(string, rf.meter_mod); break; case OFPRFR_N_REASONS: OVS_NOT_REACHED(); } ofputil_destroy_requestforward(&rf); } static void ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw, struct ds *string, int verbosity) { const void *msg = oh; ofp_header_to_string__(oh, raw, string); switch (ofptype_from_ofpraw(raw)) { case OFPTYPE_GROUP_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_ofpst_group_request(string, oh); break; case OFPTYPE_GROUP_STATS_REPLY: ofp_print_group_stats(string, oh); break; case OFPTYPE_GROUP_DESC_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_ofpst_group_desc_request(string, oh); break; case OFPTYPE_GROUP_DESC_STATS_REPLY: ofp_print_group_desc(string, oh); break; case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: ofp_print_stats(string, oh); break; case OFPTYPE_GROUP_FEATURES_STATS_REPLY: ofp_print_group_features(string, oh); break; case OFPTYPE_GROUP_MOD: ofp_print_group_mod(string, oh); break; case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: ofp_print_table_features_reply(string, oh); break; case OFPTYPE_TABLE_DESC_REQUEST: case OFPTYPE_TABLE_DESC_REPLY: ofp_print_table_desc_reply(string, oh); break; case OFPTYPE_HELLO: ofp_print_hello(string, oh); break; case OFPTYPE_ERROR: ofp_print_error_msg(string, oh); break; case OFPTYPE_ECHO_REQUEST: case OFPTYPE_ECHO_REPLY: ofp_print_echo(string, oh, verbosity); break; case OFPTYPE_FEATURES_REQUEST: break; case OFPTYPE_FEATURES_REPLY: ofp_print_switch_features(string, oh); break; case OFPTYPE_GET_CONFIG_REQUEST: break; case OFPTYPE_GET_CONFIG_REPLY: case OFPTYPE_SET_CONFIG: ofp_print_switch_config(string, ofpmsg_body(oh)); break; case OFPTYPE_PACKET_IN: ofp_print_packet_in(string, oh, verbosity); break; case OFPTYPE_FLOW_REMOVED: ofp_print_flow_removed(string, oh); break; case OFPTYPE_PORT_STATUS: ofp_print_port_status(string, oh); break; case OFPTYPE_PACKET_OUT: ofp_print_packet_out(string, oh, verbosity); break; case OFPTYPE_FLOW_MOD: ofp_print_flow_mod(string, oh, verbosity); break; case OFPTYPE_PORT_MOD: ofp_print_port_mod(string, oh); break; case OFPTYPE_TABLE_MOD: ofp_print_table_mod(string, oh); break; case OFPTYPE_METER_MOD: ofp_print_meter_mod(string, oh); break; case OFPTYPE_BARRIER_REQUEST: case OFPTYPE_BARRIER_REPLY: break; case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: ofp_print_queue_get_config_request(string, oh); break; case OFPTYPE_QUEUE_GET_CONFIG_REPLY: ofp_print_queue_get_config_reply(string, oh); break; case OFPTYPE_ROLE_REQUEST: case OFPTYPE_ROLE_REPLY: ofp_print_role_message(string, oh); break; case OFPTYPE_ROLE_STATUS: ofp_print_role_status_message(string, oh); break; case OFPTYPE_REQUESTFORWARD: ofp_print_requestforward(string, oh); break; case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_meter_stats_request(string, oh); break; case OFPTYPE_METER_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_meter_stats_reply(string, oh); break; case OFPTYPE_METER_CONFIG_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_meter_config_reply(string, oh); break; case OFPTYPE_METER_FEATURES_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_meter_features_reply(string, oh); break; case OFPTYPE_DESC_STATS_REQUEST: case OFPTYPE_METER_FEATURES_STATS_REQUEST: ofp_print_stats(string, oh); break; case OFPTYPE_FLOW_STATS_REQUEST: case OFPTYPE_AGGREGATE_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_flow_stats_request(string, oh); break; case OFPTYPE_TABLE_STATS_REQUEST: ofp_print_stats(string, oh); break; case OFPTYPE_PORT_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_ofpst_port_request(string, oh); break; case OFPTYPE_QUEUE_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_ofpst_queue_request(string, oh); break; case OFPTYPE_DESC_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_ofpst_desc_reply(string, oh); break; case OFPTYPE_FLOW_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_flow_stats_reply(string, oh); break; case OFPTYPE_QUEUE_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_ofpst_queue_reply(string, oh, verbosity); break; case OFPTYPE_PORT_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_ofpst_port_reply(string, oh, verbosity); break; case OFPTYPE_TABLE_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_table_stats_reply(string, oh); break; case OFPTYPE_AGGREGATE_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_aggregate_stats_reply(string, oh); break; case OFPTYPE_PORT_DESC_STATS_REQUEST: ofp_print_stats(string, oh); ofp_print_ofpst_port_desc_request(string, oh); break; case OFPTYPE_PORT_DESC_STATS_REPLY: ofp_print_stats(string, oh); ofp_print_ofpst_port_desc_reply(string, oh); break; case OFPTYPE_FLOW_MOD_TABLE_ID: ofp_print_nxt_flow_mod_table_id(string, ofpmsg_body(oh)); break; case OFPTYPE_SET_FLOW_FORMAT: ofp_print_nxt_set_flow_format(string, ofpmsg_body(oh)); break; case OFPTYPE_SET_PACKET_IN_FORMAT: ofp_print_nxt_set_packet_in_format(string, ofpmsg_body(oh)); break; case OFPTYPE_FLOW_AGE: break; case OFPTYPE_SET_CONTROLLER_ID: ofp_print_nxt_set_controller_id(string, ofpmsg_body(oh)); break; case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_SET_ASYNC_CONFIG: ofp_print_nxt_set_async_config(string, oh); break; case OFPTYPE_GET_ASYNC_REQUEST: break; case OFPTYPE_FLOW_MONITOR_CANCEL: ofp_print_nxt_flow_monitor_cancel(string, msg); break; case OFPTYPE_FLOW_MONITOR_PAUSED: case OFPTYPE_FLOW_MONITOR_RESUMED: break; case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: ofp_print_nxst_flow_monitor_request(string, msg); break; case OFPTYPE_FLOW_MONITOR_STATS_REPLY: ofp_print_nxst_flow_monitor_reply(string, msg); break; case OFPTYPE_BUNDLE_CONTROL: ofp_print_bundle_ctrl(string, msg); break; case OFPTYPE_BUNDLE_ADD_MESSAGE: ofp_print_bundle_add(string, msg, verbosity); break; case OFPTYPE_NXT_TLV_TABLE_MOD: ofp_print_tlv_table_mod(string, msg); break; case OFPTYPE_NXT_TLV_TABLE_REQUEST: break; case OFPTYPE_NXT_TLV_TABLE_REPLY: ofp_print_tlv_table_reply(string, msg); break; } } /* Composes and returns a string representing the OpenFlow packet of 'len' * bytes at 'oh' at the given 'verbosity' level. 0 is a minimal amount of * verbosity and higher numbers increase verbosity. The caller is responsible * for freeing the string. */ char * ofp_to_string(const void *oh_, size_t len, int verbosity) { struct ds string = DS_EMPTY_INITIALIZER; const struct ofp_header *oh = oh_; if (!len) { ds_put_cstr(&string, "OpenFlow message is empty\n"); } else if (len < sizeof(struct ofp_header)) { ds_put_format(&string, "OpenFlow packet too short (only %"PRIuSIZE" bytes):\n", len); } else if (ntohs(oh->length) > len) { enum ofperr error; enum ofpraw raw; error = ofpraw_decode_partial(&raw, oh, len); if (!error) { ofp_header_to_string__(oh, raw, &string); ds_put_char(&string, '\n'); } ds_put_format(&string, "(***truncated to %"PRIuSIZE" bytes from %"PRIu16"***)\n", len, ntohs(oh->length)); } else if (ntohs(oh->length) < len) { ds_put_format(&string, "(***only uses %"PRIu16" bytes out of %"PRIuSIZE"***)\n", ntohs(oh->length), len); } else { enum ofperr error; enum ofpraw raw; error = ofpraw_decode(&raw, oh); if (!error) { ofp_to_string__(oh, raw, &string, verbosity); if (verbosity >= 5) { if (ds_last(&string) != '\n') { ds_put_char(&string, '\n'); } ds_put_hex_dump(&string, oh, len, 0, true); } if (ds_last(&string) != '\n') { ds_put_char(&string, '\n'); } return ds_steal_cstr(&string); } ofp_print_error(&string, error); } ds_put_hex_dump(&string, oh, len, 0, true); return ds_steal_cstr(&string); } static void print_and_free(FILE *stream, char *string) { fputs(string, stream); free(string); } /* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the * given 'verbosity' level. 0 is a minimal amount of verbosity and higher * numbers increase verbosity. */ void ofp_print(FILE *stream, const void *oh, size_t len, int verbosity) { print_and_free(stream, ofp_to_string(oh, len, verbosity)); } /* Dumps the contents of the Ethernet frame in the 'len' bytes starting at * 'data' to 'stream'. */ void ofp_print_packet(FILE *stream, const void *data, size_t len) { print_and_free(stream, ofp_packet_to_string(data, len)); } openvswitch-2.5.9/lib/PaxHeaders.82075/odp-execute.c0000644000000000000000000000013213534540071017000 xustar0030 mtime=1567801401.477681728 30 atime=1567801402.081686164 30 ctime=1567801424.785853456 openvswitch-2.5.9/lib/odp-execute.c0000644000175000017500000004723413534540071020500 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * Copyright (c) 2013 Simon Horman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "odp-execute.h" #include #include #include #include #include #include #include "dp-packet.h" #include "dpif.h" #include "netlink.h" #include "odp-netlink.h" #include "odp-util.h" #include "packets.h" #include "flow.h" #include "unaligned.h" #include "util.h" /* Masked copy of an ethernet address. 'src' is already properly masked. */ static void ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src, const struct eth_addr mask) { int i; for (i = 0; i < ARRAY_SIZE(dst->be16); i++) { dst->be16[i] = src.be16[i] | (dst->be16[i] & ~mask.be16[i]); } } static void odp_eth_set_addrs(struct dp_packet *packet, const struct ovs_key_ethernet *key, const struct ovs_key_ethernet *mask) { struct eth_header *eh = dp_packet_l2(packet); if (eh) { if (!mask) { eh->eth_src = key->eth_src; eh->eth_dst = key->eth_dst; } else { ether_addr_copy_masked(&eh->eth_src, key->eth_src, mask->eth_src); ether_addr_copy_masked(&eh->eth_dst, key->eth_dst, mask->eth_dst); } } } static void odp_set_ipv4(struct dp_packet *packet, const struct ovs_key_ipv4 *key, const struct ovs_key_ipv4 *mask) { struct ip_header *nh = dp_packet_l3(packet); packet_set_ipv4( packet, key->ipv4_src | (get_16aligned_be32(&nh->ip_src) & ~mask->ipv4_src), key->ipv4_dst | (get_16aligned_be32(&nh->ip_dst) & ~mask->ipv4_dst), key->ipv4_tos | (nh->ip_tos & ~mask->ipv4_tos), key->ipv4_ttl | (nh->ip_ttl & ~mask->ipv4_ttl)); } static const ovs_be32 * mask_ipv6_addr(const ovs_16aligned_be32 *old, const ovs_be32 *addr, const ovs_be32 *mask, ovs_be32 *masked) { for (int i = 0; i < 4; i++) { masked[i] = addr[i] | (get_16aligned_be32(&old[i]) & ~mask[i]); } return masked; } static void odp_set_ipv6(struct dp_packet *packet, const struct ovs_key_ipv6 *key, const struct ovs_key_ipv6 *mask) { struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet); ovs_be32 sbuf[4], dbuf[4]; uint8_t old_tc = ntohl(get_16aligned_be32(&nh->ip6_flow)) >> 20; ovs_be32 old_fl = get_16aligned_be32(&nh->ip6_flow) & htonl(0xfffff); packet_set_ipv6( packet, mask_ipv6_addr(nh->ip6_src.be32, key->ipv6_src, mask->ipv6_src, sbuf), mask_ipv6_addr(nh->ip6_dst.be32, key->ipv6_dst, mask->ipv6_dst, dbuf), key->ipv6_tclass | (old_tc & ~mask->ipv6_tclass), key->ipv6_label | (old_fl & ~mask->ipv6_label), key->ipv6_hlimit | (nh->ip6_hlim & ~mask->ipv6_hlimit)); } static void odp_set_tcp(struct dp_packet *packet, const struct ovs_key_tcp *key, const struct ovs_key_tcp *mask) { struct tcp_header *th = dp_packet_l4(packet); if (OVS_LIKELY(th && dp_packet_get_tcp_payload(packet))) { packet_set_tcp_port(packet, key->tcp_src | (th->tcp_src & ~mask->tcp_src), key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst)); } } static void odp_set_udp(struct dp_packet *packet, const struct ovs_key_udp *key, const struct ovs_key_udp *mask) { struct udp_header *uh = dp_packet_l4(packet); if (OVS_LIKELY(uh && dp_packet_get_udp_payload(packet))) { packet_set_udp_port(packet, key->udp_src | (uh->udp_src & ~mask->udp_src), key->udp_dst | (uh->udp_dst & ~mask->udp_dst)); } } static void odp_set_sctp(struct dp_packet *packet, const struct ovs_key_sctp *key, const struct ovs_key_sctp *mask) { struct sctp_header *sh = dp_packet_l4(packet); if (OVS_LIKELY(sh && dp_packet_get_sctp_payload(packet))) { packet_set_sctp_port(packet, key->sctp_src | (sh->sctp_src & ~mask->sctp_src), key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst)); } } static void odp_set_tunnel_action(const struct nlattr *a, struct flow_tnl *tun_key) { enum odp_key_fitness fitness; fitness = odp_tun_key_from_attr(a, true, tun_key); ovs_assert(fitness != ODP_FIT_ERROR); } static void set_arp(struct dp_packet *packet, const struct ovs_key_arp *key, const struct ovs_key_arp *mask) { struct arp_eth_header *arp = dp_packet_l3(packet); if (!mask) { arp->ar_op = key->arp_op; arp->ar_sha = key->arp_sha; put_16aligned_be32(&arp->ar_spa, key->arp_sip); arp->ar_tha = key->arp_tha; put_16aligned_be32(&arp->ar_tpa, key->arp_tip); } else { ovs_be32 ar_spa = get_16aligned_be32(&arp->ar_spa); ovs_be32 ar_tpa = get_16aligned_be32(&arp->ar_tpa); arp->ar_op = key->arp_op | (arp->ar_op & ~mask->arp_op); ether_addr_copy_masked(&arp->ar_sha, key->arp_sha, mask->arp_sha); put_16aligned_be32(&arp->ar_spa, key->arp_sip | (ar_spa & ~mask->arp_sip)); ether_addr_copy_masked(&arp->ar_tha, key->arp_tha, mask->arp_tha); put_16aligned_be32(&arp->ar_tpa, key->arp_tip | (ar_tpa & ~mask->arp_tip)); } } static void odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key, const struct ovs_key_nd *mask) { const struct ovs_nd_msg *ns = dp_packet_l4(packet); const struct ovs_nd_opt *nd_opt = dp_packet_get_nd_payload(packet); if (OVS_LIKELY(ns && nd_opt)) { int bytes_remain = dp_packet_l4_size(packet) - sizeof(*ns); ovs_be32 tgt_buf[4]; struct eth_addr sll_buf = eth_addr_zero; struct eth_addr tll_buf = eth_addr_zero; while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) { if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR && nd_opt->nd_opt_len == 1) { sll_buf = nd_opt->nd_opt_mac; ether_addr_copy_masked(&sll_buf, key->nd_sll, mask->nd_sll); /* A packet can only contain one SLL or TLL option */ break; } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR && nd_opt->nd_opt_len == 1) { tll_buf = nd_opt->nd_opt_mac; ether_addr_copy_masked(&tll_buf, key->nd_tll, mask->nd_tll); /* A packet can only contain one SLL or TLL option */ break; } nd_opt += nd_opt->nd_opt_len; bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN; } packet_set_nd(packet, mask_ipv6_addr(ns->target.be32, key->nd_target, mask->nd_target, tgt_buf), sll_buf, tll_buf); } } static void odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a) { enum ovs_key_attr type = nl_attr_type(a); const struct ovs_key_ipv4 *ipv4_key; const struct ovs_key_ipv6 *ipv6_key; struct pkt_metadata *md = &packet->md; switch (type) { case OVS_KEY_ATTR_PRIORITY: md->skb_priority = nl_attr_get_u32(a); break; case OVS_KEY_ATTR_TUNNEL: odp_set_tunnel_action(a, &md->tunnel); break; case OVS_KEY_ATTR_SKB_MARK: md->pkt_mark = nl_attr_get_u32(a); break; case OVS_KEY_ATTR_ETHERNET: odp_eth_set_addrs(packet, nl_attr_get(a), NULL); break; case OVS_KEY_ATTR_IPV4: ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4)); packet_set_ipv4(packet, ipv4_key->ipv4_src, ipv4_key->ipv4_dst, ipv4_key->ipv4_tos, ipv4_key->ipv4_ttl); break; case OVS_KEY_ATTR_IPV6: ipv6_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv6)); packet_set_ipv6(packet, ipv6_key->ipv6_src, ipv6_key->ipv6_dst, ipv6_key->ipv6_tclass, ipv6_key->ipv6_label, ipv6_key->ipv6_hlimit); break; case OVS_KEY_ATTR_TCP: if (OVS_LIKELY(dp_packet_get_tcp_payload(packet))) { const struct ovs_key_tcp *tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp)); packet_set_tcp_port(packet, tcp_key->tcp_src, tcp_key->tcp_dst); } break; case OVS_KEY_ATTR_UDP: if (OVS_LIKELY(dp_packet_get_udp_payload(packet))) { const struct ovs_key_udp *udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp)); packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst); } break; case OVS_KEY_ATTR_SCTP: if (OVS_LIKELY(dp_packet_get_sctp_payload(packet))) { const struct ovs_key_sctp *sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp)); packet_set_sctp_port(packet, sctp_key->sctp_src, sctp_key->sctp_dst); } break; case OVS_KEY_ATTR_MPLS: set_mpls_lse(packet, nl_attr_get_be32(a)); break; case OVS_KEY_ATTR_ARP: set_arp(packet, nl_attr_get(a), NULL); break; case OVS_KEY_ATTR_ICMP: case OVS_KEY_ATTR_ICMPV6: if (OVS_LIKELY(dp_packet_get_icmp_payload(packet))) { const struct ovs_key_icmp *icmp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_icmp)); packet_set_icmp(packet, icmp_key->icmp_type, icmp_key->icmp_code); } break; case OVS_KEY_ATTR_ND: if (OVS_LIKELY(dp_packet_get_nd_payload(packet))) { const struct ovs_key_nd *nd_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd)); packet_set_nd(packet, nd_key->nd_target, nd_key->nd_sll, nd_key->nd_tll); } break; case OVS_KEY_ATTR_DP_HASH: md->dp_hash = nl_attr_get_u32(a); break; case OVS_KEY_ATTR_RECIRC_ID: md->recirc_id = nl_attr_get_u32(a); break; case OVS_KEY_ATTR_UNSPEC: case OVS_KEY_ATTR_ENCAP: case OVS_KEY_ATTR_ETHERTYPE: case OVS_KEY_ATTR_IN_PORT: case OVS_KEY_ATTR_VLAN: case OVS_KEY_ATTR_TCP_FLAGS: case OVS_KEY_ATTR_CT_STATE: case OVS_KEY_ATTR_CT_ZONE: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: case __OVS_KEY_ATTR_MAX: default: OVS_NOT_REACHED(); } } #define get_mask(a, type) ((const type *)(const void *)(a + 1) + 1) static void odp_execute_masked_set_action(struct dp_packet *packet, const struct nlattr *a) { struct pkt_metadata *md = &packet->md; enum ovs_key_attr type = nl_attr_type(a); struct mpls_hdr *mh; switch (type) { case OVS_KEY_ATTR_PRIORITY: md->skb_priority = nl_attr_get_u32(a) | (md->skb_priority & ~*get_mask(a, uint32_t)); break; case OVS_KEY_ATTR_SKB_MARK: md->pkt_mark = nl_attr_get_u32(a) | (md->pkt_mark & ~*get_mask(a, uint32_t)); break; case OVS_KEY_ATTR_ETHERNET: odp_eth_set_addrs(packet, nl_attr_get(a), get_mask(a, struct ovs_key_ethernet)); break; case OVS_KEY_ATTR_IPV4: odp_set_ipv4(packet, nl_attr_get(a), get_mask(a, struct ovs_key_ipv4)); break; case OVS_KEY_ATTR_IPV6: odp_set_ipv6(packet, nl_attr_get(a), get_mask(a, struct ovs_key_ipv6)); break; case OVS_KEY_ATTR_TCP: odp_set_tcp(packet, nl_attr_get(a), get_mask(a, struct ovs_key_tcp)); break; case OVS_KEY_ATTR_UDP: odp_set_udp(packet, nl_attr_get(a), get_mask(a, struct ovs_key_udp)); break; case OVS_KEY_ATTR_SCTP: odp_set_sctp(packet, nl_attr_get(a), get_mask(a, struct ovs_key_sctp)); break; case OVS_KEY_ATTR_MPLS: mh = dp_packet_l2_5(packet); if (mh) { put_16aligned_be32(&mh->mpls_lse, nl_attr_get_be32(a) | (get_16aligned_be32(&mh->mpls_lse) & ~*get_mask(a, ovs_be32))); } break; case OVS_KEY_ATTR_ARP: set_arp(packet, nl_attr_get(a), get_mask(a, struct ovs_key_arp)); break; case OVS_KEY_ATTR_ND: odp_set_nd(packet, nl_attr_get(a), get_mask(a, struct ovs_key_nd)); break; case OVS_KEY_ATTR_DP_HASH: md->dp_hash = nl_attr_get_u32(a) | (md->dp_hash & ~*get_mask(a, uint32_t)); break; case OVS_KEY_ATTR_RECIRC_ID: md->recirc_id = nl_attr_get_u32(a) | (md->recirc_id & ~*get_mask(a, uint32_t)); break; case OVS_KEY_ATTR_TUNNEL: /* Masked data not supported for tunnel. */ case OVS_KEY_ATTR_UNSPEC: case OVS_KEY_ATTR_CT_STATE: case OVS_KEY_ATTR_CT_ZONE: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: case OVS_KEY_ATTR_ENCAP: case OVS_KEY_ATTR_ETHERTYPE: case OVS_KEY_ATTR_IN_PORT: case OVS_KEY_ATTR_VLAN: case OVS_KEY_ATTR_ICMP: case OVS_KEY_ATTR_ICMPV6: case OVS_KEY_ATTR_TCP_FLAGS: case __OVS_KEY_ATTR_MAX: default: OVS_NOT_REACHED(); } } static void odp_execute_sample(void *dp, struct dp_packet *packet, bool steal, const struct nlattr *action, odp_execute_cb dp_execute_action) { const struct nlattr *subactions = NULL; const struct nlattr *a; size_t left; NL_NESTED_FOR_EACH_UNSAFE (a, left, action) { int type = nl_attr_type(a); switch ((enum ovs_sample_attr) type) { case OVS_SAMPLE_ATTR_PROBABILITY: if (random_uint32() >= nl_attr_get_u32(a)) { if (steal) { dp_packet_delete(packet); } return; } break; case OVS_SAMPLE_ATTR_ACTIONS: subactions = a; break; case OVS_SAMPLE_ATTR_UNSPEC: case __OVS_SAMPLE_ATTR_MAX: default: OVS_NOT_REACHED(); } } odp_execute_actions(dp, &packet, 1, steal, nl_attr_get(subactions), nl_attr_get_size(subactions), dp_execute_action); } static bool requires_datapath_assistance(const struct nlattr *a) { enum ovs_action_attr type = nl_attr_type(a); switch (type) { /* These only make sense in the context of a datapath. */ case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_CT: return true; case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: return false; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } return false; } void odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal, const struct nlattr *actions, size_t actions_len, odp_execute_cb dp_execute_action) { const struct nlattr *a; unsigned int left; int i; NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) { int type = nl_attr_type(a); bool last_action = (left <= NLA_ALIGN(a->nla_len)); if (requires_datapath_assistance(a)) { if (dp_execute_action) { /* Allow 'dp_execute_action' to steal the packet data if we do * not need it any more. */ bool may_steal = steal && last_action; dp_execute_action(dp, packets, cnt, a, may_steal); if (last_action) { /* We do not need to free the packets. dp_execute_actions() * has stolen them */ return; } } continue; } switch ((enum ovs_action_attr) type) { case OVS_ACTION_ATTR_HASH: { const struct ovs_action_hash *hash_act = nl_attr_get(a); /* Calculate a hash value directly. This might not match the * value computed by the datapath, but it is much less expensive, * and the current use case (bonding) does not require a strict * match to work properly. */ if (hash_act->hash_alg == OVS_HASH_ALG_L4) { struct flow flow; uint32_t hash; for (i = 0; i < cnt; i++) { flow_extract(packets[i], &flow); hash = flow_hash_5tuple(&flow, hash_act->hash_basis); packets[i]->md.dp_hash = hash; } } else { /* Assert on unknown hash algorithm. */ OVS_NOT_REACHED(); } break; } case OVS_ACTION_ATTR_PUSH_VLAN: { const struct ovs_action_push_vlan *vlan = nl_attr_get(a); for (i = 0; i < cnt; i++) { eth_push_vlan(packets[i], vlan->vlan_tpid, vlan->vlan_tci); } break; } case OVS_ACTION_ATTR_POP_VLAN: for (i = 0; i < cnt; i++) { eth_pop_vlan(packets[i]); } break; case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nl_attr_get(a); for (i = 0; i < cnt; i++) { push_mpls(packets[i], mpls->mpls_ethertype, mpls->mpls_lse); } break; } case OVS_ACTION_ATTR_POP_MPLS: for (i = 0; i < cnt; i++) { pop_mpls(packets[i], nl_attr_get_be16(a)); } break; case OVS_ACTION_ATTR_SET: for (i = 0; i < cnt; i++) { odp_execute_set_action(packets[i], nl_attr_get(a)); } break; case OVS_ACTION_ATTR_SET_MASKED: for (i = 0; i < cnt; i++) { odp_execute_masked_set_action(packets[i], nl_attr_get(a)); } break; case OVS_ACTION_ATTR_SAMPLE: for (i = 0; i < cnt; i++) { odp_execute_sample(dp, packets[i], steal && last_action, a, dp_execute_action); } if (last_action) { /* We do not need to free the packets. odp_execute_sample() has * stolen them*/ return; } break; case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_CT: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } } if (steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(packets[i]); } } } openvswitch-2.5.9/lib/PaxHeaders.82075/packets.h0000644000000000000000000000013213534540071016215 xustar0030 mtime=1567801401.573682433 30 atime=1567801402.093686252 30 ctime=1567801424.845853898 openvswitch-2.5.9/lib/packets.h0000644000175000017500000007620613534540071017716 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PACKETS_H #define PACKETS_H 1 #include #include #include #include #include #include "compiler.h" #include "geneve.h" #include "openvswitch/types.h" #include "odp-netlink.h" #include "random.h" #include "hash.h" #include "tun-metadata.h" #include "unaligned.h" #include "util.h" struct dp_packet; struct ds; /* Tunnel information used in flow key and metadata. */ struct flow_tnl { ovs_be32 ip_dst; struct in6_addr ipv6_dst; ovs_be32 ip_src; struct in6_addr ipv6_src; ovs_be64 tun_id; uint16_t flags; uint8_t ip_tos; uint8_t ip_ttl; ovs_be16 tp_src; ovs_be16 tp_dst; ovs_be16 gbp_id; uint8_t gbp_flags; uint8_t pad1[5]; /* Pad to 64 bits. */ struct tun_metadata metadata; }; /* Some flags are exposed through OpenFlow while others are used only * internally. */ /* Public flags */ #define FLOW_TNL_F_OAM (1 << 0) #define FLOW_TNL_PUB_F_MASK ((1 << 1) - 1) /* Private flags */ #define FLOW_TNL_F_DONT_FRAGMENT (1 << 1) #define FLOW_TNL_F_CSUM (1 << 2) #define FLOW_TNL_F_KEY (1 << 3) #define FLOW_TNL_F_MASK ((1 << 4) - 1) /* Purely internal to OVS userspace. These flags should never be exposed to * the outside world and so aren't included in the flags mask. */ /* Tunnel information is in userspace datapath format. */ #define FLOW_TNL_F_UDPIF (1 << 4) static inline bool ipv6_addr_is_set(const struct in6_addr *addr); static inline bool flow_tnl_dst_is_set(const struct flow_tnl *tnl) { return tnl->ip_dst || ipv6_addr_is_set(&tnl->ipv6_dst); } struct in6_addr flow_tnl_dst(const struct flow_tnl *tnl); struct in6_addr flow_tnl_src(const struct flow_tnl *tnl); /* Returns an offset to 'src' covering all the meaningful fields in 'src'. */ static inline size_t flow_tnl_size(const struct flow_tnl *src) { if (!flow_tnl_dst_is_set(src)) { /* Covers ip_dst and ipv6_dst only. */ return offsetof(struct flow_tnl, ip_src); } if (src->flags & FLOW_TNL_F_UDPIF) { /* Datapath format, cover all options we have. */ return offsetof(struct flow_tnl, metadata.opts) + src->metadata.present.len; } if (!src->metadata.present.map) { /* No TLVs, opts is irrelevant. */ return offsetof(struct flow_tnl, metadata.opts); } /* Have decoded TLVs, opts is relevant. */ return sizeof *src; } /* Copy flow_tnl, but avoid copying unused portions of tun_metadata. Unused * data in 'dst' is NOT cleared, so this must not be used in cases where the * uninitialized portion may be hashed over. */ static inline void flow_tnl_copy__(struct flow_tnl *dst, const struct flow_tnl *src) { memcpy(dst, src, flow_tnl_size(src)); } static inline bool flow_tnl_equal(const struct flow_tnl *a, const struct flow_tnl *b) { size_t a_size = flow_tnl_size(a); return a_size == flow_tnl_size(b) && !memcmp(a, b, a_size); } /* Unfortunately, a "struct flow" sometimes has to handle OpenFlow port * numbers and other times datapath (dpif) port numbers. This union allows * access to both. */ union flow_in_port { odp_port_t odp_port; ofp_port_t ofp_port; }; /* Datapath packet metadata */ struct pkt_metadata { uint32_t recirc_id; /* Recirculation id carried with the recirculating packets. 0 for packets received from the wire. */ uint32_t dp_hash; /* hash value computed by the recirculation action. */ uint32_t skb_priority; /* Packet priority for QoS. */ uint32_t pkt_mark; /* Packet mark. */ uint16_t ct_state; /* Connection state. */ uint16_t ct_zone; /* Connection zone. */ uint32_t ct_mark; /* Connection mark. */ ovs_u128 ct_label; /* Connection label. */ union flow_in_port in_port; /* Input port. */ struct flow_tnl tunnel; /* Encapsulating tunnel parameters. Note that * if 'ip_dst' == 0, the rest of the fields may * be uninitialized. */ }; static inline void pkt_metadata_init(struct pkt_metadata *md, odp_port_t port) { /* It can be expensive to zero out all of the tunnel metadata. However, * we can just zero out ip_dst and the rest of the data will never be * looked at. */ memset(md, 0, offsetof(struct pkt_metadata, in_port)); md->tunnel.ip_dst = 0; md->tunnel.ipv6_dst = in6addr_any; md->in_port.odp_port = port; } /* This function prefetches the cachelines touched by pkt_metadata_init() * For performance reasons the two functions should be kept in sync. */ static inline void pkt_metadata_prefetch_init(struct pkt_metadata *md) { ovs_prefetch_range(md, offsetof(struct pkt_metadata, tunnel.ip_src)); } bool dpid_from_string(const char *s, uint64_t *dpidp); #define ETH_ADDR_LEN 6 static const struct eth_addr eth_addr_broadcast OVS_UNUSED = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; static const struct eth_addr eth_addr_exact OVS_UNUSED = { { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } } }; static const struct eth_addr eth_addr_zero OVS_UNUSED = { { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }; static const struct eth_addr eth_addr_stp OVS_UNUSED = { { { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } } }; static const struct eth_addr eth_addr_lacp OVS_UNUSED = { { { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 } } }; static const struct eth_addr eth_addr_bfd OVS_UNUSED = { { { 0x00, 0x23, 0x20, 0x00, 0x00, 0x01 } } }; static inline bool eth_addr_is_broadcast(const struct eth_addr a) { return (a.be16[0] & a.be16[1] & a.be16[2]) == htons(0xffff); } static inline bool eth_addr_is_multicast(const struct eth_addr a) { return a.ea[0] & 1; } static inline bool eth_addr_is_local(const struct eth_addr a) { /* Local if it is either a locally administered address or a Nicira random * address. */ return a.ea[0] & 2 || (a.be16[0] == htons(0x0023) && (a.be16[1] & htons(0xff80)) == htons(0x2080)); } static inline bool eth_addr_is_zero(const struct eth_addr a) { return !(a.be16[0] | a.be16[1] | a.be16[2]); } static inline int eth_mask_is_exact(const struct eth_addr a) { return (a.be16[0] & a.be16[1] & a.be16[2]) == htons(0xffff); } static inline int eth_addr_compare_3way(const struct eth_addr a, const struct eth_addr b) { return memcmp(&a, &b, sizeof a); } static inline bool eth_addr_equals(const struct eth_addr a, const struct eth_addr b) { return !eth_addr_compare_3way(a, b); } static inline bool eth_addr_equal_except(const struct eth_addr a, const struct eth_addr b, const struct eth_addr mask) { return !(((a.be16[0] ^ b.be16[0]) & mask.be16[0]) || ((a.be16[1] ^ b.be16[1]) & mask.be16[1]) || ((a.be16[2] ^ b.be16[2]) & mask.be16[2])); } static inline uint64_t eth_addr_to_uint64(const struct eth_addr ea) { return (((uint64_t) ntohs(ea.be16[0]) << 32) | ((uint64_t) ntohs(ea.be16[1]) << 16) | ntohs(ea.be16[2])); } static inline uint64_t eth_addr_vlan_to_uint64(const struct eth_addr ea, uint16_t vlan) { return (((uint64_t)vlan << 48) | eth_addr_to_uint64(ea)); } static inline void eth_addr_from_uint64(uint64_t x, struct eth_addr *ea) { ea->be16[0] = htons(x >> 32); ea->be16[1] = htons((x & 0xFFFF0000) >> 16); ea->be16[2] = htons(x & 0xFFFF); } static inline struct eth_addr eth_addr_invert(const struct eth_addr src) { struct eth_addr dst; for (int i = 0; i < ARRAY_SIZE(src.be16); i++) { dst.be16[i] = ~src.be16[i]; } return dst; } static inline void eth_addr_mark_random(struct eth_addr *ea) { ea->ea[0] &= ~1; /* Unicast. */ ea->ea[0] |= 2; /* Private. */ } static inline void eth_addr_random(struct eth_addr *ea) { random_bytes((uint8_t *)ea, sizeof *ea); eth_addr_mark_random(ea); } static inline void eth_addr_nicira_random(struct eth_addr *ea) { eth_addr_random(ea); /* Set the OUI to the Nicira one. */ ea->ea[0] = 0x00; ea->ea[1] = 0x23; ea->ea[2] = 0x20; /* Set the top bit to indicate random Nicira address. */ ea->ea[3] |= 0x80; } static inline uint32_t hash_mac(const struct eth_addr ea, const uint16_t vlan, const uint32_t basis) { return hash_uint64_basis(eth_addr_vlan_to_uint64(ea, vlan), basis); } bool eth_addr_is_reserved(const struct eth_addr); bool eth_addr_from_string(const char *, struct eth_addr *); void compose_rarp(struct dp_packet *, const struct eth_addr); void eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci); void eth_pop_vlan(struct dp_packet *); const char *eth_from_hex(const char *hex, struct dp_packet **packetp); void eth_format_masked(const struct eth_addr ea, const struct eth_addr *mask, struct ds *s); void set_mpls_lse(struct dp_packet *, ovs_be32 label); void push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse); void pop_mpls(struct dp_packet *, ovs_be16 ethtype); void set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl); void set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc); void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label); void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos); ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, ovs_be32 label); /* Example: * * struct eth_addr mac; * [...] * printf("The Ethernet address is "ETH_ADDR_FMT"\n", ETH_ADDR_ARGS(mac)); * */ #define ETH_ADDR_FMT \ "%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8 #define ETH_ADDR_ARGS(EA) ETH_ADDR_BYTES_ARGS((EA).ea) #define ETH_ADDR_BYTES_ARGS(EAB) \ (EAB)[0], (EAB)[1], (EAB)[2], (EAB)[3], (EAB)[4], (EAB)[5] /* Example: * * char *string = "1 00:11:22:33:44:55 2"; * struct eth_addr mac; * int a, b; * * if (ovs_scan(string, "%d"ETH_ADDR_SCAN_FMT"%d", * &a, ETH_ADDR_SCAN_ARGS(mac), &b)) { * ... * } */ #define ETH_ADDR_SCAN_FMT "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8 #define ETH_ADDR_SCAN_ARGS(EA) \ &(EA).ea[0], &(EA).ea[1], &(EA).ea[2], &(EA).ea[3], &(EA).ea[4], &(EA).ea[5] #define ETH_TYPE_IP 0x0800 #define ETH_TYPE_ARP 0x0806 #define ETH_TYPE_TEB 0x6558 #define ETH_TYPE_VLAN_8021Q 0x8100 #define ETH_TYPE_VLAN ETH_TYPE_VLAN_8021Q #define ETH_TYPE_VLAN_8021AD 0x88a8 #define ETH_TYPE_IPV6 0x86dd #define ETH_TYPE_LACP 0x8809 #define ETH_TYPE_RARP 0x8035 #define ETH_TYPE_MPLS 0x8847 #define ETH_TYPE_MPLS_MCAST 0x8848 static inline bool eth_type_mpls(ovs_be16 eth_type) { return eth_type == htons(ETH_TYPE_MPLS) || eth_type == htons(ETH_TYPE_MPLS_MCAST); } static inline bool eth_type_vlan(ovs_be16 eth_type) { return eth_type == htons(ETH_TYPE_VLAN_8021Q) || eth_type == htons(ETH_TYPE_VLAN_8021AD); } /* Minimum value for an Ethernet type. Values below this are IEEE 802.2 frame * lengths. */ #define ETH_TYPE_MIN 0x600 #define ETH_HEADER_LEN 14 #define ETH_PAYLOAD_MIN 46 #define ETH_PAYLOAD_MAX 1500 #define ETH_TOTAL_MIN (ETH_HEADER_LEN + ETH_PAYLOAD_MIN) #define ETH_TOTAL_MAX (ETH_HEADER_LEN + ETH_PAYLOAD_MAX) #define ETH_VLAN_TOTAL_MAX (ETH_HEADER_LEN + VLAN_HEADER_LEN + ETH_PAYLOAD_MAX) struct eth_header { struct eth_addr eth_dst; struct eth_addr eth_src; ovs_be16 eth_type; }; BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header)); #define LLC_DSAP_SNAP 0xaa #define LLC_SSAP_SNAP 0xaa #define LLC_CNTL_SNAP 3 #define LLC_HEADER_LEN 3 OVS_PACKED( struct llc_header { uint8_t llc_dsap; uint8_t llc_ssap; uint8_t llc_cntl; }); BUILD_ASSERT_DECL(LLC_HEADER_LEN == sizeof(struct llc_header)); /* LLC field values used for STP frames. */ #define STP_LLC_SSAP 0x42 #define STP_LLC_DSAP 0x42 #define STP_LLC_CNTL 0x03 #define SNAP_ORG_ETHERNET "\0\0" /* The compiler adds a null byte, so sizeof(SNAP_ORG_ETHERNET) == 3. */ #define SNAP_HEADER_LEN 5 OVS_PACKED( struct snap_header { uint8_t snap_org[3]; ovs_be16 snap_type; }); BUILD_ASSERT_DECL(SNAP_HEADER_LEN == sizeof(struct snap_header)); #define LLC_SNAP_HEADER_LEN (LLC_HEADER_LEN + SNAP_HEADER_LEN) OVS_PACKED( struct llc_snap_header { struct llc_header llc; struct snap_header snap; }); BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header)); #define VLAN_VID_MASK 0x0fff #define VLAN_VID_SHIFT 0 #define VLAN_PCP_MASK 0xe000 #define VLAN_PCP_SHIFT 13 #define VLAN_CFI 0x1000 #define VLAN_CFI_SHIFT 12 /* Given the vlan_tci field from an 802.1Q header, in network byte order, * returns the VLAN ID in host byte order. */ static inline uint16_t vlan_tci_to_vid(ovs_be16 vlan_tci) { return (ntohs(vlan_tci) & VLAN_VID_MASK) >> VLAN_VID_SHIFT; } /* Given the vlan_tci field from an 802.1Q header, in network byte order, * returns the priority code point (PCP) in host byte order. */ static inline int vlan_tci_to_pcp(ovs_be16 vlan_tci) { return (ntohs(vlan_tci) & VLAN_PCP_MASK) >> VLAN_PCP_SHIFT; } /* Given the vlan_tci field from an 802.1Q header, in network byte order, * returns the Canonical Format Indicator (CFI). */ static inline int vlan_tci_to_cfi(ovs_be16 vlan_tci) { return (vlan_tci & htons(VLAN_CFI)) != 0; } #define VLAN_HEADER_LEN 4 struct vlan_header { ovs_be16 vlan_tci; /* Lowest 12 bits are VLAN ID. */ ovs_be16 vlan_next_type; }; BUILD_ASSERT_DECL(VLAN_HEADER_LEN == sizeof(struct vlan_header)); #define VLAN_ETH_HEADER_LEN (ETH_HEADER_LEN + VLAN_HEADER_LEN) OVS_PACKED( struct vlan_eth_header { struct eth_addr veth_dst; struct eth_addr veth_src; ovs_be16 veth_type; /* Always htons(ETH_TYPE_VLAN). */ ovs_be16 veth_tci; /* Lowest 12 bits are VLAN ID. */ ovs_be16 veth_next_type; }); BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header)); /* MPLS related definitions */ #define MPLS_TTL_MASK 0x000000ff #define MPLS_TTL_SHIFT 0 #define MPLS_BOS_MASK 0x00000100 #define MPLS_BOS_SHIFT 8 #define MPLS_TC_MASK 0x00000e00 #define MPLS_TC_SHIFT 9 #define MPLS_LABEL_MASK 0xfffff000 #define MPLS_LABEL_SHIFT 12 #define MPLS_HLEN 4 struct mpls_hdr { ovs_16aligned_be32 mpls_lse; }; BUILD_ASSERT_DECL(MPLS_HLEN == sizeof(struct mpls_hdr)); /* Given a mpls label stack entry in network byte order * return mpls label in host byte order */ static inline uint32_t mpls_lse_to_label(ovs_be32 mpls_lse) { return (ntohl(mpls_lse) & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT; } /* Given a mpls label stack entry in network byte order * return mpls tc */ static inline uint8_t mpls_lse_to_tc(ovs_be32 mpls_lse) { return (ntohl(mpls_lse) & MPLS_TC_MASK) >> MPLS_TC_SHIFT; } /* Given a mpls label stack entry in network byte order * return mpls ttl */ static inline uint8_t mpls_lse_to_ttl(ovs_be32 mpls_lse) { return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT; } /* Set TTL in mpls lse. */ static inline void flow_set_mpls_lse_ttl(ovs_be32 *mpls_lse, uint8_t ttl) { *mpls_lse &= ~htonl(MPLS_TTL_MASK); *mpls_lse |= htonl(ttl << MPLS_TTL_SHIFT); } /* Given a mpls label stack entry in network byte order * return mpls BoS bit */ static inline uint8_t mpls_lse_to_bos(ovs_be32 mpls_lse) { return (mpls_lse & htonl(MPLS_BOS_MASK)) != 0; } #define IP_FMT "%"PRIu32".%"PRIu32".%"PRIu32".%"PRIu32 #define IP_ARGS(ip) \ ntohl(ip) >> 24, \ (ntohl(ip) >> 16) & 0xff, \ (ntohl(ip) >> 8) & 0xff, \ ntohl(ip) & 0xff /* Example: * * char *string = "1 33.44.55.66 2"; * ovs_be32 ip; * int a, b; * * if (ovs_scan(string, "%d"IP_SCAN_FMT"%d", &a, IP_SCAN_ARGS(&ip), &b)) { * ... * } */ #define IP_SCAN_FMT "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8 #define IP_SCAN_ARGS(ip) \ ((void) (ovs_be32) *(ip), &((uint8_t *) ip)[0]), \ &((uint8_t *) ip)[1], \ &((uint8_t *) ip)[2], \ &((uint8_t *) ip)[3] /* Returns true if 'netmask' is a CIDR netmask, that is, if it consists of N * high-order 1-bits and 32-N low-order 0-bits. */ static inline bool ip_is_cidr(ovs_be32 netmask) { uint32_t x = ~ntohl(netmask); return !(x & (x + 1)); } static inline bool ip_is_multicast(ovs_be32 ip) { return (ip & htonl(0xf0000000)) == htonl(0xe0000000); } static inline bool ip_is_local_multicast(ovs_be32 ip) { return (ip & htonl(0xffffff00)) == htonl(0xe0000000); } int ip_count_cidr_bits(ovs_be32 netmask); void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *); char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask) OVS_WARN_UNUSED_RESULT; #define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4) #define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15) #define IP_IHL_VER(ihl, ver) (((ver) << 4) | (ihl)) #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 #endif /* TOS fields. */ #define IP_ECN_NOT_ECT 0x0 #define IP_ECN_ECT_1 0x01 #define IP_ECN_ECT_0 0x02 #define IP_ECN_CE 0x03 #define IP_ECN_MASK 0x03 #define IP_DSCP_MASK 0xfc #define IP_VERSION 4 #define IP_DONT_FRAGMENT 0x4000 /* Don't fragment. */ #define IP_MORE_FRAGMENTS 0x2000 /* More fragments. */ #define IP_FRAG_OFF_MASK 0x1fff /* Fragment offset. */ #define IP_IS_FRAGMENT(ip_frag_off) \ ((ip_frag_off) & htons(IP_MORE_FRAGMENTS | IP_FRAG_OFF_MASK)) #define IP_HEADER_LEN 20 struct ip_header { uint8_t ip_ihl_ver; uint8_t ip_tos; ovs_be16 ip_tot_len; ovs_be16 ip_id; ovs_be16 ip_frag_off; uint8_t ip_ttl; uint8_t ip_proto; ovs_be16 ip_csum; ovs_16aligned_be32 ip_src; ovs_16aligned_be32 ip_dst; }; BUILD_ASSERT_DECL(IP_HEADER_LEN == sizeof(struct ip_header)); #define ICMP_HEADER_LEN 8 struct icmp_header { uint8_t icmp_type; uint8_t icmp_code; ovs_be16 icmp_csum; union { struct { ovs_be16 id; ovs_be16 seq; } echo; struct { ovs_be16 empty; ovs_be16 mtu; } frag; ovs_16aligned_be32 gateway; } icmp_fields; }; BUILD_ASSERT_DECL(ICMP_HEADER_LEN == sizeof(struct icmp_header)); #define IGMP_HEADER_LEN 8 struct igmp_header { uint8_t igmp_type; uint8_t igmp_code; ovs_be16 igmp_csum; ovs_16aligned_be32 group; }; BUILD_ASSERT_DECL(IGMP_HEADER_LEN == sizeof(struct igmp_header)); #define IGMPV3_HEADER_LEN 8 struct igmpv3_header { uint8_t type; uint8_t rsvr1; ovs_be16 csum; ovs_be16 rsvr2; ovs_be16 ngrp; }; BUILD_ASSERT_DECL(IGMPV3_HEADER_LEN == sizeof(struct igmpv3_header)); #define IGMPV3_RECORD_LEN 8 struct igmpv3_record { uint8_t type; uint8_t aux_len; ovs_be16 nsrcs; ovs_16aligned_be32 maddr; }; BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct igmpv3_record)); #define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */ #define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */ #define IGMPV2_HOST_MEMBERSHIP_REPORT 0x16 /* V2 version of 0x12 */ #define IGMP_HOST_LEAVE_MESSAGE 0x17 #define IGMPV3_HOST_MEMBERSHIP_REPORT 0x22 /* V3 version of 0x12 */ /* * IGMPv3 and MLDv2 use the same codes. */ #define IGMPV3_MODE_IS_INCLUDE 1 #define IGMPV3_MODE_IS_EXCLUDE 2 #define IGMPV3_CHANGE_TO_INCLUDE_MODE 3 #define IGMPV3_CHANGE_TO_EXCLUDE_MODE 4 #define IGMPV3_ALLOW_NEW_SOURCES 5 #define IGMPV3_BLOCK_OLD_SOURCES 6 #define SCTP_HEADER_LEN 12 struct sctp_header { ovs_be16 sctp_src; ovs_be16 sctp_dst; ovs_16aligned_be32 sctp_vtag; ovs_16aligned_be32 sctp_csum; }; BUILD_ASSERT_DECL(SCTP_HEADER_LEN == sizeof(struct sctp_header)); #define UDP_HEADER_LEN 8 struct udp_header { ovs_be16 udp_src; ovs_be16 udp_dst; ovs_be16 udp_len; ovs_be16 udp_csum; }; BUILD_ASSERT_DECL(UDP_HEADER_LEN == sizeof(struct udp_header)); #define TCP_FIN 0x001 #define TCP_SYN 0x002 #define TCP_RST 0x004 #define TCP_PSH 0x008 #define TCP_ACK 0x010 #define TCP_URG 0x020 #define TCP_ECE 0x040 #define TCP_CWR 0x080 #define TCP_NS 0x100 #define TCP_CTL(flags, offset) (htons((flags) | ((offset) << 12))) #define TCP_FLAGS(tcp_ctl) (ntohs(tcp_ctl) & 0x0fff) #define TCP_FLAGS_BE16(tcp_ctl) ((tcp_ctl) & htons(0x0fff)) #define TCP_OFFSET(tcp_ctl) (ntohs(tcp_ctl) >> 12) #define TCP_HEADER_LEN 20 struct tcp_header { ovs_be16 tcp_src; ovs_be16 tcp_dst; ovs_16aligned_be32 tcp_seq; ovs_16aligned_be32 tcp_ack; ovs_be16 tcp_ctl; ovs_be16 tcp_winsz; ovs_be16 tcp_csum; ovs_be16 tcp_urg; }; BUILD_ASSERT_DECL(TCP_HEADER_LEN == sizeof(struct tcp_header)); /* Connection states */ #define CS_NEW 0x01 #define CS_ESTABLISHED 0x02 #define CS_RELATED 0x04 #define CS_REPLY_DIR 0x08 #define CS_INVALID 0x10 #define CS_TRACKED 0x20 /* Undefined connection state bits. */ #define CS_SUPPORTED_MASK (CS_NEW | CS_ESTABLISHED | CS_RELATED \ | CS_INVALID | CS_REPLY_DIR | CS_TRACKED) #define CS_UNSUPPORTED_MASK (~(uint32_t)CS_SUPPORTED_MASK) #define ARP_HRD_ETHERNET 1 #define ARP_PRO_IP 0x0800 #define ARP_OP_REQUEST 1 #define ARP_OP_REPLY 2 #define ARP_OP_RARP 3 #define ARP_ETH_HEADER_LEN 28 struct arp_eth_header { /* Generic members. */ ovs_be16 ar_hrd; /* Hardware type. */ ovs_be16 ar_pro; /* Protocol type. */ uint8_t ar_hln; /* Hardware address length. */ uint8_t ar_pln; /* Protocol address length. */ ovs_be16 ar_op; /* Opcode. */ /* Ethernet+IPv4 specific members. */ struct eth_addr ar_sha; /* Sender hardware address. */ ovs_16aligned_be32 ar_spa; /* Sender protocol address. */ struct eth_addr ar_tha; /* Target hardware address. */ ovs_16aligned_be32 ar_tpa; /* Target protocol address. */ }; BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header)); #define IPV6_HEADER_LEN 40 /* Like struct in6_addr, but whereas that struct requires 32-bit alignment on * most implementations, this one only requires 16-bit alignment. */ union ovs_16aligned_in6_addr { ovs_be16 be16[8]; ovs_16aligned_be32 be32[4]; }; /* Like struct in6_hdr, but whereas that struct requires 32-bit alignment, this * one only requires 16-bit alignment. */ struct ovs_16aligned_ip6_hdr { union { struct ovs_16aligned_ip6_hdrctl { ovs_16aligned_be32 ip6_un1_flow; ovs_be16 ip6_un1_plen; uint8_t ip6_un1_nxt; uint8_t ip6_un1_hlim; } ip6_un1; uint8_t ip6_un2_vfc; } ip6_ctlun; union ovs_16aligned_in6_addr ip6_src; union ovs_16aligned_in6_addr ip6_dst; }; /* Like struct in6_frag, but whereas that struct requires 32-bit alignment, * this one only requires 16-bit alignment. */ struct ovs_16aligned_ip6_frag { uint8_t ip6f_nxt; uint8_t ip6f_reserved; ovs_be16 ip6f_offlg; ovs_16aligned_be32 ip6f_ident; }; #define ICMP6_HEADER_LEN 4 struct icmp6_header { uint8_t icmp6_type; uint8_t icmp6_code; ovs_be16 icmp6_cksum; }; BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header)); uint32_t packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *); /* Neighbor Discovery option field. * ND options are always a multiple of 8 bytes in size. */ #define ND_OPT_LEN 8 struct ovs_nd_opt { uint8_t nd_opt_type; /* Values defined in icmp6.h */ uint8_t nd_opt_len; /* in units of 8 octets (the size of this struct) */ struct eth_addr nd_opt_mac; /* Ethernet address in the case of SLL or TLL options */ }; BUILD_ASSERT_DECL(ND_OPT_LEN == sizeof(struct ovs_nd_opt)); /* Like struct nd_msg (from ndisc.h), but whereas that struct requires 32-bit * alignment, this one only requires 16-bit alignment. */ #define ND_MSG_LEN 24 struct ovs_nd_msg { struct icmp6_header icmph; ovs_16aligned_be32 rco_flags; union ovs_16aligned_in6_addr target; struct ovs_nd_opt options[0]; }; BUILD_ASSERT_DECL(ND_MSG_LEN == sizeof(struct ovs_nd_msg)); /* * Use the same struct for MLD and MLD2, naming members as the defined fields in * in the corresponding version of the protocol, though they are reserved in the * other one. */ #define MLD_HEADER_LEN 8 struct mld_header { uint8_t type; uint8_t code; ovs_be16 csum; ovs_be16 mrd; ovs_be16 ngrp; }; BUILD_ASSERT_DECL(MLD_HEADER_LEN == sizeof(struct mld_header)); #define MLD2_RECORD_LEN 20 struct mld2_record { uint8_t type; uint8_t aux_len; ovs_be16 nsrcs; union ovs_16aligned_in6_addr maddr; }; BUILD_ASSERT_DECL(MLD2_RECORD_LEN == sizeof(struct mld2_record)); #define MLD_QUERY 130 #define MLD_REPORT 131 #define MLD_DONE 132 #define MLD2_REPORT 143 /* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */ #define IPV6_LABEL_MASK 0x000fffff /* Example: * * char *string = "1 ::1 2"; * char ipv6_s[IPV6_SCAN_LEN + 1]; * struct in6_addr ipv6; * * if (ovs_scan(string, "%d"IPV6_SCAN_FMT"%d", &a, ipv6_s, &b) * && inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) { * ... * } */ #define IPV6_SCAN_FMT "%46[0123456789abcdefABCDEF:.]" #define IPV6_SCAN_LEN 46 extern const struct in6_addr in6addr_exact; #define IN6ADDR_EXACT_INIT { { { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff } } } extern const struct in6_addr in6addr_all_hosts; #define IN6ADDR_ALL_HOSTS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 } } } static inline bool ipv6_addr_equals(const struct in6_addr *a, const struct in6_addr *b) { #ifdef IN6_ARE_ADDR_EQUAL return IN6_ARE_ADDR_EQUAL(a, b); #else return !memcmp(a, b, sizeof(*a)); #endif } static inline bool ipv6_mask_is_any(const struct in6_addr *mask) { return ipv6_addr_equals(mask, &in6addr_any); } static inline bool ipv6_mask_is_exact(const struct in6_addr *mask) { return ipv6_addr_equals(mask, &in6addr_exact); } static inline bool ipv6_is_all_hosts(const struct in6_addr *addr) { return ipv6_addr_equals(addr, &in6addr_all_hosts); } static inline bool ipv6_addr_is_set(const struct in6_addr *addr) { return !ipv6_addr_equals(addr, &in6addr_any); } static inline bool ipv6_addr_is_multicast(const struct in6_addr *ip) { return ip->s6_addr[0] == 0xff; } static inline struct in6_addr in6_addr_mapped_ipv4(ovs_be32 ip4) { struct in6_addr ip6 = { .s6_addr = { [10] = 0xff, [11] = 0xff } }; memcpy(&ip6.s6_addr[12], &ip4, 4); return ip6; } static inline void in6_addr_set_mapped_ipv4(struct in6_addr *ip6, ovs_be32 ip4) { *ip6 = in6_addr_mapped_ipv4(ip4); } static inline ovs_be32 in6_addr_get_mapped_ipv4(const struct in6_addr *addr) { union ovs_16aligned_in6_addr *taddr = (void *) addr; if (IN6_IS_ADDR_V4MAPPED(addr)) { return get_16aligned_be32(&taddr->be32[3]); } else { return INADDR_ANY; } } static inline void in6_addr_solicited_node(struct in6_addr *addr, const struct in6_addr *ip6) { union ovs_16aligned_in6_addr *taddr = (void *) addr; memset(taddr->be16, 0, sizeof(taddr->be16)); taddr->be16[0] = htons(0xff02); taddr->be16[5] = htons(0x1); taddr->be16[6] = htons(0xff00); memcpy(&addr->s6_addr[13], &ip6->s6_addr[13], 3); } static inline void ipv6_multicast_to_ethernet(struct eth_addr *eth, const struct in6_addr *ip6) { eth->ea[0] = 0x33; eth->ea[1] = 0x33; eth->ea[2] = ip6->s6_addr[12]; eth->ea[3] = ip6->s6_addr[13]; eth->ea[4] = ip6->s6_addr[14]; eth->ea[5] = ip6->s6_addr[15]; } static inline bool dl_type_is_ip_any(ovs_be16 dl_type) { return dl_type == htons(ETH_TYPE_IP) || dl_type == htons(ETH_TYPE_IPV6); } /* Tunnel header */ /* GRE protocol header */ struct gre_base_hdr { ovs_be16 flags; ovs_be16 protocol; }; #define GRE_CSUM 0x8000 #define GRE_ROUTING 0x4000 #define GRE_KEY 0x2000 #define GRE_SEQ 0x1000 #define GRE_STRICT 0x0800 #define GRE_REC 0x0700 #define GRE_FLAGS 0x00F8 #define GRE_VERSION 0x0007 /* VXLAN protocol header */ struct vxlanhdr { ovs_16aligned_be32 vx_flags; ovs_16aligned_be32 vx_vni; }; #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ void ipv6_format_addr(const struct in6_addr *addr, struct ds *); void ipv6_format_mapped(const struct in6_addr *addr, struct ds *); void ipv6_format_masked(const struct in6_addr *addr, const struct in6_addr *mask, struct ds *); const char * ipv6_string_mapped(char *addr_str, const struct in6_addr *addr); struct in6_addr ipv6_addr_bitand(const struct in6_addr *src, const struct in6_addr *mask); struct in6_addr ipv6_create_mask(int mask); int ipv6_count_cidr_bits(const struct in6_addr *netmask); bool ipv6_is_cidr(const struct in6_addr *netmask); char *ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask); void *eth_compose(struct dp_packet *, const struct eth_addr eth_dst, const struct eth_addr eth_src, uint16_t eth_type, size_t size); void *snap_compose(struct dp_packet *, const struct eth_addr eth_dst, const struct eth_addr eth_src, unsigned int oui, uint16_t snap_type, size_t size); void packet_set_ipv4(struct dp_packet *, ovs_be32 src, ovs_be32 dst, uint8_t tos, uint8_t ttl); void packet_set_ipv6(struct dp_packet *, const ovs_be32 src[4], const ovs_be32 dst[4], uint8_t tc, ovs_be32 fl, uint8_t hlmit); void packet_set_tcp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst); void packet_set_udp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst); void packet_set_sctp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst); void packet_set_icmp(struct dp_packet *, uint8_t type, uint8_t code); void packet_set_nd(struct dp_packet *, const ovs_be32 target[4], const struct eth_addr sll, const struct eth_addr tll); void packet_format_tcp_flags(struct ds *, uint16_t); const char *packet_tcp_flag_to_string(uint32_t flag); void compose_arp(struct dp_packet *, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa); void compose_nd(struct dp_packet *, const struct eth_addr eth_src, struct in6_addr *, struct in6_addr *); uint32_t packet_csum_pseudoheader(const struct ip_header *); #endif /* packets.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-numa.c0000644000000000000000000000013213534540071016323 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 30 ctime=1567801424.985854931 openvswitch-2.5.9/lib/ovs-numa.c0000644000175000017500000003024213534540071020012 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* On non-Linux, these functions are defined inline in ovs-numa.h. */ #ifdef __linux__ #include #include "ovs-numa.h" #include #include #include #include #include #include #include #include "hash.h" #include "hmap.h" #include "list.h" #include "ovs-thread.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovs_numa); /* ovs-numa module * =============== * * This module stores the affinity information of numa nodes and cpu cores. * It also provides functions to bookkeep the pin of threads on cpu cores. * * It is assumed that the numa node ids and cpu core ids all start from 0 and * range continuously. So, for example, if 'ovs_numa_get_n_cores()' returns N, * user can assume core ids from 0 to N-1 are all valid and there is a * 'struct cpu_core' for each id. * * NOTE, this module should only be used by the main thread. * * NOTE, the assumption above will fail when cpu hotplug is used. In that * case ovs-numa will not function correctly. For now, add a TODO entry * for addressing it in the future. * * TODO: Fix ovs-numa when cpu hotplug is used. */ #define MAX_NUMA_NODES 128 /* numa node. */ struct numa_node { struct hmap_node hmap_node; /* In the 'all_numa_nodes'. */ struct ovs_list cores; /* List of cpu cores on the numa node. */ int numa_id; /* numa node id. */ }; /* Cpu core on a numa node. */ struct cpu_core { struct hmap_node hmap_node;/* In the 'all_cpu_cores'. */ struct ovs_list list_node; /* In 'numa_node->cores' list. */ struct numa_node *numa; /* numa node containing the core. */ unsigned core_id; /* Core id. */ bool available; /* If the core can be pinned. */ bool pinned; /* If a thread has been pinned to the core. */ }; /* Contains all 'struct numa_node's. */ static struct hmap all_numa_nodes = HMAP_INITIALIZER(&all_numa_nodes); /* Contains all 'struct cpu_core's. */ static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores); /* True if numa node and core info are correctly extracted. */ static bool found_numa_and_core; /* Returns true if 'str' contains all digits. Returns false otherwise. */ static bool contain_all_digits(const char *str) { return str[strspn(str, "0123456789")] == '\0'; } /* Discovers all numa nodes and the corresponding cpu cores. * Constructs the 'struct numa_node' and 'struct cpu_core'. */ static void discover_numa_and_core(void) { int n_cpus = 0; int i; for (i = 0; i < MAX_NUMA_NODES; i++) { DIR *dir; char* path; /* Constructs the path to node /sys/devices/system/nodeX. */ path = xasprintf("/sys/devices/system/node/node%d", i); dir = opendir(path); /* Creates 'struct numa_node' if the 'dir' is non-null. */ if (dir) { struct numa_node *n = xzalloc(sizeof *n); struct dirent *subdir; hmap_insert(&all_numa_nodes, &n->hmap_node, hash_int(i, 0)); list_init(&n->cores); n->numa_id = i; while ((subdir = readdir(dir)) != NULL) { if (!strncmp(subdir->d_name, "cpu", 3) && contain_all_digits(subdir->d_name + 3)){ struct cpu_core *c = xzalloc(sizeof *c); unsigned core_id; core_id = strtoul(subdir->d_name + 3, NULL, 10); hmap_insert(&all_cpu_cores, &c->hmap_node, hash_int(core_id, 0)); list_insert(&n->cores, &c->list_node); c->core_id = core_id; c->numa = n; c->available = true; n_cpus++; } } VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on NUMA node %d", list_size(&n->cores), n->numa_id); free(path); closedir(dir); } else { if (errno != ENOENT) { VLOG_WARN("opendir(%s) failed (%s)", path, ovs_strerror(errno)); } free(path); break; } } VLOG_INFO("Discovered %"PRIuSIZE" NUMA nodes and %d CPU cores", hmap_count(&all_numa_nodes), n_cpus); if (hmap_count(&all_numa_nodes) && hmap_count(&all_cpu_cores)) { found_numa_and_core = true; } } /* Gets 'struct cpu_core' by 'core_id'. */ static struct cpu_core* get_core_by_core_id(unsigned core_id) { struct cpu_core *core = NULL; if (ovs_numa_core_id_is_valid(core_id)) { core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores, hash_int(core_id, 0)), struct cpu_core, hmap_node); } return core; } /* Gets 'struct numa_node' by 'numa_id'. */ static struct numa_node* get_numa_by_numa_id(int numa_id) { struct numa_node *numa = NULL; if (ovs_numa_numa_id_is_valid(numa_id)) { numa = CONTAINER_OF(hmap_first_with_hash(&all_numa_nodes, hash_int(numa_id, 0)), struct numa_node, hmap_node); } return numa; } /* Extracts the numa node and core info from the 'sysfs'. */ void ovs_numa_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { discover_numa_and_core(); ovsthread_once_done(&once); } } bool ovs_numa_numa_id_is_valid(int numa_id) { return found_numa_and_core && numa_id < ovs_numa_get_n_numas(); } bool ovs_numa_core_id_is_valid(unsigned core_id) { return found_numa_and_core && core_id < ovs_numa_get_n_cores(); } bool ovs_numa_core_is_pinned(unsigned core_id) { struct cpu_core *core = get_core_by_core_id(core_id); if (core) { return core->pinned; } return false; } /* Returns the number of numa nodes. */ int ovs_numa_get_n_numas(void) { return found_numa_and_core ? hmap_count(&all_numa_nodes) : OVS_NUMA_UNSPEC; } /* Returns the number of cpu cores. */ int ovs_numa_get_n_cores(void) { return found_numa_and_core ? hmap_count(&all_cpu_cores) : OVS_CORE_UNSPEC; } /* Given 'core_id', returns the corresponding numa node id. Returns * OVS_NUMA_UNSPEC if 'core_id' is invalid. */ int ovs_numa_get_numa_id(unsigned core_id) { struct cpu_core *core = get_core_by_core_id(core_id); if (core) { return core->numa->numa_id; } return OVS_NUMA_UNSPEC; } /* Returns the number of cpu cores on numa node. Returns OVS_CORE_UNSPEC * if 'numa_id' is invalid. */ int ovs_numa_get_n_cores_on_numa(int numa_id) { struct numa_node *numa = get_numa_by_numa_id(numa_id); if (numa) { return list_size(&numa->cores); } return OVS_CORE_UNSPEC; } /* Returns the number of cpu cores that are available and unpinned * on numa node. Returns OVS_CORE_UNSPEC if 'numa_id' is invalid. */ int ovs_numa_get_n_unpinned_cores_on_numa(int numa_id) { struct numa_node *numa = get_numa_by_numa_id(numa_id); if (numa) { struct cpu_core *core; int count = 0; LIST_FOR_EACH(core, list_node, &numa->cores) { if (core->available && !core->pinned) { count++; } } return count; } return OVS_CORE_UNSPEC; } /* Given 'core_id', tries to pin that core. Returns true, if succeeds. * False, if the core has already been pinned, or if it is invalid or * not available. */ bool ovs_numa_try_pin_core_specific(unsigned core_id) { struct cpu_core *core = get_core_by_core_id(core_id); if (core) { if (core->available && !core->pinned) { core->pinned = true; return true; } } return false; } /* Searches through all cores for an unpinned and available core. Returns * the 'core_id' if found and sets the 'core->pinned' to true. Otherwise, * returns OVS_CORE_UNSPEC. */ unsigned ovs_numa_get_unpinned_core_any(void) { struct cpu_core *core; HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) { if (core->available && !core->pinned) { core->pinned = true; return core->core_id; } } return OVS_CORE_UNSPEC; } /* Searches through all cores on numa node with 'numa_id' for an * unpinned and available core. Returns the core_id if found and * sets the 'core->pinned' to true. Otherwise, returns OVS_CORE_UNSPEC. */ unsigned ovs_numa_get_unpinned_core_on_numa(int numa_id) { struct numa_node *numa = get_numa_by_numa_id(numa_id); if (numa) { struct cpu_core *core; LIST_FOR_EACH(core, list_node, &numa->cores) { if (core->available && !core->pinned) { core->pinned = true; return core->core_id; } } } return OVS_CORE_UNSPEC; } /* Unpins the core with 'core_id'. */ void ovs_numa_unpin_core(unsigned core_id) { struct cpu_core *core = get_core_by_core_id(core_id); if (core) { core->pinned = false; } } /* Given the 'numa_id', returns dump of all cores on the numa node. */ struct ovs_numa_dump * ovs_numa_dump_cores_on_numa(int numa_id) { struct ovs_numa_dump *dump = NULL; struct numa_node *numa = get_numa_by_numa_id(numa_id); if (numa) { struct cpu_core *core; dump = xmalloc(sizeof *dump); list_init(&dump->dump); LIST_FOR_EACH(core, list_node, &numa->cores) { struct ovs_numa_info *info = xmalloc(sizeof *info); info->numa_id = numa->numa_id; info->core_id = core->core_id; list_insert(&dump->dump, &info->list_node); } } return dump; } void ovs_numa_dump_destroy(struct ovs_numa_dump *dump) { struct ovs_numa_info *iter; LIST_FOR_EACH_POP (iter, list_node, &dump->dump) { free(iter); } free(dump); } /* Reads the cpu mask configuration from 'cmask' and sets the * 'available' of corresponding cores. For unspecified cores, * sets 'available' to false. */ void ovs_numa_set_cpu_mask(const char *cmask) { int core_id = 0; int i; if (!found_numa_and_core) { return; } /* If no mask specified, resets the 'available' to true for all cores. */ if (!cmask) { struct cpu_core *core; HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) { core->available = true; } return; } for (i = strlen(cmask) - 1; i >= 0; i--) { char hex = toupper(cmask[i]); int bin, j; if (hex >= '0' && hex <= '9') { bin = hex - '0'; } else if (hex >= 'A' && hex <= 'F') { bin = hex - 'A' + 10; } else { bin = 0; VLOG_WARN("Invalid cpu mask: %c", cmask[i]); } for (j = 0; j < 4; j++) { struct cpu_core *core; core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores, hash_int(core_id++, 0)), struct cpu_core, hmap_node); core->available = (bin >> j) & 0x1; if (core_id >= hmap_count(&all_cpu_cores)) { return; } } } /* For unspecified cores, sets 'available' to false. */ while (core_id < hmap_count(&all_cpu_cores)) { struct cpu_core *core; core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores, hash_int(core_id++, 0)), struct cpu_core, hmap_node); core->available = false; } } #endif /* __linux__ */ openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-conntrack.c0000644000000000000000000000013213534540071020202 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 30 ctime=1567801424.977854871 openvswitch-2.5.9/lib/netlink-conntrack.c0000644000175000017500000006763213534540071021706 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netlink-conntrack.h" #include #include #include #include #include #include #include "byte-order.h" #include "compiler.h" #include "dynamic-string.h" #include "netlink.h" #include "netlink-socket.h" #include "ofpbuf.h" #include "openvswitch/vlog.h" #include "poll-loop.h" #include "timeval.h" #include "unixctl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(netlink_conntrack); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); /* This module works only if conntrack modules and features are enabled in the * Linux kernel. This can be done from a root shell like this: * * $ modprobe ip_conntrack * $ sysctl -w net.netfilter.nf_conntrack_acct=1 * $ sysctl -w net.netfilter.nf_conntrack_timestamp=1 * * Also, if testing conntrack label feature without conntrack-aware OVS kernel * module, there must be a connlabel rule in iptables for space to be reserved * for the labels (see kernel source connlabel_mt_check()). Such a rule can be * inserted from a root shell like this: * * $ iptables -A INPUT -m conntrack -m connlabel \ * --ctstate NEW,ESTABLISHED,RELATED --label 127 -j ACCEPT */ /* Some attributes were introduced in later kernels: with these definitions * we should be able to compile userspace against Linux 2.6.32+. */ #define CTA_ZONE (CTA_SECMARK + 1) #define CTA_SECCTX (CTA_SECMARK + 2) #define CTA_TIMESTAMP (CTA_SECMARK + 3) #define CTA_MARK_MASK (CTA_SECMARK + 4) #define CTA_LABELS (CTA_SECMARK + 5) #define CTA_LABELS_MASK (CTA_SECMARK + 6) #define CTA_TIMESTAMP_START 1 #define CTA_TIMESTAMP_STOP 2 #define IPS_TEMPLATE_BIT 11 #define IPS_TEMPLATE (1 << IPS_TEMPLATE_BIT) #define IPS_UNTRACKED_BIT 12 #define IPS_UNTRACKED (1 << IPS_UNTRACKED_BIT) static const struct nl_policy nfnlgrp_conntrack_policy[] = { [CTA_TUPLE_ORIG] = { .type = NL_A_NESTED, .optional = false }, [CTA_TUPLE_REPLY] = { .type = NL_A_NESTED, .optional = false }, [CTA_ZONE] = { .type = NL_A_BE16, .optional = true }, [CTA_STATUS] = { .type = NL_A_BE32, .optional = false }, [CTA_TIMESTAMP] = { .type = NL_A_NESTED, .optional = true }, [CTA_TIMEOUT] = { .type = NL_A_BE32, .optional = true }, [CTA_COUNTERS_ORIG] = { .type = NL_A_NESTED, .optional = true }, [CTA_COUNTERS_REPLY] = { .type = NL_A_NESTED, .optional = true }, [CTA_PROTOINFO] = { .type = NL_A_NESTED, .optional = true }, [CTA_HELP] = { .type = NL_A_NESTED, .optional = true }, [CTA_MARK] = { .type = NL_A_BE32, .optional = true }, [CTA_SECCTX] = { .type = NL_A_NESTED, .optional = true }, [CTA_ID] = { .type = NL_A_BE32, .optional = false }, [CTA_USE] = { .type = NL_A_BE32, .optional = true }, [CTA_TUPLE_MASTER] = { .type = NL_A_NESTED, .optional = true }, [CTA_NAT_SEQ_ADJ_ORIG] = { .type = NL_A_NESTED, .optional = true }, [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NL_A_NESTED, .optional = true }, [CTA_LABELS] = { .type = NL_A_UNSPEC, .optional = true }, /* CTA_NAT_SRC, CTA_NAT_DST, CTA_TIMESTAMP, CTA_MARK_MASK, and * CTA_LABELS_MASK are not received from kernel. */ }; /* Declarations for conntrack netlink dumping. */ static void nl_msg_put_nfgenmsg(struct ofpbuf *msg, size_t expected_payload, int family, uint8_t subsystem, uint8_t cmd, uint32_t flags); static bool nl_ct_parse_header_policy(struct ofpbuf *buf, enum nl_ct_event_type *event_type, uint8_t *nfgen_family, struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]); static bool nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry, struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)], uint8_t nfgen_family); struct nl_ct_dump_state { struct nl_dump dump; struct ofpbuf buf; bool filter_zone; uint16_t zone; }; /* Conntrack netlink dumping. */ /* Initialize a conntrack netlink dump. */ int nl_ct_dump_start(struct nl_ct_dump_state **statep, const uint16_t *zone) { struct nl_ct_dump_state *state; *statep = state = xzalloc(sizeof *state); ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE); if (zone) { state->filter_zone = true; state->zone = *zone; } nl_msg_put_nfgenmsg(&state->buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, NLM_F_REQUEST); nl_dump_start(&state->dump, NETLINK_NETFILTER, &state->buf); ofpbuf_clear(&state->buf); return 0; } /* Receive the next 'entry' from the conntrack netlink dump with 'state'. * Returns 'EOF' when no more entries are available, 0 otherwise. 'entry' may * be uninitilized memory on entry, and must be uninitialized with * ct_dpif_entry_uninit() afterwards by the caller. In case the same 'entry' is * passed to this function again, the entry must also be uninitialized before * the next call. */ int nl_ct_dump_next(struct nl_ct_dump_state *state, struct ct_dpif_entry *entry) { struct ofpbuf buf; memset(entry, 0, sizeof *entry); for (;;) { struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]; enum nl_ct_event_type type; uint8_t nfgen_family; if (!nl_dump_next(&state->dump, &buf, &state->buf)) { return EOF; } if (!nl_ct_parse_header_policy(&buf, &type, &nfgen_family, attrs)) { continue; }; if (state->filter_zone) { uint16_t entry_zone = attrs[CTA_ZONE] ? ntohs(nl_attr_get_be16(attrs[CTA_ZONE])) : 0; if (entry_zone != state->zone) { continue; } } if (nl_ct_attrs_to_ct_dpif_entry(entry, attrs, nfgen_family)) { break; } ct_dpif_entry_uninit(entry); memset(entry, 0, sizeof *entry); /* Ignore the failed entry and get the next one. */ } ofpbuf_uninit(&buf); return 0; } /* End a conntrack netlink dump. */ int nl_ct_dump_done(struct nl_ct_dump_state *state) { int error = nl_dump_done(&state->dump); ofpbuf_uninit(&state->buf); free(state); return error; } /* Format conntrack event 'entry' of 'type' to 'ds'. */ void nl_ct_format_event_entry(const struct ct_dpif_entry *entry, enum nl_ct_event_type type, struct ds *ds, bool verbose, bool print_stats) { ds_put_format(ds, "%s ", type == NL_CT_EVENT_NEW ? "NEW" : type == NL_CT_EVENT_UPDATE ? "UPDATE" : type == NL_CT_EVENT_DELETE ? "DELETE" : "UNKNOWN"); ct_dpif_format_entry(entry, ds, verbose, print_stats); } int nl_ct_flush(void) { struct ofpbuf buf; int err; ofpbuf_init(&buf, NL_DUMP_BUFSIZE); nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST); err = nl_transact(NETLINK_NETFILTER, &buf, NULL); ofpbuf_uninit(&buf); /* Expectations are flushed automatically, because they do not * have a master connection anymore */ return err; } int nl_ct_flush_zone(uint16_t flush_zone) { /* Apparently, there's no netlink interface to flush a specific zone. * This code dumps every connection, checks the zone and eventually * delete the entry. * * This is race-prone, but it is better than using shell scripts. */ struct nl_dump dump; struct ofpbuf buf, reply, delete; ofpbuf_init(&buf, NL_DUMP_BUFSIZE); ofpbuf_init(&delete, NL_DUMP_BUFSIZE); nl_msg_put_nfgenmsg(&buf, 0, AF_UNSPEC, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, NLM_F_REQUEST); nl_dump_start(&dump, NETLINK_NETFILTER, &buf); ofpbuf_clear(&buf); for (;;) { struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]; enum nl_ct_event_type event_type; uint8_t nfgen_family; uint16_t zone = 0; if (!nl_dump_next(&dump, &reply, &buf)) { break; } if (!nl_ct_parse_header_policy(&reply, &event_type, &nfgen_family, attrs)) { continue; }; if (attrs[CTA_ZONE]) { zone = ntohs(nl_attr_get_be16(attrs[CTA_ZONE])); } if (zone != flush_zone) { /* The entry is not in the zone we're flushing. */ continue; } nl_msg_put_nfgenmsg(&delete, 0, nfgen_family, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_DELETE, NLM_F_REQUEST); nl_msg_put_be16(&delete, CTA_ZONE, htons(zone)); nl_msg_put_unspec(&delete, CTA_TUPLE_ORIG, attrs[CTA_TUPLE_ORIG] + 1, attrs[CTA_TUPLE_ORIG]->nla_len - NLA_HDRLEN); nl_msg_put_unspec(&delete, CTA_ID, attrs[CTA_ID] + 1, attrs[CTA_ID]->nla_len - NLA_HDRLEN); nl_transact(NETLINK_NETFILTER, &delete, NULL); ofpbuf_clear(&delete); } nl_dump_done(&dump); ofpbuf_uninit(&delete); ofpbuf_uninit(&buf); /* Expectations are flushed automatically, because they do not * have a master connection anymore */ return 0; } /* Conntrack netlink parsing. */ static bool nl_ct_parse_counters(struct nlattr *nla, struct ct_dpif_counters *counters) { static const struct nl_policy policy[] = { [CTA_COUNTERS_PACKETS] = { .type = NL_A_BE64, .optional = false }, [CTA_COUNTERS_BYTES] = { .type = NL_A_BE64, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { counters->packets = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_PACKETS])); counters->bytes = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_BYTES])); } else { VLOG_ERR_RL(&rl, "Could not parse nested counters. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_timestamp(struct nlattr *nla, struct ct_dpif_timestamp *timestamp) { static const struct nl_policy policy[] = { [CTA_TIMESTAMP_START] = { .type = NL_A_BE64, .optional = false }, [CTA_TIMESTAMP_STOP] = { .type = NL_A_BE64, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { timestamp->start = ntohll(nl_attr_get_be64(attrs[CTA_TIMESTAMP_START])); if (attrs[CTA_TIMESTAMP_STOP]) { timestamp->stop = ntohll(nl_attr_get_be64(attrs[CTA_TIMESTAMP_STOP])); } } else { VLOG_ERR_RL(&rl, "Could not parse nested timestamp. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_tuple_ip(struct nlattr *nla, struct ct_dpif_tuple *tuple) { static const struct nl_policy policy[] = { [CTA_IP_V4_SRC] = { .type = NL_A_BE32, .optional = true }, [CTA_IP_V4_DST] = { .type = NL_A_BE32, .optional = true }, [CTA_IP_V6_SRC] = { NL_POLICY_FOR(struct in6_addr), .optional = true }, [CTA_IP_V6_DST] = { NL_POLICY_FOR(struct in6_addr), .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { if (tuple->l3_type == AF_INET) { if (attrs[CTA_IP_V4_SRC]) { tuple->src.ip = nl_attr_get_be32(attrs[CTA_IP_V4_SRC]); } if (attrs[CTA_IP_V4_DST]) { tuple->dst.ip = nl_attr_get_be32(attrs[CTA_IP_V4_DST]); } } else if (tuple->l3_type == AF_INET6) { if (attrs[CTA_IP_V6_SRC]) { memcpy(&tuple->src.in6, nl_attr_get(attrs[CTA_IP_V6_SRC]), sizeof tuple->src.in6); } if (attrs[CTA_IP_V6_DST]) { memcpy(&tuple->dst.in6, nl_attr_get(attrs[CTA_IP_V6_DST]), sizeof tuple->dst.in6); } } else { VLOG_WARN_RL(&rl, "Unsupported IP protocol: %u.", tuple->l3_type); return false; } } else { VLOG_ERR_RL(&rl, "Could not parse nested tuple IP options. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_tuple_proto(struct nlattr *nla, struct ct_dpif_tuple *tuple) { static const struct nl_policy policy[] = { [CTA_PROTO_NUM] = { .type = NL_A_U8, .optional = false }, [CTA_PROTO_SRC_PORT] = { .type = NL_A_BE16, .optional = true }, [CTA_PROTO_DST_PORT] = { .type = NL_A_BE16, .optional = true }, [CTA_PROTO_ICMP_ID] = { .type = NL_A_BE16, .optional = true }, [CTA_PROTO_ICMP_TYPE] = { .type = NL_A_U8, .optional = true }, [CTA_PROTO_ICMP_CODE] = { .type = NL_A_U8, .optional = true }, [CTA_PROTO_ICMPV6_ID] = { .type = NL_A_BE16, .optional = true }, [CTA_PROTO_ICMPV6_TYPE] = { .type = NL_A_U8, .optional = true }, [CTA_PROTO_ICMPV6_CODE] = { .type = NL_A_U8, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { tuple->ip_proto = nl_attr_get_u8(attrs[CTA_PROTO_NUM]); if (tuple->l3_type == AF_INET && tuple->ip_proto == IPPROTO_ICMP) { if (!attrs[CTA_PROTO_ICMP_ID] || !attrs[CTA_PROTO_ICMP_TYPE] || !attrs[CTA_PROTO_ICMP_CODE]) { VLOG_ERR_RL(&rl, "Tuple ICMP data missing."); return false; } tuple->icmp_id = nl_attr_get_be16(attrs[CTA_PROTO_ICMP_ID]); tuple->icmp_type = nl_attr_get_u8(attrs[CTA_PROTO_ICMP_TYPE]); tuple->icmp_code = nl_attr_get_u8(attrs[CTA_PROTO_ICMP_CODE]); } else if (tuple->l3_type == AF_INET6 && tuple->ip_proto == IPPROTO_ICMPV6) { if (!attrs[CTA_PROTO_ICMPV6_ID] || !attrs[CTA_PROTO_ICMPV6_TYPE] || !attrs[CTA_PROTO_ICMPV6_CODE]) { VLOG_ERR_RL(&rl, "Tuple ICMPv6 data missing."); return false; } tuple->icmp_id = nl_attr_get_be16(attrs[CTA_PROTO_ICMPV6_ID]); tuple->icmp_type = nl_attr_get_u8(attrs[CTA_PROTO_ICMPV6_TYPE]); tuple->icmp_code = nl_attr_get_u8(attrs[CTA_PROTO_ICMPV6_CODE]); } else if (attrs[CTA_PROTO_SRC_PORT] && attrs[CTA_PROTO_DST_PORT]) { tuple->src_port = nl_attr_get_be16(attrs[CTA_PROTO_SRC_PORT]); tuple->dst_port = nl_attr_get_be16(attrs[CTA_PROTO_DST_PORT]); } else { /* Unsupported IPPROTO and no ports, leave them zeroed. * We have parsed the ip_proto, so this is not a total failure. */ VLOG_INFO_RL(&rl, "Unsupported L4 protocol: %u.", tuple->ip_proto); } } else { VLOG_ERR_RL(&rl, "Could not parse nested tuple protocol options. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_tuple(struct nlattr *nla, struct ct_dpif_tuple *tuple, uint16_t l3_type) { static const struct nl_policy policy[] = { [CTA_TUPLE_IP] = { .type = NL_A_NESTED, .optional = false }, [CTA_TUPLE_PROTO] = { .type = NL_A_NESTED, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); memset(tuple, 0, sizeof *tuple); if (parsed) { tuple->l3_type = l3_type; if (!nl_ct_parse_tuple_ip(attrs[CTA_TUPLE_IP], tuple) || !nl_ct_parse_tuple_proto(attrs[CTA_TUPLE_PROTO], tuple)) { struct ds ds; ds_init(&ds); ct_dpif_format_tuple(&ds, tuple, true); VLOG_ERR_RL(&rl, "Failed to parse tuple: %s", ds_cstr(&ds)); ds_destroy(&ds); memset(tuple, 0, sizeof *tuple); return false; } } else { VLOG_ERR_RL(&rl, "Could not parse nested tuple options. " "Possibly incompatible Linux kernel version."); } return parsed; } /* Translate netlink TCP state to CT_DPIF_TCP state. */ static uint8_t nl_ct_tcp_state_to_dpif(uint8_t state) { switch (state) { case TCP_CONNTRACK_NONE: return CT_DPIF_TCPS_CLOSED; case TCP_CONNTRACK_SYN_SENT: return CT_DPIF_TCPS_SYN_SENT; case TCP_CONNTRACK_SYN_SENT2: return CT_DPIF_TCPS_SYN_SENT; case TCP_CONNTRACK_SYN_RECV: return CT_DPIF_TCPS_SYN_RECV; case TCP_CONNTRACK_ESTABLISHED: return CT_DPIF_TCPS_ESTABLISHED; case TCP_CONNTRACK_FIN_WAIT: return CT_DPIF_TCPS_FIN_WAIT_1; case TCP_CONNTRACK_CLOSE_WAIT: return CT_DPIF_TCPS_CLOSE_WAIT; case TCP_CONNTRACK_LAST_ACK: return CT_DPIF_TCPS_LAST_ACK; case TCP_CONNTRACK_TIME_WAIT: return CT_DPIF_TCPS_TIME_WAIT; case TCP_CONNTRACK_CLOSE: return CT_DPIF_TCPS_CLOSING; default: return CT_DPIF_TCPS_CLOSED; } } static uint8_t ip_ct_tcp_flags_to_dpif(uint8_t flags) { uint8_t ret = 0; #define CT_DPIF_TCP_FLAG(FLAG) \ ret |= (flags & IP_CT_TCP_FLAG_##FLAG) ? CT_DPIF_TCPF_##FLAG : 0; CT_DPIF_TCP_FLAGS #undef CT_DPIF_STATUS_FLAG return ret; } static bool nl_ct_parse_protoinfo_tcp(struct nlattr *nla, struct ct_dpif_protoinfo *protoinfo) { static const struct nl_policy policy[] = { [CTA_PROTOINFO_TCP_STATE] = { .type = NL_A_U8, .optional = false }, [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NL_A_U8, .optional = false }, [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NL_A_U8, .optional = false }, [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .type = NL_A_U16, .optional = false }, [CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .type = NL_A_U16, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { const struct nf_ct_tcp_flags *flags_orig, *flags_reply; uint8_t state; protoinfo->proto = IPPROTO_TCP; state = nl_ct_tcp_state_to_dpif( nl_attr_get_u8(attrs[CTA_PROTOINFO_TCP_STATE])); /* The connection tracker keeps only one tcp state for the * connection, but our structures store a separate state for * each endpoint. Here we duplicate the state. */ protoinfo->tcp.state_orig = protoinfo->tcp.state_reply = state; protoinfo->tcp.wscale_orig = nl_attr_get_u8( attrs[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]); protoinfo->tcp.wscale_reply = nl_attr_get_u8( attrs[CTA_PROTOINFO_TCP_WSCALE_REPLY]); flags_orig = nl_attr_get_unspec(attrs[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL], sizeof *flags_orig); protoinfo->tcp.flags_orig = ip_ct_tcp_flags_to_dpif(flags_orig->flags); flags_reply = nl_attr_get_unspec(attrs[CTA_PROTOINFO_TCP_FLAGS_REPLY], sizeof *flags_reply); protoinfo->tcp.flags_reply = ip_ct_tcp_flags_to_dpif(flags_reply->flags); } else { VLOG_ERR_RL(&rl, "Could not parse nested TCP protoinfo options. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_protoinfo(struct nlattr *nla, struct ct_dpif_protoinfo *protoinfo) { /* These are mutually exclusive. */ static const struct nl_policy policy[] = { [CTA_PROTOINFO_TCP] = { .type = NL_A_NESTED, .optional = true }, [CTA_PROTOINFO_SCTP] = { .type = NL_A_NESTED, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); memset(protoinfo, 0, sizeof *protoinfo); if (parsed) { if (attrs[CTA_PROTOINFO_TCP]) { parsed = nl_ct_parse_protoinfo_tcp(attrs[CTA_PROTOINFO_TCP], protoinfo); } else if (attrs[CTA_PROTOINFO_SCTP]) { VLOG_WARN_RL(&rl, "SCTP protoinfo not yet supported!"); } else { VLOG_WARN_RL(&rl, "Empty protoinfo!"); } } else { VLOG_ERR_RL(&rl, "Could not parse nested protoinfo options. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_helper(struct nlattr *nla, struct ct_dpif_helper *helper) { static const struct nl_policy policy[] = { [CTA_HELP_NAME] = { .type = NL_A_STRING, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); memset(helper, 0, sizeof *helper); if (parsed) { helper->name = xstrdup(nl_attr_get_string(attrs[CTA_HELP_NAME])); } else { VLOG_ERR_RL(&rl, "Could not parse nested helper options. " "Possibly incompatible Linux kernel version."); } return parsed; } /* Translate netlink entry status flags to CT_DPIF_TCP status flags. */ static uint32_t ips_status_to_dpif_flags(uint32_t status) { uint32_t ret = 0; #define CT_DPIF_STATUS_FLAG(FLAG) \ ret |= (status & IPS_##FLAG) ? CT_DPIF_STATUS_##FLAG : 0; CT_DPIF_STATUS_FLAGS #undef CT_DPIF_STATUS_FLAG return ret; } static bool nl_ct_parse_header_policy(struct ofpbuf *buf, enum nl_ct_event_type *event_type, uint8_t *nfgen_family, struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]) { struct nlmsghdr *nlh; struct nfgenmsg *nfm; uint8_t type; nlh = ofpbuf_at(buf, 0, NLMSG_HDRLEN); nfm = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *nfm); if (!nfm) { VLOG_ERR_RL(&rl, "Received bad nfnl message (no nfgenmsg)."); return false; } if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_CTNETLINK) { VLOG_ERR_RL(&rl, "Received non-conntrack message (subsystem: %u).", NFNL_SUBSYS_ID(nlh->nlmsg_type)); return false; } if (nfm->version != NFNETLINK_V0) { VLOG_ERR_RL(&rl, "Received unsupported nfnetlink version (%u).", NFNL_MSG_TYPE(nfm->version)); return false; } if (!nl_policy_parse(buf, NLMSG_HDRLEN + sizeof *nfm, nfnlgrp_conntrack_policy, attrs, ARRAY_SIZE(nfnlgrp_conntrack_policy))) { VLOG_ERR_RL(&rl, "Received bad nfnl message (policy)."); return false; } type = NFNL_MSG_TYPE(nlh->nlmsg_type); *nfgen_family = nfm->nfgen_family; switch (type) { case IPCTNL_MSG_CT_NEW: *event_type = nlh->nlmsg_flags & NLM_F_CREATE ? NL_CT_EVENT_NEW : NL_CT_EVENT_UPDATE; break; case IPCTNL_MSG_CT_DELETE: *event_type = NL_CT_EVENT_DELETE; break; default: VLOG_ERR_RL(&rl, "Can't parse conntrack event type."); return false; } return true; } static bool nl_ct_attrs_to_ct_dpif_entry(struct ct_dpif_entry *entry, struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)], uint8_t nfgen_family) { if (!nl_ct_parse_tuple(attrs[CTA_TUPLE_ORIG], &entry->tuple_orig, nfgen_family)) { return false; } if (!nl_ct_parse_tuple(attrs[CTA_TUPLE_REPLY], &entry->tuple_reply, nfgen_family)) { return false; } if (attrs[CTA_COUNTERS_ORIG] && !nl_ct_parse_counters(attrs[CTA_COUNTERS_ORIG], &entry->counters_orig)) { return false; } if (attrs[CTA_COUNTERS_REPLY] && !nl_ct_parse_counters(attrs[CTA_COUNTERS_REPLY], &entry->counters_reply)) { return false; } if (attrs[CTA_TIMESTAMP] && !nl_ct_parse_timestamp(attrs[CTA_TIMESTAMP], &entry->timestamp)) { return false; } if (attrs[CTA_ID]) { entry->id = ntohl(nl_attr_get_be32(attrs[CTA_ID])); } if (attrs[CTA_ZONE]) { entry->zone = ntohs(nl_attr_get_be16(attrs[CTA_ZONE])); } if (attrs[CTA_STATUS]) { entry->status = ips_status_to_dpif_flags( ntohl(nl_attr_get_be32(attrs[CTA_STATUS]))); } if (attrs[CTA_TIMEOUT]) { entry->timeout = ntohl(nl_attr_get_be32(attrs[CTA_TIMEOUT])); } if (attrs[CTA_MARK]) { entry->mark = ntohl(nl_attr_get_be32(attrs[CTA_MARK])); } if (attrs[CTA_LABELS]) { memcpy(&entry->labels, nl_attr_get(attrs[CTA_LABELS]), MIN(sizeof entry->labels, nl_attr_get_size(attrs[CTA_LABELS]))); } if (attrs[CTA_PROTOINFO] && !nl_ct_parse_protoinfo(attrs[CTA_PROTOINFO], &entry->protoinfo)) { return false; } if (attrs[CTA_HELP] && !nl_ct_parse_helper(attrs[CTA_HELP], &entry->helper)) { return false; } if (attrs[CTA_TUPLE_MASTER] && !nl_ct_parse_tuple(attrs[CTA_TUPLE_MASTER], &entry->tuple_master, nfgen_family)) { return false; } return true; } bool nl_ct_parse_entry(struct ofpbuf *buf, struct ct_dpif_entry *entry, enum nl_ct_event_type *event_type) { struct nlattr *attrs[ARRAY_SIZE(nfnlgrp_conntrack_policy)]; uint8_t nfgen_family; memset(entry, 0, sizeof *entry); if (!nl_ct_parse_header_policy(buf, event_type, &nfgen_family, attrs)) { return false; }; if (!nl_ct_attrs_to_ct_dpif_entry(entry, attrs, nfgen_family)) { ct_dpif_entry_uninit(entry); memset(entry, 0, sizeof *entry); return false; } return true; } /* NetFilter utility functions. */ /* Puts a nlmsghdr and nfgenmsg at the beginning of 'msg', which must be * initially empty. 'expected_payload' should be an estimate of the number of * payload bytes to be supplied; if the size of the payload is unknown a value * of 0 is acceptable. * * Non-zero 'family' is the address family of items to get (e.g. AF_INET). * * 'flags' is a bit-mask that indicates what kind of request is being made. It * is often NLM_F_REQUEST indicating that a request is being made, commonly * or'd with NLM_F_ACK to request an acknowledgement. NLM_F_DUMP flag reguests * a dump of the table. * * 'subsystem' is a netfilter subsystem id, e.g., NFNL_SUBSYS_CTNETLINK. * * 'cmd' is an enumerated value specific to the 'subsystem'. * * Sets the new nlmsghdr's nlmsg_pid field to 0 for now. nl_sock_send() will * fill it in just before sending the message. * * nl_msg_put_nlmsghdr() should be used to compose Netlink messages that are * not NetFilter Netlink messages. */ static void nl_msg_put_nfgenmsg(struct ofpbuf *msg, size_t expected_payload, int family, uint8_t subsystem, uint8_t cmd, uint32_t flags) { struct nfgenmsg *nfm; nl_msg_put_nlmsghdr(msg, sizeof *nfm + expected_payload, subsystem << 8 | cmd, flags); ovs_assert(msg->size == NLMSG_HDRLEN); nfm = nl_msg_put_uninit(msg, sizeof *nfm); nfm->nfgen_family = family; nfm->version = NFNETLINK_V0; nfm->res_id = 0; } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-locked.h0000644000000000000000000000013213534540071020103 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.817853691 openvswitch-2.5.9/lib/ovs-atomic-locked.h0000644000175000017500000000312513534540071021572 0ustar00jpettitjpettit00000000000000/* This header implements atomic operation locking helpers. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #define OVS_ATOMIC_LOCKED_IMPL 1 void atomic_lock__(void *); void atomic_unlock__(void *); #define atomic_store_locked(DST, SRC) \ (atomic_lock__(DST), \ *(DST) = (SRC), \ atomic_unlock__(DST), \ (void) 0) #define atomic_read_locked(SRC, DST) \ (atomic_lock__(SRC), \ *(DST) = *(SRC), \ atomic_unlock__(SRC), \ (void) 0) /* XXX: Evaluates EXP multiple times. */ #define atomic_compare_exchange_locked(DST, EXP, SRC) \ (atomic_lock__(DST), \ (*(DST) == *(EXP) \ ? (*(DST) = (SRC), \ atomic_unlock__(DST), \ true) \ : (*(EXP) = *(DST), \ atomic_unlock__(DST), \ false))) #define atomic_op_locked_add += #define atomic_op_locked_sub -= #define atomic_op_locked_or |= #define atomic_op_locked_xor ^= #define atomic_op_locked_and &= #define atomic_op_locked(RMW, OP, OPERAND, ORIG) \ (atomic_lock__(RMW), \ *(ORIG) = *(RMW), \ *(RMW) atomic_op_locked_##OP (OPERAND), \ atomic_unlock__(RMW)) openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-error.c0000644000000000000000000000013213534540071017022 xustar0030 mtime=1567801401.553682286 30 atime=1567801402.093686252 30 ctime=1567801424.833853809 openvswitch-2.5.9/lib/ovsdb-error.c0000644000175000017500000001465313534540071020521 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb-error.h" #include #include "backtrace.h" #include "dynamic-string.h" #include "json.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_error); struct ovsdb_error { const char *tag; /* String for "error" member. */ char *details; /* String for "details" member. */ char *syntax; /* String for "syntax" member. */ int errno_; /* Unix errno value, 0 if none. */ }; static struct ovsdb_error * ovsdb_error_valist(const char *tag, const char *details, va_list args) { struct ovsdb_error *error = xmalloc(sizeof *error); error->tag = tag ? tag : "ovsdb error"; error->details = details ? xvasprintf(details, args) : NULL; error->syntax = NULL; error->errno_ = 0; return error; } struct ovsdb_error * ovsdb_error(const char *tag, const char *details, ...) { struct ovsdb_error *error; va_list args; va_start(args, details); error = ovsdb_error_valist(tag, details, args); va_end(args); return error; } struct ovsdb_error * ovsdb_io_error(int errno_, const char *details, ...) { struct ovsdb_error *error; va_list args; va_start(args, details); error = ovsdb_error_valist("I/O error", details, args); va_end(args); error->errno_ = errno_; return error; } struct ovsdb_error * ovsdb_syntax_error(const struct json *json, const char *tag, const char *details, ...) { struct ovsdb_error *error; va_list args; va_start(args, details); error = ovsdb_error_valist(tag ? tag : "syntax error", details, args); va_end(args); if (json) { /* XXX this is much too much information in some cases */ error->syntax = json_to_string(json, JSSF_SORT); } return error; } struct ovsdb_error * ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...) { va_list args; char *msg; va_start(args, details); msg = xvasprintf(details, args); va_end(args); if (error->details) { char *new = xasprintf("%s: %s", msg, error->details); free(error->details); error->details = new; free(msg); } else { error->details = msg; } return error; } /* Returns an ovsdb_error that represents an internal error for file name * 'file' and line number 'line', with 'details' (formatted as with printf()) * as the associated message. The caller is responsible for freeing the * returned error. * * If 'inner_error' is nonnull then the returned error is wrapped around * 'inner_error'. Takes ownership of 'inner_error'. */ struct ovsdb_error * ovsdb_internal_error(struct ovsdb_error *inner_error, const char *file, int line, const char *details, ...) { struct ds ds = DS_EMPTY_INITIALIZER; struct backtrace backtrace; struct ovsdb_error *error; va_list args; ds_put_format(&ds, "%s:%d:", file, line); if (details) { ds_put_char(&ds, ' '); va_start(args, details); ds_put_format_valist(&ds, details, args); va_end(args); } backtrace_capture(&backtrace); if (backtrace.n_frames) { int i; ds_put_cstr(&ds, " (backtrace:"); for (i = 0; i < backtrace.n_frames; i++) { ds_put_format(&ds, " 0x%08"PRIxPTR, backtrace.frames[i]); } ds_put_char(&ds, ')'); } ds_put_format(&ds, " (%s %s)", program_name, VERSION); if (inner_error) { char *s = ovsdb_error_to_string(inner_error); ds_put_format(&ds, " (generated from: %s)", s); free(s); ovsdb_error_destroy(inner_error); } error = ovsdb_error("internal error", "%s", ds_cstr(&ds)); ds_destroy(&ds); return error; } void ovsdb_error_destroy(struct ovsdb_error *error) { if (error) { free(error->details); free(error->syntax); free(error); } } struct ovsdb_error * ovsdb_error_clone(const struct ovsdb_error *old) { if (old) { struct ovsdb_error *new = xmalloc(sizeof *new); new->tag = old->tag; new->details = old->details ? xstrdup(old->details) : NULL; new->syntax = old->syntax ? xstrdup(old->syntax) : NULL; new->errno_ = old->errno_; return new; } else { return NULL; } } struct json * ovsdb_error_to_json(const struct ovsdb_error *error) { struct json *json = json_object_create(); json_object_put_string(json, "error", error->tag); if (error->details) { json_object_put_string(json, "details", error->details); } if (error->syntax) { json_object_put_string(json, "syntax", error->syntax); } if (error->errno_) { json_object_put_string(json, "io-error", ovs_retval_to_string(error->errno_)); } return json; } char * ovsdb_error_to_string(const struct ovsdb_error *error) { struct ds ds = DS_EMPTY_INITIALIZER; if (error->syntax) { ds_put_format(&ds, "syntax \"%s\": ", error->syntax); } ds_put_cstr(&ds, error->tag); if (error->details) { ds_put_format(&ds, ": %s", error->details); } if (error->errno_) { ds_put_format(&ds, " (%s)", ovs_retval_to_string(error->errno_)); } return ds_steal_cstr(&ds); } const char * ovsdb_error_get_tag(const struct ovsdb_error *error) { return error->tag; } /* If 'error' is nonnull, logs it as an error and frees it. To be used in * situations where an error should never occur, but an 'ovsdb_error *' gets * passed back anyhow. */ void ovsdb_error_assert(struct ovsdb_error *error) { if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); char *s = ovsdb_error_to_string(error); VLOG_ERR_RL(&rl, "unexpected ovsdb error: %s", s); free(s); ovsdb_error_destroy(error); } } openvswitch-2.5.9/lib/PaxHeaders.82075/svec.h0000644000000000000000000000013213534540071015523 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.901854311 openvswitch-2.5.9/lib/svec.h0000644000175000017500000000506513534540071017217 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SVEC_H #define SVEC_H 1 #include #include #ifdef __cplusplus extern "C" { #endif struct svec { char **names; size_t n; size_t allocated; }; #define SVEC_EMPTY_INITIALIZER { NULL, 0, 0 } void svec_init(struct svec *); void svec_clone(struct svec *, const struct svec *); void svec_destroy(struct svec *); void svec_clear(struct svec *); bool svec_is_empty(const struct svec *); void svec_add(struct svec *, const char *); void svec_add_nocopy(struct svec *, char *); void svec_del(struct svec *, const char *); void svec_append(struct svec *, const struct svec *); void svec_terminate(struct svec *); void svec_sort(struct svec *); void svec_sort_unique(struct svec *); void svec_unique(struct svec *); void svec_compact(struct svec *); void svec_diff(const struct svec *a, const struct svec *b, struct svec *a_only, struct svec *both, struct svec *b_only); bool svec_contains(const struct svec *, const char *); size_t svec_find(const struct svec *, const char *); bool svec_is_sorted(const struct svec *); bool svec_is_unique(const struct svec *); const char *svec_get_duplicate(const struct svec *); void svec_swap(struct svec *a, struct svec *b); void svec_print(const struct svec *svec, const char *title); void svec_parse_words(struct svec *svec, const char *words); bool svec_equal(const struct svec *, const struct svec *); char *svec_join(const struct svec *, const char *delimiter, const char *terminator); const char *svec_back(const struct svec *); void svec_pop_back(struct svec *); /* Iterates over the names in SVEC, assigning each name in turn to NAME and its * index to INDEX. */ #define SVEC_FOR_EACH(INDEX, NAME, SVEC) \ for ((INDEX) = 0; \ ((INDEX) < (SVEC)->n \ ? (NAME) = (SVEC)->names[INDEX], 1 \ : 0); \ (INDEX)++) #ifdef __cplusplus } #endif #endif /* svec.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ct-dpif.c0000644000000000000000000000013213534540071016104 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.689852748 openvswitch-2.5.9/lib/ct-dpif.c0000644000175000017500000003040713534540071017576 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "ct-dpif.h" #include "dpif-provider.h" /* Declarations for conntrack entry formatting. */ struct flags { uint32_t flag; const char *name; }; static void ct_dpif_format_ipproto(struct ds *, uint16_t ipproto); static void ct_dpif_format_counters(struct ds *, const struct ct_dpif_counters *); static void ct_dpif_format_timestamp(struct ds *, const struct ct_dpif_timestamp *); static void ct_dpif_format_flags(struct ds *, const char *title, uint32_t flags, const struct flags *); static void ct_dpif_format_protoinfo(struct ds *, const char *title, const struct ct_dpif_protoinfo *, bool verbose); static void ct_dpif_format_helper(struct ds *, const char *title, const struct ct_dpif_helper *); static const struct flags ct_dpif_status_flags[] = { #define CT_DPIF_STATUS_FLAG(FLAG) { CT_DPIF_STATUS_##FLAG, #FLAG }, CT_DPIF_STATUS_FLAGS #undef CT_DPIF_STATUS_FLAG { 0, NULL } /* End marker. */ }; /* Dumping */ /* Start dumping the entries from the connection tracker used by 'dpif'. * * 'dump' must be the address of a pointer to a struct ct_dpif_dump_state, * which should be passed (unaltered) to ct_dpif_dump_{next,done}(). * * If 'zone' is not NULL, it should point to an integer identifing a * conntrack zone to which the dump will be limited. If it is NULL, * conntrack entries from all zones will be dumped. * * If there has been a problem the function returns a non-zero value * that represents the error. Otherwise it returns zero. */ int ct_dpif_dump_start(struct dpif *dpif, struct ct_dpif_dump_state **dump, const uint16_t *zone) { int err; err = (dpif->dpif_class->ct_dump_start ? dpif->dpif_class->ct_dump_start(dpif, dump, zone) : EOPNOTSUPP); if (!err) { (*dump)->dpif = dpif; } return err; } /* Dump one connection from a tracker, and put it in 'entry'. * * 'dump' should have been initialized by ct_dpif_dump_start(). * * The function returns 0, if an entry has been dumped succesfully. * Otherwise it returns a non-zero value which can be: * - EOF: meaning that there are no more entries to dump. * - an error value. * In both cases, the user should call ct_dpif_dump_done(). */ int ct_dpif_dump_next(struct ct_dpif_dump_state *dump, struct ct_dpif_entry *entry) { struct dpif *dpif = dump->dpif; return (dpif->dpif_class->ct_dump_next ? dpif->dpif_class->ct_dump_next(dpif, dump, entry) : EOPNOTSUPP); } /* Free resources used by 'dump' */ int ct_dpif_dump_done(struct ct_dpif_dump_state *dump) { struct dpif *dpif = dump->dpif; return (dpif->dpif_class->ct_dump_done ? dpif->dpif_class->ct_dump_done(dpif, dump) : EOPNOTSUPP); } /* Flush the entries in the connection tracker used by 'dpif'. * * If 'zone' is not NULL, flush only the entries in '*zone'. */ int ct_dpif_flush(struct dpif *dpif, const uint16_t *zone) { return (dpif->dpif_class->ct_flush ? dpif->dpif_class->ct_flush(dpif, zone) : EOPNOTSUPP); } void ct_dpif_entry_uninit(struct ct_dpif_entry *entry) { if (entry) { if (entry->helper.name) { free(entry->helper.name); } } } void ct_dpif_format_entry(const struct ct_dpif_entry *entry, struct ds *ds, bool verbose, bool print_stats) { ct_dpif_format_ipproto(ds, entry->tuple_orig.ip_proto); ds_put_cstr(ds, ",orig=("); ct_dpif_format_tuple(ds, &entry->tuple_orig, verbose); if (print_stats) { ct_dpif_format_counters(ds, &entry->counters_orig); } ds_put_cstr(ds, ")"); ds_put_cstr(ds, ",reply=("); ct_dpif_format_tuple(ds, &entry->tuple_reply, verbose); if (print_stats) { ct_dpif_format_counters(ds, &entry->counters_reply); } ds_put_cstr(ds, ")"); if (print_stats) { ct_dpif_format_timestamp(ds, &entry->timestamp); } if (verbose) { ds_put_format(ds, ",id=%"PRIu32, entry->id); } if (entry->zone) { ds_put_format(ds, ",zone=%"PRIu16, entry->zone); } if (verbose) { ct_dpif_format_flags(ds, ",status=", entry->status, ct_dpif_status_flags); } if (print_stats) { ds_put_format(ds, ",timeout=%"PRIu32, entry->timeout); } if (entry->mark) { ds_put_format(ds, ",mark=%"PRIu32, entry->mark); } if (!ovs_u128_is_zero(&entry->labels)) { ovs_be128 value; ds_put_cstr(ds, ",labels="); value = hton128(entry->labels); ds_put_hex(ds, &value, sizeof value); } ct_dpif_format_protoinfo(ds, ",protoinfo=", &entry->protoinfo, verbose); ct_dpif_format_helper(ds, ",helper=", &entry->helper); if (verbose && entry->tuple_master.l3_type != 0) { ds_put_cstr(ds, ",master=("); ct_dpif_format_tuple(ds, &entry->tuple_master, verbose); ds_put_cstr(ds, ")"); } } static void ct_dpif_format_ipproto(struct ds *ds, uint16_t ipproto) { const char *name; name = (ipproto == IPPROTO_ICMP) ? "icmp" : (ipproto == IPPROTO_ICMPV6) ? "icmpv6" : (ipproto == IPPROTO_TCP) ? "tcp" : (ipproto == IPPROTO_UDP) ? "udp" : (ipproto == IPPROTO_SCTP) ? "sctp" : NULL; if (name) { ds_put_cstr(ds, name); } else { ds_put_format(ds, "%u", ipproto); } } static void ct_dpif_format_counters(struct ds *ds, const struct ct_dpif_counters *counters) { if (counters->packets || counters->bytes) { ds_put_format(ds, ",packets=%"PRIu64",bytes=%"PRIu64, counters->packets, counters->bytes); } } static void ct_dpif_format_timestamp(struct ds *ds, const struct ct_dpif_timestamp *timestamp) { if (timestamp->start || timestamp->stop) { ds_put_strftime_msec(ds, ",start=%Y-%m-%dT%H:%M:%S.###", timestamp->start / UINT64_C(1000000), false); if (timestamp->stop) { ds_put_strftime_msec(ds, ",stop=%Y-%m-%dT%H:%M:%S.###", timestamp->stop / UINT64_C(1000000), false); } } } static void ct_dpif_format_tuple_icmp(struct ds *ds, const struct ct_dpif_tuple *tuple, bool verbose) { if (verbose) { ds_put_format(ds, ",id=%u,type=%u,code=%u", ntohs(tuple->icmp_id), tuple->icmp_type, tuple->icmp_code); } else { ds_put_format(ds, ",id=%u", ntohs(tuple->icmp_id)); } } static void ct_dpif_format_tuple_tp(struct ds *ds, const struct ct_dpif_tuple *tuple) { ds_put_format(ds, ",sport=%u,dport=%u", ntohs(tuple->src_port), ntohs(tuple->dst_port)); } void ct_dpif_format_tuple(struct ds *ds, const struct ct_dpif_tuple *tuple, bool verbose) { if (tuple->l3_type == AF_INET) { ds_put_format(ds, "src="IP_FMT",dst="IP_FMT, IP_ARGS(tuple->src.ip), IP_ARGS(tuple->dst.ip)); } else if (tuple->l3_type == AF_INET6) { ds_put_cstr(ds, "src="); ipv6_format_addr(&tuple->src.in6, ds); ds_put_cstr(ds, ",dst="); ipv6_format_addr(&tuple->dst.in6, ds); } else { ds_put_format(ds, "Unsupported address family: %u. HEX:\n", tuple->l3_type); ds_put_hex_dump(ds, tuple, sizeof *tuple, 0, false); return; } if (tuple->ip_proto == IPPROTO_ICMP || tuple->ip_proto == IPPROTO_ICMPV6) { ct_dpif_format_tuple_icmp(ds, tuple, verbose); } else { ct_dpif_format_tuple_tp(ds, tuple); } } static void ct_dpif_format_flags(struct ds *ds, const char *title, uint32_t flags, const struct flags *table) { if (title) { ds_put_cstr(ds, title); } for (; table->name; table++) { if (flags & table->flag) { ds_put_format(ds, "%s|", table->name); } } ds_chomp(ds, '|'); } static const struct flags tcp_flags[] = { #define CT_DPIF_TCP_FLAG(FLAG) { CT_DPIF_TCPF_##FLAG, #FLAG }, CT_DPIF_TCP_FLAGS #undef CT_DPIF_TCP_FLAG { 0, NULL } /* End marker. */ }; const char *ct_dpif_tcp_state_string[] = { #define CT_DPIF_TCP_STATE(STATE) [CT_DPIF_TCPS_##STATE] = #STATE, CT_DPIF_TCP_STATES #undef CT_DPIF_TCP_STATE }; static void ct_dpif_format_enum__(struct ds *ds, const char *title, unsigned int state, const char *names[], unsigned int max) { if (title) { ds_put_cstr(ds, title); } if (state < max) { ds_put_cstr(ds, names[state]); } else { ds_put_format(ds, "[%u]", state); } } #define ct_dpif_format_enum(DS, TITLE, STATE, NAMES) \ ct_dpif_format_enum__((DS), (TITLE), (STATE), (NAMES), ARRAY_SIZE(NAMES)) static uint8_t coalesce_tcp_state(uint8_t state) { /* The Linux kernel connection tracker and the userspace view the * tcp states differently in some situations. If we're formatting * the entry without being verbose, it is worth to adjust the * differences, to ease writing testcases. */ switch (state) { case CT_DPIF_TCPS_FIN_WAIT_2: return CT_DPIF_TCPS_TIME_WAIT; case CT_DPIF_TCPS_SYN_RECV: return CT_DPIF_TCPS_ESTABLISHED; default: return state; } } static void ct_dpif_format_protoinfo_tcp(struct ds *ds, const struct ct_dpif_protoinfo *protoinfo) { uint8_t tcp_state; /* We keep two separate tcp states, but we print just one. The Linux * kernel connection tracker internally keeps only one state, so * 'state_orig' and 'state_reply', will be the same. */ tcp_state = MAX(protoinfo->tcp.state_orig, protoinfo->tcp.state_reply); tcp_state = coalesce_tcp_state(tcp_state); ct_dpif_format_enum(ds, "state=", tcp_state, ct_dpif_tcp_state_string); } static void ct_dpif_format_protoinfo_tcp_verbose(struct ds *ds, const struct ct_dpif_protoinfo *protoinfo) { ct_dpif_format_enum(ds, "state_orig=", protoinfo->tcp.state_orig, ct_dpif_tcp_state_string); ct_dpif_format_enum(ds, ",state_reply=", protoinfo->tcp.state_reply, ct_dpif_tcp_state_string); if (protoinfo->tcp.wscale_orig || protoinfo->tcp.wscale_reply) { ds_put_format(ds, ",wscale_orig=%u,wscale_reply=%u", protoinfo->tcp.wscale_orig, protoinfo->tcp.wscale_reply); } ct_dpif_format_flags(ds, ",flags_orig=", protoinfo->tcp.flags_orig, tcp_flags); ct_dpif_format_flags(ds, ",flags_reply=", protoinfo->tcp.flags_reply, tcp_flags); } static void ct_dpif_format_protoinfo(struct ds *ds, const char *title, const struct ct_dpif_protoinfo *protoinfo, bool verbose) { if (protoinfo->proto != 0) { if (title) { ds_put_format(ds, "%s(", title); } switch (protoinfo->proto) { case IPPROTO_TCP: if (verbose) { ct_dpif_format_protoinfo_tcp_verbose(ds, protoinfo); } else { ct_dpif_format_protoinfo_tcp(ds, protoinfo); } break; } if (title) { ds_put_cstr(ds, ")"); } } } static void ct_dpif_format_helper(struct ds *ds, const char *title, const struct ct_dpif_helper *helper) { if (helper->name) { if (title) { ds_put_cstr(ds, title); } ds_put_cstr(ds, helper->name); } } openvswitch-2.5.9/lib/PaxHeaders.82075/cfm.c0000644000000000000000000000013013534540071015321 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 28 ctime=1567801424.6698526 openvswitch-2.5.9/lib/cfm.c0000644000175000017500000010500013534540071017005 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "cfm.h" #include #include #include #include "byte-order.h" #include "connectivity.h" #include "dp-packet.h" #include "dynamic-string.h" #include "flow.h" #include "hash.h" #include "hmap.h" #include "netdev.h" #include "ovs-atomic.h" #include "packets.h" #include "poll-loop.h" #include "random.h" #include "seq.h" #include "timer.h" #include "timeval.h" #include "unixctl.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(cfm); #define CFM_MAX_RMPS 256 /* Ethernet destination address of CCM packets. */ static const struct eth_addr eth_addr_ccm = { { { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x30 } } }; static const struct eth_addr eth_addr_ccm_x = { { { 0x01, 0x23, 0x20, 0x00, 0x00, 0x30 } } }; #define ETH_TYPE_CFM 0x8902 /* A 'ccm' represents a Continuity Check Message from the 802.1ag * specification. Continuity Check Messages are broadcast periodically so that * hosts can determine whom they have connectivity to. * * The minimum length of a CCM as specified by IEEE 802.1ag is 75 bytes. * Previous versions of Open vSwitch generated 74-byte CCM messages, so we * accept such messages too. */ #define CCM_LEN 75 #define CCM_ACCEPT_LEN 74 #define CCM_MAID_LEN 48 #define CCM_OPCODE 1 /* CFM message opcode meaning CCM. */ #define CCM_RDI_MASK 0x80 #define CFM_HEALTH_INTERVAL 6 OVS_PACKED( struct ccm { uint8_t mdlevel_version; /* MD Level and Version */ uint8_t opcode; uint8_t flags; uint8_t tlv_offset; ovs_be32 seq; ovs_be16 mpid; uint8_t maid[CCM_MAID_LEN]; /* Defined by ITU-T Y.1731 should be zero */ ovs_be16 interval_ms_x; /* Transmission interval in ms. */ ovs_be64 mpid64; /* MPID in extended mode. */ uint8_t opdown; /* Operationally down. */ uint8_t zero[5]; /* TLV space. */ uint8_t end_tlv; }); BUILD_ASSERT_DECL(CCM_LEN == sizeof(struct ccm)); struct cfm { const char *name; /* Name of this CFM object. */ struct hmap_node hmap_node; /* Node in all_cfms list. */ struct netdev *netdev; uint64_t rx_packets; /* Packets received by 'netdev'. */ uint64_t mpid; bool demand; /* Demand mode. */ bool booted; /* A full fault interval has occurred. */ enum cfm_fault_reason fault; /* Connectivity fault status. */ enum cfm_fault_reason recv_fault; /* Bit mask of faults occurring on receive. */ bool opup; /* Operational State. */ bool remote_opup; /* Remote Operational State. */ int fault_override; /* Manual override of 'fault' status. Ignored if negative. */ uint32_t seq; /* The sequence number of our last CCM. */ uint8_t ccm_interval; /* The CCM transmission interval. */ int ccm_interval_ms; /* 'ccm_interval' in milliseconds. */ uint16_t ccm_vlan; /* Vlan tag of CCM PDUs. CFM_RANDOM_VLAN if random. */ uint8_t ccm_pcp; /* Priority of CCM PDUs. */ uint8_t maid[CCM_MAID_LEN]; /* The MAID of this CFM. */ struct timer tx_timer; /* Send CCM when expired. */ struct timer fault_timer; /* Check for faults when expired. */ struct hmap remote_mps; /* Remote MPs. */ /* Result of cfm_get_remote_mpids(). Updated only during fault check to * avoid flapping. */ uint64_t *rmps_array; /* Cache of remote_mps. */ size_t rmps_array_len; /* Number of rmps in 'rmps_array'. */ int health; /* Percentage of the number of CCM frames received. */ int health_interval; /* Number of fault_intervals since health was recomputed. */ long long int last_tx; /* Last CCM transmission time. */ /* These bools are atomic to allow readers to check their values * without taking 'mutex'. Such readers do not assume the values they * read are synchronized with any other members. */ atomic_bool check_tnl_key; /* Verify the tunnel key of inbound packets? */ atomic_bool extended; /* Extended mode. */ struct ovs_refcount ref_cnt; uint64_t flap_count; /* Count the flaps since boot. */ /* True when the variables returned by cfm_get_*() are changed * since last check. */ bool status_changed; /* When 'cfm->demand' is set, at least one ccm is required to be received * every 100 * cfm_interval. If ccm is not received within this interval, * even if data packets are received, the cfm fault will be set. */ struct timer demand_rx_ccm_t; }; /* Remote MPs represent foreign network entities that are configured to have * the same MAID as this CFM instance. */ struct remote_mp { uint64_t mpid; /* The Maintenance Point ID of this 'remote_mp'. */ struct hmap_node node; /* Node in 'remote_mps' map. */ bool recv; /* CCM was received since last fault check. */ bool opup; /* Operational State. */ uint32_t seq; /* Most recently received sequence number. */ uint8_t num_health_ccm; /* Number of received ccm frames every CFM_HEALTH_INTERVAL * 'fault_interval'. */ long long int last_rx; /* Last CCM reception time. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 30); static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct hmap all_cfms__ = HMAP_INITIALIZER(&all_cfms__); static struct hmap *const all_cfms OVS_GUARDED_BY(mutex) = &all_cfms__; static unixctl_cb_func cfm_unixctl_show; static unixctl_cb_func cfm_unixctl_set_fault; static uint64_t cfm_rx_packets(const struct cfm *cfm) OVS_REQUIRES(mutex) { struct netdev_stats stats; if (!netdev_get_stats(cfm->netdev, &stats)) { return stats.rx_packets; } else { return 0; } } static struct eth_addr cfm_ccm_addr(struct cfm *cfm) { bool extended; atomic_read_relaxed(&cfm->extended, &extended); return extended ? eth_addr_ccm_x : eth_addr_ccm; } /* Returns the string representation of the given cfm_fault_reason 'reason'. */ const char * cfm_fault_reason_to_str(int reason) { switch (reason) { #define CFM_FAULT_REASON(NAME, STR) case CFM_FAULT_##NAME: return #STR; CFM_FAULT_REASONS #undef CFM_FAULT_REASON default: return ""; } } static void ds_put_cfm_fault(struct ds *ds, int fault) { int i; for (i = 0; i < CFM_FAULT_N_REASONS; i++) { int reason = 1 << i; if (fault & reason) { ds_put_format(ds, "%s ", cfm_fault_reason_to_str(reason)); } } ds_chomp(ds, ' '); } static void cfm_generate_maid(struct cfm *cfm) OVS_REQUIRES(mutex) { const char *ovs_md_name = "ovs"; const char *ovs_ma_name = "ovs"; uint8_t *ma_p; size_t md_len, ma_len; memset(cfm->maid, 0, CCM_MAID_LEN); md_len = strlen(ovs_md_name); ma_len = strlen(ovs_ma_name); ovs_assert(md_len && ma_len && md_len + ma_len + 4 <= CCM_MAID_LEN); cfm->maid[0] = 4; /* MD name string format. */ cfm->maid[1] = md_len; /* MD name size. */ memcpy(&cfm->maid[2], ovs_md_name, md_len); /* MD name. */ ma_p = cfm->maid + 2 + md_len; ma_p[0] = 2; /* MA name string format. */ ma_p[1] = ma_len; /* MA name size. */ memcpy(&ma_p[2], ovs_ma_name, ma_len); /* MA name. */ } static int ccm_interval_to_ms(uint8_t interval) { switch (interval) { case 0: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */ case 1: return 3; /* Not recommended due to timer resolution. */ case 2: return 10; /* Not recommended due to timer resolution. */ case 3: return 100; case 4: return 1000; case 5: return 10000; case 6: return 60000; case 7: return 600000; default: OVS_NOT_REACHED(); /* Explicitly not supported by 802.1ag. */ } OVS_NOT_REACHED(); } static long long int cfm_fault_interval(struct cfm *cfm) OVS_REQUIRES(mutex) { /* According to the 802.1ag specification we should assume every other MP * with the same MAID has the same transmission interval that we have. If * an MP has a different interval, cfm_process_heartbeat will register it * as a fault (likely due to a configuration error). Thus we can check all * MPs at once making this quite a bit simpler. * * When cfm is not in demand mode, we check when (ccm_interval_ms * 3.5) ms * have passed. When cfm is in demand mode, we check when * (MAX(ccm_interval_ms, 500) * 3.5) ms have passed. This ensures that * ovs-vswitchd has enough time to pull statistics from the datapath. */ return (MAX(cfm->ccm_interval_ms, cfm->demand ? 500 : cfm->ccm_interval_ms) * 7) / 2; } static uint8_t ms_to_ccm_interval(int interval_ms) { uint8_t i; for (i = 7; i > 0; i--) { if (ccm_interval_to_ms(i) <= interval_ms) { return i; } } return 1; } static uint32_t hash_mpid(uint64_t mpid) { return hash_uint64(mpid); } static bool cfm_is_valid_mpid(bool extended, uint64_t mpid) { /* 802.1ag specification requires MPIDs to be within the range [1, 8191]. * In extended mode we relax this requirement. */ return mpid >= 1 && (extended || mpid <= 8191); } static struct remote_mp * lookup_remote_mp(const struct cfm *cfm, uint64_t mpid) OVS_REQUIRES(mutex) { struct remote_mp *rmp; HMAP_FOR_EACH_IN_BUCKET (rmp, node, hash_mpid(mpid), &cfm->remote_mps) { if (rmp->mpid == mpid) { return rmp; } } return NULL; } void cfm_init(void) { unixctl_command_register("cfm/show", "[interface]", 0, 1, cfm_unixctl_show, NULL); unixctl_command_register("cfm/set-fault", "[interface] normal|false|true", 1, 2, cfm_unixctl_set_fault, NULL); } /* Records the status change and changes the global connectivity seq. */ static void cfm_status_changed(struct cfm *cfm) OVS_REQUIRES(mutex) { seq_change(connectivity_seq_get()); cfm->status_changed = true; } /* Allocates a 'cfm' object called 'name'. 'cfm' should be initialized by * cfm_configure() before use. */ struct cfm * cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex) { struct cfm *cfm; cfm = xzalloc(sizeof *cfm); cfm->netdev = netdev_ref(netdev); cfm->name = netdev_get_name(cfm->netdev); hmap_init(&cfm->remote_mps); cfm->remote_opup = true; cfm->fault_override = -1; cfm->health = -1; cfm->last_tx = 0; cfm->flap_count = 0; atomic_init(&cfm->extended, false); atomic_init(&cfm->check_tnl_key, false); ovs_refcount_init(&cfm->ref_cnt); ovs_mutex_lock(&mutex); cfm_status_changed(cfm); cfm_generate_maid(cfm); hmap_insert(all_cfms, &cfm->hmap_node, hash_string(cfm->name, 0)); ovs_mutex_unlock(&mutex); return cfm; } void cfm_unref(struct cfm *cfm) OVS_EXCLUDED(mutex) { struct remote_mp *rmp, *rmp_next; if (!cfm) { return; } if (ovs_refcount_unref_relaxed(&cfm->ref_cnt) != 1) { return; } ovs_mutex_lock(&mutex); cfm_status_changed(cfm); hmap_remove(all_cfms, &cfm->hmap_node); ovs_mutex_unlock(&mutex); HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { hmap_remove(&cfm->remote_mps, &rmp->node); free(rmp); } hmap_destroy(&cfm->remote_mps); netdev_close(cfm->netdev); free(cfm->rmps_array); free(cfm); } struct cfm * cfm_ref(const struct cfm *cfm_) { struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); if (cfm) { ovs_refcount_ref(&cfm->ref_cnt); } return cfm; } /* Should be run periodically to update fault statistics messages. */ void cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (timer_expired(&cfm->fault_timer)) { long long int interval = cfm_fault_interval(cfm); struct remote_mp *rmp, *rmp_next; enum cfm_fault_reason old_cfm_fault = cfm->fault; uint64_t old_flap_count = cfm->flap_count; int old_health = cfm->health; size_t old_rmps_array_len = cfm->rmps_array_len; bool old_rmps_deleted = false; bool old_rmp_opup = cfm->remote_opup; bool demand_override; bool rmp_set_opup = false; bool rmp_set_opdown = false; cfm->fault = cfm->recv_fault; cfm->recv_fault = 0; cfm->rmps_array_len = 0; free(cfm->rmps_array); cfm->rmps_array = xmalloc(hmap_count(&cfm->remote_mps) * sizeof *cfm->rmps_array); if (cfm->health_interval == CFM_HEALTH_INTERVAL) { /* Calculate the cfm health of the interface. If the number of * remote_mpids of a cfm interface is > 1, the cfm health is * undefined. If the number of remote_mpids is 1, the cfm health is * the percentage of the ccm frames received in the * (CFM_HEALTH_INTERVAL * 3.5)ms, else it is 0. */ if (hmap_count(&cfm->remote_mps) > 1) { cfm->health = -1; } else if (hmap_is_empty(&cfm->remote_mps)) { cfm->health = 0; } else { int exp_ccm_recvd; rmp = CONTAINER_OF(hmap_first(&cfm->remote_mps), struct remote_mp, node); exp_ccm_recvd = (CFM_HEALTH_INTERVAL * 7) / 2; /* Calculate the percentage of healthy ccm frames received. * Since the 'fault_interval' is (3.5 * cfm_interval), and * 1 CCM packet must be received every cfm_interval, * the 'remote_mpid' health reports the percentage of * healthy CCM frames received every * 'CFM_HEALTH_INTERVAL'th 'fault_interval'. */ cfm->health = (rmp->num_health_ccm * 100) / exp_ccm_recvd; cfm->health = MIN(cfm->health, 100); rmp->num_health_ccm = 0; ovs_assert(cfm->health >= 0 && cfm->health <= 100); } cfm->health_interval = 0; } cfm->health_interval++; demand_override = false; if (cfm->demand) { uint64_t rx_packets = cfm_rx_packets(cfm); demand_override = hmap_count(&cfm->remote_mps) == 1 && rx_packets > cfm->rx_packets && !timer_expired(&cfm->demand_rx_ccm_t); cfm->rx_packets = rx_packets; } HMAP_FOR_EACH_SAFE (rmp, rmp_next, node, &cfm->remote_mps) { if (!rmp->recv) { VLOG_INFO("%s: Received no CCM from RMP %"PRIu64" in the last" " %lldms", cfm->name, rmp->mpid, time_msec() - rmp->last_rx); if (!demand_override) { old_rmps_deleted = true; hmap_remove(&cfm->remote_mps, &rmp->node); free(rmp); } } else { rmp->recv = false; if (rmp->opup) { rmp_set_opup = true; } else { rmp_set_opdown = true; } cfm->rmps_array[cfm->rmps_array_len++] = rmp->mpid; } } if (rmp_set_opdown) { cfm->remote_opup = false; } else if (rmp_set_opup) { cfm->remote_opup = true; } if (hmap_is_empty(&cfm->remote_mps)) { cfm->fault |= CFM_FAULT_RECV; } if (old_cfm_fault != cfm->fault) { if (!VLOG_DROP_INFO(&rl)) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_cstr(&ds, "from ["); ds_put_cfm_fault(&ds, old_cfm_fault); ds_put_cstr(&ds, "] to ["); ds_put_cfm_fault(&ds, cfm->fault); ds_put_char(&ds, ']'); VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds)); ds_destroy(&ds); } /* If there is a flap, increments the counter. */ if (old_cfm_fault == 0 || cfm->fault == 0) { cfm->flap_count++; } } /* These variables represent the cfm session status, it is desirable * to update them to database immediately after change. */ if (old_health != cfm->health || old_rmp_opup != cfm->remote_opup || (old_rmps_array_len != cfm->rmps_array_len || old_rmps_deleted) || old_cfm_fault != cfm->fault || old_flap_count != cfm->flap_count) { cfm_status_changed(cfm); } cfm->booted = true; timer_set_duration(&cfm->fault_timer, interval); VLOG_DBG("%s: new fault interval", cfm->name); } ovs_mutex_unlock(&mutex); } /* Should be run periodically to check if the CFM module has a CCM message it * wishes to send. */ bool cfm_should_send_ccm(struct cfm *cfm) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = timer_expired(&cfm->tx_timer); ovs_mutex_unlock(&mutex); return ret; } /* Composes a CCM message into 'packet'. Messages generated with this function * should be sent whenever cfm_should_send_ccm() indicates. */ void cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet, const struct eth_addr eth_src) OVS_EXCLUDED(mutex) { uint16_t ccm_vlan; struct ccm *ccm; bool extended; ovs_mutex_lock(&mutex); timer_set_duration(&cfm->tx_timer, cfm->ccm_interval_ms); eth_compose(packet, cfm_ccm_addr(cfm), eth_src, ETH_TYPE_CFM, sizeof *ccm); ccm_vlan = (cfm->ccm_vlan != CFM_RANDOM_VLAN ? cfm->ccm_vlan : random_uint16()); ccm_vlan = ccm_vlan & VLAN_VID_MASK; if (ccm_vlan || cfm->ccm_pcp) { uint16_t tci = ccm_vlan | (cfm->ccm_pcp << VLAN_PCP_SHIFT); eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(tci)); } atomic_read_relaxed(&cfm->extended, &extended); ccm = dp_packet_l3(packet); ccm->mdlevel_version = 0; ccm->opcode = CCM_OPCODE; ccm->tlv_offset = 70; ccm->seq = htonl(++cfm->seq); ccm->flags = cfm->ccm_interval; memcpy(ccm->maid, cfm->maid, sizeof ccm->maid); memset(ccm->zero, 0, sizeof ccm->zero); ccm->end_tlv = 0; if (extended) { ccm->mpid = htons(hash_mpid(cfm->mpid)); ccm->mpid64 = htonll(cfm->mpid); ccm->opdown = !cfm->opup; } else { ccm->mpid = htons(cfm->mpid); ccm->mpid64 = htonll(0); ccm->opdown = 0; } if (cfm->ccm_interval == 0) { ovs_assert(extended); ccm->interval_ms_x = htons(cfm->ccm_interval_ms); } else { ccm->interval_ms_x = htons(0); } if (cfm->booted && hmap_is_empty(&cfm->remote_mps)) { ccm->flags |= CCM_RDI_MASK; } if (cfm->last_tx) { long long int delay = time_msec() - cfm->last_tx; if (delay > (cfm->ccm_interval_ms * 3 / 2)) { VLOG_INFO("%s: long delay of %lldms (expected %dms) sending CCM" " seq %"PRIu32, cfm->name, delay, cfm->ccm_interval_ms, cfm->seq); } } cfm->last_tx = time_msec(); ovs_mutex_unlock(&mutex); } long long int cfm_wait(struct cfm *cfm) OVS_EXCLUDED(mutex) { long long int wake_time = cfm_wake_time(cfm); poll_timer_wait_until(wake_time); return wake_time; } /* Returns the next cfm wakeup time. */ long long int cfm_wake_time(struct cfm *cfm) OVS_EXCLUDED(mutex) { long long int retval; if (!cfm) { return LLONG_MAX; } ovs_mutex_lock(&mutex); retval = MIN(cfm->tx_timer.t, cfm->fault_timer.t); ovs_mutex_unlock(&mutex); return retval; } /* Configures 'cfm' with settings from 's'. */ bool cfm_configure(struct cfm *cfm, const struct cfm_settings *s) OVS_EXCLUDED(mutex) { uint8_t interval; int interval_ms; if (!cfm_is_valid_mpid(s->extended, s->mpid) || s->interval <= 0) { return false; } ovs_mutex_lock(&mutex); cfm->mpid = s->mpid; cfm->opup = s->opup; interval = ms_to_ccm_interval(s->interval); interval_ms = ccm_interval_to_ms(interval); atomic_store_relaxed(&cfm->check_tnl_key, s->check_tnl_key); atomic_store_relaxed(&cfm->extended, s->extended); cfm->ccm_vlan = s->ccm_vlan; cfm->ccm_pcp = s->ccm_pcp & (VLAN_PCP_MASK >> VLAN_PCP_SHIFT); if (s->extended && interval_ms != s->interval) { interval = 0; interval_ms = MIN(s->interval, UINT16_MAX); } if (s->extended && s->demand) { if (!cfm->demand) { cfm->demand = true; cfm->rx_packets = cfm_rx_packets(cfm); } } else { cfm->demand = false; } if (interval != cfm->ccm_interval || interval_ms != cfm->ccm_interval_ms) { cfm->ccm_interval = interval; cfm->ccm_interval_ms = interval_ms; timer_set_expired(&cfm->tx_timer); timer_set_duration(&cfm->fault_timer, cfm_fault_interval(cfm)); } ovs_mutex_unlock(&mutex); return true; } /* Must be called when the netdev owned by 'cfm' should change. */ void cfm_set_netdev(struct cfm *cfm, const struct netdev *netdev) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (cfm->netdev != netdev) { netdev_close(cfm->netdev); cfm->netdev = netdev_ref(netdev); } ovs_mutex_unlock(&mutex); } /* Returns true if 'cfm' should process packets from 'flow'. Sets * fields in 'wc' that were used to make the determination. */ bool cfm_should_process_flow(const struct cfm *cfm_, const struct flow *flow, struct flow_wildcards *wc) { struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); bool check_tnl_key; /* Most packets are not CFM. */ if (OVS_LIKELY(flow->dl_type != htons(ETH_TYPE_CFM))) { return false; } memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); if (OVS_UNLIKELY(!eth_addr_equals(flow->dl_dst, cfm_ccm_addr(cfm)))) { return false; } atomic_read_relaxed(&cfm->check_tnl_key, &check_tnl_key); if (check_tnl_key) { memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id); return flow->tunnel.tun_id == htonll(0); } return true; } /* Updates internal statistics relevant to packet 'p'. Should be called on * every packet whose flow returned true when passed to * cfm_should_process_flow. */ void cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p) OVS_EXCLUDED(mutex) { struct ccm *ccm; struct eth_header *eth; bool extended; ovs_mutex_lock(&mutex); atomic_read_relaxed(&cfm->extended, &extended); eth = dp_packet_l2(p); ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p), CCM_ACCEPT_LEN); if (!ccm) { VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.", cfm->name); goto out; } if (ccm->opcode != CCM_OPCODE) { VLOG_INFO_RL(&rl, "%s: Received an unsupported 802.1ag message. " "(opcode %u)", cfm->name, ccm->opcode); goto out; } /* According to the 802.1ag specification, reception of a CCM with an * incorrect ccm_interval, unexpected MAID, or unexpected MPID should * trigger a fault. We ignore this requirement for several reasons. * * Faults can cause a controller or Open vSwitch to make potentially * expensive changes to the network topology. It seems prudent to trigger * them judiciously, especially when CFM is used to check slave status of * bonds. Furthermore, faults can be maliciously triggered by crafting * unexpected CCMs. */ if (memcmp(ccm->maid, cfm->maid, sizeof ccm->maid)) { cfm->recv_fault |= CFM_FAULT_MAID; VLOG_WARN_RL(&rl, "%s: Received unexpected remote MAID from MAC " ETH_ADDR_FMT, cfm->name, ETH_ADDR_ARGS(eth->eth_src)); } else { uint8_t ccm_interval = ccm->flags & 0x7; bool ccm_rdi = ccm->flags & CCM_RDI_MASK; uint16_t ccm_interval_ms_x = ntohs(ccm->interval_ms_x); struct remote_mp *rmp; uint64_t ccm_mpid; uint32_t ccm_seq; bool ccm_opdown; enum cfm_fault_reason cfm_fault = 0; if (extended) { ccm_mpid = ntohll(ccm->mpid64); ccm_opdown = ccm->opdown; } else { ccm_mpid = ntohs(ccm->mpid); ccm_opdown = false; } ccm_seq = ntohl(ccm->seq); if (ccm_interval != cfm->ccm_interval) { VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected interval" " (%"PRIu8") from RMP %"PRIu64, cfm->name, ccm_interval, ccm_mpid); } if (extended && ccm_interval == 0 && ccm_interval_ms_x != cfm->ccm_interval_ms) { VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected extended" " interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name, ccm_interval_ms_x, ccm_mpid); } rmp = lookup_remote_mp(cfm, ccm_mpid); if (!rmp) { if (hmap_count(&cfm->remote_mps) < CFM_MAX_RMPS) { rmp = xzalloc(sizeof *rmp); hmap_insert(&cfm->remote_mps, &rmp->node, hash_mpid(ccm_mpid)); } else { cfm_fault |= CFM_FAULT_OVERFLOW; VLOG_WARN_RL(&rl, "%s: dropped CCM with MPID %"PRIu64" from MAC " ETH_ADDR_FMT, cfm->name, ccm_mpid, ETH_ADDR_ARGS(eth->eth_src)); } } if (ccm_rdi) { cfm_fault |= CFM_FAULT_RDI; VLOG_DBG("%s: RDI bit flagged from RMP %"PRIu64, cfm->name, ccm_mpid); } VLOG_DBG("%s: received CCM (seq %"PRIu32") (mpid %"PRIu64")" " (interval %"PRIu8") (RDI %s)", cfm->name, ccm_seq, ccm_mpid, ccm_interval, ccm_rdi ? "true" : "false"); if (rmp) { if (rmp->mpid == cfm->mpid) { cfm_fault |= CFM_FAULT_LOOPBACK; VLOG_WARN_RL(&rl,"%s: received CCM with local MPID" " %"PRIu64, cfm->name, rmp->mpid); } if (rmp->seq && ccm_seq != (rmp->seq + 1)) { VLOG_WARN_RL(&rl, "%s: (mpid %"PRIu64") detected sequence" " numbers which indicate possible connectivity" " problems (previous %"PRIu32") (current %"PRIu32 ")", cfm->name, ccm_mpid, rmp->seq, ccm_seq); } rmp->mpid = ccm_mpid; if (!cfm_fault) { rmp->num_health_ccm++; if (cfm->demand) { timer_set_duration(&cfm->demand_rx_ccm_t, 100 * cfm->ccm_interval_ms); } } rmp->recv = true; cfm->recv_fault |= cfm_fault; rmp->seq = ccm_seq; rmp->opup = !ccm_opdown; rmp->last_rx = time_msec(); } } out: ovs_mutex_unlock(&mutex); } /* Returns and resets the 'cfm->status_changed'. */ bool cfm_check_status_change(struct cfm *cfm) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = cfm->status_changed; cfm->status_changed = false; ovs_mutex_unlock(&mutex); return ret; } static int cfm_get_fault__(const struct cfm *cfm) OVS_REQUIRES(mutex) { if (cfm->fault_override >= 0) { return cfm->fault_override ? CFM_FAULT_OVERRIDE : 0; } return cfm->fault; } /* Gets the fault status of 'cfm'. Returns a bit mask of 'cfm_fault_reason's * indicating the cause of the connectivity fault, or zero if there is no * fault. */ int cfm_get_fault(const struct cfm *cfm) OVS_EXCLUDED(mutex) { int fault; ovs_mutex_lock(&mutex); fault = cfm_get_fault__(cfm); ovs_mutex_unlock(&mutex); return fault; } /* Gets the number of cfm fault flapping since start. */ uint64_t cfm_get_flap_count(const struct cfm *cfm) OVS_EXCLUDED(mutex) { uint64_t flap_count; ovs_mutex_lock(&mutex); flap_count = cfm->flap_count; ovs_mutex_unlock(&mutex); return flap_count; } /* Gets the health of 'cfm'. Returns an integer between 0 and 100 indicating * the health of the link as a percentage of ccm frames received in * CFM_HEALTH_INTERVAL * 'fault_interval' if there is only 1 remote_mpid, * returns 0 if there are no remote_mpids, and returns -1 if there are more * than 1 remote_mpids. */ int cfm_get_health(const struct cfm *cfm) OVS_EXCLUDED(mutex) { int health; ovs_mutex_lock(&mutex); health = cfm->health; ovs_mutex_unlock(&mutex); return health; } static int cfm_get_opup__(const struct cfm *cfm_) OVS_REQUIRES(mutex) { struct cfm *cfm = CONST_CAST(struct cfm *, cfm_); bool extended; atomic_read_relaxed(&cfm->extended, &extended); return extended ? cfm->remote_opup : -1; } /* Gets the operational state of 'cfm'. 'cfm' is considered operationally down * if it has received a CCM with the operationally down bit set from any of its * remote maintenance points. Returns 1 if 'cfm' is operationally up, 0 if * 'cfm' is operationally down, or -1 if 'cfm' has no operational state * (because it isn't in extended mode). */ int cfm_get_opup(const struct cfm *cfm) OVS_EXCLUDED(mutex) { int opup; ovs_mutex_lock(&mutex); opup = cfm_get_opup__(cfm); ovs_mutex_unlock(&mutex); return opup; } static void cfm_get_remote_mpids__(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps) OVS_REQUIRES(mutex) { *rmps = xmemdup(cfm->rmps_array, cfm->rmps_array_len * sizeof **rmps); *n_rmps = cfm->rmps_array_len; } /* Populates 'rmps' with an array of remote maintenance points reachable by * 'cfm'. The number of remote maintenance points is written to 'n_rmps'. * 'cfm' retains ownership of the array written to 'rmps' */ void cfm_get_remote_mpids(const struct cfm *cfm, uint64_t **rmps, size_t *n_rmps) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); cfm_get_remote_mpids__(cfm, rmps, n_rmps); ovs_mutex_unlock(&mutex); } /* Extracts the status of 'cfm' and fills in the 's'. */ void cfm_get_status(const struct cfm *cfm, struct cfm_status *s) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); s->faults = cfm_get_fault__(cfm); s->remote_opstate = cfm_get_opup__(cfm); s->flap_count = cfm->flap_count; s->health = cfm->health; cfm_get_remote_mpids__(cfm, &s->rmps, &s->n_rmps); ovs_mutex_unlock(&mutex); } static struct cfm * cfm_find(const char *name) OVS_REQUIRES(mutex) { struct cfm *cfm; HMAP_FOR_EACH_WITH_HASH (cfm, hmap_node, hash_string(name, 0), all_cfms) { if (!strcmp(cfm->name, name)) { return cfm; } } return NULL; } static void cfm_print_details(struct ds *ds, struct cfm *cfm) OVS_REQUIRES(mutex) { struct remote_mp *rmp; bool extended; int fault; atomic_read_relaxed(&cfm->extended, &extended); ds_put_format(ds, "---- %s ----\n", cfm->name); ds_put_format(ds, "MPID %"PRIu64":%s%s\n", cfm->mpid, extended ? " extended" : "", cfm->fault_override >= 0 ? " fault_override" : ""); fault = cfm_get_fault__(cfm); if (fault) { ds_put_cstr(ds, "\tfault: "); ds_put_cfm_fault(ds, fault); ds_put_cstr(ds, "\n"); } if (cfm->health == -1) { ds_put_format(ds, "\taverage health: undefined\n"); } else { ds_put_format(ds, "\taverage health: %d\n", cfm->health); } ds_put_format(ds, "\topstate: %s\n", cfm->opup ? "up" : "down"); ds_put_format(ds, "\tremote_opstate: %s\n", cfm->remote_opup ? "up" : "down"); ds_put_format(ds, "\tinterval: %dms\n", cfm->ccm_interval_ms); ds_put_format(ds, "\tnext CCM tx: %lldms\n", timer_msecs_until_expired(&cfm->tx_timer)); ds_put_format(ds, "\tnext fault check: %lldms\n", timer_msecs_until_expired(&cfm->fault_timer)); HMAP_FOR_EACH (rmp, node, &cfm->remote_mps) { ds_put_format(ds, "Remote MPID %"PRIu64"\n", rmp->mpid); ds_put_format(ds, "\trecv since check: %s\n", rmp->recv ? "true" : "false"); ds_put_format(ds, "\topstate: %s\n", rmp->opup? "up" : "down"); } } static void cfm_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; struct cfm *cfm; ovs_mutex_lock(&mutex); if (argc > 1) { cfm = cfm_find(argv[1]); if (!cfm) { unixctl_command_reply_error(conn, "no such CFM object"); goto out; } cfm_print_details(&ds, cfm); } else { HMAP_FOR_EACH (cfm, hmap_node, all_cfms) { cfm_print_details(&ds, cfm); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); out: ovs_mutex_unlock(&mutex); } static void cfm_unixctl_set_fault(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { const char *fault_str = argv[argc - 1]; int fault_override; struct cfm *cfm; ovs_mutex_lock(&mutex); if (!strcasecmp("true", fault_str)) { fault_override = 1; } else if (!strcasecmp("false", fault_str)) { fault_override = 0; } else if (!strcasecmp("normal", fault_str)) { fault_override = -1; } else { unixctl_command_reply_error(conn, "unknown fault string"); goto out; } if (argc > 2) { cfm = cfm_find(argv[1]); if (!cfm) { unixctl_command_reply_error(conn, "no such CFM object"); goto out; } cfm->fault_override = fault_override; cfm_status_changed(cfm); } else { HMAP_FOR_EACH (cfm, hmap_node, all_cfms) { cfm->fault_override = fault_override; cfm_status_changed(cfm); } } unixctl_command_reply(conn, "OK"); out: ovs_mutex_unlock(&mutex); } openvswitch-2.5.9/lib/PaxHeaders.82075/jsonrpc.h0000644000000000000000000000013113534540071016240 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.745853161 openvswitch-2.5.9/lib/jsonrpc.h0000644000175000017500000001234513534540071017734 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JSONRPC_H #define JSONRPC_H 1 /* This is an implementation of the JSON-RPC 1.0 specification defined at * http://json-rpc.org/wiki/specification. */ #include #include #include "openvswitch/types.h" struct json; struct jsonrpc_msg; struct pstream; struct reconnect_stats; struct stream; /* API for a JSON-RPC stream. */ /* Default port numbers. * * OVSDB_OLD_PORT defines the original port number used by OVS. * OVSDB_PORT defines the official port number assigned by IANA. */ #define OVSDB_OLD_PORT 6632 #define OVSDB_PORT 6640 int jsonrpc_stream_open(const char *name, struct stream **, uint8_t dscp); int jsonrpc_pstream_open(const char *name, struct pstream **, uint8_t dscp); struct jsonrpc *jsonrpc_open(struct stream *); void jsonrpc_close(struct jsonrpc *); void jsonrpc_run(struct jsonrpc *); void jsonrpc_wait(struct jsonrpc *); int jsonrpc_get_status(const struct jsonrpc *); size_t jsonrpc_get_backlog(const struct jsonrpc *); unsigned int jsonrpc_get_received_bytes(const struct jsonrpc *); const char *jsonrpc_get_name(const struct jsonrpc *); int jsonrpc_send(struct jsonrpc *, struct jsonrpc_msg *); int jsonrpc_recv(struct jsonrpc *, struct jsonrpc_msg **); void jsonrpc_recv_wait(struct jsonrpc *); int jsonrpc_send_block(struct jsonrpc *, struct jsonrpc_msg *); int jsonrpc_recv_block(struct jsonrpc *, struct jsonrpc_msg **); int jsonrpc_transact_block(struct jsonrpc *, struct jsonrpc_msg *, struct jsonrpc_msg **); /* Messages. */ enum jsonrpc_msg_type { JSONRPC_REQUEST, /* Request. */ JSONRPC_NOTIFY, /* Notification. */ JSONRPC_REPLY, /* Successful reply. */ JSONRPC_ERROR /* Error reply. */ }; struct jsonrpc_msg { enum jsonrpc_msg_type type; char *method; /* Request or notification only. */ struct json *params; /* Request or notification only. */ struct json *result; /* Successful reply only. */ struct json *error; /* Error reply only. */ struct json *id; /* Request or reply only. */ }; struct jsonrpc_msg *jsonrpc_create_request(const char *method, struct json *params, struct json **idp); struct jsonrpc_msg *jsonrpc_create_notify(const char *method, struct json *params); struct jsonrpc_msg *jsonrpc_create_reply(struct json *result, const struct json *id); struct jsonrpc_msg *jsonrpc_create_error(struct json *error, const struct json *id); const char *jsonrpc_msg_type_to_string(enum jsonrpc_msg_type); char *jsonrpc_msg_is_valid(const struct jsonrpc_msg *); void jsonrpc_msg_destroy(struct jsonrpc_msg *); char *jsonrpc_msg_from_json(struct json *, struct jsonrpc_msg **); struct json *jsonrpc_msg_to_json(struct jsonrpc_msg *); /* A JSON-RPC session with reconnection. */ struct jsonrpc_session *jsonrpc_session_open(const char *name, bool retry); struct jsonrpc_session *jsonrpc_session_open_unreliably(struct jsonrpc *, uint8_t); void jsonrpc_session_close(struct jsonrpc_session *); void jsonrpc_session_run(struct jsonrpc_session *); void jsonrpc_session_wait(struct jsonrpc_session *); size_t jsonrpc_session_get_backlog(const struct jsonrpc_session *); const char *jsonrpc_session_get_name(const struct jsonrpc_session *); int jsonrpc_session_send(struct jsonrpc_session *, struct jsonrpc_msg *); struct jsonrpc_msg *jsonrpc_session_recv(struct jsonrpc_session *); void jsonrpc_session_recv_wait(struct jsonrpc_session *); bool jsonrpc_session_is_alive(const struct jsonrpc_session *); bool jsonrpc_session_is_connected(const struct jsonrpc_session *); unsigned int jsonrpc_session_get_seqno(const struct jsonrpc_session *); int jsonrpc_session_get_status(const struct jsonrpc_session *); int jsonrpc_session_get_last_error(const struct jsonrpc_session *); void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *, struct reconnect_stats *); void jsonrpc_session_enable_reconnect(struct jsonrpc_session *); void jsonrpc_session_force_reconnect(struct jsonrpc_session *); void jsonrpc_session_set_max_backoff(struct jsonrpc_session *, int max_backofF); void jsonrpc_session_set_probe_interval(struct jsonrpc_session *, int probe_interval); void jsonrpc_session_set_dscp(struct jsonrpc_session *, uint8_t dscp); #endif /* jsonrpc.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/seq.h0000644000000000000000000000013213534540071015353 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.873854104 openvswitch-2.5.9/lib/seq.h0000644000175000017500000001135013534540071017041 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SEQ_H #define SEQ_H 1 /* Thread-safe, pollable sequence number. * * * Motivation * ========== * * It is sometimes desirable to take an action whenever an object changes. * Suppose we associate a sequence number with an object and increment the * sequence number whenver we change the object. An observer can then record * the sequence number it sees. Later on, if the current sequence number * differs from the one it saw last, then the observer knows to examine the * object for changes. * * Code that wants to run when a sequence number changes is challenging to * implement in a multithreaded environment. A naive implementation, that * simply checks whether the sequence number changed and, if so, calls * poll_immediate_wake(), will fail when another thread increments the sequence * number after the check (including during poll_block()). * * struct seq is a solution. It implements a sequence number along with enough * internal infrastructure so that a thread waiting on a particular value will * wake up if the sequence number changes, or even if the "struct seq" is * destroyed. * * * Usage * ===== * * The object that includes a sequence number should use seq_create() and * seq_destroy() at creation and destruction, and seq_change() whenever the * object's observable state changes. * * An observer may seq_read() to read the current sequence number and * seq_wait() to cause poll_block() to wake up when the sequence number changes * from a specified value. * * To avoid races, observers should use seq_read() to check for changes, * process any changes, and then use seq_wait() to wait for a change from the * previously read value. That is, a correct usage looks something like this: * * new_seq = seq_read(seq); * if (new_seq != last_seq) { * ...process changes... * last_seq = new_seq; * } * seq_wait(seq, new_seq); * poll_block(); * * * Alternate Usage * =============== * * struct seq can also be used as a sort of pollable condition variable. * Suppose that we want a thread to process items in a queue, and thus to be * able to wake up whenever the queue is nonempty. This requires a lock to * protect the queue and a seq to signal that the queue has become nonempty, * e.g.: * * struct ovs_mutex mutex; * struct ovs_list queue OVS_GUARDED_BY(mutex); * struct seq nonempty_seq; * * To add an element to the queue: * * ovs_mutex_lock(&mutex); * list_push_back(&queue, ...element...); * if (list_is_singleton(&queue)) { // The 'if' test here is optional. * seq_change(&nonempty_seq); * } * ovs_mutex_unlock(&mutex); * * To wait for the queue to become nonempty: * * ovs_mutex_lock(&mutex); * if (list_is_empty(&queue)) { * seq_wait(&nonempty_seq, seq_read(&nonempty_seq)); * } else { * poll_immediate_wake(); * } * ovs_mutex_unlock(&mutex); * * (In the above code 'mutex' prevents the queue from changing between * seq_read() and seq_wait(). Otherwise, it would be necessary to seq_read(), * check for a nonempty queue, and then seq_wait() on the previously read * sequence number, as under Usage above.) * * * Thread-safety * ============= * * Fully thread safe. seq_change() synchronizes with seq_read() and * seq_wait() on the same variable in release-acquire fashion. That * is, all effects of the memory accesses performed by a thread prior * to seq_change() are visible to the threads returning from * seq_read() or seq_wait() observing that change. */ #include #include "util.h" /* For implementation of an object with a sequence number attached. */ struct seq *seq_create(void); void seq_destroy(struct seq *); void seq_change(struct seq *); void seq_change_protected(struct seq *); void seq_lock(void); int seq_try_lock(void); void seq_unlock(void); /* For observers. */ uint64_t seq_read(const struct seq *); uint64_t seq_read_protected(const struct seq *); void seq_wait_at(const struct seq *, uint64_t value, const char *where); #define seq_wait(seq, value) seq_wait_at(seq, value, OVS_SOURCE_LOCATOR) /* For poll_block() internal use. */ void seq_woke(void); #endif /* seq.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/fat-rwlock.c0000644000000000000000000000013213534540071016627 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.721852984 openvswitch-2.5.9/lib/fat-rwlock.c0000644000175000017500000001661213534540071020323 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "fat-rwlock.h" #include #include "hmap.h" #include "list.h" #include "ovs-thread.h" #include "random.h" struct fat_rwlock_slot { /* Membership in rwlock's list of "struct fat_rwlock_slot"s. * * fat_rwlock_destroy() sets 'rwlock' to NULL to indicate that this * slot may be destroyed. */ struct ovs_list list_node; /* In struct rwlock's 'threads' list. */ struct fat_rwlock *rwlock; /* Owner. */ /* Mutex. * * A thread holding the read-lock holds its own mutex. * * A thread holding the write-lock holds every thread's mutex, plus * 'rwlock->mutex'. */ struct ovs_mutex mutex; /* This thread's locking status for 'rwlock': * * - 0: This thread does not have any lock on 'rwlock'. This thread * does not have 'mutex' locked. * * - 1: This thread has a read-lock on 'rwlock' and holds 'mutex'. * * - 2...UINT_MAX-1: This thread has recursively taken the read-lock on * 'rwlock' to the level of 'depth'. This thread holds 'mutex'. * * - UINT_MAX: This thread has the write-lock on 'rwlock' and holds * 'mutex' (plus the 'mutex' of all of 'rwlock''s other slots). * * Accessed only by the slot's own thread, so no synchronization is * needed. */ unsigned int depth; }; static void free_slot(struct fat_rwlock_slot *slot) { if (slot->depth) { abort(); } list_remove(&slot->list_node); free_cacheline(slot); } static void slot_destructor(void *slot_) { struct fat_rwlock_slot *slot = slot_; struct fat_rwlock *rwlock = slot->rwlock; ovs_mutex_lock(&rwlock->mutex); free_slot(slot); ovs_mutex_unlock(&rwlock->mutex); } /* Initialize 'rwlock' as a new fat_rwlock. */ void fat_rwlock_init(struct fat_rwlock *rwlock) { ovsthread_key_create(&rwlock->key, slot_destructor); ovs_mutex_init(&rwlock->mutex); ovs_mutex_lock(&rwlock->mutex); list_init(&rwlock->threads); ovs_mutex_unlock(&rwlock->mutex); } /* Destroys 'rwlock', which must not be locked or otherwise in use by any * thread. */ void fat_rwlock_destroy(struct fat_rwlock *rwlock) { struct fat_rwlock_slot *slot, *next; /* Order is important here. By destroying the thread-specific data first, * before we destroy the slots, we ensure that the thread-specific * data destructor can't race with our loop below. */ ovsthread_key_delete(rwlock->key); LIST_FOR_EACH_SAFE (slot, next, list_node, &rwlock->threads) { free_slot(slot); } ovs_mutex_destroy(&rwlock->mutex); } static struct fat_rwlock_slot * fat_rwlock_get_slot__(struct fat_rwlock *rwlock) { struct fat_rwlock_slot *slot; /* Fast path. */ slot = ovsthread_getspecific(rwlock->key); if (slot) { return slot; } /* Slow path: create a new slot for 'rwlock' in this thread. */ slot = xmalloc_cacheline(sizeof *slot); slot->rwlock = rwlock; ovs_mutex_init(&slot->mutex); slot->depth = 0; ovs_mutex_lock(&rwlock->mutex); list_push_back(&rwlock->threads, &slot->list_node); ovs_mutex_unlock(&rwlock->mutex); ovsthread_setspecific(rwlock->key, slot); return slot; } /* Locks 'rwlock' for reading. The read-lock is recursive: it may be acquired * any number of times by a single thread (which must then release it the same * number of times for it to truly be released). */ void fat_rwlock_rdlock(const struct fat_rwlock *rwlock_) OVS_ACQ_RDLOCK(rwlock_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock); switch (this->depth) { case UINT_MAX: /* This thread already holds the write-lock. */ abort(); case 0: ovs_mutex_lock(&this->mutex); /* fall through */ default: this->depth++; break; } } static struct fat_rwlock_slot * fat_rwlock_try_get_slot__(struct fat_rwlock *rwlock) { struct fat_rwlock_slot *slot; /* Fast path. */ slot = ovsthread_getspecific(rwlock->key); if (slot) { return slot; } /* Slow path: create a new slot for 'rwlock' in this thread. */ if (!ovs_mutex_trylock(&rwlock->mutex)) { slot = xmalloc_cacheline(sizeof *slot); slot->rwlock = rwlock; ovs_mutex_init(&slot->mutex); slot->depth = 0; list_push_back(&rwlock->threads, &slot->list_node); ovs_mutex_unlock(&rwlock->mutex); ovsthread_setspecific(rwlock->key, slot); } return slot; } /* Tries to lock 'rwlock' for reading. If successful, returns 0. If taking * the lock would require blocking, returns EBUSY (without blocking). */ int fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock_) OVS_TRY_RDLOCK(0, rwlock_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); struct fat_rwlock_slot *this = fat_rwlock_try_get_slot__(rwlock); int error; if (!this) { return EBUSY; } switch (this->depth) { case UINT_MAX: return EBUSY; case 0: error = ovs_mutex_trylock(&this->mutex); if (error) { return error; } /* fall through */ default: this->depth++; break; } return 0; } /* Locks 'rwlock' for writing. * * The write lock is not recursive. */ void fat_rwlock_wrlock(const struct fat_rwlock *rwlock_) OVS_ACQ_WRLOCK(rwlock_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock); struct fat_rwlock_slot *slot; ovs_assert(!this->depth); this->depth = UINT_MAX; ovs_mutex_lock(&rwlock->mutex); LIST_FOR_EACH (slot, list_node, &rwlock->threads) { ovs_mutex_lock(&slot->mutex); } } /* Unlocks 'rwlock', which the current thread must have locked for reading or * for writing. If the read lock has been taken recursively, it must be * released the same number of times to be truly released. */ void fat_rwlock_unlock(const struct fat_rwlock *rwlock_) OVS_RELEASES(rwlock_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock); struct fat_rwlock_slot *slot; switch (this->depth) { case UINT_MAX: LIST_FOR_EACH (slot, list_node, &rwlock->threads) { ovs_mutex_unlock(&slot->mutex); } ovs_mutex_unlock(&rwlock->mutex); this->depth = 0; break; case 0: /* This thread doesn't hold any lock. */ abort(); case 1: ovs_mutex_unlock(&this->mutex); default: this->depth--; break; } } openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-dummy.c0000644000000000000000000000013213534540071017174 xustar0030 mtime=1567801401.445681493 30 atime=1567801402.081686164 30 ctime=1567801424.773853367 openvswitch-2.5.9/lib/netdev-dummy.c0000644000175000017500000012031713534540071020666 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dummy.h" #include #include "dp-packet.h" #include "dpif-netdev.h" #include "dynamic-string.h" #include "flow.h" #include "list.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "odp-util.h" #include "ofp-print.h" #include "ofpbuf.h" #include "ovs-atomic.h" #include "packets.h" #include "pcap-file.h" #include "poll-loop.h" #include "shash.h" #include "sset.h" #include "stream.h" #include "unaligned.h" #include "timeval.h" #include "unixctl.h" #include "reconnect.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_dummy); struct reconnect; struct dummy_packet_stream { struct stream *stream; struct dp_packet rxbuf; struct ovs_list txq; }; enum dummy_packet_conn_type { NONE, /* No connection is configured. */ PASSIVE, /* Listener. */ ACTIVE /* Connect to listener. */ }; enum dummy_netdev_conn_state { CONN_STATE_CONNECTED, /* Listener connected. */ CONN_STATE_NOT_CONNECTED, /* Listener not connected. */ CONN_STATE_UNKNOWN, /* No relavent information. */ }; struct dummy_packet_pconn { struct pstream *pstream; struct dummy_packet_stream *streams; size_t n_streams; }; struct dummy_packet_rconn { struct dummy_packet_stream *rstream; struct reconnect *reconnect; }; struct dummy_packet_conn { enum dummy_packet_conn_type type; union { struct dummy_packet_pconn pconn; struct dummy_packet_rconn rconn; } u; }; struct pkt_list_node { struct dp_packet *pkt; struct ovs_list list_node; }; /* Protects 'dummy_list'. */ static struct ovs_mutex dummy_list_mutex = OVS_MUTEX_INITIALIZER; /* Contains all 'struct dummy_dev's. */ static struct ovs_list dummy_list OVS_GUARDED_BY(dummy_list_mutex) = OVS_LIST_INITIALIZER(&dummy_list); struct netdev_dummy { struct netdev up; /* In dummy_list. */ struct ovs_list list_node OVS_GUARDED_BY(dummy_list_mutex); /* Protects all members below. */ struct ovs_mutex mutex OVS_ACQ_AFTER(dummy_list_mutex); struct eth_addr hwaddr OVS_GUARDED; int mtu OVS_GUARDED; struct netdev_stats stats OVS_GUARDED; enum netdev_flags flags OVS_GUARDED; int ifindex OVS_GUARDED; struct dummy_packet_conn conn OVS_GUARDED; FILE *tx_pcap, *rxq_pcap OVS_GUARDED; struct in_addr address, netmask; struct in6_addr ipv6; struct ovs_list rxes OVS_GUARDED; /* List of child "netdev_rxq_dummy"s. */ }; /* Max 'recv_queue_len' in struct netdev_dummy. */ #define NETDEV_DUMMY_MAX_QUEUE 100 struct netdev_rxq_dummy { struct netdev_rxq up; struct ovs_list node; /* In netdev_dummy's "rxes" list. */ struct ovs_list recv_queue; int recv_queue_len; /* list_size(&recv_queue). */ struct seq *seq; /* Reports newly queued packets. */ }; static unixctl_cb_func netdev_dummy_set_admin_state; static int netdev_dummy_construct(struct netdev *); static void netdev_dummy_queue_packet(struct netdev_dummy *, struct dp_packet *); static void dummy_packet_stream_close(struct dummy_packet_stream *); static void pkt_list_delete(struct ovs_list *); static bool is_dummy_class(const struct netdev_class *class) { return class->construct == netdev_dummy_construct; } static struct netdev_dummy * netdev_dummy_cast(const struct netdev *netdev) { ovs_assert(is_dummy_class(netdev_get_class(netdev))); return CONTAINER_OF(netdev, struct netdev_dummy, up); } static struct netdev_rxq_dummy * netdev_rxq_dummy_cast(const struct netdev_rxq *rx) { ovs_assert(is_dummy_class(netdev_get_class(rx->netdev))); return CONTAINER_OF(rx, struct netdev_rxq_dummy, up); } static void dummy_packet_stream_init(struct dummy_packet_stream *s, struct stream *stream) { int rxbuf_size = stream ? 2048 : 0; s->stream = stream; dp_packet_init(&s->rxbuf, rxbuf_size); list_init(&s->txq); } static struct dummy_packet_stream * dummy_packet_stream_create(struct stream *stream) { struct dummy_packet_stream *s; s = xzalloc(sizeof *s); dummy_packet_stream_init(s, stream); return s; } static void dummy_packet_stream_wait(struct dummy_packet_stream *s) { stream_run_wait(s->stream); if (!list_is_empty(&s->txq)) { stream_send_wait(s->stream); } stream_recv_wait(s->stream); } static void dummy_packet_stream_send(struct dummy_packet_stream *s, const void *buffer, size_t size) { if (list_size(&s->txq) < NETDEV_DUMMY_MAX_QUEUE) { struct dp_packet *b; struct pkt_list_node *node; b = dp_packet_clone_data_with_headroom(buffer, size, 2); put_unaligned_be16(dp_packet_push_uninit(b, 2), htons(size)); node = xmalloc(sizeof *node); node->pkt = b; list_push_back(&s->txq, &node->list_node); } } static int dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s) { int error = 0; size_t n; stream_run(s->stream); if (!list_is_empty(&s->txq)) { struct pkt_list_node *txbuf_node; struct dp_packet *txbuf; int retval; ASSIGN_CONTAINER(txbuf_node, list_front(&s->txq), list_node); txbuf = txbuf_node->pkt; retval = stream_send(s->stream, dp_packet_data(txbuf), dp_packet_size(txbuf)); if (retval > 0) { dp_packet_pull(txbuf, retval); if (!dp_packet_size(txbuf)) { list_remove(&txbuf_node->list_node); free(txbuf_node); dp_packet_delete(txbuf); } } else if (retval != -EAGAIN) { error = -retval; } } if (!error) { if (dp_packet_size(&s->rxbuf) < 2) { n = 2 - dp_packet_size(&s->rxbuf); } else { uint16_t frame_len; frame_len = ntohs(get_unaligned_be16(dp_packet_data(&s->rxbuf))); if (frame_len < ETH_HEADER_LEN) { error = EPROTO; n = 0; } else { n = (2 + frame_len) - dp_packet_size(&s->rxbuf); } } } if (!error) { int retval; dp_packet_prealloc_tailroom(&s->rxbuf, n); retval = stream_recv(s->stream, dp_packet_tail(&s->rxbuf), n); if (retval > 0) { dp_packet_set_size(&s->rxbuf, dp_packet_size(&s->rxbuf) + retval); if (retval == n && dp_packet_size(&s->rxbuf) > 2) { dp_packet_pull(&s->rxbuf, 2); netdev_dummy_queue_packet(dev, dp_packet_clone(&s->rxbuf)); dp_packet_clear(&s->rxbuf); } } else if (retval != -EAGAIN) { error = (retval < 0 ? -retval : dp_packet_size(&s->rxbuf) ? EPROTO : EOF); } } return error; } static void dummy_packet_stream_close(struct dummy_packet_stream *s) { stream_close(s->stream); dp_packet_uninit(&s->rxbuf); pkt_list_delete(&s->txq); } static void dummy_packet_conn_init(struct dummy_packet_conn *conn) { memset(conn, 0, sizeof *conn); conn->type = NONE; } static void dummy_packet_conn_get_config(struct dummy_packet_conn *conn, struct smap *args) { switch (conn->type) { case PASSIVE: smap_add(args, "pstream", pstream_get_name(conn->u.pconn.pstream)); break; case ACTIVE: smap_add(args, "stream", stream_get_name(conn->u.rconn.rstream->stream)); break; case NONE: default: break; } } static void dummy_packet_conn_close(struct dummy_packet_conn *conn) { int i; struct dummy_packet_pconn *pconn = &conn->u.pconn; struct dummy_packet_rconn *rconn = &conn->u.rconn; switch (conn->type) { case PASSIVE: pstream_close(pconn->pstream); for (i = 0; i < pconn->n_streams; i++) { dummy_packet_stream_close(&pconn->streams[i]); } free(pconn->streams); pconn->pstream = NULL; pconn->streams = NULL; break; case ACTIVE: dummy_packet_stream_close(rconn->rstream); free(rconn->rstream); rconn->rstream = NULL; reconnect_destroy(rconn->reconnect); rconn->reconnect = NULL; break; case NONE: default: break; } conn->type = NONE; memset(conn, 0, sizeof *conn); } static void dummy_packet_conn_set_config(struct dummy_packet_conn *conn, const struct smap *args) { const char *pstream = smap_get(args, "pstream"); const char *stream = smap_get(args, "stream"); if (pstream && stream) { VLOG_WARN("Open failed: both %s and %s are configured", pstream, stream); return; } switch (conn->type) { case PASSIVE: if (pstream && !strcmp(pstream_get_name(conn->u.pconn.pstream), pstream)) { return; } dummy_packet_conn_close(conn); break; case ACTIVE: if (stream && !strcmp(stream_get_name(conn->u.rconn.rstream->stream), stream)) { return; } dummy_packet_conn_close(conn); break; case NONE: default: break; } if (pstream) { int error; error = pstream_open(pstream, &conn->u.pconn.pstream, DSCP_DEFAULT); if (error) { VLOG_WARN("%s: open failed (%s)", pstream, ovs_strerror(error)); } else { conn->type = PASSIVE; } } if (stream) { int error; struct stream *active_stream; struct reconnect *reconnect; reconnect = reconnect_create(time_msec()); reconnect_set_name(reconnect, stream); reconnect_set_passive(reconnect, false, time_msec()); reconnect_enable(reconnect, time_msec()); reconnect_set_backoff(reconnect, 100, INT_MAX); reconnect_set_probe_interval(reconnect, 0); conn->u.rconn.reconnect = reconnect; conn->type = ACTIVE; error = stream_open(stream, &active_stream, DSCP_DEFAULT); conn->u.rconn.rstream = dummy_packet_stream_create(active_stream); switch (error) { case 0: reconnect_connected(reconnect, time_msec()); break; case EAGAIN: reconnect_connecting(reconnect, time_msec()); break; default: reconnect_connect_failed(reconnect, time_msec(), error); stream_close(active_stream); conn->u.rconn.rstream->stream = NULL; break; } } } static void dummy_pconn_run(struct netdev_dummy *dev) OVS_REQUIRES(dev->mutex) { struct stream *new_stream; struct dummy_packet_pconn *pconn = &dev->conn.u.pconn; int error; size_t i; error = pstream_accept(pconn->pstream, &new_stream); if (!error) { struct dummy_packet_stream *s; pconn->streams = xrealloc(pconn->streams, ((pconn->n_streams + 1) * sizeof *s)); s = &pconn->streams[pconn->n_streams++]; dummy_packet_stream_init(s, new_stream); } else if (error != EAGAIN) { VLOG_WARN("%s: accept failed (%s)", pstream_get_name(pconn->pstream), ovs_strerror(error)); pstream_close(pconn->pstream); pconn->pstream = NULL; dev->conn.type = NONE; } for (i = 0; i < pconn->n_streams; i++) { struct dummy_packet_stream *s = &pconn->streams[i]; error = dummy_packet_stream_run(dev, s); if (error) { VLOG_DBG("%s: closing connection (%s)", stream_get_name(s->stream), ovs_retval_to_string(error)); dummy_packet_stream_close(s); pconn->streams[i] = pconn->streams[--pconn->n_streams]; } } } static void dummy_rconn_run(struct netdev_dummy *dev) OVS_REQUIRES(dev->mutex) { struct dummy_packet_rconn *rconn = &dev->conn.u.rconn; switch (reconnect_run(rconn->reconnect, time_msec())) { case RECONNECT_CONNECT: { int error; if (rconn->rstream->stream) { error = stream_connect(rconn->rstream->stream); } else { error = stream_open(reconnect_get_name(rconn->reconnect), &rconn->rstream->stream, DSCP_DEFAULT); } switch (error) { case 0: reconnect_connected(rconn->reconnect, time_msec()); break; case EAGAIN: reconnect_connecting(rconn->reconnect, time_msec()); break; default: reconnect_connect_failed(rconn->reconnect, time_msec(), error); stream_close(rconn->rstream->stream); rconn->rstream->stream = NULL; break; } } break; case RECONNECT_DISCONNECT: case RECONNECT_PROBE: default: break; } if (reconnect_is_connected(rconn->reconnect)) { int err; err = dummy_packet_stream_run(dev, rconn->rstream); if (err) { reconnect_disconnected(rconn->reconnect, time_msec(), err); stream_close(rconn->rstream->stream); rconn->rstream->stream = NULL; } } } static void dummy_packet_conn_run(struct netdev_dummy *dev) OVS_REQUIRES(dev->mutex) { switch (dev->conn.type) { case PASSIVE: dummy_pconn_run(dev); break; case ACTIVE: dummy_rconn_run(dev); break; case NONE: default: break; } } static void dummy_packet_conn_wait(struct dummy_packet_conn *conn) { int i; switch (conn->type) { case PASSIVE: pstream_wait(conn->u.pconn.pstream); for (i = 0; i < conn->u.pconn.n_streams; i++) { struct dummy_packet_stream *s = &conn->u.pconn.streams[i]; dummy_packet_stream_wait(s); } break; case ACTIVE: if (reconnect_is_connected(conn->u.rconn.reconnect)) { dummy_packet_stream_wait(conn->u.rconn.rstream); } break; case NONE: default: break; } } static void dummy_packet_conn_send(struct dummy_packet_conn *conn, const void *buffer, size_t size) { int i; switch (conn->type) { case PASSIVE: for (i = 0; i < conn->u.pconn.n_streams; i++) { struct dummy_packet_stream *s = &conn->u.pconn.streams[i]; dummy_packet_stream_send(s, buffer, size); pstream_wait(conn->u.pconn.pstream); } break; case ACTIVE: if (reconnect_is_connected(conn->u.rconn.reconnect)) { dummy_packet_stream_send(conn->u.rconn.rstream, buffer, size); dummy_packet_stream_wait(conn->u.rconn.rstream); } break; case NONE: default: break; } } static enum dummy_netdev_conn_state dummy_netdev_get_conn_state(struct dummy_packet_conn *conn) { enum dummy_netdev_conn_state state; if (conn->type == ACTIVE) { if (reconnect_is_connected(conn->u.rconn.reconnect)) { state = CONN_STATE_CONNECTED; } else { state = CONN_STATE_NOT_CONNECTED; } } else { state = CONN_STATE_UNKNOWN; } return state; } static void netdev_dummy_run(void) { struct netdev_dummy *dev; ovs_mutex_lock(&dummy_list_mutex); LIST_FOR_EACH (dev, list_node, &dummy_list) { ovs_mutex_lock(&dev->mutex); dummy_packet_conn_run(dev); ovs_mutex_unlock(&dev->mutex); } ovs_mutex_unlock(&dummy_list_mutex); } static void netdev_dummy_wait(void) { struct netdev_dummy *dev; ovs_mutex_lock(&dummy_list_mutex); LIST_FOR_EACH (dev, list_node, &dummy_list) { ovs_mutex_lock(&dev->mutex); dummy_packet_conn_wait(&dev->conn); ovs_mutex_unlock(&dev->mutex); } ovs_mutex_unlock(&dummy_list_mutex); } static struct netdev * netdev_dummy_alloc(void) { struct netdev_dummy *netdev = xzalloc(sizeof *netdev); return &netdev->up; } static int netdev_dummy_construct(struct netdev *netdev_) { static atomic_count next_n = ATOMIC_COUNT_INIT(0xaa550000); struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); unsigned int n; n = atomic_count_inc(&next_n); ovs_mutex_init(&netdev->mutex); ovs_mutex_lock(&netdev->mutex); netdev->hwaddr.ea[0] = 0xaa; netdev->hwaddr.ea[1] = 0x55; netdev->hwaddr.ea[2] = n >> 24; netdev->hwaddr.ea[3] = n >> 16; netdev->hwaddr.ea[4] = n >> 8; netdev->hwaddr.ea[5] = n; netdev->mtu = 1500; netdev->flags = 0; netdev->ifindex = -EOPNOTSUPP; dummy_packet_conn_init(&netdev->conn); list_init(&netdev->rxes); ovs_mutex_unlock(&netdev->mutex); ovs_mutex_lock(&dummy_list_mutex); list_push_back(&dummy_list, &netdev->list_node); ovs_mutex_unlock(&dummy_list_mutex); return 0; } static void netdev_dummy_destruct(struct netdev *netdev_) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&dummy_list_mutex); list_remove(&netdev->list_node); ovs_mutex_unlock(&dummy_list_mutex); ovs_mutex_lock(&netdev->mutex); dummy_packet_conn_close(&netdev->conn); netdev->conn.type = NONE; ovs_mutex_unlock(&netdev->mutex); ovs_mutex_destroy(&netdev->mutex); } static void netdev_dummy_dealloc(struct netdev *netdev_) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); free(netdev); } static int netdev_dummy_get_config(const struct netdev *netdev_, struct smap *args) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&netdev->mutex); if (netdev->ifindex >= 0) { smap_add_format(args, "ifindex", "%d", netdev->ifindex); } dummy_packet_conn_get_config(&netdev->conn, args); ovs_mutex_unlock(&netdev->mutex); return 0; } static int netdev_dummy_get_in4(const struct netdev *netdev_, struct in_addr *address, struct in_addr *netmask) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&netdev->mutex); *address = netdev->address; *netmask = netdev->netmask; ovs_mutex_unlock(&netdev->mutex); return address->s_addr ? 0 : EADDRNOTAVAIL; } static int netdev_dummy_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&netdev->mutex); *in6 = netdev->ipv6; ovs_mutex_unlock(&netdev->mutex); return ipv6_addr_is_set(in6) ? 0 : EADDRNOTAVAIL; } static int netdev_dummy_set_in4(struct netdev *netdev_, struct in_addr address, struct in_addr netmask) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&netdev->mutex); netdev->address = address; netdev->netmask = netmask; ovs_mutex_unlock(&netdev->mutex); return 0; } static int netdev_dummy_set_in6(struct netdev *netdev_, struct in6_addr *in6) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); ovs_mutex_lock(&netdev->mutex); netdev->ipv6 = *in6; ovs_mutex_unlock(&netdev->mutex); return 0; } static int netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); const char *pcap; ovs_mutex_lock(&netdev->mutex); netdev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP); dummy_packet_conn_set_config(&netdev->conn, args); if (netdev->rxq_pcap) { fclose(netdev->rxq_pcap); } if (netdev->tx_pcap && netdev->tx_pcap != netdev->rxq_pcap) { fclose(netdev->tx_pcap); } netdev->rxq_pcap = netdev->tx_pcap = NULL; pcap = smap_get(args, "pcap"); if (pcap) { netdev->rxq_pcap = netdev->tx_pcap = ovs_pcap_open(pcap, "ab"); } else { const char *rxq_pcap = smap_get(args, "rxq_pcap"); const char *tx_pcap = smap_get(args, "tx_pcap"); if (rxq_pcap) { netdev->rxq_pcap = ovs_pcap_open(rxq_pcap, "ab"); } if (tx_pcap) { netdev->tx_pcap = ovs_pcap_open(tx_pcap, "ab"); } } ovs_mutex_unlock(&netdev->mutex); return 0; } static struct netdev_rxq * netdev_dummy_rxq_alloc(void) { struct netdev_rxq_dummy *rx = xzalloc(sizeof *rx); return &rx->up; } static int netdev_dummy_rxq_construct(struct netdev_rxq *rxq_) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); ovs_mutex_lock(&netdev->mutex); list_push_back(&netdev->rxes, &rx->node); list_init(&rx->recv_queue); rx->recv_queue_len = 0; rx->seq = seq_create(); ovs_mutex_unlock(&netdev->mutex); return 0; } static void netdev_dummy_rxq_destruct(struct netdev_rxq *rxq_) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); ovs_mutex_lock(&netdev->mutex); list_remove(&rx->node); pkt_list_delete(&rx->recv_queue); ovs_mutex_unlock(&netdev->mutex); seq_destroy(rx->seq); } static void netdev_dummy_rxq_dealloc(struct netdev_rxq *rxq_) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); free(rx); } static int netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **arr, int *c) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); struct dp_packet *packet; ovs_mutex_lock(&netdev->mutex); if (!list_is_empty(&rx->recv_queue)) { struct pkt_list_node *pkt_node; ASSIGN_CONTAINER(pkt_node, list_pop_front(&rx->recv_queue), list_node); packet = pkt_node->pkt; free(pkt_node); rx->recv_queue_len--; } else { packet = NULL; } ovs_mutex_unlock(&netdev->mutex); if (!packet) { return EAGAIN; } ovs_mutex_lock(&netdev->mutex); netdev->stats.rx_packets++; netdev->stats.rx_bytes += dp_packet_size(packet); ovs_mutex_unlock(&netdev->mutex); dp_packet_pad(packet); arr[0] = packet; *c = 1; return 0; } static void netdev_dummy_rxq_wait(struct netdev_rxq *rxq_) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); uint64_t seq = seq_read(rx->seq); ovs_mutex_lock(&netdev->mutex); if (!list_is_empty(&rx->recv_queue)) { poll_immediate_wake(); } else { seq_wait(rx->seq, seq); } ovs_mutex_unlock(&netdev->mutex); } static int netdev_dummy_rxq_drain(struct netdev_rxq *rxq_) { struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_); struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); ovs_mutex_lock(&netdev->mutex); pkt_list_delete(&rx->recv_queue); rx->recv_queue_len = 0; ovs_mutex_unlock(&netdev->mutex); seq_change(rx->seq); return 0; } static int netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED, struct dp_packet **pkts, int cnt, bool may_steal) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); int error = 0; int i; for (i = 0; i < cnt; i++) { const void *buffer = dp_packet_data(pkts[i]); size_t size = dp_packet_size(pkts[i]); if (size < ETH_HEADER_LEN) { error = EMSGSIZE; break; } else { const struct eth_header *eth = buffer; int max_size; ovs_mutex_lock(&dev->mutex); max_size = dev->mtu + ETH_HEADER_LEN; ovs_mutex_unlock(&dev->mutex); if (eth->eth_type == htons(ETH_TYPE_VLAN)) { max_size += VLAN_HEADER_LEN; } if (size > max_size) { error = EMSGSIZE; break; } } ovs_mutex_lock(&dev->mutex); dev->stats.tx_packets++; dev->stats.tx_bytes += size; dummy_packet_conn_send(&dev->conn, buffer, size); /* Reply to ARP requests for 'dev''s assigned IP address. */ if (dev->address.s_addr) { struct dp_packet packet; struct flow flow; dp_packet_use_const(&packet, buffer, size); flow_extract(&packet, &flow); if (flow.dl_type == htons(ETH_TYPE_ARP) && flow.nw_proto == ARP_OP_REQUEST && flow.nw_dst == dev->address.s_addr) { struct dp_packet *reply = dp_packet_new(0); compose_arp(reply, ARP_OP_REPLY, dev->hwaddr, flow.dl_src, false, flow.nw_dst, flow.nw_src); netdev_dummy_queue_packet(dev, reply); } } if (dev->tx_pcap) { struct dp_packet packet; dp_packet_use_const(&packet, buffer, size); ovs_pcap_write(dev->tx_pcap, &packet); fflush(dev->tx_pcap); } ovs_mutex_unlock(&dev->mutex); } if (may_steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(pkts[i]); } } return error; } static int netdev_dummy_set_etheraddr(struct netdev *netdev, const struct eth_addr mac) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dev->mutex); if (!eth_addr_equals(dev->hwaddr, mac)) { dev->hwaddr = mac; netdev_change_seq_changed(netdev); } ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dummy_get_etheraddr(const struct netdev *netdev, struct eth_addr *mac) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dev->mutex); *mac = dev->hwaddr; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dummy_get_mtu(const struct netdev *netdev, int *mtup) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dev->mutex); *mtup = dev->mtu; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dummy_set_mtu(const struct netdev *netdev, int mtu) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dev->mutex); dev->mtu = mtu; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dev->mutex); *stats = dev->stats; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dummy_get_ifindex(const struct netdev *netdev) { struct netdev_dummy *dev = netdev_dummy_cast(netdev); int ifindex; ovs_mutex_lock(&dev->mutex); ifindex = dev->ifindex; ovs_mutex_unlock(&dev->mutex); return ifindex; } static int netdev_dummy_update_flags__(struct netdev_dummy *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) OVS_REQUIRES(netdev->mutex) { if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) { return EINVAL; } *old_flagsp = netdev->flags; netdev->flags |= on; netdev->flags &= ~off; if (*old_flagsp != netdev->flags) { netdev_change_seq_changed(&netdev->up); } return 0; } static int netdev_dummy_update_flags(struct netdev *netdev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { struct netdev_dummy *netdev = netdev_dummy_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = netdev_dummy_update_flags__(netdev, off, on, old_flagsp); ovs_mutex_unlock(&netdev->mutex); return error; } /* Helper functions. */ static const struct netdev_class dummy_class = { "dummy", NULL, /* init */ netdev_dummy_run, netdev_dummy_wait, netdev_dummy_alloc, netdev_dummy_construct, netdev_dummy_destruct, netdev_dummy_dealloc, netdev_dummy_get_config, netdev_dummy_set_config, NULL, /* get_tunnel_config */ NULL, /* build header */ NULL, /* push header */ NULL, /* pop header */ NULL, /* get_numa_id */ NULL, /* set_multiq */ netdev_dummy_send, /* send */ NULL, /* send_wait */ netdev_dummy_set_etheraddr, netdev_dummy_get_etheraddr, netdev_dummy_get_mtu, netdev_dummy_set_mtu, netdev_dummy_get_ifindex, NULL, /* get_carrier */ NULL, /* get_carrier_resets */ NULL, /* get_miimon */ netdev_dummy_get_stats, NULL, /* get_features */ NULL, /* set_advertisements */ NULL, /* set_policing */ NULL, /* get_qos_types */ NULL, /* get_qos_capabilities */ NULL, /* get_qos */ NULL, /* set_qos */ NULL, /* get_queue */ NULL, /* set_queue */ NULL, /* delete_queue */ NULL, /* get_queue_stats */ NULL, /* queue_dump_start */ NULL, /* queue_dump_next */ NULL, /* queue_dump_done */ NULL, /* dump_queue_stats */ netdev_dummy_get_in4, /* get_in4 */ NULL, /* set_in4 */ netdev_dummy_get_in6, /* get_in6 */ NULL, /* add_router */ NULL, /* get_next_hop */ NULL, /* get_status */ NULL, /* arp_lookup */ netdev_dummy_update_flags, netdev_dummy_rxq_alloc, netdev_dummy_rxq_construct, netdev_dummy_rxq_destruct, netdev_dummy_rxq_dealloc, netdev_dummy_rxq_recv, netdev_dummy_rxq_wait, netdev_dummy_rxq_drain, }; static void pkt_list_delete(struct ovs_list *l) { struct pkt_list_node *pkt; LIST_FOR_EACH_POP(pkt, list_node, l) { dp_packet_delete(pkt->pkt); free(pkt); } } static struct dp_packet * eth_from_packet_or_flow(const char *s) { enum odp_key_fitness fitness; struct dp_packet *packet; struct ofpbuf odp_key; struct flow flow; int error; if (!eth_from_hex(s, &packet)) { return packet; } /* Convert string to datapath key. * * It would actually be nicer to parse an OpenFlow-like flow key here, but * the code for that currently calls exit() on parse error. We have to * settle for parsing a datapath key for now. */ ofpbuf_init(&odp_key, 0); error = odp_flow_from_string(s, NULL, &odp_key, NULL); if (error) { ofpbuf_uninit(&odp_key); return NULL; } /* Convert odp_key to flow. */ fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); if (fitness == ODP_FIT_ERROR) { ofpbuf_uninit(&odp_key); return NULL; } packet = dp_packet_new(0); flow_compose(packet, &flow); ofpbuf_uninit(&odp_key); return packet; } static void netdev_dummy_queue_packet__(struct netdev_rxq_dummy *rx, struct dp_packet *packet) { struct pkt_list_node *pkt_node = xmalloc(sizeof *pkt_node); pkt_node->pkt = packet; list_push_back(&rx->recv_queue, &pkt_node->list_node); rx->recv_queue_len++; seq_change(rx->seq); } static void netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct dp_packet *packet) OVS_REQUIRES(dummy->mutex) { struct netdev_rxq_dummy *rx, *prev; if (dummy->rxq_pcap) { ovs_pcap_write(dummy->rxq_pcap, packet); fflush(dummy->rxq_pcap); } prev = NULL; LIST_FOR_EACH (rx, node, &dummy->rxes) { if (rx->recv_queue_len < NETDEV_DUMMY_MAX_QUEUE) { if (prev) { netdev_dummy_queue_packet__(prev, dp_packet_clone(packet)); } prev = rx; } } if (prev) { netdev_dummy_queue_packet__(prev, packet); } else { dp_packet_delete(packet); } } static void netdev_dummy_receive(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct netdev_dummy *dummy_dev; struct netdev *netdev; int i; netdev = netdev_from_name(argv[1]); if (!netdev || !is_dummy_class(netdev->netdev_class)) { unixctl_command_reply_error(conn, "no such dummy netdev"); goto exit; } dummy_dev = netdev_dummy_cast(netdev); for (i = 2; i < argc; i++) { struct dp_packet *packet; packet = eth_from_packet_or_flow(argv[i]); if (!packet) { unixctl_command_reply_error(conn, "bad packet syntax"); goto exit; } ovs_mutex_lock(&dummy_dev->mutex); netdev_dummy_queue_packet(dummy_dev, packet); ovs_mutex_unlock(&dummy_dev->mutex); } unixctl_command_reply(conn, NULL); exit: netdev_close(netdev); } static void netdev_dummy_set_admin_state__(struct netdev_dummy *dev, bool admin_state) OVS_REQUIRES(dev->mutex) { enum netdev_flags old_flags; if (admin_state) { netdev_dummy_update_flags__(dev, 0, NETDEV_UP, &old_flags); } else { netdev_dummy_update_flags__(dev, NETDEV_UP, 0, &old_flags); } } static void netdev_dummy_set_admin_state(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { bool up; if (!strcasecmp(argv[argc - 1], "up")) { up = true; } else if ( !strcasecmp(argv[argc - 1], "down")) { up = false; } else { unixctl_command_reply_error(conn, "Invalid Admin State"); return; } if (argc > 2) { struct netdev *netdev = netdev_from_name(argv[1]); if (netdev && is_dummy_class(netdev->netdev_class)) { struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dummy_dev->mutex); netdev_dummy_set_admin_state__(dummy_dev, up); ovs_mutex_unlock(&dummy_dev->mutex); netdev_close(netdev); } else { unixctl_command_reply_error(conn, "Unknown Dummy Interface"); netdev_close(netdev); return; } } else { struct netdev_dummy *netdev; ovs_mutex_lock(&dummy_list_mutex); LIST_FOR_EACH (netdev, list_node, &dummy_list) { ovs_mutex_lock(&netdev->mutex); netdev_dummy_set_admin_state__(netdev, up); ovs_mutex_unlock(&netdev->mutex); } ovs_mutex_unlock(&dummy_list_mutex); } unixctl_command_reply(conn, "OK"); } static void display_conn_state__(struct ds *s, const char *name, enum dummy_netdev_conn_state state) { ds_put_format(s, "%s: ", name); switch (state) { case CONN_STATE_CONNECTED: ds_put_cstr(s, "connected\n"); break; case CONN_STATE_NOT_CONNECTED: ds_put_cstr(s, "disconnected\n"); break; case CONN_STATE_UNKNOWN: default: ds_put_cstr(s, "unknown\n"); break; }; } static void netdev_dummy_conn_state(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { enum dummy_netdev_conn_state state = CONN_STATE_UNKNOWN; struct ds s; ds_init(&s); if (argc > 1) { const char *dev_name = argv[1]; struct netdev *netdev = netdev_from_name(dev_name); if (netdev && is_dummy_class(netdev->netdev_class)) { struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev); ovs_mutex_lock(&dummy_dev->mutex); state = dummy_netdev_get_conn_state(&dummy_dev->conn); ovs_mutex_unlock(&dummy_dev->mutex); netdev_close(netdev); } display_conn_state__(&s, dev_name, state); } else { struct netdev_dummy *netdev; ovs_mutex_lock(&dummy_list_mutex); LIST_FOR_EACH (netdev, list_node, &dummy_list) { ovs_mutex_lock(&netdev->mutex); state = dummy_netdev_get_conn_state(&netdev->conn); ovs_mutex_unlock(&netdev->mutex); if (state != CONN_STATE_UNKNOWN) { display_conn_state__(&s, netdev->up.name, state); } } ovs_mutex_unlock(&dummy_list_mutex); } unixctl_command_reply(conn, ds_cstr(&s)); ds_destroy(&s); } static void netdev_dummy_ip4addr(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct netdev *netdev = netdev_from_name(argv[1]); if (netdev && is_dummy_class(netdev->netdev_class)) { struct in_addr ip; uint16_t plen; if (ovs_scan(argv[2], IP_SCAN_FMT"/%"SCNi16, IP_SCAN_ARGS(&ip.s_addr), &plen)) { struct in_addr mask; mask.s_addr = be32_prefix_mask(plen); netdev_dummy_set_in4(netdev, ip, mask); unixctl_command_reply(conn, "OK"); } else { unixctl_command_reply_error(conn, "Invalid parameters"); } } else { unixctl_command_reply_error(conn, "Unknown Dummy Interface"); } netdev_close(netdev); } static void netdev_dummy_ip6addr(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct netdev *netdev = netdev_from_name(argv[1]); if (netdev && is_dummy_class(netdev->netdev_class)) { char ip6_s[IPV6_SCAN_LEN + 1]; struct in6_addr ip6; if (ovs_scan(argv[2], IPV6_SCAN_FMT, ip6_s) && inet_pton(AF_INET6, ip6_s, &ip6) == 1) { netdev_dummy_set_in6(netdev, &ip6); unixctl_command_reply(conn, "OK"); } else { unixctl_command_reply_error(conn, "Invalid parameters"); } netdev_close(netdev); } else { unixctl_command_reply_error(conn, "Unknown Dummy Interface"); } netdev_close(netdev); } static void netdev_dummy_override(const char *type) { if (!netdev_unregister_provider(type)) { struct netdev_class *class; int error; class = xmemdup(&dummy_class, sizeof dummy_class); class->type = xstrdup(type); error = netdev_register_provider(class); if (error) { VLOG_ERR("%s: failed to register netdev provider (%s)", type, ovs_strerror(error)); free(CONST_CAST(char *, class->type)); free(class); } } } void netdev_dummy_register(enum dummy_level level) { unixctl_command_register("netdev-dummy/receive", "name packet|flow...", 2, INT_MAX, netdev_dummy_receive, NULL); unixctl_command_register("netdev-dummy/set-admin-state", "[netdev] up|down", 1, 2, netdev_dummy_set_admin_state, NULL); unixctl_command_register("netdev-dummy/conn-state", "[netdev]", 0, 1, netdev_dummy_conn_state, NULL); unixctl_command_register("netdev-dummy/ip4addr", "[netdev] ipaddr/mask-prefix-len", 2, 2, netdev_dummy_ip4addr, NULL); unixctl_command_register("netdev-dummy/ip6addr", "[netdev] ip6addr", 2, 2, netdev_dummy_ip6addr, NULL); if (level == DUMMY_OVERRIDE_ALL) { struct sset types; const char *type; sset_init(&types); netdev_enumerate_types(&types); SSET_FOR_EACH (type, &types) { if (strcmp(type, "patch")) { netdev_dummy_override(type); } } sset_destroy(&types); } else if (level == DUMMY_OVERRIDE_SYSTEM) { netdev_dummy_override("system"); } netdev_register_provider(&dummy_class); netdev_vport_tunnel_register(); } openvswitch-2.5.9/lib/PaxHeaders.82075/pvector.h0000644000000000000000000000013213534540071016245 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.857853986 openvswitch-2.5.9/lib/pvector.h0000644000175000017500000002071413534540071017737 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PVECTOR_H #define PVECTOR_H 1 #include #include #include #include "ovs-rcu.h" #include "util.h" /* Concurrent Priority Vector * ========================== * * Concurrent priority vector holds non-NULL pointers to objects in an * increasing priority order and allows readers to traverse the vector without * being concerned about writers modifying the vector as they are traversing * it. * * The priority order is maintained as a linear vector of elements to allow * for efficient memory prefetching. * * Concurrency is implemented with OVS RCU so that the readers can assume * that once they have taken a pointer to the vector with * pvector_cursor_init(), the 'size' member will not decrease, so that * they can safely read 'size' entries from 'vector', and find that each * entry has a valid, non-NULL 'ptr', and the vector is in order from highest * to lowest 'priority'. The 'priority' values can change any time, but only * so that the order of the entries does not change, so readers can use * 'priority' values read at any time after acquisition of the vector pointer. * * Writers can concurrently add entries to the end of the vector, incrementing * 'size', or update the 'priority' value of an entry, but only if that does * not change the ordering of the entries. Writers will never change the 'ptr' * values, or decrement the 'size' on a copy that readers have access to. * * Most modifications are internally staged at the 'temp' vector, from which * they can be published at 'impl' by calling pvector_publish(). This saves * unnecessary memory allocations when many changes are done back-to-back. * 'temp' may contain NULL pointers and it may be in unsorted order. It is * sorted before it is published at 'impl', which also removes the NULLs from * the published vector. * * Clients should not use priority INT_MIN. */ struct pvector_entry { int priority; void *ptr; }; /* Writers will preallocate space for some entries at the end to avoid future * reallocations. */ enum { PVECTOR_EXTRA_ALLOC = 4 }; struct pvector_impl { size_t size; /* Number of entries in the vector. */ size_t allocated; /* Number of allocated entries. */ struct pvector_entry vector[]; }; /* Concurrent priority vector. */ struct pvector { OVSRCU_TYPE(struct pvector_impl *) impl; struct pvector_impl *temp; }; /* Initialization. */ void pvector_init(struct pvector *); void pvector_destroy(struct pvector *); /* Insertion and deletion. These work on 'temp'. */ void pvector_insert(struct pvector *, void *, int priority); void pvector_change_priority(struct pvector *, void *, int priority); void pvector_remove(struct pvector *, void *); /* Make the modified pvector available for iteration. */ static inline void pvector_publish(struct pvector *); /* Count. These operate on the published pvector. */ static inline size_t pvector_count(const struct pvector *); static inline bool pvector_is_empty(const struct pvector *); /* Iteration. * * * Thread-safety * ============= * * Iteration is safe even in a pvector that is changing concurrently. * Multiple writers must exclude each other via e.g., a mutex. * * Example * ======= * * struct my_node { * int data; * }; * * struct my_node elem1, elem2, *iter; * struct pvector my_pvector; * * pvector_init(&my_pvector); * ...add data... * pvector_insert(&my_pvector, &elem1, 1); * pvector_insert(&my_pvector, &elem2, 2); * ... * PVECTOR_FOR_EACH (iter, &my_pvector) { * ...operate on '*iter'... * ...elem2 to be seen before elem1... * } * ... * pvector_destroy(&my_pvector); * * There is no PVECTOR_FOR_EACH_SAFE variant as iteration is performed on RCU * protected instance of the priority vector. Any concurrent modifications * that would be disruptive for readers (such as deletions), will be performed * on a new instance. To see any of the modifications, a new iteration loop * has to be started. * * The PVECTOR_FOR_EACH_PRIORITY limits the iteration to entries with higher * than given priority and allows for object lookahead. * * The iteration loop must be completed without entering the OVS RCU quiescent * period. That is, an old iteration loop must not be continued after any * blocking IO (VLOG is non-blocking, so that is OK). */ struct pvector_cursor { size_t size; /* Number of entries in the vector. */ size_t entry_idx; /* Current index. */ const struct pvector_entry *vector; }; static inline struct pvector_cursor pvector_cursor_init(const struct pvector *, size_t n_ahead, size_t obj_size); static inline void *pvector_cursor_next(struct pvector_cursor *, int stop_at_priority, size_t n_ahead, size_t obj_size); static inline void pvector_cursor_lookahead(const struct pvector_cursor *, int n, size_t size); #define PVECTOR_FOR_EACH(PTR, PVECTOR) \ for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, 0, 0); \ ((PTR) = pvector_cursor_next(&cursor__, INT_MIN, 0, 0)) != NULL; ) /* Loop while priority is higher than 'PRIORITY' and prefetch objects * of size 'SZ' 'N' objects ahead from the current object. */ #define PVECTOR_FOR_EACH_PRIORITY(PTR, PRIORITY, N, SZ, PVECTOR) \ for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, N, SZ); \ ((PTR) = pvector_cursor_next(&cursor__, PRIORITY, N, SZ)) != NULL; ) #define PVECTOR_CURSOR_FOR_EACH(PTR, CURSOR, PVECTOR) \ for (*(CURSOR) = pvector_cursor_init(PVECTOR, 0, 0); \ ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; ) #define PVECTOR_CURSOR_FOR_EACH_CONTINUE(PTR, CURSOR) \ for (; ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; ) /* Inline implementations. */ static inline struct pvector_cursor pvector_cursor_init(const struct pvector *pvec, size_t n_ahead, size_t obj_size) { const struct pvector_impl *impl; struct pvector_cursor cursor; impl = ovsrcu_get(struct pvector_impl *, &pvec->impl); ovs_prefetch_range(impl->vector, impl->size * sizeof impl->vector[0]); cursor.size = impl->size; cursor.vector = impl->vector; cursor.entry_idx = -1; for (size_t i = 0; i < n_ahead; i++) { /* Prefetch the first objects. */ pvector_cursor_lookahead(&cursor, i, obj_size); } return cursor; } static inline void *pvector_cursor_next(struct pvector_cursor *cursor, int stop_at_priority, size_t n_ahead, size_t obj_size) { if (++cursor->entry_idx < cursor->size && cursor->vector[cursor->entry_idx].priority > stop_at_priority) { if (n_ahead) { pvector_cursor_lookahead(cursor, n_ahead, obj_size); } return cursor->vector[cursor->entry_idx].ptr; } return NULL; } static inline void pvector_cursor_lookahead(const struct pvector_cursor *cursor, int n, size_t size) { if (cursor->entry_idx + n < cursor->size) { ovs_prefetch_range(cursor->vector[cursor->entry_idx + n].ptr, size); } } static inline size_t pvector_count(const struct pvector *pvec) { return ovsrcu_get(struct pvector_impl *, &pvec->impl)->size; } static inline bool pvector_is_empty(const struct pvector *pvec) { return pvector_count(pvec) == 0; } void pvector_publish__(struct pvector *); /* Make the modified pvector available for iteration. */ static inline void pvector_publish(struct pvector *pvec) { if (pvec->temp) { pvector_publish__(pvec); } } #endif /* pvector.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-locked.c0000644000000000000000000000013213534540071020076 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.817853691 openvswitch-2.5.9/lib/ovs-atomic-locked.c0000644000175000017500000000326213534540071021567 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovs-atomic.h" #include "hash.h" #include "ovs-thread.h" #ifdef OVS_ATOMIC_LOCKED_IMPL static struct ovs_mutex * mutex_for_pointer(void *p) { OVS_ALIGNED_STRUCT(CACHE_LINE_SIZE, aligned_mutex) { struct ovs_mutex mutex; char pad[PAD_SIZE(sizeof(struct ovs_mutex), CACHE_LINE_SIZE)]; }; static struct aligned_mutex atomic_mutexes[] = { #define MUTEX_INIT { .mutex = OVS_MUTEX_INITIALIZER } #define MUTEX_INIT4 MUTEX_INIT, MUTEX_INIT, MUTEX_INIT, MUTEX_INIT #define MUTEX_INIT16 MUTEX_INIT4, MUTEX_INIT4, MUTEX_INIT4, MUTEX_INIT4 MUTEX_INIT16, MUTEX_INIT16, }; BUILD_ASSERT_DECL(IS_POW2(ARRAY_SIZE(atomic_mutexes))); uint32_t hash = hash_pointer(p, 0); uint32_t indx = hash & (ARRAY_SIZE(atomic_mutexes) - 1); return &atomic_mutexes[indx].mutex; } void atomic_lock__(void *p) OVS_ACQUIRES(mutex_for_pointer(p)) { ovs_mutex_lock(mutex_for_pointer(p)); } void atomic_unlock__(void *p) OVS_RELEASES(mutex_for_pointer(p)) { ovs_mutex_unlock(mutex_for_pointer(p)); } #endif /* OVS_ATOMIC_LOCKED_IMPL */ openvswitch-2.5.9/lib/PaxHeaders.82075/pcap-file.c0000644000000000000000000000013213534540071016416 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.845853898 openvswitch-2.5.9/lib/pcap-file.c0000644000175000017500000002363013534540071020110 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "pcap-file.h" #include #include #include #include #include #include "byte-order.h" #include "compiler.h" #include "dp-packet.h" #include "flow.h" #include "hmap.h" #include "packets.h" #include "timeval.h" #include "unaligned.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(pcap); struct pcap_hdr { uint32_t magic_number; /* magic number */ uint16_t version_major; /* major version number */ uint16_t version_minor; /* minor version number */ int32_t thiszone; /* GMT to local correction */ uint32_t sigfigs; /* accuracy of timestamps */ uint32_t snaplen; /* max length of captured packets */ uint32_t network; /* data link type */ }; BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24); struct pcaprec_hdr { uint32_t ts_sec; /* timestamp seconds */ uint32_t ts_usec; /* timestamp microseconds */ uint32_t incl_len; /* number of octets of packet saved in file */ uint32_t orig_len; /* actual length of packet */ }; BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16); FILE * ovs_pcap_open(const char *file_name, const char *mode) { struct stat s; FILE *file; int error; ovs_assert(!strcmp(mode, "rb") || !strcmp(mode, "wb") || !strcmp(mode, "ab")); file = fopen(file_name, mode); if (file == NULL) { VLOG_WARN("%s: failed to open pcap file for %s (%s)", file_name, (mode[0] == 'r' ? "reading" : mode[0] == 'w' ? "writing" : "appending"), ovs_strerror(errno)); return NULL; } switch (mode[0]) { case 'r': error = ovs_pcap_read_header(file); if (error) { errno = error; fclose(file); return NULL; } break; case 'w': ovs_pcap_write_header(file); break; case 'a': if (!fstat(fileno(file), &s) && !s.st_size) { ovs_pcap_write_header(file); } break; default: OVS_NOT_REACHED(); } return file; } int ovs_pcap_read_header(FILE *file) { struct pcap_hdr ph; if (fread(&ph, sizeof ph, 1, file) != 1) { int error = ferror(file) ? errno : EOF; VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error)); return error; } if (ph.magic_number != 0xa1b2c3d4 && ph.magic_number != 0xd4c3b2a1) { VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file " "(expected 0xa1b2c3d4 or 0xd4c3b2a1)", ph.magic_number); return EPROTO; } return 0; } void ovs_pcap_write_header(FILE *file) { /* The pcap reader is responsible for figuring out endianness based on the * magic number, so the lack of htonX calls here is intentional. */ struct pcap_hdr ph; ph.magic_number = 0xa1b2c3d4; ph.version_major = 2; ph.version_minor = 4; ph.thiszone = 0; ph.sigfigs = 0; ph.snaplen = 1518; ph.network = 1; /* Ethernet */ ignore(fwrite(&ph, sizeof ph, 1, file)); fflush(file); } int ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when) { struct pcaprec_hdr prh; struct dp_packet *buf; void *data; size_t len; bool swap; *bufp = NULL; /* Read header. */ if (fread(&prh, sizeof prh, 1, file) != 1) { if (ferror(file)) { int error = errno; VLOG_WARN("failed to read pcap record header: %s", ovs_retval_to_string(error)); return error; } else { return EOF; } } /* Calculate length. */ len = prh.incl_len; swap = len > 0xffff; if (swap) { len = uint32_byteswap(len); if (len > 0xffff) { VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32" " "reading pcap file", len, uint32_byteswap(len)); return EPROTO; } } /* Calculate time. */ if (when) { uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec; uint32_t ts_usec = swap ? uint32_byteswap(prh.ts_usec) : prh.ts_usec; *when = ts_sec * 1000LL + ts_usec / 1000; } /* Read packet. */ buf = dp_packet_new(len); data = dp_packet_put_uninit(buf, len); if (fread(data, len, 1, file) != 1) { int error = ferror(file) ? errno : EOF; VLOG_WARN("failed to read pcap packet: %s", ovs_retval_to_string(error)); dp_packet_delete(buf); return error; } *bufp = buf; return 0; } void ovs_pcap_write(FILE *file, struct dp_packet *buf) { struct pcaprec_hdr prh; struct timeval tv; xgettimeofday(&tv); prh.ts_sec = tv.tv_sec; prh.ts_usec = tv.tv_usec; prh.incl_len = dp_packet_size(buf); prh.orig_len = dp_packet_size(buf); ignore(fwrite(&prh, sizeof prh, 1, file)); ignore(fwrite(dp_packet_data(buf), dp_packet_size(buf), 1, file)); fflush(file); } struct tcp_key { ovs_be32 nw_src, nw_dst; ovs_be16 tp_src, tp_dst; }; struct tcp_stream { struct hmap_node hmap_node; struct tcp_key key; uint32_t seq_no; struct dp_packet payload; }; struct tcp_reader { struct hmap streams; }; static void tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream) { hmap_remove(&r->streams, &stream->hmap_node); dp_packet_uninit(&stream->payload); free(stream); } /* Returns a new data structure for extracting TCP stream data from an * Ethernet packet capture */ struct tcp_reader * tcp_reader_open(void) { struct tcp_reader *r; r = xmalloc(sizeof *r); hmap_init(&r->streams); return r; } /* Closes and frees 'r'. */ void tcp_reader_close(struct tcp_reader *r) { struct tcp_stream *stream, *next_stream; HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) { tcp_stream_destroy(r, stream); } hmap_destroy(&r->streams); free(r); } static struct tcp_stream * tcp_stream_lookup(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash) { struct tcp_stream *stream; HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) { if (!memcmp(&stream->key, key, sizeof *key)) { return stream; } } return NULL; } static struct tcp_stream * tcp_stream_new(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash) { struct tcp_stream *stream; stream = xmalloc(sizeof *stream); hmap_insert(&r->streams, &stream->hmap_node, hash); memcpy(&stream->key, key, sizeof *key); stream->seq_no = 0; dp_packet_init(&stream->payload, 2048); return stream; } /* Processes 'packet' through TCP reader 'r'. The caller must have already * extracted the packet's headers into 'flow', using flow_extract(). * * If 'packet' is a TCP packet, then the reader attempts to reconstruct the * data stream. If successful, it returns an dp_packet that represents the data * stream so far. The caller may examine the data in the dp_packet and pull off * any data that it has fully processed. The remaining data that the caller * does not pull off will be presented again in future calls if more data * arrives in the stream. * * Returns null if 'packet' doesn't add new data to a TCP stream. */ struct dp_packet * tcp_reader_run(struct tcp_reader *r, const struct flow *flow, const struct dp_packet *packet) { struct tcp_stream *stream; struct tcp_header *tcp; struct dp_packet *payload; unsigned int l7_length; struct tcp_key key; uint32_t hash; uint32_t seq; uint8_t flags; const char *l7 = dp_packet_get_tcp_payload(packet); if (flow->dl_type != htons(ETH_TYPE_IP) || flow->nw_proto != IPPROTO_TCP || !l7) { return NULL; } tcp = dp_packet_l4(packet); flags = TCP_FLAGS(tcp->tcp_ctl); l7_length = (char *) dp_packet_tail(packet) - l7; seq = ntohl(get_16aligned_be32(&tcp->tcp_seq)); /* Construct key. */ memset(&key, 0, sizeof key); key.nw_src = flow->nw_src; key.nw_dst = flow->nw_dst; key.tp_src = flow->tp_src; key.tp_dst = flow->tp_dst; hash = hash_bytes(&key, sizeof key, 0); /* Find existing stream or start a new one for a SYN or if there's data. */ stream = tcp_stream_lookup(r, &key, hash); if (!stream) { if (flags & TCP_SYN || l7_length) { stream = tcp_stream_new(r, &key, hash); stream->seq_no = flags & TCP_SYN ? seq + 1 : seq; } else { return NULL; } } payload = &stream->payload; if (flags & TCP_SYN || !stream->seq_no) { dp_packet_clear(payload); stream->seq_no = seq + 1; return NULL; } else if (flags & (TCP_FIN | TCP_RST)) { tcp_stream_destroy(r, stream); return NULL; } else if (seq == stream->seq_no) { /* Shift all of the existing payload to the very beginning of the * allocated space, so that we reuse allocated space instead of * continually expanding it. */ dp_packet_shift(payload, (char *) dp_packet_base(payload) - (char *) dp_packet_data(payload)); dp_packet_put(payload, l7, l7_length); stream->seq_no += l7_length; return payload; } else { return NULL; } } openvswitch-2.5.9/lib/PaxHeaders.82075/reconnect.h0000644000000000000000000000013213534540071016543 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.865854045 openvswitch-2.5.9/lib/reconnect.h0000644000175000017500000001206113534540071020231 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RECONNECT_H #define RECONNECT_H 1 /* This library implements a finite-state machine for connecting and * reconnecting to a network resource with exponential backoff. It also * provides optional support for detecting a connection on which the peer is no * longer responding. * * The library does not implement anything networking related, only an FSM for * networking code to use. * * Many "reconnect" functions take a "now" argument. This makes testing easier * since there is no hidden state. When not testing, just pass the return * value of time_msec() from timeval.h. (Perhaps this design should be * revisited later.) */ #include struct reconnect *reconnect_create(long long int now); void reconnect_destroy(struct reconnect *); void reconnect_set_quiet(struct reconnect *, bool quiet); const char *reconnect_get_name(const struct reconnect *); void reconnect_set_name(struct reconnect *, const char *name); /* Defaults, all in msecs. */ #define RECONNECT_DEFAULT_MIN_BACKOFF 1000 #define RECONNECT_DEFAULT_MAX_BACKOFF 8000 #define RECONNECT_DEFAULT_PROBE_INTERVAL 5000 int reconnect_get_min_backoff(const struct reconnect *); int reconnect_get_max_backoff(const struct reconnect *); int reconnect_get_probe_interval(const struct reconnect *); void reconnect_set_max_tries(struct reconnect *, unsigned int max_tries); unsigned int reconnect_get_max_tries(struct reconnect *); void reconnect_set_backoff(struct reconnect *, int min_backoff, int max_backoff); void reconnect_set_probe_interval(struct reconnect *, int probe_interval); bool reconnect_is_passive(const struct reconnect *); void reconnect_set_passive(struct reconnect *, bool passive, long long int now); bool reconnect_is_enabled(const struct reconnect *); void reconnect_enable(struct reconnect *, long long int now); void reconnect_disable(struct reconnect *, long long int now); void reconnect_force_reconnect(struct reconnect *, long long int now); bool reconnect_is_connected(const struct reconnect *); unsigned int reconnect_get_last_connect_elapsed(const struct reconnect *, long long int now); unsigned int reconnect_get_last_disconnect_elapsed(const struct reconnect *, long long int now); void reconnect_disconnected(struct reconnect *, long long int now, int error); void reconnect_connecting(struct reconnect *, long long int now); void reconnect_listening(struct reconnect *, long long int now); void reconnect_listen_error(struct reconnect *, long long int now, int error); void reconnect_connected(struct reconnect *, long long int now); void reconnect_connect_failed(struct reconnect *, long long int now, int error); void reconnect_activity(struct reconnect *, long long int now); enum reconnect_action { RECONNECT_CONNECT = 1, RECONNECT_DISCONNECT, RECONNECT_PROBE, }; enum reconnect_action reconnect_run(struct reconnect *, long long int now); void reconnect_wait(struct reconnect *, long long int now); int reconnect_timeout(struct reconnect *, long long int now); struct reconnect_stats { /* All times and durations in this structure are in milliseconds. */ long long int creation_time; /* Time reconnect_create() called. */ long long int last_activity; /* Last call to reconnect_activity(). */ long long int last_connected; /* Last call to reconnect_connected(). */ long long int last_disconnected; /* Last call to reconnect_disconnected(). */ int backoff; /* Current backoff duration. */ unsigned int seqno; /* # of connections + # of disconnections. */ bool is_connected; /* Currently connected? */ unsigned int msec_since_connect; /* Time since last connect. */ unsigned int msec_since_disconnect; /* Time since last disconnect. */ unsigned int total_connected_duration; /* Sum of all connections. */ unsigned int n_attempted_connections; unsigned int n_successful_connections; /* These should only be provided to a human user for debugging purposes. * The client should not attempt to interpret them. */ const char *state; /* FSM state. */ unsigned int state_elapsed; /* Time since FSM state entered. */ }; void reconnect_get_stats(const struct reconnect *, long long int now, struct reconnect_stats *); #endif /* reconnect.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/random.c0000644000000000000000000000013213534540071016036 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.857853986 openvswitch-2.5.9/lib/random.c0000644000175000017500000000516313534540071017531 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "random.h" #include #include #include #include "entropy.h" #include "hash.h" #include "ovs-thread.h" #include "timeval.h" #include "util.h" /* This is the 32-bit PRNG recommended in G. Marsaglia, "Xorshift RNGs", * _Journal of Statistical Software_ 8:14 (July 2003). According to the paper, * it has a period of 2**32 - 1 and passes almost all tests of randomness. * * We use this PRNG instead of libc's rand() because rand() varies in quality * and because its maximum value also varies between 32767 and INT_MAX, whereas * we often want random numbers in the full range of uint32_t. * * This random number generator is intended for purposes that do not require * cryptographic-quality randomness. */ /* Current random state. */ DEFINE_STATIC_PER_THREAD_DATA(uint32_t, seed, 0); static uint32_t random_next(void); void random_init(void) { uint32_t *seedp = seed_get(); while (!*seedp) { struct timeval tv; uint32_t entropy; pthread_t self; xgettimeofday(&tv); get_entropy_or_die(&entropy, 4); self = pthread_self(); *seedp = (tv.tv_sec ^ tv.tv_usec ^ entropy ^ hash_bytes(&self, sizeof self, 0)); } } void random_set_seed(uint32_t seed_) { ovs_assert(seed_); *seed_get() = seed_; } void random_bytes(void *p_, size_t n) { uint8_t *p = p_; random_init(); for (; n > 4; p += 4, n -= 4) { uint32_t x = random_next(); memcpy(p, &x, 4); } if (n) { uint32_t x = random_next(); memcpy(p, &x, n); } } uint32_t random_uint32(void) { random_init(); return random_next(); } uint64_t random_uint64(void) { uint64_t x; random_init(); x = random_next(); x |= (uint64_t) random_next() << 32; return x; } static uint32_t random_next(void) { uint32_t *seedp = seed_get_unsafe(); *seedp ^= *seedp << 13; *seedp ^= *seedp >> 17; *seedp ^= *seedp << 5; return *seedp; } openvswitch-2.5.9/lib/PaxHeaders.82075/latch-windows.c0000644000000000000000000000013113534540071017340 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.961854753 openvswitch-2.5.9/lib/latch-windows.c0000644000175000017500000000400613534540071021027 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "latch.h" #include #include #include #include "poll-loop.h" #include "socket-util.h" /* Initializes 'latch' as initially unset. */ void latch_init(struct latch *latch) { latch->is_set = FALSE; latch->wevent = CreateEvent(NULL, TRUE, FALSE, NULL); } /* Destroys 'latch'. */ void latch_destroy(struct latch *latch) { latch->is_set = FALSE; CloseHandle(latch->wevent); } /* Resets 'latch' to the unset state. Returns true if 'latch' was previously * set, false otherwise. */ bool latch_poll(struct latch *latch) { bool is_set; is_set = latch->is_set; latch->is_set = FALSE; ResetEvent(latch->wevent); return is_set; } /* Sets 'latch'. * * Calls are not additive: a single latch_poll() clears out any number of * latch_set(). */ void latch_set(struct latch *latch) { latch->is_set = TRUE; SetEvent(latch->wevent); } /* Returns true if 'latch' is set, false otherwise. Does not reset 'latch' * to the unset state. */ bool latch_is_set(const struct latch *latch) { return latch->is_set; } /* Causes the next poll_block() to wake up when 'latch' is set. * * ('where' is used in debug logging. Commonly one would use latch_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ void latch_wait_at(const struct latch *latch, const char *where) { poll_wevent_wait_at(latch->wevent, where); } openvswitch-2.5.9/lib/PaxHeaders.82075/lockfile.c0000644000000000000000000000013013534540071016344 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.757853249 openvswitch-2.5.9/lib/lockfile.c0000644000175000017500000002430613534540071020041 0ustar00jpettitjpettit00000000000000 /* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "lockfile.h" #include #include #include #include #include #include #include "coverage.h" #include "hash.h" #include "hmap.h" #include "ovs-thread.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(lockfile); COVERAGE_DEFINE(lockfile_lock); COVERAGE_DEFINE(lockfile_error); COVERAGE_DEFINE(lockfile_unlock); struct lockfile { struct hmap_node hmap_node; char *name; dev_t device; ino_t inode; int fd; HANDLE lock_handle; }; /* Lock table. * * We have to do this stupid dance because POSIX says that closing *any* file * descriptor for a file on which a process holds a lock drops *all* locks on * that file. That means that we can't afford to open a lockfile more than * once. */ static struct ovs_mutex lock_table_mutex = OVS_MUTEX_INITIALIZER; static struct hmap lock_table__ = HMAP_INITIALIZER(&lock_table__); static struct hmap *const lock_table OVS_GUARDED_BY(lock_table_mutex) = &lock_table__; static void lockfile_unhash(struct lockfile *); static int lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) OVS_REQUIRES(&lock_table_mutex); static void lockfile_do_unlock(struct lockfile * lockfile) OVS_REQUIRES(&lock_table_mutex); /* Returns the name of the lockfile that would be created for locking a file * named 'filename_'. The caller is responsible for freeing the returned name, * with free(), when it is no longer needed. */ char * lockfile_name(const char *filename_) { char *filename; const char *slash; char *lockname; /* If 'filename_' is a symlink, base the name of the lockfile on the * symlink's target rather than the name of the symlink. That way, if a * file is symlinked, but there is no symlink for its lockfile, then there * is only a single lockfile for both the source and the target of the * symlink, not one for each. */ filename = follow_symlinks(filename_); slash = strrchr(filename, '/'); #ifdef _WIN32 char *backslash = strrchr(filename, '\\'); if (backslash && (!slash || backslash > slash)) { slash = backslash; } #endif lockname = (slash ? xasprintf("%.*s/.%s.~lock~", (int) (slash - filename), filename, slash + 1) : xasprintf(".%s.~lock~", filename)); free(filename); return lockname; } /* Locks the configuration file against modification by other processes and * re-reads it from disk. * * Returns 0 on success, otherwise a positive errno value. On success, * '*lockfilep' is set to point to a new "struct lockfile *" that may be * unlocked with lockfile_unlock(). On failure, '*lockfilep' is set to * NULL. Will not block if the lock cannot be immediately acquired. */ int lockfile_lock(const char *file, struct lockfile **lockfilep) { /* Only exclusive ("write") locks are supported. This is not a problem * because the Open vSwitch code that currently uses lock files does so in * stylized ways such that any number of readers may access a file while it * is being written. */ char *lock_name; pid_t pid; int error; COVERAGE_INC(lockfile_lock); lock_name = lockfile_name(file); ovs_mutex_lock(&lock_table_mutex); error = lockfile_try_lock(lock_name, &pid, lockfilep); ovs_mutex_unlock(&lock_table_mutex); if (error) { COVERAGE_INC(lockfile_error); if (error == EACCES) { error = EAGAIN; } if (pid == getpid()) { VLOG_WARN("%s: cannot lock file because this process has already " "locked it", lock_name); } else if (pid) { VLOG_WARN("%s: cannot lock file because it is already locked by " "pid %ld", lock_name, (long int) pid); } else { VLOG_WARN("%s: failed to lock file: %s", lock_name, ovs_strerror(error)); } } free(lock_name); return error; } /* Unlocks 'lockfile', which must have been created by a call to * lockfile_lock(), and frees 'lockfile'. */ void lockfile_unlock(struct lockfile *lockfile) { if (lockfile) { ovs_mutex_lock(&lock_table_mutex); lockfile_do_unlock(lockfile); ovs_mutex_unlock(&lock_table_mutex); COVERAGE_INC(lockfile_unlock); free(lockfile->name); free(lockfile); } } /* Marks all the currently locked lockfiles as no longer locked. It makes * sense to call this function after fork(), because a child created by fork() * does not hold its parents' locks. */ void lockfile_postfork(void) { struct lockfile *lockfile; ovs_mutex_lock(&lock_table_mutex); HMAP_FOR_EACH (lockfile, hmap_node, lock_table) { if (lockfile->fd >= 0) { VLOG_WARN("%s: child does not inherit lock", lockfile->name); lockfile_unhash(lockfile); } } ovs_mutex_unlock(&lock_table_mutex); } static uint32_t lockfile_hash(dev_t device, ino_t inode) { return hash_bytes(&device, sizeof device, hash_bytes(&inode, sizeof inode, 0)); } static struct lockfile * lockfile_find(dev_t device, ino_t inode) OVS_REQUIRES(&lock_table_mutex) { struct lockfile *lockfile; HMAP_FOR_EACH_WITH_HASH (lockfile, hmap_node, lockfile_hash(device, inode), lock_table) { if (lockfile->device == device && lockfile->inode == inode) { return lockfile; } } return NULL; } static void lockfile_unhash(struct lockfile *lockfile) OVS_REQUIRES(&lock_table_mutex) { if (lockfile->fd >= 0) { close(lockfile->fd); lockfile->fd = -1; hmap_remove(lock_table, &lockfile->hmap_node); } } static struct lockfile * lockfile_register(const char *name, dev_t device, ino_t inode, int fd) OVS_REQUIRES(&lock_table_mutex) { struct lockfile *lockfile; lockfile = lockfile_find(device, inode); if (lockfile) { VLOG_ERR("%s: lock file disappeared and reappeared!", name); lockfile_unhash(lockfile); } lockfile = xmalloc(sizeof *lockfile); lockfile->name = xstrdup(name); lockfile->device = device; lockfile->inode = inode; lockfile->fd = fd; hmap_insert(lock_table, &lockfile->hmap_node, lockfile_hash(device, inode)); return lockfile; } #ifdef _WIN32 static void lockfile_do_unlock(struct lockfile *lockfile) OVS_REQUIRES(&lock_table_mutex) { if (lockfile->fd >= 0) { OVERLAPPED overl; overl.hEvent = 0; overl.Offset = 0; overl.OffsetHigh = 0; UnlockFileEx(lockfile->lock_handle, 0, 1, 0, &overl); close(lockfile->fd); lockfile->fd = -1; } } static int lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) OVS_REQUIRES(&lock_table_mutex) { HANDLE lock_handle; BOOL retval; OVERLAPPED overl; struct lockfile *lockfile; int fd; *pidp = 0; fd = open(name, O_RDWR | O_CREAT, 0600); if (fd < 0) { VLOG_WARN("%s: failed to open lock file: %s", name, ovs_strerror(errno)); return errno; } lock_handle = (HANDLE)_get_osfhandle(fd); if (lock_handle < 0) { VLOG_WARN("%s: failed to get the file handle: %s", name, ovs_strerror(errno)); return errno; } /* Lock the file 'name' for the region that includes just the first * byte. */ overl.hEvent = 0; overl.Offset = 0; overl.OffsetHigh = 0; retval = LockFileEx(lock_handle, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &overl); if (!retval) { VLOG_DBG("Failed to lock file : %s", ovs_lasterror_to_string()); *pidp = getpid(); return EDEADLK; } lockfile = xmalloc(sizeof *lockfile); lockfile->name = xstrdup(name); lockfile->fd = fd; lockfile->lock_handle = lock_handle; *lockfilep = lockfile; return 0; } #else /* !_WIN32 */ static void lockfile_do_unlock(struct lockfile *lockfile) { lockfile_unhash(lockfile); } static int lockfile_try_lock(const char *name, pid_t *pidp, struct lockfile **lockfilep) OVS_REQUIRES(&lock_table_mutex) { struct flock l; struct stat s; int error; int fd; *lockfilep = NULL; *pidp = 0; /* Check whether we've already got a lock on that file. */ if (!stat(name, &s)) { if (lockfile_find(s.st_dev, s.st_ino)) { *pidp = getpid(); return EDEADLK; } } else if (errno != ENOENT) { VLOG_WARN("%s: failed to stat lock file: %s", name, ovs_strerror(errno)); return errno; } /* Open the lock file. */ fd = open(name, O_RDWR | O_CREAT, 0600); if (fd < 0) { VLOG_WARN("%s: failed to open lock file: %s", name, ovs_strerror(errno)); return errno; } /* Get the inode and device number for the lock table. */ if (fstat(fd, &s)) { VLOG_ERR("%s: failed to fstat lock file: %s", name, ovs_strerror(errno)); close(fd); return errno; } /* Try to lock the file. */ memset(&l, 0, sizeof l); l.l_type = F_WRLCK; l.l_whence = SEEK_SET; l.l_start = 0; l.l_len = 0; error = fcntl(fd, F_SETLK, &l) == -1 ? errno : 0; if (!error) { *lockfilep = lockfile_register(name, s.st_dev, s.st_ino, fd); } else { if (!fcntl(fd, F_GETLK, &l) && l.l_type != F_UNLCK) { *pidp = l.l_pid; } close(fd); } return error; } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/pcap-file.h0000644000000000000000000000013213534540071016423 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.845853898 openvswitch-2.5.9/lib/pcap-file.h0000644000175000017500000000242113534540071020110 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PCAP_FILE_H #define PCAP_FILE_H 1 #include struct flow; struct dp_packet; /* PCAP file reading and writing. */ FILE *ovs_pcap_open(const char *file_name, const char *mode); int ovs_pcap_read_header(FILE *); void ovs_pcap_write_header(FILE *); int ovs_pcap_read(FILE *, struct dp_packet **, long long int *when); void ovs_pcap_write(FILE *, struct dp_packet *); /* Extracting TCP stream data from an Ethernet packet capture. */ struct tcp_reader *tcp_reader_open(void); void tcp_reader_close(struct tcp_reader *); struct dp_packet *tcp_reader_run(struct tcp_reader *, const struct flow *, const struct dp_packet *); #endif /* pcap-file.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/classifier.c0000644000000000000000000000013013534540071016700 xustar0030 mtime=1567801401.321680583 30 atime=1567801402.069686075 28 ctime=1567801424.6698526 openvswitch-2.5.9/lib/classifier.c0000644000175000017500000024072413534540071020401 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "classifier.h" #include "classifier-private.h" #include #include #include "byte-order.h" #include "dynamic-string.h" #include "odp-util.h" #include "ofp-util.h" #include "packets.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(classifier); struct trie_ctx; /* A collection of "struct cls_conjunction"s currently embedded into a * cls_match. */ struct cls_conjunction_set { /* Link back to the cls_match. * * cls_conjunction_set is mostly used during classifier lookup, and, in * turn, during classifier lookup the most used member of * cls_conjunction_set is the rule's priority, so we cache it here for fast * access. */ struct cls_match *match; int priority; /* Cached copy of match->priority. */ /* Conjunction information. * * 'min_n_clauses' allows some optimization during classifier lookup. */ unsigned int n; /* Number of elements in 'conj'. */ unsigned int min_n_clauses; /* Smallest 'n' among elements of 'conj'. */ struct cls_conjunction conj[]; }; /* Ports trie depends on both ports sharing the same ovs_be32. */ #define TP_PORTS_OFS32 (offsetof(struct flow, tp_src) / 4) BUILD_ASSERT_DECL(TP_PORTS_OFS32 == offsetof(struct flow, tp_dst) / 4); BUILD_ASSERT_DECL(TP_PORTS_OFS32 % 2 == 0); #define TP_PORTS_OFS64 (TP_PORTS_OFS32 / 2) static size_t cls_conjunction_set_size(size_t n) { return (sizeof(struct cls_conjunction_set) + n * sizeof(struct cls_conjunction)); } static struct cls_conjunction_set * cls_conjunction_set_alloc(struct cls_match *match, const struct cls_conjunction conj[], size_t n) { if (n) { size_t min_n_clauses = conj[0].n_clauses; for (size_t i = 1; i < n; i++) { min_n_clauses = MIN(min_n_clauses, conj[i].n_clauses); } struct cls_conjunction_set *set = xmalloc(cls_conjunction_set_size(n)); set->match = match; set->priority = match->priority; set->n = n; set->min_n_clauses = min_n_clauses; memcpy(set->conj, conj, n * sizeof *conj); return set; } else { return NULL; } } static struct cls_match * cls_match_alloc(const struct cls_rule *rule, cls_version_t version, const struct cls_conjunction conj[], size_t n) { size_t count = miniflow_n_values(rule->match.flow); struct cls_match *cls_match = xmalloc(sizeof *cls_match + MINIFLOW_VALUES_SIZE(count)); ovsrcu_init(&cls_match->next, NULL); *CONST_CAST(const struct cls_rule **, &cls_match->cls_rule) = rule; *CONST_CAST(int *, &cls_match->priority) = rule->priority; *CONST_CAST(cls_version_t *, &cls_match->add_version) = version; atomic_init(&cls_match->remove_version, version); /* Initially * invisible. */ miniflow_clone(CONST_CAST(struct miniflow *, &cls_match->flow), rule->match.flow, count); ovsrcu_set_hidden(&cls_match->conj_set, cls_conjunction_set_alloc(cls_match, conj, n)); return cls_match; } static struct cls_subtable *find_subtable(const struct classifier *cls, const struct minimask *); static struct cls_subtable *insert_subtable(struct classifier *cls, const struct minimask *); static void destroy_subtable(struct classifier *cls, struct cls_subtable *); static const struct cls_match *find_match_wc(const struct cls_subtable *, cls_version_t version, const struct flow *, struct trie_ctx *, unsigned int n_tries, struct flow_wildcards *); static struct cls_match *find_equal(const struct cls_subtable *, const struct miniflow *, uint32_t hash); /* Return the next visible (lower-priority) rule in the list. Multiple * identical rules with the same priority may exist transitionally, but when * versioning is used at most one of them is ever visible for lookups on any * given 'version'. */ static inline const struct cls_match * next_visible_rule_in_list(const struct cls_match *rule, cls_version_t version) { do { rule = cls_match_next(rule); } while (rule && !cls_match_visible_in_version(rule, version)); return rule; } /* Type with maximum supported prefix length. */ union trie_prefix { struct in6_addr ipv6; /* For sizing. */ ovs_be32 be32; /* For access. */ }; static unsigned int minimask_get_prefix_len(const struct minimask *, const struct mf_field *); static void trie_init(struct classifier *cls, int trie_idx, const struct mf_field *); static unsigned int trie_lookup(const struct cls_trie *, const struct flow *, union trie_prefix *plens); static unsigned int trie_lookup_value(const rcu_trie_ptr *, const ovs_be32 value[], ovs_be32 plens[], unsigned int value_bits); static void trie_destroy(rcu_trie_ptr *); static void trie_insert(struct cls_trie *, const struct cls_rule *, int mlen); static void trie_insert_prefix(rcu_trie_ptr *, const ovs_be32 *prefix, int mlen); static void trie_remove(struct cls_trie *, const struct cls_rule *, int mlen); static void trie_remove_prefix(rcu_trie_ptr *, const ovs_be32 *prefix, int mlen); static void mask_set_prefix_bits(struct flow_wildcards *, uint8_t be32ofs, unsigned int n_bits); static bool mask_prefix_bits_set(const struct flow_wildcards *, uint8_t be32ofs, unsigned int n_bits); /* cls_rule. */ static inline void cls_rule_init__(struct cls_rule *rule, unsigned int priority) { rculist_init(&rule->node); *CONST_CAST(int *, &rule->priority) = priority; ovsrcu_init(&rule->cls_match, NULL); } /* Initializes 'rule' to match packets specified by 'match' at the given * 'priority'. 'match' must satisfy the invariant described in the comment at * the definition of struct match. * * The caller must eventually destroy 'rule' with cls_rule_destroy(). * * Clients should not use priority INT_MIN. (OpenFlow uses priorities between * 0 and UINT16_MAX, inclusive.) */ void cls_rule_init(struct cls_rule *rule, const struct match *match, int priority) { cls_rule_init__(rule, priority); minimatch_init(CONST_CAST(struct minimatch *, &rule->match), match); } /* Same as cls_rule_init() for initialization from a "struct minimatch". */ void cls_rule_init_from_minimatch(struct cls_rule *rule, const struct minimatch *match, int priority) { cls_rule_init__(rule, priority); minimatch_clone(CONST_CAST(struct minimatch *, &rule->match), match); } /* Initializes 'dst' as a copy of 'src'. * * The caller must eventually destroy 'dst' with cls_rule_destroy(). */ void cls_rule_clone(struct cls_rule *dst, const struct cls_rule *src) { cls_rule_init__(dst, src->priority); minimatch_clone(CONST_CAST(struct minimatch *, &dst->match), &src->match); } /* Initializes 'dst' with the data in 'src', destroying 'src'. * * 'src' must be a cls_rule NOT in a classifier. * * The caller must eventually destroy 'dst' with cls_rule_destroy(). */ void cls_rule_move(struct cls_rule *dst, struct cls_rule *src) { cls_rule_init__(dst, src->priority); minimatch_move(CONST_CAST(struct minimatch *, &dst->match), CONST_CAST(struct minimatch *, &src->match)); } /* Frees memory referenced by 'rule'. Doesn't free 'rule' itself (it's * normally embedded into a larger structure). * * ('rule' must not currently be in a classifier.) */ void cls_rule_destroy(struct cls_rule *rule) OVS_NO_THREAD_SAFETY_ANALYSIS { /* Must not be in a classifier. */ ovs_assert(!get_cls_match_protected(rule)); /* Check that the rule has been properly removed from the classifier. */ ovs_assert(rule->node.prev == RCULIST_POISON || rculist_is_empty(&rule->node)); rculist_poison__(&rule->node); /* Poisons also the next pointer. */ minimatch_destroy(CONST_CAST(struct minimatch *, &rule->match)); } /* This may only be called by the exclusive writer. */ void cls_rule_set_conjunctions(struct cls_rule *cr, const struct cls_conjunction *conj, size_t n) { struct cls_match *match = get_cls_match_protected(cr); struct cls_conjunction_set *old = ovsrcu_get_protected(struct cls_conjunction_set *, &match->conj_set); struct cls_conjunction *old_conj = old ? old->conj : NULL; unsigned int old_n = old ? old->n : 0; if (old_n != n || (n && memcmp(old_conj, conj, n * sizeof *conj))) { if (old) { ovsrcu_postpone(free, old); } ovsrcu_set(&match->conj_set, cls_conjunction_set_alloc(match, conj, n)); } } /* Returns true if 'a' and 'b' match the same packets at the same priority, * false if they differ in some way. */ bool cls_rule_equal(const struct cls_rule *a, const struct cls_rule *b) { return a->priority == b->priority && minimatch_equal(&a->match, &b->match); } /* Appends a string describing 'rule' to 's'. */ void cls_rule_format(const struct cls_rule *rule, struct ds *s) { minimatch_format(&rule->match, s, rule->priority); } /* Returns true if 'rule' matches every packet, false otherwise. */ bool cls_rule_is_catchall(const struct cls_rule *rule) { return minimask_is_catchall(rule->match.mask); } /* Makes 'rule' invisible in 'remove_version'. Once that version is used in * lookups, the caller should remove 'rule' via ovsrcu_postpone(). * * 'rule' must be in a classifier. * This may only be called by the exclusive writer. */ void cls_rule_make_invisible_in_version(const struct cls_rule *rule, cls_version_t remove_version) { struct cls_match *cls_match = get_cls_match_protected(rule); ovs_assert(remove_version >= cls_match->add_version); cls_match_set_remove_version(cls_match, remove_version); } /* This undoes the change made by cls_rule_make_invisible_in_version(). * * 'rule' must be in a classifier. * This may only be called by the exclusive writer. */ void cls_rule_restore_visibility(const struct cls_rule *rule) { cls_match_set_remove_version(get_cls_match_protected(rule), CLS_NOT_REMOVED_VERSION); } /* Return true if 'rule' is visible in 'version'. * * 'rule' must be in a classifier. */ bool cls_rule_visible_in_version(const struct cls_rule *rule, cls_version_t version) { struct cls_match *cls_match = get_cls_match(rule); return cls_match && cls_match_visible_in_version(cls_match, version); } /* Initializes 'cls' as a classifier that initially contains no classification * rules. */ void classifier_init(struct classifier *cls, const uint8_t *flow_segments) { cls->n_rules = 0; cmap_init(&cls->subtables_map); pvector_init(&cls->subtables); cls->n_flow_segments = 0; if (flow_segments) { while (cls->n_flow_segments < CLS_MAX_INDICES && *flow_segments < FLOW_U64S) { cls->flow_segments[cls->n_flow_segments++] = *flow_segments++; } } cls->n_tries = 0; for (int i = 0; i < CLS_MAX_TRIES; i++) { trie_init(cls, i, NULL); } cls->publish = true; } /* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the * caller's responsibility. * May only be called after all the readers have been terminated. */ void classifier_destroy(struct classifier *cls) { if (cls) { struct cls_subtable *subtable; int i; for (i = 0; i < cls->n_tries; i++) { trie_destroy(&cls->tries[i].root); } CMAP_FOR_EACH (subtable, cmap_node, &cls->subtables_map) { destroy_subtable(cls, subtable); } cmap_destroy(&cls->subtables_map); pvector_destroy(&cls->subtables); } } /* Set the fields for which prefix lookup should be performed. */ bool classifier_set_prefix_fields(struct classifier *cls, const enum mf_field_id *trie_fields, unsigned int n_fields) { const struct mf_field * new_fields[CLS_MAX_TRIES]; struct mf_bitmap fields = MF_BITMAP_INITIALIZER; int i, n_tries = 0; bool changed = false; for (i = 0; i < n_fields && n_tries < CLS_MAX_TRIES; i++) { const struct mf_field *field = mf_from_id(trie_fields[i]); if (field->flow_be32ofs < 0 || field->n_bits % 32) { /* Incompatible field. This is the only place where we * enforce these requirements, but the rest of the trie code * depends on the flow_be32ofs to be non-negative and the * field length to be a multiple of 32 bits. */ continue; } if (bitmap_is_set(fields.bm, trie_fields[i])) { /* Duplicate field, there is no need to build more than * one index for any one field. */ continue; } bitmap_set1(fields.bm, trie_fields[i]); new_fields[n_tries] = NULL; if (n_tries >= cls->n_tries || field != cls->tries[n_tries].field) { new_fields[n_tries] = field; changed = true; } n_tries++; } if (changed || n_tries < cls->n_tries) { struct cls_subtable *subtable; /* Trie configuration needs to change. Disable trie lookups * for the tries that are changing and wait all the current readers * with the old configuration to be done. */ changed = false; CMAP_FOR_EACH (subtable, cmap_node, &cls->subtables_map) { for (i = 0; i < cls->n_tries; i++) { if ((i < n_tries && new_fields[i]) || i >= n_tries) { if (subtable->trie_plen[i]) { subtable->trie_plen[i] = 0; changed = true; } } } } /* Synchronize if any readers were using tries. The readers may * temporarily function without the trie lookup based optimizations. */ if (changed) { /* ovsrcu_synchronize() functions as a memory barrier, so it does * not matter that subtable->trie_plen is not atomic. */ ovsrcu_synchronize(); } /* Now set up the tries. */ for (i = 0; i < n_tries; i++) { if (new_fields[i]) { trie_init(cls, i, new_fields[i]); } } /* Destroy the rest, if any. */ for (; i < cls->n_tries; i++) { trie_init(cls, i, NULL); } cls->n_tries = n_tries; return true; } return false; /* No change. */ } static void trie_init(struct classifier *cls, int trie_idx, const struct mf_field *field) { struct cls_trie *trie = &cls->tries[trie_idx]; struct cls_subtable *subtable; if (trie_idx < cls->n_tries) { trie_destroy(&trie->root); } else { ovsrcu_set_hidden(&trie->root, NULL); } trie->field = field; /* Add existing rules to the new trie. */ CMAP_FOR_EACH (subtable, cmap_node, &cls->subtables_map) { unsigned int plen; plen = field ? minimask_get_prefix_len(&subtable->mask, field) : 0; if (plen) { struct cls_match *head; CMAP_FOR_EACH (head, cmap_node, &subtable->rules) { trie_insert(trie, head->cls_rule, plen); } } /* Initialize subtable's prefix length on this field. This will * allow readers to use the trie. */ atomic_thread_fence(memory_order_release); subtable->trie_plen[trie_idx] = plen; } } /* Returns true if 'cls' contains no classification rules, false otherwise. * Checking the cmap requires no locking. */ bool classifier_is_empty(const struct classifier *cls) { return cmap_is_empty(&cls->subtables_map); } /* Returns the number of rules in 'cls'. */ int classifier_count(const struct classifier *cls) { /* n_rules is an int, so in the presence of concurrent writers this will * return either the old or a new value. */ return cls->n_rules; } static inline ovs_be32 minimatch_get_ports(const struct minimatch *match) { /* Could optimize to use the same map if needed for fast path. */ return MINIFLOW_GET_BE32(match->flow, tp_src) & MINIFLOW_GET_BE32(&match->mask->masks, tp_src); } static void subtable_replace_head_rule(struct classifier *cls OVS_UNUSED, struct cls_subtable *subtable, struct cls_match *head, struct cls_match *new, uint32_t hash, uint32_t ihash[CLS_MAX_INDICES]) { /* Rule's data is already in the tries. */ for (int i = 0; i < subtable->n_indices; i++) { cmap_replace(&subtable->indices[i], &head->index_nodes[i], &new->index_nodes[i], ihash[i]); } cmap_replace(&subtable->rules, &head->cmap_node, &new->cmap_node, hash); } /* Inserts 'rule' into 'cls' in 'version'. Until 'rule' is removed from 'cls', * the caller must not modify or free it. * * If 'cls' already contains an identical rule (including wildcards, values of * fixed fields, and priority) that is visible in 'version', replaces the old * rule by 'rule' and returns the rule that was replaced. The caller takes * ownership of the returned rule and is thus responsible for destroying it * with cls_rule_destroy(), after RCU grace period has passed (see * ovsrcu_postpone()). * * Returns NULL if 'cls' does not contain a rule with an identical key, after * inserting the new rule. In this case, no rules are displaced by the new * rule, even rules that cannot have any effect because the new rule matches a * superset of their flows and has higher priority. */ const struct cls_rule * classifier_replace(struct classifier *cls, const struct cls_rule *rule, cls_version_t version, const struct cls_conjunction *conjs, size_t n_conjs) { struct cls_match *new; struct cls_subtable *subtable; uint32_t ihash[CLS_MAX_INDICES]; struct cls_match *head; unsigned int mask_offset; size_t n_rules = 0; uint32_t basis; uint32_t hash; unsigned int i; /* 'new' is initially invisible to lookups. */ new = cls_match_alloc(rule, version, conjs, n_conjs); ovsrcu_set(&CONST_CAST(struct cls_rule *, rule)->cls_match, new); subtable = find_subtable(cls, rule->match.mask); if (!subtable) { subtable = insert_subtable(cls, rule->match.mask); } /* Compute hashes in segments. */ basis = 0; mask_offset = 0; for (i = 0; i < subtable->n_indices; i++) { ihash[i] = minimatch_hash_range(&rule->match, subtable->index_maps[i], &mask_offset, &basis); } hash = minimatch_hash_range(&rule->match, subtable->index_maps[i], &mask_offset, &basis); head = find_equal(subtable, rule->match.flow, hash); if (!head) { /* Add rule to tries. * * Concurrent readers might miss seeing the rule until this update, * which might require being fixed up by revalidation later. */ for (i = 0; i < cls->n_tries; i++) { if (subtable->trie_plen[i]) { trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]); } } /* Add rule to ports trie. */ if (subtable->ports_mask_len) { /* We mask the value to be inserted to always have the wildcarded * bits in known (zero) state, so we can include them in comparison * and they will always match (== their original value does not * matter). */ ovs_be32 masked_ports = minimatch_get_ports(&rule->match); trie_insert_prefix(&subtable->ports_trie, &masked_ports, subtable->ports_mask_len); } /* Add new node to segment indices. * * Readers may find the rule in the indices before the rule is visible * in the subtables 'rules' map. This may result in us losing the * opportunity to quit lookups earlier, resulting in sub-optimal * wildcarding. This will be fixed later by revalidation (always * scheduled after flow table changes). */ for (i = 0; i < subtable->n_indices; i++) { cmap_insert(&subtable->indices[i], &new->index_nodes[i], ihash[i]); } n_rules = cmap_insert(&subtable->rules, &new->cmap_node, hash); } else { /* Equal rules exist in the classifier already. */ struct cls_match *prev, *iter; /* Scan the list for the insertion point that will keep the list in * order of decreasing priority. Insert after rules marked invisible * in any version of the same priority. */ FOR_EACH_RULE_IN_LIST_PROTECTED (iter, prev, head) { if (rule->priority > iter->priority || (rule->priority == iter->priority && !cls_match_is_eventually_invisible(iter))) { break; } } /* Replace 'iter' with 'new' or insert 'new' between 'prev' and * 'iter'. */ if (iter) { struct cls_rule *old; if (rule->priority == iter->priority) { cls_match_replace(prev, iter, new); old = CONST_CAST(struct cls_rule *, iter->cls_rule); } else { cls_match_insert(prev, iter, new); old = NULL; } /* Replace the existing head in data structures, if rule is the new * head. */ if (iter == head) { subtable_replace_head_rule(cls, subtable, head, new, hash, ihash); } if (old) { struct cls_conjunction_set *conj_set; conj_set = ovsrcu_get_protected(struct cls_conjunction_set *, &iter->conj_set); if (conj_set) { ovsrcu_postpone(free, conj_set); } ovsrcu_set(&old->cls_match, NULL); /* Marks old rule as removed * from the classifier. */ ovsrcu_postpone(cls_match_free_cb, iter); /* No change in subtable's max priority or max count. */ /* Make 'new' visible to lookups in the appropriate version. */ cls_match_set_remove_version(new, CLS_NOT_REMOVED_VERSION); /* Make rule visible to iterators (immediately). */ rculist_replace(CONST_CAST(struct rculist *, &rule->node), &old->node); /* Return displaced rule. Caller is responsible for keeping it * around until all threads quiesce. */ return old; } } else { /* 'new' is new node after 'prev' */ cls_match_insert(prev, iter, new); } } /* Make 'new' visible to lookups in the appropriate version. */ cls_match_set_remove_version(new, CLS_NOT_REMOVED_VERSION); /* Make rule visible to iterators (immediately). */ rculist_push_back(&subtable->rules_list, CONST_CAST(struct rculist *, &rule->node)); /* Rule was added, not replaced. Update 'subtable's 'max_priority' and * 'max_count', if necessary. * * The rule was already inserted, but concurrent readers may not see the * rule yet as the subtables vector is not updated yet. This will have to * be fixed by revalidation later. */ if (n_rules == 1) { subtable->max_priority = rule->priority; subtable->max_count = 1; pvector_insert(&cls->subtables, subtable, rule->priority); } else if (rule->priority == subtable->max_priority) { ++subtable->max_count; } else if (rule->priority > subtable->max_priority) { subtable->max_priority = rule->priority; subtable->max_count = 1; pvector_change_priority(&cls->subtables, subtable, rule->priority); } /* Nothing was replaced. */ cls->n_rules++; if (cls->publish) { pvector_publish(&cls->subtables); } return NULL; } /* Inserts 'rule' into 'cls'. Until 'rule' is removed from 'cls', the caller * must not modify or free it. * * 'cls' must not contain an identical rule (including wildcards, values of * fixed fields, and priority). Use classifier_find_rule_exactly() to find * such a rule. */ void classifier_insert(struct classifier *cls, const struct cls_rule *rule, cls_version_t version, const struct cls_conjunction conj[], size_t n_conj) { const struct cls_rule *displaced_rule = classifier_replace(cls, rule, version, conj, n_conj); ovs_assert(!displaced_rule); } /* Removes 'rule' from 'cls'. It is the caller's responsibility to destroy * 'rule' with cls_rule_destroy(), freeing the memory block in which 'rule' * resides, etc., as necessary. * * Does nothing if 'rule' has been already removed, or was never inserted. * * Returns the removed rule, or NULL, if it was already removed. */ const struct cls_rule * classifier_remove(struct classifier *cls, const struct cls_rule *cls_rule) { struct cls_match *rule, *prev, *next, *head; struct cls_conjunction_set *conj_set; struct cls_subtable *subtable; uint32_t basis = 0, hash, ihash[CLS_MAX_INDICES]; unsigned int mask_offset; size_t n_rules; unsigned int i; rule = get_cls_match_protected(cls_rule); if (!rule) { return NULL; } /* Mark as removed. */ ovsrcu_set(&CONST_CAST(struct cls_rule *, cls_rule)->cls_match, NULL); /* Remove 'cls_rule' from the subtable's rules list. */ rculist_remove(CONST_CAST(struct rculist *, &cls_rule->node)); subtable = find_subtable(cls, cls_rule->match.mask); ovs_assert(subtable); mask_offset = 0; for (i = 0; i < subtable->n_indices; i++) { ihash[i] = minimatch_hash_range(&cls_rule->match, subtable->index_maps[i], &mask_offset, &basis); } hash = minimatch_hash_range(&cls_rule->match, subtable->index_maps[i], &mask_offset, &basis); head = find_equal(subtable, cls_rule->match.flow, hash); /* Check if the rule is not the head rule. */ if (rule != head) { struct cls_match *iter; /* Not the head rule, but potentially one with the same priority. */ /* Remove from the list of equal rules. */ FOR_EACH_RULE_IN_LIST_PROTECTED (iter, prev, head) { if (rule == iter) { break; } } ovs_assert(iter == rule); cls_match_remove(prev, rule); goto check_priority; } /* 'rule' is the head rule. Check if there is another rule to * replace 'rule' in the data structures. */ next = cls_match_next_protected(rule); if (next) { subtable_replace_head_rule(cls, subtable, rule, next, hash, ihash); goto check_priority; } /* 'rule' is last of the kind in the classifier, must remove from all the * data structures. */ if (subtable->ports_mask_len) { ovs_be32 masked_ports = minimatch_get_ports(&cls_rule->match); trie_remove_prefix(&subtable->ports_trie, &masked_ports, subtable->ports_mask_len); } for (i = 0; i < cls->n_tries; i++) { if (subtable->trie_plen[i]) { trie_remove(&cls->tries[i], cls_rule, subtable->trie_plen[i]); } } /* Remove rule node from indices. */ for (i = 0; i < subtable->n_indices; i++) { cmap_remove(&subtable->indices[i], &rule->index_nodes[i], ihash[i]); } n_rules = cmap_remove(&subtable->rules, &rule->cmap_node, hash); if (n_rules == 0) { destroy_subtable(cls, subtable); } else { check_priority: if (subtable->max_priority == rule->priority && --subtable->max_count == 0) { /* Find the new 'max_priority' and 'max_count'. */ int max_priority = INT_MIN; struct cls_match *head; CMAP_FOR_EACH (head, cmap_node, &subtable->rules) { if (head->priority > max_priority) { max_priority = head->priority; subtable->max_count = 1; } else if (head->priority == max_priority) { ++subtable->max_count; } } subtable->max_priority = max_priority; pvector_change_priority(&cls->subtables, subtable, max_priority); } } if (cls->publish) { pvector_publish(&cls->subtables); } /* free the rule. */ conj_set = ovsrcu_get_protected(struct cls_conjunction_set *, &rule->conj_set); if (conj_set) { ovsrcu_postpone(free, conj_set); } ovsrcu_postpone(cls_match_free_cb, rule); cls->n_rules--; return cls_rule; } /* Prefix tree context. Valid when 'lookup_done' is true. Can skip all * subtables which have a prefix match on the trie field, but whose prefix * length is not indicated in 'match_plens'. For example, a subtable that * has a 8-bit trie field prefix match can be skipped if * !be_get_bit_at(&match_plens, 8 - 1). If skipped, 'maskbits' prefix bits * must be unwildcarded to make datapath flow only match packets it should. */ struct trie_ctx { const struct cls_trie *trie; bool lookup_done; /* Status of the lookup. */ uint8_t be32ofs; /* U32 offset of the field in question. */ unsigned int maskbits; /* Prefix length needed to avoid false matches. */ union trie_prefix match_plens; /* Bitmask of prefix lengths with possible * matches. */ }; static void trie_ctx_init(struct trie_ctx *ctx, const struct cls_trie *trie) { ctx->trie = trie; ctx->be32ofs = trie->field->flow_be32ofs; ctx->lookup_done = false; } struct conjunctive_match { struct hmap_node hmap_node; uint32_t id; uint64_t clauses; }; static struct conjunctive_match * find_conjunctive_match__(struct hmap *matches, uint64_t id, uint32_t hash) { struct conjunctive_match *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash, matches) { if (m->id == id) { return m; } } return NULL; } static bool find_conjunctive_match(const struct cls_conjunction_set *set, unsigned int max_n_clauses, struct hmap *matches, struct conjunctive_match *cm_stubs, size_t n_cm_stubs, uint32_t *idp) { const struct cls_conjunction *c; if (max_n_clauses < set->min_n_clauses) { return false; } for (c = set->conj; c < &set->conj[set->n]; c++) { struct conjunctive_match *cm; uint32_t hash; if (c->n_clauses > max_n_clauses) { continue; } hash = hash_int(c->id, 0); cm = find_conjunctive_match__(matches, c->id, hash); if (!cm) { size_t n = hmap_count(matches); cm = n < n_cm_stubs ? &cm_stubs[n] : xmalloc(sizeof *cm); hmap_insert(matches, &cm->hmap_node, hash); cm->id = c->id; cm->clauses = UINT64_MAX << (c->n_clauses & 63); } cm->clauses |= UINT64_C(1) << c->clause; if (cm->clauses == UINT64_MAX) { *idp = cm->id; return true; } } return false; } static void free_conjunctive_matches(struct hmap *matches, struct conjunctive_match *cm_stubs, size_t n_cm_stubs) { if (hmap_count(matches) > n_cm_stubs) { struct conjunctive_match *cm, *next; HMAP_FOR_EACH_SAFE (cm, next, hmap_node, matches) { if (!(cm >= cm_stubs && cm < &cm_stubs[n_cm_stubs])) { free(cm); } } } hmap_destroy(matches); } /* Like classifier_lookup(), except that support for conjunctive matches can be * configured with 'allow_conjunctive_matches'. That feature is not exposed * externally because turning off conjunctive matches is only useful to avoid * recursion within this function itself. * * 'flow' is non-const to allow for temporary modifications during the lookup. * Any changes are restored before returning. */ static const struct cls_rule * classifier_lookup__(const struct classifier *cls, cls_version_t version, struct flow *flow, struct flow_wildcards *wc, bool allow_conjunctive_matches) { struct trie_ctx trie_ctx[CLS_MAX_TRIES]; const struct cls_match *match; /* Highest-priority flow in 'cls' that certainly matches 'flow'. */ const struct cls_match *hard = NULL; int hard_pri = INT_MIN; /* hard ? hard->priority : INT_MIN. */ /* Highest-priority conjunctive flows in 'cls' matching 'flow'. Since * these are (components of) conjunctive flows, we can only know whether * the full conjunctive flow matches after seeing multiple of them. Thus, * we refer to these as "soft matches". */ struct cls_conjunction_set *soft_stub[64]; struct cls_conjunction_set **soft = soft_stub; size_t n_soft = 0, allocated_soft = ARRAY_SIZE(soft_stub); int soft_pri = INT_MIN; /* n_soft ? MAX(soft[*]->priority) : INT_MIN. */ /* Synchronize for cls->n_tries and subtable->trie_plen. They can change * when table configuration changes, which happens typically only on * startup. */ atomic_thread_fence(memory_order_acquire); /* Initialize trie contexts for find_match_wc(). */ for (int i = 0; i < cls->n_tries; i++) { trie_ctx_init(&trie_ctx[i], &cls->tries[i]); } /* Main loop. */ struct cls_subtable *subtable; PVECTOR_FOR_EACH_PRIORITY (subtable, hard_pri, 2, sizeof *subtable, &cls->subtables) { struct cls_conjunction_set *conj_set; /* Skip subtables with no match, or where the match is lower-priority * than some certain match we've already found. */ match = find_match_wc(subtable, version, flow, trie_ctx, cls->n_tries, wc); if (!match || match->priority <= hard_pri) { continue; } conj_set = ovsrcu_get(struct cls_conjunction_set *, &match->conj_set); if (!conj_set) { /* 'match' isn't part of a conjunctive match. It's the best * certain match we've got so far, since we know that it's * higher-priority than hard_pri. * * (There might be a higher-priority conjunctive match. We can't * tell yet.) */ hard = match; hard_pri = hard->priority; } else if (allow_conjunctive_matches) { /* 'match' is part of a conjunctive match. Add it to the list. */ if (OVS_UNLIKELY(n_soft >= allocated_soft)) { struct cls_conjunction_set **old_soft = soft; allocated_soft *= 2; soft = xmalloc(allocated_soft * sizeof *soft); memcpy(soft, old_soft, n_soft * sizeof *soft); if (old_soft != soft_stub) { free(old_soft); } } soft[n_soft++] = conj_set; /* Keep track of the highest-priority soft match. */ if (soft_pri < match->priority) { soft_pri = match->priority; } } } /* In the common case, at this point we have no soft matches and we can * return immediately. (We do the same thing if we have potential soft * matches but none of them are higher-priority than our hard match.) */ if (hard_pri >= soft_pri) { if (soft != soft_stub) { free(soft); } return hard ? hard->cls_rule : NULL; } /* At this point, we have some soft matches. We might also have a hard * match; if so, its priority is lower than the highest-priority soft * match. */ /* Soft match loop. * * Check whether soft matches are real matches. */ for (;;) { /* Delete soft matches that are null. This only happens in second and * subsequent iterations of the soft match loop, when we drop back from * a high-priority soft match to a lower-priority one. * * Also, delete soft matches whose priority is less than or equal to * the hard match's priority. In the first iteration of the soft * match, these can be in 'soft' because the earlier main loop found * the soft match before the hard match. In second and later iteration * of the soft match loop, these can be in 'soft' because we dropped * back from a high-priority soft match to a lower-priority soft match. * * It is tempting to delete soft matches that cannot be satisfied * because there are fewer soft matches than required to satisfy any of * their conjunctions, but we cannot do that because there might be * lower priority soft or hard matches with otherwise identical * matches. (We could special case those here, but there's no * need--we'll do so at the bottom of the soft match loop anyway and * this duplicates less code.) * * It's also tempting to break out of the soft match loop if 'n_soft == * 1' but that would also miss lower-priority hard matches. We could * special case that also but again there's no need. */ for (int i = 0; i < n_soft; ) { if (!soft[i] || soft[i]->priority <= hard_pri) { soft[i] = soft[--n_soft]; } else { i++; } } if (!n_soft) { break; } /* Find the highest priority among the soft matches. (We know this * must be higher than the hard match's priority; otherwise we would * have deleted all of the soft matches in the previous loop.) Count * the number of soft matches that have that priority. */ soft_pri = INT_MIN; int n_soft_pri = 0; for (int i = 0; i < n_soft; i++) { if (soft[i]->priority > soft_pri) { soft_pri = soft[i]->priority; n_soft_pri = 1; } else if (soft[i]->priority == soft_pri) { n_soft_pri++; } } ovs_assert(soft_pri > hard_pri); /* Look for a real match among the highest-priority soft matches. * * It's unusual to have many conjunctive matches, so we use stubs to * avoid calling malloc() in the common case. An hmap has a built-in * stub for up to 2 hmap_nodes; possibly, we would benefit a variant * with a bigger stub. */ struct conjunctive_match cm_stubs[16]; struct hmap matches; hmap_init(&matches); for (int i = 0; i < n_soft; i++) { uint32_t id; if (soft[i]->priority == soft_pri && find_conjunctive_match(soft[i], n_soft_pri, &matches, cm_stubs, ARRAY_SIZE(cm_stubs), &id)) { uint32_t saved_conj_id = flow->conj_id; const struct cls_rule *rule; flow->conj_id = id; rule = classifier_lookup__(cls, version, flow, wc, false); flow->conj_id = saved_conj_id; if (rule) { free_conjunctive_matches(&matches, cm_stubs, ARRAY_SIZE(cm_stubs)); if (soft != soft_stub) { free(soft); } return rule; } } } free_conjunctive_matches(&matches, cm_stubs, ARRAY_SIZE(cm_stubs)); /* There's no real match among the highest-priority soft matches. * However, if any of those soft matches has a lower-priority but * otherwise identical flow match, then we need to consider those for * soft or hard matches. * * The next iteration of the soft match loop will delete any null * pointers we put into 'soft' (and some others too). */ for (int i = 0; i < n_soft; i++) { if (soft[i]->priority != soft_pri) { continue; } /* Find next-lower-priority flow with identical flow match. */ match = next_visible_rule_in_list(soft[i]->match, version); if (match) { soft[i] = ovsrcu_get(struct cls_conjunction_set *, &match->conj_set); if (!soft[i]) { /* The flow is a hard match; don't treat as a soft * match. */ if (match->priority > hard_pri) { hard = match; hard_pri = hard->priority; } } } else { /* No such lower-priority flow (probably the common case). */ soft[i] = NULL; } } } if (soft != soft_stub) { free(soft); } return hard ? hard->cls_rule : NULL; } /* Finds and returns the highest-priority rule in 'cls' that matches 'flow' and * that is visible in 'version'. Returns a null pointer if no rules in 'cls' * match 'flow'. If multiple rules of equal priority match 'flow', returns one * arbitrarily. * * If a rule is found and 'wc' is non-null, bitwise-OR's 'wc' with the * set of bits that were significant in the lookup. At some point * earlier, 'wc' should have been initialized (e.g., by * flow_wildcards_init_catchall()). * * 'flow' is non-const to allow for temporary modifications during the lookup. * Any changes are restored before returning. */ const struct cls_rule * classifier_lookup(const struct classifier *cls, cls_version_t version, struct flow *flow, struct flow_wildcards *wc) { return classifier_lookup__(cls, version, flow, wc, true); } /* Finds and returns a rule in 'cls' with exactly the same priority and * matching criteria as 'target', and that is visible in 'version'. * Only one such rule may ever exist. Returns a null pointer if 'cls' doesn't * contain an exact match. */ const struct cls_rule * classifier_find_rule_exactly(const struct classifier *cls, const struct cls_rule *target, cls_version_t version) { const struct cls_match *head, *rule; const struct cls_subtable *subtable; subtable = find_subtable(cls, target->match.mask); if (!subtable) { return NULL; } head = find_equal(subtable, target->match.flow, miniflow_hash_in_minimask(target->match.flow, target->match.mask, 0)); if (!head) { return NULL; } CLS_MATCH_FOR_EACH (rule, head) { if (rule->priority < target->priority) { break; /* Not found. */ } if (rule->priority == target->priority && cls_match_visible_in_version(rule, version)) { return rule->cls_rule; } } return NULL; } /* Finds and returns a rule in 'cls' with priority 'priority' and exactly the * same matching criteria as 'target', and that is visible in 'version'. * Returns a null pointer if 'cls' doesn't contain an exact match visible in * 'version'. */ const struct cls_rule * classifier_find_match_exactly(const struct classifier *cls, const struct match *target, int priority, cls_version_t version) { const struct cls_rule *retval; struct cls_rule cr; cls_rule_init(&cr, target, priority); retval = classifier_find_rule_exactly(cls, &cr, version); cls_rule_destroy(&cr); return retval; } /* Checks if 'target' would overlap any other rule in 'cls' in 'version'. Two * rules are considered to overlap if both rules have the same priority and a * packet could match both, and if both rules are visible in the same version. * * A trivial example of overlapping rules is two rules matching disjoint sets * of fields. E.g., if one rule matches only on port number, while another only * on dl_type, any packet from that specific port and with that specific * dl_type could match both, if the rules also have the same priority. */ bool classifier_rule_overlaps(const struct classifier *cls, const struct cls_rule *target, cls_version_t version) { struct cls_subtable *subtable; /* Iterate subtables in the descending max priority order. */ PVECTOR_FOR_EACH_PRIORITY (subtable, target->priority - 1, 2, sizeof(struct cls_subtable), &cls->subtables) { struct { struct minimask mask; uint64_t storage[FLOW_U64S]; } m; const struct cls_rule *rule; minimask_combine(&m.mask, target->match.mask, &subtable->mask, m.storage); RCULIST_FOR_EACH (rule, node, &subtable->rules_list) { if (rule->priority == target->priority && miniflow_equal_in_minimask(target->match.flow, rule->match.flow, &m.mask) && cls_rule_visible_in_version(rule, version)) { return true; } } } return false; } /* Returns true if 'rule' exactly matches 'criteria' or if 'rule' is more * specific than 'criteria'. That is, 'rule' matches 'criteria' and this * function returns true if, for every field: * * - 'criteria' and 'rule' specify the same (non-wildcarded) value for the * field, or * * - 'criteria' wildcards the field, * * Conversely, 'rule' does not match 'criteria' and this function returns false * if, for at least one field: * * - 'criteria' and 'rule' specify different values for the field, or * * - 'criteria' specifies a value for the field but 'rule' wildcards it. * * Equivalently, the truth table for whether a field matches is: * * rule * * c wildcard exact * r +---------+---------+ * i wild | yes | yes | * t card | | | * e +---------+---------+ * r exact | no |if values| * i | |are equal| * a +---------+---------+ * * This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD * commands and by OpenFlow 1.0 aggregate and flow stats. * * Ignores rule->priority. */ bool cls_rule_is_loose_match(const struct cls_rule *rule, const struct minimatch *criteria) { return (!minimask_has_extra(rule->match.mask, criteria->mask) && miniflow_equal_in_minimask(rule->match.flow, criteria->flow, criteria->mask)); } /* Iteration. */ static bool rule_matches(const struct cls_rule *rule, const struct cls_rule *target, cls_version_t version) { /* Rule may only match a target if it is visible in target's version. */ return cls_rule_visible_in_version(rule, version) && (!target || miniflow_equal_in_minimask(rule->match.flow, target->match.flow, target->match.mask)); } static const struct cls_rule * search_subtable(const struct cls_subtable *subtable, struct cls_cursor *cursor) { if (!cursor->target || !minimask_has_extra(&subtable->mask, cursor->target->match.mask)) { const struct cls_rule *rule; RCULIST_FOR_EACH (rule, node, &subtable->rules_list) { if (rule_matches(rule, cursor->target, cursor->version)) { return rule; } } } return NULL; } /* Initializes 'cursor' for iterating through rules in 'cls', and returns the * cursor. * * - If 'target' is null, or if the 'target' is a catchall target, the * cursor will visit every rule in 'cls' that is visible in 'version'. * * - If 'target' is nonnull, the cursor will visit each 'rule' in 'cls' * such that cls_rule_is_loose_match(rule, target) returns true and that * the rule is visible in 'version'. * * Ignores target->priority. */ struct cls_cursor cls_cursor_start(const struct classifier *cls, const struct cls_rule *target, cls_version_t version) { struct cls_cursor cursor; struct cls_subtable *subtable; cursor.cls = cls; cursor.target = target && !cls_rule_is_catchall(target) ? target : NULL; cursor.version = version; cursor.rule = NULL; /* Find first rule. */ PVECTOR_CURSOR_FOR_EACH (subtable, &cursor.subtables, &cursor.cls->subtables) { const struct cls_rule *rule = search_subtable(subtable, &cursor); if (rule) { cursor.subtable = subtable; cursor.rule = rule; break; } } return cursor; } static const struct cls_rule * cls_cursor_next(struct cls_cursor *cursor) { const struct cls_rule *rule; const struct cls_subtable *subtable; rule = cursor->rule; subtable = cursor->subtable; RCULIST_FOR_EACH_CONTINUE (rule, node, &subtable->rules_list) { if (rule_matches(rule, cursor->target, cursor->version)) { return rule; } } PVECTOR_CURSOR_FOR_EACH_CONTINUE (subtable, &cursor->subtables) { rule = search_subtable(subtable, cursor); if (rule) { cursor->subtable = subtable; return rule; } } return NULL; } /* Sets 'cursor->rule' to the next matching cls_rule in 'cursor''s iteration, * or to null if all matching rules have been visited. */ void cls_cursor_advance(struct cls_cursor *cursor) { cursor->rule = cls_cursor_next(cursor); } static struct cls_subtable * find_subtable(const struct classifier *cls, const struct minimask *mask) { struct cls_subtable *subtable; CMAP_FOR_EACH_WITH_HASH (subtable, cmap_node, minimask_hash(mask, 0), &cls->subtables_map) { if (minimask_equal(mask, &subtable->mask)) { return subtable; } } return NULL; } /* Initializes 'map' with a subset of 'miniflow''s maps that includes only the * portions with u64-offset 'i' such that 'start' <= i < 'end'. Does not copy * any data from 'miniflow' to 'map'. */ static struct flowmap miniflow_get_map_in_range(const struct miniflow *miniflow, uint8_t start, uint8_t end) { struct flowmap map; size_t ofs = 0; map = miniflow->map; /* Clear the bits before 'start'. */ while (start >= MAP_T_BITS) { start -= MAP_T_BITS; ofs += MAP_T_BITS; map.bits[start / MAP_T_BITS] = 0; } if (start > 0) { flowmap_clear(&map, ofs, start); } /* Clear the bits starting at 'end'. */ if (end < FLOW_U64S) { /* flowmap_clear() can handle at most MAP_T_BITS at a time. */ ovs_assert(FLOW_U64S - end <= MAP_T_BITS); flowmap_clear(&map, end, FLOW_U64S - end); } return map; } /* The new subtable will be visible to the readers only after this. */ static struct cls_subtable * insert_subtable(struct classifier *cls, const struct minimask *mask) { uint32_t hash = minimask_hash(mask, 0); struct cls_subtable *subtable; int i, index = 0; struct flowmap stage_map; uint8_t prev; size_t count = miniflow_n_values(&mask->masks); subtable = xzalloc(sizeof *subtable + MINIFLOW_VALUES_SIZE(count)); cmap_init(&subtable->rules); miniflow_clone(CONST_CAST(struct miniflow *, &subtable->mask.masks), &mask->masks, count); /* Init indices for segmented lookup, if any. */ prev = 0; for (i = 0; i < cls->n_flow_segments; i++) { stage_map = miniflow_get_map_in_range(&mask->masks, prev, cls->flow_segments[i]); /* Add an index if it adds mask bits. */ if (!flowmap_is_empty(stage_map)) { cmap_init(&subtable->indices[index]); *CONST_CAST(struct flowmap *, &subtable->index_maps[index]) = stage_map; index++; } prev = cls->flow_segments[i]; } /* Map for the final stage. */ *CONST_CAST(struct flowmap *, &subtable->index_maps[index]) = miniflow_get_map_in_range(&mask->masks, prev, FLOW_U64S); /* Check if the final stage adds any bits, * and remove the last index if it doesn't. */ if (index > 0) { if (flowmap_equal(subtable->index_maps[index], subtable->index_maps[index - 1])) { --index; cmap_destroy(&subtable->indices[index]); } } *CONST_CAST(uint8_t *, &subtable->n_indices) = index; for (i = 0; i < cls->n_tries; i++) { subtable->trie_plen[i] = minimask_get_prefix_len(mask, cls->tries[i].field); } /* Ports trie. */ ovsrcu_set_hidden(&subtable->ports_trie, NULL); *CONST_CAST(int *, &subtable->ports_mask_len) = 32 - ctz32(ntohl(MINIFLOW_GET_BE32(&mask->masks, tp_src))); /* List of rules. */ rculist_init(&subtable->rules_list); cmap_insert(&cls->subtables_map, &subtable->cmap_node, hash); return subtable; } /* RCU readers may still access the subtable before it is actually freed. */ static void destroy_subtable(struct classifier *cls, struct cls_subtable *subtable) { int i; pvector_remove(&cls->subtables, subtable); cmap_remove(&cls->subtables_map, &subtable->cmap_node, minimask_hash(&subtable->mask, 0)); ovs_assert(ovsrcu_get_protected(struct trie_node *, &subtable->ports_trie) == NULL); ovs_assert(cmap_is_empty(&subtable->rules)); ovs_assert(rculist_is_empty(&subtable->rules_list)); for (i = 0; i < subtable->n_indices; i++) { cmap_destroy(&subtable->indices[i]); } cmap_destroy(&subtable->rules); ovsrcu_postpone(free, subtable); } static unsigned int be_get_bit_at(const ovs_be32 value[], unsigned int ofs); /* Return 'true' if can skip rest of the subtable based on the prefix trie * lookup results. */ static inline bool check_tries(struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries, const unsigned int field_plen[CLS_MAX_TRIES], const struct flowmap range_map, const struct flow *flow, struct flow_wildcards *wc) { int j; /* Check if we could avoid fully unwildcarding the next level of * fields using the prefix tries. The trie checks are done only as * needed to avoid folding in additional bits to the wildcards mask. */ for (j = 0; j < n_tries; j++) { /* Is the trie field relevant for this subtable, and is the trie field within the current range of fields? */ if (field_plen[j] && flowmap_is_set(&range_map, trie_ctx[j].be32ofs / 2)) { struct trie_ctx *ctx = &trie_ctx[j]; /* On-demand trie lookup. */ if (!ctx->lookup_done) { memset(&ctx->match_plens, 0, sizeof ctx->match_plens); ctx->maskbits = trie_lookup(ctx->trie, flow, &ctx->match_plens); ctx->lookup_done = true; } /* Possible to skip the rest of the subtable if subtable's * prefix on the field is not included in the lookup result. */ if (!be_get_bit_at(&ctx->match_plens.be32, field_plen[j] - 1)) { /* We want the trie lookup to never result in unwildcarding * any bits that would not be unwildcarded otherwise. * Since the trie is shared by the whole classifier, it is * possible that the 'maskbits' contain bits that are * irrelevant for the partition relevant for the current * packet. Hence the checks below. */ /* Check that the trie result will not unwildcard more bits * than this subtable would otherwise. */ if (ctx->maskbits <= field_plen[j]) { /* Unwildcard the bits and skip the rest. */ mask_set_prefix_bits(wc, ctx->be32ofs, ctx->maskbits); /* Note: Prerequisite already unwildcarded, as the only * prerequisite of the supported trie lookup fields is * the ethertype, which is always unwildcarded. */ return true; } /* Can skip if the field is already unwildcarded. */ if (mask_prefix_bits_set(wc, ctx->be32ofs, ctx->maskbits)) { return true; } } } } return false; } /* Returns true if 'target' satisifies 'flow'/'mask', that is, if each bit * for which 'flow', for which 'mask' has a bit set, specifies a particular * value has the correct value in 'target'. * * This function is equivalent to miniflow_equal_flow_in_minimask(flow, * target, mask) but this is faster because of the invariant that * flow->map and mask->masks.map are the same, and that this version * takes the 'wc'. */ static inline bool miniflow_and_mask_matches_flow(const struct miniflow *flow, const struct minimask *mask, const struct flow *target) { const uint64_t *flowp = miniflow_get_values(flow); const uint64_t *maskp = miniflow_get_values(&mask->masks); const uint64_t *target_u64 = (const uint64_t *)target; map_t map; FLOWMAP_FOR_EACH_MAP (map, mask->masks.map) { size_t idx; MAP_FOR_EACH_INDEX (idx, map) { if ((*flowp++ ^ target_u64[idx]) & *maskp++) { return false; } } target_u64 += MAP_T_BITS; } return true; } static inline const struct cls_match * find_match(const struct cls_subtable *subtable, cls_version_t version, const struct flow *flow, uint32_t hash) { const struct cls_match *head, *rule; CMAP_FOR_EACH_WITH_HASH (head, cmap_node, hash, &subtable->rules) { if (OVS_LIKELY(miniflow_and_mask_matches_flow(&head->flow, &subtable->mask, flow))) { /* Return highest priority rule that is visible. */ CLS_MATCH_FOR_EACH (rule, head) { if (OVS_LIKELY(cls_match_visible_in_version(rule, version))) { return rule; } } } } return NULL; } /* Returns true if 'target' satisifies 'flow'/'mask', that is, if each bit * for which 'flow', for which 'mask' has a bit set, specifies a particular * value has the correct value in 'target'. * * This function is equivalent to miniflow_and_mask_matches_flow() but this * version fills in the mask bits in 'wc'. */ static inline bool miniflow_and_mask_matches_flow_wc(const struct miniflow *flow, const struct minimask *mask, const struct flow *target, struct flow_wildcards *wc) { const uint64_t *flowp = miniflow_get_values(flow); const uint64_t *maskp = miniflow_get_values(&mask->masks); const uint64_t *target_u64 = (const uint64_t *)target; uint64_t *wc_u64 = (uint64_t *)&wc->masks; uint64_t diff; size_t idx; map_t map; FLOWMAP_FOR_EACH_MAP (map, mask->masks.map) { MAP_FOR_EACH_INDEX(idx, map) { uint64_t msk = *maskp++; diff = (*flowp++ ^ target_u64[idx]) & msk; if (diff) { goto out; } /* Fill in the bits that were looked at. */ wc_u64[idx] |= msk; } target_u64 += MAP_T_BITS; wc_u64 += MAP_T_BITS; } return true; out: /* Only unwildcard if none of the differing bits is already * exact-matched. */ if (!(wc_u64[idx] & diff)) { /* Keep one bit of the difference. The selected bit may be * different in big-endian v.s. little-endian systems. */ wc_u64[idx] |= rightmost_1bit(diff); } return false; } static const struct cls_match * find_match_wc(const struct cls_subtable *subtable, cls_version_t version, const struct flow *flow, struct trie_ctx trie_ctx[CLS_MAX_TRIES], unsigned int n_tries, struct flow_wildcards *wc) { if (OVS_UNLIKELY(!wc)) { return find_match(subtable, version, flow, flow_hash_in_minimask(flow, &subtable->mask, 0)); } uint32_t basis = 0, hash; const struct cls_match *rule = NULL; struct flowmap stages_map = FLOWMAP_EMPTY_INITIALIZER; unsigned int mask_offset = 0; int i; /* Try to finish early by checking fields in segments. */ for (i = 0; i < subtable->n_indices; i++) { const struct cmap_node *inode; if (check_tries(trie_ctx, n_tries, subtable->trie_plen, subtable->index_maps[i], flow, wc)) { /* 'wc' bits for the trie field set, now unwildcard the preceding * bits used so far. */ goto no_match; } /* Accumulate the map used so far. */ stages_map = flowmap_or(stages_map, subtable->index_maps[i]); hash = flow_hash_in_minimask_range(flow, &subtable->mask, subtable->index_maps[i], &mask_offset, &basis); inode = cmap_find(&subtable->indices[i], hash); if (!inode) { goto no_match; } /* If we have narrowed down to a single rule already, check whether * that rule matches. Either way, we're done. * * (Rare) hash collisions may cause us to miss the opportunity for this * optimization. */ if (!cmap_node_next(inode)) { const struct cls_match *head; ASSIGN_CONTAINER(head, inode - i, index_nodes); if (miniflow_and_mask_matches_flow_wc(&head->flow, &subtable->mask, flow, wc)) { /* Return highest priority rule that is visible. */ CLS_MATCH_FOR_EACH (rule, head) { if (OVS_LIKELY(cls_match_visible_in_version(rule, version))) { return rule; } } } return NULL; } } /* Trie check for the final range. */ if (check_tries(trie_ctx, n_tries, subtable->trie_plen, subtable->index_maps[i], flow, wc)) { goto no_match; } hash = flow_hash_in_minimask_range(flow, &subtable->mask, subtable->index_maps[i], &mask_offset, &basis); rule = find_match(subtable, version, flow, hash); if (!rule && subtable->ports_mask_len) { /* The final stage had ports, but there was no match. Instead of * unwildcarding all the ports bits, use the ports trie to figure out a * smaller set of bits to unwildcard. */ unsigned int mbits; ovs_be32 value, plens, mask; mask = MINIFLOW_GET_BE32(&subtable->mask.masks, tp_src); value = ((OVS_FORCE ovs_be32 *)flow)[TP_PORTS_OFS32] & mask; mbits = trie_lookup_value(&subtable->ports_trie, &value, &plens, 32); ((OVS_FORCE ovs_be32 *)&wc->masks)[TP_PORTS_OFS32] |= mask & be32_prefix_mask(mbits); goto no_match; } /* Must unwildcard all the fields, as they were looked at. */ flow_wildcards_fold_minimask(wc, &subtable->mask); return rule; no_match: /* Unwildcard the bits in stages so far, as they were used in determining * there is no match. */ flow_wildcards_fold_minimask_in_map(wc, &subtable->mask, stages_map); return NULL; } static struct cls_match * find_equal(const struct cls_subtable *subtable, const struct miniflow *flow, uint32_t hash) { struct cls_match *head; CMAP_FOR_EACH_WITH_HASH (head, cmap_node, hash, &subtable->rules) { if (miniflow_equal(&head->flow, flow)) { return head; } } return NULL; } /* A longest-prefix match tree. */ /* Return at least 'plen' bits of the 'prefix', starting at bit offset 'ofs'. * Prefixes are in the network byte order, and the offset 0 corresponds to * the most significant bit of the first byte. The offset can be read as * "how many bits to skip from the start of the prefix starting at 'pr'". */ static uint32_t raw_get_prefix(const ovs_be32 pr[], unsigned int ofs, unsigned int plen) { uint32_t prefix; pr += ofs / 32; /* Where to start. */ ofs %= 32; /* How many bits to skip at 'pr'. */ prefix = ntohl(*pr) << ofs; /* Get the first 32 - ofs bits. */ if (plen > 32 - ofs) { /* Need more than we have already? */ prefix |= ntohl(*++pr) >> (32 - ofs); } /* Return with possible unwanted bits at the end. */ return prefix; } /* Return min(TRIE_PREFIX_BITS, plen) bits of the 'prefix', starting at bit * offset 'ofs'. Prefixes are in the network byte order, and the offset 0 * corresponds to the most significant bit of the first byte. The offset can * be read as "how many bits to skip from the start of the prefix starting at * 'pr'". */ static uint32_t trie_get_prefix(const ovs_be32 pr[], unsigned int ofs, unsigned int plen) { if (!plen) { return 0; } if (plen > TRIE_PREFIX_BITS) { plen = TRIE_PREFIX_BITS; /* Get at most TRIE_PREFIX_BITS. */ } /* Return with unwanted bits cleared. */ return raw_get_prefix(pr, ofs, plen) & ~0u << (32 - plen); } /* Return the number of equal bits in 'n_bits' of 'prefix's MSBs and a 'value' * starting at "MSB 0"-based offset 'ofs'. */ static unsigned int prefix_equal_bits(uint32_t prefix, unsigned int n_bits, const ovs_be32 value[], unsigned int ofs) { uint64_t diff = prefix ^ raw_get_prefix(value, ofs, n_bits); /* Set the bit after the relevant bits to limit the result. */ return raw_clz64(diff << 32 | UINT64_C(1) << (63 - n_bits)); } /* Return the number of equal bits in 'node' prefix and a 'prefix' of length * 'plen', starting at "MSB 0"-based offset 'ofs'. */ static unsigned int trie_prefix_equal_bits(const struct trie_node *node, const ovs_be32 prefix[], unsigned int ofs, unsigned int plen) { return prefix_equal_bits(node->prefix, MIN(node->n_bits, plen - ofs), prefix, ofs); } /* Return the bit at ("MSB 0"-based) offset 'ofs' as an int. 'ofs' can * be greater than 31. */ static unsigned int be_get_bit_at(const ovs_be32 value[], unsigned int ofs) { return (((const uint8_t *)value)[ofs / 8] >> (7 - ofs % 8)) & 1u; } /* Return the bit at ("MSB 0"-based) offset 'ofs' as an int. 'ofs' must * be between 0 and 31, inclusive. */ static unsigned int get_bit_at(const uint32_t prefix, unsigned int ofs) { return (prefix >> (31 - ofs)) & 1u; } /* Create new branch. */ static struct trie_node * trie_branch_create(const ovs_be32 *prefix, unsigned int ofs, unsigned int plen, unsigned int n_rules) { struct trie_node *node = xmalloc(sizeof *node); node->prefix = trie_get_prefix(prefix, ofs, plen); if (plen <= TRIE_PREFIX_BITS) { node->n_bits = plen; ovsrcu_set_hidden(&node->edges[0], NULL); ovsrcu_set_hidden(&node->edges[1], NULL); node->n_rules = n_rules; } else { /* Need intermediate nodes. */ struct trie_node *subnode = trie_branch_create(prefix, ofs + TRIE_PREFIX_BITS, plen - TRIE_PREFIX_BITS, n_rules); int bit = get_bit_at(subnode->prefix, 0); node->n_bits = TRIE_PREFIX_BITS; ovsrcu_set_hidden(&node->edges[bit], subnode); ovsrcu_set_hidden(&node->edges[!bit], NULL); node->n_rules = 0; } return node; } static void trie_node_destroy(const struct trie_node *node) { ovsrcu_postpone(free, CONST_CAST(struct trie_node *, node)); } /* Copy a trie node for modification and postpone delete the old one. */ static struct trie_node * trie_node_rcu_realloc(const struct trie_node *node) { struct trie_node *new_node = xmalloc(sizeof *node); *new_node = *node; trie_node_destroy(node); return new_node; } static void trie_destroy(rcu_trie_ptr *trie) { struct trie_node *node = ovsrcu_get_protected(struct trie_node *, trie); if (node) { ovsrcu_set_hidden(trie, NULL); trie_destroy(&node->edges[0]); trie_destroy(&node->edges[1]); trie_node_destroy(node); } } static bool trie_is_leaf(const struct trie_node *trie) { /* No children? */ return !ovsrcu_get(struct trie_node *, &trie->edges[0]) && !ovsrcu_get(struct trie_node *, &trie->edges[1]); } static void mask_set_prefix_bits(struct flow_wildcards *wc, uint8_t be32ofs, unsigned int n_bits) { ovs_be32 *mask = &((ovs_be32 *)&wc->masks)[be32ofs]; unsigned int i; for (i = 0; i < n_bits / 32; i++) { mask[i] = OVS_BE32_MAX; } if (n_bits % 32) { mask[i] |= htonl(~0u << (32 - n_bits % 32)); } } static bool mask_prefix_bits_set(const struct flow_wildcards *wc, uint8_t be32ofs, unsigned int n_bits) { ovs_be32 *mask = &((ovs_be32 *)&wc->masks)[be32ofs]; unsigned int i; ovs_be32 zeroes = 0; for (i = 0; i < n_bits / 32; i++) { zeroes |= ~mask[i]; } if (n_bits % 32) { zeroes |= ~mask[i] & htonl(~0u << (32 - n_bits % 32)); } return !zeroes; /* All 'n_bits' bits set. */ } static rcu_trie_ptr * trie_next_edge(struct trie_node *node, const ovs_be32 value[], unsigned int ofs) { return node->edges + be_get_bit_at(value, ofs); } static const struct trie_node * trie_next_node(const struct trie_node *node, const ovs_be32 value[], unsigned int ofs) { return ovsrcu_get(struct trie_node *, &node->edges[be_get_bit_at(value, ofs)]); } /* Set the bit at ("MSB 0"-based) offset 'ofs'. 'ofs' can be greater than 31. */ static void be_set_bit_at(ovs_be32 value[], unsigned int ofs) { ((uint8_t *)value)[ofs / 8] |= 1u << (7 - ofs % 8); } /* Returns the number of bits in the prefix mask necessary to determine a * mismatch, in case there are longer prefixes in the tree below the one that * matched. * '*plens' will have a bit set for each prefix length that may have matching * rules. The caller is responsible for clearing the '*plens' prior to * calling this. */ static unsigned int trie_lookup_value(const rcu_trie_ptr *trie, const ovs_be32 value[], ovs_be32 plens[], unsigned int n_bits) { const struct trie_node *prev = NULL; const struct trie_node *node = ovsrcu_get(struct trie_node *, trie); unsigned int match_len = 0; /* Number of matching bits. */ for (; node; prev = node, node = trie_next_node(node, value, match_len)) { unsigned int eqbits; /* Check if this edge can be followed. */ eqbits = prefix_equal_bits(node->prefix, node->n_bits, value, match_len); match_len += eqbits; if (eqbits < node->n_bits) { /* Mismatch, nothing more to be found. */ /* Bit at offset 'match_len' differed. */ return match_len + 1; /* Includes the first mismatching bit. */ } /* Full match, check if rules exist at this prefix length. */ if (node->n_rules > 0) { be_set_bit_at(plens, match_len - 1); } if (match_len >= n_bits) { return n_bits; /* Full prefix. */ } } /* node == NULL. Full match so far, but we tried to follow an * non-existing branch. Need to exclude the other branch if it exists * (it does not if we were called on an empty trie or 'prev' is a leaf * node). */ return !prev || trie_is_leaf(prev) ? match_len : match_len + 1; } static unsigned int trie_lookup(const struct cls_trie *trie, const struct flow *flow, union trie_prefix *plens) { const struct mf_field *mf = trie->field; /* Check that current flow matches the prerequisites for the trie * field. Some match fields are used for multiple purposes, so we * must check that the trie is relevant for this flow. */ if (mf_are_prereqs_ok(mf, flow)) { return trie_lookup_value(&trie->root, &((ovs_be32 *)flow)[mf->flow_be32ofs], &plens->be32, mf->n_bits); } memset(plens, 0xff, sizeof *plens); /* All prefixes, no skipping. */ return 0; /* Value not used in this case. */ } /* Returns the length of a prefix match mask for the field 'mf' in 'minimask'. * Returns the u32 offset to the miniflow data in '*miniflow_index', if * 'miniflow_index' is not NULL. */ static unsigned int minimask_get_prefix_len(const struct minimask *minimask, const struct mf_field *mf) { unsigned int n_bits = 0, mask_tz = 0; /* Non-zero when end of mask seen. */ uint8_t be32_ofs = mf->flow_be32ofs; uint8_t be32_end = be32_ofs + mf->n_bytes / 4; for (; be32_ofs < be32_end; ++be32_ofs) { uint32_t mask = ntohl(minimask_get_be32(minimask, be32_ofs)); /* Validate mask, count the mask length. */ if (mask_tz) { if (mask) { return 0; /* No bits allowed after mask ended. */ } } else { if (~mask & (~mask + 1)) { return 0; /* Mask not contiguous. */ } mask_tz = ctz32(mask); n_bits += 32 - mask_tz; } } return n_bits; } /* * This is called only when mask prefix is known to be CIDR and non-zero. * Relies on the fact that the flow and mask have the same map, and since * the mask is CIDR, the storage for the flow field exists even if it * happened to be zeros. */ static const ovs_be32 * minimatch_get_prefix(const struct minimatch *match, const struct mf_field *mf) { size_t u64_ofs = mf->flow_be32ofs / 2; return (OVS_FORCE const ovs_be32 *)miniflow_get__(match->flow, u64_ofs) + (mf->flow_be32ofs & 1); } /* Insert rule in to the prefix tree. * 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask * in 'rule'. */ static void trie_insert(struct cls_trie *trie, const struct cls_rule *rule, int mlen) { trie_insert_prefix(&trie->root, minimatch_get_prefix(&rule->match, trie->field), mlen); } static void trie_insert_prefix(rcu_trie_ptr *edge, const ovs_be32 *prefix, int mlen) { struct trie_node *node; int ofs = 0; /* Walk the tree. */ for (; (node = ovsrcu_get_protected(struct trie_node *, edge)); edge = trie_next_edge(node, prefix, ofs)) { unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen); ofs += eqbits; if (eqbits < node->n_bits) { /* Mismatch, new node needs to be inserted above. */ int old_branch = get_bit_at(node->prefix, eqbits); struct trie_node *new_parent; new_parent = trie_branch_create(prefix, ofs - eqbits, eqbits, ofs == mlen ? 1 : 0); /* Copy the node to modify it. */ node = trie_node_rcu_realloc(node); /* Adjust the new node for its new position in the tree. */ node->prefix <<= eqbits; node->n_bits -= eqbits; ovsrcu_set_hidden(&new_parent->edges[old_branch], node); /* Check if need a new branch for the new rule. */ if (ofs < mlen) { ovsrcu_set_hidden(&new_parent->edges[!old_branch], trie_branch_create(prefix, ofs, mlen - ofs, 1)); } ovsrcu_set(edge, new_parent); /* Publish changes. */ return; } /* Full match so far. */ if (ofs == mlen) { /* Full match at the current node, rule needs to be added here. */ node->n_rules++; return; } } /* Must insert a new tree branch for the new rule. */ ovsrcu_set(edge, trie_branch_create(prefix, ofs, mlen - ofs, 1)); } /* 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask * in 'rule'. */ static void trie_remove(struct cls_trie *trie, const struct cls_rule *rule, int mlen) { trie_remove_prefix(&trie->root, minimatch_get_prefix(&rule->match, trie->field), mlen); } /* 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask * in 'rule'. */ static void trie_remove_prefix(rcu_trie_ptr *root, const ovs_be32 *prefix, int mlen) { struct trie_node *node; rcu_trie_ptr *edges[sizeof(union trie_prefix) * CHAR_BIT]; int depth = 0, ofs = 0; /* Walk the tree. */ for (edges[0] = root; (node = ovsrcu_get_protected(struct trie_node *, edges[depth])); edges[++depth] = trie_next_edge(node, prefix, ofs)) { unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen); if (eqbits < node->n_bits) { /* Mismatch, nothing to be removed. This should never happen, as * only rules in the classifier are ever removed. */ break; /* Log a warning. */ } /* Full match so far. */ ofs += eqbits; if (ofs == mlen) { /* Full prefix match at the current node, remove rule here. */ if (!node->n_rules) { break; /* Log a warning. */ } node->n_rules--; /* Check if can prune the tree. */ while (!node->n_rules) { struct trie_node *next, *edge0 = ovsrcu_get_protected(struct trie_node *, &node->edges[0]), *edge1 = ovsrcu_get_protected(struct trie_node *, &node->edges[1]); if (edge0 && edge1) { break; /* A branching point, cannot prune. */ } /* Else have at most one child node, remove this node. */ next = edge0 ? edge0 : edge1; if (next) { if (node->n_bits + next->n_bits > TRIE_PREFIX_BITS) { break; /* Cannot combine. */ } next = trie_node_rcu_realloc(next); /* Modify. */ /* Combine node with next. */ next->prefix = node->prefix | next->prefix >> node->n_bits; next->n_bits += node->n_bits; } /* Update the parent's edge. */ ovsrcu_set(edges[depth], next); /* Publish changes. */ trie_node_destroy(node); if (next || !depth) { /* Branch not pruned or at root, nothing more to do. */ break; } node = ovsrcu_get_protected(struct trie_node *, edges[--depth]); } return; } } /* Cannot go deeper. This should never happen, since only rules * that actually exist in the classifier are ever removed. */ VLOG_WARN("Trying to remove non-existing rule from a prefix trie."); } #define CLS_MATCH_POISON (struct cls_match *)(UINTPTR_MAX / 0xf * 0xb) void cls_match_free_cb(struct cls_match *rule) { ovsrcu_set_hidden(&rule->next, CLS_MATCH_POISON); free(rule); } openvswitch-2.5.9/lib/PaxHeaders.82075/sha1.c0000644000000000000000000000013113534540071015411 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.873854104 openvswitch-2.5.9/lib/sha1.c0000644000175000017500000002140313534540071017100 0ustar00jpettitjpettit00000000000000/* * This file is from the Apache Portable Runtime Library. * The full upstream copyright and license statement is included below. * Modifications copyright (c) 2009, 2010 Nicira, Inc. */ /* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This software also makes use of the following component: * * NIST Secure Hash Algorithm * heavily modified by Uwe Hollerbach uh@alumni.caltech edu * from Peter C. Gutmann's implementation as found in * Applied Cryptography by Bruce Schneier * This code is hereby placed in the public domain */ #include #include "sha1.h" #include #include #include "compiler.h" #include "util.h" /* a bit faster & bigger, if defined */ #define UNROLL_LOOPS /* SHA f()-functions */ static inline uint32_t f1(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (~x & z); } static inline uint32_t f2(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } static inline uint32_t f3(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (x & z) | (y & z); } static inline uint32_t f4(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } /* SHA constants */ #define CONST1 0x5a827999L #define CONST2 0x6ed9eba1L #define CONST3 0x8f1bbcdcL #define CONST4 0xca62c1d6L /* 32-bit rotate */ static inline uint32_t rotate32(uint32_t x, int n) { return ((x << n) | (x >> (32 - n))); } #define FUNC(n, i) \ do { \ temp = rotate32(A, 5) + f##n(B, C, D) + E + W[i] + CONST##n; \ E = D; \ D = C; \ C = rotate32(B, 30); \ B = A; \ A = temp; \ } while (0) #define SHA_BLOCK_SIZE 64 /* Do SHA transformation. */ static void sha_transform(struct sha1_ctx *sha_info) { int i; uint32_t temp, A, B, C, D, E, W[80]; for (i = 0; i < 16; ++i) { W[i] = sha_info->data[i]; } for (i = 16; i < 80; ++i) { W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; W[i] = rotate32(W[i], 1); } A = sha_info->digest[0]; B = sha_info->digest[1]; C = sha_info->digest[2]; D = sha_info->digest[3]; E = sha_info->digest[4]; #ifdef UNROLL_LOOPS FUNC(1, 0); FUNC(1, 1); FUNC(1, 2); FUNC(1, 3); FUNC(1, 4); FUNC(1, 5); FUNC(1, 6); FUNC(1, 7); FUNC(1, 8); FUNC(1, 9); FUNC(1,10); FUNC(1,11); FUNC(1,12); FUNC(1,13); FUNC(1,14); FUNC(1,15); FUNC(1,16); FUNC(1,17); FUNC(1,18); FUNC(1,19); FUNC(2,20); FUNC(2,21); FUNC(2,22); FUNC(2,23); FUNC(2,24); FUNC(2,25); FUNC(2,26); FUNC(2,27); FUNC(2,28); FUNC(2,29); FUNC(2,30); FUNC(2,31); FUNC(2,32); FUNC(2,33); FUNC(2,34); FUNC(2,35); FUNC(2,36); FUNC(2,37); FUNC(2,38); FUNC(2,39); FUNC(3,40); FUNC(3,41); FUNC(3,42); FUNC(3,43); FUNC(3,44); FUNC(3,45); FUNC(3,46); FUNC(3,47); FUNC(3,48); FUNC(3,49); FUNC(3,50); FUNC(3,51); FUNC(3,52); FUNC(3,53); FUNC(3,54); FUNC(3,55); FUNC(3,56); FUNC(3,57); FUNC(3,58); FUNC(3,59); FUNC(4,60); FUNC(4,61); FUNC(4,62); FUNC(4,63); FUNC(4,64); FUNC(4,65); FUNC(4,66); FUNC(4,67); FUNC(4,68); FUNC(4,69); FUNC(4,70); FUNC(4,71); FUNC(4,72); FUNC(4,73); FUNC(4,74); FUNC(4,75); FUNC(4,76); FUNC(4,77); FUNC(4,78); FUNC(4,79); #else /* !UNROLL_LOOPS */ for (i = 0; i < 20; ++i) { FUNC(1,i); } for (i = 20; i < 40; ++i) { FUNC(2,i); } for (i = 40; i < 60; ++i) { FUNC(3,i); } for (i = 60; i < 80; ++i) { FUNC(4,i); } #endif /* !UNROLL_LOOPS */ sha_info->digest[0] += A; sha_info->digest[1] += B; sha_info->digest[2] += C; sha_info->digest[3] += D; sha_info->digest[4] += E; } /* 'count' is the number of bytes to do an endian flip. */ static void maybe_byte_reverse(uint32_t *buffer OVS_UNUSED, int count OVS_UNUSED) { #if !WORDS_BIGENDIAN int i; uint8_t ct[4], *cp; count /= sizeof(uint32_t); cp = (uint8_t *) buffer; for (i = 0; i < count; i++) { ct[0] = cp[0]; ct[1] = cp[1]; ct[2] = cp[2]; ct[3] = cp[3]; cp[0] = ct[3]; cp[1] = ct[2]; cp[2] = ct[1]; cp[3] = ct[0]; cp += sizeof(uint32_t); } #endif } /* * Initialize the SHA digest. * context: The SHA context to initialize */ void sha1_init(struct sha1_ctx *sha_info) { sha_info->digest[0] = 0x67452301L; sha_info->digest[1] = 0xefcdab89L; sha_info->digest[2] = 0x98badcfeL; sha_info->digest[3] = 0x10325476L; sha_info->digest[4] = 0xc3d2e1f0L; sha_info->count_lo = 0L; sha_info->count_hi = 0L; sha_info->local = 0; } /* * Update the SHA digest. * context: The SHA1 context to update. * input: The buffer to add to the SHA digest. * inputLen: The length of the input buffer. */ void sha1_update(struct sha1_ctx *ctx, const void *buffer_, size_t count) { const uint8_t *buffer = buffer_; unsigned int i; if ((ctx->count_lo + (count << 3)) < ctx->count_lo) { ctx->count_hi++; } ctx->count_lo += count << 3; ctx->count_hi += count >> 29; if (ctx->local) { i = SHA_BLOCK_SIZE - ctx->local; if (i > count) { i = count; } memcpy(((uint8_t *) ctx->data) + ctx->local, buffer, i); count -= i; buffer += i; ctx->local += i; if (ctx->local == SHA_BLOCK_SIZE) { maybe_byte_reverse(ctx->data, SHA_BLOCK_SIZE); sha_transform(ctx); } else { return; } } while (count >= SHA_BLOCK_SIZE) { memcpy(ctx->data, buffer, SHA_BLOCK_SIZE); buffer += SHA_BLOCK_SIZE; count -= SHA_BLOCK_SIZE; maybe_byte_reverse(ctx->data, SHA_BLOCK_SIZE); sha_transform(ctx); } memcpy(ctx->data, buffer, count); ctx->local = count; } /* * Finish computing the SHA digest. * digest: the output buffer in which to store the digest. * context: The context to finalize. */ void sha1_final(struct sha1_ctx *ctx, uint8_t digest[SHA1_DIGEST_SIZE]) { int count, i, j; uint32_t lo_bit_count, hi_bit_count, k; lo_bit_count = ctx->count_lo; hi_bit_count = ctx->count_hi; count = (int) ((lo_bit_count >> 3) & 0x3f); ((uint8_t *) ctx->data)[count++] = 0x80; if (count > SHA_BLOCK_SIZE - 8) { memset(((uint8_t *) ctx->data) + count, 0, SHA_BLOCK_SIZE - count); maybe_byte_reverse(ctx->data, SHA_BLOCK_SIZE); sha_transform(ctx); memset((uint8_t *) ctx->data, 0, SHA_BLOCK_SIZE - 8); } else { memset(((uint8_t *) ctx->data) + count, 0, SHA_BLOCK_SIZE - 8 - count); } maybe_byte_reverse(ctx->data, SHA_BLOCK_SIZE); ctx->data[14] = hi_bit_count; ctx->data[15] = lo_bit_count; sha_transform(ctx); for (i = j = 0; j < SHA1_DIGEST_SIZE; i++) { k = ctx->digest[i]; digest[j++] = k >> 24; digest[j++] = k >> 16; digest[j++] = k >> 8; digest[j++] = k; } } /* Computes the hash of 'n' bytes in 'data' into 'digest'. */ void sha1_bytes(const void *data, size_t n, uint8_t digest[SHA1_DIGEST_SIZE]) { struct sha1_ctx ctx; sha1_init(&ctx); sha1_update(&ctx, data, n); sha1_final(&ctx, digest); } void sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char hex[SHA1_HEX_DIGEST_LEN + 1]) { int i; for (i = 0; i < SHA1_DIGEST_SIZE; i++) { *hex++ = "0123456789abcdef"[digest[i] >> 4]; *hex++ = "0123456789abcdef"[digest[i] & 15]; } *hex = '\0'; } bool sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex) { int i; for (i = 0; i < SHA1_DIGEST_SIZE; i++) { bool ok; digest[i] = hexits_value(hex, 2, &ok); if (!ok) { return false; } hex += 2; } return true; } openvswitch-2.5.9/lib/PaxHeaders.82075/sset.h0000644000000000000000000000013213534540071015541 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.885854193 openvswitch-2.5.9/lib/sset.h0000644000175000017500000000667713534540071017247 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SSET_H #define SSET_H #include "hmap.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif struct sset_node { struct hmap_node hmap_node; char name[1]; }; /* A set of strings. */ struct sset { struct hmap map; }; #define SSET_INITIALIZER(SSET) { HMAP_INITIALIZER(&(SSET)->map) } /* Basics. */ void sset_init(struct sset *); void sset_destroy(struct sset *); void sset_clone(struct sset *, const struct sset *); void sset_swap(struct sset *, struct sset *); void sset_moved(struct sset *); /* Count. */ bool sset_is_empty(const struct sset *); size_t sset_count(const struct sset *); /* Insertion. */ struct sset_node *sset_add(struct sset *, const char *); struct sset_node *sset_add_and_free(struct sset *, char *); void sset_add_assert(struct sset *, const char *); void sset_add_array(struct sset *, char **, size_t n); /* Deletion. */ void sset_clear(struct sset *); void sset_delete(struct sset *, struct sset_node *); bool sset_find_and_delete(struct sset *, const char *); void sset_find_and_delete_assert(struct sset *, const char *); char *sset_pop(struct sset *); /* Search. */ struct sset_node *sset_find(const struct sset *, const char *); bool sset_contains(const struct sset *, const char *); bool sset_equals(const struct sset *, const struct sset *); struct sset_node *sset_at_position(const struct sset *, uint32_t *bucketp, uint32_t *offsetp); /* Set operations. */ void sset_intersect(struct sset *, const struct sset *); /* Iteration macros. */ #define SSET_FOR_EACH(NAME, SSET) \ for ((NAME) = SSET_FIRST(SSET); \ NAME != NULL; \ (NAME) = SSET_NEXT(SSET, NAME)) #define SSET_FOR_EACH_SAFE(NAME, NEXT, SSET) \ for ((NAME) = SSET_FIRST(SSET); \ (NAME != NULL \ ? (NEXT) = SSET_NEXT(SSET, NAME), true \ : false); \ (NAME) = (NEXT)) const char **sset_array(const struct sset *); const char **sset_sort(const struct sset *); /* Implementation helper macros. */ #define SSET_NODE_FROM_HMAP_NODE(HMAP_NODE) \ CONTAINER_OF(HMAP_NODE, struct sset_node, hmap_node) #define SSET_NAME_FROM_HMAP_NODE(HMAP_NODE) \ HMAP_NODE == NULL \ ? NULL \ : (CONST_CAST(const char *, (SSET_NODE_FROM_HMAP_NODE(HMAP_NODE)->name))) #define SSET_NODE_FROM_NAME(NAME) CONTAINER_OF(NAME, struct sset_node, name) #define SSET_FIRST(SSET) SSET_NAME_FROM_HMAP_NODE(hmap_first(&(SSET)->map)) #define SSET_NEXT(SSET, NAME) \ SSET_NAME_FROM_HMAP_NODE( \ hmap_next(&(SSET)->map, &SSET_NODE_FROM_NAME(NAME)->hmap_node)) #ifdef __cplusplus } #endif #endif /* sset.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-error.h0000644000000000000000000000013213534540071017027 xustar0030 mtime=1567801401.553682286 30 atime=1567801402.093686252 30 ctime=1567801424.837853839 openvswitch-2.5.9/lib/ovsdb-error.h0000644000175000017500000000531213534540071020516 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_ERROR_H #define OVSDB_ERROR_H 1 #include "compiler.h" struct json; struct ovsdb_error *ovsdb_error(const char *tag, const char *details, ...) OVS_PRINTF_FORMAT(2, 3) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_io_error(int error, const char *details, ...) OVS_PRINTF_FORMAT(2, 3) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_syntax_error(const struct json *, const char *tag, const char *details, ...) OVS_PRINTF_FORMAT(3, 4) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...) OVS_PRINTF_FORMAT(2, 3); struct ovsdb_error *ovsdb_internal_error(struct ovsdb_error *error, const char *file, int line, const char *details, ...) OVS_PRINTF_FORMAT(4, 5) OVS_WARN_UNUSED_RESULT; /* Returns a pointer to an ovsdb_error that represents an internal error for * the current file name and line number with MSG as the associated message. * The caller is responsible for freeing the internal error. */ #define OVSDB_BUG(MSG) \ ovsdb_internal_error(NULL, __FILE__, __LINE__, "%s", MSG) /* Returns a pointer to an ovsdb_error that represents an internal error for * the current file name and line number, with MSG as the associated message. * If ERROR is nonnull then the internal error is wrapped around ERROR. Takes * ownership of ERROR. The caller is responsible for freeing the returned * error. */ #define OVSDB_WRAP_BUG(MSG, ERROR) \ ovsdb_internal_error(ERROR, __FILE__, __LINE__, "%s", MSG) void ovsdb_error_destroy(struct ovsdb_error *); struct ovsdb_error *ovsdb_error_clone(const struct ovsdb_error *) OVS_WARN_UNUSED_RESULT; char *ovsdb_error_to_string(const struct ovsdb_error *); struct json *ovsdb_error_to_json(const struct ovsdb_error *); const char *ovsdb_error_get_tag(const struct ovsdb_error *); void ovsdb_error_assert(struct ovsdb_error *); #endif /* ovsdb-error.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/jsonrpc.c0000644000000000000000000000013113534540071016233 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.745853161 openvswitch-2.5.9/lib/jsonrpc.c0000644000175000017500000010133013534540071017720 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "jsonrpc.h" #include #include "byteq.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "json.h" #include "list.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "poll-loop.h" #include "reconnect.h" #include "stream.h" #include "timeval.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(jsonrpc); struct jsonrpc { struct stream *stream; char *name; int status; /* Input. */ struct byteq input; uint8_t input_buffer[512]; struct json_parser *parser; /* Output. */ struct ovs_list output; /* Contains "struct ofpbuf"s. */ size_t output_count; /* Number of elements in "output". */ size_t backlog; }; /* Rate limit for error messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); static struct jsonrpc_msg *jsonrpc_parse_received_message(struct jsonrpc *); static void jsonrpc_cleanup(struct jsonrpc *); static void jsonrpc_error(struct jsonrpc *, int error); /* This is just the same as stream_open() except that it uses the default * JSONRPC port if none is specified. */ int jsonrpc_stream_open(const char *name, struct stream **streamp, uint8_t dscp) { return stream_open_with_default_port(name, OVSDB_PORT, streamp, dscp); } /* This is just the same as pstream_open() except that it uses the default * JSONRPC port if none is specified. */ int jsonrpc_pstream_open(const char *name, struct pstream **pstreamp, uint8_t dscp) { return pstream_open_with_default_port(name, OVSDB_PORT, pstreamp, dscp); } /* Returns a new JSON-RPC stream that uses 'stream' for input and output. The * new jsonrpc object takes ownership of 'stream'. */ struct jsonrpc * jsonrpc_open(struct stream *stream) { struct jsonrpc *rpc; ovs_assert(stream != NULL); rpc = xzalloc(sizeof *rpc); rpc->name = xstrdup(stream_get_name(stream)); rpc->stream = stream; byteq_init(&rpc->input, rpc->input_buffer, sizeof rpc->input_buffer); list_init(&rpc->output); return rpc; } /* Destroys 'rpc', closing the stream on which it is based, and frees its * memory. */ void jsonrpc_close(struct jsonrpc *rpc) { if (rpc) { jsonrpc_cleanup(rpc); free(rpc->name); free(rpc); } } /* Performs periodic maintenance on 'rpc', such as flushing output buffers. */ void jsonrpc_run(struct jsonrpc *rpc) { if (rpc->status) { return; } stream_run(rpc->stream); while (!list_is_empty(&rpc->output)) { struct ofpbuf *buf = ofpbuf_from_list(rpc->output.next); int retval; retval = stream_send(rpc->stream, buf->data, buf->size); if (retval >= 0) { rpc->backlog -= retval; ofpbuf_pull(buf, retval); if (!buf->size) { list_remove(&buf->list_node); rpc->output_count--; ofpbuf_delete(buf); } } else { if (retval != -EAGAIN) { VLOG_WARN_RL(&rl, "%s: send error: %s", rpc->name, ovs_strerror(-retval)); jsonrpc_error(rpc, -retval); } break; } } } /* Arranges for the poll loop to wake up when 'rpc' needs to perform * maintenance activities. */ void jsonrpc_wait(struct jsonrpc *rpc) { if (!rpc->status) { stream_run_wait(rpc->stream); if (!list_is_empty(&rpc->output)) { stream_send_wait(rpc->stream); } } } /* * Returns the current status of 'rpc'. The possible return values are: * - 0: no error yet * - >0: errno value * - EOF: end of file (remote end closed connection; not necessarily an error). * * When this functions nonzero, 'rpc' is effectively out of commission. 'rpc' * will not receive any more messages and any further messages that one * attempts to send with 'rpc' will be discarded. The caller can keep 'rpc' * around as long as it wants, but it's not going to provide any more useful * services. */ int jsonrpc_get_status(const struct jsonrpc *rpc) { return rpc->status; } /* Returns the number of bytes buffered by 'rpc' to be written to the * underlying stream. Always returns 0 if 'rpc' has encountered an error or if * the remote end closed the connection. */ size_t jsonrpc_get_backlog(const struct jsonrpc *rpc) { return rpc->status ? 0 : rpc->backlog; } /* Returns the number of bytes that have been received on 'rpc''s underlying * stream. (The value wraps around if it exceeds UINT_MAX.) */ unsigned int jsonrpc_get_received_bytes(const struct jsonrpc *rpc) { return rpc->input.head; } /* Returns 'rpc''s name, that is, the name returned by stream_get_name() for * the stream underlying 'rpc' when 'rpc' was created. */ const char * jsonrpc_get_name(const struct jsonrpc *rpc) { return rpc->name; } static void jsonrpc_log_msg(const struct jsonrpc *rpc, const char *title, const struct jsonrpc_msg *msg) { if (VLOG_IS_DBG_ENABLED()) { struct ds s = DS_EMPTY_INITIALIZER; if (msg->method) { ds_put_format(&s, ", method=\"%s\"", msg->method); } if (msg->params) { ds_put_cstr(&s, ", params="); json_to_ds(msg->params, 0, &s); } if (msg->result) { ds_put_cstr(&s, ", result="); json_to_ds(msg->result, 0, &s); } if (msg->error) { ds_put_cstr(&s, ", error="); json_to_ds(msg->error, 0, &s); } if (msg->id) { ds_put_cstr(&s, ", id="); json_to_ds(msg->id, 0, &s); } VLOG_DBG("%s: %s %s%s", rpc->name, title, jsonrpc_msg_type_to_string(msg->type), ds_cstr(&s)); ds_destroy(&s); } } /* Schedules 'msg' to be sent on 'rpc' and returns 'rpc''s status (as with * jsonrpc_get_status()). * * If 'msg' cannot be sent immediately, it is appended to a buffer. The caller * is responsible for ensuring that the amount of buffered data is somehow * limited. (jsonrpc_get_backlog() returns the amount of data currently * buffered in 'rpc'.) * * Always takes ownership of 'msg', regardless of success. */ int jsonrpc_send(struct jsonrpc *rpc, struct jsonrpc_msg *msg) { struct ofpbuf *buf; struct json *json; struct ds ds = DS_EMPTY_INITIALIZER; size_t length; if (rpc->status) { jsonrpc_msg_destroy(msg); return rpc->status; } jsonrpc_log_msg(rpc, "send", msg); json = jsonrpc_msg_to_json(msg); json_to_ds(json, 0, &ds); length = ds.length; json_destroy(json); buf = xmalloc(sizeof *buf); ofpbuf_use_ds(buf, &ds); list_push_back(&rpc->output, &buf->list_node); rpc->output_count++; rpc->backlog += length; if (rpc->output_count >= 50) { VLOG_INFO_RL(&rl, "excessive sending backlog, jsonrpc: %s, num of" " msgs: %"PRIuSIZE", backlog: %"PRIuSIZE".", rpc->name, rpc->output_count, rpc->backlog); } if (rpc->backlog == length) { jsonrpc_run(rpc); } return rpc->status; } /* Attempts to receive a message from 'rpc'. * * If successful, stores the received message in '*msgp' and returns 0. The * caller takes ownership of '*msgp' and must eventually destroy it with * jsonrpc_msg_destroy(). * * Otherwise, stores NULL in '*msgp' and returns one of the following: * * - EAGAIN: No message has been received. * * - EOF: The remote end closed the connection gracefully. * * - Otherwise an errno value that represents a JSON-RPC protocol violation * or another error fatal to the connection. 'rpc' will not send or * receive any more messages. */ int jsonrpc_recv(struct jsonrpc *rpc, struct jsonrpc_msg **msgp) { int i; *msgp = NULL; if (rpc->status) { return rpc->status; } for (i = 0; i < 50; i++) { size_t n, used; /* Fill our input buffer if it's empty. */ if (byteq_is_empty(&rpc->input)) { size_t chunk; int retval; chunk = byteq_headroom(&rpc->input); retval = stream_recv(rpc->stream, byteq_head(&rpc->input), chunk); if (retval < 0) { if (retval == -EAGAIN) { return EAGAIN; } else { VLOG_WARN_RL(&rl, "%s: receive error: %s", rpc->name, ovs_strerror(-retval)); jsonrpc_error(rpc, -retval); return rpc->status; } } else if (retval == 0) { jsonrpc_error(rpc, EOF); return EOF; } byteq_advance_head(&rpc->input, retval); } /* We have some input. Feed it into the JSON parser. */ if (!rpc->parser) { rpc->parser = json_parser_create(0); } n = byteq_tailroom(&rpc->input); used = json_parser_feed(rpc->parser, (char *) byteq_tail(&rpc->input), n); byteq_advance_tail(&rpc->input, used); /* If we have complete JSON, attempt to parse it as JSON-RPC. */ if (json_parser_is_done(rpc->parser)) { *msgp = jsonrpc_parse_received_message(rpc); if (*msgp) { return 0; } if (rpc->status) { const struct byteq *q = &rpc->input; if (q->head <= q->size) { stream_report_content(q->buffer, q->head, STREAM_JSONRPC, THIS_MODULE, rpc->name); } return rpc->status; } } } return EAGAIN; } /* Causes the poll loop to wake up when jsonrpc_recv() may return a value other * than EAGAIN. */ void jsonrpc_recv_wait(struct jsonrpc *rpc) { if (rpc->status || !byteq_is_empty(&rpc->input)) { poll_immediate_wake_at(rpc->name); } else { stream_recv_wait(rpc->stream); } } /* Sends 'msg' on 'rpc' and waits for it to be successfully queued to the * underlying stream. Returns 0 if 'msg' was sent successfully, otherwise a * status value (see jsonrpc_get_status()). * * Always takes ownership of 'msg', regardless of success. */ int jsonrpc_send_block(struct jsonrpc *rpc, struct jsonrpc_msg *msg) { int error; fatal_signal_run(); error = jsonrpc_send(rpc, msg); if (error) { return error; } for (;;) { jsonrpc_run(rpc); if (list_is_empty(&rpc->output) || rpc->status) { return rpc->status; } jsonrpc_wait(rpc); poll_block(); } } /* Waits for a message to be received on 'rpc'. Same semantics as * jsonrpc_recv() except that EAGAIN will never be returned. */ int jsonrpc_recv_block(struct jsonrpc *rpc, struct jsonrpc_msg **msgp) { for (;;) { int error = jsonrpc_recv(rpc, msgp); if (error != EAGAIN) { fatal_signal_run(); return error; } jsonrpc_run(rpc); jsonrpc_wait(rpc); jsonrpc_recv_wait(rpc); poll_block(); } } /* Sends 'request' to 'rpc' then waits for a reply. The return value is 0 if * successful, in which case '*replyp' is set to the reply, which the caller * must eventually free with jsonrpc_msg_destroy(). Otherwise returns a status * value (see jsonrpc_get_status()). * * Discards any message received on 'rpc' that is not a reply to 'request' * (based on message id). * * Always takes ownership of 'request', regardless of success. */ int jsonrpc_transact_block(struct jsonrpc *rpc, struct jsonrpc_msg *request, struct jsonrpc_msg **replyp) { struct jsonrpc_msg *reply = NULL; struct json *id; int error; id = json_clone(request->id); error = jsonrpc_send_block(rpc, request); if (!error) { for (;;) { error = jsonrpc_recv_block(rpc, &reply); if (error) { break; } if ((reply->type == JSONRPC_REPLY || reply->type == JSONRPC_ERROR) && json_equal(id, reply->id)) { break; } jsonrpc_msg_destroy(reply); } } *replyp = error ? NULL : reply; json_destroy(id); return error; } /* Attempts to parse the content of 'rpc->parser' (which is complete JSON) as a * JSON-RPC message. If successful, returns the JSON-RPC message. On failure, * signals an error on 'rpc' with jsonrpc_error() and returns NULL. */ static struct jsonrpc_msg * jsonrpc_parse_received_message(struct jsonrpc *rpc) { struct jsonrpc_msg *msg; struct json *json; char *error; json = json_parser_finish(rpc->parser); rpc->parser = NULL; if (json->type == JSON_STRING) { VLOG_WARN_RL(&rl, "%s: error parsing stream: %s", rpc->name, json_string(json)); jsonrpc_error(rpc, EPROTO); json_destroy(json); return NULL; } error = jsonrpc_msg_from_json(json, &msg); if (error) { VLOG_WARN_RL(&rl, "%s: received bad JSON-RPC message: %s", rpc->name, error); free(error); jsonrpc_error(rpc, EPROTO); return NULL; } jsonrpc_log_msg(rpc, "received", msg); return msg; } static void jsonrpc_error(struct jsonrpc *rpc, int error) { ovs_assert(error); if (!rpc->status) { rpc->status = error; jsonrpc_cleanup(rpc); } } static void jsonrpc_cleanup(struct jsonrpc *rpc) { stream_close(rpc->stream); rpc->stream = NULL; json_parser_abort(rpc->parser); rpc->parser = NULL; ofpbuf_list_delete(&rpc->output); rpc->backlog = 0; rpc->output_count = 0; } static struct jsonrpc_msg * jsonrpc_create(enum jsonrpc_msg_type type, const char *method, struct json *params, struct json *result, struct json *error, struct json *id) { struct jsonrpc_msg *msg = xmalloc(sizeof *msg); msg->type = type; msg->method = method ? xstrdup(method) : NULL; msg->params = params; msg->result = result; msg->error = error; msg->id = id; return msg; } static struct json * jsonrpc_create_id(void) { static atomic_count next_id = ATOMIC_COUNT_INIT(0); unsigned int id; id = atomic_count_inc(&next_id); return json_integer_create(id); } struct jsonrpc_msg * jsonrpc_create_request(const char *method, struct json *params, struct json **idp) { struct json *id = jsonrpc_create_id(); if (idp) { *idp = json_clone(id); } return jsonrpc_create(JSONRPC_REQUEST, method, params, NULL, NULL, id); } struct jsonrpc_msg * jsonrpc_create_notify(const char *method, struct json *params) { return jsonrpc_create(JSONRPC_NOTIFY, method, params, NULL, NULL, NULL); } struct jsonrpc_msg * jsonrpc_create_reply(struct json *result, const struct json *id) { return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, result, NULL, json_clone(id)); } struct jsonrpc_msg * jsonrpc_create_error(struct json *error, const struct json *id) { return jsonrpc_create(JSONRPC_REPLY, NULL, NULL, NULL, error, json_clone(id)); } const char * jsonrpc_msg_type_to_string(enum jsonrpc_msg_type type) { switch (type) { case JSONRPC_REQUEST: return "request"; case JSONRPC_NOTIFY: return "notification"; case JSONRPC_REPLY: return "reply"; case JSONRPC_ERROR: return "error"; } return "(null)"; } char * jsonrpc_msg_is_valid(const struct jsonrpc_msg *m) { const char *type_name; unsigned int pattern; if (m->params && m->params->type != JSON_ARRAY) { return xstrdup("\"params\" must be JSON array"); } switch (m->type) { case JSONRPC_REQUEST: pattern = 0x11001; break; case JSONRPC_NOTIFY: pattern = 0x11000; break; case JSONRPC_REPLY: pattern = 0x00101; break; case JSONRPC_ERROR: pattern = 0x00011; break; default: return xasprintf("invalid JSON-RPC message type %d", m->type); } type_name = jsonrpc_msg_type_to_string(m->type); if ((m->method != NULL) != ((pattern & 0x10000) != 0)) { return xasprintf("%s must%s have \"method\"", type_name, (pattern & 0x10000) ? "" : " not"); } if ((m->params != NULL) != ((pattern & 0x1000) != 0)) { return xasprintf("%s must%s have \"params\"", type_name, (pattern & 0x1000) ? "" : " not"); } if ((m->result != NULL) != ((pattern & 0x100) != 0)) { return xasprintf("%s must%s have \"result\"", type_name, (pattern & 0x100) ? "" : " not"); } if ((m->error != NULL) != ((pattern & 0x10) != 0)) { return xasprintf("%s must%s have \"error\"", type_name, (pattern & 0x10) ? "" : " not"); } if ((m->id != NULL) != ((pattern & 0x1) != 0)) { return xasprintf("%s must%s have \"id\"", type_name, (pattern & 0x1) ? "" : " not"); } return NULL; } void jsonrpc_msg_destroy(struct jsonrpc_msg *m) { if (m) { free(m->method); json_destroy(m->params); json_destroy(m->result); json_destroy(m->error); json_destroy(m->id); free(m); } } static struct json * null_from_json_null(struct json *json) { if (json && json->type == JSON_NULL) { json_destroy(json); return NULL; } return json; } char * jsonrpc_msg_from_json(struct json *json, struct jsonrpc_msg **msgp) { struct json *method = NULL; struct jsonrpc_msg *msg = NULL; struct shash *object; char *error; if (json->type != JSON_OBJECT) { error = xstrdup("message is not a JSON object"); goto exit; } object = json_object(json); method = shash_find_and_delete(object, "method"); if (method && method->type != JSON_STRING) { error = xstrdup("method is not a JSON string"); goto exit; } msg = xzalloc(sizeof *msg); msg->method = method ? xstrdup(method->u.string) : NULL; msg->params = null_from_json_null(shash_find_and_delete(object, "params")); msg->result = null_from_json_null(shash_find_and_delete(object, "result")); msg->error = null_from_json_null(shash_find_and_delete(object, "error")); msg->id = null_from_json_null(shash_find_and_delete(object, "id")); msg->type = (msg->result ? JSONRPC_REPLY : msg->error ? JSONRPC_ERROR : msg->id ? JSONRPC_REQUEST : JSONRPC_NOTIFY); if (!shash_is_empty(object)) { error = xasprintf("message has unexpected member \"%s\"", shash_first(object)->name); goto exit; } error = jsonrpc_msg_is_valid(msg); if (error) { goto exit; } exit: json_destroy(method); json_destroy(json); if (error) { jsonrpc_msg_destroy(msg); msg = NULL; } *msgp = msg; return error; } struct json * jsonrpc_msg_to_json(struct jsonrpc_msg *m) { struct json *json = json_object_create(); if (m->method) { json_object_put(json, "method", json_string_create_nocopy(m->method)); } if (m->params) { json_object_put(json, "params", m->params); } if (m->result) { json_object_put(json, "result", m->result); } else if (m->type == JSONRPC_ERROR) { json_object_put(json, "result", json_null_create()); } if (m->error) { json_object_put(json, "error", m->error); } else if (m->type == JSONRPC_REPLY) { json_object_put(json, "error", json_null_create()); } if (m->id) { json_object_put(json, "id", m->id); } else if (m->type == JSONRPC_NOTIFY) { json_object_put(json, "id", json_null_create()); } free(m); return json; } /* A JSON-RPC session with reconnection. */ struct jsonrpc_session { struct reconnect *reconnect; struct jsonrpc *rpc; struct stream *stream; struct pstream *pstream; int last_error; unsigned int seqno; uint8_t dscp; }; /* Creates and returns a jsonrpc_session to 'name', which should be a string * acceptable to stream_open() or pstream_open(). * * If 'name' is an active connection method, e.g. "tcp:127.1.2.3", the new * jsonrpc_session connects to 'name'. If 'retry' is true, then the new * session connects and reconnects to 'name', with backoff. If 'retry' is * false, the new session will only try to connect once and after a connection * failure or a disconnection jsonrpc_session_is_alive() will return false for * the new session. * * If 'name' is a passive connection method, e.g. "ptcp:", the new * jsonrpc_session listens for connections to 'name'. It maintains at most one * connection at any given time. Any new connection causes the previous one * (if any) to be dropped. */ struct jsonrpc_session * jsonrpc_session_open(const char *name, bool retry) { struct jsonrpc_session *s; s = xmalloc(sizeof *s); s->reconnect = reconnect_create(time_msec()); reconnect_set_name(s->reconnect, name); reconnect_enable(s->reconnect, time_msec()); s->rpc = NULL; s->stream = NULL; s->pstream = NULL; s->seqno = 0; s->dscp = 0; s->last_error = 0; if (!pstream_verify_name(name)) { reconnect_set_passive(s->reconnect, true, time_msec()); } else if (!retry) { reconnect_set_max_tries(s->reconnect, 1); reconnect_set_backoff(s->reconnect, INT_MAX, INT_MAX); } if (!stream_or_pstream_needs_probes(name)) { reconnect_set_probe_interval(s->reconnect, 0); } return s; } /* Creates and returns a jsonrpc_session that is initially connected to * 'jsonrpc'. If the connection is dropped, it will not be reconnected. * * On the assumption that such connections are likely to be short-lived * (e.g. from ovs-vsctl), informational logging for them is suppressed. */ struct jsonrpc_session * jsonrpc_session_open_unreliably(struct jsonrpc *jsonrpc, uint8_t dscp) { struct jsonrpc_session *s; s = xmalloc(sizeof *s); s->reconnect = reconnect_create(time_msec()); reconnect_set_quiet(s->reconnect, true); reconnect_set_name(s->reconnect, jsonrpc_get_name(jsonrpc)); reconnect_set_max_tries(s->reconnect, 0); reconnect_connected(s->reconnect, time_msec()); s->dscp = dscp; s->rpc = jsonrpc; s->stream = NULL; s->pstream = NULL; s->seqno = 0; return s; } void jsonrpc_session_close(struct jsonrpc_session *s) { if (s) { jsonrpc_close(s->rpc); reconnect_destroy(s->reconnect); stream_close(s->stream); pstream_close(s->pstream); free(s); } } static void jsonrpc_session_disconnect(struct jsonrpc_session *s) { if (s->rpc) { jsonrpc_error(s->rpc, EOF); jsonrpc_close(s->rpc); s->rpc = NULL; s->seqno++; } else if (s->stream) { stream_close(s->stream); s->stream = NULL; s->seqno++; } } static void jsonrpc_session_connect(struct jsonrpc_session *s) { const char *name = reconnect_get_name(s->reconnect); int error; jsonrpc_session_disconnect(s); if (!reconnect_is_passive(s->reconnect)) { error = jsonrpc_stream_open(name, &s->stream, s->dscp); if (!error) { reconnect_connecting(s->reconnect, time_msec()); } else { s->last_error = error; } } else { error = s->pstream ? 0 : jsonrpc_pstream_open(name, &s->pstream, s->dscp); if (!error) { reconnect_listening(s->reconnect, time_msec()); } } if (error) { reconnect_connect_failed(s->reconnect, time_msec(), error); } s->seqno++; } void jsonrpc_session_run(struct jsonrpc_session *s) { if (s->pstream) { struct stream *stream; int error; error = pstream_accept(s->pstream, &stream); if (!error) { if (s->rpc || s->stream) { VLOG_INFO_RL(&rl, "%s: new connection replacing active connection", reconnect_get_name(s->reconnect)); jsonrpc_session_disconnect(s); } reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(stream); } else if (error != EAGAIN) { reconnect_listen_error(s->reconnect, time_msec(), error); pstream_close(s->pstream); s->pstream = NULL; } } if (s->rpc) { size_t backlog; int error; backlog = jsonrpc_get_backlog(s->rpc); jsonrpc_run(s->rpc); if (jsonrpc_get_backlog(s->rpc) < backlog) { /* Data previously caught in a queue was successfully sent (or * there's an error, which we'll catch below.) * * We don't count data that is successfully sent immediately as * activity, because there's a lot of queuing downstream from us, * which means that we can push a lot of data into a connection * that has stalled and won't ever recover. */ reconnect_activity(s->reconnect, time_msec()); } error = jsonrpc_get_status(s->rpc); if (error) { reconnect_disconnected(s->reconnect, time_msec(), error); jsonrpc_session_disconnect(s); s->last_error = error; } } else if (s->stream) { int error; stream_run(s->stream); error = stream_connect(s->stream); if (!error) { reconnect_connected(s->reconnect, time_msec()); s->rpc = jsonrpc_open(s->stream); s->stream = NULL; } else if (error != EAGAIN) { reconnect_connect_failed(s->reconnect, time_msec(), error); stream_close(s->stream); s->stream = NULL; s->last_error = error; } } switch (reconnect_run(s->reconnect, time_msec())) { case RECONNECT_CONNECT: jsonrpc_session_connect(s); break; case RECONNECT_DISCONNECT: reconnect_disconnected(s->reconnect, time_msec(), 0); jsonrpc_session_disconnect(s); break; case RECONNECT_PROBE: if (s->rpc) { struct json *params; struct jsonrpc_msg *request; params = json_array_create_empty(); request = jsonrpc_create_request("echo", params, NULL); json_destroy(request->id); request->id = json_string_create("echo"); jsonrpc_send(s->rpc, request); } break; } } void jsonrpc_session_wait(struct jsonrpc_session *s) { if (s->rpc) { jsonrpc_wait(s->rpc); } else if (s->stream) { stream_run_wait(s->stream); stream_connect_wait(s->stream); } if (s->pstream) { pstream_wait(s->pstream); } reconnect_wait(s->reconnect, time_msec()); } size_t jsonrpc_session_get_backlog(const struct jsonrpc_session *s) { return s->rpc ? jsonrpc_get_backlog(s->rpc) : 0; } /* Always returns a pointer to a valid C string, assuming 's' was initialized * correctly. */ const char * jsonrpc_session_get_name(const struct jsonrpc_session *s) { return reconnect_get_name(s->reconnect); } /* Always takes ownership of 'msg', regardless of success. */ int jsonrpc_session_send(struct jsonrpc_session *s, struct jsonrpc_msg *msg) { if (s->rpc) { return jsonrpc_send(s->rpc, msg); } else { jsonrpc_msg_destroy(msg); return ENOTCONN; } } struct jsonrpc_msg * jsonrpc_session_recv(struct jsonrpc_session *s) { if (s->rpc) { unsigned int received_bytes; struct jsonrpc_msg *msg; received_bytes = jsonrpc_get_received_bytes(s->rpc); jsonrpc_recv(s->rpc, &msg); if (received_bytes != jsonrpc_get_received_bytes(s->rpc)) { /* Data was successfully received. * * Previously we only counted receiving a full message as activity, * but with large messages or a slow connection that policy could * time out the session mid-message. */ reconnect_activity(s->reconnect, time_msec()); } if (msg) { if (msg->type == JSONRPC_REQUEST && !strcmp(msg->method, "echo")) { /* Echo request. Send reply. */ struct jsonrpc_msg *reply; reply = jsonrpc_create_reply(json_clone(msg->params), msg->id); jsonrpc_session_send(s, reply); } else if (msg->type == JSONRPC_REPLY && msg->id && msg->id->type == JSON_STRING && !strcmp(msg->id->u.string, "echo")) { /* It's a reply to our echo request. Suppress it. */ } else { return msg; } jsonrpc_msg_destroy(msg); } } return NULL; } void jsonrpc_session_recv_wait(struct jsonrpc_session *s) { if (s->rpc) { jsonrpc_recv_wait(s->rpc); } } /* Returns true if 's' is currently connected or trying to connect. */ bool jsonrpc_session_is_alive(const struct jsonrpc_session *s) { return s->rpc || s->stream || reconnect_get_max_tries(s->reconnect); } /* Returns true if 's' is currently connected. */ bool jsonrpc_session_is_connected(const struct jsonrpc_session *s) { return s->rpc != NULL; } /* Returns a sequence number for 's'. The sequence number increments every * time 's' connects or disconnects. Thus, a caller can use the change (or * lack of change) in the sequence number to figure out whether the underlying * connection is the same as before. */ unsigned int jsonrpc_session_get_seqno(const struct jsonrpc_session *s) { return s->seqno; } /* Returns the current status of 's'. If 's' is NULL or is disconnected, this * is 0, otherwise it is the status of the connection, as reported by * jsonrpc_get_status(). */ int jsonrpc_session_get_status(const struct jsonrpc_session *s) { return s && s->rpc ? jsonrpc_get_status(s->rpc) : 0; } /* Returns the last error reported on a connection by 's'. The return value is * 0 only if no connection made by 's' has ever encountered an error. See * jsonrpc_get_status() for return value interpretation. */ int jsonrpc_session_get_last_error(const struct jsonrpc_session *s) { return s->last_error; } /* Populates 'stats' with statistics from 's'. */ void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *s, struct reconnect_stats *stats) { reconnect_get_stats(s->reconnect, time_msec(), stats); } /* Enables 's' to reconnect to the peer if the connection drops. */ void jsonrpc_session_enable_reconnect(struct jsonrpc_session *s) { reconnect_set_max_tries(s->reconnect, UINT_MAX); reconnect_set_backoff(s->reconnect, RECONNECT_DEFAULT_MIN_BACKOFF, RECONNECT_DEFAULT_MAX_BACKOFF); } /* Forces 's' to drop its connection (if any) and reconnect. */ void jsonrpc_session_force_reconnect(struct jsonrpc_session *s) { reconnect_force_reconnect(s->reconnect, time_msec()); } /* Sets 'max_backoff' as the maximum time, in milliseconds, to wait after a * connection attempt fails before attempting to connect again. */ void jsonrpc_session_set_max_backoff(struct jsonrpc_session *s, int max_backoff) { reconnect_set_backoff(s->reconnect, 0, max_backoff); } /* Sets the "probe interval" for 's' to 'probe_interval', in milliseconds. If * this is zero, it disables the connection keepalive feature. Otherwise, if * 's' is idle for 'probe_interval' milliseconds then 's' will send an echo * request and, if no reply is received within an additional 'probe_interval' * milliseconds, close the connection (then reconnect, if that feature is * enabled). */ void jsonrpc_session_set_probe_interval(struct jsonrpc_session *s, int probe_interval) { reconnect_set_probe_interval(s->reconnect, probe_interval); } /* Sets the DSCP value used for 's''s connection to 'dscp'. If this is * different from the DSCP value currently in use then the connection is closed * and reconnected. */ void jsonrpc_session_set_dscp(struct jsonrpc_session *s, uint8_t dscp) { if (s->dscp != dscp) { pstream_close(s->pstream); s->pstream = NULL; s->dscp = dscp; jsonrpc_session_force_reconnect(s); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ssl-syn.man0000644000000000000000000000013213534540071016517 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801423.741845759 openvswitch-2.5.9/lib/ssl-syn.man0000644000175000017500000000024013534540071020201 0ustar00jpettitjpettit00000000000000.IP "Public key infrastructure options:" [\fB\-\-private\-key=\fIprivkey.pem\fR] .br [\fB\-\-certificate=\fIcert.pem\fR] .br [\fB\-\-ca\-cert=\fIcacert.pem\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/rconn.c0000644000000000000000000000013213534540071015675 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.861854016 openvswitch-2.5.9/lib/rconn.c0000644000175000017500000012304013534540071017363 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2019 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "rconn.h" #include #include #include #include #include "coverage.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "sat-math.h" #include "timeval.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(rconn); COVERAGE_DEFINE(rconn_discarded); COVERAGE_DEFINE(rconn_overflow); COVERAGE_DEFINE(rconn_queued); COVERAGE_DEFINE(rconn_sent); /* The connection states have the following meanings: * * - S_VOID: No connection information is configured. * * - S_BACKOFF: Waiting for a period of time before reconnecting. * * - S_CONNECTING: A connection attempt is in progress and has not yet * succeeded or failed. * * - S_ACTIVE: A connection has been established and appears to be healthy. * * - S_IDLE: A connection has been established but has been idle for some * time. An echo request has been sent, but no reply has yet been * received. * * - S_DISCONNECTED: An unreliable connection has disconnected and cannot be * automatically retried. */ #define STATES \ STATE(VOID, 1 << 0) \ STATE(BACKOFF, 1 << 1) \ STATE(CONNECTING, 1 << 2) \ STATE(ACTIVE, 1 << 3) \ STATE(IDLE, 1 << 4) \ STATE(DISCONNECTED, 1 << 5) enum state { #define STATE(NAME, VALUE) S_##NAME = VALUE, STATES #undef STATE }; static const char * state_name(enum state state) { switch (state) { #define STATE(NAME, VALUE) case S_##NAME: return #NAME; STATES #undef STATE } return "***ERROR***"; } /* A reliable connection to an OpenFlow switch or controller. * * See the large comment in rconn.h for more information. */ struct rconn { struct ovs_mutex mutex; enum state state; time_t state_entered; struct vconn *vconn; char *name; /* Human-readable descriptive name. */ char *target; /* vconn name, passed to vconn_open(). */ bool reliable; struct ovs_list txq; /* Contains "struct ofpbuf"s. */ int backoff; int max_backoff; time_t backoff_deadline; time_t last_connected; time_t last_disconnected; unsigned int seqno; int last_error; /* In S_ACTIVE and S_IDLE, probably_admitted reports whether we believe * that the peer has made a (positive) admission control decision on our * connection. If we have not yet been (probably) admitted, then the * connection does not reset the timer used for deciding whether the switch * should go into fail-open mode. * * last_admitted reports the last time we believe such a positive admission * control decision was made. */ bool probably_admitted; time_t last_admitted; /* These values are simply for statistics reporting, not used directly by * anything internal to the rconn (or ofproto for that matter). */ unsigned int n_attempted_connections, n_successful_connections; time_t creation_time; unsigned long int total_time_connected; /* Throughout this file, "probe" is shorthand for "inactivity probe". When * no activity has been observed from the peer for a while, we send out an * echo request as an inactivity probe packet. We should receive back a * response. * * "Activity" is defined as either receiving an OpenFlow message from the * peer or successfully sending a message that had been in 'txq'. */ int probe_interval; /* Secs of inactivity before sending probe. */ time_t last_activity; /* Last time we saw some activity. */ uint8_t dscp; /* Messages sent or received are copied to the monitor connections. */ #define MAXIMUM_MONITORS 8 struct vconn *monitors[MAXIMUM_MONITORS]; size_t n_monitors; uint32_t allowed_versions; /* Acceptable OpenFlow versions. */ int version; /* Current or most recent version. */ }; uint32_t rconn_get_allowed_versions(const struct rconn *rconn) { return rconn->allowed_versions; } static unsigned int elapsed_in_this_state(const struct rconn *rc) OVS_REQUIRES(rc->mutex); static unsigned int timeout(const struct rconn *rc) OVS_REQUIRES(rc->mutex); static bool timed_out(const struct rconn *rc) OVS_REQUIRES(rc->mutex); static void state_transition(struct rconn *rc, enum state) OVS_REQUIRES(rc->mutex); static void rconn_set_target__(struct rconn *rc, const char *target, const char *name) OVS_REQUIRES(rc->mutex); static int rconn_send__(struct rconn *rc, struct ofpbuf *, struct rconn_packet_counter *) OVS_REQUIRES(rc->mutex); static int try_send(struct rconn *rc) OVS_REQUIRES(rc->mutex); static void reconnect(struct rconn *rc) OVS_REQUIRES(rc->mutex); static void report_error(struct rconn *rc, int error) OVS_REQUIRES(rc->mutex); static void rconn_disconnect__(struct rconn *rc) OVS_REQUIRES(rc->mutex); static void disconnect(struct rconn *rc, int error) OVS_REQUIRES(rc->mutex); static void flush_queue(struct rconn *rc) OVS_REQUIRES(rc->mutex); static void close_monitor(struct rconn *rc, size_t idx, int retval) OVS_REQUIRES(rc->mutex); static void copy_to_monitor(struct rconn *, const struct ofpbuf *); static bool is_connected_state(enum state); static bool is_admitted_msg(const struct ofpbuf *); static bool rconn_logging_connection_attempts__(const struct rconn *rc) OVS_REQUIRES(rc->mutex); /* The following prototypes duplicate those in rconn.h, but there we weren't * able to add the OVS_EXCLUDED annotations because the definition of struct * rconn was not visible. */ void rconn_set_max_backoff(struct rconn *rc, int max_backoff) OVS_EXCLUDED(rc->mutex); void rconn_connect(struct rconn *rc, const char *target, const char *name) OVS_EXCLUDED(rc->mutex); void rconn_connect_unreliably(struct rconn *rc, struct vconn *vconn, const char *name) OVS_EXCLUDED(rc->mutex); void rconn_reconnect(struct rconn *rc) OVS_EXCLUDED(rc->mutex); void rconn_disconnect(struct rconn *rc) OVS_EXCLUDED(rc->mutex); void rconn_run(struct rconn *rc) OVS_EXCLUDED(rc->mutex); void rconn_run_wait(struct rconn *rc) OVS_EXCLUDED(rc->mutex); struct ofpbuf *rconn_recv(struct rconn *rc) OVS_EXCLUDED(rc->mutex); void rconn_recv_wait(struct rconn *rc) OVS_EXCLUDED(rc->mutex); int rconn_send(struct rconn *rc, struct ofpbuf *b, struct rconn_packet_counter *counter) OVS_EXCLUDED(rc->mutex); int rconn_send_with_limit(struct rconn *rc, struct ofpbuf *b, struct rconn_packet_counter *counter, int queue_limit) OVS_EXCLUDED(rc->mutex); void rconn_add_monitor(struct rconn *rc, struct vconn *vconn) OVS_EXCLUDED(rc->mutex); void rconn_set_name(struct rconn *rc, const char *new_name) OVS_EXCLUDED(rc->mutex); bool rconn_is_admitted(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex); int rconn_failure_duration(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex); ovs_be16 rconn_get_local_port(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex); int rconn_get_version(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex); unsigned int rconn_count_txqlen(const struct rconn *rc) OVS_EXCLUDED(rc->mutex); /* Creates and returns a new rconn. * * 'probe_interval' is a number of seconds. If the interval passes once * without an OpenFlow message being received from the peer, the rconn sends * out an "echo request" message. If the interval passes again without a * message being received, the rconn disconnects and re-connects to the peer. * Setting 'probe_interval' to 0 disables this behavior. * * 'max_backoff' is the maximum number of seconds between attempts to connect * to the peer. The actual interval starts at 1 second and doubles on each * failure until it reaches 'max_backoff'. If 0 is specified, the default of * 8 seconds is used. * * The new rconn is initially unconnected. Use rconn_connect() or * rconn_connect_unreliably() to connect it. * * Connections made by the rconn will automatically negotiate an OpenFlow * protocol version acceptable to both peers on the connection. The version * negotiated will be one of those in the 'allowed_versions' bitmap: version * 'x' is allowed if allowed_versions & (1 << x) is nonzero. (The underlying * vconn will treat an 'allowed_versions' of 0 as OFPUTIL_DEFAULT_VERSIONS.) */ struct rconn * rconn_create(int probe_interval, int max_backoff, uint8_t dscp, uint32_t allowed_versions) { struct rconn *rc = xzalloc(sizeof *rc); ovs_mutex_init(&rc->mutex); rc->state = S_VOID; rc->state_entered = time_now(); rc->vconn = NULL; rc->name = xstrdup("void"); rc->target = xstrdup("void"); rc->reliable = false; list_init(&rc->txq); rc->backoff = 0; rc->max_backoff = max_backoff ? max_backoff : 8; rc->backoff_deadline = TIME_MIN; rc->last_connected = TIME_MIN; rc->last_disconnected = TIME_MIN; rc->seqno = 0; rc->probably_admitted = false; rc->last_admitted = time_now(); rc->n_attempted_connections = 0; rc->n_successful_connections = 0; rc->creation_time = time_now(); rc->total_time_connected = 0; rc->last_activity = time_now(); rconn_set_probe_interval(rc, probe_interval); rconn_set_dscp(rc, dscp); rc->n_monitors = 0; rc->allowed_versions = allowed_versions; rc->version = -1; return rc; } void rconn_set_max_backoff(struct rconn *rc, int max_backoff) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); rc->max_backoff = MAX(1, max_backoff); if (rc->state == S_BACKOFF && rc->backoff > max_backoff) { rc->backoff = max_backoff; if (rc->backoff_deadline > time_now() + max_backoff) { rc->backoff_deadline = time_now() + max_backoff; } } ovs_mutex_unlock(&rc->mutex); } int rconn_get_max_backoff(const struct rconn *rc) { return rc->max_backoff; } void rconn_set_dscp(struct rconn *rc, uint8_t dscp) { rc->dscp = dscp; } uint8_t rconn_get_dscp(const struct rconn *rc) { return rc->dscp; } void rconn_set_probe_interval(struct rconn *rc, int probe_interval) { rc->probe_interval = probe_interval ? MAX(5, probe_interval) : 0; } int rconn_get_probe_interval(const struct rconn *rc) { return rc->probe_interval; } /* Drops any existing connection on 'rc', then sets up 'rc' to connect to * 'target' and reconnect as needed. 'target' should be a remote OpenFlow * target in a form acceptable to vconn_open(). * * If 'name' is nonnull, then it is used in log messages in place of 'target'. * It should presumably give more information to a human reader than 'target', * but it need not be acceptable to vconn_open(). */ void rconn_connect(struct rconn *rc, const char *target, const char *name) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); rconn_disconnect__(rc); rconn_set_target__(rc, target, name); rc->reliable = true; reconnect(rc); ovs_mutex_unlock(&rc->mutex); } /* Drops any existing connection on 'rc', then configures 'rc' to use * 'vconn'. If the connection on 'vconn' drops, 'rc' will not reconnect on it * own. * * By default, the target obtained from vconn_get_name(vconn) is used in log * messages. If 'name' is nonnull, then it is used instead. It should * presumably give more information to a human reader than the target, but it * need not be acceptable to vconn_open(). */ void rconn_connect_unreliably(struct rconn *rc, struct vconn *vconn, const char *name) OVS_EXCLUDED(rc->mutex) { ovs_assert(vconn != NULL); ovs_mutex_lock(&rc->mutex); rconn_disconnect__(rc); rconn_set_target__(rc, vconn_get_name(vconn), name); rc->reliable = false; rc->vconn = vconn; state_transition(rc, S_CONNECTING); ovs_mutex_unlock(&rc->mutex); } /* If 'rc' is connected, forces it to drop the connection and reconnect. */ void rconn_reconnect(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); if (rc->state & (S_ACTIVE | S_IDLE)) { VLOG_INFO("%s: disconnecting", rc->name); disconnect(rc, 0); } ovs_mutex_unlock(&rc->mutex); } static void rconn_disconnect__(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (rc->state != S_VOID) { if (rc->vconn) { vconn_close(rc->vconn); rc->vconn = NULL; } rconn_set_target__(rc, "void", NULL); rc->reliable = false; rc->backoff = 0; rc->backoff_deadline = TIME_MIN; state_transition(rc, S_VOID); } } void rconn_disconnect(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); rconn_disconnect__(rc); ovs_mutex_unlock(&rc->mutex); } /* Disconnects 'rc' and frees the underlying storage. */ void rconn_destroy(struct rconn *rc) { if (rc) { size_t i; ovs_mutex_lock(&rc->mutex); free(rc->name); free(rc->target); vconn_close(rc->vconn); flush_queue(rc); ofpbuf_list_delete(&rc->txq); for (i = 0; i < rc->n_monitors; i++) { vconn_close(rc->monitors[i]); } ovs_mutex_unlock(&rc->mutex); ovs_mutex_destroy(&rc->mutex); free(rc); } } static unsigned int timeout_VOID(const struct rconn *rc OVS_UNUSED) OVS_REQUIRES(rc->mutex) { return UINT_MAX; } static void run_VOID(struct rconn *rc OVS_UNUSED) OVS_REQUIRES(rc->mutex) { /* Nothing to do. */ } static void reconnect(struct rconn *rc) OVS_REQUIRES(rc->mutex) { int retval; if (rconn_logging_connection_attempts__(rc)) { VLOG_INFO("%s: connecting...", rc->name); } rc->n_attempted_connections++; retval = vconn_open(rc->target, rc->allowed_versions, rc->dscp, &rc->vconn); if (!retval) { rc->backoff_deadline = time_now() + rc->backoff; state_transition(rc, S_CONNECTING); } else { VLOG_WARN("%s: connection failed (%s)", rc->name, ovs_strerror(retval)); rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */ disconnect(rc, retval); } } static unsigned int timeout_BACKOFF(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return rc->backoff; } static void run_BACKOFF(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (timed_out(rc)) { reconnect(rc); } } static unsigned int timeout_CONNECTING(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return MAX(2, rc->backoff); } static void run_CONNECTING(struct rconn *rc) OVS_REQUIRES(rc->mutex) { int retval = vconn_connect(rc->vconn); if (!retval) { VLOG(rc->reliable ? VLL_INFO : VLL_DBG, "%s: connected", rc->name); rc->n_successful_connections++; state_transition(rc, S_ACTIVE); rc->version = vconn_get_version(rc->vconn); rc->last_connected = rc->state_entered; } else if (retval != EAGAIN) { if (rconn_logging_connection_attempts__(rc)) { VLOG_INFO("%s: connection failed (%s)", rc->name, ovs_strerror(retval)); } disconnect(rc, retval); } else if (timed_out(rc)) { if (rconn_logging_connection_attempts__(rc)) { VLOG_INFO("%s: connection timed out", rc->name); } rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */ disconnect(rc, ETIMEDOUT); } } static void do_tx_work(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (list_is_empty(&rc->txq)) { return; } while (!list_is_empty(&rc->txq)) { int error = try_send(rc); if (error) { break; } rc->last_activity = time_now(); } if (list_is_empty(&rc->txq)) { poll_immediate_wake(); } } static unsigned int timeout_ACTIVE(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (rc->probe_interval) { unsigned int base = MAX(rc->last_activity, rc->state_entered); unsigned int arg = base + rc->probe_interval - rc->state_entered; return arg; } return UINT_MAX; } static void run_ACTIVE(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (timed_out(rc)) { unsigned int base = MAX(rc->last_activity, rc->state_entered); VLOG_DBG("%s: idle %u seconds, sending inactivity probe", rc->name, (unsigned int) (time_now() - base)); /* Ordering is important here: rconn_send() can transition to BACKOFF, * and we don't want to transition back to IDLE if so, because then we * can end up queuing a packet with vconn == NULL and then *boom*. */ state_transition(rc, S_IDLE); /* Send an echo request. */ rconn_send__(rc, make_echo_request(rc->version), NULL); return; } do_tx_work(rc); } static unsigned int timeout_IDLE(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return rc->probe_interval; } static void run_IDLE(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (timed_out(rc)) { VLOG_ERR("%s: no response to inactivity probe after %u " "seconds, disconnecting", rc->name, elapsed_in_this_state(rc)); disconnect(rc, ETIMEDOUT); } else { do_tx_work(rc); } } static unsigned int timeout_DISCONNECTED(const struct rconn *rc OVS_UNUSED) OVS_REQUIRES(rc->mutex) { return UINT_MAX; } static void run_DISCONNECTED(struct rconn *rc OVS_UNUSED) OVS_REQUIRES(rc->mutex) { /* Nothing to do. */ } /* Performs whatever activities are necessary to maintain 'rc': if 'rc' is * disconnected, attempts to (re)connect, backing off as necessary; if 'rc' is * connected, attempts to send packets in the send queue, if any. */ void rconn_run(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { int old_state; size_t i; ovs_mutex_lock(&rc->mutex); if (rc->vconn) { int error; vconn_run(rc->vconn); error = vconn_get_status(rc->vconn); if (error) { report_error(rc, error); disconnect(rc, error); } } for (i = 0; i < rc->n_monitors; ) { struct ofpbuf *msg; int retval; vconn_run(rc->monitors[i]); /* Drain any stray message that came in on the monitor connection. */ retval = vconn_recv(rc->monitors[i], &msg); if (!retval) { ofpbuf_delete(msg); } else if (retval != EAGAIN) { close_monitor(rc, i, retval); continue; } i++; } do { old_state = rc->state; switch (rc->state) { #define STATE(NAME, VALUE) case S_##NAME: run_##NAME(rc); break; STATES #undef STATE default: OVS_NOT_REACHED(); } } while (rc->state != old_state); ovs_mutex_unlock(&rc->mutex); } /* Causes the next call to poll_block() to wake up when rconn_run() should be * called on 'rc'. */ void rconn_run_wait(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { unsigned int timeo; size_t i; ovs_mutex_lock(&rc->mutex); if (rc->vconn) { vconn_run_wait(rc->vconn); if ((rc->state & (S_ACTIVE | S_IDLE)) && !list_is_empty(&rc->txq)) { vconn_wait(rc->vconn, WAIT_SEND); } } for (i = 0; i < rc->n_monitors; i++) { vconn_run_wait(rc->monitors[i]); vconn_recv_wait(rc->monitors[i]); } timeo = timeout(rc); if (timeo != UINT_MAX) { long long int expires = sat_add(rc->state_entered, timeo); poll_timer_wait_until(expires * 1000); } ovs_mutex_unlock(&rc->mutex); } /* Attempts to receive a packet from 'rc'. If successful, returns the packet; * otherwise, returns a null pointer. The caller is responsible for freeing * the packet (with ofpbuf_delete()). */ struct ofpbuf * rconn_recv(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { struct ofpbuf *buffer = NULL; ovs_mutex_lock(&rc->mutex); if (rc->state & (S_ACTIVE | S_IDLE)) { int error = vconn_recv(rc->vconn, &buffer); if (!error) { copy_to_monitor(rc, buffer); if (rc->probably_admitted || is_admitted_msg(buffer) || time_now() - rc->last_connected >= 30) { rc->probably_admitted = true; rc->last_admitted = time_now(); } rc->last_activity = time_now(); if (rc->state == S_IDLE) { state_transition(rc, S_ACTIVE); } } else if (error != EAGAIN) { report_error(rc, error); disconnect(rc, error); } } ovs_mutex_unlock(&rc->mutex); return buffer; } /* Causes the next call to poll_block() to wake up when a packet may be ready * to be received by vconn_recv() on 'rc'. */ void rconn_recv_wait(struct rconn *rc) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); if (rc->vconn) { vconn_wait(rc->vconn, WAIT_RECV); } ovs_mutex_unlock(&rc->mutex); } static int rconn_send__(struct rconn *rc, struct ofpbuf *b, struct rconn_packet_counter *counter) OVS_REQUIRES(rc->mutex) { if (rconn_is_connected(rc)) { COVERAGE_INC(rconn_queued); copy_to_monitor(rc, b); if (counter) { rconn_packet_counter_inc(counter, b->size); } /* Reuse 'frame' as a private pointer while 'b' is in txq. */ b->header = counter; list_push_back(&rc->txq, &b->list_node); /* If the queue was empty before we added 'b', try to send some * packets. (But if the queue had packets in it, it's because the * vconn is backlogged and there's no point in stuffing more into it * now. We'll get back to that in rconn_run().) */ if (rc->txq.next == &b->list_node) { try_send(rc); } return 0; } else { ofpbuf_delete(b); return ENOTCONN; } } /* Sends 'b' on 'rc'. Returns 0 if successful, or ENOTCONN if 'rc' is not * currently connected. Takes ownership of 'b'. * * If 'counter' is non-null, then 'counter' will be incremented while the * packet is in flight, then decremented when it has been sent (or discarded * due to disconnection). Because 'b' may be sent (or discarded) before this * function returns, the caller may not be able to observe any change in * 'counter'. * * There is no rconn_send_wait() function: an rconn has a send queue that it * takes care of sending if you call rconn_run(), which will have the side * effect of waking up poll_block(). */ int rconn_send(struct rconn *rc, struct ofpbuf *b, struct rconn_packet_counter *counter) OVS_EXCLUDED(rc->mutex) { int error; ovs_mutex_lock(&rc->mutex); error = rconn_send__(rc, b, counter); ovs_mutex_unlock(&rc->mutex); return error; } /* Sends 'b' on 'rc'. Increments 'counter' while the packet is in flight; it * will be decremented when it has been sent (or discarded due to * disconnection). Returns 0 if successful, EAGAIN if 'counter->n' is already * at least as large as 'queue_limit', or ENOTCONN if 'rc' is not currently * connected. Regardless of return value, 'b' is destroyed. * * Because 'b' may be sent (or discarded) before this function returns, the * caller may not be able to observe any change in 'counter'. * * There is no rconn_send_wait() function: an rconn has a send queue that it * takes care of sending if you call rconn_run(), which will have the side * effect of waking up poll_block(). */ int rconn_send_with_limit(struct rconn *rc, struct ofpbuf *b, struct rconn_packet_counter *counter, int queue_limit) OVS_EXCLUDED(rc->mutex) { int error; ovs_mutex_lock(&rc->mutex); if (rconn_packet_counter_n_packets(counter) < queue_limit) { error = rconn_send__(rc, b, counter); } else { COVERAGE_INC(rconn_overflow); ofpbuf_delete(b); error = EAGAIN; } ovs_mutex_unlock(&rc->mutex); return error; } /* Adds 'vconn' to 'rc' as a monitoring connection, to which all messages sent * and received on 'rconn' will be copied. 'rc' takes ownership of 'vconn'. */ void rconn_add_monitor(struct rconn *rc, struct vconn *vconn) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); if (rc->n_monitors < ARRAY_SIZE(rc->monitors)) { VLOG_INFO("new monitor connection from %s", vconn_get_name(vconn)); rc->monitors[rc->n_monitors++] = vconn; } else { VLOG_DBG("too many monitor connections, discarding %s", vconn_get_name(vconn)); vconn_close(vconn); } ovs_mutex_unlock(&rc->mutex); } /* Returns 'rc''s name. This is a name for human consumption, appropriate for * use in log messages. It is not necessarily a name that may be passed * directly to, e.g., vconn_open(). */ const char * rconn_get_name(const struct rconn *rc) { return rc->name; } /* Sets 'rc''s name to 'new_name'. */ void rconn_set_name(struct rconn *rc, const char *new_name) OVS_EXCLUDED(rc->mutex) { ovs_mutex_lock(&rc->mutex); free(rc->name); rc->name = xstrdup(new_name); ovs_mutex_unlock(&rc->mutex); } /* Returns 'rc''s target. This is intended to be a string that may be passed * directly to, e.g., vconn_open(). */ const char * rconn_get_target(const struct rconn *rc) { return rc->target; } /* Returns true if 'rconn' is connected or in the process of reconnecting, * false if 'rconn' is disconnected and will not reconnect on its own. */ bool rconn_is_alive(const struct rconn *rconn) { return rconn->state != S_VOID && rconn->state != S_DISCONNECTED; } /* Returns true if 'rconn' is connected, false otherwise. */ bool rconn_is_connected(const struct rconn *rconn) { return is_connected_state(rconn->state); } static bool rconn_is_admitted__(const struct rconn *rconn) OVS_REQUIRES(rconn->mutex) { return (rconn_is_connected(rconn) && rconn->last_admitted >= rconn->last_connected); } /* Returns true if 'rconn' is connected and thought to have been accepted by * the peer's admission-control policy. */ bool rconn_is_admitted(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex) { bool admitted; ovs_mutex_lock(&rconn->mutex); admitted = rconn_is_admitted__(rconn); ovs_mutex_unlock(&rconn->mutex); return admitted; } /* Returns 0 if 'rconn' is currently connected and considered to have been * accepted by the peer's admission-control policy, otherwise the number of * seconds since 'rconn' was last in such a state. */ int rconn_failure_duration(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex) { int duration; ovs_mutex_lock(&rconn->mutex); duration = (rconn_is_admitted__(rconn) ? 0 : time_now() - rconn->last_admitted); ovs_mutex_unlock(&rconn->mutex); return duration; } /* Returns the OpenFlow version most recently negotiated with a peer, or -1 if * no version has ever been negotiated. * * If 'rconn' is connected (that is, if 'rconn_is_connected(rconn)' would * return true), then the return value is guaranteed to be the OpenFlow version * in use for the connection. The converse is not true: when the return value * is not -1, 'rconn' might be disconnected. */ int rconn_get_version(const struct rconn *rconn) OVS_EXCLUDED(rconn->mutex) { ovs_mutex_lock(&rconn->mutex); int version = rconn->version; ovs_mutex_unlock(&rconn->mutex); return version; } /* Returns a string representing the internal state of 'rc'. The caller must * not modify or free the string. */ const char * rconn_get_state(const struct rconn *rc) { return state_name(rc->state); } /* Returns the time at which the last successful connection was made by * 'rc'. Returns TIME_MIN if never connected. */ time_t rconn_get_last_connection(const struct rconn *rc) { return rc->last_connected; } /* Returns the time at which 'rc' was last disconnected. Returns TIME_MIN * if never disconnected. */ time_t rconn_get_last_disconnect(const struct rconn *rc) { return rc->last_disconnected; } /* Returns 'rc''s current connection sequence number, a number that changes * every time that 'rconn' connects or disconnects. */ unsigned int rconn_get_connection_seqno(const struct rconn *rc) { return rc->seqno; } /* Returns a value that explains why 'rc' last disconnected: * * - 0 means that the last disconnection was caused by a call to * rconn_disconnect(), or that 'rc' is new and has not yet completed its * initial connection or connection attempt. * * - EOF means that the connection was closed in the normal way by the peer. * * - A positive integer is an errno value that represents the error. */ int rconn_get_last_error(const struct rconn *rc) { return rc->last_error; } /* Returns the number of messages queued for transmission on 'rc'. */ unsigned int rconn_count_txqlen(const struct rconn *rc) OVS_EXCLUDED(rc->mutex) { unsigned int len; ovs_mutex_lock(&rc->mutex); len = list_size(&rc->txq); ovs_mutex_unlock(&rc->mutex); return len; } struct rconn_packet_counter * rconn_packet_counter_create(void) { struct rconn_packet_counter *c = xzalloc(sizeof *c); ovs_mutex_init(&c->mutex); ovs_mutex_lock(&c->mutex); c->ref_cnt = 1; ovs_mutex_unlock(&c->mutex); return c; } void rconn_packet_counter_destroy(struct rconn_packet_counter *c) { if (c) { bool dead; ovs_mutex_lock(&c->mutex); ovs_assert(c->ref_cnt > 0); dead = !--c->ref_cnt && !c->n_packets; ovs_mutex_unlock(&c->mutex); if (dead) { ovs_mutex_destroy(&c->mutex); free(c); } } } void rconn_packet_counter_inc(struct rconn_packet_counter *c, unsigned int n_bytes) { ovs_mutex_lock(&c->mutex); c->n_packets++; c->n_bytes += n_bytes; ovs_mutex_unlock(&c->mutex); } void rconn_packet_counter_dec(struct rconn_packet_counter *c, unsigned int n_bytes) { bool dead = false; ovs_mutex_lock(&c->mutex); ovs_assert(c->n_packets > 0); ovs_assert(c->n_packets == 1 ? c->n_bytes == n_bytes : c->n_bytes > n_bytes); c->n_packets--; c->n_bytes -= n_bytes; dead = !c->n_packets && !c->ref_cnt; ovs_mutex_unlock(&c->mutex); if (dead) { ovs_mutex_destroy(&c->mutex); free(c); } } unsigned int rconn_packet_counter_n_packets(const struct rconn_packet_counter *c) { unsigned int n; ovs_mutex_lock(&c->mutex); n = c->n_packets; ovs_mutex_unlock(&c->mutex); return n; } unsigned int rconn_packet_counter_n_bytes(const struct rconn_packet_counter *c) { unsigned int n; ovs_mutex_lock(&c->mutex); n = c->n_bytes; ovs_mutex_unlock(&c->mutex); return n; } /* Set rc->target and rc->name to 'target' and 'name', respectively. If 'name' * is null, 'target' is used. */ static void rconn_set_target__(struct rconn *rc, const char *target, const char *name) OVS_REQUIRES(rc->mutex) { free(rc->name); rc->name = xstrdup(name ? name : target); free(rc->target); rc->target = xstrdup(target); } /* Tries to send a packet from 'rc''s send buffer. Returns 0 if successful, * otherwise a positive errno value. */ static int try_send(struct rconn *rc) OVS_REQUIRES(rc->mutex) { struct ofpbuf *msg = ofpbuf_from_list(rc->txq.next); unsigned int n_bytes = msg->size; struct rconn_packet_counter *counter = msg->header; int retval; /* Eagerly remove 'msg' from the txq. We can't remove it from the list * after sending, if sending is successful, because it is then owned by the * vconn, which might have freed it already. */ list_remove(&msg->list_node); msg->header = NULL; retval = vconn_send(rc->vconn, msg); if (retval) { msg->header = counter; list_push_front(&rc->txq, &msg->list_node); if (retval != EAGAIN) { report_error(rc, retval); disconnect(rc, retval); } return retval; } COVERAGE_INC(rconn_sent); if (counter) { rconn_packet_counter_dec(counter, n_bytes); } return 0; } /* Reports that 'error' caused 'rc' to disconnect. 'error' may be a positive * errno value, or it may be EOF to indicate that the connection was closed * normally. */ static void report_error(struct rconn *rc, int error) OVS_REQUIRES(rc->mutex) { /* On Windows, when a peer terminates without calling a closesocket() * on socket fd, we get WSAECONNRESET. Don't print warning messages * for that case. */ if (error == EOF #ifdef _WIN32 || error == WSAECONNRESET #endif ) { /* If 'rc' isn't reliable, then we don't really expect this connection * to last forever anyway (probably it's a connection that we received * via accept()), so use DBG level to avoid cluttering the logs. */ enum vlog_level level = rc->reliable ? VLL_INFO : VLL_DBG; VLOG(level, "%s: connection closed by peer", rc->name); } else { VLOG_WARN("%s: connection dropped (%s)", rc->name, ovs_strerror(error)); } } /* Disconnects 'rc' and records 'error' as the error that caused 'rc''s last * disconnection: * * - 0 means that this disconnection is due to a request by 'rc''s client, * not due to any kind of network error. * * - EOF means that the connection was closed in the normal way by the peer. * * - A positive integer is an errno value that represents the error. */ static void disconnect(struct rconn *rc, int error) OVS_REQUIRES(rc->mutex) { rc->last_error = error; if (rc->vconn) { vconn_close(rc->vconn); rc->vconn = NULL; } if (rc->reliable) { time_t now = time_now(); if (rc->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) { rc->last_disconnected = now; flush_queue(rc); } if (now >= rc->backoff_deadline) { rc->backoff = 1; } else if (rc->backoff < rc->max_backoff / 2) { rc->backoff = MAX(1, 2 * rc->backoff); VLOG_INFO("%s: waiting %d seconds before reconnect", rc->name, rc->backoff); } else { if (rconn_logging_connection_attempts__(rc)) { VLOG_INFO("%s: continuing to retry connections in the " "background but suppressing further logging", rc->name); } rc->backoff = rc->max_backoff; } rc->backoff_deadline = now + rc->backoff; state_transition(rc, S_BACKOFF); } else { rc->last_disconnected = time_now(); state_transition(rc, S_DISCONNECTED); } } /* Drops all the packets from 'rc''s send queue and decrements their queue * counts. */ static void flush_queue(struct rconn *rc) OVS_REQUIRES(rc->mutex) { if (list_is_empty(&rc->txq)) { return; } while (!list_is_empty(&rc->txq)) { struct ofpbuf *b = ofpbuf_from_list(list_pop_front(&rc->txq)); struct rconn_packet_counter *counter = b->header; if (counter) { rconn_packet_counter_dec(counter, b->size); } COVERAGE_INC(rconn_discarded); ofpbuf_delete(b); } poll_immediate_wake(); } static unsigned int elapsed_in_this_state(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return time_now() - rc->state_entered; } static unsigned int timeout(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { switch (rc->state) { #define STATE(NAME, VALUE) case S_##NAME: return timeout_##NAME(rc); STATES #undef STATE default: OVS_NOT_REACHED(); } } static bool timed_out(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return time_now() >= sat_add(rc->state_entered, timeout(rc)); } static void state_transition(struct rconn *rc, enum state state) OVS_REQUIRES(rc->mutex) { rc->seqno += is_connected_state(rc->state) != is_connected_state(state); if (is_connected_state(state) && !is_connected_state(rc->state)) { rc->probably_admitted = false; } if (rconn_is_connected(rc)) { rc->total_time_connected += elapsed_in_this_state(rc); } VLOG_DBG("%s: entering %s", rc->name, state_name(state)); rc->state = state; rc->state_entered = time_now(); } static void close_monitor(struct rconn *rc, size_t idx, int retval) OVS_REQUIRES(rc->mutex) { VLOG_DBG("%s: closing monitor connection to %s: %s", rconn_get_name(rc), vconn_get_name(rc->monitors[idx]), ovs_retval_to_string(retval)); rc->monitors[idx] = rc->monitors[--rc->n_monitors]; } static void copy_to_monitor(struct rconn *rc, const struct ofpbuf *b) OVS_REQUIRES(rc->mutex) { struct ofpbuf *clone = NULL; int retval; size_t i; for (i = 0; i < rc->n_monitors; ) { struct vconn *vconn = rc->monitors[i]; if (!clone) { clone = ofpbuf_clone(b); } retval = vconn_send(vconn, clone); if (!retval) { clone = NULL; } else if (retval != EAGAIN) { close_monitor(rc, i, retval); continue; } i++; } ofpbuf_delete(clone); } static bool is_connected_state(enum state state) { return (state & (S_ACTIVE | S_IDLE)) != 0; } /* When a switch initially connects to a controller, the controller may spend a * little time examining the switch, looking at, for example, its datapath ID, * before it decides whether it is willing to control that switch. At that * point, it either disconnects or starts controlling the switch. * * This function returns a guess to its caller about whether 'b' is OpenFlow * message that indicates that the controller has decided to control the * switch. It returns false if the message is one that a controller typically * uses to determine whether a switch is admissible, true if the message is one * that would typically be used only after the controller has admitted the * switch. */ static bool is_admitted_msg(const struct ofpbuf *b) { enum ofptype type; enum ofperr error; error = ofptype_decode(&type, b->data); if (error) { return false; } switch (type) { case OFPTYPE_HELLO: case OFPTYPE_ERROR: case OFPTYPE_ECHO_REQUEST: case OFPTYPE_ECHO_REPLY: case OFPTYPE_FEATURES_REQUEST: case OFPTYPE_FEATURES_REPLY: case OFPTYPE_GET_CONFIG_REQUEST: case OFPTYPE_GET_CONFIG_REPLY: case OFPTYPE_SET_CONFIG: case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: case OFPTYPE_QUEUE_GET_CONFIG_REPLY: case OFPTYPE_GET_ASYNC_REQUEST: case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_GROUP_STATS_REQUEST: case OFPTYPE_GROUP_STATS_REPLY: case OFPTYPE_GROUP_DESC_STATS_REQUEST: case OFPTYPE_GROUP_DESC_STATS_REPLY: case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: case OFPTYPE_GROUP_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_DESC_REQUEST: case OFPTYPE_TABLE_DESC_REPLY: return false; case OFPTYPE_PACKET_IN: case OFPTYPE_FLOW_REMOVED: case OFPTYPE_PORT_STATUS: case OFPTYPE_PACKET_OUT: case OFPTYPE_FLOW_MOD: case OFPTYPE_GROUP_MOD: case OFPTYPE_PORT_MOD: case OFPTYPE_TABLE_MOD: case OFPTYPE_METER_MOD: case OFPTYPE_BARRIER_REQUEST: case OFPTYPE_BARRIER_REPLY: case OFPTYPE_DESC_STATS_REQUEST: case OFPTYPE_DESC_STATS_REPLY: case OFPTYPE_FLOW_STATS_REQUEST: case OFPTYPE_FLOW_STATS_REPLY: case OFPTYPE_AGGREGATE_STATS_REQUEST: case OFPTYPE_AGGREGATE_STATS_REPLY: case OFPTYPE_TABLE_STATS_REQUEST: case OFPTYPE_TABLE_STATS_REPLY: case OFPTYPE_PORT_STATS_REQUEST: case OFPTYPE_PORT_STATS_REPLY: case OFPTYPE_QUEUE_STATS_REQUEST: case OFPTYPE_QUEUE_STATS_REPLY: case OFPTYPE_PORT_DESC_STATS_REQUEST: case OFPTYPE_PORT_DESC_STATS_REPLY: case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_STATS_REPLY: case OFPTYPE_METER_CONFIG_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REPLY: case OFPTYPE_METER_FEATURES_STATS_REQUEST: case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_ROLE_REQUEST: case OFPTYPE_ROLE_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: case OFPTYPE_SET_FLOW_FORMAT: case OFPTYPE_FLOW_MOD_TABLE_ID: case OFPTYPE_SET_PACKET_IN_FORMAT: case OFPTYPE_FLOW_AGE: case OFPTYPE_SET_ASYNC_CONFIG: case OFPTYPE_SET_CONTROLLER_ID: case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: case OFPTYPE_FLOW_MONITOR_STATS_REPLY: case OFPTYPE_FLOW_MONITOR_CANCEL: case OFPTYPE_FLOW_MONITOR_PAUSED: case OFPTYPE_FLOW_MONITOR_RESUMED: case OFPTYPE_BUNDLE_CONTROL: case OFPTYPE_BUNDLE_ADD_MESSAGE: case OFPTYPE_NXT_TLV_TABLE_MOD: case OFPTYPE_NXT_TLV_TABLE_REQUEST: case OFPTYPE_NXT_TLV_TABLE_REPLY: default: return true; } } /* Returns true if 'rc' is currently logging information about connection * attempts, false if logging should be suppressed because 'rc' hasn't * successuflly connected in too long. */ static bool rconn_logging_connection_attempts__(const struct rconn *rc) OVS_REQUIRES(rc->mutex) { return rc->backoff < rc->max_backoff; } openvswitch-2.5.9/lib/PaxHeaders.82075/latch.h0000644000000000000000000000013113534540071015655 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.749853191 openvswitch-2.5.9/lib/latch.h0000644000175000017500000000241513534540071017346 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LATCH_H #define LATCH_H 1 /* A thread-safe, signal-safe, pollable doorbell. * * This is a thin wrapper around a pipe that allows threads to notify each * other that an event has occurred in a signal-safe way */ #include #include "util.h" struct latch { #ifndef _WIN32 int fds[2]; #else HANDLE wevent; bool is_set; #endif }; void latch_init(struct latch *); void latch_destroy(struct latch *); bool latch_poll(struct latch *); void latch_set(struct latch *); bool latch_is_set(const struct latch *); void latch_wait_at(const struct latch *, const char *where); #define latch_wait(latch) latch_wait_at(latch, OVS_SOURCE_LOCATOR) #endif /* latch.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/hash.h0000644000000000000000000000013213534540071015506 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.729853043 openvswitch-2.5.9/lib/hash.h0000644000175000017500000002343213534540071017200 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HASH_H #define HASH_H 1 #include #include #include #include #include "util.h" #ifdef __cplusplus extern "C" { #endif static inline uint32_t hash_rot(uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } uint32_t hash_bytes(const void *, size_t n_bytes, uint32_t basis); /* The hash input must be a word larger than 128 bits. */ void hash_bytes128(const void *_, size_t n_bytes, uint32_t basis, ovs_u128 *out); static inline uint32_t hash_int(uint32_t x, uint32_t basis); static inline uint32_t hash_2words(uint32_t, uint32_t); static inline uint32_t hash_uint64(const uint64_t); static inline uint32_t hash_uint64_basis(const uint64_t x, const uint32_t basis); uint32_t hash_3words(uint32_t, uint32_t, uint32_t); static inline uint32_t hash_boolean(bool x, uint32_t basis); uint32_t hash_double(double, uint32_t basis); static inline uint32_t hash_pointer(const void *, uint32_t basis); static inline uint32_t hash_string(const char *, uint32_t basis); /* Murmurhash by Austin Appleby, * from http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp. * * The upstream license there says: * * // MurmurHash3 was written by Austin Appleby, and is placed in the public * // domain. The author hereby disclaims copyright to this source code. * * See hash_words() for sample usage. */ static inline uint32_t mhash_add__(uint32_t hash, uint32_t data) { data *= 0xcc9e2d51; data = hash_rot(data, 15); data *= 0x1b873593; return hash ^ data; } static inline uint32_t mhash_add(uint32_t hash, uint32_t data) { hash = mhash_add__(hash, data); hash = hash_rot(hash, 13); return hash * 5 + 0xe6546b64; } static inline uint32_t mhash_finish(uint32_t hash) { hash ^= hash >> 16; hash *= 0x85ebca6b; hash ^= hash >> 13; hash *= 0xc2b2ae35; hash ^= hash >> 16; return hash; } #if !(defined(__SSE4_2__) && defined(__x86_64__)) /* Mhash-based implementation. */ static inline uint32_t hash_add(uint32_t hash, uint32_t data) { return mhash_add(hash, data); } static inline uint32_t hash_add64(uint32_t hash, uint64_t data) { return hash_add(hash_add(hash, data), data >> 32); } static inline uint32_t hash_finish(uint32_t hash, uint32_t final) { return mhash_finish(hash ^ final); } /* Returns the hash of the 'n' 32-bit words at 'p', starting from 'basis'. * 'p' must be properly aligned. * * This is inlined for the compiler to have access to the 'n_words', which * in many cases is a constant. */ static inline uint32_t hash_words_inline(const uint32_t p[], size_t n_words, uint32_t basis) { uint32_t hash; size_t i; hash = basis; for (i = 0; i < n_words; i++) { hash = hash_add(hash, p[i]); } return hash_finish(hash, n_words * 4); } static inline uint32_t hash_words64_inline(const uint64_t p[], size_t n_words, uint32_t basis) { uint32_t hash; size_t i; hash = basis; for (i = 0; i < n_words; i++) { hash = hash_add64(hash, p[i]); } return hash_finish(hash, n_words * 8); } static inline uint32_t hash_pointer(const void *p, uint32_t basis) { /* Often pointers are hashed simply by casting to integer type, but that * has pitfalls since the lower bits of a pointer are often all 0 for * alignment reasons. It's hard to guess where the entropy really is, so * we give up here and just use a high-quality hash function. * * The double cast suppresses a warning on 64-bit systems about casting to * an integer to different size. That's OK in this case, since most of the * entropy in the pointer is almost certainly in the lower 32 bits. */ return hash_int((uint32_t) (uintptr_t) p, basis); } static inline uint32_t hash_2words(uint32_t x, uint32_t y) { return hash_finish(hash_add(hash_add(x, 0), y), 8); } static inline uint32_t hash_uint64_basis(const uint64_t x, const uint32_t basis) { return hash_finish(hash_add64(basis, x), 8); } static inline uint32_t hash_uint64(const uint64_t x) { return hash_uint64_basis(x, 0); } #else /* __SSE4_2__ && __x86_64__ */ #include static inline uint32_t hash_add(uint32_t hash, uint32_t data) { return _mm_crc32_u32(hash, data); } /* Add the halves of 'data' in the memory order. */ static inline uint32_t hash_add64(uint32_t hash, uint64_t data) { return _mm_crc32_u64(hash, data); } static inline uint32_t hash_finish(uint64_t hash, uint64_t final) { /* The finishing multiplier 0x805204f3 has been experimentally * derived to pass the testsuite hash tests. */ hash = _mm_crc32_u64(hash, final) * 0x805204f3; return hash ^ (uint32_t)hash >> 16; /* Increase entropy in LSBs. */ } /* Returns the hash of the 'n' 32-bit words at 'p_', starting from 'basis'. * We access 'p_' as a uint64_t pointer, which is fine for __SSE_4_2__. * * This is inlined for the compiler to have access to the 'n_words', which * in many cases is a constant. */ static inline uint32_t hash_words_inline(const uint32_t p_[], size_t n_words, uint32_t basis) { const uint64_t *p = (const void *)p_; uint64_t hash1 = basis; uint64_t hash2 = 0; uint64_t hash3 = n_words; const uint32_t *endp = (const uint32_t *)p + n_words; const uint64_t *limit = p + n_words / 2 - 3; while (p <= limit) { hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); hash3 = _mm_crc32_u64(hash3, p[2]); p += 3; } switch (endp - (const uint32_t *)p) { case 1: hash1 = _mm_crc32_u32(hash1, *(const uint32_t *)&p[0]); break; case 2: hash1 = _mm_crc32_u64(hash1, p[0]); break; case 3: hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u32(hash2, *(const uint32_t *)&p[1]); break; case 4: hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); break; case 5: hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); hash3 = _mm_crc32_u32(hash3, *(const uint32_t *)&p[2]); break; } return hash_finish(hash1, hash2 << 32 | hash3); } /* A simpler version for 64-bit data. * 'n_words' is the count of 64-bit words, basis is 64 bits. */ static inline uint32_t hash_words64_inline(const uint64_t p[], size_t n_words, uint32_t basis) { uint64_t hash1 = basis; uint64_t hash2 = 0; uint64_t hash3 = n_words; const uint64_t *endp = p + n_words; const uint64_t *limit = endp - 3; while (p <= limit) { hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); hash3 = _mm_crc32_u64(hash3, p[2]); p += 3; } switch (endp - p) { case 1: hash1 = _mm_crc32_u64(hash1, p[0]); break; case 2: hash1 = _mm_crc32_u64(hash1, p[0]); hash2 = _mm_crc32_u64(hash2, p[1]); break; } return hash_finish(hash1, hash2 << 32 | hash3); } static inline uint32_t hash_uint64_basis(const uint64_t x, const uint32_t basis) { /* '23' chosen to mix bits enough for the test-hash to pass. */ return hash_finish(hash_add64(basis, x), 23); } static inline uint32_t hash_uint64(const uint64_t x) { return hash_uint64_basis(x, 0); } static inline uint32_t hash_2words(uint32_t x, uint32_t y) { return hash_uint64((uint64_t)y << 32 | x); } static inline uint32_t hash_pointer(const void *p, uint32_t basis) { return hash_uint64_basis((uint64_t) (uintptr_t) p, basis); } #endif uint32_t hash_words__(const uint32_t p[], size_t n_words, uint32_t basis); uint32_t hash_words64__(const uint64_t p[], size_t n_words, uint32_t basis); /* Inline the larger hash functions only when 'n_words' is known to be * compile-time constant. */ #if __GNUC__ >= 4 static inline uint32_t hash_words(const uint32_t p[], size_t n_words, uint32_t basis) { if (__builtin_constant_p(n_words)) { return hash_words_inline(p, n_words, basis); } else { return hash_words__(p, n_words, basis); } } static inline uint32_t hash_words64(const uint64_t p[], size_t n_words, uint32_t basis) { if (__builtin_constant_p(n_words)) { return hash_words64_inline(p, n_words, basis); } else { return hash_words64__(p, n_words, basis); } } #else static inline uint32_t hash_words(const uint32_t p[], size_t n_words, uint32_t basis) { return hash_words__(p, n_words, basis); } static inline uint32_t hash_words64(const uint64_t p[], size_t n_words, uint32_t basis) { return hash_words64__(p, n_words, basis); } #endif static inline uint32_t hash_string(const char *s, uint32_t basis) { return hash_bytes(s, strlen(s), basis); } static inline uint32_t hash_int(uint32_t x, uint32_t basis) { return hash_2words(x, basis); } /* An attempt at a useful 1-bit hash function. Has not been analyzed for * quality. */ static inline uint32_t hash_boolean(bool x, uint32_t basis) { const uint32_t P0 = 0xc2b73583; /* This is hash_int(1, 0). */ const uint32_t P1 = 0xe90f1258; /* This is hash_int(2, 0). */ return (x ? P0 : P1) ^ hash_rot(basis, 1); } #ifdef __cplusplus } #endif #endif /* hash.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-lldp.h0000644000000000000000000000013113534540071016322 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 29 ctime=1567801424.82585375 openvswitch-2.5.9/lib/ovs-lldp.h0000644000175000017500000000655013534540071020017 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2014 Wind River Systems, Inc. * Copyright (c) 2015 Avaya, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_LLDP_H #define OVS_LLDP_H #include #include "dp-packet.h" #include "hmap.h" #include "list.h" #include "lldp/lldpd.h" #include "ovs-atomic.h" #include "packets.h" #include "timer.h" /* Transmit every LLDPD_TX_INTERVAL seconds. */ #define LLDP_DEFAULT_TRANSMIT_INTERVAL_MS (LLDPD_TX_INTERVAL * 1000) struct flow; struct netdev; struct smap; /* Structure per LLDP instance (at the moment per port when enabled). */ struct lldp { struct hmap_node hmap_node; /* Node in all_lldps list. */ struct lldpd *lldpd; char *name; /* Name of the port. */ struct timer tx_timer; /* Send LLDP when expired. */ struct hmap mappings_by_isid; /* "struct" indexed by ISID */ struct hmap mappings_by_aux; /* "struct" indexed by aux */ struct ovs_list active_mapping_queue; struct ovs_refcount ref_cnt; bool enabled; /* LLDP enabled on port */ }; /* Configuration specific to Auto Attach. */ struct aa_settings { char *system_description; char *system_name; }; /* Configuration of Auto Attach mappings. */ struct aa_mapping_settings { uint32_t isid; uint16_t vlan; }; enum bridge_aa_vlan_oper { BRIDGE_AA_VLAN_OPER_UNDEF, BRIDGE_AA_VLAN_OPER_ADD, BRIDGE_AA_VLAN_OPER_REMOVE }; /* Bridge Auto Attach operations. Mostly for adding/removing VLAN on * the trunk port connected to the Auto Attach server. */ struct bridge_aa_vlan { struct ovs_list list_node; char *port_name; uint16_t vlan; enum bridge_aa_vlan_oper oper; }; void lldp_init(void); long long int lldp_wait(struct lldp *lldp); long long int lldp_wake_time(const struct lldp *lldp); void lldp_run(struct lldpd *cfg); bool lldp_should_send_packet(struct lldp *cfg); bool lldp_should_process_flow(struct lldp *lldp, const struct flow *flow); bool lldp_configure(struct lldp *lldp, const struct smap *cfg); void lldp_process_packet(struct lldp *cfg, const struct dp_packet *); void lldp_put_packet(struct lldp *lldp, struct dp_packet *packet, const struct eth_addr eth_src); void lldpd_assign_cfg_to_protocols(struct lldpd *cfg); struct lldp * lldp_create(const struct netdev *netdev, const uint32_t mtu, const struct smap *cfg); struct lldp * lldp_ref(const struct lldp *lldp_); void lldp_unref(struct lldp *lldp); int aa_get_vlan_queued(struct ovs_list *list); unsigned int aa_get_vlan_queue_size(void); int aa_configure(const struct aa_settings *s); int aa_mapping_register(void *aux, const struct aa_mapping_settings *s); int aa_mapping_unregister(void *aux); /* Used by unit tests */ struct lldp * lldp_create_dummy(void); #endif /* OVS_LLDP_H */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-provider.h0000644000000000000000000000013213534540071017700 xustar0030 mtime=1567801401.457681581 30 atime=1567801402.081686164 30 ctime=1567801424.773853367 openvswitch-2.5.9/lib/netdev-provider.h0000644000175000017500000010261213534540071021370 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETDEV_PROVIDER_H #define NETDEV_PROVIDER_H 1 /* Generic interface to network devices. */ #include "connectivity.h" #include "netdev.h" #include "list.h" #include "ovs-numa.h" #include "packets.h" #include "seq.h" #include "shash.h" #include "smap.h" #ifdef __cplusplus extern "C" { #endif #define NETDEV_NUMA_UNSPEC OVS_NUMA_UNSPEC /* A network device (e.g. an Ethernet device). * * Network device implementations may read these members but should not modify * them. */ struct netdev { /* The following do not change during the lifetime of a struct netdev. */ char *name; /* Name of network device. */ const struct netdev_class *netdev_class; /* Functions to control this device. */ /* A sequence number which indicates changes in one of 'netdev''s * properties. It must be nonzero so that users have a value which * they may use as a reset when tracking 'netdev'. * * Minimally, the sequence number is required to change whenever * 'netdev''s flags, features, ethernet address, or carrier changes. */ uint64_t change_seq; /* The following are protected by 'netdev_mutex' (internal to netdev.c). */ int n_txq; int n_rxq; int ref_cnt; /* Times this devices was opened. */ struct shash_node *node; /* Pointer to element in global map. */ struct ovs_list saved_flags_list; /* Contains "struct netdev_saved_flags". */ }; static void netdev_change_seq_changed(const struct netdev *netdev_) { struct netdev *netdev = CONST_CAST(struct netdev *, netdev_); seq_change(connectivity_seq_get()); netdev->change_seq++; if (!netdev->change_seq) { netdev->change_seq++; } } const char *netdev_get_type(const struct netdev *); const struct netdev_class *netdev_get_class(const struct netdev *); const char *netdev_get_name(const struct netdev *); struct netdev *netdev_from_name(const char *name); void netdev_get_devices(const struct netdev_class *, struct shash *device_list); struct netdev **netdev_get_vports(size_t *size); /* A data structure for capturing packets received by a network device. * * Network device implementations may read these members but should not modify * them. * * None of these members change during the lifetime of a struct netdev_rxq. */ struct netdev_rxq { struct netdev *netdev; /* Owns a reference to the netdev. */ int queue_id; }; struct netdev *netdev_rxq_get_netdev(const struct netdev_rxq *); /* Network device class structure, to be defined by each implementation of a * network device. * * These functions return 0 if successful or a positive errno value on failure, * except where otherwise noted. * * * Data Structures * =============== * * These functions work primarily with two different kinds of data structures: * * - "struct netdev", which represents a network device. * * - "struct netdev_rxq", which represents a handle for capturing packets * received on a network device * * Each of these data structures contains all of the implementation-independent * generic state for the respective concept, called the "base" state. None of * them contains any extra space for implementations to use. Instead, each * implementation is expected to declare its own data structure that contains * an instance of the generic data structure plus additional * implementation-specific members, called the "derived" state. The * implementation can use casts or (preferably) the CONTAINER_OF macro to * obtain access to derived state given only a pointer to the embedded generic * data structure. * * * Life Cycle * ========== * * Four stylized functions accompany each of these data structures: * * "alloc" "construct" "destruct" "dealloc" * ------------ ---------------- --------------- -------------- * netdev ->alloc ->construct ->destruct ->dealloc * netdev_rxq ->rxq_alloc ->rxq_construct ->rxq_destruct ->rxq_dealloc * * Any instance of a given data structure goes through the following life * cycle: * * 1. The client calls the "alloc" function to obtain raw memory. If "alloc" * fails, skip all the other steps. * * 2. The client initializes all of the data structure's base state. If this * fails, skip to step 7. * * 3. The client calls the "construct" function. The implementation * initializes derived state. It may refer to the already-initialized * base state. If "construct" fails, skip to step 6. * * 4. The data structure is now initialized and in use. * * 5. When the data structure is no longer needed, the client calls the * "destruct" function. The implementation uninitializes derived state. * The base state has not been uninitialized yet, so the implementation * may still refer to it. * * 6. The client uninitializes all of the data structure's base state. * * 7. The client calls the "dealloc" to free the raw memory. The * implementation must not refer to base or derived state in the data * structure, because it has already been uninitialized. * * If netdev support multi-queue IO then netdev->construct should set initialize * netdev->n_rxq to number of queues. * * Each "alloc" function allocates and returns a new instance of the respective * data structure. The "alloc" function is not given any information about the * use of the new data structure, so it cannot perform much initialization. * Its purpose is just to ensure that the new data structure has enough room * for base and derived state. It may return a null pointer if memory is not * available, in which case none of the other functions is called. * * Each "construct" function initializes derived state in its respective data * structure. When "construct" is called, all of the base state has already * been initialized, so the "construct" function may refer to it. The * "construct" function is allowed to fail, in which case the client calls the * "dealloc" function (but not the "destruct" function). * * Each "destruct" function uninitializes and frees derived state in its * respective data structure. When "destruct" is called, the base state has * not yet been uninitialized, so the "destruct" function may refer to it. The * "destruct" function is not allowed to fail. * * Each "dealloc" function frees raw memory that was allocated by the * "alloc" function. The memory's base and derived members might not have ever * been initialized (but if "construct" returned successfully, then it has been * "destruct"ed already). The "dealloc" function is not allowed to fail. * * * Device Change Notification * ========================== * * Minimally, implementations are required to report changes to netdev flags, * features, ethernet address or carrier through connectivity_seq. Changes to * other properties are allowed to cause notification through this interface, * although implementations should try to avoid this. connectivity_seq_get() * can be used to acquire a reference to the struct seq. The interface is * described in detail in seq.h. */ struct netdev_class { /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc. * * One of the providers should supply a "system" type, since this is * the type assumed if no type is specified when opening a netdev. * The "system" type corresponds to an existing network device on * the system. */ const char *type; /* ## ------------------- ## */ /* ## Top-Level Functions ## */ /* ## ------------------- ## */ /* Called when the netdev provider is registered, typically at program * startup. Returning an error from this function will prevent any network * device in this class from being opened. * * This function may be set to null if a network device class needs no * initialization at registration time. */ int (*init)(void); /* Performs periodic work needed by netdevs of this class. May be null if * no periodic work is necessary. */ void (*run)(void); /* Arranges for poll_block() to wake up if the "run" member function needs * to be called. Implementations are additionally required to wake * whenever something changes in any of its netdevs which would cause their * ->change_seq() function to change its result. May be null if nothing is * needed here. */ void (*wait)(void); /* ## ---------------- ## */ /* ## netdev Functions ## */ /* ## ---------------- ## */ /* Life-cycle functions for a netdev. See the large comment above on * struct netdev_class. */ struct netdev *(*alloc)(void); int (*construct)(struct netdev *); void (*destruct)(struct netdev *); void (*dealloc)(struct netdev *); /* Fetches the device 'netdev''s configuration, storing it in 'args'. * The caller owns 'args' and pre-initializes it to an empty smap. * * If this netdev class does not have any configuration options, this may * be a null pointer. */ int (*get_config)(const struct netdev *netdev, struct smap *args); /* Changes the device 'netdev''s configuration to 'args'. * * If this netdev class does not support configuration, this may be a null * pointer. */ int (*set_config)(struct netdev *netdev, const struct smap *args); /* Returns the tunnel configuration of 'netdev'. If 'netdev' is * not a tunnel, returns null. * * If this function would always return null, it may be null instead. */ const struct netdev_tunnel_config * (*get_tunnel_config)(const struct netdev *netdev); /* Build Partial Tunnel header. Ethernet and ip header is already built, * build_header() is suppose build protocol specific part of header. */ int (*build_header)(const struct netdev *, struct ovs_action_push_tnl *data, const struct flow *tnl_flow); /* build_header() can not build entire header for all packets for given * flow. Push header is called for packet to build header specific to * a packet on actual transmit. It uses partial header build by * build_header() which is passed as data. */ void (*push_header)(struct dp_packet *packet, const struct ovs_action_push_tnl *data); /* Pop tunnel header from packet, build tunnel metadata and resize packet * for further processing. */ int (*pop_header)(struct dp_packet *packet); /* Returns the id of the numa node the 'netdev' is on. If there is no * such info, returns NETDEV_NUMA_UNSPEC. */ int (*get_numa_id)(const struct netdev *netdev); /* Configures the number of tx queues and rx queues of 'netdev'. * Return 0 if successful, otherwise a positive errno value. * * 'n_rxq' specifies the maximum number of receive queues to create. * The netdev provider might choose to create less (e.g. if the hardware * supports only a smaller number). The actual number of queues created * is stored in the 'netdev->n_rxq' field. * * 'n_txq' specifies the exact number of transmission queues to create. * The caller will call netdev_send() concurrently from 'n_txq' different * threads (with different qid). The netdev provider is responsible for * making sure that these concurrent calls do not create a race condition * by using multiple hw queues or locking. * * On error, the tx queue and rx queue configuration is indeterminant. * Caller should make decision on whether to restore the previous or * the default configuration. Also, caller must make sure there is no * other thread accessing the queues at the same time. */ int (*set_multiq)(struct netdev *netdev, unsigned int n_txq, unsigned int n_rxq); /* Sends buffers on 'netdev'. * Returns 0 if successful (for every buffer), otherwise a positive errno * value. Returns EAGAIN without blocking if one or more packets cannot be * queued immediately. Returns EMSGSIZE if a partial packet was transmitted * or if a packet is too big or too small to transmit on the device. * * If the function returns a non-zero value, some of the packets might have * been sent anyway. * * To retain ownership of 'buffers' caller can set may_steal to false. * * The network device is expected to maintain one or more packet * transmission queues, so that the caller does not ordinarily have to * do additional queuing of packets. 'qid' specifies the queue to use * and can be ignored if the implementation does not support multiple * queues. * * May return EOPNOTSUPP if a network device does not implement packet * transmission through this interface. This function may be set to null * if it would always return EOPNOTSUPP anyhow. (This will prevent the * network device from being usefully used by the netdev-based "userspace * datapath". It will also prevent the OVS implementation of bonding from * working properly over 'netdev'.) */ int (*send)(struct netdev *netdev, int qid, struct dp_packet **buffers, int cnt, bool may_steal); /* Registers with the poll loop to wake up from the next call to * poll_block() when the packet transmission queue for 'netdev' has * sufficient room to transmit a packet with netdev_send(). * * The network device is expected to maintain one or more packet * transmission queues, so that the caller does not ordinarily have to * do additional queuing of packets. 'qid' specifies the queue to use * and can be ignored if the implementation does not support multiple * queues. * * May be null if not needed, such as for a network device that does not * implement packet transmission through the 'send' member function. */ void (*send_wait)(struct netdev *netdev, int qid); /* Sets 'netdev''s Ethernet address to 'mac' */ int (*set_etheraddr)(struct netdev *netdev, const struct eth_addr mac); /* Retrieves 'netdev''s Ethernet address into 'mac'. * * This address will be advertised as 'netdev''s MAC address through the * OpenFlow protocol, among other uses. */ int (*get_etheraddr)(const struct netdev *netdev, struct eth_addr *mac); /* Retrieves 'netdev''s MTU into '*mtup'. * * The MTU is the maximum size of transmitted (and received) packets, in * bytes, not including the hardware header; thus, this is typically 1500 * bytes for Ethernet devices. * * If 'netdev' does not have an MTU (e.g. as some tunnels do not), then * this function should return EOPNOTSUPP. This function may be set to * null if it would always return EOPNOTSUPP. */ int (*get_mtu)(const struct netdev *netdev, int *mtup); /* Sets 'netdev''s MTU to 'mtu'. * * If 'netdev' does not have an MTU (e.g. as some tunnels do not), then * this function should return EOPNOTSUPP. This function may be set to * null if it would always return EOPNOTSUPP. */ int (*set_mtu)(const struct netdev *netdev, int mtu); /* Returns the ifindex of 'netdev', if successful, as a positive number. * On failure, returns a negative errno value. * * The desired semantics of the ifindex value are a combination of those * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An * ifindex value should be unique within a host and remain stable at least * until reboot. SNMP says an ifindex "ranges between 1 and the value of * ifNumber" but many systems do not follow this rule anyhow. * * This function may be set to null if it would always return -EOPNOTSUPP. */ int (*get_ifindex)(const struct netdev *netdev); /* Sets 'carrier' to true if carrier is active (link light is on) on * 'netdev'. * * May be null if device does not provide carrier status (will be always * up as long as device is up). */ int (*get_carrier)(const struct netdev *netdev, bool *carrier); /* Returns the number of times 'netdev''s carrier has changed since being * initialized. * * If null, callers will assume the number of carrier resets is zero. */ long long int (*get_carrier_resets)(const struct netdev *netdev); /* Forces ->get_carrier() to poll 'netdev''s MII registers for link status * instead of checking 'netdev''s carrier. 'netdev''s MII registers will * be polled once every 'interval' milliseconds. If 'netdev' does not * support MII, another method may be used as a fallback. If 'interval' is * less than or equal to zero, reverts ->get_carrier() to its normal * behavior. * * Most network devices won't support this feature and will set this * function pointer to NULL, which is equivalent to returning EOPNOTSUPP. */ int (*set_miimon_interval)(struct netdev *netdev, long long int interval); /* Retrieves current device stats for 'netdev' into 'stats'. * * A network device that supports some statistics but not others, it should * set the values of the unsupported statistics to all-1-bits * (UINT64_MAX). */ int (*get_stats)(const struct netdev *netdev, struct netdev_stats *); /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer'. Each value is a bitmap of * NETDEV_F_* bits. * * This function may be set to null if it would always return EOPNOTSUPP. */ int (*get_features)(const struct netdev *netdev, enum netdev_features *current, enum netdev_features *advertised, enum netdev_features *supported, enum netdev_features *peer); /* Set the features advertised by 'netdev' to 'advertise', which is a * set of NETDEV_F_* bits. * * This function may be set to null for a network device that does not * support configuring advertisements. */ int (*set_advertisements)(struct netdev *netdev, enum netdev_features advertise); /* Attempts to set input rate limiting (policing) policy, such that up to * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative * burst size of 'kbits' kb. * * This function may be set to null if policing is not supported. */ int (*set_policing)(struct netdev *netdev, unsigned int kbits_rate, unsigned int kbits_burst); /* Adds to 'types' all of the forms of QoS supported by 'netdev', or leaves * it empty if 'netdev' does not support QoS. Any names added to 'types' * should be documented as valid for the "type" column in the "QoS" table * in vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * Every network device must support disabling QoS with a type of "", but * this function must not add "" to 'types'. * * The caller is responsible for initializing 'types' (e.g. with * sset_init()) before calling this function. The caller retains ownership * of 'types'. * * May be NULL if 'netdev' does not support QoS at all. */ int (*get_qos_types)(const struct netdev *netdev, struct sset *types); /* Queries 'netdev' for its capabilities regarding the specified 'type' of * QoS. On success, initializes 'caps' with the QoS capabilities. * * Should return EOPNOTSUPP if 'netdev' does not support 'type'. May be * NULL if 'netdev' does not support QoS at all. */ int (*get_qos_capabilities)(const struct netdev *netdev, const char *type, struct netdev_qos_capabilities *caps); /* Queries 'netdev' about its currently configured form of QoS. If * successful, stores the name of the current form of QoS into '*typep' * and any details of configuration as string key-value pairs in * 'details'. * * A '*typep' of "" indicates that QoS is currently disabled on 'netdev'. * * The caller initializes 'details' before calling this function. The * caller takes ownership of the string key-values pairs added to * 'details'. * * The netdev retains ownership of '*typep'. * * '*typep' will be one of the types returned by netdev_get_qos_types() for * 'netdev'. The contents of 'details' should be documented as valid for * '*typep' in the "other_config" column in the "QoS" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * May be NULL if 'netdev' does not support QoS at all. */ int (*get_qos)(const struct netdev *netdev, const char **typep, struct smap *details); /* Attempts to reconfigure QoS on 'netdev', changing the form of QoS to * 'type' with details of configuration from 'details'. * * On error, the previous QoS configuration is retained. * * When this function changes the type of QoS (not just 'details'), this * also resets all queue configuration for 'netdev' to their defaults * (which depend on the specific type of QoS). Otherwise, the queue * configuration for 'netdev' is unchanged. * * 'type' should be "" (to disable QoS) or one of the types returned by * netdev_get_qos_types() for 'netdev'. The contents of 'details' should * be documented as valid for the given 'type' in the "other_config" column * in the "QoS" table in vswitchd/vswitch.xml (which is built as * ovs-vswitchd.conf.db(8)). * * May be NULL if 'netdev' does not support QoS at all. */ int (*set_qos)(struct netdev *netdev, const char *type, const struct smap *details); /* Queries 'netdev' for information about the queue numbered 'queue_id'. * If successful, adds that information as string key-value pairs to * 'details'. Returns 0 if successful, otherwise a positive errno value. * * Should return EINVAL if 'queue_id' is greater than or equal to the * number of supported queues (as reported in the 'n_queues' member of * struct netdev_qos_capabilities by 'get_qos_capabilities'). * * The caller initializes 'details' before calling this function. The * caller takes ownership of the string key-values pairs added to * 'details'. * * The returned contents of 'details' should be documented as valid for the * given 'type' in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). */ int (*get_queue)(const struct netdev *netdev, unsigned int queue_id, struct smap *details); /* Configures the queue numbered 'queue_id' on 'netdev' with the key-value * string pairs in 'details'. The contents of 'details' should be * documented as valid for the given 'type' in the "other_config" column in * the "Queue" table in vswitchd/vswitch.xml (which is built as * ovs-vswitchd.conf.db(8)). Returns 0 if successful, otherwise a positive * errno value. On failure, the given queue's configuration should be * unmodified. * * Should return EINVAL if 'queue_id' is greater than or equal to the * number of supported queues (as reported in the 'n_queues' member of * struct netdev_qos_capabilities by 'get_qos_capabilities'), or if * 'details' is invalid for the type of queue. * * This function does not modify 'details', and the caller retains * ownership of it. * * May be NULL if 'netdev' does not support QoS at all. */ int (*set_queue)(struct netdev *netdev, unsigned int queue_id, const struct smap *details); /* Attempts to delete the queue numbered 'queue_id' from 'netdev'. * * Should return EINVAL if 'queue_id' is greater than or equal to the * number of supported queues (as reported in the 'n_queues' member of * struct netdev_qos_capabilities by 'get_qos_capabilities'). Should * return EOPNOTSUPP if 'queue_id' is valid but may not be deleted (e.g. if * 'netdev' has a fixed set of queues with the current QoS mode). * * May be NULL if 'netdev' does not support QoS at all, or if all of its * QoS modes have fixed sets of queues. */ int (*delete_queue)(struct netdev *netdev, unsigned int queue_id); /* Obtains statistics about 'queue_id' on 'netdev'. Fills 'stats' with the * queue's statistics. May set individual members of 'stats' to all-1-bits * if the statistic is unavailable. * * May be NULL if 'netdev' does not support QoS at all. */ int (*get_queue_stats)(const struct netdev *netdev, unsigned int queue_id, struct netdev_queue_stats *stats); /* Attempts to begin dumping the queues in 'netdev'. On success, returns 0 * and initializes '*statep' with any data needed for iteration. On * failure, returns a positive errno value. * * May be NULL if 'netdev' does not support QoS at all. */ int (*queue_dump_start)(const struct netdev *netdev, void **statep); /* Attempts to retrieve another queue from 'netdev' for 'state', which was * initialized by a successful call to the 'queue_dump_start' function for * 'netdev'. On success, stores a queue ID into '*queue_id' and fills * 'details' with the configuration of the queue with that ID. Returns EOF * if the last queue has been dumped, or a positive errno value on error. * This function will not be called again once it returns nonzero once for * a given iteration (but the 'queue_dump_done' function will be called * afterward). * * The caller initializes and clears 'details' before calling this * function. The caller takes ownership of the string key-values pairs * added to 'details'. * * The returned contents of 'details' should be documented as valid for the * given 'type' in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * May be NULL if 'netdev' does not support QoS at all. */ int (*queue_dump_next)(const struct netdev *netdev, void *state, unsigned int *queue_id, struct smap *details); /* Releases resources from 'netdev' for 'state', which was initialized by a * successful call to the 'queue_dump_start' function for 'netdev'. * * May be NULL if 'netdev' does not support QoS at all. */ int (*queue_dump_done)(const struct netdev *netdev, void *state); /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's * ID, its statistics, and the 'aux' specified by the caller. The order of * iteration is unspecified, but (when successful) each queue must be * visited exactly once. * * 'cb' will not modify or free the statistics passed in. */ int (*dump_queue_stats)(const struct netdev *netdev, void (*cb)(unsigned int queue_id, struct netdev_queue_stats *, void *aux), void *aux); /* If 'netdev' has an assigned IPv4 address, sets '*address' to that * address and '*netmask' to the associated netmask. * * The following error values have well-defined meanings: * * - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address. * * - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*get_in4)(const struct netdev *netdev, struct in_addr *address, struct in_addr *netmask); /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*set_in4)(struct netdev *netdev, struct in_addr addr, struct in_addr mask); /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address. * * The following error values have well-defined meanings: * * - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address. * * - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*get_in6)(const struct netdev *netdev, struct in6_addr *in6); /* Adds 'router' as a default IP gateway for the TCP/IP stack that * corresponds to 'netdev'. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*add_router)(struct netdev *netdev, struct in_addr router); /* Looks up the next hop for 'host' in the host's routing table. If * successful, stores the next hop gateway's address (0 if 'host' is on a * directly connected network) in '*next_hop' and a copy of the name of the * device to reach 'host' in '*netdev_name', and returns 0. The caller is * responsible for freeing '*netdev_name' (by calling free()). * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*get_next_hop)(const struct in_addr *host, struct in_addr *next_hop, char **netdev_name); /* Retrieves driver information of the device. * * Populates 'smap' with key-value pairs representing the status of the * device. 'smap' is a set of key-value string pairs representing netdev * type specific information. For more information see * ovs-vswitchd.conf.db(5). * * The caller is responsible for destroying 'smap' and its data. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*get_status)(const struct netdev *netdev, struct smap *smap); /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the * corresponding MAC address in 'mac'. A return value of ENXIO, in * particular, indicates that there is no ARP table entry for 'ip' on * 'netdev'. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*arp_lookup)(const struct netdev *netdev, ovs_be32 ip, struct eth_addr *mac); /* Retrieves the current set of flags on 'netdev' into '*old_flags'. Then, * turns off the flags that are set to 1 in 'off' and turns on the flags * that are set to 1 in 'on'. (No bit will be set to 1 in both 'off' and * 'on'; that is, off & on == 0.) * * This function may be invoked from a signal handler. Therefore, it * should not do anything that is not signal-safe (such as logging). */ int (*update_flags)(struct netdev *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flags); /* ## -------------------- ## */ /* ## netdev_rxq Functions ## */ /* ## -------------------- ## */ /* If a particular netdev class does not support receiving packets, all these * function pointers must be NULL. */ /* Life-cycle functions for a netdev_rxq. See the large comment above on * struct netdev_class. */ struct netdev_rxq *(*rxq_alloc)(void); int (*rxq_construct)(struct netdev_rxq *); void (*rxq_destruct)(struct netdev_rxq *); void (*rxq_dealloc)(struct netdev_rxq *); /* Attempts to receive batch of packets from 'rx' and place array of * pointers into '*pkts'. netdev is responsible for allocating buffers. * '*cnt' points to packet count for given batch. Once packets are returned * to caller, netdev should give up ownership of ofpbuf data. * * Implementations should allocate buffer with DP_NETDEV_HEADROOM headroom * and add a VLAN header which is obtained out-of-band to the packet. * * Caller is expected to pass array of size MAX_RX_BATCH. * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet **pkts, int *cnt); /* Registers with the poll loop to wake up from the next call to * poll_block() when a packet is ready to be received with netdev_rxq_recv() * on 'rx'. */ void (*rxq_wait)(struct netdev_rxq *rx); /* Discards all packets waiting to be received from 'rx'. */ int (*rxq_drain)(struct netdev_rxq *rx); }; int netdev_register_provider(const struct netdev_class *); int netdev_unregister_provider(const char *type); #if defined(__FreeBSD__) || defined(__NetBSD__) extern const struct netdev_class netdev_bsd_class; #elif defined(_WIN32) extern const struct netdev_class netdev_windows_class; #else extern const struct netdev_class netdev_linux_class; #endif extern const struct netdev_class netdev_internal_class; extern const struct netdev_class netdev_tap_class; #ifdef __cplusplus } #endif #endif /* netdev.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/bitmap.h0000644000000000000000000000013213534540071016037 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.069686075 30 ctime=1567801424.661852541 openvswitch-2.5.9/lib/bitmap.h0000644000175000017500000001753113534540071017534 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BITMAP_H #define BITMAP_H 1 #include #include #include "util.h" #define BITMAP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) static inline unsigned long * bitmap_unit__(const unsigned long *bitmap, size_t offset) { return CONST_CAST(unsigned long *, &bitmap[offset / BITMAP_ULONG_BITS]); } static inline unsigned long bitmap_bit__(size_t offset) { return 1UL << (offset % BITMAP_ULONG_BITS); } #define BITMAP_N_LONGS(N_BITS) DIV_ROUND_UP(N_BITS, BITMAP_ULONG_BITS) static inline size_t bitmap_n_longs(size_t n_bits) { return BITMAP_N_LONGS(n_bits); } static inline size_t bitmap_n_bytes(size_t n_bits) { return bitmap_n_longs(n_bits) * sizeof(unsigned long int); } static inline unsigned long * bitmap_allocate(size_t n_bits) { return xzalloc(bitmap_n_bytes(n_bits)); } /* Initializes bitmap to all-1-bits and returns the bitmap pointer. */ static inline unsigned long * bitmap_init1(unsigned long *bitmap, size_t n_bits) { size_t n_longs = bitmap_n_longs(n_bits); size_t n_bytes = bitmap_n_bytes(n_bits); size_t r_bits = n_bits % BITMAP_ULONG_BITS; memset(bitmap, 0xff, n_bytes); if (r_bits) { bitmap[n_longs - 1] >>= BITMAP_ULONG_BITS - r_bits; } return bitmap; } /* Allocates and returns a bitmap initialized to all-1-bits. */ static inline unsigned long * bitmap_allocate1(size_t n_bits) { return bitmap_init1(xmalloc(bitmap_n_bytes(n_bits)), n_bits); } static inline unsigned long * bitmap_clone(const unsigned long *bitmap, size_t n_bits) { return xmemdup(bitmap, bitmap_n_bytes(n_bits)); } static inline void bitmap_free(unsigned long *bitmap) { free(bitmap); } static inline bool bitmap_is_set(const unsigned long *bitmap, size_t offset) { return (*bitmap_unit__(bitmap, offset) & bitmap_bit__(offset)) != 0; } static inline unsigned long * bitmap_set1(unsigned long *bitmap, size_t offset) { *bitmap_unit__(bitmap, offset) |= bitmap_bit__(offset); return bitmap; } static inline unsigned long * bitmap_set0(unsigned long *bitmap, size_t offset) { *bitmap_unit__(bitmap, offset) &= ~bitmap_bit__(offset); return bitmap; } static inline unsigned long * bitmap_set(unsigned long *bitmap, size_t offset, bool value) { return (value) ? bitmap_set1(bitmap, offset) : bitmap_set0(bitmap, offset); } /* Sets 'n' bits of a single unit. */ static inline void bitmap_set_n__(unsigned long *bitmap, size_t start, size_t n, bool value) { unsigned long mask = ((1UL << n) - 1) << start % BITMAP_ULONG_BITS; if (value) { *bitmap_unit__(bitmap, start) |= mask; } else { *bitmap_unit__(bitmap, start) &= ~mask; } } /* Sets 'count' consecutive bits in 'bitmap', starting at bit offset 'start', * to 'value'. */ static inline unsigned long * bitmap_set_multiple(unsigned long *bitmap, size_t start, size_t count, bool value) { if (count && start % BITMAP_ULONG_BITS) { size_t n = MIN(count, BITMAP_ULONG_BITS - start % BITMAP_ULONG_BITS); bitmap_set_n__(bitmap, start, n, value); count -= n; start += n; } for (; count >= BITMAP_ULONG_BITS; count -= BITMAP_ULONG_BITS) { *bitmap_unit__(bitmap, start) = (unsigned long)!value - 1; start += BITMAP_ULONG_BITS; } if (count) { bitmap_set_n__(bitmap, start, count, value); } return bitmap; } /* Returns the number of 1-bits in the 'n'-bit bitmap at 'bitmap'. */ static inline size_t bitmap_count1(const unsigned long int *bitmap, size_t n) { size_t i; size_t count = 0; BUILD_ASSERT(ULONG_MAX <= UINT64_MAX); for (i = 0; i < BITMAP_N_LONGS(n); i++) { count += count_1bits(bitmap[i]); } return count; } /* "dst &= arg;" for n-bit dst and arg. */ static inline unsigned long * bitmap_and(unsigned long *dst, const unsigned long *arg, size_t n) { size_t i; for (i = 0; i < BITMAP_N_LONGS(n); i++) { dst[i] &= arg[i]; } return dst; } /* "dst |= arg;" for n-bit dst and arg. */ static inline unsigned long * bitmap_or(unsigned long *dst, const unsigned long *arg, size_t n) { size_t i; for (i = 0; i < BITMAP_N_LONGS(n); i++) { dst[i] |= arg[i]; } return dst; } /* "dst = ~dst;" for n-bit dst. */ static inline unsigned long * bitmap_not(unsigned long *dst, size_t n) { size_t i; for (i = 0; i < n / BITMAP_ULONG_BITS; i++) { dst[i] = ~dst[i]; } if (n % BITMAP_ULONG_BITS) { dst[i] ^= (1UL << (n % BITMAP_ULONG_BITS)) - 1; } return dst; } /* Compares the 'n' bits in bitmaps 'a' and 'b'. Returns true if all bits are * equal, false otherwise. */ static inline bool bitmap_equal(const unsigned long *a, const unsigned long *b, size_t n) { if (memcmp(a, b, n / BITMAP_ULONG_BITS * sizeof(unsigned long))) { return false; } if (n % BITMAP_ULONG_BITS) { unsigned long mask = (1UL << n % BITMAP_ULONG_BITS) - 1; unsigned long diff = *bitmap_unit__(a, n) ^ *bitmap_unit__(b, n); return !(diff & mask); } return true; } /* Scans 'bitmap' from bit offset 'start' to 'end', excluding 'end' itself. * Returns the bit offset of the lowest-numbered bit set to 'target', or 'end' * if all of the bits are set to '!target'. 'target' is typically a * compile-time constant, so it makes sense to inline this. Compiler may also * optimize parts away depending on the 'start' and 'end' values passed in. */ static inline size_t bitmap_scan(const unsigned long *bitmap, bool target, size_t start, size_t end) { if (OVS_LIKELY(start < end)) { unsigned long *p, unit; p = bitmap_unit__(bitmap, start); unit = (target ? *p : ~*p) >> (start % BITMAP_ULONG_BITS); if (!unit) { start -= start % BITMAP_ULONG_BITS; /* Round down. */ start += BITMAP_ULONG_BITS; /* Start of the next unit. */ for (; start < end; start += BITMAP_ULONG_BITS) { unit = target ? *++p : ~*++p; if (unit) { goto found; } } return end; } found: start += raw_ctz(unit); /* unit != 0 */ if (OVS_LIKELY(start < end)) { return start; } } return end; } /* Returns true if all of the 'n' bits in 'bitmap' are 0, * false if at least one bit is a 1.*/ static inline bool bitmap_is_all_zeros(const unsigned long *bitmap, size_t n) { return bitmap_scan(bitmap, true, 0, n) == n; } #define BITMAP_FOR_EACH_1_RANGE(IDX, BEGIN, END, BITMAP) \ for ((IDX) = bitmap_scan(BITMAP, true, BEGIN, END); (IDX) < (END); \ (IDX) = bitmap_scan(BITMAP, true, (IDX) + 1, END)) #define BITMAP_FOR_EACH_1(IDX, SIZE, BITMAP) \ BITMAP_FOR_EACH_1_RANGE(IDX, 0, SIZE, BITMAP) /* More efficient access to a map of single ullong. */ #define ULLONG_FOR_EACH_1(IDX, MAP) \ for (uint64_t map__ = (MAP); \ map__ && (((IDX) = raw_ctz(map__)), true); \ map__ = zero_rightmost_1bit(map__)) #define ULLONG_SET0(MAP, OFFSET) ((MAP) &= ~(1ULL << (OFFSET))) #define ULLONG_SET1(MAP, OFFSET) ((MAP) |= 1ULL << (OFFSET)) /* Returns the value of a bit in a map as a bool. */ #define ULLONG_GET(MAP, OFFSET) !!((MAP) & (1ULL << (OFFSET))) #endif /* bitmap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/db-ctl-base.man0000644000000000000000000000013213534540071017164 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801423.729845671 openvswitch-2.5.9/lib/db-ctl-base.man0000644000175000017500000003162413534540071020660 0ustar00jpettitjpettit00000000000000.ST "Database Values" .PP Each column in the database accepts a fixed type of data. The currently defined basic types, and their representations, are: .IP "integer" A decimal integer in the range \-2**63 to 2**63\-1, inclusive. .IP "real" A floating-point number. .IP "Boolean" True or false, written \fBtrue\fR or \fBfalse\fR, respectively. .IP "string" An arbitrary Unicode string, except that null bytes are not allowed. Quotes are optional for most strings that begin with an English letter or underscore and consist only of letters, underscores, hyphens, and periods. However, \fBtrue\fR and \fBfalse\fR and strings that match the syntax of UUIDs (see below) must be enclosed in double quotes to distinguish them from other basic types. When double quotes are used, the syntax is that of strings in JSON, e.g. backslashes may be used to escape special characters. The empty string must be represented as a pair of double quotes (\fB""\fR). .IP "UUID" Either a universally unique identifier in the style of RFC 4122, e.g. \fBf81d4fae\-7dec\-11d0\-a765\-00a0c91e6bf6\fR, or an \fB@\fIname\fR defined by a \fBget\fR or \fBcreate\fR command within the same \fB\*(PN\fR invocation. .PP Multiple values in a single column may be separated by spaces or a single comma. When multiple values are present, duplicates are not allowed, and order is not important. Conversely, some database columns can have an empty set of values, represented as \fB[]\fR, and square brackets may optionally enclose other non-empty sets or single values as well. .PP A few database columns are ``maps'' of key-value pairs, where the key and the value are each some fixed database type. These are specified in the form \fIkey\fB=\fIvalue\fR, where \fIkey\fR and \fIvalue\fR follow the syntax for the column's key type and value type, respectively. When multiple pairs are present (separated by spaces or a comma), duplicate keys are not allowed, and again the order is not important. Duplicate values are allowed. An empty map is represented as \fB{}\fR. Curly braces may optionally enclose non-empty maps as well (but use quotes to prevent the shell from expanding \fBother-config={0=x,1=y}\fR into \fBother-config=0=x other-config=1=y\fR, which may not have the desired effect). . .ST "Database Command Syntax" . .IP "[\fB\-\-if\-exists\fR] [\fB\-\-columns=\fIcolumn\fR[\fB,\fIcolumn\fR]...] \fBlist \fItable \fR[\fIrecord\fR]..." Lists the data in each specified \fIrecord\fR. If no records are specified, lists all the records in \fItable\fR. .IP If \fB\-\-columns\fR is specified, only the requested columns are listed, in the specified order. Otherwise, all columns are listed, in alphabetical order by column name. .IP Without \fB\-\-if-exists\fR, it is an error if any specified \fIrecord\fR does not exist. With \fB\-\-if-exists\fR, the command ignores any \fIrecord\fR that does not exist, without producing any output. . .IP "[\fB\-\-columns=\fIcolumn\fR[\fB,\fIcolumn\fR]...] \fBfind \fItable \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..." Lists the data in each record in \fItable\fR whose \fIcolumn\fR equals \fIvalue\fR or, if \fIkey\fR is specified, whose \fIcolumn\fR contains a \fIkey\fR with the specified \fIvalue\fR. The following operators may be used where \fB=\fR is written in the syntax summary: .RS .IP "\fB= != < > <= >=\fR" Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] equals, does not equal, is less than, is greater than, is less than or equal to, or is greater than or equal to \fIvalue\fR, respectively. .IP Consider \fIcolumn\fR[\fB:\fIkey\fR] and \fIvalue\fR as sets of elements. Identical sets are considered equal. Otherwise, if the sets have different numbers of elements, then the set with more elements is considered to be larger. Otherwise, consider a element from each set pairwise, in increasing order within each set. The first pair that differs determines the result. (For a column that contains key-value pairs, first all the keys are compared, and values are considered only if the two sets contain identical keys.) .IP "\fB{=} {!=}\fR" Test for set equality or inequality, respectively. .IP "\fB{<=}\fR" Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] is a subset of \fIvalue\fR. For example, \fBflood-vlans{<=}1,2\fR selects records in which the \fBflood-vlans\fR column is the empty set or contains 1 or 2 or both. .IP "\fB{<}\fR" Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] is a proper subset of \fIvalue\fR. For example, \fBflood-vlans{<}1,2\fR selects records in which the \fBflood-vlans\fR column is the empty set or contains 1 or 2 but not both. .IP "\fB{>=} {>}\fR" Same as \fB{<=}\fR and \fB{<}\fR, respectively, except that the relationship is reversed. For example, \fBflood-vlans{>=}1,2\fR selects records in which the \fBflood-vlans\fR column contains both 1 and 2. .RE .IP For arithmetic operators (\fB= != < > <= >=\fR), when \fIkey\fR is specified but a particular record's \fIcolumn\fR does not contain \fIkey\fR, the record is always omitted from the results. Thus, the condition \fBother-config:mtu!=1500\fR matches records that have a \fBmtu\fR key whose value is not 1500, but not those that lack an \fBmtu\fR key. .IP For the set operators, when \fIkey\fR is specified but a particular record's \fIcolumn\fR does not contain \fIkey\fR, the comparison is done against an empty set. Thus, the condition \fBother-config:mtu{!=}1500\fR matches records that have a \fBmtu\fR key whose value is not 1500 and those that lack an \fBmtu\fR key. .IP Don't forget to escape \fB<\fR or \fB>\fR from interpretation by the shell. .IP If \fB\-\-columns\fR is specified, only the requested columns are listed, in the specified order. Otherwise all columns are listed, in alphabetical order by column name. .IP The UUIDs shown for rows created in the same \fB\*(PN\fR invocation will be wrong. . .IP "[\fB\-\-if\-exists\fR] [\fB\-\-id=@\fIname\fR] \fBget \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]]..." Prints the value of each specified \fIcolumn\fR in the given \fIrecord\fR in \fItable\fR. For map columns, a \fIkey\fR may optionally be specified, in which case the value associated with \fIkey\fR in the column is printed, instead of the entire map. .IP Without \fB\-\-if\-exists\fR, it is an error if \fIrecord\fR does not exist or \fIkey\fR is specified, if \fIkey\fR does not exist in \fIrecord\fR. With \fB\-\-if\-exists\fR, a missing \fIrecord\fR yields no output and a missing \fIkey\fR prints a blank line. .IP If \fB@\fIname\fR is specified, then the UUID for \fIrecord\fR may be referred to by that name later in the same \fB\*(PN\fR invocation in contexts where a UUID is expected. .IP Both \fB\-\-id\fR and the \fIcolumn\fR arguments are optional, but usually at least one or the other should be specified. If both are omitted, then \fBget\fR has no effect except to verify that \fIrecord\fR exists in \fItable\fR. .IP \fB\-\-id\fR and \fB\-\-if\-exists\fR cannot be used together. . .IP "[\fB\-\-if\-exists\fR] \fBset \fItable record column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..." Sets the value of each specified \fIcolumn\fR in the given \fIrecord\fR in \fItable\fR to \fIvalue\fR. For map columns, a \fIkey\fR may optionally be specified, in which case the value associated with \fIkey\fR in that column is changed (or added, if none exists), instead of the entire map. .IP Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not exist. With \fB\-\-if-exists\fR, this command does nothing if \fIrecord\fR does not exist. . .IP "[\fB\-\-if\-exists\fR] \fBadd \fItable record column \fR[\fIkey\fB=\fR]\fIvalue\fR..." Adds the specified value or key-value pair to \fIcolumn\fR in \fIrecord\fR in \fItable\fR. If \fIcolumn\fR is a map, then \fIkey\fR is required, otherwise it is prohibited. If \fIkey\fR already exists in a map column, then the current \fIvalue\fR is not replaced (use the \fBset\fR command to replace an existing value). .IP Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not exist. With \fB\-\-if-exists\fR, this command does nothing if \fIrecord\fR does not exist. . .IP "[\fB\-\-if\-exists\fR] \fBremove \fItable record column \fR\fIvalue\fR..." .IQ "[\fB\-\-if\-exists\fR] \fBremove \fItable record column \fR\fIkey\fR..." .IQ "[\fB\-\-if\-exists\fR] \fBremove \fItable record column \fR\fIkey\fB=\fR\fIvalue\fR..." Removes the specified values or key-value pairs from \fIcolumn\fR in \fIrecord\fR in \fItable\fR. The first form applies to columns that are not maps: each specified \fIvalue\fR is removed from the column. The second and third forms apply to map columns: if only a \fIkey\fR is specified, then any key-value pair with the given \fIkey\fR is removed, regardless of its value; if a \fIvalue\fR is given then a pair is removed only if both key and value match. .IP It is not an error if the column does not contain the specified key or value or pair. .IP Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not exist. With \fB\-\-if-exists\fR, this command does nothing if \fIrecord\fR does not exist. . .IP "[\fB\-\-if\-exists\fR] \fBclear\fR \fItable record column\fR..." Sets each \fIcolumn\fR in \fIrecord\fR in \fItable\fR to the empty set or empty map, as appropriate. This command applies only to columns that are allowed to be empty. .IP Without \fB\-\-if-exists\fR, it is an error if \fIrecord\fR does not exist. With \fB\-\-if-exists\fR, this command does nothing if \fIrecord\fR does not exist. . .IP "[\fB\-\-id=@\fIname\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..." Creates a new record in \fItable\fR and sets the initial values of each \fIcolumn\fR. Columns not explicitly set will receive their default values. Outputs the UUID of the new row. .IP If \fB@\fIname\fR is specified, then the UUID for the new row may be referred to by that name elsewhere in the same \fB\*(PN\fR invocation in contexts where a UUID is expected. Such references may precede or follow the \fBcreate\fR command. . .RS .IP "Caution (ovs-vsctl as exmaple)" Records in the Open vSwitch database are significant only when they can be reached directly or indirectly from the \fBOpen_vSwitch\fR table. Except for records in the \fBQoS\fR or \fBQueue\fR tables, records that are not reachable from the \fBOpen_vSwitch\fR table are automatically deleted from the database. This deletion happens immediately, without waiting for additional \fBovs\-vsctl\fR commands or other database activity. Thus, a \fBcreate\fR command must generally be accompanied by additional commands \fIwithin the same \fBovs\-vsctl\fI invocation\fR to add a chain of references to the newly created record from the top-level \fBOpen_vSwitch\fR record. The \fBEXAMPLES\fR section gives some examples that show how to do this. .RE . .IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..." Deletes each specified \fIrecord\fR from \fItable\fR. Unless \fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist. .IP "\fB\-\-all destroy \fItable\fR" Deletes all records from the \fItable\fR. . .RS .IP "Caution (ovs-vsctl as exmaple)" The \fBdestroy\fR command is only useful for records in the \fBQoS\fR or \fBQueue\fR tables. Records in other tables are automatically deleted from the database when they become unreachable from the \fBOpen_vSwitch\fR table. This means that deleting the last reference to a record is sufficient for deleting the record itself. For records in these tables, \fBdestroy\fR is silently ignored. See the \fBEXAMPLES\fR section below for more information. .RE . .IP "\fBwait\-until \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..." Waits until \fItable\fR contains a record named \fIrecord\fR whose \fIcolumn\fR equals \fIvalue\fR or, if \fIkey\fR is specified, whose \fIcolumn\fR contains a \fIkey\fR with the specified \fIvalue\fR. Any of the operators \fB!=\fR, \fB<\fR, \fB>\fR, \fB<=\fR, or \fB>=\fR may be substituted for \fB=\fR to test for inequality, less than, greater than, less than or equal to, or greater than or equal to, respectively. (Don't forget to escape \fB<\fR or \fB>\fR from interpretation by the shell.) .IP If no \fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR arguments are given, this command waits only until \fIrecord\fR exists. If more than one such argument is given, the command waits until all of them are satisfied. . .RS .IP "Caution (ovs-vsctl as exmaple)" Usually \fBwait\-until\fR should be placed at the beginning of a set of \fBovs\-vsctl\fR commands. For example, \fBwait\-until bridge br0 \-\- get bridge br0 datapath_id\fR waits until a bridge named \fBbr0\fR is created, then prints its \fBdatapath_id\fR column, whereas \fBget bridge br0 datapath_id \-\- wait\-until bridge br0\fR will abort if no bridge named \fBbr0\fR exists when \fBovs\-vsctl\fR initially connects to the database. .RE .IP Consider specifying \fB\-\-timeout=0\fR along with \fB\-\-wait\-until\fR, to prevent \fB\*(PN\fR from terminating after waiting only at most 5 seconds. .IP "\fBcomment \fR[\fIarg\fR]..." This command has no effect on behavior, but any database log record created by the command will include the command and its arguments. openvswitch-2.5.9/lib/PaxHeaders.82075/match.h0000644000000000000000000000013213534540071015657 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.761853278 openvswitch-2.5.9/lib/match.h0000644000175000017500000002401113534540071017343 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MATCH_H #define MATCH_H 1 #include "flow.h" #include "packets.h" #include "tun-metadata.h" struct ds; /* A flow classification match. * * Use one of the match_*() functions to initialize a "struct match". * * The match_*() functions below maintain the following important invariant. * If a bit or a field is wildcarded in 'wc', then the corresponding bit or * field in 'flow' is set to all-0-bits. (The match_zero_wildcarded_fields() * function can be used to restore this invariant after adding wildcards.) */ struct match { struct flow flow; struct flow_wildcards wc; struct tun_metadata_allocation tun_md; }; /* Initializer for a "struct match" that matches every packet. */ #define MATCH_CATCHALL_INITIALIZER { .flow = { .dl_type = 0 } } void match_init(struct match *, const struct flow *, const struct flow_wildcards *); void match_wc_init(struct match *match, const struct flow *flow); void match_init_catchall(struct match *); void match_zero_wildcarded_fields(struct match *); void match_set_dp_hash(struct match *, uint32_t value); void match_set_dp_hash_masked(struct match *, uint32_t value, uint32_t mask); void match_set_recirc_id(struct match *, uint32_t value); void match_set_conj_id(struct match *, uint32_t value); void match_set_reg(struct match *, unsigned int reg_idx, uint32_t value); void match_set_reg_masked(struct match *, unsigned int reg_idx, uint32_t value, uint32_t mask); void match_set_xreg(struct match *, unsigned int xreg_idx, uint64_t value); void match_set_xreg_masked(struct match *, unsigned int xreg_idx, uint64_t value, uint64_t mask); void match_set_actset_output(struct match *, ofp_port_t actset_output); void match_set_metadata(struct match *, ovs_be64 metadata); void match_set_metadata_masked(struct match *, ovs_be64 metadata, ovs_be64 mask); void match_set_tun_id(struct match *, ovs_be64 tun_id); void match_set_tun_id_masked(struct match *, ovs_be64 tun_id, ovs_be64 mask); void match_set_tun_src(struct match *match, ovs_be32 src); void match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask); void match_set_tun_dst(struct match *match, ovs_be32 dst); void match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask); void match_set_tun_ipv6_src(struct match *, const struct in6_addr *); void match_set_tun_ipv6_src_masked(struct match *, const struct in6_addr *, const struct in6_addr *); void match_set_tun_ipv6_dst(struct match *, const struct in6_addr *); void match_set_tun_ipv6_dst_masked(struct match *, const struct in6_addr *, const struct in6_addr *); void match_set_tun_ttl(struct match *match, uint8_t ttl); void match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask); void match_set_tun_tos(struct match *match, uint8_t tos); void match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask); void match_set_tun_flags(struct match *match, uint16_t flags); void match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask); void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 mask); void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id); void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask); void match_set_tun_gbp_flags(struct match *match, uint8_t flags); void match_set_in_port(struct match *, ofp_port_t ofp_port); void match_set_pkt_mark(struct match *, uint32_t pkt_mark); void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask); void match_set_ct_state(struct match *, uint32_t ct_state); void match_set_ct_state_masked(struct match *, uint32_t ct_state, uint32_t mask); void match_set_ct_zone(struct match *, uint16_t ct_zone); void match_set_ct_mark(struct match *, uint32_t ct_mark); void match_set_ct_mark_masked(struct match *, uint32_t ct_mark, uint32_t mask); void match_set_ct_label(struct match *, ovs_u128 ct_label); void match_set_ct_label_masked(struct match *, ovs_u128 ct_label, ovs_u128 mask); void match_set_skb_priority(struct match *, uint32_t skb_priority); void match_set_dl_type(struct match *, ovs_be16); void match_set_dl_src(struct match *, const struct eth_addr ); void match_set_dl_src_masked(struct match *, const struct eth_addr dl_src, const struct eth_addr mask); void match_set_dl_dst(struct match *, const struct eth_addr); void match_set_dl_dst_masked(struct match *, const struct eth_addr dl_dst, const struct eth_addr mask); void match_set_dl_tci(struct match *, ovs_be16 tci); void match_set_dl_tci_masked(struct match *, ovs_be16 tci, ovs_be16 mask); void match_set_any_vid(struct match *); void match_set_dl_vlan(struct match *, ovs_be16); void match_set_vlan_vid(struct match *, ovs_be16); void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask); void match_set_any_pcp(struct match *); void match_set_dl_vlan_pcp(struct match *, uint8_t); void match_set_any_mpls_lse(struct match *, int idx); void match_set_mpls_lse(struct match *, int idx, ovs_be32); void match_set_any_mpls_label(struct match *, int idx); void match_set_mpls_label(struct match *, int idx, ovs_be32); void match_set_any_mpls_tc(struct match *, int idx); void match_set_mpls_tc(struct match *, int idx, uint8_t); void match_set_any_mpls_bos(struct match *, int idx); void match_set_mpls_bos(struct match *, int idx, uint8_t); void match_set_tp_src(struct match *, ovs_be16); void match_set_mpls_lse(struct match *, int idx, ovs_be32 lse); void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask); void match_set_tp_dst(struct match *, ovs_be16); void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask); void match_set_tcp_flags(struct match *, ovs_be16); void match_set_tcp_flags_masked(struct match *, ovs_be16 flags, ovs_be16 mask); void match_set_nw_proto(struct match *, uint8_t); void match_set_nw_src(struct match *, ovs_be32); void match_set_nw_src_masked(struct match *, ovs_be32 ip, ovs_be32 mask); void match_set_nw_dst(struct match *, ovs_be32); void match_set_nw_dst_masked(struct match *, ovs_be32 ip, ovs_be32 mask); void match_set_nw_dscp(struct match *, uint8_t); void match_set_nw_ecn(struct match *, uint8_t); void match_set_nw_ttl(struct match *, uint8_t); void match_set_nw_frag(struct match *, uint8_t nw_frag); void match_set_nw_frag_masked(struct match *, uint8_t nw_frag, uint8_t mask); void match_set_icmp_type(struct match *, uint8_t); void match_set_icmp_code(struct match *, uint8_t); void match_set_arp_sha(struct match *, const struct eth_addr); void match_set_arp_sha_masked(struct match *, const struct eth_addr arp_sha, const struct eth_addr mask); void match_set_arp_tha(struct match *, const struct eth_addr); void match_set_arp_tha_masked(struct match *, const struct eth_addr arp_tha, const struct eth_addr mask); void match_set_ipv6_src(struct match *, const struct in6_addr *); void match_set_ipv6_src_masked(struct match *, const struct in6_addr *, const struct in6_addr *); void match_set_ipv6_dst(struct match *, const struct in6_addr *); void match_set_ipv6_dst_masked(struct match *, const struct in6_addr *, const struct in6_addr *); void match_set_ipv6_label(struct match *, ovs_be32); void match_set_ipv6_label_masked(struct match *, ovs_be32, ovs_be32); void match_set_nd_target(struct match *, const struct in6_addr *); void match_set_nd_target_masked(struct match *, const struct in6_addr *, const struct in6_addr *); bool match_equal(const struct match *, const struct match *); uint32_t match_hash(const struct match *, uint32_t basis); void match_init_hidden_fields(struct match *); bool match_has_default_hidden_fields(const struct match *); void match_format(const struct match *, struct ds *, int priority); char *match_to_string(const struct match *, int priority); void match_print(const struct match *); /* Compressed match. */ /* A sparse representation of a "struct match". * * 'flows' is used for allocating both 'flow' and 'mask' with one * miniflow_alloc() call. * * There are two invariants: * * - The same invariant as "struct match", that is, a 1-bit in the 'flow' * must correspond to a 1-bit in 'mask'. * * - 'flow' and 'mask' have the same 'map'. This implies that 'flow' and * 'mask' have the same part of "struct flow" at the same offset into * 'values', which makes minimatch_matches_flow() faster. */ struct minimatch { union { struct { struct miniflow *flow; struct minimask *mask; }; struct miniflow *flows[2]; }; }; void minimatch_init(struct minimatch *, const struct match *); void minimatch_clone(struct minimatch *, const struct minimatch *); void minimatch_move(struct minimatch *dst, struct minimatch *src); void minimatch_destroy(struct minimatch *); void minimatch_expand(const struct minimatch *, struct match *); bool minimatch_equal(const struct minimatch *a, const struct minimatch *b); bool minimatch_matches_flow(const struct minimatch *, const struct flow *); void minimatch_format(const struct minimatch *, struct ds *, int priority); char *minimatch_to_string(const struct minimatch *, int priority); #endif /* match.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/string.h.in0000644000000000000000000000013213534540071016476 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801423.789846113 openvswitch-2.5.9/lib/string.h.in0000644000175000017500000000264713534540071020175 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STRING_WRAPPER_H #define STRING_WRAPPER_H 1 #@INCLUDE_NEXT@ @NEXT_STRING_H@ /* Glibc 2.7 has a bug in strtok_r when compiling with optimization that can * cause segfaults if the delimiters argument is a compile-time constant that * has exactly 1 character: * * http://sources.redhat.com/bugzilla/show_bug.cgi?id=5614 * * The bug is only present in the inline version of strtok_r(), so force the * out-of-line version to be used instead. */ #if HAVE_STRTOK_R_BUG #undef strtok_r #endif #ifdef _WIN32 #define strtok_r strtok_s #define strcasecmp _stricmp #define strncasecmp _strnicmp #define strerror_r(errnum, buf, buflen) strerror_s(buf, buflen, errnum) #endif #ifndef HAVE_STRNLEN #undef strnlen #define strnlen rpl_strnlen size_t strnlen(const char *, size_t maxlen); #endif #endif /* string.h wrapper */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlan-bitmap.c0000644000000000000000000000013213534540071016770 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.941854606 openvswitch-2.5.9/lib/vlan-bitmap.c0000644000175000017500000000371213534540071020461 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "vlan-bitmap.h" /* Allocates and returns a new 4096-bit bitmap that has 1-bit in positions in * the 'n_vlans' bits indicated in 'vlans' and 0-bits everywhere else. Returns * a null pointer if there are no (valid) VLANs in 'vlans'. */ unsigned long * vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans) { unsigned long *b; if (!n_vlans) { return NULL; } b = bitmap_allocate(4096); if (!vlan_bitmap_from_array__(vlans, n_vlans, b)) { free(b); return NULL; } return b; } /* Adds to 4096-bit VLAN bitmap 'b' a 1-bit in each position in the 'n_vlans' * bits indicated in 'vlans'. Returns the number of 1-bits added to 'b'. */ int vlan_bitmap_from_array__(const int64_t *vlans, size_t n_vlans, unsigned long int *b) { size_t i; int n; n = 0; for (i = 0; i < n_vlans; i++) { int64_t vlan = vlans[i]; if (vlan >= 0 && vlan < 4096 && !bitmap_is_set(b, vlan)) { bitmap_set1(b, vlan); n++; } } return n; } /* Returns true if 'a' and 'b' are the same: either both null or both the same * 4096-bit bitmap. * * (We assume that a nonnull bitmap is not all 0-bits.) */ bool vlan_bitmap_equal(const unsigned long *a, const unsigned long *b) { return (!a && !b) || (a && b && bitmap_equal(a, b, 4096)); } openvswitch-2.5.9/lib/PaxHeaders.82075/nx-match.c0000644000000000000000000000013213534540071016275 xustar0030 mtime=1567801401.473681699 30 atime=1567801402.081686164 30 ctime=1567801424.781853426 openvswitch-2.5.9/lib/nx-match.c0000644000175000017500000020055713534540071017774 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "nx-match.h" #include #include "classifier.h" #include "dynamic-string.h" #include "hmap.h" #include "meta-flow.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "packets.h" #include "shash.h" #include "tun-metadata.h" #include "unaligned.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(nx_match); /* OXM headers. * * * Standard OXM/NXM * ================ * * The header is 32 bits long. It looks like this: * * |31 16 15 9| 8 7 0 * +----------------------------------+---------------+--+------------------+ * | oxm_class | oxm_field |hm| oxm_length | * +----------------------------------+---------------+--+------------------+ * * where hm stands for oxm_hasmask. It is followed by oxm_length bytes of * payload. When oxm_hasmask is 0, the payload is the value of the field * identified by the header; when oxm_hasmask is 1, the payload is a value for * the field followed by a mask of equal length. * * Internally, we represent a standard OXM header as a 64-bit integer with the * above information in the most-significant bits. * * * Experimenter OXM * ================ * * The header is 64 bits long. It looks like the diagram above except that a * 32-bit experimenter ID, which we call oxm_vendor and which identifies a * vendor, is inserted just before the payload. Experimenter OXMs are * identified by an all-1-bits oxm_class (OFPXMC12_EXPERIMENTER). The * oxm_length value *includes* the experimenter ID, so that the real payload is * only oxm_length - 4 bytes long. * * Internally, we represent an experimenter OXM header as a 64-bit integer with * the standard header in the upper 32 bits and the experimenter ID in the * lower 32 bits. (It would be more convenient to swap the positions of the * two 32-bit words, but this would be more error-prone because experimenter * OXMs are very rarely used, so accidentally passing one through a 32-bit type * somewhere in the OVS code would be hard to find.) */ /* * OXM Class IDs. * The high order bit differentiate reserved classes from member classes. * Classes 0x0000 to 0x7FFF are member classes, allocated by ONF. * Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation. */ enum ofp12_oxm_class { OFPXMC12_NXM_0 = 0x0000, /* Backward compatibility with NXM */ OFPXMC12_NXM_1 = 0x0001, /* Backward compatibility with NXM */ OFPXMC12_OPENFLOW_BASIC = 0x8000, /* Basic class for OpenFlow */ OFPXMC15_PACKET_REGS = 0x8001, /* Packet registers (pipeline fields). */ OFPXMC12_EXPERIMENTER = 0xffff, /* Experimenter class */ }; /* Functions for extracting raw field values from OXM/NXM headers. */ static uint32_t nxm_vendor(uint64_t header) { return header; } static int nxm_class(uint64_t header) { return header >> 48; } static int nxm_field(uint64_t header) { return (header >> 41) & 0x7f; } static bool nxm_hasmask(uint64_t header) { return (header >> 40) & 1; } static int nxm_length(uint64_t header) { return (header >> 32) & 0xff; } static uint64_t nxm_no_len(uint64_t header) { return header & 0xffffff80ffffffffULL; } static bool is_experimenter_oxm(uint64_t header) { return nxm_class(header) == OFPXMC12_EXPERIMENTER; } /* The OXM header "length" field is somewhat tricky: * * - For a standard OXM header, the length is the number of bytes of the * payload, and the payload consists of just the value (and mask, if * present). * * - For an experimenter OXM header, the length is the number of bytes in * the payload plus 4 (the length of the experimenter ID). That is, the * experimenter ID is included in oxm_length. * * This function returns the length of the experimenter ID field in 'header'. * That is, for an experimenter OXM (when an experimenter ID is present), it * returns 4, and for a standard OXM (when no experimenter ID is present), it * returns 0. */ static int nxm_experimenter_len(uint64_t header) { return is_experimenter_oxm(header) ? 4 : 0; } /* Returns the number of bytes that follow the header for an NXM/OXM entry * with the given 'header'. */ static int nxm_payload_len(uint64_t header) { return nxm_length(header) - nxm_experimenter_len(header); } /* Returns the number of bytes in the header for an NXM/OXM entry with the * given 'header'. */ static int nxm_header_len(uint64_t header) { return 4 + nxm_experimenter_len(header); } #define NXM_HEADER(VENDOR, CLASS, FIELD, HASMASK, LENGTH) \ (((uint64_t) (CLASS) << 48) | \ ((uint64_t) (FIELD) << 41) | \ ((uint64_t) (HASMASK) << 40) | \ ((uint64_t) (LENGTH) << 32) | \ (VENDOR)) #define NXM_HEADER_FMT "%#"PRIx32":%d:%d:%d:%d" #define NXM_HEADER_ARGS(HEADER) \ nxm_vendor(HEADER), nxm_class(HEADER), nxm_field(HEADER), \ nxm_hasmask(HEADER), nxm_length(HEADER) /* Functions for turning the "hasmask" bit on or off. (This also requires * adjusting the length.) */ static uint64_t nxm_make_exact_header(uint64_t header) { int new_len = nxm_payload_len(header) / 2 + nxm_experimenter_len(header); return NXM_HEADER(nxm_vendor(header), nxm_class(header), nxm_field(header), 0, new_len); } static uint64_t nxm_make_wild_header(uint64_t header) { int new_len = nxm_payload_len(header) * 2 + nxm_experimenter_len(header); return NXM_HEADER(nxm_vendor(header), nxm_class(header), nxm_field(header), 1, new_len); } /* Flow cookie. * * This may be used to gain the OpenFlow 1.1-like ability to restrict * certain NXM-based Flow Mod and Flow Stats Request messages to flows * with specific cookies. See the "nx_flow_mod" and "nx_flow_stats_request" * structure definitions for more details. This match is otherwise not * allowed. */ #define NXM_NX_COOKIE NXM_HEADER (0, 0x0001, 30, 0, 8) #define NXM_NX_COOKIE_W nxm_make_wild_header(NXM_NX_COOKIE) struct nxm_field { uint64_t header; enum ofp_version version; const char *name; /* e.g. "NXM_OF_IN_PORT". */ enum mf_field_id id; }; static const struct nxm_field *nxm_field_by_header(uint64_t header); static const struct nxm_field *nxm_field_by_name(const char *name, size_t len); static const struct nxm_field *nxm_field_by_mf_id(enum mf_field_id, enum ofp_version); static void nx_put_header__(struct ofpbuf *, uint64_t header, bool masked); static void nx_put_header_len(struct ofpbuf *, enum mf_field_id field, enum ofp_version version, bool masked, size_t n_bytes); /* Rate limit for nx_match parse errors. These always indicate a bug in the * peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static const struct nxm_field * mf_parse_subfield_name(const char *name, int name_len, bool *wild); /* Returns the preferred OXM header to use for field 'id' in OpenFlow version * 'version'. Specify 0 for 'version' if an NXM legacy header should be * preferred over any standardized OXM header. Returns 0 if field 'id' cannot * be expressed in NXM or OXM. */ static uint64_t mf_oxm_header(enum mf_field_id id, enum ofp_version version) { const struct nxm_field *f = nxm_field_by_mf_id(id, version); return f ? f->header : 0; } /* Returns the 32-bit OXM or NXM header to use for field 'id', preferring an * NXM legacy header over any standardized OXM header. Returns 0 if field 'id' * cannot be expressed with a 32-bit NXM or OXM header. * * Whenever possible, use nx_pull_header() instead of this function, because * this function cannot support 64-bit experimenter OXM headers. */ uint32_t mf_nxm_header(enum mf_field_id id) { uint64_t oxm = mf_oxm_header(id, 0); return is_experimenter_oxm(oxm) ? 0 : oxm >> 32; } static const struct mf_field * mf_from_oxm_header(uint64_t header) { const struct nxm_field *f = nxm_field_by_header(header); return f ? mf_from_id(f->id) : NULL; } /* Returns the "struct mf_field" that corresponds to NXM or OXM header * 'header', or NULL if 'header' doesn't correspond to any known field. */ const struct mf_field * mf_from_nxm_header(uint32_t header) { return mf_from_oxm_header((uint64_t) header << 32); } /* Returns the width of the data for a field with the given 'header', in * bytes. */ static int nxm_field_bytes(uint64_t header) { unsigned int length = nxm_payload_len(header); return nxm_hasmask(header) ? length / 2 : length; } /* nx_pull_match() and helpers. */ /* Given NXM/OXM value 'value' and mask 'mask' associated with 'header', checks * for any 1-bit in the value where there is a 0-bit in the mask. Returns 0 if * none, otherwise an error code. */ static bool is_mask_consistent(uint64_t header, const uint8_t *value, const uint8_t *mask) { unsigned int width = nxm_field_bytes(header); unsigned int i; for (i = 0; i < width; i++) { if (value[i] & ~mask[i]) { if (!VLOG_DROP_WARN(&rl)) { VLOG_WARN_RL(&rl, "Rejecting NXM/OXM entry "NXM_HEADER_FMT " " "with 1-bits in value for bits wildcarded by the " "mask.", NXM_HEADER_ARGS(header)); } return false; } } return true; } static bool is_cookie_pseudoheader(uint64_t header) { return header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W; } static enum ofperr nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint64_t *header, const struct mf_field **field) { if (b->size < 4) { goto bad_len; } *header = ((uint64_t) ntohl(get_unaligned_be32(b->data))) << 32; if (is_experimenter_oxm(*header)) { if (b->size < 8) { goto bad_len; } *header = ntohll(get_unaligned_be64(b->data)); } if (nxm_length(*header) < nxm_experimenter_len(*header)) { VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has invalid length %d " "(minimum is %d)", NXM_HEADER_ARGS(*header), nxm_length(*header), nxm_header_len(*header)); goto error; } ofpbuf_pull(b, nxm_header_len(*header)); if (field) { *field = mf_from_oxm_header(*header); if (!*field && !(allow_cookie && is_cookie_pseudoheader(*header))) { VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" is unknown", NXM_HEADER_ARGS(*header)); return OFPERR_OFPBMC_BAD_FIELD; } } return 0; bad_len: VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry", b->size); error: *header = 0; if (field) { *field = NULL; } return OFPERR_OFPBMC_BAD_LEN; } static void copy_entry_value(const struct mf_field *field, union mf_value *value, const uint8_t *payload, int width) { int copy_len; void *copy_dst; copy_dst = value; copy_len = MIN(width, field ? field->n_bytes : sizeof *value); if (field && field->variable_len) { memset(value, 0, field->n_bytes); copy_dst = &value->u8 + field->n_bytes - copy_len; } memcpy(copy_dst, payload, copy_len); } static enum ofperr nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint64_t *header, const struct mf_field **field_, union mf_value *value, union mf_value *mask) { const struct mf_field *field; enum ofperr header_error; unsigned int payload_len; const uint8_t *payload; int width; header_error = nx_pull_header__(b, allow_cookie, header, &field); if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) { return header_error; } payload_len = nxm_payload_len(*header); payload = ofpbuf_try_pull(b, payload_len); if (!payload) { VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" calls for %u-byte " "payload but only %"PRIu32" bytes follow OXM header", NXM_HEADER_ARGS(*header), payload_len, b->size); return OFPERR_OFPBMC_BAD_LEN; } width = nxm_field_bytes(*header); if (nxm_hasmask(*header) && !is_mask_consistent(*header, payload, payload + width)) { return OFPERR_OFPBMC_BAD_WILDCARDS; } copy_entry_value(field, value, payload, width); if (mask) { if (nxm_hasmask(*header)) { copy_entry_value(field, mask, payload + width, width); } else { memset(mask, 0xff, sizeof *mask); } } else if (nxm_hasmask(*header)) { VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" includes mask but " "masked OXMs are not allowed here", NXM_HEADER_ARGS(*header)); return OFPERR_OFPBMC_BAD_MASK; } if (field_) { *field_ = field; return header_error; } return 0; } /* Attempts to pull an NXM or OXM header, value, and mask (if present) from the * beginning of 'b'. If successful, stores a pointer to the "struct mf_field" * corresponding to the pulled header in '*field', the value into '*value', * and the mask into '*mask', and returns 0. On error, returns an OpenFlow * error; in this case, some bytes might have been pulled off 'b' anyhow, and * the output parameters might have been modified. * * If a NULL 'mask' is supplied, masked OXM or NXM entries are treated as * errors (with OFPERR_OFPBMC_BAD_MASK). */ enum ofperr nx_pull_entry(struct ofpbuf *b, const struct mf_field **field, union mf_value *value, union mf_value *mask) { uint64_t header; return nx_pull_entry__(b, false, &header, field, value, mask); } /* Attempts to pull an NXM or OXM header from the beginning of 'b'. If * successful, stores a pointer to the "struct mf_field" corresponding to the * pulled header in '*field', stores the header's hasmask bit in '*masked' * (true if hasmask=1, false if hasmask=0), and returns 0. On error, returns * an OpenFlow error; in this case, some bytes might have been pulled off 'b' * anyhow, and the output parameters might have been modified. * * If NULL 'masked' is supplied, masked OXM or NXM headers are treated as * errors (with OFPERR_OFPBMC_BAD_MASK). */ enum ofperr nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked) { enum ofperr error; uint64_t header; error = nx_pull_header__(b, false, &header, field); if (masked) { *masked = !error && nxm_hasmask(header); } else if (!error && nxm_hasmask(header)) { error = OFPERR_OFPBMC_BAD_MASK; } return error; } static enum ofperr nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie, const struct mf_field **field, union mf_value *value, union mf_value *mask) { enum ofperr error; uint64_t header; error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask); if (error) { return error; } if (field && *field) { if (!mf_is_mask_valid(*field, mask)) { VLOG_DBG_RL(&rl, "bad mask for field %s", (*field)->name); return OFPERR_OFPBMC_BAD_MASK; } if (!mf_is_value_valid(*field, value)) { VLOG_DBG_RL(&rl, "bad value for field %s", (*field)->name); return OFPERR_OFPBMC_BAD_VALUE; } } return 0; } static enum ofperr nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { struct ofpbuf b; ovs_assert((cookie != NULL) == (cookie_mask != NULL)); match_init_catchall(match); if (cookie) { *cookie = *cookie_mask = htonll(0); } ofpbuf_use_const(&b, p, match_len); while (b.size) { const uint8_t *pos = b.data; const struct mf_field *field; union mf_value value; union mf_value mask; enum ofperr error; error = nx_pull_match_entry(&b, cookie != NULL, &field, &value, &mask); if (error) { if (error == OFPERR_OFPBMC_BAD_FIELD && !strict) { continue; } } else if (!field) { if (!cookie) { error = OFPERR_OFPBMC_BAD_FIELD; } else if (*cookie_mask) { error = OFPERR_OFPBMC_DUP_FIELD; } else { *cookie = value.be64; *cookie_mask = mask.be64; } } else if (!mf_are_prereqs_ok(field, &match->flow)) { error = OFPERR_OFPBMC_BAD_PREREQ; } else if (!mf_is_all_wild(field, &match->wc)) { error = OFPERR_OFPBMC_DUP_FIELD; } else { char *err_str; mf_set(field, &value, &mask, match, &err_str); if (err_str) { VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" " "within match (%s)", pos - p, err_str); free(err_str); return OFPERR_OFPBMC_BAD_VALUE; } } if (error) { VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" " "within match (%s)", pos - p, ofperr_to_string(error)); return error; } } return 0; } static enum ofperr nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { uint8_t *p = NULL; if (match_len) { p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); if (!p) { VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a " "multiple of 8, is longer than space in message (max " "length %"PRIu32")", match_len, b->size); return OFPERR_OFPBMC_BAD_LEN; } } return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask); } /* Parses the nx_match formatted match description in 'b' with length * 'match_len'. Stores the results in 'match'. If 'cookie' and 'cookie_mask' * are valid pointers, then stores the cookie and mask in them if 'b' contains * a "NXM_NX_COOKIE*" match. Otherwise, stores 0 in both. * * Fails with an error upon encountering an unknown NXM header. * * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask); } /* Behaves the same as nx_pull_match(), but skips over unknown NXM headers, * instead of failing with an error. */ enum ofperr nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask); } static enum ofperr oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match) { struct ofp11_match_header *omh = b->data; uint8_t *p; uint16_t match_len; if (b->size < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } match_len = ntohs(omh->length); if (match_len < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } if (omh->type != htons(OFPMT_OXM)) { return OFPERR_OFPBMC_BAD_TYPE; } p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); if (!p) { VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a " "multiple of 8, is longer than space in message (max " "length %"PRIu32")", match_len, b->size); return OFPERR_OFPBMC_BAD_LEN; } return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh, strict, match, NULL, NULL); } /* Parses the oxm formatted match description preceded by a struct * ofp11_match_header in 'b'. Stores the result in 'match'. * * Fails with an error when encountering unknown OXM headers. * * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr oxm_pull_match(struct ofpbuf *b, struct match *match) { return oxm_pull_match__(b, true, match); } /* Behaves the same as oxm_pull_match() with one exception. Skips over unknown * OXM headers instead of failing with an error when they are encountered. */ enum ofperr oxm_pull_match_loose(struct ofpbuf *b, struct match *match) { return oxm_pull_match__(b, false, match); } /* Verify an array of OXM TLVs treating value of each TLV as a mask, * disallowing masks in each TLV and ignoring pre-requisites. */ enum ofperr oxm_pull_field_array(const void *fields_data, size_t fields_len, struct field_array *fa) { struct ofpbuf b; ofpbuf_use_const(&b, fields_data, fields_len); while (b.size) { const uint8_t *pos = b.data; const struct mf_field *field; union mf_value value; enum ofperr error; uint64_t header; error = nx_pull_entry__(&b, false, &header, &field, &value, NULL); if (error) { VLOG_DBG_RL(&rl, "error pulling field array field"); return error; } else if (!field) { VLOG_DBG_RL(&rl, "unknown field array field"); error = OFPERR_OFPBMC_BAD_FIELD; } else if (bitmap_is_set(fa->used.bm, field->id)) { VLOG_DBG_RL(&rl, "duplicate field array field '%s'", field->name); error = OFPERR_OFPBMC_DUP_FIELD; } else if (!mf_is_mask_valid(field, &value)) { VLOG_DBG_RL(&rl, "bad mask in field array field '%s'", field->name); return OFPERR_OFPBMC_BAD_MASK; } else { field_array_set(field->id, &value, fa); } if (error) { const uint8_t *start = fields_data; VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" " "within field array (%s)", pos - start, ofperr_to_string(error)); return error; } } return 0; } /* nx_put_match() and helpers. * * 'put' functions whose names end in 'w' add a wildcarded field. * 'put' functions whose names end in 'm' add a field that might be wildcarded. * Other 'put' functions add exact-match fields. */ void nxm_put__(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const void *value, const void *mask, size_t n_bytes) { nx_put_header_len(b, field, version, !!mask, n_bytes); ofpbuf_put(b, value, n_bytes); if (mask) { ofpbuf_put(b, mask, n_bytes); } } static void nxm_put(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const void *value, const void *mask, size_t n_bytes) { if (!is_all_zeros(mask, n_bytes)) { bool masked = !is_all_ones(mask, n_bytes); nxm_put__(b, field, version, value, masked ? mask : NULL, n_bytes); } } static void nxm_put_8m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, uint8_t value, uint8_t mask) { nxm_put(b, field, version, &value, &mask, sizeof value); } static void nxm_put_8(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, uint8_t value) { nxm_put__(b, field, version, &value, NULL, sizeof value); } static void nxm_put_16m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, ovs_be16 value, ovs_be16 mask) { nxm_put(b, field, version, &value, &mask, sizeof value); } static void nxm_put_16(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, ovs_be16 value) { nxm_put__(b, field, version, &value, NULL, sizeof value); } static void nxm_put_32m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, ovs_be32 value, ovs_be32 mask) { nxm_put(b, field, version, &value, &mask, sizeof value); } static void nxm_put_32(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, ovs_be32 value) { nxm_put__(b, field, version, &value, NULL, sizeof value); } static void nxm_put_64m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, ovs_be64 value, ovs_be64 mask) { nxm_put(b, field, version, &value, &mask, sizeof value); } static void nxm_put_128m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const ovs_be128 value, const ovs_be128 mask) { nxm_put(b, field, version, &value, &mask, sizeof(value)); } static void nxm_put_eth_masked(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const struct eth_addr value, const struct eth_addr mask) { nxm_put(b, field, version, value.ea, mask.ea, ETH_ADDR_LEN); } static void nxm_put_ipv6(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const struct in6_addr *value, const struct in6_addr *mask) { nxm_put(b, field, version, value->s6_addr, mask->s6_addr, sizeof value->s6_addr); } static void nxm_put_frag(struct ofpbuf *b, const struct match *match, enum ofp_version version) { uint8_t nw_frag = match->flow.nw_frag & FLOW_NW_FRAG_MASK; uint8_t nw_frag_mask = match->wc.masks.nw_frag & FLOW_NW_FRAG_MASK; nxm_put_8m(b, MFF_IP_FRAG, version, nw_frag, nw_frag_mask == FLOW_NW_FRAG_MASK ? UINT8_MAX : nw_frag_mask); } /* Appends to 'b' a set of OXM or NXM matches for the IPv4 or IPv6 fields in * 'match'. */ static void nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm) { const struct flow *flow = &match->flow; if (flow->dl_type == htons(ETH_TYPE_IP)) { nxm_put_32m(b, MFF_IPV4_SRC, oxm, flow->nw_src, match->wc.masks.nw_src); nxm_put_32m(b, MFF_IPV4_DST, oxm, flow->nw_dst, match->wc.masks.nw_dst); } else { nxm_put_ipv6(b, MFF_IPV6_SRC, oxm, &flow->ipv6_src, &match->wc.masks.ipv6_src); nxm_put_ipv6(b, MFF_IPV6_DST, oxm, &flow->ipv6_dst, &match->wc.masks.ipv6_dst); } nxm_put_frag(b, match, oxm); if (match->wc.masks.nw_tos & IP_DSCP_MASK) { if (oxm) { nxm_put_8(b, MFF_IP_DSCP_SHIFTED, oxm, flow->nw_tos >> 2); } else { nxm_put_8(b, MFF_IP_DSCP, oxm, flow->nw_tos & IP_DSCP_MASK); } } if (match->wc.masks.nw_tos & IP_ECN_MASK) { nxm_put_8(b, MFF_IP_ECN, oxm, flow->nw_tos & IP_ECN_MASK); } if (match->wc.masks.nw_ttl) { nxm_put_8(b, MFF_IP_TTL, oxm, flow->nw_ttl); } nxm_put_32m(b, MFF_IPV6_LABEL, oxm, flow->ipv6_label, match->wc.masks.ipv6_label); if (match->wc.masks.nw_proto) { nxm_put_8(b, MFF_IP_PROTO, oxm, flow->nw_proto); if (flow->nw_proto == IPPROTO_TCP) { nxm_put_16m(b, MFF_TCP_SRC, oxm, flow->tp_src, match->wc.masks.tp_src); nxm_put_16m(b, MFF_TCP_DST, oxm, flow->tp_dst, match->wc.masks.tp_dst); nxm_put_16m(b, MFF_TCP_FLAGS, oxm, flow->tcp_flags, match->wc.masks.tcp_flags); } else if (flow->nw_proto == IPPROTO_UDP) { nxm_put_16m(b, MFF_UDP_SRC, oxm, flow->tp_src, match->wc.masks.tp_src); nxm_put_16m(b, MFF_UDP_DST, oxm, flow->tp_dst, match->wc.masks.tp_dst); } else if (flow->nw_proto == IPPROTO_SCTP) { nxm_put_16m(b, MFF_SCTP_SRC, oxm, flow->tp_src, match->wc.masks.tp_src); nxm_put_16m(b, MFF_SCTP_DST, oxm, flow->tp_dst, match->wc.masks.tp_dst); } else if (is_icmpv4(flow, NULL)) { if (match->wc.masks.tp_src) { nxm_put_8(b, MFF_ICMPV4_TYPE, oxm, ntohs(flow->tp_src)); } if (match->wc.masks.tp_dst) { nxm_put_8(b, MFF_ICMPV4_CODE, oxm, ntohs(flow->tp_dst)); } } else if (is_icmpv6(flow, NULL)) { if (match->wc.masks.tp_src) { nxm_put_8(b, MFF_ICMPV6_TYPE, oxm, ntohs(flow->tp_src)); } if (match->wc.masks.tp_dst) { nxm_put_8(b, MFF_ICMPV6_CODE, oxm, ntohs(flow->tp_dst)); } if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) { nxm_put_ipv6(b, MFF_ND_TARGET, oxm, &flow->nd_target, &match->wc.masks.nd_target); if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) { nxm_put_eth_masked(b, MFF_ND_SLL, oxm, flow->arp_sha, match->wc.masks.arp_sha); } if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) { nxm_put_eth_masked(b, MFF_ND_TLL, oxm, flow->arp_tha, match->wc.masks.arp_tha); } } } } } /* Appends to 'b' the nx_match format that expresses 'match'. For Flow Mod and * Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied. * Otherwise, 'cookie_mask' should be zero. * * Specify 'oxm' as 0 to express the match in NXM format; otherwise, specify * 'oxm' as the OpenFlow version number for the OXM format to use. * * This function can cause 'b''s data to be reallocated. * * Returns the number of bytes appended to 'b', excluding padding. * * If 'match' is a catch-all rule that matches every packet, then this function * appends nothing to 'b' and returns 0. */ static int nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, ovs_be64 cookie, ovs_be64 cookie_mask) { const struct flow *flow = &match->flow; const size_t start_len = b->size; int match_len; int i; BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); /* Metadata. */ if (match->wc.masks.dp_hash) { nxm_put_32m(b, MFF_DP_HASH, oxm, htonl(flow->dp_hash), htonl(match->wc.masks.dp_hash)); } if (match->wc.masks.recirc_id) { nxm_put_32(b, MFF_RECIRC_ID, oxm, htonl(flow->recirc_id)); } if (match->wc.masks.conj_id) { nxm_put_32(b, MFF_CONJ_ID, oxm, htonl(flow->conj_id)); } if (match->wc.masks.in_port.ofp_port) { ofp_port_t in_port = flow->in_port.ofp_port; if (oxm) { nxm_put_32(b, MFF_IN_PORT_OXM, oxm, ofputil_port_to_ofp11(in_port)); } else { nxm_put_16(b, MFF_IN_PORT, oxm, htons(ofp_to_u16(in_port))); } } if (match->wc.masks.actset_output) { nxm_put_32(b, MFF_ACTSET_OUTPUT, oxm, ofputil_port_to_ofp11(flow->actset_output)); } /* Ethernet. */ nxm_put_eth_masked(b, MFF_ETH_SRC, oxm, flow->dl_src, match->wc.masks.dl_src); nxm_put_eth_masked(b, MFF_ETH_DST, oxm, flow->dl_dst, match->wc.masks.dl_dst); nxm_put_16m(b, MFF_ETH_TYPE, oxm, ofputil_dl_type_to_openflow(flow->dl_type), match->wc.masks.dl_type); /* 802.1Q. */ if (oxm) { ovs_be16 VID_CFI_MASK = htons(VLAN_VID_MASK | VLAN_CFI); ovs_be16 vid = flow->vlan_tci & VID_CFI_MASK; ovs_be16 mask = match->wc.masks.vlan_tci & VID_CFI_MASK; if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) { nxm_put_16(b, MFF_VLAN_VID, oxm, vid); } else if (mask) { nxm_put_16m(b, MFF_VLAN_VID, oxm, vid, mask); } if (vid && vlan_tci_to_pcp(match->wc.masks.vlan_tci)) { nxm_put_8(b, MFF_VLAN_PCP, oxm, vlan_tci_to_pcp(flow->vlan_tci)); } } else { nxm_put_16m(b, MFF_VLAN_TCI, oxm, flow->vlan_tci, match->wc.masks.vlan_tci); } /* MPLS. */ if (eth_type_mpls(flow->dl_type)) { if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) { nxm_put_8(b, MFF_MPLS_TC, oxm, mpls_lse_to_tc(flow->mpls_lse[0])); } if (match->wc.masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) { nxm_put_8(b, MFF_MPLS_BOS, oxm, mpls_lse_to_bos(flow->mpls_lse[0])); } if (match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) { nxm_put_32(b, MFF_MPLS_LABEL, oxm, htonl(mpls_lse_to_label(flow->mpls_lse[0]))); } } /* L3. */ if (is_ip_any(flow)) { nxm_put_ip(b, match, oxm); } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { /* ARP. */ if (match->wc.masks.nw_proto) { nxm_put_16(b, MFF_ARP_OP, oxm, htons(flow->nw_proto)); } nxm_put_32m(b, MFF_ARP_SPA, oxm, flow->nw_src, match->wc.masks.nw_src); nxm_put_32m(b, MFF_ARP_TPA, oxm, flow->nw_dst, match->wc.masks.nw_dst); nxm_put_eth_masked(b, MFF_ARP_SHA, oxm, flow->arp_sha, match->wc.masks.arp_sha); nxm_put_eth_masked(b, MFF_ARP_THA, oxm, flow->arp_tha, match->wc.masks.arp_tha); } /* Tunnel ID. */ nxm_put_64m(b, MFF_TUN_ID, oxm, flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id); /* Other tunnel metadata. */ nxm_put_16m(b, MFF_TUN_FLAGS, oxm, htons(flow->tunnel.flags), htons(match->wc.masks.tunnel.flags)); nxm_put_32m(b, MFF_TUN_SRC, oxm, flow->tunnel.ip_src, match->wc.masks.tunnel.ip_src); nxm_put_32m(b, MFF_TUN_DST, oxm, flow->tunnel.ip_dst, match->wc.masks.tunnel.ip_dst); nxm_put_ipv6(b, MFF_TUN_IPV6_SRC, oxm, &flow->tunnel.ipv6_src, &match->wc.masks.tunnel.ipv6_src); nxm_put_ipv6(b, MFF_TUN_IPV6_DST, oxm, &flow->tunnel.ipv6_dst, &match->wc.masks.tunnel.ipv6_dst); nxm_put_16m(b, MFF_TUN_GBP_ID, oxm, flow->tunnel.gbp_id, match->wc.masks.tunnel.gbp_id); nxm_put_8m(b, MFF_TUN_GBP_FLAGS, oxm, flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags); tun_metadata_to_nx_match(b, oxm, match); /* Registers. */ if (oxm < OFP15_VERSION) { for (i = 0; i < FLOW_N_REGS; i++) { nxm_put_32m(b, MFF_REG0 + i, oxm, htonl(flow->regs[i]), htonl(match->wc.masks.regs[i])); } } else { for (i = 0; i < FLOW_N_XREGS; i++) { nxm_put_64m(b, MFF_XREG0 + i, oxm, htonll(flow_get_xreg(flow, i)), htonll(flow_get_xreg(&match->wc.masks, i))); } } /* Packet mark. */ nxm_put_32m(b, MFF_PKT_MARK, oxm, htonl(flow->pkt_mark), htonl(match->wc.masks.pkt_mark)); /* Connection tracking. */ nxm_put_32m(b, MFF_CT_STATE, oxm, htonl(flow->ct_state), htonl(match->wc.masks.ct_state)); nxm_put_16m(b, MFF_CT_ZONE, oxm, htons(flow->ct_zone), htons(match->wc.masks.ct_zone)); nxm_put_32m(b, MFF_CT_MARK, oxm, htonl(flow->ct_mark), htonl(match->wc.masks.ct_mark)); nxm_put_128m(b, MFF_CT_LABEL, oxm, hton128(flow->ct_label), hton128(match->wc.masks.ct_label)); /* OpenFlow 1.1+ Metadata. */ nxm_put_64m(b, MFF_METADATA, oxm, flow->metadata, match->wc.masks.metadata); /* Cookie. */ if (cookie_mask) { bool masked = cookie_mask != OVS_BE64_MAX; cookie &= cookie_mask; nx_put_header__(b, NXM_NX_COOKIE, masked); ofpbuf_put(b, &cookie, sizeof cookie); if (masked) { ofpbuf_put(b, &cookie_mask, sizeof cookie_mask); } } match_len = b->size - start_len; return match_len; } /* Appends to 'b' the nx_match format that expresses 'match', plus enough zero * bytes to pad the nx_match out to a multiple of 8. For Flow Mod and Flow * Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied. * Otherwise, 'cookie_mask' should be zero. * * This function can cause 'b''s data to be reallocated. * * Returns the number of bytes appended to 'b', excluding padding. The return * value can be zero if it appended nothing at all to 'b' (which happens if * 'cr' is a catch-all rule that matches every packet). */ int nx_put_match(struct ofpbuf *b, const struct match *match, ovs_be64 cookie, ovs_be64 cookie_mask) { int match_len = nx_put_raw(b, 0, match, cookie, cookie_mask); ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8)); return match_len; } /* Appends to 'b' an struct ofp11_match_header followed by the OXM format that * expresses 'cr', plus enough zero bytes to pad the data appended out to a * multiple of 8. * * OXM differs slightly among versions of OpenFlow. Specify the OpenFlow * version in use as 'version'. * * This function can cause 'b''s data to be reallocated. * * Returns the number of bytes appended to 'b', excluding the padding. Never * returns zero. */ int oxm_put_match(struct ofpbuf *b, const struct match *match, enum ofp_version version) { int match_len; struct ofp11_match_header *omh; size_t start_len = b->size; ovs_be64 cookie = htonll(0), cookie_mask = htonll(0); ofpbuf_put_uninit(b, sizeof *omh); match_len = (nx_put_raw(b, version, match, cookie, cookie_mask) + sizeof *omh); ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8)); omh = ofpbuf_at(b, start_len, sizeof *omh); omh->type = htons(OFPMT_OXM); omh->length = htons(match_len); return match_len; } /* Appends to 'b' the nx_match format that expresses the tlv corresponding * to 'id'. If mask is not all-ones then it is also formated as the value * of the tlv. */ static void nx_format_mask_tlv(struct ds *ds, enum mf_field_id id, const union mf_value *mask) { const struct mf_field *mf = mf_from_id(id); ds_put_format(ds, "%s", mf->name); if (!is_all_ones(mask, mf->n_bytes)) { ds_put_char(ds, '='); mf_format(mf, mask, NULL, ds); } ds_put_char(ds, ','); } /* Appends a string representation of 'fa_' to 'ds'. * The TLVS value of 'fa_' is treated as a mask and * only the name of fields is formated if it is all ones. */ void oxm_format_field_array(struct ds *ds, const struct field_array *fa) { size_t start_len = ds->length; int i; for (i = 0; i < MFF_N_IDS; i++) { if (bitmap_is_set(fa->used.bm, i)) { nx_format_mask_tlv(ds, i, &fa->value[i]); } } if (ds->length > start_len) { ds_chomp(ds, ','); } } /* Appends to 'b' a series of OXM TLVs corresponding to the series * of enum mf_field_id and value tuples in 'fa_'. * * OXM differs slightly among versions of OpenFlow. Specify the OpenFlow * version in use as 'version'. * * This function can cause 'b''s data to be reallocated. * * Returns the number of bytes appended to 'b'. May return zero. */ int oxm_put_field_array(struct ofpbuf *b, const struct field_array *fa, enum ofp_version version) { size_t start_len = b->size; int i; /* Field arrays are only used with the group selection method * property and group properties are only available in OpenFlow 1.5+. * So the following assertion should never fail. * * If support for older OpenFlow versions is desired then some care * will need to be taken of different TLVs that handle the same * flow fields. In particular: * - VLAN_TCI, VLAN_VID and MFF_VLAN_PCP * - IP_DSCP_MASK and DSCP_SHIFTED * - REGS and XREGS */ ovs_assert(version >= OFP15_VERSION); for (i = 0; i < MFF_N_IDS; i++) { if (bitmap_is_set(fa->used.bm, i)) { int len = mf_field_len(mf_from_id(i), &fa->value[i], NULL, NULL); nxm_put__(b, i, version, &fa->value[i].u8 + mf_from_id(i)->n_bytes - len, NULL, len); } } return b->size - start_len; } static void nx_put_header__(struct ofpbuf *b, uint64_t header, bool masked) { uint64_t masked_header = masked ? nxm_make_wild_header(header) : header; ovs_be64 network_header = htonll(masked_header); ofpbuf_put(b, &network_header, nxm_header_len(header)); } void nx_put_header(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, bool masked) { nx_put_header__(b, mf_oxm_header(field, version), masked); } static void nx_put_header_len(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, bool masked, size_t n_bytes) { uint64_t header = mf_oxm_header(field, version); header = NXM_HEADER(nxm_vendor(header), nxm_class(header), nxm_field(header), false, nxm_experimenter_len(header) + n_bytes); nx_put_header__(b, header, masked); } void nx_put_entry(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const union mf_value *value, const union mf_value *mask) { const struct mf_field *mf = mf_from_id(field); bool masked; int len, offset; len = mf_field_len(mf, value, mask, &masked); offset = mf->n_bytes - len; nx_put_header_len(b, field, version, masked, len); ofpbuf_put(b, &value->u8 + offset, len); if (masked) { ofpbuf_put(b, &mask->u8 + offset, len); } } /* nx_match_to_string() and helpers. */ static void format_nxm_field_name(struct ds *, uint64_t header); char * nx_match_to_string(const uint8_t *p, unsigned int match_len) { struct ofpbuf b; struct ds s; if (!match_len) { return xstrdup(""); } ofpbuf_use_const(&b, p, match_len); ds_init(&s); while (b.size) { union mf_value value; union mf_value mask; enum ofperr error; uint64_t header; int value_len; error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask); if (error) { break; } value_len = MIN(sizeof value, nxm_field_bytes(header)); if (s.length) { ds_put_cstr(&s, ", "); } format_nxm_field_name(&s, header); ds_put_char(&s, '('); for (int i = 0; i < value_len; i++) { ds_put_format(&s, "%02x", ((const uint8_t *) &value)[i]); } if (nxm_hasmask(header)) { ds_put_char(&s, '/'); for (int i = 0; i < value_len; i++) { ds_put_format(&s, "%02x", ((const uint8_t *) &mask)[i]); } } ds_put_char(&s, ')'); } if (b.size) { if (s.length) { ds_put_cstr(&s, ", "); } ds_put_format(&s, "<%u invalid bytes>", b.size); } return ds_steal_cstr(&s); } char * oxm_match_to_string(const struct ofpbuf *p, unsigned int match_len) { const struct ofp11_match_header *omh = p->data; uint16_t match_len_; struct ds s; ds_init(&s); if (match_len < sizeof *omh) { ds_put_format(&s, "", match_len); goto err; } if (omh->type != htons(OFPMT_OXM)) { ds_put_format(&s, "", ntohs(omh->type)); goto err; } match_len_ = ntohs(omh->length); if (match_len_ < sizeof *omh) { ds_put_format(&s, "", match_len_); goto err; } if (match_len_ != match_len) { ds_put_format(&s, "", match_len_, match_len); goto err; } return nx_match_to_string(ofpbuf_at(p, sizeof *omh, 0), match_len - sizeof *omh); err: return ds_steal_cstr(&s); } void nx_format_field_name(enum mf_field_id id, enum ofp_version version, struct ds *s) { format_nxm_field_name(s, mf_oxm_header(id, version)); } static void format_nxm_field_name(struct ds *s, uint64_t header) { const struct nxm_field *f = nxm_field_by_header(header); if (f) { ds_put_cstr(s, f->name); if (nxm_hasmask(header)) { ds_put_cstr(s, "_W"); } } else if (header == NXM_NX_COOKIE) { ds_put_cstr(s, "NXM_NX_COOKIE"); } else if (header == NXM_NX_COOKIE_W) { ds_put_cstr(s, "NXM_NX_COOKIE_W"); } else { ds_put_format(s, "%d:%d", nxm_class(header), nxm_field(header)); } } static bool streq_len(const char *a, size_t a_len, const char *b) { return strlen(b) == a_len && !memcmp(a, b, a_len); } static uint64_t parse_nxm_field_name(const char *name, int name_len) { const struct nxm_field *f; bool wild; f = mf_parse_subfield_name(name, name_len, &wild); if (f) { if (!wild) { return f->header; } else if (mf_from_id(f->id)->maskable != MFM_NONE) { return nxm_make_wild_header(f->header); } } if (streq_len(name, name_len, "NXM_NX_COOKIE")) { return NXM_NX_COOKIE; } else if (streq_len(name, name_len, "NXM_NX_COOKIE_W")) { return NXM_NX_COOKIE_W; } /* Check whether it's a field header value as hex. * (This isn't ordinarily useful except for testing error behavior.) */ if (name_len == 8) { uint64_t header; bool ok; header = hexits_value(name, name_len, &ok) << 32; if (ok) { return header; } } else if (name_len == 16) { uint64_t header; bool ok; header = hexits_value(name, name_len, &ok); if (ok && is_experimenter_oxm(header)) { return header; } } return 0; } /* nx_match_from_string(). */ static int nx_match_from_string_raw(const char *s, struct ofpbuf *b) { const char *full_s = s; const size_t start_len = b->size; if (!strcmp(s, "")) { /* Ensure that 'b->data' isn't actually null. */ ofpbuf_prealloc_tailroom(b, 1); return 0; } for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) { const char *name; uint64_t header; ovs_be64 nw_header; ovs_be64 *header_ptr; int name_len; size_t n; name = s; name_len = strcspn(s, "("); if (s[name_len] != '(') { ovs_fatal(0, "%s: missing ( at end of nx_match", full_s); } header = parse_nxm_field_name(name, name_len); if (!header) { ovs_fatal(0, "%s: unknown field `%.*s'", full_s, name_len, s); } s += name_len + 1; header_ptr = ofpbuf_put_uninit(b, nxm_header_len(header)); s = ofpbuf_put_hex(b, s, &n); if (n != nxm_field_bytes(header)) { const struct mf_field *field = mf_from_oxm_header(header); if (field && field->variable_len) { if (n <= field->n_bytes) { int len = (nxm_hasmask(header) ? n * 2 : n) + nxm_experimenter_len(header); header = NXM_HEADER(nxm_vendor(header), nxm_class(header), nxm_field(header), nxm_hasmask(header) ? 1 : 0, len); } else { ovs_fatal(0, "expected to read at most %d bytes but got " "%"PRIuSIZE, field->n_bytes, n); } } else { ovs_fatal(0, "expected to read %d bytes but got %"PRIuSIZE, nxm_field_bytes(header), n); } } nw_header = htonll(header); memcpy(header_ptr, &nw_header, nxm_header_len(header)); if (nxm_hasmask(header)) { s += strspn(s, " "); if (*s != '/') { ovs_fatal(0, "%s: missing / in masked field %.*s", full_s, name_len, name); } s = ofpbuf_put_hex(b, s + 1, &n); if (n != nxm_field_bytes(header)) { ovs_fatal(0, "%.2s: hex digits expected", s); } } s += strspn(s, " "); if (*s != ')') { ovs_fatal(0, "%s: missing ) following field %.*s", full_s, name_len, name); } s++; } return b->size - start_len; } int nx_match_from_string(const char *s, struct ofpbuf *b) { int match_len = nx_match_from_string_raw(s, b); ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8)); return match_len; } int oxm_match_from_string(const char *s, struct ofpbuf *b) { int match_len; struct ofp11_match_header *omh; size_t start_len = b->size; ofpbuf_put_uninit(b, sizeof *omh); match_len = nx_match_from_string_raw(s, b) + sizeof *omh; ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8)); omh = ofpbuf_at(b, start_len, sizeof *omh); omh->type = htons(OFPMT_OXM); omh->length = htons(match_len); return match_len; } /* Parses 's' as a "move" action, in the form described in ovs-ofctl(8), into * '*move'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s) { const char *full_s = s; char *error; error = mf_parse_subfield__(&move->src, &s); if (error) { return error; } if (strncmp(s, "->", 2)) { return xasprintf("%s: missing `->' following source", full_s); } s += 2; error = mf_parse_subfield(&move->dst, s); if (error) { return error; } if (move->src.n_bits != move->dst.n_bits) { return xasprintf("%s: source field is %d bits wide but destination is " "%d bits wide", full_s, move->src.n_bits, move->dst.n_bits); } return NULL; } /* nxm_format_reg_move(). */ void nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s) { ds_put_format(s, "move:"); mf_format_subfield(&move->src, s); ds_put_cstr(s, "->"); mf_format_subfield(&move->dst, s); } enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow) { enum ofperr error; error = mf_check_src(&move->src, flow); if (error) { return error; } return mf_check_dst(&move->dst, flow); } /* nxm_execute_reg_move(). */ void nxm_execute_reg_move(const struct ofpact_reg_move *move, struct flow *flow, struct flow_wildcards *wc) { union mf_value src_value; union mf_value dst_value; mf_mask_field_and_prereqs(move->dst.field, wc); mf_mask_field_and_prereqs(move->src.field, wc); /* A flow may wildcard nw_frag. Do nothing if setting a transport * header field on a packet that does not have them. */ if (mf_are_prereqs_ok(move->dst.field, flow) && mf_are_prereqs_ok(move->src.field, flow)) { mf_get_value(move->dst.field, flow, &dst_value); mf_get_value(move->src.field, flow, &src_value); bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs, &dst_value, move->dst.field->n_bytes, move->dst.ofs, move->src.n_bits); mf_set_flow_value(move->dst.field, &dst_value, flow); } } void nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data, struct flow *flow, struct flow_wildcards *wc) { union mf_subvalue src_subvalue; union mf_subvalue mask_value; ovs_be64 src_data_be = htonll(src_data); memset(&mask_value, 0xff, sizeof mask_value); mf_write_subfield_flow(dst, &mask_value, &wc->masks); bitwise_copy(&src_data_be, sizeof src_data_be, 0, &src_subvalue, sizeof src_subvalue, 0, sizeof src_data_be * 8); mf_write_subfield_flow(dst, &src_subvalue, flow); } /* nxm_parse_stack_action, works for both push() and pop(). */ /* Parses 's' as a "push" or "pop" action, in the form described in * ovs-ofctl(8), into '*stack_action'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s) { char *error; error = mf_parse_subfield__(&stack_action->subfield, &s); if (error) { return error; } if (*s != '\0') { return xasprintf("%s: trailing garbage following push or pop", s); } return NULL; } void nxm_format_stack_push(const struct ofpact_stack *push, struct ds *s) { ds_put_cstr(s, "push:"); mf_format_subfield(&push->subfield, s); } void nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s) { ds_put_cstr(s, "pop:"); mf_format_subfield(&pop->subfield, s); } enum ofperr nxm_stack_push_check(const struct ofpact_stack *push, const struct flow *flow) { return mf_check_src(&push->subfield, flow); } enum ofperr nxm_stack_pop_check(const struct ofpact_stack *pop, const struct flow *flow) { return mf_check_dst(&pop->subfield, flow); } /* nxm_execute_stack_push(), nxm_execute_stack_pop(). */ static void nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v) { ofpbuf_put(stack, v, sizeof *v); } static union mf_subvalue * nx_stack_pop(struct ofpbuf *stack) { union mf_subvalue *v = NULL; if (stack->size) { stack->size -= sizeof *v; v = (union mf_subvalue *) ofpbuf_tail(stack); } return v; } void nxm_execute_stack_push(const struct ofpact_stack *push, const struct flow *flow, struct flow_wildcards *wc, struct ofpbuf *stack) { union mf_subvalue mask_value; union mf_subvalue dst_value; memset(&mask_value, 0xff, sizeof mask_value); mf_write_subfield_flow(&push->subfield, &mask_value, &wc->masks); mf_read_subfield(&push->subfield, flow, &dst_value); nx_stack_push(stack, &dst_value); } void nxm_execute_stack_pop(const struct ofpact_stack *pop, struct flow *flow, struct flow_wildcards *wc, struct ofpbuf *stack) { union mf_subvalue *src_value; src_value = nx_stack_pop(stack); /* Only pop if stack is not empty. Otherwise, give warning. */ if (src_value) { union mf_subvalue mask_value; memset(&mask_value, 0xff, sizeof mask_value); mf_write_subfield_flow(&pop->subfield, &mask_value, &wc->masks); mf_write_subfield_flow(&pop->subfield, src_value, flow); } else { if (!VLOG_DROP_WARN(&rl)) { char *flow_str = flow_to_string(flow); VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow\n" " %s", flow_str); free(flow_str); } } } /* Formats 'sf' into 's' in a format normally acceptable to * mf_parse_subfield(). (It won't be acceptable if sf->field is NULL or if * sf->field has no NXM name.) */ void mf_format_subfield(const struct mf_subfield *sf, struct ds *s) { if (!sf->field) { ds_put_cstr(s, ""); } else { const struct nxm_field *f = nxm_field_by_mf_id(sf->field->id, 0); ds_put_cstr(s, f ? f->name : sf->field->name); } if (sf->field && sf->ofs == 0 && sf->n_bits == sf->field->n_bits) { ds_put_cstr(s, "[]"); } else if (sf->n_bits == 1) { ds_put_format(s, "[%d]", sf->ofs); } else { ds_put_format(s, "[%d..%d]", sf->ofs, sf->ofs + sf->n_bits - 1); } } static const struct nxm_field * mf_parse_subfield_name(const char *name, int name_len, bool *wild) { *wild = name_len > 2 && !memcmp(&name[name_len - 2], "_W", 2); if (*wild) { name_len -= 2; } return nxm_field_by_name(name, name_len); } /* Parses a subfield from the beginning of '*sp' into 'sf'. If successful, * returns NULL and advances '*sp' to the first byte following the parsed * string. On failure, returns a malloc()'d error message, does not modify * '*sp', and does not properly initialize 'sf'. * * The syntax parsed from '*sp' takes the form "header[start..end]" where * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" * may both be omitted (the [] are still required) to indicate an entire * field. */ char * OVS_WARN_UNUSED_RESULT mf_parse_subfield__(struct mf_subfield *sf, const char **sp) { const struct mf_field *field; const struct nxm_field *f; const char *name; int start, end; const char *s; int name_len; bool wild; s = *sp; name = s; name_len = strcspn(s, "["); if (s[name_len] != '[') { return xasprintf("%s: missing [ looking for field name", *sp); } f = mf_parse_subfield_name(name, name_len, &wild); if (!f) { return xasprintf("%s: unknown field `%.*s'", *sp, name_len, s); } field = mf_from_id(f->id); s += name_len; if (ovs_scan(s, "[%d..%d]", &start, &end)) { /* Nothing to do. */ } else if (ovs_scan(s, "[%d]", &start)) { end = start; } else if (!strncmp(s, "[]", 2)) { start = 0; end = field->n_bits - 1; } else { return xasprintf("%s: syntax error expecting [] or [] or " "[..]", *sp); } s = strchr(s, ']') + 1; if (start > end) { return xasprintf("%s: starting bit %d is after ending bit %d", *sp, start, end); } else if (start >= field->n_bits) { return xasprintf("%s: starting bit %d is not valid because field is " "only %d bits wide", *sp, start, field->n_bits); } else if (end >= field->n_bits){ return xasprintf("%s: ending bit %d is not valid because field is " "only %d bits wide", *sp, end, field->n_bits); } sf->field = field; sf->ofs = start; sf->n_bits = end - start + 1; *sp = s; return NULL; } /* Parses a subfield from the entirety of 's' into 'sf'. Returns NULL if * successful, otherwise a malloc()'d string describing the error. The caller * is responsible for freeing the returned string. * * The syntax parsed from 's' takes the form "header[start..end]" where * 'header' is the name of an NXM field and 'start' and 'end' are (inclusive) * bit indexes. "..end" may be omitted to indicate a single bit. "start..end" * may both be omitted (the [] are still required) to indicate an entire * field. */ char * OVS_WARN_UNUSED_RESULT mf_parse_subfield(struct mf_subfield *sf, const char *s) { char *error = mf_parse_subfield__(sf, &s); if (!error && s[0]) { error = xstrdup("unexpected input following field syntax"); } return error; } /* Returns an bitmap in which each bit corresponds to the like-numbered field * in the OFPXMC12_OPENFLOW_BASIC OXM class, in which the bit values are taken * from the 'fields' bitmap. Only fields defined in OpenFlow 'version' are * considered. * * This is useful for encoding OpenFlow 1.2 table stats messages. */ ovs_be64 oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *fields, enum ofp_version version) { uint64_t oxm_bitmap = 0; int i; BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) { uint64_t oxm = mf_oxm_header(i, version); uint32_t class = nxm_class(oxm); int field = nxm_field(oxm); if (class == OFPXMC12_OPENFLOW_BASIC && field < 64) { oxm_bitmap |= UINT64_C(1) << field; } } return htonll(oxm_bitmap); } /* Opposite conversion from oxm_bitmap_from_mf_bitmap(). * * This is useful for decoding OpenFlow 1.2 table stats messages. */ struct mf_bitmap oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, enum ofp_version version) { struct mf_bitmap fields = MF_BITMAP_INITIALIZER; for (enum mf_field_id id = 0; id < MFF_N_IDS; id++) { uint64_t oxm = mf_oxm_header(id, version); if (oxm && version >= nxm_field_by_header(oxm)->version) { uint32_t class = nxm_class(oxm); int field = nxm_field(oxm); if (class == OFPXMC12_OPENFLOW_BASIC && field < 64 && oxm_bitmap & htonll(UINT64_C(1) << field)) { bitmap_set1(fields.bm, id); } } } return fields; } /* Returns a bitmap of fields that can be encoded in OXM and that can be * modified with a "set_field" action. */ struct mf_bitmap oxm_writable_fields(void) { struct mf_bitmap b = MF_BITMAP_INITIALIZER; int i; for (i = 0; i < MFF_N_IDS; i++) { if (mf_oxm_header(i, 0) && mf_from_id(i)->writable) { bitmap_set1(b.bm, i); } } return b; } /* Returns a bitmap of fields that can be encoded in OXM and that can be * matched in a flow table. */ struct mf_bitmap oxm_matchable_fields(void) { struct mf_bitmap b = MF_BITMAP_INITIALIZER; int i; for (i = 0; i < MFF_N_IDS; i++) { if (mf_oxm_header(i, 0)) { bitmap_set1(b.bm, i); } } return b; } /* Returns a bitmap of fields that can be encoded in OXM and that can be * matched in a flow table with an arbitrary bitmask. */ struct mf_bitmap oxm_maskable_fields(void) { struct mf_bitmap b = MF_BITMAP_INITIALIZER; int i; for (i = 0; i < MFF_N_IDS; i++) { if (mf_oxm_header(i, 0) && mf_from_id(i)->maskable == MFM_FULLY) { bitmap_set1(b.bm, i); } } return b; } struct nxm_field_index { struct hmap_node header_node; /* In nxm_header_map. */ struct hmap_node name_node; /* In nxm_name_map. */ struct ovs_list mf_node; /* In mf_mf_map[nf.id]. */ const struct nxm_field nf; }; #include "nx-match.inc" static struct hmap nxm_header_map; static struct hmap nxm_name_map; static struct ovs_list nxm_mf_map[MFF_N_IDS]; static void nxm_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { hmap_init(&nxm_header_map); hmap_init(&nxm_name_map); for (int i = 0; i < MFF_N_IDS; i++) { list_init(&nxm_mf_map[i]); } for (struct nxm_field_index *nfi = all_nxm_fields; nfi < &all_nxm_fields[ARRAY_SIZE(all_nxm_fields)]; nfi++) { hmap_insert(&nxm_header_map, &nfi->header_node, hash_uint64(nxm_no_len(nfi->nf.header))); hmap_insert(&nxm_name_map, &nfi->name_node, hash_string(nfi->nf.name, 0)); list_push_back(&nxm_mf_map[nfi->nf.id], &nfi->mf_node); } ovsthread_once_done(&once); } } static const struct nxm_field * nxm_field_by_header(uint64_t header) { const struct nxm_field_index *nfi; uint64_t header_no_len; nxm_init(); if (nxm_hasmask(header)) { header = nxm_make_exact_header(header); } header_no_len = nxm_no_len(header); HMAP_FOR_EACH_IN_BUCKET (nfi, header_node, hash_uint64(header_no_len), &nxm_header_map) { if (header_no_len == nxm_no_len(nfi->nf.header)) { if (nxm_length(header) == nxm_length(nfi->nf.header) || mf_from_id(nfi->nf.id)->variable_len) { return &nfi->nf; } else { return NULL; } } } return NULL; } static const struct nxm_field * nxm_field_by_name(const char *name, size_t len) { const struct nxm_field_index *nfi; nxm_init(); HMAP_FOR_EACH_WITH_HASH (nfi, name_node, hash_bytes(name, len, 0), &nxm_name_map) { if (strlen(nfi->nf.name) == len && !memcmp(nfi->nf.name, name, len)) { return &nfi->nf; } } return NULL; } static const struct nxm_field * nxm_field_by_mf_id(enum mf_field_id id, enum ofp_version version) { const struct nxm_field_index *nfi; const struct nxm_field *f; nxm_init(); f = NULL; LIST_FOR_EACH (nfi, mf_node, &nxm_mf_map[id]) { if (!f || version >= nfi->nf.version) { f = &nfi->nf; } } return f; } openvswitch-2.5.9/lib/PaxHeaders.82075/string.c0000644000000000000000000000013213534540071016064 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.897854281 openvswitch-2.5.9/lib/string.c0000644000175000017500000000145113534540071017553 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #ifndef HAVE_STRNLEN size_t strnlen(const char *s, size_t maxlen) { const char *end = memchr(s, '\0', maxlen); return end ? end - s : maxlen; } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/dynamic-string.h0000644000000000000000000000013213534540071017513 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.717852954 openvswitch-2.5.9/lib/dynamic-string.h0000644000175000017500000000640513534540071021206 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DYNAMIC_STRING_H #define DYNAMIC_STRING_H 1 #include #include #include #include #include #include #include "compiler.h" /* A "dynamic string", that is, a buffer that can be used to construct a * string across a series of operations that extend or modify it. * * The 'string' member does not always point to a null-terminated string. * Initially it is NULL, and even when it is nonnull, some operations do not * ensure that it is null-terminated. Use ds_cstr() to ensure that memory is * allocated for the string and that it is null-terminated. */ struct ds { char *string; /* Null-terminated string. */ size_t length; /* Bytes used, not including null terminator. */ size_t allocated; /* Bytes allocated, not including null terminator. */ }; #define DS_EMPTY_INITIALIZER { NULL, 0, 0 } void ds_init(struct ds *); void ds_clear(struct ds *); void ds_truncate(struct ds *, size_t new_length); void ds_reserve(struct ds *, size_t min_length); char *ds_put_uninit(struct ds *, size_t n); static inline void ds_put_char(struct ds *, char); void ds_put_utf8(struct ds *, int uc); void ds_put_char_multiple(struct ds *, char, size_t n); void ds_put_buffer(struct ds *, const char *, size_t n); void ds_put_cstr(struct ds *, const char *); void ds_put_and_free_cstr(struct ds *, char *); void ds_put_format(struct ds *, const char *, ...) OVS_PRINTF_FORMAT(2, 3); void ds_put_format_valist(struct ds *, const char *, va_list) OVS_PRINTF_FORMAT(2, 0); void ds_put_printable(struct ds *, const char *, size_t); void ds_put_hex(struct ds *ds, const void *buf, size_t size); void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, uintptr_t ofs, bool ascii); int ds_get_line(struct ds *, FILE *); int ds_get_preprocessed_line(struct ds *, FILE *, int *line_number); int ds_get_test_line(struct ds *, FILE *); void ds_put_strftime_msec(struct ds *, const char *format, long long int when, bool utc); char *xastrftime_msec(const char *format, long long int when, bool utc); char *ds_cstr(struct ds *); const char *ds_cstr_ro(const struct ds *); char *ds_steal_cstr(struct ds *); void ds_destroy(struct ds *); void ds_swap(struct ds *, struct ds *); int ds_last(const struct ds *); void ds_chomp(struct ds *, int c); /* Inline functions. */ void ds_put_char__(struct ds *, char); static inline void ds_put_char(struct ds *ds, char c) { if (ds->length < ds->allocated) { ds->string[ds->length++] = c; ds->string[ds->length] = '\0'; } else { ds_put_char__(ds, c); } } #endif /* dynamic-string.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-clang.h0000644000000000000000000000013213534540071017726 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.809853633 openvswitch-2.5.9/lib/ovs-atomic-clang.h0000644000175000017500000000726313534540071021424 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on Clang. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #define OVS_ATOMIC_CLANG_IMPL 1 #define ATOMIC(TYPE) _Atomic(TYPE) #define ATOMIC_VAR_INIT(VALUE) (VALUE) #define atomic_init(OBJECT, VALUE) __c11_atomic_init(OBJECT, VALUE) /* Clang hard-codes these exact values internally but does not appear to * export any names for them. */ typedef enum { memory_order_relaxed = 0, memory_order_consume = 1, memory_order_acquire = 2, memory_order_release = 3, memory_order_acq_rel = 4, memory_order_seq_cst = 5 } memory_order; #define atomic_thread_fence(ORDER) __c11_atomic_thread_fence(ORDER) #define atomic_signal_fence(ORDER) __c11_atomic_signal_fence(ORDER) #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) #define atomic_store_explicit(DST, SRC, ORDER) \ __c11_atomic_store(DST, SRC, ORDER) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_read_explicit(SRC, DST, ORDER) \ (*(DST) = __c11_atomic_load(SRC, ORDER), \ (void) 0) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \ __c11_atomic_compare_exchange_strong(DST, EXP, SRC, ORD1, ORD2) #define atomic_compare_exchange_weak(DST, EXP, SRC) \ atomic_compare_exchange_weak_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_weak_explicit(DST, EXP, SRC, ORD1, ORD2) \ __c11_atomic_compare_exchange_weak(DST, EXP, SRC, ORD1, ORD2) #define atomic_add(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_sub(RMW, ARG, ORIG) \ atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_or(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_xor(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_and(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = __c11_atomic_fetch_add(RMW, ARG, ORDER), (void) 0) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = __c11_atomic_fetch_sub(RMW, ARG, ORDER), (void) 0) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = __c11_atomic_fetch_or(RMW, ARG, ORDER), (void) 0) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = __c11_atomic_fetch_xor(RMW, ARG, ORDER), (void) 0) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = __c11_atomic_fetch_and(RMW, ARG, ORDER), (void) 0) #include "ovs-atomic-flag-gcc4.7+.h" openvswitch-2.5.9/lib/PaxHeaders.82075/stream.c0000644000000000000000000000013213534540071016051 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.897854281 openvswitch-2.5.9/lib/stream.c0000644000175000017500000005471413534540071017552 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream-provider.h" #include #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "flow.h" #include "jsonrpc.h" #include "ofp-print.h" #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "ovs-thread.h" #include "packets.h" #include "poll-loop.h" #include "random.h" #include "socket-util.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stream); COVERAGE_DEFINE(pstream_open); COVERAGE_DEFINE(stream_open); /* State of an active stream.*/ enum stream_state { SCS_CONNECTING, /* Underlying stream is not connected. */ SCS_CONNECTED, /* Connection established. */ SCS_DISCONNECTED /* Connection failed or connection closed. */ }; static const struct stream_class *stream_classes[] = { &tcp_stream_class, #ifndef _WIN32 &unix_stream_class, #else &windows_stream_class, #endif #ifdef HAVE_OPENSSL &ssl_stream_class, #endif }; static const struct pstream_class *pstream_classes[] = { &ptcp_pstream_class, #ifndef _WIN32 &punix_pstream_class, #else &pwindows_pstream_class, #endif #ifdef HAVE_OPENSSL &pssl_pstream_class, #endif }; /* Check the validity of the stream class structures. */ static void check_stream_classes(void) { #ifndef NDEBUG size_t i; for (i = 0; i < ARRAY_SIZE(stream_classes); i++) { const struct stream_class *class = stream_classes[i]; ovs_assert(class->name != NULL); ovs_assert(class->open != NULL); if (class->close || class->recv || class->send || class->run || class->run_wait || class->wait) { ovs_assert(class->close != NULL); ovs_assert(class->recv != NULL); ovs_assert(class->send != NULL); ovs_assert(class->wait != NULL); } else { /* This class delegates to another one. */ } } for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) { const struct pstream_class *class = pstream_classes[i]; ovs_assert(class->name != NULL); ovs_assert(class->listen != NULL); if (class->close || class->accept || class->wait) { ovs_assert(class->close != NULL); ovs_assert(class->accept != NULL); ovs_assert(class->wait != NULL); } else { /* This class delegates to another one. */ } } #endif } /* Prints information on active (if 'active') and passive (if 'passive') * connection methods supported by the stream. */ void stream_usage(const char *name, bool active, bool passive, bool bootstrap OVS_UNUSED) { /* Really this should be implemented via callbacks into the stream * providers, but that seems too heavy-weight to bother with at the * moment. */ printf("\n"); if (active) { printf("Active %s connection methods:\n", name); printf(" tcp:IP:PORT " "PORT at remote IP\n"); #ifdef HAVE_OPENSSL printf(" ssl:IP:PORT " "SSL PORT at remote IP\n"); #endif printf(" unix:FILE " "Unix domain socket named FILE\n"); } if (passive) { printf("Passive %s connection methods:\n", name); printf(" ptcp:PORT[:IP] " "listen to TCP PORT on IP\n"); #ifdef HAVE_OPENSSL printf(" pssl:PORT[:IP] " "listen for SSL on PORT on IP\n"); #endif printf(" punix:FILE " "listen on Unix domain socket FILE\n"); } #ifdef HAVE_OPENSSL printf("PKI configuration (required to use SSL):\n" " -p, --private-key=FILE file with private key\n" " -c, --certificate=FILE file with certificate for private key\n" " -C, --ca-cert=FILE file with peer CA certificate\n"); if (bootstrap) { printf(" --bootstrap-ca-cert=FILE file with peer CA certificate " "to read or create\n"); } #endif } /* Given 'name', a stream name in the form "TYPE:ARGS", stores the class * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores * a null pointer into '*classp' if 'name' is in the wrong form or if no such * class exists. */ static int stream_lookup_class(const char *name, const struct stream_class **classp) { size_t prefix_len; size_t i; check_stream_classes(); *classp = NULL; prefix_len = strcspn(name, ":"); if (name[prefix_len] == '\0') { return EAFNOSUPPORT; } for (i = 0; i < ARRAY_SIZE(stream_classes); i++) { const struct stream_class *class = stream_classes[i]; if (strlen(class->name) == prefix_len && !memcmp(class->name, name, prefix_len)) { *classp = class; return 0; } } return EAFNOSUPPORT; } /* Returns 0 if 'name' is a stream name in the form "TYPE:ARGS" and TYPE is * a supported stream type, otherwise EAFNOSUPPORT. */ int stream_verify_name(const char *name) { const struct stream_class *class; return stream_lookup_class(name, &class); } /* Attempts to connect a stream to a remote peer. 'name' is a connection name * in the form "TYPE:ARGS", where TYPE is an active stream class's name and * ARGS are stream class-specific. * * Returns 0 if successful, otherwise a positive errno value. If successful, * stores a pointer to the new connection in '*streamp', otherwise a null * pointer. */ int stream_open(const char *name, struct stream **streamp, uint8_t dscp) { const struct stream_class *class; struct stream *stream; char *suffix_copy; int error; COVERAGE_INC(stream_open); /* Look up the class. */ error = stream_lookup_class(name, &class); if (!class) { goto error; } /* Call class's "open" function. */ suffix_copy = xstrdup(strchr(name, ':') + 1); error = class->open(name, suffix_copy, &stream, dscp); free(suffix_copy); if (error) { goto error; } /* Success. */ *streamp = stream; return 0; error: *streamp = NULL; return error; } /* Blocks until a previously started stream connection attempt succeeds or * fails. 'error' should be the value returned by stream_open() and 'streamp' * should point to the stream pointer set by stream_open(). Returns 0 if * successful, otherwise a positive errno value other than EAGAIN or * EINPROGRESS. If successful, leaves '*streamp' untouched; on error, closes * '*streamp' and sets '*streamp' to null. * * Typical usage: * error = stream_open_block(stream_open("tcp:1.2.3.4:5", &stream), &stream); */ int stream_open_block(int error, struct stream **streamp) { struct stream *stream = *streamp; fatal_signal_run(); if (!error) { while ((error = stream_connect(stream)) == EAGAIN) { stream_run(stream); stream_run_wait(stream); stream_connect_wait(stream); poll_block(); } ovs_assert(error != EINPROGRESS); } if (error) { stream_close(stream); *streamp = NULL; } else { *streamp = stream; } return error; } /* Closes 'stream'. */ void stream_close(struct stream *stream) { if (stream != NULL) { char *name = stream->name; (stream->class->close)(stream); free(name); } } /* Returns the name of 'stream', that is, the string passed to * stream_open(). */ const char * stream_get_name(const struct stream *stream) { return stream ? stream->name : "(null)"; } static void scs_connecting(struct stream *stream) { int retval = (stream->class->connect)(stream); ovs_assert(retval != EINPROGRESS); if (!retval) { stream->state = SCS_CONNECTED; } else if (retval != EAGAIN) { stream->state = SCS_DISCONNECTED; stream->error = retval; } } /* Tries to complete the connection on 'stream'. If 'stream''s connection is * complete, returns 0 if the connection was successful or a positive errno * value if it failed. If the connection is still in progress, returns * EAGAIN. */ int stream_connect(struct stream *stream) { enum stream_state last_state; do { last_state = stream->state; switch (stream->state) { case SCS_CONNECTING: scs_connecting(stream); break; case SCS_CONNECTED: return 0; case SCS_DISCONNECTED: return stream->error; default: OVS_NOT_REACHED(); } } while (stream->state != last_state); return EAGAIN; } /* Tries to receive up to 'n' bytes from 'stream' into 'buffer', and returns: * * - If successful, the number of bytes received (between 1 and 'n'). * * - On error, a negative errno value. * * - 0, if the connection has been closed in the normal fashion, or if 'n' * is zero. * * The recv function will not block waiting for a packet to arrive. If no * data have been received, it returns -EAGAIN immediately. */ int stream_recv(struct stream *stream, void *buffer, size_t n) { int retval = stream_connect(stream); return (retval ? -retval : n == 0 ? 0 : (stream->class->recv)(stream, buffer, n)); } /* Tries to send up to 'n' bytes of 'buffer' on 'stream', and returns: * * - If successful, the number of bytes sent (between 1 and 'n'). 0 is * only a valid return value if 'n' is 0. * * - On error, a negative errno value. * * The send function will not block. If no bytes can be immediately accepted * for transmission, it returns -EAGAIN immediately. */ int stream_send(struct stream *stream, const void *buffer, size_t n) { int retval = stream_connect(stream); return (retval ? -retval : n == 0 ? 0 : (stream->class->send)(stream, buffer, n)); } /* Allows 'stream' to perform maintenance activities, such as flushing * output buffers. */ void stream_run(struct stream *stream) { if (stream->class->run) { (stream->class->run)(stream); } } /* Arranges for the poll loop to wake up when 'stream' needs to perform * maintenance activities. */ void stream_run_wait(struct stream *stream) { if (stream->class->run_wait) { (stream->class->run_wait)(stream); } } /* Arranges for the poll loop to wake up when 'stream' is ready to take an * action of the given 'type'. */ void stream_wait(struct stream *stream, enum stream_wait_type wait) { ovs_assert(wait == STREAM_CONNECT || wait == STREAM_RECV || wait == STREAM_SEND); switch (stream->state) { case SCS_CONNECTING: wait = STREAM_CONNECT; break; case SCS_DISCONNECTED: poll_immediate_wake(); return; } (stream->class->wait)(stream, wait); } void stream_connect_wait(struct stream *stream) { stream_wait(stream, STREAM_CONNECT); } void stream_recv_wait(struct stream *stream) { stream_wait(stream, STREAM_RECV); } void stream_send_wait(struct stream *stream) { stream_wait(stream, STREAM_SEND); } /* Given 'name', a pstream name in the form "TYPE:ARGS", stores the class * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores * a null pointer into '*classp' if 'name' is in the wrong form or if no such * class exists. */ static int pstream_lookup_class(const char *name, const struct pstream_class **classp) { size_t prefix_len; size_t i; check_stream_classes(); *classp = NULL; prefix_len = strcspn(name, ":"); if (name[prefix_len] == '\0') { return EAFNOSUPPORT; } for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) { const struct pstream_class *class = pstream_classes[i]; if (strlen(class->name) == prefix_len && !memcmp(class->name, name, prefix_len)) { *classp = class; return 0; } } return EAFNOSUPPORT; } /* Returns 0 if 'name' is a pstream name in the form "TYPE:ARGS" and TYPE is * a supported pstream type, otherwise EAFNOSUPPORT. */ int pstream_verify_name(const char *name) { const struct pstream_class *class; return pstream_lookup_class(name, &class); } /* Returns 1 if the stream or pstream specified by 'name' needs periodic probes * to verify connectivity. For [p]streams which need probes, it can take a * long time to notice the connection has been dropped. Returns 0 if the * stream or pstream does not need probes, and -1 if 'name' is not valid. */ int stream_or_pstream_needs_probes(const char *name) { const struct pstream_class *pclass; const struct stream_class *class; if (!stream_lookup_class(name, &class)) { return class->needs_probes; } else if (!pstream_lookup_class(name, &pclass)) { return pclass->needs_probes; } else { return -1; } } /* Attempts to start listening for remote stream connections. 'name' is a * connection name in the form "TYPE:ARGS", where TYPE is an passive stream * class's name and ARGS are stream class-specific. * * Returns 0 if successful, otherwise a positive errno value. If successful, * stores a pointer to the new connection in '*pstreamp', otherwise a null * pointer. */ int pstream_open(const char *name, struct pstream **pstreamp, uint8_t dscp) { const struct pstream_class *class; struct pstream *pstream; char *suffix_copy; int error; COVERAGE_INC(pstream_open); /* Look up the class. */ error = pstream_lookup_class(name, &class); if (!class) { goto error; } /* Call class's "open" function. */ suffix_copy = xstrdup(strchr(name, ':') + 1); error = class->listen(name, suffix_copy, &pstream, dscp); free(suffix_copy); if (error) { goto error; } /* Success. */ *pstreamp = pstream; return 0; error: *pstreamp = NULL; return error; } /* Returns the name that was used to open 'pstream'. The caller must not * modify or free the name. */ const char * pstream_get_name(const struct pstream *pstream) { return pstream->name; } /* Closes 'pstream'. */ void pstream_close(struct pstream *pstream) { if (pstream != NULL) { char *name = pstream->name; (pstream->class->close)(pstream); free(name); } } /* Tries to accept a new connection on 'pstream'. If successful, stores the * new connection in '*new_stream' and returns 0. Otherwise, returns a * positive errno value. * * pstream_accept() will not block waiting for a connection. If no connection * is ready to be accepted, it returns EAGAIN immediately. */ int pstream_accept(struct pstream *pstream, struct stream **new_stream) { int retval = (pstream->class->accept)(pstream, new_stream); if (retval) { *new_stream = NULL; } else { ovs_assert((*new_stream)->state != SCS_CONNECTING || (*new_stream)->class->connect); } return retval; } /* Tries to accept a new connection on 'pstream'. If successful, stores the * new connection in '*new_stream' and returns 0. Otherwise, returns a * positive errno value. * * pstream_accept_block() blocks until a connection is ready or until an error * occurs. It will not return EAGAIN. */ int pstream_accept_block(struct pstream *pstream, struct stream **new_stream) { int error; fatal_signal_run(); while ((error = pstream_accept(pstream, new_stream)) == EAGAIN) { pstream_wait(pstream); poll_block(); } if (error) { *new_stream = NULL; } return error; } void pstream_wait(struct pstream *pstream) { (pstream->class->wait)(pstream); } /* Returns the transport port on which 'pstream' is listening, or 0 if the * concept doesn't apply. */ ovs_be16 pstream_get_bound_port(const struct pstream *pstream) { return pstream->bound_port; } /* Initializes 'stream' as a new stream named 'name', implemented via 'class'. * The initial connection status, supplied as 'connect_status', is interpreted * as follows: * * - 0: 'stream' is connected. Its 'send' and 'recv' functions may be * called in the normal fashion. * * - EAGAIN: 'stream' is trying to complete a connection. Its 'connect' * function should be called to complete the connection. * * - Other positive errno values indicate that the connection failed with * the specified error. * * After calling this function, stream_close() must be used to destroy * 'stream', otherwise resources will be leaked. * * The caller retains ownership of 'name'. */ void stream_init(struct stream *stream, const struct stream_class *class, int connect_status, const char *name) { memset(stream, 0, sizeof *stream); stream->class = class; stream->state = (connect_status == EAGAIN ? SCS_CONNECTING : !connect_status ? SCS_CONNECTED : SCS_DISCONNECTED); stream->error = connect_status; stream->name = xstrdup(name); ovs_assert(stream->state != SCS_CONNECTING || class->connect); } void pstream_init(struct pstream *pstream, const struct pstream_class *class, const char *name) { memset(pstream, 0, sizeof *pstream); pstream->class = class; pstream->name = xstrdup(name); } void pstream_set_bound_port(struct pstream *pstream, ovs_be16 port) { pstream->bound_port = port; } static int count_fields(const char *s_) { char *s, *field, *save_ptr; int n = 0; save_ptr = NULL; s = xstrdup(s_); for (field = strtok_r(s, ":", &save_ptr); field != NULL; field = strtok_r(NULL, ":", &save_ptr)) { n++; } free(s); return n; } /* Like stream_open(), but the port defaults to 'default_port' if no port * number is given. */ int stream_open_with_default_port(const char *name_, uint16_t default_port, struct stream **streamp, uint8_t dscp) { char *name; int error; if ((!strncmp(name_, "tcp:", 4) || !strncmp(name_, "ssl:", 4)) && count_fields(name_) < 3) { if (default_port == OFP_PORT) { VLOG_WARN_ONCE("The default OpenFlow port number has changed " "from %d to %d", OFP_OLD_PORT, OFP_PORT); } else if (default_port == OVSDB_PORT) { VLOG_WARN_ONCE("The default OVSDB port number has changed " "from %d to %d", OVSDB_OLD_PORT, OVSDB_PORT); } name = xasprintf("%s:%d", name_, default_port); } else { name = xstrdup(name_); } error = stream_open(name, streamp, dscp); free(name); return error; } /* Like pstream_open(), but port defaults to 'default_port' if no port * number is given. */ int pstream_open_with_default_port(const char *name_, uint16_t default_port, struct pstream **pstreamp, uint8_t dscp) { char *name; int error; if ((!strncmp(name_, "ptcp:", 5) || !strncmp(name_, "pssl:", 5)) && count_fields(name_) < 2) { name = xasprintf("%s%d", name_, default_port); } else { name = xstrdup(name_); } error = pstream_open(name, pstreamp, dscp); free(name); return error; } /* * This function extracts IP address and port from the target string. * * - On success, function returns true and fills *ss structure with port * and IP address. If port was absent in target string then it will use * corresponding default port value. * - On error, function returns false and *ss contains garbage. */ bool stream_parse_target_with_default_port(const char *target, uint16_t default_port, struct sockaddr_storage *ss) { return ((!strncmp(target, "tcp:", 4) || !strncmp(target, "ssl:", 4)) && inet_parse_active(target + 4, default_port, ss)); } /* Attempts to guess the content type of a stream whose first few bytes were * the 'size' bytes of 'data'. */ static enum stream_content_type stream_guess_content(const uint8_t *data, ssize_t size) { if (size >= 2) { #define PAIR(A, B) (((A) << 8) | (B)) switch (PAIR(data[0], data[1])) { case PAIR(0x16, 0x03): /* Handshake, version 3. */ return STREAM_SSL; case PAIR('{', '"'): return STREAM_JSONRPC; case PAIR(OFP10_VERSION, 0 /* OFPT_HELLO */): return STREAM_OPENFLOW; } } return STREAM_UNKNOWN; } /* Returns a string represenation of 'type'. */ static const char * stream_content_type_to_string(enum stream_content_type type) { switch (type) { case STREAM_UNKNOWN: default: return "unknown"; case STREAM_JSONRPC: return "JSON-RPC"; case STREAM_OPENFLOW: return "OpenFlow"; case STREAM_SSL: return "SSL"; } } /* Attempts to guess the content type of a stream whose first few bytes were * the 'size' bytes of 'data'. If this is done successfully, and the guessed * content type is other than 'expected_type', then log a message in vlog * module 'module', naming 'stream_name' as the source, explaining what * content was expected and what was actually received. */ void stream_report_content(const void *data, ssize_t size, enum stream_content_type expected_type, struct vlog_module *module, const char *stream_name) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); enum stream_content_type actual_type; actual_type = stream_guess_content(data, size); if (actual_type != expected_type && actual_type != STREAM_UNKNOWN) { vlog_rate_limit(module, VLL_WARN, &rl, "%s: received %s data on %s channel", stream_name, stream_content_type_to_string(actual_type), stream_content_type_to_string(expected_type)); } } openvswitch-2.5.9/lib/PaxHeaders.82075/memory-unixctl.man0000644000000000000000000000013213534540071020103 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801423.729845671 openvswitch-2.5.9/lib/memory-unixctl.man0000644000175000017500000000040113534540071021564 0ustar00jpettitjpettit00000000000000.SS "MEMORY COMMANDS" These commands report memory usage. . .IP "\fBmemory/show\fR" Displays some basic statistics about \fB\*(PN\fR's memory usage. \fB\*(PN\fR also logs this information soon after startup and periodically as its memory consumption grows. openvswitch-2.5.9/lib/PaxHeaders.82075/meta-flow.c0000644000000000000000000000013213534540071016451 xustar0030 mtime=1567801401.417681288 30 atime=1567801402.077686135 30 ctime=1567801424.769853338 openvswitch-2.5.9/lib/meta-flow.c0000644000175000017500000021131713534540071020144 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "meta-flow.h" #include #include #include #include #include "classifier.h" #include "dynamic-string.h" #include "nx-match.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ovs-thread.h" #include "packets.h" #include "random.h" #include "shash.h" #include "socket-util.h" #include "tun-metadata.h" #include "unaligned.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(meta_flow); #define FLOW_U32OFS(FIELD) \ offsetof(struct flow, FIELD) % 4 ? -1 : offsetof(struct flow, FIELD) / 4 #define MF_FIELD_SIZES(MEMBER) \ sizeof ((union mf_value *)0)->MEMBER, \ 8 * sizeof ((union mf_value *)0)->MEMBER extern const struct mf_field mf_fields[MFF_N_IDS]; /* Silence a warning. */ const struct mf_field mf_fields[MFF_N_IDS] = { #include "meta-flow.inc" }; /* Maps from an mf_field's 'name' or 'extra_name' to the mf_field. */ static struct shash mf_by_name; /* Rate limit for parse errors. These always indicate a bug in an OpenFlow * controller and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); #define MF_VALUE_EXACT_8 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff #define MF_VALUE_EXACT_16 MF_VALUE_EXACT_8, MF_VALUE_EXACT_8 #define MF_VALUE_EXACT_32 MF_VALUE_EXACT_16, MF_VALUE_EXACT_16 #define MF_VALUE_EXACT_64 MF_VALUE_EXACT_32, MF_VALUE_EXACT_32 #define MF_VALUE_EXACT_128 MF_VALUE_EXACT_64, MF_VALUE_EXACT_64 #define MF_VALUE_EXACT_INITIALIZER { .tun_metadata = { MF_VALUE_EXACT_128 } } const union mf_value exact_match_mask = MF_VALUE_EXACT_INITIALIZER; static void nxm_init(void); /* Returns the field with the given 'name', or a null pointer if no field has * that name. */ const struct mf_field * mf_from_name(const char *name) { nxm_init(); return shash_find_data(&mf_by_name, name); } static void nxm_do_init(void) { int i; shash_init(&mf_by_name); for (i = 0; i < MFF_N_IDS; i++) { const struct mf_field *mf = &mf_fields[i]; ovs_assert(mf->id == i); /* Fields must be in the enum order. */ shash_add_once(&mf_by_name, mf->name, mf); if (mf->extra_name) { shash_add_once(&mf_by_name, mf->extra_name, mf); } } } static void nxm_init(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, nxm_do_init); } /* Consider the two value/mask pairs 'a_value/a_mask' and 'b_value/b_mask' as * restrictions on a field's value. Then, this function initializes * 'dst_value/dst_mask' such that it combines the restrictions of both pairs. * This is not always possible, i.e. if one pair insists on a value of 0 in * some bit and the other pair insists on a value of 1 in that bit. This * function returns false in a case where the combined restriction is * impossible (in which case 'dst_value/dst_mask' is not fully initialized), * true otherwise. * * (As usually true for value/mask pairs in OVS, any 1-bit in a value must have * a corresponding 1-bit in its mask.) */ bool mf_subvalue_intersect(const union mf_subvalue *a_value, const union mf_subvalue *a_mask, const union mf_subvalue *b_value, const union mf_subvalue *b_mask, union mf_subvalue *dst_value, union mf_subvalue *dst_mask) { for (int i = 0; i < ARRAY_SIZE(a_value->be64); i++) { ovs_be64 av = a_value->be64[i]; ovs_be64 am = a_mask->be64[i]; ovs_be64 bv = b_value->be64[i]; ovs_be64 bm = b_mask->be64[i]; ovs_be64 *dv = &dst_value->be64[i]; ovs_be64 *dm = &dst_mask->be64[i]; if ((av ^ bv) & (am & bm)) { return false; } *dv = av | bv; *dm = am | bm; } return true; } /* Returns the "number of bits" in 'v', e.g. 1 if only the lowest-order bit is * set, 2 if the second-lowest-order bit is set, and so on. */ int mf_subvalue_width(const union mf_subvalue *v) { return 1 + bitwise_rscan(v, sizeof *v, true, sizeof *v * 8 - 1, -1); } /* For positive 'n', shifts the bits in 'value' 'n' bits to the left, and for * negative 'n', shifts the bits '-n' bits to the right. */ void mf_subvalue_shift(union mf_subvalue *value, int n) { if (n) { union mf_subvalue tmp; memset(&tmp, 0, sizeof tmp); if (n > 0 && n < 8 * sizeof tmp) { bitwise_copy(value, sizeof *value, 0, &tmp, sizeof tmp, n, 8 * sizeof tmp - n); } else if (n < 0 && n > -8 * sizeof tmp) { bitwise_copy(value, sizeof *value, -n, &tmp, sizeof tmp, 0, 8 * sizeof tmp + n); } *value = tmp; } } /* Returns true if 'wc' wildcards all the bits in field 'mf', false if 'wc' * specifies at least one bit in the field. * * The caller is responsible for ensuring that 'wc' corresponds to a flow that * meets 'mf''s prerequisites. */ bool mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) { switch (mf->id) { case MFF_DP_HASH: return !wc->masks.dp_hash; case MFF_RECIRC_ID: return !wc->masks.recirc_id; case MFF_CONJ_ID: return !wc->masks.conj_id; case MFF_TUN_SRC: return !wc->masks.tunnel.ip_src; case MFF_TUN_DST: return !wc->masks.tunnel.ip_dst; case MFF_TUN_IPV6_SRC: return ipv6_mask_is_any(&wc->masks.tunnel.ipv6_src); case MFF_TUN_IPV6_DST: return ipv6_mask_is_any(&wc->masks.tunnel.ipv6_dst); case MFF_TUN_ID: return !wc->masks.tunnel.tun_id; case MFF_TUN_TOS: return !wc->masks.tunnel.ip_tos; case MFF_TUN_TTL: return !wc->masks.tunnel.ip_ttl; case MFF_TUN_FLAGS: return !(wc->masks.tunnel.flags & FLOW_TNL_PUB_F_MASK); case MFF_TUN_GBP_ID: return !wc->masks.tunnel.gbp_id; case MFF_TUN_GBP_FLAGS: return !wc->masks.tunnel.gbp_flags; CASE_MFF_TUN_METADATA: return !ULLONG_GET(wc->masks.tunnel.metadata.present.map, mf->id - MFF_TUN_METADATA0); case MFF_METADATA: return !wc->masks.metadata; case MFF_IN_PORT: case MFF_IN_PORT_OXM: return !wc->masks.in_port.ofp_port; case MFF_SKB_PRIORITY: return !wc->masks.skb_priority; case MFF_PKT_MARK: return !wc->masks.pkt_mark; case MFF_CT_STATE: return !wc->masks.ct_state; case MFF_CT_ZONE: return !wc->masks.ct_zone; case MFF_CT_MARK: return !wc->masks.ct_mark; case MFF_CT_LABEL: return ovs_u128_is_zero(&wc->masks.ct_label); CASE_MFF_REGS: return !wc->masks.regs[mf->id - MFF_REG0]; CASE_MFF_XREGS: return !flow_get_xreg(&wc->masks, mf->id - MFF_XREG0); case MFF_ACTSET_OUTPUT: return !wc->masks.actset_output; case MFF_ETH_SRC: return eth_addr_is_zero(wc->masks.dl_src); case MFF_ETH_DST: return eth_addr_is_zero(wc->masks.dl_dst); case MFF_ETH_TYPE: return !wc->masks.dl_type; case MFF_ARP_SHA: case MFF_ND_SLL: return eth_addr_is_zero(wc->masks.arp_sha); case MFF_ARP_THA: case MFF_ND_TLL: return eth_addr_is_zero(wc->masks.arp_tha); case MFF_VLAN_TCI: return !wc->masks.vlan_tci; case MFF_DL_VLAN: return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK)); case MFF_VLAN_VID: return !(wc->masks.vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI)); case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK)); case MFF_MPLS_LABEL: return !(wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)); case MFF_MPLS_TC: return !(wc->masks.mpls_lse[0] & htonl(MPLS_TC_MASK)); case MFF_MPLS_BOS: return !(wc->masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)); case MFF_IPV4_SRC: return !wc->masks.nw_src; case MFF_IPV4_DST: return !wc->masks.nw_dst; case MFF_IPV6_SRC: return ipv6_mask_is_any(&wc->masks.ipv6_src); case MFF_IPV6_DST: return ipv6_mask_is_any(&wc->masks.ipv6_dst); case MFF_IPV6_LABEL: return !wc->masks.ipv6_label; case MFF_IP_PROTO: return !wc->masks.nw_proto; case MFF_IP_DSCP: case MFF_IP_DSCP_SHIFTED: return !(wc->masks.nw_tos & IP_DSCP_MASK); case MFF_IP_ECN: return !(wc->masks.nw_tos & IP_ECN_MASK); case MFF_IP_TTL: return !wc->masks.nw_ttl; case MFF_ND_TARGET: return ipv6_mask_is_any(&wc->masks.nd_target); case MFF_IP_FRAG: return !(wc->masks.nw_frag & FLOW_NW_FRAG_MASK); case MFF_ARP_OP: return !wc->masks.nw_proto; case MFF_ARP_SPA: return !wc->masks.nw_src; case MFF_ARP_TPA: return !wc->masks.nw_dst; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: case MFF_ICMPV4_TYPE: case MFF_ICMPV6_TYPE: return !wc->masks.tp_src; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: case MFF_ICMPV4_CODE: case MFF_ICMPV6_CODE: return !wc->masks.tp_dst; case MFF_TCP_FLAGS: return !wc->masks.tcp_flags; case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Initializes 'mask' with the wildcard bit pattern for field 'mf' within 'wc'. * Each bit in 'mask' will be set to 1 if the bit is significant for matching * purposes, or to 0 if it is wildcarded. * * The caller is responsible for ensuring that 'wc' corresponds to a flow that * meets 'mf''s prerequisites. */ void mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc, union mf_value *mask) { mf_get_value(mf, &wc->masks, mask); } /* Tests whether 'mask' is a valid wildcard bit pattern for 'mf'. Returns true * if the mask is valid, false otherwise. */ bool mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask) { switch (mf->maskable) { case MFM_NONE: return (is_all_zeros(mask, mf->n_bytes) || is_all_ones(mask, mf->n_bytes)); case MFM_FULLY: return true; } OVS_NOT_REACHED(); } /* Returns true if 'flow' meets the prerequisites for 'mf', false otherwise. */ bool mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow) { switch (mf->prereqs) { case MFP_NONE: return true; case MFP_ARP: return (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)); case MFP_IPV4: return flow->dl_type == htons(ETH_TYPE_IP); case MFP_IPV6: return flow->dl_type == htons(ETH_TYPE_IPV6); case MFP_VLAN_VID: return (flow->vlan_tci & htons(VLAN_CFI)) != 0; case MFP_MPLS: return eth_type_mpls(flow->dl_type); case MFP_IP_ANY: return is_ip_any(flow); case MFP_TCP: return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP && !(flow->nw_frag & FLOW_NW_FRAG_LATER); case MFP_UDP: return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP && !(flow->nw_frag & FLOW_NW_FRAG_LATER); case MFP_SCTP: return is_ip_any(flow) && flow->nw_proto == IPPROTO_SCTP && !(flow->nw_frag & FLOW_NW_FRAG_LATER); case MFP_ICMPV4: return is_icmpv4(flow, NULL); case MFP_ICMPV6: return is_icmpv6(flow, NULL); case MFP_ND: return (is_icmpv6(flow, NULL) && flow->tp_dst == htons(0) && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || flow->tp_src == htons(ND_NEIGHBOR_ADVERT))); case MFP_ND_SOLICIT: return (is_icmpv6(flow, NULL) && flow->tp_dst == htons(0) && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT))); case MFP_ND_ADVERT: return (is_icmpv6(flow, NULL) && flow->tp_dst == htons(0) && (flow->tp_src == htons(ND_NEIGHBOR_ADVERT))); } OVS_NOT_REACHED(); } /* Set field and it's prerequisities in the mask. * This is only ever called for writeable 'mf's, but we do not make the * distinction here. */ void mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow_wildcards *wc) { mf_mask_field_and_prereqs__(mf, &exact_match_mask, wc); } void mf_mask_field_and_prereqs__(const struct mf_field *mf, const union mf_value *mask, struct flow_wildcards *wc) { mf_set_flow_value_masked(mf, &exact_match_mask, mask, &wc->masks); switch (mf->prereqs) { case MFP_ND: case MFP_ND_SOLICIT: case MFP_ND_ADVERT: WC_MASK_FIELD(wc, tp_src); WC_MASK_FIELD(wc, tp_dst); /* Fall through. */ case MFP_TCP: case MFP_UDP: case MFP_SCTP: case MFP_ICMPV4: case MFP_ICMPV6: /* nw_frag always unwildcarded. */ WC_MASK_FIELD(wc, nw_proto); /* Fall through. */ case MFP_ARP: case MFP_IPV4: case MFP_IPV6: case MFP_MPLS: case MFP_IP_ANY: /* dl_type always unwildcarded. */ break; case MFP_VLAN_VID: WC_MASK_FIELD_MASK(wc, vlan_tci, htons(VLAN_CFI)); break; case MFP_NONE: break; } } /* Set bits of 'bm' corresponding to the field 'mf' and it's prerequisities. */ void mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap *bm) { bitmap_set1(bm->bm, mf->id); switch (mf->prereqs) { case MFP_ND: case MFP_ND_SOLICIT: case MFP_ND_ADVERT: bitmap_set1(bm->bm, MFF_TCP_SRC); bitmap_set1(bm->bm, MFF_TCP_DST); /* Fall through. */ case MFP_TCP: case MFP_UDP: case MFP_SCTP: case MFP_ICMPV4: case MFP_ICMPV6: /* nw_frag always unwildcarded. */ bitmap_set1(bm->bm, MFF_IP_PROTO); /* Fall through. */ case MFP_ARP: case MFP_IPV4: case MFP_IPV6: case MFP_MPLS: case MFP_IP_ANY: bitmap_set1(bm->bm, MFF_ETH_TYPE); break; case MFP_VLAN_VID: bitmap_set1(bm->bm, MFF_VLAN_TCI); break; case MFP_NONE: break; } } /* Returns true if 'value' may be a valid value *as part of a masked match*, * false otherwise. * * A value is not rejected just because it is not valid for the field in * question, but only if it doesn't make sense to test the bits in question at * all. For example, the MFF_VLAN_TCI field will never have a nonzero value * without the VLAN_CFI bit being set, but we can't reject those values because * it is still legitimate to test just for those bits (see the documentation * for NXM_OF_VLAN_TCI in nicira-ext.h). On the other hand, there is never a * reason to set the low bit of MFF_IP_DSCP to 1, so we reject that. */ bool mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) { switch (mf->id) { case MFF_DP_HASH: case MFF_RECIRC_ID: case MFF_CONJ_ID: case MFF_TUN_ID: case MFF_TUN_SRC: case MFF_TUN_DST: case MFF_TUN_IPV6_SRC: case MFF_TUN_IPV6_DST: case MFF_TUN_TOS: case MFF_TUN_TTL: case MFF_TUN_GBP_ID: case MFF_TUN_GBP_FLAGS: CASE_MFF_TUN_METADATA: case MFF_METADATA: case MFF_IN_PORT: case MFF_SKB_PRIORITY: case MFF_PKT_MARK: case MFF_CT_ZONE: case MFF_CT_MARK: case MFF_CT_LABEL: CASE_MFF_REGS: CASE_MFF_XREGS: case MFF_ETH_SRC: case MFF_ETH_DST: case MFF_ETH_TYPE: case MFF_VLAN_TCI: case MFF_IPV4_SRC: case MFF_IPV4_DST: case MFF_IPV6_SRC: case MFF_IPV6_DST: case MFF_IP_PROTO: case MFF_IP_TTL: case MFF_ARP_SPA: case MFF_ARP_TPA: case MFF_ARP_SHA: case MFF_ARP_THA: case MFF_TCP_SRC: case MFF_TCP_DST: case MFF_UDP_SRC: case MFF_UDP_DST: case MFF_SCTP_SRC: case MFF_SCTP_DST: case MFF_ICMPV4_TYPE: case MFF_ICMPV4_CODE: case MFF_ICMPV6_TYPE: case MFF_ICMPV6_CODE: case MFF_ND_TARGET: case MFF_ND_SLL: case MFF_ND_TLL: return true; case MFF_IN_PORT_OXM: case MFF_ACTSET_OUTPUT: { ofp_port_t port; return !ofputil_port_from_ofp11(value->be32, &port); } case MFF_IP_DSCP: return !(value->u8 & ~IP_DSCP_MASK); case MFF_IP_DSCP_SHIFTED: return !(value->u8 & (~IP_DSCP_MASK >> 2)); case MFF_IP_ECN: return !(value->u8 & ~IP_ECN_MASK); case MFF_IP_FRAG: return !(value->u8 & ~FLOW_NW_FRAG_MASK); case MFF_TCP_FLAGS: return !(value->be16 & ~htons(0x0fff)); case MFF_ARP_OP: return !(value->be16 & htons(0xff00)); case MFF_DL_VLAN: return !(value->be16 & htons(VLAN_CFI | VLAN_PCP_MASK)); case MFF_VLAN_VID: return !(value->be16 & htons(VLAN_PCP_MASK)); case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: return !(value->u8 & ~(VLAN_PCP_MASK >> VLAN_PCP_SHIFT)); case MFF_IPV6_LABEL: return !(value->be32 & ~htonl(IPV6_LABEL_MASK)); case MFF_MPLS_LABEL: return !(value->be32 & ~htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT)); case MFF_MPLS_TC: return !(value->u8 & ~(MPLS_TC_MASK >> MPLS_TC_SHIFT)); case MFF_MPLS_BOS: return !(value->u8 & ~(MPLS_BOS_MASK >> MPLS_BOS_SHIFT)); case MFF_TUN_FLAGS: return !(value->be16 & ~htons(FLOW_TNL_PUB_F_MASK)); case MFF_CT_STATE: return !(value->be32 & ~htonl(CS_SUPPORTED_MASK)); case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Copies the value of field 'mf' from 'flow' into 'value'. The caller is * responsible for ensuring that 'flow' meets 'mf''s prerequisites. */ void mf_get_value(const struct mf_field *mf, const struct flow *flow, union mf_value *value) { switch (mf->id) { case MFF_DP_HASH: value->be32 = htonl(flow->dp_hash); break; case MFF_RECIRC_ID: value->be32 = htonl(flow->recirc_id); break; case MFF_CONJ_ID: value->be32 = htonl(flow->conj_id); break; case MFF_TUN_ID: value->be64 = flow->tunnel.tun_id; break; case MFF_TUN_SRC: value->be32 = flow->tunnel.ip_src; break; case MFF_TUN_DST: value->be32 = flow->tunnel.ip_dst; break; case MFF_TUN_IPV6_SRC: value->ipv6 = flow->tunnel.ipv6_src; break; case MFF_TUN_IPV6_DST: value->ipv6 = flow->tunnel.ipv6_dst; break; case MFF_TUN_FLAGS: value->be16 = htons(flow->tunnel.flags & FLOW_TNL_PUB_F_MASK); break; case MFF_TUN_GBP_ID: value->be16 = flow->tunnel.gbp_id; break; case MFF_TUN_GBP_FLAGS: value->u8 = flow->tunnel.gbp_flags; break; case MFF_TUN_TTL: value->u8 = flow->tunnel.ip_ttl; break; case MFF_TUN_TOS: value->u8 = flow->tunnel.ip_tos; break; CASE_MFF_TUN_METADATA: tun_metadata_read(&flow->tunnel, mf, value); break; case MFF_METADATA: value->be64 = flow->metadata; break; case MFF_IN_PORT: value->be16 = htons(ofp_to_u16(flow->in_port.ofp_port)); break; case MFF_IN_PORT_OXM: value->be32 = ofputil_port_to_ofp11(flow->in_port.ofp_port); break; case MFF_ACTSET_OUTPUT: value->be32 = ofputil_port_to_ofp11(flow->actset_output); break; case MFF_SKB_PRIORITY: value->be32 = htonl(flow->skb_priority); break; case MFF_PKT_MARK: value->be32 = htonl(flow->pkt_mark); break; case MFF_CT_STATE: value->be32 = htonl(flow->ct_state); break; case MFF_CT_ZONE: value->be16 = htons(flow->ct_zone); break; case MFF_CT_MARK: value->be32 = htonl(flow->ct_mark); break; case MFF_CT_LABEL: value->be128 = hton128(flow->ct_label); break; CASE_MFF_REGS: value->be32 = htonl(flow->regs[mf->id - MFF_REG0]); break; CASE_MFF_XREGS: value->be64 = htonll(flow_get_xreg(flow, mf->id - MFF_XREG0)); break; case MFF_ETH_SRC: value->mac = flow->dl_src; break; case MFF_ETH_DST: value->mac = flow->dl_dst; break; case MFF_ETH_TYPE: value->be16 = flow->dl_type; break; case MFF_VLAN_TCI: value->be16 = flow->vlan_tci; break; case MFF_DL_VLAN: value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK); break; case MFF_VLAN_VID: value->be16 = flow->vlan_tci & htons(VLAN_VID_MASK | VLAN_CFI); break; case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: value->u8 = vlan_tci_to_pcp(flow->vlan_tci); break; case MFF_MPLS_LABEL: value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse[0])); break; case MFF_MPLS_TC: value->u8 = mpls_lse_to_tc(flow->mpls_lse[0]); break; case MFF_MPLS_BOS: value->u8 = mpls_lse_to_bos(flow->mpls_lse[0]); break; case MFF_IPV4_SRC: value->be32 = flow->nw_src; break; case MFF_IPV4_DST: value->be32 = flow->nw_dst; break; case MFF_IPV6_SRC: value->ipv6 = flow->ipv6_src; break; case MFF_IPV6_DST: value->ipv6 = flow->ipv6_dst; break; case MFF_IPV6_LABEL: value->be32 = flow->ipv6_label; break; case MFF_IP_PROTO: value->u8 = flow->nw_proto; break; case MFF_IP_DSCP: value->u8 = flow->nw_tos & IP_DSCP_MASK; break; case MFF_IP_DSCP_SHIFTED: value->u8 = flow->nw_tos >> 2; break; case MFF_IP_ECN: value->u8 = flow->nw_tos & IP_ECN_MASK; break; case MFF_IP_TTL: value->u8 = flow->nw_ttl; break; case MFF_IP_FRAG: value->u8 = flow->nw_frag; break; case MFF_ARP_OP: value->be16 = htons(flow->nw_proto); break; case MFF_ARP_SPA: value->be32 = flow->nw_src; break; case MFF_ARP_TPA: value->be32 = flow->nw_dst; break; case MFF_ARP_SHA: case MFF_ND_SLL: value->mac = flow->arp_sha; break; case MFF_ARP_THA: case MFF_ND_TLL: value->mac = flow->arp_tha; break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: value->be16 = flow->tp_src; break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: value->be16 = flow->tp_dst; break; case MFF_TCP_FLAGS: value->be16 = flow->tcp_flags; break; case MFF_ICMPV4_TYPE: case MFF_ICMPV6_TYPE: value->u8 = ntohs(flow->tp_src); break; case MFF_ICMPV4_CODE: case MFF_ICMPV6_CODE: value->u8 = ntohs(flow->tp_dst); break; case MFF_ND_TARGET: value->ipv6 = flow->nd_target; break; case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Makes 'match' match field 'mf' exactly, with the value matched taken from * 'value'. The caller is responsible for ensuring that 'match' meets 'mf''s * prerequisites. * * If non-NULL, 'err_str' returns a malloc'ed string describing any errors * with the request or NULL if there is no error. The caller is reponsible * for freeing the string. */ void mf_set_value(const struct mf_field *mf, const union mf_value *value, struct match *match, char **err_str) { if (err_str) { *err_str = NULL; } switch (mf->id) { case MFF_DP_HASH: match_set_dp_hash(match, ntohl(value->be32)); break; case MFF_RECIRC_ID: match_set_recirc_id(match, ntohl(value->be32)); break; case MFF_CONJ_ID: match_set_conj_id(match, ntohl(value->be32)); break; case MFF_TUN_ID: match_set_tun_id(match, value->be64); break; case MFF_TUN_SRC: match_set_tun_src(match, value->be32); break; case MFF_TUN_DST: match_set_tun_dst(match, value->be32); break; case MFF_TUN_IPV6_SRC: match_set_tun_ipv6_src(match, &value->ipv6); break; case MFF_TUN_IPV6_DST: match_set_tun_ipv6_dst(match, &value->ipv6); break; case MFF_TUN_FLAGS: match_set_tun_flags(match, ntohs(value->be16)); break; case MFF_TUN_GBP_ID: match_set_tun_gbp_id(match, value->be16); break; case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags(match, value->u8); break; case MFF_TUN_TOS: match_set_tun_tos(match, value->u8); break; case MFF_TUN_TTL: match_set_tun_ttl(match, value->u8); break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, value, NULL, match, err_str); break; case MFF_METADATA: match_set_metadata(match, value->be64); break; case MFF_IN_PORT: match_set_in_port(match, u16_to_ofp(ntohs(value->be16))); break; case MFF_IN_PORT_OXM: { ofp_port_t port; ofputil_port_from_ofp11(value->be32, &port); match_set_in_port(match, port); break; } case MFF_ACTSET_OUTPUT: { ofp_port_t port; ofputil_port_from_ofp11(value->be32, &port); match_set_actset_output(match, port); break; } case MFF_SKB_PRIORITY: match_set_skb_priority(match, ntohl(value->be32)); break; case MFF_PKT_MARK: match_set_pkt_mark(match, ntohl(value->be32)); break; case MFF_CT_STATE: match_set_ct_state(match, ntohl(value->be32)); break; case MFF_CT_ZONE: match_set_ct_zone(match, ntohs(value->be16)); break; case MFF_CT_MARK: match_set_ct_mark(match, ntohl(value->be32)); break; case MFF_CT_LABEL: match_set_ct_label(match, ntoh128(value->be128)); break; CASE_MFF_REGS: match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32)); break; CASE_MFF_XREGS: match_set_xreg(match, mf->id - MFF_XREG0, ntohll(value->be64)); break; case MFF_ETH_SRC: match_set_dl_src(match, value->mac); break; case MFF_ETH_DST: match_set_dl_dst(match, value->mac); break; case MFF_ETH_TYPE: match_set_dl_type(match, value->be16); break; case MFF_VLAN_TCI: match_set_dl_tci(match, value->be16); break; case MFF_DL_VLAN: match_set_dl_vlan(match, value->be16); break; case MFF_VLAN_VID: match_set_vlan_vid(match, value->be16); break; case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: match_set_dl_vlan_pcp(match, value->u8); break; case MFF_MPLS_LABEL: match_set_mpls_label(match, 0, value->be32); break; case MFF_MPLS_TC: match_set_mpls_tc(match, 0, value->u8); break; case MFF_MPLS_BOS: match_set_mpls_bos(match, 0, value->u8); break; case MFF_IPV4_SRC: match_set_nw_src(match, value->be32); break; case MFF_IPV4_DST: match_set_nw_dst(match, value->be32); break; case MFF_IPV6_SRC: match_set_ipv6_src(match, &value->ipv6); break; case MFF_IPV6_DST: match_set_ipv6_dst(match, &value->ipv6); break; case MFF_IPV6_LABEL: match_set_ipv6_label(match, value->be32); break; case MFF_IP_PROTO: match_set_nw_proto(match, value->u8); break; case MFF_IP_DSCP: match_set_nw_dscp(match, value->u8); break; case MFF_IP_DSCP_SHIFTED: match_set_nw_dscp(match, value->u8 << 2); break; case MFF_IP_ECN: match_set_nw_ecn(match, value->u8); break; case MFF_IP_TTL: match_set_nw_ttl(match, value->u8); break; case MFF_IP_FRAG: match_set_nw_frag(match, value->u8); break; case MFF_ARP_OP: match_set_nw_proto(match, ntohs(value->be16)); break; case MFF_ARP_SPA: match_set_nw_src(match, value->be32); break; case MFF_ARP_TPA: match_set_nw_dst(match, value->be32); break; case MFF_ARP_SHA: case MFF_ND_SLL: match_set_arp_sha(match, value->mac); break; case MFF_ARP_THA: case MFF_ND_TLL: match_set_arp_tha(match, value->mac); break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: match_set_tp_src(match, value->be16); break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: match_set_tp_dst(match, value->be16); break; case MFF_TCP_FLAGS: match_set_tcp_flags(match, value->be16); break; case MFF_ICMPV4_TYPE: case MFF_ICMPV6_TYPE: match_set_icmp_type(match, value->u8); break; case MFF_ICMPV4_CODE: case MFF_ICMPV6_CODE: match_set_icmp_code(match, value->u8); break; case MFF_ND_TARGET: match_set_nd_target(match, &value->ipv6); break; case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Unwildcard 'mask' member field described by 'mf'. The caller is * responsible for ensuring that 'mask' meets 'mf''s prerequisites. */ void mf_mask_field(const struct mf_field *mf, struct flow *mask) { /* For MFF_DL_VLAN, we cannot send a all 1's to flow_set_dl_vlan() * as that will be considered as OFP10_VLAN_NONE. So consider it as a * special case. For the rest, calling mf_set_flow_value() is good * enough. */ if (mf->id == MFF_DL_VLAN) { flow_set_dl_vlan(mask, htons(VLAN_VID_MASK)); } else { mf_set_flow_value(mf, &exact_match_mask, mask); } } static int field_len(const struct mf_field *mf, const union mf_value *value_) { const uint8_t *value = &value_->u8; int i; if (!mf->variable_len) { return mf->n_bytes; } if (!value) { return 0; } for (i = 0; i < mf->n_bytes; i++) { if (value[i] != 0) { break; } } return mf->n_bytes - i; } /* Returns the effective length of the field. For fixed length fields, * this is just the defined length. For variable length fields, it is * the minimum size encoding that retains the same meaning (i.e. * discarding leading zeros). * * 'is_masked' returns (if non-NULL) whether the original contained * a mask. Otherwise, a mask that is the same length as the value * might be misinterpreted as an exact match. */ int mf_field_len(const struct mf_field *mf, const union mf_value *value, const union mf_value *mask, bool *is_masked_) { int len, mask_len; bool is_masked = mask && !is_all_ones(mask, mf->n_bytes); len = field_len(mf, value); if (is_masked) { mask_len = field_len(mf, mask); len = MAX(len, mask_len); } if (is_masked_) { *is_masked_ = is_masked; } return len; } /* Sets 'flow' member field described by 'mf' to 'value'. The caller is * responsible for ensuring that 'flow' meets 'mf''s prerequisites.*/ void mf_set_flow_value(const struct mf_field *mf, const union mf_value *value, struct flow *flow) { switch (mf->id) { case MFF_DP_HASH: flow->dp_hash = ntohl(value->be32); break; case MFF_RECIRC_ID: flow->recirc_id = ntohl(value->be32); break; case MFF_CONJ_ID: flow->conj_id = ntohl(value->be32); break; case MFF_TUN_ID: flow->tunnel.tun_id = value->be64; break; case MFF_TUN_SRC: flow->tunnel.ip_src = value->be32; break; case MFF_TUN_DST: flow->tunnel.ip_dst = value->be32; break; case MFF_TUN_IPV6_SRC: flow->tunnel.ipv6_src = value->ipv6; break; case MFF_TUN_IPV6_DST: flow->tunnel.ipv6_dst = value->ipv6; break; case MFF_TUN_FLAGS: flow->tunnel.flags = (flow->tunnel.flags & ~FLOW_TNL_PUB_F_MASK) | ntohs(value->be16); break; case MFF_TUN_GBP_ID: flow->tunnel.gbp_id = value->be16; break; case MFF_TUN_GBP_FLAGS: flow->tunnel.gbp_flags = value->u8; break; case MFF_TUN_TOS: flow->tunnel.ip_tos = value->u8; break; case MFF_TUN_TTL: flow->tunnel.ip_ttl = value->u8; break; CASE_MFF_TUN_METADATA: tun_metadata_write(&flow->tunnel, mf, value); break; case MFF_METADATA: flow->metadata = value->be64; break; case MFF_IN_PORT: flow->in_port.ofp_port = u16_to_ofp(ntohs(value->be16)); break; case MFF_IN_PORT_OXM: ofputil_port_from_ofp11(value->be32, &flow->in_port.ofp_port); break; case MFF_ACTSET_OUTPUT: ofputil_port_from_ofp11(value->be32, &flow->actset_output); break; case MFF_SKB_PRIORITY: flow->skb_priority = ntohl(value->be32); break; case MFF_PKT_MARK: flow->pkt_mark = ntohl(value->be32); break; case MFF_CT_STATE: flow->ct_state = ntohl(value->be32); break; case MFF_CT_ZONE: flow->ct_zone = ntohs(value->be16); break; case MFF_CT_MARK: flow->ct_mark = ntohl(value->be32); break; case MFF_CT_LABEL: flow->ct_label = ntoh128(value->be128); break; CASE_MFF_REGS: flow->regs[mf->id - MFF_REG0] = ntohl(value->be32); break; CASE_MFF_XREGS: flow_set_xreg(flow, mf->id - MFF_XREG0, ntohll(value->be64)); break; case MFF_ETH_SRC: flow->dl_src = value->mac; break; case MFF_ETH_DST: flow->dl_dst = value->mac; break; case MFF_ETH_TYPE: flow->dl_type = value->be16; break; case MFF_VLAN_TCI: flow->vlan_tci = value->be16; break; case MFF_DL_VLAN: flow_set_dl_vlan(flow, value->be16); break; case MFF_VLAN_VID: flow_set_vlan_vid(flow, value->be16); break; case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: flow_set_vlan_pcp(flow, value->u8); break; case MFF_MPLS_LABEL: flow_set_mpls_label(flow, 0, value->be32); break; case MFF_MPLS_TC: flow_set_mpls_tc(flow, 0, value->u8); break; case MFF_MPLS_BOS: flow_set_mpls_bos(flow, 0, value->u8); break; case MFF_IPV4_SRC: flow->nw_src = value->be32; break; case MFF_IPV4_DST: flow->nw_dst = value->be32; break; case MFF_IPV6_SRC: flow->ipv6_src = value->ipv6; break; case MFF_IPV6_DST: flow->ipv6_dst = value->ipv6; break; case MFF_IPV6_LABEL: flow->ipv6_label = value->be32 & htonl(IPV6_LABEL_MASK); break; case MFF_IP_PROTO: flow->nw_proto = value->u8; break; case MFF_IP_DSCP: flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= value->u8 & IP_DSCP_MASK; break; case MFF_IP_DSCP_SHIFTED: flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= value->u8 << 2; break; case MFF_IP_ECN: flow->nw_tos &= ~IP_ECN_MASK; flow->nw_tos |= value->u8 & IP_ECN_MASK; break; case MFF_IP_TTL: flow->nw_ttl = value->u8; break; case MFF_IP_FRAG: flow->nw_frag = value->u8 & FLOW_NW_FRAG_MASK; break; case MFF_ARP_OP: flow->nw_proto = ntohs(value->be16); break; case MFF_ARP_SPA: flow->nw_src = value->be32; break; case MFF_ARP_TPA: flow->nw_dst = value->be32; break; case MFF_ARP_SHA: case MFF_ND_SLL: flow->arp_sha = value->mac; break; case MFF_ARP_THA: case MFF_ND_TLL: flow->arp_tha = value->mac; break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: flow->tp_src = value->be16; break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: flow->tp_dst = value->be16; break; case MFF_TCP_FLAGS: flow->tcp_flags = value->be16; break; case MFF_ICMPV4_TYPE: case MFF_ICMPV6_TYPE: flow->tp_src = htons(value->u8); break; case MFF_ICMPV4_CODE: case MFF_ICMPV6_CODE: flow->tp_dst = htons(value->u8); break; case MFF_ND_TARGET: flow->nd_target = value->ipv6; break; case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Consider each of 'src', 'mask', and 'dst' as if they were arrays of 8*n * bits. Then, for each 0 <= i < 8 * n such that mask[i] == 1, sets dst[i] = * src[i]. */ static void apply_mask(const uint8_t *src, const uint8_t *mask, uint8_t *dst, size_t n) { size_t i; for (i = 0; i < n; i++) { dst[i] = (src[i] & mask[i]) | (dst[i] & ~mask[i]); } } /* Sets 'flow' member field described by 'field' to 'value', except that bits * for which 'mask' has a 0-bit keep their existing values. The caller is * responsible for ensuring that 'flow' meets 'field''s prerequisites.*/ void mf_set_flow_value_masked(const struct mf_field *field, const union mf_value *value, const union mf_value *mask, struct flow *flow) { union mf_value tmp; mf_get_value(field, flow, &tmp); apply_mask((const uint8_t *) value, (const uint8_t *) mask, (uint8_t *) &tmp, field->n_bytes); mf_set_flow_value(field, &tmp, flow); } bool mf_is_tun_metadata(const struct mf_field *mf) { return mf->id >= MFF_TUN_METADATA0 && mf->id < MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS; } /* Returns true if 'mf' has previously been set in 'flow', false if * it contains a non-default value. * * The caller is responsible for ensuring that 'flow' meets 'mf''s * prerequisites. */ bool mf_is_set(const struct mf_field *mf, const struct flow *flow) { if (!mf_is_tun_metadata(mf)) { union mf_value value; mf_get_value(mf, flow, &value); return !is_all_zeros(&value, mf->n_bytes); } else { return ULLONG_GET(flow->tunnel.metadata.present.map, mf->id - MFF_TUN_METADATA0); } } /* Makes 'match' wildcard field 'mf'. * * The caller is responsible for ensuring that 'match' meets 'mf''s * prerequisites. * * If non-NULL, 'err_str' returns a malloc'ed string describing any errors * with the request or NULL if there is no error. The caller is reponsible * for freeing the string. */ void mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) { if (err_str) { *err_str = NULL; } switch (mf->id) { case MFF_DP_HASH: match->flow.dp_hash = 0; match->wc.masks.dp_hash = 0; break; case MFF_RECIRC_ID: match->flow.recirc_id = 0; match->wc.masks.recirc_id = 0; break; case MFF_CONJ_ID: match->flow.conj_id = 0; match->wc.masks.conj_id = 0; break; case MFF_TUN_ID: match_set_tun_id_masked(match, htonll(0), htonll(0)); break; case MFF_TUN_SRC: match_set_tun_src_masked(match, htonl(0), htonl(0)); break; case MFF_TUN_DST: match_set_tun_dst_masked(match, htonl(0), htonl(0)); break; case MFF_TUN_IPV6_SRC: memset(&match->wc.masks.tunnel.ipv6_src, 0, sizeof match->wc.masks.tunnel.ipv6_src); memset(&match->flow.tunnel.ipv6_src, 0, sizeof match->flow.tunnel.ipv6_src); break; case MFF_TUN_IPV6_DST: memset(&match->wc.masks.tunnel.ipv6_dst, 0, sizeof match->wc.masks.tunnel.ipv6_dst); memset(&match->flow.tunnel.ipv6_dst, 0, sizeof match->flow.tunnel.ipv6_dst); break; case MFF_TUN_FLAGS: match_set_tun_flags_masked(match, 0, 0); break; case MFF_TUN_GBP_ID: match_set_tun_gbp_id_masked(match, 0, 0); break; case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags_masked(match, 0, 0); break; case MFF_TUN_TOS: match_set_tun_tos_masked(match, 0, 0); break; case MFF_TUN_TTL: match_set_tun_ttl_masked(match, 0, 0); break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, NULL, NULL, match, err_str); break; case MFF_METADATA: match_set_metadata_masked(match, htonll(0), htonll(0)); break; case MFF_IN_PORT: case MFF_IN_PORT_OXM: match->flow.in_port.ofp_port = 0; match->wc.masks.in_port.ofp_port = 0; break; case MFF_ACTSET_OUTPUT: match->flow.actset_output = 0; match->wc.masks.actset_output = 0; break; case MFF_SKB_PRIORITY: match->flow.skb_priority = 0; match->wc.masks.skb_priority = 0; break; case MFF_PKT_MARK: match->flow.pkt_mark = 0; match->wc.masks.pkt_mark = 0; break; case MFF_CT_STATE: match->flow.ct_state = 0; match->wc.masks.ct_state = 0; break; case MFF_CT_ZONE: match->flow.ct_zone = 0; match->wc.masks.ct_zone = 0; break; case MFF_CT_MARK: match->flow.ct_mark = 0; match->wc.masks.ct_mark = 0; break; case MFF_CT_LABEL: memset(&match->flow.ct_label, 0, sizeof(match->flow.ct_label)); memset(&match->wc.masks.ct_label, 0, sizeof(match->wc.masks.ct_label)); break; CASE_MFF_REGS: match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0); break; CASE_MFF_XREGS: match_set_xreg_masked(match, mf->id - MFF_XREG0, 0, 0); break; case MFF_ETH_SRC: match->flow.dl_src = eth_addr_zero; match->wc.masks.dl_src = eth_addr_zero; break; case MFF_ETH_DST: match->flow.dl_dst = eth_addr_zero; match->wc.masks.dl_dst = eth_addr_zero; break; case MFF_ETH_TYPE: match->flow.dl_type = htons(0); match->wc.masks.dl_type = htons(0); break; case MFF_VLAN_TCI: match_set_dl_tci_masked(match, htons(0), htons(0)); break; case MFF_DL_VLAN: case MFF_VLAN_VID: match_set_any_vid(match); break; case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: match_set_any_pcp(match); break; case MFF_MPLS_LABEL: match_set_any_mpls_label(match, 0); break; case MFF_MPLS_TC: match_set_any_mpls_tc(match, 0); break; case MFF_MPLS_BOS: match_set_any_mpls_bos(match, 0); break; case MFF_IPV4_SRC: case MFF_ARP_SPA: match_set_nw_src_masked(match, htonl(0), htonl(0)); break; case MFF_IPV4_DST: case MFF_ARP_TPA: match_set_nw_dst_masked(match, htonl(0), htonl(0)); break; case MFF_IPV6_SRC: memset(&match->wc.masks.ipv6_src, 0, sizeof match->wc.masks.ipv6_src); memset(&match->flow.ipv6_src, 0, sizeof match->flow.ipv6_src); break; case MFF_IPV6_DST: memset(&match->wc.masks.ipv6_dst, 0, sizeof match->wc.masks.ipv6_dst); memset(&match->flow.ipv6_dst, 0, sizeof match->flow.ipv6_dst); break; case MFF_IPV6_LABEL: match->wc.masks.ipv6_label = htonl(0); match->flow.ipv6_label = htonl(0); break; case MFF_IP_PROTO: match->wc.masks.nw_proto = 0; match->flow.nw_proto = 0; break; case MFF_IP_DSCP: case MFF_IP_DSCP_SHIFTED: match->wc.masks.nw_tos &= ~IP_DSCP_MASK; match->flow.nw_tos &= ~IP_DSCP_MASK; break; case MFF_IP_ECN: match->wc.masks.nw_tos &= ~IP_ECN_MASK; match->flow.nw_tos &= ~IP_ECN_MASK; break; case MFF_IP_TTL: match->wc.masks.nw_ttl = 0; match->flow.nw_ttl = 0; break; case MFF_IP_FRAG: match->wc.masks.nw_frag &= ~FLOW_NW_FRAG_MASK; match->flow.nw_frag &= ~FLOW_NW_FRAG_MASK; break; case MFF_ARP_OP: match->wc.masks.nw_proto = 0; match->flow.nw_proto = 0; break; case MFF_ARP_SHA: case MFF_ND_SLL: match->flow.arp_sha = eth_addr_zero; match->wc.masks.arp_sha = eth_addr_zero; break; case MFF_ARP_THA: case MFF_ND_TLL: match->flow.arp_tha = eth_addr_zero; match->wc.masks.arp_tha = eth_addr_zero; break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: case MFF_ICMPV4_TYPE: case MFF_ICMPV6_TYPE: match->wc.masks.tp_src = htons(0); match->flow.tp_src = htons(0); break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: case MFF_ICMPV4_CODE: case MFF_ICMPV6_CODE: match->wc.masks.tp_dst = htons(0); match->flow.tp_dst = htons(0); break; case MFF_TCP_FLAGS: match->wc.masks.tcp_flags = htons(0); match->flow.tcp_flags = htons(0); break; case MFF_ND_TARGET: memset(&match->wc.masks.nd_target, 0, sizeof match->wc.masks.nd_target); memset(&match->flow.nd_target, 0, sizeof match->flow.nd_target); break; case MFF_N_IDS: default: OVS_NOT_REACHED(); } } /* Makes 'match' match field 'mf' with the specified 'value' and 'mask'. * 'value' specifies a value to match and 'mask' specifies a wildcard pattern, * with a 1-bit indicating that the corresponding value bit must match and a * 0-bit indicating a don't-care. * * If 'mask' is NULL or points to all-1-bits, then this call is equivalent to * mf_set_value(mf, value, match). If 'mask' points to all-0-bits, then this * call is equivalent to mf_set_wild(mf, match). * * 'mask' must be a valid mask for 'mf' (see mf_is_mask_valid()). The caller * is responsible for ensuring that 'match' meets 'mf''s prerequisites. * * If non-NULL, 'err_str' returns a malloc'ed string describing any errors * with the request or NULL if there is no error. The caller is reponsible * for freeing the string. * * Return a set of enum ofputil_protocol bits (as an uint32_t to avoid circular * dependency on enum ofputil_protocol definition) indicating which OpenFlow * protocol versions can support this functionality. */ uint32_t mf_set(const struct mf_field *mf, const union mf_value *value, const union mf_value *mask, struct match *match, char **err_str) { if (!mask || is_all_ones(mask, mf->n_bytes)) { mf_set_value(mf, value, match, err_str); return mf->usable_protocols_exact; } else if (is_all_zeros(mask, mf->n_bytes) && !mf_is_tun_metadata(mf)) { /* Tunnel metadata matches on the existence of the field itself, so * it still needs to be encoded even if the value is wildcarded. */ mf_set_wild(mf, match, err_str); return OFPUTIL_P_ANY; } if (err_str) { *err_str = NULL; } switch (mf->id) { case MFF_CT_ZONE: case MFF_RECIRC_ID: case MFF_CONJ_ID: case MFF_IN_PORT: case MFF_IN_PORT_OXM: case MFF_ACTSET_OUTPUT: case MFF_SKB_PRIORITY: case MFF_ETH_TYPE: case MFF_DL_VLAN: case MFF_DL_VLAN_PCP: case MFF_VLAN_PCP: case MFF_MPLS_LABEL: case MFF_MPLS_TC: case MFF_MPLS_BOS: case MFF_IP_PROTO: case MFF_IP_TTL: case MFF_IP_DSCP: case MFF_IP_DSCP_SHIFTED: case MFF_IP_ECN: case MFF_ARP_OP: case MFF_ICMPV4_TYPE: case MFF_ICMPV4_CODE: case MFF_ICMPV6_TYPE: case MFF_ICMPV6_CODE: return OFPUTIL_P_NONE; case MFF_DP_HASH: match_set_dp_hash_masked(match, ntohl(value->be32), ntohl(mask->be32)); break; case MFF_TUN_ID: match_set_tun_id_masked(match, value->be64, mask->be64); break; case MFF_TUN_SRC: match_set_tun_src_masked(match, value->be32, mask->be32); break; case MFF_TUN_DST: match_set_tun_dst_masked(match, value->be32, mask->be32); break; case MFF_TUN_IPV6_SRC: match_set_tun_ipv6_src_masked(match, &value->ipv6, &mask->ipv6); break; case MFF_TUN_IPV6_DST: match_set_tun_ipv6_dst_masked(match, &value->ipv6, &mask->ipv6); break; case MFF_TUN_FLAGS: match_set_tun_flags_masked(match, ntohs(value->be16), ntohs(mask->be16)); break; case MFF_TUN_GBP_ID: match_set_tun_gbp_id_masked(match, value->be16, mask->be16); break; case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags_masked(match, value->u8, mask->u8); break; case MFF_TUN_TTL: match_set_tun_ttl_masked(match, value->u8, mask->u8); break; case MFF_TUN_TOS: match_set_tun_tos_masked(match, value->u8, mask->u8); break; CASE_MFF_TUN_METADATA: tun_metadata_set_match(mf, value, mask, match, err_str); break; case MFF_METADATA: match_set_metadata_masked(match, value->be64, mask->be64); break; CASE_MFF_REGS: match_set_reg_masked(match, mf->id - MFF_REG0, ntohl(value->be32), ntohl(mask->be32)); break; CASE_MFF_XREGS: match_set_xreg_masked(match, mf->id - MFF_XREG0, ntohll(value->be64), ntohll(mask->be64)); break; case MFF_PKT_MARK: match_set_pkt_mark_masked(match, ntohl(value->be32), ntohl(mask->be32)); break; case MFF_CT_STATE: match_set_ct_state_masked(match, ntohl(value->be32), ntohl(mask->be32)); break; case MFF_CT_MARK: match_set_ct_mark_masked(match, ntohl(value->be32), ntohl(mask->be32)); break; case MFF_CT_LABEL: match_set_ct_label_masked(match, ntoh128(value->be128), mask ? ntoh128(mask->be128) : OVS_U128_MAX); break; case MFF_ETH_DST: match_set_dl_dst_masked(match, value->mac, mask->mac); break; case MFF_ETH_SRC: match_set_dl_src_masked(match, value->mac, mask->mac); break; case MFF_ARP_SHA: case MFF_ND_SLL: match_set_arp_sha_masked(match, value->mac, mask->mac); break; case MFF_ARP_THA: case MFF_ND_TLL: match_set_arp_tha_masked(match, value->mac, mask->mac); break; case MFF_VLAN_TCI: match_set_dl_tci_masked(match, value->be16, mask->be16); break; case MFF_VLAN_VID: match_set_vlan_vid_masked(match, value->be16, mask->be16); break; case MFF_IPV4_SRC: match_set_nw_src_masked(match, value->be32, mask->be32); break; case MFF_IPV4_DST: match_set_nw_dst_masked(match, value->be32, mask->be32); break; case MFF_IPV6_SRC: match_set_ipv6_src_masked(match, &value->ipv6, &mask->ipv6); break; case MFF_IPV6_DST: match_set_ipv6_dst_masked(match, &value->ipv6, &mask->ipv6); break; case MFF_IPV6_LABEL: if ((mask->be32 & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK)) { mf_set_value(mf, value, match, err_str); } else { match_set_ipv6_label_masked(match, value->be32, mask->be32); } break; case MFF_ND_TARGET: match_set_nd_target_masked(match, &value->ipv6, &mask->ipv6); break; case MFF_IP_FRAG: match_set_nw_frag_masked(match, value->u8, mask->u8); break; case MFF_ARP_SPA: match_set_nw_src_masked(match, value->be32, mask->be32); break; case MFF_ARP_TPA: match_set_nw_dst_masked(match, value->be32, mask->be32); break; case MFF_TCP_SRC: case MFF_UDP_SRC: case MFF_SCTP_SRC: match_set_tp_src_masked(match, value->be16, mask->be16); break; case MFF_TCP_DST: case MFF_UDP_DST: case MFF_SCTP_DST: match_set_tp_dst_masked(match, value->be16, mask->be16); break; case MFF_TCP_FLAGS: match_set_tcp_flags_masked(match, value->be16, mask->be16); break; case MFF_N_IDS: default: OVS_NOT_REACHED(); } return ((mf->usable_protocols_bitwise == mf->usable_protocols_cidr || ip_is_cidr(mask->be32)) ? mf->usable_protocols_cidr : mf->usable_protocols_bitwise); } static enum ofperr mf_check__(const struct mf_subfield *sf, const struct flow *flow, const char *type) { if (!sf->field) { VLOG_WARN_RL(&rl, "unknown %s field", type); return OFPERR_OFPBAC_BAD_SET_TYPE; } else if (!sf->n_bits) { VLOG_WARN_RL(&rl, "zero bit %s field %s", type, sf->field->name); return OFPERR_OFPBAC_BAD_SET_LEN; } else if (sf->ofs >= sf->field->n_bits) { VLOG_WARN_RL(&rl, "bit offset %d exceeds %d-bit width of %s field %s", sf->ofs, sf->field->n_bits, type, sf->field->name); return OFPERR_OFPBAC_BAD_SET_LEN; } else if (sf->ofs + sf->n_bits > sf->field->n_bits) { VLOG_WARN_RL(&rl, "bit offset %d and width %d exceeds %d-bit width " "of %s field %s", sf->ofs, sf->n_bits, sf->field->n_bits, type, sf->field->name); return OFPERR_OFPBAC_BAD_SET_LEN; } else if (flow && !mf_are_prereqs_ok(sf->field, flow)) { VLOG_WARN_RL(&rl, "%s field %s lacks correct prerequisites", type, sf->field->name); return OFPERR_OFPBAC_MATCH_INCONSISTENT; } else { return 0; } } /* Checks whether 'sf' is valid for reading a subfield out of 'flow'. Returns * 0 if so, otherwise an OpenFlow error code (e.g. as returned by * ofp_mkerr()). */ enum ofperr mf_check_src(const struct mf_subfield *sf, const struct flow *flow) { return mf_check__(sf, flow, "source"); } /* Checks whether 'sf' is valid for writing a subfield into 'flow'. Returns 0 * if so, otherwise an OpenFlow error code (e.g. as returned by * ofp_mkerr()). */ enum ofperr mf_check_dst(const struct mf_subfield *sf, const struct flow *flow) { int error = mf_check__(sf, flow, "destination"); if (!error && !sf->field->writable) { VLOG_WARN_RL(&rl, "destination field %s is not writable", sf->field->name); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } return error; } /* Copies the value and wildcard bit pattern for 'mf' from 'match' into the * 'value' and 'mask', respectively. */ void mf_get(const struct mf_field *mf, const struct match *match, union mf_value *value, union mf_value *mask) { mf_get_value(mf, &match->flow, value); mf_get_mask(mf, &match->wc, mask); } static char * mf_from_integer_string(const struct mf_field *mf, const char *s, uint8_t *valuep, uint8_t *maskp) { char *tail; const char *err_str = ""; int err; err = parse_int_string(s, valuep, mf->n_bytes, &tail); if (err || (*tail != '\0' && *tail != '/')) { err_str = "value"; goto syntax_error; } if (*tail == '/') { err = parse_int_string(tail + 1, maskp, mf->n_bytes, &tail); if (err || *tail != '\0') { err_str = "mask"; goto syntax_error; } } else { memset(maskp, 0xff, mf->n_bytes); } return NULL; syntax_error: if (err == ERANGE) { return xasprintf("%s: %s too large for %u-byte field %s", s, err_str, mf->n_bytes, mf->name); } else { return xasprintf("%s: bad syntax for %s %s", s, mf->name, err_str); } } static char * mf_from_ethernet_string(const struct mf_field *mf, const char *s, struct eth_addr *mac, struct eth_addr *mask) { int n; ovs_assert(mf->n_bytes == ETH_ADDR_LEN); n = -1; if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*mac), &n) && n == strlen(s)) { *mask = eth_addr_exact; return NULL; } n = -1; if (ovs_scan(s, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*mac), ETH_ADDR_SCAN_ARGS(*mask), &n) && n == strlen(s)) { return NULL; } return xasprintf("%s: invalid Ethernet address", s); } static char * mf_from_ipv4_string(const struct mf_field *mf, const char *s, ovs_be32 *ip, ovs_be32 *mask) { ovs_assert(mf->n_bytes == sizeof *ip); return ip_parse_masked(s, ip, mask); } static char * mf_from_ipv6_string(const struct mf_field *mf, const char *s, struct in6_addr *ipv6, struct in6_addr *mask) { ovs_assert(mf->n_bytes == sizeof *ipv6); return ipv6_parse_masked(s, ipv6, mask); } static char * mf_from_ofp_port_string(const struct mf_field *mf, const char *s, ovs_be16 *valuep, ovs_be16 *maskp) { ofp_port_t port; ovs_assert(mf->n_bytes == sizeof(ovs_be16)); if (ofputil_port_from_string(s, &port)) { *valuep = htons(ofp_to_u16(port)); *maskp = OVS_BE16_MAX; return NULL; } return xasprintf("%s: port value out of range for %s", s, mf->name); } static char * mf_from_ofp_port_string32(const struct mf_field *mf, const char *s, ovs_be32 *valuep, ovs_be32 *maskp) { ofp_port_t port; ovs_assert(mf->n_bytes == sizeof(ovs_be32)); if (ofputil_port_from_string(s, &port)) { *valuep = ofputil_port_to_ofp11(port); *maskp = OVS_BE32_MAX; return NULL; } return xasprintf("%s: port value out of range for %s", s, mf->name); } struct frag_handling { const char *name; uint8_t mask; uint8_t value; }; static const struct frag_handling all_frags[] = { #define A FLOW_NW_FRAG_ANY #define L FLOW_NW_FRAG_LATER /* name mask value */ { "no", A|L, 0 }, { "first", A|L, A }, { "later", A|L, A|L }, { "no", A, 0 }, { "yes", A, A }, { "not_later", L, 0 }, { "later", L, L }, #undef A #undef L }; static char * mf_from_frag_string(const char *s, uint8_t *valuep, uint8_t *maskp) { const struct frag_handling *h; for (h = all_frags; h < &all_frags[ARRAY_SIZE(all_frags)]; h++) { if (!strcasecmp(s, h->name)) { /* We force the upper bits of the mask on to make mf_parse_value() * happy (otherwise it will never think it's an exact match.) */ *maskp = h->mask | ~FLOW_NW_FRAG_MASK; *valuep = h->value; return NULL; } } return xasprintf("%s: unknown fragment type (valid types are \"no\", " "\"yes\", \"first\", \"later\", \"not_later\"", s); } static char * parse_mf_flags(const char *s, const char *(*bit_to_string)(uint32_t), const char *field_name, ovs_be16 *flagsp, ovs_be16 allowed, ovs_be16 *maskp) { int err; char *err_str; uint32_t flags, mask; err = parse_flags(s, bit_to_string, '\0', field_name, &err_str, &flags, ntohs(allowed), maskp ? &mask : NULL); if (err < 0) { return err_str; } *flagsp = htons(flags); if (maskp) { *maskp = htons(mask); } return NULL; } static char * mf_from_tcp_flags_string(const char *s, ovs_be16 *flagsp, ovs_be16 *maskp) { return parse_mf_flags(s, packet_tcp_flag_to_string, "TCP", flagsp, TCP_FLAGS_BE16(OVS_BE16_MAX), maskp); } static char * mf_from_tun_flags_string(const char *s, ovs_be16 *flagsp, ovs_be16 *maskp) { return parse_mf_flags(s, flow_tun_flag_to_string, "tunnel", flagsp, htons(FLOW_TNL_PUB_F_MASK), maskp); } static char * mf_from_ct_state_string(const char *s, ovs_be32 *flagsp, ovs_be32 *maskp) { int err; char *err_str; uint32_t flags, mask; err = parse_flags(s, ct_state_to_string, '\0', "ct_state", &err_str, &flags, CS_SUPPORTED_MASK, maskp ? &mask : NULL); if (err < 0) { return err_str; } *flagsp = htonl(flags); if (maskp) { *maskp = htonl(mask); } return NULL; } /* Parses 's', a string value for field 'mf', into 'value' and 'mask'. Returns * NULL if successful, otherwise a malloc()'d string describing the error. */ char * mf_parse(const struct mf_field *mf, const char *s, union mf_value *value, union mf_value *mask) { char *error; if (!strcmp(s, "*")) { memset(value, 0, mf->n_bytes); memset(mask, 0, mf->n_bytes); return NULL; } switch (mf->string) { case MFS_DECIMAL: case MFS_HEXADECIMAL: error = mf_from_integer_string(mf, s, (uint8_t *) value, (uint8_t *) mask); break; case MFS_CT_STATE: ovs_assert(mf->n_bytes == sizeof(ovs_be32)); error = mf_from_ct_state_string(s, &value->be32, &mask->be32); break; case MFS_ETHERNET: error = mf_from_ethernet_string(mf, s, &value->mac, &mask->mac); break; case MFS_IPV4: error = mf_from_ipv4_string(mf, s, &value->be32, &mask->be32); break; case MFS_IPV6: error = mf_from_ipv6_string(mf, s, &value->ipv6, &mask->ipv6); break; case MFS_OFP_PORT: error = mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16); break; case MFS_OFP_PORT_OXM: error = mf_from_ofp_port_string32(mf, s, &value->be32, &mask->be32); break; case MFS_FRAG: error = mf_from_frag_string(s, &value->u8, &mask->u8); break; case MFS_TNL_FLAGS: ovs_assert(mf->n_bytes == sizeof(ovs_be16)); error = mf_from_tun_flags_string(s, &value->be16, &mask->be16); break; case MFS_TCP_FLAGS: ovs_assert(mf->n_bytes == sizeof(ovs_be16)); error = mf_from_tcp_flags_string(s, &value->be16, &mask->be16); break; default: OVS_NOT_REACHED(); } if (!error && !mf_is_mask_valid(mf, mask)) { error = xasprintf("%s: invalid mask for field %s", s, mf->name); } return error; } /* Parses 's', a string value for field 'mf', into 'value'. Returns NULL if * successful, otherwise a malloc()'d string describing the error. */ char * mf_parse_value(const struct mf_field *mf, const char *s, union mf_value *value) { union mf_value mask; char *error; error = mf_parse(mf, s, value, &mask); if (error) { return error; } if (!is_all_ones((const uint8_t *) &mask, mf->n_bytes)) { return xasprintf("%s: wildcards not allowed here", s); } return NULL; } static void mf_format_integer_string(const struct mf_field *mf, const uint8_t *valuep, const uint8_t *maskp, struct ds *s) { if (mf->string == MFS_HEXADECIMAL) { ds_put_hex(s, valuep, mf->n_bytes); } else { unsigned long long int integer = 0; int i; ovs_assert(mf->n_bytes <= 8); for (i = 0; i < mf->n_bytes; i++) { integer = (integer << 8) | valuep[i]; } ds_put_format(s, "%lld", integer); } if (maskp) { /* I guess we could write the mask in decimal for MFS_DECIMAL but I'm * not sure that that a bit-mask written in decimal is ever easier to * understand than the same bit-mask written in hexadecimal. */ ds_put_char(s, '/'); ds_put_hex(s, maskp, mf->n_bytes); } } static void mf_format_frag_string(uint8_t value, uint8_t mask, struct ds *s) { const struct frag_handling *h; mask &= FLOW_NW_FRAG_MASK; value &= mask; for (h = all_frags; h < &all_frags[ARRAY_SIZE(all_frags)]; h++) { if (value == h->value && mask == h->mask) { ds_put_cstr(s, h->name); return; } } ds_put_cstr(s, ""); } static void mf_format_tnl_flags_string(ovs_be16 value, ovs_be16 mask, struct ds *s) { format_flags_masked(s, NULL, flow_tun_flag_to_string, ntohs(value), ntohs(mask) & FLOW_TNL_PUB_F_MASK, FLOW_TNL_PUB_F_MASK); } static void mf_format_tcp_flags_string(ovs_be16 value, ovs_be16 mask, struct ds *s) { format_flags_masked(s, NULL, packet_tcp_flag_to_string, ntohs(value), TCP_FLAGS(mask), TCP_FLAGS(OVS_BE16_MAX)); } static void mf_format_ct_state_string(ovs_be32 value, ovs_be32 mask, struct ds *s) { format_flags_masked(s, NULL, ct_state_to_string, ntohl(value), ntohl(mask), UINT16_MAX); } /* Appends to 's' a string representation of field 'mf' whose value is in * 'value' and 'mask'. 'mask' may be NULL to indicate an exact match. */ void mf_format(const struct mf_field *mf, const union mf_value *value, const union mf_value *mask, struct ds *s) { if (mask) { if (is_all_zeros(mask, mf->n_bytes)) { ds_put_cstr(s, "ANY"); return; } else if (is_all_ones(mask, mf->n_bytes)) { mask = NULL; } } switch (mf->string) { case MFS_OFP_PORT_OXM: if (!mask) { ofp_port_t port; ofputil_port_from_ofp11(value->be32, &port); ofputil_format_port(port, s); break; } /* fall through */ case MFS_OFP_PORT: if (!mask) { ofputil_format_port(u16_to_ofp(ntohs(value->be16)), s); break; } /* fall through */ case MFS_DECIMAL: case MFS_HEXADECIMAL: mf_format_integer_string(mf, (uint8_t *) value, (uint8_t *) mask, s); break; case MFS_CT_STATE: mf_format_ct_state_string(value->be32, mask ? mask->be32 : OVS_BE32_MAX, s); break; case MFS_ETHERNET: eth_format_masked(value->mac, mask ? &mask->mac : NULL, s); break; case MFS_IPV4: ip_format_masked(value->be32, mask ? mask->be32 : OVS_BE32_MAX, s); break; case MFS_IPV6: ipv6_format_masked(&value->ipv6, mask ? &mask->ipv6 : NULL, s); break; case MFS_FRAG: mf_format_frag_string(value->u8, mask ? mask->u8 : UINT8_MAX, s); break; case MFS_TNL_FLAGS: mf_format_tnl_flags_string(value->be16, mask ? mask->be16 : OVS_BE16_MAX, s); break; case MFS_TCP_FLAGS: mf_format_tcp_flags_string(value->be16, mask ? mask->be16 : OVS_BE16_MAX, s); break; default: OVS_NOT_REACHED(); } } /* Makes subfield 'sf' within 'flow' exactly match the 'sf->n_bits' * least-significant bits in 'x'. */ void mf_write_subfield_flow(const struct mf_subfield *sf, const union mf_subvalue *x, struct flow *flow) { const struct mf_field *field = sf->field; union mf_value value; mf_get_value(field, flow, &value); bitwise_copy(x, sizeof *x, 0, &value, field->n_bytes, sf->ofs, sf->n_bits); mf_set_flow_value(field, &value, flow); } /* Makes subfield 'sf' within 'match' exactly match the 'sf->n_bits' * least-significant bits in 'x'. */ void mf_write_subfield(const struct mf_subfield *sf, const union mf_subvalue *x, struct match *match) { const struct mf_field *field = sf->field; union mf_value value, mask; mf_get(field, match, &value, &mask); bitwise_copy(x, sizeof *x, 0, &value, field->n_bytes, sf->ofs, sf->n_bits); bitwise_one ( &mask, field->n_bytes, sf->ofs, sf->n_bits); mf_set(field, &value, &mask, match, NULL); } /* 'v' and 'm' correspond to values of 'field'. This function copies them into * 'match' in the correspond positions. */ void mf_mask_subfield(const struct mf_field *field, const union mf_subvalue *v, const union mf_subvalue *m, struct match *match) { union mf_value value, mask; mf_get(field, match, &value, &mask); bitwise_copy(v, sizeof *v, 0, &value, field->n_bytes, 0, field->n_bits); bitwise_copy(m, sizeof *m, 0, &mask, field->n_bytes, 0, field->n_bits); mf_set(field, &value, &mask, match, NULL); } /* Initializes 'x' to the value of 'sf' within 'flow'. 'sf' must be valid for * reading 'flow', e.g. as checked by mf_check_src(). */ void mf_read_subfield(const struct mf_subfield *sf, const struct flow *flow, union mf_subvalue *x) { union mf_value value; mf_get_value(sf->field, flow, &value); memset(x, 0, sizeof *x); bitwise_copy(&value, sf->field->n_bytes, sf->ofs, x, sizeof *x, 0, sf->n_bits); } /* Returns the value of 'sf' within 'flow'. 'sf' must be valid for reading * 'flow', e.g. as checked by mf_check_src() and sf->n_bits must be 64 or * less. */ uint64_t mf_get_subfield(const struct mf_subfield *sf, const struct flow *flow) { union mf_value value; mf_get_value(sf->field, flow, &value); return bitwise_get(&value, sf->field->n_bytes, sf->ofs, sf->n_bits); } void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s) { ds_put_hex(s, subvalue->u8, sizeof subvalue->u8); } void field_array_set(enum mf_field_id id, const union mf_value *value, struct field_array *fa) { ovs_assert(id < MFF_N_IDS); bitmap_set1(fa->used.bm, id); fa->value[id] = *value; } openvswitch-2.5.9/lib/PaxHeaders.82075/command-line.c0000644000000000000000000000013213534540071017121 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.677852659 openvswitch-2.5.9/lib/command-line.c0000644000175000017500000001767513534540071020627 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "command-line.h" #include #include #include #include "dynamic-string.h" #include "ovs-thread.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(command_line); /* Given the GNU-style long options in 'options', returns a string that may be * passed to getopt() with the corresponding short options. The caller is * responsible for freeing the string. */ char * ovs_cmdl_long_options_to_short_options(const struct option options[]) { char short_options[UCHAR_MAX * 3 + 1]; char *p = short_options; for (; options->name; options++) { const struct option *o = options; if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) { *p++ = o->val; if (o->has_arg == required_argument) { *p++ = ':'; } else if (o->has_arg == optional_argument) { *p++ = ':'; *p++ = ':'; } } } *p = '\0'; return xstrdup(short_options); } /* Given the 'struct ovs_cmdl_command' array, prints the usage of all commands. */ void ovs_cmdl_print_commands(const struct ovs_cmdl_command commands[]) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_cstr(&ds, "The available commands are:\n"); for (; commands->name; commands++) { const struct ovs_cmdl_command *c = commands; ds_put_format(&ds, " %-23s %s\n", c->name, c->usage ? c->usage : ""); } printf("%s", ds.string); ds_destroy(&ds); } /* Given the GNU-style options in 'options', prints all options. */ void ovs_cmdl_print_options(const struct option options[]) { struct ds ds = DS_EMPTY_INITIALIZER; for (; options->name; options++) { const struct option *o = options; const char *arg = o->has_arg == required_argument ? "ARG" : "[ARG]"; ds_put_format(&ds, "--%s%s%s\n", o->name, o->has_arg ? "=" : "", o->has_arg ? arg : ""); if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) { ds_put_format(&ds, "-%c %s\n", o->val, o->has_arg ? arg : ""); } } printf("%s", ds.string); ds_destroy(&ds); } /* Runs the command designated by argv[0] within the command table specified by * 'commands', which must be terminated by a command whose 'name' member is a * null pointer. * * Command-line options should be stripped off, so that a typical invocation * looks like: * struct ovs_cmdl_context ctx = { * .argc = argc - optind, * .argv = argv + optind, * }; * ovs_cmdl_run_command(&ctx, my_commands); * */ void ovs_cmdl_run_command(struct ovs_cmdl_context *ctx, const struct ovs_cmdl_command commands[]) { const struct ovs_cmdl_command *p; if (ctx->argc < 1) { ovs_fatal(0, "missing command name; use --help for help"); } for (p = commands; p->name != NULL; p++) { if (!strcmp(p->name, ctx->argv[0])) { int n_arg = ctx->argc - 1; if (n_arg < p->min_args) { VLOG_FATAL( "'%s' command requires at least %d arguments", p->name, p->min_args); } else if (n_arg > p->max_args) { VLOG_FATAL("'%s' command takes at most %d arguments", p->name, p->max_args); } else { p->handler(ctx); if (ferror(stdout)) { VLOG_FATAL("write to stdout failed"); } if (ferror(stderr)) { VLOG_FATAL("write to stderr failed"); } return; } } } VLOG_FATAL("unknown command '%s'; use --help for help", ctx->argv[0]); } /* Process title. */ #ifdef __linux__ static struct ovs_mutex proctitle_mutex = OVS_MUTEX_INITIALIZER; /* Start of command-line arguments in memory. */ static char *argv_start OVS_GUARDED_BY(proctitle_mutex); /* Number of bytes of command-line arguments. */ static size_t argv_size OVS_GUARDED_BY(proctitle_mutex); /* Saved command-line arguments. */ static char *saved_proctitle OVS_GUARDED_BY(proctitle_mutex); /* Prepares the process so that proctitle_set() can later succeed. * * This modifies the argv[] array so that it no longer points into the memory * that it originally does. Later, proctitle_set() might overwrite that * memory. That means that this function should be called before anything else * that accesses the process's argv[] array. Ideally, it should be called * before anything else, period, at the very beginning of program * execution. */ void ovs_cmdl_proctitle_init(int argc, char **argv) { int i; assert_single_threaded(); if (!argc || !argv[0]) { /* This situation should never occur, but... */ return; } ovs_mutex_lock(&proctitle_mutex); /* Specialized version of first loop iteration below. */ argv_start = argv[0]; argv_size = strlen(argv[0]) + 1; argv[0] = xstrdup(argv[0]); for (i = 1; i < argc; i++) { size_t size = strlen(argv[i]) + 1; /* Add (argv[i], strlen(argv[i])+1) to (argv_start, argv_size). */ if (argv[i] + size == argv_start) { /* Arguments grow downward in memory. */ argv_start -= size; argv_size += size; } else if (argv[i] == argv_start + argv_size) { /* Arguments grow upward in memory. */ argv_size += size; } else { /* Arguments not contiguous. (Is this really Linux?) */ } /* Copy out the old argument so we can reuse the space. */ argv[i] = xstrdup(argv[i]); } ovs_mutex_unlock(&proctitle_mutex); } /* Changes the name of the process, as shown by "ps", to the program name * followed by 'format', which is formatted as if by printf(). */ void ovs_cmdl_proctitle_set(const char *format, ...) { va_list args; int n; ovs_mutex_lock(&proctitle_mutex); if (!argv_start || argv_size < 8) { goto out; } if (!saved_proctitle) { saved_proctitle = xmemdup(argv_start, argv_size); } va_start(args, format); n = snprintf(argv_start, argv_size, "%s: ", program_name); if (n < argv_size) { n += vsnprintf(argv_start + n, argv_size - n, format, args); } if (n >= argv_size) { /* The name is too long, so add an ellipsis at the end. */ strcpy(&argv_start[argv_size - 4], "..."); } else { /* Fill the extra space with null bytes, so that trailing bytes don't * show up in the command line. */ memset(&argv_start[n], '\0', argv_size - n); } va_end(args); out: ovs_mutex_unlock(&proctitle_mutex); } /* Restores the process's original command line, as seen by "ps". */ void ovs_cmdl_proctitle_restore(void) { ovs_mutex_lock(&proctitle_mutex); if (saved_proctitle) { memcpy(argv_start, saved_proctitle, argv_size); free(saved_proctitle); saved_proctitle = NULL; } ovs_mutex_unlock(&proctitle_mutex); } #else /* !__linux__ */ /* Stubs that don't do anything on non-Linux systems. */ void ovs_cmdl_proctitle_init(int argc OVS_UNUSED, char **argv OVS_UNUSED) { } #if !(defined(__FreeBSD__) || defined(__NetBSD__)) /* On these platforms we #define this to setproctitle. */ void ovs_cmdl_proctitle_set(const char *format OVS_UNUSED, ...) { } #endif void ovs_cmdl_proctitle_restore(void) { } #endif /* !__linux__ */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev.c0000644000000000000000000000013213534540071016043 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 30 ctime=1567801424.777853396 openvswitch-2.5.9/lib/netdev.c0000644000175000017500000016515313534540071017544 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netdev.h" #include #include #include #include #include #include #include "coverage.h" #include "dpif.h" #include "dp-packet.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "list.h" #include "netdev-dpdk.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "odp-netlink.h" #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "seq.h" #include "shash.h" #include "smap.h" #include "sset.h" #include "svec.h" #include "openvswitch/vlog.h" #include "flow.h" VLOG_DEFINE_THIS_MODULE(netdev); COVERAGE_DEFINE(netdev_received); COVERAGE_DEFINE(netdev_sent); COVERAGE_DEFINE(netdev_add_router); COVERAGE_DEFINE(netdev_get_stats); struct netdev_saved_flags { struct netdev *netdev; struct ovs_list node; /* In struct netdev's saved_flags_list. */ enum netdev_flags saved_flags; enum netdev_flags saved_values; }; /* Protects 'netdev_shash' and the mutable members of struct netdev. */ static struct ovs_mutex netdev_mutex = OVS_MUTEX_INITIALIZER; /* All created network devices. */ static struct shash netdev_shash OVS_GUARDED_BY(netdev_mutex) = SHASH_INITIALIZER(&netdev_shash); /* Protects 'netdev_classes' against insertions or deletions. * * This is a recursive mutex to allow recursive acquisition when calling into * providers. For example, netdev_run() calls into provider 'run' functions, * which might reasonably want to call one of the netdev functions that takes * netdev_class_mutex. */ static struct ovs_mutex netdev_class_mutex OVS_ACQ_BEFORE(netdev_mutex); /* Contains 'struct netdev_registered_class'es. */ static struct hmap netdev_classes OVS_GUARDED_BY(netdev_class_mutex) = HMAP_INITIALIZER(&netdev_classes); struct netdev_registered_class { /* In 'netdev_classes', by class->type. */ struct hmap_node hmap_node OVS_GUARDED_BY(netdev_class_mutex); const struct netdev_class *class OVS_GUARDED_BY(netdev_class_mutex); /* Number of 'struct netdev's of this class. */ int ref_cnt OVS_GUARDED_BY(netdev_class_mutex); }; /* This is set pretty low because we probably won't learn anything from the * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); static void restore_all_flags(void *aux OVS_UNUSED); void update_device_args(struct netdev *, const struct shash *args); int netdev_n_txq(const struct netdev *netdev) { return netdev->n_txq; } int netdev_n_rxq(const struct netdev *netdev) { return netdev->n_rxq; } bool netdev_is_pmd(const struct netdev *netdev) { return (!strcmp(netdev->netdev_class->type, "dpdk") || !strcmp(netdev->netdev_class->type, "dpdkr") || !strcmp(netdev->netdev_class->type, "dpdkvhostcuse") || !strcmp(netdev->netdev_class->type, "dpdkvhostuser")); } static void netdev_class_mutex_initialize(void) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { ovs_mutex_init_recursive(&netdev_class_mutex); ovsthread_once_done(&once); } } static void netdev_initialize(void) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { netdev_class_mutex_initialize(); fatal_signal_add_hook(restore_all_flags, NULL, NULL, true); netdev_vport_patch_register(); #ifdef __linux__ netdev_register_provider(&netdev_linux_class); netdev_register_provider(&netdev_internal_class); netdev_register_provider(&netdev_tap_class); netdev_vport_tunnel_register(); #endif #if defined(__FreeBSD__) || defined(__NetBSD__) netdev_register_provider(&netdev_tap_class); netdev_register_provider(&netdev_bsd_class); #endif #ifdef _WIN32 netdev_register_provider(&netdev_windows_class); netdev_register_provider(&netdev_internal_class); netdev_vport_tunnel_register(); #endif netdev_dpdk_register(); ovsthread_once_done(&once); } } /* Performs periodic work needed by all the various kinds of netdevs. * * If your program opens any netdevs, it must call this function within its * main poll loop. */ void netdev_run(void) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { struct netdev_registered_class *rc; netdev_initialize(); ovs_mutex_lock(&netdev_class_mutex); HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) { if (rc->class->run) { rc->class->run(); } } ovs_mutex_unlock(&netdev_class_mutex); } /* Arranges for poll_block() to wake up when netdev_run() needs to be called. * * If your program opens any netdevs, it must call this function within its * main poll loop. */ void netdev_wait(void) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { struct netdev_registered_class *rc; ovs_mutex_lock(&netdev_class_mutex); HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) { if (rc->class->wait) { rc->class->wait(); } } ovs_mutex_unlock(&netdev_class_mutex); } static struct netdev_registered_class * netdev_lookup_class(const char *type) OVS_REQ_RDLOCK(netdev_class_mutex) { struct netdev_registered_class *rc; HMAP_FOR_EACH_WITH_HASH (rc, hmap_node, hash_string(type, 0), &netdev_classes) { if (!strcmp(type, rc->class->type)) { return rc; } } return NULL; } /* Initializes and registers a new netdev provider. After successful * registration, new netdevs of that type can be opened using netdev_open(). */ int netdev_register_provider(const struct netdev_class *new_class) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { int error; netdev_class_mutex_initialize(); ovs_mutex_lock(&netdev_class_mutex); if (netdev_lookup_class(new_class->type)) { VLOG_WARN("attempted to register duplicate netdev provider: %s", new_class->type); error = EEXIST; } else { error = new_class->init ? new_class->init() : 0; if (!error) { struct netdev_registered_class *rc; rc = xmalloc(sizeof *rc); hmap_insert(&netdev_classes, &rc->hmap_node, hash_string(new_class->type, 0)); rc->class = new_class; rc->ref_cnt = 0; } else { VLOG_ERR("failed to initialize %s network device class: %s", new_class->type, ovs_strerror(error)); } } ovs_mutex_unlock(&netdev_class_mutex); return error; } /* Unregisters a netdev provider. 'type' must have been previously * registered and not currently be in use by any netdevs. After unregistration * new netdevs of that type cannot be opened using netdev_open(). */ int netdev_unregister_provider(const char *type) OVS_EXCLUDED(netdev_class_mutex, netdev_mutex) { struct netdev_registered_class *rc; int error; netdev_initialize(); ovs_mutex_lock(&netdev_class_mutex); rc = netdev_lookup_class(type); if (!rc) { VLOG_WARN("attempted to unregister a netdev provider that is not " "registered: %s", type); error = EAFNOSUPPORT; } else { if (!rc->ref_cnt) { hmap_remove(&netdev_classes, &rc->hmap_node); free(rc); error = 0; } else { VLOG_WARN("attempted to unregister in use netdev provider: %s", type); error = EBUSY; } } ovs_mutex_unlock(&netdev_class_mutex); return error; } /* Clears 'types' and enumerates the types of all currently registered netdev * providers into it. The caller must first initialize the sset. */ void netdev_enumerate_types(struct sset *types) OVS_EXCLUDED(netdev_mutex) { struct netdev_registered_class *rc; netdev_initialize(); sset_clear(types); ovs_mutex_lock(&netdev_class_mutex); HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) { sset_add(types, rc->class->type); } ovs_mutex_unlock(&netdev_class_mutex); } /* Check that the network device name is not the same as any of the registered * vport providers' dpif_port name (dpif_port is NULL if the vport provider * does not define it) or the datapath internal port name (e.g. ovs-system). * * Returns true if there is a name conflict, false otherwise. */ bool netdev_is_reserved_name(const char *name) OVS_EXCLUDED(netdev_mutex) { struct netdev_registered_class *rc; netdev_initialize(); ovs_mutex_lock(&netdev_class_mutex); HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) { const char *dpif_port = netdev_vport_class_get_dpif_port(rc->class); if (dpif_port && !strncmp(name, dpif_port, strlen(dpif_port))) { ovs_mutex_unlock(&netdev_class_mutex); return true; } } ovs_mutex_unlock(&netdev_class_mutex); if (!strncmp(name, "ovs-", 4)) { struct sset types; const char *type; sset_init(&types); dp_enumerate_types(&types); SSET_FOR_EACH (type, &types) { if (!strcmp(name+4, type)) { sset_destroy(&types); return true; } } sset_destroy(&types); } return false; } /* Opens the network device named 'name' (e.g. "eth0") of the specified 'type' * (e.g. "system") and returns zero if successful, otherwise a positive errno * value. On success, sets '*netdevp' to the new network device, otherwise to * null. * * Some network devices may need to be configured (with netdev_set_config()) * before they can be used. */ int netdev_open(const char *name, const char *type, struct netdev **netdevp) OVS_EXCLUDED(netdev_mutex) { struct netdev *netdev; int error; netdev_initialize(); ovs_mutex_lock(&netdev_class_mutex); ovs_mutex_lock(&netdev_mutex); netdev = shash_find_data(&netdev_shash, name); if (!netdev) { struct netdev_registered_class *rc; rc = netdev_lookup_class(type && type[0] ? type : "system"); if (rc) { netdev = rc->class->alloc(); if (netdev) { memset(netdev, 0, sizeof *netdev); netdev->netdev_class = rc->class; netdev->name = xstrdup(name); netdev->change_seq = 1; netdev->node = shash_add(&netdev_shash, name, netdev); /* By default enable one tx and rx queue per netdev. */ netdev->n_txq = netdev->netdev_class->send ? 1 : 0; netdev->n_rxq = netdev->netdev_class->rxq_alloc ? 1 : 0; list_init(&netdev->saved_flags_list); error = rc->class->construct(netdev); if (!error) { rc->ref_cnt++; netdev_change_seq_changed(netdev); } else { free(netdev->name); ovs_assert(list_is_empty(&netdev->saved_flags_list)); shash_delete(&netdev_shash, netdev->node); rc->class->dealloc(netdev); } } else { error = ENOMEM; } } else { VLOG_WARN("could not create netdev %s of unknown type %s", name, type); error = EAFNOSUPPORT; } } else { error = 0; } if (!error) { netdev->ref_cnt++; *netdevp = netdev; } else { *netdevp = NULL; } ovs_mutex_unlock(&netdev_mutex); ovs_mutex_unlock(&netdev_class_mutex); return error; } /* Returns a reference to 'netdev_' for the caller to own. Returns null if * 'netdev_' is null. */ struct netdev * netdev_ref(const struct netdev *netdev_) OVS_EXCLUDED(netdev_mutex) { struct netdev *netdev = CONST_CAST(struct netdev *, netdev_); if (netdev) { ovs_mutex_lock(&netdev_mutex); ovs_assert(netdev->ref_cnt > 0); netdev->ref_cnt++; ovs_mutex_unlock(&netdev_mutex); } return netdev; } /* Reconfigures the device 'netdev' with 'args'. 'args' may be empty * or NULL if none are needed. */ int netdev_set_config(struct netdev *netdev, const struct smap *args, char **errp) OVS_EXCLUDED(netdev_mutex) { if (netdev->netdev_class->set_config) { const struct smap no_args = SMAP_INITIALIZER(&no_args); int error; error = netdev->netdev_class->set_config(netdev, args ? args : &no_args); if (error) { VLOG_WARN_BUF(errp, "%s: could not set configuration (%s)", netdev_get_name(netdev), ovs_strerror(error)); } return error; } else if (args && !smap_is_empty(args)) { VLOG_WARN_BUF(errp, "%s: arguments provided to device that is not configurable", netdev_get_name(netdev)); } return 0; } /* Returns the current configuration for 'netdev' in 'args'. The caller must * have already initialized 'args' with smap_init(). Returns 0 on success, in * which case 'args' will be filled with 'netdev''s configuration. On failure * returns a positive errno value, in which case 'args' will be empty. * * The caller owns 'args' and its contents and must eventually free them with * smap_destroy(). */ int netdev_get_config(const struct netdev *netdev, struct smap *args) OVS_EXCLUDED(netdev_mutex) { int error; smap_clear(args); if (netdev->netdev_class->get_config) { error = netdev->netdev_class->get_config(netdev, args); if (error) { smap_clear(args); } } else { error = 0; } return error; } const struct netdev_tunnel_config * netdev_get_tunnel_config(const struct netdev *netdev) OVS_EXCLUDED(netdev_mutex) { if (netdev->netdev_class->get_tunnel_config) { return netdev->netdev_class->get_tunnel_config(netdev); } else { return NULL; } } /* Returns the id of the numa node the 'netdev' is on. If the function * is not implemented, returns NETDEV_NUMA_UNSPEC. */ int netdev_get_numa_id(const struct netdev *netdev) { if (netdev->netdev_class->get_numa_id) { return netdev->netdev_class->get_numa_id(netdev); } else { return NETDEV_NUMA_UNSPEC; } } static void netdev_unref(struct netdev *dev) OVS_RELEASES(netdev_mutex) { ovs_assert(dev->ref_cnt); if (!--dev->ref_cnt) { const struct netdev_class *class = dev->netdev_class; struct netdev_registered_class *rc; dev->netdev_class->destruct(dev); if (dev->node) { shash_delete(&netdev_shash, dev->node); } free(dev->name); dev->netdev_class->dealloc(dev); ovs_mutex_unlock(&netdev_mutex); ovs_mutex_lock(&netdev_class_mutex); rc = netdev_lookup_class(class->type); ovs_assert(rc->ref_cnt > 0); rc->ref_cnt--; ovs_mutex_unlock(&netdev_class_mutex); } else { ovs_mutex_unlock(&netdev_mutex); } } /* Closes and destroys 'netdev'. */ void netdev_close(struct netdev *netdev) OVS_EXCLUDED(netdev_mutex) { if (netdev) { ovs_mutex_lock(&netdev_mutex); netdev_unref(netdev); } } /* Removes 'netdev' from the global shash and unrefs 'netdev'. * * This allows handler and revalidator threads to still retain references * to this netdev while the main thread changes interface configuration. * * This function should only be called by the main thread when closing * netdevs during user configuration changes. Otherwise, netdev_close should be * used to close netdevs. */ void netdev_remove(struct netdev *netdev) { if (netdev) { ovs_mutex_lock(&netdev_mutex); if (netdev->node) { shash_delete(&netdev_shash, netdev->node); netdev->node = NULL; netdev_change_seq_changed(netdev); } netdev_unref(netdev); } } /* Parses 'netdev_name_', which is of the form [type@]name into its component * pieces. 'name' and 'type' must be freed by the caller. */ void netdev_parse_name(const char *netdev_name_, char **name, char **type) { char *netdev_name = xstrdup(netdev_name_); char *separator; separator = strchr(netdev_name, '@'); if (separator) { *separator = '\0'; *type = netdev_name; *name = xstrdup(separator + 1); } else { *name = netdev_name; *type = xstrdup("system"); } } /* Attempts to open a netdev_rxq handle for obtaining packets received on * 'netdev'. On success, returns 0 and stores a nonnull 'netdev_rxq *' into * '*rxp'. On failure, returns a positive errno value and stores NULL into * '*rxp'. * * Some kinds of network devices might not support receiving packets. This * function returns EOPNOTSUPP in that case.*/ int netdev_rxq_open(struct netdev *netdev, struct netdev_rxq **rxp, int id) OVS_EXCLUDED(netdev_mutex) { int error; if (netdev->netdev_class->rxq_alloc && id < netdev->n_rxq) { struct netdev_rxq *rx = netdev->netdev_class->rxq_alloc(); if (rx) { rx->netdev = netdev; rx->queue_id = id; error = netdev->netdev_class->rxq_construct(rx); if (!error) { netdev_ref(netdev); *rxp = rx; return 0; } netdev->netdev_class->rxq_dealloc(rx); } else { error = ENOMEM; } } else { error = EOPNOTSUPP; } *rxp = NULL; return error; } /* Closes 'rx'. */ void netdev_rxq_close(struct netdev_rxq *rx) OVS_EXCLUDED(netdev_mutex) { if (rx) { struct netdev *netdev = rx->netdev; netdev->netdev_class->rxq_destruct(rx); netdev->netdev_class->rxq_dealloc(rx); netdev_close(netdev); } } /* Attempts to receive batch of packets from 'rx'. * * Returns EAGAIN immediately if no packet is ready to be received. * * Returns EMSGSIZE, and discards the packet, if the received packet is longer * than 'dp_packet_tailroom(buffer)'. * * It is advised that the tailroom of 'buffer' should be * VLAN_HEADER_LEN bytes longer than the MTU to allow space for an * out-of-band VLAN header to be added to the packet. At the very least, * 'buffer' must have at least ETH_TOTAL_MIN bytes of tailroom. * * This function may be set to null if it would always return EOPNOTSUPP * anyhow. */ int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet **buffers, int *cnt) { int retval; retval = rx->netdev->netdev_class->rxq_recv(rx, buffers, cnt); if (!retval) { COVERAGE_INC(netdev_received); } return retval; } /* Arranges for poll_block() to wake up when a packet is ready to be received * on 'rx'. */ void netdev_rxq_wait(struct netdev_rxq *rx) { rx->netdev->netdev_class->rxq_wait(rx); } /* Discards any packets ready to be received on 'rx'. */ int netdev_rxq_drain(struct netdev_rxq *rx) { return (rx->netdev->netdev_class->rxq_drain ? rx->netdev->netdev_class->rxq_drain(rx) : 0); } /* Configures the number of tx queues and rx queues of 'netdev'. * Return 0 if successful, otherwise a positive errno value. * * 'n_rxq' specifies the maximum number of receive queues to create. * The netdev provider might choose to create less (e.g. if the hardware * supports only a smaller number). The caller can check how many have been * actually created by calling 'netdev_n_rxq()' * * 'n_txq' specifies the exact number of transmission queues to create. * If this function returns successfully, the caller can make 'n_txq' * concurrent calls to netdev_send() (each one with a different 'qid' in the * range [0..'n_txq'-1]). * * On error, the tx queue and rx queue configuration is indeterminant. * Caller should make decision on whether to restore the previous or * the default configuration. Also, caller must make sure there is no * other thread accessing the queues at the same time. */ int netdev_set_multiq(struct netdev *netdev, unsigned int n_txq, unsigned int n_rxq) { int error; error = (netdev->netdev_class->set_multiq ? netdev->netdev_class->set_multiq(netdev, MAX(n_txq, 1), MAX(n_rxq, 1)) : EOPNOTSUPP); if (error && error != EOPNOTSUPP) { VLOG_DBG_RL(&rl, "failed to set tx/rx queue for network device %s:" "%s", netdev_get_name(netdev), ovs_strerror(error)); } return error; } /* Sends 'buffers' on 'netdev'. Returns 0 if successful (for every packet), * otherwise a positive errno value. Returns EAGAIN without blocking if * at least one the packets cannot be queued immediately. Returns EMSGSIZE * if a partial packet was transmitted or if a packet is too big or too small * to transmit on the device. * * If the function returns a non-zero value, some of the packets might have * been sent anyway. * * To retain ownership of 'buffer' caller can set may_steal to false. * * The network device is expected to maintain one or more packet * transmission queues, so that the caller does not ordinarily have to * do additional queuing of packets. 'qid' specifies the queue to use * and can be ignored if the implementation does not support multiple * queues. * * Some network devices may not implement support for this function. In such * cases this function will always return EOPNOTSUPP. */ int netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers, int cnt, bool may_steal) { int error; error = (netdev->netdev_class->send ? netdev->netdev_class->send(netdev, qid, buffers, cnt, may_steal) : EOPNOTSUPP); if (!error) { COVERAGE_INC(netdev_sent); } return error; } int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int cnt) { int i; if (!netdev->netdev_class->pop_header) { return EOPNOTSUPP; } for (i = 0; i < cnt; i++) { int err; err = netdev->netdev_class->pop_header(buffers[i]); if (err) { dp_packet_clear(buffers[i]); } } return 0; } int netdev_build_header(const struct netdev *netdev, struct ovs_action_push_tnl *data, const struct flow *tnl_flow) { if (netdev->netdev_class->build_header) { return netdev->netdev_class->build_header(netdev, data, tnl_flow); } return EOPNOTSUPP; } int netdev_push_header(const struct netdev *netdev, struct dp_packet **buffers, int cnt, const struct ovs_action_push_tnl *data) { int i; if (!netdev->netdev_class->push_header) { return -EINVAL; } for (i = 0; i < cnt; i++) { netdev->netdev_class->push_header(buffers[i], data); pkt_metadata_init(&buffers[i]->md, u32_to_odp(data->out_port)); } return 0; } /* Registers with the poll loop to wake up from the next call to poll_block() * when the packet transmission queue has sufficient room to transmit a packet * with netdev_send(). * * The network device is expected to maintain one or more packet * transmission queues, so that the caller does not ordinarily have to * do additional queuing of packets. 'qid' specifies the queue to use * and can be ignored if the implementation does not support multiple * queues. */ void netdev_send_wait(struct netdev *netdev, int qid) { if (netdev->netdev_class->send_wait) { netdev->netdev_class->send_wait(netdev, qid); } } /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, * otherwise a positive errno value. */ int netdev_set_etheraddr(struct netdev *netdev, const struct eth_addr mac) { return netdev->netdev_class->set_etheraddr(netdev, mac); } /* Retrieves 'netdev''s MAC address. If successful, returns 0 and copies the * the MAC address into 'mac'. On failure, returns a positive errno value and * clears 'mac' to all-zeros. */ int netdev_get_etheraddr(const struct netdev *netdev, struct eth_addr *mac) { int error; error = netdev->netdev_class->get_etheraddr(netdev, mac); if (error) { memset(mac, 0, sizeof *mac); } return error; } /* Returns the name of the network device that 'netdev' represents, * e.g. "eth0". The caller must not modify or free the returned string. */ const char * netdev_get_name(const struct netdev *netdev) { return netdev->name; } /* Retrieves the MTU of 'netdev'. The MTU is the maximum size of transmitted * (and received) packets, in bytes, not including the hardware header; thus, * this is typically 1500 bytes for Ethernet devices. * * If successful, returns 0 and stores the MTU size in '*mtup'. Returns * EOPNOTSUPP if 'netdev' does not have an MTU (as e.g. some tunnels do not). * On other failure, returns a positive errno value. On failure, sets '*mtup' * to 0. */ int netdev_get_mtu(const struct netdev *netdev, int *mtup) { const struct netdev_class *class = netdev->netdev_class; int error; error = class->get_mtu ? class->get_mtu(netdev, mtup) : EOPNOTSUPP; if (error) { *mtup = 0; if (error != EOPNOTSUPP) { VLOG_DBG_RL(&rl, "failed to retrieve MTU for network device %s: " "%s", netdev_get_name(netdev), ovs_strerror(error)); } } return error; } /* Sets the MTU of 'netdev'. The MTU is the maximum size of transmitted * (and received) packets, in bytes. * * If successful, returns 0. Returns EOPNOTSUPP if 'netdev' does not have an * MTU (as e.g. some tunnels do not). On other failure, returns a positive * errno value. */ int netdev_set_mtu(const struct netdev *netdev, int mtu) { const struct netdev_class *class = netdev->netdev_class; int error; error = class->set_mtu ? class->set_mtu(netdev, mtu) : EOPNOTSUPP; if (error && error != EOPNOTSUPP) { VLOG_DBG_RL(&rl, "failed to set MTU for network device %s: %s", netdev_get_name(netdev), ovs_strerror(error)); } return error; } /* Returns the ifindex of 'netdev', if successful, as a positive number. On * failure, returns a negative errno value. * * The desired semantics of the ifindex value are a combination of those * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An ifindex * value should be unique within a host and remain stable at least until * reboot. SNMP says an ifindex "ranges between 1 and the value of ifNumber" * but many systems do not follow this rule anyhow. * * Some network devices may not implement support for this function. In such * cases this function will always return -EOPNOTSUPP. */ int netdev_get_ifindex(const struct netdev *netdev) { int (*get_ifindex)(const struct netdev *); get_ifindex = netdev->netdev_class->get_ifindex; return get_ifindex ? get_ifindex(netdev) : -EOPNOTSUPP; } /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value is a * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if * successful, otherwise a positive errno value. On failure, all of the * passed-in values are set to 0. * * Some network devices may not implement support for this function. In such * cases this function will always return EOPNOTSUPP. */ int netdev_get_features(const struct netdev *netdev, enum netdev_features *current, enum netdev_features *advertised, enum netdev_features *supported, enum netdev_features *peer) { int (*get_features)(const struct netdev *netdev, enum netdev_features *current, enum netdev_features *advertised, enum netdev_features *supported, enum netdev_features *peer); enum netdev_features dummy[4]; int error; if (!current) { current = &dummy[0]; } if (!advertised) { advertised = &dummy[1]; } if (!supported) { supported = &dummy[2]; } if (!peer) { peer = &dummy[3]; } get_features = netdev->netdev_class->get_features; error = get_features ? get_features(netdev, current, advertised, supported, peer) : EOPNOTSUPP; if (error) { *current = *advertised = *supported = *peer = 0; } return error; } /* Returns the maximum speed of a network connection that has the NETDEV_F_* * bits in 'features', in bits per second. If no bits that indicate a speed * are set in 'features', returns 'default_bps'. */ uint64_t netdev_features_to_bps(enum netdev_features features, uint64_t default_bps) { enum { F_1000000MB = NETDEV_F_1TB_FD, F_100000MB = NETDEV_F_100GB_FD, F_40000MB = NETDEV_F_40GB_FD, F_10000MB = NETDEV_F_10GB_FD, F_1000MB = NETDEV_F_1GB_HD | NETDEV_F_1GB_FD, F_100MB = NETDEV_F_100MB_HD | NETDEV_F_100MB_FD, F_10MB = NETDEV_F_10MB_HD | NETDEV_F_10MB_FD }; return ( features & F_1000000MB ? UINT64_C(1000000000000) : features & F_100000MB ? UINT64_C(100000000000) : features & F_40000MB ? UINT64_C(40000000000) : features & F_10000MB ? UINT64_C(10000000000) : features & F_1000MB ? UINT64_C(1000000000) : features & F_100MB ? UINT64_C(100000000) : features & F_10MB ? UINT64_C(10000000) : default_bps); } /* Returns true if any of the NETDEV_F_* bits that indicate a full-duplex link * are set in 'features', otherwise false. */ bool netdev_features_is_full_duplex(enum netdev_features features) { return (features & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD | NETDEV_F_1GB_FD | NETDEV_F_10GB_FD | NETDEV_F_40GB_FD | NETDEV_F_100GB_FD | NETDEV_F_1TB_FD)) != 0; } /* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if * successful, otherwise a positive errno value. */ int netdev_set_advertisements(struct netdev *netdev, enum netdev_features advertise) { return (netdev->netdev_class->set_advertisements ? netdev->netdev_class->set_advertisements( netdev, advertise) : EOPNOTSUPP); } /* If 'netdev' has an assigned IPv4 address, sets '*address' to that address * and '*netmask' to its netmask and returns 0. Otherwise, returns a positive * errno value and sets '*address' to 0 (INADDR_ANY). * * The following error values have well-defined meanings: * * - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address. * * - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'. * * 'address' or 'netmask' or both may be null, in which case the address or * netmask is not reported. */ int netdev_get_in4(const struct netdev *netdev, struct in_addr *address_, struct in_addr *netmask_) { struct in_addr address; struct in_addr netmask; int error; error = (netdev->netdev_class->get_in4 ? netdev->netdev_class->get_in4(netdev, &address, &netmask) : EOPNOTSUPP); if (address_) { address_->s_addr = error ? 0 : address.s_addr; } if (netmask_) { netmask_->s_addr = error ? 0 : netmask.s_addr; } return error; } /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a * positive errno value. */ int netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask) { return (netdev->netdev_class->set_in4 ? netdev->netdev_class->set_in4(netdev, addr, mask) : EOPNOTSUPP); } /* Obtains ad IPv4 address from device name and save the address in * in4. Returns 0 if successful, otherwise a positive errno value. */ int netdev_get_in4_by_name(const char *device_name, struct in_addr *in4) { struct netdev *netdev; int error; error = netdev_open(device_name, "system", &netdev); if (error) { in4->s_addr = htonl(0); return error; } error = netdev_get_in4(netdev, in4, NULL); netdev_close(netdev); return error; } /* Adds 'router' as a default IP gateway for the TCP/IP stack that corresponds * to 'netdev'. */ int netdev_add_router(struct netdev *netdev, struct in_addr router) { COVERAGE_INC(netdev_add_router); return (netdev->netdev_class->add_router ? netdev->netdev_class->add_router(netdev, router) : EOPNOTSUPP); } /* Looks up the next hop for 'host' for the TCP/IP stack that corresponds to * 'netdev'. If a route cannot not be determined, sets '*next_hop' to 0, * '*netdev_name' to null, and returns a positive errno value. Otherwise, if a * next hop is found, stores the next hop gateway's address (0 if 'host' is on * a directly connected network) in '*next_hop' and a copy of the name of the * device to reach 'host' in '*netdev_name', and returns 0. The caller is * responsible for freeing '*netdev_name' (by calling free()). */ int netdev_get_next_hop(const struct netdev *netdev, const struct in_addr *host, struct in_addr *next_hop, char **netdev_name) { int error = (netdev->netdev_class->get_next_hop ? netdev->netdev_class->get_next_hop( host, next_hop, netdev_name) : EOPNOTSUPP); if (error) { next_hop->s_addr = 0; *netdev_name = NULL; } return error; } /* Populates 'smap' with status information. * * Populates 'smap' with 'netdev' specific status information. This * information may be used to populate the status column of the Interface table * as defined in ovs-vswitchd.conf.db(5). */ int netdev_get_status(const struct netdev *netdev, struct smap *smap) { return (netdev->netdev_class->get_status ? netdev->netdev_class->get_status(netdev, smap) : EOPNOTSUPP); } /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and * returns 0. Otherwise, returns a positive errno value and sets '*in6' to * all-zero-bits (in6addr_any). * * The following error values have well-defined meanings: * * - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address. * * - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'. * * 'in6' may be null, in which case the address itself is not reported. */ int netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6) { struct in6_addr dummy; int error; error = (netdev->netdev_class->get_in6 ? netdev->netdev_class->get_in6(netdev, in6 ? in6 : &dummy) : EOPNOTSUPP); if (error && in6) { memset(in6, 0, sizeof *in6); } return error; } /* On 'netdev', turns off the flags in 'off' and then turns on the flags in * 'on'. Returns 0 if successful, otherwise a positive errno value. */ static int do_update_flags(struct netdev *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp, struct netdev_saved_flags **sfp) OVS_EXCLUDED(netdev_mutex) { struct netdev_saved_flags *sf = NULL; enum netdev_flags old_flags; int error; error = netdev->netdev_class->update_flags(netdev, off & ~on, on, &old_flags); if (error) { VLOG_WARN_RL(&rl, "failed to %s flags for network device %s: %s", off || on ? "set" : "get", netdev_get_name(netdev), ovs_strerror(error)); old_flags = 0; } else if ((off || on) && sfp) { enum netdev_flags new_flags = (old_flags & ~off) | on; enum netdev_flags changed_flags = old_flags ^ new_flags; if (changed_flags) { ovs_mutex_lock(&netdev_mutex); *sfp = sf = xmalloc(sizeof *sf); sf->netdev = netdev; list_push_front(&netdev->saved_flags_list, &sf->node); sf->saved_flags = changed_flags; sf->saved_values = changed_flags & new_flags; netdev->ref_cnt++; ovs_mutex_unlock(&netdev_mutex); } } if (old_flagsp) { *old_flagsp = old_flags; } if (sfp) { *sfp = sf; } return error; } /* Obtains the current flags for 'netdev' and stores them into '*flagsp'. * Returns 0 if successful, otherwise a positive errno value. On failure, * stores 0 into '*flagsp'. */ int netdev_get_flags(const struct netdev *netdev_, enum netdev_flags *flagsp) { struct netdev *netdev = CONST_CAST(struct netdev *, netdev_); return do_update_flags(netdev, 0, 0, flagsp, NULL); } /* Sets the flags for 'netdev' to 'flags'. * Returns 0 if successful, otherwise a positive errno value. */ int netdev_set_flags(struct netdev *netdev, enum netdev_flags flags, struct netdev_saved_flags **sfp) { return do_update_flags(netdev, -1, flags, NULL, sfp); } /* Turns on the specified 'flags' on 'netdev': * * - On success, returns 0. If 'sfp' is nonnull, sets '*sfp' to a newly * allocated 'struct netdev_saved_flags *' that may be passed to * netdev_restore_flags() to restore the original values of 'flags' on * 'netdev' (this will happen automatically at program termination if * netdev_restore_flags() is never called) , or to NULL if no flags were * actually changed. * * - On failure, returns a positive errno value. If 'sfp' is nonnull, sets * '*sfp' to NULL. */ int netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags, struct netdev_saved_flags **sfp) { return do_update_flags(netdev, 0, flags, NULL, sfp); } /* Turns off the specified 'flags' on 'netdev'. See netdev_turn_flags_on() for * details of the interface. */ int netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags, struct netdev_saved_flags **sfp) { return do_update_flags(netdev, flags, 0, NULL, sfp); } /* Restores the flags that were saved in 'sf', and destroys 'sf'. * Does nothing if 'sf' is NULL. */ void netdev_restore_flags(struct netdev_saved_flags *sf) OVS_EXCLUDED(netdev_mutex) { if (sf) { struct netdev *netdev = sf->netdev; enum netdev_flags old_flags; netdev->netdev_class->update_flags(netdev, sf->saved_flags & sf->saved_values, sf->saved_flags & ~sf->saved_values, &old_flags); ovs_mutex_lock(&netdev_mutex); list_remove(&sf->node); free(sf); netdev_unref(netdev); } } /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be * successfully retrieved, it stores the corresponding MAC address in 'mac' and * returns 0. Otherwise, it returns a positive errno value; in particular, * ENXIO indicates that there is no ARP table entry for 'ip' on 'netdev'. */ int netdev_arp_lookup(const struct netdev *netdev, ovs_be32 ip, struct eth_addr *mac) { int error = (netdev->netdev_class->arp_lookup ? netdev->netdev_class->arp_lookup(netdev, ip, mac) : EOPNOTSUPP); if (error) { *mac = eth_addr_zero; } return error; } /* Returns true if carrier is active (link light is on) on 'netdev'. */ bool netdev_get_carrier(const struct netdev *netdev) { int error; enum netdev_flags flags; bool carrier; netdev_get_flags(netdev, &flags); if (!(flags & NETDEV_UP)) { return false; } if (!netdev->netdev_class->get_carrier) { return true; } error = netdev->netdev_class->get_carrier(netdev, &carrier); if (error) { VLOG_DBG("%s: failed to get network device carrier status, assuming " "down: %s", netdev_get_name(netdev), ovs_strerror(error)); carrier = false; } return carrier; } /* Returns the number of times 'netdev''s carrier has changed. */ long long int netdev_get_carrier_resets(const struct netdev *netdev) { return (netdev->netdev_class->get_carrier_resets ? netdev->netdev_class->get_carrier_resets(netdev) : 0); } /* Attempts to force netdev_get_carrier() to poll 'netdev''s MII registers for * link status instead of checking 'netdev''s carrier. 'netdev''s MII * registers will be polled once ever 'interval' milliseconds. If 'netdev' * does not support MII, another method may be used as a fallback. If * 'interval' is less than or equal to zero, reverts netdev_get_carrier() to * its normal behavior. * * Returns 0 if successful, otherwise a positive errno value. */ int netdev_set_miimon_interval(struct netdev *netdev, long long int interval) { return (netdev->netdev_class->set_miimon_interval ? netdev->netdev_class->set_miimon_interval(netdev, interval) : EOPNOTSUPP); } /* Retrieves current device stats for 'netdev'. */ int netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) { int error; COVERAGE_INC(netdev_get_stats); error = (netdev->netdev_class->get_stats ? netdev->netdev_class->get_stats(netdev, stats) : EOPNOTSUPP); if (error) { memset(stats, 0xff, sizeof *stats); } return error; } /* Attempts to set input rate limiting (policing) policy, such that up to * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative burst * size of 'kbits' kb. */ int netdev_set_policing(struct netdev *netdev, uint32_t kbits_rate, uint32_t kbits_burst) { return (netdev->netdev_class->set_policing ? netdev->netdev_class->set_policing(netdev, kbits_rate, kbits_burst) : EOPNOTSUPP); } /* Adds to 'types' all of the forms of QoS supported by 'netdev', or leaves it * empty if 'netdev' does not support QoS. Any names added to 'types' should * be documented as valid for the "type" column in the "QoS" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * Every network device supports disabling QoS with a type of "", but this type * will not be added to 'types'. * * The caller must initialize 'types' (e.g. with sset_init()) before calling * this function. The caller is responsible for destroying 'types' (e.g. with * sset_destroy()) when it is no longer needed. * * Returns 0 if successful, otherwise a positive errno value. */ int netdev_get_qos_types(const struct netdev *netdev, struct sset *types) { const struct netdev_class *class = netdev->netdev_class; return (class->get_qos_types ? class->get_qos_types(netdev, types) : 0); } /* Queries 'netdev' for its capabilities regarding the specified 'type' of QoS, * which should be "" or one of the types returned by netdev_get_qos_types() * for 'netdev'. Returns 0 if successful, otherwise a positive errno value. * On success, initializes 'caps' with the QoS capabilities; on failure, clears * 'caps' to all zeros. */ int netdev_get_qos_capabilities(const struct netdev *netdev, const char *type, struct netdev_qos_capabilities *caps) { const struct netdev_class *class = netdev->netdev_class; if (*type) { int retval = (class->get_qos_capabilities ? class->get_qos_capabilities(netdev, type, caps) : EOPNOTSUPP); if (retval) { memset(caps, 0, sizeof *caps); } return retval; } else { /* Every netdev supports turning off QoS. */ memset(caps, 0, sizeof *caps); return 0; } } /* Obtains the number of queues supported by 'netdev' for the specified 'type' * of QoS. Returns 0 if successful, otherwise a positive errno value. Stores * the number of queues (zero on failure) in '*n_queuesp'. * * This is just a simple wrapper around netdev_get_qos_capabilities(). */ int netdev_get_n_queues(const struct netdev *netdev, const char *type, unsigned int *n_queuesp) { struct netdev_qos_capabilities caps; int retval; retval = netdev_get_qos_capabilities(netdev, type, &caps); *n_queuesp = caps.n_queues; return retval; } /* Queries 'netdev' about its currently configured form of QoS. If successful, * stores the name of the current form of QoS into '*typep', stores any details * of configuration as string key-value pairs in 'details', and returns 0. On * failure, sets '*typep' to NULL and returns a positive errno value. * * A '*typep' of "" indicates that QoS is currently disabled on 'netdev'. * * The caller must initialize 'details' as an empty smap (e.g. with * smap_init()) before calling this function. The caller must free 'details' * when it is no longer needed (e.g. with smap_destroy()). * * The caller must not modify or free '*typep'. * * '*typep' will be one of the types returned by netdev_get_qos_types() for * 'netdev'. The contents of 'details' should be documented as valid for * '*typep' in the "other_config" column in the "QoS" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). */ int netdev_get_qos(const struct netdev *netdev, const char **typep, struct smap *details) { const struct netdev_class *class = netdev->netdev_class; int retval; if (class->get_qos) { retval = class->get_qos(netdev, typep, details); if (retval) { *typep = NULL; smap_clear(details); } return retval; } else { /* 'netdev' doesn't support QoS, so report that QoS is disabled. */ *typep = ""; return 0; } } /* Attempts to reconfigure QoS on 'netdev', changing the form of QoS to 'type' * with details of configuration from 'details'. Returns 0 if successful, * otherwise a positive errno value. On error, the previous QoS configuration * is retained. * * When this function changes the type of QoS (not just 'details'), this also * resets all queue configuration for 'netdev' to their defaults (which depend * on the specific type of QoS). Otherwise, the queue configuration for * 'netdev' is unchanged. * * 'type' should be "" (to disable QoS) or one of the types returned by * netdev_get_qos_types() for 'netdev'. The contents of 'details' should be * documented as valid for the given 'type' in the "other_config" column in the * "QoS" table in vswitchd/vswitch.xml (which is built as * ovs-vswitchd.conf.db(8)). * * NULL may be specified for 'details' if there are no configuration * details. */ int netdev_set_qos(struct netdev *netdev, const char *type, const struct smap *details) { const struct netdev_class *class = netdev->netdev_class; if (!type) { type = ""; } if (class->set_qos) { if (!details) { static const struct smap empty = SMAP_INITIALIZER(&empty); details = ∅ } return class->set_qos(netdev, type, details); } else { return *type ? EOPNOTSUPP : 0; } } /* Queries 'netdev' for information about the queue numbered 'queue_id'. If * successful, adds that information as string key-value pairs to 'details'. * Returns 0 if successful, otherwise a positive errno value. * * 'queue_id' must be less than the number of queues supported by 'netdev' for * the current form of QoS (e.g. as returned by netdev_get_n_queues(netdev)). * * The returned contents of 'details' should be documented as valid for the * given 'type' in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * The caller must initialize 'details' (e.g. with smap_init()) before calling * this function. The caller must free 'details' when it is no longer needed * (e.g. with smap_destroy()). */ int netdev_get_queue(const struct netdev *netdev, unsigned int queue_id, struct smap *details) { const struct netdev_class *class = netdev->netdev_class; int retval; retval = (class->get_queue ? class->get_queue(netdev, queue_id, details) : EOPNOTSUPP); if (retval) { smap_clear(details); } return retval; } /* Configures the queue numbered 'queue_id' on 'netdev' with the key-value * string pairs in 'details'. The contents of 'details' should be documented * as valid for the given 'type' in the "other_config" column in the "Queue" * table in vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * Returns 0 if successful, otherwise a positive errno value. On failure, the * given queue's configuration should be unmodified. * * 'queue_id' must be less than the number of queues supported by 'netdev' for * the current form of QoS (e.g. as returned by netdev_get_n_queues(netdev)). * * This function does not modify 'details', and the caller retains ownership of * it. */ int netdev_set_queue(struct netdev *netdev, unsigned int queue_id, const struct smap *details) { const struct netdev_class *class = netdev->netdev_class; return (class->set_queue ? class->set_queue(netdev, queue_id, details) : EOPNOTSUPP); } /* Attempts to delete the queue numbered 'queue_id' from 'netdev'. Some kinds * of QoS may have a fixed set of queues, in which case attempts to delete them * will fail with EOPNOTSUPP. * * Returns 0 if successful, otherwise a positive errno value. On failure, the * given queue will be unmodified. * * 'queue_id' must be less than the number of queues supported by 'netdev' for * the current form of QoS (e.g. as returned by * netdev_get_n_queues(netdev)). */ int netdev_delete_queue(struct netdev *netdev, unsigned int queue_id) { const struct netdev_class *class = netdev->netdev_class; return (class->delete_queue ? class->delete_queue(netdev, queue_id) : EOPNOTSUPP); } /* Obtains statistics about 'queue_id' on 'netdev'. On success, returns 0 and * fills 'stats' with the queue's statistics; individual members of 'stats' may * be set to all-1-bits if the statistic is unavailable. On failure, returns a * positive errno value and fills 'stats' with values indicating unsupported * statistics. */ int netdev_get_queue_stats(const struct netdev *netdev, unsigned int queue_id, struct netdev_queue_stats *stats) { const struct netdev_class *class = netdev->netdev_class; int retval; retval = (class->get_queue_stats ? class->get_queue_stats(netdev, queue_id, stats) : EOPNOTSUPP); if (retval) { stats->tx_bytes = UINT64_MAX; stats->tx_packets = UINT64_MAX; stats->tx_errors = UINT64_MAX; stats->created = LLONG_MIN; } return retval; } /* Initializes 'dump' to begin dumping the queues in a netdev. * * This function provides no status indication. An error status for the entire * dump operation is provided when it is completed by calling * netdev_queue_dump_done(). */ void netdev_queue_dump_start(struct netdev_queue_dump *dump, const struct netdev *netdev) { dump->netdev = netdev_ref(netdev); if (netdev->netdev_class->queue_dump_start) { dump->error = netdev->netdev_class->queue_dump_start(netdev, &dump->state); } else { dump->error = EOPNOTSUPP; } } /* Attempts to retrieve another queue from 'dump', which must have been * initialized with netdev_queue_dump_start(). On success, stores a new queue * ID into '*queue_id', fills 'details' with configuration details for the * queue, and returns true. On failure, returns false. * * Queues are not necessarily dumped in increasing order of queue ID (or any * other predictable order). * * Failure might indicate an actual error or merely that the last queue has * been dumped. An error status for the entire dump operation is provided when * it is completed by calling netdev_queue_dump_done(). * * The returned contents of 'details' should be documented as valid for the * given 'type' in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * The caller must initialize 'details' (e.g. with smap_init()) before calling * this function. This function will clear and replace its contents. The * caller must free 'details' when it is no longer needed (e.g. with * smap_destroy()). */ bool netdev_queue_dump_next(struct netdev_queue_dump *dump, unsigned int *queue_id, struct smap *details) { smap_clear(details); const struct netdev *netdev = dump->netdev; if (dump->error) { return false; } dump->error = netdev->netdev_class->queue_dump_next(netdev, dump->state, queue_id, details); if (dump->error) { netdev->netdev_class->queue_dump_done(netdev, dump->state); return false; } return true; } /* Completes queue table dump operation 'dump', which must have been * initialized with netdev_queue_dump_start(). Returns 0 if the dump operation * was error-free, otherwise a positive errno value describing the problem. */ int netdev_queue_dump_done(struct netdev_queue_dump *dump) { const struct netdev *netdev = dump->netdev; if (!dump->error && netdev->netdev_class->queue_dump_done) { dump->error = netdev->netdev_class->queue_dump_done(netdev, dump->state); } netdev_close(dump->netdev); return dump->error == EOF ? 0 : dump->error; } /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's ID, * its statistics, and the 'aux' specified by the caller. The order of * iteration is unspecified, but (when successful) each queue is visited * exactly once. * * Calling this function may be more efficient than calling * netdev_get_queue_stats() for every queue. * * 'cb' must not modify or free the statistics passed in. * * Returns 0 if successful, otherwise a positive errno value. On error, some * configured queues may not have been included in the iteration. */ int netdev_dump_queue_stats(const struct netdev *netdev, netdev_dump_queue_stats_cb *cb, void *aux) { const struct netdev_class *class = netdev->netdev_class; return (class->dump_queue_stats ? class->dump_queue_stats(netdev, cb, aux) : EOPNOTSUPP); } /* Returns the class type of 'netdev'. * * The caller must not free the returned value. */ const char * netdev_get_type(const struct netdev *netdev) { return netdev->netdev_class->type; } /* Returns the class associated with 'netdev'. */ const struct netdev_class * netdev_get_class(const struct netdev *netdev) { return netdev->netdev_class; } /* Returns the netdev with 'name' or NULL if there is none. * * The caller must free the returned netdev with netdev_close(). */ struct netdev * netdev_from_name(const char *name) OVS_EXCLUDED(netdev_mutex) { struct netdev *netdev; ovs_mutex_lock(&netdev_mutex); netdev = shash_find_data(&netdev_shash, name); if (netdev) { netdev->ref_cnt++; } ovs_mutex_unlock(&netdev_mutex); return netdev; } /* Fills 'device_list' with devices that match 'netdev_class'. * * The caller is responsible for initializing and destroying 'device_list' and * must close each device on the list. */ void netdev_get_devices(const struct netdev_class *netdev_class, struct shash *device_list) OVS_EXCLUDED(netdev_mutex) { struct shash_node *node; ovs_mutex_lock(&netdev_mutex); SHASH_FOR_EACH (node, &netdev_shash) { struct netdev *dev = node->data; if (dev->netdev_class == netdev_class) { dev->ref_cnt++; shash_add(device_list, node->name, node->data); } } ovs_mutex_unlock(&netdev_mutex); } /* Extracts pointers to all 'netdev-vports' into an array 'vports' * and returns it. Stores the size of the array into '*size'. * * The caller is responsible for freeing 'vports' and must close * each 'netdev-vport' in the list. */ struct netdev ** netdev_get_vports(size_t *size) OVS_EXCLUDED(netdev_mutex) { struct netdev **vports; struct shash_node *node; size_t n = 0; if (!size) { return NULL; } /* Explicitly allocates big enough chunk of memory. */ vports = xmalloc(shash_count(&netdev_shash) * sizeof *vports); ovs_mutex_lock(&netdev_mutex); SHASH_FOR_EACH (node, &netdev_shash) { struct netdev *dev = node->data; if (netdev_vport_is_vport_class(dev->netdev_class)) { dev->ref_cnt++; vports[n] = dev; n++; } } ovs_mutex_unlock(&netdev_mutex); *size = n; return vports; } const char * netdev_get_type_from_name(const char *name) { struct netdev *dev = netdev_from_name(name); const char *type = dev ? netdev_get_type(dev) : NULL; netdev_close(dev); return type; } struct netdev * netdev_rxq_get_netdev(const struct netdev_rxq *rx) { ovs_assert(rx->netdev->ref_cnt > 0); return rx->netdev; } const char * netdev_rxq_get_name(const struct netdev_rxq *rx) { return netdev_get_name(netdev_rxq_get_netdev(rx)); } int netdev_rxq_get_queue_id(const struct netdev_rxq *rx) { return rx->queue_id; } static void restore_all_flags(void *aux OVS_UNUSED) { struct shash_node *node; SHASH_FOR_EACH (node, &netdev_shash) { struct netdev *netdev = node->data; const struct netdev_saved_flags *sf; enum netdev_flags saved_values; enum netdev_flags saved_flags; saved_values = saved_flags = 0; LIST_FOR_EACH (sf, node, &netdev->saved_flags_list) { saved_flags |= sf->saved_flags; saved_values &= ~sf->saved_flags; saved_values |= sf->saved_flags & sf->saved_values; } if (saved_flags) { enum netdev_flags old_flags; netdev->netdev_class->update_flags(netdev, saved_flags & saved_values, saved_flags & ~saved_values, &old_flags); } } } uint64_t netdev_get_change_seq(const struct netdev *netdev) { return netdev->change_seq; } openvswitch-2.5.9/lib/PaxHeaders.82075/stream-ssl.c0000644000000000000000000000013213534540071016650 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801425.005855078 openvswitch-2.5.9/lib/stream-ssl.c0000644000175000017500000012376113534540071020350 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream-ssl.h" #include "dhparams.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "entropy.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "shash.h" #include "socket-util.h" #include "util.h" #include "stream-provider.h" #include "stream.h" #include "timeval.h" #include "openvswitch/vlog.h" #ifdef _WIN32 /* Ref: https://www.openssl.org/support/faq.html#PROG2 * Your application must link against the same version of the Win32 C-Runtime * against which your openssl libraries were linked. The default version for * OpenSSL is /MD - "Multithreaded DLL". If we compile Open vSwitch with * something other than /MD, instead of re-compiling OpenSSL * toolkit, openssl/applink.c can be #included. Also, it is important * to add CRYPTO_malloc_init prior first call to OpenSSL. * * XXX: The behavior of the following #include when Open vSwitch is * compiled with /MD is not tested. */ #include #define SHUT_RDWR SD_BOTH #endif VLOG_DEFINE_THIS_MODULE(stream_ssl); /* Active SSL. */ enum ssl_state { STATE_TCP_CONNECTING, STATE_SSL_CONNECTING }; enum session_type { CLIENT, SERVER }; struct ssl_stream { struct stream stream; enum ssl_state state; enum session_type type; int fd; SSL *ssl; struct ofpbuf *txbuf; unsigned int session_nr; /* rx_want and tx_want record the result of the last call to SSL_read() * and SSL_write(), respectively: * * - If the call reported that data needed to be read from the file * descriptor, the corresponding member is set to SSL_READING. * * - If the call reported that data needed to be written to the file * descriptor, the corresponding member is set to SSL_WRITING. * * - Otherwise, the member is set to SSL_NOTHING, indicating that the * call completed successfully (or with an error) and that there is no * need to block. * * These are needed because there is no way to ask OpenSSL what a data read * or write would require without giving it a buffer to receive into or * data to send, respectively. (Note that the SSL_want() status is * overwritten by each SSL_read() or SSL_write() call, so we can't rely on * its value.) * * A single call to SSL_read() or SSL_write() can perform both reading * and writing and thus invalidate not one of these values but actually * both. Consider this situation, for example: * * - SSL_write() blocks on a read, so tx_want gets SSL_READING. * * - SSL_read() laters succeeds reading from 'fd' and clears out the * whole receive buffer, so rx_want gets SSL_READING. * * - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND) * and blocks. * * - Now we're stuck blocking until the peer sends us data, even though * SSL_write() could now succeed, which could easily be a deadlock * condition. * * On the other hand, we can't reset both tx_want and rx_want on every call * to SSL_read() or SSL_write(), because that would produce livelock, * e.g. in this situation: * * - SSL_write() blocks, so tx_want gets SSL_READING or SSL_WRITING. * * - SSL_read() blocks, so rx_want gets SSL_READING or SSL_WRITING, * but tx_want gets reset to SSL_NOTHING. * * - Client calls stream_wait(STREAM_RECV) and stream_wait(STREAM_SEND) * and blocks. * * - Client wakes up immediately since SSL_NOTHING in tx_want indicates * that no blocking is necessary. * * The solution we adopt here is to set tx_want to SSL_NOTHING after * calling SSL_read() only if the SSL state of the connection changed, * which indicates that an SSL-level renegotiation made some progress, and * similarly for rx_want and SSL_write(). This prevents both the * deadlock and livelock situations above. */ int rx_want, tx_want; /* A few bytes of header data in case SSL negotiation fails. */ uint8_t head[2]; short int n_head; }; /* SSL context created by ssl_init(). */ static SSL_CTX *ctx; struct ssl_config_file { bool read; /* Whether the file was successfully read. */ char *file_name; /* Configured file name, if any. */ struct timespec mtime; /* File mtime as of last time we read it. */ }; /* SSL configuration files. */ static struct ssl_config_file private_key; static struct ssl_config_file certificate; static struct ssl_config_file ca_cert; /* Ordinarily, the SSL client and server verify each other's certificates using * a CA certificate. Setting this to false disables this behavior. (This is a * security risk.) */ static bool verify_peer_cert = true; /* Ordinarily, we require a CA certificate for the peer to be locally * available. We can, however, bootstrap the CA certificate from the peer at * the beginning of our first connection then use that certificate on all * subsequent connections, saving it to a file for use in future runs also. In * this case, 'bootstrap_ca_cert' is true. */ static bool bootstrap_ca_cert; /* Session number. Used in debug logging messages to uniquely identify a * session. */ static unsigned int next_session_nr; /* Who knows what can trigger various SSL errors, so let's throttle them down * quite a bit. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); static int ssl_init(void); static int do_ssl_init(void); static bool ssl_wants_io(int ssl_error); static void ssl_close(struct stream *); static void ssl_clear_txbuf(struct ssl_stream *); static void interpret_queued_ssl_error(const char *function); static int interpret_ssl_error(const char *function, int ret, int error, int *want); static DH *tmp_dh_callback(SSL *ssl, int is_export OVS_UNUSED, int keylength); static void log_ca_cert(const char *file_name, X509 *cert); static void stream_ssl_set_ca_cert_file__(const char *file_name, bool bootstrap, bool force); static void ssl_protocol_cb(int write_p, int version, int content_type, const void *, size_t, SSL *, void *sslv_); static bool update_ssl_config(struct ssl_config_file *, const char *file_name); static int sock_errno(void); static short int want_to_poll_events(int want) { switch (want) { case SSL_NOTHING: OVS_NOT_REACHED(); case SSL_READING: return POLLIN; case SSL_WRITING: return POLLOUT; default: OVS_NOT_REACHED(); } } static int new_ssl_stream(const char *name, int fd, enum session_type type, enum ssl_state state, struct stream **streamp) { struct ssl_stream *sslv; SSL *ssl = NULL; int retval; /* Check for all the needful configuration. */ retval = 0; if (!private_key.read) { VLOG_ERR("Private key must be configured to use SSL"); retval = ENOPROTOOPT; } if (!certificate.read) { VLOG_ERR("Certificate must be configured to use SSL"); retval = ENOPROTOOPT; } if (!ca_cert.read && verify_peer_cert && !bootstrap_ca_cert) { VLOG_ERR("CA certificate must be configured to use SSL"); retval = ENOPROTOOPT; } if (!retval && !SSL_CTX_check_private_key(ctx)) { VLOG_ERR("Private key does not match certificate public key: %s", ERR_error_string(ERR_get_error(), NULL)); retval = ENOPROTOOPT; } if (retval) { goto error; } /* Disable Nagle. * On windows platforms, this can only be called upon TCP connected. */ if (state == STATE_SSL_CONNECTING) { setsockopt_tcp_nodelay(fd); } /* Create and configure OpenSSL stream. */ ssl = SSL_new(ctx); if (ssl == NULL) { VLOG_ERR("SSL_new: %s", ERR_error_string(ERR_get_error(), NULL)); retval = ENOPROTOOPT; goto error; } if (SSL_set_fd(ssl, fd) == 0) { VLOG_ERR("SSL_set_fd: %s", ERR_error_string(ERR_get_error(), NULL)); retval = ENOPROTOOPT; goto error; } if (!verify_peer_cert || (bootstrap_ca_cert && type == CLIENT)) { SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } /* Create and return the ssl_stream. */ sslv = xmalloc(sizeof *sslv); stream_init(&sslv->stream, &ssl_stream_class, EAGAIN, name); sslv->state = state; sslv->type = type; sslv->fd = fd; sslv->ssl = ssl; sslv->txbuf = NULL; sslv->rx_want = sslv->tx_want = SSL_NOTHING; sslv->session_nr = next_session_nr++; sslv->n_head = 0; if (VLOG_IS_DBG_ENABLED()) { SSL_set_msg_callback(ssl, ssl_protocol_cb); SSL_set_msg_callback_arg(ssl, sslv); } *streamp = &sslv->stream; return 0; error: if (ssl) { SSL_free(ssl); } closesocket(fd); return retval; } static struct ssl_stream * ssl_stream_cast(struct stream *stream) { stream_assert_class(stream, &ssl_stream_class); return CONTAINER_OF(stream, struct ssl_stream, stream); } static int ssl_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp) { int error, fd; error = ssl_init(); if (error) { return error; } error = inet_open_active(SOCK_STREAM, suffix, OFP_PORT, NULL, &fd, dscp); if (fd >= 0) { int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING; return new_ssl_stream(name, fd, CLIENT, state, streamp); } else { VLOG_ERR("%s: connect: %s", name, ovs_strerror(error)); return error; } } static int do_ca_cert_bootstrap(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); STACK_OF(X509) *chain; X509 *cert; FILE *file; int error; int fd; chain = SSL_get_peer_cert_chain(sslv->ssl); if (!chain || !sk_X509_num(chain)) { VLOG_ERR("could not bootstrap CA cert: no certificate presented by " "peer"); return EPROTO; } cert = sk_X509_value(chain, sk_X509_num(chain) - 1); /* Check that 'cert' is self-signed. Otherwise it is not a CA * certificate and we should not attempt to use it as one. */ error = X509_check_issued(cert, cert); if (error) { VLOG_ERR("could not bootstrap CA cert: obtained certificate is " "not self-signed (%s)", X509_verify_cert_error_string(error)); if (sk_X509_num(chain) < 2) { VLOG_ERR("only one certificate was received, so probably the peer " "is not configured to send its CA certificate"); } return EPROTO; } fd = open(ca_cert.file_name, O_CREAT | O_EXCL | O_WRONLY, 0444); if (fd < 0) { if (errno == EEXIST) { VLOG_INFO_RL(&rl, "reading CA cert %s created by another process", ca_cert.file_name); stream_ssl_set_ca_cert_file__(ca_cert.file_name, true, true); return EPROTO; } else { VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s", ca_cert.file_name, ovs_strerror(errno)); return errno; } } file = fdopen(fd, "w"); if (!file) { error = errno; VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s", ovs_strerror(error)); unlink(ca_cert.file_name); return error; } if (!PEM_write_X509(file, cert)) { VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: " "%s", ca_cert.file_name, ERR_error_string(ERR_get_error(), NULL)); fclose(file); unlink(ca_cert.file_name); return EIO; } if (fclose(file)) { error = errno; VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s", ca_cert.file_name, ovs_strerror(error)); unlink(ca_cert.file_name); return error; } VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert.file_name); log_ca_cert(ca_cert.file_name, cert); bootstrap_ca_cert = false; ca_cert.read = true; /* SSL_CTX_add_client_CA makes a copy of cert's relevant data. */ SSL_CTX_add_client_CA(ctx, cert); SSL_CTX_set_cert_store(ctx, X509_STORE_new()); if (SSL_CTX_load_verify_locations(ctx, ca_cert.file_name, NULL) != 1) { VLOG_ERR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ERR_get_error(), NULL)); return EPROTO; } VLOG_INFO("killing successful connection to retry using CA cert"); return EPROTO; } static int ssl_connect(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); int retval; switch (sslv->state) { case STATE_TCP_CONNECTING: retval = check_connection_completion(sslv->fd); if (retval) { return retval; } sslv->state = STATE_SSL_CONNECTING; setsockopt_tcp_nodelay(sslv->fd); /* Fall through. */ case STATE_SSL_CONNECTING: /* Capture the first few bytes of received data so that we can guess * what kind of funny data we've been sent if SSL negotiation fails. */ if (sslv->n_head <= 0) { sslv->n_head = recv(sslv->fd, sslv->head, sizeof sslv->head, MSG_PEEK); } retval = (sslv->type == CLIENT ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl)); if (retval != 1) { int error = SSL_get_error(sslv->ssl, retval); if (retval < 0 && ssl_wants_io(error)) { return EAGAIN; } else { int unused; interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect" : "SSL_accept"), retval, error, &unused); shutdown(sslv->fd, SHUT_RDWR); stream_report_content(sslv->head, sslv->n_head, STREAM_SSL, THIS_MODULE, stream_get_name(stream)); return EPROTO; } } else if (bootstrap_ca_cert) { return do_ca_cert_bootstrap(stream); } else if (verify_peer_cert && ((SSL_get_verify_mode(sslv->ssl) & (SSL_VERIFY_NONE | SSL_VERIFY_PEER)) != SSL_VERIFY_PEER)) { /* Two or more SSL connections completed at the same time while we * were in bootstrap mode. Only one of these can finish the * bootstrap successfully. The other one(s) must be rejected * because they were not verified against the bootstrapped CA * certificate. (Alternatively we could verify them against the CA * certificate, but that's more trouble than it's worth. These * connections will succeed the next time they retry, assuming that * they have a certificate against the correct CA.) */ VLOG_INFO("rejecting SSL connection during bootstrap race window"); return EPROTO; } else { return 0; } } OVS_NOT_REACHED(); } static void ssl_close(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); ssl_clear_txbuf(sslv); /* Attempt clean shutdown of the SSL connection. This will work most of * the time, as long as the kernel send buffer has some free space and the * SSL connection isn't renegotiating, etc. That has to be good enough, * since we don't have any way to continue the close operation in the * background. */ SSL_shutdown(sslv->ssl); /* SSL_shutdown() might have signaled an error, in which case we need to * flush it out of the OpenSSL error queue or the next OpenSSL operation * will falsely signal an error. */ ERR_clear_error(); SSL_free(sslv->ssl); closesocket(sslv->fd); free(sslv); } static void interpret_queued_ssl_error(const char *function) { int queued_error = ERR_get_error(); if (queued_error != 0) { VLOG_WARN_RL(&rl, "%s: %s", function, ERR_error_string(queued_error, NULL)); } else { VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error", function); } } static int interpret_ssl_error(const char *function, int ret, int error, int *want) { *want = SSL_NOTHING; switch (error) { case SSL_ERROR_NONE: VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_NONE", function); break; case SSL_ERROR_ZERO_RETURN: VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_ZERO_RETURN", function); break; case SSL_ERROR_WANT_READ: *want = SSL_READING; return EAGAIN; case SSL_ERROR_WANT_WRITE: *want = SSL_WRITING; return EAGAIN; case SSL_ERROR_WANT_CONNECT: VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_CONNECT", function); break; case SSL_ERROR_WANT_ACCEPT: VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_ACCEPT", function); break; case SSL_ERROR_WANT_X509_LOOKUP: VLOG_ERR_RL(&rl, "%s: unexpected SSL_ERROR_WANT_X509_LOOKUP", function); break; case SSL_ERROR_SYSCALL: { int queued_error = ERR_get_error(); if (queued_error == 0) { if (ret < 0) { int status = errno; VLOG_WARN_RL(&rl, "%s: system error (%s)", function, ovs_strerror(status)); return status; } else { VLOG_WARN_RL(&rl, "%s: unexpected SSL connection close", function); return EPROTO; } } else { VLOG_WARN_RL(&rl, "%s: %s", function, ERR_error_string(queued_error, NULL)); break; } } case SSL_ERROR_SSL: interpret_queued_ssl_error(function); break; default: VLOG_ERR_RL(&rl, "%s: bad SSL error code %d", function, error); break; } return EIO; } static ssize_t ssl_recv(struct stream *stream, void *buffer, size_t n) { struct ssl_stream *sslv = ssl_stream_cast(stream); int old_state; ssize_t ret; /* Behavior of zero-byte SSL_read is poorly defined. */ ovs_assert(n > 0); old_state = SSL_get_state(sslv->ssl); ret = SSL_read(sslv->ssl, buffer, n); if (old_state != SSL_get_state(sslv->ssl)) { sslv->tx_want = SSL_NOTHING; } sslv->rx_want = SSL_NOTHING; if (ret > 0) { return ret; } else { int error = SSL_get_error(sslv->ssl, ret); if (error == SSL_ERROR_ZERO_RETURN) { return 0; } else { return -interpret_ssl_error("SSL_read", ret, error, &sslv->rx_want); } } } static void ssl_clear_txbuf(struct ssl_stream *sslv) { ofpbuf_delete(sslv->txbuf); sslv->txbuf = NULL; } static int ssl_do_tx(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); for (;;) { int old_state = SSL_get_state(sslv->ssl); int ret = SSL_write(sslv->ssl, sslv->txbuf->data, sslv->txbuf->size); if (old_state != SSL_get_state(sslv->ssl)) { sslv->rx_want = SSL_NOTHING; } sslv->tx_want = SSL_NOTHING; if (ret > 0) { ofpbuf_pull(sslv->txbuf, ret); if (sslv->txbuf->size == 0) { return 0; } } else { int ssl_error = SSL_get_error(sslv->ssl, ret); if (ssl_error == SSL_ERROR_ZERO_RETURN) { VLOG_WARN_RL(&rl, "SSL_write: connection closed"); return EPIPE; } else { return interpret_ssl_error("SSL_write", ret, ssl_error, &sslv->tx_want); } } } } static ssize_t ssl_send(struct stream *stream, const void *buffer, size_t n) { struct ssl_stream *sslv = ssl_stream_cast(stream); if (sslv->txbuf) { return -EAGAIN; } else { int error; sslv->txbuf = ofpbuf_clone_data(buffer, n); error = ssl_do_tx(stream); switch (error) { case 0: ssl_clear_txbuf(sslv); return n; case EAGAIN: return n; default: ssl_clear_txbuf(sslv); return -error; } } } static void ssl_run(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); if (sslv->txbuf && ssl_do_tx(stream) != EAGAIN) { ssl_clear_txbuf(sslv); } } static void ssl_run_wait(struct stream *stream) { struct ssl_stream *sslv = ssl_stream_cast(stream); if (sslv->tx_want != SSL_NOTHING) { poll_fd_wait(sslv->fd, want_to_poll_events(sslv->tx_want)); } } static void ssl_wait(struct stream *stream, enum stream_wait_type wait) { struct ssl_stream *sslv = ssl_stream_cast(stream); switch (wait) { case STREAM_CONNECT: if (stream_connect(stream) != EAGAIN) { poll_immediate_wake(); } else { switch (sslv->state) { case STATE_TCP_CONNECTING: poll_fd_wait(sslv->fd, POLLOUT); break; case STATE_SSL_CONNECTING: /* ssl_connect() called SSL_accept() or SSL_connect(), which * set up the status that we test here. */ poll_fd_wait(sslv->fd, want_to_poll_events(SSL_want(sslv->ssl))); break; default: OVS_NOT_REACHED(); } } break; case STREAM_RECV: if (sslv->rx_want != SSL_NOTHING) { poll_fd_wait(sslv->fd, want_to_poll_events(sslv->rx_want)); } else { poll_immediate_wake(); } break; case STREAM_SEND: if (!sslv->txbuf) { /* We have room in our tx queue. */ poll_immediate_wake(); } else { /* stream_run_wait() will do the right thing; don't bother with * redundancy. */ } break; default: OVS_NOT_REACHED(); } } const struct stream_class ssl_stream_class = { "ssl", /* name */ true, /* needs_probes */ ssl_open, /* open */ ssl_close, /* close */ ssl_connect, /* connect */ ssl_recv, /* recv */ ssl_send, /* send */ ssl_run, /* run */ ssl_run_wait, /* run_wait */ ssl_wait, /* wait */ }; /* Passive SSL. */ struct pssl_pstream { struct pstream pstream; int fd; }; const struct pstream_class pssl_pstream_class; static struct pssl_pstream * pssl_pstream_cast(struct pstream *pstream) { pstream_assert_class(pstream, &pssl_pstream_class); return CONTAINER_OF(pstream, struct pssl_pstream, pstream); } static int pssl_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp, uint8_t dscp) { char bound_name[SS_NTOP_BUFSIZE + 16]; char addrbuf[SS_NTOP_BUFSIZE]; struct sockaddr_storage ss; struct pssl_pstream *pssl; uint16_t port; int retval; int fd; retval = ssl_init(); if (retval) { return retval; } fd = inet_open_passive(SOCK_STREAM, suffix, OFP_PORT, &ss, dscp, true); if (fd < 0) { return -fd; } port = ss_get_port(&ss); snprintf(bound_name, sizeof bound_name, "pssl:%"PRIu16":%s", port, ss_format_address(&ss, addrbuf, sizeof addrbuf)); pssl = xmalloc(sizeof *pssl); pstream_init(&pssl->pstream, &pssl_pstream_class, bound_name); pstream_set_bound_port(&pssl->pstream, htons(port)); pssl->fd = fd; *pstreamp = &pssl->pstream; return 0; } static void pssl_close(struct pstream *pstream) { struct pssl_pstream *pssl = pssl_pstream_cast(pstream); closesocket(pssl->fd); free(pssl); } static int pssl_accept(struct pstream *pstream, struct stream **new_streamp) { struct pssl_pstream *pssl = pssl_pstream_cast(pstream); char name[SS_NTOP_BUFSIZE + 16]; char addrbuf[SS_NTOP_BUFSIZE]; struct sockaddr_storage ss; socklen_t ss_len = sizeof ss; int new_fd; int error; new_fd = accept(pssl->fd, (struct sockaddr *) &ss, &ss_len); if (new_fd < 0) { error = sock_errno(); #ifdef _WIN32 if (error == WSAEWOULDBLOCK) { error = EAGAIN; } #endif if (error != EAGAIN) { VLOG_DBG_RL(&rl, "accept: %s", sock_strerror(error)); } return error; } error = set_nonblocking(new_fd); if (error) { closesocket(new_fd); return error; } snprintf(name, sizeof name, "ssl:%s:%"PRIu16, ss_format_address(&ss, addrbuf, sizeof addrbuf), ss_get_port(&ss)); return new_ssl_stream(name, new_fd, SERVER, STATE_SSL_CONNECTING, new_streamp); } static void pssl_wait(struct pstream *pstream) { struct pssl_pstream *pssl = pssl_pstream_cast(pstream); poll_fd_wait(pssl->fd, POLLIN); } const struct pstream_class pssl_pstream_class = { "pssl", true, pssl_open, pssl_close, pssl_accept, pssl_wait, }; /* * Returns true if OpenSSL error is WANT_READ or WANT_WRITE, indicating that * OpenSSL is requesting that we call it back when the socket is ready for read * or writing, respectively. */ static bool ssl_wants_io(int ssl_error) { return (ssl_error == SSL_ERROR_WANT_WRITE || ssl_error == SSL_ERROR_WANT_READ); } static int ssl_init(void) { static int init_status = -1; if (init_status < 0) { init_status = do_ssl_init(); ovs_assert(init_status >= 0); } return init_status; } static int do_ssl_init(void) { SSL_METHOD *method; #ifdef _WIN32 /* The following call is needed if we "#include ". */ CRYPTO_malloc_init(); #endif SSL_library_init(); SSL_load_error_strings(); if (!RAND_status()) { /* We occasionally see OpenSSL fail to seed its random number generator * in heavily loaded hypervisors. I suspect the following scenario: * * 1. OpenSSL calls read() to get 32 bytes from /dev/urandom. * 2. The kernel generates 10 bytes of randomness and copies it out. * 3. A signal arrives (perhaps SIGALRM). * 4. The kernel interrupts the system call to service the signal. * 5. Userspace gets 10 bytes of entropy. * 6. OpenSSL doesn't read again to get the final 22 bytes. Therefore * OpenSSL doesn't have enough entropy to consider itself * initialized. * * The only part I'm not entirely sure about is #6, because the OpenSSL * code is so hard to read. */ uint8_t seed[32]; int retval; VLOG_WARN("OpenSSL random seeding failed, reseeding ourselves"); retval = get_entropy(seed, sizeof seed); if (retval) { VLOG_ERR("failed to obtain entropy (%s)", ovs_retval_to_string(retval)); return retval > 0 ? retval : ENOPROTOOPT; } RAND_seed(seed, sizeof seed); } /* OpenSSL has a bunch of "connection methods": SSLv2_method(), * SSLv3_method(), TLSv1_method(), SSLv23_method(), ... Most of these * support exactly one version of SSL, e.g. TLSv1_method() supports TLSv1 * only, not any earlier *or later* version. The only exception is * SSLv23_method(), which in fact supports *any* version of SSL and TLS. * We don't want SSLv2 or SSLv3 support, so we turn it off below with * SSL_CTX_set_options(). * * The cast is needed to avoid a warning with newer versions of OpenSSL in * which SSLv23_method() returns a "const" pointer. */ method = CONST_CAST(SSL_METHOD *, SSLv23_method()); if (method == NULL) { VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL)); return ENOPROTOOPT; } ctx = SSL_CTX_new(method); if (ctx == NULL) { VLOG_ERR("SSL_CTX_new: %s", ERR_error_string(ERR_get_error(), NULL)); return ENOPROTOOPT; } SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_callback); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); return 0; } static DH * tmp_dh_callback(SSL *ssl OVS_UNUSED, int is_export OVS_UNUSED, int keylength) { struct dh { int keylength; DH *dh; DH *(*constructor)(void); }; static struct dh dh_table[] = { {1024, NULL, get_dh1024}, {2048, NULL, get_dh2048}, {4096, NULL, get_dh4096}, }; struct dh *dh; for (dh = dh_table; dh < &dh_table[ARRAY_SIZE(dh_table)]; dh++) { if (dh->keylength == keylength) { if (!dh->dh) { dh->dh = dh->constructor(); if (!dh->dh) { out_of_memory(); } } return dh->dh; } } VLOG_ERR_RL(&rl, "no Diffie-Hellman parameters for key length %d", keylength); return NULL; } /* Returns true if SSL is at least partially configured. */ bool stream_ssl_is_configured(void) { return private_key.file_name || certificate.file_name || ca_cert.file_name; } static bool update_ssl_config(struct ssl_config_file *config, const char *file_name) { struct timespec mtime; int error; if (ssl_init() || !file_name) { return false; } /* If the file name hasn't changed and neither has the file contents, stop * here. */ error = get_mtime(file_name, &mtime); if (error && error != ENOENT) { VLOG_ERR_RL(&rl, "%s: stat failed (%s)", file_name, ovs_strerror(error)); } if (config->file_name && !strcmp(config->file_name, file_name) && mtime.tv_sec == config->mtime.tv_sec && mtime.tv_nsec == config->mtime.tv_nsec) { return false; } /* Update 'config'. */ config->mtime = mtime; if (file_name != config->file_name) { free(config->file_name); config->file_name = xstrdup(file_name); } return true; } static void stream_ssl_set_private_key_file__(const char *file_name) { if (SSL_CTX_use_PrivateKey_file(ctx, file_name, SSL_FILETYPE_PEM) == 1) { private_key.read = true; } else { VLOG_ERR("SSL_use_PrivateKey_file: %s", ERR_error_string(ERR_get_error(), NULL)); } } void stream_ssl_set_private_key_file(const char *file_name) { if (update_ssl_config(&private_key, file_name)) { stream_ssl_set_private_key_file__(file_name); } } static void stream_ssl_set_certificate_file__(const char *file_name) { if (SSL_CTX_use_certificate_file(ctx, file_name, SSL_FILETYPE_PEM) == 1) { certificate.read = true; } else { VLOG_ERR("SSL_use_certificate_file: %s", ERR_error_string(ERR_get_error(), NULL)); } } void stream_ssl_set_certificate_file(const char *file_name) { if (update_ssl_config(&certificate, file_name)) { stream_ssl_set_certificate_file__(file_name); } } /* Sets the private key and certificate files in one operation. Use this * interface, instead of calling stream_ssl_set_private_key_file() and * stream_ssl_set_certificate_file() individually, in the main loop of a * long-running program whose key and certificate might change at runtime. * * This is important because of OpenSSL's behavior. If an OpenSSL context * already has a certificate, and stream_ssl_set_private_key_file() is called * to install a new private key, OpenSSL will report an error because the new * private key does not match the old certificate. The other order, of setting * a new certificate, then setting a new private key, does work. * * If this were the only problem, calling stream_ssl_set_certificate_file() * before stream_ssl_set_private_key_file() would fix it. But, if the private * key is changed before the certificate (e.g. someone "scp"s or "mv"s the new * private key in place before the certificate), then OpenSSL would reject that * change, and then the change of certificate would succeed, but there would be * no associated private key (because it had only changed once and therefore * there was no point in re-reading it). * * This function avoids both problems by, whenever either the certificate or * the private key file changes, re-reading both of them, in the correct order. */ void stream_ssl_set_key_and_cert(const char *private_key_file, const char *certificate_file) { if (update_ssl_config(&private_key, private_key_file) && update_ssl_config(&certificate, certificate_file)) { stream_ssl_set_certificate_file__(certificate_file); stream_ssl_set_private_key_file__(private_key_file); } } /* Reads the X509 certificate or certificates in file 'file_name'. On success, * stores the address of the first element in an array of pointers to * certificates in '*certs' and the number of certificates in the array in * '*n_certs', and returns 0. On failure, stores a null pointer in '*certs', 0 * in '*n_certs', and returns a positive errno value. * * The caller is responsible for freeing '*certs'. */ static int read_cert_file(const char *file_name, X509 ***certs, size_t *n_certs) { FILE *file; size_t allocated_certs = 0; *certs = NULL; *n_certs = 0; file = fopen(file_name, "r"); if (!file) { VLOG_ERR("failed to open %s for reading: %s", file_name, ovs_strerror(errno)); return errno; } for (;;) { X509 *certificate; int c; /* Read certificate from file. */ certificate = PEM_read_X509(file, NULL, NULL, NULL); if (!certificate) { size_t i; VLOG_ERR("PEM_read_X509 failed reading %s: %s", file_name, ERR_error_string(ERR_get_error(), NULL)); for (i = 0; i < *n_certs; i++) { X509_free((*certs)[i]); } free(*certs); *certs = NULL; *n_certs = 0; fclose(file); return EIO; } /* Add certificate to array. */ if (*n_certs >= allocated_certs) { *certs = x2nrealloc(*certs, &allocated_certs, sizeof **certs); } (*certs)[(*n_certs)++] = certificate; /* Are there additional certificates in the file? */ do { c = getc(file); } while (isspace(c)); if (c == EOF) { break; } ungetc(c, file); } fclose(file); return 0; } /* Sets 'file_name' as the name of a file containing one or more X509 * certificates to send to the peer. Typical use in OpenFlow is to send the CA * certificate to the peer, which enables a switch to pick up the controller's * CA certificate on its first connection. */ void stream_ssl_set_peer_ca_cert_file(const char *file_name) { X509 **certs; size_t n_certs; size_t i; if (ssl_init()) { return; } if (!read_cert_file(file_name, &certs, &n_certs)) { for (i = 0; i < n_certs; i++) { if (SSL_CTX_add_extra_chain_cert(ctx, certs[i]) != 1) { VLOG_ERR("SSL_CTX_add_extra_chain_cert: %s", ERR_error_string(ERR_get_error(), NULL)); } } free(certs); } } /* Logs fingerprint of CA certificate 'cert' obtained from 'file_name'. */ static void log_ca_cert(const char *file_name, X509 *cert) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int n_bytes; struct ds fp; char *subject; ds_init(&fp); if (!X509_digest(cert, EVP_sha1(), digest, &n_bytes)) { ds_put_cstr(&fp, ""); } else { unsigned int i; for (i = 0; i < n_bytes; i++) { if (i) { ds_put_char(&fp, ':'); } ds_put_format(&fp, "%02x", digest[i]); } } subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); VLOG_INFO("Trusting CA cert from %s (%s) (fingerprint %s)", file_name, subject ? subject : "", ds_cstr(&fp)); OPENSSL_free(subject); ds_destroy(&fp); } static void stream_ssl_set_ca_cert_file__(const char *file_name, bool bootstrap, bool force) { struct stat s; if (!update_ssl_config(&ca_cert, file_name) && !force) { return; } if (!strcmp(file_name, "none")) { verify_peer_cert = false; VLOG_WARN("Peer certificate validation disabled " "(this is a security risk)"); } else if (bootstrap && stat(file_name, &s) && errno == ENOENT) { bootstrap_ca_cert = true; } else { STACK_OF(X509_NAME) *cert_names = SSL_load_client_CA_file(file_name); if (cert_names) { /* Set up list of CAs that the server will accept from the * client. */ SSL_CTX_set_client_CA_list(ctx, cert_names); /* Set up CAs for OpenSSL to trust in verifying the peer's * certificate. */ SSL_CTX_set_cert_store(ctx, X509_STORE_new()); if (SSL_CTX_load_verify_locations(ctx, file_name, NULL) != 1) { VLOG_ERR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ERR_get_error(), NULL)); return; } bootstrap_ca_cert = false; } else { VLOG_ERR("failed to load client certificates from %s: %s", file_name, ERR_error_string(ERR_get_error(), NULL)); } } ca_cert.read = true; } /* Sets 'file_name' as the name of the file from which to read the CA * certificate used to verify the peer within SSL connections. If 'bootstrap' * is false, the file must exist. If 'bootstrap' is false, then the file is * read if it is exists; if it does not, then it will be created from the CA * certificate received from the peer on the first SSL connection. */ void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap) { stream_ssl_set_ca_cert_file__(file_name, bootstrap, false); } /* SSL protocol logging. */ static const char * ssl_alert_level_to_string(uint8_t type) { switch (type) { case 1: return "warning"; case 2: return "fatal"; default: return ""; } } static const char * ssl_alert_description_to_string(uint8_t type) { switch (type) { case 0: return "close_notify"; case 10: return "unexpected_message"; case 20: return "bad_record_mac"; case 21: return "decryption_failed"; case 22: return "record_overflow"; case 30: return "decompression_failure"; case 40: return "handshake_failure"; case 42: return "bad_certificate"; case 43: return "unsupported_certificate"; case 44: return "certificate_revoked"; case 45: return "certificate_expired"; case 46: return "certificate_unknown"; case 47: return "illegal_parameter"; case 48: return "unknown_ca"; case 49: return "access_denied"; case 50: return "decode_error"; case 51: return "decrypt_error"; case 60: return "export_restriction"; case 70: return "protocol_version"; case 71: return "insufficient_security"; case 80: return "internal_error"; case 90: return "user_canceled"; case 100: return "no_renegotiation"; default: return ""; } } static const char * ssl_handshake_type_to_string(uint8_t type) { switch (type) { case 0: return "hello_request"; case 1: return "client_hello"; case 2: return "server_hello"; case 11: return "certificate"; case 12: return "server_key_exchange"; case 13: return "certificate_request"; case 14: return "server_hello_done"; case 15: return "certificate_verify"; case 16: return "client_key_exchange"; case 20: return "finished"; default: return ""; } } static void ssl_protocol_cb(int write_p, int version OVS_UNUSED, int content_type, const void *buf_, size_t len, SSL *ssl OVS_UNUSED, void *sslv_) { const struct ssl_stream *sslv = sslv_; const uint8_t *buf = buf_; struct ds details; if (!VLOG_IS_DBG_ENABLED()) { return; } ds_init(&details); if (content_type == 20) { ds_put_cstr(&details, "change_cipher_spec"); } else if (content_type == 21) { ds_put_format(&details, "alert: %s, %s", ssl_alert_level_to_string(buf[0]), ssl_alert_description_to_string(buf[1])); } else if (content_type == 22) { ds_put_format(&details, "handshake: %s", ssl_handshake_type_to_string(buf[0])); } else { ds_put_format(&details, "type %d", content_type); } VLOG_DBG("%s%u%s%s %s (%"PRIuSIZE" bytes)", sslv->type == CLIENT ? "client" : "server", sslv->session_nr, write_p ? "-->" : "<--", stream_get_name(&sslv->stream), ds_cstr(&details), len); ds_destroy(&details); } openvswitch-2.5.9/lib/PaxHeaders.82075/dynamic-string.c0000644000000000000000000000013213534540071017506 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.713852925 openvswitch-2.5.9/lib/dynamic-string.c0000644000175000017500000002654013534540071021203 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dynamic-string.h" #include #include #include #include #include "timeval.h" #include "util.h" /* Initializes 'ds' as an empty string buffer. */ void ds_init(struct ds *ds) { ds->string = NULL; ds->length = 0; ds->allocated = 0; } /* Sets 'ds''s length to 0, effectively clearing any existing content. Does * not free any memory. */ void ds_clear(struct ds *ds) { ds->length = 0; } /* Reduces 'ds''s length to no more than 'new_length'. (If its length is * already 'new_length' or less, does nothing.) */ void ds_truncate(struct ds *ds, size_t new_length) { if (ds->length > new_length) { ds->length = new_length; ds->string[new_length] = '\0'; } } /* Ensures that at least 'min_length + 1' bytes (including space for a null * terminator) are allocated for ds->string, allocating or reallocating memory * as necessary. */ void ds_reserve(struct ds *ds, size_t min_length) { if (min_length > ds->allocated || !ds->string) { ds->allocated += MAX(min_length, ds->allocated); ds->allocated = MAX(8, ds->allocated); ds->string = xrealloc(ds->string, ds->allocated + 1); } } /* Appends space for 'n' bytes to the end of 'ds->string', increasing * 'ds->length' by the same amount, and returns the first appended byte. The * caller should fill in all 'n' bytes starting at the return value. */ char * ds_put_uninit(struct ds *ds, size_t n) { ds_reserve(ds, ds->length + n); ds->length += n; ds->string[ds->length] = '\0'; return &ds->string[ds->length - n]; } void ds_put_char__(struct ds *ds, char c) { *ds_put_uninit(ds, 1) = c; } /* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */ void ds_put_utf8(struct ds *ds, int uc) { if (uc <= 0x7f) { ds_put_char(ds, uc); } else if (uc <= 0x7ff) { ds_put_char(ds, 0xc0 | (uc >> 6)); ds_put_char(ds, 0x80 | (uc & 0x3f)); } else if (uc <= 0xffff) { ds_put_char(ds, 0xe0 | (uc >> 12)); ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f)); ds_put_char(ds, 0x80 | (uc & 0x3f)); } else if (uc <= 0x10ffff) { ds_put_char(ds, 0xf0 | (uc >> 18)); ds_put_char(ds, 0x80 | ((uc >> 12) & 0x3f)); ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f)); ds_put_char(ds, 0x80 | (uc & 0x3f)); } else { /* Invalid code point. Insert the Unicode general substitute * REPLACEMENT CHARACTER. */ ds_put_utf8(ds, 0xfffd); } } void ds_put_char_multiple(struct ds *ds, char c, size_t n) { memset(ds_put_uninit(ds, n), c, n); } void ds_put_buffer(struct ds *ds, const char *s, size_t n) { memcpy(ds_put_uninit(ds, n), s, n); } void ds_put_cstr(struct ds *ds, const char *s) { size_t s_len = strlen(s); memcpy(ds_put_uninit(ds, s_len), s, s_len); } void ds_put_and_free_cstr(struct ds *ds, char *s) { ds_put_cstr(ds, s); free(s); } void ds_put_format(struct ds *ds, const char *format, ...) { va_list args; va_start(args, format); ds_put_format_valist(ds, format, args); va_end(args); } void ds_put_format_valist(struct ds *ds, const char *format, va_list args_) { va_list args; size_t available; int needed; va_copy(args, args_); available = ds->string ? ds->allocated - ds->length + 1 : 0; needed = vsnprintf(&ds->string[ds->length], available, format, args); va_end(args); if (needed < available) { ds->length += needed; } else { ds_reserve(ds, ds->length + needed); va_copy(args, args_); available = ds->allocated - ds->length + 1; needed = vsnprintf(&ds->string[ds->length], available, format, args); va_end(args); ovs_assert(needed < available); ds->length += needed; } } void ds_put_printable(struct ds *ds, const char *s, size_t n) { ds_reserve(ds, ds->length + n); while (n-- > 0) { unsigned char c = *s++; if (c < 0x20 || c > 0x7e || c == '\\' || c == '"') { ds_put_format(ds, "\\%03o", (int) c); } else { ds_put_char(ds, c); } } } /* Writes the current time with optional millisecond resolution to 'string' * based on 'template'. * The current time is either localtime or UTC based on 'utc'. */ void ds_put_strftime_msec(struct ds *ds, const char *template, long long int when, bool utc) { struct tm_msec tm; if (utc) { gmtime_msec(when, &tm); } else { localtime_msec(when, &tm); } for (;;) { size_t avail = ds->string ? ds->allocated - ds->length + 1 : 0; size_t used = strftime_msec(&ds->string[ds->length], avail, template, &tm); if (used) { ds->length += used; return; } ds_reserve(ds, ds->length + (avail < 32 ? 64 : 2 * avail)); } } /* Returns a malloc()'d string for time 'when' based on 'template', in local * time or UTC based on 'utc'. */ char * xastrftime_msec(const char *template, long long int when, bool utc) { struct ds s; ds_init(&s); ds_put_strftime_msec(&s, template, when, utc); return s.string; } int ds_get_line(struct ds *ds, FILE *file) { ds_clear(ds); for (;;) { int c = getc(file); if (c == EOF) { return ds->length ? 0 : EOF; } else if (c == '\n') { return 0; } else { ds_put_char(ds, c); } } } /* Reads a line from 'file' into 'ds', clearing anything initially in 'ds'. * Deletes comments introduced by "#" and skips lines that contains only white * space (after deleting comments). * * If 'line_numberp' is nonnull, increments '*line_numberp' by the number of * lines read from 'file'. * * Returns 0 if successful, EOF if no non-blank line was found. */ int ds_get_preprocessed_line(struct ds *ds, FILE *file, int *line_numberp) { while (!ds_get_line(ds, file)) { char *line = ds_cstr(ds); char *comment; if (line_numberp) { ++*line_numberp; } /* Delete comments. */ comment = strchr(line, '#'); if (comment) { *comment = '\0'; } /* Return successfully unless the line is all spaces. */ if (line[strspn(line, " \t\n")] != '\0') { return 0; } } return EOF; } /* Reads a line from 'file' into 'ds' and does some preprocessing on it: * * - If the line begins with #, prints it on stdout and reads the next line. * * - Otherwise, if the line contains an # somewhere else, strips it and * everything following it (as a comment). * * - If (after comment removal) the line contains only white space, prints * a blank line on stdout and reads the next line. * * - Otherwise, returns the line to the caller. * * This is useful in some of the OVS tests, where we want to check that parsing * and then re-formatting some kind of data does not change it, but we also * want to be able to put comments in the input. * * Returns 0 if successful, EOF if no non-blank line was found. */ int ds_get_test_line(struct ds *ds, FILE *file) { for (;;) { char *s, *comment; int retval; retval = ds_get_line(ds, file); if (retval) { return retval; } s = ds_cstr(ds); if (*s == '#') { puts(s); continue; } comment = strchr(s, '#'); if (comment) { *comment = '\0'; } if (s[strspn(s, " \t\n")] == '\0') { putchar('\n'); continue; } return 0; } } char * ds_cstr(struct ds *ds) { if (!ds->string) { ds_reserve(ds, 0); } ds->string[ds->length] = '\0'; return ds->string; } const char * ds_cstr_ro(const struct ds *ds) { return ds_cstr(CONST_CAST(struct ds *, ds)); } /* Returns a null-terminated string representing the current contents of 'ds', * which the caller is expected to free with free(), then clears the contents * of 'ds'. */ char * ds_steal_cstr(struct ds *ds) { char *s = ds_cstr(ds); ds_init(ds); return s; } void ds_destroy(struct ds *ds) { free(ds->string); } /* Swaps the content of 'a' and 'b'. */ void ds_swap(struct ds *a, struct ds *b) { struct ds temp = *a; *a = *b; *b = temp; } void ds_put_hex(struct ds *ds, const void *buf_, size_t size) { const uint8_t *buf = buf_; bool printed = false; int i; for (i = 0; i < size; i++) { uint8_t val = buf[i]; if (val || printed) { if (!printed) { ds_put_format(ds, "0x%"PRIx8, val); } else { ds_put_format(ds, "%02"PRIx8, val); } printed = true; } } if (!printed) { ds_put_char(ds, '0'); } } /* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per * line. Numeric offsets are also included, starting at 'ofs' for the first * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters * are also rendered alongside. */ void ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size, uintptr_t ofs, bool ascii) { const uint8_t *buf = buf_; const size_t per_line = 16; /* Maximum bytes per line. */ while (size > 0) { size_t start, end, n; size_t i; /* Number of bytes on this line. */ start = ofs % per_line; end = per_line; if (end - start > size) end = start + size; n = end - start; /* Print line. */ ds_put_format(ds, "%08"PRIxMAX" ", (uintmax_t) ROUND_DOWN(ofs, per_line)); for (i = 0; i < start; i++) { ds_put_format(ds, " "); } for (; i < end; i++) { ds_put_format(ds, "%02x%c", buf[i - start], i == per_line / 2 - 1? '-' : ' '); } if (ascii) { for (; i < per_line; i++) ds_put_format(ds, " "); ds_put_format(ds, "|"); for (i = 0; i < start; i++) ds_put_format(ds, " "); for (; i < end; i++) { int c = buf[i - start]; ds_put_char(ds, c >= 32 && c < 127 ? c : '.'); } for (; i < per_line; i++) ds_put_format(ds, " "); ds_put_format(ds, "|"); } else { ds_chomp(ds, ' '); } ds_put_format(ds, "\n"); ofs += n; buf += n; size -= n; } } int ds_last(const struct ds *ds) { return ds->length > 0 ? (unsigned char) ds->string[ds->length - 1] : EOF; } void ds_chomp(struct ds *ds, int c) { if (ds->length > 0 && ds->string[ds->length - 1] == (char) c) { ds->string[--ds->length] = '\0'; } } openvswitch-2.5.9/lib/PaxHeaders.82075/vconn.c0000644000000000000000000000013213534540071015701 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.941854606 openvswitch-2.5.9/lib/vconn.c0000644000175000017500000011554613534540071017403 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "vconn-provider.h" #include #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "flow.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "random.h" #include "util.h" #include "openvswitch/vlog.h" #include "socket-util.h" VLOG_DEFINE_THIS_MODULE(vconn); COVERAGE_DEFINE(vconn_open); COVERAGE_DEFINE(vconn_received); COVERAGE_DEFINE(vconn_sent); /* State of an active vconn.*/ enum vconn_state { /* This is the ordinary progression of states. */ VCS_CONNECTING, /* Underlying vconn is not connected. */ VCS_SEND_HELLO, /* Waiting to send OFPT_HELLO message. */ VCS_RECV_HELLO, /* Waiting to receive OFPT_HELLO message. */ VCS_CONNECTED, /* Connection established. */ /* These states are entered only when something goes wrong. */ VCS_SEND_ERROR, /* Sending OFPT_ERROR message. */ VCS_DISCONNECTED /* Connection failed or connection closed. */ }; static const struct vconn_class *vconn_classes[] = { &tcp_vconn_class, &unix_vconn_class, #ifdef HAVE_OPENSSL &ssl_vconn_class, #endif }; static const struct pvconn_class *pvconn_classes[] = { &ptcp_pvconn_class, &punix_pvconn_class, #ifdef HAVE_OPENSSL &pssl_pvconn_class, #endif }; /* Rate limit for individual OpenFlow messages going over the vconn, output at * DBG level. This is very high because, if these are enabled, it is because * we really need to see them. */ static struct vlog_rate_limit ofmsg_rl = VLOG_RATE_LIMIT_INIT(600, 600); /* Rate limit for OpenFlow message parse errors. These always indicate a bug * in the peer and so there's not much point in showing a lot of them. */ static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5); static int do_recv(struct vconn *, struct ofpbuf **); static int do_send(struct vconn *, struct ofpbuf *); /* Check the validity of the vconn class structures. */ static void check_vconn_classes(void) { #ifndef NDEBUG size_t i; for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) { const struct vconn_class *class = vconn_classes[i]; ovs_assert(class->name != NULL); ovs_assert(class->open != NULL); if (class->close || class->recv || class->send || class->run || class->run_wait || class->wait) { ovs_assert(class->close != NULL); ovs_assert(class->recv != NULL); ovs_assert(class->send != NULL); ovs_assert(class->wait != NULL); } else { /* This class delegates to another one. */ } } for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) { const struct pvconn_class *class = pvconn_classes[i]; ovs_assert(class->name != NULL); ovs_assert(class->listen != NULL); if (class->close || class->accept || class->wait) { ovs_assert(class->close != NULL); ovs_assert(class->accept != NULL); ovs_assert(class->wait != NULL); } else { /* This class delegates to another one. */ } } #endif } /* Prints information on active (if 'active') and passive (if 'passive') * connection methods supported by the vconn. If 'bootstrap' is true, also * advertises options to bootstrap the CA certificate. */ void vconn_usage(bool active, bool passive, bool bootstrap OVS_UNUSED) { /* Really this should be implemented via callbacks into the vconn * providers, but that seems too heavy-weight to bother with at the * moment. */ printf("\n"); if (active) { printf("Active OpenFlow connection methods:\n"); printf(" tcp:IP[:PORT] " "PORT (default: %d) at remote IP\n", OFP_PORT); #ifdef HAVE_OPENSSL printf(" ssl:IP[:PORT] " "SSL PORT (default: %d) at remote IP\n", OFP_PORT); #endif printf(" unix:FILE Unix domain socket named FILE\n"); } if (passive) { printf("Passive OpenFlow connection methods:\n"); printf(" ptcp:[PORT][:IP] " "listen to TCP PORT (default: %d) on IP\n", OFP_PORT); #ifdef HAVE_OPENSSL printf(" pssl:[PORT][:IP] " "listen for SSL on PORT (default: %d) on IP\n", OFP_PORT); #endif printf(" punix:FILE " "listen on Unix domain socket FILE\n"); } #ifdef HAVE_OPENSSL printf("PKI configuration (required to use SSL):\n" " -p, --private-key=FILE file with private key\n" " -c, --certificate=FILE file with certificate for private key\n" " -C, --ca-cert=FILE file with peer CA certificate\n"); if (bootstrap) { printf(" --bootstrap-ca-cert=FILE file with peer CA certificate " "to read or create\n"); } #endif } /* Given 'name', a connection name in the form "TYPE:ARGS", stores the class * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores * a null pointer into '*classp' if 'name' is in the wrong form or if no such * class exists. */ static int vconn_lookup_class(const char *name, const struct vconn_class **classp) { size_t prefix_len; prefix_len = strcspn(name, ":"); if (name[prefix_len] != '\0') { size_t i; for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) { const struct vconn_class *class = vconn_classes[i]; if (strlen(class->name) == prefix_len && !memcmp(class->name, name, prefix_len)) { *classp = class; return 0; } } } *classp = NULL; return EAFNOSUPPORT; } /* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is * a supported connection type, otherwise EAFNOSUPPORT. */ int vconn_verify_name(const char *name) { const struct vconn_class *class; return vconn_lookup_class(name, &class); } /* Attempts to connect to an OpenFlow device. 'name' is a connection name in * the form "TYPE:ARGS", where TYPE is an active vconn class's name and ARGS * are vconn class-specific. * * The vconn will automatically negotiate an OpenFlow protocol version * acceptable to both peers on the connection. The version negotiated will be * one of those in the 'allowed_versions' bitmap: version 'x' is allowed if * allowed_versions & (1 << x) is nonzero. If 'allowed_versions' is zero, then * OFPUTIL_DEFAULT_VERSIONS are allowed. * * Returns 0 if successful, otherwise a positive errno value. If successful, * stores a pointer to the new connection in '*vconnp', otherwise a null * pointer. */ int vconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp, struct vconn **vconnp) { const struct vconn_class *class; struct vconn *vconn; char *suffix_copy; int error; COVERAGE_INC(vconn_open); check_vconn_classes(); if (!allowed_versions) { allowed_versions = OFPUTIL_DEFAULT_VERSIONS; } /* Look up the class. */ error = vconn_lookup_class(name, &class); if (!class) { goto error; } /* Call class's "open" function. */ suffix_copy = xstrdup(strchr(name, ':') + 1); error = class->open(name, allowed_versions, suffix_copy, &vconn, dscp); free(suffix_copy); if (error) { goto error; } /* Success. */ ovs_assert(vconn->state != VCS_CONNECTING || vconn->vclass->connect); *vconnp = vconn; return 0; error: *vconnp = NULL; return error; } /* Allows 'vconn' to perform maintenance activities, such as flushing output * buffers. */ void vconn_run(struct vconn *vconn) { if (vconn->state == VCS_CONNECTING || vconn->state == VCS_SEND_HELLO || vconn->state == VCS_RECV_HELLO) { vconn_connect(vconn); } if (vconn->vclass->run) { (vconn->vclass->run)(vconn); } } /* Arranges for the poll loop to wake up when 'vconn' needs to perform * maintenance activities. */ void vconn_run_wait(struct vconn *vconn) { if (vconn->state == VCS_CONNECTING || vconn->state == VCS_SEND_HELLO || vconn->state == VCS_RECV_HELLO) { vconn_connect_wait(vconn); } if (vconn->vclass->run_wait) { (vconn->vclass->run_wait)(vconn); } } /* Returns 0 if 'vconn' is healthy (connecting or connected), a positive errno * value if the connection died abnormally (connection failed or aborted), or * EOF if the connection was closed in a normal way. */ int vconn_get_status(const struct vconn *vconn) { return vconn->error == EAGAIN ? 0 : vconn->error; } int vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp, struct vconn **vconnp) { struct vconn *vconn; int error; fatal_signal_run(); error = vconn_open(name, allowed_versions, dscp, &vconn); if (!error) { error = vconn_connect_block(vconn); } if (error) { vconn_close(vconn); *vconnp = NULL; } else { *vconnp = vconn; } return error; } /* Closes 'vconn'. */ void vconn_close(struct vconn *vconn) { if (vconn != NULL) { char *name = vconn->name; (vconn->vclass->close)(vconn); free(name); } } /* Returns the name of 'vconn', that is, the string passed to vconn_open(). */ const char * vconn_get_name(const struct vconn *vconn) { return vconn->name; } /* Returns the allowed_versions of 'vconn', that is, * the allowed_versions passed to vconn_open(). */ uint32_t vconn_get_allowed_versions(const struct vconn *vconn) { return vconn->allowed_versions; } /* Sets the allowed_versions of 'vconn', overriding * the allowed_versions passed to vconn_open(). */ void vconn_set_allowed_versions(struct vconn *vconn, uint32_t allowed_versions) { vconn->allowed_versions = allowed_versions; } /* Returns the OpenFlow version negotiated with the peer, or -1 if version * negotiation is not yet complete. * * A vconn that has successfully connected (that is, vconn_connect() or * vconn_send() or vconn_recv() has returned 0) always negotiated a version. */ int vconn_get_version(const struct vconn *vconn) { return vconn->version ? vconn->version : -1; } /* By default, a vconn accepts only OpenFlow messages whose version matches the * one negotiated for the connection. A message received with a different * version is an error that causes the vconn to drop the connection. * * This functions allows 'vconn' to accept messages with any OpenFlow version. * This is useful in the special case where 'vconn' is used as an rconn * "monitor" connection (see rconn_add_monitor()), that is, where 'vconn' is * used as a target for mirroring OpenFlow messages for debugging and * troubleshooting. * * This function should be called after a successful vconn_open() or * pvconn_accept() but before the connection completes, that is, before * vconn_connect() returns success. Otherwise, messages that arrive on 'vconn' * beforehand with an unexpected version will the vconn to drop the * connection. */ void vconn_set_recv_any_version(struct vconn *vconn) { vconn->recv_any_version = true; } static void vcs_connecting(struct vconn *vconn) { int retval = (vconn->vclass->connect)(vconn); ovs_assert(retval != EINPROGRESS); if (!retval) { vconn->state = VCS_SEND_HELLO; } else if (retval != EAGAIN) { vconn->state = VCS_DISCONNECTED; vconn->error = retval; } } static void vcs_send_hello(struct vconn *vconn) { struct ofpbuf *b; int retval; b = ofputil_encode_hello(vconn->allowed_versions); retval = do_send(vconn, b); if (!retval) { vconn->state = VCS_RECV_HELLO; } else { ofpbuf_delete(b); if (retval != EAGAIN) { vconn->state = VCS_DISCONNECTED; vconn->error = retval; } } } static char * version_bitmap_to_string(uint32_t bitmap) { struct ds s; ds_init(&s); if (!bitmap) { ds_put_cstr(&s, "no versions"); } else if (is_pow2(bitmap)) { ds_put_cstr(&s, "version "); ofputil_format_version(&s, leftmost_1bit_idx(bitmap)); } else if (is_pow2((bitmap >> 1) + 1)) { ds_put_cstr(&s, "version "); ofputil_format_version(&s, leftmost_1bit_idx(bitmap)); ds_put_cstr(&s, " and earlier"); } else { ds_put_cstr(&s, "versions "); ofputil_format_version_bitmap(&s, bitmap); } return ds_steal_cstr(&s); } static void vcs_recv_hello(struct vconn *vconn) { struct ofpbuf *b; int retval; retval = do_recv(vconn, &b); if (!retval) { enum ofptype type; enum ofperr error; error = ofptype_decode(&type, b->data); if (!error && type == OFPTYPE_HELLO) { char *peer_s, *local_s; uint32_t common_versions; if (!ofputil_decode_hello(b->data, &vconn->peer_versions)) { struct ds msg = DS_EMPTY_INITIALIZER; ds_put_format(&msg, "%s: unknown data in hello:\n", vconn->name); ds_put_hex_dump(&msg, b->data, b->size, 0, true); VLOG_WARN_RL(&bad_ofmsg_rl, "%s", ds_cstr(&msg)); ds_destroy(&msg); } local_s = version_bitmap_to_string(vconn->allowed_versions); peer_s = version_bitmap_to_string(vconn->peer_versions); common_versions = vconn->peer_versions & vconn->allowed_versions; if (!common_versions) { vconn->version = leftmost_1bit_idx(vconn->peer_versions); VLOG_WARN_RL(&bad_ofmsg_rl, "%s: version negotiation failed (we support " "%s, peer supports %s)", vconn->name, local_s, peer_s); vconn->state = VCS_SEND_ERROR; } else { vconn->version = leftmost_1bit_idx(common_versions); VLOG_DBG("%s: negotiated OpenFlow version 0x%02x " "(we support %s, peer supports %s)", vconn->name, vconn->version, local_s, peer_s); vconn->state = VCS_CONNECTED; } free(local_s); free(peer_s); ofpbuf_delete(b); return; } else { char *s = ofp_to_string(b->data, b->size, 1); VLOG_WARN_RL(&bad_ofmsg_rl, "%s: received message while expecting hello: %s", vconn->name, s); free(s); retval = EPROTO; ofpbuf_delete(b); } } if (retval != EAGAIN) { vconn->state = VCS_DISCONNECTED; vconn->error = retval == EOF ? ECONNRESET : retval; } } static void vcs_send_error(struct vconn *vconn) { struct ofpbuf *b; char s[128]; int retval; char *local_s, *peer_s; local_s = version_bitmap_to_string(vconn->allowed_versions); peer_s = version_bitmap_to_string(vconn->peer_versions); snprintf(s, sizeof s, "We support %s, you support %s, no common versions.", local_s, peer_s); free(peer_s); free(local_s); b = ofperr_encode_hello(OFPERR_OFPHFC_INCOMPATIBLE, vconn->version, s); retval = do_send(vconn, b); if (retval) { ofpbuf_delete(b); } if (retval != EAGAIN) { vconn->state = VCS_DISCONNECTED; vconn->error = retval ? retval : EPROTO; } } /* Tries to complete the connection on 'vconn'. If 'vconn''s connection is * complete, returns 0 if the connection was successful or a positive errno * value if it failed. If the connection is still in progress, returns * EAGAIN. */ int vconn_connect(struct vconn *vconn) { enum vconn_state last_state; do { last_state = vconn->state; switch (vconn->state) { case VCS_CONNECTING: vcs_connecting(vconn); break; case VCS_SEND_HELLO: vcs_send_hello(vconn); break; case VCS_RECV_HELLO: vcs_recv_hello(vconn); break; case VCS_CONNECTED: return 0; case VCS_SEND_ERROR: vcs_send_error(vconn); break; case VCS_DISCONNECTED: ovs_assert(vconn->error != 0); return vconn->error; default: OVS_NOT_REACHED(); } } while (vconn->state != last_state); return EAGAIN; } /* Tries to receive an OpenFlow message from 'vconn'. If successful, stores * the received message into '*msgp' and returns 0. The caller is responsible * for destroying the message with ofpbuf_delete(). On failure, returns a * positive errno value and stores a null pointer into '*msgp'. On normal * connection close, returns EOF. * * vconn_recv will not block waiting for a packet to arrive. If no packets * have been received, it returns EAGAIN immediately. */ int vconn_recv(struct vconn *vconn, struct ofpbuf **msgp) { struct ofpbuf *msg; int retval; retval = vconn_connect(vconn); if (!retval) { retval = do_recv(vconn, &msg); } if (!retval && !vconn->recv_any_version) { const struct ofp_header *oh = msg->data; if (oh->version != vconn->version) { enum ofptype type; if (ofptype_decode(&type, msg->data) || (type != OFPTYPE_HELLO && type != OFPTYPE_ERROR && type != OFPTYPE_ECHO_REQUEST && type != OFPTYPE_ECHO_REPLY)) { struct ofpbuf *reply; VLOG_ERR_RL(&bad_ofmsg_rl, "%s: received OpenFlow version " "0x%02"PRIx8" != expected %02x", vconn->name, oh->version, vconn->version); /* Send a "bad version" reply, if we can. */ reply = ofperr_encode_reply(OFPERR_OFPBRC_BAD_VERSION, oh); retval = vconn_send(vconn, reply); if (retval) { VLOG_INFO_RL(&bad_ofmsg_rl, "%s: failed to queue error reply (%s)", vconn->name, ovs_strerror(retval)); ofpbuf_delete(reply); } /* Suppress the received message, as if it had not arrived. */ retval = EAGAIN; ofpbuf_delete(msg); } } } *msgp = retval ? NULL : msg; return retval; } static int do_recv(struct vconn *vconn, struct ofpbuf **msgp) { int retval = (vconn->vclass->recv)(vconn, msgp); if (!retval) { COVERAGE_INC(vconn_received); if (VLOG_IS_DBG_ENABLED()) { char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1); VLOG_DBG_RL(&ofmsg_rl, "%s: received: %s", vconn->name, s); free(s); } } return retval; } /* Tries to queue 'msg' for transmission on 'vconn'. If successful, returns 0, * in which case ownership of 'msg' is transferred to the vconn. Success does * not guarantee that 'msg' has been or ever will be delivered to the peer, * only that it has been queued for transmission. * * Returns a positive errno value on failure, in which case the caller * retains ownership of 'msg'. * * vconn_send will not block. If 'msg' cannot be immediately accepted for * transmission, it returns EAGAIN immediately. */ int vconn_send(struct vconn *vconn, struct ofpbuf *msg) { int retval = vconn_connect(vconn); if (!retval) { retval = do_send(vconn, msg); } return retval; } static int do_send(struct vconn *vconn, struct ofpbuf *msg) { int retval; ovs_assert(msg->size >= sizeof(struct ofp_header)); ofpmsg_update_length(msg); if (!VLOG_IS_DBG_ENABLED()) { COVERAGE_INC(vconn_sent); retval = (vconn->vclass->send)(vconn, msg); } else { char *s = ofp_to_string(msg->data, msg->size, 1); retval = (vconn->vclass->send)(vconn, msg); if (retval != EAGAIN) { VLOG_DBG_RL(&ofmsg_rl, "%s: sent (%s): %s", vconn->name, ovs_strerror(retval), s); } free(s); } return retval; } /* Same as vconn_connect(), except that it waits until the connection on * 'vconn' completes or fails. Thus, it will never return EAGAIN. */ int vconn_connect_block(struct vconn *vconn) { int error; while ((error = vconn_connect(vconn)) == EAGAIN) { vconn_run(vconn); vconn_run_wait(vconn); vconn_connect_wait(vconn); poll_block(); } ovs_assert(error != EINPROGRESS); return error; } /* Same as vconn_send, except that it waits until 'msg' can be transmitted. */ int vconn_send_block(struct vconn *vconn, struct ofpbuf *msg) { int retval; fatal_signal_run(); while ((retval = vconn_send(vconn, msg)) == EAGAIN) { vconn_run(vconn); vconn_run_wait(vconn); vconn_send_wait(vconn); poll_block(); } return retval; } /* Same as vconn_recv, except that it waits until a message is received. */ int vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp) { int retval; fatal_signal_run(); while ((retval = vconn_recv(vconn, msgp)) == EAGAIN) { vconn_run(vconn); vconn_run_wait(vconn); vconn_recv_wait(vconn); poll_block(); } return retval; } static int vconn_recv_xid__(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp, void (*error_reporter)(const struct ofp_header *)) { for (;;) { ovs_be32 recv_xid; struct ofpbuf *reply; const struct ofp_header *oh; enum ofptype type; int error; error = vconn_recv_block(vconn, &reply); if (error) { *replyp = NULL; return error; } oh = reply->data; recv_xid = oh->xid; if (xid == recv_xid) { *replyp = reply; return 0; } error = ofptype_decode(&type, oh); if (!error && type == OFPTYPE_ERROR && error_reporter) { error_reporter(oh); } else { VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32 " != expected %08"PRIx32, vconn->name, ntohl(recv_xid), ntohl(xid)); } ofpbuf_delete(reply); } } /* Waits until a message with a transaction ID matching 'xid' is received on * 'vconn'. Returns 0 if successful, in which case the reply is stored in * '*replyp' for the caller to examine and free. Otherwise returns a positive * errno value, or EOF, and sets '*replyp' to null. * * 'request' is always destroyed, regardless of the return value. */ int vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp) { return vconn_recv_xid__(vconn, xid, replyp, NULL); } static int vconn_transact__(struct vconn *vconn, struct ofpbuf *request, struct ofpbuf **replyp, void (*error_reporter)(const struct ofp_header *)) { ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid; int error; *replyp = NULL; error = vconn_send_block(vconn, request); if (error) { ofpbuf_delete(request); } return error ? error : vconn_recv_xid__(vconn, send_xid, replyp, error_reporter); } /* Sends 'request' to 'vconn' and blocks until it receives a reply with a * matching transaction ID. Returns 0 if successful, in which case the reply * is stored in '*replyp' for the caller to examine and free. Otherwise * returns a positive errno value, or EOF, and sets '*replyp' to null. * * 'request' should be an OpenFlow request that requires a reply. Otherwise, * if there is no reply, this function can end up blocking forever (or until * the peer drops the connection). * * 'request' is always destroyed, regardless of the return value. */ int vconn_transact(struct vconn *vconn, struct ofpbuf *request, struct ofpbuf **replyp) { return vconn_transact__(vconn, request, replyp, NULL); } /* Sends 'request' followed by a barrier request to 'vconn', then blocks until * it receives a reply to the barrier. If successful, stores the reply to * 'request' in '*replyp', if one was received, and otherwise NULL, then * returns 0. Otherwise returns a positive errno value, or EOF, and sets * '*replyp' to null. * * This function is useful for sending an OpenFlow request that doesn't * ordinarily include a reply but might report an error in special * circumstances. * * 'request' is always destroyed, regardless of the return value. */ int vconn_transact_noreply(struct vconn *vconn, struct ofpbuf *request, struct ofpbuf **replyp) { ovs_be32 request_xid; ovs_be32 barrier_xid; struct ofpbuf *barrier; int error; *replyp = NULL; /* Send request. */ request_xid = ((struct ofp_header *) request->data)->xid; error = vconn_send_block(vconn, request); if (error) { ofpbuf_delete(request); return error; } /* Send barrier. */ barrier = ofputil_encode_barrier_request(vconn_get_version(vconn)); barrier_xid = ((struct ofp_header *) barrier->data)->xid; error = vconn_send_block(vconn, barrier); if (error) { ofpbuf_delete(barrier); return error; } for (;;) { struct ofpbuf *msg; ovs_be32 msg_xid; int error; error = vconn_recv_block(vconn, &msg); if (error) { ofpbuf_delete(*replyp); *replyp = NULL; return error; } msg_xid = ((struct ofp_header *) msg->data)->xid; if (msg_xid == request_xid) { if (*replyp) { VLOG_WARN_RL(&bad_ofmsg_rl, "%s: duplicate replies with " "xid %08"PRIx32, vconn->name, ntohl(msg_xid)); ofpbuf_delete(*replyp); } *replyp = msg; } else { ofpbuf_delete(msg); if (msg_xid == barrier_xid) { return 0; } else { VLOG_DBG_RL(&bad_ofmsg_rl, "%s: reply with xid %08"PRIx32 " != expected %08"PRIx32" or %08"PRIx32, vconn->name, ntohl(msg_xid), ntohl(request_xid), ntohl(barrier_xid)); } } } } /* vconn_transact_noreply() for a list of "struct ofpbuf"s, sent one by one. * All of the requests on 'requests' are always destroyed, regardless of the * return value. */ int vconn_transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests, struct ofpbuf **replyp) { struct ofpbuf *request; LIST_FOR_EACH_POP (request, list_node, requests) { int error; error = vconn_transact_noreply(vconn, request, replyp); if (error || *replyp) { ofpbuf_list_delete(requests); return error; } } *replyp = NULL; return 0; } static enum ofperr vconn_bundle_reply_validate(struct ofpbuf *reply, struct ofputil_bundle_ctrl_msg *request, void (*error_reporter)(const struct ofp_header *)) { const struct ofp_header *oh; enum ofptype type; enum ofperr error; struct ofputil_bundle_ctrl_msg rbc; oh = reply->data; error = ofptype_decode(&type, oh); if (error) { return error; } if (type == OFPTYPE_ERROR) { error_reporter(oh); return ofperr_decode_msg(oh, NULL); } if (type != OFPTYPE_BUNDLE_CONTROL) { return OFPERR_OFPBRC_BAD_TYPE; } error = ofputil_decode_bundle_ctrl(oh, &rbc); if (error) { return error; } if (rbc.bundle_id != request->bundle_id) { return OFPERR_OFPBFC_BAD_ID; } if (rbc.type != request->type + 1) { return OFPERR_OFPBFC_BAD_TYPE; } return 0; } /* Send bundle control message 'bc' of 'type' via 'vconn', and wait for either * an error or the corresponding bundle control message response. * * 'error_reporter' is called for any error responses received, which may be * also regarding earlier OpenFlow messages than this bundle control message. * * Returns errno value, or 0 when successful. */ static int vconn_bundle_control_transact(struct vconn *vconn, struct ofputil_bundle_ctrl_msg *bc, uint16_t type, void (*error_reporter)(const struct ofp_header *)) { struct ofpbuf *request, *reply; int error; enum ofperr ofperr; bc->type = type; request = ofputil_encode_bundle_ctrl_request(vconn->version, bc); ofpmsg_update_length(request); error = vconn_transact__(vconn, request, &reply, error_reporter); if (error) { return error; } ofperr = vconn_bundle_reply_validate(reply, bc, error_reporter); if (ofperr) { VLOG_WARN_RL(&bad_ofmsg_rl, "Bundle %s failed (%s).", type == OFPBCT_OPEN_REQUEST ? "open" : type == OFPBCT_CLOSE_REQUEST ? "close" : type == OFPBCT_COMMIT_REQUEST ? "commit" : type == OFPBCT_DISCARD_REQUEST ? "discard" : "control message", ofperr_to_string(ofperr)); } ofpbuf_delete(reply); return ofperr ? EPROTO : 0; } /* Checks if error responses can be received on 'vconn'. */ static void vconn_recv_error(struct vconn *vconn, void (*error_reporter)(const struct ofp_header *)) { int error; do { struct ofpbuf *reply; error = vconn_recv(vconn, &reply); if (!error) { const struct ofp_header *oh; enum ofptype type; enum ofperr ofperr; oh = reply->data; ofperr = ofptype_decode(&type, oh); if (!ofperr && type == OFPTYPE_ERROR) { error_reporter(oh); } else { VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received unexpected reply with xid %08"PRIx32, vconn->name, ntohl(oh->xid)); } ofpbuf_delete(reply); } } while (!error); } static int vconn_bundle_add_msg(struct vconn *vconn, struct ofputil_bundle_ctrl_msg *bc, struct ofpbuf *msg, void (*error_reporter)(const struct ofp_header *)) { struct ofputil_bundle_add_msg bam; struct ofpbuf *request; int error; bam.bundle_id = bc->bundle_id; bam.flags = bc->flags; bam.msg = msg->data; request = ofputil_encode_bundle_add(vconn->version, &bam); ofpmsg_update_length(request); error = vconn_send_block(vconn, request); if (!error) { /* Check for an error return, so that the socket buffer does not become * full of errors. */ vconn_recv_error(vconn, error_reporter); } return error; } int vconn_bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags, void (*error_reporter)(const struct ofp_header *)) { struct ofputil_bundle_ctrl_msg bc; struct ofpbuf *request; int error; memset(&bc, 0, sizeof bc); bc.flags = flags; error = vconn_bundle_control_transact(vconn, &bc, OFPBCT_OPEN_REQUEST, error_reporter); if (error) { return error; } LIST_FOR_EACH (request, list_node, requests) { error = vconn_bundle_add_msg(vconn, &bc, request, error_reporter); if (error) { break; } } if (!error) { error = vconn_bundle_control_transact(vconn, &bc, OFPBCT_COMMIT_REQUEST, error_reporter); } else { /* Do not overwrite the error code from vconn_bundle_add_msg(). * Any error in discard should be either reported or logged, so it * should not get lost. */ vconn_bundle_control_transact(vconn, &bc, OFPBCT_DISCARD_REQUEST, error_reporter); } return error; } void vconn_wait(struct vconn *vconn, enum vconn_wait_type wait) { ovs_assert(wait == WAIT_CONNECT || wait == WAIT_RECV || wait == WAIT_SEND); switch (vconn->state) { case VCS_CONNECTING: wait = WAIT_CONNECT; break; case VCS_SEND_HELLO: case VCS_SEND_ERROR: wait = WAIT_SEND; break; case VCS_RECV_HELLO: wait = WAIT_RECV; break; case VCS_CONNECTED: break; case VCS_DISCONNECTED: poll_immediate_wake(); return; } (vconn->vclass->wait)(vconn, wait); } void vconn_connect_wait(struct vconn *vconn) { vconn_wait(vconn, WAIT_CONNECT); } void vconn_recv_wait(struct vconn *vconn) { vconn_wait(vconn, WAIT_RECV); } void vconn_send_wait(struct vconn *vconn) { vconn_wait(vconn, WAIT_SEND); } /* Given 'name', a connection name in the form "TYPE:ARGS", stores the class * named "TYPE" into '*classp' and returns 0. Returns EAFNOSUPPORT and stores * a null pointer into '*classp' if 'name' is in the wrong form or if no such * class exists. */ static int pvconn_lookup_class(const char *name, const struct pvconn_class **classp) { size_t prefix_len; prefix_len = strcspn(name, ":"); if (name[prefix_len] != '\0') { size_t i; for (i = 0; i < ARRAY_SIZE(pvconn_classes); i++) { const struct pvconn_class *class = pvconn_classes[i]; if (strlen(class->name) == prefix_len && !memcmp(class->name, name, prefix_len)) { *classp = class; return 0; } } } *classp = NULL; return EAFNOSUPPORT; } /* Returns 0 if 'name' is a connection name in the form "TYPE:ARGS" and TYPE is * a supported connection type, otherwise EAFNOSUPPORT. */ int pvconn_verify_name(const char *name) { const struct pvconn_class *class; return pvconn_lookup_class(name, &class); } /* Attempts to start listening for OpenFlow connections. 'name' is a * connection name in the form "TYPE:ARGS", where TYPE is an passive vconn * class's name and ARGS are vconn class-specific. * * vconns accepted by the pvconn will automatically negotiate an OpenFlow * protocol version acceptable to both peers on the connection. The version * negotiated will be one of those in the 'allowed_versions' bitmap: version * 'x' is allowed if allowed_versions & (1 << x) is nonzero. If * 'allowed_versions' is zero, then OFPUTIL_DEFAULT_VERSIONS are allowed. * * Returns 0 if successful, otherwise a positive errno value. If successful, * stores a pointer to the new connection in '*pvconnp', otherwise a null * pointer. */ int pvconn_open(const char *name, uint32_t allowed_versions, uint8_t dscp, struct pvconn **pvconnp) { const struct pvconn_class *class; struct pvconn *pvconn; char *suffix_copy; int error; check_vconn_classes(); if (!allowed_versions) { allowed_versions = OFPUTIL_DEFAULT_VERSIONS; } /* Look up the class. */ error = pvconn_lookup_class(name, &class); if (!class) { goto error; } /* Call class's "open" function. */ suffix_copy = xstrdup(strchr(name, ':') + 1); error = class->listen(name, allowed_versions, suffix_copy, &pvconn, dscp); free(suffix_copy); if (error) { goto error; } /* Success. */ *pvconnp = pvconn; return 0; error: *pvconnp = NULL; return error; } /* Returns the name that was used to open 'pvconn'. The caller must not * modify or free the name. */ const char * pvconn_get_name(const struct pvconn *pvconn) { return pvconn->name; } /* Closes 'pvconn'. */ void pvconn_close(struct pvconn *pvconn) { if (pvconn != NULL) { char *name = pvconn->name; (pvconn->pvclass->close)(pvconn); free(name); } } /* Tries to accept a new connection on 'pvconn'. If successful, stores the new * connection in '*new_vconn' and returns 0. Otherwise, returns a positive * errno value. * * The new vconn will automatically negotiate an OpenFlow protocol version * acceptable to both peers on the connection. The version negotiated will be * no lower than 'min_version' and no higher than 'max_version'. * * pvconn_accept() will not block waiting for a connection. If no connection * is ready to be accepted, it returns EAGAIN immediately. */ int pvconn_accept(struct pvconn *pvconn, struct vconn **new_vconn) { int retval = (pvconn->pvclass->accept)(pvconn, new_vconn); if (retval) { *new_vconn = NULL; } else { ovs_assert((*new_vconn)->state != VCS_CONNECTING || (*new_vconn)->vclass->connect); } return retval; } void pvconn_wait(struct pvconn *pvconn) { (pvconn->pvclass->wait)(pvconn); } /* Initializes 'vconn' as a new vconn named 'name', implemented via 'class'. * The initial connection status, supplied as 'connect_status', is interpreted * as follows: * * - 0: 'vconn' is connected. Its 'send' and 'recv' functions may be * called in the normal fashion. * * - EAGAIN: 'vconn' is trying to complete a connection. Its 'connect' * function should be called to complete the connection. * * - Other positive errno values indicate that the connection failed with * the specified error. * * After calling this function, vconn_close() must be used to destroy 'vconn', * otherwise resources will be leaked. * * The caller retains ownership of 'name'. */ void vconn_init(struct vconn *vconn, const struct vconn_class *class, int connect_status, const char *name, uint32_t allowed_versions) { memset(vconn, 0, sizeof *vconn); vconn->vclass = class; vconn->state = (connect_status == EAGAIN ? VCS_CONNECTING : !connect_status ? VCS_SEND_HELLO : VCS_DISCONNECTED); vconn->error = connect_status; vconn->allowed_versions = allowed_versions; vconn->name = xstrdup(name); ovs_assert(vconn->state != VCS_CONNECTING || class->connect); } void pvconn_init(struct pvconn *pvconn, const struct pvconn_class *class, const char *name, uint32_t allowed_versions) { pvconn->pvclass = class; pvconn->name = xstrdup(name); pvconn->allowed_versions = allowed_versions; } openvswitch-2.5.9/lib/PaxHeaders.82075/netlink.h0000644000000000000000000000013213534540071016227 xustar0030 mtime=1567801401.465681639 30 atime=1567801402.081686164 30 ctime=1567801424.781853426 openvswitch-2.5.9/lib/netlink.h0000644000175000017500000002263613534540071017726 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETLINK_H #define NETLINK_H 1 /* Netlink message helpers. * * Netlink is a datagram-based network protocol primarily for communication * between user processes and the kernel, and mainly on Linux. Netlink is * specified in RFC 3549, "Linux Netlink as an IP Services Protocol". * * Netlink is not suitable for use in physical networks of heterogeneous * machines because host byte order is used throughout. * * This header file defines helper functions for working with Netlink messages. * For Netlink protocol definitions, see netlink-protocol.h. For * Linux-specific definitions for Netlink sockets, see netlink-socket.h. */ #include #include #include #include #include "netlink-protocol.h" #include "openvswitch/types.h" struct ofpbuf; struct nlattr; /* Accessing headers and data. */ struct nlmsghdr *nl_msg_nlmsghdr(const struct ofpbuf *); struct genlmsghdr *nl_msg_genlmsghdr(const struct ofpbuf *); bool nl_msg_nlmsgerr(const struct ofpbuf *, int *error); void nl_msg_reserve(struct ofpbuf *, size_t); /* Appending and prepending headers and raw data. */ void nl_msg_put_nlmsghdr(struct ofpbuf *, size_t expected_payload, uint32_t type, uint32_t flags); void nl_msg_put_genlmsghdr(struct ofpbuf *, size_t expected_payload, int family, uint32_t flags, uint8_t cmd, uint8_t version); void nl_msg_put(struct ofpbuf *, const void *, size_t); void *nl_msg_put_uninit(struct ofpbuf *, size_t); void nl_msg_push(struct ofpbuf *, const void *, size_t); void *nl_msg_push_uninit(struct ofpbuf *, size_t); /* Appending attributes. */ void *nl_msg_put_unspec_uninit(struct ofpbuf *, uint16_t type, size_t); void *nl_msg_put_unspec_zero(struct ofpbuf *, uint16_t type, size_t); void nl_msg_put_unspec(struct ofpbuf *, uint16_t type, const void *, size_t); void nl_msg_put_flag(struct ofpbuf *, uint16_t type); void nl_msg_put_u8(struct ofpbuf *, uint16_t type, uint8_t value); void nl_msg_put_u16(struct ofpbuf *, uint16_t type, uint16_t value); void nl_msg_put_u32(struct ofpbuf *, uint16_t type, uint32_t value); void nl_msg_put_u64(struct ofpbuf *, uint16_t type, uint64_t value); void nl_msg_put_be16(struct ofpbuf *, uint16_t type, ovs_be16 value); void nl_msg_put_be32(struct ofpbuf *, uint16_t type, ovs_be32 value); void nl_msg_put_be64(struct ofpbuf *, uint16_t type, ovs_be64 value); void nl_msg_put_in6_addr(struct ofpbuf *msg, uint16_t type, const struct in6_addr *value); void nl_msg_put_odp_port(struct ofpbuf *, uint16_t type, odp_port_t value); void nl_msg_put_string__(struct ofpbuf *, uint16_t type, const char *value, size_t len); void nl_msg_put_string(struct ofpbuf *, uint16_t type, const char *value); size_t nl_msg_start_nested(struct ofpbuf *, uint16_t type); void nl_msg_end_nested(struct ofpbuf *, size_t offset); void nl_msg_put_nested(struct ofpbuf *, uint16_t type, const void *data, size_t size); /* Prepending attributes. */ void *nl_msg_push_unspec_uninit(struct ofpbuf *, uint16_t type, size_t); void nl_msg_push_unspec(struct ofpbuf *, uint16_t type, const void *, size_t); void nl_msg_push_flag(struct ofpbuf *, uint16_t type); void nl_msg_push_u8(struct ofpbuf *, uint16_t type, uint8_t value); void nl_msg_push_u16(struct ofpbuf *, uint16_t type, uint16_t value); void nl_msg_push_u32(struct ofpbuf *, uint16_t type, uint32_t value); void nl_msg_push_u64(struct ofpbuf *, uint16_t type, uint64_t value); void nl_msg_push_be16(struct ofpbuf *, uint16_t type, ovs_be16 value); void nl_msg_push_be32(struct ofpbuf *, uint16_t type, ovs_be32 value); void nl_msg_push_be64(struct ofpbuf *, uint16_t type, ovs_be64 value); void nl_msg_push_string(struct ofpbuf *, uint16_t type, const char *value); /* Separating buffers into individual messages. */ struct nlmsghdr *nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg); /* Sizes of various attribute types, in bytes, including the attribute header * and padding. * * A minimum-size attribute is 4 bytes long: 4 bytes of header, no bytes of * payload, no padding. * * A maximum-size attribute is 65536 bytes long: 4 bytes of header, 65531 bytes * of payload, 1 byte of padding. (Thus, NL_ATTR_SIZE() of a maximum length * attribute payload does not fit in 16 bits.) */ #define NL_ATTR_SIZE(PAYLOAD_SIZE) (NLA_HDRLEN + NLA_ALIGN(PAYLOAD_SIZE)) #define NL_A_U8_SIZE NL_ATTR_SIZE(sizeof(uint8_t)) #define NL_A_U16_SIZE NL_ATTR_SIZE(sizeof(uint16_t)) #define NL_A_U32_SIZE NL_ATTR_SIZE(sizeof(uint32_t)) #define NL_A_U64_SIZE NL_ATTR_SIZE(sizeof(uint64_t)) #define NL_A_BE16_SIZE NL_ATTR_SIZE(sizeof(ovs_be16)) #define NL_A_BE32_SIZE NL_ATTR_SIZE(sizeof(ovs_be32)) #define NL_A_BE64_SIZE NL_ATTR_SIZE(sizeof(ovs_be64)) #define NL_A_FLAG_SIZE NL_ATTR_SIZE(0) #define NL_A_IPV6_SIZE NL_ATTR_SIZE(sizeof(struct in6_addr)) bool nl_attr_oversized(size_t payload_size); /* Netlink attribute types. */ enum nl_attr_type { NL_A_NO_ATTR = 0, NL_A_UNSPEC, NL_A_U8, NL_A_U16, NL_A_BE16 = NL_A_U16, NL_A_U32, NL_A_BE32 = NL_A_U32, NL_A_U64, NL_A_BE64 = NL_A_U64, NL_A_STRING, NL_A_FLAG, NL_A_IPV6, NL_A_NESTED, N_NL_ATTR_TYPES }; /* Netlink attribute iteration. */ static inline struct nlattr * nl_attr_next(const struct nlattr *nla) { return (void *) ((uint8_t *) nla + NLA_ALIGN(nla->nla_len)); } static inline bool nl_attr_is_valid(const struct nlattr *nla, size_t maxlen) { return (maxlen >= sizeof *nla && nla->nla_len >= sizeof *nla && nla->nla_len <= maxlen); } static inline size_t nl_attr_len_pad(const struct nlattr *nla, size_t maxlen) { size_t len = NLA_ALIGN(nla->nla_len); return len <= maxlen ? len : nla->nla_len; } /* This macro is careful to check for attributes with bad lengths. */ #define NL_ATTR_FOR_EACH(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ nl_attr_is_valid(ITER, LEFT); \ (LEFT) -= nl_attr_len_pad(ITER, LEFT), (ITER) = nl_attr_next(ITER)) /* This macro does not check for attributes with bad lengths. It should only * be used with messages from trusted sources or with messages that have * already been validated (e.g. with NL_ATTR_FOR_EACH). */ #define NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ (LEFT) > 0; \ (LEFT) -= nl_attr_len_pad(ITER, LEFT), (ITER) = nl_attr_next(ITER)) /* These variants are convenient for iterating nested attributes. */ #define NL_NESTED_FOR_EACH(ITER, LEFT, A) \ NL_ATTR_FOR_EACH(ITER, LEFT, nl_attr_get(A), nl_attr_get_size(A)) #define NL_NESTED_FOR_EACH_UNSAFE(ITER, LEFT, A) \ NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, nl_attr_get(A), nl_attr_get_size(A)) /* Netlink attribute parsing. */ int nl_attr_type(const struct nlattr *); const void *nl_attr_get(const struct nlattr *); size_t nl_attr_get_size(const struct nlattr *); const void *nl_attr_get_unspec(const struct nlattr *, size_t size); bool nl_attr_get_flag(const struct nlattr *); uint8_t nl_attr_get_u8(const struct nlattr *); uint16_t nl_attr_get_u16(const struct nlattr *); uint32_t nl_attr_get_u32(const struct nlattr *); uint64_t nl_attr_get_u64(const struct nlattr *); ovs_be16 nl_attr_get_be16(const struct nlattr *); ovs_be32 nl_attr_get_be32(const struct nlattr *); ovs_be64 nl_attr_get_be64(const struct nlattr *); struct in6_addr nl_attr_get_in6_addr(const struct nlattr *nla); odp_port_t nl_attr_get_odp_port(const struct nlattr *); const char *nl_attr_get_string(const struct nlattr *); void nl_attr_get_nested(const struct nlattr *, struct ofpbuf *); /* Netlink attribute policy. * * Specifies how to parse a single attribute from a Netlink message payload. */ struct nl_policy { enum nl_attr_type type; size_t min_len, max_len; bool optional; }; #define NL_POLICY_FOR(TYPE) \ .type = NL_A_UNSPEC, .min_len = sizeof(TYPE), .max_len = sizeof(TYPE) bool nl_attr_validate(const struct nlattr *, const struct nl_policy *); bool nl_policy_parse(const struct ofpbuf *, size_t offset, const struct nl_policy[], struct nlattr *[], size_t n_attrs); bool nl_parse_nested(const struct nlattr *, const struct nl_policy[], struct nlattr *[], size_t n_attrs); const struct nlattr *nl_attr_find(const struct ofpbuf *, size_t hdr_len, uint16_t type); const struct nlattr *nl_attr_find_nested(const struct nlattr *, uint16_t type); const struct nlattr *nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type); #endif /* netlink.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpctl.man0000644000000000000000000000013113534540071016214 xustar0029 mtime=1567801401.34168073 30 atime=1567801402.073686105 30 ctime=1567801423.729845671 openvswitch-2.5.9/lib/dpctl.man0000644000175000017500000002021613534540071017704 0ustar00jpettitjpettit00000000000000.TP \*(DX\fBadd\-dp \fIdp\fR [\fInetdev\fR[\fB,\fIoption\fR]...] Creates datapath \fIdp\fR, with a local port also named \fIdp\fR. This will fail if a network device \fIdp\fR already exists. .IP If \fInetdev\fRs are specified, \fB\*(PN\fR adds them to the new datapath, just as if \fBadd\-if\fR was specified. . .TP \*(DX\fBdel\-dp \fIdp\fR Deletes datapath \fIdp\fR. If \fIdp\fR is associated with any network devices, they are automatically removed. . .TP \*(DX\fBadd\-if \fIdp netdev\fR[\fB,\fIoption\fR]... Adds each \fInetdev\fR to the set of network devices datapath \fIdp\fR monitors, where \fIdp\fR is the name of an existing datapath, and \fInetdev\fR is the name of one of the host's network devices, e.g. \fBeth0\fR. Once a network device has been added to a datapath, the datapath has complete ownership of the network device's traffic and the network device appears silent to the rest of the system. .IP A \fInetdev\fR may be followed by a comma-separated list of options. The following options are currently supported: . .RS .IP "\fBtype=\fItype\fR" Specifies the type of port to add. The default type is \fBsystem\fR. .IP "\fBport_no=\fIport\fR" Requests a specific port number within the datapath. If this option is not specified then one will be automatically assigned. .IP "\fIkey\fB=\fIvalue\fR" Adds an arbitrary key-value option to the port's configuration. .RE .IP \fBovs\-vswitchd.conf.db\fR(5) documents the available port types and options. . .IP "\*(DX\fBset\-if \fIdp port\fR[\fB,\fIoption\fR]..." Reconfigures each \fIport\fR in \fIdp\fR as specified. An \fIoption\fR of the form \fIkey\fB=\fIvalue\fR adds the specified key-value option to the port or overrides an existing key's value. An \fIoption\fR of the form \fIkey\fB=\fR, that is, without a value, deletes the key-value named \fIkey\fR. The type and port number of a port cannot be changed, so \fBtype\fR and \fBport_no\fR are only allowed if they match the existing configuration. .TP \*(DX\fBdel\-if \fIdp netdev\fR... Removes each \fInetdev\fR from the list of network devices datapath \fIdp\fR monitors. . .TP \*(DX\fBdump\-dps\fR Prints the name of each configured datapath on a separate line. . .TP .DO "[\fB\-s\fR | \fB\-\-statistics\fR]" "\*(DX\fBshow" "\fR[\fIdp\fR...]" Prints a summary of configured datapaths, including their datapath numbers and a list of ports connected to each datapath. (The local port is identified as port 0.) If \fB\-s\fR or \fB\-\-statistics\fR is specified, then packet and byte counters are also printed for each port. .IP The datapath numbers consists of flow stats and mega flow mask stats. .IP The "lookups" row displays three stats related to flow lookup triggered by processing incoming packets in the datapath. "hit" displays number of packets matches existing flows. "missed" displays the number of packets not matching any existing flow and require user space processing. "lost" displays number of packets destined for user space process but subsequently dropped before reaching userspace. The sum of "hit" and "miss" equals to the total number of packets datapath processed. .IP The "flows" row displays the number of flows in datapath. .IP The "masks" row displays the mega flow mask stats. This row is omitted for datapath not implementing mega flow. "hit" displays the total number of masks visited for matching incoming packets. "total" displays number of masks in the datapath. "hit/pkt" displays the average number of masks visited per packet; the ratio between "hit" and total number of packets processed by the datapath". .IP If one or more datapaths are specified, information on only those datapaths are displayed. Otherwise, \fB\*(PN\fR displays information about all configured datapaths. .SS "DATAPATH FLOW TABLE DEBUGGING COMMANDS" The following commands are primarily useful for debugging Open vSwitch. The flow table entries (both matches and actions) that they work with are not OpenFlow flow entries. Instead, they are different and considerably simpler flows maintained by the Open vSwitch kernel module. Use \fBovs\-ofctl\fR(8), instead, to work with OpenFlow flow entries. . .PP The \fIdp\fR argument to each of these commands is optional when exactly one datapath exists, in which case that datapath is the default. When multiple datapaths exist, then a datapath name is required. . .TP .DO "[\fB\-m \fR| \fB\-\-more\fR]" \*(DX\fBdump\-flows\fR "[\fIdp\fR] [\fBfilter=\fIfilter\fR]" Prints to the console all flow entries in datapath \fIdp\fR's flow table. Without \fB\-m\fR or \fB\-\-more\fR, output omits match fields that a flow wildcards entirely; with \fB\-m\fR or \fB\-\-more\fR, output includes all wildcarded fields. .IP If \fBfilter=\fIfilter\fR is specified, only displays the flows that match the \fIfilter\fR. \fIfilter\fR is a flow in the form similiar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR command. (This is not an OpenFlow flow: besides other differences, it never contains wildcards.) The \fIfilter\fR is also useful to match wildcarded fields in the datapath flow. As an example, \fBfilter='tcp,tp_src=100'\fR will match the datapath flow containing '\fBtcp(src=80/0xff00,dst=8080/0xff)\fR'. . .IP "\*(DX\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR" .TP .DO "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | \fB\-\-statistics\fR]" "\*(DX\fBmod\-flow\fR" "[\fIdp\fR] \fIflow actions\fR" Adds or modifies a flow in \fIdp\fR's flow table that, when a packet matching \fIflow\fR arrives, causes \fIactions\fR to be executed. .IP The \fBadd\-flow\fR command succeeds only if \fIflow\fR does not already exist in \fIdp\fR. Contrariwise, \fBmod\-flow\fR without \fB\-\-may\-create\fR only modifies the actions for an existing flow. With \fB\-\-may\-create\fR, \fBmod\-flow\fR will add a new flow or modify an existing one. .IP If \fB\-s\fR or \fB\-\-statistics\fR is specified, then \fBmod\-flow\fR prints the modified flow's statistics. A flow's statistics are the number of packets and bytes that have passed through the flow, the elapsed time since the flow last processed a packet (if ever), and (for TCP flows) the union of the TCP flags processed through the flow. .IP With \fB\-\-clear\fR, \fBmod\-flow\fR zeros out the flow's statistics. The statistics printed if \fB\-s\fR or \fB\-\-statistics\fR is also specified are those from just before clearing the statistics. . .TP .DO "[\fB\-s\fR | \fB\-\-statistics\fR]" "\*(DX\fBdel\-flow\fR" "[\fIdp\fR] \fIflow\fR" Deletes the flow from \fIdp\fR's flow table that matches \fIflow\fR. If \fB\-s\fR or \fB\-\-statistics\fR is specified, then \fBdel\-flow\fR prints the deleted flow's statistics. . .IP "\*(DX\fBget\-flow\fR [\fIdp\fR] ufid:\fIufid\fR" Fetches the flow from \fIdp\fR's flow table with unique identifier \fIufid\fR. \fIufid\fR must be specified as a string of 32 hexadecimal characters. . .IP "\*(DX\fBdel\-flows\fR [\fIdp\fR]" Deletes all flow entries from datapath \fIdp\fR's flow table. .SS "CONNECTION TRACKING TABLE DEBUGGING COMMANDS" The following commands are primarily useful for debugging the connection tracking entries in the datapath. . .PP The \fIdp\fR argument to each of these commands is optional when exactly one datapath exists, in which case that datapath is the default. When multiple datapaths exist, then a datapath name is required. . .PP \fBN.B.\fR(Linux specific): the \fIsystem\fR datapaths (i.e. the Linux kernel module Open vSwitch datapaths) share a single connection tracking table (which is also used by other kernel subsystems, such as iptables, nftables and the regular host stack). Therefore, the following commands do not apply specifically to one datapath. . .TP .DO "[\fB\-m\fR | \fB\-\-more\fR] [\fB\-s\fR | \fB\-\-statistics\fR]" "\*(DX\fBdump\-conntrack\fR" "[\fIdp\fR] [\fBzone=\fIzone\fR]" Prints to the console all the connection entries in the tracker used by \fIdp\fR. If \fBzone=\fIzone\fR is specified, only shows the connections in \fBzone\fR. With \fB\-\-more\fR, some implementation specific details are included. With \fB\-\-statistics\fR timeouts and timestamps are added to the output. . .TP \*(DX\fBflush\-conntrack [\fIdp\fR] [\fBzone=\fIzone\fR] Flushes all the connection entries in the tracker used by \fIdp\fR. If \fBzone=\fIzone\fR is specified, only flushes the connections in \fBzone\fR. openvswitch-2.5.9/lib/PaxHeaders.82075/dirs.c.in0000644000000000000000000000013213534540071016124 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801423.793846143 openvswitch-2.5.9/lib/dirs.c.in0000644000175000017500000000524613534540071017621 0ustar00jpettitjpettit00000000000000#line 2 "@srcdir@/lib/dirs.c.in" /* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dirs.h" #include #include "ovs-thread.h" #include "util.h" struct directory { const char *value; /* Actual value; NULL if not yet determined. */ const char *default_value; /* Default value. */ const char *var_name; /* Environment variable to override default. */ struct ovsthread_once once; /* Ensures 'value' gets initialized once. */ }; static const char * get_dir(struct directory *d) { if (ovsthread_once_start(&d->once)) { d->value = getenv(d->var_name); if (!d->value || !d->value[0]) { d->value = d->default_value; } ovsthread_once_done(&d->once); } return d->value; } const char * ovs_sysconfdir(void) { static struct directory d = { NULL, @sysconfdir@, "OVS_SYSCONFDIR", OVSTHREAD_ONCE_INITIALIZER }; return get_dir(&d); } const char * ovs_pkgdatadir(void) { static struct directory d = { NULL, @pkgdatadir@, "OVS_PKGDATADIR", OVSTHREAD_ONCE_INITIALIZER }; return get_dir(&d); } const char * ovs_rundir(void) { static struct directory d = { NULL, @RUNDIR@, "OVS_RUNDIR", OVSTHREAD_ONCE_INITIALIZER }; return get_dir(&d); } const char * ovs_logdir(void) { static struct directory d = { NULL, @LOGDIR@, "OVS_LOGDIR", OVSTHREAD_ONCE_INITIALIZER }; return get_dir(&d); } const char * ovs_dbdir(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static const char *dbdir; if (ovsthread_once_start(&once)) { dbdir = getenv("OVS_DBDIR"); if (!dbdir || !dbdir[0]) { char *sysconfdir = getenv("OVS_SYSCONFDIR"); dbdir = (sysconfdir ? xasprintf("%s/openvswitch", sysconfdir) : @DBDIR@); } ovsthread_once_done(&once); } return dbdir; } const char * ovs_bindir(void) { static struct directory d = { NULL, @bindir@, "OVS_BINDIR", OVSTHREAD_ONCE_INITIALIZER }; return get_dir(&d); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-msvc.h0000644000000000000000000000013213534540071017612 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.817853691 openvswitch-2.5.9/lib/ovs-atomic-msvc.h0000644000175000017500000004267613534540071021317 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives for MSVC * on i586 or greater platforms (32 bit). */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif /* From msdn documentation: With Visual Studio 2003, volatile to volatile * references are ordered; the compiler will not re-order volatile variable * access. With Visual Studio 2005, the compiler also uses acquire semantics * for read operations on volatile variables and release semantics for write * operations on volatile variables (when supported by the CPU). * * Though there is no clear documentation that states that anything greater * than VS 2005 has the same behavior as described above, looking through MSVCs * C++ atomics library in VS2013 shows that the compiler still takes * acquire/release semantics on volatile variables. */ #define ATOMIC(TYPE) TYPE volatile typedef enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; #define ATOMIC_BOOL_LOCK_FREE 2 #define ATOMIC_CHAR_LOCK_FREE 2 #define ATOMIC_SHORT_LOCK_FREE 2 #define ATOMIC_INT_LOCK_FREE 2 #define ATOMIC_LONG_LOCK_FREE 2 #define ATOMIC_LLONG_LOCK_FREE 2 #define ATOMIC_POINTER_LOCK_FREE 2 #define IS_LOCKLESS_ATOMIC(OBJECT) \ (sizeof(OBJECT) <= 8 && IS_POW2(sizeof(OBJECT))) #define ATOMIC_VAR_INIT(VALUE) (VALUE) #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) static inline void atomic_compiler_barrier(memory_order order) { /* In case of 'memory_order_consume', it is implicitly assumed that * the compiler will not move instructions that have data-dependency * on the variable in question before the barrier. */ if (order > memory_order_consume) { _ReadWriteBarrier(); } } static inline void atomic_thread_fence(memory_order order) { /* x86 is strongly ordered and acquire/release semantics come * automatically. */ atomic_compiler_barrier(order); if (order == memory_order_seq_cst) { MemoryBarrier(); atomic_compiler_barrier(order); } } static inline void atomic_signal_fence(memory_order order) { atomic_compiler_barrier(order); } /* 1, 2 and 4 bytes loads and stores are atomic on aligned memory. In addition, * since the compiler automatically takes acquire and release semantics on * volatile variables, for any order lesser than 'memory_order_seq_cst', we * can directly assign or read values. */ #define atomic_store32(DST, SRC, ORDER) \ if (ORDER == memory_order_seq_cst) { \ InterlockedExchange((int32_t volatile *) (DST), \ (int32_t) (SRC)); \ } else { \ *(DST) = (SRC); \ } /* MSVC converts 64 bit writes into two instructions. So there is * a possibility that an interrupt can make a 64 bit write non-atomic even * when 8 byte aligned. So use InterlockedExchange64(). * * For atomic stores, 'consume' and 'acquire' semantics are not valid. But we * are using 'Exchange' to get atomic stores here and we only have * InterlockedExchange64(), InterlockedExchangeNoFence64() and * InterlockedExchange64Acquire() available. So we are forced to use * InterlockedExchange64() which uses full memory barrier for everything * greater than 'memory_order_relaxed'. */ #define atomic_store64(DST, SRC, ORDER) \ if (ORDER == memory_order_relaxed) { \ InterlockedExchangeNoFence64((int64_t volatile *) (DST), \ (int64_t) (SRC)); \ } else { \ InterlockedExchange64((int64_t volatile *) (DST), (int64_t) (SRC));\ } /* Used for 8 and 16 bit variations. */ #define atomic_storeX(X, DST, SRC, ORDER) \ if (ORDER == memory_order_seq_cst) { \ InterlockedExchange##X((int##X##_t volatile *) (DST), \ (int##X##_t) (SRC)); \ } else { \ *(DST) = (SRC); \ } #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) #define atomic_store_explicit(DST, SRC, ORDER) \ if (sizeof *(DST) == 1) { \ atomic_storeX(8, DST, SRC, ORDER) \ } else if (sizeof *(DST) == 2) { \ atomic_storeX(16, DST, SRC, ORDER) \ } else if (sizeof *(DST) == 4) { \ atomic_store32(DST, SRC, ORDER) \ } else if (sizeof *(DST) == 8) { \ atomic_store64(DST, SRC, ORDER) \ } else { \ abort(); \ } /* On x86, for 'memory_order_seq_cst', if stores are locked, the corresponding * reads don't need to be locked (based on the following in Intel Developers * manual: * “Locked operations are atomic with respect to all other memory operations * and all externally visible events. Only instruction fetch and page table * accesses can pass locked instructions. Locked instructions can be used to * synchronize data written by one processor and read by another processor. * For the P6 family processors, locked operations serialize all outstanding * load and store operations (that is, wait for them to complete). This rule * is also true for the Pentium 4 and Intel Xeon processors, with one * exception. Load operations that reference weakly ordered memory types * (such as the WC memory type) may not be serialized."). */ /* For 8, 16 and 32 bit variations. */ #define atomic_readX(SRC, DST, ORDER) \ *(DST) = *(SRC); /* MSVC converts 64 bit reads into two instructions. So there is * a possibility that an interrupt can make a 64 bit read non-atomic even * when 8 byte aligned. So use fully memory barrier InterlockedOr64(). */ #define atomic_read64(SRC, DST, ORDER) \ __pragma (warning(push)) \ __pragma (warning(disable:4047)) \ *(DST) = InterlockedOr64((int64_t volatile *) (SRC), 0); \ __pragma (warning(pop)) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_read_explicit(SRC, DST, ORDER) \ if (sizeof *(DST) == 1 || sizeof *(DST) == 2 || sizeof *(DST) == 4) { \ atomic_readX(SRC, DST, ORDER) \ } else if (sizeof *(DST) == 8) { \ atomic_read64(SRC, DST, ORDER) \ } else { \ abort(); \ } /* For add, sub, and logical operations, for 8, 16 and 64 bit data types, * functions for all the different memory orders does not exist * (though documentation exists for some of them). The MSVC C++ library which * implements the c11 atomics simply calls the full memory barrier function * for everything in x86(see xatomic.h). So do the same here. */ /* For 8, 16 and 64 bit variations. */ #define atomic_op(OP, X, RMW, ARG, ORIG, ORDER) \ atomic_##OP##_generic(X, RMW, ARG, ORIG, ORDER) /* Arithmetic addition calls. */ #define atomic_add32(RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedExchangeAdd((int32_t volatile *) (RMW), \ (int32_t) (ARG)); /* For 8, 16 and 64 bit variations. */ #define atomic_add_generic(X, RMW, ARG, ORIG, ORDER) \ *(ORIG) = _InterlockedExchangeAdd##X((int##X##_t volatile *) (RMW), \ (int##X##_t) (ARG)); #define atomic_add(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ if (sizeof *(RMW) == 1) { \ atomic_op(add, 8, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 2) { \ atomic_op(add, 16, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 4) { \ atomic_add32(RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 8) { \ atomic_op(add, 64, RMW, ARG, ORIG, ORDER) \ } else { \ abort(); \ } /* Arithmetic subtraction calls. */ #define atomic_sub(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, (0 - (ARG)), ORIG, memory_order_seq_cst) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ atomic_add_explicit(RMW, (0 - (ARG)), ORIG, ORDER) /* Logical 'and' calls. */ #define atomic_and32(RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedAnd((int32_t volatile *) (RMW), (int32_t) (ARG)); /* For 8, 16 and 64 bit variations. */ #define atomic_and_generic(X, RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedAnd##X((int##X##_t volatile *) (RMW), \ (int##X##_t) (ARG)); #define atomic_and(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ if (sizeof *(RMW) == 1) { \ atomic_op(and, 8, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 2) { \ atomic_op(and, 16, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 4) { \ atomic_and32(RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 8) { \ atomic_op(and, 64, RMW, ARG, ORIG, ORDER) \ } else { \ abort(); \ } /* Logical 'Or' calls. */ #define atomic_or32(RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedOr((int32_t volatile *) (RMW), (int32_t) (ARG)); /* For 8, 16 and 64 bit variations. */ #define atomic_or_generic(X, RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedOr##X((int##X##_t volatile *) (RMW), \ (int##X##_t) (ARG)); #define atomic_or(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ if (sizeof *(RMW) == 1) { \ atomic_op(or, 8, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 2) { \ atomic_op(or, 16, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 4) { \ atomic_or32(RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 8) { \ atomic_op(or, 64, RMW, ARG, ORIG, ORDER) \ } else { \ abort(); \ } /* Logical Xor calls. */ #define atomic_xor32(RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedXor((int32_t volatile *) (RMW), (int32_t) (ARG)); /* For 8, 16 and 64 bit variations. */ #define atomic_xor_generic(X, RMW, ARG, ORIG, ORDER) \ *(ORIG) = InterlockedXor##X((int##X##_t volatile *) (RMW), \ (int##X##_t) (ARG)); #define atomic_xor(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ if (sizeof *(RMW) == 1) { \ atomic_op(xor, 8, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 2) { \ atomic_op(xor, 16, RMW, ARG, ORIG, ORDER) \ } else if (sizeof *(RMW) == 4) { \ atomic_xor32(RMW, ARG, ORIG, ORDER); \ } else if (sizeof *(RMW) == 8) { \ atomic_op(xor, 64, RMW, ARG, ORIG, ORDER) \ } else { \ abort(); \ } #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_weak atomic_compare_exchange_strong #define atomic_compare_exchange_weak_explicit \ atomic_compare_exchange_strong_explicit /* MSVCs c++ compiler implements c11 atomics and looking through its * implementation (in xatomic.h), orders are ignored for x86 platform. * Do the same here. */ static inline bool atomic_compare_exchange8(int8_t volatile *dst, int8_t *expected, int8_t src) { int8_t previous = _InterlockedCompareExchange8(dst, src, *expected); if (previous == *expected) { return true; } else { *expected = previous; return false; } } static inline bool atomic_compare_exchange16(int16_t volatile *dst, int16_t *expected, int16_t src) { int16_t previous = InterlockedCompareExchange16(dst, src, *expected); if (previous == *expected) { return true; } else { *expected = previous; return false; } } static inline bool atomic_compare_exchange32(int32_t volatile *dst, int32_t *expected, int32_t src) { int32_t previous = InterlockedCompareExchange(dst, src, *expected); if (previous == *expected) { return true; } else { *expected = previous; return false; } } static inline bool atomic_compare_exchange64(int64_t volatile *dst, int64_t *expected, int64_t src) { int64_t previous = InterlockedCompareExchange64(dst, src, *expected); if (previous == *expected) { return true; } else { *expected = previous; return false; } } static inline bool atomic_compare_unreachable() { return true; } #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \ (sizeof *(DST) == 1 \ ? atomic_compare_exchange8((int8_t volatile *) (DST), (int8_t *) (EXP), \ (int8_t) (SRC)) \ : (sizeof *(DST) == 2 \ ? atomic_compare_exchange16((int16_t volatile *) (DST), \ (int16_t *) (EXP), (int16_t) (SRC)) \ : (sizeof *(DST) == 4 \ ? atomic_compare_exchange32((int32_t volatile *) (DST), \ (int32_t *) (EXP), (int32_t) (SRC)) \ : (sizeof *(DST) == 8 \ ? atomic_compare_exchange64((int64_t volatile *) (DST), \ (int64_t *) (EXP), (int64_t) (SRC)) \ : ovs_fatal(0, "atomic operation with size greater than 8 bytes"), \ atomic_compare_unreachable())))) /* atomic_flag */ typedef ATOMIC(int32_t) atomic_flag; #define ATOMIC_FLAG_INIT 0 #define atomic_flag_test_and_set(FLAG) \ (bool) InterlockedBitTestAndSet(FLAG, 0) #define atomic_flag_test_and_set_explicit(FLAG, ORDER) \ atomic_flag_test_and_set(FLAG) #define atomic_flag_clear_explicit(FLAG, ORDER) \ atomic_flag_clear() #define atomic_flag_clear(FLAG) \ InterlockedBitTestAndReset(FLAG, 0) openvswitch-2.5.9/lib/PaxHeaders.82075/hmap.h0000644000000000000000000000013213534540071015510 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.737853101 openvswitch-2.5.9/lib/hmap.h0000644000175000017500000003216413534540071017204 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HMAP_H #define HMAP_H 1 #include #include #include "util.h" #ifdef __cplusplus extern "C" { #endif /* A hash map node, to be embedded inside the data structure being mapped. */ struct hmap_node { size_t hash; /* Hash value. */ struct hmap_node *next; /* Next in linked list. */ }; /* Returns the hash value embedded in 'node'. */ static inline size_t hmap_node_hash(const struct hmap_node *node) { return node->hash; } #define HMAP_NODE_NULL ((struct hmap_node *) 1) #define HMAP_NODE_NULL_INITIALIZER { 0, HMAP_NODE_NULL } /* Returns true if 'node' has been set to null by hmap_node_nullify() and has * not been un-nullified by being inserted into an hmap. */ static inline bool hmap_node_is_null(const struct hmap_node *node) { return node->next == HMAP_NODE_NULL; } /* Marks 'node' with a distinctive value that can be tested with * hmap_node_is_null(). */ static inline void hmap_node_nullify(struct hmap_node *node) { node->next = HMAP_NODE_NULL; } /* A hash map. */ struct hmap { struct hmap_node **buckets; /* Must point to 'one' iff 'mask' == 0. */ struct hmap_node *one; size_t mask; size_t n; }; /* Initializer for an empty hash map. */ #define HMAP_INITIALIZER(HMAP) \ { (struct hmap_node **const) &(HMAP)->one, NULL, 0, 0 } /* Initializer for an immutable struct hmap 'HMAP' that contains a single * 'NODE'. */ #define HMAP_CONST1(HMAP, NODE) { \ CONST_CAST(struct hmap_node **, &(HMAP)->one), NODE, 0, 1 } #define HMAP_NODE_INIT(HASH) { HASH, NULL } /* Initialization. */ void hmap_init(struct hmap *); void hmap_destroy(struct hmap *); void hmap_clear(struct hmap *); void hmap_swap(struct hmap *a, struct hmap *b); void hmap_moved(struct hmap *hmap); static inline size_t hmap_count(const struct hmap *); static inline bool hmap_is_empty(const struct hmap *); /* Adjusting capacity. */ void hmap_expand_at(struct hmap *, const char *where); #define hmap_expand(HMAP) hmap_expand_at(HMAP, OVS_SOURCE_LOCATOR) void hmap_shrink_at(struct hmap *, const char *where); #define hmap_shrink(HMAP) hmap_shrink_at(HMAP, OVS_SOURCE_LOCATOR) void hmap_reserve_at(struct hmap *, size_t capacity, const char *where); #define hmap_reserve(HMAP, CAPACITY) \ hmap_reserve_at(HMAP, CAPACITY, OVS_SOURCE_LOCATOR) /* Insertion and deletion. */ static inline void hmap_insert_at(struct hmap *, struct hmap_node *, size_t hash, const char *where); #define hmap_insert(HMAP, NODE, HASH) \ hmap_insert_at(HMAP, NODE, HASH, OVS_SOURCE_LOCATOR) static inline void hmap_insert_fast(struct hmap *, struct hmap_node *, size_t hash); static inline void hmap_remove(struct hmap *, struct hmap_node *); void hmap_node_moved(struct hmap *, struct hmap_node *, struct hmap_node *); static inline void hmap_replace(struct hmap *, const struct hmap_node *old, struct hmap_node *new_node); struct hmap_node *hmap_random_node(const struct hmap *); /* Search. * * HMAP_FOR_EACH_WITH_HASH iterates NODE over all of the nodes in HMAP that * have hash value equal to HASH. HMAP_FOR_EACH_IN_BUCKET iterates NODE over * all of the nodes in HMAP that would fall in the same bucket as HASH. MEMBER * must be the name of the 'struct hmap_node' member within NODE. * * These macros may be used interchangeably to search for a particular value in * an hmap, see, e.g. shash_find() for an example. Usually, using * HMAP_FOR_EACH_WITH_HASH provides an optimization, because comparing a hash * value is usually cheaper than comparing an entire hash map key. But for * simple hash map keys, it makes sense to use HMAP_FOR_EACH_IN_BUCKET because * it avoids doing two comparisons when a single simple comparison suffices. * * The loop should not change NODE to point to a different node or insert or * delete nodes in HMAP (unless it "break"s out of the loop to terminate * iteration). * * HASH is only evaluated once. * * When the loop terminates normally, meaning the iteration has completed * without using 'break', NODE will be NULL. This is true for all of the * HMAP_FOR_EACH_*() macros. */ #define HMAP_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HMAP) \ for (INIT_CONTAINER(NODE, hmap_first_with_hash(HMAP, HASH), MEMBER); \ (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) || (NODE = NULL); \ ASSIGN_CONTAINER(NODE, hmap_next_with_hash(&(NODE)->MEMBER), \ MEMBER)) #define HMAP_FOR_EACH_IN_BUCKET(NODE, MEMBER, HASH, HMAP) \ for (INIT_CONTAINER(NODE, hmap_first_in_bucket(HMAP, HASH), MEMBER); \ (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) || (NODE = NULL); \ ASSIGN_CONTAINER(NODE, hmap_next_in_bucket(&(NODE)->MEMBER), MEMBER)) static inline struct hmap_node *hmap_first_with_hash(const struct hmap *, size_t hash); static inline struct hmap_node *hmap_next_with_hash(const struct hmap_node *); static inline struct hmap_node *hmap_first_in_bucket(const struct hmap *, size_t hash); static inline struct hmap_node *hmap_next_in_bucket(const struct hmap_node *); bool hmap_contains(const struct hmap *, const struct hmap_node *); /* Iteration. */ /* Iterates through every node in HMAP. */ #define HMAP_FOR_EACH(NODE, MEMBER, HMAP) \ for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER); \ (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) || (NODE = NULL); \ ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER)) /* Safe when NODE may be freed (not needed when NODE may be removed from the * hash map but its members remain accessible and intact). */ #define HMAP_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HMAP) \ for (INIT_CONTAINER(NODE, hmap_first(HMAP), MEMBER); \ ((NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) || (NODE = NULL) \ ? INIT_CONTAINER(NEXT, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER), 1 \ : 0); \ (NODE) = (NEXT)) /* Continues an iteration from just after NODE. */ #define HMAP_FOR_EACH_CONTINUE(NODE, MEMBER, HMAP) \ for (ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER); \ (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER)) || (NODE = NULL); \ ASSIGN_CONTAINER(NODE, hmap_next(HMAP, &(NODE)->MEMBER), MEMBER)) static inline struct hmap_node *hmap_first(const struct hmap *); static inline struct hmap_node *hmap_next(const struct hmap *, const struct hmap_node *); struct hmap_node *hmap_at_position(const struct hmap *, uint32_t *bucket, uint32_t *offset); /* Returns the number of nodes currently in 'hmap'. */ static inline size_t hmap_count(const struct hmap *hmap) { return hmap->n; } /* Returns the maximum number of nodes that 'hmap' may hold before it should be * rehashed. */ static inline size_t hmap_capacity(const struct hmap *hmap) { return hmap->mask * 2 + 1; } /* Returns true if 'hmap' currently contains no nodes, * false otherwise. * Note: While hmap in general is not thread-safe without additional locking, * hmap_is_empty() is. */ static inline bool hmap_is_empty(const struct hmap *hmap) { return hmap->n == 0; } /* Inserts 'node', with the given 'hash', into 'hmap'. 'hmap' is never * expanded automatically. */ static inline void hmap_insert_fast(struct hmap *hmap, struct hmap_node *node, size_t hash) { struct hmap_node **bucket = &hmap->buckets[hash & hmap->mask]; node->hash = hash; node->next = *bucket; *bucket = node; hmap->n++; } /* Inserts 'node', with the given 'hash', into 'hmap', and expands 'hmap' if * necessary to optimize search performance. * * ('where' is used in debug logging. Commonly one would use hmap_insert() to * automatically provide the caller's source file and line number for * 'where'.) */ static inline void hmap_insert_at(struct hmap *hmap, struct hmap_node *node, size_t hash, const char *where) { hmap_insert_fast(hmap, node, hash); if (hmap->n / 2 > hmap->mask) { hmap_expand_at(hmap, where); } } /* Removes 'node' from 'hmap'. Does not shrink the hash table; call * hmap_shrink() directly if desired. */ static inline void hmap_remove(struct hmap *hmap, struct hmap_node *node) { struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask]; while (*bucket != node) { bucket = &(*bucket)->next; } *bucket = node->next; hmap->n--; } /* Puts 'new_node' in the position in 'hmap' currently occupied by 'old_node'. * The 'new_node' must hash to the same value as 'old_node'. The client is * responsible for ensuring that the replacement does not violate any * client-imposed invariants (e.g. uniqueness of keys within a map). * * Afterward, 'old_node' is not part of 'hmap', and the client is responsible * for freeing it (if this is desirable). */ static inline void hmap_replace(struct hmap *hmap, const struct hmap_node *old_node, struct hmap_node *new_node) { struct hmap_node **bucket = &hmap->buckets[old_node->hash & hmap->mask]; while (*bucket != old_node) { bucket = &(*bucket)->next; } *bucket = new_node; new_node->hash = old_node->hash; new_node->next = old_node->next; } static inline struct hmap_node * hmap_next_with_hash__(const struct hmap_node *node, size_t hash) { while (node != NULL && node->hash != hash) { node = node->next; } return CONST_CAST(struct hmap_node *, node); } /* Returns the first node in 'hmap' with the given 'hash', or a null pointer if * no nodes have that hash value. */ static inline struct hmap_node * hmap_first_with_hash(const struct hmap *hmap, size_t hash) { return hmap_next_with_hash__(hmap->buckets[hash & hmap->mask], hash); } /* Returns the first node in 'hmap' in the bucket in which the given 'hash' * would land, or a null pointer if that bucket is empty. */ static inline struct hmap_node * hmap_first_in_bucket(const struct hmap *hmap, size_t hash) { return hmap->buckets[hash & hmap->mask]; } /* Returns the next node in the same bucket as 'node', or a null pointer if * there are no more nodes in that bucket. * * If the hash map has been reallocated since 'node' was visited, some nodes * may be skipped; if new nodes with the same hash value have been added, they * will be skipped. (Removing 'node' from the hash map does not prevent * calling this function, since node->next is preserved, although freeing * 'node' of course does.) */ static inline struct hmap_node * hmap_next_in_bucket(const struct hmap_node *node) { return node->next; } /* Returns the next node in the same hash map as 'node' with the same hash * value, or a null pointer if no more nodes have that hash value. * * If the hash map has been reallocated since 'node' was visited, some nodes * may be skipped; if new nodes with the same hash value have been added, they * will be skipped. (Removing 'node' from the hash map does not prevent * calling this function, since node->next is preserved, although freeing * 'node' of course does.) */ static inline struct hmap_node * hmap_next_with_hash(const struct hmap_node *node) { return hmap_next_with_hash__(node->next, node->hash); } static inline struct hmap_node * hmap_next__(const struct hmap *hmap, size_t start) { size_t i; for (i = start; i <= hmap->mask; i++) { struct hmap_node *node = hmap->buckets[i]; if (node) { return node; } } return NULL; } /* Returns the first node in 'hmap', in arbitrary order, or a null pointer if * 'hmap' is empty. */ static inline struct hmap_node * hmap_first(const struct hmap *hmap) { return hmap_next__(hmap, 0); } /* Returns the next node in 'hmap' following 'node', in arbitrary order, or a * null pointer if 'node' is the last node in 'hmap'. * * If the hash map has been reallocated since 'node' was visited, some nodes * may be skipped or visited twice. (Removing 'node' from the hash map does * not prevent calling this function, since node->next is preserved, although * freeing 'node' of course does.) */ static inline struct hmap_node * hmap_next(const struct hmap *hmap, const struct hmap_node *node) { return (node->next ? node->next : hmap_next__(hmap, (node->hash & hmap->mask) + 1)); } #ifdef __cplusplus } #endif #endif /* hmap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/daemon.man0000644000000000000000000000013213534540071016352 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801423.725845641 openvswitch-2.5.9/lib/daemon.man0000644000175000017500000000604713534540071020047 0ustar00jpettitjpettit00000000000000The following options are valid on POSIX based platforms. .TP \fB\-\-pidfile\fR[\fB=\fIpidfile\fR] Causes a file (by default, \fB\*(PN.pid\fR) to be created indicating the PID of the running process. If the \fIpidfile\fR argument is not specified, or if it does not begin with \fB/\fR, then it is created in \fB@RUNDIR@\fR. .IP If \fB\-\-pidfile\fR is not specified, no pidfile is created. . .TP \fB\-\-overwrite\-pidfile\fR By default, when \fB\-\-pidfile\fR is specified and the specified pidfile already exists and is locked by a running process, \fB\*(PN\fR refuses to start. Specify \fB\-\-overwrite\-pidfile\fR to cause it to instead overwrite the pidfile. .IP When \fB\-\-pidfile\fR is not specified, this option has no effect. . .IP \fB\-\-detach\fR Runs \fB\*(PN\fR as a background process. The process forks, and in the child it starts a new session, closes the standard file descriptors (which has the side effect of disabling logging to the console), and changes its current directory to the root (unless \fB\-\-no\-chdir\fR is specified). After the child completes its initialization, the parent exits. \*(DD . .TP \fB\-\-monitor\fR Creates an additional process to monitor the \fB\*(PN\fR daemon. If the daemon dies due to a signal that indicates a programming error (\fBSIGABRT\fR, \fBSIGALRM\fR, \fBSIGBUS\fR, \fBSIGFPE\fR, \fBSIGILL\fR, \fBSIGPIPE\fR, \fBSIGSEGV\fR, \fBSIGXCPU\fR, or \fBSIGXFSZ\fR) then the monitor process starts a new copy of it. If the daemon dies or exits for another reason, the monitor process exits. .IP This option is normally used with \fB\-\-detach\fR, but it also functions without it. . .TP \fB\-\-no\-chdir\fR By default, when \fB\-\-detach\fR is specified, \fB\*(PN\fR changes its current working directory to the root directory after it detaches. Otherwise, invoking \fB\*(PN\fR from a carelessly chosen directory would prevent the administrator from unmounting the file system that holds that directory. .IP Specifying \fB\-\-no\-chdir\fR suppresses this behavior, preventing \fB\*(PN\fR from changing its current working directory. This may be useful for collecting core files, since it is common behavior to write core dumps into the current working directory and the root directory is not a good directory to use. .IP This option has no effect when \fB\-\-detach\fR is not specified. . .TP \fB\-\-user\fR Causes \fB\*(PN\fR to run as a different user specified in "user:group", thus dropping most of the root privileges. Short forms "user" and ":group" are also allowed, with current user or group are assumed respectively. Only daemons started by the root user accepts this argument. .IP On Linux, daemons will be granted CAP_IPC_LOCK and CAP_NET_BIND_SERVICES before dropping root privileges. Daemons interact with datapath, such as ovs-vswitchd, will be granted two additional capabilities, namely CAP_NET_ADMIN and CAP_NET_RAW. The capability change will apply even if new user is "root". .IP On Windows, this option is not currently supported. For security reasons, specifying this option will cause the daemon process not to start. openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-version.man0000644000000000000000000000013013534540071017354 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 28 ctime=1567801423.7338457 openvswitch-2.5.9/lib/ofp-version.man0000644000175000017500000000130613534540071021044 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .IP "\fB\-O \fR[\fIversion\fR[\fB,\fIversion\fR]...]\fR" .IQ "\fB\-\-protocols=\fR[\fIversion\fR[\fB,\fIversion\fR]...]\fR" Sets the OpenFlow protocol versions that are allowed when establishing an OpenFlow session. . .IP The following versions are considered to be ready for general use. These protocol versions are enabled by default: . .RS .IP \(bu \fBOpenFlow10\fR, for OpenFlow 1.0. .RE . .IP Support for the following protocol versions is provided for testing and development purposes. They are not enabled by default: . .RS .IP \(bu \fBOpenFlow11\fR, for OpenFlow 1.1. . .IP \(bu \fBOpenFlow12\fR, for OpenFlow 1.2. . .IP \(bu \fBOpenFlow13\fR, for OpenFlow 1.3. .RE openvswitch-2.5.9/lib/PaxHeaders.82075/dp-packet.c0000644000000000000000000000013213534540071016426 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.705852866 openvswitch-2.5.9/lib/dp-packet.c0000644000175000017500000004021313534540071020114 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "dynamic-string.h" #include "netdev-dpdk.h" #include "dp-packet.h" #include "util.h" static void dp_packet_init__(struct dp_packet *b, size_t allocated, enum dp_packet_source source) { dp_packet_set_allocated(b, allocated); b->source = source; dp_packet_reset_offsets(b); pkt_metadata_init(&b->md, 0); dp_packet_rss_invalidate(b); } static void dp_packet_use__(struct dp_packet *b, void *base, size_t allocated, enum dp_packet_source source) { dp_packet_set_base(b, base); dp_packet_set_data(b, base); dp_packet_set_size(b, 0); dp_packet_init__(b, allocated, source); } /* Initializes 'b' as an empty dp_packet that contains the 'allocated' bytes of * memory starting at 'base'. 'base' should be the first byte of a region * obtained from malloc(). It will be freed (with free()) if 'b' is resized or * freed. */ void dp_packet_use(struct dp_packet *b, void *base, size_t allocated) { dp_packet_use__(b, base, allocated, DPBUF_MALLOC); } /* Initializes 'b' as an empty dp_packet that contains the 'allocated' bytes of * memory starting at 'base'. 'base' should point to a buffer on the stack. * (Nothing actually relies on 'base' being allocated on the stack. It could * be static or malloc()'d memory. But stack space is the most common use * case.) * * 'base' should be appropriately aligned. Using an array of uint32_t or * uint64_t for the buffer is a reasonable way to ensure appropriate alignment * for 32- or 64-bit data. * * An dp_packet operation that requires reallocating data will copy the provided * buffer into a malloc()'d buffer. Thus, it is wise to call dp_packet_uninit() * on an dp_packet initialized by this function, so that if it expanded into the * heap, that memory is freed. */ void dp_packet_use_stub(struct dp_packet *b, void *base, size_t allocated) { dp_packet_use__(b, base, allocated, DPBUF_STUB); } /* Initializes 'b' as an dp_packet whose data starts at 'data' and continues for * 'size' bytes. This is appropriate for an dp_packet that will be used to * inspect existing data, without moving it around or reallocating it, and * generally without modifying it at all. * * An dp_packet operation that requires reallocating data will assert-fail if this * function was used to initialize it. */ void dp_packet_use_const(struct dp_packet *b, const void *data, size_t size) { dp_packet_use__(b, CONST_CAST(void *, data), size, DPBUF_STACK); dp_packet_set_size(b, size); } /* Initializes 'b' as an empty dp_packet that contains the 'allocated' bytes of * memory starting at 'base'. DPDK allocated dp_packet and *data is allocated * from one continous memory region, so in memory data start right after * dp_packet. Therefore there is special method to free this type of * buffer. dp_packet base, data and size are initialized by dpdk rcv() so no * need to initialize those fields. */ void dp_packet_init_dpdk(struct dp_packet *b, size_t allocated) { dp_packet_init__(b, allocated, DPBUF_DPDK); } /* Initializes 'b' as an empty dp_packet with an initial capacity of 'size' * bytes. */ void dp_packet_init(struct dp_packet *b, size_t size) { dp_packet_use(b, size ? xmalloc(size) : NULL, size); } /* Frees memory that 'b' points to. */ void dp_packet_uninit(struct dp_packet *b) { if (b) { if (b->source == DPBUF_MALLOC) { free(dp_packet_base(b)); } else if (b->source == DPBUF_DPDK) { #ifdef DPDK_NETDEV /* If this dp_packet was allocated by DPDK it must have been * created as a dp_packet */ free_dpdk_buf((struct dp_packet*) b); #endif } } } /* Creates and returns a new dp_packet with an initial capacity of 'size' * bytes. */ struct dp_packet * dp_packet_new(size_t size) { struct dp_packet *b = xmalloc(sizeof *b); dp_packet_init(b, size); return b; } /* Creates and returns a new dp_packet with an initial capacity of 'size + * headroom' bytes, reserving the first 'headroom' bytes as headroom. */ struct dp_packet * dp_packet_new_with_headroom(size_t size, size_t headroom) { struct dp_packet *b = dp_packet_new(size + headroom); dp_packet_reserve(b, headroom); return b; } /* Creates and returns a new dp_packet that initially contains a copy of the * 'dp_packet_size(buffer)' bytes of data starting at 'buffer->data' with no headroom or * tailroom. */ struct dp_packet * dp_packet_clone(const struct dp_packet *buffer) { return dp_packet_clone_with_headroom(buffer, 0); } /* Creates and returns a new dp_packet whose data are copied from 'buffer'. The * returned dp_packet will additionally have 'headroom' bytes of headroom. */ struct dp_packet * dp_packet_clone_with_headroom(const struct dp_packet *buffer, size_t headroom) { struct dp_packet *new_buffer; new_buffer = dp_packet_clone_data_with_headroom(dp_packet_data(buffer), dp_packet_size(buffer), headroom); new_buffer->l2_pad_size = buffer->l2_pad_size; new_buffer->l2_5_ofs = buffer->l2_5_ofs; new_buffer->l3_ofs = buffer->l3_ofs; new_buffer->l4_ofs = buffer->l4_ofs; new_buffer->md = buffer->md; #ifdef DPDK_NETDEV new_buffer->mbuf.ol_flags = buffer->mbuf.ol_flags; #else new_buffer->rss_hash_valid = buffer->rss_hash_valid; #endif if (dp_packet_rss_valid(new_buffer)) { #ifdef DPDK_NETDEV new_buffer->mbuf.hash.rss = buffer->mbuf.hash.rss; #else new_buffer->rss_hash = buffer->rss_hash; #endif } return new_buffer; } /* Creates and returns a new dp_packet that initially contains a copy of the * 'size' bytes of data starting at 'data' with no headroom or tailroom. */ struct dp_packet * dp_packet_clone_data(const void *data, size_t size) { return dp_packet_clone_data_with_headroom(data, size, 0); } /* Creates and returns a new dp_packet that initially contains 'headroom' bytes of * headroom followed by a copy of the 'size' bytes of data starting at * 'data'. */ struct dp_packet * dp_packet_clone_data_with_headroom(const void *data, size_t size, size_t headroom) { struct dp_packet *b = dp_packet_new_with_headroom(size, headroom); dp_packet_put(b, data, size); return b; } static void dp_packet_copy__(struct dp_packet *b, uint8_t *new_base, size_t new_headroom, size_t new_tailroom) { const uint8_t *old_base = dp_packet_base(b); size_t old_headroom = dp_packet_headroom(b); size_t old_tailroom = dp_packet_tailroom(b); size_t copy_headroom = MIN(old_headroom, new_headroom); size_t copy_tailroom = MIN(old_tailroom, new_tailroom); memcpy(&new_base[new_headroom - copy_headroom], &old_base[old_headroom - copy_headroom], copy_headroom + dp_packet_size(b) + copy_tailroom); } /* Reallocates 'b' so that it has exactly 'new_headroom' and 'new_tailroom' * bytes of headroom and tailroom, respectively. */ static void dp_packet_resize__(struct dp_packet *b, size_t new_headroom, size_t new_tailroom) { void *new_base, *new_data; size_t new_allocated; new_allocated = new_headroom + dp_packet_size(b) + new_tailroom; switch (b->source) { case DPBUF_DPDK: OVS_NOT_REACHED(); case DPBUF_MALLOC: if (new_headroom == dp_packet_headroom(b)) { new_base = xrealloc(dp_packet_base(b), new_allocated); } else { new_base = xmalloc(new_allocated); dp_packet_copy__(b, new_base, new_headroom, new_tailroom); free(dp_packet_base(b)); } break; case DPBUF_STACK: OVS_NOT_REACHED(); case DPBUF_STUB: b->source = DPBUF_MALLOC; new_base = xmalloc(new_allocated); dp_packet_copy__(b, new_base, new_headroom, new_tailroom); break; default: OVS_NOT_REACHED(); } dp_packet_set_allocated(b, new_allocated); dp_packet_set_base(b, new_base); new_data = (char *) new_base + new_headroom; if (dp_packet_data(b) != new_data) { dp_packet_set_data(b, new_data); } } /* Ensures that 'b' has room for at least 'size' bytes at its tail end, * reallocating and copying its data if necessary. Its headroom, if any, is * preserved. */ void dp_packet_prealloc_tailroom(struct dp_packet *b, size_t size) { if (size > dp_packet_tailroom(b)) { dp_packet_resize__(b, dp_packet_headroom(b), MAX(size, 64)); } } /* Ensures that 'b' has room for at least 'size' bytes at its head, * reallocating and copying its data if necessary. Its tailroom, if any, is * preserved. */ void dp_packet_prealloc_headroom(struct dp_packet *b, size_t size) { if (size > dp_packet_headroom(b)) { dp_packet_resize__(b, MAX(size, 64), dp_packet_tailroom(b)); } } /* Shifts all of the data within the allocated space in 'b' by 'delta' bytes. * For example, a 'delta' of 1 would cause each byte of data to move one byte * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would cause each * byte to move one byte backward (from 'p' to 'p-1'). */ void dp_packet_shift(struct dp_packet *b, int delta) { ovs_assert(delta > 0 ? delta <= dp_packet_tailroom(b) : delta < 0 ? -delta <= dp_packet_headroom(b) : true); if (delta != 0) { char *dst = (char *) dp_packet_data(b) + delta; memmove(dst, dp_packet_data(b), dp_packet_size(b)); dp_packet_set_data(b, dst); } } /* Appends 'size' bytes of data to the tail end of 'b', reallocating and * copying its data if necessary. Returns a pointer to the first byte of the * new data, which is left uninitialized. */ void * dp_packet_put_uninit(struct dp_packet *b, size_t size) { void *p; dp_packet_prealloc_tailroom(b, size); p = dp_packet_tail(b); dp_packet_set_size(b, dp_packet_size(b) + size); return p; } /* Appends 'size' zeroed bytes to the tail end of 'b'. Data in 'b' is * reallocated and copied if necessary. Returns a pointer to the first byte of * the data's location in the dp_packet. */ void * dp_packet_put_zeros(struct dp_packet *b, size_t size) { void *dst = dp_packet_put_uninit(b, size); memset(dst, 0, size); return dst; } /* Appends the 'size' bytes of data in 'p' to the tail end of 'b'. Data in 'b' * is reallocated and copied if necessary. Returns a pointer to the first * byte of the data's location in the dp_packet. */ void * dp_packet_put(struct dp_packet *b, const void *p, size_t size) { void *dst = dp_packet_put_uninit(b, size); memcpy(dst, p, size); return dst; } /* Parses as many pairs of hex digits as possible (possibly separated by * spaces) from the beginning of 's', appending bytes for their values to 'b'. * Returns the first character of 's' that is not the first of a pair of hex * digits. If 'n' is nonnull, stores the number of bytes added to 'b' in * '*n'. */ char * dp_packet_put_hex(struct dp_packet *b, const char *s, size_t *n) { size_t initial_size = dp_packet_size(b); for (;;) { uint8_t byte; bool ok; s += strspn(s, " \t\r\n"); byte = hexits_value(s, 2, &ok); if (!ok) { if (n) { *n = dp_packet_size(b) - initial_size; } return CONST_CAST(char *, s); } dp_packet_put(b, &byte, 1); s += 2; } } /* Reserves 'size' bytes of headroom so that they can be later allocated with * dp_packet_push_uninit() without reallocating the dp_packet. */ void dp_packet_reserve(struct dp_packet *b, size_t size) { ovs_assert(!dp_packet_size(b)); dp_packet_prealloc_tailroom(b, size); dp_packet_set_data(b, (char*)dp_packet_data(b) + size); } /* Reserves 'headroom' bytes at the head and 'tailroom' at the end so that * they can be later allocated with dp_packet_push_uninit() or * dp_packet_put_uninit() without reallocating the dp_packet. */ void dp_packet_reserve_with_tailroom(struct dp_packet *b, size_t headroom, size_t tailroom) { ovs_assert(!dp_packet_size(b)); dp_packet_prealloc_tailroom(b, headroom + tailroom); dp_packet_set_data(b, (char*)dp_packet_data(b) + headroom); } /* Prefixes 'size' bytes to the head end of 'b', reallocating and copying its * data if necessary. Returns a pointer to the first byte of the data's * location in the dp_packet. The new data is left uninitialized. */ void * dp_packet_push_uninit(struct dp_packet *b, size_t size) { dp_packet_prealloc_headroom(b, size); dp_packet_set_data(b, (char*)dp_packet_data(b) - size); dp_packet_set_size(b, dp_packet_size(b) + size); return dp_packet_data(b); } /* Prefixes 'size' zeroed bytes to the head end of 'b', reallocating and * copying its data if necessary. Returns a pointer to the first byte of the * data's location in the dp_packet. */ void * dp_packet_push_zeros(struct dp_packet *b, size_t size) { void *dst = dp_packet_push_uninit(b, size); memset(dst, 0, size); return dst; } /* Copies the 'size' bytes starting at 'p' to the head end of 'b', reallocating * and copying its data if necessary. Returns a pointer to the first byte of * the data's location in the dp_packet. */ void * dp_packet_push(struct dp_packet *b, const void *p, size_t size) { void *dst = dp_packet_push_uninit(b, size); memcpy(dst, p, size); return dst; } /* Returns the data in 'b' as a block of malloc()'d memory and frees the buffer * within 'b'. (If 'b' itself was dynamically allocated, e.g. with * dp_packet_new(), then it should still be freed with, e.g., dp_packet_delete().) */ void * dp_packet_steal_data(struct dp_packet *b) { void *p; ovs_assert(b->source != DPBUF_DPDK); if (b->source == DPBUF_MALLOC && dp_packet_data(b) == dp_packet_base(b)) { p = dp_packet_data(b); } else { p = xmemdup(dp_packet_data(b), dp_packet_size(b)); if (b->source == DPBUF_MALLOC) { free(dp_packet_base(b)); } } dp_packet_set_base(b, NULL); dp_packet_set_data(b, NULL); return p; } /* Returns a string that describes some of 'b''s metadata plus a hex dump of up * to 'maxbytes' from the start of the buffer. */ char * dp_packet_to_string(const struct dp_packet *b, size_t maxbytes) { struct ds s; ds_init(&s); ds_put_format(&s, "size=%"PRIu32", allocated=%"PRIu32", head=%"PRIuSIZE", tail=%"PRIuSIZE"\n", dp_packet_size(b), dp_packet_get_allocated(b), dp_packet_headroom(b), dp_packet_tailroom(b)); ds_put_hex_dump(&s, dp_packet_data(b), MIN(dp_packet_size(b), maxbytes), 0, false); return ds_cstr(&s); } static inline void dp_packet_adjust_layer_offset(uint16_t *offset, int increment) { if (*offset != UINT16_MAX) { *offset += increment; } } /* Adjust the size of the l2_5 portion of the dp_packet, updating the l2 * pointer and the layer offsets. The caller is responsible for * modifying the contents. */ void * dp_packet_resize_l2_5(struct dp_packet *b, int increment) { if (increment >= 0) { dp_packet_push_uninit(b, increment); } else { dp_packet_pull(b, -increment); } /* Adjust layer offsets after l2_5. */ dp_packet_adjust_layer_offset(&b->l3_ofs, increment); dp_packet_adjust_layer_offset(&b->l4_ofs, increment); return dp_packet_data(b); } /* Adjust the size of the l2 portion of the dp_packet, updating the l2 * pointer and the layer offsets. The caller is responsible for * modifying the contents. */ void * dp_packet_resize_l2(struct dp_packet *b, int increment) { dp_packet_resize_l2_5(b, increment); dp_packet_adjust_layer_offset(&b->l2_5_ofs, increment); return dp_packet_data(b); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-idl.h0000644000000000000000000000013213534540071016446 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.837853839 openvswitch-2.5.9/lib/ovsdb-idl.h0000644000175000017500000002772013534540071020144 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_IDL_H #define OVSDB_IDL_H 1 /* Open vSwitch Database Interface Definition Language (OVSDB IDL). * * The OVSDB IDL maintains an in-memory replica of a database. It issues RPC * requests to an OVSDB database server and parses the responses, converting * raw JSON into data structures that are easier for clients to digest. Most * notably, references to rows via UUID become C pointers. * * The IDL always presents a consistent snapshot of the database to its client, * that is, it won't present the effects of some part of a transaction applied * at the database server without presenting all of its effects. * * The IDL also assists with issuing database transactions. The client creates * a transaction, manipulates the IDL data structures, and commits or aborts * the transaction. The IDL then composes and issues the necessary JSON-RPC * requests and reports to the client whether the transaction completed * successfully. */ #include #include #include "compiler.h" #include "ovsdb-types.h" struct json; struct ovsdb_datum; struct ovsdb_idl_class; struct ovsdb_idl_row; struct ovsdb_idl_column; struct ovsdb_idl_table_class; struct uuid; struct ovsdb_idl *ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *, bool monitor_everything_by_default, bool retry); void ovsdb_idl_destroy(struct ovsdb_idl *); void ovsdb_idl_run(struct ovsdb_idl *); void ovsdb_idl_wait(struct ovsdb_idl *); void ovsdb_idl_set_lock(struct ovsdb_idl *, const char *lock_name); bool ovsdb_idl_has_lock(const struct ovsdb_idl *); bool ovsdb_idl_is_lock_contended(const struct ovsdb_idl *); unsigned int ovsdb_idl_get_seqno(const struct ovsdb_idl *); bool ovsdb_idl_has_ever_connected(const struct ovsdb_idl *); void ovsdb_idl_enable_reconnect(struct ovsdb_idl *); void ovsdb_idl_force_reconnect(struct ovsdb_idl *); void ovsdb_idl_verify_write_only(struct ovsdb_idl *); bool ovsdb_idl_is_alive(const struct ovsdb_idl *); int ovsdb_idl_get_last_error(const struct ovsdb_idl *); /* Choosing columns and tables to replicate. */ /* Modes with which the IDL can monitor a column. * * If no bits are set, the column is not monitored at all. Its value will * always appear to the client to be the default value for its type. * * If OVSDB_IDL_MONITOR is set, then the column is replicated. Its value will * reflect the value in the database. If OVSDB_IDL_ALERT is also set, then the * value returned by ovsdb_idl_get_seqno() will change when the column's value * changes. * * The possible mode combinations are: * * - 0, for a column that a client doesn't care about. * * - (OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT), for a column that a client wants * to track and possibly update. * * - OVSDB_IDL_MONITOR, for columns that a client treats as "write-only", * that is, it updates them but doesn't want to get alerted about its own * updates. It also won't be alerted about other clients' updates, so this * is suitable only for use by a client that "owns" a particular column. * * - OVDSB_IDL_ALERT without OVSDB_IDL_MONITOR is not valid. * * - (OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT | OVSDB_IDL_TRACK), for a column * that a client wants to track using the change tracking * ovsdb_idl_track_get_*() functions. */ #define OVSDB_IDL_MONITOR (1 << 0) /* Monitor this column? */ #define OVSDB_IDL_ALERT (1 << 1) /* Alert client when column updated? */ #define OVSDB_IDL_TRACK (1 << 2) void ovsdb_idl_add_column(struct ovsdb_idl *, const struct ovsdb_idl_column *); void ovsdb_idl_add_table(struct ovsdb_idl *, const struct ovsdb_idl_table_class *); void ovsdb_idl_omit(struct ovsdb_idl *, const struct ovsdb_idl_column *); void ovsdb_idl_omit_alert(struct ovsdb_idl *, const struct ovsdb_idl_column *); /* Change tracking. */ enum ovsdb_idl_change { OVSDB_IDL_CHANGE_INSERT, OVSDB_IDL_CHANGE_MODIFY, OVSDB_IDL_CHANGE_DELETE, OVSDB_IDL_CHANGE_MAX }; /* Row, table sequence numbers */ unsigned int ovsdb_idl_table_get_seqno( const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *table_class); unsigned int ovsdb_idl_row_get_seqno( const struct ovsdb_idl_row *row, enum ovsdb_idl_change change); void ovsdb_idl_track_add_column(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column); void ovsdb_idl_track_add_all(struct ovsdb_idl *idl); const struct ovsdb_idl_row *ovsdb_idl_track_get_first( const struct ovsdb_idl *, const struct ovsdb_idl_table_class *); const struct ovsdb_idl_row *ovsdb_idl_track_get_next(const struct ovsdb_idl_row *); void ovsdb_idl_track_clear(const struct ovsdb_idl *); /* Reading the database replica. */ const struct ovsdb_idl_row *ovsdb_idl_get_row_for_uuid( const struct ovsdb_idl *, const struct ovsdb_idl_table_class *, const struct uuid *); const struct ovsdb_idl_row *ovsdb_idl_first_row( const struct ovsdb_idl *, const struct ovsdb_idl_table_class *); const struct ovsdb_idl_row *ovsdb_idl_next_row(const struct ovsdb_idl_row *); const struct ovsdb_datum *ovsdb_idl_read(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *); const struct ovsdb_datum *ovsdb_idl_get(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *, enum ovsdb_atomic_type key_type, enum ovsdb_atomic_type value_type); bool ovsdb_idl_is_mutable(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *); bool ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *); /* Transactions. * * A transaction may modify the contents of a database by modifying the values * of columns, deleting rows, inserting rows, or adding checks that columns in * the database have not changed ("verify" operations), through * ovsdb_idl_txn_*() functions. (The OVSDB IDL code generator produces helper * functions that internally call the ovsdb_idl_txn_*() functions. These are * likely to be more convenient.) * * Reading and writing columns and inserting and deleting rows are all * straightforward. The reasons to verify columns are less obvious. * Verification is the key to maintaining transactional integrity. Because * OVSDB handles multiple clients, it can happen that between the time that * OVSDB client A reads a column and writes a new value, OVSDB client B has * written that column. Client A's write should not ordinarily overwrite * client B's, especially if the column in question is a "map" column that * contains several more or less independent data items. If client A adds a * "verify" operation before it writes the column, then the transaction fails * in case client B modifies it first. Client A will then see the new value of * the column and compose a new transaction based on the new contents written * by client B. * * When a transaction is complete, which must be before the next call to * ovsdb_idl_run() on 'idl', call ovsdb_idl_txn_commit() or * ovsdb_idl_txn_abort(). * * The life-cycle of a transaction looks like this: * * 1. Create the transaction and record the initial sequence number: * * seqno = ovsdb_idl_get_seqno(idl); * txn = ovsdb_idl_txn_create(idl); * * 2. Modify the database with ovsdb_idl_txn_*() functions directly or * indirectly. * * 3. Commit the transaction by calling ovsdb_idl_txn_commit(). The first call * to this function probably returns TXN_INCOMPLETE. The client must keep * calling again along as this remains true, calling ovsdb_idl_run() in * between to let the IDL do protocol processing. (If the client doesn't * have anything else to do in the meantime, it can use * ovsdb_idl_txn_commit_block() to avoid having to loop itself.) * * 4. If the final status is TXN_TRY_AGAIN, wait for ovsdb_idl_get_seqno() to * change from the saved 'seqno' (it's possible that it's already changed, * in which case the client should not wait at all), then start over from * step 1. Only a call to ovsdb_idl_run() will change the return value of * ovsdb_idl_get_seqno(). (ovsdb_idl_txn_commit_block() calls * ovsdb_idl_run().) */ enum ovsdb_idl_txn_status { TXN_UNCOMMITTED, /* Not yet committed or aborted. */ TXN_UNCHANGED, /* Transaction didn't include any changes. */ TXN_INCOMPLETE, /* Commit in progress, please wait. */ TXN_ABORTED, /* ovsdb_idl_txn_abort() called. */ TXN_SUCCESS, /* Commit successful. */ TXN_TRY_AGAIN, /* Commit failed because a "verify" operation * reported an inconsistency, due to a network * problem, or other transient failure. Wait * for a change, then try again. */ TXN_NOT_LOCKED, /* Server hasn't given us the lock yet. */ TXN_ERROR /* Commit failed due to a hard error. */ }; const char *ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status); struct ovsdb_idl_txn *ovsdb_idl_txn_create(struct ovsdb_idl *); void ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *, const char *, ...) OVS_PRINTF_FORMAT (2, 3); void ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *); void ovsdb_idl_txn_increment(struct ovsdb_idl_txn *, const struct ovsdb_idl_row *, const struct ovsdb_idl_column *); void ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *); void ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *); enum ovsdb_idl_txn_status ovsdb_idl_txn_commit(struct ovsdb_idl_txn *); enum ovsdb_idl_txn_status ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *); void ovsdb_idl_txn_abort(struct ovsdb_idl_txn *); const char *ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *); int64_t ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *); const struct uuid *ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *, const struct uuid *); void ovsdb_idl_txn_write(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *, struct ovsdb_datum *); void ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *, const struct ovsdb_datum *); void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *); const struct ovsdb_idl_row *ovsdb_idl_txn_insert( struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *, const struct uuid *); struct ovsdb_idl *ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *); void ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *); /* ovsdb_idl_loop provides an easy way to manage the transactions related * to 'idl' and to cope with different status during transaction. */ struct ovsdb_idl_loop { struct ovsdb_idl *idl; unsigned int skip_seqno; struct ovsdb_idl_txn *committing_txn; unsigned int precommit_seqno; struct ovsdb_idl_txn *open_txn; }; #define OVSDB_IDL_LOOP_INITIALIZER(IDL) { .idl = (IDL) } void ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *); struct ovsdb_idl_txn *ovsdb_idl_loop_run(struct ovsdb_idl_loop *); void ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *); #endif /* ovsdb-idl.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/shash.h0000644000000000000000000000013113534540071015670 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.877854134 openvswitch-2.5.9/lib/shash.h0000644000175000017500000000477613534540071017375 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SHASH_H #define SHASH_H 1 #include "hmap.h" #ifdef __cplusplus extern "C" { #endif struct shash_node { struct hmap_node node; char *name; void *data; }; struct shash { struct hmap map; }; #define SHASH_INITIALIZER(SHASH) { HMAP_INITIALIZER(&(SHASH)->map) } #define SHASH_FOR_EACH(SHASH_NODE, SHASH) \ HMAP_FOR_EACH (SHASH_NODE, node, &(SHASH)->map) #define SHASH_FOR_EACH_SAFE(SHASH_NODE, NEXT, SHASH) \ HMAP_FOR_EACH_SAFE (SHASH_NODE, NEXT, node, &(SHASH)->map) void shash_init(struct shash *); void shash_destroy(struct shash *); void shash_destroy_free_data(struct shash *); void shash_swap(struct shash *, struct shash *); void shash_moved(struct shash *); void shash_clear(struct shash *); void shash_clear_free_data(struct shash *); bool shash_is_empty(const struct shash *); size_t shash_count(const struct shash *); struct shash_node *shash_add(struct shash *, const char *, const void *); struct shash_node *shash_add_nocopy(struct shash *, char *, const void *); bool shash_add_once(struct shash *, const char *, const void *); void shash_add_assert(struct shash *, const char *, const void *); void *shash_replace(struct shash *, const char *, const void *data); void shash_delete(struct shash *, struct shash_node *); char *shash_steal(struct shash *, struct shash_node *); struct shash_node *shash_find(const struct shash *, const char *); struct shash_node *shash_find_len(const struct shash *, const char *, size_t); void *shash_find_data(const struct shash *, const char *); void *shash_find_and_delete(struct shash *, const char *); void *shash_find_and_delete_assert(struct shash *, const char *); struct shash_node *shash_first(const struct shash *); const struct shash_node **shash_sort(const struct shash *); bool shash_equal_keys(const struct shash *, const struct shash *); struct shash_node *shash_random_node(struct shash *); #ifdef __cplusplus } #endif #endif /* shash.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/unicode.c0000644000000000000000000000013213534540071016204 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.925854488 openvswitch-2.5.9/lib/unicode.c0000644000175000017500000001064613534540071017701 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "unicode.h" #include #include "dynamic-string.h" #include "util.h" /* Returns the unicode code point corresponding to leading surrogate 'leading' * and trailing surrogate 'trailing'. The return value will not make any * sense if 'leading' or 'trailing' are not in the correct ranges for leading * or trailing surrogates. */ int utf16_decode_surrogate_pair(int leading, int trailing) { /* * Leading surrogate: 110110wwwwxxxxxx * Trailing surrogate: 110111xxxxxxxxxx * Code point: 000uuuuuxxxxxxxxxxxxxxxx */ int w = (leading >> 6) & 0xf; int u = w + 1; int x0 = leading & 0x3f; int x1 = trailing & 0x3ff; return (u << 16) | (x0 << 10) | x1; } /* Returns the number of Unicode characters in UTF-8 string 's'. */ size_t utf8_length(const char *s_) { const uint8_t *s; size_t length; length = 0; for (s = (const uint8_t *) s_; *s != '\0'; s++) { /* The most-significant bits of the first byte in a character are one * of 2#01, 2#00, or 2#11. 2#10 is a continuation byte. */ length += (*s & 0xc0) != 0x80; } return length; } static char * invalid_utf8_sequence(const uint8_t *s, int n, size_t *lengthp) { struct ds msg; int i; if (lengthp) { *lengthp = 0; } ds_init(&msg); ds_put_cstr(&msg, "invalid UTF-8 sequence"); for (i = 0; i < n; i++) { ds_put_format(&msg, " 0x%02"PRIx8, s[i]); } return ds_steal_cstr(&msg); } struct utf8_sequence { uint8_t octets[5][2]; }; static const struct utf8_sequence * lookup_utf8_sequence(uint8_t c) { static const struct utf8_sequence seqs[] = { { { { 0x01, 0x7f }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, { { { 0xc2, 0xdf }, { 0x80, 0xbf }, { 0, 0 }, { 0, 0 }, { 0, 0 } } }, { { { 0xe0, 0xe0 }, { 0xa0, 0xbf }, { 0x80, 0xbf }, {0,0}, {0, 0 } } }, { { { 0xe1, 0xec }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 }, { 0, 0 } } }, { { { 0xed, 0xed }, { 0x80, 0x9f }, { 0x80, 0xbf }, { 0, 0 }, { 0, 0 } } }, { { { 0xee, 0xef }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 }, { 0, 0 } } }, { { { 0xf0, 0xf0 }, { 0x90, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } } }, { { { 0xf1, 0xf3 }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } } }, { { { 0xf4, 0xf4 }, { 0x80, 0x8f }, { 0x80, 0xbf }, { 0x80, 0xbf }, { 0, 0 } } }, }; size_t i; for (i = 0; i < ARRAY_SIZE(seqs); i++) { const uint8_t *o = seqs[i].octets[0]; if (c >= o[0] && c <= o[1]) { return &seqs[i]; } } return NULL; } /* Checks that 's' is a valid, null-terminated UTF-8 string. If so, returns a * null pointer and sets '*lengthp' to the number of Unicode characters in * 's'. If not, returns an error message that the caller must free and sets * '*lengthp' to 0. * * 'lengthp' may be NULL if the length is not needed. */ char * utf8_validate(const char *s_, size_t *lengthp) { size_t length = 0; const uint8_t *s; for (s = (const uint8_t *) s_; *s != '\0'; ) { length++; if (s[0] < 0x80) { s++; } else { const struct utf8_sequence *seq; int i; seq = lookup_utf8_sequence(s[0]); if (!seq) { return invalid_utf8_sequence(s, 1, lengthp); } for (i = 1; seq->octets[i][0]; i++) { const uint8_t *o = seq->octets[i]; if (s[i] < o[0] || s[i] > o[1]) { return invalid_utf8_sequence(s, i + 1, lengthp); } } s += i; } } if (lengthp) { *lengthp = length; } return NULL; } openvswitch-2.5.9/lib/PaxHeaders.82075/async-append-aio.c0000644000000000000000000000013213534540071017706 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.997855018 openvswitch-2.5.9/lib/async-append-aio.c0000644000175000017500000000773413534540071021407 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /* This implementation of the async-append.h interface uses the POSIX * asynchronous I/O interface. */ #include "async-append.h" #include #include #include #include #include "byteq.h" #include "ovs-thread.h" #include "util.h" /* Maximum number of bytes of buffered data. */ enum { BUFFER_SIZE = 65536 }; /* Maximum number of aiocbs to use. * * aiocbs are big (144 bytes with glibc 2.11 on i386) so we try to allow for a * reasonable number by basing the number we allocate on the amount of buffer * space. */ enum { MAX_CBS = ROUND_DOWN_POW2(BUFFER_SIZE / sizeof(struct aiocb)) }; BUILD_ASSERT_DECL(IS_POW2(MAX_CBS)); struct async_append { int fd; struct aiocb *aiocbs; unsigned int aiocb_head, aiocb_tail; uint8_t *buffer; struct byteq byteq; }; struct async_append * async_append_create(int fd) { struct async_append *ap; ap = xmalloc(sizeof *ap); ap->fd = fd; ap->aiocbs = xmalloc(MAX_CBS * sizeof *ap->aiocbs); ap->aiocb_head = ap->aiocb_tail = 0; ap->buffer = xmalloc(BUFFER_SIZE); byteq_init(&ap->byteq, ap->buffer, BUFFER_SIZE); return ap; } void async_append_destroy(struct async_append *ap) { if (ap) { async_append_flush(ap); free(ap->aiocbs); free(ap->buffer); free(ap); } } static bool async_append_is_full(const struct async_append *ap) { return (ap->aiocb_head - ap->aiocb_tail >= MAX_CBS || byteq_is_full(&ap->byteq)); } static bool async_append_is_empty(const struct async_append *ap) { return byteq_is_empty(&ap->byteq); } static void async_append_wait(struct async_append *ap) { int n = 0; while (!async_append_is_empty(ap)) { struct aiocb *aiocb = &ap->aiocbs[ap->aiocb_tail & (MAX_CBS - 1)]; int error = aio_error(aiocb); if (error == EINPROGRESS) { const struct aiocb *p = aiocb; if (n > 0) { return; } aio_suspend(&p, 1, NULL); } else { ignore(aio_return(aiocb)); ap->aiocb_tail++; byteq_advance_tail(&ap->byteq, aiocb->aio_nbytes); n++; } } } void async_append_write(struct async_append *ap, const void *data_, size_t size) { const uint8_t *data = data_; while (size > 0) { struct aiocb *aiocb; size_t chunk_size; void *chunk; while (async_append_is_full(ap)) { async_append_wait(ap); } chunk = byteq_head(&ap->byteq); chunk_size = byteq_headroom(&ap->byteq); if (chunk_size > size) { chunk_size = size; } memcpy(chunk, data, chunk_size); aiocb = &ap->aiocbs[ap->aiocb_head & (MAX_CBS - 1)]; memset(aiocb, 0, sizeof *aiocb); aiocb->aio_fildes = ap->fd; aiocb->aio_offset = 0; aiocb->aio_buf = chunk; aiocb->aio_nbytes = chunk_size; aiocb->aio_sigevent.sigev_notify = SIGEV_NONE; if (aio_write(aiocb) == -1) { async_append_flush(ap); ignore(write(ap->fd, data, size)); return; } data += chunk_size; size -= chunk_size; byteq_advance_head(&ap->byteq, chunk_size); ap->aiocb_head++; } } void async_append_flush(struct async_append *ap) { while (!async_append_is_empty(ap)) { async_append_wait(ap); } } openvswitch-2.5.9/lib/PaxHeaders.82075/route-table-stub.c0000644000000000000000000000013213534540071017754 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.961854753 openvswitch-2.5.9/lib/route-table-stub.c0000644000175000017500000000207413534540071021445 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "compiler.h" #include "ovs-router.h" #include "route-table.h" bool route_table_fallback_lookup(ovs_be32 ip_dst OVS_UNUSED, char output_bridge[] OVS_UNUSED, ovs_be32 *gw) { *gw = 0; return false; } uint64_t route_table_get_change_seq(void) { return 0; } void route_table_init(void) { ovs_router_init(); } void route_table_run(void) { } void route_table_wait(void) { } openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-data.c0000644000000000000000000000013213534540071016602 xustar0030 mtime=1567801401.549682257 30 atime=1567801402.093686252 30 ctime=1567801424.833853809 openvswitch-2.5.9/lib/ovsdb-data.c0000644000175000017500000016346313534540071020305 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb-data.h" #include #include #include #include #include "dynamic-string.h" #include "hash.h" #include "ovs-thread.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" #include "json.h" #include "shash.h" #include "smap.h" #include "sort.h" #include "unicode.h" static struct json * wrap_json(const char *name, struct json *wrapped) { return json_array_create_2(json_string_create(name), wrapped); } /* Initializes 'atom' with the default value of the given 'type'. * * The default value for an atom is as defined in RFC 7047: * * - "integer" or "real": 0 * * - "boolean": false * * - "string": "" (the empty string) * * - "uuid": 00000000-0000-0000-0000-000000000000 * * The caller must eventually arrange for 'atom' to be destroyed (with * ovsdb_atom_destroy()). */ void ovsdb_atom_init_default(union ovsdb_atom *atom, enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: atom->integer = 0; break; case OVSDB_TYPE_REAL: atom->real = 0.0; break; case OVSDB_TYPE_BOOLEAN: atom->boolean = false; break; case OVSDB_TYPE_STRING: atom->string = xmemdup("", 1); break; case OVSDB_TYPE_UUID: uuid_zero(&atom->uuid); break; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } /* Returns a read-only atom of the given 'type' that has the default value for * 'type'. The caller must not modify or free the returned atom. * * See ovsdb_atom_init_default() for an explanation of the default value of an * atom. */ const union ovsdb_atom * ovsdb_atom_default(enum ovsdb_atomic_type type) { static union ovsdb_atom default_atoms[OVSDB_N_TYPES]; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { int i; for (i = 0; i < OVSDB_N_TYPES; i++) { if (i != OVSDB_TYPE_VOID) { ovsdb_atom_init_default(&default_atoms[i], i); } } ovsthread_once_done(&once); } ovs_assert(ovsdb_atomic_type_is_valid(type)); return &default_atoms[type]; } /* Returns true if 'atom', which must have the given 'type', has the default * value for that type. * * See ovsdb_atom_init_default() for an explanation of the default value of an * atom. */ bool ovsdb_atom_is_default(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: return atom->integer == 0; case OVSDB_TYPE_REAL: return atom->real == 0.0; case OVSDB_TYPE_BOOLEAN: return atom->boolean == false; case OVSDB_TYPE_STRING: return atom->string[0] == '\0'; case OVSDB_TYPE_UUID: return uuid_is_zero(&atom->uuid); case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } /* Initializes 'new' as a copy of 'old', with the given 'type'. * * The caller must eventually arrange for 'new' to be destroyed (with * ovsdb_atom_destroy()). */ void ovsdb_atom_clone(union ovsdb_atom *new, const union ovsdb_atom *old, enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: new->integer = old->integer; break; case OVSDB_TYPE_REAL: new->real = old->real; break; case OVSDB_TYPE_BOOLEAN: new->boolean = old->boolean; break; case OVSDB_TYPE_STRING: new->string = xstrdup(old->string); break; case OVSDB_TYPE_UUID: new->uuid = old->uuid; break; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } /* Swaps the contents of 'a' and 'b', which need not have the same type. */ void ovsdb_atom_swap(union ovsdb_atom *a, union ovsdb_atom *b) { union ovsdb_atom tmp = *a; *a = *b; *b = tmp; } /* Returns a hash value for 'atom', which has the specified 'type', folding * 'basis' into the calculation. */ uint32_t ovsdb_atom_hash(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, uint32_t basis) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: return hash_int(atom->integer, basis); case OVSDB_TYPE_REAL: return hash_double(atom->real, basis); case OVSDB_TYPE_BOOLEAN: return hash_boolean(atom->boolean, basis); case OVSDB_TYPE_STRING: return hash_string(atom->string, basis); case OVSDB_TYPE_UUID: return hash_int(uuid_hash(&atom->uuid), basis); case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } /* Compares 'a' and 'b', which both have type 'type', and returns a * strcmp()-like result. */ int ovsdb_atom_compare_3way(const union ovsdb_atom *a, const union ovsdb_atom *b, enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: return a->integer < b->integer ? -1 : a->integer > b->integer; case OVSDB_TYPE_REAL: return a->real < b->real ? -1 : a->real > b->real; case OVSDB_TYPE_BOOLEAN: return a->boolean - b->boolean; case OVSDB_TYPE_STRING: return strcmp(a->string, b->string); case OVSDB_TYPE_UUID: return uuid_compare_3way(&a->uuid, &b->uuid); case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } static struct ovsdb_error * unwrap_json(const struct json *json, const char *name, enum json_type value_type, const struct json **value) { if (json->type != JSON_ARRAY || json->u.array.n != 2 || json->u.array.elems[0]->type != JSON_STRING || (name && strcmp(json->u.array.elems[0]->u.string, name)) || json->u.array.elems[1]->type != value_type) { *value = NULL; return ovsdb_syntax_error(json, NULL, "expected [\"%s\", <%s>]", name, json_type_to_string(value_type)); } *value = json->u.array.elems[1]; return NULL; } static struct ovsdb_error * parse_json_pair(const struct json *json, const struct json **elem0, const struct json **elem1) { if (json->type != JSON_ARRAY || json->u.array.n != 2) { return ovsdb_syntax_error(json, NULL, "expected 2-element array"); } *elem0 = json->u.array.elems[0]; *elem1 = json->u.array.elems[1]; return NULL; } static void ovsdb_symbol_referenced(struct ovsdb_symbol *symbol, const struct ovsdb_base_type *base) { ovs_assert(base->type == OVSDB_TYPE_UUID); if (base->u.uuid.refTableName) { switch (base->u.uuid.refType) { case OVSDB_REF_STRONG: symbol->strong_ref = true; break; case OVSDB_REF_WEAK: symbol->weak_ref = true; break; } } } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json, struct ovsdb_symbol_table *symtab, const struct ovsdb_base_type *base) { struct ovsdb_error *error0; const struct json *value; error0 = unwrap_json(json, "uuid", JSON_STRING, &value); if (!error0) { const char *uuid_string = json_string(value); if (!uuid_from_string(uuid, uuid_string)) { return ovsdb_syntax_error(json, NULL, "\"%s\" is not a valid UUID", uuid_string); } } else if (symtab) { struct ovsdb_error *error1; error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value); if (!error1) { struct ovsdb_symbol *symbol; ovsdb_error_destroy(error0); if (!ovsdb_parser_is_id(json_string(value))) { return ovsdb_syntax_error(json, NULL, "named-uuid string is " "not a valid "); } symbol = ovsdb_symbol_table_insert(symtab, json_string(value)); *uuid = symbol->uuid; ovsdb_symbol_referenced(symbol, base); return NULL; } ovsdb_error_destroy(error1); } return error0; } static struct ovsdb_error * OVS_WARN_UNUSED_RESULT ovsdb_atom_from_json__(union ovsdb_atom *atom, const struct ovsdb_base_type *base, const struct json *json, struct ovsdb_symbol_table *symtab) { enum ovsdb_atomic_type type = base->type; switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: if (json->type == JSON_INTEGER) { atom->integer = json->u.integer; return NULL; } break; case OVSDB_TYPE_REAL: if (json->type == JSON_INTEGER) { atom->real = json->u.integer; return NULL; } else if (json->type == JSON_REAL) { atom->real = json->u.real; return NULL; } break; case OVSDB_TYPE_BOOLEAN: if (json->type == JSON_TRUE) { atom->boolean = true; return NULL; } else if (json->type == JSON_FALSE) { atom->boolean = false; return NULL; } break; case OVSDB_TYPE_STRING: if (json->type == JSON_STRING) { atom->string = xstrdup(json->u.string); return NULL; } break; case OVSDB_TYPE_UUID: return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab, base); case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } return ovsdb_syntax_error(json, NULL, "expected %s", ovsdb_atomic_type_to_string(type)); } /* Parses 'json' as an atom of the type described by 'base'. If successful, * returns NULL and initializes 'atom' with the parsed atom. On failure, * returns an error and the contents of 'atom' are indeterminate. The caller * is responsible for freeing the error or the atom that is returned. * * Violations of constraints expressed by 'base' are treated as errors. * * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to * RFC 7047 for information about this, and for the syntax that this function * accepts. If 'base' is a reference and a symbol is parsed, then the symbol's * 'strong_ref' or 'weak_ref' member is set to true, as appropriate. */ struct ovsdb_error * ovsdb_atom_from_json(union ovsdb_atom *atom, const struct ovsdb_base_type *base, const struct json *json, struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; error = ovsdb_atom_from_json__(atom, base, json, symtab); if (error) { return error; } error = ovsdb_atom_check_constraints(atom, base); if (error) { ovsdb_atom_destroy(atom, base->type); } return error; } /* Converts 'atom', of the specified 'type', to JSON format, and returns the * JSON. The caller is responsible for freeing the returned JSON. * * Refer to RFC 7047 for the format of the JSON that this function produces. */ struct json * ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: return json_integer_create(atom->integer); case OVSDB_TYPE_REAL: return json_real_create(atom->real); case OVSDB_TYPE_BOOLEAN: return json_boolean_create(atom->boolean); case OVSDB_TYPE_STRING: return json_string_create(atom->string); case OVSDB_TYPE_UUID: return wrap_json("uuid", json_string_create_nocopy( xasprintf(UUID_FMT, UUID_ARGS(&atom->uuid)))); case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } static char * ovsdb_atom_from_string__(union ovsdb_atom *atom, const struct ovsdb_base_type *base, const char *s, struct ovsdb_symbol_table *symtab) { enum ovsdb_atomic_type type = base->type; switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: { long long int integer; if (!str_to_llong(s, 10, &integer)) { return xasprintf("\"%s\" is not a valid integer", s); } atom->integer = integer; } break; case OVSDB_TYPE_REAL: if (!str_to_double(s, &atom->real)) { return xasprintf("\"%s\" is not a valid real number", s); } /* Our JSON input routines map negative zero to zero, so do that here * too for consistency. */ if (atom->real == 0.0) { atom->real = 0.0; } break; case OVSDB_TYPE_BOOLEAN: if (!strcmp(s, "true") || !strcmp(s, "yes") || !strcmp(s, "on") || !strcmp(s, "1")) { atom->boolean = true; } else if (!strcmp(s, "false") || !strcmp(s, "no") || !strcmp(s, "off") || !strcmp(s, "0")) { atom->boolean = false; } else { return xasprintf("\"%s\" is not a valid boolean " "(use \"true\" or \"false\")", s); } break; case OVSDB_TYPE_STRING: if (*s == '\0') { return xstrdup("An empty string is not valid as input; " "use \"\" to represent the empty string"); } else if (*s == '"') { size_t s_len = strlen(s); if (s_len < 2 || s[s_len - 1] != '"') { return xasprintf("%s: missing quote at end of " "quoted string", s); } else if (!json_string_unescape(s + 1, s_len - 2, &atom->string)) { char *error = xasprintf("%s: %s", s, atom->string); free(atom->string); return error; } } else { atom->string = xstrdup(s); } break; case OVSDB_TYPE_UUID: if (*s == '@') { struct ovsdb_symbol *symbol = ovsdb_symbol_table_insert(symtab, s); atom->uuid = symbol->uuid; ovsdb_symbol_referenced(symbol, base); } else if (!uuid_from_string(&atom->uuid, s)) { return xasprintf("\"%s\" is not a valid UUID", s); } break; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } return NULL; } /* Initializes 'atom' to a value of type 'base' parsed from 's', which takes * one of the following forms: * * - OVSDB_TYPE_INTEGER: A decimal integer optionally preceded by a sign. * * - OVSDB_TYPE_REAL: A floating-point number in the format accepted by * strtod(). * * - OVSDB_TYPE_BOOLEAN: "true", "yes", "on", "1" for true, or "false", * "no", "off", or "0" for false. * * - OVSDB_TYPE_STRING: A JSON string if it begins with a quote, otherwise * an arbitrary string. * * - OVSDB_TYPE_UUID: A UUID in RFC 4122 format. If 'symtab' is nonnull, * then an identifier beginning with '@' is also acceptable. If the * named identifier is already in 'symtab', then the associated UUID is * used; otherwise, a new, random UUID is used and added to the symbol * table. If 'base' is a reference and a symbol is parsed, then the * symbol's 'strong_ref' or 'weak_ref' member is set to true, as * appropriate. * * Returns a null pointer if successful, otherwise an error message describing * the problem. On failure, the contents of 'atom' are indeterminate. The * caller is responsible for freeing the atom or the error. */ char * ovsdb_atom_from_string(union ovsdb_atom *atom, const struct ovsdb_base_type *base, const char *s, struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; char *msg; msg = ovsdb_atom_from_string__(atom, base, s, symtab); if (msg) { return msg; } error = ovsdb_atom_check_constraints(atom, base); if (error) { ovsdb_atom_destroy(atom, base->type); msg = ovsdb_error_to_string(error); ovsdb_error_destroy(error); } return msg; } static bool string_needs_quotes(const char *s) { const char *p = s; unsigned char c; c = *p++; if (!isalpha(c) && c != '_') { return true; } while ((c = *p++) != '\0') { if (!isalpha(c) && c != '_' && c != '-' && c != '.') { return true; } } if (!strcmp(s, "true") || !strcmp(s, "false")) { return true; } return false; } /* Appends 'atom' (which has the given 'type') to 'out', in a format acceptable * to ovsdb_atom_from_string(). */ void ovsdb_atom_to_string(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, struct ds *out) { switch (type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: ds_put_format(out, "%"PRId64, atom->integer); break; case OVSDB_TYPE_REAL: ds_put_format(out, "%.*g", DBL_DIG, atom->real); break; case OVSDB_TYPE_BOOLEAN: ds_put_cstr(out, atom->boolean ? "true" : "false"); break; case OVSDB_TYPE_STRING: if (string_needs_quotes(atom->string)) { struct json json; json.type = JSON_STRING; json.u.string = atom->string; json_to_ds(&json, 0, out); } else { ds_put_cstr(out, atom->string); } break; case OVSDB_TYPE_UUID: ds_put_format(out, UUID_FMT, UUID_ARGS(&atom->uuid)); break; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } /* Appends 'atom' (which has the given 'type') to 'out', in a bare string * format that cannot be parsed uniformly back into a datum but is easier for * shell scripts, etc., to deal with. */ void ovsdb_atom_to_bare(const union ovsdb_atom *atom, enum ovsdb_atomic_type type, struct ds *out) { if (type == OVSDB_TYPE_STRING) { ds_put_cstr(out, atom->string); } else { ovsdb_atom_to_string(atom, type, out); } } static struct ovsdb_error * check_string_constraints(const char *s, const struct ovsdb_string_constraints *c) { size_t n_chars; char *msg; msg = utf8_validate(s, &n_chars); if (msg) { struct ovsdb_error *error; error = ovsdb_error("constraint violation", "not a valid UTF-8 string: %s", msg); free(msg); return error; } if (n_chars < c->minLen) { return ovsdb_error( "constraint violation", "\"%s\" length %"PRIuSIZE" is less than minimum allowed " "length %u", s, n_chars, c->minLen); } else if (n_chars > c->maxLen) { return ovsdb_error( "constraint violation", "\"%s\" length %"PRIuSIZE" is greater than maximum allowed " "length %u", s, n_chars, c->maxLen); } return NULL; } /* Checks whether 'atom' meets the constraints (if any) defined in 'base'. * (base->type must specify 'atom''s type.) Returns a null pointer if the * constraints are met, otherwise an error that explains the violation. * * Checking UUID constraints is deferred to transaction commit time, so this * function does nothing for UUID constraints. */ struct ovsdb_error * ovsdb_atom_check_constraints(const union ovsdb_atom *atom, const struct ovsdb_base_type *base) { if (base->enum_ && ovsdb_datum_find_key(base->enum_, atom, base->type) == UINT_MAX) { struct ovsdb_error *error; struct ds actual = DS_EMPTY_INITIALIZER; struct ds valid = DS_EMPTY_INITIALIZER; ovsdb_atom_to_string(atom, base->type, &actual); ovsdb_datum_to_string(base->enum_, ovsdb_base_type_get_enum_type(base->type), &valid); error = ovsdb_error("constraint violation", "%s is not one of the allowed values (%s)", ds_cstr(&actual), ds_cstr(&valid)); ds_destroy(&actual); ds_destroy(&valid); return error; } switch (base->type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: if (atom->integer >= base->u.integer.min && atom->integer <= base->u.integer.max) { return NULL; } else if (base->u.integer.min != INT64_MIN) { if (base->u.integer.max != INT64_MAX) { return ovsdb_error("constraint violation", "%"PRId64" is not in the valid range " "%"PRId64" to %"PRId64" (inclusive)", atom->integer, base->u.integer.min, base->u.integer.max); } else { return ovsdb_error("constraint violation", "%"PRId64" is less than minimum allowed " "value %"PRId64, atom->integer, base->u.integer.min); } } else { return ovsdb_error("constraint violation", "%"PRId64" is greater than maximum allowed " "value %"PRId64, atom->integer, base->u.integer.max); } OVS_NOT_REACHED(); case OVSDB_TYPE_REAL: if (atom->real >= base->u.real.min && atom->real <= base->u.real.max) { return NULL; } else if (base->u.real.min != -DBL_MAX) { if (base->u.real.max != DBL_MAX) { return ovsdb_error("constraint violation", "%.*g is not in the valid range " "%.*g to %.*g (inclusive)", DBL_DIG, atom->real, DBL_DIG, base->u.real.min, DBL_DIG, base->u.real.max); } else { return ovsdb_error("constraint violation", "%.*g is less than minimum allowed " "value %.*g", DBL_DIG, atom->real, DBL_DIG, base->u.real.min); } } else { return ovsdb_error("constraint violation", "%.*g is greater than maximum allowed " "value %.*g", DBL_DIG, atom->real, DBL_DIG, base->u.real.max); } OVS_NOT_REACHED(); case OVSDB_TYPE_BOOLEAN: return NULL; case OVSDB_TYPE_STRING: return check_string_constraints(atom->string, &base->u.string); case OVSDB_TYPE_UUID: return NULL; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } static union ovsdb_atom * alloc_default_atoms(enum ovsdb_atomic_type type, size_t n) { if (type != OVSDB_TYPE_VOID && n) { union ovsdb_atom *atoms; unsigned int i; atoms = xmalloc(n * sizeof *atoms); for (i = 0; i < n; i++) { ovsdb_atom_init_default(&atoms[i], type); } return atoms; } else { /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is * treated as xmalloc(1). */ return NULL; } } /* Initializes 'datum' as an empty datum. (An empty datum can be treated as * any type.) */ void ovsdb_datum_init_empty(struct ovsdb_datum *datum) { datum->n = 0; datum->keys = NULL; datum->values = NULL; } /* Initializes 'datum' as a datum that has the default value for 'type'. * * The default value for a particular type is as defined in RFC 7047: * * - If n_min is 0, then the default value is the empty set (or map). * * - If n_min is 1, the default value is a single value or a single * key-value pair, whose key and value are the defaults for their * atomic types. (See ovsdb_atom_init_default() for details.) * * - n_min > 1 is invalid. See ovsdb_type_is_valid(). */ void ovsdb_datum_init_default(struct ovsdb_datum *datum, const struct ovsdb_type *type) { datum->n = type->n_min; datum->keys = alloc_default_atoms(type->key.type, datum->n); datum->values = alloc_default_atoms(type->value.type, datum->n); } /* Returns a read-only datum of the given 'type' that has the default value for * 'type'. The caller must not modify or free the returned datum. * * See ovsdb_datum_init_default() for an explanation of the default value of a * datum. */ const struct ovsdb_datum * ovsdb_datum_default(const struct ovsdb_type *type) { if (type->n_min == 0) { static const struct ovsdb_datum empty; return ∅ } else if (type->n_min == 1) { static struct ovsdb_datum default_data[OVSDB_N_TYPES][OVSDB_N_TYPES]; struct ovsdb_datum *d; int kt = type->key.type; int vt = type->value.type; ovs_assert(ovsdb_type_is_valid(type)); d = &default_data[kt][vt]; if (!d->n) { d->n = 1; d->keys = CONST_CAST(union ovsdb_atom *, ovsdb_atom_default(kt)); if (vt != OVSDB_TYPE_VOID) { d->values = CONST_CAST(union ovsdb_atom *, ovsdb_atom_default(vt)); } } return d; } else { OVS_NOT_REACHED(); } } /* Returns true if 'datum', which must have the given 'type', has the default * value for that type. * * See ovsdb_datum_init_default() for an explanation of the default value of a * datum. */ bool ovsdb_datum_is_default(const struct ovsdb_datum *datum, const struct ovsdb_type *type) { size_t i; if (datum->n != type->n_min) { return false; } for (i = 0; i < datum->n; i++) { if (!ovsdb_atom_is_default(&datum->keys[i], type->key.type)) { return false; } if (type->value.type != OVSDB_TYPE_VOID && !ovsdb_atom_is_default(&datum->values[i], type->value.type)) { return false; } } return true; } static union ovsdb_atom * clone_atoms(const union ovsdb_atom *old, enum ovsdb_atomic_type type, size_t n) { if (type != OVSDB_TYPE_VOID && n) { union ovsdb_atom *new; unsigned int i; new = xmalloc(n * sizeof *new); for (i = 0; i < n; i++) { ovsdb_atom_clone(&new[i], &old[i], type); } return new; } else { /* Avoid wasting memory in the n == 0 case, because xmalloc(0) is * treated as xmalloc(1). */ return NULL; } } /* Initializes 'new' as a copy of 'old', with the given 'type'. * * The caller must eventually arrange for 'new' to be destroyed (with * ovsdb_datum_destroy()). */ void ovsdb_datum_clone(struct ovsdb_datum *new, const struct ovsdb_datum *old, const struct ovsdb_type *type) { unsigned int n = old->n; new->n = n; new->keys = clone_atoms(old->keys, type->key.type, n); new->values = clone_atoms(old->values, type->value.type, n); } static void free_data(enum ovsdb_atomic_type type, union ovsdb_atom *atoms, size_t n_atoms) { if (ovsdb_atom_needs_destruction(type)) { unsigned int i; for (i = 0; i < n_atoms; i++) { ovsdb_atom_destroy(&atoms[i], type); } } free(atoms); } /* Frees the data owned by 'datum', which must have the given 'type'. * * This does not actually call free(datum). If necessary, the caller must be * responsible for that. */ void ovsdb_datum_destroy(struct ovsdb_datum *datum, const struct ovsdb_type *type) { free_data(type->key.type, datum->keys, datum->n); free_data(type->value.type, datum->values, datum->n); } /* Swaps the contents of 'a' and 'b', which need not have the same type. */ void ovsdb_datum_swap(struct ovsdb_datum *a, struct ovsdb_datum *b) { struct ovsdb_datum tmp = *a; *a = *b; *b = tmp; } struct ovsdb_datum_sort_cbdata { enum ovsdb_atomic_type key_type; enum ovsdb_atomic_type value_type; struct ovsdb_datum *datum; }; static int ovsdb_datum_sort_compare_cb(size_t a, size_t b, void *cbdata_) { struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; int retval; retval = ovsdb_atom_compare_3way(&cbdata->datum->keys[a], &cbdata->datum->keys[b], cbdata->key_type); if (retval || cbdata->value_type == OVSDB_TYPE_VOID) { return retval; } return ovsdb_atom_compare_3way(&cbdata->datum->values[a], &cbdata->datum->values[b], cbdata->value_type); } static void ovsdb_datum_sort_swap_cb(size_t a, size_t b, void *cbdata_) { struct ovsdb_datum_sort_cbdata *cbdata = cbdata_; ovsdb_atom_swap(&cbdata->datum->keys[a], &cbdata->datum->keys[b]); if (cbdata->datum->values) { ovsdb_atom_swap(&cbdata->datum->values[a], &cbdata->datum->values[b]); } } static void ovsdb_datum_sort__(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type, enum ovsdb_atomic_type value_type) { struct ovsdb_datum_sort_cbdata cbdata; cbdata.key_type = key_type; cbdata.value_type = value_type; cbdata.datum = datum; sort(datum->n, ovsdb_datum_sort_compare_cb, ovsdb_datum_sort_swap_cb, &cbdata); } /* The keys in an ovsdb_datum must be unique and in sorted order. Most * functions that modify an ovsdb_datum maintain these invariants. For those * that don't, this function checks and restores these invariants for 'datum', * whose keys are of type 'key_type'. * * This function returns NULL if successful, otherwise an error message. The * caller must free the returned error when it is no longer needed. On error, * 'datum' is sorted but not unique. */ struct ovsdb_error * ovsdb_datum_sort(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type) { size_t i; if (datum->n < 2) { return NULL; } ovsdb_datum_sort__(datum, key_type, OVSDB_TYPE_VOID); for (i = 0; i < datum->n - 1; i++) { if (ovsdb_atom_equals(&datum->keys[i], &datum->keys[i + 1], key_type)) { if (datum->values) { return ovsdb_error(NULL, "map contains duplicate key"); } else { return ovsdb_error(NULL, "set contains duplicate"); } } } return NULL; } /* This function is the same as ovsdb_datum_sort(), except that the caller * knows that 'datum' is unique. The operation therefore "cannot fail", so * this function assert-fails if it actually does. */ void ovsdb_datum_sort_assert(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type) { struct ovsdb_error *error = ovsdb_datum_sort(datum, key_type); if (error) { OVS_NOT_REACHED(); } } /* This is similar to ovsdb_datum_sort(), except that it drops duplicate keys * instead of reporting an error. In a map type, the smallest value among a * group of duplicate pairs is retained and the others are dropped. * * Returns the number of keys (or pairs) that were dropped. */ size_t ovsdb_datum_sort_unique(struct ovsdb_datum *datum, enum ovsdb_atomic_type key_type, enum ovsdb_atomic_type value_type) { size_t src, dst; if (datum->n < 2) { return 0; } ovsdb_datum_sort__(datum, key_type, value_type); dst = 1; for (src = 1; src < datum->n; src++) { if (ovsdb_atom_equals(&datum->keys[src], &datum->keys[dst - 1], key_type)) { ovsdb_atom_destroy(&datum->keys[src], key_type); if (value_type != OVSDB_TYPE_VOID) { ovsdb_atom_destroy(&datum->values[src], value_type); } } else { if (src != dst) { datum->keys[dst] = datum->keys[src]; if (value_type != OVSDB_TYPE_VOID) { datum->values[dst] = datum->values[src]; } } dst++; } } datum->n = dst; return datum->n - src; } /* Checks that each of the atoms in 'datum' conforms to the constraints * specified by its 'type'. Returns an error if a constraint is violated, * otherwise a null pointer. * * This function is not commonly useful because the most ordinary way to obtain * a datum is ultimately via ovsdb_atom_from_string() or * ovsdb_atom_from_json(), which check constraints themselves. */ struct ovsdb_error * ovsdb_datum_check_constraints(const struct ovsdb_datum *datum, const struct ovsdb_type *type) { struct ovsdb_error *error; unsigned int i; for (i = 0; i < datum->n; i++) { error = ovsdb_atom_check_constraints(&datum->keys[i], &type->key); if (error) { return error; } } if (type->value.type != OVSDB_TYPE_VOID) { for (i = 0; i < datum->n; i++) { error = ovsdb_atom_check_constraints(&datum->values[i], &type->value); if (error) { return error; } } } return NULL; } static struct ovsdb_error * ovsdb_datum_from_json__(struct ovsdb_datum *datum, const struct ovsdb_type *type, const struct json *json, struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; if (ovsdb_type_is_map(type) || (json->type == JSON_ARRAY && json->u.array.n > 0 && json->u.array.elems[0]->type == JSON_STRING && !strcmp(json->u.array.elems[0]->u.string, "set"))) { bool is_map = ovsdb_type_is_map(type); const char *class = is_map ? "map" : "set"; const struct json *inner; unsigned int i; size_t n; error = unwrap_json(json, class, JSON_ARRAY, &inner); if (error) { return error; } n = inner->u.array.n; if (n < type->n_min || n > type->n_max) { return ovsdb_syntax_error(json, NULL, "%s must have %u to " "%u members but %"PRIuSIZE" are present", class, type->n_min, type->n_max, n); } datum->n = 0; datum->keys = xmalloc(n * sizeof *datum->keys); datum->values = is_map ? xmalloc(n * sizeof *datum->values) : NULL; for (i = 0; i < n; i++) { const struct json *element = inner->u.array.elems[i]; const struct json *key = NULL; const struct json *value = NULL; if (!is_map) { key = element; } else { error = parse_json_pair(element, &key, &value); if (error) { goto error; } } error = ovsdb_atom_from_json(&datum->keys[i], &type->key, key, symtab); if (error) { goto error; } if (is_map) { error = ovsdb_atom_from_json(&datum->values[i], &type->value, value, symtab); if (error) { ovsdb_atom_destroy(&datum->keys[i], type->key.type); goto error; } } datum->n++; } return NULL; error: ovsdb_datum_destroy(datum, type); return error; } else { datum->n = 1; datum->keys = xmalloc(sizeof *datum->keys); datum->values = NULL; error = ovsdb_atom_from_json(&datum->keys[0], &type->key, json, symtab); if (error) { free(datum->keys); } return error; } } /* Parses 'json' as a datum of the type described by 'type'. If successful, * returns NULL and initializes 'datum' with the parsed datum. On failure, * returns an error and the contents of 'datum' are indeterminate. The caller * is responsible for freeing the error or the datum that is returned. * * Violations of constraints expressed by 'type' are treated as errors. * * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted. Refer to * RFC 7047 for information about this, and for the syntax that this function * accepts. */ struct ovsdb_error * ovsdb_datum_from_json(struct ovsdb_datum *datum, const struct ovsdb_type *type, const struct json *json, struct ovsdb_symbol_table *symtab) { struct ovsdb_error *error; error = ovsdb_datum_from_json__(datum, type, json, symtab); if (error) { return error; } error = ovsdb_datum_sort(datum, type->key.type); if (error) { ovsdb_datum_destroy(datum, type); } return error; } /* Converts 'datum', of the specified 'type', to JSON format, and returns the * JSON. The caller is responsible for freeing the returned JSON. * * 'type' constraints on datum->n are ignored. * * Refer to RFC 7047 for the format of the JSON that this function produces. */ struct json * ovsdb_datum_to_json(const struct ovsdb_datum *datum, const struct ovsdb_type *type) { if (ovsdb_type_is_map(type)) { struct json **elems; size_t i; elems = xmalloc(datum->n * sizeof *elems); for (i = 0; i < datum->n; i++) { elems[i] = json_array_create_2( ovsdb_atom_to_json(&datum->keys[i], type->key.type), ovsdb_atom_to_json(&datum->values[i], type->value.type)); } return wrap_json("map", json_array_create(elems, datum->n)); } else if (datum->n == 1) { return ovsdb_atom_to_json(&datum->keys[0], type->key.type); } else { struct json **elems; size_t i; elems = xmalloc(datum->n * sizeof *elems); for (i = 0; i < datum->n; i++) { elems[i] = ovsdb_atom_to_json(&datum->keys[i], type->key.type); } return wrap_json("set", json_array_create(elems, datum->n)); } } static const char * skip_spaces(const char *p) { while (isspace((unsigned char) *p)) { p++; } return p; } static char * parse_atom_token(const char **s, const struct ovsdb_base_type *base, union ovsdb_atom *atom, struct ovsdb_symbol_table *symtab) { char *token, *error; error = ovsdb_token_parse(s, &token); if (!error) { error = ovsdb_atom_from_string(atom, base, token, symtab); free(token); } return error; } static char * parse_key_value(const char **s, const struct ovsdb_type *type, union ovsdb_atom *key, union ovsdb_atom *value, struct ovsdb_symbol_table *symtab) { const char *start = *s; char *error; error = parse_atom_token(s, &type->key, key, symtab); if (!error && type->value.type != OVSDB_TYPE_VOID) { *s = skip_spaces(*s); if (**s == '=') { (*s)++; *s = skip_spaces(*s); error = parse_atom_token(s, &type->value, value, symtab); } else { error = xasprintf("%s: syntax error at \"%c\" expecting \"=\"", start, **s); } if (error) { ovsdb_atom_destroy(key, type->key.type); } } return error; } static void free_key_value(const struct ovsdb_type *type, union ovsdb_atom *key, union ovsdb_atom *value) { ovsdb_atom_destroy(key, type->key.type); if (type->value.type != OVSDB_TYPE_VOID) { ovsdb_atom_destroy(value, type->value.type); } } /* Initializes 'datum' as a datum of the given 'type', parsing its contents * from 's'. The format of 's' is a series of space or comma separated atoms * or, for a map, '='-delimited pairs of atoms. Each atom must in a format * acceptable to ovsdb_atom_from_string(). Optionally, a set may be enclosed * in "[]" or a map in "{}"; for an empty set or map these punctuators are * required. * * Optionally, a symbol table may be supplied as 'symtab'. It is passed to * ovsdb_atom_to_string(). */ char * ovsdb_datum_from_string(struct ovsdb_datum *datum, const struct ovsdb_type *type, const char *s, struct ovsdb_symbol_table *symtab) { bool is_map = ovsdb_type_is_map(type); struct ovsdb_error *dberror; const char *p; int end_delim; char *error; ovsdb_datum_init_empty(datum); /* Swallow a leading delimiter if there is one. */ p = skip_spaces(s); if (*p == (is_map ? '{' : '[')) { end_delim = is_map ? '}' : ']'; p = skip_spaces(p + 1); } else if (!*p) { if (is_map) { return xstrdup("use \"{}\" to specify the empty map"); } else { return xstrdup("use \"[]\" to specify the empty set"); } } else { end_delim = 0; } while (*p && *p != end_delim) { union ovsdb_atom key, value; if (ovsdb_token_is_delim(*p)) { char *type_str = ovsdb_type_to_english(type); error = xasprintf("%s: unexpected \"%c\" parsing %s", s, *p, type_str); free(type_str); goto error; } /* Add to datum. */ error = parse_key_value(&p, type, &key, &value, symtab); if (error) { goto error; } ovsdb_datum_add_unsafe(datum, &key, &value, type); free_key_value(type, &key, &value); /* Skip optional white space and comma. */ p = skip_spaces(p); if (*p == ',') { p = skip_spaces(p + 1); } } if (*p != end_delim) { error = xasprintf("%s: missing \"%c\" at end of data", s, end_delim); goto error; } if (end_delim) { p = skip_spaces(p + 1); if (*p) { error = xasprintf("%s: trailing garbage after \"%c\"", s, end_delim); goto error; } } if (datum->n < type->n_min) { error = xasprintf("%s: %u %s specified but the minimum number is %u", s, datum->n, is_map ? "pair(s)" : "value(s)", type->n_min); goto error; } else if (datum->n > type->n_max) { error = xasprintf("%s: %u %s specified but the maximum number is %u", s, datum->n, is_map ? "pair(s)" : "value(s)", type->n_max); goto error; } dberror = ovsdb_datum_sort(datum, type->key.type); if (dberror) { ovsdb_error_destroy(dberror); if (ovsdb_type_is_map(type)) { error = xasprintf("%s: map contains duplicate key", s); } else { error = xasprintf("%s: set contains duplicate value", s); } goto error; } return NULL; error: ovsdb_datum_destroy(datum, type); ovsdb_datum_init_empty(datum); return error; } /* Appends to 'out' the 'datum' (with the given 'type') in a format acceptable * to ovsdb_datum_from_string(). */ void ovsdb_datum_to_string(const struct ovsdb_datum *datum, const struct ovsdb_type *type, struct ds *out) { bool is_map = ovsdb_type_is_map(type); size_t i; if (type->n_max > 1 || !datum->n) { ds_put_char(out, is_map ? '{' : '['); } for (i = 0; i < datum->n; i++) { if (i > 0) { ds_put_cstr(out, ", "); } ovsdb_atom_to_string(&datum->keys[i], type->key.type, out); if (is_map) { ds_put_char(out, '='); ovsdb_atom_to_string(&datum->values[i], type->value.type, out); } } if (type->n_max > 1 || !datum->n) { ds_put_char(out, is_map ? '}' : ']'); } } /* Appends to 'out' the 'datum' (with the given 'type') in a bare string format * that cannot be parsed uniformly back into a datum but is easier for shell * scripts, etc., to deal with. */ void ovsdb_datum_to_bare(const struct ovsdb_datum *datum, const struct ovsdb_type *type, struct ds *out) { bool is_map = ovsdb_type_is_map(type); size_t i; for (i = 0; i < datum->n; i++) { if (i > 0) { ds_put_cstr(out, " "); } ovsdb_atom_to_bare(&datum->keys[i], type->key.type, out); if (is_map) { ds_put_char(out, '='); ovsdb_atom_to_bare(&datum->values[i], type->value.type, out); } } } /* Initializes 'datum' as a string-to-string map whose contents are taken from * 'smap'. Destroys 'smap'. */ void ovsdb_datum_from_smap(struct ovsdb_datum *datum, struct smap *smap) { struct smap_node *node, *next; size_t i; datum->n = smap_count(smap); datum->keys = xmalloc(datum->n * sizeof *datum->keys); datum->values = xmalloc(datum->n * sizeof *datum->values); i = 0; SMAP_FOR_EACH_SAFE (node, next, smap) { smap_steal(smap, node, &datum->keys[i].string, &datum->values[i].string); i++; } ovs_assert(i == datum->n); smap_destroy(smap); ovsdb_datum_sort_unique(datum, OVSDB_TYPE_STRING, OVSDB_TYPE_STRING); } static uint32_t hash_atoms(enum ovsdb_atomic_type type, const union ovsdb_atom *atoms, unsigned int n, uint32_t basis) { if (type != OVSDB_TYPE_VOID) { unsigned int i; for (i = 0; i < n; i++) { basis = ovsdb_atom_hash(&atoms[i], type, basis); } } return basis; } uint32_t ovsdb_datum_hash(const struct ovsdb_datum *datum, const struct ovsdb_type *type, uint32_t basis) { basis = hash_atoms(type->key.type, datum->keys, datum->n, basis); basis ^= (type->key.type << 24) | (type->value.type << 16) | datum->n; basis = hash_atoms(type->value.type, datum->values, datum->n, basis); return basis; } static int atom_arrays_compare_3way(const union ovsdb_atom *a, const union ovsdb_atom *b, enum ovsdb_atomic_type type, size_t n) { unsigned int i; for (i = 0; i < n; i++) { int cmp = ovsdb_atom_compare_3way(&a[i], &b[i], type); if (cmp) { return cmp; } } return 0; } bool ovsdb_datum_equals(const struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type) { return !ovsdb_datum_compare_3way(a, b, type); } int ovsdb_datum_compare_3way(const struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type) { int cmp; if (a->n != b->n) { return a->n < b->n ? -1 : 1; } cmp = atom_arrays_compare_3way(a->keys, b->keys, type->key.type, a->n); if (cmp) { return cmp; } return (type->value.type == OVSDB_TYPE_VOID ? 0 : atom_arrays_compare_3way(a->values, b->values, type->value.type, a->n)); } /* If 'key' is one of the keys in 'datum', returns its index within 'datum', * otherwise UINT_MAX. 'key.type' must be the type of the atoms stored in the * 'keys' array in 'datum'. */ unsigned int ovsdb_datum_find_key(const struct ovsdb_datum *datum, const union ovsdb_atom *key, enum ovsdb_atomic_type key_type) { unsigned int low = 0; unsigned int high = datum->n; while (low < high) { unsigned int idx = (low + high) / 2; int cmp = ovsdb_atom_compare_3way(key, &datum->keys[idx], key_type); if (cmp < 0) { high = idx; } else if (cmp > 0) { low = idx + 1; } else { return idx; } } return UINT_MAX; } /* If 'key' and 'value' is one of the key-value pairs in 'datum', returns its * index within 'datum', otherwise UINT_MAX. 'key.type' must be the type of * the atoms stored in the 'keys' array in 'datum'. 'value_type' may be the * type of the 'values' atoms or OVSDB_TYPE_VOID to compare only keys. */ unsigned int ovsdb_datum_find_key_value(const struct ovsdb_datum *datum, const union ovsdb_atom *key, enum ovsdb_atomic_type key_type, const union ovsdb_atom *value, enum ovsdb_atomic_type value_type) { unsigned int idx = ovsdb_datum_find_key(datum, key, key_type); if (idx != UINT_MAX && value_type != OVSDB_TYPE_VOID && !ovsdb_atom_equals(&datum->values[idx], value, value_type)) { idx = UINT_MAX; } return idx; } /* If atom 'i' in 'a' is also in 'b', returns its index in 'b', otherwise * UINT_MAX. 'type' must be the type of 'a' and 'b', except that * type->value.type may be set to OVSDB_TYPE_VOID to compare keys but not * values. */ static unsigned int ovsdb_datum_find(const struct ovsdb_datum *a, int i, const struct ovsdb_datum *b, const struct ovsdb_type *type) { return ovsdb_datum_find_key_value(b, &a->keys[i], type->key.type, a->values ? &a->values[i] : NULL, type->value.type); } /* Returns true if every element in 'a' is also in 'b', false otherwise. */ bool ovsdb_datum_includes_all(const struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type) { size_t i; if (a->n > b->n) { return false; } for (i = 0; i < a->n; i++) { if (ovsdb_datum_find(a, i, b, type) == UINT_MAX) { return false; } } return true; } /* Returns true if no element in 'a' is also in 'b', false otherwise. */ bool ovsdb_datum_excludes_all(const struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type) { size_t i; for (i = 0; i < a->n; i++) { if (ovsdb_datum_find(a, i, b, type) != UINT_MAX) { return false; } } return true; } static void ovsdb_datum_reallocate(struct ovsdb_datum *a, const struct ovsdb_type *type, unsigned int capacity) { a->keys = xrealloc(a->keys, capacity * sizeof *a->keys); if (type->value.type != OVSDB_TYPE_VOID) { a->values = xrealloc(a->values, capacity * sizeof *a->values); } } /* Removes the element with index 'idx' from 'datum', which has type 'type'. * If 'idx' is not the last element in 'datum', then the removed element is * replaced by the (former) last element. * * This function does not maintain ovsdb_datum invariants. Use * ovsdb_datum_sort() to check and restore these invariants. */ void ovsdb_datum_remove_unsafe(struct ovsdb_datum *datum, size_t idx, const struct ovsdb_type *type) { ovsdb_atom_destroy(&datum->keys[idx], type->key.type); datum->keys[idx] = datum->keys[datum->n - 1]; if (type->value.type != OVSDB_TYPE_VOID) { ovsdb_atom_destroy(&datum->values[idx], type->value.type); datum->values[idx] = datum->values[datum->n - 1]; } datum->n--; } /* Adds the element with the given 'key' and 'value' to 'datum', which must * have the specified 'type'. * * This function always allocates memory, so it is not an efficient way to add * a number of elements to a datum. * * This function does not maintain ovsdb_datum invariants. Use * ovsdb_datum_sort() to check and restore these invariants. (But a datum with * 0 or 1 elements cannot violate the invariants anyhow.) */ void ovsdb_datum_add_unsafe(struct ovsdb_datum *datum, const union ovsdb_atom *key, const union ovsdb_atom *value, const struct ovsdb_type *type) { size_t idx = datum->n++; datum->keys = xrealloc(datum->keys, datum->n * sizeof *datum->keys); ovsdb_atom_clone(&datum->keys[idx], key, type->key.type); if (type->value.type != OVSDB_TYPE_VOID) { datum->values = xrealloc(datum->values, datum->n * sizeof *datum->values); ovsdb_atom_clone(&datum->values[idx], value, type->value.type); } } void ovsdb_datum_union(struct ovsdb_datum *a, const struct ovsdb_datum *b, const struct ovsdb_type *type, bool replace) { unsigned int n; size_t bi; n = a->n; for (bi = 0; bi < b->n; bi++) { unsigned int ai; ai = ovsdb_datum_find_key(a, &b->keys[bi], type->key.type); if (ai == UINT_MAX) { if (n == a->n) { ovsdb_datum_reallocate(a, type, a->n + (b->n - bi)); } ovsdb_atom_clone(&a->keys[n], &b->keys[bi], type->key.type); if (type->value.type != OVSDB_TYPE_VOID) { ovsdb_atom_clone(&a->values[n], &b->values[bi], type->value.type); } n++; } else if (replace && type->value.type != OVSDB_TYPE_VOID) { ovsdb_atom_destroy(&a->values[ai], type->value.type); ovsdb_atom_clone(&a->values[ai], &b->values[bi], type->value.type); } } if (n != a->n) { struct ovsdb_error *error; a->n = n; error = ovsdb_datum_sort(a, type->key.type); ovs_assert(!error); } } void ovsdb_datum_subtract(struct ovsdb_datum *a, const struct ovsdb_type *a_type, const struct ovsdb_datum *b, const struct ovsdb_type *b_type) { bool changed = false; size_t i; ovs_assert(a_type->key.type == b_type->key.type); ovs_assert(a_type->value.type == b_type->value.type || b_type->value.type == OVSDB_TYPE_VOID); /* XXX The big-O of this could easily be improved. */ for (i = 0; i < a->n; ) { unsigned int idx = ovsdb_datum_find(a, i, b, b_type); if (idx != UINT_MAX) { changed = true; ovsdb_datum_remove_unsafe(a, i, a_type); } else { i++; } } if (changed) { ovsdb_datum_sort_assert(a, a_type->key.type); } } struct ovsdb_symbol_table * ovsdb_symbol_table_create(void) { struct ovsdb_symbol_table *symtab = xmalloc(sizeof *symtab); shash_init(&symtab->sh); return symtab; } void ovsdb_symbol_table_destroy(struct ovsdb_symbol_table *symtab) { if (symtab) { shash_destroy_free_data(&symtab->sh); free(symtab); } } struct ovsdb_symbol * ovsdb_symbol_table_get(const struct ovsdb_symbol_table *symtab, const char *name) { return shash_find_data(&symtab->sh, name); } struct ovsdb_symbol * ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name, const struct uuid *uuid, bool created) { struct ovsdb_symbol *symbol; ovs_assert(!ovsdb_symbol_table_get(symtab, name)); symbol = xmalloc(sizeof *symbol); symbol->uuid = *uuid; symbol->created = created; symbol->strong_ref = false; symbol->weak_ref = false; shash_add(&symtab->sh, name, symbol); return symbol; } struct ovsdb_symbol * ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab, const char *name) { struct ovsdb_symbol *symbol; symbol = ovsdb_symbol_table_get(symtab, name); if (!symbol) { struct uuid uuid; uuid_generate(&uuid); symbol = ovsdb_symbol_table_put(symtab, name, &uuid, false); } return symbol; } /* Extracts a token from the beginning of 's' and returns a pointer just after * the token. Stores the token itself into '*outp', which the caller is * responsible for freeing (with free()). * * If 's[0]' is a delimiter, the returned token is the empty string. * * A token extends from 's' to the first delimiter, as defined by * ovsdb_token_is_delim(), or until the end of the string. A delimiter can be * escaped with a backslash, in which case the backslash does not appear in the * output. Double quotes also cause delimiters to be ignored, but the double * quotes are retained in the output. (Backslashes inside double quotes are * not removed, either.) */ char * ovsdb_token_parse(const char **s, char **outp) { const char *p; struct ds out; bool in_quotes; char *error; ds_init(&out); in_quotes = false; for (p = *s; *p != '\0'; ) { int c = *p++; if (c == '\\') { if (in_quotes) { ds_put_char(&out, '\\'); } if (!*p) { error = xasprintf("%s: backslash at end of argument", *s); goto error; } ds_put_char(&out, *p++); } else if (!in_quotes && ovsdb_token_is_delim(c)) { p--; break; } else { ds_put_char(&out, c); if (c == '"') { in_quotes = !in_quotes; } } } if (in_quotes) { error = xasprintf("%s: quoted string extends past end of argument", *s); goto error; } *outp = ds_cstr(&out); *s = p; return NULL; error: ds_destroy(&out); *outp = NULL; return error; } /* Returns true if 'c' delimits tokens, or if 'c' is 0, and false otherwise. */ bool ovsdb_token_is_delim(unsigned char c) { return strchr(":=, []{}!<>", c) != NULL; } openvswitch-2.5.9/lib/PaxHeaders.82075/table.c0000644000000000000000000000013113534540071015644 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90585434 openvswitch-2.5.9/lib/table.c0000644000175000017500000003701513534540071017341 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "table.h" #include "dynamic-string.h" #include "json.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "timeval.h" #include "util.h" struct column { char *heading; }; static char * cell_to_text(struct cell *cell, const struct table_style *style) { if (!cell->text) { if (cell->json) { if (style->cell_format == CF_JSON || !cell->type) { cell->text = json_to_string(cell->json, JSSF_SORT); } else { struct ovsdb_datum datum; struct ovsdb_error *error; struct ds s; error = ovsdb_datum_from_json(&datum, cell->type, cell->json, NULL); if (!error) { ds_init(&s); if (style->cell_format == CF_STRING) { ovsdb_datum_to_string(&datum, cell->type, &s); } else { ovsdb_datum_to_bare(&datum, cell->type, &s); } ovsdb_datum_destroy(&datum, cell->type); cell->text = ds_steal_cstr(&s); } else { cell->text = json_to_string(cell->json, JSSF_SORT); ovsdb_error_destroy(error); } } } else { cell->text = xstrdup(""); } } return cell->text; } static void cell_destroy(struct cell *cell) { free(cell->text); json_destroy(cell->json); } /* Initializes 'table' as an empty table. * * The caller should then: * * 1. Call table_add_column() once for each column. * 2. For each row: * 2a. Call table_add_row(). * 2b. For each column in the cell, call table_add_cell() and fill in * the returned cell. * 3. Call table_print() to print the final table. * 4. Free the table with table_destroy(). */ void table_init(struct table *table) { memset(table, 0, sizeof *table); } /* Destroys 'table' and frees all associated storage. (However, the client * owns the 'type' members pointed to by cells, so these are not destroyed.) */ void table_destroy(struct table *table) { if (table) { size_t i; for (i = 0; i < table->n_columns; i++) { free(table->columns[i].heading); } free(table->columns); for (i = 0; i < table->n_columns * table->n_rows; i++) { cell_destroy(&table->cells[i]); } free(table->cells); free(table->caption); } } /* Sets 'caption' as the caption for 'table'. * * 'table' takes ownership of 'caption'. */ void table_set_caption(struct table *table, char *caption) { free(table->caption); table->caption = caption; } /* Turns printing a timestamp along with 'table' on or off, according to * 'timestamp'. */ void table_set_timestamp(struct table *table, bool timestamp) { table->timestamp = timestamp; } /* Adds a new column to 'table' just to the right of any existing column, with * 'heading' as a title for the column. 'heading' must be a valid printf() * format specifier. * * Columns must be added before any data is put into 'table'. */ void table_add_column(struct table *table, const char *heading, ...) { struct column *column; va_list args; ovs_assert(!table->n_rows); if (table->n_columns >= table->allocated_columns) { table->columns = x2nrealloc(table->columns, &table->allocated_columns, sizeof *table->columns); } column = &table->columns[table->n_columns++]; va_start(args, heading); column->heading = xvasprintf(heading, args); va_end(args); } static struct cell * table_cell__(const struct table *table, size_t row, size_t column) { return &table->cells[column + row * table->n_columns]; } /* Adds a new row to 'table'. The table's columns must already have been added * with table_add_column(). * * The row is initially empty; use table_add_cell() to start filling it in. */ void table_add_row(struct table *table) { size_t x, y; if (table->n_rows >= table->allocated_rows) { table->cells = x2nrealloc(table->cells, &table->allocated_rows, table->n_columns * sizeof *table->cells); } y = table->n_rows++; table->current_column = 0; for (x = 0; x < table->n_columns; x++) { struct cell *cell = table_cell__(table, y, x); memset(cell, 0, sizeof *cell); } } /* Adds a new cell in the current row of 'table', which must have been added * with table_add_row(). Cells are filled in the same order that the columns * were added with table_add_column(). * * The caller is responsible for filling in the returned cell, in one of two * fashions: * * - If the cell should contain an ovsdb_datum, formatted according to the * table style, then fill in the 'json' member with the JSON representation * of the datum and 'type' with its type. * * - If the cell should contain a fixed text string, then the caller should * assign that string to the 'text' member. This is undesirable if the * cell actually contains OVSDB data because 'text' cannot be formatted * according to the table style; it is always output verbatim. */ struct cell * table_add_cell(struct table *table) { size_t x, y; ovs_assert(table->n_rows > 0); ovs_assert(table->current_column < table->n_columns); x = table->current_column++; y = table->n_rows - 1; return table_cell__(table, y, x); } static void table_print_table_line__(struct ds *line) { puts(ds_cstr(line)); ds_clear(line); } static char * table_format_timestamp__(void) { return xastrftime_msec("%Y-%m-%d %H:%M:%S.###", time_wall_msec(), true); } static void table_print_timestamp__(const struct table *table) { if (table->timestamp) { char *s = table_format_timestamp__(); puts(s); free(s); } } static void table_print_table__(const struct table *table, const struct table_style *style) { static int n = 0; struct ds line = DS_EMPTY_INITIALIZER; int *widths; size_t x, y; if (n++ > 0) { putchar('\n'); } table_print_timestamp__(table); if (table->caption) { puts(table->caption); } widths = xmalloc(table->n_columns * sizeof *widths); for (x = 0; x < table->n_columns; x++) { const struct column *column = &table->columns[x]; widths[x] = strlen(column->heading); for (y = 0; y < table->n_rows; y++) { const char *text = cell_to_text(table_cell__(table, y, x), style); size_t length = strlen(text); if (length > widths[x]) { widths[x] = length; } } } if (style->headings) { for (x = 0; x < table->n_columns; x++) { const struct column *column = &table->columns[x]; if (x) { ds_put_char(&line, ' '); } ds_put_format(&line, "%-*s", widths[x], column->heading); } table_print_table_line__(&line); for (x = 0; x < table->n_columns; x++) { if (x) { ds_put_char(&line, ' '); } ds_put_char_multiple(&line, '-', widths[x]); } table_print_table_line__(&line); } for (y = 0; y < table->n_rows; y++) { for (x = 0; x < table->n_columns; x++) { const char *text = cell_to_text(table_cell__(table, y, x), style); if (x) { ds_put_char(&line, ' '); } ds_put_format(&line, "%-*s", widths[x], text); } table_print_table_line__(&line); } ds_destroy(&line); free(widths); } static void table_print_list__(const struct table *table, const struct table_style *style) { static int n = 0; size_t x, y; if (n++ > 0) { putchar('\n'); } table_print_timestamp__(table); if (table->caption) { puts(table->caption); } for (y = 0; y < table->n_rows; y++) { if (y > 0) { putchar('\n'); } for (x = 0; x < table->n_columns; x++) { const char *text = cell_to_text(table_cell__(table, y, x), style); if (style->headings) { printf("%-20s: ", table->columns[x].heading); } puts(text); } } } static void table_escape_html_text__(const char *s, size_t n) { size_t i; for (i = 0; i < n; i++) { char c = s[i]; switch (c) { case '&': fputs("&", stdout); break; case '<': fputs("<", stdout); break; case '>': fputs(">", stdout); break; case '"': fputs(""", stdout); break; default: putchar(c); break; } } } static void table_print_html_cell__(const char *element, const char *content) { const char *p; printf(" <%s>", element); for (p = content; *p; ) { struct uuid uuid; if (uuid_from_string_prefix(&uuid, p)) { printf("%.*s", UUID_LEN, p, 8, p); p += UUID_LEN; } else { table_escape_html_text__(p, 1); p++; } } printf("\n", element); } static void table_print_html__(const struct table *table, const struct table_style *style) { size_t x, y; table_print_timestamp__(table); fputs("
    \n", stdout); if (table->caption) { table_print_html_cell__("caption", table->caption); } if (style->headings) { fputs(" \n", stdout); for (x = 0; x < table->n_columns; x++) { const struct column *column = &table->columns[x]; table_print_html_cell__("th", column->heading); } fputs(" \n", stdout); } for (y = 0; y < table->n_rows; y++) { fputs(" \n", stdout); for (x = 0; x < table->n_columns; x++) { const char *content; content = cell_to_text(table_cell__(table, y, x), style); if (!strcmp(table->columns[x].heading, "_uuid")) { fputs(" \n", stdout); } else { table_print_html_cell__("td", content); } } fputs(" \n", stdout); } fputs("
    ", stdout); table_escape_html_text__(content, 8); fputs("
    \n", stdout); } static void table_print_csv_cell__(const char *content) { const char *p; if (!strpbrk(content, "\n\",")) { fputs(content, stdout); } else { putchar('"'); for (p = content; *p != '\0'; p++) { switch (*p) { case '"': fputs("\"\"", stdout); break; default: putchar(*p); break; } } putchar('"'); } } static void table_print_csv__(const struct table *table, const struct table_style *style) { static int n = 0; size_t x, y; if (n++ > 0) { putchar('\n'); } table_print_timestamp__(table); if (table->caption) { puts(table->caption); } if (style->headings) { for (x = 0; x < table->n_columns; x++) { const struct column *column = &table->columns[x]; if (x) { putchar(','); } table_print_csv_cell__(column->heading); } putchar('\n'); } for (y = 0; y < table->n_rows; y++) { for (x = 0; x < table->n_columns; x++) { if (x) { putchar(','); } table_print_csv_cell__(cell_to_text(table_cell__(table, y, x), style)); } putchar('\n'); } } static void table_print_json__(const struct table *table, const struct table_style *style) { struct json *json, *headings, *data; size_t x, y; char *s; json = json_object_create(); if (table->caption) { json_object_put_string(json, "caption", table->caption); } if (table->timestamp) { char *s = table_format_timestamp__(); json_object_put_string(json, "time", s); free(s); } headings = json_array_create_empty(); for (x = 0; x < table->n_columns; x++) { const struct column *column = &table->columns[x]; json_array_add(headings, json_string_create(column->heading)); } json_object_put(json, "headings", headings); data = json_array_create_empty(); for (y = 0; y < table->n_rows; y++) { struct json *row = json_array_create_empty(); for (x = 0; x < table->n_columns; x++) { const struct cell *cell = table_cell__(table, y, x); if (cell->text) { json_array_add(row, json_string_create(cell->text)); } else if (cell->json) { json_array_add(row, json_clone(cell->json)); } else { json_array_add(row, json_null_create()); } } json_array_add(data, row); } json_object_put(json, "data", data); s = json_to_string(json, style->json_flags); json_destroy(json); puts(s); free(s); } /* Parses 'format' as the argument to a --format command line option, updating * 'style->format'. */ void table_parse_format(struct table_style *style, const char *format) { if (!strcmp(format, "table")) { style->format = TF_TABLE; } else if (!strcmp(format, "list")) { style->format = TF_LIST; } else if (!strcmp(format, "html")) { style->format = TF_HTML; } else if (!strcmp(format, "csv")) { style->format = TF_CSV; } else if (!strcmp(format, "json")) { style->format = TF_JSON; } else { ovs_fatal(0, "unknown output format \"%s\"", format); } } /* Parses 'format' as the argument to a --data command line option, updating * 'style->cell_format'. */ void table_parse_cell_format(struct table_style *style, const char *format) { if (!strcmp(format, "string")) { style->cell_format = CF_STRING; } else if (!strcmp(format, "bare")) { style->cell_format = CF_BARE; } else if (!strcmp(format, "json")) { style->cell_format = CF_JSON; } else { ovs_fatal(0, "unknown data format \"%s\"", format); } } /* Outputs 'table' on stdout in the specified 'style'. */ void table_print(const struct table *table, const struct table_style *style) { switch (style->format) { case TF_TABLE: table_print_table__(table, style); break; case TF_LIST: table_print_list__(table, style); break; case TF_HTML: table_print_html__(table, style); break; case TF_CSV: table_print_csv__(table, style); break; case TF_JSON: table_print_json__(table, style); break; } } openvswitch-2.5.9/lib/PaxHeaders.82075/mac-learning.c0000644000000000000000000000013013534540071017111 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.757853249 openvswitch-2.5.9/lib/mac-learning.c0000644000175000017500000003062113534540071020603 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "mac-learning.h" #include #include #include "bitmap.h" #include "coverage.h" #include "hash.h" #include "list.h" #include "poll-loop.h" #include "timeval.h" #include "unaligned.h" #include "util.h" #include "vlan-bitmap.h" COVERAGE_DEFINE(mac_learning_learned); COVERAGE_DEFINE(mac_learning_expired); /* Returns the number of seconds since 'e' (within 'ml') was last learned. */ int mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e) { time_t remaining = e->expires - time_now(); return ml->idle_time - remaining; } static uint32_t mac_table_hash(const struct mac_learning *ml, const struct eth_addr mac, uint16_t vlan) { return hash_mac(mac, vlan, ml->secret); } static struct mac_entry * mac_entry_from_lru_node(struct ovs_list *list) { return CONTAINER_OF(list, struct mac_entry, lru_node); } static struct mac_entry * mac_entry_lookup(const struct mac_learning *ml, const struct eth_addr mac, uint16_t vlan) { struct mac_entry *e; HMAP_FOR_EACH_WITH_HASH (e, hmap_node, mac_table_hash(ml, mac, vlan), &ml->table) { if (e->vlan == vlan && eth_addr_equals(e->mac, mac)) { return e; } } return NULL; } static struct mac_learning_port * mac_learning_port_lookup(struct mac_learning *ml, void *port) { struct mac_learning_port *mlport; HMAP_FOR_EACH_IN_BUCKET (mlport, hmap_node, hash_pointer(port, ml->secret), &ml->ports_by_ptr) { if (mlport->port == port) { return mlport; } } return NULL; } /* Changes the client-owned pointer for entry 'e' in 'ml' to 'port'. The * pointer can be retrieved with mac_entry_get_port(). * * The MAC-learning implementation treats the data that 'port' points to as * opaque and never tries to dereference it. However, when a MAC learning * table becomes overfull, so that eviction is required, the implementation * does first evict MAC entries for the most common 'port's values in 'ml', so * that there is a degree of fairness, that is, each port is entitled to its * fair share of MAC entries. */ void mac_entry_set_port(struct mac_learning *ml, struct mac_entry *e, void *port) OVS_REQ_WRLOCK(ml->rwlock) { if (mac_entry_get_port(ml, e) != port) { ml->need_revalidate = true; if (e->mlport) { struct mac_learning_port *mlport = e->mlport; list_remove(&e->port_lru_node); if (list_is_empty(&mlport->port_lrus)) { ovs_assert(mlport->heap_node.priority == 1); hmap_remove(&ml->ports_by_ptr, &mlport->hmap_node); heap_remove(&ml->ports_by_usage, &mlport->heap_node); free(mlport); } else { ovs_assert(mlport->heap_node.priority > 1); heap_change(&ml->ports_by_usage, &mlport->heap_node, mlport->heap_node.priority - 1); } e->mlport = NULL; } if (port) { struct mac_learning_port *mlport; mlport = mac_learning_port_lookup(ml, port); if (!mlport) { mlport = xzalloc(sizeof *mlport); hmap_insert(&ml->ports_by_ptr, &mlport->hmap_node, hash_pointer(port, ml->secret)); heap_insert(&ml->ports_by_usage, &mlport->heap_node, 1); mlport->port = port; list_init(&mlport->port_lrus); } else { heap_change(&ml->ports_by_usage, &mlport->heap_node, mlport->heap_node.priority + 1); } list_push_back(&mlport->port_lrus, &e->port_lru_node); e->mlport = mlport; } } } /* Finds one of the ports with the most MAC entries and evicts its least * recently used entry. */ static void evict_mac_entry_fairly(struct mac_learning *ml) OVS_REQ_WRLOCK(ml->rwlock) { struct mac_learning_port *mlport; struct mac_entry *e; mlport = CONTAINER_OF(heap_max(&ml->ports_by_usage), struct mac_learning_port, heap_node); e = CONTAINER_OF(list_front(&mlport->port_lrus), struct mac_entry, port_lru_node); mac_learning_expire(ml, e); } /* If the LRU list is not empty, stores the least-recently-used entry in '*e' * and returns true. Otherwise, if the LRU list is empty, stores NULL in '*e' * and return false. */ static bool get_lru(struct mac_learning *ml, struct mac_entry **e) OVS_REQ_RDLOCK(ml->rwlock) { if (!list_is_empty(&ml->lrus)) { *e = mac_entry_from_lru_node(ml->lrus.next); return true; } else { *e = NULL; return false; } } static unsigned int normalize_idle_time(unsigned int idle_time) { return (idle_time < 15 ? 15 : idle_time > 3600 ? 3600 : idle_time); } /* Creates and returns a new MAC learning table with an initial MAC aging * timeout of 'idle_time' seconds and an initial maximum of MAC_DEFAULT_MAX * entries. */ struct mac_learning * mac_learning_create(unsigned int idle_time) { struct mac_learning *ml; ml = xmalloc(sizeof *ml); list_init(&ml->lrus); hmap_init(&ml->table); ml->secret = random_uint32(); ml->flood_vlans = NULL; ml->idle_time = normalize_idle_time(idle_time); ml->max_entries = MAC_DEFAULT_MAX; ml->need_revalidate = false; hmap_init(&ml->ports_by_ptr); heap_init(&ml->ports_by_usage); ovs_refcount_init(&ml->ref_cnt); ovs_rwlock_init(&ml->rwlock); return ml; } struct mac_learning * mac_learning_ref(const struct mac_learning *ml_) { struct mac_learning *ml = CONST_CAST(struct mac_learning *, ml_); if (ml) { ovs_refcount_ref(&ml->ref_cnt); } return ml; } /* Unreferences (and possibly destroys) MAC learning table 'ml'. */ void mac_learning_unref(struct mac_learning *ml) { if (ml && ovs_refcount_unref(&ml->ref_cnt) == 1) { struct mac_entry *e, *next; ovs_rwlock_wrlock(&ml->rwlock); HMAP_FOR_EACH_SAFE (e, next, hmap_node, &ml->table) { mac_learning_expire(ml, e); } hmap_destroy(&ml->table); hmap_destroy(&ml->ports_by_ptr); heap_destroy(&ml->ports_by_usage); bitmap_free(ml->flood_vlans); ovs_rwlock_unlock(&ml->rwlock); ovs_rwlock_destroy(&ml->rwlock); free(ml); } } /* Provides a bitmap of VLANs which have learning disabled, that is, VLANs on * which all packets are flooded. Returns true if the set has changed from the * previous value. */ bool mac_learning_set_flood_vlans(struct mac_learning *ml, const unsigned long *bitmap) { if (vlan_bitmap_equal(ml->flood_vlans, bitmap)) { return false; } else { bitmap_free(ml->flood_vlans); ml->flood_vlans = vlan_bitmap_clone(bitmap); return true; } } /* Changes the MAC aging timeout of 'ml' to 'idle_time' seconds. */ void mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time) { idle_time = normalize_idle_time(idle_time); if (idle_time != ml->idle_time) { struct mac_entry *e; int delta; delta = (int) idle_time - (int) ml->idle_time; LIST_FOR_EACH (e, lru_node, &ml->lrus) { e->expires += delta; } ml->idle_time = idle_time; } } /* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it * to be within a reasonable range. */ void mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries) { ml->max_entries = (max_entries < 10 ? 10 : max_entries > 1000 * 1000 ? 1000 * 1000 : max_entries); } static bool is_learning_vlan(const struct mac_learning *ml, uint16_t vlan) { return !ml->flood_vlans || !bitmap_is_set(ml->flood_vlans, vlan); } /* Returns true if 'src_mac' may be learned on 'vlan' for 'ml'. * Returns false if 'ml' is NULL, if src_mac is not valid for learning, or if * 'vlan' is configured on 'ml' to flood all packets. */ bool mac_learning_may_learn(const struct mac_learning *ml, const struct eth_addr src_mac, uint16_t vlan) { return ml && is_learning_vlan(ml, vlan) && !eth_addr_is_multicast(src_mac); } /* Searches 'ml' for and returns a MAC learning entry for 'src_mac' in 'vlan', * inserting a new entry if necessary. The caller must have already verified, * by calling mac_learning_may_learn(), that 'src_mac' and 'vlan' are * learnable. * * If the returned MAC entry is new (that is, if it has a NULL client-provided * port, as returned by mac_entry_get_port()), then the caller must initialize * the new entry's port to a nonnull value with mac_entry_set_port(). */ struct mac_entry * mac_learning_insert(struct mac_learning *ml, const struct eth_addr src_mac, uint16_t vlan) { struct mac_entry *e; e = mac_entry_lookup(ml, src_mac, vlan); if (!e) { uint32_t hash = mac_table_hash(ml, src_mac, vlan); if (hmap_count(&ml->table) >= ml->max_entries) { evict_mac_entry_fairly(ml); } e = xmalloc(sizeof *e); hmap_insert(&ml->table, &e->hmap_node, hash); e->mac = src_mac; e->vlan = vlan; e->grat_arp_lock = TIME_MIN; e->mlport = NULL; COVERAGE_INC(mac_learning_learned); } else { list_remove(&e->lru_node); } /* Mark 'e' as recently used. */ list_push_back(&ml->lrus, &e->lru_node); if (e->mlport) { list_remove(&e->port_lru_node); list_push_back(&e->mlport->port_lrus, &e->port_lru_node); } e->expires = time_now() + ml->idle_time; return e; } /* Looks up MAC 'dst' for VLAN 'vlan' in 'ml' and returns the associated MAC * learning entry, if any. */ struct mac_entry * mac_learning_lookup(const struct mac_learning *ml, const struct eth_addr dst, uint16_t vlan) { if (eth_addr_is_multicast(dst)) { /* No tag because the treatment of multicast destinations never * changes. */ return NULL; } else if (!is_learning_vlan(ml, vlan)) { /* We don't tag this property. The set of learning VLANs changes so * rarely that we revalidate every flow when it changes. */ return NULL; } else { struct mac_entry *e = mac_entry_lookup(ml, dst, vlan); ovs_assert(e == NULL || mac_entry_get_port(ml, e) != NULL); return e; } } /* Expires 'e' from the 'ml' hash table. */ void mac_learning_expire(struct mac_learning *ml, struct mac_entry *e) { ml->need_revalidate = true; mac_entry_set_port(ml, e, NULL); hmap_remove(&ml->table, &e->hmap_node); list_remove(&e->lru_node); free(e); } /* Expires all the mac-learning entries in 'ml'. */ void mac_learning_flush(struct mac_learning *ml) { struct mac_entry *e; while (get_lru(ml, &e)){ mac_learning_expire(ml, e); } hmap_shrink(&ml->table); } /* Does periodic work required by 'ml'. Returns true if something changed that * may require flow revalidation. */ bool mac_learning_run(struct mac_learning *ml) { bool need_revalidate; struct mac_entry *e; while (get_lru(ml, &e) && (hmap_count(&ml->table) > ml->max_entries || time_now() >= e->expires)) { COVERAGE_INC(mac_learning_expired); mac_learning_expire(ml, e); } need_revalidate = ml->need_revalidate; ml->need_revalidate = false; return need_revalidate; } void mac_learning_wait(struct mac_learning *ml) { if (hmap_count(&ml->table) > ml->max_entries || ml->need_revalidate) { poll_immediate_wake(); } else if (!list_is_empty(&ml->lrus)) { struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next); poll_timer_wait_until(e->expires * 1000LL); } } openvswitch-2.5.9/lib/PaxHeaders.82075/process.h0000644000000000000000000000013213534540071016241 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.853853956 openvswitch-2.5.9/lib/process.h0000644000175000017500000000314313534540071017730 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PROCESS_H #define PROCESS_H 1 #include #include struct process; /* Starting and monitoring subprocesses. * * process_init() and process_start() may safely be called only from a * single-threaded parent process. The parent process may safely create * additional threads afterward, as long as the remaining functions in this * group are called only from a single thread at any given time. */ void process_init(void); int process_start(char **argv, struct process **); void process_destroy(struct process *); int process_kill(const struct process *, int signr); pid_t process_pid(const struct process *); const char *process_name(const struct process *); bool process_exited(struct process *); int process_status(const struct process *); void process_run(void); void process_wait(struct process *); /* These functions are thread-safe. */ char *process_status_msg(int); char *process_escape_args(char **argv); char *process_search_path(const char *); #endif /* process.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/crc32c.h0000644000000000000000000000013213534540071015642 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.685852718 openvswitch-2.5.9/lib/crc32c.h0000644000175000017500000000141213534540071017326 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 The University of Waikato. * Author: Joe Stringer * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CRC32C_H #define CRC32C_H 1 #include "openvswitch/types.h" ovs_be32 crc32c(const uint8_t *data, size_t); #endif /* crc32c.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sort.h0000644000000000000000000000013213534540071015552 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.885854193 openvswitch-2.5.9/lib/sort.h0000644000175000017500000000150013534540071017234 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SORT_H #define SORT_H 1 #include void sort(size_t count, int (*compare)(size_t a, size_t b, void *aux), void (*swap)(size_t a, size_t b, void *aux), void *aux); #endif /* sort.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vswitch-idl.ann0000644000000000000000000000013213534540071017345 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.793846143 openvswitch-2.5.9/lib/vswitch-idl.ann0000644000175000017500000000054013534540071021032 0ustar00jpettitjpettit00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates vswitch.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "ovsrec_" s["idlHeader"] = "\"lib/vswitch-idl.h\"" openvswitch-2.5.9/lib/PaxHeaders.82075/if-notifier-stub.c0000644000000000000000000000013213534540071017744 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.961854753 openvswitch-2.5.9/lib/if-notifier-stub.c0000644000175000017500000000165513534540071021441 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "if-notifier.h" #include #include "compiler.h" struct if_notifier * if_notifier_create(if_notify_func *cb OVS_UNUSED, void *aux OVS_UNUSED) { return NULL; } void if_notifier_destroy(struct if_notifier *notifier OVS_UNUSED) { } void if_notifier_run(void) { } void if_notifier_wait(void) { } openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-parser.h0000644000000000000000000000013213534540071017172 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.841853868 openvswitch-2.5.9/lib/ovsdb-parser.h0000644000175000017500000000627313534540071020670 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_PARSER_H #define OVSDB_PARSER_H 1 #include #include "compiler.h" #include "json.h" #include "sset.h" #include "util.h" struct ovsdb_parser { char *name; /* Used only in error messages. */ struct sset used; /* Already-parsed names from 'object'. */ const struct json *json; /* JSON object being parsed. */ struct ovsdb_error *error; /* Error signaled, if any. */ }; /* Check that the JSON types make the bitwise tricks below work OK. */ BUILD_ASSERT_DECL(JSON_NULL >= 0 && JSON_NULL < 10); BUILD_ASSERT_DECL(JSON_FALSE >= 0 && JSON_FALSE < 10); BUILD_ASSERT_DECL(JSON_TRUE >= 0 && JSON_TRUE < 10); BUILD_ASSERT_DECL(JSON_OBJECT >= 0 && JSON_OBJECT < 10); BUILD_ASSERT_DECL(JSON_ARRAY >= 0 && JSON_ARRAY < 10); BUILD_ASSERT_DECL(JSON_INTEGER >= 0 && JSON_INTEGER < 10); BUILD_ASSERT_DECL(JSON_REAL >= 0 && JSON_REAL < 10); BUILD_ASSERT_DECL(JSON_STRING >= 0 && JSON_STRING < 10); BUILD_ASSERT_DECL(JSON_N_TYPES == 8); enum ovsdb_parser_types { OP_NULL = 1 << JSON_NULL, /* null */ OP_FALSE = 1 << JSON_FALSE, /* false */ OP_TRUE = 1 << JSON_TRUE, /* true */ OP_OBJECT = 1 << JSON_OBJECT, /* {"a": b, "c": d, ...} */ OP_ARRAY = 1 << JSON_ARRAY, /* [1, 2, 3, ...] */ OP_INTEGER = 1 << JSON_INTEGER, /* 123. */ OP_NONINTEGER = 1 << JSON_REAL, /* 123.456. */ OP_STRING = 1 << JSON_STRING, /* "..." */ OP_ANY = (OP_NULL | OP_FALSE | OP_TRUE | OP_OBJECT | OP_ARRAY | OP_INTEGER | OP_NONINTEGER | OP_STRING), OP_BOOLEAN = OP_FALSE | OP_TRUE, OP_NUMBER = OP_INTEGER | OP_NONINTEGER, OP_ID = 1 << JSON_N_TYPES, /* "[_a-zA-Z][_a-zA-Z0-9]*" */ OP_OPTIONAL = 1 << (JSON_N_TYPES + 1) /* no value at all */ }; void ovsdb_parser_init(struct ovsdb_parser *, const struct json *, const char *name, ...) OVS_PRINTF_FORMAT(3, 4); const struct json *ovsdb_parser_member(struct ovsdb_parser *, const char *name, enum ovsdb_parser_types); void ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...) OVS_PRINTF_FORMAT(2, 3); bool ovsdb_parser_has_error(const struct ovsdb_parser *); struct ovsdb_error *ovsdb_parser_get_error(const struct ovsdb_parser *); struct ovsdb_error *ovsdb_parser_finish(struct ovsdb_parser *) OVS_WARN_UNUSED_RESULT; struct ovsdb_error *ovsdb_parser_destroy(struct ovsdb_parser *) OVS_WARN_UNUSED_RESULT; bool ovsdb_parser_is_id(const char *string); #endif /* ovsdb-parser.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/syslog-direct.c0000644000000000000000000000013213534540071017346 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.901854311 openvswitch-2.5.9/lib/syslog-direct.c0000644000175000017500000000647613534540071021051 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "syslog-direct.h" #include #include #include #include "compiler.h" #include "dynamic-string.h" #include "socket-util.h" #include "syslog-provider.h" #include "util.h" #define FACILITY_MASK 0x03f8 static void syslog_direct_open(struct syslogger *this, int facility); static void syslog_direct_log(struct syslogger *this, int pri, const char *msg); static struct syslog_class syslog_direct_class = { syslog_direct_open, syslog_direct_log, }; struct syslog_direct { struct syslogger parent; int fd; /* Negative number in error case. Otherwise, socket. */ int facility; }; /* This function creates object that directly interacts with syslog over * UDP or Unix domain socket specified in 'method'. */ struct syslogger * syslog_direct_create(const char *method) { struct syslog_direct *this = xmalloc(sizeof *this); this->parent.class = &syslog_direct_class; this->parent.prefix = "<%B>"; /* socket is created from here (opposed to syslog_direct_open()) * so that deadlocks would be avoided. The problem is that these * functions that create socket might call VLOG() */ if (!strncmp(method, "udp:", 4)) { inet_open_active(SOCK_DGRAM, &method[4], 514, NULL, &this->fd, 0); } else if (!strncmp(method, "unix:", 5)) { this->fd = make_unix_socket(SOCK_DGRAM, true, NULL, &method[5]); } else { this->fd = -1; } return &this->parent; } static void syslog_direct_open(struct syslogger *this, int facility) { struct syslog_direct *this_ = (struct syslog_direct*) this; this_->facility = facility; } static void syslog_direct_log(struct syslogger *this, int pri, const char *msg) { static size_t max_len = SIZE_MAX; /* max message size we have discovered * to be able to send() without failing * with EMSGSIZE. */ struct syslog_direct *this_ = (struct syslog_direct*) this; struct ds ds = DS_EMPTY_INITIALIZER; const char *wire_msg; size_t send_len; if (this_->fd < 0) { /* Failed to open socket for logging. */ return; } if (!(pri & FACILITY_MASK)) { pri |= this_->facility; } ds_put_format(&ds, "<%u>%s", pri, msg); wire_msg = ds_cstr(&ds); send_len = MIN(strlen(wire_msg), max_len); while (send(this_->fd, wire_msg, send_len, 0) < 0 && errno == EMSGSIZE) { /* If message was too large for send() function then try to discover * max_len supported for this particular socket and retry sending a * truncated version of the same message. */ send_len -= send_len / 20; max_len = send_len; } ds_destroy(&ds); } openvswitch-2.5.9/lib/PaxHeaders.82075/timeval.h0000644000000000000000000000013213534540071016224 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.913854399 openvswitch-2.5.9/lib/timeval.h0000644000175000017500000000443613534540071017721 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TIMEVAL_H #define TIMEVAL_H 1 #include #include "type-props.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif struct ds; struct pollfd; struct timespec; struct timeval; /* POSIX allows floating-point time_t, but we don't support it. */ BUILD_ASSERT_DECL(TYPE_IS_INTEGER(time_t)); /* We do try to cater to unsigned time_t, but I want to know about it if we * ever encounter such a platform. */ BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t)); #define TIME_MAX TYPE_MAXIMUM(time_t) #define TIME_MIN TYPE_MINIMUM(time_t) #ifdef _WIN32 #define localtime_r(timep, result) localtime_s(result, timep) #define gmtime_r(timep, result) gmtime_s(result, timep) #endif /* _WIN32 */ struct tm_msec { struct tm tm; int msec; }; time_t time_now(void); time_t time_wall(void); long long int time_msec(void); long long int time_wall_msec(void); void time_timespec(struct timespec *); void time_wall_timespec(struct timespec *); void time_alarm(unsigned int secs); int time_poll(struct pollfd *, int n_pollfds, HANDLE *handles, long long int timeout_when, int *elapsed); long long int timespec_to_msec(const struct timespec *); long long int timeval_to_msec(const struct timeval *); struct tm_msec *localtime_msec(long long int now, struct tm_msec *result); struct tm_msec *gmtime_msec(long long int now, struct tm_msec *result); size_t strftime_msec(char *s, size_t max, const char *format, const struct tm_msec *); void xgettimeofday(struct timeval *); void xclock_gettime(clock_t, struct timespec *); int get_cpu_usage(void); long long int time_boot_msec(void); void timewarp_run(void); #ifdef __cplusplus } #endif #endif /* timeval.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/uuid.h0000644000000000000000000000013213534540071015531 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.937854576 openvswitch-2.5.9/lib/uuid.h0000644000175000017500000000536513534540071017230 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UUID_H #define UUID_H 1 #include #include #include #include "util.h" #define UUID_BIT 128 /* Number of bits in a UUID. */ #define UUID_OCTET (UUID_BIT / 8) /* Number of bytes in a UUID. */ /* A Universally Unique IDentifier (UUID) compliant with RFC 4122. * * Each of the parts is stored in host byte order, but the parts themselves are * ordered from left to right. That is, (parts[0] >> 24) is the first 8 bits * of the UUID when output in the standard form, and (parts[3] & 0xff) is the * final 8 bits. */ struct uuid { uint32_t parts[4]; }; BUILD_ASSERT_DECL(sizeof(struct uuid) == UUID_OCTET); /* Formats a UUID as a string, in the conventional format. * * Example: * struct uuid uuid = ...; * printf("This UUID is "UUID_FMT"\n", UUID_ARGS(&uuid)); * */ #define UUID_LEN 36 #define UUID_FMT "%08x-%04x-%04x-%04x-%04x%08x" #define UUID_ARGS(UUID) \ ((unsigned int) ((UUID)->parts[0])), \ ((unsigned int) ((UUID)->parts[1] >> 16)), \ ((unsigned int) ((UUID)->parts[1] & 0xffff)), \ ((unsigned int) ((UUID)->parts[2] >> 16)), \ ((unsigned int) ((UUID)->parts[2] & 0xffff)), \ ((unsigned int) ((UUID)->parts[3])) /* Returns a hash value for 'uuid'. This hash value is the same regardless of * whether we are running on a 32-bit or 64-bit or big-endian or little-endian * architecture. */ static inline size_t uuid_hash(const struct uuid *uuid) { return uuid->parts[0]; } /* Returns true if 'a == b', false otherwise. */ static inline bool uuid_equals(const struct uuid *a, const struct uuid *b) { return (a->parts[0] == b->parts[0] && a->parts[1] == b->parts[1] && a->parts[2] == b->parts[2] && a->parts[3] == b->parts[3]); } void uuid_init(void); void uuid_generate(struct uuid *); void uuid_zero(struct uuid *); bool uuid_is_zero(const struct uuid *); int uuid_compare_3way(const struct uuid *, const struct uuid *); bool uuid_from_string(struct uuid *, const char *); bool uuid_from_string_prefix(struct uuid *, const char *); void uuid_set_bits_v4(struct uuid *); #endif /* uuid.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/multipath.c0000644000000000000000000000013213534540071016565 xustar0030 mtime=1567801401.421681316 30 atime=1567801402.077686135 30 ctime=1567801424.769853338 openvswitch-2.5.9/lib/multipath.c0000644000175000017500000001711513534540071020260 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "multipath.h" #include #include #include #include #include "dynamic-string.h" #include "nx-match.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/nicira-ext.h" #include "packets.h" /* Checks that 'mp' is valid on flow. Returns 0 if it is valid, otherwise an * OFPERR_*. */ enum ofperr multipath_check(const struct ofpact_multipath *mp, const struct flow *flow) { return mf_check_dst(&mp->dst, flow); } /* multipath_execute(). */ static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm, unsigned int n_links, unsigned int arg); /* Executes 'mp' based on the current contents of 'flow', writing the results * back into 'flow'. Sets fields in 'wc' that were used to calculate * the result. */ void multipath_execute(const struct ofpact_multipath *mp, struct flow *flow, struct flow_wildcards *wc) { /* Calculate value to store. */ uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis); uint16_t link = multipath_algorithm(hash, mp->algorithm, mp->max_link + 1, mp->arg); flow_mask_hash_fields(flow, wc, mp->fields); nxm_reg_load(&mp->dst, link, flow, wc); } static uint16_t algorithm_hrw(uint32_t hash, unsigned int n_links) { uint32_t best_weight; uint16_t best_link; unsigned int link; best_link = 0; best_weight = hash_2words(hash, 0); for (link = 1; link < n_links; link++) { uint32_t weight = hash_2words(hash, link); if (weight > best_weight) { best_link = link; best_weight = weight; } } return best_link; } /* Works for 'x' in the range [1,65536], which is all we need. */ static unsigned int round_up_pow2(unsigned int x) { x--; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; return x + 1; } static uint16_t algorithm_iter_hash(uint32_t hash, unsigned int n_links, unsigned int modulo) { uint16_t link; int i; if (modulo < n_links || modulo / 2 > n_links) { modulo = round_up_pow2(n_links); } i = 0; do { link = hash_2words(hash, i++) % modulo; } while (link >= n_links); return link; } static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm, unsigned int n_links, unsigned int arg) { switch (algorithm) { case NX_MP_ALG_MODULO_N: return hash % n_links; case NX_MP_ALG_HASH_THRESHOLD: if (n_links == 1) { return 0; } return hash / (UINT32_MAX / n_links + 1); case NX_MP_ALG_HRW: return (n_links <= 64 ? algorithm_hrw(hash, n_links) : algorithm_iter_hash(hash, n_links, 0)); case NX_MP_ALG_ITER_HASH: return algorithm_iter_hash(hash, n_links, arg); } OVS_NOT_REACHED(); } /* Parses 's_' as a set of arguments to the "multipath" action and initializes * 'mp' accordingly. ovs-ofctl(8) describes the format parsed. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string.*/ static char * OVS_WARN_UNUSED_RESULT multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s) { char *save_ptr = NULL; char *fields, *basis, *algorithm, *n_links_str, *arg, *dst; char *error; int n_links; fields = strtok_r(s, ", ", &save_ptr); basis = strtok_r(NULL, ", ", &save_ptr); algorithm = strtok_r(NULL, ", ", &save_ptr); n_links_str = strtok_r(NULL, ", ", &save_ptr); arg = strtok_r(NULL, ", ", &save_ptr); dst = strtok_r(NULL, ", ", &save_ptr); if (!dst) { return xasprintf("%s: not enough arguments to multipath action", s_); } ofpact_init_MULTIPATH(mp); if (!strcasecmp(fields, "eth_src")) { mp->fields = NX_HASH_FIELDS_ETH_SRC; } else if (!strcasecmp(fields, "symmetric_l4")) { mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4; } else if (!strcasecmp(fields, "symmetric_l3l4")) { mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4; } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) { mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP; } else { return xasprintf("%s: unknown fields `%s'", s_, fields); } mp->basis = atoi(basis); if (!strcasecmp(algorithm, "modulo_n")) { mp->algorithm = NX_MP_ALG_MODULO_N; } else if (!strcasecmp(algorithm, "hash_threshold")) { mp->algorithm = NX_MP_ALG_HASH_THRESHOLD; } else if (!strcasecmp(algorithm, "hrw")) { mp->algorithm = NX_MP_ALG_HRW; } else if (!strcasecmp(algorithm, "iter_hash")) { mp->algorithm = NX_MP_ALG_ITER_HASH; } else { return xasprintf("%s: unknown algorithm `%s'", s_, algorithm); } n_links = atoi(n_links_str); if (n_links < 1 || n_links > 65536) { return xasprintf("%s: n_links %d is not in valid range 1 to 65536", s_, n_links); } mp->max_link = n_links - 1; mp->arg = atoi(arg); error = mf_parse_subfield(&mp->dst, dst); if (error) { return error; } if (!mf_nxm_header(mp->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", s, dst); } if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) { return xasprintf("%s: %d-bit destination field has %u possible " "values, less than specified n_links %d", s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links); } return NULL; } /* Parses 's_' as a set of arguments to the "multipath" action and initializes * 'mp' accordingly. ovs-ofctl(8) describes the format parsed. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT multipath_parse(struct ofpact_multipath *mp, const char *s_) { char *s = xstrdup(s_); char *error = multipath_parse__(mp, s_, s); free(s); return error; } /* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8) * describes. */ void multipath_format(const struct ofpact_multipath *mp, struct ds *s) { const char *fields, *algorithm; fields = flow_hash_fields_to_str(mp->fields); switch (mp->algorithm) { case NX_MP_ALG_MODULO_N: algorithm = "modulo_n"; break; case NX_MP_ALG_HASH_THRESHOLD: algorithm = "hash_threshold"; break; case NX_MP_ALG_HRW: algorithm = "hrw"; break; case NX_MP_ALG_ITER_HASH: algorithm = "iter_hash"; break; default: algorithm = ""; } ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",", fields, mp->basis, algorithm, mp->max_link + 1, mp->arg); mf_format_subfield(&mp->dst, s); ds_put_char(s, ')'); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-types.h0000644000000000000000000000013213534540071017042 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.841853868 openvswitch-2.5.9/lib/ovsdb-types.h0000644000175000017500000002032613534540071020533 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_TYPES_H #define OVSDB_TYPES_H 1 #include #include #include #include "compiler.h" #include "uuid.h" struct json; /* An atomic type: one that OVSDB regards as a single unit of data. */ enum ovsdb_atomic_type { OVSDB_TYPE_VOID, /* No value. */ OVSDB_TYPE_INTEGER, /* Signed 64-bit integer. */ OVSDB_TYPE_REAL, /* IEEE 754 double-precision floating point. */ OVSDB_TYPE_BOOLEAN, /* True or false. */ OVSDB_TYPE_STRING, /* UTF-8 string. */ OVSDB_TYPE_UUID, /* RFC 4122 UUID referencing a table row. */ OVSDB_N_TYPES }; static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type); bool ovsdb_atomic_type_from_string(const char *, enum ovsdb_atomic_type *); struct ovsdb_error *ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *, const struct json *); const char *ovsdb_atomic_type_to_string(enum ovsdb_atomic_type); struct json *ovsdb_atomic_type_to_json(enum ovsdb_atomic_type); /* An atomic type plus optional constraints. */ enum ovsdb_ref_type { OVSDB_REF_STRONG, /* Target must exist. */ OVSDB_REF_WEAK /* Delete reference if target disappears. */ }; struct ovsdb_base_type { enum ovsdb_atomic_type type; /* If nonnull, a datum with keys of type 'type' that expresses all the * valid values for this base_type. */ struct ovsdb_datum *enum_; union { struct ovsdb_integer_constraints { int64_t min; /* minInteger or INT64_MIN. */ int64_t max; /* maxInteger or INT64_MAX. */ } integer; struct ovsdb_real_constraints { double min; /* minReal or -DBL_MAX. */ double max; /* minReal or DBL_MAX. */ } real; /* No constraints for Boolean types. */ struct ovsdb_string_constraints { unsigned int minLen; /* minLength or 0. */ unsigned int maxLen; /* maxLength or UINT_MAX. */ } string; struct ovsdb_uuid_constraints { char *refTableName; /* Name of referenced table, or NULL. */ struct ovsdb_table *refTable; /* Referenced table, if available. */ enum ovsdb_ref_type refType; /* Reference type. */ } uuid; } u; }; #define OVSDB_BASE_VOID_INIT { .type = OVSDB_TYPE_VOID } #define OVSDB_BASE_INTEGER_INIT { .type = OVSDB_TYPE_INTEGER, \ .u.integer = { INT64_MIN, INT64_MAX } } #define OVSDB_BASE_REAL_INIT { .type = OVSDB_TYPE_REAL, \ .u.real = { -DBL_MAX, DBL_MAX } } #define OVSDB_BASE_BOOLEAN_INIT { .type = OVSDB_TYPE_BOOLEAN } #define OVSDB_BASE_STRING_INIT { .type = OVSDB_TYPE_STRING, \ .u.string = { 0, UINT_MAX } } #define OVSDB_BASE_UUID_INIT { .type = OVSDB_TYPE_UUID, \ .u.uuid = { NULL, NULL, 0 } } void ovsdb_base_type_init(struct ovsdb_base_type *, enum ovsdb_atomic_type); void ovsdb_base_type_clone(struct ovsdb_base_type *, const struct ovsdb_base_type *); void ovsdb_base_type_destroy(struct ovsdb_base_type *); bool ovsdb_base_type_is_valid(const struct ovsdb_base_type *); bool ovsdb_base_type_has_constraints(const struct ovsdb_base_type *); void ovsdb_base_type_clear_constraints(struct ovsdb_base_type *); const struct ovsdb_type *ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type); struct ovsdb_error *ovsdb_base_type_from_json(struct ovsdb_base_type *, const struct json *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_base_type_to_json(const struct ovsdb_base_type *); static inline bool ovsdb_base_type_is_ref(const struct ovsdb_base_type *); static inline bool ovsdb_base_type_is_strong_ref( const struct ovsdb_base_type *); static inline bool ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *); /* An OVSDB type. * * Several rules constrain the valid types. See ovsdb_type_is_valid() (in * ovsdb-types.c) for details. * * If 'value_type' is OVSDB_TYPE_VOID, 'n_min' is 1, and 'n_max' is 1, then the * type is a single atomic 'key_type'. * * If 'value_type' is OVSDB_TYPE_VOID and 'n_min' or 'n_max' (or both) has a * value other than 1, then the type is a set of 'key_type'. If 'n_min' is 0 * and 'n_max' is 1, then the type can also be considered an optional * 'key_type'. * * If 'value_type' is not OVSDB_TYPE_VOID, then the type is a map from * 'key_type' to 'value_type'. If 'n_min' is 0 and 'n_max' is 1, then the type * can also be considered an optional pair of 'key_type' and 'value_type'. */ struct ovsdb_type { struct ovsdb_base_type key; struct ovsdb_base_type value; unsigned int n_min; unsigned int n_max; /* UINT_MAX stands in for "unlimited". */ }; #define OVSDB_TYPE_SCALAR_INITIALIZER(KEY) { KEY, OVSDB_BASE_VOID_INIT, 1, 1 } extern const struct ovsdb_type ovsdb_type_integer; extern const struct ovsdb_type ovsdb_type_real; extern const struct ovsdb_type ovsdb_type_boolean; extern const struct ovsdb_type ovsdb_type_string; extern const struct ovsdb_type ovsdb_type_uuid; void ovsdb_type_clone(struct ovsdb_type *, const struct ovsdb_type *); void ovsdb_type_destroy(struct ovsdb_type *); bool ovsdb_type_is_valid(const struct ovsdb_type *); static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *); static inline bool ovsdb_type_is_optional(const struct ovsdb_type *); static inline bool ovsdb_type_is_optional_scalar( const struct ovsdb_type *); static inline bool ovsdb_type_is_composite(const struct ovsdb_type *); static inline bool ovsdb_type_is_set(const struct ovsdb_type *); static inline bool ovsdb_type_is_map(const struct ovsdb_type *); char *ovsdb_type_to_english(const struct ovsdb_type *); struct ovsdb_error *ovsdb_type_from_json(struct ovsdb_type *, const struct json *) OVS_WARN_UNUSED_RESULT; struct json *ovsdb_type_to_json(const struct ovsdb_type *); /* Inline function implementations. */ static inline bool ovsdb_atomic_type_is_valid(enum ovsdb_atomic_type atomic_type) { return (int) atomic_type >= 0 && atomic_type < OVSDB_N_TYPES; } static inline bool ovsdb_base_type_is_ref(const struct ovsdb_base_type *base) { return base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName; } static inline bool ovsdb_base_type_is_strong_ref(const struct ovsdb_base_type *base) { return (ovsdb_base_type_is_ref(base) && base->u.uuid.refType == OVSDB_REF_STRONG); } static inline bool ovsdb_base_type_is_weak_ref(const struct ovsdb_base_type *base) { return (ovsdb_base_type_is_ref(base) && base->u.uuid.refType == OVSDB_REF_WEAK); } static inline bool ovsdb_type_is_scalar(const struct ovsdb_type *type) { return (type->value.type == OVSDB_TYPE_VOID && type->n_min == 1 && type->n_max == 1); } static inline bool ovsdb_type_is_optional(const struct ovsdb_type *type) { return type->n_min == 0; } static inline bool ovsdb_type_is_optional_scalar( const struct ovsdb_type *type) { return (type->value.type == OVSDB_TYPE_VOID && type->n_min == 0 && type->n_max == 1); } static inline bool ovsdb_type_is_composite(const struct ovsdb_type *type) { return type->n_max > 1; } static inline bool ovsdb_type_is_set(const struct ovsdb_type *type) { return (type->value.type == OVSDB_TYPE_VOID && (type->n_min != 1 || type->n_max != 1)); } static inline bool ovsdb_type_is_map(const struct ovsdb_type *type) { return type->value.type != OVSDB_TYPE_VOID; } #endif /* ovsdb-types.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dp-packet.h0000644000000000000000000000013013534540071016431 xustar0028 mtime=1567801401.3376807 30 atime=1567801402.069686075 30 ctime=1567801424.705852866 openvswitch-2.5.9/lib/dp-packet.h0000644000175000017500000004205513534540071020127 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DPBUF_H #define DPBUF_H 1 #include #include #include "list.h" #include "packets.h" #include "util.h" #include "netdev-dpdk.h" #ifdef __cplusplus extern "C" { #endif enum OVS_PACKED_ENUM dp_packet_source { DPBUF_MALLOC, /* Obtained via malloc(). */ DPBUF_STACK, /* Un-movable stack space or static buffer. */ DPBUF_STUB, /* Starts on stack, may expand into heap. */ DPBUF_DPDK, /* buffer data is from DPDK allocated memory. * ref to build_dp_packet() in netdev-dpdk. */ }; /* Buffer for holding packet data. A dp_packet is automatically reallocated * as necessary if it grows too large for the available memory. */ struct dp_packet { #ifdef DPDK_NETDEV struct rte_mbuf mbuf; /* DPDK mbuf */ #else void *base_; /* First byte of allocated space. */ uint16_t allocated_; /* Number of bytes allocated. */ uint16_t data_ofs; /* First byte actually in use. */ uint32_t size_; /* Number of bytes in use. */ uint32_t rss_hash; /* Packet hash. */ bool rss_hash_valid; /* Is the 'rss_hash' valid? */ #endif enum dp_packet_source source; /* Source of memory allocated as 'base'. */ uint8_t l2_pad_size; /* Detected l2 padding size. * Padding is non-pullable. */ uint16_t l2_5_ofs; /* MPLS label stack offset, or UINT16_MAX */ uint16_t l3_ofs; /* Network-level header offset, * or UINT16_MAX. */ uint16_t l4_ofs; /* Transport-level header offset, or UINT16_MAX. */ struct pkt_metadata md; }; static inline void *dp_packet_data(const struct dp_packet *); static inline void dp_packet_set_data(struct dp_packet *, void *); static inline void *dp_packet_base(const struct dp_packet *); static inline void dp_packet_set_base(struct dp_packet *, void *); static inline uint32_t dp_packet_size(const struct dp_packet *); static inline void dp_packet_set_size(struct dp_packet *, uint32_t); static inline uint16_t dp_packet_get_allocated(const struct dp_packet *); static inline void dp_packet_set_allocated(struct dp_packet *, uint16_t); void *dp_packet_resize_l2(struct dp_packet *, int increment); void *dp_packet_resize_l2_5(struct dp_packet *, int increment); static inline void *dp_packet_l2(const struct dp_packet *); static inline void dp_packet_reset_offsets(struct dp_packet *); static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *); static inline void dp_packet_set_l2_pad_size(struct dp_packet *, uint8_t); static inline void *dp_packet_l2_5(const struct dp_packet *); static inline void dp_packet_set_l2_5(struct dp_packet *, void *); static inline void *dp_packet_l3(const struct dp_packet *); static inline void dp_packet_set_l3(struct dp_packet *, void *); static inline void *dp_packet_l4(const struct dp_packet *); static inline void dp_packet_set_l4(struct dp_packet *, void *); static inline size_t dp_packet_l4_size(const struct dp_packet *); static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *); static inline const void *dp_packet_get_udp_payload(const struct dp_packet *); static inline const void *dp_packet_get_sctp_payload(const struct dp_packet *); static inline const void *dp_packet_get_icmp_payload(const struct dp_packet *); static inline const void *dp_packet_get_nd_payload(const struct dp_packet *); void dp_packet_use(struct dp_packet *, void *, size_t); void dp_packet_use_stub(struct dp_packet *, void *, size_t); void dp_packet_use_const(struct dp_packet *, const void *, size_t); void dp_packet_init_dpdk(struct dp_packet *, size_t allocated); void dp_packet_init(struct dp_packet *, size_t); void dp_packet_uninit(struct dp_packet *); struct dp_packet *dp_packet_new(size_t); struct dp_packet *dp_packet_new_with_headroom(size_t, size_t headroom); struct dp_packet *dp_packet_clone(const struct dp_packet *); struct dp_packet *dp_packet_clone_with_headroom(const struct dp_packet *, size_t headroom); struct dp_packet *dp_packet_clone_data(const void *, size_t); struct dp_packet *dp_packet_clone_data_with_headroom(const void *, size_t, size_t headroom); static inline void dp_packet_delete(struct dp_packet *); static inline void *dp_packet_at(const struct dp_packet *, size_t offset, size_t size); static inline void *dp_packet_at_assert(const struct dp_packet *, size_t offset, size_t size); static inline void *dp_packet_tail(const struct dp_packet *); static inline void *dp_packet_end(const struct dp_packet *); void *dp_packet_put_uninit(struct dp_packet *, size_t); void *dp_packet_put_zeros(struct dp_packet *, size_t); void *dp_packet_put(struct dp_packet *, const void *, size_t); char *dp_packet_put_hex(struct dp_packet *, const char *s, size_t *n); void dp_packet_reserve(struct dp_packet *, size_t); void dp_packet_reserve_with_tailroom(struct dp_packet *, size_t headroom, size_t tailroom); void *dp_packet_push_uninit(struct dp_packet *, size_t); void *dp_packet_push_zeros(struct dp_packet *, size_t); void *dp_packet_push(struct dp_packet *, const void *, size_t); static inline size_t dp_packet_headroom(const struct dp_packet *); static inline size_t dp_packet_tailroom(const struct dp_packet *); void dp_packet_prealloc_headroom(struct dp_packet *, size_t); void dp_packet_prealloc_tailroom(struct dp_packet *, size_t); void dp_packet_shift(struct dp_packet *, int); static inline void dp_packet_clear(struct dp_packet *); static inline void *dp_packet_pull(struct dp_packet *, size_t); static inline void *dp_packet_try_pull(struct dp_packet *, size_t); void *dp_packet_steal_data(struct dp_packet *); char *dp_packet_to_string(const struct dp_packet *, size_t maxbytes); static inline bool dp_packet_equal(const struct dp_packet *, const struct dp_packet *); /* Frees memory that 'b' points to, as well as 'b' itself. */ static inline void dp_packet_delete(struct dp_packet *b) { if (b) { if (b->source == DPBUF_DPDK) { /* If this dp_packet was allocated by DPDK it must have been * created as a dp_packet */ free_dpdk_buf((struct dp_packet*) b); return; } dp_packet_uninit(b); free(b); } } /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to * byte 'offset'. Otherwise, returns a null pointer. */ static inline void * dp_packet_at(const struct dp_packet *b, size_t offset, size_t size) { return offset + size <= dp_packet_size(b) ? (char *) dp_packet_data(b) + offset : NULL; } /* Returns a pointer to byte 'offset' in 'b', which must contain at least * 'offset + size' bytes of data. */ static inline void * dp_packet_at_assert(const struct dp_packet *b, size_t offset, size_t size) { ovs_assert(offset + size <= dp_packet_size(b)); return ((char *) dp_packet_data(b)) + offset; } /* Returns a pointer to byte following the last byte of data in use in 'b'. */ static inline void * dp_packet_tail(const struct dp_packet *b) { return (char *) dp_packet_data(b) + dp_packet_size(b); } /* Returns a pointer to byte following the last byte allocated for use (but * not necessarily in use) in 'b'. */ static inline void * dp_packet_end(const struct dp_packet *b) { return (char *) dp_packet_base(b) + dp_packet_get_allocated(b); } /* Returns the number of bytes of headroom in 'b', that is, the number of bytes * of unused space in dp_packet 'b' before the data that is in use. (Most * commonly, the data in a dp_packet is at its beginning, and thus the * dp_packet's headroom is 0.) */ static inline size_t dp_packet_headroom(const struct dp_packet *b) { return (char *) dp_packet_data(b) - (char *) dp_packet_base(b); } /* Returns the number of bytes that may be appended to the tail end of * dp_packet 'b' before the dp_packet must be reallocated. */ static inline size_t dp_packet_tailroom(const struct dp_packet *b) { return (char *) dp_packet_end(b) - (char *) dp_packet_tail(b); } /* Clears any data from 'b'. */ static inline void dp_packet_clear(struct dp_packet *b) { dp_packet_set_data(b, dp_packet_base(b)); dp_packet_set_size(b, 0); } /* Removes 'size' bytes from the head end of 'b', which must contain at least * 'size' bytes of data. Returns the first byte of data removed. */ static inline void * dp_packet_pull(struct dp_packet *b, size_t size) { void *data = dp_packet_data(b); ovs_assert(dp_packet_size(b) - dp_packet_l2_pad_size(b) >= size); dp_packet_set_data(b, (char *) dp_packet_data(b) + size); dp_packet_set_size(b, dp_packet_size(b) - size); return data; } /* If 'b' has at least 'size' bytes of data, removes that many bytes from the * head end of 'b' and returns the first byte removed. Otherwise, returns a * null pointer without modifying 'b'. */ static inline void * dp_packet_try_pull(struct dp_packet *b, size_t size) { return dp_packet_size(b) - dp_packet_l2_pad_size(b) >= size ? dp_packet_pull(b, size) : NULL; } static inline bool dp_packet_equal(const struct dp_packet *a, const struct dp_packet *b) { return dp_packet_size(a) == dp_packet_size(b) && !memcmp(dp_packet_data(a), dp_packet_data(b), dp_packet_size(a)); } /* Get the start of the Ethernet frame. 'l3_ofs' marks the end of the l2 * headers, so return NULL if it is not set. */ static inline void * dp_packet_l2(const struct dp_packet *b) { return (b->l3_ofs != UINT16_MAX) ? dp_packet_data(b) : NULL; } /* Resets all layer offsets. 'l3' offset must be set before 'l2' can be * retrieved. */ static inline void dp_packet_reset_offsets(struct dp_packet *b) { b->l2_pad_size = 0; b->l2_5_ofs = UINT16_MAX; b->l3_ofs = UINT16_MAX; b->l4_ofs = UINT16_MAX; } static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *b) { return b->l2_pad_size; } static inline void dp_packet_set_l2_pad_size(struct dp_packet *b, uint8_t pad_size) { ovs_assert(pad_size <= dp_packet_size(b)); b->l2_pad_size = pad_size; } static inline void * dp_packet_l2_5(const struct dp_packet *b) { return b->l2_5_ofs != UINT16_MAX ? (char *) dp_packet_data(b) + b->l2_5_ofs : NULL; } static inline void dp_packet_set_l2_5(struct dp_packet *b, void *l2_5) { b->l2_5_ofs = l2_5 ? (char *) l2_5 - (char *) dp_packet_data(b) : UINT16_MAX; } static inline void * dp_packet_l3(const struct dp_packet *b) { return b->l3_ofs != UINT16_MAX ? (char *) dp_packet_data(b) + b->l3_ofs : NULL; } static inline void dp_packet_set_l3(struct dp_packet *b, void *l3) { b->l3_ofs = l3 ? (char *) l3 - (char *) dp_packet_data(b) : UINT16_MAX; } static inline void * dp_packet_l4(const struct dp_packet *b) { return b->l4_ofs != UINT16_MAX ? (char *) dp_packet_data(b) + b->l4_ofs : NULL; } static inline void dp_packet_set_l4(struct dp_packet *b, void *l4) { b->l4_ofs = l4 ? (char *) l4 - (char *) dp_packet_data(b) : UINT16_MAX; } static inline size_t dp_packet_l4_size(const struct dp_packet *b) { return b->l4_ofs != UINT16_MAX ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b) - dp_packet_l2_pad_size(b) : 0; } static inline const void * dp_packet_get_tcp_payload(const struct dp_packet *b) { size_t l4_size = dp_packet_l4_size(b); if (OVS_LIKELY(l4_size >= TCP_HEADER_LEN)) { struct tcp_header *tcp = dp_packet_l4(b); int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4; if (OVS_LIKELY(tcp_len >= TCP_HEADER_LEN && tcp_len <= l4_size)) { return (const char *)tcp + tcp_len; } } return NULL; } static inline const void * dp_packet_get_udp_payload(const struct dp_packet *b) { return OVS_LIKELY(dp_packet_l4_size(b) >= UDP_HEADER_LEN) ? (const char *)dp_packet_l4(b) + UDP_HEADER_LEN : NULL; } static inline const void * dp_packet_get_sctp_payload(const struct dp_packet *b) { return OVS_LIKELY(dp_packet_l4_size(b) >= SCTP_HEADER_LEN) ? (const char *)dp_packet_l4(b) + SCTP_HEADER_LEN : NULL; } static inline const void * dp_packet_get_icmp_payload(const struct dp_packet *b) { return OVS_LIKELY(dp_packet_l4_size(b) >= ICMP_HEADER_LEN) ? (const char *)dp_packet_l4(b) + ICMP_HEADER_LEN : NULL; } static inline const void * dp_packet_get_nd_payload(const struct dp_packet *b) { return OVS_LIKELY(dp_packet_l4_size(b) >= ND_MSG_LEN) ? (const char *)dp_packet_l4(b) + ND_MSG_LEN : NULL; } #ifdef DPDK_NETDEV BUILD_ASSERT_DECL(offsetof(struct dp_packet, mbuf) == 0); static inline void * dp_packet_base(const struct dp_packet *b) { return b->mbuf.buf_addr; } static inline void dp_packet_set_base(struct dp_packet *b, void *d) { b->mbuf.buf_addr = d; } static inline uint32_t dp_packet_size(const struct dp_packet *b) { return b->mbuf.pkt_len; } static inline void dp_packet_set_size(struct dp_packet *b, uint32_t v) { /* netdev-dpdk does not currently support segmentation; consequently, for * all intents and purposes, 'data_len' (16 bit) and 'pkt_len' (32 bit) may * be used interchangably. * * On the datapath, it is expected that the size of packets * (and thus 'v') will always be <= UINT16_MAX; this means that there is no * loss of accuracy in assigning 'v' to 'data_len'. */ b->mbuf.data_len = (uint16_t)v; /* Current seg length. */ b->mbuf.pkt_len = v; /* Total length of all segments linked to * this segment. */ } static inline uint16_t __packet_data(const struct dp_packet *b) { return b->mbuf.data_off; } static inline void __packet_set_data(struct dp_packet *b, uint16_t v) { b->mbuf.data_off = v; } static inline uint16_t dp_packet_get_allocated(const struct dp_packet *b) { return b->mbuf.buf_len; } static inline void dp_packet_set_allocated(struct dp_packet *b, uint16_t s) { b->mbuf.buf_len = s; } #else static inline void * dp_packet_base(const struct dp_packet *b) { return b->base_; } static inline void dp_packet_set_base(struct dp_packet *b, void *d) { b->base_ = d; } static inline uint32_t dp_packet_size(const struct dp_packet *b) { return b->size_; } static inline void dp_packet_set_size(struct dp_packet *b, uint32_t v) { b->size_ = v; } static inline uint16_t __packet_data(const struct dp_packet *b) { return b->data_ofs; } static inline void __packet_set_data(struct dp_packet *b, uint16_t v) { b->data_ofs = v; } static inline uint16_t dp_packet_get_allocated(const struct dp_packet *b) { return b->allocated_; } static inline void dp_packet_set_allocated(struct dp_packet *b, uint16_t s) { b->allocated_ = s; } #endif static inline void * dp_packet_data(const struct dp_packet *b) { return __packet_data(b) != UINT16_MAX ? (char *) dp_packet_base(b) + __packet_data(b) : NULL; } static inline void dp_packet_set_data(struct dp_packet *b, void *data) { if (data) { __packet_set_data(b, (char *) data - (char *) dp_packet_base(b)); } else { __packet_set_data(b, UINT16_MAX); } } static inline void dp_packet_reset_packet(struct dp_packet *b, int off) { dp_packet_set_size(b, dp_packet_size(b) - off); dp_packet_set_data(b, ((unsigned char *) dp_packet_data(b) + off)); b->l2_5_ofs = b->l3_ofs = b->l4_ofs = UINT16_MAX; } /* Returns the RSS hash of the packet 'p'. Note that the returned value is * correct only if 'dp_packet_rss_valid(p)' returns true */ static inline uint32_t dp_packet_get_rss_hash(struct dp_packet *p) { #ifdef DPDK_NETDEV return p->mbuf.hash.rss; #else return p->rss_hash; #endif } static inline void dp_packet_set_rss_hash(struct dp_packet *p, uint32_t hash) { #ifdef DPDK_NETDEV p->mbuf.hash.rss = hash; p->mbuf.ol_flags |= PKT_RX_RSS_HASH; #else p->rss_hash = hash; p->rss_hash_valid = true; #endif } static inline bool dp_packet_rss_valid(struct dp_packet *p) { #ifdef DPDK_NETDEV return p->mbuf.ol_flags & PKT_RX_RSS_HASH; #else return p->rss_hash_valid; #endif } static inline void dp_packet_rss_invalidate(struct dp_packet *p) { #ifdef DPDK_NETDEV p->mbuf.ol_flags &= ~PKT_RX_RSS_HASH; #else p->rss_hash_valid = false; #endif } #ifdef __cplusplus } #endif #endif /* dp-packet.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/unaligned.h0000644000000000000000000000013213534540071016531 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.925854488 openvswitch-2.5.9/lib/unaligned.h0000644000175000017500000002054613534540071020226 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UNALIGNED_H #define UNALIGNED_H 1 #include #include "byte-order.h" #include "openvswitch/types.h" #include "type-props.h" #include "util.h" /* Public API. */ static inline uint16_t get_unaligned_u16(const uint16_t *); static inline uint32_t get_unaligned_u32(const uint32_t *); static inline void put_unaligned_u16(uint16_t *, uint16_t); static inline void put_unaligned_u32(uint32_t *, uint32_t); static inline void put_unaligned_u64(uint64_t *, uint64_t); static inline ovs_be16 get_unaligned_be16(const ovs_be16 *); static inline ovs_be32 get_unaligned_be32(const ovs_be32 *); static inline ovs_be64 get_unaligned_be64(const ovs_be64 *); static inline void put_unaligned_be16(ovs_be16 *, ovs_be16); static inline void put_unaligned_be32(ovs_be32 *, ovs_be32); static inline void put_unaligned_be64(ovs_be64 *, ovs_be64); /* uint64_t get_unaligned_u64(uint64_t *p); * * Returns the value of the possibly misaligned uint64_t at 'p'. 'p' may * actually be any type that points to a 64-bit integer. That is, on Unix-like * 32-bit ABIs, it may point to an "unsigned long long int", and on Unix-like * 64-bit ABIs, it may point to an "unsigned long int" or an "unsigned long * long int". * * This is special-cased because on some Linux targets, the kernel __u64 is * unsigned long long int and the userspace uint64_t is unsigned long int, so * that any single function prototype would fail to accept one or the other. * * Below, "sizeof (*(P) % 1)" verifies that *P has an integer type, since * operands to % must be integers. */ #define get_unaligned_u64(P) \ (BUILD_ASSERT(sizeof *(P) == 8), \ BUILD_ASSERT_GCCONLY(!TYPE_IS_SIGNED(typeof(*(P)))), \ (void) sizeof (*(P) % 1), \ get_unaligned_u64__((const uint64_t *) (P))) #ifdef __GNUC__ /* GCC implementations. */ #define GCC_UNALIGNED_ACCESSORS(TYPE, ABBREV) \ struct unaligned_##ABBREV { \ TYPE x __attribute__((__packed__)); \ }; \ static inline struct unaligned_##ABBREV * \ unaligned_##ABBREV(const TYPE *p) \ { \ return (struct unaligned_##ABBREV *) p; \ } \ \ static inline TYPE \ get_unaligned_##ABBREV(const TYPE *p) \ { \ return unaligned_##ABBREV(p)->x; \ } \ \ static inline void \ put_unaligned_##ABBREV(TYPE *p, TYPE x) \ { \ unaligned_##ABBREV(p)->x = x; \ } GCC_UNALIGNED_ACCESSORS(uint16_t, u16); GCC_UNALIGNED_ACCESSORS(uint32_t, u32); GCC_UNALIGNED_ACCESSORS(uint64_t, u64__); /* Special case: see below. */ GCC_UNALIGNED_ACCESSORS(ovs_be16, be16); GCC_UNALIGNED_ACCESSORS(ovs_be32, be32); GCC_UNALIGNED_ACCESSORS(ovs_be64, be64); #else /* Generic implementations. */ static inline uint16_t get_unaligned_u16(const uint16_t *p_) { const uint8_t *p = (const uint8_t *) p_; return ntohs((p[0] << 8) | p[1]); } static inline void put_unaligned_u16(uint16_t *p_, uint16_t x_) { uint8_t *p = (uint8_t *) p_; uint16_t x = ntohs(x_); p[0] = x >> 8; p[1] = x; } static inline uint32_t get_unaligned_u32(const uint32_t *p_) { const uint8_t *p = (const uint8_t *) p_; return ntohl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } static inline void put_unaligned_u32(uint32_t *p_, uint32_t x_) { uint8_t *p = (uint8_t *) p_; uint32_t x = ntohl(x_); p[0] = x >> 24; p[1] = x >> 16; p[2] = x >> 8; p[3] = x; } static inline uint64_t get_unaligned_u64__(const uint64_t *p_) { const uint8_t *p = (const uint8_t *) p_; return ntohll(((uint64_t) p[0] << 56) | ((uint64_t) p[1] << 48) | ((uint64_t) p[2] << 40) | ((uint64_t) p[3] << 32) | (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); } static inline void put_unaligned_u64__(uint64_t *p_, uint64_t x_) { uint8_t *p = (uint8_t *) p_; uint64_t x = ntohll(x_); p[0] = x >> 56; p[1] = x >> 48; p[2] = x >> 40; p[3] = x >> 32; p[4] = x >> 24; p[5] = x >> 16; p[6] = x >> 8; p[7] = x; } /* Only sparse cares about the difference between uint_t and ovs_be, and * that takes the GCC branch, so there's no point in working too hard on these * accessors. */ #define get_unaligned_be16 get_unaligned_u16 #define get_unaligned_be32 get_unaligned_u32 #define put_unaligned_be16 put_unaligned_u16 #define put_unaligned_be32 put_unaligned_u32 #define put_unaligned_be64 put_unaligned_u64 /* We do not #define get_unaligned_be64 as for the other be functions above, * because such a definition would mean that get_unaligned_be64() would have a * different interface in each branch of the #if: with GCC it would take a * "ovs_be64 *", with other compilers any pointer-to-64-bit-type (but not void * *). The latter means code like "get_unaligned_be64(ofpbuf_data(b))" would * work with GCC but not with other compilers, which is surprising and * undesirable. Hence this wrapper function. */ static inline ovs_be64 get_unaligned_be64(const ovs_be64 *p) { return get_unaligned_u64(p); } #endif /* Stores 'x' at possibly misaligned address 'p'. * * put_unaligned_u64() could be overloaded in the same way as * get_unaligned_u64(), but so far it has not proven necessary. */ static inline void put_unaligned_u64(uint64_t *p, uint64_t x) { put_unaligned_u64__(p, x); } /* Returns the value in 'x'. */ static inline uint32_t get_16aligned_u32(const ovs_16aligned_u32 *x) { return ((uint32_t) x->hi << 16) | x->lo; } /* Stores 'value' in 'x'. */ static inline void put_16aligned_u32(ovs_16aligned_u32 *x, uint32_t value) { x->hi = value >> 16; x->lo = value; } /* Returns the value in 'x'. */ static inline uint64_t get_32aligned_u64(const ovs_32aligned_u64 *x) { return ((uint64_t) x->hi << 32) | x->lo; } /* Stores 'value' in 'x'. */ static inline void put_32aligned_u64(ovs_32aligned_u64 *x, uint64_t value) { x->hi = value >> 32; x->lo = value; } #ifndef __CHECKER__ /* Returns the value of 'x'. */ static inline ovs_be32 get_16aligned_be32(const ovs_16aligned_be32 *x) { #ifdef WORDS_BIGENDIAN return ((ovs_be32) x->hi << 16) | x->lo; #else return ((ovs_be32) x->lo << 16) | x->hi; #endif } /* Stores network byte order 'value' into 'x'. */ static inline void put_16aligned_be32(ovs_16aligned_be32 *x, ovs_be32 value) { #if WORDS_BIGENDIAN x->hi = value >> 16; x->lo = value; #else x->hi = value; x->lo = value >> 16; #endif } /* Returns the value of 'x'. */ static inline ovs_be64 get_32aligned_be64(const ovs_32aligned_be64 *x) { #ifdef WORDS_BIGENDIAN return ((ovs_be64) x->hi << 32) | x->lo; #else return ((ovs_be64) x->lo << 32) | x->hi; #endif } /* Stores network byte order 'value' into 'x'. */ static inline void put_32aligned_be64(ovs_32aligned_be64 *x, ovs_be64 value) { #if WORDS_BIGENDIAN x->hi = value >> 32; x->lo = value; #else x->hi = value; x->lo = value >> 32; #endif } #else /* __CHECKER__ */ /* Making sparse happy with these functions also makes them unreadable, so * don't bother to show it their implementations. */ ovs_be32 get_16aligned_be32(const ovs_16aligned_be32 *); void put_16aligned_be32(ovs_16aligned_be32 *, ovs_be32); ovs_be64 get_32aligned_be64(const ovs_32aligned_be64 *); void put_32aligned_be64(ovs_32aligned_be64 *, ovs_be64); #endif #endif /* unaligned.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/strsep.c0000644000000000000000000000013213534540071016076 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.965854783 openvswitch-2.5.9/lib/strsep.c0000644000175000017500000000476413534540071017577 0ustar00jpettitjpettit00000000000000/*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "util.h" /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ #define _DIAGASSERT(q) ovs_assert(q) char * strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; _DIAGASSERT(stringp != NULL); _DIAGASSERT(delim != NULL); if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } openvswitch-2.5.9/lib/PaxHeaders.82075/smap.h0000644000000000000000000000013113534540071015522 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.881854163 openvswitch-2.5.9/lib/smap.h0000644000175000017500000000651013534540071017213 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2012, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SMAP_H #define SMAP_H 1 #include "hmap.h" struct json; struct uuid; /* A map from string to string. */ struct smap { struct hmap map; /* Contains "struct smap_node"s. */ }; struct smap_node { struct hmap_node node; /* In struct smap's 'map' hmap. */ char *key; char *value; }; #define SMAP_INITIALIZER(SMAP) { HMAP_INITIALIZER(&(SMAP)->map) } #define SMAP_FOR_EACH(SMAP_NODE, SMAP) \ HMAP_FOR_EACH (SMAP_NODE, node, &(SMAP)->map) #define SMAP_FOR_EACH_SAFE(SMAP_NODE, NEXT, SMAP) \ HMAP_FOR_EACH_SAFE (SMAP_NODE, NEXT, node, &(SMAP)->map) /* Initializer for an immutable struct smap 'SMAP' that contains a single * 'KEY'-'VALUE' pair, e.g. * * const struct smap smap = SMAP1_CONST1(&smap, "key", "value"); * * An smap initialized this way must not be modified or destroyed. * * The 'KEY' argument is evaluated multiple times. */ #define SMAP_CONST1(SMAP, KEY, VALUE) { \ HMAP_CONST1(&(SMAP)->map, \ (&(struct smap_node) SMAP_NODE_INIT(KEY, VALUE).node)) \ } #define SMAP_NODE_INIT(KEY, VALUE) { \ HMAP_NODE_INIT(hash_string(KEY, 0)), \ CONST_CAST(char *, KEY), \ CONST_CAST(char *, VALUE) } void smap_init(struct smap *); void smap_destroy(struct smap *); struct smap_node *smap_add(struct smap *, const char *, const char *); struct smap_node *smap_add_nocopy(struct smap *, char *, char *); bool smap_add_once(struct smap *, const char *, const char *); void smap_add_format(struct smap *, const char *key, const char *, ...) OVS_PRINTF_FORMAT(3, 4); void smap_add_ipv6(struct smap *, const char *, struct in6_addr *); void smap_replace(struct smap *, const char *, const char *); void smap_remove(struct smap *, const char *); void smap_remove_node(struct smap *, struct smap_node *); void smap_steal(struct smap *, struct smap_node *, char **keyp, char **valuep); void smap_clear(struct smap *); const char *smap_get(const struct smap *, const char *); struct smap_node *smap_get_node(const struct smap *, const char *); bool smap_get_bool(const struct smap *smap, const char *key, bool def); int smap_get_int(const struct smap *smap, const char *key, int def); bool smap_get_uuid(const struct smap *, const char *key, struct uuid *); bool smap_is_empty(const struct smap *); size_t smap_count(const struct smap *); void smap_clone(struct smap *dst, const struct smap *src); const struct smap_node **smap_sort(const struct smap *); void smap_from_json(struct smap *, const struct json *); struct json *smap_to_json(const struct smap *); bool smap_equal(const struct smap *, const struct smap *); #endif /* smap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/libopenvswitch.sym.in0000644000000000000000000000013113534540071020610 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.641852394 openvswitch-2.5.9/lib/libopenvswitch.sym.in0000644000175000017500000000006413534540071022277 0ustar00jpettitjpettit00000000000000libopenvswitch_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/lib/PaxHeaders.82075/dhparams.h0000644000000000000000000000013213534540071016362 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.701852836 openvswitch-2.5.9/lib/dhparams.h0000644000175000017500000000136713534540071020057 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DHPARAMS_H #define DHPARAMS_H 1 #include DH *get_dh1024(void); DH *get_dh2048(void); DH *get_dh4096(void); #endif /* dhparams.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071016551 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.605852128 openvswitch-2.5.9/lib/automake.mk0000644000175000017500000002730313534540071020244 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. lib_LTLIBRARIES += lib/libopenvswitch.la lib_libopenvswitch_la_LIBADD = $(SSL_LIBS) lib_libopenvswitch_la_LIBADD += $(CAPNG_LDADD) if WIN32 lib_libopenvswitch_la_LIBADD += ${PTHREAD_LIBS} endif lib_libopenvswitch_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/lib/libopenvswitch.sym \ $(AM_LDFLAGS) lib_libopenvswitch_la_SOURCES = \ lib/aes128.c \ lib/aes128.h \ lib/async-append.h \ lib/backtrace.c \ lib/backtrace.h \ lib/bfd.c \ lib/bfd.h \ lib/bitmap.h \ lib/bundle.c \ lib/bundle.h \ lib/byte-order.h \ lib/byteq.c \ lib/byteq.h \ lib/cfm.c \ lib/cfm.h \ lib/classifier.c \ lib/classifier.h \ lib/classifier-private.h \ lib/cmap.c \ lib/cmap.h \ lib/command-line.c \ lib/command-line.h \ lib/compiler.h \ lib/connectivity.c \ lib/connectivity.h \ lib/coverage.c \ lib/coverage.h \ lib/crc32c.c \ lib/crc32c.h \ lib/csum.c \ lib/csum.h \ lib/ct-dpif.c \ lib/ct-dpif.h \ lib/daemon.c \ lib/daemon.h \ lib/daemon-private.h \ lib/db-ctl-base.c \ lib/db-ctl-base.h \ lib/dhcp.h \ lib/dummy.c \ lib/dummy.h \ lib/dhparams.h \ lib/dirs.h \ lib/dpctl.c \ lib/dpctl.h \ lib/dp-packet.h \ lib/dp-packet.c \ lib/dpif-netdev.c \ lib/dpif-netdev.h \ lib/dpif-provider.h \ lib/dpif.c \ lib/dpif.h \ lib/heap.c \ lib/heap.h \ lib/dynamic-string.c \ lib/dynamic-string.h \ lib/entropy.c \ lib/entropy.h \ lib/fat-rwlock.c \ lib/fat-rwlock.h \ lib/fatal-signal.c \ lib/fatal-signal.h \ lib/flow.c \ lib/flow.h \ lib/geneve.h \ lib/guarded-list.c \ lib/guarded-list.h \ lib/hash.c \ lib/hash.h \ lib/hindex.c \ lib/hindex.h \ lib/hmap.c \ lib/hmap.h \ lib/hmapx.c \ lib/hmapx.h \ lib/id-pool.c \ lib/id-pool.h \ lib/jhash.c \ lib/jhash.h \ lib/json.c \ lib/json.h \ lib/jsonrpc.c \ lib/jsonrpc.h \ lib/lacp.c \ lib/lacp.h \ lib/latch.h \ lib/learn.c \ lib/learn.h \ lib/learning-switch.c \ lib/learning-switch.h \ lib/list.h \ lib/lockfile.c \ lib/lockfile.h \ lib/mac-learning.c \ lib/mac-learning.h \ lib/match.c \ lib/match.h \ lib/mcast-snooping.c \ lib/mcast-snooping.h \ lib/memory.c \ lib/memory.h \ lib/meta-flow.c \ lib/meta-flow.h \ lib/multipath.c \ lib/multipath.h \ lib/netdev-dummy.c \ lib/netdev-provider.h \ lib/netdev-vport.c \ lib/netdev-vport.h \ lib/netdev.c \ lib/netdev.h \ lib/netflow.h \ lib/netlink.c \ lib/netlink.h \ lib/nx-match.c \ lib/nx-match.h \ lib/odp-execute.c \ lib/odp-execute.h \ lib/odp-util.c \ lib/odp-util.h \ lib/ofp-actions.c \ lib/ofp-actions.h \ lib/ofp-errors.c \ lib/ofp-errors.h \ lib/ofp-msgs.c \ lib/ofp-msgs.h \ lib/ofp-parse.c \ lib/ofp-parse.h \ lib/ofp-print.c \ lib/ofp-print.h \ lib/ofp-util.c \ lib/ofp-util.h \ lib/ofp-version-opt.h \ lib/ofp-version-opt.c \ lib/ofpbuf.c \ lib/ofpbuf.h \ lib/ovs-atomic-c11.h \ lib/ovs-atomic-clang.h \ lib/ovs-atomic-flag-gcc4.7+.h \ lib/ovs-atomic-gcc4+.h \ lib/ovs-atomic-gcc4.7+.h \ lib/ovs-atomic-i586.h \ lib/ovs-atomic-locked.c \ lib/ovs-atomic-locked.h \ lib/ovs-atomic-msvc.h \ lib/ovs-atomic-pthreads.h \ lib/ovs-atomic-x86_64.h \ lib/ovs-atomic.h \ lib/ovs-lldp.c \ lib/ovs-lldp.h \ lib/ovs-rcu.c \ lib/ovs-rcu.h \ lib/ovs-router.h \ lib/ovs-router.c \ lib/ovs-thread.c \ lib/ovs-thread.h \ lib/ovsdb-data.c \ lib/ovsdb-data.h \ lib/ovsdb-error.c \ lib/ovsdb-error.h \ lib/ovsdb-idl-provider.h \ lib/ovsdb-idl.c \ lib/ovsdb-idl.h \ lib/ovsdb-parser.c \ lib/ovsdb-parser.h \ lib/ovsdb-types.c \ lib/ovsdb-types.h \ lib/packets.c \ lib/packets.h \ lib/pcap-file.c \ lib/pcap-file.h \ lib/perf-counter.h \ lib/perf-counter.c \ lib/poll-loop.c \ lib/poll-loop.h \ lib/process.c \ lib/process.h \ lib/pvector.c \ lib/pvector.h \ lib/random.c \ lib/random.h \ lib/rconn.c \ lib/rconn.h \ lib/rculist.h \ lib/reconnect.c \ lib/reconnect.h \ lib/rstp.c \ lib/rstp.h \ lib/rstp-common.h \ lib/rstp-state-machines.c \ lib/rstp-state-machines.h \ lib/sat-math.h \ lib/seq.c \ lib/seq.h \ lib/sha1.c \ lib/sha1.h \ lib/shash.c \ lib/shash.h \ lib/simap.c \ lib/simap.h \ lib/smap.c \ lib/smap.h \ lib/socket-util.c \ lib/socket-util.h \ lib/sort.c \ lib/sort.h \ lib/sset.c \ lib/sset.h \ lib/stp.c \ lib/stp.h \ lib/stream-fd.c \ lib/stream-fd.h \ lib/stream-provider.h \ lib/stream-ssl.h \ lib/stream-tcp.c \ lib/stream.c \ lib/stream.h \ lib/stdio.c \ lib/string.c \ lib/svec.c \ lib/svec.h \ lib/syslog-direct.c \ lib/syslog-direct.h \ lib/syslog-libc.c \ lib/syslog-libc.h \ lib/syslog-provider.h \ lib/table.c \ lib/table.h \ lib/timer.c \ lib/timer.h \ lib/timeval.c \ lib/timeval.h \ lib/tnl-neigh-cache.c \ lib/tnl-neigh-cache.h \ lib/tnl-ports.c \ lib/tnl-ports.h \ lib/token-bucket.c \ lib/tun-metadata.c \ lib/tun-metadata.h \ lib/type-props.h \ lib/unaligned.h \ lib/unicode.c \ lib/unicode.h \ lib/unixctl.c \ lib/unixctl.h \ lib/util.c \ lib/util.h \ lib/uuid.c \ lib/uuid.h \ lib/valgrind.h \ lib/vconn-provider.h \ lib/vconn-stream.c \ lib/vconn.c \ lib/vlan-bitmap.c \ lib/vlan-bitmap.h \ lib/vlandev.c \ lib/vlandev.h \ lib/vlog.c \ lib/lldp/aa-structs.h \ lib/lldp/lldp.c \ lib/lldp/lldp-const.h \ lib/lldp/lldp-tlv.h \ lib/lldp/lldpd.c \ lib/lldp/lldpd.h \ lib/lldp/lldpd-structs.c \ lib/lldp/lldpd-structs.h if WIN32 lib_libopenvswitch_la_SOURCES += \ lib/daemon-windows.c \ lib/getopt_long.c \ lib/getrusage-windows.c \ lib/latch-windows.c \ lib/route-table-stub.c \ lib/if-notifier-stub.c \ lib/strsep.c else lib_libopenvswitch_la_SOURCES += \ lib/daemon-unix.c \ lib/latch-unix.c \ lib/signals.c \ lib/signals.h \ lib/socket-util-unix.c \ lib/stream-unix.c endif EXTRA_DIST += \ lib/stdio.h.in \ lib/string.h.in nodist_lib_libopenvswitch_la_SOURCES = \ lib/dirs.c \ lib/vswitch-idl.c \ lib/vswitch-idl.h CLEANFILES += $(nodist_lib_libopenvswitch_la_SOURCES) lib_LTLIBRARIES += lib/libsflow.la lib_libsflow_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/lib/libsflow.sym \ $(AM_LDFLAGS) lib_libsflow_la_SOURCES = \ lib/sflow_api.h \ lib/sflow.h \ lib/sflow_agent.c \ lib/sflow_sampler.c \ lib/sflow_poller.c \ lib/sflow_receiver.c lib_libsflow_la_CPPFLAGS = $(AM_CPPFLAGS) lib_libsflow_la_CFLAGS = $(AM_CFLAGS) if HAVE_WNO_UNUSED lib_libsflow_la_CFLAGS += -Wno-unused endif if HAVE_WNO_UNUSED_PARAMETER lib_libsflow_la_CFLAGS += -Wno-unused-parameter endif if LINUX lib_libopenvswitch_la_SOURCES += \ lib/dpif-netlink.c \ lib/dpif-netlink.h \ lib/if-notifier.c \ lib/if-notifier.h \ lib/netdev-linux.c \ lib/netdev-linux.h \ lib/netlink-conntrack.c \ lib/netlink-conntrack.h \ lib/netlink-notifier.c \ lib/netlink-notifier.h \ lib/netlink-protocol.h \ lib/netlink-socket.c \ lib/netlink-socket.h \ lib/ovs-numa.c \ lib/ovs-numa.h \ lib/rtnetlink.c \ lib/rtnetlink.h \ lib/route-table.c \ lib/route-table.h endif if DPDK_NETDEV lib_libopenvswitch_la_SOURCES += \ lib/netdev-dpdk.c \ lib/netdev-dpdk.h endif if WIN32 lib_libopenvswitch_la_SOURCES += \ lib/dpif-netlink.c \ lib/dpif-netlink.h \ lib/netdev-windows.c \ lib/netlink-notifier.c \ lib/netlink-notifier.h \ lib/netlink-protocol.h \ lib/netlink-socket.c \ lib/netlink-socket.h endif if HAVE_POSIX_AIO lib_libopenvswitch_la_SOURCES += lib/async-append-aio.c else lib_libopenvswitch_la_SOURCES += lib/async-append-null.c endif if ESX lib_libopenvswitch_la_SOURCES += \ lib/route-table-stub.c \ lib/if-notifier-stub.c endif if HAVE_IF_DL lib_libopenvswitch_la_SOURCES += \ lib/if-notifier-bsd.c \ lib/netdev-bsd.c \ lib/rtbsd.c \ lib/rtbsd.h \ lib/route-table-bsd.c endif if HAVE_OPENSSL lib_libopenvswitch_la_SOURCES += lib/stream-ssl.c nodist_lib_libopenvswitch_la_SOURCES += lib/dhparams.c lib/dhparams.c: lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem $(AM_V_GEN)(echo '#include "lib/dhparams.h"' && \ openssl dhparam -C -in $(srcdir)/lib/dh1024.pem -noout && \ openssl dhparam -C -in $(srcdir)/lib/dh2048.pem -noout && \ openssl dhparam -C -in $(srcdir)/lib/dh4096.pem -noout) \ | sed 's/\(get_dh[0-9]*\)()/\1(void)/' > lib/dhparams.c.tmp && \ mv lib/dhparams.c.tmp lib/dhparams.c else lib_libopenvswitch_la_SOURCES += lib/stream-nossl.c endif pkgconfig_DATA += \ $(srcdir)/lib/libopenvswitch.pc \ $(srcdir)/lib/libsflow.pc EXTRA_DIST += \ lib/dh1024.pem \ lib/dh2048.pem \ lib/dh4096.pem \ lib/dirs.c.in MAN_FRAGMENTS += \ lib/common.man \ lib/common-syn.man \ lib/coverage-unixctl.man \ lib/daemon.man \ lib/daemon-syn.man \ lib/db-ctl-base.man \ lib/dpctl.man \ lib/memory-unixctl.man \ lib/ofp-version.man \ lib/ovs.tmac \ lib/service.man \ lib/service-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-peer-ca-cert.man \ lib/ssl-peer-ca-cert-syn.man \ lib/ssl.man \ lib/ssl-syn.man \ lib/table.man \ lib/unixctl.man \ lib/unixctl-syn.man \ lib/vconn-active.man \ lib/vconn-passive.man \ lib/vlog-unixctl.man \ lib/vlog-syn.man \ lib/vlog.man # vswitch IDL OVSIDL_BUILT += lib/vswitch-idl.c lib/vswitch-idl.h lib/vswitch-idl.ovsidl EXTRA_DIST += lib/vswitch-idl.ann lib/vswitch-idl.ovsidl: vswitchd/vswitch.ovsschema lib/vswitch-idl.ann $(AM_V_GEN)$(OVSDB_IDLC) annotate $(srcdir)/vswitchd/vswitch.ovsschema $(srcdir)/lib/vswitch-idl.ann > $@.tmp && mv $@.tmp $@ lib/dirs.c: lib/dirs.c.in Makefile $(AM_V_GEN)($(ro_c) && sed < $(srcdir)/lib/dirs.c.in \ -e 's,[@]srcdir[@],$(srcdir),g' \ -e 's,[@]LOGDIR[@],"$(LOGDIR)",g' \ -e 's,[@]RUNDIR[@],"$(RUNDIR)",g' \ -e 's,[@]DBDIR[@],"$(DBDIR)",g' \ -e 's,[@]bindir[@],"$(bindir)",g' \ -e 's,[@]sysconfdir[@],"$(sysconfdir)",g' \ -e 's,[@]pkgdatadir[@],"$(pkgdatadir)",g') \ > lib/dirs.c.tmp && \ mv lib/dirs.c.tmp lib/dirs.c lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h $(AM_V_GEN)$(run_python) $^ --meta-flow > $@.tmp && mv $@.tmp $@ lib/meta-flow.lo: lib/meta-flow.inc lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h $(AM_V_GEN)$(run_python) $^ --nx-match > $@.tmp && mv $@.tmp $@ lib/nx-match.lo: lib/nx-match.inc CLEANFILES += lib/meta-flow.inc lib/nx-match.inc EXTRA_DIST += build-aux/extract-ofp-fields lib/ofp-actions.inc1: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c $(AM_V_GEN)$(run_python) $^ --prototypes > $@.tmp && mv $@.tmp $@ lib/ofp-actions.inc2: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c $(AM_V_GEN)$(run_python) $^ --definitions > $@.tmp && mv $@.tmp $@ lib/ofp-actions.lo: lib/ofp-actions.inc1 lib/ofp-actions.inc2 CLEANFILES += lib/ofp-actions.inc1 lib/ofp-actions.inc2 EXTRA_DIST += build-aux/extract-ofp-actions lib/ofp-errors.inc: lib/ofp-errors.h include/openflow/openflow-common.h \ $(srcdir)/build-aux/extract-ofp-errors $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/extract-ofp-errors \ $(srcdir)/lib/ofp-errors.h \ $(srcdir)/include/openflow/openflow-common.h > $@.tmp && \ mv $@.tmp $@ lib/ofp-errors.lo: lib/ofp-errors.inc CLEANFILES += lib/ofp-errors.inc EXTRA_DIST += build-aux/extract-ofp-errors lib/ofp-msgs.inc: lib/ofp-msgs.h $(srcdir)/build-aux/extract-ofp-msgs $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/extract-ofp-msgs \ $(srcdir)/lib/ofp-msgs.h $@ > $@.tmp && mv $@.tmp $@ lib/ofp-msgs.lo: lib/ofp-msgs.inc CLEANFILES += lib/ofp-msgs.inc EXTRA_DIST += build-aux/extract-ofp-msgs INSTALL_DATA_LOCAL += lib-install-data-local lib-install-data-local: $(MKDIR_P) $(DESTDIR)$(RUNDIR) $(MKDIR_P) $(DESTDIR)$(PKIDIR) $(MKDIR_P) $(DESTDIR)$(LOGDIR) $(MKDIR_P) $(DESTDIR)$(DBDIR) openvswitch-2.5.9/lib/PaxHeaders.82075/byteq.h0000644000000000000000000000013013534540071015705 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 28 ctime=1567801424.6698526 openvswitch-2.5.9/lib/byteq.h0000644000175000017500000000342713534540071017403 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BYTEQ_H #define BYTEQ_H 1 #include #include #include /* General-purpose circular queue of bytes. */ struct byteq { uint8_t *buffer; /* Circular queue. */ unsigned int size; /* Number of bytes allocated for 'buffer'. */ unsigned int head; /* Head of queue. */ unsigned int tail; /* Chases the head. */ }; void byteq_init(struct byteq *, uint8_t *buffer, size_t size); int byteq_used(const struct byteq *); int byteq_avail(const struct byteq *); bool byteq_is_empty(const struct byteq *); bool byteq_is_full(const struct byteq *); void byteq_put(struct byteq *, uint8_t c); void byteq_putn(struct byteq *, const void *, size_t n); void byteq_put_string(struct byteq *, const char *); uint8_t byteq_get(struct byteq *); int byteq_write(struct byteq *, int fd); int byteq_read(struct byteq *, int fd); uint8_t *byteq_head(struct byteq *); int byteq_headroom(const struct byteq *); void byteq_advance_head(struct byteq *, unsigned int n); int byteq_tailroom(const struct byteq *); const uint8_t *byteq_tail(const struct byteq *); void byteq_advance_tail(struct byteq *, unsigned int n); #endif /* byteq.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/entropy.c0000644000000000000000000000013213534540071016256 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.717852954 openvswitch-2.5.9/lib/entropy.c0000644000175000017500000000430113534540071017742 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "entropy.h" #include #include #include #ifdef _WIN32 #include #endif #include "util.h" #include "socket-util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(entropy); static const char urandom[] = "/dev/urandom"; /* Initializes 'buffer' with 'n' bytes of high-quality random numbers. Returns * 0 if successful, otherwise a positive errno value or EOF on error. */ int get_entropy(void *buffer, size_t n) { #ifndef _WIN32 size_t bytes_read; int error; int fd; fd = open(urandom, O_RDONLY); if (fd < 0) { VLOG_ERR("%s: open failed (%s)", urandom, ovs_strerror(errno)); return errno ? errno : EINVAL; } error = read_fully(fd, buffer, n, &bytes_read); close(fd); if (error) { VLOG_ERR("%s: read error (%s)", urandom, ovs_retval_to_string(error)); } #else int error = 0; HCRYPTPROV crypt_prov = 0; CryptAcquireContext(&crypt_prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); if (!CryptGenRandom(crypt_prov, n, buffer)) { VLOG_ERR("CryptGenRandom: read error (%s)", ovs_lasterror_to_string()); error = EINVAL; } CryptReleaseContext(crypt_prov, 0); #endif return error; } /* Initializes 'buffer' with 'n' bytes of high-quality random numbers. Exits * if an error occurs. */ void get_entropy_or_die(void *buffer, size_t n) { int error = get_entropy(buffer, n); if (error) { VLOG_FATAL("%s: read error (%s)", urandom, ovs_retval_to_string(error)); } } openvswitch-2.5.9/lib/PaxHeaders.82075/jhash.c0000644000000000000000000000013213534540071015653 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.741853131 openvswitch-2.5.9/lib/jhash.c0000644000175000017500000000636213534540071017350 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "jhash.h" #include #include "unaligned.h" /* This is the public domain lookup3 hash by Bob Jenkins from * http://burtleburtle.net/bob/c/lookup3.c, modified for style. */ static inline uint32_t jhash_rot(uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } static inline void jhash_mix(uint32_t *a, uint32_t *b, uint32_t *c) { *a -= *c; *a ^= jhash_rot(*c, 4); *c += *b; *b -= *a; *b ^= jhash_rot(*a, 6); *a += *c; *c -= *b; *c ^= jhash_rot(*b, 8); *b += *a; *a -= *c; *a ^= jhash_rot(*c, 16); *c += *b; *b -= *a; *b ^= jhash_rot(*a, 19); *a += *c; *c -= *b; *c ^= jhash_rot(*b, 4); *b += *a; } static inline void jhash_final(uint32_t *a, uint32_t *b, uint32_t *c) { *c ^= *b; *c -= jhash_rot(*b, 14); *a ^= *c; *a -= jhash_rot(*c, 11); *b ^= *a; *b -= jhash_rot(*a, 25); *c ^= *b; *c -= jhash_rot(*b, 16); *a ^= *c; *a -= jhash_rot(*c, 4); *b ^= *a; *b -= jhash_rot(*a, 14); *c ^= *b; *c -= jhash_rot(*b, 24); } /* Returns the Jenkins hash of the 'n' 32-bit words at 'p', starting from * 'basis'. 'p' must be properly aligned. * * Use hash_words() instead, unless you're computing a hash function whose * value is exposed "on the wire" so we don't want to change it. */ uint32_t jhash_words(const uint32_t *p, size_t n, uint32_t basis) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (((uint32_t) n) << 2) + basis; while (n > 3) { a += p[0]; b += p[1]; c += p[2]; jhash_mix(&a, &b, &c); n -= 3; p += 3; } switch (n) { case 3: c += p[2]; /* fall through */ case 2: b += p[1]; /* fall through */ case 1: a += p[0]; jhash_final(&a, &b, &c); /* fall through */ case 0: break; } return c; } /* Returns the Jenkins hash of the 'n' bytes at 'p', starting from 'basis'. * * Use hash_bytes() instead, unless you're computing a hash function whose * value is exposed "on the wire" so we don't want to change it. */ uint32_t jhash_bytes(const void *p_, size_t n, uint32_t basis) { const uint32_t *p = p_; uint32_t a, b, c; a = b = c = 0xdeadbeef + n + basis; while (n >= 12) { a += get_unaligned_u32(p); b += get_unaligned_u32(p + 1); c += get_unaligned_u32(p + 2); jhash_mix(&a, &b, &c); n -= 12; p += 3; } if (n) { uint32_t tmp[3]; tmp[0] = tmp[1] = tmp[2] = 0; memcpy(tmp, p, n); a += tmp[0]; b += tmp[1]; c += tmp[2]; jhash_final(&a, &b, &c); } return c; } openvswitch-2.5.9/lib/PaxHeaders.82075/vconn-active.man0000644000000000000000000000013213534540071017503 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801423.745845788 openvswitch-2.5.9/lib/vconn-active.man0000644000175000017500000000113313534540071021167 0ustar00jpettitjpettit00000000000000.IP "\fBssl:\fIip\fR[\fB:\fIport\fR]" .IQ "\fBtcp:\fIip\fR[\fB:\fIport\fR]" The specified \fIport\fR on the host at the given \fIip\fR, which must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format. Wrap IPv6 addresses in square brackets, e.g. \fBtcp:[::1]:6653\fR. For \fBssl\fR, the \fB\-\-private\-key\fR, \fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory. .IP If \fIport\fR is not specified, it defaults to 6653. .TP \fBunix:\fIfile\fR On POSIX, a Unix domain server socket named \fIfile\fR. .IP On Windows, a localhost TCP port written in \fIfile\fR. openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-idl-provider.h0000644000000000000000000000013213534540071020276 xustar0030 mtime=1567801401.553682286 30 atime=1567801402.093686252 30 ctime=1567801424.837853839 openvswitch-2.5.9/lib/ovsdb-idl-provider.h0000644000175000017500000000624013534540071021766 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVSDB_IDL_PROVIDER_H #define OVSDB_IDL_PROVIDER_H 1 #include "hmap.h" #include "list.h" #include "ovsdb-idl.h" #include "ovsdb-types.h" #include "shash.h" #include "uuid.h" struct ovsdb_idl_row { struct hmap_node hmap_node; /* In struct ovsdb_idl_table's 'rows'. */ struct uuid uuid; /* Row "_uuid" field. */ struct ovs_list src_arcs; /* Forward arcs (ovsdb_idl_arc.src_node). */ struct ovs_list dst_arcs; /* Backward arcs (ovsdb_idl_arc.dst_node). */ struct ovsdb_idl_table *table; /* Containing table. */ struct ovsdb_datum *old; /* Committed data (null if orphaned). */ /* Transactional data. */ struct ovsdb_datum *new; /* Modified data (null to delete row). */ unsigned long int *prereqs; /* Bitmap of columns to verify in "old". */ unsigned long int *written; /* Bitmap of columns from "new" to write. */ struct hmap_node txn_node; /* Node in ovsdb_idl_txn's list. */ unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX]; struct ovs_list track_node; }; struct ovsdb_idl_column { char *name; struct ovsdb_type type; bool mutable; void (*parse)(struct ovsdb_idl_row *, const struct ovsdb_datum *); void (*unparse)(struct ovsdb_idl_row *); }; struct ovsdb_idl_table_class { char *name; bool is_root; const struct ovsdb_idl_column *columns; size_t n_columns; size_t allocation_size; void (*row_init)(struct ovsdb_idl_row *); }; struct ovsdb_idl_table { const struct ovsdb_idl_table_class *class; unsigned char *modes; /* OVSDB_IDL_* bitmasks, indexed by column. */ bool need_table; /* Monitor table even if no columns are selected * for replication. */ struct shash columns; /* Contains "const struct ovsdb_idl_column *"s. */ struct hmap rows; /* Contains "struct ovsdb_idl_row"s. */ struct ovsdb_idl *idl; /* Containing idl. */ unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX]; struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */ }; struct ovsdb_idl_class { const char *database; /* for this database. */ const struct ovsdb_idl_table_class *tables; size_t n_tables; }; struct ovsdb_idl_row *ovsdb_idl_get_row_arc( struct ovsdb_idl_row *src, struct ovsdb_idl_table_class *dst_table, const struct uuid *dst_uuid); void ovsdb_idl_txn_verify(const struct ovsdb_idl_row *, const struct ovsdb_idl_column *); struct ovsdb_idl_txn *ovsdb_idl_txn_get(const struct ovsdb_idl_row *); #endif /* ovsdb-idl-provider.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-vport.h0000644000000000000000000000013213534540071017220 xustar0030 mtime=1567801401.457681581 30 atime=1567801402.081686164 30 ctime=1567801424.773853367 openvswitch-2.5.9/lib/netdev-vport.h0000644000175000017500000000341613534540071020712 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETDEV_VPORT_H #define NETDEV_VPORT_H 1 #include #include #include "compiler.h" struct dpif_netlink_vport; struct dpif_flow_stats; struct netdev; struct netdev_class; struct netdev_stats; void netdev_vport_tunnel_register(void); void netdev_vport_patch_register(void); bool netdev_vport_is_patch(const struct netdev *); bool netdev_vport_is_layer3(const struct netdev *); char *netdev_vport_patch_peer(const struct netdev *netdev); void netdev_vport_inc_rx(const struct netdev *, const struct dpif_flow_stats *); void netdev_vport_inc_tx(const struct netdev *, const struct dpif_flow_stats *); bool netdev_vport_is_vport_class(const struct netdev_class *); const char *netdev_vport_class_get_dpif_port(const struct netdev_class *); #ifndef _WIN32 enum { NETDEV_VPORT_NAME_BUFSIZE = 16 }; #else enum { NETDEV_VPORT_NAME_BUFSIZE = 256 }; #endif const char *netdev_vport_get_dpif_port(const struct netdev *, char namebuf[], size_t bufsize) OVS_WARN_UNUSED_RESULT; char *netdev_vport_get_dpif_port_strdup(const struct netdev *); #endif /* netdev-vport.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sha1.h0000644000000000000000000000013113534540071015416 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.873854104 openvswitch-2.5.9/lib/sha1.h0000644000175000017500000000517313534540071017113 0ustar00jpettitjpettit00000000000000/* * This file is from the Apache Portable Runtime Library. * The full upstream copyright and license statement is included below. * Modifications copyright (c) 2009 Nicira, Inc. */ /* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* NIST Secure Hash Algorithm * heavily modified by Uwe Hollerbach uh@alumni.caltech edu * from Peter C. Gutmann's implementation as found in * Applied Cryptography by Bruce Schneier * This code is hereby placed in the public domain */ #ifndef SHA1_H #define SHA1_H #include #include #include #define SHA1_DIGEST_SIZE 20 /* Size of the SHA1 digest. */ #define SHA1_HEX_DIGEST_LEN 40 /* Length of SHA1 digest as hex in ASCII. */ /* SHA1 context structure. */ struct sha1_ctx { uint32_t digest[5]; /* Message digest. */ uint32_t count_lo, count_hi; /* 64-bit bit counts. */ uint32_t data[16]; /* SHA data buffer */ int local; /* Unprocessed amount in data. */ }; void sha1_init(struct sha1_ctx *); void sha1_update(struct sha1_ctx *, const void *, size_t); void sha1_final(struct sha1_ctx *, uint8_t digest[SHA1_DIGEST_SIZE]); void sha1_bytes(const void *, size_t, uint8_t digest[SHA1_DIGEST_SIZE]); #define SHA1_FMT \ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" \ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" #define SHA1_ARGS(DIGEST) \ ((DIGEST)[0]), ((DIGEST)[1]), ((DIGEST)[2]), ((DIGEST)[3]), \ ((DIGEST)[4]), ((DIGEST)[5]), ((DIGEST)[6]), ((DIGEST)[7]), \ ((DIGEST)[8]), ((DIGEST)[9]), ((DIGEST)[10]), ((DIGEST)[11]), \ ((DIGEST)[12]), ((DIGEST)[13]), ((DIGEST)[14]), ((DIGEST)[15]), \ ((DIGEST)[16]), ((DIGEST)[17]), ((DIGEST)[18]), ((DIGEST)[19]) void sha1_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char hex[SHA1_HEX_DIGEST_LEN + 1]); bool sha1_from_hex(uint8_t digest[SHA1_DIGEST_SIZE], const char *hex); #endif /* sha1.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/tnl-ports.h0000644000000000000000000000013213534540071016525 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.921854458 openvswitch-2.5.9/lib/tnl-ports.h0000644000175000017500000000217513534540071020220 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TNL_PORT_H #define TNL_PORT_H 1 #include #include #include "flow.h" #include "packets.h" #include "util.h" odp_port_t tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc); void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[]); void tnl_port_map_delete(ovs_be16 udp_port); void tnl_port_map_insert_ipdev(const char dev[]); void tnl_port_map_delete_ipdev(const char dev[]); void tnl_port_map_run(void); void tnl_port_map_init(void); #endif openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-c11.h0000644000000000000000000000013213534540071017226 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.809853633 openvswitch-2.5.9/lib/ovs-atomic-c11.h0000644000175000017500000000430113534540071020712 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on compilers that * have built-in support for C11 */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #include #define OMIT_STANDARD_ATOMIC_TYPES 1 #define ATOMIC(TYPE) _Atomic(TYPE) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_read_explicit(SRC, DST, ORDER) \ (*(DST) = atomic_load_explicit(SRC, ORDER), \ (void) 0) #define atomic_add(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_sub(RMW, ARG, ORIG) \ atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_or(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_xor(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_and(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = atomic_fetch_add_explicit(RMW, ARG, ORDER), (void) 0) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = atomic_fetch_sub_explicit(RMW, ARG, ORDER), (void) 0) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = atomic_fetch_or_explicit(RMW, ARG, ORDER), (void) 0) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = atomic_fetch_xor_explicit(RMW, ARG, ORDER), (void) 0) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ (*(ORIG) = atomic_fetch_and_explicit(RMW, ARG, ORDER), (void) 0) openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-errors.h0000644000000000000000000000013213534540071016661 xustar0030 mtime=1567801401.517682022 30 atime=1567801402.085686193 30 ctime=1567801424.793853514 openvswitch-2.5.9/lib/ofp-errors.h0000644000175000017500000006257613534540071020367 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFP_ERRORS_H #define OFP_ERRORS_H 1 #include #include #include #include "openflow/openflow.h" struct ds; struct ofpbuf; /* Error codes. * * We embed system errno values and OpenFlow standard and vendor extension * error codes into the positive range of "int": * * - Errno values are assumed to use the range 1 through 2**30 - 1. * * (C and POSIX say that errno values are positive. We assume that they * are less than 2**29. They are actually less than 65536 on at least * Linux, FreeBSD, OpenBSD, and Windows.) * * - OpenFlow standard and vendor extension error codes use the range * starting at 2**30 (OFPERR_OFS). * * Zero and negative values are not used. */ #define OFPERR_OFS (1 << 30) /* OpenFlow error codes * -------------------- * * The comments below are parsed by the extract-ofp-errors program at build * time and used to determine the mapping between "enum ofperr" constants and * error type/code values used in the OpenFlow protocol: * * - The first part of each comment specifies the vendor, OpenFlow versions, * type, and sometimes a code for each protocol that supports the error: * * # The vendor is OF for standard OpenFlow error codes. Otherwise it * is one of the *_VENDOR_ID codes defined in openflow-common.h. * * # The version can specify a specific OpenFlow version, a version * range delimited by "-", or an open-ended range with "+". * * # Standard OpenFlow errors have both a type and a code. Extension * errors generally have only a type, no code. There is one * exception: Nicira extension (NX) errors for OpenFlow 1.0 and 1.1 * have both a type and a code. (This means that the version * specification for NX errors may not include version 1.0 or 1.1 (or * both) along with version 1.2 or later, because the requirements * for those versions are different.) * * - Additional text is a human-readable description of the meaning of each * error, used to explain the error to the user. Any text enclosed in * square brackets is omitted; this can be used to explain rationale for * choice of error codes in the case where this is desirable. * * * Expected duplications * --------------------- * * Occasionally, in one version of OpenFlow a single named error can indicate * two or more distinct errors, then a later version of OpenFlow splits those * meanings into different error codes. When that happens, both errors are * assigned the same value in the earlier version. That is ordinarily a * mistake, so the build system reports an error. When that happens, add the * error message to the list of "Expected duplications" below to suppress the * error. In such a case, the named error defined earlier is how OVS * interprets the earlier, merged form of the error. * * For example, OpenFlow 1.1 defined (3,5) as OFPBIC_UNSUP_EXP_INST, then * OpenFlow 1.2 broke this error into OFPBIC_BAD_EXPERIMENTER as (3,5) and * OFPBIC_BAD_EXT_TYPE as (3,6). To allow the OVS code to report just a single * error code, instead of protocol version dependent errors, this list of * errors only lists the latter two errors, giving both of them the same code * (3,5) for OpenFlow 1.1. Then, when OVS serializes either error into * OpenFlow 1.1, it uses the same code (3,5). In the other direction, when OVS * deserializes (3,5) from OpenFlow 1.1, it translates it into * OFPBIC_BAD_EXPERIMENTER (because its definition precedes that of * OFPBIC_BAD_EXT_TYPE below). See the "encoding OFPBIC_* experimenter errors" * and "decoding OFPBIC_* experimenter errors" tests in tests/ofp-errors.at for * full details. */ enum ofperr { /* Expected duplications. */ /* Expected: 0x0,3,5 in OF1.1 means both OFPBIC_BAD_EXPERIMENTER and * OFPBIC_BAD_EXP_TYPE. */ /* Expected: 0x0,1,5 in OF1.0 means both OFPBRC_EPERM and * OFPBRC_IS_SLAVE. */ /* Expected: 0x0,1,5 in OF1.1 means both OFPBRC_EPERM and * OFPBRC_IS_SLAVE. */ /* ## ------------------ ## */ /* ## OFPET_HELLO_FAILED ## */ /* ## ------------------ ## */ /* OF1.0+(0,0). No compatible version. */ OFPERR_OFPHFC_INCOMPATIBLE = OFPERR_OFS, /* OF1.0+(0,1). Permissions error. */ OFPERR_OFPHFC_EPERM, /* ## ----------------- ## */ /* ## OFPET_BAD_REQUEST ## */ /* ## ----------------- ## */ /* OF1.0+(1,0). ofp_header.version not supported. */ OFPERR_OFPBRC_BAD_VERSION, /* OF1.0+(1,1). ofp_header.type not supported. */ OFPERR_OFPBRC_BAD_TYPE, /* OF1.0+(1,2). ofp_stats_msg.type not supported. */ OFPERR_OFPBRC_BAD_STAT, /* OF1.0+(1,3). Vendor not supported (in ofp_vendor_header or * ofp_stats_msg). */ OFPERR_OFPBRC_BAD_VENDOR, /* OF1.0+(1,4). Vendor subtype not supported. */ OFPERR_OFPBRC_BAD_SUBTYPE, /* OF1.0+(1,5). Permissions error. */ OFPERR_OFPBRC_EPERM, /* OF1.0+(1,6). Wrong request length for type. */ OFPERR_OFPBRC_BAD_LEN, /* OF1.0+(1,7). Specified buffer has already been used. */ OFPERR_OFPBRC_BUFFER_EMPTY, /* OF1.0+(1,8). Specified buffer does not exist. */ OFPERR_OFPBRC_BUFFER_UNKNOWN, /* NX1.0(1,512), OF1.1+(1,9). Specified table-id invalid or does not exist. * [ A non-standard error (1,512), formerly OFPERR_NXBRC_BAD_TABLE_ID, * is used for OpenFlow 1.0 as there seems to be no appropriate error * code defined the specification. ] */ OFPERR_OFPBRC_BAD_TABLE_ID, /* OF1.0-1.1(1,5), OF1.2+(1,10). Denied because controller is slave. */ OFPERR_OFPBRC_IS_SLAVE, /* NX1.0-1.1(1,514), OF1.2+(1,11). Invalid port. [ A non-standard error * (1,514), formerly OFPERR_NXBRC_BAD_IN_PORT is used for OpenFlow 1.0 and * 1.1 as there seems to be no appropriate error code defined the * specifications. ] */ OFPERR_OFPBRC_BAD_PORT, /* OF1.2+(1,12). Invalid packet in packet-out. */ OFPERR_OFPBRC_BAD_PACKET, /* OF1.3+(1,13). Multipart request overflowed the assigned buffer. */ OFPERR_OFPBRC_MULTIPART_BUFFER_OVERFLOW, /* NX1.0-1.1(1,256), NX1.2+(2). Invalid NXM flow match. */ OFPERR_NXBRC_NXM_INVALID, /* NX1.0-1.1(1,257), NX1.2+(3). The nxm_type, or nxm_type taken in * combination with nxm_hasmask or nxm_length or both, is invalid or not * implemented. */ OFPERR_NXBRC_NXM_BAD_TYPE, /* NX1.0-1.1(1,515), NX1.2+(4). Must-be-zero field had nonzero value. */ OFPERR_NXBRC_MUST_BE_ZERO, /* NX1.0-1.1(1,516), NX1.2+(5). The reason in an ofp_port_status message * is not valid. */ OFPERR_NXBRC_BAD_REASON, /* NX1.0-1.1(1,520), NX1.2+(9). The 'event' in an NXST_FLOW_MONITOR reply * does not specify one of the NXFME_ABBREV, NXFME_ADD, NXFME_DELETE, or * NXFME_MODIFY. */ OFPERR_NXBRC_FM_BAD_EVENT, /* NX1.0-1.1(1,521), NX1.2+(10). The error that occurred cannot be * represented in this OpenFlow version. */ OFPERR_NXBRC_UNENCODABLE_ERROR, /* ## ---------------- ## */ /* ## OFPET_BAD_ACTION ## */ /* ## ---------------- ## */ /* OF1.0+(2,0). Unknown action type. */ OFPERR_OFPBAC_BAD_TYPE, /* OF1.0+(2,1). Length problem in actions. */ OFPERR_OFPBAC_BAD_LEN, /* OF1.0+(2,2). Unknown experimenter id specified. */ OFPERR_OFPBAC_BAD_VENDOR, /* OF1.0+(2,3). Unknown action type for experimenter id. */ OFPERR_OFPBAC_BAD_VENDOR_TYPE, /* OF1.0+(2,4). Problem validating output port. */ OFPERR_OFPBAC_BAD_OUT_PORT, /* OF1.0+(2,5). Bad action argument. */ OFPERR_OFPBAC_BAD_ARGUMENT, /* OF1.0+(2,6). Permissions error. */ OFPERR_OFPBAC_EPERM, /* OF1.0+(2,7). Can't handle this many actions. */ OFPERR_OFPBAC_TOO_MANY, /* OF1.0+(2,8). Problem validating output queue. */ OFPERR_OFPBAC_BAD_QUEUE, /* OF1.1+(2,9). Invalid group id in forward action. */ OFPERR_OFPBAC_BAD_OUT_GROUP, /* NX1.0(1,522), OF1.1+(2,10). Action can't apply for this match or a * prerequisite for use of this field is unmet. */ OFPERR_OFPBAC_MATCH_INCONSISTENT, /* OF1.1+(2,11). Action order is unsupported for the action list in an * Apply-Actions instruction */ OFPERR_OFPBAC_UNSUPPORTED_ORDER, /* OF1.1+(2,12). Actions uses an unsupported tag/encap. */ OFPERR_OFPBAC_BAD_TAG, /* NX1.0-1.1(1,523), OF1.2+(2,13). Action uses unknown or unsupported OXM * or NXM field. */ OFPERR_OFPBAC_BAD_SET_TYPE, /* NX1.0-1.1(1,524), OF1.2+(2,14). Action references past the end of an * OXM or NXM field, or uses a length of zero. */ OFPERR_OFPBAC_BAD_SET_LEN, /* NX1.0-1.1(1,525), OF1.2+(2,15). Action sets a field to an invalid or * unsupported value, or modifies a read-only field. */ OFPERR_OFPBAC_BAD_SET_ARGUMENT, /* ONF1.3-1.4(4250), OF1.5+(2,16). Field in Set-Field action has Has-Mask * bit set to 1. */ OFPERR_OFPBAC_BAD_SET_MASK, /* NX1.0-1.1(2,256), NX1.2+(11). Must-be-zero action argument had nonzero * value. */ OFPERR_NXBAC_MUST_BE_ZERO, /* NX1.0-1.1(2,526), NX1.2+(15). Conjunction action must be only action * present. conjunction(id, k/n) must satisfy 1 <= k <= n and 2 <= n <= * 64. */ OFPERR_NXBAC_BAD_CONJUNCTION, /* ## --------------------- ## */ /* ## OFPET_BAD_INSTRUCTION ## */ /* ## --------------------- ## */ /* OF1.1+(3,0). Unknown instruction. */ OFPERR_OFPBIC_UNKNOWN_INST, /* NX1.0(2,257), OF1.1+(3,1). Switch or table does not support the * instruction. */ OFPERR_OFPBIC_UNSUP_INST, /* OF1.1+(3,2). Invalid Table-ID specified. */ OFPERR_OFPBIC_BAD_TABLE_ID, /* OF1.1+(3,3). Metadata value unsupported by datapath. */ OFPERR_OFPBIC_UNSUP_METADATA, /* OF1.1+(3,4). Metadata mask value unsupported by datapath. */ OFPERR_OFPBIC_UNSUP_METADATA_MASK, /* OF1.1+(3,5). Unknown experimenter id specified. */ OFPERR_OFPBIC_BAD_EXPERIMENTER, /* OF1.1(3,5), OF1.2+(3,6). Unknown instruction for experimenter id. */ OFPERR_OFPBIC_BAD_EXP_TYPE, /* OF1.2+(3,7). Length problem in instructions. */ OFPERR_OFPBIC_BAD_LEN, /* OF1.2+(3,8). Permissions error. */ OFPERR_OFPBIC_EPERM, /* NX1.1(3,256), ONF1.2-1.3(2600), OF1.4+(3,9). Duplicate instruction. */ OFPERR_OFPBIC_DUP_INST, /* ## --------------- ## */ /* ## OFPET_BAD_MATCH ## */ /* ## --------------- ## */ /* OF1.1+(4,0). Unsupported match type specified by the match */ OFPERR_OFPBMC_BAD_TYPE, /* OF1.1+(4,1). Length problem in match. */ OFPERR_OFPBMC_BAD_LEN, /* OF1.1+(4,2). Match uses an unsupported tag/encap. */ OFPERR_OFPBMC_BAD_TAG, /* OF1.1+(4,3). Unsupported datalink addr mask - switch does not support * arbitrary datalink address mask. */ OFPERR_OFPBMC_BAD_DL_ADDR_MASK, /* OF1.1+(4,4). Unsupported network addr mask - switch does not support * arbitrary network address mask. */ OFPERR_OFPBMC_BAD_NW_ADDR_MASK, /* NX1.0(1,262), OF1.1+(4,5). Unsupported wildcard specified in the * match. */ OFPERR_OFPBMC_BAD_WILDCARDS, /* NX1.0(0,263), OF1.1+(4,6). Unsupported field in the match. */ OFPERR_OFPBMC_BAD_FIELD, /* NX1.0(1,258), OF1.1+(4,7). Unsupported value in a match * field. */ OFPERR_OFPBMC_BAD_VALUE, /* NX1.0-1.1(1,259), OF1.2+(4,8). Unsupported mask specified in the match, * field is not dl-address or nw-address. */ OFPERR_OFPBMC_BAD_MASK, /* NX1.0-1.1(1,260), OF1.2+(4,9). A prerequisite was not met. */ OFPERR_OFPBMC_BAD_PREREQ, /* NX1.0-1.1(1,261), OF1.2+(4,10). A field type was duplicated. */ OFPERR_OFPBMC_DUP_FIELD, /* OF1.2+(4,11). Permissions error. */ OFPERR_OFPBMC_EPERM, /* ## --------------------- ## */ /* ## OFPET_FLOW_MOD_FAILED ## */ /* ## --------------------- ## */ /* OF1.1+(5,0). Unspecified error. */ OFPERR_OFPFMFC_UNKNOWN, /* OF1.0(3,0), OF1.1+(5,1). Flow not added because of full table(s). */ OFPERR_OFPFMFC_TABLE_FULL, /* OF1.1+(5,2). Table does not exist */ OFPERR_OFPFMFC_BAD_TABLE_ID, /* OF1.0(3,1), OF1.1+(5,3). Attempted to add overlapping flow with * CHECK_OVERLAP flag set. */ OFPERR_OFPFMFC_OVERLAP, /* OF1.0(3,2), OF1.1+(5,4). Permissions error. */ OFPERR_OFPFMFC_EPERM, /* OF1.1+(5,5). Flow not added because of unsupported idle/hard * timeout. */ OFPERR_OFPFMFC_BAD_TIMEOUT, /* OF1.0(3,3). Flow not added because of non-zero idle/hard timeout. */ OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT, /* OF1.0(3,4), OF1.1+(5,6). Unsupported or unknown command. */ OFPERR_OFPFMFC_BAD_COMMAND, /* NX1.0(3,258), NX1.1(5,258), OF1.2+(5,7). Unsupported or unknown * flags. */ OFPERR_OFPFMFC_BAD_FLAGS, /* OF1.0(3,5). Unsupported action list - cannot process in the order * specified. */ OFPERR_OFPFMFC_UNSUPPORTED, /* NX1.0-1.1(5,256), NX1.2+(12). Generic hardware error. */ OFPERR_NXFMFC_HARDWARE, /* NX1.0-1.1(5,257), NX1.2+(13). A nonexistent table ID was specified in * the "command" field of struct ofp_flow_mod, when the * nxt_flow_mod_table_id extension is enabled. */ OFPERR_NXFMFC_BAD_TABLE_ID, /* ## ---------------------- ## */ /* ## OFPET_GROUP_MOD_FAILED ## */ /* ## ---------------------- ## */ /* OF1.1+(6,0). Group not added because a group ADD attempted to replace * an already-present group. */ OFPERR_OFPGMFC_GROUP_EXISTS, /* OF1.1+(6,1). Group not added because Group specified is invalid. */ OFPERR_OFPGMFC_INVALID_GROUP, /* OF1.1+(6,2). Switch does not support unequal load sharing with select * groups. */ OFPERR_OFPGMFC_WEIGHT_UNSUPPORTED, /* OF1.1+(6,3). The group table is full. */ OFPERR_OFPGMFC_OUT_OF_GROUPS, /* OF1.1+(6,4). The maximum number of action buckets for a group has been * exceeded. */ OFPERR_OFPGMFC_OUT_OF_BUCKETS, /* OF1.1+(6,5). Switch does not support groups that forward to groups. */ OFPERR_OFPGMFC_CHAINING_UNSUPPORTED, /* OF1.1+(6,6). This group cannot watch the watch_port or watch_group * specified. */ OFPERR_OFPGMFC_WATCH_UNSUPPORTED, /* OF1.1+(6,7). Group entry would cause a loop. */ OFPERR_OFPGMFC_LOOP, /* OF1.1+(6,8). Group not modified because a group MODIFY attempted to * modify a non-existent group. */ OFPERR_OFPGMFC_UNKNOWN_GROUP, /* OF1.2+(6,9). Group not deleted because another group is forwarding to it. */ OFPERR_OFPGMFC_CHAINED_GROUP, /* OF1.2+(6,10). Unsupported or unknown group type. */ OFPERR_OFPGMFC_BAD_TYPE, /* OF1.2+(6,11). Unsupported or unknown command. */ OFPERR_OFPGMFC_BAD_COMMAND, /* OF1.2+(6,12). Error in bucket. */ OFPERR_OFPGMFC_BAD_BUCKET, /* OF1.2+(6,13). Error in watch port/group. */ OFPERR_OFPGMFC_BAD_WATCH, /* OF1.2+(6,14). Permissions error. */ OFPERR_OFPGMFC_EPERM, /* OF1.5+(6,15). Invalid bucket identifier used in * INSERT BUCKET or REMOVE BUCKET command. */ OFPERR_OFPGMFC_UNKNOWN_BUCKET, /* OF1.5+(6,16). Can't insert bucket because a bucket * already exist with that bucket-id. */ OFPERR_OFPGMFC_BUCKET_EXISTS, /* ## --------------------- ## */ /* ## OFPET_PORT_MOD_FAILED ## */ /* ## --------------------- ## */ /* OF1.0(4,0), OF1.1+(7,0). Specified port does not exist. */ OFPERR_OFPPMFC_BAD_PORT, /* OF1.0(4,1), OF1.1+(7,1). Specified hardware address does not match the * port number. */ OFPERR_OFPPMFC_BAD_HW_ADDR, /* OF1.1+(7,2). Specified config is invalid. */ OFPERR_OFPPMFC_BAD_CONFIG, /* OF1.1+(7,3). Specified advertise is invalid. */ OFPERR_OFPPMFC_BAD_ADVERTISE, /* OF1.2+(7,4). Permissions error. */ OFPERR_OFPPMFC_EPERM, /* ## ---------------------- ## */ /* ## OFPET_TABLE_MOD_FAILED ## */ /* ## ---------------------- ## */ /* OF1.1+(8,0). Specified table does not exist. */ OFPERR_OFPTMFC_BAD_TABLE, /* OF1.1+(8,1). Specified config is invalid. */ OFPERR_OFPTMFC_BAD_CONFIG, /* OF1.2+(8,2). Permissions error. */ OFPERR_OFPTMFC_EPERM, /* ## --------------------- ## */ /* ## OFPET_QUEUE_OP_FAILED ## */ /* ## --------------------- ## */ /* OF1.0(5,0), OF1.1+(9,0). Invalid port (or port does not exist). */ OFPERR_OFPQOFC_BAD_PORT, /* OF1.0(5,1), OF1.1+(9,1). Queue does not exist. */ OFPERR_OFPQOFC_BAD_QUEUE, /* OF1.0(5,2), OF1.1+(9,2). Permissions error. */ OFPERR_OFPQOFC_EPERM, /* ## -------------------------- ## */ /* ## OFPET_SWITCH_CONFIG_FAILED ## */ /* ## -------------------------- ## */ /* OF1.1+(10,0). Specified flags is invalid. */ OFPERR_OFPSCFC_BAD_FLAGS, /* OF1.1+(10,1). Specified len is invalid. */ OFPERR_OFPSCFC_BAD_LEN, /* OF1.2+(10,2). Permissions error. */ OFPERR_OFPSCFC_EPERM, /* ## ------------------------- ## */ /* ## OFPET_ROLE_REQUEST_FAILED ## */ /* ## ------------------------- ## */ /* OF1.2+(11,0). Stale Message: old generation_id. */ OFPERR_OFPRRFC_STALE, /* OF1.2+(11,1). Controller role change unsupported. */ OFPERR_OFPRRFC_UNSUP, /* NX1.0-1.1(1,513), OF1.2+(11,2). Invalid role. */ OFPERR_OFPRRFC_BAD_ROLE, /* ## ---------------------- ## */ /* ## OFPET_METER_MOD_FAILED ## */ /* ## ---------------------- ## */ /* OF1.3+(12,0). Unspecified error. */ OFPERR_OFPMMFC_UNKNOWN, /* OF1.3+(12,1). Meter not added because a Meter ADD attempted to * replace an existing Meter. */ OFPERR_OFPMMFC_METER_EXISTS, /* OF1.3+(12,2). Meter not added because Meter specified is invalid. */ OFPERR_OFPMMFC_INVALID_METER, /* OF1.3+(12,3). Meter not modified because a Meter MODIFY attempted * to modify a non-existent Meter. */ OFPERR_OFPMMFC_UNKNOWN_METER, /* OF1.3+(12,4). Unsupported or unknown command. */ OFPERR_OFPMMFC_BAD_COMMAND, /* OF1.3+(12,5). Flag configuration unsupported. */ OFPERR_OFPMMFC_BAD_FLAGS, /* OF1.3+(12,6). Rate unsupported. */ OFPERR_OFPMMFC_BAD_RATE, /* OF1.3+(12,7). Burst size unsupported. */ OFPERR_OFPMMFC_BAD_BURST, /* OF1.3+(12,8). Band unsupported. */ OFPERR_OFPMMFC_BAD_BAND, /* OF1.3+(12,9). Band value unsupported. */ OFPERR_OFPMMFC_BAD_BAND_VALUE, /* OF1.3+(12,10). No more meters available. */ OFPERR_OFPMMFC_OUT_OF_METERS, /* OF1.3+(12,11). The maximum number of properties for a meter has * been exceeded. */ OFPERR_OFPMMFC_OUT_OF_BANDS, /* ## --------------------------- ## */ /* ## OFPET_TABLE_FEATURES_FAILED ## */ /* ## --------------------------- ## */ /* OF1.3+(13,0). Specified table does not exist. */ OFPERR_OFPTFFC_BAD_TABLE, /* OF1.3+(13,1). Invalid metadata mask. */ OFPERR_OFPTFFC_BAD_METADATA, /* OF1.3+(13,5). Permissions error. */ OFPERR_OFPTFFC_EPERM, /* ## ------------------ ## */ /* ## OFPET_BAD_PROPERTY ## */ /* ## ------------------ ## */ /* OF1.3(13,2), OF1.4+(14,0). Unknown property type. * * [Known as OFPTFFC_BAD_TYPE in OF1.3.] */ OFPERR_OFPBPC_BAD_TYPE, /* OF1.3(13,3), OF1.4+(14,1). Length problem in property. * * [Known as OFPTFFC_BAD_LEN in OF1.3.] */ OFPERR_OFPBPC_BAD_LEN, /* OF1.3(13,4), OF1.4+(14,2). Unsupported property value. * * [Known as OFPTFFC_BAD_ARGUMENT in OF1.3.] */ OFPERR_OFPBPC_BAD_VALUE, /* ONF1.3(4443), OF1.4+(14,3). Can't handle this many properties. */ OFPERR_OFPBPC_TOO_MANY, /* ONF1.3(4444), OF1.4+(14,4). A property type was duplicated. */ OFPERR_OFPBPC_DUP_TYPE, /* ONF1.3(4445), OF1.4+(14,5). Unknown experimenter id specified. */ OFPERR_OFPBPC_BAD_EXPERIMENTER, /* ONF1.3(4446), OF1.4+(14,6). Unknown exp_type for experimenter id. */ OFPERR_OFPBPC_BAD_EXP_TYPE, /* ONF1.3(4447), OF1.4+(14,7). Unknown value for experimenter id. */ OFPERR_OFPBPC_BAD_EXP_VALUE, /* ONF1.3(4448), OF1.4+(14,8). Permissions error. */ OFPERR_OFPBPC_EPERM, /* ## -------------------------- ## */ /* ## OFPET_ASYNC_CONFIG_FAILED ## */ /* ## -------------------------- ## */ /* OF1.4+(15,0). One mask is invalid. */ OFPERR_OFPACFC_INVALID, /* OF1.4+(15,1). Requested configuration not supported. */ OFPERR_OFPACFC_UNSUPPORTED, /* OF1.4+(15,2). Permissions error. */ OFPERR_OFPACFC_EPERM, /* ## -------------------- ## */ /* ## OFPET_BUNDLE_FAILED ## */ /* ## -------------------- ## */ /* OF1.4+(17,0). Unspecified error. */ OFPERR_OFPBFC_UNKNOWN, /* OF1.4+(17,1). Permissions error. */ OFPERR_OFPBFC_EPERM, /* OF1.4+(17,2). Bundle ID doesn't exist. */ OFPERR_OFPBFC_BAD_ID, /* OF1.4+(17,3). Bundle ID already exists. */ OFPERR_OFPBFC_BUNDLE_EXIST, /* OF1.4+(17,4). Bundle ID is closed. */ OFPERR_OFPBFC_BUNDLE_CLOSED, /* OF1.4+(17,5). Too many bundle IDs. */ OFPERR_OFPBFC_OUT_OF_BUNDLES, /* OF1.4+(17,6). Unsupported of unknown message control type. */ OFPERR_OFPBFC_BAD_TYPE, /* OF1.4+(17,7). Unsupported, unknown, or inconsistent flags. */ OFPERR_OFPBFC_BAD_FLAGS, /* OF1.4+(17,8). Length problem in included message. */ OFPERR_OFPBFC_MSG_BAD_LEN, /* OF1.4+(17,9). Inconsistent or duplicate XID. */ OFPERR_OFPBFC_MSG_BAD_XID, /* OF1.4+(17,10). Unsupported message in this bundle. */ OFPERR_OFPBFC_MSG_UNSUP, /* OF1.4+(17,11). Unsupported message combination in this bundle. */ OFPERR_OFPBFC_MSG_CONFLICT, /* OF1.4+(17,12). Cant handle this many messages in bundle. */ OFPERR_OFPBFC_MSG_TOO_MANY, /* OF1.4+(17,13). One message in bundle failed. */ OFPERR_OFPBFC_MSG_FAILED, /* OF1.4+(17,14). Bundle is taking too long. */ OFPERR_OFPBFC_TIMEOUT, /* OF1.4+(17,15). Bundle is locking the resource. */ OFPERR_OFPBFC_BUNDLE_IN_PROGRESS, /* NX1.4+(22). In an OFPT_BUNDLE_ADD_MESSAGE, the OpenFlow version in the * inner and outer messages differ. */ OFPERR_NXBFC_BAD_VERSION, /* ## ------------------------- ## */ /* ## OFPET_FLOW_MONITOR_FAILED ## */ /* ## ------------------------- ## */ /* OF1.4+(16,0). Unspecified error. */ OFPERR_OFPMOFC_UNKNOWN, /* NX1.0-1.1(1,517), NX1.2-1.3(6), OF1.4+(16,1). Monitor not added * because a Monitor ADD attempted to replace an existing Monitor. */ OFPERR_OFPMOFC_MONITOR_EXISTS, /* OF1.4+(16,2). Monitor not added because * Monitor specified is invalid. */ OFPERR_OFPMOFC_INVALID_MONITOR, /* NX1.0-1.1(1,519), NX1.2-1.3(8), OF1.4+(16,3). Monitor not modified * because a Monitor MODIFY attempted to modify a non-existent Monitor. */ OFPERR_OFPMOFC_UNKNOWN_MONITOR, /* OF1.4+(16,4). Unsupported or unknown command. */ OFPERR_OFPMOFC_BAD_COMMAND, /* NX1.0-1.1(1,518), NX1.2-1.3(7), OF1.4+(16,5). Flag configuration * unsupported. */ OFPERR_OFPMOFC_BAD_FLAGS, /* OF1.4+(16,6). Specified table does not exist. */ OFPERR_OFPMOFC_BAD_TABLE_ID, /* OF1.4+(16,7). Error in output port/group. */ OFPERR_OFPMOFC_BAD_OUT, /* ## ----------------------------- ## */ /* ## OFPET_TLV_TABLE_MOD_FAILED ## */ /* ## ----------------------------- ## */ /* NX1.0-1.1(1,527), NX1.2+(16). The TLV table mod command is not * recognized as a valid operation. */ OFPERR_NXTTMFC_BAD_COMMAND, /* NX1.0-1.1(1,528), NX1.2+(17). The option length is not a valid * option size for TLVs. */ OFPERR_NXTTMFC_BAD_OPT_LEN, /* NX1.0-1.1(1,529), NX1.2+(18). The field index is out of range for * the supported NX_TUN_METADATA match. */ OFPERR_NXTTMFC_BAD_FIELD_IDX, /* NX1.0-1.1(1,530), NX1.2+(19). The total set of configured options * exceeds the maximum supported by the switch. */ OFPERR_NXTTMFC_TABLE_FULL, /* NX1.0-1.1(1,531), NX1.2+(20). The controller issued an NXTTMC_ADD * command for a field index that is already mapped. */ OFPERR_NXTTMFC_ALREADY_MAPPED, /* NX1.0-1.1(1,532), NX1.2+(21). The option TLV that is attempting * to be mapped is the same as one assigned to a different field. */ OFPERR_NXTTMFC_DUP_ENTRY, /* ## ------------------ ## */ /* ## OFPET_EXPERIMENTER ## */ /* ## ------------------ ## */ }; const char *ofperr_domain_get_name(enum ofp_version); bool ofperr_is_valid(enum ofperr); enum ofperr ofperr_from_name(const char *); enum ofperr ofperr_decode_msg(const struct ofp_header *, struct ofpbuf *payload); struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *); struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version, const char *); int ofperr_get_vendor(enum ofperr, enum ofp_version); int ofperr_get_type(enum ofperr, enum ofp_version); int ofperr_get_code(enum ofperr, enum ofp_version); const char *ofperr_get_name(enum ofperr); const char *ofperr_get_description(enum ofperr); void ofperr_format(struct ds *, enum ofperr); const char *ofperr_to_string(enum ofperr); #endif /* ofp-errors.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlog.man0000644000000000000000000000013213534540071016056 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.749845818 openvswitch-2.5.9/lib/vlog.man0000644000175000017500000001042613534540071017547 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .IP "\fB\-v\fR[\fIspec\fR] .IQ "\fB\-\-verbose=\fR[\fIspec\fR] . Sets logging levels. Without any \fIspec\fR, sets the log level for every module and destination to \fBdbg\fR. Otherwise, \fIspec\fR is a list of words separated by spaces or commas or colons, up to one from each category below: . .RS .IP \(bu A valid module name, as displayed by the \fBvlog/list\fR command on \fBovs\-appctl\fR(8), limits the log level change to the specified module. . .IP \(bu \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level change to only to the system log, to the console, or to a file, respectively. (If \fB\-\-detach\fR is specified, \fB\*(PN\fR closes its standard file descriptors, so logging to the console will have no effect.) .IP On Windows platform, \fBsyslog\fR is accepted as a word and is only useful along with the \fB\-\-syslog\-target\fR option (the word has no effect otherwise). . .IP \(bu \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or \fBdbg\fR, to control the log level. Messages of the given severity or higher will be logged, and messages of lower severity will be filtered out. \fBoff\fR filters out all messages. See \fBovs\-appctl\fR(8) for a definition of each log level. .RE . .IP Case is not significant within \fIspec\fR. .IP Regardless of the log levels set for \fBfile\fR, logging to a file will not take place unless \fB\-\-log\-file\fR is also specified (see below). .IP For compatibility with older versions of OVS, \fBany\fR is accepted as a word but has no effect. . .IP "\fB\-v\fR" .IQ "\fB\-\-verbose\fR" Sets the maximum logging verbosity level, equivalent to \fB\-\-verbose=dbg\fR. . .IP "\fB\-vPATTERN:\fIdestination\fB:\fIpattern\fR" .IQ "\fB\-\-verbose=PATTERN:\fIdestination\fB:\fIpattern\fR" Sets the log pattern for \fIdestination\fR to \fIpattern\fR. Refer to \fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR. . .IP "\fB\-vFACILITY:\fIfacility\fR" .IQ "\fB\-\-verbose=FACILITY:\fIfacility\fR" Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of \fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR, \fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR, \fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR, \fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or \fBlocal7\fR. If this option is not specified, \fBdaemon\fR is used as the default for the local system syslog and \fBlocal0\fR is used while sending a message to the target provided via the \fB\-\-syslog\-target\fR option. . .TP \fB\-\-log\-file\fR[\fB=\fIfile\fR] Enables logging to a file. If \fIfile\fR is specified, then it is used as the exact name for the log file. The default log file name used if \fIfile\fR is omitted is \fB@LOGDIR@/\*(PN.log\fR. . .IP "\fB\-\-syslog\-target=\fIhost\fB:\fIport\fR" Send syslog messages to UDP \fIport\fR on \fIhost\fR, in addition to the system syslog. The \fIhost\fR must be a numerical IP address, not a hostname. . .IP "\fB\-\-syslog\-method=\fImethod\fR" Specify \fImethod\fR how syslog messages should be sent to syslog daemon. Following forms are supported: .RS .IP \(bu \fBlibc\fR, use libc \fBsyslog()\fR function. This is the default behavior. Downside of using this options is that libc adds fixed prefix to every message before it is actually sent to the syslog daemon over \fB/dev/log\fR UNIX domain socket. .IP \(bu \fBunix:\fIfile\fR\fR, use UNIX domain socket directly. It is possible to specify arbitrary message format with this option. However, \fBrsyslogd 8.9\fR and older versions use hard coded parser function anyway that limits UNIX domain socket use. If you want to use arbitrary message format with older \fBrsyslogd\fR versions, then use UDP socket to localhost IP address instead. .IP \(bu \fBudp:\fIip\fR:\fIport\fR\fR, use UDP socket. With this method it is possible to use arbitrary message format also with older \fBrsyslogd\fR. When sending syslog messages over UDP socket extra precaution needs to be taken into account, for example, syslog daemon needs to be configured to listen on the specified UDP port, accidental iptables rules could be interfering with local syslog traffic and there are some security considerations that apply to UDP sockets, but do not apply to UNIX domain sockets. .RE openvswitch-2.5.9/lib/PaxHeaders.82075/odp-util.h0000644000000000000000000000013213534540071016320 xustar0030 mtime=1567801401.493681846 30 atime=1567801402.085686193 30 ctime=1567801424.789853486 openvswitch-2.5.9/lib/odp-util.h0000644000175000017500000003400613534540071020011 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ODP_UTIL_H #define ODP_UTIL_H 1 #include #include #include #include #include "flow.h" #include "hash.h" #include "hmap.h" #include "odp-netlink.h" #include "openflow/openflow.h" #include "util.h" struct ds; struct nlattr; struct ofpbuf; struct simap; struct pkt_metadata; #define SLOW_PATH_REASONS \ /* These reasons are mutually exclusive. */ \ SPR(SLOW_CFM, "cfm", "Consists of CFM packets") \ SPR(SLOW_BFD, "bfd", "Consists of BFD packets") \ SPR(SLOW_LACP, "lacp", "Consists of LACP packets") \ SPR(SLOW_STP, "stp", "Consists of STP packets") \ SPR(SLOW_LLDP, "lldp", "Consists of LLDP packets") \ SPR(SLOW_CONTROLLER, "controller", \ "Sends \"packet-in\" messages to the OpenFlow controller") \ SPR(SLOW_ACTION, "action", \ "Uses action(s) not supported by datapath") /* Indexes for slow-path reasons. Client code uses "enum slow_path_reason" * values instead of these, these are just a way to construct those. */ enum { #define SPR(ENUM, STRING, EXPLANATION) ENUM##_INDEX, SLOW_PATH_REASONS #undef SPR }; /* Reasons why a subfacet might not be fast-pathable. * * Each reason is a separate bit to allow reasons to be combined. */ enum slow_path_reason { #define SPR(ENUM, STRING, EXPLANATION) ENUM = 1 << ENUM##_INDEX, SLOW_PATH_REASONS #undef SPR }; /* Mask of all slow_path_reasons. */ enum { SLOW_PATH_REASON_MASK = 0 #define SPR(ENUM, STRING, EXPLANATION) | 1 << ENUM##_INDEX SLOW_PATH_REASONS #undef SPR }; const char *slow_path_reason_to_explanation(enum slow_path_reason); #define ODPP_LOCAL ODP_PORT_C(OVSP_LOCAL) #define ODPP_NONE ODP_PORT_C(UINT32_MAX) void format_odp_actions(struct ds *, const struct nlattr *odp_actions, size_t actions_len); int odp_actions_from_string(const char *, const struct simap *port_names, struct ofpbuf *odp_actions); /* A map from odp port number to its name. */ struct odp_portno_names { struct hmap_node hmap_node; /* A node in a port number to name hmap. */ odp_port_t port_no; /* Port number in the datapath. */ char *name; /* Name associated with the above 'port_no'. */ }; void odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no, char *port_name); void odp_portno_names_destroy(struct hmap *portno_names); /* The maximum number of bytes that odp_flow_key_from_flow() appends to a * buffer. This is the upper bound on the length of a nlattr-formatted flow * key that ovs-vswitchd fully understands. * * OVS doesn't insist that ovs-vswitchd and the datapath have exactly the same * idea of a flow, so therefore this value isn't necessarily an upper bound on * the length of a flow key that the datapath can pass to ovs-vswitchd. * * The longest nlattr-formatted flow key appended by odp_flow_key_from_flow() * would be: * * struct pad nl hdr total * ------ --- ------ ----- * OVS_KEY_ATTR_PRIORITY 4 -- 4 8 * OVS_KEY_ATTR_TUNNEL 0 -- 4 4 * - OVS_TUNNEL_KEY_ATTR_ID 8 -- 4 12 * - OVS_TUNNEL_KEY_ATTR_IPV4_SRC 4 -- 4 8 * - OVS_TUNNEL_KEY_ATTR_IPV4_DST 4 -- 4 8 * - OVS_TUNNEL_KEY_ATTR_IPV6_SRC 16 -- 4 20 * - OVS_TUNNEL_KEY_ATTR_IPV6_DST 16 -- 4 20 * - OVS_TUNNEL_KEY_ATTR_TOS 1 3 4 8 * - OVS_TUNNEL_KEY_ATTR_TTL 1 3 4 8 * - OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT 0 -- 4 4 * - OVS_TUNNEL_KEY_ATTR_CSUM 0 -- 4 4 * - OVS_TUNNEL_KEY_ATTR_OAM 0 -- 4 4 * - OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS 256 -- 4 260 * - OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS - -- - - (shared with _GENEVE_OPTS) * OVS_KEY_ATTR_IN_PORT 4 -- 4 8 * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8 * OVS_KEY_ATTR_DP_HASH 4 -- 4 8 * OVS_KEY_ATTR_RECIRC_ID 4 -- 4 8 * OVS_KEY_ATTR_CT_STATE 4 -- 4 8 * OVS_KEY_ATTR_CT_ZONE 2 2 4 8 * OVS_KEY_ATTR_CT_MARK 4 -- 4 8 * OVS_KEY_ATTR_CT_LABEL 16 -- 4 20 * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype) * OVS_KEY_ATTR_VLAN 2 2 4 8 * OVS_KEY_ATTR_ENCAP 0 -- 4 4 (VLAN encapsulation) * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (inner VLAN ethertype) * OVS_KEY_ATTR_IPV6 40 -- 4 44 * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ---------------------------------------------------------- * total 572 * * We include some slack space in case the calculation isn't quite right or we * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 640 BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow * key. An array of "struct nlattr" might not, in theory, be sufficiently * aligned because it only contains 16-bit types. */ struct odputil_keybuf { uint32_t keybuf[DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4)]; }; enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, bool udpif, struct flow_tnl *); int odp_ufid_from_string(const char *s_, ovs_u128 *ufid); void odp_format_ufid(const ovs_u128 *ufid, struct ds *); void odp_flow_format(const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const struct hmap *portno_names, struct ds *, bool verbose); void odp_flow_key_format(const struct nlattr *, size_t, struct ds *); int odp_flow_from_string(const char *s, const struct simap *port_names, struct ofpbuf *, struct ofpbuf *); /* Indicates support for various fields. This defines how flows will be * serialised. */ struct odp_support { /* Maximum number of MPLS label stack entries to serialise in a mask. */ size_t max_mpls_depth; /* If this is true, then recirculation fields will always be serialised. */ bool recirc; /* If true, serialise the corresponding OVS_KEY_ATTR_CONN_* field. */ bool ct_state; bool ct_zone; bool ct_mark; bool ct_label; }; struct odp_flow_key_parms { /* The flow and mask to be serialized. In the case of masks, 'flow' * is used as a template to determine how to interpret 'mask'. For * example, the 'dl_type' of 'mask' describes the mask, but it doesn't * indicate whether the other fields should be interpreted as ARP, IPv4, * IPv6, etc. */ const struct flow *flow; const struct flow *mask; /* 'flow->in_port' is ignored (since it is likely to be an OpenFlow port * number rather than a datapath port number). Instead, if 'odp_in_port' * is anything other than ODPP_NONE, it is included in 'buf' as the input * port. */ odp_port_t odp_in_port; /* Indicates support for various fields. If the datapath supports a field, * then it will always be serialised. */ struct odp_support support; /* The netlink formatted version of the flow. It is used in cases where * the mask cannot be constructed from the OVS internal representation * and needs to see the original form. */ const struct ofpbuf *key_buf; }; void odp_flow_key_from_flow(const struct odp_flow_key_parms *, struct ofpbuf *); void odp_flow_key_from_mask(const struct odp_flow_key_parms *, struct ofpbuf *); uint32_t odp_flow_key_hash(const struct nlattr *, size_t); /* Estimated space needed for metadata. */ enum { ODP_KEY_METADATA_SIZE = 9 * 8 }; void odp_key_from_pkt_metadata(struct ofpbuf *, const struct pkt_metadata *); void odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, struct pkt_metadata *md); /* How well a kernel-provided flow key (a sequence of OVS_KEY_ATTR_* * attributes) matches OVS userspace expectations. * * These values are arranged so that greater values are "more important" than * lesser ones. In particular, a single flow key can fit the descriptions for * both ODP_FIT_TOO_LITTLE and ODP_FIT_TOO_MUCH. Such a key is treated as * ODP_FIT_TOO_LITTLE. */ enum odp_key_fitness { ODP_FIT_PERFECT, /* The key had exactly the fields we expect. */ ODP_FIT_TOO_MUCH, /* The key had fields we don't understand. */ ODP_FIT_TOO_LITTLE, /* The key lacked fields we expected to see. */ ODP_FIT_ERROR, /* The key was invalid. */ }; enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t, struct flow *); enum odp_key_fitness odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len, const struct nlattr *flow_key, size_t flow_key_len, struct flow_wildcards *mask, const struct flow *flow); enum odp_key_fitness odp_flow_key_to_flow_udpif(const struct nlattr *, size_t, struct flow *); enum odp_key_fitness odp_flow_key_to_mask_udpif(const struct nlattr *mask_key, size_t mask_key_len, const struct nlattr *flow_key, size_t flow_key_len, struct flow_wildcards *mask, const struct flow *flow); const char *odp_key_fitness_to_string(enum odp_key_fitness); void commit_odp_tunnel_action(const struct flow *, struct flow *base, struct ofpbuf *odp_actions); void commit_masked_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, const void *key, const void *mask, size_t key_size); enum slow_path_reason commit_odp_actions(const struct flow *, struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked); /* ofproto-dpif interface. * * The following types and functions are logically part of ofproto-dpif. * ofproto-dpif puts values of these types into the flows that it installs in * the kernel datapath, though, so ovs-dpctl needs to interpret them so that * it can print flows in a more human-readable manner. */ enum user_action_cookie_type { USER_ACTION_COOKIE_UNSPEC, USER_ACTION_COOKIE_SFLOW, /* Packet for per-bridge sFlow sampling. */ USER_ACTION_COOKIE_SLOW_PATH, /* Userspace must process this flow. */ USER_ACTION_COOKIE_FLOW_SAMPLE, /* Packet for per-flow sampling. */ USER_ACTION_COOKIE_IPFIX, /* Packet for per-bridge IPFIX sampling. */ }; /* user_action_cookie is passed as argument to OVS_ACTION_ATTR_USERSPACE. * Since it is passed to kernel as u64, its size has to be 8 bytes. */ union user_action_cookie { uint16_t type; /* enum user_action_cookie_type. */ struct { uint16_t type; /* USER_ACTION_COOKIE_SFLOW. */ ovs_be16 vlan_tci; /* Destination VLAN TCI. */ uint32_t output; /* SFL_FLOW_SAMPLE_TYPE 'output' value. */ } sflow; struct { uint16_t type; /* USER_ACTION_COOKIE_SLOW_PATH. */ uint16_t unused; uint32_t reason; /* enum slow_path_reason. */ } slow_path; struct { uint16_t type; /* USER_ACTION_COOKIE_FLOW_SAMPLE. */ uint16_t probability; /* Sampling probability. */ uint32_t collector_set_id; /* ID of IPFIX collector set. */ uint32_t obs_domain_id; /* Observation Domain ID. */ uint32_t obs_point_id; /* Observation Point ID. */ } flow_sample; struct { uint16_t type; /* USER_ACTION_COOKIE_IPFIX. */ odp_port_t output_odp_port; /* The output odp port. */ } ipfix; }; BUILD_ASSERT_DECL(sizeof(union user_action_cookie) == 16); size_t odp_put_userspace_action(uint32_t pid, const void *userdata, size_t userdata_size, odp_port_t tunnel_out_port, bool include_actions, struct ofpbuf *odp_actions); void odp_put_tunnel_action(const struct flow_tnl *tunnel, struct ofpbuf *odp_actions); void odp_put_tnl_push_action(struct ofpbuf *odp_actions, struct ovs_action_push_tnl *data); #endif /* odp-util.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-parse.h0000644000000000000000000000013213534540071016457 xustar0030 mtime=1567801401.521682051 30 atime=1567801402.085686193 30 ctime=1567801424.797853544 openvswitch-2.5.9/lib/ofp-parse.h0000644000175000017500000001042313534540071020145 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenFlow protocol string to flow parser. */ #ifndef OFP_PARSE_H #define OFP_PARSE_H 1 #include #include #include #include "compiler.h" #include "openvswitch/types.h" #include "packets.h" struct flow; struct ofpbuf; struct ofputil_flow_mod; struct ofputil_flow_monitor_request; struct ofputil_flow_stats_request; struct ofputil_group_mod; struct ofputil_meter_mod; struct ofputil_table_mod; struct ofputil_tlv_table_mod; struct simap; enum ofputil_protocol; char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string, int command, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_table_mod(struct ofputil_table_mod *, const char *table_id, const char *flow_miss_handling, uint32_t *usable_versions) OVS_WARN_UNUSED_RESULT; char *parse_ofp_flow_mod_file(const char *file_name, int command, struct ofputil_flow_mod **fms, size_t *n_fms, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *, bool aggregate, const char *string, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, const struct simap *portno_names); char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string, int command, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *, const char *, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_group_mod_file(const char *file_name, uint16_t command, struct ofputil_group_mod **gms, size_t *n_gms, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_group_mod_str(struct ofputil_group_mod *, uint16_t command, const char *string, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *, uint16_t command, const char *string, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *str_to_u8(const char *str, const char *name, uint8_t *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_u16(const char *str, const char *name, uint16_t *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_u32(const char *str, uint32_t *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_u64(const char *str, uint64_t *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_be64(const char *str, ovs_be64 *valuep) OVS_WARN_UNUSED_RESULT; char *str_to_mac(const char *str, struct eth_addr *mac) OVS_WARN_UNUSED_RESULT; char *str_to_ip(const char *str, ovs_be32 *ip) OVS_WARN_UNUSED_RESULT; char *str_to_connhelper(const char *str, uint16_t *alg) OVS_WARN_UNUSED_RESULT; char *parse_ofp_table_vacancy(struct ofputil_table_mod *, const char *flow_miss_handling) OVS_WARN_UNUSED_RESULT; #endif /* ofp-parse.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/backtrace.h0000644000000000000000000000013213534540071016502 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.657852512 openvswitch-2.5.9/lib/backtrace.h0000644000175000017500000000475213534540071020200 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BACKTRACE_H #define BACKTRACE_H 1 #include #include "dynamic-string.h" /* log_backtrace() will save the backtrace of a running program * into the log at the DEBUG level. * * To use it, insert the following code to where backtrace is * desired: * #include "backtrace.h" * * log_backtrace(); * // A message can be added with log_backtrace_msg("your message") * * * A typical log will look like the following. The hex numbers listed after * "backtrace" are the addresses of the backtrace. * * 2014-03-13T23:18:11.979Z|00002|backtrace(revalidator_6)|ERR|lib/dpif-netdev.c:1312: (backtrace: 0x00521f57 0x00460365 0x00463ea4 0x0046470b 0x0043b32d 0x0043bac3 0x0043bae2 0x0043943b 0x004c22b3 0x2b5b3ac94e9a 0x2b5b3b4a33fd) * * The following bash command can be used to view backtrace in * a more readable form. * addr2line -p -e vswitchd/ovs-vswitchd * * An typical run and output will look like: * addr2line -p -e vswitchd/ovs-vswitchd 0x00521f57 0x00460365 0x00463ea4 * 0x0046470b 0x0043b32d 0x0043bac3 0x0043bae2 0x0043943b 0x004c22b3 * 0x2b5b3ac94e9a 0x2b5b3b4a33fd * * openvswitch/lib/backtrace.c:33 * openvswitch/lib/dpif-netdev.c:1312 * openvswitch/lib/dpif.c:937 * openvswitch/lib/dpif.c:1258 * openvswitch/ofproto/ofproto-dpif-upcall.c:1440 * openvswitch/ofproto/ofproto-dpif-upcall.c:1595 * openvswitch/ofproto/ofproto-dpif-upcall.c:160 * openvswitch/ofproto/ofproto-dpif-upcall.c:717 * openvswitch/lib/ovs-thread.c:268 * ??:0 * ??:0 */ #define log_backtrace() log_backtrace_at(NULL, OVS_SOURCE_LOCATOR); #define log_backtrace_msg(msg) log_backtrace_at(msg, OVS_SOURCE_LOCATOR); #define BACKTRACE_MAX_FRAMES 31 struct backtrace { int n_frames; uintptr_t frames[BACKTRACE_MAX_FRAMES]; }; void backtrace_capture(struct backtrace *); void log_backtrace_at(const char *msg, const char *where); #endif /* backtrace.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-numa.h0000644000000000000000000000013213534540071016330 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 30 ctime=1567801424.985854931 openvswitch-2.5.9/lib/ovs-numa.h0000644000175000017500000000701713534540071020023 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_NUMA_H #define OVS_NUMA_H 1 #include #include #include "compiler.h" #include "list.h" #define OVS_CORE_UNSPEC INT_MAX #define OVS_NUMA_UNSPEC INT_MAX /* Dump of a list of 'struct ovs_numa_info'. */ struct ovs_numa_dump { struct ovs_list dump; }; /* A numa_id - core_id pair. */ struct ovs_numa_info { struct ovs_list list_node; int numa_id; unsigned core_id; }; #ifdef __linux__ void ovs_numa_init(void); bool ovs_numa_numa_id_is_valid(int numa_id); bool ovs_numa_core_id_is_valid(unsigned core_id); bool ovs_numa_core_is_pinned(unsigned core_id); int ovs_numa_get_n_numas(void); void ovs_numa_set_cpu_mask(const char *cmask); int ovs_numa_get_n_cores(void); int ovs_numa_get_numa_id(unsigned core_id); int ovs_numa_get_n_cores_on_numa(int numa_id); int ovs_numa_get_n_unpinned_cores_on_numa(int numa_id); bool ovs_numa_try_pin_core_specific(unsigned core_id); unsigned ovs_numa_get_unpinned_core_any(void); unsigned ovs_numa_get_unpinned_core_on_numa(int numa_id); void ovs_numa_unpin_core(unsigned core_id); struct ovs_numa_dump *ovs_numa_dump_cores_on_numa(int numa_id); void ovs_numa_dump_destroy(struct ovs_numa_dump *); #define FOR_EACH_CORE_ON_NUMA(ITER, DUMP) \ LIST_FOR_EACH((ITER), list_node, &(DUMP)->dump) #else static inline void ovs_numa_init(void) { /* Nothing */ } static inline bool ovs_numa_numa_id_is_valid(int numa_id OVS_UNUSED) { return false; } static inline bool ovs_numa_core_id_is_valid(unsigned core_id OVS_UNUSED) { return false; } static inline bool ovs_numa_core_is_pinned(unsigned core_id OVS_UNUSED) { return false; } static inline void ovs_numa_set_cpu_mask(const char *cmask OVS_UNUSED) { /* Nothing */ } static inline int ovs_numa_get_n_numas(void) { return OVS_NUMA_UNSPEC; } static inline int ovs_numa_get_n_cores(void) { return OVS_CORE_UNSPEC; } static inline int ovs_numa_get_numa_id(unsigned core_id OVS_UNUSED) { return OVS_NUMA_UNSPEC; } static inline int ovs_numa_get_n_cores_on_numa(int numa_id OVS_UNUSED) { return OVS_CORE_UNSPEC; } static inline int ovs_numa_get_n_unpinned_cores_on_numa(int numa_id OVS_UNUSED) { return OVS_CORE_UNSPEC; } static inline bool ovs_numa_try_pin_core_specific(unsigned core_id OVS_UNUSED) { return false; } static inline unsigned ovs_numa_get_unpinned_core_any(void) { return OVS_CORE_UNSPEC; } static inline unsigned ovs_numa_get_unpinned_core_on_numa(int numa_id OVS_UNUSED) { return OVS_CORE_UNSPEC; } static inline void ovs_numa_unpin_core(unsigned core_id OVS_UNUSED) { /* Nothing */ } static inline struct ovs_numa_dump * ovs_numa_dump_cores_on_numa(int numa_id OVS_UNUSED) { return NULL; } static inline void ovs_numa_dump_destroy(struct ovs_numa_dump *dump OVS_UNUSED) { /* Nothing */ } /* No loop. */ #define FOR_EACH_CORE_ON_NUMA(ITER, DUMP) \ for ((ITER) = NULL; (ITER);) #endif /* __linux__ */ #endif /* ovs-thead.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/table.h0000644000000000000000000000013113534540071015651 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90985437 openvswitch-2.5.9/lib/table.h0000644000175000017500000001030713534540071017341 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TABLE_H #define TABLE_H 1 #include #include #include "compiler.h" struct table_style; /* Manipulating tables and their rows and columns. */ struct table { struct cell *cells; struct column *columns; size_t n_columns, allocated_columns; size_t n_rows, allocated_rows; size_t current_column; char *caption; bool timestamp; }; void table_init(struct table *); void table_destroy(struct table *); void table_set_caption(struct table *, char *caption); void table_set_timestamp(struct table *, bool timestamp); void table_add_column(struct table *, const char *heading, ...) OVS_PRINTF_FORMAT(2, 3); void table_add_row(struct table *); /* Table cells. */ struct cell { /* Literal text. */ char *text; /* JSON. */ struct json *json; const struct ovsdb_type *type; }; struct cell *table_add_cell(struct table *); /* Table formatting. */ enum table_format { TF_TABLE, /* 2-d table. */ TF_LIST, /* One cell per line, one row per paragraph. */ TF_HTML, /* HTML table. */ TF_CSV, /* Comma-separated lines. */ TF_JSON /* JSON. */ }; enum cell_format { CF_STRING, /* String format. */ CF_BARE, /* String format without most punctuation. */ CF_JSON /* JSON. */ }; struct table_style { enum table_format format; /* TF_*. */ enum cell_format cell_format; /* CF_*. */ bool headings; /* Include headings? */ int json_flags; /* CF_JSON: Flags for json_to_string(). */ }; #define TABLE_STYLE_DEFAULT { TF_TABLE, CF_STRING, true, JSSF_SORT } #define TABLE_OPTION_ENUMS \ OPT_NO_HEADINGS, \ OPT_PRETTY, \ OPT_BARE #define TABLE_LONG_OPTIONS \ {"format", required_argument, NULL, 'f'}, \ {"data", required_argument, NULL, 'd'}, \ {"no-headings", no_argument, NULL, OPT_NO_HEADINGS}, \ {"pretty", no_argument, NULL, OPT_PRETTY}, \ {"bare", no_argument, NULL, OPT_BARE} #define TABLE_OPTION_HANDLERS(STYLE) \ case 'f': \ table_parse_format(STYLE, optarg); \ break; \ \ case 'd': \ table_parse_cell_format(STYLE, optarg); \ break; \ \ case OPT_NO_HEADINGS: \ (STYLE)->headings = false; \ break; \ \ case OPT_PRETTY: \ (STYLE)->json_flags |= JSSF_PRETTY; \ break; \ \ case OPT_BARE: \ (STYLE)->format = TF_LIST; \ (STYLE)->cell_format = CF_BARE; \ (STYLE)->headings = false; \ break; void table_parse_format(struct table_style *, const char *format); void table_parse_cell_format(struct table_style *, const char *format); void table_print(const struct table *, const struct table_style *); #endif /* table.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/poll-loop.c0000644000000000000000000000013213534540071016473 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.849853928 openvswitch-2.5.9/lib/poll-loop.c0000644000175000017500000003154713534540071020173 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "poll-loop.h" #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "list.h" #include "ovs-thread.h" #include "seq.h" #include "socket-util.h" #include "timeval.h" #include "openvswitch/vlog.h" #include "hmap.h" #include "hash.h" VLOG_DEFINE_THIS_MODULE(poll_loop); COVERAGE_DEFINE(poll_create_node); COVERAGE_DEFINE(poll_zero_timeout); struct poll_node { struct hmap_node hmap_node; struct pollfd pollfd; /* Events to pass to time_poll(). */ HANDLE wevent; /* Events for WaitForMultipleObjects(). */ const char *where; /* Where poll_node was created. */ }; struct poll_loop { /* All active poll waiters. */ struct hmap poll_nodes; /* Time at which to wake up the next call to poll_block(), LLONG_MIN to * wake up immediately, or LLONG_MAX to wait forever. */ long long int timeout_when; /* In msecs as returned by time_msec(). */ const char *timeout_where; /* Where 'timeout_when' was set. */ }; static struct poll_loop *poll_loop(void); /* Look up the node with same fd or wevent. */ static struct poll_node * find_poll_node(struct poll_loop *loop, int fd, HANDLE wevent) { struct poll_node *node; /* Both 'fd' and 'wevent' cannot be set. */ ovs_assert(!fd != !wevent); HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash_2words(fd, (uint32_t)wevent), &loop->poll_nodes) { if ((fd && node->pollfd.fd == fd) || (wevent && node->wevent == wevent)) { return node; } } return NULL; } /* On Unix based systems: * * Registers 'fd' as waiting for the specified 'events' (which should be * POLLIN or POLLOUT or POLLIN | POLLOUT). The following call to * poll_block() will wake up when 'fd' becomes ready for one or more of the * requested events. The 'fd's are given to poll() function later. * * On Windows system: * * If 'fd' is specified, create a new 'wevent'. Association of 'fd' and * 'wevent' for 'events' happens in poll_block(). If 'wevent' is specified, * it is assumed that it is unrelated to any sockets and poll_block() * will wake up on any event on that 'wevent'. It is an error to pass * both 'wevent' and 'fd'. * * The event registration is one-shot: only the following call to * poll_block() is affected. The event will need to be re-registered after * poll_block() is called if it is to persist. * * ('where' is used in debug logging. Commonly one would use poll_fd_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ static void poll_create_node(int fd, HANDLE wevent, short int events, const char *where) { struct poll_loop *loop = poll_loop(); struct poll_node *node; COVERAGE_INC(poll_create_node); /* Both 'fd' and 'wevent' cannot be set. */ ovs_assert(!fd != !wevent); /* Check for duplicate. If found, "or" the events. */ node = find_poll_node(loop, fd, wevent); if (node) { node->pollfd.events |= events; } else { node = xzalloc(sizeof *node); hmap_insert(&loop->poll_nodes, &node->hmap_node, hash_2words(fd, (uint32_t)wevent)); node->pollfd.fd = fd; node->pollfd.events = events; #ifdef _WIN32 if (!wevent) { wevent = CreateEvent(NULL, FALSE, FALSE, NULL); } #endif node->wevent = wevent; node->where = where; } } /* Registers 'fd' as waiting for the specified 'events' (which should be POLLIN * or POLLOUT or POLLIN | POLLOUT). The following call to poll_block() will * wake up when 'fd' becomes ready for one or more of the requested events. * * On Windows, 'fd' must be a socket. * * The event registration is one-shot: only the following call to poll_block() * is affected. The event will need to be re-registered after poll_block() is * called if it is to persist. * * ('where' is used in debug logging. Commonly one would use poll_fd_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ void poll_fd_wait_at(int fd, short int events, const char *where) { poll_create_node(fd, 0, events, where); } #ifdef _WIN32 /* Registers for the next call to poll_block() to wake up when 'wevent' is * signaled. * * The event registration is one-shot: only the following call to poll_block() * is affected. The event will need to be re-registered after poll_block() is * called if it is to persist. * * ('where' is used in debug logging. Commonly one would use * poll_wevent_wait() to automatically provide the caller's source file and * line number for 'where'.) */ void poll_wevent_wait_at(HANDLE wevent, const char *where) { poll_create_node(0, wevent, 0, where); } #endif /* _WIN32 */ /* Causes the following call to poll_block() to block for no more than 'msec' * milliseconds. If 'msec' is nonpositive, the following call to poll_block() * will not block at all. * * The timer registration is one-shot: only the following call to poll_block() * is affected. The timer will need to be re-registered after poll_block() is * called if it is to persist. * * ('where' is used in debug logging. Commonly one would use poll_timer_wait() * to automatically provide the caller's source file and line number for * 'where'.) */ void poll_timer_wait_at(long long int msec, const char *where) { long long int now = time_msec(); long long int when; if (msec <= 0) { /* Wake up immediately. */ when = LLONG_MIN; } else if ((unsigned long long int) now + msec <= LLONG_MAX) { /* Normal case. */ when = now + msec; } else { /* now + msec would overflow. */ when = LLONG_MAX; } poll_timer_wait_until_at(when, where); } /* Causes the following call to poll_block() to wake up when the current time, * as returned by time_msec(), reaches 'when' or later. If 'when' is earlier * than the current time, the following call to poll_block() will not block at * all. * * The timer registration is one-shot: only the following call to poll_block() * is affected. The timer will need to be re-registered after poll_block() is * called if it is to persist. * * ('where' is used in debug logging. Commonly one would use * poll_timer_wait_until() to automatically provide the caller's source file * and line number for 'where'.) */ void poll_timer_wait_until_at(long long int when, const char *where) { struct poll_loop *loop = poll_loop(); if (when < loop->timeout_when) { loop->timeout_when = when; loop->timeout_where = where; } } /* Causes the following call to poll_block() to wake up immediately, without * blocking. * * ('where' is used in debug logging. Commonly one would use * poll_immediate_wake() to automatically provide the caller's source file and * line number for 'where'.) */ void poll_immediate_wake_at(const char *where) { poll_timer_wait_at(0, where); } /* Logs, if appropriate, that the poll loop was awakened by an event * registered at 'where' (typically a source file and line number). The other * arguments have two possible interpretations: * * - If 'pollfd' is nonnull then it should be the "struct pollfd" that caused * the wakeup. 'timeout' is ignored. * * - If 'pollfd' is NULL then 'timeout' is the number of milliseconds after * which the poll loop woke up. */ static void log_wakeup(const char *where, const struct pollfd *pollfd, int timeout) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); enum vlog_level level; int cpu_usage; struct ds s; cpu_usage = get_cpu_usage(); if (VLOG_IS_DBG_ENABLED()) { level = VLL_DBG; } else if (cpu_usage > 50 && !thread_is_pmd() && !VLOG_DROP_INFO(&rl)) { level = VLL_INFO; } else { return; } ds_init(&s); ds_put_cstr(&s, "wakeup due to "); if (pollfd) { char *description = describe_fd(pollfd->fd); if (pollfd->revents & POLLIN) { ds_put_cstr(&s, "[POLLIN]"); } if (pollfd->revents & POLLOUT) { ds_put_cstr(&s, "[POLLOUT]"); } if (pollfd->revents & POLLERR) { ds_put_cstr(&s, "[POLLERR]"); } if (pollfd->revents & POLLHUP) { ds_put_cstr(&s, "[POLLHUP]"); } if (pollfd->revents & POLLNVAL) { ds_put_cstr(&s, "[POLLNVAL]"); } ds_put_format(&s, " on fd %d (%s)", pollfd->fd, description); free(description); } else { ds_put_format(&s, "%d-ms timeout", timeout); } if (where) { ds_put_format(&s, " at %s", where); } if (cpu_usage >= 0) { ds_put_format(&s, " (%d%% CPU usage)", cpu_usage); } VLOG(level, "%s", ds_cstr(&s)); ds_destroy(&s); } static void free_poll_nodes(struct poll_loop *loop) { struct poll_node *node, *next; HMAP_FOR_EACH_SAFE (node, next, hmap_node, &loop->poll_nodes) { hmap_remove(&loop->poll_nodes, &node->hmap_node); #ifdef _WIN32 if (node->wevent && node->pollfd.fd) { WSAEventSelect(node->pollfd.fd, NULL, 0); CloseHandle(node->wevent); } #endif free(node); } } /* Blocks until one or more of the events registered with poll_fd_wait() * occurs, or until the minimum duration registered with poll_timer_wait() * elapses, or not at all if poll_immediate_wake() has been called. */ void poll_block(void) { struct poll_loop *loop = poll_loop(); struct poll_node *node; struct pollfd *pollfds; HANDLE *wevents = NULL; int elapsed; int retval; int i; /* Register fatal signal events before actually doing any real work for * poll_block. */ fatal_signal_wait(); if (loop->timeout_when == LLONG_MIN) { COVERAGE_INC(poll_zero_timeout); } timewarp_run(); pollfds = xmalloc(hmap_count(&loop->poll_nodes) * sizeof *pollfds); #ifdef _WIN32 wevents = xmalloc(hmap_count(&loop->poll_nodes) * sizeof *wevents); #endif /* Populate with all the fds and events. */ i = 0; HMAP_FOR_EACH (node, hmap_node, &loop->poll_nodes) { pollfds[i] = node->pollfd; #ifdef _WIN32 wevents[i] = node->wevent; if (node->pollfd.fd && node->wevent) { short int wsa_events = 0; if (node->pollfd.events & POLLIN) { wsa_events |= FD_READ | FD_ACCEPT | FD_CLOSE; } if (node->pollfd.events & POLLOUT) { wsa_events |= FD_WRITE | FD_CONNECT | FD_CLOSE; } WSAEventSelect(node->pollfd.fd, node->wevent, wsa_events); } #endif i++; } retval = time_poll(pollfds, hmap_count(&loop->poll_nodes), wevents, loop->timeout_when, &elapsed); if (retval < 0) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "poll: %s", ovs_strerror(-retval)); } else if (!retval) { log_wakeup(loop->timeout_where, NULL, elapsed); } else if (get_cpu_usage() > 50 || VLOG_IS_DBG_ENABLED()) { i = 0; HMAP_FOR_EACH (node, hmap_node, &loop->poll_nodes) { if (pollfds[i].revents) { log_wakeup(node->where, &pollfds[i], 0); } i++; } } free_poll_nodes(loop); loop->timeout_when = LLONG_MAX; loop->timeout_where = NULL; free(pollfds); free(wevents); /* Handle any pending signals before doing anything else. */ fatal_signal_run(); seq_woke(); } static void free_poll_loop(void *loop_) { struct poll_loop *loop = loop_; free_poll_nodes(loop); hmap_destroy(&loop->poll_nodes); free(loop); } static struct poll_loop * poll_loop(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static pthread_key_t key; struct poll_loop *loop; if (ovsthread_once_start(&once)) { xpthread_key_create(&key, free_poll_loop); ovsthread_once_done(&once); } loop = pthread_getspecific(key); if (!loop) { loop = xzalloc(sizeof *loop); hmap_init(&loop->poll_nodes); xpthread_setspecific(key, loop); } return loop; } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-util.h0000644000000000000000000000013213534540071016322 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.801853573 openvswitch-2.5.9/lib/ofp-util.h0000644000175000017500000015436613534540071020027 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFP_UTIL_H #define OFP_UTIL_H 1 #include #include #include #include "bitmap.h" #include "compiler.h" #include "flow.h" #include "list.h" #include "match.h" #include "meta-flow.h" #include "netdev.h" #include "openflow/netronome-ext.h" #include "openflow/nicira-ext.h" #include "openvswitch/types.h" #include "type-props.h" struct ofpbuf; union ofp_action; struct ofpact_set_field; /* Port numbers. */ enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port, ofp_port_t *ofp10_port); ovs_be32 ofputil_port_to_ofp11(ofp_port_t ofp10_port); bool ofputil_port_from_string(const char *, ofp_port_t *portp); void ofputil_format_port(ofp_port_t port, struct ds *); void ofputil_port_to_string(ofp_port_t, char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize); /* Group numbers. */ enum { MAX_GROUP_NAME_LEN = INT_STRLEN(uint32_t) }; bool ofputil_group_from_string(const char *, uint32_t *group_id); void ofputil_format_group(uint32_t group_id, struct ds *); void ofputil_group_to_string(uint32_t group_id, char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize); /* Converting OFPFW10_NW_SRC_MASK and OFPFW10_NW_DST_MASK wildcard bit counts * to and from IP bitmasks. */ ovs_be32 ofputil_wcbits_to_netmask(int wcbits); int ofputil_netmask_to_wcbits(ovs_be32 netmask); /* Protocols. * * A "protocol" is an OpenFlow version plus, for some OpenFlow versions, * a bit extra about the flow match format in use. * * These are arranged from most portable to least portable, or alternatively * from least powerful to most powerful. Protocols earlier on the list are * more likely to be understood for the purpose of making requests, but * protocol later on the list are more likely to accurately describe a flow * within a switch. * * On any given OpenFlow connection, a single protocol is in effect at any * given time. These values use separate bits only because that makes it easy * to test whether a particular protocol is within a given set of protocols and * to implement set union and intersection. */ enum ofputil_protocol { /* OpenFlow 1.0 protocols. * * The "STD" protocols use the standard OpenFlow 1.0 flow format. * The "NXM" protocols use the Nicira Extensible Match (NXM) flow format. * * The protocols with "TID" mean that the nx_flow_mod_table_id Nicira * extension has been enabled. The other protocols have it disabled. */ #define OFPUTIL_P_NONE 0 OFPUTIL_P_OF10_STD = 1 << 0, OFPUTIL_P_OF10_STD_TID = 1 << 1, OFPUTIL_P_OF10_NXM = 1 << 2, OFPUTIL_P_OF10_NXM_TID = 1 << 3, #define OFPUTIL_P_OF10_STD_ANY (OFPUTIL_P_OF10_STD | OFPUTIL_P_OF10_STD_TID) #define OFPUTIL_P_OF10_NXM_ANY (OFPUTIL_P_OF10_NXM | OFPUTIL_P_OF10_NXM_TID) #define OFPUTIL_P_OF10_ANY (OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY) /* OpenFlow 1.1 protocol. * * We only support the standard OpenFlow 1.1 flow format. * * OpenFlow 1.1 always operates with an equivalent of the * nx_flow_mod_table_id Nicira extension enabled, so there is no "TID" * variant. */ OFPUTIL_P_OF11_STD = 1 << 4, /* OpenFlow 1.2+ protocols (only one variant each). * * These use the standard OpenFlow Extensible Match (OXM) flow format. * * OpenFlow 1.2+ always operates with an equivalent of the * nx_flow_mod_table_id Nicira extension enabled, so there is no "TID" * variant. */ OFPUTIL_P_OF12_OXM = 1 << 5, OFPUTIL_P_OF13_OXM = 1 << 6, OFPUTIL_P_OF14_OXM = 1 << 7, OFPUTIL_P_OF15_OXM = 1 << 8, #define OFPUTIL_P_ANY_OXM (OFPUTIL_P_OF12_OXM | \ OFPUTIL_P_OF13_OXM | \ OFPUTIL_P_OF14_OXM | \ OFPUTIL_P_OF15_OXM) #define OFPUTIL_P_NXM_OF11_UP (OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF11_STD | \ OFPUTIL_P_ANY_OXM) #define OFPUTIL_P_NXM_OXM_ANY (OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_ANY_OXM) #define OFPUTIL_P_OF11_UP (OFPUTIL_P_OF11_STD | OFPUTIL_P_ANY_OXM) #define OFPUTIL_P_OF12_UP (OFPUTIL_P_OF12_OXM | OFPUTIL_P_OF13_UP) #define OFPUTIL_P_OF13_UP (OFPUTIL_P_OF13_OXM | OFPUTIL_P_OF14_UP) #define OFPUTIL_P_OF14_UP (OFPUTIL_P_OF14_OXM | OFPUTIL_P_OF15_UP) #define OFPUTIL_P_OF15_UP OFPUTIL_P_OF15_OXM /* All protocols. */ #define OFPUTIL_P_ANY ((1 << 9) - 1) /* Protocols in which a specific table may be specified in flow_mods. */ #define OFPUTIL_P_TID (OFPUTIL_P_OF10_STD_TID | \ OFPUTIL_P_OF10_NXM_TID | \ OFPUTIL_P_OF11_STD | \ OFPUTIL_P_ANY_OXM) }; /* Valid value of mask for asynchronous messages. */ #define MAXIMUM_MASK_PACKET_IN ((1 << OFPR_N_REASONS) - 1) #define MAXIMUM_MASK_FLOW_REMOVED ((1 << OVS_OFPRR_NONE) - 1) #define MAXIMUM_MASK_PORT_STATUS ((1 << OFPPR_N_REASONS) - 1) #define MAXIMUM_MASK_ROLE_STATUS ((1 << OFPCRR_N_REASONS) - 1) #define MINIMUM_MASK_TABLE_STATUS (1 << OFPTR_VACANCY_DOWN) #define MAXIMUM_MASK_TABLE_STATUS ((1 << OFPTR_N_REASONS) - \ MINIMUM_MASK_TABLE_STATUS) #define MAXIMUM_MASK_REQUESTFORWARD ((1 << OFPRFR_N_REASONS) - 1) /* Protocols to use for flow dumps, from most to least preferred. */ extern enum ofputil_protocol ofputil_flow_dump_protocols[]; extern size_t ofputil_n_flow_dump_protocols; enum ofputil_protocol ofputil_protocol_from_ofp_version(enum ofp_version); enum ofputil_protocol ofputil_protocols_from_ofp_version(enum ofp_version); enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol); bool ofputil_protocol_is_valid(enum ofputil_protocol); enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol, bool enable); enum ofputil_protocol ofputil_protocol_to_base(enum ofputil_protocol); enum ofputil_protocol ofputil_protocol_set_base( enum ofputil_protocol cur, enum ofputil_protocol new_base); const char *ofputil_protocol_to_string(enum ofputil_protocol); char *ofputil_protocols_to_string(enum ofputil_protocol); enum ofputil_protocol ofputil_protocols_from_string(const char *); void ofputil_format_version(struct ds *, enum ofp_version); void ofputil_format_version_name(struct ds *, enum ofp_version); /* A bitmap of version numbers * * Bit offsets correspond to ofp_version numbers which in turn correspond to * wire-protocol numbers for OpenFlow versions, e.g. (1u << OFP11_VERSION) * is the mask for OpenFlow 1.1. If the bit for a version is set then it is * allowed, otherwise it is disallowed. */ void ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap); void ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap); enum ofp_version ofputil_version_from_string(const char *s); uint32_t ofputil_protocols_to_version_bitmap(enum ofputil_protocol); enum ofputil_protocol ofputil_protocols_from_version_bitmap(uint32_t bitmap); /* Bitmaps of OpenFlow versions that Open vSwitch supports, and that it enables * by default. When Open vSwitch has experimental or incomplete support for * newer versions of OpenFlow, those versions should not be supported by * default and thus should be omitted from the latter bitmap. */ #define OFPUTIL_SUPPORTED_VERSIONS ((1u << OFP10_VERSION) | \ (1u << OFP11_VERSION) | \ (1u << OFP12_VERSION) | \ (1u << OFP13_VERSION)) #define OFPUTIL_DEFAULT_VERSIONS OFPUTIL_SUPPORTED_VERSIONS enum ofputil_protocol ofputil_protocols_from_string(const char *s); const char *ofputil_version_to_string(enum ofp_version ofp_version); uint32_t ofputil_versions_from_string(const char *s); uint32_t ofputil_versions_from_strings(char ** const s, size_t count); bool ofputil_decode_hello(const struct ofp_header *, uint32_t *allowed_versions); struct ofpbuf *ofputil_encode_hello(uint32_t version_bitmap); struct ofpbuf *ofputil_encode_set_protocol(enum ofputil_protocol current, enum ofputil_protocol want, enum ofputil_protocol *next); /* nx_flow_format */ struct ofpbuf *ofputil_encode_nx_set_flow_format(enum nx_flow_format); enum ofputil_protocol ofputil_nx_flow_format_to_protocol(enum nx_flow_format); bool ofputil_nx_flow_format_is_valid(enum nx_flow_format); const char *ofputil_nx_flow_format_to_string(enum nx_flow_format); /* Work with ofp10_match. */ void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *); void ofputil_match_from_ofp10_match(const struct ofp10_match *, struct match *); void ofputil_normalize_match(struct match *); void ofputil_normalize_match_quiet(struct match *); void ofputil_match_to_ofp10_match(const struct match *, struct ofp10_match *); /* Work with ofp11_match. */ enum ofperr ofputil_pull_ofp11_match(struct ofpbuf *, struct match *, uint16_t *padded_match_len); enum ofperr ofputil_pull_ofp11_mask(struct ofpbuf *, struct match *, struct mf_bitmap *bm); enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *, struct match *); int ofputil_put_ofp11_match(struct ofpbuf *, const struct match *, enum ofputil_protocol); void ofputil_match_to_ofp11_match(const struct match *, struct ofp11_match *); int ofputil_match_typical_len(enum ofputil_protocol); /* dl_type translation between OpenFlow and 'struct flow' format. */ ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type); ovs_be16 ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type); /* PACKET_IN. */ bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format); int ofputil_packet_in_format_from_string(const char *); const char *ofputil_packet_in_format_to_string(enum nx_packet_in_format); struct ofpbuf *ofputil_make_set_packet_in_format(enum ofp_version, enum nx_packet_in_format); /* NXT_FLOW_MOD_TABLE_ID extension. */ struct ofpbuf *ofputil_make_flow_mod_table_id(bool flow_mod_table_id); /* Protocol-independent flow_mod flags. */ enum ofputil_flow_mod_flags { /* Flags that are maintained with a flow as part of its state. * * (OFPUTIL_FF_EMERG would be here too, if OVS supported it.) */ OFPUTIL_FF_SEND_FLOW_REM = 1 << 0, /* All versions. */ OFPUTIL_FF_NO_PKT_COUNTS = 1 << 1, /* OpenFlow 1.3+. */ OFPUTIL_FF_NO_BYT_COUNTS = 1 << 2, /* OpenFlow 1.3+. */ /* These flags primarily affects flow_mod behavior. They are not * particularly useful as part of flow state. We include them in flow * state only because OpenFlow implies that they should be. */ OFPUTIL_FF_CHECK_OVERLAP = 1 << 3, /* All versions. */ OFPUTIL_FF_RESET_COUNTS = 1 << 4, /* OpenFlow 1.2+. */ /* Not supported by OVS. */ OFPUTIL_FF_EMERG = 1 << 5, /* OpenFlow 1.0 only. */ /* The set of flags maintained as part of a flow table entry. */ #define OFPUTIL_FF_STATE (OFPUTIL_FF_SEND_FLOW_REM \ | OFPUTIL_FF_NO_PKT_COUNTS \ | OFPUTIL_FF_NO_BYT_COUNTS \ | OFPUTIL_FF_CHECK_OVERLAP \ | OFPUTIL_FF_RESET_COUNTS) /* Flags that are only set by OVS for its internal use. Cannot be set via * OpenFlow. */ OFPUTIL_FF_HIDDEN_FIELDS = 1 << 6, /* Allow hidden match fields to be set or modified. */ OFPUTIL_FF_NO_READONLY = 1 << 7, /* Allow rules within read only tables to be modified */ }; /* Protocol-independent flow_mod. * * The handling of cookies across multiple versions of OpenFlow is a bit * confusing. See DESIGN for the details. */ struct ofputil_flow_mod { struct ovs_list list_node; /* For queuing flow_mods. */ struct match match; int priority; /* Cookie matching. The flow_mod affects only flows that have cookies that * bitwise match 'cookie' bits in positions where 'cookie_mask has 1-bits. * * 'cookie_mask' should be zero for OFPFC_ADD flow_mods. */ ovs_be64 cookie; /* Cookie bits to match. */ ovs_be64 cookie_mask; /* 1-bit in each 'cookie' bit to match. */ /* Cookie changes. * * OFPFC_ADD uses 'new_cookie' as the new flow's cookie. 'new_cookie' * should not be UINT64_MAX. * * OFPFC_MODIFY and OFPFC_MODIFY_STRICT have two cases: * * - If one or more matching flows exist and 'modify_cookie' is true, * then the flow_mod changes the existing flows' cookies to * 'new_cookie'. 'new_cookie' should not be UINT64_MAX. * * - If no matching flow exists, 'new_cookie' is not UINT64_MAX, and * 'cookie_mask' is 0, then the flow_mod adds a new flow with * 'new_cookie' as its cookie. */ ovs_be64 new_cookie; /* New cookie to install or UINT64_MAX. */ bool modify_cookie; /* Set cookie of existing flow to 'new_cookie'? */ uint8_t table_id; uint16_t command; uint16_t idle_timeout; uint16_t hard_timeout; uint32_t buffer_id; ofp_port_t out_port; uint32_t out_group; enum ofputil_flow_mod_flags flags; uint16_t importance; /* Eviction precedence. */ struct ofpact *ofpacts; /* Series of "struct ofpact"s. */ size_t ofpacts_len; /* Length of ofpacts, in bytes. */ /* Reason for delete; ignored for non-delete commands */ enum ofp_flow_removed_reason delete_reason; }; enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *, const struct ofp_header *, enum ofputil_protocol, struct ofpbuf *ofpacts, ofp_port_t max_port, uint8_t max_table); struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *, enum ofputil_protocol); /* Flow stats or aggregate stats request, independent of protocol. */ struct ofputil_flow_stats_request { bool aggregate; /* Aggregate results? */ struct match match; ovs_be64 cookie; ovs_be64 cookie_mask; ofp_port_t out_port; uint32_t out_group; uint8_t table_id; }; enum ofperr ofputil_decode_flow_stats_request( struct ofputil_flow_stats_request *, const struct ofp_header *); struct ofpbuf *ofputil_encode_flow_stats_request( const struct ofputil_flow_stats_request *, enum ofputil_protocol); /* Flow stats reply, independent of protocol. */ struct ofputil_flow_stats { struct match match; ovs_be64 cookie; uint8_t table_id; uint16_t priority; uint16_t idle_timeout; uint16_t hard_timeout; uint32_t duration_sec; uint32_t duration_nsec; int idle_age; /* Seconds since last packet, -1 if unknown. */ int hard_age; /* Seconds since last change, -1 if unknown. */ uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */ uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */ const struct ofpact *ofpacts; size_t ofpacts_len; enum ofputil_flow_mod_flags flags; uint16_t importance; /* Eviction precedence. */ }; int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *, struct ofpbuf *msg, bool flow_age_extension, struct ofpbuf *ofpacts); void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *, struct ovs_list *replies); /* Aggregate stats reply, independent of protocol. */ struct ofputil_aggregate_stats { uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */ uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */ uint32_t flow_count; }; struct ofpbuf *ofputil_encode_aggregate_stats_reply( const struct ofputil_aggregate_stats *stats, const struct ofp_header *request); enum ofperr ofputil_decode_aggregate_stats_reply( struct ofputil_aggregate_stats *, const struct ofp_header *reply); /* Flow removed message, independent of protocol. */ struct ofputil_flow_removed { struct match match; ovs_be64 cookie; uint16_t priority; uint8_t reason; /* One of OFPRR_*. */ uint8_t table_id; /* 255 if message didn't include table ID. */ uint32_t duration_sec; uint32_t duration_nsec; uint16_t idle_timeout; uint16_t hard_timeout; uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */ uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */ }; enum ofperr ofputil_decode_flow_removed(struct ofputil_flow_removed *, const struct ofp_header *); struct ofpbuf *ofputil_encode_flow_removed(const struct ofputil_flow_removed *, enum ofputil_protocol); /* Abstract packet-in message. */ struct ofputil_packet_in { /* Packet data and metadata. * * To save bandwidth, in some cases a switch may send only the first * several bytes of a packet, indicated by 'packet_len < total_len'. When * the full packet is included, 'packet_len == total_len'. */ const void *packet; size_t packet_len; /* Number of bytes in 'packet'. */ size_t total_len; /* Size of packet, pre-truncation. */ struct match flow_metadata; /* Identifies a buffer in the switch that contains the full packet, to * allow the controller to reference it later without having to send the * entire packet back to the switch. * * UINT32_MAX indicates that the packet is not buffered in the switch. A * switch should only use UINT32_MAX when it sends the entire packet. */ uint32_t buffer_id; /* Reason that the packet-in is being sent. */ enum ofp_packet_in_reason reason; /* One of OFPR_*. */ /* Information about the OpenFlow flow that triggered the packet-in. * * A packet-in triggered by a flow table miss has no associated flow. In * that case, 'cookie' is UINT64_MAX. */ uint8_t table_id; /* OpenFlow table ID. */ ovs_be64 cookie; /* Flow's cookie. */ }; enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *, const struct ofp_header *); struct ofpbuf *ofputil_encode_packet_in(const struct ofputil_packet_in *, enum ofputil_protocol protocol, enum nx_packet_in_format); enum { OFPUTIL_PACKET_IN_REASON_BUFSIZE = INT_STRLEN(int) + 1 }; const char *ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason, char *reasonbuf, size_t bufsize); bool ofputil_packet_in_reason_from_string(const char *, enum ofp_packet_in_reason *); /* Abstract packet-out message. * * ofputil_decode_packet_out() will ensure that 'in_port' is a physical port * (OFPP_MAX or less) or one of OFPP_LOCAL, OFPP_NONE, or OFPP_CONTROLLER. */ struct ofputil_packet_out { const void *packet; /* Packet data, if buffer_id == UINT32_MAX. */ size_t packet_len; /* Length of packet data in bytes. */ uint32_t buffer_id; /* Buffer id or UINT32_MAX if no buffer. */ ofp_port_t in_port; /* Packet's input port. */ struct ofpact *ofpacts; /* Actions. */ size_t ofpacts_len; /* Size of ofpacts in bytes. */ }; enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *, const struct ofp_header *, struct ofpbuf *ofpacts); struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *, enum ofputil_protocol protocol); enum ofputil_port_config { /* OpenFlow 1.0 and 1.1 share these values for these port config bits. */ OFPUTIL_PC_PORT_DOWN = 1 << 0, /* Port is administratively down. */ OFPUTIL_PC_NO_RECV = 1 << 2, /* Drop all packets received by port. */ OFPUTIL_PC_NO_FWD = 1 << 5, /* Drop packets forwarded to port. */ OFPUTIL_PC_NO_PACKET_IN = 1 << 6, /* No send packet-in msgs for port. */ /* OpenFlow 1.0 only. */ OFPUTIL_PC_NO_STP = 1 << 1, /* No 802.1D spanning tree for port. */ OFPUTIL_PC_NO_RECV_STP = 1 << 3, /* Drop received 802.1D STP packets. */ OFPUTIL_PC_NO_FLOOD = 1 << 4, /* Do not include port when flooding. */ /* There are no OpenFlow 1.1-only bits. */ }; enum ofputil_port_state { /* OpenFlow 1.0 and 1.1 share this values for these port state bits. */ OFPUTIL_PS_LINK_DOWN = 1 << 0, /* No physical link present. */ /* OpenFlow 1.1 only. */ OFPUTIL_PS_BLOCKED = 1 << 1, /* Port is blocked */ OFPUTIL_PS_LIVE = 1 << 2, /* Live for Fast Failover Group. */ /* OpenFlow 1.0 only. */ OFPUTIL_PS_STP_LISTEN = 0 << 8, /* Not learning or relaying frames. */ OFPUTIL_PS_STP_LEARN = 1 << 8, /* Learning but not relaying frames. */ OFPUTIL_PS_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */ OFPUTIL_PS_STP_BLOCK = 3 << 8, /* Not part of spanning tree. */ OFPUTIL_PS_STP_MASK = 3 << 8 /* Bit mask for OFPPS10_STP_* values. */ }; /* Abstract ofp10_phy_port or ofp11_port. */ struct ofputil_phy_port { ofp_port_t port_no; struct eth_addr hw_addr; char name[OFP_MAX_PORT_NAME_LEN]; enum ofputil_port_config config; enum ofputil_port_state state; /* NETDEV_F_* feature bitmasks. */ enum netdev_features curr; /* Current features. */ enum netdev_features advertised; /* Features advertised by the port. */ enum netdev_features supported; /* Features supported by the port. */ enum netdev_features peer; /* Features advertised by peer. */ /* Speed. */ uint32_t curr_speed; /* Current speed, in kbps. */ uint32_t max_speed; /* Maximum supported speed, in kbps. */ }; enum ofputil_capabilities { /* All OpenFlow versions share these capability values. */ OFPUTIL_C_FLOW_STATS = 1 << 0, /* Flow statistics. */ OFPUTIL_C_TABLE_STATS = 1 << 1, /* Table statistics. */ OFPUTIL_C_PORT_STATS = 1 << 2, /* Port statistics. */ OFPUTIL_C_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */ OFPUTIL_C_QUEUE_STATS = 1 << 6, /* Queue statistics. */ /* OpenFlow 1.0 and 1.1 share this capability. */ OFPUTIL_C_ARP_MATCH_IP = 1 << 7, /* Match IP addresses in ARP pkts. */ /* OpenFlow 1.0 only. */ OFPUTIL_C_STP = 1 << 3, /* 802.1d spanning tree. */ /* OpenFlow 1.1+ only. Note that this bit value does not match the one * in the OpenFlow message. */ OFPUTIL_C_GROUP_STATS = 1 << 4, /* Group statistics. */ /* OpenFlow 1.2+ only. */ OFPUTIL_C_PORT_BLOCKED = 1 << 8, /* Switch will block looping ports */ /* OpenFlow 1.4+ only. */ OFPUTIL_C_BUNDLES = 1 << 9, /* Switch supports bundles. */ OFPUTIL_C_FLOW_MONITORING = 1 << 10, /* Switch supports flow monitoring. */ }; /* Abstract ofp_switch_features. */ struct ofputil_switch_features { uint64_t datapath_id; /* Datapath unique ID. */ uint32_t n_buffers; /* Max packets buffered at once. */ uint8_t n_tables; /* Number of tables supported by datapath. */ uint8_t auxiliary_id; /* Identify auxiliary connections */ enum ofputil_capabilities capabilities; uint64_t ofpacts; /* Bitmap of OFPACT_* bits. */ }; enum ofperr ofputil_decode_switch_features(const struct ofp_header *, struct ofputil_switch_features *, struct ofpbuf *); struct ofpbuf *ofputil_encode_switch_features( const struct ofputil_switch_features *, enum ofputil_protocol, ovs_be32 xid); void ofputil_put_switch_features_port(const struct ofputil_phy_port *, struct ofpbuf *); bool ofputil_switch_features_has_ports(struct ofpbuf *b); /* phy_port helper functions. */ int ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *, struct ofputil_phy_port *); /* Abstract ofp_port_status. */ struct ofputil_port_status { enum ofp_port_reason reason; struct ofputil_phy_port desc; }; enum ofperr ofputil_decode_port_status(const struct ofp_header *, struct ofputil_port_status *); struct ofpbuf *ofputil_encode_port_status(const struct ofputil_port_status *, enum ofputil_protocol); /* Abstract ofp_port_mod. */ struct ofputil_port_mod { ofp_port_t port_no; struct eth_addr hw_addr; enum ofputil_port_config config; enum ofputil_port_config mask; enum netdev_features advertise; }; enum ofperr ofputil_decode_port_mod(const struct ofp_header *, struct ofputil_port_mod *, bool loose); struct ofpbuf *ofputil_encode_port_mod(const struct ofputil_port_mod *, enum ofputil_protocol); /* Abstract version of OFPTC11_TABLE_MISS_*. * * OpenFlow 1.0 always sends packets that miss to the next flow table, or to * the controller if they miss in the last flow table. * * OpenFlow 1.1 and 1.2 can configure table miss behavior via a "table-mod" * that specifies "send to controller", "miss", or "drop". * * OpenFlow 1.3 and later never sends packets that miss to the controller. */ enum ofputil_table_miss { /* Protocol-specific default behavior. On OpenFlow 1.0 through 1.2 * connections, the packet is sent to the controller, and on OpenFlow 1.3 * and later connections, the packet is dropped. * * This is also used as a result of decoding OpenFlow 1.3+ "config" values * in table-mods, to indicate that no table-miss was specified. */ OFPUTIL_TABLE_MISS_DEFAULT, /* Protocol default behavior. */ /* These constants have the same meanings as those in OpenFlow with the * same names. */ OFPUTIL_TABLE_MISS_CONTROLLER, /* Send to controller. */ OFPUTIL_TABLE_MISS_CONTINUE, /* Go to next table. */ OFPUTIL_TABLE_MISS_DROP, /* Drop the packet. */ }; /* Abstract version of OFPTC14_EVICTION. * * OpenFlow 1.0 through 1.3 don't know anything about eviction, so decoding a * message for one of these protocols always yields * OFPUTIL_TABLE_EVICTION_DEFAULT. */ enum ofputil_table_eviction { OFPUTIL_TABLE_EVICTION_DEFAULT, /* No value. */ OFPUTIL_TABLE_EVICTION_ON, /* Enable eviction. */ OFPUTIL_TABLE_EVICTION_OFF /* Disable eviction. */ }; /* Abstract version of OFPTC14_VACANCY_EVENTS. * * OpenFlow 1.0 through 1.3 don't know anything about vacancy events, so * decoding a message for one of these protocols always yields * OFPUTIL_TABLE_VACANCY_DEFAULT. */ enum ofputil_table_vacancy { OFPUTIL_TABLE_VACANCY_DEFAULT, /* No value. */ OFPUTIL_TABLE_VACANCY_ON, /* Enable vacancy events. */ OFPUTIL_TABLE_VACANCY_OFF /* Disable vacancy events. */ }; /* Abstract version of OFPTMPT_VACANCY. * * Openflow 1.4+ defines vacancy events. * The fields vacancy_down and vacancy_up are the threshold for generating * vacancy events that should be configured on the flow table, expressed as * a percent. * The vacancy field is only used when this property in included in a * OFPMP_TABLE_DESC multipart reply or a OFPT_TABLE_STATUS message and * represent the current vacancy of the table, expressed as a percent. In * OFP_TABLE_MOD requests, this field must be set to 0 */ struct ofputil_table_mod_prop_vacancy { uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ uint8_t vacancy; /* Current vacancy (%). */ }; /* Abstract ofp_table_mod. */ struct ofputil_table_mod { uint8_t table_id; /* ID of the table, 0xff indicates all tables. */ /* OpenFlow 1.1 and 1.2 only. For other versions, ignored on encoding, * decoded to OFPUTIL_TABLE_MISS_DEFAULT. */ enum ofputil_table_miss miss; /* OpenFlow 1.4+ only. For other versions, ignored on encoding, decoded to * OFPUTIL_TABLE_EVICTION_DEFAULT. */ enum ofputil_table_eviction eviction; /* OpenFlow 1.4+ only and optional even there; UINT32_MAX indicates * absence. For other versions, ignored on encoding, decoded to * UINT32_MAX.*/ uint32_t eviction_flags; /* OFPTMPEF14_*. */ /* OpenFlow 1.4+ only. For other versions, ignored on encoding, decoded to * OFPUTIL_TABLE_VACANCY_DEFAULT. */ enum ofputil_table_vacancy vacancy; /* Openflow 1.4+ only. Defines threshold values of vacancy expressed as * percent, value of current vacancy is set to zero for table-mod. * For other versions, ignored on encoding, all values decoded to * zero. */ struct ofputil_table_mod_prop_vacancy table_vacancy; }; /* Abstract ofp14_table_desc. */ struct ofputil_table_desc { uint8_t table_id; /* ID of the table. */ enum ofputil_table_eviction eviction; uint32_t eviction_flags; /* UINT32_MAX if not present. */ enum ofputil_table_vacancy vacancy; struct ofputil_table_mod_prop_vacancy table_vacancy; }; enum ofperr ofputil_decode_table_mod(const struct ofp_header *, struct ofputil_table_mod *); struct ofpbuf *ofputil_encode_table_mod(const struct ofputil_table_mod *, enum ofputil_protocol); /* Abstract ofp_table_features. * * This is used for all versions of OpenFlow, even though ofp_table_features * was only introduced in OpenFlow 1.3, because earlier versions of OpenFlow * include support for a subset of ofp_table_features through OFPST_TABLE (aka * OFPMP_TABLE). */ struct ofputil_table_features { uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */ char name[OFP_MAX_TABLE_NAME_LEN]; ovs_be64 metadata_match; /* Bits of metadata table can match. */ ovs_be64 metadata_write; /* Bits of metadata table can write. */ uint32_t max_entries; /* Max number of entries supported. */ /* Flags. * * 'miss_config' is relevant for OpenFlow 1.1 and 1.2 only, because those * versions include OFPTC_MISS_* flags in OFPST_TABLE. For other versions, * it is decoded to OFPUTIL_TABLE_MISS_DEFAULT and ignored for encoding. * * 'supports_eviction' and 'supports_vacancy_events' are relevant only for * OpenFlow 1.4 and later only. For OF1.4, they are boolean: 1 if * supported, otherwise 0. For other versions, they are decoded as -1 and * ignored for encoding. * * See the section "OFPTC_* Table Configuration" in DESIGN.md for more * details of how OpenFlow has changed in this area. */ enum ofputil_table_miss miss_config; /* OF1.1 and 1.2 only. */ int supports_eviction; /* OF1.4+ only. */ int supports_vacancy_events; /* OF1.4+ only. */ /* Table features related to instructions. There are two instances: * * - 'miss' reports features available in the table miss flow. * * - 'nonmiss' reports features available in other flows. */ struct ofputil_table_instruction_features { /* Tables that "goto-table" may jump to. */ unsigned long int next[BITMAP_N_LONGS(255)]; /* Bitmap of OVSINST_* for supported instructions. */ uint32_t instructions; /* Table features related to actions. There are two instances: * * - 'write' reports features available in a "write_actions" * instruction. * * - 'apply' reports features available in an "apply_actions" * instruction. */ struct ofputil_table_action_features { uint64_t ofpacts; /* Bitmap of supported OFPACT_*. */ struct mf_bitmap set_fields; /* Fields for "set-field". */ } write, apply; } nonmiss, miss; /* MFF_* bitmaps. * * For any given field the following combinations are valid: * * - match=0, wildcard=0, mask=0: Flows in this table cannot match on * this field. * * - match=1, wildcard=0, mask=0: Flows in this table must match on all * the bits in this field. * * - match=1, wildcard=1, mask=0: Flows in this table must either match * on all the bits in the field or wildcard the field entirely. * * - match=1, wildcard=1, mask=1: Flows in this table may arbitrarily * mask this field (as special cases, they may match on all the bits * or wildcard it entirely). * * Other combinations do not make sense. */ struct mf_bitmap match; /* Fields that may be matched. */ struct mf_bitmap mask; /* Subset of 'match' that may have masks. */ struct mf_bitmap wildcard; /* Subset of 'match' that may be wildcarded. */ }; int ofputil_decode_table_features(struct ofpbuf *, struct ofputil_table_features *, bool loose); int ofputil_decode_table_desc(struct ofpbuf *, struct ofputil_table_desc *, enum ofp_version); struct ofpbuf *ofputil_encode_table_features_request(enum ofp_version); struct ofpbuf *ofputil_encode_table_desc_request(enum ofp_version); void ofputil_append_table_features_reply( const struct ofputil_table_features *tf, struct ovs_list *replies); void ofputil_append_table_desc_reply(const struct ofputil_table_desc *td, struct ovs_list *replies, enum ofp_version); /* Meter band configuration for all supported band types. */ struct ofputil_meter_band { uint16_t type; uint8_t prec_level; /* Non-zero if type == OFPMBT_DSCP_REMARK. */ uint32_t rate; uint32_t burst_size; }; struct ofputil_meter_band_stats { uint64_t packet_count; uint64_t byte_count; }; struct ofputil_meter_config { uint32_t meter_id; uint16_t flags; uint16_t n_bands; struct ofputil_meter_band *bands; }; /* Abstract ofp_meter_mod. */ struct ofputil_meter_mod { uint16_t command; struct ofputil_meter_config meter; }; struct ofputil_meter_stats { uint32_t meter_id; uint32_t flow_count; uint64_t packet_in_count; uint64_t byte_in_count; uint32_t duration_sec; uint32_t duration_nsec; uint16_t n_bands; struct ofputil_meter_band_stats *bands; }; struct ofputil_meter_features { uint32_t max_meters; /* Maximum number of meters. */ uint32_t band_types; /* Can support max 32 band types. */ uint32_t capabilities; /* Supported flags. */ uint8_t max_bands; uint8_t max_color; }; enum ofperr ofputil_decode_meter_mod(const struct ofp_header *, struct ofputil_meter_mod *, struct ofpbuf *bands); struct ofpbuf *ofputil_encode_meter_mod(enum ofp_version, const struct ofputil_meter_mod *); void ofputil_decode_meter_features(const struct ofp_header *, struct ofputil_meter_features *); struct ofpbuf *ofputil_encode_meter_features_reply(const struct ofputil_meter_features *, const struct ofp_header * request); void ofputil_decode_meter_request(const struct ofp_header *, uint32_t *meter_id); void ofputil_append_meter_config(struct ovs_list *replies, const struct ofputil_meter_config *); void ofputil_append_meter_stats(struct ovs_list *replies, const struct ofputil_meter_stats *); enum ofputil_meter_request_type { OFPUTIL_METER_FEATURES, OFPUTIL_METER_CONFIG, OFPUTIL_METER_STATS }; struct ofpbuf *ofputil_encode_meter_request(enum ofp_version, enum ofputil_meter_request_type, uint32_t meter_id); int ofputil_decode_meter_stats(struct ofpbuf *, struct ofputil_meter_stats *, struct ofpbuf *bands); int ofputil_decode_meter_config(struct ofpbuf *, struct ofputil_meter_config *, struct ofpbuf *bands); /* Type for meter_id in ofproto provider interface, UINT32_MAX if invalid. */ typedef struct { uint32_t uint32; } ofproto_meter_id; /* Abstract ofp_role_request and reply. */ struct ofputil_role_request { enum ofp12_controller_role role; bool have_generation_id; uint64_t generation_id; }; struct ofputil_role_status { enum ofp12_controller_role role; enum ofp14_controller_role_reason reason; uint64_t generation_id; }; enum ofperr ofputil_decode_role_message(const struct ofp_header *, struct ofputil_role_request *); struct ofpbuf *ofputil_encode_role_reply(const struct ofp_header *, const struct ofputil_role_request *); struct ofpbuf *ofputil_encode_role_status( const struct ofputil_role_status *status, enum ofputil_protocol protocol); enum ofperr ofputil_decode_role_status(const struct ofp_header *oh, struct ofputil_role_status *rs); /* Abstract table stats. * * This corresponds to the OpenFlow 1.3 table statistics structure, which only * includes actual statistics. In earlier versions of OpenFlow, several * members describe table features, so this structure has to be paired with * struct ofputil_table_features to get all information. */ struct ofputil_table_stats { uint8_t table_id; /* Identifier of table. */ uint32_t active_count; /* Number of active entries. */ uint64_t lookup_count; /* Number of packets looked up in table. */ uint64_t matched_count; /* Number of packets that hit table. */ }; struct ofpbuf *ofputil_encode_table_stats_reply(const struct ofp_header *rq); struct ofpbuf *ofputil_encode_table_desc_reply(const struct ofp_header *rq); void ofputil_append_table_stats_reply(struct ofpbuf *reply, const struct ofputil_table_stats *, const struct ofputil_table_features *); int ofputil_decode_table_stats_reply(struct ofpbuf *reply, struct ofputil_table_stats *, struct ofputil_table_features *); /* Queue configuration request. */ struct ofpbuf *ofputil_encode_queue_get_config_request(enum ofp_version, ofp_port_t port); enum ofperr ofputil_decode_queue_get_config_request(const struct ofp_header *, ofp_port_t *port); /* Queue configuration reply. */ struct ofputil_queue_config { uint32_t queue_id; /* Each of these optional values is expressed in tenths of a percent. * Values greater than 1000 indicate that the feature is disabled. * UINT16_MAX indicates that the value is omitted. */ uint16_t min_rate; uint16_t max_rate; }; struct ofpbuf *ofputil_encode_queue_get_config_reply( const struct ofp_header *request); void ofputil_append_queue_get_config_reply( struct ofpbuf *reply, const struct ofputil_queue_config *); enum ofperr ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *); int ofputil_pull_queue_get_config_reply(struct ofpbuf *reply, struct ofputil_queue_config *); /* Abstract nx_flow_monitor_request. */ struct ofputil_flow_monitor_request { uint32_t id; enum nx_flow_monitor_flags flags; ofp_port_t out_port; uint8_t table_id; struct match match; }; int ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *, struct ofpbuf *msg); void ofputil_append_flow_monitor_request( const struct ofputil_flow_monitor_request *, struct ofpbuf *msg); /* Abstract nx_flow_update. */ struct ofputil_flow_update { enum nx_flow_update_event event; /* Used only for NXFME_ADDED, NXFME_DELETED, NXFME_MODIFIED. */ enum ofp_flow_removed_reason reason; uint16_t idle_timeout; uint16_t hard_timeout; uint8_t table_id; uint16_t priority; ovs_be64 cookie; struct match *match; const struct ofpact *ofpacts; size_t ofpacts_len; /* Used only for NXFME_ABBREV. */ ovs_be32 xid; }; int ofputil_decode_flow_update(struct ofputil_flow_update *, struct ofpbuf *msg, struct ofpbuf *ofpacts); void ofputil_start_flow_update(struct ovs_list *replies); void ofputil_append_flow_update(const struct ofputil_flow_update *, struct ovs_list *replies); /* Abstract nx_flow_monitor_cancel. */ uint32_t ofputil_decode_flow_monitor_cancel(const struct ofp_header *); struct ofpbuf *ofputil_encode_flow_monitor_cancel(uint32_t id); /* Port desc stats requests and replies. */ enum ofperr ofputil_decode_port_desc_stats_request(const struct ofp_header *, ofp_port_t *portp); struct ofpbuf *ofputil_encode_port_desc_stats_request( enum ofp_version ofp_version, ofp_port_t); void ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp, struct ovs_list *replies); /* Encoding simple OpenFlow messages. */ struct ofpbuf *make_echo_request(enum ofp_version); struct ofpbuf *make_echo_reply(const struct ofp_header *rq); struct ofpbuf *ofputil_encode_barrier_request(enum ofp_version); const char *ofputil_frag_handling_to_string(enum ofp_config_flags); bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *); /* Actions. */ bool action_outputs_to_port(const union ofp_action *, ovs_be16 port); enum ofperr ofputil_pull_actions(struct ofpbuf *, unsigned int actions_len, union ofp_action **, size_t *); bool ofputil_actions_equal(const union ofp_action *a, size_t n_a, const union ofp_action *b, size_t n_b); union ofp_action *ofputil_actions_clone(const union ofp_action *, size_t n); /* Handy utility for parsing flows and actions. */ bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep); struct ofputil_port_stats { ofp_port_t port_no; struct netdev_stats stats; uint32_t duration_sec; /* UINT32_MAX if unknown. */ uint32_t duration_nsec; }; struct ofpbuf *ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port); void ofputil_append_port_stat(struct ovs_list *replies, const struct ofputil_port_stats *ops); size_t ofputil_count_port_stats(const struct ofp_header *); int ofputil_decode_port_stats(struct ofputil_port_stats *, struct ofpbuf *msg); enum ofperr ofputil_decode_port_stats_request(const struct ofp_header *request, ofp_port_t *ofp10_port); struct ofputil_queue_stats_request { ofp_port_t port_no; /* OFPP_ANY means "all ports". */ uint32_t queue_id; }; enum ofperr ofputil_decode_queue_stats_request(const struct ofp_header *request, struct ofputil_queue_stats_request *oqsr); struct ofpbuf * ofputil_encode_queue_stats_request(enum ofp_version ofp_version, const struct ofputil_queue_stats_request *oqsr); struct ofputil_queue_stats { ofp_port_t port_no; uint32_t queue_id; /* Values of unsupported statistics are set to all-1-bits (UINT64_MAX). */ uint64_t tx_bytes; uint64_t tx_packets; uint64_t tx_errors; /* UINT32_MAX if unknown. */ uint32_t duration_sec; uint32_t duration_nsec; }; size_t ofputil_count_queue_stats(const struct ofp_header *); int ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg); void ofputil_append_queue_stat(struct ovs_list *replies, const struct ofputil_queue_stats *oqs); struct bucket_counter { uint64_t packet_count; /* Number of packets processed by bucket. */ uint64_t byte_count; /* Number of bytes processed by bucket. */ }; /* Bucket for use in groups. */ struct ofputil_bucket { struct ovs_list list_node; uint16_t weight; /* Relative weight, for "select" groups. */ ofp_port_t watch_port; /* Port whose state affects whether this bucket * is live. Only required for fast failover * groups. */ uint32_t watch_group; /* Group whose state affects whether this * bucket is live. Only required for fast * failover groups. */ uint32_t bucket_id; /* Bucket Id used to identify bucket*/ struct ofpact *ofpacts; /* Series of "struct ofpact"s. */ size_t ofpacts_len; /* Length of ofpacts, in bytes. */ struct bucket_counter stats; }; /* Protocol-independent group_mod. */ struct ofputil_group_props { /* NTR selection method */ char selection_method[NTR_MAX_SELECTION_METHOD_LEN]; uint64_t selection_method_param; struct field_array fields; }; /* Protocol-independent group_mod. */ struct ofputil_group_mod { uint16_t command; /* One of OFPGC15_*. */ uint8_t type; /* One of OFPGT11_*. */ uint32_t group_id; /* Group identifier. */ uint32_t command_bucket_id; /* Bucket Id used as part of * OFPGC15_INSERT_BUCKET and * OFPGC15_REMOVE_BUCKET commands * execution.*/ struct ovs_list buckets; /* Contains "struct ofputil_bucket"s. */ struct ofputil_group_props props; /* Group properties. */ }; /* Group stats reply, independent of protocol. */ struct ofputil_group_stats { uint32_t group_id; /* Group identifier. */ uint32_t ref_count; uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */ uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */ uint32_t duration_sec; /* UINT32_MAX if unknown. */ uint32_t duration_nsec; uint32_t n_buckets; struct bucket_counter *bucket_stats; }; /* Group features reply, independent of protocol. * * Only OF1.2 and later support group features replies. */ struct ofputil_group_features { uint32_t types; /* Bitmap of OFPGT_* values supported. */ uint32_t capabilities; /* Bitmap of OFPGFC12_* capability supported. */ uint32_t max_groups[4]; /* Maximum number of groups for each type. */ uint64_t ofpacts[4]; /* Bitmaps of supported OFPACT_* */ }; /* Group desc reply, independent of protocol. */ struct ofputil_group_desc { uint8_t type; /* One of OFPGT_*. */ uint32_t group_id; /* Group identifier. */ struct ovs_list buckets; /* Contains "struct ofputil_bucket"s. */ struct ofputil_group_props props; /* Group properties. */ }; void ofputil_bucket_list_destroy(struct ovs_list *buckets); void ofputil_bucket_clone_list(struct ovs_list *dest, const struct ovs_list *src, const struct ofputil_bucket *); struct ofputil_bucket *ofputil_bucket_find(const struct ovs_list *, uint32_t bucket_id); bool ofputil_bucket_check_duplicate_id(const struct ovs_list *); struct ofputil_bucket *ofputil_bucket_list_front(const struct ovs_list *); struct ofputil_bucket *ofputil_bucket_list_back(const struct ovs_list *); static inline bool ofputil_bucket_has_liveness(const struct ofputil_bucket *bucket) { return (bucket->watch_port != OFPP_ANY || bucket->watch_group != OFPG_ANY); } struct ofpbuf *ofputil_encode_group_stats_request(enum ofp_version, uint32_t group_id); enum ofperr ofputil_decode_group_stats_request( const struct ofp_header *request, uint32_t *group_id); void ofputil_append_group_stats(struct ovs_list *replies, const struct ofputil_group_stats *); struct ofpbuf *ofputil_encode_group_features_request(enum ofp_version); struct ofpbuf *ofputil_encode_group_features_reply( const struct ofputil_group_features *, const struct ofp_header *request); void ofputil_decode_group_features_reply(const struct ofp_header *, struct ofputil_group_features *); void ofputil_uninit_group_mod(struct ofputil_group_mod *gm); struct ofpbuf *ofputil_encode_group_mod(enum ofp_version ofp_version, const struct ofputil_group_mod *gm); enum ofperr ofputil_decode_group_mod(const struct ofp_header *, struct ofputil_group_mod *); int ofputil_decode_group_stats_reply(struct ofpbuf *, struct ofputil_group_stats *); void ofputil_uninit_group_desc(struct ofputil_group_desc *gd); uint32_t ofputil_decode_group_desc_request(const struct ofp_header *); struct ofpbuf *ofputil_encode_group_desc_request(enum ofp_version, uint32_t group_id); int ofputil_decode_group_desc_reply(struct ofputil_group_desc *, struct ofpbuf *, enum ofp_version); void ofputil_append_group_desc_reply(const struct ofputil_group_desc *, const struct ovs_list *buckets, struct ovs_list *replies); struct ofputil_bundle_ctrl_msg { uint32_t bundle_id; uint16_t type; uint16_t flags; }; struct ofputil_bundle_add_msg { uint32_t bundle_id; uint16_t flags; const struct ofp_header *msg; }; enum ofptype; enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *, struct ofputil_bundle_ctrl_msg *); struct ofpbuf *ofputil_encode_bundle_ctrl_request(enum ofp_version, struct ofputil_bundle_ctrl_msg *); struct ofpbuf *ofputil_encode_bundle_ctrl_reply(const struct ofp_header *, struct ofputil_bundle_ctrl_msg *); struct ofpbuf *ofputil_encode_bundle_add(enum ofp_version ofp_version, struct ofputil_bundle_add_msg *msg); enum ofperr ofputil_decode_bundle_add(const struct ofp_header *, struct ofputil_bundle_add_msg *, enum ofptype *type); struct ofputil_tlv_map { struct ovs_list list_node; uint16_t option_class; uint8_t option_type; uint8_t option_len; uint16_t index; }; struct ofputil_tlv_table_mod { uint16_t command; struct ovs_list mappings; /* Contains "struct ofputil_tlv_map"s. */ }; struct ofputil_tlv_table_reply { uint32_t max_option_space; uint16_t max_fields; struct ovs_list mappings; /* Contains "struct ofputil_tlv_map"s. */ }; struct ofpbuf *ofputil_encode_tlv_table_mod(enum ofp_version ofp_version, struct ofputil_tlv_table_mod *); enum ofperr ofputil_decode_tlv_table_mod(const struct ofp_header *, struct ofputil_tlv_table_mod *); struct ofpbuf *ofputil_encode_tlv_table_reply(const struct ofp_header *, struct ofputil_tlv_table_reply *); enum ofperr ofputil_decode_tlv_table_reply(const struct ofp_header *, struct ofputil_tlv_table_reply *); void ofputil_uninit_tlv_table(struct ovs_list *mappings); enum ofputil_async_msg_type { OAM_PACKET_IN, /* OFPT_PACKET_IN or NXT_PACKET_IN. */ OAM_PORT_STATUS, /* OFPT_PORT_STATUS. */ OAM_FLOW_REMOVED, /* OFPT_FLOW_REMOVED or * NXT_FLOW_REMOVED. */ OAM_ROLE_STATUS, /* OFPT_ROLE_STATUS. */ OAM_TABLE_STATUS, /* OFPT_TABLE_STATUS. */ OAM_REQUESTFORWARD, /* OFPT_REQUESTFORWARD. */ OAM_N_TYPES }; enum ofperr ofputil_decode_set_async_config(const struct ofp_header *, uint32_t master[OAM_N_TYPES], uint32_t slave[OAM_N_TYPES], bool loose); struct ofpbuf *ofputil_encode_get_async_config(const struct ofp_header *, uint32_t master[OAM_N_TYPES], uint32_t slave[OAM_N_TYPES]); struct ofputil_requestforward { ovs_be32 xid; enum ofp14_requestforward_reason reason; union { /* reason == OFPRFR_METER_MOD. */ struct { struct ofputil_meter_mod *meter_mod; struct ofpbuf bands; }; /* reason == OFPRFR_GROUP_MOD. */ struct ofputil_group_mod *group_mod; }; }; struct ofpbuf *ofputil_encode_requestforward( const struct ofputil_requestforward *, enum ofputil_protocol); enum ofperr ofputil_decode_requestforward(const struct ofp_header *, struct ofputil_requestforward *); void ofputil_destroy_requestforward(struct ofputil_requestforward *); #endif /* ofp-util.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/perf-counter.h0000644000000000000000000000013213534540071017174 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.849853928 openvswitch-2.5.9/lib/perf-counter.h0000644000175000017500000000773213534540071020673 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PERF_COUNTER_H #define __PERF_COUNTER_H 1 /* Motivation * ========== * * It is sometimes desirable to gain performance insights of a program * by using hardware counters. Recent Linux kernels started to support * a set of portable API for configuring and access those counter across * multiple platforms. * * APIs provided by perf-counter.h provides a set of APIs that are * semi-integrated into OVS user spaces. The infrastructure that initializes, * cleanup, display and clear them at run time is provided. However the * sample points are not. A programmer needs insert sample points when needed. * * Since there is no pre configured sample points, there is no run time * over head for the released product. * * Limitations * =========== * - Hard coded to sample CPU cycle count in user space only. * - Only one counter is sampled. * - Useful macros are only provided for function profiling. * - show and clear command applies to all counters, there is no way * to select a sub-set of counter. * * Those are not fundamental limits, but only limited by current * implementation. * * Usage: * ======= * * Adding performance counter is easy. Simply use the following macro to * wrap around the expression you are interested in measuring. * * PERF(name, expr). * * The 'expr' is a set of C expressions you are interested in measuring. * 'name' is the counter name. * * For example, if we are interested in performance of perf_func(): * * int perf_func() { * * } * * void func() { * int rt; * * ... * PERF("perf_func", rt = perf_func()); * * return rt; * } * * * This will maintain the number of times 'perf_func()' is called, total * number of instructions '' plus function call overhead * executed. * */ #if defined(__linux__) && defined(HAVE_LINUX_PERF_EVENT_H) struct perf_counter { const char *name; bool once; uint64_t n_events; uint64_t total_count; }; #define PERF_COUNTER_ONCE_INITIALIZER(name) \ { \ name, \ false, \ 0, \ 0, \ } void perf_counters_init(void); void perf_counters_destroy(void); void perf_counters_clear(void); uint64_t perf_counter_read(uint64_t *counter); void perf_counter_accumulate(struct perf_counter *counter, uint64_t start_count); char *perf_counters_to_string(void); /* User access macros. */ #define PERF(name, expr) \ { \ static struct perf_counter c = PERF_COUNTER_ONCE_INITIALIZER(name);\ uint64_t start_count = perf_counter_read(&start_count); \ \ expr; \ \ perf_counter_accumulate(&c, start_count); \ } #else #define PERF(name, expr) { expr; } static inline void perf_counters_init(void) {} static inline void perf_counters_destroy(void) {} static inline void perf_counters_clear(void) {} static inline char * perf_counters_to_string(void) { return xstrdup("Not Supported on this platform. Only available on Linux (version >= 2.6.32)"); } #endif #endif openvswitch-2.5.9/lib/PaxHeaders.82075/command-line.h0000644000000000000000000000013213534540071017126 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.677852659 openvswitch-2.5.9/lib/command-line.h0000644000175000017500000000337613534540071020625 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef COMMAND_LINE_H #define COMMAND_LINE_H 1 /* Utilities for command-line parsing. */ #include "compiler.h" struct option; /* Command handler context */ struct ovs_cmdl_context { /* number of command line arguments */ int argc; /* array of command line arguments */ char **argv; /* private context data defined by the API user */ void *pvt; }; typedef void (*ovs_cmdl_handler)(struct ovs_cmdl_context *); struct ovs_cmdl_command { const char *name; const char *usage; int min_args; int max_args; ovs_cmdl_handler handler; }; char *ovs_cmdl_long_options_to_short_options(const struct option *options); void ovs_cmdl_print_options(const struct option *options); void ovs_cmdl_print_commands(const struct ovs_cmdl_command *commands); void ovs_cmdl_run_command(struct ovs_cmdl_context *, const struct ovs_cmdl_command[]); void ovs_cmdl_proctitle_init(int argc, char **argv); #if defined(__FreeBSD__) || defined(__NetBSD__) #define ovs_cmdl_proctitle_set setproctitle #else void ovs_cmdl_proctitle_set(const char *, ...) OVS_PRINTF_FORMAT(1, 2); #endif void ovs_cmdl_proctitle_restore(void); #endif /* command-line.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dh2048.pem0000644000000000000000000000013213534540071016026 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801423.793846143 openvswitch-2.5.9/lib/dh2048.pem0000644000175000017500000000111413534540071017511 0ustar00jpettitjpettit00000000000000-----BEGIN DH PARAMETERS----- MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV 89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50 T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg== -----END DH PARAMETERS----- These are the 2048 bit DH parameters from "Assigned Number for SKIP Protocols" (http://www.skip-vpn.org/spec/numbers.html). See there for how they were generated. openvswitch-2.5.9/lib/PaxHeaders.82075/if-notifier-bsd.c0000644000000000000000000000013213534540071017537 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801425.001855048 openvswitch-2.5.9/lib/if-notifier-bsd.c0000644000175000017500000000317513534540071021233 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "if-notifier.h" #include "rtbsd.h" #include "util.h" struct if_notifier { struct rtbsd_notifier notifier; if_notify_func *cb; void *aux; }; static void if_notifier_cb(const struct rtbsd_change *change OVS_UNUSED, void *aux) { struct if_notifier *notifier; notifier = aux; notifier->cb(notifier->aux); } struct if_notifier * if_notifier_create(if_notify_func *cb, void *aux) { struct if_notifier *notifier; int ret; notifier = xzalloc(sizeof *notifier); notifier->cb = cb; notifier->aux = aux; ret = rtbsd_notifier_register(¬ifier->notifier, if_notifier_cb, notifier); if (ret) { free(notifier); return NULL; } return notifier; } void if_notifier_destroy(struct if_notifier *notifier) { if (notifier) { rtbsd_notifier_unregister(¬ifier->notifier); free(notifier); } } void if_notifier_run(void) { rtbsd_notifier_run(); } void if_notifier_wait(void) { rtbsd_notifier_wait(); } openvswitch-2.5.9/lib/PaxHeaders.82075/shash.c0000644000000000000000000000013113534540071015663 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.873854104 openvswitch-2.5.9/lib/shash.c0000644000175000017500000001706113534540071017357 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "shash.h" #include "hash.h" static struct shash_node *shash_find__(const struct shash *, const char *name, size_t name_len, size_t hash); static size_t hash_name(const char *name) { return hash_string(name, 0); } void shash_init(struct shash *sh) { hmap_init(&sh->map); } void shash_destroy(struct shash *sh) { if (sh) { shash_clear(sh); hmap_destroy(&sh->map); } } /* Like shash_destroy(), but also free() each node's 'data'. */ void shash_destroy_free_data(struct shash *sh) { if (sh) { shash_clear_free_data(sh); hmap_destroy(&sh->map); } } void shash_swap(struct shash *a, struct shash *b) { hmap_swap(&a->map, &b->map); } void shash_moved(struct shash *sh) { hmap_moved(&sh->map); } void shash_clear(struct shash *sh) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, sh) { hmap_remove(&sh->map, &node->node); free(node->name); free(node); } } /* Like shash_clear(), but also free() each node's 'data'. */ void shash_clear_free_data(struct shash *sh) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, sh) { hmap_remove(&sh->map, &node->node); free(node->data); free(node->name); free(node); } } bool shash_is_empty(const struct shash *shash) { return hmap_is_empty(&shash->map); } size_t shash_count(const struct shash *shash) { return hmap_count(&shash->map); } static struct shash_node * shash_add_nocopy__(struct shash *sh, char *name, const void *data, size_t hash) { struct shash_node *node = xmalloc(sizeof *node); node->name = name; node->data = CONST_CAST(void *, data); hmap_insert(&sh->map, &node->node, hash); return node; } /* It is the caller's responsibility to avoid duplicate names, if that is * desirable. */ struct shash_node * shash_add_nocopy(struct shash *sh, char *name, const void *data) { return shash_add_nocopy__(sh, name, data, hash_name(name)); } /* It is the caller's responsibility to avoid duplicate names, if that is * desirable. */ struct shash_node * shash_add(struct shash *sh, const char *name, const void *data) { return shash_add_nocopy(sh, xstrdup(name), data); } bool shash_add_once(struct shash *sh, const char *name, const void *data) { if (!shash_find(sh, name)) { shash_add(sh, name, data); return true; } else { return false; } } void shash_add_assert(struct shash *sh, const char *name, const void *data) { bool added OVS_UNUSED = shash_add_once(sh, name, data); ovs_assert(added); } /* Searches for 'name' in 'sh'. If it does not already exist, adds it along * with 'data' and returns NULL. If it does already exist, replaces its data * by 'data' and returns the data that it formerly contained. */ void * shash_replace(struct shash *sh, const char *name, const void *data) { size_t hash = hash_name(name); struct shash_node *node; node = shash_find__(sh, name, strlen(name), hash); if (!node) { shash_add_nocopy__(sh, xstrdup(name), data, hash); return NULL; } else { void *old_data = node->data; node->data = CONST_CAST(void *, data); return old_data; } } /* Deletes 'node' from 'sh' and frees the node's name. The caller is still * responsible for freeing the node's data, if necessary. */ void shash_delete(struct shash *sh, struct shash_node *node) { free(shash_steal(sh, node)); } /* Deletes 'node' from 'sh'. Neither the node's name nor its data is freed; * instead, ownership is transferred to the caller. Returns the node's * name. */ char * shash_steal(struct shash *sh, struct shash_node *node) { char *name = node->name; hmap_remove(&sh->map, &node->node); free(node); return name; } static struct shash_node * shash_find__(const struct shash *sh, const char *name, size_t name_len, size_t hash) { struct shash_node *node; HMAP_FOR_EACH_WITH_HASH (node, node, hash, &sh->map) { if (!strncmp(node->name, name, name_len) && !node->name[name_len]) { return node; } } return NULL; } /* If there are duplicates, returns a random element. */ struct shash_node * shash_find(const struct shash *sh, const char *name) { return shash_find__(sh, name, strlen(name), hash_name(name)); } /* Finds and returns a shash_node within 'sh' that has the given 'name' that is * exactly 'len' bytes long. Returns NULL if no node in 'sh' has that name. */ struct shash_node * shash_find_len(const struct shash *sh, const char *name, size_t len) { return shash_find__(sh, name, len, hash_bytes(name, len, 0)); } void * shash_find_data(const struct shash *sh, const char *name) { struct shash_node *node = shash_find(sh, name); return node ? node->data : NULL; } void * shash_find_and_delete(struct shash *sh, const char *name) { struct shash_node *node = shash_find(sh, name); if (node) { void *data = node->data; shash_delete(sh, node); return data; } else { return NULL; } } void * shash_find_and_delete_assert(struct shash *sh, const char *name) { void *data = shash_find_and_delete(sh, name); ovs_assert(data != NULL); return data; } struct shash_node * shash_first(const struct shash *shash) { struct hmap_node *node = hmap_first(&shash->map); return node ? CONTAINER_OF(node, struct shash_node, node) : NULL; } static int compare_nodes_by_name(const void *a_, const void *b_) { const struct shash_node *const *a = a_; const struct shash_node *const *b = b_; return strcmp((*a)->name, (*b)->name); } const struct shash_node ** shash_sort(const struct shash *sh) { if (shash_is_empty(sh)) { return NULL; } else { const struct shash_node **nodes; struct shash_node *node; size_t i, n; n = shash_count(sh); nodes = xmalloc(n * sizeof *nodes); i = 0; SHASH_FOR_EACH (node, sh) { nodes[i++] = node; } ovs_assert(i == n); qsort(nodes, n, sizeof *nodes, compare_nodes_by_name); return nodes; } } /* Returns true if 'a' and 'b' contain the same keys (regardless of their * values), false otherwise. */ bool shash_equal_keys(const struct shash *a, const struct shash *b) { struct shash_node *node; if (hmap_count(&a->map) != hmap_count(&b->map)) { return false; } SHASH_FOR_EACH (node, a) { if (!shash_find(b, node->name)) { return false; } } return true; } /* Chooses and returns a randomly selected node from 'sh', which must not be * empty. * * I wouldn't depend on this algorithm to be fair, since I haven't analyzed it. * But it does at least ensure that any node in 'sh' can be chosen. */ struct shash_node * shash_random_node(struct shash *sh) { return CONTAINER_OF(hmap_random_node(&sh->map), struct shash_node, node); } openvswitch-2.5.9/lib/PaxHeaders.82075/sort.c0000644000000000000000000000013213534540071015545 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.885854193 openvswitch-2.5.9/lib/sort.c0000644000175000017500000000323713534540071017240 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "sort.h" #include "random.h" static size_t partition(size_t p, size_t r, int (*compare)(size_t a, size_t b, void *aux), void (*swap)(size_t a, size_t b, void *aux), void *aux) { size_t x = r - 1; size_t i, j; i = p; for (j = p; j < x; j++) { if (compare(j, x, aux) <= 0) { swap(i++, j, aux); } } swap(i, x, aux); return i; } static void quicksort(size_t p, size_t r, int (*compare)(size_t a, size_t b, void *aux), void (*swap)(size_t a, size_t b, void *aux), void *aux) { size_t i, q; if (r - p < 2) { return; } i = random_range(r - p) + p; if (r - 1 != i) { swap(r - 1, i, aux); } q = partition(p, r, compare, swap, aux); quicksort(p, q, compare, swap, aux); quicksort(q, r, compare, swap, aux); } void sort(size_t count, int (*compare)(size_t a, size_t b, void *aux), void (*swap)(size_t a, size_t b, void *aux), void *aux) { quicksort(0, count, compare, swap, aux); } openvswitch-2.5.9/lib/PaxHeaders.82075/pvector.c0000644000000000000000000000013213534540071016240 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.853853956 openvswitch-2.5.9/lib/pvector.c0000644000175000017500000001370213534540071017731 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "pvector.h" static struct pvector_impl * pvector_impl_get(const struct pvector *pvec) { return ovsrcu_get(struct pvector_impl *, &pvec->impl); } static struct pvector_impl * pvector_impl_alloc(size_t size) { struct pvector_impl *impl; impl = xmalloc(sizeof *impl + size * sizeof impl->vector[0]); impl->size = 0; impl->allocated = size; return impl; } static struct pvector_impl * pvector_impl_dup(struct pvector_impl *old) { struct pvector_impl *impl; size_t alloc = old->size + PVECTOR_EXTRA_ALLOC; impl = xmalloc(sizeof *impl + alloc * sizeof impl->vector[0]); impl->allocated = alloc; impl->size = old->size; memcpy(impl->vector, old->vector, old->size * sizeof old->vector[0]); return impl; } /* Initializes 'pvec' as an empty concurrent priority vector. */ void pvector_init(struct pvector *pvec) { ovsrcu_set(&pvec->impl, pvector_impl_alloc(PVECTOR_EXTRA_ALLOC)); pvec->temp = NULL; } /* Destroys 'pvec'. * * The client is responsible for destroying any data previously held in * 'pvec'. */ void pvector_destroy(struct pvector *pvec) { free(pvec->temp); pvec->temp = NULL; ovsrcu_postpone(free, pvector_impl_get(pvec)); ovsrcu_set(&pvec->impl, NULL); /* Poison. */ } /* Iterators for callers that need the 'index' afterward. */ #define PVECTOR_IMPL_FOR_EACH(ENTRY, INDEX, IMPL) \ for ((INDEX) = 0; \ (INDEX) < (IMPL)->size \ && ((ENTRY) = &(IMPL)->vector[INDEX], true); \ (INDEX)++) static int pvector_entry_cmp(const void *a_, const void *b_) { const struct pvector_entry *ap = a_; const struct pvector_entry *bp = b_; int a = ap->priority; int b = bp->priority; return a > b ? -1 : a < b; } static void pvector_impl_sort(struct pvector_impl *impl) { qsort(impl->vector, impl->size, sizeof *impl->vector, pvector_entry_cmp); /* Trim gaps. */ while (impl->size && impl->vector[impl->size - 1].priority == INT_MIN) { impl->size = impl->size - 1; } } /* Returns the index of the 'ptr' in the vector, or -1 if none is found. */ static int pvector_impl_find(struct pvector_impl *impl, void *target) { const struct pvector_entry *entry; int index; PVECTOR_IMPL_FOR_EACH (entry, index, impl) { if (entry->ptr == target) { return index; } } return -1; } void pvector_insert(struct pvector *pvec, void *ptr, int priority) { struct pvector_impl *temp = pvec->temp; struct pvector_impl *old = pvector_impl_get(pvec); ovs_assert(ptr != NULL); /* Check if can add to the end without reallocation. */ if (!temp && old->allocated > old->size && (!old->size || priority <= old->vector[old->size - 1].priority)) { old->vector[old->size].ptr = ptr; old->vector[old->size].priority = priority; /* Size increment must not be visible to the readers before the new * entry is stored. */ atomic_thread_fence(memory_order_release); ++old->size; } else { if (!temp) { temp = pvector_impl_dup(old); pvec->temp = temp; } else if (temp->size == temp->allocated) { temp = pvector_impl_dup(temp); free(pvec->temp); pvec->temp = temp; } /* Insert at the end, publish will sort. */ temp->vector[temp->size].ptr = ptr; temp->vector[temp->size].priority = priority; temp->size += 1; } } void pvector_remove(struct pvector *pvec, void *ptr) { struct pvector_impl *temp = pvec->temp; int index; if (!temp) { temp = pvector_impl_dup(pvector_impl_get(pvec)); pvec->temp = temp; } ovs_assert(temp->size > 0); index = pvector_impl_find(temp, ptr); ovs_assert(index >= 0); /* Now at the index of the entry to be deleted. * Clear in place, publish will sort and clean these off. */ temp->vector[index].ptr = NULL; temp->vector[index].priority = INT_MIN; } /* Change entry's 'priority' and keep the vector ordered. */ void pvector_change_priority(struct pvector *pvec, void *ptr, int priority) { struct pvector_impl *old = pvec->temp; int index; if (!old) { old = pvector_impl_get(pvec); } index = pvector_impl_find(old, ptr); ovs_assert(index >= 0); /* Now at the index of the entry to be updated. */ /* Check if can not update in place. */ if ((priority > old->vector[index].priority && index > 0 && priority > old->vector[index - 1].priority) || (priority < old->vector[index].priority && index < old->size - 1 && priority < old->vector[index + 1].priority)) { /* Have to use a temp. */ if (!pvec->temp) { /* Have to reallocate to reorder. */ pvec->temp = pvector_impl_dup(old); old = pvec->temp; /* Publish will sort. */ } } old->vector[index].priority = priority; } /* Make the modified pvector available for iteration. */ void pvector_publish__(struct pvector *pvec) { struct pvector_impl *temp = pvec->temp; pvec->temp = NULL; pvector_impl_sort(temp); /* Also removes gaps. */ ovsrcu_postpone(free, ovsrcu_get_protected(struct pvector_impl *, &pvec->impl)); ovsrcu_set(&pvec->impl, temp); } openvswitch-2.5.9/lib/PaxHeaders.82075/hindex.c0000644000000000000000000000013213534540071016035 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.733853073 openvswitch-2.5.9/lib/hindex.c0000644000175000017500000002251713534540071017532 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "hindex.h" #include "coverage.h" static bool hindex_node_is_body(const struct hindex_node *); static bool hindex_node_is_head(const struct hindex_node *); static void hindex_resize(struct hindex *, size_t new_mask); static size_t hindex_calc_mask(size_t capacity); COVERAGE_DEFINE(hindex_pathological); COVERAGE_DEFINE(hindex_expand); COVERAGE_DEFINE(hindex_shrink); COVERAGE_DEFINE(hindex_reserve); /* Initializes 'hindex' as an empty hash index. */ void hindex_init(struct hindex *hindex) { hindex->buckets = &hindex->one; hindex->one = NULL; hindex->mask = 0; hindex->n_unique = 0; } /* Frees memory reserved by 'hindex'. It is the client's responsibility to * free the nodes themselves, if necessary. */ void hindex_destroy(struct hindex *hindex) { if (hindex && hindex->buckets != &hindex->one) { free(hindex->buckets); } } /* Removes all node from 'hindex', leaving it ready to accept more nodes. Does * not free memory allocated for 'hindex'. * * This function is appropriate when 'hindex' will soon have about as many * elements as it before. If 'hindex' will likely have fewer elements than * before, use hindex_destroy() followed by hindex_clear() to save memory and * iteration time. */ void hindex_clear(struct hindex *hindex) { if (hindex->n_unique > 0) { hindex->n_unique = 0; memset(hindex->buckets, 0, (hindex->mask + 1) * sizeof *hindex->buckets); } } /* Exchanges hash indexes 'a' and 'b'. */ void hindex_swap(struct hindex *a, struct hindex *b) { struct hindex tmp = *a; *a = *b; *b = tmp; hindex_moved(a); hindex_moved(b); } /* Adjusts 'hindex' to compensate for having moved position in memory (e.g. due * to realloc()). */ void hindex_moved(struct hindex *hindex) { if (!hindex->mask) { hindex->buckets = &hindex->one; } } /* Expands 'hindex', if necessary, to optimize the performance of searches. */ void hindex_expand(struct hindex *hindex) { size_t new_mask = hindex_calc_mask(hindex->n_unique); if (new_mask > hindex->mask) { COVERAGE_INC(hindex_expand); hindex_resize(hindex, new_mask); } } /* Shrinks 'hindex', if necessary, to optimize the performance of iteration. */ void hindex_shrink(struct hindex *hindex) { size_t new_mask = hindex_calc_mask(hindex->n_unique); if (new_mask < hindex->mask) { COVERAGE_INC(hindex_shrink); hindex_resize(hindex, new_mask); } } /* Expands 'hindex', if necessary, to optimize the performance of searches when * it has up to 'n' unique hashes. (But iteration will be slow in a hash index * whose allocated capacity is much higher than its current number of * nodes.) */ void hindex_reserve(struct hindex *hindex, size_t n) { size_t new_mask = hindex_calc_mask(n); if (new_mask > hindex->mask) { COVERAGE_INC(hindex_reserve); hindex_resize(hindex, new_mask); } } /* Inserts 'node', with the given 'hash', into 'hindex'. Never automatically * expands 'hindex' (use hindex_insert() instead if you want that). */ void hindex_insert_fast(struct hindex *hindex, struct hindex_node *node, size_t hash) { struct hindex_node *head = hindex_node_with_hash(hindex, hash); if (head) { /* 'head' is an existing head with hash == 'hash'. * Insert 'node' as a body node just below 'head'. */ node->s = head->s; node->d = head; if (node->s) { node->s->d = node; } head->s = node; } else { /* No existing node has hash 'hash'. Insert 'node' as a new head in * its bucket. */ struct hindex_node **bucket = &hindex->buckets[hash & hindex->mask]; node->s = NULL; node->d = *bucket; *bucket = node; hindex->n_unique++; } node->hash = hash; } /* Inserts 'node', with the given 'hash', into 'hindex', and expands 'hindex' * if necessary to optimize search performance. */ void hindex_insert(struct hindex *hindex, struct hindex_node *node, size_t hash) { hindex_insert_fast(hindex, node, hash); if (hindex->n_unique / 2 > hindex->mask) { hindex_expand(hindex); } } /* Removes 'node' from 'hindex'. Does not shrink the hash index; call * hindex_shrink() directly if desired. */ void hindex_remove(struct hindex *hindex, struct hindex_node *node) { if (!hindex_node_is_head(node)) { node->d->s = node->s; if (node->s) { node->s->d = node->d; } } else { struct hindex_node **head; for (head = &hindex->buckets[node->hash & hindex->mask]; (*head)->hash != node->hash; head = &(*head)->d) { continue; } if (node->s) { *head = node->s; node->s->d = node->d; } else { *head = node->d; hindex->n_unique--; } } } /* Helper functions. */ /* Returns true if 'node', which must be inserted into an hindex, is a "body" * node, that is, it is not reachable from a bucket by following zero or more * 'd' pointers. Returns false otherwise. */ static bool hindex_node_is_body(const struct hindex_node *node) { return node->d && node->d->hash == node->hash; } /* Returns true if 'node', which must be inserted into an hindex, is a "head" * node, that is, if it is reachable from a bucket by following zero or more * 'd' pointers. Returns false if 'node' is a body node (and therefore one * must follow at least one 's' pointer to reach it). */ static bool hindex_node_is_head(const struct hindex_node *node) { return !hindex_node_is_body(node); } /* Reallocates 'hindex''s array of buckets to use bitwise mask 'new_mask'. */ static void hindex_resize(struct hindex *hindex, size_t new_mask) { struct hindex tmp; size_t i; ovs_assert(is_pow2(new_mask + 1)); ovs_assert(new_mask != SIZE_MAX); hindex_init(&tmp); if (new_mask) { tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1)); tmp.mask = new_mask; for (i = 0; i <= tmp.mask; i++) { tmp.buckets[i] = NULL; } } for (i = 0; i <= hindex->mask; i++) { struct hindex_node *node, *next; int count; count = 0; for (node = hindex->buckets[i]; node; node = next) { struct hindex_node **head = &tmp.buckets[node->hash & tmp.mask]; next = node->d; node->d = *head; *head = node; count++; } if (count > 5) { COVERAGE_INC(hindex_pathological); } } tmp.n_unique = hindex->n_unique; hindex_swap(hindex, &tmp); hindex_destroy(&tmp); } /* Returns the bitwise mask to use in struct hindex to support 'capacity' * hindex_nodes with unique hashes. */ static size_t hindex_calc_mask(size_t capacity) { size_t mask = capacity / 2; mask |= mask >> 1; mask |= mask >> 2; mask |= mask >> 4; mask |= mask >> 8; mask |= mask >> 16; #if SIZE_MAX > UINT32_MAX mask |= mask >> 32; #endif /* If we need to dynamically allocate buckets we might as well allocate at * least 4 of them. */ mask |= (mask & 1) << 1; return mask; } /* Returns the head node in 'hindex' with the given 'hash'. 'hindex' must * contain a head node with the given hash. */ static struct hindex_node * hindex_head_node(const struct hindex *hindex, size_t hash) { struct hindex_node *node = hindex->buckets[hash & hindex->mask]; while (node->hash != hash) { node = node->d; } return node; } static struct hindex_node * hindex_next__(const struct hindex *hindex, size_t start) { size_t i; for (i = start; i <= hindex->mask; i++) { struct hindex_node *node = hindex->buckets[i]; if (node) { return node; } } return NULL; } /* Returns the first node in 'hindex', in arbitrary order, or a null pointer if * 'hindex' is empty. */ struct hindex_node * hindex_first(const struct hindex *hindex) { return hindex_next__(hindex, 0); } /* Returns the next node in 'hindex' following 'node', in arbitrary order, or a * null pointer if 'node' is the last node in 'hindex'. * * If the hash index has been reallocated since 'node' was visited, some nodes * may be skipped or visited twice. */ struct hindex_node * hindex_next(const struct hindex *hindex, const struct hindex_node *node) { struct hindex_node *head; /* If there's a node with the same hash, return it. */ if (node->s) { return node->s; } /* If there's another node in the same bucket, return it. */ head = hindex_head_node(hindex, node->hash); if (head->d) { return head->d; } /* Return the first node in the next (or later) bucket. */ return hindex_next__(hindex, (node->hash & hindex->mask) + 1); } openvswitch-2.5.9/lib/PaxHeaders.82075/bfd.h0000644000000000000000000000013213534540071015316 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.069686075 30 ctime=1567801424.661852541 openvswitch-2.5.9/lib/bfd.h0000644000175000017500000000360313534540071017006 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BFD_H #define BFD_H 1 #define BFD_PACKET_LEN 24 #define BFD_DEST_PORT 3784 #include #include #include "packets.h" struct bfd; struct dpif_flow_stats; struct flow; struct flow_wildcards; struct netdev; struct dp_packet; struct smap; long long int bfd_wait(const struct bfd *); void bfd_run(struct bfd *); bool bfd_should_send_packet(const struct bfd *); void bfd_put_packet(struct bfd *bfd, struct dp_packet *packet, const struct eth_addr eth_src); bool bfd_should_process_flow(const struct bfd *, const struct flow *, struct flow_wildcards *); void bfd_process_packet(struct bfd *, const struct flow *, const struct dp_packet *); void bfd_init(void); struct bfd *bfd_configure(struct bfd *, const char *name, const struct smap *smap, struct netdev *netdev); struct bfd *bfd_ref(const struct bfd *); void bfd_unref(struct bfd *); void bfd_account_rx(struct bfd *, const struct dpif_flow_stats *); bool bfd_forwarding(struct bfd *); bool bfd_check_status_change(struct bfd *); void bfd_get_status(const struct bfd *, struct smap *); void bfd_set_netdev(struct bfd *, const struct netdev *); long long int bfd_wake_time(const struct bfd *); #endif /* bfd.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/crc32c.c0000644000000000000000000000013213534540071015635 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.685852718 openvswitch-2.5.9/lib/crc32c.c0000644000175000017500000001726013534540071017331 0ustar00jpettitjpettit00000000000000/*- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ /* * First, the polynomial itself and its table of feedback terms. The * polynomial is * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 * * Note that we take it "backwards" and put the highest-order term in * the lowest-order bit. The X^32 term is "implied"; the LSB is the * X^31 term, etc. The X^0 term (usually shown as "+1") results in * the MSB being 1 * * Note that the usual hardware shift register implementation, which * is what we're using (we're merely optimizing it by doing eight-bit * chunks at a time) shifts bits into the lowest-order term. In our * implementation, that means shifting towards the right. Why do we * do it this way? Because the calculated CRC must be transmitted in * order from highest-order term to lowest-order term. UARTs transmit * characters in order from LSB to MSB. By storing the CRC this way * we hand it to the UART in the order low-byte to high-byte; the UART * sends each low-bit to hight-bit; and the result is transmission bit * by bit from highest- to lowest-order term without requiring any bit * shuffling on our part. Reception works similarly * * The feedback terms table consists of 256, 32-bit entries. Notes * * The table can be generated at runtime if desired; code to do so * can be found in FreeBSD. It might not be obvious, but the feedback * terms simply represent the results of eight shift/xor opera * tions for all combinations of data and CRC register values * * The values must be right-shifted by eight bits by the "updcrc * logic; the shift must be unsigned (bring in zeroes). On some * hardware you could probably optimize the shift in assembler by * using byte-swap instructions * polynomial $edb88320 * * * CRC32 code derived from work by Gary S. Brown. */ #include #include "crc32c.h" #include "byte-order.h" /*****************************************************************/ /* */ /* CRC LOOKUP TABLE */ /* ================ */ /* The following CRC lookup table was generated automagically */ /* by the Rocksoft^tm Model CRC Algorithm Table Generation */ /* Program V1.0 using the following model parameters: */ /* */ /* Width : 4 bytes. */ /* Poly : 0x1EDC6F41L */ /* Reverse : TRUE. */ /* */ /* For more information on the Rocksoft^tm Model CRC Algorithm, */ /* see the document titled "A Painless Guide to CRC Error */ /* Detection Algorithms" by Ross Williams */ /* (ross@guest.adelaide.edu.au.). This document is likely to be */ /* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ /* */ /*****************************************************************/ static const uint32_t crc32Table[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L }; /* * Compute a CRC32c checksum as per the SCTP requirements in RFC4960. This * includes beginning with a checksum of all ones, and returning the negated * CRC. Unlike the RFC, we return the checksum in network byte-order. */ ovs_be32 crc32c(const uint8_t *data, size_t size) { uint32_t crc = 0xffffffffL; while (size--) { crc = crc32Table[(crc ^ *data++) & 0xff] ^ (crc >> 8); } /* The result of this CRC calculation provides us a value in the reverse * byte-order as compared with our architecture. On big-endian systems, * this is opposite to our return type. So, to return a big-endian * value, we must swap the byte-order. */ #if defined(WORDS_BIGENDIAN) crc = uint32_byteswap(crc); #endif /* Our value is in network byte-order. OVS_FORCE keeps sparse happy. */ return (OVS_FORCE ovs_be32) ~crc; } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic.h0000644000000000000000000000013213534540071016644 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 30 ctime=1567801424.821853721 openvswitch-2.5.9/lib/ovs-atomic.h0000644000175000017500000005535313534540071020345 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_ATOMIC_H #define OVS_ATOMIC_H 1 /* Atomic operations. * * This library implements atomic operations with an API based on the one * defined in C11. It includes multiple implementations for compilers and * libraries with varying degrees of built-in support for C11, including a * fallback implementation for systems that have pthreads but no other support * for atomics. * * This comment describes the common features of all the implementations. * * * Types * ===== * * The following atomic types are supported as typedefs for atomic versions of * the listed ordinary types: * * ordinary type atomic version * ------------------- ---------------------- * bool atomic_bool * * char atomic_char * signed char atomic_schar * unsigned char atomic_uchar * * short atomic_short * unsigned short atomic_ushort * * int atomic_int * unsigned int atomic_uint * * long atomic_long * unsigned long atomic_ulong * * long long atomic_llong * unsigned long long atomic_ullong * * size_t atomic_size_t * ptrdiff_t atomic_ptrdiff_t * * intmax_t atomic_intmax_t * uintmax_t atomic_uintmax_t * * intptr_t atomic_intptr_t * uintptr_t atomic_uintptr_t * * uint8_t atomic_uint8_t (*) * uint16_t atomic_uint16_t (*) * uint32_t atomic_uint32_t (*) * int8_t atomic_int8_t (*) * int16_t atomic_int16_t (*) * int32_t atomic_int32_t (*) * * (*) Not specified by C11. * * Atomic types may also be obtained via ATOMIC(TYPE), e.g. ATOMIC(void *). * Only basic integer types and pointer types can be made atomic this way, * e.g. atomic structs are not supported. * * The atomic version of a type doesn't necessarily have the same size or * representation as the ordinary version; for example, atomic_int might be a * typedef for a struct. The range of an atomic type does match the range of * the corresponding ordinary type. * * C11 says that one may use the _Atomic keyword in place of the typedef name, * e.g. "_Atomic int" instead of "atomic_int". This library doesn't support * that. * * * Life Cycle * ========== * * To initialize an atomic variable at its point of definition, use * ATOMIC_VAR_INIT: * * static atomic_int ai = ATOMIC_VAR_INIT(123); * * To initialize an atomic variable in code, use atomic_init(): * * static atomic_int ai; * ... * atomic_init(&ai, 123); * * * Barriers * ======== * * enum memory_order specifies the strictness of a memory barrier. It has the * following values: * * memory_order_relaxed: * * Only atomicity is provided, does not imply any memory ordering with * respect to any other variable (atomic or not). Relaxed accesses to * the same atomic variable will be performed in the program order. * The compiler and CPU are free to move memory accesses to other * variables past the atomic operation. * * memory_order_consume: * * Memory accesses with data dependency on the result of the consume * operation (atomic_read_explicit, or a load operation preceding a * atomic_thread_fence) will not be moved prior to the consume * barrier. Non-data-dependent loads and stores can be reordered to * happen before the consume barrier. * * RCU is the prime example of the use of the consume barrier: The * consume barrier guarantees that reads from a RCU protected object * are performed after the RCU protected pointer is read. A * corresponding release barrier is used to store the modified RCU * protected pointer after the RCU protected object has been fully * constructed. The synchronization between these barriers prevents * the RCU "consumer" from seeing uninitialized data. * * May not be used with atomic_store_explicit(), as consume semantics * applies only to atomic loads. * * memory_order_acquire: * * Memory accesses after an acquire barrier cannot be moved before the * barrier. Memory accesses before an acquire barrier *can* be moved * after it. * * An atomic_thread_fence with memory_order_acquire does not have a * load operation by itself; it prevents all following memory accesses * from moving prior to preceding loads. * * May not be used with atomic_store_explicit(), as acquire semantics * applies only to atomic loads. * * memory_order_release: * * Memory accesses before a release barrier cannot be moved after the * barrier. Memory accesses after a release barrier *can* be moved * before it. * * An atomic_thread_fence with memory_order_release does not have a * store operation by itself; it prevents all preceding memory accesses * from moving past subsequent stores. * * May not be used with atomic_read_explicit(), as release semantics * applies only to atomic stores. * * memory_order_acq_rel: * * Memory accesses cannot be moved across an acquire-release barrier in * either direction. * * May only be used with atomic read-modify-write operations, as both * load and store operation is required for acquire-release semantics. * * An atomic_thread_fence with memory_order_acq_rel does not have * either load or store operation by itself; it prevents all following * memory accesses from moving prior to preceding loads and all * preceding memory accesses from moving past subsequent stores. * * memory_order_seq_cst: * * Prevents movement of memory accesses like an acquire-release barrier, * but whereas acquire-release synchronizes cooperating threads (using * the same atomic variable), sequential-consistency synchronizes the * whole system, providing a total order for stores on all atomic * variables. * * OVS atomics require the memory_order to be passed as a compile-time constant * value, as some compiler implementations may perform poorly if the memory * order parameter is passed in as a run-time value. * * The following functions insert explicit barriers. Most of the other atomic * functions also include barriers. * * void atomic_thread_fence(memory_order order); * * Inserts a barrier of the specified type. * * For memory_order_relaxed, this is a no-op. * * void atomic_signal_fence(memory_order order); * * Inserts a barrier of the specified type, but only with respect to * signal handlers in the same thread as the barrier. This is * basically a compiler optimization barrier, except for * memory_order_relaxed, which is a no-op. * * * Atomic Operations * ================= * * In this section, A is an atomic type and C is the corresponding non-atomic * type. * * The "store" and "compare_exchange" primitives match C11: * * void atomic_store(A *object, C value); * void atomic_store_explicit(A *object, C value, memory_order); * * Atomically stores 'value' into '*object', respecting the given * memory order (or memory_order_seq_cst for atomic_store()). * * bool atomic_compare_exchange_strong(A *object, C *expected, C desired); * bool atomic_compare_exchange_weak(A *object, C *expected, C desired); * bool atomic_compare_exchange_strong_explicit(A *object, C *expected, * C desired, * memory_order success, * memory_order failure); * bool atomic_compare_exchange_weak_explicit(A *object, C *expected, * C desired, * memory_order success, * memory_order failure); * * Atomically loads '*object' and compares it with '*expected' and if * equal, stores 'desired' into '*object' (an atomic read-modify-write * operation) and returns true, and if non-equal, stores the actual * value of '*object' into '*expected' (an atomic load operation) and * returns false. The memory order for the successful case (atomic * read-modify-write operation) is 'success', and for the unsuccessful * case (atomic load operation) 'failure'. 'failure' may not be * stronger than 'success'. * * The weak forms may fail (returning false) also when '*object' equals * '*expected'. The strong form can be implemented by the weak form in * a loop. Some platforms can implement the weak form more * efficiently, so it should be used if the application will need to * loop anyway. * * The following primitives differ from the C11 ones (and have different names) * because there does not appear to be a way to implement the standard * primitives in standard C: * * void atomic_read(A *src, C *dst); * void atomic_read_explicit(A *src, C *dst, memory_order); * * Atomically loads a value from 'src', writing the value read into * '*dst', respecting the given memory order (or memory_order_seq_cst * for atomic_read()). * * void atomic_add(A *rmw, C arg, C *orig); * void atomic_sub(A *rmw, C arg, C *orig); * void atomic_or(A *rmw, C arg, C *orig); * void atomic_xor(A *rmw, C arg, C *orig); * void atomic_and(A *rmw, C arg, C *orig); * void atomic_add_explicit(A *rmw, C arg, C *orig, memory_order); * void atomic_sub_explicit(A *rmw, C arg, C *orig, memory_order); * void atomic_or_explicit(A *rmw, C arg, C *orig, memory_order); * void atomic_xor_explicit(A *rmw, C arg, C *orig, memory_order); * void atomic_and_explicit(A *rmw, C arg, C *orig, memory_order); * * Atomically applies the given operation, with 'arg' as the second * operand, to '*rmw', and stores the original value of '*rmw' into * '*orig', respecting the given memory order (or memory_order_seq_cst * if none is specified). * * The results are similar to those that would be obtained with +=, -=, * |=, ^=, or |= on non-atomic types. * * * atomic_flag * =========== * * atomic_flag is a typedef for a type with two states, set and clear, that * provides atomic test-and-set functionality. * * * Life Cycle * ---------- * * ATOMIC_FLAG_INIT is an initializer for atomic_flag. The initial state is * "clear". * * An atomic_flag may also be initialized at runtime with atomic_flag_clear(). * * * Operations * ---------- * * The following functions are available. * * bool atomic_flag_test_and_set(atomic_flag *object) * bool atomic_flag_test_and_set_explicit(atomic_flag *object, * memory_order); * * Atomically sets '*object', respsecting the given memory order (or * memory_order_seq_cst for atomic_flag_test_and_set()). Returns the * previous value of the flag (false for clear, true for set). * * void atomic_flag_clear(atomic_flag *object); * void atomic_flag_clear_explicit(atomic_flag *object, memory_order); * * Atomically clears '*object', respecting the given memory order (or * memory_order_seq_cst for atomic_flag_clear()). */ #include #include #include #include #include #include "compiler.h" #include "util.h" #define IN_OVS_ATOMIC_H #if __CHECKER__ /* sparse doesn't understand some GCC extensions we use. */ #include "ovs-atomic-pthreads.h" #elif __has_extension(c_atomic) #include "ovs-atomic-clang.h" #elif HAVE_STDATOMIC_H #include "ovs-atomic-c11.h" #elif __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 #include "ovs-atomic-gcc4.7+.h" #elif __GNUC__ && defined(__x86_64__) #include "ovs-atomic-x86_64.h" #elif __GNUC__ && defined(__i386__) #include "ovs-atomic-i586.h" #elif HAVE_GCC4_ATOMICS #include "ovs-atomic-gcc4+.h" #elif _MSC_VER && _M_IX86 >= 500 #include "ovs-atomic-msvc.h" #else /* ovs-atomic-pthreads implementation is provided for portability. * It might be too slow for real use because Open vSwitch is * optimized for platforms where real atomic ops are available. */ #include "ovs-atomic-pthreads.h" #endif #undef IN_OVS_ATOMIC_H #ifndef OMIT_STANDARD_ATOMIC_TYPES typedef ATOMIC(bool) atomic_bool; typedef ATOMIC(char) atomic_char; typedef ATOMIC(signed char) atomic_schar; typedef ATOMIC(unsigned char) atomic_uchar; typedef ATOMIC(short) atomic_short; typedef ATOMIC(unsigned short) atomic_ushort; typedef ATOMIC(int) atomic_int; typedef ATOMIC(unsigned int) atomic_uint; typedef ATOMIC(long) atomic_long; typedef ATOMIC(unsigned long) atomic_ulong; typedef ATOMIC(long long) atomic_llong; typedef ATOMIC(unsigned long long) atomic_ullong; typedef ATOMIC(size_t) atomic_size_t; typedef ATOMIC(ptrdiff_t) atomic_ptrdiff_t; typedef ATOMIC(intmax_t) atomic_intmax_t; typedef ATOMIC(uintmax_t) atomic_uintmax_t; typedef ATOMIC(intptr_t) atomic_intptr_t; typedef ATOMIC(uintptr_t) atomic_uintptr_t; #endif /* !OMIT_STANDARD_ATOMIC_TYPES */ /* Nonstandard atomic types. */ typedef ATOMIC(uint8_t) atomic_uint8_t; typedef ATOMIC(uint16_t) atomic_uint16_t; typedef ATOMIC(uint32_t) atomic_uint32_t; typedef ATOMIC(int8_t) atomic_int8_t; typedef ATOMIC(int16_t) atomic_int16_t; typedef ATOMIC(int32_t) atomic_int32_t; /* Relaxed atomic operations. * * When an operation on an atomic variable is not expected to synchronize * with operations on other (atomic or non-atomic) variables, no memory * barriers are needed and the relaxed memory ordering can be used. These * macros make such uses less daunting, but not invisible. */ #define atomic_store_relaxed(VAR, VALUE) \ atomic_store_explicit(VAR, VALUE, memory_order_relaxed) #define atomic_read_relaxed(VAR, DST) \ atomic_read_explicit(VAR, DST, memory_order_relaxed) #define atomic_compare_exchange_strong_relaxed(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_relaxed, \ memory_order_relaxed) #define atomic_compare_exchange_weak_relaxed(DST, EXP, SRC) \ atomic_compare_exchange_weak_explicit(DST, EXP, SRC, \ memory_order_relaxed, \ memory_order_relaxed) #define atomic_add_relaxed(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_relaxed) #define atomic_sub_relaxed(RMW, ARG, ORIG) \ atomic_sub_explicit(RMW, ARG, ORIG, memory_order_relaxed) #define atomic_or_relaxed(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_relaxed) #define atomic_xor_relaxed(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_relaxed) #define atomic_and_relaxed(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_relaxed) #define atomic_flag_test_and_set_relaxed(FLAG) \ atomic_flag_test_and_set_explicit(FLAG, memory_order_relaxed) #define atomic_flag_clear_relaxed(FLAG) \ atomic_flag_clear_explicit(FLAG, memory_order_relaxed) /* A simplified atomic count. Does not provide any synchronization with any * other variables. * * Typically a counter is not used to synchronize the state of any other * variables (with the notable exception of reference count, below). * This abstraction releaves the user from the memory order considerations, * and may make the code easier to read. * * We only support the unsigned int counters, as those are the most common. */ typedef struct atomic_count { atomic_uint count; } atomic_count; #define ATOMIC_COUNT_INIT(VALUE) { VALUE } static inline void atomic_count_init(atomic_count *count, unsigned int value) { atomic_init(&count->count, value); } static inline unsigned int atomic_count_inc(atomic_count *count) { unsigned int old; atomic_add_relaxed(&count->count, 1, &old); return old; } static inline unsigned int atomic_count_dec(atomic_count *count) { unsigned int old; atomic_sub_relaxed(&count->count, 1, &old); return old; } static inline unsigned int atomic_count_get(atomic_count *count) { unsigned int value; atomic_read_relaxed(&count->count, &value); return value; } static inline void atomic_count_set(atomic_count *count, unsigned int value) { atomic_store_relaxed(&count->count, value); } /* Reference count. */ struct ovs_refcount { atomic_uint count; }; /* Initializes 'refcount'. The reference count is initially 1. */ static inline void ovs_refcount_init(struct ovs_refcount *refcount) { atomic_init(&refcount->count, 1); } /* Increments 'refcount'. * * Does not provide a memory barrier, as the calling thread must have * protected access to the object already. */ static inline void ovs_refcount_ref(struct ovs_refcount *refcount) { unsigned int old_refcount; atomic_add_explicit(&refcount->count, 1, &old_refcount, memory_order_relaxed); ovs_assert(old_refcount > 0); } /* Decrements 'refcount' and returns the previous reference count. Often used * in this form: * * if (ovs_refcount_unref(&object->ref_cnt) == 1) { * // ...uninitialize object... * free(object); * } * * Provides a release barrier making the preceding loads and stores to not be * reordered after the unref, and in case of the last reference provides also * an acquire barrier to keep all the following uninitialization from being * reordered before the atomic decrement operation. Together these synchronize * any concurrent unref operations between each other. */ static inline unsigned int ovs_refcount_unref(struct ovs_refcount *refcount) { unsigned int old_refcount; atomic_sub_explicit(&refcount->count, 1, &old_refcount, memory_order_release); ovs_assert(old_refcount > 0); if (old_refcount == 1) { /* 'memory_order_release' above means that there are no (reordered) * accesses to the protected object from any thread at this point. * An acquire barrier is needed to keep all subsequent access to the * object's memory from being reordered before the atomic operation * above. */ atomic_thread_fence(memory_order_acquire); } return old_refcount; } /* Reads and returns 'refcount_''s current reference count. * * Does not provide a memory barrier. * * Rarely useful. */ static inline unsigned int ovs_refcount_read(const struct ovs_refcount *refcount_) { struct ovs_refcount *refcount = CONST_CAST(struct ovs_refcount *, refcount_); unsigned int count; atomic_read_explicit(&refcount->count, &count, memory_order_relaxed); return count; } /* Increments 'refcount', but only if it is non-zero. * * This may only be called for an object which is RCU protected during * this call. This implies that its possible destruction is postponed * until all current RCU threads quiesce. * * Returns false if the refcount was zero. In this case the object may * be safely accessed until the current thread quiesces, but no additional * references to the object may be taken. * * Does not provide a memory barrier, as the calling thread must have * RCU protected access to the object already. * * It is critical that we never increment a zero refcount to a * non-zero value, as whenever a refcount reaches the zero value, the * protected object may be irrevocably scheduled for deletion. */ static inline bool ovs_refcount_try_ref_rcu(struct ovs_refcount *refcount) { unsigned int count; atomic_read_explicit(&refcount->count, &count, memory_order_relaxed); do { if (count == 0) { return false; } } while (!atomic_compare_exchange_weak_explicit(&refcount->count, &count, count + 1, memory_order_relaxed, memory_order_relaxed)); return true; } /* Decrements 'refcount' and returns the previous reference count. To * be used only when a memory barrier is already provided for the * protected object independently. * * For example: * * if (ovs_refcount_unref_relaxed(&object->ref_cnt) == 1) { * // Schedule uninitialization and freeing of the object: * ovsrcu_postpone(destructor_function, object); * } * * Here RCU quiescing already provides a full memory barrier. No additional * barriers are needed here. * * Or: * * if (stp && ovs_refcount_unref_relaxed(&stp->ref_cnt) == 1) { * ovs_mutex_lock(&mutex); * list_remove(&stp->node); * ovs_mutex_unlock(&mutex); * free(stp->name); * free(stp); * } * * Here a mutex is used to guard access to all of 'stp' apart from * 'ref_cnt'. Hence all changes to 'stp' by other threads must be * visible when we get the mutex, and no access after the unlock can * be reordered to happen prior the lock operation. No additional * barriers are needed here. */ static inline unsigned int ovs_refcount_unref_relaxed(struct ovs_refcount *refcount) { unsigned int old_refcount; atomic_sub_explicit(&refcount->count, 1, &old_refcount, memory_order_relaxed); ovs_assert(old_refcount > 0); return old_refcount; } #endif /* ovs-atomic.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/lldp0000644000000000000000000000013213534540120015263 xustar0030 mtime=1567801424.957854723 30 atime=1567801425.625859648 30 ctime=1567801424.957854723 openvswitch-2.5.9/lib/lldp/0000755000175000017500000000000013534540120017026 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldp-const.h0000644000000000000000000000013013534540071017573 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.949854664 openvswitch-2.5.9/lib/lldp/lldp-const.h0000644000175000017500000002007613534540071021270 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2008 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _LLDP_H #define _LLDP_H /* Definitions prefixed by `LLDP_` are constants from LLDP * specifications. Definitions prefixed by `LLDPD_` are custom * constants that are useful in the context of lldpd and its clients. */ /* Chassis ID subtype */ #define LLDP_CHASSISID_SUBTYPE_CHASSIS 1 #define LLDP_CHASSISID_SUBTYPE_IFALIAS 2 #define LLDP_CHASSISID_SUBTYPE_PORT 3 #define LLDP_CHASSISID_SUBTYPE_LLADDR 4 #define LLDP_CHASSISID_SUBTYPE_ADDR 5 #define LLDP_CHASSISID_SUBTYPE_IFNAME 6 #define LLDP_CHASSISID_SUBTYPE_LOCAL 7 /* Port ID subtype */ #define LLDP_PORTID_SUBTYPE_UNKNOWN 0 #define LLDP_PORTID_SUBTYPE_IFALIAS 1 #define LLDP_PORTID_SUBTYPE_PORT 2 #define LLDP_PORTID_SUBTYPE_LLADDR 3 #define LLDP_PORTID_SUBTYPE_ADDR 4 #define LLDP_PORTID_SUBTYPE_IFNAME 5 #define LLDP_PORTID_SUBTYPE_AGENTCID 6 #define LLDP_PORTID_SUBTYPE_LOCAL 7 #define LLDP_PORTID_SUBTYPE_MAX LLDP_PORTID_SUBTYPE_LOCAL /* Operational MAU Type field, from RFC 3636 */ #define LLDP_DOT3_MAU_AUI 1 #define LLDP_DOT3_MAU_10BASE5 2 #define LLDP_DOT3_MAU_FOIRL 3 #define LLDP_DOT3_MAU_10BASE2 4 #define LLDP_DOT3_MAU_10BASET 5 #define LLDP_DOT3_MAU_10BASEFP 6 #define LLDP_DOT3_MAU_10BASEFB 7 #define LLDP_DOT3_MAU_10BASEFL 8 #define LLDP_DOT3_MAU_10BROAD36 9 #define LLDP_DOT3_MAU_10BASETHD 10 #define LLDP_DOT3_MAU_10BASETFD 11 #define LLDP_DOT3_MAU_10BASEFLHD 12 #define LLDP_DOT3_MAU_10BASEFLFD 13 #define LLDP_DOT3_MAU_10BASET4 14 #define LLDP_DOT3_MAU_100BASETXHD 15 #define LLDP_DOT3_MAU_100BASETXFD 16 #define LLDP_DOT3_MAU_100BASEFXHD 17 #define LLDP_DOT3_MAU_100BASEFXFD 18 #define LLDP_DOT3_MAU_100BASET2HD 19 #define LLDP_DOT3_MAU_100BASET2FD 20 #define LLDP_DOT3_MAU_1000BASEXHD 21 #define LLDP_DOT3_MAU_1000BASEXFD 22 #define LLDP_DOT3_MAU_1000BASELXHD 23 #define LLDP_DOT3_MAU_1000BASELXFD 24 #define LLDP_DOT3_MAU_1000BASESXHD 25 #define LLDP_DOT3_MAU_1000BASESXFD 26 #define LLDP_DOT3_MAU_1000BASECXHD 27 #define LLDP_DOT3_MAU_1000BASECXFD 28 #define LLDP_DOT3_MAU_1000BASETHD 29 #define LLDP_DOT3_MAU_1000BASETFD 30 #define LLDP_DOT3_MAU_10GIGBASEX 31 #define LLDP_DOT3_MAU_10GIGBASELX4 32 #define LLDP_DOT3_MAU_10GIGBASER 33 #define LLDP_DOT3_MAU_10GIGBASEER 34 #define LLDP_DOT3_MAU_10GIGBASELR 35 #define LLDP_DOT3_MAU_10GIGBASESR 36 #define LLDP_DOT3_MAU_10GIGBASEW 37 #define LLDP_DOT3_MAU_10GIGBASEEW 38 #define LLDP_DOT3_MAU_10GIGBASELW 39 #define LLDP_DOT3_MAU_10GIGBASESW 40 /* Dot3 Power Devicetype */ #define LLDP_DOT3_POWER_PSE 1 #define LLDP_DOT3_POWER_PD 2 /* Dot3 Power Pairs (RFC 3621) */ #define LLDP_DOT3_POWERPAIRS_SIGNAL 1 #define LLDP_DOT3_POWERPAIRS_SPARE 2 /* Dot3 Power type (for 802.3at) */ #define LLDP_DOT3_POWER_8023AT_OFF 0 #define LLDP_DOT3_POWER_8023AT_TYPE1 1 #define LLDP_DOT3_POWER_8023AT_TYPE2 2 /* Dot3 power source */ #define LLDP_DOT3_POWER_SOURCE_UNKNOWN 0 #define LLDP_DOT3_POWER_SOURCE_PRIMARY 1 #define LLDP_DOT3_POWER_SOURCE_PSE 1 #define LLDP_DOT3_POWER_SOURCE_BACKUP 2 #define LLDP_DOT3_POWER_SOURCE_LOCAL 2 #define LLDP_DOT3_POWER_SOURCE_BOTH 3 /* Dot3 power priority */ #define LLDP_DOT3_POWER_PRIO_UNKNOWN 0 #define LLDP_DOT3_POWER_PRIO_CRITICAL 1 #define LLDP_DOT3_POWER_PRIO_HIGH 2 #define LLDP_DOT3_POWER_PRIO_LOW 3 /* PMD Auto-Negotiation Advertised Capability field, from RFC 3636 */ #define LLDP_DOT3_LINK_AUTONEG_OTHER 0x8000 #define LLDP_DOT3_LINK_AUTONEG_10BASE_T 0x4000 #define LLDP_DOT3_LINK_AUTONEG_10BASET_FD 0x2000 #define LLDP_DOT3_LINK_AUTONEG_100BASE_T4 0x1000 #define LLDP_DOT3_LINK_AUTONEG_100BASE_TX 0x0800 #define LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD 0x0400 #define LLDP_DOT3_LINK_AUTONEG_100BASE_T2 0x0200 #define LLDP_DOT3_LINK_AUTONEG_100BASE_T2FD 0x0100 #define LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE 0x0080 #define LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE 0x0040 #define LLDP_DOT3_LINK_AUTONEG_FDX_SPAUSE 0x0020 #define LLDP_DOT3_LINK_AUTONEG_FDX_BPAUSE 0x0010 #define LLDP_DOT3_LINK_AUTONEG_1000BASE_X 0x0008 #define LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD 0x0004 #define LLDP_DOT3_LINK_AUTONEG_1000BASE_T 0x0002 #define LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD 0x0001 /* Capabilities */ #define LLDP_CAP_OTHER 0x01 #define LLDP_CAP_REPEATER 0x02 #define LLDP_CAP_BRIDGE 0x04 #define LLDP_CAP_WLAN 0x08 #define LLDP_CAP_ROUTER 0x10 #define LLDP_CAP_TELEPHONE 0x20 #define LLDP_CAP_DOCSIS 0x40 #define LLDP_CAP_STATION 0x80 #define LLDP_PPVID_CAP_SUPPORTED (1 << 1) #define LLDP_PPVID_CAP_ENABLED (1 << 2) /* see http://www.iana.org/assignments/address-family-numbers */ #define LLDP_MGMT_ADDR_NONE 0 #define LLDP_MGMT_ADDR_IP4 1 #define LLDP_MGMT_ADDR_IP6 2 #define LLDP_MGMT_IFACE_UNKNOWN 1 #define LLDP_MGMT_IFACE_IFINDEX 2 #define LLDP_MGMT_IFACE_SYSPORT 3 #define LLDP_MED_CLASS_I 1 #define LLDP_MED_CLASS_II 2 #define LLDP_MED_CLASS_III 3 #define LLDP_MED_NETWORK_DEVICE 4 /* LLDP MED application ttpes */ #define LLDP_MED_APPTYPE_UNDEFINED 0 #define LLDP_MED_APPTYPE_VOICE 1 #define LLDP_MED_APPTYPE_VOICESIGNAL 2 #define LLDP_MED_APPTYPE_GUESTVOICE 3 #define LLDP_MED_APPTYPE_GUESTVOICESIGNAL 4 #define LLDP_MED_APPTYPE_SOFTPHONEVOICE 5 #define LLDP_MED_APPTYPE_VIDEOCONFERENCE 6 #define LLDP_MED_APPTYPE_VIDEOSTREAM 7 #define LLDP_MED_APPTYPE_VIDEOSIGNAL 8 #define LLDP_MED_APPTYPE_LAST LLDP_MED_APPTYPE_VIDEOSIGNAL /* LLDP MED location formats */ #define LLDP_MED_LOCFORMAT_COORD 1 #define LLDP_MED_LOCFORMAT_CIVIC 2 #define LLDP_MED_LOCFORMAT_ELIN 3 #define LLDP_MED_LOCFORMAT_LAST LLDP_MED_LOCFORMAT_ELIN #define LLDP_MED_LOCATION_GEOID_WGS84 1 #define LLDP_MED_LOCATION_GEOID_NAD83 2 #define LLDP_MED_LOCATION_GEOID_NAD83_MLLW 3 #define LLDP_MED_LOCATION_ALTITUDE_UNIT_METER 1 #define LLDP_MED_LOCATION_ALTITUDE_UNIT_FLOOR 2 /* LLDP MED power related constants */ #define LLDP_MED_POW_TYPE_PSE 1 #define LLDP_MED_POW_TYPE_PD 2 #define LLDP_MED_POW_TYPE_RESERVED 3 #define LLDP_MED_POW_SOURCE_UNKNOWN 1 #define LLDP_MED_POW_SOURCE_PRIMARY 2 #define LLDP_MED_POW_SOURCE_BACKUP 3 #define LLDP_MED_POW_SOURCE_RESERVED 4 #define LLDP_MED_POW_SOURCE_PSE 5 #define LLDP_MED_POW_SOURCE_LOCAL 6 #define LLDP_MED_POW_SOURCE_BOTH 7 #define LLDP_MED_POW_PRIO_UNKNOWN 0 #define LLDP_MED_POW_PRIO_CRITICAL 1 #define LLDP_MED_POW_PRIO_HIGH 2 #define LLDP_MED_POW_PRIO_LOW 3 /* LLDP MED capabilities */ #define LLDP_MED_CAP_CAP 0x01 #define LLDP_MED_CAP_POLICY 0x02 #define LLDP_MED_CAP_LOCATION 0x04 #define LLDP_MED_CAP_MDI_PSE 0x08 #define LLDP_MED_CAP_MDI_PD 0x10 #define LLDP_MED_CAP_IV 0x20 /* Protocol constants for multi-protocol lldpd */ #define LLDPD_MODE_LLDP 1 #define LLDPD_MODE_CDPV1 2 #define LLDPD_MODE_CDPV2 3 #define LLDPD_MODE_SONMP 4 #define LLDPD_MODE_EDP 5 #define LLDPD_MODE_FDP 6 #define LLDPD_MODE_MAX LLDPD_MODE_FDP /* Bond slave src mac type constants */ #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_UNKNOWN 0 #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_REAL 1 #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_ZERO 2 #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_FIXED 3 #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED 4 #define LLDP_BOND_SLAVE_SRC_MAC_TYPE_MAX \ LLDP_BOND_SLAVE_SRC_MAC_TYPE_LOCALLY_ADMINISTERED #endif /* _LLDP_H */ openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldpd.h0000644000000000000000000000013013534540071016613 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.953854694 openvswitch-2.5.9/lib/lldp/lldpd.h0000644000175000017500000000670613534540071020314 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _LLDPD_H #define _LLDPD_H #ifndef _WIN32 #include #endif #include #include #include #include #include "dp-packet.h" #include "list.h" #include "lldpd-structs.h" #include "lldp-tlv.h" #include "packets.h" #include "openvswitch/vlog.h" #define ETHERTYPE_LLDP 0x88cc #define LLDPD_TX_INTERVAL 5 #define LLDPD_TX_HOLD 4 #define LLDPD_TTL (LLDPD_TX_INTERVAL * LLDPD_TX_HOLD) #define PROTO_SEND_SIG struct lldpd *, struct lldpd_hardware *,struct dp_packet * #define PROTO_DECODE_SIG struct lldpd *, char *, int, struct lldpd_hardware *,\ struct lldpd_chassis **, struct lldpd_port ** #define PROTO_GUESS_SIG char *, int struct protocol { int mode; /* > 0 mode identifier (unique per protocol) */ int enabled; /* Is this protocol enabled? */ char *name; /* Name of protocol */ char arg; /* Argument to enable this protocol */ int(*send)(PROTO_SEND_SIG); /* How to send a frame */ int(*decode)(PROTO_DECODE_SIG); /* How to decode a frame */ int(*guess)(PROTO_GUESS_SIG); /* Can be NULL, use MAC address in this * case */ struct eth_addr mac; /* Destination MAC address used by this protocol */ }; #define SMART_HIDDEN(port) (port->p_hidden_in) struct lldpd { struct lldpd_config g_config; struct protocol *g_protocols; int g_lastrid; struct ovs_list g_chassis; /* Contains "struct lldp_chassis". */ struct ovs_list g_hardware; /* Contains "struct lldpd_hardware". */ }; static inline struct lldpd_hardware * lldpd_first_hardware(struct lldpd *lldpd) { return CONTAINER_OF(list_front(&lldpd->g_hardware), struct lldpd_hardware, h_entries); } /* lldpd.c */ struct lldpd_hardware *lldpd_get_hardware(struct lldpd *, char *, int, struct lldpd_ops *); struct lldpd_hardware *lldpd_alloc_hardware(struct lldpd *, char *, int); void lldpd_hardware_cleanup(struct lldpd*, struct lldpd_hardware *); struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface); void lldpd_recv(struct lldpd *, struct lldpd_hardware *, char *, size_t); uint32_t lldpd_send(struct lldpd_hardware *, struct dp_packet *); void lldpd_loop(struct lldpd *); int lldpd_main(int, char **); void lldpd_update_localports(struct lldpd *); void lldpd_cleanup(struct lldpd *); void lldpd_assign_cfg_to_protocols(struct lldpd *); /* lldp.c */ int lldp_send(PROTO_SEND_SIG); int lldp_decode(PROTO_DECODE_SIG); #endif /* _LLDPD_H */ openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/aa-structs.h0000644000000000000000000000013113534540071017603 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.949854664 openvswitch-2.5.9/lib/lldp/aa-structs.h0000644000175000017500000000315413534540071021275 0ustar00jpettitjpettit00000000000000/* aa-structs.h */ /* contains tlv structures for various auto attach functionality */ /* Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2014 Avaya, Inc * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef AA_STRUCTS_H #define AA_STRUCTS_H #include #include "list.h" struct lldp_aa_element_system_id { struct eth_addr system_mac; uint16_t conn_type; uint16_t rsvd; uint8_t rsvd2[2]; }; struct lldpd_aa_element_tlv { uint16_t type; uint16_t vlan_tagging; uint16_t auto_prov_mode; uint16_t mgmt_vlan; struct lldp_aa_element_system_id system_id; }; struct lldpd_aa_isid_vlan_map_data { uint16_t status; uint16_t vlan; uint32_t isid; }; struct lldpd_aa_isid_vlan_maps_tlv { struct ovs_list m_entries; struct lldpd_aa_isid_vlan_map_data isid_vlan_data; }; #endif openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldpd-structs.h0000644000000000000000000000013013534540071020320 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.957854723 openvswitch-2.5.9/lib/lldp/lldpd-structs.h0000644000175000017500000002064613534540071022020 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _LLDPD_STRUCTS_H #define _LLDPD_STRUCTS_H #include #ifndef _WIN32 #include #endif #include #include #include "aa-structs.h" #include "lldp-const.h" #include "packets.h" enum { LLDPD_AF_UNSPEC = 0, LLDPD_AF_IPV4, LLDPD_AF_IPV6, LLDPD_AF_LAST }; inline static int lldpd_af(int af) { switch (af) { case LLDPD_AF_IPV4: return AF_INET; case LLDPD_AF_IPV6: return AF_INET6; case LLDPD_AF_LAST: return AF_MAX; default: return AF_UNSPEC; } } #define LLDPD_MGMT_MAXADDRSIZE 16 /* sizeof(struct in6_addr) */ struct lldpd_mgmt { struct ovs_list m_entries; int m_family; union { struct in_addr inet; struct in6_addr inet6; u_int8_t octets[LLDPD_MGMT_MAXADDRSIZE]; } m_addr; size_t m_addrsize; u_int32_t m_iface; }; struct lldpd_chassis { struct ovs_list list; u_int16_t c_refcount; /* Reference count by ports */ u_int16_t c_index; /* Monotonic index */ u_int8_t c_protocol; /* Protocol used to get this chassis */ u_int8_t c_id_subtype; uint8_t *c_id; /* Typically an Ethernet address. */ int c_id_len; char *c_name; char *c_descr; u_int16_t c_cap_available; u_int16_t c_cap_enabled; u_int16_t c_ttl; struct ovs_list c_mgmt; /* Contains "struct lldp_mgmt"s. */ }; /* WARNING: any change to this structure should also be reflected into `lldpd_copy_chassis()` which is not using marshaling. */ struct lldpd_port { struct ovs_list p_entries; struct lldpd_chassis *p_chassis; /* Attached chassis */ time_t p_lastchange; /* Time of last change of values */ time_t p_lastupdate; /* Time of last update received */ struct lldpd_frame *p_lastframe; /* Frame received during last update */ u_int8_t p_protocol; /* Protocol used to get this port */ u_int8_t p_hidden_in:1; /* Considered hidden for reception */ u_int8_t p_hidden_out:1; /* Considered hidden for emission */ /* Important: all fields that should be ignored to check if a port has * been changed should be before p_id_subtype. Check * `lldpd_reset_timer()`. */ u_int8_t p_id_subtype; char *p_id; int p_id_len; char *p_descr; u_int16_t p_mfs; struct lldpd_aa_element_tlv p_element; struct ovs_list p_isid_vlan_maps; /* Contains "struct lldpd_aa_isid_vlan_maps_tlv"s. */ }; /* Smart mode / Hide mode */ #define SMART_INCOMING_FILTER (1<<0) /* Incoming filtering enabled */ #define SMART_INCOMING_ONE_PROTO (1<<1) /* On reception, keep only 1 proto */ #define SMART_INCOMING_ONE_NEIGH (1<<2) /* On recep., keep only 1 neighbor */ #define SMART_OUTGOING_FILTER (1<<3) /* Outgoing filtering enabled */ #define SMART_OUTGOING_ONE_PROTO (1<<4) /* On emission, keep only one proto */ #define SMART_OUTGOING_ONE_NEIGH (1<<5) /* On emission, consider only one neighbor */ #define SMART_INCOMING (SMART_INCOMING_FILTER | \ SMART_INCOMING_ONE_PROTO | \ SMART_INCOMING_ONE_NEIGH) #define SMART_OUTGOING (SMART_OUTGOING_FILTER | \ SMART_OUTGOING_ONE_PROTO | \ SMART_OUTGOING_ONE_NEIGH) struct lldpd_config { int c_paused; /* lldpd is paused */ int c_tx_interval; /* Transmit interval */ int c_smart; /* Bitmask for smart configuration (see SMART_*) */ int c_receiveonly; /* Receive only mode */ int c_max_neighbors; /* Maximum number of neighbors (per protocol) */ char *c_mgmt_pattern; /* Pattern to match a management address */ char *c_cid_pattern; /* Pattern to match interfaces to use for chassis * ID */ char *c_iface_pattern; /* Pattern to match interfaces to use */ char *c_platform; /* Override platform description (for CDP) */ char *c_description; /* Override chassis description */ char *c_hostname; /* Override system name */ int c_advertise_version; /* Should the precise version be advertised? */ int c_set_ifdescr; /* Set interface description */ int c_promisc; /* Interfaces should be in promiscuous mode */ int c_tx_hold; /* Transmit hold */ int c_bond_slave_src_mac_type; /* Src mac type in lldp frames over bond * slaves */ int c_lldp_portid_type; /* The PortID type */ }; struct lldpd_frame { int size; unsigned char frame[]; }; struct lldpd_hardware; struct lldpd; struct lldpd_ops { int (*send)(struct lldpd *, struct lldpd_hardware *, char *, size_t); /* Function to send a frame */ int (*recv)(struct lldpd *, struct lldpd_hardware *, int, char *, size_t); /* Function to receive a frame */ int (*cleanup)(struct lldpd *, struct lldpd_hardware *); /* Cleanup */ }; /* An interface is uniquely identified by h_ifindex, h_ifname and h_ops. This * means if an interface becomes enslaved, it will be considered as a new * interface. The same applies for renaming and we include the index in case of * renaming to an existing interface. */ struct lldpd_hardware { struct ovs_list h_entries; struct lldpd *h_cfg; /* Pointer to main configuration */ void *h_recv; /* FD for reception */ int h_sendfd; /* FD for sending, only used by h_ops */ int h_mangle; /* 1 if we have to mangle the MAC address */ struct lldpd_ops *h_ops; /* Hardware-dependent functions */ void *h_data; /* Hardware-dependent data */ void *h_timer; /* Timer for this port */ int h_mtu; int h_flags; /* Packets will be sent only * if IFF_RUNNING. Will be * removed if this is left * to 0. */ int h_ifindex; /* Interface index, used by SNMP */ char h_ifname[IFNAMSIZ]; /* Should be unique */ struct eth_addr h_lladdr; u_int64_t h_tx_cnt; u_int64_t h_rx_cnt; u_int64_t h_rx_discarded_cnt; u_int64_t h_rx_unrecognized_cnt; u_int64_t h_ageout_cnt; u_int64_t h_insert_cnt; u_int64_t h_delete_cnt; u_int64_t h_drop_cnt; u_int16_t h_lport_cksum; /* Checksum on local port to see if there * is a change */ struct lldpd_port h_lport; /* Port attached to this hardware port */ struct ovs_list h_rports; /* Contains "struct lldp_port"s. */ }; struct lldpd_interface; struct lldpd_interface_list; struct lldpd_neighbor_change { char *ifname; #define NEIGHBOR_CHANGE_DELETED -1 #define NEIGHBOR_CHANGE_ADDED 1 #define NEIGHBOR_CHANGE_UPDATED 0 int state; struct lldpd_port *neighbor; }; /* Cleanup functions */ void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *); void lldpd_chassis_cleanup(struct lldpd_chassis *, bool all); void lldpd_remote_cleanup(struct lldpd_hardware *, void (*expire)(struct lldpd_hardware *, struct lldpd_port *), bool all); void lldpd_port_cleanup(struct lldpd_port *, bool all); void lldpd_config_cleanup(struct lldpd_config *); #endif openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldpd.c0000644000000000000000000000013013534540071016606 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.953854694 openvswitch-2.5.9/lib/lldp/lldpd.c0000644000175000017500000004650613534540071020311 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "lldpd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #endif #include "compiler.h" #include "list.h" #include "packets.h" #include "timeval.h" VLOG_DEFINE_THIS_MODULE(lldpd); static struct protocol protos[] = { { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL, LLDP_MULTICAST_ADDR }, { 0, 0, "any", ' ', NULL, NULL, NULL, { { { 0,0,0,0,0,0 } } } } }; void lldpd_assign_cfg_to_protocols(struct lldpd *cfg) { cfg->g_protocols = protos; } struct lldpd_hardware * lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops) { struct lldpd_hardware *hw; LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) { if (!strcmp(hw->h_ifname, name) && hw->h_ifindex == index && (!ops || ops == hw->h_ops)) { return hw; } } return NULL; } struct lldpd_hardware * lldpd_alloc_hardware(struct lldpd *cfg, char *name, int index) { struct lldpd_hardware *hw; VLOG_DBG("allocate a new local hardware interface (%s)", name); hw = xzalloc(sizeof *hw); hw->h_cfg = cfg; ovs_strlcpy(hw->h_ifname, name, sizeof hw->h_ifname); hw->h_ifindex = index; hw->h_lport.p_chassis = CONTAINER_OF(list_front(&cfg->g_chassis), struct lldpd_chassis, list); hw->h_lport.p_chassis->c_refcount++; list_init(&hw->h_rports); return hw; } struct lldpd_mgmt * lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface) { struct lldpd_mgmt *mgmt; VLOG_DBG("allocate a new management address (family: %d)", family); if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) { errno = EAFNOSUPPORT; return NULL; } if (addrsize > LLDPD_MGMT_MAXADDRSIZE) { errno = EOVERFLOW; return NULL; } mgmt = xzalloc(sizeof *mgmt); mgmt->m_family = family; memcpy(&mgmt->m_addr, addrptr, addrsize); mgmt->m_addrsize = addrsize; mgmt->m_iface = iface; return mgmt; } void lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) { VLOG_DBG("cleanup hardware port %s", hardware->h_ifname); lldpd_port_cleanup(&hardware->h_lport, true); if (hardware->h_ops && hardware->h_ops->cleanup) { hardware->h_ops->cleanup(cfg, hardware); } free(hardware); } void lldpd_cleanup(struct lldpd *cfg) { struct lldpd_hardware *hw, *hw_next; struct lldpd_chassis *chassis, *chassis_next; VLOG_DBG("cleanup all ports"); LIST_FOR_EACH_SAFE (hw, hw_next, h_entries, &cfg->g_hardware) { if (!hw->h_flags) { list_remove(&hw->h_entries); lldpd_remote_cleanup(hw, NULL, true); lldpd_hardware_cleanup(cfg, hw); } else { lldpd_remote_cleanup(hw, NULL, false); } } VLOG_DBG("cleanup all chassis"); LIST_FOR_EACH_SAFE (chassis, chassis_next, list, &cfg->g_chassis) { if (chassis->c_refcount == 0) { list_remove(&chassis->list); lldpd_chassis_cleanup(chassis, 1); } } } /* Update chassis `ochassis' with values from `chassis'. The later one is not * expected to be part of a list! It will also be wiped from memory. */ static void lldpd_move_chassis(struct lldpd_chassis *ochassis, struct lldpd_chassis *chassis) { struct lldpd_mgmt *mgmt; int refcount = ochassis->c_refcount; int index = ochassis->c_index; struct ovs_list listcopy; /* We want to keep refcount, index and list stuff from the current chassis */ memcpy(&listcopy, &ochassis->list, sizeof listcopy); lldpd_chassis_cleanup(ochassis, 0); /* Make the copy. */ /* WARNING: this is a kludgy hack, we need in-place copy and cannot use * marshaling. */ memcpy(ochassis, chassis, sizeof *ochassis); list_init(&ochassis->c_mgmt); /* Copy of management addresses */ LIST_FOR_EACH_POP (mgmt, m_entries, &chassis->c_mgmt) { list_insert(&ochassis->c_mgmt, &mgmt->m_entries); } /* Restore saved values */ ochassis->c_refcount = refcount; ochassis->c_index = index; memcpy(&ochassis->list, &listcopy, sizeof ochassis->list); /* Get rid of the new chassis */ free(chassis); } static int lldpd_guess_type(struct lldpd *cfg, char *frame, int s) { int i; if (s < ETH_ADDR_LEN) { return -1; } for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (!cfg->g_protocols[i].enabled) { continue; } if (cfg->g_protocols[i].guess == NULL) { if (memcmp(frame, &cfg->g_protocols[i].mac, ETH_ADDR_LEN) == 0) { VLOG_DBG("guessed protocol is %s (from MAC address)", cfg->g_protocols[i].name); return cfg->g_protocols[i].mode; } } else { if (cfg->g_protocols[i].guess(frame, s)) { VLOG_DBG("guessed protocol is %s (from detector function)", cfg->g_protocols[i].name); return cfg->g_protocols[i].mode; } } } return -1; } static void lldpd_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hw) { size_t listsize, i; struct lldpd_chassis *chassis, *ochassis = NULL; struct lldpd_port *port, *oport; int guess = LLDPD_MODE_LLDP; struct eth_header eheader; int count = 0; bool found = false; VLOG_DBG("decode a received frame on %s size %d", hw->h_ifname,s); if (s < sizeof(struct eth_header) + 4) { /* Too short, just discard it */ return; } /* Decapsulate VLAN frames */ memcpy(&eheader, frame, sizeof eheader); if (eheader.eth_type == htons(ETH_TYPE_VLAN)) { /* VLAN decapsulation means to shift 4 bytes left the frame from * offset 2 * ETH_ADDR_LEN */ memmove(frame + 2 * ETH_ADDR_LEN, frame + 2 * ETH_ADDR_LEN + 4, s - 2 * ETH_ADDR_LEN); s -= 4; } LIST_FOR_EACH (oport, p_entries, &hw->h_rports) { if (oport->p_lastframe && oport->p_lastframe->size == s && !memcmp(oport->p_lastframe->frame, frame, s)) { /* Already received the same frame */ VLOG_DBG("duplicate frame, no need to decode"); oport->p_lastupdate = time_now(); return; } } guess = lldpd_guess_type(cfg, frame, s); VLOG_DBG("guessed %d enabled:%d", guess, cfg->g_protocols[0].enabled); for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (!cfg->g_protocols[i].enabled) { continue; } if (cfg->g_protocols[i].mode == guess) { VLOG_DBG("using decode function for %s protocol", cfg->g_protocols[i].name); if (cfg->g_protocols[i].decode(cfg, frame, s, hw, &chassis, &port) == -1) { VLOG_DBG("function for %s protocol did not " "decode this frame", cfg->g_protocols[i].name); return; } chassis->c_protocol = port->p_protocol = cfg->g_protocols[i].mode; break; } VLOG_DBG(" %"PRIuSIZE "mode:%d enabled:%d", i, cfg->g_protocols[i].mode, cfg->g_protocols[i].enabled); } if (cfg->g_protocols[i].mode == 0) { VLOG_DBG("unable to guess frame type on %s", hw->h_ifname); return; } /* Do we already have the same MSAP somewhere? */ VLOG_DBG("search for the same MSAP"); LIST_FOR_EACH (oport, p_entries, &hw->h_rports) { if (port->p_protocol == oport->p_protocol) { count++; if (port->p_id_subtype == oport->p_id_subtype && port->p_id_len == oport->p_id_len && !memcmp(port->p_id, oport->p_id, port->p_id_len) && chassis->c_id_subtype == oport->p_chassis->c_id_subtype && chassis->c_id_len == oport->p_chassis->c_id_len && !memcmp(chassis->c_id, oport->p_chassis->c_id, chassis->c_id_len)) { ochassis = oport->p_chassis; VLOG_DBG("MSAP is already known"); found = true; break; } } } if (!found) { oport = NULL; } /* Do we have room for a new MSAP? */ if (!oport && cfg->g_config.c_max_neighbors) { if (count == (cfg->g_config.c_max_neighbors - 1)) { VLOG_DBG("max neighbors %d reached for port %s, " "dropping any new ones silently", cfg->g_config.c_max_neighbors, hw->h_ifname); } else if (count > cfg->g_config.c_max_neighbors - 1) { VLOG_DBG("too many neighbors for port %s, drop this new one", hw->h_ifname); lldpd_port_cleanup(port, true); lldpd_chassis_cleanup(chassis, true); free(port); return; } } /* No, but do we already know the system? */ if (!oport) { bool found = false; VLOG_DBG("MSAP is unknown, search for the chassis"); LIST_FOR_EACH (ochassis, list, &cfg->g_chassis) { if ((chassis->c_protocol == ochassis->c_protocol) && (chassis->c_id_subtype == ochassis->c_id_subtype) && (chassis->c_id_len == ochassis->c_id_len) && (memcmp(chassis->c_id, ochassis->c_id, chassis->c_id_len) == 0)) { found = true; break; } } if (!found) { ochassis = NULL; } } if (oport) { /* The port is known, remove it before adding it back */ list_remove(&oport->p_entries); lldpd_port_cleanup(oport, 1); free(oport); } if (ochassis) { lldpd_move_chassis(ochassis, chassis); chassis = ochassis; } else { /* Chassis not known, add it */ VLOG_DBG("unknown chassis, add it to the list"); chassis->c_index = ++cfg->g_lastrid; chassis->c_refcount = 0; list_push_back(&cfg->g_chassis, &chassis->list); listsize = list_size(&cfg->g_chassis); VLOG_DBG("%"PRIuSIZE " different systems are known", listsize); } /* Add port */ port->p_lastchange = port->p_lastupdate = time_now(); port->p_lastframe = xmalloc(s + sizeof(struct lldpd_frame)); port->p_lastframe->size = s; memcpy(port->p_lastframe->frame, frame, s); list_insert(&hw->h_rports, &port->p_entries); port->p_chassis = chassis; port->p_chassis->c_refcount++; /* Several cases are possible : * 1. chassis is new, its refcount was 0. It is now attached * to this port, its refcount is 1. * 2. chassis already exists and was attached to another * port, we increase its refcount accordingly. * 3. chassis already exists and was attached to the same * port, its refcount was decreased with * lldpd_port_cleanup() and is now increased again. * * In all cases, if the port already existed, it has been * freed with lldpd_port_cleanup() and therefore, the refcount * of the chassis that was attached to it is decreased. */ i = list_size(&hw->h_rports); VLOG_DBG("%"PRIuSIZE " neighbors for %s", i, hw->h_ifname); if (!oport) { hw->h_insert_cnt++; } return; } static void lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hw, int mask) { struct lldpd_port *port; int protocols[LLDPD_MODE_MAX + 1]; char buffer[256]; bool found = false; int i, j, k; unsigned int min; VLOG_DBG("apply smart filter for port %s", hw->h_ifname); /* Compute the number of occurrences of each protocol */ for (i = 0; i <= LLDPD_MODE_MAX; i++) { protocols[i] = 0; } LIST_FOR_EACH (port, p_entries, &hw->h_rports) { protocols[port->p_protocol]++; } /* Turn the protocols[] array into an array of * enabled/disabled protocols. 1 means enabled, 0 * means disabled. */ min = (unsigned int) - 1; for (i = 0; i <= LLDPD_MODE_MAX; i++) { if (protocols[i] && (protocols[i] < min)) { min = protocols[i]; } } for (i = 0; i <= LLDPD_MODE_MAX; i++) { if (protocols[i] == min && !found) { /* If we need a tie breaker, we take the first protocol only */ if (cfg->g_config.c_smart & mask & (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO)) { found = true; } protocols[i] = 1; } else { protocols[i] = 0; } } /* We set the p_hidden flag to 1 if the protocol is disabled */ LIST_FOR_EACH (port, p_entries, &hw->h_rports) { if (mask == SMART_OUTGOING) { port->p_hidden_out = protocols[port->p_protocol] ? false : true; } else { port->p_hidden_in = protocols[port->p_protocol] ? false : true; } } /* If we want only one neighbor, we take the first one */ if (cfg->g_config.c_smart & mask & (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) { found = false; LIST_FOR_EACH (port, p_entries, &hw->h_rports) { if (mask == SMART_OUTGOING) { if (found) { port->p_hidden_out = true; } if (!port->p_hidden_out) { found = true; } } if (mask == SMART_INCOMING) { if (found) { port->p_hidden_in = true; } if (!port->p_hidden_in) { found = true; } } } } /* Print a debug message summarizing the operation */ for (i = 0; i <= LLDPD_MODE_MAX; i++) { protocols[i] = 0; } k = j = 0; LIST_FOR_EACH (port, p_entries, &hw->h_rports) { if (!((mask == SMART_OUTGOING && port->p_hidden_out) || (mask == SMART_INCOMING && port->p_hidden_in))) { k++; protocols[port->p_protocol] = 1; } j++; } buffer[0] = '\0'; for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) { if (strlen(buffer) + strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) { /* Unlikely, our buffer is too small */ memcpy(buffer + sizeof(buffer) - 4, "...", 4); break; } if (buffer[0]) { strncat(buffer, ", ", 2); strncat(buffer, cfg->g_protocols[i].name, strlen(cfg->g_protocols[i].name)); } } } VLOG_DBG("%s: %s: %d visible neighbors (out of %d)", hw->h_ifname, (mask == SMART_OUTGOING) ? "out filter" : "in filter", k, j); VLOG_DBG("%s: protocols: %s", hw->h_ifname, buffer[0] ? buffer : "(none)"); } /* Hide unwanted ports depending on smart mode set by the user */ static void lldpd_hide_all(struct lldpd *cfg) { struct lldpd_hardware *hw; if (!cfg->g_config.c_smart) { return; } VLOG_DBG("apply smart filter results on all ports"); LIST_FOR_EACH (hw, h_entries, &cfg->g_hardware) { if (cfg->g_config.c_smart & SMART_INCOMING_FILTER) { lldpd_hide_ports(cfg, hw, SMART_INCOMING); } if (cfg->g_config.c_smart & SMART_OUTGOING_FILTER) { lldpd_hide_ports(cfg, hw, SMART_OUTGOING); } } } void lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hw, char *buffer, size_t bufSize) { int n = bufSize; VLOG_DBG("receive a frame on %s", hw->h_ifname); if (cfg->g_config.c_paused) { VLOG_DBG("paused, ignore the frame on %s", hw->h_ifname); return; } hw->h_rx_cnt++; VLOG_DBG("decode received frame on %s h_rx_cnt=%" PRIu64, hw->h_ifname, hw->h_rx_cnt); lldpd_decode(cfg, buffer, n, hw); lldpd_hide_all(cfg); /* Immediatly hide */ } uint32_t lldpd_send(struct lldpd_hardware *hw, struct dp_packet *p) { struct lldpd *cfg = hw->h_cfg; struct lldpd_port *port; int i, sent = 0; int lldp_size = 0; if (cfg->g_config.c_receiveonly || cfg->g_config.c_paused) { return 0; } #ifndef _WIN32 if ((hw->h_flags & IFF_RUNNING) == 0) { return 0; } #endif for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (!cfg->g_protocols[i].enabled) { continue; } /* We send only if we have at least one remote system * speaking this protocol or if the protocol is forced */ if (cfg->g_protocols[i].enabled > 1) { if ((lldp_size = cfg->g_protocols[i].send(cfg, hw, p)) != -E2BIG) { sent++; continue; } else { VLOG_DBG("send PDU on %s failed E2BIG", hw->h_ifname); continue; } } LIST_FOR_EACH (port, p_entries, &hw->h_rports) { /* If this remote port is disabled, we don't consider it */ if (port->p_hidden_out) { continue; } if (port->p_protocol == cfg->g_protocols[i].mode) { VLOG_DBG("send PDU on %s with protocol %s", hw->h_ifname, cfg->g_protocols[i].name); lldp_size = cfg->g_protocols[i].send(cfg, hw, p); sent++; break; } } } if (!sent) { /* Nothing was sent for this port, let's speak the first * available protocol. */ for (i = 0; cfg->g_protocols[i].mode != 0; i++) { if (!cfg->g_protocols[i].enabled) { continue; } VLOG_DBG("fallback to protocol %s for %s", cfg->g_protocols[i].name, hw->h_ifname); lldp_size = cfg->g_protocols[i].send(cfg, hw, p); break; } if (cfg->g_protocols[i].mode == 0) { VLOG_WARN("no protocol enabled, dunno what to send"); } } return lldp_size; } openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldp-tlv.h0000644000000000000000000000013013534540071017252 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.949854664 openvswitch-2.5.9/lib/lldp/lldp-tlv.h0000644000175000017500000000702013534540071020741 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2012 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _LLDP_TLV_H #define _LLDP_TLV_H #define LLDP_MULTICAST_ADDR { \ { { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } } \ } #define LLDP_TLV_END 0 #define LLDP_TLV_CHASSIS_ID 1 #define LLDP_TLV_PORT_ID 2 #define LLDP_TLV_TTL 3 #define LLDP_TLV_PORT_DESCR 4 #define LLDP_TLV_SYSTEM_NAME 5 #define LLDP_TLV_SYSTEM_DESCR 6 #define LLDP_TLV_SYSTEM_CAP 7 #define LLDP_TLV_MGMT_ADDR 8 #define LLDP_TLV_ORG 127 #define LLDP_TLV_ORG_DOT1 {0x00, 0x80, 0xc2} #define LLDP_TLV_ORG_DOT3 {0x00, 0x12, 0x0f} #define LLDP_TLV_ORG_MED {0x00, 0x12, 0xbb} #define LLDP_TLV_ORG_AVAYA {0x00, 0x04, 0x0D} #define LLDP_TLV_ORG_DCBX {0x00, 0x1b, 0x21} #define LLDP_TLV_DOT1_PVID 1 #define LLDP_TLV_DOT1_PPVID 2 #define LLDP_TLV_DOT1_VLANNAME 3 #define LLDP_TLV_DOT1_PI 4 #define LLDP_TLV_DOT3_MAC 1 #define LLDP_TLV_DOT3_POWER 2 #define LLDP_TLV_DOT3_LA 3 #define LLDP_TLV_DOT3_MFS 4 #define LLDP_TLV_MED_CAP 1 #define LLDP_TLV_MED_POLICY 2 #define LLDP_TLV_MED_LOCATION 3 #define LLDP_TLV_MED_MDI 4 #define LLDP_TLV_MED_IV_HW 5 #define LLDP_TLV_MED_IV_FW 6 #define LLDP_TLV_MED_IV_SW 7 #define LLDP_TLV_MED_IV_SN 8 #define LLDP_TLV_MED_IV_MANUF 9 #define LLDP_TLV_MED_IV_MODEL 10 #define LLDP_TLV_MED_IV_ASSET 11 #define LLDP_TLV_AA_ELEMENT_SUBTYPE 0x0b #define LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE 0x0c #define LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH 32 #define LLDP_TLV_AA_ELEM_TYPE_UNKNOWN 1 #define LLDP_TLV_AA_ELEM_TYPE_SERVER 2 #define LLDP_TLV_AA_ELEM_TYPE_PROXY 3 #define LLDP_TLV_AA_ELEM_TYPE_SERV_NO_AUTH 4 #define LLDP_TLV_AA_ELEM_TYPE_PROXY_NO_AUTH 5 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_WIRELESS_ACCESS_POINT_TYPE1 6 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_WIRELESS_ACCESS_POINT_TYPE2 7 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_SWITCH 8 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_ROUTER 9 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_IP_PHONE 10 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_IP_CAMERA 11 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_IP_VIDEO 12 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_SECURITY_DEVICE 13 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_VIRTUAL_SWITCH 14 #define LLDP_TLV_AA_ELEM_TYPE_CLIENT_SERVER_ENDPOINT 15 #define LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE 0 #define LLDP_TLV_AA_ELEM_CONN_TYPE_MLT 1 #define LLDP_TLV_AA_ELEM_CONN_TYPE_SLT 2 #define LLDP_TLV_AA_ELEM_CONN_TYPE_SMLT 3 #endif openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldp.c0000644000000000000000000000013013534540071016442 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.949854664 openvswitch-2.5.9/lib/lldp/lldp.c0000644000175000017500000005641513534540071020145 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * Copyright (c) 2014 Michael Chapman * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "lldpd.h" #include #include #include #include #include #include #include "compiler.h" #include "dp-packet.h" #include "packets.h" VLOG_DEFINE_THIS_MODULE(lldp); /* This set of macro are used to parse packets. The current position in buffer * is `pos'. The length of the remaining space in buffer is `length'. There is * no check on boundaries. */ #define PEEK(type, func) \ ( \ memcpy(&type, pos, sizeof type), \ length -= sizeof type, \ pos += sizeof type, \ func(type) \ ) #define PEEK_UINT8 PEEK(types.f_uint8, ) #define PEEK_UINT16 PEEK(types.f_uint16, ntohs) #define PEEK_UINT32 PEEK(types.f_uint32, ntohl) #define PEEK_BYTES(value, bytes) \ do { \ memcpy(value, pos, bytes); \ length -= (bytes); \ pos += (bytes); \ } while (0) #define PEEK_DISCARD(bytes) \ do { \ length -= (bytes); \ pos += (bytes); \ } while (0) #define PEEK_DISCARD_UINT8 PEEK_DISCARD(1) #define PEEK_DISCARD_UINT16 PEEK_DISCARD(2) #define PEEK_DISCARD_UINT32 PEEK_DISCARD(3) #define PEEK_CMP(value, bytes) \ (length -= (bytes), \ pos += (bytes), \ memcmp(pos-bytes, value, bytes)) #define CHECK_TLV_SIZE(x, name) \ do { \ if (tlv_size < (x)) { \ VLOG_WARN(name " TLV too short received on %s", \ hardware->h_ifname); \ goto malformed; \ } \ } while (0) #define PEEK_SAVE(where) (where = pos, 1) static union { uint8_t f_uint8; ovs_be16 f_uint16; ovs_be32 f_uint32; } types; static int lldpd_af_to_lldp_proto(int af) { switch (af) { case LLDPD_AF_IPV4: return LLDP_MGMT_ADDR_IP4; case LLDPD_AF_IPV6: return LLDP_MGMT_ADDR_IP6; default: return LLDP_MGMT_ADDR_NONE; } } static int lldpd_af_from_lldp_proto(int proto) { switch (proto) { case LLDP_MGMT_ADDR_IP4: return LLDPD_AF_IPV4; case LLDP_MGMT_ADDR_IP6: return LLDPD_AF_IPV6; default: return LLDPD_AF_UNSPEC; } } static void lldp_tlv_put_u8(struct dp_packet *p, uint8_t x) { dp_packet_put(p, &x, sizeof x); } static void lldp_tlv_put_u16(struct dp_packet *p, uint16_t x) { ovs_be16 nx = htons(x); dp_packet_put(p, &nx, sizeof nx); } static void lldp_tlv_put_u32(struct dp_packet *p, uint32_t x) { ovs_be32 nx = htonl(x); dp_packet_put(p, &nx, sizeof nx); } static void lldp_tlv_put_isid(struct dp_packet *p, uint32_t isid) { uint8_t *data = dp_packet_put_uninit(p, 3); data[0] = isid >> 16; data[1] = isid >> 8; data[2] = isid; } static void lldp_tlv_start(struct dp_packet *p, uint8_t tlv, unsigned int *start) { *start = dp_packet_size(p); lldp_tlv_put_u16(p, tlv << 9); } static void lldp_tlv_end(struct dp_packet *p, unsigned int start) { ovs_be16 *tlv = dp_packet_at_assert(p, start, 2); *tlv |= htons((dp_packet_size(p) - (start + 2)) & 0x1ff); } int lldp_send(struct lldpd *global OVS_UNUSED, struct lldpd_hardware *hardware, struct dp_packet *p) { unsigned int orig_size = dp_packet_size(p); unsigned int start; struct lldpd_port *port; struct lldpd_chassis *chassis; struct lldpd_mgmt *mgmt; const uint8_t avaya[] = LLDP_TLV_ORG_AVAYA; struct lldpd_aa_isid_vlan_maps_tlv *vlan_isid_map; uint8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH]; port = &hardware->h_lport; chassis = port->p_chassis; /* The ethernet header is filled in elsewhere, we must save room for it. */ VLOG_DBG("LLDP PDU send to %s mtu %d incoming", hardware->h_ifname, hardware->h_mtu); /* Chassis ID */ lldp_tlv_start(p, LLDP_TLV_CHASSIS_ID, &start); lldp_tlv_put_u8(p, chassis->c_id_subtype); dp_packet_put(p, chassis->c_id, chassis->c_id_len); lldp_tlv_end(p, start); /* Port ID */ lldp_tlv_start(p, LLDP_TLV_PORT_ID, &start); lldp_tlv_put_u8(p, port->p_id_subtype); dp_packet_put(p, port->p_id, port->p_id_len); lldp_tlv_end(p, start); /* Time to live */ lldp_tlv_start(p, LLDP_TLV_TTL, &start); lldp_tlv_put_u16(p, chassis->c_ttl); lldp_tlv_end(p, start); /* System name */ if (chassis->c_name && *chassis->c_name != '\0') { lldp_tlv_start(p, LLDP_TLV_SYSTEM_NAME, &start); dp_packet_put(p, chassis->c_name, strlen(chassis->c_name)); lldp_tlv_end(p, start); } /* System description (skip it if empty) */ if (chassis->c_descr && *chassis->c_descr != '\0') { lldp_tlv_start(p, LLDP_TLV_SYSTEM_DESCR, &start); dp_packet_put(p, chassis->c_descr, strlen(chassis->c_descr)); lldp_tlv_end(p, start); } /* System capabilities */ lldp_tlv_start(p, LLDP_TLV_SYSTEM_CAP, &start); lldp_tlv_put_u16(p, chassis->c_cap_available); lldp_tlv_put_u16(p, chassis->c_cap_enabled); lldp_tlv_end(p, start); LIST_FOR_EACH (mgmt, m_entries, &chassis->c_mgmt) { lldp_tlv_start(p, LLDP_TLV_MGMT_ADDR, &start); lldp_tlv_put_u8(p, mgmt->m_addrsize + 1); lldp_tlv_put_u8(p, lldpd_af_to_lldp_proto(mgmt->m_family)); dp_packet_put(p, &mgmt->m_addr, mgmt->m_addrsize); /* Interface port type, OID */ if (mgmt->m_iface == 0) { /* We don't know the management interface */ lldp_tlv_put_u8(p, LLDP_MGMT_IFACE_UNKNOWN); lldp_tlv_put_u32(p, 0); } else { /* We have the index of the management interface */ lldp_tlv_put_u8(p, LLDP_MGMT_IFACE_IFINDEX); lldp_tlv_put_u32(p, mgmt->m_iface); } lldp_tlv_put_u8(p, 0); lldp_tlv_end(p, start); } /* Port description */ if (port->p_descr && *port->p_descr != '\0') { lldp_tlv_start(p, LLDP_TLV_PORT_DESCR, &start); dp_packet_put(p, port->p_descr, strlen(port->p_descr)); lldp_tlv_end(p, start); } /* Add Auto Attach tlvs V3.1 to packet. LLDP FA element v3.1 format: TLV Type[127] TLV Length[50 octets] Avaya OUI[00-04-0D] Subtype[11] 7 bits 9 bits 3 octets 1 octet HMAC-SHA Digest Element Type State Mgmt VLAN Rsvd System ID 32 octets 6 bits 6 bits 12 bits 1 octet 10 octets */ /* AA-ELEMENT */ if (port->p_element.type != 0) { u_int16_t aa_element_first_word = 0; u_int16_t aa_element_second_word = 0; u_int16_t aa_element_state = 0; u_int8_t aa_elem_sys_id_first_byte; u_int8_t aa_elem_sys_id_second_byte; /* Link VLAN Tagging Requirements (bit 1), * Automatic Provisioning Mode (bit 2/3) (left to right, 1 based) */ aa_element_state = ((port->p_element.vlan_tagging & 0x1) << 5) | ((port->p_element.auto_prov_mode & 0x3) << 3); /* Element first word should be first 6 most significant bits of * element type, bitwise OR that with the next 6 bits of the state, * bitwise OR with the first 4 bits of mgmt vlan id. * Element type should be LLDP_TLV_AA_ELEM_TYPE_VIRTUAL_SWITCH for * AA client */ aa_element_first_word = (port->p_element.type << 10) | (aa_element_state << 4) | ((port->p_element.mgmt_vlan & 0x0F00)>> 8); /* Element second type should be the first 8 most significant bits * of the remaining 8 bits of mgmt vlan id. */ aa_element_second_word = (port->p_element.mgmt_vlan & 0xFF) << 8; /* System id first byte should be first 3 most significant bits of * connecion type, bitwise OR that with the device state and bitwise * OR that with the first 2 most significant bitsof rsvd (10 bits). */ aa_elem_sys_id_first_byte = ((port->p_element.system_id.conn_type & 0x7) << 5) | ((port->p_element.system_id.rsvd >> 8) & 0x3); /* Second byte should just be the remaining 8 bits of 10 bits rsvd */ aa_elem_sys_id_second_byte = (port->p_element.system_id.rsvd & 0xFF); memset(msg_auth_digest, 0, sizeof msg_auth_digest); lldp_tlv_start(p, LLDP_TLV_ORG, &start); dp_packet_put(p, avaya, sizeof avaya); lldp_tlv_put_u8(p, LLDP_TLV_AA_ELEMENT_SUBTYPE); dp_packet_put(p, msg_auth_digest, sizeof msg_auth_digest); lldp_tlv_put_u16(p, aa_element_first_word); lldp_tlv_put_u16(p, aa_element_second_word); dp_packet_put(p, &port->p_element.system_id.system_mac, sizeof port->p_element.system_id.system_mac); lldp_tlv_put_u8(p, aa_elem_sys_id_first_byte); lldp_tlv_put_u8(p, aa_elem_sys_id_second_byte); dp_packet_put(p, &port->p_element.system_id.rsvd2, sizeof port->p_element.system_id.rsvd2); lldp_tlv_end(p, start); } if (!list_is_empty(&port->p_isid_vlan_maps)) { memset(msg_auth_digest, 0, sizeof msg_auth_digest); lldp_tlv_start(p, LLDP_TLV_ORG, &start); dp_packet_put(p, avaya, sizeof avaya); lldp_tlv_put_u8(p, LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE); dp_packet_put(p, msg_auth_digest, sizeof msg_auth_digest); LIST_FOR_EACH (vlan_isid_map, m_entries, &hardware->h_lport.p_isid_vlan_maps) { u_int16_t status_vlan_word; status_vlan_word = (vlan_isid_map->isid_vlan_data.status << 12) | vlan_isid_map->isid_vlan_data.vlan; lldp_tlv_put_u16(p, status_vlan_word); lldp_tlv_put_isid(p, vlan_isid_map->isid_vlan_data.isid); } lldp_tlv_end(p, start); } /* END */ lldp_tlv_start(p, LLDP_TLV_END, &start); lldp_tlv_end(p, start); hardware->h_tx_cnt++; const char *lldp = dp_packet_at_assert(p, orig_size, 0); unsigned int lldp_len = dp_packet_size(p) - orig_size; if (!hardware->h_lport.p_lastframe || hardware->h_lport.p_lastframe->size != lldp_len || memcmp(hardware->h_lport.p_lastframe->frame, lldp, lldp_len)) { struct lldpd_frame *frame = xmalloc(sizeof *frame + lldp_len); frame->size = lldp_len; memcpy(frame->frame, lldp, lldp_len); free(hardware->h_lport.p_lastframe); hardware->h_lport.p_lastframe = frame; hardware->h_lport.p_lastchange = time(NULL); } return dp_packet_size(p); } int lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, struct lldpd_hardware *hardware, struct lldpd_chassis **newchassis, struct lldpd_port **newport) { struct lldpd_chassis *chassis; struct lldpd_port *port; const struct eth_addr lldpaddr = LLDP_MULTICAST_ADDR; const char dot1[] = LLDP_TLV_ORG_DOT1; const char dot3[] = LLDP_TLV_ORG_DOT3; const char med[] = LLDP_TLV_ORG_MED; const char avaya_oid[] = LLDP_TLV_ORG_AVAYA; const char dcbx[] = LLDP_TLV_ORG_DCBX; char orgid[3]; int length, af; bool gotend = false; bool ttl_received = false; int tlv_size, tlv_type, tlv_subtype; u_int8_t *pos, *tlv; void *b; struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL; u_int8_t msg_auth_digest[LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH]; struct lldpd_mgmt *mgmt; u_int8_t addr_str_length, addr_str_buffer[32]; u_int8_t addr_family, addr_length, *addr_ptr, iface_subtype; u_int32_t iface_number, iface; VLOG_DBG("receive LLDP PDU on %s", hardware->h_ifname); chassis = xzalloc(sizeof *chassis); list_init(&chassis->c_mgmt); port = xzalloc(sizeof *port); list_init(&port->p_isid_vlan_maps); length = s; pos = (u_int8_t*) frame; if (length < 2 * ETH_ADDR_LEN + sizeof(u_int16_t)) { VLOG_WARN("too short frame received on %s", hardware->h_ifname); goto malformed; } if (PEEK_CMP(&lldpaddr, ETH_ADDR_LEN) != 0) { VLOG_INFO("frame not targeted at LLDP multicast address " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(ETH_ADDR_LEN); /* Skip source address */ if (PEEK_UINT16 != ETHERTYPE_LLDP) { VLOG_INFO("non LLDP frame received on %s", hardware->h_ifname); goto malformed; } while (length && !gotend) { if (length < 2) { VLOG_WARN("tlv header too short received on %s", hardware->h_ifname); goto malformed; } tlv_size = PEEK_UINT16; tlv_type = tlv_size >> 9; tlv_size = tlv_size & 0x1ff; (void) PEEK_SAVE(tlv); if (length < tlv_size) { VLOG_WARN("frame too short for tlv received on %s", hardware->h_ifname); goto malformed; } switch (tlv_type) { case LLDP_TLV_END: if (tlv_size != 0) { VLOG_WARN("lldp end received with size not null on %s", hardware->h_ifname); goto malformed; } if (length) { VLOG_DBG("extra data after lldp end on %s", hardware->h_ifname); } gotend = true; break; case LLDP_TLV_CHASSIS_ID: case LLDP_TLV_PORT_ID: CHECK_TLV_SIZE(2, "Port Id"); tlv_subtype = PEEK_UINT8; if (tlv_subtype == 0 || tlv_subtype > 7) { VLOG_WARN("unknown subtype for tlv id received on %s", hardware->h_ifname); goto malformed; } b = xzalloc(tlv_size - 1); PEEK_BYTES(b, tlv_size - 1); if (tlv_type == LLDP_TLV_PORT_ID) { port->p_id_subtype = tlv_subtype; port->p_id = b; port->p_id_len = tlv_size - 1; } else { chassis->c_id_subtype = tlv_subtype; chassis->c_id = b; chassis->c_id_len = tlv_size - 1; } break; case LLDP_TLV_TTL: CHECK_TLV_SIZE(2, "TTL"); chassis->c_ttl = PEEK_UINT16; ttl_received = true; break; case LLDP_TLV_PORT_DESCR: case LLDP_TLV_SYSTEM_NAME: case LLDP_TLV_SYSTEM_DESCR: if (tlv_size < 1) { VLOG_DBG("empty tlv received on %s", hardware->h_ifname); break; } b = xzalloc(tlv_size + 1); PEEK_BYTES(b, tlv_size); if (tlv_type == LLDP_TLV_PORT_DESCR) { port->p_descr = b; } else if (tlv_type == LLDP_TLV_SYSTEM_NAME) { chassis->c_name = b; } else { chassis->c_descr = b; } break; case LLDP_TLV_SYSTEM_CAP: CHECK_TLV_SIZE(4, "System capabilities"); chassis->c_cap_available = PEEK_UINT16; chassis->c_cap_enabled = PEEK_UINT16; break; case LLDP_TLV_MGMT_ADDR: CHECK_TLV_SIZE(1, "Management address"); addr_str_length = PEEK_UINT8; CHECK_TLV_SIZE(1 + addr_str_length, "Management address"); PEEK_BYTES(addr_str_buffer, addr_str_length); addr_length = addr_str_length - 1; addr_family = addr_str_buffer[0]; addr_ptr = &addr_str_buffer[1]; CHECK_TLV_SIZE(1 + addr_str_length + 5, "Management address"); iface_subtype = PEEK_UINT8; iface_number = PEEK_UINT32; af = lldpd_af_from_lldp_proto(addr_family); if (af == LLDPD_AF_UNSPEC) { break; } iface = iface_subtype == LLDP_MGMT_IFACE_IFINDEX ? iface_number : 0; mgmt = lldpd_alloc_mgmt(af, addr_ptr, addr_length, iface); if (mgmt == NULL) { VLOG_WARN("unable to allocate memory for management address"); goto malformed; } list_push_back(&chassis->c_mgmt, &mgmt->m_entries); break; case LLDP_TLV_ORG: CHECK_TLV_SIZE(4, "Organisational"); PEEK_BYTES(orgid, sizeof orgid); tlv_subtype = PEEK_UINT8; if (memcmp(dot1, orgid, sizeof orgid) == 0) { hardware->h_rx_unrecognized_cnt++; } else if (memcmp(dot3, orgid, sizeof orgid) == 0) { hardware->h_rx_unrecognized_cnt++; } else if (memcmp(med, orgid, sizeof orgid) == 0) { /* LLDP-MED */ hardware->h_rx_unrecognized_cnt++; } else if (memcmp(avaya_oid, orgid, sizeof orgid) == 0) { u_int32_t aa_element_dword; u_int16_t aa_system_id_word; u_int16_t aa_status_vlan_word; u_int8_t aa_element_state; unsigned short num_mappings; switch(tlv_subtype) { case LLDP_TLV_AA_ELEMENT_SUBTYPE: PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); aa_element_dword = PEEK_UINT32; /* Type is first 6 most-significant bits of * aa_element_dword */ port->p_element.type = aa_element_dword >> 26; /* State is 6 most significant bits of aa_element_dword */ aa_element_state = (aa_element_dword >> 20) & 0x3F; /* vlan tagging requirement is the bit 1(left to right) * of the 6 bits state (1 based) */ port->p_element.vlan_tagging = (aa_element_state >> 5) & 0x1; /* Automatic provision mode is the bit 2/3(left to right) * of the 6 bits state (1 based) */ port->p_element.auto_prov_mode = (aa_element_state >> 3) & 0x3; /* mgmt_vlan is the 12 bits of aa_element_dword from * bit 12 */ port->p_element.mgmt_vlan = (aa_element_dword >> 8) & 0xFFF; VLOG_INFO("Element type: %X, vlan tagging %X, " "auto prov mode %x, Mgmt vlan: %X", port->p_element.type, port->p_element.vlan_tagging, port->p_element.auto_prov_mode, port->p_element.mgmt_vlan); PEEK_BYTES(&port->p_element.system_id.system_mac, sizeof port->p_element.system_id.system_mac); VLOG_INFO("System mac: "ETH_ADDR_FMT, ETH_ADDR_ARGS(port->p_element.system_id.system_mac)); aa_system_id_word = PEEK_UINT16; port->p_element.system_id.conn_type = aa_system_id_word >> 13; port->p_element.system_id.rsvd = aa_system_id_word & 0x03FF; PEEK_BYTES(&port->p_element.system_id.rsvd2, sizeof port->p_element.system_id.rsvd2); break; case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE: PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); /* Subtract off tlv type and length (2Bytes) + OUI (3B) + * Subtype (1B) + MSG DIGEST (32B). */ num_mappings = tlv_size - 4 - LLDP_TLV_AA_ISID_VLAN_DIGEST_LENGTH; if (num_mappings % 5 != 0) { VLOG_INFO("malformed vlan-isid mappings tlv received"); goto malformed; } num_mappings /= 5; /* Each mapping is 5 Bytes */ for(; num_mappings > 0; num_mappings--) { uint8_t isid[3]; isid_vlan_map = xzalloc(sizeof *isid_vlan_map); aa_status_vlan_word = PEEK_UINT16; /* Status is first 4 most-significant bits. */ isid_vlan_map->isid_vlan_data.status = aa_status_vlan_word >> 12; /* Vlan is last 12 bits */ isid_vlan_map->isid_vlan_data.vlan = aa_status_vlan_word & 0x0FFF; PEEK_BYTES(isid, 3); isid_vlan_map->isid_vlan_data.isid = (isid[0] << 16) | (isid[1] << 8) | isid[2]; list_push_back(&port->p_isid_vlan_maps, &isid_vlan_map->m_entries); isid_vlan_map = NULL; } break; default: hardware->h_rx_unrecognized_cnt++; VLOG_INFO("Unrecogised tlv subtype received"); break; } } else if (memcmp(dcbx, orgid, sizeof orgid) == 0) { VLOG_DBG("unsupported DCBX tlv received on %s " "- ignore", hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } else { VLOG_INFO("unknown org tlv [%02x:%02x:%02x] received " "on %s", orgid[0], orgid[1], orgid[2], hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } break; default: VLOG_WARN("unknown tlv (%d) received on %s", tlv_type, hardware->h_ifname); goto malformed; } if (pos > tlv + tlv_size) { VLOG_WARN("BUG: already past TLV!"); goto malformed; } PEEK_DISCARD(tlv + tlv_size - pos); } /* Some random check */ if (!chassis->c_id || !port->p_id || !ttl_received || !gotend) { VLOG_WARN("some mandatory tlv are missing for frame received " "on %s", hardware->h_ifname); goto malformed; } *newchassis = chassis; *newport = port; return 1; malformed: lldpd_chassis_cleanup(chassis, true); lldpd_port_cleanup(port, true); free(port); return -1; } openvswitch-2.5.9/lib/lldp/PaxHeaders.82075/lldpd-structs.c0000644000000000000000000000013013534540071020313 xustar0028 mtime=1567801401.4056812 30 atime=1567801402.077686135 30 ctime=1567801424.953854694 openvswitch-2.5.9/lib/lldp/lldpd-structs.c0000644000175000017500000001043213534540071022003 0ustar00jpettitjpettit00000000000000/* -*- mode: c; c-file-style: "openbsd" -*- */ /* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2008 Vincent Bernat * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "lldpd-structs.h" #include #include #include "lldpd.h" #include "timeval.h" VLOG_DEFINE_THIS_MODULE(lldpd_structs); void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *chassis) { struct lldpd_mgmt *mgmt; VLOG_DBG("cleanup management addresses for chassis %s", chassis->c_name ? chassis->c_name : "(unknown)"); LIST_FOR_EACH_POP (mgmt, m_entries, &chassis->c_mgmt) { free(mgmt); } list_init(&chassis->c_mgmt); } void lldpd_chassis_cleanup(struct lldpd_chassis *chassis, bool all) { lldpd_chassis_mgmt_cleanup(chassis); VLOG_DBG("cleanup chassis %s", chassis->c_name ? chassis->c_name : "(unknown)"); free(chassis->c_id); free(chassis->c_name); free(chassis->c_descr); if (all) { free(chassis); } } /* Cleanup a remote port. The before last argument, `expire` is a function that * should be called when a remote port is removed. If the last argument is * true, all remote ports are removed. */ void lldpd_remote_cleanup(struct lldpd_hardware *hw, void(*expire)(struct lldpd_hardware *, struct lldpd_port *), bool all) { struct lldpd_port *port, *port_next; time_t now = time_now(); VLOG_DBG("cleanup remote port on %s", hw->h_ifname); LIST_FOR_EACH_SAFE (port, port_next, p_entries, &hw->h_rports) { bool del = all; if (!all && expire && (now >= port->p_lastupdate + port->p_chassis->c_ttl)) { hw->h_ageout_cnt++; hw->h_delete_cnt++; del = true; } if (del) { if (expire) { expire(hw, port); } if (!all) { list_remove(&port->p_entries); } lldpd_port_cleanup(port, true); free(port); } } if (all) { list_init(&hw->h_rports); } } /* Cleanup the auto-attach mappings attached to port. */ static void lldpd_aa_maps_cleanup(struct lldpd_port *port) { struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map = NULL; struct lldpd_aa_isid_vlan_maps_tlv *isid_vlan_map_next = NULL; if (!list_is_empty(&port->p_isid_vlan_maps)) { LIST_FOR_EACH_SAFE (isid_vlan_map, isid_vlan_map_next, m_entries, &port->p_isid_vlan_maps) { list_remove(&isid_vlan_map->m_entries); free(isid_vlan_map); } list_init(&port->p_isid_vlan_maps); } } /* If `all' is true, clear all information, including information that are not refreshed periodically. Port should be freed manually. */ void lldpd_port_cleanup(struct lldpd_port *port, bool all) { /* We set these to NULL so we don't free wrong memory */ free(port->p_id); port->p_id = NULL; free(port->p_descr); port->p_descr = NULL; /* Cleanup auto-attach mappings */ lldpd_aa_maps_cleanup(port); if (all) { free(port->p_lastframe); /* Chassis may not have been attributed, yet.*/ if (port->p_chassis) { port->p_chassis->c_refcount--; port->p_chassis = NULL; } } } void lldpd_config_cleanup(struct lldpd_config *config) { VLOG_DBG("general configuration cleanup"); free(config->c_mgmt_pattern); free(config->c_cid_pattern); free(config->c_iface_pattern); free(config->c_platform); free(config->c_description); } openvswitch-2.5.9/lib/PaxHeaders.82075/dpif-provider.h0000644000000000000000000000013213534540071017335 xustar0030 mtime=1567801401.377680993 30 atime=1567801402.073686105 30 ctime=1567801424.709852895 openvswitch-2.5.9/lib/dpif-provider.h0000644000175000017500000004573113534540071021035 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DPIF_PROVIDER_H #define DPIF_PROVIDER_H 1 /* Provider interface to dpifs, which provide an interface to an Open vSwitch * datapath. A datapath is a collection of physical or virtual ports that are * exposed over OpenFlow as a single switch. Datapaths and the collections of * ports that they contain may be fixed or dynamic. */ #include "openflow/openflow.h" #include "dpif.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif /* Open vSwitch datapath interface. * * This structure should be treated as opaque by dpif implementations. */ struct dpif { const struct dpif_class *dpif_class; char *base_name; char *full_name; uint8_t netflow_engine_type; uint8_t netflow_engine_id; }; void dpif_init(struct dpif *, const struct dpif_class *, const char *name, uint8_t netflow_engine_type, uint8_t netflow_engine_id); void dpif_uninit(struct dpif *dpif, bool close); static inline void dpif_assert_class(const struct dpif *dpif, const struct dpif_class *dpif_class) { ovs_assert(dpif->dpif_class == dpif_class); } struct dpif_flow_dump { struct dpif *dpif; bool terse; /* If true, key/mask/actions may be omitted. */ }; static inline void dpif_flow_dump_init(struct dpif_flow_dump *dump, const struct dpif *dpif) { dump->dpif = CONST_CAST(struct dpif *, dpif); } struct dpif_flow_dump_thread { struct dpif *dpif; }; static inline void dpif_flow_dump_thread_init(struct dpif_flow_dump_thread *thread, struct dpif_flow_dump *dump) { thread->dpif = dump->dpif; } struct ct_dpif_dump_state; struct ct_dpif_entry; /* Datapath interface class structure, to be defined by each implementation of * a datapath interface. * * These functions return 0 if successful or a positive errno value on failure, * except where otherwise noted. * * These functions are expected to execute synchronously, that is, to block as * necessary to obtain a result. Thus, they may not return EAGAIN or * EWOULDBLOCK or EINPROGRESS. We may relax this requirement in the future if * and when we encounter performance problems. */ struct dpif_class { /* Type of dpif in this class, e.g. "system", "netdev", etc. * * One of the providers should supply a "system" type, since this is * the type assumed if no type is specified when opening a dpif. */ const char *type; /* Called when the dpif provider is registered, typically at program * startup. Returning an error from this function will prevent any * datapath with this class from being created. * * This function may be set to null if a datapath class needs no * initialization at registration time. */ int (*init)(void); /* Enumerates the names of all known created datapaths (of class * 'dpif_class'), if possible, into 'all_dps'. The caller has already * initialized 'all_dps' and other dpif classes might already have added * names to it. * * This is used by the vswitch at startup, so that it can delete any * datapaths that are not configured. * * Some kinds of datapaths might not be practically enumerable, in which * case this function may be a null pointer. */ int (*enumerate)(struct sset *all_dps, const struct dpif_class *dpif_class); /* Returns the type to pass to netdev_open() when a dpif of class * 'dpif_class' has a port of type 'type', for a few special cases * when a netdev type differs from a port type. For example, when * using the userspace datapath, a port of type "internal" needs to * be opened as "tap". * * Returns either 'type' itself or a string literal, which must not * be freed. */ const char *(*port_open_type)(const struct dpif_class *dpif_class, const char *type); /* Attempts to open an existing dpif called 'name', if 'create' is false, * or to open an existing dpif or create a new one, if 'create' is true. * * 'dpif_class' is the class of dpif to open. * * If successful, stores a pointer to the new dpif in '*dpifp', which must * have class 'dpif_class'. On failure there are no requirements on what * is stored in '*dpifp'. */ int (*open)(const struct dpif_class *dpif_class, const char *name, bool create, struct dpif **dpifp); /* Closes 'dpif' and frees associated memory. */ void (*close)(struct dpif *dpif); /* Attempts to destroy the dpif underlying 'dpif'. * * If successful, 'dpif' will not be used again except as an argument for * the 'close' member function. */ int (*destroy)(struct dpif *dpif); /* Performs periodic work needed by 'dpif', if any is necessary. * Returns true if need to revalidate. */ bool (*run)(struct dpif *dpif); /* Arranges for poll_block() to wake up if the "run" member function needs * to be called for 'dpif'. */ void (*wait)(struct dpif *dpif); /* Retrieves statistics for 'dpif' into 'stats'. */ int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats); /* Adds 'netdev' as a new port in 'dpif'. If '*port_no' is not * UINT32_MAX, attempts to use that as the port's port number. * * If port is successfully added, sets '*port_no' to the new port's * port number. Returns EBUSY if caller attempted to choose a port * number, and it was in use. */ int (*port_add)(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_no); /* Removes port numbered 'port_no' from 'dpif'. */ int (*port_del)(struct dpif *dpif, odp_port_t port_no); /* Queries 'dpif' for a port with the given 'port_no' or 'devname'. * If 'port' is not null, stores information about the port into * '*port' if successful. * * If 'port' is not null, the caller takes ownership of data in * 'port' and must free it with dpif_port_destroy() when it is no * longer needed. */ int (*port_query_by_number)(const struct dpif *dpif, odp_port_t port_no, struct dpif_port *port); int (*port_query_by_name)(const struct dpif *dpif, const char *devname, struct dpif_port *port); /* Returns the Netlink PID value to supply in OVS_ACTION_ATTR_USERSPACE * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in * flows whose packets arrived on port 'port_no'. In the case where the * provider allocates multiple Netlink PIDs to a single port, it may use * 'hash' to spread load among them. The caller need not use a particular * hash function; a 5-tuple hash is suitable. * * (The datapath implementation might use some different hash function for * distributing packets received via flow misses among PIDs. This means * that packets received via flow misses might be reordered relative to * packets received via userspace actions. This is not ordinarily a * problem.) * * A 'port_no' of UINT32_MAX should be treated as a special case. The * implementation should return a reserved PID, not allocated to any port, * that the client may use for special purposes. * * The return value only needs to be meaningful when DPIF_UC_ACTION has * been enabled in the 'dpif''s listen mask, and it is allowed to change * when DPIF_UC_ACTION is disabled and then re-enabled. * * A dpif provider that doesn't have meaningful Netlink PIDs can use NULL * for this function. This is equivalent to always returning 0. */ uint32_t (*port_get_pid)(const struct dpif *dpif, odp_port_t port_no, uint32_t hash); /* Attempts to begin dumping the ports in a dpif. On success, returns 0 * and initializes '*statep' with any data needed for iteration. On * failure, returns a positive errno value. */ int (*port_dump_start)(const struct dpif *dpif, void **statep); /* Attempts to retrieve another port from 'dpif' for 'state', which was * initialized by a successful call to the 'port_dump_start' function for * 'dpif'. On success, stores a new dpif_port into 'port' and returns 0. * Returns EOF if the end of the port table has been reached, or a positive * errno value on error. This function will not be called again once it * returns nonzero once for a given iteration (but the 'port_dump_done' * function will be called afterward). * * The dpif provider retains ownership of the data stored in 'port'. It * must remain valid until at least the next call to 'port_dump_next' or * 'port_dump_done' for 'state'. */ int (*port_dump_next)(const struct dpif *dpif, void *state, struct dpif_port *port); /* Releases resources from 'dpif' for 'state', which was initialized by a * successful call to the 'port_dump_start' function for 'dpif'. */ int (*port_dump_done)(const struct dpif *dpif, void *state); /* Polls for changes in the set of ports in 'dpif'. If the set of ports in * 'dpif' has changed, then this function should do one of the * following: * * - Preferably: store the name of the device that was added to or deleted * from 'dpif' in '*devnamep' and return 0. The caller is responsible * for freeing '*devnamep' (with free()) when it no longer needs it. * * - Alternatively: return ENOBUFS, without indicating the device that was * added or deleted. * * Occasional 'false positives', in which the function returns 0 while * indicating a device that was not actually added or deleted or returns * ENOBUFS without any change, are acceptable. * * If the set of ports in 'dpif' has not changed, returns EAGAIN. May also * return other positive errno values to indicate that something has gone * wrong. */ int (*port_poll)(const struct dpif *dpif, char **devnamep); /* Arranges for the poll loop to wake up when 'port_poll' will return a * value other than EAGAIN. */ void (*port_poll_wait)(const struct dpif *dpif); /* Deletes all flows from 'dpif' and clears all of its queues of received * packets. */ int (*flow_flush)(struct dpif *dpif); /* Flow dumping interface. * * This is the back-end for the flow dumping interface described in * dpif.h. Please read the comments there first, because this code * closely follows it. * * 'flow_dump_create' and 'flow_dump_thread_create' must always return an * initialized and usable data structure and defer error return until * flow_dump_destroy(). This hasn't been a problem for the dpifs that * exist so far. * * 'flow_dump_create' and 'flow_dump_thread_create' must initialize the * structures that they return with dpif_flow_dump_init() and * dpif_flow_dump_thread_init(), respectively. * * If 'terse' is true, then only UID and statistics will * be returned in the dump. Otherwise, all fields will be returned. */ struct dpif_flow_dump *(*flow_dump_create)(const struct dpif *dpif, bool terse); int (*flow_dump_destroy)(struct dpif_flow_dump *dump); struct dpif_flow_dump_thread *(*flow_dump_thread_create)( struct dpif_flow_dump *dump); void (*flow_dump_thread_destroy)(struct dpif_flow_dump_thread *thread); int (*flow_dump_next)(struct dpif_flow_dump_thread *thread, struct dpif_flow *flows, int max_flows); /* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order * in which they are specified, placing each operation's results in the * "output" members documented in comments and the 'error' member of each * dpif_op. */ void (*operate)(struct dpif *dpif, struct dpif_op **ops, size_t n_ops); /* Enables or disables receiving packets with dpif_recv() for 'dpif'. * Turning packet receive off and then back on is allowed to change Netlink * PID assignments (see ->port_get_pid()). The client is responsible for * updating flows as necessary if it does this. */ int (*recv_set)(struct dpif *dpif, bool enable); /* Refreshes the poll loops and Netlink sockets associated to each port, * when the number of upcall handlers (upcall receiving thread) is changed * to 'n_handlers' and receiving packets for 'dpif' is enabled by * recv_set(). * * Since multiple upcall handlers can read upcalls simultaneously from * 'dpif', each port can have multiple Netlink sockets, one per upcall * handler. So, handlers_set() is responsible for the following tasks: * * When receiving upcall is enabled, extends or creates the * configuration to support: * * - 'n_handlers' Netlink sockets for each port. * * - 'n_handlers' poll loops, one for each upcall handler. * * - registering the Netlink sockets for the same upcall handler to * the corresponding poll loop. * */ int (*handlers_set)(struct dpif *dpif, uint32_t n_handlers); /* If 'dpif' creates its own I/O polling threads, refreshes poll threads * configuration. 'n_rxqs' configures the number of rx_queues, which * are distributed among threads. 'cmask' configures the cpu mask * for setting the polling threads' cpu affinity. */ int (*poll_threads_set)(struct dpif *dpif, unsigned int n_rxqs, const char *cmask); /* Translates OpenFlow queue ID 'queue_id' (in host byte order) into a * priority value used for setting packet priority. */ int (*queue_to_priority)(const struct dpif *dpif, uint32_t queue_id, uint32_t *priority); /* Polls for an upcall from 'dpif' for an upcall handler. Since there * can be multiple poll loops (see ->handlers_set()), 'handler_id' is * needed as index to identify the corresponding poll loop. If * successful, stores the upcall into '*upcall', using 'buf' for * storage. Should only be called if 'recv_set' has been used to enable * receiving packets from 'dpif'. * * The implementation should point 'upcall->key' and 'upcall->userdata' * (if any) into data in the caller-provided 'buf'. The implementation may * also use 'buf' for storing the data of 'upcall->packet'. If necessary * to make room, the implementation may reallocate the data in 'buf'. * * The caller owns the data of 'upcall->packet' and may modify it. If * packet's headroom is exhausted as it is manipulated, 'upcall->packet' * will be reallocated. This requires the data of 'upcall->packet' to be * released with ofpbuf_uninit() before 'upcall' is destroyed. However, * when an error is returned, the 'upcall->packet' may be uninitialized * and should not be released. * * This function must not block. If no upcall is pending when it is * called, it should return EAGAIN without blocking. */ int (*recv)(struct dpif *dpif, uint32_t handler_id, struct dpif_upcall *upcall, struct ofpbuf *buf); /* Arranges for the poll loop for an upcall handler to wake up when 'dpif' * has a message queued to be received with the recv member functions. * Since there can be multiple poll loops (see ->handlers_set()), * 'handler_id' is needed as index to identify the corresponding poll loop. * */ void (*recv_wait)(struct dpif *dpif, uint32_t handler_id); /* Throws away any queued upcalls that 'dpif' currently has ready to * return. */ void (*recv_purge)(struct dpif *dpif); /* When 'dpif' is about to purge the datapath, the higher layer may want * to be notified so that it could try reacting accordingly (e.g. grabbing * all flow stats before they are gone). * * Registers an upcall callback function with 'dpif'. This is only used * if 'dpif' needs to notify the purging of datapath. 'aux' is passed to * the callback on invocation. */ void (*register_dp_purge_cb)(struct dpif *, dp_purge_callback *, void *aux); /* For datapaths that run in userspace (i.e. dpif-netdev), threads polling * for incoming packets can directly call upcall functions instead of * offloading packet processing to separate handler threads. Datapaths * that directly call upcall functions should use the functions below to * to register an upcall function and enable / disable upcalls. * * Registers an upcall callback function with 'dpif'. This is only used * if 'dpif' directly executes upcall functions. 'aux' is passed to the * callback on invocation. */ void (*register_upcall_cb)(struct dpif *, upcall_callback *, void *aux); /* Enables upcalls if 'dpif' directly executes upcall functions. */ void (*enable_upcall)(struct dpif *); /* Disables upcalls if 'dpif' directly executes upcall functions. */ void (*disable_upcall)(struct dpif *); /* Get datapath version. Caller is responsible for freeing the string * returned. */ char *(*get_datapath_version)(void); /* Conntrack entry dumping interface. * * These functions are used by ct-dpif.c to provide a datapath-agnostic * dumping interface to the connection trackes provided by the * datapaths. * * ct_dump_start() should put in '*state' a pointer to a newly allocated * stucture that will be passed by the caller to ct_dump_next() and * ct_dump_done(). If 'zone' is not NULL, only the entries in '*zone' * should be dumped. * * ct_dump_next() should fill 'entry' with information from a connection * and prepare to dump the next one on a subsequest invocation. * * ct_dump_done should perform any cleanup necessary (including * deallocating the 'state' structure, if applicable). */ int (*ct_dump_start)(struct dpif *, struct ct_dpif_dump_state **state, const uint16_t *zone); int (*ct_dump_next)(struct dpif *, struct ct_dpif_dump_state *, struct ct_dpif_entry *entry); int (*ct_dump_done)(struct dpif *, struct ct_dpif_dump_state *state); /* Flushes the connection tracking tables. If 'zone' is not NULL, * only deletes connections in '*zone'. */ int (*ct_flush)(struct dpif *, const uint16_t *zone); }; extern const struct dpif_class dpif_netlink_class; extern const struct dpif_class dpif_netdev_class; #ifdef __cplusplus } #endif #endif /* dpif-provider.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpif-netdev.h0000644000000000000000000000013213534540071016770 xustar0030 mtime=1567801401.365680906 30 atime=1567801402.073686105 30 ctime=1567801424.709852895 openvswitch-2.5.9/lib/dpif-netdev.h0000644000175000017500000000253713534540071020465 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DPIF_NETDEV_H #define DPIF_NETDEV_H 1 #include #include #include #include "dpif.h" #include "openvswitch/types.h" #include "dp-packet.h" #include "packets.h" #ifdef __cplusplus extern "C" { #endif /* Enough headroom to add a vlan tag, plus an extra 2 bytes to allow IP * headers to be aligned on a 4-byte boundary. */ enum { DP_NETDEV_HEADROOM = 2 + VLAN_HEADER_LEN }; static inline void dp_packet_pad(struct dp_packet *p) { if (dp_packet_size(p) < ETH_TOTAL_MIN) { dp_packet_put_zeros(p, ETH_TOTAL_MIN - dp_packet_size(p)); } } bool dpif_is_netdev(const struct dpif *); #define NR_QUEUE 1 #define NR_PMD_THREADS 1 #ifdef __cplusplus } #endif #endif /* netdev.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/rstp-common.h0000644000000000000000000000013213534540071017041 xustar0030 mtime=1567801401.581682492 30 atime=1567801402.093686252 30 ctime=1567801424.865854045 openvswitch-2.5.9/lib/rstp-common.h0000644000175000017500000010075313534540071020535 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011-2014 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) common header file. * * Authors: * Martino Fornasa * Daniele Venturino * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. * */ #ifndef RSTP_COMMON_H #define RSTP_COMMON_H 1 #include "rstp.h" #include #include #include "hmap.h" #include "list.h" #include "ovs-atomic.h" #include "packets.h" enum admin_port_state { RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED = 0, RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED = 1 }; enum oper_p2p_mac_state { RSTP_OPER_P2P_MAC_STATE_DISABLED = 0, RSTP_OPER_P2P_MAC_STATE_ENABLED = 1 }; /* State enumerations for state machines defined in rstp-state-machines.c */ enum port_receive_state_machine { PORT_RECEIVE_SM_INIT, PORT_RECEIVE_SM_DISCARD_EXEC, PORT_RECEIVE_SM_DISCARD, PORT_RECEIVE_SM_RECEIVE_EXEC, PORT_RECEIVE_SM_RECEIVE }; enum port_transmit_state_machine { PORT_TRANSMIT_SM_INIT, PORT_TRANSMIT_SM_TRANSMIT_INIT_EXEC, PORT_TRANSMIT_SM_TRANSMIT_INIT, PORT_TRANSMIT_SM_TRANSMIT_PERIODIC_EXEC, PORT_TRANSMIT_SM_TRANSMIT_PERIODIC, PORT_TRANSMIT_SM_IDLE_EXEC, PORT_TRANSMIT_SM_IDLE, PORT_TRANSMIT_SM_TRANSMIT_CONFIG_EXEC, PORT_TRANSMIT_SM_TRANSMIT_CONFIG, PORT_TRANSMIT_SM_TRANSMIT_TCN_EXEC, PORT_TRANSMIT_SM_TRANSMIT_TCN, PORT_TRANSMIT_SM_TRANSMIT_RSTP_EXEC, PORT_TRANSMIT_SM_TRANSMIT_RSTP }; enum bridge_detection_state_machine { BRIDGE_DETECTION_SM_INIT, BRIDGE_DETECTION_SM_EDGE_EXEC, BRIDGE_DETECTION_SM_EDGE, BRIDGE_DETECTION_SM_NOT_EDGE_EXEC, BRIDGE_DETECTION_SM_NOT_EDGE }; enum port_protocol_migration_state_machine { PORT_PROTOCOL_MIGRATION_SM_INIT, PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP_EXEC, PORT_PROTOCOL_MIGRATION_SM_CHECKING_RSTP, PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP_EXEC, PORT_PROTOCOL_MIGRATION_SM_SELECTING_STP, PORT_PROTOCOL_MIGRATION_SM_SENSING_EXEC, PORT_PROTOCOL_MIGRATION_SM_SENSING }; enum port_information_state_machine { PORT_INFORMATION_SM_INIT, PORT_INFORMATION_SM_DISABLED_EXEC, PORT_INFORMATION_SM_DISABLED, PORT_INFORMATION_SM_AGED_EXEC, PORT_INFORMATION_SM_AGED, PORT_INFORMATION_SM_UPDATE_EXEC, PORT_INFORMATION_SM_UPDATE, PORT_INFORMATION_SM_CURRENT_EXEC, PORT_INFORMATION_SM_CURRENT, PORT_INFORMATION_SM_RECEIVE_EXEC, PORT_INFORMATION_SM_RECEIVE, PORT_INFORMATION_SM_OTHER_EXEC, PORT_INFORMATION_SM_OTHER, PORT_INFORMATION_SM_NOT_DESIGNATED_EXEC, PORT_INFORMATION_SM_NOT_DESIGNATED, PORT_INFORMATION_SM_INFERIOR_DESIGNATED_EXEC, PORT_INFORMATION_SM_INFERIOR_DESIGNATED, PORT_INFORMATION_SM_REPEATED_DESIGNATED_EXEC, PORT_INFORMATION_SM_REPEATED_DESIGNATED, PORT_INFORMATION_SM_SUPERIOR_DESIGNATED_EXEC, PORT_INFORMATION_SM_SUPERIOR_DESIGNATED }; enum port_role_selection_state_machine { PORT_ROLE_SELECTION_SM_INIT, PORT_ROLE_SELECTION_SM_INIT_BRIDGE_EXEC, PORT_ROLE_SELECTION_SM_INIT_BRIDGE, PORT_ROLE_SELECTION_SM_ROLE_SELECTION_EXEC, PORT_ROLE_SELECTION_SM_ROLE_SELECTION }; enum port_role_transition_state_machine { PORT_ROLE_TRANSITION_SM_INIT, PORT_ROLE_TRANSITION_SM_INIT_PORT_EXEC, PORT_ROLE_TRANSITION_SM_DISABLE_PORT_EXEC, PORT_ROLE_TRANSITION_SM_DISABLE_PORT, PORT_ROLE_TRANSITION_SM_DISABLED_PORT_EXEC, PORT_ROLE_TRANSITION_SM_DISABLED_PORT, PORT_ROLE_TRANSITION_SM_ROOT_PORT_EXEC, PORT_ROLE_TRANSITION_SM_ROOT_PORT, PORT_ROLE_TRANSITION_SM_REROOT_EXEC, PORT_ROLE_TRANSITION_SM_ROOT_AGREED_EXEC, PORT_ROLE_TRANSITION_SM_ROOT_PROPOSED_EXEC, PORT_ROLE_TRANSITION_SM_ROOT_FORWARD_EXEC, PORT_ROLE_TRANSITION_SM_ROOT_LEARN_EXEC, PORT_ROLE_TRANSITION_SM_REROOTED_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_PORT, PORT_ROLE_TRANSITION_SM_DESIGNATED_RETIRED_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_SYNCED_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_PROPOSE_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_FORWARD_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_LEARN_EXEC, PORT_ROLE_TRANSITION_SM_DESIGNATED_DISCARD_EXEC, PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT_EXEC, PORT_ROLE_TRANSITION_SM_ALTERNATE_PORT, PORT_ROLE_TRANSITION_SM_ALTERNATE_AGREED_EXEC, PORT_ROLE_TRANSITION_SM_ALTERNATE_PROPOSED_EXEC, PORT_ROLE_TRANSITION_SM_BLOCK_PORT_EXEC, PORT_ROLE_TRANSITION_SM_BLOCK_PORT, PORT_ROLE_TRANSITION_SM_BACKUP_PORT_EXEC }; enum port_state_transition_state_machine { PORT_STATE_TRANSITION_SM_INIT, PORT_STATE_TRANSITION_SM_DISCARDING_EXEC, PORT_STATE_TRANSITION_SM_DISCARDING, PORT_STATE_TRANSITION_SM_LEARNING_EXEC, PORT_STATE_TRANSITION_SM_LEARNING, PORT_STATE_TRANSITION_SM_FORWARDING_EXEC, PORT_STATE_TRANSITION_SM_FORWARDING }; enum topology_change_state_machine { TOPOLOGY_CHANGE_SM_INIT, TOPOLOGY_CHANGE_SM_INACTIVE_EXEC, TOPOLOGY_CHANGE_SM_INACTIVE, TOPOLOGY_CHANGE_SM_LEARNING_EXEC, TOPOLOGY_CHANGE_SM_LEARNING, TOPOLOGY_CHANGE_SM_DETECTED_EXEC, TOPOLOGY_CHANGE_SM_ACTIVE_EXEC, TOPOLOGY_CHANGE_SM_ACTIVE, TOPOLOGY_CHANGE_SM_ACKNOWLEDGED_EXEC, TOPOLOGY_CHANGE_SM_PROPAGATING_EXEC, TOPOLOGY_CHANGE_SM_NOTIFIED_TC_EXEC, TOPOLOGY_CHANGE_SM_NOTIFIED_TCN_EXEC, }; /* [17.18.4, 17.13, Table 17-1]. */ struct rstp_times { /* [17.13.5 - Bridge Forward Delay] The delay (expressed in seconds) used * by STP Bridges (17.4) to transition Root and Designated Ports to * Forwarding (Table 17-1). * Default = 15.0 s. Values in range 4.0 - 30.0 */ uint16_t forward_delay; /* [17.13.6 - Bridge Hello Time] * The interval between periodic transmissions of Configuration Messages * by Designated Ports (Table 17-1). * Default = 2.0 s. Fixed value */ uint16_t hello_time; /* [17.13.8 - Bridge Max Age] * The maximum age of the information transmitted by the Bridge when it is * the Root Bridge (Table 17-1). * Default = 20.0 s. Values in range 6.0 - 40.0 */ uint16_t max_age; uint16_t message_age; }; /* Priority vector [17.6] */ struct rstp_priority_vector { rstp_identifier root_bridge_id; uint32_t root_path_cost; rstp_identifier designated_bridge_id; uint16_t designated_port_id; uint16_t bridge_port_id; }; enum rstp_bpdu_type { CONFIGURATION_BPDU = 0x0, TOPOLOGY_CHANGE_NOTIFICATION_BPDU = 0x80, RAPID_SPANNING_TREE_BPDU = 0x2 }; enum rstp_bpdu_flag { BPDU_FLAG_TOPCHANGE = 0x01, BPDU_FLAG_PROPOSAL = 0x02, BPDU_FLAG_LEARNING = 0x10, BPDU_FLAG_FORWARDING = 0x20, BPDU_FLAG_AGREEMENT = 0x40, BPDU_FLAG_TOPCHANGEACK = 0x80 }; /* Rapid Spanning Tree BPDU [9.3.3] */ OVS_PACKED( struct rstp_bpdu { ovs_be16 protocol_identifier; uint8_t protocol_version_identifier; uint8_t bpdu_type; uint8_t flags; ovs_be64 root_bridge_id; ovs_be32 root_path_cost; ovs_be64 designated_bridge_id; ovs_be16 designated_port_id; ovs_be16 message_age; ovs_be16 max_age; ovs_be16 hello_time; ovs_be16 forward_delay; uint8_t version1_length; uint8_t padding[7]; }); enum rstp_info_is { INFO_IS_DISABLED, INFO_IS_RECEIVED, INFO_IS_AGED, INFO_IS_MINE }; enum rstp_rcvd_info { SUPERIOR_DESIGNATED_INFO, REPEATED_DESIGNATED_INFO, INFERIOR_DESIGNATED_INFO, INFERIOR_ROOT_ALTERNATE_INFO, OTHER_INFO }; struct rstp_port { struct ovs_refcount ref_cnt; struct rstp *rstp OVS_GUARDED_BY(rstp_mutex); struct hmap_node node OVS_GUARDED_BY(rstp_mutex); /* In rstp->ports. */ void *aux OVS_GUARDED_BY(rstp_mutex); struct rstp_bpdu received_bpdu_buffer OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * MAC status parameters ************************************************************************/ /* [6.4.2 - MAC_Operational] * The value of this parameter is TRUE if [...] the MAC entity can be used * to transmit and/or receive frames, and its use is permitted by * management. */ bool mac_operational OVS_GUARDED_BY(rstp_mutex); /* [14.8.2.2] Administrative Bridge Port State */ bool is_administrative_bridge_port OVS_GUARDED_BY(rstp_mutex); /* [6.4.3 - operPointToPointMAC] * a) True. The MAC is connected to a point-to-point LAN; i.e., there is * at most one other system attached to the LAN. * b) False. The MAC is connected to a non-point-to-point LAN; i.e., * there can be more than one other system attached to the LAN. * * If adminPointToPointMAC is set to ForceTrue, then operPointToPointMAC * shall be set True. If adminPointToPointMAC is set to ForceFalse, then * operPointToPointMAC shall be set False. */ bool oper_point_to_point_mac OVS_GUARDED_BY(rstp_mutex); /* [6.4.3 - adminPointToPointMAC] * a) ForceTrue. The administrator requires the MAC to be treated as if it * is connected to a point-to-point LAN, regardless of any indications * to the contrary that are generated by the MAC entity. * b) ForceFalse. The administrator requires the MAC to be treated as * connected to a non-point-to-point LAN, regardless of any indications * to the contrary that are generated by the MAC entity. * c) Auto. The administrator requires the point-to-point status of the * MAC to be determined in accordance with the specific MAC procedures * defined in 6.5. */ enum rstp_admin_point_to_point_mac_state admin_point_to_point_mac OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * [17.3 - RSTP performance parameters] Set by management actions on the * bridge *************************************************************************/ /* [17.13.1 - Admin Edge Port] * The AdminEdgePort parameter for the Port (14.8.2). */ bool admin_edge OVS_GUARDED_BY(rstp_mutex); /* [17.13.3 - AutoEdge] * The AutoEdgePort parameter for the Port (14.8.2). */ bool auto_edge OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * The following variables are set by management actions on the bridge ************************************************************************/ /* Port number and priority * >=1 (max 12 bits [9.2.7]) */ uint16_t port_number OVS_GUARDED_BY(rstp_mutex); /* Port priority * Range: 0-240 in steps of 16 (table 17-2) */ uint8_t priority OVS_GUARDED_BY(rstp_mutex); /* [17.13.11 - PortPathCost] * The Port's contribution, when it is the Root Port, to the Root Path Cost * (17.3.1, 17.5, 17.6) for the Bridge. */ uint32_t port_path_cost OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * The following variables are defined in [17.17 - State machine timers] ************************************************************************/ /* [17.17.1 - edgeDelayWhile] * The Edge Delay timer. The time remaining, in the absence of a received * BPDU, before this port is identified as an operEdgePort. */ uint16_t edge_delay_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.2 - fdWhile] * The Forward Delay timer. Used to delay Port State transitions until * other Bridges have received spanning tree information. */ uint16_t fd_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.3 - helloWhen] * The Hello timer. Used to ensure that at least one BPDU is transmitted by * a Designated Port in each HelloTime period. */ uint16_t hello_when OVS_GUARDED_BY(rstp_mutex); /* [17.17.4 - mdelayWhile] * The Migration Delay timer. Used by the Port Protocol Migration state * machine to allow time for another RSTP Bridge on the same LAN to * synchronize its migration state with this Port before the receipt of a * BPDU can cause this Port to change the BPDU types it transmits. * Initialized to MigrateTime (17.13.9). */ uint16_t mdelay_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.5 - rbWhile] * The Recent Backup timer. Maintained at its initial value, twice * HelloTime, while the Port is a Backup Port. */ uint16_t rb_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.6 - rcvdInfoWhile] * The Received Info timer. The time remaining before the spanning tree * information received by this Port [portPriority (17.19.21) and portTimes * (17.19.22)] is aged out if not refreshed by the receipt of a further * Configuration Message. */ uint16_t rcvd_info_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.7 - rrWhile] * The Recent Root timer. */ uint16_t rr_while OVS_GUARDED_BY(rstp_mutex); /* [17.17.8 - tcWhile] * The Topology Change timer. TCN Messages are sent while this timer is * running. */ uint16_t tc_while OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * The following variables are defined in [17.19 - Per-Port variables] ************************************************************************/ /* [17.19.1 - ageingTime] * Filtering database entries for this Port are aged out after ageingTime * has elapsed since they were first created or refreshed by the Learning * Process. * The value of this parameter is normally Ageing Time (7.9.2, Table 7-5), * and is changed to FwdDelay (17.20.6) for a period of FwdDelay after * fdbFlush (17.19.7) is set by the topology change state machine if * stpVersion (17.19.7) is TRUE. */ uint32_t ageing_time OVS_GUARDED_BY(rstp_mutex); /* [17.19.2 - agree] * Set if synced is set for all other Ports. An RST BPDU with the Agreement * flag set is transmitted and proposed is reset when agree is first set, * and when proposed is set. * Initialized by Port Information state machine. */ bool agree OVS_GUARDED_BY(rstp_mutex); /* [17.19.3 - agreed] * Set when an RST BPDU is received with a Port Role of Root, Alternate, or * Backup Port, the Agreement flag set, and a message priority the same or * worse than the port priority. When agreed is set, the Designated Port * knows that its neighbouring Bridge has confirmed that it can proceed to * the Forwarding state without further delay. * Initialized by Port Information state machine. */ bool agreed OVS_GUARDED_BY(rstp_mutex); /* [17.19.4 - designatedPriority] * The first four components of the Port's designated priority vector * value, as defined in 17.6. The fifth component of the designated * priority vector value is portId (17.19.19). * (Fifth component of the structure must not be used) */ struct rstp_priority_vector designated_priority_vector OVS_GUARDED_BY(rstp_mutex); /* [17.19.5 - designatedTimes] * The designatedTimes variable comprises the set of timer parameter values * (Message Age, Max Age, Forward Delay, and Hello Time) that used to * update Port Times when updtInfo is set. Updated by the updtRolesTree() * procedure (17.21.25). */ struct rstp_times designated_times OVS_GUARDED_BY(rstp_mutex); /* [17.19.6 - disputed] */ bool disputed OVS_GUARDED_BY(rstp_mutex); /* [17.19.7 - fdbFlush] * A boolean. Set by the topology change state machine to instruct the * filtering database to remove all entries for this Port, immediately if * rstpVersion (17.20.11) is TRUE, or by rapid ageing (17.19.1) if * stpVersion (17.20.12) is TRUE. Reset by the filtering database once the * entries are * removed if rstpVersion is TRUE, and immediately if stpVersion is TRUE. */ uint8_t fdb_flush OVS_GUARDED_BY(rstp_mutex); /* [17.19.8 - forward] * Initialized by Port State Transition state machine. */ bool forward OVS_GUARDED_BY(rstp_mutex); /* [17.19.9 - forwarding] * Initialized by Port State Transition state machine. */ bool forwarding OVS_GUARDED_BY(rstp_mutex); /* [17.19.10 - infoIs] * A variable that takes the values Mine, Aged, Received, or Disabled, to * indicate the origin/state of the Port's Spanning Tree information * (portInfo) held for the Port, as follows: * a) If infoIs is Received, the port has received current (not aged out) * information from the Designated Bridge for the attached LAN (a * point-to-point bridge link being a special case of a LAN). * b) If infoIs is Mine, information for the port has been derived from * the Root Port for the Bridge (with the addition of root port cost * information). This includes the possibility that the Root Port is * "Port 0," i.e., the bridge is the Root Bridge for the Bridged Local * Area Network. * c) If infoIs is Aged, information from the Root Bridge has been aged * out. Just as for "reselect" (see 17.19.34), the state machine does * not formally allow the "Aged" state to persist. However, if there is * a delay in recomputing the new root port, correct processing of a * received BPDU is specified. * d) Finally if the port is disabled, infoIs is Disabled. */ enum rstp_info_is info_is OVS_GUARDED_BY(rstp_mutex); /* [17.19.11 - learn] * Initialized by Port State Transition state machine. */ bool learn OVS_GUARDED_BY(rstp_mutex); /* [17.19.12 - learning] * Initialized by Port State Transition state machine. */ bool learning OVS_GUARDED_BY(rstp_mutex); /* [17.19.13 - mcheck] * A boolean. May be set by management to force the Port Protocol Migration * state machine to transmit RST BPDUs for a MigrateTime (17.13.9) period, * to test whether all STP Bridges (17.4) on the attached LAN have been * removed and the Port can continue to transmit RSTP BPDUs. Setting mcheck * has no effect if stpVersion (17.20.12) is TRUE, i.e., the Bridge is * operating in STP Compatibility mode. */ bool mcheck OVS_GUARDED_BY(rstp_mutex); /* [17.19.14 - msgPriority] * The first four components of the message priority vector conveyed in a * received BPDU, as defined in 17.6. * (Fifth component of the structure must not be used). */ struct rstp_priority_vector msg_priority OVS_GUARDED_BY(rstp_mutex); /* [17.19.15 - msgTimes] * The msgTimes variable comprises the timer parameter values (Message Age, * Max Age, Forward Delay, and Hello Time) conveyed in a received BPDU. */ struct rstp_times msg_times OVS_GUARDED_BY(rstp_mutex); /* [17.19.16 - newInfo] * A boolean. Set if a BPDU is to be transmitted. Reset by the Port * Transmit state machine. */ bool new_info OVS_GUARDED_BY(rstp_mutex); /* [17.19.17 - operEdge] * A boolean. The value of the operEdgePort parameter, as determined by the * operation of the Bridge Detection state machine (17.25). */ bool oper_edge OVS_GUARDED_BY(rstp_mutex); /* [17.19.18 - portEnabled] * A boolean. Set if the Bridge's MAC Relay Entity and Spanning Tree * Protocol Entity can use the MAC Service provided by the Port's MAC * entity to transmit and receive frames to and from the attached LAN, * i.e., portEnabled is TRUE if and only if: * a) MAC_Operational (6.4.2) is TRUE; and * b) Administrative Bridge Port State (14.8.2.2) for the Port is * Enabled; and * c) AuthControlledPortStatus is Authorized [if the port is a network * access port (IEEE Std 802.1X)]. */ bool port_enabled OVS_GUARDED_BY(rstp_mutex); /* [17.19.19 - portId] * The Port Identifier. This variable forms the fifth component of the port * priority and designated priority vectors defined in 17.6. */ uint16_t port_id OVS_GUARDED_BY(rstp_mutex); /* [17.19.21 - portPriority] * The first four components of the Port's port priority vector value, as * defined in 17.6. * (Fifth component of the structure must not be used) */ struct rstp_priority_vector port_priority OVS_GUARDED_BY(rstp_mutex); /* [17.19.22 - portTimes] * The portTimes variable comprises the Port's timer parameter values * (Message Age, Max Age, Forward Delay, and Hello Time). These timer * values are used in BPDUs transmitted from the Port. */ struct rstp_times port_times OVS_GUARDED_BY(rstp_mutex); /* [17.19.23 - proposed] * Set when an RST BPDU with a Designated Port role and the Proposal flag * set is received. If agree is not set, proposed causes sync to be set for * all other Ports.of the Bridge. */ bool proposed OVS_GUARDED_BY(rstp_mutex); /* [17.19.24 - proposing] * Set by a Designated Port that is not Forwarding, and conveyed to the * Root Port or Alternate Port of a neighboring Bridge in the Proposal flag * of an RST BPDU (9.3.3). */ bool proposing OVS_GUARDED_BY(rstp_mutex); /* [17.19.25 - rcvdBPDU] * A boolean. Set by system dependent processes, this variable notifies the * Port Receive state machine (17.23) when a valid (9.3.4) Configuration, * TCN, or RST BPDU (9.3.1, 9.3.2, 9.3.3) is received on the Port. Reset * by the Port Receive state machine. */ bool rcvd_bpdu OVS_GUARDED_BY(rstp_mutex); /* [17.19.26 - rcvdInfo] * Set to the result of the rcvInfo() procedure (17.21.8). */ enum rstp_rcvd_info rcvd_info OVS_GUARDED_BY(rstp_mutex); /* [17.19.27 - rcvdMsg] */ bool rcvd_msg OVS_GUARDED_BY(rstp_mutex); /* [17.19.28 - rcvdRSTP] */ bool rcvd_rstp OVS_GUARDED_BY(rstp_mutex); /* [17.19.29 - rcvdSTP] */ bool rcvd_stp OVS_GUARDED_BY(rstp_mutex); /* [17.19.30 - rcvdTc] */ bool rcvd_tc OVS_GUARDED_BY(rstp_mutex); /* [17.19.31 - rcvdTcAck] */ bool rcvd_tc_ack OVS_GUARDED_BY(rstp_mutex); /* [17.19.32 - rcvdTcn] */ bool rcvd_tcn OVS_GUARDED_BY(rstp_mutex); /* [17.19.33 - reRoot] */ bool re_root OVS_GUARDED_BY(rstp_mutex); /* [17.19.34 - reselect] */ bool reselect OVS_GUARDED_BY(rstp_mutex); /* [17.19.35 - role] * The assigned Port Role (17.7). */ enum rstp_port_role role OVS_GUARDED_BY(rstp_mutex); /* [17.19.36 - selected] * A boolean. See 17.28, 17.21.16. */ bool selected OVS_GUARDED_BY(rstp_mutex); /* [17.19.37 - selectedRole] * The newly computed role for the Port (17.7, 17.28, 17.21.25, 17.19.35). */ enum rstp_port_role selected_role OVS_GUARDED_BY(rstp_mutex); /* [17.19.38 - sendRSTP] * A boolean. See 17.24, 17.26. */ bool send_rstp OVS_GUARDED_BY(rstp_mutex); /* [17.19.39 - sync] * A boolean. See 17.10. */ bool sync OVS_GUARDED_BY(rstp_mutex); /* [17.19.40 - synced] * A boolean. See 17.10. */ bool synced OVS_GUARDED_BY(rstp_mutex); /* [17.19.41 - tcAck] * A boolean. Set if a Configuration Message with a topology change * acknowledge flag set is to be transmitted. */ bool tc_ack OVS_GUARDED_BY(rstp_mutex); /* [17.19.42 - tcProp] * A boolean. Set by the Topology Change state machine of any other Port, * to indicate that a topology change should be propagated through this * Port. */ bool tc_prop OVS_GUARDED_BY(rstp_mutex); /* [17.19.43 - tick] * A boolean. See 17.22. */ bool tick OVS_GUARDED_BY(rstp_mutex); /* [17.19.44 - txCount] * A counter. Incremented by the Port Transmission (17.26) state machine on * every BPDU transmission, and decremented used by the Port Timers state * machine (17.22) once a second. Transmissions are delayed if txCount * reaches TxHoldCount (17.13.12). */ uint16_t tx_count OVS_GUARDED_BY(rstp_mutex); /* [17.19.45 - updtInfo] * A boolean. Set by the Port Role Selection state machine (17.28, * 17.21.25) to tell the Port Information state machine that it should copy * designatedPriority to portPriority and designatedTimes to portTimes. */ bool updt_info OVS_GUARDED_BY(rstp_mutex); /* Counter for RSTP received frames - for rstpd */ uint32_t rx_rstp_bpdu_cnt; /* Counter for bad RSTP received frames */ uint32_t error_count OVS_GUARDED_BY(rstp_mutex); /* [14.8.2.1.3] Outputs * a) Uptime count in seconds of the time elapsed since the Port was last * reset or initialized. */ uint32_t uptime OVS_GUARDED_BY(rstp_mutex); enum rstp_state rstp_state OVS_GUARDED_BY(rstp_mutex); bool state_changed OVS_GUARDED_BY(rstp_mutex); /* Per-port state machines state */ enum port_receive_state_machine port_receive_sm_state OVS_GUARDED_BY(rstp_mutex); enum port_protocol_migration_state_machine port_protocol_migration_sm_state OVS_GUARDED_BY(rstp_mutex); enum bridge_detection_state_machine bridge_detection_sm_state OVS_GUARDED_BY(rstp_mutex); enum port_transmit_state_machine port_transmit_sm_state OVS_GUARDED_BY(rstp_mutex); enum port_information_state_machine port_information_sm_state OVS_GUARDED_BY(rstp_mutex); enum port_role_transition_state_machine port_role_transition_sm_state OVS_GUARDED_BY(rstp_mutex); enum port_state_transition_state_machine port_state_transition_sm_state OVS_GUARDED_BY(rstp_mutex); enum topology_change_state_machine topology_change_sm_state OVS_GUARDED_BY(rstp_mutex); }; struct rstp { struct ovs_list node OVS_GUARDED_BY(rstp_mutex); /* In rstp instances list */ char *name; /* Bridge name. */ /* Changes in last SM execution. */ bool changes OVS_GUARDED_BY(rstp_mutex); /* Per-bridge state machines state */ enum port_role_selection_state_machine port_role_selection_sm_state OVS_GUARDED_BY(rstp_mutex); /* Bridge MAC address * (stored in the least significant 48 bits of rstp_identifier). */ rstp_identifier address OVS_GUARDED_BY(rstp_mutex); /* [7.12.5] */ /* Bridge priority */ uint16_t priority OVS_GUARDED_BY(rstp_mutex); /* Valid values: 0-61440 in steps of 4096 */ /************************************************************************* * [17.3 - RSTP performance parameters] ************************************************************************/ /* [17.13] * The Spanning Tree Protocol Entity shall be reinitialized, as specified * by the assertion of BEGIN (17.18.1) in the state machine specification, * if the following parameters are modified: * a) Force Protocol Version (17.13.4) * * The spanning tree priority vectors and Port Role assignments for a * Bridge shall be recomputed, as specified by the operation of the Port * Role Selection state machine (17.28) by clearing selected (17.19.36) and * setting reselect (17.19.34) for any Port or Ports for which the * following parameters are modified: * b) Bridge Identifier Priority (17.13.7) * c) Port Identifier Priority (17.13.10) * d) Port Path Cost (17.13.11) * * If the Transmit Hold Count is modified the value of txCount (17.19.44) * for all Ports shall be set to zero. * * The RSTP specification permits changes in other performance parameters * without exceptional actions. */ /* [17.13.2 - Ageing Time] * The Ageing Time parameter for the Bridge (7.9.2, Table 7-5). */ uint32_t ageing_time OVS_GUARDED_BY(rstp_mutex); /* [17.13.4 - Force Protocol Version] * The Force Protocol Version parameter for the Bridge (17.4, 14.8.1). * This can take the value 0 (STP Compatibility mode) or 2 (the default, * normal operation). */ enum rstp_force_protocol_version force_protocol_version OVS_GUARDED_BY(rstp_mutex); /* [17.13.5 - Bridge Forward Delay] * The delay used by STP Bridges (17.4) to transition Root and Designated * Ports to Forwarding (Table 17-1). */ uint16_t bridge_forward_delay OVS_GUARDED_BY(rstp_mutex); /* [17.13.6 - Bridge Hello Time] * The interval between periodic transmissions of Configuration Messages * by Designated Ports (Table 17-1). */ uint16_t bridge_hello_time OVS_GUARDED_BY(rstp_mutex); /* [17.13.8 - Bridge Max Age] * The maximum age of the information transmitted by the Bridge when it is * the Root Bridge (Table 17-1). */ uint16_t bridge_max_age OVS_GUARDED_BY(rstp_mutex); /* [17.13.9 - Migrate Time] * The initial value of the mdelayWhile and edgeDelayWhile timers (17.17.4, * 17.17.1), fixed for all RSTP implementations conforming to this * specification (Table 17-1). */ uint16_t migrate_time OVS_GUARDED_BY(rstp_mutex); /* [17.13.12 - Transmit Hold Count] * The Transmit Hold Count (Table 17-1) used by the Port Transmit state * machine to limit transmission rate. */ uint16_t transmit_hold_count OVS_GUARDED_BY(rstp_mutex); /************************************************************************* * The following variables are defined in [17.18 - Per-Bridge variables] ************************************************************************/ /* [17.18.1 - BEGIN] * A Boolean controlled by the system initialization (17.16). If TRUE * causes all state machines, including per Port state machines, to * continuously execute their initial state. */ bool begin OVS_GUARDED_BY(rstp_mutex); /* [17.18.2 BridgeIdentifier] * The unique Bridge Identifier assigned to this Bridge, comprising two * components: the Bridge Identifier Priority, which may be modified by * management (see 9.2.5 and 14.8.1.2) and is the more significant when * Bridge Identifiers are compared, and a component derived from the Bridge * Address (7.12.5), which guarantees uniqueness of the Bridge Identifiers * of different Bridges. */ rstp_identifier bridge_identifier OVS_GUARDED_BY(rstp_mutex); /* [17.8.3 BridgePriority] * The bridge priority vector, as defined in 17.6. The first (RootBridgeID) * and third (DesignatedBridgeID) components are both equal to the value * of the Bridge Identifier (17.18.2). The other components are zero. */ struct rstp_priority_vector bridge_priority OVS_GUARDED_BY(rstp_mutex); /* [17.18.4 - BridgeTimes] * BridgeTimes comprises four components: the current values of Bridge * Forward Delay, Bridge Hello Time, Bridge Max Age (17.13, Table 17-1), * and a Message Age of zero. */ struct rstp_times bridge_times OVS_GUARDED_BY(rstp_mutex); /* [17.18.6 - rootPriority] * The first four components of the Bridge's root priority vector, as * defined in 17.6. */ struct rstp_priority_vector root_priority OVS_GUARDED_BY(rstp_mutex); /* [17.18.5 - rootPortId] * The Port Identifier of the Root Port. This is the fifth component of * the root priority vector, as defined in 17.6. */ uint16_t root_port_id OVS_GUARDED_BY(rstp_mutex); /* [17.18.7 - rootTimes] * The rootTimes variable comprises the Bridge's operational timer * parameter values (Message Age, Max Age, Forward Delay, and Hello Time), * derived from the values stored in portTimes (17.19.22) for the Root Port * or from BridgeTimes (17.18.4). */ struct rstp_times root_times OVS_GUARDED_BY(rstp_mutex); /* 17.20 State machine conditions and parameters */ /* [17.20.11] rstpVersion * TRUE if Force Protocol Version (17.13.4) is greater than or equal to 2. */ bool rstp_version OVS_GUARDED_BY(rstp_mutex); /* [17.20.12] stpVersion * TRUE if Force Protocol Version (17.13.4) is less than 2. */ bool stp_version OVS_GUARDED_BY(rstp_mutex); /* Ports */ struct hmap ports OVS_GUARDED_BY(rstp_mutex); struct ovs_refcount ref_cnt; /* Interface to client. */ void (*send_bpdu)(struct dp_packet *bpdu, void *port_aux, void *rstp_aux); void *aux; bool root_changed; void *old_root_aux; void *new_root_aux; }; #endif /* rstp-common.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlog-syn.man0000644000000000000000000000013213534540071016665 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801423.749845818 openvswitch-2.5.9/lib/vlog-syn.man0000644000175000017500000000033113534540071020350 0ustar00jpettitjpettit00000000000000.IP "Logging options:" [\fB\-v\fR[\fImodule\fR[\fB:\fIdestination\fR[\fB:\fIlevel\fR]]]]\&... .br [\fB\-\-verbose[=\fImodule\fR[\fB:\fIdestination\fR[\fB:\fIlevel\fR]]]]\&... .br [\fB\-\-log\-file\fR[\fB=\fIfile\fR]] openvswitch-2.5.9/lib/PaxHeaders.82075/id-pool.h0000644000000000000000000000013213534540071016126 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.741853131 openvswitch-2.5.9/lib/id-pool.h0000644000175000017500000000222313534540071017613 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * Copyright (c) 2014 Netronome. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ID_POOL_H #define ID_POOL_H #include #include #include struct id_pool; struct id_pool *id_pool_create(uint32_t base, uint32_t n_ids); void id_pool_destroy(struct id_pool *); bool id_pool_alloc_id(struct id_pool *, uint32_t *id); void id_pool_free_id(struct id_pool *, uint32_t id); void id_pool_add(struct id_pool *, uint32_t id); /* * ID pool. * ======== * * Pool of unique 32bit ids. * * * Thread-safety * ============= * * APIs are not thread safe. */ #endif /* id-pool.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-version-opt.h0000644000000000000000000000013213534540071017632 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.801853573 openvswitch-2.5.9/lib/ofp-version-opt.h0000644000175000017500000000175613534540071021331 0ustar00jpettitjpettit00000000000000#ifndef OFP_VERSION_H #define OFP_VERSION_H 1 #include #include "util.h" #include "ofp-util.h" #define OFP_VERSION_LONG_OPTIONS \ {"version", no_argument, NULL, 'V'}, \ {"protocols", required_argument, NULL, 'O'} #define OFP_VERSION_OPTION_HANDLERS \ case 'V': \ ovs_print_version(OFP10_VERSION, OFP13_VERSION); \ exit(EXIT_SUCCESS); \ \ case 'O': \ set_allowed_ofp_versions(optarg); \ break; uint32_t get_allowed_ofp_versions(void); void set_allowed_ofp_versions(const char *string); void mask_allowed_ofp_versions(uint32_t); void add_allowed_ofp_versions(uint32_t); void ofp_version_usage(void); #endif openvswitch-2.5.9/lib/PaxHeaders.82075/aes128.h0000644000000000000000000000013213534540071015566 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.657852512 openvswitch-2.5.9/lib/aes128.h0000644000175000017500000000222213534540071017252 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Based on rijndael.txt by Philip J. Erdelsky, downloaded from * http://www.efgh.com/software/rijndael.htm on September 24, 2009. The * license information there is: "Public domain; no restrictions on use." * The Apache license above applies only to Nicira's modifications to the * original code. */ #ifndef AES128_H #define AES128_H #include struct aes128 { uint32_t rk[128/8 + 28]; }; void aes128_schedule(struct aes128 *, const uint8_t key[16]); void aes128_encrypt(const struct aes128 *, const void *, void *); #endif /* aes128.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/memory.c0000644000000000000000000000013213534540071016066 xustar0030 mtime=1567801401.413681258 30 atime=1567801402.077686135 30 ctime=1567801424.765853308 openvswitch-2.5.9/lib/memory.c0000644000175000017500000001154013534540071017555 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "memory.h" #include #include #include #include "dynamic-string.h" #include "poll-loop.h" #include "simap.h" #include "timeval.h" #include "unixctl.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(memory); /* The number of milliseconds before the first report of daemon memory usage, * and the number of milliseconds between checks for daemon memory growth. */ #define MEMORY_CHECK_INTERVAL (10 * 1000) /* When we should next check memory usage and possibly trigger a report. */ static long long int next_check; /* The last time at which we reported memory usage, and the usage we reported * at that time. */ static long long int last_report; static unsigned long int last_reported_maxrss; /* Are we expecting a call to memory_report()? */ static bool want_report; /* Unixctl connections waiting for responses. */ static struct unixctl_conn **conns; static size_t n_conns; static void memory_init(void); /* Runs the memory monitor. * * The client should call memory_should_report() afterward. * * This function, and the remainder of this module's interface, should be * called from only a single thread. */ void memory_run(void) { struct rusage usage; long long int now; memory_init(); /* Time for a check? */ now = time_msec(); if (now < next_check) { return; } next_check = now + MEMORY_CHECK_INTERVAL; /* Time for a report? */ getrusage(RUSAGE_SELF, &usage); if (!last_reported_maxrss) { VLOG_INFO("%lu kB peak resident set size after %.1f seconds", (unsigned long int) usage.ru_maxrss, (now - time_boot_msec()) / 1000.0); } else if (usage.ru_maxrss >= last_reported_maxrss * 1.5) { VLOG_INFO("peak resident set size grew %.0f%% in last %.1f seconds, " "from %lu kB to %lu kB", ((double) usage.ru_maxrss / last_reported_maxrss - 1) * 100, (now - last_report) / 1000.0, last_reported_maxrss, (unsigned long int) usage.ru_maxrss); } else { return; } /* Request a report. */ want_report = true; last_report = now; last_reported_maxrss = usage.ru_maxrss; } /* Causes the poll loop to wake up if the memory monitor needs to run. */ void memory_wait(void) { if (memory_should_report()) { poll_immediate_wake(); } } /* Returns true if the caller should log some information about memory usage * (with memory_report()), false otherwise. */ bool memory_should_report(void) { return want_report || n_conns > 0; } static void compose_report(const struct simap *usage, struct ds *s) { const struct simap_node **nodes = simap_sort(usage); size_t n = simap_count(usage); size_t i; for (i = 0; i < n; i++) { const struct simap_node *node = nodes[i]; ds_put_format(s, "%s:%u ", node->name, node->data); } ds_chomp(s, ' '); free(nodes); } /* Logs the contents of 'usage', as a collection of name-count pairs. * * 'usage' should capture large-scale statistics that one might reasonably * expect to correlate with memory usage. For example, each OpenFlow flow * requires some memory, so ovs-vswitchd includes the total number of flows in * 'usage'. */ void memory_report(const struct simap *usage) { struct ds s; size_t i; ds_init(&s); compose_report(usage, &s); if (want_report) { if (s.length) { VLOG_INFO("%s", ds_cstr(&s)); } want_report = false; } if (n_conns) { for (i = 0; i < n_conns; i++) { unixctl_command_reply(conns[i], ds_cstr(&s)); } free(conns); conns = NULL; n_conns = 0; } ds_destroy(&s); } static void memory_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { conns = xrealloc(conns, (n_conns + 1) * sizeof *conns); conns[n_conns++] = conn; } static void memory_init(void) { static bool inited = false; if (!inited) { inited = true; unixctl_command_register("memory/show", "", 0, 0, memory_unixctl_show, NULL); next_check = time_boot_msec() + MEMORY_CHECK_INTERVAL; } } openvswitch-2.5.9/lib/PaxHeaders.82075/rstp.c0000644000000000000000000000013213534540071015546 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.865854045 openvswitch-2.5.9/lib/rstp.c0000644000175000017500000013257513534540071017251 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011-2014 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) public interface. * * Authors: * Martino Fornasa * Daniele Venturino * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. * */ #include #include "rstp.h" #include "rstp-common.h" #include "rstp-state-machines.h" #include #include #include #include #include #include "byte-order.h" #include "connectivity.h" #include "ofpbuf.h" #include "ofproto/ofproto.h" #include "dp-packet.h" #include "packets.h" #include "seq.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(rstp); struct ovs_mutex rstp_mutex = OVS_MUTEX_INITIALIZER; static struct ovs_list all_rstps__ = OVS_LIST_INITIALIZER(&all_rstps__); static struct ovs_list *const all_rstps OVS_GUARDED_BY(rstp_mutex) = &all_rstps__; /* Internal use only. */ static void rstp_set_bridge_address__(struct rstp *, rstp_identifier) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_priority__(struct rstp *, int new_priority) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_ageing_time__(struct rstp *, int new_ageing_time) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_force_protocol_version__(struct rstp *, enum rstp_force_protocol_version) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_hello_time__(struct rstp *) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_max_age__(struct rstp *, int new_max_age) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_forward_delay__(struct rstp *, int new_forward_delay) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_transmit_hold_count__(struct rstp *, int new_transmit_hold_count) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_migrate_time__(struct rstp *) OVS_REQUIRES(rstp_mutex); static void rstp_set_bridge_times__(struct rstp *, int new_forward_delay, int new_hello_time, int new_max_age, int new_message_age) OVS_REQUIRES(rstp_mutex); static struct rstp_port *rstp_get_port__(struct rstp *rstp, uint16_t port_number) OVS_REQUIRES(rstp_mutex); static void set_port_id__(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static void update_port_enabled__(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static void set_bridge_priority__(struct rstp *) OVS_REQUIRES(rstp_mutex); static void reinitialize_rstp__(struct rstp *) OVS_REQUIRES(rstp_mutex); static bool is_port_number_available__(struct rstp *, int, struct rstp_port *) OVS_REQUIRES(rstp_mutex); static uint16_t rstp_first_free_number__(struct rstp *, struct rstp_port *) OVS_REQUIRES(rstp_mutex); static void rstp_initialize_port_defaults__(struct rstp_port *) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_priority__(struct rstp_port *, int priority) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_port_number__(struct rstp_port *, uint16_t port_number) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_path_cost__(struct rstp_port *, uint32_t path_cost) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_administrative_bridge_port__(struct rstp_port *, uint8_t admin_port_state, bool initializing) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_admin_edge__(struct rstp_port *, bool admin_edge) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_auto_edge__(struct rstp_port *, bool auto_edge) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_admin_point_to_point_mac__(struct rstp_port *, enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state) OVS_REQUIRES(rstp_mutex); static void rstp_port_set_mcheck__(struct rstp_port *, bool mcheck) OVS_REQUIRES(rstp_mutex); static void reinitialize_port__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex); const char * rstp_state_name(enum rstp_state state) { switch (state) { case RSTP_DISABLED: return "Disabled"; case RSTP_LEARNING: return "Learning"; case RSTP_FORWARDING: return "Forwarding"; case RSTP_DISCARDING: return "Discarding"; default: return "Unknown"; } } const char * rstp_port_role_name(enum rstp_port_role role) { switch (role) { case ROLE_ROOT: return "Root"; case ROLE_DESIGNATED: return "Designated"; case ROLE_ALTERNATE: return "Alternate"; case ROLE_BACKUP: return "Backup"; case ROLE_DISABLED: return "Disabled"; default: return "Unknown"; } } /* Caller has to hold a reference to prevent 'rstp' from being deleted * while taking a new reference. */ struct rstp * rstp_ref(struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { if (rstp) { ovs_refcount_ref(&rstp->ref_cnt); } return rstp; } /* Frees RSTP struct when reference count reaches zero. */ void rstp_unref(struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { if (rstp && ovs_refcount_unref_relaxed(&rstp->ref_cnt) == 1) { ovs_mutex_lock(&rstp_mutex); /* Each RSTP port points back to struct rstp without holding a * reference for that pointer. This is OK as we never move * ports from one bridge to another, and holders always * release their ports before releasing the bridge. This * means that there should be not ports at this time. */ ovs_assert(hmap_is_empty(&rstp->ports)); list_remove(&rstp->node); ovs_mutex_unlock(&rstp_mutex); hmap_destroy(&rstp->ports); free(rstp->name); free(rstp); } } /* Returns the port number. Mutex is needed to guard against * concurrent reinitialization (which can temporarily clear the * port_number). */ int rstp_port_get_number(const struct rstp_port *p) OVS_EXCLUDED(rstp_mutex) { int number; ovs_mutex_lock(&rstp_mutex); number = p->port_number; ovs_mutex_unlock(&rstp_mutex); return number; } static void rstp_unixctl_tcn(struct unixctl_conn *, int argc, const char *argv[], void *aux); /* Decrements the State Machines' timers. */ void rstp_tick_timers(struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); decrease_rstp_port_timers__(rstp); ovs_mutex_unlock(&rstp_mutex); } /* Processes an incoming BPDU. */ void rstp_port_received_bpdu(struct rstp_port *rp, const void *bpdu, size_t bpdu_size) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); /* Only process packets on ports that have RSTP enabled. */ if (rp && rp->rstp_state != RSTP_DISABLED) { process_received_bpdu__(rp, bpdu, bpdu_size); } ovs_mutex_unlock(&rstp_mutex); } void rstp_init(void) OVS_EXCLUDED(rstp_mutex) { unixctl_command_register("rstp/tcn", "[bridge]", 0, 1, rstp_unixctl_tcn, NULL); } /* Creates and returns a new RSTP instance that initially has no ports. */ struct rstp * rstp_create(const char *name, rstp_identifier bridge_address, void (*send_bpdu)(struct dp_packet *bpdu, void *port_aux, void *rstp_aux), void *aux) OVS_EXCLUDED(rstp_mutex) { struct rstp *rstp; VLOG_DBG("Creating RSTP instance"); rstp = xzalloc(sizeof *rstp); rstp->name = xstrdup(name); /* Initialize the ports map before calling any setters, * so that the state machines will see an empty ports map. */ hmap_init(&rstp->ports); ovs_mutex_lock(&rstp_mutex); /* Set bridge address. */ rstp_set_bridge_address__(rstp, bridge_address); /* Set default parameters values. */ rstp_set_bridge_priority__(rstp, RSTP_DEFAULT_PRIORITY); rstp_set_bridge_ageing_time__(rstp, RSTP_DEFAULT_AGEING_TIME); rstp_set_bridge_force_protocol_version__(rstp, FPV_DEFAULT); rstp_set_bridge_forward_delay__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY); rstp_set_bridge_hello_time__(rstp); rstp_set_bridge_max_age__(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE); rstp_set_bridge_migrate_time__(rstp); rstp_set_bridge_transmit_hold_count__(rstp, RSTP_DEFAULT_TRANSMIT_HOLD_COUNT); rstp_set_bridge_times__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY, RSTP_BRIDGE_HELLO_TIME, RSTP_DEFAULT_BRIDGE_MAX_AGE, 0); rstp->send_bpdu = send_bpdu; rstp->aux = aux; rstp->changes = false; rstp->begin = true; rstp->old_root_aux = NULL; rstp->new_root_aux = NULL; ovs_refcount_init(&rstp->ref_cnt); list_push_back(all_rstps, &rstp->node); ovs_mutex_unlock(&rstp_mutex); VLOG_DBG("RSTP instance creation done"); return rstp; } /* Called by rstp_set_bridge_address() and rstp_set_bridge_priority(), * it updates the bridge priority vector according to the values passed by * those setters. */ static void set_bridge_priority__(struct rstp *rstp) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; rstp->bridge_priority.root_bridge_id = rstp->bridge_identifier; rstp->bridge_priority.designated_bridge_id = rstp->bridge_identifier; VLOG_DBG("%s: new bridge identifier: "RSTP_ID_FMT"", rstp->name, RSTP_ID_ARGS(rstp->bridge_identifier)); /* [17.13] When the bridge address changes, recalculates all priority * vectors. */ HMAP_FOR_EACH (p, node, &rstp->ports) { p->selected = false; p->reselect = true; } rstp->changes = true; updt_roles_tree__(rstp); } /* Sets the bridge address. */ static void rstp_set_bridge_address__(struct rstp *rstp, rstp_identifier bridge_address) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s: set bridge address to: "RSTP_ID_FMT"", rstp->name, RSTP_ID_ARGS(bridge_address)); if (rstp->address != bridge_address) { rstp->address = bridge_address; rstp->bridge_identifier &= 0xffff000000000000ULL; rstp->bridge_identifier |= bridge_address; set_bridge_priority__(rstp); } } /* Sets the bridge address. */ void rstp_set_bridge_address(struct rstp *rstp, rstp_identifier bridge_address) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_address__(rstp, bridge_address); ovs_mutex_unlock(&rstp_mutex); } const char * rstp_get_name(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { char *name; ovs_mutex_lock(&rstp_mutex); name = rstp->name; ovs_mutex_unlock(&rstp_mutex); return name; } rstp_identifier rstp_get_bridge_id(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { rstp_identifier bridge_id; ovs_mutex_lock(&rstp_mutex); bridge_id = rstp->bridge_identifier; ovs_mutex_unlock(&rstp_mutex); return bridge_id; } /* Sets the bridge priority. */ static void rstp_set_bridge_priority__(struct rstp *rstp, int new_priority) OVS_REQUIRES(rstp_mutex) { new_priority = ROUND_DOWN(new_priority, RSTP_PRIORITY_STEP); if (rstp->priority != new_priority && new_priority >= RSTP_MIN_PRIORITY && new_priority <= RSTP_MAX_PRIORITY) { VLOG_DBG("%s: set bridge priority to %d", rstp->name, new_priority); rstp->priority = new_priority; rstp->bridge_identifier &= 0x0000ffffffffffffULL; rstp->bridge_identifier |= (uint64_t)new_priority << 48; set_bridge_priority__(rstp); } } void rstp_set_bridge_priority(struct rstp *rstp, int new_priority) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_priority__(rstp, new_priority); ovs_mutex_unlock(&rstp_mutex); } /* Sets the bridge ageing time. */ static void rstp_set_bridge_ageing_time__(struct rstp *rstp, int new_ageing_time) OVS_REQUIRES(rstp_mutex) { if (new_ageing_time >= RSTP_MIN_AGEING_TIME && new_ageing_time <= RSTP_MAX_AGEING_TIME) { VLOG_DBG("%s: set ageing time to %d", rstp->name, new_ageing_time); rstp->ageing_time = new_ageing_time; } } void rstp_set_bridge_ageing_time(struct rstp *rstp, int new_ageing_time) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_ageing_time__(rstp, new_ageing_time); ovs_mutex_unlock(&rstp_mutex); } /* Reinitializes RSTP when switching from RSTP mode to STP mode * or vice versa. */ static void reinitialize_rstp__(struct rstp *rstp) OVS_REQUIRES(rstp_mutex) { struct rstp temp; static struct hmap ports; struct rstp_port *p; /* Copy rstp in temp */ temp = *rstp; ports = rstp->ports; /* stop and clear rstp */ memset(rstp, 0, sizeof(struct rstp)); /* Initialize rstp. */ rstp->name = temp.name; /* Initialize the ports hmap before calling any setters, * so that the state machines will see an empty ports list. */ hmap_init(&rstp->ports); /* Set bridge address. */ rstp_set_bridge_address__(rstp, temp.address); /* Set default parameters values. */ rstp_set_bridge_priority__(rstp, RSTP_DEFAULT_PRIORITY); rstp_set_bridge_ageing_time__(rstp, RSTP_DEFAULT_AGEING_TIME); rstp_set_bridge_forward_delay__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY); rstp_set_bridge_hello_time__(rstp); rstp_set_bridge_max_age__(rstp, RSTP_DEFAULT_BRIDGE_MAX_AGE); rstp_set_bridge_migrate_time__(rstp); rstp_set_bridge_transmit_hold_count__(rstp, RSTP_DEFAULT_TRANSMIT_HOLD_COUNT); rstp_set_bridge_times__(rstp, RSTP_DEFAULT_BRIDGE_FORWARD_DELAY, RSTP_BRIDGE_HELLO_TIME, RSTP_DEFAULT_BRIDGE_MAX_AGE, 0); rstp->send_bpdu = temp.send_bpdu; rstp->aux = temp.aux; rstp->node = temp.node; rstp->changes = false; rstp->begin = true; /* Restore ports. */ rstp->ports = ports; HMAP_FOR_EACH (p, node, &rstp->ports) { reinitialize_port__(p); } rstp->ref_cnt = temp.ref_cnt; } /* Sets the force protocol version parameter. */ static void rstp_set_bridge_force_protocol_version__(struct rstp *rstp, enum rstp_force_protocol_version new_force_protocol_version) OVS_REQUIRES(rstp_mutex) { if (new_force_protocol_version != rstp->force_protocol_version && (new_force_protocol_version == FPV_STP_COMPATIBILITY || new_force_protocol_version == FPV_DEFAULT)) { VLOG_DBG("%s: set bridge Force Protocol Version to %d", rstp->name, new_force_protocol_version); /* [17.13] The Spanning Tree Protocol Entity shall be reinitialized, * as specified by the assertion of BEGIN (17.18.1) in the state * machine specification. */ reinitialize_rstp__(rstp); rstp->force_protocol_version = new_force_protocol_version; if (rstp->force_protocol_version < 2) { rstp->stp_version = true; rstp->rstp_version = false; } else { rstp->stp_version = false; rstp->rstp_version = true; } rstp->changes = true; move_rstp__(rstp); } } void rstp_set_bridge_force_protocol_version(struct rstp *rstp, enum rstp_force_protocol_version new_force_protocol_version) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_force_protocol_version__(rstp, new_force_protocol_version); ovs_mutex_unlock(&rstp_mutex); } /* Sets the bridge Hello Time parameter. */ static void rstp_set_bridge_hello_time__(struct rstp *rstp) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s: set RSTP Hello Time to %d", rstp->name, RSTP_BRIDGE_HELLO_TIME); /* 2 is the only acceptable value. */ rstp->bridge_hello_time = RSTP_BRIDGE_HELLO_TIME; } /* Sets the bridge max age parameter. */ static void rstp_set_bridge_max_age__(struct rstp *rstp, int new_max_age) OVS_REQUIRES(rstp_mutex) { if (rstp->bridge_max_age != new_max_age && new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) { /* [17.13] */ if ((2 * (rstp->bridge_forward_delay - 1) >= new_max_age) && (new_max_age >= 2 * rstp->bridge_hello_time)) { VLOG_DBG("%s: set RSTP bridge Max Age to %d", rstp->name, new_max_age); rstp->bridge_max_age = new_max_age; rstp->bridge_times.max_age = new_max_age; rstp->changes = true; updt_roles_tree__(rstp); } } } void rstp_set_bridge_max_age(struct rstp *rstp, int new_max_age) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_max_age__(rstp, new_max_age); ovs_mutex_unlock(&rstp_mutex); } /* Sets the bridge forward delay parameter. */ static void rstp_set_bridge_forward_delay__(struct rstp *rstp, int new_forward_delay) OVS_REQUIRES(rstp_mutex) { if (rstp->bridge_forward_delay != new_forward_delay && new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) { if (2 * (new_forward_delay - 1) >= rstp->bridge_max_age) { VLOG_DBG("%s: set RSTP Forward Delay to %d", rstp->name, new_forward_delay); rstp->bridge_forward_delay = new_forward_delay; rstp->bridge_times.forward_delay = new_forward_delay; rstp->changes = true; updt_roles_tree__(rstp); } } } void rstp_set_bridge_forward_delay(struct rstp *rstp, int new_forward_delay) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_forward_delay__(rstp, new_forward_delay); ovs_mutex_unlock(&rstp_mutex); } /* Sets the bridge transmit hold count parameter. */ static void rstp_set_bridge_transmit_hold_count__(struct rstp *rstp, int new_transmit_hold_count) OVS_REQUIRES(rstp_mutex) { if (rstp->transmit_hold_count != new_transmit_hold_count && new_transmit_hold_count >= RSTP_MIN_TRANSMIT_HOLD_COUNT && new_transmit_hold_count <= RSTP_MAX_TRANSMIT_HOLD_COUNT) { struct rstp_port *p; VLOG_DBG("%s: set RSTP Transmit Hold Count to %d", rstp->name, new_transmit_hold_count); /* Resetting txCount on all ports [17.13]. */ rstp->transmit_hold_count = new_transmit_hold_count; HMAP_FOR_EACH (p, node, &rstp->ports) { p->tx_count = 0; } } } void rstp_set_bridge_transmit_hold_count(struct rstp *rstp, int new_transmit_hold_count) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_set_bridge_transmit_hold_count__(rstp, new_transmit_hold_count); ovs_mutex_unlock(&rstp_mutex); } /* Sets the bridge migrate time parameter. */ static void rstp_set_bridge_migrate_time__(struct rstp *rstp) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s: set RSTP Migrate Time to %d", rstp->name, RSTP_MIGRATE_TIME); /* 3 is the only acceptable value */ rstp->migrate_time = RSTP_MIGRATE_TIME; } /* Sets the bridge times. */ static void rstp_set_bridge_times__(struct rstp *rstp, int new_forward_delay, int new_hello_time, int new_max_age, int new_message_age) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s: set RSTP times to (%d, %d, %d, %d)", rstp->name, new_forward_delay, new_hello_time, new_max_age, new_message_age); if (new_forward_delay >= RSTP_MIN_BRIDGE_FORWARD_DELAY && new_forward_delay <= RSTP_MAX_BRIDGE_FORWARD_DELAY) { rstp->bridge_times.forward_delay = new_forward_delay; } if (new_hello_time == RSTP_BRIDGE_HELLO_TIME) { rstp->bridge_times.hello_time = new_hello_time; } if (new_max_age >= RSTP_MIN_BRIDGE_MAX_AGE && new_max_age <= RSTP_MAX_BRIDGE_MAX_AGE) { rstp->bridge_times.max_age = new_max_age; } rstp->bridge_times.message_age = new_message_age; } /* Sets the port id, it is called by rstp_port_set_port_number__() or * rstp_port_set_priority__(). */ static void set_port_id__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp *rstp; rstp = p->rstp; /* [9.2.7] Port identifier. */ p->port_id = p->port_number | (p->priority << 8); VLOG_DBG("%s: new RSTP port id "RSTP_PORT_ID_FMT"", rstp->name, p->port_id); } /* Sets the port priority. */ static void rstp_port_set_priority__(struct rstp_port *port, int priority) OVS_REQUIRES(rstp_mutex) { if (port->priority != priority && priority >= RSTP_MIN_PORT_PRIORITY && priority <= RSTP_MAX_PORT_PRIORITY) { VLOG_DBG("%s, port %u: set RSTP port priority to %d", port->rstp->name, port->port_number, priority); priority -= priority % RSTP_STEP_PORT_PRIORITY; port->priority = priority; set_port_id__(port); port->selected = false; port->reselect = true; } } /* Checks if a port number is available. */ static bool is_port_number_available__(struct rstp *rstp, int n, struct rstp_port *port) OVS_REQUIRES(rstp_mutex) { if (n >= 1 && n <= RSTP_MAX_PORTS) { struct rstp_port *p = rstp_get_port__(rstp, n); return p == NULL || p == port; } return false; } static uint16_t rstp_first_free_number__(struct rstp *rstp, struct rstp_port *rstp_port) OVS_REQUIRES(rstp_mutex) { int free_number = 1; while (free_number <= RSTP_MAX_PORTS) { if (is_port_number_available__(rstp, free_number, rstp_port)) { return free_number; } free_number++; } VLOG_DBG("%s, No free port number available.", rstp->name); return 0; } /* Sets the port number. */ static void rstp_port_set_port_number__(struct rstp_port *port, uint16_t port_number) OVS_REQUIRES(rstp_mutex) { int old_port_number = port->port_number; /* If new_port_number is available, use it, otherwise use the first free * available port number. */ if (port->port_number != port_number || port_number == 0) { port->port_number = is_port_number_available__(port->rstp, port_number, port) ? port_number : rstp_first_free_number__(port->rstp, port); if (port->port_number != old_port_number) { set_port_id__(port); /* [17.13] is not clear. I suppose that a port number change * should trigger reselection like a port priority change. */ port->selected = false; port->reselect = true; /* Adjust the ports hmap. */ if (!hmap_node_is_null(&port->node)) { hmap_remove(&port->rstp->ports, &port->node); } hmap_insert(&port->rstp->ports, &port->node, hash_int(port->port_number, 0)); VLOG_DBG("%s: set new RSTP port number %d", port->rstp->name, port->port_number); } } } /* Converts the link speed to a port path cost [Table 17-3]. */ uint32_t rstp_convert_speed_to_cost(unsigned int speed) { uint32_t value; value = speed >= 10000000 ? 2 /* 10 Tb/s. */ : speed >= 1000000 ? 20 /* 1 Tb/s. */ : speed >= 100000 ? 200 /* 100 Gb/s. */ : speed >= 10000 ? 2000 /* 10 Gb/s. */ : speed >= 1000 ? 20000 /* 1 Gb/s. */ : speed >= 100 ? 200000 /* 100 Mb/s. */ : speed >= 10 ? 2000000 /* 10 Mb/s. */ : speed >= 1 ? 20000000 /* 1 Mb/s. */ : RSTP_DEFAULT_PORT_PATH_COST; /* 100 Mb/s. */ return value; } /* Sets the port path cost. */ static void rstp_port_set_path_cost__(struct rstp_port *port, uint32_t path_cost) OVS_REQUIRES(rstp_mutex) { if (port->port_path_cost != path_cost && path_cost >= RSTP_MIN_PORT_PATH_COST && path_cost <= RSTP_MAX_PORT_PATH_COST) { VLOG_DBG("%s, port %u, set RSTP port path cost to %d", port->rstp->name, port->port_number, path_cost); port->port_path_cost = path_cost; port->selected = false; port->reselect = true; } } /* Gets the root path cost. */ uint32_t rstp_get_root_path_cost(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { uint32_t cost; ovs_mutex_lock(&rstp_mutex); cost = rstp->root_priority.root_path_cost; ovs_mutex_unlock(&rstp_mutex); return cost; } /* Finds a port which needs to flush its own MAC learning table. A NULL * pointer is returned if no port needs to flush its MAC learning table. * '*port' needs to be NULL in the first call to start the iteration. If * '*port' is passed as non-NULL, it must be the value set by the last * invocation of this function. * * This function may only be called by the thread that creates and deletes * ports. Otherwise this function is not thread safe, as the returned * '*port' could become stale before it is used in the next invocation. */ void * rstp_check_and_reset_fdb_flush(struct rstp *rstp, struct rstp_port **port) OVS_EXCLUDED(rstp_mutex) { void *aux = NULL; ovs_mutex_lock(&rstp_mutex); if (*port == NULL) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &rstp->ports) { if (p->fdb_flush) { aux = p->aux; *port = p; goto out; } } } else { /* continue */ struct rstp_port *p = *port; HMAP_FOR_EACH_CONTINUE (p, node, &rstp->ports) { if (p->fdb_flush) { aux = p->aux; *port = p; goto out; } } } /* No port needs flushing. */ *port = NULL; out: /* fdb_flush should be reset by the filtering database * once the entries are removed if rstp_version is TRUE, and * immediately if stp_version is TRUE.*/ if (*port != NULL) { (*port)->fdb_flush = false; } ovs_mutex_unlock(&rstp_mutex); return aux; } /* Finds a port whose state has changed, and returns the aux pointer set for * the port. A NULL pointer is returned when no changed port is found. On * return '*portp' contains the pointer to the rstp port that changed, or NULL * if no changed port can be found. * * If '*portp' is passed as non-NULL, it must be the value set by the last * invocation of this function. * * This function may only be called by the thread that creates and deletes * ports. Otherwise this function is not thread safe, as the returned * '*portp' could become stale before it is used in the next invocation. */ void * rstp_get_next_changed_port_aux(struct rstp *rstp, struct rstp_port **portp) { void *aux = NULL; ovs_mutex_lock(&rstp_mutex); if (*portp == NULL) { struct rstp_port *p; HMAP_FOR_EACH (p, node, &rstp->ports) { if (p->state_changed) { p->state_changed = false; aux = p->aux; *portp = p; goto out; } } } else { /* continue */ struct rstp_port *p = *portp; HMAP_FOR_EACH_CONTINUE (p, node, &rstp->ports) { if (p->state_changed) { p->state_changed = false; aux = p->aux; *portp = p; goto out; } } } /* No changed port found. */ *portp = NULL; out: ovs_mutex_unlock(&rstp_mutex); return aux; } bool rstp_shift_root_learned_address(struct rstp *rstp) { bool ret; ovs_mutex_lock(&rstp_mutex); ret = rstp->root_changed; ovs_mutex_unlock(&rstp_mutex); return ret; } void * rstp_get_old_root_aux(struct rstp *rstp) { void *aux; ovs_mutex_lock(&rstp_mutex); aux = rstp->old_root_aux; ovs_mutex_unlock(&rstp_mutex); return aux; } void * rstp_get_new_root_aux(struct rstp *rstp) { void *aux; ovs_mutex_lock(&rstp_mutex); aux = rstp->new_root_aux; ovs_mutex_unlock(&rstp_mutex); return aux; } void rstp_reset_root_changed(struct rstp *rstp) { ovs_mutex_lock(&rstp_mutex); rstp->root_changed = false; ovs_mutex_unlock(&rstp_mutex); } /* Returns the port in 'rstp' with number 'port_number'. * * XXX: May only be called while concurrent deletion of ports is excluded. */ static struct rstp_port * rstp_get_port__(struct rstp *rstp, uint16_t port_number) OVS_REQUIRES(rstp_mutex) { struct rstp_port *port; ovs_assert(rstp && port_number > 0 && port_number <= RSTP_MAX_PORTS); HMAP_FOR_EACH_WITH_HASH (port, node, hash_int(port_number, 0), &rstp->ports) { if (port->port_number == port_number) { return port; } } return NULL; } struct rstp_port * rstp_get_port(struct rstp *rstp, uint16_t port_number) OVS_EXCLUDED(rstp_mutex) { struct rstp_port *p; ovs_mutex_lock(&rstp_mutex); p = rstp_get_port__(rstp, port_number); ovs_mutex_unlock(&rstp_mutex); return p; } void * rstp_get_port_aux__(struct rstp *rstp, uint16_t port_number) OVS_REQUIRES(rstp_mutex) { struct rstp_port *p; p = rstp_get_port__(rstp, port_number); if (p) { return p->aux; } return NULL; } /* Updates the port_enabled parameter. */ static void update_port_enabled__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { if (p->mac_operational && p->is_administrative_bridge_port == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED) { p->port_enabled = true; } else { p->port_enabled = false; } } /* Sets the port MAC_Operational parameter [6.4.2]. */ void rstp_port_set_mac_operational(struct rstp_port *p, bool new_mac_operational) OVS_EXCLUDED(rstp_mutex) { struct rstp *rstp; ovs_mutex_lock(&rstp_mutex); rstp = p->rstp; if (p->mac_operational != new_mac_operational) { p->mac_operational = new_mac_operational; update_port_enabled__(p); rstp->changes = true; move_rstp__(rstp); } ovs_mutex_unlock(&rstp_mutex); } /* Sets the port Administrative Bridge Port parameter. */ static void rstp_port_set_administrative_bridge_port__(struct rstp_port *p, uint8_t admin_port_state, bool initializing) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s, port %u: set RSTP port admin-port-state to %d", p->rstp->name, p->port_number, admin_port_state); if (p->is_administrative_bridge_port != admin_port_state && (admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_DISABLED || admin_port_state == RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED)) { p->is_administrative_bridge_port = admin_port_state; update_port_enabled__(p); if (!initializing) { struct rstp *rstp = p->rstp; rstp->changes = true; move_rstp__(rstp); } } } /* Sets the port oper_point_to_point_mac parameter. */ static void rstp_port_set_oper_point_to_point_mac__(struct rstp_port *p, uint8_t new_oper_p2p_mac) OVS_REQUIRES(rstp_mutex) { if (p->oper_point_to_point_mac != new_oper_p2p_mac && (new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_DISABLED || new_oper_p2p_mac == RSTP_OPER_P2P_MAC_STATE_ENABLED)) { p->oper_point_to_point_mac = new_oper_p2p_mac; update_port_enabled__(p); } } /* Initializes a port with the defaults values for its parameters. */ static void rstp_initialize_port_defaults__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { rstp_port_set_administrative_bridge_port__(p, RSTP_ADMIN_BRIDGE_PORT_STATE_ENABLED, true); rstp_port_set_oper_point_to_point_mac__(p, RSTP_OPER_P2P_MAC_STATE_ENABLED); rstp_port_set_path_cost__(p, RSTP_DEFAULT_PORT_PATH_COST); rstp_port_set_admin_edge__(p, false); rstp_port_set_auto_edge__(p, true); rstp_port_set_mcheck__(p, false); /* Initialize state machines. */ p->port_receive_sm_state = PORT_RECEIVE_SM_INIT; p->port_protocol_migration_sm_state = PORT_PROTOCOL_MIGRATION_SM_INIT; p->bridge_detection_sm_state = BRIDGE_DETECTION_SM_INIT; p->port_transmit_sm_state = PORT_TRANSMIT_SM_INIT; p->port_information_sm_state = PORT_INFORMATION_SM_INIT; p->port_role_transition_sm_state = PORT_ROLE_TRANSITION_SM_INIT; p->port_state_transition_sm_state = PORT_STATE_TRANSITION_SM_INIT; p->topology_change_sm_state = TOPOLOGY_CHANGE_SM_INIT; p->uptime = 0; } static void reinitialize_port__(struct rstp_port *p) OVS_REQUIRES(rstp_mutex) { struct rstp_port temp_port; struct rstp *rstp; rstp = p->rstp; temp_port = *p; memset(p, 0, sizeof(struct rstp_port)); p->ref_cnt = temp_port.ref_cnt; p->rstp = rstp; p->node = temp_port.node; p->aux = temp_port.aux; p->port_number = temp_port.port_number; p->port_priority = temp_port.port_priority; p->port_id = temp_port.port_id; p->rstp_state = RSTP_DISCARDING; rstp_initialize_port_defaults__(p); VLOG_DBG("%s: RSTP port "RSTP_PORT_ID_FMT" reinitialized.", rstp->name, p->port_id); } void reinitialize_port(struct rstp_port *p) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); reinitialize_port__(p); ovs_mutex_unlock(&rstp_mutex); } /* Sets the port state. */ void rstp_port_set_state__(struct rstp_port *p, enum rstp_state state) OVS_REQUIRES(rstp_mutex) { struct rstp *rstp; rstp = p->rstp; VLOG_DBG("%s, port %u: set RSTP port state %s -> %s", rstp->name, p->port_number, rstp_state_name(p->rstp_state), rstp_state_name(state)); if (state != p->rstp_state && !p->state_changed) { p->state_changed = true; seq_change(connectivity_seq_get()); } p->rstp_state = state; } void rstp_port_set_state(struct rstp_port *p, enum rstp_state state) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_port_set_state__(p, state); ovs_mutex_unlock(&rstp_mutex); } /* Adds a RSTP port. */ struct rstp_port * rstp_add_port(struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { struct rstp_port *p = xzalloc(sizeof *p); ovs_refcount_init(&p->ref_cnt); hmap_node_nullify(&p->node); ovs_mutex_lock(&rstp_mutex); p->rstp = rstp; rstp_port_set_priority__(p, RSTP_DEFAULT_PORT_PRIORITY); rstp_port_set_port_number__(p, 0); p->aux = NULL; rstp_initialize_port_defaults__(p); VLOG_DBG("%s: RSTP port "RSTP_PORT_ID_FMT" initialized.", rstp->name, p->port_id); rstp_port_set_state__(p, RSTP_DISCARDING); rstp->changes = true; move_rstp__(rstp); VLOG_DBG("%s: added port "RSTP_PORT_ID_FMT"", rstp->name, p->port_id); ovs_mutex_unlock(&rstp_mutex); return p; } /* Caller has to hold a reference to prevent 'rstp_port' from being deleted * while taking a new reference. */ struct rstp_port * rstp_port_ref(const struct rstp_port *rp_) OVS_EXCLUDED(rstp_mutex) { struct rstp_port *rp = CONST_CAST(struct rstp_port *, rp_); if (rp) { ovs_refcount_ref(&rp->ref_cnt); } return rp; } /* Frees RSTP struct. This can be caller by any thread. */ void rstp_port_unref(struct rstp_port *rp) OVS_EXCLUDED(rstp_mutex) { if (rp && ovs_refcount_unref_relaxed(&rp->ref_cnt) == 1) { struct rstp *rstp; ovs_mutex_lock(&rstp_mutex); rstp = rp->rstp; rstp_port_set_state__(rp, RSTP_DISABLED); hmap_remove(&rstp->ports, &rp->node); VLOG_DBG("%s: removed port "RSTP_PORT_ID_FMT"", rstp->name, rp->port_id); ovs_mutex_unlock(&rstp_mutex); free(rp); } } /* Sets the port Admin Edge parameter. */ static void rstp_port_set_admin_edge__(struct rstp_port *port, bool admin_edge) OVS_REQUIRES(rstp_mutex) { if (port->admin_edge != admin_edge) { VLOG_DBG("%s, port %u: set RSTP Admin Edge to %d", port->rstp->name, port->port_number, admin_edge); port->admin_edge = admin_edge; } } /* Sets the port Auto Edge parameter. */ static void rstp_port_set_auto_edge__(struct rstp_port *port, bool auto_edge) OVS_REQUIRES(rstp_mutex) { if (port->auto_edge != auto_edge) { VLOG_DBG("%s, port %u: set RSTP Auto Edge to %d", port->rstp->name, port->port_number, auto_edge); port->auto_edge = auto_edge; } } /* Sets the port admin_point_to_point_mac parameter. */ static void rstp_port_set_admin_point_to_point_mac__(struct rstp_port *port, enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state) OVS_REQUIRES(rstp_mutex) { VLOG_DBG("%s, port %u: set RSTP port admin-point-to-point-mac to %d", port->rstp->name, port->port_number, admin_p2p_mac_state); if (port->admin_point_to_point_mac != admin_p2p_mac_state) { if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_TRUE) { port->admin_point_to_point_mac = admin_p2p_mac_state; rstp_port_set_oper_point_to_point_mac__( port, RSTP_OPER_P2P_MAC_STATE_ENABLED); } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_FORCE_FALSE) { port->admin_point_to_point_mac = admin_p2p_mac_state; rstp_port_set_oper_point_to_point_mac__( port, RSTP_OPER_P2P_MAC_STATE_DISABLED); } else if (admin_p2p_mac_state == RSTP_ADMIN_P2P_MAC_AUTO) { /* If adminPointToPointMAC is set to Auto, then the value of * operPointToPointMAC is determined in accordance with the * specific procedures defined for the MAC entity concerned, as * defined in 6.5. If these procedures determine that the MAC * entity is connected to a point-to-point LAN, then * operPointToPointMAC is set TRUE; otherwise it is set FALSE. * In the absence of a specific definition of how to determine * whether the MAC is connected to a point-to-point LAN or not, * the value of operPointToPointMAC shall be FALSE. */ port->admin_point_to_point_mac = admin_p2p_mac_state; rstp_port_set_oper_point_to_point_mac__( port, RSTP_OPER_P2P_MAC_STATE_DISABLED); } } } /* Sets the port mcheck parameter. * [17.19.13] May be set by management to force the Port Protocol Migration * state machine to transmit RST BPDUs for a MigrateTime (17.13.9) period, to * test whether all STP Bridges (17.4) on the attached LAN have been removed * and the Port can continue to transmit RSTP BPDUs. Setting mcheck has no * effect if stpVersion (17.20.12) is TRUE, i.e., the Bridge is operating in * STP Compatibility mode. */ static void rstp_port_set_mcheck__(struct rstp_port *port, bool mcheck) OVS_REQUIRES(rstp_mutex) { if (mcheck == true && port->rstp->force_protocol_version >= 2) { port->mcheck = true; VLOG_DBG("%s, port %u: set RSTP mcheck to %d", port->rstp->name, port->port_number, mcheck); } } /* Returns the designated bridge id. */ rstp_identifier rstp_get_designated_id(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { rstp_identifier designated_id; ovs_mutex_lock(&rstp_mutex); designated_id = rstp->root_priority.designated_bridge_id; ovs_mutex_unlock(&rstp_mutex); return designated_id; } /* Returns the root bridge id. */ rstp_identifier rstp_get_root_id(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { rstp_identifier root_id; ovs_mutex_lock(&rstp_mutex); root_id = rstp->root_priority.root_bridge_id; ovs_mutex_unlock(&rstp_mutex); return root_id; } /* Returns the designated port id. */ uint16_t rstp_get_designated_port_id(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { uint16_t designated_port_id; ovs_mutex_lock(&rstp_mutex); designated_port_id = rstp->root_priority.designated_port_id; ovs_mutex_unlock(&rstp_mutex); return designated_port_id; } /* Return the bridge port id. */ uint16_t rstp_get_bridge_port_id(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { uint16_t bridge_port_id; ovs_mutex_lock(&rstp_mutex); bridge_port_id = rstp->root_priority.bridge_port_id; ovs_mutex_unlock(&rstp_mutex); return bridge_port_id; } /* Returns true if the bridge believes to the be root of the spanning tree, * false otherwise. */ bool rstp_is_root_bridge(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { bool is_root; ovs_mutex_lock(&rstp_mutex); is_root = rstp->bridge_identifier == rstp->root_priority.designated_bridge_id; ovs_mutex_unlock(&rstp_mutex); return is_root; } /* Returns the bridge ID of the bridge currently believed to be the root. */ rstp_identifier rstp_get_designated_root(const struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { rstp_identifier designated_root; ovs_mutex_lock(&rstp_mutex); designated_root = rstp->root_priority.designated_bridge_id; ovs_mutex_unlock(&rstp_mutex); return designated_root; } /* Returns the port connecting 'rstp' to the root bridge, or a null pointer if * there is no such port. */ struct rstp_port * rstp_get_root_port(struct rstp *rstp) OVS_EXCLUDED(rstp_mutex) { struct rstp_port *p; ovs_mutex_lock(&rstp_mutex); HMAP_FOR_EACH (p, node, &rstp->ports) { if (p->port_id == rstp->root_port_id) { ovs_mutex_unlock(&rstp_mutex); return p; } } ovs_mutex_unlock(&rstp_mutex); return NULL; } /* Returns the state of port 'p'. */ enum rstp_state rstp_port_get_state(const struct rstp_port *p) OVS_EXCLUDED(rstp_mutex) { enum rstp_state state; ovs_mutex_lock(&rstp_mutex); state = p->rstp_state; ovs_mutex_unlock(&rstp_mutex); return state; } /* Retrieves port status. */ void rstp_port_get_status(const struct rstp_port *p, uint16_t *id, enum rstp_state *state, enum rstp_port_role *role, rstp_identifier *designated_bridge_id, uint16_t *designated_port_id, uint32_t *designated_path_cost, int *tx_count, int *rx_count, int *error_count, int *uptime) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); *id = p->port_id; *state = p->rstp_state; *role = p->role; *designated_bridge_id = p->port_priority.designated_bridge_id; *designated_port_id = p->port_priority.designated_port_id; *designated_path_cost = p->port_priority.root_path_cost; *tx_count = p->tx_count; *rx_count = p->rx_rstp_bpdu_cnt; *error_count = p->error_count; *uptime = p->uptime; ovs_mutex_unlock(&rstp_mutex); } void rstp_port_set(struct rstp_port *port, uint16_t port_num, int priority, uint32_t path_cost, bool is_admin_edge, bool is_auto_edge, enum rstp_admin_point_to_point_mac_state admin_p2p_mac_state, bool admin_port_state, bool do_mcheck, void *aux) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); port->aux = aux; rstp_port_set_priority__(port, priority); rstp_port_set_port_number__(port, port_num); rstp_port_set_path_cost__(port, path_cost); rstp_port_set_admin_edge__(port, is_admin_edge); rstp_port_set_auto_edge__(port, is_auto_edge); rstp_port_set_admin_point_to_point_mac__(port, admin_p2p_mac_state); rstp_port_set_administrative_bridge_port__(port, admin_port_state, false); rstp_port_set_mcheck__(port, do_mcheck); ovs_mutex_unlock(&rstp_mutex); } /* Individual setters only used by test-rstp.c. */ void rstp_port_set_priority(struct rstp_port *port, int priority) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_port_set_priority__(port, priority); ovs_mutex_unlock(&rstp_mutex); } void rstp_port_set_path_cost(struct rstp_port *port, uint32_t path_cost) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); rstp_port_set_path_cost__(port, path_cost); ovs_mutex_unlock(&rstp_mutex); } void rstp_port_set_aux(struct rstp_port *port, void *aux) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); port->aux = aux; ovs_mutex_unlock(&rstp_mutex); } /* Unixctl. */ static struct rstp * rstp_find(const char *name) OVS_REQUIRES(rstp_mutex) { struct rstp *rstp; LIST_FOR_EACH (rstp, node, all_rstps) { if (!strcmp(rstp->name, name)) { return rstp; } } return NULL; } static void rstp_unixctl_tcn(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(rstp_mutex) { ovs_mutex_lock(&rstp_mutex); if (argc > 1) { struct rstp *rstp = rstp_find(argv[1]); if (!rstp) { unixctl_command_reply_error(conn, "No such RSTP object"); goto out; } rstp->changes = true; move_rstp__(rstp); } else { struct rstp *rstp; LIST_FOR_EACH (rstp, node, all_rstps) { rstp->changes = true; move_rstp__(rstp); } } unixctl_command_reply(conn, "OK"); out: ovs_mutex_unlock(&rstp_mutex); } openvswitch-2.5.9/lib/PaxHeaders.82075/async-append.h0000644000000000000000000000013213534540071017145 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.657852512 openvswitch-2.5.9/lib/async-append.h0000644000175000017500000000403613534540071020636 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ASYNC_APPEND_H #define ASYNC_APPEND_H 1 #include /* This module defines a simple, abstract interface to asynchronous file I/O. * It is currently used only for logging. Thus, for now the interface only * supports appending to a file. Multiple implementations are possible * depending on the operating system's degree and form of support for * asynchronous I/O. * * The comments below document the requirements on any implementation. * * Thread-safety * ============= * * Only a single thread may use a given 'struct async_append' at one time. */ /* Creates and returns a new asynchronous appender for file descriptor 'fd', * which the caller must have opened in append mode (O_APPEND). If the system * is for some reason unable to support asynchronous I/O on 'fd' this function * may return NULL. */ struct async_append *async_append_create(int fd); /* Destroys 'ap', without closing its underlying file descriptor. */ void async_append_destroy(struct async_append *ap); /* Appends the 'size' bytes of 'data' to 'ap', asynchronously if possible. */ void async_append_write(struct async_append *ap, const void *data, size_t size); /* Blocks until all data asynchronously written to 'ap' with * async_append_write() has been committed to the point that it will be written * to disk barring an operating system or hardware failure. */ void async_append_flush(struct async_append *ap); #endif /* async-append.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/timer.c0000644000000000000000000000013113534540071015675 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90985437 openvswitch-2.5.9/lib/timer.c0000644000175000017500000000250613534540071017367 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "timer.h" #include "poll-loop.h" #include "timeval.h" /* Returns the number of milliseconds until 'timer' expires. */ long long int timer_msecs_until_expired(const struct timer *timer) { switch (timer->t) { case LLONG_MAX: return LLONG_MAX; case LLONG_MIN: return 0; default: return timer->t - time_msec(); } } /* Causes poll_block() to wake when 'timer' expires. * * ('where' is used in debug logging. Commonly one would use timer_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ void timer_wait_at(const struct timer *timer, const char *where) { if (timer->t < LLONG_MAX) { poll_timer_wait_until_at(timer->t, where); } } openvswitch-2.5.9/lib/PaxHeaders.82075/backtrace.c0000644000000000000000000000013213534540071016475 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.657852512 openvswitch-2.5.9/lib/backtrace.c0000644000175000017500000000341513534540071020166 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "backtrace.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(backtrace); #ifdef HAVE_BACKTRACE #include void backtrace_capture(struct backtrace *b) { void *frames[BACKTRACE_MAX_FRAMES]; int i; b->n_frames = backtrace(frames, BACKTRACE_MAX_FRAMES); for (i = 0; i < b->n_frames; i++) { b->frames[i] = (uintptr_t) frames[i]; } } #else void backtrace_capture(struct backtrace *backtrace) { backtrace->n_frames = 0; } #endif static char * backtrace_format(const struct backtrace *b, struct ds *ds) { if (b->n_frames) { int i; ds_put_cstr(ds, " (backtrace:"); for (i = 0; i < b->n_frames; i++) { ds_put_format(ds, " 0x%08"PRIxPTR, b->frames[i]); } ds_put_cstr(ds, ")"); } return ds_cstr(ds); } void log_backtrace_at(const char *msg, const char *where) { struct backtrace b; struct ds ds = DS_EMPTY_INITIALIZER; backtrace_capture(&b); if (msg) { ds_put_format(&ds, "%s ", msg); } ds_put_cstr(&ds, where); VLOG_ERR("%s", backtrace_format(&b, &ds)); ds_destroy(&ds); } openvswitch-2.5.9/lib/PaxHeaders.82075/daemon-windows.c0000644000000000000000000000013213534540071017511 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.957854723 openvswitch-2.5.9/lib/daemon-windows.c0000644000175000017500000003571613534540071021213 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "daemon.h" #include "daemon-private.h" #include #include #include "dirs.h" #include "ovs-thread.h" #include "poll-loop.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(daemon_windows); static bool service_create; /* Was --service specified? */ static bool service_started; /* Have we dispatched service to start? */ /* --service-monitor: Should the service be restarted if it dies * unexpectedly? */ static bool monitor; bool detach; /* Was --detach specified? */ static bool detached; /* Running as the child process. */ static HANDLE write_handle; /* End of pipe to write to parent. */ char *pidfile; /* --pidfile: Name of pidfile (null if none). */ static FILE *filep_pidfile; /* File pointer to access the pidfile. */ /* Handle to the Services Manager and the created service. */ static SC_HANDLE manager, service; /* Handle to the status information structure for the current service. */ static SERVICE_STATUS_HANDLE hstatus; /* Hold the service's current status. */ static SERVICE_STATUS service_status; /* Handle to an event object used to wakeup from poll_block(). */ static HANDLE wevent; /* Hold the arguments sent to the main function. */ static int sargc; static char ***sargvp; static void check_service(void); static void handle_scm_callback(void); static void init_service_status(void); static void set_config_failure_actions(void); static bool detach_process(int argc, char *argv[]); extern int main(int argc, char *argv[]); void daemon_usage(void) { printf( "\nService options:\n" " --service run in background as a service.\n" " --service-monitor restart the service in case of an " "unexpected failure. \n", ovs_rundir(), program_name); } /* Registers the call-back and configures the actions in case of a failure * with the Windows services manager. */ void service_start(int *argcp, char **argvp[]) { int argc = *argcp; char **argv = *argvp; int i; SERVICE_TABLE_ENTRY service_table[] = { {(LPTSTR)program_name, (LPSERVICE_MAIN_FUNCTION)main}, {NULL, NULL} }; /* If one of the command line option is "--detach", we create * a new process in case of parent, wait for child to start and exit. * In case of the child, we just return. We should not be creating a * service in either case. */ if (detach_process(argc, argv)) { return; } /* 'service_started' is 'false' when service_start() is called the first * time. It is 'true', when it is called the second time by the Windows * services manager. */ if (service_started) { init_service_status(); wevent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!wevent) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to create a event (%s).", msg_buf); } poll_wevent_wait(wevent); /* Register the control handler. This function is called by the service * manager to stop the service. */ hstatus = RegisterServiceCtrlHandler(program_name, (LPHANDLER_FUNCTION)control_handler); if (!hstatus) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to register the service control handler (%s).", msg_buf); } if (monitor) { set_config_failure_actions(); } /* When the service control manager does the call back, it does not * send the same arguments as sent to the main function during the * service start. So, use the arguments passed over during the first * time. */ *argcp = sargc; *argvp = *sargvp; /* XXX: Windows implementation cannot have a unixctl commands in the * traditional sense of unix domain sockets. If an implementation is * done that involves 'unixctl' vlog commands the following call is * needed to make sure that the unixctl commands for vlog get * registered in a daemon, even before the first log message. */ vlog_init(); return; } assert_single_threaded(); /* A reference to arguments passed to the main function the first time. * We need it after the call-back from service control manager. */ sargc = argc; sargvp = argvp; /* We are only interested in the '--service' and '--service-monitor' * options before the call-back from the service control manager. */ for (i = 0; i < argc; i ++) { if (!strcmp(argv[i], "--service")) { service_create = true; } else if (!strcmp(argv[i], "--service-monitor")) { monitor = true; } } /* If '--service' is not a command line option, run in foreground. */ if (!service_create) { return; } /* If we have been configured to run as a service, then that service * should already have been created either manually or through a start up * script. */ check_service(); service_started = true; /* StartServiceCtrlDispatcher blocks and returns after the service is * stopped. */ if (!StartServiceCtrlDispatcher(service_table)) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed at StartServiceCtrlDispatcher (%s)", msg_buf); } exit(0); } /* This function is registered with the Windows services manager through * a call to RegisterServiceCtrlHandler() and will be called by the Windows * services manager asynchronously to stop the service. */ void control_handler(DWORD request) { switch (request) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: service_status.dwCurrentState = SERVICE_STOPPED; service_status.dwWin32ExitCode = NO_ERROR; SetEvent(wevent); break; default: break; } } /* Return 'true' if the Windows services manager has called the * control_handler() and asked the program to terminate. */ bool should_service_stop(void) { if (service_started) { if (service_status.dwCurrentState != SERVICE_RUNNING) { return true; } else { poll_wevent_wait(wevent); } } return false; } /* Set the service as stopped. The control manager will terminate the * service soon after this call. Hence, this should ideally be the last * call before termination. */ void service_stop() { if (!service_started) { return; } fatal_signal_atexit_handler(); ResetEvent(wevent); CloseHandle(wevent); service_status.dwCurrentState = SERVICE_STOPPED; service_status.dwWin32ExitCode = NO_ERROR; SetServiceStatus(hstatus, &service_status); } /* Call this function to signal that the daemon is ready. init_service() * or control_handler() has already initalized/set the * service_status.dwCurrentState .*/ static void service_complete(void) { if (hstatus) { SetServiceStatus(hstatus, &service_status); } } /* Check whether 'program_name' has been created as a service. */ static void check_service() { /* Establish a connection to the local service control manager. */ manager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); if (!manager) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to open the service control manager (%s).", msg_buf); } service = OpenService(manager, program_name, SERVICE_ALL_ACCESS); if (!service) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to open service (%s).", msg_buf); } } /* Service status of a service can be checked asynchronously through * tools like 'sc' or through Windows services manager and is set * through a call to SetServiceStatus(). */ static void init_service_status() { /* The service runs in its own process. */ service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; /* The control codes the service accepts. */ service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; /* Initialize the current state as SERVICE_RUNNING. */ service_status.dwCurrentState = SERVICE_RUNNING; /* The exit code to indicate if there was an error. */ service_status.dwWin32ExitCode = NO_ERROR; /* The checkpoint value the service increments periodically. Set as 0 * as we do not plan to periodically increment the value. */ service_status.dwCheckPoint = 0; /* The estimated time required for the stop operation in ms. */ service_status.dwWaitHint = 1000; } /* In case of an unexpected termination, configure the action to be * taken. */ static void set_config_failure_actions() { /* In case of a failure, restart the process the first two times * After 'dwResetPeriod', the failure count is reset. */ SC_ACTION fail_action[3] = { {SC_ACTION_RESTART, 0}, {SC_ACTION_RESTART, 0}, {SC_ACTION_NONE, 0} }; SERVICE_FAILURE_ACTIONS service_fail_action; /* Reset failure count after (in seconds). */ service_fail_action.dwResetPeriod = 10; /* Reboot message. */ service_fail_action.lpRebootMsg = NULL; /* The command line of the process. */ service_fail_action.lpCommand = NULL; /* Number of elements in 'fail_actions'. */ service_fail_action.cActions = sizeof(fail_action)/sizeof(fail_action[0]); /* A pointer to an array of SC_ACTION structures. */ service_fail_action.lpsaActions = fail_action; if (!ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &service_fail_action)) { char *msg_buf = ovs_lasterror_to_string(); VLOG_FATAL("Failed to configure service fail actions (%s).", msg_buf); } } /* When a daemon is passed the --detach option, we create a new * process and pass an additional non-documented option called --pipe-handle. * Through this option, the parent passes one end of a pipe handle. */ void set_pipe_handle(const char *pipe_handle) { write_handle = (HANDLE) atoi(pipe_handle); } /* If one of the command line option is "--detach", creates * a new process in case of parent, waits for child to start and exits. * In case of the child, returns. */ static bool detach_process(int argc, char *argv[]) { SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE read_pipe, write_pipe; char *buffer; int error, i; char ch; /* We are only interested in the '--detach' and '--pipe-handle'. */ for (i = 0; i < argc; i ++) { if (!strcmp(argv[i], "--detach")) { detach = true; } else if (!strncmp(argv[i], "--pipe-handle", 13)) { /* If running as a child, return. */ detached = true; return true; } } /* Nothing to do if the option --detach is not set. */ if (!detach) { return false; } /* Set the security attribute such that a process created will * inherit the pipe handles. */ sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; /* Create an anonymous pipe to communicate with the child. */ error = CreatePipe(&read_pipe, &write_pipe, &sa, 0); if (!error) { VLOG_FATAL("CreatePipe failed (%s)", ovs_lasterror_to_string()); } GetStartupInfo(&si); /* To the child, we pass an extra argument '--pipe-handle=write_pipe' */ buffer = xasprintf("%s %s=%ld", GetCommandLine(), "--pipe-handle", write_pipe); /* Create a detached child */ error = CreateProcess(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi); if (!error) { VLOG_FATAL("CreateProcess failed (%s)", ovs_lasterror_to_string()); } /* Close one end of the pipe in the parent. */ CloseHandle(write_pipe); /* Block and wait for child to say it is ready. */ error = ReadFile(read_pipe, &ch, 1, NULL, NULL); if (!error) { VLOG_FATAL("Failed to read from child (%s)", ovs_lasterror_to_string()); } /* The child has successfully started and is ready. */ exit(0); } static void unlink_pidfile(void) { if (filep_pidfile) { fclose(filep_pidfile); } if (pidfile) { unlink(pidfile); } } /* If a pidfile has been configured, creates it and stores the running * process's pid in it. Ensures that the pidfile will be deleted when the * process exits. */ static void make_pidfile(void) { int error; error = GetFileAttributes(pidfile); if (error != INVALID_FILE_ATTRIBUTES) { /* pidfile exists. Try to unlink() it. */ error = unlink(pidfile); if (error) { VLOG_FATAL("Failed to delete existing pidfile %s (%s)", pidfile, ovs_strerror(errno)); } } filep_pidfile = fopen(pidfile, "w"); if (filep_pidfile == NULL) { VLOG_FATAL("failed to open %s (%s)", pidfile, ovs_strerror(errno)); } fatal_signal_add_hook(unlink_pidfile, NULL, NULL, true); fprintf(filep_pidfile, "%d\n", _getpid()); if (fflush(filep_pidfile) == EOF) { VLOG_FATAL("Failed to write into the pidfile %s", pidfile); } /* Don't close the pidfile till the process exits. */ } void daemonize_start(bool access_datapath OVS_UNUSED) { if (pidfile) { make_pidfile(); } } void daemonize_complete(void) { /* If running as a child because '--detach' option was specified, * communicate with the parent to inform that the child is ready. */ if (detached) { int error; close_standard_fds(); error = WriteFile(write_handle, "a", 1, NULL, NULL); if (!error) { VLOG_FATAL("Failed to communicate with the parent (%s)", ovs_lasterror_to_string()); } } service_complete(); } void daemon_become_new_user(bool access_datapath OVS_UNUSED) { } /* Returns the file name that would be used for a pidfile if 'name' were * provided to set_pidfile(). The caller must free the returned string. */ char * make_pidfile_name(const char *name) { if (name && strchr(name, ':')) { return xstrdup(name); } else { return xasprintf("%s/%s.pid", ovs_rundir(), program_name); } } void daemon_set_new_user(const char *user_spec OVS_UNUSED) { VLOG_FATAL("--user options is not currently supported."); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-router.c0000644000000000000000000000013213534540071016703 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 30 ctime=1567801424.829853781 openvswitch-2.5.9/lib/ovs-router.c0000644000175000017500000002450013534540071020372 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovs-router.h" #include #include #include #include #include #include #include #include #include #include #include "classifier.h" #include "command-line.h" #include "compiler.h" #include "dpif.h" #include "dynamic-string.h" #include "netdev.h" #include "packets.h" #include "seq.h" #include "ovs-thread.h" #include "route-table.h" #include "tnl-ports.h" #include "unixctl.h" #include "util.h" static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct classifier cls; struct ovs_router_entry { struct cls_rule cr; char output_bridge[IFNAMSIZ]; struct in6_addr gw; struct in6_addr nw_addr; uint8_t plen; uint8_t priority; }; static struct ovs_router_entry * ovs_router_entry_cast(const struct cls_rule *cr) { if (offsetof(struct ovs_router_entry, cr) == 0) { return CONTAINER_OF(cr, struct ovs_router_entry, cr); } else { return cr ? CONTAINER_OF(cr, struct ovs_router_entry, cr) : NULL; } } bool ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[], struct in6_addr *gw) { const struct cls_rule *cr; struct flow flow = {.ipv6_dst = *ip6_dst}; cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL); if (cr) { struct ovs_router_entry *p = ovs_router_entry_cast(cr); ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ); *gw = p->gw; return true; } return false; } bool ovs_router_lookup4(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw) { struct in6_addr ip6_dst = in6_addr_mapped_ipv4(ip_dst); struct in6_addr gw6; if (ovs_router_lookup(&ip6_dst, output_bridge, &gw6)) { *gw = in6_addr_get_mapped_ipv4(&gw6); return true; } return route_table_fallback_lookup(ip_dst, output_bridge, gw); } static void rt_entry_free(struct ovs_router_entry *p) { cls_rule_destroy(&p->cr); free(p); } static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst, uint8_t plen) { struct in6_addr dst; struct in6_addr mask; mask = ipv6_create_mask(plen); dst = ipv6_addr_bitand(ip6_dst, &mask); memset(match, 0, sizeof *match); match->flow.ipv6_dst = dst; match->wc.masks.ipv6_dst = mask; } static void ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen, const char output_bridge[], const struct in6_addr *gw) { const struct cls_rule *cr; struct ovs_router_entry *p; struct match match; rt_init_match(&match, ip6_dst, plen); p = xzalloc(sizeof *p); ovs_strlcpy(p->output_bridge, output_bridge, sizeof p->output_bridge); if (ipv6_addr_is_set(gw)) { p->gw = *gw; } p->nw_addr = match.flow.ipv6_dst; p->plen = plen; p->priority = priority; /* Longest prefix matches first. */ cls_rule_init(&p->cr, &match, priority); ovs_mutex_lock(&mutex); cr = classifier_replace(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0); ovs_mutex_unlock(&mutex); if (cr) { /* An old rule with the same match was displaced. */ ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); } tnl_port_map_insert_ipdev(output_bridge); seq_change(tnl_conf_seq); } void ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen, const char output_bridge[], const struct in6_addr *gw) { ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw); } static bool __rt_entry_delete(const struct cls_rule *cr) { struct ovs_router_entry *p = ovs_router_entry_cast(cr); tnl_port_map_delete_ipdev(p->output_bridge); /* Remove it. */ cr = classifier_remove(&cls, cr); if (cr) { ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr)); return true; } return false; } static bool rt_entry_delete(uint8_t priority, const struct in6_addr *ip6_dst, uint8_t plen) { const struct cls_rule *cr; struct cls_rule rule; struct match match; bool res = false; rt_init_match(&match, ip6_dst, plen); cls_rule_init(&rule, &match, priority); /* Find the exact rule. */ cr = classifier_find_rule_exactly(&cls, &rule, CLS_MAX_VERSION); if (cr) { ovs_mutex_lock(&mutex); res = __rt_entry_delete(cr); ovs_mutex_unlock(&mutex); } return res; } static bool scan_ipv6_route(const char *s, struct in6_addr *addr, unsigned int *plen) { struct in6_addr mask; char *error; error = ipv6_parse_masked(s, addr, &mask); if (error) { free(error); return false; } if (!ipv6_is_cidr(&mask)) { return false; } *plen = ipv6_count_cidr_bits(&mask); return true; } static bool scan_ipv4_route(const char *s, ovs_be32 *addr, unsigned int *plen) { int len, max_plen, n; int slen = strlen(s); uint8_t *ip = (uint8_t *)addr; *addr = htonl(0); if (!ovs_scan(s, "%"SCNu8"%n", &ip[0], &n)) { return false; } len = n; max_plen = 8; for (int i = 1; i < 4; i++) { if (ovs_scan(s + len, ".%"SCNu8"%n", &ip[i], &n)) { len += n; max_plen += 8; } else { break; } } if (len == slen && max_plen == 32) { *plen = 32; return true; } if (ovs_scan(s + len, "/%u%n", plen, &n) && len + n == slen && *plen <= max_plen) { return true; } return false; } static void ovs_router_add(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { ovs_be32 ip, gw; unsigned int plen; struct in6_addr ip6; struct in6_addr gw6; if (scan_ipv4_route(argv[1], &ip, &plen)) { if (argc > 3) { inet_pton(AF_INET, argv[3], (struct in_addr *)&gw); } else { gw = 0; } in6_addr_set_mapped_ipv4(&ip6, ip); in6_addr_set_mapped_ipv4(&gw6, gw); plen += 96; } else if (scan_ipv6_route(argv[1], &ip6, &plen)) { if (argc > 3) { inet_pton(AF_INET6, argv[3], &gw6); } else { gw6 = in6addr_any; } } else { unixctl_command_reply_error(conn, "Invalid parameters"); return; } ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6); unixctl_command_reply(conn, "OK"); } static void ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { ovs_be32 ip; unsigned int plen; struct in6_addr ip6; if (scan_ipv4_route(argv[1], &ip, &plen)) { in6_addr_set_mapped_ipv4(&ip6, ip); plen += 96; } else if (!scan_ipv6_route(argv[1], &ip6, &plen)) { unixctl_command_reply_error(conn, "Invalid parameters"); return; } if (rt_entry_delete(plen + 32, &ip6, plen)) { unixctl_command_reply(conn, "OK"); seq_change(tnl_conf_seq); } else { unixctl_command_reply_error(conn, "Not found"); } } static void ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ovs_router_entry *rt; struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "Route Table:\n"); CLS_FOR_EACH(rt, cr, &cls) { uint8_t plen; if (rt->priority == rt->plen) { ds_put_format(&ds, "Cached: "); } else { ds_put_format(&ds, "User: "); } ipv6_format_mapped(&rt->nw_addr, &ds); plen = rt->plen; if (IN6_IS_ADDR_V4MAPPED(&rt->nw_addr)) { plen -= 96; } ds_put_format(&ds, "/%"PRIu16" dev %s", plen, rt->output_bridge); if (ipv6_addr_is_set(&rt->gw)) { ds_put_format(&ds, " GW "); ipv6_format_mapped(&rt->gw, &ds); } ds_put_format(&ds, "\n"); } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { ovs_be32 ip; struct in6_addr ip6; unsigned int plen; char iface[IFNAMSIZ]; struct in6_addr gw; if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) { in6_addr_set_mapped_ipv4(&ip6, ip); } else if (!(scan_ipv6_route(argv[1], &ip6, &plen) && plen == 128)) { unixctl_command_reply_error(conn, "Invalid parameters"); return; } if (ovs_router_lookup(&ip6, iface, &gw)) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "gateway "); ipv6_format_mapped(&gw, &ds); ds_put_format(&ds, "\ndev %s\n", iface); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } else { unixctl_command_reply_error(conn, "Not found"); } } void ovs_router_flush(void) { struct ovs_router_entry *rt; ovs_mutex_lock(&mutex); classifier_defer(&cls); CLS_FOR_EACH(rt, cr, &cls) { if (rt->priority == rt->plen) { __rt_entry_delete(&rt->cr); } } classifier_publish(&cls); ovs_mutex_unlock(&mutex); seq_change(tnl_conf_seq); } /* May not be called more than once. */ void ovs_router_init(void) { classifier_init(&cls, NULL); unixctl_command_register("ovs/route/add", "ip_addr/prefix_len out_br_name gw", 2, 3, ovs_router_add, NULL); unixctl_command_register("ovs/route/show", "", 0, 0, ovs_router_show, NULL); unixctl_command_register("ovs/route/del", "ip_addr/prefix_len", 1, 1, ovs_router_del, NULL); unixctl_command_register("ovs/route/lookup", "ip_addr", 1, 1, ovs_router_lookup_cmd, NULL); } openvswitch-2.5.9/lib/PaxHeaders.82075/ofpbuf.h0000644000000000000000000000013213534540071016044 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.805853603 openvswitch-2.5.9/lib/ofpbuf.h0000644000175000017500000002061713534540071017540 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPBUF_H #define OFPBUF_H 1 #include #include #include "list.h" #include "packets.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif enum OVS_PACKED_ENUM ofpbuf_source { OFPBUF_MALLOC, /* Obtained via malloc(). */ OFPBUF_STACK, /* Un-movable stack space or static buffer. */ OFPBUF_STUB, /* Starts on stack, may expand into heap. */ }; /* Buffer for holding arbitrary data. An ofpbuf is automatically reallocated * as necessary if it grows too large for the available memory. * * 'header' and 'msg' conventions: * * OpenFlow messages: 'header' points to the start of the OpenFlow * header, while 'msg' is the OpenFlow msg bofy. * When parsing, the 'data' will move past these, as data is being * pulled from the OpenFlow message. * * Caution: buffer manipulation of 'struct ofpbuf' must always update * the 'header' and 'msg' pointers. * * * Actions: When encoding OVS action lists, the 'header' is used * as a pointer to the beginning of the current action (see ofpact_put()). * * rconn: Reuses 'header' as a private pointer while queuing. */ struct ofpbuf { void *base; /* First byte of allocated space. */ void *data; /* First byte actually in use. */ uint32_t size; /* Number of bytes in use. */ uint32_t allocated; /* Number of bytes allocated. */ void *header; /* OpenFlow header. */ void *msg; /* message's body */ struct ovs_list list_node; /* Private list element for use by owner. */ enum ofpbuf_source source; /* Source of memory allocated as 'base'. */ }; /* An initializer for a struct ofpbuf that will be initially empty and * uses the space in STUB (which should be an array) as a stub. * * Usage example: * * uint64_t stub[1024 / 8]; // 1 kB stub properly aligned for 64-bit data. * struct ofpbuf ofpbuf = OFPBUF_STUB_INITIALIZER(stub); */ #define OFPBUF_STUB_INITIALIZER(STUB) { \ .base = (STUB), \ .data = (STUB), \ .size = 0, \ .allocated = sizeof (STUB), \ .header = NULL, \ .msg = NULL, \ .list_node = OVS_LIST_POISON, \ .source = OFPBUF_STUB, \ } void ofpbuf_use_ds(struct ofpbuf *, const struct ds *); void ofpbuf_use_stack(struct ofpbuf *, void *, size_t); void ofpbuf_use_stub(struct ofpbuf *, void *, size_t); void ofpbuf_use_const(struct ofpbuf *, const void *, size_t); void ofpbuf_init(struct ofpbuf *, size_t); void ofpbuf_uninit(struct ofpbuf *); void ofpbuf_reinit(struct ofpbuf *, size_t); struct ofpbuf *ofpbuf_new(size_t); struct ofpbuf *ofpbuf_new_with_headroom(size_t, size_t headroom); struct ofpbuf *ofpbuf_clone(const struct ofpbuf *); struct ofpbuf *ofpbuf_clone_with_headroom(const struct ofpbuf *, size_t headroom); struct ofpbuf *ofpbuf_clone_data(const void *, size_t); struct ofpbuf *ofpbuf_clone_data_with_headroom(const void *, size_t, size_t headroom); static inline void ofpbuf_delete(struct ofpbuf *); static inline void *ofpbuf_at(const struct ofpbuf *, size_t offset, size_t size); static inline void *ofpbuf_at_assert(const struct ofpbuf *, size_t offset, size_t size); static inline void *ofpbuf_tail(const struct ofpbuf *); static inline void *ofpbuf_end(const struct ofpbuf *); void *ofpbuf_put_uninit(struct ofpbuf *, size_t); void *ofpbuf_put_zeros(struct ofpbuf *, size_t); void *ofpbuf_put(struct ofpbuf *, const void *, size_t); char *ofpbuf_put_hex(struct ofpbuf *, const char *s, size_t *n); void ofpbuf_reserve(struct ofpbuf *, size_t); void *ofpbuf_push_uninit(struct ofpbuf *b, size_t); void *ofpbuf_push_zeros(struct ofpbuf *, size_t); void *ofpbuf_push(struct ofpbuf *b, const void *, size_t); static inline size_t ofpbuf_headroom(const struct ofpbuf *); static inline size_t ofpbuf_tailroom(const struct ofpbuf *); void ofpbuf_prealloc_headroom(struct ofpbuf *, size_t); void ofpbuf_prealloc_tailroom(struct ofpbuf *, size_t); void ofpbuf_trim(struct ofpbuf *); void ofpbuf_padto(struct ofpbuf *, size_t); void ofpbuf_shift(struct ofpbuf *, int); static inline void ofpbuf_clear(struct ofpbuf *); static inline void *ofpbuf_pull(struct ofpbuf *, size_t); static inline void *ofpbuf_try_pull(struct ofpbuf *, size_t); void *ofpbuf_steal_data(struct ofpbuf *); char *ofpbuf_to_string(const struct ofpbuf *, size_t maxbytes); static inline struct ofpbuf *ofpbuf_from_list(const struct ovs_list *); void ofpbuf_list_delete(struct ovs_list *); static inline bool ofpbuf_equal(const struct ofpbuf *, const struct ofpbuf *); /* Frees memory that 'b' points to, as well as 'b' itself. */ static inline void ofpbuf_delete(struct ofpbuf *b) { if (b) { ofpbuf_uninit(b); free(b); } } /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to * byte 'offset'. Otherwise, returns a null pointer. */ static inline void *ofpbuf_at(const struct ofpbuf *b, size_t offset, size_t size) { return offset + size <= b->size ? (char *) b->data + offset : NULL; } /* Returns a pointer to byte 'offset' in 'b', which must contain at least * 'offset + size' bytes of data. */ static inline void *ofpbuf_at_assert(const struct ofpbuf *b, size_t offset, size_t size) { ovs_assert(offset + size <= b->size); return ((char *) b->data) + offset; } /* Returns a pointer to byte following the last byte of data in use in 'b'. */ static inline void *ofpbuf_tail(const struct ofpbuf *b) { return (char *) b->data + b->size; } /* Returns a pointer to byte following the last byte allocated for use (but * not necessarily in use) in 'b'. */ static inline void *ofpbuf_end(const struct ofpbuf *b) { return (char *) b->base + b->allocated; } /* Returns the number of bytes of headroom in 'b', that is, the number of bytes * of unused space in ofpbuf 'b' before the data that is in use. (Most * commonly, the data in a ofpbuf is at its beginning, and thus the ofpbuf's * headroom is 0.) */ static inline size_t ofpbuf_headroom(const struct ofpbuf *b) { return (char*)b->data - (char*)b->base; } /* Returns the number of bytes that may be appended to the tail end of ofpbuf * 'b' before the ofpbuf must be reallocated. */ static inline size_t ofpbuf_tailroom(const struct ofpbuf *b) { return (char*)ofpbuf_end(b) - (char*)ofpbuf_tail(b); } /* Clears any data from 'b'. */ static inline void ofpbuf_clear(struct ofpbuf *b) { b->data = b->base; b->size = 0; } /* Removes 'size' bytes from the head end of 'b', which must contain at least * 'size' bytes of data. Returns the first byte of data removed. */ static inline void *ofpbuf_pull(struct ofpbuf *b, size_t size) { void *data = b->data; b->data = (char*)b->data + size; b->size = b->size - size; return data; } /* If 'b' has at least 'size' bytes of data, removes that many bytes from the * head end of 'b' and returns the first byte removed. Otherwise, returns a * null pointer without modifying 'b'. */ static inline void *ofpbuf_try_pull(struct ofpbuf *b, size_t size) { return b->size >= size ? ofpbuf_pull(b, size) : NULL; } static inline struct ofpbuf *ofpbuf_from_list(const struct ovs_list *list) { return CONTAINER_OF(list, struct ofpbuf, list_node); } static inline bool ofpbuf_equal(const struct ofpbuf *a, const struct ofpbuf *b) { return a->size == b->size && memcmp(a->data, b->data, a->size) == 0; } #ifdef __cplusplus } #endif #endif /* ofpbuf.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofpbuf.c0000644000000000000000000000013213534540071016037 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.805853603 openvswitch-2.5.9/lib/ofpbuf.c0000644000175000017500000003677513534540071017547 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofpbuf.h" #include #include #include "dynamic-string.h" #include "netdev-dpdk.h" #include "util.h" static void ofpbuf_init__(struct ofpbuf *b, size_t allocated, enum ofpbuf_source source) { b->allocated = allocated; b->source = source; b->header = NULL; b->msg = NULL; list_poison(&b->list_node); } static void ofpbuf_use__(struct ofpbuf *b, void *base, size_t allocated, size_t size, enum ofpbuf_source source) { b->base = base; b->data = base; b->size = size; ofpbuf_init__(b, allocated, source); } /* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of * memory starting at 'base'. 'base' should be the first byte of a region * obtained from malloc(). It will be freed (with free()) if 'b' is resized or * freed. */ static void ofpbuf_use(struct ofpbuf *b, void *base, size_t allocated) { ofpbuf_use__(b, base, allocated, 0, OFPBUF_MALLOC); } /* Converts ds into ofpbuf 'b'. 'b' contains the 'ds->allocated' bytes of * memory starting at 'ds->string'. 'ds' should not be modified any more. * The memory allocated for 'ds' will be freed (with free()) if 'b' is * resized or freed. */ void ofpbuf_use_ds(struct ofpbuf *b, const struct ds *ds) { ofpbuf_use__(b, ds->string, ds->allocated + 1, ds->length, OFPBUF_MALLOC); } /* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of * memory starting at 'base'. 'base' should point to a buffer on the stack. * (Nothing actually relies on 'base' being allocated on the stack. It could * be static or malloc()'d memory. But stack space is the most common use * case.) * * 'base' should be appropriately aligned. Using an array of uint32_t or * uint64_t for the buffer is a reasonable way to ensure appropriate alignment * for 32- or 64-bit data. * * An ofpbuf operation that requires reallocating data will assert-fail if this * function was used to initialize it. Thus, one need not call ofpbuf_uninit() * on an ofpbuf initialized by this function (though doing so is harmless), * because it is guaranteed that 'b' does not own any heap-allocated memory. */ void ofpbuf_use_stack(struct ofpbuf *b, void *base, size_t allocated) { ofpbuf_use__(b, base, allocated, 0, OFPBUF_STACK); } /* Initializes 'b' as an empty ofpbuf that contains the 'allocated' bytes of * memory starting at 'base'. 'base' should point to a buffer on the stack. * (Nothing actually relies on 'base' being allocated on the stack. It could * be static or malloc()'d memory. But stack space is the most common use * case.) * * 'base' should be appropriately aligned. Using an array of uint32_t or * uint64_t for the buffer is a reasonable way to ensure appropriate alignment * for 32- or 64-bit data. * * An ofpbuf operation that requires reallocating data will copy the provided * buffer into a malloc()'d buffer. Thus, it is wise to call ofpbuf_uninit() * on an ofpbuf initialized by this function, so that if it expanded into the * heap, that memory is freed. */ void ofpbuf_use_stub(struct ofpbuf *b, void *base, size_t allocated) { ofpbuf_use__(b, base, allocated, 0, OFPBUF_STUB); } /* Initializes 'b' as an ofpbuf whose data starts at 'data' and continues for * 'size' bytes. This is appropriate for an ofpbuf that will be used to * inspect existing data, without moving it around or reallocating it, and * generally without modifying it at all. * * An ofpbuf operation that requires reallocating data will assert-fail if this * function was used to initialize it. */ void ofpbuf_use_const(struct ofpbuf *b, const void *data, size_t size) { ofpbuf_use__(b, CONST_CAST(void *, data), size, size, OFPBUF_STACK); } /* Initializes 'b' as an empty ofpbuf with an initial capacity of 'size' * bytes. */ void ofpbuf_init(struct ofpbuf *b, size_t size) { ofpbuf_use(b, size ? xmalloc(size) : NULL, size); } /* Frees memory that 'b' points to. */ void ofpbuf_uninit(struct ofpbuf *b) { if (b) { if (b->source == OFPBUF_MALLOC) { free(b->base); } } } /* Frees memory that 'b' points to and allocates a new ofpbuf */ void ofpbuf_reinit(struct ofpbuf *b, size_t size) { ofpbuf_uninit(b); ofpbuf_init(b, size); } /* Creates and returns a new ofpbuf with an initial capacity of 'size' * bytes. */ struct ofpbuf * ofpbuf_new(size_t size) { struct ofpbuf *b = xmalloc(sizeof *b); ofpbuf_init(b, size); return b; } /* Creates and returns a new ofpbuf with an initial capacity of 'size + * headroom' bytes, reserving the first 'headroom' bytes as headroom. */ struct ofpbuf * ofpbuf_new_with_headroom(size_t size, size_t headroom) { struct ofpbuf *b = ofpbuf_new(size + headroom); ofpbuf_reserve(b, headroom); return b; } /* Creates and returns a new ofpbuf that initially contains a copy of the * 'buffer->size' bytes of data starting at 'buffer->data' with no headroom or * tailroom. */ struct ofpbuf * ofpbuf_clone(const struct ofpbuf *buffer) { return ofpbuf_clone_with_headroom(buffer, 0); } /* Creates and returns a new ofpbuf whose data are copied from 'buffer'. The * returned ofpbuf will additionally have 'headroom' bytes of headroom. */ struct ofpbuf * ofpbuf_clone_with_headroom(const struct ofpbuf *buffer, size_t headroom) { struct ofpbuf *new_buffer; new_buffer = ofpbuf_clone_data_with_headroom(buffer->data, buffer->size, headroom); if (buffer->header) { uintptr_t data_delta = (char *)new_buffer->data - (char *)buffer->data; new_buffer->header = (char *) buffer->header + data_delta; } new_buffer->msg = buffer->msg; return new_buffer; } /* Creates and returns a new ofpbuf that initially contains a copy of the * 'size' bytes of data starting at 'data' with no headroom or tailroom. */ struct ofpbuf * ofpbuf_clone_data(const void *data, size_t size) { return ofpbuf_clone_data_with_headroom(data, size, 0); } /* Creates and returns a new ofpbuf that initially contains 'headroom' bytes of * headroom followed by a copy of the 'size' bytes of data starting at * 'data'. */ struct ofpbuf * ofpbuf_clone_data_with_headroom(const void *data, size_t size, size_t headroom) { struct ofpbuf *b = ofpbuf_new_with_headroom(size, headroom); ofpbuf_put(b, data, size); return b; } static void ofpbuf_copy__(struct ofpbuf *b, uint8_t *new_base, size_t new_headroom, size_t new_tailroom) { const uint8_t *old_base = b->base; size_t old_headroom = ofpbuf_headroom(b); size_t old_tailroom = ofpbuf_tailroom(b); size_t copy_headroom = MIN(old_headroom, new_headroom); size_t copy_tailroom = MIN(old_tailroom, new_tailroom); memcpy(&new_base[new_headroom - copy_headroom], &old_base[old_headroom - copy_headroom], copy_headroom + b->size + copy_tailroom); } /* Reallocates 'b' so that it has exactly 'new_headroom' and 'new_tailroom' * bytes of headroom and tailroom, respectively. */ static void ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom) { void *new_base, *new_data; size_t new_allocated; new_allocated = new_headroom + b->size + new_tailroom; switch (b->source) { case OFPBUF_MALLOC: if (new_headroom == ofpbuf_headroom(b)) { new_base = xrealloc(b->base, new_allocated); } else { new_base = xmalloc(new_allocated); ofpbuf_copy__(b, new_base, new_headroom, new_tailroom); free(b->base); } break; case OFPBUF_STACK: OVS_NOT_REACHED(); case OFPBUF_STUB: b->source = OFPBUF_MALLOC; new_base = xmalloc(new_allocated); ofpbuf_copy__(b, new_base, new_headroom, new_tailroom); break; default: OVS_NOT_REACHED(); } b->allocated = new_allocated; b->base = new_base; new_data = (char *) new_base + new_headroom; if (b->data != new_data) { if (b->header) { uintptr_t data_delta = (char *) b->header - (char *) b->data; b->header = (char *) new_data + data_delta; } if (b->msg) { uintptr_t data_delta = (char *) b->msg - (char *) b->data; b->msg = (char *) new_data + data_delta; } b->data = new_data; } } /* Ensures that 'b' has room for at least 'size' bytes at its tail end, * reallocating and copying its data if necessary. Its headroom, if any, is * preserved. */ void ofpbuf_prealloc_tailroom(struct ofpbuf *b, size_t size) { if (size > ofpbuf_tailroom(b)) { ofpbuf_resize__(b, ofpbuf_headroom(b), MAX(size, 64)); } } /* Ensures that 'b' has room for at least 'size' bytes at its head, * reallocating and copying its data if necessary. Its tailroom, if any, is * preserved. */ void ofpbuf_prealloc_headroom(struct ofpbuf *b, size_t size) { if (size > ofpbuf_headroom(b)) { ofpbuf_resize__(b, MAX(size, 64), ofpbuf_tailroom(b)); } } /* Trims the size of 'b' to fit its actual content, reducing its headroom and * tailroom to 0, if any. * * Buffers not obtained from malloc() are not resized, since that wouldn't save * any memory. * * Caller needs to updates 'b->header' and 'b->msg' so that they point to the * same locations in the data. (If they pointed into the tailroom or headroom * then they become invalid.) * */ void ofpbuf_trim(struct ofpbuf *b) { if (b->source == OFPBUF_MALLOC && (ofpbuf_headroom(b) || ofpbuf_tailroom(b))) { ofpbuf_resize__(b, 0, 0); } } /* If 'b' is shorter than 'length' bytes, pads its tail out with zeros to that * length. */ void ofpbuf_padto(struct ofpbuf *b, size_t length) { if (b->size < length) { ofpbuf_put_zeros(b, length - b->size); } } /* Shifts all of the data within the allocated space in 'b' by 'delta' bytes. * For example, a 'delta' of 1 would cause each byte of data to move one byte * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would cause each * byte to move one byte backward (from 'p' to 'p-1'). * * If used, user must make sure the 'header' and 'msg' pointers are updated * after shifting. */ void ofpbuf_shift(struct ofpbuf *b, int delta) { ovs_assert(delta > 0 ? delta <= ofpbuf_tailroom(b) : delta < 0 ? -delta <= ofpbuf_headroom(b) : true); if (delta != 0) { char *dst = (char *) b->data + delta; memmove(dst, b->data, b->size); b->data = dst; } } /* Appends 'size' bytes of data to the tail end of 'b', reallocating and * copying its data if necessary. Returns a pointer to the first byte of the * new data, which is left uninitialized. */ void * ofpbuf_put_uninit(struct ofpbuf *b, size_t size) { void *p; ofpbuf_prealloc_tailroom(b, size); p = ofpbuf_tail(b); b->size += size; return p; } /* Appends 'size' zeroed bytes to the tail end of 'b'. Data in 'b' is * reallocated and copied if necessary. Returns a pointer to the first byte of * the data's location in the ofpbuf. */ void * ofpbuf_put_zeros(struct ofpbuf *b, size_t size) { void *dst = ofpbuf_put_uninit(b, size); memset(dst, 0, size); return dst; } /* Appends the 'size' bytes of data in 'p' to the tail end of 'b'. Data in 'b' * is reallocated and copied if necessary. Returns a pointer to the first * byte of the data's location in the ofpbuf. */ void * ofpbuf_put(struct ofpbuf *b, const void *p, size_t size) { void *dst = ofpbuf_put_uninit(b, size); memcpy(dst, p, size); return dst; } /* Parses as many pairs of hex digits as possible (possibly separated by * spaces) from the beginning of 's', appending bytes for their values to 'b'. * Returns the first character of 's' that is not the first of a pair of hex * digits. If 'n' is nonnull, stores the number of bytes added to 'b' in * '*n'. */ char * ofpbuf_put_hex(struct ofpbuf *b, const char *s, size_t *n) { size_t initial_size = b->size; for (;;) { uint8_t byte; bool ok; s += strspn(s, " \t\r\n"); byte = hexits_value(s, 2, &ok); if (!ok) { if (n) { *n = b->size - initial_size; } return CONST_CAST(char *, s); } ofpbuf_put(b, &byte, 1); s += 2; } } /* Reserves 'size' bytes of headroom so that they can be later allocated with * ofpbuf_push_uninit() without reallocating the ofpbuf. */ void ofpbuf_reserve(struct ofpbuf *b, size_t size) { ovs_assert(!b->size); ofpbuf_prealloc_tailroom(b, size); b->data = (char*)b->data + size; } /* Prefixes 'size' bytes to the head end of 'b', reallocating and copying its * data if necessary. Returns a pointer to the first byte of the data's * location in the ofpbuf. The new data is left uninitialized. */ void * ofpbuf_push_uninit(struct ofpbuf *b, size_t size) { ofpbuf_prealloc_headroom(b, size); b->data = (char*)b->data - size; b->size += size; return b->data; } /* Prefixes 'size' zeroed bytes to the head end of 'b', reallocating and * copying its data if necessary. Returns a pointer to the first byte of the * data's location in the ofpbuf. */ void * ofpbuf_push_zeros(struct ofpbuf *b, size_t size) { void *dst = ofpbuf_push_uninit(b, size); memset(dst, 0, size); return dst; } /* Copies the 'size' bytes starting at 'p' to the head end of 'b', reallocating * and copying its data if necessary. Returns a pointer to the first byte of * the data's location in the ofpbuf. */ void * ofpbuf_push(struct ofpbuf *b, const void *p, size_t size) { void *dst = ofpbuf_push_uninit(b, size); memcpy(dst, p, size); return dst; } /* Returns the data in 'b' as a block of malloc()'d memory and frees the buffer * within 'b'. (If 'b' itself was dynamically allocated, e.g. with * ofpbuf_new(), then it should still be freed with, e.g., ofpbuf_delete().) */ void * ofpbuf_steal_data(struct ofpbuf *b) { void *p; if (b->source == OFPBUF_MALLOC && b->data == b->base) { p = b->data; } else { p = xmemdup(b->data, b->size); if (b->source == OFPBUF_MALLOC) { free(b->base); } } b->base = NULL; b->data = NULL; b->header = NULL; b->msg = NULL; return p; } /* Returns a string that describes some of 'b''s metadata plus a hex dump of up * to 'maxbytes' from the start of the buffer. */ char * ofpbuf_to_string(const struct ofpbuf *b, size_t maxbytes) { struct ds s; ds_init(&s); ds_put_format(&s, "size=%"PRIu32", allocated=%"PRIu32", head=%"PRIuSIZE", tail=%"PRIuSIZE"\n", b->size, b->allocated, ofpbuf_headroom(b), ofpbuf_tailroom(b)); ds_put_hex_dump(&s, b->data, MIN(b->size, maxbytes), 0, false); return ds_cstr(&s); } /* Removes each of the "struct ofpbuf"s on 'list' from the list and frees * them. */ void ofpbuf_list_delete(struct ovs_list *list) { struct ofpbuf *b; LIST_FOR_EACH_POP (b, list_node, list) { ofpbuf_delete(b); } } openvswitch-2.5.9/lib/PaxHeaders.82075/aes128.c0000644000000000000000000000013213534540071015561 xustar0030 mtime=1567801401.309680495 30 atime=1567801402.065686047 30 ctime=1567801424.653852483 openvswitch-2.5.9/lib/aes128.c0000644000175000017500000005565513534540071017267 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Based on rijndael.txt by Philip J. Erdelsky, downloaded from * http://www.efgh.com/software/rijndael.htm on September 24, 2009. The * license information there is: "Public domain; no restrictions on use." * The Apache license above applies only to Nicira's modifications to the * original code. */ #include #include "aes128.h" #include "util.h" static const uint32_t Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; static const uint32_t Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; static const uint32_t Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; static const uint32_t Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; static const uint32_t Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; static const uint32_t rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, }; static uint32_t get_u32(const uint8_t *p) { uint32_t p0 = p[0]; uint32_t p1 = p[1]; uint32_t p2 = p[2]; uint32_t p3 = p[3]; return (p0 << 24) | (p1 << 16) | (p2 << 8) | p3; } static void put_u32(uint8_t *p, uint32_t x) { p[0] = x >> 24; p[1] = x >> 16; p[2] = x >> 8; p[3] = x; } /* Expands 128-bit 'key' into the encryption key 'schedule'. */ void aes128_schedule(struct aes128 *aes, const uint8_t key[16]) { uint32_t *rk = aes->rk; int i; rk[0] = get_u32(key); rk[1] = get_u32(key + 4); rk[2] = get_u32(key + 8); rk[3] = get_u32(key + 12); for (i = 0; i < 10; i++, rk += 4) { uint32_t temp = rk[3]; rk[4] = (rk[0] ^ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (Te4[(temp ) & 0xff] & 0x0000ff00) ^ (Te4[(temp >> 24) ] & 0x000000ff) ^ rcon[i]); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; } ovs_assert(rk == &aes->rk[40]); } void aes128_encrypt(const struct aes128 *aes, const void *input_, void *output_) { const uint8_t *input = input_; uint8_t *output = output_; const uint32_t *rk = aes->rk; uint32_t s0, s1, s2, s3; uint32_t t0, t1, t2, t3; int r; /* Map byte array block to cipher state and add initial round key. */ s0 = get_u32(input ) ^ rk[0]; s1 = get_u32(input + 4) ^ rk[1]; s2 = get_u32(input + 8) ^ rk[2]; s3 = get_u32(input + 12) ^ rk[3]; /* 10 full rounds. */ r = 10 / 2; for (;;) { t0 = (Te0[(s0 >> 24) ] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[(s3 ) & 0xff] ^ rk[4]); t1 = (Te0[(s1 >> 24) ] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[(s0 ) & 0xff] ^ rk[5]); t2 = (Te0[(s2 >> 24) ] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[(s1 ) & 0xff] ^ rk[6]); t3 = (Te0[(s3 >> 24) ] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[(s2 ) & 0xff] ^ rk[7]); rk += 8; if (--r == 0) { break; } s0 = (Te0[(t0 >> 24) ] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[(t3 ) & 0xff] ^ rk[0]); s1 = (Te0[(t1 >> 24) ] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[(t0 ) & 0xff] ^ rk[1]); s2 = (Te0[(t2 >> 24) ] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[(t1 ) & 0xff] ^ rk[2]); s3 = (Te0[(t3 >> 24) ] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[(t2 ) & 0xff] ^ rk[3]); } /* Apply last round and map cipher state to byte array block. */ s0 = ((Te4[(t0 >> 24) ] & 0xff000000) ^ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t3 ) & 0xff] & 0x000000ff) ^ rk[0]); put_u32(output , s0); s1 = ((Te4[(t1 >> 24) ] & 0xff000000) ^ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t0 ) & 0xff] & 0x000000ff) ^ rk[1]); put_u32(output + 4, s1); s2 = ((Te4[(t2 >> 24) ] & 0xff000000) ^ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t1 ) & 0xff] & 0x000000ff) ^ rk[2]); put_u32(output + 8, s2); s3 = ((Te4[(t3 >> 24) ] & 0xff000000) ^ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t2 ) & 0xff] & 0x000000ff) ^ rk[3]); put_u32(output + 12, s3); } openvswitch-2.5.9/lib/PaxHeaders.82075/db-ctl-base.h0000644000000000000000000000013213534540071016640 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.697852806 openvswitch-2.5.9/lib/db-ctl-base.h0000644000175000017500000002137213534540071020333 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DB_CTL_BASE_H #define DB_CTL_BASE_H 1 #include "compiler.h" #include "dynamic-string.h" #include "shash.h" struct ctl_context; struct option; struct ovsdb_idl; struct ovsdb_idl_row; struct ovsdb_idl_txn; struct ovsdb_symbol_table; struct table; /* This library module contains the common parts for ovsdb manipulation * (structs, commands and functions). To utilize this module, user must * define the following: * * - the command syntaxes for each command. (See 'struct ctl_command_syntax' * for more info) and regiters them using ctl_register_commands(). * * - the *ctl command context by inheriting the 'struct ctl_context' for * additional commands implemented by user. (See 'struct ctl_context' for * more info) */ /* ctl_fatal() also logs the error, so it is preferred in this file. */ #define ovs_fatal please_use_ctl_fatal_instead_of_ovs_fatal struct ctl_table_class; struct cmd_show_table; void ctl_init(const struct ctl_table_class *tables, const struct cmd_show_table *cmd_show_tables, void (*ctl_exit_func)(int status)); char *ctl_default_db(void); OVS_NO_RETURN void ctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2); /* *ctl command syntax structure, to be defined by each command implementation. * * Execution Path * ============== * * Three stylized functions accompany each of these data structures: * * "pre-run" "run" "post-run" * --------------- ------------ ----------------- * *ctl ->prerequisites ->run ->postprocess * * Any *ctl command implementation should go through the following execution * path: * * 1. parses user command-line input and finds the corresponding syntax * structures. * * 2. calls prerequisites() for getting the columns or tables used by each * command. * * 3. calls run() to execute each command and to generate output. * * 4. calls postprocess() after output has been committed. (Only needed * by 'create' command sofar) * * Execution Context * ================= * * Each of the stylized functions requires the 'struct ctl_context' as input * to provide context e.g. command-line arguments, table to be modified. User * may define more specific context (by inheriting 'struct ctl_context') and * write stylized functions that use it. In that case, CONTAINER_OF() can * be used to cast the generic context to the specific one. * * */ struct ctl_command_syntax { const char *name; /* e.g. "add-br" */ int min_args; /* Min number of arguments following name. */ int max_args; /* Max number of arguments following name. */ /* Names that roughly describe the arguments that the command * uses. These should be similar to the names displayed in the * man page or in the help output. */ const char *arguments; /* If nonnull, calls ovsdb_idl_add_column() or ovsdb_idl_add_table() for * each column or table in ctx->idl that it uses. */ void (*prerequisites)(struct ctl_context *ctx); /* Does the actual work of the command and puts the command's output, if * any, in ctx->output or ctx->table. * * Alternatively, if some prerequisite of the command is not met and the * caller should wait for something to change and then retry, it may set * ctx->try_again to true. (Only the "wait-until" command currently does * this.) */ void (*run)(struct ctl_context *ctx); /* If nonnull, called after the transaction has been successfully * committed. ctx->output is the output from the "run" function, which * this function may modify and otherwise postprocess as needed. (Only the * "create" command currently does any postprocessing.) */ void (*postprocess)(struct ctl_context *ctx); /* A comma-separated list of supported options, e.g. "--a,--b", or the * empty string if the command does not support any options. */ const char *options; enum { RO, RW } mode; /* Does this command modify the database? */ }; /* A command extracted from command-line input plus the structs for * output generation. */ struct ctl_command { /* Data that remains constant after initialization. */ const struct ctl_command_syntax *syntax; int argc; char **argv; struct shash options; /* Data modified by commands. */ struct ds output; struct table *table; }; bool ctl_might_write_to_db(char **argv); const char *ctl_get_db_cmd_usage(void); void ctl_print_commands(void); void ctl_print_options(const struct option *); void ctl_add_cmd_options(struct option **, size_t *n_options_p, size_t *allocated_options_p, int opt_val); void ctl_register_commands(const struct ctl_command_syntax *); struct ctl_command *ctl_parse_commands(int argc, char *argv[], struct shash *local_options, size_t *n_commandsp); /* Sometimes, it is desirable to print the table with weak reference to * rows in a 'cmd_show_table' table. In that case, the 'weak_ref_table' * should be used and user must define all variables. */ struct weak_ref_table { const struct ovsdb_idl_table_class *table; const struct ovsdb_idl_column *name_column; /* This colum must be a weak reference to the owning * 'struct cmd_show_table''s table row. */ const struct ovsdb_idl_column *wref_column; }; /* This struct is for organizing the 'show' command output where: * * - 'table' is the table to show. * * - if 'name_column' is not null, it is used as the name for each row * in 'table'. * * - 'columns[]' allows user to specify the print of additional columns * in 'table'. * * - if 'wref_table' is populated, print 'wref_table.name_column' for * each row in table 'wref_table.table' that has a reference to 'table' * in 'wref_table.wref_column'. Every field must be populated. * * */ struct cmd_show_table { const struct ovsdb_idl_table_class *table; const struct ovsdb_idl_column *name_column; const struct ovsdb_idl_column *columns[3]; /* Seems like a good number. */ const struct weak_ref_table wref_table; }; /* The base context struct for conducting the common database * operations (commands listed in 'db_ctl_commands'). User should * define the per-schema context by inheriting this struct as base. * * Database Caches * =============== * * User may implement caches for contents of the database to facilitate * specific commands. In that case, the common commands defined in * 'db_ctl_commands' that may invalidate the cache must call the * invalidate_cache(). * **/ struct ctl_context { /* Read-only. */ int argc; char **argv; struct shash options; /* Modifiable state. */ struct ds output; struct table *table; struct ovsdb_idl *idl; struct ovsdb_idl_txn *txn; struct ovsdb_symbol_table *symtab; /* For implementation with a cache of the contents of the database, * this function will be called when the database is changed and the * change makes the cache no longer valid. */ void (*invalidate_cache)(struct ctl_context *); /* A command may set this member to true if some prerequisite is not met * and the caller should wait for something to change and then retry. */ bool try_again; }; void ctl_context_init_command(struct ctl_context *, struct ctl_command *); void ctl_context_init(struct ctl_context *, struct ctl_command *, struct ovsdb_idl *, struct ovsdb_idl_txn *, struct ovsdb_symbol_table *, void (*invalidate_cache)(struct ctl_context *)); void ctl_context_done_command(struct ctl_context *, struct ctl_command *); void ctl_context_done(struct ctl_context *, struct ctl_command *); struct ctl_row_id { const struct ovsdb_idl_table_class *table; const struct ovsdb_idl_column *name_column; const struct ovsdb_idl_column *uuid_column; }; struct ctl_table_class { struct ovsdb_idl_table_class *class; struct ctl_row_id row_ids[2]; }; void ctl_set_column(const char *table_name, const struct ovsdb_idl_row *, const char *arg, struct ovsdb_symbol_table *); #endif /* db-ctl-base.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpif.h0000644000000000000000000000013213534540071015505 xustar0030 mtime=1567801401.381681023 30 atime=1567801402.073686105 30 ctime=1567801424.713852925 openvswitch-2.5.9/lib/dpif.h0000644000175000017500000011270413534540071017200 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * dpif, the DataPath InterFace. * * In Open vSwitch terminology, a "datapath" is a flow-based software switch. * A datapath has no intelligence of its own. Rather, it relies entirely on * its client to set up flows. The datapath layer is core to the Open vSwitch * software switch: one could say, without much exaggeration, that everything * in ovs-vswitchd above dpif exists only to make the correct decisions * interacting with dpif. * * Typically, the client of a datapath is the software switch module in * "ovs-vswitchd", but other clients can be written. The "ovs-dpctl" utility * is also a (simple) client. * * * Overview * ======== * * The terms written in quotes below are defined in later sections. * * When a datapath "port" receives a packet, it extracts the headers (the * "flow"). If the datapath's "flow table" contains a "flow entry" matching * the packet, then it executes the "actions" in the flow entry and increments * the flow's statistics. If there is no matching flow entry, the datapath * instead appends the packet to an "upcall" queue. * * * Ports * ===== * * A datapath has a set of ports that are analogous to the ports on an Ethernet * switch. At the datapath level, each port has the following information * associated with it: * * - A name, a short string that must be unique within the host. This is * typically a name that would be familiar to the system administrator, * e.g. "eth0" or "vif1.1", but it is otherwise arbitrary. * * - A 32-bit port number that must be unique within the datapath but is * otherwise arbitrary. The port number is the most important identifier * for a port in the datapath interface. * * - A type, a short string that identifies the kind of port. On a Linux * host, typical types are "system" (for a network device such as eth0), * "internal" (for a simulated port used to connect to the TCP/IP stack), * and "gre" (for a GRE tunnel). * * - A Netlink PID for each upcall reading thread (see "Upcall Queuing and * Ordering" below). * * The dpif interface has functions for adding and deleting ports. When a * datapath implements these (e.g. as the Linux and netdev datapaths do), then * Open vSwitch's ovs-vswitchd daemon can directly control what ports are used * for switching. Some datapaths might not implement them, or implement them * with restrictions on the types of ports that can be added or removed * (e.g. on ESX), on systems where port membership can only be changed by some * external entity. * * Each datapath must have a port, sometimes called the "local port", whose * name is the same as the datapath itself, with port number 0. The local port * cannot be deleted. * * Ports are available as "struct netdev"s. To obtain a "struct netdev *" for * a port named 'name' with type 'port_type', in a datapath of type * 'datapath_type', call netdev_open(name, dpif_port_open_type(datapath_type, * port_type). The netdev can be used to get and set important data related to * the port, such as: * * - MTU (netdev_get_mtu(), netdev_set_mtu()). * * - Ethernet address (netdev_get_etheraddr(), netdev_set_etheraddr()). * * - Statistics such as the number of packets and bytes transmitted and * received (netdev_get_stats()). * * - Carrier status (netdev_get_carrier()). * * - Speed (netdev_get_features()). * * - QoS queue configuration (netdev_get_queue(), netdev_set_queue() and * related functions.) * * - Arbitrary port-specific configuration parameters (netdev_get_config(), * netdev_set_config()). An example of such a parameter is the IP * endpoint for a GRE tunnel. * * * Flow Table * ========== * * The flow table is a collection of "flow entries". Each flow entry contains: * * - A "flow", that is, a summary of the headers in an Ethernet packet. The * flow must be unique within the flow table. Flows are fine-grained * entities that include L2, L3, and L4 headers. A single TCP connection * consists of two flows, one in each direction. * * In Open vSwitch userspace, "struct flow" is the typical way to describe * a flow, but the datapath interface uses a different data format to * allow ABI forward- and backward-compatibility. datapath/README.md * describes the rationale and design. Refer to OVS_KEY_ATTR_* and * "struct ovs_key_*" in include/odp-netlink.h for details. * lib/odp-util.h defines several functions for working with these flows. * * - A "mask" that, for each bit in the flow, specifies whether the datapath * should consider the corresponding flow bit when deciding whether a * given packet matches the flow entry. The original datapath design did * not support matching: every flow entry was exact match. With the * addition of a mask, the interface supports datapaths with a spectrum of * wildcard matching capabilities, from those that only support exact * matches to those that support bitwise wildcarding on the entire flow * key, as well as datapaths with capabilities somewhere in between. * * Datapaths do not provide a way to query their wildcarding capabilities, * nor is it expected that the client should attempt to probe for the * details of their support. Instead, a client installs flows with masks * that wildcard as many bits as acceptable. The datapath then actually * wildcards as many of those bits as it can and changes the wildcard bits * that it does not support into exact match bits. A datapath that can * wildcard any bit, for example, would install the supplied mask, an * exact-match only datapath would install an exact-match mask regardless * of what mask the client supplied, and a datapath in the middle of the * spectrum would selectively change some wildcard bits into exact match * bits. * * Regardless of the requested or installed mask, the datapath retains the * original flow supplied by the client. (It does not, for example, "zero * out" the wildcarded bits.) This allows the client to unambiguously * identify the flow entry in later flow table operations. * * The flow table does not have priorities; that is, all flow entries have * equal priority. Detecting overlapping flow entries is expensive in * general, so the datapath is not required to do it. It is primarily the * client's responsibility not to install flow entries whose flow and mask * combinations overlap. * * - A list of "actions" that tell the datapath what to do with packets * within a flow. Some examples of actions are OVS_ACTION_ATTR_OUTPUT, * which transmits the packet out a port, and OVS_ACTION_ATTR_SET, which * modifies packet headers. Refer to OVS_ACTION_ATTR_* and "struct * ovs_action_*" in include/odp-netlink.h for details. lib/odp-util.h * defines several functions for working with datapath actions. * * The actions list may be empty. This indicates that nothing should be * done to matching packets, that is, they should be dropped. * * (In case you are familiar with OpenFlow, datapath actions are analogous * to OpenFlow actions.) * * - Statistics: the number of packets and bytes that the flow has * processed, the last time that the flow processed a packet, and the * union of all the TCP flags in packets processed by the flow. (The * latter is 0 if the flow is not a TCP flow.) * * The datapath's client manages the flow table, primarily in reaction to * "upcalls" (see below). * * * Upcalls * ======= * * A datapath sometimes needs to notify its client that a packet was received. * The datapath mechanism to do this is called an "upcall". * * Upcalls are used in two situations: * * - When a packet is received, but there is no matching flow entry in its * flow table (a flow table "miss"), this causes an upcall of type * DPIF_UC_MISS. These are called "miss" upcalls. * * - A datapath action of type OVS_ACTION_ATTR_USERSPACE causes an upcall of * type DPIF_UC_ACTION. These are called "action" upcalls. * * An upcall contains an entire packet. There is no attempt to, e.g., copy * only as much of the packet as normally needed to make a forwarding decision. * Such an optimization is doable, but experimental prototypes showed it to be * of little benefit because an upcall typically contains the first packet of a * flow, which is usually short (e.g. a TCP SYN). Also, the entire packet can * sometimes really be needed. * * After a client reads a given upcall, the datapath is finished with it, that * is, the datapath doesn't maintain any lingering state past that point. * * The latency from the time that a packet arrives at a port to the time that * it is received from dpif_recv() is critical in some benchmarks. For * example, if this latency is 1 ms, then a netperf TCP_CRR test, which opens * and closes TCP connections one at a time as quickly as it can, cannot * possibly achieve more than 500 transactions per second, since every * connection consists of two flows with 1-ms latency to set up each one. * * To receive upcalls, a client has to enable them with dpif_recv_set(). A * datapath should generally support being opened multiple times (e.g. so that * one may run "ovs-dpctl show" or "ovs-dpctl dump-flows" while "ovs-vswitchd" * is also running) but need not support more than one of these clients * enabling upcalls at once. * * * Upcall Queuing and Ordering * --------------------------- * * The datapath's client reads upcalls one at a time by calling dpif_recv(). * When more than one upcall is pending, the order in which the datapath * presents upcalls to its client is important. The datapath's client does not * directly control this order, so the datapath implementer must take care * during design. * * The minimal behavior, suitable for initial testing of a datapath * implementation, is that all upcalls are appended to a single queue, which is * delivered to the client in order. * * The datapath should ensure that a high rate of upcalls from one particular * port cannot cause upcalls from other sources to be dropped or unreasonably * delayed. Otherwise, one port conducting a port scan or otherwise initiating * high-rate traffic spanning many flows could suppress other traffic. * Ideally, the datapath should present upcalls from each port in a "round * robin" manner, to ensure fairness. * * The client has no control over "miss" upcalls and no insight into the * datapath's implementation, so the datapath is entirely responsible for * queuing and delivering them. On the other hand, the datapath has * considerable freedom of implementation. One good approach is to maintain a * separate queue for each port, to prevent any given port's upcalls from * interfering with other ports' upcalls. If this is impractical, then another * reasonable choice is to maintain some fixed number of queues and assign each * port to one of them. Ports assigned to the same queue can then interfere * with each other, but not with ports assigned to different queues. Other * approaches are also possible. * * The client has some control over "action" upcalls: it can specify a 32-bit * "Netlink PID" as part of the action. This terminology comes from the Linux * datapath implementation, which uses a protocol called Netlink in which a PID * designates a particular socket and the upcall data is delivered to the * socket's receive queue. Generically, though, a Netlink PID identifies a * queue for upcalls. The basic requirements on the datapath are: * * - The datapath must provide a Netlink PID associated with each port. The * client can retrieve the PID with dpif_port_get_pid(). * * - The datapath must provide a "special" Netlink PID not associated with * any port. dpif_port_get_pid() also provides this PID. (ovs-vswitchd * uses this PID to queue special packets that must not be lost even if a * port is otherwise busy, such as packets used for tunnel monitoring.) * * The minimal behavior of dpif_port_get_pid() and the treatment of the Netlink * PID in "action" upcalls is that dpif_port_get_pid() returns a constant value * and all upcalls are appended to a single queue. * * The preferred behavior is: * * - Each port has a PID that identifies the queue used for "miss" upcalls * on that port. (Thus, if each port has its own queue for "miss" * upcalls, then each port has a different Netlink PID.) * * - "miss" upcalls for a given port and "action" upcalls that specify that * port's Netlink PID add their upcalls to the same queue. The upcalls * are delivered to the datapath's client in the order that the packets * were received, regardless of whether the upcalls are "miss" or "action" * upcalls. * * - Upcalls that specify the "special" Netlink PID are queued separately. * * Multiple threads may want to read upcalls simultaneously from a single * datapath. To support multiple threads well, one extends the above preferred * behavior: * * - Each port has multiple PIDs. The datapath distributes "miss" upcalls * across the PIDs, ensuring that a given flow is mapped in a stable way * to a single PID. * * - For "action" upcalls, the thread can specify its own Netlink PID or * other threads' Netlink PID of the same port for offloading purpose * (e.g. in a "round robin" manner). * * * Packet Format * ============= * * The datapath interface works with packets in a particular form. This is the * form taken by packets received via upcalls (i.e. by dpif_recv()). Packets * supplied to the datapath for processing (i.e. to dpif_execute()) also take * this form. * * A VLAN tag is represented by an 802.1Q header. If the layer below the * datapath interface uses another representation, then the datapath interface * must perform conversion. * * The datapath interface requires all packets to fit within the MTU. Some * operating systems internally process packets larger than MTU, with features * such as TSO and UFO. When such a packet passes through the datapath * interface, it must be broken into multiple MTU or smaller sized packets for * presentation as upcalls. (This does not happen often, because an upcall * typically contains the first packet of a flow, which is usually short.) * * Some operating system TCP/IP stacks maintain packets in an unchecksummed or * partially checksummed state until transmission. The datapath interface * requires all host-generated packets to be fully checksummed (e.g. IP and TCP * checksums must be correct). On such an OS, the datapath interface must fill * in these checksums. * * Packets passed through the datapath interface must be at least 14 bytes * long, that is, they must have a complete Ethernet header. They are not * required to be padded to the minimum Ethernet length. * * * Typical Usage * ============= * * Typically, the client of a datapath begins by configuring the datapath with * a set of ports. Afterward, the client runs in a loop polling for upcalls to * arrive. * * For each upcall received, the client examines the enclosed packet and * figures out what should be done with it. For example, if the client * implements a MAC-learning switch, then it searches the forwarding database * for the packet's destination MAC and VLAN and determines the set of ports to * which it should be sent. In any case, the client composes a set of datapath * actions to properly dispatch the packet and then directs the datapath to * execute those actions on the packet (e.g. with dpif_execute()). * * Most of the time, the actions that the client executed on the packet apply * to every packet with the same flow. For example, the flow includes both * destination MAC and VLAN ID (and much more), so this is true for the * MAC-learning switch example above. In such a case, the client can also * direct the datapath to treat any further packets in the flow in the same * way, using dpif_flow_put() to add a new flow entry. * * Other tasks the client might need to perform, in addition to reacting to * upcalls, include: * * - Periodically polling flow statistics, perhaps to supply to its own * clients. * * - Deleting flow entries from the datapath that haven't been used * recently, to save memory. * * - Updating flow entries whose actions should change. For example, if a * MAC learning switch learns that a MAC has moved, then it must update * the actions of flow entries that sent packets to the MAC at its old * location. * * - Adding and removing ports to achieve a new configuration. * * * Thread-safety * ============= * * Most of the dpif functions are fully thread-safe: they may be called from * any number of threads on the same or different dpif objects. The exceptions * are: * * - dpif_port_poll() and dpif_port_poll_wait() are conditionally * thread-safe: they may be called from different threads only on * different dpif objects. * * - dpif_flow_dump_next() is conditionally thread-safe: It may be called * from different threads with the same 'struct dpif_flow_dump', but all * other parameters must be different for each thread. * * - dpif_flow_dump_done() is conditionally thread-safe: All threads that * share the same 'struct dpif_flow_dump' must have finished using it. * This function must then be called exactly once for a particular * dpif_flow_dump to finish the corresponding flow dump operation. * * - Functions that operate on 'struct dpif_port_dump' are conditionally * thread-safe with respect to those objects. That is, one may dump ports * from any number of threads at once, but each thread must use its own * struct dpif_port_dump. */ #ifndef DPIF_H #define DPIF_H 1 #include #include #include #include "netdev.h" #include "dp-packet.h" #include "openflow/openflow.h" #include "ovs-numa.h" #include "packets.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif struct dpif; struct dpif_class; struct dpif_flow; struct ds; struct flow; struct flow_wildcards; struct nlattr; struct sset; int dp_register_provider(const struct dpif_class *); int dp_unregister_provider(const char *type); void dp_blacklist_provider(const char *type); void dp_enumerate_types(struct sset *types); const char *dpif_normalize_type(const char *); int dp_enumerate_names(const char *type, struct sset *names); void dp_parse_name(const char *datapath_name, char **name, char **type); int dpif_open(const char *name, const char *type, struct dpif **); int dpif_create(const char *name, const char *type, struct dpif **); int dpif_create_and_open(const char *name, const char *type, struct dpif **); void dpif_close(struct dpif *); bool dpif_run(struct dpif *); void dpif_wait(struct dpif *); const char *dpif_name(const struct dpif *); const char *dpif_base_name(const struct dpif *); const char *dpif_type(const struct dpif *); int dpif_delete(struct dpif *); /* Statistics for a dpif as a whole. */ struct dpif_dp_stats { uint64_t n_hit; /* Number of flow table matches. */ uint64_t n_missed; /* Number of flow table misses. */ uint64_t n_lost; /* Number of misses not sent to userspace. */ uint64_t n_flows; /* Number of flows present. */ uint64_t n_mask_hit; /* Number of mega flow masks visited for flow table matches. */ uint32_t n_masks; /* Number of mega flow masks. */ }; int dpif_get_dp_stats(const struct dpif *, struct dpif_dp_stats *); /* Port operations. */ const char *dpif_port_open_type(const char *datapath_type, const char *port_type); int dpif_port_add(struct dpif *, struct netdev *, odp_port_t *port_nop); int dpif_port_del(struct dpif *, odp_port_t port_no); /* A port within a datapath. * * 'name' and 'type' are suitable for passing to netdev_open(). */ struct dpif_port { char *name; /* Network device name, e.g. "eth0". */ char *type; /* Network device type, e.g. "system". */ odp_port_t port_no; /* Port number within datapath. */ }; void dpif_port_clone(struct dpif_port *, const struct dpif_port *); void dpif_port_destroy(struct dpif_port *); bool dpif_port_exists(const struct dpif *dpif, const char *devname); int dpif_port_query_by_number(const struct dpif *, odp_port_t port_no, struct dpif_port *); int dpif_port_query_by_name(const struct dpif *, const char *devname, struct dpif_port *); int dpif_port_get_name(struct dpif *, odp_port_t port_no, char *name, size_t name_size); uint32_t dpif_port_get_pid(const struct dpif *, odp_port_t port_no, uint32_t hash); struct dpif_port_dump { const struct dpif *dpif; int error; void *state; }; void dpif_port_dump_start(struct dpif_port_dump *, const struct dpif *); bool dpif_port_dump_next(struct dpif_port_dump *, struct dpif_port *); int dpif_port_dump_done(struct dpif_port_dump *); /* Iterates through each DPIF_PORT in DPIF, using DUMP as state. * * Arguments all have pointer type. * * If you break out of the loop, then you need to free the dump structure by * hand using dpif_port_dump_done(). */ #define DPIF_PORT_FOR_EACH(DPIF_PORT, DUMP, DPIF) \ for (dpif_port_dump_start(DUMP, DPIF); \ (dpif_port_dump_next(DUMP, DPIF_PORT) \ ? true \ : (dpif_port_dump_done(DUMP), false)); \ ) int dpif_port_poll(const struct dpif *, char **devnamep); void dpif_port_poll_wait(const struct dpif *); /* Flow table operations. */ struct dpif_flow_stats { uint64_t n_packets; uint64_t n_bytes; long long int used; uint16_t tcp_flags; }; void dpif_flow_stats_extract(const struct flow *, const struct dp_packet *packet, long long int used, struct dpif_flow_stats *); void dpif_flow_stats_format(const struct dpif_flow_stats *, struct ds *); enum dpif_flow_put_flags { DPIF_FP_CREATE = 1 << 0, /* Allow creating a new flow. */ DPIF_FP_MODIFY = 1 << 1, /* Allow modifying an existing flow. */ DPIF_FP_ZERO_STATS = 1 << 2, /* Zero the stats of an existing flow. */ DPIF_FP_PROBE = 1 << 3 /* Suppress error messages, if any. */ }; bool dpif_probe_feature(struct dpif *, const char *name, const struct ofpbuf *key, const ovs_u128 *ufid); void dpif_flow_hash(const struct dpif *, const void *key, size_t key_len, ovs_u128 *hash); int dpif_flow_flush(struct dpif *); int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags, const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const struct nlattr *actions, size_t actions_len, const ovs_u128 *ufid, const unsigned pmd_id, struct dpif_flow_stats *); int dpif_flow_del(struct dpif *, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, const unsigned pmd_id, struct dpif_flow_stats *); int dpif_flow_get(struct dpif *, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, const unsigned pmd_id, struct ofpbuf *, struct dpif_flow *); /* Flow dumping interface * ====================== * * This interface allows iteration through all of the flows currently installed * in a datapath. It is somewhat complicated by two requirements: * * - Efficient support for dumping flows in parallel from multiple threads. * * - Allow callers to avoid making unnecessary copies of data returned by * the interface across several flows in cases where the dpif * implementation has to maintain a copy of that information anyhow. * (That is, allow the client visibility into any underlying batching as * part of its own batching.) * * * Usage * ----- * * 1. Call dpif_flow_dump_create(). * 2. In each thread that participates in the dump (which may be just a single * thread if parallelism isn't important): * (a) Call dpif_flow_dump_thread_create(). * (b) Call dpif_flow_dump_next() repeatedly until it returns 0. * (c) Call dpif_flow_dump_thread_destroy(). * 3. Call dpif_flow_dump_destroy(). * * All error reporting is deferred to the call to dpif_flow_dump_destroy(). */ struct dpif_flow_dump *dpif_flow_dump_create(const struct dpif *, bool terse); int dpif_flow_dump_destroy(struct dpif_flow_dump *); struct dpif_flow_dump_thread *dpif_flow_dump_thread_create( struct dpif_flow_dump *); void dpif_flow_dump_thread_destroy(struct dpif_flow_dump_thread *); #define PMD_ID_NULL OVS_CORE_UNSPEC /* A datapath flow as dumped by dpif_flow_dump_next(). */ struct dpif_flow { const struct nlattr *key; /* Flow key, as OVS_KEY_ATTR_* attrs. */ size_t key_len; /* 'key' length in bytes. */ const struct nlattr *mask; /* Flow mask, as OVS_KEY_ATTR_* attrs. */ size_t mask_len; /* 'mask' length in bytes. */ const struct nlattr *actions; /* Actions, as OVS_ACTION_ATTR_ */ size_t actions_len; /* 'actions' length in bytes. */ ovs_u128 ufid; /* Unique flow identifier. */ bool ufid_present; /* True if 'ufid' was provided by datapath.*/ unsigned pmd_id; /* Datapath poll mode driver id. */ struct dpif_flow_stats stats; /* Flow statistics. */ }; int dpif_flow_dump_next(struct dpif_flow_dump_thread *, struct dpif_flow *flows, int max_flows); #define DPIF_FLOW_BUFSIZE 2048 /* Operation batching interface. * * Some datapaths are faster at performing N operations together than the same * N operations individually, hence an interface for batching. */ enum dpif_op_type { DPIF_OP_FLOW_PUT = 1, DPIF_OP_FLOW_DEL, DPIF_OP_EXECUTE, DPIF_OP_FLOW_GET, }; /* Add or modify a flow. * * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in * the 'key_len' bytes starting at 'key'. The associated actions are specified * by the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len' * bytes starting at 'actions'. * * - If the flow's key does not exist in the dpif, then the flow will be * added if 'flags' includes DPIF_FP_CREATE. Otherwise the operation will * fail with ENOENT. * * If the operation succeeds, then 'stats', if nonnull, will be zeroed. * * - If the flow's key does exist in the dpif, then the flow's actions will * be updated if 'flags' includes DPIF_FP_MODIFY. Otherwise the operation * will fail with EEXIST. If the flow's actions are updated, then its * statistics will be zeroed if 'flags' includes DPIF_FP_ZERO_STATS, and * left as-is otherwise. * * If the operation succeeds, then 'stats', if nonnull, will be set to the * flow's statistics before the update. * * - If the datapath implements multiple pmd thread with its own flow * table, 'pmd_id' should be used to specify the particular polling * thread for the operation. */ struct dpif_flow_put { /* Input. */ enum dpif_flow_put_flags flags; /* DPIF_FP_*. */ const struct nlattr *key; /* Flow to put. */ size_t key_len; /* Length of 'key' in bytes. */ const struct nlattr *mask; /* Mask to put. */ size_t mask_len; /* Length of 'mask' in bytes. */ const struct nlattr *actions; /* Actions to perform on flow. */ size_t actions_len; /* Length of 'actions' in bytes. */ const ovs_u128 *ufid; /* Optional unique flow identifier. */ unsigned pmd_id; /* Datapath poll mode driver id. */ /* Output. */ struct dpif_flow_stats *stats; /* Optional flow statistics. */ }; /* Delete a flow. * * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in * the 'key_len' bytes starting at 'key', or the unique identifier 'ufid'. If * the flow was created using 'ufid', then 'ufid' must be specified to delete * the flow. If both are specified, 'key' will be ignored for flow deletion. * Succeeds with status 0 if the flow is deleted, or fails with ENOENT if the * dpif does not contain such a flow. * * Callers should always provide the 'key' to improve dpif logging in the event * of errors or unexpected behaviour. * * If the datapath implements multiple polling thread with its own flow table, * 'pmd_id' should be used to specify the particular polling thread for the * operation. * * If the operation succeeds, then 'stats', if nonnull, will be set to the * flow's statistics before its deletion. */ struct dpif_flow_del { /* Input. */ const struct nlattr *key; /* Flow to delete. */ size_t key_len; /* Length of 'key' in bytes. */ const ovs_u128 *ufid; /* Unique identifier of flow to delete. */ bool terse; /* OK to skip sending/receiving full flow * info? */ unsigned pmd_id; /* Datapath poll mode driver id. */ /* Output. */ struct dpif_flow_stats *stats; /* Optional flow statistics. */ }; /* Executes actions on a specified packet. * * Performs the 'actions_len' bytes of actions in 'actions' on the Ethernet * frame in 'packet' and on the packet metadata in 'md'. May modify both * 'packet' and 'md'. * * Some dpif providers do not implement every action. The Linux kernel * datapath, in particular, does not implement ARP field modification. If * 'needs_help' is true, the dpif layer executes in userspace all of the * actions that it can, and for OVS_ACTION_ATTR_OUTPUT and * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif * implementation. * * This works even if 'actions_len' is too long for a Netlink attribute. */ struct dpif_execute { /* Input. */ const struct nlattr *actions; /* Actions to execute on packet. */ size_t actions_len; /* Length of 'actions' in bytes. */ bool needs_help; bool probe; /* Suppress error messages. */ unsigned int mtu; /* Maximum transmission unit to fragment. 0 if not a fragmented packet */ /* Input, but possibly modified as a side effect of execution. */ struct dp_packet *packet; /* Packet to execute. */ }; /* Queries the dpif for a flow entry. * * The flow is specified by the Netlink attributes with types OVS_KEY_ATTR_* in * the 'key_len' bytes starting at 'key', or the unique identifier 'ufid'. If * the flow was created using 'ufid', then 'ufid' must be specified to fetch * the flow. If both are specified, 'key' will be ignored for the flow query. * 'buffer' must point to an initialized buffer, with a recommended size of * DPIF_FLOW_BUFSIZE bytes. * * On success, 'flow' will be populated with the mask, actions and stats for * the datapath flow corresponding to 'key'. The mask and actions may point * within '*buffer', or may point at RCU-protected data. Therefore, callers * that wish to hold these over quiescent periods must make a copy of these * fields before quiescing. * * Callers should always provide 'key' to improve dpif logging in the event of * errors or unexpected behaviour. * * If the datapath implements multiple polling thread with its own flow table, * 'pmd_id' should be used to specify the particular polling thread for the * operation. * * Succeeds with status 0 if the flow is fetched, or fails with ENOENT if no * such flow exists. Other failures are indicated with a positive errno value. */ struct dpif_flow_get { /* Input. */ const struct nlattr *key; /* Flow to get. */ size_t key_len; /* Length of 'key' in bytes. */ const ovs_u128 *ufid; /* Unique identifier of flow to get. */ unsigned pmd_id; /* Datapath poll mode driver id. */ struct ofpbuf *buffer; /* Storage for output parameters. */ /* Output. */ struct dpif_flow *flow; /* Resulting flow from datapath. */ }; int dpif_execute(struct dpif *, struct dpif_execute *); struct dpif_op { enum dpif_op_type type; int error; union { struct dpif_flow_put flow_put; struct dpif_flow_del flow_del; struct dpif_execute execute; struct dpif_flow_get flow_get; } u; }; void dpif_operate(struct dpif *, struct dpif_op **ops, size_t n_ops); /* Upcalls. */ enum dpif_upcall_type { DPIF_UC_MISS, /* Miss in flow table. */ DPIF_UC_ACTION, /* OVS_ACTION_ATTR_USERSPACE action. */ DPIF_N_UC_TYPES }; const char *dpif_upcall_type_to_string(enum dpif_upcall_type); /* A packet passed up from the datapath to userspace. * * The 'packet', 'key' and 'userdata' may point into data in a buffer * provided by the caller, so the buffer should be released only after the * upcall processing has been finished. * * While being processed, the 'packet' may be reallocated, so the packet must * be separately released with ofpbuf_uninit(). */ struct dpif_upcall { /* All types. */ enum dpif_upcall_type type; struct dp_packet packet; /* Packet data. */ struct nlattr *key; /* Flow key. */ size_t key_len; /* Length of 'key' in bytes. */ ovs_u128 ufid; /* Unique flow identifier for 'key'. */ struct nlattr *mru; /* Maximum receive unit. */ /* DPIF_UC_ACTION only. */ struct nlattr *userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */ struct nlattr *out_tun_key; /* Output tunnel key. */ struct nlattr *actions; /* Argument to OVS_ACTION_ATTR_USERSPACE. */ }; /* A callback to notify higher layer of dpif about to be purged, so that * higher layer could try reacting to this (e.g. grabbing all flow stats * before they are gone). This function is currently implemented only by * dpif-netdev. * * The caller needs to provide the 'aux' pointer passed down by higher * layer from the dpif_register_notify_cb() function and the 'pmd_id' of * the polling thread. */ typedef void dp_purge_callback(void *aux, unsigned pmd_id); void dpif_register_dp_purge_cb(struct dpif *, dp_purge_callback *, void *aux); /* A callback to process an upcall, currently implemented only by dpif-netdev. * * The caller provides the 'packet' and 'flow' to process, the corresponding * 'ufid' as generated by dpif_flow_hash(), the polling thread id 'pmd_id', * the 'type' of the upcall, and if 'type' is DPIF_UC_ACTION then the * 'userdata' attached to the action. * * The callback must fill in 'actions' with the datapath actions to apply to * 'packet'. 'wc' and 'put_actions' will either be both null or both nonnull. * If they are nonnull, then the caller will install a flow entry to process * all future packets that match 'flow' and 'wc'; the callback must store a * wildcard mask suitable for that purpose into 'wc'. If the actions to store * into the flow entry are the same as 'actions', then the callback may leave * 'put_actions' empty; otherwise it must store the desired actions into * 'put_actions'. * * Returns 0 if successful, ENOSPC if the flow limit has been reached and no * flow should be installed, or some otherwise a positive errno value. */ typedef int upcall_callback(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufid, unsigned pmd_id, enum dpif_upcall_type type, const struct nlattr *userdata, struct ofpbuf *actions, struct flow_wildcards *wc, struct ofpbuf *put_actions, void *aux); void dpif_register_upcall_cb(struct dpif *, upcall_callback *, void *aux); int dpif_recv_set(struct dpif *, bool enable); int dpif_handlers_set(struct dpif *, uint32_t n_handlers); int dpif_poll_threads_set(struct dpif *, unsigned int n_rxqs, const char *cmask); int dpif_recv(struct dpif *, uint32_t handler_id, struct dpif_upcall *, struct ofpbuf *); void dpif_recv_purge(struct dpif *); void dpif_recv_wait(struct dpif *, uint32_t handler_id); void dpif_enable_upcall(struct dpif *); void dpif_disable_upcall(struct dpif *); void dpif_print_packet(struct dpif *, struct dpif_upcall *); /* Miscellaneous. */ void dpif_get_netflow_ids(const struct dpif *, uint8_t *engine_type, uint8_t *engine_id); int dpif_queue_to_priority(const struct dpif *, uint32_t queue_id, uint32_t *priority); char *dpif_get_dp_version(const struct dpif *); bool dpif_supports_tnl_push_pop(const struct dpif *); #ifdef __cplusplus } #endif #endif /* dpif.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/uuid.c0000644000000000000000000000013213534540071015524 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.933854546 openvswitch-2.5.9/lib/uuid.c0000644000175000017500000001605213534540071017216 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "uuid.h" #include #include #include #include #include #include #include "aes128.h" #include "entropy.h" #include "ovs-thread.h" #include "sha1.h" #include "timeval.h" #include "util.h" static struct aes128 key; static uint64_t counter[2]; BUILD_ASSERT_DECL(sizeof counter == 16); static void do_init(void); /* * Initialize the UUID module. Aborts the program with an error message if * initialization fails (which should never happen on a properly configured * machine.) * * Currently initialization is only needed by uuid_generate(). uuid_generate() * will automatically call uuid_init() itself, so it's only necessary to call * this function explicitly if you want to abort the program earlier than the * first UUID generation in case of failure. */ void uuid_init(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, do_init); } /* Generates a new random UUID in 'uuid'. * * We go to some trouble to ensure as best we can that the generated UUID has * these properties: * * - Uniqueness. The random number generator is seeded using both the * system clock and the system random number generator, plus a few * other identifiers, which is about as good as we can get in any kind * of simple way. * * - Unpredictability. In some situations it could be bad for an * adversary to be able to guess the next UUID to be generated with some * probability of success. This property may or may not be important * for our purposes, but it is better if we can get it. * * To ensure both of these, we start by taking our seed data and passing it * through SHA-1. We use the result as an AES-128 key. We also generate a * random 16-byte value[*] which we then use as the counter for CTR mode. To * generate a UUID in a manner compliant with the above goals, we merely * increment the counter and encrypt it. * * [*] It is not actually important that the initial value of the counter be * random. AES-128 in counter mode is secure either way. */ void uuid_generate(struct uuid *uuid) { static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; uint64_t copy[2]; uuid_init(); /* Copy out the counter's current value, then increment it. */ ovs_mutex_lock(&mutex); copy[0] = counter[0]; copy[1] = counter[1]; if (++counter[1] == 0) { counter[0]++; } ovs_mutex_unlock(&mutex); /* AES output is exactly 16 bytes, so we encrypt directly into 'uuid'. */ aes128_encrypt(&key, copy, uuid); uuid_set_bits_v4(uuid); } void uuid_set_bits_v4(struct uuid *uuid) { /* Set bits to indicate a random UUID. See RFC 4122 section 4.4. */ uuid->parts[2] &= ~0xc0000000; uuid->parts[2] |= 0x80000000; uuid->parts[1] &= ~0x0000f000; uuid->parts[1] |= 0x00004000; } /* Sets 'uuid' to all-zero-bits. */ void uuid_zero(struct uuid *uuid) { uuid->parts[0] = uuid->parts[1] = uuid->parts[2] = uuid->parts[3] = 0; } /* Returns true if 'uuid' is all zero, otherwise false. */ bool uuid_is_zero(const struct uuid *uuid) { return (!uuid->parts[0] && !uuid->parts[1] && !uuid->parts[2] && !uuid->parts[3]); } /* Compares 'a' and 'b'. Returns a negative value if 'a < b', zero if 'a == * b', or positive if 'a > b'. The ordering is lexicographical order of the * conventional way of writing out UUIDs as strings. */ int uuid_compare_3way(const struct uuid *a, const struct uuid *b) { if (a->parts[0] != b->parts[0]) { return a->parts[0] > b->parts[0] ? 1 : -1; } else if (a->parts[1] != b->parts[1]) { return a->parts[1] > b->parts[1] ? 1 : -1; } else if (a->parts[2] != b->parts[2]) { return a->parts[2] > b->parts[2] ? 1 : -1; } else if (a->parts[3] != b->parts[3]) { return a->parts[3] > b->parts[3] ? 1 : -1; } else { return 0; } } /* Attempts to convert string 's' into a UUID in 'uuid'. Returns true if * successful, which will be the case only if 's' has the exact format * specified by RFC 4122. Returns false on failure. On failure, 'uuid' will * be set to all-zero-bits. */ bool uuid_from_string(struct uuid *uuid, const char *s) { if (!uuid_from_string_prefix(uuid, s)) { return false; } else if (s[UUID_LEN] != '\0') { uuid_zero(uuid); return false; } else { return true; } } /* Same as uuid_from_string() but s[UUID_LEN] is not required to be a null byte * to succeed; that is, 's' need only begin with UUID syntax, not consist * entirely of it. */ bool uuid_from_string_prefix(struct uuid *uuid, const char *s) { /* 0 1 2 3 */ /* 012345678901234567890123456789012345 */ /* ------------------------------------ */ /* 00000000-1111-1111-2222-222233333333 */ bool ok; uuid->parts[0] = hexits_value(s, 8, &ok); if (!ok || s[8] != '-') { goto error; } uuid->parts[1] = hexits_value(s + 9, 4, &ok) << 16; if (!ok || s[13] != '-') { goto error; } uuid->parts[1] += hexits_value(s + 14, 4, &ok); if (!ok || s[18] != '-') { goto error; } uuid->parts[2] = hexits_value(s + 19, 4, &ok) << 16; if (!ok || s[23] != '-') { goto error; } uuid->parts[2] += hexits_value(s + 24, 4, &ok); if (!ok) { goto error; } uuid->parts[3] = hexits_value(s + 28, 8, &ok); if (!ok) { goto error; } return true; error: uuid_zero(uuid); return false; } static void sha1_update_int(struct sha1_ctx *sha1_ctx, uintmax_t x) { sha1_update(sha1_ctx, &x, sizeof x); } static void do_init(void) { uint8_t sha1[SHA1_DIGEST_SIZE]; struct sha1_ctx sha1_ctx; uint8_t random_seed[16]; struct timeval now; /* Get seed data. */ get_entropy_or_die(random_seed, sizeof random_seed); xgettimeofday(&now); /* Convert seed into key. */ sha1_init(&sha1_ctx); sha1_update(&sha1_ctx, random_seed, sizeof random_seed); sha1_update(&sha1_ctx, &now, sizeof now); sha1_update_int(&sha1_ctx, getpid()); #ifndef _WIN32 sha1_update_int(&sha1_ctx, getppid()); sha1_update_int(&sha1_ctx, getuid()); sha1_update_int(&sha1_ctx, getgid()); #endif sha1_final(&sha1_ctx, sha1); /* Generate key. */ BUILD_ASSERT(sizeof sha1 >= 16); aes128_schedule(&key, sha1); /* Generate initial counter. */ get_entropy_or_die(counter, sizeof counter); } openvswitch-2.5.9/lib/PaxHeaders.82075/classifier.h0000644000000000000000000000013213534540071016707 xustar0030 mtime=1567801401.321680583 30 atime=1567801402.069686075 30 ctime=1567801424.673852631 openvswitch-2.5.9/lib/classifier.h0000644000175000017500000005431213534540071020402 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CLASSIFIER_H #define CLASSIFIER_H 1 /* Flow classifier. * * * What? * ===== * * A flow classifier holds any number of "rules", each of which specifies * values to match for some fields or subfields and a priority. Each OpenFlow * table is implemented as a flow classifier. * * The classifier has two primary design goals. The first is obvious: given a * set of packet headers, as quickly as possible find the highest-priority rule * that matches those headers. The following section describes the second * goal. * * * "Un-wildcarding" * ================ * * A primary goal of the flow classifier is to produce, as a side effect of a * packet lookup, a wildcard mask that indicates which bits of the packet * headers were essential to the classification result. Ideally, a 1-bit in * any position of this mask means that, if the corresponding bit in the packet * header were flipped, then the classification result might change. A 0-bit * means that changing the packet header bit would have no effect. Thus, the * wildcarded bits are the ones that played no role in the classification * decision. * * Such a wildcard mask is useful with datapaths that support installing flows * that wildcard fields or subfields. If an OpenFlow lookup for a TCP flow * does not actually look at the TCP source or destination ports, for example, * then the switch may install into the datapath a flow that wildcards the port * numbers, which in turn allows the datapath to handle packets that arrive for * other TCP source or destination ports without additional help from * ovs-vswitchd. This is useful for the Open vSwitch software and, * potentially, for ASIC-based switches as well. * * Some properties of the wildcard mask: * * - "False 1-bits" are acceptable, that is, setting a bit in the wildcard * mask to 1 will never cause a packet to be forwarded the wrong way. * As a corollary, a wildcard mask composed of all 1-bits will always * yield correct (but often needlessly inefficient) behavior. * * - "False 0-bits" can cause problems, so they must be avoided. In the * extreme case, a mask of all 0-bits is only correct if the classifier * contains only a single flow that matches all packets. * * - 0-bits are desirable because they allow the datapath to act more * autonomously, relying less on ovs-vswitchd to process flow setups, * thereby improving performance. * * - We don't know a good way to generate wildcard masks with the maximum * (correct) number of 0-bits. We use various approximations, described * in later sections. * * - Wildcard masks for lookups in a given classifier yield a * non-overlapping set of rules. More specifically: * * Consider an classifier C1 filled with an arbitrary collection of rules * and an empty classifier C2. Now take a set of packet headers H and * look it up in C1, yielding a highest-priority matching rule R1 and * wildcard mask M. Form a new classifier rule R2 out of packet headers * H and mask M, and add R2 to C2 with a fixed priority. If one were to * do this for every possible set of packet headers H, then this * process would not attempt to add any overlapping rules to C2, that is, * any packet lookup using the rules generated by this process matches at * most one rule in C2. * * During the lookup process, the classifier starts out with a wildcard mask * that is all 0-bits, that is, fully wildcarded. As lookup proceeds, each * step tends to add constraints to the wildcard mask, that is, change * wildcarded 0-bits into exact-match 1-bits. We call this "un-wildcarding". * A lookup step that examines a particular field must un-wildcard that field. * In general, un-wildcarding is necessary for correctness but undesirable for * performance. * * * Basic Classifier Design * ======================= * * Suppose that all the rules in a classifier had the same form. For example, * suppose that they all matched on the source and destination Ethernet address * and wildcarded all the other fields. Then the obvious way to implement a * classifier would be a hash table on the source and destination Ethernet * addresses. If new classification rules came along with a different form, * you could add a second hash table that hashed on the fields matched in those * rules. With two hash tables, you look up a given flow in each hash table. * If there are no matches, the classifier didn't contain a match; if you find * a match in one of them, that's the result; if you find a match in both of * them, then the result is the rule with the higher priority. * * This is how the classifier works. In a "struct classifier", each form of * "struct cls_rule" present (based on its ->match.mask) goes into a separate * "struct cls_subtable". A lookup does a hash lookup in every "struct * cls_subtable" in the classifier and tracks the highest-priority match that * it finds. The subtables are kept in a descending priority order according * to the highest priority rule in each subtable, which allows lookup to skip * over subtables that can't possibly have a higher-priority match than already * found. Eliminating lookups through priority ordering aids both classifier * primary design goals: skipping lookups saves time and avoids un-wildcarding * fields that those lookups would have examined. * * One detail: a classifier can contain multiple rules that are identical other * than their priority. When this happens, only the highest priority rule out * of a group of otherwise identical rules is stored directly in the "struct * cls_subtable", with the other almost-identical rules chained off a linked * list inside that highest-priority rule. * * The following sub-sections describe various optimizations over this simple * approach. * * * Staged Lookup (Wildcard Optimization) * ------------------------------------- * * Subtable lookup is performed in ranges defined for struct flow, starting * from metadata (registers, in_port, etc.), then L2 header, L3, and finally * L4 ports. Whenever it is found that there are no matches in the current * subtable, the rest of the subtable can be skipped. * * Staged lookup does not reduce lookup time, and it may increase it, because * it changes a single hash table lookup into multiple hash table lookups. * It reduces un-wildcarding significantly in important use cases. * * * Prefix Tracking (Wildcard Optimization) * --------------------------------------- * * Classifier uses prefix trees ("tries") for tracking the used * address space, enabling skipping classifier tables containing * longer masks than necessary for the given address. This reduces * un-wildcarding for datapath flows in parts of the address space * without host routes, but consulting extra data structures (the * tries) may slightly increase lookup time. * * Trie lookup is interwoven with staged lookup, so that a trie is * searched only when the configured trie field becomes relevant for * the lookup. The trie lookup results are retained so that each trie * is checked at most once for each classifier lookup. * * This implementation tracks the number of rules at each address * prefix for the whole classifier. More aggressive table skipping * would be possible by maintaining lists of tables that have prefixes * at the lengths encountered on tree traversal, or by maintaining * separate tries for subsets of rules separated by metadata fields. * * Prefix tracking is configured via OVSDB "Flow_Table" table, * "fieldspec" column. "fieldspec" is a string map where a "prefix" * key tells which fields should be used for prefix tracking. The * value of the "prefix" key is a comma separated list of field names. * * There is a maximum number of fields that can be enabled for any one * flow table. Currently this limit is 3. * * * Partitioning (Lookup Time and Wildcard Optimization) * ---------------------------------------------------- * * Suppose that a given classifier is being used to handle multiple stages in a * pipeline using "resubmit", with metadata (that is, the OpenFlow 1.1+ field * named "metadata") distinguishing between the different stages. For example, * metadata value 1 might identify ingress rules, metadata value 2 might * identify ACLs, and metadata value 3 might identify egress rules. Such a * classifier is essentially partitioned into multiple sub-classifiers on the * basis of the metadata value. * * The classifier has a special optimization to speed up matching in this * scenario: * * - Each cls_subtable that matches on metadata gets a tag derived from the * subtable's mask, so that it is likely that each subtable has a unique * tag. (Duplicate tags have a performance cost but do not affect * correctness.) * * - For each metadata value matched by any cls_rule, the classifier * constructs a "struct cls_partition" indexed by the metadata value. * The cls_partition has a 'tags' member whose value is the bitwise-OR of * the tags of each cls_subtable that contains any rule that matches on * the cls_partition's metadata value. In other words, struct * cls_partition associates metadata values with subtables that need to * be checked with flows with that specific metadata value. * * Thus, a flow lookup can start by looking up the partition associated with * the flow's metadata, and then skip over any cls_subtable whose 'tag' does * not intersect the partition's 'tags'. (The flow must also be looked up in * any cls_subtable that doesn't match on metadata. We handle that by giving * any such cls_subtable TAG_ALL as its 'tags' so that it matches any tag.) * * Partitioning saves lookup time by reducing the number of subtable lookups. * Each eliminated subtable lookup also reduces the amount of un-wildcarding. * * * Classifier Versioning * ===================== * * Classifier lookups are always done in a specific classifier version, where * a version is defined to be a natural number. * * When a new rule is added to a classifier, it is set to become visible in a * specific version. If the version number used at insert time is larger than * any version number currently used in lookups, the new rule is said to be * invisible to lookups. This means that lookups won't find the rule, but the * rule is immediately available to classifier iterations. * * Similarly, a rule can be marked as to be deleted in a future version. To * delete a rule in a way to not remove the rule before all ongoing lookups are * finished, the rule should be made invisible in a specific version number. * Then, when all the lookups use a later version number, the rule can be * actually removed from the classifier. * * Classifiers can hold duplicate rules (rules with the same match criteria and * priority) when at most one of these duplicates is visible in any given * lookup version. The caller responsible for classifier modifications must * maintain this invariant. * * The classifier supports versioning for two reasons: * * 1. Support for versioned modifications makes it possible to perform an * arbitraty series of classifier changes as one atomic transaction, * where intermediate versions of the classifier are not visible to any * lookups. Also, when a rule is added for a future version, or marked * for removal after the current version, such modifications can be * reverted without any visible effects to any of the current lookups. * * 2. Performance: Adding (or deleting) a large set of rules can, in * pathological cases, have a cost proportional to the number of rules * already in the classifier. When multiple rules are being added (or * deleted) in one go, though, this pathological case cost can be * typically avoided, as long as it is OK for any new rules to be * invisible until the batch change is complete. * * Note that the classifier_replace() function replaces a rule immediately, and * is therefore not safe to use with versioning. It is still available for the * users that do not use versioning. * * * Deferred Publication * ==================== * * Removing large number of rules from classifier can be costly, as the * supporting data structures are teared down, in many cases just to be * re-instantiated right after. In the worst case, as when each rule has a * different match pattern (mask), the maintenance of the match patterns can * have cost O(N^2), where N is the number of different match patterns. To * alleviate this, the classifier supports a "deferred mode", in which changes * in internal data structures needed for future version lookups may not be * fully computed yet. The computation is finalized when the deferred mode is * turned off. * * This feature can be used with versioning such that all changes to future * versions are made in the deferred mode. Then, right before making the new * version visible to lookups, the deferred mode is turned off so that all the * data structures are ready for lookups with the new version number. * * To use deferred publication, first call classifier_defer(). Then, modify * the classifier via additions (classifier_insert() with a specific, future * version number) and deletions (use cls_rule_make_removable_after_version()). * Then call classifier_publish(), and after that, announce the new version * number to be used in lookups. * * * Thread-safety * ============= * * The classifier may safely be accessed by many reader threads concurrently * and by a single writer, or by multiple writers when they guarantee mutually * exlucive access to classifier modifications. * * Since the classifier rules are RCU protected, the rule destruction after * removal from the classifier must be RCU postponed. Also, when versioning is * used, the rule removal itself needs to be typically RCU postponed. In this * case the rule destruction is doubly RCU postponed, i.e., the second * ovsrcu_postpone() call to destruct the rule is called from the first RCU * callback that removes the rule. * * Rules that have never been visible to lookups are an exeption to the above * rule. Such rules can be removed immediately, but their destruction must * still be RCU postponed, as the rule's visibility attribute may be examined * parallel to the rule's removal. */ #include "cmap.h" #include "match.h" #include "meta-flow.h" #include "pvector.h" #include "rculist.h" #include "type-props.h" #ifdef __cplusplus extern "C" { #endif /* Classifier internal data structures. */ struct cls_subtable; struct cls_match; struct trie_node; typedef OVSRCU_TYPE(struct trie_node *) rcu_trie_ptr; /* Prefix trie for a 'field' */ struct cls_trie { const struct mf_field *field; /* Trie field, or NULL. */ rcu_trie_ptr root; /* NULL if none. */ }; typedef uint64_t cls_version_t; #define CLS_MIN_VERSION 0 /* Default version number to use. */ #define CLS_MAX_VERSION (TYPE_MAXIMUM(cls_version_t) - 1) #define CLS_NOT_REMOVED_VERSION TYPE_MAXIMUM(cls_version_t) enum { CLS_MAX_INDICES = 3, /* Maximum number of lookup indices per subtable. */ CLS_MAX_TRIES = 3 /* Maximum number of prefix trees per classifier. */ }; /* A flow classifier. */ struct classifier { int n_rules; /* Total number of rules. */ uint8_t n_flow_segments; uint8_t flow_segments[CLS_MAX_INDICES]; /* Flow segment boundaries to use * for staged lookup. */ struct cmap subtables_map; /* Contains "struct cls_subtable"s. */ struct pvector subtables; struct cmap partitions; /* Contains "struct cls_partition"s. */ struct cls_trie tries[CLS_MAX_TRIES]; /* Prefix tries. */ unsigned int n_tries; bool publish; /* Make changes visible to lookups? */ }; struct cls_conjunction { uint32_t id; uint8_t clause; uint8_t n_clauses; }; /* A rule to be inserted to the classifier. */ struct cls_rule { struct rculist node; /* In struct cls_subtable 'rules_list'. */ const int priority; /* Larger numbers are higher priorities. */ OVSRCU_TYPE(struct cls_match *) cls_match; /* NULL if not in a * classifier. */ const struct minimatch match; /* Matching rule. */ }; void cls_rule_init(struct cls_rule *, const struct match *, int priority); void cls_rule_init_from_minimatch(struct cls_rule *, const struct minimatch *, int priority); void cls_rule_clone(struct cls_rule *, const struct cls_rule *); void cls_rule_move(struct cls_rule *dst, struct cls_rule *src); void cls_rule_destroy(struct cls_rule *); void cls_rule_set_conjunctions(struct cls_rule *, const struct cls_conjunction *, size_t n); bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *); void cls_rule_format(const struct cls_rule *, struct ds *); bool cls_rule_is_catchall(const struct cls_rule *); bool cls_rule_is_loose_match(const struct cls_rule *rule, const struct minimatch *criteria); bool cls_rule_visible_in_version(const struct cls_rule *, cls_version_t); void cls_rule_make_invisible_in_version(const struct cls_rule *, cls_version_t); void cls_rule_restore_visibility(const struct cls_rule *); /* Constructor/destructor. Must run single-threaded. */ void classifier_init(struct classifier *, const uint8_t *flow_segments); void classifier_destroy(struct classifier *); /* Modifiers. Caller MUST exclude concurrent calls from other threads. */ bool classifier_set_prefix_fields(struct classifier *, const enum mf_field_id *trie_fields, unsigned int n_trie_fields); void classifier_insert(struct classifier *, const struct cls_rule *, cls_version_t, const struct cls_conjunction *, size_t n_conjunctions); const struct cls_rule *classifier_replace(struct classifier *, const struct cls_rule *, cls_version_t, const struct cls_conjunction *, size_t n_conjunctions); const struct cls_rule *classifier_remove(struct classifier *, const struct cls_rule *); static inline void classifier_defer(struct classifier *); static inline void classifier_publish(struct classifier *); /* Lookups. These are RCU protected and may run concurrently with modifiers * and each other. */ const struct cls_rule *classifier_lookup(const struct classifier *, cls_version_t, struct flow *, struct flow_wildcards *); bool classifier_rule_overlaps(const struct classifier *, const struct cls_rule *, cls_version_t); const struct cls_rule *classifier_find_rule_exactly(const struct classifier *, const struct cls_rule *, cls_version_t); const struct cls_rule *classifier_find_match_exactly(const struct classifier *, const struct match *, int priority, cls_version_t); bool classifier_is_empty(const struct classifier *); int classifier_count(const struct classifier *); /* Iteration. * * Iteration is lockless and RCU-protected. Concurrent threads may perform all * kinds of concurrent modifications without ruining the iteration. Obviously, * any modifications may or may not be visible to the concurrent iterator, but * all the rules not deleted are visited by the iteration. The iterating * thread may also modify the classifier rules itself. * * 'TARGET' iteration only iterates rules matching the 'TARGET' criteria. * Rather than looping through all the rules and skipping ones that can't * match, 'TARGET' iteration skips whole subtables, if the 'TARGET' happens to * be more specific than the subtable. */ struct cls_cursor { const struct classifier *cls; const struct cls_subtable *subtable; const struct cls_rule *target; cls_version_t version; /* Version to iterate. */ struct pvector_cursor subtables; const struct cls_rule *rule; }; struct cls_cursor cls_cursor_start(const struct classifier *, const struct cls_rule *target, cls_version_t); void cls_cursor_advance(struct cls_cursor *); #define CLS_FOR_EACH(RULE, MEMBER, CLS) \ CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, NULL, CLS_MAX_VERSION) #define CLS_FOR_EACH_TARGET(RULE, MEMBER, CLS, TARGET, VERSION) \ for (struct cls_cursor cursor__ = cls_cursor_start(CLS, TARGET, VERSION); \ (cursor__.rule \ ? (INIT_CONTAINER(RULE, cursor__.rule, MEMBER), \ cls_cursor_advance(&cursor__), \ true) \ : false); \ ) static inline void classifier_defer(struct classifier *cls) { cls->publish = false; } static inline void classifier_publish(struct classifier *cls) { cls->publish = true; pvector_publish(&cls->subtables); } #ifdef __cplusplus } #endif #endif /* classifier.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpctl.c0000644000000000000000000000013013534540071015662 xustar0028 mtime=1567801401.3376807 30 atime=1567801402.069686075 30 ctime=1567801424.701852836 openvswitch-2.5.9/lib/dpctl.c0000644000175000017500000014452713534540071017367 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "ct-dpif.h" #include "dirs.h" #include "dpctl.h" #include "dpif.h" #include "dynamic-string.h" #include "flow.h" #include "match.h" #include "netdev.h" #include "netdev-dpdk.h" #include "netlink.h" #include "odp-util.h" #include "ofp-parse.h" #include "ofpbuf.h" #include "ovs-numa.h" #include "packets.h" #include "shash.h" #include "simap.h" #include "smap.h" #include "sset.h" #include "timeval.h" #include "unixctl.h" #include "util.h" typedef int dpctl_command_handler(int argc, const char *argv[], struct dpctl_params *); struct dpctl_command { const char *name; const char *usage; int min_args; int max_args; dpctl_command_handler *handler; }; static const struct dpctl_command *get_all_dpctl_commands(void); static void dpctl_print(struct dpctl_params *dpctl_p, const char *fmt, ...) OVS_PRINTF_FORMAT(2, 3); static void dpctl_error(struct dpctl_params* dpctl_p, int err_no, const char *fmt, ...) OVS_PRINTF_FORMAT(3, 4); static void dpctl_puts(struct dpctl_params *dpctl_p, bool error, const char *string) { dpctl_p->output(dpctl_p->aux, error, string); } static void dpctl_print(struct dpctl_params *dpctl_p, const char *fmt, ...) { char *string; va_list args; va_start(args, fmt); string = xvasprintf(fmt, args); va_end(args); dpctl_puts(dpctl_p, false, string); free(string); } static void dpctl_error(struct dpctl_params* dpctl_p, int err_no, const char *fmt, ...) { const char *subprogram_name = get_subprogram_name(); struct ds ds = DS_EMPTY_INITIALIZER; int save_errno = errno; va_list args; if (subprogram_name[0]) { ds_put_format(&ds, "%s(%s): ", program_name,subprogram_name); } else { ds_put_format(&ds, "%s: ", program_name); } va_start(args, fmt); ds_put_format_valist(&ds, fmt, args); va_end(args); if (err_no != 0) { ds_put_format(&ds, " (%s)", ovs_retval_to_string(err_no)); } ds_put_cstr(&ds, "\n"); dpctl_puts(dpctl_p, true, ds_cstr(&ds)); ds_destroy(&ds); errno = save_errno; } static int dpctl_add_if(int argc, const char *argv[], struct dpctl_params *); static int if_up(struct netdev *netdev) { return netdev_turn_flags_on(netdev, NETDEV_UP, NULL); } /* Retrieve the name of the datapath if exactly one exists. The caller * is responsible for freeing the returned string. If there is not one * datapath, aborts with an error message. */ static char * get_one_dp(struct dpctl_params *dpctl_p) { struct sset types; const char *type; char *dp_name = NULL; size_t count = 0; sset_init(&types); dp_enumerate_types(&types); SSET_FOR_EACH (type, &types) { struct sset names; sset_init(&names); if (!dp_enumerate_names(type, &names)) { count += sset_count(&names); if (!dp_name && count == 1) { dp_name = xasprintf("%s@%s", type, SSET_FIRST(&names)); } } sset_destroy(&names); } sset_destroy(&types); if (!count) { dpctl_error(dpctl_p, 0, "no datapaths exist"); } else if (count > 1) { dpctl_error(dpctl_p, 0, "multiple datapaths, specify one"); free(dp_name); dp_name = NULL; } return dp_name; } static int parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp) { int result; char *name, *type; dp_parse_name(arg_, &name, &type); if (create) { result = dpif_create(name, type, dpifp); } else { result = dpif_open(name, type, dpifp); } free(name); free(type); return result; } static int dpctl_add_dp(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; int error; error = parsed_dpif_open(argv[1], true, &dpif); if (error) { dpctl_error(dpctl_p, error, "add_dp"); return error; } dpif_close(dpif); if (argc > 2) { error = dpctl_add_if(argc, argv, dpctl_p); } return error; } static int dpctl_del_dp(int argc OVS_UNUSED, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; int error; error = parsed_dpif_open(argv[1], false, &dpif); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } error = dpif_delete(dpif); if (error) { dpctl_error(dpctl_p, error, "del_dp"); } dpif_close(dpif); return error; } static int dpctl_add_if(int argc OVS_UNUSED, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; int i, error, lasterror = 0; error = parsed_dpif_open(argv[1], false, &dpif); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } for (i = 2; i < argc; i++) { const char *name, *type; char *save_ptr = NULL, *argcopy; struct netdev *netdev = NULL; struct smap args; odp_port_t port_no = ODPP_NONE; char *option; argcopy = xstrdup(argv[i]); name = strtok_r(argcopy, ",", &save_ptr); type = "system"; if (!name) { dpctl_error(dpctl_p, 0, "%s is not a valid network device name", argv[i]); error = EINVAL; goto next; } smap_init(&args); while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) { char *save_ptr_2 = NULL; char *key, *value; key = strtok_r(option, "=", &save_ptr_2); value = strtok_r(NULL, "", &save_ptr_2); if (!value) { value = ""; } if (!strcmp(key, "type")) { type = value; } else if (!strcmp(key, "port_no")) { port_no = u32_to_odp(atoi(value)); } else if (!smap_add_once(&args, key, value)) { dpctl_error(dpctl_p, 0, "duplicate \"%s\" option", key); } } error = netdev_open(name, type, &netdev); if (error) { dpctl_error(dpctl_p, error, "%s: failed to open network device", name); goto next_destroy_args; } error = netdev_set_config(netdev, &args, NULL); if (error) { goto next_destroy_args; } error = dpif_port_add(dpif, netdev, &port_no); if (error) { dpctl_error(dpctl_p, error, "adding %s to %s failed", name, argv[1]); goto next_destroy_args; } error = if_up(netdev); if (error) { dpctl_error(dpctl_p, error, "%s: failed bringing interface up", name); } next_destroy_args: netdev_close(netdev); smap_destroy(&args); next: free(argcopy); if (error) { lasterror = error; } } dpif_close(dpif); return lasterror; } static int dpctl_set_if(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; int i, error, lasterror = 0; error = parsed_dpif_open(argv[1], false, &dpif); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } for (i = 2; i < argc; i++) { struct netdev *netdev = NULL; struct dpif_port dpif_port; char *save_ptr = NULL; char *type = NULL; char *argcopy; const char *name; struct smap args; odp_port_t port_no; char *option; int error = 0; argcopy = xstrdup(argv[i]); name = strtok_r(argcopy, ",", &save_ptr); if (!name) { dpctl_error(dpctl_p, 0, "%s is not a valid network device name", argv[i]); goto next; } /* Get the port's type from the datapath. */ error = dpif_port_query_by_name(dpif, name, &dpif_port); if (error) { dpctl_error(dpctl_p, error, "%s: failed to query port in %s", name, argv[1]); goto next; } type = xstrdup(dpif_port.type); port_no = dpif_port.port_no; dpif_port_destroy(&dpif_port); /* Retrieve its existing configuration. */ error = netdev_open(name, type, &netdev); if (error) { dpctl_error(dpctl_p, error, "%s: failed to open network device", name); goto next; } smap_init(&args); error = netdev_get_config(netdev, &args); if (error) { dpctl_error(dpctl_p, error, "%s: failed to fetch configuration", name); goto next_destroy_args; } /* Parse changes to configuration. */ while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) { char *save_ptr_2 = NULL; char *key, *value; key = strtok_r(option, "=", &save_ptr_2); value = strtok_r(NULL, "", &save_ptr_2); if (!value) { value = ""; } if (!strcmp(key, "type")) { if (strcmp(value, type)) { dpctl_error(dpctl_p, 0, "%s: can't change type from %s to %s", name, type, value); error = EINVAL; goto next_destroy_args; } } else if (!strcmp(key, "port_no")) { if (port_no != u32_to_odp(atoi(value))) { dpctl_error(dpctl_p, 0, "%s: can't change port number from" " %"PRIu32" to %d", name, port_no, atoi(value)); error = EINVAL; goto next_destroy_args; } } else if (value[0] == '\0') { smap_remove(&args, key); } else { smap_replace(&args, key, value); } } /* Update configuration. */ char *err_s = NULL; error = netdev_set_config(netdev, &args, &err_s); if (err_s || error) { dpctl_error(dpctl_p, error, "%s", err_s ? err_s : "Error updating configuration"); free(err_s); } if (error) { goto next_destroy_args; } next_destroy_args: smap_destroy(&args); next: netdev_close(netdev); free(type); free(argcopy); if (error) { lasterror = error; } } dpif_close(dpif); return lasterror; } static bool get_port_number(struct dpif *dpif, const char *name, odp_port_t *port, struct dpctl_params *dpctl_p) { struct dpif_port dpif_port; if (!dpif_port_query_by_name(dpif, name, &dpif_port)) { *port = dpif_port.port_no; dpif_port_destroy(&dpif_port); return true; } else { dpctl_error(dpctl_p, 0, "no port named %s", name); return false; } } static int dpctl_del_if(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; int i, error, lasterror = 0; error = parsed_dpif_open(argv[1], false, &dpif); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } for (i = 2; i < argc; i++) { const char *name = argv[i]; odp_port_t port; if (!name[strspn(name, "0123456789")]) { port = u32_to_odp(atoi(name)); } else if (!get_port_number(dpif, name, &port, dpctl_p)) { lasterror = ENOENT; continue; } error = dpif_port_del(dpif, port); if (error) { dpctl_error(dpctl_p, error, "deleting port %s from %s failed", name, argv[1]); lasterror = error; } } dpif_close(dpif); return lasterror; } static void print_stat(struct dpctl_params *dpctl_p, const char *leader, uint64_t value) { dpctl_print(dpctl_p, "%s", leader); if (value != UINT64_MAX) { dpctl_print(dpctl_p, "%"PRIu64, value); } else { dpctl_print(dpctl_p, "?"); } } static void print_human_size(struct dpctl_params *dpctl_p, uint64_t value) { if (value == UINT64_MAX) { /* Nothing to do. */ } else if (value >= 1024ULL * 1024 * 1024 * 1024) { dpctl_print(dpctl_p, " (%.1f TiB)", value / (1024.0 * 1024 * 1024 * 1024)); } else if (value >= 1024ULL * 1024 * 1024) { dpctl_print(dpctl_p, " (%.1f GiB)", value / (1024.0 * 1024 * 1024)); } else if (value >= 1024ULL * 1024) { dpctl_print(dpctl_p, " (%.1f MiB)", value / (1024.0 * 1024)); } else if (value >= 1024) { dpctl_print(dpctl_p, " (%.1f KiB)", value / 1024.0); } } static void show_dpif(struct dpif *dpif, struct dpctl_params *dpctl_p) { struct dpif_port_dump dump; struct dpif_port dpif_port; struct dpif_dp_stats stats; struct netdev *netdev; dpctl_print(dpctl_p, "%s:\n", dpif_name(dpif)); if (!dpif_get_dp_stats(dpif, &stats)) { dpctl_print(dpctl_p, "\tlookups: hit:%"PRIu64" missed:%"PRIu64 " lost:%"PRIu64"\n\tflows: %"PRIu64"\n", stats.n_hit, stats.n_missed, stats.n_lost, stats.n_flows); if (stats.n_masks != UINT32_MAX) { uint64_t n_pkts = stats.n_hit + stats.n_missed; double avg = n_pkts ? (double) stats.n_mask_hit / n_pkts : 0.0; dpctl_print(dpctl_p, "\tmasks: hit:%"PRIu64" total:%"PRIu32 " hit/pkt:%.2f\n", stats.n_mask_hit, stats.n_masks, avg); } } DPIF_PORT_FOR_EACH (&dpif_port, &dump, dpif) { dpctl_print(dpctl_p, "\tport %u: %s", dpif_port.port_no, dpif_port.name); if (strcmp(dpif_port.type, "system")) { int error; dpctl_print(dpctl_p, " (%s", dpif_port.type); error = netdev_open(dpif_port.name, dpif_port.type, &netdev); if (!error) { struct smap config; smap_init(&config); error = netdev_get_config(netdev, &config); if (!error) { const struct smap_node **nodes; size_t i; nodes = smap_sort(&config); for (i = 0; i < smap_count(&config); i++) { const struct smap_node *node = nodes[i]; dpctl_print(dpctl_p, "%c %s=%s", i ? ',' : ':', node->key, node->value); } free(nodes); } else { dpctl_print(dpctl_p, ", could not retrieve configuration " "(%s)", ovs_strerror(error)); } smap_destroy(&config); netdev_close(netdev); } else { dpctl_print(dpctl_p, ": open failed (%s)", ovs_strerror(error)); } dpctl_print(dpctl_p, ")"); } dpctl_print(dpctl_p, "\n"); if (dpctl_p->print_statistics) { struct netdev_stats s; int error; error = netdev_open(dpif_port.name, dpif_port.type, &netdev); if (error) { dpctl_print(dpctl_p, ", open failed (%s)", ovs_strerror(error)); continue; } error = netdev_get_stats(netdev, &s); if (!error) { netdev_close(netdev); print_stat(dpctl_p, "\t\tRX packets:", s.rx_packets); print_stat(dpctl_p, " errors:", s.rx_errors); print_stat(dpctl_p, " dropped:", s.rx_dropped); print_stat(dpctl_p, " overruns:", s.rx_over_errors); print_stat(dpctl_p, " frame:", s.rx_frame_errors); dpctl_print(dpctl_p, "\n"); print_stat(dpctl_p, "\t\tTX packets:", s.tx_packets); print_stat(dpctl_p, " errors:", s.tx_errors); print_stat(dpctl_p, " dropped:", s.tx_dropped); print_stat(dpctl_p, " aborted:", s.tx_aborted_errors); print_stat(dpctl_p, " carrier:", s.tx_carrier_errors); dpctl_print(dpctl_p, "\n"); print_stat(dpctl_p, "\t\tcollisions:", s.collisions); dpctl_print(dpctl_p, "\n"); print_stat(dpctl_p, "\t\tRX bytes:", s.rx_bytes); print_human_size(dpctl_p, s.rx_bytes); print_stat(dpctl_p, " TX bytes:", s.tx_bytes); print_human_size(dpctl_p, s.tx_bytes); dpctl_print(dpctl_p, "\n"); } else { dpctl_print(dpctl_p, ", could not retrieve stats (%s)", ovs_strerror(error)); } } } } typedef void (*dps_for_each_cb)(struct dpif *, struct dpctl_params *); static int dps_for_each(struct dpctl_params *dpctl_p, dps_for_each_cb cb) { struct sset dpif_names = SSET_INITIALIZER(&dpif_names), dpif_types = SSET_INITIALIZER(&dpif_types); int error, openerror = 0, enumerror = 0; const char *type, *name; bool at_least_one = false; dp_enumerate_types(&dpif_types); SSET_FOR_EACH (type, &dpif_types) { error = dp_enumerate_names(type, &dpif_names); if (error) { enumerror = error; } SSET_FOR_EACH (name, &dpif_names) { struct dpif *dpif; at_least_one = true; error = dpif_open(name, type, &dpif); if (!error) { cb(dpif, dpctl_p); dpif_close(dpif); } else { openerror = error; dpctl_error(dpctl_p, error, "opening datapath %s failed", name); } } } sset_destroy(&dpif_names); sset_destroy(&dpif_types); /* If there has been an error while opening a datapath it should be * reported. Otherwise, we want to ignore the errors generated by * dp_enumerate_names() if at least one datapath has been discovered, * because they're not interesting for the user. This happens, for * example, if OVS is using a userspace datapath and the kernel module * is not loaded. */ if (openerror) { return openerror; } else { return at_least_one ? 0 : enumerror; } } static int dpctl_show(int argc, const char *argv[], struct dpctl_params *dpctl_p) { int error, lasterror = 0; if (argc > 1) { int i; for (i = 1; i < argc; i++) { const char *name = argv[i]; struct dpif *dpif; error = parsed_dpif_open(name, false, &dpif); if (!error) { show_dpif(dpif, dpctl_p); dpif_close(dpif); } else { dpctl_error(dpctl_p, error, "opening datapath %s failed", name); lasterror = error; } } } else { lasterror = dps_for_each(dpctl_p, show_dpif); } return lasterror; } static void dump_cb(struct dpif *dpif, struct dpctl_params *dpctl_p) { dpctl_print(dpctl_p, "%s\n", dpif_name(dpif)); } static int dpctl_dump_dps(int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, struct dpctl_params *dpctl_p) { return dps_for_each(dpctl_p, dump_cb); } static void format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports, struct dpctl_params *dpctl_p) { if (dpctl_p->verbosity && f->ufid_present) { odp_format_ufid(&f->ufid, ds); ds_put_cstr(ds, ", "); } odp_flow_format(f->key, f->key_len, f->mask, f->mask_len, ports, ds, dpctl_p->verbosity); ds_put_cstr(ds, ", "); dpif_flow_stats_format(&f->stats, ds); ds_put_cstr(ds, ", actions:"); format_odp_actions(ds, f->actions, f->actions_len); } static int dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; struct ds ds; char *name; char *filter = NULL; struct flow flow_filter; struct flow_wildcards wc_filter; struct dpif_port_dump port_dump; struct dpif_port dpif_port; struct hmap portno_names; struct simap names_portno; struct dpif_flow_dump_thread *flow_dump_thread; struct dpif_flow_dump *flow_dump; struct dpif_flow f; int pmd_id = PMD_ID_NULL; int error; if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) { filter = xstrdup(argv[--argc] + 7); } name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!name) { error = EINVAL; goto out_freefilter; } error = parsed_dpif_open(name, false, &dpif); free(name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); goto out_freefilter; } hmap_init(&portno_names); simap_init(&names_portno); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) { odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name); simap_put(&names_portno, dpif_port.name, odp_to_u32(dpif_port.port_no)); } if (filter) { char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks, filter, &names_portno); if (err) { dpctl_error(dpctl_p, 0, "Failed to parse filter (%s)", err); error = EINVAL; goto out_dpifclose; } } /* Make sure that these values are different. PMD_ID_NULL means that the * pmd is unspecified (e.g. because the datapath doesn't have different * pmd threads), while NON_PMD_CORE_ID refers to every non pmd threads * in the userspace datapath */ BUILD_ASSERT(PMD_ID_NULL != NON_PMD_CORE_ID); ds_init(&ds); flow_dump = dpif_flow_dump_create(dpif, false); flow_dump_thread = dpif_flow_dump_thread_create(flow_dump); while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) { if (filter) { struct flow flow; struct flow_wildcards wc; struct match match, match_filter; struct minimatch minimatch; odp_flow_key_to_flow(f.key, f.key_len, &flow); odp_flow_key_to_mask(f.mask, f.mask_len, f.key, f.key_len, &wc, &flow); match_init(&match, &flow, &wc); match_init(&match_filter, &flow_filter, &wc); match_init(&match_filter, &match_filter.flow, &wc_filter); minimatch_init(&minimatch, &match_filter); if (!minimatch_matches_flow(&minimatch, &match.flow)) { minimatch_destroy(&minimatch); continue; } minimatch_destroy(&minimatch); } ds_clear(&ds); /* If 'pmd_id' is specified, overlapping flows could be dumped from * different pmd threads. So, separates dumps from different pmds * by printing a title line. */ if (pmd_id != f.pmd_id) { if (f.pmd_id == NON_PMD_CORE_ID) { ds_put_format(&ds, "flow-dump from non-dpdk interfaces:\n"); } else { ds_put_format(&ds, "flow-dump from pmd on cpu core: %d\n", f.pmd_id); } pmd_id = f.pmd_id; } format_dpif_flow(&ds, &f, &portno_names, dpctl_p); dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds)); } dpif_flow_dump_thread_destroy(flow_dump_thread); error = dpif_flow_dump_destroy(flow_dump); if (error) { dpctl_error(dpctl_p, error, "Failed to dump flows from datapath"); } ds_destroy(&ds); out_dpifclose: odp_portno_names_destroy(&portno_names); simap_destroy(&names_portno); hmap_destroy(&portno_names); dpif_close(dpif); out_freefilter: free(filter); return error; } /* Extracts the in_port from the parsed keys, and returns the reference * to the 'struct netdev *' of the dpif port. On error, returns NULL. * Users must call 'netdev_close()' after finish using the returned * reference. */ static struct netdev * get_in_port_netdev_from_key(struct dpif *dpif, const struct ofpbuf *key) { const struct nlattr *in_port_nla; struct netdev *dev = NULL; in_port_nla = nl_attr_find(key, 0, OVS_KEY_ATTR_IN_PORT); if (in_port_nla) { struct dpif_port dpif_port; odp_port_t port_no; int error; port_no = ODP_PORT_C(nl_attr_get_u32(in_port_nla)); error = dpif_port_query_by_number(dpif, port_no, &dpif_port); if (error) { goto out; } netdev_open(dpif_port.name, dpif_port.type, &dev); dpif_port_destroy(&dpif_port); } out: return dev; } static int dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags, struct dpctl_params *dpctl_p) { const char *key_s = argv[argc - 2]; const char *actions_s = argv[argc - 1]; struct netdev *in_port_netdev = NULL; struct dpif_flow_stats stats; struct dpif_port dpif_port; struct dpif_port_dump port_dump; struct ofpbuf actions; struct ofpbuf key; struct ofpbuf mask; struct dpif *dpif; ovs_u128 ufid; bool ufid_present; char *dp_name; struct simap port_names; int n, error; dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!dp_name) { return EINVAL; } error = parsed_dpif_open(dp_name, false, &dpif); free(dp_name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } ufid_present = false; n = odp_ufid_from_string(key_s, &ufid); if (n < 0) { dpctl_error(dpctl_p, -n, "parsing flow ufid"); return -n; } else if (n) { key_s += n; ufid_present = true; } simap_init(&port_names); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) { simap_put(&port_names, dpif_port.name, odp_to_u32(dpif_port.port_no)); } ofpbuf_init(&key, 0); ofpbuf_init(&mask, 0); error = odp_flow_from_string(key_s, &port_names, &key, &mask); simap_destroy(&port_names); if (error) { dpctl_error(dpctl_p, error, "parsing flow key"); goto out_freekeymask; } ofpbuf_init(&actions, 0); error = odp_actions_from_string(actions_s, NULL, &actions); if (error) { dpctl_error(dpctl_p, error, "parsing actions"); goto out_freeactions; } /* For DPDK interface, applies the operation to all pmd threads * on the same numa node. */ in_port_netdev = get_in_port_netdev_from_key(dpif, &key); if (in_port_netdev && netdev_is_pmd(in_port_netdev)) { int numa_id; numa_id = netdev_get_numa_id(in_port_netdev); if (ovs_numa_numa_id_is_valid(numa_id)) { struct ovs_numa_dump *dump = ovs_numa_dump_cores_on_numa(numa_id); struct ovs_numa_info *iter; FOR_EACH_CORE_ON_NUMA (iter, dump) { if (ovs_numa_core_is_pinned(iter->core_id)) { error = dpif_flow_put(dpif, flags, key.data, key.size, mask.size == 0 ? NULL : mask.data, mask.size, actions.data, actions.size, ufid_present ? &ufid : NULL, iter->core_id, dpctl_p->print_statistics ? &stats : NULL); } } ovs_numa_dump_destroy(dump); } else { error = EINVAL; } } else { error = dpif_flow_put(dpif, flags, key.data, key.size, mask.size == 0 ? NULL : mask.data, mask.size, actions.data, actions.size, ufid_present ? &ufid : NULL, PMD_ID_NULL, dpctl_p->print_statistics ? &stats : NULL); } if (error) { dpctl_error(dpctl_p, error, "updating flow table"); goto out_freeactions; } if (dpctl_p->print_statistics) { struct ds s; ds_init(&s); dpif_flow_stats_format(&stats, &s); dpctl_print(dpctl_p, "%s\n", ds_cstr(&s)); ds_destroy(&s); } out_freeactions: ofpbuf_uninit(&actions); out_freekeymask: ofpbuf_uninit(&mask); ofpbuf_uninit(&key); dpif_close(dpif); netdev_close(in_port_netdev); return error; } static int dpctl_add_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p) { return dpctl_put_flow(argc, argv, DPIF_FP_CREATE, dpctl_p); } static int dpctl_mod_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p) { enum dpif_flow_put_flags flags; flags = DPIF_FP_MODIFY; if (dpctl_p->may_create) { flags |= DPIF_FP_CREATE; } if (dpctl_p->zero_statistics) { flags |= DPIF_FP_ZERO_STATS; } return dpctl_put_flow(argc, argv, flags, dpctl_p); } static int dpctl_get_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p) { const char *key_s = argv[argc - 1]; struct dpif_flow flow; struct dpif_port dpif_port; struct dpif_port_dump port_dump; struct dpif *dpif; char *dp_name; struct hmap portno_names; ovs_u128 ufid; struct ofpbuf buf; uint64_t stub[DPIF_FLOW_BUFSIZE / 8]; struct ds ds; int n, error; dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!dp_name) { return EINVAL; } error = parsed_dpif_open(dp_name, false, &dpif); free(dp_name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } ofpbuf_use_stub(&buf, &stub, sizeof stub); hmap_init(&portno_names); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) { odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name); } n = odp_ufid_from_string(key_s, &ufid); if (n <= 0) { dpctl_error(dpctl_p, -n, "parsing flow ufid"); goto out; } /* Does not work for DPDK, since do not know which 'pmd' to apply the * operation. So, just uses PMD_ID_NULL. */ error = dpif_flow_get(dpif, NULL, 0, &ufid, PMD_ID_NULL, &buf, &flow); if (error) { dpctl_error(dpctl_p, error, "getting flow"); goto out; } ds_init(&ds); format_dpif_flow(&ds, &flow, &portno_names, dpctl_p); dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds)); ds_destroy(&ds); out: odp_portno_names_destroy(&portno_names); hmap_destroy(&portno_names); ofpbuf_uninit(&buf); dpif_close(dpif); return error; } static int dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p) { const char *key_s = argv[argc - 1]; struct netdev *in_port_netdev = NULL; struct dpif_flow_stats stats; struct dpif_port dpif_port; struct dpif_port_dump port_dump; struct ofpbuf key; struct ofpbuf mask; /* To be ignored. */ struct dpif *dpif; ovs_u128 ufid; bool ufid_present; char *dp_name; struct simap port_names; int n, error; dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!dp_name) { return EINVAL; } error = parsed_dpif_open(dp_name, false, &dpif); free(dp_name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } ufid_present = false; n = odp_ufid_from_string(key_s, &ufid); if (n < 0) { dpctl_error(dpctl_p, -n, "parsing flow ufid"); return -n; } else if (n) { key_s += n; ufid_present = true; } simap_init(&port_names); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) { simap_put(&port_names, dpif_port.name, odp_to_u32(dpif_port.port_no)); } ofpbuf_init(&key, 0); ofpbuf_init(&mask, 0); error = odp_flow_from_string(key_s, &port_names, &key, &mask); if (error) { dpctl_error(dpctl_p, error, "parsing flow key"); goto out; } /* For DPDK interface, applies the operation to all pmd threads * on the same numa node. */ in_port_netdev = get_in_port_netdev_from_key(dpif, &key); if (in_port_netdev && netdev_is_pmd(in_port_netdev)) { int numa_id; numa_id = netdev_get_numa_id(in_port_netdev); if (ovs_numa_numa_id_is_valid(numa_id)) { struct ovs_numa_dump *dump = ovs_numa_dump_cores_on_numa(numa_id); struct ovs_numa_info *iter; FOR_EACH_CORE_ON_NUMA (iter, dump) { if (ovs_numa_core_is_pinned(iter->core_id)) { error = dpif_flow_del(dpif, key.data, key.size, ufid_present ? &ufid : NULL, iter->core_id, dpctl_p->print_statistics ? &stats : NULL); } } ovs_numa_dump_destroy(dump); } else { error = EINVAL; } } else { error = dpif_flow_del(dpif, key.data, key.size, ufid_present ? &ufid : NULL, PMD_ID_NULL, dpctl_p->print_statistics ? &stats : NULL); } if (error) { dpctl_error(dpctl_p, error, "deleting flow"); if (error == ENOENT && !ufid_present) { struct ds s; ds_init(&s); ds_put_format(&s, "Perhaps you need to specify a UFID?"); dpctl_print(dpctl_p, "%s\n", ds_cstr(&s)); ds_destroy(&s); } goto out; } if (dpctl_p->print_statistics) { struct ds s; ds_init(&s); dpif_flow_stats_format(&stats, &s); dpctl_print(dpctl_p, "%s\n", ds_cstr(&s)); ds_destroy(&s); } out: ofpbuf_uninit(&mask); ofpbuf_uninit(&key); simap_destroy(&port_names); dpif_close(dpif); netdev_close(in_port_netdev); return error; } static int dpctl_del_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; char *name; int error; name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!name) { return EINVAL; } error = parsed_dpif_open(name, false, &dpif); free(name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } error = dpif_flow_flush(dpif); if (error) { dpctl_error(dpctl_p, error, "deleting all flows"); } dpif_close(dpif); return error; } static int dpctl_help(int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, struct dpctl_params *dpctl_p) { if (dpctl_p->usage) { dpctl_p->usage(dpctl_p->aux); } return 0; } static int dpctl_list_commands(int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, struct dpctl_params *dpctl_p) { struct ds ds = DS_EMPTY_INITIALIZER; const struct dpctl_command *commands = get_all_dpctl_commands(); ds_put_cstr(&ds, "The available commands are:\n"); for (; commands->name; commands++) { const struct dpctl_command *c = commands; ds_put_format(&ds, " %s%-23s %s\n", dpctl_p->is_appctl ? "dpctl/" : "", c->name, c->usage); } dpctl_puts(dpctl_p, false, ds.string); ds_destroy(&ds); return 0; } static int dpctl_dump_conntrack(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct ct_dpif_dump_state *dump; struct ct_dpif_entry cte; uint16_t zone, *pzone = NULL; struct dpif *dpif; char *name; int error; if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) { pzone = &zone; argc--; } name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!name) { return EINVAL; } error = parsed_dpif_open(name, false, &dpif); free(name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } error = ct_dpif_dump_start(dpif, &dump, pzone); if (error) { dpctl_error(dpctl_p, error, "starting conntrack dump"); dpif_close(dpif); return error; } while (!ct_dpif_dump_next(dump, &cte)) { struct ds s = DS_EMPTY_INITIALIZER; ct_dpif_format_entry(&cte, &s, dpctl_p->verbosity, dpctl_p->print_statistics); ct_dpif_entry_uninit(&cte); dpctl_print(dpctl_p, "%s\n", ds_cstr(&s)); ds_destroy(&s); } ct_dpif_dump_done(dump); dpif_close(dpif); return error; } static int dpctl_flush_conntrack(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct dpif *dpif; uint16_t zone, *pzone = NULL; char *name; int error; if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) { pzone = &zone; argc--; } name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p); if (!name) { return EINVAL; } error = parsed_dpif_open(name, false, &dpif); free(name); if (error) { dpctl_error(dpctl_p, error, "opening datapath"); return error; } error = ct_dpif_flush(dpif, pzone); dpif_close(dpif); return error; } /* Undocumented commands for unit testing. */ static int dpctl_parse_actions(int argc, const char *argv[], struct dpctl_params* dpctl_p) { int i, error = 0; for (i = 1; i < argc; i++) { struct ofpbuf actions; struct ds s; ofpbuf_init(&actions, 0); error = odp_actions_from_string(argv[i], NULL, &actions); if (error) { ofpbuf_uninit(&actions); dpctl_error(dpctl_p, error, "odp_actions_from_string"); return error; } ds_init(&s); format_odp_actions(&s, actions.data, actions.size); dpctl_print(dpctl_p, "%s\n", ds_cstr(&s)); ds_destroy(&s); ofpbuf_uninit(&actions); } return error; } struct actions_for_flow { struct hmap_node hmap_node; struct flow flow; struct ofpbuf actions; }; static struct actions_for_flow * get_actions_for_flow(struct hmap *actions_per_flow, const struct flow *flow) { uint32_t hash = flow_hash(flow, 0); struct actions_for_flow *af; HMAP_FOR_EACH_WITH_HASH (af, hmap_node, hash, actions_per_flow) { if (flow_equal(&af->flow, flow)) { return af; } } af = xmalloc(sizeof *af); af->flow = *flow; ofpbuf_init(&af->actions, 0); hmap_insert(actions_per_flow, &af->hmap_node, hash); return af; } static int compare_actions_for_flow(const void *a_, const void *b_) { struct actions_for_flow *const *a = a_; struct actions_for_flow *const *b = b_; return flow_compare_3way(&(*a)->flow, &(*b)->flow); } static int compare_output_actions(const void *a_, const void *b_) { const struct nlattr *a = a_; const struct nlattr *b = b_; uint32_t a_port = nl_attr_get_u32(a); uint32_t b_port = nl_attr_get_u32(b); return a_port < b_port ? -1 : a_port > b_port; } static void sort_output_actions__(struct nlattr *first, struct nlattr *end) { size_t bytes = (uint8_t *) end - (uint8_t *) first; size_t n = bytes / NL_A_U32_SIZE; ovs_assert(bytes % NL_A_U32_SIZE == 0); qsort(first, n, NL_A_U32_SIZE, compare_output_actions); } static void sort_output_actions(struct nlattr *actions, size_t length) { struct nlattr *first_output = NULL; struct nlattr *a; int left; NL_ATTR_FOR_EACH (a, left, actions, length) { if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT) { if (!first_output) { first_output = a; } } else { if (first_output) { sort_output_actions__(first_output, a); first_output = NULL; } } } if (first_output) { uint8_t *end = (uint8_t *) actions + length; sort_output_actions__(first_output, ALIGNED_CAST(struct nlattr *, end)); } } /* usage: "ovs-dpctl normalize-actions FLOW ACTIONS" where FLOW and ACTIONS * have the syntax used by "ovs-dpctl dump-flows". * * This command prints ACTIONS in a format that shows what happens for each * VLAN, independent of the order of the ACTIONS. For example, there is more * than one way to output a packet on VLANs 9 and 11, but this command will * print the same output for any form. * * The idea here generalizes beyond VLANs (e.g. to setting other fields) but * so far the implementation only covers VLANs. */ static int dpctl_normalize_actions(int argc, const char *argv[], struct dpctl_params *dpctl_p) { struct simap port_names; struct ofpbuf keybuf; struct flow flow; struct ofpbuf odp_actions; struct hmap actions_per_flow; struct actions_for_flow **afs; struct actions_for_flow *af; struct nlattr *a; size_t n_afs; struct ds s; int left; int i, error; ds_init(&s); simap_init(&port_names); for (i = 3; i < argc; i++) { char name[16]; int number; if (ovs_scan(argv[i], "%15[^=]=%d", name, &number)) { uintptr_t n = number; simap_put(&port_names, name, n); } else { dpctl_error(dpctl_p, 0, "%s: expected NAME=NUMBER", argv[i]); error = EINVAL; goto out; } } /* Parse flow key. */ ofpbuf_init(&keybuf, 0); error = odp_flow_from_string(argv[1], &port_names, &keybuf, NULL); if (error) { dpctl_error(dpctl_p, error, "odp_flow_key_from_string"); goto out_freekeybuf; } ds_clear(&s); odp_flow_format(keybuf.data, keybuf.size, NULL, 0, NULL, &s, dpctl_p->verbosity); dpctl_print(dpctl_p, "input flow: %s\n", ds_cstr(&s)); error = odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow); if (error) { dpctl_error(dpctl_p, error, "odp_flow_key_to_flow"); goto out_freekeybuf; } /* Parse actions. */ ofpbuf_init(&odp_actions, 0); error = odp_actions_from_string(argv[2], &port_names, &odp_actions); if (error) { dpctl_error(dpctl_p, error, "odp_actions_from_string"); goto out_freeactions; } if (dpctl_p->verbosity) { ds_clear(&s); format_odp_actions(&s, odp_actions.data, odp_actions.size); dpctl_print(dpctl_p, "input actions: %s\n", ds_cstr(&s)); } hmap_init(&actions_per_flow); NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) { const struct ovs_action_push_vlan *push; switch(nl_attr_type(a)) { case OVS_ACTION_ATTR_POP_VLAN: flow.vlan_tci = htons(0); continue; case OVS_ACTION_ATTR_PUSH_VLAN: push = nl_attr_get_unspec(a, sizeof *push); flow.vlan_tci = push->vlan_tci; continue; } af = get_actions_for_flow(&actions_per_flow, &flow); nl_msg_put_unspec(&af->actions, nl_attr_type(a), nl_attr_get(a), nl_attr_get_size(a)); } n_afs = hmap_count(&actions_per_flow); afs = xmalloc(n_afs * sizeof *afs); i = 0; HMAP_FOR_EACH (af, hmap_node, &actions_per_flow) { afs[i++] = af; } ovs_assert(i == n_afs); hmap_destroy(&actions_per_flow); qsort(afs, n_afs, sizeof *afs, compare_actions_for_flow); for (i = 0; i < n_afs; i++) { struct actions_for_flow *af = afs[i]; sort_output_actions(af->actions.data, af->actions.size); if (af->flow.vlan_tci != htons(0)) { dpctl_print(dpctl_p, "vlan(vid=%"PRIu16",pcp=%d): ", vlan_tci_to_vid(af->flow.vlan_tci), vlan_tci_to_pcp(af->flow.vlan_tci)); } else { dpctl_print(dpctl_p, "no vlan: "); } if (eth_type_mpls(af->flow.dl_type)) { dpctl_print(dpctl_p, "mpls(label=%"PRIu32",tc=%d,ttl=%d): ", mpls_lse_to_label(af->flow.mpls_lse[0]), mpls_lse_to_tc(af->flow.mpls_lse[0]), mpls_lse_to_ttl(af->flow.mpls_lse[0])); } else { dpctl_print(dpctl_p, "no mpls: "); } ds_clear(&s); format_odp_actions(&s, af->actions.data, af->actions.size); dpctl_puts(dpctl_p, false, ds_cstr(&s)); ofpbuf_uninit(&af->actions); free(af); } free(afs); out_freeactions: ofpbuf_uninit(&odp_actions); out_freekeybuf: ofpbuf_uninit(&keybuf); out: simap_destroy(&port_names); ds_destroy(&s); return error; } static const struct dpctl_command all_commands[] = { { "add-dp", "add-dp dp [iface...]", 1, INT_MAX, dpctl_add_dp }, { "del-dp", "del-dp dp", 1, 1, dpctl_del_dp }, { "add-if", "add-if dp iface...", 2, INT_MAX, dpctl_add_if }, { "del-if", "del-if dp iface...", 2, INT_MAX, dpctl_del_if }, { "set-if", "set-if dp iface...", 2, INT_MAX, dpctl_set_if }, { "dump-dps", "", 0, 0, dpctl_dump_dps }, { "show", "[dp...]", 0, INT_MAX, dpctl_show }, { "dump-flows", "[dp]", 0, 2, dpctl_dump_flows }, { "add-flow", "add-flow [dp] flow actions", 2, 3, dpctl_add_flow }, { "mod-flow", "mod-flow [dp] flow actions", 2, 3, dpctl_mod_flow }, { "get-flow", "get-flow [dp] ufid", 1, 2, dpctl_get_flow }, { "del-flow", "del-flow [dp] flow", 1, 2, dpctl_del_flow }, { "del-flows", "[dp]", 0, 1, dpctl_del_flows }, { "dump-conntrack", "[dp] [zone=N]", 0, 2, dpctl_dump_conntrack }, { "flush-conntrack", "[dp] [zone=N]", 0, 2, dpctl_flush_conntrack }, { "help", "", 0, INT_MAX, dpctl_help }, { "list-commands", "", 0, INT_MAX, dpctl_list_commands }, /* Undocumented commands for testing. */ { "parse-actions", "actions", 1, INT_MAX, dpctl_parse_actions }, { "normalize-actions", "actions", 2, INT_MAX, dpctl_normalize_actions }, { NULL, NULL, 0, 0, NULL }, }; static const struct dpctl_command *get_all_dpctl_commands(void) { return all_commands; } /* Runs the command designated by argv[0] within the command table specified by * 'commands', which must be terminated by a command whose 'name' member is a * null pointer. */ int dpctl_run_command(int argc, const char *argv[], struct dpctl_params *dpctl_p) { const struct dpctl_command *p; if (argc < 1) { dpctl_error(dpctl_p, 0, "missing command name; use --help for help"); return EINVAL; } for (p = all_commands; p->name != NULL; p++) { if (!strcmp(p->name, argv[0])) { int n_arg = argc - 1; if (n_arg < p->min_args) { dpctl_error(dpctl_p, 0, "'%s' command requires at least %d arguments", p->name, p->min_args); return EINVAL; } else if (n_arg > p->max_args) { dpctl_error(dpctl_p, 0, "'%s' command takes at most %d arguments", p->name, p->max_args); return EINVAL; } else { return p->handler(argc, argv, dpctl_p); } } } dpctl_error(dpctl_p, 0, "unknown command '%s'; use --help for help", argv[0]); return EINVAL; } static void dpctl_unixctl_print(void *userdata, bool error OVS_UNUSED, const char *msg) { struct ds *ds = userdata; ds_put_cstr(ds, msg); } static void dpctl_unixctl_handler(struct unixctl_conn *conn, int argc, const char *argv[], void *aux) { struct ds ds = DS_EMPTY_INITIALIZER; bool error = false; struct dpctl_params dpctl_p = { .is_appctl = true, .output = dpctl_unixctl_print, .aux = &ds, }; /* Parse options (like getopt). Unfortunately it does * not seem a good idea to call getopt_long() here, since it uses global * variables */ while (argc > 1 && !error) { const char *arg = argv[1]; if (!strncmp(arg, "--", 2)) { /* Long option */ if (!strcmp(arg, "--statistics")) { dpctl_p.print_statistics = true; } else if (!strcmp(arg, "--clear")) { dpctl_p.zero_statistics = true; } else if (!strcmp(arg, "--may-create")) { dpctl_p.may_create = true; } else if (!strcmp(arg, "--more")) { dpctl_p.verbosity++; } else { ds_put_format(&ds, "Unrecognized option %s", argv[1]); error = true; } } else if (arg[0] == '-' && arg[1] != '\0') { /* Short option[s] */ const char *opt = &arg[1]; while (*opt && !error) { switch (*opt) { case 'm': dpctl_p.verbosity++; break; case 's': dpctl_p.print_statistics = true; break; default: ds_put_format(&ds, "Unrecognized option -%c", *opt); error = true; break; } opt++; } } else { /* Doesn't start with -, not an option */ break; } if (error) { break; } argv++; argc--; } if (!error) { dpctl_command_handler *handler = (dpctl_command_handler *) aux; error = handler(argc, argv, &dpctl_p) != 0; } if (error) { unixctl_command_reply_error(conn, ds_cstr(&ds)); } else { unixctl_command_reply(conn, ds_cstr(&ds)); } ds_destroy(&ds); } void dpctl_unixctl_register(void) { const struct dpctl_command *p; for (p = all_commands; p->name != NULL; p++) { if (strcmp(p->name, "help")) { char *cmd_name = xasprintf("dpctl/%s", p->name); unixctl_command_register(cmd_name, "", p->min_args, p->max_args, dpctl_unixctl_handler, p->handler); free(cmd_name); } } } openvswitch-2.5.9/lib/PaxHeaders.82075/netlink.c0000644000000000000000000000013213534540071016222 xustar0030 mtime=1567801401.465681639 30 atime=1567801402.081686164 30 ctime=1567801424.781853426 openvswitch-2.5.9/lib/netlink.c0000644000175000017500000006502213534540071017715 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netlink.h" #include #include #include #include #include "coverage.h" #include "flow.h" #include "netlink-protocol.h" #include "ofpbuf.h" #include "timeval.h" #include "unaligned.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netlink); /* A single (bad) Netlink message can in theory dump out many, many log * messages, so the burst size is set quite high here to avoid missing useful * information. Also, at high logging levels we log *all* Netlink messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 600); /* Returns the nlmsghdr at the head of 'msg'. * * 'msg' must be at least as large as a nlmsghdr. */ struct nlmsghdr * nl_msg_nlmsghdr(const struct ofpbuf *msg) { return ofpbuf_at_assert(msg, 0, NLMSG_HDRLEN); } /* Returns the genlmsghdr just past 'msg''s nlmsghdr. * * Returns a null pointer if 'msg' is not large enough to contain an nlmsghdr * and a genlmsghdr. */ struct genlmsghdr * nl_msg_genlmsghdr(const struct ofpbuf *msg) { return ofpbuf_at(msg, NLMSG_HDRLEN, GENL_HDRLEN); } /* If 'buffer' is a NLMSG_ERROR message, stores 0 in '*errorp' if it is an ACK * message, otherwise a positive errno value, and returns true. If 'buffer' is * not an NLMSG_ERROR message, returns false. * * 'msg' must be at least as large as a nlmsghdr. */ bool nl_msg_nlmsgerr(const struct ofpbuf *msg, int *errorp) { if (nl_msg_nlmsghdr(msg)->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = ofpbuf_at(msg, NLMSG_HDRLEN, sizeof *err); int code = EPROTO; if (!err) { VLOG_ERR_RL(&rl, "received invalid nlmsgerr (%"PRIu32" bytes < %"PRIuSIZE")", msg->size, NLMSG_HDRLEN + sizeof *err); } else if (err->error <= 0 && err->error > INT_MIN) { code = -err->error; } if (errorp) { *errorp = code; } return true; } else { return false; } } /* Ensures that 'b' has room for at least 'size' bytes plus netlink padding at * its tail end, reallocating and copying its data if necessary. */ void nl_msg_reserve(struct ofpbuf *msg, size_t size) { ofpbuf_prealloc_tailroom(msg, NLMSG_ALIGN(size)); } /* Puts a nlmsghdr at the beginning of 'msg', which must be initially empty. * Uses the given 'type' and 'flags'. 'expected_payload' should be * an estimate of the number of payload bytes to be supplied; if the size of * the payload is unknown a value of 0 is acceptable. * * 'type' is ordinarily an enumerated value specific to the Netlink protocol * (e.g. RTM_NEWLINK, for NETLINK_ROUTE protocol). For Generic Netlink, 'type' * is the family number obtained via nl_lookup_genl_family(). * * 'flags' is a bit-mask that indicates what kind of request is being made. It * is often NLM_F_REQUEST indicating that a request is being made, commonly * or'd with NLM_F_ACK to request an acknowledgement. * * Sets the new nlmsghdr's nlmsg_len, nlmsg_seq, and nlmsg_pid fields to 0 for * now. Functions that send Netlink messages will fill these in just before * sending the message. * * nl_msg_put_genlmsghdr() is more convenient for composing a Generic Netlink * message. */ void nl_msg_put_nlmsghdr(struct ofpbuf *msg, size_t expected_payload, uint32_t type, uint32_t flags) { struct nlmsghdr *nlmsghdr; ovs_assert(msg->size == 0); nl_msg_reserve(msg, NLMSG_HDRLEN + expected_payload); nlmsghdr = nl_msg_put_uninit(msg, NLMSG_HDRLEN); nlmsghdr->nlmsg_len = 0; nlmsghdr->nlmsg_type = type; nlmsghdr->nlmsg_flags = flags; nlmsghdr->nlmsg_seq = 0; nlmsghdr->nlmsg_pid = 0; } /* Puts a nlmsghdr and genlmsghdr at the beginning of 'msg', which must be * initially empty. 'expected_payload' should be an estimate of the number of * payload bytes to be supplied; if the size of the payload is unknown a value * of 0 is acceptable. * * 'family' is the family number obtained via nl_lookup_genl_family(). * * 'flags' is a bit-mask that indicates what kind of request is being made. It * is often NLM_F_REQUEST indicating that a request is being made, commonly * or'd with NLM_F_ACK to request an acknowledgement. * * 'cmd' is an enumerated value specific to the Generic Netlink family * (e.g. CTRL_CMD_NEWFAMILY for the GENL_ID_CTRL family). * * 'version' is a version number specific to the family and command (often 1). * * Sets the new nlmsghdr's nlmsg_pid field to 0 for now. nl_sock_send() will * fill it in just before sending the message. * * nl_msg_put_nlmsghdr() should be used to compose Netlink messages that are * not Generic Netlink messages. */ void nl_msg_put_genlmsghdr(struct ofpbuf *msg, size_t expected_payload, int family, uint32_t flags, uint8_t cmd, uint8_t version) { struct genlmsghdr *genlmsghdr; nl_msg_put_nlmsghdr(msg, GENL_HDRLEN + expected_payload, family, flags); ovs_assert(msg->size == NLMSG_HDRLEN); genlmsghdr = nl_msg_put_uninit(msg, GENL_HDRLEN); genlmsghdr->cmd = cmd; genlmsghdr->version = version; genlmsghdr->reserved = 0; } /* Appends the 'size' bytes of data in 'p', plus Netlink padding if needed, to * the tail end of 'msg'. Data in 'msg' is reallocated and copied if * necessary. */ void nl_msg_put(struct ofpbuf *msg, const void *data, size_t size) { memcpy(nl_msg_put_uninit(msg, size), data, size); } /* Appends 'size' bytes of data, plus Netlink padding if needed, to the tail * end of 'msg', reallocating and copying its data if necessary. Returns a * pointer to the first byte of the new data, which is left uninitialized. */ void * nl_msg_put_uninit(struct ofpbuf *msg, size_t size) { size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); char *p = ofpbuf_put_uninit(msg, size + pad); if (pad) { memset(p + size, 0, pad); } return p; } /* Prepends the 'size' bytes of data in 'p', plus Netlink padding if needed, to * the head end of 'msg'. Data in 'msg' is reallocated and copied if * necessary. */ void nl_msg_push(struct ofpbuf *msg, const void *data, size_t size) { memcpy(nl_msg_push_uninit(msg, size), data, size); } /* Prepends 'size' bytes of data, plus Netlink padding if needed, to the head * end of 'msg', reallocating and copying its data if necessary. Returns a * pointer to the first byte of the new data, which is left uninitialized. */ void * nl_msg_push_uninit(struct ofpbuf *msg, size_t size) { size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); char *p = ofpbuf_push_uninit(msg, size + pad); if (pad) { memset(p + size, 0, pad); } return p; } /* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of * data as its payload, plus Netlink padding if needed, to the tail end of * 'msg', reallocating and copying its data if necessary. Returns a pointer to * the first byte of data in the attribute, which is left uninitialized. */ void * nl_msg_put_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size) { size_t total_size = NLA_HDRLEN + size; struct nlattr* nla = nl_msg_put_uninit(msg, total_size); ovs_assert(!nl_attr_oversized(size)); nla->nla_len = total_size; nla->nla_type = type; return nla + 1; } /* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of * data as its payload, plus Netlink padding if needed, to the tail end of * 'msg', reallocating and copying its data if necessary. Returns a pointer to * the first byte of data in the attribute, which is zeroed. */ void * nl_msg_put_unspec_zero(struct ofpbuf *msg, uint16_t type, size_t size) { void *data = nl_msg_put_unspec_uninit(msg, type, size); memset(data, 0, size); return data; } /* Appends a Netlink attribute of the given 'type' and the 'size' bytes of * 'data' as its payload, to the tail end of 'msg', reallocating and copying * its data if necessary. Returns a pointer to the first byte of data in the * attribute, which is left uninitialized. */ void nl_msg_put_unspec(struct ofpbuf *msg, uint16_t type, const void *data, size_t size) { memcpy(nl_msg_put_unspec_uninit(msg, type, size), data, size); } /* Appends a Netlink attribute of the given 'type' and no payload to 'msg'. * (Some Netlink protocols use the presence or absence of an attribute as a * Boolean flag.) */ void nl_msg_put_flag(struct ofpbuf *msg, uint16_t type) { nl_msg_put_unspec(msg, type, NULL, 0); } /* Appends a Netlink attribute of the given 'type' and the given 8-bit 'value' * to 'msg'. */ void nl_msg_put_u8(struct ofpbuf *msg, uint16_t type, uint8_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 16-bit host * byte order 'value' to 'msg'. */ void nl_msg_put_u16(struct ofpbuf *msg, uint16_t type, uint16_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 32-bit host * byte order 'value' to 'msg'. */ void nl_msg_put_u32(struct ofpbuf *msg, uint16_t type, uint32_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 64-bit host * byte order 'value' to 'msg'. */ void nl_msg_put_u64(struct ofpbuf *msg, uint16_t type, uint64_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 16-bit network * byte order 'value' to 'msg'. */ void nl_msg_put_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 32-bit network * byte order 'value' to 'msg'. */ void nl_msg_put_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given 64-bit network * byte order 'value' to 'msg'. */ void nl_msg_put_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } /* Appends a Netlink attribute of the given 'type' and the given IPv6 * address order 'value' to 'msg'. */ void nl_msg_put_in6_addr(struct ofpbuf *msg, uint16_t type, const struct in6_addr *value) { nl_msg_put_unspec(msg, type, value, sizeof *value); } /* Appends a Netlink attribute of the given 'type' and the given odp_port_t * 'value' to 'msg'. */ void nl_msg_put_odp_port(struct ofpbuf *msg, uint16_t type, odp_port_t value) { nl_msg_put_u32(msg, type, odp_to_u32(value)); } /* Appends a Netlink attribute of the given 'type' with the 'len' characters * of 'value', followed by the null byte to 'msg'. */ void nl_msg_put_string__(struct ofpbuf *msg, uint16_t type, const char *value, size_t len) { char *data = nl_msg_put_unspec_uninit(msg, type, len + 1); memcpy(data, value, len); data[len] = '\0'; } /* Appends a Netlink attribute of the given 'type' and the given * null-terminated string 'value' to 'msg'. */ void nl_msg_put_string(struct ofpbuf *msg, uint16_t type, const char *value) { nl_msg_put_unspec(msg, type, value, strlen(value) + 1); } /* Prepends a Netlink attribute of the given 'type' and room for 'size' bytes * of data as its payload, plus Netlink padding if needed, to the head end of * 'msg', reallocating and copying its data if necessary. Returns a pointer to * the first byte of data in the attribute, which is left uninitialized. */ void * nl_msg_push_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size) { size_t total_size = NLA_HDRLEN + size; struct nlattr* nla = nl_msg_push_uninit(msg, total_size); ovs_assert(!nl_attr_oversized(size)); nla->nla_len = total_size; nla->nla_type = type; return nla + 1; } /* Prepends a Netlink attribute of the given 'type' and the 'size' bytes of * 'data' as its payload, to the head end of 'msg', reallocating and copying * its data if necessary. Returns a pointer to the first byte of data in the * attribute, which is left uninitialized. */ void nl_msg_push_unspec(struct ofpbuf *msg, uint16_t type, const void *data, size_t size) { memcpy(nl_msg_push_unspec_uninit(msg, type, size), data, size); } /* Prepends a Netlink attribute of the given 'type' and no payload to 'msg'. * (Some Netlink protocols use the presence or absence of an attribute as a * Boolean flag.) */ void nl_msg_push_flag(struct ofpbuf *msg, uint16_t type) { nl_msg_push_unspec(msg, type, NULL, 0); } /* Prepends a Netlink attribute of the given 'type' and the given 8-bit 'value' * to 'msg'. */ void nl_msg_push_u8(struct ofpbuf *msg, uint16_t type, uint8_t value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 16-bit host * byte order 'value' to 'msg'. */ void nl_msg_push_u16(struct ofpbuf *msg, uint16_t type, uint16_t value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 32-bit host * byte order 'value' to 'msg'. */ void nl_msg_push_u32(struct ofpbuf *msg, uint16_t type, uint32_t value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 64-bit host * byte order 'value' to 'msg'. */ void nl_msg_push_u64(struct ofpbuf *msg, uint16_t type, uint64_t value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 16-bit * network byte order 'value' to 'msg'. */ void nl_msg_push_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 32-bit * network byte order 'value' to 'msg'. */ void nl_msg_push_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given 64-bit * network byte order 'value' to 'msg'. */ void nl_msg_push_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value) { nl_msg_push_unspec(msg, type, &value, sizeof value); } /* Prepends a Netlink attribute of the given 'type' and the given * null-terminated string 'value' to 'msg'. */ void nl_msg_push_string(struct ofpbuf *msg, uint16_t type, const char *value) { nl_msg_push_unspec(msg, type, value, strlen(value) + 1); } /* Adds the header for nested Netlink attributes to 'msg', with the specified * 'type', and returns the header's offset within 'msg'. The caller should add * the content for the nested Netlink attribute to 'msg' (e.g. using the other * nl_msg_*() functions), and then pass the returned offset to * nl_msg_end_nested() to finish up the nested attributes. */ size_t nl_msg_start_nested(struct ofpbuf *msg, uint16_t type) { size_t offset = msg->size; nl_msg_put_unspec(msg, type, NULL, 0); return offset; } /* Finalizes a nested Netlink attribute in 'msg'. 'offset' should be the value * returned by nl_msg_start_nested(). */ void nl_msg_end_nested(struct ofpbuf *msg, size_t offset) { struct nlattr *attr = ofpbuf_at_assert(msg, offset, sizeof *attr); attr->nla_len = msg->size - offset; } /* Appends a nested Netlink attribute of the given 'type', with the 'size' * bytes of content starting at 'data', to 'msg'. */ void nl_msg_put_nested(struct ofpbuf *msg, uint16_t type, const void *data, size_t size) { size_t offset = nl_msg_start_nested(msg, type); nl_msg_put(msg, data, size); nl_msg_end_nested(msg, offset); } /* If 'buffer' begins with a valid "struct nlmsghdr", pulls the header and its * payload off 'buffer', stores header and payload in 'msg->data' and * 'msg->size', and returns a pointer to the header. * * If 'buffer' does not begin with a "struct nlmsghdr" or begins with one that * is invalid, returns NULL and clears 'buffer' and 'msg'. */ struct nlmsghdr * nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg) { if (buffer->size >= sizeof(struct nlmsghdr)) { struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(buffer); size_t len = nlmsghdr->nlmsg_len; if (len >= sizeof *nlmsghdr && len <= buffer->size) { ofpbuf_use_const(msg, nlmsghdr, len); ofpbuf_pull(buffer, len); return nlmsghdr; } } ofpbuf_clear(buffer); msg->data = NULL; msg->size = 0; return NULL; } /* Returns true if a Netlink attribute with a payload that is 'payload_size' * bytes long would be oversized, that is, if it's not possible to create an * nlattr of that size because its size wouldn't fit in the 16-bit nla_len * field. */ bool nl_attr_oversized(size_t payload_size) { return payload_size > UINT16_MAX - NLA_HDRLEN; } /* Attributes. */ /* Returns the bits of 'nla->nla_type' that are significant for determining its * type. */ int nl_attr_type(const struct nlattr *nla) { return nla->nla_type & NLA_TYPE_MASK; } /* Returns the first byte in the payload of attribute 'nla'. */ const void * nl_attr_get(const struct nlattr *nla) { ovs_assert(nla->nla_len >= NLA_HDRLEN); return nla + 1; } /* Returns the number of bytes in the payload of attribute 'nla'. */ size_t nl_attr_get_size(const struct nlattr *nla) { ovs_assert(nla->nla_len >= NLA_HDRLEN); return nla->nla_len - NLA_HDRLEN; } /* Asserts that 'nla''s payload is at least 'size' bytes long, and returns the * first byte of the payload. */ const void * nl_attr_get_unspec(const struct nlattr *nla, size_t size) { ovs_assert(nla->nla_len >= NLA_HDRLEN + size); return nla + 1; } /* Returns true if 'nla' is nonnull. (Some Netlink protocols use the presence * or absence of an attribute as a Boolean flag.) */ bool nl_attr_get_flag(const struct nlattr *nla) { return nla != NULL; } #define NL_ATTR_GET_AS(NLA, TYPE) \ (*(TYPE*) nl_attr_get_unspec(nla, sizeof(TYPE))) /* Returns the 8-bit value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 1 byte long. */ uint8_t nl_attr_get_u8(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, uint8_t); } /* Returns the 16-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 2 bytes long. */ uint16_t nl_attr_get_u16(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, uint16_t); } /* Returns the 32-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 4 bytes long. */ uint32_t nl_attr_get_u32(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, uint32_t); } /* Returns the 64-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 8 bytes long. */ uint64_t nl_attr_get_u64(const struct nlattr *nla) { const ovs_32aligned_u64 *x = nl_attr_get_unspec(nla, sizeof *x); return get_32aligned_u64(x); } /* Returns the 16-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 2 bytes long. */ ovs_be16 nl_attr_get_be16(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, ovs_be16); } /* Returns the 32-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 4 bytes long. */ ovs_be32 nl_attr_get_be32(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, ovs_be32); } /* Returns the 64-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 8 bytes long. */ ovs_be64 nl_attr_get_be64(const struct nlattr *nla) { const ovs_32aligned_be64 *x = nl_attr_get_unspec(nla, sizeof *x); return get_32aligned_be64(x); } /* Returns the IPv6 address value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 16 bytes long. */ struct in6_addr nl_attr_get_in6_addr(const struct nlattr *nla) { return NL_ATTR_GET_AS(nla, struct in6_addr); } /* Returns the 32-bit odp_port_t value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 4 bytes long. */ odp_port_t nl_attr_get_odp_port(const struct nlattr *nla) { return u32_to_odp(nl_attr_get_u32(nla)); } /* Returns the null-terminated string value in 'nla''s payload. * * Asserts that 'nla''s payload contains a null-terminated string. */ const char * nl_attr_get_string(const struct nlattr *nla) { ovs_assert(nla->nla_len > NLA_HDRLEN); ovs_assert(memchr(nl_attr_get(nla), '\0', nla->nla_len - NLA_HDRLEN)); return nl_attr_get(nla); } /* Initializes 'nested' to the payload of 'nla'. */ void nl_attr_get_nested(const struct nlattr *nla, struct ofpbuf *nested) { ofpbuf_use_const(nested, nl_attr_get(nla), nl_attr_get_size(nla)); } /* Default minimum payload size for each type of attribute. */ static size_t min_attr_len(enum nl_attr_type type) { switch (type) { case NL_A_NO_ATTR: return 0; case NL_A_UNSPEC: return 0; case NL_A_U8: return 1; case NL_A_U16: return 2; case NL_A_U32: return 4; case NL_A_U64: return 8; case NL_A_STRING: return 1; case NL_A_FLAG: return 0; case NL_A_IPV6: return 16; case NL_A_NESTED: return 0; case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); } } /* Default maximum payload size for each type of attribute. */ static size_t max_attr_len(enum nl_attr_type type) { switch (type) { case NL_A_NO_ATTR: return SIZE_MAX; case NL_A_UNSPEC: return SIZE_MAX; case NL_A_U8: return 1; case NL_A_U16: return 2; case NL_A_U32: return 4; case NL_A_U64: return 8; case NL_A_STRING: return SIZE_MAX; case NL_A_FLAG: return SIZE_MAX; case NL_A_IPV6: return 16; case NL_A_NESTED: return SIZE_MAX; case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); } } bool nl_attr_validate(const struct nlattr *nla, const struct nl_policy *policy) { uint16_t type = nl_attr_type(nla); size_t min_len; size_t max_len; size_t len; if (policy->type == NL_A_NO_ATTR) { return true; } /* Figure out min and max length. */ min_len = policy->min_len; if (!min_len) { min_len = min_attr_len(policy->type); } max_len = policy->max_len; if (!max_len) { max_len = max_attr_len(policy->type); } /* Verify length. */ len = nl_attr_get_size(nla); if (len < min_len || len > max_len) { VLOG_DBG_RL(&rl, "attr %"PRIu16" length %"PRIuSIZE" not in " "allowed range %"PRIuSIZE"...%"PRIuSIZE, type, len, min_len, max_len); return false; } /* Strings must be null terminated and must not have embedded nulls. */ if (policy->type == NL_A_STRING) { if (((char *) nla)[nla->nla_len - 1]) { VLOG_DBG_RL(&rl, "attr %"PRIu16" lacks null at end", type); return false; } if (memchr(nla + 1, '\0', len - 1) != NULL) { VLOG_DBG_RL(&rl, "attr %"PRIu16" has bad length", type); return false; } } return true; } /* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink * attributes. 'policy[i]', for 0 <= i < n_attrs, specifies how the attribute * with nla_type == i is parsed; a pointer to attribute i is stored in * attrs[i]. Returns true if successful, false on failure. * * If the Netlink attributes in 'msg' follow a Netlink header and a Generic * Netlink header, then 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN. */ bool nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset, const struct nl_policy policy[], struct nlattr *attrs[], size_t n_attrs) { struct nlattr *nla; size_t left; size_t i; memset(attrs, 0, n_attrs * sizeof *attrs); if (msg->size < nla_offset) { VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse"); return false; } NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, nla_offset, 0), msg->size - nla_offset) { uint16_t type = nl_attr_type(nla); if (type < n_attrs && policy[type].type != NL_A_NO_ATTR) { const struct nl_policy *e = &policy[type]; if (!nl_attr_validate(nla, e)) { return false; } if (attrs[type]) { VLOG_DBG_RL(&rl, "duplicate attr %"PRIu16, type); } attrs[type] = nla; } } if (left) { VLOG_DBG_RL(&rl, "attributes followed by garbage"); return false; } for (i = 0; i < n_attrs; i++) { const struct nl_policy *e = &policy[i]; if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[i]) { VLOG_DBG_RL(&rl, "required attr %"PRIuSIZE" missing", i); return false; } } return true; } /* Parses the Netlink attributes within 'nla'. 'policy[i]', for 0 <= i < * n_attrs, specifies how the attribute with nla_type == i is parsed; a pointer * to attribute i is stored in attrs[i]. Returns true if successful, false on * failure. */ bool nl_parse_nested(const struct nlattr *nla, const struct nl_policy policy[], struct nlattr *attrs[], size_t n_attrs) { struct ofpbuf buf; nl_attr_get_nested(nla, &buf); return nl_policy_parse(&buf, 0, policy, attrs, n_attrs); } const struct nlattr * nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type) { const struct nlattr *nla; size_t left; NL_ATTR_FOR_EACH (nla, left, attrs, size) { if (nl_attr_type(nla) == type) { return nla; } } return NULL; } /* Returns the first Netlink attribute within 'buf' with the specified 'type', * skipping a header of 'hdr_len' bytes at the beginning of 'buf'. * * This function does not validate the attribute's length. */ const struct nlattr * nl_attr_find(const struct ofpbuf *buf, size_t hdr_len, uint16_t type) { return nl_attr_find__(ofpbuf_at(buf, hdr_len, 0), buf->size - hdr_len, type); } /* Returns the first Netlink attribute within 'nla' with the specified * 'type'. * * This function does not validate the attribute's length. */ const struct nlattr * nl_attr_find_nested(const struct nlattr *nla, uint16_t type) { return nl_attr_find__(nl_attr_get(nla), nl_attr_get_size(nla), type); } openvswitch-2.5.9/lib/PaxHeaders.82075/bundle.h0000644000000000000000000000013213534540071016034 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 30 ctime=1567801424.665852571 openvswitch-2.5.9/lib/bundle.h0000644000175000017500000000324513534540071017526 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BUNDLE_H #define BUNDLE_H 1 #include #include #include #include #include "compiler.h" #include "ofp-errors.h" #include "openflow/nicira-ext.h" #include "openvswitch/types.h" struct ds; struct flow; struct flow_wildcards; struct ofpact_bundle; struct ofpbuf; /* NXAST_BUNDLE helper functions. * * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */ #define BUNDLE_MAX_SLAVES 2048 ofp_port_t bundle_execute(const struct ofpact_bundle *, const struct flow *, struct flow_wildcards *wc, bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux); enum ofperr bundle_check(const struct ofpact_bundle *, ofp_port_t max_ports, const struct flow *); char *bundle_parse(const char *, struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT; char *bundle_parse_load(const char *, struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT; void bundle_format(const struct ofpact_bundle *, struct ds *); #endif /* bundle.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/common.man0000644000000000000000000000013213534540071016377 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801423.721845611 openvswitch-2.5.9/lib/common.man0000644000175000017500000000031113534540071020060 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .IP "\fB\-h\fR" .IQ "\fB\-\-help\fR" Prints a brief help message to the console. . .IP "\fB\-V\fR" .IQ "\fB\-\-version\fR" Prints version information to the console. openvswitch-2.5.9/lib/PaxHeaders.82075/ssl-bootstrap-syn.man0000644000000000000000000000013113534540071020531 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 29 ctime=1567801423.73784573 openvswitch-2.5.9/lib/ssl-bootstrap-syn.man0000644000175000017500000000006213534540071022216 0ustar00jpettitjpettit00000000000000.br [\fB\-\-bootstrap\-ca\-cert=\fIcacert.pem\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-version-opt.c0000644000000000000000000000013213534540071017625 xustar0030 mtime=1567801401.537682169 30 atime=1567801402.089686223 30 ctime=1567801424.805853603 openvswitch-2.5.9/lib/ofp-version-opt.c0000644000175000017500000000210413534540071021310 0ustar00jpettitjpettit00000000000000#include #include "dynamic-string.h" #include "ofp-util.h" #include "ofp-version-opt.h" #include "ovs-thread.h" static uint32_t allowed_versions = 0; uint32_t get_allowed_ofp_versions(void) { return allowed_versions ? allowed_versions : OFPUTIL_DEFAULT_VERSIONS; } void set_allowed_ofp_versions(const char *string) { assert_single_threaded(); allowed_versions = ofputil_versions_from_string(string); } void mask_allowed_ofp_versions(uint32_t bitmap) { assert_single_threaded(); allowed_versions &= bitmap; } void add_allowed_ofp_versions(uint32_t bitmap) { assert_single_threaded(); allowed_versions |= bitmap; } void ofp_version_usage(void) { struct ds msg = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&msg, OFPUTIL_DEFAULT_VERSIONS); printf( "\nOpenFlow version options:\n" " -V, --version display version information\n" " -O, --protocols set allowed OpenFlow versions\n" " (default: %s)\n", ds_cstr(&msg)); ds_destroy(&msg); } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-actions.c0000644000000000000000000000013213534540071017000 xustar0030 mtime=1567801401.509681963 30 atime=1567801402.085686193 30 ctime=1567801424.789853486 openvswitch-2.5.9/lib/ofp-actions.c0000644000175000017500000074216713534540071020507 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "ofp-actions.h" #include "bundle.h" #include "byte-order.h" #include "compiler.h" #include "dummy.h" #include "dynamic-string.h" #include "hmap.h" #include "learn.h" #include "meta-flow.h" #include "multipath.h" #include "nx-match.h" #include "ofp-parse.h" #include "ofp-util.h" #include "ofpbuf.h" #include "unaligned.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_actions); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); struct ofp_action_header; /* Raw identifiers for OpenFlow actions. * * Decoding and encoding OpenFlow actions across multiple versions is difficult * to do in a clean, consistent way. This enumeration lays out all of the * forms of actions that Open vSwitch supports. * * The comments here must follow a stylized form because the * "extract-ofp-actions" program parses them at build time to generate data * tables. * * - The first part of each comment specifies the vendor, OpenFlow versions, * and type for each protocol that supports the action: * * # The vendor is OF for standard OpenFlow actions, NX for Nicira * extension actions. (Support for other vendors can be added, but * it can't be done just based on a vendor ID definition alone * because OpenFlow doesn't define a standard way to specify a * subtype for vendor actions, so other vendors might do it different * from Nicira.) * * # The version can specify a specific OpenFlow version, a version * range delimited by "-", or an open-ended range with "+". * * # The type, in parentheses, is the action type number (for standard * OpenFlow actions) or subtype (for vendor extension actions). * * # Optionally one may add "is deprecated" followed by a * human-readable reason in parentheses (which will be used in log * messages), if a particular action should no longer be used. * * Multiple such specifications may be separated by commas. * * - The second part describes the action's wire format. It may be: * * # "struct ": The struct fully specifies the wire format. The * action is exactly the size of the struct. (Thus, the struct must * be an exact multiple of 8 bytes in size.) * * # "struct , ...": The struct specifies the beginning of the * wire format. An instance of the action is either the struct's * exact size, or a multiple of 8 bytes longer. * * # "uint_t" or "ovs_be": The action consists of a (standard or * vendor extension) header, followed by 0 or more pad bytes to align * to a multiple of bits, followed by an argument of the given * type, followed by 0 or more pad bytes to bring the total action up * to a multiple of 8 bytes. * * # "void": The action is just a (standard or vendor extension) * header. * * - Optional additional text enclosed in square brackets is commentary for * the human reader. */ enum ofp_raw_action_type { /* ## ----------------- ## */ /* ## Standard actions. ## */ /* ## ----------------- ## */ /* OF1.0(0): struct ofp10_action_output. */ OFPAT_RAW10_OUTPUT, /* OF1.1+(0): struct ofp11_action_output. */ OFPAT_RAW11_OUTPUT, /* OF1.0(1): uint16_t. */ OFPAT_RAW10_SET_VLAN_VID, /* OF1.0(2): uint8_t. */ OFPAT_RAW10_SET_VLAN_PCP, /* OF1.1(1), OF1.2+(1) is deprecated (use Set-Field): uint16_t. * * [Semantics differ slightly between the 1.0 and 1.1 versions of the VLAN * modification actions: the 1.0 versions push a VLAN header if none is * present, but the 1.1 versions do not. That is the only reason that we * distinguish their raw action types.] */ OFPAT_RAW11_SET_VLAN_VID, /* OF1.1(2), OF1.2+(2) is deprecated (use Set-Field): uint8_t. */ OFPAT_RAW11_SET_VLAN_PCP, /* OF1.1+(17): ovs_be16. * * [The argument is the Ethertype, e.g. ETH_TYPE_VLAN_8021Q, not the VID or * TCI.] */ OFPAT_RAW11_PUSH_VLAN, /* OF1.0(3): void. */ OFPAT_RAW10_STRIP_VLAN, /* OF1.1+(18): void. */ OFPAT_RAW11_POP_VLAN, /* OF1.0(4), OF1.1(3), OF1.2+(3) is deprecated (use Set-Field): struct * ofp_action_dl_addr. */ OFPAT_RAW_SET_DL_SRC, /* OF1.0(5), OF1.1(4), OF1.2+(4) is deprecated (use Set-Field): struct * ofp_action_dl_addr. */ OFPAT_RAW_SET_DL_DST, /* OF1.0(6), OF1.1(5), OF1.2+(5) is deprecated (use Set-Field): * ovs_be32. */ OFPAT_RAW_SET_NW_SRC, /* OF1.0(7), OF1.1(6), OF1.2+(6) is deprecated (use Set-Field): * ovs_be32. */ OFPAT_RAW_SET_NW_DST, /* OF1.0(8), OF1.1(7), OF1.2+(7) is deprecated (use Set-Field): uint8_t. */ OFPAT_RAW_SET_NW_TOS, /* OF1.1(8), OF1.2+(8) is deprecated (use Set-Field): uint8_t. */ OFPAT_RAW11_SET_NW_ECN, /* OF1.0(9), OF1.1(9), OF1.2+(9) is deprecated (use Set-Field): * ovs_be16. */ OFPAT_RAW_SET_TP_SRC, /* OF1.0(10), OF1.1(10), OF1.2+(10) is deprecated (use Set-Field): * ovs_be16. */ OFPAT_RAW_SET_TP_DST, /* OF1.0(11): struct ofp10_action_enqueue. */ OFPAT_RAW10_ENQUEUE, /* NX1.0(30), OF1.1(13), OF1.2+(13) is deprecated (use Set-Field): * ovs_be32. */ OFPAT_RAW_SET_MPLS_LABEL, /* NX1.0(31), OF1.1(14), OF1.2+(14) is deprecated (use Set-Field): * uint8_t. */ OFPAT_RAW_SET_MPLS_TC, /* NX1.0(25), OF1.1(15), OF1.2+(15) is deprecated (use Set-Field): * uint8_t. */ OFPAT_RAW_SET_MPLS_TTL, /* NX1.0(26), OF1.1+(16): void. */ OFPAT_RAW_DEC_MPLS_TTL, /* NX1.0(23), OF1.1+(19): ovs_be16. * * [The argument is the Ethertype, e.g. ETH_TYPE_MPLS, not the label.] */ OFPAT_RAW_PUSH_MPLS, /* NX1.0(24), OF1.1+(20): ovs_be16. * * [The argument is the Ethertype, e.g. ETH_TYPE_IPV4 if at BoS or * ETH_TYPE_MPLS otherwise, not the label.] */ OFPAT_RAW_POP_MPLS, /* NX1.0(4), OF1.1+(21): uint32_t. */ OFPAT_RAW_SET_QUEUE, /* OF1.1+(22): uint32_t. */ OFPAT_RAW11_GROUP, /* OF1.1+(23): uint8_t. */ OFPAT_RAW11_SET_NW_TTL, /* NX1.0(18), OF1.1+(24): void. */ OFPAT_RAW_DEC_NW_TTL, /* NX1.0+(21): struct nx_action_cnt_ids, ... */ NXAST_RAW_DEC_TTL_CNT_IDS, /* OF1.2-1.4(25): struct ofp12_action_set_field, ... */ OFPAT_RAW12_SET_FIELD, /* OF1.5+(25): struct ofp12_action_set_field, ... */ OFPAT_RAW15_SET_FIELD, /* NX1.0-1.4(7): struct nx_action_reg_load. * * [In OpenFlow 1.5, set_field is a superset of reg_load functionality, so * we drop reg_load.] */ NXAST_RAW_REG_LOAD, /* NX1.0-1.4(33): struct nx_action_reg_load2, ... * * [In OpenFlow 1.5, set_field is a superset of reg_load2 functionality, so * we drop reg_load2.] */ NXAST_RAW_REG_LOAD2, /* OF1.5+(28): struct ofp15_action_copy_field, ... */ OFPAT_RAW15_COPY_FIELD, /* ONF1.3-1.4(3200): struct onf_action_copy_field, ... */ ONFACT_RAW13_COPY_FIELD, /* NX1.0-1.4(6): struct nx_action_reg_move, ... */ NXAST_RAW_REG_MOVE, /* ## ------------------------- ## */ /* ## Nicira extension actions. ## */ /* ## ------------------------- ## */ /* Actions similar to standard actions are listed with the standard actions. */ /* NX1.0+(1): uint16_t. */ NXAST_RAW_RESUBMIT, /* NX1.0+(14): struct nx_action_resubmit. */ NXAST_RAW_RESUBMIT_TABLE, /* NX1.0+(2): uint32_t. */ NXAST_RAW_SET_TUNNEL, /* NX1.0+(9): uint64_t. */ NXAST_RAW_SET_TUNNEL64, /* NX1.0+(5): void. */ NXAST_RAW_POP_QUEUE, /* NX1.0+(8): struct nx_action_note, ... */ NXAST_RAW_NOTE, /* NX1.0+(10): struct nx_action_multipath. */ NXAST_RAW_MULTIPATH, /* NX1.0+(12): struct nx_action_bundle, ... */ NXAST_RAW_BUNDLE, /* NX1.0+(13): struct nx_action_bundle, ... */ NXAST_RAW_BUNDLE_LOAD, /* NX1.0+(15): struct nx_action_output_reg. */ NXAST_RAW_OUTPUT_REG, /* NX1.0+(32): struct nx_action_output_reg2. */ NXAST_RAW_OUTPUT_REG2, /* NX1.0+(16): struct nx_action_learn, ... */ NXAST_RAW_LEARN, /* NX1.0+(17): void. */ NXAST_RAW_EXIT, /* NX1.0+(19): struct nx_action_fin_timeout. */ NXAST_RAW_FIN_TIMEOUT, /* NX1.0+(20): struct nx_action_controller. */ NXAST_RAW_CONTROLLER, /* NX1.0+(22): struct nx_action_write_metadata. */ NXAST_RAW_WRITE_METADATA, /* NX1.0+(27): struct nx_action_stack. */ NXAST_RAW_STACK_PUSH, /* NX1.0+(28): struct nx_action_stack. */ NXAST_RAW_STACK_POP, /* NX1.0+(29): struct nx_action_sample. */ NXAST_RAW_SAMPLE, /* NX1.0+(34): struct nx_action_conjunction. */ NXAST_RAW_CONJUNCTION, /* NX1.0+(35): struct nx_action_conntrack, ... */ NXAST_RAW_CT, /* ## ------------------ ## */ /* ## Debugging actions. ## */ /* ## ------------------ ## */ /* These are intentionally undocumented, subject to change, and ovs-vswitchd */ /* accepts them only if started with --enable-dummy. */ /* NX1.0+(255): void. */ NXAST_RAW_DEBUG_RECIRC, }; /* OpenFlow actions are always a multiple of 8 bytes in length. */ #define OFP_ACTION_ALIGN 8 /* Define a few functions for working with instructions. */ #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ static inline const struct STRUCT * OVS_UNUSED \ instruction_get_##ENUM(const struct ofp11_instruction *inst)\ { \ ovs_assert(inst->type == htons(ENUM)); \ return ALIGNED_CAST(struct STRUCT *, inst); \ } \ \ static inline void OVS_UNUSED \ instruction_init_##ENUM(struct STRUCT *s) \ { \ memset(s, 0, sizeof *s); \ s->type = htons(ENUM); \ s->len = htons(sizeof *s); \ } \ \ static inline struct STRUCT * OVS_UNUSED \ instruction_put_##ENUM(struct ofpbuf *buf) \ { \ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ instruction_init_##ENUM(s); \ return s; \ } OVS_INSTRUCTIONS #undef DEFINE_INST static void ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs); static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs); static enum ofperr ofpacts_verify(const struct ofpact[], size_t ofpacts_len, uint32_t allowed_ovsinsts, enum ofpact_type outer_action); static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version, enum mf_field_id, uint64_t value); static enum ofperr ofpact_pull_raw(struct ofpbuf *, enum ofp_version, enum ofp_raw_action_type *, uint64_t *arg); static void *ofpact_put_raw(struct ofpbuf *, enum ofp_version, enum ofp_raw_action_type, uint64_t arg); static char *OVS_WARN_UNUSED_RESULT ofpacts_parse( char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, bool allow_instructions, enum ofpact_type outer_action); static enum ofperr ofpacts_pull_openflow_actions__( struct ofpbuf *openflow, unsigned int actions_len, enum ofp_version version, uint32_t allowed_ovsinsts, struct ofpbuf *ofpacts, enum ofpact_type outer_action); static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy( const char *s_, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, bool allow_instructions, enum ofpact_type outer_action); /* Returns the ofpact following 'ofpact', except that if 'ofpact' contains * nested ofpacts it returns the first one. */ struct ofpact * ofpact_next_flattened(const struct ofpact *ofpact) { switch (ofpact->type) { case OFPACT_OUTPUT: case OFPACT_GROUP: case OFPACT_CONTROLLER: case OFPACT_ENQUEUE: case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: case OFPACT_SET_FIELD: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: case OFPACT_LEARN: case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_EXIT: case OFPACT_SAMPLE: case OFPACT_UNROLL_XLATE: case OFPACT_DEBUG_RECIRC: case OFPACT_METER: case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: return ofpact_next(ofpact); case OFPACT_CT: return ofpact_get_CT(ofpact)->actions; case OFPACT_WRITE_ACTIONS: return ofpact_get_WRITE_ACTIONS(ofpact)->actions; } OVS_NOT_REACHED(); } /* Pull off existing actions or instructions. Used by nesting actions to keep * ofpacts_parse() oblivious of actions nesting. * * Push the actions back on after nested parsing, e.g.: * * size_t ofs = ofpacts_pull(ofpacts); * ...nested parsing... * ofpbuf_push_uninit(ofpacts, ofs); */ static size_t ofpacts_pull(struct ofpbuf *ofpacts) { size_t ofs; ofpact_pad(ofpacts); ofs = ofpacts->size; ofpbuf_pull(ofpacts, ofs); return ofs; } #include "ofp-actions.inc1" /* Output actions. */ /* Action structure for OFPAT10_OUTPUT, which sends packets out 'port'. * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max * number of bytes to send. A 'max_len' of zero means no bytes of the * packet should be sent. */ struct ofp10_action_output { ovs_be16 type; /* OFPAT10_OUTPUT. */ ovs_be16 len; /* Length is 8. */ ovs_be16 port; /* Output port. */ ovs_be16 max_len; /* Max length to send to controller. */ }; OFP_ASSERT(sizeof(struct ofp10_action_output) == 8); /* Action structure for OFPAT_OUTPUT, which sends packets out 'port'. * When the 'port' is the OFPP_CONTROLLER, 'max_len' indicates the max * number of bytes to send. A 'max_len' of zero means no bytes of the * packet should be sent.*/ struct ofp11_action_output { ovs_be16 type; /* OFPAT11_OUTPUT. */ ovs_be16 len; /* Length is 16. */ ovs_be32 port; /* Output port. */ ovs_be16 max_len; /* Max length to send to controller. */ uint8_t pad[6]; /* Pad to 64 bits. */ }; OFP_ASSERT(sizeof(struct ofp11_action_output) == 16); static enum ofperr decode_OFPAT_RAW10_OUTPUT(const struct ofp10_action_output *oao, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output *output; output = ofpact_put_OUTPUT(out); output->port = u16_to_ofp(ntohs(oao->port)); output->max_len = ntohs(oao->max_len); return ofpact_check_output_port(output->port, OFPP_MAX); } static enum ofperr decode_OFPAT_RAW11_OUTPUT(const struct ofp11_action_output *oao, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output *output; enum ofperr error; output = ofpact_put_OUTPUT(out); output->max_len = ntohs(oao->max_len); error = ofputil_port_from_ofp11(oao->port, &output->port); if (error) { return error; } return ofpact_check_output_port(output->port, OFPP_MAX); } static void encode_OUTPUT(const struct ofpact_output *output, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { struct ofp10_action_output *oao; oao = put_OFPAT10_OUTPUT(out); oao->port = htons(ofp_to_u16(output->port)); oao->max_len = htons(output->max_len); } else { struct ofp11_action_output *oao; oao = put_OFPAT11_OUTPUT(out); oao->port = ofputil_port_to_ofp11(output->port); oao->max_len = htons(output->max_len); } } static char * OVS_WARN_UNUSED_RESULT parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { if (strchr(arg, '[')) { struct ofpact_output_reg *output_reg; output_reg = ofpact_put_OUTPUT_REG(ofpacts); output_reg->max_len = UINT16_MAX; return mf_parse_subfield(&output_reg->src, arg); } else { struct ofpact_output *output; output = ofpact_put_OUTPUT(ofpacts); if (!ofputil_port_from_string(arg, &output->port)) { return xasprintf("%s: output to unknown port", arg); } output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; return NULL; } } static void format_OUTPUT(const struct ofpact_output *a, struct ds *s) { if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) { ds_put_format(s, "output:%"PRIu16, a->port); } else { ofputil_format_port(a->port, s); if (a->port == OFPP_CONTROLLER) { ds_put_format(s, ":%"PRIu16, a->max_len); } } } /* Group actions. */ static enum ofperr decode_OFPAT_RAW11_GROUP(uint32_t group_id, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_GROUP(out)->group_id = group_id; return 0; } static void encode_GROUP(const struct ofpact_group *group, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { /* XXX */ } else { put_OFPAT11_GROUP(out, group->group_id); } } static char * OVS_WARN_UNUSED_RESULT parse_GROUP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id); } static void format_GROUP(const struct ofpact_group *a, struct ds *s) { ds_put_format(s, "group:%"PRIu32, a->group_id); } /* Action structure for NXAST_CONTROLLER. * * This generalizes using OFPAT_OUTPUT to send a packet to OFPP_CONTROLLER. In * addition to the 'max_len' that OFPAT_OUTPUT supports, it also allows * specifying: * * - 'reason': The reason code to use in the ofp_packet_in or nx_packet_in. * * - 'controller_id': The ID of the controller connection to which the * ofp_packet_in should be sent. The ofp_packet_in or nx_packet_in is * sent only to controllers that have the specified controller connection * ID. See "struct nx_controller_id" for more information. */ struct nx_action_controller { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_CONTROLLER. */ ovs_be16 max_len; /* Maximum length to send to controller. */ ovs_be16 controller_id; /* Controller ID to send packet-in. */ uint8_t reason; /* enum ofp_packet_in_reason (OFPR_*). */ uint8_t zero; /* Must be zero. */ }; OFP_ASSERT(sizeof(struct nx_action_controller) == 16); static enum ofperr decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_controller *oc; oc = ofpact_put_CONTROLLER(out); oc->max_len = ntohs(nac->max_len); oc->controller_id = ntohs(nac->controller_id); oc->reason = nac->reason; return 0; } static void encode_CONTROLLER(const struct ofpact_controller *controller, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct nx_action_controller *nac; nac = put_NXAST_CONTROLLER(out); nac->max_len = htons(controller->max_len); nac->controller_id = htons(controller->controller_id); nac->reason = controller->reason; } static char * OVS_WARN_UNUSED_RESULT parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { enum ofp_packet_in_reason reason = OFPR_ACTION; uint16_t controller_id = 0; uint16_t max_len = UINT16_MAX; if (!arg[0]) { /* Use defaults. */ } else if (strspn(arg, "0123456789") == strlen(arg)) { char *error = str_to_u16(arg, "max_len", &max_len); if (error) { return error; } } else { char *name, *value; while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "reason")) { if (!ofputil_packet_in_reason_from_string(value, &reason)) { return xasprintf("unknown reason \"%s\"", value); } } else if (!strcmp(name, "max_len")) { char *error = str_to_u16(value, "max_len", &max_len); if (error) { return error; } } else if (!strcmp(name, "id")) { char *error = str_to_u16(value, "id", &controller_id); if (error) { return error; } } else { return xasprintf("unknown key \"%s\" parsing controller " "action", name); } } } if (reason == OFPR_ACTION && controller_id == 0) { struct ofpact_output *output; output = ofpact_put_OUTPUT(ofpacts); output->port = OFPP_CONTROLLER; output->max_len = max_len; } else { struct ofpact_controller *controller; controller = ofpact_put_CONTROLLER(ofpacts); controller->max_len = max_len; controller->reason = reason; controller->controller_id = controller_id; } return NULL; } static void format_CONTROLLER(const struct ofpact_controller *a, struct ds *s) { if (a->reason == OFPR_ACTION && a->controller_id == 0) { ds_put_format(s, "CONTROLLER:%"PRIu16, a->max_len); } else { enum ofp_packet_in_reason reason = a->reason; ds_put_cstr(s, "controller("); if (reason != OFPR_ACTION) { char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE]; ds_put_format(s, "reason=%s,", ofputil_packet_in_reason_to_string( reason, reasonbuf, sizeof reasonbuf)); } if (a->max_len != UINT16_MAX) { ds_put_format(s, "max_len=%"PRIu16",", a->max_len); } if (a->controller_id != 0) { ds_put_format(s, "id=%"PRIu16",", a->controller_id); } ds_chomp(s, ','); ds_put_char(s, ')'); } } /* Enqueue action. */ struct ofp10_action_enqueue { ovs_be16 type; /* OFPAT10_ENQUEUE. */ ovs_be16 len; /* Len is 16. */ ovs_be16 port; /* Port that queue belongs. Should refer to a valid physical port (i.e. < OFPP_MAX) or OFPP_IN_PORT. */ uint8_t pad[6]; /* Pad for 64-bit alignment. */ ovs_be32 queue_id; /* Where to enqueue the packets. */ }; OFP_ASSERT(sizeof(struct ofp10_action_enqueue) == 16); static enum ofperr decode_OFPAT_RAW10_ENQUEUE(const struct ofp10_action_enqueue *oae, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_enqueue *enqueue; enqueue = ofpact_put_ENQUEUE(out); enqueue->port = u16_to_ofp(ntohs(oae->port)); enqueue->queue = ntohl(oae->queue_id); if (ofp_to_u16(enqueue->port) >= ofp_to_u16(OFPP_MAX) && enqueue->port != OFPP_IN_PORT && enqueue->port != OFPP_LOCAL) { return OFPERR_OFPBAC_BAD_OUT_PORT; } return 0; } static void encode_ENQUEUE(const struct ofpact_enqueue *enqueue, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { struct ofp10_action_enqueue *oae; oae = put_OFPAT10_ENQUEUE(out); oae->port = htons(ofp_to_u16(enqueue->port)); oae->queue_id = htonl(enqueue->queue); } else { /* XXX */ } } static char * OVS_WARN_UNUSED_RESULT parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { char *sp = NULL; char *port = strtok_r(arg, ":q,", &sp); char *queue = strtok_r(NULL, "", &sp); struct ofpact_enqueue *enqueue; if (port == NULL || queue == NULL) { return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or " "\"enqueue(PORT,QUEUE)\""); } enqueue = ofpact_put_ENQUEUE(ofpacts); if (!ofputil_port_from_string(port, &enqueue->port)) { return xasprintf("%s: enqueue to unknown port", port); } return str_to_u32(queue, &enqueue->queue); } static void format_ENQUEUE(const struct ofpact_enqueue *a, struct ds *s) { ds_put_format(s, "enqueue:"); ofputil_format_port(a->port, s); ds_put_format(s, ":%"PRIu32, a->queue); } /* Action structure for NXAST_OUTPUT_REG. * * Outputs to the OpenFlow port number written to src[ofs:ofs+nbits]. * * The format and semantics of 'src' and 'ofs_nbits' are similar to those for * the NXAST_REG_LOAD action. * * The acceptable nxm_header values for 'src' are the same as the acceptable * nxm_header values for the 'src' field of NXAST_REG_MOVE. * * The 'max_len' field indicates the number of bytes to send when the chosen * port is OFPP_CONTROLLER. Its semantics are equivalent to the 'max_len' * field of OFPAT_OUTPUT. * * The 'zero' field is required to be zeroed for forward compatibility. */ struct nx_action_output_reg { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_OUTPUT_REG. */ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ ovs_be32 src; /* Source. */ ovs_be16 max_len; /* Max length to send to controller. */ uint8_t zero[6]; /* Reserved, must be zero. */ }; OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); /* Action structure for NXAST_OUTPUT_REG2. * * Like the NXAST_OUTPUT_REG but organized so that there is room for a 64-bit * experimenter OXM as 'src'. */ struct nx_action_output_reg2 { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_OUTPUT_REG2. */ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ ovs_be16 max_len; /* Max length to send to controller. */ /* Followed by: * - 'src', as an OXM/NXM header (either 4 or 8 bytes). * - Enough 0-bytes to pad the action out to 24 bytes. */ uint8_t pad[10]; }; OFP_ASSERT(sizeof(struct nx_action_output_reg2) == 24); static enum ofperr decode_NXAST_RAW_OUTPUT_REG(const struct nx_action_output_reg *naor, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output_reg *output_reg; if (!is_all_zeros(naor->zero, sizeof naor->zero)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } output_reg = ofpact_put_OUTPUT_REG(out); output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG; output_reg->src.field = mf_from_nxm_header(ntohl(naor->src)); output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); output_reg->max_len = ntohs(naor->max_len); return mf_check_src(&output_reg->src, NULL); } static enum ofperr decode_NXAST_RAW_OUTPUT_REG2(const struct nx_action_output_reg2 *naor, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_output_reg *output_reg; enum ofperr error; struct ofpbuf b; output_reg = ofpact_put_OUTPUT_REG(out); output_reg->ofpact.raw = NXAST_RAW_OUTPUT_REG2; output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); output_reg->max_len = ntohs(naor->max_len); ofpbuf_use_const(&b, naor, ntohs(naor->len)); ofpbuf_pull(&b, OBJECT_OFFSETOF(naor, pad)); error = nx_pull_header(&b, &output_reg->src.field, NULL); if (error) { return error; } if (!is_all_zeros(b.data, b.size)) { return OFPERR_NXBRC_MUST_BE_ZERO; } return mf_check_src(&output_reg->src, NULL); } static void encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { /* If 'output_reg' came in as an NXAST_RAW_OUTPUT_REG2 action, or if it * cannot be encoded in the older form, encode it as * NXAST_RAW_OUTPUT_REG2. */ if (output_reg->ofpact.raw == NXAST_RAW_OUTPUT_REG2 || !mf_nxm_header(output_reg->src.field->id)) { struct nx_action_output_reg2 *naor = put_NXAST_OUTPUT_REG2(out); size_t size = out->size; naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, output_reg->src.n_bits); naor->max_len = htons(output_reg->max_len); out->size = size - sizeof naor->pad; nx_put_header(out, output_reg->src.field->id, 0, false); out->size = size; } else { struct nx_action_output_reg *naor = put_NXAST_OUTPUT_REG(out); naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, output_reg->src.n_bits); naor->src = htonl(mf_nxm_header(output_reg->src.field->id)); naor->max_len = htons(output_reg->max_len); } } static char * OVS_WARN_UNUSED_RESULT parse_OUTPUT_REG(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return parse_OUTPUT(arg, ofpacts, usable_protocols); } static void format_OUTPUT_REG(const struct ofpact_output_reg *a, struct ds *s) { ds_put_cstr(s, "output:"); mf_format_subfield(&a->src, s); } /* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD. * * The bundle actions choose a slave from a supplied list of options. * NXAST_BUNDLE outputs to its selection. NXAST_BUNDLE_LOAD writes its * selection to a register. * * The list of possible slaves follows the nx_action_bundle structure. The size * of each slave is governed by its type as indicated by the 'slave_type' * parameter. The list of slaves should be padded at its end with zeros to make * the total length of the action a multiple of 8. * * Switches infer from the 'slave_type' parameter the size of each slave. All * implementations must support the NXM_OF_IN_PORT 'slave_type' which indicates * that the slaves are OpenFlow port numbers with NXM_LENGTH(NXM_OF_IN_PORT) == * 2 byte width. Switches should reject actions which indicate unknown or * unsupported slave types. * * Switches use a strategy dictated by the 'algorithm' parameter to choose a * slave. If the switch does not support the specified 'algorithm' parameter, * it should reject the action. * * Several algorithms take into account liveness when selecting slaves. The * liveness of a slave is implementation defined (with one exception), but will * generally take into account things like its carrier status and the results * of any link monitoring protocols which happen to be running on it. In order * to give controllers a place-holder value, the OFPP_NONE port is always * considered live. * * Some slave selection strategies require the use of a hash function, in which * case the 'fields' and 'basis' parameters should be populated. The 'fields' * parameter (one of NX_HASH_FIELDS_*) designates which parts of the flow to * hash. Refer to the definition of "enum nx_hash_fields" for details. The * 'basis' parameter is used as a universal hash parameter. Different values * of 'basis' yield different hash results. * * The 'zero' parameter at the end of the action structure is reserved for * future use. Switches are required to reject actions which have nonzero * bytes in the 'zero' field. * * NXAST_BUNDLE actions should have 'ofs_nbits' and 'dst' zeroed. Switches * should reject actions which have nonzero bytes in either of these fields. * * NXAST_BUNDLE_LOAD stores the OpenFlow port number of the selected slave in * dst[ofs:ofs+n_bits]. The format and semantics of 'dst' and 'ofs_nbits' are * similar to those for the NXAST_REG_LOAD action. */ struct nx_action_bundle { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length including slaves. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_BUNDLE or NXAST_BUNDLE_LOAD. */ /* Slave choice algorithm to apply to hash value. */ ovs_be16 algorithm; /* One of NX_BD_ALG_*. */ /* What fields to hash and how. */ ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ ovs_be16 basis; /* Universal hash parameter. */ ovs_be32 slave_type; /* NXM_OF_IN_PORT. */ ovs_be16 n_slaves; /* Number of slaves. */ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ ovs_be32 dst; /* Destination. */ uint8_t zero[4]; /* Reserved. Must be zero. */ }; OFP_ASSERT(sizeof(struct nx_action_bundle) == 32); static enum ofperr decode_bundle(bool load, const struct nx_action_bundle *nab, struct ofpbuf *ofpacts) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); struct ofpact_bundle *bundle; uint32_t slave_type; size_t slaves_size, i; enum ofperr error; bundle = ofpact_put_BUNDLE(ofpacts); bundle->n_slaves = ntohs(nab->n_slaves); bundle->basis = ntohs(nab->basis); bundle->fields = ntohs(nab->fields); bundle->algorithm = ntohs(nab->algorithm); slave_type = ntohl(nab->slave_type); slaves_size = ntohs(nab->len) - sizeof *nab; error = OFPERR_OFPBAC_BAD_ARGUMENT; if (!flow_hash_fields_valid(bundle->fields)) { VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields); } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) { VLOG_WARN_RL(&rl, "too many slaves"); } else if (bundle->algorithm != NX_BD_ALG_HRW && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) { VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm); } else if (slave_type != mf_nxm_header(MFF_IN_PORT)) { VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type); } else { error = 0; } if (!is_all_zeros(nab->zero, sizeof nab->zero)) { VLOG_WARN_RL(&rl, "reserved field is nonzero"); error = OFPERR_OFPBAC_BAD_ARGUMENT; } if (load) { bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst)); bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits); bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits); if (bundle->dst.n_bits < 16) { VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit " "destination."); error = OFPERR_OFPBAC_BAD_ARGUMENT; } } else { if (nab->ofs_nbits || nab->dst) { VLOG_WARN_RL(&rl, "bundle action has nonzero reserved fields"); error = OFPERR_OFPBAC_BAD_ARGUMENT; } } if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) { VLOG_WARN_RL(&rl, "Nicira action %s only has %"PRIuSIZE" bytes " "allocated for slaves. %"PRIuSIZE" bytes are required " "for %"PRIu16" slaves.", load ? "bundle_load" : "bundle", slaves_size, bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves); error = OFPERR_OFPBAC_BAD_LEN; } else { for (i = 0; i < bundle->n_slaves; i++) { ofp_port_t ofp_port = u16_to_ofp(ntohs(((ovs_be16 *)(nab + 1))[i])); ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port); bundle = ofpacts->header; } } ofpact_update_len(ofpacts, &bundle->ofpact); if (!error) { error = bundle_check(bundle, OFPP_MAX, NULL); } return error; } static enum ofperr decode_NXAST_RAW_BUNDLE(const struct nx_action_bundle *nab, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_bundle(false, nab, out); } static enum ofperr decode_NXAST_RAW_BUNDLE_LOAD(const struct nx_action_bundle *nab, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_bundle(true, nab, out); } static void encode_BUNDLE(const struct ofpact_bundle *bundle, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN); struct nx_action_bundle *nab; ovs_be16 *slaves; size_t i; nab = (bundle->dst.field ? put_NXAST_BUNDLE_LOAD(out) : put_NXAST_BUNDLE(out)); nab->len = htons(ntohs(nab->len) + slaves_len); nab->algorithm = htons(bundle->algorithm); nab->fields = htons(bundle->fields); nab->basis = htons(bundle->basis); nab->slave_type = htonl(mf_nxm_header(MFF_IN_PORT)); nab->n_slaves = htons(bundle->n_slaves); if (bundle->dst.field) { nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, bundle->dst.n_bits); nab->dst = htonl(mf_nxm_header(bundle->dst.field->id)); } slaves = ofpbuf_put_zeros(out, slaves_len); for (i = 0; i < bundle->n_slaves; i++) { slaves[i] = htons(ofp_to_u16(bundle->slaves[i])); } } static char * OVS_WARN_UNUSED_RESULT parse_BUNDLE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return bundle_parse(arg, ofpacts); } static char * OVS_WARN_UNUSED_RESULT parse_bundle_load(const char *arg, struct ofpbuf *ofpacts) { return bundle_parse_load(arg, ofpacts); } static void format_BUNDLE(const struct ofpact_bundle *a, struct ds *s) { bundle_format(a, s); } /* Set VLAN actions. */ static enum ofperr decode_set_vlan_vid(uint16_t vid, bool push_vlan_if_needed, struct ofpbuf *out) { if (vid & ~0xfff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } else { struct ofpact_vlan_vid *vlan_vid = ofpact_put_SET_VLAN_VID(out); vlan_vid->vlan_vid = vid; vlan_vid->push_vlan_if_needed = push_vlan_if_needed; return 0; } } static enum ofperr decode_OFPAT_RAW10_SET_VLAN_VID(uint16_t vid, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_set_vlan_vid(vid, true, out); } static enum ofperr decode_OFPAT_RAW11_SET_VLAN_VID(uint16_t vid, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_set_vlan_vid(vid, false, out); } static void encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid, enum ofp_version ofp_version, struct ofpbuf *out) { uint16_t vid = vlan_vid->vlan_vid; /* Push a VLAN tag, if none is present and this form of the action calls * for such a feature. */ if (ofp_version > OFP10_VERSION && vlan_vid->push_vlan_if_needed && !vlan_vid->flow_has_vlan) { put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); } if (ofp_version == OFP10_VERSION) { put_OFPAT10_SET_VLAN_VID(out, vid); } else if (ofp_version == OFP11_VERSION) { put_OFPAT11_SET_VLAN_VID(out, vid); } else { ofpact_put_set_field(out, ofp_version, MFF_VLAN_VID, vid | OFPVID12_PRESENT); } } static char * OVS_WARN_UNUSED_RESULT parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) { struct ofpact_vlan_vid *vlan_vid; uint16_t vid; char *error; error = str_to_u16(arg, "VLAN VID", &vid); if (error) { return error; } if (vid & ~VLAN_VID_MASK) { return xasprintf("%s: not a valid VLAN VID", arg); } vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts); vlan_vid->vlan_vid = vid; vlan_vid->push_vlan_if_needed = push_vlan_if_needed; return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_SET_VLAN_VID(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return parse_set_vlan_vid(arg, ofpacts, false); } static void format_SET_VLAN_VID(const struct ofpact_vlan_vid *a, struct ds *s) { ds_put_format(s, "%s:%"PRIu16, a->push_vlan_if_needed ? "mod_vlan_vid" : "set_vlan_vid", a->vlan_vid); } /* Set PCP actions. */ static enum ofperr decode_set_vlan_pcp(uint8_t pcp, bool push_vlan_if_needed, struct ofpbuf *out) { if (pcp & ~7) { return OFPERR_OFPBAC_BAD_ARGUMENT; } else { struct ofpact_vlan_pcp *vlan_pcp = ofpact_put_SET_VLAN_PCP(out); vlan_pcp->vlan_pcp = pcp; vlan_pcp->push_vlan_if_needed = push_vlan_if_needed; return 0; } } static enum ofperr decode_OFPAT_RAW10_SET_VLAN_PCP(uint8_t pcp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_set_vlan_pcp(pcp, true, out); } static enum ofperr decode_OFPAT_RAW11_SET_VLAN_PCP(uint8_t pcp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { return decode_set_vlan_pcp(pcp, false, out); } static void encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp, enum ofp_version ofp_version, struct ofpbuf *out) { uint8_t pcp = vlan_pcp->vlan_pcp; /* Push a VLAN tag, if none is present and this form of the action calls * for such a feature. */ if (ofp_version > OFP10_VERSION && vlan_pcp->push_vlan_if_needed && !vlan_pcp->flow_has_vlan) { put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); } if (ofp_version == OFP10_VERSION) { put_OFPAT10_SET_VLAN_PCP(out, pcp); } else if (ofp_version == OFP11_VERSION) { put_OFPAT11_SET_VLAN_PCP(out, pcp); } else { ofpact_put_set_field(out, ofp_version, MFF_VLAN_PCP, pcp); } } static char * OVS_WARN_UNUSED_RESULT parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed) { struct ofpact_vlan_pcp *vlan_pcp; uint8_t pcp; char *error; error = str_to_u8(arg, "VLAN PCP", &pcp); if (error) { return error; } if (pcp & ~7) { return xasprintf("%s: not a valid VLAN PCP", arg); } vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts); vlan_pcp->vlan_pcp = pcp; vlan_pcp->push_vlan_if_needed = push_vlan_if_needed; return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_SET_VLAN_PCP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return parse_set_vlan_pcp(arg, ofpacts, false); } static void format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a, struct ds *s) { ds_put_format(s, "%s:%"PRIu8, a->push_vlan_if_needed ? "mod_vlan_pcp" : "set_vlan_pcp", a->vlan_pcp); } /* Strip VLAN actions. */ static enum ofperr decode_OFPAT_RAW10_STRIP_VLAN(struct ofpbuf *out) { ofpact_put_STRIP_VLAN(out)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN; return 0; } static enum ofperr decode_OFPAT_RAW11_POP_VLAN(struct ofpbuf *out) { ofpact_put_STRIP_VLAN(out)->ofpact.raw = OFPAT_RAW11_POP_VLAN; return 0; } static void encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { put_OFPAT10_STRIP_VLAN(out); } else { put_OFPAT11_POP_VLAN(out); } } static char * OVS_WARN_UNUSED_RESULT parse_STRIP_VLAN(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN; return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_pop_vlan(struct ofpbuf *ofpacts) { ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN; return NULL; } static void format_STRIP_VLAN(const struct ofpact_null *a, struct ds *s) { ds_put_cstr(s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN ? "pop_vlan" : "strip_vlan")); } /* Push VLAN action. */ static enum ofperr decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { if (eth_type != htons(ETH_TYPE_VLAN_8021Q)) { /* XXX 802.1AD(QinQ) isn't supported at the moment */ return OFPERR_OFPBAC_BAD_ARGUMENT; } ofpact_put_PUSH_VLAN(out); return 0; } static void encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { /* PUSH is a side effect of a SET_VLAN_VID/PCP, which should * follow this action. */ } else { /* XXX ETH_TYPE_VLAN_8021AD case */ put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); } } static char * OVS_WARN_UNUSED_RESULT parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint16_t ethertype; char *error; *usable_protocols &= OFPUTIL_P_OF11_UP; error = str_to_u16(arg, "ethertype", ðertype); if (error) { return error; } if (ethertype != ETH_TYPE_VLAN_8021Q) { /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */ return xasprintf("%s: not a valid VLAN ethertype", arg); } ofpact_put_PUSH_VLAN(ofpacts); return NULL; } static void format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { /* XXX 802.1AD case*/ ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q); } /* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */ struct ofp_action_dl_addr { ovs_be16 type; /* Type. */ ovs_be16 len; /* Length is 16. */ struct eth_addr dl_addr; /* Ethernet address. */ uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ofp_action_dl_addr) == 16); static enum ofperr decode_OFPAT_RAW_SET_DL_SRC(const struct ofp_action_dl_addr *a, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_ETH_SRC(out)->mac = a->dl_addr; return 0; } static enum ofperr decode_OFPAT_RAW_SET_DL_DST(const struct ofp_action_dl_addr *a, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_ETH_DST(out)->mac = a->dl_addr; return 0; } static void encode_SET_ETH_addr(const struct ofpact_mac *mac, enum ofp_version ofp_version, enum ofp_raw_action_type raw, enum mf_field_id field, struct ofpbuf *out) { if (ofp_version < OFP12_VERSION) { struct ofp_action_dl_addr *oada; oada = ofpact_put_raw(out, ofp_version, raw, 0); oada->dl_addr = mac->mac; } else { ofpact_put_set_field(out, ofp_version, field, eth_addr_to_uint64(mac->mac)); } } static void encode_SET_ETH_SRC(const struct ofpact_mac *mac, enum ofp_version ofp_version, struct ofpbuf *out) { encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_SRC, MFF_ETH_SRC, out); } static void encode_SET_ETH_DST(const struct ofpact_mac *mac, enum ofp_version ofp_version, struct ofpbuf *out) { encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_DST, MFF_ETH_DST, out); } static char * OVS_WARN_UNUSED_RESULT parse_SET_ETH_SRC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac); } static char * OVS_WARN_UNUSED_RESULT parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac); } static void format_SET_ETH_SRC(const struct ofpact_mac *a, struct ds *s) { ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, ETH_ADDR_ARGS(a->mac)); } static void format_SET_ETH_DST(const struct ofpact_mac *a, struct ds *s) { ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, ETH_ADDR_ARGS(a->mac)); } /* Set IPv4 address actions. */ static enum ofperr decode_OFPAT_RAW_SET_NW_SRC(ovs_be32 ipv4, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_IPV4_SRC(out)->ipv4 = ipv4; return 0; } static enum ofperr decode_OFPAT_RAW_SET_NW_DST(ovs_be32 ipv4, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_IPV4_DST(out)->ipv4 = ipv4; return 0; } static void encode_SET_IPV4_addr(const struct ofpact_ipv4 *ipv4, enum ofp_version ofp_version, enum ofp_raw_action_type raw, enum mf_field_id field, struct ofpbuf *out) { ovs_be32 addr = ipv4->ipv4; if (ofp_version < OFP12_VERSION) { ofpact_put_raw(out, ofp_version, raw, ntohl(addr)); } else { ofpact_put_set_field(out, ofp_version, field, ntohl(addr)); } } static void encode_SET_IPV4_SRC(const struct ofpact_ipv4 *ipv4, enum ofp_version ofp_version, struct ofpbuf *out) { encode_SET_IPV4_addr(ipv4, ofp_version, OFPAT_RAW_SET_NW_SRC, MFF_IPV4_SRC, out); } static void encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4, enum ofp_version ofp_version, struct ofpbuf *out) { encode_SET_IPV4_addr(ipv4, ofp_version, OFPAT_RAW_SET_NW_DST, MFF_IPV4_DST, out); } static char * OVS_WARN_UNUSED_RESULT parse_SET_IPV4_SRC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4); } static char * OVS_WARN_UNUSED_RESULT parse_SET_IPV4_DST(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4); } static void format_SET_IPV4_SRC(const struct ofpact_ipv4 *a, struct ds *s) { ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(a->ipv4)); } static void format_SET_IPV4_DST(const struct ofpact_ipv4 *a, struct ds *s) { ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(a->ipv4)); } /* Set IPv4/v6 TOS actions. */ static enum ofperr decode_OFPAT_RAW_SET_NW_TOS(uint8_t dscp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { if (dscp & ~IP_DSCP_MASK) { return OFPERR_OFPBAC_BAD_ARGUMENT; } else { ofpact_put_SET_IP_DSCP(out)->dscp = dscp; return 0; } } static void encode_SET_IP_DSCP(const struct ofpact_dscp *dscp, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version < OFP12_VERSION) { put_OFPAT_SET_NW_TOS(out, ofp_version, dscp->dscp); } else { ofpact_put_set_field(out, ofp_version, MFF_IP_DSCP_SHIFTED, dscp->dscp >> 2); } } static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_DSCP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint8_t tos; char *error; error = str_to_u8(arg, "TOS", &tos); if (error) { return error; } if (tos & ~IP_DSCP_MASK) { return xasprintf("%s: not a valid TOS", arg); } ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos; return NULL; } static void format_SET_IP_DSCP(const struct ofpact_dscp *a, struct ds *s) { ds_put_format(s, "mod_nw_tos:%d", a->dscp); } /* Set IPv4/v6 ECN actions. */ static enum ofperr decode_OFPAT_RAW11_SET_NW_ECN(uint8_t ecn, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { if (ecn & ~IP_ECN_MASK) { return OFPERR_OFPBAC_BAD_ARGUMENT; } else { ofpact_put_SET_IP_ECN(out)->ecn = ecn; return 0; } } static void encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn, enum ofp_version ofp_version, struct ofpbuf *out) { uint8_t ecn = ip_ecn->ecn; if (ofp_version == OFP10_VERSION) { /* XXX */ } else if (ofp_version == OFP11_VERSION) { put_OFPAT11_SET_NW_ECN(out, ecn); } else { ofpact_put_set_field(out, ofp_version, MFF_IP_ECN, ecn); } } static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_ECN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint8_t ecn; char *error; error = str_to_u8(arg, "ECN", &ecn); if (error) { return error; } if (ecn & ~IP_ECN_MASK) { return xasprintf("%s: not a valid ECN", arg); } ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn; return NULL; } static void format_SET_IP_ECN(const struct ofpact_ecn *a, struct ds *s) { ds_put_format(s, "mod_nw_ecn:%d", a->ecn); } /* Set IPv4/v6 TTL actions. */ static enum ofperr decode_OFPAT_RAW11_SET_NW_TTL(uint8_t ttl, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_IP_TTL(out)->ttl = ttl; return 0; } static void encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version >= OFP11_VERSION) { put_OFPAT11_SET_NW_TTL(out, ttl->ttl); } else { /* XXX */ } } static char * OVS_WARN_UNUSED_RESULT parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint8_t ttl; char *error; error = str_to_u8(arg, "TTL", &ttl); if (error) { return error; } ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl; return NULL; } static void format_SET_IP_TTL(const struct ofpact_ip_ttl *a, struct ds *s) { ds_put_format(s, "mod_nw_ttl:%d", a->ttl); } /* Set TCP/UDP/SCTP port actions. */ static enum ofperr decode_OFPAT_RAW_SET_TP_SRC(ovs_be16 port, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(port); return 0; } static enum ofperr decode_OFPAT_RAW_SET_TP_DST(ovs_be16 port, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(port); return 0; } static void encode_SET_L4_port(const struct ofpact_l4_port *l4_port, enum ofp_version ofp_version, enum ofp_raw_action_type raw, enum mf_field_id field, struct ofpbuf *out) { uint16_t port = l4_port->port; if (ofp_version >= OFP12_VERSION && field != MFF_N_IDS) { ofpact_put_set_field(out, ofp_version, field, port); } else { ofpact_put_raw(out, ofp_version, raw, port); } } static void encode_SET_L4_SRC_PORT(const struct ofpact_l4_port *l4_port, enum ofp_version ofp_version, struct ofpbuf *out) { uint8_t proto = l4_port->flow_ip_proto; enum mf_field_id field = (proto == IPPROTO_TCP ? MFF_TCP_SRC : proto == IPPROTO_UDP ? MFF_UDP_SRC : proto == IPPROTO_SCTP ? MFF_SCTP_SRC : MFF_N_IDS); encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_SRC, field, out); } static void encode_SET_L4_DST_PORT(const struct ofpact_l4_port *l4_port, enum ofp_version ofp_version, struct ofpbuf *out) { uint8_t proto = l4_port->flow_ip_proto; enum mf_field_id field = (proto == IPPROTO_TCP ? MFF_TCP_DST : proto == IPPROTO_UDP ? MFF_UDP_DST : proto == IPPROTO_SCTP ? MFF_SCTP_DST : MFF_N_IDS); encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_DST, field, out); } static char * OVS_WARN_UNUSED_RESULT parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_u16(arg, "source port", &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port); } static char * OVS_WARN_UNUSED_RESULT parse_SET_L4_DST_PORT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_u16(arg, "destination port", &ofpact_put_SET_L4_DST_PORT(ofpacts)->port); } static void format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a, struct ds *s) { ds_put_format(s, "mod_tp_src:%d", a->port); } static void format_SET_L4_DST_PORT(const struct ofpact_l4_port *a, struct ds *s) { ds_put_format(s, "mod_tp_dst:%d", a->port); } /* Action structure for OFPAT_COPY_FIELD. */ struct ofp15_action_copy_field { ovs_be16 type; /* OFPAT_COPY_FIELD. */ ovs_be16 len; /* Length is padded to 64 bits. */ ovs_be16 n_bits; /* Number of bits to copy. */ ovs_be16 src_offset; /* Starting bit offset in source. */ ovs_be16 dst_offset; /* Starting bit offset in destination. */ uint8_t pad[2]; /* Followed by: * - OXM header for source field. * - OXM header for destination field. * - Padding with 0-bytes to a multiple of 8 bytes. * The "pad2" member is the beginning of the above. */ uint8_t pad2[4]; }; OFP_ASSERT(sizeof(struct ofp15_action_copy_field) == 16); /* Action structure for OpenFlow 1.3 extension copy-field action.. */ struct onf_action_copy_field { ovs_be16 type; /* OFPAT_EXPERIMENTER. */ ovs_be16 len; /* Length is padded to 64 bits. */ ovs_be32 experimenter; /* ONF_VENDOR_ID. */ ovs_be16 exp_type; /* 3200. */ uint8_t pad[2]; /* Not used. */ ovs_be16 n_bits; /* Number of bits to copy. */ ovs_be16 src_offset; /* Starting bit offset in source. */ ovs_be16 dst_offset; /* Starting bit offset in destination. */ uint8_t pad2[2]; /* Not used. */ /* Followed by: * - OXM header for source field. * - OXM header for destination field. * - Padding with 0-bytes (either 0 or 4 of them) to a multiple of 8 bytes. * The "pad3" member is the beginning of the above. */ uint8_t pad3[4]; /* Not used. */ }; OFP_ASSERT(sizeof(struct onf_action_copy_field) == 24); /* Action structure for NXAST_REG_MOVE. * * Copies src[src_ofs:src_ofs+n_bits] to dst[dst_ofs:dst_ofs+n_bits], where * a[b:c] denotes the bits within 'a' numbered 'b' through 'c' (not including * bit 'c'). Bit numbering starts at 0 for the least-significant bit, 1 for * the next most significant bit, and so on. * * 'src' and 'dst' are nxm_header values with nxm_hasmask=0. (It doesn't make * sense to use nxm_hasmask=1 because the action does not do any kind of * matching; it uses the actual value of a field.) * * The following nxm_header values are potentially acceptable as 'src': * * - NXM_OF_IN_PORT * - NXM_OF_ETH_DST * - NXM_OF_ETH_SRC * - NXM_OF_ETH_TYPE * - NXM_OF_VLAN_TCI * - NXM_OF_IP_TOS * - NXM_OF_IP_PROTO * - NXM_OF_IP_SRC * - NXM_OF_IP_DST * - NXM_OF_TCP_SRC * - NXM_OF_TCP_DST * - NXM_OF_UDP_SRC * - NXM_OF_UDP_DST * - NXM_OF_ICMP_TYPE * - NXM_OF_ICMP_CODE * - NXM_OF_ARP_OP * - NXM_OF_ARP_SPA * - NXM_OF_ARP_TPA * - NXM_NX_TUN_ID * - NXM_NX_ARP_SHA * - NXM_NX_ARP_THA * - NXM_NX_ICMPV6_TYPE * - NXM_NX_ICMPV6_CODE * - NXM_NX_ND_SLL * - NXM_NX_ND_TLL * - NXM_NX_REG(idx) for idx in the switch's accepted range. * - NXM_NX_PKT_MARK * - NXM_NX_TUN_IPV4_SRC * - NXM_NX_TUN_IPV4_DST * * The following nxm_header values are potentially acceptable as 'dst': * * - NXM_OF_ETH_DST * - NXM_OF_ETH_SRC * - NXM_OF_IP_TOS * - NXM_OF_IP_SRC * - NXM_OF_IP_DST * - NXM_OF_TCP_SRC * - NXM_OF_TCP_DST * - NXM_OF_UDP_SRC * - NXM_OF_UDP_DST * - NXM_OF_ICMP_TYPE * - NXM_OF_ICMP_CODE * - NXM_NX_ICMPV6_TYPE * - NXM_NX_ICMPV6_CODE * - NXM_NX_ARP_SHA * - NXM_NX_ARP_THA * - NXM_OF_ARP_OP * - NXM_OF_ARP_SPA * - NXM_OF_ARP_TPA * Modifying any of the above fields changes the corresponding packet * header. * * - NXM_OF_IN_PORT * * - NXM_NX_REG(idx) for idx in the switch's accepted range. * * - NXM_NX_PKT_MARK * * - NXM_OF_VLAN_TCI. Modifying this field's value has side effects on the * packet's 802.1Q header. Setting a value with CFI=0 removes the 802.1Q * header (if any), ignoring the other bits. Setting a value with CFI=1 * adds or modifies the 802.1Q header appropriately, setting the TCI field * to the field's new value (with the CFI bit masked out). * * - NXM_NX_TUN_ID, NXM_NX_TUN_IPV4_SRC, NXM_NX_TUN_IPV4_DST. Modifying * any of these values modifies the corresponding tunnel header field used * for the packet's next tunnel encapsulation, if allowed by the * configuration of the output tunnel port. * * A given nxm_header value may be used as 'src' or 'dst' only on a flow whose * nx_match satisfies its prerequisites. For example, NXM_OF_IP_TOS may be * used only if the flow's nx_match includes an nxm_entry that specifies * nxm_type=NXM_OF_ETH_TYPE, nxm_hasmask=0, and nxm_value=0x0800. * * The switch will reject actions for which src_ofs+n_bits is greater than the * width of 'src' or dst_ofs+n_bits is greater than the width of 'dst' with * error type OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. * * This action behaves properly when 'src' overlaps with 'dst', that is, it * behaves as if 'src' were copied out to a temporary buffer, then the * temporary buffer copied to 'dst'. */ struct nx_action_reg_move { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_REG_MOVE. */ ovs_be16 n_bits; /* Number of bits. */ ovs_be16 src_ofs; /* Starting bit offset in source. */ ovs_be16 dst_ofs; /* Starting bit offset in destination. */ /* Followed by: * - OXM/NXM header for source field (4 or 8 bytes). * - OXM/NXM header for destination field (4 or 8 bytes). * - Padding with 0-bytes to a multiple of 8 bytes, if necessary. */ }; OFP_ASSERT(sizeof(struct nx_action_reg_move) == 16); static enum ofperr decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits, const void *action, ovs_be16 action_len, size_t oxm_offset, struct ofpbuf *ofpacts) { struct ofpact_reg_move *move; enum ofperr error; struct ofpbuf b; move = ofpact_put_REG_MOVE(ofpacts); move->ofpact.raw = ONFACT_RAW13_COPY_FIELD; move->src.ofs = ntohs(src_offset); move->src.n_bits = ntohs(n_bits); move->dst.ofs = ntohs(dst_offset); move->dst.n_bits = ntohs(n_bits); ofpbuf_use_const(&b, action, ntohs(action_len)); ofpbuf_pull(&b, oxm_offset); error = nx_pull_header(&b, &move->src.field, NULL); if (error) { return error; } error = nx_pull_header(&b, &move->dst.field, NULL); if (error) { return error; } if (!is_all_zeros(b.data, b.size)) { return OFPERR_NXBRC_MUST_BE_ZERO; } return nxm_reg_move_check(move, NULL); } static enum ofperr decode_OFPAT_RAW15_COPY_FIELD(const struct ofp15_action_copy_field *oacf, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { return decode_copy_field__(oacf->src_offset, oacf->dst_offset, oacf->n_bits, oacf, oacf->len, OBJECT_OFFSETOF(oacf, pad2), ofpacts); } static enum ofperr decode_ONFACT_RAW13_COPY_FIELD(const struct onf_action_copy_field *oacf, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { return decode_copy_field__(oacf->src_offset, oacf->dst_offset, oacf->n_bits, oacf, oacf->len, OBJECT_OFFSETOF(oacf, pad3), ofpacts); } static enum ofperr decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_reg_move *move; enum ofperr error; struct ofpbuf b; move = ofpact_put_REG_MOVE(ofpacts); move->ofpact.raw = NXAST_RAW_REG_MOVE; move->src.ofs = ntohs(narm->src_ofs); move->src.n_bits = ntohs(narm->n_bits); move->dst.ofs = ntohs(narm->dst_ofs); move->dst.n_bits = ntohs(narm->n_bits); ofpbuf_use_const(&b, narm, ntohs(narm->len)); ofpbuf_pull(&b, sizeof *narm); error = nx_pull_header(&b, &move->src.field, NULL); if (error) { return error; } error = nx_pull_header(&b, &move->dst.field, NULL); if (error) { return error; } if (!is_all_zeros(b.data, b.size)) { return OFPERR_NXBRC_MUST_BE_ZERO; } return nxm_reg_move_check(move, NULL); } static void encode_REG_MOVE(const struct ofpact_reg_move *move, enum ofp_version ofp_version, struct ofpbuf *out) { /* For OpenFlow 1.3, the choice of ONFACT_RAW13_COPY_FIELD versus * NXAST_RAW_REG_MOVE is somewhat difficult. Neither one is guaranteed to * be supported by every OpenFlow 1.3 implementation. It would be ideal to * probe for support. Until we have that ability, we currently prefer * NXAST_RAW_REG_MOVE for backward compatibility with older Open vSwitch * versions. */ size_t start_ofs = out->size; if (ofp_version >= OFP15_VERSION) { struct ofp15_action_copy_field *copy = put_OFPAT15_COPY_FIELD(out); copy->n_bits = htons(move->dst.n_bits); copy->src_offset = htons(move->src.ofs); copy->dst_offset = htons(move->dst.ofs); out->size = out->size - sizeof copy->pad2; nx_put_header(out, move->src.field->id, ofp_version, false); nx_put_header(out, move->dst.field->id, ofp_version, false); } else if (ofp_version == OFP13_VERSION && move->ofpact.raw == ONFACT_RAW13_COPY_FIELD) { struct onf_action_copy_field *copy = put_ONFACT13_COPY_FIELD(out); copy->n_bits = htons(move->dst.n_bits); copy->src_offset = htons(move->src.ofs); copy->dst_offset = htons(move->dst.ofs); out->size = out->size - sizeof copy->pad3; nx_put_header(out, move->src.field->id, ofp_version, false); nx_put_header(out, move->dst.field->id, ofp_version, false); } else { struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out); narm->n_bits = htons(move->dst.n_bits); narm->src_ofs = htons(move->src.ofs); narm->dst_ofs = htons(move->dst.ofs); nx_put_header(out, move->src.field->id, 0, false); nx_put_header(out, move->dst.field->id, 0, false); } pad_ofpat(out, start_ofs); } static char * OVS_WARN_UNUSED_RESULT parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); const char *full_arg = arg; char *error; error = mf_parse_subfield__(&move->src, &arg); if (error) { return error; } if (strncmp(arg, "->", 2)) { return xasprintf("%s: missing `->' following source", full_arg); } arg += 2; error = mf_parse_subfield(&move->dst, arg); if (error) { return error; } if (move->src.n_bits != move->dst.n_bits) { return xasprintf("%s: source field is %d bits wide but destination is " "%d bits wide", full_arg, move->src.n_bits, move->dst.n_bits); } return NULL; } static void format_REG_MOVE(const struct ofpact_reg_move *a, struct ds *s) { nxm_format_reg_move(a, s); } /* Action structure for OFPAT12_SET_FIELD. */ struct ofp12_action_set_field { ovs_be16 type; /* OFPAT12_SET_FIELD. */ ovs_be16 len; /* Length is padded to 64 bits. */ /* Followed by: * - An OXM header, value, and (in OpenFlow 1.5+) optionally a mask. * - Enough 0-bytes to pad out to a multiple of 64 bits. * * The "pad" member is the beginning of the above. */ uint8_t pad[4]; }; OFP_ASSERT(sizeof(struct ofp12_action_set_field) == 8); /* Action structure for NXAST_REG_LOAD. * * Copies value[0:n_bits] to dst[ofs:ofs+n_bits], where a[b:c] denotes the bits * within 'a' numbered 'b' through 'c' (not including bit 'c'). Bit numbering * starts at 0 for the least-significant bit, 1 for the next most significant * bit, and so on. * * 'dst' is an nxm_header with nxm_hasmask=0. See the documentation for * NXAST_REG_MOVE, above, for the permitted fields and for the side effects of * loading them. * * The 'ofs' and 'n_bits' fields are combined into a single 'ofs_nbits' field * to avoid enlarging the structure by another 8 bytes. To allow 'n_bits' to * take a value between 1 and 64 (inclusive) while taking up only 6 bits, it is * also stored as one less than its true value: * * 15 6 5 0 * +------------------------------+------------------+ * | ofs | n_bits - 1 | * +------------------------------+------------------+ * * The switch will reject actions for which ofs+n_bits is greater than the * width of 'dst', or in which any bits in 'value' with value 2**n_bits or * greater are set to 1, with error type OFPET_BAD_ACTION, code * OFPBAC_BAD_ARGUMENT. */ struct nx_action_reg_load { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_REG_LOAD. */ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ ovs_be32 dst; /* Destination register. */ ovs_be64 value; /* Immediate value. */ }; OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); /* Action structure for NXAST_REG_LOAD2. * * Compared to OFPAT_SET_FIELD, we can use this to set whole or partial fields * in any OpenFlow version. Compared to NXAST_REG_LOAD, we can use this to set * OXM experimenter fields. */ struct nx_action_reg_load2 { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_SET_FIELD. */ /* Followed by: * - An NXM/OXM header, value, and optionally a mask. * - Enough 0-bytes to pad out to a multiple of 64 bits. * * The "pad" member is the beginning of the above. */ uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct nx_action_reg_load2) == 16); static enum ofperr decode_ofpat_set_field(const struct ofp12_action_set_field *oasf, bool may_mask, struct ofpbuf *ofpacts) { struct ofpact_set_field *sf; enum ofperr error; struct ofpbuf b; sf = ofpact_put_SET_FIELD(ofpacts); ofpbuf_use_const(&b, oasf, ntohs(oasf->len)); ofpbuf_pull(&b, OBJECT_OFFSETOF(oasf, pad)); error = nx_pull_entry(&b, &sf->field, &sf->value, may_mask ? &sf->mask : NULL); if (error) { return (error == OFPERR_OFPBMC_BAD_MASK ? OFPERR_OFPBAC_BAD_SET_MASK : error); } if (!may_mask) { memset(&sf->mask, 0xff, sf->field->n_bytes); } if (!is_all_zeros(b.data, b.size)) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } /* OpenFlow says specifically that one may not set OXM_OF_IN_PORT via * Set-Field. */ if (sf->field->id == MFF_IN_PORT_OXM) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } /* oxm_length is now validated to be compatible with mf_value. */ if (!sf->field->writable) { VLOG_WARN_RL(&rl, "destination field %s is not writable", sf->field->name); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } /* The value must be valid for match. OpenFlow 1.5 also says, * "In an OXM_OF_VLAN_VID set-field action, the OFPVID_PRESENT bit must be * a 1-bit in oxm_value and in oxm_mask." */ if (!mf_is_value_valid(sf->field, &sf->value) || (sf->field->id == MFF_VLAN_VID && (!(sf->mask.be16 & htons(OFPVID12_PRESENT)) || !(sf->value.be16 & htons(OFPVID12_PRESENT))))) { struct ds ds = DS_EMPTY_INITIALIZER; mf_format(sf->field, &sf->value, NULL, &ds); VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s", sf->field->name, ds_cstr(&ds)); ds_destroy(&ds); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } return 0; } static enum ofperr decode_OFPAT_RAW12_SET_FIELD(const struct ofp12_action_set_field *oasf, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { return decode_ofpat_set_field(oasf, false, ofpacts); } static enum ofperr decode_OFPAT_RAW15_SET_FIELD(const struct ofp12_action_set_field *oasf, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { return decode_ofpat_set_field(oasf, true, ofpacts); } static enum ofperr decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_set_field *sf = ofpact_put_reg_load(out); struct mf_subfield dst; enum ofperr error; sf->ofpact.raw = NXAST_RAW_REG_LOAD; dst.field = mf_from_nxm_header(ntohl(narl->dst)); dst.ofs = nxm_decode_ofs(narl->ofs_nbits); dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); error = mf_check_dst(&dst, NULL); if (error) { return error; } /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in * narl->value. */ if (dst.n_bits < 64 && ntohll(narl->value) >> dst.n_bits) { return OFPERR_OFPBAC_BAD_ARGUMENT; } sf->field = dst.field; bitwise_put(ntohll(narl->value), &sf->value, dst.field->n_bytes, dst.ofs, dst.n_bits); bitwise_put(UINT64_MAX, &sf->mask, dst.field->n_bytes, dst.ofs, dst.n_bits); return 0; } static enum ofperr decode_NXAST_RAW_REG_LOAD2(const struct nx_action_reg_load2 *narl, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_set_field *sf; enum ofperr error; struct ofpbuf b; sf = ofpact_put_SET_FIELD(out); sf->ofpact.raw = NXAST_RAW_REG_LOAD2; ofpbuf_use_const(&b, narl, ntohs(narl->len)); ofpbuf_pull(&b, OBJECT_OFFSETOF(narl, pad)); error = nx_pull_entry(&b, &sf->field, &sf->value, &sf->mask); if (error) { return error; } if (!is_all_zeros(b.data, b.size)) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } if (!sf->field->writable) { VLOG_WARN_RL(&rl, "destination field %s is not writable", sf->field->name); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } return 0; } static void ofpact_put_set_field(struct ofpbuf *openflow, enum ofp_version ofp_version, enum mf_field_id field, uint64_t value_) { struct ofp12_action_set_field *oasf OVS_UNUSED; int n_bytes = mf_from_id(field)->n_bytes; size_t start_ofs = openflow->size; union mf_value value; value.be64 = htonll(value_ << (8 * (8 - n_bytes))); oasf = put_OFPAT12_SET_FIELD(openflow); openflow->size = openflow->size - sizeof oasf->pad; nx_put_entry(openflow, field, ofp_version, &value, NULL); pad_ofpat(openflow, start_ofs); } static bool next_load_segment(const struct ofpact_set_field *sf, struct mf_subfield *dst, uint64_t *value) { int n_bits = sf->field->n_bits; int n_bytes = sf->field->n_bytes; int start = dst->ofs + dst->n_bits; if (start < n_bits) { dst->field = sf->field; dst->ofs = bitwise_scan(&sf->mask, n_bytes, 1, start, n_bits); if (dst->ofs < n_bits) { dst->n_bits = bitwise_scan(&sf->mask, n_bytes, 0, dst->ofs + 1, MIN(dst->ofs + 64, n_bits)) - dst->ofs; *value = bitwise_get(&sf->value, n_bytes, dst->ofs, dst->n_bits); return true; } } return false; } /* Convert 'sf' to a series of REG_LOADs. */ static void set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow) { /* If 'sf' cannot be encoded as NXAST_REG_LOAD because it requires an * experimenter OXM or is variable length (or if it came in as * NXAST_REG_LOAD2), encode as NXAST_REG_LOAD2. Otherwise use * NXAST_REG_LOAD, which is backward compatible. */ if (sf->ofpact.raw == NXAST_RAW_REG_LOAD2 || !mf_nxm_header(sf->field->id) || sf->field->variable_len) { struct nx_action_reg_load2 *narl OVS_UNUSED; size_t start_ofs = openflow->size; narl = put_NXAST_REG_LOAD2(openflow); openflow->size = openflow->size - sizeof narl->pad; nx_put_entry(openflow, sf->field->id, 0, &sf->value, &sf->mask); pad_ofpat(openflow, start_ofs); } else { struct mf_subfield dst; uint64_t value; dst.ofs = dst.n_bits = 0; while (next_load_segment(sf, &dst, &value)) { struct nx_action_reg_load *narl = put_NXAST_REG_LOAD(openflow); narl->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); narl->dst = htonl(mf_nxm_header(dst.field->id)); narl->value = htonll(value); } } } /* Convert 'sf', which must set an entire field, to standard OpenFlow 1.0/1.1 * actions, if we can, falling back to Nicira extensions if we must. * * We check only meta-flow types that can appear within set field actions and * that have a mapping to compatible action types. These struct mf_field * definitions have a defined OXM or NXM header value and specify the field as * writable. */ static void set_field_to_legacy_openflow(const struct ofpact_set_field *sf, enum ofp_version ofp_version, struct ofpbuf *out) { switch ((int) sf->field->id) { case MFF_VLAN_TCI: { ovs_be16 tci = sf->value.be16; bool cfi = (tci & htons(VLAN_CFI)) != 0; uint16_t vid = vlan_tci_to_vid(tci); uint8_t pcp = vlan_tci_to_pcp(tci); if (ofp_version < OFP11_VERSION) { /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping: * * If CFI=1, Add or modify VLAN VID & PCP. * If CFI=0, strip VLAN header, if any. */ if (cfi) { put_OFPAT10_SET_VLAN_VID(out, vid); put_OFPAT10_SET_VLAN_PCP(out, pcp); } else { put_OFPAT10_STRIP_VLAN(out); } } else { /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping: * * If CFI=1, Add or modify VLAN VID & PCP. * OpenFlow 1.1 set actions only apply if the packet * already has VLAN tags. To be sure that is the case * we have to push a VLAN header. As we do not support * multiple layers of VLANs, this is a no-op, if a VLAN * header already exists. This may backfire, however, * when we start supporting multiple layers of VLANs. * If CFI=0, strip VLAN header, if any. */ if (cfi) { /* Push a VLAN tag, if one was not seen at action validation * time. */ if (!sf->flow_has_vlan) { put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q)); } put_OFPAT11_SET_VLAN_VID(out, vid); put_OFPAT11_SET_VLAN_PCP(out, pcp); } else { /* If the flow did not match on vlan, we have no way of * knowing if the vlan tag exists, so we must POP just to be * sure. */ put_OFPAT11_POP_VLAN(out); } } break; } case MFF_VLAN_VID: { uint16_t vid = ntohs(sf->value.be16) & VLAN_VID_MASK; if (ofp_version == OFP10_VERSION) { put_OFPAT10_SET_VLAN_VID(out, vid); } else { put_OFPAT11_SET_VLAN_VID(out, vid); } break; } case MFF_VLAN_PCP: if (ofp_version == OFP10_VERSION) { put_OFPAT10_SET_VLAN_PCP(out, sf->value.u8); } else { put_OFPAT11_SET_VLAN_PCP(out, sf->value.u8); } break; case MFF_ETH_SRC: put_OFPAT_SET_DL_SRC(out, ofp_version)->dl_addr = sf->value.mac; break; case MFF_ETH_DST: put_OFPAT_SET_DL_DST(out, ofp_version)->dl_addr = sf->value.mac; break; case MFF_IPV4_SRC: put_OFPAT_SET_NW_SRC(out, ofp_version, sf->value.be32); break; case MFF_IPV4_DST: put_OFPAT_SET_NW_DST(out, ofp_version, sf->value.be32); break; case MFF_IP_DSCP: put_OFPAT_SET_NW_TOS(out, ofp_version, sf->value.u8); break; case MFF_IP_DSCP_SHIFTED: put_OFPAT_SET_NW_TOS(out, ofp_version, sf->value.u8 << 2); break; case MFF_TCP_SRC: case MFF_UDP_SRC: put_OFPAT_SET_TP_SRC(out, sf->value.be16); break; case MFF_TCP_DST: case MFF_UDP_DST: put_OFPAT_SET_TP_DST(out, sf->value.be16); break; default: set_field_to_nxast(sf, out); break; } } static void set_field_to_set_field(const struct ofpact_set_field *sf, enum ofp_version ofp_version, struct ofpbuf *out) { struct ofp12_action_set_field *oasf OVS_UNUSED; size_t start_ofs = out->size; oasf = put_OFPAT12_SET_FIELD(out); out->size = out->size - sizeof oasf->pad; nx_put_entry(out, sf->field->id, ofp_version, &sf->value, &sf->mask); pad_ofpat(out, start_ofs); } static void encode_SET_FIELD(const struct ofpact_set_field *sf, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version >= OFP15_VERSION) { /* OF1.5+ only has Set-Field (reg_load is redundant so we drop it * entirely). */ set_field_to_set_field(sf, ofp_version, out); } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD || sf->ofpact.raw == NXAST_RAW_REG_LOAD2) { /* It came in as reg_load, send it out the same way. */ set_field_to_nxast(sf, out); } else if (ofp_version < OFP12_VERSION) { /* OpenFlow 1.0 and 1.1 don't have Set-Field. */ set_field_to_legacy_openflow(sf, ofp_version, out); } else if (is_all_ones((const uint8_t *) &sf->mask, sf->field->n_bytes)) { /* We're encoding to OpenFlow 1.2, 1.3, or 1.4. The action sets an * entire field, so encode it as OFPAT_SET_FIELD. */ set_field_to_set_field(sf, ofp_version, out); } else { /* We're encoding to OpenFlow 1.2, 1.3, or 1.4. The action cannot be * encoded as OFPAT_SET_FIELD because it does not set an entire field, * so encode it as reg_load. */ set_field_to_nxast(sf, out); } } /* Parses the input argument 'arg' into the key, value, and delimiter * components that are common across the reg_load and set_field action format. * * With an argument like "1->metadata", sets the following pointers to * point within 'arg': * key: "metadata" * value: "1" * delim: "->" * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT set_field_split_str(char *arg, char **key, char **value, char **delim) { char *value_end; *value = arg; value_end = strstr(arg, "->"); *key = value_end + strlen("->"); if (delim) { *delim = value_end; } if (!value_end) { return xasprintf("%s: missing `->'", arg); } if (strlen(value_end) <= strlen("->")) { return xasprintf("%s: missing field name following `->'", arg); } return NULL; } /* Parses a "set_field" action with argument 'arg', appending the parsed * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT set_field_parse__(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); char *value; char *delim; char *key; const struct mf_field *mf; char *error; error = set_field_split_str(arg, &key, &value, &delim); if (error) { return error; } mf = mf_from_name(key); if (!mf) { return xasprintf("%s is not a valid OXM field name", key); } if (!mf->writable) { return xasprintf("%s is read-only", key); } sf->field = mf; delim[0] = '\0'; error = mf_parse(mf, value, &sf->value, &sf->mask); if (error) { return error; } if (!mf_is_value_valid(mf, &sf->value)) { return xasprintf("%s is not a valid value for field %s", value, key); } *usable_protocols &= mf->usable_protocols_exact; return NULL; } /* Parses 'arg' as the argument to a "set_field" action, and appends such an * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT parse_SET_FIELD(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { char *copy = xstrdup(arg); char *error = set_field_parse__(copy, ofpacts, usable_protocols); free(copy); return error; } static char * OVS_WARN_UNUSED_RESULT parse_reg_load(char *arg, struct ofpbuf *ofpacts) { struct ofpact_set_field *sf = ofpact_put_reg_load(ofpacts); struct mf_subfield dst; char *key, *value_str; union mf_value value; char *error; error = set_field_split_str(arg, &key, &value_str, NULL); if (error) { return error; } error = mf_parse_subfield(&dst, key); if (error) { return error; } if (parse_int_string(value_str, (uint8_t *)&value, dst.field->n_bytes, &key)) { return xasprintf("%s: cannot parse integer value", arg); } if (!bitwise_is_all_zeros(&value, dst.field->n_bytes, dst.n_bits, dst.field->n_bytes * 8 - dst.n_bits)) { struct ds ds; ds_init(&ds); mf_format(dst.field, &value, NULL, &ds); error = xasprintf("%s: value %s does not fit into %d bits", arg, ds_cstr(&ds), dst.n_bits); ds_destroy(&ds); return error; } sf->field = dst.field; memset(&sf->value, 0, sizeof sf->value); bitwise_copy(&value, dst.field->n_bytes, 0, &sf->value, dst.field->n_bytes, dst.ofs, dst.n_bits); bitwise_one(&sf->mask, dst.field->n_bytes, dst.ofs, dst.n_bits); return NULL; } static void format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s) { if (a->ofpact.raw == NXAST_RAW_REG_LOAD) { struct mf_subfield dst; uint64_t value; dst.ofs = dst.n_bits = 0; while (next_load_segment(a, &dst, &value)) { ds_put_format(s, "load:%#"PRIx64"->", value); mf_format_subfield(&dst, s); ds_put_char(s, ','); } ds_chomp(s, ','); } else { ds_put_cstr(s, "set_field:"); mf_format(a->field, &a->value, &a->mask, s); ds_put_format(s, "->%s", a->field->name); } } /* Appends an OFPACT_SET_FIELD ofpact to 'ofpacts' and returns it. The ofpact * is marked such that, if possible, it will be translated to OpenFlow as * NXAST_REG_LOAD extension actions rather than OFPAT_SET_FIELD, either because * that was the way that the action was expressed when it came into OVS or for * backward compatibility. */ struct ofpact_set_field * ofpact_put_reg_load(struct ofpbuf *ofpacts) { struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); sf->ofpact.raw = NXAST_RAW_REG_LOAD; return sf; } /* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP. * * Pushes (or pops) field[offset: offset + n_bits] to (or from) * top of the stack. */ struct nx_action_stack { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. */ ovs_be16 offset; /* Bit offset into the field. */ /* Followed by: * - OXM/NXM header for field to push or pop (4 or 8 bytes). * - ovs_be16 'n_bits', the number of bits to extract from the field. * - Enough 0-bytes to pad out the action to 24 bytes. */ uint8_t pad[12]; /* See above. */ }; OFP_ASSERT(sizeof(struct nx_action_stack) == 24); static enum ofperr decode_stack_action(const struct nx_action_stack *nasp, struct ofpact_stack *stack_action) { enum ofperr error; struct ofpbuf b; stack_action->subfield.ofs = ntohs(nasp->offset); ofpbuf_use_const(&b, nasp, sizeof *nasp); ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad)); error = nx_pull_header(&b, &stack_action->subfield.field, NULL); if (error) { return error; } stack_action->subfield.n_bits = ntohs(*(const ovs_be16 *) b.data); ofpbuf_pull(&b, 2); if (!is_all_zeros(b.data, b.size)) { return OFPERR_NXBRC_MUST_BE_ZERO; } return 0; } static enum ofperr decode_NXAST_RAW_STACK_PUSH(const struct nx_action_stack *nasp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_stack *push = ofpact_put_STACK_PUSH(ofpacts); enum ofperr error = decode_stack_action(nasp, push); return error ? error : nxm_stack_push_check(push, NULL); } static enum ofperr decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts); enum ofperr error = decode_stack_action(nasp, pop); return error ? error : nxm_stack_pop_check(pop, NULL); } static void encode_STACK_op(const struct ofpact_stack *stack_action, struct nx_action_stack *nasp) { struct ofpbuf b; ovs_be16 n_bits; nasp->offset = htons(stack_action->subfield.ofs); ofpbuf_use_stack(&b, nasp, ntohs(nasp->len)); ofpbuf_put_uninit(&b, OBJECT_OFFSETOF(nasp, pad)); nx_put_header(&b, stack_action->subfield.field->id, 0, false); n_bits = htons(stack_action->subfield.n_bits); ofpbuf_put(&b, &n_bits, sizeof n_bits); } static void encode_STACK_PUSH(const struct ofpact_stack *stack, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { encode_STACK_op(stack, put_NXAST_STACK_PUSH(out)); } static void encode_STACK_POP(const struct ofpact_stack *stack, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { encode_STACK_op(stack, put_NXAST_STACK_POP(out)); } static char * OVS_WARN_UNUSED_RESULT parse_STACK_PUSH(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); } static char * OVS_WARN_UNUSED_RESULT parse_STACK_POP(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); } static void format_STACK_PUSH(const struct ofpact_stack *a, struct ds *s) { nxm_format_stack_push(a, s); } static void format_STACK_POP(const struct ofpact_stack *a, struct ds *s) { nxm_format_stack_pop(a, s); } /* Action structure for NXAST_DEC_TTL_CNT_IDS. * * If the packet is not IPv4 or IPv6, does nothing. For IPv4 or IPv6, if the * TTL or hop limit is at least 2, decrements it by 1. Otherwise, if TTL or * hop limit is 0 or 1, sends a packet-in to the controllers with each of the * 'n_controllers' controller IDs specified in 'cnt_ids'. * * (This differs from NXAST_DEC_TTL in that for NXAST_DEC_TTL the packet-in is * sent only to controllers with id 0.) */ struct nx_action_cnt_ids { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length including slaves. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_DEC_TTL_CNT_IDS. */ ovs_be16 n_controllers; /* Number of controllers. */ uint8_t zeros[4]; /* Must be zero. */ /* Followed by 1 or more controller ids. * * uint16_t cnt_ids[]; // Controller ids. * uint8_t pad[]; // Must be 0 to 8-byte align cnt_ids[]. */ }; OFP_ASSERT(sizeof(struct nx_action_cnt_ids) == 16); static enum ofperr decode_OFPAT_RAW_DEC_NW_TTL(struct ofpbuf *out) { uint16_t id = 0; struct ofpact_cnt_ids *ids; enum ofperr error = 0; ids = ofpact_put_DEC_TTL(out); ids->n_controllers = 1; ofpbuf_put(out, &id, sizeof id); ids = out->header; ofpact_update_len(out, &ids->ofpact); return error; } static enum ofperr decode_NXAST_RAW_DEC_TTL_CNT_IDS(const struct nx_action_cnt_ids *nac_ids, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_cnt_ids *ids; size_t ids_size; int i; ids = ofpact_put_DEC_TTL(out); ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS; ids->n_controllers = ntohs(nac_ids->n_controllers); ids_size = ntohs(nac_ids->len) - sizeof *nac_ids; if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) { return OFPERR_NXBRC_MUST_BE_ZERO; } if (ids_size < ids->n_controllers * sizeof(ovs_be16)) { VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %"PRIuSIZE" " "bytes allocated for controller ids. %"PRIuSIZE" bytes " "are required for %"PRIu16" controllers.", ids_size, ids->n_controllers * sizeof(ovs_be16), ids->n_controllers); return OFPERR_OFPBAC_BAD_LEN; } for (i = 0; i < ids->n_controllers; i++) { uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]); ofpbuf_put(out, &id, sizeof id); ids = out->header; } ofpact_update_len(out, &ids->ofpact); return 0; } static void encode_DEC_TTL(const struct ofpact_cnt_ids *dec_ttl, enum ofp_version ofp_version, struct ofpbuf *out) { if (dec_ttl->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS || dec_ttl->n_controllers != 1 || dec_ttl->cnt_ids[0] != 0) { struct nx_action_cnt_ids *nac_ids = put_NXAST_DEC_TTL_CNT_IDS(out); int ids_len = ROUND_UP(2 * dec_ttl->n_controllers, OFP_ACTION_ALIGN); ovs_be16 *ids; size_t i; nac_ids->len = htons(ntohs(nac_ids->len) + ids_len); nac_ids->n_controllers = htons(dec_ttl->n_controllers); ids = ofpbuf_put_zeros(out, ids_len); for (i = 0; i < dec_ttl->n_controllers; i++) { ids[i] = htons(dec_ttl->cnt_ids[i]); } } else { put_OFPAT_DEC_NW_TTL(out, ofp_version); } } static void parse_noargs_dec_ttl(struct ofpbuf *ofpacts) { struct ofpact_cnt_ids *ids; uint16_t id = 0; ofpact_put_DEC_TTL(ofpacts); ofpbuf_put(ofpacts, &id, sizeof id); ids = ofpacts->header; ids->n_controllers++; ofpact_update_len(ofpacts, &ids->ofpact); } static char * OVS_WARN_UNUSED_RESULT parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { if (*arg == '\0') { parse_noargs_dec_ttl(ofpacts); } else { struct ofpact_cnt_ids *ids; char *cntr; ids = ofpact_put_DEC_TTL(ofpacts); ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS; for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL; cntr = strtok_r(NULL, ", ", &arg)) { uint16_t id = atoi(cntr); ofpbuf_put(ofpacts, &id, sizeof id); ids = ofpacts->header; ids->n_controllers++; } if (!ids->n_controllers) { return xstrdup("dec_ttl_cnt_ids: expected at least one controller " "id."); } ofpact_update_len(ofpacts, &ids->ofpact); } return NULL; } static void format_DEC_TTL(const struct ofpact_cnt_ids *a, struct ds *s) { size_t i; ds_put_cstr(s, "dec_ttl"); if (a->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) { ds_put_cstr(s, "("); for (i = 0; i < a->n_controllers; i++) { if (i) { ds_put_cstr(s, ","); } ds_put_format(s, "%"PRIu16, a->cnt_ids[i]); } ds_put_cstr(s, ")"); } } /* Set MPLS label actions. */ static enum ofperr decode_OFPAT_RAW_SET_MPLS_LABEL(ovs_be32 label, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_MPLS_LABEL(out)->label = label; return 0; } static void encode_SET_MPLS_LABEL(const struct ofpact_mpls_label *label, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version < OFP12_VERSION) { put_OFPAT_SET_MPLS_LABEL(out, ofp_version, label->label); } else { ofpact_put_set_field(out, ofp_version, MFF_MPLS_LABEL, ntohl(label->label)); } } static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_LABEL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(ofpacts); if (*arg == '\0') { return xstrdup("set_mpls_label: expected label."); } mpls_label->label = htonl(atoi(arg)); return NULL; } static void format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a, struct ds *s) { ds_put_format(s, "set_mpls_label(%"PRIu32")", ntohl(a->label)); } /* Set MPLS TC actions. */ static enum ofperr decode_OFPAT_RAW_SET_MPLS_TC(uint8_t tc, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_MPLS_TC(out)->tc = tc; return 0; } static void encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version < OFP12_VERSION) { put_OFPAT_SET_MPLS_TC(out, ofp_version, tc->tc); } else { ofpact_put_set_field(out, ofp_version, MFF_MPLS_TC, tc->tc); } } static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_TC(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(ofpacts); if (*arg == '\0') { return xstrdup("set_mpls_tc: expected tc."); } mpls_tc->tc = atoi(arg); return NULL; } static void format_SET_MPLS_TC(const struct ofpact_mpls_tc *a, struct ds *s) { ds_put_format(s, "set_mpls_ttl(%"PRIu8")", a->tc); } /* Set MPLS TTL actions. */ static enum ofperr decode_OFPAT_RAW_SET_MPLS_TTL(uint8_t ttl, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_MPLS_TTL(out)->ttl = ttl; return 0; } static void encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl *ttl, enum ofp_version ofp_version, struct ofpbuf *out) { put_OFPAT_SET_MPLS_TTL(out, ofp_version, ttl->ttl); } /* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT parse_SET_MPLS_TTL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(ofpacts); if (*arg == '\0') { return xstrdup("set_mpls_ttl: expected ttl."); } mpls_ttl->ttl = atoi(arg); return NULL; } static void format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a, struct ds *s) { ds_put_format(s, "set_mpls_ttl(%"PRIu8")", a->ttl); } /* Decrement MPLS TTL actions. */ static enum ofperr decode_OFPAT_RAW_DEC_MPLS_TTL(struct ofpbuf *out) { ofpact_put_DEC_MPLS_TTL(out); return 0; } static void encode_DEC_MPLS_TTL(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version, struct ofpbuf *out) { put_OFPAT_DEC_MPLS_TTL(out, ofp_version); } static char * OVS_WARN_UNUSED_RESULT parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_DEC_MPLS_TTL(ofpacts); return NULL; } static void format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "dec_mpls_ttl"); } /* Push MPLS label action. */ static enum ofperr decode_OFPAT_RAW_PUSH_MPLS(ovs_be16 ethertype, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_push_mpls *oam; if (!eth_type_mpls(ethertype)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } oam = ofpact_put_PUSH_MPLS(out); oam->ethertype = ethertype; return 0; } static void encode_PUSH_MPLS(const struct ofpact_push_mpls *push_mpls, enum ofp_version ofp_version, struct ofpbuf *out) { put_OFPAT_PUSH_MPLS(out, ofp_version, push_mpls->ethertype); } static char * OVS_WARN_UNUSED_RESULT parse_PUSH_MPLS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint16_t ethertype; char *error; error = str_to_u16(arg, "push_mpls", ðertype); if (!error) { ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype); } return error; } static void format_PUSH_MPLS(const struct ofpact_push_mpls *a, struct ds *s) { ds_put_format(s, "push_mpls:0x%04"PRIx16, ntohs(a->ethertype)); } /* Pop MPLS label action. */ static enum ofperr decode_OFPAT_RAW_POP_MPLS(ovs_be16 ethertype, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_POP_MPLS(out)->ethertype = ethertype; return 0; } static void encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls, enum ofp_version ofp_version, struct ofpbuf *out) { put_OFPAT_POP_MPLS(out, ofp_version, pop_mpls->ethertype); } static char * OVS_WARN_UNUSED_RESULT parse_POP_MPLS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint16_t ethertype; char *error; error = str_to_u16(arg, "pop_mpls", ðertype); if (!error) { ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype); } return error; } static void format_POP_MPLS(const struct ofpact_pop_mpls *a, struct ds *s) { ds_put_format(s, "pop_mpls:0x%04"PRIx16, ntohs(a->ethertype)); } /* Set tunnel ID actions. */ static enum ofperr decode_NXAST_RAW_SET_TUNNEL(uint32_t tun_id, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL; tunnel->tun_id = tun_id; return 0; } static enum ofperr decode_NXAST_RAW_SET_TUNNEL64(uint64_t tun_id, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out); tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL64; tunnel->tun_id = tun_id; return 0; } static void encode_SET_TUNNEL(const struct ofpact_tunnel *tunnel, enum ofp_version ofp_version, struct ofpbuf *out) { uint64_t tun_id = tunnel->tun_id; if (ofp_version < OFP12_VERSION) { if (tun_id <= UINT32_MAX && tunnel->ofpact.raw != NXAST_RAW_SET_TUNNEL64) { put_NXAST_SET_TUNNEL(out, tun_id); } else { put_NXAST_SET_TUNNEL64(out, tun_id); } } else { ofpact_put_set_field(out, ofp_version, MFF_TUN_ID, tun_id); } } static char * OVS_WARN_UNUSED_RESULT parse_set_tunnel(char *arg, struct ofpbuf *ofpacts, enum ofp_raw_action_type raw) { struct ofpact_tunnel *tunnel; tunnel = ofpact_put_SET_TUNNEL(ofpacts); tunnel->ofpact.raw = raw; return str_to_u64(arg, &tunnel->tun_id); } static char * OVS_WARN_UNUSED_RESULT parse_SET_TUNNEL(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return parse_set_tunnel(arg, ofpacts, NXAST_RAW_SET_TUNNEL); } static void format_SET_TUNNEL(const struct ofpact_tunnel *a, struct ds *s) { ds_put_format(s, "set_tunnel%s:%#"PRIx64, (a->tun_id > UINT32_MAX || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""), a->tun_id); } /* Set queue action. */ static enum ofperr decode_OFPAT_RAW_SET_QUEUE(uint32_t queue_id, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { ofpact_put_SET_QUEUE(out)->queue_id = queue_id; return 0; } static void encode_SET_QUEUE(const struct ofpact_queue *queue, enum ofp_version ofp_version, struct ofpbuf *out) { put_OFPAT_SET_QUEUE(out, ofp_version, queue->queue_id); } static char * OVS_WARN_UNUSED_RESULT parse_SET_QUEUE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id); } static void format_SET_QUEUE(const struct ofpact_queue *a, struct ds *s) { ds_put_format(s, "set_queue:%"PRIu32, a->queue_id); } /* Pop queue action. */ static enum ofperr decode_NXAST_RAW_POP_QUEUE(struct ofpbuf *out) { ofpact_put_POP_QUEUE(out); return 0; } static void encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { put_NXAST_POP_QUEUE(out); } static char * OVS_WARN_UNUSED_RESULT parse_POP_QUEUE(const char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_POP_QUEUE(ofpacts); return NULL; } static void format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "pop_queue"); } /* Action structure for NXAST_FIN_TIMEOUT. * * This action changes the idle timeout or hard timeout, or both, of this * OpenFlow rule when the rule matches a TCP packet with the FIN or RST flag. * When such a packet is observed, the action reduces the rule's idle timeout * to 'fin_idle_timeout' and its hard timeout to 'fin_hard_timeout'. This * action has no effect on an existing timeout that is already shorter than the * one that the action specifies. A 'fin_idle_timeout' or 'fin_hard_timeout' * of zero has no effect on the respective timeout. * * 'fin_idle_timeout' and 'fin_hard_timeout' are measured in seconds. * 'fin_hard_timeout' specifies time since the flow's creation, not since the * receipt of the FIN or RST. * * This is useful for quickly discarding learned TCP flows that otherwise will * take a long time to expire. * * This action is intended for use with an OpenFlow rule that matches only a * single TCP flow. If the rule matches multiple TCP flows (e.g. it wildcards * all TCP traffic, or all TCP traffic to a particular port), then any FIN or * RST in any of those flows will cause the entire OpenFlow rule to expire * early, which is not normally desirable. */ struct nx_action_fin_timeout { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_FIN_TIMEOUT. */ ovs_be16 fin_idle_timeout; /* New idle timeout, if nonzero. */ ovs_be16 fin_hard_timeout; /* New hard timeout, if nonzero. */ ovs_be16 pad; /* Must be zero. */ }; OFP_ASSERT(sizeof(struct nx_action_fin_timeout) == 16); static enum ofperr decode_NXAST_RAW_FIN_TIMEOUT(const struct nx_action_fin_timeout *naft, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(out); oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout); oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout); return 0; } static void encode_FIN_TIMEOUT(const struct ofpact_fin_timeout *fin_timeout, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct nx_action_fin_timeout *naft = put_NXAST_FIN_TIMEOUT(out); naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout); naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout); } static char * OVS_WARN_UNUSED_RESULT parse_FIN_TIMEOUT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(ofpacts); char *key, *value; while (ofputil_parse_key_value(&arg, &key, &value)) { char *error; if (!strcmp(key, "idle_timeout")) { error = str_to_u16(value, key, &oft->fin_idle_timeout); } else if (!strcmp(key, "hard_timeout")) { error = str_to_u16(value, key, &oft->fin_hard_timeout); } else { error = xasprintf("invalid key '%s' in 'fin_timeout' argument", key); } if (error) { return error; } } return NULL; } static void format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a, struct ds *s) { ds_put_cstr(s, "fin_timeout("); if (a->fin_idle_timeout) { ds_put_format(s, "idle_timeout=%"PRIu16",", a->fin_idle_timeout); } if (a->fin_hard_timeout) { ds_put_format(s, "hard_timeout=%"PRIu16",", a->fin_hard_timeout); } ds_chomp(s, ','); ds_put_char(s, ')'); } /* Action structures for NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE. * * These actions search one of the switch's flow tables: * * - For NXAST_RESUBMIT_TABLE only, if the 'table' member is not 255, then * it specifies the table to search. * * - Otherwise (for NXAST_RESUBMIT_TABLE with a 'table' of 255, or for * NXAST_RESUBMIT regardless of 'table'), it searches the current flow * table, that is, the OpenFlow flow table that contains the flow from * which this action was obtained. If this action did not come from a * flow table (e.g. it came from an OFPT_PACKET_OUT message), then table 0 * is the current table. * * The flow table lookup uses a flow that may be slightly modified from the * original lookup: * * - For NXAST_RESUBMIT, the 'in_port' member of struct nx_action_resubmit * is used as the flow's in_port. * * - For NXAST_RESUBMIT_TABLE, if the 'in_port' member is not OFPP_IN_PORT, * then its value is used as the flow's in_port. Otherwise, the original * in_port is used. * * - If actions that modify the flow (e.g. OFPAT_SET_VLAN_VID) precede the * resubmit action, then the flow is updated with the new values. * * Following the lookup, the original in_port is restored. * * If the modified flow matched in the flow table, then the corresponding * actions are executed. Afterward, actions following the resubmit in the * original set of actions, if any, are executed; any changes made to the * packet (e.g. changes to VLAN) by secondary actions persist when those * actions are executed, although the original in_port is restored. * * Resubmit actions may be used any number of times within a set of actions. * * Resubmit actions may nest to an implementation-defined depth. Beyond this * implementation-defined depth, further resubmit actions are simply ignored. * * NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires * 'pad' to be all-bits-zero. * * Open vSwitch 1.0.1 and earlier did not support recursion. Open vSwitch * before 1.2.90 did not support NXAST_RESUBMIT_TABLE. */ struct nx_action_resubmit { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_RESUBMIT. */ ovs_be16 in_port; /* New in_port for checking flow table. */ uint8_t table; /* NXAST_RESUBMIT_TABLE: table to use. */ uint8_t pad[3]; }; OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16); static enum ofperr decode_NXAST_RAW_RESUBMIT(uint16_t port, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_resubmit *resubmit; resubmit = ofpact_put_RESUBMIT(out); resubmit->ofpact.raw = NXAST_RAW_RESUBMIT; resubmit->in_port = u16_to_ofp(port); resubmit->table_id = 0xff; return 0; } static enum ofperr decode_NXAST_RAW_RESUBMIT_TABLE(const struct nx_action_resubmit *nar, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_resubmit *resubmit; if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { return OFPERR_OFPBAC_BAD_ARGUMENT; } resubmit = ofpact_put_RESUBMIT(out); resubmit->ofpact.raw = NXAST_RAW_RESUBMIT_TABLE; resubmit->in_port = u16_to_ofp(ntohs(nar->in_port)); resubmit->table_id = nar->table; return 0; } static void encode_RESUBMIT(const struct ofpact_resubmit *resubmit, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { uint16_t in_port = ofp_to_u16(resubmit->in_port); if (resubmit->table_id == 0xff && resubmit->ofpact.raw != NXAST_RAW_RESUBMIT_TABLE) { put_NXAST_RESUBMIT(out, in_port); } else { struct nx_action_resubmit *nar = put_NXAST_RESUBMIT_TABLE(out); nar->table = resubmit->table_id; nar->in_port = htons(in_port); } } static char * OVS_WARN_UNUSED_RESULT parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_resubmit *resubmit; char *in_port_s, *table_s; resubmit = ofpact_put_RESUBMIT(ofpacts); in_port_s = strsep(&arg, ","); if (in_port_s && in_port_s[0]) { if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { return xasprintf("%s: resubmit to unknown port", in_port_s); } } else { resubmit->in_port = OFPP_IN_PORT; } table_s = strsep(&arg, ","); if (table_s && table_s[0]) { uint32_t table_id = 0; char *error; error = str_to_u32(table_s, &table_id); if (error) { return error; } resubmit->table_id = table_id; } else { resubmit->table_id = 255; } if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { return xstrdup("at least one \"in_port\" or \"table\" must be " "specified on resubmit"); } return NULL; } static void format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s) { if (a->in_port != OFPP_IN_PORT && a->table_id == 255) { ds_put_cstr(s, "resubmit:"); ofputil_format_port(a->in_port, s); } else { ds_put_format(s, "resubmit("); if (a->in_port != OFPP_IN_PORT) { ofputil_format_port(a->in_port, s); } ds_put_char(s, ','); if (a->table_id != 255) { ds_put_format(s, "%"PRIu8, a->table_id); } ds_put_char(s, ')'); } } /* Action structure for NXAST_LEARN. * * This action adds or modifies a flow in an OpenFlow table, similar to * OFPT_FLOW_MOD with OFPFC_MODIFY_STRICT as 'command'. The new flow has the * specified idle timeout, hard timeout, priority, cookie, and flags. The new * flow's match criteria and actions are built by applying each of the series * of flow_mod_spec elements included as part of the action. * * A flow_mod_spec starts with a 16-bit header. A header that is all-bits-0 is * a no-op used for padding the action as a whole to a multiple of 8 bytes in * length. Otherwise, the flow_mod_spec can be thought of as copying 'n_bits' * bits from a source to a destination. In this case, the header contains * multiple fields: * * 15 14 13 12 11 10 0 * +------+---+------+---------------------------------+ * | 0 |src| dst | n_bits | * +------+---+------+---------------------------------+ * * The meaning and format of a flow_mod_spec depends on 'src' and 'dst'. The * following table summarizes the meaning of each possible combination. * Details follow the table: * * src dst meaning * --- --- ---------------------------------------------------------- * 0 0 Add match criteria based on value in a field. * 1 0 Add match criteria based on an immediate value. * 0 1 Add NXAST_REG_LOAD action to copy field into a different field. * 1 1 Add NXAST_REG_LOAD action to load immediate value into a field. * 0 2 Add OFPAT_OUTPUT action to output to port from specified field. * All other combinations are undefined and not allowed. * * The flow_mod_spec header is followed by a source specification and a * destination specification. The format and meaning of the source * specification depends on 'src': * * - If 'src' is 0, the source bits are taken from a field in the flow to * which this action is attached. (This should be a wildcarded field. If * its value is fully specified then the source bits being copied have * constant values.) * * The source specification is an ovs_be32 'field' and an ovs_be16 'ofs'. * 'field' is an nxm_header with nxm_hasmask=0, and 'ofs' the starting bit * offset within that field. The source bits are field[ofs:ofs+n_bits-1]. * 'field' and 'ofs' are subject to the same restrictions as the source * field in NXAST_REG_MOVE. * * - If 'src' is 1, the source bits are a constant value. The source * specification is (n_bits+15)/16*2 bytes long. Taking those bytes as a * number in network order, the source bits are the 'n_bits' * least-significant bits. The switch will report an error if other bits * in the constant are nonzero. * * The flow_mod_spec destination specification, for 'dst' of 0 or 1, is an * ovs_be32 'field' and an ovs_be16 'ofs'. 'field' is an nxm_header with * nxm_hasmask=0 and 'ofs' is a starting bit offset within that field. The * meaning of the flow_mod_spec depends on 'dst': * * - If 'dst' is 0, the flow_mod_spec specifies match criteria for the new * flow. The new flow matches only if bits field[ofs:ofs+n_bits-1] in a * packet equal the source bits. 'field' may be any nxm_header with * nxm_hasmask=0 that is allowed in NXT_FLOW_MOD. * * Order is significant. Earlier flow_mod_specs must satisfy any * prerequisites for matching fields specified later, by copying constant * values into prerequisite fields. * * The switch will reject flow_mod_specs that do not satisfy NXM masking * restrictions. * * - If 'dst' is 1, the flow_mod_spec specifies an NXAST_REG_LOAD action for * the new flow. The new flow copies the source bits into * field[ofs:ofs+n_bits-1]. Actions are executed in the same order as the * flow_mod_specs. * * A single NXAST_REG_LOAD action writes no more than 64 bits, so n_bits * greater than 64 yields multiple NXAST_REG_LOAD actions. * * The flow_mod_spec destination spec for 'dst' of 2 (when 'src' is 0) is * empty. It has the following meaning: * * - The flow_mod_spec specifies an OFPAT_OUTPUT action for the new flow. * The new flow outputs to the OpenFlow port specified by the source field. * Of the special output ports with value OFPP_MAX or larger, OFPP_IN_PORT, * OFPP_FLOOD, OFPP_LOCAL, and OFPP_ALL are supported. Other special ports * may not be used. * * Resource Management * ------------------- * * A switch has a finite amount of flow table space available for learning. * When this space is exhausted, no new learning table entries will be learned * until some existing flow table entries expire. The controller should be * prepared to handle this by flooding (which can be implemented as a * low-priority flow). * * If a learned flow matches a single TCP stream with a relatively long * timeout, one may make the best of resource constraints by setting * 'fin_idle_timeout' or 'fin_hard_timeout' (both measured in seconds), or * both, to shorter timeouts. When either of these is specified as a nonzero * value, OVS adds a NXAST_FIN_TIMEOUT action, with the specified timeouts, to * the learned flow. * * Examples * -------- * * The following examples give a prose description of the flow_mod_specs along * with informal notation for how those would be represented and a hex dump of * the bytes that would be required. * * These examples could work with various nx_action_learn parameters. Typical * values would be idle_timeout=OFP_FLOW_PERMANENT, hard_timeout=60, * priority=OFP_DEFAULT_PRIORITY, flags=0, table_id=10. * * 1. Learn input port based on the source MAC, with lookup into * NXM_NX_REG1[16:31] by resubmit to in_port=99: * * Match on in_port=99: * ovs_be16(src=1, dst=0, n_bits=16), 20 10 * ovs_be16(99), 00 63 * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 * * Match Ethernet destination on Ethernet source from packet: * ovs_be16(src=0, dst=0, n_bits=48), 00 30 * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 * * Set NXM_NX_REG1[16:31] to the packet's input port: * ovs_be16(src=0, dst=1, n_bits=16), 08 10 * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 * ovs_be32(NXM_NX_REG1), ovs_be16(16) 00 01 02 04 00 10 * * Given a packet that arrived on port A with Ethernet source address B, * this would set up the flow "in_port=99, dl_dst=B, * actions=load:A->NXM_NX_REG1[16..31]". * * In syntax accepted by ovs-ofctl, this action is: learn(in_port=99, * NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], * load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) * * 2. Output to input port based on the source MAC and VLAN VID, with lookup * into NXM_NX_REG1[16:31]: * * Match on same VLAN ID as packet: * ovs_be16(src=0, dst=0, n_bits=12), 00 0c * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 * ovs_be32(NXM_OF_VLAN_TCI), ovs_be16(0) 00 00 08 02 00 00 * * Match Ethernet destination on Ethernet source from packet: * ovs_be16(src=0, dst=0, n_bits=48), 00 30 * ovs_be32(NXM_OF_ETH_SRC), ovs_be16(0) 00 00 04 06 00 00 * ovs_be32(NXM_OF_ETH_DST), ovs_be16(0) 00 00 02 06 00 00 * * Output to the packet's input port: * ovs_be16(src=0, dst=2, n_bits=16), 10 10 * ovs_be32(NXM_OF_IN_PORT), ovs_be16(0) 00 00 00 02 00 00 * * Given a packet that arrived on port A with Ethernet source address B in * VLAN C, this would set up the flow "dl_dst=B, vlan_vid=C, * actions=output:A". * * In syntax accepted by ovs-ofctl, this action is: * learn(NXM_OF_VLAN_TCI[0..11], NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], * output:NXM_OF_IN_PORT[]) * * 3. Here's a recipe for a very simple-minded MAC learning switch. It uses a * 10-second MAC expiration time to make it easier to see what's going on * * ovs-vsctl del-controller br0 * ovs-ofctl del-flows br0 * ovs-ofctl add-flow br0 "table=0 actions=learn(table=1, \ hard_timeout=10, NXM_OF_VLAN_TCI[0..11], \ NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[], \ output:NXM_OF_IN_PORT[]), resubmit(,1)" * ovs-ofctl add-flow br0 "table=1 priority=0 actions=flood" * * You can then dump the MAC learning table with: * * ovs-ofctl dump-flows br0 table=1 * * Usage Advice * ------------ * * For best performance, segregate learned flows into a table that is not used * for any other flows except possibly for a lowest-priority "catch-all" flow * (a flow with no match criteria). If different learning actions specify * different match criteria, use different tables for the learned flows. * * The meaning of 'hard_timeout' and 'idle_timeout' can be counterintuitive. * These timeouts apply to the flow that is added, which means that a flow with * an idle timeout will expire when no traffic has been sent *to* the learned * address. This is not usually the intent in MAC learning; instead, we want * the MAC learn entry to expire when no traffic has been sent *from* the * learned address. Use a hard timeout for that. * * * Visibility of Changes * --------------------- * * Prior to Open vSwitch 2.4, any changes made by a "learn" action in a given * flow translation are visible to flow table lookups made later in the flow * translation. This means that, in the example above, a MAC learned by the * learn action in table 0 would be found in table 1 (if the packet being * processed had the same source and destination MAC address). * * In Open vSwitch 2.4 and later, changes to a flow table (whether to add or * modify a flow) by a "learn" action are visible only for later flow * translations, not for later lookups within the same flow translation. In * the MAC learning example, a MAC learned by the learn action in table 0 would * not be found in table 1 if the flow translation would resubmit to table 1 * after the processing of the learn action, meaning that if this MAC had not * been learned before then the packet would be flooded. */ struct nx_action_learn { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_LEARN. */ ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */ ovs_be16 hard_timeout; /* Max time before discarding (seconds). */ ovs_be16 priority; /* Priority level of flow entry. */ ovs_be64 cookie; /* Cookie for new flow. */ ovs_be16 flags; /* NX_LEARN_F_*. */ uint8_t table_id; /* Table to insert flow entry. */ uint8_t pad; /* Must be zero. */ ovs_be16 fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */ ovs_be16 fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */ /* Followed by a sequence of flow_mod_spec elements, as described above, * until the end of the action is reached. */ }; OFP_ASSERT(sizeof(struct nx_action_learn) == 32); static ovs_be16 get_be16(const void **pp) { const ovs_be16 *p = *pp; ovs_be16 value = *p; *pp = p + 1; return value; } static ovs_be32 get_be32(const void **pp) { const ovs_be32 *p = *pp; ovs_be32 value = get_unaligned_be32(p); *pp = p + 1; return value; } static void get_subfield(int n_bits, const void **p, struct mf_subfield *sf) { sf->field = mf_from_nxm_header(ntohl(get_be32(p))); sf->ofs = ntohs(get_be16(p)); sf->n_bits = n_bits; } static unsigned int learn_min_len(uint16_t header) { int n_bits = header & NX_LEARN_N_BITS_MASK; int src_type = header & NX_LEARN_SRC_MASK; int dst_type = header & NX_LEARN_DST_MASK; unsigned int min_len; min_len = 0; if (src_type == NX_LEARN_SRC_FIELD) { min_len += sizeof(ovs_be32); /* src_field */ min_len += sizeof(ovs_be16); /* src_ofs */ } else { min_len += 2 * DIV_ROUND_UP(n_bits, 16); } if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { min_len += sizeof(ovs_be32); /* dst_field */ min_len += sizeof(ovs_be16); /* dst_ofs */ } return min_len; } /* Converts 'nal' into a "struct ofpact_learn" and appends that struct to * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ static enum ofperr decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; const void *p, *end; if (nal->pad) { return OFPERR_OFPBAC_BAD_ARGUMENT; } learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = ntohs(nal->idle_timeout); learn->hard_timeout = ntohs(nal->hard_timeout); learn->priority = ntohs(nal->priority); learn->cookie = nal->cookie; learn->table_id = nal->table_id; learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout); learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout); learn->flags = ntohs(nal->flags); if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM | NX_LEARN_F_DELETE_LEARNED)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } if (learn->table_id == 0xff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } end = (char *) nal + ntohs(nal->len); for (p = nal + 1; p != end; ) { struct ofpact_learn_spec *spec; uint16_t header = ntohs(get_be16(&p)); if (!header) { break; } spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->header; learn->n_specs++; spec->src_type = header & NX_LEARN_SRC_MASK; spec->dst_type = header & NX_LEARN_DST_MASK; spec->n_bits = header & NX_LEARN_N_BITS_MASK; /* Check for valid src and dst type combination. */ if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD || (spec->dst_type == NX_LEARN_DST_OUTPUT && spec->src_type == NX_LEARN_SRC_FIELD)) { /* OK. */ } else { return OFPERR_OFPBAC_BAD_ARGUMENT; } /* Check that the arguments don't overrun the end of the action. */ if ((char *) end - (char *) p < learn_min_len(header)) { return OFPERR_OFPBAC_BAD_LEN; } /* Get the source. */ if (spec->src_type == NX_LEARN_SRC_FIELD) { get_subfield(spec->n_bits, &p, &spec->src); } else { int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); bitwise_copy(p, p_bytes, 0, &spec->src_imm, sizeof spec->src_imm, 0, spec->n_bits); p = (const uint8_t *) p + p_bytes; } /* Get the destination. */ if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD) { get_subfield(spec->n_bits, &p, &spec->dst); } } ofpact_update_len(ofpacts, &learn->ofpact); if (!is_all_zeros(p, (char *) end - (char *) p)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } return 0; } static void put_be16(struct ofpbuf *b, ovs_be16 x) { ofpbuf_put(b, &x, sizeof x); } static void put_be32(struct ofpbuf *b, ovs_be32 x) { ofpbuf_put(b, &x, sizeof x); } static void put_u16(struct ofpbuf *b, uint16_t x) { put_be16(b, htons(x)); } static void put_u32(struct ofpbuf *b, uint32_t x) { put_be32(b, htonl(x)); } static void encode_LEARN(const struct ofpact_learn *learn, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { const struct ofpact_learn_spec *spec; struct nx_action_learn *nal; size_t start_ofs; start_ofs = out->size; nal = put_NXAST_LEARN(out); nal->idle_timeout = htons(learn->idle_timeout); nal->hard_timeout = htons(learn->hard_timeout); nal->fin_idle_timeout = htons(learn->fin_idle_timeout); nal->fin_hard_timeout = htons(learn->fin_hard_timeout); nal->priority = htons(learn->priority); nal->cookie = learn->cookie; nal->flags = htons(learn->flags); nal->table_id = learn->table_id; for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { put_u16(out, spec->n_bits | spec->dst_type | spec->src_type); if (spec->src_type == NX_LEARN_SRC_FIELD) { put_u32(out, mf_nxm_header(spec->src.field->id)); put_u16(out, spec->src.ofs); } else { size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); uint8_t *bits = ofpbuf_put_zeros(out, n_dst_bytes); bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, bits, n_dst_bytes, 0, spec->n_bits); } if (spec->dst_type == NX_LEARN_DST_MATCH || spec->dst_type == NX_LEARN_DST_LOAD) { put_u32(out, mf_nxm_header(spec->dst.field->id)); put_u16(out, spec->dst.ofs); } } pad_ofpat(out, start_ofs); } static char * OVS_WARN_UNUSED_RESULT parse_LEARN(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return learn_parse(arg, ofpacts); } static void format_LEARN(const struct ofpact_learn *a, struct ds *s) { learn_format(a, s); } /* Action structure for NXAST_CONJUNCTION. */ struct nx_action_conjunction { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* See enum ofp_raw_action_type. */ uint8_t clause; uint8_t n_clauses; ovs_be32 id; }; OFP_ASSERT(sizeof(struct nx_action_conjunction) == 16); static void add_conjunction(struct ofpbuf *out, uint32_t id, uint8_t clause, uint8_t n_clauses) { struct ofpact_conjunction *oc; oc = ofpact_put_CONJUNCTION(out); oc->id = id; oc->clause = clause; oc->n_clauses = n_clauses; } static enum ofperr decode_NXAST_RAW_CONJUNCTION(const struct nx_action_conjunction *nac, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { if (nac->n_clauses < 2 || nac->n_clauses > 64 || nac->clause >= nac->n_clauses) { return OFPERR_NXBAC_BAD_CONJUNCTION; } else { add_conjunction(out, ntohl(nac->id), nac->clause, nac->n_clauses); return 0; } } static void encode_CONJUNCTION(const struct ofpact_conjunction *oc, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct nx_action_conjunction *nac = put_NXAST_CONJUNCTION(out); nac->clause = oc->clause; nac->n_clauses = oc->n_clauses; nac->id = htonl(oc->id); } static void format_CONJUNCTION(const struct ofpact_conjunction *oc, struct ds *s) { ds_put_format(s, "conjunction(%"PRIu32",%"PRIu8"/%"PRIu8")", oc->id, oc->clause + 1, oc->n_clauses); } static char * OVS_WARN_UNUSED_RESULT parse_CONJUNCTION(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { uint8_t n_clauses; uint8_t clause; uint32_t id; int n; if (!ovs_scan(arg, "%"SCNi32" , %"SCNu8" / %"SCNu8" %n", &id, &clause, &n_clauses, &n) || n != strlen(arg)) { return xstrdup("\"conjunction\" syntax is \"conjunction(id,i/n)\""); } if (n_clauses < 2) { return xstrdup("conjunction must have at least 2 clauses"); } else if (n_clauses > 64) { return xstrdup("conjunction must have at most 64 clauses"); } else if (clause < 1) { return xstrdup("clause index must be positive"); } else if (clause > n_clauses) { return xstrdup("clause index must be less than or equal to " "number of clauses"); } add_conjunction(ofpacts, id, clause - 1, n_clauses); return NULL; } /* Action structure for NXAST_MULTIPATH. * * This action performs the following steps in sequence: * * 1. Hashes the fields designated by 'fields', one of NX_HASH_FIELDS_*. * Refer to the definition of "enum nx_mp_fields" for details. * * The 'basis' value is used as a universal hash parameter, that is, * different values of 'basis' yield different hash functions. The * particular universal hash function used is implementation-defined. * * The hashed fields' values are drawn from the current state of the * flow, including all modifications that have been made by actions up to * this point. * * 2. Applies the multipath link choice algorithm specified by 'algorithm', * one of NX_MP_ALG_*. Refer to the definition of "enum nx_mp_algorithm" * for details. * * The output of the algorithm is 'link', an unsigned integer less than * or equal to 'max_link'. * * Some algorithms use 'arg' as an additional argument. * * 3. Stores 'link' in dst[ofs:ofs+n_bits]. The format and semantics of * 'dst' and 'ofs_nbits' are similar to those for the NXAST_REG_LOAD * action. * * The switch will reject actions that have an unknown 'fields', or an unknown * 'algorithm', or in which ofs+n_bits is greater than the width of 'dst', or * in which 'max_link' is greater than or equal to 2**n_bits, with error type * OFPET_BAD_ACTION, code OFPBAC_BAD_ARGUMENT. */ struct nx_action_multipath { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 32. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_MULTIPATH. */ /* What fields to hash and how. */ ovs_be16 fields; /* One of NX_HASH_FIELDS_*. */ ovs_be16 basis; /* Universal hash parameter. */ ovs_be16 pad0; /* Multipath link choice algorithm to apply to hash value. */ ovs_be16 algorithm; /* One of NX_MP_ALG_*. */ ovs_be16 max_link; /* Number of output links, minus 1. */ ovs_be32 arg; /* Algorithm-specific argument. */ ovs_be16 pad1; /* Where to store the result. */ ovs_be16 ofs_nbits; /* (ofs << 6) | (n_bits - 1). */ ovs_be32 dst; /* Destination. */ }; OFP_ASSERT(sizeof(struct nx_action_multipath) == 32); static enum ofperr decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { uint32_t n_links = ntohs(nam->max_link) + 1; size_t min_n_bits = log_2_ceil(n_links); struct ofpact_multipath *mp; mp = ofpact_put_MULTIPATH(out); mp->fields = ntohs(nam->fields); mp->basis = ntohs(nam->basis); mp->algorithm = ntohs(nam->algorithm); mp->max_link = ntohs(nam->max_link); mp->arg = ntohl(nam->arg); mp->dst.field = mf_from_nxm_header(ntohl(nam->dst)); mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits); mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits); if (!flow_hash_fields_valid(mp->fields)) { VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields); return OFPERR_OFPBAC_BAD_ARGUMENT; } else if (mp->algorithm != NX_MP_ALG_MODULO_N && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD && mp->algorithm != NX_MP_ALG_HRW && mp->algorithm != NX_MP_ALG_ITER_HASH) { VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm); return OFPERR_OFPBAC_BAD_ARGUMENT; } else if (mp->dst.n_bits < min_n_bits) { VLOG_WARN_RL(&rl, "multipath action requires at least %"PRIuSIZE" bits for " "%"PRIu32" links", min_n_bits, n_links); return OFPERR_OFPBAC_BAD_ARGUMENT; } return multipath_check(mp, NULL); } static void encode_MULTIPATH(const struct ofpact_multipath *mp, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct nx_action_multipath *nam = put_NXAST_MULTIPATH(out); nam->fields = htons(mp->fields); nam->basis = htons(mp->basis); nam->algorithm = htons(mp->algorithm); nam->max_link = htons(mp->max_link); nam->arg = htonl(mp->arg); nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits); nam->dst = htonl(mf_nxm_header(mp->dst.field->id)); } static char * OVS_WARN_UNUSED_RESULT parse_MULTIPATH(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { return multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); } static void format_MULTIPATH(const struct ofpact_multipath *a, struct ds *s) { multipath_format(a, s); } /* Action structure for NXAST_NOTE. * * This action has no effect. It is variable length. The switch does not * attempt to interpret the user-defined 'note' data in any way. A controller * can use this action to attach arbitrary metadata to a flow. * * This action might go away in the future. */ struct nx_action_note { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* A multiple of 8, but at least 16. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_NOTE. */ uint8_t note[6]; /* Start of user-defined data. */ /* Possibly followed by additional user-defined data. */ }; OFP_ASSERT(sizeof(struct nx_action_note) == 16); static enum ofperr decode_NXAST_RAW_NOTE(const struct nx_action_note *nan, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_note *note; unsigned int length; length = ntohs(nan->len) - offsetof(struct nx_action_note, note); note = ofpact_put(out, OFPACT_NOTE, offsetof(struct ofpact_note, data) + length); note->length = length; memcpy(note->data, nan->note, length); return 0; } static void encode_NOTE(const struct ofpact_note *note, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { size_t start_ofs = out->size; struct nx_action_note *nan; unsigned int remainder; unsigned int len; put_NXAST_NOTE(out); out->size = out->size - sizeof nan->note; ofpbuf_put(out, note->data, note->length); len = out->size - start_ofs; remainder = len % OFP_ACTION_ALIGN; if (remainder) { ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder); } nan = ofpbuf_at(out, start_ofs, sizeof *nan); nan->len = htons(out->size - start_ofs); } static char * OVS_WARN_UNUSED_RESULT parse_NOTE(const char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_note *note; note = ofpact_put_NOTE(ofpacts); while (*arg != '\0') { uint8_t byte; bool ok; if (*arg == '.') { arg++; } if (*arg == '\0') { break; } byte = hexits_value(arg, 2, &ok); if (!ok) { return xstrdup("bad hex digit in `note' argument"); } ofpbuf_put(ofpacts, &byte, 1); note = ofpacts->header; note->length++; arg += 2; } ofpact_update_len(ofpacts, ¬e->ofpact); return NULL; } static void format_NOTE(const struct ofpact_note *a, struct ds *s) { size_t i; ds_put_cstr(s, "note:"); for (i = 0; i < a->length; i++) { if (i) { ds_put_char(s, '.'); } ds_put_format(s, "%02"PRIx8, a->data[i]); } } /* Exit action. */ static enum ofperr decode_NXAST_RAW_EXIT(struct ofpbuf *out) { ofpact_put_EXIT(out); return 0; } static void encode_EXIT(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { put_NXAST_EXIT(out); } static char * OVS_WARN_UNUSED_RESULT parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_EXIT(ofpacts); return NULL; } static void format_EXIT(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "exit"); } /* Unroll xlate action. */ static void encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out OVS_UNUSED) { OVS_NOT_REACHED(); } static char * OVS_WARN_UNUSED_RESULT parse_UNROLL_XLATE(char *arg OVS_UNUSED, struct ofpbuf *ofpacts OVS_UNUSED, enum ofputil_protocol *usable_protocols OVS_UNUSED) { OVS_NOT_REACHED(); return NULL; } static void format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "unroll_xlate"); } /* Action structure for NXAST_SAMPLE. * * Samples matching packets with the given probability and sends them * each to the set of collectors identified with the given ID. The * probability is expressed as a number of packets to be sampled out * of USHRT_MAX packets, and must be >0. * * When sending packet samples to IPFIX collectors, the IPFIX flow * record sent for each sampled packet is associated with the given * observation domain ID and observation point ID. Each IPFIX flow * record contain the sampled packet's headers when executing this * rule. If a sampled packet's headers are modified by previous * actions in the flow, those modified headers are sent. */ struct nx_action_sample { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_SAMPLE. */ ovs_be16 probability; /* Fraction of packets to sample. */ ovs_be32 collector_set_id; /* ID of collector set in OVSDB. */ ovs_be32 obs_domain_id; /* ID of sampling observation domain. */ ovs_be32 obs_point_id; /* ID of sampling observation point. */ }; OFP_ASSERT(sizeof(struct nx_action_sample) == 24); static enum ofperr decode_NXAST_RAW_SAMPLE(const struct nx_action_sample *nas, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_sample *sample; sample = ofpact_put_SAMPLE(out); sample->probability = ntohs(nas->probability); sample->collector_set_id = ntohl(nas->collector_set_id); sample->obs_domain_id = ntohl(nas->obs_domain_id); sample->obs_point_id = ntohl(nas->obs_point_id); if (sample->probability == 0) { return OFPERR_OFPBAC_BAD_ARGUMENT; } return 0; } static void encode_SAMPLE(const struct ofpact_sample *sample, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct nx_action_sample *nas; nas = put_NXAST_SAMPLE(out); nas->probability = htons(sample->probability); nas->collector_set_id = htonl(sample->collector_set_id); nas->obs_domain_id = htonl(sample->obs_domain_id); nas->obs_point_id = htonl(sample->obs_point_id); } /* Parses 'arg' as the argument to a "sample" action, and appends such an * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT parse_SAMPLE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts); char *key, *value; while (ofputil_parse_key_value(&arg, &key, &value)) { char *error = NULL; if (!strcmp(key, "probability")) { error = str_to_u16(value, "probability", &os->probability); if (!error && os->probability == 0) { error = xasprintf("invalid probability value \"%s\"", value); } } else if (!strcmp(key, "collector_set_id")) { error = str_to_u32(value, &os->collector_set_id); } else if (!strcmp(key, "obs_domain_id")) { error = str_to_u32(value, &os->obs_domain_id); } else if (!strcmp(key, "obs_point_id")) { error = str_to_u32(value, &os->obs_point_id); } else { error = xasprintf("invalid key \"%s\" in \"sample\" argument", key); } if (error) { return error; } } if (os->probability == 0) { return xstrdup("non-zero \"probability\" must be specified on sample"); } return NULL; } static void format_SAMPLE(const struct ofpact_sample *a, struct ds *s) { ds_put_format(s, "sample(probability=%"PRIu16",collector_set_id=%"PRIu32 ",obs_domain_id=%"PRIu32",obs_point_id=%"PRIu32")", a->probability, a->collector_set_id, a->obs_domain_id, a->obs_point_id); } /* debug_recirc instruction. */ static bool enable_debug; void ofpact_dummy_enable(void) { enable_debug = true; } static enum ofperr decode_NXAST_RAW_DEBUG_RECIRC(struct ofpbuf *out) { if (!enable_debug) { return OFPERR_OFPBAC_BAD_VENDOR_TYPE; } ofpact_put_DEBUG_RECIRC(out); return 0; } static void encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { put_NXAST_DEBUG_RECIRC(out); } static char * OVS_WARN_UNUSED_RESULT parse_DEBUG_RECIRC(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_DEBUG_RECIRC(ofpacts); return NULL; } static void format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "debug_recirc"); } /* Action structure for NXAST_CT. * * Pass traffic to the connection tracker. * * There are two important concepts to understanding the connection tracking * interface: Packet state and Connection state. Packets may be "Untracked" or * "Tracked". Connections may be "Uncommitted" or "Committed". * * - Packet State: * * Untracked packets have not yet passed through the connection tracker, * and the connection state for such packets is unknown. In most cases, * packets entering the OpenFlow pipeline will initially be in the * untracked state. Untracked packets may become tracked by executing * NXAST_CT with a "recirc_table" specified. This makes various aspects * about the connection available, in particular the connection state. * * Tracked packets have previously passed through the connection tracker. * These packets will remain tracked through until the end of the OpenFlow * pipeline. Tracked packets which have NXAST_CT executed with a * "recirc_table" specified will return to the tracked state. * * The packet state is only significant for the duration of packet * processing within the OpenFlow pipeline. * * - Connection State: * * Multiple packets may be associated with a single connection. Initially, * all connections are uncommitted. The connection state corresponding to * a packet is available in the NXM_NX_CT_STATE field for tracked packets. * * Uncommitted connections have no state stored about them. Uncommitted * connections may transition into the committed state by executing * NXAST_CT with the NX_CT_F_COMMIT flag. * * Once a connection becomes committed, information may be gathered about * the connection by passing subsequent packets through the connection * tracker, and the state of the connection will be stored beyond the * lifetime of packet processing. * * Connections may transition back into the uncommitted state due to * external timers, or due to the contents of packets that are sent to the * connection tracker. This behaviour is outside of the scope of the * OpenFlow interface. * * The "zone" specifies a context within which the tracking is done: * * The connection tracking zone is a 16-bit number. Each zone is an * independent connection tracking context. The connection state for each * connection is completely separate for each zone, so if a connection * is committed to zone A, then it will remain uncommitted in zone B. * If NXAST_CT is executed with the same zone multiple times, later * executions have no effect. * * If 'zone_src' is nonzero, this specifies that the zone should be * sourced from a field zone_src[ofs:ofs+nbits]. The format and semantics * of 'zone_src' and 'zone_ofs_nbits' are similar to those for the * NXAST_REG_LOAD action. The acceptable nxm_header values for 'zone_src' * are the same as the acceptable nxm_header values for the 'src' field of * NXAST_REG_MOVE. * * If 'zone_src' is zero, then the value of 'zone_imm' will be used as the * connection tracking zone. * * The "recirc_table" allows NXM_NX_CT_* fields to become available: * * If "recirc_table" has a value other than NX_CT_RECIRC_NONE, then the * packet will be logically cloned prior to executing this action. One * copy will be sent to the connection tracker, then will be re-injected * into the OpenFlow pipeline beginning at the OpenFlow table specified in * this field. When the packet re-enters the pipeline, the NXM_NX_CT_* * fields will be populated. The original instance of the packet will * continue the current actions list. This can be thought of as similar to * the effect of the "output" action: One copy is sent out (in this case, * to the connection tracker), but the current copy continues processing. * * It is strongly recommended that this table is later than the current * table, to prevent loops. * * The "alg" attaches protocol-specific behaviour to this action: * * The ALG is a 16-bit number which specifies that additional * processing should be applied to this traffic. * * Protocol | Value | Meaning * -------------------------------------------------------------------- * None | 0 | No protocol-specific behaviour. * FTP | 21 | Parse FTP control connections and observe the * | | negotiation of related data connections. * Other | Other | Unsupported protocols. * * By way of example, if FTP control connections have this action applied * with the ALG set to FTP (21), then the connection tracker will observe * the negotiation of data connections. This allows the connection * tracker to identify subsequent data connections as "related" to this * existing connection. The "related" flag will be populated in the * NXM_NX_CT_STATE field for such connections if the 'recirc_table' is * specified. * * Zero or more actions may immediately follow this action. These actions will * be executed within the context of the connection tracker, and they require * the NX_CT_F_COMMIT flag to be set. */ struct nx_action_conntrack { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 24. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_CT. */ ovs_be16 flags; /* Zero or more NX_CT_F_* flags. * Unspecified flag bits must be zero. */ ovs_be32 zone_src; /* Connection tracking context. */ union { ovs_be16 zone_ofs_nbits;/* Range to use from source field. */ ovs_be16 zone_imm; /* Immediate value for zone. */ }; uint8_t recirc_table; /* Recirculate to a specific table, or NX_CT_RECIRC_NONE for no recirculation. */ uint8_t pad[3]; /* Zeroes */ ovs_be16 alg; /* Well-known port number for the protocol. * 0 indicates no ALG is required. */ /* Followed by a sequence of zero or more OpenFlow actions. The length of * these is included in 'len'. */ }; OFP_ASSERT(sizeof(struct nx_action_conntrack) == 24); static enum ofperr decode_ct_zone(const struct nx_action_conntrack *nac, struct ofpact_conntrack *out) { if (nac->zone_src) { enum ofperr error; out->zone_src.field = mf_from_nxm_header(ntohl(nac->zone_src)); out->zone_src.ofs = nxm_decode_ofs(nac->zone_ofs_nbits); out->zone_src.n_bits = nxm_decode_n_bits(nac->zone_ofs_nbits); error = mf_check_src(&out->zone_src, NULL); if (error) { return error; } if (out->zone_src.n_bits != 16) { VLOG_WARN_RL(&rl, "zone n_bits %d not within valid range [16..16]", out->zone_src.n_bits); return OFPERR_OFPBAC_BAD_SET_LEN; } } else { out->zone_src.field = NULL; out->zone_imm = ntohs(nac->zone_imm); } return 0; } static enum ofperr decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac, enum ofp_version ofp_version, struct ofpbuf *out) { const size_t ct_offset = ofpacts_pull(out); struct ofpact_conntrack *conntrack; struct ofpbuf openflow; int error = 0; conntrack = ofpact_put_CT(out); conntrack->flags = ntohs(nac->flags); error = decode_ct_zone(nac, conntrack); if (error) { goto out; } conntrack->recirc_table = nac->recirc_table; conntrack->alg = ntohs(nac->alg); ofpbuf_pull(out, sizeof(*conntrack)); ofpbuf_use_const(&openflow, nac + 1, ntohs(nac->len) - sizeof(*nac)); error = ofpacts_pull_openflow_actions__(&openflow, openflow.size, ofp_version, 1u << OVSINST_OFPIT11_APPLY_ACTIONS, out, OFPACT_CT); if (error) { goto out; } conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack)); out->header = &conntrack->ofpact; ofpact_update_len(out, &conntrack->ofpact); if (conntrack->ofpact.len > sizeof(*conntrack) && !(conntrack->flags & NX_CT_F_COMMIT)) { VLOG_WARN_RL(&rl, "CT action requires commit flag if actions are " "specified."); error = OFPERR_OFPBAC_BAD_ARGUMENT; } out: ofpbuf_push_uninit(out, ct_offset); return error; } static void encode_CT(const struct ofpact_conntrack *conntrack, enum ofp_version ofp_version, struct ofpbuf *out) { struct nx_action_conntrack *nac; const size_t ofs = out->size; size_t len; nac = put_NXAST_CT(out); nac->flags = htons(conntrack->flags); if (conntrack->zone_src.field) { nac->zone_src = htonl(mf_nxm_header(conntrack->zone_src.field->id)); nac->zone_ofs_nbits = nxm_encode_ofs_nbits(conntrack->zone_src.ofs, conntrack->zone_src.n_bits); } else { nac->zone_src = htonl(0); nac->zone_imm = htons(conntrack->zone_imm); } nac->recirc_table = conntrack->recirc_table; nac->alg = htons(conntrack->alg); len = ofpacts_put_openflow_actions(conntrack->actions, ofpact_ct_get_action_len(conntrack), out, ofp_version); len += sizeof(*nac); nac = ofpbuf_at(out, ofs, sizeof(*nac)); nac->len = htons(len); } /* Parses 'arg' as the argument to a "ct" action, and appends such an * action to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT parse_CT(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { const size_t ct_offset = ofpacts_pull(ofpacts); struct ofpact_conntrack *oc; char *error = NULL; char *key, *value; oc = ofpact_put_CT(ofpacts); oc->flags = 0; oc->recirc_table = NX_CT_RECIRC_NONE; while (ofputil_parse_key_value(&arg, &key, &value)) { if (!strcmp(key, "commit")) { oc->flags |= NX_CT_F_COMMIT; } else if (!strcmp(key, "table")) { error = str_to_u8(value, "recirc_table", &oc->recirc_table); if (!error && oc->recirc_table == NX_CT_RECIRC_NONE) { error = xasprintf("invalid table %#"PRIx16, oc->recirc_table); } } else if (!strcmp(key, "zone")) { error = str_to_u16(value, "zone", &oc->zone_imm); if (error) { free(error); error = mf_parse_subfield(&oc->zone_src, value); if (error) { return error; } } } else if (!strcmp(key, "alg")) { error = str_to_connhelper(value, &oc->alg); } else if (!strcmp(key, "exec")) { /* Hide existing actions from ofpacts_parse_copy(), so the * nesting can be handled transparently. */ enum ofputil_protocol usable_protocols2; ofpbuf_pull(ofpacts, sizeof(*oc)); /* Initializes 'usable_protocol2', fold it back to * '*usable_protocols' afterwards, so that we do not lose * restrictions already in there. */ error = ofpacts_parse_copy(value, ofpacts, &usable_protocols2, false, OFPACT_CT); *usable_protocols &= usable_protocols2; ofpact_pad(ofpacts); ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof(*oc)); oc = ofpacts->header; } else { error = xasprintf("invalid argument to \"ct\" action: `%s'", key); } if (error) { break; } } ofpact_update_len(ofpacts, &oc->ofpact); ofpbuf_push_uninit(ofpacts, ct_offset); return error; } static void format_alg(int port, struct ds *s) { if (port == IPPORT_FTP) { ds_put_format(s, "alg=ftp,"); } else if (port) { ds_put_format(s, "alg=%d,", port); } } static void format_CT(const struct ofpact_conntrack *a, struct ds *s) { ds_put_cstr(s, "ct("); if (a->flags & NX_CT_F_COMMIT) { ds_put_cstr(s, "commit,"); } if (a->recirc_table != NX_CT_RECIRC_NONE) { ds_put_format(s, "table=%"PRIu8",", a->recirc_table); } if (a->zone_src.field) { ds_put_format(s, "zone="); mf_format_subfield(&a->zone_src, s); ds_put_char(s, ','); } else if (a->zone_imm) { ds_put_format(s, "zone=%"PRIu16",", a->zone_imm); } if (ofpact_ct_get_action_len(a)) { ds_put_cstr(s, "exec("); ofpacts_format(a->actions, ofpact_ct_get_action_len(a), s); ds_put_format(s, "),"); } format_alg(a->alg, s); ds_chomp(s, ','); ds_put_char(s, ')'); } /* Meter instruction. */ static void encode_METER(const struct ofpact_meter *meter, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version >= OFP13_VERSION) { instruction_put_OFPIT13_METER(out)->meter_id = htonl(meter->meter_id); } } static char * OVS_WARN_UNUSED_RESULT parse_METER(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { *usable_protocols &= OFPUTIL_P_OF13_UP; return str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id); } static void format_METER(const struct ofpact_meter *a, struct ds *s) { ds_put_format(s, "meter:%"PRIu32, a->meter_id); } /* Clear-Actions instruction. */ static void encode_CLEAR_ACTIONS(const struct ofpact_null *null OVS_UNUSED, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out OVS_UNUSED) { if (ofp_version > OFP10_VERSION) { instruction_put_OFPIT11_CLEAR_ACTIONS(out); } } static char * OVS_WARN_UNUSED_RESULT parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { ofpact_put_CLEAR_ACTIONS(ofpacts); return NULL; } static void format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED, struct ds *s) { ds_put_cstr(s, "clear_actions"); } /* Write-Actions instruction. */ static void encode_WRITE_ACTIONS(const struct ofpact_nest *actions, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version > OFP10_VERSION) { const size_t ofs = out->size; instruction_put_OFPIT11_WRITE_ACTIONS(out); ofpacts_put_openflow_actions(actions->actions, ofpact_nest_get_action_len(actions), out, ofp_version); ofpacts_update_instruction_actions(out, ofs); } } static char * OVS_WARN_UNUSED_RESULT parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { size_t ofs = ofpacts_pull(ofpacts); struct ofpact_nest *on; char *error; /* Add a Write-Actions instruction and then pull it off. */ ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on); ofpbuf_pull(ofpacts, sizeof *on); /* Parse nested actions. * * We pulled off "write-actions" and the previous actions because the * OFPACT_WRITE_ACTIONS is only partially constructed: its length is such * that it doesn't actually include the nested actions. That means that * ofpacts_parse() would reject them as being part of an Apply-Actions that * follows a Write-Actions, which is an invalid order. */ error = ofpacts_parse(arg, ofpacts, usable_protocols, false, OFPACT_WRITE_ACTIONS); /* Put the Write-Actions back on and update its length. */ on = ofpbuf_push_uninit(ofpacts, sizeof *on); on->ofpact.len = ofpacts->size; /* Put any previous actions or instructions back on. */ ofpbuf_push_uninit(ofpacts, ofs); return error; } static void format_WRITE_ACTIONS(const struct ofpact_nest *a, struct ds *s) { ds_put_cstr(s, "write_actions("); ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s); ds_put_char(s, ')'); } /* Action structure for NXAST_WRITE_METADATA. * * Modifies the 'mask' bits of the metadata value. */ struct nx_action_write_metadata { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* Length is 32. */ ovs_be32 vendor; /* NX_VENDOR_ID. */ ovs_be16 subtype; /* NXAST_WRITE_METADATA. */ uint8_t zeros[6]; /* Must be zero. */ ovs_be64 metadata; /* Metadata register. */ ovs_be64 mask; /* Metadata mask. */ }; OFP_ASSERT(sizeof(struct nx_action_write_metadata) == 32); static enum ofperr decode_NXAST_RAW_WRITE_METADATA(const struct nx_action_write_metadata *nawm, enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) { struct ofpact_metadata *om; if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) { return OFPERR_NXBRC_MUST_BE_ZERO; } om = ofpact_put_WRITE_METADATA(out); om->metadata = nawm->metadata; om->mask = nawm->mask; return 0; } static void encode_WRITE_METADATA(const struct ofpact_metadata *metadata, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { struct nx_action_write_metadata *nawm; nawm = put_NXAST_WRITE_METADATA(out); nawm->metadata = metadata->metadata; nawm->mask = metadata->mask; } else { struct ofp11_instruction_write_metadata *oiwm; oiwm = instruction_put_OFPIT11_WRITE_METADATA(out); oiwm->metadata = metadata->metadata; oiwm->metadata_mask = metadata->mask; } } static char * OVS_WARN_UNUSED_RESULT parse_WRITE_METADATA(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { struct ofpact_metadata *om; char *mask = strchr(arg, '/'); *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; om = ofpact_put_WRITE_METADATA(ofpacts); if (mask) { char *error; *mask = '\0'; error = str_to_be64(mask + 1, &om->mask); if (error) { return error; } } else { om->mask = OVS_BE64_MAX; } return str_to_be64(arg, &om->metadata); } static void format_WRITE_METADATA(const struct ofpact_metadata *a, struct ds *s) { ds_put_format(s, "write_metadata:%#"PRIx64, ntohll(a->metadata)); if (a->mask != OVS_BE64_MAX) { ds_put_format(s, "/%#"PRIx64, ntohll(a->mask)); } } /* Goto-Table instruction. */ static void encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table, enum ofp_version ofp_version, struct ofpbuf *out) { if (ofp_version == OFP10_VERSION) { struct nx_action_resubmit *nar; nar = put_NXAST_RESUBMIT_TABLE(out); nar->table = goto_table->table_id; nar->in_port = htons(ofp_to_u16(OFPP_IN_PORT)); } else { struct ofp11_instruction_goto_table *oigt; oigt = instruction_put_OFPIT11_GOTO_TABLE(out); oigt->table_id = goto_table->table_id; memset(oigt->pad, 0, sizeof oigt->pad); } } static char * OVS_WARN_UNUSED_RESULT parse_GOTO_TABLE(char *arg, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols OVS_UNUSED) { struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts); char *table_s = strsep(&arg, ","); if (!table_s || !table_s[0]) { return xstrdup("instruction goto-table needs table id"); } return str_to_u8(table_s, "table", &ogt->table_id); } static void format_GOTO_TABLE(const struct ofpact_goto_table *a, struct ds *s) { ds_put_format(s, "goto_table:%"PRIu8, a->table_id); } static void log_bad_action(const struct ofp_action_header *actions, size_t actions_len, const struct ofp_action_header *bad_action, enum ofperr error) { if (!VLOG_DROP_WARN(&rl)) { struct ds s; ds_init(&s); ds_put_hex_dump(&s, actions, actions_len, 0, false); VLOG_WARN("bad action at offset %#"PRIxPTR" (%s):\n%s", (char *)bad_action - (char *)actions, ofperr_get_name(error), ds_cstr(&s)); ds_destroy(&s); } } static enum ofperr ofpacts_decode(const void *actions, size_t actions_len, enum ofp_version ofp_version, struct ofpbuf *ofpacts) { struct ofpbuf openflow; ofpbuf_use_const(&openflow, actions, actions_len); while (openflow.size) { const struct ofp_action_header *action = openflow.data; enum ofp_raw_action_type raw; enum ofperr error; uint64_t arg; error = ofpact_pull_raw(&openflow, ofp_version, &raw, &arg); if (!error) { error = ofpact_decode(action, raw, ofp_version, arg, ofpacts); } if (error) { log_bad_action(actions, actions_len, action, error); return error; } } ofpact_pad(ofpacts); return 0; } static enum ofperr ofpacts_pull_openflow_actions__(struct ofpbuf *openflow, unsigned int actions_len, enum ofp_version version, uint32_t allowed_ovsinsts, struct ofpbuf *ofpacts, enum ofpact_type outer_action) { const struct ofp_action_header *actions; enum ofperr error; if (!outer_action) { ofpbuf_clear(ofpacts); } if (actions_len % OFP_ACTION_ALIGN != 0) { VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a " "multiple of %d", actions_len, OFP_ACTION_ALIGN); return OFPERR_OFPBRC_BAD_LEN; } actions = ofpbuf_try_pull(openflow, actions_len); if (actions == NULL) { VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " "remaining message length (%"PRIu32")", actions_len, openflow->size); return OFPERR_OFPBRC_BAD_LEN; } error = ofpacts_decode(actions, actions_len, version, ofpacts); if (error) { ofpbuf_clear(ofpacts); return error; } error = ofpacts_verify(ofpacts->data, ofpacts->size, allowed_ovsinsts, outer_action); if (error) { ofpbuf_clear(ofpacts); } return error; } /* Attempts to convert 'actions_len' bytes of OpenFlow actions from the * front of 'openflow' into ofpacts. On success, replaces any existing content * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'. * Returns 0 if successful, otherwise an OpenFlow error. * * Actions are processed according to their OpenFlow version which * is provided in the 'version' parameter. * * In most places in OpenFlow, actions appear encapsulated in instructions, so * you should call ofpacts_pull_openflow_instructions() instead of this * function. * * The parsed actions are valid generically, but they may not be valid in a * specific context. For example, port numbers up to OFPP_MAX are valid * generically, but specific datapaths may only support port numbers in a * smaller range. Use ofpacts_check() to additional check whether actions are * valid in a specific context. */ enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow, unsigned int actions_len, enum ofp_version version, struct ofpbuf *ofpacts) { return ofpacts_pull_openflow_actions__(openflow, actions_len, version, 1u << OVSINST_OFPIT11_APPLY_ACTIONS, ofpacts, 0); } /* OpenFlow 1.1 actions. */ /* True if an action sets the value of a field * in a way that is compatibile with the action set. * The field can be set via either a set or a move action. * False otherwise. */ static bool ofpact_is_set_or_move_action(const struct ofpact *a) { switch (a->type) { case OFPACT_SET_FIELD: case OFPACT_REG_MOVE: case OFPACT_SET_ETH_DST: case OFPACT_SET_ETH_SRC: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_L4_DST_PORT: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_SET_QUEUE: case OFPACT_SET_TUNNEL: case OFPACT_SET_VLAN_PCP: case OFPACT_SET_VLAN_VID: return true; case OFPACT_BUNDLE: case OFPACT_CLEAR_ACTIONS: case OFPACT_CT: case OFPACT_CONTROLLER: case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: case OFPACT_ENQUEUE: case OFPACT_EXIT: case OFPACT_UNROLL_XLATE: case OFPACT_FIN_TIMEOUT: case OFPACT_GOTO_TABLE: case OFPACT_GROUP: case OFPACT_LEARN: case OFPACT_CONJUNCTION: case OFPACT_METER: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_OUTPUT: case OFPACT_OUTPUT_REG: case OFPACT_POP_MPLS: case OFPACT_POP_QUEUE: case OFPACT_PUSH_MPLS: case OFPACT_PUSH_VLAN: case OFPACT_RESUBMIT: case OFPACT_SAMPLE: case OFPACT_STACK_POP: case OFPACT_STACK_PUSH: case OFPACT_STRIP_VLAN: case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_METADATA: case OFPACT_DEBUG_RECIRC: return false; default: OVS_NOT_REACHED(); } } /* True if an action is allowed in the action set. * False otherwise. */ static bool ofpact_is_allowed_in_actions_set(const struct ofpact *a) { switch (a->type) { case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: case OFPACT_GROUP: case OFPACT_OUTPUT: case OFPACT_POP_MPLS: case OFPACT_PUSH_MPLS: case OFPACT_PUSH_VLAN: case OFPACT_REG_MOVE: case OFPACT_SET_FIELD: case OFPACT_SET_ETH_DST: case OFPACT_SET_ETH_SRC: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_L4_DST_PORT: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_SET_QUEUE: case OFPACT_SET_TUNNEL: case OFPACT_SET_VLAN_PCP: case OFPACT_SET_VLAN_VID: case OFPACT_STRIP_VLAN: return true; /* In general these actions are excluded because they are not part of * the OpenFlow specification nor map to actions that are defined in * the specification. Thus the order in which they should be applied * in the action set is undefined. */ case OFPACT_BUNDLE: case OFPACT_CONTROLLER: case OFPACT_CT: case OFPACT_ENQUEUE: case OFPACT_EXIT: case OFPACT_UNROLL_XLATE: case OFPACT_FIN_TIMEOUT: case OFPACT_LEARN: case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_OUTPUT_REG: case OFPACT_POP_QUEUE: case OFPACT_RESUBMIT: case OFPACT_SAMPLE: case OFPACT_STACK_POP: case OFPACT_STACK_PUSH: case OFPACT_DEBUG_RECIRC: /* The action set may only include actions and thus * may not include any instructions */ case OFPACT_CLEAR_ACTIONS: case OFPACT_GOTO_TABLE: case OFPACT_METER: case OFPACT_WRITE_ACTIONS: case OFPACT_WRITE_METADATA: return false; default: OVS_NOT_REACHED(); } } /* Append ofpact 'a' onto the tail of 'out' */ static void ofpact_copy(struct ofpbuf *out, const struct ofpact *a) { ofpbuf_put(out, a, OFPACT_ALIGN(a->len)); } /* Copies the last ofpact whose type is 'filter' from 'in' to 'out'. */ static bool ofpacts_copy_last(struct ofpbuf *out, const struct ofpbuf *in, enum ofpact_type filter) { const struct ofpact *target; const struct ofpact *a; target = NULL; OFPACT_FOR_EACH (a, in->data, in->size) { if (a->type == filter) { target = a; } } if (target) { ofpact_copy(out, target); } return target != NULL; } /* Append all ofpacts, for which 'filter' returns true, from 'in' to 'out'. * The order of appended ofpacts is preserved between 'in' and 'out' */ static void ofpacts_copy_all(struct ofpbuf *out, const struct ofpbuf *in, bool (*filter)(const struct ofpact *)) { const struct ofpact *a; OFPACT_FOR_EACH (a, in->data, in->size) { if (filter(a)) { ofpact_copy(out, a); } } } /* Reads 'action_set', which contains ofpacts accumulated by * OFPACT_WRITE_ACTIONS instructions, and writes equivalent actions to be * executed directly into 'action_list'. (These names correspond to the * "Action Set" and "Action List" terms used in OpenFlow 1.1+.) * * In general this involves appending the last instance of each action that is * admissible in the action set in the order described in the OpenFlow * specification. * * Exceptions: * + output action is only appended if no group action was present in 'in'. * + As a simplification all set actions are copied in the order the are * provided in 'in' as many set actions applied to a field has the same * affect as only applying the last action that sets a field and * duplicates are removed by do_xlate_actions(). * This has an unwanted side-effect of compsoting multiple * LOAD_REG actions that touch different regions of the same field. */ void ofpacts_execute_action_set(struct ofpbuf *action_list, const struct ofpbuf *action_set) { /* The OpenFlow spec "Action Set" section specifies this order. */ ofpacts_copy_last(action_list, action_set, OFPACT_STRIP_VLAN); ofpacts_copy_last(action_list, action_set, OFPACT_POP_MPLS); ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_MPLS); ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_VLAN); ofpacts_copy_last(action_list, action_set, OFPACT_DEC_TTL); ofpacts_copy_last(action_list, action_set, OFPACT_DEC_MPLS_TTL); ofpacts_copy_all(action_list, action_set, ofpact_is_set_or_move_action); ofpacts_copy_last(action_list, action_set, OFPACT_SET_QUEUE); /* If both OFPACT_GROUP and OFPACT_OUTPUT are present, OpenFlow says that * we should execute only OFPACT_GROUP. * * If neither OFPACT_GROUP nor OFPACT_OUTPUT is present, then we can drop * all the actions because there's no point in modifying a packet that will * not be sent anywhere. */ if (!ofpacts_copy_last(action_list, action_set, OFPACT_GROUP) && !ofpacts_copy_last(action_list, action_set, OFPACT_OUTPUT) && !ofpacts_copy_last(action_list, action_set, OFPACT_RESUBMIT)) { ofpbuf_clear(action_list); } } static enum ofperr ofpacts_decode_for_action_set(const struct ofp_action_header *in, size_t n_in, enum ofp_version version, struct ofpbuf *out) { enum ofperr error; struct ofpact *a; size_t start = out->size; error = ofpacts_decode(in, n_in, version, out); if (error) { return error; } OFPACT_FOR_EACH (a, ofpact_end(out->data, start), out->size - start) { if (!ofpact_is_allowed_in_actions_set(a)) { VLOG_WARN_RL(&rl, "disallowed action in action set"); return OFPERR_OFPBAC_BAD_TYPE; } } return 0; } /* OpenFlow 1.1 instructions. */ struct instruction_type_info { enum ovs_instruction_type type; const char *name; }; static const struct instruction_type_info inst_info[] = { #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) {OVSINST_##ENUM, NAME}, OVS_INSTRUCTIONS #undef DEFINE_INST }; const char * ovs_instruction_name_from_type(enum ovs_instruction_type type) { return type < ARRAY_SIZE(inst_info) ? inst_info[type].name : NULL; } int ovs_instruction_type_from_name(const char *name) { const struct instruction_type_info *p; for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) { if (!strcasecmp(name, p->name)) { return p->type; } } return -1; } enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(enum ofpact_type type) { switch (type) { case OFPACT_METER: return OVSINST_OFPIT13_METER; case OFPACT_CLEAR_ACTIONS: return OVSINST_OFPIT11_CLEAR_ACTIONS; case OFPACT_WRITE_ACTIONS: return OVSINST_OFPIT11_WRITE_ACTIONS; case OFPACT_WRITE_METADATA: return OVSINST_OFPIT11_WRITE_METADATA; case OFPACT_GOTO_TABLE: return OVSINST_OFPIT11_GOTO_TABLE; case OFPACT_OUTPUT: case OFPACT_GROUP: case OFPACT_CONTROLLER: case OFPACT_ENQUEUE: case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: case OFPACT_SET_FIELD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: case OFPACT_LEARN: case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_EXIT: case OFPACT_UNROLL_XLATE: case OFPACT_SAMPLE: case OFPACT_DEBUG_RECIRC: case OFPACT_CT: default: return OVSINST_OFPIT11_APPLY_ACTIONS; } } enum ofperr ovs_instruction_type_from_inst_type(enum ovs_instruction_type *instruction_type, const uint16_t inst_type) { switch (inst_type) { #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ case ENUM: \ *instruction_type = OVSINST_##ENUM; \ return 0; OVS_INSTRUCTIONS #undef DEFINE_INST default: return OFPERR_OFPBIC_UNKNOWN_INST; } } /* Two-way translation between OVS's internal "OVSINST_*" representation of * instructions and the "OFPIT_*" representation used in OpenFlow. */ struct ovsinst_map { enum ovs_instruction_type ovsinst; /* Internal name for instruction. */ int ofpit; /* OFPIT_* number from OpenFlow spec. */ }; static const struct ovsinst_map * get_ovsinst_map(enum ofp_version version) { /* OpenFlow 1.1 and 1.2 instructions. */ static const struct ovsinst_map of11[] = { { OVSINST_OFPIT11_GOTO_TABLE, 1 }, { OVSINST_OFPIT11_WRITE_METADATA, 2 }, { OVSINST_OFPIT11_WRITE_ACTIONS, 3 }, { OVSINST_OFPIT11_APPLY_ACTIONS, 4 }, { OVSINST_OFPIT11_CLEAR_ACTIONS, 5 }, { 0, -1 }, }; /* OpenFlow 1.3+ instructions. */ static const struct ovsinst_map of13[] = { { OVSINST_OFPIT11_GOTO_TABLE, 1 }, { OVSINST_OFPIT11_WRITE_METADATA, 2 }, { OVSINST_OFPIT11_WRITE_ACTIONS, 3 }, { OVSINST_OFPIT11_APPLY_ACTIONS, 4 }, { OVSINST_OFPIT11_CLEAR_ACTIONS, 5 }, { OVSINST_OFPIT13_METER, 6 }, { 0, -1 }, }; return version < OFP13_VERSION ? of11 : of13; } /* Converts 'ovsinst_bitmap', a bitmap whose bits correspond to OVSINST_* * values, into a bitmap of instructions suitable for OpenFlow 'version' * (OFP11_VERSION or later), and returns the result. */ ovs_be32 ovsinst_bitmap_to_openflow(uint32_t ovsinst_bitmap, enum ofp_version version) { uint32_t ofpit_bitmap = 0; const struct ovsinst_map *x; for (x = get_ovsinst_map(version); x->ofpit >= 0; x++) { if (ovsinst_bitmap & (1u << x->ovsinst)) { ofpit_bitmap |= 1u << x->ofpit; } } return htonl(ofpit_bitmap); } /* Converts 'ofpit_bitmap', a bitmap of instructions from an OpenFlow message * with the given 'version' (OFP11_VERSION or later) into a bitmap whose bits * correspond to OVSINST_* values, and returns the result. */ uint32_t ovsinst_bitmap_from_openflow(ovs_be32 ofpit_bitmap, enum ofp_version version) { uint32_t ovsinst_bitmap = 0; const struct ovsinst_map *x; for (x = get_ovsinst_map(version); x->ofpit >= 0; x++) { if (ofpit_bitmap & htonl(1u << x->ofpit)) { ovsinst_bitmap |= 1u << x->ovsinst; } } return ovsinst_bitmap; } static inline struct ofp11_instruction * instruction_next(const struct ofp11_instruction *inst) { return ((struct ofp11_instruction *) (void *) ((uint8_t *) inst + ntohs(inst->len))); } static inline bool instruction_is_valid(const struct ofp11_instruction *inst, size_t n_instructions) { uint16_t len = ntohs(inst->len); return (!(len % OFP11_INSTRUCTION_ALIGN) && len >= sizeof *inst && len / sizeof *inst <= n_instructions); } /* This macro is careful to check for instructions with bad lengths. */ #define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS) \ for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS); \ (LEFT) > 0 && instruction_is_valid(ITER, LEFT); \ ((LEFT) -= (ntohs((ITER)->len) \ / sizeof(struct ofp11_instruction)), \ (ITER) = instruction_next(ITER))) static enum ofperr decode_openflow11_instruction(const struct ofp11_instruction *inst, enum ovs_instruction_type *type) { uint16_t len = ntohs(inst->len); switch (inst->type) { case CONSTANT_HTONS(OFPIT11_EXPERIMENTER): return OFPERR_OFPBIC_BAD_EXPERIMENTER; #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ case CONSTANT_HTONS(ENUM): \ if (EXTENSIBLE \ ? len >= sizeof(struct STRUCT) \ : len == sizeof(struct STRUCT)) { \ *type = OVSINST_##ENUM; \ return 0; \ } else { \ return OFPERR_OFPBIC_BAD_LEN; \ } OVS_INSTRUCTIONS #undef DEFINE_INST default: return OFPERR_OFPBIC_UNKNOWN_INST; } } static enum ofperr decode_openflow11_instructions(const struct ofp11_instruction insts[], size_t n_insts, const struct ofp11_instruction *out[]) { const struct ofp11_instruction *inst; size_t left; memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out); INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) { enum ovs_instruction_type type; enum ofperr error; error = decode_openflow11_instruction(inst, &type); if (error) { return error; } if (out[type]) { return OFPERR_OFPBIC_DUP_INST; } out[type] = inst; } if (left) { VLOG_WARN_RL(&rl, "bad instruction format at offset %"PRIuSIZE, (n_insts - left) * sizeof *inst); return OFPERR_OFPBIC_BAD_LEN; } return 0; } static void get_actions_from_instruction(const struct ofp11_instruction *inst, const struct ofp_action_header **actions, size_t *actions_len) { *actions = ALIGNED_CAST(const struct ofp_action_header *, inst + 1); *actions_len = ntohs(inst->len) - sizeof *inst; } enum ofperr ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, unsigned int instructions_len, enum ofp_version version, struct ofpbuf *ofpacts) { const struct ofp11_instruction *instructions; const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS]; enum ofperr error; if (version == OFP10_VERSION) { return ofpacts_pull_openflow_actions__(openflow, instructions_len, version, (1u << N_OVS_INSTRUCTIONS) - 1, ofpacts, 0); } ofpbuf_clear(ofpacts); if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) { VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a " "multiple of %d", instructions_len, OFP11_INSTRUCTION_ALIGN); error = OFPERR_OFPBIC_BAD_LEN; goto exit; } instructions = ofpbuf_try_pull(openflow, instructions_len); if (instructions == NULL) { VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds " "remaining message length (%"PRIu32")", instructions_len, openflow->size); error = OFPERR_OFPBIC_BAD_LEN; goto exit; } error = decode_openflow11_instructions( instructions, instructions_len / OFP11_INSTRUCTION_ALIGN, insts); if (error) { goto exit; } if (insts[OVSINST_OFPIT13_METER]) { const struct ofp13_instruction_meter *oim; struct ofpact_meter *om; oim = ALIGNED_CAST(const struct ofp13_instruction_meter *, insts[OVSINST_OFPIT13_METER]); om = ofpact_put_METER(ofpacts); om->meter_id = ntohl(oim->meter_id); } if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) { const struct ofp_action_header *actions; size_t actions_len; get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS], &actions, &actions_len); error = ofpacts_decode(actions, actions_len, version, ofpacts); if (error) { goto exit; } } if (insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) { instruction_get_OFPIT11_CLEAR_ACTIONS( insts[OVSINST_OFPIT11_CLEAR_ACTIONS]); ofpact_put_CLEAR_ACTIONS(ofpacts); } if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) { struct ofpact_nest *on; const struct ofp_action_header *actions; size_t actions_len; size_t start; ofpact_pad(ofpacts); start = ofpacts->size; ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, offsetof(struct ofpact_nest, actions)); get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS], &actions, &actions_len); error = ofpacts_decode_for_action_set(actions, actions_len, version, ofpacts); if (error) { goto exit; } on = ofpbuf_at_assert(ofpacts, start, sizeof *on); on->ofpact.len = ofpacts->size - start; } if (insts[OVSINST_OFPIT11_WRITE_METADATA]) { const struct ofp11_instruction_write_metadata *oiwm; struct ofpact_metadata *om; oiwm = ALIGNED_CAST(const struct ofp11_instruction_write_metadata *, insts[OVSINST_OFPIT11_WRITE_METADATA]); om = ofpact_put_WRITE_METADATA(ofpacts); om->metadata = oiwm->metadata; om->mask = oiwm->metadata_mask; } if (insts[OVSINST_OFPIT11_GOTO_TABLE]) { const struct ofp11_instruction_goto_table *oigt; struct ofpact_goto_table *ogt; oigt = instruction_get_OFPIT11_GOTO_TABLE( insts[OVSINST_OFPIT11_GOTO_TABLE]); ogt = ofpact_put_GOTO_TABLE(ofpacts); ogt->table_id = oigt->table_id; } ofpact_pad(ofpacts); error = ofpacts_verify(ofpacts->data, ofpacts->size, (1u << N_OVS_INSTRUCTIONS) - 1, 0); exit: if (error) { ofpbuf_clear(ofpacts); } return error; } /* Update the length of the instruction that begins at offset 'ofs' within * 'openflow' and contains nested actions that extend to the end of 'openflow'. * If the instruction contains no nested actions, deletes it entirely. */ static void ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) { struct ofp11_instruction_actions *oia; oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); if (openflow->size > ofs + sizeof *oia) { oia->len = htons(openflow->size - ofs); } else { openflow->size = ofs; } } /* Checks that 'port' is a valid output port for OFPACT_OUTPUT, given that the * switch will never have more than 'max_ports' ports. Returns 0 if 'port' is * valid, otherwise an OpenFlow error code. */ enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports) { switch (port) { case OFPP_IN_PORT: case OFPP_TABLE: case OFPP_NORMAL: case OFPP_FLOOD: case OFPP_ALL: case OFPP_CONTROLLER: case OFPP_LOCAL: return 0; case OFPP_NONE: return OFPERR_OFPBAC_BAD_OUT_PORT; default: if (ofp_to_u16(port) < ofp_to_u16(max_ports)) { return 0; } return OFPERR_OFPBAC_BAD_OUT_PORT; } } /* Removes the protocols that require consistency between match and actions * (that's everything but OpenFlow 1.0) from '*usable_protocols'. * * (An example of an inconsistency between match and actions is a flow that * does not match on an MPLS Ethertype but has an action that pops an MPLS * label.) */ static void inconsistent_match(enum ofputil_protocol *usable_protocols) { *usable_protocols &= OFPUTIL_P_OF10_ANY; } /* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci, * caller must restore them. * * Modifies some actions, filling in fields that could not be properly set * without context. */ static enum ofperr ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, struct flow *flow, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables) { const struct ofpact_enqueue *enqueue; const struct mf_field *mf; switch (a->type) { case OFPACT_OUTPUT: return ofpact_check_output_port(ofpact_get_OUTPUT(a)->port, max_ports); case OFPACT_CONTROLLER: return 0; case OFPACT_ENQUEUE: enqueue = ofpact_get_ENQUEUE(a); if (ofp_to_u16(enqueue->port) >= ofp_to_u16(max_ports) && enqueue->port != OFPP_IN_PORT && enqueue->port != OFPP_LOCAL) { return OFPERR_OFPBAC_BAD_OUT_PORT; } return 0; case OFPACT_OUTPUT_REG: return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow); case OFPACT_BUNDLE: return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow); case OFPACT_SET_VLAN_VID: /* Remember if we saw a vlan tag in the flow to aid translating to * OpenFlow 1.1+ if need be. */ ofpact_get_SET_VLAN_VID(a)->flow_has_vlan = (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI); if (!(flow->vlan_tci & htons(VLAN_CFI)) && !ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) { inconsistent_match(usable_protocols); } /* Temporary mark that we have a vlan tag. */ flow->vlan_tci |= htons(VLAN_CFI); return 0; case OFPACT_SET_VLAN_PCP: /* Remember if we saw a vlan tag in the flow to aid translating to * OpenFlow 1.1+ if need be. */ ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan = (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI); if (!(flow->vlan_tci & htons(VLAN_CFI)) && !ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) { inconsistent_match(usable_protocols); } /* Temporary mark that we have a vlan tag. */ flow->vlan_tci |= htons(VLAN_CFI); return 0; case OFPACT_STRIP_VLAN: if (!(flow->vlan_tci & htons(VLAN_CFI))) { inconsistent_match(usable_protocols); } /* Temporary mark that we have no vlan tag. */ flow->vlan_tci = htons(0); return 0; case OFPACT_PUSH_VLAN: if (flow->vlan_tci & htons(VLAN_CFI)) { /* Multiple VLAN headers not supported. */ return OFPERR_OFPBAC_BAD_TAG; } /* Temporary mark that we have a vlan tag. */ flow->vlan_tci |= htons(VLAN_CFI); return 0; case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: return 0; case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: if (flow->dl_type != htons(ETH_TYPE_IP)) { inconsistent_match(usable_protocols); } return 0; case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_DEC_TTL: if (!is_ip_any(flow)) { inconsistent_match(usable_protocols); } return 0; case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: if (!is_ip_any(flow) || (flow->nw_frag & FLOW_NW_FRAG_LATER) || (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP && flow->nw_proto != IPPROTO_SCTP)) { inconsistent_match(usable_protocols); } /* Note on which transport protocol the port numbers are set. * This allows this set action to be converted to an OF1.2 set field * action. */ if (a->type == OFPACT_SET_L4_SRC_PORT) { ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto; } else { ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto; } return 0; case OFPACT_REG_MOVE: return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow); case OFPACT_SET_FIELD: mf = ofpact_get_SET_FIELD(a)->field; /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */ if (!mf_are_prereqs_ok(mf, flow) || (mf->id == MFF_VLAN_VID && !(flow->vlan_tci & htons(VLAN_CFI)))) { VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisities", mf->name); return OFPERR_OFPBAC_MATCH_INCONSISTENT; } /* Remember if we saw a vlan tag in the flow to aid translating to * OpenFlow 1.1 if need be. */ ofpact_get_SET_FIELD(a)->flow_has_vlan = (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI); if (mf->id == MFF_VLAN_TCI) { /* The set field may add or remove the vlan tag, * Mark the status temporarily. */ flow->vlan_tci = ofpact_get_SET_FIELD(a)->value.be16; } return 0; case OFPACT_STACK_PUSH: return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow); case OFPACT_STACK_POP: return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow); case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: if (!eth_type_mpls(flow->dl_type)) { inconsistent_match(usable_protocols); } return 0; case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_RESUBMIT: return 0; case OFPACT_FIN_TIMEOUT: if (flow->nw_proto != IPPROTO_TCP) { inconsistent_match(usable_protocols); } return 0; case OFPACT_LEARN: return learn_check(ofpact_get_LEARN(a), flow); case OFPACT_CONJUNCTION: return 0; case OFPACT_MULTIPATH: return multipath_check(ofpact_get_MULTIPATH(a), flow); case OFPACT_NOTE: case OFPACT_EXIT: return 0; case OFPACT_PUSH_MPLS: flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; /* The packet is now MPLS and the MPLS payload is opaque. * Thus nothing can be assumed about the network protocol. * Temporarily mark that we have no nw_proto. */ flow->nw_proto = 0; return 0; case OFPACT_POP_MPLS: if (!eth_type_mpls(flow->dl_type)) { inconsistent_match(usable_protocols); } flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype; return 0; case OFPACT_SAMPLE: return 0; case OFPACT_CT: { struct ofpact_conntrack *oc = ofpact_get_CT(a); if (!dl_type_is_ip_any(flow->dl_type) || (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT) || (oc->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP)) { /* We can't downgrade to OF1.0 and expect inconsistent CT actions * be silently discarded. Instead, datapath flow install fails, so * it is better to flag inconsistent CT actions as hard errors. */ return OFPERR_OFPBAC_MATCH_INCONSISTENT; } if (oc->zone_src.field) { return mf_check_src(&oc->zone_src, flow); } return ofpacts_check(oc->actions, ofpact_ct_get_action_len(oc), flow, max_ports, table_id, n_tables, usable_protocols); } case OFPACT_CLEAR_ACTIONS: return 0; case OFPACT_WRITE_ACTIONS: { /* Use a temporary copy of 'usable_protocols' because we can't check * consistency of an action set. */ struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a); enum ofputil_protocol p = *usable_protocols; return ofpacts_check(on->actions, ofpact_nest_get_action_len(on), flow, max_ports, table_id, n_tables, &p); } case OFPACT_WRITE_METADATA: return 0; case OFPACT_METER: { uint32_t mid = ofpact_get_METER(a)->meter_id; if (mid == 0 || mid > OFPM13_MAX) { return OFPERR_OFPMMFC_INVALID_METER; } return 0; } case OFPACT_GOTO_TABLE: { uint8_t goto_table = ofpact_get_GOTO_TABLE(a)->table_id; if ((table_id != 255 && goto_table <= table_id) || (n_tables != 255 && goto_table >= n_tables)) { return OFPERR_OFPBIC_BAD_TABLE_ID; } return 0; } case OFPACT_GROUP: return 0; case OFPACT_UNROLL_XLATE: /* UNROLL is an internal action that should never be seen via * OpenFlow. */ return OFPERR_OFPBAC_BAD_TYPE; case OFPACT_DEBUG_RECIRC: return 0; default: OVS_NOT_REACHED(); } } /* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are * appropriate for a packet with the prerequisites satisfied by 'flow' in a * switch with no more than 'max_ports' ports. * * If 'ofpacts' and 'flow' are inconsistent with one another, un-sets in * '*usable_protocols' the protocols that forbid the inconsistency. (An * example of an inconsistency between match and actions is a flow that does * not match on an MPLS Ethertype but has an action that pops an MPLS label.) * * May annotate ofpacts with information gathered from the 'flow'. * * May temporarily modify 'flow', but restores the changes before returning. */ enum ofperr ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len, struct flow *flow, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables, enum ofputil_protocol *usable_protocols) { struct ofpact *a; ovs_be16 dl_type = flow->dl_type; ovs_be16 vlan_tci = flow->vlan_tci; uint8_t nw_proto = flow->nw_proto; enum ofperr error = 0; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { error = ofpact_check__(usable_protocols, a, flow, max_ports, table_id, n_tables); if (error) { break; } } /* Restore fields that may have been modified. */ flow->dl_type = dl_type; flow->vlan_tci = vlan_tci; flow->nw_proto = nw_proto; return error; } /* Like ofpacts_check(), but reports inconsistencies as * OFPERR_OFPBAC_MATCH_INCONSISTENT rather than clearing bits. */ enum ofperr ofpacts_check_consistency(struct ofpact ofpacts[], size_t ofpacts_len, struct flow *flow, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables, enum ofputil_protocol usable_protocols) { enum ofputil_protocol p = usable_protocols; enum ofperr error; error = ofpacts_check(ofpacts, ofpacts_len, flow, max_ports, table_id, n_tables, &p); return (error ? error : p != usable_protocols ? OFPERR_OFPBAC_MATCH_INCONSISTENT : 0); } /* Returns the destination field that 'ofpact' would write to, or NULL * if the action would not write to an mf_field. */ const struct mf_field * ofpact_get_mf_dst(const struct ofpact *ofpact) { if (ofpact->type == OFPACT_SET_FIELD) { const struct ofpact_set_field *orl; orl = CONTAINER_OF(ofpact, struct ofpact_set_field, ofpact); return orl->field; } else if (ofpact->type == OFPACT_REG_MOVE) { const struct ofpact_reg_move *orm; orm = CONTAINER_OF(ofpact, struct ofpact_reg_move, ofpact); return orm->dst.field; } return NULL; } static enum ofperr unsupported_nesting(enum ofpact_type action, enum ofpact_type outer_action) { VLOG_WARN("%s action doesn't support nested action %s", ofpact_name(outer_action), ofpact_name(action)); return OFPERR_OFPBAC_BAD_ARGUMENT; } static bool field_requires_ct(enum mf_field_id field) { return field == MFF_CT_MARK || field == MFF_CT_LABEL; } /* Apply nesting constraints for actions */ static enum ofperr ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action) { const struct mf_field *field = ofpact_get_mf_dst(a); if (field && field_requires_ct(field->id) && outer_action != OFPACT_CT) { VLOG_WARN("cannot set CT fields outside of ct action"); return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } if (outer_action) { ovs_assert(outer_action == OFPACT_WRITE_ACTIONS || outer_action == OFPACT_CT); if (outer_action == OFPACT_CT) { if (!field) { return unsupported_nesting(a->type, outer_action); } else if (!field_requires_ct(field->id)) { VLOG_WARN("%s action doesn't support nested modification " "of %s", ofpact_name(outer_action), field->name); return OFPERR_OFPBAC_BAD_ARGUMENT; } } } return 0; } /* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are in the * appropriate order as defined by the OpenFlow spec and as required by Open * vSwitch. * * 'allowed_ovsinsts' is a bitmap of OVSINST_* values, in which 1-bits indicate * instructions that are allowed within 'ofpacts[]'. * * If 'outer_action' is not zero, it specifies that the actions are nested * within another action of type 'outer_action'. */ static enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len, uint32_t allowed_ovsinsts, enum ofpact_type outer_action) { const struct ofpact *a; enum ovs_instruction_type inst; inst = OVSINST_OFPIT13_METER; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { enum ovs_instruction_type next; enum ofperr error; if (a->type == OFPACT_CONJUNCTION) { OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { if (a->type != OFPACT_CONJUNCTION && a->type != OFPACT_NOTE) { VLOG_WARN("\"conjunction\" actions may be used along with " "\"note\" but not any other kind of action " "(such as the \"%s\" action used here)", ofpact_name(a->type)); return OFPERR_NXBAC_BAD_CONJUNCTION; } } return 0; } error = ofpacts_verify_nested(a, outer_action); if (error) { return error; } next = ovs_instruction_type_from_ofpact_type(a->type); if (a > ofpacts && (inst == OVSINST_OFPIT11_APPLY_ACTIONS ? next < inst : next <= inst)) { const char *name = ovs_instruction_name_from_type(inst); const char *next_name = ovs_instruction_name_from_type(next); if (next == inst) { VLOG_WARN("duplicate %s instruction not allowed, for OpenFlow " "1.1+ compatibility", name); } else { VLOG_WARN("invalid instruction ordering: %s must appear " "before %s, for OpenFlow 1.1+ compatibility", next_name, name); } return OFPERR_OFPBAC_UNSUPPORTED_ORDER; } if (!((1u << next) & allowed_ovsinsts)) { const char *name = ovs_instruction_name_from_type(next); VLOG_WARN("%s instruction not allowed here", name); return OFPERR_OFPBIC_UNSUP_INST; } inst = next; } return 0; } /* Converting ofpacts to OpenFlow. */ static void encode_ofpact(const struct ofpact *a, enum ofp_version ofp_version, struct ofpbuf *out) { switch (a->type) { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ case OFPACT_##ENUM: \ encode_##ENUM(ofpact_get_##ENUM(a), ofp_version, out); \ return; OFPACTS #undef OFPACT default: OVS_NOT_REACHED(); } } /* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow * actions in 'openflow', appending the actions to any existing data in * 'openflow'. */ size_t ofpacts_put_openflow_actions(const struct ofpact ofpacts[], size_t ofpacts_len, struct ofpbuf *openflow, enum ofp_version ofp_version) { const struct ofpact *a; size_t start_size = openflow->size; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { encode_ofpact(a, ofp_version, openflow); } return openflow->size - start_size; } static enum ovs_instruction_type ofpact_is_apply_actions(const struct ofpact *a) { return (ovs_instruction_type_from_ofpact_type(a->type) == OVSINST_OFPIT11_APPLY_ACTIONS); } void ofpacts_put_openflow_instructions(const struct ofpact ofpacts[], size_t ofpacts_len, struct ofpbuf *openflow, enum ofp_version ofp_version) { const struct ofpact *end = ofpact_end(ofpacts, ofpacts_len); const struct ofpact *a; if (ofp_version == OFP10_VERSION) { ofpacts_put_openflow_actions(ofpacts, ofpacts_len, openflow, ofp_version); return; } a = ofpacts; while (a < end) { if (ofpact_is_apply_actions(a)) { size_t ofs = openflow->size; instruction_put_OFPIT11_APPLY_ACTIONS(openflow); do { encode_ofpact(a, ofp_version, openflow); a = ofpact_next(a); } while (a < end && ofpact_is_apply_actions(a)); ofpacts_update_instruction_actions(openflow, ofs); } else { encode_ofpact(a, ofp_version, openflow); a = ofpact_next(a); } } } /* Sets of supported actions. */ /* Two-way translation between OVS's internal "OFPACT_*" representation of * actions and the "OFPAT_*" representation used in some OpenFlow version. * (OFPAT_* numbering varies from one OpenFlow version to another, so a given * instance is specific to one OpenFlow version.) */ struct ofpact_map { enum ofpact_type ofpact; /* Internal name for action type. */ int ofpat; /* OFPAT_* number from OpenFlow spec. */ }; static const struct ofpact_map * get_ofpact_map(enum ofp_version version) { /* OpenFlow 1.0 actions. */ static const struct ofpact_map of10[] = { { OFPACT_OUTPUT, 0 }, { OFPACT_SET_VLAN_VID, 1 }, { OFPACT_SET_VLAN_PCP, 2 }, { OFPACT_STRIP_VLAN, 3 }, { OFPACT_SET_ETH_SRC, 4 }, { OFPACT_SET_ETH_DST, 5 }, { OFPACT_SET_IPV4_SRC, 6 }, { OFPACT_SET_IPV4_DST, 7 }, { OFPACT_SET_IP_DSCP, 8 }, { OFPACT_SET_L4_SRC_PORT, 9 }, { OFPACT_SET_L4_DST_PORT, 10 }, { OFPACT_ENQUEUE, 11 }, { 0, -1 }, }; /* OpenFlow 1.1 actions. */ static const struct ofpact_map of11[] = { { OFPACT_OUTPUT, 0 }, { OFPACT_SET_VLAN_VID, 1 }, { OFPACT_SET_VLAN_PCP, 2 }, { OFPACT_SET_ETH_SRC, 3 }, { OFPACT_SET_ETH_DST, 4 }, { OFPACT_SET_IPV4_SRC, 5 }, { OFPACT_SET_IPV4_DST, 6 }, { OFPACT_SET_IP_DSCP, 7 }, { OFPACT_SET_IP_ECN, 8 }, { OFPACT_SET_L4_SRC_PORT, 9 }, { OFPACT_SET_L4_DST_PORT, 10 }, /* OFPAT_COPY_TTL_OUT (11) not supported. */ /* OFPAT_COPY_TTL_IN (12) not supported. */ { OFPACT_SET_MPLS_LABEL, 13 }, { OFPACT_SET_MPLS_TC, 14 }, { OFPACT_SET_MPLS_TTL, 15 }, { OFPACT_DEC_MPLS_TTL, 16 }, { OFPACT_PUSH_VLAN, 17 }, { OFPACT_STRIP_VLAN, 18 }, { OFPACT_PUSH_MPLS, 19 }, { OFPACT_POP_MPLS, 20 }, { OFPACT_SET_QUEUE, 21 }, { OFPACT_GROUP, 22 }, { OFPACT_SET_IP_TTL, 23 }, { OFPACT_DEC_TTL, 24 }, { 0, -1 }, }; /* OpenFlow 1.2, 1.3, and 1.4 actions. */ static const struct ofpact_map of12[] = { { OFPACT_OUTPUT, 0 }, /* OFPAT_COPY_TTL_OUT (11) not supported. */ /* OFPAT_COPY_TTL_IN (12) not supported. */ { OFPACT_SET_MPLS_TTL, 15 }, { OFPACT_DEC_MPLS_TTL, 16 }, { OFPACT_PUSH_VLAN, 17 }, { OFPACT_STRIP_VLAN, 18 }, { OFPACT_PUSH_MPLS, 19 }, { OFPACT_POP_MPLS, 20 }, { OFPACT_SET_QUEUE, 21 }, { OFPACT_GROUP, 22 }, { OFPACT_SET_IP_TTL, 23 }, { OFPACT_DEC_TTL, 24 }, { OFPACT_SET_FIELD, 25 }, /* OF1.3+ OFPAT_PUSH_PBB (26) not supported. */ /* OF1.3+ OFPAT_POP_PBB (27) not supported. */ { 0, -1 }, }; switch (version) { case OFP10_VERSION: return of10; case OFP11_VERSION: return of11; case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: default: return of12; } } /* Converts 'ofpacts_bitmap', a bitmap whose bits correspond to OFPACT_* * values, into a bitmap of actions suitable for OpenFlow 'version', and * returns the result. */ ovs_be32 ofpact_bitmap_to_openflow(uint64_t ofpacts_bitmap, enum ofp_version version) { uint32_t openflow_bitmap = 0; const struct ofpact_map *x; for (x = get_ofpact_map(version); x->ofpat >= 0; x++) { if (ofpacts_bitmap & (UINT64_C(1) << x->ofpact)) { openflow_bitmap |= 1u << x->ofpat; } } return htonl(openflow_bitmap); } /* Converts 'ofpat_bitmap', a bitmap of actions from an OpenFlow message with * the given 'version' into a bitmap whose bits correspond to OFPACT_* values, * and returns the result. */ uint64_t ofpact_bitmap_from_openflow(ovs_be32 ofpat_bitmap, enum ofp_version version) { uint64_t ofpact_bitmap = 0; const struct ofpact_map *x; for (x = get_ofpact_map(version); x->ofpat >= 0; x++) { if (ofpat_bitmap & htonl(1u << x->ofpat)) { ofpact_bitmap |= UINT64_C(1) << x->ofpact; } } return ofpact_bitmap; } /* Appends to 's' a string representation of the set of OFPACT_* represented * by 'ofpacts_bitmap'. */ void ofpact_bitmap_format(uint64_t ofpacts_bitmap, struct ds *s) { if (!ofpacts_bitmap) { ds_put_cstr(s, ""); } else { while (ofpacts_bitmap) { ds_put_format(s, "%s ", ofpact_name(rightmost_1bit_idx(ofpacts_bitmap))); ofpacts_bitmap = zero_rightmost_1bit(ofpacts_bitmap); } ds_chomp(s, ' '); } } /* Returns true if 'action' outputs to 'port', false otherwise. */ static bool ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) { switch (ofpact->type) { case OFPACT_OUTPUT: return ofpact_get_OUTPUT(ofpact)->port == port; case OFPACT_ENQUEUE: return ofpact_get_ENQUEUE(ofpact)->port == port; case OFPACT_CONTROLLER: return port == OFPP_CONTROLLER; case OFPACT_OUTPUT_REG: case OFPACT_BUNDLE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: case OFPACT_SET_FIELD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: case OFPACT_SET_TUNNEL: case OFPACT_WRITE_METADATA: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: case OFPACT_RESUBMIT: case OFPACT_LEARN: case OFPACT_CONJUNCTION: case OFPACT_MULTIPATH: case OFPACT_NOTE: case OFPACT_EXIT: case OFPACT_UNROLL_XLATE: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SAMPLE: case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_ACTIONS: case OFPACT_GOTO_TABLE: case OFPACT_METER: case OFPACT_GROUP: case OFPACT_DEBUG_RECIRC: case OFPACT_CT: default: return false; } } /* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs * to 'port', false otherwise. */ bool ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len, ofp_port_t port) { const struct ofpact *a; OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { if (ofpact_outputs_to_port(a, port)) { return true; } } return false; } /* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs * to 'group', false otherwise. */ bool ofpacts_output_to_group(const struct ofpact *ofpacts, size_t ofpacts_len, uint32_t group_id) { const struct ofpact *a; OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { if (a->type == OFPACT_GROUP && ofpact_get_GROUP(a)->group_id == group_id) { return true; } } return false; } bool ofpacts_equal(const struct ofpact *a, size_t a_len, const struct ofpact *b, size_t b_len) { return a_len == b_len && !memcmp(a, b, a_len); } /* Finds the OFPACT_METER action, if any, in the 'ofpacts_len' bytes of * 'ofpacts'. If found, returns its meter ID; if not, returns 0. * * This function relies on the order of 'ofpacts' being correct (as checked by * ofpacts_verify()). */ uint32_t ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len) { const struct ofpact *a; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { enum ovs_instruction_type inst; inst = ovs_instruction_type_from_ofpact_type(a->type); if (a->type == OFPACT_METER) { return ofpact_get_METER(a)->meter_id; } else if (inst > OVSINST_OFPIT13_METER) { break; } } return 0; } /* Formatting ofpacts. */ static void ofpact_format(const struct ofpact *a, struct ds *s) { switch (a->type) { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ case OFPACT_##ENUM: \ format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s); \ break; OFPACTS #undef OFPACT default: OVS_NOT_REACHED(); } } /* Appends a string representing the 'ofpacts_len' bytes of ofpacts in * 'ofpacts' to 'string'. */ void ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, struct ds *string) { if (!ofpacts_len) { ds_put_cstr(string, "drop"); } else { const struct ofpact *a; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { if (a != ofpacts) { ds_put_cstr(string, ","); } /* XXX write-actions */ ofpact_format(a, string); } } } /* Internal use by helpers. */ void * ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len) { struct ofpact *ofpact; ofpact_pad(ofpacts); ofpacts->header = ofpbuf_put_uninit(ofpacts, len); ofpact = ofpacts->header; ofpact_init(ofpact, type, len); return ofpact; } void ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len) { memset(ofpact, 0, len); ofpact->type = type; ofpact->raw = -1; ofpact->len = len; } /* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts' * starting at 'ofpact'. * * This is the correct way to update a variable-length ofpact's length after * adding the variable-length part of the payload. (See the large comment * near the end of ofp-actions.h for more information.) */ void ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact) { ptrdiff_t len; ovs_assert(ofpact == ofpacts->header); len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact; ovs_assert(len <= UINT16_MAX); ofpact->len = len; } /* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length. Each * ofpact_put_() calls this function automatically beforehand, but the * client must call this itself after adding the final ofpact to an array of * them. * * (The consequences of failing to call this function are probably not dire. * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but * not dereference it. That's undefined behavior, technically, but it will not * cause a real problem on common systems. Still, it seems better to call * it.) */ void ofpact_pad(struct ofpbuf *ofpacts) { unsigned int pad = PAD_SIZE(ofpacts->size, OFPACT_ALIGNTO); if (pad) { ofpbuf_put_zeros(ofpacts, pad); } } static char * OVS_WARN_UNUSED_RESULT ofpact_parse(enum ofpact_type type, char *value, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { switch (type) { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ case OFPACT_##ENUM: \ return parse_##ENUM(value, ofpacts, usable_protocols); OFPACTS #undef OFPACT default: OVS_NOT_REACHED(); } } static bool ofpact_type_from_name(const char *name, enum ofpact_type *type) { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ if (!strcasecmp(name, NAME)) { \ *type = OFPACT_##ENUM; \ return true; \ } OFPACTS #undef OFPACT return false; } /* Parses 'str' as a series of instructions, and appends them to 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. * * If 'outer_action' is specified, indicates that the actions being parsed * are nested within another action of the type specified in 'outer_action'. */ static char * OVS_WARN_UNUSED_RESULT ofpacts_parse__(char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, bool allow_instructions, enum ofpact_type outer_action) { int prev_inst = -1; enum ofperr retval; char *key, *value; bool drop = false; char *pos; pos = str; while (ofputil_parse_key_value(&pos, &key, &value)) { enum ovs_instruction_type inst = OVSINST_OFPIT11_APPLY_ACTIONS; enum ofpact_type type; char *error = NULL; ofp_port_t port; if (ofpact_type_from_name(key, &type)) { error = ofpact_parse(type, value, ofpacts, usable_protocols); inst = ovs_instruction_type_from_ofpact_type(type); } else if (!strcasecmp(key, "mod_vlan_vid")) { error = parse_set_vlan_vid(value, ofpacts, true); } else if (!strcasecmp(key, "mod_vlan_pcp")) { error = parse_set_vlan_pcp(value, ofpacts, true); } else if (!strcasecmp(key, "set_nw_ttl")) { error = parse_SET_IP_TTL(value, ofpacts, usable_protocols); } else if (!strcasecmp(key, "pop_vlan")) { error = parse_pop_vlan(ofpacts); } else if (!strcasecmp(key, "set_tunnel64")) { error = parse_set_tunnel(value, ofpacts, NXAST_RAW_SET_TUNNEL64); } else if (!strcasecmp(key, "load")) { error = parse_reg_load(value, ofpacts); } else if (!strcasecmp(key, "bundle_load")) { error = parse_bundle_load(value, ofpacts); } else if (!strcasecmp(key, "drop")) { drop = true; } else if (!strcasecmp(key, "apply_actions")) { return xstrdup("apply_actions is the default instruction"); } else if (ofputil_port_from_string(key, &port)) { ofpact_put_OUTPUT(ofpacts)->port = port; } else { return xasprintf("unknown action %s", key); } if (error) { return error; } if (inst != OVSINST_OFPIT11_APPLY_ACTIONS) { if (!allow_instructions) { return xasprintf("only actions are allowed here (not " "instruction %s)", ovs_instruction_name_from_type(inst)); } if (inst == prev_inst) { return xasprintf("instruction %s may be specified only once", ovs_instruction_name_from_type(inst)); } } if (prev_inst != -1 && inst < prev_inst) { return xasprintf("instruction %s must be specified before %s", ovs_instruction_name_from_type(inst), ovs_instruction_name_from_type(prev_inst)); } prev_inst = inst; } ofpact_pad(ofpacts); if (drop && ofpacts->size) { return xstrdup("\"drop\" must not be accompanied by any other action " "or instruction"); } retval = ofpacts_verify(ofpacts->data, ofpacts->size, (allow_instructions ? (1u << N_OVS_INSTRUCTIONS) - 1 : 1u << OVSINST_OFPIT11_APPLY_ACTIONS), outer_action); if (retval) { return xstrdup("Incorrect instruction ordering"); } return NULL; } static char * OVS_WARN_UNUSED_RESULT ofpacts_parse(char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, bool allow_instructions, enum ofpact_type outer_action) { uint32_t orig_size = ofpacts->size; char *error = ofpacts_parse__(str, ofpacts, usable_protocols, allow_instructions, outer_action); if (error) { ofpacts->size = orig_size; } return error; } static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols, bool allow_instructions, enum ofpact_type outer_action) { char *error, *s; *usable_protocols = OFPUTIL_P_ANY; s = xstrdup(s_); error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions, outer_action); free(s); return error; } /* Parses 's' as a set of OpenFlow actions and appends the actions to * 'ofpacts'. 'outer_action', if nonzero, specifies that 's' contains actions * that are nested within the action of type 'outer_action'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { return ofpacts_parse_copy(s, ofpacts, usable_protocols, false, 0); } /* Parses 's' as a set of OpenFlow instructions and appends the instructions to * 'ofpacts'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT ofpacts_parse_instructions(const char *s, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) { return ofpacts_parse_copy(s, ofpacts, usable_protocols, true, 0); } const char * ofpact_name(enum ofpact_type type) { switch (type) { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) case OFPACT_##ENUM: return NAME; OFPACTS #undef OFPACT } return ""; } /* Low-level action decoding and encoding functions. */ /* Everything needed to identify a particular OpenFlow action. */ struct ofpact_hdrs { uint32_t vendor; /* 0 if standard, otherwise a vendor code. */ uint16_t type; /* Type if standard, otherwise subtype. */ uint8_t ofp_version; /* From ofp_header. */ }; /* Information about a particular OpenFlow action. */ struct ofpact_raw_instance { /* The action's identity. */ struct ofpact_hdrs hdrs; enum ofp_raw_action_type raw; /* Looking up the action. */ struct hmap_node decode_node; /* Based on 'hdrs'. */ struct hmap_node encode_node; /* Based on 'raw' + 'hdrs.ofp_version'. */ /* The action's encoded size. * * If this action is fixed-length, 'min_length' == 'max_length'. * If it is variable length, then 'max_length' is ROUND_DOWN(UINT16_MAX, * OFP_ACTION_ALIGN) == 65528. */ unsigned short int min_length; unsigned short int max_length; /* For actions with a simple integer numeric argument, 'arg_ofs' is the * offset of that argument from the beginning of the action and 'arg_len' * its length, both in bytes. * * For actions that take other forms, these are both zero. */ unsigned short int arg_ofs; unsigned short int arg_len; /* The name of the action, e.g. "OFPAT_OUTPUT" or "NXAST_RESUBMIT". */ const char *name; /* If this action is deprecated, a human-readable string with a brief * explanation. */ const char *deprecation; }; /* Action header. */ struct ofp_action_header { /* The meaning of other values of 'type' generally depends on the OpenFlow * version (see enum ofp_raw_action_type). * * Across all OpenFlow versions, OFPAT_VENDOR indicates that 'vendor' * designates an OpenFlow vendor ID and that the remainder of the action * structure has a vendor-defined meaning. */ #define OFPAT_VENDOR 0xffff ovs_be16 type; /* Always a multiple of 8. */ ovs_be16 len; /* For type == OFPAT_VENDOR only, this is a vendor ID, e.g. NX_VENDOR_ID or * ONF_VENDOR_ID. Other 'type's use this space for some other purpose. */ ovs_be32 vendor; }; OFP_ASSERT(sizeof(struct ofp_action_header) == 8); /* Header for Nicira-defined actions and for ONF vendor extensions. * * This cannot be used as an entirely generic vendor extension action header, * because OpenFlow does not specify the location or size of the action * subtype; it just happens that ONF extensions and Nicira extensions share * this format. */ struct ext_action_header { ovs_be16 type; /* OFPAT_VENDOR. */ ovs_be16 len; /* At least 16. */ ovs_be32 vendor; /* NX_VENDOR_ID or ONF_VENDOR_ID. */ ovs_be16 subtype; /* See enum ofp_raw_action_type. */ uint8_t pad[6]; }; OFP_ASSERT(sizeof(struct ext_action_header) == 16); static bool ofpact_hdrs_equal(const struct ofpact_hdrs *a, const struct ofpact_hdrs *b) { return (a->vendor == b->vendor && a->type == b->type && a->ofp_version == b->ofp_version); } static uint32_t ofpact_hdrs_hash(const struct ofpact_hdrs *hdrs) { return hash_2words(hdrs->vendor, (hdrs->type << 16) | hdrs->ofp_version); } #include "ofp-actions.inc2" static struct hmap * ofpact_decode_hmap(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static struct hmap hmap; if (ovsthread_once_start(&once)) { struct ofpact_raw_instance *inst; hmap_init(&hmap); for (inst = all_raw_instances; inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)]; inst++) { hmap_insert(&hmap, &inst->decode_node, ofpact_hdrs_hash(&inst->hdrs)); } ovsthread_once_done(&once); } return &hmap; } static struct hmap * ofpact_encode_hmap(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static struct hmap hmap; if (ovsthread_once_start(&once)) { struct ofpact_raw_instance *inst; hmap_init(&hmap); for (inst = all_raw_instances; inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)]; inst++) { hmap_insert(&hmap, &inst->encode_node, hash_2words(inst->raw, inst->hdrs.ofp_version)); } ovsthread_once_done(&once); } return &hmap; } static enum ofperr ofpact_decode_raw(enum ofp_version ofp_version, const struct ofp_action_header *oah, size_t length, const struct ofpact_raw_instance **instp) { const struct ofpact_raw_instance *inst; struct ofpact_hdrs hdrs; *instp = NULL; if (length < sizeof *oah) { return OFPERR_OFPBAC_BAD_LEN; } /* Get base action type. */ if (oah->type == htons(OFPAT_VENDOR)) { /* Get vendor. */ hdrs.vendor = ntohl(oah->vendor); if (hdrs.vendor == NX_VENDOR_ID || hdrs.vendor == ONF_VENDOR_ID) { /* Get extension subtype. */ const struct ext_action_header *nah; nah = ALIGNED_CAST(const struct ext_action_header *, oah); if (length < sizeof *nah) { return OFPERR_OFPBAC_BAD_LEN; } hdrs.type = ntohs(nah->subtype); } else { VLOG_WARN_RL(&rl, "OpenFlow action has unknown vendor %#"PRIx32, hdrs.vendor); return OFPERR_OFPBAC_BAD_VENDOR; } } else { hdrs.vendor = 0; hdrs.type = ntohs(oah->type); } hdrs.ofp_version = ofp_version; HMAP_FOR_EACH_WITH_HASH (inst, decode_node, ofpact_hdrs_hash(&hdrs), ofpact_decode_hmap()) { if (ofpact_hdrs_equal(&hdrs, &inst->hdrs)) { *instp = inst; return 0; } } return (hdrs.vendor ? OFPERR_OFPBAC_BAD_VENDOR_TYPE : OFPERR_OFPBAC_BAD_TYPE); } static enum ofperr ofpact_pull_raw(struct ofpbuf *buf, enum ofp_version ofp_version, enum ofp_raw_action_type *raw, uint64_t *arg) { const struct ofp_action_header *oah = buf->data; const struct ofpact_raw_instance *action; unsigned int length; enum ofperr error; *raw = *arg = 0; error = ofpact_decode_raw(ofp_version, oah, buf->size, &action); if (error) { return error; } if (action->deprecation) { VLOG_INFO_RL(&rl, "%s is deprecated in %s (%s)", action->name, ofputil_version_to_string(ofp_version), action->deprecation); } length = ntohs(oah->len); if (length > buf->size) { VLOG_WARN_RL(&rl, "OpenFlow action %s length %u exceeds action buffer " "length %"PRIu32, action->name, length, buf->size); return OFPERR_OFPBAC_BAD_LEN; } if (length < action->min_length || length > action->max_length) { VLOG_WARN_RL(&rl, "OpenFlow action %s length %u not in valid range " "[%hu,%hu]", action->name, length, action->min_length, action->max_length); return OFPERR_OFPBAC_BAD_LEN; } if (length % 8) { VLOG_WARN_RL(&rl, "OpenFlow action %s length %u is not a multiple " "of 8", action->name, length); return OFPERR_OFPBAC_BAD_LEN; } *raw = action->raw; *arg = 0; if (action->arg_len) { const uint8_t *p; int i; p = ofpbuf_at_assert(buf, action->arg_ofs, action->arg_len); for (i = 0; i < action->arg_len; i++) { *arg = (*arg << 8) | p[i]; } } ofpbuf_pull(buf, length); return 0; } static const struct ofpact_raw_instance * ofpact_raw_lookup(enum ofp_version ofp_version, enum ofp_raw_action_type raw) { const struct ofpact_raw_instance *inst; HMAP_FOR_EACH_WITH_HASH (inst, encode_node, hash_2words(raw, ofp_version), ofpact_encode_hmap()) { if (inst->raw == raw && inst->hdrs.ofp_version == ofp_version) { return inst; } } OVS_NOT_REACHED(); } static void * ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version, enum ofp_raw_action_type raw, uint64_t arg) { const struct ofpact_raw_instance *inst; struct ofp_action_header *oah; const struct ofpact_hdrs *hdrs; inst = ofpact_raw_lookup(ofp_version, raw); hdrs = &inst->hdrs; oah = ofpbuf_put_zeros(buf, inst->min_length); oah->type = htons(hdrs->vendor ? OFPAT_VENDOR : hdrs->type); oah->len = htons(inst->min_length); oah->vendor = htonl(hdrs->vendor); switch (hdrs->vendor) { case 0: break; case NX_VENDOR_ID: case ONF_VENDOR_ID: { struct ext_action_header *nah = (struct ext_action_header *) oah; nah->subtype = htons(hdrs->type); break; } default: OVS_NOT_REACHED(); } if (inst->arg_len) { uint8_t *p = (uint8_t *) oah + inst->arg_ofs + inst->arg_len; int i; for (i = 0; i < inst->arg_len; i++) { *--p = arg; arg >>= 8; } } else { ovs_assert(!arg); } return oah; } static void pad_ofpat(struct ofpbuf *openflow, size_t start_ofs) { struct ofp_action_header *oah; ofpbuf_put_zeros(openflow, PAD_SIZE(openflow->size - start_ofs, 8)); oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah); oah->len = htons(openflow->size - start_ofs); } openvswitch-2.5.9/lib/PaxHeaders.82075/stream-nossl.c0000644000000000000000000000013213534540071017205 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801425.005855078 openvswitch-2.5.9/lib/stream-nossl.c0000644000175000017500000000345413534540071020701 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream-ssl.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stream_nossl); /* Dummy function definitions, used when OVS is built without OpenSSL. */ bool stream_ssl_is_configured(void) { return false; } OVS_NO_RETURN static void nossl_option(const char *detail) { VLOG_FATAL("%s specified but Open vSwitch was built without SSL support", detail); } void stream_ssl_set_private_key_file(const char *file_name) { if (file_name != NULL) { nossl_option("Private key"); } } void stream_ssl_set_certificate_file(const char *file_name) { if (file_name != NULL) { nossl_option("Certificate"); } } void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap OVS_UNUSED) { if (file_name != NULL) { nossl_option("CA certificate"); } } void stream_ssl_set_peer_ca_cert_file(const char *file_name) { if (file_name != NULL) { nossl_option("Peer CA certificate"); } } void stream_ssl_set_key_and_cert(const char *private_key_file, const char *certificate_file) { stream_ssl_set_private_key_file(private_key_file); stream_ssl_set_certificate_file(certificate_file); } openvswitch-2.5.9/lib/PaxHeaders.82075/seq.c0000644000000000000000000000013213534540071015346 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.869854075 openvswitch-2.5.9/lib/seq.c0000644000175000017500000002112113534540071017031 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "seq.h" #include #include "coverage.h" #include "hash.h" #include "hmap.h" #include "latch.h" #include "list.h" #include "ovs-thread.h" #include "poll-loop.h" COVERAGE_DEFINE(seq_change); /* A sequence number object. */ struct seq { uint64_t value OVS_GUARDED; struct hmap waiters OVS_GUARDED; /* Contains 'struct seq_waiter's. */ }; /* A thread waiting on a particular seq. */ struct seq_waiter { struct seq *seq OVS_GUARDED; /* Seq being waited for. */ struct hmap_node hmap_node OVS_GUARDED; /* In 'seq->waiters'. */ unsigned int ovsthread_id OVS_GUARDED; /* Key in 'waiters' hmap. */ struct seq_thread *thread OVS_GUARDED; /* Thread preparing to wait. */ struct ovs_list list_node OVS_GUARDED; /* In 'thread->waiters'. */ uint64_t value OVS_GUARDED; /* seq->value we're waiting to change. */ }; /* A thread that might be waiting on one or more seqs. */ struct seq_thread { struct ovs_list waiters OVS_GUARDED; /* Contains 'struct seq_waiter's. */ struct latch latch OVS_GUARDED; /* Wakeup latch for this thread. */ bool waiting OVS_GUARDED; /* True if latch_wait() already called. */ }; static struct ovs_mutex seq_mutex = OVS_MUTEX_INITIALIZER; static uint64_t seq_next OVS_GUARDED_BY(seq_mutex) = 1; static pthread_key_t seq_thread_key; static void seq_init(void); static struct seq_thread *seq_thread_get(void) OVS_REQUIRES(seq_mutex); static void seq_thread_exit(void *thread_) OVS_EXCLUDED(seq_mutex); static void seq_thread_woke(struct seq_thread *) OVS_REQUIRES(seq_mutex); static void seq_waiter_destroy(struct seq_waiter *) OVS_REQUIRES(seq_mutex); static void seq_wake_waiters(struct seq *) OVS_REQUIRES(seq_mutex); /* Creates and returns a new 'seq' object. */ struct seq * OVS_EXCLUDED(seq_mutex) seq_create(void) { struct seq *seq; seq_init(); seq = xmalloc(sizeof *seq); COVERAGE_INC(seq_change); ovs_mutex_lock(&seq_mutex); seq->value = seq_next++; hmap_init(&seq->waiters); ovs_mutex_unlock(&seq_mutex); return seq; } /* Destroys 'seq', waking up threads that were waiting on it, if any. */ void seq_destroy(struct seq *seq) OVS_EXCLUDED(seq_mutex) { ovs_mutex_lock(&seq_mutex); seq_wake_waiters(seq); hmap_destroy(&seq->waiters); free(seq); ovs_mutex_unlock(&seq_mutex); } int seq_try_lock(void) { return ovs_mutex_trylock(&seq_mutex); } void seq_lock(void) OVS_ACQUIRES(seq_mutex) { ovs_mutex_lock(&seq_mutex); } void seq_unlock(void) OVS_RELEASES(seq_mutex) { ovs_mutex_unlock(&seq_mutex); } /* Increments 'seq''s sequence number, waking up any threads that are waiting * on 'seq'. */ void seq_change_protected(struct seq *seq) OVS_REQUIRES(seq_mutex) { COVERAGE_INC(seq_change); seq->value = seq_next++; seq_wake_waiters(seq); } /* Increments 'seq''s sequence number, waking up any threads that are waiting * on 'seq'. */ void seq_change(struct seq *seq) OVS_EXCLUDED(seq_mutex) { ovs_mutex_lock(&seq_mutex); seq_change_protected(seq); ovs_mutex_unlock(&seq_mutex); } /* Returns 'seq''s current sequence number (which could change immediately). * * seq_read() and seq_wait() can be used together to yield a race-free wakeup * when an object changes, even without an ability to lock the object. See * Usage in seq.h for details. */ uint64_t seq_read_protected(const struct seq *seq) OVS_REQUIRES(seq_mutex) { return seq->value; } /* Returns 'seq''s current sequence number (which could change immediately). * * seq_read() and seq_wait() can be used together to yield a race-free wakeup * when an object changes, even without an ability to lock the object. See * Usage in seq.h for details. */ uint64_t seq_read(const struct seq *seq) OVS_EXCLUDED(seq_mutex) { uint64_t value; ovs_mutex_lock(&seq_mutex); value = seq_read_protected(seq); ovs_mutex_unlock(&seq_mutex); return value; } static void seq_wait__(struct seq *seq, uint64_t value, const char *where) OVS_REQUIRES(seq_mutex) { unsigned int id = ovsthread_id_self(); uint32_t hash = hash_int(id, 0); struct seq_waiter *waiter; HMAP_FOR_EACH_IN_BUCKET (waiter, hmap_node, hash, &seq->waiters) { if (waiter->ovsthread_id == id) { if (waiter->value != value) { /* The current value is different from the value we've already * waited for, */ poll_immediate_wake_at(where); } else { /* Already waiting on 'value', nothing more to do. */ } return; } } waiter = xmalloc(sizeof *waiter); waiter->seq = seq; hmap_insert(&seq->waiters, &waiter->hmap_node, hash); waiter->ovsthread_id = id; waiter->value = value; waiter->thread = seq_thread_get(); list_push_back(&waiter->thread->waiters, &waiter->list_node); if (!waiter->thread->waiting) { latch_wait_at(&waiter->thread->latch, where); waiter->thread->waiting = true; } } /* Causes the following poll_block() to wake up when 'seq''s sequence number * changes from 'value'. (If 'seq''s sequence number isn't 'value', then * poll_block() won't block at all.) * * seq_read() and seq_wait() can be used together to yield a race-free wakeup * when an object changes, even without an ability to lock the object. See * Usage in seq.h for details. * * ('where' is used in debug logging. Commonly one would use seq_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ void seq_wait_at(const struct seq *seq_, uint64_t value, const char *where) OVS_EXCLUDED(seq_mutex) { struct seq *seq = CONST_CAST(struct seq *, seq_); ovs_mutex_lock(&seq_mutex); if (value == seq->value) { seq_wait__(seq, value, where); } else { poll_immediate_wake_at(where); } ovs_mutex_unlock(&seq_mutex); } /* Called by poll_block() just before it returns, this function destroys any * seq_waiter objects associated with the current thread. */ void seq_woke(void) OVS_EXCLUDED(seq_mutex) { struct seq_thread *thread; seq_init(); thread = pthread_getspecific(seq_thread_key); if (thread) { ovs_mutex_lock(&seq_mutex); seq_thread_woke(thread); thread->waiting = false; ovs_mutex_unlock(&seq_mutex); } } static void seq_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { xpthread_key_create(&seq_thread_key, seq_thread_exit); ovsthread_once_done(&once); } } static struct seq_thread * seq_thread_get(void) OVS_REQUIRES(seq_mutex) { struct seq_thread *thread = pthread_getspecific(seq_thread_key); if (!thread) { thread = xmalloc(sizeof *thread); list_init(&thread->waiters); latch_init(&thread->latch); thread->waiting = false; xpthread_setspecific(seq_thread_key, thread); } return thread; } static void seq_thread_exit(void *thread_) OVS_EXCLUDED(seq_mutex) { struct seq_thread *thread = thread_; ovs_mutex_lock(&seq_mutex); seq_thread_woke(thread); latch_destroy(&thread->latch); free(thread); ovs_mutex_unlock(&seq_mutex); } static void seq_thread_woke(struct seq_thread *thread) OVS_REQUIRES(seq_mutex) { struct seq_waiter *waiter, *next_waiter; LIST_FOR_EACH_SAFE (waiter, next_waiter, list_node, &thread->waiters) { ovs_assert(waiter->thread == thread); seq_waiter_destroy(waiter); } latch_poll(&thread->latch); } static void seq_waiter_destroy(struct seq_waiter *waiter) OVS_REQUIRES(seq_mutex) { hmap_remove(&waiter->seq->waiters, &waiter->hmap_node); list_remove(&waiter->list_node); free(waiter); } static void seq_wake_waiters(struct seq *seq) OVS_REQUIRES(seq_mutex) { struct seq_waiter *waiter, *next_waiter; HMAP_FOR_EACH_SAFE (waiter, next_waiter, hmap_node, &seq->waiters) { latch_set(&waiter->thread->latch); seq_waiter_destroy(waiter); } } openvswitch-2.5.9/lib/PaxHeaders.82075/stream-ssl.h0000644000000000000000000000013213534540071016655 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.893854251 openvswitch-2.5.9/lib/stream-ssl.h0000644000175000017500000000373713534540071020355 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STREAM_SSL_H #define STREAM_SSL_H 1 #include bool stream_ssl_is_configured(void); void stream_ssl_set_private_key_file(const char *file_name); void stream_ssl_set_certificate_file(const char *file_name); void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap); void stream_ssl_set_peer_ca_cert_file(const char *file_name); void stream_ssl_set_key_and_cert(const char *private_key_file, const char *certificate_file); #define STREAM_SSL_LONG_OPTIONS \ {"private-key", required_argument, NULL, 'p'}, \ {"certificate", required_argument, NULL, 'c'}, \ {"ca-cert", required_argument, NULL, 'C'} #define STREAM_SSL_OPTION_HANDLERS \ case 'p': \ stream_ssl_set_private_key_file(optarg); \ break; \ \ case 'c': \ stream_ssl_set_certificate_file(optarg); \ break; \ \ case 'C': \ stream_ssl_set_ca_cert_file(optarg, false); \ break; #endif /* stream-ssl.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/daemon.c0000644000000000000000000000013213534540071016021 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.689852748 openvswitch-2.5.9/lib/daemon.c0000644000175000017500000000710513534540071017512 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "daemon.h" #include "daemon-private.h" #include #include #include #include "util.h" #include "ovs-thread.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(daemon); /* For each of the standard file descriptors, whether to replace it by * /dev/null (if false) or keep it for the daemon to use (if true). */ static bool save_fds[3]; /* Will daemonize() really detach? */ bool get_detach(void) { return detach; } /* If configured with set_pidfile() or set_detach(), creates the pid file and * detaches from the foreground session. */ void daemonize(void) { daemonize_start(false); daemonize_complete(); } /* Sets up a following call to daemonize() to create a pidfile named 'name'. * If 'name' begins with '/' (or contains ':' in windows), then it is treated * as an absolute path. Otherwise, it is taken relative to RUNDIR, * which is $(prefix)/var/run by default. * * If 'name' is null, then program_name followed by ".pid" is used. */ void set_pidfile(const char *name) { assert_single_threaded(); free(pidfile); pidfile = make_pidfile_name(name); } /* A daemon doesn't normally have any use for the file descriptors for stdin, * stdout, and stderr after it detaches. To keep these file descriptors from * e.g. holding an SSH session open, by default detaching replaces each of * these file descriptors by /dev/null. But a few daemons expect the user to * redirect stdout or stderr to a file, in which case it is desirable to keep * these file descriptors. This function, therefore, disables replacing 'fd' * by /dev/null when the daemon detaches. */ void daemon_save_fd(int fd) { ovs_assert(fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO); save_fds[fd] = true; } /* Returns a readable and writable fd for /dev/null, if successful, otherwise * a negative errno value. The caller must not close the returned fd (because * the same fd will be handed out to subsequent callers). */ static int get_null_fd(void) { static int null_fd; #ifndef _WIN32 char *device = "/dev/null"; #else char *device = "nul"; #endif if (!null_fd) { null_fd = open(device, O_RDWR); if (null_fd < 0) { int error = errno; VLOG_ERR("could not open %s: %s", device, ovs_strerror(error)); null_fd = -error; } } return null_fd; } /* Close standard file descriptors (except any that the client has requested we * leave open by calling daemon_save_fd()). If we're started from e.g. an SSH * session, then this keeps us from holding that session open artificially. */ void close_standard_fds(void) { int null_fd = get_null_fd(); if (null_fd >= 0) { int fd; for (fd = 0; fd < 3; fd++) { if (!save_fds[fd]) { dup2(null_fd, fd); } } } /* Disable logging to stderr to avoid wasting CPU time. */ vlog_set_levels(NULL, VLF_CONSOLE, VLL_OFF); } openvswitch-2.5.9/lib/PaxHeaders.82075/rtnetlink.c0000644000000000000000000000013213534540071016570 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.989854959 openvswitch-2.5.9/lib/rtnetlink.c0000644000175000017500000001337113534540071020263 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "rtnetlink.h" #include #include #include #include "netlink.h" #include "netlink-notifier.h" #include "ofpbuf.h" static struct nln *nln = NULL; static struct rtnetlink_change rtn_change; /* Returns true if the given netlink msg type corresponds to RTNLGRP_LINK. */ bool rtnetlink_type_is_rtnlgrp_link(uint16_t type) { return type == RTM_NEWLINK || type == RTM_DELLINK; } /* Returns true if the given netlink msg type corresponds to * RTNLGRP_IPV4_IFADDR or RTNLGRP_IPV6_IFADDR. */ bool rtnetlink_type_is_rtnlgrp_addr(uint16_t type) { return type == RTM_NEWADDR || type == RTM_DELADDR; } /* Parses a rtnetlink message 'buf' into 'change'. If 'buf' is unparseable, * leaves 'change' untouched and returns false. Otherwise, populates 'change' * and returns true. */ bool rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change) { const struct nlmsghdr *nlmsg = buf->data; bool parsed = false; if (rtnetlink_type_is_rtnlgrp_link(nlmsg->nlmsg_type)) { /* Policy for RTNLGRP_LINK messages. * * There are *many* more fields in these messages, but currently we * only care about these fields. */ static const struct nl_policy policy[] = { [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, [IFLA_MTU] = { .type = NL_A_U32, .optional = true }, [IFLA_ADDRESS] = { .type = NL_A_UNSPEC, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), policy, attrs, ARRAY_SIZE(policy)); if (parsed) { const struct ifinfomsg *ifinfo; ifinfo = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifinfo); change->nlmsg_type = nlmsg->nlmsg_type; change->if_index = ifinfo->ifi_index; change->ifname = nl_attr_get_string(attrs[IFLA_IFNAME]); change->ifi_flags = ifinfo->ifi_flags; change->master_ifindex = (attrs[IFLA_MASTER] ? nl_attr_get_u32(attrs[IFLA_MASTER]) : 0); change->mtu = (attrs[IFLA_MTU] ? nl_attr_get_u32(attrs[IFLA_MTU]) : 0); if (attrs[IFLA_ADDRESS] && nl_attr_get_size(attrs[IFLA_ADDRESS]) == ETH_ADDR_LEN) { memcpy(&change->mac, nl_attr_get(attrs[IFLA_ADDRESS]), ETH_ADDR_LEN); } else { memset(&change->mac, 0, ETH_ADDR_LEN); } } } else if (rtnetlink_type_is_rtnlgrp_addr(nlmsg->nlmsg_type)) { /* Policy for RTNLGRP_IPV4_IFADDR/RTNLGRP_IPV6_IFADDR messages. * * There are *many* more fields in these messages, but currently we * only care about these fields. */ static const struct nl_policy policy[] = { [IFA_LABEL] = { .type = NL_A_STRING, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; parsed = nl_policy_parse(buf, NLMSG_HDRLEN + sizeof(struct ifaddrmsg), policy, attrs, ARRAY_SIZE(policy)); if (parsed) { const struct ifaddrmsg *ifaddr; ifaddr = ofpbuf_at(buf, NLMSG_HDRLEN, sizeof *ifaddr); change->nlmsg_type = nlmsg->nlmsg_type; change->if_index = ifaddr->ifa_index; change->ifname = nl_attr_get_string(attrs[IFA_LABEL]); } } return parsed; } static bool rtnetlink_parse_cb(struct ofpbuf *buf, void *change) { return rtnetlink_parse(buf, change); } /* Registers 'cb' to be called with auxiliary data 'aux' with network device * change notifications. The notifier is stored in 'notifier', which the * caller must not modify or free. * * This is probably not the function that you want. You should probably be * using dpif_port_poll() or netdev_change_seq(), which unlike this function * are not Linux-specific. * * xxx Joins more multicast groups when needed. * * Returns an initialized nln_notifier if successful, NULL otherwise. */ struct nln_notifier * rtnetlink_notifier_create(rtnetlink_notify_func *cb, void *aux) { if (!nln) { nln = nln_create(NETLINK_ROUTE, RTNLGRP_LINK, rtnetlink_parse_cb, &rtn_change); } return nln_notifier_create(nln, (nln_notify_func *) cb, aux); } /* Destroys 'notifier', which must have previously been created with * rtnetlink_notifier_register(). */ void rtnetlink_notifier_destroy(struct nln_notifier *notifier) { nln_notifier_destroy(notifier); } /* Calls all of the registered notifiers, passing along any as-yet-unreported * netdev change events. */ void rtnetlink_run(void) { if (nln) { nln_run(nln); } } /* Causes poll_block() to wake up when network device change notifications are * ready. */ void rtnetlink_wait(void) { if (nln) { nln_wait(nln); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-rcu.c0000644000000000000000000000013113534540071016153 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 29 ctime=1567801424.82585375 openvswitch-2.5.9/lib/ovs-rcu.c0000644000175000017500000002426713534540071017655 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "ovs-rcu.h" #include "fatal-signal.h" #include "guarded-list.h" #include "list.h" #include "ovs-thread.h" #include "poll-loop.h" #include "seq.h" #include "timeval.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovs_rcu); struct ovsrcu_cb { void (*function)(void *aux); void *aux; }; struct ovsrcu_cbset { struct ovs_list list_node; struct ovsrcu_cb cbs[16]; int n_cbs; }; struct ovsrcu_perthread { struct ovs_list list_node; /* In global list. */ struct ovs_mutex mutex; uint64_t seqno; struct ovsrcu_cbset *cbset; char name[16]; /* This thread's name. */ }; static struct seq *global_seqno; static pthread_key_t perthread_key; static struct ovs_list ovsrcu_threads; static struct ovs_mutex ovsrcu_threads_mutex; static struct guarded_list flushed_cbsets; static struct seq *flushed_cbsets_seq; static void ovsrcu_init_module(void); static void ovsrcu_flush_cbset__(struct ovsrcu_perthread *, bool); static void ovsrcu_flush_cbset(struct ovsrcu_perthread *); static void ovsrcu_unregister__(struct ovsrcu_perthread *); static bool ovsrcu_call_postponed(void); static void *ovsrcu_postpone_thread(void *arg OVS_UNUSED); static struct ovsrcu_perthread * ovsrcu_perthread_get(void) { struct ovsrcu_perthread *perthread; ovsrcu_init_module(); perthread = pthread_getspecific(perthread_key); if (!perthread) { const char *name = get_subprogram_name(); perthread = xmalloc(sizeof *perthread); ovs_mutex_init(&perthread->mutex); perthread->seqno = seq_read(global_seqno); perthread->cbset = NULL; ovs_strlcpy(perthread->name, name[0] ? name : "main", sizeof perthread->name); ovs_mutex_lock(&ovsrcu_threads_mutex); list_push_back(&ovsrcu_threads, &perthread->list_node); ovs_mutex_unlock(&ovsrcu_threads_mutex); pthread_setspecific(perthread_key, perthread); } return perthread; } /* Indicates the end of a quiescent state. See "Details" near the top of * ovs-rcu.h. * * Quiescent states don't stack or nest, so this always ends a quiescent state * even if ovsrcu_quiesce_start() was called multiple times in a row. */ void ovsrcu_quiesce_end(void) { ovsrcu_perthread_get(); } static void ovsrcu_quiesced(void) { if (single_threaded()) { ovsrcu_call_postponed(); } else { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { ovs_thread_create("urcu", ovsrcu_postpone_thread, NULL); ovsthread_once_done(&once); } } } /* Indicates the beginning of a quiescent state. See "Details" near the top of * ovs-rcu.h. */ void ovsrcu_quiesce_start(void) { struct ovsrcu_perthread *perthread; ovsrcu_init_module(); perthread = pthread_getspecific(perthread_key); if (perthread) { pthread_setspecific(perthread_key, NULL); ovsrcu_unregister__(perthread); } ovsrcu_quiesced(); } /* Indicates a momentary quiescent state. See "Details" near the top of * ovs-rcu.h. * * Provides a full memory barrier via seq_change(). */ void ovsrcu_quiesce(void) { struct ovsrcu_perthread *perthread; perthread = ovsrcu_perthread_get(); perthread->seqno = seq_read(global_seqno); if (perthread->cbset) { ovsrcu_flush_cbset(perthread); } seq_change(global_seqno); ovsrcu_quiesced(); } int ovsrcu_try_quiesce(void) { struct ovsrcu_perthread *perthread; int ret = EBUSY; ovs_assert(!single_threaded()); perthread = ovsrcu_perthread_get(); if (!seq_try_lock()) { perthread->seqno = seq_read_protected(global_seqno); if (perthread->cbset) { ovsrcu_flush_cbset__(perthread, true); } seq_change_protected(global_seqno); seq_unlock(); ovsrcu_quiesced(); ret = 0; } return ret; } bool ovsrcu_is_quiescent(void) { ovsrcu_init_module(); return pthread_getspecific(perthread_key) == NULL; } void ovsrcu_synchronize(void) { unsigned int warning_threshold = 1000; uint64_t target_seqno; long long int start; if (single_threaded()) { return; } target_seqno = seq_read(global_seqno); ovsrcu_quiesce_start(); start = time_msec(); for (;;) { uint64_t cur_seqno = seq_read(global_seqno); struct ovsrcu_perthread *perthread; char stalled_thread[16]; unsigned int elapsed; bool done = true; ovs_mutex_lock(&ovsrcu_threads_mutex); LIST_FOR_EACH (perthread, list_node, &ovsrcu_threads) { if (perthread->seqno <= target_seqno) { ovs_strlcpy(stalled_thread, perthread->name, sizeof stalled_thread); done = false; break; } } ovs_mutex_unlock(&ovsrcu_threads_mutex); if (done) { break; } elapsed = time_msec() - start; if (elapsed >= warning_threshold) { VLOG_WARN("blocked %u ms waiting for %s to quiesce", elapsed, stalled_thread); warning_threshold *= 2; } poll_timer_wait_until(start + warning_threshold); seq_wait(global_seqno, cur_seqno); poll_block(); } ovsrcu_quiesce_end(); } /* Registers 'function' to be called, passing 'aux' as argument, after the * next grace period. * * The call is guaranteed to happen after the next time all participating * threads have quiesced at least once, but there is no quarantee that all * registered functions are called as early as possible, or that the functions * registered by different threads would be called in the order the * registrations took place. In particular, even if two threads provably * register a function each in a specific order, the functions may still be * called in the opposite order, depending on the timing of when the threads * call ovsrcu_quiesce(), how many functions they postpone, and when the * ovs-rcu thread happens to grab the functions to be called. * * All functions registered by a single thread are guaranteed to execute in the * registering order, however. * * This function is more conveniently called through the ovsrcu_postpone() * macro, which provides a type-safe way to allow 'function''s parameter to be * any pointer type. */ void ovsrcu_postpone__(void (*function)(void *aux), void *aux) { struct ovsrcu_perthread *perthread = ovsrcu_perthread_get(); struct ovsrcu_cbset *cbset; struct ovsrcu_cb *cb; cbset = perthread->cbset; if (!cbset) { cbset = perthread->cbset = xmalloc(sizeof *perthread->cbset); cbset->n_cbs = 0; } cb = &cbset->cbs[cbset->n_cbs++]; cb->function = function; cb->aux = aux; if (cbset->n_cbs >= ARRAY_SIZE(cbset->cbs)) { ovsrcu_flush_cbset(perthread); } } static bool ovsrcu_call_postponed(void) { struct ovsrcu_cbset *cbset; struct ovs_list cbsets; guarded_list_pop_all(&flushed_cbsets, &cbsets); if (list_is_empty(&cbsets)) { return false; } ovsrcu_synchronize(); LIST_FOR_EACH_POP (cbset, list_node, &cbsets) { struct ovsrcu_cb *cb; for (cb = cbset->cbs; cb < &cbset->cbs[cbset->n_cbs]; cb++) { cb->function(cb->aux); } free(cbset); } return true; } static void * ovsrcu_postpone_thread(void *arg OVS_UNUSED) { pthread_detach(pthread_self()); for (;;) { uint64_t seqno = seq_read(flushed_cbsets_seq); if (!ovsrcu_call_postponed()) { seq_wait(flushed_cbsets_seq, seqno); poll_block(); } } OVS_NOT_REACHED(); } static void ovsrcu_flush_cbset__(struct ovsrcu_perthread *perthread, bool protected) { struct ovsrcu_cbset *cbset = perthread->cbset; if (cbset) { guarded_list_push_back(&flushed_cbsets, &cbset->list_node, SIZE_MAX); perthread->cbset = NULL; if (protected) { seq_change_protected(flushed_cbsets_seq); } else { seq_change(flushed_cbsets_seq); } } } static void ovsrcu_flush_cbset(struct ovsrcu_perthread *perthread) { ovsrcu_flush_cbset__(perthread, false); } static void ovsrcu_unregister__(struct ovsrcu_perthread *perthread) { if (perthread->cbset) { ovsrcu_flush_cbset(perthread); } ovs_mutex_lock(&ovsrcu_threads_mutex); list_remove(&perthread->list_node); ovs_mutex_unlock(&ovsrcu_threads_mutex); ovs_mutex_destroy(&perthread->mutex); free(perthread); seq_change(global_seqno); } static void ovsrcu_thread_exit_cb(void *perthread) { ovsrcu_unregister__(perthread); } /* Cancels the callback to ovsrcu_thread_exit_cb(). * * Cancelling the call to the destructor during the main thread exit * is needed while using pthreads-win32 library in Windows. It has been * observed that in pthreads-win32, a call to the destructor during * main thread exit causes undefined behavior. */ static void ovsrcu_cancel_thread_exit_cb(void *aux OVS_UNUSED) { pthread_setspecific(perthread_key, NULL); } static void ovsrcu_init_module(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { global_seqno = seq_create(); xpthread_key_create(&perthread_key, ovsrcu_thread_exit_cb); fatal_signal_add_hook(ovsrcu_cancel_thread_exit_cb, NULL, NULL, true); list_init(&ovsrcu_threads); ovs_mutex_init(&ovsrcu_threads_mutex); guarded_list_init(&flushed_cbsets); flushed_cbsets_seq = seq_create(); ovsthread_once_done(&once); } } openvswitch-2.5.9/lib/PaxHeaders.82075/hmapx.c0000644000000000000000000000013213534540071015673 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.737853101 openvswitch-2.5.9/lib/hmapx.c0000644000175000017500000001120113534540071017354 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "hmapx.h" #include "hash.h" static struct hmapx_node * hmapx_find__(const struct hmapx *map, const void *data, size_t hash) { struct hmapx_node *node; HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash, &map->map) { if (node->data == data) { return node; } } return NULL; } static struct hmapx_node * hmapx_add__(struct hmapx *map, void *data, size_t hash) { struct hmapx_node *node = xmalloc(sizeof *node); node->data = data; hmap_insert(&map->map, &node->hmap_node, hash); return node; } /* Initializes 'map' as an empty set of pointers. */ void hmapx_init(struct hmapx *map) { hmap_init(&map->map); } /* Destroys 'map'. */ void hmapx_destroy(struct hmapx *map) { if (map) { hmapx_clear(map); hmap_destroy(&map->map); } } /* Initializes 'map' to contain the same pointers as 'orig'. */ void hmapx_clone(struct hmapx *map, const struct hmapx *orig) { struct hmapx_node *node; hmapx_init(map); HMAP_FOR_EACH (node, hmap_node, &orig->map) { hmapx_add__(map, node->data, node->hmap_node.hash); } } /* Exchanges the contents of 'a' and 'b'. */ void hmapx_swap(struct hmapx *a, struct hmapx *b) { hmap_swap(&a->map, &b->map); } /* Adjusts 'map' so that it is still valid after it has been moved around in * memory (e.g. due to realloc()). */ void hmapx_moved(struct hmapx *map) { hmap_moved(&map->map); } /* Returns true if 'map' contains no nodes, false if it contains at least one * node. */ bool hmapx_is_empty(const struct hmapx *map) { return hmap_is_empty(&map->map); } /* Returns the number of nodes in 'map'. */ size_t hmapx_count(const struct hmapx *map) { return hmap_count(&map->map); } /* Adds 'data' to 'map'. If 'data' is new, returns the new hmapx_node; * otherwise (if a 'data' already existed in 'map'), returns NULL. */ struct hmapx_node * hmapx_add(struct hmapx *map, void *data) { uint32_t hash = hash_pointer(data, 0); return (hmapx_find__(map, data, hash) ? NULL : hmapx_add__(map, data, hash)); } /* Adds 'data' to 'map'. Assert-fails if 'data' was already in 'map'. */ void hmapx_add_assert(struct hmapx *map, void *data) { bool added OVS_UNUSED = hmapx_add(map, data); ovs_assert(added); } /* Removes all of the nodes from 'map'. */ void hmapx_clear(struct hmapx *map) { struct hmapx_node *node, *next; HMAPX_FOR_EACH_SAFE (node, next, map) { hmapx_delete(map, node); } } /* Deletes 'node' from 'map' and frees 'node'. */ void hmapx_delete(struct hmapx *map, struct hmapx_node *node) { hmap_remove(&map->map, &node->hmap_node); free(node); } /* Searches for 'data' in 'map'. If found, deletes it and returns true. If * not found, returns false without modifying 'map'. */ bool hmapx_find_and_delete(struct hmapx *map, const void *data) { struct hmapx_node *node = hmapx_find(map, data); if (node) { hmapx_delete(map, node); } return node != NULL; } /* Searches for 'data' in 'map' and deletes it. Assert-fails if 'data' is not * in 'map'. */ void hmapx_find_and_delete_assert(struct hmapx *map, const void *data) { bool deleted OVS_UNUSED = hmapx_find_and_delete(map, data); ovs_assert(deleted); } /* Searches for 'data' in 'map'. Returns its node, if found, otherwise a null * pointer. */ struct hmapx_node * hmapx_find(const struct hmapx *map, const void *data) { return hmapx_find__(map, data, hash_pointer(data, 0)); } /* Returns true if 'map' contains 'data', false otherwise. */ bool hmapx_contains(const struct hmapx *map, const void *data) { return hmapx_find(map, data) != NULL; } /* Returns true if 'a' and 'b' contain the same pointers, false otherwise. */ bool hmapx_equals(const struct hmapx *a, const struct hmapx *b) { struct hmapx_node *node; if (hmapx_count(a) != hmapx_count(b)) { return false; } HMAP_FOR_EACH (node, hmap_node, &a->map) { if (!hmapx_find__(b, node->data, node->hmap_node.hash)) { return false; } } return true; } openvswitch-2.5.9/lib/PaxHeaders.82075/byteq.c0000644000000000000000000000013213534540071015702 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 30 ctime=1567801424.665852571 openvswitch-2.5.9/lib/byteq.c0000644000175000017500000001223113534540071017367 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "byteq.h" #include #include #include #include "util.h" /* Initializes 'q' as an empty byteq that uses the 'size' bytes of 'buffer' to * store data. 'size' must be a power of 2. * * The caller must ensure that 'buffer' remains available to the byteq as long * as 'q' is in use. */ void byteq_init(struct byteq *q, uint8_t *buffer, size_t size) { ovs_assert(is_pow2(size)); q->buffer = buffer; q->size = size; q->head = q->tail = 0; } /* Returns the number of bytes current queued in 'q'. */ int byteq_used(const struct byteq *q) { return q->head - q->tail; } /* Returns the number of bytes that can be added to 'q' without overflow. */ int byteq_avail(const struct byteq *q) { return q->size - byteq_used(q); } /* Returns true if no bytes are queued in 'q', * false if at least one byte is queued. */ bool byteq_is_empty(const struct byteq *q) { return !byteq_used(q); } /* Returns true if 'q' has no room to queue additional bytes, * false if 'q' has room for at least one more byte. */ bool byteq_is_full(const struct byteq *q) { return !byteq_avail(q); } /* Adds 'c' at the head of 'q', which must not be full. */ void byteq_put(struct byteq *q, uint8_t c) { ovs_assert(!byteq_is_full(q)); *byteq_head(q) = c; q->head++; } /* Adds the 'n' bytes in 'p' at the head of 'q', which must have at least 'n' * bytes of free space. */ void byteq_putn(struct byteq *q, const void *p_, size_t n) { const uint8_t *p = p_; ovs_assert(byteq_avail(q) >= n); while (n > 0) { size_t chunk = MIN(n, byteq_headroom(q)); memcpy(byteq_head(q), p, chunk); byteq_advance_head(q, chunk); p += chunk; n -= chunk; } } /* Appends null-terminated string 's' to the head of 'q', which must have * enough space. The null terminator is not added to 'q'. */ void byteq_put_string(struct byteq *q, const char *s) { byteq_putn(q, s, strlen(s)); } /* Removes a byte from the tail of 'q' and returns it. 'q' must not be * empty. */ uint8_t byteq_get(struct byteq *q) { uint8_t c; ovs_assert(!byteq_is_empty(q)); c = *byteq_tail(q); q->tail++; return c; } /* Writes as much of 'q' as possible to 'fd'. Returns 0 if 'q' is fully * drained by the write, otherwise a positive errno value (e.g. EAGAIN if a * socket or tty buffer filled up). */ int byteq_write(struct byteq *q, int fd) { while (!byteq_is_empty(q)) { ssize_t n = write(fd, byteq_tail(q), byteq_tailroom(q)); if (n > 0) { byteq_advance_tail(q, n); } else { ovs_assert(n < 0); return errno; } } return 0; } /* Reads as much possible from 'fd' into 'q'. Returns 0 if 'q' is completely * filled up by the read, EOF if end-of-file was reached before 'q' was filled, * and otherwise a positive errno value (e.g. EAGAIN if a socket or tty buffer * was drained). */ int byteq_read(struct byteq *q, int fd) { while (!byteq_is_full(q)) { ssize_t n = read(fd, byteq_head(q), byteq_headroom(q)); if (n > 0) { byteq_advance_head(q, n); } else { return !n ? EOF : errno; } } return 0; } /* Returns the number of contiguous bytes of in-use space starting at the tail * of 'q'. */ int byteq_tailroom(const struct byteq *q) { int used = byteq_used(q); int tail_to_end = q->size - (q->tail & (q->size - 1)); return MIN(used, tail_to_end); } /* Returns the first in-use byte of 'q', the point at which data is removed * from 'q'. */ const uint8_t * byteq_tail(const struct byteq *q) { return &q->buffer[q->tail & (q->size - 1)]; } /* Removes 'n' bytes from the tail of 'q', which must have at least 'n' bytes * of tailroom. */ void byteq_advance_tail(struct byteq *q, unsigned int n) { ovs_assert(byteq_tailroom(q) >= n); q->tail += n; } /* Returns the byte after the last in-use byte of 'q', the point at which new * data will be added to 'q'. */ uint8_t * byteq_head(struct byteq *q) { return &q->buffer[q->head & (q->size - 1)]; } /* Returns the number of contiguous bytes of free space starting at the head * of 'q'. */ int byteq_headroom(const struct byteq *q) { int avail = byteq_avail(q); int head_to_end = q->size - (q->head & (q->size - 1)); return MIN(avail, head_to_end); } /* Adds to 'q' the 'n' bytes after the last currently in-use byte of 'q'. 'q' * must have at least 'n' bytes of headroom. */ void byteq_advance_head(struct byteq *q, unsigned int n) { ovs_assert(byteq_headroom(q) >= n); q->head += n; } openvswitch-2.5.9/lib/PaxHeaders.82075/byte-order.h0000644000000000000000000000013213534540071016637 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 30 ctime=1567801424.665852571 openvswitch-2.5.9/lib/byte-order.h0000644000175000017500000001235013534540071020326 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2010, 2011, 2013, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BYTE_ORDER_H #define BYTE_ORDER_H 1 #include #include #include #include "openvswitch/types.h" #ifndef __CHECKER__ #ifndef _WIN32 static inline ovs_be64 htonll(uint64_t n) { return htonl(1) == 1 ? n : ((uint64_t) htonl(n) << 32) | htonl(n >> 32); } static inline uint64_t ntohll(ovs_be64 n) { return htonl(1) == 1 ? n : ((uint64_t) ntohl(n) << 32) | ntohl(n >> 32); } #endif /* _WIN32 */ #else /* Making sparse happy with these functions also makes them unreadable, so * don't bother to show it their implementations. */ ovs_be64 htonll(uint64_t); uint64_t ntohll(ovs_be64); #endif static inline ovs_be128 hton128(const ovs_u128 src) { ovs_be128 dst; dst.be64.hi = htonll(src.u64.hi); dst.be64.lo = htonll(src.u64.lo); return dst; } static inline ovs_u128 ntoh128(const ovs_be128 src) { ovs_u128 dst; dst.u64.hi = ntohll(src.be64.hi); dst.u64.lo = ntohll(src.be64.lo); return dst; } static inline uint32_t uint32_byteswap(uint32_t crc) { return (((crc & 0x000000ff) << 24) | ((crc & 0x0000ff00) << 8) | ((crc & 0x00ff0000) >> 8) | ((crc & 0xff000000) >> 24)); } /* These macros may substitute for htons(), htonl(), and htonll() in contexts * where function calls are not allowed, such as case labels. They should not * be used elsewhere because all of them evaluate their argument many times. */ #if defined(WORDS_BIGENDIAN) || __CHECKER__ #define CONSTANT_HTONS(VALUE) ((OVS_FORCE ovs_be16) ((VALUE) & 0xffff)) #define CONSTANT_HTONL(VALUE) ((OVS_FORCE ovs_be32) ((VALUE) & 0xffffffff)) #define CONSTANT_HTONLL(VALUE) \ ((OVS_FORCE ovs_be64) ((VALUE) & UINT64_C(0xffffffffffffffff))) #else #define CONSTANT_HTONS(VALUE) \ (((((ovs_be16) (VALUE)) & 0xff00) >> 8) | \ ((((ovs_be16) (VALUE)) & 0x00ff) << 8)) #define CONSTANT_HTONL(VALUE) \ (((((ovs_be32) (VALUE)) & 0x000000ff) << 24) | \ ((((ovs_be32) (VALUE)) & 0x0000ff00) << 8) | \ ((((ovs_be32) (VALUE)) & 0x00ff0000) >> 8) | \ ((((ovs_be32) (VALUE)) & 0xff000000) >> 24)) #define CONSTANT_HTONLL(VALUE) \ (((((ovs_be64) (VALUE)) & UINT64_C(0x00000000000000ff)) << 56) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x000000000000ff00)) << 40) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x0000000000ff0000)) << 24) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x00000000ff000000)) << 8) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x000000ff00000000)) >> 8) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x0000ff0000000000)) >> 24) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0x00ff000000000000)) >> 40) | \ ((((ovs_be64) (VALUE)) & UINT64_C(0xff00000000000000)) >> 56)) #endif #if WORDS_BIGENDIAN #define BYTES_TO_BE32(B1, B2, B3, B4) \ (OVS_FORCE ovs_be32)((uint32_t)(B1) << 24 | (B2) << 16 | (B3) << 8 | (B4)) #define BE16S_TO_BE32(B1, B2) \ (OVS_FORCE ovs_be32)((uint32_t)(B1) << 16 | (B2)) #else #define BYTES_TO_BE32(B1, B2, B3, B4) \ (OVS_FORCE ovs_be32)((uint32_t)(B1) | (B2) << 8 | (B3) << 16 | (B4) << 24) #define BE16S_TO_BE32(B1, B2) \ (OVS_FORCE ovs_be32)((uint32_t)(B1) | (B2) << 16) #endif /* These functions zero-extend big-endian values to longer ones, * or truncate long big-endian value to shorter ones. */ #ifndef __CHECKER__ #if WORDS_BIGENDIAN static inline ovs_be32 be16_to_be32(ovs_be16 x) { return x; } static inline ovs_be64 be16_to_be64(ovs_be16 x) { return x; } static inline ovs_be64 be32_to_be64(ovs_be32 x) { return x; } static inline ovs_be32 be64_to_be32(ovs_be64 x) { return x; } static inline ovs_be16 be64_to_be16(ovs_be64 x) { return x; } static inline ovs_be16 be32_to_be16(ovs_be32 x) { return x; } #else /* !WORDS_BIGENDIAN */ static inline ovs_be32 be16_to_be32(ovs_be16 x) { return (ovs_be32) x << 16; } static inline ovs_be64 be16_to_be64(ovs_be16 x) { return (ovs_be64) x << 48; } static inline ovs_be64 be32_to_be64(ovs_be32 x) { return (ovs_be64) x << 32; } static inline ovs_be32 be64_to_be32(ovs_be64 x) { return x >> 32; } static inline ovs_be16 be64_to_be16(ovs_be64 x) { return x >> 48; } static inline ovs_be16 be32_to_be16(ovs_be32 x) { return x >> 16; } #endif /* !WORDS_BIGENDIAN */ #else /* __CHECKER__ */ /* Making sparse happy with these functions also makes them unreadable, so * don't bother to show it their implementations. */ ovs_be32 be16_to_be32(ovs_be16); ovs_be64 be16_to_be64(ovs_be16); ovs_be64 be32_to_be64(ovs_be32); ovs_be32 be64_to_be32(ovs_be64); ovs_be16 be64_to_be16(ovs_be64); ovs_be16 be32_to_be16(ovs_be32); #endif #endif /* byte-order.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/getopt_long.c0000644000000000000000000000013213534540071017077 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.957854723 openvswitch-2.5.9/lib/getopt_long.c0000644000175000017500000002772613534540071020603 0ustar00jpettitjpettit00000000000000/*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(getopt_long); int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #define IGNORE_FIRST (*options == '-' || *options == '+') #define PRINT_ERROR ((opterr) && ((*options != ':') \ || (IGNORE_FIRST && options[1] != ':'))) #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) /* XXX: GNU ignores PC if *options == '-' */ #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') /* return values */ #define BADCH (int)'?' #define BADARG ((IGNORE_FIRST && options[1] == ':') \ || (*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" #define _DIAGASSERT(q) ovs_assert(q) #define warnx VLOG_WARN static int getopt_internal(int, char **, const char *); static int gcd(int, int); static void permute_args(int, int, int, char **); static const char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return b; } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char **nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; _DIAGASSERT(nargv != NULL); /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; nargv[pos] = nargv[cstart]; nargv[cstart] = swap; } } } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. * Returns -2 if -- is found (can be long option or end of options marker). */ static int getopt_internal(int nargc, char **nargv, const char *options) { char *oli; /* option letter list index */ int optchar; _DIAGASSERT(nargv != NULL); _DIAGASSERT(options != NULL); optarg = NULL; /* * XXX Some programs (like rsyncd) expect to be able to * XXX re-initialize optind to 0 and have getopt_long(3) * XXX properly function again. Work around this braindamage. */ if (optind == 0) optind = 1; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return -1; } if ((*(place = nargv[optind]) != '-') || (place[1] == '\0')) { /* found non-option */ place = EMSG; if (IN_ORDER) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return INORDER; } if (!PERMUTE) { /* * if no permutation wanted, stop parsing * at first non-option */ return -1; } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; if (place[1] && *++place == '-') { /* found "--" */ place++; return -2; } } if ((optchar = (int)*place++) == (int)':' || (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { /* option letter unknown or ':' */ if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return BADCH; } if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ /* XXX: what if no long options provided (called by getopt)? */ if (*place) return -2; if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return BADARG; } else /* white space */ place = nargv[optind]; /* * Handle -W arg the same as --arg (which causes getopt to * stop parsing). */ return -2; } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = CONST_CAST(char *, place); /* XXX: disable test for :: if PC? (GNU doesn't) */ else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return BADARG; } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return optchar; } #ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the real getopt] */ int getopt(nargc, nargv, options) int nargc; char * const *nargv; const char *options; { int retval; _DIAGASSERT(nargv != NULL); _DIAGASSERT(options != NULL); retval = getopt_internal(nargc, CONST_CAST(char **, nargv), options); if (retval == -2) { ++optind; /* * We found an option (--), so if we skipped non-options, * we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; retval = -1; } return retval; } #endif /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { int retval; #define IDENTICAL_INTERPRETATION(_x, _y) \ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ long_options[(_x)].flag == long_options[(_y)].flag && \ long_options[(_x)].val == long_options[(_y)].val) _DIAGASSERT(nargv != NULL); _DIAGASSERT(options != NULL); _DIAGASSERT(long_options != NULL); /* idx may be NULL */ retval = getopt_internal(nargc, CONST_CAST(char **, nargv), options); if (retval == -2) { char *current_argv, *has_equal; size_t current_argv_len; int i, ambiguous, match; current_argv = CONST_CAST(char *, place); match = -1; ambiguous = 0; optind++; place = EMSG; if (*current_argv == '\0') { /* found "--" */ /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, CONST_CAST(char **, nargv)); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return -1; } if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == (unsigned)current_argv_len) { /* exact match */ match = i; ambiguous = 0; break; } if (match == -1) /* partial match */ match = i; else if (!IDENTICAL_INTERPRETATION(i, match)) ambiguous = 1; } if (ambiguous) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return BADCH; } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of * flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return BADARG; } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use * next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' * indicates no error should be generated */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless * of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return BADARG; } } else { /* unknown option */ if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return BADCH; } if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; retval = 0; } else retval = long_options[match].val; if (idx) *idx = match; } return retval; #undef IDENTICAL_INTERPRETATION } openvswitch-2.5.9/lib/PaxHeaders.82075/unixctl-syn.man0000644000000000000000000000013213534540071017404 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801423.745845788 openvswitch-2.5.9/lib/unixctl-syn.man0000644000175000017500000000007613534540071021075 0ustar00jpettitjpettit00000000000000.IP "Runtime management options:" \fB\-\-unixctl=\fIsocket\fR openvswitch-2.5.9/lib/PaxHeaders.82075/nx-match.h0000644000000000000000000000013213534540071016302 xustar0030 mtime=1567801401.473681699 30 atime=1567801402.081686164 30 ctime=1567801424.785853456 openvswitch-2.5.9/lib/nx-match.h0000644000175000017500000001410713534540071017773 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NX_MATCH_H #define NX_MATCH_H 1 #include #include #include #include "compiler.h" #include "flow.h" #include "meta-flow.h" #include "ofp-errors.h" #include "openvswitch/types.h" struct ds; struct match; struct ofpact_reg_move; struct ofpact_reg_load; struct ofpact_stack; struct ofpbuf; struct nx_action_reg_load; struct nx_action_reg_move; /* Nicira Extended Match (NXM) flexible flow match helper functions. * * See include/openflow/nicira-ext.h for NXM specification. */ void mf_format_subfield(const struct mf_subfield *, struct ds *); char *mf_parse_subfield__(struct mf_subfield *sf, const char **s) OVS_WARN_UNUSED_RESULT; char *mf_parse_subfield(struct mf_subfield *, const char *s) OVS_WARN_UNUSED_RESULT; enum ofperr nx_pull_match(struct ofpbuf *, unsigned int match_len, struct match *, ovs_be64 *cookie, ovs_be64 *cookie_mask); enum ofperr nx_pull_match_loose(struct ofpbuf *, unsigned int match_len, struct match *, ovs_be64 *cookie, ovs_be64 *cookie_mask); enum ofperr oxm_pull_match(struct ofpbuf *, struct match *); enum ofperr oxm_pull_match_loose(struct ofpbuf *, struct match *); enum ofperr oxm_pull_field_array(const void *, size_t fields_len, struct field_array *); int nx_put_match(struct ofpbuf *, const struct match *, ovs_be64 cookie, ovs_be64 cookie_mask); int oxm_put_match(struct ofpbuf *, const struct match *, enum ofp_version); void oxm_format_field_array(struct ds *, const struct field_array *); int oxm_put_field_array(struct ofpbuf *, const struct field_array *, enum ofp_version version); /* Decoding and encoding OXM/NXM headers (just a field ID) or entries (a field * ID followed by a value and possibly a mask). */ enum ofperr nx_pull_entry(struct ofpbuf *, const struct mf_field **, union mf_value *value, union mf_value *mask); enum ofperr nx_pull_header(struct ofpbuf *, const struct mf_field **, bool *masked); void nxm_put__(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version, const void *value, const void *mask, size_t n_bytes); void nx_put_entry(struct ofpbuf *, enum mf_field_id, enum ofp_version, const union mf_value *value, const union mf_value *mask); void nx_put_header(struct ofpbuf *, enum mf_field_id, enum ofp_version, bool masked); /* NXM and OXM protocol headers values. * * These are often alternatives to nx_pull_entry/header() and * nx_put_entry/header() for decoding and encoding OXM/NXM. In those cases, * the nx_*() functions should be preferred because they can support the 64-bit * "experimenter" OXM format (even though it is not yet implemented). */ uint32_t mf_nxm_header(enum mf_field_id); const struct mf_field *mf_from_nxm_header(uint32_t nxm_header); char *nx_match_to_string(const uint8_t *, unsigned int match_len); char *oxm_match_to_string(const struct ofpbuf *, unsigned int match_len); int nx_match_from_string(const char *, struct ofpbuf *); int oxm_match_from_string(const char *, struct ofpbuf *); void nx_format_field_name(enum mf_field_id, enum ofp_version, struct ds *); char *nxm_parse_reg_move(struct ofpact_reg_move *, const char *) OVS_WARN_UNUSED_RESULT; void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *); enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *, const struct flow *); void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *, struct flow_wildcards *); void nxm_reg_load(const struct mf_subfield *, uint64_t src_data, struct flow *, struct flow_wildcards *); char *nxm_parse_stack_action(struct ofpact_stack *, const char *) OVS_WARN_UNUSED_RESULT; void nxm_format_stack_push(const struct ofpact_stack *, struct ds *); void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *); enum ofperr nxm_stack_push_check(const struct ofpact_stack *, const struct flow *); enum ofperr nxm_stack_pop_check(const struct ofpact_stack *, const struct flow *); void nxm_execute_stack_push(const struct ofpact_stack *, const struct flow *, struct flow_wildcards *, struct ofpbuf *); void nxm_execute_stack_pop(const struct ofpact_stack *, struct flow *, struct flow_wildcards *, struct ofpbuf *); ovs_be64 oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *, enum ofp_version); struct mf_bitmap oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, enum ofp_version); struct mf_bitmap oxm_writable_fields(void); struct mf_bitmap oxm_matchable_fields(void); struct mf_bitmap oxm_maskable_fields(void); /* Dealing with the 'ofs_nbits' members in several Nicira extensions. */ static inline ovs_be16 nxm_encode_ofs_nbits(int ofs, int n_bits) { return htons((ofs << 6) | (n_bits - 1)); } static inline int nxm_decode_ofs(ovs_be16 ofs_nbits) { return ntohs(ofs_nbits) >> 6; } static inline int nxm_decode_n_bits(ovs_be16 ofs_nbits) { return (ntohs(ofs_nbits) & 0x3f) + 1; } /* This is my guess at the length of a "typical" nx_match, for use in * predicting space requirements. */ #define NXM_TYPICAL_LEN 64 #endif /* nx-match.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dpif.c0000644000000000000000000000013213534540071015500 xustar0030 mtime=1567801401.381681023 30 atime=1567801402.073686105 30 ctime=1567801424.709852895 openvswitch-2.5.9/lib/dpif.c0000644000175000017500000015470513534540071017202 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dpif-provider.h" #include #include #include #include #include #include "coverage.h" #include "dpctl.h" #include "dp-packet.h" #include "dpif-netdev.h" #include "dynamic-string.h" #include "flow.h" #include "netdev.h" #include "netlink.h" #include "odp-execute.h" #include "odp-util.h" #include "ofp-errors.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" #include "route-table.h" #include "seq.h" #include "shash.h" #include "sset.h" #include "timeval.h" #include "tnl-neigh-cache.h" #include "tnl-ports.h" #include "util.h" #include "uuid.h" #include "valgrind.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(dpif); COVERAGE_DEFINE(dpif_destroy); COVERAGE_DEFINE(dpif_port_add); COVERAGE_DEFINE(dpif_port_del); COVERAGE_DEFINE(dpif_flow_flush); COVERAGE_DEFINE(dpif_flow_get); COVERAGE_DEFINE(dpif_flow_put); COVERAGE_DEFINE(dpif_flow_del); COVERAGE_DEFINE(dpif_execute); COVERAGE_DEFINE(dpif_purge); COVERAGE_DEFINE(dpif_execute_with_help); static const struct dpif_class *base_dpif_classes[] = { #if defined(__linux__) || defined(_WIN32) &dpif_netlink_class, #endif &dpif_netdev_class, }; struct registered_dpif_class { const struct dpif_class *dpif_class; int refcount; }; static struct shash dpif_classes = SHASH_INITIALIZER(&dpif_classes); static struct sset dpif_blacklist = SSET_INITIALIZER(&dpif_blacklist); /* Protects 'dpif_classes', including the refcount, and 'dpif_blacklist'. */ static struct ovs_mutex dpif_mutex = OVS_MUTEX_INITIALIZER; /* Rate limit for individual messages going to or from the datapath, output at * DBG level. This is very high because, if these are enabled, it is because * we really need to see them. */ static struct vlog_rate_limit dpmsg_rl = VLOG_RATE_LIMIT_INIT(600, 600); /* Not really much point in logging many dpif errors. */ static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5); static void log_flow_message(const struct dpif *dpif, int error, const char *operation, const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const ovs_u128 *ufid, const struct dpif_flow_stats *stats, const struct nlattr *actions, size_t actions_len); static void log_operation(const struct dpif *, const char *operation, int error); static bool should_log_flow_message(int error); static void log_flow_put_message(struct dpif *, const struct dpif_flow_put *, int error); static void log_flow_del_message(struct dpif *, const struct dpif_flow_del *, int error); static void log_execute_message(struct dpif *, const struct dpif_execute *, bool subexecute, int error); static void log_flow_get_message(const struct dpif *, const struct dpif_flow_get *, int error); /* Incremented whenever tnl route, arp, etc changes. */ struct seq *tnl_conf_seq; static void dp_initialize(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { int i; tnl_conf_seq = seq_create(); dpctl_unixctl_register(); tnl_port_map_init(); tnl_neigh_cache_init(); route_table_init(); for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) { dp_register_provider(base_dpif_classes[i]); } ovsthread_once_done(&once); } } static int dp_register_provider__(const struct dpif_class *new_class) { struct registered_dpif_class *registered_class; int error; if (sset_contains(&dpif_blacklist, new_class->type)) { VLOG_DBG("attempted to register blacklisted provider: %s", new_class->type); return EINVAL; } if (shash_find(&dpif_classes, new_class->type)) { VLOG_WARN("attempted to register duplicate datapath provider: %s", new_class->type); return EEXIST; } error = new_class->init ? new_class->init() : 0; if (error) { VLOG_WARN("failed to initialize %s datapath class: %s", new_class->type, ovs_strerror(error)); return error; } registered_class = xmalloc(sizeof *registered_class); registered_class->dpif_class = new_class; registered_class->refcount = 0; shash_add(&dpif_classes, new_class->type, registered_class); return 0; } /* Registers a new datapath provider. After successful registration, new * datapaths of that type can be opened using dpif_open(). */ int dp_register_provider(const struct dpif_class *new_class) { int error; ovs_mutex_lock(&dpif_mutex); error = dp_register_provider__(new_class); ovs_mutex_unlock(&dpif_mutex); return error; } /* Unregisters a datapath provider. 'type' must have been previously * registered and not currently be in use by any dpifs. After unregistration * new datapaths of that type cannot be opened using dpif_open(). */ static int dp_unregister_provider__(const char *type) { struct shash_node *node; struct registered_dpif_class *registered_class; node = shash_find(&dpif_classes, type); if (!node) { VLOG_WARN("attempted to unregister a datapath provider that is not " "registered: %s", type); return EAFNOSUPPORT; } registered_class = node->data; if (registered_class->refcount) { VLOG_WARN("attempted to unregister in use datapath provider: %s", type); return EBUSY; } shash_delete(&dpif_classes, node); free(registered_class); return 0; } /* Unregisters a datapath provider. 'type' must have been previously * registered and not currently be in use by any dpifs. After unregistration * new datapaths of that type cannot be opened using dpif_open(). */ int dp_unregister_provider(const char *type) { int error; dp_initialize(); ovs_mutex_lock(&dpif_mutex); error = dp_unregister_provider__(type); ovs_mutex_unlock(&dpif_mutex); return error; } /* Blacklists a provider. Causes future calls of dp_register_provider() with * a dpif_class which implements 'type' to fail. */ void dp_blacklist_provider(const char *type) { ovs_mutex_lock(&dpif_mutex); sset_add(&dpif_blacklist, type); ovs_mutex_unlock(&dpif_mutex); } /* Adds the types of all currently registered datapath providers to 'types'. * The caller must first initialize the sset. */ void dp_enumerate_types(struct sset *types) { struct shash_node *node; dp_initialize(); ovs_mutex_lock(&dpif_mutex); SHASH_FOR_EACH(node, &dpif_classes) { const struct registered_dpif_class *registered_class = node->data; sset_add(types, registered_class->dpif_class->type); } ovs_mutex_unlock(&dpif_mutex); } static void dp_class_unref(struct registered_dpif_class *rc) { ovs_mutex_lock(&dpif_mutex); ovs_assert(rc->refcount); rc->refcount--; ovs_mutex_unlock(&dpif_mutex); } static struct registered_dpif_class * dp_class_lookup(const char *type) { struct registered_dpif_class *rc; ovs_mutex_lock(&dpif_mutex); rc = shash_find_data(&dpif_classes, type); if (rc) { rc->refcount++; } ovs_mutex_unlock(&dpif_mutex); return rc; } /* Clears 'names' and enumerates the names of all known created datapaths with * the given 'type'. The caller must first initialize the sset. Returns 0 if * successful, otherwise a positive errno value. * * Some kinds of datapaths might not be practically enumerable. This is not * considered an error. */ int dp_enumerate_names(const char *type, struct sset *names) { struct registered_dpif_class *registered_class; const struct dpif_class *dpif_class; int error; dp_initialize(); sset_clear(names); registered_class = dp_class_lookup(type); if (!registered_class) { VLOG_WARN("could not enumerate unknown type: %s", type); return EAFNOSUPPORT; } dpif_class = registered_class->dpif_class; error = (dpif_class->enumerate ? dpif_class->enumerate(names, dpif_class) : 0); if (error) { VLOG_WARN("failed to enumerate %s datapaths: %s", dpif_class->type, ovs_strerror(error)); } dp_class_unref(registered_class); return error; } /* Parses 'datapath_name_', which is of the form [type@]name into its * component pieces. 'name' and 'type' must be freed by the caller. * * The returned 'type' is normalized, as if by dpif_normalize_type(). */ void dp_parse_name(const char *datapath_name_, char **name, char **type) { char *datapath_name = xstrdup(datapath_name_); char *separator; separator = strchr(datapath_name, '@'); if (separator) { *separator = '\0'; *type = datapath_name; *name = xstrdup(dpif_normalize_type(separator + 1)); } else { *name = datapath_name; *type = xstrdup(dpif_normalize_type(NULL)); } } static int do_open(const char *name, const char *type, bool create, struct dpif **dpifp) { struct dpif *dpif = NULL; int error; struct registered_dpif_class *registered_class; dp_initialize(); type = dpif_normalize_type(type); registered_class = dp_class_lookup(type); if (!registered_class) { VLOG_WARN("could not create datapath %s of unknown type %s", name, type); error = EAFNOSUPPORT; goto exit; } error = registered_class->dpif_class->open(registered_class->dpif_class, name, create, &dpif); if (!error) { ovs_assert(dpif->dpif_class == registered_class->dpif_class); } else { dp_class_unref(registered_class); } exit: *dpifp = error ? NULL : dpif; return error; } /* Tries to open an existing datapath named 'name' and type 'type'. Will fail * if no datapath with 'name' and 'type' exists. 'type' may be either NULL or * the empty string to specify the default system type. Returns 0 if * successful, otherwise a positive errno value. On success stores a pointer * to the datapath in '*dpifp', otherwise a null pointer. */ int dpif_open(const char *name, const char *type, struct dpif **dpifp) { return do_open(name, type, false, dpifp); } /* Tries to create and open a new datapath with the given 'name' and 'type'. * 'type' may be either NULL or the empty string to specify the default system * type. Will fail if a datapath with 'name' and 'type' already exists. * Returns 0 if successful, otherwise a positive errno value. On success * stores a pointer to the datapath in '*dpifp', otherwise a null pointer. */ int dpif_create(const char *name, const char *type, struct dpif **dpifp) { return do_open(name, type, true, dpifp); } /* Tries to open a datapath with the given 'name' and 'type', creating it if it * does not exist. 'type' may be either NULL or the empty string to specify * the default system type. Returns 0 if successful, otherwise a positive * errno value. On success stores a pointer to the datapath in '*dpifp', * otherwise a null pointer. */ int dpif_create_and_open(const char *name, const char *type, struct dpif **dpifp) { int error; error = dpif_create(name, type, dpifp); if (error == EEXIST || error == EBUSY) { error = dpif_open(name, type, dpifp); if (error) { VLOG_WARN("datapath %s already exists but cannot be opened: %s", name, ovs_strerror(error)); } } else if (error) { VLOG_WARN("failed to create datapath %s: %s", name, ovs_strerror(error)); } return error; } /* Closes and frees the connection to 'dpif'. Does not destroy the datapath * itself; call dpif_delete() first, instead, if that is desirable. */ void dpif_close(struct dpif *dpif) { if (dpif) { struct registered_dpif_class *rc; rc = shash_find_data(&dpif_classes, dpif->dpif_class->type); dpif_uninit(dpif, true); dp_class_unref(rc); } } /* Performs periodic work needed by 'dpif'. */ bool dpif_run(struct dpif *dpif) { if (dpif->dpif_class->run) { return dpif->dpif_class->run(dpif); } return false; } /* Arranges for poll_block() to wake up when dp_run() needs to be called for * 'dpif'. */ void dpif_wait(struct dpif *dpif) { if (dpif->dpif_class->wait) { dpif->dpif_class->wait(dpif); } } /* Returns the name of datapath 'dpif' prefixed with the type * (for use in log messages). */ const char * dpif_name(const struct dpif *dpif) { return dpif->full_name; } /* Returns the name of datapath 'dpif' without the type * (for use in device names). */ const char * dpif_base_name(const struct dpif *dpif) { return dpif->base_name; } /* Returns the type of datapath 'dpif'. */ const char * dpif_type(const struct dpif *dpif) { return dpif->dpif_class->type; } /* Returns the fully spelled out name for the given datapath 'type'. * * Normalized type string can be compared with strcmp(). Unnormalized type * string might be the same even if they have different spellings. */ const char * dpif_normalize_type(const char *type) { return type && type[0] ? type : "system"; } /* Destroys the datapath that 'dpif' is connected to, first removing all of its * ports. After calling this function, it does not make sense to pass 'dpif' * to any functions other than dpif_name() or dpif_close(). */ int dpif_delete(struct dpif *dpif) { int error; COVERAGE_INC(dpif_destroy); error = dpif->dpif_class->destroy(dpif); log_operation(dpif, "delete", error); return error; } /* Retrieves statistics for 'dpif' into 'stats'. Returns 0 if successful, * otherwise a positive errno value. */ int dpif_get_dp_stats(const struct dpif *dpif, struct dpif_dp_stats *stats) { int error = dpif->dpif_class->get_stats(dpif, stats); if (error) { memset(stats, 0, sizeof *stats); } log_operation(dpif, "get_stats", error); return error; } const char * dpif_port_open_type(const char *datapath_type, const char *port_type) { struct registered_dpif_class *rc; datapath_type = dpif_normalize_type(datapath_type); ovs_mutex_lock(&dpif_mutex); rc = shash_find_data(&dpif_classes, datapath_type); if (rc && rc->dpif_class->port_open_type) { port_type = rc->dpif_class->port_open_type(rc->dpif_class, port_type); } ovs_mutex_unlock(&dpif_mutex); return port_type; } /* Attempts to add 'netdev' as a port on 'dpif'. If 'port_nop' is * non-null and its value is not ODPP_NONE, then attempts to use the * value as the port number. * * If successful, returns 0 and sets '*port_nop' to the new port's port * number (if 'port_nop' is non-null). On failure, returns a positive * errno value and sets '*port_nop' to ODPP_NONE (if 'port_nop' is * non-null). */ int dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) { const char *netdev_name = netdev_get_name(netdev); odp_port_t port_no = ODPP_NONE; int error; COVERAGE_INC(dpif_port_add); if (port_nop) { port_no = *port_nop; } error = dpif->dpif_class->port_add(dpif, netdev, &port_no); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu32, dpif_name(dpif), netdev_name, port_no); } else { VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s", dpif_name(dpif), netdev_name, ovs_strerror(error)); port_no = ODPP_NONE; } if (port_nop) { *port_nop = port_no; } return error; } /* Attempts to remove 'dpif''s port number 'port_no'. Returns 0 if successful, * otherwise a positive errno value. */ int dpif_port_del(struct dpif *dpif, odp_port_t port_no) { int error; COVERAGE_INC(dpif_port_del); error = dpif->dpif_class->port_del(dpif, port_no); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: port_del(%"PRIu32")", dpif_name(dpif), port_no); } else { log_operation(dpif, "port_del", error); } return error; } /* Makes a deep copy of 'src' into 'dst'. */ void dpif_port_clone(struct dpif_port *dst, const struct dpif_port *src) { dst->name = xstrdup(src->name); dst->type = xstrdup(src->type); dst->port_no = src->port_no; } /* Frees memory allocated to members of 'dpif_port'. * * Do not call this function on a dpif_port obtained from * dpif_port_dump_next(): that function retains ownership of the data in the * dpif_port. */ void dpif_port_destroy(struct dpif_port *dpif_port) { free(dpif_port->name); free(dpif_port->type); } /* Checks if port named 'devname' exists in 'dpif'. If so, returns * true; otherwise, returns false. */ bool dpif_port_exists(const struct dpif *dpif, const char *devname) { int error = dpif->dpif_class->port_query_by_name(dpif, devname, NULL); if (error != 0 && error != ENOENT && error != ENODEV) { VLOG_WARN_RL(&error_rl, "%s: failed to query port %s: %s", dpif_name(dpif), devname, ovs_strerror(error)); } return !error; } /* Looks up port number 'port_no' in 'dpif'. On success, returns 0 and * initializes '*port' appropriately; on failure, returns a positive errno * value. * * The caller owns the data in 'port' and must free it with * dpif_port_destroy() when it is no longer needed. */ int dpif_port_query_by_number(const struct dpif *dpif, odp_port_t port_no, struct dpif_port *port) { int error = dpif->dpif_class->port_query_by_number(dpif, port_no, port); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu32" is device %s", dpif_name(dpif), port_no, port->name); } else { memset(port, 0, sizeof *port); VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu32": %s", dpif_name(dpif), port_no, ovs_strerror(error)); } return error; } /* Looks up port named 'devname' in 'dpif'. On success, returns 0 and * initializes '*port' appropriately; on failure, returns a positive errno * value. * * The caller owns the data in 'port' and must free it with * dpif_port_destroy() when it is no longer needed. */ int dpif_port_query_by_name(const struct dpif *dpif, const char *devname, struct dpif_port *port) { int error = dpif->dpif_class->port_query_by_name(dpif, devname, port); if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu32, dpif_name(dpif), devname, port->port_no); } else { memset(port, 0, sizeof *port); /* For ENOENT or ENODEV we use DBG level because the caller is probably * interested in whether 'dpif' actually has a port 'devname', so that * it's not an issue worth logging if it doesn't. Other errors are * uncommon and more likely to indicate a real problem. */ VLOG_RL(&error_rl, error == ENOENT || error == ENODEV ? VLL_DBG : VLL_WARN, "%s: failed to query port %s: %s", dpif_name(dpif), devname, ovs_strerror(error)); } return error; } /* Returns the Netlink PID value to supply in OVS_ACTION_ATTR_USERSPACE * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in * flows whose packets arrived on port 'port_no'. In the case where the * provider allocates multiple Netlink PIDs to a single port, it may use * 'hash' to spread load among them. The caller need not use a particular * hash function; a 5-tuple hash is suitable. * * (The datapath implementation might use some different hash function for * distributing packets received via flow misses among PIDs. This means * that packets received via flow misses might be reordered relative to * packets received via userspace actions. This is not ordinarily a * problem.) * * A 'port_no' of ODPP_NONE is a special case: it returns a reserved PID, not * allocated to any port, that the client may use for special purposes. * * The return value is only meaningful when DPIF_UC_ACTION has been enabled in * the 'dpif''s listen mask. It is allowed to change when DPIF_UC_ACTION is * disabled and then re-enabled, so a client that does that must be prepared to * update all of the flows that it installed that contain * OVS_ACTION_ATTR_USERSPACE actions. */ uint32_t dpif_port_get_pid(const struct dpif *dpif, odp_port_t port_no, uint32_t hash) { return (dpif->dpif_class->port_get_pid ? (dpif->dpif_class->port_get_pid)(dpif, port_no, hash) : 0); } /* Looks up port number 'port_no' in 'dpif'. On success, returns 0 and copies * the port's name into the 'name_size' bytes in 'name', ensuring that the * result is null-terminated. On failure, returns a positive errno value and * makes 'name' the empty string. */ int dpif_port_get_name(struct dpif *dpif, odp_port_t port_no, char *name, size_t name_size) { struct dpif_port port; int error; ovs_assert(name_size > 0); error = dpif_port_query_by_number(dpif, port_no, &port); if (!error) { ovs_strlcpy(name, port.name, name_size); dpif_port_destroy(&port); } else { *name = '\0'; } return error; } /* Initializes 'dump' to begin dumping the ports in a dpif. * * This function provides no status indication. An error status for the entire * dump operation is provided when it is completed by calling * dpif_port_dump_done(). */ void dpif_port_dump_start(struct dpif_port_dump *dump, const struct dpif *dpif) { dump->dpif = dpif; dump->error = dpif->dpif_class->port_dump_start(dpif, &dump->state); log_operation(dpif, "port_dump_start", dump->error); } /* Attempts to retrieve another port from 'dump', which must have been * initialized with dpif_port_dump_start(). On success, stores a new dpif_port * into 'port' and returns true. On failure, returns false. * * Failure might indicate an actual error or merely that the last port has been * dumped. An error status for the entire dump operation is provided when it * is completed by calling dpif_port_dump_done(). * * The dpif owns the data stored in 'port'. It will remain valid until at * least the next time 'dump' is passed to dpif_port_dump_next() or * dpif_port_dump_done(). */ bool dpif_port_dump_next(struct dpif_port_dump *dump, struct dpif_port *port) { const struct dpif *dpif = dump->dpif; if (dump->error) { return false; } dump->error = dpif->dpif_class->port_dump_next(dpif, dump->state, port); if (dump->error == EOF) { VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all ports", dpif_name(dpif)); } else { log_operation(dpif, "port_dump_next", dump->error); } if (dump->error) { dpif->dpif_class->port_dump_done(dpif, dump->state); return false; } return true; } /* Completes port table dump operation 'dump', which must have been initialized * with dpif_port_dump_start(). Returns 0 if the dump operation was * error-free, otherwise a positive errno value describing the problem. */ int dpif_port_dump_done(struct dpif_port_dump *dump) { const struct dpif *dpif = dump->dpif; if (!dump->error) { dump->error = dpif->dpif_class->port_dump_done(dpif, dump->state); log_operation(dpif, "port_dump_done", dump->error); } return dump->error == EOF ? 0 : dump->error; } /* Polls for changes in the set of ports in 'dpif'. If the set of ports in * 'dpif' has changed, this function does one of the following: * * - Stores the name of the device that was added to or deleted from 'dpif' in * '*devnamep' and returns 0. The caller is responsible for freeing * '*devnamep' (with free()) when it no longer needs it. * * - Returns ENOBUFS and sets '*devnamep' to NULL. * * This function may also return 'false positives', where it returns 0 and * '*devnamep' names a device that was not actually added or deleted or it * returns ENOBUFS without any change. * * Returns EAGAIN if the set of ports in 'dpif' has not changed. May also * return other positive errno values to indicate that something has gone * wrong. */ int dpif_port_poll(const struct dpif *dpif, char **devnamep) { int error = dpif->dpif_class->port_poll(dpif, devnamep); if (error) { *devnamep = NULL; } return error; } /* Arranges for the poll loop to wake up when port_poll(dpif) will return a * value other than EAGAIN. */ void dpif_port_poll_wait(const struct dpif *dpif) { dpif->dpif_class->port_poll_wait(dpif); } /* Extracts the flow stats for a packet. The 'flow' and 'packet' * arguments must have been initialized through a call to flow_extract(). * 'used' is stored into stats->used. */ void dpif_flow_stats_extract(const struct flow *flow, const struct dp_packet *packet, long long int used, struct dpif_flow_stats *stats) { stats->tcp_flags = ntohs(flow->tcp_flags); stats->n_bytes = dp_packet_size(packet); stats->n_packets = 1; stats->used = used; } /* Appends a human-readable representation of 'stats' to 's'. */ void dpif_flow_stats_format(const struct dpif_flow_stats *stats, struct ds *s) { ds_put_format(s, "packets:%"PRIu64", bytes:%"PRIu64", used:", stats->n_packets, stats->n_bytes); if (stats->used) { ds_put_format(s, "%.3fs", (time_msec() - stats->used) / 1000.0); } else { ds_put_format(s, "never"); } if (stats->tcp_flags) { ds_put_cstr(s, ", flags:"); packet_format_tcp_flags(s, stats->tcp_flags); } } /* Places the hash of the 'key_len' bytes starting at 'key' into '*hash'. */ void dpif_flow_hash(const struct dpif *dpif OVS_UNUSED, const void *key, size_t key_len, ovs_u128 *hash) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static uint32_t secret; if (ovsthread_once_start(&once)) { secret = random_uint32(); ovsthread_once_done(&once); } hash_bytes128(key, key_len, secret, hash); uuid_set_bits_v4((struct uuid *)hash); } /* Deletes all flows from 'dpif'. Returns 0 if successful, otherwise a * positive errno value. */ int dpif_flow_flush(struct dpif *dpif) { int error; COVERAGE_INC(dpif_flow_flush); error = dpif->dpif_class->flow_flush(dpif); log_operation(dpif, "flow_flush", error); return error; } /* Attempts to install 'key' into the datapath, fetches it, then deletes it. * Returns true if the datapath supported installing 'flow', false otherwise. */ bool dpif_probe_feature(struct dpif *dpif, const char *name, const struct ofpbuf *key, const ovs_u128 *ufid) { struct dpif_flow flow; struct ofpbuf reply; uint64_t stub[DPIF_FLOW_BUFSIZE / 8]; bool enable_feature = false; int error; /* Use DPIF_FP_MODIFY to cover the case where ovs-vswitchd is killed (and * restarted) at just the right time such that feature probes from the * previous run are still present in the datapath. */ error = dpif_flow_put(dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY | DPIF_FP_PROBE, key->data, key->size, NULL, 0, NULL, 0, ufid, PMD_ID_NULL, NULL); if (error) { if (error != EINVAL) { VLOG_WARN("%s: %s flow probe failed (%s)", dpif_name(dpif), name, ovs_strerror(error)); } return false; } ofpbuf_use_stack(&reply, &stub, sizeof stub); error = dpif_flow_get(dpif, key->data, key->size, ufid, PMD_ID_NULL, &reply, &flow); if (!error && (!ufid || (flow.ufid_present && ovs_u128_equals(ufid, &flow.ufid)))) { enable_feature = true; } error = dpif_flow_del(dpif, key->data, key->size, ufid, PMD_ID_NULL, NULL); if (error) { VLOG_WARN("%s: failed to delete %s feature probe flow", dpif_name(dpif), name); } return enable_feature; } /* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_GET. */ int dpif_flow_get(struct dpif *dpif, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, const unsigned pmd_id, struct ofpbuf *buf, struct dpif_flow *flow) { struct dpif_op *opp; struct dpif_op op; op.type = DPIF_OP_FLOW_GET; op.u.flow_get.key = key; op.u.flow_get.key_len = key_len; op.u.flow_get.ufid = ufid; op.u.flow_get.pmd_id = pmd_id; op.u.flow_get.buffer = buf; memset(flow, 0, sizeof *flow); op.u.flow_get.flow = flow; op.u.flow_get.flow->key = key; op.u.flow_get.flow->key_len = key_len; opp = &op; dpif_operate(dpif, &opp, 1); return op.error; } /* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_PUT. */ int dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags, const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const struct nlattr *actions, size_t actions_len, const ovs_u128 *ufid, const unsigned pmd_id, struct dpif_flow_stats *stats) { struct dpif_op *opp; struct dpif_op op; op.type = DPIF_OP_FLOW_PUT; op.u.flow_put.flags = flags; op.u.flow_put.key = key; op.u.flow_put.key_len = key_len; op.u.flow_put.mask = mask; op.u.flow_put.mask_len = mask_len; op.u.flow_put.actions = actions; op.u.flow_put.actions_len = actions_len; op.u.flow_put.ufid = ufid; op.u.flow_put.pmd_id = pmd_id; op.u.flow_put.stats = stats; opp = &op; dpif_operate(dpif, &opp, 1); return op.error; } /* A dpif_operate() wrapper for performing a single DPIF_OP_FLOW_DEL. */ int dpif_flow_del(struct dpif *dpif, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, const unsigned pmd_id, struct dpif_flow_stats *stats) { struct dpif_op *opp; struct dpif_op op; op.type = DPIF_OP_FLOW_DEL; op.u.flow_del.key = key; op.u.flow_del.key_len = key_len; op.u.flow_del.ufid = ufid; op.u.flow_del.pmd_id = pmd_id; op.u.flow_del.stats = stats; op.u.flow_del.terse = false; opp = &op; dpif_operate(dpif, &opp, 1); return op.error; } /* Creates and returns a new 'struct dpif_flow_dump' for iterating through the * flows in 'dpif'. If 'terse' is true, then only UFID and statistics will * be returned in the dump. Otherwise, all fields will be returned. * * This function always successfully returns a dpif_flow_dump. Error * reporting is deferred to dpif_flow_dump_destroy(). */ struct dpif_flow_dump * dpif_flow_dump_create(const struct dpif *dpif, bool terse) { return dpif->dpif_class->flow_dump_create(dpif, terse); } /* Destroys 'dump', which must have been created with dpif_flow_dump_create(). * All dpif_flow_dump_thread structures previously created for 'dump' must * previously have been destroyed. * * Returns 0 if the dump operation was error-free, otherwise a positive errno * value describing the problem. */ int dpif_flow_dump_destroy(struct dpif_flow_dump *dump) { const struct dpif *dpif = dump->dpif; int error = dpif->dpif_class->flow_dump_destroy(dump); log_operation(dpif, "flow_dump_destroy", error); return error == EOF ? 0 : error; } /* Returns new thread-local state for use with dpif_flow_dump_next(). */ struct dpif_flow_dump_thread * dpif_flow_dump_thread_create(struct dpif_flow_dump *dump) { return dump->dpif->dpif_class->flow_dump_thread_create(dump); } /* Releases 'thread'. */ void dpif_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread) { thread->dpif->dpif_class->flow_dump_thread_destroy(thread); } /* Attempts to retrieve up to 'max_flows' more flows from 'thread'. Returns 0 * if and only if no flows remained to be retrieved, otherwise a positive * number reflecting the number of elements in 'flows[]' that were updated. * The number of flows returned might be less than 'max_flows' because * fewer than 'max_flows' remained, because this particular datapath does not * benefit from batching, or because an error occurred partway through * retrieval. Thus, the caller should continue calling until a 0 return value, * even if intermediate return values are less than 'max_flows'. * * No error status is immediately provided. An error status for the entire * dump operation is provided when it is completed by calling * dpif_flow_dump_destroy(). * * All of the data stored into 'flows' is owned by the datapath, not by the * caller, and the caller must not modify or free it. The datapath guarantees * that it remains accessible and unchanged until the first of: * - The next call to dpif_flow_dump_next() for 'thread', or * - The next rcu quiescent period. */ int dpif_flow_dump_next(struct dpif_flow_dump_thread *thread, struct dpif_flow *flows, int max_flows) { struct dpif *dpif = thread->dpif; int n; ovs_assert(max_flows > 0); n = dpif->dpif_class->flow_dump_next(thread, flows, max_flows); if (n > 0) { struct dpif_flow *f; for (f = flows; f < &flows[n] && should_log_flow_message(0); f++) { log_flow_message(dpif, 0, "flow_dump", f->key, f->key_len, f->mask, f->mask_len, &f->ufid, &f->stats, f->actions, f->actions_len); } } else { VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif)); } return n; } struct dpif_execute_helper_aux { struct dpif *dpif; int error; }; /* This is called for actions that need the context of the datapath to be * meaningful. */ static void dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, const struct nlattr *action, bool may_steal OVS_UNUSED) { struct dpif_execute_helper_aux *aux = aux_; int type = nl_attr_type(action); struct dp_packet *packet = *packets; ovs_assert(cnt == 1); switch ((enum ovs_action_attr)type) { case OVS_ACTION_ATTR_CT: case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: { struct dpif_execute execute; struct ofpbuf execute_actions; uint64_t stub[256 / 8]; struct pkt_metadata *md = &packet->md; bool dst_set; dst_set = flow_tnl_dst_is_set(&md->tunnel); if (dst_set) { /* The Linux kernel datapath throws away the tunnel information * that we supply as metadata. We have to use a "set" action to * supply it. */ ofpbuf_use_stub(&execute_actions, stub, sizeof stub); odp_put_tunnel_action(&md->tunnel, &execute_actions); ofpbuf_put(&execute_actions, action, NLA_ALIGN(action->nla_len)); execute.actions = execute_actions.data; execute.actions_len = execute_actions.size; } else { execute.actions = action; execute.actions_len = NLA_ALIGN(action->nla_len); } execute.packet = packet; execute.needs_help = false; execute.probe = false; execute.mtu = 0; aux->error = dpif_execute(aux->dpif, &execute); log_execute_message(aux->dpif, &execute, true, aux->error); if (dst_set) { ofpbuf_uninit(&execute_actions); } break; } case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } } /* Executes 'execute' by performing most of the actions in userspace and * passing the fully constructed packets to 'dpif' for output and userspace * actions. * * This helps with actions that a given 'dpif' doesn't implement directly. */ static int dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute) { struct dpif_execute_helper_aux aux = {dpif, 0}; struct dp_packet *pp; COVERAGE_INC(dpif_execute_with_help); pp = execute->packet; odp_execute_actions(&aux, &pp, 1, false, execute->actions, execute->actions_len, dpif_execute_helper_cb); return aux.error; } /* Returns true if the datapath needs help executing 'execute'. */ static bool dpif_execute_needs_help(const struct dpif_execute *execute) { return execute->needs_help || nl_attr_oversized(execute->actions_len); } /* A dpif_operate() wrapper for performing a single DPIF_OP_EXECUTE. */ int dpif_execute(struct dpif *dpif, struct dpif_execute *execute) { if (execute->actions_len) { struct dpif_op *opp; struct dpif_op op; op.type = DPIF_OP_EXECUTE; op.u.execute = *execute; opp = &op; dpif_operate(dpif, &opp, 1); return op.error; } else { return 0; } } /* Executes each of the 'n_ops' operations in 'ops' on 'dpif', in the order in * which they are specified. Places each operation's results in the "output" * members documented in comments, and 0 in the 'error' member on success or a * positive errno on failure. */ void dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops) { while (n_ops > 0) { size_t chunk; /* Count 'chunk', the number of ops that can be executed without * needing any help. Ops that need help should be rare, so we * expect this to ordinarily be 'n_ops', that is, all the ops. */ for (chunk = 0; chunk < n_ops; chunk++) { struct dpif_op *op = ops[chunk]; if (op->type == DPIF_OP_EXECUTE && dpif_execute_needs_help(&op->u.execute)) { break; } } if (chunk) { /* Execute a chunk full of ops that the dpif provider can * handle itself, without help. */ size_t i; dpif->dpif_class->operate(dpif, ops, chunk); for (i = 0; i < chunk; i++) { struct dpif_op *op = ops[i]; int error = op->error; switch (op->type) { case DPIF_OP_FLOW_PUT: { struct dpif_flow_put *put = &op->u.flow_put; COVERAGE_INC(dpif_flow_put); log_flow_put_message(dpif, put, error); if (error && put->stats) { memset(put->stats, 0, sizeof *put->stats); } break; } case DPIF_OP_FLOW_GET: { struct dpif_flow_get *get = &op->u.flow_get; COVERAGE_INC(dpif_flow_get); if (error) { memset(get->flow, 0, sizeof *get->flow); } log_flow_get_message(dpif, get, error); break; } case DPIF_OP_FLOW_DEL: { struct dpif_flow_del *del = &op->u.flow_del; COVERAGE_INC(dpif_flow_del); log_flow_del_message(dpif, del, error); if (error && del->stats) { memset(del->stats, 0, sizeof *del->stats); } break; } case DPIF_OP_EXECUTE: COVERAGE_INC(dpif_execute); log_execute_message(dpif, &op->u.execute, false, error); break; } } ops += chunk; n_ops -= chunk; } else { /* Help the dpif provider to execute one op. */ struct dpif_op *op = ops[0]; COVERAGE_INC(dpif_execute); op->error = dpif_execute_with_help(dpif, &op->u.execute); ops++; n_ops--; } } } /* Returns a string that represents 'type', for use in log messages. */ const char * dpif_upcall_type_to_string(enum dpif_upcall_type type) { switch (type) { case DPIF_UC_MISS: return "miss"; case DPIF_UC_ACTION: return "action"; case DPIF_N_UC_TYPES: default: return ""; } } /* Enables or disables receiving packets with dpif_recv() on 'dpif'. Returns 0 * if successful, otherwise a positive errno value. * * Turning packet receive off and then back on may change the Netlink PID * assignments returned by dpif_port_get_pid(). If the client does this, it * must update all of the flows that have OVS_ACTION_ATTR_USERSPACE actions * using the new PID assignment. */ int dpif_recv_set(struct dpif *dpif, bool enable) { int error = 0; if (dpif->dpif_class->recv_set) { error = dpif->dpif_class->recv_set(dpif, enable); log_operation(dpif, "recv_set", error); } return error; } /* Refreshes the poll loops and Netlink sockets associated to each port, * when the number of upcall handlers (upcall receiving thread) is changed * to 'n_handlers' and receiving packets for 'dpif' is enabled by * recv_set(). * * Since multiple upcall handlers can read upcalls simultaneously from * 'dpif', each port can have multiple Netlink sockets, one per upcall * handler. So, handlers_set() is responsible for the following tasks: * * When receiving upcall is enabled, extends or creates the * configuration to support: * * - 'n_handlers' Netlink sockets for each port. * * - 'n_handlers' poll loops, one for each upcall handler. * * - registering the Netlink sockets for the same upcall handler to * the corresponding poll loop. * * Returns 0 if successful, otherwise a positive errno value. */ int dpif_handlers_set(struct dpif *dpif, uint32_t n_handlers) { int error = 0; if (dpif->dpif_class->handlers_set) { error = dpif->dpif_class->handlers_set(dpif, n_handlers); log_operation(dpif, "handlers_set", error); } return error; } void dpif_register_dp_purge_cb(struct dpif *dpif, dp_purge_callback *cb, void *aux) { if (dpif->dpif_class->register_dp_purge_cb) { dpif->dpif_class->register_dp_purge_cb(dpif, cb, aux); } } void dpif_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, void *aux) { if (dpif->dpif_class->register_upcall_cb) { dpif->dpif_class->register_upcall_cb(dpif, cb, aux); } } void dpif_enable_upcall(struct dpif *dpif) { if (dpif->dpif_class->enable_upcall) { dpif->dpif_class->enable_upcall(dpif); } } void dpif_disable_upcall(struct dpif *dpif) { if (dpif->dpif_class->disable_upcall) { dpif->dpif_class->disable_upcall(dpif); } } void dpif_print_packet(struct dpif *dpif, struct dpif_upcall *upcall) { if (!VLOG_DROP_DBG(&dpmsg_rl)) { struct ds flow; char *packet; packet = ofp_packet_to_string(dp_packet_data(&upcall->packet), dp_packet_size(&upcall->packet)); ds_init(&flow); odp_flow_key_format(upcall->key, upcall->key_len, &flow); VLOG_DBG("%s: %s upcall:\n%s\n%s", dpif_name(dpif), dpif_upcall_type_to_string(upcall->type), ds_cstr(&flow), packet); ds_destroy(&flow); free(packet); } } /* If 'dpif' creates its own I/O polling threads, refreshes poll threads * configuration. */ int dpif_poll_threads_set(struct dpif *dpif, unsigned int n_rxqs, const char *cmask) { int error = 0; if (dpif->dpif_class->poll_threads_set) { error = dpif->dpif_class->poll_threads_set(dpif, n_rxqs, cmask); if (error) { log_operation(dpif, "poll_threads_set", error); } } return error; } /* Polls for an upcall from 'dpif' for an upcall handler. Since there * there can be multiple poll loops, 'handler_id' is needed as index to * identify the corresponding poll loop. If successful, stores the upcall * into '*upcall', using 'buf' for storage. Should only be called if * 'recv_set' has been used to enable receiving packets from 'dpif'. * * 'upcall->key' and 'upcall->userdata' point into data in the caller-provided * 'buf', so their memory cannot be freed separately from 'buf'. * * The caller owns the data of 'upcall->packet' and may modify it. If * packet's headroom is exhausted as it is manipulated, 'upcall->packet' * will be reallocated. This requires the data of 'upcall->packet' to be * released with ofpbuf_uninit() before 'upcall' is destroyed. However, * when an error is returned, the 'upcall->packet' may be uninitialized * and should not be released. * * Returns 0 if successful, otherwise a positive errno value. Returns EAGAIN * if no upcall is immediately available. */ int dpif_recv(struct dpif *dpif, uint32_t handler_id, struct dpif_upcall *upcall, struct ofpbuf *buf) { int error = EAGAIN; if (dpif->dpif_class->recv) { error = dpif->dpif_class->recv(dpif, handler_id, upcall, buf); if (!error) { dpif_print_packet(dpif, upcall); } else if (error != EAGAIN) { log_operation(dpif, "recv", error); } } return error; } /* Discards all messages that would otherwise be received by dpif_recv() on * 'dpif'. */ void dpif_recv_purge(struct dpif *dpif) { COVERAGE_INC(dpif_purge); if (dpif->dpif_class->recv_purge) { dpif->dpif_class->recv_purge(dpif); } } /* Arranges for the poll loop for an upcall handler to wake up when 'dpif' * 'dpif' has a message queued to be received with the recv member * function. Since there can be multiple poll loops, 'handler_id' is * needed as index to identify the corresponding poll loop. */ void dpif_recv_wait(struct dpif *dpif, uint32_t handler_id) { if (dpif->dpif_class->recv_wait) { dpif->dpif_class->recv_wait(dpif, handler_id); } } /* * Return the datapath version. Caller is responsible for freeing * the string. */ char * dpif_get_dp_version(const struct dpif *dpif) { char *version = NULL; if (dpif->dpif_class->get_datapath_version) { version = dpif->dpif_class->get_datapath_version(); } return version; } /* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type' * and '*engine_id', respectively. */ void dpif_get_netflow_ids(const struct dpif *dpif, uint8_t *engine_type, uint8_t *engine_id) { *engine_type = dpif->netflow_engine_type; *engine_id = dpif->netflow_engine_id; } /* Translates OpenFlow queue ID 'queue_id' (in host byte order) into a priority * value used for setting packet priority. * On success, returns 0 and stores the priority into '*priority'. * On failure, returns a positive errno value and stores 0 into '*priority'. */ int dpif_queue_to_priority(const struct dpif *dpif, uint32_t queue_id, uint32_t *priority) { int error = (dpif->dpif_class->queue_to_priority ? dpif->dpif_class->queue_to_priority(dpif, queue_id, priority) : EOPNOTSUPP); if (error) { *priority = 0; } log_operation(dpif, "queue_to_priority", error); return error; } void dpif_init(struct dpif *dpif, const struct dpif_class *dpif_class, const char *name, uint8_t netflow_engine_type, uint8_t netflow_engine_id) { dpif->dpif_class = dpif_class; dpif->base_name = xstrdup(name); dpif->full_name = xasprintf("%s@%s", dpif_class->type, name); dpif->netflow_engine_type = netflow_engine_type; dpif->netflow_engine_id = netflow_engine_id; } /* Undoes the results of initialization. * * Normally this function only needs to be called from dpif_close(). * However, it may be called by providers due to an error on opening * that occurs after initialization. It this case dpif_close() would * never be called. */ void dpif_uninit(struct dpif *dpif, bool close) { char *base_name = dpif->base_name; char *full_name = dpif->full_name; if (close) { dpif->dpif_class->close(dpif); } free(base_name); free(full_name); } static void log_operation(const struct dpif *dpif, const char *operation, int error) { if (!error) { VLOG_DBG_RL(&dpmsg_rl, "%s: %s success", dpif_name(dpif), operation); } else if (ofperr_is_valid(error)) { VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)", dpif_name(dpif), operation, ofperr_get_name(error)); } else { VLOG_WARN_RL(&error_rl, "%s: %s failed (%s)", dpif_name(dpif), operation, ovs_strerror(error)); } } static enum vlog_level flow_message_log_level(int error) { /* If flows arrive in a batch, userspace may push down multiple * unique flow definitions that overlap when wildcards are applied. * Kernels that support flow wildcarding will reject these flows as * duplicates (EEXIST), so lower the log level to debug for these * types of messages. */ return (error && error != EEXIST) ? VLL_WARN : VLL_DBG; } static bool should_log_flow_message(int error) { return !vlog_should_drop(THIS_MODULE, flow_message_log_level(error), error ? &error_rl : &dpmsg_rl); } static void log_flow_message(const struct dpif *dpif, int error, const char *operation, const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const ovs_u128 *ufid, const struct dpif_flow_stats *stats, const struct nlattr *actions, size_t actions_len) { struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "%s: ", dpif_name(dpif)); if (error) { ds_put_cstr(&ds, "failed to "); } ds_put_format(&ds, "%s ", operation); if (error) { ds_put_format(&ds, "(%s) ", ovs_strerror(error)); } if (ufid) { odp_format_ufid(ufid, &ds); ds_put_cstr(&ds, " "); } odp_flow_format(key, key_len, mask, mask_len, NULL, &ds, true); if (stats) { ds_put_cstr(&ds, ", "); dpif_flow_stats_format(stats, &ds); } if (actions || actions_len) { ds_put_cstr(&ds, ", actions:"); format_odp_actions(&ds, actions, actions_len); } vlog(THIS_MODULE, flow_message_log_level(error), "%s", ds_cstr(&ds)); ds_destroy(&ds); } static void log_flow_put_message(struct dpif *dpif, const struct dpif_flow_put *put, int error) { if (should_log_flow_message(error) && !(put->flags & DPIF_FP_PROBE)) { struct ds s; ds_init(&s); ds_put_cstr(&s, "put"); if (put->flags & DPIF_FP_CREATE) { ds_put_cstr(&s, "[create]"); } if (put->flags & DPIF_FP_MODIFY) { ds_put_cstr(&s, "[modify]"); } if (put->flags & DPIF_FP_ZERO_STATS) { ds_put_cstr(&s, "[zero]"); } log_flow_message(dpif, error, ds_cstr(&s), put->key, put->key_len, put->mask, put->mask_len, put->ufid, put->stats, put->actions, put->actions_len); ds_destroy(&s); } } static void log_flow_del_message(struct dpif *dpif, const struct dpif_flow_del *del, int error) { if (should_log_flow_message(error)) { log_flow_message(dpif, error, "flow_del", del->key, del->key_len, NULL, 0, del->ufid, !error ? del->stats : NULL, NULL, 0); } } /* Logs that 'execute' was executed on 'dpif' and completed with errno 'error' * (0 for success). 'subexecute' should be true if the execution is a result * of breaking down a larger execution that needed help, false otherwise. * * * XXX In theory, the log message could be deceptive because this function is * called after the dpif_provider's '->execute' function, which is allowed to * modify execute->packet and execute->md. In practice, though: * * - dpif-netlink doesn't modify execute->packet or execute->md. * * - dpif-netdev does modify them but it is less likely to have problems * because it is built into ovs-vswitchd and cannot have version skew, * etc. * * It would still be better to avoid the potential problem. I don't know of a * good way to do that, though, that isn't expensive. */ static void log_execute_message(struct dpif *dpif, const struct dpif_execute *execute, bool subexecute, int error) { if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl)) && !execute->probe) { struct ds ds = DS_EMPTY_INITIALIZER; char *packet; uint64_t stub[1024 / 8]; struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub); packet = ofp_packet_to_string(dp_packet_data(execute->packet), dp_packet_size(execute->packet)); odp_key_from_pkt_metadata(&md, &execute->packet->md); ds_put_format(&ds, "%s: %sexecute ", dpif_name(dpif), (subexecute ? "sub-" : dpif_execute_needs_help(execute) ? "super-" : "")); format_odp_actions(&ds, execute->actions, execute->actions_len); if (error) { ds_put_format(&ds, " failed (%s)", ovs_strerror(error)); } ds_put_format(&ds, " on packet %s", packet); ds_put_format(&ds, " with metadata "); odp_flow_format(md.data, md.size, NULL, 0, NULL, &ds, true); ds_put_format(&ds, " mtu %d", execute->mtu); vlog(THIS_MODULE, error ? VLL_WARN : VLL_DBG, "%s", ds_cstr(&ds)); ds_destroy(&ds); free(packet); ofpbuf_uninit(&md); } } static void log_flow_get_message(const struct dpif *dpif, const struct dpif_flow_get *get, int error) { if (should_log_flow_message(error)) { log_flow_message(dpif, error, "flow_get", get->key, get->key_len, get->flow->mask, get->flow->mask_len, get->ufid, &get->flow->stats, get->flow->actions, get->flow->actions_len); } } bool dpif_supports_tnl_push_pop(const struct dpif *dpif) { return dpif_is_netdev(dpif); } openvswitch-2.5.9/lib/PaxHeaders.82075/sflow_poller.c0000644000000000000000000000013113534540071017264 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801425.013855136 openvswitch-2.5.9/lib/sflow_poller.c0000644000175000017500000001473413534540071020764 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #include "sflow_api.h" /*_________________--------------------------__________________ _________________ sfl_poller_init __________________ -----------------__________________________------------------ */ void sfl_poller_init(SFLPoller *poller, SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn) { /* copy the dsi in case it points to poller->dsi, which we are about to clear */ SFLDataSource_instance dsi = *pdsi; /* preserve the *nxt pointer too, in case we are resetting this poller and it is already part of the agent's linked list (thanks to Matt Woodly for pointing this out) */ SFLPoller *nxtPtr = poller->nxt; /* clear everything */ memset(poller, 0, sizeof(*poller)); /* restore the linked list ptr */ poller->nxt = nxtPtr; /* now copy in the parameters */ poller->agent = agent; poller->dsi = dsi; /* structure copy */ poller->magic = magic; poller->getCountersFn = getCountersFn; } /*_________________--------------------------__________________ _________________ reset __________________ -----------------__________________________------------------ */ static void reset(SFLPoller *poller) { SFLDataSource_instance dsi = poller->dsi; sfl_poller_init(poller, poller->agent, &dsi, poller->magic, poller->getCountersFn); } /*_________________---------------------------__________________ _________________ MIB access __________________ -----------------___________________________------------------ */ u_int32_t sfl_poller_get_sFlowCpReceiver(SFLPoller *poller) { return poller->sFlowCpReceiver; } void sfl_poller_set_sFlowCpReceiver(SFLPoller *poller, u_int32_t sFlowCpReceiver) { poller->sFlowCpReceiver = sFlowCpReceiver; if(sFlowCpReceiver == 0) reset(poller); else { /* retrieve and cache a direct pointer to my receiver */ poller->myReceiver = sfl_agent_getReceiver(poller->agent, poller->sFlowCpReceiver); } } u_int32_t sfl_poller_get_sFlowCpInterval(SFLPoller *poller) { return poller->sFlowCpInterval; } void sfl_poller_set_sFlowCpInterval(SFLPoller *poller, u_int32_t sFlowCpInterval) { poller->sFlowCpInterval = sFlowCpInterval; if(sFlowCpInterval) { /* Set the countersCountdown to be a randomly selected value between 1 and sFlowCpInterval. That way the counter polling will be desynchronised (on a 200-port switch, polling all the counters in one second could be harmful). In a large network, even this might not be ideal if time-synchroniziation between devices is close and counters are always polled on second boundaries. If 1000 different devices all send an sFlow datagram on the same second boundary it could result in an antisocial burst. However when counter-samples are packed into the export datagram they do not always result in that datagram being sent immediately. It is more likely that a subsequent packet-sample will be the one that triggers the datagram to be sent. The packet-sample events are not sychronized to any clock, so that results in excellent desynchronization (http://blog.sflow.com/2009/05/measurement-traffic.html). Another smoothing factor is that the tick() function called here is usually driven from a fairly "soft" polling loop rather than a hard real-time event. */ poller->countersCountdown = 1 + (random() % sFlowCpInterval); } else { /* Setting sFlowCpInterval to 0 disables counter polling altogether. Thanks to Andy Kitchingman for spotting this ommission. */ poller->countersCountdown = 0; } } /*_________________---------------------------------__________________ _________________ bridge port __________________ -----------------_________________________________------------------ May need a separate number to reference the local bridge port to get counters if it is not the same as the global ifIndex. */ void sfl_poller_set_bridgePort(SFLPoller *poller, u_int32_t port_no) { poller->bridgePort = port_no; } u_int32_t sfl_poller_get_bridgePort(SFLPoller *poller) { return poller->bridgePort; } /*_________________---------------------------------__________________ _________________ sequence number reset __________________ -----------------_________________________________------------------ Used to indicate a counter discontinuity so that the sflow collector will know to ignore the next delta. */ void sfl_poller_resetCountersSeqNo(SFLPoller *poller) { poller->countersSampleSeqNo = 0; } /*_________________---------------------------__________________ _________________ sfl_poller_tick __________________ -----------------___________________________------------------ */ void sfl_poller_tick(SFLPoller *poller, time_t now) { if(poller->countersCountdown == 0) return; /* counters retrieval was not enabled */ if(poller->sFlowCpReceiver == 0) return; if(--poller->countersCountdown == 0) { if(poller->getCountersFn != NULL) { /* call out for counters */ SFL_COUNTERS_SAMPLE_TYPE cs; memset(&cs, 0, sizeof(cs)); poller->getCountersFn(poller->magic, poller, &cs); /* this countersFn is expected to fill in some counter block elements and then call sfl_poller_writeCountersSample(poller, &cs); */ } /* reset the countdown */ poller->countersCountdown = poller->sFlowCpInterval; } } /*_________________---------------------------------__________________ _________________ sfl_poller_writeCountersSample __________________ -----------------_________________________________------------------ */ void sfl_poller_writeCountersSample(SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) { /* fill in the rest of the header fields, and send to the receiver */ cs->sequence_number = ++poller->countersSampleSeqNo; #ifdef SFL_USE_32BIT_INDEX cs->ds_class = SFL_DS_CLASS(poller->dsi); cs->ds_index = SFL_DS_INDEX(poller->dsi); #else cs->source_id = SFL_DS_DATASOURCE(poller->dsi); #endif /* sent to my receiver */ if(poller->myReceiver) sfl_receiver_writeCountersSample(poller->myReceiver, cs); } openvswitch-2.5.9/lib/PaxHeaders.82075/hmap.c0000644000000000000000000000013213534540071015503 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.733853073 openvswitch-2.5.9/lib/hmap.c0000644000175000017500000001774213534540071017204 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "hmap.h" #include #include #include "coverage.h" #include "random.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(hmap); COVERAGE_DEFINE(hmap_pathological); COVERAGE_DEFINE(hmap_expand); COVERAGE_DEFINE(hmap_shrink); COVERAGE_DEFINE(hmap_reserve); /* Initializes 'hmap' as an empty hash table. */ void hmap_init(struct hmap *hmap) { hmap->buckets = &hmap->one; hmap->one = NULL; hmap->mask = 0; hmap->n = 0; } /* Frees memory reserved by 'hmap'. It is the client's responsibility to free * the nodes themselves, if necessary. */ void hmap_destroy(struct hmap *hmap) { if (hmap && hmap->buckets != &hmap->one) { free(hmap->buckets); } } /* Removes all node from 'hmap', leaving it ready to accept more nodes. Does * not free memory allocated for 'hmap'. * * This function is appropriate when 'hmap' will soon have about as many * elements as it did before. If 'hmap' will likely have fewer elements than * before, use hmap_destroy() followed by hmap_init() to save memory and * iteration time. */ void hmap_clear(struct hmap *hmap) { if (hmap->n > 0) { hmap->n = 0; memset(hmap->buckets, 0, (hmap->mask + 1) * sizeof *hmap->buckets); } } /* Exchanges hash maps 'a' and 'b'. */ void hmap_swap(struct hmap *a, struct hmap *b) { struct hmap tmp = *a; *a = *b; *b = tmp; hmap_moved(a); hmap_moved(b); } /* Adjusts 'hmap' to compensate for having moved position in memory (e.g. due * to realloc()). */ void hmap_moved(struct hmap *hmap) { if (!hmap->mask) { hmap->buckets = &hmap->one; } } static void resize(struct hmap *hmap, size_t new_mask, const char *where) { struct hmap tmp; size_t i; ovs_assert(is_pow2(new_mask + 1)); hmap_init(&tmp); if (new_mask) { tmp.buckets = xmalloc(sizeof *tmp.buckets * (new_mask + 1)); tmp.mask = new_mask; for (i = 0; i <= tmp.mask; i++) { tmp.buckets[i] = NULL; } } for (i = 0; i <= hmap->mask; i++) { struct hmap_node *node, *next; int count = 0; for (node = hmap->buckets[i]; node; node = next) { next = node->next; hmap_insert_fast(&tmp, node, node->hash); count++; } if (count > 5) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); COVERAGE_INC(hmap_pathological); VLOG_DBG_RL(&rl, "%s: %d nodes in bucket (%"PRIuSIZE" nodes, %"PRIuSIZE" buckets)", where, count, hmap->n, hmap->mask + 1); } } hmap_swap(hmap, &tmp); hmap_destroy(&tmp); } static size_t calc_mask(size_t capacity) { size_t mask = capacity / 2; mask |= mask >> 1; mask |= mask >> 2; mask |= mask >> 4; mask |= mask >> 8; mask |= mask >> 16; #if SIZE_MAX > UINT32_MAX mask |= mask >> 32; #endif /* If we need to dynamically allocate buckets we might as well allocate at * least 4 of them. */ mask |= (mask & 1) << 1; return mask; } /* Expands 'hmap', if necessary, to optimize the performance of searches. * * ('where' is used in debug logging. Commonly one would use hmap_expand() to * automatically provide the caller's source file and line number for * 'where'.) */ void hmap_expand_at(struct hmap *hmap, const char *where) { size_t new_mask = calc_mask(hmap->n); if (new_mask > hmap->mask) { COVERAGE_INC(hmap_expand); resize(hmap, new_mask, where); } } /* Shrinks 'hmap', if necessary, to optimize the performance of iteration. * * ('where' is used in debug logging. Commonly one would use hmap_shrink() to * automatically provide the caller's source file and line number for * 'where'.) */ void hmap_shrink_at(struct hmap *hmap, const char *where) { size_t new_mask = calc_mask(hmap->n); if (new_mask < hmap->mask) { COVERAGE_INC(hmap_shrink); resize(hmap, new_mask, where); } } /* Expands 'hmap', if necessary, to optimize the performance of searches when * it has up to 'n' elements. (But iteration will be slow in a hash map whose * allocated capacity is much higher than its current number of nodes.) * * ('where' is used in debug logging. Commonly one would use hmap_reserve() to * automatically provide the caller's source file and line number for * 'where'.) */ void hmap_reserve_at(struct hmap *hmap, size_t n, const char *where) { size_t new_mask = calc_mask(n); if (new_mask > hmap->mask) { COVERAGE_INC(hmap_reserve); resize(hmap, new_mask, where); } } /* Adjusts 'hmap' to compensate for 'old_node' having moved position in memory * to 'node' (e.g. due to realloc()). */ void hmap_node_moved(struct hmap *hmap, struct hmap_node *old_node, struct hmap_node *node) { struct hmap_node **bucket = &hmap->buckets[node->hash & hmap->mask]; while (*bucket != old_node) { bucket = &(*bucket)->next; } *bucket = node; } /* Chooses and returns a randomly selected node from 'hmap', which must not be * empty. * * I wouldn't depend on this algorithm to be fair, since I haven't analyzed it. * But it does at least ensure that any node in 'hmap' can be chosen. */ struct hmap_node * hmap_random_node(const struct hmap *hmap) { struct hmap_node *bucket, *node; size_t n, i; /* Choose a random non-empty bucket. */ for (;;) { bucket = hmap->buckets[random_uint32() & hmap->mask]; if (bucket) { break; } } /* Count nodes in bucket. */ n = 0; for (node = bucket; node; node = node->next) { n++; } /* Choose random node from bucket. */ i = random_range(n); for (node = bucket; i-- > 0; node = node->next) { continue; } return node; } /* Returns the next node in 'hmap' in hash order, or NULL if no nodes remain in * 'hmap'. Uses '*bucketp' and '*offsetp' to determine where to begin * iteration, and stores new values to pass on the next iteration into them * before returning. * * It's better to use plain HMAP_FOR_EACH and related functions, since they are * faster and better at dealing with hmaps that change during iteration. * * Before beginning iteration, store 0 into '*bucketp' and '*offsetp'. */ struct hmap_node * hmap_at_position(const struct hmap *hmap, uint32_t *bucketp, uint32_t *offsetp) { size_t offset; size_t b_idx; offset = *offsetp; for (b_idx = *bucketp; b_idx <= hmap->mask; b_idx++) { struct hmap_node *node; size_t n_idx; for (n_idx = 0, node = hmap->buckets[b_idx]; node != NULL; n_idx++, node = node->next) { if (n_idx == offset) { if (node->next) { *bucketp = node->hash & hmap->mask; *offsetp = offset + 1; } else { *bucketp = (node->hash & hmap->mask) + 1; *offsetp = 0; } return node; } } offset = 0; } *bucketp = 0; *offsetp = 0; return NULL; } /* Returns true if 'node' is in 'hmap', false otherwise. */ bool hmap_contains(const struct hmap *hmap, const struct hmap_node *node) { struct hmap_node *p; for (p = hmap_first_in_bucket(hmap, node->hash); p; p = p->next) { if (p == node) { return true; } } return false; } openvswitch-2.5.9/lib/PaxHeaders.82075/syslog-libc.c0000644000000000000000000000013113534540071017004 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90585434 openvswitch-2.5.9/lib/syslog-libc.c0000644000175000017500000000411613534540071020475 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "syslog-libc.h" #include #include #include #include #include "compiler.h" #include "dynamic-string.h" #include "socket-util.h" #include "syslog-provider.h" #include "util.h" static void syslog_libc_open(struct syslogger *this, int facility); static void syslog_libc_log(struct syslogger *this, int pri, const char *msg); static struct syslog_class syslog_libc_class = { syslog_libc_open, syslog_libc_log, }; struct syslog_libc { struct syslogger parent; }; /* This function creates object that delegate all logging to libc's * syslog implementation. */ struct syslogger * syslog_libc_create(void) { struct syslog_libc *this = xmalloc(sizeof *this); this->parent.class = &syslog_libc_class; this->parent.prefix = "<%B> %D{%h %e %T} %E %A:"; return &this->parent; } static void syslog_libc_open(struct syslogger *this OVS_UNUSED, int facility) { static char *ident; /* openlog() is allowed to keep the pointer passed in, without making a * copy. The daemonize code sometimes frees and replaces * 'program_name', so make a private copy just for openlog(). (We keep * a pointer to the private copy to suppress memory leak warnings in * case openlog() does make its own copy.) */ ident = program_name ? xstrdup(program_name) : NULL; openlog(ident, LOG_NDELAY, facility); } static void syslog_libc_log(struct syslogger *this OVS_UNUSED, int pri, const char *msg) { syslog(pri, "%s", msg); } openvswitch-2.5.9/lib/PaxHeaders.82075/socket-util.c0000644000000000000000000000013213534540071017021 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.881854163 openvswitch-2.5.9/lib/socket-util.c0000644000175000017500000006756013534540071020525 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "socket-util.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dynamic-string.h" #include "ovs-thread.h" #include "packets.h" #include "poll-loop.h" #include "util.h" #include "openvswitch/vlog.h" #ifdef __linux__ #include #endif #ifdef HAVE_NETLINK #include "netlink-protocol.h" #include "netlink-socket.h" #endif VLOG_DEFINE_THIS_MODULE(socket_util); static int getsockopt_int(int fd, int level, int option, const char *optname, int *valuep); /* Sets 'fd' to non-blocking mode. Returns 0 if successful, otherwise a * positive errno value. */ int set_nonblocking(int fd) { #ifndef _WIN32 int flags = fcntl(fd, F_GETFL, 0); if (flags != -1) { if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1) { return 0; } else { VLOG_ERR("fcntl(F_SETFL) failed: %s", ovs_strerror(errno)); return errno; } } else { VLOG_ERR("fcntl(F_GETFL) failed: %s", ovs_strerror(errno)); return errno; } #else unsigned long arg = 1; if (ioctlsocket(fd, FIONBIO, &arg)) { int error = sock_errno(); VLOG_ERR("set_nonblocking failed: %s", sock_strerror(error)); return error; } return 0; #endif } void xset_nonblocking(int fd) { if (set_nonblocking(fd)) { exit(EXIT_FAILURE); } } void setsockopt_tcp_nodelay(int fd) { int on = 1; int retval; retval = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof on); if (retval) { retval = sock_errno(); VLOG_ERR("setsockopt(TCP_NODELAY): %s", sock_strerror(retval)); } } /* Sets the DSCP value of socket 'fd' to 'dscp', which must be 63 or less. * 'family' must indicate the socket's address family (AF_INET or AF_INET6, to * do anything useful). */ int set_dscp(int fd, int family, uint8_t dscp) { int retval; int val; #ifdef _WIN32 /* XXX: Consider using QoS2 APIs for Windows to set dscp. */ return 0; #endif if (dscp > 63) { return EINVAL; } val = dscp << 2; switch (family) { case AF_INET: retval = setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val); break; case AF_INET6: retval = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof val); break; default: return ENOPROTOOPT; } return retval ? sock_errno() : 0; } /* Checks whether 'host_name' is an IPv4 or IPv6 address. It is assumed * that 'host_name' is valid. Returns false if it is IPv4 address, true if * it is IPv6 address. */ bool addr_is_ipv6(const char *host_name) { return strchr(host_name, ':') != NULL; } /* Translates 'host_name', which must be a string representation of an IP * address, into a numeric IP address in '*addr'. Returns 0 if successful, * otherwise a positive errno value. */ int lookup_ip(const char *host_name, struct in_addr *addr) { if (!inet_pton(AF_INET, host_name, addr)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name); return ENOENT; } return 0; } /* Translates 'host_name', which must be a string representation of an IPv6 * address, into a numeric IPv6 address in '*addr'. Returns 0 if successful, * otherwise a positive errno value. */ int lookup_ipv6(const char *host_name, struct in6_addr *addr) { if (inet_pton(AF_INET6, host_name, addr) != 1) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "\"%s\" is not a valid IPv6 address", host_name); return ENOENT; } return 0; } /* Translates 'host_name', which must be a host name or a string representation * of an IP address, into a numeric IP address in '*addr'. Returns 0 if * successful, otherwise a positive errno value. * * Most Open vSwitch code should not use this because it causes deadlocks: * getaddrinfo() sends out a DNS request but that starts a new flow for which * OVS must set up a flow, but it can't because it's waiting for a DNS reply. * The synchronous lookup also delays other activity. (Of course we can solve * this but it doesn't seem worthwhile quite yet.) */ int lookup_hostname(const char *host_name, struct in_addr *addr) { struct addrinfo *result; struct addrinfo hints; if (inet_pton(AF_INET, host_name, addr)) { return 0; } memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; switch (getaddrinfo(host_name, NULL, &hints, &result)) { case 0: *addr = ALIGNED_CAST(struct sockaddr_in *, result->ai_addr)->sin_addr; freeaddrinfo(result); return 0; #ifdef EAI_ADDRFAMILY case EAI_ADDRFAMILY: #endif case EAI_NONAME: case EAI_SERVICE: return ENOENT; case EAI_AGAIN: return EAGAIN; case EAI_BADFLAGS: case EAI_FAMILY: case EAI_SOCKTYPE: return EINVAL; case EAI_FAIL: return EIO; case EAI_MEMORY: return ENOMEM; #if defined (EAI_NODATA) && EAI_NODATA != EAI_NONAME case EAI_NODATA: return ENXIO; #endif #ifdef EAI_SYSTEM case EAI_SYSTEM: return sock_errno(); #endif default: return EPROTO; } } int check_connection_completion(int fd) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10); struct pollfd pfd; int retval; pfd.fd = fd; pfd.events = POLLOUT; #ifndef _WIN32 do { retval = poll(&pfd, 1, 0); } while (retval < 0 && errno == EINTR); #else retval = WSAPoll(&pfd, 1, 0); #endif if (retval == 1) { if (pfd.revents & POLLERR) { ssize_t n = send(fd, "", 1, 0); if (n < 0) { return sock_errno(); } else { VLOG_ERR_RL(&rl, "poll return POLLERR but send succeeded"); return EPROTO; } } return 0; } else if (retval < 0) { VLOG_ERR_RL(&rl, "poll: %s", sock_strerror(sock_errno())); return errno; } else { return EAGAIN; } } /* Returns the size of socket 'sock''s receive buffer (SO_RCVBUF), or a * negative errno value if an error occurs. */ int get_socket_rcvbuf(int sock) { int rcvbuf; int error; error = getsockopt_int(sock, SOL_SOCKET, SO_RCVBUF, "SO_RCVBUF", &rcvbuf); return error ? -error : rcvbuf; } /* Reads and discards up to 'n' datagrams from 'fd', stopping as soon as no * more data can be immediately read. ('fd' should therefore be in * non-blocking mode.)*/ void drain_fd(int fd, size_t n_packets) { for (; n_packets > 0; n_packets--) { /* 'buffer' only needs to be 1 byte long in most circumstances. This * size is defensive against the possibility that we someday want to * use a Linux tap device without TUN_NO_PI, in which case a buffer * smaller than sizeof(struct tun_pi) will give EINVAL on read. */ char buffer[128]; if (read(fd, buffer, sizeof buffer) <= 0) { break; } } } ovs_be32 guess_netmask(ovs_be32 ip_) { uint32_t ip = ntohl(ip_); return ((ip >> 31) == 0 ? htonl(0xff000000) /* Class A */ : (ip >> 30) == 2 ? htonl(0xffff0000) /* Class B */ : (ip >> 29) == 6 ? htonl(0xffffff00) /* Class C */ : htonl(0)); /* ??? */ } /* This is like strsep() except: * * - The separator string is ":". * * - Square brackets [] quote ":" separators and are removed from the * tokens. */ static char * parse_bracketed_token(char **pp) { char *p = *pp; if (p == NULL) { return NULL; } else if (*p == '\0') { *pp = NULL; return p; } else if (*p == '[') { char *start = p + 1; char *end = start + strcspn(start, "]"); *pp = (*end == '\0' ? NULL : end[1] == ':' ? end + 2 : end + 1); *end = '\0'; return start; } else { char *start = p; char *end = start + strcspn(start, ":"); *pp = *end == '\0' ? NULL : end + 1; *end = '\0'; return start; } } static bool parse_sockaddr_components(struct sockaddr_storage *ss, const char *host_s, const char *port_s, uint16_t default_port, const char *s) { struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, ss); int port; if (port_s && port_s[0]) { if (!str_to_int(port_s, 10, &port) || port < 0 || port > 65535) { VLOG_ERR("%s: bad port number \"%s\"", s, port_s); } } else { port = default_port; } memset(ss, 0, sizeof *ss); if (strchr(host_s, ':')) { struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *, ss); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); if (!inet_pton(AF_INET6, host_s, sin6->sin6_addr.s6_addr)) { VLOG_ERR("%s: bad IPv6 address \"%s\"", s, host_s); goto exit; } } else { sin->sin_family = AF_INET; sin->sin_port = htons(port); if (!inet_pton(AF_INET, host_s, &sin->sin_addr.s_addr)) { VLOG_ERR("%s: bad IPv4 address \"%s\"", s, host_s); goto exit; } } return true; exit: memset(ss, 0, sizeof *ss); return false; } /* Parses 'target', which should be a string in the format "[:]". * , which is required, may be an IPv4 address or an IPv6 address * enclosed in square brackets. If 'default_port' is nonzero then is * optional and defaults to 'default_port'. * * On success, returns true and stores the parsed remote address into '*ss'. * On failure, logs an error, stores zeros into '*ss', and returns false. */ bool inet_parse_active(const char *target_, uint16_t default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); const char *port; const char *host; char *p; bool ok; p = target; host = parse_bracketed_token(&p); port = parse_bracketed_token(&p); if (!host) { VLOG_ERR("%s: host must be specified", target_); ok = false; } else if (!port && !default_port) { VLOG_ERR("%s: port must be specified", target_); ok = false; } else { ok = parse_sockaddr_components(ss, host, port, default_port, target_); } if (!ok) { memset(ss, 0, sizeof *ss); } free(target); return ok; } /* Opens a non-blocking IPv4 or IPv6 socket of the specified 'style' and * connects to 'target', which should be a string in the format * "[:]". , which is required, may be an IPv4 address or an * IPv6 address enclosed in square brackets. If 'default_port' is nonzero then * is optional and defaults to 'default_port'. * * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). * * On success, returns 0 (indicating connection complete) or EAGAIN (indicating * connection in progress), in which case the new file descriptor is stored * into '*fdp'. On failure, returns a positive errno value other than EAGAIN * and stores -1 into '*fdp'. * * If 'ss' is non-null, then on success stores the target address into '*ss'. * * 'dscp' becomes the DSCP bits in the IP headers for the new connection. It * should be in the range [0, 63] and will automatically be shifted to the * appropriately place in the IP tos field. */ int inet_open_active(int style, const char *target, uint16_t default_port, struct sockaddr_storage *ssp, int *fdp, uint8_t dscp) { struct sockaddr_storage ss; int fd = -1; int error; /* Parse. */ if (!inet_parse_active(target, default_port, &ss)) { error = EAFNOSUPPORT; goto exit; } /* Create non-blocking socket. */ fd = socket(ss.ss_family, style, 0); if (fd < 0) { error = sock_errno(); VLOG_ERR("%s: socket: %s", target, sock_strerror(error)); goto exit; } error = set_nonblocking(fd); if (error) { goto exit; } /* The dscp bits must be configured before connect() to ensure that the * TOS field is set during the connection establishment. If set after * connect(), the handshake SYN frames will be sent with a TOS of 0. */ error = set_dscp(fd, ss.ss_family, dscp); if (error) { VLOG_ERR("%s: set_dscp: %s", target, sock_strerror(error)); goto exit; } /* Connect. */ error = connect(fd, (struct sockaddr *) &ss, ss_length(&ss)) == 0 ? 0 : sock_errno(); if (error == EINPROGRESS #ifdef _WIN32 || error == WSAEALREADY || error == WSAEWOULDBLOCK #endif ) { error = EAGAIN; } exit: if (error && error != EAGAIN) { if (ssp) { memset(ssp, 0, sizeof *ssp); } if (fd >= 0) { closesocket(fd); fd = -1; } } else { if (ssp) { *ssp = ss; } } *fdp = fd; return error; } /* Parses 'target', which should be a string in the format "[][:]": * * - If 'default_port' is -1, then is required. Otherwise, if * is omitted, then 'default_port' is used instead. * * - If (or 'default_port', if used) is 0, then no port is bound * and the TCP/IP stack will select a port. * * - is optional. If supplied, it may be an IPv4 address or an * IPv6 address enclosed in square brackets. If omitted, the IP address * is wildcarded. * * If successful, stores the address into '*ss' and returns true; otherwise * zeros '*ss' and returns false. */ bool inet_parse_passive(const char *target_, int default_port, struct sockaddr_storage *ss) { char *target = xstrdup(target_); const char *port; const char *host; char *p; bool ok; p = target; port = parse_bracketed_token(&p); host = parse_bracketed_token(&p); if (!port && default_port < 0) { VLOG_ERR("%s: port must be specified", target_); ok = false; } else { ok = parse_sockaddr_components(ss, host ? host : "0.0.0.0", port, default_port, target_); } if (!ok) { memset(ss, 0, sizeof *ss); } free(target); return ok; } /* Opens a non-blocking IPv4 or IPv6 socket of the specified 'style', binds to * 'target', and listens for incoming connections. Parses 'target' in the same * way was inet_parse_passive(). * * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP). * * For TCP, the socket will have SO_REUSEADDR turned on. * * On success, returns a non-negative file descriptor. On failure, returns a * negative errno value. * * If 'ss' is non-null, then on success stores the bound address into '*ss'. * * 'dscp' becomes the DSCP bits in the IP headers for the new connection. It * should be in the range [0, 63] and will automatically be shifted to the * appropriately place in the IP tos field. * * If 'kernel_print_port' is true and the port is dynamically assigned by * the kernel, print the chosen port. */ int inet_open_passive(int style, const char *target, int default_port, struct sockaddr_storage *ssp, uint8_t dscp, bool kernel_print_port) { bool kernel_chooses_port; struct sockaddr_storage ss; int fd = 0, error; unsigned int yes = 1; if (!inet_parse_passive(target, default_port, &ss)) { return -EAFNOSUPPORT; } kernel_chooses_port = ss_get_port(&ss) == 0; /* Create non-blocking socket, set SO_REUSEADDR. */ fd = socket(ss.ss_family, style, 0); if (fd < 0) { error = sock_errno(); VLOG_ERR("%s: socket: %s", target, sock_strerror(error)); return -error; } error = set_nonblocking(fd); if (error) { goto error; } if (style == SOCK_STREAM && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) { error = sock_errno(); VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target, sock_strerror(error)); goto error; } /* Bind. */ if (bind(fd, (struct sockaddr *) &ss, ss_length(&ss)) < 0) { error = sock_errno(); VLOG_ERR("%s: bind: %s", target, sock_strerror(error)); goto error; } /* The dscp bits must be configured before connect() to ensure that the TOS * field is set during the connection establishment. If set after * connect(), the handshake SYN frames will be sent with a TOS of 0. */ error = set_dscp(fd, ss.ss_family, dscp); if (error) { VLOG_ERR("%s: set_dscp: %s", target, sock_strerror(error)); goto error; } /* Listen. */ if (style == SOCK_STREAM && listen(fd, 10) < 0) { error = sock_errno(); VLOG_ERR("%s: listen: %s", target, sock_strerror(error)); goto error; } if (ssp || kernel_chooses_port) { socklen_t ss_len = sizeof ss; if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) < 0) { error = sock_errno(); VLOG_ERR("%s: getsockname: %s", target, sock_strerror(error)); goto error; } if (kernel_chooses_port && kernel_print_port) { VLOG_INFO("%s: listening on port %"PRIu16, target, ss_get_port(&ss)); } if (ssp) { *ssp = ss; } } return fd; error: if (ssp) { memset(ssp, 0, sizeof *ssp); } closesocket(fd); return -error; } int read_fully(int fd, void *p_, size_t size, size_t *bytes_read) { uint8_t *p = p_; *bytes_read = 0; while (size > 0) { ssize_t retval = read(fd, p, size); if (retval > 0) { *bytes_read += retval; size -= retval; p += retval; } else if (retval == 0) { return EOF; } else if (errno != EINTR) { return errno; } } return 0; } int write_fully(int fd, const void *p_, size_t size, size_t *bytes_written) { const uint8_t *p = p_; *bytes_written = 0; while (size > 0) { ssize_t retval = write(fd, p, size); if (retval > 0) { *bytes_written += retval; size -= retval; p += retval; } else if (retval == 0) { VLOG_WARN("write returned 0"); return EPROTO; } else if (errno != EINTR) { return errno; } } return 0; } /* Given file name 'file_name', fsyncs the directory in which it is contained. * Returns 0 if successful, otherwise a positive errno value. */ int fsync_parent_dir(const char *file_name) { int error = 0; #ifndef _WIN32 char *dir; int fd; dir = dir_name(file_name); fd = open(dir, O_RDONLY); if (fd >= 0) { if (fsync(fd)) { if (errno == EINVAL || errno == EROFS) { /* This directory does not support synchronization. Not * really an error. */ } else { error = errno; VLOG_ERR("%s: fsync failed (%s)", dir, ovs_strerror(error)); } } close(fd); } else { error = errno; VLOG_ERR("%s: open failed (%s)", dir, ovs_strerror(error)); } free(dir); #endif return error; } /* Obtains the modification time of the file named 'file_name' to the greatest * supported precision. If successful, stores the mtime in '*mtime' and * returns 0. On error, returns a positive errno value and stores zeros in * '*mtime'. */ int get_mtime(const char *file_name, struct timespec *mtime) { struct stat s; if (!stat(file_name, &s)) { mtime->tv_sec = s.st_mtime; #if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC mtime->tv_nsec = s.st_mtim.tv_nsec; #elif HAVE_STRUCT_STAT_ST_MTIMENSEC mtime->tv_nsec = s.st_mtimensec; #else mtime->tv_nsec = 0; #endif return 0; } else { mtime->tv_sec = mtime->tv_nsec = 0; return errno; } } static int getsockopt_int(int fd, int level, int option, const char *optname, int *valuep) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 10); socklen_t len; int value; int error; len = sizeof value; if (getsockopt(fd, level, option, &value, &len)) { error = sock_errno(); VLOG_ERR_RL(&rl, "getsockopt(%s): %s", optname, sock_strerror(error)); } else if (len != sizeof value) { error = EINVAL; VLOG_ERR_RL(&rl, "getsockopt(%s): value is %u bytes (expected %"PRIuSIZE")", optname, (unsigned int) len, sizeof value); } else { error = 0; } *valuep = error ? 0 : value; return error; } static void describe_sockaddr(struct ds *string, int fd, int (*getaddr)(int, struct sockaddr *, socklen_t *)) { struct sockaddr_storage ss; socklen_t len = sizeof ss; if (!getaddr(fd, (struct sockaddr *) &ss, &len)) { if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { char addrbuf[SS_NTOP_BUFSIZE]; ds_put_format(string, "%s:%"PRIu16, ss_format_address(&ss, addrbuf, sizeof addrbuf), ss_get_port(&ss)); #ifndef _WIN32 } else if (ss.ss_family == AF_UNIX) { struct sockaddr_un sun; const char *null; size_t maxlen; memcpy(&sun, &ss, sizeof sun); maxlen = len - offsetof(struct sockaddr_un, sun_path); null = memchr(sun.sun_path, '\0', maxlen); ds_put_buffer(string, sun.sun_path, null ? null - sun.sun_path : maxlen); #endif } #ifdef HAVE_NETLINK else if (ss.ss_family == AF_NETLINK) { int protocol; /* SO_PROTOCOL was introduced in 2.6.32. Support it regardless of the version * of the Linux kernel headers in use at build time. */ #ifndef SO_PROTOCOL #define SO_PROTOCOL 38 #endif if (!getsockopt_int(fd, SOL_SOCKET, SO_PROTOCOL, "SO_PROTOCOL", &protocol)) { switch (protocol) { case NETLINK_ROUTE: ds_put_cstr(string, "NETLINK_ROUTE"); break; case NETLINK_GENERIC: ds_put_cstr(string, "NETLINK_GENERIC"); break; default: ds_put_format(string, "AF_NETLINK family %d", protocol); break; } } else { ds_put_cstr(string, "AF_NETLINK"); } } #endif #if __linux__ else if (ss.ss_family == AF_PACKET) { struct sockaddr_ll sll; memcpy(&sll, &ss, sizeof sll); ds_put_cstr(string, "AF_PACKET"); if (sll.sll_ifindex) { char name[IFNAMSIZ]; if (if_indextoname(sll.sll_ifindex, name)) { ds_put_format(string, "(%s)", name); } else { ds_put_format(string, "(ifindex=%d)", sll.sll_ifindex); } } if (sll.sll_protocol) { ds_put_format(string, "(protocol=0x%"PRIu16")", ntohs(sll.sll_protocol)); } } #endif else if (ss.ss_family == AF_UNSPEC) { ds_put_cstr(string, "AF_UNSPEC"); } else { ds_put_format(string, "AF_%d", (int) ss.ss_family); } } } #ifdef __linux__ static void put_fd_filename(struct ds *string, int fd) { char buf[1024]; char *linkname; int n; linkname = xasprintf("/proc/self/fd/%d", fd); n = readlink(linkname, buf, sizeof buf); if (n > 0) { ds_put_char(string, ' '); ds_put_buffer(string, buf, n); if (n > sizeof buf) { ds_put_cstr(string, "..."); } } free(linkname); } #endif /* Returns a malloc()'d string describing 'fd', for use in logging. */ char * describe_fd(int fd) { struct ds string; struct stat s; ds_init(&string); #ifndef _WIN32 if (fstat(fd, &s)) { ds_put_format(&string, "fstat failed (%s)", ovs_strerror(errno)); } else if (S_ISSOCK(s.st_mode)) { describe_sockaddr(&string, fd, getsockname); ds_put_cstr(&string, "<->"); describe_sockaddr(&string, fd, getpeername); } else { ds_put_cstr(&string, (isatty(fd) ? "tty" : S_ISDIR(s.st_mode) ? "directory" : S_ISCHR(s.st_mode) ? "character device" : S_ISBLK(s.st_mode) ? "block device" : S_ISREG(s.st_mode) ? "file" : S_ISFIFO(s.st_mode) ? "FIFO" : S_ISLNK(s.st_mode) ? "symbolic link" : "unknown")); #ifdef __linux__ put_fd_filename(&string, fd); #endif } #else ds_put_format(&string,"file descriptor"); #endif /* _WIN32 */ return ds_steal_cstr(&string); } /* sockaddr_storage helpers. */ /* Returns the IPv4 or IPv6 port in 'ss'. */ uint16_t ss_get_port(const struct sockaddr_storage *ss) { if (ss->ss_family == AF_INET) { const struct sockaddr_in *sin = ALIGNED_CAST(const struct sockaddr_in *, ss); return ntohs(sin->sin_port); } else if (ss->ss_family == AF_INET6) { const struct sockaddr_in6 *sin6 = ALIGNED_CAST(const struct sockaddr_in6 *, ss); return ntohs(sin6->sin6_port); } else { OVS_NOT_REACHED(); } } /* Formats the IPv4 or IPv6 address in 'ss' into the 'bufsize' bytes in 'buf'. * If 'ss' is an IPv6 address, puts square brackets around the address. * 'bufsize' should be at least SS_NTOP_BUFSIZE. * * Returns 'buf'. */ char * ss_format_address(const struct sockaddr_storage *ss, char *buf, size_t bufsize) { ovs_assert(bufsize >= SS_NTOP_BUFSIZE); if (ss->ss_family == AF_INET) { const struct sockaddr_in *sin = ALIGNED_CAST(const struct sockaddr_in *, ss); snprintf(buf, bufsize, IP_FMT, IP_ARGS(sin->sin_addr.s_addr)); } else if (ss->ss_family == AF_INET6) { const struct sockaddr_in6 *sin6 = ALIGNED_CAST(const struct sockaddr_in6 *, ss); buf[0] = '['; inet_ntop(AF_INET6, sin6->sin6_addr.s6_addr, buf + 1, bufsize - 1); strcpy(strchr(buf, '\0'), "]"); } else { OVS_NOT_REACHED(); } return buf; } size_t ss_length(const struct sockaddr_storage *ss) { switch (ss->ss_family) { case AF_INET: return sizeof(struct sockaddr_in); case AF_INET6: return sizeof(struct sockaddr_in6); default: OVS_NOT_REACHED(); } } /* For Windows socket calls, 'errno' is not set. One has to call * WSAGetLastError() to get the error number and then pass it to * this function to get the correct error string. * * ovs_strerror() calls strerror_r() and would not get the correct error * string for Windows sockets, but is good for POSIX. */ const char * sock_strerror(int error) { #ifdef _WIN32 return ovs_format_message(error); #else return ovs_strerror(error); #endif } openvswitch-2.5.9/lib/PaxHeaders.82075/if-notifier.c0000644000000000000000000000013213534540071016771 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.973854841 openvswitch-2.5.9/lib/if-notifier.c0000644000175000017500000000300513534540071020455 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "if-notifier.h" #include "rtnetlink.h" #include "util.h" struct if_notifier { struct nln_notifier *notifier; if_notify_func *cb; void *aux; }; static void if_notifier_cb(const struct rtnetlink_change *change OVS_UNUSED, void *aux) { struct if_notifier *notifier; notifier = aux; notifier->cb(notifier->aux); } struct if_notifier * if_notifier_create(if_notify_func *cb, void *aux) { struct if_notifier *notifier; notifier = xmalloc(sizeof *notifier); notifier->cb = cb; notifier->aux = aux; notifier->notifier = rtnetlink_notifier_create(if_notifier_cb, notifier); return notifier; } void if_notifier_destroy(struct if_notifier *notifier) { if (notifier) { rtnetlink_notifier_destroy(notifier->notifier); free(notifier); } } void if_notifier_run(void) { rtnetlink_run(); } void if_notifier_wait(void) { rtnetlink_wait(); } openvswitch-2.5.9/lib/PaxHeaders.82075/timeval.c0000644000000000000000000000013213534540071016217 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.913854399 openvswitch-2.5.9/lib/timeval.c0000644000175000017500000005370113534540071017713 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "timeval.h" #include #include #include #include #include #include #include #include #include #include "coverage.h" #include "dummy.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "hmap.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "signals.h" #include "seq.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(timeval); #ifdef _WIN32 typedef unsigned int clockid_t; #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 1 #endif #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 2 #endif /* Number of 100 ns intervals from January 1, 1601 till January 1, 1970. */ const static unsigned long long unix_epoch = 116444736000000000; #endif /* _WIN32 */ /* Structure set by unixctl time/warp command. */ struct large_warp { struct unixctl_conn *conn; /* Connection waiting for warp response. */ long long int total_warp; /* Total offset to be added to monotonic time. */ long long int warp; /* 'total_warp' offset done in steps of 'warp'. */ unsigned int main_thread_id; /* Identification for the main thread. */ }; struct clock { clockid_t id; /* CLOCK_MONOTONIC or CLOCK_REALTIME. */ /* Features for use by unit tests. Protected by 'mutex'. */ struct ovs_mutex mutex; atomic_bool slow_path; /* True if warped or stopped. */ struct timespec warp OVS_GUARDED; /* Offset added for unit tests. */ bool stopped OVS_GUARDED; /* Disable real-time updates if true. */ struct timespec cache OVS_GUARDED; /* Last time read from kernel. */ struct large_warp large_warp OVS_GUARDED; /* Connection information waiting for warp response. */ }; /* Our clocks. */ static struct clock monotonic_clock; /* CLOCK_MONOTONIC, if available. */ static struct clock wall_clock; /* CLOCK_REALTIME. */ /* The monotonic time at which the time module was initialized. */ static long long int boot_time; /* True only when timeval_dummy_register() is called. */ static bool timewarp_enabled; /* Reference to the seq struct. Threads other than main thread can * wait on timewarp_seq and be waken up when time is warped. */ static struct seq *timewarp_seq; /* Last value of 'timewarp_seq'. */ DEFINE_STATIC_PER_THREAD_DATA(uint64_t, last_seq, 0); /* Monotonic time in milliseconds at which to die with SIGALRM (if not * LLONG_MAX). */ static long long int deadline = LLONG_MAX; /* Monotonic time, in milliseconds, at which the last call to time_poll() woke * up. */ DEFINE_STATIC_PER_THREAD_DATA(long long int, last_wakeup, 0); static void log_poll_interval(long long int last_wakeup); static struct rusage *get_recent_rusage(void); static int getrusage_thread(struct rusage *); static void refresh_rusage(void); static void timespec_add(struct timespec *sum, const struct timespec *a, const struct timespec *b); static void init_clock(struct clock *c, clockid_t id) { memset(c, 0, sizeof *c); c->id = id; ovs_mutex_init(&c->mutex); atomic_init(&c->slow_path, false); xclock_gettime(c->id, &c->cache); } static void do_init_time(void) { struct timespec ts; coverage_init(); timewarp_seq = seq_create(); init_clock(&monotonic_clock, (!clock_gettime(CLOCK_MONOTONIC, &ts) ? CLOCK_MONOTONIC : CLOCK_REALTIME)); init_clock(&wall_clock, CLOCK_REALTIME); boot_time = timespec_to_msec(&monotonic_clock.cache); } /* Initializes the timetracking module, if not already initialized. */ static void time_init(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, do_init_time); } static void time_timespec__(struct clock *c, struct timespec *ts) { bool slow_path; time_init(); atomic_read_relaxed(&c->slow_path, &slow_path); if (!slow_path) { xclock_gettime(c->id, ts); } else { struct timespec warp; struct timespec cache; bool stopped; ovs_mutex_lock(&c->mutex); stopped = c->stopped; warp = c->warp; cache = c->cache; ovs_mutex_unlock(&c->mutex); if (!stopped) { xclock_gettime(c->id, &cache); } timespec_add(ts, &cache, &warp); } } /* Stores a monotonic timer, accurate within TIME_UPDATE_INTERVAL ms, into * '*ts'. */ void time_timespec(struct timespec *ts) { time_timespec__(&monotonic_clock, ts); } /* Stores the current time, accurate within TIME_UPDATE_INTERVAL ms, into * '*ts'. */ void time_wall_timespec(struct timespec *ts) { time_timespec__(&wall_clock, ts); } static time_t time_sec__(struct clock *c) { struct timespec ts; time_timespec__(c, &ts); return ts.tv_sec; } /* Returns a monotonic timer, in seconds. */ time_t time_now(void) { return time_sec__(&monotonic_clock); } /* Returns the current time, in seconds. */ time_t time_wall(void) { return time_sec__(&wall_clock); } static long long int time_msec__(struct clock *c) { struct timespec ts; time_timespec__(c, &ts); return timespec_to_msec(&ts); } /* Returns a monotonic timer, in ms (within TIME_UPDATE_INTERVAL ms). */ long long int time_msec(void) { return time_msec__(&monotonic_clock); } /* Returns the current time, in ms (within TIME_UPDATE_INTERVAL ms). */ long long int time_wall_msec(void) { return time_msec__(&wall_clock); } /* Configures the program to die with SIGALRM 'secs' seconds from now, if * 'secs' is nonzero, or disables the feature if 'secs' is zero. */ void time_alarm(unsigned int secs) { long long int now; long long int msecs; assert_single_threaded(); time_init(); now = time_msec(); msecs = secs * 1000LL; deadline = now < LLONG_MAX - msecs ? now + msecs : LLONG_MAX; } /* Like poll(), except: * * - The timeout is specified as an absolute time, as defined by * time_msec(), instead of a duration. * * - On error, returns a negative error code (instead of setting errno). * * - If interrupted by a signal, retries automatically until the original * timeout is reached. (Because of this property, this function will * never return -EINTR.) * * Stores the number of milliseconds elapsed during poll in '*elapsed'. */ int time_poll(struct pollfd *pollfds, int n_pollfds, HANDLE *handles OVS_UNUSED, long long int timeout_when, int *elapsed) { long long int *last_wakeup = last_wakeup_get(); long long int start; bool quiescent; int retval = 0; time_init(); coverage_clear(); coverage_run(); if (*last_wakeup && !thread_is_pmd()) { log_poll_interval(*last_wakeup); } start = time_msec(); timeout_when = MIN(timeout_when, deadline); quiescent = ovsrcu_is_quiescent(); for (;;) { long long int now = time_msec(); int time_left; if (now >= timeout_when) { time_left = 0; } else if ((unsigned long long int) timeout_when - now > INT_MAX) { time_left = INT_MAX; } else { time_left = timeout_when - now; } if (!quiescent) { if (!time_left) { ovsrcu_quiesce(); } else { ovsrcu_quiesce_start(); } } #ifndef _WIN32 retval = poll(pollfds, n_pollfds, time_left); if (retval < 0) { retval = -errno; } #else if (n_pollfds > MAXIMUM_WAIT_OBJECTS) { VLOG_ERR("Cannot handle more than maximum wait objects\n"); } else if (n_pollfds != 0) { retval = WaitForMultipleObjects(n_pollfds, handles, FALSE, time_left); } if (retval < 0) { /* XXX This will be replace by a win error to errno conversion function */ retval = -WSAGetLastError(); retval = -EINVAL; } #endif if (!quiescent && time_left) { ovsrcu_quiesce_end(); } if (deadline <= time_msec()) { #ifndef _WIN32 fatal_signal_handler(SIGALRM); #else VLOG_ERR("wake up from WaitForMultipleObjects after deadline"); fatal_signal_handler(SIGTERM); #endif if (retval < 0) { retval = 0; } break; } if (retval != -EINTR) { break; } } *last_wakeup = time_msec(); refresh_rusage(); *elapsed = *last_wakeup - start; return retval; } long long int timespec_to_msec(const struct timespec *ts) { return (long long int) ts->tv_sec * 1000 + ts->tv_nsec / (1000 * 1000); } long long int timeval_to_msec(const struct timeval *tv) { return (long long int) tv->tv_sec * 1000 + tv->tv_usec / 1000; } /* Returns the monotonic time at which the "time" module was initialized, in * milliseconds. */ long long int time_boot_msec(void) { time_init(); return boot_time; } #ifdef _WIN32 static ULARGE_INTEGER xgetfiletime(void) { ULARGE_INTEGER current_time; FILETIME current_time_ft; /* Returns current time in UTC as a 64-bit value representing the number * of 100-nanosecond intervals since January 1, 1601 . */ GetSystemTimePreciseAsFileTime(¤t_time_ft); current_time.LowPart = current_time_ft.dwLowDateTime; current_time.HighPart = current_time_ft.dwHighDateTime; return current_time; } static int clock_gettime(clock_t id, struct timespec *ts) { if (id == CLOCK_MONOTONIC) { static LARGE_INTEGER freq; LARGE_INTEGER count; long long int ns; if (!freq.QuadPart) { /* Number of counts per second. */ QueryPerformanceFrequency(&freq); } /* Total number of counts from a starting point. */ QueryPerformanceCounter(&count); /* Total nano seconds from a starting point. */ ns = (double) count.QuadPart / freq.QuadPart * 1000000000; ts->tv_sec = count.QuadPart / freq.QuadPart; ts->tv_nsec = ns % 1000000000; } else if (id == CLOCK_REALTIME) { ULARGE_INTEGER current_time = xgetfiletime(); /* Time from Epoch to now. */ ts->tv_sec = (current_time.QuadPart - unix_epoch) / 10000000; ts->tv_nsec = ((current_time.QuadPart - unix_epoch) % 10000000) * 100; } else { return -1; } return 0; } #endif /* _WIN32 */ void xgettimeofday(struct timeval *tv) { #ifndef _WIN32 if (gettimeofday(tv, NULL) == -1) { VLOG_FATAL("gettimeofday failed (%s)", ovs_strerror(errno)); } #else ULARGE_INTEGER current_time = xgetfiletime(); tv->tv_sec = (current_time.QuadPart - unix_epoch) / 10000000; tv->tv_usec = ((current_time.QuadPart - unix_epoch) % 10000000) / 10; #endif } void xclock_gettime(clock_t id, struct timespec *ts) { if (clock_gettime(id, ts) == -1) { /* It seems like a bad idea to try to use vlog here because it is * likely to try to check the current time. */ ovs_abort(errno, "xclock_gettime() failed"); } } static void msec_to_timespec(long long int ms, struct timespec *ts) { ts->tv_sec = ms / 1000; ts->tv_nsec = (ms % 1000) * 1000 * 1000; } static void timewarp_work(void) { struct clock *c = &monotonic_clock; struct timespec warp; ovs_mutex_lock(&c->mutex); if (!c->large_warp.conn) { ovs_mutex_unlock(&c->mutex); return; } if (c->large_warp.total_warp >= c->large_warp.warp) { msec_to_timespec(c->large_warp.warp, &warp); timespec_add(&c->warp, &c->warp, &warp); c->large_warp.total_warp -= c->large_warp.warp; } else if (c->large_warp.total_warp) { msec_to_timespec(c->large_warp.total_warp, &warp); timespec_add(&c->warp, &c->warp, &warp); c->large_warp.total_warp = 0; } else { /* c->large_warp.total_warp is 0. */ msec_to_timespec(c->large_warp.warp, &warp); timespec_add(&c->warp, &c->warp, &warp); } if (!c->large_warp.total_warp) { unixctl_command_reply(c->large_warp.conn, "warped"); c->large_warp.conn = NULL; } ovs_mutex_unlock(&c->mutex); seq_change(timewarp_seq); /* give threads (eg. monitor) some chances to run */ #ifndef _WIN32 poll(NULL, 0, 10); #else Sleep(10); #endif } /* Perform work needed for "timewarp_seq"'s producer and consumers. */ void timewarp_run(void) { /* The function is a no-op unless timeval_dummy_register() is called. */ if (timewarp_enabled) { unsigned int thread_id; ovs_mutex_lock(&monotonic_clock.mutex); thread_id = monotonic_clock.large_warp.main_thread_id; ovs_mutex_unlock(&monotonic_clock.mutex); if (thread_id != ovsthread_id_self()) { /* For threads other than the thread that changes the sequence, * wait on it. */ uint64_t *last_seq = last_seq_get(); *last_seq = seq_read(timewarp_seq); seq_wait(timewarp_seq, *last_seq); } else { /* Work on adding the remaining warps. */ timewarp_work(); } } } static long long int timeval_diff_msec(const struct timeval *a, const struct timeval *b) { return timeval_to_msec(a) - timeval_to_msec(b); } static void timespec_add(struct timespec *sum, const struct timespec *a, const struct timespec *b) { struct timespec tmp; tmp.tv_sec = a->tv_sec + b->tv_sec; tmp.tv_nsec = a->tv_nsec + b->tv_nsec; if (tmp.tv_nsec >= 1000 * 1000 * 1000) { tmp.tv_nsec -= 1000 * 1000 * 1000; tmp.tv_sec++; } *sum = tmp; } static bool is_warped(const struct clock *c) { bool warped; ovs_mutex_lock(&c->mutex); warped = monotonic_clock.warp.tv_sec || monotonic_clock.warp.tv_nsec; ovs_mutex_unlock(&c->mutex); return warped; } static void log_poll_interval(long long int last_wakeup) { long long int interval = time_msec() - last_wakeup; if (interval >= 1000 && !is_warped(&monotonic_clock)) { const struct rusage *last_rusage = get_recent_rusage(); struct rusage rusage; if (!getrusage_thread(&rusage)) { VLOG_WARN("Unreasonably long %lldms poll interval" " (%lldms user, %lldms system)", interval, timeval_diff_msec(&rusage.ru_utime, &last_rusage->ru_utime), timeval_diff_msec(&rusage.ru_stime, &last_rusage->ru_stime)); if (rusage.ru_minflt > last_rusage->ru_minflt || rusage.ru_majflt > last_rusage->ru_majflt) { VLOG_WARN("faults: %ld minor, %ld major", rusage.ru_minflt - last_rusage->ru_minflt, rusage.ru_majflt - last_rusage->ru_majflt); } if (rusage.ru_inblock > last_rusage->ru_inblock || rusage.ru_oublock > last_rusage->ru_oublock) { VLOG_WARN("disk: %ld reads, %ld writes", rusage.ru_inblock - last_rusage->ru_inblock, rusage.ru_oublock - last_rusage->ru_oublock); } if (rusage.ru_nvcsw > last_rusage->ru_nvcsw || rusage.ru_nivcsw > last_rusage->ru_nivcsw) { VLOG_WARN("context switches: %ld voluntary, %ld involuntary", rusage.ru_nvcsw - last_rusage->ru_nvcsw, rusage.ru_nivcsw - last_rusage->ru_nivcsw); } } else { VLOG_WARN("Unreasonably long %lldms poll interval", interval); } coverage_log(); } } /* CPU usage tracking. */ struct cpu_usage { long long int when; /* Time that this sample was taken. */ unsigned long long int cpu; /* Total user+system CPU usage when sampled. */ }; struct cpu_tracker { struct cpu_usage older; struct cpu_usage newer; int cpu_usage; struct rusage recent_rusage; }; DEFINE_PER_THREAD_MALLOCED_DATA(struct cpu_tracker *, cpu_tracker_var); static struct cpu_tracker * get_cpu_tracker(void) { struct cpu_tracker *t = cpu_tracker_var_get(); if (!t) { t = xzalloc(sizeof *t); t->older.when = LLONG_MIN; t->newer.when = LLONG_MIN; cpu_tracker_var_set_unsafe(t); } return t; } static struct rusage * get_recent_rusage(void) { return &get_cpu_tracker()->recent_rusage; } static int getrusage_thread(struct rusage *rusage OVS_UNUSED) { #ifdef RUSAGE_THREAD return getrusage(RUSAGE_THREAD, rusage); #else errno = EINVAL; return -1; #endif } static void refresh_rusage(void) { struct cpu_tracker *t = get_cpu_tracker(); struct rusage *recent_rusage = &t->recent_rusage; if (!getrusage_thread(recent_rusage)) { long long int now = time_msec(); if (now >= t->newer.when + 3 * 1000) { t->older = t->newer; t->newer.when = now; t->newer.cpu = (timeval_to_msec(&recent_rusage->ru_utime) + timeval_to_msec(&recent_rusage->ru_stime)); if (t->older.when != LLONG_MIN && t->newer.cpu > t->older.cpu) { unsigned int dividend = t->newer.cpu - t->older.cpu; unsigned int divisor = (t->newer.when - t->older.when) / 100; t->cpu_usage = divisor > 0 ? dividend / divisor : -1; } else { t->cpu_usage = -1; } } } } /* Returns an estimate of this process's CPU usage, as a percentage, over the * past few seconds of wall-clock time. Returns -1 if no estimate is available * (which will happen if the process has not been running long enough to have * an estimate, and can happen for other reasons as well). */ int get_cpu_usage(void) { return get_cpu_tracker()->cpu_usage; } /* Unixctl interface. */ /* "time/stop" stops the monotonic time returned by e.g. time_msec() from * advancing, except due to later calls to "time/warp". */ static void timeval_stop_cb(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { ovs_mutex_lock(&monotonic_clock.mutex); atomic_store_relaxed(&monotonic_clock.slow_path, true); monotonic_clock.stopped = true; xclock_gettime(monotonic_clock.id, &monotonic_clock.cache); ovs_mutex_unlock(&monotonic_clock.mutex); unixctl_command_reply(conn, NULL); } /* "time/warp MSECS" advances the current monotonic time by the specified * number of milliseconds. Unless "time/stop" has also been executed, the * monotonic clock continues to tick forward at the normal rate afterward. * * "time/warp LARGE_MSECS MSECS" is a variation of the above command. It * advances the current monotonic time by LARGE_MSECS. This is done MSECS * at a time in each run of the main thread. This gives other threads * time to run after the clock has been advanced by MSECS. * * Does not affect wall clock readings. */ static void timeval_warp_cb(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { long long int total_warp = argc > 2 ? atoll(argv[1]) : 0; long long int msecs = argc > 2 ? atoll(argv[2]) : atoll(argv[1]); if (msecs <= 0 || total_warp < 0) { unixctl_command_reply_error(conn, "invalid MSECS"); return; } ovs_mutex_lock(&monotonic_clock.mutex); if (monotonic_clock.large_warp.conn) { ovs_mutex_unlock(&monotonic_clock.mutex); unixctl_command_reply_error(conn, "A previous warp in progress"); return; } atomic_store_relaxed(&monotonic_clock.slow_path, true); monotonic_clock.large_warp.conn = conn; monotonic_clock.large_warp.total_warp = total_warp; monotonic_clock.large_warp.warp = msecs; monotonic_clock.large_warp.main_thread_id = ovsthread_id_self(); ovs_mutex_unlock(&monotonic_clock.mutex); timewarp_work(); } void timeval_dummy_register(void) { timewarp_enabled = true; unixctl_command_register("time/stop", "", 0, 0, timeval_stop_cb, NULL); unixctl_command_register("time/warp", "[large_msecs] msecs", 1, 2, timeval_warp_cb, NULL); } /* strftime() with an extension for high-resolution timestamps. Any '#'s in * 'format' will be replaced by subseconds, e.g. use "%S.###" to obtain results * like "01.123". */ size_t strftime_msec(char *s, size_t max, const char *format, const struct tm_msec *tm) { size_t n; /* Visual Studio 2013's behavior is to crash when 0 is passed as second * argument to strftime. */ n = max ? strftime(s, max, format, &tm->tm) : 0; if (n) { char decimals[4]; char *p; sprintf(decimals, "%03d", tm->msec); for (p = strchr(s, '#'); p; p = strchr(p, '#')) { char *d = decimals; while (*p == '#') { *p++ = *d ? *d++ : '0'; } } } return n; } struct tm_msec * localtime_msec(long long int now, struct tm_msec *result) { time_t now_sec = now / 1000; localtime_r(&now_sec, &result->tm); result->msec = now % 1000; return result; } struct tm_msec * gmtime_msec(long long int now, struct tm_msec *result) { time_t now_sec = now / 1000; gmtime_r(&now_sec, &result->tm); result->msec = now % 1000; return result; } openvswitch-2.5.9/lib/PaxHeaders.82075/syslog-direct.h0000644000000000000000000000013213534540071017353 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.901854311 openvswitch-2.5.9/lib/syslog-direct.h0000644000175000017500000000134713534540071021046 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SYSLOG_DIRECT_H #define SYSLOG_DIRECT_H 1 struct syslogger *syslog_direct_create(const char *method); #endif /* syslog-direct.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-notifier.h0000644000000000000000000000013013534540071020042 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 28 ctime=1567801424.9818549 openvswitch-2.5.9/lib/netlink-notifier.h0000644000175000017500000000350113534540071021531 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETLINK_NOTIFIER_H #define NETLINK_NOTIFIER_H 1 /* These functions are Linux specific, so they should be used directly only by * Linux-specific code. */ #include "list.h" struct nln; struct nln_notifier; struct nlattr; struct ofpbuf; /* Function called to report netlink notifications. 'change' describes the * specific change filled out by an nln_parse_func. It may be null if the * buffer of change information overflowed, in which case the function must * assume that everything may have changed. 'aux' is as specified in * nln_notifier_register(). */ typedef void nln_notify_func(const void *change, void *aux); /* Function called to parse incoming nln notifications. The 'buf' message * should be parsed into 'change' as specified in nln_create(). */ typedef bool nln_parse_func(struct ofpbuf *buf, void *change); struct nln *nln_create(int protocol, int multicast_group, nln_parse_func *, void *change); void nln_destroy(struct nln *); struct nln_notifier *nln_notifier_create(struct nln *, nln_notify_func *, void *aux); void nln_notifier_destroy(struct nln_notifier *); void nln_run(struct nln *); void nln_wait(struct nln *); #endif /* netlink-notifier.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-linux.h0000644000000000000000000000013213534540071017205 xustar0030 mtime=1567801401.453681551 30 atime=1567801402.081686164 30 ctime=1567801424.977854871 openvswitch-2.5.9/lib/netdev-linux.h0000644000175000017500000000174213534540071020677 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETDEV_LINUX_H #define NETDEV_LINUX_H 1 #include #include /* These functions are Linux specific, so they should be used directly only by * Linux-specific code. */ struct netdev; int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, const char *flag_name, bool enable); #endif /* netdev-linux.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/socket-util.h0000644000000000000000000000013213534540071017026 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801424.881854163 openvswitch-2.5.9/lib/socket-util.h0000644000175000017500000001110513534540071020512 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SOCKET_UTIL_H #define SOCKET_UTIL_H 1 #include #include #include #include #include #include #include "openvswitch/types.h" #include #include int set_nonblocking(int fd); void xset_nonblocking(int fd); void setsockopt_tcp_nodelay(int fd); int set_dscp(int fd, int family, uint8_t dscp); bool addr_is_ipv6(const char *host_name); int lookup_ip(const char *host_name, struct in_addr *address); int lookup_ipv6(const char *host_name, struct in6_addr *address); int lookup_hostname(const char *host_name, struct in_addr *); int get_socket_rcvbuf(int sock); int check_connection_completion(int fd); void drain_fd(int fd, size_t n_packets); ovs_be32 guess_netmask(ovs_be32 ip); bool inet_parse_active(const char *target, uint16_t default_port, struct sockaddr_storage *ssp); int inet_open_active(int style, const char *target, uint16_t default_port, struct sockaddr_storage *ssp, int *fdp, uint8_t dscp); bool inet_parse_passive(const char *target, int default_port, struct sockaddr_storage *ssp); int inet_open_passive(int style, const char *target, int default_port, struct sockaddr_storage *ssp, uint8_t dscp, bool kernel_print_port); int read_fully(int fd, void *, size_t, size_t *bytes_read); int write_fully(int fd, const void *, size_t, size_t *bytes_written); int fsync_parent_dir(const char *file_name); int get_mtime(const char *file_name, struct timespec *mtime); char *describe_fd(int fd); /* Default value of dscp bits for connection between controller and manager. * Value of IPTOS_PREC_INTERNETCONTROL = 0xc0 which is defined * in is used. */ #define DSCP_DEFAULT (IPTOS_PREC_INTERNETCONTROL >> 2) /* Functions for working with sockaddr_storage that might contain an IPv4 or * IPv6 address. */ uint16_t ss_get_port(const struct sockaddr_storage *); #define SS_NTOP_BUFSIZE (1 + INET6_ADDRSTRLEN + 1) char *ss_format_address(const struct sockaddr_storage *, char *buf, size_t bufsize); size_t ss_length(const struct sockaddr_storage *); const char *sock_strerror(int error); #ifndef _WIN32 void xpipe(int fds[2]); void xpipe_nonblocking(int fds[2]); int drain_rcvbuf(int fd); int make_unix_socket(int style, bool nonblock, const char *bind_path, const char *connect_path); int get_unix_name_len(socklen_t sun_len); /* Helpers for calling ioctl() on an AF_INET socket. */ struct ifreq; int af_inet_ioctl(unsigned long int command, const void *arg); int af_inet_ifreq_ioctl(const char *name, struct ifreq *, unsigned long int cmd, const char *cmd_name); #define closesocket close #endif #ifdef _WIN32 static inline int make_unix_socket(int style, bool nonblock, const char *bind_path, const char *connect_path) { return -EINVAL; } /* Windows defines the 'optval' argument as char * instead of void *. */ #define setsockopt(sock, level, optname, optval, optlen) \ rpl_setsockopt(sock, level, optname, optval, optlen) static inline int rpl_setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen) { return (setsockopt)(sock, level, optname, optval, optlen); } #define getsockopt(sock, level, optname, optval, optlen) \ rpl_getsockopt(sock, level, optname, optval, optlen) static inline int rpl_getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen) { return (getsockopt)(sock, level, optname, optval, optlen); } #endif /* In Windows platform, errno is not set for socket calls. * The last error has to be gotten from WSAGetLastError(). */ static inline int sock_errno(void) { #ifdef _WIN32 return WSAGetLastError(); #else return errno; #endif } #endif /* socket-util.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/learn.c0000644000000000000000000000013113534540071015656 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.749853191 openvswitch-2.5.9/lib/learn.c0000644000175000017500000004015513534540071017352 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "learn.h" #include "byte-order.h" #include "dynamic-string.h" #include "match.h" #include "meta-flow.h" #include "nx-match.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "unaligned.h" /* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid, * otherwise an OFPERR_*. */ enum ofperr learn_check(const struct ofpact_learn *learn, const struct flow *flow) { const struct ofpact_learn_spec *spec; struct match match; match_init_catchall(&match); for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { enum ofperr error; /* Check the source. */ if (spec->src_type == NX_LEARN_SRC_FIELD) { error = mf_check_src(&spec->src, flow); if (error) { return error; } } /* Check the destination. */ switch (spec->dst_type) { case NX_LEARN_DST_MATCH: error = mf_check_src(&spec->dst, &match.flow); if (error) { return error; } mf_write_subfield(&spec->dst, &spec->src_imm, &match); break; case NX_LEARN_DST_LOAD: error = mf_check_dst(&spec->dst, &match.flow); if (error) { return error; } break; case NX_LEARN_DST_OUTPUT: /* Nothing to do. */ break; } } return 0; } /* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->importance = 0; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = 0; if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; } fm->ofpacts = NULL; fm->ofpacts_len = 0; fm->delete_reason = OFPRR_DELETE; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { struct ofpact_set_field *sf; union mf_subvalue value; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { value = spec->src_imm; } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); break; case NX_LEARN_DST_LOAD: sf = ofpact_put_reg_load(ofpacts); sf->field = spec->dst.field; bitwise_copy(&value, sizeof value, 0, &sf->value, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); bitwise_one(&sf->mask, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofp_port_t port = u16_to_ofp(ntohll(value.integer)); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } ofpact_pad(ofpacts); fm->ofpacts = ofpacts->data; fm->ofpacts_len = ofpacts->size; } /* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in * the learn action 'learn'. */ void learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc) { const struct ofpact_learn_spec *spec; union mf_subvalue value; memset(&value, 0xff, sizeof value); for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_write_subfield_flow(&spec->src, &value, &wc->masks); } } } /* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec) { const char *full_s = s; struct mf_subfield dst; union mf_subvalue imm; char *error; int err; err = parse_int_string(s, imm.u8, sizeof imm.u8, (char **) &s); if (err) { return xasprintf("%s: too many bits in immediate value", full_s); } if (strncmp(s, "->", 2)) { return xasprintf("%s: missing `->' following value", full_s); } s += 2; error = mf_parse_subfield(&dst, s); if (error) { return error; } if (!mf_nxm_header(dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", full_s, s); } if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits, (8 * sizeof imm) - dst.n_bits)) { return xasprintf("%s: value does not fit into %u bits", full_s, dst.n_bits); } spec->n_bits = dst.n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; spec->src_imm = imm; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = dst; return NULL; } /* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_spec(const char *orig, char *name, char *value, struct ofpact_learn_spec *spec) { if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; char *error; error = mf_parse_value(dst, value, &imm); if (error) { return error; } spec->n_bits = dst->n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; memset(&spec->src_imm, 0, sizeof spec->src_imm); memcpy(&spec->src_imm.u8[sizeof spec->src_imm - dst->n_bytes], &imm, dst->n_bytes); spec->dst_type = NX_LEARN_DST_MATCH; spec->dst.field = dst; spec->dst.ofs = 0; spec->dst.n_bits = dst->n_bits; } else if (strchr(name, '[')) { /* Parse destination and check prerequisites. */ char *error; error = mf_parse_subfield(&spec->dst, name); if (error) { return error; } if (!mf_nxm_header(spec->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } /* Parse source and check prerequisites. */ if (value[0] != '\0') { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_MATCH; } else if (!strcmp(name, "load")) { if (value[strcspn(value, "[-")] == '-') { char *error = learn_parse_load_immediate(value, spec); if (error) { return error; } } else { struct ofpact_reg_move move; char *error; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { char *error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; } /* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; learn = ofpact_put_LEARN(ofpacts); learn->idle_timeout = OFP_FLOW_PERMANENT; learn->hard_timeout = OFP_FLOW_PERMANENT; learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else if (!strcmp(name, "send_flow_rem")) { learn->flags |= NX_LEARN_F_SEND_FLOW_REM; } else if (!strcmp(name, "delete_learned")) { learn->flags |= NX_LEARN_F_DELETE_LEARNED; } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->header; learn->n_specs++; error = learn_parse_spec(orig, name, value, spec); if (error) { return error; } /* Update 'match' to allow for satisfying destination * prerequisites. */ if (spec->src_type == NX_LEARN_SRC_IMMEDIATE && spec->dst_type == NX_LEARN_DST_MATCH) { mf_write_subfield(&spec->dst, &spec->src_imm, &match); } } } ofpact_update_len(ofpacts, &learn->ofpact); return NULL; } /* Parses 'arg' as a set of arguments to the "learn" action and appends a * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the * format parsed. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. * * If 'flow' is nonnull, then it should be the flow from a struct match that is * the matching rule for the learning action. This helps to better validate * the action's arguments. * * Modifies 'arg'. */ char * OVS_WARN_UNUSED_RESULT learn_parse(char *arg, struct ofpbuf *ofpacts) { char *orig = xstrdup(arg); char *error = learn_parse__(orig, arg, ofpacts); free(orig); return error; } /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8) * describes. */ void learn_format(const struct ofpact_learn *learn, struct ds *s) { const struct ofpact_learn_spec *spec; struct match match; match_init_catchall(&match); ds_put_format(s, "learn(table=%"PRIu8, learn->table_id); if (learn->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout); } if (learn->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout); } if (learn->fin_idle_timeout) { ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout); } if (learn->fin_hard_timeout) { ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout); } if (learn->priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, ",priority=%"PRIu16, learn->priority); } if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { ds_put_cstr(s, ",send_flow_rem"); } if (learn->flags & NX_LEARN_F_DELETE_LEARNED) { ds_put_cstr(s, ",delete_learned"); } if (learn->cookie != 0) { ds_put_format(s, ",cookie=%#"PRIx64, ntohll(learn->cookie)); } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { ds_put_char(s, ','); switch (spec->src_type | spec->dst_type) { case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: if (spec->dst.ofs == 0 && spec->dst.n_bits == spec->dst.field->n_bits) { union mf_value value; memset(&value, 0, sizeof value); bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, &value, spec->dst.field->n_bytes, 0, spec->dst.field->n_bits); ds_put_format(s, "%s=", spec->dst.field->name); mf_format(spec->dst.field, &value, NULL, s); } else { mf_format_subfield(&spec->dst, s); ds_put_char(s, '='); mf_format_subvalue(&spec->src_imm, s); } break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: mf_format_subfield(&spec->dst, s); if (spec->src.field != spec->dst.field || spec->src.ofs != spec->dst.ofs) { ds_put_char(s, '='); mf_format_subfield(&spec->src, s); } break; case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: ds_put_format(s, "load:"); mf_format_subvalue(&spec->src_imm, s); ds_put_cstr(s, "->"); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: ds_put_cstr(s, "load:"); mf_format_subfield(&spec->src, s); ds_put_cstr(s, "->"); mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: ds_put_cstr(s, "output:"); mf_format_subfield(&spec->src, s); break; } } ds_put_char(s, ')'); } openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-socket.c0000644000000000000000000000013013534540071017506 xustar0030 mtime=1567801401.465681639 30 atime=1567801402.081686164 28 ctime=1567801424.9818549 openvswitch-2.5.9/lib/netlink-socket.c0000644000175000017500000016141213534540071021203 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netlink-socket.h" #include #include #include #include #include #include #include "coverage.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "netlink.h" #include "netlink-protocol.h" #include "odp-netlink.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "poll-loop.h" #include "seq.h" #include "socket-util.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netlink_socket); COVERAGE_DEFINE(netlink_overflow); COVERAGE_DEFINE(netlink_received); COVERAGE_DEFINE(netlink_recv_jumbo); COVERAGE_DEFINE(netlink_sent); /* Linux header file confusion causes this to be undefined. */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #ifdef _WIN32 static struct ovs_mutex portid_mutex = OVS_MUTEX_INITIALIZER; static uint32_t g_last_portid = 0; /* Port IDs must be unique! */ static uint32_t portid_next(void) OVS_GUARDED_BY(portid_mutex) { g_last_portid++; return g_last_portid; } #endif /* _WIN32 */ /* A single (bad) Netlink message can in theory dump out many, many log * messages, so the burst size is set quite high here to avoid missing useful * information. Also, at high logging levels we log *all* Netlink messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 600); static uint32_t nl_sock_allocate_seq(struct nl_sock *, unsigned int n); static void log_nlmsg(const char *function, int error, const void *message, size_t size, int protocol); #ifdef _WIN32 static int get_sock_pid_from_kernel(struct nl_sock *sock); #endif /* Netlink sockets. */ struct nl_sock { #ifdef _WIN32 HANDLE handle; OVERLAPPED overlapped; DWORD read_ioctl; #else int fd; #endif uint32_t next_seq; uint32_t pid; int protocol; unsigned int rcvbuf; /* Receive buffer size (SO_RCVBUF). */ }; /* Compile-time limit on iovecs, so that we can allocate a maximum-size array * of iovecs on the stack. */ #define MAX_IOVS 128 /* Maximum number of iovecs that may be passed to sendmsg, capped at a * minimum of _XOPEN_IOV_MAX (16) and a maximum of MAX_IOVS. * * Initialized by nl_sock_create(). */ static int max_iovs; static int nl_pool_alloc(int protocol, struct nl_sock **sockp); static void nl_pool_release(struct nl_sock *); /* Creates a new netlink socket for the given netlink 'protocol' * (NETLINK_ROUTE, NETLINK_GENERIC, ...). Returns 0 and sets '*sockp' to the * new socket if successful, otherwise returns a positive errno value. */ int nl_sock_create(int protocol, struct nl_sock **sockp) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct nl_sock *sock; #ifndef _WIN32 struct sockaddr_nl local, remote; #endif socklen_t local_size; int rcvbuf; int retval = 0; if (ovsthread_once_start(&once)) { int save_errno = errno; errno = 0; max_iovs = sysconf(_SC_UIO_MAXIOV); if (max_iovs < _XOPEN_IOV_MAX) { if (max_iovs == -1 && errno) { VLOG_WARN("sysconf(_SC_UIO_MAXIOV): %s", ovs_strerror(errno)); } max_iovs = _XOPEN_IOV_MAX; } else if (max_iovs > MAX_IOVS) { max_iovs = MAX_IOVS; } errno = save_errno; ovsthread_once_done(&once); } *sockp = NULL; sock = xmalloc(sizeof *sock); #ifdef _WIN32 sock->handle = CreateFile(OVS_DEVICE_NAME_USER, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (sock->handle == INVALID_HANDLE_VALUE) { VLOG_ERR("fcntl: %s", ovs_lasterror_to_string()); goto error; } memset(&sock->overlapped, 0, sizeof sock->overlapped); sock->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (sock->overlapped.hEvent == NULL) { VLOG_ERR("fcntl: %s", ovs_lasterror_to_string()); goto error; } /* Initialize the type/ioctl to Generic */ sock->read_ioctl = OVS_IOCTL_READ; #else sock->fd = socket(AF_NETLINK, SOCK_RAW, protocol); if (sock->fd < 0) { VLOG_ERR("fcntl: %s", ovs_strerror(errno)); goto error; } #endif sock->protocol = protocol; sock->next_seq = 1; rcvbuf = 1024 * 1024; #ifdef _WIN32 sock->rcvbuf = rcvbuf; retval = get_sock_pid_from_kernel(sock); if (retval != 0) { goto error; } #else if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof rcvbuf)) { /* Only root can use SO_RCVBUFFORCE. Everyone else gets EPERM. * Warn only if the failure is therefore unexpected. */ if (errno != EPERM) { VLOG_WARN_RL(&rl, "setting %d-byte socket receive buffer failed " "(%s)", rcvbuf, ovs_strerror(errno)); } } retval = get_socket_rcvbuf(sock->fd); if (retval < 0) { retval = -retval; goto error; } sock->rcvbuf = retval; retval = 0; /* Connect to kernel (pid 0) as remote address. */ memset(&remote, 0, sizeof remote); remote.nl_family = AF_NETLINK; remote.nl_pid = 0; if (connect(sock->fd, (struct sockaddr *) &remote, sizeof remote) < 0) { VLOG_ERR("connect(0): %s", ovs_strerror(errno)); goto error; } /* Obtain pid assigned by kernel. */ local_size = sizeof local; if (getsockname(sock->fd, (struct sockaddr *) &local, &local_size) < 0) { VLOG_ERR("getsockname: %s", ovs_strerror(errno)); goto error; } if (local_size < sizeof local || local.nl_family != AF_NETLINK) { VLOG_ERR("getsockname returned bad Netlink name"); retval = EINVAL; goto error; } sock->pid = local.nl_pid; #endif *sockp = sock; return 0; error: if (retval == 0) { retval = errno; if (retval == 0) { retval = EINVAL; } } #ifdef _WIN32 if (sock->overlapped.hEvent) { CloseHandle(sock->overlapped.hEvent); } if (sock->handle != INVALID_HANDLE_VALUE) { CloseHandle(sock->handle); } #else if (sock->fd >= 0) { close(sock->fd); } #endif free(sock); return retval; } /* Creates a new netlink socket for the same protocol as 'src'. Returns 0 and * sets '*sockp' to the new socket if successful, otherwise returns a positive * errno value. */ int nl_sock_clone(const struct nl_sock *src, struct nl_sock **sockp) { return nl_sock_create(src->protocol, sockp); } /* Destroys netlink socket 'sock'. */ void nl_sock_destroy(struct nl_sock *sock) { if (sock) { #ifdef _WIN32 if (sock->overlapped.hEvent) { CloseHandle(sock->overlapped.hEvent); } CloseHandle(sock->handle); #else close(sock->fd); #endif free(sock); } } #ifdef _WIN32 /* Reads the pid for 'sock' generated in the kernel datapath. The function * uses a separate IOCTL instead of a transaction semantic to avoid unnecessary * message overhead. */ static int get_sock_pid_from_kernel(struct nl_sock *sock) { uint32_t pid = 0; int retval = 0; DWORD bytes = 0; if (!DeviceIoControl(sock->handle, OVS_IOCTL_GET_PID, NULL, 0, &pid, sizeof(pid), &bytes, NULL)) { retval = EINVAL; } else { if (bytes < sizeof(pid)) { retval = EINVAL; } else { sock->pid = pid; } } return retval; } #endif /* _WIN32 */ #ifdef _WIN32 static int __inline nl_sock_mcgroup(struct nl_sock *sock, unsigned int multicast_group, bool join) { struct ofpbuf request; uint64_t request_stub[128]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; int error; ofpbuf_use_stub(&request, request_stub, sizeof request_stub); nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0, OVS_CTRL_CMD_MC_SUBSCRIBE_REQ, OVS_WIN_CONTROL_VERSION); ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header); ovs_header->dp_ifindex = 0; nl_msg_put_u32(&request, OVS_NL_ATTR_MCAST_GRP, multicast_group); nl_msg_put_u8(&request, OVS_NL_ATTR_MCAST_JOIN, join ? 1 : 0); error = nl_sock_send(sock, &request, true); ofpbuf_uninit(&request); return error; } #endif /* Tries to add 'sock' as a listener for 'multicast_group'. Returns 0 if * successful, otherwise a positive errno value. * * A socket that is subscribed to a multicast group that receives asynchronous * notifications must not be used for Netlink transactions or dumps, because * transactions and dumps can cause notifications to be lost. * * Multicast group numbers are always positive. * * It is not an error to attempt to join a multicast group to which a socket * already belongs. */ int nl_sock_join_mcgroup(struct nl_sock *sock, unsigned int multicast_group) { #ifdef _WIN32 /* Set the socket type as a "multicast" socket */ sock->read_ioctl = OVS_IOCTL_READ_EVENT; int error = nl_sock_mcgroup(sock, multicast_group, true); if (error) { sock->read_ioctl = OVS_IOCTL_READ; VLOG_WARN("could not join multicast group %u (%s)", multicast_group, ovs_strerror(error)); return error; } #else if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &multicast_group, sizeof multicast_group) < 0) { VLOG_WARN("could not join multicast group %u (%s)", multicast_group, ovs_strerror(errno)); return errno; } #endif return 0; } #ifdef _WIN32 int nl_sock_subscribe_packets(struct nl_sock *sock) { int error; if (sock->read_ioctl != OVS_IOCTL_READ) { return EINVAL; } error = nl_sock_subscribe_packet__(sock, true); if (error) { VLOG_WARN("could not subscribe packets (%s)", ovs_strerror(error)); return error; } sock->read_ioctl = OVS_IOCTL_READ_PACKET; return 0; } int nl_sock_unsubscribe_packets(struct nl_sock *sock) { ovs_assert(sock->read_ioctl == OVS_IOCTL_READ_PACKET); int error = nl_sock_subscribe_packet__(sock, false); if (error) { VLOG_WARN("could not unsubscribe to packets (%s)", ovs_strerror(error)); return error; } sock->read_ioctl = OVS_IOCTL_READ; return 0; } int nl_sock_subscribe_packet__(struct nl_sock *sock, bool subscribe) { struct ofpbuf request; uint64_t request_stub[128]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; int error; ofpbuf_use_stub(&request, request_stub, sizeof request_stub); nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0, OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ, OVS_WIN_CONTROL_VERSION); ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header); ovs_header->dp_ifindex = 0; nl_msg_put_u8(&request, OVS_NL_ATTR_PACKET_SUBSCRIBE, subscribe ? 1 : 0); nl_msg_put_u32(&request, OVS_NL_ATTR_PACKET_PID, sock->pid); error = nl_sock_send(sock, &request, true); ofpbuf_uninit(&request); return error; } #endif /* Tries to make 'sock' stop listening to 'multicast_group'. Returns 0 if * successful, otherwise a positive errno value. * * Multicast group numbers are always positive. * * It is not an error to attempt to leave a multicast group to which a socket * does not belong. * * On success, reading from 'sock' will still return any messages that were * received on 'multicast_group' before the group was left. */ int nl_sock_leave_mcgroup(struct nl_sock *sock, unsigned int multicast_group) { #ifdef _WIN32 int error = nl_sock_mcgroup(sock, multicast_group, false); if (error) { VLOG_WARN("could not leave multicast group %u (%s)", multicast_group, ovs_strerror(error)); return error; } sock->read_ioctl = OVS_IOCTL_READ; #else if (setsockopt(sock->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &multicast_group, sizeof multicast_group) < 0) { VLOG_WARN("could not leave multicast group %u (%s)", multicast_group, ovs_strerror(errno)); return errno; } #endif return 0; } static int nl_sock_send__(struct nl_sock *sock, const struct ofpbuf *msg, uint32_t nlmsg_seq, bool wait) { struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(msg); int error; nlmsg->nlmsg_len = msg->size; nlmsg->nlmsg_seq = nlmsg_seq; nlmsg->nlmsg_pid = sock->pid; do { int retval; #ifdef _WIN32 DWORD bytes; if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE, msg->data, msg->size, NULL, 0, &bytes, NULL)) { retval = -1; /* XXX: Map to a more appropriate error based on GetLastError(). */ errno = EINVAL; VLOG_DBG_RL(&rl, "fatal driver failure in write: %s", ovs_lasterror_to_string()); } else { retval = msg->size; } #else retval = send(sock->fd, msg->data, msg->size, wait ? 0 : MSG_DONTWAIT); #endif error = retval < 0 ? errno : 0; } while (error == EINTR); log_nlmsg(__func__, error, msg->data, msg->size, sock->protocol); if (!error) { COVERAGE_INC(netlink_sent); } return error; } /* Tries to send 'msg', which must contain a Netlink message, to the kernel on * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid * will be set to 'sock''s pid, and nlmsg_seq will be initialized to a fresh * sequence number, before the message is sent. * * Returns 0 if successful, otherwise a positive errno value. If * 'wait' is true, then the send will wait until buffer space is ready; * otherwise, returns EAGAIN if the 'sock' send buffer is full. */ int nl_sock_send(struct nl_sock *sock, const struct ofpbuf *msg, bool wait) { return nl_sock_send_seq(sock, msg, nl_sock_allocate_seq(sock, 1), wait); } /* Tries to send 'msg', which must contain a Netlink message, to the kernel on * 'sock'. nlmsg_len in 'msg' will be finalized to match msg->size, nlmsg_pid * will be set to 'sock''s pid, and nlmsg_seq will be initialized to * 'nlmsg_seq', before the message is sent. * * Returns 0 if successful, otherwise a positive errno value. If * 'wait' is true, then the send will wait until buffer space is ready; * otherwise, returns EAGAIN if the 'sock' send buffer is full. * * This function is suitable for sending a reply to a request that was received * with sequence number 'nlmsg_seq'. Otherwise, use nl_sock_send() instead. */ int nl_sock_send_seq(struct nl_sock *sock, const struct ofpbuf *msg, uint32_t nlmsg_seq, bool wait) { return nl_sock_send__(sock, msg, nlmsg_seq, wait); } static int nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait) { /* We can't accurately predict the size of the data to be received. The * caller is supposed to have allocated enough space in 'buf' to handle the * "typical" case. To handle exceptions, we make available enough space in * 'tail' to allow Netlink messages to be up to 64 kB long (a reasonable * figure since that's the maximum length of a Netlink attribute). */ struct nlmsghdr *nlmsghdr; uint8_t tail[65536]; struct iovec iov[2]; struct msghdr msg; ssize_t retval; int error; ovs_assert(buf->allocated >= sizeof *nlmsghdr); ofpbuf_clear(buf); iov[0].iov_base = buf->base; iov[0].iov_len = buf->allocated; iov[1].iov_base = tail; iov[1].iov_len = sizeof tail; memset(&msg, 0, sizeof msg); msg.msg_iov = iov; msg.msg_iovlen = 2; /* Receive a Netlink message from the kernel. * * This works around a kernel bug in which the kernel returns an error code * as if it were the number of bytes read. It doesn't actually modify * anything in the receive buffer in that case, so we can initialize the * Netlink header with an impossible message length and then, upon success, * check whether it changed. */ nlmsghdr = buf->base; do { nlmsghdr->nlmsg_len = UINT32_MAX; #ifdef _WIN32 DWORD bytes; if (!DeviceIoControl(sock->handle, sock->read_ioctl, NULL, 0, tail, sizeof tail, &bytes, NULL)) { VLOG_DBG_RL(&rl, "fatal driver failure in transact: %s", ovs_lasterror_to_string()); retval = -1; /* XXX: Map to a more appropriate error. */ errno = EINVAL; } else { retval = bytes; if (retval == 0) { retval = -1; errno = EAGAIN; } else { if (retval >= buf->allocated) { ofpbuf_reinit(buf, retval); nlmsghdr = buf->base; nlmsghdr->nlmsg_len = UINT32_MAX; } memcpy(buf->data, tail, retval); buf->size = retval; } } #else retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT); #endif error = (retval < 0 ? errno : retval == 0 ? ECONNRESET /* not possible? */ : nlmsghdr->nlmsg_len != UINT32_MAX ? 0 : retval); } while (error == EINTR); if (error) { if (error == ENOBUFS) { /* Socket receive buffer overflow dropped one or more messages that * the kernel tried to send to us. */ COVERAGE_INC(netlink_overflow); } return error; } if (msg.msg_flags & MSG_TRUNC) { VLOG_ERR_RL(&rl, "truncated message (longer than %"PRIuSIZE" bytes)", sizeof tail); return E2BIG; } if (retval < sizeof *nlmsghdr || nlmsghdr->nlmsg_len < sizeof *nlmsghdr || nlmsghdr->nlmsg_len > retval) { VLOG_ERR_RL(&rl, "received invalid nlmsg (%"PRIuSIZE" bytes < %"PRIuSIZE")", retval, sizeof *nlmsghdr); return EPROTO; } #ifndef _WIN32 buf->size = MIN(retval, buf->allocated); if (retval > buf->allocated) { COVERAGE_INC(netlink_recv_jumbo); ofpbuf_put(buf, tail, retval - buf->allocated); } #endif log_nlmsg(__func__, 0, buf->data, buf->size, sock->protocol); COVERAGE_INC(netlink_received); return 0; } /* Tries to receive a Netlink message from the kernel on 'sock' into 'buf'. If * 'wait' is true, waits for a message to be ready. Otherwise, fails with * EAGAIN if the 'sock' receive buffer is empty. * * The caller must have initialized 'buf' with an allocation of at least * NLMSG_HDRLEN bytes. For best performance, the caller should allocate enough * space for a "typical" message. * * On success, returns 0 and replaces 'buf''s previous content by the received * message. This function expands 'buf''s allocated memory, as necessary, to * hold the actual size of the received message. * * On failure, returns a positive errno value and clears 'buf' to zero length. * 'buf' retains its previous memory allocation. * * Regardless of success or failure, this function resets 'buf''s headroom to * 0. */ int nl_sock_recv(struct nl_sock *sock, struct ofpbuf *buf, bool wait) { return nl_sock_recv__(sock, buf, wait); } static void nl_sock_record_errors__(struct nl_transaction **transactions, size_t n, int error) { size_t i; for (i = 0; i < n; i++) { struct nl_transaction *txn = transactions[i]; txn->error = error; if (txn->reply) { ofpbuf_clear(txn->reply); } } } static int nl_sock_transact_multiple__(struct nl_sock *sock, struct nl_transaction **transactions, size_t n, size_t *done) { uint64_t tmp_reply_stub[1024 / 8]; struct nl_transaction tmp_txn; struct ofpbuf tmp_reply; uint32_t base_seq; struct iovec iovs[MAX_IOVS]; struct msghdr msg; int error; int i; base_seq = nl_sock_allocate_seq(sock, n); *done = 0; for (i = 0; i < n; i++) { struct nl_transaction *txn = transactions[i]; struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(txn->request); nlmsg->nlmsg_len = txn->request->size; nlmsg->nlmsg_seq = base_seq + i; nlmsg->nlmsg_pid = sock->pid; iovs[i].iov_base = txn->request->data; iovs[i].iov_len = txn->request->size; } #ifndef _WIN32 memset(&msg, 0, sizeof msg); msg.msg_iov = iovs; msg.msg_iovlen = n; do { error = sendmsg(sock->fd, &msg, 0) < 0 ? errno : 0; } while (error == EINTR); for (i = 0; i < n; i++) { struct nl_transaction *txn = transactions[i]; log_nlmsg(__func__, error, txn->request->data, txn->request->size, sock->protocol); } if (!error) { COVERAGE_ADD(netlink_sent, n); } if (error) { return error; } ofpbuf_use_stub(&tmp_reply, tmp_reply_stub, sizeof tmp_reply_stub); tmp_txn.request = NULL; tmp_txn.reply = &tmp_reply; tmp_txn.error = 0; while (n > 0) { struct nl_transaction *buf_txn, *txn; uint32_t seq; /* Find a transaction whose buffer we can use for receiving a reply. * If no such transaction is left, use tmp_txn. */ buf_txn = &tmp_txn; for (i = 0; i < n; i++) { if (transactions[i]->reply) { buf_txn = transactions[i]; break; } } /* Receive a reply. */ error = nl_sock_recv__(sock, buf_txn->reply, false); if (error) { if (error == EAGAIN) { nl_sock_record_errors__(transactions, n, 0); *done += n; error = 0; } break; } /* Match the reply up with a transaction. */ seq = nl_msg_nlmsghdr(buf_txn->reply)->nlmsg_seq; if (seq < base_seq || seq >= base_seq + n) { VLOG_DBG_RL(&rl, "ignoring unexpected seq %#"PRIx32, seq); continue; } i = seq - base_seq; txn = transactions[i]; /* Fill in the results for 'txn'. */ if (nl_msg_nlmsgerr(buf_txn->reply, &txn->error)) { if (txn->reply) { ofpbuf_clear(txn->reply); } if (txn->error) { VLOG_DBG_RL(&rl, "received NAK error=%d (%s)", error, ovs_strerror(txn->error)); } } else { txn->error = 0; if (txn->reply && txn != buf_txn) { /* Swap buffers. */ struct ofpbuf *reply = buf_txn->reply; buf_txn->reply = txn->reply; txn->reply = reply; } } /* Fill in the results for transactions before 'txn'. (We have to do * this after the results for 'txn' itself because of the buffer swap * above.) */ nl_sock_record_errors__(transactions, i, 0); /* Advance. */ *done += i + 1; transactions += i + 1; n -= i + 1; base_seq += i + 1; } ofpbuf_uninit(&tmp_reply); #else error = 0; uint8_t reply_buf[65536]; for (i = 0; i < n; i++) { DWORD reply_len; bool ret; struct nl_transaction *txn = transactions[i]; struct nlmsghdr *request_nlmsg, *reply_nlmsg; ret = DeviceIoControl(sock->handle, OVS_IOCTL_TRANSACT, txn->request->data, txn->request->size, reply_buf, sizeof reply_buf, &reply_len, NULL); if (ret && reply_len == 0) { /* * The current transaction did not produce any data to read and that * is not an error as such. Continue with the remainder of the * transactions. */ txn->error = 0; if (txn->reply) { ofpbuf_clear(txn->reply); } } else if (!ret) { /* XXX: Map to a more appropriate error. */ error = EINVAL; VLOG_DBG_RL(&rl, "fatal driver failure: %s", ovs_lasterror_to_string()); break; } if (reply_len != 0) { if (reply_len < sizeof *reply_nlmsg) { nl_sock_record_errors__(transactions, n, 0); VLOG_DBG_RL(&rl, "insufficient length of reply %#"PRIu32 " for seq: %#"PRIx32, reply_len, request_nlmsg->nlmsg_seq); break; } /* Validate the sequence number in the reply. */ request_nlmsg = nl_msg_nlmsghdr(txn->request); reply_nlmsg = (struct nlmsghdr *)reply_buf; if (request_nlmsg->nlmsg_seq != reply_nlmsg->nlmsg_seq) { ovs_assert(request_nlmsg->nlmsg_seq == reply_nlmsg->nlmsg_seq); VLOG_DBG_RL(&rl, "mismatched seq request %#"PRIx32 ", reply %#"PRIx32, request_nlmsg->nlmsg_seq, reply_nlmsg->nlmsg_seq); break; } /* Handle errors embedded within the netlink message. */ ofpbuf_use_stub(&tmp_reply, reply_buf, sizeof reply_buf); tmp_reply.size = sizeof reply_buf; if (nl_msg_nlmsgerr(&tmp_reply, &txn->error)) { if (txn->reply) { ofpbuf_clear(txn->reply); } if (txn->error) { VLOG_DBG_RL(&rl, "received NAK error=%d (%s)", error, ovs_strerror(txn->error)); } } else { txn->error = 0; if (txn->reply) { /* Copy the reply to the buffer specified by the caller. */ if (reply_len > txn->reply->allocated) { ofpbuf_reinit(txn->reply, reply_len); } memcpy(txn->reply->data, reply_buf, reply_len); txn->reply->size = reply_len; } } ofpbuf_uninit(&tmp_reply); } /* Count the number of successful transactions. */ (*done)++; } if (!error) { COVERAGE_ADD(netlink_sent, n); } #endif return error; } static void nl_sock_transact_multiple(struct nl_sock *sock, struct nl_transaction **transactions, size_t n) { int max_batch_count; int error; if (!n) { return; } /* In theory, every request could have a 64 kB reply. But the default and * maximum socket rcvbuf size with typical Dom0 memory sizes both tend to * be a bit below 128 kB, so that would only allow a single message in a * "batch". So we assume that replies average (at most) 4 kB, which allows * a good deal of batching. * * In practice, most of the requests that we batch either have no reply at * all or a brief reply. */ max_batch_count = MAX(sock->rcvbuf / 4096, 1); max_batch_count = MIN(max_batch_count, max_iovs); while (n > 0) { size_t count, bytes; size_t done; /* Batch up to 'max_batch_count' transactions. But cap it at about a * page of requests total because big skbuffs are expensive to * allocate in the kernel. */ #if defined(PAGESIZE) enum { MAX_BATCH_BYTES = MAX(1, PAGESIZE - 512) }; #else enum { MAX_BATCH_BYTES = 4096 - 512 }; #endif bytes = transactions[0]->request->size; for (count = 1; count < n && count < max_batch_count; count++) { if (bytes + transactions[count]->request->size > MAX_BATCH_BYTES) { break; } bytes += transactions[count]->request->size; } error = nl_sock_transact_multiple__(sock, transactions, count, &done); transactions += done; n -= done; if (error == ENOBUFS) { VLOG_DBG_RL(&rl, "receive buffer overflow, resending request"); } else if (error) { VLOG_ERR_RL(&rl, "transaction error (%s)", ovs_strerror(error)); nl_sock_record_errors__(transactions, n, error); if (error != EAGAIN) { /* A fatal error has occurred. Abort the rest of * transactions. */ break; } } } } static int nl_sock_transact(struct nl_sock *sock, const struct ofpbuf *request, struct ofpbuf **replyp) { struct nl_transaction *transactionp; struct nl_transaction transaction; transaction.request = CONST_CAST(struct ofpbuf *, request); transaction.reply = replyp ? ofpbuf_new(1024) : NULL; transactionp = &transaction; nl_sock_transact_multiple(sock, &transactionp, 1); if (replyp) { if (transaction.error) { ofpbuf_delete(transaction.reply); *replyp = NULL; } else { *replyp = transaction.reply; } } return transaction.error; } /* Drain all the messages currently in 'sock''s receive queue. */ int nl_sock_drain(struct nl_sock *sock) { #ifdef _WIN32 return 0; #else return drain_rcvbuf(sock->fd); #endif } /* Starts a Netlink "dump" operation, by sending 'request' to the kernel on a * Netlink socket created with the given 'protocol', and initializes 'dump' to * reflect the state of the operation. * * 'request' must contain a Netlink message. Before sending the message, * nlmsg_len will be finalized to match request->size, and nlmsg_pid will be * set to the Netlink socket's pid. NLM_F_DUMP and NLM_F_ACK will be set in * nlmsg_flags. * * The design of this Netlink socket library ensures that the dump is reliable. * * This function provides no status indication. nl_dump_done() provides an * error status for the entire dump operation. * * The caller must eventually destroy 'request'. */ void nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request) { nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK; ovs_mutex_init(&dump->mutex); ovs_mutex_lock(&dump->mutex); dump->status = nl_pool_alloc(protocol, &dump->sock); if (!dump->status) { dump->status = nl_sock_send__(dump->sock, request, nl_sock_allocate_seq(dump->sock, 1), true); } dump->nl_seq = nl_msg_nlmsghdr(request)->nlmsg_seq; ovs_mutex_unlock(&dump->mutex); } static int nl_dump_refill(struct nl_dump *dump, struct ofpbuf *buffer) OVS_REQUIRES(dump->mutex) { struct nlmsghdr *nlmsghdr; int error; while (!buffer->size) { error = nl_sock_recv__(dump->sock, buffer, false); if (error) { /* The kernel never blocks providing the results of a dump, so * error == EAGAIN means that we've read the whole thing, and * therefore transform it into EOF. (The kernel always provides * NLMSG_DONE as a sentinel. Some other thread must have received * that already but not yet signaled it in 'status'.) * * Any other error is just an error. */ return error == EAGAIN ? EOF : error; } nlmsghdr = nl_msg_nlmsghdr(buffer); if (dump->nl_seq != nlmsghdr->nlmsg_seq) { VLOG_DBG_RL(&rl, "ignoring seq %#"PRIx32" != expected %#"PRIx32, nlmsghdr->nlmsg_seq, dump->nl_seq); ofpbuf_clear(buffer); } } if (nl_msg_nlmsgerr(buffer, &error) && error) { VLOG_INFO_RL(&rl, "netlink dump request error (%s)", ovs_strerror(error)); ofpbuf_clear(buffer); return error; } return 0; } static int nl_dump_next__(struct ofpbuf *reply, struct ofpbuf *buffer) { struct nlmsghdr *nlmsghdr = nl_msg_next(buffer, reply); if (!nlmsghdr) { VLOG_WARN_RL(&rl, "netlink dump contains message fragment"); return EPROTO; } else if (nlmsghdr->nlmsg_type == NLMSG_DONE) { return EOF; } else { return 0; } } /* Attempts to retrieve another reply from 'dump' into 'buffer'. 'dump' must * have been initialized with nl_dump_start(), and 'buffer' must have been * initialized. 'buffer' should be at least NL_DUMP_BUFSIZE bytes long. * * If successful, returns true and points 'reply->data' and * 'reply->size' to the message that was retrieved. The caller must not * modify 'reply' (because it points within 'buffer', which will be used by * future calls to this function). * * On failure, returns false and sets 'reply->data' to NULL and * 'reply->size' to 0. Failure might indicate an actual error or merely * the end of replies. An error status for the entire dump operation is * provided when it is completed by calling nl_dump_done(). * * Multiple threads may call this function, passing the same nl_dump, however * each must provide independent buffers. This function may cache multiple * replies in the buffer, and these will be processed before more replies are * fetched. When this function returns false, other threads may continue to * process replies in their buffers, but they will not fetch more replies. */ bool nl_dump_next(struct nl_dump *dump, struct ofpbuf *reply, struct ofpbuf *buffer) { int retval = 0; /* If the buffer is empty, refill it. * * If the buffer is not empty, we don't check the dump's status. * Otherwise, we could end up skipping some of the dump results if thread A * hits EOF while thread B is in the midst of processing a batch. */ if (!buffer->size) { ovs_mutex_lock(&dump->mutex); if (!dump->status) { /* Take the mutex here to avoid an in-kernel race. If two threads * try to read from a Netlink dump socket at once, then the socket * error can be set to EINVAL, which will be encountered on the * next recv on that socket, which could be anywhere due to the way * that we pool Netlink sockets. Serializing the recv calls avoids * the issue. */ dump->status = nl_dump_refill(dump, buffer); } retval = dump->status; ovs_mutex_unlock(&dump->mutex); } /* Fetch the next message from the buffer. */ if (!retval) { retval = nl_dump_next__(reply, buffer); if (retval) { /* Record 'retval' as the dump status, but don't overwrite an error * with EOF. */ ovs_mutex_lock(&dump->mutex); if (dump->status <= 0) { dump->status = retval; } ovs_mutex_unlock(&dump->mutex); } } if (retval) { reply->data = NULL; reply->size = 0; } return !retval; } /* Completes Netlink dump operation 'dump', which must have been initialized * with nl_dump_start(). Returns 0 if the dump operation was error-free, * otherwise a positive errno value describing the problem. */ int nl_dump_done(struct nl_dump *dump) { int status; ovs_mutex_lock(&dump->mutex); status = dump->status; ovs_mutex_unlock(&dump->mutex); /* Drain any remaining messages that the client didn't read. Otherwise the * kernel will continue to queue them up and waste buffer space. * * XXX We could just destroy and discard the socket in this case. */ if (!status) { uint64_t tmp_reply_stub[NL_DUMP_BUFSIZE / 8]; struct ofpbuf reply, buf; ofpbuf_use_stub(&buf, tmp_reply_stub, sizeof tmp_reply_stub); while (nl_dump_next(dump, &reply, &buf)) { /* Nothing to do. */ } ofpbuf_uninit(&buf); ovs_mutex_lock(&dump->mutex); status = dump->status; ovs_mutex_unlock(&dump->mutex); ovs_assert(status); } nl_pool_release(dump->sock); ovs_mutex_destroy(&dump->mutex); return status == EOF ? 0 : status; } #ifdef _WIN32 /* Pend an I/O request in the driver. The driver completes the I/O whenever * an event or a packet is ready to be read. Once the I/O is completed * the overlapped structure event associated with the pending I/O will be set */ static int pend_io_request(struct nl_sock *sock) { struct ofpbuf request; uint64_t request_stub[128]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; uint32_t seq; int retval = 0; int error; DWORD bytes; OVERLAPPED *overlapped = CONST_CAST(OVERLAPPED *, &sock->overlapped); uint16_t cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ; ovs_assert(sock->read_ioctl == OVS_IOCTL_READ_PACKET || sock->read_ioctl == OVS_IOCTL_READ_EVENT); if (sock->read_ioctl == OVS_IOCTL_READ_EVENT) { cmd = OVS_CTRL_CMD_WIN_PEND_REQ; } int ovs_msg_size = sizeof (struct nlmsghdr) + sizeof (struct genlmsghdr) + sizeof (struct ovs_header); ofpbuf_use_stub(&request, request_stub, sizeof request_stub); seq = nl_sock_allocate_seq(sock, 1); nl_msg_put_genlmsghdr(&request, 0, OVS_WIN_NL_CTRL_FAMILY_ID, 0, cmd, OVS_WIN_CONTROL_VERSION); nlmsg = nl_msg_nlmsghdr(&request); nlmsg->nlmsg_seq = seq; nlmsg->nlmsg_pid = sock->pid; ovs_header = ofpbuf_put_uninit(&request, sizeof *ovs_header); ovs_header->dp_ifindex = 0; if (!DeviceIoControl(sock->handle, OVS_IOCTL_WRITE, request.data, request.size, NULL, 0, &bytes, overlapped)) { error = GetLastError(); /* Check if the I/O got pended */ if (error != ERROR_IO_INCOMPLETE && error != ERROR_IO_PENDING) { VLOG_ERR("nl_sock_wait failed - %s\n", ovs_format_message(error)); retval = EINVAL; } } else { retval = EAGAIN; } done: ofpbuf_uninit(&request); return retval; } #endif /* _WIN32 */ /* Causes poll_block() to wake up when any of the specified 'events' (which is * a OR'd combination of POLLIN, POLLOUT, etc.) occur on 'sock'. * On Windows, 'sock' is not treated as const, and may be modified. */ void nl_sock_wait(const struct nl_sock *sock, short int events) { #ifdef _WIN32 if (sock->overlapped.Internal != STATUS_PENDING) { int ret = pend_io_request(CONST_CAST(struct nl_sock *, sock)); if (ret == 0) { poll_wevent_wait(sock->overlapped.hEvent); } else { poll_immediate_wake(); } } else { poll_wevent_wait(sock->overlapped.hEvent); } #else poll_fd_wait(sock->fd, events); #endif } #ifndef _WIN32 /* Returns the underlying fd for 'sock', for use in "poll()"-like operations * that can't use nl_sock_wait(). * * It's a little tricky to use the returned fd correctly, because nl_sock does * "copy on write" to allow a single nl_sock to be used for notifications, * transactions, and dumps. If 'sock' is used only for notifications and * transactions (and never for dump) then the usage is safe. */ int nl_sock_fd(const struct nl_sock *sock) { return sock->fd; } #endif /* Returns the PID associated with this socket. */ uint32_t nl_sock_pid(const struct nl_sock *sock) { return sock->pid; } /* Miscellaneous. */ struct genl_family { struct hmap_node hmap_node; uint16_t id; char *name; }; static struct hmap genl_families = HMAP_INITIALIZER(&genl_families); static const struct nl_policy family_policy[CTRL_ATTR_MAX + 1] = { [CTRL_ATTR_FAMILY_ID] = {.type = NL_A_U16}, [CTRL_ATTR_MCAST_GROUPS] = {.type = NL_A_NESTED, .optional = true}, }; static struct genl_family * find_genl_family_by_id(uint16_t id) { struct genl_family *family; HMAP_FOR_EACH_IN_BUCKET (family, hmap_node, hash_int(id, 0), &genl_families) { if (family->id == id) { return family; } } return NULL; } static void define_genl_family(uint16_t id, const char *name) { struct genl_family *family = find_genl_family_by_id(id); if (family) { if (!strcmp(family->name, name)) { return; } free(family->name); } else { family = xmalloc(sizeof *family); family->id = id; hmap_insert(&genl_families, &family->hmap_node, hash_int(id, 0)); } family->name = xstrdup(name); } static const char * genl_family_to_name(uint16_t id) { if (id == GENL_ID_CTRL) { return "control"; } else { struct genl_family *family = find_genl_family_by_id(id); return family ? family->name : "unknown"; } } #ifndef _WIN32 static int do_lookup_genl_family(const char *name, struct nlattr **attrs, struct ofpbuf **replyp) { struct nl_sock *sock; struct ofpbuf request, *reply; int error; *replyp = NULL; error = nl_sock_create(NETLINK_GENERIC, &sock); if (error) { return error; } ofpbuf_init(&request, 0); nl_msg_put_genlmsghdr(&request, 0, GENL_ID_CTRL, NLM_F_REQUEST, CTRL_CMD_GETFAMILY, 1); nl_msg_put_string(&request, CTRL_ATTR_FAMILY_NAME, name); error = nl_sock_transact(sock, &request, &reply); ofpbuf_uninit(&request); if (error) { nl_sock_destroy(sock); return error; } if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN, family_policy, attrs, ARRAY_SIZE(family_policy)) || nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]) == 0) { nl_sock_destroy(sock); ofpbuf_delete(reply); return EPROTO; } nl_sock_destroy(sock); *replyp = reply; return 0; } #else static int do_lookup_genl_family(const char *name, struct nlattr **attrs, struct ofpbuf **replyp) { struct nlmsghdr *nlmsg; struct ofpbuf *reply; int error; uint16_t family_id; const char *family_name; uint32_t family_version; uint32_t family_attrmax; uint32_t mcgrp_id = OVS_WIN_NL_INVALID_MCGRP_ID; const char *mcgrp_name = NULL; *replyp = NULL; reply = ofpbuf_new(1024); /* CTRL_ATTR_MCAST_GROUPS is supported only for VPORT family. */ if (!strcmp(name, OVS_WIN_CONTROL_FAMILY)) { family_id = OVS_WIN_NL_CTRL_FAMILY_ID; family_name = OVS_WIN_CONTROL_FAMILY; family_version = OVS_WIN_CONTROL_VERSION; family_attrmax = OVS_WIN_CONTROL_ATTR_MAX; } else if (!strcmp(name, OVS_DATAPATH_FAMILY)) { family_id = OVS_WIN_NL_DATAPATH_FAMILY_ID; family_name = OVS_DATAPATH_FAMILY; family_version = OVS_DATAPATH_VERSION; family_attrmax = OVS_DP_ATTR_MAX; } else if (!strcmp(name, OVS_PACKET_FAMILY)) { family_id = OVS_WIN_NL_PACKET_FAMILY_ID; family_name = OVS_PACKET_FAMILY; family_version = OVS_PACKET_VERSION; family_attrmax = OVS_PACKET_ATTR_MAX; } else if (!strcmp(name, OVS_VPORT_FAMILY)) { family_id = OVS_WIN_NL_VPORT_FAMILY_ID; family_name = OVS_VPORT_FAMILY; family_version = OVS_VPORT_VERSION; family_attrmax = OVS_VPORT_ATTR_MAX; mcgrp_id = OVS_WIN_NL_VPORT_MCGRP_ID; mcgrp_name = OVS_VPORT_MCGROUP; } else if (!strcmp(name, OVS_FLOW_FAMILY)) { family_id = OVS_WIN_NL_FLOW_FAMILY_ID; family_name = OVS_FLOW_FAMILY; family_version = OVS_FLOW_VERSION; family_attrmax = OVS_FLOW_ATTR_MAX; } else if (!strcmp(name, OVS_WIN_NETDEV_FAMILY)) { family_id = OVS_WIN_NL_NETDEV_FAMILY_ID; family_name = OVS_WIN_NETDEV_FAMILY; family_version = OVS_WIN_NETDEV_VERSION; family_attrmax = OVS_WIN_NETDEV_ATTR_MAX; } else { ofpbuf_delete(reply); return EINVAL; } nl_msg_put_genlmsghdr(reply, 0, GENL_ID_CTRL, 0, CTRL_CMD_NEWFAMILY, family_version); /* CTRL_ATTR_HDRSIZE and CTRL_ATTR_OPS are not populated, but the * callers do not seem to need them. */ nl_msg_put_u16(reply, CTRL_ATTR_FAMILY_ID, family_id); nl_msg_put_string(reply, CTRL_ATTR_FAMILY_NAME, family_name); nl_msg_put_u32(reply, CTRL_ATTR_VERSION, family_version); nl_msg_put_u32(reply, CTRL_ATTR_MAXATTR, family_attrmax); if (mcgrp_id != OVS_WIN_NL_INVALID_MCGRP_ID) { size_t mcgrp_ofs1 = nl_msg_start_nested(reply, CTRL_ATTR_MCAST_GROUPS); size_t mcgrp_ofs2= nl_msg_start_nested(reply, OVS_WIN_NL_VPORT_MCGRP_ID - OVS_WIN_NL_MCGRP_START_ID); nl_msg_put_u32(reply, CTRL_ATTR_MCAST_GRP_ID, mcgrp_id); ovs_assert(mcgrp_name != NULL); nl_msg_put_string(reply, CTRL_ATTR_MCAST_GRP_NAME, mcgrp_name); nl_msg_end_nested(reply, mcgrp_ofs2); nl_msg_end_nested(reply, mcgrp_ofs1); } /* Set the total length of the netlink message. */ nlmsg = nl_msg_nlmsghdr(reply); nlmsg->nlmsg_len = reply->size; if (!nl_policy_parse(reply, NLMSG_HDRLEN + GENL_HDRLEN, family_policy, attrs, ARRAY_SIZE(family_policy)) || nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]) == 0) { ofpbuf_delete(reply); return EPROTO; } *replyp = reply; return 0; } #endif /* Finds the multicast group called 'group_name' in genl family 'family_name'. * When successful, writes its result to 'multicast_group' and returns 0. * Otherwise, clears 'multicast_group' and returns a positive error code. */ int nl_lookup_genl_mcgroup(const char *family_name, const char *group_name, unsigned int *multicast_group) { struct nlattr *family_attrs[ARRAY_SIZE(family_policy)]; const struct nlattr *mc; struct ofpbuf *reply; unsigned int left; int error; *multicast_group = 0; error = do_lookup_genl_family(family_name, family_attrs, &reply); if (error) { return error; } if (!family_attrs[CTRL_ATTR_MCAST_GROUPS]) { error = EPROTO; goto exit; } NL_NESTED_FOR_EACH (mc, left, family_attrs[CTRL_ATTR_MCAST_GROUPS]) { static const struct nl_policy mc_policy[] = { [CTRL_ATTR_MCAST_GRP_ID] = {.type = NL_A_U32}, [CTRL_ATTR_MCAST_GRP_NAME] = {.type = NL_A_STRING}, }; struct nlattr *mc_attrs[ARRAY_SIZE(mc_policy)]; const char *mc_name; if (!nl_parse_nested(mc, mc_policy, mc_attrs, ARRAY_SIZE(mc_policy))) { error = EPROTO; goto exit; } mc_name = nl_attr_get_string(mc_attrs[CTRL_ATTR_MCAST_GRP_NAME]); if (!strcmp(group_name, mc_name)) { *multicast_group = nl_attr_get_u32(mc_attrs[CTRL_ATTR_MCAST_GRP_ID]); error = 0; goto exit; } } error = EPROTO; exit: ofpbuf_delete(reply); return error; } /* If '*number' is 0, translates the given Generic Netlink family 'name' to a * number and stores it in '*number'. If successful, returns 0 and the caller * may use '*number' as the family number. On failure, returns a positive * errno value and '*number' caches the errno value. */ int nl_lookup_genl_family(const char *name, int *number) { if (*number == 0) { struct nlattr *attrs[ARRAY_SIZE(family_policy)]; struct ofpbuf *reply; int error; error = do_lookup_genl_family(name, attrs, &reply); if (!error) { *number = nl_attr_get_u16(attrs[CTRL_ATTR_FAMILY_ID]); define_genl_family(*number, name); } else { *number = -error; } ofpbuf_delete(reply); ovs_assert(*number != 0); } return *number > 0 ? 0 : -*number; } struct nl_pool { struct nl_sock *socks[16]; int n; }; static struct ovs_mutex pool_mutex = OVS_MUTEX_INITIALIZER; static struct nl_pool pools[MAX_LINKS] OVS_GUARDED_BY(pool_mutex); static int nl_pool_alloc(int protocol, struct nl_sock **sockp) { struct nl_sock *sock = NULL; struct nl_pool *pool; ovs_assert(protocol >= 0 && protocol < ARRAY_SIZE(pools)); ovs_mutex_lock(&pool_mutex); pool = &pools[protocol]; if (pool->n > 0) { sock = pool->socks[--pool->n]; } ovs_mutex_unlock(&pool_mutex); if (sock) { *sockp = sock; return 0; } else { return nl_sock_create(protocol, sockp); } } static void nl_pool_release(struct nl_sock *sock) { if (sock) { struct nl_pool *pool = &pools[sock->protocol]; ovs_mutex_lock(&pool_mutex); if (pool->n < ARRAY_SIZE(pool->socks)) { pool->socks[pool->n++] = sock; sock = NULL; } ovs_mutex_unlock(&pool_mutex); nl_sock_destroy(sock); } } /* Sends 'request' to the kernel on a Netlink socket for the given 'protocol' * (e.g. NETLINK_ROUTE or NETLINK_GENERIC) and waits for a response. If * successful, returns 0. On failure, returns a positive errno value. * * If 'replyp' is nonnull, then on success '*replyp' is set to the kernel's * reply, which the caller is responsible for freeing with ofpbuf_delete(), and * on failure '*replyp' is set to NULL. If 'replyp' is null, then the kernel's * reply, if any, is discarded. * * Before the message is sent, nlmsg_len in 'request' will be finalized to * match msg->size, nlmsg_pid will be set to the pid of the socket used * for sending the request, and nlmsg_seq will be initialized. * * The caller is responsible for destroying 'request'. * * Bare Netlink is an unreliable transport protocol. This function layers * reliable delivery and reply semantics on top of bare Netlink. * * In Netlink, sending a request to the kernel is reliable enough, because the * kernel will tell us if the message cannot be queued (and we will in that * case put it on the transmit queue and wait until it can be delivered). * * Receiving the reply is the real problem: if the socket buffer is full when * the kernel tries to send the reply, the reply will be dropped. However, the * kernel sets a flag that a reply has been dropped. The next call to recv * then returns ENOBUFS. We can then re-send the request. * * Caveats: * * 1. Netlink depends on sequence numbers to match up requests and * replies. The sender of a request supplies a sequence number, and * the reply echos back that sequence number. * * This is fine, but (1) some kernel netlink implementations are * broken, in that they fail to echo sequence numbers and (2) this * function will drop packets with non-matching sequence numbers, so * that only a single request can be usefully transacted at a time. * * 2. Resending the request causes it to be re-executed, so the request * needs to be idempotent. */ int nl_transact(int protocol, const struct ofpbuf *request, struct ofpbuf **replyp) { struct nl_sock *sock; int error; error = nl_pool_alloc(protocol, &sock); if (error) { *replyp = NULL; return error; } error = nl_sock_transact(sock, request, replyp); nl_pool_release(sock); return error; } /* Sends the 'request' member of the 'n' transactions in 'transactions' on a * Netlink socket for the given 'protocol' (e.g. NETLINK_ROUTE or * NETLINK_GENERIC), in order, and receives responses to all of them. Fills in * the 'error' member of each transaction with 0 if it was successful, * otherwise with a positive errno value. If 'reply' is nonnull, then it will * be filled with the reply if the message receives a detailed reply. In other * cases, i.e. where the request failed or had no reply beyond an indication of * success, 'reply' will be cleared if it is nonnull. * * The caller is responsible for destroying each request and reply, and the * transactions array itself. * * Before sending each message, this function will finalize nlmsg_len in each * 'request' to match the ofpbuf's size, set nlmsg_pid to the pid of the socket * used for the transaction, and initialize nlmsg_seq. * * Bare Netlink is an unreliable transport protocol. This function layers * reliable delivery and reply semantics on top of bare Netlink. See * nl_transact() for some caveats. */ void nl_transact_multiple(int protocol, struct nl_transaction **transactions, size_t n) { struct nl_sock *sock; int error; error = nl_pool_alloc(protocol, &sock); if (!error) { nl_sock_transact_multiple(sock, transactions, n); nl_pool_release(sock); } else { nl_sock_record_errors__(transactions, n, error); } } static uint32_t nl_sock_allocate_seq(struct nl_sock *sock, unsigned int n) { uint32_t seq = sock->next_seq; sock->next_seq += n; /* Make it impossible for the next request for sequence numbers to wrap * around to 0. Start over with 1 to avoid ever using a sequence number of * 0, because the kernel uses sequence number 0 for notifications. */ if (sock->next_seq >= UINT32_MAX / 2) { sock->next_seq = 1; } return seq; } static void nlmsghdr_to_string(const struct nlmsghdr *h, int protocol, struct ds *ds) { struct nlmsg_flag { unsigned int bits; const char *name; }; static const struct nlmsg_flag flags[] = { { NLM_F_REQUEST, "REQUEST" }, { NLM_F_MULTI, "MULTI" }, { NLM_F_ACK, "ACK" }, { NLM_F_ECHO, "ECHO" }, { NLM_F_DUMP, "DUMP" }, { NLM_F_ROOT, "ROOT" }, { NLM_F_MATCH, "MATCH" }, { NLM_F_ATOMIC, "ATOMIC" }, }; const struct nlmsg_flag *flag; uint16_t flags_left; ds_put_format(ds, "nl(len:%"PRIu32", type=%"PRIu16, h->nlmsg_len, h->nlmsg_type); if (h->nlmsg_type == NLMSG_NOOP) { ds_put_cstr(ds, "(no-op)"); } else if (h->nlmsg_type == NLMSG_ERROR) { ds_put_cstr(ds, "(error)"); } else if (h->nlmsg_type == NLMSG_DONE) { ds_put_cstr(ds, "(done)"); } else if (h->nlmsg_type == NLMSG_OVERRUN) { ds_put_cstr(ds, "(overrun)"); } else if (h->nlmsg_type < NLMSG_MIN_TYPE) { ds_put_cstr(ds, "(reserved)"); } else if (protocol == NETLINK_GENERIC) { ds_put_format(ds, "(%s)", genl_family_to_name(h->nlmsg_type)); } else { ds_put_cstr(ds, "(family-defined)"); } ds_put_format(ds, ", flags=%"PRIx16, h->nlmsg_flags); flags_left = h->nlmsg_flags; for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) { if ((flags_left & flag->bits) == flag->bits) { ds_put_format(ds, "[%s]", flag->name); flags_left &= ~flag->bits; } } if (flags_left) { ds_put_format(ds, "[OTHER:%"PRIx16"]", flags_left); } ds_put_format(ds, ", seq=%"PRIx32", pid=%"PRIu32, h->nlmsg_seq, h->nlmsg_pid); } static char * nlmsg_to_string(const struct ofpbuf *buffer, int protocol) { struct ds ds = DS_EMPTY_INITIALIZER; const struct nlmsghdr *h = ofpbuf_at(buffer, 0, NLMSG_HDRLEN); if (h) { nlmsghdr_to_string(h, protocol, &ds); if (h->nlmsg_type == NLMSG_ERROR) { const struct nlmsgerr *e; e = ofpbuf_at(buffer, NLMSG_HDRLEN, NLMSG_ALIGN(sizeof(struct nlmsgerr))); if (e) { ds_put_format(&ds, " error(%d", e->error); if (e->error < 0) { ds_put_format(&ds, "(%s)", ovs_strerror(-e->error)); } ds_put_cstr(&ds, ", in-reply-to("); nlmsghdr_to_string(&e->msg, protocol, &ds); ds_put_cstr(&ds, "))"); } else { ds_put_cstr(&ds, " error(truncated)"); } } else if (h->nlmsg_type == NLMSG_DONE) { int *error = ofpbuf_at(buffer, NLMSG_HDRLEN, sizeof *error); if (error) { ds_put_format(&ds, " done(%d", *error); if (*error < 0) { ds_put_format(&ds, "(%s)", ovs_strerror(-*error)); } ds_put_cstr(&ds, ")"); } else { ds_put_cstr(&ds, " done(truncated)"); } } else if (protocol == NETLINK_GENERIC) { struct genlmsghdr *genl = nl_msg_genlmsghdr(buffer); if (genl) { ds_put_format(&ds, ",genl(cmd=%"PRIu8",version=%"PRIu8")", genl->cmd, genl->version); } } } else { ds_put_cstr(&ds, "nl(truncated)"); } return ds.string; } static void log_nlmsg(const char *function, int error, const void *message, size_t size, int protocol) { struct ofpbuf buffer; char *nlmsg; if (!VLOG_IS_DBG_ENABLED()) { return; } ofpbuf_use_const(&buffer, message, size); nlmsg = nlmsg_to_string(&buffer, protocol); VLOG_DBG_RL(&rl, "%s (%s): %s", function, ovs_strerror(error), nlmsg); free(nlmsg); } openvswitch-2.5.9/lib/PaxHeaders.82075/if-notifier.h0000644000000000000000000000013213534540071016776 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.973854841 openvswitch-2.5.9/lib/if-notifier.h0000644000175000017500000000162413534540071020467 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef IF_NOTIFIER_H #define IF_NOTIFIER_H 1 struct if_notifier; typedef void if_notify_func(void *aux); struct if_notifier *if_notifier_create(if_notify_func *, void *aux); void if_notifier_destroy(struct if_notifier *); void if_notifier_run(void); void if_notifier_wait(void); #endif /* if-notifier.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-types.c0000644000000000000000000000013213534540071017035 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.841853868 openvswitch-2.5.9/lib/ovsdb-types.c0000644000175000017500000004763013534540071020535 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb-types.h" #include #include #include "dynamic-string.h" #include "json.h" #include "ovs-thread.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-parser.h" const struct ovsdb_type ovsdb_type_integer = OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_INTEGER_INIT); const struct ovsdb_type ovsdb_type_real = OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_REAL_INIT); const struct ovsdb_type ovsdb_type_boolean = OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_BOOLEAN_INIT); const struct ovsdb_type ovsdb_type_string = OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_STRING_INIT); const struct ovsdb_type ovsdb_type_uuid = OVSDB_TYPE_SCALAR_INITIALIZER(OVSDB_BASE_UUID_INIT); /* ovsdb_atomic_type */ const char * ovsdb_atomic_type_to_string(enum ovsdb_atomic_type type) { switch (type) { case OVSDB_TYPE_VOID: return "void"; case OVSDB_TYPE_INTEGER: return "integer"; case OVSDB_TYPE_REAL: return "real"; case OVSDB_TYPE_BOOLEAN: return "boolean"; case OVSDB_TYPE_STRING: return "string"; case OVSDB_TYPE_UUID: return "uuid"; case OVSDB_N_TYPES: default: return ""; } } struct json * ovsdb_atomic_type_to_json(enum ovsdb_atomic_type type) { return json_string_create(ovsdb_atomic_type_to_string(type)); } bool ovsdb_atomic_type_from_string(const char *string, enum ovsdb_atomic_type *type) { if (!strcmp(string, "integer")) { *type = OVSDB_TYPE_INTEGER; } else if (!strcmp(string, "real")) { *type = OVSDB_TYPE_REAL; } else if (!strcmp(string, "boolean")) { *type = OVSDB_TYPE_BOOLEAN; } else if (!strcmp(string, "string")) { *type = OVSDB_TYPE_STRING; } else if (!strcmp(string, "uuid")) { *type = OVSDB_TYPE_UUID; } else { return false; } return true; } struct ovsdb_error * ovsdb_atomic_type_from_json(enum ovsdb_atomic_type *type, const struct json *json) { if (json->type == JSON_STRING) { if (ovsdb_atomic_type_from_string(json_string(json), type)) { return NULL; } else { *type = OVSDB_TYPE_VOID; return ovsdb_syntax_error(json, NULL, "\"%s\" is not an atomic-type", json_string(json)); } } else { *type = OVSDB_TYPE_VOID; return ovsdb_syntax_error(json, NULL, "atomic-type expected"); } } /* ovsdb_base_type */ void ovsdb_base_type_init(struct ovsdb_base_type *base, enum ovsdb_atomic_type type) { base->type = type; base->enum_ = NULL; switch (base->type) { case OVSDB_TYPE_VOID: break; case OVSDB_TYPE_INTEGER: base->u.integer.min = INT64_MIN; base->u.integer.max = INT64_MAX; break; case OVSDB_TYPE_REAL: base->u.real.min = -DBL_MAX; base->u.real.max = DBL_MAX; break; case OVSDB_TYPE_BOOLEAN: break; case OVSDB_TYPE_STRING: base->u.string.minLen = 0; base->u.string.maxLen = UINT_MAX; break; case OVSDB_TYPE_UUID: base->u.uuid.refTableName = NULL; base->u.uuid.refTable = NULL; break; case OVSDB_N_TYPES: OVS_NOT_REACHED(); default: OVS_NOT_REACHED(); } } /* Returns the type of the 'enum_' member for an ovsdb_base_type whose 'type' * is 'atomic_type'. */ const struct ovsdb_type * ovsdb_base_type_get_enum_type(enum ovsdb_atomic_type atomic_type) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static struct ovsdb_type *types[OVSDB_N_TYPES]; if (ovsthread_once_start(&once)) { enum ovsdb_atomic_type i; for (i = 0; i < OVSDB_N_TYPES; i++) { struct ovsdb_type *type; types[i] = type = xmalloc(sizeof *type); ovsdb_base_type_init(&type->key, i); ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); type->n_min = 1; type->n_max = UINT_MAX; } ovsthread_once_done(&once); } return types[atomic_type]; } void ovsdb_base_type_clone(struct ovsdb_base_type *dst, const struct ovsdb_base_type *src) { *dst = *src; if (src->enum_) { dst->enum_ = xmalloc(sizeof *dst->enum_); ovsdb_datum_clone(dst->enum_, src->enum_, ovsdb_base_type_get_enum_type(dst->type)); } switch (dst->type) { case OVSDB_TYPE_VOID: case OVSDB_TYPE_INTEGER: case OVSDB_TYPE_REAL: case OVSDB_TYPE_BOOLEAN: break; case OVSDB_TYPE_STRING: break; case OVSDB_TYPE_UUID: if (dst->u.uuid.refTableName) { dst->u.uuid.refTableName = xstrdup(dst->u.uuid.refTableName); } break; case OVSDB_N_TYPES: default: OVS_NOT_REACHED(); } } void ovsdb_base_type_destroy(struct ovsdb_base_type *base) { if (base) { if (base->enum_) { ovsdb_datum_destroy(base->enum_, ovsdb_base_type_get_enum_type(base->type)); free(base->enum_); } switch (base->type) { case OVSDB_TYPE_VOID: case OVSDB_TYPE_INTEGER: case OVSDB_TYPE_REAL: case OVSDB_TYPE_BOOLEAN: break; case OVSDB_TYPE_STRING: break; case OVSDB_TYPE_UUID: free(base->u.uuid.refTableName); break; case OVSDB_N_TYPES: OVS_NOT_REACHED(); default: OVS_NOT_REACHED(); } } } bool ovsdb_base_type_is_valid(const struct ovsdb_base_type *base) { switch (base->type) { case OVSDB_TYPE_VOID: return true; case OVSDB_TYPE_INTEGER: return base->u.integer.min <= base->u.integer.max; case OVSDB_TYPE_REAL: return base->u.real.min <= base->u.real.max; case OVSDB_TYPE_BOOLEAN: return true; case OVSDB_TYPE_STRING: return base->u.string.minLen <= base->u.string.maxLen; case OVSDB_TYPE_UUID: return true; case OVSDB_N_TYPES: default: return false; } } bool ovsdb_base_type_has_constraints(const struct ovsdb_base_type *base) { if (base->enum_) { return true; } switch (base->type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: return (base->u.integer.min != INT64_MIN || base->u.integer.max != INT64_MAX); case OVSDB_TYPE_REAL: return (base->u.real.min != -DBL_MAX || base->u.real.max != DBL_MAX); case OVSDB_TYPE_BOOLEAN: return false; case OVSDB_TYPE_STRING: return base->u.string.minLen != 0 || base->u.string.maxLen != UINT_MAX; case OVSDB_TYPE_UUID: return base->u.uuid.refTableName != NULL; case OVSDB_N_TYPES: OVS_NOT_REACHED(); default: OVS_NOT_REACHED(); } } void ovsdb_base_type_clear_constraints(struct ovsdb_base_type *base) { enum ovsdb_atomic_type type = base->type; ovsdb_base_type_destroy(base); ovsdb_base_type_init(base, type); } static struct ovsdb_error * parse_optional_uint(struct ovsdb_parser *parser, const char *member, unsigned int *uint) { const struct json *json; json = ovsdb_parser_member(parser, member, OP_INTEGER | OP_OPTIONAL); if (json) { if (json->u.integer < 0 || json->u.integer > UINT_MAX) { return ovsdb_syntax_error(json, NULL, "%s out of valid range 0 to %u", member, UINT_MAX); } *uint = json->u.integer; } return NULL; } struct ovsdb_error * ovsdb_base_type_from_json(struct ovsdb_base_type *base, const struct json *json) { struct ovsdb_parser parser; struct ovsdb_error *error; const struct json *type, *enum_; if (json->type == JSON_STRING) { error = ovsdb_atomic_type_from_json(&base->type, json); if (error) { return error; } ovsdb_base_type_init(base, base->type); return NULL; } ovsdb_parser_init(&parser, json, "ovsdb type"); type = ovsdb_parser_member(&parser, "type", OP_STRING); if (ovsdb_parser_has_error(&parser)) { base->type = OVSDB_TYPE_VOID; return ovsdb_parser_finish(&parser); } error = ovsdb_atomic_type_from_json(&base->type, type); if (error) { ovsdb_error_destroy(ovsdb_parser_destroy(&parser)); return error; } ovsdb_base_type_init(base, base->type); enum_ = ovsdb_parser_member(&parser, "enum", OP_ANY | OP_OPTIONAL); if (enum_) { base->enum_ = xmalloc(sizeof *base->enum_); error = ovsdb_datum_from_json( base->enum_, ovsdb_base_type_get_enum_type(base->type), enum_, NULL); if (error) { free(base->enum_); base->enum_ = NULL; } } else if (base->type == OVSDB_TYPE_INTEGER) { const struct json *min, *max; min = ovsdb_parser_member(&parser, "minInteger", OP_INTEGER | OP_OPTIONAL); max = ovsdb_parser_member(&parser, "maxInteger", OP_INTEGER | OP_OPTIONAL); base->u.integer.min = min ? min->u.integer : INT64_MIN; base->u.integer.max = max ? max->u.integer : INT64_MAX; if (base->u.integer.min > base->u.integer.max) { error = ovsdb_syntax_error(json, NULL, "minInteger exceeds maxInteger"); } } else if (base->type == OVSDB_TYPE_REAL) { const struct json *min, *max; min = ovsdb_parser_member(&parser, "minReal", OP_NUMBER | OP_OPTIONAL); max = ovsdb_parser_member(&parser, "maxReal", OP_NUMBER | OP_OPTIONAL); base->u.real.min = min ? json_real(min) : -DBL_MAX; base->u.real.max = max ? json_real(max) : DBL_MAX; if (base->u.real.min > base->u.real.max) { error = ovsdb_syntax_error(json, NULL, "minReal exceeds maxReal"); } } else if (base->type == OVSDB_TYPE_STRING) { if (!error) { error = parse_optional_uint(&parser, "minLength", &base->u.string.minLen); } if (!error) { error = parse_optional_uint(&parser, "maxLength", &base->u.string.maxLen); } if (!error && base->u.string.minLen > base->u.string.maxLen) { error = ovsdb_syntax_error(json, NULL, "minLength exceeds maxLength"); } } else if (base->type == OVSDB_TYPE_UUID) { const struct json *refTable; refTable = ovsdb_parser_member(&parser, "refTable", OP_ID | OP_OPTIONAL); if (refTable) { const struct json *refType; base->u.uuid.refTableName = xstrdup(refTable->u.string); /* We can't set base->u.uuid.refTable here because we don't have * enough context (we might not even be running in ovsdb-server). * ovsdb_create() will set refTable later. */ refType = ovsdb_parser_member(&parser, "refType", OP_ID | OP_OPTIONAL); if (refType) { const char *refType_s = json_string(refType); if (!strcmp(refType_s, "strong")) { base->u.uuid.refType = OVSDB_REF_STRONG; } else if (!strcmp(refType_s, "weak")) { base->u.uuid.refType = OVSDB_REF_WEAK; } else { error = ovsdb_syntax_error(json, NULL, "refType must be " "\"strong\" or \"weak\" (not " "\"%s\")", refType_s); } } else { base->u.uuid.refType = OVSDB_REF_STRONG; } } } if (error) { ovsdb_error_destroy(ovsdb_parser_finish(&parser)); } else { error = ovsdb_parser_finish(&parser); } if (error) { ovsdb_base_type_destroy(base); base->type = OVSDB_TYPE_VOID; } return error; } struct json * ovsdb_base_type_to_json(const struct ovsdb_base_type *base) { struct json *json; if (!ovsdb_base_type_has_constraints(base)) { return json_string_create(ovsdb_atomic_type_to_string(base->type)); } json = json_object_create(); json_object_put_string(json, "type", ovsdb_atomic_type_to_string(base->type)); if (base->enum_) { const struct ovsdb_type *type; type = ovsdb_base_type_get_enum_type(base->type); json_object_put(json, "enum", ovsdb_datum_to_json(base->enum_, type)); } switch (base->type) { case OVSDB_TYPE_VOID: OVS_NOT_REACHED(); case OVSDB_TYPE_INTEGER: if (base->u.integer.min != INT64_MIN) { json_object_put(json, "minInteger", json_integer_create(base->u.integer.min)); } if (base->u.integer.max != INT64_MAX) { json_object_put(json, "maxInteger", json_integer_create(base->u.integer.max)); } break; case OVSDB_TYPE_REAL: if (base->u.real.min != -DBL_MAX) { json_object_put(json, "minReal", json_real_create(base->u.real.min)); } if (base->u.real.max != DBL_MAX) { json_object_put(json, "maxReal", json_real_create(base->u.real.max)); } break; case OVSDB_TYPE_BOOLEAN: break; case OVSDB_TYPE_STRING: if (base->u.string.minLen != 0) { json_object_put(json, "minLength", json_integer_create(base->u.string.minLen)); } if (base->u.string.maxLen != UINT_MAX) { json_object_put(json, "maxLength", json_integer_create(base->u.string.maxLen)); } break; case OVSDB_TYPE_UUID: if (base->u.uuid.refTableName) { json_object_put_string(json, "refTable", base->u.uuid.refTableName); if (base->u.uuid.refType == OVSDB_REF_WEAK) { json_object_put_string(json, "refType", "weak"); } } break; case OVSDB_N_TYPES: OVS_NOT_REACHED(); default: OVS_NOT_REACHED(); } return json; } /* ovsdb_type */ void ovsdb_type_clone(struct ovsdb_type *dst, const struct ovsdb_type *src) { ovsdb_base_type_clone(&dst->key, &src->key); ovsdb_base_type_clone(&dst->value, &src->value); dst->n_min = src->n_min; dst->n_max = src->n_max; } void ovsdb_type_destroy(struct ovsdb_type *type) { ovsdb_base_type_destroy(&type->key); ovsdb_base_type_destroy(&type->value); } bool ovsdb_type_is_valid(const struct ovsdb_type *type) { return (type->key.type != OVSDB_TYPE_VOID && ovsdb_base_type_is_valid(&type->key) && ovsdb_base_type_is_valid(&type->value) && type->n_min <= 1 && type->n_max >= 1); } static struct ovsdb_error * n_from_json(const struct json *json, unsigned int *n) { if (!json) { return NULL; } else if (json->type == JSON_INTEGER && json->u.integer >= 0 && json->u.integer < UINT_MAX) { *n = json->u.integer; return NULL; } else { return ovsdb_syntax_error(json, NULL, "bad min or max value"); } } char * ovsdb_type_to_english(const struct ovsdb_type *type) { const char *key = ovsdb_atomic_type_to_string(type->key.type); const char *value = ovsdb_atomic_type_to_string(type->value.type); if (ovsdb_type_is_scalar(type)) { return xstrdup(key); } else { struct ds s = DS_EMPTY_INITIALIZER; ds_put_cstr(&s, ovsdb_type_is_set(type) ? "set" : "map"); if (type->n_max == UINT_MAX) { if (type->n_min) { ds_put_format(&s, " of %u or more", type->n_min); } else { ds_put_cstr(&s, " of"); } } else if (type->n_min) { ds_put_format(&s, " of %u to %u", type->n_min, type->n_max); } else { ds_put_format(&s, " of up to %u", type->n_max); } if (ovsdb_type_is_set(type)) { ds_put_format(&s, " %ss", key); } else { ds_put_format(&s, " (%s, %s) pairs", key, value); } return ds_cstr(&s); } } struct ovsdb_error * ovsdb_type_from_json(struct ovsdb_type *type, const struct json *json) { ovsdb_base_type_init(&type->value, OVSDB_TYPE_VOID); type->n_min = 1; type->n_max = 1; if (json->type == JSON_STRING) { return ovsdb_base_type_from_json(&type->key, json); } else if (json->type == JSON_OBJECT) { const struct json *key, *value, *min, *max; struct ovsdb_error *error; struct ovsdb_parser parser; ovsdb_parser_init(&parser, json, "ovsdb type"); key = ovsdb_parser_member(&parser, "key", OP_STRING | OP_OBJECT); value = ovsdb_parser_member(&parser, "value", OP_STRING | OP_OBJECT | OP_OPTIONAL); min = ovsdb_parser_member(&parser, "min", OP_INTEGER | OP_OPTIONAL); max = ovsdb_parser_member(&parser, "max", OP_INTEGER | OP_STRING | OP_OPTIONAL); error = ovsdb_parser_finish(&parser); if (error) { return error; } error = ovsdb_base_type_from_json(&type->key, key); if (error) { return error; } if (value) { error = ovsdb_base_type_from_json(&type->value, value); if (error) { return error; } } error = n_from_json(min, &type->n_min); if (error) { return error; } if (max && max->type == JSON_STRING && !strcmp(max->u.string, "unlimited")) { type->n_max = UINT_MAX; } else { error = n_from_json(max, &type->n_max); if (error) { return error; } } if (!ovsdb_type_is_valid(type)) { return ovsdb_syntax_error(json, NULL, "ovsdb type fails constraint checks"); } return NULL; } else { return ovsdb_syntax_error(json, NULL, "ovsdb type expected"); } } struct json * ovsdb_type_to_json(const struct ovsdb_type *type) { if (ovsdb_type_is_scalar(type) && !ovsdb_base_type_has_constraints(&type->key)) { return ovsdb_base_type_to_json(&type->key); } else { struct json *json = json_object_create(); json_object_put(json, "key", ovsdb_base_type_to_json(&type->key)); if (type->value.type != OVSDB_TYPE_VOID) { json_object_put(json, "value", ovsdb_base_type_to_json(&type->value)); } if (type->n_min != 1) { json_object_put(json, "min", json_integer_create(type->n_min)); } if (type->n_max == UINT_MAX) { json_object_put_string(json, "max", "unlimited"); } else if (type->n_max != 1) { json_object_put(json, "max", json_integer_create(type->n_max)); } return json; } } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-lldp.c0000644000000000000000000000013213534540071016316 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 30 ctime=1567801424.821853721 openvswitch-2.5.9/lib/ovs-lldp.c0000644000175000017500000006606413534540071020020 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * Copyright (c) 2014 WindRiver, Inc. * Copyright (c) 2015 Avaya, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Implementation of Auto Attach. * Based on sample implementation in 802.1ab. Above copyright and license * applies to all modifications. * Limitations: * - No support for multiple bridge. * - Auto Attach state machine not implemented. * - Auto Attach and LLDP code are bundled together. The plan is to decoupled * them. */ #include #include "ovs-lldp.h" #include #include #include #include #include #include #include "dynamic-string.h" #include "flow.h" #include "list.h" #include "lldp/lldpd.h" #include "lldp/lldpd-structs.h" #include "netdev.h" #include "openvswitch/types.h" #include "packets.h" #include "poll-loop.h" #include "smap.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovs_lldp); #define LLDP_PROTOCOL_ID 0x0000 #define LLDP_PROTOCOL_VERSION 0x00 #define LLDP_TYPE_CONFIG 0x00 #define LLDP_CHASSIS_TTL 120 #define ETH_TYPE_LLDP 0x88cc #define MINIMUM_ETH_PACKET_SIZE 68 #define AA_STATUS_MULTIPLE \ AA_STATUS(ACTIVE,2,Active) \ AA_STATUS(REJECT_GENERIC,3,Reject (Generic)) \ AA_STATUS(REJECT_AA_RES_NOTAVAIL,4,Reject (AA resources unavailable)) \ AA_STATUS(REJECT_INVALID,6,Reject (Invalid)) \ AA_STATUS(REJECT_VLAN_RES_UNAVAIL,8,Reject (VLAN resources unavailable)) \ AA_STATUS(REJECT_VLAN_APP_ISSUE,9,Reject (Application interaction issue)) \ AA_STATUS(PENDING,255,Pending) enum aa_status { #define AA_STATUS(NAME, VALUE, STR) AA_STATUS_##NAME = VALUE, AA_STATUS_MULTIPLE #undef AA_STATUS AA_STATUS_N_MULTIPLE }; /* Internal structure for an Auto Attach mapping. */ struct aa_mapping_internal { struct hmap_node hmap_node_isid; struct hmap_node hmap_node_aux; uint32_t isid; uint16_t vlan; void *aux; enum aa_status status; }; static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; /* Hash map of all LLDP instances keyed by name (port at the moment). */ static struct hmap all_lldps__ = HMAP_INITIALIZER(&all_lldps__); static struct hmap *const all_lldps OVS_GUARDED_BY(mutex) = &all_lldps__; /* Hash map of all the Auto Attach mappings. Global at the moment (but will * be per bridge). Used when adding a new port to a bridge so that we can * properly install all the configured mapping on the port and export them * To the Auto Attach server via LLDP. */ static struct hmap all_mappings__ = HMAP_INITIALIZER(&all_mappings__); static struct hmap *const all_mappings OVS_GUARDED_BY(mutex) = &all_mappings__; static struct lldp_aa_element_system_id system_id_null; /* Convert an LLDP chassis ID to a string. */ static void chassisid_to_string(uint8_t *array, size_t len, char **str) { unsigned int i; *str = xmalloc(len * 3); for (i = 0; i < len; i++) { snprintf(&(*str)[i * 3], 4, "%02x:", array[i]); } (*str)[(i * 3) - 1] = '\0'; } /* Find an Auto Attach mapping keyed by I-SID. */ static struct aa_mapping_internal * mapping_find_by_isid(struct lldp *lldp, uint32_t isid) OVS_REQUIRES(mutex) { struct aa_mapping_internal *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node_isid, hash_int(isid, 0), &lldp->mappings_by_isid) { if (isid == m->isid) { return m; } } return NULL; } /* Find an Auto Attach mapping keyed by aux. aux is an opaque pointer created * by the bridge that refers to an OVSDB mapping record. */ static struct aa_mapping_internal * mapping_find_by_aux(struct lldp *lldp, const void *aux) OVS_REQUIRES(mutex) { struct aa_mapping_internal *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node_aux, hash_pointer(aux, 0), &lldp->mappings_by_aux) { if (aux == m->aux) { return m; } } return NULL; } /* Convert an Auto Attach request status to a string. */ static char * aa_status_to_str(uint8_t status) { switch (status) { #define AA_STATUS(NAME, VALUE, STR) case AA_STATUS_##NAME: return #STR; AA_STATUS_MULTIPLE #undef AA_STATUS default: return "Undefined"; } } /* Display LLDP and Auto Attach statistics. */ static void aa_print_lldp_and_aa_stats(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex) { struct lldpd_hardware *hw; ds_put_format(ds, "Statistics: %s\n", lldp->name); if (!lldp->lldpd) { return; } LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) { ds_put_format(ds, "\ttx cnt: %"PRIu64"\n", hw->h_tx_cnt); ds_put_format(ds, "\trx cnt: %"PRIu64"\n", hw->h_rx_cnt); ds_put_format(ds, "\trx discarded cnt: %"PRIu64"\n", hw->h_rx_discarded_cnt); ds_put_format(ds, "\trx unrecognized cnt: %"PRIu64"\n", hw->h_rx_unrecognized_cnt); ds_put_format(ds, "\tageout cnt: %"PRIu64"\n", hw->h_ageout_cnt); ds_put_format(ds, "\tinsert cnt: %"PRIu64"\n", hw->h_insert_cnt); ds_put_format(ds, "\tdelete cnt: %"PRIu64"\n", hw->h_delete_cnt); ds_put_format(ds, "\tdrop cnt: %"PRIu64"\n", hw->h_drop_cnt); } } static void aa_print_element_status_port(struct ds *ds, struct lldpd_hardware *hw) { struct lldpd_port *port; LIST_FOR_EACH (port, p_entries, &hw->h_rports) { if (memcmp(&port->p_element.system_id, &system_id_null, sizeof port->p_element.system_id)) { const char *none_str = ""; const char *descr = NULL; char *id = NULL; char *system; if (port->p_chassis) { if (port->p_chassis->c_id_len > 0) { chassisid_to_string(port->p_chassis->c_id, port->p_chassis->c_id_len, &id); } descr = port->p_chassis->c_descr; } chassisid_to_string((uint8_t *) &port->p_element.system_id, sizeof port->p_element.system_id, &system); ds_put_format(ds, "\tAuto Attach Primary Server Id: %s\n", id ? id : none_str); ds_put_format(ds, "\tAuto Attach Primary Server Descr: %s\n", descr ? descr : none_str); ds_put_format(ds, "\tAuto Attach Primary Server System Id: %s\n", system); free(id); free(system); } } } /* Auto Attach server broadcast an LLDP message periodically. Display * the discovered server. */ static void aa_print_element_status(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex) { struct lldpd_hardware *hw; ds_put_format(ds, "LLDP: %s\n", lldp->name); if (!lldp->lldpd) { return; } LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) { aa_print_element_status_port(ds, hw); } } static void aa_print_isid_status_port_isid(struct lldp *lldp, struct lldpd_port *port) OVS_REQUIRES(mutex) { struct lldpd_aa_isid_vlan_maps_tlv *mapping; if (list_is_empty(&port->p_isid_vlan_maps)) { return; } LIST_FOR_EACH (mapping, m_entries, &port->p_isid_vlan_maps) { uint32_t isid = mapping->isid_vlan_data.isid; struct aa_mapping_internal *m = mapping_find_by_isid(lldp, isid); VLOG_INFO("h_rport: isid=%u, vlan=%u, status=%d", isid, mapping->isid_vlan_data.vlan, mapping->isid_vlan_data.status); /* Update the status of our internal state for the mapping. */ if (m) { VLOG_INFO("Setting status for ISID=%"PRIu32" to %"PRIu16, isid, mapping->isid_vlan_data.status); m->status = mapping->isid_vlan_data.status; } else { VLOG_WARN("Couldn't find mapping for I-SID=%"PRIu32, isid); } } } static void aa_print_isid_status_port(struct lldp *lldp, struct lldpd_hardware *hw) OVS_REQUIRES(mutex) { struct lldpd_port *port; LIST_FOR_EACH (port, p_entries, &hw->h_rports) { aa_print_isid_status_port_isid(lldp, port); } } /* The Auto Attach server will broadcast the status of the configured mappings * via LLDP. Display the status. */ static void aa_print_isid_status(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex) { struct lldpd_hardware *hw; struct aa_mapping_internal *m; if (!lldp->lldpd) { return; } ds_put_format(ds, "LLDP: %s\n", lldp->name); LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) { aa_print_isid_status_port(lldp, hw); } ds_put_format(ds, "%-8s %-4s %-11s %-8s\n", "I-SID", "VLAN", "Source", "Status"); ds_put_format(ds, "-------- ---- ----------- --------\n"); HMAP_FOR_EACH (m, hmap_node_isid, &lldp->mappings_by_isid) { ds_put_format(ds, "%-8"PRIu32" %-4"PRIu16" %-11s %-11s\n", m->isid, m->vlan, "Switch", aa_status_to_str(m->status)); } } static void aa_unixctl_status(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct lldp *lldp; struct ds ds = DS_EMPTY_INITIALIZER; ovs_mutex_lock(&mutex); HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { aa_print_element_status(&ds, lldp); } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); ovs_mutex_unlock(&mutex); } static void aa_unixctl_show_isid(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct lldp *lldp; struct ds ds = DS_EMPTY_INITIALIZER; ovs_mutex_lock(&mutex); HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { aa_print_isid_status(&ds, lldp); } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); ovs_mutex_unlock(&mutex); } static void aa_unixctl_statistics(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; struct lldp *lldp; ovs_mutex_lock(&mutex); /* Cycle through all ports and dump the stats for each one */ HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { aa_print_lldp_and_aa_stats(&ds, lldp); } ovs_mutex_unlock(&mutex); unixctl_command_reply(conn, ds_cstr(&ds)); } /* An Auto Attach mapping was configured. Populate the corresponding * structures in the LLDP hardware. */ static void update_mapping_on_lldp(struct lldp *lldp, struct lldpd_hardware *hardware, struct aa_mapping_internal *m) { struct lldpd_aa_isid_vlan_maps_tlv *lm = xzalloc(sizeof *lm); VLOG_INFO("\t\t hardware->h_ifname=%s", hardware->h_ifname); lm->isid_vlan_data.isid = m->isid; lm->isid_vlan_data.vlan = m->vlan; list_push_back(&hardware->h_lport.p_isid_vlan_maps, &lm->m_entries); /* TODO Should be done in the Auto Attach state machine when a mapping goes * from "pending" to "active". */ struct bridge_aa_vlan *node = xmalloc(sizeof *node); node->port_name = xstrdup(hardware->h_ifname); node->vlan = m->vlan; node->oper = BRIDGE_AA_VLAN_OPER_ADD; list_push_back(&lldp->active_mapping_queue, &node->list_node); } /* Bridge will poll the list of VLAN that needs to be auto configure based on * the Auto Attach mappings that have been exchanged with the server. */ int aa_get_vlan_queued(struct ovs_list *list) { struct lldp *lldp; ovs_mutex_lock(&mutex); HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { struct bridge_aa_vlan *node; LIST_FOR_EACH_POP (node, list_node, &lldp->active_mapping_queue) { struct bridge_aa_vlan *copy; copy = xmalloc(sizeof *copy); copy->port_name = xstrdup(node->port_name); copy->vlan = node->vlan; copy->oper = node->oper; list_push_back(list, ©->list_node); /* Cleanup */ free(node->port_name); free(node); } } ovs_mutex_unlock(&mutex); return 0; } /* Bridge will poll whether or not VLAN have been auto-configured. */ unsigned int aa_get_vlan_queue_size(void) { struct lldp *lldp; unsigned int size = 0; ovs_mutex_lock(&mutex); HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { size += list_size(&lldp->active_mapping_queue); } ovs_mutex_unlock(&mutex); return size; } /* Configure Auto Attach. */ int aa_configure(const struct aa_settings *s) { struct lldp *lldp; ovs_mutex_lock(&mutex); /* TODO Change all instances for now */ HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { struct lldpd_chassis *chassis; LIST_FOR_EACH (chassis, list, &lldp->lldpd->g_chassis) { /* System Description */ free(chassis->c_descr); chassis->c_descr = s && s->system_description[0] ? xstrdup(s->system_description) : xstrdup(PACKAGE_STRING); /* System Name */ if (s) { free(chassis->c_name); chassis->c_name = xstrdup(s->system_name); } } } ovs_mutex_unlock(&mutex); return 0; } /* Add a new Auto Attach mapping. */ int aa_mapping_register(void *aux, const struct aa_mapping_settings *s) { struct aa_mapping_internal *bridge_m; struct lldp *lldp; VLOG_INFO("Adding mapping ISID=%"PRIu32", VLAN=%"PRIu16", aux=%p", s->isid, s->vlan, aux); ovs_mutex_lock(&mutex); /* TODO These mappings should be stores per bridge. This is used * When a port is added. Auto Attach mappings need to be added on this * port. */ bridge_m = xzalloc(sizeof *bridge_m); bridge_m->isid = s->isid; bridge_m->vlan = s->vlan; bridge_m->aux = aux; bridge_m->status = AA_STATUS_PENDING; hmap_insert(all_mappings, &bridge_m->hmap_node_isid, hash_int(bridge_m->isid, 0)); /* Update mapping on the all the LLDP instances. */ HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { struct lldpd_hardware *hw; struct aa_mapping_internal *m; VLOG_INFO("\t lldp->name=%s", lldp->name); if (mapping_find_by_isid(lldp, s->isid)) { continue; } m = xzalloc(sizeof *m); m->isid = s->isid; m->vlan = s->vlan; m->status = AA_STATUS_PENDING; m->aux = aux; hmap_insert(&lldp->mappings_by_isid, &m->hmap_node_isid, hash_int(m->isid, 0)); hmap_insert(&lldp->mappings_by_aux, &m->hmap_node_aux, hash_pointer(m->aux, 0)); /* Configure the mapping on each port of the LLDP stack. */ LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) { update_mapping_on_lldp(lldp, hw, m); } } ovs_mutex_unlock(&mutex); return 0; } static void aa_mapping_unregister_mapping(struct lldp *lldp, struct lldpd_hardware *hw, struct aa_mapping_internal *m) { struct lldpd_aa_isid_vlan_maps_tlv *lm, *lm_next; LIST_FOR_EACH_SAFE (lm, lm_next, m_entries, &hw->h_lport.p_isid_vlan_maps) { uint32_t isid = lm->isid_vlan_data.isid; if (isid == m->isid) { VLOG_INFO("\t\t Removing lport, isid=%u, vlan=%u", isid, lm->isid_vlan_data.vlan); list_remove(&lm->m_entries); /* TODO Should be done in the AA SM when a mapping goes * from "pending" to "active". */ struct bridge_aa_vlan *node = xmalloc(sizeof *node); node->port_name = xstrdup(hw->h_ifname); node->vlan = m->vlan; node->oper = BRIDGE_AA_VLAN_OPER_REMOVE; list_push_back(&lldp->active_mapping_queue, &node->list_node); break; } } } /* Remove an existing Auto Attach mapping. */ int aa_mapping_unregister(void *aux) { struct lldp *lldp; VLOG_INFO("Removing mapping aux=%p", aux); ovs_mutex_lock(&mutex); HMAP_FOR_EACH (lldp, hmap_node, all_lldps) { struct lldpd_hardware *hw; struct aa_mapping_internal *m = mapping_find_by_aux(lldp, aux); /* Remove from internal hash tables. */ if (m) { uint32_t isid = m->isid; uint16_t vlan = m->vlan; struct aa_mapping_internal *p = mapping_find_by_isid(lldp, isid); VLOG_INFO("\t Removing mapping ISID=%"PRIu32", VLAN=%"PRIu16 " (lldp->name=%s)", isid, vlan, lldp->name); if (p) { hmap_remove(&lldp->mappings_by_isid, &p->hmap_node_isid); } hmap_remove(&lldp->mappings_by_aux, &m->hmap_node_aux); /* Remove from all the lldp instances */ LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) { VLOG_INFO("\t\t hardware->h_ifname=%s", hw->h_ifname); aa_mapping_unregister_mapping(lldp, hw, m); } free(m); /* Remove from the all_mappings */ HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) { if (m && isid == m->isid && vlan == m->vlan) { hmap_remove(all_mappings, &m->hmap_node_isid); break; } } } } ovs_mutex_unlock(&mutex); return 0; } void lldp_init(void) { unixctl_command_register("autoattach/status", "[bridge]", 0, 1, aa_unixctl_status, NULL); unixctl_command_register("autoattach/show-isid", "[bridge]", 0, 1, aa_unixctl_show_isid, NULL); unixctl_command_register("autoattach/statistics", "[bridge]", 0, 1, aa_unixctl_statistics, NULL); } /* Returns true if 'lldp' should process packets from 'flow'. Sets * fields in 'wc' that were used to make the determination. */ bool lldp_should_process_flow(struct lldp *lldp, const struct flow *flow) { return (flow->dl_type == htons(ETH_TYPE_LLDP) && lldp->enabled); } /* Process an LLDP packet that was received on a bridge port. */ void lldp_process_packet(struct lldp *lldp, const struct dp_packet *p) { if (lldp) { lldpd_recv(lldp->lldpd, lldpd_first_hardware(lldp->lldpd), (char *) dp_packet_data(p), dp_packet_size(p)); } } /* This code is called periodically to check if the LLDP module has an LLDP * message it wishes to send. It is called several times every second. */ bool lldp_should_send_packet(struct lldp *cfg) OVS_EXCLUDED(mutex) { bool ret; ovs_mutex_lock(&mutex); ret = timer_expired(&cfg->tx_timer); ovs_mutex_unlock(&mutex); /* LLDP must be enabled */ ret &= cfg->enabled; return ret; } /* Returns the next wake up time. */ long long int lldp_wake_time(const struct lldp *lldp) OVS_EXCLUDED(mutex) { long long int retval; if (!lldp || !lldp->enabled) { return LLONG_MAX; } ovs_mutex_lock(&mutex); retval = lldp->tx_timer.t; ovs_mutex_unlock(&mutex); return retval; } /* Put the monitor thread to sleep until it's next wake time. */ long long int lldp_wait(struct lldp *lldp) OVS_EXCLUDED(mutex) { long long int wake_time = lldp_wake_time(lldp); poll_timer_wait_until(wake_time); return wake_time; } /* Prepare the LLDP packet to be sent on a bridge port. */ void lldp_put_packet(struct lldp *lldp, struct dp_packet *packet, const struct eth_addr eth_src) OVS_EXCLUDED(mutex) { struct lldpd *mylldpd = lldp->lldpd; struct lldpd_hardware *hw = lldpd_first_hardware(mylldpd); static const struct eth_addr eth_addr_lldp = { { { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0e } } }; ovs_mutex_lock(&mutex); eth_compose(packet, eth_addr_lldp, eth_src, ETH_TYPE_LLDP, 0); lldpd_send(hw, packet); timer_set_duration(&lldp->tx_timer, lldp->lldpd->g_config.c_tx_interval); ovs_mutex_unlock(&mutex); } /* Configures the LLDP stack. */ bool lldp_configure(struct lldp *lldp, const struct smap *cfg) OVS_EXCLUDED(mutex) { if (lldp) { if (cfg && smap_get_bool(cfg, "enable", false)) { lldp->enabled = true; } else { lldp->enabled = false; } ovs_mutex_lock(&mutex); timer_set_expired(&lldp->tx_timer); timer_set_duration(&lldp->tx_timer, LLDP_DEFAULT_TRANSMIT_INTERVAL_MS); lldp->lldpd->g_config.c_tx_interval = LLDP_DEFAULT_TRANSMIT_INTERVAL_MS; ovs_mutex_unlock(&mutex); } return true; } /* Create an LLDP stack instance. At the moment there is one per bridge port. */ struct lldp * lldp_create(const struct netdev *netdev, const uint32_t mtu, const struct smap *cfg) OVS_EXCLUDED(mutex) { struct lldp *lldp; struct lldpd_chassis *lchassis; struct lldpd_hardware *hw; struct aa_mapping_internal *m; if (!cfg || !smap_get_bool(cfg, "enable", false)) { return NULL; } lldp = xzalloc(sizeof *lldp); lldp->name = xstrdup(netdev_get_name(netdev)); lldp->lldpd = xzalloc(sizeof *lldp->lldpd); hmap_init(&lldp->mappings_by_isid); hmap_init(&lldp->mappings_by_aux); list_init(&lldp->active_mapping_queue); lchassis = xzalloc(sizeof *lchassis); lchassis->c_cap_available = LLDP_CAP_BRIDGE; lchassis->c_cap_enabled = LLDP_CAP_BRIDGE; lchassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; lchassis->c_id_len = ETH_ADDR_LEN; struct eth_addr *mac = xmalloc(ETH_ADDR_LEN); netdev_get_etheraddr(netdev, mac); lchassis->c_id = &mac->ea[0]; list_init(&lchassis->c_mgmt); lchassis->c_ttl = lldp->lldpd->g_config.c_tx_interval * lldp->lldpd->g_config.c_tx_hold; lchassis->c_ttl = LLDP_CHASSIS_TTL; lldpd_assign_cfg_to_protocols(lldp->lldpd); list_init(&lldp->lldpd->g_chassis); list_push_back(&lldp->lldpd->g_chassis, &lchassis->list); if ((hw = lldpd_alloc_hardware(lldp->lldpd, (char *) netdev_get_name(netdev), 0)) == NULL) { VLOG_WARN("Unable to allocate space for %s", (char *) netdev_get_name(netdev)); out_of_memory(); } ovs_refcount_init(&lldp->ref_cnt); #ifndef _WIN32 hw->h_flags |= IFF_RUNNING; #endif hw->h_mtu = mtu; hw->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; hw->h_lport.p_id = xstrdup(netdev_get_name(netdev)); /* p_id is not necessarily a null terminated string. */ hw->h_lport.p_id_len = strlen(netdev_get_name(netdev)); /* Auto Attach element tlv */ hw->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_CLIENT_VIRTUAL_SWITCH; hw->h_lport.p_element.mgmt_vlan = 0; memcpy(&hw->h_lport.p_element.system_id.system_mac, lchassis->c_id, lchassis->c_id_len); hw->h_lport.p_element.system_id.conn_type = LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE; hw->h_lport.p_element.system_id.rsvd = 0; hw->h_lport.p_element.system_id.rsvd2[0] = 0; hw->h_lport.p_element.system_id.rsvd2[1] = 0; list_init(&hw->h_lport.p_isid_vlan_maps); list_init(&lldp->lldpd->g_hardware); list_push_back(&lldp->lldpd->g_hardware, &hw->h_entries); ovs_mutex_lock(&mutex); /* Update port with Auto Attach mappings configured. */ HMAP_FOR_EACH (m, hmap_node_isid, all_mappings) { struct aa_mapping_internal *p; if (mapping_find_by_isid(lldp, m->isid)) { continue; } p = xmemdup(m, sizeof *p); hmap_insert(&lldp->mappings_by_isid, &p->hmap_node_isid, hash_int(p->isid, 0)); hmap_insert(&lldp->mappings_by_aux, &p->hmap_node_aux, hash_pointer(p->aux, 0)); update_mapping_on_lldp(lldp, hw, p); } hmap_insert(all_lldps, &lldp->hmap_node, hash_string(netdev_get_name(netdev), 0)); ovs_mutex_unlock(&mutex); return lldp; } struct lldp * lldp_create_dummy(void) { struct lldp *lldp; struct lldpd_chassis *lchassis; struct lldpd_hardware *hw; lldp = xzalloc(sizeof *lldp); lldp->name = "dummy-lldp"; lldp->lldpd = xzalloc(sizeof *lldp->lldpd); hmap_init(&lldp->mappings_by_isid); hmap_init(&lldp->mappings_by_aux); list_init(&lldp->active_mapping_queue); lchassis = xzalloc(sizeof *lchassis); lchassis->c_cap_available = LLDP_CAP_BRIDGE; lchassis->c_cap_enabled = LLDP_CAP_BRIDGE; lchassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR; lchassis->c_id_len = ETH_ADDR_LEN; lchassis->c_id = xmalloc(ETH_ADDR_LEN); list_init(&lchassis->c_mgmt); lchassis->c_ttl = LLDP_CHASSIS_TTL; lldpd_assign_cfg_to_protocols(lldp->lldpd); list_init(&lldp->lldpd->g_chassis); list_push_back(&lldp->lldpd->g_chassis, &lchassis->list); hw = lldpd_alloc_hardware(lldp->lldpd, "dummy-hw", 0); ovs_refcount_init(&lldp->ref_cnt); #ifndef _WIN32 hw->h_flags |= IFF_RUNNING; #endif hw->h_mtu = 1500; hw->h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; hw->h_lport.p_id = "dummy-port"; /* p_id is not necessarily a null terminated string. */ hw->h_lport.p_id_len = strlen(hw->h_lport.p_id); /* Auto Attach element tlv */ hw->h_lport.p_element.type = LLDP_TLV_AA_ELEM_TYPE_CLIENT_VIRTUAL_SWITCH; hw->h_lport.p_element.mgmt_vlan = 0; memcpy(&hw->h_lport.p_element.system_id.system_mac, lchassis->c_id, lchassis->c_id_len); hw->h_lport.p_element.system_id.conn_type = LLDP_TLV_AA_ELEM_CONN_TYPE_SINGLE; hw->h_lport.p_element.system_id.rsvd = 0; hw->h_lport.p_element.system_id.rsvd2[0] = 0; hw->h_lport.p_element.system_id.rsvd2[1] = 0; list_init(&hw->h_lport.p_isid_vlan_maps); list_init(&lldp->lldpd->g_hardware); list_push_back(&lldp->lldpd->g_hardware, &hw->h_entries); return lldp; } /* Unreference a specific LLDP instance. */ void lldp_unref(struct lldp *lldp) { if (!lldp) { return; } ovs_mutex_lock(&mutex); if (ovs_refcount_unref_relaxed(&lldp->ref_cnt) != 1) { ovs_mutex_unlock(&mutex); return; } hmap_remove(all_lldps, &lldp->hmap_node); ovs_mutex_unlock(&mutex); lldpd_cleanup(lldp->lldpd); free(lldp->lldpd); free(lldp->name); free(lldp); } /* Unreference a specific LLDP instance. */ struct lldp * lldp_ref(const struct lldp *lldp_) { struct lldp *lldp = CONST_CAST(struct lldp *, lldp_); if (lldp) { ovs_refcount_ref(&lldp->ref_cnt); } return lldp; } openvswitch-2.5.9/lib/PaxHeaders.82075/hmapx.h0000644000000000000000000000013213534540071015700 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.737853101 openvswitch-2.5.9/lib/hmapx.h0000644000175000017500000000423013534540071017365 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HMAPX_H #define HMAPX_H #include "hmap.h" struct hmapx_node { struct hmap_node hmap_node; void *data; }; /* A set of "void *" pointers. */ struct hmapx { struct hmap map; }; #define HMAPX_INITIALIZER(HMAPX) { HMAP_INITIALIZER(&(HMAPX)->map) } /* Basics. */ void hmapx_init(struct hmapx *); void hmapx_destroy(struct hmapx *); void hmapx_clone(struct hmapx *, const struct hmapx *); void hmapx_swap(struct hmapx *, struct hmapx *); void hmapx_moved(struct hmapx *); /* Count. */ bool hmapx_is_empty(const struct hmapx *); size_t hmapx_count(const struct hmapx *); /* Insertion. */ struct hmapx_node *hmapx_add(struct hmapx *, void *); void hmapx_add_assert(struct hmapx *, void *); /* Deletion. */ void hmapx_clear(struct hmapx *); void hmapx_delete(struct hmapx *, struct hmapx_node *); bool hmapx_find_and_delete(struct hmapx *, const void *); void hmapx_find_and_delete_assert(struct hmapx *, const void *); /* Search. */ struct hmapx_node *hmapx_find(const struct hmapx *, const void *); bool hmapx_contains(const struct hmapx *, const void *); bool hmapx_equals(const struct hmapx *, const struct hmapx *); /* Iteration. */ /* Iterates through every hmapx_node in HMAPX. */ #define HMAPX_FOR_EACH(NODE, HMAPX) \ HMAP_FOR_EACH(NODE, hmap_node, &(HMAPX)->map) /* Safe when NODE may be freed (not needed when NODE may be removed from the * hash map but its members remain accessible and intact). */ #define HMAPX_FOR_EACH_SAFE(NODE, NEXT, HMAPX) \ HMAP_FOR_EACH_SAFE(NODE, NEXT, hmap_node, &(HMAPX)->map) #endif /* hmapx.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/odp-util.c0000644000000000000000000000013213534540071016313 xustar0030 mtime=1567801401.493681846 30 atime=1567801402.081686164 30 ctime=1567801424.789853486 openvswitch-2.5.9/lib/odp-util.c0000644000175000017500000055253713534540071020022 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "odp-util.h" #include #include #include #include #include #include #include #include #include "byte-order.h" #include "coverage.h" #include "dpif.h" #include "dynamic-string.h" #include "flow.h" #include "netlink.h" #include "ofpbuf.h" #include "packets.h" #include "simap.h" #include "timeval.h" #include "tun-metadata.h" #include "unaligned.h" #include "util.h" #include "uuid.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(odp_util); /* The interface between userspace and kernel uses an "OVS_*" prefix. * Since this is fairly non-specific for the OVS userspace components, * "ODP_*" (Open vSwitch Datapath) is used as the prefix for * interactions with the datapath. */ /* The set of characters that may separate one action or one key attribute * from another. */ static const char *delimiters = ", \t\r\n"; static const char *delimiters_end = ", \t\r\n)"; struct attr_len_tbl { int len; const struct attr_len_tbl *next; int next_max; }; #define ATTR_LEN_INVALID -1 #define ATTR_LEN_VARIABLE -2 #define ATTR_LEN_NESTED -3 static int parse_odp_key_mask_attr(const char *, const struct simap *port_names, struct ofpbuf *, struct ofpbuf *); static void format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, const struct hmap *portno_names, struct ds *ds, bool verbose); struct geneve_scan { struct geneve_opt d[63]; int len; }; static int scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask); static void format_geneve_opts(const struct geneve_opt *opt, const struct geneve_opt *mask, int opts_len, struct ds *, bool verbose); static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[], int max, struct ofpbuf *, const struct nlattr *key); static void format_u128(struct ds *ds, const ovs_u128 *value, const ovs_u128 *mask, bool verbose); static int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask); /* Returns one the following for the action with the given OVS_ACTION_ATTR_* * 'type': * * - For an action whose argument has a fixed length, returned that * nonnegative length in bytes. * * - For an action with a variable-length argument, returns ATTR_LEN_VARIABLE. * * - For an invalid 'type', returns ATTR_LEN_INVALID. */ static int odp_action_len(uint16_t type) { if (type > OVS_ACTION_ATTR_MAX) { return -1; } switch ((enum ovs_action_attr) type) { case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t); case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t); case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan); case OVS_ACTION_ATTR_POP_VLAN: return 0; case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls); case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16); case OVS_ACTION_ATTR_RECIRC: return sizeof(uint32_t); case OVS_ACTION_ATTR_HASH: return sizeof(struct ovs_action_hash); case OVS_ACTION_ATTR_SET: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: return ATTR_LEN_INVALID; } return ATTR_LEN_INVALID; } /* Returns a string form of 'attr'. The return value is either a statically * allocated constant string or the 'bufsize'-byte buffer 'namebuf'. 'bufsize' * should be at least OVS_KEY_ATTR_BUFSIZE. */ enum { OVS_KEY_ATTR_BUFSIZE = 3 + INT_STRLEN(unsigned int) + 1 }; static const char * ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize) { switch (attr) { case OVS_KEY_ATTR_UNSPEC: return "unspec"; case OVS_KEY_ATTR_ENCAP: return "encap"; case OVS_KEY_ATTR_PRIORITY: return "skb_priority"; case OVS_KEY_ATTR_SKB_MARK: return "skb_mark"; case OVS_KEY_ATTR_CT_STATE: return "ct_state"; case OVS_KEY_ATTR_CT_ZONE: return "ct_zone"; case OVS_KEY_ATTR_CT_MARK: return "ct_mark"; case OVS_KEY_ATTR_CT_LABELS: return "ct_label"; case OVS_KEY_ATTR_TUNNEL: return "tunnel"; case OVS_KEY_ATTR_IN_PORT: return "in_port"; case OVS_KEY_ATTR_ETHERNET: return "eth"; case OVS_KEY_ATTR_VLAN: return "vlan"; case OVS_KEY_ATTR_ETHERTYPE: return "eth_type"; case OVS_KEY_ATTR_IPV4: return "ipv4"; case OVS_KEY_ATTR_IPV6: return "ipv6"; case OVS_KEY_ATTR_TCP: return "tcp"; case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags"; case OVS_KEY_ATTR_UDP: return "udp"; case OVS_KEY_ATTR_SCTP: return "sctp"; case OVS_KEY_ATTR_ICMP: return "icmp"; case OVS_KEY_ATTR_ICMPV6: return "icmpv6"; case OVS_KEY_ATTR_ARP: return "arp"; case OVS_KEY_ATTR_ND: return "nd"; case OVS_KEY_ATTR_MPLS: return "mpls"; case OVS_KEY_ATTR_DP_HASH: return "dp_hash"; case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id"; case __OVS_KEY_ATTR_MAX: default: snprintf(namebuf, bufsize, "key%u", (unsigned int) attr); return namebuf; } } static void format_generic_odp_action(struct ds *ds, const struct nlattr *a) { size_t len = nl_attr_get_size(a); ds_put_format(ds, "action%"PRId16, nl_attr_type(a)); if (len) { const uint8_t *unspec; unsigned int i; unspec = nl_attr_get(a); for (i = 0; i < len; i++) { ds_put_char(ds, i ? ' ': '('); ds_put_format(ds, "%02x", unspec[i]); } ds_put_char(ds, ')'); } } static void format_odp_sample_action(struct ds *ds, const struct nlattr *attr) { static const struct nl_policy ovs_sample_policy[] = { [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 }, [OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED } }; struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)]; double percentage; const struct nlattr *nla_acts; int len; ds_put_cstr(ds, "sample"); if (!nl_parse_nested(attr, ovs_sample_policy, a, ARRAY_SIZE(a))) { ds_put_cstr(ds, "(error)"); return; } percentage = (100.0 * nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY])) / UINT32_MAX; ds_put_format(ds, "(sample=%.1f%%,", percentage); ds_put_cstr(ds, "actions("); nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]); len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]); format_odp_actions(ds, nla_acts, len); ds_put_format(ds, "))"); } static const char * slow_path_reason_to_string(uint32_t reason) { switch ((enum slow_path_reason) reason) { #define SPR(ENUM, STRING, EXPLANATION) case ENUM: return STRING; SLOW_PATH_REASONS #undef SPR } return NULL; } const char * slow_path_reason_to_explanation(enum slow_path_reason reason) { switch (reason) { #define SPR(ENUM, STRING, EXPLANATION) case ENUM: return EXPLANATION; SLOW_PATH_REASONS #undef SPR } return ""; } static int parse_odp_flags(const char *s, const char *(*bit_to_string)(uint32_t), uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask) { return parse_flags(s, bit_to_string, ')', NULL, NULL, res_flags, allowed, res_mask); } static void format_odp_userspace_action(struct ds *ds, const struct nlattr *attr) { static const struct nl_policy ovs_userspace_policy[] = { [OVS_USERSPACE_ATTR_PID] = { .type = NL_A_U32 }, [OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true }, [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32, .optional = true }, [OVS_USERSPACE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC, .optional = true }, }; struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)]; const struct nlattr *userdata_attr; const struct nlattr *tunnel_out_port_attr; if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) { ds_put_cstr(ds, "userspace(error)"); return; } ds_put_format(ds, "userspace(pid=%"PRIu32, nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID])); userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA]; if (userdata_attr) { const uint8_t *userdata = nl_attr_get(userdata_attr); size_t userdata_len = nl_attr_get_size(userdata_attr); bool userdata_unspec = true; union user_action_cookie cookie; if (userdata_len >= sizeof cookie.type && userdata_len <= sizeof cookie) { memset(&cookie, 0, sizeof cookie); memcpy(&cookie, userdata, userdata_len); userdata_unspec = false; if (userdata_len == sizeof cookie.sflow && cookie.type == USER_ACTION_COOKIE_SFLOW) { ds_put_format(ds, ",sFlow(" "vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")", vlan_tci_to_vid(cookie.sflow.vlan_tci), vlan_tci_to_pcp(cookie.sflow.vlan_tci), cookie.sflow.output); } else if (userdata_len == sizeof cookie.slow_path && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) { ds_put_cstr(ds, ",slow_path("); format_flags(ds, slow_path_reason_to_string, cookie.slow_path.reason, ','); ds_put_format(ds, ")"); } else if (userdata_len == sizeof cookie.flow_sample && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) { ds_put_format(ds, ",flow_sample(probability=%"PRIu16 ",collector_set_id=%"PRIu32 ",obs_domain_id=%"PRIu32 ",obs_point_id=%"PRIu32")", cookie.flow_sample.probability, cookie.flow_sample.collector_set_id, cookie.flow_sample.obs_domain_id, cookie.flow_sample.obs_point_id); } else if (userdata_len >= sizeof cookie.ipfix && cookie.type == USER_ACTION_COOKIE_IPFIX) { ds_put_format(ds, ",ipfix(output_port=%"PRIu32")", cookie.ipfix.output_odp_port); } else { userdata_unspec = true; } } if (userdata_unspec) { size_t i; ds_put_format(ds, ",userdata("); for (i = 0; i < userdata_len; i++) { ds_put_format(ds, "%02x", userdata[i]); } ds_put_char(ds, ')'); } } if (a[OVS_USERSPACE_ATTR_ACTIONS]) { ds_put_cstr(ds, ",actions"); } tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT]; if (tunnel_out_port_attr) { ds_put_format(ds, ",tunnel_out_port=%"PRIu32, nl_attr_get_u32(tunnel_out_port_attr)); } ds_put_char(ds, ')'); } static void format_vlan_tci(struct ds *ds, ovs_be16 tci, ovs_be16 mask, bool verbose) { if (verbose || vlan_tci_to_vid(tci) || vlan_tci_to_vid(mask)) { ds_put_format(ds, "vid=%"PRIu16, vlan_tci_to_vid(tci)); if (vlan_tci_to_vid(mask) != VLAN_VID_MASK) { /* Partially masked. */ ds_put_format(ds, "/0x%"PRIx16, vlan_tci_to_vid(mask)); }; ds_put_char(ds, ','); } if (verbose || vlan_tci_to_pcp(tci) || vlan_tci_to_pcp(mask)) { ds_put_format(ds, "pcp=%d", vlan_tci_to_pcp(tci)); if (vlan_tci_to_pcp(mask) != (VLAN_PCP_MASK >> VLAN_PCP_SHIFT)) { ds_put_format(ds, "/0x%x", vlan_tci_to_pcp(mask)); } ds_put_char(ds, ','); } if (!(tci & htons(VLAN_CFI))) { ds_put_cstr(ds, "cfi=0"); ds_put_char(ds, ','); } ds_chomp(ds, ','); } static void format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse) { ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d", mpls_lse_to_label(mpls_lse), mpls_lse_to_tc(mpls_lse), mpls_lse_to_ttl(mpls_lse), mpls_lse_to_bos(mpls_lse)); } static void format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key, const struct ovs_key_mpls *mpls_mask, int n) { if (n == 1) { ovs_be32 key = mpls_key->mpls_lse; if (mpls_mask == NULL) { format_mpls_lse(ds, key); } else { ovs_be32 mask = mpls_mask->mpls_lse; ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x", mpls_lse_to_label(key), mpls_lse_to_label(mask), mpls_lse_to_tc(key), mpls_lse_to_tc(mask), mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask), mpls_lse_to_bos(key), mpls_lse_to_bos(mask)); } } else { int i; for (i = 0; i < n; i++) { ds_put_format(ds, "lse%d=%#"PRIx32, i, ntohl(mpls_key[i].mpls_lse)); if (mpls_mask) { ds_put_format(ds, "/%#"PRIx32, ntohl(mpls_mask[i].mpls_lse)); } ds_put_char(ds, ','); } ds_chomp(ds, ','); } } static void format_odp_recirc_action(struct ds *ds, uint32_t recirc_id) { ds_put_format(ds, "recirc(%#"PRIx32")", recirc_id); } static void format_odp_hash_action(struct ds *ds, const struct ovs_action_hash *hash_act) { ds_put_format(ds, "hash("); if (hash_act->hash_alg == OVS_HASH_ALG_L4) { ds_put_format(ds, "hash_l4(%"PRIu32")", hash_act->hash_basis); } else { ds_put_format(ds, "Unknown hash algorithm(%"PRIu32")", hash_act->hash_alg); } ds_put_format(ds, ")"); } static const void * format_udp_tnl_push_header(struct ds *ds, const struct udp_header *udp) { ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),", ntohs(udp->udp_src), ntohs(udp->udp_dst), ntohs(udp->udp_csum)); return udp + 1; } static void format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data) { const struct eth_header *eth; const void *l3; const void *l4; const struct udp_header *udp; eth = ALIGNED_CAST(const struct eth_header *, data->header); l3 = eth + 1; /* Ethernet */ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=", data->header_len, data->tnl_type); ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst)); ds_put_format(ds, ",src="); ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src)); ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type)); if (eth->eth_type == htons(ETH_TYPE_IP)) { /* IPv4 */ const struct ip_header *ip = l3; ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8 ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),", IP_ARGS(get_16aligned_be32(&ip->ip_src)), IP_ARGS(get_16aligned_be32(&ip->ip_dst)), ip->ip_proto, ip->ip_tos, ip->ip_ttl, ntohs(ip->ip_frag_off)); l4 = (ip + 1); } else { const struct ovs_16aligned_ip6_hdr *ip6 = l3; struct in6_addr src, dst; memcpy(&src, &ip6->ip6_src, sizeof src); memcpy(&dst, &ip6->ip6_dst, sizeof dst); uint32_t ipv6_flow = ntohl(get_16aligned_be32(&ip6->ip6_flow)); ds_put_format(ds, "ipv6(src="); ipv6_format_addr(&src, ds); ds_put_format(ds, ",dst="); ipv6_format_addr(&dst, ds); ds_put_format(ds, ",label=%i,proto=%"PRIu8",tclass=0x%"PRIx32 ",hlimit=%"PRIu8"),", ipv6_flow & IPV6_LABEL_MASK, ip6->ip6_nxt, (ipv6_flow >> 20) & 0xff, ip6->ip6_hlim); l4 = (ip6 + 1); } udp = (const struct udp_header *) l4; if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) { const struct vxlanhdr *vxh; vxh = format_udp_tnl_push_header(ds, udp); ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")", ntohl(get_16aligned_be32(&vxh->vx_flags)), ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8); } else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) { const struct genevehdr *gnh; gnh = format_udp_tnl_push_header(ds, udp); ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32, gnh->oam ? "oam," : "", gnh->critical ? "crit," : "", ntohl(get_16aligned_be32(&gnh->vni)) >> 8); if (gnh->opt_len) { ds_put_cstr(ds, ",options("); format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4, ds, false); ds_put_char(ds, ')'); } ds_put_char(ds, ')'); } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) { const struct gre_base_hdr *greh; ovs_16aligned_be32 *options; greh = (const struct gre_base_hdr *) l4; ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")", ntohs(greh->flags), ntohs(greh->protocol)); options = (ovs_16aligned_be32 *)(greh + 1); if (greh->flags & htons(GRE_CSUM)) { ds_put_format(ds, ",csum=0x%"PRIx16, ntohs(*((ovs_be16 *)options))); options++; } if (greh->flags & htons(GRE_KEY)) { ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options))); options++; } if (greh->flags & htons(GRE_SEQ)) { ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options))); options++; } ds_put_format(ds, ")"); } ds_put_format(ds, ")"); } static void format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr) { struct ovs_action_push_tnl *data; data = (struct ovs_action_push_tnl *) nl_attr_get(attr); ds_put_format(ds, "tnl_push(tnl_port(%"PRIu32"),", data->tnl_port); format_odp_tnl_push_header(ds, data); ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port); } static const struct nl_policy ovs_conntrack_policy[] = { [OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .optional = true, }, [OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true, }, [OVS_CT_ATTR_MARK] = { .type = NL_A_UNSPEC, .optional = true, .min_len = sizeof(uint32_t) * 2 }, [OVS_CT_ATTR_LABELS] = { .type = NL_A_UNSPEC, .optional = true, .min_len = sizeof(struct ovs_key_ct_labels) * 2 }, [OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true, .min_len = 1, .max_len = 16 }, }; static void format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr) { struct nlattr *a[ARRAY_SIZE(ovs_conntrack_policy)]; const ovs_u128 *label; const uint32_t *mark; const char *helper; uint16_t zone; bool commit; if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) { ds_put_cstr(ds, "ct(error)"); return; } commit = a[OVS_CT_ATTR_COMMIT] ? true : false; zone = a[OVS_CT_ATTR_ZONE] ? nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]) : 0; mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL; label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL; helper = a[OVS_CT_ATTR_HELPER] ? nl_attr_get(a[OVS_CT_ATTR_HELPER]) : NULL; ds_put_format(ds, "ct"); if (commit || zone || mark || label || helper) { ds_put_cstr(ds, "("); if (commit) { ds_put_format(ds, "commit,"); } if (zone) { ds_put_format(ds, "zone=%"PRIu16",", zone); } if (mark) { ds_put_format(ds, "mark=%#"PRIx32"/%#"PRIx32",", *mark, *(mark + 1)); } if (label) { ds_put_format(ds, "label="); format_u128(ds, label, label + 1, true); ds_put_char(ds, ','); } if (helper) { ds_put_format(ds, "helper=%s,", helper); } ds_chomp(ds, ','); ds_put_cstr(ds, ")"); } } static void format_odp_action(struct ds *ds, const struct nlattr *a) { int expected_len; enum ovs_action_attr type = nl_attr_type(a); size_t size; expected_len = odp_action_len(nl_attr_type(a)); if (expected_len != ATTR_LEN_VARIABLE && nl_attr_get_size(a) != expected_len) { ds_put_format(ds, "bad length %"PRIuSIZE", expected %d for: ", nl_attr_get_size(a), expected_len); format_generic_odp_action(ds, a); return; } switch (type) { case OVS_ACTION_ATTR_OUTPUT: ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_TUNNEL_POP: ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_TUNNEL_PUSH: format_odp_tnl_push_action(ds, a); break; case OVS_ACTION_ATTR_USERSPACE: format_odp_userspace_action(ds, a); break; case OVS_ACTION_ATTR_RECIRC: format_odp_recirc_action(ds, nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_HASH: format_odp_hash_action(ds, nl_attr_get(a)); break; case OVS_ACTION_ATTR_SET_MASKED: a = nl_attr_get(a); size = nl_attr_get_size(a) / 2; ds_put_cstr(ds, "set("); /* Masked set action not supported for tunnel key, which is bigger. */ if (size <= sizeof(struct ovs_key_ipv6)) { struct nlattr attr[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6), sizeof(struct nlattr))]; struct nlattr mask[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6), sizeof(struct nlattr))]; mask->nla_type = attr->nla_type = nl_attr_type(a); mask->nla_len = attr->nla_len = NLA_HDRLEN + size; memcpy(attr + 1, (char *)(a + 1), size); memcpy(mask + 1, (char *)(a + 1) + size, size); format_odp_key_attr(attr, mask, NULL, ds, false); } else { format_odp_key_attr(a, NULL, NULL, ds, false); } ds_put_cstr(ds, ")"); break; case OVS_ACTION_ATTR_SET: ds_put_cstr(ds, "set("); format_odp_key_attr(nl_attr_get(a), NULL, NULL, ds, true); ds_put_cstr(ds, ")"); break; case OVS_ACTION_ATTR_PUSH_VLAN: { const struct ovs_action_push_vlan *vlan = nl_attr_get(a); ds_put_cstr(ds, "push_vlan("); if (vlan->vlan_tpid != htons(ETH_TYPE_VLAN)) { ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(vlan->vlan_tpid)); } format_vlan_tci(ds, vlan->vlan_tci, OVS_BE16_MAX, false); ds_put_char(ds, ')'); break; } case OVS_ACTION_ATTR_POP_VLAN: ds_put_cstr(ds, "pop_vlan"); break; case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nl_attr_get(a); ds_put_cstr(ds, "push_mpls("); format_mpls_lse(ds, mpls->mpls_lse); ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype)); break; } case OVS_ACTION_ATTR_POP_MPLS: { ovs_be16 ethertype = nl_attr_get_be16(a); ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype)); break; } case OVS_ACTION_ATTR_SAMPLE: format_odp_sample_action(ds, a); break; case OVS_ACTION_ATTR_CT: format_odp_conntrack_action(ds, a); break; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: format_generic_odp_action(ds, a); break; } } void format_odp_actions(struct ds *ds, const struct nlattr *actions, size_t actions_len) { if (actions_len) { const struct nlattr *a; unsigned int left; NL_ATTR_FOR_EACH (a, left, actions, actions_len) { if (a != actions) { ds_put_char(ds, ','); } format_odp_action(ds, a); } if (left) { int i; if (left == actions_len) { ds_put_cstr(ds, ""); } ds_put_format(ds, ",***%u leftover bytes*** (", left); for (i = 0; i < left; i++) { ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]); } ds_put_char(ds, ')'); } } else { ds_put_cstr(ds, "drop"); } } /* Separate out parse_odp_userspace_action() function. */ static int parse_odp_userspace_action(const char *s, struct ofpbuf *actions) { uint32_t pid; union user_action_cookie cookie; struct ofpbuf buf; odp_port_t tunnel_out_port; int n = -1; void *user_data = NULL; size_t user_data_size = 0; bool include_actions = false; int res; if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) { return -EINVAL; } ofpbuf_init(&buf, 16); { uint32_t output; uint32_t probability; uint32_t collector_set_id; uint32_t obs_domain_id; uint32_t obs_point_id; int vid, pcp; int n1 = -1; if (ovs_scan(&s[n], ",sFlow(vid=%i," "pcp=%i,output=%"SCNi32")%n", &vid, &pcp, &output, &n1)) { uint16_t tci; n += n1; tci = vid | (pcp << VLAN_PCP_SHIFT); if (tci) { tci |= VLAN_CFI; } cookie.type = USER_ACTION_COOKIE_SFLOW; cookie.sflow.vlan_tci = htons(tci); cookie.sflow.output = output; user_data = &cookie; user_data_size = sizeof cookie.sflow; } else if (ovs_scan(&s[n], ",slow_path(%n", &n1)) { n += n1; cookie.type = USER_ACTION_COOKIE_SLOW_PATH; cookie.slow_path.unused = 0; cookie.slow_path.reason = 0; res = parse_odp_flags(&s[n], slow_path_reason_to_string, &cookie.slow_path.reason, SLOW_PATH_REASON_MASK, NULL); if (res < 0 || s[n + res] != ')') { goto out; } n += res + 1; user_data = &cookie; user_data_size = sizeof cookie.slow_path; } else if (ovs_scan(&s[n], ",flow_sample(probability=%"SCNi32"," "collector_set_id=%"SCNi32"," "obs_domain_id=%"SCNi32"," "obs_point_id=%"SCNi32")%n", &probability, &collector_set_id, &obs_domain_id, &obs_point_id, &n1)) { n += n1; cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE; cookie.flow_sample.probability = probability; cookie.flow_sample.collector_set_id = collector_set_id; cookie.flow_sample.obs_domain_id = obs_domain_id; cookie.flow_sample.obs_point_id = obs_point_id; user_data = &cookie; user_data_size = sizeof cookie.flow_sample; } else if (ovs_scan(&s[n], ",ipfix(output_port=%"SCNi32")%n", &output, &n1) ) { n += n1; cookie.type = USER_ACTION_COOKIE_IPFIX; cookie.ipfix.output_odp_port = u32_to_odp(output); user_data = &cookie; user_data_size = sizeof cookie.ipfix; } else if (ovs_scan(&s[n], ",userdata(%n", &n1)) { char *end; n += n1; end = ofpbuf_put_hex(&buf, &s[n], NULL); if (end[0] != ')') { res = -EINVAL; goto out; } user_data = buf.data; user_data_size = buf.size; n = (end + 1) - s; } } { int n1 = -1; if (ovs_scan(&s[n], ",actions%n", &n1)) { n += n1; include_actions = true; } } { int n1 = -1; if (ovs_scan(&s[n], ",tunnel_out_port=%"SCNi32")%n", &tunnel_out_port, &n1)) { odp_put_userspace_action(pid, user_data, user_data_size, tunnel_out_port, include_actions, actions); res = n + n1; } else if (s[n] == ')') { odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, include_actions, actions); res = n + 1; } else { res = -EINVAL; } } out: ofpbuf_uninit(&buf); return res; } static int ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data) { struct eth_header *eth; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; struct udp_header *udp; struct gre_base_hdr *greh; uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum; ovs_be32 sip, dip; uint32_t tnl_type = 0, header_len = 0, ip_len = 0; void *l3, *l4; int n = 0; if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) { return -EINVAL; } eth = ALIGNED_CAST(struct eth_header *, data->header); l3 = (data->header + sizeof *eth); ip = (struct ip_header *) l3; ip6 = (struct ovs_16aligned_ip6_hdr *) l3; if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32"," "eth(dst="ETH_ADDR_SCAN_FMT",", &data->header_len, &data->tnl_type, ETH_ADDR_SCAN_ARGS(eth->eth_dst))) { return -EINVAL; } if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",", ETH_ADDR_SCAN_ARGS(eth->eth_src))) { return -EINVAL; } if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) { return -EINVAL; } eth->eth_type = htons(dl_type); if (eth->eth_type == htons(ETH_TYPE_IP)) { /* IPv4 */ uint16_t ip_frag_off; if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8 ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),", IP_SCAN_ARGS(&sip), IP_SCAN_ARGS(&dip), &ip->ip_proto, &ip->ip_tos, &ip->ip_ttl, &ip_frag_off)) { return -EINVAL; } put_16aligned_be32(&ip->ip_src, sip); put_16aligned_be32(&ip->ip_dst, dip); ip->ip_frag_off = htons(ip_frag_off); ip_len = sizeof *ip; } else { char sip6_s[IPV6_SCAN_LEN + 1]; char dip6_s[IPV6_SCAN_LEN + 1]; struct in6_addr sip6, dip6; uint8_t tclass; uint32_t label; if (!ovs_scan_len(s, &n, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT ",label=%i,proto=%"SCNi8",tclass=0x%"SCNx8 ",hlimit=%"SCNi8"),", sip6_s, dip6_s, &label, &ip6->ip6_nxt, &tclass, &ip6->ip6_hlim) || (label & ~IPV6_LABEL_MASK) != 0 || inet_pton(AF_INET6, sip6_s, &sip6) != 1 || inet_pton(AF_INET6, dip6_s, &dip6) != 1) { return -EINVAL; } put_16aligned_be32(&ip6->ip6_flow, htonl(6 << 28) | htonl(tclass << 20) | htonl(label)); memcpy(&ip6->ip6_src, &sip6, sizeof(ip6->ip6_src)); memcpy(&ip6->ip6_dst, &dip6, sizeof(ip6->ip6_dst)); ip_len = sizeof *ip6; } /* Tunnel header */ l4 = ((uint8_t *) l3 + ip_len); udp = (struct udp_header *) l4; greh = (struct gre_base_hdr *) l4; if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),", &udp_src, &udp_dst, &csum)) { uint32_t vx_flags, vni; udp->udp_src = htons(udp_src); udp->udp_dst = htons(udp_dst); udp->udp_len = 0; udp->udp_csum = htons(csum); if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))", &vx_flags, &vni)) { struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1); put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags)); put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8)); tnl_type = OVS_VPORT_TYPE_VXLAN; header_len = sizeof *eth + ip_len + sizeof *udp + sizeof *vxh; } else if (ovs_scan_len(s, &n, "geneve(")) { struct genevehdr *gnh = (struct genevehdr *) (udp + 1); memset(gnh, 0, sizeof *gnh); header_len = sizeof *eth + ip_len + sizeof *udp + sizeof *gnh; if (ovs_scan_len(s, &n, "oam,")) { gnh->oam = 1; } if (ovs_scan_len(s, &n, "crit,")) { gnh->critical = 1; } if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) { return -EINVAL; } if (ovs_scan_len(s, &n, ",options(")) { struct geneve_scan options; int len; memset(&options, 0, sizeof options); len = scan_geneve(s + n, &options, NULL); if (!len) { return -EINVAL; } memcpy(gnh->options, options.d, options.len); gnh->opt_len = options.len / 4; header_len += options.len; n += len; } if (!ovs_scan_len(s, &n, "))")) { return -EINVAL; } gnh->proto_type = htons(ETH_TYPE_TEB); put_16aligned_be32(&gnh->vni, htonl(vni << 8)); tnl_type = OVS_VPORT_TYPE_GENEVE; } else { return -EINVAL; } } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")", &gre_flags, &gre_proto)){ tnl_type = OVS_VPORT_TYPE_GRE; greh->flags = htons(gre_flags); greh->protocol = htons(gre_proto); ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1); if (greh->flags & htons(GRE_CSUM)) { if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) { return -EINVAL; } memset(options, 0, sizeof *options); *((ovs_be16 *)options) = htons(csum); options++; } if (greh->flags & htons(GRE_KEY)) { uint32_t key; if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) { return -EINVAL; } put_16aligned_be32(options, htonl(key)); options++; } if (greh->flags & htons(GRE_SEQ)) { uint32_t seq; if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) { return -EINVAL; } put_16aligned_be32(options, htonl(seq)); options++; } if (!ovs_scan_len(s, &n, "))")) { return -EINVAL; } header_len = sizeof *eth + ip_len + ((uint8_t *) options - (uint8_t *) greh); } else { return -EINVAL; } /* check tunnel meta data. */ if (data->tnl_type != tnl_type) { return -EINVAL; } if (data->header_len != header_len) { return -EINVAL; } /* Out port */ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) { return -EINVAL; } return n; } static int parse_conntrack_action(const char *s_, struct ofpbuf *actions) { const char *s = s_; if (ovs_scan(s, "ct")) { const char *helper = NULL; size_t helper_len = 0; bool commit = false; uint16_t zone = 0; struct { uint32_t value; uint32_t mask; } ct_mark = { 0, 0 }; struct { ovs_u128 value; ovs_u128 mask; } ct_label; size_t start; char *end; memset(&ct_label, 0, sizeof(ct_label)); s += 2; if (ovs_scan(s, "(")) { s++; end = strchr(s, ')'); if (!end) { return -EINVAL; } while (s != end) { int n = -1; s += strspn(s, delimiters); if (ovs_scan(s, "commit%n", &n)) { commit = true; s += n; continue; } if (ovs_scan(s, "zone=%"SCNu16"%n", &zone, &n)) { s += n; continue; } if (ovs_scan(s, "mark=%"SCNx32"%n", &ct_mark.value, &n)) { s += n; n = -1; if (ovs_scan(s, "/%"SCNx32"%n", &ct_mark.mask, &n)) { s += n; } else { ct_mark.mask = UINT32_MAX; } continue; } if (ovs_scan(s, "label=%n", &n)) { int retval; s += n; retval = scan_u128(s, &ct_label.value, &ct_label.mask); if (retval < 0) { return retval; } s += retval; continue; } if (ovs_scan(s, "helper=%n", &n)) { s += n; helper_len = strcspn(s, delimiters_end); if (!helper_len || helper_len > 15) { return -EINVAL; } helper = s; s += helper_len; continue; } return -EINVAL; } s++; } start = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CT); if (commit) { nl_msg_put_flag(actions, OVS_CT_ATTR_COMMIT); } if (zone) { nl_msg_put_u16(actions, OVS_CT_ATTR_ZONE, zone); } if (ct_mark.mask) { nl_msg_put_unspec(actions, OVS_CT_ATTR_MARK, &ct_mark, sizeof(ct_mark)); } if (!ovs_u128_is_zero(&ct_label.mask)) { nl_msg_put_unspec(actions, OVS_CT_ATTR_LABELS, &ct_label, sizeof ct_label); } if (helper) { nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper, helper_len); } nl_msg_end_nested(actions, start); } return s - s_; } static int parse_odp_action(const char *s, const struct simap *port_names, struct ofpbuf *actions) { { uint32_t port; int n; if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) { nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port); return n; } } if (port_names) { int len = strcspn(s, delimiters); struct simap_node *node; node = simap_find_len(port_names, s, len); if (node) { nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data); return len; } } { uint32_t recirc_id; int n = -1; if (ovs_scan(s, "recirc(%"PRIu32")%n", &recirc_id, &n)) { nl_msg_put_u32(actions, OVS_ACTION_ATTR_RECIRC, recirc_id); return n; } } if (!strncmp(s, "userspace(", 10)) { return parse_odp_userspace_action(s, actions); } if (!strncmp(s, "set(", 4)) { size_t start_ofs; int retval; struct nlattr mask[1024 / sizeof(struct nlattr)]; struct ofpbuf maskbuf = OFPBUF_STUB_INITIALIZER(mask); struct nlattr *nested, *key; size_t size; start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET); retval = parse_odp_key_mask_attr(s + 4, port_names, actions, &maskbuf); if (retval < 0) { ofpbuf_uninit(&maskbuf); return retval; } if (s[retval + 4] != ')') { ofpbuf_uninit(&maskbuf); return -EINVAL; } nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested); key = nested + 1; size = nl_attr_get_size(mask); if (size == nl_attr_get_size(key)) { /* Change to masked set action if not fully masked. */ if (!is_all_ones(mask + 1, size)) { key->nla_len += size; ofpbuf_put(actions, mask + 1, size); /* 'actions' may have been reallocated by ofpbuf_put(). */ nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested); nested->nla_type = OVS_ACTION_ATTR_SET_MASKED; } } ofpbuf_uninit(&maskbuf); nl_msg_end_nested(actions, start_ofs); return retval + 5; } { struct ovs_action_push_vlan push; int tpid = ETH_TYPE_VLAN; int vid, pcp; int cfi = 1; int n = -1; if (ovs_scan(s, "push_vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n) || ovs_scan(s, "push_vlan(vid=%i,pcp=%i,cfi=%i)%n", &vid, &pcp, &cfi, &n) || ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i)%n", &tpid, &vid, &pcp, &n) || ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i,cfi=%i)%n", &tpid, &vid, &pcp, &cfi, &n)) { push.vlan_tpid = htons(tpid); push.vlan_tci = htons((vid << VLAN_VID_SHIFT) | (pcp << VLAN_PCP_SHIFT) | (cfi ? VLAN_CFI : 0)); nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_VLAN, &push, sizeof push); return n; } } if (!strncmp(s, "pop_vlan", 8)) { nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_VLAN); return 8; } { double percentage; int n = -1; if (ovs_scan(s, "sample(sample=%lf%%,actions(%n", &percentage, &n) && percentage >= 0. && percentage <= 100.0) { size_t sample_ofs, actions_ofs; double probability; probability = floor(UINT32_MAX * (percentage / 100.0) + .5); sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE); nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY, (probability <= 0 ? 0 : probability >= UINT32_MAX ? UINT32_MAX : probability)); actions_ofs = nl_msg_start_nested(actions, OVS_SAMPLE_ATTR_ACTIONS); for (;;) { int retval; n += strspn(s + n, delimiters); if (s[n] == ')') { break; } retval = parse_odp_action(s + n, port_names, actions); if (retval < 0) { return retval; } n += retval; } nl_msg_end_nested(actions, actions_ofs); nl_msg_end_nested(actions, sample_ofs); return s[n + 1] == ')' ? n + 2 : -EINVAL; } } { uint32_t port; int n; if (ovs_scan(s, "tnl_pop(%"SCNi32")%n", &port, &n)) { nl_msg_put_u32(actions, OVS_ACTION_ATTR_TUNNEL_POP, port); return n; } } { int retval; retval = parse_conntrack_action(s, actions); if (retval) { return retval; } } { struct ovs_action_push_tnl data; int n; n = ovs_parse_tnl_push(s, &data); if (n > 0) { odp_put_tnl_push_action(actions, &data); return n; } else if (n < 0) { return n; } } return -EINVAL; } /* Parses the string representation of datapath actions, in the format output * by format_odp_action(). Returns 0 if successful, otherwise a positive errno * value. On success, the ODP actions are appended to 'actions' as a series of * Netlink attributes. On failure, no data is appended to 'actions'. Either * way, 'actions''s data might be reallocated. */ int odp_actions_from_string(const char *s, const struct simap *port_names, struct ofpbuf *actions) { size_t old_size; if (!strcasecmp(s, "drop")) { return 0; } old_size = actions->size; for (;;) { int retval; s += strspn(s, delimiters); if (!*s) { return 0; } retval = parse_odp_action(s, port_names, actions); if (retval < 0 || !strchr(delimiters, s[retval])) { actions->size = old_size; return -retval; } s += retval; } return 0; } static const struct attr_len_tbl ovs_vxlan_ext_attr_lens[OVS_VXLAN_EXT_MAX + 1] = { [OVS_VXLAN_EXT_GBP] = { .len = 4 }, }; static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { [OVS_TUNNEL_KEY_ATTR_ID] = { .len = 8 }, [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = { .len = 4 }, [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = { .len = 4 }, [OVS_TUNNEL_KEY_ATTR_TOS] = { .len = 1 }, [OVS_TUNNEL_KEY_ATTR_TTL] = { .len = 1 }, [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_CSUM] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_TP_SRC] = { .len = 2 }, [OVS_TUNNEL_KEY_ATTR_TP_DST] = { .len = 2 }, [OVS_TUNNEL_KEY_ATTR_OAM] = { .len = 0 }, [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = ATTR_LEN_VARIABLE }, [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = ATTR_LEN_NESTED, .next = ovs_vxlan_ext_attr_lens , .next_max = OVS_VXLAN_EXT_MAX}, [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 }, [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 }, }; static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = { .len = ATTR_LEN_NESTED }, [OVS_KEY_ATTR_PRIORITY] = { .len = 4 }, [OVS_KEY_ATTR_SKB_MARK] = { .len = 4 }, [OVS_KEY_ATTR_DP_HASH] = { .len = 4 }, [OVS_KEY_ATTR_RECIRC_ID] = { .len = 4 }, [OVS_KEY_ATTR_TUNNEL] = { .len = ATTR_LEN_NESTED, .next = ovs_tun_key_attr_lens, .next_max = OVS_TUNNEL_KEY_ATTR_MAX }, [OVS_KEY_ATTR_IN_PORT] = { .len = 4 }, [OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) }, [OVS_KEY_ATTR_VLAN] = { .len = 2 }, [OVS_KEY_ATTR_ETHERTYPE] = { .len = 2 }, [OVS_KEY_ATTR_MPLS] = { .len = ATTR_LEN_VARIABLE }, [OVS_KEY_ATTR_IPV4] = { .len = sizeof(struct ovs_key_ipv4) }, [OVS_KEY_ATTR_IPV6] = { .len = sizeof(struct ovs_key_ipv6) }, [OVS_KEY_ATTR_TCP] = { .len = sizeof(struct ovs_key_tcp) }, [OVS_KEY_ATTR_TCP_FLAGS] = { .len = 2 }, [OVS_KEY_ATTR_UDP] = { .len = sizeof(struct ovs_key_udp) }, [OVS_KEY_ATTR_SCTP] = { .len = sizeof(struct ovs_key_sctp) }, [OVS_KEY_ATTR_ICMP] = { .len = sizeof(struct ovs_key_icmp) }, [OVS_KEY_ATTR_ICMPV6] = { .len = sizeof(struct ovs_key_icmpv6) }, [OVS_KEY_ATTR_ARP] = { .len = sizeof(struct ovs_key_arp) }, [OVS_KEY_ATTR_ND] = { .len = sizeof(struct ovs_key_nd) }, [OVS_KEY_ATTR_CT_STATE] = { .len = 4 }, [OVS_KEY_ATTR_CT_ZONE] = { .len = 2 }, [OVS_KEY_ATTR_CT_MARK] = { .len = 4 }, [OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) }, }; /* Returns the correct length of the payload for a flow key attribute of the * specified 'type', ATTR_LEN_INVALID if 'type' is unknown, ATTR_LEN_VARIABLE * if the attribute's payload is variable length, or ATTR_LEN_NESTED if the * payload is a nested type. */ static int odp_key_attr_len(const struct attr_len_tbl tbl[], int max_len, uint16_t type) { if (type > max_len) { return ATTR_LEN_INVALID; } return tbl[type].len; } static void format_generic_odp_key(const struct nlattr *a, struct ds *ds) { size_t len = nl_attr_get_size(a); if (len) { const uint8_t *unspec; unsigned int i; unspec = nl_attr_get(a); for (i = 0; i < len; i++) { if (i) { ds_put_char(ds, ' '); } ds_put_format(ds, "%02x", unspec[i]); } } } static const char * ovs_frag_type_to_string(enum ovs_frag_type type) { switch (type) { case OVS_FRAG_TYPE_NONE: return "no"; case OVS_FRAG_TYPE_FIRST: return "first"; case OVS_FRAG_TYPE_LATER: return "later"; case __OVS_FRAG_TYPE_MAX: default: return ""; } } static enum odp_key_fitness odp_tun_key_from_attr__(const struct nlattr *attr, const struct nlattr *flow_attrs, size_t flow_attr_len, const struct flow_tnl *src_tun, struct flow_tnl *tun, bool udpif) { unsigned int left; const struct nlattr *a; bool ttl = false; bool unknown = false; NL_NESTED_FOR_EACH(a, left, attr) { uint16_t type = nl_attr_type(a); size_t len = nl_attr_get_size(a); int expected_len = odp_key_attr_len(ovs_tun_key_attr_lens, OVS_TUNNEL_ATTR_MAX, type); if (len != expected_len && expected_len >= 0) { return ODP_FIT_ERROR; } switch (type) { case OVS_TUNNEL_KEY_ATTR_ID: tun->tun_id = nl_attr_get_be64(a); tun->flags |= FLOW_TNL_F_KEY; break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: tun->ip_src = nl_attr_get_be32(a); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: tun->ip_dst = nl_attr_get_be32(a); break; case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: tun->ipv6_src = nl_attr_get_in6_addr(a); break; case OVS_TUNNEL_KEY_ATTR_IPV6_DST: tun->ipv6_dst = nl_attr_get_in6_addr(a); break; case OVS_TUNNEL_KEY_ATTR_TOS: tun->ip_tos = nl_attr_get_u8(a); break; case OVS_TUNNEL_KEY_ATTR_TTL: tun->ip_ttl = nl_attr_get_u8(a); ttl = true; break; case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: tun->flags |= FLOW_TNL_F_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: tun->flags |= FLOW_TNL_F_CSUM; break; case OVS_TUNNEL_KEY_ATTR_TP_SRC: tun->tp_src = nl_attr_get_be16(a); break; case OVS_TUNNEL_KEY_ATTR_TP_DST: tun->tp_dst = nl_attr_get_be16(a); break; case OVS_TUNNEL_KEY_ATTR_OAM: tun->flags |= FLOW_TNL_F_OAM; break; case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: { static const struct nl_policy vxlan_opts_policy[] = { [OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 }, }; struct nlattr *ext[ARRAY_SIZE(vxlan_opts_policy)]; if (!nl_parse_nested(a, vxlan_opts_policy, ext, ARRAY_SIZE(ext))) { return ODP_FIT_ERROR; } if (ext[OVS_VXLAN_EXT_GBP]) { uint32_t gbp = nl_attr_get_u32(ext[OVS_VXLAN_EXT_GBP]); tun->gbp_id = htons(gbp & 0xFFFF); tun->gbp_flags = (gbp >> 16) & 0xFF; } break; } case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len, src_tun, udpif, tun)) { return ODP_FIT_ERROR; } break; default: /* Allow this to show up as unexpected, if there are unknown * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */ unknown = true; break; } } if (!ttl) { return ODP_FIT_ERROR; } if (unknown) { return ODP_FIT_TOO_MUCH; } return ODP_FIT_PERFECT; } enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *attr, bool udpif, struct flow_tnl *tun) { memset(tun, 0, sizeof *tun); return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun, udpif); } static void tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key, const struct flow_tnl *tun_flow_key, const struct ofpbuf *key_buf) { size_t tun_key_ofs; tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL); /* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */ if (tun_key->tun_id || tun_key->flags & FLOW_TNL_F_KEY) { nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id); } if (tun_key->ip_src) { nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src); } if (tun_key->ip_dst) { nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst); } if (ipv6_addr_is_set(&tun_key->ipv6_src)) { nl_msg_put_in6_addr(a, OVS_TUNNEL_KEY_ATTR_IPV6_SRC, &tun_key->ipv6_src); } if (ipv6_addr_is_set(&tun_key->ipv6_dst)) { nl_msg_put_in6_addr(a, OVS_TUNNEL_KEY_ATTR_IPV6_DST, &tun_key->ipv6_dst); } if (tun_key->ip_tos) { nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos); } nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl); if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT); } if (tun_key->flags & FLOW_TNL_F_CSUM) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM); } if (tun_key->tp_src) { nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_SRC, tun_key->tp_src); } if (tun_key->tp_dst) { nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_DST, tun_key->tp_dst); } if (tun_key->flags & FLOW_TNL_F_OAM) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM); } if (tun_key->gbp_flags || tun_key->gbp_id) { size_t vxlan_opts_ofs; vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS); nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP, (tun_key->gbp_flags << 16) | ntohs(tun_key->gbp_id)); nl_msg_end_nested(a, vxlan_opts_ofs); } tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a); nl_msg_end_nested(a, tun_key_ofs); } static bool odp_mask_attr_is_wildcard(const struct nlattr *ma) { return is_all_zeros(nl_attr_get(ma), nl_attr_get_size(ma)); } static bool odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size) { if (attr == OVS_KEY_ATTR_TCP_FLAGS) { return TCP_FLAGS(*(ovs_be16 *)mask) == TCP_FLAGS(OVS_BE16_MAX); } if (attr == OVS_KEY_ATTR_IPV6) { const struct ovs_key_ipv6 *ipv6_mask = mask; return ((ipv6_mask->ipv6_label & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK)) && ipv6_mask->ipv6_proto == UINT8_MAX && ipv6_mask->ipv6_tclass == UINT8_MAX && ipv6_mask->ipv6_hlimit == UINT8_MAX && ipv6_mask->ipv6_frag == UINT8_MAX && ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_src) && ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_dst); } if (attr == OVS_KEY_ATTR_TUNNEL) { return false; } if (attr == OVS_KEY_ATTR_ARP) { /* ARP key has padding, ignore it. */ BUILD_ASSERT_DECL(sizeof(struct ovs_key_arp) == 24); BUILD_ASSERT_DECL(offsetof(struct ovs_key_arp, arp_tha) == 10 + 6); size = offsetof(struct ovs_key_arp, arp_tha) + ETH_ADDR_LEN; ovs_assert(((uint16_t *)mask)[size/2] == 0); } return is_all_ones(mask, size); } static bool odp_mask_attr_is_exact(const struct nlattr *ma) { enum ovs_key_attr attr = nl_attr_type(ma); const void *mask; size_t size; if (attr == OVS_KEY_ATTR_TUNNEL) { return false; } else { mask = nl_attr_get(ma); size = nl_attr_get_size(ma); } return odp_mask_is_exact(attr, mask, size); } void odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no, char *port_name) { struct odp_portno_names *odp_portno_names; odp_portno_names = xmalloc(sizeof *odp_portno_names); odp_portno_names->port_no = port_no; odp_portno_names->name = xstrdup(port_name); hmap_insert(portno_names, &odp_portno_names->hmap_node, hash_odp_port(port_no)); } static char * odp_portno_names_get(const struct hmap *portno_names, odp_port_t port_no) { struct odp_portno_names *odp_portno_names; HMAP_FOR_EACH_IN_BUCKET (odp_portno_names, hmap_node, hash_odp_port(port_no), portno_names) { if (odp_portno_names->port_no == port_no) { return odp_portno_names->name; } } return NULL; } void odp_portno_names_destroy(struct hmap *portno_names) { struct odp_portno_names *odp_portno_names, *odp_portno_names_next; HMAP_FOR_EACH_SAFE (odp_portno_names, odp_portno_names_next, hmap_node, portno_names) { hmap_remove(portno_names, &odp_portno_names->hmap_node); free(odp_portno_names->name); free(odp_portno_names); } } /* Format helpers. */ static void format_eth(struct ds *ds, const char *name, const struct eth_addr key, const struct eth_addr *mask, bool verbose) { bool mask_empty = mask && eth_addr_is_zero(*mask); if (verbose || !mask_empty) { bool mask_full = !mask || eth_mask_is_exact(*mask); if (mask_full) { ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, ETH_ADDR_ARGS(key)); } else { ds_put_format(ds, "%s=", name); eth_format_masked(key, mask, ds); ds_put_char(ds, ','); } } } static void format_be64(struct ds *ds, const char *name, ovs_be64 key, const ovs_be64 *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == OVS_BE64_MAX; ds_put_format(ds, "%s=0x%"PRIx64, name, ntohll(key)); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx64, ntohll(*mask)); } ds_put_char(ds, ','); } } static void format_ipv4(struct ds *ds, const char *name, ovs_be32 key, const ovs_be32 *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == OVS_BE32_MAX; ds_put_format(ds, "%s="IP_FMT, name, IP_ARGS(key)); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/"IP_FMT, IP_ARGS(*mask)); } ds_put_char(ds, ','); } } static void format_in6_addr(struct ds *ds, const char *name, const struct in6_addr *key, const struct in6_addr *mask, bool verbose) { char buf[INET6_ADDRSTRLEN]; bool mask_empty = mask && ipv6_mask_is_any(mask); if (verbose || !mask_empty) { bool mask_full = !mask || ipv6_mask_is_exact(mask); inet_ntop(AF_INET6, key, buf, sizeof buf); ds_put_format(ds, "%s=%s", name, buf); if (!mask_full) { /* Partially masked. */ inet_ntop(AF_INET6, mask, buf, sizeof buf); ds_put_format(ds, "/%s", buf); } ds_put_char(ds, ','); } } static void format_ipv6(struct ds *ds, const char *name, const ovs_be32 key_[4], const ovs_be32 (*mask_)[4], bool verbose) { format_in6_addr(ds, name, (const struct in6_addr *)key_, mask_ ? (const struct in6_addr *)*mask_ : NULL, verbose); } static void format_ipv6_label(struct ds *ds, const char *name, ovs_be32 key, const ovs_be32 *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || (*mask & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK); ds_put_format(ds, "%s=%#"PRIx32, name, ntohl(key)); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx32, ntohl(*mask)); } ds_put_char(ds, ','); } } static void format_u8x(struct ds *ds, const char *name, uint8_t key, const uint8_t *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == UINT8_MAX; ds_put_format(ds, "%s=%#"PRIx8, name, key); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx8, *mask); } ds_put_char(ds, ','); } } static void format_u8u(struct ds *ds, const char *name, uint8_t key, const uint8_t *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == UINT8_MAX; ds_put_format(ds, "%s=%"PRIu8, name, key); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx8, *mask); } ds_put_char(ds, ','); } } static void format_be16(struct ds *ds, const char *name, ovs_be16 key, const ovs_be16 *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == OVS_BE16_MAX; ds_put_format(ds, "%s=%"PRIu16, name, ntohs(key)); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx16, ntohs(*mask)); } ds_put_char(ds, ','); } } static void format_be16x(struct ds *ds, const char *name, ovs_be16 key, const ovs_be16 *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { bool mask_full = !mask || *mask == OVS_BE16_MAX; ds_put_format(ds, "%s=%#"PRIx16, name, ntohs(key)); if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "/%#"PRIx16, ntohs(*mask)); } ds_put_char(ds, ','); } } static void format_tun_flags(struct ds *ds, const char *name, uint16_t key, const uint16_t *mask, bool verbose) { bool mask_empty = mask && !*mask; if (verbose || !mask_empty) { ds_put_cstr(ds, name); ds_put_char(ds, '('); if (mask) { format_flags_masked(ds, NULL, flow_tun_flag_to_string, key, *mask & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK); } else { /* Fully masked. */ format_flags(ds, flow_tun_flag_to_string, key, '|'); } ds_put_cstr(ds, "),"); } } static bool check_attr_len(struct ds *ds, const struct nlattr *a, const struct nlattr *ma, const struct attr_len_tbl tbl[], int max_len, bool need_key) { int expected_len; expected_len = odp_key_attr_len(tbl, max_len, nl_attr_type(a)); if (expected_len != ATTR_LEN_VARIABLE && expected_len != ATTR_LEN_NESTED) { bool bad_key_len = nl_attr_get_size(a) != expected_len; bool bad_mask_len = ma && nl_attr_get_size(ma) != expected_len; if (bad_key_len || bad_mask_len) { if (need_key) { ds_put_format(ds, "key%u", nl_attr_type(a)); } if (bad_key_len) { ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(", nl_attr_get_size(a), expected_len); } format_generic_odp_key(a, ds); if (ma) { ds_put_char(ds, '/'); if (bad_mask_len) { ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected %d)(", nl_attr_get_size(ma), expected_len); } format_generic_odp_key(ma, ds); } ds_put_char(ds, ')'); return false; } } return true; } static void format_unknown_key(struct ds *ds, const struct nlattr *a, const struct nlattr *ma) { ds_put_format(ds, "key%u(", nl_attr_type(a)); format_generic_odp_key(a, ds); if (ma && !odp_mask_attr_is_exact(ma)) { ds_put_char(ds, '/'); format_generic_odp_key(ma, ds); } ds_put_cstr(ds, "),"); } static void format_odp_tun_vxlan_opt(const struct nlattr *attr, const struct nlattr *mask_attr, struct ds *ds, bool verbose) { unsigned int left; const struct nlattr *a; struct ofpbuf ofp; ofpbuf_init(&ofp, 100); NL_NESTED_FOR_EACH(a, left, attr) { uint16_t type = nl_attr_type(a); const struct nlattr *ma = NULL; if (mask_attr) { ma = nl_attr_find__(nl_attr_get(mask_attr), nl_attr_get_size(mask_attr), type); if (!ma) { ma = generate_all_wildcard_mask(ovs_vxlan_ext_attr_lens, OVS_VXLAN_EXT_MAX, &ofp, a); } } if (!check_attr_len(ds, a, ma, ovs_vxlan_ext_attr_lens, OVS_VXLAN_EXT_MAX, true)) { continue; } switch (type) { case OVS_VXLAN_EXT_GBP: { uint32_t key = nl_attr_get_u32(a); ovs_be16 id, id_mask; uint8_t flags, flags_mask; id = htons(key & 0xFFFF); flags = (key >> 16) & 0xFF; if (ma) { uint32_t mask = nl_attr_get_u32(ma); id_mask = htons(mask & 0xFFFF); flags_mask = (mask >> 16) & 0xFF; } ds_put_cstr(ds, "gbp("); format_be16(ds, "id", id, ma ? &id_mask : NULL, verbose); format_u8x(ds, "flags", flags, ma ? &flags_mask : NULL, verbose); ds_chomp(ds, ','); ds_put_cstr(ds, "),"); break; } default: format_unknown_key(ds, a, ma); } ofpbuf_clear(&ofp); } ds_chomp(ds, ','); ofpbuf_uninit(&ofp); } #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL static void format_geneve_opts(const struct geneve_opt *opt, const struct geneve_opt *mask, int opts_len, struct ds *ds, bool verbose) { while (opts_len > 0) { unsigned int len; uint8_t data_len, data_len_mask; if (opts_len < sizeof *opt) { ds_put_format(ds, "opt len %u less than minimum %"PRIuSIZE, opts_len, sizeof *opt); return; } data_len = opt->length * 4; if (mask) { if (mask->length == 0x1f) { data_len_mask = UINT8_MAX; } else { data_len_mask = mask->length; } } len = sizeof *opt + data_len; if (len > opts_len) { ds_put_format(ds, "opt len %u greater than remaining %u", len, opts_len); return; } ds_put_char(ds, '{'); format_be16x(ds, "class", opt->opt_class, MASK(mask, opt_class), verbose); format_u8x(ds, "type", opt->type, MASK(mask, type), verbose); format_u8u(ds, "len", data_len, mask ? &data_len_mask : NULL, verbose); if (data_len && (verbose || !mask || !is_all_zeros(mask + 1, data_len))) { ds_put_hex(ds, opt + 1, data_len); if (mask && !is_all_ones(mask + 1, data_len)) { ds_put_char(ds, '/'); ds_put_hex(ds, mask + 1, data_len); } } else { ds_chomp(ds, ','); } ds_put_char(ds, '}'); opt += len / sizeof(*opt); if (mask) { mask += len / sizeof(*opt); } opts_len -= len; }; } static void format_odp_tun_geneve(const struct nlattr *attr, const struct nlattr *mask_attr, struct ds *ds, bool verbose) { int opts_len = nl_attr_get_size(attr); const struct geneve_opt *opt = nl_attr_get(attr); const struct geneve_opt *mask = mask_attr ? nl_attr_get(mask_attr) : NULL; if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) { ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE, nl_attr_get_size(attr), nl_attr_get_size(mask_attr)); return; } format_geneve_opts(opt, mask, opts_len, ds, verbose); } static void format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr, struct ds *ds, bool verbose) { unsigned int left; const struct nlattr *a; uint16_t flags = 0; uint16_t mask_flags = 0; struct ofpbuf ofp; ofpbuf_init(&ofp, 100); NL_NESTED_FOR_EACH(a, left, attr) { enum ovs_tunnel_key_attr type = nl_attr_type(a); const struct nlattr *ma = NULL; if (mask_attr) { ma = nl_attr_find__(nl_attr_get(mask_attr), nl_attr_get_size(mask_attr), type); if (!ma) { ma = generate_all_wildcard_mask(ovs_tun_key_attr_lens, OVS_TUNNEL_KEY_ATTR_MAX, &ofp, a); } } if (!check_attr_len(ds, a, ma, ovs_tun_key_attr_lens, OVS_TUNNEL_KEY_ATTR_MAX, true)) { continue; } switch (type) { case OVS_TUNNEL_KEY_ATTR_ID: format_be64(ds, "tun_id", nl_attr_get_be64(a), ma ? nl_attr_get(ma) : NULL, verbose); flags |= FLOW_TNL_F_KEY; if (ma) { mask_flags |= FLOW_TNL_F_KEY; } break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: format_ipv4(ds, "src", nl_attr_get_be32(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: format_ipv4(ds, "dst", nl_attr_get_be32(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: { struct in6_addr ipv6_src; ipv6_src = nl_attr_get_in6_addr(a); format_in6_addr(ds, "ipv6_src", &ipv6_src, ma ? nl_attr_get(ma) : NULL, verbose); break; } case OVS_TUNNEL_KEY_ATTR_IPV6_DST: { struct in6_addr ipv6_dst; ipv6_dst = nl_attr_get_in6_addr(a); format_in6_addr(ds, "ipv6_dst", &ipv6_dst, ma ? nl_attr_get(ma) : NULL, verbose); break; } case OVS_TUNNEL_KEY_ATTR_TOS: format_u8x(ds, "tos", nl_attr_get_u8(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_TTL: format_u8u(ds, "ttl", nl_attr_get_u8(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: flags |= FLOW_TNL_F_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: flags |= FLOW_TNL_F_CSUM; break; case OVS_TUNNEL_KEY_ATTR_TP_SRC: format_be16(ds, "tp_src", nl_attr_get_be16(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_TP_DST: format_be16(ds, "tp_dst", nl_attr_get_be16(a), ma ? nl_attr_get(ma) : NULL, verbose); break; case OVS_TUNNEL_KEY_ATTR_OAM: flags |= FLOW_TNL_F_OAM; break; case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: ds_put_cstr(ds, "vxlan("); format_odp_tun_vxlan_opt(a, ma, ds, verbose); ds_put_cstr(ds, "),"); break; case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: ds_put_cstr(ds, "geneve("); format_odp_tun_geneve(a, ma, ds, verbose); ds_put_cstr(ds, "),"); break; case __OVS_TUNNEL_KEY_ATTR_MAX: default: format_unknown_key(ds, a, ma); } ofpbuf_clear(&ofp); } /* Flags can have a valid mask even if the attribute is not set, so * we need to collect these separately. */ if (mask_attr) { NL_NESTED_FOR_EACH(a, left, mask_attr) { switch (nl_attr_type(a)) { case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: mask_flags |= FLOW_TNL_F_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: mask_flags |= FLOW_TNL_F_CSUM; break; case OVS_TUNNEL_KEY_ATTR_OAM: mask_flags |= FLOW_TNL_F_OAM; break; } } } format_tun_flags(ds, "flags", flags, mask_attr ? &mask_flags : NULL, verbose); ds_chomp(ds, ','); ofpbuf_uninit(&ofp); } static const char * odp_ct_state_to_string(uint32_t flag) { switch (flag) { case OVS_CS_F_REPLY_DIR: return "rpl"; case OVS_CS_F_TRACKED: return "trk"; case OVS_CS_F_NEW: return "new"; case OVS_CS_F_ESTABLISHED: return "est"; case OVS_CS_F_RELATED: return "rel"; case OVS_CS_F_INVALID: return "inv"; default: return NULL; } } static void format_frag(struct ds *ds, const char *name, uint8_t key, const uint8_t *mask, bool verbose) { bool mask_empty = mask && !*mask; /* ODP frag is an enumeration field; partial masks are not meaningful. */ if (verbose || !mask_empty) { bool mask_full = !mask || *mask == UINT8_MAX; if (!mask_full) { /* Partially masked. */ ds_put_format(ds, "error: partial mask not supported for frag (%#" PRIx8"),", *mask); } else { ds_put_format(ds, "%s=%s,", name, ovs_frag_type_to_string(key)); } } } static bool mask_empty(const struct nlattr *ma) { const void *mask; size_t n; if (!ma) { return true; } mask = nl_attr_get(ma); n = nl_attr_get_size(ma); return is_all_zeros(mask, n); } static void format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, const struct hmap *portno_names, struct ds *ds, bool verbose) { enum ovs_key_attr attr = nl_attr_type(a); char namebuf[OVS_KEY_ATTR_BUFSIZE]; bool is_exact; is_exact = ma ? odp_mask_attr_is_exact(ma) : true; ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf)); if (!check_attr_len(ds, a, ma, ovs_flow_key_attr_lens, OVS_KEY_ATTR_MAX, false)) { return; } ds_put_char(ds, '('); switch (attr) { case OVS_KEY_ATTR_ENCAP: if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) { odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds, verbose); } else if (nl_attr_get_size(a)) { odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL, ds, verbose); } break; case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_DP_HASH: case OVS_KEY_ATTR_RECIRC_ID: ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma)); } break; case OVS_KEY_ATTR_CT_MARK: if (verbose || !mask_empty(ma)) { ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma)); } } break; case OVS_KEY_ATTR_CT_STATE: if (verbose) { ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx32, mask_empty(ma) ? 0 : nl_attr_get_u32(ma)); } } else if (!is_exact) { format_flags_masked(ds, NULL, odp_ct_state_to_string, nl_attr_get_u32(a), mask_empty(ma) ? 0 : nl_attr_get_u32(ma), UINT32_MAX); } else { format_flags(ds, odp_ct_state_to_string, nl_attr_get_u32(a), '|'); } break; case OVS_KEY_ATTR_CT_ZONE: if (verbose || !mask_empty(ma)) { ds_put_format(ds, "%#"PRIx16, nl_attr_get_u16(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx16, nl_attr_get_u16(ma)); } } break; case OVS_KEY_ATTR_CT_LABELS: { const ovs_u128 *value = nl_attr_get(a); const ovs_u128 *mask = ma ? nl_attr_get(ma) : NULL; format_u128(ds, value, mask, verbose); break; } case OVS_KEY_ATTR_TUNNEL: format_odp_tun_attr(a, ma, ds, verbose); break; case OVS_KEY_ATTR_IN_PORT: if (portno_names && verbose && is_exact) { char *name = odp_portno_names_get(portno_names, u32_to_odp(nl_attr_get_u32(a))); if (name) { ds_put_format(ds, "%s", name); } else { ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); } } else { ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma)); } } break; case OVS_KEY_ATTR_ETHERNET: { const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL; const struct ovs_key_ethernet *key = nl_attr_get(a); format_eth(ds, "src", key->eth_src, MASK(mask, eth_src), verbose); format_eth(ds, "dst", key->eth_dst, MASK(mask, eth_dst), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_VLAN: format_vlan_tci(ds, nl_attr_get_be16(a), ma ? nl_attr_get_be16(ma) : OVS_BE16_MAX, verbose); break; case OVS_KEY_ATTR_MPLS: { const struct ovs_key_mpls *mpls_key = nl_attr_get(a); const struct ovs_key_mpls *mpls_mask = NULL; size_t size = nl_attr_get_size(a); if (!size || size % sizeof *mpls_key) { ds_put_format(ds, "(bad key length %"PRIuSIZE")", size); return; } if (!is_exact) { mpls_mask = nl_attr_get(ma); if (size != nl_attr_get_size(ma)) { ds_put_format(ds, "(key length %"PRIuSIZE" != " "mask length %"PRIuSIZE")", size, nl_attr_get_size(ma)); return; } } format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key); break; } case OVS_KEY_ATTR_ETHERTYPE: ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a))); if (!is_exact) { ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma))); } break; case OVS_KEY_ATTR_IPV4: { const struct ovs_key_ipv4 *key = nl_attr_get(a); const struct ovs_key_ipv4 *mask = ma ? nl_attr_get(ma) : NULL; format_ipv4(ds, "src", key->ipv4_src, MASK(mask, ipv4_src), verbose); format_ipv4(ds, "dst", key->ipv4_dst, MASK(mask, ipv4_dst), verbose); format_u8u(ds, "proto", key->ipv4_proto, MASK(mask, ipv4_proto), verbose); format_u8x(ds, "tos", key->ipv4_tos, MASK(mask, ipv4_tos), verbose); format_u8u(ds, "ttl", key->ipv4_ttl, MASK(mask, ipv4_ttl), verbose); format_frag(ds, "frag", key->ipv4_frag, MASK(mask, ipv4_frag), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_IPV6: { const struct ovs_key_ipv6 *key = nl_attr_get(a); const struct ovs_key_ipv6 *mask = ma ? nl_attr_get(ma) : NULL; format_ipv6(ds, "src", key->ipv6_src, MASK(mask, ipv6_src), verbose); format_ipv6(ds, "dst", key->ipv6_dst, MASK(mask, ipv6_dst), verbose); format_ipv6_label(ds, "label", key->ipv6_label, MASK(mask, ipv6_label), verbose); format_u8u(ds, "proto", key->ipv6_proto, MASK(mask, ipv6_proto), verbose); format_u8x(ds, "tclass", key->ipv6_tclass, MASK(mask, ipv6_tclass), verbose); format_u8u(ds, "hlimit", key->ipv6_hlimit, MASK(mask, ipv6_hlimit), verbose); format_frag(ds, "frag", key->ipv6_frag, MASK(mask, ipv6_frag), verbose); ds_chomp(ds, ','); break; } /* These have the same structure and format. */ case OVS_KEY_ATTR_TCP: case OVS_KEY_ATTR_UDP: case OVS_KEY_ATTR_SCTP: { const struct ovs_key_tcp *key = nl_attr_get(a); const struct ovs_key_tcp *mask = ma ? nl_attr_get(ma) : NULL; format_be16(ds, "src", key->tcp_src, MASK(mask, tcp_src), verbose); format_be16(ds, "dst", key->tcp_dst, MASK(mask, tcp_dst), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_TCP_FLAGS: if (!is_exact) { format_flags_masked(ds, NULL, packet_tcp_flag_to_string, ntohs(nl_attr_get_be16(a)), TCP_FLAGS(nl_attr_get_be16(ma)), TCP_FLAGS(OVS_BE16_MAX)); } else { format_flags(ds, packet_tcp_flag_to_string, ntohs(nl_attr_get_be16(a)), '|'); } break; case OVS_KEY_ATTR_ICMP: { const struct ovs_key_icmp *key = nl_attr_get(a); const struct ovs_key_icmp *mask = ma ? nl_attr_get(ma) : NULL; format_u8u(ds, "type", key->icmp_type, MASK(mask, icmp_type), verbose); format_u8u(ds, "code", key->icmp_code, MASK(mask, icmp_code), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_ICMPV6: { const struct ovs_key_icmpv6 *key = nl_attr_get(a); const struct ovs_key_icmpv6 *mask = ma ? nl_attr_get(ma) : NULL; format_u8u(ds, "type", key->icmpv6_type, MASK(mask, icmpv6_type), verbose); format_u8u(ds, "code", key->icmpv6_code, MASK(mask, icmpv6_code), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_ARP: { const struct ovs_key_arp *mask = ma ? nl_attr_get(ma) : NULL; const struct ovs_key_arp *key = nl_attr_get(a); format_ipv4(ds, "sip", key->arp_sip, MASK(mask, arp_sip), verbose); format_ipv4(ds, "tip", key->arp_tip, MASK(mask, arp_tip), verbose); format_be16(ds, "op", key->arp_op, MASK(mask, arp_op), verbose); format_eth(ds, "sha", key->arp_sha, MASK(mask, arp_sha), verbose); format_eth(ds, "tha", key->arp_tha, MASK(mask, arp_tha), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_ND: { const struct ovs_key_nd *mask = ma ? nl_attr_get(ma) : NULL; const struct ovs_key_nd *key = nl_attr_get(a); format_ipv6(ds, "target", key->nd_target, MASK(mask, nd_target), verbose); format_eth(ds, "sll", key->nd_sll, MASK(mask, nd_sll), verbose); format_eth(ds, "tll", key->nd_tll, MASK(mask, nd_tll), verbose); ds_chomp(ds, ','); break; } case OVS_KEY_ATTR_UNSPEC: case __OVS_KEY_ATTR_MAX: default: format_generic_odp_key(a, ds); if (!is_exact) { ds_put_char(ds, '/'); format_generic_odp_key(ma, ds); } break; } ds_put_char(ds, ')'); } static struct nlattr * generate_all_wildcard_mask(const struct attr_len_tbl tbl[], int max, struct ofpbuf *ofp, const struct nlattr *key) { const struct nlattr *a; unsigned int left; int type = nl_attr_type(key); int size = nl_attr_get_size(key); if (odp_key_attr_len(tbl, max, type) != ATTR_LEN_NESTED) { nl_msg_put_unspec_zero(ofp, type, size); } else { size_t nested_mask; if (tbl[type].next) { const struct attr_len_tbl *entry = &tbl[type]; tbl = entry->next; max = entry->next_max; } nested_mask = nl_msg_start_nested(ofp, type); NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) { generate_all_wildcard_mask(tbl, max, ofp, nl_attr_get(a)); } nl_msg_end_nested(ofp, nested_mask); } return ofp->base; } static void format_u128(struct ds *ds, const ovs_u128 *key, const ovs_u128 *mask, bool verbose) { if (verbose || (mask && !ovs_u128_is_zero(mask))) { ovs_be128 value; value = hton128(*key); ds_put_hex(ds, &value, sizeof value); if (mask && !(ovs_u128_is_ones(mask))) { value = hton128(*mask); ds_put_char(ds, '/'); ds_put_hex(ds, &value, sizeof value); } } } static int scan_u128(const char *s_, ovs_u128 *value, ovs_u128 *mask) { char *s = CONST_CAST(char *, s_); ovs_be128 be_value; ovs_be128 be_mask; if (!parse_int_string(s, (uint8_t *)&be_value, sizeof be_value, &s)) { *value = ntoh128(be_value); if (mask) { int n; if (ovs_scan(s, "/%n", &n)) { int error; s += n; error = parse_int_string(s, (uint8_t *)&be_mask, sizeof be_mask, &s); if (error) { return 0; } *mask = ntoh128(be_mask); } else { *mask = OVS_U128_MAX; } } return s - s_; } return 0; } int odp_ufid_from_string(const char *s_, ovs_u128 *ufid) { const char *s = s_; if (ovs_scan(s, "ufid:")) { s += 5; if (!uuid_from_string_prefix((struct uuid *)ufid, s)) { return -EINVAL; } s += UUID_LEN; return s - s_; } return 0; } void odp_format_ufid(const ovs_u128 *ufid, struct ds *ds) { ds_put_format(ds, "ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid)); } /* Appends to 'ds' a string representation of the 'key_len' bytes of * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the * 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is * non-null and 'verbose' is true, translates odp port number to its name. */ void odp_flow_format(const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, const struct hmap *portno_names, struct ds *ds, bool verbose) { if (key_len) { const struct nlattr *a; unsigned int left; bool has_ethtype_key = false; const struct nlattr *ma = NULL; struct ofpbuf ofp; bool first_field = true; ofpbuf_init(&ofp, 100); NL_ATTR_FOR_EACH (a, left, key, key_len) { bool is_nested_attr; bool is_wildcard = false; int attr_type = nl_attr_type(a); if (attr_type == OVS_KEY_ATTR_ETHERTYPE) { has_ethtype_key = true; } is_nested_attr = odp_key_attr_len(ovs_flow_key_attr_lens, OVS_KEY_ATTR_MAX, attr_type) == ATTR_LEN_NESTED; if (mask && mask_len) { ma = nl_attr_find__(mask, mask_len, nl_attr_type(a)); is_wildcard = ma ? odp_mask_attr_is_wildcard(ma) : true; } if (verbose || !is_wildcard || is_nested_attr) { if (is_wildcard && !ma) { ma = generate_all_wildcard_mask(ovs_flow_key_attr_lens, OVS_KEY_ATTR_MAX, &ofp, a); } if (!first_field) { ds_put_char(ds, ','); } format_odp_key_attr(a, ma, portno_names, ds, verbose); first_field = false; } ofpbuf_clear(&ofp); } ofpbuf_uninit(&ofp); if (left) { int i; if (left == key_len) { ds_put_cstr(ds, ""); } ds_put_format(ds, ",***%u leftover bytes*** (", left); for (i = 0; i < left; i++) { ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]); } ds_put_char(ds, ')'); } if (!has_ethtype_key) { ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE); if (ma) { ds_put_format(ds, ",eth_type(0/0x%04"PRIx16")", ntohs(nl_attr_get_be16(ma))); } } } else { ds_put_cstr(ds, ""); } } /* Appends to 'ds' a string representation of the 'key_len' bytes of * OVS_KEY_ATTR_* attributes in 'key'. */ void odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) { odp_flow_format(key, key_len, NULL, 0, NULL, ds, true); } static bool ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type) { if (!strcasecmp(s, "no")) { *type = OVS_FRAG_TYPE_NONE; } else if (!strcasecmp(s, "first")) { *type = OVS_FRAG_TYPE_FIRST; } else if (!strcasecmp(s, "later")) { *type = OVS_FRAG_TYPE_LATER; } else { return false; } return true; } /* Parsing. */ static int scan_eth(const char *s, struct eth_addr *key, struct eth_addr *mask) { int n; if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*key), &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/"ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*mask), &n)) { len += n; } else { memset(mask, 0xff, sizeof *mask); } } return len; } return 0; } static int scan_ipv4(const char *s, ovs_be32 *key, ovs_be32 *mask) { int n; if (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(key), &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/"IP_SCAN_FMT"%n", IP_SCAN_ARGS(mask), &n)) { len += n; } else { *mask = OVS_BE32_MAX; } } return len; } return 0; } static int scan_in6_addr(const char *s, struct in6_addr *key, struct in6_addr *mask) { int n; char ipv6_s[IPV6_SCAN_LEN + 1]; if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && inet_pton(AF_INET6, ipv6_s, key) == 1) { int len = n; if (mask) { if (ovs_scan(s + len, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n) && inet_pton(AF_INET6, ipv6_s, mask) == 1) { len += n; } else { memset(mask, 0xff, sizeof *mask); } } return len; } return 0; } static int scan_ipv6(const char *s, ovs_be32 (*key)[4], ovs_be32 (*mask)[4]) { return scan_in6_addr(s, key ? (struct in6_addr *) *key : NULL, mask ? (struct in6_addr *) *mask : NULL); } static int scan_ipv6_label(const char *s, ovs_be32 *key, ovs_be32 *mask) { int key_, mask_; int n; if (ovs_scan(s, "%i%n", &key_, &n) && (key_ & ~IPV6_LABEL_MASK) == 0) { int len = n; *key = htonl(key_); if (mask) { if (ovs_scan(s + len, "/%i%n", &mask_, &n) && (mask_ & ~IPV6_LABEL_MASK) == 0) { len += n; *mask = htonl(mask_); } else { *mask = htonl(IPV6_LABEL_MASK); } } return len; } return 0; } static int scan_u8(const char *s, uint8_t *key, uint8_t *mask) { int n; if (ovs_scan(s, "%"SCNi8"%n", key, &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/%"SCNi8"%n", mask, &n)) { len += n; } else { *mask = UINT8_MAX; } } return len; } return 0; } static int scan_u16(const char *s, uint16_t *key, uint16_t *mask) { int n; if (ovs_scan(s, "%"SCNi16"%n", key, &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/%"SCNi16"%n", mask, &n)) { len += n; } else { *mask = UINT16_MAX; } } return len; } return 0; } static int scan_u32(const char *s, uint32_t *key, uint32_t *mask) { int n; if (ovs_scan(s, "%"SCNi32"%n", key, &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) { len += n; } else { *mask = UINT32_MAX; } } return len; } return 0; } static int scan_be16(const char *s, ovs_be16 *key, ovs_be16 *mask) { uint16_t key_, mask_; int n; if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) { int len = n; *key = htons(key_); if (mask) { if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) { len += n; *mask = htons(mask_); } else { *mask = OVS_BE16_MAX; } } return len; } return 0; } static int scan_be64(const char *s, ovs_be64 *key, ovs_be64 *mask) { uint64_t key_, mask_; int n; if (ovs_scan(s, "%"SCNi64"%n", &key_, &n)) { int len = n; *key = htonll(key_); if (mask) { if (ovs_scan(s + len, "/%"SCNi64"%n", &mask_, &n)) { len += n; *mask = htonll(mask_); } else { *mask = OVS_BE64_MAX; } } return len; } return 0; } static int scan_tun_flags(const char *s, uint16_t *key, uint16_t *mask) { uint32_t flags, fmask; int n; n = parse_odp_flags(s, flow_tun_flag_to_string, &flags, FLOW_TNL_F_MASK, mask ? &fmask : NULL); if (n >= 0 && s[n] == ')') { *key = flags; if (mask) { *mask = fmask; } return n + 1; } return 0; } static int scan_tcp_flags(const char *s, ovs_be16 *key, ovs_be16 *mask) { uint32_t flags, fmask; int n; n = parse_odp_flags(s, packet_tcp_flag_to_string, &flags, TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL); if (n >= 0) { *key = htons(flags); if (mask) { *mask = htons(fmask); } return n; } return 0; } static uint32_t ovs_to_odp_ct_state(uint8_t state) { uint32_t odp = 0; if (state & CS_NEW) { odp |= OVS_CS_F_NEW; } if (state & CS_ESTABLISHED) { odp |= OVS_CS_F_ESTABLISHED; } if (state & CS_RELATED) { odp |= OVS_CS_F_RELATED; } if (state & CS_INVALID) { odp |= OVS_CS_F_INVALID; } if (state & CS_REPLY_DIR) { odp |= OVS_CS_F_REPLY_DIR; } if (state & CS_TRACKED) { odp |= OVS_CS_F_TRACKED; } return odp; } static uint8_t odp_to_ovs_ct_state(uint32_t flags) { uint32_t state = 0; if (flags & OVS_CS_F_NEW) { state |= CS_NEW; } if (flags & OVS_CS_F_ESTABLISHED) { state |= CS_ESTABLISHED; } if (flags & OVS_CS_F_RELATED) { state |= CS_RELATED; } if (flags & OVS_CS_F_INVALID) { state |= CS_INVALID; } if (flags & OVS_CS_F_REPLY_DIR) { state |= CS_REPLY_DIR; } if (flags & OVS_CS_F_TRACKED) { state |= CS_TRACKED; } return state; } static int scan_ct_state(const char *s, uint32_t *key, uint32_t *mask) { uint32_t flags, fmask; int n; n = parse_flags(s, odp_ct_state_to_string, ')', NULL, NULL, &flags, ovs_to_odp_ct_state(CS_SUPPORTED_MASK), mask ? &fmask : NULL); if (n >= 0) { *key = flags; if (mask) { *mask = fmask; } return n; } return 0; } static int scan_frag(const char *s, uint8_t *key, uint8_t *mask) { int n; char frag[8]; enum ovs_frag_type frag_type; if (ovs_scan(s, "%7[a-z]%n", frag, &n) && ovs_frag_type_from_string(frag, &frag_type)) { int len = n; *key = frag_type; if (mask) { *mask = UINT8_MAX; } return len; } return 0; } static int scan_port(const char *s, uint32_t *key, uint32_t *mask, const struct simap *port_names) { int n; if (ovs_scan(s, "%"SCNi32"%n", key, &n)) { int len = n; if (mask) { if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) { len += n; } else { *mask = UINT32_MAX; } } return len; } else if (port_names) { const struct simap_node *node; int len; len = strcspn(s, ")"); node = simap_find_len(port_names, s, len); if (node) { *key = node->data; if (mask) { *mask = UINT32_MAX; } return len; } } return 0; } /* Helper for vlan parsing. */ struct ovs_key_vlan__ { ovs_be16 tci; }; static bool set_be16_bf(ovs_be16 *bf, uint8_t bits, uint8_t offset, uint16_t value) { const uint16_t mask = ((1U << bits) - 1) << offset; if (value >> bits) { return false; } *bf = htons((ntohs(*bf) & ~mask) | (value << offset)); return true; } static int scan_be16_bf(const char *s, ovs_be16 *key, ovs_be16 *mask, uint8_t bits, uint8_t offset) { uint16_t key_, mask_; int n; if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) { int len = n; if (set_be16_bf(key, bits, offset, key_)) { if (mask) { if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) { len += n; if (!set_be16_bf(mask, bits, offset, mask_)) { return 0; } } else { *mask |= htons(((1U << bits) - 1) << offset); } } return len; } } return 0; } static int scan_vid(const char *s, ovs_be16 *key, ovs_be16 *mask) { return scan_be16_bf(s, key, mask, 12, VLAN_VID_SHIFT); } static int scan_pcp(const char *s, ovs_be16 *key, ovs_be16 *mask) { return scan_be16_bf(s, key, mask, 3, VLAN_PCP_SHIFT); } static int scan_cfi(const char *s, ovs_be16 *key, ovs_be16 *mask) { return scan_be16_bf(s, key, mask, 1, VLAN_CFI_SHIFT); } /* For MPLS. */ static bool set_be32_bf(ovs_be32 *bf, uint8_t bits, uint8_t offset, uint32_t value) { const uint32_t mask = ((1U << bits) - 1) << offset; if (value >> bits) { return false; } *bf = htonl((ntohl(*bf) & ~mask) | (value << offset)); return true; } static int scan_be32_bf(const char *s, ovs_be32 *key, ovs_be32 *mask, uint8_t bits, uint8_t offset) { uint32_t key_, mask_; int n; if (ovs_scan(s, "%"SCNi32"%n", &key_, &n)) { int len = n; if (set_be32_bf(key, bits, offset, key_)) { if (mask) { if (ovs_scan(s + len, "/%"SCNi32"%n", &mask_, &n)) { len += n; if (!set_be32_bf(mask, bits, offset, mask_)) { return 0; } } else { *mask |= htonl(((1U << bits) - 1) << offset); } } return len; } } return 0; } static int scan_mpls_label(const char *s, ovs_be32 *key, ovs_be32 *mask) { return scan_be32_bf(s, key, mask, 20, MPLS_LABEL_SHIFT); } static int scan_mpls_tc(const char *s, ovs_be32 *key, ovs_be32 *mask) { return scan_be32_bf(s, key, mask, 3, MPLS_TC_SHIFT); } static int scan_mpls_ttl(const char *s, ovs_be32 *key, ovs_be32 *mask) { return scan_be32_bf(s, key, mask, 8, MPLS_TTL_SHIFT); } static int scan_mpls_bos(const char *s, ovs_be32 *key, ovs_be32 *mask) { return scan_be32_bf(s, key, mask, 1, MPLS_BOS_SHIFT); } static int scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask) { const char *s_base = s; ovs_be16 id = 0, id_mask = 0; uint8_t flags = 0, flags_mask = 0; if (!strncmp(s, "id=", 3)) { s += 3; s += scan_be16(s, &id, mask ? &id_mask : NULL); } if (s[0] == ',') { s++; } if (!strncmp(s, "flags=", 6)) { s += 6; s += scan_u8(s, &flags, mask ? &flags_mask : NULL); } if (!strncmp(s, "))", 2)) { s += 2; *key = (flags << 16) | ntohs(id); if (mask) { *mask = (flags_mask << 16) | ntohs(id_mask); } return s - s_base; } return 0; } static int scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask) { const char *s_base = s; struct geneve_opt *opt = key->d; struct geneve_opt *opt_mask = mask ? mask->d : NULL; int len_remain = sizeof key->d; while (s[0] == '{' && len_remain >= sizeof *opt) { int data_len = 0; s++; len_remain -= sizeof *opt; if (!strncmp(s, "class=", 6)) { s += 6; s += scan_be16(s, &opt->opt_class, mask ? &opt_mask->opt_class : NULL); } else if (mask) { memset(&opt_mask->opt_class, 0, sizeof opt_mask->opt_class); } if (s[0] == ',') { s++; } if (!strncmp(s, "type=", 5)) { s += 5; s += scan_u8(s, &opt->type, mask ? &opt_mask->type : NULL); } else if (mask) { memset(&opt_mask->type, 0, sizeof opt_mask->type); } if (s[0] == ',') { s++; } if (!strncmp(s, "len=", 4)) { uint8_t opt_len, opt_len_mask; s += 4; s += scan_u8(s, &opt_len, mask ? &opt_len_mask : NULL); if (opt_len > 124 || opt_len % 4 || opt_len > len_remain) { return 0; } opt->length = opt_len / 4; if (mask) { opt_mask->length = opt_len_mask; } data_len = opt_len; } else if (mask) { memset(&opt_mask->type, 0, sizeof opt_mask->type); } if (s[0] == ',') { s++; } if (parse_int_string(s, (uint8_t *)(opt + 1), data_len, (char **)&s)) { return 0; } if (mask) { if (s[0] == '/') { s++; if (parse_int_string(s, (uint8_t *)(opt_mask + 1), data_len, (char **)&s)) { return 0; } } opt_mask->r1 = 0; opt_mask->r2 = 0; opt_mask->r3 = 0; } if (s[0] == '}') { s++; opt += 1 + data_len / 4; if (mask) { opt_mask += 1 + data_len / 4; } len_remain -= data_len; } } if (s[0] == ')') { int len = sizeof key->d - len_remain; s++; key->len = len; if (mask) { mask->len = len; } return s - s_base; } return 0; } static void tun_flags_to_attr(struct ofpbuf *a, const void *data_) { const uint16_t *flags = data_; if (*flags & FLOW_TNL_F_DONT_FRAGMENT) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT); } if (*flags & FLOW_TNL_F_CSUM) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM); } if (*flags & FLOW_TNL_F_OAM) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM); } } static void vxlan_gbp_to_attr(struct ofpbuf *a, const void *data_) { const uint32_t *gbp = data_; if (*gbp) { size_t vxlan_opts_ofs; vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS); nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP, *gbp); nl_msg_end_nested(a, vxlan_opts_ofs); } } static void geneve_to_attr(struct ofpbuf *a, const void *data_) { const struct geneve_scan *geneve = data_; nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, geneve->d, geneve->len); } #define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC) \ { \ unsigned long call_fn = (unsigned long)FUNC; \ if (call_fn) { \ typedef void (*fn)(struct ofpbuf *, const void *); \ fn func = FUNC; \ func(BUF, &(DATA)); \ } else { \ nl_msg_put_unspec(BUF, ATTR, &(DATA), sizeof (DATA)); \ } \ } #define SCAN_IF(NAME) \ if (strncmp(s, NAME, strlen(NAME)) == 0) { \ const char *start = s; \ int len; \ \ s += strlen(NAME) /* Usually no special initialization is needed. */ #define SCAN_BEGIN(NAME, TYPE) \ SCAN_IF(NAME); \ TYPE skey, smask; \ memset(&skey, 0, sizeof skey); \ memset(&smask, 0, sizeof smask); \ do { \ len = 0; /* Init as fully-masked as mask will not be scanned. */ #define SCAN_BEGIN_FULLY_MASKED(NAME, TYPE) \ SCAN_IF(NAME); \ TYPE skey, smask; \ memset(&skey, 0, sizeof skey); \ memset(&smask, 0xff, sizeof smask); \ do { \ len = 0; /* VLAN needs special initialization. */ #define SCAN_BEGIN_INIT(NAME, TYPE, KEY_INIT, MASK_INIT) \ SCAN_IF(NAME); \ TYPE skey = KEY_INIT; \ TYPE smask = MASK_INIT; \ do { \ len = 0; /* Scan unnamed entry as 'TYPE' */ #define SCAN_TYPE(TYPE, KEY, MASK) \ len = scan_##TYPE(s, KEY, MASK); \ if (len == 0) { \ return -EINVAL; \ } \ s += len /* Scan named ('NAME') entry 'FIELD' as 'TYPE'. */ #define SCAN_FIELD(NAME, TYPE, FIELD) \ if (strncmp(s, NAME, strlen(NAME)) == 0) { \ s += strlen(NAME); \ SCAN_TYPE(TYPE, &skey.FIELD, mask ? &smask.FIELD : NULL); \ continue; \ } #define SCAN_FINISH() \ } while (*s++ == ',' && len != 0); \ if (s[-1] != ')') { \ return -EINVAL; \ } #define SCAN_FINISH_SINGLE() \ } while (false); \ if (*s++ != ')') { \ return -EINVAL; \ } /* Beginning of nested attribute. */ #define SCAN_BEGIN_NESTED(NAME, ATTR) \ SCAN_IF(NAME); \ size_t key_offset, mask_offset; \ key_offset = nl_msg_start_nested(key, ATTR); \ if (mask) { \ mask_offset = nl_msg_start_nested(mask, ATTR); \ } \ do { \ len = 0; #define SCAN_END_NESTED() \ SCAN_FINISH(); \ nl_msg_end_nested(key, key_offset); \ if (mask) { \ nl_msg_end_nested(mask, mask_offset); \ } \ return s - start; \ } #define SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, ATTR, FUNC) \ if (strncmp(s, NAME, strlen(NAME)) == 0) { \ TYPE skey, smask; \ memset(&skey, 0, sizeof skey); \ memset(&smask, 0xff, sizeof smask); \ s += strlen(NAME); \ SCAN_TYPE(SCAN_AS, &skey, &smask); \ SCAN_PUT(ATTR, FUNC); \ continue; \ } #define SCAN_FIELD_NESTED(NAME, TYPE, SCAN_AS, ATTR) \ SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, ATTR, NULL) #define SCAN_FIELD_NESTED_FUNC(NAME, TYPE, SCAN_AS, FUNC) \ SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, 0, FUNC) #define SCAN_PUT(ATTR, FUNC) \ if (!mask || !is_all_zeros(&smask, sizeof smask)) { \ SCAN_PUT_ATTR(key, ATTR, skey, FUNC); \ if (mask) { \ SCAN_PUT_ATTR(mask, ATTR, smask, FUNC); \ } \ } #define SCAN_END(ATTR) \ SCAN_FINISH(); \ SCAN_PUT(ATTR, NULL); \ return s - start; \ } #define SCAN_END_SINGLE(ATTR) \ SCAN_FINISH_SINGLE(); \ SCAN_PUT(ATTR, NULL); \ return s - start; \ } #define SCAN_SINGLE(NAME, TYPE, SCAN_AS, ATTR) \ SCAN_BEGIN(NAME, TYPE) { \ SCAN_TYPE(SCAN_AS, &skey, &smask); \ } SCAN_END_SINGLE(ATTR) #define SCAN_SINGLE_FULLY_MASKED(NAME, TYPE, SCAN_AS, ATTR) \ SCAN_BEGIN_FULLY_MASKED(NAME, TYPE) { \ SCAN_TYPE(SCAN_AS, &skey, NULL); \ } SCAN_END_SINGLE(ATTR) /* scan_port needs one extra argument. */ #define SCAN_SINGLE_PORT(NAME, TYPE, ATTR) \ SCAN_BEGIN(NAME, TYPE) { \ len = scan_port(s, &skey, &smask, port_names); \ if (len == 0) { \ return -EINVAL; \ } \ s += len; \ } SCAN_END_SINGLE(ATTR) static int parse_odp_key_mask_attr(const char *s, const struct simap *port_names, struct ofpbuf *key, struct ofpbuf *mask) { ovs_u128 ufid; int len; /* Skip UFID. */ len = odp_ufid_from_string(s, &ufid); if (len) { return len; } SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY); SCAN_SINGLE("skb_mark(", uint32_t, u32, OVS_KEY_ATTR_SKB_MARK); SCAN_SINGLE_FULLY_MASKED("recirc_id(", uint32_t, u32, OVS_KEY_ATTR_RECIRC_ID); SCAN_SINGLE("dp_hash(", uint32_t, u32, OVS_KEY_ATTR_DP_HASH); SCAN_SINGLE("ct_state(", uint32_t, ct_state, OVS_KEY_ATTR_CT_STATE); SCAN_SINGLE("ct_zone(", uint16_t, u16, OVS_KEY_ATTR_CT_ZONE); SCAN_SINGLE("ct_mark(", uint32_t, u32, OVS_KEY_ATTR_CT_MARK); SCAN_SINGLE("ct_label(", ovs_u128, u128, OVS_KEY_ATTR_CT_LABELS); SCAN_BEGIN_NESTED("tunnel(", OVS_KEY_ATTR_TUNNEL) { SCAN_FIELD_NESTED("tun_id=", ovs_be64, be64, OVS_TUNNEL_KEY_ATTR_ID); SCAN_FIELD_NESTED("src=", ovs_be32, ipv4, OVS_TUNNEL_KEY_ATTR_IPV4_SRC); SCAN_FIELD_NESTED("dst=", ovs_be32, ipv4, OVS_TUNNEL_KEY_ATTR_IPV4_DST); SCAN_FIELD_NESTED("ipv6_src=", struct in6_addr, in6_addr, OVS_TUNNEL_KEY_ATTR_IPV6_SRC); SCAN_FIELD_NESTED("ipv6_dst=", struct in6_addr, in6_addr, OVS_TUNNEL_KEY_ATTR_IPV6_DST); SCAN_FIELD_NESTED("tos=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TOS); SCAN_FIELD_NESTED("ttl=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TTL); SCAN_FIELD_NESTED("tp_src=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_SRC); SCAN_FIELD_NESTED("tp_dst=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_DST); SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr); SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve, geneve_to_attr); SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr); } SCAN_END_NESTED(); SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT); SCAN_BEGIN("eth(", struct ovs_key_ethernet) { SCAN_FIELD("src=", eth, eth_src); SCAN_FIELD("dst=", eth, eth_dst); } SCAN_END(OVS_KEY_ATTR_ETHERNET); SCAN_BEGIN_INIT("vlan(", struct ovs_key_vlan__, { htons(VLAN_CFI) }, { htons(VLAN_CFI) }) { SCAN_FIELD("vid=", vid, tci); SCAN_FIELD("pcp=", pcp, tci); SCAN_FIELD("cfi=", cfi, tci); } SCAN_END(OVS_KEY_ATTR_VLAN); SCAN_SINGLE("eth_type(", ovs_be16, be16, OVS_KEY_ATTR_ETHERTYPE); SCAN_BEGIN("mpls(", struct ovs_key_mpls) { SCAN_FIELD("label=", mpls_label, mpls_lse); SCAN_FIELD("tc=", mpls_tc, mpls_lse); SCAN_FIELD("ttl=", mpls_ttl, mpls_lse); SCAN_FIELD("bos=", mpls_bos, mpls_lse); } SCAN_END(OVS_KEY_ATTR_MPLS); SCAN_BEGIN("ipv4(", struct ovs_key_ipv4) { SCAN_FIELD("src=", ipv4, ipv4_src); SCAN_FIELD("dst=", ipv4, ipv4_dst); SCAN_FIELD("proto=", u8, ipv4_proto); SCAN_FIELD("tos=", u8, ipv4_tos); SCAN_FIELD("ttl=", u8, ipv4_ttl); SCAN_FIELD("frag=", frag, ipv4_frag); } SCAN_END(OVS_KEY_ATTR_IPV4); SCAN_BEGIN("ipv6(", struct ovs_key_ipv6) { SCAN_FIELD("src=", ipv6, ipv6_src); SCAN_FIELD("dst=", ipv6, ipv6_dst); SCAN_FIELD("label=", ipv6_label, ipv6_label); SCAN_FIELD("proto=", u8, ipv6_proto); SCAN_FIELD("tclass=", u8, ipv6_tclass); SCAN_FIELD("hlimit=", u8, ipv6_hlimit); SCAN_FIELD("frag=", frag, ipv6_frag); } SCAN_END(OVS_KEY_ATTR_IPV6); SCAN_BEGIN("tcp(", struct ovs_key_tcp) { SCAN_FIELD("src=", be16, tcp_src); SCAN_FIELD("dst=", be16, tcp_dst); } SCAN_END(OVS_KEY_ATTR_TCP); SCAN_SINGLE("tcp_flags(", ovs_be16, tcp_flags, OVS_KEY_ATTR_TCP_FLAGS); SCAN_BEGIN("udp(", struct ovs_key_udp) { SCAN_FIELD("src=", be16, udp_src); SCAN_FIELD("dst=", be16, udp_dst); } SCAN_END(OVS_KEY_ATTR_UDP); SCAN_BEGIN("sctp(", struct ovs_key_sctp) { SCAN_FIELD("src=", be16, sctp_src); SCAN_FIELD("dst=", be16, sctp_dst); } SCAN_END(OVS_KEY_ATTR_SCTP); SCAN_BEGIN("icmp(", struct ovs_key_icmp) { SCAN_FIELD("type=", u8, icmp_type); SCAN_FIELD("code=", u8, icmp_code); } SCAN_END(OVS_KEY_ATTR_ICMP); SCAN_BEGIN("icmpv6(", struct ovs_key_icmpv6) { SCAN_FIELD("type=", u8, icmpv6_type); SCAN_FIELD("code=", u8, icmpv6_code); } SCAN_END(OVS_KEY_ATTR_ICMPV6); SCAN_BEGIN("arp(", struct ovs_key_arp) { SCAN_FIELD("sip=", ipv4, arp_sip); SCAN_FIELD("tip=", ipv4, arp_tip); SCAN_FIELD("op=", be16, arp_op); SCAN_FIELD("sha=", eth, arp_sha); SCAN_FIELD("tha=", eth, arp_tha); } SCAN_END(OVS_KEY_ATTR_ARP); SCAN_BEGIN("nd(", struct ovs_key_nd) { SCAN_FIELD("target=", ipv6, nd_target); SCAN_FIELD("sll=", eth, nd_sll); SCAN_FIELD("tll=", eth, nd_tll); } SCAN_END(OVS_KEY_ATTR_ND); /* Encap open-coded. */ if (!strncmp(s, "encap(", 6)) { const char *start = s; size_t encap, encap_mask = 0; encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP); if (mask) { encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP); } s += 6; for (;;) { int retval; s += strspn(s, delimiters); if (!*s) { return -EINVAL; } else if (*s == ')') { break; } retval = parse_odp_key_mask_attr(s, port_names, key, mask); if (retval < 0) { return retval; } if (nl_attr_oversized(key->size - encap - NLA_HDRLEN)) { return -E2BIG; } s += retval; } s++; nl_msg_end_nested(key, encap); if (mask) { nl_msg_end_nested(mask, encap_mask); } return s - start; } return -EINVAL; } /* Parses the string representation of a datapath flow key, in the * format output by odp_flow_key_format(). Returns 0 if successful, * otherwise a positive errno value. On success, the flow key is * appended to 'key' as a series of Netlink attributes. On failure, no * data is appended to 'key'. Either way, 'key''s data might be * reallocated. * * If 'port_names' is nonnull, it points to an simap that maps from a port name * to a port number. (Port names may be used instead of port numbers in * in_port.) * * On success, the attributes appended to 'key' are individually syntactically * valid, but they may not be valid as a sequence. 'key' might, for example, * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */ int odp_flow_from_string(const char *s, const struct simap *port_names, struct ofpbuf *key, struct ofpbuf *mask) { const size_t old_size = key->size; for (;;) { int retval; s += strspn(s, delimiters); if (!*s) { return 0; } retval = parse_odp_key_mask_attr(s, port_names, key, mask); if (retval < 0) { key->size = old_size; return -retval; } s += retval; } return 0; } static uint8_t ovs_to_odp_frag(uint8_t nw_frag, bool is_mask) { if (is_mask) { /* Netlink interface 'enum ovs_frag_type' is an 8-bit enumeration type, * not a set of flags or bitfields. Hence, if the struct flow nw_frag * mask, which is a set of bits, has the FLOW_NW_FRAG_ANY as zero, we * must use a zero mask for the netlink frag field, and all ones mask * otherwise. */ return (nw_frag & FLOW_NW_FRAG_ANY) ? UINT8_MAX : 0; } return !(nw_frag & FLOW_NW_FRAG_ANY) ? OVS_FRAG_TYPE_NONE : nw_frag & FLOW_NW_FRAG_LATER ? OVS_FRAG_TYPE_LATER : OVS_FRAG_TYPE_FIRST; } static void get_ethernet_key(const struct flow *, struct ovs_key_ethernet *); static void put_ethernet_key(const struct ovs_key_ethernet *, struct flow *); static void get_ipv4_key(const struct flow *, struct ovs_key_ipv4 *, bool is_mask); static void put_ipv4_key(const struct ovs_key_ipv4 *, struct flow *, bool is_mask); static void get_ipv6_key(const struct flow *, struct ovs_key_ipv6 *, bool is_mask); static void put_ipv6_key(const struct ovs_key_ipv6 *, struct flow *, bool is_mask); static void get_arp_key(const struct flow *, struct ovs_key_arp *); static void put_arp_key(const struct ovs_key_arp *, struct flow *); static void get_nd_key(const struct flow *, struct ovs_key_nd *); static void put_nd_key(const struct ovs_key_nd *, struct flow *); /* These share the same layout. */ union ovs_key_tp { struct ovs_key_tcp tcp; struct ovs_key_udp udp; struct ovs_key_sctp sctp; }; static void get_tp_key(const struct flow *, union ovs_key_tp *); static void put_tp_key(const union ovs_key_tp *, struct flow *); static void odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms, bool export_mask, struct ofpbuf *buf) { struct ovs_key_ethernet *eth_key; size_t encap; const struct flow *flow = parms->flow; const struct flow *data = export_mask ? parms->mask : parms->flow; nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority); if (flow_tnl_dst_is_set(&flow->tunnel) || export_mask) { tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel, parms->key_buf); } nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark); if (parms->support.ct_state) { nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_STATE, ovs_to_odp_ct_state(data->ct_state)); } if (parms->support.ct_zone) { nl_msg_put_u16(buf, OVS_KEY_ATTR_CT_ZONE, data->ct_zone); } if (parms->support.ct_mark) { nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, data->ct_mark); } if (parms->support.ct_label) { nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &data->ct_label, sizeof(data->ct_label)); } if (parms->support.recirc) { nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id); nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash); } /* Add an ingress port attribute if this is a mask or 'odp_in_port' * is not the magical value "ODPP_NONE". */ if (export_mask || parms->odp_in_port != ODPP_NONE) { nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_port); } eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, sizeof *eth_key); get_ethernet_key(data, eth_key); if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) { if (export_mask) { nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); } else { nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN)); } nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); if (flow->vlan_tci == htons(0)) { goto unencap; } } else { encap = 0; } if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { /* For backwards compatibility with kernels that don't support * wildcarding, the following convention is used to encode the * OVS_KEY_ATTR_ETHERTYPE for key and mask: * * key mask matches * -------- -------- ------- * >0x5ff 0xffff Specified Ethernet II Ethertype. * >0x5ff 0 Any Ethernet II or non-Ethernet II frame. * 0xffff Any non-Ethernet II frame (except valid * 802.3 SNAP packet with valid eth_type). */ if (export_mask) { nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); } goto unencap; } nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type); if (flow->dl_type == htons(ETH_TYPE_IP)) { struct ovs_key_ipv4 *ipv4_key; ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4, sizeof *ipv4_key); get_ipv4_key(data, ipv4_key, export_mask); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { struct ovs_key_ipv6 *ipv6_key; ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6, sizeof *ipv6_key); get_ipv6_key(data, ipv6_key, export_mask); } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { struct ovs_key_arp *arp_key; arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP, sizeof *arp_key); get_arp_key(data, arp_key); } else if (eth_type_mpls(flow->dl_type)) { struct ovs_key_mpls *mpls_key; int i, n; n = flow_count_mpls_labels(flow, NULL); if (export_mask) { n = MIN(n, parms->support.max_mpls_depth); } mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS, n * sizeof *mpls_key); for (i = 0; i < n; i++) { mpls_key[i].mpls_lse = data->mpls_lse[i]; } } if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (flow->nw_proto == IPPROTO_TCP) { union ovs_key_tp *tcp_key; tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP, sizeof *tcp_key); get_tp_key(data, tcp_key); if (data->tcp_flags) { nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags); } } else if (flow->nw_proto == IPPROTO_UDP) { union ovs_key_tp *udp_key; udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP, sizeof *udp_key); get_tp_key(data, udp_key); } else if (flow->nw_proto == IPPROTO_SCTP) { union ovs_key_tp *sctp_key; sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP, sizeof *sctp_key); get_tp_key(data, sctp_key); } else if (flow->dl_type == htons(ETH_TYPE_IP) && flow->nw_proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP, sizeof *icmp_key); icmp_key->icmp_type = ntohs(data->tp_src); icmp_key->icmp_code = ntohs(data->tp_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6) && flow->nw_proto == IPPROTO_ICMPV6) { struct ovs_key_icmpv6 *icmpv6_key; icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6, sizeof *icmpv6_key); icmpv6_key->icmpv6_type = ntohs(data->tp_src); icmpv6_key->icmpv6_code = ntohs(data->tp_dst); if (flow->tp_dst == htons(0) && (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) /* Even though 'tp_src' and 'tp_dst' are 16 bits wide, ICMP * type and code are 8 bits wide. Therefore, an exact match * looks like htons(0xff), not htons(0xffff). See * xlate_wc_finish() for details. */ && (!export_mask || (data->tp_src == htons(0xff) && data->tp_dst == htons(0xff)))) { struct ovs_key_nd *nd_key; nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND, sizeof *nd_key); memcpy(nd_key->nd_target, &data->nd_target, sizeof nd_key->nd_target); nd_key->nd_sll = data->arp_sha; nd_key->nd_tll = data->arp_tha; } } } unencap: if (encap) { nl_msg_end_nested(buf, encap); } } /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'. * * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be * capable of being expanded to allow for that much space. */ void odp_flow_key_from_flow(const struct odp_flow_key_parms *parms, struct ofpbuf *buf) { odp_flow_key_from_flow__(parms, false, buf); } /* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to * 'buf'. * * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be * capable of being expanded to allow for that much space. */ void odp_flow_key_from_mask(const struct odp_flow_key_parms *parms, struct ofpbuf *buf) { odp_flow_key_from_flow__(parms, true, buf); } /* Generate ODP flow key from the given packet metadata */ void odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md) { nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority); if (flow_tnl_dst_is_set(&md->tunnel)) { tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL); } nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark); if (md->ct_state) { nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_STATE, ovs_to_odp_ct_state(md->ct_state)); if (md->ct_zone) { nl_msg_put_u16(buf, OVS_KEY_ATTR_CT_ZONE, md->ct_zone); } if (md->ct_mark) { nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, md->ct_mark); } if (!ovs_u128_is_zero(&md->ct_label)) { nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &md->ct_label, sizeof(md->ct_label)); } } /* Add an ingress port attribute if 'odp_in_port' is not the magical * value "ODPP_NONE". */ if (md->in_port.odp_port != ODPP_NONE) { nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port); } } /* Generate packet metadata from the given ODP flow key. */ void odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, struct pkt_metadata *md) { const struct nlattr *nla; size_t left; uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY | 1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL | 1u << OVS_KEY_ATTR_IN_PORT | 1u << OVS_KEY_ATTR_CT_STATE | 1u << OVS_KEY_ATTR_CT_ZONE | 1u << OVS_KEY_ATTR_CT_MARK | 1u << OVS_KEY_ATTR_CT_LABELS; pkt_metadata_init(md, ODPP_NONE); NL_ATTR_FOR_EACH (nla, left, key, key_len) { uint16_t type = nl_attr_type(nla); size_t len = nl_attr_get_size(nla); int expected_len = odp_key_attr_len(ovs_flow_key_attr_lens, OVS_KEY_ATTR_MAX, type); if (len != expected_len && expected_len >= 0) { continue; } switch (type) { case OVS_KEY_ATTR_RECIRC_ID: md->recirc_id = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_RECIRC_ID); break; case OVS_KEY_ATTR_DP_HASH: md->dp_hash = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_DP_HASH); break; case OVS_KEY_ATTR_PRIORITY: md->skb_priority = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_PRIORITY); break; case OVS_KEY_ATTR_SKB_MARK: md->pkt_mark = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK); break; case OVS_KEY_ATTR_CT_STATE: md->ct_state = odp_to_ovs_ct_state(nl_attr_get_u32(nla)); wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_STATE); break; case OVS_KEY_ATTR_CT_ZONE: md->ct_zone = nl_attr_get_u16(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_ZONE); break; case OVS_KEY_ATTR_CT_MARK: md->ct_mark = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_MARK); break; case OVS_KEY_ATTR_CT_LABELS: { const ovs_u128 *cl = nl_attr_get(nla); md->ct_label = *cl; wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_LABELS); break; } case OVS_KEY_ATTR_TUNNEL: { enum odp_key_fitness res; res = odp_tun_key_from_attr(nla, true, &md->tunnel); if (res == ODP_FIT_ERROR) { memset(&md->tunnel, 0, sizeof md->tunnel); } else if (res == ODP_FIT_PERFECT) { wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL); } break; } case OVS_KEY_ATTR_IN_PORT: md->in_port.odp_port = nl_attr_get_odp_port(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT); break; default: break; } if (!wanted_attrs) { return; /* Have everything. */ } } } uint32_t odp_flow_key_hash(const struct nlattr *key, size_t key_len) { BUILD_ASSERT_DECL(!(NLA_ALIGNTO % sizeof(uint32_t))); return hash_words(ALIGNED_CAST(const uint32_t *, key), key_len / sizeof(uint32_t), 0); } static void log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title, uint64_t attrs, int out_of_range_attr, const struct nlattr *key, size_t key_len) { struct ds s; int i; if (VLOG_DROP_DBG(rl)) { return; } ds_init(&s); for (i = 0; i < 64; i++) { if (attrs & (UINT64_C(1) << i)) { char namebuf[OVS_KEY_ATTR_BUFSIZE]; ds_put_format(&s, " %s", ovs_key_attr_to_string(i, namebuf, sizeof namebuf)); } } if (out_of_range_attr) { ds_put_format(&s, " %d (and possibly others)", out_of_range_attr); } ds_put_cstr(&s, ": "); odp_flow_key_format(key, key_len, &s); VLOG_DBG("%s:%s", title, ds_cstr(&s)); ds_destroy(&s); } static uint8_t odp_to_ovs_frag(uint8_t odp_frag, bool is_mask) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (is_mask) { return odp_frag ? FLOW_NW_FRAG_MASK : 0; } if (odp_frag > OVS_FRAG_TYPE_LATER) { VLOG_ERR_RL(&rl, "invalid frag %"PRIu8" in flow key", odp_frag); return 0xff; /* Error. */ } return (odp_frag == OVS_FRAG_TYPE_NONE) ? 0 : (odp_frag == OVS_FRAG_TYPE_FIRST) ? FLOW_NW_FRAG_ANY : FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER; } static bool parse_flow_nlattrs(const struct nlattr *key, size_t key_len, const struct nlattr *attrs[], uint64_t *present_attrsp, int *out_of_range_attrp) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); const struct nlattr *nla; uint64_t present_attrs; size_t left; BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs); present_attrs = 0; *out_of_range_attrp = 0; NL_ATTR_FOR_EACH (nla, left, key, key_len) { uint16_t type = nl_attr_type(nla); size_t len = nl_attr_get_size(nla); int expected_len = odp_key_attr_len(ovs_flow_key_attr_lens, OVS_KEY_ATTR_MAX, type); if (len != expected_len && expected_len >= 0) { char namebuf[OVS_KEY_ATTR_BUFSIZE]; VLOG_ERR_RL(&rl, "attribute %s has length %"PRIuSIZE" but should have " "length %d", ovs_key_attr_to_string(type, namebuf, sizeof namebuf), len, expected_len); return false; } if (type > OVS_KEY_ATTR_MAX) { *out_of_range_attrp = type; } else { if (present_attrs & (UINT64_C(1) << type)) { char namebuf[OVS_KEY_ATTR_BUFSIZE]; VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key", ovs_key_attr_to_string(type, namebuf, sizeof namebuf)); return false; } present_attrs |= UINT64_C(1) << type; attrs[type] = nla; } } if (left) { VLOG_ERR_RL(&rl, "trailing garbage in flow key"); return false; } *present_attrsp = present_attrs; return true; } static enum odp_key_fitness check_expectations(uint64_t present_attrs, int out_of_range_attr, uint64_t expected_attrs, const struct nlattr *key, size_t key_len) { uint64_t missing_attrs; uint64_t extra_attrs; missing_attrs = expected_attrs & ~present_attrs; if (missing_attrs) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); log_odp_key_attributes(&rl, "expected but not present", missing_attrs, 0, key, key_len); return ODP_FIT_TOO_LITTLE; } extra_attrs = present_attrs & ~expected_attrs; if (extra_attrs || out_of_range_attr) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10); log_odp_key_attributes(&rl, "present but not expected", extra_attrs, out_of_range_attr, key, key_len); return ODP_FIT_TOO_MUCH; } return ODP_FIT_PERFECT; } static bool parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], uint64_t present_attrs, uint64_t *expected_attrs, struct flow *flow, const struct flow *src_flow) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); bool is_mask = flow != src_flow; if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) { flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]); if (!is_mask && ntohs(flow->dl_type) < ETH_TYPE_MIN) { VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key", ntohs(flow->dl_type)); return false; } if (is_mask && ntohs(src_flow->dl_type) < ETH_TYPE_MIN && flow->dl_type != htons(0xffff)) { return false; } *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE; } else { if (!is_mask) { flow->dl_type = htons(FLOW_DL_TYPE_NONE); } else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) { /* See comments in odp_flow_key_from_flow__(). */ VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame"); return false; } } return true; } static enum odp_key_fitness parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], uint64_t present_attrs, int out_of_range_attr, uint64_t expected_attrs, struct flow *flow, const struct nlattr *key, size_t key_len, const struct flow *src_flow) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); bool is_mask = src_flow != flow; const void *check_start = NULL; size_t check_len = 0; enum ovs_key_attr expected_bit = 0xff; if (eth_type_mpls(src_flow->dl_type)) { if (!is_mask || present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) { expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS); } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) { size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]); const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]); int n = size / sizeof(ovs_be32); int i; if (!size || size % sizeof(ovs_be32)) { return ODP_FIT_ERROR; } if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) { return ODP_FIT_ERROR; } for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) { flow->mpls_lse[i] = mpls_lse[i]; } if (n > FLOW_MAX_MPLS_LABELS) { return ODP_FIT_TOO_MUCH; } if (!is_mask) { /* BOS may be set only in the innermost label. */ for (i = 0; i < n - 1; i++) { if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) { return ODP_FIT_ERROR; } } /* BOS must be set in the innermost label. */ if (n < FLOW_MAX_MPLS_LABELS && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) { return ODP_FIT_TOO_LITTLE; } } } goto done; } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]); put_ipv4_key(ipv4_key, flow, is_mask); if (flow->nw_frag > FLOW_NW_FRAG_MASK) { return ODP_FIT_ERROR; } if (is_mask) { check_start = ipv4_key; check_len = sizeof *ipv4_key; expected_bit = OVS_KEY_ATTR_IPV4; } } } else if (src_flow->dl_type == htons(ETH_TYPE_IPV6)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) { const struct ovs_key_ipv6 *ipv6_key; ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]); put_ipv6_key(ipv6_key, flow, is_mask); if (flow->nw_frag > FLOW_NW_FRAG_MASK) { return ODP_FIT_ERROR; } if (is_mask) { check_start = ipv6_key; check_len = sizeof *ipv6_key; expected_bit = OVS_KEY_ATTR_IPV6; } } } else if (src_flow->dl_type == htons(ETH_TYPE_ARP) || src_flow->dl_type == htons(ETH_TYPE_RARP)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]); if (!is_mask && (arp_key->arp_op & htons(0xff00))) { VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow " "key", ntohs(arp_key->arp_op)); return ODP_FIT_ERROR; } put_arp_key(arp_key, flow); if (is_mask) { check_start = arp_key; check_len = sizeof *arp_key; expected_bit = OVS_KEY_ATTR_ARP; } } } else { goto done; } if (check_len > 0) { /* Happens only when 'is_mask'. */ if (!is_all_zeros(check_start, check_len) && flow->dl_type != htons(0xffff)) { return ODP_FIT_ERROR; } else { expected_attrs |= UINT64_C(1) << expected_bit; } } expected_bit = OVS_KEY_ATTR_UNSPEC; if (src_flow->nw_proto == IPPROTO_TCP && (src_flow->dl_type == htons(ETH_TYPE_IP) || src_flow->dl_type == htons(ETH_TYPE_IPV6)) && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) { const union ovs_key_tp *tcp_key; tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]); put_tp_key(tcp_key, flow); expected_bit = OVS_KEY_ATTR_TCP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS; flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]); } } else if (src_flow->nw_proto == IPPROTO_UDP && (src_flow->dl_type == htons(ETH_TYPE_IP) || src_flow->dl_type == htons(ETH_TYPE_IPV6)) && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) { const union ovs_key_tp *udp_key; udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]); put_tp_key(udp_key, flow); expected_bit = OVS_KEY_ATTR_UDP; } } else if (src_flow->nw_proto == IPPROTO_SCTP && (src_flow->dl_type == htons(ETH_TYPE_IP) || src_flow->dl_type == htons(ETH_TYPE_IPV6)) && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) { const union ovs_key_tp *sctp_key; sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]); put_tp_key(sctp_key, flow); expected_bit = OVS_KEY_ATTR_SCTP; } } else if (src_flow->nw_proto == IPPROTO_ICMP && src_flow->dl_type == htons(ETH_TYPE_IP) && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) { const struct ovs_key_icmp *icmp_key; icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]); flow->tp_src = htons(icmp_key->icmp_type); flow->tp_dst = htons(icmp_key->icmp_code); expected_bit = OVS_KEY_ATTR_ICMP; } } else if (src_flow->nw_proto == IPPROTO_ICMPV6 && src_flow->dl_type == htons(ETH_TYPE_IPV6) && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) { const struct ovs_key_icmpv6 *icmpv6_key; icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]); flow->tp_src = htons(icmpv6_key->icmpv6_type); flow->tp_dst = htons(icmpv6_key->icmpv6_code); expected_bit = OVS_KEY_ATTR_ICMPV6; if (src_flow->tp_dst == htons(0) && (src_flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || src_flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) { if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) { const struct ovs_key_nd *nd_key; nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]); memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target); flow->arp_sha = nd_key->nd_sll; flow->arp_tha = nd_key->nd_tll; if (is_mask) { /* Even though 'tp_src' and 'tp_dst' are 16 bits wide, * ICMP type and code are 8 bits wide. Therefore, an * exact match looks like htons(0xff), not * htons(0xffff). See xlate_wc_finish() for details. * */ if (!is_all_zeros(nd_key, sizeof *nd_key) && (flow->tp_src != htons(0xff) || flow->tp_dst != htons(0xff))) { return ODP_FIT_ERROR; } else { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND; } } } } } } if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) { if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) { return ODP_FIT_ERROR; } else { expected_attrs |= UINT64_C(1) << expected_bit; } } done: return check_expectations(present_attrs, out_of_range_attr, expected_attrs, key, key_len); } /* Parse 802.1Q header then encapsulated L3 attributes. */ static enum odp_key_fitness parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], uint64_t present_attrs, int out_of_range_attr, uint64_t expected_attrs, struct flow *flow, const struct nlattr *key, size_t key_len, const struct flow *src_flow) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); bool is_mask = src_flow != flow; const struct nlattr *encap = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP) ? attrs[OVS_KEY_ATTR_ENCAP] : NULL); enum odp_key_fitness encap_fitness; enum odp_key_fitness fitness; /* Calculate fitness of outer attributes. */ if (!is_mask) { expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) | (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)); } else { if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) { expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN); } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) { expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP); } } fitness = check_expectations(present_attrs, out_of_range_attr, expected_attrs, key, key_len); /* Set vlan_tci. * Remove the TPID from dl_type since it's not the real Ethertype. */ flow->dl_type = htons(0); flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN) ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]) : htons(0)); if (!is_mask) { if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) { return ODP_FIT_TOO_LITTLE; } else if (flow->vlan_tci == htons(0)) { /* Corner case for a truncated 802.1Q header. */ if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) { return ODP_FIT_TOO_MUCH; } return fitness; } else if (!(flow->vlan_tci & htons(VLAN_CFI))) { VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero " "but CFI bit is not set", ntohs(flow->vlan_tci)); return ODP_FIT_ERROR; } } else { if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) { return fitness; } } /* Now parse the encapsulated attributes. */ if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap), attrs, &present_attrs, &out_of_range_attr)) { return ODP_FIT_ERROR; } expected_attrs = 0; if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { return ODP_FIT_ERROR; } encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr, expected_attrs, flow, key, key_len, src_flow); /* The overall fitness is the worse of the outer and inner attributes. */ return MAX(fitness, encap_fitness); } static enum odp_key_fitness odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, const struct nlattr *src_key, size_t src_key_len, struct flow *flow, const struct flow *src_flow, bool udpif) { const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1]; uint64_t expected_attrs; uint64_t present_attrs; int out_of_range_attr; bool is_mask = src_flow != flow; memset(flow, 0, sizeof *flow); /* Parse attributes. */ if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs, &out_of_range_attr)) { return ODP_FIT_ERROR; } expected_attrs = 0; /* Metadata. */ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) { flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID; } else if (is_mask) { /* Always exact match recirc_id if it is not specified. */ flow->recirc_id = UINT32_MAX; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_DP_HASH)) { flow->dp_hash = nl_attr_get_u32(attrs[OVS_KEY_ATTR_DP_HASH]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_DP_HASH; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PRIORITY)) { flow->skb_priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK)) { flow->pkt_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_SKB_MARK]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_STATE)) { uint32_t odp_state = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CT_STATE]); flow->ct_state = odp_to_ovs_ct_state(odp_state); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_STATE; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_ZONE)) { flow->ct_zone = nl_attr_get_u16(attrs[OVS_KEY_ATTR_CT_ZONE]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_ZONE; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_MARK)) { flow->ct_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CT_MARK]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_MARK; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_LABELS)) { const ovs_u128 *cl = nl_attr_get(attrs[OVS_KEY_ATTR_CT_LABELS]); flow->ct_label = *cl; expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_LABELS; } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) { enum odp_key_fitness res; res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], is_mask ? src_key : NULL, src_key_len, &src_flow->tunnel, &flow->tunnel, udpif); if (res == ODP_FIT_ERROR) { return ODP_FIT_ERROR; } else if (res == ODP_FIT_PERFECT) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL; } } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) { flow->in_port.odp_port = nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]); expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT; } else if (!is_mask) { flow->in_port.odp_port = ODPP_NONE; } /* Ethernet header. */ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) { const struct ovs_key_ethernet *eth_key; eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]); put_ethernet_key(eth_key, flow); if (is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; } } if (!is_mask) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; } /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { return ODP_FIT_ERROR; } if (is_mask ? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0 : src_flow->dl_type == htons(ETH_TYPE_VLAN)) { return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, expected_attrs, flow, key, key_len, src_flow); } if (is_mask) { /* A missing VLAN mask means exact match on vlan_tci 0 (== no VLAN). */ flow->vlan_tci = htons(0xffff); if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) { flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]); expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN); } } return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr, expected_attrs, flow, key, key_len, src_flow); } /* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow * structure in 'flow'. Returns an ODP_FIT_* value that indicates how well * 'key' fits our expectations for what a flow key should contain. * * The 'in_port' will be the datapath's understanding of the port. The * caller will need to translate with odp_port_to_ofp_port() if the * OpenFlow port is needed. * * This function doesn't take the packet itself as an argument because none of * the currently understood OVS_KEY_ATTR_* attributes require it. Currently, * it is always possible to infer which additional attribute(s) should appear * by looking at the attributes for lower-level protocols, e.g. if the network * protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we * know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it * must be absent. */ enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, struct flow *flow) { return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow, false); } static enum odp_key_fitness odp_flow_key_to_mask__(const struct nlattr *mask_key, size_t mask_key_len, const struct nlattr *flow_key, size_t flow_key_len, struct flow_wildcards *mask, const struct flow *src_flow, bool udpif) { if (mask_key_len) { return odp_flow_key_to_flow__(mask_key, mask_key_len, flow_key, flow_key_len, &mask->masks, src_flow, udpif); } else { /* A missing mask means that the flow should be exact matched. * Generate an appropriate exact wildcard for the flow. */ flow_wildcards_init_for_packet(mask, src_flow); return ODP_FIT_PERFECT; } } /* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key' * to a mask structure in 'mask'. 'flow' must be a previously translated flow * corresponding to 'mask' and similarly flow_key/flow_key_len must be the * attributes from that flow. Returns an ODP_FIT_* value that indicates how * well 'key' fits our expectations for what a flow key should contain. */ enum odp_key_fitness odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len, const struct nlattr *flow_key, size_t flow_key_len, struct flow_wildcards *mask, const struct flow *flow) { return odp_flow_key_to_mask__(mask_key, mask_key_len, flow_key, flow_key_len, mask, flow, false); } /* These functions are similar to their non-"_udpif" variants but output a * 'flow' that is suitable for fast-path packet processing. * * Some fields have different representation for flow setup and per- * packet processing (i.e. different between ofproto-dpif and userspace * datapath). In particular, with the non-"_udpif" functions, struct * tun_metadata is in the per-flow format (using 'present.map' and 'opts.u8'); * with these functions, struct tun_metadata is in the per-packet format * (using 'present.len' and 'opts.gnv'). */ enum odp_key_fitness odp_flow_key_to_flow_udpif(const struct nlattr *key, size_t key_len, struct flow *flow) { return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow, true); } enum odp_key_fitness odp_flow_key_to_mask_udpif(const struct nlattr *mask_key, size_t mask_key_len, const struct nlattr *flow_key, size_t flow_key_len, struct flow_wildcards *mask, const struct flow *flow) { return odp_flow_key_to_mask__(mask_key, mask_key_len, flow_key, flow_key_len, mask, flow, true); } /* Returns 'fitness' as a string, for use in debug messages. */ const char * odp_key_fitness_to_string(enum odp_key_fitness fitness) { switch (fitness) { case ODP_FIT_PERFECT: return "OK"; case ODP_FIT_TOO_MUCH: return "too_much"; case ODP_FIT_TOO_LITTLE: return "too_little"; case ODP_FIT_ERROR: return "error"; default: return ""; } } /* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies * Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute * whose contents are the 'userdata_size' bytes at 'userdata' and returns the * offset within 'odp_actions' of the start of the cookie. (If 'userdata' is * null, then the return value is not meaningful.) */ size_t odp_put_userspace_action(uint32_t pid, const void *userdata, size_t userdata_size, odp_port_t tunnel_out_port, bool include_actions, struct ofpbuf *odp_actions) { size_t userdata_ofs; size_t offset; offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE); nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid); if (userdata) { userdata_ofs = odp_actions->size + NLA_HDRLEN; /* The OVS kernel module before OVS 1.11 and the upstream Linux kernel * module before Linux 3.10 required the userdata to be exactly 8 bytes * long: * * - The kernel rejected shorter userdata with -ERANGE. * * - The kernel silently dropped userdata beyond the first 8 bytes. * * Thus, for maximum compatibility, always put at least 8 bytes. (We * separately disable features that required more than 8 bytes.) */ memcpy(nl_msg_put_unspec_zero(odp_actions, OVS_USERSPACE_ATTR_USERDATA, MAX(8, userdata_size)), userdata, userdata_size); } else { userdata_ofs = 0; } if (tunnel_out_port != ODPP_NONE) { nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT, tunnel_out_port); } if (include_actions) { nl_msg_put_flag(odp_actions, OVS_USERSPACE_ATTR_ACTIONS); } nl_msg_end_nested(odp_actions, offset); return userdata_ofs; } void odp_put_tunnel_action(const struct flow_tnl *tunnel, struct ofpbuf *odp_actions) { size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); tun_key_to_attr(odp_actions, tunnel, tunnel, NULL); nl_msg_end_nested(odp_actions, offset); } void odp_put_tnl_push_action(struct ofpbuf *odp_actions, struct ovs_action_push_tnl *data) { int size = offsetof(struct ovs_action_push_tnl, header); size += data->header_len; nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_PUSH, data, size); } /* The commit_odp_actions() function and its helpers. */ static void commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, const void *key, size_t key_size) { size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); nl_msg_put_unspec(odp_actions, key_type, key, key_size); nl_msg_end_nested(odp_actions, offset); } /* Masked set actions have a mask following the data within the netlink * attribute. The unmasked bits in the data will be cleared as the data * is copied to the action. */ void commit_masked_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, const void *key_, const void *mask_, size_t key_size) { size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET_MASKED); char *data = nl_msg_put_unspec_uninit(odp_actions, key_type, key_size * 2); const char *key = key_, *mask = mask_; memcpy(data + key_size, mask, key_size); /* Clear unmasked bits while copying. */ while (key_size--) { *data++ = *key++ & *mask++; } nl_msg_end_nested(odp_actions, offset); } /* If any of the flow key data that ODP actions can modify are different in * 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to * 'odp_actions' that change the flow tunneling information in key from * 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the * same way. In other words, operates the same as commit_odp_actions(), but * only on tunneling information. */ void commit_odp_tunnel_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) { /* A valid IPV4_TUNNEL must have non-zero ip_dst; a valid IPv6 tunnel * must have non-zero ipv6_dst. */ if (flow_tnl_dst_is_set(&flow->tunnel)) { if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) { return; } memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel); odp_put_tunnel_action(&base->tunnel, odp_actions); } } static bool commit(enum ovs_key_attr attr, bool use_masked_set, const void *key, void *base, void *mask, size_t size, struct ofpbuf *odp_actions) { if (memcmp(key, base, size)) { bool fully_masked = odp_mask_is_exact(attr, mask, size); if (use_masked_set && !fully_masked) { commit_masked_set_action(odp_actions, attr, key, mask, size); } else { if (!fully_masked) { memset(mask, 0xff, size); } commit_set_action(odp_actions, attr, key, size); } memcpy(base, key, size); return true; } else { /* Mask bits are set when we have either read or set the corresponding * values. Masked bits will be exact-matched, no need to set them * if the value did not actually change. */ return false; } } static void get_ethernet_key(const struct flow *flow, struct ovs_key_ethernet *eth) { eth->eth_src = flow->dl_src; eth->eth_dst = flow->dl_dst; } static void put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow) { flow->dl_src = eth->eth_src; flow->dl_dst = eth->eth_dst; } static void commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { struct ovs_key_ethernet key, base, mask; get_ethernet_key(flow, &key); get_ethernet_key(base_flow, &base); get_ethernet_key(&wc->masks, &mask); if (commit(OVS_KEY_ATTR_ETHERNET, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { put_ethernet_key(&base, base_flow); put_ethernet_key(&mask, &wc->masks); } } static void pop_vlan(struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); if (base->vlan_tci & htons(VLAN_CFI)) { nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); base->vlan_tci = 0; } } static void commit_vlan_action(ovs_be16 vlan_tci, struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { if (base->vlan_tci == vlan_tci) { return; } pop_vlan(base, odp_actions, wc); if (vlan_tci & htons(VLAN_CFI)) { struct ovs_action_push_vlan vlan; vlan.vlan_tpid = htons(ETH_TYPE_VLAN); vlan.vlan_tci = vlan_tci; nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, &vlan, sizeof vlan); } base->vlan_tci = vlan_tci; } /* Wildcarding already done at action translation time. */ static void commit_mpls_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions) { int base_n = flow_count_mpls_labels(base, NULL); int flow_n = flow_count_mpls_labels(flow, NULL); int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n, NULL); while (base_n > common_n) { if (base_n - 1 == common_n && flow_n > common_n) { /* If there is only one more LSE in base than there are common * between base and flow; and flow has at least one more LSE than * is common then the topmost LSE of base may be updated using * set */ struct ovs_key_mpls mpls_key; mpls_key.mpls_lse = flow->mpls_lse[flow_n - base_n]; commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS, &mpls_key, sizeof mpls_key); flow_set_mpls_lse(base, 0, mpls_key.mpls_lse); common_n++; } else { /* Otherwise, if there more LSEs in base than are common between * base and flow then pop the topmost one. */ ovs_be16 dl_type; bool popped; /* If all the LSEs are to be popped and this is not the outermost * LSE then use ETH_TYPE_MPLS as the ethertype parameter of the * POP_MPLS action instead of flow->dl_type. * * This is because the POP_MPLS action requires its ethertype * argument to be an MPLS ethernet type but in this case * flow->dl_type will be a non-MPLS ethernet type. * * When the final POP_MPLS action occurs it use flow->dl_type and * the and the resulting packet will have the desired dl_type. */ if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) { dl_type = htons(ETH_TYPE_MPLS); } else { dl_type = flow->dl_type; } nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type); popped = flow_pop_mpls(base, base_n, flow->dl_type, NULL); ovs_assert(popped); base_n--; } } /* If, after the above popping and setting, there are more LSEs in flow * than base then some LSEs need to be pushed. */ while (base_n < flow_n) { struct ovs_action_push_mpls *mpls; mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS, sizeof *mpls); mpls->mpls_ethertype = flow->dl_type; mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; /* Update base flow's MPLS stack, but do not clear L3. We need the L3 * headers if the flow is restored later due to returning from a patch * port or group bucket. */ flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL, false); flow_set_mpls_lse(base, 0, mpls->mpls_lse); base_n++; } } static void get_ipv4_key(const struct flow *flow, struct ovs_key_ipv4 *ipv4, bool is_mask) { ipv4->ipv4_src = flow->nw_src; ipv4->ipv4_dst = flow->nw_dst; ipv4->ipv4_proto = flow->nw_proto; ipv4->ipv4_tos = flow->nw_tos; ipv4->ipv4_ttl = flow->nw_ttl; ipv4->ipv4_frag = ovs_to_odp_frag(flow->nw_frag, is_mask); } static void put_ipv4_key(const struct ovs_key_ipv4 *ipv4, struct flow *flow, bool is_mask) { flow->nw_src = ipv4->ipv4_src; flow->nw_dst = ipv4->ipv4_dst; flow->nw_proto = ipv4->ipv4_proto; flow->nw_tos = ipv4->ipv4_tos; flow->nw_ttl = ipv4->ipv4_ttl; flow->nw_frag = odp_to_ovs_frag(ipv4->ipv4_frag, is_mask); } static void commit_set_ipv4_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { struct ovs_key_ipv4 key, mask, base; /* Check that nw_proto and nw_frag remain unchanged. */ ovs_assert(flow->nw_proto == base_flow->nw_proto && flow->nw_frag == base_flow->nw_frag); get_ipv4_key(flow, &key, false); get_ipv4_key(base_flow, &base, false); get_ipv4_key(&wc->masks, &mask, true); mask.ipv4_proto = 0; /* Not writeable. */ mask.ipv4_frag = 0; /* Not writable. */ if (commit(OVS_KEY_ATTR_IPV4, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { put_ipv4_key(&base, base_flow, false); if (mask.ipv4_proto != 0) { /* Mask was changed by commit(). */ put_ipv4_key(&mask, &wc->masks, true); } } } static void get_ipv6_key(const struct flow *flow, struct ovs_key_ipv6 *ipv6, bool is_mask) { memcpy(ipv6->ipv6_src, &flow->ipv6_src, sizeof ipv6->ipv6_src); memcpy(ipv6->ipv6_dst, &flow->ipv6_dst, sizeof ipv6->ipv6_dst); ipv6->ipv6_label = flow->ipv6_label; ipv6->ipv6_proto = flow->nw_proto; ipv6->ipv6_tclass = flow->nw_tos; ipv6->ipv6_hlimit = flow->nw_ttl; ipv6->ipv6_frag = ovs_to_odp_frag(flow->nw_frag, is_mask); } static void put_ipv6_key(const struct ovs_key_ipv6 *ipv6, struct flow *flow, bool is_mask) { memcpy(&flow->ipv6_src, ipv6->ipv6_src, sizeof flow->ipv6_src); memcpy(&flow->ipv6_dst, ipv6->ipv6_dst, sizeof flow->ipv6_dst); flow->ipv6_label = ipv6->ipv6_label; flow->nw_proto = ipv6->ipv6_proto; flow->nw_tos = ipv6->ipv6_tclass; flow->nw_ttl = ipv6->ipv6_hlimit; flow->nw_frag = odp_to_ovs_frag(ipv6->ipv6_frag, is_mask); } static void commit_set_ipv6_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { struct ovs_key_ipv6 key, mask, base; /* Check that nw_proto and nw_frag remain unchanged. */ ovs_assert(flow->nw_proto == base_flow->nw_proto && flow->nw_frag == base_flow->nw_frag); get_ipv6_key(flow, &key, false); get_ipv6_key(base_flow, &base, false); get_ipv6_key(&wc->masks, &mask, true); mask.ipv6_proto = 0; /* Not writeable. */ mask.ipv6_frag = 0; /* Not writable. */ mask.ipv6_label &= htonl(IPV6_LABEL_MASK); /* Not writable. */ if (commit(OVS_KEY_ATTR_IPV6, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { put_ipv6_key(&base, base_flow, false); if (mask.ipv6_proto != 0) { /* Mask was changed by commit(). */ put_ipv6_key(&mask, &wc->masks, true); } } } static void get_arp_key(const struct flow *flow, struct ovs_key_arp *arp) { /* ARP key has padding, clear it. */ memset(arp, 0, sizeof *arp); arp->arp_sip = flow->nw_src; arp->arp_tip = flow->nw_dst; arp->arp_op = htons(flow->nw_proto); arp->arp_sha = flow->arp_sha; arp->arp_tha = flow->arp_tha; } static void put_arp_key(const struct ovs_key_arp *arp, struct flow *flow) { flow->nw_src = arp->arp_sip; flow->nw_dst = arp->arp_tip; flow->nw_proto = ntohs(arp->arp_op); flow->arp_sha = arp->arp_sha; flow->arp_tha = arp->arp_tha; } static enum slow_path_reason commit_set_arp_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { struct ovs_key_arp key, mask, base; get_arp_key(flow, &key); get_arp_key(base_flow, &base); get_arp_key(&wc->masks, &mask); if (commit(OVS_KEY_ATTR_ARP, true, &key, &base, &mask, sizeof key, odp_actions)) { put_arp_key(&base, base_flow); put_arp_key(&mask, &wc->masks); return SLOW_ACTION; } return 0; } static void get_icmp_key(const struct flow *flow, struct ovs_key_icmp *icmp) { /* icmp_type and icmp_code are stored in tp_src and tp_dst, respectively */ icmp->icmp_type = ntohs(flow->tp_src); icmp->icmp_code = ntohs(flow->tp_dst); } static void put_icmp_key(const struct ovs_key_icmp *icmp, struct flow *flow) { /* icmp_type and icmp_code are stored in tp_src and tp_dst, respectively */ flow->tp_src = htons(icmp->icmp_type); flow->tp_dst = htons(icmp->icmp_code); } static enum slow_path_reason commit_set_icmp_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { struct ovs_key_icmp key, mask, base; enum ovs_key_attr attr; if (is_icmpv4(flow, NULL)) { attr = OVS_KEY_ATTR_ICMP; } else if (is_icmpv6(flow, NULL)) { attr = OVS_KEY_ATTR_ICMPV6; } else { return 0; } get_icmp_key(flow, &key); get_icmp_key(base_flow, &base); get_icmp_key(&wc->masks, &mask); if (commit(attr, false, &key, &base, &mask, sizeof key, odp_actions)) { put_icmp_key(&base, base_flow); put_icmp_key(&mask, &wc->masks); return SLOW_ACTION; } return 0; } static void get_nd_key(const struct flow *flow, struct ovs_key_nd *nd) { memcpy(nd->nd_target, &flow->nd_target, sizeof flow->nd_target); /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */ nd->nd_sll = flow->arp_sha; nd->nd_tll = flow->arp_tha; } static void put_nd_key(const struct ovs_key_nd *nd, struct flow *flow) { memcpy(&flow->nd_target, nd->nd_target, sizeof flow->nd_target); /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */ flow->arp_sha = nd->nd_sll; flow->arp_tha = nd->nd_tll; } static enum slow_path_reason commit_set_nd_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { struct ovs_key_nd key, mask, base; get_nd_key(flow, &key); get_nd_key(base_flow, &base); get_nd_key(&wc->masks, &mask); if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { put_nd_key(&base, base_flow); put_nd_key(&mask, &wc->masks); return SLOW_ACTION; } return 0; } static enum slow_path_reason commit_set_nw_action(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { /* Check if 'flow' really has an L3 header. */ if (!flow->nw_proto) { return 0; } switch (ntohs(base->dl_type)) { case ETH_TYPE_IP: commit_set_ipv4_action(flow, base, odp_actions, wc, use_masked); break; case ETH_TYPE_IPV6: commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked); return commit_set_nd_action(flow, base, odp_actions, wc, use_masked); case ETH_TYPE_ARP: return commit_set_arp_action(flow, base, odp_actions, wc); } return 0; } /* TCP, UDP, and SCTP keys have the same layout. */ BUILD_ASSERT_DECL(sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_udp) && sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_sctp)); static void get_tp_key(const struct flow *flow, union ovs_key_tp *tp) { tp->tcp.tcp_src = flow->tp_src; tp->tcp.tcp_dst = flow->tp_dst; } static void put_tp_key(const union ovs_key_tp *tp, struct flow *flow) { flow->tp_src = tp->tcp.tcp_src; flow->tp_dst = tp->tcp.tcp_dst; } static void commit_set_port_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { enum ovs_key_attr key_type; union ovs_key_tp key, mask, base; /* Check if 'flow' really has an L3 header. */ if (!flow->nw_proto) { return; } if (!is_ip_any(base_flow)) { return; } if (flow->nw_proto == IPPROTO_TCP) { key_type = OVS_KEY_ATTR_TCP; } else if (flow->nw_proto == IPPROTO_UDP) { key_type = OVS_KEY_ATTR_UDP; } else if (flow->nw_proto == IPPROTO_SCTP) { key_type = OVS_KEY_ATTR_SCTP; } else { return; } get_tp_key(flow, &key); get_tp_key(base_flow, &base); get_tp_key(&wc->masks, &mask); if (commit(key_type, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { put_tp_key(&base, base_flow); put_tp_key(&mask, &wc->masks); } } static void commit_set_priority_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { uint32_t key, mask, base; key = flow->skb_priority; base = base_flow->skb_priority; mask = wc->masks.skb_priority; if (commit(OVS_KEY_ATTR_PRIORITY, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { base_flow->skb_priority = base; wc->masks.skb_priority = mask; } } static void commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { uint32_t key, mask, base; key = flow->pkt_mark; base = base_flow->pkt_mark; mask = wc->masks.pkt_mark; if (commit(OVS_KEY_ATTR_SKB_MARK, use_masked, &key, &base, &mask, sizeof key, odp_actions)) { base_flow->pkt_mark = base; wc->masks.pkt_mark = mask; } } /* If any of the flow key data that ODP actions can modify are different in * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow * key from 'base' into 'flow', and then changes 'base' the same way. Does not * commit set_tunnel actions. Users should call commit_odp_tunnel_action() * in addition to this function if needed. Sets fields in 'wc' that are * used as part of the action. * * Returns a reason to force processing the flow's packets into the userspace * slow path, if there is one, otherwise 0. */ enum slow_path_reason commit_odp_actions(const struct flow *flow, struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc, bool use_masked) { enum slow_path_reason slow1, slow2; bool mpls_done = false; commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked); /* Make packet a non-MPLS packet before committing L3/4 actions, * which would otherwise do nothing. */ if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) { commit_mpls_action(flow, base, odp_actions); mpls_done = true; } slow1 = commit_set_nw_action(flow, base, odp_actions, wc, use_masked); commit_set_port_action(flow, base, odp_actions, wc, use_masked); slow2 = commit_set_icmp_action(flow, base, odp_actions, wc); if (!mpls_done) { commit_mpls_action(flow, base, odp_actions); } commit_vlan_action(flow->vlan_tci, base, odp_actions, wc); commit_set_priority_action(flow, base, odp_actions, wc, use_masked); commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked); return slow1 ? slow1 : slow2; } openvswitch-2.5.9/lib/PaxHeaders.82075/connectivity.h0000644000000000000000000000013213534540071017301 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.681852689 openvswitch-2.5.9/lib/connectivity.h0000644000175000017500000000142713534540071020773 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CONNECTIVITY_H #define CONNECTIVITY_H 1 #include /* For tracking connectivity changes globally. */ struct seq *connectivity_seq_get(void); #endif /* connectivity.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dirs.h0000644000000000000000000000013213534540071015524 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.701852836 openvswitch-2.5.9/lib/dirs.h0000644000175000017500000000205413534540071017213 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DIRS_H #define DIRS_H 1 const char *ovs_sysconfdir(void); /* /usr/local/etc */ const char *ovs_pkgdatadir(void); /* /usr/local/share/openvswitch */ const char *ovs_rundir(void); /* /usr/local/var/run/openvswitch */ const char *ovs_logdir(void); /* /usr/local/var/log/openvswitch */ const char *ovs_dbdir(void); /* /usr/local/etc/openvswitch */ const char *ovs_bindir(void); /* /usr/local/bin */ #endif /* dirs.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/table.man0000644000000000000000000000013213534540071016176 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801423.741845759 openvswitch-2.5.9/lib/table.man0000644000175000017500000000510613534540071017666 0ustar00jpettitjpettit00000000000000.IP "\fB\-f \fIformat\fR" .IQ "\fB\-\-format=\fIformat\fR" Sets the type of table formatting. The following types of \fIformat\fR are available: .RS .ie '\*(PN'ovs\-vsctl' .IP "\fBtable\fR" .el .IP "\fBtable\fR (default)" 2-D text tables with aligned columns. .ie '\*(PN'ovs\-vsctl' .IP "\fBlist\fR (default)" .el .IP "\fBlist\fR" A list with one column per line and rows separated by a blank line. .IP "\fBhtml\fR" HTML tables. .IP "\fBcsv\fR" Comma-separated values as defined in RFC 4180. .IP "\fBjson\fR" JSON format as defined in RFC 4627. The output is a sequence of JSON objects, each of which corresponds to one table. Each JSON object has the following members with the noted values: .RS .IP "\fBcaption\fR" The table's caption. This member is omitted if the table has no caption. .IP "\fBheadings\fR" An array with one element per table column. Each array element is a string giving the corresponding column's heading. .IP "\fBdata\fR" An array with one element per table row. Each element is also an array with one element per table column. The elements of this second-level array are the cells that constitute the table. Cells that represent OVSDB data or data types are expressed in the format described in the OVSDB specification; other cells are simply expressed as text strings. .RE .RE . .IP "\fB\-d \fIformat\fR" .IQ "\fB\-\-data=\fIformat\fR" Sets the formatting for cells within output tables. The following types of \fIformat\fR are available: .RS .IP "\fBstring\fR (default)" The simple format described in the \fBDatabase Values\fR .ie '\*(PN'ovs\-vsctl' section below. .el section of \fBovs\-vsctl\fR(8). .IP "\fBbare\fR" The simple format with punctuation stripped off: \fB[]\fR and \fB{}\fR are omitted around sets, maps, and empty columns, items within sets and maps are space-separated, and strings are never quoted. This format may be easier for scripts to parse. .IP "\fBjson\fR" JSON. .RE .IP The \fBjson\fR output format always outputs cells in JSON format, ignoring this option. . .IP "\fB\-\-no\-heading\fR" This option suppresses the heading row that otherwise appears in the first row of table output. . .IP "\fB\-\-pretty\fR" By default, JSON in output is printed as compactly as possible. This option causes JSON in output to be printed in a more readable fashion. Members of objects and elements of arrays are printed one per line, with indentation. .IP This option does not affect JSON in tables, which is always printed compactly. .IP "\fB\-\-bare\fR" Equivalent to \fB\-\-format=list \-\-data=bare \-\-no\-headings\fR. openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-i586.h0000644000000000000000000000013213534540071017335 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.813853662 openvswitch-2.5.9/lib/ovs-atomic-i586.h0000644000175000017500000005576113534540071021041 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on 32-bit 586+ with GCC. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #define OVS_ATOMIC_I586_IMPL 1 /* * These assumptions have been adopted from the x86_64 Memory model: * * - 1, 2, and 4 byte loads and stores are atomic on aligned memory. * - Loads are not reordered with other loads. * - Stores are not reordered with OLDER loads. * - Loads may be reordered with OLDER stores to a different memory location, * but not with OLDER stores to the same memory location. * - Stores are not reordered with other stores, except maybe for special * instructions not emitted by compilers, or by the stores performed by * a single fast string operation (e.g., "stos"). As long as the atomic * stores are not combined with any other stores, even the allowed reordering * of the stores by a single fast string operation is not a problem. * - Neither loads nor stores are reordered with locked instructions. * - Stores by a single processor are observed in the same order by all * processors. * - (Unlocked) Stores from different processors are NOT ordered. * - Memory ordering obeys causality (memory ordering respects transitive * visibility). * - Any two stores are seen in a consistent order by processors other than * the those performing the stores. * - Locked instructions have total order. * * These rules imply that: * * - Locked instructions are not needed for aligned loads or stores to make * them atomic for sizes upto 4 bytes. 8 byte objects need locked * instructions. * - All stores have release semantics; none of the preceding stores or loads * can be reordered with following stores. Following loads could still be * reordered to happen before the store, but that is not a violation of the * release semantics. * - All loads from a given memory location have acquire semantics with * respect to the stores on the same memory location; none of the following * loads or stores can be reordered with the load. Preceding stores to a * different memory location MAY be reordered with the load, but that is not * a violation of the acquire semantics (i.e., the loads and stores of two * critical sections guarded by a different memory location can overlap). * - Locked instructions serve as CPU memory barriers by themselves. * - Locked stores implement the sequential consistency memory order. Using * locked instructions when seq_cst memory order is requested allows normal * loads to observe the stores in the same (total) order without using CPU * memory barrier after the loads. * * NOTE: Some older AMD Opteron processors have a bug that violates the * acquire semantics described above. The bug manifests as an unlocked * read-modify-write operation following a "semaphore operation" operating * on data that existed before entering the critical section; i.e., the * preceding "semaphore operation" fails to function as an acquire barrier. * The affected CPUs are AMD family 15, models 32 to 63. * * Ref. http://support.amd.com/TechDocs/25759.pdf errata #147. */ /* Barriers. */ #define compiler_barrier() asm volatile(" " : : : "memory") #define cpu_barrier() asm volatile("lock; addl $0,(%%esp)" ::: "memory", "cc") /* * The 'volatile' keyword prevents the compiler from keeping the atomic * value in a register, and generates a new memory access for each atomic * operation. This allows the implementations of memory_order_relaxed and * memory_order_consume to avoid issuing a compiler memory barrier, allowing * full optimization of all surrounding non-atomic variables. * * The placement of the 'volatile' keyword after the 'TYPE' below is highly * significant when the TYPE is a pointer type. In that case we want the * pointer to be declared volatile, not the data type that is being pointed * at! * * Attribute aligned is used to tell the compiler to align 64-bit data * on a 8-byte boundary. This allows more efficient atomic access, as the * the CPU guarantees such memory accesses to be atomic. */ #define ATOMIC(TYPE) TYPE volatile __attribute__((aligned(sizeof(TYPE)))) /* Memory ordering. Must be passed in as a constant. */ typedef enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; #define ATOMIC_BOOL_LOCK_FREE 2 #define ATOMIC_CHAR_LOCK_FREE 2 #define ATOMIC_SHORT_LOCK_FREE 2 #define ATOMIC_INT_LOCK_FREE 2 #define ATOMIC_LONG_LOCK_FREE 2 #define ATOMIC_LLONG_LOCK_FREE 2 #define ATOMIC_POINTER_LOCK_FREE 2 #define IS_LOCKLESS_ATOMIC(OBJECT) \ (sizeof(OBJECT) <= 8 && IS_POW2(sizeof(OBJECT))) #define ATOMIC_VAR_INIT(VALUE) VALUE #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) /* * The memory_model_relaxed does not need a compiler barrier, if the * atomic operation can otherwise be guaranteed to not be moved with * respect to other atomic operations on the same memory location. Using * the 'volatile' keyword in the definition of the atomic types * accomplishes this, as memory accesses to volatile data may not be * optimized away, or be reordered with other volatile accesses. * * On x86 also memory_order_consume is automatic, and data dependency on a * volatile atomic variable means that the compiler optimizations should not * cause problems. That is, the compiler should not speculate the value of * the atomic_read, as it is going to read it from the memory anyway. * This allows omiting the compiler memory barrier on atomic_reads with * memory_order_consume. This matches the definition of * smp_read_barrier_depends() in Linux kernel as a nop for x86, and its usage * in rcu_dereference(). * * We use this same logic below to choose inline assembly statements with or * without a compiler memory barrier. */ static inline void atomic_compiler_barrier(memory_order order) { if (order > memory_order_consume) { compiler_barrier(); } } static inline void atomic_thread_fence(memory_order order) { if (order == memory_order_seq_cst) { cpu_barrier(); } else { atomic_compiler_barrier(order); } } static inline void atomic_signal_fence(memory_order order) { atomic_compiler_barrier(order); } #define atomic_is_lock_free(OBJ) \ ((void) *(OBJ), \ IS_LOCKLESS_ATOMIC(*(OBJ)) ? 2 : 0) /* The 8-byte atomic exchange uses cmpxchg8b with the SRC (ax:dx) as * the expected value (bx:cx), which will get replaced by the current * value in the likely case it did not match, after which we keep * trying until the swap succeeds. */ #if defined(__PIC__) /* ebx may not be clobbered when compiled with -fPIC, must save and * restore it. Furthermore, 'DST' may be addressed via ebx, so the * address must be passed via a register so that it remains valid also * after changing ebx. */ #define atomic_exchange_8__(DST, SRC, CLOB) \ uint32_t temp____; \ \ asm volatile(" movl %%ebx,%2 ; " \ " movl %%eax,%%ebx ; " \ " movl %%edx,%%ecx ; " \ "1: " \ "lock; cmpxchg8b (%0); " \ " jne 1b ; " \ " movl %2,%%ebx ; " \ " # atomic_exchange_8__ " \ : "+r" (DST), /* 0 */ \ "+A" (SRC), /* 1 */ \ "=mr" (temp____) /* 2 */ \ :: "ecx", CLOB, "cc") #else #define atomic_exchange_8__(DST, SRC, CLOB) \ asm volatile(" movl %%eax,%%ebx ; " \ " movl %%edx,%%ecx ; " \ "1: " \ "lock; cmpxchg8b %0 ; " \ " jne 1b ; " \ " # atomic_exchange_8__ " \ : "+m" (*DST), /* 0 */ \ "+A" (SRC) /* 1 */ \ :: "ebx", "ecx", CLOB, "cc") #endif #define atomic_exchange__(DST, SRC, ORDER) \ ({ \ typeof(DST) dst___ = (DST); \ typeof(*(DST)) src___ = (SRC); \ \ if ((ORDER) > memory_order_consume) { \ if (sizeof(*(DST)) == 8) { \ atomic_exchange_8__(dst___, src___, "memory"); \ } else { \ asm volatile("xchg %1,%0 ; " \ "# atomic_exchange__" \ : "+r" (src___), /* 0 */ \ "+m" (*dst___) /* 1 */ \ :: "memory"); \ } \ } else { \ if (sizeof(*(DST)) == 8) { \ atomic_exchange_8__(dst___, src___, "cc"); \ } else { \ asm volatile("xchg %1,%0 ; " \ "# atomic_exchange__" \ : "+r" (src___), /* 0 */ \ "+m" (*dst___)); /* 1 */ \ } \ } \ src___; \ }) #if defined(__SSE__) /* SSE registers are 128-bit wide, and moving the lowest 64-bits of an SSE * register to proerly aligned memory is atomic. See ATOMIC(TYPE) above. */ #define atomic_store_8__(DST, SRC) \ asm volatile("movq %1,%0 ; # atomic_store_8__" \ : "=m" (*DST) /* 0 */ \ : "x" (SRC)) /* 1, SSE */ #else /* Locked 64-bit exchange is available on all i586 CPUs. */ #define atomic_store_8__(DST, SRC) \ atomic_exchange_8__(DST, SRC, "cc") #endif #define atomic_store_explicit(DST, SRC, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(*(DST)) src__ = (SRC); \ \ if ((ORDER) != memory_order_seq_cst) { \ atomic_compiler_barrier(ORDER); \ if (sizeof(*(DST)) == 8) { \ atomic_store_8__(dst__, src__); \ } else { \ *dst__ = src__; \ } \ } else { \ atomic_exchange__(dst__, src__, ORDER); \ } \ (void) 0; \ }) #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) #if defined(__SSE__) /* SSE registers are 128-bit wide, and moving 64-bits from properly aligned * memory to an SSE register is atomic. See ATOMIC(TYPE) above. */ #define atomic_read_8__(SRC, DST) \ ({ \ typeof(*(DST)) res__; \ \ asm ("movq %1,%0 ; # atomic_read_8__" \ : "=x" (res__) /* 0, SSE. */ \ : "m" (*SRC)); /* 1 */ \ *(DST) = res__; \ }) #else /* Must use locked cmpxchg8b (available on all i586 CPUs) if compiled w/o sse * support. Compare '*DST' to a random value in bx:cx and returns the actual * value in ax:dx. The registers bx and cx are only read, so they are not * clobbered. */ #define atomic_read_8__(SRC, DST) \ ({ \ typeof(*(DST)) res__; \ \ asm (" movl %%ebx,%%eax ; " \ " movl %%ecx,%%edx ; " \ "lock; cmpxchg8b %1 ; " \ "# atomic_read_8__ " \ : "=&A" (res__), /* 0 */ \ "+m" (*SRC) /* 1 */ \ : : "cc"); \ *(DST) = res__; \ }) #endif #define atomic_read_explicit(SRC, DST, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(SRC) src__ = (SRC); \ \ if (sizeof(*(DST)) <= 4) { \ *dst__ = *src__; \ } else { \ atomic_read_8__(SRC, DST); \ } \ atomic_compiler_barrier(ORDER); \ (void) 0; \ }) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #if defined(__PIC__) /* ebx may not be used as an input when compiled with -fPIC, must save * and restore it. Furthermore, 'DST' may be addressed via ebx, so * the address must be passed via a register so that it remains valid * also after changing ebx. */ #define atomic_compare_exchange_8__(DST, EXP, SRC, RES, CLOB) \ asm volatile(" xchgl %%ebx,%3 ; " \ "lock; cmpxchg8b (%1) ; " \ " xchgl %3,%%ebx ; " \ " sete %0 " \ "# atomic_compare_exchange_8__" \ : "=q" (RES), /* 0 */ \ "+r" (DST), /* 1 */ \ "+A" (EXP) /* 2 */ \ : "r" ((uint32_t)SRC), /* 3 */ \ "c" ((uint32_t)((uint64_t)SRC >> 32)) /* 4 */ \ : CLOB, "cc") #else #define atomic_compare_exchange_8__(DST, EXP, SRC, RES, CLOB) \ asm volatile("lock; cmpxchg8b %1 ; " \ " sete %0 " \ "# atomic_compare_exchange_8__" \ : "=q" (RES), /* 0 */ \ "+m" (*DST), /* 1 */ \ "+A" (EXP) /* 2 */ \ : "b" ((uint32_t)SRC), /* 3 */ \ "c" ((uint32_t)((uint64_t)SRC >> 32)) /* 4 */ \ : CLOB, "cc") #endif #define atomic_compare_exchange__(DST, EXP, SRC, RES, CLOB) \ asm volatile("lock; cmpxchg %3,%1 ; " \ " sete %0 " \ "# atomic_compare_exchange__" \ : "=q" (RES), /* 0 */ \ "+m" (*DST), /* 1 */ \ "+a" (EXP) /* 2 */ \ : "r" (SRC) /* 3 */ \ : CLOB, "cc") /* ORD_FAIL is ignored, as atomic_compare_exchange__ already implements * at least as strong a barrier as allowed for ORD_FAIL in all cases. */ #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORDER, ORD_FAIL) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(DST) expp__ = (EXP); \ typeof(*(DST)) src__ = (SRC); \ typeof(*(DST)) exp__ = *expp__; \ uint8_t res__; \ (void)ORD_FAIL; \ \ if ((ORDER) > memory_order_consume) { \ if (sizeof(*(DST)) <= 4) { \ atomic_compare_exchange__(dst__, exp__, src__, res__, \ "memory"); \ } else { \ atomic_compare_exchange_8__(dst__, exp__, src__, res__, \ "memory"); \ } \ } else { \ if (sizeof(*(DST)) <= 4) { \ atomic_compare_exchange__(dst__, exp__, src__, res__, \ "cc"); \ } else { \ atomic_compare_exchange_8__(dst__, exp__, src__, res__, \ "cc"); \ } \ } \ if (!res__) { \ *expp__ = exp__; \ } \ (bool)res__; \ }) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_weak \ atomic_compare_exchange_strong #define atomic_compare_exchange_weak_explicit \ atomic_compare_exchange_strong_explicit #define atomic_add__(RMW, ARG, CLOB) \ asm volatile("lock; xadd %0,%1 ; " \ "# atomic_add__ " \ : "+r" (ARG), /* 0 */ \ "+m" (*RMW) /* 1 */ \ :: CLOB, "cc") #define atomic_add_32__(RMW, ARG, ORIG, ORDER) \ ({ \ typeof(RMW) rmw__ = (RMW); \ typeof(*(RMW)) arg__ = (ARG); \ \ if ((ORDER) > memory_order_consume) { \ atomic_add__(rmw__, arg__, "memory"); \ } else { \ atomic_add__(rmw__, arg__, "cc"); \ } \ *(ORIG) = arg__; \ }) /* We could use simple locked instructions if the original value was not * needed. */ #define atomic_op__(RMW, OP, ARG, ORIG, ORDER) \ ({ \ typeof(RMW) rmw__ = (RMW); \ typeof(ARG) arg__ = (ARG); \ \ typeof(*(RMW)) val__; \ \ atomic_read_explicit(rmw__, &val__, memory_order_relaxed); \ do { \ } while (!atomic_compare_exchange_weak_explicit(rmw__, &val__, \ val__ OP arg__, \ ORDER, \ memory_order_relaxed)); \ *(ORIG) = val__; \ }) #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ (sizeof(*(RMW)) <= 4 \ ? atomic_add_32__(RMW, ARG, ORIG, ORDER) \ : atomic_op__(RMW, +, ARG, ORIG, ORDER)) #define atomic_add(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ (sizeof(*(RMW)) <= 4 \ ? atomic_add_32__(RMW, -(ARG), ORIG, ORDER) \ : atomic_op__(RMW, -, ARG, ORIG, ORDER)) #define atomic_sub(RMW, ARG, ORIG) \ atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, |, ARG, ORIG, ORDER) #define atomic_or(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, ^, ARG, ORIG, ORDER) #define atomic_xor(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, &, ARG, ORIG, ORDER) #define atomic_and(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) /* atomic_flag */ typedef ATOMIC(int) atomic_flag; #define ATOMIC_FLAG_INIT { false } #define atomic_flag_test_and_set_explicit(FLAG, ORDER) \ ((bool)atomic_exchange__(FLAG, 1, ORDER)) #define atomic_flag_test_and_set(FLAG) \ atomic_flag_test_and_set_explicit(FLAG, memory_order_seq_cst) #define atomic_flag_clear_explicit(FLAG, ORDER) \ atomic_store_explicit(FLAG, 0, ORDER) #define atomic_flag_clear(FLAG) \ atomic_flag_clear_explicit(FLAG, memory_order_seq_cst) openvswitch-2.5.9/lib/PaxHeaders.82075/fat-rwlock.h0000644000000000000000000000013213534540071016634 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.721852984 openvswitch-2.5.9/lib/fat-rwlock.h0000644000175000017500000000324313534540071020324 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FAT_RWLOCK_H #define FAT_RWLOCK_H 1 #include "compiler.h" #include "list.h" #include "ovs-thread.h" /* "Fat rwlock". * * This implements a reader-writer lock that uses a lot of memory (128 to 192 * bytes per thread that takes the lock) but avoids cache line bouncing when * taking the read side. Thus, a fat_rwlock is a good choice for rwlocks taken * frequently by readers. */ struct OVS_LOCKABLE fat_rwlock { ovsthread_key_t key; /* Contains "struct fat_rwlock_slot"s, one for each thread that has taken * this lock. Guarded by 'mutex'. */ struct ovs_list threads OVS_GUARDED; struct ovs_mutex mutex; }; void fat_rwlock_init(struct fat_rwlock *); void fat_rwlock_destroy(struct fat_rwlock *); void fat_rwlock_rdlock(const struct fat_rwlock *rwlock) OVS_ACQ_RDLOCK(rwlock); int fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock) OVS_TRY_RDLOCK(0, rwlock); void fat_rwlock_wrlock(const struct fat_rwlock *rwlock) OVS_ACQ_WRLOCK(rwlock); void fat_rwlock_unlock(const struct fat_rwlock *rwlock) OVS_RELEASES(rwlock); #endif /* fat-rwlock.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/tun-metadata.h0000644000000000000000000000013213534540071017147 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.921854458 openvswitch-2.5.9/lib/tun-metadata.h0000644000175000017500000001521513534540071020641 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TUN_METADATA_H #define TUN_METADATA_H 1 #include #include "dynamic-string.h" #include "geneve.h" #include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" struct flow_tnl; struct match; struct mf_field; union mf_value; struct ofputil_tlv_table_mod; struct ofputil_tlv_table_reply; struct tun_table; #define TUN_METADATA_NUM_OPTS 64 #define TUN_METADATA_TOT_OPT_SIZE 256 /* Tunnel option data, plus metadata to aid in their interpretation. * * The option data exists in two forms and is interpreted differently depending * on whether FLOW_TNL_F_UDPIF is set in struct flow_tnl flags: * * When FLOW_TNL_F_UDPIF is set, the tunnel metadata is in "userspace datapath * format". This is typically used for fast-path packet processing to avoid * the cost of translating options and in situations where we need to maintain * tunnel metadata exactly as it came in. In this case 'opts.gnv' is raw * packet data from the tunnel header and 'present.len' indicates the length * of the data stored there. In these situations, 'tab' is NULL. * * In all other cases, we are doing flow-based processing (such as during * upcalls). FLOW_TNL_F_UDPIF is not set and options are reordered into * pre-allocated locations. 'present.map' is indexed by type, that is, by the * in TUN_METADATA, so that e.g. TUN_METADATA5 is present if * 'present.map & (1ULL << 5)' is nonzero. The actual data for TUN_METADATA5, * if present, might be anywhere in 'opts.u8' (not necessarily even contiguous), * and finding it requires referring to 'tab', if set, or the global metadata * table. */ struct tun_metadata { union { /* Valid members of 'opts'. When 'opts' is sorted into known types, * 'map' is used. When 'opts' is raw packet data, 'len' is used. */ uint64_t map; /* 1-bit for each present TLV. */ uint8_t len; /* Length of data in 'opts'. */ } present; struct tun_table *tab; /* Types & lengths for 'opts' and 'opt_map'. */ #if UINTPTR_MAX == UINT32_MAX uint8_t pad[4]; /* Pad to 64-bit boundary. */ #endif union { uint8_t u8[TUN_METADATA_TOT_OPT_SIZE]; /* Values from tunnel TLVs. */ struct geneve_opt gnv[TLV_TOT_OPT_SIZE / sizeof(struct geneve_opt)]; } opts; }; BUILD_ASSERT_DECL(offsetof(struct tun_metadata, opts) % 8 == 0); BUILD_ASSERT_DECL(sizeof(((struct tun_metadata *)0)->present.map) * 8 >= TUN_METADATA_NUM_OPTS); /* The location of an option can be stored either as a single offset/len * pair (hopefully) or if the address space is fragmented then it is a * linked list of these blocks. */ struct tun_metadata_loc_chain { struct tun_metadata_loc_chain *next; int offset; /* In bytes, from start of 'opts', multiple of 4. */ int len; /* In bytes, multiple of 4. */ }; struct tun_metadata_loc { int len; /* Sum of 'len' over elements in chain. */ struct tun_metadata_loc_chain c; }; /* Bookkeeping information to keep track of an option that was allocated * inside struct match. */ struct tun_metadata_match_entry { struct tun_metadata_loc loc; /* Allocated position. */ bool masked; /* Source value had a mask. Otherwise we can't tell if the * entire field was exact matched or only the portion that * is the same size as the value. */ }; /* Allocation of options inside struct match. This is important if we don't * have access to a global allocation table - either because there isn't one * (ovs-ofctl) or if we need to keep the allocation outside of packet * processing context (Packet-In). These structures never have dynamically * allocated memory because the address space is never fragmented. */ struct tun_metadata_allocation { struct tun_metadata_match_entry entry[TUN_METADATA_NUM_OPTS]; int alloc_offset; /* Byte offset into 'opts', multiple of 4. */ bool valid; /* Set to true after any allocation occurs. */ }; void tun_metadata_init(void); enum ofperr tun_metadata_table_mod(struct ofputil_tlv_table_mod *); void tun_metadata_table_request(struct ofputil_tlv_table_reply *); void tun_metadata_read(const struct flow_tnl *, const struct mf_field *, union mf_value *); void tun_metadata_write(struct flow_tnl *, const struct mf_field *, const union mf_value *); void tun_metadata_set_match(const struct mf_field *, const union mf_value *value, const union mf_value *mask, struct match *, char **err_str); void tun_metadata_get_fmd(const struct flow_tnl *, struct match *flow_metadata); int tun_metadata_from_geneve_nlattr(const struct nlattr *attr, const struct nlattr *flow_attrs, size_t flow_attr_len, const struct flow_tnl *flow_tun, bool udpif, struct flow_tnl *tun); void tun_metadata_to_geneve_nlattr(const struct flow_tnl *tun, const struct flow_tnl *flow, const struct ofpbuf *key, struct ofpbuf *); int tun_metadata_from_geneve_udpif(const struct flow_tnl *flow, const struct flow_tnl *src, struct flow_tnl *dst); void tun_metadata_to_geneve_udpif_mask(const struct flow_tnl *flow_src, const struct flow_tnl *mask_src, const struct geneve_opt *flow_src_opt, int opts_len, struct geneve_opt *dst); int tun_metadata_to_geneve_header(const struct flow_tnl *flow, struct geneve_opt *, bool *crit_opt); void tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm, const struct match *); void tun_metadata_match_format(struct ds *, const struct match *); #endif /* tun-metadata.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dummy.c0000644000000000000000000000013213534540071015711 xustar0030 mtime=1567801401.381681023 30 atime=1567801402.073686105 30 ctime=1567801424.697852806 openvswitch-2.5.9/lib/dummy.c0000644000175000017500000000313213534540071017376 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dummy.h" #include #include "util.h" /* Enables support for "dummy" network devices and dpifs, which are useful for * testing. A client program might call this function if it is designed * specifically for testing or the user enables it on the command line. * * 'arg' is parsed to determine the override level (see the definition of enum * dummy_level). * * There is no strong reason why dummy devices shouldn't always be enabled. */ void dummy_enable(const char *arg) { enum dummy_level level; if (!arg || !arg[0]) { level = DUMMY_OVERRIDE_NONE; } else if (!strcmp(arg, "system")) { level = DUMMY_OVERRIDE_SYSTEM; } else if (!strcmp(arg, "override")) { level = DUMMY_OVERRIDE_ALL; } else { ovs_fatal(0, "%s: unknown dummy level", arg); } netdev_dummy_register(level); dpif_dummy_register(level); timeval_dummy_register(); vlandev_dummy_enable(); ofpact_dummy_enable(); } openvswitch-2.5.9/lib/PaxHeaders.82075/latch-unix.c0000644000000000000000000000013113534540071016631 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.965854783 openvswitch-2.5.9/lib/latch-unix.c0000644000175000017500000000414313534540071020322 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "latch.h" #include #include #include #include "poll-loop.h" #include "socket-util.h" /* Initializes 'latch' as initially unset. */ void latch_init(struct latch *latch) { xpipe_nonblocking(latch->fds); } /* Destroys 'latch'. */ void latch_destroy(struct latch *latch) { close(latch->fds[0]); close(latch->fds[1]); } /* Resets 'latch' to the unset state. Returns true if 'latch' was previously * set, false otherwise. */ bool latch_poll(struct latch *latch) { char buffer[_POSIX_PIPE_BUF]; return read(latch->fds[0], buffer, sizeof buffer) > 0; } /* Sets 'latch'. * * Calls are not additive: a single latch_poll() clears out any number of * latch_set(). */ void latch_set(struct latch *latch) { ignore(write(latch->fds[1], "", 1)); } /* Returns true if 'latch' is set, false otherwise. Does not reset 'latch' * to the unset state. */ bool latch_is_set(const struct latch *latch) { struct pollfd pfd; int retval; pfd.fd = latch->fds[0]; pfd.events = POLLIN; do { retval = poll(&pfd, 1, 0); } while (retval < 0 && errno == EINTR); return pfd.revents & POLLIN; } /* Causes the next poll_block() to wake up when 'latch' is set. * * ('where' is used in debug logging. Commonly one would use latch_wait() to * automatically provide the caller's source file and line number for * 'where'.) */ void latch_wait_at(const struct latch *latch, const char *where) { poll_fd_wait_at(latch->fds[0], POLLIN, where); } openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-dpdk.c0000644000000000000000000000013213534540071016763 xustar0030 mtime=1567801401.441681464 30 atime=1567801402.077686135 30 ctime=1567801424.993854989 openvswitch-2.5.9/lib/netdev-dpdk.c0000644000175000017500000021314313534540071020455 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dirs.h" #include "dp-packet.h" #include "dpif-netdev.h" #include "fatal-signal.h" #include "list.h" #include "netdev-dpdk.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "odp-util.h" #include "ofp-print.h" #include "ovs-numa.h" #include "ovs-thread.h" #include "ovs-rcu.h" #include "packets.h" #include "shash.h" #include "sset.h" #include "unaligned.h" #include "timeval.h" #include "unixctl.h" #include "openvswitch/vlog.h" #include "rte_config.h" #include "rte_mbuf.h" #include "rte_virtio_net.h" VLOG_DEFINE_THIS_MODULE(dpdk); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); #define DPDK_PORT_WATCHDOG_INTERVAL 5 #define OVS_CACHE_LINE_SIZE CACHE_LINE_SIZE #define OVS_VPORT_DPDK "ovs_dpdk" /* * need to reserve tons of extra space in the mbufs so we can align the * DMA addresses to 4KB. * The minimum mbuf size is limited to avoid scatter behaviour and drop in * performance for standard Ethernet MTU. */ #define MTU_TO_MAX_LEN(mtu) ((mtu) + ETHER_HDR_LEN + ETHER_CRC_LEN) #define MBUF_SIZE_MTU(mtu) (MTU_TO_MAX_LEN(mtu) \ + sizeof(struct dp_packet) \ + RTE_PKTMBUF_HEADROOM) #define MBUF_SIZE_DRIVER (2048 \ + sizeof (struct rte_mbuf) \ + RTE_PKTMBUF_HEADROOM) #define MBUF_SIZE(mtu) MAX(MBUF_SIZE_MTU(mtu), MBUF_SIZE_DRIVER) /* Max and min number of packets in the mempool. OVS tries to allocate a * mempool with MAX_NB_MBUF: if this fails (because the system doesn't have * enough hugepages) we keep halving the number until the allocation succeeds * or we reach MIN_NB_MBUF */ #define MAX_NB_MBUF (4096 * 64) #define MIN_NB_MBUF (4096 * 4) #define MP_CACHE_SZ RTE_MEMPOOL_CACHE_MAX_SIZE /* MAX_NB_MBUF can be divided by 2 many times, until MIN_NB_MBUF */ BUILD_ASSERT_DECL(MAX_NB_MBUF % ROUND_DOWN_POW2(MAX_NB_MBUF/MIN_NB_MBUF) == 0); /* The smallest possible NB_MBUF that we're going to try should be a multiple * of MP_CACHE_SZ. This is advised by DPDK documentation. */ BUILD_ASSERT_DECL((MAX_NB_MBUF / ROUND_DOWN_POW2(MAX_NB_MBUF/MIN_NB_MBUF)) % MP_CACHE_SZ == 0); #define SOCKET0 0 #define NIC_PORT_RX_Q_SIZE 2048 /* Size of Physical NIC RX Queue, Max (n+32<=4096)*/ #define NIC_PORT_TX_Q_SIZE 2048 /* Size of Physical NIC TX Queue, Max (n+32<=4096)*/ #define OVS_VHOST_MAX_QUEUE_NUM 1024 /* Maximum number of vHost TX queues. */ #define OVS_VHOST_QUEUE_MAP_UNKNOWN (-1) /* Mapping not initialized. */ #define OVS_VHOST_QUEUE_DISABLED (-2) /* Queue was disabled by guest and not * yet mapped to another queue. */ static char *cuse_dev_name = NULL; /* Character device cuse_dev_name. */ static char *vhost_sock_dir = NULL; /* Location of vhost-user sockets */ #define VHOST_ENQ_RETRY_NUM 8 static const struct rte_eth_conf port_conf = { .rxmode = { .mq_mode = ETH_MQ_RX_RSS, .split_hdr_size = 0, .header_split = 0, /* Header Split disabled */ .hw_ip_checksum = 0, /* IP checksum offload disabled */ .hw_vlan_filter = 0, /* VLAN filtering disabled */ .jumbo_frame = 0, /* Jumbo Frame Support disabled */ .hw_strip_crc = 0, }, .rx_adv_conf = { .rss_conf = { .rss_key = NULL, .rss_hf = ETH_RSS_IP | ETH_RSS_UDP | ETH_RSS_TCP, }, }, .txmode = { .mq_mode = ETH_MQ_TX_NONE, }, }; enum { DPDK_RING_SIZE = 256 }; BUILD_ASSERT_DECL(IS_POW2(DPDK_RING_SIZE)); enum { DRAIN_TSC = 200000ULL }; enum dpdk_dev_type { DPDK_DEV_ETH = 0, DPDK_DEV_VHOST = 1, }; static int rte_eal_init_ret = ENODEV; static struct ovs_mutex dpdk_mutex = OVS_MUTEX_INITIALIZER; /* Contains all 'struct dpdk_dev's. */ static struct ovs_list dpdk_list OVS_GUARDED_BY(dpdk_mutex) = OVS_LIST_INITIALIZER(&dpdk_list); static struct ovs_list dpdk_mp_list OVS_GUARDED_BY(dpdk_mutex) = OVS_LIST_INITIALIZER(&dpdk_mp_list); /* This mutex must be used by non pmd threads when allocating or freeing * mbufs through mempools. */ static struct ovs_mutex nonpmd_mempool_mutex = OVS_MUTEX_INITIALIZER; struct dpdk_mp { struct rte_mempool *mp; int mtu; int socket_id; int refcount; struct ovs_list list_node OVS_GUARDED_BY(dpdk_mutex); }; /* There should be one 'struct dpdk_tx_queue' created for * each cpu core. */ struct dpdk_tx_queue { rte_spinlock_t tx_lock; /* Protects the members and the NIC queue * from concurrent access. It is used only * if the queue is shared among different * pmd threads (see 'txq_needs_locking'). */ int map; /* Mapping of configured vhost-user queues * to enabled by guest. */ }; /* dpdk has no way to remove dpdk ring ethernet devices so we have to keep them around once they've been created */ static struct ovs_list dpdk_ring_list OVS_GUARDED_BY(dpdk_mutex) = OVS_LIST_INITIALIZER(&dpdk_ring_list); struct dpdk_ring { /* For the client rings */ struct rte_ring *cring_tx; struct rte_ring *cring_rx; int user_port_id; /* User given port no, parsed from port name */ int eth_port_id; /* ethernet device port id */ struct ovs_list list_node OVS_GUARDED_BY(dpdk_mutex); }; struct netdev_dpdk { struct netdev up; int port_id; int max_packet_len; enum dpdk_dev_type type; struct dpdk_tx_queue *tx_q; struct ovs_mutex mutex OVS_ACQ_AFTER(dpdk_mutex); struct dpdk_mp *dpdk_mp; int mtu; int socket_id; int buf_size; struct netdev_stats stats; /* Protects stats */ rte_spinlock_t stats_lock; struct eth_addr hwaddr; enum netdev_flags flags; struct rte_eth_link link; int link_reset_cnt; /* The user might request more txqs than the NIC has. We remap those * ('up.n_txq') on these ('real_n_txq'). * If the numbers match, 'txq_needs_locking' is false, otherwise it is * true and we will take a spinlock on transmission */ int real_n_txq; int real_n_rxq; bool txq_needs_locking; /* virtio-net structure for vhost device */ OVSRCU_TYPE(struct virtio_net *) virtio_dev; /* Identifier used to distinguish vhost devices from each other */ char vhost_id[PATH_MAX]; /* In dpdk_list. */ struct ovs_list list_node OVS_GUARDED_BY(dpdk_mutex); }; struct netdev_rxq_dpdk { struct netdev_rxq up; int port_id; }; static bool dpdk_thread_is_pmd(void); static int netdev_dpdk_construct(struct netdev *); struct virtio_net * netdev_dpdk_get_virtio(const struct netdev_dpdk *dev); static bool is_dpdk_class(const struct netdev_class *class) { return class->construct == netdev_dpdk_construct; } /* XXX: use dpdk malloc for entire OVS. in fact huge page should be used * for all other segments data, bss and text. */ static void * dpdk_rte_mzalloc(size_t sz) { void *ptr; ptr = rte_zmalloc(OVS_VPORT_DPDK, sz, OVS_CACHE_LINE_SIZE); if (ptr == NULL) { out_of_memory(); } return ptr; } /* XXX this function should be called only by pmd threads (or by non pmd * threads holding the nonpmd_mempool_mutex) */ void free_dpdk_buf(struct dp_packet *p) { struct rte_mbuf *pkt = (struct rte_mbuf *) p; rte_pktmbuf_free(pkt); } static void __rte_pktmbuf_init(struct rte_mempool *mp, void *opaque_arg OVS_UNUSED, void *_m, unsigned i OVS_UNUSED) { struct rte_mbuf *m = _m; uint32_t buf_len = mp->elt_size - sizeof(struct dp_packet); RTE_MBUF_ASSERT(mp->elt_size >= sizeof(struct dp_packet)); memset(m, 0, mp->elt_size); /* start of buffer is just after mbuf structure */ m->buf_addr = (char *)m + sizeof(struct dp_packet); m->buf_physaddr = rte_mempool_virt2phy(mp, m) + sizeof(struct dp_packet); m->buf_len = (uint16_t)buf_len; /* keep some headroom between start of buffer and data */ m->data_off = RTE_MIN(RTE_PKTMBUF_HEADROOM, m->buf_len); /* init some constant fields */ m->pool = mp; m->nb_segs = 1; m->port = 0xff; } static void ovs_rte_pktmbuf_init(struct rte_mempool *mp, void *opaque_arg OVS_UNUSED, void *_m, unsigned i OVS_UNUSED) { struct rte_mbuf *m = _m; __rte_pktmbuf_init(mp, opaque_arg, _m, i); dp_packet_init_dpdk((struct dp_packet *) m, m->buf_len); } static struct dpdk_mp * dpdk_mp_get(int socket_id, int mtu) OVS_REQUIRES(dpdk_mutex) { struct dpdk_mp *dmp = NULL; char mp_name[RTE_MEMPOOL_NAMESIZE]; unsigned mp_size; LIST_FOR_EACH (dmp, list_node, &dpdk_mp_list) { if (dmp->socket_id == socket_id && dmp->mtu == mtu) { dmp->refcount++; return dmp; } } dmp = dpdk_rte_mzalloc(sizeof *dmp); dmp->socket_id = socket_id; dmp->mtu = mtu; dmp->refcount = 1; mp_size = MAX_NB_MBUF; do { if (snprintf(mp_name, RTE_MEMPOOL_NAMESIZE, "ovs_mp_%d_%d_%u", dmp->mtu, dmp->socket_id, mp_size) < 0) { return NULL; } dmp->mp = rte_mempool_create(mp_name, mp_size, MBUF_SIZE(mtu), MP_CACHE_SZ, sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, ovs_rte_pktmbuf_init, NULL, socket_id, 0); } while (!dmp->mp && rte_errno == ENOMEM && (mp_size /= 2) >= MIN_NB_MBUF); if (dmp->mp == NULL) { return NULL; } else { VLOG_DBG("Allocated \"%s\" mempool with %u mbufs", mp_name, mp_size ); } list_push_back(&dpdk_mp_list, &dmp->list_node); return dmp; } static void dpdk_mp_put(struct dpdk_mp *dmp) { if (!dmp) { return; } dmp->refcount--; ovs_assert(dmp->refcount >= 0); #if 0 /* I could not find any API to destroy mp. */ if (dmp->refcount == 0) { list_delete(dmp->list_node); /* destroy mp-pool. */ } #endif } static void check_link_status(struct netdev_dpdk *dev) { struct rte_eth_link link; rte_eth_link_get_nowait(dev->port_id, &link); if (dev->link.link_status != link.link_status) { netdev_change_seq_changed(&dev->up); dev->link_reset_cnt++; dev->link = link; if (dev->link.link_status) { VLOG_DBG_RL(&rl, "Port %d Link Up - speed %u Mbps - %s", dev->port_id, (unsigned)dev->link.link_speed, (dev->link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex")); } else { VLOG_DBG_RL(&rl, "Port %d Link Down", dev->port_id); } } } static void * dpdk_watchdog(void *dummy OVS_UNUSED) { struct netdev_dpdk *dev; pthread_detach(pthread_self()); for (;;) { ovs_mutex_lock(&dpdk_mutex); LIST_FOR_EACH (dev, list_node, &dpdk_list) { ovs_mutex_lock(&dev->mutex); if (dev->type == DPDK_DEV_ETH) { check_link_status(dev); } ovs_mutex_unlock(&dev->mutex); } ovs_mutex_unlock(&dpdk_mutex); xsleep(DPDK_PORT_WATCHDOG_INTERVAL); } return NULL; } static int dpdk_eth_dev_queue_setup(struct netdev_dpdk *dev, int n_rxq, int n_txq) { int diag = 0; int i; /* A device may report more queues than it makes available (this has * been observed for Intel xl710, which reserves some of them for * SRIOV): rte_eth_*_queue_setup will fail if a queue is not * available. When this happens we can retry the configuration * and request less queues */ while (n_rxq && n_txq) { if (diag) { VLOG_INFO("Retrying setup with (rxq:%d txq:%d)", n_rxq, n_txq); } diag = rte_eth_dev_configure(dev->port_id, n_rxq, n_txq, &port_conf); if (diag) { break; } for (i = 0; i < n_txq; i++) { diag = rte_eth_tx_queue_setup(dev->port_id, i, NIC_PORT_TX_Q_SIZE, dev->socket_id, NULL); if (diag) { VLOG_INFO("Interface %s txq(%d) setup error: %s", dev->up.name, i, rte_strerror(-diag)); break; } } if (i != n_txq) { /* Retry with less tx queues */ n_txq = i; continue; } for (i = 0; i < n_rxq; i++) { diag = rte_eth_rx_queue_setup(dev->port_id, i, NIC_PORT_RX_Q_SIZE, dev->socket_id, NULL, dev->dpdk_mp->mp); if (diag) { VLOG_INFO("Interface %s rxq(%d) setup error: %s", dev->up.name, i, rte_strerror(-diag)); break; } } if (i != n_rxq) { /* Retry with less rx queues */ n_rxq = i; continue; } dev->up.n_rxq = n_rxq; dev->real_n_txq = n_txq; return 0; } return diag; } static int dpdk_eth_dev_init(struct netdev_dpdk *dev) OVS_REQUIRES(dpdk_mutex) { struct rte_pktmbuf_pool_private *mbp_priv; struct rte_eth_dev_info info; struct ether_addr eth_addr; int diag; int n_rxq, n_txq; if (dev->port_id < 0 || dev->port_id >= rte_eth_dev_count()) { return ENODEV; } rte_eth_dev_info_get(dev->port_id, &info); n_rxq = MIN(info.max_rx_queues, dev->up.n_rxq); n_txq = MIN(info.max_tx_queues, dev->up.n_txq); diag = dpdk_eth_dev_queue_setup(dev, n_rxq, n_txq); if (diag) { VLOG_ERR("Interface %s(rxq:%d txq:%d) configure error: %s", dev->up.name, n_rxq, n_txq, rte_strerror(-diag)); return -diag; } diag = rte_eth_dev_start(dev->port_id); if (diag) { VLOG_ERR("Interface %s start error: %s", dev->up.name, rte_strerror(-diag)); return -diag; } rte_eth_promiscuous_enable(dev->port_id); rte_eth_allmulticast_enable(dev->port_id); memset(ð_addr, 0x0, sizeof(eth_addr)); rte_eth_macaddr_get(dev->port_id, ð_addr); VLOG_INFO_RL(&rl, "Port %d: "ETH_ADDR_FMT"", dev->port_id, ETH_ADDR_BYTES_ARGS(eth_addr.addr_bytes)); memcpy(dev->hwaddr.ea, eth_addr.addr_bytes, ETH_ADDR_LEN); rte_eth_link_get_nowait(dev->port_id, &dev->link); mbp_priv = rte_mempool_get_priv(dev->dpdk_mp->mp); dev->buf_size = mbp_priv->mbuf_data_room_size - RTE_PKTMBUF_HEADROOM; dev->flags = NETDEV_UP | NETDEV_PROMISC; return 0; } static struct netdev_dpdk * netdev_dpdk_cast(const struct netdev *netdev) { return CONTAINER_OF(netdev, struct netdev_dpdk, up); } static struct netdev * netdev_dpdk_alloc(void) { struct netdev_dpdk *netdev = dpdk_rte_mzalloc(sizeof *netdev); return &netdev->up; } static void netdev_dpdk_alloc_txq(struct netdev_dpdk *netdev, unsigned int n_txqs) { unsigned i; netdev->tx_q = dpdk_rte_mzalloc(n_txqs * sizeof *netdev->tx_q); for (i = 0; i < n_txqs; i++) { /* Initialize map for vhost devices. */ netdev->tx_q[i].map = OVS_VHOST_QUEUE_MAP_UNKNOWN; rte_spinlock_init(&netdev->tx_q[i].tx_lock); } } static int netdev_dpdk_init(struct netdev *netdev_, unsigned int port_no, enum dpdk_dev_type type) OVS_REQUIRES(dpdk_mutex) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int sid; int err = 0; ovs_mutex_init(&netdev->mutex); ovs_mutex_lock(&netdev->mutex); rte_spinlock_init(&netdev->stats_lock); /* If the 'sid' is negative, it means that the kernel fails * to obtain the pci numa info. In that situation, always * use 'SOCKET0'. */ if (type == DPDK_DEV_ETH) { sid = rte_eth_dev_socket_id(port_no); } else { sid = rte_lcore_to_socket_id(rte_get_master_lcore()); } netdev->socket_id = sid < 0 ? SOCKET0 : sid; netdev->port_id = port_no; netdev->type = type; netdev->flags = 0; netdev->mtu = ETHER_MTU; netdev->max_packet_len = MTU_TO_MAX_LEN(netdev->mtu); netdev->dpdk_mp = dpdk_mp_get(netdev->socket_id, netdev->mtu); if (!netdev->dpdk_mp) { err = ENOMEM; goto unlock; } netdev_->n_txq = NR_QUEUE; netdev_->n_rxq = NR_QUEUE; netdev->real_n_txq = NR_QUEUE; if (type == DPDK_DEV_ETH) { netdev_dpdk_alloc_txq(netdev, NR_QUEUE); err = dpdk_eth_dev_init(netdev); if (err) { goto unlock; } } else { netdev_dpdk_alloc_txq(netdev, OVS_VHOST_MAX_QUEUE_NUM); /* Enable DPDK_DEV_VHOST device and set promiscuous mode flag. */ netdev->flags = NETDEV_UP | NETDEV_PROMISC; } list_push_back(&dpdk_list, &netdev->list_node); unlock: if (err) { rte_free(netdev->tx_q); } ovs_mutex_unlock(&netdev->mutex); return err; } static int dpdk_dev_parse_name(const char dev_name[], const char prefix[], unsigned int *port_no) { const char *cport; if (strncmp(dev_name, prefix, strlen(prefix))) { return ENODEV; } cport = dev_name + strlen(prefix); *port_no = strtol(cport, NULL, 0); /* string must be null terminated */ return 0; } static int vhost_construct_helper(struct netdev *netdev_) OVS_REQUIRES(dpdk_mutex) { if (rte_eal_init_ret) { return rte_eal_init_ret; } return netdev_dpdk_init(netdev_, -1, DPDK_DEV_VHOST); } static int netdev_dpdk_vhost_cuse_construct(struct netdev *netdev_) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int err; ovs_mutex_lock(&dpdk_mutex); strncpy(netdev->vhost_id, netdev->up.name, sizeof(netdev->vhost_id)); err = vhost_construct_helper(netdev_); ovs_mutex_unlock(&dpdk_mutex); return err; } static int netdev_dpdk_vhost_user_construct(struct netdev *netdev_) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); const char *name = netdev_->name; int err; /* 'name' is appended to 'vhost_sock_dir' and used to create a socket in * the file system. '/' or '\' would traverse directories, so they're not * acceptable in 'name'. */ if (strchr(name, '/') || strchr(name, '\\')) { VLOG_ERR("\"%s\" is not a valid name for a vhost-user port. " "A valid name must not include '/' or '\\'", name); return EINVAL; } ovs_mutex_lock(&dpdk_mutex); /* Take the name of the vhost-user port and append it to the location where * the socket is to be created, then register the socket. */ snprintf(netdev->vhost_id, sizeof(netdev->vhost_id), "%s/%s", vhost_sock_dir, name); err = rte_vhost_driver_register(netdev->vhost_id); if (err) { VLOG_ERR("vhost-user socket device setup failure for socket %s\n", netdev->vhost_id); } else { fatal_signal_add_file_to_unlink(netdev->vhost_id); VLOG_INFO("Socket %s created for vhost-user port %s\n", netdev->vhost_id, name); err = vhost_construct_helper(netdev_); } ovs_mutex_unlock(&dpdk_mutex); return err; } static int netdev_dpdk_construct(struct netdev *netdev) { unsigned int port_no; int err; if (rte_eal_init_ret) { return rte_eal_init_ret; } /* Names always start with "dpdk" */ err = dpdk_dev_parse_name(netdev->name, "dpdk", &port_no); if (err) { return err; } ovs_mutex_lock(&dpdk_mutex); err = netdev_dpdk_init(netdev, port_no, DPDK_DEV_ETH); ovs_mutex_unlock(&dpdk_mutex); return err; } static void netdev_dpdk_destruct(struct netdev *netdev_) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); ovs_mutex_lock(&dev->mutex); rte_eth_dev_stop(dev->port_id); ovs_mutex_unlock(&dev->mutex); ovs_mutex_lock(&dpdk_mutex); rte_free(dev->tx_q); list_remove(&dev->list_node); dpdk_mp_put(dev->dpdk_mp); ovs_mutex_unlock(&dpdk_mutex); } static void netdev_dpdk_vhost_destruct(struct netdev *netdev_) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); /* Guest becomes an orphan if still attached. */ if (netdev_dpdk_get_virtio(dev) != NULL) { VLOG_ERR("Removing port '%s' while vhost device still attached.", netdev_->name); VLOG_ERR("To restore connectivity after re-adding of port, VM on socket" " '%s' must be restarted.", dev->vhost_id); } if (rte_vhost_driver_unregister(dev->vhost_id)) { VLOG_ERR("Unable to remove vhost-user socket %s", dev->vhost_id); } else { fatal_signal_remove_file_to_unlink(dev->vhost_id); } ovs_mutex_lock(&dpdk_mutex); rte_free(dev->tx_q); list_remove(&dev->list_node); dpdk_mp_put(dev->dpdk_mp); ovs_mutex_unlock(&dpdk_mutex); } static void netdev_dpdk_dealloc(struct netdev *netdev_) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); rte_free(netdev); } static int netdev_dpdk_get_config(const struct netdev *netdev_, struct smap *args) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); ovs_mutex_lock(&dev->mutex); smap_add_format(args, "configured_rx_queues", "%d", netdev_->n_rxq); smap_add_format(args, "requested_tx_queues", "%d", netdev_->n_txq); smap_add_format(args, "configured_tx_queues", "%d", dev->real_n_txq); ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_get_numa_id(const struct netdev *netdev_) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); return netdev->socket_id; } /* Sets the number of tx queues and rx queues for the dpdk interface. * If the configuration fails, do not try restoring its old configuration * and just returns the error. */ static int netdev_dpdk_set_multiq(struct netdev *netdev_, unsigned int n_txq, unsigned int n_rxq) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int err = 0; int old_rxq, old_txq; if (netdev->up.n_txq == n_txq && netdev->up.n_rxq == n_rxq) { return err; } ovs_mutex_lock(&dpdk_mutex); ovs_mutex_lock(&netdev->mutex); rte_eth_dev_stop(netdev->port_id); old_txq = netdev->up.n_txq; old_rxq = netdev->up.n_rxq; netdev->up.n_txq = n_txq; netdev->up.n_rxq = n_rxq; rte_free(netdev->tx_q); err = dpdk_eth_dev_init(netdev); netdev_dpdk_alloc_txq(netdev, netdev->real_n_txq); if (err) { /* If there has been an error, it means that the requested queues * have not been created. Restore the old numbers. */ netdev->up.n_txq = old_txq; netdev->up.n_rxq = old_rxq; } netdev->txq_needs_locking = netdev->real_n_txq != netdev->up.n_txq; ovs_mutex_unlock(&netdev->mutex); ovs_mutex_unlock(&dpdk_mutex); return err; } static int netdev_dpdk_vhost_cuse_set_multiq(struct netdev *netdev_, unsigned int n_txq, unsigned int n_rxq) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int err = 0; if (netdev->up.n_txq == n_txq && netdev->up.n_rxq == n_rxq) { return err; } ovs_mutex_lock(&dpdk_mutex); ovs_mutex_lock(&netdev->mutex); netdev->up.n_txq = n_txq; netdev->real_n_txq = 1; netdev->up.n_rxq = 1; netdev->txq_needs_locking = netdev->real_n_txq != netdev->up.n_txq; ovs_mutex_unlock(&netdev->mutex); ovs_mutex_unlock(&dpdk_mutex); return err; } static int netdev_dpdk_vhost_set_multiq(struct netdev *netdev_, unsigned int n_txq, unsigned int n_rxq) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int err = 0; if (netdev->up.n_txq == n_txq && netdev->up.n_rxq == n_rxq) { return err; } ovs_mutex_lock(&dpdk_mutex); ovs_mutex_lock(&netdev->mutex); netdev->up.n_txq = n_txq; netdev->up.n_rxq = n_rxq; ovs_mutex_unlock(&netdev->mutex); ovs_mutex_unlock(&dpdk_mutex); return err; } static struct netdev_rxq * netdev_dpdk_rxq_alloc(void) { struct netdev_rxq_dpdk *rx = dpdk_rte_mzalloc(sizeof *rx); return &rx->up; } static struct netdev_rxq_dpdk * netdev_rxq_dpdk_cast(const struct netdev_rxq *rx) { return CONTAINER_OF(rx, struct netdev_rxq_dpdk, up); } static int netdev_dpdk_rxq_construct(struct netdev_rxq *rxq_) { struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq_); struct netdev_dpdk *netdev = netdev_dpdk_cast(rx->up.netdev); ovs_mutex_lock(&netdev->mutex); rx->port_id = netdev->port_id; ovs_mutex_unlock(&netdev->mutex); return 0; } static void netdev_dpdk_rxq_destruct(struct netdev_rxq *rxq_ OVS_UNUSED) { } static void netdev_dpdk_rxq_dealloc(struct netdev_rxq *rxq_) { struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq_); rte_free(rx); } static inline void netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid, struct rte_mbuf **pkts, int cnt) { uint32_t nb_tx = 0; while (nb_tx != cnt) { uint32_t ret; ret = rte_eth_tx_burst(dev->port_id, qid, pkts + nb_tx, cnt - nb_tx); if (!ret) { break; } nb_tx += ret; } if (OVS_UNLIKELY(nb_tx != cnt)) { /* free buffers, which we couldn't transmit, one at a time (each * packet could come from a different mempool) */ int i; for (i = nb_tx; i < cnt; i++) { rte_pktmbuf_free(pkts[i]); } rte_spinlock_lock(&dev->stats_lock); dev->stats.tx_dropped += cnt - nb_tx; rte_spinlock_unlock(&dev->stats_lock); } } static bool is_vhost_running(struct virtio_net *dev) { return (dev != NULL && (dev->flags & VIRTIO_DEV_RUNNING)); } static inline void netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats, struct dp_packet **packets, int count) { int i; struct dp_packet *packet; stats->rx_packets += count; for (i = 0; i < count; i++) { packet = packets[i]; if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) { /* This only protects the following multicast counting from * too short packets, but it does not stop the packet from * further processing. */ stats->rx_errors++; stats->rx_length_errors++; continue; } struct eth_header *eh = (struct eth_header *) dp_packet_data(packet); if (OVS_UNLIKELY(eth_addr_is_multicast(eh->eth_dst))) { stats->multicast++; } stats->rx_bytes += dp_packet_size(packet); } } /* * The receive path for the vhost port is the TX path out from guest. */ static int netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **packets, int *c) { struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq_); struct netdev *netdev = rx->up.netdev; struct netdev_dpdk *vhost_dev = netdev_dpdk_cast(netdev); struct virtio_net *virtio_dev = netdev_dpdk_get_virtio(vhost_dev); int qid = rxq_->queue_id; uint16_t nb_rx = 0; if (OVS_UNLIKELY(!is_vhost_running(virtio_dev) || !(vhost_dev->flags & NETDEV_UP))) { return EAGAIN; } if (rxq_->queue_id >= vhost_dev->real_n_rxq) { return EOPNOTSUPP; } nb_rx = rte_vhost_dequeue_burst(virtio_dev, qid * VIRTIO_QNUM + VIRTIO_TXQ, vhost_dev->dpdk_mp->mp, (struct rte_mbuf **)packets, NETDEV_MAX_BURST); if (!nb_rx) { return EAGAIN; } rte_spinlock_lock(&vhost_dev->stats_lock); netdev_dpdk_vhost_update_rx_counters(&vhost_dev->stats, packets, nb_rx); rte_spinlock_unlock(&vhost_dev->stats_lock); *c = (int) nb_rx; return 0; } static int netdev_dpdk_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **packets, int *c) { struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq_); int nb_rx; nb_rx = rte_eth_rx_burst(rx->port_id, rxq_->queue_id, (struct rte_mbuf **) packets, NETDEV_MAX_BURST); if (!nb_rx) { return EAGAIN; } *c = nb_rx; return 0; } static inline void netdev_dpdk_vhost_update_tx_counters(struct netdev_stats *stats, struct dp_packet **packets, int attempted, int dropped) { int i; int sent = attempted - dropped; stats->tx_packets += sent; stats->tx_dropped += dropped; for (i = 0; i < sent; i++) { stats->tx_bytes += dp_packet_size(packets[i]); } } static void __netdev_dpdk_vhost_send(struct netdev *netdev, int qid, struct dp_packet **pkts, int cnt, bool may_steal) { struct netdev_dpdk *vhost_dev = netdev_dpdk_cast(netdev); struct virtio_net *virtio_dev = netdev_dpdk_get_virtio(vhost_dev); struct rte_mbuf **cur_pkts = (struct rte_mbuf **) pkts; unsigned int total_pkts = cnt; int retries = 0; qid = vhost_dev->tx_q[qid % vhost_dev->real_n_txq].map; if (OVS_UNLIKELY(!is_vhost_running(virtio_dev) || qid < 0 || !(vhost_dev->flags & NETDEV_UP))) { rte_spinlock_lock(&vhost_dev->stats_lock); vhost_dev->stats.tx_dropped+= cnt; rte_spinlock_unlock(&vhost_dev->stats_lock); goto out; } rte_spinlock_lock(&vhost_dev->tx_q[qid].tx_lock); do { int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ; unsigned int tx_pkts; tx_pkts = rte_vhost_enqueue_burst(virtio_dev, vhost_qid, cur_pkts, cnt); if (OVS_LIKELY(tx_pkts)) { /* Packets have been sent.*/ cnt -= tx_pkts; /* Prepare for possible retry.*/ cur_pkts = &cur_pkts[tx_pkts]; } else { /* No packets sent - do not retry.*/ break; } } while (cnt && (retries++ < VHOST_ENQ_RETRY_NUM)); rte_spinlock_unlock(&vhost_dev->tx_q[qid].tx_lock); rte_spinlock_lock(&vhost_dev->stats_lock); netdev_dpdk_vhost_update_tx_counters(&vhost_dev->stats, pkts, total_pkts, cnt); rte_spinlock_unlock(&vhost_dev->stats_lock); out: if (may_steal) { int i; for (i = 0; i < total_pkts; i++) { dp_packet_delete(pkts[i]); } } } /* Tx function. Transmit packets indefinitely */ static void dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet **pkts, int cnt) OVS_NO_THREAD_SAFETY_ANALYSIS { #if !defined(__CHECKER__) && !defined(_WIN32) const size_t PKT_ARRAY_SIZE = cnt; #else /* Sparse or MSVC doesn't like variable length array. */ enum { PKT_ARRAY_SIZE = NETDEV_MAX_BURST }; #endif struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); struct rte_mbuf *mbufs[PKT_ARRAY_SIZE]; int dropped = 0; int newcnt = 0; int i; /* If we are on a non pmd thread we have to use the mempool mutex, because * every non pmd thread shares the same mempool cache */ if (!dpdk_thread_is_pmd()) { ovs_mutex_lock(&nonpmd_mempool_mutex); } for (i = 0; i < cnt; i++) { int size = dp_packet_size(pkts[i]); if (OVS_UNLIKELY(size > dev->max_packet_len)) { VLOG_WARN_RL(&rl, "Too big size %d max_packet_len %d", (int)size , dev->max_packet_len); dropped++; continue; } mbufs[newcnt] = rte_pktmbuf_alloc(dev->dpdk_mp->mp); if (!mbufs[newcnt]) { dropped += cnt - i; break; } /* We have to do a copy for now */ memcpy(rte_pktmbuf_mtod(mbufs[newcnt], void *), dp_packet_data(pkts[i]), size); rte_pktmbuf_data_len(mbufs[newcnt]) = size; rte_pktmbuf_pkt_len(mbufs[newcnt]) = size; newcnt++; } if (OVS_UNLIKELY(dropped)) { rte_spinlock_lock(&dev->stats_lock); dev->stats.tx_dropped += dropped; rte_spinlock_unlock(&dev->stats_lock); } if (dev->type == DPDK_DEV_VHOST) { __netdev_dpdk_vhost_send(netdev, qid, (struct dp_packet **) mbufs, newcnt, true); } else { netdev_dpdk_eth_tx_burst(dev, qid, mbufs, newcnt); } if (!dpdk_thread_is_pmd()) { ovs_mutex_unlock(&nonpmd_mempool_mutex); } } static int netdev_dpdk_vhost_send(struct netdev *netdev, int qid, struct dp_packet **pkts, int cnt, bool may_steal) { if (OVS_UNLIKELY(pkts[0]->source != DPBUF_DPDK)) { int i; dpdk_do_tx_copy(netdev, qid, pkts, cnt); if (may_steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(pkts[i]); } } } else { __netdev_dpdk_vhost_send(netdev, qid, pkts, cnt, may_steal); } return 0; } static inline void netdev_dpdk_send__(struct netdev_dpdk *dev, int qid, struct dp_packet **pkts, int cnt, bool may_steal) { int i; if (OVS_UNLIKELY(dev->txq_needs_locking)) { qid = qid % dev->real_n_txq; rte_spinlock_lock(&dev->tx_q[qid].tx_lock); } if (OVS_UNLIKELY(!may_steal || pkts[0]->source != DPBUF_DPDK)) { struct netdev *netdev = &dev->up; dpdk_do_tx_copy(netdev, qid, pkts, cnt); if (may_steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(pkts[i]); } } } else { int next_tx_idx = 0; int dropped = 0; for (i = 0; i < cnt; i++) { int size = dp_packet_size(pkts[i]); if (OVS_UNLIKELY(size > dev->max_packet_len)) { if (next_tx_idx != i) { netdev_dpdk_eth_tx_burst(dev, qid, (struct rte_mbuf **)&pkts[next_tx_idx], i-next_tx_idx); } VLOG_WARN_RL(&rl, "Too big size %d max_packet_len %d", (int)size , dev->max_packet_len); dp_packet_delete(pkts[i]); dropped++; next_tx_idx = i + 1; } } if (next_tx_idx != cnt) { netdev_dpdk_eth_tx_burst(dev, qid, (struct rte_mbuf **)&pkts[next_tx_idx], cnt-next_tx_idx); } if (OVS_UNLIKELY(dropped)) { rte_spinlock_lock(&dev->stats_lock); dev->stats.tx_dropped += dropped; rte_spinlock_unlock(&dev->stats_lock); } } if (OVS_UNLIKELY(dev->txq_needs_locking)) { rte_spinlock_unlock(&dev->tx_q[qid].tx_lock); } } static int netdev_dpdk_eth_send(struct netdev *netdev, int qid, struct dp_packet **pkts, int cnt, bool may_steal) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); netdev_dpdk_send__(dev, qid, pkts, cnt, may_steal); return 0; } static int netdev_dpdk_set_etheraddr(struct netdev *netdev, const struct eth_addr mac) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ovs_mutex_lock(&dev->mutex); if (!eth_addr_equals(dev->hwaddr, mac)) { dev->hwaddr = mac; netdev_change_seq_changed(netdev); } ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_get_etheraddr(const struct netdev *netdev, struct eth_addr *mac) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ovs_mutex_lock(&dev->mutex); *mac = dev->hwaddr; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_get_mtu(const struct netdev *netdev, int *mtup) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ovs_mutex_lock(&dev->mutex); *mtup = dev->mtu; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_set_mtu(const struct netdev *netdev, int mtu) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); int old_mtu, err; struct dpdk_mp *old_mp; struct dpdk_mp *mp; ovs_mutex_lock(&dpdk_mutex); ovs_mutex_lock(&dev->mutex); if (dev->mtu == mtu) { err = 0; goto out; } mp = dpdk_mp_get(dev->socket_id, dev->mtu); if (!mp) { err = ENOMEM; goto out; } rte_eth_dev_stop(dev->port_id); old_mtu = dev->mtu; old_mp = dev->dpdk_mp; dev->dpdk_mp = mp; dev->mtu = mtu; dev->max_packet_len = MTU_TO_MAX_LEN(dev->mtu); err = dpdk_eth_dev_init(dev); if (err) { dpdk_mp_put(mp); dev->mtu = old_mtu; dev->dpdk_mp = old_mp; dev->max_packet_len = MTU_TO_MAX_LEN(dev->mtu); dpdk_eth_dev_init(dev); goto out; } dpdk_mp_put(old_mp); netdev_change_seq_changed(netdev); out: ovs_mutex_unlock(&dev->mutex); ovs_mutex_unlock(&dpdk_mutex); return err; } static int netdev_dpdk_get_carrier(const struct netdev *netdev_, bool *carrier); static int netdev_dpdk_vhost_get_stats(const struct netdev *netdev, struct netdev_stats *stats) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); ovs_mutex_lock(&dev->mutex); memset(stats, 0, sizeof(*stats)); /* Unsupported Stats */ stats->collisions = UINT64_MAX; stats->rx_crc_errors = UINT64_MAX; stats->rx_fifo_errors = UINT64_MAX; stats->rx_frame_errors = UINT64_MAX; stats->rx_missed_errors = UINT64_MAX; stats->rx_over_errors = UINT64_MAX; stats->tx_aborted_errors = UINT64_MAX; stats->tx_carrier_errors = UINT64_MAX; stats->tx_errors = UINT64_MAX; stats->tx_fifo_errors = UINT64_MAX; stats->tx_heartbeat_errors = UINT64_MAX; stats->tx_window_errors = UINT64_MAX; stats->rx_dropped += UINT64_MAX; rte_spinlock_lock(&dev->stats_lock); /* Supported Stats */ stats->rx_packets += dev->stats.rx_packets; stats->tx_packets += dev->stats.tx_packets; stats->tx_dropped += dev->stats.tx_dropped; stats->multicast = dev->stats.multicast; stats->rx_bytes = dev->stats.rx_bytes; stats->tx_bytes = dev->stats.tx_bytes; stats->rx_errors = dev->stats.rx_errors; stats->rx_length_errors = dev->stats.rx_length_errors; rte_spinlock_unlock(&dev->stats_lock); ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); struct rte_eth_stats rte_stats; bool gg; netdev_dpdk_get_carrier(netdev, &gg); ovs_mutex_lock(&dev->mutex); rte_eth_stats_get(dev->port_id, &rte_stats); memset(stats, 0, sizeof(*stats)); stats->rx_packets = rte_stats.ipackets; stats->tx_packets = rte_stats.opackets; stats->rx_bytes = rte_stats.ibytes; stats->tx_bytes = rte_stats.obytes; /* DPDK counts imissed as errors, but count them here as dropped instead */ stats->rx_errors = rte_stats.ierrors - rte_stats.imissed; stats->tx_errors = rte_stats.oerrors; stats->multicast = rte_stats.imcasts; rte_spinlock_lock(&dev->stats_lock); stats->tx_dropped = dev->stats.tx_dropped; rte_spinlock_unlock(&dev->stats_lock); /* These are the available DPDK counters for packets not received due to * local resource constraints in DPDK and NIC respectively. */ stats->rx_dropped = rte_stats.rx_nombuf + rte_stats.imissed; stats->collisions = UINT64_MAX; stats->rx_length_errors = UINT64_MAX; stats->rx_over_errors = UINT64_MAX; stats->rx_crc_errors = UINT64_MAX; stats->rx_frame_errors = UINT64_MAX; stats->rx_fifo_errors = UINT64_MAX; stats->rx_missed_errors = rte_stats.imissed; stats->tx_aborted_errors = UINT64_MAX; stats->tx_carrier_errors = UINT64_MAX; stats->tx_fifo_errors = UINT64_MAX; stats->tx_heartbeat_errors = UINT64_MAX; stats->tx_window_errors = UINT64_MAX; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_get_features(const struct netdev *netdev_, enum netdev_features *current, enum netdev_features *advertised OVS_UNUSED, enum netdev_features *supported OVS_UNUSED, enum netdev_features *peer OVS_UNUSED) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); struct rte_eth_link link; ovs_mutex_lock(&dev->mutex); link = dev->link; ovs_mutex_unlock(&dev->mutex); if (link.link_duplex == ETH_LINK_AUTONEG_DUPLEX) { if (link.link_speed == ETH_LINK_SPEED_AUTONEG) { *current = NETDEV_F_AUTONEG; } } else if (link.link_duplex == ETH_LINK_HALF_DUPLEX) { if (link.link_speed == ETH_LINK_SPEED_10) { *current = NETDEV_F_10MB_HD; } if (link.link_speed == ETH_LINK_SPEED_100) { *current = NETDEV_F_100MB_HD; } if (link.link_speed == ETH_LINK_SPEED_1000) { *current = NETDEV_F_1GB_HD; } } else if (link.link_duplex == ETH_LINK_FULL_DUPLEX) { if (link.link_speed == ETH_LINK_SPEED_10) { *current = NETDEV_F_10MB_FD; } if (link.link_speed == ETH_LINK_SPEED_100) { *current = NETDEV_F_100MB_FD; } if (link.link_speed == ETH_LINK_SPEED_1000) { *current = NETDEV_F_1GB_FD; } if (link.link_speed == ETH_LINK_SPEED_10000) { *current = NETDEV_F_10GB_FD; } } return 0; } static int netdev_dpdk_get_ifindex(const struct netdev *netdev) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev); int ifindex; ovs_mutex_lock(&dev->mutex); ifindex = dev->port_id; ovs_mutex_unlock(&dev->mutex); return ifindex; } static int netdev_dpdk_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); ovs_mutex_lock(&dev->mutex); check_link_status(dev); *carrier = dev->link.link_status; ovs_mutex_unlock(&dev->mutex); return 0; } static int netdev_dpdk_vhost_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); struct virtio_net *virtio_dev = netdev_dpdk_get_virtio(dev); ovs_mutex_lock(&dev->mutex); if (is_vhost_running(virtio_dev)) { *carrier = 1; } else { *carrier = 0; } ovs_mutex_unlock(&dev->mutex); return 0; } static long long int netdev_dpdk_get_carrier_resets(const struct netdev *netdev_) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); long long int carrier_resets; ovs_mutex_lock(&dev->mutex); carrier_resets = dev->link_reset_cnt; ovs_mutex_unlock(&dev->mutex); return carrier_resets; } static int netdev_dpdk_set_miimon(struct netdev *netdev_ OVS_UNUSED, long long int interval OVS_UNUSED) { return EOPNOTSUPP; } static int netdev_dpdk_update_flags__(struct netdev_dpdk *dev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) OVS_REQUIRES(dev->mutex) { int err; if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) { return EINVAL; } *old_flagsp = dev->flags; dev->flags |= on; dev->flags &= ~off; if (dev->flags == *old_flagsp) { return 0; } if (dev->type == DPDK_DEV_ETH) { if (dev->flags & NETDEV_UP) { err = rte_eth_dev_start(dev->port_id); if (err) return -err; } if (dev->flags & NETDEV_PROMISC) { rte_eth_promiscuous_enable(dev->port_id); } if (!(dev->flags & NETDEV_UP)) { rte_eth_dev_stop(dev->port_id); } } else { /* If DPDK_DEV_VHOST device's NETDEV_UP flag was changed and vhost is * running then change netdev's change_seq to trigger link state * update. */ struct virtio_net *virtio_dev = netdev_dpdk_get_virtio(dev); if ((NETDEV_UP & ((*old_flagsp ^ on) | (*old_flagsp ^ off))) && is_vhost_running(virtio_dev)) { netdev_change_seq_changed(&dev->up); /* Clear statistics if device is getting up. */ if (NETDEV_UP & on) { rte_spinlock_lock(&dev->stats_lock); memset(&dev->stats, 0, sizeof(dev->stats)); rte_spinlock_unlock(&dev->stats_lock); } } } return 0; } static int netdev_dpdk_update_flags(struct netdev *netdev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = netdev_dpdk_update_flags__(netdev, off, on, old_flagsp); ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_dpdk_get_status(const struct netdev *netdev_, struct smap *args) { struct netdev_dpdk *dev = netdev_dpdk_cast(netdev_); struct rte_eth_dev_info dev_info; if (dev->port_id < 0) return ENODEV; ovs_mutex_lock(&dev->mutex); rte_eth_dev_info_get(dev->port_id, &dev_info); ovs_mutex_unlock(&dev->mutex); smap_add_format(args, "driver_name", "%s", dev_info.driver_name); smap_add_format(args, "port_no", "%d", dev->port_id); smap_add_format(args, "numa_id", "%d", rte_eth_dev_socket_id(dev->port_id)); smap_add_format(args, "driver_name", "%s", dev_info.driver_name); smap_add_format(args, "min_rx_bufsize", "%u", dev_info.min_rx_bufsize); smap_add_format(args, "max_rx_pktlen", "%u", dev_info.max_rx_pktlen); smap_add_format(args, "max_rx_queues", "%u", dev_info.max_rx_queues); smap_add_format(args, "max_tx_queues", "%u", dev_info.max_tx_queues); smap_add_format(args, "max_mac_addrs", "%u", dev_info.max_mac_addrs); smap_add_format(args, "max_hash_mac_addrs", "%u", dev_info.max_hash_mac_addrs); smap_add_format(args, "max_vfs", "%u", dev_info.max_vfs); smap_add_format(args, "max_vmdq_pools", "%u", dev_info.max_vmdq_pools); if (dev_info.pci_dev) { smap_add_format(args, "pci-vendor_id", "0x%u", dev_info.pci_dev->id.vendor_id); smap_add_format(args, "pci-device_id", "0x%x", dev_info.pci_dev->id.device_id); } return 0; } static void netdev_dpdk_set_admin_state__(struct netdev_dpdk *dev, bool admin_state) OVS_REQUIRES(dev->mutex) { enum netdev_flags old_flags; if (admin_state) { netdev_dpdk_update_flags__(dev, 0, NETDEV_UP, &old_flags); } else { netdev_dpdk_update_flags__(dev, NETDEV_UP, 0, &old_flags); } } static void netdev_dpdk_set_admin_state(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { bool up; if (!strcasecmp(argv[argc - 1], "up")) { up = true; } else if ( !strcasecmp(argv[argc - 1], "down")) { up = false; } else { unixctl_command_reply_error(conn, "Invalid Admin State"); return; } if (argc > 2) { struct netdev *netdev = netdev_from_name(argv[1]); if (netdev && is_dpdk_class(netdev->netdev_class)) { struct netdev_dpdk *dpdk_dev = netdev_dpdk_cast(netdev); ovs_mutex_lock(&dpdk_dev->mutex); netdev_dpdk_set_admin_state__(dpdk_dev, up); ovs_mutex_unlock(&dpdk_dev->mutex); netdev_close(netdev); } else { unixctl_command_reply_error(conn, "Not a DPDK Interface"); netdev_close(netdev); return; } } else { struct netdev_dpdk *netdev; ovs_mutex_lock(&dpdk_mutex); LIST_FOR_EACH (netdev, list_node, &dpdk_list) { ovs_mutex_lock(&netdev->mutex); netdev_dpdk_set_admin_state__(netdev, up); ovs_mutex_unlock(&netdev->mutex); } ovs_mutex_unlock(&dpdk_mutex); } unixctl_command_reply(conn, "OK"); } /* * Set virtqueue flags so that we do not receive interrupts. */ static void set_irq_status(struct virtio_net *dev) { uint32_t i; uint64_t idx; for (i = 0; i < dev->virt_qp_nb; i++) { idx = i * VIRTIO_QNUM; rte_vhost_enable_guest_notification(dev, idx + VIRTIO_RXQ, 0); rte_vhost_enable_guest_notification(dev, idx + VIRTIO_TXQ, 0); } } /* * Fixes mapping for vhost-user tx queues. Must be called after each * enabling/disabling of queues and real_n_txq modifications. */ static void netdev_dpdk_remap_txqs(struct netdev_dpdk *netdev) OVS_REQUIRES(netdev->mutex) { int *enabled_queues, n_enabled = 0; int i, k, total_txqs = netdev->real_n_txq; enabled_queues = dpdk_rte_mzalloc(total_txqs * sizeof *enabled_queues); for (i = 0; i < total_txqs; i++) { /* Enabled queues always mapped to themselves. */ if (netdev->tx_q[i].map == i) { enabled_queues[n_enabled++] = i; } } if (n_enabled == 0 && total_txqs != 0) { enabled_queues[0] = OVS_VHOST_QUEUE_DISABLED; n_enabled = 1; } k = 0; for (i = 0; i < total_txqs; i++) { if (netdev->tx_q[i].map != i) { netdev->tx_q[i].map = enabled_queues[k]; k = (k + 1) % n_enabled; } } VLOG_DBG("TX queue mapping for %s\n", netdev->vhost_id); for (i = 0; i < total_txqs; i++) { VLOG_DBG("%2d --> %2d", i, netdev->tx_q[i].map); } rte_free(enabled_queues); } static int netdev_dpdk_vhost_set_queues(struct netdev_dpdk *netdev, struct virtio_net *dev) OVS_REQUIRES(netdev->mutex) { uint32_t qp_num; qp_num = dev->virt_qp_nb; if (qp_num > netdev->up.n_rxq) { VLOG_ERR("vHost Device '%s' %"PRIu64" can't be added - " "too many queues %d > %d", dev->ifname, dev->device_fh, qp_num, netdev->up.n_rxq); return -1; } netdev->real_n_rxq = qp_num; netdev->real_n_txq = qp_num; netdev->txq_needs_locking = true; /* Enable TX queue 0 by default if it wasn't disabled. */ if (netdev->tx_q[0].map == OVS_VHOST_QUEUE_MAP_UNKNOWN) { netdev->tx_q[0].map = 0; } netdev_dpdk_remap_txqs(netdev); return 0; } /* * A new virtio-net device is added to a vhost port. */ static int new_device(struct virtio_net *dev) { struct netdev_dpdk *netdev; bool exists = false; ovs_mutex_lock(&dpdk_mutex); /* Add device to the vhost port with the same name as that passed down. */ LIST_FOR_EACH(netdev, list_node, &dpdk_list) { if (strncmp(dev->ifname, netdev->vhost_id, IF_NAME_SZ) == 0) { ovs_mutex_lock(&netdev->mutex); if (netdev_dpdk_vhost_set_queues(netdev, dev)) { ovs_mutex_unlock(&netdev->mutex); ovs_mutex_unlock(&dpdk_mutex); return -1; } ovsrcu_set(&netdev->virtio_dev, dev); exists = true; dev->flags |= VIRTIO_DEV_RUNNING; /* Disable notifications. */ set_irq_status(dev); netdev_change_seq_changed(&netdev->up); ovs_mutex_unlock(&netdev->mutex); break; } } ovs_mutex_unlock(&dpdk_mutex); if (!exists) { VLOG_INFO("vHost Device '%s' %"PRIu64" can't be added - name not " "found", dev->ifname, dev->device_fh); return -1; } VLOG_INFO("vHost Device '%s' %"PRIu64" has been added", dev->ifname, dev->device_fh); return 0; } /* Clears mapping for all available queues of vhost interface. */ static void netdev_dpdk_txq_map_clear(struct netdev_dpdk *dev) OVS_REQUIRES(dev->mutex) { int i; for (i = 0; i < dev->real_n_txq; i++) { dev->tx_q[i].map = OVS_VHOST_QUEUE_MAP_UNKNOWN; } } /* * Remove a virtio-net device from the specific vhost port. Use dev->remove * flag to stop any more packets from being sent or received to/from a VM and * ensure all currently queued packets have been sent/received before removing * the device. */ static void destroy_device(volatile struct virtio_net *dev) { struct netdev_dpdk *vhost_dev; bool exists = false; ovs_mutex_lock(&dpdk_mutex); LIST_FOR_EACH (vhost_dev, list_node, &dpdk_list) { if (netdev_dpdk_get_virtio(vhost_dev) == dev) { ovs_mutex_lock(&vhost_dev->mutex); dev->flags &= ~VIRTIO_DEV_RUNNING; ovsrcu_set(&vhost_dev->virtio_dev, NULL); netdev_dpdk_txq_map_clear(vhost_dev); exists = true; netdev_change_seq_changed(&vhost_dev->up); ovs_mutex_unlock(&vhost_dev->mutex); break; } } ovs_mutex_unlock(&dpdk_mutex); if (exists == true) { /* * Wait for other threads to quiesce after setting the 'virtio_dev' * to NULL, before returning. */ ovsrcu_synchronize(); /* * As call to ovsrcu_synchronize() will end the quiescent state, * put thread back into quiescent state before returning. */ ovsrcu_quiesce_start(); VLOG_INFO("vHost Device '%s' %"PRIu64" has been removed", dev->ifname, dev->device_fh); } else { VLOG_INFO("vHost Device '%s' %"PRIu64" not found", dev->ifname, dev->device_fh); } } static int vring_state_changed(struct virtio_net *dev, uint16_t queue_id, int enable) { struct netdev_dpdk *vhost_dev; bool exists = false; int qid = queue_id / VIRTIO_QNUM; if (queue_id % VIRTIO_QNUM == VIRTIO_TXQ) { return 0; } ovs_mutex_lock(&dpdk_mutex); LIST_FOR_EACH (vhost_dev, list_node, &dpdk_list) { if (strncmp(dev->ifname, vhost_dev->vhost_id, IF_NAME_SZ) == 0) { ovs_mutex_lock(&vhost_dev->mutex); if (enable) { vhost_dev->tx_q[qid].map = qid; } else { vhost_dev->tx_q[qid].map = OVS_VHOST_QUEUE_DISABLED; } netdev_dpdk_remap_txqs(vhost_dev); exists = true; ovs_mutex_unlock(&vhost_dev->mutex); break; } } ovs_mutex_unlock(&dpdk_mutex); if (exists) { VLOG_INFO("State of queue %d ( tx_qid %d ) of vhost device '%s' %" PRIu64" changed to \'%s\'", queue_id, qid, dev->ifname, dev->device_fh, (enable == 1) ? "enabled" : "disabled"); } else { VLOG_INFO("vHost Device '%s' %"PRIu64" not found", dev->ifname, dev->device_fh); return -1; } return 0; } struct virtio_net * netdev_dpdk_get_virtio(const struct netdev_dpdk *dev) { return ovsrcu_get(struct virtio_net *, &dev->virtio_dev); } /* * These callbacks allow virtio-net devices to be added to vhost ports when * configuration has been fully complete. */ static const struct virtio_net_device_ops virtio_net_device_ops = { .new_device = new_device, .destroy_device = destroy_device, .vring_state_changed = vring_state_changed }; static void * start_vhost_loop(void *dummy OVS_UNUSED) { pthread_detach(pthread_self()); /* Put the cuse thread into quiescent state. */ ovsrcu_quiesce_start(); rte_vhost_driver_session_start(); return NULL; } static int dpdk_vhost_class_init(void) { rte_vhost_driver_callback_register(&virtio_net_device_ops); ovs_thread_create("vhost_thread", start_vhost_loop, NULL); return 0; } static int dpdk_vhost_cuse_class_init(void) { int err = -1; /* Register CUSE device to handle IOCTLs. * Unless otherwise specified on the vswitchd command line, cuse_dev_name * is set to vhost-net. */ err = rte_vhost_driver_register(cuse_dev_name); if (err != 0) { VLOG_ERR("CUSE device setup failure."); return -1; } dpdk_vhost_class_init(); return 0; } static int dpdk_vhost_user_class_init(void) { dpdk_vhost_class_init(); return 0; } static void dpdk_common_init(void) { unixctl_command_register("netdev-dpdk/set-admin-state", "[netdev] up|down", 1, 2, netdev_dpdk_set_admin_state, NULL); ovs_thread_create("dpdk_watchdog", dpdk_watchdog, NULL); } /* Client Rings */ static int dpdk_ring_create(const char dev_name[], unsigned int port_no, unsigned int *eth_port_id) { struct dpdk_ring *ivshmem; char ring_name[10]; int err; ivshmem = dpdk_rte_mzalloc(sizeof *ivshmem); if (ivshmem == NULL) { return ENOMEM; } /* XXX: Add support for multiquque ring. */ err = snprintf(ring_name, 10, "%s_tx", dev_name); if (err < 0) { return -err; } /* Create single producer tx ring, netdev does explicit locking. */ ivshmem->cring_tx = rte_ring_create(ring_name, DPDK_RING_SIZE, SOCKET0, RING_F_SP_ENQ); if (ivshmem->cring_tx == NULL) { rte_free(ivshmem); return ENOMEM; } err = snprintf(ring_name, 10, "%s_rx", dev_name); if (err < 0) { return -err; } /* Create single consumer rx ring, netdev does explicit locking. */ ivshmem->cring_rx = rte_ring_create(ring_name, DPDK_RING_SIZE, SOCKET0, RING_F_SC_DEQ); if (ivshmem->cring_rx == NULL) { rte_free(ivshmem); return ENOMEM; } err = rte_eth_from_rings(dev_name, &ivshmem->cring_rx, 1, &ivshmem->cring_tx, 1, SOCKET0); if (err < 0) { rte_free(ivshmem); return ENODEV; } ivshmem->user_port_id = port_no; ivshmem->eth_port_id = rte_eth_dev_count() - 1; list_push_back(&dpdk_ring_list, &ivshmem->list_node); *eth_port_id = ivshmem->eth_port_id; return 0; } static int dpdk_ring_open(const char dev_name[], unsigned int *eth_port_id) OVS_REQUIRES(dpdk_mutex) { struct dpdk_ring *ivshmem; unsigned int port_no; int err = 0; /* Names always start with "dpdkr" */ err = dpdk_dev_parse_name(dev_name, "dpdkr", &port_no); if (err) { return err; } /* look through our list to find the device */ LIST_FOR_EACH (ivshmem, list_node, &dpdk_ring_list) { if (ivshmem->user_port_id == port_no) { VLOG_INFO("Found dpdk ring device %s:", dev_name); *eth_port_id = ivshmem->eth_port_id; /* really all that is needed */ return 0; } } /* Need to create the device rings */ return dpdk_ring_create(dev_name, port_no, eth_port_id); } static int netdev_dpdk_ring_send(struct netdev *netdev_, int qid, struct dp_packet **pkts, int cnt, bool may_steal) { struct netdev_dpdk *netdev = netdev_dpdk_cast(netdev_); unsigned i; /* When using 'dpdkr' and sending to a DPDK ring, we want to ensure that the * rss hash field is clear. This is because the same mbuf may be modified by * the consumer of the ring and return into the datapath without recalculating * the RSS hash. */ for (i = 0; i < cnt; i++) { dp_packet_rss_invalidate(pkts[i]); } netdev_dpdk_send__(netdev, qid, pkts, cnt, may_steal); return 0; } static int netdev_dpdk_ring_construct(struct netdev *netdev) { unsigned int port_no = 0; int err = 0; if (rte_eal_init_ret) { return rte_eal_init_ret; } ovs_mutex_lock(&dpdk_mutex); err = dpdk_ring_open(netdev->name, &port_no); if (err) { goto unlock_dpdk; } err = netdev_dpdk_init(netdev, port_no, DPDK_DEV_ETH); unlock_dpdk: ovs_mutex_unlock(&dpdk_mutex); return err; } #define NETDEV_DPDK_CLASS(NAME, INIT, CONSTRUCT, DESTRUCT, MULTIQ, SEND, \ GET_CARRIER, GET_STATS, GET_FEATURES, GET_STATUS, RXQ_RECV) \ { \ NAME, \ INIT, /* init */ \ NULL, /* netdev_dpdk_run */ \ NULL, /* netdev_dpdk_wait */ \ \ netdev_dpdk_alloc, \ CONSTRUCT, \ DESTRUCT, \ netdev_dpdk_dealloc, \ netdev_dpdk_get_config, \ NULL, /* netdev_dpdk_set_config */ \ NULL, /* get_tunnel_config */ \ NULL, /* build header */ \ NULL, /* push header */ \ NULL, /* pop header */ \ netdev_dpdk_get_numa_id, /* get_numa_id */ \ MULTIQ, /* set_multiq */ \ \ SEND, /* send */ \ NULL, /* send_wait */ \ \ netdev_dpdk_set_etheraddr, \ netdev_dpdk_get_etheraddr, \ netdev_dpdk_get_mtu, \ netdev_dpdk_set_mtu, \ netdev_dpdk_get_ifindex, \ GET_CARRIER, \ netdev_dpdk_get_carrier_resets, \ netdev_dpdk_set_miimon, \ GET_STATS, \ GET_FEATURES, \ NULL, /* set_advertisements */ \ \ NULL, /* set_policing */ \ NULL, /* get_qos_types */ \ NULL, /* get_qos_capabilities */ \ NULL, /* get_qos */ \ NULL, /* set_qos */ \ NULL, /* get_queue */ \ NULL, /* set_queue */ \ NULL, /* delete_queue */ \ NULL, /* get_queue_stats */ \ NULL, /* queue_dump_start */ \ NULL, /* queue_dump_next */ \ NULL, /* queue_dump_done */ \ NULL, /* dump_queue_stats */ \ \ NULL, /* get_in4 */ \ NULL, /* set_in4 */ \ NULL, /* get_in6 */ \ NULL, /* add_router */ \ NULL, /* get_next_hop */ \ GET_STATUS, \ NULL, /* arp_lookup */ \ \ netdev_dpdk_update_flags, \ \ netdev_dpdk_rxq_alloc, \ netdev_dpdk_rxq_construct, \ netdev_dpdk_rxq_destruct, \ netdev_dpdk_rxq_dealloc, \ RXQ_RECV, \ NULL, /* rx_wait */ \ NULL, /* rxq_drain */ \ } static int process_vhost_flags(char *flag, char *default_val, int size, char **argv, char **new_val) { int changed = 0; /* Depending on which version of vhost is in use, process the vhost-specific * flag if it is provided on the vswitchd command line, otherwise resort to * a default value. * * For vhost-user: Process "-vhost_sock_dir" to set the custom location of * the vhost-user socket(s). * For vhost-cuse: Process "-cuse_dev_name" to set the custom name of the * vhost-cuse character device. */ if (!strcmp(argv[1], flag) && (strlen(argv[2]) <= size)) { changed = 1; *new_val = strdup(argv[2]); VLOG_INFO("User-provided %s in use: %s", flag, *new_val); } else { VLOG_INFO("No %s provided - defaulting to %s", flag, default_val); *new_val = default_val; } return changed; } int dpdk_init(int argc, char **argv) { int result; int base = 0; char *pragram_name = argv[0]; if (argc < 2 || strcmp(argv[1], "--dpdk")) return 0; /* Remove the --dpdk argument from arg list.*/ argc--; argv++; /* Reject --user option */ int i; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "--user")) { VLOG_ERR("Can not mix --dpdk and --user options, aborting."); } } #ifdef VHOST_CUSE if (process_vhost_flags("-cuse_dev_name", strdup("vhost-net"), PATH_MAX, argv, &cuse_dev_name)) { #else if (process_vhost_flags("-vhost_sock_dir", strdup(ovs_rundir()), NAME_MAX, argv, &vhost_sock_dir)) { struct stat s; int err; err = stat(vhost_sock_dir, &s); if (err) { VLOG_ERR("vHostUser socket DIR '%s' does not exist.", vhost_sock_dir); return err; } #endif /* Remove the vhost flag configuration parameters from the argument * list, so that the correct elements are passed to the DPDK * initialization function */ argc -= 2; argv += 2; /* Increment by two to bypass the vhost flag arguments */ base = 2; } /* Keep the program name argument as this is needed for call to * rte_eal_init() */ argv[0] = pragram_name; /* Make sure things are initialized ... */ result = rte_eal_init(argc, argv); if (result < 0) { ovs_abort(result, "Cannot init EAL"); } rte_memzone_dump(stdout); rte_eal_init_ret = 0; if (argc > result) { argv[result] = argv[0]; } /* We are called from the main thread here */ RTE_PER_LCORE(_lcore_id) = NON_PMD_CORE_ID; return result + 1 + base; } static const struct netdev_class dpdk_class = NETDEV_DPDK_CLASS( "dpdk", NULL, netdev_dpdk_construct, netdev_dpdk_destruct, netdev_dpdk_set_multiq, netdev_dpdk_eth_send, netdev_dpdk_get_carrier, netdev_dpdk_get_stats, netdev_dpdk_get_features, netdev_dpdk_get_status, netdev_dpdk_rxq_recv); static const struct netdev_class dpdk_ring_class = NETDEV_DPDK_CLASS( "dpdkr", NULL, netdev_dpdk_ring_construct, netdev_dpdk_destruct, netdev_dpdk_set_multiq, netdev_dpdk_ring_send, netdev_dpdk_get_carrier, netdev_dpdk_get_stats, netdev_dpdk_get_features, netdev_dpdk_get_status, netdev_dpdk_rxq_recv); static const struct netdev_class OVS_UNUSED dpdk_vhost_cuse_class = NETDEV_DPDK_CLASS( "dpdkvhostcuse", dpdk_vhost_cuse_class_init, netdev_dpdk_vhost_cuse_construct, netdev_dpdk_vhost_destruct, netdev_dpdk_vhost_cuse_set_multiq, netdev_dpdk_vhost_send, netdev_dpdk_vhost_get_carrier, netdev_dpdk_vhost_get_stats, NULL, NULL, netdev_dpdk_vhost_rxq_recv); static const struct netdev_class OVS_UNUSED dpdk_vhost_user_class = NETDEV_DPDK_CLASS( "dpdkvhostuser", dpdk_vhost_user_class_init, netdev_dpdk_vhost_user_construct, netdev_dpdk_vhost_destruct, netdev_dpdk_vhost_set_multiq, netdev_dpdk_vhost_send, netdev_dpdk_vhost_get_carrier, netdev_dpdk_vhost_get_stats, NULL, NULL, netdev_dpdk_vhost_rxq_recv); void netdev_dpdk_register(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (rte_eal_init_ret) { return; } if (ovsthread_once_start(&once)) { dpdk_common_init(); netdev_register_provider(&dpdk_class); netdev_register_provider(&dpdk_ring_class); #ifdef VHOST_CUSE netdev_register_provider(&dpdk_vhost_cuse_class); #else netdev_register_provider(&dpdk_vhost_user_class); #endif ovsthread_once_done(&once); } } int pmd_thread_setaffinity_cpu(unsigned cpu) { cpu_set_t cpuset; int err; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (err) { VLOG_ERR("Thread affinity error %d",err); return err; } /* NON_PMD_CORE_ID is reserved for use by non pmd threads. */ ovs_assert(cpu != NON_PMD_CORE_ID); RTE_PER_LCORE(_lcore_id) = cpu; return 0; } static bool dpdk_thread_is_pmd(void) { return rte_lcore_id() != NON_PMD_CORE_ID; } openvswitch-2.5.9/lib/PaxHeaders.82075/compiler.h0000644000000000000000000000013213534540071016375 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.677852659 openvswitch-2.5.9/lib/compiler.h0000644000175000017500000000250313534540071020063 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef COMPILER_H #define COMPILER_H 1 #include "openvswitch/compiler.h" #if __GNUC__ && !__CHECKER__ #define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0))) #define MALLOC_LIKE __attribute__((__malloc__)) #define ALWAYS_INLINE __attribute__((always_inline)) #define SENTINEL(N) __attribute__((sentinel(N))) #else #define STRFTIME_FORMAT(FMT) #define MALLOC_LIKE #define ALWAYS_INLINE #define SENTINEL(N) #endif /* Output a message (not an error) while compiling without failing the * compilation process */ #if HAVE_PRAGMA_MESSAGE #define DO_PRAGMA(x) _Pragma(#x) #define BUILD_MESSAGE(x) \ DO_PRAGMA(message(x)) #else #define BUILD_MESSAGE(x) #endif #endif /* compiler.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/signals.c0000644000000000000000000000013113534540071016215 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.965854783 openvswitch-2.5.9/lib/signals.c0000644000175000017500000000427313534540071017712 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "signals.h" #include #include #include #include #include #include "poll-loop.h" #include "socket-util.h" #include "type-props.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(signals); #if defined(_NSIG) #define N_SIGNALS _NSIG #elif defined(NSIG) #define N_SIGNALS NSIG #else /* We could try harder to get the maximum signal number, but in practice we * only care about SIGHUP, which is normally signal 1 anyway. */ #define N_SIGNALS 32 #endif /* Returns the name of signal 'signum' as a string. The return value is either * a statically allocated constant string or the 'bufsize'-byte buffer * 'namebuf'. 'bufsize' should be at least SIGNAL_NAME_BUFSIZE. * * The string is probably a (possibly multi-word) description of the signal * (e.g. "Hangup") instead of just the stringified version of the macro * (e.g. "SIGHUP"). */ const char * signal_name(int signum, char *namebuf, size_t bufsize) { #if HAVE_DECL_SYS_SIGLIST if (signum >= 0 && signum < N_SIGNALS) { const char *name = sys_siglist[signum]; if (name) { return name; } } #endif snprintf(namebuf, bufsize, "signal %d", signum); return namebuf; } void xsigaction(int signum, const struct sigaction *new, struct sigaction *old) { if (sigaction(signum, new, old)) { char namebuf[SIGNAL_NAME_BUFSIZE]; VLOG_FATAL("sigaction(%s) failed (%s)", signal_name(signum, namebuf, sizeof namebuf), ovs_strerror(errno)); } } openvswitch-2.5.9/lib/PaxHeaders.82075/rtnetlink.h0000644000000000000000000000013213534540071016575 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.989854959 openvswitch-2.5.9/lib/rtnetlink.h0000644000175000017500000000474313534540071020273 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RTNETLINK_LINK_H #define RTNETLINK_LINK_H 1 #include #include #include #include "openvswitch/types.h" struct ofpbuf; struct nln_notifier; /* These functions are Linux specific, so they should be used directly only by * Linux-specific code. */ /* A digested version of an rtnetlink_link message sent down by the kernel to * indicate that a network device's status (link or address) has been changed. */ struct rtnetlink_change { /* Copied from struct nlmsghdr. */ int nlmsg_type; /* e.g. RTM_NEWLINK, RTM_DELLINK. */ /* Common attributes. */ int if_index; /* Index of network device. */ const char *ifname; /* Name of network device. */ /* Network device link status. */ int master_ifindex; /* Ifindex of datapath master (0 if none). */ int mtu; /* Current MTU. */ struct eth_addr mac; unsigned int ifi_flags; /* Flags of network device. */ /* Network device address status. */ /* xxx To be added when needed. */ }; /* Function called to report that a netdev has changed. 'change' describes the * specific change. It may be null if the buffer of change information * overflowed, in which case the function must assume that every device may * have changed. 'aux' is as specified in the call to * rtnetlink_notifier_register(). */ typedef void rtnetlink_notify_func(const struct rtnetlink_change *change, void *aux); bool rtnetlink_type_is_rtnlgrp_link(uint16_t type); bool rtnetlink_type_is_rtnlgrp_addr(uint16_t type); bool rtnetlink_parse(struct ofpbuf *buf, struct rtnetlink_change *change); struct nln_notifier * rtnetlink_notifier_create(rtnetlink_notify_func *, void *aux); void rtnetlink_notifier_destroy(struct nln_notifier *); void rtnetlink_run(void); void rtnetlink_wait(void); #endif /* rtnetlink.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/rconn.h0000644000000000000000000000013213534540071015702 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.861854016 openvswitch-2.5.9/lib/rconn.h0000644000175000017500000001013413534540071017367 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RCONN_H #define RCONN_H 1 #include #include #include #include "openvswitch/types.h" #include "ovs-thread.h" /* A wrapper around vconn that provides queuing and optionally reliability. * * An rconn maintains a message transmission queue of bounded length specified * by the caller. The rconn does not guarantee reliable delivery of * queued messages: all queued messages are dropped when reconnection becomes * necessary. * * An rconn optionally provides reliable communication, in this sense: the * rconn will re-connect, with exponential backoff, when the underlying vconn * disconnects. * * * Thread-safety * ============= * * Fully thread-safe. */ struct vconn; struct rconn_packet_counter; struct rconn *rconn_create(int inactivity_probe_interval, int max_backoff, uint8_t dscp, uint32_t allowed_versions); void rconn_set_dscp(struct rconn *rc, uint8_t dscp); uint32_t rconn_get_allowed_versions(const struct rconn *); uint8_t rconn_get_dscp(const struct rconn *rc); void rconn_set_max_backoff(struct rconn *, int max_backoff); int rconn_get_max_backoff(const struct rconn *); void rconn_set_probe_interval(struct rconn *, int inactivity_probe_interval); int rconn_get_probe_interval(const struct rconn *); void rconn_connect(struct rconn *, const char *target, const char *name); void rconn_connect_unreliably(struct rconn *, struct vconn *, const char *name); void rconn_reconnect(struct rconn *); void rconn_disconnect(struct rconn *); void rconn_destroy(struct rconn *); void rconn_run(struct rconn *); void rconn_run_wait(struct rconn *); struct ofpbuf *rconn_recv(struct rconn *); void rconn_recv_wait(struct rconn *); int rconn_send(struct rconn *, struct ofpbuf *, struct rconn_packet_counter *); int rconn_send_with_limit(struct rconn *, struct ofpbuf *, struct rconn_packet_counter *, int queue_limit); void rconn_add_monitor(struct rconn *, struct vconn *); const char *rconn_get_name(const struct rconn *); void rconn_set_name(struct rconn *, const char *new_name); const char *rconn_get_target(const struct rconn *); bool rconn_is_alive(const struct rconn *); bool rconn_is_connected(const struct rconn *); bool rconn_is_admitted(const struct rconn *); int rconn_failure_duration(const struct rconn *); int rconn_get_version(const struct rconn *); const char *rconn_get_state(const struct rconn *); time_t rconn_get_last_connection(const struct rconn *); time_t rconn_get_last_disconnect(const struct rconn *); unsigned int rconn_get_connection_seqno(const struct rconn *); int rconn_get_last_error(const struct rconn *); unsigned int rconn_count_txqlen(const struct rconn *); /* Counts packets and bytes queued into an rconn by a given source. */ struct rconn_packet_counter { struct ovs_mutex mutex; unsigned int n_packets OVS_GUARDED; /* Number of packets queued. */ unsigned int n_bytes OVS_GUARDED; /* Number of bytes queued. */ int ref_cnt OVS_GUARDED; /* Number of owners. */ }; struct rconn_packet_counter *rconn_packet_counter_create(void); void rconn_packet_counter_destroy(struct rconn_packet_counter *); void rconn_packet_counter_inc(struct rconn_packet_counter *, unsigned n_bytes); void rconn_packet_counter_dec(struct rconn_packet_counter *, unsigned n_bytes); unsigned int rconn_packet_counter_n_packets( const struct rconn_packet_counter *); unsigned int rconn_packet_counter_n_bytes(const struct rconn_packet_counter *); #endif /* rconn.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stream-fd.c0000644000000000000000000000013213534540071016440 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.889854223 openvswitch-2.5.9/lib/stream-fd.c0000644000175000017500000001701413534540071020131 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream-fd.h" #include #include #include #include #include #include #include #include "fatal-signal.h" #include "poll-loop.h" #include "socket-util.h" #include "util.h" #include "stream-provider.h" #include "stream.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stream_fd); /* Active file descriptor stream. */ struct stream_fd { struct stream stream; int fd; int fd_type; }; static const struct stream_class stream_fd_class; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); static void maybe_unlink_and_free(char *path); /* Creates a new stream named 'name' that will send and receive data on 'fd' * and stores a pointer to the stream in '*streamp'. Initial connection status * 'connect_status' is interpreted as described for stream_init(). 'fd_type' * tells whether the socket is TCP or Unix domain socket. * * Returns 0 if successful, otherwise a positive errno value. (The current * implementation never fails.) */ int new_fd_stream(const char *name, int fd, int connect_status, int fd_type, struct stream **streamp) { struct stream_fd *s; s = xmalloc(sizeof *s); stream_init(&s->stream, &stream_fd_class, connect_status, name); s->fd = fd; s->fd_type = fd_type; *streamp = &s->stream; return 0; } static struct stream_fd * stream_fd_cast(struct stream *stream) { stream_assert_class(stream, &stream_fd_class); return CONTAINER_OF(stream, struct stream_fd, stream); } static void fd_close(struct stream *stream) { struct stream_fd *s = stream_fd_cast(stream); closesocket(s->fd); free(s); } static int fd_connect(struct stream *stream) { struct stream_fd *s = stream_fd_cast(stream); int retval = check_connection_completion(s->fd); if (retval == 0 && s->fd_type == AF_INET) { setsockopt_tcp_nodelay(s->fd); } return retval; } static ssize_t fd_recv(struct stream *stream, void *buffer, size_t n) { struct stream_fd *s = stream_fd_cast(stream); ssize_t retval; int error; retval = recv(s->fd, buffer, n, 0); if (retval < 0) { error = sock_errno(); #ifdef _WIN32 if (error == WSAEWOULDBLOCK) { error = EAGAIN; } #endif if (error != EAGAIN) { VLOG_DBG_RL(&rl, "recv: %s", sock_strerror(error)); } return -error; } return retval; } static ssize_t fd_send(struct stream *stream, const void *buffer, size_t n) { struct stream_fd *s = stream_fd_cast(stream); ssize_t retval; int error; retval = send(s->fd, buffer, n, 0); if (retval < 0) { error = sock_errno(); #ifdef _WIN32 if (error == WSAEWOULDBLOCK) { error = EAGAIN; } #endif if (error != EAGAIN) { VLOG_DBG_RL(&rl, "send: %s", sock_strerror(error)); } return -error; } return (retval > 0 ? retval : -EAGAIN); } static void fd_wait(struct stream *stream, enum stream_wait_type wait) { struct stream_fd *s = stream_fd_cast(stream); switch (wait) { case STREAM_CONNECT: case STREAM_SEND: poll_fd_wait(s->fd, POLLOUT); break; case STREAM_RECV: poll_fd_wait(s->fd, POLLIN); break; default: OVS_NOT_REACHED(); } } static const struct stream_class stream_fd_class = { "fd", /* name */ false, /* needs_probes */ NULL, /* open */ fd_close, /* close */ fd_connect, /* connect */ fd_recv, /* recv */ fd_send, /* send */ NULL, /* run */ NULL, /* run_wait */ fd_wait, /* wait */ }; /* Passive file descriptor stream. */ struct fd_pstream { struct pstream pstream; int fd; int (*accept_cb)(int fd, const struct sockaddr_storage *, size_t ss_len, struct stream **); char *unlink_path; }; static const struct pstream_class fd_pstream_class; static struct fd_pstream * fd_pstream_cast(struct pstream *pstream) { pstream_assert_class(pstream, &fd_pstream_class); return CONTAINER_OF(pstream, struct fd_pstream, pstream); } /* Creates a new pstream named 'name' that will accept new socket connections * on 'fd' and stores a pointer to the stream in '*pstreamp'. * * When a connection has been accepted, 'accept_cb' will be called with the new * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'. * accept_cb must return 0 if the connection is successful, in which case it * must initialize '*streamp' to the new stream, or a positive errno value on * error. In either case accept_cb takes ownership of the 'fd' passed in. * * When '*pstreamp' is closed, then 'unlink_path' (if nonnull) will be passed * to fatal_signal_unlink_file_now() and freed with free(). * * Returns 0 if successful, otherwise a positive errno value. (The current * implementation never fails.) */ int new_fd_pstream(const char *name, int fd, int (*accept_cb)(int fd, const struct sockaddr_storage *ss, size_t ss_len, struct stream **streamp), char *unlink_path, struct pstream **pstreamp) { struct fd_pstream *ps = xmalloc(sizeof *ps); pstream_init(&ps->pstream, &fd_pstream_class, name); ps->fd = fd; ps->accept_cb = accept_cb; ps->unlink_path = unlink_path; *pstreamp = &ps->pstream; return 0; } static void pfd_close(struct pstream *pstream) { struct fd_pstream *ps = fd_pstream_cast(pstream); closesocket(ps->fd); maybe_unlink_and_free(ps->unlink_path); free(ps); } static int pfd_accept(struct pstream *pstream, struct stream **new_streamp) { struct fd_pstream *ps = fd_pstream_cast(pstream); struct sockaddr_storage ss; socklen_t ss_len = sizeof ss; int new_fd; int retval; new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len); if (new_fd < 0) { retval = sock_errno(); #ifdef _WIN32 if (retval == WSAEWOULDBLOCK) { retval = EAGAIN; } #endif if (retval != EAGAIN) { VLOG_DBG_RL(&rl, "accept: %s", sock_strerror(retval)); } return retval; } retval = set_nonblocking(new_fd); if (retval) { closesocket(new_fd); return retval; } return ps->accept_cb(new_fd, &ss, ss_len, new_streamp); } static void pfd_wait(struct pstream *pstream) { struct fd_pstream *ps = fd_pstream_cast(pstream); poll_fd_wait(ps->fd, POLLIN); } static const struct pstream_class fd_pstream_class = { "pstream", false, NULL, pfd_close, pfd_accept, pfd_wait, }; /* Helper functions. */ static void maybe_unlink_and_free(char *path) { if (path) { fatal_signal_unlink_file_now(path); free(path); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-pthreads.h0000644000000000000000000000013213534540071020454 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.821853721 openvswitch-2.5.9/lib/ovs-atomic-pthreads.h0000644000175000017500000001025113534540071022141 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives using pthreads. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #include "ovs-atomic-locked.h" #define OVS_ATOMIC_PTHREADS_IMPL 1 #define ATOMIC(TYPE) TYPE #define ATOMIC_BOOL_LOCK_FREE 0 #define ATOMIC_CHAR_LOCK_FREE 0 #define ATOMIC_SHORT_LOCK_FREE 0 #define ATOMIC_INT_LOCK_FREE 0 #define ATOMIC_LONG_LOCK_FREE 0 #define ATOMIC_LLONG_LOCK_FREE 0 #define ATOMIC_POINTER_LOCK_FREE 0 typedef enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; #define ATOMIC_VAR_INIT(VALUE) (VALUE) #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) static inline void atomic_thread_fence(memory_order order OVS_UNUSED) { /* Nothing to do. */ } static inline void atomic_signal_fence(memory_order order OVS_UNUSED) { /* Nothing to do. */ } #define atomic_is_lock_free(OBJ) false #define atomic_store(DST, SRC) atomic_store_locked(DST, SRC) #define atomic_store_explicit(DST, SRC, ORDER) \ ((void) (ORDER), atomic_store(DST, SRC)) #define atomic_read(SRC, DST) atomic_read_locked(SRC, DST) #define atomic_read_explicit(SRC, DST, ORDER) \ ((void) (ORDER), atomic_read(SRC, DST)) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_locked(DST, EXP, SRC) #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \ ((void) (ORD1), (void) (ORD2), \ atomic_compare_exchange_strong(DST, EXP, SRC)) #define atomic_compare_exchange_weak \ atomic_compare_exchange_strong #define atomic_compare_exchange_weak_explicit \ atomic_compare_exchange_strong_explicit #define atomic_add(RMW, ARG, ORIG) atomic_op_locked(RMW, add, ARG, ORIG) #define atomic_sub(RMW, ARG, ORIG) atomic_op_locked(RMW, sub, ARG, ORIG) #define atomic_or( RMW, ARG, ORIG) atomic_op_locked(RMW, or, ARG, ORIG) #define atomic_xor(RMW, ARG, ORIG) atomic_op_locked(RMW, xor, ARG, ORIG) #define atomic_and(RMW, ARG, ORIG) atomic_op_locked(RMW, and, ARG, ORIG) #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ ((void) (ORDER), atomic_add(RMW, ARG, ORIG)) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ ((void) (ORDER), atomic_sub(RMW, ARG, ORIG)) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ ((void) (ORDER), atomic_or(RMW, ARG, ORIG)) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ ((void) (ORDER), atomic_xor(RMW, ARG, ORIG)) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ ((void) (ORDER), atomic_and(RMW, ARG, ORIG)) /* atomic_flag */ typedef struct { bool b; } atomic_flag; #define ATOMIC_FLAG_INIT { false } static inline bool atomic_flag_test_and_set(volatile atomic_flag *flag_) { atomic_flag *flag = CONST_CAST(atomic_flag *, flag_); bool old_value; atomic_lock__(flag); old_value = flag->b; flag->b = true; atomic_unlock__(flag); return old_value; } static inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag *flag, memory_order order OVS_UNUSED) { return atomic_flag_test_and_set(flag); } static inline void atomic_flag_clear(volatile atomic_flag *flag_) { atomic_flag *flag = CONST_CAST(atomic_flag *, flag_); atomic_lock__(flag); flag->b = false; atomic_unlock__(flag); } static inline void atomic_flag_clear_explicit(volatile atomic_flag *flag, memory_order order OVS_UNUSED) { atomic_flag_clear(flag); } openvswitch-2.5.9/lib/PaxHeaders.82075/libopenvswitch.pc.in0000644000000000000000000000013113534540071020402 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.641852394 openvswitch-2.5.9/lib/libopenvswitch.pc.in0000644000175000017500000000035213534540071022071 0ustar00jpettitjpettit00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libopenvswitch Description: Open vSwitch library Version: @VERSION@ Libs: -L${libdir} -lopenvswitch Libs.private: @LIBS@ Cflags: -I${includedir} openvswitch-2.5.9/lib/PaxHeaders.82075/fatal-signal.h0000644000000000000000000000013213534540071017125 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.721852984 openvswitch-2.5.9/lib/fatal-signal.h0000644000175000017500000000320013534540071020606 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FATAL_SIGNAL_H #define FATAL_SIGNAL_H 1 #ifndef _WIN32 #include #endif #include /* Basic interface. */ void fatal_signal_init(void); void fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux), void *aux, bool run_at_exit); void fatal_signal_fork(void); void fatal_signal_run(void); void fatal_signal_wait(void); void fatal_ignore_sigpipe(void); void fatal_signal_atexit_handler(void); /* Convenience functions for unlinking files upon termination. * * These functions also unlink the files upon normal process termination via * exit(). */ void fatal_signal_add_file_to_unlink(const char *); void fatal_signal_remove_file_to_unlink(const char *); int fatal_signal_unlink_file_now(const char *); /* Interface for other code that catches one of our signals and needs to pass * it through. */ void fatal_signal_handler(int sig_nr); #ifndef _WIN32 void fatal_signal_block(sigset_t *prev_mask); #endif #endif /* fatal-signal.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/tun-metadata.c0000644000000000000000000000013213534540071017142 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.921854458 openvswitch-2.5.9/lib/tun-metadata.c0000644000175000017500000007731513534540071020645 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "bitmap.h" #include "compiler.h" #include "hmap.h" #include "match.h" #include "nx-match.h" #include "odp-netlink.h" #include "ofp-util.h" #include "ovs-thread.h" #include "ovs-rcu.h" #include "packets.h" #include "tun-metadata.h" struct tun_meta_entry { struct hmap_node node; /* In struct tun_table's key_hmap. */ uint32_t key; /* (class << 8) | type. */ struct tun_metadata_loc loc; bool valid; /* True if allocated to a class and type. */ }; /* Maps from TLV option class+type to positions in a struct tun_metadata's * 'opts' array. */ struct tun_table { /* TUN_METADATA is stored in element . */ struct tun_meta_entry entries[TUN_METADATA_NUM_OPTS]; /* Each bit represents 4 bytes of space, 0-bits are free space. */ unsigned long alloc_map[BITMAP_N_LONGS(TUN_METADATA_TOT_OPT_SIZE / 4)]; /* The valid elements in entries[], indexed by class+type. */ struct hmap key_hmap; }; BUILD_ASSERT_DECL(TUN_METADATA_TOT_OPT_SIZE % 4 == 0); static struct ovs_mutex tab_mutex = OVS_MUTEX_INITIALIZER; static OVSRCU_TYPE(struct tun_table *) metadata_tab; static enum ofperr tun_metadata_add_entry(struct tun_table *map, uint8_t idx, uint16_t opt_class, uint8_t type, uint8_t len) OVS_REQUIRES(tab_mutex); static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx) OVS_REQUIRES(tab_mutex); static void memcpy_to_metadata(struct tun_metadata *dst, const void *src, const struct tun_metadata_loc *, unsigned int idx); static void memcpy_from_metadata(void *dst, const struct tun_metadata *src, const struct tun_metadata_loc *); static uint32_t tun_meta_key(ovs_be16 class, uint8_t type) { return (OVS_FORCE uint16_t)class << 8 | type; } static ovs_be16 tun_key_class(uint32_t key) { return (OVS_FORCE ovs_be16)(key >> 8); } static uint8_t tun_key_type(uint32_t key) { return key & 0xff; } /* Returns a newly allocated tun_table. If 'old_map' is nonnull then the new * tun_table is a deep copy of the old one. */ static struct tun_table * table_alloc(const struct tun_table *old_map) OVS_REQUIRES(tab_mutex) { struct tun_table *new_map; new_map = xzalloc(sizeof *new_map); if (old_map) { struct tun_meta_entry *entry; *new_map = *old_map; hmap_init(&new_map->key_hmap); HMAP_FOR_EACH (entry, node, &old_map->key_hmap) { struct tun_meta_entry *new_entry; struct tun_metadata_loc_chain *chain; new_entry = &new_map->entries[entry - old_map->entries]; hmap_insert(&new_map->key_hmap, &new_entry->node, entry->node.hash); chain = &new_entry->loc.c; while (chain->next) { chain->next = xmemdup(chain->next, sizeof *chain->next); chain = chain->next; } } } else { hmap_init(&new_map->key_hmap); } return new_map; } /* Frees 'map' and all the memory it owns. */ static void table_free(struct tun_table *map) OVS_REQUIRES(tab_mutex) { struct tun_meta_entry *entry; if (!map) { return; } HMAP_FOR_EACH (entry, node, &map->key_hmap) { tun_metadata_del_entry(map, entry - map->entries); } hmap_destroy(&map->key_hmap); free(map); } /* Creates a global tunnel metadata mapping table, if none already exists. */ void tun_metadata_init(void) { ovs_mutex_lock(&tab_mutex); if (!ovsrcu_get_protected(struct tun_table *, &metadata_tab)) { ovsrcu_set(&metadata_tab, table_alloc(NULL)); } ovs_mutex_unlock(&tab_mutex); } enum ofperr tun_metadata_table_mod(struct ofputil_tlv_table_mod *ttm) { struct tun_table *old_map, *new_map; struct ofputil_tlv_map *ofp_map; enum ofperr err = 0; ovs_mutex_lock(&tab_mutex); old_map = ovsrcu_get_protected(struct tun_table *, &metadata_tab); switch (ttm->command) { case NXTTMC_ADD: new_map = table_alloc(old_map); LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) { err = tun_metadata_add_entry(new_map, ofp_map->index, ofp_map->option_class, ofp_map->option_type, ofp_map->option_len); if (err) { table_free(new_map); goto out; } } break; case NXTTMC_DELETE: new_map = table_alloc(old_map); LIST_FOR_EACH (ofp_map, list_node, &ttm->mappings) { tun_metadata_del_entry(new_map, ofp_map->index); } break; case NXTTMC_CLEAR: new_map = table_alloc(NULL); break; default: OVS_NOT_REACHED(); } ovsrcu_set(&metadata_tab, new_map); ovsrcu_postpone(table_free, old_map); out: ovs_mutex_unlock(&tab_mutex); return err; } void tun_metadata_table_request(struct ofputil_tlv_table_reply *ttr) { struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab); int i; ttr->max_option_space = TUN_METADATA_TOT_OPT_SIZE; ttr->max_fields = TUN_METADATA_NUM_OPTS; list_init(&ttr->mappings); for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) { struct tun_meta_entry *entry = &map->entries[i]; struct ofputil_tlv_map *map; if (!entry->valid) { continue; } map = xmalloc(sizeof *map); map->option_class = ntohs(tun_key_class(entry->key)); map->option_type = tun_key_type(entry->key); map->option_len = entry->loc.len; map->index = i; list_push_back(&ttr->mappings, &map->list_node); } } /* Copies the value of field 'mf' from 'tnl' (which must be in non-UDPIF format) * into 'value'. * * 'mf' must be an MFF_TUN_METADATA* field. * * This uses the global tunnel metadata mapping table created by * tun_metadata_init(). If no such table has been created or if 'mf' hasn't * been allocated in it yet, this just zeros 'value'. */ void tun_metadata_read(const struct flow_tnl *tnl, const struct mf_field *mf, union mf_value *value) { struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab); unsigned int idx = mf->id - MFF_TUN_METADATA0; struct tun_metadata_loc *loc; if (!map) { memset(value->tun_metadata, 0, mf->n_bytes); return; } loc = &map->entries[idx].loc; memset(value->tun_metadata, 0, mf->n_bytes - loc->len); memcpy_from_metadata(value->tun_metadata + mf->n_bytes - loc->len, &tnl->metadata, loc); } /* Copies 'value' into field 'mf' in 'tnl' (in non-UDPIF format). * * 'mf' must be an MFF_TUN_METADATA* field. * * This uses the global tunnel metadata mapping table created by * tun_metadata_init(). If no such table has been created or if 'mf' hasn't * been allocated in it yet, this function does nothing. */ void tun_metadata_write(struct flow_tnl *tnl, const struct mf_field *mf, const union mf_value *value) { struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab); unsigned int idx = mf->id - MFF_TUN_METADATA0; struct tun_metadata_loc *loc; if (!map || !map->entries[idx].valid) { return; } loc = &map->entries[idx].loc; memcpy_to_metadata(&tnl->metadata, value->tun_metadata + mf->n_bytes - loc->len, loc, idx); } static const struct tun_metadata_loc * metadata_loc_from_match(struct tun_table *map, struct match *match, const char *name, unsigned int idx, unsigned int field_len, bool masked, char **err_str) { ovs_assert(idx < TUN_METADATA_NUM_OPTS); if (err_str) { *err_str = NULL; } if (map) { if (map->entries[idx].valid) { return &map->entries[idx].loc; } else { return NULL; } } if (match->tun_md.alloc_offset + field_len > TUN_METADATA_TOT_OPT_SIZE) { if (err_str) { *err_str = xasprintf("field %s exceeds maximum size for tunnel " "metadata (used %d, max %d)", name, match->tun_md.alloc_offset + field_len, TUN_METADATA_TOT_OPT_SIZE); } return NULL; } if (ULLONG_GET(match->wc.masks.tunnel.metadata.present.map, idx)) { if (err_str) { *err_str = xasprintf("field %s set multiple times", name); } return NULL; } match->tun_md.entry[idx].loc.len = field_len; match->tun_md.entry[idx].loc.c.offset = match->tun_md.alloc_offset; match->tun_md.entry[idx].loc.c.len = field_len; match->tun_md.entry[idx].loc.c.next = NULL; match->tun_md.entry[idx].masked = masked; match->tun_md.alloc_offset += field_len; match->tun_md.valid = true; return &match->tun_md.entry[idx].loc; } /* Makes 'match' match 'value'/'mask' on field 'mf'. * * 'mf' must be an MFF_TUN_METADATA* field. 'match' must be in non-UDPIF format. * * If there is global tunnel metadata matching table, this function is * effective only if there is already a mapping for 'mf'. Otherwise, the * metadata mapping table integrated into 'match' is used, adding 'mf' to its * mapping table if it isn't already mapped (and if there is room). If 'mf' * isn't or can't be mapped, this function returns without modifying 'match'. * * 'value' may be NULL; if so, then 'mf' is made to match on an all-zeros * value. * * 'mask' may be NULL; if so, then 'mf' is made exact-match. * * If non-NULL, 'err_str' returns a malloc'ed string describing any errors * with the request or NULL if there is no error. The caller is reponsible * for freeing the string. */ void tun_metadata_set_match(const struct mf_field *mf, const union mf_value *value, const union mf_value *mask, struct match *match, char **err_str) { struct tun_table *map = ovsrcu_get(struct tun_table *, &metadata_tab); const struct tun_metadata_loc *loc; unsigned int idx = mf->id - MFF_TUN_METADATA0; unsigned int field_len; bool is_masked; unsigned int data_offset; union mf_value data; ovs_assert(!(match->flow.tunnel.flags & FLOW_TNL_F_UDPIF)); field_len = mf_field_len(mf, value, mask, &is_masked); loc = metadata_loc_from_match(map, match, mf->name, idx, field_len, is_masked, err_str); if (!loc) { return; } data_offset = mf->n_bytes - loc->len; if (!value) { memset(data.tun_metadata, 0, loc->len); } else if (!mask) { memcpy(data.tun_metadata, value->tun_metadata + data_offset, loc->len); } else { int i; for (i = 0; i < loc->len; i++) { data.tun_metadata[i] = value->tun_metadata[data_offset + i] & mask->tun_metadata[data_offset + i]; } } memcpy_to_metadata(&match->flow.tunnel.metadata, data.tun_metadata, loc, idx); if (!value) { memset(data.tun_metadata, 0, loc->len); } else if (!mask) { memset(data.tun_metadata, 0xff, loc->len); } else { memcpy(data.tun_metadata, mask->tun_metadata + data_offset, loc->len); } memcpy_to_metadata(&match->wc.masks.tunnel.metadata, data.tun_metadata, loc, idx); } static bool udpif_to_parsed(const struct flow_tnl *flow, const struct flow_tnl *mask, struct flow_tnl *flow_xlate, struct flow_tnl *mask_xlate) { if (flow->flags & FLOW_TNL_F_UDPIF) { int err; err = tun_metadata_from_geneve_udpif(flow, flow, flow_xlate); if (err) { return false; } if (mask) { tun_metadata_from_geneve_udpif(flow, mask, mask_xlate); if (err) { return false; } } } else { if (flow->metadata.present.map == 0) { /* There is no tunnel metadata, don't bother copying. */ return false; } memcpy(flow_xlate, flow, sizeof *flow_xlate); if (mask) { memcpy(mask_xlate, mask, sizeof *mask_xlate); } if (!flow_xlate->metadata.tab) { flow_xlate->metadata.tab = ovsrcu_get(struct tun_table *, &metadata_tab); } } return true; } /* Copies all MFF_TUN_METADATA* fields from 'tnl' to 'flow_metadata'. */ void tun_metadata_get_fmd(const struct flow_tnl *tnl, struct match *flow_metadata) { struct flow_tnl flow; int i; if (!udpif_to_parsed(tnl, NULL, &flow, NULL)) { return; } ULLONG_FOR_EACH_1 (i, flow.metadata.present.map) { union mf_value opts; const struct tun_metadata_loc *old_loc = &flow.metadata.tab->entries[i].loc; const struct tun_metadata_loc *new_loc; new_loc = metadata_loc_from_match(NULL, flow_metadata, NULL, i, old_loc->len, false, NULL); memcpy_from_metadata(opts.tun_metadata, &flow.metadata, old_loc); memcpy_to_metadata(&flow_metadata->flow.tunnel.metadata, opts.tun_metadata, new_loc, i); memset(opts.tun_metadata, 0xff, old_loc->len); memcpy_to_metadata(&flow_metadata->wc.masks.tunnel.metadata, opts.tun_metadata, new_loc, i); } } static uint32_t tun_meta_hash(uint32_t key) { return hash_int(key, 0); } static struct tun_meta_entry * tun_meta_find_key(const struct hmap *hmap, uint32_t key) { struct tun_meta_entry *entry; HMAP_FOR_EACH_IN_BUCKET (entry, node, tun_meta_hash(key), hmap) { if (entry->key == key) { return entry; } } return NULL; } static void memcpy_to_metadata(struct tun_metadata *dst, const void *src, const struct tun_metadata_loc *loc, unsigned int idx) { const struct tun_metadata_loc_chain *chain = &loc->c; int addr = 0; while (chain) { memcpy(dst->opts.u8 + chain->offset, (uint8_t *)src + addr, chain->len); addr += chain->len; chain = chain->next; } ULLONG_SET1(dst->present.map, idx); } static void memcpy_from_metadata(void *dst, const struct tun_metadata *src, const struct tun_metadata_loc *loc) { const struct tun_metadata_loc_chain *chain = &loc->c; int addr = 0; while (chain) { memcpy((uint8_t *)dst + addr, src->opts.u8 + chain->offset, chain->len); addr += chain->len; chain = chain->next; } } static int tun_metadata_alloc_chain(struct tun_table *map, uint8_t len, struct tun_metadata_loc_chain *loc) OVS_REQUIRES(tab_mutex) { int alloc_len = len / 4; int scan_start = 0; int scan_end = TUN_METADATA_TOT_OPT_SIZE / 4; int pos_start, pos_end, pos_len; int best_start = 0, best_len = 0; while (true) { pos_start = bitmap_scan(map->alloc_map, 0, scan_start, scan_end); if (pos_start == scan_end) { break; } pos_end = bitmap_scan(map->alloc_map, 1, pos_start, MIN(pos_start + alloc_len, scan_end)); pos_len = pos_end - pos_start; if (pos_len == alloc_len) { goto found; } if (pos_len > best_len) { best_start = pos_start; best_len = pos_len; } scan_start = pos_end + 1; } if (best_len == 0) { return ENOSPC; } pos_start = best_start; pos_len = best_len; found: bitmap_set_multiple(map->alloc_map, pos_start, pos_len, 1); loc->offset = pos_start * 4; loc->len = pos_len * 4; return 0; } static enum ofperr tun_metadata_add_entry(struct tun_table *map, uint8_t idx, uint16_t opt_class, uint8_t type, uint8_t len) OVS_REQUIRES(tab_mutex) { struct tun_meta_entry *entry; struct tun_metadata_loc_chain *cur_chain, *prev_chain; ovs_assert(idx < TUN_METADATA_NUM_OPTS); entry = &map->entries[idx]; if (entry->valid) { return OFPERR_NXTTMFC_ALREADY_MAPPED; } entry->key = tun_meta_key(htons(opt_class), type); if (tun_meta_find_key(&map->key_hmap, entry->key)) { return OFPERR_NXTTMFC_DUP_ENTRY; } entry->valid = true; hmap_insert(&map->key_hmap, &entry->node, tun_meta_hash(entry->key)); entry->loc.len = len; cur_chain = &entry->loc.c; memset(cur_chain, 0, sizeof *cur_chain); prev_chain = NULL; while (len) { int err; if (!cur_chain) { cur_chain = xzalloc(sizeof *cur_chain); prev_chain->next = cur_chain; } err = tun_metadata_alloc_chain(map, len, cur_chain); if (err) { tun_metadata_del_entry(map, idx); return OFPERR_NXTTMFC_TABLE_FULL; } len -= cur_chain->len; prev_chain = cur_chain; cur_chain = NULL; } return 0; } static void tun_metadata_del_entry(struct tun_table *map, uint8_t idx) OVS_REQUIRES(tab_mutex) { struct tun_meta_entry *entry; struct tun_metadata_loc_chain *chain; if (idx >= TUN_METADATA_NUM_OPTS) { return; } entry = &map->entries[idx]; if (!entry->valid) { return; } chain = &entry->loc.c; while (chain) { struct tun_metadata_loc_chain *next = chain->next; bitmap_set_multiple(map->alloc_map, chain->offset / 4, chain->len / 4, 0); if (chain != &entry->loc.c) { free(chain); } chain = next; } entry->valid = false; hmap_remove(&map->key_hmap, &entry->node); memset(&entry->loc, 0, sizeof entry->loc); } static int tun_metadata_from_geneve__(const struct tun_metadata *flow_metadata, const struct geneve_opt *opt, const struct geneve_opt *flow_opt, int opts_len, struct tun_metadata *metadata) { struct tun_table *map; bool is_mask = flow_opt != opt; if (!is_mask) { map = ovsrcu_get(struct tun_table *, &metadata_tab); metadata->tab = map; } else { map = flow_metadata->tab; } if (!map) { return 0; } while (opts_len > 0) { int len; struct tun_meta_entry *entry; if (opts_len < sizeof(*opt)) { return EINVAL; } len = sizeof(*opt) + flow_opt->length * 4; if (len > opts_len) { return EINVAL; } entry = tun_meta_find_key(&map->key_hmap, tun_meta_key(flow_opt->opt_class, flow_opt->type)); if (entry) { if (entry->loc.len == flow_opt->length * 4) { memcpy_to_metadata(metadata, opt + 1, &entry->loc, entry - map->entries); } else { return EINVAL; } } else if (flow_opt->type & GENEVE_CRIT_OPT_TYPE) { return EINVAL; } opt = opt + len / sizeof(*opt); flow_opt = flow_opt + len / sizeof(*opt); opts_len -= len; } return 0; } static const struct nlattr * tun_metadata_find_geneve_key(const struct nlattr *key, uint32_t key_len) { const struct nlattr *tnl_key; tnl_key = nl_attr_find__(key, key_len, OVS_KEY_ATTR_TUNNEL); if (!tnl_key) { return NULL; } return nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS); } /* Converts from Geneve netlink attributes in 'attr' to tunnel metadata * in 'tun'. The result may either in be UDPIF format or not, as determined * by 'udpif'. * * In the event that a mask is being converted, it is also necessary to * pass in flow information. This includes the full set of netlink attributes * (i.e. not just the Geneve attribute) in 'flow_attrs'/'flow_attr_len' and * the previously converted tunnel metadata 'flow_tun'. * * If a flow rather than mask is being converted, 'flow_attrs' must be NULL. */ int tun_metadata_from_geneve_nlattr(const struct nlattr *attr, const struct nlattr *flow_attrs, size_t flow_attr_len, const struct flow_tnl *flow_tun, bool udpif, struct flow_tnl *tun) { bool is_mask = !!flow_attrs; int attr_len = nl_attr_get_size(attr); const struct nlattr *flow; /* No need for real translation, just copy things over. */ if (udpif) { memcpy(tun->metadata.opts.gnv, nl_attr_get(attr), attr_len); if (!is_mask) { tun->metadata.present.len = attr_len; tun->flags |= FLOW_TNL_F_UDPIF; } else { /* We need to exact match on the length so we don't * accidentally match on sets of options that are the same * at the beginning but with additional options after. */ tun->metadata.present.len = 0xff; } return 0; } if (is_mask) { flow = tun_metadata_find_geneve_key(flow_attrs, flow_attr_len); if (!flow) { return attr_len ? EINVAL : 0; } if (attr_len != nl_attr_get_size(flow)) { return EINVAL; } } else { flow = attr; } return tun_metadata_from_geneve__(&flow_tun->metadata, nl_attr_get(attr), nl_attr_get(flow), nl_attr_get_size(flow), &tun->metadata); } /* Converts from the flat Geneve options representation extracted directly * from the tunnel header to the representation that maps options to * pre-allocated locations. The original version (in UDPIF form) is passed * in 'src' and the translated form in stored in 'dst'. To handle masks, the * flow must also be passed in through 'flow' (in the original, raw form). */ int tun_metadata_from_geneve_udpif(const struct flow_tnl *flow, const struct flow_tnl *src, struct flow_tnl *dst) { ovs_assert(flow->flags & FLOW_TNL_F_UDPIF); if (flow == src) { dst->flags = flow->flags & ~FLOW_TNL_F_UDPIF; } else { dst->metadata.tab = NULL; } dst->metadata.present.map = 0; return tun_metadata_from_geneve__(&flow->metadata, src->metadata.opts.gnv, flow->metadata.opts.gnv, flow->metadata.present.len, &dst->metadata); } static void tun_metadata_to_geneve__(const struct tun_metadata *flow, struct ofpbuf *b, bool *crit_opt) { struct tun_table *map; int i; map = flow->tab; if (!map) { map = ovsrcu_get(struct tun_table *, &metadata_tab); } *crit_opt = false; ULLONG_FOR_EACH_1 (i, flow->present.map) { struct tun_meta_entry *entry = &map->entries[i]; struct geneve_opt *opt; opt = ofpbuf_put_uninit(b, sizeof *opt + entry->loc.len); opt->opt_class = tun_key_class(entry->key); opt->type = tun_key_type(entry->key); opt->length = entry->loc.len / 4; opt->r1 = 0; opt->r2 = 0; opt->r3 = 0; memcpy_from_metadata(opt + 1, flow, &entry->loc); *crit_opt |= !!(opt->type & GENEVE_CRIT_OPT_TYPE); } } static void tun_metadata_to_geneve_nlattr_flow(const struct flow_tnl *flow, struct ofpbuf *b) { size_t nlattr_offset; bool crit_opt; if (!flow->metadata.present.map) { return; } /* For all intents and purposes, the Geneve options are nested * attributes even if this doesn't show up directly to netlink. It's * similar enough that we can use the same mechanism. */ nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS); tun_metadata_to_geneve__(&flow->metadata, b, &crit_opt); nl_msg_end_nested(b, nlattr_offset); } /* Converts from processed tunnel metadata information (in non-udpif * format) in 'flow' to a stream of Geneve options suitable for * transmission in 'opts'. Additionally returns whether there were * any critical options in 'crit_opt' as well as the total length of * data. */ int tun_metadata_to_geneve_header(const struct flow_tnl *flow, struct geneve_opt *opts, bool *crit_opt) { struct ofpbuf b; ovs_assert(!(flow->flags & FLOW_TNL_F_UDPIF)); ofpbuf_use_stack(&b, opts, TLV_TOT_OPT_SIZE); tun_metadata_to_geneve__(&flow->metadata, &b, crit_opt); return b.size; } static void tun_metadata_to_geneve_mask__(const struct tun_metadata *flow, const struct tun_metadata *mask, struct geneve_opt *opt, int opts_len) { struct tun_table *map = flow->tab; if (!map) { return; } /* All of these options have already been validated, so no need * for sanity checking. */ while (opts_len > 0) { struct tun_meta_entry *entry; int len = sizeof(*opt) + opt->length * 4; entry = tun_meta_find_key(&map->key_hmap, tun_meta_key(opt->opt_class, opt->type)); if (entry) { memcpy_from_metadata(opt + 1, mask, &entry->loc); } else { memset(opt + 1, 0, opt->length * 4); } opt->opt_class = htons(0xffff); opt->type = 0xff; opt->length = 0x1f; opt->r1 = 0; opt->r2 = 0; opt->r3 = 0; opt = opt + len / sizeof(*opt); opts_len -= len; } } static void tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key, const struct flow_tnl *mask, const struct flow_tnl *flow, struct ofpbuf *b) { const struct nlattr *geneve_key; struct nlattr *geneve_mask; struct geneve_opt *opt; int opts_len; if (!key) { return; } geneve_key = tun_metadata_find_geneve_key(key->data, key->size); if (!geneve_key) { return; } geneve_mask = ofpbuf_tail(b); nl_msg_put(b, geneve_key, geneve_key->nla_len); opt = CONST_CAST(struct geneve_opt *, nl_attr_get(geneve_mask)); opts_len = nl_attr_get_size(geneve_mask); tun_metadata_to_geneve_mask__(&flow->metadata, &mask->metadata, opt, opts_len); } /* Convert from the tunnel metadata in 'tun' to netlink attributes stored * in 'b'. Either UDPIF or non-UDPIF input forms are accepted. * * To assist with parsing, it is necessary to also pass in the tunnel metadata * from the flow in 'flow' as well in the original netlink form of the flow in * 'key'. */ void tun_metadata_to_geneve_nlattr(const struct flow_tnl *tun, const struct flow_tnl *flow, const struct ofpbuf *key, struct ofpbuf *b) { bool is_mask = tun != flow; if (!(flow->flags & FLOW_TNL_F_UDPIF)) { if (!is_mask) { tun_metadata_to_geneve_nlattr_flow(tun, b); } else { tun_metadata_to_geneve_nlattr_mask(key, tun, flow, b); } } else if (flow->metadata.present.len || is_mask) { nl_msg_put_unspec(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, tun->metadata.opts.gnv, flow->metadata.present.len); } } /* Converts 'mask_src' (in non-UDPIF format) to a series of masked options in * 'dst'. 'flow_src' (also in non-UDPIF format) and the original set of * options 'flow_src_opt'/'opts_len' are needed as a guide to interpret the * mask data. */ void tun_metadata_to_geneve_udpif_mask(const struct flow_tnl *flow_src, const struct flow_tnl *mask_src, const struct geneve_opt *flow_src_opt, int opts_len, struct geneve_opt *dst) { ovs_assert(!(flow_src->flags & FLOW_TNL_F_UDPIF)); memcpy(dst, flow_src_opt, opts_len); tun_metadata_to_geneve_mask__(&flow_src->metadata, &mask_src->metadata, dst, opts_len); } static const struct tun_metadata_loc * metadata_loc_from_match_read(struct tun_table *map, const struct match *match, unsigned int idx, struct flow_tnl *mask, bool *is_masked) { union mf_value mask_opts; if (match->tun_md.valid) { *is_masked = match->tun_md.entry[idx].masked; return &match->tun_md.entry[idx].loc; } memcpy_from_metadata(mask_opts.tun_metadata, &mask->metadata, &map->entries[idx].loc); *is_masked = map->entries[idx].loc.len == 0 || !is_all_ones(mask_opts.tun_metadata, map->entries[idx].loc.len); return &map->entries[idx].loc; } void tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm, const struct match *match) { struct flow_tnl flow, mask; int i; if (!udpif_to_parsed(&match->flow.tunnel, &match->wc.masks.tunnel, &flow, &mask)) { return; } ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) { const struct tun_metadata_loc *loc; bool is_masked; union mf_value opts; union mf_value mask_opts; loc = metadata_loc_from_match_read(flow.metadata.tab, match, i, &mask, &is_masked); memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc); memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc); nxm_put__(b, MFF_TUN_METADATA0 + i, oxm, opts.tun_metadata, is_masked ? mask_opts.tun_metadata : NULL, loc->len); } } void tun_metadata_match_format(struct ds *s, const struct match *match) { struct flow_tnl flow, mask; unsigned int i; if (!udpif_to_parsed(&match->flow.tunnel, &match->wc.masks.tunnel, &flow, &mask)) { return; } ULLONG_FOR_EACH_1 (i, mask.metadata.present.map) { const struct tun_metadata_loc *loc; bool is_masked; union mf_value opts, mask_opts; loc = metadata_loc_from_match_read(flow.metadata.tab, match, i, &mask, &is_masked); ds_put_format(s, "tun_metadata%u", i); memcpy_from_metadata(mask_opts.tun_metadata, &mask.metadata, loc); if (!ULLONG_GET(flow.metadata.present.map, i)) { /* Indicate that we are matching on the field being not present. */ ds_put_cstr(s, "=NP"); } else if (!(is_masked && is_all_zeros(mask_opts.tun_metadata, loc->len))) { ds_put_char(s, '='); memcpy_from_metadata(opts.tun_metadata, &flow.metadata, loc); ds_put_hex(s, opts.tun_metadata, loc->len); if (!is_all_ones(mask_opts.tun_metadata, loc->len)) { ds_put_char(s, '/'); ds_put_hex(s, mask_opts.tun_metadata, loc->len); } } ds_put_char(s, ','); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-msgs.h0000644000000000000000000000013213534540071016316 xustar0030 mtime=1567801401.521682051 30 atime=1567801402.085686193 30 ctime=1567801424.793853514 openvswitch-2.5.9/lib/ofp-msgs.h0000644000175000017500000007173713534540071020023 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFP_MSGS_H #define OFP_MSGS_H 1 /* OpenFlow message headers abstraction. * * OpenFlow headers are unnecessarily complicated: * * - Some messages with the same meaning were renumbered between 1.0 and 1.1. * * - "Statistics" (aka multipart) messages have a different format from other * messages. * * - The 1.0 header for statistics messages is an odd number of 32-bit words * long, leaving 64-bit quantities in the body misaligned. The 1.1 header * for statistics added a padding word to fix this misalignment, although * many statistic message bodies did not change. * * - Vendor-defined messages have an additional header but no standard way to * distinguish individual types of message within a given vendor. * * This file attempts to abstract out the differences between the various forms * of headers. */ #include "openvswitch/types.h" #include "ofp-errors.h" #include "util.h" struct ovs_list; /* Raw identifiers for OpenFlow messages. * * Some OpenFlow messages with similar meanings have multiple variants across * OpenFlow versions or vendor extensions. Each variant has a different * OFPRAW_* enumeration constant. More specifically, if two messages have * different types, different numbers, or different arguments, then they must * have different OFPRAW_* values. * * The comments here must follow a stylized form because the "extract-ofp-msgs" * program parses them at build time to generate data tables. The syntax of * each comment is: * * type versions (number): arguments. * * where the syntax of each part is: * * - type: One of OFPT (standard OpenFlow message), OFPST (standard OpenFlow * statistics message), NXT (Nicira extension message), or NXST (Nicira * extension statistics message). * * As new vendors implement extensions it will make sense to expand the * dictionary of possible types. * * - versions: The OpenFlow version or versions in which this message is * supported, e.g. "1.0" or "1.1" or "1.0+". * * - number: * For OFPT, the 'type' in struct ofp_header. * For OFPST, the 'type' in struct ofp_stats_msg or ofp11_stats_msg. * For NXT, the 'subtype' in struct nicira_header. * For NXST, the 'subtype' in struct nicira10_stats_msg or * nicira11_stats_msg. * * - arguments: The types of data that follow the OpenFlow headers (the * message "body"). This can be "void" if the message has no body. * Otherwise, it should be a comma-separated sequence of C types. The * last type in the sequence can end with [] if the body ends in a * variable-length sequence. * * The arguments are used to validate the lengths of messages when a * header is parsed. Any message whose length isn't valid as a length of * the specified types will be rejected with OFPERR_OFPBRC_BAD_LEN. * * A few OpenFlow messages, such as OFPT_PACKET_IN, intentionally end with * only part of a structure, up to some specified member. The syntax "up * to " indicates this, e.g. "struct ofp11_packet_in up to data". */ enum ofpraw { /* Immutable standard messages. * * The OpenFlow standard promises to preserve these messages and their numbers * in future versions, so we mark them as , which covers every OpenFlow * version numbered 0x01...0xff, rather than as OF1.0+, which covers only * OpenFlow versions that we otherwise implement. * * Without here, then we would fail to decode "hello" messages that * announce a version higher than we understand, even though there still could * be a version in common with the peer that we do understand. The * keyword is less useful for the other messages, because our OpenFlow channels * accept only OpenFlow messages with a previously negotiated version. */ /* OFPT (0): uint8_t[]. */ OFPRAW_OFPT_HELLO, /* OFPT (1): struct ofp_error_msg, uint8_t[]. */ OFPRAW_OFPT_ERROR, /* OFPT (2): uint8_t[]. */ OFPRAW_OFPT_ECHO_REQUEST, /* OFPT (3): uint8_t[]. */ OFPRAW_OFPT_ECHO_REPLY, /* Other standard messages. * * The meanings of these messages can (and often do) change from one version * of OpenFlow to another. */ /* OFPT 1.0+ (5): void. */ OFPRAW_OFPT_FEATURES_REQUEST, /* OFPT 1.0 (6): struct ofp_switch_features, struct ofp10_phy_port[]. */ OFPRAW_OFPT10_FEATURES_REPLY, /* OFPT 1.1-1.2 (6): struct ofp_switch_features, struct ofp11_port[]. */ OFPRAW_OFPT11_FEATURES_REPLY, /* OFPT 1.3+ (6): struct ofp_switch_features. */ OFPRAW_OFPT13_FEATURES_REPLY, /* OFPT 1.0+ (7): void. */ OFPRAW_OFPT_GET_CONFIG_REQUEST, /* OFPT 1.0+ (8): struct ofp_switch_config. */ OFPRAW_OFPT_GET_CONFIG_REPLY, /* OFPT 1.0+ (9): struct ofp_switch_config. */ OFPRAW_OFPT_SET_CONFIG, /* OFPT 1.0 (10): struct ofp10_packet_in up to data, uint8_t[]. */ OFPRAW_OFPT10_PACKET_IN, /* OFPT 1.1 (10): struct ofp11_packet_in, uint8_t[]. */ OFPRAW_OFPT11_PACKET_IN, /* OFPT 1.2 (10): struct ofp12_packet_in, uint8_t[]. */ OFPRAW_OFPT12_PACKET_IN, /* OFPT 1.3+ (10): struct ofp13_packet_in, uint8_t[]. */ OFPRAW_OFPT13_PACKET_IN, /* NXT 1.0+ (17): struct nx_packet_in, uint8_t[]. */ OFPRAW_NXT_PACKET_IN, /* OFPT 1.0 (11): struct ofp10_flow_removed. */ OFPRAW_OFPT10_FLOW_REMOVED, /* OFPT 1.1+ (11): struct ofp11_flow_removed, uint8_t[8][]. */ OFPRAW_OFPT11_FLOW_REMOVED, /* NXT 1.0+ (14): struct nx_flow_removed, uint8_t[8][]. */ OFPRAW_NXT_FLOW_REMOVED, /* OFPT 1.0 (12): struct ofp_port_status, struct ofp10_phy_port. */ OFPRAW_OFPT10_PORT_STATUS, /* OFPT 1.1-1.3 (12): struct ofp_port_status, struct ofp11_port. */ OFPRAW_OFPT11_PORT_STATUS, /* OFPT 1.4+ (12): struct ofp_port_status, struct ofp14_port, uint8_t[8][]. */ OFPRAW_OFPT14_PORT_STATUS, /* OFPT 1.0 (13): struct ofp10_packet_out, uint8_t[]. */ OFPRAW_OFPT10_PACKET_OUT, /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */ OFPRAW_OFPT11_PACKET_OUT, /* OFPT 1.0 (14): struct ofp10_flow_mod, uint8_t[8][]. */ OFPRAW_OFPT10_FLOW_MOD, /* OFPT 1.1+ (14): struct ofp11_flow_mod, struct ofp11_instruction[]. */ OFPRAW_OFPT11_FLOW_MOD, /* NXT 1.0+ (13): struct nx_flow_mod, uint8_t[8][]. */ OFPRAW_NXT_FLOW_MOD, /* OFPT 1.1-1.4 (15): struct ofp11_group_mod, uint8_t[8][]. */ OFPRAW_OFPT11_GROUP_MOD, /* OFPT 1.5+ (15): struct ofp15_group_mod, uint8_t[8][]. */ OFPRAW_OFPT15_GROUP_MOD, /* OFPT 1.0 (15): struct ofp10_port_mod. */ OFPRAW_OFPT10_PORT_MOD, /* OFPT 1.1-1.3 (16): struct ofp11_port_mod. */ OFPRAW_OFPT11_PORT_MOD, /* OFPT 1.4+ (16): struct ofp14_port_mod, uint8_t[8][]. */ OFPRAW_OFPT14_PORT_MOD, /* OFPT 1.1-1.3 (17): struct ofp11_table_mod. */ OFPRAW_OFPT11_TABLE_MOD, /* OFPT 1.4+ (17): struct ofp14_table_mod, uint8_t[8][]. */ OFPRAW_OFPT14_TABLE_MOD, /* OFPT 1.0 (18): void. */ OFPRAW_OFPT10_BARRIER_REQUEST, /* OFPT 1.1+ (20): void. */ OFPRAW_OFPT11_BARRIER_REQUEST, /* OFPT 1.0 (19): void. */ OFPRAW_OFPT10_BARRIER_REPLY, /* OFPT 1.1+ (21): void. */ OFPRAW_OFPT11_BARRIER_REPLY, /* OFPT 1.0 (20): struct ofp10_queue_get_config_request. */ OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST, /* OFPT 1.1+ (22): struct ofp11_queue_get_config_request. */ OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST, /* OFPT 1.0 (21): struct ofp10_queue_get_config_reply, uint8_t[8][]. */ OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY, /* OFPT 1.1+ (23): struct ofp11_queue_get_config_reply, uint8_t[8][]. */ OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY, /* OFPT 1.2+ (24): struct ofp12_role_request. */ OFPRAW_OFPT12_ROLE_REQUEST, /* NXT 1.0+ (10): struct nx_role_request. */ OFPRAW_NXT_ROLE_REQUEST, /* OFPT 1.2+ (25): struct ofp12_role_request. */ OFPRAW_OFPT12_ROLE_REPLY, /* NXT 1.0+ (11): struct nx_role_request. */ OFPRAW_NXT_ROLE_REPLY, /* OFPT 1.3 (26): void. */ OFPRAW_OFPT13_GET_ASYNC_REQUEST, /* OFPT 1.4+ (26): void. */ OFPRAW_OFPT14_GET_ASYNC_REQUEST, /* OFPT 1.3 (27): struct ofp13_async_config. */ OFPRAW_OFPT13_GET_ASYNC_REPLY, /* OFPT 1.4+ (27): struct ofp14_async_config, uint8_t[8][]. */ OFPRAW_OFPT14_GET_ASYNC_REPLY, /* OFPT 1.3 (28): struct ofp13_async_config. */ OFPRAW_OFPT13_SET_ASYNC, /* NXT 1.0+ (19): struct nx_async_config. */ OFPRAW_NXT_SET_ASYNC_CONFIG, /* OFPT 1.4+ (28): struct ofp14_async_config, uint8_t[8][]. */ OFPRAW_OFPT14_SET_ASYNC, /* OFPT 1.3+ (29): struct ofp13_meter_mod, uint8_t[8][]. */ OFPRAW_OFPT13_METER_MOD, /* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */ OFPRAW_OFPT14_ROLE_STATUS, /* OFPT 1.4+ (32): struct ofp14_requestforward, uint8_t[8][]. */ OFPRAW_OFPT14_REQUESTFORWARD, /* OFPT 1.4+ (33): struct ofp14_bundle_ctrl_msg, uint8_t[8][]. */ OFPRAW_OFPT14_BUNDLE_CONTROL, /* OFPT 1.4+ (34): struct ofp14_bundle_ctrl_msg, uint8_t[]. */ OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, /* Standard statistics. */ /* OFPST 1.0+ (0): void. */ OFPRAW_OFPST_DESC_REQUEST, /* OFPST 1.0+ (0): struct ofp_desc_stats. */ OFPRAW_OFPST_DESC_REPLY, /* OFPST 1.0 (1): struct ofp10_flow_stats_request. */ OFPRAW_OFPST10_FLOW_REQUEST, /* OFPST 1.1+ (1): struct ofp11_flow_stats_request, uint8_t[8][]. */ OFPRAW_OFPST11_FLOW_REQUEST, /* NXST 1.0 (0): struct nx_flow_stats_request, uint8_t[8][]. */ OFPRAW_NXST_FLOW_REQUEST, /* OFPST 1.0 (1): uint8_t[]. */ OFPRAW_OFPST10_FLOW_REPLY, /* OFPST 1.1-1.2 (1): uint8_t[]. */ OFPRAW_OFPST11_FLOW_REPLY, /* OFPST 1.3+ (1): uint8_t[]. */ OFPRAW_OFPST13_FLOW_REPLY, /* NXST 1.0 (0): uint8_t[]. */ OFPRAW_NXST_FLOW_REPLY, /* OFPST 1.0 (2): struct ofp10_flow_stats_request. */ OFPRAW_OFPST10_AGGREGATE_REQUEST, /* OFPST 1.1+ (2): struct ofp11_flow_stats_request, uint8_t[8][]. */ OFPRAW_OFPST11_AGGREGATE_REQUEST, /* NXST 1.0 (1): struct nx_flow_stats_request, uint8_t[8][]. */ OFPRAW_NXST_AGGREGATE_REQUEST, /* OFPST 1.0+ (2): struct ofp_aggregate_stats_reply. */ OFPRAW_OFPST_AGGREGATE_REPLY, /* NXST 1.0 (1): struct ofp_aggregate_stats_reply. */ OFPRAW_NXST_AGGREGATE_REPLY, /* OFPST 1.0+ (3): void. */ OFPRAW_OFPST_TABLE_REQUEST, /* OFPST 1.0 (3): struct ofp10_table_stats[]. */ OFPRAW_OFPST10_TABLE_REPLY, /* OFPST 1.1 (3): struct ofp11_table_stats[]. */ OFPRAW_OFPST11_TABLE_REPLY, /* OFPST 1.2 (3): struct ofp12_table_stats[]. */ OFPRAW_OFPST12_TABLE_REPLY, /* OFPST 1.3+ (3): struct ofp13_table_stats[]. */ OFPRAW_OFPST13_TABLE_REPLY, /* OFPST 1.0 (4): struct ofp10_port_stats_request. */ OFPRAW_OFPST10_PORT_REQUEST, /* OFPST 1.1+ (4): struct ofp11_port_stats_request. */ OFPRAW_OFPST11_PORT_REQUEST, /* OFPST 1.0 (4): struct ofp10_port_stats[]. */ OFPRAW_OFPST10_PORT_REPLY, /* OFPST 1.1-1.2 (4): struct ofp11_port_stats[]. */ OFPRAW_OFPST11_PORT_REPLY, /* OFPST 1.3 (4): struct ofp13_port_stats[]. */ OFPRAW_OFPST13_PORT_REPLY, /* OFPST 1.4+ (4): uint8_t[8][]. */ OFPRAW_OFPST14_PORT_REPLY, /* OFPST 1.0 (5): struct ofp10_queue_stats_request. */ OFPRAW_OFPST10_QUEUE_REQUEST, /* OFPST 1.1+ (5): struct ofp11_queue_stats_request. */ OFPRAW_OFPST11_QUEUE_REQUEST, /* OFPST 1.0 (5): struct ofp10_queue_stats[]. */ OFPRAW_OFPST10_QUEUE_REPLY, /* OFPST 1.1-1.2 (5): struct ofp11_queue_stats[]. */ OFPRAW_OFPST11_QUEUE_REPLY, /* OFPST 1.3 (5): struct ofp13_queue_stats[]. */ OFPRAW_OFPST13_QUEUE_REPLY, /* OFPST 1.4+ (5): uint8_t[8][]. */ OFPRAW_OFPST14_QUEUE_REPLY, /* OFPST 1.1+ (6): struct ofp11_group_stats_request. */ OFPRAW_OFPST11_GROUP_REQUEST, /* OFPST 1.1-1.2 (6): uint8_t[8][]. */ OFPRAW_OFPST11_GROUP_REPLY, /* OFPST 1.3+ (6): uint8_t[8][]. */ OFPRAW_OFPST13_GROUP_REPLY, /* OFPST 1.1-1.4 (7): void. */ OFPRAW_OFPST11_GROUP_DESC_REQUEST, /* OFPST 1.5+ (7): struct ofp15_group_desc_request. */ OFPRAW_OFPST15_GROUP_DESC_REQUEST, /* OFPST 1.1+ (7): uint8_t[8][]. */ OFPRAW_OFPST11_GROUP_DESC_REPLY, /* OFPST 1.2+ (8): void. */ OFPRAW_OFPST12_GROUP_FEATURES_REQUEST, /* OFPST 1.2+ (8): struct ofp12_group_features_stats. */ OFPRAW_OFPST12_GROUP_FEATURES_REPLY, /* OFPST 1.3+ (9): struct ofp13_meter_multipart_request. */ OFPRAW_OFPST13_METER_REQUEST, /* OFPST 1.3+ (9): uint8_t[8][]. */ OFPRAW_OFPST13_METER_REPLY, /* OFPST 1.3+ (10): struct ofp13_meter_multipart_request. */ OFPRAW_OFPST13_METER_CONFIG_REQUEST, /* OFPST 1.3+ (10): uint8_t[8][]. */ OFPRAW_OFPST13_METER_CONFIG_REPLY, /* OFPST 1.3+ (11): void. */ OFPRAW_OFPST13_METER_FEATURES_REQUEST, /* OFPST 1.3+ (11): struct ofp13_meter_features. */ OFPRAW_OFPST13_METER_FEATURES_REPLY, /* OFPST 1.3+ (12): void. */ OFPRAW_OFPST13_TABLE_FEATURES_REQUEST, /* OFPST 1.3+ (12): struct ofp13_table_features, uint8_t[8][]. */ OFPRAW_OFPST13_TABLE_FEATURES_REPLY, /* OFPST 1.4+ (14): void. */ OFPRAW_OFPST14_TABLE_DESC_REQUEST, /* OFPST 1.4+ (14): struct ofp14_table_desc, uint8_t[8][]. */ OFPRAW_OFPST14_TABLE_DESC_REPLY, /* OFPST 1.0-1.4 (13): void. */ OFPRAW_OFPST10_PORT_DESC_REQUEST, /* OFPST 1.5+ (13): struct ofp15_port_desc_request. */ OFPRAW_OFPST15_PORT_DESC_REQUEST, /* OFPST 1.0 (13): struct ofp10_phy_port[]. */ OFPRAW_OFPST10_PORT_DESC_REPLY, /* OFPST 1.1-1.3 (13): struct ofp11_port[]. */ OFPRAW_OFPST11_PORT_DESC_REPLY, /* OFPST 1.4+ (13): uint8_t[8][]. */ OFPRAW_OFPST14_PORT_DESC_REPLY, /* OFPST 1.4+ (16): uint8_t[8][]. */ OFPRAW_OFPST14_FLOW_MONITOR_REQUEST, /* NXST 1.0 (2): uint8_t[8][]. */ OFPRAW_NXST_FLOW_MONITOR_REQUEST, /* OFPST 1.4+ (16): uint8_t[8][]. */ OFPRAW_OFPST14_FLOW_MONITOR_REPLY, /* NXST 1.0 (2): uint8_t[8][]. */ OFPRAW_NXST_FLOW_MONITOR_REPLY, /* Nicira extension messages. * * Nicira extensions that correspond to standard OpenFlow messages are listed * alongside the standard versions above. */ /* NXT 1.0 (12): struct nx_set_flow_format. */ OFPRAW_NXT_SET_FLOW_FORMAT, /* NXT 1.0+ (15): struct nx_flow_mod_table_id. */ OFPRAW_NXT_FLOW_MOD_TABLE_ID, /* NXT 1.0+ (16): struct nx_set_packet_in_format. */ OFPRAW_NXT_SET_PACKET_IN_FORMAT, /* NXT 1.0+ (18): void. */ OFPRAW_NXT_FLOW_AGE, /* NXT 1.0+ (20): struct nx_controller_id. */ OFPRAW_NXT_SET_CONTROLLER_ID, /* NXT 1.0+ (21): struct nx_flow_monitor_cancel. */ OFPRAW_NXT_FLOW_MONITOR_CANCEL, /* NXT 1.0+ (22): void. */ OFPRAW_NXT_FLOW_MONITOR_PAUSED, /* NXT 1.0+ (23): void. */ OFPRAW_NXT_FLOW_MONITOR_RESUMED, /* NXT 1.0+ (24): struct nx_tlv_table_mod, struct nx_tlv_map[]. */ OFPRAW_NXT_TLV_TABLE_MOD, /* NXT 1.0+ (25): void. */ OFPRAW_NXT_TLV_TABLE_REQUEST, /* NXT 1.0+ (26): struct nx_tlv_table_reply, struct nx_tlv_map[]. */ OFPRAW_NXT_TLV_TABLE_REPLY, }; /* Decoding messages into OFPRAW_* values. */ enum ofperr ofpraw_decode(enum ofpraw *, const struct ofp_header *); enum ofpraw ofpraw_decode_assert(const struct ofp_header *); enum ofperr ofpraw_pull(enum ofpraw *, struct ofpbuf *); enum ofpraw ofpraw_pull_assert(struct ofpbuf *); enum ofperr ofpraw_decode_partial(enum ofpraw *, const struct ofp_header *, size_t length); /* Encoding messages using OFPRAW_* values. */ struct ofpbuf *ofpraw_alloc(enum ofpraw, uint8_t ofp_version, size_t extra_tailroom); struct ofpbuf *ofpraw_alloc_xid(enum ofpraw, uint8_t ofp_version, ovs_be32 xid, size_t extra_tailroom); struct ofpbuf *ofpraw_alloc_reply(enum ofpraw, const struct ofp_header *request, size_t extra_tailroom); struct ofpbuf *ofpraw_alloc_stats_reply(const struct ofp_header *request, size_t extra_tailroom); void ofpraw_put(enum ofpraw, uint8_t ofp_version, struct ofpbuf *); void ofpraw_put_xid(enum ofpraw, uint8_t ofp_version, ovs_be32 xid, struct ofpbuf *); void ofpraw_put_reply(enum ofpraw, const struct ofp_header *request, struct ofpbuf *); void ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *); /* Information about OFPRAW_* values. */ const char *ofpraw_get_name(enum ofpraw); enum ofpraw ofpraw_stats_request_to_reply(enum ofpraw, uint8_t version); /* Semantic identifiers for OpenFlow messages. * * Each OFPTYPE_* enumeration constant represents one or more concrete format * of OpenFlow message. When two variants of a message have essentially the * same meaning, they are assigned a single OFPTYPE_* value. * * The comments here must follow a stylized form because the "extract-ofp-msgs" * program parses them at build time to generate data tables. The format is * simply to list each OFPRAW_* enumeration constant for a given OFPTYPE_*, * each followed by a period. */ enum ofptype { /* Immutable messages. */ OFPTYPE_HELLO, /* OFPRAW_OFPT_HELLO. */ OFPTYPE_ERROR, /* OFPRAW_OFPT_ERROR. */ OFPTYPE_ECHO_REQUEST, /* OFPRAW_OFPT_ECHO_REQUEST. */ OFPTYPE_ECHO_REPLY, /* OFPRAW_OFPT_ECHO_REPLY. */ /* Switch configuration messages. */ OFPTYPE_FEATURES_REQUEST, /* OFPRAW_OFPT_FEATURES_REQUEST. */ OFPTYPE_FEATURES_REPLY, /* OFPRAW_OFPT10_FEATURES_REPLY. * OFPRAW_OFPT11_FEATURES_REPLY. * OFPRAW_OFPT13_FEATURES_REPLY. */ OFPTYPE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT_GET_CONFIG_REQUEST. */ OFPTYPE_GET_CONFIG_REPLY, /* OFPRAW_OFPT_GET_CONFIG_REPLY. */ OFPTYPE_SET_CONFIG, /* OFPRAW_OFPT_SET_CONFIG. */ /* Asynchronous messages. */ OFPTYPE_PACKET_IN, /* OFPRAW_OFPT10_PACKET_IN. * OFPRAW_OFPT11_PACKET_IN. * OFPRAW_OFPT12_PACKET_IN. * OFPRAW_OFPT13_PACKET_IN. * OFPRAW_NXT_PACKET_IN. */ OFPTYPE_FLOW_REMOVED, /* OFPRAW_OFPT10_FLOW_REMOVED. * OFPRAW_OFPT11_FLOW_REMOVED. * OFPRAW_NXT_FLOW_REMOVED. */ OFPTYPE_PORT_STATUS, /* OFPRAW_OFPT10_PORT_STATUS. * OFPRAW_OFPT11_PORT_STATUS. * OFPRAW_OFPT14_PORT_STATUS. */ /* Controller command messages. */ OFPTYPE_PACKET_OUT, /* OFPRAW_OFPT10_PACKET_OUT. * OFPRAW_OFPT11_PACKET_OUT. */ OFPTYPE_FLOW_MOD, /* OFPRAW_OFPT10_FLOW_MOD. * OFPRAW_OFPT11_FLOW_MOD. * OFPRAW_NXT_FLOW_MOD. */ OFPTYPE_GROUP_MOD, /* OFPRAW_OFPT11_GROUP_MOD. * OFPRAW_OFPT15_GROUP_MOD. */ OFPTYPE_PORT_MOD, /* OFPRAW_OFPT10_PORT_MOD. * OFPRAW_OFPT11_PORT_MOD. * OFPRAW_OFPT14_PORT_MOD. */ OFPTYPE_TABLE_MOD, /* OFPRAW_OFPT11_TABLE_MOD. * OFPRAW_OFPT14_TABLE_MOD. */ /* Barrier messages. */ OFPTYPE_BARRIER_REQUEST, /* OFPRAW_OFPT10_BARRIER_REQUEST. * OFPRAW_OFPT11_BARRIER_REQUEST. */ OFPTYPE_BARRIER_REPLY, /* OFPRAW_OFPT10_BARRIER_REPLY. * OFPRAW_OFPT11_BARRIER_REPLY. */ /* Queue Configuration messages. */ OFPTYPE_QUEUE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST. * OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST. */ OFPTYPE_QUEUE_GET_CONFIG_REPLY, /* OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY. * OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY. */ /* Controller role change request messages. */ OFPTYPE_ROLE_REQUEST, /* OFPRAW_OFPT12_ROLE_REQUEST. * OFPRAW_NXT_ROLE_REQUEST. */ OFPTYPE_ROLE_REPLY, /* OFPRAW_OFPT12_ROLE_REPLY. * OFPRAW_NXT_ROLE_REPLY. */ /* Asynchronous message configuration. */ OFPTYPE_GET_ASYNC_REQUEST, /* OFPRAW_OFPT13_GET_ASYNC_REQUEST. * OFPRAW_OFPT14_GET_ASYNC_REQUEST. */ OFPTYPE_GET_ASYNC_REPLY, /* OFPRAW_OFPT13_GET_ASYNC_REPLY. * OFPRAW_OFPT14_GET_ASYNC_REPLY. */ OFPTYPE_SET_ASYNC_CONFIG, /* OFPRAW_NXT_SET_ASYNC_CONFIG. * OFPRAW_OFPT13_SET_ASYNC. * OFPRAW_OFPT14_SET_ASYNC. */ /* Meters and rate limiters configuration messages. */ OFPTYPE_METER_MOD, /* OFPRAW_OFPT13_METER_MOD. */ /* Controller role change event messages. */ OFPTYPE_ROLE_STATUS, /* OFPRAW_OFPT14_ROLE_STATUS. */ /* Request forwarding by the switch. */ OFPTYPE_REQUESTFORWARD, /* OFPRAW_OFPT14_REQUESTFORWARD. */ OFPTYPE_BUNDLE_CONTROL, /* OFPRAW_OFPT14_BUNDLE_CONTROL. */ OFPTYPE_BUNDLE_ADD_MESSAGE, /* OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE. */ /* Statistics. */ OFPTYPE_DESC_STATS_REQUEST, /* OFPRAW_OFPST_DESC_REQUEST. */ OFPTYPE_DESC_STATS_REPLY, /* OFPRAW_OFPST_DESC_REPLY. */ OFPTYPE_FLOW_STATS_REQUEST, /* OFPRAW_OFPST10_FLOW_REQUEST. * OFPRAW_OFPST11_FLOW_REQUEST. * OFPRAW_NXST_FLOW_REQUEST. */ OFPTYPE_FLOW_STATS_REPLY, /* OFPRAW_OFPST10_FLOW_REPLY. * OFPRAW_OFPST11_FLOW_REPLY. * OFPRAW_OFPST13_FLOW_REPLY. * OFPRAW_NXST_FLOW_REPLY. */ OFPTYPE_AGGREGATE_STATS_REQUEST, /* OFPRAW_OFPST10_AGGREGATE_REQUEST. * OFPRAW_OFPST11_AGGREGATE_REQUEST. * OFPRAW_NXST_AGGREGATE_REQUEST. */ OFPTYPE_AGGREGATE_STATS_REPLY, /* OFPRAW_OFPST_AGGREGATE_REPLY. * OFPRAW_NXST_AGGREGATE_REPLY. */ OFPTYPE_TABLE_STATS_REQUEST, /* OFPRAW_OFPST_TABLE_REQUEST. */ OFPTYPE_TABLE_STATS_REPLY, /* OFPRAW_OFPST10_TABLE_REPLY. * OFPRAW_OFPST11_TABLE_REPLY. * OFPRAW_OFPST12_TABLE_REPLY. * OFPRAW_OFPST13_TABLE_REPLY. */ OFPTYPE_PORT_STATS_REQUEST, /* OFPRAW_OFPST10_PORT_REQUEST. * OFPRAW_OFPST11_PORT_REQUEST. */ OFPTYPE_PORT_STATS_REPLY, /* OFPRAW_OFPST10_PORT_REPLY. * OFPRAW_OFPST11_PORT_REPLY. * OFPRAW_OFPST13_PORT_REPLY. * OFPRAW_OFPST14_PORT_REPLY. */ OFPTYPE_QUEUE_STATS_REQUEST, /* OFPRAW_OFPST10_QUEUE_REQUEST. * OFPRAW_OFPST11_QUEUE_REQUEST. */ OFPTYPE_QUEUE_STATS_REPLY, /* OFPRAW_OFPST10_QUEUE_REPLY. * OFPRAW_OFPST11_QUEUE_REPLY. * OFPRAW_OFPST13_QUEUE_REPLY. * OFPRAW_OFPST14_QUEUE_REPLY. */ OFPTYPE_GROUP_STATS_REQUEST, /* OFPRAW_OFPST11_GROUP_REQUEST. */ OFPTYPE_GROUP_STATS_REPLY, /* OFPRAW_OFPST11_GROUP_REPLY. * OFPRAW_OFPST13_GROUP_REPLY. */ OFPTYPE_GROUP_DESC_STATS_REQUEST, /* OFPRAW_OFPST11_GROUP_DESC_REQUEST. * OFPRAW_OFPST15_GROUP_DESC_REQUEST. */ OFPTYPE_GROUP_DESC_STATS_REPLY, /* OFPRAW_OFPST11_GROUP_DESC_REPLY. */ OFPTYPE_GROUP_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST12_GROUP_FEATURES_REQUEST. */ OFPTYPE_GROUP_FEATURES_STATS_REPLY, /* OFPRAW_OFPST12_GROUP_FEATURES_REPLY. */ OFPTYPE_METER_STATS_REQUEST, /* OFPRAW_OFPST13_METER_REQUEST. */ OFPTYPE_METER_STATS_REPLY, /* OFPRAW_OFPST13_METER_REPLY. */ OFPTYPE_METER_CONFIG_STATS_REQUEST, /* OFPRAW_OFPST13_METER_CONFIG_REQUEST. */ OFPTYPE_METER_CONFIG_STATS_REPLY, /* OFPRAW_OFPST13_METER_CONFIG_REPLY. */ OFPTYPE_METER_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_METER_FEATURES_REQUEST. */ OFPTYPE_METER_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_METER_FEATURES_REPLY. */ OFPTYPE_TABLE_FEATURES_STATS_REQUEST, /* OFPRAW_OFPST13_TABLE_FEATURES_REQUEST. */ OFPTYPE_TABLE_FEATURES_STATS_REPLY, /* OFPRAW_OFPST13_TABLE_FEATURES_REPLY. */ OFPTYPE_TABLE_DESC_REQUEST, /* OFPRAW_OFPST14_TABLE_DESC_REQUEST. */ OFPTYPE_TABLE_DESC_REPLY, /* OFPRAW_OFPST14_TABLE_DESC_REPLY. */ OFPTYPE_PORT_DESC_STATS_REQUEST, /* OFPRAW_OFPST10_PORT_DESC_REQUEST. * OFPRAW_OFPST15_PORT_DESC_REQUEST. */ OFPTYPE_PORT_DESC_STATS_REPLY, /* OFPRAW_OFPST10_PORT_DESC_REPLY. * OFPRAW_OFPST11_PORT_DESC_REPLY. * OFPRAW_OFPST14_PORT_DESC_REPLY. */ OFPTYPE_FLOW_MONITOR_STATS_REQUEST, /* OFPRAW_OFPST14_FLOW_MONITOR_REQUEST. * OFPRAW_NXST_FLOW_MONITOR_REQUEST. */ OFPTYPE_FLOW_MONITOR_STATS_REPLY, /* OFPRAW_OFPST14_FLOW_MONITOR_REPLY. * OFPRAW_NXST_FLOW_MONITOR_REPLY. */ /* Nicira extensions. */ OFPTYPE_SET_FLOW_FORMAT, /* OFPRAW_NXT_SET_FLOW_FORMAT. */ OFPTYPE_FLOW_MOD_TABLE_ID, /* OFPRAW_NXT_FLOW_MOD_TABLE_ID. */ OFPTYPE_SET_PACKET_IN_FORMAT, /* OFPRAW_NXT_SET_PACKET_IN_FORMAT. */ OFPTYPE_FLOW_AGE, /* OFPRAW_NXT_FLOW_AGE. */ OFPTYPE_SET_CONTROLLER_ID, /* OFPRAW_NXT_SET_CONTROLLER_ID. */ OFPTYPE_NXT_TLV_TABLE_MOD, /* OFPRAW_NXT_TLV_TABLE_MOD. */ OFPTYPE_NXT_TLV_TABLE_REQUEST, /* OFPRAW_NXT_TLV_TABLE_REQUEST. */ OFPTYPE_NXT_TLV_TABLE_REPLY, /* OFPRAW_NXT_TLV_TABLE_REPLY. */ /* Flow monitor extension. */ OFPTYPE_FLOW_MONITOR_CANCEL, /* OFPRAW_NXT_FLOW_MONITOR_CANCEL. */ OFPTYPE_FLOW_MONITOR_PAUSED, /* OFPRAW_NXT_FLOW_MONITOR_PAUSED. */ OFPTYPE_FLOW_MONITOR_RESUMED, /* OFPRAW_NXT_FLOW_MONITOR_RESUMED. */ }; /* Decoding messages into OFPTYPE_* values. */ enum ofperr ofptype_decode(enum ofptype *, const struct ofp_header *); enum ofperr ofptype_pull(enum ofptype *, struct ofpbuf *); enum ofptype ofptype_from_ofpraw(enum ofpraw); /* Information about OFTYPE_* values. */ const char *ofptype_get_name(enum ofptype); /* OpenFlow message properties. */ void ofpmsg_update_length(struct ofpbuf *); const void *ofpmsg_body(const struct ofp_header *); bool ofpmsg_is_stat_request(const struct ofp_header *); /* Multipart messages (aka "statistics"). * * Individual OpenFlow messages are limited to 64 kB in size, but some messages * need to be longer. Therefore, multipart messages allow a longer message to * be divided into multiple parts at some convenient boundary. For example, * limiting the response to a "flow dump" request to 64 kB would unreasonably * limit the maximum number of flows in an OpenFlow switch, so a "flow dump" is * expressed as a multipart request/reply pair, with the reply broken into * pieces between flows. * * Multipart messages always consist of a request/reply pair. * * In OpenFlow 1.0, 1.1, and 1.2, requests must always fit in a single message, * that is, only a multipart reply may have more than one part. OpenFlow 1.3 * adds one multipart request. This code does not yet support multipart * requests. */ /* Encoding multipart replies. * * These functions are useful for multipart replies that might really require * more than one message. A multipart message that is known in advance to fit * within 64 kB doesn't need any special treatment, so you might as well use * the ofpraw_alloc_*() functions. * * These functions work with a "struct ovs_list" of "struct ofpbuf"s, each of * which represents one part of a multipart message. */ void ofpmp_init(struct ovs_list *, const struct ofp_header *request); struct ofpbuf *ofpmp_reserve(struct ovs_list *, size_t len); void *ofpmp_append(struct ovs_list *, size_t len); void ofpmp_postappend(struct ovs_list *, size_t start_ofs); enum ofp_version ofpmp_version(struct ovs_list *); enum ofpraw ofpmp_decode_raw(struct ovs_list *); /* Decoding multipart replies. */ uint16_t ofpmp_flags(const struct ofp_header *); bool ofpmp_more(const struct ofp_header *); #endif /* ofp-msgs.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/json.h0000644000000000000000000000013213534540071015534 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.745853161 openvswitch-2.5.9/lib/json.h0000644000175000017500000001053513534540071017226 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef JSON_H #define JSON_H 1 /* This is an implementation of JavaScript Object Notation (JSON) as specified * by RFC 4627. It is intended to fully comply with RFC 4627, with the * following known exceptions and clarifications: * * - Null bytes (\u0000) are not allowed in strings. * * - Only UTF-8 encoding is supported (RFC 4627 allows for other Unicode * encodings). * * - Names within an object must be unique (RFC 4627 says that they * "should" be unique). */ #include "shash.h" #ifdef __cplusplus extern "C" { #endif struct ds; /* Type of a JSON value. */ enum json_type { JSON_NULL, /* null */ JSON_FALSE, /* false */ JSON_TRUE, /* true */ JSON_OBJECT, /* {"a": b, "c": d, ...} */ JSON_ARRAY, /* [1, 2, 3, ...] */ JSON_INTEGER, /* 123. */ JSON_REAL, /* 123.456. */ JSON_STRING, /* "..." */ JSON_N_TYPES }; const char *json_type_to_string(enum json_type); /* A JSON array. */ struct json_array { size_t n, n_allocated; struct json **elems; }; /* A JSON value. */ struct json { enum json_type type; union { struct shash *object; /* Contains "struct json *"s. */ struct json_array array; long long int integer; double real; char *string; } u; }; struct json *json_null_create(void); struct json *json_boolean_create(bool); struct json *json_string_create(const char *); struct json *json_string_create_nocopy(char *); struct json *json_integer_create(long long int); struct json *json_real_create(double); struct json *json_array_create_empty(void); void json_array_add(struct json *, struct json *element); void json_array_trim(struct json *); struct json *json_array_create(struct json **, size_t n); struct json *json_array_create_1(struct json *); struct json *json_array_create_2(struct json *, struct json *); struct json *json_array_create_3(struct json *, struct json *, struct json *); struct json *json_object_create(void); void json_object_put(struct json *, const char *name, struct json *value); void json_object_put_string(struct json *, const char *name, const char *value); const char *json_string(const struct json *); struct json_array *json_array(const struct json *); struct shash *json_object(const struct json *); bool json_boolean(const struct json *); double json_real(const struct json *); int64_t json_integer(const struct json *); struct json *json_clone(const struct json *); void json_destroy(struct json *); size_t json_hash(const struct json *, size_t basis); bool json_equal(const struct json *, const struct json *); /* Parsing JSON. */ enum { JSPF_TRAILER = 1 << 0 /* Check for garbage following input. */ }; struct json_parser *json_parser_create(int flags); size_t json_parser_feed(struct json_parser *, const char *, size_t); bool json_parser_is_done(const struct json_parser *); struct json *json_parser_finish(struct json_parser *); void json_parser_abort(struct json_parser *); struct json *json_from_string(const char *string); struct json *json_from_file(const char *file_name); struct json *json_from_stream(FILE *stream); /* Serializing JSON. */ enum { JSSF_PRETTY = 1 << 0, /* Multiple lines with indentation, if true. */ JSSF_SORT = 1 << 1 /* Object members in sorted order, if true. */ }; char *json_to_string(const struct json *, int flags); void json_to_ds(const struct json *, int flags, struct ds *); /* JSON string formatting operations. */ bool json_string_unescape(const char *in, size_t in_len, char **outp); void json_string_escape(const char *in, struct ds *out); #ifdef __cplusplus } #endif #endif /* json.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/daemon-syn.man0000644000000000000000000000013213534540071017161 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801423.729845671 openvswitch-2.5.9/lib/daemon-syn.man0000644000175000017500000000020513534540071020644 0ustar00jpettitjpettit00000000000000.IP "Daemon options:" [\fB\-\-pidfile\fR[\fB=\fIpidfile\fR]] [\fB\-\-overwrite\-pidfile\fR] [\fB\-\-detach\fR] [\fB\-\-no\-chdir\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/stream-provider.h0000644000000000000000000000013213534540071017706 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.893854251 openvswitch-2.5.9/lib/stream-provider.h0000644000175000017500000001730113534540071021376 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STREAM_PROVIDER_H #define STREAM_PROVIDER_H 1 #include #include "stream.h" /* Active stream connection. */ /* Active stream connection. * * This structure should be treated as opaque by implementation. */ struct stream { const struct stream_class *class; int state; int error; char *name; }; void stream_init(struct stream *, const struct stream_class *, int connect_status, const char *name); static inline void stream_assert_class(const struct stream *stream, const struct stream_class *class) { ovs_assert(stream->class == class); } struct stream_class { /* Prefix for connection names, e.g. "tcp", "ssl", "unix". */ const char *name; /* True if this stream needs periodic probes to verify connectivity. For * streams which need probes, it can take a long time to notice the * connection was dropped. */ bool needs_probes; /* Attempts to connect to a peer. 'name' is the full connection name * provided by the user, e.g. "tcp:1.2.3.4". This name is useful for error * messages but must not be modified. * * 'suffix' is a copy of 'name' following the colon and may be modified. * 'dscp' is the DSCP value that the new connection should use in the IP * packets it sends. * * Returns 0 if successful, otherwise a positive errno value. If * successful, stores a pointer to the new connection in '*streamp'. * * The open function must not block waiting for a connection to complete. * If the connection cannot be completed immediately, it should return * EAGAIN (not EINPROGRESS, as returned by the connect system call) and * continue the connection in the background. */ int (*open)(const char *name, char *suffix, struct stream **streamp, uint8_t dscp); /* Closes 'stream' and frees associated memory. */ void (*close)(struct stream *stream); /* Tries to complete the connection on 'stream'. If 'stream''s connection * is complete, returns 0 if the connection was successful or a positive * errno value if it failed. If the connection is still in progress, * returns EAGAIN. * * The connect function must not block waiting for the connection to * complete; instead, it should return EAGAIN immediately. */ int (*connect)(struct stream *stream); /* Tries to receive up to 'n' bytes from 'stream' into 'buffer', and * returns: * * - If successful, the number of bytes received (between 1 and 'n'). * * - On error, a negative errno value. * * - 0, if the connection has been closed in the normal fashion. * * The recv function will not be passed a zero 'n'. * * The recv function must not block waiting for data to arrive. If no data * have been received, it should return -EAGAIN immediately. */ ssize_t (*recv)(struct stream *stream, void *buffer, size_t n); /* Tries to send up to 'n' bytes of 'buffer' on 'stream', and returns: * * - If successful, the number of bytes sent (between 1 and 'n'). * * - On error, a negative errno value. * * - Never returns 0. * * The send function will not be passed a zero 'n'. * * The send function must not block. If no bytes can be immediately * accepted for transmission, it should return -EAGAIN immediately. */ ssize_t (*send)(struct stream *stream, const void *buffer, size_t n); /* Allows 'stream' to perform maintenance activities, such as flushing * output buffers. * * May be null if 'stream' doesn't have anything to do here. */ void (*run)(struct stream *stream); /* Arranges for the poll loop to wake up when 'stream' needs to perform * maintenance activities. * * May be null if 'stream' doesn't have anything to do here. */ void (*run_wait)(struct stream *stream); /* Arranges for the poll loop to wake up when 'stream' is ready to take an * action of the given 'type'. */ void (*wait)(struct stream *stream, enum stream_wait_type type); }; /* Passive listener for incoming stream connections. * * This structure should be treated as opaque by stream implementations. */ struct pstream { const struct pstream_class *class; char *name; ovs_be16 bound_port; }; void pstream_init(struct pstream *, const struct pstream_class *, const char *name); void pstream_set_bound_port(struct pstream *, ovs_be16 bound_port); static inline void pstream_assert_class(const struct pstream *pstream, const struct pstream_class *class) { ovs_assert(pstream->class == class); } struct pstream_class { /* Prefix for connection names, e.g. "ptcp", "pssl", "punix". */ const char *name; /* True if this pstream needs periodic probes to verify connectivity. For * pstreams which need probes, it can take a long time to notice the * connection was dropped. */ bool needs_probes; /* Attempts to start listening for stream connections. 'name' is the full * connection name provided by the user, e.g. "ptcp:1234". This name is * useful for error messages but must not be modified. * * 'suffix' is a copy of 'name' following the colon and may be modified. * 'dscp' is the DSCP value that the new connection should use in the IP * packets it sends. * * Returns 0 if successful, otherwise a positive errno value. If * successful, stores a pointer to the new connection in '*pstreamp'. * * The listen function must not block. If the connection cannot be * completed immediately, it should return EAGAIN (not EINPROGRESS, as * returned by the connect system call) and continue the connection in the * background. */ int (*listen)(const char *name, char *suffix, struct pstream **pstreamp, uint8_t dscp); /* Closes 'pstream' and frees associated memory. */ void (*close)(struct pstream *pstream); /* Tries to accept a new connection on 'pstream'. If successful, stores * the new connection in '*new_streamp' and returns 0. Otherwise, returns * a positive errno value. * * The accept function must not block waiting for a connection. If no * connection is ready to be accepted, it should return EAGAIN. */ int (*accept)(struct pstream *pstream, struct stream **new_streamp); /* Arranges for the poll loop to wake up when a connection is ready to be * accepted on 'pstream'. */ void (*wait)(struct pstream *pstream); }; /* Active and passive stream classes. */ extern const struct stream_class tcp_stream_class; extern const struct pstream_class ptcp_pstream_class; #ifndef _WIN32 extern const struct stream_class unix_stream_class; extern const struct pstream_class punix_pstream_class; #else extern const struct stream_class windows_stream_class; extern const struct pstream_class pwindows_pstream_class; #endif #ifdef HAVE_OPENSSL extern const struct stream_class ssl_stream_class; extern const struct pstream_class pssl_pstream_class; #endif #endif /* stream-provider.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/route-table.h0000644000000000000000000000013213534540071017006 xustar0030 mtime=1567801401.581682492 30 atime=1567801402.093686252 30 ctime=1567801424.993854989 openvswitch-2.5.9/lib/route-table.h0000644000175000017500000000200613534540071020472 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ROUTE_TABLE_H #define ROUTE_TABLE_H 1 #include #include #include #include #include #include "openvswitch/types.h" uint64_t route_table_get_change_seq(void); void route_table_init(void); void route_table_run(void); void route_table_wait(void); bool route_table_fallback_lookup(ovs_be32, char [], ovs_be32 *); #endif /* route-table.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/packets.c0000644000000000000000000000013213534540071016210 xustar0030 mtime=1567801401.573682433 30 atime=1567801402.093686252 30 ctime=1567801424.845853898 openvswitch-2.5.9/lib/packets.c0000644000175000017500000011270513534540071017704 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "packets.h" #include #include #include #include #include #include #include "byte-order.h" #include "csum.h" #include "crc32c.h" #include "flow.h" #include "hmap.h" #include "dynamic-string.h" #include "ovs-thread.h" #include "odp-util.h" #include "dp-packet.h" #include "unaligned.h" const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT; const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT; struct in6_addr flow_tnl_dst(const struct flow_tnl *tnl) { return tnl->ip_dst ? in6_addr_mapped_ipv4(tnl->ip_dst) : tnl->ipv6_dst; } struct in6_addr flow_tnl_src(const struct flow_tnl *tnl) { return tnl->ip_src ? in6_addr_mapped_ipv4(tnl->ip_src) : tnl->ipv6_src; } /* Parses 's' as a 16-digit hexadecimal number representing a datapath ID. On * success stores the dpid into '*dpidp' and returns true, on failure stores 0 * into '*dpidp' and returns false. * * Rejects an all-zeros dpid as invalid. */ bool dpid_from_string(const char *s, uint64_t *dpidp) { *dpidp = (strlen(s) == 16 && strspn(s, "0123456789abcdefABCDEF") == 16 ? strtoull(s, NULL, 16) : 0); return *dpidp != 0; } /* Returns true if 'ea' is a reserved address, that a bridge must never * forward, false otherwise. * * If you change this function's behavior, please update corresponding * documentation in vswitch.xml at the same time. */ bool eth_addr_is_reserved(const struct eth_addr ea) { struct eth_addr_node { struct hmap_node hmap_node; const uint64_t ea64; }; static struct eth_addr_node nodes[] = { /* STP, IEEE pause frames, and other reserved protocols. */ { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000000ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000001ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000002ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000003ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000004ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000005ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000006ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000007ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000008ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c2000009ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000aULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000bULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000cULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000dULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000eULL }, { HMAP_NODE_NULL_INITIALIZER, 0x0180c200000fULL }, /* Extreme protocols. */ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000000ULL }, /* EDP. */ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000004ULL }, /* EAPS. */ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000006ULL }, /* EAPS. */ /* Cisco protocols. */ { HMAP_NODE_NULL_INITIALIZER, 0x01000c000000ULL }, /* ISL. */ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccccULL }, /* PAgP, UDLD, CDP, * DTP, VTP. */ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccccccdULL }, /* PVST+. */ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccdcdcdULL }, /* STP Uplink Fast, * FlexLink. */ /* Cisco CFM. */ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc0ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc1ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc2ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc3ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc4ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc5ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc6ULL }, { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc7ULL }, }; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct eth_addr_node *node; static struct hmap addrs; uint64_t ea64; if (ovsthread_once_start(&once)) { hmap_init(&addrs); for (node = nodes; node < &nodes[ARRAY_SIZE(nodes)]; node++) { hmap_insert(&addrs, &node->hmap_node, hash_uint64(node->ea64)); } ovsthread_once_done(&once); } ea64 = eth_addr_to_uint64(ea); HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_uint64(ea64), &addrs) { if (node->ea64 == ea64) { return true; } } return false; } bool eth_addr_from_string(const char *s, struct eth_addr *ea) { if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) { return true; } else { *ea = eth_addr_zero; return false; } } /* Fills 'b' with a Reverse ARP packet with Ethernet source address 'eth_src'. * This function is used by Open vSwitch to compose packets in cases where * context is important but content doesn't (or shouldn't) matter. * * The returned packet has enough headroom to insert an 802.1Q VLAN header if * desired. */ void compose_rarp(struct dp_packet *b, const struct eth_addr eth_src) { struct eth_header *eth; struct arp_eth_header *arp; dp_packet_clear(b); dp_packet_prealloc_tailroom(b, 2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + ARP_ETH_HEADER_LEN); dp_packet_reserve(b, 2 + VLAN_HEADER_LEN); eth = dp_packet_put_uninit(b, sizeof *eth); eth->eth_dst = eth_addr_broadcast; eth->eth_src = eth_src; eth->eth_type = htons(ETH_TYPE_RARP); arp = dp_packet_put_uninit(b, sizeof *arp); arp->ar_hrd = htons(ARP_HRD_ETHERNET); arp->ar_pro = htons(ARP_PRO_IP); arp->ar_hln = sizeof arp->ar_sha; arp->ar_pln = sizeof arp->ar_spa; arp->ar_op = htons(ARP_OP_RARP); arp->ar_sha = eth_src; put_16aligned_be32(&arp->ar_spa, htonl(0)); arp->ar_tha = eth_src; put_16aligned_be32(&arp->ar_tpa, htonl(0)); dp_packet_reset_offsets(b); dp_packet_set_l3(b, arp); } /* Insert VLAN header according to given TCI. Packet passed must be Ethernet * packet. Ignores the CFI bit of 'tci' using 0 instead. * * Also adjusts the layer offsets accordingly. */ void eth_push_vlan(struct dp_packet *packet, ovs_be16 tpid, ovs_be16 tci) { struct vlan_eth_header *veh; /* Insert new 802.1Q header. */ veh = dp_packet_resize_l2(packet, VLAN_HEADER_LEN); memmove(veh, (char *)veh + VLAN_HEADER_LEN, 2 * ETH_ADDR_LEN); veh->veth_type = tpid; veh->veth_tci = tci & htons(~VLAN_CFI); } /* Removes outermost VLAN header (if any is present) from 'packet'. * * 'packet->l2_5' should initially point to 'packet''s outer-most VLAN header * or may be NULL if there are no VLAN headers. */ void eth_pop_vlan(struct dp_packet *packet) { struct vlan_eth_header *veh = dp_packet_l2(packet); if (veh && dp_packet_size(packet) >= sizeof *veh && eth_type_vlan(veh->veth_type)) { memmove((char *)veh + VLAN_HEADER_LEN, veh, 2 * ETH_ADDR_LEN); dp_packet_resize_l2(packet, -VLAN_HEADER_LEN); } } /* Set ethertype of the packet. */ static void set_ethertype(struct dp_packet *packet, ovs_be16 eth_type) { struct eth_header *eh = dp_packet_l2(packet); if (!eh) { return; } if (eth_type_vlan(eh->eth_type)) { ovs_be16 *p; char *l2_5 = dp_packet_l2_5(packet); p = ALIGNED_CAST(ovs_be16 *, (l2_5 ? l2_5 : (char *)dp_packet_l3(packet)) - 2); *p = eth_type; } else { eh->eth_type = eth_type; } } static bool is_mpls(struct dp_packet *packet) { return packet->l2_5_ofs != UINT16_MAX; } /* Set time to live (TTL) of an MPLS label stack entry (LSE). */ void set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl) { *lse &= ~htonl(MPLS_TTL_MASK); *lse |= htonl((ttl << MPLS_TTL_SHIFT) & MPLS_TTL_MASK); } /* Set traffic class (TC) of an MPLS label stack entry (LSE). */ void set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc) { *lse &= ~htonl(MPLS_TC_MASK); *lse |= htonl((tc << MPLS_TC_SHIFT) & MPLS_TC_MASK); } /* Set label of an MPLS label stack entry (LSE). */ void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label) { *lse &= ~htonl(MPLS_LABEL_MASK); *lse |= htonl((ntohl(label) << MPLS_LABEL_SHIFT) & MPLS_LABEL_MASK); } /* Set bottom of stack (BoS) bit of an MPLS label stack entry (LSE). */ void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos) { *lse &= ~htonl(MPLS_BOS_MASK); *lse |= htonl((bos << MPLS_BOS_SHIFT) & MPLS_BOS_MASK); } /* Compose an MPLS label stack entry (LSE) from its components: * label, traffic class (TC), time to live (TTL) and * bottom of stack (BoS) bit. */ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, ovs_be32 label) { ovs_be32 lse = htonl(0); set_mpls_lse_ttl(&lse, ttl); set_mpls_lse_tc(&lse, tc); set_mpls_lse_bos(&lse, bos); set_mpls_lse_label(&lse, label); return lse; } /* Set MPLS label stack entry to outermost MPLS header.*/ void set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse) { /* Packet type should be MPLS to set label stack entry. */ if (is_mpls(packet)) { struct mpls_hdr *mh = dp_packet_l2_5(packet); /* Update mpls label stack entry. */ put_16aligned_be32(&mh->mpls_lse, mpls_lse); } } /* Push MPLS label stack entry 'lse' onto 'packet' as the outermost MPLS * header. If 'packet' does not already have any MPLS labels, then its * Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */ void push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse) { char * header; size_t len; if (!eth_type_mpls(ethtype)) { return; } if (!is_mpls(packet)) { /* Set MPLS label stack offset. */ packet->l2_5_ofs = packet->l3_ofs; } set_ethertype(packet, ethtype); /* Push new MPLS shim header onto packet. */ len = packet->l2_5_ofs; header = dp_packet_resize_l2_5(packet, MPLS_HLEN); memmove(header, header + MPLS_HLEN, len); memcpy(header + len, &lse, sizeof lse); } /* If 'packet' is an MPLS packet, removes its outermost MPLS label stack entry. * If the label that was removed was the only MPLS label, changes 'packet''s * Ethertype to 'ethtype' (which ordinarily should not be an MPLS * Ethertype). */ void pop_mpls(struct dp_packet *packet, ovs_be16 ethtype) { if (is_mpls(packet)) { struct mpls_hdr *mh = dp_packet_l2_5(packet); size_t len = packet->l2_5_ofs; set_ethertype(packet, ethtype); if (get_16aligned_be32(&mh->mpls_lse) & htonl(MPLS_BOS_MASK)) { dp_packet_set_l2_5(packet, NULL); } /* Shift the l2 header forward. */ memmove((char*)dp_packet_data(packet) + MPLS_HLEN, dp_packet_data(packet), len); dp_packet_resize_l2_5(packet, -MPLS_HLEN); } } /* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'. The * caller must free '*packetp'. On success, returns NULL. On failure, returns * an error message and stores NULL in '*packetp'. * * Aligns the L3 header of '*packetp' on a 32-bit boundary. */ const char * eth_from_hex(const char *hex, struct dp_packet **packetp) { struct dp_packet *packet; /* Use 2 bytes of headroom to 32-bit align the L3 header. */ packet = *packetp = dp_packet_new_with_headroom(strlen(hex) / 2, 2); if (dp_packet_put_hex(packet, hex, NULL)[0] != '\0') { dp_packet_delete(packet); *packetp = NULL; return "Trailing garbage in packet data"; } if (dp_packet_size(packet) < ETH_HEADER_LEN) { dp_packet_delete(packet); *packetp = NULL; return "Packet data too short for Ethernet"; } return NULL; } void eth_format_masked(const struct eth_addr eth, const struct eth_addr *mask, struct ds *s) { ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth)); if (mask && !eth_mask_is_exact(*mask)) { ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(*mask)); } } /* Given the IP netmask 'netmask', returns the number of bits of the IP address * that it specifies, that is, the number of 1-bits in 'netmask'. * * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will * still be in the valid range but isn't otherwise meaningful. */ int ip_count_cidr_bits(ovs_be32 netmask) { return 32 - ctz32(ntohl(netmask)); } void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s) { ds_put_format(s, IP_FMT, IP_ARGS(ip)); if (mask != OVS_BE32_MAX) { if (ip_is_cidr(mask)) { ds_put_format(s, "/%d", ip_count_cidr_bits(mask)); } else { ds_put_format(s, "/"IP_FMT, IP_ARGS(mask)); } } } /* Parses string 's', which must be an IP address with an optional netmask or * CIDR prefix length. Stores the IP address into '*ip' and the netmask into * '*mask'. (If 's' does not contain a netmask, 255.255.255.255 is * assumed.) * * Returns NULL if successful, otherwise an error message that the caller must * free(). */ char * OVS_WARN_UNUSED_RESULT ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask) { int prefix; int n; if (ovs_scan(s, IP_SCAN_FMT"/"IP_SCAN_FMT"%n", IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask), &n) && !s[n]) { /* OK. */ } else if (ovs_scan(s, IP_SCAN_FMT"/%d%n", IP_SCAN_ARGS(ip), &prefix, &n) && !s[n]) { if (prefix <= 0 || prefix > 32) { return xasprintf("%s: network prefix bits not between 0 and " "32", s); } *mask = be32_prefix_mask(prefix); } else if (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(ip), &n) && !s[n]) { *mask = OVS_BE32_MAX; } else { return xasprintf("%s: invalid IP address", s); } return NULL; } void ipv6_format_addr(const struct in6_addr *addr, struct ds *s) { char *dst; ds_reserve(s, s->length + INET6_ADDRSTRLEN); dst = s->string + s->length; inet_ntop(AF_INET6, addr, dst, INET6_ADDRSTRLEN); s->length += strlen(dst); } void ipv6_format_mapped(const struct in6_addr *addr, struct ds *s) { if (IN6_IS_ADDR_V4MAPPED(addr)) { ds_put_format(s, IP_FMT, addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } else { ipv6_format_addr(addr, s); } } void ipv6_format_masked(const struct in6_addr *addr, const struct in6_addr *mask, struct ds *s) { ipv6_format_addr(addr, s); if (mask && !ipv6_mask_is_exact(mask)) { if (ipv6_is_cidr(mask)) { int cidr_bits = ipv6_count_cidr_bits(mask); ds_put_format(s, "/%d", cidr_bits); } else { ds_put_char(s, '/'); ipv6_format_addr(mask, s); } } } /* Stores the string representation of the IPv6 address 'addr' into the * character array 'addr_str', which must be at least INET6_ADDRSTRLEN * bytes long. If addr is IPv4-mapped, store an IPv4 dotted-decimal string. */ const char * ipv6_string_mapped(char *addr_str, const struct in6_addr *addr) { ovs_be32 ip; ip = in6_addr_get_mapped_ipv4(addr); if (ip) { return inet_ntop(AF_INET, &ip, addr_str, INET6_ADDRSTRLEN); } else { return inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN); } } struct in6_addr ipv6_addr_bitand(const struct in6_addr *a, const struct in6_addr *b) { int i; struct in6_addr dst; #ifdef s6_addr32 for (i=0; i<4; i++) { dst.s6_addr32[i] = a->s6_addr32[i] & b->s6_addr32[i]; } #else for (i=0; i<16; i++) { dst.s6_addr[i] = a->s6_addr[i] & b->s6_addr[i]; } #endif return dst; } /* Returns an in6_addr consisting of 'mask' high-order 1-bits and 128-N * low-order 0-bits. */ struct in6_addr ipv6_create_mask(int mask) { struct in6_addr netmask; uint8_t *netmaskp = &netmask.s6_addr[0]; memset(&netmask, 0, sizeof netmask); while (mask > 8) { *netmaskp = 0xff; netmaskp++; mask -= 8; } if (mask) { *netmaskp = 0xff << (8 - mask); } return netmask; } /* Given the IPv6 netmask 'netmask', returns the number of bits of the IPv6 * address that it specifies, that is, the number of 1-bits in 'netmask'. * 'netmask' must be a CIDR netmask (see ipv6_is_cidr()). * * If 'netmask' is not a CIDR netmask (see ipv6_is_cidr()), the return value * will still be in the valid range but isn't otherwise meaningful. */ int ipv6_count_cidr_bits(const struct in6_addr *netmask) { int i; int count = 0; const uint8_t *netmaskp = &netmask->s6_addr[0]; for (i=0; i<16; i++) { if (netmaskp[i] == 0xff) { count += 8; } else { uint8_t nm; for(nm = netmaskp[i]; nm; nm <<= 1) { count++; } break; } } return count; } /* Returns true if 'netmask' is a CIDR netmask, that is, if it consists of N * high-order 1-bits and 128-N low-order 0-bits. */ bool ipv6_is_cidr(const struct in6_addr *netmask) { const uint8_t *netmaskp = &netmask->s6_addr[0]; int i; for (i=0; i<16; i++) { if (netmaskp[i] != 0xff) { uint8_t x = ~netmaskp[i]; if (x & (x + 1)) { return false; } while (++i < 16) { if (netmaskp[i]) { return false; } } } } return true; } /* Parses string 's', which must be an IPv6 address with an optional * CIDR prefix length. Stores the IP address into '*ipv6' and the CIDR * prefix in '*prefix'. (If 's' does not contain a CIDR length, all-ones * is assumed.) * * Returns NULL if successful, otherwise an error message that the caller must * free(). */ char * OVS_WARN_UNUSED_RESULT ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask) { char ipv6_s[IPV6_SCAN_LEN + 1]; char mask_s[IPV6_SCAN_LEN + 1]; int prefix; int n; if (ovs_scan(s, IPV6_SCAN_FMT"/"IPV6_SCAN_FMT"%n", ipv6_s, mask_s, &n) && inet_pton(AF_INET6, ipv6_s, ipv6) == 1 && inet_pton(AF_INET6, mask_s, mask) == 1 && !s[n]) { /* OK. */ } else if (ovs_scan(s, IPV6_SCAN_FMT"/%d%n", ipv6_s, &prefix, &n) && inet_pton(AF_INET6, ipv6_s, ipv6) == 1 && !s[n]) { if (prefix <= 0 || prefix > 128) { return xasprintf("%s: prefix bits not between 0 and 128", s); } *mask = ipv6_create_mask(prefix); } else if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && inet_pton(AF_INET6, ipv6_s, ipv6) == 1 && !s[n]) { *mask = in6addr_exact; } else { return xasprintf("%s: invalid IP address", s); } return NULL; } /* Populates 'b' with an Ethernet II packet headed with the given 'eth_dst', * 'eth_src' and 'eth_type' parameters. A payload of 'size' bytes is allocated * in 'b' and returned. This payload may be populated with appropriate * information by the caller. Sets 'b''s 'frame' pointer and 'l3' offset to * the Ethernet header and payload respectively. Aligns b->l3 on a 32-bit * boundary. * * The returned packet has enough headroom to insert an 802.1Q VLAN header if * desired. */ void * eth_compose(struct dp_packet *b, const struct eth_addr eth_dst, const struct eth_addr eth_src, uint16_t eth_type, size_t size) { void *data; struct eth_header *eth; dp_packet_clear(b); /* The magic 2 here ensures that the L3 header (when it is added later) * will be 32-bit aligned. */ dp_packet_prealloc_tailroom(b, 2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + size); dp_packet_reserve(b, 2 + VLAN_HEADER_LEN); eth = dp_packet_put_uninit(b, ETH_HEADER_LEN); data = dp_packet_put_uninit(b, size); eth->eth_dst = eth_dst; eth->eth_src = eth_src; eth->eth_type = htons(eth_type); dp_packet_reset_offsets(b); dp_packet_set_l3(b, data); return data; } static void packet_set_ipv4_addr(struct dp_packet *packet, ovs_16aligned_be32 *addr, ovs_be32 new_addr) { struct ip_header *nh = dp_packet_l3(packet); ovs_be32 old_addr = get_16aligned_be32(addr); size_t l4_size = dp_packet_l4_size(packet); if (nh->ip_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) { struct tcp_header *th = dp_packet_l4(packet); th->tcp_csum = recalc_csum32(th->tcp_csum, old_addr, new_addr); } else if (nh->ip_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN ) { struct udp_header *uh = dp_packet_l4(packet); if (uh->udp_csum) { uh->udp_csum = recalc_csum32(uh->udp_csum, old_addr, new_addr); if (!uh->udp_csum) { uh->udp_csum = htons(0xffff); } } } nh->ip_csum = recalc_csum32(nh->ip_csum, old_addr, new_addr); put_16aligned_be32(addr, new_addr); } /* Returns true, if packet contains at least one routing header where * segements_left > 0. * * This function assumes that L3 and L4 offsets are set in the packet. */ static bool packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr) { const struct ovs_16aligned_ip6_hdr *nh; size_t len; size_t remaining; uint8_t *data = dp_packet_l3(packet); remaining = packet->l4_ofs - packet->l3_ofs; if (remaining < sizeof *nh) { return false; } nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data); data += sizeof *nh; remaining -= sizeof *nh; *nexthdr = nh->ip6_nxt; while (1) { if ((*nexthdr != IPPROTO_HOPOPTS) && (*nexthdr != IPPROTO_ROUTING) && (*nexthdr != IPPROTO_DSTOPTS) && (*nexthdr != IPPROTO_AH) && (*nexthdr != IPPROTO_FRAGMENT)) { /* It's either a terminal header (e.g., TCP, UDP) or one we * don't understand. In either case, we're done with the * packet, so use it to fill in 'nw_proto'. */ break; } /* We only verify that at least 8 bytes of the next header are * available, but many of these headers are longer. Ensure that * accesses within the extension header are within those first 8 * bytes. All extension headers are required to be at least 8 * bytes. */ if (remaining < 8) { return false; } if (*nexthdr == IPPROTO_AH) { /* A standard AH definition isn't available, but the fields * we care about are in the same location as the generic * option header--only the header length is calculated * differently. */ const struct ip6_ext *ext_hdr = (struct ip6_ext *)data; *nexthdr = ext_hdr->ip6e_nxt; len = (ext_hdr->ip6e_len + 2) * 4; } else if (*nexthdr == IPPROTO_FRAGMENT) { const struct ovs_16aligned_ip6_frag *frag_hdr = ALIGNED_CAST(struct ovs_16aligned_ip6_frag *, data); *nexthdr = frag_hdr->ip6f_nxt; len = sizeof *frag_hdr; } else if (*nexthdr == IPPROTO_ROUTING) { const struct ip6_rthdr *rh = (struct ip6_rthdr *)data; if (rh->ip6r_segleft > 0) { return true; } *nexthdr = rh->ip6r_nxt; len = (rh->ip6r_len + 1) * 8; } else { const struct ip6_ext *ext_hdr = (struct ip6_ext *)data; *nexthdr = ext_hdr->ip6e_nxt; len = (ext_hdr->ip6e_len + 1) * 8; } if (remaining < len) { return false; } remaining -= len; data += len; } return false; } static void packet_update_csum128(struct dp_packet *packet, uint8_t proto, ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4]) { size_t l4_size = dp_packet_l4_size(packet); if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) { struct tcp_header *th = dp_packet_l4(packet); th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr); } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) { struct udp_header *uh = dp_packet_l4(packet); if (uh->udp_csum) { uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr); if (!uh->udp_csum) { uh->udp_csum = htons(0xffff); } } } else if (proto == IPPROTO_ICMPV6 && l4_size >= sizeof(struct icmp6_header)) { struct icmp6_header *icmp = dp_packet_l4(packet); icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr, new_addr); } } static void packet_set_ipv6_addr(struct dp_packet *packet, uint8_t proto, ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4], bool recalculate_csum) { if (recalculate_csum) { packet_update_csum128(packet, proto, addr, new_addr); } memcpy(addr, new_addr, sizeof(ovs_be32[4])); } static void packet_set_ipv6_flow_label(ovs_16aligned_be32 *flow_label, ovs_be32 flow_key) { ovs_be32 old_label = get_16aligned_be32(flow_label); ovs_be32 new_label = (old_label & htonl(~IPV6_LABEL_MASK)) | flow_key; put_16aligned_be32(flow_label, new_label); } static void packet_set_ipv6_tc(ovs_16aligned_be32 *flow_label, uint8_t tc) { ovs_be32 old_label = get_16aligned_be32(flow_label); ovs_be32 new_label = (old_label & htonl(0xF00FFFFF)) | htonl(tc << 20); put_16aligned_be32(flow_label, new_label); } /* Modifies the IPv4 header fields of 'packet' to be consistent with 'src', * 'dst', 'tos', and 'ttl'. Updates 'packet''s L4 checksums as appropriate. * 'packet' must contain a valid IPv4 packet with correctly populated l[347] * markers. */ void packet_set_ipv4(struct dp_packet *packet, ovs_be32 src, ovs_be32 dst, uint8_t tos, uint8_t ttl) { struct ip_header *nh = dp_packet_l3(packet); if (get_16aligned_be32(&nh->ip_src) != src) { packet_set_ipv4_addr(packet, &nh->ip_src, src); } if (get_16aligned_be32(&nh->ip_dst) != dst) { packet_set_ipv4_addr(packet, &nh->ip_dst, dst); } if (nh->ip_tos != tos) { uint8_t *field = &nh->ip_tos; nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t) *field), htons((uint16_t) tos)); *field = tos; } if (nh->ip_ttl != ttl) { uint8_t *field = &nh->ip_ttl; nh->ip_csum = recalc_csum16(nh->ip_csum, htons(*field << 8), htons(ttl << 8)); *field = ttl; } } /* Modifies the IPv6 header fields of 'packet' to be consistent with 'src', * 'dst', 'traffic class', and 'next hop'. Updates 'packet''s L4 checksums as * appropriate. 'packet' must contain a valid IPv6 packet with correctly * populated l[34] offsets. */ void packet_set_ipv6(struct dp_packet *packet, const ovs_be32 src[4], const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl, uint8_t key_hl) { struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet); uint8_t proto = 0; bool rh_present; rh_present = packet_rh_present(packet, &proto); if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) { packet_set_ipv6_addr(packet, proto, nh->ip6_src.be32, src, true); } if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) { packet_set_ipv6_addr(packet, proto, nh->ip6_dst.be32, dst, !rh_present); } packet_set_ipv6_tc(&nh->ip6_flow, key_tc); packet_set_ipv6_flow_label(&nh->ip6_flow, key_fl); nh->ip6_hlim = key_hl; } static void packet_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum) { if (*port != new_port) { *csum = recalc_csum16(*csum, *port, new_port); *port = new_port; } } /* Sets the TCP source and destination port ('src' and 'dst' respectively) of * the TCP header contained in 'packet'. 'packet' must be a valid TCP packet * with its l4 offset properly populated. */ void packet_set_tcp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst) { struct tcp_header *th = dp_packet_l4(packet); packet_set_port(&th->tcp_src, src, &th->tcp_csum); packet_set_port(&th->tcp_dst, dst, &th->tcp_csum); } /* Sets the UDP source and destination port ('src' and 'dst' respectively) of * the UDP header contained in 'packet'. 'packet' must be a valid UDP packet * with its l4 offset properly populated. */ void packet_set_udp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst) { struct udp_header *uh = dp_packet_l4(packet); if (uh->udp_csum) { packet_set_port(&uh->udp_src, src, &uh->udp_csum); packet_set_port(&uh->udp_dst, dst, &uh->udp_csum); if (!uh->udp_csum) { uh->udp_csum = htons(0xffff); } } else { uh->udp_src = src; uh->udp_dst = dst; } } /* Sets the SCTP source and destination port ('src' and 'dst' respectively) of * the SCTP header contained in 'packet'. 'packet' must be a valid SCTP packet * with its l4 offset properly populated. */ void packet_set_sctp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst) { struct sctp_header *sh = dp_packet_l4(packet); ovs_be32 old_csum, old_correct_csum, new_csum; uint16_t tp_len = dp_packet_l4_size(packet); old_csum = get_16aligned_be32(&sh->sctp_csum); put_16aligned_be32(&sh->sctp_csum, 0); old_correct_csum = crc32c((void *)sh, tp_len); sh->sctp_src = src; sh->sctp_dst = dst; new_csum = crc32c((void *)sh, tp_len); put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum); } /* Sets the ICMP type and code of the ICMP header contained in 'packet'. * 'packet' must be a valid ICMP packet with its l4 offset properly * populated. */ void packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code) { struct icmp_header *ih = dp_packet_l4(packet); ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code); ovs_be16 new_tc = htons(type << 8 | code); if (orig_tc != new_tc) { ih->icmp_type = type; ih->icmp_code = code; ih->icmp_csum = recalc_csum16(ih->icmp_csum, orig_tc, new_tc); } } void packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4], const struct eth_addr sll, const struct eth_addr tll) { struct ovs_nd_msg *ns; struct ovs_nd_opt *nd_opt; int bytes_remain = dp_packet_l4_size(packet); if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) { return; } ns = dp_packet_l4(packet); nd_opt = &ns->options[0]; bytes_remain -= sizeof(*ns); if (memcmp(&ns->target, target, sizeof(ovs_be32[4]))) { packet_set_ipv6_addr(packet, IPPROTO_ICMPV6, ns->target.be32, target, true); } while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) { if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR && nd_opt->nd_opt_len == 1) { if (!eth_addr_equals(nd_opt->nd_opt_mac, sll)) { ovs_be16 *csum = &(ns->icmph.icmp6_cksum); *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, sll); nd_opt->nd_opt_mac = sll; } /* A packet can only contain one SLL or TLL option */ break; } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR && nd_opt->nd_opt_len == 1) { if (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) { ovs_be16 *csum = &(ns->icmph.icmp6_cksum); *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, tll); nd_opt->nd_opt_mac = tll; } /* A packet can only contain one SLL or TLL option */ break; } nd_opt += nd_opt->nd_opt_len; bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN; } } const char * packet_tcp_flag_to_string(uint32_t flag) { switch (flag) { case TCP_FIN: return "fin"; case TCP_SYN: return "syn"; case TCP_RST: return "rst"; case TCP_PSH: return "psh"; case TCP_ACK: return "ack"; case TCP_URG: return "urg"; case TCP_ECE: return "ece"; case TCP_CWR: return "cwr"; case TCP_NS: return "ns"; case 0x200: return "[200]"; case 0x400: return "[400]"; case 0x800: return "[800]"; default: return NULL; } } /* Appends a string representation of the TCP flags value 'tcp_flags' * (e.g. from struct flow.tcp_flags or obtained via TCP_FLAGS) to 's', in the * format used by tcpdump. */ void packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags) { if (!tcp_flags) { ds_put_cstr(s, "none"); return; } if (tcp_flags & TCP_SYN) { ds_put_char(s, 'S'); } if (tcp_flags & TCP_FIN) { ds_put_char(s, 'F'); } if (tcp_flags & TCP_PSH) { ds_put_char(s, 'P'); } if (tcp_flags & TCP_RST) { ds_put_char(s, 'R'); } if (tcp_flags & TCP_URG) { ds_put_char(s, 'U'); } if (tcp_flags & TCP_ACK) { ds_put_char(s, '.'); } if (tcp_flags & TCP_ECE) { ds_put_cstr(s, "E"); } if (tcp_flags & TCP_CWR) { ds_put_cstr(s, "C"); } if (tcp_flags & TCP_NS) { ds_put_cstr(s, "N"); } if (tcp_flags & 0x200) { ds_put_cstr(s, "[200]"); } if (tcp_flags & 0x400) { ds_put_cstr(s, "[400]"); } if (tcp_flags & 0x800) { ds_put_cstr(s, "[800]"); } } #define ARP_PACKET_SIZE (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \ ARP_ETH_HEADER_LEN) /* Clears 'b' and replaces its contents by an ARP frame with the specified * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination * 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if * 'broadcast' is true. */ void compose_arp(struct dp_packet *b, uint16_t arp_op, const struct eth_addr arp_sha, const struct eth_addr arp_tha, bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa) { struct eth_header *eth; struct arp_eth_header *arp; dp_packet_clear(b); dp_packet_prealloc_tailroom(b, ARP_PACKET_SIZE); dp_packet_reserve(b, 2 + VLAN_HEADER_LEN); eth = dp_packet_put_uninit(b, sizeof *eth); eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha; eth->eth_src = arp_sha; eth->eth_type = htons(ETH_TYPE_ARP); arp = dp_packet_put_uninit(b, sizeof *arp); arp->ar_hrd = htons(ARP_HRD_ETHERNET); arp->ar_pro = htons(ARP_PRO_IP); arp->ar_hln = sizeof arp->ar_sha; arp->ar_pln = sizeof arp->ar_spa; arp->ar_op = htons(arp_op); arp->ar_sha = arp_sha; arp->ar_tha = arp_tha; put_16aligned_be32(&arp->ar_spa, arp_spa); put_16aligned_be32(&arp->ar_tpa, arp_tpa); dp_packet_reset_offsets(b); dp_packet_set_l3(b, arp); } void compose_nd(struct dp_packet *b, const struct eth_addr eth_src, struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst) { struct in6_addr sn_addr; struct eth_addr eth_dst; struct ovs_nd_msg *ns; struct ovs_nd_opt *nd_opt; in6_addr_solicited_node(&sn_addr, ipv6_dst); ipv6_multicast_to_ethernet(ð_dst, &sn_addr); eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN + ICMP6_HEADER_LEN + ND_OPT_LEN); packet_set_ipv6(b, ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr), ALIGNED_CAST(ovs_be32 *, sn_addr.s6_addr), 0, 0, 255); ns = dp_packet_l4(b); nd_opt = &ns->options[0]; ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT; ns->icmph.icmp6_code = 0; nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr), eth_src, eth_addr_zero); } uint32_t packet_csum_pseudoheader(const struct ip_header *ip) { uint32_t partial = 0; partial = csum_add32(partial, get_16aligned_be32(&ip->ip_src)); partial = csum_add32(partial, get_16aligned_be32(&ip->ip_dst)); partial = csum_add16(partial, htons(ip->ip_proto)); partial = csum_add16(partial, htons(ntohs(ip->ip_tot_len) - IP_IHL(ip->ip_ihl_ver) * 4)); return partial; } #ifndef __CHECKER__ uint32_t packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *ip6) { uint32_t partial = 0; partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[0]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[1]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[2]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[3]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[0]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[1]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[2]))); partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[3]))); partial = csum_add16(partial, 0); partial = csum_add16(partial, ip6->ip6_plen); partial = csum_add16(partial, 0); partial = csum_add16(partial, ip6->ip6_nxt); return partial; } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/netdev.h0000644000000000000000000000013213534540071016050 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 30 ctime=1567801424.777853396 openvswitch-2.5.9/lib/netdev.h0000644000175000017500000003147113534540071017544 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETDEV_H #define NETDEV_H 1 #include #include #include #include "openvswitch/types.h" #include "packets.h" #include "flow.h" #ifdef __cplusplus extern "C" { #endif /* Generic interface to network devices ("netdev"s). * * Every port on a switch must have a corresponding netdev that must minimally * support a few operations, such as the ability to read the netdev's MTU. * The PORTING file at the top of the source tree has more information in the * "Writing a netdev Provider" section. * * Thread-safety * ============= * * Most of the netdev functions are fully thread-safe: they may be called from * any number of threads on the same or different netdev objects. The * exceptions are: * * netdev_rxq_recv() * netdev_rxq_wait() * netdev_rxq_drain() * * These functions are conditionally thread-safe: they may be called from * different threads only on different netdev_rxq objects. (The client may * create multiple netdev_rxq objects for a single netdev and access each * of those from a different thread.) * * NETDEV_FOR_EACH_QUEUE * netdev_queue_dump_next() * netdev_queue_dump_done() * * These functions are conditionally thread-safe: they may be called from * different threads only on different netdev_queue_dump objects. (The * client may create multiple netdev_queue_dump objects for a single * netdev and access each of those from a different thread.) */ struct dp_packet; struct netdev; struct netdev_class; struct netdev_rxq; struct netdev_saved_flags; struct ofpbuf; struct in_addr; struct in6_addr; struct smap; struct sset; struct ovs_action_push_tnl; /* Network device statistics. * * Values of unsupported statistics are set to all-1-bits (UINT64_MAX). */ struct netdev_stats { uint64_t rx_packets; /* Total packets received. */ uint64_t tx_packets; /* Total packets transmitted. */ uint64_t rx_bytes; /* Total bytes received. */ uint64_t tx_bytes; /* Total bytes transmitted. */ uint64_t rx_errors; /* Bad packets received. */ uint64_t tx_errors; /* Packet transmit problems. */ uint64_t rx_dropped; /* No buffer space. */ uint64_t tx_dropped; /* No buffer space. */ uint64_t multicast; /* Multicast packets received. */ uint64_t collisions; /* Detailed receive errors. */ uint64_t rx_length_errors; uint64_t rx_over_errors; /* Receiver ring buff overflow. */ uint64_t rx_crc_errors; /* Recved pkt with crc error. */ uint64_t rx_frame_errors; /* Recv'd frame alignment error. */ uint64_t rx_fifo_errors; /* Recv'r fifo overrun . */ uint64_t rx_missed_errors; /* Receiver missed packet. */ /* Detailed transmit errors. */ uint64_t tx_aborted_errors; uint64_t tx_carrier_errors; uint64_t tx_fifo_errors; uint64_t tx_heartbeat_errors; uint64_t tx_window_errors; }; /* Configuration specific to tunnels. */ struct netdev_tunnel_config { bool in_key_present; bool in_key_flow; ovs_be64 in_key; bool out_key_present; bool out_key_flow; ovs_be64 out_key; ovs_be16 dst_port; bool ip_src_flow; bool ip_dst_flow; struct in6_addr ipv6_src; struct in6_addr ipv6_dst; uint32_t exts; uint8_t ttl; bool ttl_inherit; uint8_t tos; bool tos_inherit; bool csum; bool ipsec; bool dont_fragment; }; void netdev_run(void); void netdev_wait(void); void netdev_enumerate_types(struct sset *types); bool netdev_is_reserved_name(const char *name); int netdev_n_txq(const struct netdev *netdev); int netdev_n_rxq(const struct netdev *netdev); bool netdev_is_pmd(const struct netdev *netdev); /* Open and close. */ int netdev_open(const char *name, const char *type, struct netdev **netdevp); struct netdev *netdev_ref(const struct netdev *); void netdev_remove(struct netdev *); void netdev_close(struct netdev *); void netdev_parse_name(const char *netdev_name, char **name, char **type); /* Options. */ int netdev_set_config(struct netdev *, const struct smap *args, char **errp); int netdev_get_config(const struct netdev *, struct smap *); const struct netdev_tunnel_config * netdev_get_tunnel_config(const struct netdev *); int netdev_get_numa_id(const struct netdev *); /* Basic properties. */ const char *netdev_get_name(const struct netdev *); const char *netdev_get_type(const struct netdev *); const char *netdev_get_type_from_name(const char *); int netdev_get_mtu(const struct netdev *, int *mtup); int netdev_set_mtu(const struct netdev *, int mtu); int netdev_get_ifindex(const struct netdev *); int netdev_set_multiq(struct netdev *, unsigned int n_txq, unsigned int n_rxq); /* Packet reception. */ int netdev_rxq_open(struct netdev *, struct netdev_rxq **, int id); void netdev_rxq_close(struct netdev_rxq *); const char *netdev_rxq_get_name(const struct netdev_rxq *); int netdev_rxq_get_queue_id(const struct netdev_rxq *); int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet **buffers, int *cnt); void netdev_rxq_wait(struct netdev_rxq *); int netdev_rxq_drain(struct netdev_rxq *); /* Packet transmission. */ int netdev_send(struct netdev *, int qid, struct dp_packet **, int cnt, bool may_steal); void netdev_send_wait(struct netdev *, int qid); int netdev_build_header(const struct netdev *, struct ovs_action_push_tnl *data, const struct flow *tnl_flow); int netdev_push_header(const struct netdev *netdev, struct dp_packet **buffers, int cnt, const struct ovs_action_push_tnl *data); int netdev_pop_header(struct netdev *netdev, struct dp_packet **buffers, int cnt); /* Hardware address. */ int netdev_set_etheraddr(struct netdev *, const struct eth_addr mac); int netdev_get_etheraddr(const struct netdev *, struct eth_addr *mac); /* PHY interface. */ bool netdev_get_carrier(const struct netdev *); long long int netdev_get_carrier_resets(const struct netdev *); int netdev_set_miimon_interval(struct netdev *, long long int interval); /* Features. */ enum netdev_features { NETDEV_F_10MB_HD = 1 << 0, /* 10 Mb half-duplex rate support. */ NETDEV_F_10MB_FD = 1 << 1, /* 10 Mb full-duplex rate support. */ NETDEV_F_100MB_HD = 1 << 2, /* 100 Mb half-duplex rate support. */ NETDEV_F_100MB_FD = 1 << 3, /* 100 Mb full-duplex rate support. */ NETDEV_F_1GB_HD = 1 << 4, /* 1 Gb half-duplex rate support. */ NETDEV_F_1GB_FD = 1 << 5, /* 1 Gb full-duplex rate support. */ NETDEV_F_10GB_FD = 1 << 6, /* 10 Gb full-duplex rate support. */ NETDEV_F_40GB_FD = 1 << 7, /* 40 Gb full-duplex rate support. */ NETDEV_F_100GB_FD = 1 << 8, /* 100 Gb full-duplex rate support. */ NETDEV_F_1TB_FD = 1 << 9, /* 1 Tb full-duplex rate support. */ NETDEV_F_OTHER = 1 << 10, /* Other rate, not in the list. */ NETDEV_F_COPPER = 1 << 11, /* Copper medium. */ NETDEV_F_FIBER = 1 << 12, /* Fiber medium. */ NETDEV_F_AUTONEG = 1 << 13, /* Auto-negotiation. */ NETDEV_F_PAUSE = 1 << 14, /* Pause. */ NETDEV_F_PAUSE_ASYM = 1 << 15, /* Asymmetric pause. */ }; int netdev_get_features(const struct netdev *, enum netdev_features *current, enum netdev_features *advertised, enum netdev_features *supported, enum netdev_features *peer); uint64_t netdev_features_to_bps(enum netdev_features features, uint64_t default_bps); bool netdev_features_is_full_duplex(enum netdev_features features); int netdev_set_advertisements(struct netdev *, enum netdev_features advertise); /* Flags. */ enum netdev_flags { NETDEV_UP = 0x0001, /* Device enabled? */ NETDEV_PROMISC = 0x0002, /* Promiscuous mode? */ NETDEV_LOOPBACK = 0x0004 /* This is a loopback device. */ }; int netdev_get_flags(const struct netdev *, enum netdev_flags *); int netdev_set_flags(struct netdev *, enum netdev_flags, struct netdev_saved_flags **); int netdev_turn_flags_on(struct netdev *, enum netdev_flags, struct netdev_saved_flags **); int netdev_turn_flags_off(struct netdev *, enum netdev_flags, struct netdev_saved_flags **); void netdev_restore_flags(struct netdev_saved_flags *); /* TCP/IP stack interface. */ int netdev_get_in4(const struct netdev *, struct in_addr *address, struct in_addr *netmask); int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask); int netdev_get_in4_by_name(const char *device_name, struct in_addr *in4); int netdev_get_in6(const struct netdev *, struct in6_addr *); int netdev_add_router(struct netdev *, struct in_addr router); int netdev_get_next_hop(const struct netdev *, const struct in_addr *host, struct in_addr *next_hop, char **); int netdev_get_status(const struct netdev *, struct smap *); int netdev_arp_lookup(const struct netdev *, ovs_be32 ip, struct eth_addr *mac); struct netdev *netdev_find_dev_by_in4(const struct in_addr *); /* Statistics. */ int netdev_get_stats(const struct netdev *, struct netdev_stats *); /* Quality of service. */ struct netdev_qos_capabilities { unsigned int n_queues; }; struct netdev_queue_stats { /* Values of unsupported statistics are set to all-1-bits (UINT64_MAX). */ uint64_t tx_bytes; uint64_t tx_packets; uint64_t tx_errors; /* Time at which the queue was created, in msecs, LLONG_MIN if unknown. */ long long int created; }; int netdev_set_policing(struct netdev *, uint32_t kbits_rate, uint32_t kbits_burst); int netdev_get_qos_types(const struct netdev *, struct sset *types); int netdev_get_qos_capabilities(const struct netdev *, const char *type, struct netdev_qos_capabilities *); int netdev_get_n_queues(const struct netdev *, const char *type, unsigned int *n_queuesp); int netdev_get_qos(const struct netdev *, const char **typep, struct smap *details); int netdev_set_qos(struct netdev *, const char *type, const struct smap *details); int netdev_get_queue(const struct netdev *, unsigned int queue_id, struct smap *details); int netdev_set_queue(struct netdev *, unsigned int queue_id, const struct smap *details); int netdev_delete_queue(struct netdev *, unsigned int queue_id); int netdev_get_queue_stats(const struct netdev *, unsigned int queue_id, struct netdev_queue_stats *); uint64_t netdev_get_change_seq(const struct netdev *); struct netdev_queue_dump { struct netdev *netdev; int error; void *state; }; void netdev_queue_dump_start(struct netdev_queue_dump *, const struct netdev *); bool netdev_queue_dump_next(struct netdev_queue_dump *, unsigned int *queue_id, struct smap *details); int netdev_queue_dump_done(struct netdev_queue_dump *); /* Iterates through each queue in NETDEV, using DUMP as state. Fills QUEUE_ID * and DETAILS with information about queues. The client must initialize and * destroy DETAILS. * * Arguments all have pointer type. * * If you break out of the loop, then you need to free the dump structure by * hand using netdev_queue_dump_done(). */ #define NETDEV_QUEUE_FOR_EACH(QUEUE_ID, DETAILS, DUMP, NETDEV) \ for (netdev_queue_dump_start(DUMP, NETDEV); \ (netdev_queue_dump_next(DUMP, QUEUE_ID, DETAILS) \ ? true \ : (netdev_queue_dump_done(DUMP), false)); \ ) typedef void netdev_dump_queue_stats_cb(unsigned int queue_id, struct netdev_queue_stats *, void *aux); int netdev_dump_queue_stats(const struct netdev *, netdev_dump_queue_stats_cb *, void *aux); enum { NETDEV_MAX_BURST = 32 }; /* Maximum number packets in a batch. */ extern struct seq *tnl_conf_seq; #ifdef __cplusplus } #endif #endif /* netdev.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stream.h0000644000000000000000000000013213534540071016056 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.897854281 openvswitch-2.5.9/lib/stream.h0000644000175000017500000000657613534540071017562 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STREAM_H #define STREAM_H 1 #include #include #include #include #include "openvswitch/types.h" #include "socket-util.h" #include "util.h" struct pstream; struct stream; struct vlog_module; void stream_usage(const char *name, bool active, bool passive, bool bootstrap); /* Bidirectional byte streams. */ int stream_verify_name(const char *name); int stream_open(const char *name, struct stream **, uint8_t dscp); int stream_open_block(int error, struct stream **); void stream_close(struct stream *); const char *stream_get_name(const struct stream *); ovs_be32 stream_get_remote_ip(const struct stream *); ovs_be16 stream_get_remote_port(const struct stream *); ovs_be32 stream_get_local_ip(const struct stream *); ovs_be16 stream_get_local_port(const struct stream *); int stream_connect(struct stream *); int stream_recv(struct stream *, void *buffer, size_t n); int stream_send(struct stream *, const void *buffer, size_t n); void stream_run(struct stream *); void stream_run_wait(struct stream *); enum stream_wait_type { STREAM_CONNECT, STREAM_RECV, STREAM_SEND }; void stream_wait(struct stream *, enum stream_wait_type); void stream_connect_wait(struct stream *); void stream_recv_wait(struct stream *); void stream_send_wait(struct stream *); /* Passive streams: listeners for incoming stream connections. */ int pstream_verify_name(const char *name); int pstream_open(const char *name, struct pstream **, uint8_t dscp); const char *pstream_get_name(const struct pstream *); void pstream_close(struct pstream *); int pstream_accept(struct pstream *, struct stream **); int pstream_accept_block(struct pstream *, struct stream **); void pstream_wait(struct pstream *); ovs_be16 pstream_get_bound_port(const struct pstream *); /* Convenience functions. */ int stream_open_with_default_port(const char *name, uint16_t default_port, struct stream **, uint8_t dscp); int pstream_open_with_default_port(const char *name, uint16_t default_port, struct pstream **, uint8_t dscp); bool stream_parse_target_with_default_port(const char *target, uint16_t default_port, struct sockaddr_storage *ss); int stream_or_pstream_needs_probes(const char *name); /* Error reporting. */ enum stream_content_type { STREAM_UNKNOWN, STREAM_OPENFLOW, STREAM_SSL, STREAM_JSONRPC }; void stream_report_content(const void *, ssize_t, enum stream_content_type, struct vlog_module *, const char *stream_name); #endif /* stream.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/guarded-list.c0000644000000000000000000000013213534540071017142 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.729853043 openvswitch-2.5.9/lib/guarded-list.c0000644000175000017500000000424613534540071020636 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "guarded-list.h" void guarded_list_init(struct guarded_list *list) { ovs_mutex_init(&list->mutex); list_init(&list->list); list->n = 0; } void guarded_list_destroy(struct guarded_list *list) { ovs_mutex_destroy(&list->mutex); } bool guarded_list_is_empty(const struct guarded_list *list) { bool empty; ovs_mutex_lock(&list->mutex); empty = list->n == 0; ovs_mutex_unlock(&list->mutex); return empty; } /* If 'list' has fewer than 'max' elements, adds 'node' at the end of the list * and returns the number of elements now on the list. * * If 'list' already has at least 'max' elements, returns 0 without modifying * the list. */ size_t guarded_list_push_back(struct guarded_list *list, struct ovs_list *node, size_t max) { size_t retval = 0; ovs_mutex_lock(&list->mutex); if (list->n < max) { list_push_back(&list->list, node); retval = ++list->n; } ovs_mutex_unlock(&list->mutex); return retval; } struct ovs_list * guarded_list_pop_front(struct guarded_list *list) { struct ovs_list *node = NULL; ovs_mutex_lock(&list->mutex); if (list->n) { node = list_pop_front(&list->list); list->n--; } ovs_mutex_unlock(&list->mutex); return node; } size_t guarded_list_pop_all(struct guarded_list *list, struct ovs_list *elements) { size_t n; ovs_mutex_lock(&list->mutex); list_move(elements, &list->list); n = list->n; list_init(&list->list); list->n = 0; ovs_mutex_unlock(&list->mutex); return n; } openvswitch-2.5.9/lib/PaxHeaders.82075/learn.h0000644000000000000000000000013113534540071015663 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.753853219 openvswitch-2.5.9/lib/learn.h0000644000175000017500000000255413534540071017360 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LEARN_H #define LEARN_H 1 #include "compiler.h" #include "ofp-errors.h" struct ds; struct flow; struct flow_wildcards; struct ofpbuf; struct ofpact_learn; struct ofputil_flow_mod; struct nx_action_learn; /* NXAST_LEARN helper functions. * * See include/openflow/nicira-ext.h for NXAST_LEARN specification. */ enum ofperr learn_check(const struct ofpact_learn *, const struct flow *); void learn_execute(const struct ofpact_learn *, const struct flow *, struct ofputil_flow_mod *, struct ofpbuf *ofpacts); void learn_mask(const struct ofpact_learn *, struct flow_wildcards *); char *learn_parse(char *, struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT; void learn_format(const struct ofpact_learn *, struct ds *); #endif /* learn.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stream-fd.h0000644000000000000000000000013213534540071016445 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.889854223 openvswitch-2.5.9/lib/stream-fd.h0000644000175000017500000000244213534540071020135 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2012, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Note on windows platform, stream fd can only handle sockets, on unix any * fd is acceptable. */ #ifndef STREAM_FD_H #define STREAM_FD_H 1 #include #include #include struct stream; struct pstream; struct sockaddr_storage; int new_fd_stream(const char *name, int fd, int connect_status, int fd_type, struct stream **streamp); int new_fd_pstream(const char *name, int fd, int (*accept_cb)(int fd, const struct sockaddr_storage *ss, size_t ss_len, struct stream **), char *unlink_path, struct pstream **pstreamp); #endif /* stream-fd.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-gcc4.7+.h0000644000000000000000000000013213534540071017702 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.813853662 openvswitch-2.5.9/lib/ovs-atomic-gcc4.7+.h0000644000175000017500000000725413534540071021400 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on GCC 4.7 and later. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #define ATOMIC(TYPE) TYPE typedef enum { memory_order_relaxed = __ATOMIC_RELAXED, memory_order_consume = __ATOMIC_CONSUME, memory_order_acquire = __ATOMIC_ACQUIRE, memory_order_release = __ATOMIC_RELEASE, memory_order_acq_rel = __ATOMIC_ACQ_REL, memory_order_seq_cst = __ATOMIC_SEQ_CST } memory_order; #define ATOMIC_VAR_INIT(VALUE) (VALUE) #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) #define atomic_thread_fence __atomic_thread_fence #define atomic_signal_fence __atomic_signal_fence #define atomic_is_lock_free __atomic_is_lock_free #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) #define atomic_store_explicit __atomic_store_n #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_read_explicit(SRC, DST, ORDER) \ (*(DST) = __atomic_load_n(SRC, ORDER), \ (void) 0) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \ __atomic_compare_exchange_n(DST, EXP, SRC, false, ORD1, ORD2) #define atomic_compare_exchange_weak(DST, EXP, SRC) \ atomic_compare_exchange_weak_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_weak_explicit(DST, EXP, SRC, ORD1, ORD2) \ __atomic_compare_exchange_n(DST, EXP, SRC, true, ORD1, ORD2) #define atomic_add(RMW, OPERAND, ORIG) \ atomic_add_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) #define atomic_sub(RMW, OPERAND, ORIG) \ atomic_sub_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) #define atomic_or(RMW, OPERAND, ORIG) \ atomic_or_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) #define atomic_xor(RMW, OPERAND, ORIG) \ atomic_xor_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) #define atomic_and(RMW, OPERAND, ORIG) \ atomic_and_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst) #define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER) \ (*(ORIG) = __atomic_fetch_add(RMW, OPERAND, ORDER), (void) 0) #define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER) \ (*(ORIG) = __atomic_fetch_sub(RMW, OPERAND, ORDER), (void) 0) #define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER) \ (*(ORIG) = __atomic_fetch_or(RMW, OPERAND, ORDER), (void) 0) #define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER) \ (*(ORIG) = __atomic_fetch_xor(RMW, OPERAND, ORDER), (void) 0) #define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER) \ (*(ORIG) = __atomic_fetch_and(RMW, OPERAND, ORDER), (void) 0) #include "ovs-atomic-flag-gcc4.7+.h" openvswitch-2.5.9/lib/PaxHeaders.82075/hindex.h0000644000000000000000000000013213534540071016042 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.733853073 openvswitch-2.5.9/lib/hindex.h0000644000175000017500000001523013534540071017531 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HINDEX_H #define HINDEX_H 1 /* Hashed multimap. * * hindex is a hash table data structure that gracefully handles duplicates. * With a high-quality hash function, insertion, deletion, and search are O(1) * expected time, regardless of the number of duplicates for a given key. */ #include #include #include "util.h" /* A hash index node, to embed inside the data structure being indexed. * * Nodes are linked together like this (the boxes are labeled with hash * values): * * +--------+ d +--------+ d +--------+ d * bucket---> | 6 |---->| 20 |---->| 15 |---->null * +-|------+ +-|------+ +-|------+ * | ^ | | ^ * s| |d |s s| |d * V | V V | * +------|-+ null +------|-+ * | 6 | | 15 | * +-|------+ +-|------+ * | ^ | * s| |d s| * V | V * +------|-+ null * | 6 | * +-|------+ * | * s| * V * null * * The basic usage is: * * - To visit the unique hash values in the hindex, follow the 'd' * ("different") pointers starting from each bucket. The nodes visited * this way are called "head" nodes, because they are at the head of the * vertical chains. * * - To visit the nodes with hash value H, follow the 'd' pointers in the * appropriate bucket until you find one with hash H, then follow the 's' * ("same") pointers until you hit a null pointer. The non-head nodes * visited this way are called "body" nodes. * * - The 'd' pointers in body nodes point back to the previous body node * or, for the first body node, to the head node. (This makes it * possible to remove a body node without traversing all the way downward * from the head). */ struct hindex_node { /* Hash value. */ size_t hash; /* In a head node, the next head node (with a hash different from this * node), or NULL if this is the last node in this bucket. * * In a body node, the previous head or body node (with the same hash as * this node). Never null. */ struct hindex_node *d; /* In a head or a body node, the next body node with the same hash as this * node. NULL if this is the last node with this hash. */ struct hindex_node *s; }; /* A hash index. */ struct hindex { struct hindex_node **buckets; /* Must point to 'one' iff 'mask' == 0. */ struct hindex_node *one; size_t mask; /* 0 or more lowest-order bits set, others cleared. */ size_t n_unique; /* Number of unique hashes (the number of head nodes). */ }; /* Initializer for an empty hash index. */ #define HINDEX_INITIALIZER(HINDEX) \ { (struct hindex_node **const) &(HINDEX)->one, NULL, 0, 0 } /* Initialization. */ void hindex_init(struct hindex *); void hindex_destroy(struct hindex *); void hindex_clear(struct hindex *); void hindex_swap(struct hindex *a, struct hindex *b); void hindex_moved(struct hindex *hindex); static inline bool hindex_is_empty(const struct hindex *); /* Adjusting capacity. */ void hindex_expand(struct hindex *); void hindex_shrink(struct hindex *); void hindex_reserve(struct hindex *, size_t capacity); /* Insertion and deletion. */ void hindex_insert_fast(struct hindex *, struct hindex_node *, size_t hash); void hindex_insert(struct hindex *, struct hindex_node *, size_t hash); void hindex_remove(struct hindex *, struct hindex_node *); /* Search. * * HINDEX_FOR_EACH_WITH_HASH iterates NODE over all of the nodes in HINDEX that * have hash value equal to HASH. MEMBER must be the name of the 'struct * hindex_node' member within NODE. * * The loop should not change NODE to point to a different node or insert or * delete nodes in HINDEX (unless it "break"s out of the loop to terminate * iteration). * * Evaluates HASH only once. */ #define HINDEX_FOR_EACH_WITH_HASH(NODE, MEMBER, HASH, HINDEX) \ for (INIT_CONTAINER(NODE, hindex_node_with_hash(HINDEX, HASH), MEMBER); \ NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ ASSIGN_CONTAINER(NODE, (NODE)->MEMBER.s, MEMBER)) /* Returns the head node in 'hindex' with the given 'hash', or a null pointer * if no nodes have that hash value. */ static inline struct hindex_node * hindex_node_with_hash(const struct hindex *hindex, size_t hash) { struct hindex_node *node = hindex->buckets[hash & hindex->mask]; while (node && node->hash != hash) { node = node->d; } return node; } /* Iteration. */ /* Iterates through every node in HINDEX. */ #define HINDEX_FOR_EACH(NODE, MEMBER, HINDEX) \ for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER); \ NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER); \ ASSIGN_CONTAINER(NODE, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER)) /* Safe when NODE may be freed (not needed when NODE may be removed from the * hash index but its members remain accessible and intact). */ #define HINDEX_FOR_EACH_SAFE(NODE, NEXT, MEMBER, HINDEX) \ for (INIT_CONTAINER(NODE, hindex_first(HINDEX), MEMBER); \ (NODE != OBJECT_CONTAINING(NULL, NODE, MEMBER) \ ? INIT_CONTAINER(NEXT, hindex_next(HINDEX, &(NODE)->MEMBER), MEMBER), 1 \ : 0); \ (NODE) = (NEXT)) struct hindex_node *hindex_first(const struct hindex *); struct hindex_node *hindex_next(const struct hindex *, const struct hindex_node *); /* Returns true if 'hindex' currently contains no nodes, false otherwise. */ static inline bool hindex_is_empty(const struct hindex *hindex) { return hindex->n_unique == 0; } #endif /* hindex.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ct-dpif.h0000644000000000000000000000013213534540071016111 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.689852748 openvswitch-2.5.9/lib/ct-dpif.h0000644000175000017500000001163713534540071017607 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CT_DPIF_H #define CT_DPIF_H #include "openvswitch/types.h" #include "packets.h" union ct_dpif_inet_addr { ovs_be32 ip; ovs_be32 ip6[4]; struct in_addr in; struct in6_addr in6; }; struct ct_dpif_tuple { uint16_t l3_type; /* Address family. */ uint8_t ip_proto; union ct_dpif_inet_addr src; union ct_dpif_inet_addr dst; union { ovs_be16 src_port; ovs_be16 icmp_id; }; union { ovs_be16 dst_port; struct { uint8_t icmp_type; uint8_t icmp_code; }; }; }; BUILD_ASSERT_DECL(sizeof(struct ct_dpif_tuple) % 8 == 0); struct ct_dpif_counters { uint64_t packets; uint64_t bytes; }; /* Nanoseconds from January 1, 1970 */ struct ct_dpif_timestamp { /* When the entry was created */ uint64_t start; /* When the entry was deleted */ uint64_t stop; }; #define CT_DPIF_TCP_STATES \ CT_DPIF_TCP_STATE(CLOSED) \ CT_DPIF_TCP_STATE(LISTEN) \ CT_DPIF_TCP_STATE(SYN_SENT) \ CT_DPIF_TCP_STATE(SYN_RECV) \ CT_DPIF_TCP_STATE(ESTABLISHED) \ CT_DPIF_TCP_STATE(CLOSE_WAIT) \ CT_DPIF_TCP_STATE(FIN_WAIT_1) \ CT_DPIF_TCP_STATE(CLOSING) \ CT_DPIF_TCP_STATE(LAST_ACK) \ CT_DPIF_TCP_STATE(FIN_WAIT_2) \ CT_DPIF_TCP_STATE(TIME_WAIT) enum ct_dpif_tcp_state { #define CT_DPIF_TCP_STATE(STATE) CT_DPIF_TCPS_##STATE, CT_DPIF_TCP_STATES #undef CT_DPIF_TCP_STATE }; extern const char *ct_dpif_tcp_state_string[]; #define CT_DPIF_TCP_FLAGS \ CT_DPIF_TCP_FLAG(WINDOW_SCALE) \ CT_DPIF_TCP_FLAG(SACK_PERM) \ CT_DPIF_TCP_FLAG(CLOSE_INIT) \ CT_DPIF_TCP_FLAG(BE_LIBERAL) \ CT_DPIF_TCP_FLAG(DATA_UNACKNOWLEDGED) \ CT_DPIF_TCP_FLAG(MAXACK_SET) \ enum ct_dpif_tcp_flags_count_ { #define CT_DPIF_TCP_FLAG(FLAG) FLAG##_COUNT_, CT_DPIF_TCP_FLAGS #undef CT_DPIF_TCP_FLAG }; enum ct_dpif_tcp_flags { #define CT_DPIF_TCP_FLAG(FLAG) CT_DPIF_TCPF_##FLAG = (1 << FLAG##_COUNT_), CT_DPIF_TCP_FLAGS #undef CT_DPIF_TCP_FLAG }; struct ct_dpif_protoinfo { uint16_t proto; /* IPPROTO_* */ union { struct { uint8_t state_orig; uint8_t state_reply; uint8_t wscale_orig; uint8_t wscale_reply; uint8_t flags_orig; uint8_t flags_reply; } tcp; }; }; struct ct_dpif_helper { char *name; }; #define CT_DPIF_STATUS_FLAGS \ CT_DPIF_STATUS_FLAG(EXPECTED) \ CT_DPIF_STATUS_FLAG(SEEN_REPLY) \ CT_DPIF_STATUS_FLAG(ASSURED) \ CT_DPIF_STATUS_FLAG(CONFIRMED) \ CT_DPIF_STATUS_FLAG(SRC_NAT) \ CT_DPIF_STATUS_FLAG(DST_NAT) \ CT_DPIF_STATUS_FLAG(SEQ_ADJUST) \ CT_DPIF_STATUS_FLAG(SRC_NAT_DONE) \ CT_DPIF_STATUS_FLAG(DST_NAT_DONE) \ CT_DPIF_STATUS_FLAG(DYING) \ CT_DPIF_STATUS_FLAG(FIXED_TIMEOUT) \ CT_DPIF_STATUS_FLAG(TEMPLATE) \ CT_DPIF_STATUS_FLAG(UNTRACKED) \ enum ct_dpif_status_flags_count_ { #define CT_DPIF_STATUS_FLAG(FLAG) FLAG##_COUNT_, CT_DPIF_STATUS_FLAGS #undef CT_DPIF_STATUS_FLAG }; enum ct_dpif_status_flags { #define CT_DPIF_STATUS_FLAG(FLAG) CT_DPIF_STATUS_##FLAG = (1 << FLAG##_COUNT_), CT_DPIF_STATUS_FLAGS #undef CT_DPIF_STATUS_FLAG }; struct ct_dpif_entry { /* Const members. */ struct ct_dpif_tuple tuple_orig; struct ct_dpif_tuple tuple_reply; struct ct_dpif_tuple tuple_master; struct ct_dpif_helper helper; uint32_t id; uint16_t zone; /* Modifiable members. */ struct ct_dpif_counters counters_orig; struct ct_dpif_counters counters_reply; struct ct_dpif_timestamp timestamp; struct ct_dpif_protoinfo protoinfo; ovs_u128 labels; uint32_t status; /* Timeout for this entry in seconds */ uint32_t timeout; uint32_t mark; }; struct dpif; struct ct_dpif_dump_state { struct dpif *dpif; }; int ct_dpif_dump_start(struct dpif *, struct ct_dpif_dump_state **, const uint16_t *zone); int ct_dpif_dump_next(struct ct_dpif_dump_state *, struct ct_dpif_entry *); int ct_dpif_dump_done(struct ct_dpif_dump_state *); int ct_dpif_flush(struct dpif *, const uint16_t *zone); void ct_dpif_entry_uninit(struct ct_dpif_entry *); void ct_dpif_format_entry(const struct ct_dpif_entry *, struct ds *, bool verbose, bool print_stats); void ct_dpif_format_tuple(struct ds *, const struct ct_dpif_tuple *, bool verbose); #endif /* CT_DPIF_H */ openvswitch-2.5.9/lib/PaxHeaders.82075/rstp-state-machines.h0000644000000000000000000000013213534540071020456 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.869854075 openvswitch-2.5.9/lib/rstp-state-machines.h0000644000175000017500000000266213534540071022152 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011-2014 M3S, Srl - Italy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Rapid Spanning Tree Protocol (IEEE 802.1D-2004) state machines * implementation (header file). * * Authors: * Martino Fornasa * Daniele Venturino * * References to IEEE 802.1D-2004 standard are enclosed in square brackets. * E.g. [17.3], [Table 17-1], etc. * */ #ifndef RSTP_STATE_MACHINES_H #define RSTP_STATE_MACHINES_H 1 #include "rstp-common.h" /* Methods called by the Forwarding Layer, through functions of rstp.h. */ int move_rstp__(struct rstp *) OVS_REQUIRES(rstp_mutex); void decrease_rstp_port_timers__(struct rstp *) OVS_REQUIRES(rstp_mutex); void process_received_bpdu__(struct rstp_port *, const void *, size_t) OVS_REQUIRES(rstp_mutex); void updt_roles_tree__(struct rstp *) OVS_REQUIRES(rstp_mutex); #endif /* rstp-state-machines.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/common-syn.man0000644000000000000000000000013213534540071017206 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801423.725845641 openvswitch-2.5.9/lib/common-syn.man0000644000175000017500000000012413534540071020671 0ustar00jpettitjpettit00000000000000.IP "Common options:" [\fB\-h\fR | \fB\-\-help\fR] [\fB\-V\fR | \fB\-\-version\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/ssl-peer-ca-cert-syn.man0000644000000000000000000000013213534540071020764 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801423.741845759 openvswitch-2.5.9/lib/ssl-peer-ca-cert-syn.man0000644000175000017500000000006213534540071022450 0ustar00jpettitjpettit00000000000000.br [\fB\-\-peer\-ca\-cert=\fIpeer-cacert.pem\fR] openvswitch-2.5.9/lib/PaxHeaders.82075/lacp.c0000644000000000000000000000013113534540071015474 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.749853191 openvswitch-2.5.9/lib/lacp.c0000644000175000017500000007442413534540071017176 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "lacp.h" #include #include "connectivity.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "dp-packet.h" #include "ovs-atomic.h" #include "packets.h" #include "poll-loop.h" #include "seq.h" #include "shash.h" #include "timer.h" #include "timeval.h" #include "unixctl.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(lacp); /* Masks for lacp_info state member. */ #define LACP_STATE_ACT 0x01 /* Activity. Active or passive? */ #define LACP_STATE_TIME 0x02 /* Timeout. Short or long timeout? */ #define LACP_STATE_AGG 0x04 /* Aggregation. Is the link is bondable? */ #define LACP_STATE_SYNC 0x08 /* Synchronization. Is the link in up to date? */ #define LACP_STATE_COL 0x10 /* Collecting. Is the link receiving frames? */ #define LACP_STATE_DIST 0x20 /* Distributing. Is the link sending frames? */ #define LACP_STATE_DEF 0x40 /* Defaulted. Using default partner info? */ #define LACP_STATE_EXP 0x80 /* Expired. Using expired partner info? */ #define LACP_FAST_TIME_TX 1000 /* Fast transmission rate. */ #define LACP_SLOW_TIME_TX 30000 /* Slow transmission rate. */ #define LACP_RX_MULTIPLIER 3 /* Multiply by TX rate to get RX rate. */ #define LACP_INFO_LEN 15 OVS_PACKED( struct lacp_info { ovs_be16 sys_priority; /* System priority. */ struct eth_addr sys_id; /* System ID. */ ovs_be16 key; /* Operational key. */ ovs_be16 port_priority; /* Port priority. */ ovs_be16 port_id; /* Port ID. */ uint8_t state; /* State mask. See LACP_STATE macros. */ }); BUILD_ASSERT_DECL(LACP_INFO_LEN == sizeof(struct lacp_info)); #define LACP_PDU_LEN 110 struct lacp_pdu { uint8_t subtype; /* Always 1. */ uint8_t version; /* Always 1. */ uint8_t actor_type; /* Always 1. */ uint8_t actor_len; /* Always 20. */ struct lacp_info actor; /* LACP actor information. */ uint8_t z1[3]; /* Reserved. Always 0. */ uint8_t partner_type; /* Always 2. */ uint8_t partner_len; /* Always 20. */ struct lacp_info partner; /* LACP partner information. */ uint8_t z2[3]; /* Reserved. Always 0. */ uint8_t collector_type; /* Always 3. */ uint8_t collector_len; /* Always 16. */ ovs_be16 collector_delay; /* Maximum collector delay. Set to UINT16_MAX. */ uint8_t z3[64]; /* Combination of several fields. Always 0. */ }; BUILD_ASSERT_DECL(LACP_PDU_LEN == sizeof(struct lacp_pdu)); /* Implementation. */ enum slave_status { LACP_CURRENT, /* Current State. Partner up to date. */ LACP_EXPIRED, /* Expired State. Partner out of date. */ LACP_DEFAULTED, /* Defaulted State. No partner. */ }; struct lacp { struct ovs_list node; /* Node in all_lacps list. */ char *name; /* Name of this lacp object. */ struct eth_addr sys_id; /* System ID. */ uint16_t sys_priority; /* System Priority. */ bool active; /* Active or Passive. */ struct hmap slaves; /* Slaves this LACP object controls. */ struct slave *key_slave; /* Slave whose ID will be the aggregation key. */ bool fast; /* True if using fast probe interval. */ bool negotiated; /* True if LACP negotiations were successful. */ bool update; /* True if lacp_update() needs to be called. */ bool fallback_ab; /* True if fallback to active-backup on LACP failure. */ struct ovs_refcount ref_cnt; }; struct slave { void *aux; /* Handle used to identify this slave. */ struct hmap_node node; /* Node in master's slaves map. */ struct lacp *lacp; /* LACP object containing this slave. */ uint16_t port_id; /* Port ID. */ uint16_t port_priority; /* Port Priority. */ uint16_t key; /* Aggregation Key. 0 if default. */ char *name; /* Name of this slave. */ enum slave_status status; /* Slave status. */ bool attached; /* Attached. Traffic may flow. */ struct lacp_info partner; /* Partner information. */ struct lacp_info ntt_actor; /* Used to decide if we Need To Transmit. */ struct timer tx; /* Next message transmission timer. */ struct timer rx; /* Expected message receive timer. */ uint32_t count_rx_pdus; /* dot3adAggPortStatsLACPDUsRx */ uint32_t count_rx_pdus_bad; /* dot3adAggPortStatsIllegalRx */ uint32_t count_tx_pdus; /* dot3adAggPortStatsLACPDUsTx */ }; static struct ovs_mutex mutex; static struct ovs_list all_lacps__ = OVS_LIST_INITIALIZER(&all_lacps__); static struct ovs_list *const all_lacps OVS_GUARDED_BY(mutex) = &all_lacps__; static void lacp_update_attached(struct lacp *) OVS_REQUIRES(mutex); static void slave_destroy(struct slave *) OVS_REQUIRES(mutex); static void slave_set_defaulted(struct slave *) OVS_REQUIRES(mutex); static void slave_set_expired(struct slave *) OVS_REQUIRES(mutex); static void slave_get_actor(struct slave *, struct lacp_info *actor) OVS_REQUIRES(mutex); static void slave_get_priority(struct slave *, struct lacp_info *priority) OVS_REQUIRES(mutex); static bool slave_may_tx(const struct slave *) OVS_REQUIRES(mutex); static struct slave *slave_lookup(const struct lacp *, const void *slave) OVS_REQUIRES(mutex); static bool info_tx_equal(struct lacp_info *, struct lacp_info *) OVS_REQUIRES(mutex); static unixctl_cb_func lacp_unixctl_show; /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */ static void compose_lacp_pdu(const struct lacp_info *actor, const struct lacp_info *partner, struct lacp_pdu *pdu) { memset(pdu, 0, sizeof *pdu); pdu->subtype = 1; pdu->version = 1; pdu->actor_type = 1; pdu->actor_len = 20; pdu->actor = *actor; pdu->partner_type = 2; pdu->partner_len = 20; pdu->partner = *partner; pdu->collector_type = 3; pdu->collector_len = 16; pdu->collector_delay = htons(0); } /* Parses 'b' which represents a packet containing a LACP PDU. This function * returns NULL if 'b' is malformed, or does not represent a LACP PDU format * supported by OVS. Otherwise, it returns a pointer to the lacp_pdu contained * within 'b'. */ static const struct lacp_pdu * parse_lacp_packet(const struct dp_packet *p) { const struct lacp_pdu *pdu; pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p), LACP_PDU_LEN); if (pdu && pdu->subtype == 1 && pdu->actor_type == 1 && pdu->actor_len == 20 && pdu->partner_type == 2 && pdu->partner_len == 20) { return pdu; } else { return NULL; } } /* LACP Protocol Implementation. */ /* Initializes the lacp module. */ void lacp_init(void) { unixctl_command_register("lacp/show", "[port]", 0, 1, lacp_unixctl_show, NULL); } static void lacp_lock(void) OVS_ACQUIRES(mutex) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { ovs_mutex_init_recursive(&mutex); ovsthread_once_done(&once); } ovs_mutex_lock(&mutex); } static void lacp_unlock(void) OVS_RELEASES(mutex) { ovs_mutex_unlock(&mutex); } /* Creates a LACP object. */ struct lacp * lacp_create(void) OVS_EXCLUDED(mutex) { struct lacp *lacp; lacp = xzalloc(sizeof *lacp); hmap_init(&lacp->slaves); ovs_refcount_init(&lacp->ref_cnt); lacp_lock(); list_push_back(all_lacps, &lacp->node); lacp_unlock(); return lacp; } struct lacp * lacp_ref(const struct lacp *lacp_) { struct lacp *lacp = CONST_CAST(struct lacp *, lacp_); if (lacp) { ovs_refcount_ref(&lacp->ref_cnt); } return lacp; } /* Destroys 'lacp' and its slaves. Does nothing if 'lacp' is NULL. */ void lacp_unref(struct lacp *lacp) OVS_EXCLUDED(mutex) { if (lacp && ovs_refcount_unref_relaxed(&lacp->ref_cnt) == 1) { struct slave *slave, *next; lacp_lock(); HMAP_FOR_EACH_SAFE (slave, next, node, &lacp->slaves) { slave_destroy(slave); } hmap_destroy(&lacp->slaves); list_remove(&lacp->node); free(lacp->name); free(lacp); lacp_unlock(); } } /* Configures 'lacp' with settings from 's'. */ void lacp_configure(struct lacp *lacp, const struct lacp_settings *s) OVS_EXCLUDED(mutex) { ovs_assert(!eth_addr_is_zero(s->id)); lacp_lock(); if (!lacp->name || strcmp(s->name, lacp->name)) { free(lacp->name); lacp->name = xstrdup(s->name); } if (!eth_addr_equals(lacp->sys_id, s->id) || lacp->sys_priority != s->priority) { lacp->sys_id = s->id; lacp->sys_priority = s->priority; lacp->update = true; } lacp->active = s->active; lacp->fast = s->fast; if (lacp->fallback_ab != s->fallback_ab_cfg) { lacp->fallback_ab = s->fallback_ab_cfg; lacp->update = true; } lacp_unlock(); } /* Returns true if 'lacp' is configured in active mode, false if 'lacp' is * configured for passive mode. */ bool lacp_is_active(const struct lacp *lacp) OVS_EXCLUDED(mutex) { bool ret; lacp_lock(); ret = lacp->active; lacp_unlock(); return ret; } /* Processes 'packet' which was received on 'slave_'. This function should be * called on all packets received on 'slave_' with Ethernet Type ETH_TYPE_LACP. */ void lacp_process_packet(struct lacp *lacp, const void *slave_, const struct dp_packet *packet) OVS_EXCLUDED(mutex) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct lacp_pdu *pdu; long long int tx_rate; struct slave *slave; lacp_lock(); slave = slave_lookup(lacp, slave_); if (!slave) { goto out; } slave->count_rx_pdus++; pdu = parse_lacp_packet(packet); if (!pdu) { slave->count_rx_pdus_bad++; VLOG_WARN_RL(&rl, "%s: received an unparsable LACP PDU.", lacp->name); goto out; } slave->status = LACP_CURRENT; tx_rate = lacp->fast ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX; timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * tx_rate); slave->ntt_actor = pdu->partner; /* Update our information about our partner if it's out of date. This may * cause priorities to change so re-calculate attached status of all * slaves. */ if (memcmp(&slave->partner, &pdu->actor, sizeof pdu->actor)) { lacp->update = true; slave->partner = pdu->actor; } out: lacp_unlock(); } /* Returns the lacp_status of the given 'lacp' object (which may be NULL). */ enum lacp_status lacp_status(const struct lacp *lacp) OVS_EXCLUDED(mutex) { if (lacp) { enum lacp_status ret; lacp_lock(); ret = lacp->negotiated ? LACP_NEGOTIATED : LACP_CONFIGURED; lacp_unlock(); return ret; } else { /* Don't take 'mutex'. It might not even be initialized, since we * don't know that any lacp object has been created. */ return LACP_DISABLED; } } /* Registers 'slave_' as subordinate to 'lacp'. This should be called at least * once per slave in a LACP managed bond. Should also be called whenever a * slave's settings change. */ void lacp_slave_register(struct lacp *lacp, void *slave_, const struct lacp_slave_settings *s) OVS_EXCLUDED(mutex) { struct slave *slave; lacp_lock(); slave = slave_lookup(lacp, slave_); if (!slave) { slave = xzalloc(sizeof *slave); slave->lacp = lacp; slave->aux = slave_; hmap_insert(&lacp->slaves, &slave->node, hash_pointer(slave_, 0)); slave_set_defaulted(slave); if (!lacp->key_slave) { lacp->key_slave = slave; } } if (!slave->name || strcmp(s->name, slave->name)) { free(slave->name); slave->name = xstrdup(s->name); } if (slave->port_id != s->id || slave->port_priority != s->priority || slave->key != s->key) { slave->port_id = s->id; slave->port_priority = s->priority; slave->key = s->key; lacp->update = true; if (lacp->active || lacp->negotiated) { slave_set_expired(slave); } } lacp_unlock(); } /* Unregisters 'slave_' with 'lacp'. */ void lacp_slave_unregister(struct lacp *lacp, const void *slave_) OVS_EXCLUDED(mutex) { struct slave *slave; lacp_lock(); slave = slave_lookup(lacp, slave_); if (slave) { slave_destroy(slave); lacp->update = true; } lacp_unlock(); } /* This function should be called whenever the carrier status of 'slave_' has * changed. If 'lacp' is null, this function has no effect.*/ void lacp_slave_carrier_changed(const struct lacp *lacp, const void *slave_) OVS_EXCLUDED(mutex) { struct slave *slave; if (!lacp) { return; } lacp_lock(); slave = slave_lookup(lacp, slave_); if (!slave) { goto out; } if (slave->status == LACP_CURRENT || slave->lacp->active) { slave_set_expired(slave); } out: lacp_unlock(); } static bool slave_may_enable__(struct slave *slave) OVS_REQUIRES(mutex) { /* The slave may be enabled if it's attached to an aggregator and its * partner is synchronized.*/ return slave->attached && (slave->partner.state & LACP_STATE_SYNC || (slave->lacp && slave->lacp->fallback_ab && slave->status == LACP_DEFAULTED)); } /* This function should be called before enabling 'slave_' to send or receive * traffic. If it returns false, 'slave_' should not enabled. As a * convenience, returns true if 'lacp' is NULL. */ bool lacp_slave_may_enable(const struct lacp *lacp, const void *slave_) OVS_EXCLUDED(mutex) { if (lacp) { struct slave *slave; bool ret; lacp_lock(); slave = slave_lookup(lacp, slave_); ret = slave ? slave_may_enable__(slave) : false; lacp_unlock(); return ret; } else { return true; } } /* Returns true if partner information on 'slave_' is up to date. 'slave_' * not being current, generally indicates a connectivity problem, or a * misconfigured (or broken) partner. */ bool lacp_slave_is_current(const struct lacp *lacp, const void *slave_) OVS_EXCLUDED(mutex) { struct slave *slave; bool ret; lacp_lock(); slave = slave_lookup(lacp, slave_); ret = slave ? slave->status != LACP_DEFAULTED : false; lacp_unlock(); return ret; } /* This function should be called periodically to update 'lacp'. */ void lacp_run(struct lacp *lacp, lacp_send_pdu *send_pdu) OVS_EXCLUDED(mutex) { struct slave *slave; lacp_lock(); HMAP_FOR_EACH (slave, node, &lacp->slaves) { if (timer_expired(&slave->rx)) { enum slave_status old_status = slave->status; if (slave->status == LACP_CURRENT) { slave_set_expired(slave); } else if (slave->status == LACP_EXPIRED) { slave_set_defaulted(slave); } if (slave->status != old_status) { seq_change(connectivity_seq_get()); } } } if (lacp->update) { lacp_update_attached(lacp); seq_change(connectivity_seq_get()); } HMAP_FOR_EACH (slave, node, &lacp->slaves) { struct lacp_info actor; if (!slave_may_tx(slave)) { continue; } slave_get_actor(slave, &actor); if (timer_expired(&slave->tx) || !info_tx_equal(&actor, &slave->ntt_actor)) { long long int duration; struct lacp_pdu pdu; slave->ntt_actor = actor; compose_lacp_pdu(&actor, &slave->partner, &pdu); send_pdu(slave->aux, &pdu, sizeof pdu); slave->count_tx_pdus++; duration = (slave->partner.state & LACP_STATE_TIME ? LACP_FAST_TIME_TX : LACP_SLOW_TIME_TX); timer_set_duration(&slave->tx, duration); seq_change(connectivity_seq_get()); } } lacp_unlock(); } /* Causes poll_block() to wake up when lacp_run() needs to be called again. */ void lacp_wait(struct lacp *lacp) OVS_EXCLUDED(mutex) { struct slave *slave; lacp_lock(); HMAP_FOR_EACH (slave, node, &lacp->slaves) { if (slave_may_tx(slave)) { timer_wait(&slave->tx); } if (slave->status != LACP_DEFAULTED) { timer_wait(&slave->rx); } } lacp_unlock(); } /* Static Helpers. */ /* Updates the attached status of all slaves controlled by 'lacp' and sets its * negotiated parameter to true if any slaves are attachable. */ static void lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex) { struct slave *lead, *slave; struct lacp_info lead_pri; bool lead_enable; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); lacp->update = false; lead = NULL; lead_enable = false; HMAP_FOR_EACH (slave, node, &lacp->slaves) { struct lacp_info pri; slave->attached = false; /* XXX: In the future allow users to configure the expected system ID. * For now just special case loopback. */ if (eth_addr_equals(slave->partner.sys_id, slave->lacp->sys_id)) { VLOG_WARN_RL(&rl, "slave %s: Loopback detected. Slave is " "connected to its own bond", slave->name); continue; } if (slave->status == LACP_DEFAULTED) { if (lacp->fallback_ab) { slave->attached = true; } continue; } slave->attached = true; slave_get_priority(slave, &pri); bool enable = slave_may_enable__(slave); if (!lead || enable > lead_enable || (enable == lead_enable && memcmp(&pri, &lead_pri, sizeof pri) < 0)) { lead = slave; lead_enable = enable; lead_pri = pri; } } lacp->negotiated = lead != NULL; if (lead) { HMAP_FOR_EACH (slave, node, &lacp->slaves) { if ((lacp->fallback_ab && slave->status == LACP_DEFAULTED) || lead->partner.key != slave->partner.key || !eth_addr_equals(lead->partner.sys_id, slave->partner.sys_id)) { slave->attached = false; } } } } static void slave_destroy(struct slave *slave) OVS_REQUIRES(mutex) { if (slave) { struct lacp *lacp = slave->lacp; lacp->update = true; hmap_remove(&lacp->slaves, &slave->node); if (lacp->key_slave == slave) { struct hmap_node *slave_node = hmap_first(&lacp->slaves); if (slave_node) { lacp->key_slave = CONTAINER_OF(slave_node, struct slave, node); } else { lacp->key_slave = NULL; } } free(slave->name); free(slave); } } static void slave_set_defaulted(struct slave *slave) OVS_REQUIRES(mutex) { memset(&slave->partner, 0, sizeof slave->partner); slave->lacp->update = true; slave->status = LACP_DEFAULTED; } static void slave_set_expired(struct slave *slave) OVS_REQUIRES(mutex) { slave->status = LACP_EXPIRED; slave->partner.state |= LACP_STATE_TIME; slave->partner.state &= ~LACP_STATE_SYNC; timer_set_duration(&slave->rx, LACP_RX_MULTIPLIER * LACP_FAST_TIME_TX); } static void slave_get_actor(struct slave *slave, struct lacp_info *actor) OVS_REQUIRES(mutex) { struct lacp *lacp = slave->lacp; uint16_t key; uint8_t state = 0; if (lacp->active) { state |= LACP_STATE_ACT; } if (lacp->fast) { state |= LACP_STATE_TIME; } if (slave->attached) { state |= LACP_STATE_SYNC; } if (slave->status == LACP_DEFAULTED) { state |= LACP_STATE_DEF; } if (slave->status == LACP_EXPIRED) { state |= LACP_STATE_EXP; } if (hmap_count(&lacp->slaves) > 1) { state |= LACP_STATE_AGG; } if (slave->attached || !lacp->negotiated) { state |= LACP_STATE_COL | LACP_STATE_DIST; } key = lacp->key_slave->key; if (!key) { key = lacp->key_slave->port_id; } actor->state = state; actor->key = htons(key); actor->port_priority = htons(slave->port_priority); actor->port_id = htons(slave->port_id); actor->sys_priority = htons(lacp->sys_priority); actor->sys_id = lacp->sys_id; } /* Given 'slave', populates 'priority' with data representing its LACP link * priority. If two priority objects populated by this function are compared * using memcmp, the higher priority link will be less than the lower priority * link. */ static void slave_get_priority(struct slave *slave, struct lacp_info *priority) OVS_REQUIRES(mutex) { uint16_t partner_priority, actor_priority; /* Choose the lacp_info of the higher priority system by comparing their * system priorities and mac addresses. */ actor_priority = slave->lacp->sys_priority; partner_priority = ntohs(slave->partner.sys_priority); if (actor_priority < partner_priority) { slave_get_actor(slave, priority); } else if (partner_priority < actor_priority) { *priority = slave->partner; } else if (eth_addr_compare_3way(slave->lacp->sys_id, slave->partner.sys_id) < 0) { slave_get_actor(slave, priority); } else { *priority = slave->partner; } /* Key and state are not used in priority comparisons. */ priority->key = 0; priority->state = 0; } static bool slave_may_tx(const struct slave *slave) OVS_REQUIRES(mutex) { return slave->lacp->active || slave->status != LACP_DEFAULTED; } static struct slave * slave_lookup(const struct lacp *lacp, const void *slave_) OVS_REQUIRES(mutex) { struct slave *slave; HMAP_FOR_EACH_IN_BUCKET (slave, node, hash_pointer(slave_, 0), &lacp->slaves) { if (slave->aux == slave_) { return slave; } } return NULL; } /* Two lacp_info structures are tx_equal if and only if they do not differ in * ways which would require a lacp_pdu transmission. */ static bool info_tx_equal(struct lacp_info *a, struct lacp_info *b) { /* LACP specification dictates that we transmit whenever the actor and * remote_actor differ in the following fields: Port, Port Priority, * System, System Priority, Aggregation Key, Activity State, Timeout State, * Sync State, and Aggregation State. The state flags are most likely to * change so are checked first. */ return !((a->state ^ b->state) & (LACP_STATE_ACT | LACP_STATE_TIME | LACP_STATE_SYNC | LACP_STATE_AGG)) && a->port_id == b->port_id && a->port_priority == b->port_priority && a->key == b->key && a->sys_priority == b->sys_priority && eth_addr_equals(a->sys_id, b->sys_id); } static struct lacp * lacp_find(const char *name) OVS_REQUIRES(mutex) { struct lacp *lacp; LIST_FOR_EACH (lacp, node, all_lacps) { if (!strcmp(lacp->name, name)) { return lacp; } } return NULL; } static void ds_put_lacp_state(struct ds *ds, uint8_t state) { if (state & LACP_STATE_ACT) { ds_put_cstr(ds, " activity"); } if (state & LACP_STATE_TIME) { ds_put_cstr(ds, " timeout"); } if (state & LACP_STATE_AGG) { ds_put_cstr(ds, " aggregation"); } if (state & LACP_STATE_SYNC) { ds_put_cstr(ds, " synchronized"); } if (state & LACP_STATE_COL) { ds_put_cstr(ds, " collecting"); } if (state & LACP_STATE_DIST) { ds_put_cstr(ds, " distributing"); } if (state & LACP_STATE_DEF) { ds_put_cstr(ds, " defaulted"); } if (state & LACP_STATE_EXP) { ds_put_cstr(ds, " expired"); } } static void lacp_print_details(struct ds *ds, struct lacp *lacp) OVS_REQUIRES(mutex) { struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); const struct shash_node **sorted_slaves = NULL; struct slave *slave; int i; ds_put_format(ds, "---- %s ----\n", lacp->name); ds_put_format(ds, "\tstatus: %s", lacp->active ? "active" : "passive"); if (lacp->negotiated) { ds_put_cstr(ds, " negotiated"); } ds_put_cstr(ds, "\n"); ds_put_format(ds, "\tsys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(lacp->sys_id)); ds_put_format(ds, "\tsys_priority: %u\n", lacp->sys_priority); ds_put_cstr(ds, "\taggregation key: "); if (lacp->key_slave) { ds_put_format(ds, "%u", lacp->key_slave->key ? lacp->key_slave->key : lacp->key_slave->port_id); } else { ds_put_cstr(ds, "none"); } ds_put_cstr(ds, "\n"); ds_put_cstr(ds, "\tlacp_time: "); if (lacp->fast) { ds_put_cstr(ds, "fast\n"); } else { ds_put_cstr(ds, "slow\n"); } HMAP_FOR_EACH (slave, node, &lacp->slaves) { shash_add(&slave_shash, slave->name, slave); } sorted_slaves = shash_sort(&slave_shash); for (i = 0; i < shash_count(&slave_shash); i++) { char *status; struct lacp_info actor; slave = sorted_slaves[i]->data; slave_get_actor(slave, &actor); switch (slave->status) { case LACP_CURRENT: status = "current"; break; case LACP_EXPIRED: status = "expired"; break; case LACP_DEFAULTED: status = "defaulted"; break; default: OVS_NOT_REACHED(); } ds_put_format(ds, "\nslave: %s: %s %s\n", slave->name, status, slave->attached ? "attached" : "detached"); ds_put_format(ds, "\tport_id: %u\n", slave->port_id); ds_put_format(ds, "\tport_priority: %u\n", slave->port_priority); ds_put_format(ds, "\tmay_enable: %s\n", (slave_may_enable__(slave) ? "true" : "false")); ds_put_format(ds, "\n\tactor sys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(actor.sys_id)); ds_put_format(ds, "\tactor sys_priority: %u\n", ntohs(actor.sys_priority)); ds_put_format(ds, "\tactor port_id: %u\n", ntohs(actor.port_id)); ds_put_format(ds, "\tactor port_priority: %u\n", ntohs(actor.port_priority)); ds_put_format(ds, "\tactor key: %u\n", ntohs(actor.key)); ds_put_cstr(ds, "\tactor state:"); ds_put_lacp_state(ds, actor.state); ds_put_cstr(ds, "\n\n"); ds_put_format(ds, "\tpartner sys_id: " ETH_ADDR_FMT "\n", ETH_ADDR_ARGS(slave->partner.sys_id)); ds_put_format(ds, "\tpartner sys_priority: %u\n", ntohs(slave->partner.sys_priority)); ds_put_format(ds, "\tpartner port_id: %u\n", ntohs(slave->partner.port_id)); ds_put_format(ds, "\tpartner port_priority: %u\n", ntohs(slave->partner.port_priority)); ds_put_format(ds, "\tpartner key: %u\n", ntohs(slave->partner.key)); ds_put_cstr(ds, "\tpartner state:"); ds_put_lacp_state(ds, slave->partner.state); ds_put_cstr(ds, "\n"); } shash_destroy(&slave_shash); free(sorted_slaves); } static void lacp_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) OVS_EXCLUDED(mutex) { struct ds ds = DS_EMPTY_INITIALIZER; struct lacp *lacp; lacp_lock(); if (argc > 1) { lacp = lacp_find(argv[1]); if (!lacp) { unixctl_command_reply_error(conn, "no such lacp object"); goto out; } lacp_print_details(&ds, lacp); } else { LIST_FOR_EACH (lacp, node, all_lacps) { lacp_print_details(&ds, lacp); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); out: lacp_unlock(); } /* Extract a snapshot of the current state and counters for a slave port. Return false if the slave is not active. */ bool lacp_get_slave_stats(const struct lacp *lacp, const void *slave_, struct lacp_slave_stats *stats) OVS_EXCLUDED(mutex) { struct slave *slave; struct lacp_info actor; bool ret; ovs_mutex_lock(&mutex); slave = slave_lookup(lacp, slave_); if (slave) { ret = true; slave_get_actor(slave, &actor); stats->dot3adAggPortActorSystemID = actor.sys_id; stats->dot3adAggPortPartnerOperSystemID = slave->partner.sys_id; stats->dot3adAggPortAttachedAggID = (lacp->key_slave->key ? lacp->key_slave->key : lacp->key_slave->port_id); /* Construct my admin-state. Assume aggregation is configured on. */ stats->dot3adAggPortActorAdminState = LACP_STATE_AGG; if (lacp->active) { stats->dot3adAggPortActorAdminState |= LACP_STATE_ACT; } if (lacp->fast) { stats->dot3adAggPortActorAdminState |= LACP_STATE_TIME; } /* XXX Not sure how to know the partner admin state. It * might have to be captured and remembered during the * negotiation phase. */ stats->dot3adAggPortPartnerAdminState = 0; stats->dot3adAggPortActorOperState = actor.state; stats->dot3adAggPortPartnerOperState = slave->partner.state; /* Read out the latest counters */ stats->dot3adAggPortStatsLACPDUsRx = slave->count_rx_pdus; stats->dot3adAggPortStatsIllegalRx = slave->count_rx_pdus_bad; stats->dot3adAggPortStatsLACPDUsTx = slave->count_tx_pdus; } else { ret = false; } ovs_mutex_unlock(&mutex); return ret; } openvswitch-2.5.9/lib/PaxHeaders.82075/daemon-unix.c0000644000000000000000000000013213534540071017002 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.965854783 openvswitch-2.5.9/lib/daemon-unix.c0000644000175000017500000007373313534540071020505 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "daemon.h" #include "daemon-private.h" #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBCAPNG #include #endif #include "command-line.h" #include "fatal-signal.h" #include "dirs.h" #include "lockfile.h" #include "ovs-thread.h" #include "process.h" #include "socket-util.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(daemon_unix); #ifdef __linux__ #define LINUX 1 #else #define LINUX 0 #endif #if HAVE_LIBCAPNG #define LIBCAPNG 1 #else #define LIBCAPNG 0 #endif /* --detach: Should we run in the background? */ bool detach; /* Was --detach specified? */ static bool detached; /* Have we already detached? */ /* --pidfile: Name of pidfile (null if none). */ char *pidfile; /* Device and inode of pidfile, so we can avoid reopening it. */ static dev_t pidfile_dev; static ino_t pidfile_ino; /* --overwrite-pidfile: Create pidfile even if one already exists and is locked? */ static bool overwrite_pidfile; /* --no-chdir: Should we chdir to "/"? */ static bool chdir_ = true; /* File descriptor used by daemonize_start() and daemonize_complete(). */ static int daemonize_fd = -1; /* --monitor: Should a supervisory process monitor the daemon and restart it if * it dies due to an error signal? */ static bool monitor; /* --user: Only root can use this option. Switch to new uid:gid after * initially running as root. */ static bool switch_user = false; static uid_t uid; static gid_t gid; static char *user = NULL; static void daemon_become_new_user__(bool access_datapath); static void check_already_running(void); static int lock_pidfile(FILE *, int command); static pid_t fork_and_clean_up(void); static void daemonize_post_detach(void); /* Returns the file name that would be used for a pidfile if 'name' were * provided to set_pidfile(). The caller must free the returned string. */ char * make_pidfile_name(const char *name) { return (!name ? xasprintf("%s/%s.pid", ovs_rundir(), program_name) : abs_file_name(ovs_rundir(), name)); } /* Sets that we do not chdir to "/". */ void set_no_chdir(void) { chdir_ = false; } /* Normally, daemonize() or damonize_start() will terminate the program with a * message if a locked pidfile already exists. If this function is called, an * existing pidfile will be replaced, with a warning. */ void ignore_existing_pidfile(void) { overwrite_pidfile = true; } /* Sets up a following call to daemonize() to detach from the foreground * session, running this process in the background. */ void set_detach(void) { detach = true; } /* Sets up a following call to daemonize() to fork a supervisory process to * monitor the daemon and restart it if it dies due to an error signal. */ void daemon_set_monitor(void) { monitor = true; } /* If a pidfile has been configured, creates it and stores the running * process's pid in it. Ensures that the pidfile will be deleted when the * process exits. */ static void make_pidfile(void) { long int pid = getpid(); struct stat s; char *tmpfile; FILE *file; int error; /* Create a temporary pidfile. */ if (overwrite_pidfile) { tmpfile = xasprintf("%s.tmp%ld", pidfile, pid); fatal_signal_add_file_to_unlink(tmpfile); } else { /* Everyone shares the same file which will be treated as a lock. To * avoid some uncomfortable race conditions, we can't set up the fatal * signal unlink until we've acquired it. */ tmpfile = xasprintf("%s.tmp", pidfile); } file = fopen(tmpfile, "a+"); if (!file) { VLOG_FATAL("%s: create failed (%s)", tmpfile, ovs_strerror(errno)); } error = lock_pidfile(file, F_SETLK); if (error) { /* Looks like we failed to acquire the lock. Note that, if we failed * for some other reason (and '!overwrite_pidfile'), we will have * left 'tmpfile' as garbage in the file system. */ VLOG_FATAL("%s: fcntl(F_SETLK) failed (%s)", tmpfile, ovs_strerror(error)); } if (!overwrite_pidfile) { /* We acquired the lock. Make sure to clean up on exit, and verify * that we're allowed to create the actual pidfile. */ fatal_signal_add_file_to_unlink(tmpfile); check_already_running(); } if (fstat(fileno(file), &s) == -1) { VLOG_FATAL("%s: fstat failed (%s)", tmpfile, ovs_strerror(errno)); } if (ftruncate(fileno(file), 0) == -1) { VLOG_FATAL("%s: truncate failed (%s)", tmpfile, ovs_strerror(errno)); } fprintf(file, "%ld\n", pid); if (fflush(file) == EOF) { VLOG_FATAL("%s: write failed (%s)", tmpfile, ovs_strerror(errno)); } error = rename(tmpfile, pidfile); /* Due to a race, 'tmpfile' may be owned by a different process, so we * shouldn't delete it on exit. */ fatal_signal_remove_file_to_unlink(tmpfile); if (error < 0) { VLOG_FATAL("failed to rename \"%s\" to \"%s\" (%s)", tmpfile, pidfile, ovs_strerror(errno)); } /* Ensure that the pidfile will get deleted on exit. */ fatal_signal_add_file_to_unlink(pidfile); /* Clean up. * * We don't close 'file' because its file descriptor must remain open to * hold the lock. */ pidfile_dev = s.st_dev; pidfile_ino = s.st_ino; free(tmpfile); } /* Calls fork() and on success returns its return value. On failure, logs an * error and exits unsuccessfully. * * Post-fork, but before returning, this function calls a few other functions * that are generally useful if the child isn't planning to exec a new * process. */ static pid_t fork_and_clean_up(void) { pid_t pid = xfork(); if (pid > 0) { /* Running in parent process. */ fatal_signal_fork(); } else if (!pid) { /* Running in child process. */ lockfile_postfork(); } return pid; } /* Forks, then: * * - In the parent, waits for the child to signal that it has completed its * startup sequence. Then stores -1 in '*fdp' and returns the child's * pid in '*child_pid' argument. * * - In the child, stores a fd in '*fdp' and returns 0 through '*child_pid' * argument. The caller should pass the fd to fork_notify_startup() after * it finishes its startup sequence. * * Returns 0 on success. If something goes wrong and child process was not * able to signal its readiness by calling fork_notify_startup(), then this * function returns -1. However, even in case of failure it still sets child * process id in '*child_pid'. */ static int fork_and_wait_for_startup(int *fdp, pid_t *child_pid) { int fds[2]; pid_t pid; int ret = 0; xpipe(fds); pid = fork_and_clean_up(); if (pid > 0) { /* Running in parent process. */ size_t bytes_read; char c; close(fds[1]); if (read_fully(fds[0], &c, 1, &bytes_read) != 0) { int retval; int status; do { retval = waitpid(pid, &status, 0); } while (retval == -1 && errno == EINTR); if (retval == pid) { if (WIFEXITED(status) && WEXITSTATUS(status)) { /* Child exited with an error. Convey the same error * to our parent process as a courtesy. */ exit(WEXITSTATUS(status)); } else { char *status_msg = process_status_msg(status); VLOG_ERR("fork child died before signaling startup (%s)", status_msg); ret = -1; } } else if (retval < 0) { VLOG_FATAL("waitpid failed (%s)", ovs_strerror(errno)); } else { OVS_NOT_REACHED(); } } close(fds[0]); *fdp = -1; } else if (!pid) { /* Running in child process. */ close(fds[0]); *fdp = fds[1]; } *child_pid = pid; return ret; } static void fork_notify_startup(int fd) { if (fd != -1) { size_t bytes_written; int error; error = write_fully(fd, "", 1, &bytes_written); if (error) { VLOG_FATAL("pipe write failed (%s)", ovs_strerror(error)); } close(fd); } } static bool should_restart(int status) { if (WIFSIGNALED(status)) { static const int error_signals[] = { /* This list of signals is documented in daemon.man. If you * change the list, update the documentation too. */ SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV, SIGXCPU, SIGXFSZ }; size_t i; for (i = 0; i < ARRAY_SIZE(error_signals); i++) { if (error_signals[i] == WTERMSIG(status)) { return true; } } } return false; } static void monitor_daemon(pid_t daemon_pid) { /* XXX Should log daemon's stderr output at startup time. */ time_t last_restart; char *status_msg; int crashes; bool child_ready = true; set_subprogram_name("monitor"); status_msg = xstrdup("healthy"); last_restart = TIME_MIN; crashes = 0; for (;;) { int retval; int status; ovs_cmdl_proctitle_set("monitoring pid %lu (%s)", (unsigned long int) daemon_pid, status_msg); if (child_ready) { int error; do { retval = waitpid(daemon_pid, &status, 0); error = retval == -1 ? errno : 0; } while (error == EINTR); vlog_reopen_log_file(); if (error) { VLOG_FATAL("waitpid failed (%s)", ovs_strerror(error)); } } if (!child_ready || retval == daemon_pid) { char *s = process_status_msg(status); if (should_restart(status)) { free(status_msg); status_msg = xasprintf("%d crashes: pid %lu died, %s", ++crashes, (unsigned long int) daemon_pid, s); free(s); if (WCOREDUMP(status)) { /* Disable further core dumps to save disk space. */ struct rlimit r; r.rlim_cur = 0; r.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &r) == -1) { VLOG_WARN("failed to disable core dumps: %s", ovs_strerror(errno)); } } /* Throttle restarts to no more than once every 10 seconds. */ if (time(NULL) < last_restart + 10) { VLOG_WARN("%s, waiting until 10 seconds since last " "restart", status_msg); for (;;) { time_t now = time(NULL); time_t wakeup = last_restart + 10; if (now >= wakeup) { break; } xsleep(wakeup - now); } } last_restart = time(NULL); VLOG_ERR("%s, restarting", status_msg); child_ready = !fork_and_wait_for_startup(&daemonize_fd, &daemon_pid); if (child_ready && !daemon_pid) { /* Child process needs to break out of monitoring * loop. */ break; } } else { VLOG_INFO("pid %lu died, %s, exiting", (unsigned long int) daemon_pid, s); free(s); exit(0); } } } free(status_msg); /* Running in new daemon process. */ ovs_cmdl_proctitle_restore(); set_subprogram_name(program_name); } /* If daemonization is configured, then starts daemonization, by forking and * returning in the child process. The parent process hangs around until the * child lets it know either that it completed startup successfully (by calling * daemon_complete()) or that it failed to start up (by exiting with a nonzero * exit code). */ void daemonize_start(bool access_datapath) { assert_single_threaded(); daemonize_fd = -1; if (switch_user) { daemon_become_new_user__(access_datapath); switch_user = false; } if (detach) { pid_t pid; if (fork_and_wait_for_startup(&daemonize_fd, &pid)) { VLOG_FATAL("could not detach from foreground session"); } if (pid > 0) { /* Running in parent process. */ exit(0); } /* Running in daemon or monitor process. */ setsid(); } if (monitor) { int saved_daemonize_fd = daemonize_fd; pid_t daemon_pid; if (fork_and_wait_for_startup(&daemonize_fd, &daemon_pid)) { VLOG_FATAL("could not initiate process monitoring"); } if (daemon_pid > 0) { /* Running in monitor process. */ fork_notify_startup(saved_daemonize_fd); close_standard_fds(); monitor_daemon(daemon_pid); } /* Running in daemon process. */ } forbid_forking("running in daemon process"); if (pidfile) { make_pidfile(); } /* Make sure that the unixctl commands for vlog get registered in a * daemon, even before the first log message. */ vlog_init(); } /* If daemonization is configured, then this function notifies the parent * process that the child process has completed startup successfully. It also * call daemonize_post_detach(). * * Calling this function more than once has no additional effect. */ void daemonize_complete(void) { if (pidfile) { free(pidfile); pidfile = NULL; } if (!detached) { detached = true; fork_notify_startup(daemonize_fd); daemonize_fd = -1; daemonize_post_detach(); } } /* If daemonization is configured, then this function does traditional Unix * daemonization behavior: join a new session, chdir to the root (if not * disabled), and close the standard file descriptors. * * It only makes sense to call this function as part of an implementation of a * special daemon subprocess. A normal daemon should just call * daemonize_complete(). */ static void daemonize_post_detach(void) { if (detach) { if (chdir_) { ignore(chdir("/")); } close_standard_fds(); } } void daemon_usage(void) { printf( "\nDaemon options:\n" " --detach run in background as daemon\n" " --no-chdir do not chdir to '/'\n" " --pidfile[=FILE] create pidfile (default: %s/%s.pid)\n" " --overwrite-pidfile with --pidfile, start even if already " "running\n", ovs_rundir(), program_name); } static int lock_pidfile__(FILE *file, int command, struct flock *lck) { int error; lck->l_type = F_WRLCK; lck->l_whence = SEEK_SET; lck->l_start = 0; lck->l_len = 0; lck->l_pid = 0; do { error = fcntl(fileno(file), command, lck) == -1 ? errno : 0; } while (error == EINTR); return error; } static int lock_pidfile(FILE *file, int command) { struct flock lck; return lock_pidfile__(file, command, &lck); } static pid_t read_pidfile__(const char *pidfile, bool delete_if_stale) { struct stat s, s2; struct flock lck; char line[128]; FILE *file; int error; if ((pidfile_ino || pidfile_dev) && !stat(pidfile, &s) && s.st_ino == pidfile_ino && s.st_dev == pidfile_dev) { /* It's our own pidfile. We can't afford to open it, because closing * *any* fd for a file that a process has locked also releases all the * locks on that file. * * Fortunately, we know the associated pid anyhow: */ return getpid(); } file = fopen(pidfile, "r+"); if (!file) { if (errno == ENOENT && delete_if_stale) { return 0; } error = errno; VLOG_WARN("%s: open: %s", pidfile, ovs_strerror(error)); goto error; } error = lock_pidfile__(file, F_GETLK, &lck); if (error) { VLOG_WARN("%s: fcntl: %s", pidfile, ovs_strerror(error)); goto error; } if (lck.l_type == F_UNLCK) { /* pidfile exists but it isn't locked by anyone. We need to delete it * so that a new pidfile can go in its place. But just calling * unlink(pidfile) makes a nasty race: what if someone else unlinks it * before we do and then replaces it by a valid pidfile? We'd unlink * their valid pidfile. We do a little dance to avoid the race, by * locking the invalid pidfile. Only one process can have the invalid * pidfile locked, and only that process has the right to unlink it. */ if (!delete_if_stale) { error = ESRCH; VLOG_DBG("%s: pid file is stale", pidfile); goto error; } /* Get the lock. */ error = lock_pidfile(file, F_SETLK); if (error) { /* We lost a race with someone else doing the same thing. */ VLOG_WARN("%s: lost race to lock pidfile", pidfile); goto error; } /* Is the file we have locked still named 'pidfile'? */ if (stat(pidfile, &s) || fstat(fileno(file), &s2) || s.st_ino != s2.st_ino || s.st_dev != s2.st_dev) { /* No. We lost a race with someone else who got the lock before * us, deleted the pidfile, and closed it (releasing the lock). */ error = EALREADY; VLOG_WARN("%s: lost race to delete pidfile", pidfile); goto error; } /* We won the right to delete the stale pidfile. */ if (unlink(pidfile)) { error = errno; VLOG_WARN("%s: failed to delete stale pidfile (%s)", pidfile, ovs_strerror(error)); goto error; } VLOG_DBG("%s: deleted stale pidfile", pidfile); fclose(file); return 0; } if (!fgets(line, sizeof line, file)) { if (ferror(file)) { error = errno; VLOG_WARN("%s: read: %s", pidfile, ovs_strerror(error)); } else { error = ESRCH; VLOG_WARN("%s: read: unexpected end of file", pidfile); } goto error; } if (lck.l_pid != strtoul(line, NULL, 10)) { /* The process that has the pidfile locked is not the process that * created it. It must be stale, with the process that has it locked * preparing to delete it. */ error = ESRCH; VLOG_WARN("%s: stale pidfile for pid %s being deleted by pid %ld", pidfile, line, (long int) lck.l_pid); goto error; } fclose(file); return lck.l_pid; error: if (file) { fclose(file); } return -error; } /* Opens and reads a PID from 'pidfile'. Returns the positive PID if * successful, otherwise a negative errno value. */ pid_t read_pidfile(const char *pidfile) { return read_pidfile__(pidfile, false); } /* Checks whether a process with the given 'pidfile' is already running and, * if so, aborts. If 'pidfile' is stale, deletes it. */ static void check_already_running(void) { long int pid = read_pidfile__(pidfile, true); if (pid > 0) { VLOG_FATAL("%s: already running as pid %ld, aborting", pidfile, pid); } else if (pid < 0) { VLOG_FATAL("%s: pidfile check failed (%s), aborting", pidfile, ovs_strerror(-pid)); } } /* stub functions for non-windows platform. */ void service_start(int *argc OVS_UNUSED, char **argv[] OVS_UNUSED) { } void service_stop(void) { } bool should_service_stop(void) { return false; } static bool gid_matches(gid_t expected, gid_t value) { return expected == -1 || expected == value; } static bool gid_verify(gid_t gid) { gid_t r, e; r = getgid(); e = getegid(); return (gid_matches(gid, r) && gid_matches(gid, e)); } static void daemon_switch_group(gid_t gid) { if ((setgid(gid) == -1) || !gid_verify(gid)) { VLOG_FATAL("%s: fail to switch group to gid as %d, aborting", pidfile, gid); } } static bool uid_matches(uid_t expected, uid_t value) { return expected == -1 || expected == value; } static bool uid_verify(const uid_t uid) { uid_t r, e; r = getuid(); e = geteuid(); return (uid_matches(uid, r) && uid_matches(uid, e)); } static void daemon_switch_user(const uid_t uid, const char *user) { if ((setuid(uid) == -1) || !uid_verify(uid)) { VLOG_FATAL("%s: fail to switch user to %s, aborting", pidfile, user); } } /* Use portable Unix APIs to switch uid:gid, when datapath * access is not required. On Linux systems, all capabilities * will be dropped. */ static void daemon_become_new_user_unix(void) { /* "Setuid Demystified" by Hao Chen, etc outlines some caveats of * around unix system call setuid() and friends. This implementation * mostly follow the advice given by the paper. The paper is * published in 2002, so things could have changed. */ /* Change both real and effective uid and gid will permanently * drop the process' privilege. "Setuid Demystified" suggested * that calling getuid() after each setuid() call to verify they * are actually set, because checking return code alone is not * sufficient. */ daemon_switch_group(gid); if (user && initgroups(user, gid) == -1) { VLOG_FATAL("%s: fail to add supplementary group gid %d, " "aborting", pidfile, gid); } daemon_switch_user(uid, user); } /* Linux specific implementation of daemon_become_new_user() * using libcap-ng. */ static void daemon_become_new_user_linux(bool access_datapath OVS_UNUSED) { #if defined __linux__ && HAVE_LIBCAPNG int ret; ret = capng_get_caps_process(); if (!ret) { if (capng_have_capabilities(CAPNG_SELECT_CAPS) > CAPNG_NONE) { const capng_type_t cap_sets = CAPNG_EFFECTIVE|CAPNG_PERMITTED; capng_clear(CAPNG_SELECT_BOTH); ret = capng_update(CAPNG_ADD, cap_sets, CAP_IPC_LOCK) || capng_update(CAPNG_ADD, cap_sets, CAP_NET_BIND_SERVICE); if (access_datapath && !ret) { ret = capng_update(CAPNG_ADD, cap_sets, CAP_NET_ADMIN) || capng_update(CAPNG_ADD, cap_sets, CAP_NET_RAW); } } else { ret = -1; } } if (!ret) { /* CAPNG_INIT_SUPP_GRP will be a better choice than * CAPNG_DROP_SUPP_GRP. However this enum value is only defined * with libcap-ng higher than version 0.7.4, which is not wildly * available on many Linux distributions yet. Taking a more * conservative approach to make sure OVS behaves consistently. * * XXX We may change this for future OVS releases. */ ret = capng_change_id(uid, gid, CAPNG_DROP_SUPP_GRP | CAPNG_CLEAR_BOUNDING); } if (ret) { VLOG_FATAL("%s: libcap-ng fail to switch to user and group " "%d:%d, aborting", pidfile, uid, gid); } #endif } static void daemon_become_new_user__(bool access_datapath) { /* If vlog file has been created, change its owner to the non-root user * as specifed by the --user option. */ vlog_change_owner_unix(uid, gid); if (LINUX) { if (LIBCAPNG) { daemon_become_new_user_linux(access_datapath); } else { VLOG_FATAL("%s: fail to downgrade user using libcap-ng. " "(libcap-ng is not configured at compile time), " "aborting.", pidfile); } } else { daemon_become_new_user_unix(); } } /* Noramlly, user switch is embedded within daemonize_start(). * However, there in case the user switch needs to be done * before daemonize_start(), the following API can be used. */ void daemon_become_new_user(bool access_datapath) { assert_single_threaded(); if (switch_user) { daemon_become_new_user__(access_datapath); /* daemonize_start() should not switch user again. */ switch_user = false; } } /* Return the maximun suggested buffer size for both getpwname_r() * and getgrnam_r(). * * This size may still not be big enough. in case getpwname_r() * and friends return ERANGE, a larger buffer should be supplied to * retry. (The man page did not specify the max size to stop at, we * will keep trying with doubling the buffer size for each round until * the size wrapps around size_t. */ static size_t get_sysconf_buffer_size(void) { size_t bufsize, pwd_bs = 0, grp_bs = 0; const size_t default_bufsize = 1024; errno = 0; if ((pwd_bs = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) { if (errno) { VLOG_FATAL("%s: Read initial passwordd struct size " "failed (%s), aborting. ", pidfile, ovs_strerror(errno)); } } if ((grp_bs = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) { if (errno) { VLOG_FATAL("%s: Read initial group struct size " "failed (%s), aborting. ", pidfile, ovs_strerror(errno)); } } bufsize = MAX(pwd_bs, grp_bs); return bufsize ? bufsize : default_bufsize; } /* Try to double the size of '*buf', return true * if successful, and '*sizep' will be updated with * the new size. Otherwise, return false. */ static bool enlarge_buffer(char **buf, size_t *sizep) { size_t newsize = *sizep * 2; if (newsize > *sizep) { *buf = xrealloc(*buf, newsize); *sizep = newsize; return true; } return false; } /* Parse and sanity check user_spec. * * If successful, set global variables 'uid' and 'gid' * with the parsed results. Global variable 'user' * will be pointing to a string that stores the name * of the user to be switched into. * * Also set 'switch_to_new_user' to true, The actual * user switching is done as soon as daemonize_start() * is called. I/O access before calling daemonize_start() * will still be with root's credential. */ void daemon_set_new_user(const char *user_spec) { char *pos = strchr(user_spec, ':'); size_t init_bufsize, bufsize; init_bufsize = get_sysconf_buffer_size(); uid = getuid(); gid = getgid(); if (geteuid() || uid) { VLOG_FATAL("%s: only root can use --user option", pidfile); } user_spec += strspn(user_spec, " \t\r\n"); size_t len = pos ? pos - user_spec : strlen(user_spec); char *buf; struct passwd pwd, *res; int e; bufsize = init_bufsize; buf = xmalloc(bufsize); if (len) { user = xmemdup0(user_spec, len); while ((e = getpwnam_r(user, &pwd, buf, bufsize, &res)) == ERANGE) { if (!enlarge_buffer(&buf, &bufsize)) { break; } } if (e != 0) { VLOG_FATAL("%s: Failed to retrive user %s's uid (%s), aborting.", pidfile, user, ovs_strerror(e)); } if (res == NULL) { VLOG_FATAL("%s: user %s not found, aborting.", pidfile, user); } } else { /* User name is not specified, use current user. */ while ((e = getpwuid_r(uid, &pwd, buf, bufsize, &res)) == ERANGE) { if (!enlarge_buffer(&buf, &bufsize)) { break; } } if (e != 0) { VLOG_FATAL("%s: Failed to retrive current user's name " "(%s), aborting.", pidfile, ovs_strerror(e)); } user = xstrdup(pwd.pw_name); } uid = pwd.pw_uid; gid = pwd.pw_gid; free(buf); if (pos) { char *grpstr = pos + 1; grpstr += strspn(grpstr, " \t\r\n"); if (*grpstr) { struct group grp, *res; bufsize = init_bufsize; buf = xmalloc(bufsize); while ((e = getgrnam_r(grpstr, &grp, buf, bufsize, &res)) == ERANGE) { if (!enlarge_buffer(&buf, &bufsize)) { break; } } if (e) { VLOG_FATAL("%s: Failed to get group entry for %s, " "(%s), aborting.", pidfile, grpstr, ovs_strerror(e)); } if (res == NULL) { VLOG_FATAL("%s: group %s not found, aborting.", pidfile, grpstr); } if (gid != grp.gr_gid) { char **mem; for (mem = grp.gr_mem; *mem; ++mem) { if (!strcmp(*mem, user)) { break; } } if (!*mem) { VLOG_FATAL("%s: Invalid --user option %s (user %s is " "not in group %s), aborting.", pidfile, user_spec, user, grpstr); } gid = grp.gr_gid; } free(buf); } } switch_user = true; } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-print.h0000644000000000000000000000013213534540071016501 xustar0030 mtime=1567801401.525682081 30 atime=1567801402.089686223 30 ctime=1567801424.801853573 openvswitch-2.5.9/lib/ofp-print.h0000644000175000017500000000337013534540071020172 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011, 2012, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* OpenFlow protocol pretty-printer. */ #ifndef OFP_PRINT_H #define OFP_PRINT_H 1 #include #include struct ds; struct ofp10_match; struct ofp_flow_mod; struct ofp_header; struct ofputil_flow_stats; struct ofputil_table_features; struct ofputil_table_stats; #ifdef __cplusplus extern "C" { #endif void ofp_print(FILE *, const void *, size_t, int verbosity); void ofp_print_packet(FILE *stream, const void *data, size_t len); void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity); char *ofp_to_string(const void *, size_t, int verbosity); char *ofp10_match_to_string(const struct ofp10_match *, int verbosity); char *ofp_packet_to_string(const void *data, size_t len); void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *); void ofp_print_version(const struct ofp_header *, struct ds *); void ofp_print_table_features( struct ds *, const struct ofputil_table_features *features, const struct ofputil_table_features *prev_features, const struct ofputil_table_stats *stats, const struct ofputil_table_stats *prev_stats); #ifdef __cplusplus } #endif #endif /* ofp-print.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stream-tcp.c0000644000000000000000000000013213534540071016635 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.893854251 openvswitch-2.5.9/lib/stream-tcp.c0000644000175000017500000001506013534540071020325 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream.h" #include #include #include #include #include #include #include #include #include #include "dynamic-string.h" #include "packets.h" #include "socket-util.h" #include "util.h" #include "stream-provider.h" #include "stream-fd.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stream_tcp); /* Active TCP. */ static int new_tcp_stream(const char *name, int fd, int connect_status, struct stream **streamp) { if (connect_status == 0) { setsockopt_tcp_nodelay(fd); } return new_fd_stream(name, fd, connect_status, AF_INET, streamp); } static int tcp_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp) { int fd, error; error = inet_open_active(SOCK_STREAM, suffix, 0, NULL, &fd, dscp); if (fd >= 0) { return new_tcp_stream(name, fd, error, streamp); } else { VLOG_ERR("%s: connect: %s", name, ovs_strerror(error)); return error; } } const struct stream_class tcp_stream_class = { "tcp", /* name */ true, /* needs_probes */ tcp_open, /* open */ NULL, /* close */ NULL, /* connect */ NULL, /* recv */ NULL, /* send */ NULL, /* run */ NULL, /* run_wait */ NULL, /* wait */ }; #ifdef _WIN32 #include "dirs.h" static int windows_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp) { int error, port; FILE *file; char *suffix_new, *path; /* If the path does not contain a ':', assume it is relative to * OVS_RUNDIR. */ if (!strchr(suffix, ':')) { path = xasprintf("%s/%s", ovs_rundir(), suffix); } else { path = xstrdup(suffix); } file = fopen(path, "r"); if (!file) { error = errno; VLOG_DBG("%s: could not open %s (%s)", name, suffix, ovs_strerror(error)); return error; } error = fscanf(file, "%d", &port); if (error != 1) { VLOG_ERR("failed to read port from %s", suffix); fclose(file); return EINVAL; } fclose(file); suffix_new = xasprintf("127.0.0.1:%d", port); error = tcp_open(name, suffix_new, streamp, dscp); free(suffix_new); free(path); return error; } const struct stream_class windows_stream_class = { "unix", /* name */ false, /* needs_probes */ windows_open, /* open */ NULL, /* close */ NULL, /* connect */ NULL, /* recv */ NULL, /* send */ NULL, /* run */ NULL, /* run_wait */ NULL, /* wait */ }; #endif /* Passive TCP. */ static int ptcp_accept(int fd, const struct sockaddr_storage *, size_t, struct stream **streamp); static int new_pstream(char *suffix, const char *name, struct pstream **pstreamp, int dscp, char *unlink_path, bool kernel_print_port) { char bound_name[SS_NTOP_BUFSIZE + 16]; char addrbuf[SS_NTOP_BUFSIZE]; struct sockaddr_storage ss; int error; uint16_t port; int fd; char *conn_name = CONST_CAST(char *, name); fd = inet_open_passive(SOCK_STREAM, suffix, -1, &ss, dscp, kernel_print_port); if (fd < 0) { return -fd; } port = ss_get_port(&ss); if (!conn_name) { snprintf(bound_name, sizeof bound_name, "ptcp:%"PRIu16":%s", port, ss_format_address(&ss, addrbuf, sizeof addrbuf)); conn_name = bound_name; } error = new_fd_pstream(conn_name, fd, ptcp_accept, unlink_path, pstreamp); if (!error) { pstream_set_bound_port(*pstreamp, htons(port)); } return error; } static int ptcp_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp, uint8_t dscp) { return new_pstream(suffix, NULL, pstreamp, dscp, NULL, true); } static int ptcp_accept(int fd, const struct sockaddr_storage *ss, size_t ss_len OVS_UNUSED, struct stream **streamp) { char name[SS_NTOP_BUFSIZE + 16]; char addrbuf[SS_NTOP_BUFSIZE]; snprintf(name, sizeof name, "tcp:%s:%"PRIu16, ss_format_address(ss, addrbuf, sizeof addrbuf), ss_get_port(ss)); return new_tcp_stream(name, fd, 0, streamp); } const struct pstream_class ptcp_pstream_class = { "ptcp", true, ptcp_open, NULL, NULL, NULL, }; #ifdef _WIN32 static int pwindows_open(const char *name, char *suffix, struct pstream **pstreamp, uint8_t dscp) { int error; char *suffix_new, *path; FILE *file; struct pstream *listener; suffix_new = xstrdup("0:127.0.0.1"); /* If the path does not contain a ':', assume it is relative to * OVS_RUNDIR. */ if (!strchr(suffix, ':')) { path = xasprintf("%s/%s", ovs_rundir(), suffix); } else { path = xstrdup(suffix); } error = new_pstream(suffix_new, name, pstreamp, dscp, path, false); if (error) { goto exit; } listener = *pstreamp; file = fopen(path, "w"); if (!file) { error = errno; VLOG_DBG("could not open %s (%s)", path, ovs_strerror(error)); goto exit; } fprintf(file, "%d\n", ntohs(listener->bound_port)); if (fflush(file) == EOF) { error = EIO; VLOG_ERR("write failed for %s", path); fclose(file); goto exit; } fclose(file); exit: free(suffix_new); return error; } const struct pstream_class pwindows_pstream_class = { "punix", false, pwindows_open, NULL, NULL, NULL, }; #endif openvswitch-2.5.9/lib/PaxHeaders.82075/perf-counter.c0000644000000000000000000000013213534540071017167 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.849853928 openvswitch-2.5.9/lib/perf-counter.c0000644000175000017500000001052013534540071020653 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This implementation only applies to the Linux platform. */ #include #if defined(__linux__) && defined(HAVE_LINUX_PERF_EVENT_H) #include #include #include #include #include #include #include #include "dynamic-string.h" #include "perf-counter.h" #include "shash.h" #include "util.h" static struct shash perf_counters = SHASH_INITIALIZER(&perf_counters); static int fd__ = 0; uint64_t perf_counter_read(uint64_t *counter) { int size = sizeof *counter; if (fd__ <= 0 || read(fd__, counter, size) < size) { *counter = 0; } return *counter; } static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { int ret; ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); return ret; } /* Set up perf event counters to read user space instruction counters * only for this process, on all cpus. */ static void perf_event_setup(void) { struct perf_event_attr pe; memset(&pe, 0, sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_INSTRUCTIONS; pe.disabled = 1; pe.exclude_kernel = 1; pe.exclude_hv = 1; fd__ = perf_event_open(&pe, 0, -1, -1, 0); if (fd__ > 0) { ioctl(fd__, PERF_EVENT_IOC_RESET, 0); ioctl(fd__, PERF_EVENT_IOC_ENABLE, 0); } } static void perf_counter_init(struct perf_counter *counter) { counter->once = true; shash_add_assert(&perf_counters, counter->name, counter); } void perf_counter_accumulate(struct perf_counter *counter, uint64_t start_count) { uint64_t end_count; if (!counter->once) { perf_counter_init(counter); } counter->n_events++; perf_counter_read(&end_count); counter->total_count += end_count - start_count; } static void perf_counter_to_ds(struct ds *ds, struct perf_counter *pfc) { double ratio; if (pfc->n_events) { ratio = (double)pfc->total_count / (double)pfc->n_events; } else { ratio = 0.0; } ds_put_format(ds, "%-40s%12"PRIu64"%12"PRIu64"%12.1f\n", pfc->name, pfc->n_events, pfc->total_count, ratio); } static void perf_counters_to_ds(struct ds *ds) { const char *err_str; const struct shash_node **sorted; int i; err_str = NULL; if (fd__ == -1) { err_str = "performance counter is not supported on this platfrom"; } else if (!shash_count(&perf_counters)) { err_str = "performance counter has never been hit"; } if (err_str) { ds_put_format(ds, "%s\n", err_str); return; } /* Display counters in alphabetical order. */ sorted = shash_sort(&perf_counters); for (i = 0; i < shash_count(&perf_counters); i++) { perf_counter_to_ds(ds, sorted[i]->data); } free(sorted); } /* * Caller is responsible for free memory. */ char * perf_counters_to_string() { struct ds ds; ds_init(&ds); perf_counters_to_ds(&ds); return ds_steal_cstr(&ds); } void perf_counters_init(void) { shash_init(&perf_counters); perf_event_setup(); } void perf_counters_clear(void) { struct shash_node *node; SHASH_FOR_EACH (node, &perf_counters) { struct perf_counter *perf = node->data; perf->n_events = 0; perf->total_count = 0; } } void perf_counters_destroy() { struct shash_node *node, *next; if (fd__ != -1) { ioctl(fd__, PERF_EVENT_IOC_DISABLE, 0); close(fd__); } SHASH_FOR_EACH_SAFE (node, next, &perf_counters) { shash_delete(&perf_counters, node); } shash_destroy(&perf_counters); } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-windows.c0000644000000000000000000000013213534540071017533 xustar0030 mtime=1567801401.457681581 30 atime=1567801402.081686164 30 ctime=1567801424.997855018 openvswitch-2.5.9/lib/netdev-windows.c0000644000175000017500000003551713534540071021234 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "coverage.h" #include "fatal-signal.h" #include "netdev-provider.h" #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" #include "shash.h" #include "svec.h" #include "openvswitch/vlog.h" #include "odp-netlink.h" #include "netlink-socket.h" #include "netlink.h" VLOG_DEFINE_THIS_MODULE(netdev_windows); static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); enum { VALID_ETHERADDR = 1 << 0, VALID_MTU = 1 << 1, VALID_IFFLAG = 1 << 5, }; /* Caches the information of a netdev. */ struct netdev_windows { struct netdev up; int32_t dev_type; uint32_t port_no; unsigned int change_seq; unsigned int cache_valid; int ifindex; struct eth_addr mac; uint32_t mtu; unsigned int ifi_flags; }; /* Utility structure for netdev commands. */ struct netdev_windows_netdev_info { /* Generic Netlink header. */ uint8_t cmd; /* Information that is relevant to ovs. */ uint32_t dp_ifindex; uint32_t port_no; uint32_t ovs_type; /* General information of a network device. */ const char *name; struct eth_addr mac_address; uint32_t mtu; uint32_t ifi_flags; }; static int query_netdev(const char *devname, struct netdev_windows_netdev_info *reply, struct ofpbuf **bufp); static struct netdev *netdev_windows_alloc(void); static int netdev_windows_init_(void); /* Generic Netlink family numbers for OVS. * * Initialized by netdev_windows_init_(). */ static int ovs_win_netdev_family; struct nl_sock *ovs_win_netdev_sock; static bool is_netdev_windows_class(const struct netdev_class *netdev_class) { return netdev_class->alloc == netdev_windows_alloc; } static struct netdev_windows * netdev_windows_cast(const struct netdev *netdev_) { ovs_assert(is_netdev_windows_class(netdev_get_class(netdev_))); return CONTAINER_OF(netdev_, struct netdev_windows, up); } static int netdev_windows_init_(void) { int error = 0; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { error = nl_lookup_genl_family(OVS_WIN_NETDEV_FAMILY, &ovs_win_netdev_family); if (error) { VLOG_ERR("Generic Netlink family '%s' does not exist. " "The Open vSwitch kernel module is probably not loaded.", OVS_WIN_NETDEV_FAMILY); } if (!error) { /* XXX: Where to close this socket? */ error = nl_sock_create(NETLINK_GENERIC, &ovs_win_netdev_sock); } ovsthread_once_done(&once); } return error; } static struct netdev * netdev_windows_alloc(void) { struct netdev_windows *netdev = xzalloc(sizeof *netdev); return netdev ? &netdev->up : NULL; } static uint32_t dp_to_netdev_ifi_flags(uint32_t dp_flags) { uint32_t nd_flags = 0; if (dp_flags && OVS_WIN_NETDEV_IFF_UP) { nd_flags |= NETDEV_UP; } if (dp_flags && OVS_WIN_NETDEV_IFF_PROMISC) { nd_flags |= NETDEV_PROMISC; } return nd_flags; } static int netdev_windows_system_construct(struct netdev *netdev_) { struct netdev_windows *netdev = netdev_windows_cast(netdev_); struct netdev_windows_netdev_info info; struct ofpbuf *buf; int ret; /* Query the attributes and runtime status of the netdev. */ ret = query_netdev(netdev_get_name(&netdev->up), &info, &buf); /* "Internal" netdevs do not exist in the kernel yet. They need to be * transformed into a netdev object and passed to dpif_port_add(), which * will add them to the kernel. */ if (strcmp(netdev_get_type(&netdev->up), "internal") && ret) { return ret; } ofpbuf_delete(buf); netdev->change_seq = 1; netdev->dev_type = info.ovs_type; netdev->port_no = info.port_no; netdev->mac = info.mac_address; netdev->cache_valid = VALID_ETHERADDR; netdev->ifindex = -EOPNOTSUPP; netdev->mtu = info.mtu; netdev->cache_valid |= VALID_MTU; netdev->ifi_flags = dp_to_netdev_ifi_flags(info.ifi_flags); netdev->cache_valid |= VALID_IFFLAG; VLOG_DBG("construct device %s, ovs_type: %u.", netdev_get_name(&netdev->up), info.ovs_type); return 0; } static int netdev_windows_netdev_to_ofpbuf(struct netdev_windows_netdev_info *info, struct ofpbuf *buf) { struct ovs_header *ovs_header; int error = EINVAL; nl_msg_put_genlmsghdr(buf, 0, ovs_win_netdev_family, NLM_F_REQUEST | NLM_F_ECHO, info->cmd, OVS_WIN_NETDEV_VERSION); ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); ovs_header->dp_ifindex = info->dp_ifindex; if (info->name) { nl_msg_put_string(buf, OVS_WIN_NETDEV_ATTR_NAME, info->name); error = 0; } return error; } static void netdev_windows_info_init(struct netdev_windows_netdev_info *info) { memset(info, 0, sizeof *info); } static int netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info, struct ofpbuf *buf) { static const struct nl_policy ovs_netdev_policy[] = { [OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, [OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) }, [OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 }, }; struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; struct genlmsghdr *genl; struct ofpbuf b; netdev_windows_info_init(info); ofpbuf_use_const(&b, buf->data, buf->size); nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); genl = ofpbuf_try_pull(&b, sizeof *genl); ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_win_netdev_family || !nl_policy_parse(&b, 0, ovs_netdev_policy, a, ARRAY_SIZE(ovs_netdev_policy))) { return EINVAL; } info->cmd = genl->cmd; info->dp_ifindex = ovs_header->dp_ifindex; info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]); info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]); info->name = nl_attr_get_string(a[OVS_WIN_NETDEV_ATTR_NAME]); memcpy(&info->mac_address, nl_attr_get_unspec(a[OVS_WIN_NETDEV_ATTR_MAC_ADDR], sizeof(info->mac_address)), sizeof(info->mac_address)); info->mtu = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_MTU]); info->ifi_flags = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_IF_FLAGS]); return 0; } static int query_netdev(const char *devname, struct netdev_windows_netdev_info *info, struct ofpbuf **bufp) { int error = 0; struct ofpbuf *request_buf; ovs_assert(info != NULL); netdev_windows_info_init(info); error = netdev_windows_init_(); if (error) { if (info) { *bufp = NULL; netdev_windows_info_init(info); } return error; } request_buf = ofpbuf_new(1024); info->cmd = OVS_WIN_NETDEV_CMD_GET; info->name = devname; error = netdev_windows_netdev_to_ofpbuf(info, request_buf); if (error) { ofpbuf_delete(request_buf); return error; } error = nl_transact(NETLINK_GENERIC, request_buf, bufp); ofpbuf_delete(request_buf); if (info) { if (!error) { error = netdev_windows_netdev_from_ofpbuf(info, *bufp); } if (error) { netdev_windows_info_init(info); ofpbuf_delete(*bufp); *bufp = NULL; } } return error; } static void netdev_windows_destruct(struct netdev *netdev_) { } static void netdev_windows_dealloc(struct netdev *netdev_) { struct netdev_windows *netdev = netdev_windows_cast(netdev_); free(netdev); } static int netdev_windows_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) { struct netdev_windows *netdev = netdev_windows_cast(netdev_); ovs_assert((netdev->cache_valid & VALID_ETHERADDR) != 0); if (netdev->cache_valid & VALID_ETHERADDR) { *mac = netdev->mac; } else { return EINVAL; } return 0; } static int netdev_windows_get_mtu(const struct netdev *netdev_, int *mtup) { struct netdev_windows *netdev = netdev_windows_cast(netdev_); ovs_assert((netdev->cache_valid & VALID_MTU) != 0); if (netdev->cache_valid & VALID_MTU) { *mtup = netdev->mtu; } else { return EINVAL; } return 0; } /* This functionality is not really required by the datapath. * But vswitchd bringup expects this to be implemented. */ static int netdev_windows_set_etheraddr(const struct netdev *netdev_, const struct eth_addr mac) { return 0; } /* This functionality is not really required by the datapath. * But vswitchd bringup expects this to be implemented. */ static int netdev_windows_update_flags(struct netdev *netdev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { struct netdev_windows *netdev = netdev_windows_cast(netdev_); ovs_assert((netdev->cache_valid & VALID_IFFLAG) != 0); if (netdev->cache_valid & VALID_IFFLAG) { *old_flagsp = netdev->ifi_flags; /* Setting the interface flags is not supported. */ } else { return EINVAL; } return 0; } /* Looks up in the ARP table entry for a given 'ip'. If it is found, the * corresponding MAC address will be copied in 'mac' and return 0. If no * matching entry is found or an error occurs it will log it and return ENXIO. */ static int netdev_windows_arp_lookup(const struct netdev *netdev, ovs_be32 ip, struct eth_addr *mac) { PMIB_IPNETTABLE arp_table = NULL; /* The buffer length of all ARP entries */ uint32_t buffer_length = 0; uint32_t ret_val = 0; uint32_t counter = 0; ret_val = GetIpNetTable(arp_table, &buffer_length, false); if (ret_val != ERROR_INSUFFICIENT_BUFFER ) { VLOG_ERR("Call to GetIpNetTable failed with error: %s", ovs_format_message(ret_val)); return ENXIO; } arp_table = (MIB_IPNETTABLE *) xmalloc(buffer_length); ret_val = GetIpNetTable(arp_table, &buffer_length, false); if (ret_val == NO_ERROR) { for (counter = 0; counter < arp_table->dwNumEntries; counter++) { if (arp_table->table[counter].dwAddr == ip) { memcpy(mac, arp_table->table[counter].bPhysAddr, ETH_ADDR_LEN); free(arp_table); return 0; } } } else { VLOG_ERR("Call to GetIpNetTable failed with error: %s", ovs_format_message(ret_val)); } free(arp_table); return ENXIO; } static int netdev_windows_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, char **netdev_name) { uint32_t ret_val = 0; /* The buffer length of all addresses */ uint32_t buffer_length = 1000; PIP_ADAPTER_ADDRESSES all_addr = NULL; PIP_ADAPTER_ADDRESSES cur_addr = NULL; ret_val = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS, NULL, all_addr, &buffer_length); if (ret_val != ERROR_INSUFFICIENT_BUFFER ) { VLOG_ERR("Call to GetAdaptersAddresses failed with error: %s", ovs_format_message(ret_val)); return ENXIO; } all_addr = (IP_ADAPTER_ADDRESSES *) xmalloc(buffer_length); ret_val = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_INCLUDE_GATEWAYS, NULL, all_addr, &buffer_length); if (ret_val == NO_ERROR) { cur_addr = all_addr; while (cur_addr) { if(cur_addr->FirstGatewayAddress && cur_addr->FirstGatewayAddress->Address.lpSockaddr) { struct sockaddr_in *ipv4 = (struct sockaddr_in *) cur_addr->FirstGatewayAddress->Address.lpSockaddr; next_hop->s_addr = ipv4->sin_addr.S_un.S_addr; *netdev_name = xstrdup((char *)cur_addr->FriendlyName); free(all_addr); return 0; } cur_addr = cur_addr->Next; } } else { VLOG_ERR("Call to GetAdaptersAddresses failed with error: %s", ovs_format_message(ret_val)); } if (all_addr) { free(all_addr); } return ENXIO; } static int netdev_windows_internal_construct(struct netdev *netdev_) { return netdev_windows_system_construct(netdev_); } #define NETDEV_WINDOWS_CLASS(NAME, CONSTRUCT) \ { \ .type = NAME, \ .alloc = netdev_windows_alloc, \ .construct = CONSTRUCT, \ .destruct = netdev_windows_destruct, \ .dealloc = netdev_windows_dealloc, \ .get_etheraddr = netdev_windows_get_etheraddr, \ .set_etheraddr = netdev_windows_set_etheraddr, \ .update_flags = netdev_windows_update_flags, \ .get_next_hop = netdev_windows_get_next_hop, \ .arp_lookup = netdev_windows_arp_lookup, \ } const struct netdev_class netdev_windows_class = NETDEV_WINDOWS_CLASS( "system", netdev_windows_system_construct); const struct netdev_class netdev_internal_class = NETDEV_WINDOWS_CLASS( "internal", netdev_windows_internal_construct); openvswitch-2.5.9/lib/PaxHeaders.82075/vlog.c0000644000000000000000000000013213534540071015525 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801424.945854636 openvswitch-2.5.9/lib/vlog.c0000644000175000017500000011360413534540071017220 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "openvswitch/vlog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "async-append.h" #include "coverage.h" #include "dirs.h" #include "dynamic-string.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "sat-math.h" #include "socket-util.h" #include "svec.h" #include "syslog-direct.h" #include "syslog-libc.h" #include "syslog-provider.h" #include "timeval.h" #include "unixctl.h" #include "util.h" VLOG_DEFINE_THIS_MODULE(vlog); /* ovs_assert() logs the assertion message, so using ovs_assert() in this * source file could cause recursion. */ #undef ovs_assert #define ovs_assert use_assert_instead_of_ovs_assert_in_this_module /* Name for each logging level. */ static const char *const level_names[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) #NAME, VLOG_LEVELS #undef VLOG_LEVEL }; /* Syslog value for each logging level. */ static const int syslog_levels[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) SYSLOG_LEVEL, VLOG_LEVELS #undef VLOG_LEVEL }; /* RFC 5424 defines specific values for each syslog level. Normally LOG_* use * the same values. Verify that in fact they're the same. If we get assertion * failures here then we need to define a separate rfc5424_levels[] array. */ #define VLOG_LEVEL(NAME, SYSLOG_LEVEL, RFC5424) \ BUILD_ASSERT_DECL(SYSLOG_LEVEL == RFC5424); VLOG_LEVELS #undef VLOG_LEVELS /* Similarly, RFC 5424 defines the local0 facility with the value ordinarily * used for LOG_LOCAL0. */ BUILD_ASSERT_DECL(LOG_LOCAL0 == (16 << 3)); /* The log modules. */ struct ovs_list vlog_modules = OVS_LIST_INITIALIZER(&vlog_modules); /* Protects the 'pattern' in all "struct destination"s, so that a race between * changing and reading the pattern does not cause an access to freed * memory. */ static struct ovs_rwlock pattern_rwlock = OVS_RWLOCK_INITIALIZER; /* Information about each destination. */ struct destination { const char *name; /* Name. */ char *pattern OVS_GUARDED_BY(pattern_rwlock); /* Current pattern. */ bool default_pattern; /* Whether current pattern is the default. */ }; static struct destination destinations[VLF_N_DESTINATIONS] = { #define VLOG_DESTINATION(NAME, PATTERN) {#NAME, PATTERN, true}, VLOG_DESTINATIONS #undef VLOG_DESTINATION }; /* Sequence number for the message currently being composed. */ DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0); /* VLF_FILE configuration. * * All of the following is protected by 'log_file_mutex', which nests inside * pattern_rwlock. */ static struct ovs_mutex log_file_mutex = OVS_MUTEX_INITIALIZER; static char *log_file_name OVS_GUARDED_BY(log_file_mutex) = NULL; static int log_fd OVS_GUARDED_BY(log_file_mutex) = -1; static struct async_append *log_writer OVS_GUARDED_BY(log_file_mutex); static bool log_async OVS_GUARDED_BY(log_file_mutex); static struct syslogger *syslogger = NULL; /* Syslog export configuration. */ static int syslog_fd OVS_GUARDED_BY(pattern_rwlock) = -1; /* Log facility configuration. */ static atomic_int log_facility = ATOMIC_VAR_INIT(0); /* Facility name and its value. */ struct vlog_facility { char *name; /* Name. */ unsigned int value; /* Facility associated with 'name'. */ }; static struct vlog_facility vlog_facilities[] = { {"kern", LOG_KERN}, {"user", LOG_USER}, {"mail", LOG_MAIL}, {"daemon", LOG_DAEMON}, {"auth", LOG_AUTH}, {"syslog", LOG_SYSLOG}, {"lpr", LOG_LPR}, {"news", LOG_NEWS}, {"uucp", LOG_UUCP}, {"clock", LOG_CRON}, {"ftp", LOG_FTP}, {"ntp", 12<<3}, {"audit", 13<<3}, {"alert", 14<<3}, {"clock2", 15<<3}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1}, {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4}, {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7} }; static bool vlog_facility_exists(const char* facility, int *value); static void format_log_message(const struct vlog_module *, enum vlog_level, const char *pattern, const char *message, va_list, struct ds *) OVS_PRINTF_FORMAT(4, 0); /* Searches the 'n_names' in 'names'. Returns the index of a match for * 'target', or 'n_names' if no name matches. */ static size_t search_name_array(const char *target, const char *const *names, size_t n_names) { size_t i; for (i = 0; i < n_names; i++) { assert(names[i]); if (!strcasecmp(names[i], target)) { break; } } return i; } /* Returns the name for logging level 'level'. */ const char * vlog_get_level_name(enum vlog_level level) { assert(level < VLL_N_LEVELS); return level_names[level]; } /* Returns the logging level with the given 'name', or VLL_N_LEVELS if 'name' * is not the name of a logging level. */ enum vlog_level vlog_get_level_val(const char *name) { return search_name_array(name, level_names, ARRAY_SIZE(level_names)); } /* Returns the name for logging destination 'destination'. */ const char * vlog_get_destination_name(enum vlog_destination destination) { assert(destination < VLF_N_DESTINATIONS); return destinations[destination].name; } /* Returns the logging destination named 'name', or VLF_N_DESTINATIONS if * 'name' is not the name of a logging destination. */ enum vlog_destination vlog_get_destination_val(const char *name) { size_t i; for (i = 0; i < VLF_N_DESTINATIONS; i++) { if (!strcasecmp(destinations[i].name, name)) { break; } } return i; } void vlog_insert_module(struct ovs_list *vlog) { list_insert(&vlog_modules, vlog); } /* Returns the name for logging module 'module'. */ const char * vlog_get_module_name(const struct vlog_module *module) { return module->name; } /* Returns the logging module named 'name', or NULL if 'name' is not the name * of a logging module. */ struct vlog_module * vlog_module_from_name(const char *name) { struct vlog_module *mp; LIST_FOR_EACH (mp, list, &vlog_modules) { if (!strcasecmp(name, mp->name)) { return mp; } } return NULL; } /* Returns the current logging level for the given 'module' and * 'destination'. */ enum vlog_level vlog_get_level(const struct vlog_module *module, enum vlog_destination destination) { assert(destination < VLF_N_DESTINATIONS); return module->levels[destination]; } static void update_min_level(struct vlog_module *module) OVS_REQUIRES(&log_file_mutex) { enum vlog_destination destination; module->min_level = VLL_OFF; for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) { if (log_fd >= 0 || destination != VLF_FILE) { enum vlog_level level = module->levels[destination]; if (level > module->min_level) { module->min_level = level; } } } } static void set_destination_level(enum vlog_destination destination, struct vlog_module *module, enum vlog_level level) { assert(destination >= 0 && destination < VLF_N_DESTINATIONS); assert(level < VLL_N_LEVELS); ovs_mutex_lock(&log_file_mutex); if (!module) { struct vlog_module *mp; LIST_FOR_EACH (mp, list, &vlog_modules) { mp->levels[destination] = level; update_min_level(mp); } } else { module->levels[destination] = level; update_min_level(module); } ovs_mutex_unlock(&log_file_mutex); } /* Sets the logging level for the given 'module' and 'destination' to 'level'. * A null 'module' or a 'destination' of VLF_ANY_DESTINATION is treated as a * wildcard across all modules or destinations, respectively. */ void vlog_set_levels(struct vlog_module *module, enum vlog_destination destination, enum vlog_level level) { assert(destination < VLF_N_DESTINATIONS || destination == VLF_ANY_DESTINATION); if (destination == VLF_ANY_DESTINATION) { for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) { set_destination_level(destination, module, level); } } else { set_destination_level(destination, module, level); } } static void do_set_pattern(enum vlog_destination destination, const char *pattern) { struct destination *f = &destinations[destination]; ovs_rwlock_wrlock(&pattern_rwlock); if (!f->default_pattern) { free(f->pattern); } else { f->default_pattern = false; } f->pattern = xstrdup(pattern); ovs_rwlock_unlock(&pattern_rwlock); } /* Sets the pattern for the given 'destination' to 'pattern'. */ void vlog_set_pattern(enum vlog_destination destination, const char *pattern) { assert(destination < VLF_N_DESTINATIONS || destination == VLF_ANY_DESTINATION); if (destination == VLF_ANY_DESTINATION) { for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) { do_set_pattern(destination, pattern); } } else { do_set_pattern(destination, pattern); } } /* Sets the name of the log file used by VLF_FILE to 'file_name', or to the * default file name if 'file_name' is null. Returns 0 if successful, * otherwise a positive errno value. */ int vlog_set_log_file(const char *file_name) { char *new_log_file_name; struct vlog_module *mp; struct stat old_stat; struct stat new_stat; int new_log_fd; bool same_file; bool log_close; /* Open new log file. */ new_log_file_name = (file_name ? xstrdup(file_name) : xasprintf("%s/%s.log", ovs_logdir(), program_name)); new_log_fd = open(new_log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0666); if (new_log_fd < 0) { VLOG_WARN("failed to open %s for logging: %s", new_log_file_name, ovs_strerror(errno)); free(new_log_file_name); return errno; } /* If the new log file is the same one we already have open, bail out. */ ovs_mutex_lock(&log_file_mutex); same_file = (log_fd >= 0 && new_log_fd >= 0 && !fstat(log_fd, &old_stat) && !fstat(new_log_fd, &new_stat) && old_stat.st_dev == new_stat.st_dev && old_stat.st_ino == new_stat.st_ino); ovs_mutex_unlock(&log_file_mutex); if (same_file) { close(new_log_fd); free(new_log_file_name); return 0; } /* Log closing old log file (we can't log while holding log_file_mutex). */ ovs_mutex_lock(&log_file_mutex); log_close = log_fd >= 0; ovs_mutex_unlock(&log_file_mutex); if (log_close) { VLOG_INFO("closing log file"); } /* Close old log file, if any, and install new one. */ ovs_mutex_lock(&log_file_mutex); if (log_fd >= 0) { free(log_file_name); close(log_fd); async_append_destroy(log_writer); } log_file_name = xstrdup(new_log_file_name); log_fd = new_log_fd; if (log_async) { log_writer = async_append_create(new_log_fd); } LIST_FOR_EACH (mp, list, &vlog_modules) { update_min_level(mp); } ovs_mutex_unlock(&log_file_mutex); /* Log opening new log file (we can't log while holding log_file_mutex). */ VLOG_INFO("opened log file %s", new_log_file_name); free(new_log_file_name); return 0; } /* Closes and then attempts to re-open the current log file. (This is useful * just after log rotation, to ensure that the new log file starts being used.) * Returns 0 if successful, otherwise a positive errno value. */ int vlog_reopen_log_file(void) { char *fn; ovs_mutex_lock(&log_file_mutex); fn = log_file_name ? xstrdup(log_file_name) : NULL; ovs_mutex_unlock(&log_file_mutex); if (fn) { int error = vlog_set_log_file(fn); free(fn); return error; } else { return 0; } } #ifndef _WIN32 /* In case a log file exists, change its owner to new 'user' and 'group'. * * This is useful for handling cases where the --log-file option is * specified ahead of the --user option. */ void vlog_change_owner_unix(uid_t user, gid_t group) { struct ds err = DS_EMPTY_INITIALIZER; int error; ovs_mutex_lock(&log_file_mutex); error = log_file_name ? chown(log_file_name, user, group) : 0; if (error) { /* Build the error message. We can not call VLOG_FATAL directly * here because VLOG_FATAL() will try again to to acquire * 'log_file_mutex' lock, causing deadlock. */ ds_put_format(&err, "Failed to change %s ownership: %s.", log_file_name, ovs_strerror(errno)); } ovs_mutex_unlock(&log_file_mutex); if (error) { VLOG_FATAL("%s", ds_steal_cstr(&err)); } } #endif /* Set debugging levels. Returns null if successful, otherwise an error * message that the caller must free(). */ char * vlog_set_levels_from_string(const char *s_) { char *s = xstrdup(s_); char *save_ptr = NULL; char *msg = NULL; char *word; word = strtok_r(s, " ,:\t", &save_ptr); if (word && !strcasecmp(word, "PATTERN")) { enum vlog_destination destination; word = strtok_r(NULL, " ,:\t", &save_ptr); if (!word) { msg = xstrdup("missing destination"); goto exit; } destination = (!strcasecmp(word, "ANY") ? VLF_ANY_DESTINATION : vlog_get_destination_val(word)); if (destination == VLF_N_DESTINATIONS) { msg = xasprintf("unknown destination \"%s\"", word); goto exit; } vlog_set_pattern(destination, save_ptr); } else if (word && !strcasecmp(word, "FACILITY")) { int value; if (!vlog_facility_exists(save_ptr, &value)) { msg = xstrdup("invalid facility"); goto exit; } atomic_store_explicit(&log_facility, value, memory_order_relaxed); } else { struct vlog_module *module = NULL; enum vlog_level level = VLL_N_LEVELS; enum vlog_destination destination = VLF_N_DESTINATIONS; for (; word != NULL; word = strtok_r(NULL, " ,:\t", &save_ptr)) { if (!strcasecmp(word, "ANY")) { continue; } else if (vlog_get_destination_val(word) != VLF_N_DESTINATIONS) { if (destination != VLF_N_DESTINATIONS) { msg = xstrdup("cannot specify multiple destinations"); goto exit; } destination = vlog_get_destination_val(word); } else if (vlog_get_level_val(word) != VLL_N_LEVELS) { if (level != VLL_N_LEVELS) { msg = xstrdup("cannot specify multiple levels"); goto exit; } level = vlog_get_level_val(word); } else if (vlog_module_from_name(word)) { if (module) { msg = xstrdup("cannot specify multiple modules"); goto exit; } module = vlog_module_from_name(word); } else { msg = xasprintf("no destination, level, or module \"%s\"", word); goto exit; } } if (destination == VLF_N_DESTINATIONS) { destination = VLF_ANY_DESTINATION; } if (level == VLL_N_LEVELS) { level = VLL_DBG; } vlog_set_levels(module, destination, level); } exit: free(s); return msg; } /* Set debugging levels. Abort with an error message if 's' is invalid. */ void vlog_set_levels_from_string_assert(const char *s) { char *error = vlog_set_levels_from_string(s); if (error) { ovs_fatal(0, "%s", error); } } /* If 'arg' is null, configure maximum verbosity. Otherwise, sets * configuration according to 'arg' (see vlog_set_levels_from_string()). */ void vlog_set_verbosity(const char *arg) { if (arg) { char *msg = vlog_set_levels_from_string(arg); if (msg) { ovs_fatal(0, "processing \"%s\": %s", arg, msg); } } else { vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG); } } void vlog_set_syslog_method(const char *method) { if (syslogger) { /* Set syslogger only, if one is not already set. This effectively * means that only the first --syslog-method argument is honored. */ return; } if (!strcmp(method, "libc")) { syslogger = syslog_libc_create(); } else if (!strncmp(method, "udp:", 4) || !strncmp(method, "unix:", 5)) { syslogger = syslog_direct_create(method); } else { ovs_fatal(0, "unsupported syslog method '%s'", method); } } /* Set the vlog udp syslog target. */ void vlog_set_syslog_target(const char *target) { int new_fd; inet_open_active(SOCK_DGRAM, target, 0, NULL, &new_fd, 0); ovs_rwlock_wrlock(&pattern_rwlock); if (syslog_fd >= 0) { close(syslog_fd); } syslog_fd = new_fd; ovs_rwlock_unlock(&pattern_rwlock); } /* Returns 'false' if 'facility' is not a valid string. If 'facility' * is a valid string, sets 'value' with the integer value of 'facility' * and returns 'true'. */ static bool vlog_facility_exists(const char* facility, int *value) { size_t i; for (i = 0; i < ARRAY_SIZE(vlog_facilities); i++) { if (!strcasecmp(vlog_facilities[i].name, facility)) { *value = vlog_facilities[i].value; return true; } } return false; } static void vlog_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { int i; for (i = 1; i < argc; i++) { char *msg = vlog_set_levels_from_string(argv[i]); if (msg) { unixctl_command_reply_error(conn, msg); free(msg); return; } } unixctl_command_reply(conn, NULL); } static void vlog_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { char *msg = vlog_get_levels(); unixctl_command_reply(conn, msg); free(msg); } static void vlog_unixctl_list_pattern(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { char *msg; msg = vlog_get_patterns(); unixctl_command_reply(conn, msg); free(msg); } static void vlog_unixctl_reopen(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { bool has_log_file; ovs_mutex_lock(&log_file_mutex); has_log_file = log_file_name != NULL; ovs_mutex_unlock(&log_file_mutex); if (has_log_file) { int error = vlog_reopen_log_file(); if (error) { unixctl_command_reply_error(conn, ovs_strerror(errno)); } else { unixctl_command_reply(conn, NULL); } } else { unixctl_command_reply_error(conn, "Logging to file not configured"); } } static void set_all_rate_limits(bool enable) { struct vlog_module *mp; LIST_FOR_EACH (mp, list, &vlog_modules) { mp->honor_rate_limits = enable; } } static void set_rate_limits(struct unixctl_conn *conn, int argc, const char *argv[], bool enable) { if (argc > 1) { int i; for (i = 1; i < argc; i++) { if (!strcasecmp(argv[i], "ANY")) { set_all_rate_limits(enable); } else { struct vlog_module *module = vlog_module_from_name(argv[i]); if (!module) { unixctl_command_reply_error(conn, "unknown module"); return; } module->honor_rate_limits = enable; } } } else { set_all_rate_limits(enable); } unixctl_command_reply(conn, NULL); } static void vlog_enable_rate_limit(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { set_rate_limits(conn, argc, argv, true); } static void vlog_disable_rate_limit(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { set_rate_limits(conn, argc, argv, false); } /* Initializes the logging subsystem and registers its unixctl server * commands. */ void vlog_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { long long int now; int facility; bool print_syslog_target_deprecation; /* Do initialization work that needs to be done before any logging * occurs. We want to keep this really minimal because any attempt to * log anything before calling ovsthread_once_done() will deadlock. */ atomic_read_explicit(&log_facility, &facility, memory_order_relaxed); if (!syslogger) { syslogger = syslog_libc_create(); } syslogger->class->openlog(syslogger, facility ? facility : LOG_DAEMON); ovsthread_once_done(&once); /* Now do anything that we want to happen only once but doesn't have to * finish before we start logging. */ now = time_wall_msec(); if (now < 0) { char *s = xastrftime_msec("%a, %d %b %Y %H:%M:%S", now, true); VLOG_ERR("current time is negative: %s (%lld)", s, now); free(s); } unixctl_command_register( "vlog/set", "{spec | PATTERN:destination:pattern}", 1, INT_MAX, vlog_unixctl_set, NULL); unixctl_command_register("vlog/list", "", 0, 0, vlog_unixctl_list, NULL); unixctl_command_register("vlog/list-pattern", "", 0, 0, vlog_unixctl_list_pattern, NULL); unixctl_command_register("vlog/enable-rate-limit", "[module]...", 0, INT_MAX, vlog_enable_rate_limit, NULL); unixctl_command_register("vlog/disable-rate-limit", "[module]...", 0, INT_MAX, vlog_disable_rate_limit, NULL); unixctl_command_register("vlog/reopen", "", 0, 0, vlog_unixctl_reopen, NULL); ovs_rwlock_rdlock(&pattern_rwlock); print_syslog_target_deprecation = syslog_fd >= 0; ovs_rwlock_unlock(&pattern_rwlock); if (print_syslog_target_deprecation) { VLOG_WARN("--syslog-target flag is deprecated, use " "--syslog-method instead"); } } } /* Enables VLF_FILE log output to be written asynchronously to disk. * Asynchronous file writes avoid blocking the process in the case of a busy * disk, but on the other hand they are less robust: there is a chance that the * write will not make it to the log file if the process crashes soon after the * log call. */ void vlog_enable_async(void) { ovs_mutex_lock(&log_file_mutex); log_async = true; if (log_fd >= 0 && !log_writer) { log_writer = async_append_create(log_fd); } ovs_mutex_unlock(&log_file_mutex); } /* Print the current logging level for each module. */ char * vlog_get_levels(void) { struct ds s = DS_EMPTY_INITIALIZER; struct vlog_module *mp; struct svec lines = SVEC_EMPTY_INITIALIZER; char *line; size_t i; ds_put_format(&s, " console syslog file\n"); ds_put_format(&s, " ------- ------ ------\n"); LIST_FOR_EACH (mp, list, &vlog_modules) { struct ds line; ds_init(&line); ds_put_format(&line, "%-16s %4s %4s %4s", vlog_get_module_name(mp), vlog_get_level_name(vlog_get_level(mp, VLF_CONSOLE)), vlog_get_level_name(vlog_get_level(mp, VLF_SYSLOG)), vlog_get_level_name(vlog_get_level(mp, VLF_FILE))); if (!mp->honor_rate_limits) { ds_put_cstr(&line, " (rate limiting disabled)"); } ds_put_char(&line, '\n'); svec_add_nocopy(&lines, ds_steal_cstr(&line)); } svec_sort(&lines); SVEC_FOR_EACH (i, line, &lines) { ds_put_cstr(&s, line); } svec_destroy(&lines); return ds_cstr(&s); } /* Returns as a string current logging patterns for each destination. * This string must be released by caller. */ char * vlog_get_patterns(void) { struct ds ds = DS_EMPTY_INITIALIZER; enum vlog_destination destination; ovs_rwlock_rdlock(&pattern_rwlock); ds_put_format(&ds, " prefix format\n"); ds_put_format(&ds, " ------ ------\n"); for (destination = 0; destination < VLF_N_DESTINATIONS; destination++) { struct destination *f = &destinations[destination]; const char *prefix = "none"; if (destination == VLF_SYSLOG && syslogger) { prefix = syslog_get_prefix(syslogger); } ds_put_format(&ds, "%-7s %-32s %s\n", f->name, prefix, f->pattern); } ovs_rwlock_unlock(&pattern_rwlock); return ds_cstr(&ds); } /* Returns true if a log message emitted for the given 'module' and 'level' * would cause some log output, false if that module and level are completely * disabled. */ bool vlog_is_enabled(const struct vlog_module *module, enum vlog_level level) { return module->min_level >= level; } static const char * fetch_braces(const char *p, const char *def, char *out, size_t out_size) { if (*p == '{') { size_t n = strcspn(p + 1, "}"); size_t n_copy = MIN(n, out_size - 1); memcpy(out, p + 1, n_copy); out[n_copy] = '\0'; p += n + 2; } else { ovs_strlcpy(out, def, out_size); } return p; } static void format_log_message(const struct vlog_module *module, enum vlog_level level, const char *pattern, const char *message, va_list args_, struct ds *s) { char tmp[128]; va_list args; const char *p; int facility; ds_clear(s); for (p = pattern; *p != '\0'; ) { const char *subprogram_name; enum { LEFT, RIGHT } justify = RIGHT; int pad = '0'; size_t length, field, used; if (*p != '%') { ds_put_char(s, *p++); continue; } p++; if (*p == '-') { justify = LEFT; p++; } if (*p == '0') { pad = '0'; p++; } field = 0; while (isdigit((unsigned char)*p)) { field = (field * 10) + (*p - '0'); p++; } length = s->length; switch (*p++) { case 'A': ds_put_cstr(s, program_name); break; case 'B': atomic_read_explicit(&log_facility, &facility, memory_order_relaxed); facility = facility ? facility : LOG_LOCAL0; ds_put_format(s, "%d", facility + syslog_levels[level]); break; case 'c': p = fetch_braces(p, "", tmp, sizeof tmp); ds_put_cstr(s, vlog_get_module_name(module)); break; case 'd': p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp); ds_put_strftime_msec(s, tmp, time_wall_msec(), false); break; case 'D': p = fetch_braces(p, "%Y-%m-%d %H:%M:%S.###", tmp, sizeof tmp); ds_put_strftime_msec(s, tmp, time_wall_msec(), true); break; case 'E': gethostname(tmp, sizeof tmp); tmp[sizeof tmp - 1] = '\0'; ds_put_cstr(s, tmp); break; case 'm': /* Format user-supplied log message and trim trailing new-lines. */ length = s->length; va_copy(args, args_); ds_put_format_valist(s, message, args); va_end(args); while (s->length > length && s->string[s->length - 1] == '\n') { s->length--; } break; case 'N': ds_put_format(s, "%u", *msg_num_get_unsafe()); break; case 'n': ds_put_char(s, '\n'); break; case 'p': ds_put_cstr(s, vlog_get_level_name(level)); break; case 'P': ds_put_format(s, "%ld", (long int) getpid()); break; case 'r': ds_put_format(s, "%lld", time_msec() - time_boot_msec()); break; case 't': subprogram_name = get_subprogram_name(); ds_put_cstr(s, subprogram_name[0] ? subprogram_name : "main"); break; case 'T': subprogram_name = get_subprogram_name(); if (subprogram_name[0]) { ds_put_format(s, "(%s)", subprogram_name); } break; default: ds_put_char(s, p[-1]); break; } used = s->length - length; if (used < field) { size_t n_pad = field - used; if (justify == RIGHT) { ds_put_uninit(s, n_pad); memmove(&s->string[length + n_pad], &s->string[length], used); memset(&s->string[length], pad, n_pad); } else { ds_put_char_multiple(s, pad, n_pad); } } } } /* Exports the given 'syslog_message' to the configured udp syslog sink. */ static void send_to_syslog_fd(const char *s, size_t length) OVS_REQ_RDLOCK(pattern_rwlock) { static size_t max_length = SIZE_MAX; size_t send_len = MIN(length, max_length); while (write(syslog_fd, s, send_len) < 0 && errno == EMSGSIZE) { send_len -= send_len / 20; max_length = send_len; } } /* Writes 'message' to the log at the given 'level' and as coming from the * given 'module'. * * Guaranteed to preserve errno. */ void vlog_valist(const struct vlog_module *module, enum vlog_level level, const char *message, va_list args) { bool log_to_console = module->levels[VLF_CONSOLE] >= level; bool log_to_syslog = module->levels[VLF_SYSLOG] >= level; bool log_to_file; ovs_mutex_lock(&log_file_mutex); log_to_file = module->levels[VLF_FILE] >= level && log_fd >= 0; ovs_mutex_unlock(&log_file_mutex); if (log_to_console || log_to_syslog || log_to_file) { int save_errno = errno; struct ds s; vlog_init(); ds_init(&s); ds_reserve(&s, 1024); ++*msg_num_get(); ovs_rwlock_rdlock(&pattern_rwlock); if (log_to_console) { format_log_message(module, level, destinations[VLF_CONSOLE].pattern, message, args, &s); ds_put_char(&s, '\n'); fputs(ds_cstr(&s), stderr); } if (log_to_syslog) { int syslog_level = syslog_levels[level]; char *save_ptr = NULL; char *line; int facility; format_log_message(module, level, destinations[VLF_SYSLOG].pattern, message, args, &s); for (line = strtok_r(s.string, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr)) { atomic_read_explicit(&log_facility, &facility, memory_order_relaxed); syslogger->class->syslog(syslogger, syslog_level|facility, line); } if (syslog_fd >= 0) { format_log_message(module, level, "<%B>1 %D{%Y-%m-%dT%H:%M:%S.###Z} " "%E %A %P %c - \xef\xbb\xbf%m", message, args, &s); send_to_syslog_fd(ds_cstr(&s), s.length); } } if (log_to_file) { format_log_message(module, level, destinations[VLF_FILE].pattern, message, args, &s); ds_put_char(&s, '\n'); ovs_mutex_lock(&log_file_mutex); if (log_fd >= 0) { if (log_writer) { async_append_write(log_writer, s.string, s.length); if (level == VLL_EMER) { async_append_flush(log_writer); } } else { ignore(write(log_fd, s.string, s.length)); } } ovs_mutex_unlock(&log_file_mutex); } ovs_rwlock_unlock(&pattern_rwlock); ds_destroy(&s); errno = save_errno; } } void vlog(const struct vlog_module *module, enum vlog_level level, const char *message, ...) { va_list args; va_start(args, message); vlog_valist(module, level, message, args); va_end(args); } /* Logs 'message' to 'module' at maximum verbosity, then exits with a failure * exit code. Always writes the message to stderr, even if the console * destination is disabled. * * Choose this function instead of vlog_abort_valist() if the daemon monitoring * facility shouldn't automatically restart the current daemon. */ void vlog_fatal_valist(const struct vlog_module *module_, const char *message, va_list args) { struct vlog_module *module = CONST_CAST(struct vlog_module *, module_); /* Don't log this message to the console to avoid redundancy with the * message written by the later ovs_fatal_valist(). */ module->levels[VLF_CONSOLE] = VLL_OFF; vlog_valist(module, VLL_EMER, message, args); ovs_fatal_valist(0, message, args); } /* Logs 'message' to 'module' at maximum verbosity, then exits with a failure * exit code. Always writes the message to stderr, even if the console * destination is disabled. * * Choose this function instead of vlog_abort() if the daemon monitoring * facility shouldn't automatically restart the current daemon. */ void vlog_fatal(const struct vlog_module *module, const char *message, ...) { va_list args; va_start(args, message); vlog_fatal_valist(module, message, args); va_end(args); } /* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always * writes the message to stderr, even if the console destination is disabled. * * Choose this function instead of vlog_fatal_valist() if the daemon monitoring * facility should automatically restart the current daemon. */ void vlog_abort_valist(const struct vlog_module *module_, const char *message, va_list args) { struct vlog_module *module = (struct vlog_module *) module_; /* Don't log this message to the console to avoid redundancy with the * message written by the later ovs_abort_valist(). */ module->levels[VLF_CONSOLE] = VLL_OFF; vlog_valist(module, VLL_EMER, message, args); ovs_abort_valist(0, message, args); } /* Logs 'message' to 'module' at maximum verbosity, then calls abort(). Always * writes the message to stderr, even if the console destination is disabled. * * Choose this function instead of vlog_fatal() if the daemon monitoring * facility should automatically restart the current daemon. */ void vlog_abort(const struct vlog_module *module, const char *message, ...) { va_list args; va_start(args, message); vlog_abort_valist(module, message, args); va_end(args); } bool vlog_should_drop(const struct vlog_module *module, enum vlog_level level, struct vlog_rate_limit *rl) { if (!module->honor_rate_limits) { return false; } if (!vlog_is_enabled(module, level)) { return true; } ovs_mutex_lock(&rl->mutex); if (!token_bucket_withdraw(&rl->token_bucket, VLOG_MSG_TOKENS)) { time_t now = time_now(); if (!rl->n_dropped) { rl->first_dropped = now; } rl->last_dropped = now; rl->n_dropped++; ovs_mutex_unlock(&rl->mutex); return true; } if (!rl->n_dropped) { ovs_mutex_unlock(&rl->mutex); } else { time_t now = time_now(); unsigned int n_dropped = rl->n_dropped; unsigned int first_dropped_elapsed = now - rl->first_dropped; unsigned int last_dropped_elapsed = now - rl->last_dropped; rl->n_dropped = 0; ovs_mutex_unlock(&rl->mutex); vlog(module, level, "Dropped %u log messages in last %u seconds (most recently, " "%u seconds ago) due to excessive rate", n_dropped, first_dropped_elapsed, last_dropped_elapsed); } return false; } void vlog_rate_limit(const struct vlog_module *module, enum vlog_level level, struct vlog_rate_limit *rl, const char *message, ...) { if (!vlog_should_drop(module, level, rl)) { va_list args; va_start(args, message); vlog_valist(module, level, message, args); va_end(args); } } void vlog_usage(void) { printf("\n\ Logging options:\n\ -vSPEC, --verbose=SPEC set logging levels\n\ -v, --verbose set maximum verbosity level\n\ --log-file[=FILE] enable logging to specified FILE\n\ (default: %s/%s.log)\n\ --syslog-method=(libc|unix:file|udp:ip:port)\n\ specify how to send messages to syslog daemon\n\ --syslog-target=HOST:PORT also send syslog msgs to HOST:PORT via UDP\n", ovs_logdir(), program_name); } openvswitch-2.5.9/lib/PaxHeaders.82075/stp.h0000644000000000000000000000013213534540071015371 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.889854223 openvswitch-2.5.9/lib/stp.h0000644000175000017500000001447013534540071017065 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef STP_H #define STP_H 1 /* This is an implementation of Spanning Tree Protocol as described in IEEE * 802.1D-1998, clauses 8 and 9. Section numbers refer to this standard. */ #include #include #include "compiler.h" #include "util.h" struct dp_packet; /* Bridge and port priorities that should be used by default. */ #define STP_DEFAULT_BRIDGE_PRIORITY 32768 #define STP_DEFAULT_PORT_PRIORITY 128 /* Default time values. */ #define STP_DEFAULT_MAX_AGE 20000 #define STP_DEFAULT_HELLO_TIME 2000 #define STP_DEFAULT_FWD_DELAY 15000 /* Bridge identifier. Top 16 bits are a priority value (numerically lower * values are higher priorities). Bottom 48 bits are MAC address of bridge. */ typedef uint64_t stp_identifier; #define STP_ID_FMT "%04"PRIx16".%012"PRIx64 #define STP_ID_ARGS(stp_id) \ (uint16_t)((stp_id) >> 48), \ (uint64_t)((stp_id) & 0xffffffffffffULL) #define STP_PORT_ID_FMT "%04"PRIx16 /* Basic STP functionality. */ #define STP_MAX_PORTS 255 void stp_init(void); struct stp *stp_create(const char *name, stp_identifier bridge_id, void (*send_bpdu)(struct dp_packet *bpdu, int port_no, void *aux), void *aux); struct stp *stp_ref(const struct stp *); void stp_unref(struct stp *); void stp_tick(struct stp *, int ms); void stp_set_bridge_id(struct stp *, stp_identifier bridge_id); void stp_set_bridge_priority(struct stp *, uint16_t new_priority); void stp_set_hello_time(struct stp *, int ms); void stp_set_max_age(struct stp *, int ms); void stp_set_forward_delay(struct stp *, int ms); /* STP properties. */ const char *stp_get_name(const struct stp *); stp_identifier stp_get_bridge_id(const struct stp *); stp_identifier stp_get_designated_root(const struct stp *); bool stp_is_root_bridge(const struct stp *); int stp_get_root_path_cost(const struct stp *); int stp_get_hello_time(const struct stp *); int stp_get_max_age(const struct stp *); int stp_get_forward_delay(const struct stp *); bool stp_check_and_reset_fdb_flush(struct stp *); /* Obtaining STP ports. */ struct stp_port *stp_get_port(struct stp *, int port_no); struct stp_port *stp_get_root_port(struct stp *); bool stp_get_changed_port(struct stp *, struct stp_port **portp); /* State of an STP port. * * A port is in exactly one state at any given time, but distinct bits are used * for states to allow testing for more than one state with a bit mask. * * The STP_DISABLED state means that the port is disabled by management. * In our implementation, this state means that the port does not * participate in the spanning tree, but it still forwards traffic as if * it were in the STP_FORWARDING state. This may be different from * other implementations. * * The following diagram describes the various states and what they are * allowed to do in OVS: * * FWD LRN TX_BPDU RX_BPDU FWD_BPDU * --- --- ------- ------- -------- * Disabled Y - - - Y * Blocking - - - Y - * Listening - - Y Y Y * Learning - Y Y Y Y * Forwarding Y Y Y Y Y * * * FWD: the port should forward any incoming non-stp-BPDU * packets. * * LRN: the port should conduct MAC learning on packets received. * * TX_BPDU/RX_BPDU: the port could generate/consume bpdus. * * FWD_BPDU: the port should should always forward the BPDUS, * whether they are generated by the port or received * as incoming packets. * * Once again, note that the disabled state forwards traffic, which is * likely different than the spec would indicate. */ enum stp_state { STP_DISABLED = 1 << 0, /* 8.4.5: See note above. */ STP_LISTENING = 1 << 1, /* 8.4.2: Not learning or relaying frames. */ STP_LEARNING = 1 << 2, /* 8.4.3: Learning but not relaying frames. */ STP_FORWARDING = 1 << 3, /* 8.4.4: Learning and relaying frames. */ STP_BLOCKING = 1 << 4 /* 8.4.1: Initial boot state. */ }; const char *stp_state_name(enum stp_state); bool stp_forward_in_state(enum stp_state); bool stp_learn_in_state(enum stp_state); bool stp_should_forward_bpdu(enum stp_state); /* Role of an STP port. */ enum stp_role { STP_ROLE_ROOT, /* Path to root bridge. */ STP_ROLE_DESIGNATED, /* Path to LAN segments. */ STP_ROLE_ALTERNATE, /* Backup path to root bridge. */ STP_ROLE_DISABLED /* Port does not participate in STP. */ }; const char *stp_role_name(enum stp_role); void stp_received_bpdu(struct stp_port *, const void *bpdu, size_t bpdu_size); struct stp *stp_port_get_stp(struct stp_port *); void stp_port_set_name(struct stp_port *, const char *); void stp_port_set_aux(struct stp_port *, void *); void *stp_port_get_aux(struct stp_port *); int stp_port_no(const struct stp_port *); int stp_port_get_id(const struct stp_port *); enum stp_state stp_port_get_state(const struct stp_port *); enum stp_role stp_port_get_role(const struct stp_port *); void stp_port_get_counts(const struct stp_port *, int *tx_count, int *rx_count, int *error_count); void stp_port_enable(struct stp_port *); void stp_port_disable(struct stp_port *); void stp_port_set_priority(struct stp_port *, uint8_t new_priority); uint16_t stp_convert_speed_to_cost(unsigned int speed); void stp_port_set_path_cost(struct stp_port *, uint16_t path_cost); void stp_port_set_speed(struct stp_port *, unsigned int speed); void stp_port_enable_change_detection(struct stp_port *); void stp_port_disable_change_detection(struct stp_port *); #endif /* stp.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/heap.h0000644000000000000000000000013213534540071015500 xustar0030 mtime=1567801401.393681111 30 atime=1567801402.077686135 30 ctime=1567801424.713852925 openvswitch-2.5.9/lib/heap.h0000644000175000017500000001246713534540071017200 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef HEAP_H #define HEAP_H 1 #include #include #include /* A heap node, to be embedded inside the data structure in the heap. */ struct heap_node { size_t idx; uint64_t priority; }; /* A max-heap. */ struct heap { struct heap_node **array; /* Data in elements 1...n, element 0 unused. */ size_t n; /* Number of nodes currently in the heap. */ size_t allocated; /* Max 'n' before 'array' must be enlarged. */ }; /* Initialization. */ void heap_init(struct heap *); void heap_destroy(struct heap *); void heap_clear(struct heap *); void heap_swap(struct heap *a, struct heap *b); static inline size_t heap_count(const struct heap *); static inline bool heap_is_empty(const struct heap *); /* Insertion and deletion. */ void heap_insert(struct heap *, struct heap_node *, uint64_t priority); void heap_change(struct heap *, struct heap_node *, uint64_t priority); void heap_remove(struct heap *, struct heap_node *); static inline struct heap_node *heap_pop(struct heap *); /* Maximum. */ static inline struct heap_node *heap_max(const struct heap *); /* The "raw" functions below do not preserve the heap invariants. After you * call them, heap_max() will not necessarily return the right value until you * subsequently call heap_rebuild(). */ void heap_raw_insert(struct heap *, struct heap_node *, uint64_t priority); static inline void heap_raw_change(struct heap_node *, uint64_t priority); void heap_raw_remove(struct heap *, struct heap_node *); void heap_rebuild(struct heap *); /* Iterates through each NODE in HEAP, where NODE->MEMBER must be a "struct * heap_node". Iterates in heap level order, which in particular means that * the first node visited is the maximum value in the heap. * * If a heap_raw_*() function has been called without a later call to * heap_rebuild(), then the first node visited may not be the maximum * element. */ #define HEAP_FOR_EACH(NODE, MEMBER, HEAP) \ for (((HEAP)->n > 0 \ ? INIT_CONTAINER(NODE, (HEAP)->array[1], MEMBER) \ : ((NODE) = NULL, (void) 0)); \ (NODE) != NULL; \ ((NODE)->MEMBER.idx < (HEAP)->n \ ? ASSIGN_CONTAINER(NODE, \ (HEAP)->array[(NODE)->MEMBER.idx + 1], \ MEMBER) \ : ((NODE) = NULL, (void) 0))) /* Returns the index of the node that is the parent of the node with the given * 'idx' within a heap. */ static inline size_t heap_parent__(size_t idx) { return idx / 2; } /* Returns the index of the node that is the left child of the node with the * given 'idx' within a heap. */ static inline size_t heap_left__(size_t idx) { return idx * 2; } /* Returns the index of the node that is the right child of the node with the * given 'idx' within a heap. */ static inline size_t heap_right__(size_t idx) { return idx * 2 + 1; } /* Returns true if 'idx' is the index of a leaf node in 'heap', false * otherwise. */ static inline bool heap_is_leaf__(const struct heap *heap, size_t idx) { return heap_left__(idx) > heap->n; } /* Returns the number of elements in 'heap'. */ static inline size_t heap_count(const struct heap *heap) { return heap->n; } /* Returns true if 'heap' is empty, false if it contains at least one * element. */ static inline bool heap_is_empty(const struct heap *heap) { return heap->n == 0; } /* Returns the largest element in 'heap'. * * The caller must ensure that 'heap' contains at least one element. * * The return value may be wrong (i.e. not the maximum element but some other * element) if a heap_raw_*() function has been called without a later call to * heap_rebuild(). */ static inline struct heap_node * heap_max(const struct heap *heap) { return heap->array[1]; } /* Removes an arbitrary node from 'heap', in O(1), maintaining the heap * invariant. Returns the node removed. * * The caller must ensure that 'heap' contains at least one element. */ static inline struct heap_node * heap_pop(struct heap *heap) { return heap->array[heap->n--]; } /* Changes the priority of 'node' (which must be in 'heap') to 'priority'. * * After this call, heap_max() will no longer necessarily return the maximum * value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in * heap level order, until the next call to heap_rebuild(heap). * * This takes time O(1). */ static inline void heap_raw_change(struct heap_node *node, uint64_t priority) { node->priority = priority; } #endif /* heap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/stream-unix.c0000644000000000000000000000013213534540071017032 xustar0030 mtime=1567801401.597682609 30 atime=1567801402.097686281 30 ctime=1567801424.969854812 openvswitch-2.5.9/lib/stream-unix.c0000644000175000017500000000713513534540071020526 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "stream.h" #include #include #include #include #include #include #include #include #include #include #include "packets.h" #include "poll-loop.h" #include "socket-util.h" #include "dirs.h" #include "util.h" #include "stream-provider.h" #include "stream-fd.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(stream_unix); /* Active UNIX socket. */ static int unix_open(const char *name, char *suffix, struct stream **streamp, uint8_t dscp OVS_UNUSED) { char *connect_path; int fd; connect_path = abs_file_name(ovs_rundir(), suffix); fd = make_unix_socket(SOCK_STREAM, true, NULL, connect_path); if (fd < 0) { VLOG_DBG("%s: connection failed (%s)", connect_path, ovs_strerror(-fd)); free(connect_path); return -fd; } free(connect_path); return new_fd_stream(name, fd, check_connection_completion(fd), AF_UNIX, streamp); } const struct stream_class unix_stream_class = { "unix", /* name */ false, /* needs_probes */ unix_open, /* open */ NULL, /* close */ NULL, /* connect */ NULL, /* recv */ NULL, /* send */ NULL, /* run */ NULL, /* run_wait */ NULL, /* wait */ }; /* Passive UNIX socket. */ static int punix_accept(int fd, const struct sockaddr_storage *ss, size_t ss_len, struct stream **streamp); static int punix_open(const char *name OVS_UNUSED, char *suffix, struct pstream **pstreamp, uint8_t dscp OVS_UNUSED) { char *bind_path; int fd, error; bind_path = abs_file_name(ovs_rundir(), suffix); fd = make_unix_socket(SOCK_STREAM, true, bind_path, NULL); if (fd < 0) { VLOG_ERR("%s: binding failed: %s", bind_path, ovs_strerror(errno)); free(bind_path); return errno; } if (listen(fd, 64) < 0) { error = errno; VLOG_ERR("%s: listen: %s", name, ovs_strerror(error)); close(fd); free(bind_path); return error; } return new_fd_pstream(name, fd, punix_accept, bind_path, pstreamp); } static int punix_accept(int fd, const struct sockaddr_storage *ss, size_t ss_len, struct stream **streamp) { const struct sockaddr_un *sun = (const struct sockaddr_un *) ss; int name_len = get_unix_name_len(ss_len); char name[128]; if (name_len > 0) { snprintf(name, sizeof name, "unix:%.*s", name_len, sun->sun_path); } else { strcpy(name, "unix"); } return new_fd_stream(name, fd, 0, AF_UNIX, streamp); } const struct pstream_class punix_pstream_class = { "punix", false, punix_open, NULL, NULL, NULL, }; openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-flag-gcc4.7+.h0000644000000000000000000000013213534540071020611 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.809853633 openvswitch-2.5.9/lib/ovs-atomic-flag-gcc4.7+.h0000644000175000017500000000276213534540071022306 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic_flag on Clang and on GCC 4.7 and later. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif /* atomic_flag */ typedef struct { unsigned char b; } atomic_flag; #define ATOMIC_FLAG_INIT { .b = false } static inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object, memory_order order) { return __atomic_test_and_set(&object->b, order); } static inline bool atomic_flag_test_and_set(volatile atomic_flag *object) { return atomic_flag_test_and_set_explicit(object, memory_order_seq_cst); } static inline void atomic_flag_clear_explicit(volatile atomic_flag *object, memory_order order) { __atomic_clear(object, order); } static inline void atomic_flag_clear(volatile atomic_flag *object) { atomic_flag_clear_explicit(object, memory_order_seq_cst); } openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-actions.h0000644000000000000000000000013213534540071017005 xustar0030 mtime=1567801401.513681992 30 atime=1567801402.085686193 30 ctime=1567801424.789853486 openvswitch-2.5.9/lib/ofp-actions.h0000644000175000017500000010715713534540071020506 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFP_ACTIONS_H #define OFP_ACTIONS_H 1 #include #include #include "meta-flow.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/openflow.h" #include "openflow/nicira-ext.h" #include "openvswitch/types.h" /* List of OVS abstracted actions. * * This macro is used directly only internally by this header, but the list is * still of interest to developers. * * Each OFPACT invocation has the following parameters: * * 1. , used below in the enum definition of OFPACT_, and * elsewhere. * * 2. corresponding to a structure "struct ", that must be * defined below. This structure must be an abstract definition of the * action. Its first member must have type "struct ofpact" and name * "ofpact". It may be fixed length or end with a flexible array member * (e.g. "int member[];"). * * 3. , which has one of two possible values: * * - If "struct " is fixed-length, it must be "ofpact". * * - If "struct " is variable-length, it must be the name of the * flexible array member. * * 4. , a quoted string that gives the name of the action, for use in * parsing actions from text. */ #define OFPACTS \ /* Output. */ \ OFPACT(OUTPUT, ofpact_output, ofpact, "output") \ OFPACT(GROUP, ofpact_group, ofpact, "group") \ OFPACT(CONTROLLER, ofpact_controller, ofpact, "controller") \ OFPACT(ENQUEUE, ofpact_enqueue, ofpact, "enqueue") \ OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact, "output_reg") \ OFPACT(BUNDLE, ofpact_bundle, slaves, "bundle") \ \ /* Header changes. */ \ OFPACT(SET_FIELD, ofpact_set_field, ofpact, "set_field") \ OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact, "set_vlan_vid") \ OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact, "set_vlan_pcp") \ OFPACT(STRIP_VLAN, ofpact_null, ofpact, "strip_vlan") \ OFPACT(PUSH_VLAN, ofpact_null, ofpact, "push_vlan") \ OFPACT(SET_ETH_SRC, ofpact_mac, ofpact, "mod_dl_src") \ OFPACT(SET_ETH_DST, ofpact_mac, ofpact, "mod_dl_dst") \ OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact, "mod_nw_src") \ OFPACT(SET_IPV4_DST, ofpact_ipv4, ofpact, "mod_nw_dst") \ OFPACT(SET_IP_DSCP, ofpact_dscp, ofpact, "mod_nw_tos") \ OFPACT(SET_IP_ECN, ofpact_ecn, ofpact, "mod_nw_ecn") \ OFPACT(SET_IP_TTL, ofpact_ip_ttl, ofpact, "mod_nw_ttl") \ OFPACT(SET_L4_SRC_PORT, ofpact_l4_port, ofpact, "mod_tp_src") \ OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact, "mod_tp_dst") \ OFPACT(REG_MOVE, ofpact_reg_move, ofpact, "move") \ OFPACT(STACK_PUSH, ofpact_stack, ofpact, "push") \ OFPACT(STACK_POP, ofpact_stack, ofpact, "pop") \ OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids, "dec_ttl") \ OFPACT(SET_MPLS_LABEL, ofpact_mpls_label, ofpact, "set_mpls_label") \ OFPACT(SET_MPLS_TC, ofpact_mpls_tc, ofpact, "set_mpls_tc") \ OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact, "set_mpls_ttl") \ OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact, "dec_mpls_ttl") \ OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \ OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \ \ /* Metadata. */ \ OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \ OFPACT(SET_QUEUE, ofpact_queue, ofpact, "set_queue") \ OFPACT(POP_QUEUE, ofpact_null, ofpact, "pop_queue") \ OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact, "fin_timeout") \ \ /* Flow table interaction. */ \ OFPACT(RESUBMIT, ofpact_resubmit, ofpact, "resubmit") \ OFPACT(LEARN, ofpact_learn, specs, "learn") \ OFPACT(CONJUNCTION, ofpact_conjunction, ofpact, "conjunction") \ \ /* Arithmetic. */ \ OFPACT(MULTIPATH, ofpact_multipath, ofpact, "multipath") \ \ /* Other. */ \ OFPACT(NOTE, ofpact_note, data, "note") \ OFPACT(EXIT, ofpact_null, ofpact, "exit") \ OFPACT(SAMPLE, ofpact_sample, ofpact, "sample") \ OFPACT(UNROLL_XLATE, ofpact_unroll_xlate, ofpact, "unroll_xlate") \ OFPACT(CT, ofpact_conntrack, ofpact, "ct") \ \ /* Debugging actions. \ * \ * These are intentionally undocumented, subject to change, and \ * only accepted if ovs-vswitchd is started with --enable-dummy. */ \ OFPACT(DEBUG_RECIRC, ofpact_null, ofpact, "debug_recirc") \ \ /* Instructions. */ \ OFPACT(METER, ofpact_meter, ofpact, "meter") \ OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact, "clear_actions") \ OFPACT(WRITE_ACTIONS, ofpact_nest, ofpact, "write_actions") \ OFPACT(WRITE_METADATA, ofpact_metadata, ofpact, "write_metadata") \ OFPACT(GOTO_TABLE, ofpact_goto_table, ofpact, "goto_table") /* enum ofpact_type, with a member OFPACT_ for each action. */ enum OVS_PACKED_ENUM ofpact_type { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) OFPACT_##ENUM, OFPACTS #undef OFPACT }; /* Define N_OFPACTS to the number of types of ofpacts. */ enum { #define OFPACT(ENUM, STRUCT, MEMBER, NAME) + 1 N_OFPACTS = OFPACTS #undef OFPACT }; /* Header for an action. * * Each action is a structure "struct ofpact_*" that begins with "struct * ofpact", usually followed by other data that describes the action. Actions * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. * * The 'raw' member is special: * * - Most "struct ofpact"s correspond to one particular kind of OpenFlow * action, at least in a given OpenFlow version. For example, * OFPACT_SET_VLAN_VID corresponds to OFPAT10_SET_VLAN_VID in OpenFlow * 1.0. * * For such actions, the 'raw' member is not meaningful and generally * should be zero. * * - A few "struct ofpact"s correspond to multiple OpenFlow actions. For * example, OFPACT_SET_TUNNEL can be NXAST_SET_TUNNEL or * NXAST_SET_TUNNEL64. In these cases, if the "struct ofpact" originated * from OpenFlow, then we want to make sure that, if it gets translated * back to OpenFlow later, it is translated back to the same action type. * (Otherwise, we'd violate the promise made in DESIGN, in the "Action * Reproduction" section.) * * For such actions, the 'raw' member should be the "enum ofp_raw_action" * originally extracted from the OpenFlow action. (If the action didn't * originate from OpenFlow, then setting 'raw' to zero should be fine: * code to translate the ofpact to OpenFlow must tolerate this case.) */ struct ofpact { /* We want the space advantage of an 8-bit type here on every * implementation, without giving up the advantage of having a useful type * on implementations that support packed enums. */ #ifdef HAVE_PACKED_ENUM enum ofpact_type type; /* OFPACT_*. */ #else uint8_t type; /* OFPACT_* */ #endif uint8_t raw; /* Original type when added, if any. */ uint16_t len; /* Length of the action, in bytes, including * struct ofpact, excluding padding. */ }; BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4); /* Alignment. */ #define OFPACT_ALIGNTO 8 #define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO) /* Returns the ofpact following 'ofpact'. */ static inline struct ofpact * ofpact_next(const struct ofpact *ofpact) { return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len)); } struct ofpact *ofpact_next_flattened(const struct ofpact *); static inline struct ofpact * ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len) { return (void *) ((uint8_t *) ofpacts + ofpacts_len); } /* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts * starting at OFPACTS. */ #define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN) \ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \ (POS) = ofpact_next(POS)) /* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts * starting at OFPACTS. * * For ofpacts that contain nested ofpacts, this visits each of the inner * ofpacts as well. */ #define OFPACT_FOR_EACH_FLATTENED(POS, OFPACTS, OFPACTS_LEN) \ for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \ (POS) = ofpact_next_flattened(POS)) /* Action structure for each OFPACT_*. */ /* OFPACT_STRIP_VLAN, OFPACT_POP_QUEUE, OFPACT_EXIT, OFPACT_CLEAR_ACTIONS. * * Used for OFPAT10_STRIP_VLAN, NXAST_POP_QUEUE, NXAST_EXIT, * OFPAT11_POP_VLAN, OFPIT11_CLEAR_ACTIONS. * * Action structure for actions that do not have any extra data beyond the * action type. */ struct ofpact_null { struct ofpact ofpact; }; /* OFPACT_OUTPUT. * * Used for OFPAT10_OUTPUT. */ struct ofpact_output { struct ofpact ofpact; ofp_port_t port; /* Output port. */ uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER. */ }; /* OFPACT_CONTROLLER. * * Used for NXAST_CONTROLLER. */ struct ofpact_controller { struct ofpact ofpact; uint16_t max_len; /* Maximum length to send to controller. */ uint16_t controller_id; /* Controller ID to send packet-in. */ enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */ }; /* OFPACT_ENQUEUE. * * Used for OFPAT10_ENQUEUE. */ struct ofpact_enqueue { struct ofpact ofpact; ofp_port_t port; uint32_t queue; }; /* OFPACT_OUTPUT_REG. * * Used for NXAST_OUTPUT_REG. */ struct ofpact_output_reg { struct ofpact ofpact; uint16_t max_len; struct mf_subfield src; }; /* Bundle slave choice algorithm to apply. * * In the descriptions below, 'slaves' is the list of possible slaves in the * order they appear in the OpenFlow action. */ enum nx_bd_algorithm { /* Chooses the first live slave listed in the bundle. * * O(n_slaves) performance. */ NX_BD_ALG_ACTIVE_BACKUP = 0, /* Highest Random Weight. * * for i in [0,n_slaves): * weights[i] = hash(flow, i) * slave = { slaves[i] such that weights[i] >= weights[j] for all j != i } * * Redistributes 1/n_slaves of traffic when a slave's liveness changes. * O(n_slaves) performance. * * Uses the 'fields' and 'basis' parameters. */ NX_BD_ALG_HRW = 1 }; /* OFPACT_BUNDLE. * * Used for NXAST_BUNDLE. */ struct ofpact_bundle { struct ofpact ofpact; /* Slave choice algorithm to apply to hash value. */ enum nx_bd_algorithm algorithm; /* What fields to hash and how. */ enum nx_hash_fields fields; uint16_t basis; /* Universal hash parameter. */ struct mf_subfield dst; /* Slaves for output. */ unsigned int n_slaves; ofp_port_t slaves[]; }; /* OFPACT_SET_VLAN_VID. * * We keep track if vlan was present at action validation time to avoid a * PUSH_VLAN when translating to OpenFlow 1.1+. * * We also keep the originating OFPUTIL action code in ofpact.compat. * * Used for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */ struct ofpact_vlan_vid { struct ofpact ofpact; uint16_t vlan_vid; /* VLAN VID in low 12 bits, 0 in other bits. */ bool push_vlan_if_needed; /* OF 1.0 semantics if true. */ bool flow_has_vlan; /* VLAN present at action validation time? */ }; /* OFPACT_SET_VLAN_PCP. * * We keep track if vlan was present at action validation time to avoid a * PUSH_VLAN when translating to OpenFlow 1.1+. * * We also keep the originating OFPUTIL action code in ofpact.compat. * * Used for OFPAT10_SET_VLAN_PCP and OFPAT11_SET_VLAN_PCP. */ struct ofpact_vlan_pcp { struct ofpact ofpact; uint8_t vlan_pcp; /* VLAN PCP in low 3 bits, 0 in other bits. */ bool push_vlan_if_needed; /* OF 1.0 semantics if true. */ bool flow_has_vlan; /* VLAN present at action validation time? */ }; /* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST. * * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */ struct ofpact_mac { struct ofpact ofpact; struct eth_addr mac; }; /* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST. * * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */ struct ofpact_ipv4 { struct ofpact ofpact; ovs_be32 ipv4; }; /* OFPACT_SET_IP_DSCP. * * Used for OFPAT10_SET_NW_TOS. */ struct ofpact_dscp { struct ofpact ofpact; uint8_t dscp; /* DSCP in high 6 bits, rest ignored. */ }; /* OFPACT_SET_IP_ECN. * * Used for OFPAT11_SET_NW_ECN. */ struct ofpact_ecn { struct ofpact ofpact; uint8_t ecn; /* ECN in low 2 bits, rest ignored. */ }; /* OFPACT_SET_IP_TTL. * * Used for OFPAT11_SET_NW_TTL. */ struct ofpact_ip_ttl { struct ofpact ofpact; uint8_t ttl; }; /* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT. * * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */ struct ofpact_l4_port { struct ofpact ofpact; uint16_t port; /* TCP, UDP or SCTP port number. */ uint8_t flow_ip_proto; /* IP proto from corresponding match, or 0 */ }; /* OFPACT_REG_MOVE. * * Used for NXAST_REG_MOVE. */ struct ofpact_reg_move { struct ofpact ofpact; struct mf_subfield src; struct mf_subfield dst; }; /* OFPACT_STACK_PUSH. * * Used for NXAST_STACK_PUSH and NXAST_STACK_POP. */ struct ofpact_stack { struct ofpact ofpact; struct mf_subfield subfield; }; /* OFPACT_SET_FIELD. * * Used for NXAST_REG_LOAD and OFPAT12_SET_FIELD. */ struct ofpact_set_field { struct ofpact ofpact; const struct mf_field *field; bool flow_has_vlan; /* VLAN present at action validation time. */ union mf_value value; union mf_value mask; }; /* OFPACT_PUSH_VLAN/MPLS/PBB * * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */ struct ofpact_push_mpls { struct ofpact ofpact; ovs_be16 ethertype; }; /* OFPACT_POP_MPLS * * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS.. */ struct ofpact_pop_mpls { struct ofpact ofpact; ovs_be16 ethertype; }; /* OFPACT_SET_TUNNEL. * * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */ struct ofpact_tunnel { struct ofpact ofpact; uint64_t tun_id; }; /* OFPACT_SET_QUEUE. * * Used for NXAST_SET_QUEUE. */ struct ofpact_queue { struct ofpact ofpact; uint32_t queue_id; }; /* OFPACT_FIN_TIMEOUT. * * Used for NXAST_FIN_TIMEOUT. */ struct ofpact_fin_timeout { struct ofpact ofpact; uint16_t fin_idle_timeout; uint16_t fin_hard_timeout; }; /* OFPACT_WRITE_METADATA. * * Used for NXAST_WRITE_METADATA. */ struct ofpact_metadata { struct ofpact ofpact; ovs_be64 metadata; ovs_be64 mask; }; /* OFPACT_METER. * * Used for OFPIT13_METER. */ struct ofpact_meter { struct ofpact ofpact; uint32_t meter_id; }; /* OFPACT_WRITE_ACTIONS. * * Used for OFPIT11_WRITE_ACTIONS. */ struct ofpact_nest { struct ofpact ofpact; uint8_t pad[PAD_SIZE(sizeof(struct ofpact), OFPACT_ALIGNTO)]; struct ofpact actions[]; }; BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) % OFPACT_ALIGNTO == 0); BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) == sizeof(struct ofpact_nest)); /* Bits for 'flags' in struct nx_action_conntrack. * * If NX_CT_F_COMMIT is set, then the connection entry is moved from the * unconfirmed to confirmed list in the tracker. */ enum nx_conntrack_flags { NX_CT_F_COMMIT = 1 << 0, }; /* Magic value for struct nx_action_conntrack 'recirc_table' field, to specify * that the packet should not be recirculated. */ #define NX_CT_RECIRC_NONE OFPTT_ALL /* We want to determine the size of these elements at compile time to ensure * actions alignment, but we also want to allow ofpact_conntrack to have * basic _put(), _get(), etc accessors defined below which access these * members directly from ofpact_conntrack. An anonymous struct will serve * both of these purposes. */ #define CT_MEMBERS \ struct { \ struct ofpact ofpact; \ uint16_t flags; \ uint16_t zone_imm; \ struct mf_subfield zone_src; \ uint16_t alg; \ uint8_t recirc_table; \ } #if !defined(IPPORT_FTP) #define IPPORT_FTP 21 #endif /* OFPACT_CT. * * Used for NXAST_CT. */ struct ofpact_conntrack { union { CT_MEMBERS; uint8_t pad[OFPACT_ALIGN(sizeof(CT_MEMBERS))]; }; struct ofpact actions[0]; }; BUILD_ASSERT_DECL(offsetof(struct ofpact_conntrack, actions) % OFPACT_ALIGNTO == 0); BUILD_ASSERT_DECL(offsetof(struct ofpact_conntrack, actions) == sizeof(struct ofpact_conntrack)); static inline size_t ofpact_ct_get_action_len(const struct ofpact_conntrack *oc) { return oc->ofpact.len - offsetof(struct ofpact_conntrack, actions); } static inline size_t ofpact_nest_get_action_len(const struct ofpact_nest *on) { return on->ofpact.len - offsetof(struct ofpact_nest, actions); } void ofpacts_execute_action_set(struct ofpbuf *action_list, const struct ofpbuf *action_set); /* OFPACT_RESUBMIT. * * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */ struct ofpact_resubmit { struct ofpact ofpact; ofp_port_t in_port; uint8_t table_id; }; /* Part of struct ofpact_learn, below. */ struct ofpact_learn_spec { int n_bits; /* Number of bits in source and dest. */ int src_type; /* One of NX_LEARN_SRC_*. */ struct mf_subfield src; /* NX_LEARN_SRC_FIELD only. */ union mf_subvalue src_imm; /* NX_LEARN_SRC_IMMEDIATE only. */ int dst_type; /* One of NX_LEARN_DST_*. */ struct mf_subfield dst; /* NX_LEARN_DST_MATCH, NX_LEARN_DST_LOAD only. */ }; /* Bits for 'flags' in struct nx_action_learn. * * If NX_LEARN_F_SEND_FLOW_REM is set, then the learned flows will have their * OFPFF_SEND_FLOW_REM flag set. * * If NX_LEARN_F_DELETE_LEARNED is set, then removing this action will delete * all the flows from the learn action's 'table_id' that have the learn * action's 'cookie'. Important points: * * - The deleted flows include those created by this action, those created * by other learn actions with the same 'table_id' and 'cookie', those * created by flow_mod requests by a controller in the specified table * with the specified cookie, and those created through any other * means. * * - If multiple flows specify "learn" actions with * NX_LEARN_F_DELETE_LEARNED with the same 'table_id' and 'cookie', then * no deletion occurs until all of those "learn" actions are deleted. * * - Deleting a flow that contains a learn action is the most obvious way * to delete a learn action. Modifying a flow's actions, or replacing it * by a new flow, can also delete a learn action. Finally, replacing a * learn action with NX_LEARN_F_DELETE_LEARNED with a learn action * without that flag also effectively deletes the learn action and can * trigger flow deletion. * * NX_LEARN_F_DELETE_LEARNED was added in Open vSwitch 2.4. */ enum nx_learn_flags { NX_LEARN_F_SEND_FLOW_REM = 1 << 0, NX_LEARN_F_DELETE_LEARNED = 1 << 1, }; #define NX_LEARN_N_BITS_MASK 0x3ff #define NX_LEARN_SRC_FIELD (0 << 13) /* Copy from field. */ #define NX_LEARN_SRC_IMMEDIATE (1 << 13) /* Copy from immediate value. */ #define NX_LEARN_SRC_MASK (1 << 13) #define NX_LEARN_DST_MATCH (0 << 11) /* Add match criterion. */ #define NX_LEARN_DST_LOAD (1 << 11) /* Add NXAST_REG_LOAD action. */ #define NX_LEARN_DST_OUTPUT (2 << 11) /* Add OFPAT_OUTPUT action. */ #define NX_LEARN_DST_RESERVED (3 << 11) /* Not yet defined. */ #define NX_LEARN_DST_MASK (3 << 11) /* OFPACT_LEARN. * * Used for NXAST_LEARN. */ struct ofpact_learn { struct ofpact ofpact; uint16_t idle_timeout; /* Idle time before discarding (seconds). */ uint16_t hard_timeout; /* Max time before discarding (seconds). */ uint16_t priority; /* Priority level of flow entry. */ uint8_t table_id; /* Table to insert flow entry. */ ovs_be64 cookie; /* Cookie for new flow. */ enum nx_learn_flags flags; /* NX_LEARN_F_*. */ uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */ uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */ unsigned int n_specs; struct ofpact_learn_spec specs[]; }; /* Multipath link choice algorithm to apply. * * In the descriptions below, 'n_links' is max_link + 1. */ enum nx_mp_algorithm { /* link = hash(flow) % n_links. * * Redistributes all traffic when n_links changes. O(1) performance. See * RFC 2992. * * Use UINT16_MAX for max_link to get a raw hash value. */ NX_MP_ALG_MODULO_N = 0, /* link = hash(flow) / (MAX_HASH / n_links). * * Redistributes between one-quarter and one-half of traffic when n_links * changes. O(1) performance. See RFC 2992. */ NX_MP_ALG_HASH_THRESHOLD = 1, /* Highest Random Weight. * * for i in [0,n_links): * weights[i] = hash(flow, i) * link = { i such that weights[i] >= weights[j] for all j != i } * * Redistributes 1/n_links of traffic when n_links changes. O(n_links) * performance. If n_links is greater than a threshold (currently 64, but * subject to change), Open vSwitch will substitute another algorithm * automatically. See RFC 2992. */ NX_MP_ALG_HRW = 2, /* Iterative Hash. * * i = 0 * repeat: * i = i + 1 * link = hash(flow, i) % arg * while link > max_link * * Redistributes 1/n_links of traffic when n_links changes. O(1) * performance when arg/max_link is bounded by a constant. * * Redistributes all traffic when arg changes. * * arg must be greater than max_link and for best performance should be no * more than approximately max_link * 2. If arg is outside the acceptable * range, Open vSwitch will automatically substitute the least power of 2 * greater than max_link. * * This algorithm is specific to Open vSwitch. */ NX_MP_ALG_ITER_HASH = 3, }; /* OFPACT_CONJUNCTION. * * Used for NXAST_CONJUNCTION. */ struct ofpact_conjunction { struct ofpact ofpact; uint8_t clause; uint8_t n_clauses; uint32_t id; }; /* OFPACT_MULTIPATH. * * Used for NXAST_MULTIPATH. */ struct ofpact_multipath { struct ofpact ofpact; /* What fields to hash and how. */ enum nx_hash_fields fields; uint16_t basis; /* Universal hash parameter. */ /* Multipath link choice algorithm to apply to hash value. */ enum nx_mp_algorithm algorithm; uint16_t max_link; /* Number of output links, minus 1. */ uint32_t arg; /* Algorithm-specific argument. */ /* Where to store the result. */ struct mf_subfield dst; }; /* OFPACT_NOTE. * * Used for NXAST_NOTE. */ struct ofpact_note { struct ofpact ofpact; size_t length; uint8_t data[]; }; /* OFPACT_SAMPLE. * * Used for NXAST_SAMPLE. */ struct ofpact_sample { struct ofpact ofpact; uint16_t probability; // Always >0. uint32_t collector_set_id; uint32_t obs_domain_id; uint32_t obs_point_id; }; /* OFPACT_DEC_TTL. * * Used for OFPAT11_DEC_NW_TTL, NXAST_DEC_TTL and NXAST_DEC_TTL_CNT_IDS. */ struct ofpact_cnt_ids { struct ofpact ofpact; /* Controller ids. */ unsigned int n_controllers; uint16_t cnt_ids[]; }; /* OFPACT_SET_MPLS_LABEL. * * Used for OFPAT11_SET_MPLS_LABEL and NXAST_SET_MPLS_LABEL */ struct ofpact_mpls_label { struct ofpact ofpact; ovs_be32 label; }; /* OFPACT_SET_MPLS_TC. * * Used for OFPAT11_SET_MPLS_TC and NXAST_SET_MPLS_TC */ struct ofpact_mpls_tc { struct ofpact ofpact; uint8_t tc; }; /* OFPACT_SET_MPLS_TTL. * * Used for OFPAT11_SET_MPLS_TTL and NXAST_SET_MPLS_TTL */ struct ofpact_mpls_ttl { struct ofpact ofpact; uint8_t ttl; }; /* OFPACT_GOTO_TABLE * * Used for OFPIT11_GOTO_TABLE */ struct ofpact_goto_table { struct ofpact ofpact; uint8_t table_id; }; /* OFPACT_GROUP. * * Used for OFPAT11_GROUP. */ struct ofpact_group { struct ofpact ofpact; uint32_t group_id; }; /* OFPACT_UNROLL_XLATE. * * Used only internally. */ struct ofpact_unroll_xlate { struct ofpact ofpact; /* Metadata in xlate context, visible to controller via PACKET_INs. */ uint8_t rule_table_id; /* 0xFF if none. */ ovs_be64 rule_cookie; /* OVS_BE64_MAX if none. */ }; /* Converting OpenFlow to ofpacts. */ enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow, unsigned int actions_len, enum ofp_version version, struct ofpbuf *ofpacts); enum ofperr ofpacts_pull_openflow_instructions(struct ofpbuf *openflow, unsigned int instructions_len, enum ofp_version version, struct ofpbuf *ofpacts); enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len, struct flow *, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables, enum ofputil_protocol *usable_protocols); enum ofperr ofpacts_check_consistency(struct ofpact[], size_t ofpacts_len, struct flow *, ofp_port_t max_ports, uint8_t table_id, uint8_t n_tables, enum ofputil_protocol usable_protocols); enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports); /* Converting ofpacts to OpenFlow. */ size_t ofpacts_put_openflow_actions(const struct ofpact[], size_t ofpacts_len, struct ofpbuf *openflow, enum ofp_version); void ofpacts_put_openflow_instructions(const struct ofpact[], size_t ofpacts_len, struct ofpbuf *openflow, enum ofp_version ofp_version); /* Sets of supported actions. */ ovs_be32 ofpact_bitmap_to_openflow(uint64_t ofpacts_bitmap, enum ofp_version); uint64_t ofpact_bitmap_from_openflow(ovs_be32 ofpat_bitmap, enum ofp_version); void ofpact_bitmap_format(uint64_t ofpacts_bitmap, struct ds *); /* Working with ofpacts. */ bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len, ofp_port_t port); bool ofpacts_output_to_group(const struct ofpact[], size_t ofpacts_len, uint32_t group_id); bool ofpacts_equal(const struct ofpact a[], size_t a_len, const struct ofpact b[], size_t b_len); const struct mf_field *ofpact_get_mf_dst(const struct ofpact *ofpact); uint32_t ofpacts_get_meter(const struct ofpact[], size_t ofpacts_len); /* Formatting and parsing ofpacts. */ void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *); char *ofpacts_parse_actions(const char *, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; char *ofpacts_parse_instructions(const char *, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) OVS_WARN_UNUSED_RESULT; const char *ofpact_name(enum ofpact_type); /* Internal use by the helpers below. */ void ofpact_init(struct ofpact *, enum ofpact_type, size_t len); void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len); /* For each OFPACT_ with a corresponding struct , this defines * the following commonly useful functions: * * struct *ofpact_put_(struct ofpbuf *ofpacts); * * Appends a new 'ofpact', of length OFPACT__RAW_SIZE, to 'ofpacts', * initializes it with ofpact_init_(), and returns it. Also sets * 'ofpacts->l2' to the returned action. * * After using this function to add a variable-length action, add the * elements of the flexible array (e.g. with ofpbuf_put()), then use * ofpact_update_len() to update the length embedded into the action. * (Keep in mind the need to refresh the structure from 'ofpacts->frame' * after adding data to 'ofpacts'.) * * struct *ofpact_get_(const struct ofpact *ofpact); * * Returns 'ofpact' cast to "struct *". 'ofpact->type' must be * OFPACT_. * * as well as the following more rarely useful definitions: * * void ofpact_init_(struct *ofpact); * * Initializes the parts of 'ofpact' that identify it as having type * OFPACT_ and length OFPACT__RAW_SIZE and zeros the rest. * * _RAW_SIZE * * The size of the action structure. For a fixed-length action, this is * sizeof(struct ). For a variable-length action, this is the * offset to the variable-length part. * * _SIZE * * An integer constant, the value of OFPACT__RAW_SIZE rounded up to a * multiple of OFPACT_ALIGNTO. */ #define OFPACT(ENUM, STRUCT, MEMBER, NAME) \ BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \ \ enum { OFPACT_##ENUM##_RAW_SIZE \ = (offsetof(struct STRUCT, MEMBER) \ ? offsetof(struct STRUCT, MEMBER) \ : sizeof(struct STRUCT)) }; \ \ enum { OFPACT_##ENUM##_SIZE \ = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) }; \ \ static inline struct STRUCT * \ ofpact_get_##ENUM(const struct ofpact *ofpact) \ { \ ovs_assert(ofpact->type == OFPACT_##ENUM); \ return ALIGNED_CAST(struct STRUCT *, ofpact); \ } \ \ static inline struct STRUCT * \ ofpact_put_##ENUM(struct ofpbuf *ofpacts) \ { \ return ofpact_put(ofpacts, OFPACT_##ENUM, \ OFPACT_##ENUM##_RAW_SIZE); \ } \ \ static inline void \ ofpact_init_##ENUM(struct STRUCT *ofpact) \ { \ ofpact_init(&ofpact->ofpact, OFPACT_##ENUM, \ OFPACT_##ENUM##_RAW_SIZE); \ } OFPACTS #undef OFPACT /* Functions to use after adding ofpacts to a buffer. */ void ofpact_update_len(struct ofpbuf *, struct ofpact *); void ofpact_pad(struct ofpbuf *); /* Additional functions for composing ofpacts. */ struct ofpact_set_field *ofpact_put_reg_load(struct ofpbuf *ofpacts); /* OpenFlow 1.1 instructions. * The order is sorted in execution order. Not in the value of OFPIT11_xxx. * It is enforced on parser from text string. */ #define OVS_INSTRUCTIONS \ DEFINE_INST(OFPIT13_METER, \ ofp13_instruction_meter, false, \ "meter") \ \ DEFINE_INST(OFPIT11_APPLY_ACTIONS, \ ofp11_instruction_actions, true, \ "apply_actions") \ \ DEFINE_INST(OFPIT11_CLEAR_ACTIONS, \ ofp11_instruction, false, \ "clear_actions") \ \ DEFINE_INST(OFPIT11_WRITE_ACTIONS, \ ofp11_instruction_actions, true, \ "write_actions") \ \ DEFINE_INST(OFPIT11_WRITE_METADATA, \ ofp11_instruction_write_metadata, false, \ "write_metadata") \ \ DEFINE_INST(OFPIT11_GOTO_TABLE, \ ofp11_instruction_goto_table, false, \ "goto_table") enum ovs_instruction_type { #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) OVSINST_##ENUM, OVS_INSTRUCTIONS #undef DEFINE_INST }; enum { #define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) + 1 N_OVS_INSTRUCTIONS = OVS_INSTRUCTIONS #undef DEFINE_INST }; const char *ovs_instruction_name_from_type(enum ovs_instruction_type type); int ovs_instruction_type_from_name(const char *name); enum ovs_instruction_type ovs_instruction_type_from_ofpact_type( enum ofpact_type); enum ofperr ovs_instruction_type_from_inst_type( enum ovs_instruction_type *instruction_type, const uint16_t inst_type); ovs_be32 ovsinst_bitmap_to_openflow(uint32_t ovsinst_bitmap, enum ofp_version); uint32_t ovsinst_bitmap_from_openflow(ovs_be32 ofpit_bitmap, enum ofp_version); #endif /* ofp-actions.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dhcp.h0000644000000000000000000000013213534540071015501 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801424.697852806 openvswitch-2.5.9/lib/dhcp.h0000644000175000017500000000366713534540071017203 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DHCP_H #define DHCP_H 1 #include #include "packets.h" #include "util.h" /* Ports used by DHCP. */ #define DHCP_SERVER_PORT 67 /* Port used by DHCP server. */ #define DHCP_CLIENT_PORT 68 /* Port used by DHCP client. */ #define DHCP_HEADER_LEN 236 struct dhcp_header { uint8_t op; /* DHCP_BOOTREQUEST or DHCP_BOOTREPLY. */ uint8_t htype; /* ARP_HRD_ETHERNET (typically). */ uint8_t hlen; /* ETH_ADDR_LEN (typically). */ uint8_t hops; /* Hop count; set to 0 by client. */ ovs_be32 xid; /* Transaction ID. */ ovs_be16 secs; /* Since client started address acquisition. */ ovs_be16 flags; /* DHCP_FLAGS_*. */ ovs_be32 ciaddr; /* Client IP, if it has a lease for one. */ ovs_be32 yiaddr; /* Client ("your") IP address. */ ovs_be32 siaddr; /* Next server IP address. */ ovs_be32 giaddr; /* Relay agent IP address. */ uint8_t chaddr[16]; /* Client hardware address. */ char sname[64]; /* Optional server host name. */ char file[128]; /* Boot file name. */ /* Followed by variable-length options field. */ }; BUILD_ASSERT_DECL(DHCP_HEADER_LEN == sizeof(struct dhcp_header)); #endif /* dhcp.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-msgs.c0000644000000000000000000000013213534540071016311 xustar0030 mtime=1567801401.517682022 30 atime=1567801402.085686193 30 ctime=1567801424.793853514 openvswitch-2.5.9/lib/ofp-msgs.c0000644000175000017500000010574613534540071020014 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofp-msgs.h" #include "byte-order.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "ofpbuf.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "ovs-thread.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_msgs); #define OFPT_VENDOR 4 #define OFPT10_STATS_REQUEST 16 #define OFPT10_STATS_REPLY 17 #define OFPT11_STATS_REQUEST 18 #define OFPT11_STATS_REPLY 19 #define OFPST_VENDOR 0xffff /* A thin abstraction of OpenFlow headers: * * - 'version' and 'type' come straight from struct ofp_header, so these are * always present and meaningful. * * - 'stat' comes from the 'type' member in statistics messages only. It is * meaningful, therefore, only if 'version' and 'type' taken together * specify a statistics request or reply. Otherwise it is 0. * * - 'vendor' is meaningful only for vendor messages, that is, if 'version' * and 'type' specify a vendor message or if 'version' and 'type' specify * a statistics message and 'stat' specifies a vendor statistic type. * Otherwise it is 0. * * - 'subtype' is meaningful only for vendor messages and otherwise 0. It * specifies a vendor-defined subtype. There is no standard format for * these but 32 bits seems like it should be enough. */ struct ofphdrs { uint8_t version; /* From ofp_header. */ uint8_t type; /* From ofp_header. */ uint16_t stat; /* From ofp10_stats_msg or ofp11_stats_msg. */ uint32_t vendor; /* From ofp_vendor_header, * ofp10_vendor_stats_msg, or * ofp11_vendor_stats_msg. */ uint32_t subtype; /* From nicira_header, nicira10_stats_msg, or * nicira11_stats_msg. */ }; BUILD_ASSERT_DECL(sizeof(struct ofphdrs) == 12); /* A mapping from OpenFlow headers to OFPRAW_*. */ struct raw_instance { struct hmap_node hmap_node; /* In 'raw_instance_map'. */ struct ofphdrs hdrs; /* Key. */ enum ofpraw raw; /* Value. */ unsigned int hdrs_len; /* ofphdrs_len(hdrs). */ }; /* Information about a particular 'enum ofpraw'. */ struct raw_info { /* All possible instantiations of this OFPRAW_* into OpenFlow headers. */ struct raw_instance *instances; /* min_version - max_version + 1 elems. */ uint8_t min_version; uint8_t max_version; unsigned int min_body; unsigned int extra_multiple; enum ofptype type; const char *name; }; /* All understood OpenFlow message types, indexed by their 'struct ofphdrs'. */ static struct hmap raw_instance_map; #include "ofp-msgs.inc" static ovs_be32 alloc_xid(void); /* ofphdrs functions. */ static uint32_t ofphdrs_hash(const struct ofphdrs *); static bool ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b); static enum ofperr ofphdrs_decode(struct ofphdrs *, const struct ofp_header *oh, size_t length); static void ofphdrs_decode_assert(struct ofphdrs *, const struct ofp_header *oh, size_t length); size_t ofphdrs_len(const struct ofphdrs *); static const struct raw_info *raw_info_get(enum ofpraw); static struct raw_instance *raw_instance_get(const struct raw_info *, uint8_t version); static enum ofperr ofpraw_from_ofphdrs(enum ofpraw *, const struct ofphdrs *); /* Returns a transaction ID to use for an outgoing OpenFlow message. */ static ovs_be32 alloc_xid(void) { static atomic_count next_xid = ATOMIC_COUNT_INIT(1); return htonl(atomic_count_inc(&next_xid)); } static uint32_t ofphdrs_hash(const struct ofphdrs *hdrs) { BUILD_ASSERT_DECL(sizeof *hdrs == 12); return hash_words((const uint32_t *) hdrs, 3, 0); } static bool ofphdrs_equal(const struct ofphdrs *a, const struct ofphdrs *b) { return !memcmp(a, b, sizeof *a); } static void log_bad_vendor(uint32_t vendor) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "OpenFlow message has unknown vendor %#"PRIx32, vendor); } static enum ofperr ofphdrs_decode(struct ofphdrs *hdrs, const struct ofp_header *oh, size_t length) { memset(hdrs, 0, sizeof *hdrs); if (length < sizeof *oh) { return OFPERR_OFPBRC_BAD_LEN; } /* Get base message version and type (OFPT_*). */ hdrs->version = oh->version; hdrs->type = oh->type; if (hdrs->type == OFPT_VENDOR) { /* Get vendor. */ const struct ofp_vendor_header *ovh; if (length < sizeof *ovh) { return OFPERR_OFPBRC_BAD_LEN; } ovh = (const struct ofp_vendor_header *) oh; hdrs->vendor = ntohl(ovh->vendor); if (hdrs->vendor == NX_VENDOR_ID) { /* Get Nicira message subtype (NXT_*). */ const struct nicira_header *nh; if (length < sizeof *nh) { return OFPERR_OFPBRC_BAD_LEN; } nh = (const struct nicira_header *) oh; hdrs->subtype = ntohl(nh->subtype); } else { log_bad_vendor(hdrs->vendor); return OFPERR_OFPBRC_BAD_VENDOR; } } else if (hdrs->version == OFP10_VERSION && (hdrs->type == OFPT10_STATS_REQUEST || hdrs->type == OFPT10_STATS_REPLY)) { const struct ofp10_stats_msg *osm; /* Get statistic type (OFPST_*). */ if (length < sizeof *osm) { return OFPERR_OFPBRC_BAD_LEN; } osm = (const struct ofp10_stats_msg *) oh; hdrs->stat = ntohs(osm->type); if (hdrs->stat == OFPST_VENDOR) { /* Get vendor. */ const struct ofp10_vendor_stats_msg *ovsm; if (length < sizeof *ovsm) { return OFPERR_OFPBRC_BAD_LEN; } ovsm = (const struct ofp10_vendor_stats_msg *) oh; hdrs->vendor = ntohl(ovsm->vendor); if (hdrs->vendor == NX_VENDOR_ID) { /* Get Nicira statistic type (NXST_*). */ const struct nicira10_stats_msg *nsm; if (length < sizeof *nsm) { return OFPERR_OFPBRC_BAD_LEN; } nsm = (const struct nicira10_stats_msg *) oh; hdrs->subtype = ntohl(nsm->subtype); } else { log_bad_vendor(hdrs->vendor); return OFPERR_OFPBRC_BAD_VENDOR; } } } else if (hdrs->version != OFP10_VERSION && (hdrs->type == OFPT11_STATS_REQUEST || hdrs->type == OFPT11_STATS_REPLY)) { const struct ofp11_stats_msg *osm; /* Get statistic type (OFPST_*). */ if (length < sizeof *osm) { return OFPERR_OFPBRC_BAD_LEN; } osm = (const struct ofp11_stats_msg *) oh; hdrs->stat = ntohs(osm->type); if (hdrs->stat == OFPST_VENDOR) { /* Get vendor. */ const struct ofp11_vendor_stats_msg *ovsm; if (length < sizeof *ovsm) { return OFPERR_OFPBRC_BAD_LEN; } ovsm = (const struct ofp11_vendor_stats_msg *) oh; hdrs->vendor = ntohl(ovsm->vendor); if (hdrs->vendor == NX_VENDOR_ID) { /* Get Nicira statistic type (NXST_*). */ const struct nicira11_stats_msg *nsm; if (length < sizeof *nsm) { return OFPERR_OFPBRC_BAD_LEN; } nsm = (const struct nicira11_stats_msg *) oh; hdrs->subtype = ntohl(nsm->subtype); } else { log_bad_vendor(hdrs->vendor); return OFPERR_OFPBRC_BAD_VENDOR; } } } return 0; } static void ofphdrs_decode_assert(struct ofphdrs *hdrs, const struct ofp_header *oh, size_t length) { enum ofperr error = ofphdrs_decode(hdrs, oh, length); ovs_assert(!error); } static bool ofp_is_stat_request(enum ofp_version version, uint8_t type) { switch (version) { case OFP10_VERSION: return type == OFPT10_STATS_REQUEST; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: return type == OFPT11_STATS_REQUEST; } return false; } static bool ofp_is_stat_reply(enum ofp_version version, uint8_t type) { switch (version) { case OFP10_VERSION: return type == OFPT10_STATS_REPLY; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: return type == OFPT11_STATS_REPLY; } return false; } static bool ofp_is_stat(enum ofp_version version, uint8_t type) { return (ofp_is_stat_request(version, type) || ofp_is_stat_reply(version, type)); } static bool ofphdrs_is_stat(const struct ofphdrs *hdrs) { return ofp_is_stat(hdrs->version, hdrs->type); } size_t ofphdrs_len(const struct ofphdrs *hdrs) { if (hdrs->type == OFPT_VENDOR) { return sizeof(struct nicira_header); } switch ((enum ofp_version) hdrs->version) { case OFP10_VERSION: if (hdrs->type == OFPT10_STATS_REQUEST || hdrs->type == OFPT10_STATS_REPLY) { return (hdrs->stat == OFPST_VENDOR ? sizeof(struct nicira10_stats_msg) : sizeof(struct ofp10_stats_msg)); } break; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: if (hdrs->type == OFPT11_STATS_REQUEST || hdrs->type == OFPT11_STATS_REPLY) { return (hdrs->stat == OFPST_VENDOR ? sizeof(struct nicira11_stats_msg) : sizeof(struct ofp11_stats_msg)); } break; } return sizeof(struct ofp_header); } /* Determines the OFPRAW_* type of the OpenFlow message at 'oh', which has * length 'oh->length'. (The caller must ensure that 'oh->length' bytes of * data are readable at 'oh'.) On success, returns 0 and stores the type into * '*raw'. On failure, returns an OFPERR_* error code and zeros '*raw'. * * This function checks that 'oh' is a valid length for its particular type of * message, and returns an error if not. */ enum ofperr ofpraw_decode(enum ofpraw *raw, const struct ofp_header *oh) { struct ofpbuf msg; ofpbuf_use_const(&msg, oh, ntohs(oh->length)); return ofpraw_pull(raw, &msg); } /* Does the same job as ofpraw_decode(), except that it assert-fails if * ofpraw_decode() would have reported an error. Thus, it's able to use the * return value for the OFPRAW_* message type instead of an error code. * * (It only makes sense to use this function if you previously called * ofpraw_decode() on the message and thus know that it's OK.) */ enum ofpraw ofpraw_decode_assert(const struct ofp_header *oh) { enum ofperr error; enum ofpraw raw; error = ofpraw_decode(&raw, oh); ovs_assert(!error); return raw; } /* Determines the OFPRAW_* type of the OpenFlow message in 'msg', which starts * at 'msg->data' and has length 'msg->size' bytes. On success, * returns 0 and stores the type into '*rawp'. On failure, returns an OFPERR_* * error code and zeros '*rawp'. * * This function checks that the message has a valid length for its particular * type of message, and returns an error if not. * * In addition to setting '*rawp', this function pulls off the OpenFlow header * (including the stats headers, vendor header, and any subtype header) with * ofpbuf_pull(). It also sets 'msg->frame' to the start of the OpenFlow * header and 'msg->msg' just beyond the headers (that is, to the final value of * msg->data). */ enum ofperr ofpraw_pull(enum ofpraw *rawp, struct ofpbuf *msg) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct raw_instance *instance; const struct raw_info *info; struct ofphdrs hdrs; unsigned int min_len; unsigned int len; enum ofperr error; enum ofpraw raw; /* Set default outputs. */ msg->header = msg->data; msg->msg = msg->header; *rawp = 0; len = msg->size; error = ofphdrs_decode(&hdrs, msg->data, len); if (error) { return error; } error = ofpraw_from_ofphdrs(&raw, &hdrs); if (error) { return error; } info = raw_info_get(raw); instance = raw_instance_get(info, hdrs.version); msg->header = ofpbuf_pull(msg, instance->hdrs_len); msg->msg = msg->data; min_len = instance->hdrs_len + info->min_body; switch (info->extra_multiple) { case 0: if (len != min_len) { VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected " "length %u)", info->name, len, min_len); return OFPERR_OFPBRC_BAD_LEN; } break; case 1: if (len < min_len) { VLOG_WARN_RL(&rl, "received %s with incorrect length %u (expected " "length at least %u bytes)", info->name, len, min_len); return OFPERR_OFPBRC_BAD_LEN; } break; default: if (len < min_len || (len - min_len) % info->extra_multiple) { VLOG_WARN_RL(&rl, "received %s with incorrect length %u (must be " "exactly %u bytes or longer by an integer multiple " "of %u bytes)", info->name, len, min_len, info->extra_multiple); return OFPERR_OFPBRC_BAD_LEN; } break; } *rawp = raw; return 0; } /* Does the same job as ofpraw_pull(), except that it assert-fails if * ofpraw_pull() would have reported an error. Thus, it's able to use the * return value for the OFPRAW_* message type instead of an error code. * * (It only makes sense to use this function if you previously called * ofpraw_decode() on the message and thus know that it's OK.) */ enum ofpraw ofpraw_pull_assert(struct ofpbuf *msg) { enum ofperr error; enum ofpraw raw; error = ofpraw_pull(&raw, msg); ovs_assert(!error); return raw; } /* Determines the OFPRAW_* type of the OpenFlow message that starts at 'oh' and * has length 'length' bytes. On success, returns 0 and stores the type into * '*rawp'. On failure, returns an OFPERR_* error code and zeros '*rawp'. * * Unlike other functions for decoding message types, this one is not picky * about message length. For example, it will successfully decode a message * whose body is shorter than the minimum length for a message of its type. * Thus, this is the correct function to use for decoding the type of a message * that might have been truncated, such as the payload of an OpenFlow error * message (which is allowed to be truncated to 64 bytes). */ enum ofperr ofpraw_decode_partial(enum ofpraw *raw, const struct ofp_header *oh, size_t length) { struct ofphdrs hdrs; enum ofperr error; error = ofphdrs_decode(&hdrs, oh, length); if (!error) { error = ofpraw_from_ofphdrs(raw, &hdrs); } if (error) { *raw = 0; } return error; } /* Encoding messages using OFPRAW_* values. */ static void ofpraw_put__(enum ofpraw, uint8_t version, ovs_be32 xid, size_t extra_tailroom, struct ofpbuf *); /* Allocates and returns a new ofpbuf that contains an OpenFlow header for * 'raw' with OpenFlow version 'version' and a fresh OpenFlow transaction ID. * The ofpbuf has enough tailroom for the minimum body length of 'raw', plus * 'extra_tailroom' additional bytes. * * Each 'raw' value is valid only for certain OpenFlow versions. The caller * must specify a valid (raw, version) pair. * * In the returned ofpbuf, 'frame' points to the beginning of the * OpenFlow header and 'l3' points just after it, to where the * message's body will start. The caller must actually allocate the * body into the space reserved for it, e.g. with ofpbuf_put_uninit(). * * The caller owns the returned ofpbuf and must free it when it is no longer * needed, e.g. with ofpbuf_delete(). */ struct ofpbuf * ofpraw_alloc(enum ofpraw raw, uint8_t version, size_t extra_tailroom) { return ofpraw_alloc_xid(raw, version, alloc_xid(), extra_tailroom); } /* Same as ofpraw_alloc() but the caller provides the transaction ID. */ struct ofpbuf * ofpraw_alloc_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid, size_t extra_tailroom) { struct ofpbuf *buf = ofpbuf_new(0); ofpraw_put__(raw, version, xid, extra_tailroom, buf); return buf; } /* Same as ofpraw_alloc(), but obtains the OpenFlow version and transaction ID * from 'request->version' and 'request->xid', respectively. * * Even though the version comes from 'request->version', the caller must still * know what it is doing, by specifying a valid pairing of 'raw' and * 'request->version', just like ofpraw_alloc(). */ struct ofpbuf * ofpraw_alloc_reply(enum ofpraw raw, const struct ofp_header *request, size_t extra_tailroom) { return ofpraw_alloc_xid(raw, request->version, request->xid, extra_tailroom); } /* Allocates and returns a new ofpbuf that contains an OpenFlow header that is * a stats reply to the stats request in 'request', using the same OpenFlow * version and transaction ID as 'request'. The ofpbuf has enough tailroom for * the stats reply's minimum body length, plus 'extra_tailroom' additional * bytes. * * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST* * value. Every stats request has a corresponding reply, so the (raw, version) * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here. * * In the returned ofpbuf, 'frame' points to the beginning of the * OpenFlow header and 'l3' points just after it, to where the * message's body will start. The caller must actually allocate the * body into the space reserved for it, e.g. with ofpbuf_put_uninit(). * * The caller owns the returned ofpbuf and must free it when it is no longer * needed, e.g. with ofpbuf_delete(). */ struct ofpbuf * ofpraw_alloc_stats_reply(const struct ofp_header *request, size_t extra_tailroom) { enum ofpraw request_raw; enum ofpraw reply_raw; enum ofperr error; error = ofpraw_decode_partial(&request_raw, request, ntohs(request->length)); ovs_assert(!error); reply_raw = ofpraw_stats_request_to_reply(request_raw, request->version); ovs_assert(reply_raw); return ofpraw_alloc_reply(reply_raw, request, extra_tailroom); } /* Appends to 'buf' an OpenFlow header for 'raw' with OpenFlow version * 'version' and a fresh OpenFlow transaction ID. Preallocates enough tailroom * in 'buf' for the minimum body length of 'raw', plus 'extra_tailroom' * additional bytes. * * Each 'raw' value is valid only for certain OpenFlow versions. The caller * must specify a valid (raw, version) pair. * * Upon return, 'buf->frame' points to the beginning of the OpenFlow header and * 'buf->msg' points just after it, to where the message's body will start. The * caller must actually allocating the body into the space reserved for it, * e.g. with ofpbuf_put_uninit(). */ void ofpraw_put(enum ofpraw raw, uint8_t version, struct ofpbuf *buf) { ofpraw_put__(raw, version, alloc_xid(), 0, buf); } /* Same as ofpraw_put() but the caller provides the transaction ID. */ void ofpraw_put_xid(enum ofpraw raw, uint8_t version, ovs_be32 xid, struct ofpbuf *buf) { ofpraw_put__(raw, version, xid, 0, buf); } /* Same as ofpraw_put(), but obtains the OpenFlow version and transaction ID * from 'request->version' and 'request->xid', respectively. * * Even though the version comes from 'request->version', the caller must still * know what it is doing, by specifying a valid pairing of 'raw' and * 'request->version', just like ofpraw_put(). */ void ofpraw_put_reply(enum ofpraw raw, const struct ofp_header *request, struct ofpbuf *buf) { ofpraw_put__(raw, request->version, request->xid, 0, buf); } /* Appends to 'buf' an OpenFlow header that is a stats reply to the stats * request in 'request', using the same OpenFlow version and transaction ID as * 'request'. Preallocate enough tailroom in 'buf for the stats reply's * minimum body length, plus 'extra_tailroom' additional bytes. * * 'request' must be a stats request, that is, an OFPRAW_OFPST* or OFPRAW_NXST* * value. Every stats request has a corresponding reply, so the (raw, version) * pairing pitfalls of the other ofpraw_alloc_*() functions don't apply here. * * In the returned ofpbuf, 'frame' points to the beginning of the * OpenFlow header and 'l3' points just after it, to where the * message's body will start. The caller must actually allocate the * body into the space reserved for it, e.g. with ofpbuf_put_uninit(). * * The caller owns the returned ofpbuf and must free it when it is no longer * needed, e.g. with ofpbuf_delete(). */ void ofpraw_put_stats_reply(const struct ofp_header *request, struct ofpbuf *buf) { enum ofperr error; enum ofpraw raw; error = ofpraw_decode_partial(&raw, request, ntohs(request->length)); ovs_assert(!error); raw = ofpraw_stats_request_to_reply(raw, request->version); ovs_assert(raw); ofpraw_put__(raw, request->version, request->xid, 0, buf); } static void ofpraw_put__(enum ofpraw raw, uint8_t version, ovs_be32 xid, size_t extra_tailroom, struct ofpbuf *buf) { const struct raw_info *info = raw_info_get(raw); const struct raw_instance *instance = raw_instance_get(info, version); const struct ofphdrs *hdrs = &instance->hdrs; struct ofp_header *oh; ofpbuf_prealloc_tailroom(buf, (instance->hdrs_len + info->min_body + extra_tailroom)); buf->header = ofpbuf_put_uninit(buf, instance->hdrs_len); buf->msg = ofpbuf_tail(buf); oh = buf->header; oh->version = version; oh->type = hdrs->type; oh->length = htons(buf->size); oh->xid = xid; if (hdrs->type == OFPT_VENDOR) { struct nicira_header *nh = buf->header; ovs_assert(hdrs->vendor == NX_VENDOR_ID); nh->vendor = htonl(hdrs->vendor); nh->subtype = htonl(hdrs->subtype); } else if (version == OFP10_VERSION && (hdrs->type == OFPT10_STATS_REQUEST || hdrs->type == OFPT10_STATS_REPLY)) { struct ofp10_stats_msg *osm = buf->header; osm->type = htons(hdrs->stat); osm->flags = htons(0); if (hdrs->stat == OFPST_VENDOR) { struct ofp10_vendor_stats_msg *ovsm = buf->header; ovsm->vendor = htonl(hdrs->vendor); if (hdrs->vendor == NX_VENDOR_ID) { struct nicira10_stats_msg *nsm = buf->header; nsm->subtype = htonl(hdrs->subtype); memset(nsm->pad, 0, sizeof nsm->pad); } else { OVS_NOT_REACHED(); } } } else if (version != OFP10_VERSION && (hdrs->type == OFPT11_STATS_REQUEST || hdrs->type == OFPT11_STATS_REPLY)) { struct ofp11_stats_msg *osm = buf->header; osm->type = htons(hdrs->stat); osm->flags = htons(0); memset(osm->pad, 0, sizeof osm->pad); if (hdrs->stat == OFPST_VENDOR) { struct ofp11_vendor_stats_msg *ovsm = buf->header; ovsm->vendor = htonl(hdrs->vendor); if (hdrs->vendor == NX_VENDOR_ID) { struct nicira11_stats_msg *nsm = buf->header; nsm->subtype = htonl(hdrs->subtype); } else { OVS_NOT_REACHED(); } } } } /* Returns 'raw''s name. * * The name is the name used for 'raw' in the OpenFlow specification. For * example, ofpraw_get_name(OFPRAW_OFPT10_FEATURES_REPLY) is * "OFPT_FEATURES_REPLY". * * The caller must not modify or free the returned string. */ const char * ofpraw_get_name(enum ofpraw raw) { return raw_info_get(raw)->name; } /* Returns the stats reply that corresponds to 'raw' in the given OpenFlow * 'version'. */ enum ofpraw ofpraw_stats_request_to_reply(enum ofpraw raw, uint8_t version) { const struct raw_info *info = raw_info_get(raw); const struct raw_instance *instance = raw_instance_get(info, version); enum ofpraw reply_raw; struct ofphdrs hdrs; enum ofperr error; hdrs = instance->hdrs; switch ((enum ofp_version)hdrs.version) { case OFP10_VERSION: ovs_assert(hdrs.type == OFPT10_STATS_REQUEST); hdrs.type = OFPT10_STATS_REPLY; break; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: ovs_assert(hdrs.type == OFPT11_STATS_REQUEST); hdrs.type = OFPT11_STATS_REPLY; break; default: OVS_NOT_REACHED(); } error = ofpraw_from_ofphdrs(&reply_raw, &hdrs); ovs_assert(!error); return reply_raw; } /* Determines the OFPTYPE_* type of the OpenFlow message at 'oh', which has * length 'oh->length'. (The caller must ensure that 'oh->length' bytes of * data are readable at 'oh'.) On success, returns 0 and stores the type into * '*typep'. On failure, returns an OFPERR_* error code and zeros '*typep'. * * This function checks that 'oh' is a valid length for its particular type of * message, and returns an error if not. */ enum ofperr ofptype_decode(enum ofptype *typep, const struct ofp_header *oh) { enum ofperr error; enum ofpraw raw; error = ofpraw_decode(&raw, oh); *typep = error ? 0 : ofptype_from_ofpraw(raw); return error; } /* Determines the OFPTYPE_* type of the OpenFlow message in 'msg', which starts * at 'msg->data' and has length 'msg->size' bytes. On success, * returns 0 and stores the type into '*typep'. On failure, returns an * OFPERR_* error code and zeros '*typep'. * * This function checks that the message has a valid length for its particular * type of message, and returns an error if not. * * In addition to setting '*typep', this function pulls off the OpenFlow header * (including the stats headers, vendor header, and any subtype header) with * ofpbuf_pull(). It also sets 'msg->frame' to the start of the OpenFlow * header and 'msg->msg' just beyond the headers (that is, to the final value of * msg->data). */ enum ofperr ofptype_pull(enum ofptype *typep, struct ofpbuf *buf) { enum ofperr error; enum ofpraw raw; error = ofpraw_pull(&raw, buf); *typep = error ? 0 : ofptype_from_ofpraw(raw); return error; } /* Returns the OFPTYPE_* type that corresponds to 'raw'. * * (This is a one-way trip, because the mapping from ofpraw to ofptype is * many-to-one.) */ enum ofptype ofptype_from_ofpraw(enum ofpraw raw) { return raw_info_get(raw)->type; } const char * ofptype_get_name(enum ofptype type) { ovs_assert(type < ARRAY_SIZE(type_names)); return type_names[type]; } /* Updates the 'length' field of the OpenFlow message in 'buf' to * 'buf->size'. */ void ofpmsg_update_length(struct ofpbuf *buf) { struct ofp_header *oh = ofpbuf_at_assert(buf, 0, sizeof *oh); oh->length = htons(buf->size); } /* Returns just past the OpenFlow header (including the stats headers, vendor * header, and any subtype header) in 'oh'. */ const void * ofpmsg_body(const struct ofp_header *oh) { struct ofphdrs hdrs; ofphdrs_decode_assert(&hdrs, oh, ntohs(oh->length)); return (const uint8_t *) oh + ofphdrs_len(&hdrs); } /* Return if it's a stat/multipart (OFPST) request message. */ bool ofpmsg_is_stat_request(const struct ofp_header *oh) { return ofp_is_stat_request(oh->version, oh->type); } static ovs_be16 *ofpmp_flags__(const struct ofp_header *); /* Initializes 'replies' as a new list of stats messages that reply to * 'request', which must be a stats request message. Initially the list will * consist of only a single reply part without any body. The caller should * use calls to the other ofpmp_*() functions to add to the body and split the * message into multiple parts, if necessary. */ void ofpmp_init(struct ovs_list *replies, const struct ofp_header *request) { struct ofpbuf *msg; list_init(replies); msg = ofpraw_alloc_stats_reply(request, 1000); list_push_back(replies, &msg->list_node); } /* Prepares to append up to 'len' bytes to the series of statistics replies in * 'replies', which should have been initialized with ofpmp_init(), if * necessary adding a new reply to the list. * * Returns an ofpbuf with at least 'len' bytes of tailroom. The 'len' bytes * have not actually been allocated, so the caller must do so with * e.g. ofpbuf_put_uninit(). */ struct ofpbuf * ofpmp_reserve(struct ovs_list *replies, size_t len) { struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); if (msg->size + len <= UINT16_MAX) { ofpbuf_prealloc_tailroom(msg, len); return msg; } else { unsigned int hdrs_len; struct ofpbuf *next; struct ofphdrs hdrs; ofphdrs_decode_assert(&hdrs, msg->data, msg->size); hdrs_len = ofphdrs_len(&hdrs); next = ofpbuf_new(MAX(1024, hdrs_len + len)); ofpbuf_put(next, msg->data, hdrs_len); next->header = next->data; next->msg = ofpbuf_tail(next); list_push_back(replies, &next->list_node); *ofpmp_flags__(msg->data) |= htons(OFPSF_REPLY_MORE); return next; } } /* Appends 'len' bytes to the series of statistics replies in 'replies', and * returns the first byte. */ void * ofpmp_append(struct ovs_list *replies, size_t len) { return ofpbuf_put_uninit(ofpmp_reserve(replies, len), len); } /* Sometimes, when composing stats replies, it's difficult to predict how long * an individual reply chunk will be before actually encoding it into the reply * buffer. This function allows easy handling of this case: just encode the * reply, then use this function to break the message into two pieces if it * exceeds the OpenFlow message limit. * * In detail, if the final stats message in 'replies' is too long for OpenFlow, * this function breaks it into two separate stats replies, the first one with * the first 'start_ofs' bytes, the second one containing the bytes from that * offset onward. */ void ofpmp_postappend(struct ovs_list *replies, size_t start_ofs) { struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); ovs_assert(start_ofs <= UINT16_MAX); if (msg->size > UINT16_MAX) { size_t len = msg->size - start_ofs; memcpy(ofpmp_append(replies, len), (const uint8_t *) msg->data + start_ofs, len); msg->size = start_ofs; } } /* Returns the OpenFlow version of the replies being constructed in 'replies', * which should have been initialized by ofpmp_init(). */ enum ofp_version ofpmp_version(struct ovs_list *replies) { struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); const struct ofp_header *oh = msg->data; return oh->version; } /* Determines the OFPRAW_* type of the OpenFlow messages in 'replies', which * should have been initialized by ofpmp_init(). */ enum ofpraw ofpmp_decode_raw(struct ovs_list *replies) { struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); enum ofperr error; enum ofpraw raw; error = ofpraw_decode_partial(&raw, msg->data, msg->size); ovs_assert(!error); return raw; } static ovs_be16 * ofpmp_flags__(const struct ofp_header *oh) { switch ((enum ofp_version)oh->version) { case OFP10_VERSION: return &((struct ofp10_stats_msg *) oh)->flags; case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: return &((struct ofp11_stats_msg *) oh)->flags; default: OVS_NOT_REACHED(); } } /* Returns the OFPSF_* flags found in the OpenFlow stats header of 'oh', which * must be an OpenFlow stats request or reply. * * (OFPSF_REPLY_MORE is the only defined flag.) */ uint16_t ofpmp_flags(const struct ofp_header *oh) { return ntohs(*ofpmp_flags__(oh)); } /* Returns true if the OFPSF_REPLY_MORE flag is set in the OpenFlow stats * header of 'oh', which must be an OpenFlow stats request or reply, false if * it is not set. */ bool ofpmp_more(const struct ofp_header *oh) { return (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0; } static void ofpmsgs_init(void); static const struct raw_info * raw_info_get(enum ofpraw raw) { ofpmsgs_init(); ovs_assert(raw < ARRAY_SIZE(raw_infos)); return &raw_infos[raw]; } static struct raw_instance * raw_instance_get(const struct raw_info *info, uint8_t version) { ovs_assert(version >= info->min_version && version <= info->max_version); return &info->instances[version - info->min_version]; } static enum ofperr ofpraw_from_ofphdrs(enum ofpraw *raw, const struct ofphdrs *hdrs) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); struct raw_instance *raw_hdrs; uint32_t hash; ofpmsgs_init(); hash = ofphdrs_hash(hdrs); HMAP_FOR_EACH_WITH_HASH (raw_hdrs, hmap_node, hash, &raw_instance_map) { if (ofphdrs_equal(hdrs, &raw_hdrs->hdrs)) { *raw = raw_hdrs->raw; return 0; } } if (!VLOG_DROP_WARN(&rl)) { struct ds s; ds_init(&s); ds_put_format(&s, "version %"PRIu8", type %"PRIu8, hdrs->version, hdrs->type); if (ofphdrs_is_stat(hdrs)) { ds_put_format(&s, ", stat %"PRIu16, hdrs->stat); } if (hdrs->vendor) { ds_put_format(&s, ", vendor 0x%"PRIx32", subtype %"PRIu32, hdrs->vendor, hdrs->subtype); } VLOG_WARN("unknown OpenFlow message (%s)", ds_cstr(&s)); ds_destroy(&s); } return (hdrs->vendor ? OFPERR_OFPBRC_BAD_SUBTYPE : ofphdrs_is_stat(hdrs) ? OFPERR_OFPBRC_BAD_STAT : OFPERR_OFPBRC_BAD_TYPE); } static void ofpmsgs_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; const struct raw_info *info; if (!ovsthread_once_start(&once)) { return; } hmap_init(&raw_instance_map); for (info = raw_infos; info < &raw_infos[ARRAY_SIZE(raw_infos)]; info++) { int n_instances = info->max_version - info->min_version + 1; struct raw_instance *inst; for (inst = info->instances; inst < &info->instances[n_instances]; inst++) { inst->hdrs_len = ofphdrs_len(&inst->hdrs); hmap_insert(&raw_instance_map, &inst->hmap_node, ofphdrs_hash(&inst->hdrs)); } } ovsthread_once_done(&once); } openvswitch-2.5.9/lib/PaxHeaders.82075/stdio.h.in0000644000000000000000000000013213534540071016312 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801423.789846113 openvswitch-2.5.9/lib/stdio.h.in0000644000175000017500000000245013534540071020001 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #@INCLUDE_NEXT@ @NEXT_STDIO_H@ #if !defined STDIO_H_WRAPPER #define STDIO_H_WRAPPER 1 #ifdef _WIN32 #include #include #include /* Windows libc has defective snprintf() and vsnprintf(): * * - They return -1 if the output won't fit. * * - They don't null-terminate the output if it won't fit. * * We need working versions so here we define substitutes. */ #undef snprintf #define snprintf ovs_snprintf int ovs_snprintf(char *, size_t, const char *, ...); #undef vsnprintf #define vsnprintf ovs_vsnprintf int ovs_vsnprintf(char *, size_t, const char *, va_list); int fseeko(FILE *stream, off_t offset, int whence); #endif /* _WIN32 */ #endif /* stdio.h wrapper */ openvswitch-2.5.9/lib/PaxHeaders.82075/tnl-neigh-cache.h0000644000000000000000000000013213534540071017511 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.917854428 openvswitch-2.5.9/lib/tnl-neigh-cache.h0000644000175000017500000000224313534540071021200 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TNL_NEIGH_CACHE_H #define TNL_NEIGH_CACHE_H 1 #include #include #include #include #include #include #include #include "flow.h" #include "netdev.h" #include "packets.h" #include "util.h" int tnl_neigh_snoop(const struct flow *flow, struct flow_wildcards *wc, const char dev_name[]); int tnl_neigh_lookup(const char dev_name[], const struct in6_addr *dst, struct eth_addr *mac); void tnl_neigh_cache_init(void); void tnl_neigh_cache_run(void); #endif openvswitch-2.5.9/lib/PaxHeaders.82075/service.man0000644000000000000000000000013013534540071016545 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 28 ctime=1567801423.7338457 openvswitch-2.5.9/lib/service.man0000644000175000017500000000073413534540071020241 0ustar00jpettitjpettit00000000000000The following options are valid only on Windows platform. .TP \fB\-\-service\fR Causes \fB\*(PN\fR to run as a service in the background. The service should already have been created through external tools like \fBSC.exe\fR. . .TP \fB\-\-service\-monitor\fR Causes the \fB\*(PN\fR service to be automatically restarted by the Windows services manager if the service dies or exits for unexpected reasons. .IP When \fB\-\-service\fR is not specified, this option has no effect. openvswitch-2.5.9/lib/PaxHeaders.82075/learning-switch.h0000644000000000000000000000013113534540071017660 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.753853219 openvswitch-2.5.9/lib/learning-switch.h0000644000175000017500000000470613534540071021356 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LEARNING_SWITCH_H #define LEARNING_SWITCH_H 1 #include #include #include #include "ofp-util.h" struct ofpbuf; struct rconn; enum lswitch_mode { LSW_NORMAL, /* Always use OFPP_NORMAL. */ LSW_FLOOD, /* Always use OFPP_FLOOD. */ LSW_LEARN /* Learn MACs at controller. */ }; struct lswitch_config { enum lswitch_mode mode; /* 0 to use exact-match flow entries, * a OFPFW10_* bitmask to enable specific wildcards, * or UINT32_MAX to use the default wildcards (wildcarding as many fields * as possible. * * Ignored when max_idle < 0 (in which case no flows are set up). */ uint32_t wildcards; /* <0: Process every packet at the controller. * >=0: Expire flows after they are unused for 'max_idle' seconds. * OFP_FLOW_PERMANENT: Set up permanent flows. */ int max_idle; /* Optional "flow mod" requests to send to the switch at connection time, * to set up the flow table. */ const struct ofputil_flow_mod *default_flows; size_t n_default_flows; enum ofputil_protocol usable_protocols; /* The OpenFlow queue to use by default. Use UINT32_MAX to avoid * specifying a particular queue. */ uint32_t default_queue; /* Maps from a port name to a queue_id. */ const struct simap *port_queues; /* If true, do not reply to any messages from the switch (for debugging * fail-open mode). */ bool mute; }; struct lswitch *lswitch_create(struct rconn *, const struct lswitch_config *); bool lswitch_is_alive(const struct lswitch *); void lswitch_set_queue(struct lswitch *sw, uint32_t queue); void lswitch_run(struct lswitch *); void lswitch_wait(struct lswitch *); void lswitch_destroy(struct lswitch *); void lswitch_mute(struct lswitch *); #endif /* learning-switch.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sflow_sampler.c0000644000000000000000000000013113534540071017432 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801425.013855136 openvswitch-2.5.9/lib/sflow_sampler.c0000644000175000017500000001573013534540071021127 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #include "sflow_api.h" /*_________________--------------------------__________________ _________________ sfl_sampler_init __________________ -----------------__________________________------------------ */ void sfl_sampler_init(SFLSampler *sampler, SFLAgent *agent, SFLDataSource_instance *pdsi) { /* copy the dsi in case it points to sampler->dsi, which we are about to clear. (Thanks to Jagjit Choudray of Force 10 Networks for pointing out this bug) */ SFLDataSource_instance dsi = *pdsi; /* preserve the *nxt pointer too, in case we are resetting this poller and it is already part of the agent's linked list (thanks to Matt Woodly for pointing this out, and to Andy Kitchingman for pointing out that it applies to the hash_nxt ptr too) */ SFLSampler *nxtPtr = sampler->nxt; SFLSampler *hashPtr = sampler->hash_nxt; /* clear everything */ memset(sampler, 0, sizeof(*sampler)); /* restore the linked list and hash-table ptr */ sampler->nxt = nxtPtr; sampler->hash_nxt = hashPtr; /* now copy in the parameters */ sampler->agent = agent; sampler->dsi = dsi; /* set defaults */ sampler->sFlowFsMaximumHeaderSize = SFL_DEFAULT_HEADER_SIZE; sampler->sFlowFsPacketSamplingRate = SFL_DEFAULT_SAMPLING_RATE; } /*_________________--------------------------__________________ _________________ reset __________________ -----------------__________________________------------------ */ static void reset(SFLSampler *sampler) { SFLDataSource_instance dsi = sampler->dsi; sfl_sampler_init(sampler, sampler->agent, &dsi); } /*_________________---------------------------__________________ _________________ MIB access __________________ -----------------___________________________------------------ */ u_int32_t sfl_sampler_get_sFlowFsReceiver(SFLSampler *sampler) { return sampler->sFlowFsReceiver; } void sfl_sampler_set_sFlowFsReceiver(SFLSampler *sampler, u_int32_t sFlowFsReceiver) { sampler->sFlowFsReceiver = sFlowFsReceiver; if(sFlowFsReceiver == 0) reset(sampler); else { /* retrieve and cache a direct pointer to my receiver */ sampler->myReceiver = sfl_agent_getReceiver(sampler->agent, sampler->sFlowFsReceiver); } } u_int32_t sfl_sampler_get_sFlowFsPacketSamplingRate(SFLSampler *sampler) { return sampler->sFlowFsPacketSamplingRate; } void sfl_sampler_set_sFlowFsPacketSamplingRate(SFLSampler *sampler, u_int32_t sFlowFsPacketSamplingRate) { sampler->sFlowFsPacketSamplingRate = sFlowFsPacketSamplingRate; } u_int32_t sfl_sampler_get_sFlowFsMaximumHeaderSize(SFLSampler *sampler) { return sampler->sFlowFsMaximumHeaderSize; } void sfl_sampler_set_sFlowFsMaximumHeaderSize(SFLSampler *sampler, u_int32_t sFlowFsMaximumHeaderSize) { sampler->sFlowFsMaximumHeaderSize = sFlowFsMaximumHeaderSize; } /* call this to set a maximum samples-per-second threshold. If the sampler reaches this threshold it will automatically back off the sampling rate. A value of 0 disables the mechanism */ void sfl_sampler_set_backoffThreshold(SFLSampler *sampler, u_int32_t samplesPerSecond) { sampler->backoffThreshold = samplesPerSecond; } u_int32_t sfl_sampler_get_backoffThreshold(SFLSampler *sampler) { return sampler->backoffThreshold; } u_int32_t sfl_sampler_get_samplesLastTick(SFLSampler *sampler) { return sampler->samplesLastTick; } /*_________________---------------------------------__________________ _________________ sequence number reset __________________ -----------------_________________________________------------------ Used by the agent to indicate a samplePool discontinuity so that the sflow collector will know to ignore the next delta. */ void sfl_sampler_resetFlowSeqNo(SFLSampler *sampler) { sampler->flowSampleSeqNo = 0; } /*_________________---------------------------__________________ _________________ sfl_sampler_tick __________________ -----------------___________________________------------------ */ void sfl_sampler_tick(SFLSampler *sampler, time_t now) { if(sampler->backoffThreshold && sampler->samplesThisTick > sampler->backoffThreshold) { /* automatic backoff. If using hardware sampling then this is where you have to * call out to change the sampling rate and make sure that any other registers/variables * that hold this value are updated. */ sampler->sFlowFsPacketSamplingRate *= 2; } sampler->samplesLastTick = sampler->samplesThisTick; sampler->samplesThisTick = 0; } /*_________________------------------------------__________________ _________________ sfl_sampler_writeFlowSample __________________ -----------------______________________________------------------ */ void sfl_sampler_writeFlowSample(SFLSampler *sampler, SFL_FLOW_SAMPLE_TYPE *fs) { if(fs == NULL) return; sampler->samplesThisTick++; /* increment the sequence number */ fs->sequence_number = ++sampler->flowSampleSeqNo; /* copy the other header fields in */ #ifdef SFL_USE_32BIT_INDEX fs->ds_class = SFL_DS_CLASS(sampler->dsi); fs->ds_index = SFL_DS_INDEX(sampler->dsi); #else fs->source_id = SFL_DS_DATASOURCE(sampler->dsi); #endif /* the sampling rate may have been set already. */ if(fs->sampling_rate == 0) fs->sampling_rate = sampler->sFlowFsPacketSamplingRate; /* the samplePool may be maintained upstream too. */ if( fs->sample_pool == 0) fs->sample_pool = sampler->samplePool; /* sent to my receiver */ if(sampler->myReceiver) sfl_receiver_writeFlowSample(sampler->myReceiver, fs); } #ifdef SFLOW_SOFTWARE_SAMPLING /* ================== software sampling ========================*/ /*_________________---------------------------__________________ _________________ nextRandomSkip __________________ -----------------___________________________------------------ */ inline static u_int32_t nextRandomSkip(u_int32_t mean) { if(mean == 0 || mean == 1) return 1; return ((random() % ((2 * mean) - 1)) + 1); } /*_________________---------------------------__________________ _________________ sfl_sampler_takeSample __________________ -----------------___________________________------------------ */ int sfl_sampler_takeSample(SFLSampler *sampler) { if(sampler->skip == 0) { /* first time - seed the random number generator */ srandom(SFL_DS_INDEX(sampler->dsi)); sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); } /* increment the samplePool */ sampler->samplePool++; if(--sampler->skip == 0) { /* reached zero. Set the next skip and return true. */ sampler->skip = nextRandomSkip(sampler->sFlowFsPacketSamplingRate); return 1; } return 0; } #endif /* SFLOW_SOFTWARE_SAMPLING */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlandev.c0000644000000000000000000000013213534540071016215 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.945854636 openvswitch-2.5.9/lib/vlandev.c0000644000175000017500000002602013534540071017703 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "vlandev.h" #include #include #include #include #include "dummy.h" #include "hash.h" #include "shash.h" #include "socket-util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(vlandev); /* A vlandev implementation. */ struct vlandev_class { int (*vd_refresh)(void); int (*vd_add)(const char *real_dev, int vid); int (*vd_del)(const char *vlan_dev); }; #ifdef __linux__ static const struct vlandev_class vlandev_linux_class; #endif static const struct vlandev_class vlandev_stub_class; static const struct vlandev_class vlandev_dummy_class; /* The in-use vlandev implementation. */ static const struct vlandev_class *vd_class; /* Maps from a VLAN device name (e.g. "eth0.10") to struct vlan_dev. */ static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs); /* Maps from a VLAN real device name (e.g. "eth0") to struct vlan_real_dev. */ static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs); static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid); static int vlandev_del__(const char *vlan_dev); static void vlandev_clear__(void); static const struct vlandev_class * vlandev_get_class(void) { if (!vd_class) { #if __linux__ vd_class = &vlandev_linux_class; #else vd_class = &vlandev_stub_class; #endif } return vd_class; } /* On Linux, the default implementation of VLAN devices creates and destroys * Linux VLAN devices. On other OSess, the default implementation is a * nonfunctional stub. In either case, this function replaces this default * implementation by a "dummy" implementation that simply reports back whatever * the client sets up with vlandev_add() and vlandev_del(). * * Don't call this function directly; use dummy_enable() from dummy.h. */ void vlandev_dummy_enable(void) { if (vd_class != &vlandev_dummy_class) { vd_class = &vlandev_dummy_class; vlandev_clear__(); } } /* Creates a new VLAN device for VLAN 'vid' on top of real Ethernet device * 'real_dev'. Returns 0 if successful, otherwise a positive errno value. On * OSes other than Linux, in the absence of dummies (see * vlandev_dummy_enable()), this always fails. * * The name of the new VLAN device is not easily predictable, because Linux * provides multiple naming schemes, does not allow the client to specify a * name, and does not directly report the new VLAN device's name. Use * vlandev_refresh() then vlandev_get_name() to find out the new VLAN device's * name,. */ int vlandev_add(const char *real_dev, int vid) { return vlandev_get_class()->vd_add(real_dev, vid); } /* Deletes the VLAN device named 'vlan_dev'. Returns 0 if successful, * otherwise a positive errno value. On OSes other than Linux, in the absence * of dummies (see vlandev_dummy_enable()), this always fails. */ int vlandev_del(const char *vlan_dev) { return vlandev_get_class()->vd_del(vlan_dev); } /* Refreshes the cache of real device to VLAN device mappings reported by * vlandev_get_real_devs() and vlandev_get_name(). Without calling this * function, changes made by vlandev_add() and vlandev_del() may not be * reflected by vlandev_get_real_devs() and vlandev_get_name() output. */ int vlandev_refresh(void) { const struct vlandev_class *class = vlandev_get_class(); return class->vd_refresh ? class->vd_refresh() : 0; } /* Returns a shash mapping from the name of real Ethernet devices used as the * basis of VLAN devices to struct vlan_real_devs. The caller must not modify * or free anything in the returned shash. * * Changes made by vlandev_add() and vlandev_del() may not be reflected in this * function's output without an intervening call to vlandev_refresh(). */ struct shash * vlandev_get_real_devs(void) { return &vlan_real_devs; } /* Returns the name of the VLAN device for VLAN 'vid' on top of * 'real_dev_name', or NULL if there is no such VLAN device. * * Changes made by vlandev_add() and vlandev_del() may not be reflected in this * function's output without an intervening call to vlandev_refresh(). */ const char * vlandev_get_name(const char *real_dev_name, int vid) { const struct vlan_real_dev *real_dev; real_dev = shash_find_data(&vlan_real_devs, real_dev_name); if (real_dev) { const struct vlan_dev *vlan_dev; HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0), &real_dev->vlan_devs) { if (vlan_dev->vid == vid) { return vlan_dev->name; } } } return NULL; } /* The Linux vlandev implementation. */ #ifdef __linux__ #include "rtnetlink.h" #include #include #include "netdev-linux.h" static struct nln_notifier *vlan_cache_notifier; static bool cache_valid; static void vlan_cache_cb(const struct rtnetlink_change *change OVS_UNUSED, void *aux OVS_UNUSED) { cache_valid = false; } static int vlandev_linux_refresh(void) { const char *fn = "/proc/net/vlan/config"; char line[128]; FILE *stream; if (!vlan_cache_notifier) { vlan_cache_notifier = rtnetlink_notifier_create(vlan_cache_cb, NULL); if (!vlan_cache_notifier) { return EINVAL; } } if (cache_valid) { return 0; } vlandev_clear__(); /* Repopulate cache. */ stream = fopen(fn, "r"); if (!stream) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); int error = errno; struct stat s; if (error == ENOENT && !stat("/proc", &s)) { /* Probably the vlan module just isn't loaded, and probably that's * because no VLAN devices have been created. * * Not really an error. */ return 0; } VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, ovs_strerror(error)); return error; } while (fgets(line, sizeof line, stream)) { char vlan_dev[16], real_dev[16]; int vid; if (ovs_scan(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev)) { vlandev_add__(vlan_dev, real_dev, vid); } } fclose(stream); cache_valid = true; return 0; } static int do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via, int cmd, const char *cmd_name) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); int error; via->cmd = cmd; ovs_strlcpy(via->device1, netdev_name, sizeof via->device1); error = af_inet_ioctl(SIOCSIFVLAN, via); if (error) { VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)", netdev_name, cmd_name, ovs_strerror(error)); } return error; } static int vlandev_linux_add(const char *real_dev, int vid) { struct vlan_ioctl_args via; int error; memset(&via, 0, sizeof via); via.u.VID = vid; error = do_vlan_ioctl(real_dev, &via, ADD_VLAN_CMD, "ADD_VLAN_CMD"); if (!error) { cache_valid = false; } return error; } static int vlandev_linux_del(const char *vlan_dev) { struct vlan_ioctl_args via; int error; memset(&via, 0, sizeof via); error = do_vlan_ioctl(vlan_dev, &via, DEL_VLAN_CMD, "DEL_VLAN_CMD"); if (!error) { cache_valid = false; } return error; } static const struct vlandev_class vlandev_linux_class = { vlandev_linux_refresh, vlandev_linux_add, vlandev_linux_del }; #endif /* Stub implementation. */ static int vlandev_stub_add(const char *real_dev OVS_UNUSED, int vid OVS_UNUSED) { VLOG_ERR("not supported on non-Linux platform"); return EOPNOTSUPP; } static int vlandev_stub_del(const char *vlan_dev OVS_UNUSED) { VLOG_ERR("not supported on non-Linux platform"); return EOPNOTSUPP; } static const struct vlandev_class OVS_UNUSED vlandev_stub_class = { NULL, /* vd_refresh */ vlandev_stub_add, vlandev_stub_del }; /* Dummy implementation. */ static int vlandev_dummy_add(const char *real_dev, int vid) { char name[IFNAMSIZ]; if (snprintf(name, sizeof name, "%s.%d", real_dev, vid) >= sizeof name) { return ENAMETOOLONG; } return vlandev_add__(name, real_dev, vid); } static int vlandev_dummy_del(const char *vlan_dev) { return vlandev_del__(vlan_dev); } static const struct vlandev_class vlandev_dummy_class = { NULL, /* vd_refresh */ vlandev_dummy_add, vlandev_dummy_del }; static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid) { uint32_t vid_hash = hash_int(vid, 0); struct vlan_real_dev *vrd; struct vlan_dev *vd; if (vid < 0 || vid > 4095) { return EINVAL; } else if (shash_find(&vlan_devs, vlan_dev)) { return EEXIST; } vrd = shash_find_data(&vlan_real_devs, real_dev); if (!vrd) { vrd = xmalloc(sizeof *vrd); vrd->name = xstrdup(real_dev); hmap_init(&vrd->vlan_devs); shash_add_nocopy(&vlan_real_devs, vrd->name, vrd); } else { HMAP_FOR_EACH_WITH_HASH (vd, hmap_node, vid_hash, &vrd->vlan_devs) { if (vd->vid == vid) { return EEXIST; } } } vd = xmalloc(sizeof *vd); hmap_insert(&vrd->vlan_devs, &vd->hmap_node, vid_hash); vd->name = xstrdup(vlan_dev); vd->vid = vid; vd->real_dev = vrd; shash_add_nocopy(&vlan_devs, vd->name, vd); return 0; } static int vlandev_del__(const char *vlan_dev) { struct shash_node *vd_node = shash_find(&vlan_devs, vlan_dev); if (vd_node) { struct vlan_dev *vd = vd_node->data; struct vlan_real_dev *vrd = vd->real_dev; hmap_remove(&vrd->vlan_devs, &vd->hmap_node); if (hmap_is_empty(&vrd->vlan_devs)) { shash_find_and_delete_assert(&vlan_real_devs, vrd->name); free(vrd); } shash_delete(&vlan_devs, vd_node); free(vd); return 0; } else { return ENOENT; } } /* Clear 'vlan_devs' and 'vlan_real_devs' in preparation for repopulating. */ static void vlandev_clear__(void) { /* We do not free the 'name' members of struct vlan_dev and struct * vlan_real_dev, because the "shash"es own them.. */ struct shash_node *node; shash_clear_free_data(&vlan_devs); SHASH_FOR_EACH (node, &vlan_real_devs) { struct vlan_real_dev *vrd = node->data; hmap_destroy(&vrd->vlan_devs); } shash_clear_free_data(&vlan_real_devs); } openvswitch-2.5.9/lib/PaxHeaders.82075/dh1024.pem0000644000000000000000000000013213534540071016017 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801423.789846113 openvswitch-2.5.9/lib/dh1024.pem0000644000175000017500000000075413534540071017513 0ustar00jpettitjpettit00000000000000-----BEGIN DH PARAMETERS----- MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6 ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC -----END DH PARAMETERS----- These are the 1024 bit DH parameters from "Assigned Number for SKIP Protocols" (http://www.skip-vpn.org/spec/numbers.html). See there for how they were generated. Note that g is not a generator, but this is not a problem since p is a safe prime. openvswitch-2.5.9/lib/PaxHeaders.82075/mac-learning.h0000644000000000000000000000013213534540071017120 xustar0030 mtime=1567801401.409681228 30 atime=1567801402.077686135 30 ctime=1567801424.757853249 openvswitch-2.5.9/lib/mac-learning.h0000644000175000017500000002333513534540071020614 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MAC_LEARNING_H #define MAC_LEARNING_H 1 #include #include "heap.h" #include "hmap.h" #include "list.h" #include "ovs-atomic.h" #include "ovs-thread.h" #include "packets.h" #include "timeval.h" /* MAC learning table * ================== * * A MAC learning table is a dictionary data structure that is specialized to * map from an (Ethernet address, VLAN ID) pair to a user-provided pointer. In * an Ethernet switch implementation, it used to keep track of the port on * which a packet from a given Ethernet address was last seen. This knowledge * is useful when the switch receives a packet to such an Ethernet address, so * that the switch can send the packet directly to the correct port instead of * having to flood it to every port. * * A few complications make the implementation into more than a simple wrapper * around a hash table. First, and most simply, MAC learning can be disabled * on a per-VLAN basis. (This is most useful for RSPAN; see * ovs-vswitchd.conf.db(5) documentation of the "output_vlan" column in the * Mirror table for more information.). The data structure maintains a bitmap * to track such VLANs. * * Second, the implementation has the ability to "lock" a MAC table entry * updated by a gratuitous ARP. This is a simple feature but the rationale for * it is complicated. Please refer to the description of SLB bonding in * vswitchd/INTERNALS for an explanation. * * Third, the implementation expires entries that are idle for longer than a * configurable amount of time. This is implemented by keeping all of the * current table entries on a list ordered from least recently used (LRU) to * most recently used (MRU). Each time a MAC entry is used, it is moved to the * MRU end of the list. Periodically mac_learning_run() sweeps through the * list starting from the LRU end, deleting each entry that has been idle too * long. * * Finally, the number of MAC learning table entries has a configurable maximum * size to prevent memory exhaustion. When a new entry must be inserted but * the table is already full, the implementation uses an eviction strategy * based on fairness: it chooses the port that currently has greatest number of * learned MACs (choosing arbitrarily in case of a tie), and among that port's * entries it evicts the least recently used. (This is a security feature * because it prevents an attacker from forcing other ports' MACs out of the * MAC learning table with a "MAC flooding attack" that causes the other ports' * traffic to be flooded so that the attacker can easily sniff it.) The * implementation of this feature is like a specialized form of the * general-purpose "eviction groups" that OVS implements in OpenFlow (see the * documentation of the "groups" column in the Flow_Table table in * ovs-vswitchd.conf.db(5) for details). * * * Thread-safety * ============= * * Many operations require the caller to take the MAC learning table's rwlock * for writing (please refer to the Clang thread safety annotations). The * important exception to this is mac_learning_lookup(), which only needs a * read lock. This is useful for the common case where a MAC learning entry * being looked up already exists and does not need an update. However, * there's no deadlock-free way to upgrade a read lock to a write lock, so in * the case where the lookup result means that an update is required, the * caller must drop the read lock, take the write lock, and then repeat the * lookup (in case some other thread has already made a change). */ struct mac_learning; /* Default maximum size of a MAC learning table, in entries. */ #define MAC_DEFAULT_MAX 2048 /* Time, in seconds, before expiring a mac_entry due to inactivity. */ #define MAC_ENTRY_DEFAULT_IDLE_TIME 300 /* Time, in seconds, to lock an entry updated by a gratuitous ARP to avoid * relearning based on a reflection from a bond slave. */ #define MAC_GRAT_ARP_LOCK_TIME 5 /* A MAC learning table entry. * Guarded by owning 'mac_learning''s rwlock. */ struct mac_entry { struct hmap_node hmap_node; /* Node in a mac_learning hmap. */ time_t expires; /* Expiration time. */ time_t grat_arp_lock; /* Gratuitous ARP lock expiration time. */ struct eth_addr mac; /* Known MAC address. */ uint16_t vlan; /* VLAN tag. */ /* The following are marked guarded to prevent users from iterating over or * accessing a mac_entry without holding the parent mac_learning rwlock. */ struct ovs_list lru_node OVS_GUARDED; /* Element in 'lrus' list. */ /* Learned port. * * The client-specified data is mlport->port. */ struct mac_learning_port *mlport; struct ovs_list port_lru_node; /* In mac_learning_port's "port_lru"s. */ }; static inline void *mac_entry_get_port(const struct mac_learning *ml, const struct mac_entry *); void mac_entry_set_port(struct mac_learning *, struct mac_entry *, void *port); /* Information about client-provided port pointers (the 'port' member), to * allow for per-port fairness. * * The client-provided pointer is opaque to the MAC-learning table, which never * dereferences it. */ struct mac_learning_port { struct hmap_node hmap_node; /* In mac_learning's "ports_by_ptr". */ struct heap_node heap_node; /* In mac_learning's "ports_by_usage". */ void *port; /* Client-provided port pointer. */ struct ovs_list port_lrus; /* Contains "struct mac_entry"s by port_lru. */ }; /* Sets a gratuitous ARP lock on 'mac' that will expire in * MAC_GRAT_ARP_LOCK_TIME seconds. */ static inline void mac_entry_set_grat_arp_lock(struct mac_entry *mac) { mac->grat_arp_lock = time_now() + MAC_GRAT_ARP_LOCK_TIME; } /* Returns true if a gratuitous ARP lock is in effect on 'mac', false if none * has ever been asserted or if it has expired. */ static inline bool mac_entry_is_grat_arp_locked(const struct mac_entry *mac) { return time_now() < mac->grat_arp_lock; } /* MAC learning table. */ struct mac_learning { struct hmap table; /* Learning table. */ struct ovs_list lrus OVS_GUARDED; /* In-use entries, LRU at front. */ uint32_t secret; /* Secret for randomizing hash table. */ unsigned long *flood_vlans; /* Bitmap of learning disabled VLANs. */ unsigned int idle_time; /* Max age before deleting an entry. */ size_t max_entries; /* Max number of learned MACs. */ struct ovs_refcount ref_cnt; struct ovs_rwlock rwlock; bool need_revalidate; /* Fairness. * * Both of these data structures include the same "struct * mac_learning_port" but indexed differently. * * ports_by_usage is a per-port max-heap, in which the priority is the * number of MAC addresses for the port. When the MAC learning table * overflows, this allows us to evict a MAC entry from one of the ports * that have the largest number of MAC entries, achieving a form of * fairness. * * ports_by_ptr is a hash table indexed by the client-provided pointer. */ struct hmap ports_by_ptr; /* struct mac_learning_port hmap_nodes. */ struct heap ports_by_usage; /* struct mac_learning_port heap_nodes. */ }; int mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e) OVS_REQ_RDLOCK(ml->rwlock); /* Basics. */ struct mac_learning *mac_learning_create(unsigned int idle_time); struct mac_learning *mac_learning_ref(const struct mac_learning *); void mac_learning_unref(struct mac_learning *); bool mac_learning_run(struct mac_learning *ml) OVS_REQ_WRLOCK(ml->rwlock); void mac_learning_wait(struct mac_learning *ml) OVS_REQ_RDLOCK(ml->rwlock); /* Configuration. */ bool mac_learning_set_flood_vlans(struct mac_learning *ml, const unsigned long *bitmap) OVS_REQ_WRLOCK(ml->rwlock); void mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time) OVS_REQ_WRLOCK(ml->rwlock); void mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries) OVS_REQ_WRLOCK(ml->rwlock); /* Learning. */ bool mac_learning_may_learn(const struct mac_learning *ml, const struct eth_addr src_mac, uint16_t vlan) OVS_REQ_RDLOCK(ml->rwlock); struct mac_entry *mac_learning_insert(struct mac_learning *ml, const struct eth_addr src, uint16_t vlan) OVS_REQ_WRLOCK(ml->rwlock); /* Lookup. */ struct mac_entry *mac_learning_lookup(const struct mac_learning *ml, const struct eth_addr dst, uint16_t vlan) OVS_REQ_RDLOCK(ml->rwlock); /* Flushing. */ void mac_learning_expire(struct mac_learning *ml, struct mac_entry *e) OVS_REQ_WRLOCK(ml->rwlock); void mac_learning_flush(struct mac_learning *ml) OVS_REQ_WRLOCK(ml->rwlock); /* Inlines. */ static inline void * mac_entry_get_port(const struct mac_learning *ml OVS_UNUSED, const struct mac_entry *e) OVS_REQ_RDLOCK(ml->rwlock) { return e->mlport ? e->mlport->port : NULL; } #endif /* mac-learning.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ofp-parse.c0000644000000000000000000000013213534540071016452 xustar0030 mtime=1567801401.521682051 30 atime=1567801402.085686193 30 ctime=1567801424.797853544 openvswitch-2.5.9/lib/ofp-parse.c0000644000175000017500000015773513534540071020162 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofp-parse.h" #include #include #include #include #include "byte-order.h" #include "dynamic-string.h" #include "learn.h" #include "meta-flow.h" #include "multipath.h" #include "netdev.h" #include "nx-match.h" #include "ofp-actions.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "ovs-thread.h" #include "packets.h" #include "simap.h" #include "socket-util.h" #include "openvswitch/vconn.h" /* Parses 'str' as an 8-bit unsigned integer into '*valuep'. * * 'name' describes the value parsed in an error message, if any. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_u8(const char *str, const char *name, uint8_t *valuep) { int value; if (!str_to_int(str, 0, &value) || value < 0 || value > 255) { return xasprintf("invalid %s \"%s\"", name, str); } *valuep = value; return NULL; } /* Parses 'str' as a 16-bit unsigned integer into '*valuep'. * * 'name' describes the value parsed in an error message, if any. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_u16(const char *str, const char *name, uint16_t *valuep) { int value; if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) { return xasprintf("invalid %s \"%s\"", name, str); } *valuep = value; return NULL; } /* Parses 'str' as a 32-bit unsigned integer into '*valuep'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_u32(const char *str, uint32_t *valuep) { char *tail; uint32_t value; if (!str[0]) { return xstrdup("missing required numeric argument"); } errno = 0; value = strtoul(str, &tail, 0); if (errno == EINVAL || errno == ERANGE || *tail) { return xasprintf("invalid numeric format %s", str); } *valuep = value; return NULL; } /* Parses 'str' as an 64-bit unsigned integer into '*valuep'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_u64(const char *str, uint64_t *valuep) { char *tail; uint64_t value; if (!str[0]) { return xstrdup("missing required numeric argument"); } errno = 0; value = strtoull(str, &tail, 0); if (errno == EINVAL || errno == ERANGE || *tail) { return xasprintf("invalid numeric format %s", str); } *valuep = value; return NULL; } /* Parses 'str' as an 64-bit unsigned integer in network byte order into * '*valuep'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_be64(const char *str, ovs_be64 *valuep) { uint64_t value = 0; char *error; error = str_to_u64(str, &value); if (!error) { *valuep = htonll(value); } return error; } /* Parses 'str' as an Ethernet address into 'mac'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_mac(const char *str, struct eth_addr *mac) { if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*mac))) { return xasprintf("invalid mac address %s", str); } return NULL; } /* Parses 'str' as an IP address into '*ip'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_ip(const char *str, ovs_be32 *ip) { struct in_addr in_addr; if (lookup_ip(str, &in_addr)) { return xasprintf("%s: could not convert to IP address", str); } *ip = in_addr.s_addr; return NULL; } /* Parses 'str' as a conntrack helper into 'alg'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT str_to_connhelper(const char *str, uint16_t *alg) { if (!strcmp(str, "ftp")) { *alg = IPPORT_FTP; return NULL; } return xasprintf("invalid conntrack helper \"%s\"", str); } struct protocol { const char *name; uint16_t dl_type; uint8_t nw_proto; }; static bool parse_protocol(const char *name, const struct protocol **p_out) { static const struct protocol protocols[] = { { "ip", ETH_TYPE_IP, 0 }, { "ipv4", ETH_TYPE_IP, 0 }, { "ip4", ETH_TYPE_IP, 0 }, { "arp", ETH_TYPE_ARP, 0 }, { "icmp", ETH_TYPE_IP, IPPROTO_ICMP }, { "tcp", ETH_TYPE_IP, IPPROTO_TCP }, { "udp", ETH_TYPE_IP, IPPROTO_UDP }, { "sctp", ETH_TYPE_IP, IPPROTO_SCTP }, { "ipv6", ETH_TYPE_IPV6, 0 }, { "ip6", ETH_TYPE_IPV6, 0 }, { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 }, { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP }, { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP }, { "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP }, { "rarp", ETH_TYPE_RARP, 0}, { "mpls", ETH_TYPE_MPLS, 0 }, { "mplsm", ETH_TYPE_MPLS_MCAST, 0 }, }; const struct protocol *p; for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) { if (!strcmp(p->name, name)) { *p_out = p; return true; } } *p_out = NULL; return false; } /* Parses 's' as the (possibly masked) value of field 'mf', and updates * 'match' appropriately. Restricts the set of usable protocols to ones * supporting the parsed field. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT parse_field(const struct mf_field *mf, const char *s, struct match *match, enum ofputil_protocol *usable_protocols) { union mf_value value, mask; char *error; if (!*s) { /* If there's no string, we're just trying to match on the * existence of the field, so use a no-op value. */ s = "0/0"; } error = mf_parse(mf, s, &value, &mask); if (!error) { *usable_protocols &= mf_set(mf, &value, &mask, match, &error); } return error; } static char * extract_actions(char *s) { s = strstr(s, "action"); if (s) { *s = '\0'; s = strchr(s + 1, '='); return s ? s + 1 : NULL; } else { return NULL; } } static char * OVS_WARN_UNUSED_RESULT parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string, enum ofputil_protocol *usable_protocols) { enum { F_OUT_PORT = 1 << 0, F_ACTIONS = 1 << 1, F_IMPORTANCE = 1 << 2, F_TIMEOUT = 1 << 3, F_PRIORITY = 1 << 4, F_FLAGS = 1 << 5, } fields; char *act_str = NULL; char *name, *value; *usable_protocols = OFPUTIL_P_ANY; if (command == -2) { size_t len; string += strspn(string, " \t\r\n"); /* Skip white space. */ len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */ if (!strncmp(string, "add", len)) { command = OFPFC_ADD; } else if (!strncmp(string, "delete", len)) { command = OFPFC_DELETE; } else if (!strncmp(string, "delete_strict", len)) { command = OFPFC_DELETE_STRICT; } else if (!strncmp(string, "modify", len)) { command = OFPFC_MODIFY; } else if (!strncmp(string, "modify_strict", len)) { command = OFPFC_MODIFY_STRICT; } else { len = 0; command = OFPFC_ADD; } string += len; } switch (command) { case -1: fields = F_OUT_PORT; break; case OFPFC_ADD: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS | F_IMPORTANCE; break; case OFPFC_DELETE: fields = F_OUT_PORT; break; case OFPFC_DELETE_STRICT: fields = F_OUT_PORT | F_PRIORITY; break; case OFPFC_MODIFY: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; break; case OFPFC_MODIFY_STRICT: fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS; break; default: OVS_NOT_REACHED(); } match_init_catchall(&fm->match); fm->priority = OFP_DEFAULT_PRIORITY; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { /* For modify, by default, don't update the cookie. */ fm->new_cookie = OVS_BE64_MAX; } else{ fm->new_cookie = htonll(0); } fm->modify_cookie = false; fm->table_id = 0xff; fm->command = command; fm->idle_timeout = OFP_FLOW_PERMANENT; fm->hard_timeout = OFP_FLOW_PERMANENT; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_ANY; fm->flags = 0; fm->importance = 0; fm->out_group = OFPG_ANY; fm->delete_reason = OFPRR_DELETE; if (fields & F_ACTIONS) { act_str = extract_actions(string); if (!act_str) { return xstrdup("must specify an action"); } } while (ofputil_parse_key_value(&string, &name, &value)) { const struct protocol *p; char *error = NULL; if (parse_protocol(name, &p)) { match_set_dl_type(&fm->match, htons(p->dl_type)); if (p->nw_proto) { match_set_nw_proto(&fm->match, p->nw_proto); } } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) { fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) { fm->flags |= OFPUTIL_FF_CHECK_OVERLAP; } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) { fm->flags |= OFPUTIL_FF_RESET_COUNTS; *usable_protocols &= OFPUTIL_P_OF12_UP; } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) { fm->flags |= OFPUTIL_FF_NO_PKT_COUNTS; *usable_protocols &= OFPUTIL_P_OF13_UP; } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) { fm->flags |= OFPUTIL_FF_NO_BYT_COUNTS; *usable_protocols &= OFPUTIL_P_OF13_UP; } else if (!strcmp(name, "no_readonly_table") || !strcmp(name, "allow_hidden_fields")) { /* ignore these fields. */ } else if (mf_from_name(name)) { error = parse_field(mf_from_name(name), value, &fm->match, usable_protocols); } else { if (!*value) { return xasprintf("field %s missing value", name); } if (!strcmp(name, "table")) { error = str_to_u8(value, "table", &fm->table_id); if (fm->table_id != 0xff) { *usable_protocols &= OFPUTIL_P_TID; } } else if (fields & F_OUT_PORT && !strcmp(name, "out_port")) { if (!ofputil_port_from_string(value, &fm->out_port)) { error = xasprintf("%s is not a valid OpenFlow port", value); } } else if (fields & F_OUT_PORT && !strcmp(name, "out_group")) { *usable_protocols &= OFPUTIL_P_OF11_UP; if (!ofputil_group_from_string(value, &fm->out_group)) { error = xasprintf("%s is not a valid OpenFlow group", value); } } else if (fields & F_PRIORITY && !strcmp(name, "priority")) { uint16_t priority = 0; error = str_to_u16(value, name, &priority); fm->priority = priority; } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) { error = str_to_u16(value, name, &fm->idle_timeout); } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) { error = str_to_u16(value, name, &fm->hard_timeout); } else if (fields & F_IMPORTANCE && !strcmp(name, "importance")) { error = str_to_u16(value, name, &fm->importance); } else if (!strcmp(name, "cookie")) { char *mask = strchr(value, '/'); if (mask) { /* A mask means we're searching for a cookie. */ if (command == OFPFC_ADD) { return xstrdup("flow additions cannot use " "a cookie mask"); } *mask = '\0'; error = str_to_be64(value, &fm->cookie); if (error) { return error; } error = str_to_be64(mask + 1, &fm->cookie_mask); /* Matching of the cookie is only supported through NXM or * OF1.1+. */ if (fm->cookie_mask != htonll(0)) { *usable_protocols &= OFPUTIL_P_NXM_OF11_UP; } } else { /* No mask means that the cookie is being set. */ if (command != OFPFC_ADD && command != OFPFC_MODIFY && command != OFPFC_MODIFY_STRICT) { return xstrdup("cannot set cookie"); } error = str_to_be64(value, &fm->new_cookie); fm->modify_cookie = true; } } else if (!strcmp(name, "duration") || !strcmp(name, "n_packets") || !strcmp(name, "n_bytes") || !strcmp(name, "idle_age") || !strcmp(name, "hard_age")) { /* Ignore these, so that users can feed the output of * "ovs-ofctl dump-flows" back into commands that parse * flows. */ } else { error = xasprintf("unknown keyword %s", name); } } if (error) { return error; } } /* Check for usable protocol interdependencies between match fields. */ if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) { const struct flow_wildcards *wc = &fm->match.wc; /* Only NXM and OXM support matching L3 and L4 fields within IPv6. * * (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and * nw_ttl are covered elsewhere so they don't need to be included in * this test too.) */ if (wc->masks.nw_proto || wc->masks.nw_tos || wc->masks.tp_src || wc->masks.tp_dst) { *usable_protocols &= OFPUTIL_P_NXM_OXM_ANY; } } if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) { /* On modifies without a mask, we are supposed to add a flow if * one does not exist. If a cookie wasn't been specified, use a * default of zero. */ fm->new_cookie = htonll(0); } if (fields & F_ACTIONS) { enum ofputil_protocol action_usable_protocols; struct ofpbuf ofpacts; char *error; ofpbuf_init(&ofpacts, 32); error = ofpacts_parse_instructions(act_str, &ofpacts, &action_usable_protocols); *usable_protocols &= action_usable_protocols; if (!error) { enum ofperr err; err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow, OFPP_MAX, fm->table_id, 255, usable_protocols); if (!err && !*usable_protocols) { err = OFPERR_OFPBAC_MATCH_INCONSISTENT; } if (err) { error = xasprintf("actions are invalid with specified match " "(%s)", ofperr_to_string(err)); } } if (error) { ofpbuf_uninit(&ofpacts); return error; } fm->ofpacts_len = ofpacts.size; fm->ofpacts = ofpbuf_steal_data(&ofpacts); } else { fm->ofpacts_len = 0; fm->ofpacts = NULL; } return NULL; } /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man * page) into 'fm' for sending the specified flow_mod 'command' to a switch. * Returns the set of usable protocols in '*usable_protocols'. * * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_* * constant for 'command'. To parse syntax for an OFPST_FLOW or * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. * * If 'command' is given as -2, 'str_' may begin with a command name ("add", * "modify", "delete", "modify_strict", or "delete_strict"). A missing command * name is treated as "add". * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, enum ofputil_protocol *usable_protocols) { char *string = xstrdup(str_); char *error; error = parse_ofp_str__(fm, command, string, usable_protocols); if (error) { fm->ofpacts = NULL; fm->ofpacts_len = 0; } free(string); return error; } static char * OVS_WARN_UNUSED_RESULT parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string, struct ofpbuf *bands, int command, enum ofputil_protocol *usable_protocols) { enum { F_METER = 1 << 0, F_FLAGS = 1 << 1, F_BANDS = 1 << 2, } fields; char *save_ptr = NULL; char *band_str = NULL; char *name; /* Meters require at least OF 1.3. */ *usable_protocols = OFPUTIL_P_OF13_UP; switch (command) { case -1: fields = F_METER; break; case OFPMC13_ADD: fields = F_METER | F_FLAGS | F_BANDS; break; case OFPMC13_DELETE: fields = F_METER; break; case OFPMC13_MODIFY: fields = F_METER | F_FLAGS | F_BANDS; break; default: OVS_NOT_REACHED(); } mm->command = command; mm->meter.meter_id = 0; mm->meter.flags = 0; if (fields & F_BANDS) { band_str = strstr(string, "band"); if (!band_str) { return xstrdup("must specify bands"); } *band_str = '\0'; band_str = strchr(band_str + 1, '='); if (!band_str) { return xstrdup("must specify bands"); } band_str++; } for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { if (fields & F_FLAGS && !strcmp(name, "kbps")) { mm->meter.flags |= OFPMF13_KBPS; } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { mm->meter.flags |= OFPMF13_PKTPS; } else if (fields & F_FLAGS && !strcmp(name, "burst")) { mm->meter.flags |= OFPMF13_BURST; } else if (fields & F_FLAGS && !strcmp(name, "stats")) { mm->meter.flags |= OFPMF13_STATS; } else { char *value; value = strtok_r(NULL, ", \t\r\n", &save_ptr); if (!value) { return xasprintf("field %s missing value", name); } if (!strcmp(name, "meter")) { if (!strcmp(value, "all")) { mm->meter.meter_id = OFPM13_ALL; } else if (!strcmp(value, "controller")) { mm->meter.meter_id = OFPM13_CONTROLLER; } else if (!strcmp(value, "slowpath")) { mm->meter.meter_id = OFPM13_SLOWPATH; } else { char *error = str_to_u32(value, &mm->meter.meter_id); if (error) { return error; } if (mm->meter.meter_id > OFPM13_MAX || !mm->meter.meter_id) { return xasprintf("invalid value for %s", name); } } } else { return xasprintf("unknown keyword %s", name); } } } if (fields & F_METER && !mm->meter.meter_id) { return xstrdup("must specify 'meter'"); } if (fields & F_FLAGS && !mm->meter.flags) { return xstrdup("meter must specify either 'kbps' or 'pktps'"); } if (fields & F_BANDS) { uint16_t n_bands = 0; struct ofputil_meter_band *band = NULL; int i; for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { char *value; value = strtok_r(NULL, ", \t\r\n", &save_ptr); if (!value) { return xasprintf("field %s missing value", name); } if (!strcmp(name, "type")) { /* Start a new band */ band = ofpbuf_put_zeros(bands, sizeof *band); n_bands++; if (!strcmp(value, "drop")) { band->type = OFPMBT13_DROP; } else if (!strcmp(value, "dscp_remark")) { band->type = OFPMBT13_DSCP_REMARK; } else { return xasprintf("field %s unknown value %s", name, value); } } else if (!band || !band->type) { return xstrdup("band must start with the 'type' keyword"); } else if (!strcmp(name, "rate")) { char *error = str_to_u32(value, &band->rate); if (error) { return error; } } else if (!strcmp(name, "burst_size")) { char *error = str_to_u32(value, &band->burst_size); if (error) { return error; } } else if (!strcmp(name, "prec_level")) { char *error = str_to_u8(value, name, &band->prec_level); if (error) { return error; } } else { return xasprintf("unknown keyword %s", name); } } /* validate bands */ if (!n_bands) { return xstrdup("meter must have bands"); } mm->meter.n_bands = n_bands; mm->meter.bands = ofpbuf_steal_data(bands); for (i = 0; i < n_bands; ++i) { band = &mm->meter.bands[i]; if (!band->type) { return xstrdup("band must have 'type'"); } if (band->type == OFPMBT13_DSCP_REMARK) { if (!band->prec_level) { return xstrdup("'dscp_remark' band must have" " 'prec_level'"); } } else { if (band->prec_level) { return xstrdup("Only 'dscp_remark' band may have" " 'prec_level'"); } } if (!band->rate) { return xstrdup("band must have 'rate'"); } if (mm->meter.flags & OFPMF13_BURST) { if (!band->burst_size) { return xstrdup("band must have 'burst_size' " "when 'burst' flag is set"); } } else { if (band->burst_size) { return xstrdup("band may have 'burst_size' only " "when 'burst' flag is set"); } } } } else { mm->meter.n_bands = 0; mm->meter.bands = NULL; } return NULL; } /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man * page) into 'mm' for sending the specified meter_mod 'command' to a switch. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, int command, enum ofputil_protocol *usable_protocols) { struct ofpbuf bands; char *string; char *error; ofpbuf_init(&bands, 64); string = xstrdup(str_); error = parse_ofp_meter_mod_str__(mm, string, &bands, command, usable_protocols); free(string); ofpbuf_uninit(&bands); return error; } static char * OVS_WARN_UNUSED_RESULT parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr, const char *str_, char *string, enum ofputil_protocol *usable_protocols) { static atomic_count id = ATOMIC_COUNT_INIT(0); char *name, *value; fmr->id = atomic_count_inc(&id); fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY | NXFMF_OWN | NXFMF_ACTIONS); fmr->out_port = OFPP_NONE; fmr->table_id = 0xff; match_init_catchall(&fmr->match); while (ofputil_parse_key_value(&string, &name, &value)) { const struct protocol *p; char *error = NULL; if (!strcmp(name, "!initial")) { fmr->flags &= ~NXFMF_INITIAL; } else if (!strcmp(name, "!add")) { fmr->flags &= ~NXFMF_ADD; } else if (!strcmp(name, "!delete")) { fmr->flags &= ~NXFMF_DELETE; } else if (!strcmp(name, "!modify")) { fmr->flags &= ~NXFMF_MODIFY; } else if (!strcmp(name, "!actions")) { fmr->flags &= ~NXFMF_ACTIONS; } else if (!strcmp(name, "!own")) { fmr->flags &= ~NXFMF_OWN; } else if (parse_protocol(name, &p)) { match_set_dl_type(&fmr->match, htons(p->dl_type)); if (p->nw_proto) { match_set_nw_proto(&fmr->match, p->nw_proto); } } else if (mf_from_name(name)) { error = parse_field(mf_from_name(name), value, &fmr->match, usable_protocols); } else { if (!*value) { return xasprintf("%s: field %s missing value", str_, name); } if (!strcmp(name, "table")) { error = str_to_u8(value, "table", &fmr->table_id); } else if (!strcmp(name, "out_port")) { fmr->out_port = u16_to_ofp(atoi(value)); } else { return xasprintf("%s: unknown keyword %s", str_, name); } } if (error) { return error; } } return NULL; } /* Convert 'str_' (as described in the documentation for the "monitor" command * in the ovs-ofctl man page) into 'fmr'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr, const char *str_, enum ofputil_protocol *usable_protocols) { char *string = xstrdup(str_); char *error = parse_flow_monitor_request__(fmr, str_, string, usable_protocols); free(string); return error; } /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command' * (one of OFPFC_*) into 'fm'. * * If 'command' is given as -2, 'string' may begin with a command name ("add", * "modify", "delete", "modify_strict", or "delete_strict"). A missing command * name is treated as "add". * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string, int command, enum ofputil_protocol *usable_protocols) { char *error = parse_ofp_str(fm, command, string, usable_protocols); if (!error) { /* Normalize a copy of the match. This ensures that non-normalized * flows get logged but doesn't affect what gets sent to the switch, so * that the switch can do whatever it likes with the flow. */ struct match match_copy = fm->match; ofputil_normalize_match(&match_copy); } return error; } /* Convert 'setting' (as described for the "mod-table" command * in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and * 'tm->table_vacancy->vacancy_down' threshold values. * For the two threshold values, value of vacancy_up is always greater * than value of vacancy_down. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting) { char *save_ptr = NULL; char *vac_up, *vac_down; char *value = xstrdup(setting); char *ret_msg; int vacancy_up, vacancy_down; strtok_r(value, ":", &save_ptr); vac_down = strtok_r(NULL, ",", &save_ptr); if (!vac_down) { ret_msg = xasprintf("Vacancy down value missing"); goto exit; } if (!str_to_int(vac_down, 0, &vacancy_down) || vacancy_down < 0 || vacancy_down > 100) { ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down); goto exit; } vac_up = strtok_r(NULL, ",", &save_ptr); if (!vac_up) { ret_msg = xasprintf("Vacancy up value missing"); goto exit; } if (!str_to_int(vac_up, 0, &vacancy_up) || vacancy_up < 0 || vacancy_up > 100) { ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up); goto exit; } if (vacancy_down > vacancy_up) { ret_msg = xasprintf("Invalid vacancy range, vacancy up should be " "greater than vacancy down (%s)", ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE)); goto exit; } free(value); tm->table_vacancy.vacancy_down = vacancy_down; tm->table_vacancy.vacancy_up = vacancy_up; return NULL; exit: free(value); return ret_msg; } /* Convert 'table_id' and 'setting' (as described for the "mod-table" command * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a * switch. * * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into * '*usable_versions'. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id, const char *setting, uint32_t *usable_versions) { *usable_versions = 0; if (!strcasecmp(table_id, "all")) { tm->table_id = OFPTT_ALL; } else { char *error = str_to_u8(table_id, "table_id", &tm->table_id); if (error) { return error; } } tm->miss = OFPUTIL_TABLE_MISS_DEFAULT; tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT; tm->eviction_flags = UINT32_MAX; tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT; tm->table_vacancy.vacancy_down = 0; tm->table_vacancy.vacancy_up = 0; tm->table_vacancy.vacancy = 0; /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod. * Only OpenFlow 1.4+ can configure eviction and vacancy events * via table_mod. */ if (!strcmp(setting, "controller")) { tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER; *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); } else if (!strcmp(setting, "continue")) { tm->miss = OFPUTIL_TABLE_MISS_CONTINUE; *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); } else if (!strcmp(setting, "drop")) { tm->miss = OFPUTIL_TABLE_MISS_DROP; *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION); } else if (!strcmp(setting, "evict")) { tm->eviction = OFPUTIL_TABLE_EVICTION_ON; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else if (!strcmp(setting, "noevict")) { tm->eviction = OFPUTIL_TABLE_EVICTION_OFF; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) { tm->vacancy = OFPUTIL_TABLE_VACANCY_ON; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); char *error = parse_ofp_table_vacancy(tm, setting); if (error) { return error; } } else if (!strcmp(setting, "novacancy")) { tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF; *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION); } else { return xasprintf("invalid table_mod setting %s", setting); } if (tm->table_id == 0xfe && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) { return xstrdup("last table's flow miss handling can not be continue"); } return NULL; } /* Opens file 'file_name' and reads each line as a flow_mod of the specified * type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated * on the caller's behalf, and the number of flow_mods in '*n_fms'. * * If 'command' is given as -2, each line may start with a command name * ("add", "modify", "delete", "modify_strict", or "delete_strict"). A missing * command name is treated as "add". * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT parse_ofp_flow_mod_file(const char *file_name, int command, struct ofputil_flow_mod **fms, size_t *n_fms, enum ofputil_protocol *usable_protocols) { size_t allocated_fms; int line_number; FILE *stream; struct ds s; *usable_protocols = OFPUTIL_P_ANY; *fms = NULL; *n_fms = 0; stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); if (stream == NULL) { return xasprintf("%s: open failed (%s)", file_name, ovs_strerror(errno)); } allocated_fms = *n_fms; ds_init(&s); line_number = 0; while (!ds_get_preprocessed_line(&s, stream, &line_number)) { char *error; enum ofputil_protocol usable; if (*n_fms >= allocated_fms) { *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms); } error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command, &usable); if (error) { char *err_msg; size_t i; for (i = 0; i < *n_fms; i++) { free(CONST_CAST(struct ofpact *, (*fms)[i].ofpacts)); } free(*fms); *fms = NULL; *n_fms = 0; ds_destroy(&s); if (stream != stdin) { fclose(stream); } err_msg = xasprintf("%s:%d: %s", file_name, line_number, error); free(error); return err_msg; } *usable_protocols &= usable; /* Each line can narrow the set. */ *n_fms += 1; } ds_destroy(&s); if (stream != stdin) { fclose(stream); } return NULL; } char * OVS_WARN_UNUSED_RESULT parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, bool aggregate, const char *string, enum ofputil_protocol *usable_protocols) { struct ofputil_flow_mod fm; char *error; error = parse_ofp_str(&fm, -1, string, usable_protocols); if (error) { return error; } /* Special table ID support not required for stats requests. */ if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) { *usable_protocols |= OFPUTIL_P_OF10_STD; } if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) { *usable_protocols |= OFPUTIL_P_OF10_NXM; } fsr->aggregate = aggregate; fsr->cookie = fm.cookie; fsr->cookie_mask = fm.cookie_mask; fsr->match = fm.match; fsr->out_port = fm.out_port; fsr->out_group = fm.out_group; fsr->table_id = fm.table_id; return NULL; } /* Parses a specification of a flow from 's' into 'flow'. 's' must take the * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a * mf_field. Fields must be specified in a natural order for satisfying * prerequisites. If 'mask' is specified, fills the mask field for each of the * field specified in flow. If the map, 'names_portno' is specfied, converts * the in_port name into port no while setting the 'flow'. * * Returns NULL on success, otherwise a malloc()'d string that explains the * problem. */ char * parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, const struct simap *portno_names) { char *pos, *key, *value_s; char *error = NULL; char *copy; memset(flow, 0, sizeof *flow); if (mask) { memset(mask, 0, sizeof *mask); } pos = copy = xstrdup(s); while (ofputil_parse_key_value(&pos, &key, &value_s)) { const struct protocol *p; if (parse_protocol(key, &p)) { if (flow->dl_type) { error = xasprintf("%s: Ethernet type set multiple times", s); goto exit; } flow->dl_type = htons(p->dl_type); if (mask) { mask->dl_type = OVS_BE16_MAX; } if (p->nw_proto) { if (flow->nw_proto) { error = xasprintf("%s: network protocol set " "multiple times", s); goto exit; } flow->nw_proto = p->nw_proto; if (mask) { mask->nw_proto = UINT8_MAX; } } } else { const struct mf_field *mf; union mf_value value; char *field_error; mf = mf_from_name(key); if (!mf) { error = xasprintf("%s: unknown field %s", s, key); goto exit; } if (!mf_are_prereqs_ok(mf, flow)) { error = xasprintf("%s: prerequisites not met for setting %s", s, key); goto exit; } if (mf_is_set(mf, flow)) { error = xasprintf("%s: field %s set multiple times", s, key); goto exit; } if (!strcmp(key, "in_port") && portno_names && simap_contains(portno_names, value_s)) { flow->in_port.ofp_port = u16_to_ofp( simap_get(portno_names, value_s)); if (mask) { mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX)); } } else { field_error = mf_parse_value(mf, value_s, &value); if (field_error) { error = xasprintf("%s: bad value for %s (%s)", s, key, field_error); free(field_error); goto exit; } mf_set_flow_value(mf, &value, flow); if (mask) { mf_mask_field(mf, mask); } } } } if (!flow->in_port.ofp_port) { flow->in_port.ofp_port = OFPP_NONE; } exit: free(copy); if (error) { memset(flow, 0, sizeof *flow); if (mask) { memset(mask, 0, sizeof *mask); } } return error; } static char * OVS_WARN_UNUSED_RESULT parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type, enum ofputil_protocol *usable_protocols) { char *pos, *key, *value; struct ofpbuf ofpacts; struct ds actions; char *error; bucket->weight = group_type == OFPGT11_SELECT ? 1 : 0; bucket->bucket_id = OFPG15_BUCKET_ALL; bucket->watch_port = OFPP_ANY; bucket->watch_group = OFPG_ANY; ds_init(&actions); pos = str_; error = NULL; while (ofputil_parse_key_value(&pos, &key, &value)) { if (!strcasecmp(key, "weight")) { error = str_to_u16(value, "weight", &bucket->weight); } else if (!strcasecmp(key, "watch_port")) { if (!ofputil_port_from_string(value, &bucket->watch_port) || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX) && bucket->watch_port != OFPP_ANY)) { error = xasprintf("%s: invalid watch_port", value); } } else if (!strcasecmp(key, "watch_group")) { error = str_to_u32(value, &bucket->watch_group); if (!error && bucket->watch_group > OFPG_MAX) { error = xasprintf("invalid watch_group id %"PRIu32, bucket->watch_group); } } else if (!strcasecmp(key, "bucket_id")) { error = str_to_u32(value, &bucket->bucket_id); if (!error && bucket->bucket_id > OFPG15_BUCKET_MAX) { error = xasprintf("invalid bucket_id id %"PRIu32, bucket->bucket_id); } *usable_protocols &= OFPUTIL_P_OF15_UP; } else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) { ds_put_format(&actions, "%s,", value); } else { ds_put_format(&actions, "%s(%s),", key, value); } if (error) { ds_destroy(&actions); return error; } } if (!actions.length) { return xstrdup("bucket must specify actions"); } ds_chomp(&actions, ','); ofpbuf_init(&ofpacts, 0); error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts, usable_protocols); ds_destroy(&actions); if (error) { ofpbuf_uninit(&ofpacts); return error; } bucket->ofpacts = ofpacts.data; bucket->ofpacts_len = ofpacts.size; return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_select_group_field(char *s, struct field_array *fa, enum ofputil_protocol *usable_protocols) { char *name, *value_str; while (ofputil_parse_key_value(&s, &name, &value_str)) { const struct mf_field *mf = mf_from_name(name); if (mf) { char *error; union mf_value value; if (bitmap_is_set(fa->used.bm, mf->id)) { return xasprintf("%s: duplicate field", name); } if (*value_str) { error = mf_parse_value(mf, value_str, &value); if (error) { return error; } /* The mask cannot be all-zeros */ if (!mf_is_tun_metadata(mf) && is_all_zeros(&value, mf->n_bytes)) { return xasprintf("%s: values are wildcards here " "and must not be all-zeros", s); } /* The values parsed are masks for fields used * by the selection method */ if (!mf_is_mask_valid(mf, &value)) { return xasprintf("%s: invalid mask for field %s", value_str, mf->name); } } else { memset(&value, 0xff, mf->n_bytes); } field_array_set(mf->id, &value, fa); if (is_all_ones(&value, mf->n_bytes)) { *usable_protocols &= mf->usable_protocols_exact; } else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr || ip_is_cidr(value.be32)) { *usable_protocols &= mf->usable_protocols_cidr; } else { *usable_protocols &= mf->usable_protocols_bitwise; } } else { return xasprintf("%s: unknown field %s", s, name); } } return NULL; } static char * OVS_WARN_UNUSED_RESULT parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command, char *string, enum ofputil_protocol *usable_protocols) { enum { F_GROUP_TYPE = 1 << 0, F_BUCKETS = 1 << 1, F_COMMAND_BUCKET_ID = 1 << 2, F_COMMAND_BUCKET_ID_ALL = 1 << 3, } fields; bool had_type = false; bool had_command_bucket_id = false; struct ofputil_bucket *bucket; char *error = NULL; *usable_protocols = OFPUTIL_P_OF11_UP; switch (command) { case OFPGC11_ADD: fields = F_GROUP_TYPE | F_BUCKETS; break; case OFPGC11_DELETE: fields = 0; break; case OFPGC11_MODIFY: fields = F_GROUP_TYPE | F_BUCKETS; break; case OFPGC15_INSERT_BUCKET: fields = F_BUCKETS | F_COMMAND_BUCKET_ID; *usable_protocols &= OFPUTIL_P_OF15_UP; break; case OFPGC15_REMOVE_BUCKET: fields = F_COMMAND_BUCKET_ID | F_COMMAND_BUCKET_ID_ALL; *usable_protocols &= OFPUTIL_P_OF15_UP; break; default: OVS_NOT_REACHED(); } memset(gm, 0, sizeof *gm); gm->command = command; gm->group_id = OFPG_ANY; gm->command_bucket_id = OFPG15_BUCKET_ALL; list_init(&gm->buckets); if (command == OFPGC11_DELETE && string[0] == '\0') { gm->group_id = OFPG_ALL; return NULL; } *usable_protocols = OFPUTIL_P_OF11_UP; /* Strip the buckets off the end of 'string', if there are any, saving a * pointer for later. We want to parse the buckets last because the bucket * type influences bucket defaults. */ char *bkt_str = strstr(string, "bucket="); if (bkt_str) { if (!(fields & F_BUCKETS)) { error = xstrdup("bucket is not needed"); goto out; } *bkt_str = '\0'; } /* Parse everything before the buckets. */ char *pos = string; char *name, *value; while (ofputil_parse_key_value(&pos, &name, &value)) { if (!strcmp(name, "command_bucket_id")) { if (!(fields & F_COMMAND_BUCKET_ID)) { error = xstrdup("command bucket id is not needed"); goto out; } if (!strcmp(value, "all")) { gm->command_bucket_id = OFPG15_BUCKET_ALL; } else if (!strcmp(value, "first")) { gm->command_bucket_id = OFPG15_BUCKET_FIRST; } else if (!strcmp(value, "last")) { gm->command_bucket_id = OFPG15_BUCKET_LAST; } else { error = str_to_u32(value, &gm->command_bucket_id); if (error) { goto out; } if (gm->command_bucket_id > OFPG15_BUCKET_MAX && (gm->command_bucket_id != OFPG15_BUCKET_FIRST && gm->command_bucket_id != OFPG15_BUCKET_LAST && gm->command_bucket_id != OFPG15_BUCKET_ALL)) { error = xasprintf("invalid command bucket id %"PRIu32, gm->command_bucket_id); goto out; } } if (gm->command_bucket_id == OFPG15_BUCKET_ALL && !(fields & F_COMMAND_BUCKET_ID_ALL)) { error = xstrdup("command_bucket_id=all is not permitted"); goto out; } had_command_bucket_id = true; } else if (!strcmp(name, "group_id")) { if(!strcmp(value, "all")) { gm->group_id = OFPG_ALL; } else { error = str_to_u32(value, &gm->group_id); if (error) { goto out; } if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) { error = xasprintf("invalid group id %"PRIu32, gm->group_id); goto out; } } } else if (!strcmp(name, "type")){ if (!(fields & F_GROUP_TYPE)) { error = xstrdup("type is not needed"); goto out; } if (!strcmp(value, "all")) { gm->type = OFPGT11_ALL; } else if (!strcmp(value, "select")) { gm->type = OFPGT11_SELECT; } else if (!strcmp(value, "indirect")) { gm->type = OFPGT11_INDIRECT; } else if (!strcmp(value, "ff") || !strcmp(value, "fast_failover")) { gm->type = OFPGT11_FF; } else { error = xasprintf("invalid group type %s", value); goto out; } had_type = true; } else if (!strcmp(name, "selection_method")) { if (!(fields & F_GROUP_TYPE)) { error = xstrdup("selection method is not needed"); goto out; } if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) { error = xasprintf("selection method is longer than %u" " bytes long", NTR_MAX_SELECTION_METHOD_LEN - 1); goto out; } memset(gm->props.selection_method, '\0', NTR_MAX_SELECTION_METHOD_LEN); strcpy(gm->props.selection_method, value); *usable_protocols &= OFPUTIL_P_OF15_UP; } else if (!strcmp(name, "selection_method_param")) { if (!(fields & F_GROUP_TYPE)) { error = xstrdup("selection method param is not needed"); goto out; } error = str_to_u64(value, &gm->props.selection_method_param); if (error) { goto out; } *usable_protocols &= OFPUTIL_P_OF15_UP; } else if (!strcmp(name, "fields")) { if (!(fields & F_GROUP_TYPE)) { error = xstrdup("fields are not needed"); goto out; } error = parse_select_group_field(value, &gm->props.fields, usable_protocols); if (error) { goto out; } *usable_protocols &= OFPUTIL_P_OF15_UP; } else { error = xasprintf("unknown keyword %s", name); goto out; } } if (gm->group_id == OFPG_ANY) { error = xstrdup("must specify a group_id"); goto out; } if (fields & F_GROUP_TYPE && !had_type) { error = xstrdup("must specify a type"); goto out; } if (fields & F_COMMAND_BUCKET_ID) { if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) { error = xstrdup("must specify a command bucket id"); goto out; } } else if (had_command_bucket_id) { error = xstrdup("command bucket id is not needed"); goto out; } /* Now parse the buckets, if any. */ while (bkt_str) { char *next_bkt_str; bkt_str = strchr(bkt_str + 1, '='); if (!bkt_str) { error = xstrdup("must specify bucket content"); goto out; } bkt_str++; next_bkt_str = strstr(bkt_str, "bucket="); if (next_bkt_str) { *next_bkt_str = '\0'; } bucket = xzalloc(sizeof(struct ofputil_bucket)); error = parse_bucket_str(bucket, bkt_str, gm->type, usable_protocols); if (error) { free(bucket); goto out; } list_push_back(&gm->buckets, &bucket->list_node); if (gm->type != OFPGT11_SELECT && bucket->weight) { error = xstrdup("Only select groups can have bucket weights."); goto out; } bkt_str = next_bkt_str; } if (gm->type == OFPGT11_INDIRECT && !list_is_short(&gm->buckets)) { error = xstrdup("Indirect groups can have at most one bucket."); goto out; } return NULL; out: ofputil_bucket_list_destroy(&gm->buckets); return error; } char * OVS_WARN_UNUSED_RESULT parse_ofp_group_mod_str(struct ofputil_group_mod *gm, uint16_t command, const char *str_, enum ofputil_protocol *usable_protocols) { char *string = xstrdup(str_); char *error = parse_ofp_group_mod_str__(gm, command, string, usable_protocols); free(string); if (error) { ofputil_bucket_list_destroy(&gm->buckets); } return error; } char * OVS_WARN_UNUSED_RESULT parse_ofp_group_mod_file(const char *file_name, uint16_t command, struct ofputil_group_mod **gms, size_t *n_gms, enum ofputil_protocol *usable_protocols) { size_t allocated_gms; int line_number; FILE *stream; struct ds s; *gms = NULL; *n_gms = 0; stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r"); if (stream == NULL) { return xasprintf("%s: open failed (%s)", file_name, ovs_strerror(errno)); } allocated_gms = *n_gms; ds_init(&s); line_number = 0; *usable_protocols = OFPUTIL_P_OF11_UP; while (!ds_get_preprocessed_line(&s, stream, &line_number)) { enum ofputil_protocol usable; char *error; if (*n_gms >= allocated_gms) { struct ofputil_group_mod *new_gms; size_t i; new_gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms); for (i = 0; i < *n_gms; i++) { list_moved(&new_gms[i].buckets, &(*gms)[i].buckets); } *gms = new_gms; } error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s), &usable); if (error) { size_t i; for (i = 0; i < *n_gms; i++) { ofputil_bucket_list_destroy(&(*gms)[i].buckets); } free(*gms); *gms = NULL; *n_gms = 0; ds_destroy(&s); if (stream != stdin) { fclose(stream); } return xasprintf("%s:%d: %s", file_name, line_number, error); } *usable_protocols &= usable; *n_gms += 1; } ds_destroy(&s); if (stream != stdin) { fclose(stream); } return NULL; } char * OVS_WARN_UNUSED_RESULT parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm, uint16_t command, const char *s, enum ofputil_protocol *usable_protocols) { *usable_protocols = OFPUTIL_P_NXM_OXM_ANY; ttm->command = command; list_init(&ttm->mappings); while (*s) { struct ofputil_tlv_map *map = xmalloc(sizeof *map); int n; if (*s == ',') { s++; } list_push_back(&ttm->mappings, &map->list_node); if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n", &map->option_class, &map->option_type, &map->option_len, &map->index, &n)) { ofputil_uninit_tlv_table(&ttm->mappings); return xstrdup("invalid tlv mapping"); } s += n; } return NULL; } openvswitch-2.5.9/lib/PaxHeaders.82075/ssl.man0000644000000000000000000000013213534540071015710 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 30 ctime=1567801423.741845759 openvswitch-2.5.9/lib/ssl.man0000644000175000017500000000226313534540071017401 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .IP "\fB\-p\fR \fIprivkey.pem\fR" .IQ "\fB\-\-private\-key=\fIprivkey.pem\fR" Specifies a PEM file containing the private key used as \fB\*(PN\fR's identity for outgoing SSL connections. . .IP "\fB\-c\fR \fIcert.pem\fR" .IQ "\fB\-\-certificate=\fIcert.pem\fR" Specifies a PEM file containing a certificate that certifies the private key specified on \fB\-p\fR or \fB\-\-private\-key\fR to be trustworthy. The certificate must be signed by the certificate authority (CA) that the peer in SSL connections will use to verify it. . .IP "\fB\-C\fR \fIcacert.pem\fR" .IQ "\fB\-\-ca\-cert=\fIcacert.pem\fR" Specifies a PEM file containing the CA certificate that \fB\*(PN\fR should use to verify certificates presented to it by SSL peers. (This may be the same certificate that SSL peers use to verify the certificate specified on \fB\-c\fR or \fB\-\-certificate\fR, or it may be a different one, depending on the PKI design in use.) . .IP "\fB\-C none\fR" .IQ "\fB\-\-ca\-cert=none\fR" Disables verification of certificates presented by SSL peers. This introduces a security risk, because it means that certificates cannot be verified to be those of known trusted hosts. openvswitch-2.5.9/lib/PaxHeaders.82075/dpif-netlink.c0000644000000000000000000000013213534540071017142 xustar0030 mtime=1567801401.377680993 30 atime=1567801402.073686105 30 ctime=1567801424.973854841 openvswitch-2.5.9/lib/dpif-netlink.c0000644000175000017500000026566513534540071020654 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008-2015, 2017 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dpif-netlink.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bitmap.h" #include "dpif-provider.h" #include "dynamic-string.h" #include "flow.h" #include "fat-rwlock.h" #include "netdev.h" #include "netdev-linux.h" #include "netdev-vport.h" #include "netlink-conntrack.h" #include "netlink-notifier.h" #include "netlink-socket.h" #include "netlink.h" #include "odp-util.h" #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" #include "random.h" #include "shash.h" #include "sset.h" #include "timeval.h" #include "unaligned.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(dpif_netlink); #ifdef _WIN32 enum { WINDOWS = 1 }; #else enum { WINDOWS = 0 }; #endif enum { MAX_PORTS = USHRT_MAX }; /* This ethtool flag was introduced in Linux 2.6.24, so it might be * missing if we have old headers. */ #define ETH_FLAG_LRO (1 << 15) /* LRO is enabled */ struct dpif_netlink_dp { /* Generic Netlink header. */ uint8_t cmd; /* struct ovs_header. */ int dp_ifindex; /* Attributes. */ const char *name; /* OVS_DP_ATTR_NAME. */ const uint32_t *upcall_pid; /* OVS_DP_ATTR_UPCALL_PID. */ uint32_t user_features; /* OVS_DP_ATTR_USER_FEATURES */ const struct ovs_dp_stats *stats; /* OVS_DP_ATTR_STATS. */ const struct ovs_dp_megaflow_stats *megaflow_stats; /* OVS_DP_ATTR_MEGAFLOW_STATS.*/ }; static void dpif_netlink_dp_init(struct dpif_netlink_dp *); static int dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *, const struct ofpbuf *); static void dpif_netlink_dp_dump_start(struct nl_dump *); static int dpif_netlink_dp_transact(const struct dpif_netlink_dp *request, struct dpif_netlink_dp *reply, struct ofpbuf **bufp); static int dpif_netlink_dp_get(const struct dpif *, struct dpif_netlink_dp *reply, struct ofpbuf **bufp); struct dpif_netlink_flow { /* Generic Netlink header. */ uint8_t cmd; /* struct ovs_header. */ unsigned int nlmsg_flags; int dp_ifindex; /* Attributes. * * The 'stats' member points to 64-bit data that might only be aligned on * 32-bit boundaries, so get_unaligned_u64() should be used to access its * values. * * If 'actions' is nonnull then OVS_FLOW_ATTR_ACTIONS will be included in * the Netlink version of the command, even if actions_len is zero. */ const struct nlattr *key; /* OVS_FLOW_ATTR_KEY. */ size_t key_len; const struct nlattr *mask; /* OVS_FLOW_ATTR_MASK. */ size_t mask_len; const struct nlattr *actions; /* OVS_FLOW_ATTR_ACTIONS. */ size_t actions_len; ovs_u128 ufid; /* OVS_FLOW_ATTR_FLOW_ID. */ bool ufid_present; /* Is there a UFID? */ bool ufid_terse; /* Skip serializing key/mask/acts? */ const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */ const uint8_t *tcp_flags; /* OVS_FLOW_ATTR_TCP_FLAGS. */ const ovs_32aligned_u64 *used; /* OVS_FLOW_ATTR_USED. */ bool clear; /* OVS_FLOW_ATTR_CLEAR. */ bool probe; /* OVS_FLOW_ATTR_PROBE. */ }; static void dpif_netlink_flow_init(struct dpif_netlink_flow *); static int dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *, const struct ofpbuf *); static void dpif_netlink_flow_to_ofpbuf(const struct dpif_netlink_flow *, struct ofpbuf *); static int dpif_netlink_flow_transact(struct dpif_netlink_flow *request, struct dpif_netlink_flow *reply, struct ofpbuf **bufp); static void dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *, struct dpif_flow_stats *); static void dpif_netlink_flow_to_dpif_flow(struct dpif *, struct dpif_flow *, const struct dpif_netlink_flow *); /* One of the dpif channels between the kernel and userspace. */ struct dpif_channel { struct nl_sock *sock; /* Netlink socket. */ long long int last_poll; /* Last time this channel was polled. */ }; #ifdef _WIN32 #define VPORT_SOCK_POOL_SIZE 1 /* On Windows, there is no native support for epoll. There are equivalent * interfaces though, that are not used currently. For simpicity, a pool of * netlink sockets is used. Each socket is represented by 'struct * dpif_windows_vport_sock'. Since it is a pool, multiple OVS ports may be * sharing the same socket. In the future, we can add a reference count and * such fields. */ struct dpif_windows_vport_sock { struct nl_sock *nl_sock; /* netlink socket. */ }; #endif struct dpif_handler { struct dpif_channel *channels;/* Array of channels for each handler. */ struct epoll_event *epoll_events; int epoll_fd; /* epoll fd that includes channel socks. */ int n_events; /* Num events returned by epoll_wait(). */ int event_offset; /* Offset into 'epoll_events'. */ #ifdef _WIN32 /* Pool of sockets. */ struct dpif_windows_vport_sock *vport_sock_pool; size_t last_used_pool_idx; /* Index to aid in allocating a socket in the pool to a port. */ #endif }; /* Datapath interface for the openvswitch Linux kernel module. */ struct dpif_netlink { struct dpif dpif; int dp_ifindex; /* Upcall messages. */ struct fat_rwlock upcall_lock; struct dpif_handler *handlers; uint32_t n_handlers; /* Num of upcall handlers. */ int uc_array_size; /* Size of 'handler->channels' and */ /* 'handler->epoll_events'. */ /* Change notification. */ struct nl_sock *port_notifier; /* vport multicast group subscriber. */ bool refresh_channels; }; static void report_loss(struct dpif_netlink *, struct dpif_channel *, uint32_t ch_idx, uint32_t handler_id); static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); /* Generic Netlink family numbers for OVS. * * Initialized by dpif_netlink_init(). */ static int ovs_datapath_family; static int ovs_vport_family; static int ovs_flow_family; static int ovs_packet_family; /* Generic Netlink multicast groups for OVS. * * Initialized by dpif_netlink_init(). */ static unsigned int ovs_vport_mcgroup; static int dpif_netlink_init(void); static int open_dpif(const struct dpif_netlink_dp *, struct dpif **); static uint32_t dpif_netlink_port_get_pid(const struct dpif *, odp_port_t port_no, uint32_t hash); static void dpif_netlink_handler_uninit(struct dpif_handler *handler); static int dpif_netlink_refresh_channels(struct dpif_netlink *, uint32_t n_handlers); static void dpif_netlink_vport_to_ofpbuf(const struct dpif_netlink_vport *, struct ofpbuf *); static int dpif_netlink_vport_from_ofpbuf(struct dpif_netlink_vport *, const struct ofpbuf *); static struct dpif_netlink * dpif_netlink_cast(const struct dpif *dpif) { dpif_assert_class(dpif, &dpif_netlink_class); return CONTAINER_OF(dpif, struct dpif_netlink, dpif); } static int dpif_netlink_enumerate(struct sset *all_dps, const struct dpif_class *dpif_class OVS_UNUSED) { struct nl_dump dump; uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; struct ofpbuf msg, buf; int error; error = dpif_netlink_init(); if (error) { return error; } ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); dpif_netlink_dp_dump_start(&dump); while (nl_dump_next(&dump, &msg, &buf)) { struct dpif_netlink_dp dp; if (!dpif_netlink_dp_from_ofpbuf(&dp, &msg)) { sset_add(all_dps, dp.name); } } ofpbuf_uninit(&buf); return nl_dump_done(&dump); } static int dpif_netlink_open(const struct dpif_class *class OVS_UNUSED, const char *name, bool create, struct dpif **dpifp) { struct dpif_netlink_dp dp_request, dp; struct ofpbuf *buf; uint32_t upcall_pid; int error; error = dpif_netlink_init(); if (error) { return error; } /* Create or look up datapath. */ dpif_netlink_dp_init(&dp_request); if (create) { dp_request.cmd = OVS_DP_CMD_NEW; upcall_pid = 0; dp_request.upcall_pid = &upcall_pid; } else { /* Use OVS_DP_CMD_SET to report user features */ dp_request.cmd = OVS_DP_CMD_SET; } dp_request.name = name; dp_request.user_features |= OVS_DP_F_UNALIGNED; dp_request.user_features |= OVS_DP_F_VPORT_PIDS; error = dpif_netlink_dp_transact(&dp_request, &dp, &buf); if (error) { return error; } error = open_dpif(&dp, dpifp); ofpbuf_delete(buf); return error; } static int open_dpif(const struct dpif_netlink_dp *dp, struct dpif **dpifp) { struct dpif_netlink *dpif; dpif = xzalloc(sizeof *dpif); dpif->port_notifier = NULL; fat_rwlock_init(&dpif->upcall_lock); dpif_init(&dpif->dpif, &dpif_netlink_class, dp->name, dp->dp_ifindex, dp->dp_ifindex); dpif->dp_ifindex = dp->dp_ifindex; *dpifp = &dpif->dpif; return 0; } /* Destroys the netlink sockets pointed by the elements in 'socksp' * and frees the 'socksp'. */ static void vport_del_socksp__(struct nl_sock **socksp, uint32_t n_socks) { size_t i; for (i = 0; i < n_socks; i++) { nl_sock_destroy(socksp[i]); } free(socksp); } /* Creates an array of netlink sockets. Returns an array of the * corresponding pointers. Records the error in 'error'. */ static struct nl_sock ** vport_create_socksp__(uint32_t n_socks, int *error) { struct nl_sock **socksp = xzalloc(n_socks * sizeof *socksp); size_t i; for (i = 0; i < n_socks; i++) { *error = nl_sock_create(NETLINK_GENERIC, &socksp[i]); if (*error) { goto error; } } return socksp; error: vport_del_socksp__(socksp, n_socks); return NULL; } #ifdef _WIN32 static void vport_delete_sock_pool(struct dpif_handler *handler) OVS_REQ_WRLOCK(dpif->upcall_lock) { if (handler->vport_sock_pool) { uint32_t i; struct dpif_windows_vport_sock *sock_pool = handler->vport_sock_pool; for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { if (sock_pool[i].nl_sock) { nl_sock_unsubscribe_packets(sock_pool[i].nl_sock); nl_sock_destroy(sock_pool[i].nl_sock); sock_pool[i].nl_sock = NULL; } } free(handler->vport_sock_pool); handler->vport_sock_pool = NULL; } } static int vport_create_sock_pool(struct dpif_handler *handler) OVS_REQ_WRLOCK(dpif->upcall_lock) { struct dpif_windows_vport_sock *sock_pool; size_t i; int error = 0; sock_pool = xzalloc(VPORT_SOCK_POOL_SIZE * sizeof *sock_pool); for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { error = nl_sock_create(NETLINK_GENERIC, &sock_pool[i].nl_sock); if (error) { goto error; } /* Enable the netlink socket to receive packets. This is equivalent to * calling nl_sock_join_mcgroup() to receive events. */ error = nl_sock_subscribe_packets(sock_pool[i].nl_sock); if (error) { goto error; } } handler->vport_sock_pool = sock_pool; handler->last_used_pool_idx = 0; return 0; error: vport_delete_sock_pool(handler); return error; } /* Returns an array pointers to netlink sockets. The sockets are picked from a * pool. Records the error in 'error'. */ static struct nl_sock ** vport_create_socksp_windows(struct dpif_netlink *dpif, int *error) OVS_REQ_WRLOCK(dpif->upcall_lock) { uint32_t n_socks = dpif->n_handlers; struct nl_sock **socksp; size_t i; ovs_assert(n_socks <= 1); socksp = xzalloc(n_socks * sizeof *socksp); /* Pick netlink sockets to use in a round-robin fashion from each * handler's pool of sockets. */ for (i = 0; i < n_socks; i++) { struct dpif_handler *handler = &dpif->handlers[i]; struct dpif_windows_vport_sock *sock_pool = handler->vport_sock_pool; size_t index = handler->last_used_pool_idx; /* A pool of sockets is allocated when the handler is initialized. */ if (sock_pool == NULL) { free(socksp); *error = EINVAL; return NULL; } ovs_assert(index < VPORT_SOCK_POOL_SIZE); socksp[i] = sock_pool[index].nl_sock; socksp[i] = sock_pool[index].nl_sock; ovs_assert(socksp[i]); index = (index == VPORT_SOCK_POOL_SIZE - 1) ? 0 : index + 1; handler->last_used_pool_idx = index; } return socksp; } static void vport_del_socksp_windows(struct dpif_netlink *dpif, struct nl_sock **socksp) { free(socksp); } #endif /* _WIN32 */ static struct nl_sock ** vport_create_socksp(struct dpif_netlink *dpif, int *error) { #ifdef _WIN32 return vport_create_socksp_windows(dpif, error); #else return vport_create_socksp__(dpif->n_handlers, error); #endif } static void vport_del_socksp(struct dpif_netlink *dpif, struct nl_sock **socksp) { #ifdef _WIN32 vport_del_socksp_windows(dpif, socksp); #else vport_del_socksp__(socksp, dpif->n_handlers); #endif } /* Given the array of pointers to netlink sockets 'socksp', returns * the array of corresponding pids. If the 'socksp' is NULL, returns * a single-element array of value 0. */ static uint32_t * vport_socksp_to_pids(struct nl_sock **socksp, uint32_t n_socks) { uint32_t *pids; if (!socksp) { pids = xzalloc(sizeof *pids); } else { size_t i; pids = xzalloc(n_socks * sizeof *pids); for (i = 0; i < n_socks; i++) { pids[i] = nl_sock_pid(socksp[i]); } } return pids; } /* Given the port number 'port_idx', extracts the pids of netlink sockets * associated to the port and assigns it to 'upcall_pids'. */ static bool vport_get_pids(struct dpif_netlink *dpif, uint32_t port_idx, uint32_t **upcall_pids) { uint32_t *pids; size_t i; /* Since the nl_sock can only be assigned in either all * or none "dpif->handlers" channels, the following check * would suffice. */ if (!dpif->handlers[0].channels[port_idx].sock) { return false; } ovs_assert(!WINDOWS || dpif->n_handlers <= 1); pids = xzalloc(dpif->n_handlers * sizeof *pids); for (i = 0; i < dpif->n_handlers; i++) { pids[i] = nl_sock_pid(dpif->handlers[i].channels[port_idx].sock); } *upcall_pids = pids; return true; } static int vport_add_channels(struct dpif_netlink *dpif, odp_port_t port_no, struct nl_sock **socksp) { struct epoll_event event; uint32_t port_idx = odp_to_u32(port_no); size_t i, j; int error; if (dpif->handlers == NULL) { return 0; } /* We assume that the datapath densely chooses port numbers, which can * therefore be used as an index into 'channels' and 'epoll_events' of * 'dpif->handler'. */ if (port_idx >= dpif->uc_array_size) { uint32_t new_size = port_idx + 1; if (new_size > MAX_PORTS) { VLOG_WARN_RL(&error_rl, "%s: datapath port %"PRIu32" too big", dpif_name(&dpif->dpif), port_no); return EFBIG; } for (i = 0; i < dpif->n_handlers; i++) { struct dpif_handler *handler = &dpif->handlers[i]; handler->channels = xrealloc(handler->channels, new_size * sizeof *handler->channels); for (j = dpif->uc_array_size; j < new_size; j++) { handler->channels[j].sock = NULL; } handler->epoll_events = xrealloc(handler->epoll_events, new_size * sizeof *handler->epoll_events); } dpif->uc_array_size = new_size; } memset(&event, 0, sizeof event); event.events = EPOLLIN; event.data.u32 = port_idx; for (i = 0; i < dpif->n_handlers; i++) { struct dpif_handler *handler = &dpif->handlers[i]; #ifndef _WIN32 if (epoll_ctl(handler->epoll_fd, EPOLL_CTL_ADD, nl_sock_fd(socksp[i]), &event) < 0) { error = errno; goto error; } #endif dpif->handlers[i].channels[port_idx].sock = socksp[i]; dpif->handlers[i].channels[port_idx].last_poll = LLONG_MIN; } return 0; error: for (j = 0; j < i; j++) { #ifndef _WIN32 epoll_ctl(dpif->handlers[j].epoll_fd, EPOLL_CTL_DEL, nl_sock_fd(socksp[j]), NULL); #endif dpif->handlers[j].channels[port_idx].sock = NULL; } return error; } static void vport_del_channels(struct dpif_netlink *dpif, odp_port_t port_no) { uint32_t port_idx = odp_to_u32(port_no); size_t i; if (!dpif->handlers || port_idx >= dpif->uc_array_size) { return; } /* Since the sock can only be assigned in either all or none * of "dpif->handlers" channels, the following check would * suffice. */ if (!dpif->handlers[0].channels[port_idx].sock) { return; } for (i = 0; i < dpif->n_handlers; i++) { struct dpif_handler *handler = &dpif->handlers[i]; #ifndef _WIN32 epoll_ctl(handler->epoll_fd, EPOLL_CTL_DEL, nl_sock_fd(handler->channels[port_idx].sock), NULL); nl_sock_destroy(handler->channels[port_idx].sock); #endif handler->channels[port_idx].sock = NULL; handler->event_offset = handler->n_events = 0; } } static void destroy_all_channels(struct dpif_netlink *dpif) OVS_REQ_WRLOCK(dpif->upcall_lock) { unsigned int i; if (!dpif->handlers) { return; } for (i = 0; i < dpif->uc_array_size; i++ ) { struct dpif_netlink_vport vport_request; uint32_t upcall_pids = 0; /* Since the sock can only be assigned in either all or none * of "dpif->handlers" channels, the following check would * suffice. */ if (!dpif->handlers[0].channels[i].sock) { continue; } /* Turn off upcalls. */ dpif_netlink_vport_init(&vport_request); vport_request.cmd = OVS_VPORT_CMD_SET; vport_request.dp_ifindex = dpif->dp_ifindex; vport_request.port_no = u32_to_odp(i); vport_request.n_upcall_pids = 1; vport_request.upcall_pids = &upcall_pids; dpif_netlink_vport_transact(&vport_request, NULL, NULL); vport_del_channels(dpif, u32_to_odp(i)); } for (i = 0; i < dpif->n_handlers; i++) { struct dpif_handler *handler = &dpif->handlers[i]; dpif_netlink_handler_uninit(handler); free(handler->epoll_events); free(handler->channels); } free(dpif->handlers); dpif->handlers = NULL; dpif->n_handlers = 0; dpif->uc_array_size = 0; } static void dpif_netlink_close(struct dpif *dpif_) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); nl_sock_destroy(dpif->port_notifier); fat_rwlock_wrlock(&dpif->upcall_lock); destroy_all_channels(dpif); fat_rwlock_unlock(&dpif->upcall_lock); fat_rwlock_destroy(&dpif->upcall_lock); free(dpif); } static int dpif_netlink_destroy(struct dpif *dpif_) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_dp dp; dpif_netlink_dp_init(&dp); dp.cmd = OVS_DP_CMD_DEL; dp.dp_ifindex = dpif->dp_ifindex; return dpif_netlink_dp_transact(&dp, NULL, NULL); } static bool dpif_netlink_run(struct dpif *dpif_) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); if (dpif->refresh_channels) { dpif->refresh_channels = false; fat_rwlock_wrlock(&dpif->upcall_lock); dpif_netlink_refresh_channels(dpif, dpif->n_handlers); fat_rwlock_unlock(&dpif->upcall_lock); } return false; } static int dpif_netlink_get_stats(const struct dpif *dpif_, struct dpif_dp_stats *stats) { struct dpif_netlink_dp dp; struct ofpbuf *buf; int error; error = dpif_netlink_dp_get(dpif_, &dp, &buf); if (!error) { memset(stats, 0, sizeof *stats); if (dp.stats) { stats->n_hit = get_32aligned_u64(&dp.stats->n_hit); stats->n_missed = get_32aligned_u64(&dp.stats->n_missed); stats->n_lost = get_32aligned_u64(&dp.stats->n_lost); stats->n_flows = get_32aligned_u64(&dp.stats->n_flows); } if (dp.megaflow_stats) { stats->n_masks = dp.megaflow_stats->n_masks; stats->n_mask_hit = get_32aligned_u64( &dp.megaflow_stats->n_mask_hit); } else { stats->n_masks = UINT32_MAX; stats->n_mask_hit = UINT64_MAX; } ofpbuf_delete(buf); } return error; } static const char * get_vport_type(const struct dpif_netlink_vport *vport) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); switch (vport->type) { case OVS_VPORT_TYPE_NETDEV: { const char *type = netdev_get_type_from_name(vport->name); return type ? type : "system"; } case OVS_VPORT_TYPE_INTERNAL: return "internal"; case OVS_VPORT_TYPE_GENEVE: return "geneve"; case OVS_VPORT_TYPE_GRE: return "gre"; case OVS_VPORT_TYPE_VXLAN: return "vxlan"; case OVS_VPORT_TYPE_LISP: return "lisp"; case OVS_VPORT_TYPE_STT: return "stt"; case OVS_VPORT_TYPE_UNSPEC: case __OVS_VPORT_TYPE_MAX: break; } VLOG_WARN_RL(&rl, "dp%d: port `%s' has unsupported type %u", vport->dp_ifindex, vport->name, (unsigned int) vport->type); return "unknown"; } static enum ovs_vport_type netdev_to_ovs_vport_type(const struct netdev *netdev) { const char *type = netdev_get_type(netdev); if (!strcmp(type, "tap") || !strcmp(type, "system")) { return OVS_VPORT_TYPE_NETDEV; } else if (!strcmp(type, "internal")) { return OVS_VPORT_TYPE_INTERNAL; } else if (strstr(type, "stt")) { return OVS_VPORT_TYPE_STT; } else if (!strcmp(type, "geneve")) { return OVS_VPORT_TYPE_GENEVE; } else if (strstr(type, "gre")) { return OVS_VPORT_TYPE_GRE; } else if (!strcmp(type, "vxlan")) { return OVS_VPORT_TYPE_VXLAN; } else if (!strcmp(type, "lisp")) { return OVS_VPORT_TYPE_LISP; } else { return OVS_VPORT_TYPE_UNSPEC; } } static int dpif_netlink_port_add__(struct dpif_netlink *dpif, struct netdev *netdev, odp_port_t *port_nop) OVS_REQ_WRLOCK(dpif->upcall_lock) { const struct netdev_tunnel_config *tnl_cfg; char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); const char *type = netdev_get_type(netdev); struct dpif_netlink_vport request, reply; struct ofpbuf *buf; uint64_t options_stub[64 / 8]; struct ofpbuf options; struct nl_sock **socksp = NULL; uint32_t *upcall_pids; int error = 0; if (dpif->handlers) { socksp = vport_create_socksp(dpif, &error); if (!socksp) { return error; } } dpif_netlink_vport_init(&request); request.cmd = OVS_VPORT_CMD_NEW; request.dp_ifindex = dpif->dp_ifindex; request.type = netdev_to_ovs_vport_type(netdev); if (request.type == OVS_VPORT_TYPE_UNSPEC) { VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has " "unsupported type `%s'", dpif_name(&dpif->dpif), name, type); vport_del_socksp(dpif, socksp); return EINVAL; } request.name = name; if (request.type == OVS_VPORT_TYPE_NETDEV) { #ifdef _WIN32 /* XXX : Map appropiate Windows handle */ #else netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false); #endif } tnl_cfg = netdev_get_tunnel_config(netdev); if (tnl_cfg && (tnl_cfg->dst_port != 0 || tnl_cfg->exts)) { ofpbuf_use_stack(&options, options_stub, sizeof options_stub); if (tnl_cfg->dst_port) { nl_msg_put_u16(&options, OVS_TUNNEL_ATTR_DST_PORT, ntohs(tnl_cfg->dst_port)); } if (tnl_cfg->exts) { size_t ext_ofs; int i; ext_ofs = nl_msg_start_nested(&options, OVS_TUNNEL_ATTR_EXTENSION); for (i = 0; i < 32; i++) { if (tnl_cfg->exts & (1 << i)) { nl_msg_put_flag(&options, i); } } nl_msg_end_nested(&options, ext_ofs); } request.options = options.data; request.options_len = options.size; } request.port_no = *port_nop; upcall_pids = vport_socksp_to_pids(socksp, dpif->n_handlers); request.n_upcall_pids = socksp ? dpif->n_handlers : 1; request.upcall_pids = upcall_pids; error = dpif_netlink_vport_transact(&request, &reply, &buf); if (!error) { *port_nop = reply.port_no; } else { if (error == EBUSY && *port_nop != ODPP_NONE) { VLOG_INFO("%s: requested port %"PRIu32" is in use", dpif_name(&dpif->dpif), *port_nop); } vport_del_socksp(dpif, socksp); goto exit; } if (socksp) { error = vport_add_channels(dpif, *port_nop, socksp); if (error) { VLOG_INFO("%s: could not add channel for port %s", dpif_name(&dpif->dpif), name); /* Delete the port. */ dpif_netlink_vport_init(&request); request.cmd = OVS_VPORT_CMD_DEL; request.dp_ifindex = dpif->dp_ifindex; request.port_no = *port_nop; dpif_netlink_vport_transact(&request, NULL, NULL); vport_del_socksp(dpif, socksp); goto exit; } } free(socksp); exit: ofpbuf_delete(buf); free(upcall_pids); return error; } static int dpif_netlink_port_add(struct dpif *dpif_, struct netdev *netdev, odp_port_t *port_nop) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); int error; fat_rwlock_wrlock(&dpif->upcall_lock); error = dpif_netlink_port_add__(dpif, netdev, port_nop); fat_rwlock_unlock(&dpif->upcall_lock); return error; } static int dpif_netlink_port_del__(struct dpif_netlink *dpif, odp_port_t port_no) OVS_REQ_WRLOCK(dpif->upcall_lock) { struct dpif_netlink_vport vport; int error; dpif_netlink_vport_init(&vport); vport.cmd = OVS_VPORT_CMD_DEL; vport.dp_ifindex = dpif->dp_ifindex; vport.port_no = port_no; error = dpif_netlink_vport_transact(&vport, NULL, NULL); vport_del_channels(dpif, port_no); return error; } static int dpif_netlink_port_del(struct dpif *dpif_, odp_port_t port_no) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); int error; fat_rwlock_wrlock(&dpif->upcall_lock); error = dpif_netlink_port_del__(dpif, port_no); fat_rwlock_unlock(&dpif->upcall_lock); return error; } static int dpif_netlink_port_query__(const struct dpif_netlink *dpif, odp_port_t port_no, const char *port_name, struct dpif_port *dpif_port) { struct dpif_netlink_vport request; struct dpif_netlink_vport reply; struct ofpbuf *buf; int error; dpif_netlink_vport_init(&request); request.cmd = OVS_VPORT_CMD_GET; request.dp_ifindex = dpif->dp_ifindex; request.port_no = port_no; request.name = port_name; error = dpif_netlink_vport_transact(&request, &reply, &buf); if (!error) { if (reply.dp_ifindex != request.dp_ifindex) { /* A query by name reported that 'port_name' is in some datapath * other than 'dpif', but the caller wants to know about 'dpif'. */ error = ENODEV; } else if (dpif_port) { dpif_port->name = xstrdup(reply.name); dpif_port->type = xstrdup(get_vport_type(&reply)); dpif_port->port_no = reply.port_no; } ofpbuf_delete(buf); } return error; } static int dpif_netlink_port_query_by_number(const struct dpif *dpif_, odp_port_t port_no, struct dpif_port *dpif_port) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); return dpif_netlink_port_query__(dpif, port_no, NULL, dpif_port); } static int dpif_netlink_port_query_by_name(const struct dpif *dpif_, const char *devname, struct dpif_port *dpif_port) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); return dpif_netlink_port_query__(dpif, 0, devname, dpif_port); } static uint32_t dpif_netlink_port_get_pid__(const struct dpif_netlink *dpif, odp_port_t port_no, uint32_t hash) OVS_REQ_RDLOCK(dpif->upcall_lock) { uint32_t port_idx = odp_to_u32(port_no); uint32_t pid = 0; if (dpif->handlers && dpif->uc_array_size > 0) { /* The ODPP_NONE "reserved" port number uses the "ovs-system"'s * channel, since it is not heavily loaded. */ uint32_t idx = port_idx >= dpif->uc_array_size ? 0 : port_idx; struct dpif_handler *h = &dpif->handlers[hash % dpif->n_handlers]; /* Needs to check in case the socket pointer is changed in between * the holding of upcall_lock. A known case happens when the main * thread deletes the vport while the handler thread is handling * the upcall from that port. */ if (h->channels[idx].sock) { pid = nl_sock_pid(h->channels[idx].sock); } } return pid; } static uint32_t dpif_netlink_port_get_pid(const struct dpif *dpif_, odp_port_t port_no, uint32_t hash) { const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); uint32_t ret; fat_rwlock_rdlock(&dpif->upcall_lock); ret = dpif_netlink_port_get_pid__(dpif, port_no, hash); fat_rwlock_unlock(&dpif->upcall_lock); return ret; } static int dpif_netlink_flow_flush(struct dpif *dpif_) { const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_flow flow; dpif_netlink_flow_init(&flow); flow.cmd = OVS_FLOW_CMD_DEL; flow.dp_ifindex = dpif->dp_ifindex; return dpif_netlink_flow_transact(&flow, NULL, NULL); } struct dpif_netlink_port_state { struct nl_dump dump; struct ofpbuf buf; }; static void dpif_netlink_port_dump_start__(const struct dpif_netlink *dpif, struct nl_dump *dump) { struct dpif_netlink_vport request; struct ofpbuf *buf; dpif_netlink_vport_init(&request); request.cmd = OVS_VPORT_CMD_GET; request.dp_ifindex = dpif->dp_ifindex; buf = ofpbuf_new(1024); dpif_netlink_vport_to_ofpbuf(&request, buf); nl_dump_start(dump, NETLINK_GENERIC, buf); ofpbuf_delete(buf); } static int dpif_netlink_port_dump_start(const struct dpif *dpif_, void **statep) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_port_state *state; *statep = state = xmalloc(sizeof *state); dpif_netlink_port_dump_start__(dpif, &state->dump); ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE); return 0; } static int dpif_netlink_port_dump_next__(const struct dpif_netlink *dpif, struct nl_dump *dump, struct dpif_netlink_vport *vport, struct ofpbuf *buffer) { struct ofpbuf buf; int error; if (!nl_dump_next(dump, &buf, buffer)) { return EOF; } error = dpif_netlink_vport_from_ofpbuf(vport, &buf); if (error) { VLOG_WARN_RL(&error_rl, "%s: failed to parse vport record (%s)", dpif_name(&dpif->dpif), ovs_strerror(error)); } return error; } static int dpif_netlink_port_dump_next(const struct dpif *dpif_, void *state_, struct dpif_port *dpif_port) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_port_state *state = state_; struct dpif_netlink_vport vport; int error; error = dpif_netlink_port_dump_next__(dpif, &state->dump, &vport, &state->buf); if (error) { return error; } dpif_port->name = CONST_CAST(char *, vport.name); dpif_port->type = CONST_CAST(char *, get_vport_type(&vport)); dpif_port->port_no = vport.port_no; return 0; } static int dpif_netlink_port_dump_done(const struct dpif *dpif_ OVS_UNUSED, void *state_) { struct dpif_netlink_port_state *state = state_; int error = nl_dump_done(&state->dump); ofpbuf_uninit(&state->buf); free(state); return error; } static int dpif_netlink_port_poll(const struct dpif *dpif_, char **devnamep) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); /* Lazily create the Netlink socket to listen for notifications. */ if (!dpif->port_notifier) { struct nl_sock *sock; int error; error = nl_sock_create(NETLINK_GENERIC, &sock); if (error) { return error; } error = nl_sock_join_mcgroup(sock, ovs_vport_mcgroup); if (error) { nl_sock_destroy(sock); return error; } dpif->port_notifier = sock; /* We have no idea of the current state so report that everything * changed. */ return ENOBUFS; } for (;;) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); uint64_t buf_stub[4096 / 8]; struct ofpbuf buf; int error; ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); error = nl_sock_recv(dpif->port_notifier, &buf, false); if (!error) { struct dpif_netlink_vport vport; error = dpif_netlink_vport_from_ofpbuf(&vport, &buf); if (!error) { if (vport.dp_ifindex == dpif->dp_ifindex && (vport.cmd == OVS_VPORT_CMD_NEW || vport.cmd == OVS_VPORT_CMD_DEL || vport.cmd == OVS_VPORT_CMD_SET)) { VLOG_DBG("port_changed: dpif:%s vport:%s cmd:%"PRIu8, dpif->dpif.full_name, vport.name, vport.cmd); if (vport.cmd == OVS_VPORT_CMD_DEL && dpif->handlers) { dpif->refresh_channels = true; } *devnamep = xstrdup(vport.name); ofpbuf_uninit(&buf); return 0; } } } else if (error != EAGAIN) { VLOG_WARN_RL(&rl, "error reading or parsing netlink (%s)", ovs_strerror(error)); nl_sock_drain(dpif->port_notifier); error = ENOBUFS; } ofpbuf_uninit(&buf); if (error) { return error; } } } static void dpif_netlink_port_poll_wait(const struct dpif *dpif_) { const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); if (dpif->port_notifier) { nl_sock_wait(dpif->port_notifier, POLLIN); } else { poll_immediate_wake(); } } static void dpif_netlink_flow_init_ufid(struct dpif_netlink_flow *request, const ovs_u128 *ufid, bool terse) { if (ufid) { request->ufid = *ufid; request->ufid_present = true; } else { request->ufid_present = false; } request->ufid_terse = terse; } static void dpif_netlink_init_flow_get__(const struct dpif_netlink *dpif, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, bool terse, struct dpif_netlink_flow *request) { dpif_netlink_flow_init(request); request->cmd = OVS_FLOW_CMD_GET; request->dp_ifindex = dpif->dp_ifindex; request->key = key; request->key_len = key_len; dpif_netlink_flow_init_ufid(request, ufid, terse); } static void dpif_netlink_init_flow_get(const struct dpif_netlink *dpif, const struct dpif_flow_get *get, struct dpif_netlink_flow *request) { dpif_netlink_init_flow_get__(dpif, get->key, get->key_len, get->ufid, false, request); } static int dpif_netlink_flow_get__(const struct dpif_netlink *dpif, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, bool terse, struct dpif_netlink_flow *reply, struct ofpbuf **bufp) { struct dpif_netlink_flow request; dpif_netlink_init_flow_get__(dpif, key, key_len, ufid, terse, &request); return dpif_netlink_flow_transact(&request, reply, bufp); } static int dpif_netlink_flow_get(const struct dpif_netlink *dpif, const struct dpif_netlink_flow *flow, struct dpif_netlink_flow *reply, struct ofpbuf **bufp) { return dpif_netlink_flow_get__(dpif, flow->key, flow->key_len, flow->ufid_present ? &flow->ufid : NULL, false, reply, bufp); } static void dpif_netlink_init_flow_put(struct dpif_netlink *dpif, const struct dpif_flow_put *put, struct dpif_netlink_flow *request) { static const struct nlattr dummy_action; dpif_netlink_flow_init(request); request->cmd = (put->flags & DPIF_FP_CREATE ? OVS_FLOW_CMD_NEW : OVS_FLOW_CMD_SET); request->dp_ifindex = dpif->dp_ifindex; request->key = put->key; request->key_len = put->key_len; request->mask = put->mask; request->mask_len = put->mask_len; dpif_netlink_flow_init_ufid(request, put->ufid, false); /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */ request->actions = (put->actions ? put->actions : CONST_CAST(struct nlattr *, &dummy_action)); request->actions_len = put->actions_len; if (put->flags & DPIF_FP_ZERO_STATS) { request->clear = true; } if (put->flags & DPIF_FP_PROBE) { request->probe = true; } request->nlmsg_flags = put->flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE; } static void dpif_netlink_init_flow_del__(struct dpif_netlink *dpif, const struct nlattr *key, size_t key_len, const ovs_u128 *ufid, bool terse, struct dpif_netlink_flow *request) { dpif_netlink_flow_init(request); request->cmd = OVS_FLOW_CMD_DEL; request->dp_ifindex = dpif->dp_ifindex; request->key = key; request->key_len = key_len; dpif_netlink_flow_init_ufid(request, ufid, terse); } static void dpif_netlink_init_flow_del(struct dpif_netlink *dpif, const struct dpif_flow_del *del, struct dpif_netlink_flow *request) { dpif_netlink_init_flow_del__(dpif, del->key, del->key_len, del->ufid, del->terse, request); } struct dpif_netlink_flow_dump { struct dpif_flow_dump up; struct nl_dump nl_dump; atomic_int status; }; static struct dpif_netlink_flow_dump * dpif_netlink_flow_dump_cast(struct dpif_flow_dump *dump) { return CONTAINER_OF(dump, struct dpif_netlink_flow_dump, up); } static struct dpif_flow_dump * dpif_netlink_flow_dump_create(const struct dpif *dpif_, bool terse) { const struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_flow_dump *dump; struct dpif_netlink_flow request; struct ofpbuf *buf; dump = xmalloc(sizeof *dump); dpif_flow_dump_init(&dump->up, dpif_); dpif_netlink_flow_init(&request); request.cmd = OVS_FLOW_CMD_GET; request.dp_ifindex = dpif->dp_ifindex; request.ufid_present = false; request.ufid_terse = terse; buf = ofpbuf_new(1024); dpif_netlink_flow_to_ofpbuf(&request, buf); nl_dump_start(&dump->nl_dump, NETLINK_GENERIC, buf); ofpbuf_delete(buf); atomic_init(&dump->status, 0); dump->up.terse = terse; return &dump->up; } static int dpif_netlink_flow_dump_destroy(struct dpif_flow_dump *dump_) { struct dpif_netlink_flow_dump *dump = dpif_netlink_flow_dump_cast(dump_); unsigned int nl_status = nl_dump_done(&dump->nl_dump); int dump_status; /* No other thread has access to 'dump' at this point. */ atomic_read_relaxed(&dump->status, &dump_status); free(dump); return dump_status ? dump_status : nl_status; } struct dpif_netlink_flow_dump_thread { struct dpif_flow_dump_thread up; struct dpif_netlink_flow_dump *dump; struct dpif_netlink_flow flow; struct dpif_flow_stats stats; struct ofpbuf nl_flows; /* Always used to store flows. */ struct ofpbuf *nl_actions; /* Used if kernel does not supply actions. */ }; static struct dpif_netlink_flow_dump_thread * dpif_netlink_flow_dump_thread_cast(struct dpif_flow_dump_thread *thread) { return CONTAINER_OF(thread, struct dpif_netlink_flow_dump_thread, up); } static struct dpif_flow_dump_thread * dpif_netlink_flow_dump_thread_create(struct dpif_flow_dump *dump_) { struct dpif_netlink_flow_dump *dump = dpif_netlink_flow_dump_cast(dump_); struct dpif_netlink_flow_dump_thread *thread; thread = xmalloc(sizeof *thread); dpif_flow_dump_thread_init(&thread->up, &dump->up); thread->dump = dump; ofpbuf_init(&thread->nl_flows, NL_DUMP_BUFSIZE); thread->nl_actions = NULL; return &thread->up; } static void dpif_netlink_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread_) { struct dpif_netlink_flow_dump_thread *thread = dpif_netlink_flow_dump_thread_cast(thread_); ofpbuf_uninit(&thread->nl_flows); ofpbuf_delete(thread->nl_actions); free(thread); } static void dpif_netlink_flow_to_dpif_flow(struct dpif *dpif, struct dpif_flow *dpif_flow, const struct dpif_netlink_flow *datapath_flow) { dpif_flow->key = datapath_flow->key; dpif_flow->key_len = datapath_flow->key_len; dpif_flow->mask = datapath_flow->mask; dpif_flow->mask_len = datapath_flow->mask_len; dpif_flow->actions = datapath_flow->actions; dpif_flow->actions_len = datapath_flow->actions_len; dpif_flow->ufid_present = datapath_flow->ufid_present; dpif_flow->pmd_id = PMD_ID_NULL; if (datapath_flow->ufid_present) { dpif_flow->ufid = datapath_flow->ufid; } else { ovs_assert(datapath_flow->key && datapath_flow->key_len); dpif_flow_hash(dpif, datapath_flow->key, datapath_flow->key_len, &dpif_flow->ufid); } dpif_netlink_flow_get_stats(datapath_flow, &dpif_flow->stats); } static int dpif_netlink_flow_dump_next(struct dpif_flow_dump_thread *thread_, struct dpif_flow *flows, int max_flows) { struct dpif_netlink_flow_dump_thread *thread = dpif_netlink_flow_dump_thread_cast(thread_); struct dpif_netlink_flow_dump *dump = thread->dump; struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dpif); int n_flows; ofpbuf_delete(thread->nl_actions); thread->nl_actions = NULL; n_flows = 0; while (!n_flows || (n_flows < max_flows && thread->nl_flows.size)) { struct dpif_netlink_flow datapath_flow; struct ofpbuf nl_flow; int error; /* Try to grab another flow. */ if (!nl_dump_next(&dump->nl_dump, &nl_flow, &thread->nl_flows)) { break; } /* Convert the flow to our output format. */ error = dpif_netlink_flow_from_ofpbuf(&datapath_flow, &nl_flow); if (error) { atomic_store_relaxed(&dump->status, error); break; } if (dump->up.terse || datapath_flow.actions) { /* Common case: we don't want actions, or the flow includes * actions. */ dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++], &datapath_flow); } else { /* Rare case: the flow does not include actions. Retrieve this * individual flow again to get the actions. */ error = dpif_netlink_flow_get(dpif, &datapath_flow, &datapath_flow, &thread->nl_actions); if (error == ENOENT) { VLOG_DBG("dumped flow disappeared on get"); continue; } else if (error) { VLOG_WARN("error fetching dumped flow: %s", ovs_strerror(error)); atomic_store_relaxed(&dump->status, error); break; } /* Save this flow. Then exit, because we only have one buffer to * handle this case. */ dpif_netlink_flow_to_dpif_flow(&dpif->dpif, &flows[n_flows++], &datapath_flow); break; } } return n_flows; } static void dpif_netlink_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec, struct ofpbuf *buf) { struct ovs_header *k_exec; size_t key_ofs; ofpbuf_prealloc_tailroom(buf, (64 + dp_packet_size(d_exec->packet) + ODP_KEY_METADATA_SIZE + d_exec->actions_len)); nl_msg_put_genlmsghdr(buf, 0, ovs_packet_family, NLM_F_REQUEST, OVS_PACKET_CMD_EXECUTE, OVS_PACKET_VERSION); k_exec = ofpbuf_put_uninit(buf, sizeof *k_exec); k_exec->dp_ifindex = dp_ifindex; nl_msg_put_unspec(buf, OVS_PACKET_ATTR_PACKET, dp_packet_data(d_exec->packet), dp_packet_size(d_exec->packet)); key_ofs = nl_msg_start_nested(buf, OVS_PACKET_ATTR_KEY); odp_key_from_pkt_metadata(buf, &d_exec->packet->md); nl_msg_end_nested(buf, key_ofs); nl_msg_put_unspec(buf, OVS_PACKET_ATTR_ACTIONS, d_exec->actions, d_exec->actions_len); if (d_exec->probe) { nl_msg_put_flag(buf, OVS_PACKET_ATTR_PROBE); } if (d_exec->mtu) { nl_msg_put_u16(buf, OVS_PACKET_ATTR_MRU, d_exec->mtu); } } /* Executes, against 'dpif', up to the first 'n_ops' operations in 'ops'. * Returns the number actually executed (at least 1, if 'n_ops' is * positive). */ static size_t dpif_netlink_operate__(struct dpif_netlink *dpif, struct dpif_op **ops, size_t n_ops) { enum { MAX_OPS = 50 }; struct op_auxdata { struct nl_transaction txn; struct ofpbuf request; uint64_t request_stub[1024 / 8]; struct ofpbuf reply; uint64_t reply_stub[1024 / 8]; } auxes[MAX_OPS]; struct nl_transaction *txnsp[MAX_OPS]; size_t i; n_ops = MIN(n_ops, MAX_OPS); for (i = 0; i < n_ops; i++) { struct op_auxdata *aux = &auxes[i]; struct dpif_op *op = ops[i]; struct dpif_flow_put *put; struct dpif_flow_del *del; struct dpif_flow_get *get; struct dpif_netlink_flow flow; ofpbuf_use_stub(&aux->request, aux->request_stub, sizeof aux->request_stub); aux->txn.request = &aux->request; ofpbuf_use_stub(&aux->reply, aux->reply_stub, sizeof aux->reply_stub); aux->txn.reply = NULL; switch (op->type) { case DPIF_OP_FLOW_PUT: put = &op->u.flow_put; dpif_netlink_init_flow_put(dpif, put, &flow); if (put->stats) { flow.nlmsg_flags |= NLM_F_ECHO; aux->txn.reply = &aux->reply; } dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); break; case DPIF_OP_FLOW_DEL: del = &op->u.flow_del; dpif_netlink_init_flow_del(dpif, del, &flow); if (del->stats) { flow.nlmsg_flags |= NLM_F_ECHO; aux->txn.reply = &aux->reply; } dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); break; case DPIF_OP_EXECUTE: /* Can't execute a packet that won't fit in a Netlink attribute. */ if (OVS_UNLIKELY(nl_attr_oversized( dp_packet_size(op->u.execute.packet)))) { /* Report an error immediately if this is the first operation. * Otherwise the easiest thing to do is to postpone to the next * call (when this will be the first operation). */ if (i == 0) { VLOG_ERR_RL(&error_rl, "dropping oversized %"PRIu32"-byte packet", dp_packet_size(op->u.execute.packet)); op->error = ENOBUFS; return 1; } n_ops = i; } else { dpif_netlink_encode_execute(dpif->dp_ifindex, &op->u.execute, &aux->request); } break; case DPIF_OP_FLOW_GET: get = &op->u.flow_get; dpif_netlink_init_flow_get(dpif, get, &flow); aux->txn.reply = get->buffer; dpif_netlink_flow_to_ofpbuf(&flow, &aux->request); break; default: OVS_NOT_REACHED(); } } for (i = 0; i < n_ops; i++) { txnsp[i] = &auxes[i].txn; } nl_transact_multiple(NETLINK_GENERIC, txnsp, n_ops); for (i = 0; i < n_ops; i++) { struct op_auxdata *aux = &auxes[i]; struct nl_transaction *txn = &auxes[i].txn; struct dpif_op *op = ops[i]; struct dpif_flow_put *put; struct dpif_flow_del *del; struct dpif_flow_get *get; op->error = txn->error; switch (op->type) { case DPIF_OP_FLOW_PUT: put = &op->u.flow_put; if (put->stats) { if (!op->error) { struct dpif_netlink_flow reply; op->error = dpif_netlink_flow_from_ofpbuf(&reply, txn->reply); if (!op->error) { dpif_netlink_flow_get_stats(&reply, put->stats); } } } break; case DPIF_OP_FLOW_DEL: del = &op->u.flow_del; if (del->stats) { if (!op->error) { struct dpif_netlink_flow reply; op->error = dpif_netlink_flow_from_ofpbuf(&reply, txn->reply); if (!op->error) { dpif_netlink_flow_get_stats(&reply, del->stats); } } } break; case DPIF_OP_EXECUTE: break; case DPIF_OP_FLOW_GET: get = &op->u.flow_get; if (!op->error) { struct dpif_netlink_flow reply; op->error = dpif_netlink_flow_from_ofpbuf(&reply, txn->reply); if (!op->error) { dpif_netlink_flow_to_dpif_flow(&dpif->dpif, get->flow, &reply); } } break; default: OVS_NOT_REACHED(); } ofpbuf_uninit(&aux->request); ofpbuf_uninit(&aux->reply); } return n_ops; } static void dpif_netlink_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); while (n_ops > 0) { size_t chunk = dpif_netlink_operate__(dpif, ops, n_ops); ops += chunk; n_ops -= chunk; } } #if _WIN32 static void dpif_netlink_handler_uninit(struct dpif_handler *handler) { vport_delete_sock_pool(handler); } static int dpif_netlink_handler_init(struct dpif_handler *handler) { return vport_create_sock_pool(handler); } #else static int dpif_netlink_handler_init(struct dpif_handler *handler) { handler->epoll_fd = epoll_create(10); return handler->epoll_fd < 0 ? errno : 0; } static void dpif_netlink_handler_uninit(struct dpif_handler *handler) { close(handler->epoll_fd); } #endif /* Synchronizes 'channels' in 'dpif->handlers' with the set of vports * currently in 'dpif' in the kernel, by adding a new set of channels for * any kernel vport that lacks one and deleting any channels that have no * backing kernel vports. */ static int dpif_netlink_refresh_channels(struct dpif_netlink *dpif, uint32_t n_handlers) OVS_REQ_WRLOCK(dpif->upcall_lock) { unsigned long int *keep_channels; struct dpif_netlink_vport vport; size_t keep_channels_nbits; struct nl_dump dump; uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; struct ofpbuf buf; int retval = 0; size_t i; ovs_assert(!WINDOWS || n_handlers <= 1); ovs_assert(!WINDOWS || dpif->n_handlers <= 1); if (dpif->n_handlers != n_handlers) { destroy_all_channels(dpif); dpif->handlers = xzalloc(n_handlers * sizeof *dpif->handlers); for (i = 0; i < n_handlers; i++) { int error; struct dpif_handler *handler = &dpif->handlers[i]; error = dpif_netlink_handler_init(handler); if (error) { size_t j; for (j = 0; j < i; j++) { struct dpif_handler *tmp = &dpif->handlers[j]; dpif_netlink_handler_uninit(tmp); } free(dpif->handlers); dpif->handlers = NULL; return error; } } dpif->n_handlers = n_handlers; } for (i = 0; i < n_handlers; i++) { struct dpif_handler *handler = &dpif->handlers[i]; handler->event_offset = handler->n_events = 0; } keep_channels_nbits = dpif->uc_array_size; keep_channels = bitmap_allocate(keep_channels_nbits); ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); dpif_netlink_port_dump_start__(dpif, &dump); while (!dpif_netlink_port_dump_next__(dpif, &dump, &vport, &buf)) { uint32_t port_no = odp_to_u32(vport.port_no); uint32_t *upcall_pids = NULL; int error; if (port_no >= dpif->uc_array_size || !vport_get_pids(dpif, port_no, &upcall_pids)) { struct nl_sock **socksp = vport_create_socksp(dpif, &error); if (!socksp) { goto error; } error = vport_add_channels(dpif, vport.port_no, socksp); if (error) { VLOG_INFO("%s: could not add channels for port %s", dpif_name(&dpif->dpif), vport.name); vport_del_socksp(dpif, socksp); retval = error; goto error; } upcall_pids = vport_socksp_to_pids(socksp, dpif->n_handlers); free(socksp); } /* Configure the vport to deliver misses to 'sock'. */ if (vport.upcall_pids[0] == 0 || vport.n_upcall_pids != dpif->n_handlers || memcmp(upcall_pids, vport.upcall_pids, n_handlers * sizeof *upcall_pids)) { struct dpif_netlink_vport vport_request; dpif_netlink_vport_init(&vport_request); vport_request.cmd = OVS_VPORT_CMD_SET; vport_request.dp_ifindex = dpif->dp_ifindex; vport_request.port_no = vport.port_no; vport_request.n_upcall_pids = dpif->n_handlers; vport_request.upcall_pids = upcall_pids; error = dpif_netlink_vport_transact(&vport_request, NULL, NULL); if (error) { VLOG_WARN_RL(&error_rl, "%s: failed to set upcall pid on port: %s", dpif_name(&dpif->dpif), ovs_strerror(error)); if (error != ENODEV && error != ENOENT) { retval = error; } else { /* The vport isn't really there, even though the dump says * it is. Probably we just hit a race after a port * disappeared. */ } goto error; } } if (port_no < keep_channels_nbits) { bitmap_set1(keep_channels, port_no); } free(upcall_pids); continue; error: free(upcall_pids); vport_del_channels(dpif, vport.port_no); } nl_dump_done(&dump); ofpbuf_uninit(&buf); /* Discard any saved channels that we didn't reuse. */ for (i = 0; i < keep_channels_nbits; i++) { if (!bitmap_is_set(keep_channels, i)) { vport_del_channels(dpif, u32_to_odp(i)); } } free(keep_channels); return retval; } static int dpif_netlink_recv_set__(struct dpif_netlink *dpif, bool enable) OVS_REQ_WRLOCK(dpif->upcall_lock) { if ((dpif->handlers != NULL) == enable) { return 0; } else if (!enable) { destroy_all_channels(dpif); return 0; } else { return dpif_netlink_refresh_channels(dpif, 1); } } static int dpif_netlink_recv_set(struct dpif *dpif_, bool enable) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); int error; fat_rwlock_wrlock(&dpif->upcall_lock); error = dpif_netlink_recv_set__(dpif, enable); fat_rwlock_unlock(&dpif->upcall_lock); return error; } static int dpif_netlink_handlers_set(struct dpif *dpif_, uint32_t n_handlers) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); int error = 0; #ifdef _WIN32 /* Multiple upcall handlers will be supported once kernel datapath supports * it. */ if (n_handlers > 1) { return error; } #endif fat_rwlock_wrlock(&dpif->upcall_lock); if (dpif->handlers) { error = dpif_netlink_refresh_channels(dpif, n_handlers); } fat_rwlock_unlock(&dpif->upcall_lock); return error; } static int dpif_netlink_queue_to_priority(const struct dpif *dpif OVS_UNUSED, uint32_t queue_id, uint32_t *priority) { if (queue_id < 0xf000) { *priority = TC_H_MAKE(1 << 16, queue_id + 1); return 0; } else { return EINVAL; } } static int parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf, struct dpif_upcall *upcall, int *dp_ifindex) { static const struct nl_policy ovs_packet_policy[] = { /* Always present. */ [OVS_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC, .min_len = ETH_HEADER_LEN }, [OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED }, /* OVS_PACKET_CMD_ACTION only. */ [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true }, [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = { .type = NL_A_NESTED, .optional = true }, [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true } }; struct ovs_header *ovs_header; struct nlattr *a[ARRAY_SIZE(ovs_packet_policy)]; struct nlmsghdr *nlmsg; struct genlmsghdr *genl; struct ofpbuf b; int type; ofpbuf_use_const(&b, buf->data, buf->size); nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); genl = ofpbuf_try_pull(&b, sizeof *genl); ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_packet_family || !nl_policy_parse(&b, 0, ovs_packet_policy, a, ARRAY_SIZE(ovs_packet_policy))) { return EINVAL; } type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION : -1); if (type < 0) { return EINVAL; } /* (Re)set ALL fields of '*upcall' on successful return. */ upcall->type = type; upcall->key = CONST_CAST(struct nlattr *, nl_attr_get(a[OVS_PACKET_ATTR_KEY])); upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]); dpif_flow_hash(&dpif->dpif, upcall->key, upcall->key_len, &upcall->ufid); upcall->userdata = a[OVS_PACKET_ATTR_USERDATA]; upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY]; upcall->actions = a[OVS_PACKET_ATTR_ACTIONS]; upcall->mru = a[OVS_PACKET_ATTR_MRU]; /* Allow overwriting the netlink attribute header without reallocating. */ dp_packet_use_stub(&upcall->packet, CONST_CAST(struct nlattr *, nl_attr_get(a[OVS_PACKET_ATTR_PACKET])) - 1, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]) + sizeof(struct nlattr)); dp_packet_set_data(&upcall->packet, (char *)dp_packet_data(&upcall->packet) + sizeof(struct nlattr)); dp_packet_set_size(&upcall->packet, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET])); *dp_ifindex = ovs_header->dp_ifindex; return 0; } #ifdef _WIN32 #define PACKET_RECV_BATCH_SIZE 50 static int dpif_netlink_recv_windows(struct dpif_netlink *dpif, uint32_t handler_id, struct dpif_upcall *upcall, struct ofpbuf *buf) OVS_REQ_RDLOCK(dpif->upcall_lock) { struct dpif_handler *handler; int read_tries = 0; struct dpif_windows_vport_sock *sock_pool; uint32_t i; if (!dpif->handlers) { return EAGAIN; } /* Only one handler is supported currently. */ if (handler_id >= 1) { return EAGAIN; } if (handler_id >= dpif->n_handlers) { return EAGAIN; } handler = &dpif->handlers[handler_id]; sock_pool = handler->vport_sock_pool; for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { for (;;) { int dp_ifindex; int error; if (++read_tries > PACKET_RECV_BATCH_SIZE) { return EAGAIN; } error = nl_sock_recv(sock_pool[i].nl_sock, buf, false); if (error == ENOBUFS) { /* ENOBUFS typically means that we've received so many * packets that the buffer overflowed. Try again * immediately because there's almost certainly a packet * waiting for us. */ /* XXX: report_loss(dpif, ch, idx, handler_id); */ continue; } /* XXX: ch->last_poll = time_msec(); */ if (error) { if (error == EAGAIN) { break; } return error; } error = parse_odp_packet(dpif, buf, upcall, &dp_ifindex); if (!error && dp_ifindex == dpif->dp_ifindex) { return 0; } else if (error) { return error; } } } return EAGAIN; } #else static int dpif_netlink_recv__(struct dpif_netlink *dpif, uint32_t handler_id, struct dpif_upcall *upcall, struct ofpbuf *buf) OVS_REQ_RDLOCK(dpif->upcall_lock) { struct dpif_handler *handler; int read_tries = 0; if (!dpif->handlers || handler_id >= dpif->n_handlers) { return EAGAIN; } handler = &dpif->handlers[handler_id]; if (handler->event_offset >= handler->n_events) { int retval; handler->event_offset = handler->n_events = 0; do { retval = epoll_wait(handler->epoll_fd, handler->epoll_events, dpif->uc_array_size, 0); } while (retval < 0 && errno == EINTR); if (retval < 0) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "epoll_wait failed (%s)", ovs_strerror(errno)); } else if (retval > 0) { handler->n_events = retval; } } while (handler->event_offset < handler->n_events) { int idx = handler->epoll_events[handler->event_offset].data.u32; struct dpif_channel *ch = &dpif->handlers[handler_id].channels[idx]; handler->event_offset++; for (;;) { int dp_ifindex; int error; if (++read_tries > 50) { return EAGAIN; } error = nl_sock_recv(ch->sock, buf, false); if (error == ENOBUFS) { /* ENOBUFS typically means that we've received so many * packets that the buffer overflowed. Try again * immediately because there's almost certainly a packet * waiting for us. */ report_loss(dpif, ch, idx, handler_id); continue; } ch->last_poll = time_msec(); if (error) { if (error == EAGAIN) { break; } return error; } error = parse_odp_packet(dpif, buf, upcall, &dp_ifindex); if (!error && dp_ifindex == dpif->dp_ifindex) { return 0; } else if (error) { return error; } } } return EAGAIN; } #endif static int dpif_netlink_recv(struct dpif *dpif_, uint32_t handler_id, struct dpif_upcall *upcall, struct ofpbuf *buf) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); int error; fat_rwlock_rdlock(&dpif->upcall_lock); #ifdef _WIN32 error = dpif_netlink_recv_windows(dpif, handler_id, upcall, buf); #else error = dpif_netlink_recv__(dpif, handler_id, upcall, buf); #endif fat_rwlock_unlock(&dpif->upcall_lock); return error; } static void dpif_netlink_recv_wait__(struct dpif_netlink *dpif, uint32_t handler_id) OVS_REQ_RDLOCK(dpif->upcall_lock) { #ifdef _WIN32 uint32_t i; struct dpif_windows_vport_sock *sock_pool = dpif->handlers[handler_id].vport_sock_pool; /* Only one handler is supported currently. */ if (handler_id >= 1) { return; } for (i = 0; i < VPORT_SOCK_POOL_SIZE; i++) { nl_sock_wait(sock_pool[i].nl_sock, POLLIN); } #else if (dpif->handlers && handler_id < dpif->n_handlers) { struct dpif_handler *handler = &dpif->handlers[handler_id]; poll_fd_wait(handler->epoll_fd, POLLIN); } #endif } static void dpif_netlink_recv_wait(struct dpif *dpif_, uint32_t handler_id) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); fat_rwlock_rdlock(&dpif->upcall_lock); dpif_netlink_recv_wait__(dpif, handler_id); fat_rwlock_unlock(&dpif->upcall_lock); } static void dpif_netlink_recv_purge__(struct dpif_netlink *dpif) OVS_REQ_WRLOCK(dpif->upcall_lock) { if (dpif->handlers) { size_t i, j; for (i = 0; i < dpif->uc_array_size; i++ ) { if (!dpif->handlers[0].channels[i].sock) { continue; } for (j = 0; j < dpif->n_handlers; j++) { nl_sock_drain(dpif->handlers[j].channels[i].sock); } } } } static void dpif_netlink_recv_purge(struct dpif *dpif_) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); fat_rwlock_wrlock(&dpif->upcall_lock); dpif_netlink_recv_purge__(dpif); fat_rwlock_unlock(&dpif->upcall_lock); } static char * dpif_netlink_get_datapath_version(void) { char *version_str = NULL; #ifdef __linux__ #define MAX_VERSION_STR_SIZE 80 #define LINUX_DATAPATH_VERSION_FILE "/sys/module/openvswitch/version" FILE *f; f = fopen(LINUX_DATAPATH_VERSION_FILE, "r"); if (f) { char *newline; char version[MAX_VERSION_STR_SIZE]; if (fgets(version, MAX_VERSION_STR_SIZE, f)) { newline = strchr(version, '\n'); if (newline) { *newline = '\0'; } version_str = xstrdup(version); } fclose(f); } #endif return version_str; } #ifdef __linux__ struct dpif_netlink_ct_dump_state { struct ct_dpif_dump_state up; struct nl_ct_dump_state *nl_ct_dump; }; static int dpif_netlink_ct_dump_start(struct dpif *dpif OVS_UNUSED, struct ct_dpif_dump_state **dump_, const uint16_t *zone) { struct dpif_netlink_ct_dump_state *dump; int err; dump = xzalloc(sizeof *dump); err = nl_ct_dump_start(&dump->nl_ct_dump, zone); if (err) { free(dump); return err; } *dump_ = &dump->up; return 0; } static int dpif_netlink_ct_dump_next(struct dpif *dpif OVS_UNUSED, struct ct_dpif_dump_state *dump_, struct ct_dpif_entry *entry) { struct dpif_netlink_ct_dump_state *dump; INIT_CONTAINER(dump, dump_, up); return nl_ct_dump_next(dump->nl_ct_dump, entry); } static int dpif_netlink_ct_dump_done(struct dpif *dpif OVS_UNUSED, struct ct_dpif_dump_state *dump_) { struct dpif_netlink_ct_dump_state *dump; int err; INIT_CONTAINER(dump, dump_, up); err = nl_ct_dump_done(dump->nl_ct_dump); free(dump); return err; } static int dpif_netlink_ct_flush(struct dpif *dpif OVS_UNUSED, const uint16_t *zone) { if (zone) { return nl_ct_flush_zone(*zone); } else { return nl_ct_flush(); } } #endif const struct dpif_class dpif_netlink_class = { "system", NULL, /* init */ dpif_netlink_enumerate, NULL, dpif_netlink_open, dpif_netlink_close, dpif_netlink_destroy, dpif_netlink_run, NULL, /* wait */ dpif_netlink_get_stats, dpif_netlink_port_add, dpif_netlink_port_del, dpif_netlink_port_query_by_number, dpif_netlink_port_query_by_name, dpif_netlink_port_get_pid, dpif_netlink_port_dump_start, dpif_netlink_port_dump_next, dpif_netlink_port_dump_done, dpif_netlink_port_poll, dpif_netlink_port_poll_wait, dpif_netlink_flow_flush, dpif_netlink_flow_dump_create, dpif_netlink_flow_dump_destroy, dpif_netlink_flow_dump_thread_create, dpif_netlink_flow_dump_thread_destroy, dpif_netlink_flow_dump_next, dpif_netlink_operate, dpif_netlink_recv_set, dpif_netlink_handlers_set, NULL, /* poll_thread_set */ dpif_netlink_queue_to_priority, dpif_netlink_recv, dpif_netlink_recv_wait, dpif_netlink_recv_purge, NULL, /* register_dp_purge_cb */ NULL, /* register_upcall_cb */ NULL, /* enable_upcall */ NULL, /* disable_upcall */ dpif_netlink_get_datapath_version, /* get_datapath_version */ #ifdef __linux__ dpif_netlink_ct_dump_start, dpif_netlink_ct_dump_next, dpif_netlink_ct_dump_done, dpif_netlink_ct_flush, #else NULL, /* ct_dump_start */ NULL, /* ct_dump_next */ NULL, /* ct_dump_done */ NULL, /* ct_flush */ #endif }; static int dpif_netlink_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int error; if (ovsthread_once_start(&once)) { error = nl_lookup_genl_family(OVS_DATAPATH_FAMILY, &ovs_datapath_family); if (error) { VLOG_ERR("Generic Netlink family '%s' does not exist. " "The Open vSwitch kernel module is probably not loaded.", OVS_DATAPATH_FAMILY); } if (!error) { error = nl_lookup_genl_family(OVS_VPORT_FAMILY, &ovs_vport_family); } if (!error) { error = nl_lookup_genl_family(OVS_FLOW_FAMILY, &ovs_flow_family); } if (!error) { error = nl_lookup_genl_family(OVS_PACKET_FAMILY, &ovs_packet_family); } if (!error) { error = nl_lookup_genl_mcgroup(OVS_VPORT_FAMILY, OVS_VPORT_MCGROUP, &ovs_vport_mcgroup); } ovsthread_once_done(&once); } return error; } bool dpif_netlink_is_internal_device(const char *name) { struct dpif_netlink_vport reply; struct ofpbuf *buf; int error; error = dpif_netlink_vport_get(name, &reply, &buf); if (!error) { ofpbuf_delete(buf); } else if (error != ENODEV && error != ENOENT) { VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)", name, ovs_strerror(error)); } return reply.type == OVS_VPORT_TYPE_INTERNAL; } /* Parses the contents of 'buf', which contains a "struct ovs_header" followed * by Netlink attributes, into 'vport'. Returns 0 if successful, otherwise a * positive errno value. * * 'vport' will contain pointers into 'buf', so the caller should not free * 'buf' while 'vport' is still in use. */ static int dpif_netlink_vport_from_ofpbuf(struct dpif_netlink_vport *vport, const struct ofpbuf *buf) { static const struct nl_policy ovs_vport_policy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32 }, [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32 }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC }, [OVS_VPORT_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_vport_stats), .optional = true }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = true }, }; struct nlattr *a[ARRAY_SIZE(ovs_vport_policy)]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; struct genlmsghdr *genl; struct ofpbuf b; dpif_netlink_vport_init(vport); ofpbuf_use_const(&b, buf->data, buf->size); nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); genl = ofpbuf_try_pull(&b, sizeof *genl); ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_vport_family || !nl_policy_parse(&b, 0, ovs_vport_policy, a, ARRAY_SIZE(ovs_vport_policy))) { return EINVAL; } vport->cmd = genl->cmd; vport->dp_ifindex = ovs_header->dp_ifindex; vport->port_no = nl_attr_get_odp_port(a[OVS_VPORT_ATTR_PORT_NO]); vport->type = nl_attr_get_u32(a[OVS_VPORT_ATTR_TYPE]); vport->name = nl_attr_get_string(a[OVS_VPORT_ATTR_NAME]); if (a[OVS_VPORT_ATTR_UPCALL_PID]) { vport->n_upcall_pids = nl_attr_get_size(a[OVS_VPORT_ATTR_UPCALL_PID]) / (sizeof *vport->upcall_pids); vport->upcall_pids = nl_attr_get(a[OVS_VPORT_ATTR_UPCALL_PID]); } if (a[OVS_VPORT_ATTR_STATS]) { vport->stats = nl_attr_get(a[OVS_VPORT_ATTR_STATS]); } if (a[OVS_VPORT_ATTR_OPTIONS]) { vport->options = nl_attr_get(a[OVS_VPORT_ATTR_OPTIONS]); vport->options_len = nl_attr_get_size(a[OVS_VPORT_ATTR_OPTIONS]); } return 0; } /* Appends to 'buf' (which must initially be empty) a "struct ovs_header" * followed by Netlink attributes corresponding to 'vport'. */ static void dpif_netlink_vport_to_ofpbuf(const struct dpif_netlink_vport *vport, struct ofpbuf *buf) { struct ovs_header *ovs_header; nl_msg_put_genlmsghdr(buf, 0, ovs_vport_family, NLM_F_REQUEST | NLM_F_ECHO, vport->cmd, OVS_VPORT_VERSION); ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); ovs_header->dp_ifindex = vport->dp_ifindex; if (vport->port_no != ODPP_NONE) { nl_msg_put_odp_port(buf, OVS_VPORT_ATTR_PORT_NO, vport->port_no); } if (vport->type != OVS_VPORT_TYPE_UNSPEC) { nl_msg_put_u32(buf, OVS_VPORT_ATTR_TYPE, vport->type); } if (vport->name) { nl_msg_put_string(buf, OVS_VPORT_ATTR_NAME, vport->name); } if (vport->upcall_pids) { nl_msg_put_unspec(buf, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_pids, vport->n_upcall_pids * sizeof *vport->upcall_pids); } if (vport->stats) { nl_msg_put_unspec(buf, OVS_VPORT_ATTR_STATS, vport->stats, sizeof *vport->stats); } if (vport->options) { nl_msg_put_nested(buf, OVS_VPORT_ATTR_OPTIONS, vport->options, vport->options_len); } } /* Clears 'vport' to "empty" values. */ void dpif_netlink_vport_init(struct dpif_netlink_vport *vport) { memset(vport, 0, sizeof *vport); vport->port_no = ODPP_NONE; } /* Executes 'request' in the kernel datapath. If the command fails, returns a * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 * without doing anything else. If 'reply' and 'bufp' are nonnull, then the * result of the command is expected to be an ovs_vport also, which is decoded * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the * reply is no longer needed ('reply' will contain pointers into '*bufp'). */ int dpif_netlink_vport_transact(const struct dpif_netlink_vport *request, struct dpif_netlink_vport *reply, struct ofpbuf **bufp) { struct ofpbuf *request_buf; int error; ovs_assert((reply != NULL) == (bufp != NULL)); error = dpif_netlink_init(); if (error) { if (reply) { *bufp = NULL; dpif_netlink_vport_init(reply); } return error; } request_buf = ofpbuf_new(1024); dpif_netlink_vport_to_ofpbuf(request, request_buf); error = nl_transact(NETLINK_GENERIC, request_buf, bufp); ofpbuf_delete(request_buf); if (reply) { if (!error) { error = dpif_netlink_vport_from_ofpbuf(reply, *bufp); } if (error) { dpif_netlink_vport_init(reply); ofpbuf_delete(*bufp); *bufp = NULL; } } return error; } /* Obtains information about the kernel vport named 'name' and stores it into * '*reply' and '*bufp'. The caller must free '*bufp' when the reply is no * longer needed ('reply' will contain pointers into '*bufp'). */ int dpif_netlink_vport_get(const char *name, struct dpif_netlink_vport *reply, struct ofpbuf **bufp) { struct dpif_netlink_vport request; dpif_netlink_vport_init(&request); request.cmd = OVS_VPORT_CMD_GET; request.name = name; return dpif_netlink_vport_transact(&request, reply, bufp); } /* Parses the contents of 'buf', which contains a "struct ovs_header" followed * by Netlink attributes, into 'dp'. Returns 0 if successful, otherwise a * positive errno value. * * 'dp' will contain pointers into 'buf', so the caller should not free 'buf' * while 'dp' is still in use. */ static int dpif_netlink_dp_from_ofpbuf(struct dpif_netlink_dp *dp, const struct ofpbuf *buf) { static const struct nl_policy ovs_datapath_policy[] = { [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, [OVS_DP_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_dp_stats), .optional = true }, [OVS_DP_ATTR_MEGAFLOW_STATS] = { NL_POLICY_FOR(struct ovs_dp_megaflow_stats), .optional = true }, }; struct nlattr *a[ARRAY_SIZE(ovs_datapath_policy)]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; struct genlmsghdr *genl; struct ofpbuf b; dpif_netlink_dp_init(dp); ofpbuf_use_const(&b, buf->data, buf->size); nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); genl = ofpbuf_try_pull(&b, sizeof *genl); ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_datapath_family || !nl_policy_parse(&b, 0, ovs_datapath_policy, a, ARRAY_SIZE(ovs_datapath_policy))) { return EINVAL; } dp->cmd = genl->cmd; dp->dp_ifindex = ovs_header->dp_ifindex; dp->name = nl_attr_get_string(a[OVS_DP_ATTR_NAME]); if (a[OVS_DP_ATTR_STATS]) { dp->stats = nl_attr_get(a[OVS_DP_ATTR_STATS]); } if (a[OVS_DP_ATTR_MEGAFLOW_STATS]) { dp->megaflow_stats = nl_attr_get(a[OVS_DP_ATTR_MEGAFLOW_STATS]); } return 0; } /* Appends to 'buf' the Generic Netlink message described by 'dp'. */ static void dpif_netlink_dp_to_ofpbuf(const struct dpif_netlink_dp *dp, struct ofpbuf *buf) { struct ovs_header *ovs_header; nl_msg_put_genlmsghdr(buf, 0, ovs_datapath_family, NLM_F_REQUEST | NLM_F_ECHO, dp->cmd, OVS_DATAPATH_VERSION); ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); ovs_header->dp_ifindex = dp->dp_ifindex; if (dp->name) { nl_msg_put_string(buf, OVS_DP_ATTR_NAME, dp->name); } if (dp->upcall_pid) { nl_msg_put_u32(buf, OVS_DP_ATTR_UPCALL_PID, *dp->upcall_pid); } if (dp->user_features) { nl_msg_put_u32(buf, OVS_DP_ATTR_USER_FEATURES, dp->user_features); } /* Skip OVS_DP_ATTR_STATS since we never have a reason to serialize it. */ } /* Clears 'dp' to "empty" values. */ static void dpif_netlink_dp_init(struct dpif_netlink_dp *dp) { memset(dp, 0, sizeof *dp); } static void dpif_netlink_dp_dump_start(struct nl_dump *dump) { struct dpif_netlink_dp request; struct ofpbuf *buf; dpif_netlink_dp_init(&request); request.cmd = OVS_DP_CMD_GET; buf = ofpbuf_new(1024); dpif_netlink_dp_to_ofpbuf(&request, buf); nl_dump_start(dump, NETLINK_GENERIC, buf); ofpbuf_delete(buf); } /* Executes 'request' in the kernel datapath. If the command fails, returns a * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 * without doing anything else. If 'reply' and 'bufp' are nonnull, then the * result of the command is expected to be of the same form, which is decoded * and stored in '*reply' and '*bufp'. The caller must free '*bufp' when the * reply is no longer needed ('reply' will contain pointers into '*bufp'). */ static int dpif_netlink_dp_transact(const struct dpif_netlink_dp *request, struct dpif_netlink_dp *reply, struct ofpbuf **bufp) { struct ofpbuf *request_buf; int error; ovs_assert((reply != NULL) == (bufp != NULL)); request_buf = ofpbuf_new(1024); dpif_netlink_dp_to_ofpbuf(request, request_buf); error = nl_transact(NETLINK_GENERIC, request_buf, bufp); ofpbuf_delete(request_buf); if (reply) { dpif_netlink_dp_init(reply); if (!error) { error = dpif_netlink_dp_from_ofpbuf(reply, *bufp); } if (error) { ofpbuf_delete(*bufp); *bufp = NULL; } } return error; } /* Obtains information about 'dpif_' and stores it into '*reply' and '*bufp'. * The caller must free '*bufp' when the reply is no longer needed ('reply' * will contain pointers into '*bufp'). */ static int dpif_netlink_dp_get(const struct dpif *dpif_, struct dpif_netlink_dp *reply, struct ofpbuf **bufp) { struct dpif_netlink *dpif = dpif_netlink_cast(dpif_); struct dpif_netlink_dp request; dpif_netlink_dp_init(&request); request.cmd = OVS_DP_CMD_GET; request.dp_ifindex = dpif->dp_ifindex; return dpif_netlink_dp_transact(&request, reply, bufp); } /* Parses the contents of 'buf', which contains a "struct ovs_header" followed * by Netlink attributes, into 'flow'. Returns 0 if successful, otherwise a * positive errno value. * * 'flow' will contain pointers into 'buf', so the caller should not free 'buf' * while 'flow' is still in use. */ static int dpif_netlink_flow_from_ofpbuf(struct dpif_netlink_flow *flow, const struct ofpbuf *buf) { static const struct nl_policy ovs_flow_policy[__OVS_FLOW_ATTR_MAX] = { [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED, .optional = true }, [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true }, [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats), .optional = true }, [OVS_FLOW_ATTR_TCP_FLAGS] = { .type = NL_A_U8, .optional = true }, [OVS_FLOW_ATTR_USED] = { .type = NL_A_U64, .optional = true }, [OVS_FLOW_ATTR_UFID] = { .type = NL_A_UNSPEC, .optional = true, .min_len = sizeof(ovs_u128) }, /* The kernel never uses OVS_FLOW_ATTR_CLEAR. */ /* The kernel never uses OVS_FLOW_ATTR_PROBE. */ /* The kernel never uses OVS_FLOW_ATTR_UFID_FLAGS. */ }; struct nlattr *a[ARRAY_SIZE(ovs_flow_policy)]; struct ovs_header *ovs_header; struct nlmsghdr *nlmsg; struct genlmsghdr *genl; struct ofpbuf b; dpif_netlink_flow_init(flow); ofpbuf_use_const(&b, buf->data, buf->size); nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); genl = ofpbuf_try_pull(&b, sizeof *genl); ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_flow_family || !nl_policy_parse(&b, 0, ovs_flow_policy, a, ARRAY_SIZE(ovs_flow_policy))) { return EINVAL; } if (!a[OVS_FLOW_ATTR_KEY] && !a[OVS_FLOW_ATTR_UFID]) { return EINVAL; } flow->nlmsg_flags = nlmsg->nlmsg_flags; flow->dp_ifindex = ovs_header->dp_ifindex; if (a[OVS_FLOW_ATTR_KEY]) { flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]); flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]); } if (a[OVS_FLOW_ATTR_UFID]) { const ovs_u128 *ufid; ufid = nl_attr_get_unspec(a[OVS_FLOW_ATTR_UFID], nl_attr_get_size(a[OVS_FLOW_ATTR_UFID])); flow->ufid = *ufid; flow->ufid_present = true; } if (a[OVS_FLOW_ATTR_MASK]) { flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]); flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]); } if (a[OVS_FLOW_ATTR_ACTIONS]) { flow->actions = nl_attr_get(a[OVS_FLOW_ATTR_ACTIONS]); flow->actions_len = nl_attr_get_size(a[OVS_FLOW_ATTR_ACTIONS]); } if (a[OVS_FLOW_ATTR_STATS]) { flow->stats = nl_attr_get(a[OVS_FLOW_ATTR_STATS]); } if (a[OVS_FLOW_ATTR_TCP_FLAGS]) { flow->tcp_flags = nl_attr_get(a[OVS_FLOW_ATTR_TCP_FLAGS]); } if (a[OVS_FLOW_ATTR_USED]) { flow->used = nl_attr_get(a[OVS_FLOW_ATTR_USED]); } return 0; } /* Appends to 'buf' (which must initially be empty) a "struct ovs_header" * followed by Netlink attributes corresponding to 'flow'. */ static void dpif_netlink_flow_to_ofpbuf(const struct dpif_netlink_flow *flow, struct ofpbuf *buf) { struct ovs_header *ovs_header; nl_msg_put_genlmsghdr(buf, 0, ovs_flow_family, NLM_F_REQUEST | flow->nlmsg_flags, flow->cmd, OVS_FLOW_VERSION); ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); ovs_header->dp_ifindex = flow->dp_ifindex; if (flow->ufid_present) { nl_msg_put_unspec(buf, OVS_FLOW_ATTR_UFID, &flow->ufid, sizeof flow->ufid); } if (flow->ufid_terse) { nl_msg_put_u32(buf, OVS_FLOW_ATTR_UFID_FLAGS, OVS_UFID_F_OMIT_KEY | OVS_UFID_F_OMIT_MASK | OVS_UFID_F_OMIT_ACTIONS); } if (!flow->ufid_terse || !flow->ufid_present) { if (flow->key_len) { nl_msg_put_unspec(buf, OVS_FLOW_ATTR_KEY, flow->key, flow->key_len); } if (flow->mask_len) { nl_msg_put_unspec(buf, OVS_FLOW_ATTR_MASK, flow->mask, flow->mask_len); } if (flow->actions || flow->actions_len) { nl_msg_put_unspec(buf, OVS_FLOW_ATTR_ACTIONS, flow->actions, flow->actions_len); } } /* We never need to send these to the kernel. */ ovs_assert(!flow->stats); ovs_assert(!flow->tcp_flags); ovs_assert(!flow->used); if (flow->clear) { nl_msg_put_flag(buf, OVS_FLOW_ATTR_CLEAR); } if (flow->probe) { nl_msg_put_flag(buf, OVS_FLOW_ATTR_PROBE); } } /* Clears 'flow' to "empty" values. */ static void dpif_netlink_flow_init(struct dpif_netlink_flow *flow) { memset(flow, 0, sizeof *flow); } /* Executes 'request' in the kernel datapath. If the command fails, returns a * positive errno value. Otherwise, if 'reply' and 'bufp' are null, returns 0 * without doing anything else. If 'reply' and 'bufp' are nonnull, then the * result of the command is expected to be a flow also, which is decoded and * stored in '*reply' and '*bufp'. The caller must free '*bufp' when the reply * is no longer needed ('reply' will contain pointers into '*bufp'). */ static int dpif_netlink_flow_transact(struct dpif_netlink_flow *request, struct dpif_netlink_flow *reply, struct ofpbuf **bufp) { struct ofpbuf *request_buf; int error; ovs_assert((reply != NULL) == (bufp != NULL)); if (reply) { request->nlmsg_flags |= NLM_F_ECHO; } request_buf = ofpbuf_new(1024); dpif_netlink_flow_to_ofpbuf(request, request_buf); error = nl_transact(NETLINK_GENERIC, request_buf, bufp); ofpbuf_delete(request_buf); if (reply) { if (!error) { error = dpif_netlink_flow_from_ofpbuf(reply, *bufp); } if (error) { dpif_netlink_flow_init(reply); ofpbuf_delete(*bufp); *bufp = NULL; } } return error; } static void dpif_netlink_flow_get_stats(const struct dpif_netlink_flow *flow, struct dpif_flow_stats *stats) { if (flow->stats) { stats->n_packets = get_32aligned_u64(&flow->stats->n_packets); stats->n_bytes = get_32aligned_u64(&flow->stats->n_bytes); } else { stats->n_packets = 0; stats->n_bytes = 0; } stats->used = flow->used ? get_32aligned_u64(flow->used) : 0; stats->tcp_flags = flow->tcp_flags ? *flow->tcp_flags : 0; } /* Logs information about a packet that was recently lost in 'ch' (in * 'dpif_'). */ static void report_loss(struct dpif_netlink *dpif, struct dpif_channel *ch, uint32_t ch_idx, uint32_t handler_id) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); struct ds s; if (VLOG_DROP_WARN(&rl)) { return; } ds_init(&s); if (ch->last_poll != LLONG_MIN) { ds_put_format(&s, " (last polled %lld ms ago)", time_msec() - ch->last_poll); } VLOG_WARN("%s: lost packet on port channel %u of handler %u", dpif_name(&dpif->dpif), ch_idx, handler_id); ds_destroy(&s); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-x86_64.h0000644000000000000000000000013213534540071017600 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.821853721 openvswitch-2.5.9/lib/ovs-atomic-x86_64.h0000644000175000017500000004005513534540071021272 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on x86_64 with GCC. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #define OVS_ATOMIC_X86_64_IMPL 1 /* * x86_64 Memory model (http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html): * * - 1, 2, 4, and 8 byte loads and stores are atomic on aligned memory. * - Loads are not reordered with other loads. * - Stores are not reordered with OLDER loads. * - Loads may be reordered with OLDER stores to a different memory location, * but not with OLDER stores to the same memory location. * - Stores are not reordered with other stores, except for special * instructions (CLFLUSH, streaming stores, fast string operations). * Most of these are not emitted by compilers, and as long as the * atomic stores are not combined with any other stores, even the allowed * reordering of the stores by a single fast string operation (e.g., "stos") * is not a problem. * - Neither loads nor stores are reordered with locked instructions. * - Loads cannot pass earlier LFENCE or MFENCE instructions. * - Stores cannot pass earlier LFENCE, SFENCE, or MFENCE instructions. * - LFENCE instruction cannot pass earlier loads. * - SFENCE instruction cannot pass earlier stores. * - MFENCE instruction cannot pass earlier loads or stores. * - Stores by a single processor are observed in the same order by all * processors. * - (Unlocked) Stores from different processors are NOT ordered. * - Memory ordering obeys causality (memory ordering respects transitive * visibility). * - Any two stores are seen in a consistent order by processors other than * the those performing the stores. * - Locked instructions have total order. * * These rules imply that: * * - Locked instructions are not needed for aligned loads or stores to make * them atomic. * - All stores have release semantics; none of the preceding stores or loads * can be reordered with following stores. Following loads could still be * reordered to happen before the store, but that is not a violation of the * release semantics. * - All loads from a given memory location have acquire semantics with * respect to the stores on the same memory location; none of the following * loads or stores can be reordered with the load. Preceding stores to a * different memory location MAY be reordered with the load, but that is not * a violation of the acquire semantics (i.e., the loads and stores of two * critical sections guarded by a different memory location can overlap). * - Locked instructions serve as CPU memory barriers by themselves. * - Locked stores implement the sequential consistency memory order. Using * locked instructions when seq_cst memory order is requested allows normal * loads to observe the stores in the same (total) order without using CPU * memory barrier after the loads. * * NOTE: Some older AMD Opteron processors have a bug that violates the * acquire semantics described above. The bug manifests as an unlocked * read-modify-write operation following a "semaphore operation" operating * on data that existed before entering the critical section; i.e., the * preceding "semaphore operation" fails to function as an acquire barrier. * The affected CPUs are AMD family 15, models 32 to 63. * * Ref. http://support.amd.com/TechDocs/25759.pdf errata #147. */ /* Barriers. */ #define compiler_barrier() asm volatile(" " : : : "memory") #define cpu_barrier() asm volatile("mfence;" : : : "memory") /* * The 'volatile' keyword prevents the compiler from keeping the atomic * value in a register, and generates a new memory access for each atomic * operation. This allows the implementations of memory_order_relaxed and * memory_order_consume to avoid issuing a compiler memory barrier, allowing * full optimization of all surrounding non-atomic variables. * * The placement of the 'volatile' keyword after the 'TYPE' below is highly * significant when the TYPE is a pointer type. In that case we want the * pointer to be declared volatile, not the data type that is being pointed * at! */ #define ATOMIC(TYPE) TYPE volatile /* Memory ordering. Must be passed in as a constant. */ typedef enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; #define ATOMIC_BOOL_LOCK_FREE 2 #define ATOMIC_CHAR_LOCK_FREE 2 #define ATOMIC_SHORT_LOCK_FREE 2 #define ATOMIC_INT_LOCK_FREE 2 #define ATOMIC_LONG_LOCK_FREE 2 #define ATOMIC_LLONG_LOCK_FREE 2 #define ATOMIC_POINTER_LOCK_FREE 2 #define IS_LOCKLESS_ATOMIC(OBJECT) \ (sizeof(OBJECT) <= 8 && IS_POW2(sizeof(OBJECT))) #define ATOMIC_VAR_INIT(VALUE) VALUE #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) /* * The memory_model_relaxed does not need a compiler barrier, if the * atomic operation can otherwise be guaranteed to not be moved with * respect to other atomic operations on the same memory location. Using * the 'volatile' keyword in the definition of the atomic types * accomplishes this, as memory accesses to volatile data may not be * optimized away, or be reordered with other volatile accesses. * * On x86 also memory_order_consume is automatic, and data dependency on a * volatile atomic variable means that the compiler optimizations should not * cause problems. That is, the compiler should not speculate the value of * the atomic_read, as it is going to read it from the memory anyway. * This allows omiting the compiler memory barrier on atomic_reads with * memory_order_consume. This matches the definition of * smp_read_barrier_depends() in Linux kernel as a nop for x86, and its usage * in rcu_dereference(). * * We use this same logic below to choose inline assembly statements with or * without a compiler memory barrier. */ static inline void atomic_compiler_barrier(memory_order order) { if (order > memory_order_consume) { compiler_barrier(); } } static inline void atomic_thread_fence(memory_order order) { if (order == memory_order_seq_cst) { cpu_barrier(); } else { atomic_compiler_barrier(order); } } static inline void atomic_signal_fence(memory_order order) { atomic_compiler_barrier(order); } #define atomic_is_lock_free(OBJ) \ ((void) *(OBJ), \ IS_LOCKLESS_ATOMIC(*(OBJ)) ? 2 : 0) #define atomic_exchange__(DST, SRC, ORDER) \ ({ \ typeof(DST) dst___ = (DST); \ typeof(*(DST)) src___ = (SRC); \ \ if ((ORDER) > memory_order_consume) { \ asm volatile("xchg %1,%0 ; " \ "# atomic_exchange__" \ : "+r" (src___), /* 0 */ \ "+m" (*dst___) /* 1 */ \ :: "memory"); \ } else { \ asm volatile("xchg %1,%0 ; " \ "# atomic_exchange__" \ : "+r" (src___), /* 0 */ \ "+m" (*dst___)); /* 1 */ \ } \ src___; \ }) /* Atomic store: Valid memory models are: * * memory_order_relaxed, memory_order_release, and * memory_order_seq_cst. */ #define atomic_store_explicit(DST, SRC, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(*(DST)) src__ = (SRC); \ \ if ((ORDER) != memory_order_seq_cst) { \ atomic_compiler_barrier(ORDER); \ *dst__ = src__; \ } else { \ atomic_exchange__(dst__, src__, ORDER); \ } \ (void) 0; \ }) #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) /* Atomic read: Valid memory models are: * * memory_order_relaxed, memory_order_consume, memory_model_acquire, * and memory_order_seq_cst. */ #define atomic_read_explicit(SRC, DST, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(SRC) src__ = (SRC); \ \ *dst__ = *src__; \ atomic_compiler_barrier(ORDER); \ (void) 0; \ }) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_compare_exchange__(DST, EXP, SRC, RES, CLOB) \ asm volatile("lock; cmpxchg %3,%1 ; " \ " sete %0 " \ "# atomic_compare_exchange__" \ : "=q" (RES), /* 0 */ \ "+m" (*DST), /* 1 */ \ "+a" (EXP) /* 2 */ \ : "r" (SRC) /* 3 */ \ : CLOB, "cc") /* All memory models are valid for read-modify-write operations. * * Valid memory models for the read operation of the current value in * the failure case are the same as for atomic read, but can not be * stronger than the success memory model. * ORD_FAIL is ignored, as atomic_compare_exchange__ already implements * at least as strong a barrier as allowed for ORD_FAIL in all cases. */ #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORDER, ORD_FAIL) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(DST) expp__ = (EXP); \ typeof(*(DST)) src__ = (SRC); \ typeof(*(DST)) exp__ = *expp__; \ uint8_t res__; \ (void)ORD_FAIL; \ \ if ((ORDER) > memory_order_consume) { \ atomic_compare_exchange__(dst__, exp__, src__, res__, \ "memory"); \ } else { \ atomic_compare_exchange__(dst__, exp__, src__, res__, \ "cc"); \ } \ if (!res__) { \ *expp__ = exp__; \ } \ (bool)res__; \ }) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \ memory_order_seq_cst, \ memory_order_seq_cst) #define atomic_compare_exchange_weak \ atomic_compare_exchange_strong #define atomic_compare_exchange_weak_explicit \ atomic_compare_exchange_strong_explicit #define atomic_add__(RMW, ARG, CLOB) \ asm volatile("lock; xadd %0,%1 ; " \ "# atomic_add__ " \ : "+r" (ARG), /* 0 */ \ "+m" (*RMW) /* 1 */ \ :: CLOB, "cc") #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \ ({ \ typeof(RMW) rmw__ = (RMW); \ typeof(*(RMW)) arg__ = (ARG); \ \ if ((ORDER) > memory_order_consume) { \ atomic_add__(rmw__, arg__, "memory"); \ } else { \ atomic_add__(rmw__, arg__, "cc"); \ } \ *(ORIG) = arg__; \ }) #define atomic_add(RMW, ARG, ORIG) \ atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \ atomic_add_explicit(RMW, -(ARG), ORIG, ORDER) #define atomic_sub(RMW, ARG, ORIG) \ atomic_sub_explicit(RMW, ARG, ORIG, memory_order_seq_cst) /* We could use simple locked instructions if the original value was not * needed. */ #define atomic_op__(RMW, OP, ARG, ORIG, ORDER) \ ({ \ typeof(RMW) rmw__ = (RMW); \ typeof(ARG) arg__ = (ARG); \ \ typeof(*(RMW)) val__; \ \ atomic_read_explicit(rmw__, &val__, memory_order_relaxed); \ do { \ } while (!atomic_compare_exchange_weak_explicit(rmw__, &val__, \ val__ OP arg__, \ ORDER, \ memory_order_relaxed)); \ *(ORIG) = val__; \ }) #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, |, ARG, ORIG, ORDER) #define atomic_or(RMW, ARG, ORIG) \ atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, ^, ARG, ORIG, ORDER) #define atomic_xor(RMW, ARG, ORIG) \ atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst) #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \ atomic_op__(RMW, &, ARG, ORIG, ORDER) #define atomic_and(RMW, ARG, ORIG) \ atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst) /* atomic_flag */ typedef ATOMIC(int) atomic_flag; #define ATOMIC_FLAG_INIT { false } #define atomic_flag_test_and_set_explicit(FLAG, ORDER) \ ((bool)atomic_exchange__(FLAG, 1, ORDER)) #define atomic_flag_test_and_set(FLAG) \ atomic_flag_test_and_set_explicit(FLAG, memory_order_seq_cst) #define atomic_flag_clear_explicit(FLAG, ORDER) \ atomic_store_explicit(FLAG, 0, ORDER) #define atomic_flag_clear(FLAG) \ atomic_flag_clear_explicit(FLAG, memory_order_seq_cst) openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-thread.h0000644000000000000000000000013213534540071016637 xustar0030 mtime=1567801401.549682257 30 atime=1567801402.093686252 30 ctime=1567801424.829853781 openvswitch-2.5.9/lib/ovs-thread.h0000644000175000017500000006075213534540071020337 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_THREAD_H #define OVS_THREAD_H 1 #include #include #include #include "ovs-atomic.h" #include "openvswitch/thread.h" #include "util.h" struct seq; /* Poll-block()-able barrier similar to pthread_barrier_t. */ struct ovs_barrier { uint32_t size; /* Number of threads to wait. */ atomic_count count; /* Number of threads already hit the barrier. */ struct seq *seq; }; /* Wrappers for pthread_mutex_*() that abort the process on any error. * This is still needed when ovs-atomic-pthreads.h is used. */ void xpthread_mutex_lock(pthread_mutex_t *mutex); void xpthread_mutex_unlock(pthread_mutex_t *mutex); /* Wrappers for pthread_mutexattr_*() that abort the process on any error. */ void xpthread_mutexattr_init(pthread_mutexattr_t *); void xpthread_mutexattr_destroy(pthread_mutexattr_t *); void xpthread_mutexattr_settype(pthread_mutexattr_t *, int type); void xpthread_mutexattr_gettype(pthread_mutexattr_t *, int *typep); /* Read-write lock. * * An ovs_rwlock does not support recursive readers, because POSIX allows * taking the reader lock recursively to deadlock when a thread is waiting on * the write-lock. (NetBSD does deadlock.) glibc rwlocks in their default * configuration do not deadlock, but ovs_rwlock_init() initializes rwlocks as * non-recursive (which will deadlock) for two reasons: * * - glibc only provides fairness to writers in this mode. * * - It's better to find bugs in the primary Open vSwitch target rather * than exposing them only to porters. */ struct OVS_LOCKABLE ovs_rwlock { pthread_rwlock_t lock; const char *where; /* NULL if and only if uninitialized. */ }; /* Initializer. */ #ifdef PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP #define OVS_RWLOCK_INITIALIZER \ { PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP, "" } #else #define OVS_RWLOCK_INITIALIZER { PTHREAD_RWLOCK_INITIALIZER, "" } #endif /* ovs_rwlock functions analogous to pthread_rwlock_*() functions. * * Most of these functions abort the process with an error message on any * error. The "trylock" functions are exception: they pass through a 0 or * EBUSY return value to the caller and abort on any other error. */ void ovs_rwlock_init(const struct ovs_rwlock *); void ovs_rwlock_destroy(const struct ovs_rwlock *); void ovs_rwlock_unlock(const struct ovs_rwlock *rwlock) OVS_RELEASES(rwlock); /* Wrappers for pthread_rwlockattr_*() that abort the process on any error. */ void xpthread_rwlockattr_init(pthread_rwlockattr_t *); void xpthread_rwlockattr_destroy(pthread_rwlockattr_t *); #ifdef PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP void xpthread_rwlockattr_setkind_np(pthread_rwlockattr_t *, int kind); #endif void ovs_rwlock_wrlock_at(const struct ovs_rwlock *rwlock, const char *where) OVS_ACQ_WRLOCK(rwlock); #define ovs_rwlock_wrlock(rwlock) \ ovs_rwlock_wrlock_at(rwlock, OVS_SOURCE_LOCATOR) int ovs_rwlock_trywrlock_at(const struct ovs_rwlock *rwlock, const char *where) OVS_TRY_WRLOCK(0, rwlock); #define ovs_rwlock_trywrlock(rwlock) \ ovs_rwlock_trywrlock_at(rwlock, OVS_SOURCE_LOCATOR) void ovs_rwlock_rdlock_at(const struct ovs_rwlock *rwlock, const char *where) OVS_ACQ_RDLOCK(rwlock); #define ovs_rwlock_rdlock(rwlock) \ ovs_rwlock_rdlock_at(rwlock, OVS_SOURCE_LOCATOR) int ovs_rwlock_tryrdlock_at(const struct ovs_rwlock *rwlock, const char *where) OVS_TRY_RDLOCK(0, rwlock); #define ovs_rwlock_tryrdlock(rwlock) \ ovs_rwlock_tryrdlock_at(rwlock, OVS_SOURCE_LOCATOR) /* ovs_barrier functions analogous to pthread_barrier_*() functions. */ void ovs_barrier_init(struct ovs_barrier *, uint32_t count); void ovs_barrier_destroy(struct ovs_barrier *); void ovs_barrier_block(struct ovs_barrier *); /* Wrappers for xpthread_cond_*() that abort the process on any error. * * Use ovs_mutex_cond_wait() to wait for a condition. */ void xpthread_cond_init(pthread_cond_t *, pthread_condattr_t *); void xpthread_cond_destroy(pthread_cond_t *); void xpthread_cond_signal(pthread_cond_t *); void xpthread_cond_broadcast(pthread_cond_t *); void xpthread_key_create(pthread_key_t *, void (*destructor)(void *)); void xpthread_key_delete(pthread_key_t); void xpthread_setspecific(pthread_key_t, const void *); #ifndef _WIN32 void xpthread_sigmask(int, const sigset_t *, sigset_t *); #endif pthread_t ovs_thread_create(const char *name, void *(*)(void *), void *); void xpthread_join(pthread_t, void **); /* Per-thread data. * * * Standard Forms * ============== * * Multiple forms of standard per-thread data exist, each with its own pluses * and minuses. In general, if one of these forms is appropriate, then it's a * good idea to use it: * * - POSIX per-thread data via pthread_key_t is portable to any pthreads * implementation, and allows a destructor function to be defined. It * only (directly) supports per-thread pointers, which are always * initialized to NULL. It requires once-only allocation of a * pthread_key_t value. It is relatively slow. Typically few * "pthread_key_t"s are available (POSIX requires only at least 128, * glibc supplies only 1024). * * - The thread_local feature newly defined in C11 works with * any data type and initializer, and it is fast. thread_local does not * require once-only initialization like pthread_key_t. C11 does not * define what happens if one attempts to access a thread_local object * from a thread other than the one to which that object belongs. There * is no provision to call a user-specified destructor when a thread * ends. Typical implementations allow for an arbitrary amount of * thread_local storage, but statically allocated only. * * - The __thread keyword is a GCC extension similar to thread_local but * with a longer history. __thread is not portable to every GCC version * or environment. __thread does not restrict the use of a thread-local * object outside its own thread. * * Here's a handy summary: * * pthread_key_t thread_local __thread * ------------- ------------ ------------- * portability high low medium * speed low high high * supports destructors? yes no no * needs key allocation? yes no no * arbitrary initializer? no yes yes * cross-thread access? yes no yes * amount available? few arbitrary arbitrary * dynamically allocated? yes no no * * * Extensions * ========== * * OVS provides some extensions and wrappers: * * - In a situation where the performance of thread_local or __thread is * desirable, but portability is required, DEFINE_STATIC_PER_THREAD_DATA * and DECLARE_EXTERN_PER_THREAD_DATA/DEFINE_EXTERN_PER_THREAD_DATA may * be appropriate (see below). * * - DEFINE_PER_THREAD_MALLOCED_DATA can be convenient for simple * per-thread malloc()'d buffers. * * - struct ovs_tsd provides an alternative to pthread_key_t that isn't * limited to a small number of keys. */ /* For static data, use this macro in a source file: * * DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, INITIALIZER). * * For global data, "declare" the data in the header and "define" it in * the source file, with: * * DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME). * DEFINE_EXTERN_PER_THREAD_DATA(NAME, INITIALIZER). * * One should prefer to use POSIX per-thread data, via pthread_key_t, when its * performance is acceptable, because of its portability (see the table above). * This macro is an alternatives that takes advantage of thread_local (and * __thread), for its performance, when it is available, and falls back to * POSIX per-thread data otherwise. * * Defines per-thread variable NAME with the given TYPE, initialized to * INITIALIZER (which must be valid as an initializer for a variable with * static lifetime). * * The public interface to the variable is: * * TYPE *NAME_get(void) * TYPE *NAME_get_unsafe(void) * * Returns the address of this thread's instance of NAME. * * Use NAME_get() in a context where this might be the first use of the * per-thread variable in the program. Use NAME_get_unsafe(), which * avoids a conditional test and is thus slightly faster, in a context * where one knows that NAME_get() has already been called previously. * * There is no "NAME_set()" (or "NAME_set_unsafe()") function. To set the * value of the per-thread variable, dereference the pointer returned by * TYPE_get() or TYPE_get_unsafe(), e.g. *TYPE_get() = 0. */ #if HAVE_THREAD_LOCAL || HAVE___THREAD #if HAVE_THREAD_LOCAL #include #elif HAVE___THREAD #define thread_local __thread #else #error #endif #define DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, ...) \ typedef TYPE NAME##_type; \ \ static NAME##_type * \ NAME##_get_unsafe(void) \ { \ static thread_local NAME##_type var = __VA_ARGS__; \ return &var; \ } \ \ static NAME##_type * \ NAME##_get(void) \ { \ return NAME##_get_unsafe(); \ } #define DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME) \ typedef TYPE NAME##_type; \ extern thread_local NAME##_type NAME##_var; \ \ static inline NAME##_type * \ NAME##_get_unsafe(void) \ { \ return &NAME##_var; \ } \ \ static inline NAME##_type * \ NAME##_get(void) \ { \ return NAME##_get_unsafe(); \ } #define DEFINE_EXTERN_PER_THREAD_DATA(NAME, ...) \ thread_local NAME##_type NAME##_var = __VA_ARGS__; #else /* no C implementation support for thread-local storage */ #define DEFINE_STATIC_PER_THREAD_DATA(TYPE, NAME, ...) \ typedef TYPE NAME##_type; \ static pthread_key_t NAME##_key; \ \ static NAME##_type * \ NAME##_get_unsafe(void) \ { \ return pthread_getspecific(NAME##_key); \ } \ \ static void \ NAME##_once_init(void) \ { \ if (pthread_key_create(&NAME##_key, free)) { \ abort(); \ } \ } \ \ static NAME##_type * \ NAME##_get(void) \ { \ static pthread_once_t once = PTHREAD_ONCE_INIT; \ NAME##_type *value; \ \ pthread_once(&once, NAME##_once_init); \ value = NAME##_get_unsafe(); \ if (!value) { \ static const NAME##_type initial_value = __VA_ARGS__; \ \ value = malloc(sizeof *value); \ if (value == NULL) { \ out_of_memory(); \ } \ *value = initial_value; \ xpthread_setspecific(NAME##_key, value); \ } \ return value; \ } #define DECLARE_EXTERN_PER_THREAD_DATA(TYPE, NAME) \ typedef TYPE NAME##_type; \ static pthread_key_t NAME##_key; \ \ static inline NAME##_type * \ NAME##_get_unsafe(void) \ { \ return pthread_getspecific(NAME##_key); \ } \ \ NAME##_type *NAME##_get(void); #define DEFINE_EXTERN_PER_THREAD_DATA(NAME, ...) \ static void \ NAME##_once_init(void) \ { \ if (pthread_key_create(&NAME##_key, free)) { \ abort(); \ } \ } \ \ NAME##_type * \ NAME##_get(void) \ { \ static pthread_once_t once = PTHREAD_ONCE_INIT; \ NAME##_type *value; \ \ pthread_once(&once, NAME##_once_init); \ value = NAME##_get_unsafe(); \ if (!value) { \ static const NAME##_type initial_value = __VA_ARGS__; \ \ value = malloc(sizeof *value); \ if (value == NULL) { \ out_of_memory(); \ } \ *value = initial_value; \ xpthread_setspecific(NAME##_key, value); \ } \ return value; \ } #endif /* DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME). * * This is a simple wrapper around POSIX per-thread data primitives. It * defines per-thread variable NAME with the given TYPE, which must be a * pointer type. In each thread, the per-thread variable is initialized to * NULL. When a thread terminates, the variable is freed with free(). * * The public interface to the variable is: * * TYPE NAME_get(void) * TYPE NAME_get_unsafe(void) * * Returns the value of per-thread variable NAME in this thread. * * Use NAME_get() in a context where this might be the first use of the * per-thread variable in the program. Use NAME_get_unsafe(), which * avoids a conditional test and is thus slightly faster, in a context * where one knows that NAME_get() has already been called previously. * * TYPE NAME_set(TYPE new_value) * TYPE NAME_set_unsafe(TYPE new_value) * * Sets the value of per-thread variable NAME to 'new_value' in this * thread, and returns its previous value. * * Use NAME_set() in a context where this might be the first use of the * per-thread variable in the program. Use NAME_set_unsafe(), which * avoids a conditional test and is thus slightly faster, in a context * where one knows that NAME_set() has already been called previously. */ #define DEFINE_PER_THREAD_MALLOCED_DATA(TYPE, NAME) \ static pthread_key_t NAME##_key; \ \ static void \ NAME##_once_init(void) \ { \ if (pthread_key_create(&NAME##_key, free)) { \ abort(); \ } \ } \ \ static void \ NAME##_init(void) \ { \ static pthread_once_t once = PTHREAD_ONCE_INIT; \ pthread_once(&once, NAME##_once_init); \ } \ \ static TYPE \ NAME##_get_unsafe(void) \ { \ return pthread_getspecific(NAME##_key); \ } \ \ static OVS_UNUSED TYPE \ NAME##_get(void) \ { \ NAME##_init(); \ return NAME##_get_unsafe(); \ } \ \ static TYPE \ NAME##_set_unsafe(TYPE value) \ { \ TYPE old_value = NAME##_get_unsafe(); \ xpthread_setspecific(NAME##_key, value); \ return old_value; \ } \ \ static OVS_UNUSED TYPE \ NAME##_set(TYPE value) \ { \ NAME##_init(); \ return NAME##_set_unsafe(value); \ } /* Dynamically allocated thread-specific data with lots of slots. * * pthread_key_t can provide as few as 128 pieces of thread-specific data (even * glibc is limited to 1,024). Thus, one must be careful to allocate only a * few keys globally. One cannot, for example, allocate a key for every * instance of a data structure if there might be an arbitrary number of those * data structures. * * This API is similar to the pthread one (simply search and replace pthread_ * by ovsthread_) but it a much larger limit that can be raised if necessary * (by recompiling). Thus, one may more freely use this form of * thread-specific data. * * ovsthread_key_t also differs from pthread_key_t in the following ways: * * - Destructors must not access thread-specific data (via ovsthread_key). * * - The pthread_key_t API allows concurrently exiting threads to start * executing the destructor after pthread_key_delete() returns. The * ovsthread_key_t API guarantees that, when ovsthread_key_delete() * returns, all destructors have returned and no new ones will start * execution. */ typedef struct ovsthread_key *ovsthread_key_t; void ovsthread_key_create(ovsthread_key_t *, void (*destructor)(void *)); void ovsthread_key_delete(ovsthread_key_t); void ovsthread_setspecific(ovsthread_key_t, const void *); void *ovsthread_getspecific(ovsthread_key_t); /* Thread ID. * * pthread_t isn't so nice for some purposes. Its size and representation are * implementation dependent, which means that there is no way to hash it. * This thread ID avoids the problem. */ DECLARE_EXTERN_PER_THREAD_DATA(unsigned int, ovsthread_id); /* Returns a per-thread identifier unique within the lifetime of the * process. */ static inline unsigned int ovsthread_id_self(void) { return *ovsthread_id_get(); } /* Simulated global counter. * * Incrementing such a counter is meant to be cheaper than incrementing a * global counter protected by a lock. It is probably more expensive than * incrementing a truly thread-local variable, but such a variable has no * straightforward way to get the sum. * * * Thread-safety * ============= * * Fully thread-safe. */ struct ovsthread_stats { struct ovs_mutex mutex; void *volatile buckets[16]; }; void ovsthread_stats_init(struct ovsthread_stats *); void ovsthread_stats_destroy(struct ovsthread_stats *); void *ovsthread_stats_bucket_get(struct ovsthread_stats *, void *(*new_bucket)(void)); #define OVSTHREAD_STATS_FOR_EACH_BUCKET(BUCKET, IDX, STATS) \ for ((IDX) = ovs_thread_stats_next_bucket(STATS, 0); \ ((IDX) < ARRAY_SIZE((STATS)->buckets) \ ? ((BUCKET) = (STATS)->buckets[IDX], true) \ : false); \ (IDX) = ovs_thread_stats_next_bucket(STATS, (IDX) + 1)) size_t ovs_thread_stats_next_bucket(const struct ovsthread_stats *, size_t); bool single_threaded(void); void assert_single_threaded_at(const char *where); #define assert_single_threaded() assert_single_threaded_at(OVS_SOURCE_LOCATOR) #ifndef _WIN32 pid_t xfork_at(const char *where); #define xfork() xfork_at(OVS_SOURCE_LOCATOR) #endif void forbid_forking(const char *reason); bool may_fork(void); /* Useful functions related to threading. */ int count_cpu_cores(void); bool thread_is_pmd(void); #endif /* ovs-thread.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs.tmac0000644000000000000000000000013013534540071016065 xustar0030 mtime=1567801401.549682257 30 atime=1567801402.093686252 28 ctime=1567801423.7338457 openvswitch-2.5.9/lib/ovs.tmac0000644000175000017500000000637713534540071017572 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .\" ovs.tmac .\" .\" Open vSwitch troff macro library . . .\" Continuation line for .IP. .de IQ . br . ns . IP "\\$1" .. . .\" Introduces a sub-subsection .de ST . PP . RS -0.15in . I "\\$1" . RE .. . .\" The content between the lines below is from an-ext.tmac in groff .\" 1.21, with some modifications. .\" ---------------------------------------------------------------------- .\" an-ext.tmac .\" .\" Written by Eric S. Raymond .\" Werner Lemberg .\" .\" Version 2007-Feb-02 .\" .\" Copyright (C) 2007, 2009, 2011 Free Software Foundation, Inc. .\" You may freely use, modify and/or distribute this file. .\" .\" .\" The code below provides extension macros for the `man' macro package. .\" Care has been taken to make the code portable; groff extensions are .\" properly hidden so that all troff implementations can use it without .\" changes. .\" .\" With groff, this file is sourced by the `man' macro package itself. .\" Man page authors who are concerned about portability might add the .\" used macros directly to the prologue of the man page(s). . . .\" Convention: Auxiliary macros and registers start with `m' followed .\" by an uppercase letter or digit. . . .\" Declare start of command synopsis. Sets up hanging indentation. .de SY . ie !\\n(mS \{\ . nh . nr mS 1 . nr mA \\n(.j . ad l . nr mI \\n(.i . \} . el \{\ . br . ns . \} . . HP \w'\fB\\$1\fP\ 'u . B "\\$1" .. . . .\" End of command synopsis. Restores adjustment. .de YS . in \\n(mIu . ad \\n(mA . hy \\n(HY . nr mS 0 .. . . .\" Declare optional option. .de OP . ie \\n(.$-1 \ . RI "[\fB\\$1\fP" "\ \\$2" "]" . el \ . RB "[" "\\$1" "]" .. . . .\" Start URL. .de UR . ds m1 \\$1\" . nh . if \\n(mH \{\ . \" Start diversion in a new environment. . do ev URL-div . do di URL-div . \} .. . . .\" End URL. .de UE . ie \\n(mH \{\ . br . di . ev . . \" Has there been one or more input lines for the link text? . ie \\n(dn \{\ . do HTML-NS "" . \" Yes, strip off final newline of diversion and emit it. . do chop URL-div . do URL-div \c . do HTML-NS . \} . el \ . do HTML-NS "\\*(m1" \&\\$*\" . \} . el \ \\*(la\\*(m1\\*(ra\\$*\" . . hy \\n(HY .. . . .\" Start email address. .de MT . ds m1 \\$1\" . nh . if \\n(mH \{\ . \" Start diversion in a new environment. . do ev URL-div . do di URL-div . \} .. . . .\" End email address. .de ME . ie \\n(mH \{\ . br . di . ev . . \" Has there been one or more input lines for the link text? . ie \\n(dn \{\ . do HTML-NS "" . \" Yes, strip off final newline of diversion and emit it. . do chop URL-div . do URL-div \c . do HTML-NS . \} . el \ . do HTML-NS "\\*(m1" \&\\$*\" . \} . el \ \\*(la\\*(m1\\*(ra\\$*\" . . hy \\n(HY .. . . .\" Continuation line for .TP header. .de TQ . br . ns . TP \\$1\" no doublequotes around argument! .. . . .\" Start example. .de EX . nr mE \\n(.f . nf . nh . ft CW .. . . .\" End example. .de EE . ft \\n(mE . fi . hy \\n(HY .. . .\" EOF .\" ---------------------------------------------------------------------- openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-rcu.h0000644000000000000000000000013113534540071016160 xustar0030 mtime=1567801401.545682227 30 atime=1567801402.089686223 29 ctime=1567801424.82585375 openvswitch-2.5.9/lib/ovs-rcu.h0000644000175000017500000002301413534540071017647 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVS_RCU_H #define OVS_RCU_H 1 /* Read-Copy-Update (RCU) * ====================== * * Introduction * ------------ * * Atomic pointer access makes it pretty easy to implement lock-free * algorithms. There is one big problem, though: when a writer updates a * pointer to point to a new data structure, some thread might be reading the * old version, and there's no convenient way to free the old version when all * threads are done with the old version. * * The function ovsrcu_postpone() solves that problem. The function pointer * passed in as its argument is called only after all threads are done with old * versions of data structures. The function callback frees an old version of * data no longer in use. This technique is called "read-copy-update", or RCU * for short. * * * Details * ------- * * A "quiescent state" is a time at which a thread holds no pointers to memory * that is managed by RCU; that is, when the thread is known not to reference * memory that might be an old version of some object freed via RCU. For * example, poll_block() includes a quiescent state, as does * ovs_mutex_cond_wait(). * * The following functions manage the recognition of quiescent states: * * void ovsrcu_quiesce(void) * * Recognizes a momentary quiescent state in the current thread. * * void ovsrcu_quiesce_start(void) * void ovsrcu_quiesce_end(void) * * Brackets a time period during which the current thread is quiescent. * * A newly created thread is initially active, not quiescent. When a process * becomes multithreaded, the main thread becomes active, not quiescent. * * When a quiescient state has occurred in every thread, we say that a "grace * period" has occurred. Following a grace period, all of the callbacks * postponed before the start of the grace period MAY be invoked. OVS takes * care of this automatically through the RCU mechanism: while a process still * has only a single thread, it invokes the postponed callbacks directly from * ovsrcu_quiesce() and ovsrcu_quiesce_start(); after additional threads have * been created, it creates an extra helper thread to invoke callbacks. * * Please note that while a postponed function call is guaranteed to happen * after the next time all participating threads have quiesced at least once, * there is no quarantee that all postponed functions are called as early as * possible, or that the functions postponed by different threads would be * called in the order the registrations took place. In particular, even if * two threads provably postpone a function each in a specific order, the * postponed functions may still be called in the opposite order, depending on * the timing of when the threads call ovsrcu_quiesce(), how many functions * they postpone, and when the ovs-rcu thread happens to grab the functions to * be called. * * All functions postponed by a single thread are guaranteed to execute in the * order they were postponed, however. * * Usage * ----- * * Use OVSRCU_TYPE(TYPE) to declare a pointer to RCU-protected data, e.g. the * following declares an RCU-protected "struct flow *" named flowp: * * OVSRCU_TYPE(struct flow *) flowp; * * Use ovsrcu_get(TYPE, VAR) to read an RCU-protected pointer, e.g. to read the * pointer variable declared above: * * struct flow *flow = ovsrcu_get(struct flow *, &flowp); * * If the pointer variable is currently protected against change (because * the current thread holds a mutex that protects it), ovsrcu_get_protected() * may be used instead. Only on the Alpha architecture is this likely to * generate different code, but it may be useful documentation. * * (With GNU C or Clang, you get a compiler error if TYPE is wrong; other * compilers will merrily carry along accepting the wrong type.) * * Use ovsrcu_set() to write an RCU-protected pointer and ovsrcu_postpone() to * free the previous data. ovsrcu_set_hidden() can be used on RCU protected * data not visible to any readers yet, but will be made visible by a later * ovsrcu_set(). ovsrcu_init() can be used to initialize RCU pointers when * no readers are yet executing. If more than one thread can write the * pointer, then some form of external synchronization, e.g. a mutex, is * needed to prevent writers from interfering with one another. For example, * to write the pointer variable declared above while safely freeing the old * value: * * static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; * * OVSRCU_TYPE(struct flow *) flowp; * * void * change_flow(struct flow *new_flow) * { * ovs_mutex_lock(&mutex); * ovsrcu_postpone(free, * ovsrcu_get_protected(struct flow *, &flowp)); * ovsrcu_set(&flowp, new_flow); * ovs_mutex_unlock(&mutex); * } * */ #include "compiler.h" #include "ovs-atomic.h" #if __GNUC__ #define OVSRCU_TYPE(TYPE) struct { ATOMIC(TYPE) p; } #define OVSRCU_INITIALIZER(VALUE) { ATOMIC_VAR_INIT(VALUE) } #define ovsrcu_get__(TYPE, VAR, ORDER) \ ({ \ TYPE value__; \ typeof(VAR) ovsrcu_var = (VAR); \ \ atomic_read_explicit(CONST_CAST(ATOMIC(TYPE) *, &ovsrcu_var->p), \ &value__, ORDER); \ \ value__; \ }) #define ovsrcu_get(TYPE, VAR) \ ovsrcu_get__(TYPE, VAR, memory_order_consume) #define ovsrcu_get_protected(TYPE, VAR) \ ovsrcu_get__(TYPE, VAR, memory_order_relaxed) /* 'VALUE' may be an atomic operation, which must be evaluated before * any of the body of the atomic_store_explicit. Since the type of * 'VAR' is not fixed, we cannot use an inline function to get * function semantics for this. */ #define ovsrcu_set__(VAR, VALUE, ORDER) \ ({ \ typeof(VAR) ovsrcu_var = (VAR); \ typeof(VALUE) ovsrcu_value = (VALUE); \ memory_order ovsrcu_order = (ORDER); \ \ atomic_store_explicit(&ovsrcu_var->p, ovsrcu_value, ovsrcu_order); \ (void *) 0; \ }) #else /* not GNU C */ struct ovsrcu_pointer { ATOMIC(void *) p; }; #define OVSRCU_TYPE(TYPE) struct ovsrcu_pointer #define OVSRCU_INITIALIZER(VALUE) { ATOMIC_VAR_INIT(VALUE) } static inline void * ovsrcu_get__(const struct ovsrcu_pointer *pointer, memory_order order) { void *value; atomic_read_explicit(&CONST_CAST(struct ovsrcu_pointer *, pointer)->p, &value, order); return value; } #define ovsrcu_get(TYPE, VAR) \ CONST_CAST(TYPE, ovsrcu_get__(VAR, memory_order_consume)) #define ovsrcu_get_protected(TYPE, VAR) \ CONST_CAST(TYPE, ovsrcu_get__(VAR, memory_order_relaxed)) static inline void ovsrcu_set__(struct ovsrcu_pointer *pointer, const void *value, memory_order order) { atomic_store_explicit(&pointer->p, CONST_CAST(void *, value), order); } #endif /* Writes VALUE to the RCU-protected pointer whose address is VAR. * * Users require external synchronization (e.g. a mutex). See "Usage" above * for an example. */ #define ovsrcu_set(VAR, VALUE) \ ovsrcu_set__(VAR, VALUE, memory_order_release) /* This can be used for initializing RCU pointers before any readers can * see them. A later ovsrcu_set() needs to make the bigger structure this * is part of visible to the readers. */ #define ovsrcu_set_hidden(VAR, VALUE) \ ovsrcu_set__(VAR, VALUE, memory_order_relaxed) /* This can be used for initializing RCU pointers before any readers are * executing. */ #define ovsrcu_init(VAR, VALUE) atomic_init(&(VAR)->p, VALUE) /* Calls FUNCTION passing ARG as its pointer-type argument following the next * grace period. See "Usage" above for an example. */ void ovsrcu_postpone__(void (*function)(void *aux), void *aux); #define ovsrcu_postpone(FUNCTION, ARG) \ ((void) sizeof((FUNCTION)(ARG), 1), \ (void) sizeof(*(ARG)), \ ovsrcu_postpone__((void (*)(void *))(FUNCTION), ARG)) /* Quiescent states. */ void ovsrcu_quiesce_start(void); void ovsrcu_quiesce_end(void); void ovsrcu_quiesce(void); int ovsrcu_try_quiesce(void); bool ovsrcu_is_quiescent(void); /* Synchronization. Waits for all non-quiescent threads to quiesce at least * once. This can block for a relatively long time. */ void ovsrcu_synchronize(void); #endif /* ovs-rcu.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/flow.h0000644000000000000000000000013213534540071015532 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.725853013 openvswitch-2.5.9/lib/flow.h0000644000175000017500000011436313534540071017230 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FLOW_H #define FLOW_H 1 #include #include #include #include #include #include "bitmap.h" #include "byte-order.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "packets.h" #include "hash.h" #include "util.h" struct dpif_flow_stats; struct ds; struct flow_wildcards; struct minimask; struct dp_packet; struct pkt_metadata; struct match; /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ #define FLOW_WC_SEQ 35 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 8 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS); BUILD_ASSERT_DECL(FLOW_N_REGS % 2 == 0); /* Even. */ /* Number of OpenFlow 1.5+ 64-bit registers. * * Each of these overlays a pair of Open vSwitch 32-bit registers, so there * are half as many of them.*/ #define FLOW_N_XREGS (FLOW_N_REGS / 2) /* Used for struct flow's dl_type member for frames that have no Ethernet * type, that is, pure 802.2 frames. */ #define FLOW_DL_TYPE_NONE 0x5ff /* Fragment bits, used for IPv4 and IPv6, always zero for non-IP flows. */ #define FLOW_NW_FRAG_ANY (1 << 0) /* Set for any IP frag. */ #define FLOW_NW_FRAG_LATER (1 << 1) /* Set for IP frag with nonzero offset. */ #define FLOW_NW_FRAG_MASK (FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER) BUILD_ASSERT_DECL(FLOW_NW_FRAG_ANY == NX_IP_FRAG_ANY); BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER); BUILD_ASSERT_DECL(FLOW_TNL_F_OAM == NX_TUN_FLAG_OAM); const char *flow_tun_flag_to_string(uint32_t flags); /* Maximum number of supported MPLS labels. */ #define FLOW_MAX_MPLS_LABELS 3 /* * A flow in the network. * * Must be initialized to all zeros to make any compiler-induced padding * zeroed. Helps also in keeping unused fields (such as mutually exclusive * IPv4 and IPv6 addresses) zeroed out. * * The meaning of 'in_port' is context-dependent. In most cases, it is a * 16-bit OpenFlow 1.0 port number. In the software datapath interface (dpif) * layer and its implementations (e.g. dpif-netlink, dpif-netdev), it is * instead a 32-bit datapath port number. * * The fields are organized in four segments to facilitate staged lookup, where * lower layer fields are first used to determine if the later fields need to * be looked at. This enables better wildcarding for datapath flows. * * NOTE: Order of the fields is significant, any change in the order must be * reflected in miniflow_extract()! */ struct flow { /* Metadata */ struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */ ovs_be64 metadata; /* OpenFlow Metadata. */ uint32_t regs[FLOW_N_REGS]; /* Registers. */ uint32_t skb_priority; /* Packet priority for QoS. */ uint32_t pkt_mark; /* Packet mark. */ uint32_t dp_hash; /* Datapath computed hash value. The exact * computation is opaque to the user space. */ union flow_in_port in_port; /* Input port.*/ uint32_t recirc_id; /* Must be exact match. */ uint16_t ct_state; /* Connection tracking state. */ uint16_t ct_zone; /* Connection tracking zone. */ uint32_t ct_mark; /* Connection mark.*/ uint8_t pad1[4]; /* Pad to 64 bits. */ ovs_u128 ct_label; /* Connection label. */ uint32_t conj_id; /* Conjunction ID. */ ofp_port_t actset_output; /* Output port in action set. */ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ struct eth_addr dl_dst; /* Ethernet destination address. */ struct eth_addr dl_src; /* Ethernet source address. */ ovs_be16 dl_type; /* Ethernet frame type. */ ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */ ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack (with padding). */ /* L3 (64-bit aligned) */ ovs_be32 nw_src; /* IPv4 source address. */ ovs_be32 nw_dst; /* IPv4 destination address. */ struct in6_addr ipv6_src; /* IPv6 source address. */ struct in6_addr ipv6_dst; /* IPv6 destination address. */ ovs_be32 ipv6_label; /* IPv6 flow label. */ uint8_t nw_frag; /* FLOW_FRAG_* flags. */ uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */ uint8_t nw_ttl; /* IP TTL/Hop Limit. */ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */ struct eth_addr arp_sha; /* ARP/ND source hardware address. */ struct eth_addr arp_tha; /* ARP/ND target hardware address. */ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */ ovs_be16 pad3; /* Pad to 64 bits. */ /* L4 (64-bit aligned) */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */ ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */ ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address. * Keep last for BUILD_ASSERT_DECL below. */ }; BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); #define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t)) /* Some flow fields are mutually exclusive or only appear within the flow * pipeline. IPv6 headers are bigger than IPv4 and MPLS, and IPv6 ND packets * are bigger than TCP,UDP and IGMP packets. */ #define FLOW_MAX_PACKET_U64S (FLOW_U64S \ /* Unused in datapath */ - FLOW_U64_SIZE(regs) \ - FLOW_U64_SIZE(metadata) \ /* L2.5/3 */ - FLOW_U64_SIZE(nw_src) /* incl. nw_dst */ \ - FLOW_U64_SIZE(mpls_lse) \ /* L4 */ - FLOW_U64_SIZE(tp_src) \ ) /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) == sizeof(struct flow_tnl) + 216 && FLOW_WC_SEQ == 35); /* Incremental points at which flow classification may be performed in * segments. * This is located here since this is dependent on the structure of the * struct flow defined above: * Each offset must be on a distinct, successive U64 boundary strictly * within the struct flow. */ enum { FLOW_SEGMENT_1_ENDS_AT = offsetof(struct flow, dl_dst), FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, nw_src), FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, tp_src), }; BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(FLOW_SEGMENT_2_ENDS_AT % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(FLOW_SEGMENT_3_ENDS_AT % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL( 0 < FLOW_SEGMENT_1_ENDS_AT); BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT < FLOW_SEGMENT_2_ENDS_AT); BUILD_ASSERT_DECL(FLOW_SEGMENT_2_ENDS_AT < FLOW_SEGMENT_3_ENDS_AT); BUILD_ASSERT_DECL(FLOW_SEGMENT_3_ENDS_AT < sizeof(struct flow)); extern const uint8_t flow_segment_u64s[]; #define FLOW_U64_OFFSET(FIELD) \ (offsetof(struct flow, FIELD) / sizeof(uint64_t)) #define FLOW_U64_OFFREM(FIELD) \ (offsetof(struct flow, FIELD) % sizeof(uint64_t)) /* Number of 64-bit units spanned by a 'FIELD'. */ #define FLOW_U64_SIZE(FIELD) \ DIV_ROUND_UP(FLOW_U64_OFFREM(FIELD) + MEMBER_SIZEOF(struct flow, FIELD), \ sizeof(uint64_t)) void flow_extract(struct dp_packet *, struct flow *); void flow_zero_wildcards(struct flow *, const struct flow_wildcards *); void flow_unwildcard_tp_ports(const struct flow *, struct flow_wildcards *); void flow_get_metadata(const struct flow *, struct match *flow_metadata); const char *ct_state_to_string(uint32_t state); char *flow_to_string(const struct flow *); void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t), uint32_t flags, char del); void format_flags_masked(struct ds *ds, const char *name, const char *(*bit_to_string)(uint32_t), uint32_t flags, uint32_t mask, uint32_t max_mask); int parse_flags(const char *s, const char *(*bit_to_string)(uint32_t), char end, const char *field_name, char **res_string, uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask); void flow_format(struct ds *, const struct flow *); void flow_print(FILE *, const struct flow *); static inline int flow_compare_3way(const struct flow *, const struct flow *); static inline bool flow_equal(const struct flow *, const struct flow *); static inline size_t flow_hash(const struct flow *, uint32_t basis); void flow_set_dl_vlan(struct flow *, ovs_be16 vid); void flow_set_vlan_vid(struct flow *, ovs_be16 vid); void flow_set_vlan_pcp(struct flow *, uint8_t pcp); int flow_count_mpls_labels(const struct flow *, struct flow_wildcards *); int flow_count_common_mpls_labels(const struct flow *a, int an, const struct flow *b, int bn, struct flow_wildcards *wc); void flow_push_mpls(struct flow *, int n, ovs_be16 mpls_eth_type, struct flow_wildcards *, bool clear_flow_L3); bool flow_pop_mpls(struct flow *, int n, ovs_be16 eth_type, struct flow_wildcards *); void flow_set_mpls_label(struct flow *, int idx, ovs_be32 label); void flow_set_mpls_ttl(struct flow *, int idx, uint8_t ttl); void flow_set_mpls_tc(struct flow *, int idx, uint8_t tc); void flow_set_mpls_bos(struct flow *, int idx, uint8_t stack); void flow_set_mpls_lse(struct flow *, int idx, ovs_be32 lse); void flow_compose(struct dp_packet *, const struct flow *); static inline uint64_t flow_get_xreg(const struct flow *flow, int idx) { return ((uint64_t) flow->regs[idx * 2] << 32) | flow->regs[idx * 2 + 1]; } static inline void flow_set_xreg(struct flow *flow, int idx, uint64_t value) { flow->regs[idx * 2] = value >> 32; flow->regs[idx * 2 + 1] = value; } static inline int flow_compare_3way(const struct flow *a, const struct flow *b) { return memcmp(a, b, sizeof *a); } static inline bool flow_equal(const struct flow *a, const struct flow *b) { return !flow_compare_3way(a, b); } static inline size_t flow_hash(const struct flow *flow, uint32_t basis) { return hash_words64((const uint64_t *)flow, sizeof *flow / sizeof(uint64_t), basis); } static inline uint16_t ofp_to_u16(ofp_port_t ofp_port) { return (OVS_FORCE uint16_t) ofp_port; } static inline uint32_t odp_to_u32(odp_port_t odp_port) { return (OVS_FORCE uint32_t) odp_port; } static inline uint32_t ofp11_to_u32(ofp11_port_t ofp11_port) { return (OVS_FORCE uint32_t) ofp11_port; } static inline ofp_port_t u16_to_ofp(uint16_t port) { return OFP_PORT_C(port); } static inline odp_port_t u32_to_odp(uint32_t port) { return ODP_PORT_C(port); } static inline ofp11_port_t u32_to_ofp11(uint32_t port) { return OFP11_PORT_C(port); } static inline uint32_t hash_ofp_port(ofp_port_t ofp_port) { return hash_int(ofp_to_u16(ofp_port), 0); } static inline uint32_t hash_odp_port(odp_port_t odp_port) { return hash_int(odp_to_u32(odp_port), 0); } /* Wildcards for a flow. * * A 1-bit in each bit in 'masks' indicates that the corresponding bit of * the flow is significant (must match). A 0-bit indicates that the * corresponding bit of the flow is wildcarded (need not match). */ struct flow_wildcards { struct flow masks; }; #define WC_MASK_FIELD(WC, FIELD) \ memset(&(WC)->masks.FIELD, 0xff, sizeof (WC)->masks.FIELD) #define WC_MASK_FIELD_MASK(WC, FIELD, MASK) \ ((WC)->masks.FIELD |= (MASK)) #define WC_UNMASK_FIELD(WC, FIELD) \ memset(&(WC)->masks.FIELD, 0, sizeof (WC)->masks.FIELD) void flow_wildcards_init_catchall(struct flow_wildcards *); void flow_wildcards_init_for_packet(struct flow_wildcards *, const struct flow *); void flow_wildcards_clear_non_packet_fields(struct flow_wildcards *); bool flow_wildcards_is_catchall(const struct flow_wildcards *); void flow_wildcards_set_reg_mask(struct flow_wildcards *, int idx, uint32_t mask); void flow_wildcards_set_xreg_mask(struct flow_wildcards *, int idx, uint64_t mask); void flow_wildcards_and(struct flow_wildcards *dst, const struct flow_wildcards *src1, const struct flow_wildcards *src2); void flow_wildcards_or(struct flow_wildcards *dst, const struct flow_wildcards *src1, const struct flow_wildcards *src2); bool flow_wildcards_has_extra(const struct flow_wildcards *, const struct flow_wildcards *); uint32_t flow_wildcards_hash(const struct flow_wildcards *, uint32_t basis); bool flow_wildcards_equal(const struct flow_wildcards *, const struct flow_wildcards *); uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis); uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis); uint32_t flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis, bool inc_udp_ports ); /* Initialize a flow with random fields that matter for nx_hash_fields. */ void flow_random_hash_fields(struct flow *); void flow_mask_hash_fields(const struct flow *, struct flow_wildcards *, enum nx_hash_fields); uint32_t flow_hash_fields(const struct flow *, enum nx_hash_fields, uint16_t basis); const char *flow_hash_fields_to_str(enum nx_hash_fields); bool flow_hash_fields_valid(enum nx_hash_fields); uint32_t flow_hash_in_wildcards(const struct flow *, const struct flow_wildcards *, uint32_t basis); bool flow_equal_except(const struct flow *a, const struct flow *b, const struct flow_wildcards *); /* Bitmap for flow values. For each 1-bit the corresponding flow value is * explicitly specified, other values are zeroes. * * map_t must be wide enough to hold any member of struct flow. */ typedef unsigned long long map_t; #define MAP_T_BITS (sizeof(map_t) * CHAR_BIT) #define MAP_1 (map_t)1 #define MAP_MAX TYPE_MAXIMUM(map_t) #define MAP_IS_SET(MAP, IDX) ((MAP) & (MAP_1 << (IDX))) /* Iterate through the indices of all 1-bits in 'MAP'. */ #define MAP_FOR_EACH_INDEX(IDX, MAP) \ ULLONG_FOR_EACH_1(IDX, MAP) #define FLOWMAP_UNITS DIV_ROUND_UP(FLOW_U64S, MAP_T_BITS) struct flowmap { map_t bits[FLOWMAP_UNITS]; }; #define FLOWMAP_EMPTY_INITIALIZER { { 0 } } static inline void flowmap_init(struct flowmap *); static inline bool flowmap_equal(struct flowmap, struct flowmap); static inline bool flowmap_is_set(const struct flowmap *, size_t idx); static inline bool flowmap_are_set(const struct flowmap *, size_t idx, unsigned int n_bits); static inline void flowmap_set(struct flowmap *, size_t idx, unsigned int n_bits); static inline void flowmap_clear(struct flowmap *, size_t idx, unsigned int n_bits); static inline struct flowmap flowmap_or(struct flowmap, struct flowmap); static inline struct flowmap flowmap_and(struct flowmap, struct flowmap); static inline bool flowmap_is_empty(struct flowmap); static inline unsigned int flowmap_n_1bits(struct flowmap); #define FLOWMAP_HAS_FIELD(FM, FIELD) \ flowmap_are_set(FM, FLOW_U64_OFFSET(FIELD), FLOW_U64_SIZE(FIELD)) #define FLOWMAP_SET(FM, FIELD) \ flowmap_set(FM, FLOW_U64_OFFSET(FIELD), FLOW_U64_SIZE(FIELD)) #define FLOWMAP_SET__(FM, FIELD, SIZE) \ flowmap_set(FM, FLOW_U64_OFFSET(FIELD), \ DIV_ROUND_UP(SIZE, sizeof(uint64_t))) /* XXX: Only works for full 64-bit units. */ #define FLOWMAP_CLEAR(FM, FIELD) \ BUILD_ASSERT_DECL(FLOW_U64_OFFREM(FIELD) == 0); \ BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->FIELD) % sizeof(uint64_t) == 0); \ flowmap_clear(FM, FLOW_U64_OFFSET(FIELD), FLOW_U64_SIZE(FIELD)) /* Iterate through all units in 'FMAP'. */ #define FLOWMAP_FOR_EACH_UNIT(UNIT) \ for ((UNIT) = 0; (UNIT) < FLOWMAP_UNITS; (UNIT)++) /* Iterate through all map units in 'FMAP'. */ #define FLOWMAP_FOR_EACH_MAP(MAP, FLOWMAP) \ for (size_t unit__ = 0; \ unit__ < FLOWMAP_UNITS && ((MAP) = (FLOWMAP).bits[unit__], true); \ unit__++) struct flowmap_aux; static inline bool flowmap_next_index(struct flowmap_aux *, size_t *idx); #define FLOWMAP_AUX_INITIALIZER(FLOWMAP) { .unit = 0, .map = (FLOWMAP) } /* Iterate through all struct flow u64 indices specified by 'MAP'. This is a * slower but easier version of the FLOWMAP_FOR_EACH_MAP() & * MAP_FOR_EACH_INDEX() combination. */ #define FLOWMAP_FOR_EACH_INDEX(IDX, MAP) \ for (struct flowmap_aux aux__ = FLOWMAP_AUX_INITIALIZER(MAP); \ flowmap_next_index(&aux__, &(IDX));) /* Flowmap inline implementations. */ static inline void flowmap_init(struct flowmap *fm) { memset(fm, 0, sizeof *fm); } static inline bool flowmap_equal(struct flowmap a, struct flowmap b) { return !memcmp(&a, &b, sizeof a); } static inline bool flowmap_is_set(const struct flowmap *fm, size_t idx) { return (fm->bits[idx / MAP_T_BITS] & (MAP_1 << (idx % MAP_T_BITS))) != 0; } /* Returns 'true' if any of the 'n_bits' bits starting at 'idx' are set in * 'fm'. 'n_bits' can be at most MAP_T_BITS. */ static inline bool flowmap_are_set(const struct flowmap *fm, size_t idx, unsigned int n_bits) { map_t n_bits_mask = (MAP_1 << n_bits) - 1; size_t unit = idx / MAP_T_BITS; idx %= MAP_T_BITS; if (fm->bits[unit] & (n_bits_mask << idx)) { return true; } /* The seemingly unnecessary bounds check on 'unit' is a workaround for a * false-positive array out of bounds error by GCC 4.9. */ if (unit + 1 < FLOWMAP_UNITS && idx + n_bits > MAP_T_BITS) { /* Check the remaining bits from the next unit. */ return fm->bits[unit + 1] & (n_bits_mask >> (MAP_T_BITS - idx)); } return false; } /* Set the 'n_bits' consecutive bits in 'fm', starting at bit 'idx'. * 'n_bits' can be at most MAP_T_BITS. */ static inline void flowmap_set(struct flowmap *fm, size_t idx, unsigned int n_bits) { map_t n_bits_mask = (MAP_1 << n_bits) - 1; size_t unit = idx / MAP_T_BITS; idx %= MAP_T_BITS; fm->bits[unit] |= n_bits_mask << idx; /* The seemingly unnecessary bounds check on 'unit' is a workaround for a * false-positive array out of bounds error by GCC 4.9. */ if (unit + 1 < FLOWMAP_UNITS && idx + n_bits > MAP_T_BITS) { /* 'MAP_T_BITS - idx' bits were set on 'unit', set the remaining * bits from the next unit. */ fm->bits[unit + 1] |= n_bits_mask >> (MAP_T_BITS - idx); } } /* Clears the 'n_bits' consecutive bits in 'fm', starting at bit 'idx'. * 'n_bits' can be at most MAP_T_BITS. */ static inline void flowmap_clear(struct flowmap *fm, size_t idx, unsigned int n_bits) { map_t n_bits_mask = (MAP_1 << n_bits) - 1; size_t unit = idx / MAP_T_BITS; idx %= MAP_T_BITS; fm->bits[unit] &= ~(n_bits_mask << idx); /* The seemingly unnecessary bounds check on 'unit' is a workaround for a * false-positive array out of bounds error by GCC 4.9. */ if (unit + 1 < FLOWMAP_UNITS && idx + n_bits > MAP_T_BITS) { /* 'MAP_T_BITS - idx' bits were cleared on 'unit', clear the * remaining bits from the next unit. */ fm->bits[unit + 1] &= ~(n_bits_mask >> (MAP_T_BITS - idx)); } } /* OR the bits in the flowmaps. */ static inline struct flowmap flowmap_or(struct flowmap a, struct flowmap b) { struct flowmap map; size_t unit; FLOWMAP_FOR_EACH_UNIT (unit) { map.bits[unit] = a.bits[unit] | b.bits[unit]; } return map; } /* AND the bits in the flowmaps. */ static inline struct flowmap flowmap_and(struct flowmap a, struct flowmap b) { struct flowmap map; size_t unit; FLOWMAP_FOR_EACH_UNIT (unit) { map.bits[unit] = a.bits[unit] & b.bits[unit]; } return map; } static inline bool flowmap_is_empty(struct flowmap fm) { map_t map; FLOWMAP_FOR_EACH_MAP (map, fm) { if (map) { return false; } } return true; } static inline unsigned int flowmap_n_1bits(struct flowmap fm) { unsigned int n_1bits = 0; size_t unit; FLOWMAP_FOR_EACH_UNIT (unit) { n_1bits += count_1bits(fm.bits[unit]); } return n_1bits; } struct flowmap_aux { size_t unit; struct flowmap map; }; static inline bool flowmap_next_index(struct flowmap_aux *aux, size_t *idx) { for (;;) { map_t *map = &aux->map.bits[aux->unit]; if (*map) { *idx = aux->unit * MAP_T_BITS + raw_ctz(*map); *map = zero_rightmost_1bit(*map); return true; } if (++aux->unit >= FLOWMAP_UNITS) { return false; } } } /* Compressed flow. */ /* A sparse representation of a "struct flow". * * A "struct flow" is fairly large and tends to be mostly zeros. Sparse * representation has two advantages. First, it saves memory and, more * importantly, minimizes the number of accessed cache lines. Second, it saves * time when the goal is to iterate over only the nonzero parts of the struct. * * The map member hold one bit for each uint64_t in a "struct flow". Each * 0-bit indicates that the corresponding uint64_t is zero, each 1-bit that it * *may* be nonzero (see below how this applies to minimasks). * * The values indicated by 'map' always follow the miniflow in memory. The * user of the miniflow is responsible for always having enough storage after * the struct miniflow corresponding to the number of 1-bits in maps. * * Elements in values array are allowed to be zero. This is useful for "struct * minimatch", for which ensuring that the miniflow and minimask members have * same maps allows optimization. This allowance applies only to a miniflow * that is not a mask. That is, a minimask may NOT have zero elements in its * values. * * A miniflow is always dynamically allocated so that the maps are followed by * at least as many elements as there are 1-bits in maps. */ struct miniflow { struct flowmap map; /* Followed by: * uint64_t values[n]; * where 'n' is miniflow_n_values(miniflow). */ }; BUILD_ASSERT_DECL(sizeof(struct miniflow) % sizeof(uint64_t) == 0); #define MINIFLOW_VALUES_SIZE(COUNT) ((COUNT) * sizeof(uint64_t)) static inline uint64_t *miniflow_values(struct miniflow *mf) { return (uint64_t *)(mf + 1); } static inline const uint64_t *miniflow_get_values(const struct miniflow *mf) { return (const uint64_t *)(mf + 1); } struct pkt_metadata; /* The 'dst' must follow with buffer space for FLOW_U64S 64-bit units. * 'dst->map' is ignored on input and set on output to indicate which fields * were extracted. */ void miniflow_extract(struct dp_packet *packet, struct miniflow *dst); void miniflow_map_init(struct miniflow *, const struct flow *); void flow_wc_map(const struct flow *, struct flowmap *); size_t miniflow_alloc(struct miniflow *dsts[], size_t n, const struct miniflow *src); void miniflow_init(struct miniflow *, const struct flow *); void miniflow_clone(struct miniflow *, const struct miniflow *, size_t n_values); struct miniflow * miniflow_create(const struct flow *); void miniflow_expand(const struct miniflow *, struct flow *); static inline uint64_t flow_u64_value(const struct flow *flow, size_t index) { return ((uint64_t *)flow)[index]; } static inline uint64_t *flow_u64_lvalue(struct flow *flow, size_t index) { return &((uint64_t *)flow)[index]; } static inline size_t miniflow_n_values(const struct miniflow *flow) { return flowmap_n_1bits(flow->map); } struct flow_for_each_in_maps_aux { const struct flow *flow; struct flowmap_aux map_aux; }; static inline bool flow_values_get_next_in_maps(struct flow_for_each_in_maps_aux *aux, uint64_t *value) { size_t idx; if (flowmap_next_index(&aux->map_aux, &idx)) { *value = flow_u64_value(aux->flow, idx); return true; } return false; } /* Iterate through all flow u64 values specified by 'MAPS'. */ #define FLOW_FOR_EACH_IN_MAPS(VALUE, FLOW, MAPS) \ for (struct flow_for_each_in_maps_aux aux__ \ = { (FLOW), FLOWMAP_AUX_INITIALIZER(MAPS) }; \ flow_values_get_next_in_maps(&aux__, &(VALUE));) struct mf_for_each_in_map_aux { size_t unit; struct flowmap fmap; struct flowmap map; const uint64_t *values; }; static inline bool mf_get_next_in_map(struct mf_for_each_in_map_aux *aux, uint64_t *value) { map_t *map, *fmap; map_t rm1bit; while (OVS_UNLIKELY(!*(map = &aux->map.bits[aux->unit]))) { /* Skip remaining data in the previous unit. */ aux->values += count_1bits(aux->fmap.bits[aux->unit]); if (++aux->unit == FLOWMAP_UNITS) { return false; } } rm1bit = rightmost_1bit(*map); *map -= rm1bit; fmap = &aux->fmap.bits[aux->unit]; if (OVS_LIKELY(*fmap & rm1bit)) { map_t trash = *fmap & (rm1bit - 1); *fmap -= trash; /* count_1bits() is fast for systems where speed matters (e.g., * DPDK), so we don't try avoid using it. * Advance 'aux->values' to point to the value for 'rm1bit'. */ aux->values += count_1bits(trash); *value = *aux->values; } else { *value = 0; } return true; } /* Iterate through miniflow u64 values specified by 'FLOWMAP'. */ #define MINIFLOW_FOR_EACH_IN_FLOWMAP(VALUE, FLOW, FLOWMAP) \ for (struct mf_for_each_in_map_aux aux__ = \ { 0, (FLOW)->map, (FLOWMAP), miniflow_get_values(FLOW) }; \ mf_get_next_in_map(&aux__, &(VALUE));) /* This can be used when it is known that 'idx' is set in 'map'. */ static inline const uint64_t * miniflow_values_get__(const uint64_t *values, map_t map, size_t idx) { return values + count_1bits(map & ((MAP_1 << idx) - 1)); } /* This can be used when it is known that 'u64_idx' is set in * the map of 'mf'. */ static inline const uint64_t * miniflow_get__(const struct miniflow *mf, size_t idx) { const uint64_t *values = miniflow_get_values(mf); const map_t *map = mf->map.bits; while (idx >= MAP_T_BITS) { idx -= MAP_T_BITS; values += count_1bits(*map++); } return miniflow_values_get__(values, *map, idx); } #define MINIFLOW_IN_MAP(MF, IDX) flowmap_is_set(&(MF)->map, IDX) /* Get the value of the struct flow 'FIELD' as up to 8 byte wide integer type * 'TYPE' from miniflow 'MF'. */ #define MINIFLOW_GET_TYPE(MF, TYPE, FIELD) \ (MINIFLOW_IN_MAP(MF, FLOW_U64_OFFSET(FIELD)) \ ? ((OVS_FORCE const TYPE *)miniflow_get__(MF, FLOW_U64_OFFSET(FIELD))) \ [FLOW_U64_OFFREM(FIELD) / sizeof(TYPE)] \ : 0) #define MINIFLOW_GET_U128(FLOW, FIELD) \ (ovs_u128) { .u64 = { \ (MINIFLOW_IN_MAP(FLOW, FLOW_U64_OFFSET(FIELD)) ? \ *miniflow_get__(FLOW, FLOW_U64_OFFSET(FIELD)) : 0), \ (MINIFLOW_IN_MAP(FLOW, FLOW_U64_OFFSET(FIELD) + 1) ? \ *miniflow_get__(FLOW, FLOW_U64_OFFSET(FIELD) + 1) : 0) } } #define MINIFLOW_GET_U8(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, uint8_t, FIELD) #define MINIFLOW_GET_U16(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, uint16_t, FIELD) #define MINIFLOW_GET_BE16(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, ovs_be16, FIELD) #define MINIFLOW_GET_U32(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, uint32_t, FIELD) #define MINIFLOW_GET_BE32(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, ovs_be32, FIELD) #define MINIFLOW_GET_U64(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, uint64_t, FIELD) #define MINIFLOW_GET_BE64(FLOW, FIELD) \ MINIFLOW_GET_TYPE(FLOW, ovs_be64, FIELD) static inline uint64_t miniflow_get(const struct miniflow *, unsigned int u64_ofs); static inline uint32_t miniflow_get_u32(const struct miniflow *, unsigned int u32_ofs); static inline ovs_be32 miniflow_get_be32(const struct miniflow *, unsigned int be32_ofs); static inline uint16_t miniflow_get_vid(const struct miniflow *); static inline uint16_t miniflow_get_tcp_flags(const struct miniflow *); static inline ovs_be64 miniflow_get_metadata(const struct miniflow *); bool miniflow_equal(const struct miniflow *a, const struct miniflow *b); bool miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b, const struct minimask *); bool miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b, const struct minimask *); uint32_t miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis); /* Compressed flow wildcards. */ /* A sparse representation of a "struct flow_wildcards". * * See the large comment on struct miniflow for details. * * Note: While miniflow can have zero data for a 1-bit in the map, * a minimask may not! We rely on this in the implementation. */ struct minimask { struct miniflow masks; }; void minimask_init(struct minimask *, const struct flow_wildcards *); struct minimask * minimask_create(const struct flow_wildcards *); void minimask_combine(struct minimask *dst, const struct minimask *a, const struct minimask *b, uint64_t storage[FLOW_U64S]); void minimask_expand(const struct minimask *, struct flow_wildcards *); static inline uint32_t minimask_get_u32(const struct minimask *, unsigned int u32_ofs); static inline ovs_be32 minimask_get_be32(const struct minimask *, unsigned int be32_ofs); static inline uint16_t minimask_get_vid_mask(const struct minimask *); static inline ovs_be64 minimask_get_metadata_mask(const struct minimask *); bool minimask_equal(const struct minimask *a, const struct minimask *b); bool minimask_has_extra(const struct minimask *, const struct minimask *); /* Returns true if 'mask' matches every packet, false if 'mask' fixes any bits * or fields. */ static inline bool minimask_is_catchall(const struct minimask *mask) { /* For every 1-bit in mask's map, the corresponding value is non-zero, * so the only way the mask can not fix any bits or fields is for the * map the be zero. */ return flowmap_is_empty(mask->masks.map); } /* Returns the uint64_t that would be at byte offset '8 * u64_ofs' if 'flow' * were expanded into a "struct flow". */ static inline uint64_t miniflow_get(const struct miniflow *flow, unsigned int u64_ofs) { return MINIFLOW_IN_MAP(flow, u64_ofs) ? *miniflow_get__(flow, u64_ofs) : 0; } static inline uint32_t miniflow_get_u32(const struct miniflow *flow, unsigned int u32_ofs) { uint64_t value = miniflow_get(flow, u32_ofs / 2); #if WORDS_BIGENDIAN return (u32_ofs & 1) ? value : value >> 32; #else return (u32_ofs & 1) ? value >> 32 : value; #endif } static inline ovs_be32 miniflow_get_be32(const struct miniflow *flow, unsigned int be32_ofs) { return (OVS_FORCE ovs_be32)miniflow_get_u32(flow, be32_ofs); } /* Returns the VID within the vlan_tci member of the "struct flow" represented * by 'flow'. */ static inline uint16_t miniflow_get_vid(const struct miniflow *flow) { ovs_be16 tci = MINIFLOW_GET_BE16(flow, vlan_tci); return vlan_tci_to_vid(tci); } /* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'mask' * were expanded into a "struct flow_wildcards". */ static inline uint32_t minimask_get_u32(const struct minimask *mask, unsigned int u32_ofs) { return miniflow_get_u32(&mask->masks, u32_ofs); } static inline ovs_be32 minimask_get_be32(const struct minimask *mask, unsigned int be32_ofs) { return (OVS_FORCE ovs_be32)minimask_get_u32(mask, be32_ofs); } /* Returns the VID mask within the vlan_tci member of the "struct * flow_wildcards" represented by 'mask'. */ static inline uint16_t minimask_get_vid_mask(const struct minimask *mask) { return miniflow_get_vid(&mask->masks); } /* Returns the value of the "tcp_flags" field in 'flow'. */ static inline uint16_t miniflow_get_tcp_flags(const struct miniflow *flow) { return ntohs(MINIFLOW_GET_BE16(flow, tcp_flags)); } /* Returns the value of the OpenFlow 1.1+ "metadata" field in 'flow'. */ static inline ovs_be64 miniflow_get_metadata(const struct miniflow *flow) { return MINIFLOW_GET_BE64(flow, metadata); } /* Returns the mask for the OpenFlow 1.1+ "metadata" field in 'mask'. * * The return value is all-1-bits if 'mask' matches on the whole value of the * metadata field, all-0-bits if 'mask' entirely wildcards the metadata field, * or some other value if the metadata field is partially matched, partially * wildcarded. */ static inline ovs_be64 minimask_get_metadata_mask(const struct minimask *mask) { return MINIFLOW_GET_BE64(&mask->masks, metadata); } /* Perform a bitwise OR of miniflow 'src' flow data specified in 'subset' with * the equivalent fields in 'dst', storing the result in 'dst'. 'subset' must * be a subset of 'src's map. */ static inline void flow_union_with_miniflow_subset(struct flow *dst, const struct miniflow *src, struct flowmap subset) { uint64_t *dst_u64 = (uint64_t *) dst; const uint64_t *p = miniflow_get_values(src); map_t map; FLOWMAP_FOR_EACH_MAP (map, subset) { size_t idx; MAP_FOR_EACH_INDEX(idx, map) { dst_u64[idx] |= *p++; } dst_u64 += MAP_T_BITS; } } /* Perform a bitwise OR of miniflow 'src' flow data with the equivalent * fields in 'dst', storing the result in 'dst'. */ static inline void flow_union_with_miniflow(struct flow *dst, const struct miniflow *src) { flow_union_with_miniflow_subset(dst, src, src->map); } static inline void pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow) { md->recirc_id = flow->recirc_id; md->dp_hash = flow->dp_hash; flow_tnl_copy__(&md->tunnel, &flow->tunnel); md->skb_priority = flow->skb_priority; md->pkt_mark = flow->pkt_mark; md->in_port = flow->in_port; md->ct_state = flow->ct_state; md->ct_zone = flow->ct_zone; md->ct_mark = flow->ct_mark; md->ct_label = flow->ct_label; } /* Often, during translation we need to read a value from a flow('FLOW') and * unwildcard the corresponding bits in the wildcards('WC'). This macro makes * it easier to do that. */ #define FLOW_WC_GET_AND_MASK_WC(FLOW, WC, FIELD) \ (((WC) ? WC_MASK_FIELD(WC, FIELD) : NULL), ((FLOW)->FIELD)) static inline bool is_ip_any(const struct flow *flow) { return dl_type_is_ip_any(flow->dl_type); } static inline bool is_icmpv4(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type == htons(ETH_TYPE_IP)) { if (wc) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); } return flow->nw_proto == IPPROTO_ICMP; } return false; } static inline bool is_icmpv6(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type == htons(ETH_TYPE_IPV6)) { if (wc) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); } return flow->nw_proto == IPPROTO_ICMPV6; } return false; } static inline bool is_igmp(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type == htons(ETH_TYPE_IP)) { if (wc) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); } return flow->nw_proto == IPPROTO_IGMP; } return false; } static inline bool is_mld(const struct flow *flow, struct flow_wildcards *wc) { if (is_icmpv6(flow, wc)) { if (wc) { memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); } return (flow->tp_src == htons(MLD_QUERY) || flow->tp_src == htons(MLD_REPORT) || flow->tp_src == htons(MLD_DONE) || flow->tp_src == htons(MLD2_REPORT)); } return false; } static inline bool is_mld_query(const struct flow *flow, struct flow_wildcards *wc) { if (is_icmpv6(flow, wc)) { if (wc) { memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); } return flow->tp_src == htons(MLD_QUERY); } return false; } static inline bool is_mld_report(const struct flow *flow, struct flow_wildcards *wc) { return is_mld(flow, wc) && !is_mld_query(flow, wc); } static inline bool is_stp(const struct flow *flow) { return (eth_addr_equals(flow->dl_dst, eth_addr_stp) && flow->dl_type == htons(FLOW_DL_TYPE_NONE)); } #endif /* flow.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netdev-linux.c0000644000000000000000000000013213534540071017200 xustar0030 mtime=1567801401.453681551 30 atime=1567801402.081686164 30 ctime=1567801424.977854871 openvswitch-2.5.9/lib/netdev-linux.c0000644000175000017500000053174413534540071020704 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netdev-linux.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "coverage.h" #include "dp-packet.h" #include "dpif-netlink.h" #include "dpif-netdev.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "hmap.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "netlink-notifier.h" #include "netlink-socket.h" #include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "ovs-atomic.h" #include "packets.h" #include "poll-loop.h" #include "rtnetlink.h" #include "shash.h" #include "socket-util.h" #include "sset.h" #include "timer.h" #include "unaligned.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_linux); COVERAGE_DEFINE(netdev_set_policing); COVERAGE_DEFINE(netdev_arp_lookup); COVERAGE_DEFINE(netdev_get_ifindex); COVERAGE_DEFINE(netdev_get_hwaddr); COVERAGE_DEFINE(netdev_set_hwaddr); COVERAGE_DEFINE(netdev_get_ethtool); COVERAGE_DEFINE(netdev_set_ethtool); /* These were introduced in Linux 2.6.14, so they might be missing if we have * old headers. */ #ifndef ADVERTISED_Pause #define ADVERTISED_Pause (1 << 13) #endif #ifndef ADVERTISED_Asym_Pause #define ADVERTISED_Asym_Pause (1 << 14) #endif /* These were introduced in Linux 2.6.24, so they might be missing if we * have old headers. */ #ifndef ETHTOOL_GFLAGS #define ETHTOOL_GFLAGS 0x00000025 /* Get flags bitmap(ethtool_value) */ #endif #ifndef ETHTOOL_SFLAGS #define ETHTOOL_SFLAGS 0x00000026 /* Set flags bitmap(ethtool_value) */ #endif /* This was introduced in Linux 2.6.25, so it might be missing if we have old * headers. */ #ifndef TC_RTAB_SIZE #define TC_RTAB_SIZE 1024 #endif /* Linux 2.6.21 introduced struct tpacket_auxdata. * Linux 2.6.27 added the tp_vlan_tci member. * Linux 3.0 defined TP_STATUS_VLAN_VALID. * Linux 3.13 repurposed a padding member for tp_vlan_tpid and defined * TP_STATUS_VLAN_TPID_VALID. * * With all this churn it's easiest to unconditionally define a replacement * structure that has everything we want. */ #ifndef PACKET_AUXDATA #define PACKET_AUXDATA 8 #endif #ifndef TP_STATUS_VLAN_VALID #define TP_STATUS_VLAN_VALID (1 << 4) #endif #ifndef TP_STATUS_VLAN_TPID_VALID #define TP_STATUS_VLAN_TPID_VALID (1 << 6) #endif #undef tpacket_auxdata #define tpacket_auxdata rpl_tpacket_auxdata struct tpacket_auxdata { uint32_t tp_status; uint32_t tp_len; uint32_t tp_snaplen; uint16_t tp_mac; uint16_t tp_net; uint16_t tp_vlan_tci; uint16_t tp_vlan_tpid; }; /* Linux 2.6.27 introduced ethtool_cmd_speed * * To avoid revisiting problems reported with using configure to detect * compatibility (see report at * http://openvswitch.org/pipermail/dev/2014-October/047978.html) * unconditionally replace ethtool_cmd_speed. */ #define ethtool_cmd_speed rpl_ethtool_cmd_speed static inline uint32_t rpl_ethtool_cmd_speed(const struct ethtool_cmd *ep) { return ep->speed | (ep->speed_hi << 16); } /* Linux 2.6.30 introduced supported and advertised flags for * 1G base KX, and 10G base KX4, KR and R. */ #ifndef SUPPORTED_1000baseKX_Full #define SUPPORTED_1000baseKX_Full (1 << 17) #define SUPPORTED_10000baseKX4_Full (1 << 18) #define SUPPORTED_10000baseKR_Full (1 << 19) #define SUPPORTED_10000baseR_FEC (1 << 20) #define ADVERTISED_1000baseKX_Full (1 << 17) #define ADVERTISED_10000baseKX4_Full (1 << 18) #define ADVERTISED_10000baseKR_Full (1 << 19) #define ADVERTISED_10000baseR_FEC (1 << 20) #endif /* Linux 3.5 introduced supported and advertised flags for * 40G base KR4, CR4, SR4 and LR4. */ #ifndef SUPPORTED_40000baseKR4_Full #define SUPPORTED_40000baseKR4_Full (1 << 23) #define SUPPORTED_40000baseCR4_Full (1 << 24) #define SUPPORTED_40000baseSR4_Full (1 << 25) #define SUPPORTED_40000baseLR4_Full (1 << 26) #define ADVERTISED_40000baseKR4_Full (1 << 23) #define ADVERTISED_40000baseCR4_Full (1 << 24) #define ADVERTISED_40000baseSR4_Full (1 << 25) #define ADVERTISED_40000baseLR4_Full (1 << 26) #endif /* Linux 2.6.35 introduced IFLA_STATS64 and rtnl_link_stats64. * * Tests for rtnl_link_stats64 don't seem to consistently work, e.g. on * 2.6.32-431.29.2.el6.x86_64 (see report at * http://openvswitch.org/pipermail/dev/2014-October/047978.html). Maybe * if_link.h is not self-contained on those kernels. It is easiest to * unconditionally define a replacement. */ #ifndef IFLA_STATS64 #define IFLA_STATS64 23 #endif #define rtnl_link_stats64 rpl_rtnl_link_stats64 struct rtnl_link_stats64 { uint64_t rx_packets; uint64_t tx_packets; uint64_t rx_bytes; uint64_t tx_bytes; uint64_t rx_errors; uint64_t tx_errors; uint64_t rx_dropped; uint64_t tx_dropped; uint64_t multicast; uint64_t collisions; uint64_t rx_length_errors; uint64_t rx_over_errors; uint64_t rx_crc_errors; uint64_t rx_frame_errors; uint64_t rx_fifo_errors; uint64_t rx_missed_errors; uint64_t tx_aborted_errors; uint64_t tx_carrier_errors; uint64_t tx_fifo_errors; uint64_t tx_heartbeat_errors; uint64_t tx_window_errors; uint64_t rx_compressed; uint64_t tx_compressed; }; enum { VALID_IFINDEX = 1 << 0, VALID_ETHERADDR = 1 << 1, VALID_IN4 = 1 << 2, VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, VALID_POLICING = 1 << 5, VALID_VPORT_STAT_ERROR = 1 << 6, VALID_DRVINFO = 1 << 7, VALID_FEATURES = 1 << 8, }; /* Traffic control. */ /* An instance of a traffic control class. Always associated with a particular * network device. * * Each TC implementation subclasses this with whatever additional data it * needs. */ struct tc { const struct tc_ops *ops; struct hmap queues; /* Contains "struct tc_queue"s. * Read by generic TC layer. * Written only by TC implementation. */ }; #define TC_INITIALIZER(TC, OPS) { OPS, HMAP_INITIALIZER(&(TC)->queues) } /* One traffic control queue. * * Each TC implementation subclasses this with whatever additional data it * needs. */ struct tc_queue { struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */ unsigned int queue_id; /* OpenFlow queue ID. */ long long int created; /* Time queue was created, in msecs. */ }; /* A particular kind of traffic control. Each implementation generally maps to * one particular Linux qdisc class. * * The functions below return 0 if successful or a positive errno value on * failure, except where otherwise noted. All of them must be provided, except * where otherwise noted. */ struct tc_ops { /* Name used by kernel in the TCA_KIND attribute of tcmsg, e.g. "htb". * This is null for tc_ops_default and tc_ops_other, for which there are no * appropriate values. */ const char *linux_name; /* Name used in OVS database, e.g. "linux-htb". Must be nonnull. */ const char *ovs_name; /* Number of supported OpenFlow queues, 0 for qdiscs that have no * queues. The queues are numbered 0 through n_queues - 1. */ unsigned int n_queues; /* Called to install this TC class on 'netdev'. The implementation should * make the Netlink calls required to set up 'netdev' with the right qdisc * and configure it according to 'details'. The implementation may assume * that the current qdisc is the default; that is, there is no need for it * to delete the current qdisc before installing itself. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml * (which is built as ovs-vswitchd.conf.db(8)). * * This function must return 0 if and only if it sets 'netdev->tc' to an * initialized 'struct tc'. * * (This function is null for tc_ops_other, which cannot be installed. For * other TC classes it should always be nonnull.) */ int (*tc_install)(struct netdev *netdev, const struct smap *details); /* Called when the netdev code determines (through a Netlink query) that * this TC class's qdisc is installed on 'netdev', but we didn't install * it ourselves and so don't know any of the details. * * 'nlmsg' is the kernel reply to a RTM_GETQDISC Netlink message for * 'netdev'. The TCA_KIND attribute of 'nlmsg' is 'linux_name'. The * implementation should parse the other attributes of 'nlmsg' as * necessary to determine its configuration. If necessary it should also * use Netlink queries to determine the configuration of queues on * 'netdev'. * * This function must return 0 if and only if it sets 'netdev->tc' to an * initialized 'struct tc'. */ int (*tc_load)(struct netdev *netdev, struct ofpbuf *nlmsg); /* Destroys the data structures allocated by the implementation as part of * 'tc'. (This includes destroying 'tc->queues' by calling * tc_destroy(tc). * * The implementation should not need to perform any Netlink calls. If * desirable, the caller is responsible for deconfiguring the kernel qdisc. * (But it may not be desirable.) * * This function may be null if 'tc' is trivial. */ void (*tc_destroy)(struct tc *tc); /* Retrieves details of 'netdev->tc' configuration into 'details'. * * The implementation should not need to perform any Netlink calls, because * the 'tc_install' or 'tc_load' that instantiated 'netdev->tc' should have * cached the configuration. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml * (which is built as ovs-vswitchd.conf.db(8)). * * This function may be null if 'tc' is not configurable. */ int (*qdisc_get)(const struct netdev *netdev, struct smap *details); /* Reconfigures 'netdev->tc' according to 'details', performing any * required Netlink calls to complete the reconfiguration. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "QoS" table in vswitchd/vswitch.xml * (which is built as ovs-vswitchd.conf.db(8)). * * This function may be null if 'tc' is not configurable. */ int (*qdisc_set)(struct netdev *, const struct smap *details); /* Retrieves details of 'queue' on 'netdev->tc' into 'details'. 'queue' is * one of the 'struct tc_queue's within 'netdev->tc->queues'. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * The implementation should not need to perform any Netlink calls, because * the 'tc_install' or 'tc_load' that instantiated 'netdev->tc' should have * cached the queue configuration. * * This function may be null if 'tc' does not have queues ('n_queues' is * 0). */ int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue, struct smap *details); /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to * 'details', perfoming any required Netlink calls to complete the * reconfiguration. The caller ensures that 'queue_id' is less than * 'n_queues'. * * The contents of 'details' should be documented as valid for 'ovs_name' * in the "other_config" column in the "Queue" table in * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). * * This function may be null if 'tc' does not have queues or its queues are * not configurable. */ int (*class_set)(struct netdev *, unsigned int queue_id, const struct smap *details); /* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct * tc_queue's within 'netdev->tc->queues'. * * This function may be null if 'tc' does not have queues or its queues * cannot be deleted. */ int (*class_delete)(struct netdev *, struct tc_queue *queue); /* Obtains stats for 'queue' from 'netdev->tc'. 'queue' is one of the * 'struct tc_queue's within 'netdev->tc->queues'. * * On success, initializes '*stats'. * * This function may be null if 'tc' does not have queues or if it cannot * report queue statistics. */ int (*class_get_stats)(const struct netdev *netdev, const struct tc_queue *queue, struct netdev_queue_stats *stats); /* Extracts queue stats from 'nlmsg', which is a response to a * RTM_GETTCLASS message, and passes them to 'cb' along with 'aux'. * * This function may be null if 'tc' does not have queues or if it cannot * report queue statistics. */ int (*class_dump_stats)(const struct netdev *netdev, const struct ofpbuf *nlmsg, netdev_dump_queue_stats_cb *cb, void *aux); }; static void tc_init(struct tc *tc, const struct tc_ops *ops) { tc->ops = ops; hmap_init(&tc->queues); } static void tc_destroy(struct tc *tc) { hmap_destroy(&tc->queues); } static const struct tc_ops tc_ops_htb; static const struct tc_ops tc_ops_hfsc; static const struct tc_ops tc_ops_codel; static const struct tc_ops tc_ops_fqcodel; static const struct tc_ops tc_ops_sfq; static const struct tc_ops tc_ops_default; static const struct tc_ops tc_ops_other; static const struct tc_ops *const tcs[] = { &tc_ops_htb, /* Hierarchy token bucket (see tc-htb(8)). */ &tc_ops_hfsc, /* Hierarchical fair service curve. */ &tc_ops_codel, /* Controlled delay */ &tc_ops_fqcodel, /* Fair queue controlled delay */ &tc_ops_sfq, /* Stochastic fair queueing */ &tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */ &tc_ops_other, /* Some other qdisc. */ NULL }; static unsigned int tc_make_handle(unsigned int major, unsigned int minor); static unsigned int tc_get_major(unsigned int handle); static unsigned int tc_get_minor(unsigned int handle); static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks); static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size); static unsigned int tc_buffer_per_jiffy(unsigned int rate); static struct tcmsg *tc_make_request(const struct netdev *, int type, unsigned int flags, struct ofpbuf *); static int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp); static int tc_add_del_ingress_qdisc(struct netdev *netdev, bool add); static int tc_add_policer(struct netdev *, uint32_t kbits_rate, uint32_t kbits_burst); static int tc_parse_qdisc(const struct ofpbuf *, const char **kind, struct nlattr **options); static int tc_parse_class(const struct ofpbuf *, unsigned int *queue_id, struct nlattr **options, struct netdev_queue_stats *); static int tc_query_class(const struct netdev *, unsigned int handle, unsigned int parent, struct ofpbuf **replyp); static int tc_delete_class(const struct netdev *, unsigned int handle); static int tc_del_qdisc(struct netdev *netdev); static int tc_query_qdisc(const struct netdev *netdev); static int tc_calc_cell_log(unsigned int mtu); static void tc_fill_rate(struct tc_ratespec *rate, uint64_t bps, int mtu); static void tc_put_rtab(struct ofpbuf *, uint16_t type, const struct tc_ratespec *rate); static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes); struct netdev_linux { struct netdev up; /* Protects all members below. */ struct ovs_mutex mutex; unsigned int cache_valid; bool miimon; /* Link status of last poll. */ long long int miimon_interval; /* Miimon Poll rate. Disabled if <= 0. */ struct timer miimon_timer; /* The following are figured out "on demand" only. They are only valid * when the corresponding VALID_* bit in 'cache_valid' is set. */ int ifindex; struct eth_addr etheraddr; struct in_addr address, netmask; struct in6_addr in6; int mtu; unsigned int ifi_flags; long long int carrier_resets; uint32_t kbits_rate; /* Policing data. */ uint32_t kbits_burst; int vport_stats_error; /* Cached error code from vport_get_stats(). 0 or an errno value. */ int netdev_mtu_error; /* Cached error code from SIOCGIFMTU or SIOCSIFMTU. */ int ether_addr_error; /* Cached error code from set/get etheraddr. */ int netdev_policing_error; /* Cached error code from set policing. */ int get_features_error; /* Cached error code from ETHTOOL_GSET. */ int get_ifindex_error; /* Cached error code from SIOCGIFINDEX. */ int in4_error; /* Cached error code from reading in4 addr. */ int in6_error; /* Cached error code from reading in6 addr. */ enum netdev_features current; /* Cached from ETHTOOL_GSET. */ enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */ enum netdev_features supported; /* Cached from ETHTOOL_GSET. */ struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */ struct tc *tc; /* For devices of class netdev_tap_class only. */ int tap_fd; }; struct netdev_rxq_linux { struct netdev_rxq up; bool is_tap; int fd; }; /* This is set pretty low because we probably won't learn anything from the * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); /* Polling miimon status for all ports causes performance degradation when * handling a large number of ports. If there are no devices using miimon, then * we skip netdev_linux_miimon_run() and netdev_linux_miimon_wait(). * * Readers do not depend on this variable synchronizing with the related * changes in the device miimon status, so we can use atomic_count. */ static atomic_count miimon_cnt = ATOMIC_COUNT_INIT(0); static void netdev_linux_run(void); static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *, int cmd, const char *cmd_name); static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *, int cmd, const char *cmd_name); static int get_flags(const struct netdev *, unsigned int *flags); static int set_flags(const char *, unsigned int flags); static int update_flags(struct netdev_linux *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) OVS_REQUIRES(netdev->mutex); static int do_get_ifindex(const char *netdev_name); static int get_ifindex(const struct netdev *, int *ifindexp); static int do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr); static int get_etheraddr(const char *netdev_name, struct eth_addr *ea); static int set_etheraddr(const char *netdev_name, const struct eth_addr); static int get_stats_via_netlink(const struct netdev *, struct netdev_stats *); static int af_packet_sock(void); static bool netdev_linux_miimon_enabled(void); static void netdev_linux_miimon_run(void); static void netdev_linux_miimon_wait(void); static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup); static bool is_netdev_linux_class(const struct netdev_class *netdev_class) { return netdev_class->run == netdev_linux_run; } static bool is_tap_netdev(const struct netdev *netdev) { return netdev_get_class(netdev) == &netdev_tap_class; } static struct netdev_linux * netdev_linux_cast(const struct netdev *netdev) { ovs_assert(is_netdev_linux_class(netdev_get_class(netdev))); return CONTAINER_OF(netdev, struct netdev_linux, up); } static struct netdev_rxq_linux * netdev_rxq_linux_cast(const struct netdev_rxq *rx) { ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev))); return CONTAINER_OF(rx, struct netdev_rxq_linux, up); } static void netdev_linux_update(struct netdev_linux *netdev, const struct rtnetlink_change *) OVS_REQUIRES(netdev->mutex); static void netdev_linux_changed(struct netdev_linux *netdev, unsigned int ifi_flags, unsigned int mask) OVS_REQUIRES(netdev->mutex); /* Returns a NETLINK_ROUTE socket listening for RTNLGRP_LINK, * RTNLGRP_IPV4_IFADDR and RTNLGRP_IPV6_IFADDR changes, or NULL * if no such socket could be created. */ static struct nl_sock * netdev_linux_notify_sock(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static struct nl_sock *sock; unsigned int mcgroups[3] = {RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR}; if (ovsthread_once_start(&once)) { int error; error = nl_sock_create(NETLINK_ROUTE, &sock); if (!error) { size_t i; for (i = 0; i < ARRAY_SIZE(mcgroups); i++) { error = nl_sock_join_mcgroup(sock, mcgroups[i]); if (error) { nl_sock_destroy(sock); sock = NULL; break; } } } ovsthread_once_done(&once); } return sock; } static bool netdev_linux_miimon_enabled(void) { return atomic_count_get(&miimon_cnt) > 0; } static void netdev_linux_run(void) { struct nl_sock *sock; int error; if (netdev_linux_miimon_enabled()) { netdev_linux_miimon_run(); } sock = netdev_linux_notify_sock(); if (!sock) { return; } do { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); uint64_t buf_stub[4096 / 8]; struct ofpbuf buf; ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); error = nl_sock_recv(sock, &buf, false); if (!error) { struct rtnetlink_change change; if (rtnetlink_parse(&buf, &change)) { struct netdev *netdev_ = netdev_from_name(change.ifname); if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); ovs_mutex_lock(&netdev->mutex); netdev_linux_update(netdev, &change); ovs_mutex_unlock(&netdev->mutex); } netdev_close(netdev_); } } else if (error == ENOBUFS) { struct shash device_shash; struct shash_node *node; nl_sock_drain(sock); shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { struct netdev *netdev_ = node->data; struct netdev_linux *netdev = netdev_linux_cast(netdev_); unsigned int flags; ovs_mutex_lock(&netdev->mutex); get_flags(netdev_, &flags); netdev_linux_changed(netdev, flags, 0); ovs_mutex_unlock(&netdev->mutex); netdev_close(netdev_); } shash_destroy(&device_shash); } else if (error != EAGAIN) { VLOG_WARN_RL(&rl, "error reading or parsing netlink (%s)", ovs_strerror(error)); } ofpbuf_uninit(&buf); } while (!error); } static void netdev_linux_wait(void) { struct nl_sock *sock; if (netdev_linux_miimon_enabled()) { netdev_linux_miimon_wait(); } sock = netdev_linux_notify_sock(); if (sock) { nl_sock_wait(sock, POLLIN); } } static void netdev_linux_changed(struct netdev_linux *dev, unsigned int ifi_flags, unsigned int mask) OVS_REQUIRES(dev->mutex) { netdev_change_seq_changed(&dev->up); if ((dev->ifi_flags ^ ifi_flags) & IFF_RUNNING) { dev->carrier_resets++; } dev->ifi_flags = ifi_flags; dev->cache_valid &= mask; } static void netdev_linux_update(struct netdev_linux *dev, const struct rtnetlink_change *change) OVS_REQUIRES(dev->mutex) { if (rtnetlink_type_is_rtnlgrp_link(change->nlmsg_type)){ if (change->nlmsg_type == RTM_NEWLINK) { /* Keep drv-info, in4, in6. */ netdev_linux_changed(dev, change->ifi_flags, VALID_DRVINFO | VALID_IN4 | VALID_IN6); /* Update netdev from rtnl-change msg. */ if (change->mtu) { dev->mtu = change->mtu; dev->cache_valid |= VALID_MTU; dev->netdev_mtu_error = 0; } if (!eth_addr_is_zero(change->mac)) { dev->etheraddr = change->mac; dev->cache_valid |= VALID_ETHERADDR; dev->ether_addr_error = 0; } dev->ifindex = change->if_index; dev->cache_valid |= VALID_IFINDEX; dev->get_ifindex_error = 0; } else { netdev_linux_changed(dev, change->ifi_flags, 0); } } else if (rtnetlink_type_is_rtnlgrp_addr(change->nlmsg_type)) { /* Invalidates in4, in6. */ netdev_linux_changed(dev, dev->ifi_flags, ~(VALID_IN4 | VALID_IN6)); } else { OVS_NOT_REACHED(); } } static struct netdev * netdev_linux_alloc(void) { struct netdev_linux *netdev = xzalloc(sizeof *netdev); return &netdev->up; } static void netdev_linux_common_construct(struct netdev_linux *netdev) { ovs_mutex_init(&netdev->mutex); } /* Creates system and internal devices. */ static int netdev_linux_construct(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; netdev_linux_common_construct(netdev); error = get_flags(&netdev->up, &netdev->ifi_flags); if (error == ENODEV) { if (netdev->up.netdev_class != &netdev_internal_class) { /* The device does not exist, so don't allow it to be opened. */ return ENODEV; } else { /* "Internal" netdevs have to be created as netdev objects before * they exist in the kernel, because creating them in the kernel * happens by passing a netdev object to dpif_port_add(). * Therefore, ignore the error. */ } } return 0; } /* For most types of netdevs we open the device for each call of * netdev_open(). However, this is not the case with tap devices, * since it is only possible to open the device once. In this * situation we share a single file descriptor, and consequently * buffers, across all readers. Therefore once data is read it will * be unavailable to other reads for tap devices. */ static int netdev_linux_construct_tap(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); static const char tap_dev[] = "/dev/net/tun"; const char *name = netdev_->name; struct ifreq ifr; int error; netdev_linux_common_construct(netdev); /* Open tap device. */ netdev->tap_fd = open(tap_dev, O_RDWR); if (netdev->tap_fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error)); return error; } /* Create tap device. */ ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) { VLOG_WARN("%s: creating tap device failed: %s", name, ovs_strerror(errno)); error = errno; goto error_close; } /* Make non-blocking. */ error = set_nonblocking(netdev->tap_fd); if (error) { goto error_close; } return 0; error_close: close(netdev->tap_fd); return error; } static void netdev_linux_destruct(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (netdev->tc && netdev->tc->ops->tc_destroy) { netdev->tc->ops->tc_destroy(netdev->tc); } if (netdev_get_class(netdev_) == &netdev_tap_class && netdev->tap_fd >= 0) { close(netdev->tap_fd); } if (netdev->miimon_interval > 0) { atomic_count_dec(&miimon_cnt); } ovs_mutex_destroy(&netdev->mutex); } static void netdev_linux_dealloc(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); free(netdev); } static struct netdev_rxq * netdev_linux_rxq_alloc(void) { struct netdev_rxq_linux *rx = xzalloc(sizeof *rx); return &rx->up; } static int netdev_linux_rxq_construct(struct netdev_rxq *rxq_) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); struct netdev *netdev_ = rx->up.netdev; struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); rx->is_tap = is_tap_netdev(netdev_); if (rx->is_tap) { rx->fd = netdev->tap_fd; } else { struct sockaddr_ll sll; int ifindex, val; /* Result of tcpdump -dd inbound */ static const struct sock_filter filt[] = { { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */ { 0x15, 0, 1, 0x00000004 }, /* jeq #4 jt 2 jf 3 */ { 0x6, 0, 0, 0x00000000 }, /* ret #0 */ { 0x6, 0, 0, 0x0000ffff } /* ret #65535 */ }; static const struct sock_fprog fprog = { ARRAY_SIZE(filt), (struct sock_filter *) filt }; /* Create file descriptor. */ rx->fd = socket(PF_PACKET, SOCK_RAW, 0); if (rx->fd < 0) { error = errno; VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error)); goto error; } val = 1; if (setsockopt(rx->fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof val)) { error = errno; VLOG_ERR("%s: failed to mark socket for auxdata (%s)", netdev_get_name(netdev_), ovs_strerror(error)); goto error; } /* Set non-blocking mode. */ error = set_nonblocking(rx->fd); if (error) { goto error; } /* Get ethernet device index. */ error = get_ifindex(&netdev->up, &ifindex); if (error) { goto error; } /* Bind to specific ethernet device. */ memset(&sll, 0, sizeof sll); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; sll.sll_protocol = htons(ETH_P_ALL); if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; VLOG_ERR("%s: failed to bind raw socket (%s)", netdev_get_name(netdev_), ovs_strerror(error)); goto error; } /* Filter for only inbound packets. */ error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof fprog); if (error) { error = errno; VLOG_ERR("%s: failed to attach filter (%s)", netdev_get_name(netdev_), ovs_strerror(error)); goto error; } } ovs_mutex_unlock(&netdev->mutex); return 0; error: if (rx->fd >= 0) { close(rx->fd); } ovs_mutex_unlock(&netdev->mutex); return error; } static void netdev_linux_rxq_destruct(struct netdev_rxq *rxq_) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); if (!rx->is_tap) { close(rx->fd); } } static void netdev_linux_rxq_dealloc(struct netdev_rxq *rxq_) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); free(rx); } static ovs_be16 auxdata_to_vlan_tpid(const struct tpacket_auxdata *aux, bool double_tagged) { if (aux->tp_status & TP_STATUS_VLAN_TPID_VALID) { return htons(aux->tp_vlan_tpid); } else if (double_tagged) { return htons(ETH_TYPE_VLAN_8021AD); } else { return htons(ETH_TYPE_VLAN_8021Q); } } static bool auxdata_has_vlan_tci(const struct tpacket_auxdata *aux) { return aux->tp_vlan_tci || aux->tp_status & TP_STATUS_VLAN_VALID; } static int netdev_linux_rxq_recv_sock(int fd, struct dp_packet *buffer) { size_t size; ssize_t retval; struct iovec iov; struct cmsghdr *cmsg; union { struct cmsghdr cmsg; char buffer[CMSG_SPACE(sizeof(struct tpacket_auxdata))]; } cmsg_buffer; struct msghdr msgh; /* Reserve headroom for a single VLAN tag */ dp_packet_reserve(buffer, VLAN_HEADER_LEN); size = dp_packet_tailroom(buffer); iov.iov_base = dp_packet_data(buffer); iov.iov_len = size; msgh.msg_name = NULL; msgh.msg_namelen = 0; msgh.msg_iov = &iov; msgh.msg_iovlen = 1; msgh.msg_control = &cmsg_buffer; msgh.msg_controllen = sizeof cmsg_buffer; msgh.msg_flags = 0; do { retval = recvmsg(fd, &msgh, MSG_TRUNC); } while (retval < 0 && errno == EINTR); if (retval < 0) { return errno; } else if (retval > size) { return EMSGSIZE; } dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) { const struct tpacket_auxdata *aux; if (cmsg->cmsg_level != SOL_PACKET || cmsg->cmsg_type != PACKET_AUXDATA || cmsg->cmsg_len < CMSG_LEN(sizeof(struct tpacket_auxdata))) { continue; } aux = ALIGNED_CAST(struct tpacket_auxdata *, CMSG_DATA(cmsg)); if (auxdata_has_vlan_tci(aux)) { struct eth_header *eth; bool double_tagged; if (retval < ETH_HEADER_LEN) { return EINVAL; } eth = dp_packet_data(buffer); double_tagged = eth->eth_type == htons(ETH_TYPE_VLAN_8021Q); eth_push_vlan(buffer, auxdata_to_vlan_tpid(aux, double_tagged), htons(aux->tp_vlan_tci)); break; } } return 0; } static int netdev_linux_rxq_recv_tap(int fd, struct dp_packet *buffer) { ssize_t retval; size_t size = dp_packet_tailroom(buffer); do { retval = read(fd, dp_packet_data(buffer), size); } while (retval < 0 && errno == EINTR); if (retval < 0) { return errno; } dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); return 0; } static int netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet **packets, int *c) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); struct netdev *netdev = rx->up.netdev; struct dp_packet *buffer; ssize_t retval; int mtu; if (netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu)) { mtu = ETH_PAYLOAD_MAX; } buffer = dp_packet_new_with_headroom(VLAN_ETH_HEADER_LEN + mtu, DP_NETDEV_HEADROOM); retval = (rx->is_tap ? netdev_linux_rxq_recv_tap(rx->fd, buffer) : netdev_linux_rxq_recv_sock(rx->fd, buffer)); if (retval) { if (retval != EAGAIN && retval != EMSGSIZE) { VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", netdev_rxq_get_name(rxq_), ovs_strerror(errno)); } dp_packet_delete(buffer); } else { dp_packet_pad(buffer); packets[0] = buffer; *c = 1; } return retval; } static void netdev_linux_rxq_wait(struct netdev_rxq *rxq_) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); poll_fd_wait(rx->fd, POLLIN); } static int netdev_linux_rxq_drain(struct netdev_rxq *rxq_) { struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_); if (rx->is_tap) { struct ifreq ifr; int error = af_inet_ifreq_ioctl(netdev_rxq_get_name(rxq_), &ifr, SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; } drain_fd(rx->fd, ifr.ifr_qlen); return 0; } else { return drain_rcvbuf(rx->fd); } } /* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive * errno value. Returns EAGAIN without blocking if the packet cannot be queued * immediately. Returns EMSGSIZE if a partial packet was transmitted or if * the packet is too big or too small to transmit on the device. * * The caller retains ownership of 'buffer' in all cases. * * The kernel maintains a packet transmission queue, so the caller is not * expected to do additional queuing of packets. */ static int netdev_linux_send(struct netdev *netdev_, int qid OVS_UNUSED, struct dp_packet **pkts, int cnt, bool may_steal) { int i; int error = 0; /* 'i' is incremented only if there's no error */ for (i = 0; i < cnt;) { const void *data = dp_packet_data(pkts[i]); size_t size = dp_packet_size(pkts[i]); ssize_t retval; if (!is_tap_netdev(netdev_)) { /* Use our AF_PACKET socket to send to this device. */ struct sockaddr_ll sll; struct msghdr msg; struct iovec iov; int ifindex; int sock; sock = af_packet_sock(); if (sock < 0) { return -sock; } ifindex = netdev_get_ifindex(netdev_); if (ifindex < 0) { return -ifindex; } /* We don't bother setting most fields in sockaddr_ll because the * kernel ignores them for SOCK_RAW. */ memset(&sll, 0, sizeof sll); sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; iov.iov_base = CONST_CAST(void *, data); iov.iov_len = size; msg.msg_name = &sll; msg.msg_namelen = sizeof sll; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; retval = sendmsg(sock, &msg, 0); } else { /* Use the tap fd to send to this device. This is essential for * tap devices, because packets sent to a tap device with an * AF_PACKET socket will loop back to be *received* again on the * tap device. This doesn't occur on other interface types * because we attach a socket filter to the rx socket. */ struct netdev_linux *netdev = netdev_linux_cast(netdev_); retval = write(netdev->tap_fd, data, size); } if (retval < 0) { /* The Linux AF_PACKET implementation never blocks waiting for room * for packets, instead returning ENOBUFS. Translate this into * EAGAIN for the caller. */ error = errno == ENOBUFS ? EAGAIN : errno; if (error == EINTR) { /* continue without incrementing 'i', i.e. retry this packet */ continue; } break; } else if (retval != size) { VLOG_WARN_RL(&rl, "sent partial Ethernet packet (%"PRIuSIZE" bytes" " of %"PRIuSIZE") on %s", retval, size, netdev_get_name(netdev_)); error = EMSGSIZE; break; } /* Process the next packet in the batch */ i++; } if (may_steal) { for (i = 0; i < cnt; i++) { dp_packet_delete(pkts[i]); } } if (error && error != EAGAIN) { VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s", netdev_get_name(netdev_), ovs_strerror(error)); } return error; } /* Registers with the poll loop to wake up from the next call to poll_block() * when the packet transmission queue has sufficient room to transmit a packet * with netdev_send(). * * The kernel maintains a packet transmission queue, so the client is not * expected to do additional queuing of packets. Thus, this function is * unlikely to ever be used. It is included for completeness. */ static void netdev_linux_send_wait(struct netdev *netdev, int qid OVS_UNUSED) { if (is_tap_netdev(netdev)) { /* TAP device always accepts packets.*/ poll_immediate_wake(); } } /* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, * otherwise a positive errno value. */ static int netdev_linux_set_etheraddr(struct netdev *netdev_, const struct eth_addr mac) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); enum netdev_flags old_flags = 0; int error; ovs_mutex_lock(&netdev->mutex); if (netdev->cache_valid & VALID_ETHERADDR) { error = netdev->ether_addr_error; if (error || eth_addr_equals(netdev->etheraddr, mac)) { goto exit; } netdev->cache_valid &= ~VALID_ETHERADDR; } /* Tap devices must be brought down before setting the address. */ if (is_tap_netdev(netdev_)) { update_flags(netdev, NETDEV_UP, 0, &old_flags); } error = set_etheraddr(netdev_get_name(netdev_), mac); if (!error || error == ENODEV) { netdev->ether_addr_error = error; netdev->cache_valid |= VALID_ETHERADDR; if (!error) { netdev->etheraddr = mac; } } if (is_tap_netdev(netdev_) && old_flags & NETDEV_UP) { update_flags(netdev, 0, NETDEV_UP, &old_flags); } exit: ovs_mutex_unlock(&netdev->mutex); return error; } /* Copies 'netdev''s MAC address to 'mac' which is passed as param. */ static int netdev_linux_get_etheraddr(const struct netdev *netdev_, struct eth_addr *mac) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_ETHERADDR)) { netdev->ether_addr_error = get_etheraddr(netdev_get_name(netdev_), &netdev->etheraddr); netdev->cache_valid |= VALID_ETHERADDR; } error = netdev->ether_addr_error; if (!error) { *mac = netdev->etheraddr; } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup) { int error; if (!(netdev->cache_valid & VALID_MTU)) { struct ifreq ifr; netdev->netdev_mtu_error = af_inet_ifreq_ioctl( netdev_get_name(&netdev->up), &ifr, SIOCGIFMTU, "SIOCGIFMTU"); netdev->mtu = ifr.ifr_mtu; netdev->cache_valid |= VALID_MTU; } error = netdev->netdev_mtu_error; if (!error) { *mtup = netdev->mtu; } return error; } /* Returns the maximum size of transmitted (and received) packets on 'netdev', * in bytes, not including the hardware header; thus, this is typically 1500 * bytes for Ethernet devices. */ static int netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = netdev_linux_get_mtu__(netdev, mtup); ovs_mutex_unlock(&netdev->mutex); return error; } /* Sets the maximum size of transmitted (MTU) for given device using linux * networking ioctl interface. */ static int netdev_linux_set_mtu(const struct netdev *netdev_, int mtu) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct ifreq ifr; int error; ovs_mutex_lock(&netdev->mutex); if (netdev->cache_valid & VALID_MTU) { error = netdev->netdev_mtu_error; if (error || netdev->mtu == mtu) { goto exit; } netdev->cache_valid &= ~VALID_MTU; } ifr.ifr_mtu = mtu; error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr, SIOCSIFMTU, "SIOCSIFMTU"); if (!error || error == ENODEV) { netdev->netdev_mtu_error = error; netdev->mtu = ifr.ifr_mtu; netdev->cache_valid |= VALID_MTU; } exit: ovs_mutex_unlock(&netdev->mutex); return error; } /* Returns the ifindex of 'netdev', if successful, as a positive number. * On failure, returns a negative errno value. */ static int netdev_linux_get_ifindex(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int ifindex, error; ovs_mutex_lock(&netdev->mutex); error = get_ifindex(netdev_, &ifindex); ovs_mutex_unlock(&netdev->mutex); return error ? -error : ifindex; } static int netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); ovs_mutex_lock(&netdev->mutex); if (netdev->miimon_interval > 0) { *carrier = netdev->miimon; } else { *carrier = (netdev->ifi_flags & IFF_RUNNING) != 0; } ovs_mutex_unlock(&netdev->mutex); return 0; } static long long int netdev_linux_get_carrier_resets(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); long long int carrier_resets; ovs_mutex_lock(&netdev->mutex); carrier_resets = netdev->carrier_resets; ovs_mutex_unlock(&netdev->mutex); return carrier_resets; } static int netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name, struct mii_ioctl_data *data) { struct ifreq ifr; int error; memset(&ifr, 0, sizeof ifr); memcpy(&ifr.ifr_data, data, sizeof *data); error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name); memcpy(data, &ifr.ifr_data, sizeof *data); return error; } static int netdev_linux_get_miimon(const char *name, bool *miimon) { struct mii_ioctl_data data; int error; *miimon = false; memset(&data, 0, sizeof data); error = netdev_linux_do_miimon(name, SIOCGMIIPHY, "SIOCGMIIPHY", &data); if (!error) { /* data.phy_id is filled out by previous SIOCGMIIPHY miimon call. */ data.reg_num = MII_BMSR; error = netdev_linux_do_miimon(name, SIOCGMIIREG, "SIOCGMIIREG", &data); if (!error) { *miimon = !!(data.val_out & BMSR_LSTATUS); } } if (error) { struct ethtool_cmd ecmd; VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool", name); COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK, "ETHTOOL_GLINK"); if (!error) { struct ethtool_value eval; memcpy(&eval, &ecmd, sizeof eval); *miimon = !!eval.data; } else { VLOG_WARN_RL(&rl, "%s: ethtool link status failed", name); } } return error; } static int netdev_linux_set_miimon_interval(struct netdev *netdev_, long long int interval) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); ovs_mutex_lock(&netdev->mutex); interval = interval > 0 ? MAX(interval, 100) : 0; if (netdev->miimon_interval != interval) { if (interval && !netdev->miimon_interval) { atomic_count_inc(&miimon_cnt); } else if (!interval && netdev->miimon_interval) { atomic_count_dec(&miimon_cnt); } netdev->miimon_interval = interval; timer_set_expired(&netdev->miimon_timer); } ovs_mutex_unlock(&netdev->mutex); return 0; } static void netdev_linux_miimon_run(void) { struct shash device_shash; struct shash_node *node; shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { struct netdev *netdev = node->data; struct netdev_linux *dev = netdev_linux_cast(netdev); bool miimon; ovs_mutex_lock(&dev->mutex); if (dev->miimon_interval > 0 && timer_expired(&dev->miimon_timer)) { netdev_linux_get_miimon(dev->up.name, &miimon); if (miimon != dev->miimon) { dev->miimon = miimon; netdev_linux_changed(dev, dev->ifi_flags, 0); } timer_set_duration(&dev->miimon_timer, dev->miimon_interval); } ovs_mutex_unlock(&dev->mutex); netdev_close(netdev); } shash_destroy(&device_shash); } static void netdev_linux_miimon_wait(void) { struct shash device_shash; struct shash_node *node; shash_init(&device_shash); netdev_get_devices(&netdev_linux_class, &device_shash); SHASH_FOR_EACH (node, &device_shash) { struct netdev *netdev = node->data; struct netdev_linux *dev = netdev_linux_cast(netdev); ovs_mutex_lock(&dev->mutex); if (dev->miimon_interval > 0) { timer_wait(&dev->miimon_timer); } ovs_mutex_unlock(&dev->mutex); netdev_close(netdev); } shash_destroy(&device_shash); } static void swap_uint64(uint64_t *a, uint64_t *b) { uint64_t tmp = *a; *a = *b; *b = tmp; } /* Copies 'src' into 'dst', performing format conversion in the process. * * 'src' is allowed to be misaligned. */ static void netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst, const struct ovs_vport_stats *src) { dst->rx_packets = get_32aligned_u64(&src->rx_packets); dst->tx_packets = get_32aligned_u64(&src->tx_packets); dst->rx_bytes = get_32aligned_u64(&src->rx_bytes); dst->tx_bytes = get_32aligned_u64(&src->tx_bytes); dst->rx_errors = get_32aligned_u64(&src->rx_errors); dst->tx_errors = get_32aligned_u64(&src->tx_errors); dst->rx_dropped = get_32aligned_u64(&src->rx_dropped); dst->tx_dropped = get_32aligned_u64(&src->tx_dropped); dst->multicast = 0; dst->collisions = 0; dst->rx_length_errors = 0; dst->rx_over_errors = 0; dst->rx_crc_errors = 0; dst->rx_frame_errors = 0; dst->rx_fifo_errors = 0; dst->rx_missed_errors = 0; dst->tx_aborted_errors = 0; dst->tx_carrier_errors = 0; dst->tx_fifo_errors = 0; dst->tx_heartbeat_errors = 0; dst->tx_window_errors = 0; } static int get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats) { struct dpif_netlink_vport reply; struct ofpbuf *buf; int error; error = dpif_netlink_vport_get(netdev_get_name(netdev), &reply, &buf); if (error) { return error; } else if (!reply.stats) { ofpbuf_delete(buf); return EOPNOTSUPP; } netdev_stats_from_ovs_vport_stats(stats, reply.stats); ofpbuf_delete(buf); return 0; } static void get_stats_via_vport(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (!netdev->vport_stats_error || !(netdev->cache_valid & VALID_VPORT_STAT_ERROR)) { int error; error = get_stats_via_vport__(netdev_, stats); if (error && error != ENOENT && error != ENODEV) { VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed " "(%s)", netdev_get_name(netdev_), ovs_strerror(error)); } netdev->vport_stats_error = error; netdev->cache_valid |= VALID_VPORT_STAT_ERROR; } } /* Retrieves current device stats for 'netdev-linux'. */ static int netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct netdev_stats dev_stats; int error; ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); error = get_stats_via_netlink(netdev_, &dev_stats); if (error) { if (!netdev->vport_stats_error) { error = 0; } } else if (netdev->vport_stats_error) { /* stats not available from OVS then use netdev stats. */ *stats = dev_stats; } else { /* Use kernel netdev's packet and byte counts since vport's counters * do not reflect packet counts on the wire when GSO, TSO or GRO are * enabled. */ stats->rx_packets = dev_stats.rx_packets; stats->rx_bytes = dev_stats.rx_bytes; stats->tx_packets = dev_stats.tx_packets; stats->tx_bytes = dev_stats.tx_bytes; stats->rx_errors += dev_stats.rx_errors; stats->tx_errors += dev_stats.tx_errors; stats->rx_dropped += dev_stats.rx_dropped; stats->tx_dropped += dev_stats.tx_dropped; stats->multicast += dev_stats.multicast; stats->collisions += dev_stats.collisions; stats->rx_length_errors += dev_stats.rx_length_errors; stats->rx_over_errors += dev_stats.rx_over_errors; stats->rx_crc_errors += dev_stats.rx_crc_errors; stats->rx_frame_errors += dev_stats.rx_frame_errors; stats->rx_fifo_errors += dev_stats.rx_fifo_errors; stats->rx_missed_errors += dev_stats.rx_missed_errors; stats->tx_aborted_errors += dev_stats.tx_aborted_errors; stats->tx_carrier_errors += dev_stats.tx_carrier_errors; stats->tx_fifo_errors += dev_stats.tx_fifo_errors; stats->tx_heartbeat_errors += dev_stats.tx_heartbeat_errors; stats->tx_window_errors += dev_stats.tx_window_errors; } ovs_mutex_unlock(&netdev->mutex); return error; } /* Retrieves current device stats for 'netdev-tap' netdev or * netdev-internal. */ static int netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct netdev_stats dev_stats; int error; ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); error = get_stats_via_netlink(netdev_, &dev_stats); if (error) { if (!netdev->vport_stats_error) { error = 0; } } else if (netdev->vport_stats_error) { /* Transmit and receive stats will appear to be swapped relative to the * other ports since we are the one sending the data, not a remote * computer. For consistency, we swap them back here. This does not * apply if we are getting stats from the vport layer because it always * tracks stats from the perspective of the switch. */ *stats = dev_stats; swap_uint64(&stats->rx_packets, &stats->tx_packets); swap_uint64(&stats->rx_bytes, &stats->tx_bytes); swap_uint64(&stats->rx_errors, &stats->tx_errors); swap_uint64(&stats->rx_dropped, &stats->tx_dropped); stats->rx_length_errors = 0; stats->rx_over_errors = 0; stats->rx_crc_errors = 0; stats->rx_frame_errors = 0; stats->rx_fifo_errors = 0; stats->rx_missed_errors = 0; stats->tx_aborted_errors = 0; stats->tx_carrier_errors = 0; stats->tx_fifo_errors = 0; stats->tx_heartbeat_errors = 0; stats->tx_window_errors = 0; } else { /* Use kernel netdev's packet and byte counts since vport counters * do not reflect packet counts on the wire when GSO, TSO or GRO * are enabled. */ stats->rx_packets = dev_stats.tx_packets; stats->rx_bytes = dev_stats.tx_bytes; stats->tx_packets = dev_stats.rx_packets; stats->tx_bytes = dev_stats.rx_bytes; stats->rx_dropped += dev_stats.tx_dropped; stats->tx_dropped += dev_stats.rx_dropped; stats->rx_errors += dev_stats.tx_errors; stats->tx_errors += dev_stats.rx_errors; stats->multicast += dev_stats.multicast; stats->collisions += dev_stats.collisions; } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_internal_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); error = netdev->vport_stats_error; ovs_mutex_unlock(&netdev->mutex); return error; } static void netdev_linux_read_features(struct netdev_linux *netdev) { struct ethtool_cmd ecmd; uint32_t speed; int error; if (netdev->cache_valid & VALID_FEATURES) { return; } COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(netdev->up.name, &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { goto out; } /* Supported features. */ netdev->supported = 0; if (ecmd.supported & SUPPORTED_10baseT_Half) { netdev->supported |= NETDEV_F_10MB_HD; } if (ecmd.supported & SUPPORTED_10baseT_Full) { netdev->supported |= NETDEV_F_10MB_FD; } if (ecmd.supported & SUPPORTED_100baseT_Half) { netdev->supported |= NETDEV_F_100MB_HD; } if (ecmd.supported & SUPPORTED_100baseT_Full) { netdev->supported |= NETDEV_F_100MB_FD; } if (ecmd.supported & SUPPORTED_1000baseT_Half) { netdev->supported |= NETDEV_F_1GB_HD; } if ((ecmd.supported & SUPPORTED_1000baseT_Full) || (ecmd.supported & SUPPORTED_1000baseKX_Full)) { netdev->supported |= NETDEV_F_1GB_FD; } if ((ecmd.supported & SUPPORTED_10000baseT_Full) || (ecmd.supported & SUPPORTED_10000baseKX4_Full) || (ecmd.supported & SUPPORTED_10000baseKR_Full) || (ecmd.supported & SUPPORTED_10000baseR_FEC)) { netdev->supported |= NETDEV_F_10GB_FD; } if ((ecmd.supported & SUPPORTED_40000baseKR4_Full) || (ecmd.supported & SUPPORTED_40000baseCR4_Full) || (ecmd.supported & SUPPORTED_40000baseSR4_Full) || (ecmd.supported & SUPPORTED_40000baseLR4_Full)) { netdev->supported |= NETDEV_F_40GB_FD; } if (ecmd.supported & SUPPORTED_TP) { netdev->supported |= NETDEV_F_COPPER; } if (ecmd.supported & SUPPORTED_FIBRE) { netdev->supported |= NETDEV_F_FIBER; } if (ecmd.supported & SUPPORTED_Autoneg) { netdev->supported |= NETDEV_F_AUTONEG; } if (ecmd.supported & SUPPORTED_Pause) { netdev->supported |= NETDEV_F_PAUSE; } if (ecmd.supported & SUPPORTED_Asym_Pause) { netdev->supported |= NETDEV_F_PAUSE_ASYM; } /* Advertised features. */ netdev->advertised = 0; if (ecmd.advertising & ADVERTISED_10baseT_Half) { netdev->advertised |= NETDEV_F_10MB_HD; } if (ecmd.advertising & ADVERTISED_10baseT_Full) { netdev->advertised |= NETDEV_F_10MB_FD; } if (ecmd.advertising & ADVERTISED_100baseT_Half) { netdev->advertised |= NETDEV_F_100MB_HD; } if (ecmd.advertising & ADVERTISED_100baseT_Full) { netdev->advertised |= NETDEV_F_100MB_FD; } if (ecmd.advertising & ADVERTISED_1000baseT_Half) { netdev->advertised |= NETDEV_F_1GB_HD; } if ((ecmd.advertising & ADVERTISED_1000baseT_Full) || (ecmd.advertising & ADVERTISED_1000baseKX_Full)) { netdev->advertised |= NETDEV_F_1GB_FD; } if ((ecmd.advertising & ADVERTISED_10000baseT_Full) || (ecmd.advertising & ADVERTISED_10000baseKX4_Full) || (ecmd.advertising & ADVERTISED_10000baseKR_Full) || (ecmd.advertising & ADVERTISED_10000baseR_FEC)) { netdev->advertised |= NETDEV_F_10GB_FD; } if ((ecmd.advertising & ADVERTISED_40000baseKR4_Full) || (ecmd.advertising & ADVERTISED_40000baseCR4_Full) || (ecmd.advertising & ADVERTISED_40000baseSR4_Full) || (ecmd.advertising & ADVERTISED_40000baseLR4_Full)) { netdev->advertised |= NETDEV_F_40GB_FD; } if (ecmd.advertising & ADVERTISED_TP) { netdev->advertised |= NETDEV_F_COPPER; } if (ecmd.advertising & ADVERTISED_FIBRE) { netdev->advertised |= NETDEV_F_FIBER; } if (ecmd.advertising & ADVERTISED_Autoneg) { netdev->advertised |= NETDEV_F_AUTONEG; } if (ecmd.advertising & ADVERTISED_Pause) { netdev->advertised |= NETDEV_F_PAUSE; } if (ecmd.advertising & ADVERTISED_Asym_Pause) { netdev->advertised |= NETDEV_F_PAUSE_ASYM; } /* Current settings. */ speed = ethtool_cmd_speed(&ecmd); if (speed == SPEED_10) { netdev->current = ecmd.duplex ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD; } else if (speed == SPEED_100) { netdev->current = ecmd.duplex ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD; } else if (speed == SPEED_1000) { netdev->current = ecmd.duplex ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD; } else if (speed == SPEED_10000) { netdev->current = NETDEV_F_10GB_FD; } else if (speed == 40000) { netdev->current = NETDEV_F_40GB_FD; } else if (speed == 100000) { netdev->current = NETDEV_F_100GB_FD; } else if (speed == 1000000) { netdev->current = NETDEV_F_1TB_FD; } else { netdev->current = 0; } if (ecmd.port == PORT_TP) { netdev->current |= NETDEV_F_COPPER; } else if (ecmd.port == PORT_FIBRE) { netdev->current |= NETDEV_F_FIBER; } if (ecmd.autoneg) { netdev->current |= NETDEV_F_AUTONEG; } out: netdev->cache_valid |= VALID_FEATURES; netdev->get_features_error = error; } /* Stores the features supported by 'netdev' into of '*current', '*advertised', * '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits. * Returns 0 if successful, otherwise a positive errno value. */ static int netdev_linux_get_features(const struct netdev *netdev_, enum netdev_features *current, enum netdev_features *advertised, enum netdev_features *supported, enum netdev_features *peer) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); netdev_linux_read_features(netdev); if (!netdev->get_features_error) { *current = netdev->current; *advertised = netdev->advertised; *supported = netdev->supported; *peer = 0; /* XXX */ } error = netdev->get_features_error; ovs_mutex_unlock(&netdev->mutex); return error; } /* Set the features advertised by 'netdev' to 'advertise'. */ static int netdev_linux_set_advertisements(struct netdev *netdev_, enum netdev_features advertise) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct ethtool_cmd ecmd; int error; ovs_mutex_lock(&netdev->mutex); COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { goto exit; } ecmd.advertising = 0; if (advertise & NETDEV_F_10MB_HD) { ecmd.advertising |= ADVERTISED_10baseT_Half; } if (advertise & NETDEV_F_10MB_FD) { ecmd.advertising |= ADVERTISED_10baseT_Full; } if (advertise & NETDEV_F_100MB_HD) { ecmd.advertising |= ADVERTISED_100baseT_Half; } if (advertise & NETDEV_F_100MB_FD) { ecmd.advertising |= ADVERTISED_100baseT_Full; } if (advertise & NETDEV_F_1GB_HD) { ecmd.advertising |= ADVERTISED_1000baseT_Half; } if (advertise & NETDEV_F_1GB_FD) { ecmd.advertising |= ADVERTISED_1000baseT_Full; } if (advertise & NETDEV_F_10GB_FD) { ecmd.advertising |= ADVERTISED_10000baseT_Full; } if (advertise & NETDEV_F_COPPER) { ecmd.advertising |= ADVERTISED_TP; } if (advertise & NETDEV_F_FIBER) { ecmd.advertising |= ADVERTISED_FIBRE; } if (advertise & NETDEV_F_AUTONEG) { ecmd.advertising |= ADVERTISED_Autoneg; } if (advertise & NETDEV_F_PAUSE) { ecmd.advertising |= ADVERTISED_Pause; } if (advertise & NETDEV_F_PAUSE_ASYM) { ecmd.advertising |= ADVERTISED_Asym_Pause; } COVERAGE_INC(netdev_set_ethtool); error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); exit: ovs_mutex_unlock(&netdev->mutex); return error; } /* Attempts to set input rate limiting (policing) policy. Returns 0 if * successful, otherwise a positive errno value. */ static int netdev_linux_set_policing(struct netdev *netdev_, uint32_t kbits_rate, uint32_t kbits_burst) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); const char *netdev_name = netdev_get_name(netdev_); int error; kbits_burst = (!kbits_rate ? 0 /* Force to 0 if no rate specified. */ : !kbits_burst ? 8000 /* Default to 8000 kbits if 0. */ : kbits_burst); /* Stick with user-specified value. */ ovs_mutex_lock(&netdev->mutex); if (netdev->cache_valid & VALID_POLICING) { error = netdev->netdev_policing_error; if (error || (netdev->kbits_rate == kbits_rate && netdev->kbits_burst == kbits_burst)) { /* Assume that settings haven't changed since we last set them. */ goto out; } netdev->cache_valid &= ~VALID_POLICING; } COVERAGE_INC(netdev_set_policing); /* Remove any existing ingress qdisc. */ error = tc_add_del_ingress_qdisc(netdev_, false); if (error) { VLOG_WARN_RL(&rl, "%s: removing policing failed: %s", netdev_name, ovs_strerror(error)); goto out; } if (kbits_rate) { error = tc_add_del_ingress_qdisc(netdev_, true); if (error) { VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s", netdev_name, ovs_strerror(error)); goto out; } error = tc_add_policer(netdev_, kbits_rate, kbits_burst); if (error){ VLOG_WARN_RL(&rl, "%s: adding policing action failed: %s", netdev_name, ovs_strerror(error)); goto out; } } netdev->kbits_rate = kbits_rate; netdev->kbits_burst = kbits_burst; out: if (!error || error == ENODEV) { netdev->netdev_policing_error = error; netdev->cache_valid |= VALID_POLICING; } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED, struct sset *types) { const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; if (ops->tc_install && ops->ovs_name[0] != '\0') { sset_add(types, ops->ovs_name); } } return 0; } static const struct tc_ops * tc_lookup_ovs_name(const char *name) { const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; if (!strcmp(name, ops->ovs_name)) { return ops; } } return NULL; } static const struct tc_ops * tc_lookup_linux_name(const char *name) { const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; if (ops->linux_name && !strcmp(name, ops->linux_name)) { return ops; } } return NULL; } static struct tc_queue * tc_find_queue__(const struct netdev *netdev_, unsigned int queue_id, size_t hash) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct tc_queue *queue; HMAP_FOR_EACH_IN_BUCKET (queue, hmap_node, hash, &netdev->tc->queues) { if (queue->queue_id == queue_id) { return queue; } } return NULL; } static struct tc_queue * tc_find_queue(const struct netdev *netdev, unsigned int queue_id) { return tc_find_queue__(netdev, queue_id, hash_int(queue_id, 0)); } static int netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED, const char *type, struct netdev_qos_capabilities *caps) { const struct tc_ops *ops = tc_lookup_ovs_name(type); if (!ops) { return EOPNOTSUPP; } caps->n_queues = ops->n_queues; return 0; } static int netdev_linux_get_qos(const struct netdev *netdev_, const char **typep, struct smap *details) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { *typep = netdev->tc->ops->ovs_name; error = (netdev->tc->ops->qdisc_get ? netdev->tc->ops->qdisc_get(netdev_, details) : 0); } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_set_qos(struct netdev *netdev_, const char *type, const struct smap *details) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); const struct tc_ops *new_ops; int error; new_ops = tc_lookup_ovs_name(type); if (!new_ops || !new_ops->tc_install) { return EOPNOTSUPP; } ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (error) { goto exit; } if (new_ops == netdev->tc->ops) { error = new_ops->qdisc_set ? new_ops->qdisc_set(netdev_, details) : 0; } else { /* Delete existing qdisc. */ error = tc_del_qdisc(netdev_); if (error) { goto exit; } ovs_assert(netdev->tc == NULL); /* Install new qdisc. */ error = new_ops->tc_install(netdev_, details); ovs_assert((error == 0) == (netdev->tc != NULL)); } exit: ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_get_queue(const struct netdev *netdev_, unsigned int queue_id, struct smap *details) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { struct tc_queue *queue = tc_find_queue(netdev_, queue_id); error = (queue ? netdev->tc->ops->class_get(netdev_, queue, details) : ENOENT); } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_set_queue(struct netdev *netdev_, unsigned int queue_id, const struct smap *details) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { error = (queue_id < netdev->tc->ops->n_queues && netdev->tc->ops->class_set ? netdev->tc->ops->class_set(netdev_, queue_id, details) : EINVAL); } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_delete_queue(struct netdev *netdev_, unsigned int queue_id) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { if (netdev->tc->ops->class_delete) { struct tc_queue *queue = tc_find_queue(netdev_, queue_id); error = (queue ? netdev->tc->ops->class_delete(netdev_, queue) : ENOENT); } else { error = EINVAL; } } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_get_queue_stats(const struct netdev *netdev_, unsigned int queue_id, struct netdev_queue_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { if (netdev->tc->ops->class_get_stats) { const struct tc_queue *queue = tc_find_queue(netdev_, queue_id); if (queue) { stats->created = queue->created; error = netdev->tc->ops->class_get_stats(netdev_, queue, stats); } else { error = ENOENT; } } else { error = EOPNOTSUPP; } } ovs_mutex_unlock(&netdev->mutex); return error; } struct queue_dump_state { struct nl_dump dump; struct ofpbuf buf; }; static bool start_queue_dump(const struct netdev *netdev, struct queue_dump_state *state) { struct ofpbuf request; struct tcmsg *tcmsg; tcmsg = tc_make_request(netdev, RTM_GETTCLASS, 0, &request); if (!tcmsg) { return false; } tcmsg->tcm_parent = 0; nl_dump_start(&state->dump, NETLINK_ROUTE, &request); ofpbuf_uninit(&request); ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE); return true; } static int finish_queue_dump(struct queue_dump_state *state) { ofpbuf_uninit(&state->buf); return nl_dump_done(&state->dump); } struct netdev_linux_queue_state { unsigned int *queues; size_t cur_queue; size_t n_queues; }; static int netdev_linux_queue_dump_start(const struct netdev *netdev_, void **statep) { const struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { if (netdev->tc->ops->class_get) { struct netdev_linux_queue_state *state; struct tc_queue *queue; size_t i; *statep = state = xmalloc(sizeof *state); state->n_queues = hmap_count(&netdev->tc->queues); state->cur_queue = 0; state->queues = xmalloc(state->n_queues * sizeof *state->queues); i = 0; HMAP_FOR_EACH (queue, hmap_node, &netdev->tc->queues) { state->queues[i++] = queue->queue_id; } } else { error = EOPNOTSUPP; } } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_queue_dump_next(const struct netdev *netdev_, void *state_, unsigned int *queue_idp, struct smap *details) { const struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct netdev_linux_queue_state *state = state_; int error = EOF; ovs_mutex_lock(&netdev->mutex); while (state->cur_queue < state->n_queues) { unsigned int queue_id = state->queues[state->cur_queue++]; struct tc_queue *queue = tc_find_queue(netdev_, queue_id); if (queue) { *queue_idp = queue_id; error = netdev->tc->ops->class_get(netdev_, queue, details); break; } } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_queue_dump_done(const struct netdev *netdev OVS_UNUSED, void *state_) { struct netdev_linux_queue_state *state = state_; free(state->queues); free(state); return 0; } static int netdev_linux_dump_queue_stats(const struct netdev *netdev_, netdev_dump_queue_stats_cb *cb, void *aux) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (!error) { struct queue_dump_state state; if (!netdev->tc->ops->class_dump_stats) { error = EOPNOTSUPP; } else if (!start_queue_dump(netdev_, &state)) { error = ENODEV; } else { struct ofpbuf msg; int retval; while (nl_dump_next(&state.dump, &msg, &state.buf)) { retval = netdev->tc->ops->class_dump_stats(netdev_, &msg, cb, aux); if (retval) { error = retval; } } retval = finish_queue_dump(&state); if (retval) { error = retval; } } } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *address, struct in_addr *netmask) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_IN4)) { error = netdev_linux_get_ipv4(netdev_, &netdev->address, SIOCGIFADDR, "SIOCGIFADDR"); if (!error) { error = netdev_linux_get_ipv4(netdev_, &netdev->netmask, SIOCGIFNETMASK, "SIOCGIFNETMASK"); } netdev->in4_error = error; netdev->cache_valid |= VALID_IN4; } else { error = netdev->in4_error; } if (!error) { if (netdev->address.s_addr != INADDR_ANY) { *address = netdev->address; *netmask = netdev->netmask; } else { error = EADDRNOTAVAIL; } } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, struct in_addr netmask) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address); if (!error) { netdev->address = address; netdev->netmask = netmask; if (address.s_addr != INADDR_ANY) { error = do_set_addr(netdev_, SIOCSIFNETMASK, "SIOCSIFNETMASK", netmask); } } if (!error) { netdev->cache_valid |= VALID_IN4; netdev->in4_error = 0; } else { netdev->cache_valid &= ~VALID_IN4; } ovs_mutex_unlock(&netdev->mutex); return error; } static bool parse_if_inet6_line(const char *line, struct in6_addr *in6, char ifname[16 + 1]) { uint8_t *s6 = in6->s6_addr; #define X8 "%2"SCNx8 return ovs_scan(line, " "X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 "%*x %*x %*x %*x %16s\n", &s6[0], &s6[1], &s6[2], &s6[3], &s6[4], &s6[5], &s6[6], &s6[7], &s6[8], &s6[9], &s6[10], &s6[11], &s6[12], &s6[13], &s6[14], &s6[15], ifname); } /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address. * Otherwise, sets '*in6' to 'in6addr_any' and returns the corresponding * error. */ static int netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_IN6)) { FILE *file; char line[128]; netdev->in6 = in6addr_any; netdev->in6_error = EADDRNOTAVAIL; file = fopen("/proc/net/if_inet6", "r"); if (file != NULL) { const char *name = netdev_get_name(netdev_); while (fgets(line, sizeof line, file)) { struct in6_addr in6_tmp; char ifname[16 + 1]; if (parse_if_inet6_line(line, &in6_tmp, ifname) && !strcmp(name, ifname)) { netdev->in6 = in6_tmp; netdev->in6_error = 0; break; } } fclose(file); } else { netdev->in6_error = EOPNOTSUPP; } netdev->cache_valid |= VALID_IN6; } *in6 = netdev->in6; error = netdev->in6_error; ovs_mutex_unlock(&netdev->mutex); return error; } static void make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr) { struct sockaddr_in sin; memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr = addr; sin.sin_port = 0; memset(sa, 0, sizeof *sa); memcpy(sa, &sin, sizeof sin); } static int do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; make_in4_sockaddr(&ifr.ifr_addr, addr); return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, ioctl_name); } /* Adds 'router' as a default IP gateway. */ static int netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router) { struct in_addr any = { INADDR_ANY }; struct rtentry rt; int error; memset(&rt, 0, sizeof rt); make_in4_sockaddr(&rt.rt_dst, any); make_in4_sockaddr(&rt.rt_gateway, router); make_in4_sockaddr(&rt.rt_genmask, any); rt.rt_flags = RTF_UP | RTF_GATEWAY; error = af_inet_ioctl(SIOCADDRT, &rt); if (error) { VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error)); } return error; } static int netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, char **netdev_name) { static const char fn[] = "/proc/net/route"; FILE *stream; char line[256]; int ln; *netdev_name = NULL; stream = fopen(fn, "r"); if (stream == NULL) { VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno)); return errno; } ln = 0; while (fgets(line, sizeof line, stream)) { if (++ln >= 2) { char iface[17]; ovs_be32 dest, gateway, mask; int refcnt, metric, mtu; unsigned int flags, use, window, irtt; if (!ovs_scan(line, "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32 " %d %u %u\n", iface, &dest, &gateway, &flags, &refcnt, &use, &metric, &mask, &mtu, &window, &irtt)) { VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s", fn, ln, line); continue; } if (!(flags & RTF_UP)) { /* Skip routes that aren't up. */ continue; } /* The output of 'dest', 'mask', and 'gateway' were given in * network byte order, so we don't need need any endian * conversions here. */ if ((dest & mask) == (host->s_addr & mask)) { if (!gateway) { /* The host is directly reachable. */ next_hop->s_addr = 0; } else { /* To reach the host, we must go through a gateway. */ next_hop->s_addr = gateway; } *netdev_name = xstrdup(iface); fclose(stream); return 0; } } } fclose(stream); return ENXIO; } static int netdev_linux_get_status(const struct netdev *netdev_, struct smap *smap) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error = 0; ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_DRVINFO)) { struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev->drvinfo; COVERAGE_INC(netdev_get_ethtool); memset(&netdev->drvinfo, 0, sizeof netdev->drvinfo); error = netdev_linux_do_ethtool(netdev->up.name, cmd, ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"); if (!error) { netdev->cache_valid |= VALID_DRVINFO; } } if (!error) { smap_add(smap, "driver_name", netdev->drvinfo.driver); smap_add(smap, "driver_version", netdev->drvinfo.version); smap_add(smap, "firmware_version", netdev->drvinfo.fw_version); } ovs_mutex_unlock(&netdev->mutex); return error; } static int netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED, struct smap *smap) { smap_add(smap, "driver_name", "openvswitch"); return 0; } /* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be * successfully retrieved, it stores the corresponding MAC address in 'mac' and * returns 0. Otherwise, it returns a positive errno value; in particular, * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */ static int netdev_linux_arp_lookup(const struct netdev *netdev, ovs_be32 ip, struct eth_addr *mac) { struct arpreq r; struct sockaddr_in sin; int retval; memset(&r, 0, sizeof r); memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip; sin.sin_port = 0; memcpy(&r.arp_pa, &sin, sizeof sin); r.arp_ha.sa_family = ARPHRD_ETHER; r.arp_flags = 0; ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev); COVERAGE_INC(netdev_arp_lookup); retval = af_inet_ioctl(SIOCGARP, &r); if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", netdev_get_name(netdev), IP_ARGS(ip), ovs_strerror(retval)); } return retval; } static int nd_to_iff_flags(enum netdev_flags nd) { int iff = 0; if (nd & NETDEV_UP) { iff |= IFF_UP; } if (nd & NETDEV_PROMISC) { iff |= IFF_PROMISC; } if (nd & NETDEV_LOOPBACK) { iff |= IFF_LOOPBACK; } return iff; } static int iff_to_nd_flags(int iff) { enum netdev_flags nd = 0; if (iff & IFF_UP) { nd |= NETDEV_UP; } if (iff & IFF_PROMISC) { nd |= NETDEV_PROMISC; } if (iff & IFF_LOOPBACK) { nd |= NETDEV_LOOPBACK; } return nd; } static int update_flags(struct netdev_linux *netdev, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) OVS_REQUIRES(netdev->mutex) { int old_flags, new_flags; int error = 0; old_flags = netdev->ifi_flags; *old_flagsp = iff_to_nd_flags(old_flags); new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on); if (new_flags != old_flags) { error = set_flags(netdev_get_name(&netdev->up), new_flags); get_flags(&netdev->up, &netdev->ifi_flags); } return error; } static int netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; ovs_mutex_lock(&netdev->mutex); error = update_flags(netdev, off, on, old_flagsp); ovs_mutex_unlock(&netdev->mutex); return error; } #define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, \ GET_FEATURES, GET_STATUS) \ { \ NAME, \ \ NULL, \ netdev_linux_run, \ netdev_linux_wait, \ \ netdev_linux_alloc, \ CONSTRUCT, \ netdev_linux_destruct, \ netdev_linux_dealloc, \ NULL, /* get_config */ \ NULL, /* set_config */ \ NULL, /* get_tunnel_config */ \ NULL, /* build header */ \ NULL, /* push header */ \ NULL, /* pop header */ \ NULL, /* get_numa_id */ \ NULL, /* set_multiq */ \ \ netdev_linux_send, \ netdev_linux_send_wait, \ \ netdev_linux_set_etheraddr, \ netdev_linux_get_etheraddr, \ netdev_linux_get_mtu, \ netdev_linux_set_mtu, \ netdev_linux_get_ifindex, \ netdev_linux_get_carrier, \ netdev_linux_get_carrier_resets, \ netdev_linux_set_miimon_interval, \ GET_STATS, \ \ GET_FEATURES, \ netdev_linux_set_advertisements, \ \ netdev_linux_set_policing, \ netdev_linux_get_qos_types, \ netdev_linux_get_qos_capabilities, \ netdev_linux_get_qos, \ netdev_linux_set_qos, \ netdev_linux_get_queue, \ netdev_linux_set_queue, \ netdev_linux_delete_queue, \ netdev_linux_get_queue_stats, \ netdev_linux_queue_dump_start, \ netdev_linux_queue_dump_next, \ netdev_linux_queue_dump_done, \ netdev_linux_dump_queue_stats, \ \ netdev_linux_get_in4, \ netdev_linux_set_in4, \ netdev_linux_get_in6, \ netdev_linux_add_router, \ netdev_linux_get_next_hop, \ GET_STATUS, \ netdev_linux_arp_lookup, \ \ netdev_linux_update_flags, \ \ netdev_linux_rxq_alloc, \ netdev_linux_rxq_construct, \ netdev_linux_rxq_destruct, \ netdev_linux_rxq_dealloc, \ netdev_linux_rxq_recv, \ netdev_linux_rxq_wait, \ netdev_linux_rxq_drain, \ } const struct netdev_class netdev_linux_class = NETDEV_LINUX_CLASS( "system", netdev_linux_construct, netdev_linux_get_stats, netdev_linux_get_features, netdev_linux_get_status); const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", netdev_linux_construct_tap, netdev_tap_get_stats, netdev_linux_get_features, netdev_linux_get_status); const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", netdev_linux_construct, netdev_internal_get_stats, NULL, /* get_features */ netdev_internal_get_status); #define CODEL_N_QUEUES 0x0000 /* In sufficiently new kernel headers these are defined as enums in * . Define them here as macros to help out with older * kernels. (This overrides any enum definition in the header file but that's * harmless.) */ #define TCA_CODEL_TARGET 1 #define TCA_CODEL_LIMIT 2 #define TCA_CODEL_INTERVAL 3 struct codel { struct tc tc; uint32_t target; uint32_t limit; uint32_t interval; }; static struct codel * codel_get__(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); return CONTAINER_OF(netdev->tc, struct codel, tc); } static void codel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit, uint32_t interval) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct codel *codel; codel = xmalloc(sizeof *codel); tc_init(&codel->tc, &tc_ops_codel); codel->target = target; codel->limit = limit; codel->interval = interval; netdev->tc = &codel->tc; } static int codel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit, uint32_t interval) { size_t opt_offset; struct ofpbuf request; struct tcmsg *tcmsg; uint32_t otarget, olimit, ointerval; int error; tc_del_qdisc(netdev); tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; otarget = target ? target : 5000; olimit = limit ? limit : 10240; ointerval = interval ? interval : 100000; nl_msg_put_string(&request, TCA_KIND, "codel"); opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_u32(&request, TCA_CODEL_TARGET, otarget); nl_msg_put_u32(&request, TCA_CODEL_LIMIT, olimit); nl_msg_put_u32(&request, TCA_CODEL_INTERVAL, ointerval); nl_msg_end_nested(&request, opt_offset); error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " "target %u, limit %u, interval %u error %d(%s)", netdev_get_name(netdev), otarget, olimit, ointerval, error, ovs_strerror(error)); } return error; } static void codel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED, const struct smap *details, struct codel *codel) { const char *target_s; const char *limit_s; const char *interval_s; target_s = smap_get(details, "target"); limit_s = smap_get(details, "limit"); interval_s = smap_get(details, "interval"); codel->target = target_s ? strtoull(target_s, NULL, 10) : 0; codel->limit = limit_s ? strtoull(limit_s, NULL, 10) : 0; codel->interval = interval_s ? strtoull(interval_s, NULL, 10) : 0; if (!codel->target) { codel->target = 5000; } if (!codel->limit) { codel->limit = 10240; } if (!codel->interval) { codel->interval = 100000; } } static int codel_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct codel codel; codel_parse_qdisc_details__(netdev, details, &codel); error = codel_setup_qdisc__(netdev, codel.target, codel.limit, codel.interval); if (!error) { codel_install__(netdev, codel.target, codel.limit, codel.interval); } return error; } static int codel_parse_tca_options__(struct nlattr *nl_options, struct codel *codel) { static const struct nl_policy tca_codel_policy[] = { [TCA_CODEL_TARGET] = { .type = NL_A_U32 }, [TCA_CODEL_LIMIT] = { .type = NL_A_U32 }, [TCA_CODEL_INTERVAL] = { .type = NL_A_U32 } }; struct nlattr *attrs[ARRAY_SIZE(tca_codel_policy)]; if (!nl_parse_nested(nl_options, tca_codel_policy, attrs, ARRAY_SIZE(tca_codel_policy))) { VLOG_WARN_RL(&rl, "failed to parse CoDel class options"); return EPROTO; } codel->target = nl_attr_get_u32(attrs[TCA_CODEL_TARGET]); codel->limit = nl_attr_get_u32(attrs[TCA_CODEL_LIMIT]); codel->interval = nl_attr_get_u32(attrs[TCA_CODEL_INTERVAL]); return 0; } static int codel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) { struct nlattr *nlattr; const char * kind; int error; struct codel codel; error = tc_parse_qdisc(nlmsg, &kind, &nlattr); if (error != 0) { return error; } error = codel_parse_tca_options__(nlattr, &codel); if (error != 0) { return error; } codel_install__(netdev, codel.target, codel.limit, codel.interval); return 0; } static void codel_tc_destroy(struct tc *tc) { struct codel *codel = CONTAINER_OF(tc, struct codel, tc); tc_destroy(tc); free(codel); } static int codel_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct codel *codel = codel_get__(netdev); smap_add_format(details, "target", "%u", codel->target); smap_add_format(details, "limit", "%u", codel->limit); smap_add_format(details, "interval", "%u", codel->interval); return 0; } static int codel_qdisc_set(struct netdev *netdev, const struct smap *details) { struct codel codel; codel_parse_qdisc_details__(netdev, details, &codel); codel_install__(netdev, codel.target, codel.limit, codel.interval); codel_get__(netdev)->target = codel.target; codel_get__(netdev)->limit = codel.limit; codel_get__(netdev)->interval = codel.interval; return 0; } static const struct tc_ops tc_ops_codel = { "codel", /* linux_name */ "linux-codel", /* ovs_name */ CODEL_N_QUEUES, /* n_queues */ codel_tc_install, codel_tc_load, codel_tc_destroy, codel_qdisc_get, codel_qdisc_set, NULL, NULL, NULL, NULL, NULL }; /* FQ-CoDel traffic control class. */ #define FQCODEL_N_QUEUES 0x0000 /* In sufficiently new kernel headers these are defined as enums in * . Define them here as macros to help out with older * kernels. (This overrides any enum definition in the header file but that's * harmless.) */ #define TCA_FQ_CODEL_TARGET 1 #define TCA_FQ_CODEL_LIMIT 2 #define TCA_FQ_CODEL_INTERVAL 3 #define TCA_FQ_CODEL_ECN 4 #define TCA_FQ_CODEL_FLOWS 5 #define TCA_FQ_CODEL_QUANTUM 6 struct fqcodel { struct tc tc; uint32_t target; uint32_t limit; uint32_t interval; uint32_t flows; uint32_t quantum; }; static struct fqcodel * fqcodel_get__(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); return CONTAINER_OF(netdev->tc, struct fqcodel, tc); } static void fqcodel_install__(struct netdev *netdev_, uint32_t target, uint32_t limit, uint32_t interval, uint32_t flows, uint32_t quantum) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct fqcodel *fqcodel; fqcodel = xmalloc(sizeof *fqcodel); tc_init(&fqcodel->tc, &tc_ops_fqcodel); fqcodel->target = target; fqcodel->limit = limit; fqcodel->interval = interval; fqcodel->flows = flows; fqcodel->quantum = quantum; netdev->tc = &fqcodel->tc; } static int fqcodel_setup_qdisc__(struct netdev *netdev, uint32_t target, uint32_t limit, uint32_t interval, uint32_t flows, uint32_t quantum) { size_t opt_offset; struct ofpbuf request; struct tcmsg *tcmsg; uint32_t otarget, olimit, ointerval, oflows, oquantum; int error; tc_del_qdisc(netdev); tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; otarget = target ? target : 5000; olimit = limit ? limit : 10240; ointerval = interval ? interval : 100000; oflows = flows ? flows : 1024; oquantum = quantum ? quantum : 1514; /* fq_codel default quantum is 1514 not mtu */ nl_msg_put_string(&request, TCA_KIND, "fq_codel"); opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_u32(&request, TCA_FQ_CODEL_TARGET, otarget); nl_msg_put_u32(&request, TCA_FQ_CODEL_LIMIT, olimit); nl_msg_put_u32(&request, TCA_FQ_CODEL_INTERVAL, ointerval); nl_msg_put_u32(&request, TCA_FQ_CODEL_FLOWS, oflows); nl_msg_put_u32(&request, TCA_FQ_CODEL_QUANTUM, oquantum); nl_msg_end_nested(&request, opt_offset); error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " "target %u, limit %u, interval %u, flows %u, quantum %u error %d(%s)", netdev_get_name(netdev), otarget, olimit, ointerval, oflows, oquantum, error, ovs_strerror(error)); } return error; } static void fqcodel_parse_qdisc_details__(struct netdev *netdev OVS_UNUSED, const struct smap *details, struct fqcodel *fqcodel) { const char *target_s; const char *limit_s; const char *interval_s; const char *flows_s; const char *quantum_s; target_s = smap_get(details, "target"); limit_s = smap_get(details, "limit"); interval_s = smap_get(details, "interval"); flows_s = smap_get(details, "flows"); quantum_s = smap_get(details, "quantum"); fqcodel->target = target_s ? strtoull(target_s, NULL, 10) : 0; fqcodel->limit = limit_s ? strtoull(limit_s, NULL, 10) : 0; fqcodel->interval = interval_s ? strtoull(interval_s, NULL, 10) : 0; fqcodel->flows = flows_s ? strtoull(flows_s, NULL, 10) : 0; fqcodel->quantum = quantum_s ? strtoull(quantum_s, NULL, 10) : 0; if (!fqcodel->target) { fqcodel->target = 5000; } if (!fqcodel->limit) { fqcodel->limit = 10240; } if (!fqcodel->interval) { fqcodel->interval = 1000000; } if (!fqcodel->flows) { fqcodel->flows = 1024; } if (!fqcodel->quantum) { fqcodel->quantum = 1514; } } static int fqcodel_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct fqcodel fqcodel; fqcodel_parse_qdisc_details__(netdev, details, &fqcodel); error = fqcodel_setup_qdisc__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, fqcodel.flows, fqcodel.quantum); if (!error) { fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, fqcodel.flows, fqcodel.quantum); } return error; } static int fqcodel_parse_tca_options__(struct nlattr *nl_options, struct fqcodel *fqcodel) { static const struct nl_policy tca_fqcodel_policy[] = { [TCA_FQ_CODEL_TARGET] = { .type = NL_A_U32 }, [TCA_FQ_CODEL_LIMIT] = { .type = NL_A_U32 }, [TCA_FQ_CODEL_INTERVAL] = { .type = NL_A_U32 }, [TCA_FQ_CODEL_FLOWS] = { .type = NL_A_U32 }, [TCA_FQ_CODEL_QUANTUM] = { .type = NL_A_U32 } }; struct nlattr *attrs[ARRAY_SIZE(tca_fqcodel_policy)]; if (!nl_parse_nested(nl_options, tca_fqcodel_policy, attrs, ARRAY_SIZE(tca_fqcodel_policy))) { VLOG_WARN_RL(&rl, "failed to parse FQ_CoDel class options"); return EPROTO; } fqcodel->target = nl_attr_get_u32(attrs[TCA_FQ_CODEL_TARGET]); fqcodel->limit = nl_attr_get_u32(attrs[TCA_FQ_CODEL_LIMIT]); fqcodel->interval =nl_attr_get_u32(attrs[TCA_FQ_CODEL_INTERVAL]); fqcodel->flows = nl_attr_get_u32(attrs[TCA_FQ_CODEL_FLOWS]); fqcodel->quantum = nl_attr_get_u32(attrs[TCA_FQ_CODEL_QUANTUM]); return 0; } static int fqcodel_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) { struct nlattr *nlattr; const char * kind; int error; struct fqcodel fqcodel; error = tc_parse_qdisc(nlmsg, &kind, &nlattr); if (error != 0) { return error; } error = fqcodel_parse_tca_options__(nlattr, &fqcodel); if (error != 0) { return error; } fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, fqcodel.flows, fqcodel.quantum); return 0; } static void fqcodel_tc_destroy(struct tc *tc) { struct fqcodel *fqcodel = CONTAINER_OF(tc, struct fqcodel, tc); tc_destroy(tc); free(fqcodel); } static int fqcodel_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct fqcodel *fqcodel = fqcodel_get__(netdev); smap_add_format(details, "target", "%u", fqcodel->target); smap_add_format(details, "limit", "%u", fqcodel->limit); smap_add_format(details, "interval", "%u", fqcodel->interval); smap_add_format(details, "flows", "%u", fqcodel->flows); smap_add_format(details, "quantum", "%u", fqcodel->quantum); return 0; } static int fqcodel_qdisc_set(struct netdev *netdev, const struct smap *details) { struct fqcodel fqcodel; fqcodel_parse_qdisc_details__(netdev, details, &fqcodel); fqcodel_install__(netdev, fqcodel.target, fqcodel.limit, fqcodel.interval, fqcodel.flows, fqcodel.quantum); fqcodel_get__(netdev)->target = fqcodel.target; fqcodel_get__(netdev)->limit = fqcodel.limit; fqcodel_get__(netdev)->interval = fqcodel.interval; fqcodel_get__(netdev)->flows = fqcodel.flows; fqcodel_get__(netdev)->quantum = fqcodel.quantum; return 0; } static const struct tc_ops tc_ops_fqcodel = { "fq_codel", /* linux_name */ "linux-fq_codel", /* ovs_name */ FQCODEL_N_QUEUES, /* n_queues */ fqcodel_tc_install, fqcodel_tc_load, fqcodel_tc_destroy, fqcodel_qdisc_get, fqcodel_qdisc_set, NULL, NULL, NULL, NULL, NULL }; /* SFQ traffic control class. */ #define SFQ_N_QUEUES 0x0000 struct sfq { struct tc tc; uint32_t quantum; uint32_t perturb; }; static struct sfq * sfq_get__(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); return CONTAINER_OF(netdev->tc, struct sfq, tc); } static void sfq_install__(struct netdev *netdev_, uint32_t quantum, uint32_t perturb) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct sfq *sfq; sfq = xmalloc(sizeof *sfq); tc_init(&sfq->tc, &tc_ops_sfq); sfq->perturb = perturb; sfq->quantum = quantum; netdev->tc = &sfq->tc; } static int sfq_setup_qdisc__(struct netdev *netdev, uint32_t quantum, uint32_t perturb) { struct tc_sfq_qopt opt; struct ofpbuf request; struct tcmsg *tcmsg; int mtu; int mtu_error, error; mtu_error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); tc_del_qdisc(netdev); tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; memset(&opt, 0, sizeof opt); if (!quantum) { if (!mtu_error) { opt.quantum = mtu; /* if we cannot find mtu, use default */ } } else { opt.quantum = quantum; } if (!perturb) { opt.perturb_period = 10; } else { opt.perturb_period = perturb; } nl_msg_put_string(&request, TCA_KIND, "sfq"); nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt); error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to replace %s qdisc, " "quantum %u, perturb %u error %d(%s)", netdev_get_name(netdev), opt.quantum, opt.perturb_period, error, ovs_strerror(error)); } return error; } static void sfq_parse_qdisc_details__(struct netdev *netdev, const struct smap *details, struct sfq *sfq) { const char *perturb_s; const char *quantum_s; int mtu; int mtu_error; perturb_s = smap_get(details, "perturb"); quantum_s = smap_get(details, "quantum"); sfq->perturb = perturb_s ? strtoull(perturb_s, NULL, 10) : 0; sfq->quantum = quantum_s ? strtoull(quantum_s, NULL, 10) : 0; if (!sfq->perturb) { sfq->perturb = 10; } if (!sfq->quantum) { mtu_error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); if (!mtu_error) { sfq->quantum = mtu; } else { VLOG_WARN_RL(&rl, "when using SFQ, you must specify quantum on a " "device without mtu"); return; } } } static int sfq_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct sfq sfq; sfq_parse_qdisc_details__(netdev, details, &sfq); error = sfq_setup_qdisc__(netdev, sfq.quantum, sfq.perturb); if (!error) { sfq_install__(netdev, sfq.quantum, sfq.perturb); } return error; } static int sfq_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg) { const struct tc_sfq_qopt *sfq; struct nlattr *nlattr; const char * kind; int error; error = tc_parse_qdisc(nlmsg, &kind, &nlattr); if (error == 0) { sfq = nl_attr_get(nlattr); sfq_install__(netdev, sfq->quantum, sfq->perturb_period); return 0; } return error; } static void sfq_tc_destroy(struct tc *tc) { struct sfq *sfq = CONTAINER_OF(tc, struct sfq, tc); tc_destroy(tc); free(sfq); } static int sfq_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct sfq *sfq = sfq_get__(netdev); smap_add_format(details, "quantum", "%u", sfq->quantum); smap_add_format(details, "perturb", "%u", sfq->perturb); return 0; } static int sfq_qdisc_set(struct netdev *netdev, const struct smap *details) { struct sfq sfq; sfq_parse_qdisc_details__(netdev, details, &sfq); sfq_install__(netdev, sfq.quantum, sfq.perturb); sfq_get__(netdev)->quantum = sfq.quantum; sfq_get__(netdev)->perturb = sfq.perturb; return 0; } static const struct tc_ops tc_ops_sfq = { "sfq", /* linux_name */ "linux-sfq", /* ovs_name */ SFQ_N_QUEUES, /* n_queues */ sfq_tc_install, sfq_tc_load, sfq_tc_destroy, sfq_qdisc_get, sfq_qdisc_set, NULL, NULL, NULL, NULL, NULL }; /* HTB traffic control class. */ #define HTB_N_QUEUES 0xf000 #define HTB_RATE2QUANTUM 10 struct htb { struct tc tc; unsigned int max_rate; /* In bytes/s. */ }; struct htb_class { struct tc_queue tc_queue; unsigned int min_rate; /* In bytes/s. */ unsigned int max_rate; /* In bytes/s. */ unsigned int burst; /* In bytes. */ unsigned int priority; /* Lower values are higher priorities. */ }; static struct htb * htb_get__(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); return CONTAINER_OF(netdev->tc, struct htb, tc); } static void htb_install__(struct netdev *netdev_, uint64_t max_rate) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct htb *htb; htb = xmalloc(sizeof *htb); tc_init(&htb->tc, &tc_ops_htb); htb->max_rate = max_rate; netdev->tc = &htb->tc; } /* Create an HTB qdisc. * * Equivalent to "tc qdisc add dev root handle 1: htb default 1". */ static int htb_setup_qdisc__(struct netdev *netdev) { size_t opt_offset; struct tc_htb_glob opt; struct ofpbuf request; struct tcmsg *tcmsg; tc_del_qdisc(netdev); tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; nl_msg_put_string(&request, TCA_KIND, "htb"); memset(&opt, 0, sizeof opt); opt.rate2quantum = HTB_RATE2QUANTUM; opt.version = 3; opt.defcls = 1; opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_unspec(&request, TCA_HTB_INIT, &opt, sizeof opt); nl_msg_end_nested(&request, opt_offset); return tc_transact(&request, NULL); } /* Equivalent to "tc class replace classid parent htb * rate bps ceil bps burst b prio ". */ static int htb_setup_class__(struct netdev *netdev, unsigned int handle, unsigned int parent, struct htb_class *class) { size_t opt_offset; struct tc_htb_opt opt; struct ofpbuf request; struct tcmsg *tcmsg; int error; int mtu; error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); if (error) { VLOG_WARN_RL(&rl, "cannot set up HTB on device %s that lacks MTU", netdev_get_name(netdev)); return error; } memset(&opt, 0, sizeof opt); tc_fill_rate(&opt.rate, class->min_rate, mtu); tc_fill_rate(&opt.ceil, class->max_rate, mtu); /* Makes sure the quantum is at least MTU. Setting quantum will * make htb ignore the r2q for this class. */ if ((class->min_rate / HTB_RATE2QUANTUM) < mtu) { opt.quantum = mtu; } opt.buffer = tc_calc_buffer(opt.rate.rate, mtu, class->burst); opt.cbuffer = tc_calc_buffer(opt.ceil.rate, mtu, class->burst); opt.prio = class->priority; tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = parent; nl_msg_put_string(&request, TCA_KIND, "htb"); opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_unspec(&request, TCA_HTB_PARMS, &opt, sizeof opt); tc_put_rtab(&request, TCA_HTB_RTAB, &opt.rate); tc_put_rtab(&request, TCA_HTB_CTAB, &opt.ceil); nl_msg_end_nested(&request, opt_offset); error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to replace %s class %u:%u, parent %u:%u, " "min_rate=%u max_rate=%u burst=%u prio=%u (%s)", netdev_get_name(netdev), tc_get_major(handle), tc_get_minor(handle), tc_get_major(parent), tc_get_minor(parent), class->min_rate, class->max_rate, class->burst, class->priority, ovs_strerror(error)); } return error; } /* Parses Netlink attributes in 'options' for HTB parameters and stores a * description of them into 'details'. The description complies with the * specification given in the vswitch database documentation for linux-htb * queue details. */ static int htb_parse_tca_options__(struct nlattr *nl_options, struct htb_class *class) { static const struct nl_policy tca_htb_policy[] = { [TCA_HTB_PARMS] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof(struct tc_htb_opt) }, }; struct nlattr *attrs[ARRAY_SIZE(tca_htb_policy)]; const struct tc_htb_opt *htb; if (!nl_parse_nested(nl_options, tca_htb_policy, attrs, ARRAY_SIZE(tca_htb_policy))) { VLOG_WARN_RL(&rl, "failed to parse HTB class options"); return EPROTO; } htb = nl_attr_get(attrs[TCA_HTB_PARMS]); class->min_rate = htb->rate.rate; class->max_rate = htb->ceil.rate; class->burst = tc_ticks_to_bytes(htb->rate.rate, htb->buffer); class->priority = htb->prio; return 0; } static int htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id, struct htb_class *options, struct netdev_queue_stats *stats) { struct nlattr *nl_options; unsigned int handle; int error; error = tc_parse_class(tcmsg, &handle, &nl_options, stats); if (!error && queue_id) { unsigned int major = tc_get_major(handle); unsigned int minor = tc_get_minor(handle); if (major == 1 && minor > 0 && minor <= HTB_N_QUEUES) { *queue_id = minor - 1; } else { error = EPROTO; } } if (!error && options) { error = htb_parse_tca_options__(nl_options, options); } return error; } static void htb_parse_qdisc_details__(struct netdev *netdev_, const struct smap *details, struct htb_class *hc) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); const char *max_rate_s; max_rate_s = smap_get(details, "max-rate"); hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!hc->max_rate) { enum netdev_features current; netdev_linux_read_features(netdev); current = !netdev->get_features_error ? netdev->current : 0; hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } hc->min_rate = hc->max_rate; hc->burst = 0; hc->priority = 0; } static int htb_parse_class_details__(struct netdev *netdev, const struct smap *details, struct htb_class *hc) { const struct htb *htb = htb_get__(netdev); const char *min_rate_s = smap_get(details, "min-rate"); const char *max_rate_s = smap_get(details, "max-rate"); const char *burst_s = smap_get(details, "burst"); const char *priority_s = smap_get(details, "priority"); int mtu, error; error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); if (error) { VLOG_WARN_RL(&rl, "cannot parse HTB class on device %s that lacks MTU", netdev_get_name(netdev)); return error; } /* HTB requires at least an mtu sized min-rate to send any traffic even * on uncongested links. */ hc->min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0; hc->min_rate = MAX(hc->min_rate, mtu); hc->min_rate = MIN(hc->min_rate, htb->max_rate); /* max-rate */ hc->max_rate = (max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : htb->max_rate); hc->max_rate = MAX(hc->max_rate, hc->min_rate); hc->max_rate = MIN(hc->max_rate, htb->max_rate); /* burst * * According to hints in the documentation that I've read, it is important * that 'burst' be at least as big as the largest frame that might be * transmitted. Also, making 'burst' a bit bigger than necessary is OK, * but having it a bit too small is a problem. Since netdev_get_mtu() * doesn't include the Ethernet header, we need to add at least 14 (18?) to * the MTU. We actually add 64, instead of 14, as a guard against * additional headers get tacked on somewhere that we're not aware of. */ hc->burst = burst_s ? strtoull(burst_s, NULL, 10) / 8 : 0; hc->burst = MAX(hc->burst, mtu + 64); /* priority */ hc->priority = priority_s ? strtoul(priority_s, NULL, 10) : 0; return 0; } static int htb_query_class__(const struct netdev *netdev, unsigned int handle, unsigned int parent, struct htb_class *options, struct netdev_queue_stats *stats) { struct ofpbuf *reply; int error; error = tc_query_class(netdev, handle, parent, &reply); if (!error) { error = htb_parse_tcmsg__(reply, NULL, options, stats); ofpbuf_delete(reply); } return error; } static int htb_tc_install(struct netdev *netdev, const struct smap *details) { int error; error = htb_setup_qdisc__(netdev); if (!error) { struct htb_class hc; htb_parse_qdisc_details__(netdev, details, &hc); error = htb_setup_class__(netdev, tc_make_handle(1, 0xfffe), tc_make_handle(1, 0), &hc); if (!error) { htb_install__(netdev, hc.max_rate); } } return error; } static struct htb_class * htb_class_cast__(const struct tc_queue *queue) { return CONTAINER_OF(queue, struct htb_class, tc_queue); } static void htb_update_queue__(struct netdev *netdev, unsigned int queue_id, const struct htb_class *hc) { struct htb *htb = htb_get__(netdev); size_t hash = hash_int(queue_id, 0); struct tc_queue *queue; struct htb_class *hcp; queue = tc_find_queue__(netdev, queue_id, hash); if (queue) { hcp = htb_class_cast__(queue); } else { hcp = xmalloc(sizeof *hcp); queue = &hcp->tc_queue; queue->queue_id = queue_id; queue->created = time_msec(); hmap_insert(&htb->tc.queues, &queue->hmap_node, hash); } hcp->min_rate = hc->min_rate; hcp->max_rate = hc->max_rate; hcp->burst = hc->burst; hcp->priority = hc->priority; } static int htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) { struct ofpbuf msg; struct queue_dump_state state; struct htb_class hc; /* Get qdisc options. */ hc.max_rate = 0; htb_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL); htb_install__(netdev, hc.max_rate); /* Get queues. */ if (!start_queue_dump(netdev, &state)) { return ENODEV; } while (nl_dump_next(&state.dump, &msg, &state.buf)) { unsigned int queue_id; if (!htb_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) { htb_update_queue__(netdev, queue_id, &hc); } } finish_queue_dump(&state); return 0; } static void htb_tc_destroy(struct tc *tc) { struct htb *htb = CONTAINER_OF(tc, struct htb, tc); struct htb_class *hc, *next; HMAP_FOR_EACH_SAFE (hc, next, tc_queue.hmap_node, &htb->tc.queues) { hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); free(hc); } tc_destroy(tc); free(htb); } static int htb_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct htb *htb = htb_get__(netdev); smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate); return 0; } static int htb_qdisc_set(struct netdev *netdev, const struct smap *details) { struct htb_class hc; int error; htb_parse_qdisc_details__(netdev, details, &hc); error = htb_setup_class__(netdev, tc_make_handle(1, 0xfffe), tc_make_handle(1, 0), &hc); if (!error) { htb_get__(netdev)->max_rate = hc.max_rate; } return error; } static int htb_class_get(const struct netdev *netdev OVS_UNUSED, const struct tc_queue *queue, struct smap *details) { const struct htb_class *hc = htb_class_cast__(queue); smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } smap_add_format(details, "burst", "%llu", 8ULL * hc->burst); if (hc->priority) { smap_add_format(details, "priority", "%u", hc->priority); } return 0; } static int htb_class_set(struct netdev *netdev, unsigned int queue_id, const struct smap *details) { struct htb_class hc; int error; error = htb_parse_class_details__(netdev, details, &hc); if (error) { return error; } error = htb_setup_class__(netdev, tc_make_handle(1, queue_id + 1), tc_make_handle(1, 0xfffe), &hc); if (error) { return error; } htb_update_queue__(netdev, queue_id, &hc); return 0; } static int htb_class_delete(struct netdev *netdev, struct tc_queue *queue) { struct htb_class *hc = htb_class_cast__(queue); struct htb *htb = htb_get__(netdev); int error; error = tc_delete_class(netdev, tc_make_handle(1, queue->queue_id + 1)); if (!error) { hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); free(hc); } return error; } static int htb_class_get_stats(const struct netdev *netdev, const struct tc_queue *queue, struct netdev_queue_stats *stats) { return htb_query_class__(netdev, tc_make_handle(1, queue->queue_id + 1), tc_make_handle(1, 0xfffe), NULL, stats); } static int htb_class_dump_stats(const struct netdev *netdev OVS_UNUSED, const struct ofpbuf *nlmsg, netdev_dump_queue_stats_cb *cb, void *aux) { struct netdev_queue_stats stats; unsigned int handle, major, minor; int error; error = tc_parse_class(nlmsg, &handle, NULL, &stats); if (error) { return error; } major = tc_get_major(handle); minor = tc_get_minor(handle); if (major == 1 && minor > 0 && minor <= HTB_N_QUEUES) { (*cb)(minor - 1, &stats, aux); } return 0; } static const struct tc_ops tc_ops_htb = { "htb", /* linux_name */ "linux-htb", /* ovs_name */ HTB_N_QUEUES, /* n_queues */ htb_tc_install, htb_tc_load, htb_tc_destroy, htb_qdisc_get, htb_qdisc_set, htb_class_get, htb_class_set, htb_class_delete, htb_class_get_stats, htb_class_dump_stats }; /* "linux-hfsc" traffic control class. */ #define HFSC_N_QUEUES 0xf000 struct hfsc { struct tc tc; uint32_t max_rate; }; struct hfsc_class { struct tc_queue tc_queue; uint32_t min_rate; uint32_t max_rate; }; static struct hfsc * hfsc_get__(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); return CONTAINER_OF(netdev->tc, struct hfsc, tc); } static struct hfsc_class * hfsc_class_cast__(const struct tc_queue *queue) { return CONTAINER_OF(queue, struct hfsc_class, tc_queue); } static void hfsc_install__(struct netdev *netdev_, uint32_t max_rate) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct hfsc *hfsc; hfsc = xmalloc(sizeof *hfsc); tc_init(&hfsc->tc, &tc_ops_hfsc); hfsc->max_rate = max_rate; netdev->tc = &hfsc->tc; } static void hfsc_update_queue__(struct netdev *netdev, unsigned int queue_id, const struct hfsc_class *hc) { size_t hash; struct hfsc *hfsc; struct hfsc_class *hcp; struct tc_queue *queue; hfsc = hfsc_get__(netdev); hash = hash_int(queue_id, 0); queue = tc_find_queue__(netdev, queue_id, hash); if (queue) { hcp = hfsc_class_cast__(queue); } else { hcp = xmalloc(sizeof *hcp); queue = &hcp->tc_queue; queue->queue_id = queue_id; queue->created = time_msec(); hmap_insert(&hfsc->tc.queues, &queue->hmap_node, hash); } hcp->min_rate = hc->min_rate; hcp->max_rate = hc->max_rate; } static int hfsc_parse_tca_options__(struct nlattr *nl_options, struct hfsc_class *class) { const struct tc_service_curve *rsc, *fsc, *usc; static const struct nl_policy tca_hfsc_policy[] = { [TCA_HFSC_RSC] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof(struct tc_service_curve), }, [TCA_HFSC_FSC] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof(struct tc_service_curve), }, [TCA_HFSC_USC] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof(struct tc_service_curve), }, }; struct nlattr *attrs[ARRAY_SIZE(tca_hfsc_policy)]; if (!nl_parse_nested(nl_options, tca_hfsc_policy, attrs, ARRAY_SIZE(tca_hfsc_policy))) { VLOG_WARN_RL(&rl, "failed to parse HFSC class options"); return EPROTO; } rsc = nl_attr_get(attrs[TCA_HFSC_RSC]); fsc = nl_attr_get(attrs[TCA_HFSC_FSC]); usc = nl_attr_get(attrs[TCA_HFSC_USC]); if (rsc->m1 != 0 || rsc->d != 0 || fsc->m1 != 0 || fsc->d != 0 || usc->m1 != 0 || usc->d != 0) { VLOG_WARN_RL(&rl, "failed to parse HFSC class options. " "Non-linear service curves are not supported."); return EPROTO; } if (rsc->m2 != fsc->m2) { VLOG_WARN_RL(&rl, "failed to parse HFSC class options. " "Real-time service curves are not supported "); return EPROTO; } if (rsc->m2 > usc->m2) { VLOG_WARN_RL(&rl, "failed to parse HFSC class options. " "Min-rate service curve is greater than " "the max-rate service curve."); return EPROTO; } class->min_rate = fsc->m2; class->max_rate = usc->m2; return 0; } static int hfsc_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id, struct hfsc_class *options, struct netdev_queue_stats *stats) { int error; unsigned int handle; struct nlattr *nl_options; error = tc_parse_class(tcmsg, &handle, &nl_options, stats); if (error) { return error; } if (queue_id) { unsigned int major, minor; major = tc_get_major(handle); minor = tc_get_minor(handle); if (major == 1 && minor > 0 && minor <= HFSC_N_QUEUES) { *queue_id = minor - 1; } else { return EPROTO; } } if (options) { error = hfsc_parse_tca_options__(nl_options, options); } return error; } static int hfsc_query_class__(const struct netdev *netdev, unsigned int handle, unsigned int parent, struct hfsc_class *options, struct netdev_queue_stats *stats) { int error; struct ofpbuf *reply; error = tc_query_class(netdev, handle, parent, &reply); if (error) { return error; } error = hfsc_parse_tcmsg__(reply, NULL, options, stats); ofpbuf_delete(reply); return error; } static void hfsc_parse_qdisc_details__(struct netdev *netdev_, const struct smap *details, struct hfsc_class *class) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); uint32_t max_rate; const char *max_rate_s; max_rate_s = smap_get(details, "max-rate"); max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!max_rate) { enum netdev_features current; netdev_linux_read_features(netdev); current = !netdev->get_features_error ? netdev->current : 0; max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } class->min_rate = max_rate; class->max_rate = max_rate; } static int hfsc_parse_class_details__(struct netdev *netdev, const struct smap *details, struct hfsc_class * class) { const struct hfsc *hfsc; uint32_t min_rate, max_rate; const char *min_rate_s, *max_rate_s; hfsc = hfsc_get__(netdev); min_rate_s = smap_get(details, "min-rate"); max_rate_s = smap_get(details, "max-rate"); min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0; min_rate = MAX(min_rate, 1); min_rate = MIN(min_rate, hfsc->max_rate); max_rate = (max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : hfsc->max_rate); max_rate = MAX(max_rate, min_rate); max_rate = MIN(max_rate, hfsc->max_rate); class->min_rate = min_rate; class->max_rate = max_rate; return 0; } /* Create an HFSC qdisc. * * Equivalent to "tc qdisc add dev root handle 1: hfsc default 1". */ static int hfsc_setup_qdisc__(struct netdev * netdev) { struct tcmsg *tcmsg; struct ofpbuf request; struct tc_hfsc_qopt opt; tc_del_qdisc(netdev); tcmsg = tc_make_request(netdev, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; memset(&opt, 0, sizeof opt); opt.defcls = 1; nl_msg_put_string(&request, TCA_KIND, "hfsc"); nl_msg_put_unspec(&request, TCA_OPTIONS, &opt, sizeof opt); return tc_transact(&request, NULL); } /* Create an HFSC class. * * Equivalent to "tc class add parent classid hfsc * sc rate ul rate " */ static int hfsc_setup_class__(struct netdev *netdev, unsigned int handle, unsigned int parent, struct hfsc_class *class) { int error; size_t opt_offset; struct tcmsg *tcmsg; struct ofpbuf request; struct tc_service_curve min, max; tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = parent; min.m1 = 0; min.d = 0; min.m2 = class->min_rate; max.m1 = 0; max.d = 0; max.m2 = class->max_rate; nl_msg_put_string(&request, TCA_KIND, "hfsc"); opt_offset = nl_msg_start_nested(&request, TCA_OPTIONS); nl_msg_put_unspec(&request, TCA_HFSC_RSC, &min, sizeof min); nl_msg_put_unspec(&request, TCA_HFSC_FSC, &min, sizeof min); nl_msg_put_unspec(&request, TCA_HFSC_USC, &max, sizeof max); nl_msg_end_nested(&request, opt_offset); error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "failed to replace %s class %u:%u, parent %u:%u, " "min-rate %ubps, max-rate %ubps (%s)", netdev_get_name(netdev), tc_get_major(handle), tc_get_minor(handle), tc_get_major(parent), tc_get_minor(parent), class->min_rate, class->max_rate, ovs_strerror(error)); } return error; } static int hfsc_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; error = hfsc_setup_qdisc__(netdev); if (error) { return error; } hfsc_parse_qdisc_details__(netdev, details, &class); error = hfsc_setup_class__(netdev, tc_make_handle(1, 0xfffe), tc_make_handle(1, 0), &class); if (error) { return error; } hfsc_install__(netdev, class.max_rate); return 0; } static int hfsc_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) { struct ofpbuf msg; struct queue_dump_state state; struct hfsc_class hc; hc.max_rate = 0; hfsc_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL); hfsc_install__(netdev, hc.max_rate); if (!start_queue_dump(netdev, &state)) { return ENODEV; } while (nl_dump_next(&state.dump, &msg, &state.buf)) { unsigned int queue_id; if (!hfsc_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) { hfsc_update_queue__(netdev, queue_id, &hc); } } finish_queue_dump(&state); return 0; } static void hfsc_tc_destroy(struct tc *tc) { struct hfsc *hfsc; struct hfsc_class *hc, *next; hfsc = CONTAINER_OF(tc, struct hfsc, tc); HMAP_FOR_EACH_SAFE (hc, next, tc_queue.hmap_node, &hfsc->tc.queues) { hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node); free(hc); } tc_destroy(tc); free(hfsc); } static int hfsc_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct hfsc *hfsc; hfsc = hfsc_get__(netdev); smap_add_format(details, "max-rate", "%llu", 8ULL * hfsc->max_rate); return 0; } static int hfsc_qdisc_set(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; hfsc_parse_qdisc_details__(netdev, details, &class); error = hfsc_setup_class__(netdev, tc_make_handle(1, 0xfffe), tc_make_handle(1, 0), &class); if (!error) { hfsc_get__(netdev)->max_rate = class.max_rate; } return error; } static int hfsc_class_get(const struct netdev *netdev OVS_UNUSED, const struct tc_queue *queue, struct smap *details) { const struct hfsc_class *hc; hc = hfsc_class_cast__(queue); smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } return 0; } static int hfsc_class_set(struct netdev *netdev, unsigned int queue_id, const struct smap *details) { int error; struct hfsc_class class; error = hfsc_parse_class_details__(netdev, details, &class); if (error) { return error; } error = hfsc_setup_class__(netdev, tc_make_handle(1, queue_id + 1), tc_make_handle(1, 0xfffe), &class); if (error) { return error; } hfsc_update_queue__(netdev, queue_id, &class); return 0; } static int hfsc_class_delete(struct netdev *netdev, struct tc_queue *queue) { int error; struct hfsc *hfsc; struct hfsc_class *hc; hc = hfsc_class_cast__(queue); hfsc = hfsc_get__(netdev); error = tc_delete_class(netdev, tc_make_handle(1, queue->queue_id + 1)); if (!error) { hmap_remove(&hfsc->tc.queues, &hc->tc_queue.hmap_node); free(hc); } return error; } static int hfsc_class_get_stats(const struct netdev *netdev, const struct tc_queue *queue, struct netdev_queue_stats *stats) { return hfsc_query_class__(netdev, tc_make_handle(1, queue->queue_id + 1), tc_make_handle(1, 0xfffe), NULL, stats); } static int hfsc_class_dump_stats(const struct netdev *netdev OVS_UNUSED, const struct ofpbuf *nlmsg, netdev_dump_queue_stats_cb *cb, void *aux) { struct netdev_queue_stats stats; unsigned int handle, major, minor; int error; error = tc_parse_class(nlmsg, &handle, NULL, &stats); if (error) { return error; } major = tc_get_major(handle); minor = tc_get_minor(handle); if (major == 1 && minor > 0 && minor <= HFSC_N_QUEUES) { (*cb)(minor - 1, &stats, aux); } return 0; } static const struct tc_ops tc_ops_hfsc = { "hfsc", /* linux_name */ "linux-hfsc", /* ovs_name */ HFSC_N_QUEUES, /* n_queues */ hfsc_tc_install, /* tc_install */ hfsc_tc_load, /* tc_load */ hfsc_tc_destroy, /* tc_destroy */ hfsc_qdisc_get, /* qdisc_get */ hfsc_qdisc_set, /* qdisc_set */ hfsc_class_get, /* class_get */ hfsc_class_set, /* class_set */ hfsc_class_delete, /* class_delete */ hfsc_class_get_stats, /* class_get_stats */ hfsc_class_dump_stats /* class_dump_stats */ }; /* "linux-default" traffic control class. * * This class represents the default, unnamed Linux qdisc. It corresponds to * the "" (empty string) QoS type in the OVS database. */ static void default_install__(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default); /* Nothing but a tc class implementation is allowed to write to a tc. This * class never does that, so we can legitimately use a const tc object. */ netdev->tc = CONST_CAST(struct tc *, &tc); } static int default_tc_install(struct netdev *netdev, const struct smap *details OVS_UNUSED) { default_install__(netdev); return 0; } static int default_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) { default_install__(netdev); return 0; } static const struct tc_ops tc_ops_default = { NULL, /* linux_name */ "", /* ovs_name */ 0, /* n_queues */ default_tc_install, default_tc_load, NULL, /* tc_destroy */ NULL, /* qdisc_get */ NULL, /* qdisc_set */ NULL, /* class_get */ NULL, /* class_set */ NULL, /* class_delete */ NULL, /* class_get_stats */ NULL /* class_dump_stats */ }; /* "linux-other" traffic control class. * * */ static int other_tc_load(struct netdev *netdev_, struct ofpbuf *nlmsg OVS_UNUSED) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_other); /* Nothing but a tc class implementation is allowed to write to a tc. This * class never does that, so we can legitimately use a const tc object. */ netdev->tc = CONST_CAST(struct tc *, &tc); return 0; } static const struct tc_ops tc_ops_other = { NULL, /* linux_name */ "linux-other", /* ovs_name */ 0, /* n_queues */ NULL, /* tc_install */ other_tc_load, NULL, /* tc_destroy */ NULL, /* qdisc_get */ NULL, /* qdisc_set */ NULL, /* class_get */ NULL, /* class_set */ NULL, /* class_delete */ NULL, /* class_get_stats */ NULL /* class_dump_stats */ }; /* Traffic control. */ /* Number of kernel "tc" ticks per second. */ static double ticks_per_s; /* Number of kernel "jiffies" per second. This is used for the purpose of * computing buffer sizes. Generally kernel qdiscs need to be able to buffer * one jiffy's worth of data. * * There are two possibilities here: * * - 'buffer_hz' is the kernel's real timer tick rate, a small number in the * approximate range of 100 to 1024. That means that we really need to * make sure that the qdisc can buffer that much data. * * - 'buffer_hz' is an absurdly large number. That means that the kernel * has finely granular timers and there's no need to fudge additional room * for buffers. (There's no extra effort needed to implement that: the * large 'buffer_hz' is used as a divisor, so practically any number will * come out as 0 in the division. Small integer results in the case of * really high dividends won't have any real effect anyhow.) */ static unsigned int buffer_hz; /* Returns tc handle 'major':'minor'. */ static unsigned int tc_make_handle(unsigned int major, unsigned int minor) { return TC_H_MAKE(major << 16, minor); } /* Returns the major number from 'handle'. */ static unsigned int tc_get_major(unsigned int handle) { return TC_H_MAJ(handle) >> 16; } /* Returns the minor number from 'handle'. */ static unsigned int tc_get_minor(unsigned int handle) { return TC_H_MIN(handle); } static struct tcmsg * tc_make_request(const struct netdev *netdev, int type, unsigned int flags, struct ofpbuf *request) { struct tcmsg *tcmsg; int ifindex; int error; error = get_ifindex(netdev, &ifindex); if (error) { return NULL; } ofpbuf_init(request, 512); nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags); tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg); tcmsg->tcm_family = AF_UNSPEC; tcmsg->tcm_ifindex = ifindex; /* Caller should fill in tcmsg->tcm_handle. */ /* Caller should fill in tcmsg->tcm_parent. */ return tcmsg; } static int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp) { int error = nl_transact(NETLINK_ROUTE, request, replyp); ofpbuf_uninit(request); return error; } /* Adds or deletes a root ingress qdisc on 'netdev'. We use this for * policing configuration. * * This function is equivalent to running the following when 'add' is true: * /sbin/tc qdisc add dev handle ffff: ingress * * This function is equivalent to running the following when 'add' is false: * /sbin/tc qdisc del dev handle ffff: ingress * * The configuration and stats may be seen with the following command: * /sbin/tc -s qdisc show dev * * Returns 0 if successful, otherwise a positive errno value. */ static int tc_add_del_ingress_qdisc(struct netdev *netdev, bool add) { struct ofpbuf request; struct tcmsg *tcmsg; int error; int type = add ? RTM_NEWQDISC : RTM_DELQDISC; int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0; tcmsg = tc_make_request(netdev, type, flags, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(0xffff, 0); tcmsg->tcm_parent = TC_H_INGRESS; nl_msg_put_string(&request, TCA_KIND, "ingress"); nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0); error = tc_transact(&request, NULL); if (error) { /* If we're deleting the qdisc, don't worry about some of the * error conditions. */ if (!add && (error == ENOENT || error == EINVAL)) { return 0; } return error; } return 0; } /* Adds a policer to 'netdev' with a rate of 'kbits_rate' and a burst size * of 'kbits_burst'. * * This function is equivalent to running: * /sbin/tc filter add dev parent ffff: protocol all prio 49 * basic police rate kbit burst k * mtu 65535 drop * * The configuration and stats may be seen with the following command: * /sbin/tc -s filter show dev parent ffff: * * Returns 0 if successful, otherwise a positive errno value. */ static int tc_add_policer(struct netdev *netdev, uint32_t kbits_rate, uint32_t kbits_burst) { struct tc_police tc_police; struct ofpbuf request; struct tcmsg *tcmsg; size_t basic_offset; size_t police_offset; int error; int mtu = 65535; memset(&tc_police, 0, sizeof tc_police); tc_police.action = TC_POLICE_SHOT; tc_police.mtu = mtu; tc_fill_rate(&tc_police.rate, ((uint64_t) kbits_rate * 1000)/8, mtu); /* The following appears wrong in one way: In networking a kilobit is * usually 1000 bits but this uses 1024 bits. * * However if you "fix" those problems then "tc filter show ..." shows * "125000b", meaning 125,000 bits, when OVS configures it for 1000 kbit == * 1,000,000 bits, whereas this actually ends up doing the right thing from * tc's point of view. Whatever. */ tc_police.burst = tc_bytes_to_ticks( tc_police.rate.rate, MIN(UINT32_MAX / 1024, kbits_burst) * 1024 / 8); tcmsg = tc_make_request(netdev, RTM_NEWTFILTER, NLM_F_EXCL | NLM_F_CREATE, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_parent = tc_make_handle(0xffff, 0); tcmsg->tcm_info = tc_make_handle(49, (OVS_FORCE uint16_t) htons(ETH_P_ALL)); nl_msg_put_string(&request, TCA_KIND, "basic"); basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS); police_offset = nl_msg_start_nested(&request, TCA_BASIC_POLICE); nl_msg_put_unspec(&request, TCA_POLICE_TBF, &tc_police, sizeof tc_police); tc_put_rtab(&request, TCA_POLICE_RATE, &tc_police.rate); nl_msg_end_nested(&request, police_offset); nl_msg_end_nested(&request, basic_offset); error = tc_transact(&request, NULL); if (error) { return error; } return 0; } static void read_psched(void) { /* The values in psched are not individually very meaningful, but they are * important. The tables below show some values seen in the wild. * * Some notes: * * - "c" has always been a constant 1000000 since at least Linux 2.4.14. * (Before that, there are hints that it was 1000000000.) * * - "d" can be unrealistically large, see the comment on 'buffer_hz' * above. * * /proc/net/psched * ----------------------------------- * [1] 000c8000 000f4240 000f4240 00000064 * [2] 000003e8 00000400 000f4240 3b9aca00 * [3] 000003e8 00000400 000f4240 3b9aca00 * [4] 000003e8 00000400 000f4240 00000064 * [5] 000003e8 00000040 000f4240 3b9aca00 * [6] 000003e8 00000040 000f4240 000000f9 * * a b c d ticks_per_s buffer_hz * ------- --------- ---------- ------------- ----------- ------------- * [1] 819,200 1,000,000 1,000,000 100 819,200 100 * [2] 1,000 1,024 1,000,000 1,000,000,000 976,562 1,000,000,000 * [3] 1,000 1,024 1,000,000 1,000,000,000 976,562 1,000,000,000 * [4] 1,000 1,024 1,000,000 100 976,562 100 * [5] 1,000 64 1,000,000 1,000,000,000 15,625,000 1,000,000,000 * [6] 1,000 64 1,000,000 249 15,625,000 249 * * [1] 2.6.18-128.1.6.el5.xs5.5.0.505.1024xen from XenServer 5.5.0-24648p * [2] 2.6.26-1-686-bigmem from Debian lenny * [3] 2.6.26-2-sparc64 from Debian lenny * [4] 2.6.27.42-0.1.1.xs5.6.810.44.111163xen from XenServer 5.6.810-31078p * [5] 2.6.32.21.22 (approx.) from Ubuntu 10.04 on VMware Fusion * [6] 2.6.34 from kernel.org on KVM */ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static const char fn[] = "/proc/net/psched"; unsigned int a, b, c, d; FILE *stream; if (!ovsthread_once_start(&once)) { return; } ticks_per_s = 1.0; buffer_hz = 100; stream = fopen(fn, "r"); if (!stream) { VLOG_WARN("%s: open failed: %s", fn, ovs_strerror(errno)); goto exit; } if (fscanf(stream, "%x %x %x %x", &a, &b, &c, &d) != 4) { VLOG_WARN("%s: read failed", fn); fclose(stream); goto exit; } VLOG_DBG("%s: psched parameters are: %u %u %u %u", fn, a, b, c, d); fclose(stream); if (!a || !b || !c) { VLOG_WARN("%s: invalid scheduler parameters", fn); goto exit; } ticks_per_s = (double) a * c / b; if (c == 1000000) { buffer_hz = d; } else { VLOG_WARN("%s: unexpected psched parameters: %u %u %u %u", fn, a, b, c, d); } VLOG_DBG("%s: ticks_per_s=%f buffer_hz=%u", fn, ticks_per_s, buffer_hz); exit: ovsthread_once_done(&once); } /* Returns the number of bytes that can be transmitted in 'ticks' ticks at a * rate of 'rate' bytes per second. */ static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks) { read_psched(); return (rate * ticks) / ticks_per_s; } /* Returns the number of ticks that it would take to transmit 'size' bytes at a * rate of 'rate' bytes per second. */ static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size) { read_psched(); return rate ? ((unsigned long long int) ticks_per_s * size) / rate : 0; } /* Returns the number of bytes that need to be reserved for qdisc buffering at * a transmission rate of 'rate' bytes per second. */ static unsigned int tc_buffer_per_jiffy(unsigned int rate) { read_psched(); return rate / buffer_hz; } /* Given Netlink 'msg' that describes a qdisc, extracts the name of the qdisc, * e.g. "htb", into '*kind' (if it is nonnull). If 'options' is nonnull, * extracts 'msg''s TCA_OPTIONS attributes into '*options' if it is present or * stores NULL into it if it is absent. * * '*kind' and '*options' point into 'msg', so they are owned by whoever owns * 'msg'. * * Returns 0 if successful, otherwise a positive errno value. */ static int tc_parse_qdisc(const struct ofpbuf *msg, const char **kind, struct nlattr **options) { static const struct nl_policy tca_policy[] = { [TCA_KIND] = { .type = NL_A_STRING, .optional = false }, [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = true }, }; struct nlattr *ta[ARRAY_SIZE(tca_policy)]; if (!nl_policy_parse(msg, NLMSG_HDRLEN + sizeof(struct tcmsg), tca_policy, ta, ARRAY_SIZE(ta))) { VLOG_WARN_RL(&rl, "failed to parse qdisc message"); goto error; } if (kind) { *kind = nl_attr_get_string(ta[TCA_KIND]); } if (options) { *options = ta[TCA_OPTIONS]; } return 0; error: if (kind) { *kind = NULL; } if (options) { *options = NULL; } return EPROTO; } /* Given Netlink 'msg' that describes a class, extracts the queue ID (e.g. the * minor number of its class ID) into '*queue_id', its TCA_OPTIONS attribute * into '*options', and its queue statistics into '*stats'. Any of the output * arguments may be null. * * Returns 0 if successful, otherwise a positive errno value. */ static int tc_parse_class(const struct ofpbuf *msg, unsigned int *handlep, struct nlattr **options, struct netdev_queue_stats *stats) { static const struct nl_policy tca_policy[] = { [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false }, [TCA_STATS2] = { .type = NL_A_NESTED, .optional = false }, }; struct nlattr *ta[ARRAY_SIZE(tca_policy)]; if (!nl_policy_parse(msg, NLMSG_HDRLEN + sizeof(struct tcmsg), tca_policy, ta, ARRAY_SIZE(ta))) { VLOG_WARN_RL(&rl, "failed to parse class message"); goto error; } if (handlep) { struct tcmsg *tc = ofpbuf_at_assert(msg, NLMSG_HDRLEN, sizeof *tc); *handlep = tc->tcm_handle; } if (options) { *options = ta[TCA_OPTIONS]; } if (stats) { const struct gnet_stats_queue *gsq; struct gnet_stats_basic gsb; static const struct nl_policy stats_policy[] = { [TCA_STATS_BASIC] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof gsb }, [TCA_STATS_QUEUE] = { .type = NL_A_UNSPEC, .optional = false, .min_len = sizeof *gsq }, }; struct nlattr *sa[ARRAY_SIZE(stats_policy)]; if (!nl_parse_nested(ta[TCA_STATS2], stats_policy, sa, ARRAY_SIZE(sa))) { VLOG_WARN_RL(&rl, "failed to parse class stats"); goto error; } /* Alignment issues screw up the length of struct gnet_stats_basic on * some arch/bitsize combinations. Newer versions of Linux have a * struct gnet_stats_basic_packed, but we can't depend on that. The * easiest thing to do is just to make a copy. */ memset(&gsb, 0, sizeof gsb); memcpy(&gsb, nl_attr_get(sa[TCA_STATS_BASIC]), MIN(nl_attr_get_size(sa[TCA_STATS_BASIC]), sizeof gsb)); stats->tx_bytes = gsb.bytes; stats->tx_packets = gsb.packets; gsq = nl_attr_get(sa[TCA_STATS_QUEUE]); stats->tx_errors = gsq->drops; } return 0; error: if (options) { *options = NULL; } if (stats) { memset(stats, 0, sizeof *stats); } return EPROTO; } /* Queries the kernel for class with identifier 'handle' and parent 'parent' * on 'netdev'. */ static int tc_query_class(const struct netdev *netdev, unsigned int handle, unsigned int parent, struct ofpbuf **replyp) { struct ofpbuf request; struct tcmsg *tcmsg; int error; tcmsg = tc_make_request(netdev, RTM_GETTCLASS, NLM_F_ECHO, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = parent; error = tc_transact(&request, replyp); if (error) { VLOG_WARN_RL(&rl, "query %s class %u:%u (parent %u:%u) failed (%s)", netdev_get_name(netdev), tc_get_major(handle), tc_get_minor(handle), tc_get_major(parent), tc_get_minor(parent), ovs_strerror(error)); } return error; } /* Equivalent to "tc class del dev handle ". */ static int tc_delete_class(const struct netdev *netdev, unsigned int handle) { struct ofpbuf request; struct tcmsg *tcmsg; int error; tcmsg = tc_make_request(netdev, RTM_DELTCLASS, 0, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = handle; tcmsg->tcm_parent = 0; error = tc_transact(&request, NULL); if (error) { VLOG_WARN_RL(&rl, "delete %s class %u:%u failed (%s)", netdev_get_name(netdev), tc_get_major(handle), tc_get_minor(handle), ovs_strerror(error)); } return error; } /* Equivalent to "tc qdisc del dev root". */ static int tc_del_qdisc(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct ofpbuf request; struct tcmsg *tcmsg; int error; tcmsg = tc_make_request(netdev_, RTM_DELQDISC, 0, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(1, 0); tcmsg->tcm_parent = TC_H_ROOT; error = tc_transact(&request, NULL); if (error == EINVAL) { /* EINVAL probably means that the default qdisc was in use, in which * case we've accomplished our purpose. */ error = 0; } if (!error && netdev->tc) { if (netdev->tc->ops->tc_destroy) { netdev->tc->ops->tc_destroy(netdev->tc); } netdev->tc = NULL; } return error; } static bool getqdisc_is_safe(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static bool safe = false; if (ovsthread_once_start(&once)) { struct utsname utsname; int major, minor; if (uname(&utsname) == -1) { VLOG_WARN("uname failed (%s)", ovs_strerror(errno)); } else if (!ovs_scan(utsname.release, "%d.%d", &major, &minor)) { VLOG_WARN("uname reported bad OS release (%s)", utsname.release); } else if (major < 2 || (major == 2 && minor < 35)) { VLOG_INFO("disabling unsafe RTM_GETQDISC in Linux kernel %s", utsname.release); } else { safe = true; } ovsthread_once_done(&once); } return safe; } /* If 'netdev''s qdisc type and parameters are not yet known, queries the * kernel to determine what they are. Returns 0 if successful, otherwise a * positive errno value. */ static int tc_query_qdisc(const struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct ofpbuf request, *qdisc; const struct tc_ops *ops; struct tcmsg *tcmsg; int load_error; int error; if (netdev->tc) { return 0; } /* This RTM_GETQDISC is crafted to avoid OOPSing kernels that do not have * commit 53b0f08 "net_sched: Fix qdisc_notify()", which is anything before * 2.6.35 without that fix backported to it. * * To avoid the OOPS, we must not make a request that would attempt to dump * a "built-in" qdisc, that is, the default pfifo_fast qdisc or one of a * few others. There are a few ways that I can see to do this, but most of * them seem to be racy (and if you lose the race the kernel OOPSes). The * technique chosen here is to assume that any non-default qdisc that we * create will have a class with handle 1:0. The built-in qdiscs only have * a class with handle 0:0. * * On Linux 2.6.35+ we use the straightforward method because it allows us * to handle non-builtin qdiscs without handle 1:0 (e.g. codel). However, * in such a case we get no response at all from the kernel (!) if a * builtin qdisc is in use (which is later caught by "!error && * !qdisc->size"). */ tcmsg = tc_make_request(netdev_, RTM_GETQDISC, NLM_F_ECHO, &request); if (!tcmsg) { return ENODEV; } tcmsg->tcm_handle = tc_make_handle(getqdisc_is_safe() ? 0 : 1, 0); tcmsg->tcm_parent = getqdisc_is_safe() ? TC_H_ROOT : 0; /* Figure out what tc class to instantiate. */ error = tc_transact(&request, &qdisc); if (!error && qdisc->size) { const char *kind; error = tc_parse_qdisc(qdisc, &kind, NULL); if (error) { ops = &tc_ops_other; } else { ops = tc_lookup_linux_name(kind); if (!ops) { static struct vlog_rate_limit rl2 = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_DBG_RL(&rl2, "unknown qdisc \"%s\"", kind); ops = &tc_ops_other; } } } else if ((!error && !qdisc->size) || error == ENOENT) { /* Either it's a built-in qdisc, or (on Linux pre-2.6.35) it's a qdisc * set up by some other entity that doesn't have a handle 1:0. We will * assume that it's the system default qdisc. */ ops = &tc_ops_default; error = 0; } else { /* Who knows? Maybe the device got deleted. */ VLOG_WARN_RL(&rl, "query %s qdisc failed (%s)", netdev_get_name(netdev_), ovs_strerror(error)); ops = &tc_ops_other; } /* Instantiate it. */ load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev_), qdisc); ovs_assert((load_error == 0) == (netdev->tc != NULL)); ofpbuf_delete(qdisc); return error ? error : load_error; } /* Linux traffic control uses tables with 256 entries ("rtab" tables) to approximate the time to transmit packets of various lengths. For an MTU of 256 or less, each entry is exact; for an MTU of 257 through 512, each entry represents two possible packet lengths; for a MTU of 513 through 1024, four possible lengths; and so on. Returns, for the specified 'mtu', the number of bits that packet lengths need to be shifted right to fit within such a 256-entry table. */ static int tc_calc_cell_log(unsigned int mtu) { int cell_log; if (!mtu) { mtu = ETH_PAYLOAD_MAX; } mtu += ETH_HEADER_LEN + VLAN_HEADER_LEN; for (cell_log = 0; mtu >= 256; cell_log++) { mtu >>= 1; } return cell_log; } /* Initializes 'rate' properly for a rate of 'Bps' bytes per second with an MTU * of 'mtu'. */ static void tc_fill_rate(struct tc_ratespec *rate, uint64_t Bps, int mtu) { memset(rate, 0, sizeof *rate); rate->cell_log = tc_calc_cell_log(mtu); /* rate->overhead = 0; */ /* New in 2.6.24, not yet in some */ /* rate->cell_align = 0; */ /* distro headers. */ rate->mpu = ETH_TOTAL_MIN; rate->rate = Bps; } /* Appends to 'msg' an "rtab" table for the specified 'rate' as a Netlink * attribute of the specified "type". * * See tc_calc_cell_log() above for a description of "rtab"s. */ static void tc_put_rtab(struct ofpbuf *msg, uint16_t type, const struct tc_ratespec *rate) { uint32_t *rtab; unsigned int i; rtab = nl_msg_put_unspec_uninit(msg, type, TC_RTAB_SIZE); for (i = 0; i < TC_RTAB_SIZE / sizeof *rtab; i++) { unsigned packet_size = (i + 1) << rate->cell_log; if (packet_size < rate->mpu) { packet_size = rate->mpu; } rtab[i] = tc_bytes_to_ticks(rate->rate, packet_size); } } /* Calculates the proper value of 'buffer' or 'cbuffer' in HTB options given a * rate of 'Bps' bytes per second, the specified 'mtu', and a user-requested * burst size of 'burst_bytes'. (If no value was requested, a 'burst_bytes' of * 0 is fine.) */ static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes) { unsigned int min_burst = tc_buffer_per_jiffy(Bps) + mtu; return tc_bytes_to_ticks(Bps, MAX(burst_bytes, min_burst)); } /* Linux-only functions declared in netdev-linux.h */ /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If * 'enable' is true, the bit is set. Otherwise, it is cleared. */ int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, const char *flag_name, bool enable) { const char *netdev_name = netdev_get_name(netdev); struct ethtool_value evalue; uint32_t new_flags; int error; COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); if (error) { return error; } COVERAGE_INC(netdev_set_ethtool); new_flags = (evalue.data & ~flag) | (enable ? flag : 0); if (new_flags == evalue.data) { return 0; } evalue.data = new_flags; error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, ETHTOOL_SFLAGS, "ETHTOOL_SFLAGS"); if (error) { return error; } COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, ETHTOOL_GFLAGS, "ETHTOOL_GFLAGS"); if (error) { return error; } if (new_flags != evalue.data) { VLOG_WARN_RL(&rl, "attempt to %s ethtool %s flag on network " "device %s failed", enable ? "enable" : "disable", flag_name, netdev_name); return EOPNOTSUPP; } return 0; } /* Utility functions. */ /* Copies 'src' into 'dst', performing format conversion in the process. */ static void netdev_stats_from_rtnl_link_stats(struct netdev_stats *dst, const struct rtnl_link_stats *src) { dst->rx_packets = src->rx_packets; dst->tx_packets = src->tx_packets; dst->rx_bytes = src->rx_bytes; dst->tx_bytes = src->tx_bytes; dst->rx_errors = src->rx_errors; dst->tx_errors = src->tx_errors; dst->rx_dropped = src->rx_dropped; dst->tx_dropped = src->tx_dropped; dst->multicast = src->multicast; dst->collisions = src->collisions; dst->rx_length_errors = src->rx_length_errors; dst->rx_over_errors = src->rx_over_errors; dst->rx_crc_errors = src->rx_crc_errors; dst->rx_frame_errors = src->rx_frame_errors; dst->rx_fifo_errors = src->rx_fifo_errors; dst->rx_missed_errors = src->rx_missed_errors; dst->tx_aborted_errors = src->tx_aborted_errors; dst->tx_carrier_errors = src->tx_carrier_errors; dst->tx_fifo_errors = src->tx_fifo_errors; dst->tx_heartbeat_errors = src->tx_heartbeat_errors; dst->tx_window_errors = src->tx_window_errors; } /* Copies 'src' into 'dst', performing format conversion in the process. */ static void netdev_stats_from_rtnl_link_stats64(struct netdev_stats *dst, const struct rtnl_link_stats64 *src) { dst->rx_packets = src->rx_packets; dst->tx_packets = src->tx_packets; dst->rx_bytes = src->rx_bytes; dst->tx_bytes = src->tx_bytes; dst->rx_errors = src->rx_errors; dst->tx_errors = src->tx_errors; dst->rx_dropped = src->rx_dropped; dst->tx_dropped = src->tx_dropped; dst->multicast = src->multicast; dst->collisions = src->collisions; dst->rx_length_errors = src->rx_length_errors; dst->rx_over_errors = src->rx_over_errors; dst->rx_crc_errors = src->rx_crc_errors; dst->rx_frame_errors = src->rx_frame_errors; dst->rx_fifo_errors = src->rx_fifo_errors; dst->rx_missed_errors = src->rx_missed_errors; dst->tx_aborted_errors = src->tx_aborted_errors; dst->tx_carrier_errors = src->tx_carrier_errors; dst->tx_fifo_errors = src->tx_fifo_errors; dst->tx_heartbeat_errors = src->tx_heartbeat_errors; dst->tx_window_errors = src->tx_window_errors; } static int get_stats_via_netlink(const struct netdev *netdev_, struct netdev_stats *stats) { struct ofpbuf request; struct ofpbuf *reply; int error; ofpbuf_init(&request, 0); nl_msg_put_nlmsghdr(&request, sizeof(struct ifinfomsg) + NL_ATTR_SIZE(IFNAMSIZ), RTM_GETLINK, NLM_F_REQUEST); ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg)); nl_msg_put_string(&request, IFLA_IFNAME, netdev_get_name(netdev_)); error = nl_transact(NETLINK_ROUTE, &request, &reply); ofpbuf_uninit(&request); if (error) { return error; } if (ofpbuf_try_pull(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg))) { const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS64); if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats64)) { netdev_stats_from_rtnl_link_stats64(stats, nl_attr_get(a)); error = 0; } else { const struct nlattr *a = nl_attr_find(reply, 0, IFLA_STATS); if (a && nl_attr_get_size(a) >= sizeof(struct rtnl_link_stats)) { netdev_stats_from_rtnl_link_stats(stats, nl_attr_get(a)); error = 0; } else { VLOG_WARN_RL(&rl, "RTM_GETLINK reply lacks stats"); error = EPROTO; } } } else { VLOG_WARN_RL(&rl, "short RTM_GETLINK reply"); error = EPROTO; } ofpbuf_delete(reply); return error; } static int get_flags(const struct netdev *dev, unsigned int *flags) { struct ifreq ifr; int error; *flags = 0; error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); if (!error) { *flags = ifr.ifr_flags; } return error; } static int set_flags(const char *name, unsigned int flags) { struct ifreq ifr; ifr.ifr_flags = flags; return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); } static int do_get_ifindex(const char *netdev_name) { struct ifreq ifr; int error; ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_ifindex); error = af_inet_ioctl(SIOCGIFINDEX, &ifr); if (error) { VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s", netdev_name, ovs_strerror(error)); return -error; } return ifr.ifr_ifindex; } static int get_ifindex(const struct netdev *netdev_, int *ifindexp) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); if (!(netdev->cache_valid & VALID_IFINDEX)) { int ifindex = do_get_ifindex(netdev_get_name(netdev_)); if (ifindex < 0) { netdev->get_ifindex_error = -ifindex; netdev->ifindex = 0; } else { netdev->get_ifindex_error = 0; netdev->ifindex = ifindex; } netdev->cache_valid |= VALID_IFINDEX; } *ifindexp = netdev->ifindex; return netdev->get_ifindex_error; } static int get_etheraddr(const char *netdev_name, struct eth_addr *ea) { struct ifreq ifr; int hwaddr_family; int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_hwaddr); error = af_inet_ioctl(SIOCGIFHWADDR, &ifr); if (error) { /* ENODEV probably means that a vif disappeared asynchronously and * hasn't been removed from the database yet, so reduce the log level * to INFO for that case. */ VLOG(error == ENODEV ? VLL_INFO : VLL_ERR, "ioctl(SIOCGIFHWADDR) on %s device failed: %s", netdev_name, ovs_strerror(error)); return error; } hwaddr_family = ifr.ifr_hwaddr.sa_family; if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) { VLOG_INFO("%s device has unknown hardware address family %d", netdev_name, hwaddr_family); return EINVAL; } memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN); return 0; } static int set_etheraddr(const char *netdev_name, const struct eth_addr mac) { struct ifreq ifr; int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, &mac, ETH_ADDR_LEN); COVERAGE_INC(netdev_set_hwaddr); error = af_inet_ioctl(SIOCSIFHWADDR, &ifr); if (error) { VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s", netdev_name, ovs_strerror(error)); } return error; } static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, int cmd, const char *cmd_name) { struct ifreq ifr; int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; error = af_inet_ioctl(SIOCETHTOOL, &ifr); if (error) { if (error != EOPNOTSUPP) { VLOG_WARN_RL(&rl, "ethtool command %s on network device %s " "failed: %s", cmd_name, name, ovs_strerror(error)); } else { /* The device doesn't support this operation. That's pretty * common, so there's no point in logging anything. */ } } return error; } static int netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, int cmd, const char *cmd_name) { struct ifreq ifr; int error; ifr.ifr_addr.sa_family = AF_INET; error = af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); if (!error) { const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ifr.ifr_addr); *ip = sin->sin_addr; } return error; } /* Returns an AF_PACKET raw socket or a negative errno value. */ static int af_packet_sock(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int sock; if (ovsthread_once_start(&once)) { sock = socket(AF_PACKET, SOCK_RAW, 0); if (sock >= 0) { int error = set_nonblocking(sock); if (error) { close(sock); sock = -error; } } else { sock = -errno; VLOG_ERR("failed to create packet socket: %s", ovs_strerror(errno)); } ovsthread_once_done(&once); } return sock; } openvswitch-2.5.9/lib/PaxHeaders.82075/syslog-provider.h0000644000000000000000000000013113534540071017732 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90585434 openvswitch-2.5.9/lib/syslog-provider.h0000644000175000017500000000350513534540071021424 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SYSLOG_PROVIDER_H #define SYSLOG_PROVIDER_H 1 /* Open vSwitch interface to syslog daemon's interface. * * 'syslogger' is the base class that provides abstraction. */ struct syslogger { const struct syslog_class *class; /* Virtual functions for concrete * syslogger implementations. */ const char *prefix; /* Prefix that is enforced by concrete * syslogger implementation. Used * in vlog/list-pattern function. */ }; /* Each concrete syslogger implementation must define it's own table with * following functions. These functions must never call any other VLOG_ * function to prevent deadlocks. */ struct syslog_class { /* openlog() function should be called before syslog() function. It * should initialize all system resources needed to perform logging. */ void (*openlog)(struct syslogger *this, int facility); /* syslog() function sends message 'msg' to syslog daemon. */ void (*syslog)(struct syslogger *this, int pri, const char *msg); }; static inline const char * syslog_get_prefix(struct syslogger *this) { return this->prefix; } #endif /* syslog-provider.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/timer.h0000644000000000000000000000013213534540071015703 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.913854399 openvswitch-2.5.9/lib/timer.h0000644000175000017500000000344613534540071017400 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TIMER_H #define TIMER_H 1 #include #include "timeval.h" #include "util.h" struct timer { long long int t; }; long long int timer_msecs_until_expired(const struct timer *); void timer_wait_at(const struct timer *, const char *where); #define timer_wait(timer) timer_wait_at(timer, OVS_SOURCE_LOCATOR) /* Causes 'timer' to expire when 'duration' milliseconds have passed. * * May be used to initialize 'timer'. */ static inline void timer_set_duration(struct timer *timer, long long int duration) { timer->t = time_msec() + duration; } /* Causes 'timer' never to expire. * * May be used to initialize 'timer'. */ static inline void timer_set_infinite(struct timer *timer) { timer->t = LLONG_MAX; } /* Causes 'timer' to expire immediately. * * May be used to initialize 'timer'. */ static inline void timer_set_expired(struct timer *timer) { timer->t = LLONG_MIN; } /* True if 'timer' has expired. */ static inline bool timer_expired(const struct timer *timer) { return time_msec() >= timer->t; } /* Returns ture if 'timer' will never expire. */ static inline bool timer_is_infinite(const struct timer *timer) { return timer->t == LLONG_MAX; } #endif /* timer.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/token-bucket.c0000644000000000000000000000013213534540071017151 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.921854458 openvswitch-2.5.9/lib/token-bucket.c0000644000175000017500000000533613534540071020646 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "openvswitch/token-bucket.h" #include "poll-loop.h" #include "sat-math.h" #include "timeval.h" #include "util.h" /* Initializes 'tb' to accumulate 'rate' tokens per millisecond, with a * maximum of 'burst' tokens. * * The token bucket is initially full. * * It may be more convenient to use TOKEN_BUCKET_INIT. */ void token_bucket_init(struct token_bucket *tb, unsigned int rate, unsigned int burst) { tb->rate = rate; tb->burst = burst; tb->tokens = 0; tb->last_fill = LLONG_MIN; } /* Changes 'tb' to accumulate 'rate' tokens per millisecond, with a maximum of * 'burst' tokens. * * 'tb' must already have been initialized with TOKEN_BUCKET_INIT or * token_bucket_init(). */ void token_bucket_set(struct token_bucket *tb, unsigned int rate, unsigned int burst) { tb->rate = rate; tb->burst = burst; if (burst < tb->tokens) { tb->tokens = burst; } } /* Attempts to remove 'n' tokens from 'tb'. Returns true if successful, false * if 'tb' contained fewer than 'n' tokens (and thus 'n' tokens could not be * removed) . */ bool token_bucket_withdraw(struct token_bucket *tb, unsigned int n) { if (tb->tokens < n) { long long int now = time_msec(); if (now > tb->last_fill) { unsigned long long int elapsed_ull = (unsigned long long int) now - tb->last_fill; unsigned int elapsed = MIN(UINT_MAX, elapsed_ull); unsigned int add = sat_mul(tb->rate, elapsed); unsigned int tokens = sat_add(tb->tokens, add); tb->tokens = MIN(tokens, tb->burst); tb->last_fill = now; } if (tb->tokens < n) { return false; } } tb->tokens -= n; return true; } /* Causes the poll loop to wake up when at least 'n' tokens will be available * for withdrawal from 'tb'. */ void token_bucket_wait(struct token_bucket *tb, unsigned int n) { if (tb->tokens >= n) { poll_immediate_wake(); } else { unsigned int need = n - tb->tokens; poll_timer_wait_until(tb->last_fill + need / tb->rate + 1); } } openvswitch-2.5.9/lib/PaxHeaders.82075/entropy.h0000644000000000000000000000013213534540071016263 xustar0030 mtime=1567801401.385681053 30 atime=1567801402.073686105 30 ctime=1567801424.717852954 openvswitch-2.5.9/lib/entropy.h0000644000175000017500000000136513534540071017756 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ENTROPY_H #define ENTROPY_H 1 #include int get_entropy(void *, size_t); void get_entropy_or_die(void *, size_t); #endif /* entropy.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/netflow.h0000644000000000000000000000013213534540071016241 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 30 ctime=1567801424.777853396 openvswitch-2.5.9/lib/netflow.h0000644000175000017500000000651113534540071017732 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETFLOW_H #define NETFLOW_H 1 /* NetFlow v5 protocol definitions. */ #include #include "openvswitch/types.h" #include "util.h" #define NETFLOW_V5_VERSION 5 /* Every NetFlow v5 message contains the header that follows. This is * followed by up to thirty records that describe a terminating flow. * We only send a single record per NetFlow message. */ struct netflow_v5_header { ovs_be16 version; /* NetFlow version is 5. */ ovs_be16 count; /* Number of records in this message. */ ovs_be32 sysuptime; /* System uptime in milliseconds. */ ovs_be32 unix_secs; /* Number of seconds since Unix epoch. */ ovs_be32 unix_nsecs; /* Number of residual nanoseconds after epoch seconds. */ ovs_be32 flow_seq; /* Number of flows since sending messages began. */ uint8_t engine_type; /* Engine type. */ uint8_t engine_id; /* Engine id. */ ovs_be16 sampling_interval; /* Set to zero. */ }; BUILD_ASSERT_DECL(sizeof(struct netflow_v5_header) == 24); /* A NetFlow v5 description of a terminating flow. It is preceded by a * NetFlow v5 header. */ struct netflow_v5_record { ovs_be32 src_addr; /* Source IP address. */ ovs_be32 dst_addr; /* Destination IP address. */ ovs_be32 nexthop; /* IP address of next hop. Set to 0. */ ovs_be16 input; /* Input interface index. */ ovs_be16 output; /* Output interface index. */ ovs_be32 packet_count; /* Number of packets. */ ovs_be32 byte_count; /* Number of bytes. */ ovs_be32 init_time; /* Value of sysuptime on first packet. */ ovs_be32 used_time; /* Value of sysuptime on last packet. */ /* The 'src_port' and 'dst_port' identify the source and destination * port, respectively, for TCP and UDP. For ICMP, the high-order * byte identifies the type and low-order byte identifies the code * in the 'dst_port' field. */ ovs_be16 src_port; ovs_be16 dst_port; uint8_t pad1; uint8_t tcp_flags; /* Union of seen TCP flags. */ uint8_t ip_proto; /* IP protocol. */ uint8_t ip_tos; /* IP TOS value. */ ovs_be16 src_as; /* Source AS ID. Set to 0. */ ovs_be16 dst_as; /* Destination AS ID. Set to 0. */ uint8_t src_mask; /* Source mask bits. Set to 0. */ uint8_t dst_mask; /* Destination mask bits. Set to 0. */ uint8_t pad[2]; }; BUILD_ASSERT_DECL(sizeof(struct netflow_v5_record) == 48); #endif /* lib/netflow.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/vlan-bitmap.h0000644000000000000000000000013213534540071016775 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.941854606 openvswitch-2.5.9/lib/vlan-bitmap.h0000644000175000017500000000301113534540071020456 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VLAN_BITMAP_H #define VLAN_BITMAP_H 1 #include #include #include "bitmap.h" /* A "VLAN bitmap" is a 4096-bit bitmap that represents a set. A 1-bit * indicates that the respective VLAN is a member of the set, a 0-bit indicates * that it is not. There is one wrinkle: NULL is a valid value that indicates * either that all VLANs are or are not members, depending on the vlan_bitmap. * * This is empirically a useful data structure. */ unsigned long *vlan_bitmap_from_array(const int64_t *vlans, size_t n_vlans); int vlan_bitmap_from_array__(const int64_t *vlans, size_t n_vlans, unsigned long int *b); bool vlan_bitmap_equal(const unsigned long *a, const unsigned long *b); /* Returns a new copy of 'vlans'. */ static inline unsigned long * vlan_bitmap_clone(const unsigned long *vlans) { return vlans ? bitmap_clone(vlans, 4096) : NULL; } #endif /* lib/vlan-bitmap.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/csum.c0000644000000000000000000000013213534540071015525 xustar0030 mtime=1567801401.329680641 30 atime=1567801402.069686075 30 ctime=1567801424.685852718 openvswitch-2.5.9/lib/csum.c0000644000175000017500000001105613534540071017216 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "csum.h" #include "unaligned.h" #ifndef __CHECKER__ /* Returns the IP checksum of the 'n' bytes in 'data'. * * The return value has the same endianness as the data. That is, if 'data' * consists of a packet in network byte order, then the return value is a value * in network byte order, and if 'data' consists of a data structure in host * byte order, then the return value is in host byte order. */ ovs_be16 csum(const void *data, size_t n) { return csum_finish(csum_continue(0, data, n)); } /* Adds the 'n' bytes in 'data' to the partial IP checksum 'partial' and * returns the updated checksum. (To start a new checksum, pass 0 for * 'partial'. To obtain the finished checksum, pass the return value to * csum_finish().) */ uint32_t csum_continue(uint32_t partial, const void *data_, size_t n) { const ovs_be16 *data = data_; for (; n > 1; n -= 2, data++) { partial = csum_add16(partial, get_unaligned_be16(data)); } if (n) { #ifdef WORDS_BIGENDIAN partial += (*(uint8_t *) data) << 8; #else partial += *(uint8_t *) data; #endif } return partial; } /* Returns the IP checksum corresponding to 'partial', which is a value updated * by some combination of csum_add16(), csum_add32(), and csum_continue(). * * The return value has the same endianness as the checksummed data. That is, * if the data consist of a packet in network byte order, then the return value * is a value in network byte order, and if the data are a data structure in * host byte order, then the return value is in host byte order. */ ovs_be16 csum_finish(uint32_t partial) { while (partial >> 16) { partial = (partial & 0xffff) + (partial >> 16); } return ~partial; } /* Returns the new checksum for a packet in which the checksum field previously * contained 'old_csum' and in which a field that contained 'old_u16' was * changed to contain 'new_u16'. */ ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16) { /* Ones-complement arithmetic is endian-independent, so this code does not * use htons() or ntohs(). * * See RFC 1624 for formula and explanation. */ uint16_t hc_complement = ~old_csum; uint16_t m_complement = ~old_u16; uint16_t m_prime = new_u16; uint32_t sum = hc_complement + m_complement + m_prime; return csum_finish(sum); } /* Returns the new checksum for a packet in which the checksum field previously * contained 'old_csum' and in which a field that contained 'old_u32' was * changed to contain 'new_u32'. */ ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32) { return recalc_csum16(recalc_csum16(old_csum, old_u32, new_u32), old_u32 >> 16, new_u32 >> 16); } /* Returns the new checksum for a packet in which the checksum field previously * contained 'old_csum' and in which a field that contained the 6 bytes at * 'old_mac' was changed to contain the 6 bytes at 'new_mac'. */ ovs_be16 recalc_csum48(ovs_be16 old_csum, const struct eth_addr old_mac, const struct eth_addr new_mac) { ovs_be16 new_csum = old_csum; for (int i = 0; i < 3; ++i) { new_csum = recalc_csum16(new_csum, old_mac.be16[i], new_mac.be16[i]); } return new_csum; } /* Returns the new checksum for a packet in which the checksum field previously * contained 'old_csum' and in which a field that contained 'old_u32[4]' was * changed to contain 'new_u32[4]'. */ ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4], const ovs_be32 new_u32[4]) { ovs_be16 new_csum = old_csum; int i; for (i = 0; i < 4; ++i) { new_csum = recalc_csum32(new_csum, get_16aligned_be32(&old_u32[i]), new_u32[i]); } return new_csum; } #else /* __CHECKER__ */ /* Making sparse happy with these functions also makes them unreadable, so * don't bother to show it their implementations. */ #endif openvswitch-2.5.9/lib/PaxHeaders.82075/rtbsd.h0000644000000000000000000000013213534540071015701 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801425.005855078 openvswitch-2.5.9/lib/rtbsd.h0000644000175000017500000000363513534540071017376 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Gaetano Catalli. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RTBSD_H #define RTBSD_H 1 #include #include "list.h" /* * A digested version of a message received from a PF_ROUTE socket which * indicates that a network device has been created or destroyed or changed. */ struct rtbsd_change { /* Copied from struct if_msghdr. */ int msg_type; /* e.g. XXX. */ /* Copied from struct if_msghdr. */ int if_index; /* Index of network device. */ char if_name[IF_NAMESIZE]; /* Name of network device. */ int master_ifindex; /* Ifindex of datapath master (0 if none). */ }; /* * Function called to report that a netdev has changed. 'change' describes the * specific change. It may be null if the buffer of change information * overflowed, in which case the function must assume that every device may * have changed. 'aux' is as specified in the call to * rtbsd_notifier_register(). */ typedef void rtbsd_notify_func(const struct rtbsd_change *, void *aux); struct rtbsd_notifier { struct ovs_list node; rtbsd_notify_func *cb; void *aux; }; int rtbsd_notifier_register(struct rtbsd_notifier *, rtbsd_notify_func *, void *aux); void rtbsd_notifier_unregister(struct rtbsd_notifier *); void rtbsd_notifier_run(void); void rtbsd_notifier_wait(void); #endif /* rtbsd.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/syslog-libc.h0000644000000000000000000000013113534540071017011 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 29 ctime=1567801424.90585434 openvswitch-2.5.9/lib/syslog-libc.h0000644000175000017500000000132113534540071020475 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SYSLOG_LIBC_H #define SYSLOG_LIBC_H 1 struct syslogger *syslog_libc_create(void); #endif /* syslog-libc.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sat-math.h0000644000000000000000000000013213534540071016301 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801424.869854075 openvswitch-2.5.9/lib/sat-math.h0000644000175000017500000000217213534540071017771 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SAT_MATH_H #define SAT_MATH_H 1 #include #include "openvswitch/util.h" /* Saturating addition: overflow yields UINT_MAX. */ static inline unsigned int sat_add(unsigned int x, unsigned int y) { return x + y >= x ? x + y : UINT_MAX; } /* Saturating subtraction: underflow yields 0. */ static inline unsigned int sat_sub(unsigned int x, unsigned int y) { return x >= y ? x - y : 0; } static inline unsigned int sat_mul(unsigned int x, unsigned int y) { return OVS_SAT_MUL(x, y); } #endif /* sat-math.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-atomic-gcc4+.h0000644000000000000000000000013213534540071017535 xustar0030 mtime=1567801401.541682198 30 atime=1567801402.089686223 30 ctime=1567801424.809853633 openvswitch-2.5.9/lib/ovs-atomic-gcc4+.h0000644000175000017500000001722613534540071021233 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This header implements atomic operation primitives on GCC 4.x. */ #ifndef IN_OVS_ATOMIC_H #error "This header should only be included indirectly via ovs-atomic.h." #endif #include "ovs-atomic-locked.h" #define OVS_ATOMIC_GCC4P_IMPL 1 #define ATOMIC(TYPE) TYPE #define ATOMIC_BOOL_LOCK_FREE 2 #define ATOMIC_CHAR_LOCK_FREE 2 #define ATOMIC_SHORT_LOCK_FREE 2 #define ATOMIC_INT_LOCK_FREE 2 #define ATOMIC_LONG_LOCK_FREE (ULONG_MAX <= UINTPTR_MAX ? 2 : 0) #define ATOMIC_LLONG_LOCK_FREE (ULLONG_MAX <= UINTPTR_MAX ? 2 : 0) #define ATOMIC_POINTER_LOCK_FREE 2 typedef enum { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order; #define IS_LOCKLESS_ATOMIC(OBJECT) (sizeof(OBJECT) <= sizeof(void *)) #define ATOMIC_VAR_INIT(VALUE) VALUE #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0) static inline void atomic_thread_fence(memory_order order) { if (order != memory_order_relaxed) { __sync_synchronize(); } } static inline void atomic_thread_fence_if_seq_cst(memory_order order) { if (order == memory_order_seq_cst) { __sync_synchronize(); } } static inline void atomic_signal_fence(memory_order order) { if (order != memory_order_relaxed) { asm volatile("" : : : "memory"); } } #define atomic_is_lock_free(OBJ) \ ((void) *(OBJ), \ IS_LOCKLESS_ATOMIC(*(OBJ)) ? 2 : 0) #define atomic_store(DST, SRC) \ atomic_store_explicit(DST, SRC, memory_order_seq_cst) #define atomic_store_explicit(DST, SRC, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(SRC) src__ = (SRC); \ \ if (IS_LOCKLESS_ATOMIC(*dst__)) { \ atomic_thread_fence(ORDER); \ *(typeof(*(DST)) volatile *)dst__ = src__; \ atomic_thread_fence_if_seq_cst(ORDER); \ } else { \ atomic_store_locked(dst__, src__); \ } \ (void) 0; \ }) #define atomic_read(SRC, DST) \ atomic_read_explicit(SRC, DST, memory_order_seq_cst) #define atomic_read_explicit(SRC, DST, ORDER) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(SRC) src__ = (SRC); \ \ if (IS_LOCKLESS_ATOMIC(*src__)) { \ atomic_thread_fence_if_seq_cst(ORDER); \ *dst__ = *(typeof(*(SRC)) volatile *)src__; \ } else { \ atomic_read_locked(src__, dst__); \ } \ (void) 0; \ }) #define atomic_compare_exchange_strong(DST, EXP, SRC) \ ({ \ typeof(DST) dst__ = (DST); \ typeof(EXP) expp__ = (EXP); \ typeof(SRC) src__ = (SRC); \ typeof(SRC) exp__ = *expp__; \ typeof(SRC) ret__; \ \ ret__ = __sync_val_compare_and_swap(dst__, exp__, src__); \ if (ret__ != exp__) { \ *expp__ = ret__; \ } \ ret__ == exp__; \ }) #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \ ((void) (ORD1), (void) (ORD2), \ atomic_compare_exchange_strong(DST, EXP, SRC)) #define atomic_compare_exchange_weak \ atomic_compare_exchange_strong #define atomic_compare_exchange_weak_explicit \ atomic_compare_exchange_strong_explicit #define atomic_op__(RMW, OP, ARG, ORIG) \ ({ \ typeof(RMW) rmw__ = (RMW); \ typeof(ARG) arg__ = (ARG); \ typeof(ORIG) orig__ = (ORIG); \ \ if (IS_LOCKLESS_ATOMIC(*rmw__)) { \ *orig__ = __sync_fetch_and_##OP(rmw__, arg__); \ } else { \ atomic_op_locked(rmw__, OP, arg__, orig__); \ } \ (void) 0; \ }) #define atomic_add(RMW, ARG, ORIG) atomic_op__(RMW, add, ARG, ORIG) #define atomic_sub(RMW, ARG, ORIG) atomic_op__(RMW, sub, ARG, ORIG) #define atomic_or(RMW, ARG, ORIG) atomic_op__(RMW, or, ARG, ORIG) #define atomic_xor(RMW, ARG, ORIG) atomic_op__(RMW, xor, ARG, ORIG) #define atomic_and(RMW, ARG, ORIG) atomic_op__(RMW, and, ARG, ORIG) #define atomic_add_explicit(RMW, OPERAND, ORIG, ORDER) \ ((void) (ORDER), atomic_add(RMW, OPERAND, ORIG)) #define atomic_sub_explicit(RMW, OPERAND, ORIG, ORDER) \ ((void) (ORDER), atomic_sub(RMW, OPERAND, ORIG)) #define atomic_or_explicit(RMW, OPERAND, ORIG, ORDER) \ ((void) (ORDER), atomic_or(RMW, OPERAND, ORIG)) #define atomic_xor_explicit(RMW, OPERAND, ORIG, ORDER) \ ((void) (ORDER), atomic_xor(RMW, OPERAND, ORIG)) #define atomic_and_explicit(RMW, OPERAND, ORIG, ORDER) \ ((void) (ORDER), atomic_and(RMW, OPERAND, ORIG)) /* atomic_flag */ typedef struct { int b; } atomic_flag; #define ATOMIC_FLAG_INIT { false } static inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object, memory_order order) { bool old; /* __sync_lock_test_and_set() by itself is an acquire barrier. * For anything higher additional barriers are needed. */ if (order > memory_order_acquire) { atomic_thread_fence(order); } old = __sync_lock_test_and_set(&object->b, 1); atomic_thread_fence_if_seq_cst(order); return old; } #define atomic_flag_test_and_set(FLAG) \ atomic_flag_test_and_set_explicit(FLAG, memory_order_seq_cst) static inline void atomic_flag_clear_explicit(volatile atomic_flag *object, memory_order order) { /* __sync_lock_release() by itself is a release barrier. For * anything else additional barrier may be needed. */ if (order != memory_order_release) { atomic_thread_fence(order); } __sync_lock_release(&object->b); atomic_thread_fence_if_seq_cst(order); } #define atomic_flag_clear(FLAG) \ atomic_flag_clear_explicit(FLAG, memory_order_seq_cst) openvswitch-2.5.9/lib/PaxHeaders.82075/libsflow.sym.in0000644000000000000000000000013113534540071017371 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.641852394 openvswitch-2.5.9/lib/libsflow.sym.in0000644000175000017500000000005613534540071021061 0ustar00jpettitjpettit00000000000000libsflow_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/lib/PaxHeaders.82075/heap.c0000644000000000000000000000013213534540071015473 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.077686135 30 ctime=1567801424.713852925 openvswitch-2.5.9/lib/heap.c0000644000175000017500000001240613534540071017164 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "heap.h" #include #include "util.h" static void put_node(struct heap *, struct heap_node *, size_t i); static void swap_nodes(struct heap *, size_t i, size_t j); static bool float_up(struct heap *, size_t i); static void float_down(struct heap *, size_t i); static void float_up_or_down(struct heap *, size_t i); /* Initializes 'heap' as an empty heap. */ void heap_init(struct heap *heap) { heap->array = NULL; heap->n = 0; heap->allocated = 0; } /* Frees memory owned internally by 'heap'. The caller is responsible for * freeing 'heap' itself, if necessary. */ void heap_destroy(struct heap *heap) { if (heap) { free(heap->array); } } /* Removes all of the elements from 'heap', without freeing any allocated * memory. */ void heap_clear(struct heap *heap) { heap->n = 0; } /* Exchanges the contents of 'a' and 'b'. */ void heap_swap(struct heap *a, struct heap *b) { struct heap tmp = *a; *a = *b; *b = tmp; } /* Inserts 'node' into 'heap' with the specified 'priority'. * * This takes time O(lg n). */ void heap_insert(struct heap *heap, struct heap_node *node, uint64_t priority) { heap_raw_insert(heap, node, priority); float_up(heap, node->idx); } /* Removes 'node' from 'heap'. * * This takes time O(lg n). */ void heap_remove(struct heap *heap, struct heap_node *node) { size_t i = node->idx; heap_raw_remove(heap, node); if (i <= heap->n) { float_up_or_down(heap, i); } } /* Changes the priority of 'node' (which must be in 'heap') to 'priority'. * * This takes time O(lg n). */ void heap_change(struct heap *heap, struct heap_node *node, uint64_t priority) { heap_raw_change(node, priority); float_up_or_down(heap, node->idx); } /* Inserts 'node' into 'heap' with the specified 'priority', without * maintaining the heap invariant. * * After this call, heap_max() will no longer necessarily return the maximum * value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in * heap level order, until the next call to heap_rebuild(heap). * * This takes time O(1). */ void heap_raw_insert(struct heap *heap, struct heap_node *node, uint64_t priority) { if (heap->n >= heap->allocated) { heap->allocated = heap->n == 0 ? 1 : 2 * heap->n; heap->array = xrealloc(heap->array, (heap->allocated + 1) * sizeof *heap->array); } put_node(heap, node, ++heap->n); node->priority = priority; } /* Removes 'node' from 'heap', without maintaining the heap invariant. * * After this call, heap_max() will no longer necessarily return the maximum * value in the heap, and HEAP_FOR_EACH will no longer necessarily iterate in * heap level order, until the next call to heap_rebuild(heap). * * This takes time O(1). */ void heap_raw_remove(struct heap *heap, struct heap_node *node) { size_t i = node->idx; if (i < heap->n) { put_node(heap, heap->array[heap->n], i); } heap->n--; } /* Rebuilds 'heap' to restore the heap invariant following a series of one or * more calls to heap_raw_*() functions. (Otherwise this function need not be * called.) * * This takes time O(n) in the current size of the heap. */ void heap_rebuild(struct heap *heap) { size_t i; for (i = heap->n / 2; i >= 1; i--) { float_down(heap, i); } } static void put_node(struct heap *heap, struct heap_node *node, size_t i) { heap->array[i] = node; node->idx = i; } static void swap_nodes(struct heap *heap, size_t i, size_t j) { struct heap_node *old_i = heap->array[i]; struct heap_node *old_j = heap->array[j]; put_node(heap, old_j, i); put_node(heap, old_i, j); } static bool float_up(struct heap *heap, size_t i) { bool moved = false; size_t parent; for (; i > 1; i = parent) { parent = heap_parent__(i); if (heap->array[parent]->priority >= heap->array[i]->priority) { break; } swap_nodes(heap, parent, i); moved = true; } return moved; } static void float_down(struct heap *heap, size_t i) { while (!heap_is_leaf__(heap, i)) { size_t left = heap_left__(i); size_t right = heap_right__(i); size_t max = i; if (heap->array[left]->priority > heap->array[max]->priority) { max = left; } if (right <= heap->n && heap->array[right]->priority > heap->array[max]->priority) { max = right; } if (max == i) { break; } swap_nodes(heap, max, i); i = max; } } static void float_up_or_down(struct heap *heap, size_t i) { if (!float_up(heap, i)) { float_down(heap, i); } } openvswitch-2.5.9/lib/PaxHeaders.82075/connectivity.c0000644000000000000000000000013213534540071017274 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801424.681852689 openvswitch-2.5.9/lib/connectivity.c0000644000175000017500000000253013534540071020762 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "connectivity.h" #include "ovs-thread.h" #include "seq.h" static struct seq *connectivity_seq; /* Provides a global seq for connectivity changes. * * Connectivity monitoring modules should call seq_change() on the returned * object whenever the status of a port changes, whether the cause is local or * remote. * * Clients can seq_wait() on this object for changes to netdev flags, features, * ethernet addresses, carrier changes, and bfd/cfm/lacp/stp status. */ struct seq * connectivity_seq_get(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { connectivity_seq = seq_create(); ovsthread_once_done(&once); } return connectivity_seq; } openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-notifier.c0000644000000000000000000000013013534540071020035 xustar0030 mtime=1567801401.461681611 30 atime=1567801402.081686164 28 ctime=1567801424.9818549 openvswitch-2.5.9/lib/netlink-notifier.c0000644000175000017500000001441113534540071021526 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netlink-notifier.h" #include #include #include #include "coverage.h" #include "netlink.h" #include "netlink-socket.h" #include "ofpbuf.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netlink_notifier); COVERAGE_DEFINE(nln_changed); static void nln_report(struct nln *nln, void *change); struct nln { struct nl_sock *notify_sock; /* Netlink socket. */ struct ovs_list all_notifiers; /* All nln notifiers. */ bool has_run; /* Guard for run and wait functions. */ /* Passed in by nln_create(). */ int multicast_group; /* Multicast group we listen on. */ int protocol; /* Protocol passed to nl_sock_create(). */ nln_parse_func *parse; /* Message parsing function. */ void *change; /* Change passed to parse. */ }; struct nln_notifier { struct nln *nln; /* Parent nln. */ struct ovs_list node; nln_notify_func *cb; void *aux; }; /* Creates an nln handle which may be used to manage change notifications. The * created handle will listen for netlink messages on 'multicast_group' using * netlink protocol 'protocol' (e.g. NETLINK_ROUTE, NETLINK_GENERIC, ...). * Incoming messages will be parsed with 'parse' which will be passed 'change' * as an argument. */ struct nln * nln_create(int protocol, int multicast_group, nln_parse_func *parse, void *change) { struct nln *nln; nln = xzalloc(sizeof *nln); nln->notify_sock = NULL; nln->protocol = protocol; nln->multicast_group = multicast_group; nln->parse = parse; nln->change = change; nln->has_run = false; list_init(&nln->all_notifiers); return nln; } /* Destroys 'nln' by freeing any memory it has reserved and closing any sockets * it has opened. * * The caller is responsible for destroying any notifiers created by this * 'nln' before destroying 'nln'. */ void nln_destroy(struct nln *nln) { if (nln) { ovs_assert(list_is_empty(&nln->all_notifiers)); nl_sock_destroy(nln->notify_sock); free(nln); } } /* Registers 'cb' to be called with auxiliary data 'aux' with change * notifications. The notifier is stored in 'notifier', which the caller must * not modify or free. * * This is probably not the function you want. You should probably be using * message specific notifiers like rtnetlink_link_notifier_register(). * * Returns an initialized nln_notifier if successful, otherwise NULL. */ struct nln_notifier * nln_notifier_create(struct nln *nln, nln_notify_func *cb, void *aux) { struct nln_notifier *notifier; if (!nln->notify_sock) { struct nl_sock *sock; int error; error = nl_sock_create(nln->protocol, &sock); if (!error) { error = nl_sock_join_mcgroup(sock, nln->multicast_group); } if (error) { nl_sock_destroy(sock); VLOG_WARN("could not create netlink socket: %s", ovs_strerror(error)); return NULL; } nln->notify_sock = sock; } else { /* Catch up on notification work so that the new notifier won't * receive any stale notifications. */ nln_run(nln); } notifier = xmalloc(sizeof *notifier); list_push_back(&nln->all_notifiers, ¬ifier->node); notifier->cb = cb; notifier->aux = aux; notifier->nln = nln; return notifier; } /* Destroys 'notifier', which must have previously been created with * nln_notifier_register(). */ void nln_notifier_destroy(struct nln_notifier *notifier) { if (notifier) { struct nln *nln = notifier->nln; list_remove(¬ifier->node); if (list_is_empty(&nln->all_notifiers)) { nl_sock_destroy(nln->notify_sock); nln->notify_sock = NULL; } free(notifier); } } /* Calls all of the registered notifiers, passing along any as-yet-unreported * change events. */ void nln_run(struct nln *nln) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (!nln->notify_sock || nln->has_run) { return; } nln->has_run = true; for (;;) { uint64_t buf_stub[4096 / 8]; struct ofpbuf buf; int error; ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); error = nl_sock_recv(nln->notify_sock, &buf, false); if (!error) { if (nln->parse(&buf, nln->change)) { nln_report(nln, nln->change); } else { VLOG_WARN_RL(&rl, "received bad netlink message"); nln_report(nln, NULL); } ofpbuf_uninit(&buf); } else if (error == EAGAIN) { return; } else { if (error == ENOBUFS) { /* The socket buffer might be full, there could be too many * notifications, so it makes sense to call nln_report() */ nln_report(nln, NULL); VLOG_WARN_RL(&rl, "netlink receive buffer overflowed"); } else { VLOG_WARN_RL(&rl, "error reading netlink socket: %s", ovs_strerror(error)); } return; } } } /* Causes poll_block() to wake up when change notifications are ready. */ void nln_wait(struct nln *nln) { nln->has_run = false; if (nln->notify_sock) { nl_sock_wait(nln->notify_sock, POLLIN); } } static void nln_report(struct nln *nln, void *change) { struct nln_notifier *notifier; if (change) { COVERAGE_INC(nln_changed); } LIST_FOR_EACH (notifier, node, &nln->all_notifiers) { notifier->cb(change, notifier->aux); } } openvswitch-2.5.9/lib/PaxHeaders.82075/ssl-peer-ca-cert.man0000644000000000000000000000013113534540071020154 xustar0030 mtime=1567801401.593682581 30 atime=1567801402.097686281 29 ctime=1567801423.73784573 openvswitch-2.5.9/lib/ssl-peer-ca-cert.man0000644000175000017500000000133113534540071021641 0ustar00jpettitjpettit00000000000000.IP "\fB\-\-peer\-ca\-cert=\fIpeer-cacert.pem\fR" Specifies a PEM file that contains one or more additional certificates to send to SSL peers. \fIpeer-cacert.pem\fR should be the CA certificate used to sign \fB\*(PN\fR's own certificate, that is, the certificate specified on \fB\-c\fR or \fB\-\-certificate\fR. If \fB\*(PN\fR's certificate is self-signed, then \fB\-\-certificate\fR and \fB\-\-peer\-ca\-cert\fR should specify the same file. .IP This option is not useful in normal operation, because the SSL peer must already have the CA certificate for the peer to have any confidence in \fB\*(PN\fR's identity. However, this offers a way for a new installation to bootstrap the CA certificate on its first SSL connection. openvswitch-2.5.9/lib/PaxHeaders.82075/type-props.h0000644000000000000000000000013213534540071016705 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.925854488 openvswitch-2.5.9/lib/type-props.h0000644000175000017500000000363613534540071020403 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2011, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TYPE_PROPS_H #define TYPE_PROPS_H 1 #include /* True if TYPE is _Bool, false otherwise. */ #define TYPE_IS_BOOL(TYPE) ((TYPE) 1 == (TYPE) 2) /* True if TYPE is an integer type (including _Bool), false if it is a * floating-point type. */ #define TYPE_IS_INTEGER(TYPE) ((TYPE) 1.5 == (TYPE) 1) /* True if TYPE is a signed integer or floating point type, otherwise false. */ #define TYPE_IS_SIGNED(TYPE) ((TYPE) 1 > (TYPE) -1) /* The number of value bits in an signed or unsigned integer TYPE: * * - _Bool has 1 value bit. * * - An N-bit unsigned integer type has N value bits. * * - An N-bit signed integer type has N-1 value bits. */ #define TYPE_VALUE_BITS(TYPE) \ (TYPE_IS_BOOL(TYPE) ? 1 : sizeof(TYPE) * CHAR_BIT - TYPE_IS_SIGNED(TYPE)) /* The minimum or maximum value of a signed or unsigned integer TYPE. */ #define TYPE_MINIMUM(TYPE) (TYPE_IS_SIGNED(TYPE) ? -TYPE_MAXIMUM(TYPE) - 1 : 0) #define TYPE_MAXIMUM(TYPE) \ ((((TYPE)1 << (TYPE_VALUE_BITS(TYPE) - 1)) - 1) * 2 + 1) /* Number of decimal digits required to format an integer of the given TYPE. * Includes space for a sign, if TYPE is signed, but not for a null * terminator. * * The value is an overestimate. */ #define INT_STRLEN(TYPE) (TYPE_IS_SIGNED(TYPE) + TYPE_VALUE_BITS(TYPE) / 3 + 1) #endif /* type-props.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/classifier-private.h0000644000000000000000000000013213534540071020357 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 30 ctime=1567801424.673852631 openvswitch-2.5.9/lib/classifier-private.h0000644000175000017500000003375413534540071022061 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CLASSIFIER_PRIVATE_H #define CLASSIFIER_PRIVATE_H 1 #include "cmap.h" #include "flow.h" #include "hash.h" #include "rculist.h" /* Classifier internal definitions, subject to change at any time. */ /* A set of rules that all have the same fields wildcarded. */ struct cls_subtable { struct cmap_node cmap_node; /* Within classifier's 'subtables_map'. */ /* These fields are only used by writers. */ int max_priority; /* Max priority of any rule in subtable. */ unsigned int max_count; /* Count of max_priority rules. */ /* Accessed by iterators. */ struct rculist rules_list; /* Unordered. */ /* Identical, but lower priority rules are not inserted to any of the * following data structures. */ /* These fields are accessed by readers who care about wildcarding. */ const uint8_t n_indices; /* How many indices to use. */ const struct flowmap index_maps[CLS_MAX_INDICES + 1]; /* Stage maps. */ unsigned int trie_plen[CLS_MAX_TRIES]; /* Trie prefix length in 'mask' * (runtime configurable). */ const int ports_mask_len; struct cmap indices[CLS_MAX_INDICES]; /* Staged lookup indices. */ rcu_trie_ptr ports_trie; /* NULL if none. */ /* These fields are accessed by all readers. */ struct cmap rules; /* Contains 'cls_match'es. */ const struct minimask mask; /* Wildcards for fields. */ /* 'mask' must be the last field. */ }; /* Internal representation of a rule in a "struct cls_subtable". * * The 'next' member is an element in a singly linked, null-terminated list. * This list links together identical "cls_match"es in order of decreasing * priority. The classifier code maintains the invariant that at most one rule * of a given priority is visible for any given lookup version. */ struct cls_match { /* Accessed by everybody. */ OVSRCU_TYPE(struct cls_match *) next; /* Equal, lower-priority matches. */ OVSRCU_TYPE(struct cls_conjunction_set *) conj_set; /* Accessed by readers interested in wildcarding. */ const int priority; /* Larger numbers are higher priorities. */ struct cmap_node index_nodes[CLS_MAX_INDICES]; /* Within subtable's * 'indices'. */ /* Accessed by all readers. */ struct cmap_node cmap_node; /* Within struct cls_subtable 'rules'. */ /* Rule versioning. * * CLS_NOT_REMOVED_VERSION has a special meaning for 'remove_version', * meaning that the rule has been added but not yet removed. */ const cls_version_t add_version; /* Version rule was added in. */ ATOMIC(cls_version_t) remove_version; /* Version rule is removed in. */ const struct cls_rule *cls_rule; const struct miniflow flow; /* Matching rule. Mask is in the subtable. */ /* 'flow' must be the last field. */ }; /* Utilities for accessing the 'cls_match' member of struct cls_rule. */ static inline struct cls_match * get_cls_match_protected(const struct cls_rule *rule) { return ovsrcu_get_protected(struct cls_match *, &rule->cls_match); } static inline struct cls_match * get_cls_match(const struct cls_rule *rule) { return ovsrcu_get(struct cls_match *, &rule->cls_match); } /* Must be RCU postponed. */ void cls_match_free_cb(struct cls_match *); static inline void cls_match_set_remove_version(struct cls_match *rule, cls_version_t version) { atomic_store_relaxed(&rule->remove_version, version); } static inline bool cls_match_visible_in_version(const struct cls_match *rule, cls_version_t version) { cls_version_t remove_version; /* C11 does not want to access an atomic via a const object pointer. */ atomic_read_relaxed(&CONST_CAST(struct cls_match *, rule)->remove_version, &remove_version); return rule->add_version <= version && version < remove_version; } static inline bool cls_match_is_eventually_invisible(const struct cls_match *rule) { cls_version_t remove_version; /* C11 does not want to access an atomic via a const object pointer. */ atomic_read_relaxed(&CONST_CAST(struct cls_match *, rule)->remove_version, &remove_version); return remove_version <= CLS_MAX_VERSION; } /* cls_match 'next' */ static inline const struct cls_match * cls_match_next(const struct cls_match *rule) { return ovsrcu_get(struct cls_match *, &rule->next); } static inline struct cls_match * cls_match_next_protected(const struct cls_match *rule) { return ovsrcu_get_protected(struct cls_match *, &rule->next); } /* Puts 'rule' in the position between 'prev' and 'next'. If 'prev' == NULL, * then the 'rule' is the new list head, and if 'next' == NULL, the rule is the * new list tail. * If there are any nodes between 'prev' and 'next', they are dropped from the * list. */ static inline void cls_match_insert(struct cls_match *prev, struct cls_match *next, struct cls_match *rule) { ovsrcu_set_hidden(&rule->next, next); if (prev) { ovsrcu_set(&prev->next, rule); } } /* Puts 'new_rule' in the position of 'old_rule', which is the next node after * 'prev'. If 'prev' == NULL, then the 'new_rule' is the new list head. * * The replaced cls_match still links to the later rules, and may still be * referenced by other threads until all other threads quiesce. The replaced * rule may not be re-inserted, re-initialized, or deleted until after all * other threads have quiesced (use ovsrcu_postpone). */ static inline void cls_match_replace(struct cls_match *prev, struct cls_match *old_rule, struct cls_match *new_rule) { cls_match_insert(prev, cls_match_next_protected(old_rule), new_rule); } /* Removes 'rule' following 'prev' from the list. If 'prev' is NULL, then the * 'rule' is a list head, and the caller is responsible for maintaining its * list head pointer (if any). * * Afterward, the removed rule is not linked to any more, but still links to * the following rules, and may still be referenced by other threads until all * other threads quiesce. The removed rule may not be re-inserted, * re-initialized, or deleted until after all other threads have quiesced (use * ovsrcu_postpone). */ static inline void cls_match_remove(struct cls_match *prev, struct cls_match *rule) { if (prev) { ovsrcu_set(&prev->next, cls_match_next_protected(rule)); } } #define CLS_MATCH_FOR_EACH(ITER, HEAD) \ for ((ITER) = (HEAD); (ITER); (ITER) = cls_match_next(ITER)) #define CLS_MATCH_FOR_EACH_AFTER_HEAD(ITER, HEAD) \ CLS_MATCH_FOR_EACH(ITER, cls_match_next(HEAD)) /* Iterate cls_matches keeping the previous pointer for modifications. */ #define FOR_EACH_RULE_IN_LIST_PROTECTED(ITER, PREV, HEAD) \ for ((PREV) = NULL, (ITER) = (HEAD); \ (ITER); \ (PREV) = (ITER), (ITER) = cls_match_next_protected(ITER)) /* A longest-prefix match tree. */ struct trie_node { uint32_t prefix; /* Prefix bits for this node, MSB first. */ uint8_t n_bits; /* Never zero, except for the root node. */ unsigned int n_rules; /* Number of rules that have this prefix. */ rcu_trie_ptr edges[2]; /* Both NULL if leaf. */ }; /* Max bits per node. Must fit in struct trie_node's 'prefix'. * Also tested with 16, 8, and 5 to stress the implementation. */ #define TRIE_PREFIX_BITS 32 /* flow/miniflow/minimask/minimatch utilities. * These are only used by the classifier, so place them here to allow * for better optimization. */ /* Returns a hash value for the bits of 'flow' where there are 1-bits in * 'mask', given 'basis'. * * The hash values returned by this function are the same as those returned by * miniflow_hash_in_minimask(), only the form of the arguments differ. */ static inline uint32_t flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask, uint32_t basis) { const uint64_t *mask_values = miniflow_get_values(&mask->masks); const uint64_t *flow_u64 = (const uint64_t *)flow; const uint64_t *p = mask_values; uint32_t hash = basis; map_t map; FLOWMAP_FOR_EACH_MAP (map, mask->masks.map) { size_t idx; MAP_FOR_EACH_INDEX (idx, map) { hash = hash_add64(hash, flow_u64[idx] & *p++); } flow_u64 += MAP_T_BITS; } return hash_finish(hash, (p - mask_values) * 8); } /* Returns a hash value for the bits of 'flow' where there are 1-bits in * 'mask', given 'basis'. * * The hash values returned by this function are the same as those returned by * flow_hash_in_minimask(), only the form of the arguments differ. */ static inline uint32_t miniflow_hash_in_minimask(const struct miniflow *flow, const struct minimask *mask, uint32_t basis) { const uint64_t *mask_values = miniflow_get_values(&mask->masks); const uint64_t *p = mask_values; uint32_t hash = basis; uint64_t value; MINIFLOW_FOR_EACH_IN_FLOWMAP(value, flow, mask->masks.map) { hash = hash_add64(hash, value & *p++); } return hash_finish(hash, (p - mask_values) * 8); } /* Returns a hash value for the values of 'flow', indicated by 'range', where * there are 1-bits in 'mask', given 'basis'. 'range' must be a continuous * subset of the bits in 'mask''s map, representing a continuous range of the * minimask's mask data. '*offset' must be the number of 64-bit units of the * minimask's data to skip to get to the first unit covered by 'range'. On * return '*offset' is updated with the number of 64-bit units of the minimask * consumed. * * Typically this function is called for successive ranges of minimask's masks, * and the first invocation passes '*offset' as zero. * * The hash values returned by this function are the same as those returned by * minimatch_hash_range(), only the form of the arguments differ. */ static inline uint32_t flow_hash_in_minimask_range(const struct flow *flow, const struct minimask *mask, const struct flowmap range, unsigned int *offset, uint32_t *basis) { const uint64_t *mask_values = miniflow_get_values(&mask->masks); const uint64_t *flow_u64 = (const uint64_t *)flow; const uint64_t *p = mask_values + *offset; uint32_t hash = *basis; map_t map; FLOWMAP_FOR_EACH_MAP (map, range) { size_t idx; MAP_FOR_EACH_INDEX (idx, map) { hash = hash_add64(hash, flow_u64[idx] & *p++); } flow_u64 += MAP_T_BITS; } *basis = hash; /* Allow continuation from the unfinished value. */ *offset = p - mask_values; return hash_finish(hash, *offset * 8); } /* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask. */ static inline void flow_wildcards_fold_minimask(struct flow_wildcards *wc, const struct minimask *mask) { flow_union_with_miniflow(&wc->masks, &mask->masks); } /* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask for bits in * 'fmap'. 1-bits in 'fmap' are a subset of 1-bits in 'mask''s map. */ static inline void flow_wildcards_fold_minimask_in_map(struct flow_wildcards *wc, const struct minimask *mask, const struct flowmap fmap) { flow_union_with_miniflow_subset(&wc->masks, &mask->masks, fmap); } /* Returns a hash value for 'mask', given 'basis'. */ static inline uint32_t minimask_hash(const struct minimask *mask, uint32_t basis) { const uint64_t *p = miniflow_get_values(&mask->masks); size_t n_values = miniflow_n_values(&mask->masks); uint32_t hash = basis; for (size_t i = 0; i < n_values; i++) { hash = hash_add64(hash, *p++); } map_t map; FLOWMAP_FOR_EACH_MAP (map, mask->masks.map) { hash = hash_add64(hash, map); } return hash_finish(hash, n_values); } /* Returns a hash value for the values of 'match->flow', indicated by 'range', * where there are 1-bits in 'match->mask', given 'basis'. 'range' must be a * continuous subset of the bits in the map of 'match', representing a * continuous range of the mask data of 'match'. '*offset' must be the number * of 64-bit units of the match data to skip to get to the first unit covered * by 'range'. On return '*offset' is updated with the number of 64-bit units * of the match consumed. * * Typically this function is called for successive ranges of minimask's masks, * and the first invocation passes '*offset' as zero. * * The hash values returned by this function are the same as those returned by * flow_hash_in_minimask_range(), only the form of the arguments differ. */ static inline uint32_t minimatch_hash_range(const struct minimatch *match, const struct flowmap range, unsigned int *offset, uint32_t *basis) { const uint64_t *p = miniflow_get_values(match->flow) + *offset; const uint64_t *q = miniflow_get_values(&match->mask->masks) + *offset; unsigned int n = flowmap_n_1bits(range); uint32_t hash = *basis; for (unsigned int i = 0; i < n; i++) { hash = hash_add64(hash, p[i] & q[i]); } *basis = hash; /* Allow continuation from the unfinished value. */ *offset += n; return hash_finish(hash, *offset * 8); } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/bundle.c0000644000000000000000000000013213534540071016027 xustar0030 mtime=1567801401.317680553 30 atime=1567801402.069686075 30 ctime=1567801424.665852571 openvswitch-2.5.9/lib/bundle.c0000644000175000017500000002267713534540071017533 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "bundle.h" #include #include #include "dynamic-string.h" #include "multipath.h" #include "meta-flow.h" #include "nx-match.h" #include "ofpbuf.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/nicira-ext.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(bundle); static ofp_port_t execute_ab(const struct ofpact_bundle *bundle, bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux) { size_t i; for (i = 0; i < bundle->n_slaves; i++) { ofp_port_t slave = bundle->slaves[i]; if (slave_enabled(slave, aux)) { return slave; } } return OFPP_NONE; } static ofp_port_t execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow, struct flow_wildcards *wc, bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux) { uint32_t flow_hash, best_hash; int best, i; if (bundle->n_slaves > 1) { flow_mask_hash_fields(flow, wc, bundle->fields); } flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis); best = -1; best_hash = 0; for (i = 0; i < bundle->n_slaves; i++) { if (slave_enabled(bundle->slaves[i], aux)) { uint32_t hash = hash_2words(i, flow_hash); if (best < 0 || hash > best_hash) { best_hash = hash; best = i; } } } return best >= 0 ? bundle->slaves[best] : OFPP_NONE; } /* Executes 'bundle' on 'flow'. Sets fields in 'wc' that were used to * calculate the result. Uses 'slave_enabled' to determine if the slave * designated by 'ofp_port' is up. Returns the chosen slave, or * OFPP_NONE if none of the slaves are acceptable. */ ofp_port_t bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow, struct flow_wildcards *wc, bool (*slave_enabled)(ofp_port_t ofp_port, void *aux), void *aux) { switch (bundle->algorithm) { case NX_BD_ALG_HRW: return execute_hrw(bundle, flow, wc, slave_enabled, aux); case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(bundle, slave_enabled, aux); default: OVS_NOT_REACHED(); } } enum ofperr bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports, const struct flow *flow) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); size_t i; if (bundle->dst.field) { enum ofperr error = mf_check_dst(&bundle->dst, flow); if (error) { return error; } } for (i = 0; i < bundle->n_slaves; i++) { ofp_port_t ofp_port = bundle->slaves[i]; if (ofp_port != OFPP_NONE) { enum ofperr error = ofpact_check_output_port(ofp_port, max_ports); if (error) { VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port); return error; } } /* Controller slaves are unsupported due to the lack of a max_len * argument. This may or may not change in the future. There doesn't * seem to be a real-world use-case for supporting it. */ if (ofp_port == OFPP_CONTROLLER) { VLOG_WARN_RL(&rl, "unsupported controller slave"); return OFPERR_OFPBAC_BAD_OUT_PORT; } } return 0; } /* Helper for bundle_parse and bundle_parse_load. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string.*/ static char * OVS_WARN_UNUSED_RESULT bundle_parse__(const char *s, char **save_ptr, const char *fields, const char *basis, const char *algorithm, const char *slave_type, const char *dst, const char *slave_delim, struct ofpbuf *ofpacts) { struct ofpact_bundle *bundle; if (!slave_delim) { return xasprintf("%s: not enough arguments to bundle action", s); } if (strcasecmp(slave_delim, "slaves")) { return xasprintf("%s: missing slave delimiter, expected `slaves' " "got `%s'", s, slave_delim); } bundle = ofpact_put_BUNDLE(ofpacts); for (;;) { ofp_port_t slave_port; char *slave; slave = strtok_r(NULL, ", []", save_ptr); if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { break; } if (!ofputil_port_from_string(slave, &slave_port)) { return xasprintf("%s: bad port number", slave); } ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); bundle = ofpacts->header; bundle->n_slaves++; } ofpact_update_len(ofpacts, &bundle->ofpact); bundle->basis = atoi(basis); if (!strcasecmp(fields, "eth_src")) { bundle->fields = NX_HASH_FIELDS_ETH_SRC; } else if (!strcasecmp(fields, "symmetric_l4")) { bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; } else if (!strcasecmp(fields, "symmetric_l3l4")) { bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4; } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) { bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP; } else { return xasprintf("%s: unknown fields `%s'", s, fields); } if (!strcasecmp(algorithm, "active_backup")) { bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; } else if (!strcasecmp(algorithm, "hrw")) { bundle->algorithm = NX_BD_ALG_HRW; } else { return xasprintf("%s: unknown algorithm `%s'", s, algorithm); } if (strcasecmp(slave_type, "ofport")) { return xasprintf("%s: unknown slave_type `%s'", s, slave_type); } if (dst) { char *error = mf_parse_subfield(&bundle->dst, dst); if (error) { return error; } if (!mf_nxm_header(bundle->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", s, dst); } } return NULL; } /* Converts a bundle action string contained in 's' to an nx_action_bundle and * stores it in 'b'. Sets 'b''s l2 pointer to NULL. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ char * OVS_WARN_UNUSED_RESULT bundle_parse(const char *s, struct ofpbuf *ofpacts) { char *fields, *basis, *algorithm, *slave_type, *slave_delim; char *tokstr, *save_ptr; char *error; save_ptr = NULL; tokstr = xstrdup(s); fields = strtok_r(tokstr, ", ", &save_ptr); basis = strtok_r(NULL, ", ", &save_ptr); algorithm = strtok_r(NULL, ", ", &save_ptr); slave_type = strtok_r(NULL, ", ", &save_ptr); slave_delim = strtok_r(NULL, ": ", &save_ptr); error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL, slave_delim, ofpacts); free(tokstr); return error; } /* Converts a bundle_load action string contained in 's' to an nx_action_bundle * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. * * Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string.*/ char * OVS_WARN_UNUSED_RESULT bundle_parse_load(const char *s, struct ofpbuf *ofpacts) { char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; char *tokstr, *save_ptr; char *error; save_ptr = NULL; tokstr = xstrdup(s); fields = strtok_r(tokstr, ", ", &save_ptr); basis = strtok_r(NULL, ", ", &save_ptr); algorithm = strtok_r(NULL, ", ", &save_ptr); slave_type = strtok_r(NULL, ", ", &save_ptr); dst = strtok_r(NULL, ", ", &save_ptr); slave_delim = strtok_r(NULL, ": ", &save_ptr); error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst, slave_delim, ofpacts); free(tokstr); return error; } /* Appends a human-readable representation of 'nab' to 's'. */ void bundle_format(const struct ofpact_bundle *bundle, struct ds *s) { const char *action, *fields, *algorithm; size_t i; fields = flow_hash_fields_to_str(bundle->fields); switch (bundle->algorithm) { case NX_BD_ALG_HRW: algorithm = "hrw"; break; case NX_BD_ALG_ACTIVE_BACKUP: algorithm = "active_backup"; break; default: algorithm = ""; } action = bundle->dst.field ? "bundle_load" : "bundle"; ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields, bundle->basis, algorithm, "ofport"); if (bundle->dst.field) { mf_format_subfield(&bundle->dst, s); ds_put_cstr(s, ","); } ds_put_cstr(s, "slaves:"); for (i = 0; i < bundle->n_slaves; i++) { if (i) { ds_put_cstr(s, ","); } ofputil_format_port(bundle->slaves[i], s); } ds_put_cstr(s, ")"); } openvswitch-2.5.9/lib/PaxHeaders.82075/unixctl.h0000644000000000000000000000013213534540071016251 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.929854517 openvswitch-2.5.9/lib/unixctl.h0000644000175000017500000000357113534540071017745 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UNIXCTL_H #define UNIXCTL_H 1 #ifdef __cplusplus extern "C" { #endif /* Server for Unix domain socket control connection. */ struct unixctl_server; int unixctl_server_create(const char *path, struct unixctl_server **); void unixctl_server_run(struct unixctl_server *); void unixctl_server_wait(struct unixctl_server *); void unixctl_server_destroy(struct unixctl_server *); /* Client for Unix domain socket control connection. */ struct jsonrpc; int unixctl_client_create(const char *path, struct jsonrpc **client); int unixctl_client_transact(struct jsonrpc *client, const char *command, int argc, char *argv[], char **result, char **error); /* Command registration. */ struct unixctl_conn; typedef void unixctl_cb_func(struct unixctl_conn *, int argc, const char *argv[], void *aux); void unixctl_command_register(const char *name, const char *usage, int min_args, int max_args, unixctl_cb_func *cb, void *aux); void unixctl_command_reply_error(struct unixctl_conn *, const char *error); void unixctl_command_reply(struct unixctl_conn *, const char *body); #ifdef __cplusplus } #endif #endif /* unixctl.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sflow_api.h0000644000000000000000000000013113534540071016545 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801425.009855106 openvswitch-2.5.9/lib/sflow_api.h0000644000175000017500000003225313534540071020241 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #ifndef SFLOW_API_H #define SFLOW_API_H 1 /* define SFLOW_DO_SOCKET to 1 if you want the agent to send the packets itself, otherwise set the sendFn callback in sfl_agent_init.*/ /* #define SFLOW_DO_SOCKET */ #include #include #include #include #include #include #include /* for htonl */ #ifdef SFLOW_DO_SOCKET #include #include #include #include #endif #include "sflow.h" /* define SFLOW_SOFTWARE_SAMPLING to 1 if you need to use the sfl_sampler_takeSample routine and give it every packet */ /* #define SFLOW_SOFTWARE_SAMPLING */ /* uncomment this preprocessor flag (or compile with -DSFL_USE_32BIT_INDEX) if your ds_index numbers can ever be >= 2^30-1 (i.e. >= 0x3FFFFFFF) */ /* #define SFL_USE_32BIT_INDEX */ /* Used to combine ds_class, ds_index and instance into a single 64-bit number like this: __________________________________ | cls| index | instance | ---------------------------------- but now is opened up to a 12-byte struct to ensure that ds_index has a full 32-bit field, and to make accessing the components simpler. The macros have the same behavior as before, so this change should be transparent. The only difference is that these objects are now passed around by reference instead of by value, and the comparison is done using a fn. */ typedef struct _SFLDataSource_instance { u_int32_t ds_class; u_int32_t ds_index; u_int32_t ds_instance; } SFLDataSource_instance; #ifdef SFL_USE_32BIT_INDEX #define SFL_FLOW_SAMPLE_TYPE SFLFlow_sample_expanded #define SFL_COUNTERS_SAMPLE_TYPE SFLCounters_sample_expanded #else #define SFL_FLOW_SAMPLE_TYPE SFLFlow_sample #define SFL_COUNTERS_SAMPLE_TYPE SFLCounters_sample /* if index numbers are not going to use all 32 bits, then we can use the more compact encoding, with the dataSource class and index merged */ #define SFL_DS_DATASOURCE(dsi) (((dsi).ds_class << 24) + (dsi).ds_index) #endif #define SFL_DS_INSTANCE(dsi) (dsi).ds_instance #define SFL_DS_CLASS(dsi) (dsi).ds_class #define SFL_DS_INDEX(dsi) (dsi).ds_index #define SFL_DS_SET(dsi,clss,indx,inst) \ do { \ (dsi).ds_class = (clss); \ (dsi).ds_index = (indx); \ (dsi).ds_instance = (inst); \ } while(0) typedef struct _SFLSampleCollector { u_int32_t data[(SFL_MAX_DATAGRAM_SIZE + SFL_DATA_PAD) / sizeof(u_int32_t)]; u_int32_t *datap; /* packet fill pointer */ u_int32_t pktlen; /* accumulated size */ u_int32_t packetSeqNo; u_int32_t numSamples; } SFLSampleCollector; struct _SFLAgent; /* forward decl */ typedef struct _SFLReceiver { struct _SFLReceiver *nxt; /* MIB fields */ char *sFlowRcvrOwner; time_t sFlowRcvrTimeout; u_int32_t sFlowRcvrMaximumDatagramSize; SFLAddress sFlowRcvrAddress; u_int32_t sFlowRcvrPort; u_int32_t sFlowRcvrDatagramVersion; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ /* private fields */ SFLSampleCollector sampleCollector; #ifdef SFLOW_DO_SOCKET struct sockaddr_in receiver4; struct sockaddr_in6 receiver6; #endif } SFLReceiver; typedef struct _SFLSampler { /* for linked list */ struct _SFLSampler *nxt; /* for hash lookup table */ struct _SFLSampler *hash_nxt; /* MIB fields */ SFLDataSource_instance dsi; u_int32_t sFlowFsReceiver; u_int32_t sFlowFsPacketSamplingRate; u_int32_t sFlowFsMaximumHeaderSize; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ /* private fields */ SFLReceiver *myReceiver; u_int32_t skip; u_int32_t samplePool; u_int32_t flowSampleSeqNo; /* rate checking */ u_int32_t samplesThisTick; u_int32_t samplesLastTick; u_int32_t backoffThreshold; } SFLSampler; /* declare */ struct _SFLPoller; typedef void (*getCountersFn_t)(void *magic, /* callback to get counters */ struct _SFLPoller *sampler, /* called with self */ SFL_COUNTERS_SAMPLE_TYPE *cs); /* struct to fill in */ typedef struct _SFLPoller { /* for linked list */ struct _SFLPoller *nxt; /* MIB fields */ SFLDataSource_instance dsi; u_int32_t sFlowCpReceiver; time_t sFlowCpInterval; /* public fields */ struct _SFLAgent *agent; /* pointer to my agent */ void *magic; /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn; u_int32_t bridgePort; /* port number local to bridge */ /* private fields */ SFLReceiver *myReceiver; time_t countersCountdown; u_int32_t countersSampleSeqNo; } SFLPoller; typedef void *(*allocFn_t)(void *magic, /* callback to allocate space on heap */ struct _SFLAgent *agent, /* called with self */ size_t bytes); /* bytes requested */ typedef int (*freeFn_t)(void *magic, /* callback to free space on heap */ struct _SFLAgent *agent, /* called with self */ void *obj); /* obj to free */ typedef void (*errorFn_t)(void *magic, /* callback to log error message */ struct _SFLAgent *agent, /* called with self */ char *msg); /* error message */ typedef void (*sendFn_t)(void *magic, /* optional override fn to send packet */ struct _SFLAgent *agent, SFLReceiver *receiver, u_char *pkt, u_int32_t pktLen); /* prime numbers are good for hash tables */ #define SFL_HASHTABLE_SIZ 199 typedef struct _SFLAgent { SFLSampler *jumpTable[SFL_HASHTABLE_SIZ]; /* fast lookup table for samplers (by ifIndex) */ SFLSampler *samplers; /* the list of samplers */ SFLPoller *pollers; /* the list of samplers */ SFLReceiver *receivers; /* the array of receivers */ time_t bootTime; /* time when we booted or started */ time_t now; /* time now */ SFLAddress myIP; /* IP address of this node */ u_int32_t subId; /* sub_agent_id */ void *magic; /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn; freeFn_t freeFn; errorFn_t errorFn; sendFn_t sendFn; #ifdef SFLOW_DO_SOCKET int receiverSocket4; int receiverSocket6; #endif } SFLAgent; /* call this at the start with a newly created agent */ void sfl_agent_init(SFLAgent *agent, SFLAddress *myIP, /* IP address of this agent */ u_int32_t subId, /* agent_sub_id */ time_t bootTime, /* agent boot time */ time_t now, /* time now */ void *magic, /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn, freeFn_t freeFn, errorFn_t errorFn, sendFn_t sendFn); /* call this to create samplers */ SFLSampler *sfl_agent_addSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); /* call this to create pollers */ SFLPoller *sfl_agent_addPoller(SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn); /* call this to create receivers */ SFLReceiver *sfl_agent_addReceiver(SFLAgent *agent); /* call this to remove samplers */ int sfl_agent_removeSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); /* call this to remove pollers */ int sfl_agent_removePoller(SFLAgent *agent, SFLDataSource_instance *pdsi); /* note: receivers should not be removed. Typically the receivers list will be created at init time and never changed */ /* call these fns to retrieve sampler, poller or receiver (e.g. for SNMP GET or GETNEXT operation) */ SFLSampler *sfl_agent_getSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLSampler *sfl_agent_getNextSampler(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLPoller *sfl_agent_getPoller(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLPoller *sfl_agent_getNextPoller(SFLAgent *agent, SFLDataSource_instance *pdsi); SFLReceiver *sfl_agent_getReceiver(SFLAgent *agent, u_int32_t receiverIndex); SFLReceiver *sfl_agent_getNextReceiver(SFLAgent *agent, u_int32_t receiverIndex); /* jump table access - for performance */ SFLSampler *sfl_agent_getSamplerByIfIndex(SFLAgent *agent, u_int32_t ifIndex); /* call these functions to GET and SET MIB values */ /* receiver */ char * sfl_receiver_get_sFlowRcvrOwner(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrOwner(SFLReceiver *receiver, char *sFlowRcvrOwner); time_t sfl_receiver_get_sFlowRcvrTimeout(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrTimeout(SFLReceiver *receiver, time_t sFlowRcvrTimeout); u_int32_t sfl_receiver_get_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrMaximumDatagramSize(SFLReceiver *receiver, u_int32_t sFlowRcvrMaximumDatagramSize); SFLAddress *sfl_receiver_get_sFlowRcvrAddress(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrAddress(SFLReceiver *receiver, SFLAddress *sFlowRcvrAddress); u_int32_t sfl_receiver_get_sFlowRcvrPort(SFLReceiver *receiver); void sfl_receiver_set_sFlowRcvrPort(SFLReceiver *receiver, u_int32_t sFlowRcvrPort); /* sampler */ u_int32_t sfl_sampler_get_sFlowFsReceiver(SFLSampler *sampler); void sfl_sampler_set_sFlowFsReceiver(SFLSampler *sampler, u_int32_t sFlowFsReceiver); u_int32_t sfl_sampler_get_sFlowFsPacketSamplingRate(SFLSampler *sampler); void sfl_sampler_set_sFlowFsPacketSamplingRate(SFLSampler *sampler, u_int32_t sFlowFsPacketSamplingRate); u_int32_t sfl_sampler_get_sFlowFsMaximumHeaderSize(SFLSampler *sampler); void sfl_sampler_set_sFlowFsMaximumHeaderSize(SFLSampler *sampler, u_int32_t sFlowFsMaximumHeaderSize); u_int32_t sfl_sampler_get_samplesLastTick(SFLSampler *sampler); /* poller */ u_int32_t sfl_poller_get_sFlowCpReceiver(SFLPoller *poller); void sfl_poller_set_sFlowCpReceiver(SFLPoller *poller, u_int32_t sFlowCpReceiver); u_int32_t sfl_poller_get_sFlowCpInterval(SFLPoller *poller); void sfl_poller_set_sFlowCpInterval(SFLPoller *poller, u_int32_t sFlowCpInterval); /* fns to set the sflow agent address or sub-id */ void sfl_agent_set_agentAddress(SFLAgent *agent, SFLAddress *addr); void sfl_agent_set_agentSubId(SFLAgent *agent, u_int32_t subId); /* The poller may need a separate number to reference the local bridge port to get counters if it is not the same as the global ifIndex */ void sfl_poller_set_bridgePort(SFLPoller *poller, u_int32_t port_no); u_int32_t sfl_poller_get_bridgePort(SFLPoller *poller); SFLPoller *sfl_agent_getPollerByBridgePort(SFLAgent *agent, u_int32_t port_no); /* call this to indicate a discontinuity with a counter like samplePool so that the sflow collector will ignore the next delta */ void sfl_sampler_resetFlowSeqNo(SFLSampler *sampler); /* call this to indicate a discontinuity with one or more of the counters so that the sflow collector will ignore the next delta */ void sfl_poller_resetCountersSeqNo(SFLPoller *poller); #ifdef SFLOW_SOFTWARE_SAMLING /* software sampling: call this with every packet - returns non-zero if the packet should be sampled (in which case you then call sfl_sampler_writeFlowSample()) */ int sfl_sampler_takeSample(SFLSampler *sampler); #endif /* call this to set a maximum samples-per-second threshold. If the sampler reaches this threshold it will automatically back off the sampling rate. A value of 0 disables the mechanism */ void sfl_sampler_set_backoffThreshold(SFLSampler *sampler, u_int32_t samplesPerSecond); u_int32_t sfl_sampler_get_backoffThreshold(SFLSampler *sampler); /* call this once per second (N.B. not on interrupt stack i.e. not hard real-time) */ void sfl_agent_tick(SFLAgent *agent, time_t now); /* call this with each flow sample */ void sfl_sampler_writeFlowSample(SFLSampler *sampler, SFL_FLOW_SAMPLE_TYPE *fs); /* call this to push counters samples (usually done in the getCountersFn callback) */ void sfl_poller_writeCountersSample(SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs); /* call this to deallocate resources */ void sfl_agent_release(SFLAgent *agent); /* internal fns */ void sfl_receiver_init(SFLReceiver *receiver, SFLAgent *agent); void sfl_sampler_init(SFLSampler *sampler, SFLAgent *agent, SFLDataSource_instance *pdsi); void sfl_poller_init(SFLPoller *poller, SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, getCountersFn_t getCountersFn); void sfl_receiver_tick(SFLReceiver *receiver, time_t now); void sfl_poller_tick(SFLPoller *poller, time_t now); void sfl_sampler_tick(SFLSampler *sampler, time_t now); int sfl_receiver_writeFlowSample(SFLReceiver *receiver, SFL_FLOW_SAMPLE_TYPE *fs); int sfl_receiver_writeCountersSample(SFLReceiver *receiver, SFL_COUNTERS_SAMPLE_TYPE *cs); void sfl_agent_resetReceiver(SFLAgent *agent, SFLReceiver *receiver); void sfl_agent_error(SFLAgent *agent, char *modName, char *msg); void sfl_agent_sysError(SFLAgent *agent, char *modName, char *msg); u_int32_t sfl_receiver_samplePacketsSent(SFLReceiver *receiver); #define SFL_ALLOC malloc #define SFL_FREE free #endif /* SFLOW_API_H */ openvswitch-2.5.9/lib/PaxHeaders.82075/async-append-null.c0000644000000000000000000000013213534540071020110 xustar0030 mtime=1567801401.313680523 30 atime=1567801402.065686047 30 ctime=1567801424.997855018 openvswitch-2.5.9/lib/async-append-null.c0000644000175000017500000000234313534540071021600 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include /* This is a null implementation of the asynchronous I/O interface for systems * that don't have a form of asynchronous I/O. */ #include "async-append.h" #include #include #include "util.h" struct async_append * async_append_create(int fd OVS_UNUSED) { return NULL; } void async_append_destroy(struct async_append *ap) { ovs_assert(ap == NULL); } void async_append_write(struct async_append *ap OVS_UNUSED, const void *data OVS_UNUSED, size_t size OVS_UNUSED) { OVS_NOT_REACHED(); } void async_append_flush(struct async_append *ap OVS_UNUSED) { OVS_NOT_REACHED(); } openvswitch-2.5.9/lib/PaxHeaders.82075/poll-loop.h0000644000000000000000000000013213534540071016500 xustar0030 mtime=1567801401.577682462 30 atime=1567801402.093686252 30 ctime=1567801424.853853956 openvswitch-2.5.9/lib/poll-loop.h0000644000175000017500000000534313534540071020173 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* High-level wrapper around the "poll" system call. * * The intended usage is for each thread's main loop to go about its business * servicing whatever events it needs to. Then, when it runs out of immediate * tasks, it calls each subordinate module's "wait" function, which in turn * calls one (or more) of the functions poll_fd_wait(), poll_immediate_wake(), * and poll_timer_wait() to register to be awakened when the appropriate event * occurs. Then the main loop calls poll_block(), which blocks until one of * the registered events happens. * * * Thread-safety * ============= * * The poll set is per-thread, so all functions in this module are thread-safe. */ #ifndef POLL_LOOP_H #define POLL_LOOP_H 1 #include #include "util.h" #ifdef __cplusplus extern "C" { #endif /* Schedule events to wake up the following poll_block(). * * The poll_loop logs the 'where' argument to each function at "debug" level * when an event causes a wakeup. Each of these ways to schedule an event has * a function and a macro wrapper. The macro version automatically supplies * the source code location of the caller. The function version allows the * caller to supply a location explicitly, which is useful if the caller's own * caller would be more useful in log output. See timer_wait_at() for an * example. */ void poll_fd_wait_at(int fd, short int events, const char *where); #define poll_fd_wait(fd, events) poll_fd_wait_at(fd, events, OVS_SOURCE_LOCATOR) #ifdef _WIN32 #define poll_wevent_wait(wevent) poll_wevent_wait_at(wevent, OVS_SOURCE_LOCATOR) #endif /* _WIN32 */ void poll_timer_wait_at(long long int msec, const char *where); #define poll_timer_wait(msec) poll_timer_wait_at(msec, OVS_SOURCE_LOCATOR) void poll_timer_wait_until_at(long long int msec, const char *where); #define poll_timer_wait_until(msec) \ poll_timer_wait_until_at(msec, OVS_SOURCE_LOCATOR) void poll_immediate_wake_at(const char *where); #define poll_immediate_wake() poll_immediate_wake_at(OVS_SOURCE_LOCATOR) /* Wait until an event occurs. */ void poll_block(void); #ifdef __cplusplus } #endif #endif /* poll-loop.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/signals.h0000644000000000000000000000013113534540071016222 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.969854812 openvswitch-2.5.9/lib/signals.h0000644000175000017500000000166113534540071017715 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2011, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef SIGNALS_H #define SIGNALS_H 1 #include #include #include "type-props.h" enum { SIGNAL_NAME_BUFSIZE = 7 + INT_STRLEN(int) + 1 }; const char *signal_name(int signum, char *namebuf, size_t bufsize); void xsigaction(int signum, const struct sigaction *, struct sigaction *old); #endif /* signals.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-idl.c0000644000000000000000000000013213534540071016441 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.837853839 openvswitch-2.5.9/lib/ovsdb-idl.c0000644000175000017500000030607113534540071020136 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb-idl.h" #include #include #include #include #include "bitmap.h" #include "coverage.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "json.h" #include "jsonrpc.h" #include "ovsdb/ovsdb.h" #include "ovsdb/table.h" #include "ovsdb-data.h" #include "ovsdb-error.h" #include "ovsdb-idl-provider.h" #include "ovsdb-parser.h" #include "poll-loop.h" #include "shash.h" #include "sset.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovsdb_idl); COVERAGE_DEFINE(txn_uncommitted); COVERAGE_DEFINE(txn_unchanged); COVERAGE_DEFINE(txn_incomplete); COVERAGE_DEFINE(txn_aborted); COVERAGE_DEFINE(txn_success); COVERAGE_DEFINE(txn_try_again); COVERAGE_DEFINE(txn_not_locked); COVERAGE_DEFINE(txn_error); /* An arc from one idl_row to another. When row A contains a UUID that * references row B, this is represented by an arc from A (the source) to B * (the destination). * * Arcs from a row to itself are omitted, that is, src and dst are always * different. * * Arcs are never duplicated, that is, even if there are multiple references * from A to B, there is only a single arc from A to B. * * Arcs are directed: an arc from A to B is the converse of an an arc from B to * A. Both an arc and its converse may both be present, if each row refers * to the other circularly. * * The source and destination row may be in the same table or in different * tables. */ struct ovsdb_idl_arc { struct ovs_list src_node; /* In src->src_arcs list. */ struct ovs_list dst_node; /* In dst->dst_arcs list. */ struct ovsdb_idl_row *src; /* Source row. */ struct ovsdb_idl_row *dst; /* Destination row. */ }; enum ovsdb_idl_state { IDL_S_SCHEMA_REQUESTED, IDL_S_MONITOR_REQUESTED, IDL_S_MONITORING }; struct ovsdb_idl { const struct ovsdb_idl_class *class; struct jsonrpc_session *session; struct shash table_by_name; struct ovsdb_idl_table *tables; /* Contains "struct ovsdb_idl_table *"s.*/ unsigned int change_seqno; bool verify_write_only; /* Session state. */ unsigned int state_seqno; enum ovsdb_idl_state state; struct json *request_id; /* Database locking. */ char *lock_name; /* Name of lock we need, NULL if none. */ bool has_lock; /* Has db server told us we have the lock? */ bool is_lock_contended; /* Has db server told us we can't get lock? */ struct json *lock_request_id; /* JSON-RPC ID of in-flight lock request. */ /* Transaction support. */ struct ovsdb_idl_txn *txn; struct hmap outstanding_txns; }; struct ovsdb_idl_txn { struct hmap_node hmap_node; struct json *request_id; struct ovsdb_idl *idl; struct hmap txn_rows; enum ovsdb_idl_txn_status status; char *error; bool dry_run; struct ds comment; /* Increments. */ const char *inc_table; const char *inc_column; struct uuid inc_row; unsigned int inc_index; int64_t inc_new_value; /* Inserted rows. */ struct hmap inserted_rows; /* Contains "struct ovsdb_idl_txn_insert"s. */ }; struct ovsdb_idl_txn_insert { struct hmap_node hmap_node; /* In struct ovsdb_idl_txn's inserted_rows. */ struct uuid dummy; /* Dummy UUID used locally. */ int op_index; /* Index into transaction's operation array. */ struct uuid real; /* Real UUID used by database server. */ }; static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5); static struct vlog_rate_limit semantic_rl = VLOG_RATE_LIMIT_INIT(1, 5); static void ovsdb_idl_clear(struct ovsdb_idl *); static void ovsdb_idl_send_schema_request(struct ovsdb_idl *); static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *, const struct json *schema_json); static void ovsdb_idl_parse_update(struct ovsdb_idl *, const struct json *); static struct ovsdb_error *ovsdb_idl_parse_update__(struct ovsdb_idl *, const struct json *); static bool ovsdb_idl_process_update(struct ovsdb_idl_table *, const struct uuid *, const struct json *old, const struct json *new); static void ovsdb_idl_insert_row(struct ovsdb_idl_row *, const struct json *); static void ovsdb_idl_delete_row(struct ovsdb_idl_row *); static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *, const struct json *); static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *); static struct ovsdb_idl_row *ovsdb_idl_row_create__( const struct ovsdb_idl_table_class *); static struct ovsdb_idl_row *ovsdb_idl_row_create(struct ovsdb_idl_table *, const struct uuid *); static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *); static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *); static void ovsdb_idl_row_parse(struct ovsdb_idl_row *); static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *); static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *); static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *); static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *, bool destroy_dsts); static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *); static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *, const struct jsonrpc_msg *msg); static void ovsdb_idl_send_lock_request(struct ovsdb_idl *); static void ovsdb_idl_send_unlock_request(struct ovsdb_idl *); static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *, const struct json *); static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *, const struct json *params, bool new_has_lock); static struct ovsdb_idl_table * ovsdb_idl_table_from_class(const struct ovsdb_idl *, const struct ovsdb_idl_table_class *); static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table); /* Creates and returns a connection to database 'remote', which should be in a * form acceptable to jsonrpc_session_open(). The connection will maintain an * in-memory replica of the remote database whose schema is described by * 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically * by ovsdb-idlc.) * * Passes 'retry' to jsonrpc_session_open(). See that function for * documentation. * * If 'monitor_everything_by_default' is true, then everything in the remote * database will be replicated by default. ovsdb_idl_omit() and * ovsdb_idl_omit_alert() may be used to selectively drop some columns from * monitoring. * * If 'monitor_everything_by_default' is false, then no columns or tables will * be replicated by default. ovsdb_idl_add_column() and ovsdb_idl_add_table() * must be used to choose some columns or tables to replicate. */ struct ovsdb_idl * ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class, bool monitor_everything_by_default, bool retry) { struct ovsdb_idl *idl; uint8_t default_mode; size_t i; default_mode = (monitor_everything_by_default ? OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT : 0); idl = xzalloc(sizeof *idl); idl->class = class; idl->session = jsonrpc_session_open(remote, retry); shash_init(&idl->table_by_name); idl->tables = xmalloc(class->n_tables * sizeof *idl->tables); for (i = 0; i < class->n_tables; i++) { const struct ovsdb_idl_table_class *tc = &class->tables[i]; struct ovsdb_idl_table *table = &idl->tables[i]; size_t j; shash_add_assert(&idl->table_by_name, tc->name, table); table->class = tc; table->modes = xmalloc(tc->n_columns); memset(table->modes, default_mode, tc->n_columns); table->need_table = false; shash_init(&table->columns); for (j = 0; j < tc->n_columns; j++) { const struct ovsdb_idl_column *column = &tc->columns[j]; shash_add_assert(&table->columns, column->name, column); } hmap_init(&table->rows); list_init(&table->track_list); table->change_seqno[OVSDB_IDL_CHANGE_INSERT] = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY] = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0; table->idl = idl; } idl->state_seqno = UINT_MAX; idl->request_id = NULL; hmap_init(&idl->outstanding_txns); return idl; } /* Destroys 'idl' and all of the data structures that it manages. */ void ovsdb_idl_destroy(struct ovsdb_idl *idl) { if (idl) { size_t i; ovs_assert(!idl->txn); ovsdb_idl_clear(idl); jsonrpc_session_close(idl->session); for (i = 0; i < idl->class->n_tables; i++) { struct ovsdb_idl_table *table = &idl->tables[i]; shash_destroy(&table->columns); hmap_destroy(&table->rows); free(table->modes); } shash_destroy(&idl->table_by_name); free(idl->tables); json_destroy(idl->request_id); free(idl->lock_name); json_destroy(idl->lock_request_id); hmap_destroy(&idl->outstanding_txns); free(idl); } } static void ovsdb_idl_clear(struct ovsdb_idl *idl) { bool changed = false; size_t i; for (i = 0; i < idl->class->n_tables; i++) { struct ovsdb_idl_table *table = &idl->tables[i]; struct ovsdb_idl_row *row, *next_row; if (hmap_is_empty(&table->rows)) { continue; } changed = true; HMAP_FOR_EACH_SAFE (row, next_row, hmap_node, &table->rows) { struct ovsdb_idl_arc *arc, *next_arc; if (!ovsdb_idl_row_is_orphan(row)) { ovsdb_idl_row_unparse(row); } LIST_FOR_EACH_SAFE (arc, next_arc, src_node, &row->src_arcs) { free(arc); } /* No need to do anything with dst_arcs: some node has those arcs * as forward arcs and will destroy them itself. */ if (!list_is_empty(&row->track_node)) { list_remove(&row->track_node); } ovsdb_idl_row_destroy(row); } } ovsdb_idl_track_clear(idl); if (changed) { idl->change_seqno++; } } /* Processes a batch of messages from the database server on 'idl'. This may * cause the IDL's contents to change. The client may check for that with * ovsdb_idl_get_seqno(). */ void ovsdb_idl_run(struct ovsdb_idl *idl) { int i; ovs_assert(!idl->txn); jsonrpc_session_run(idl->session); for (i = 0; jsonrpc_session_is_connected(idl->session) && i < 50; i++) { struct jsonrpc_msg *msg; unsigned int seqno; seqno = jsonrpc_session_get_seqno(idl->session); if (idl->state_seqno != seqno) { idl->state_seqno = seqno; json_destroy(idl->request_id); idl->request_id = NULL; ovsdb_idl_txn_abort_all(idl); ovsdb_idl_send_schema_request(idl); idl->state = IDL_S_SCHEMA_REQUESTED; if (idl->lock_name) { ovsdb_idl_send_lock_request(idl); } } msg = jsonrpc_session_recv(idl->session); if (!msg) { break; } if (msg->type == JSONRPC_NOTIFY && !strcmp(msg->method, "update") && msg->params->type == JSON_ARRAY && msg->params->u.array.n == 2 && msg->params->u.array.elems[0]->type == JSON_NULL) { /* Database contents changed. */ ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1]); } else if (msg->type == JSONRPC_REPLY && idl->request_id && json_equal(idl->request_id, msg->id)) { switch (idl->state) { case IDL_S_SCHEMA_REQUESTED: /* Reply to our "get_schema" request. */ json_destroy(idl->request_id); idl->request_id = NULL; ovsdb_idl_send_monitor_request(idl, msg->result); idl->state = IDL_S_MONITOR_REQUESTED; break; case IDL_S_MONITOR_REQUESTED: /* Reply to our "monitor" request. */ idl->change_seqno++; json_destroy(idl->request_id); idl->request_id = NULL; idl->state = IDL_S_MONITORING; ovsdb_idl_clear(idl); ovsdb_idl_parse_update(idl, msg->result); break; case IDL_S_MONITORING: default: OVS_NOT_REACHED(); } } else if (msg->type == JSONRPC_REPLY && idl->lock_request_id && json_equal(idl->lock_request_id, msg->id)) { /* Reply to our "lock" request. */ ovsdb_idl_parse_lock_reply(idl, msg->result); } else if (msg->type == JSONRPC_NOTIFY && !strcmp(msg->method, "locked")) { /* We got our lock. */ ovsdb_idl_parse_lock_notify(idl, msg->params, true); } else if (msg->type == JSONRPC_NOTIFY && !strcmp(msg->method, "stolen")) { /* Someone else stole our lock. */ ovsdb_idl_parse_lock_notify(idl, msg->params, false); } else if ((msg->type == JSONRPC_ERROR || msg->type == JSONRPC_REPLY) && ovsdb_idl_txn_process_reply(idl, msg)) { /* ovsdb_idl_txn_process_reply() did everything needful. */ } else { /* This can happen if ovsdb_idl_txn_destroy() is called to destroy * a transaction before we receive the reply, so keep the log level * low. */ VLOG_DBG("%s: received unexpected %s message", jsonrpc_session_get_name(idl->session), jsonrpc_msg_type_to_string(msg->type)); } jsonrpc_msg_destroy(msg); } ovsdb_idl_row_destroy_postprocess(idl); } /* Arranges for poll_block() to wake up when ovsdb_idl_run() has something to * do or when activity occurs on a transaction on 'idl'. */ void ovsdb_idl_wait(struct ovsdb_idl *idl) { jsonrpc_session_wait(idl->session); jsonrpc_session_recv_wait(idl->session); } /* Returns a "sequence number" that represents the state of 'idl'. When * ovsdb_idl_run() changes the database, the sequence number changes. The * initial fetch of the entire contents of the remote database is considered to * be one kind of change. Successfully acquiring a lock, if one has been * configured with ovsdb_idl_set_lock(), is also considered to be a change. * * As long as the sequence number does not change, the client may continue to * use any data structures it obtains from 'idl'. But when it changes, the * client must not access any of these data structures again, because they * could have freed or reused for other purposes. * * The sequence number can occasionally change even if the database does not. * This happens if the connection to the database drops and reconnects, which * causes the database contents to be reloaded even if they didn't change. (It * could also happen if the database server sends out a "change" that reflects * what the IDL already thought was in the database. The database server is * not supposed to do that, but bugs could in theory cause it to do so.) */ unsigned int ovsdb_idl_get_seqno(const struct ovsdb_idl *idl) { return idl->change_seqno; } /* Returns true if 'idl' successfully connected to the remote database and * retrieved its contents (even if the connection subsequently dropped and is * in the process of reconnecting). If so, then 'idl' contains an atomic * snapshot of the database's contents (but it might be arbitrarily old if the * connection dropped). * * Returns false if 'idl' has never connected or retrieved the database's * contents. If so, 'idl' is empty. */ bool ovsdb_idl_has_ever_connected(const struct ovsdb_idl *idl) { return ovsdb_idl_get_seqno(idl) != 0; } /* Reconfigures 'idl' so that it would reconnect to the database, if * connection was dropped. */ void ovsdb_idl_enable_reconnect(struct ovsdb_idl *idl) { jsonrpc_session_enable_reconnect(idl->session); } /* Forces 'idl' to drop its connection to the database and reconnect. In the * meantime, the contents of 'idl' will not change. */ void ovsdb_idl_force_reconnect(struct ovsdb_idl *idl) { jsonrpc_session_force_reconnect(idl->session); } /* Some IDL users should only write to write-only columns. Furthermore, * writing to a column which is not write-only can cause serious performance * degradations for these users. This function causes 'idl' to reject writes * to columns which are not marked write only using ovsdb_idl_omit_alert(). */ void ovsdb_idl_verify_write_only(struct ovsdb_idl *idl) { idl->verify_write_only = true; } /* Returns true if 'idl' is currently connected or trying to connect. */ bool ovsdb_idl_is_alive(const struct ovsdb_idl *idl) { return jsonrpc_session_is_alive(idl->session); } /* Returns the last error reported on a connection by 'idl'. The return value * is 0 only if no connection made by 'idl' has ever encountered an error. See * jsonrpc_get_status() for return value interpretation. */ int ovsdb_idl_get_last_error(const struct ovsdb_idl *idl) { return jsonrpc_session_get_last_error(idl->session); } static unsigned char * ovsdb_idl_get_mode(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { size_t i; ovs_assert(!idl->change_seqno); for (i = 0; i < idl->class->n_tables; i++) { const struct ovsdb_idl_table *table = &idl->tables[i]; const struct ovsdb_idl_table_class *tc = table->class; if (column >= tc->columns && column < &tc->columns[tc->n_columns]) { return &table->modes[column - tc->columns]; } } OVS_NOT_REACHED(); } static void add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base) { if (base->type == OVSDB_TYPE_UUID && base->u.uuid.refTableName) { struct ovsdb_idl_table *table; table = shash_find_data(&idl->table_by_name, base->u.uuid.refTableName); if (table) { table->need_table = true; } else { VLOG_WARN("%s IDL class missing referenced table %s", idl->class->database, base->u.uuid.refTableName); } } } /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'. Also * ensures that any tables referenced by 'column' will be replicated, even if * no columns in that table are selected for replication (see * ovsdb_idl_add_table() for more information). * * This function is only useful if 'monitor_everything_by_default' was false in * the call to ovsdb_idl_create(). This function should be called between * ovsdb_idl_create() and the first call to ovsdb_idl_run(). */ void ovsdb_idl_add_column(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_MONITOR | OVSDB_IDL_ALERT; add_ref_table(idl, &column->type.key); add_ref_table(idl, &column->type.value); } /* Ensures that the table with class 'tc' will be replicated on 'idl' even if * no columns are selected for replication. Just the necessary data for table * references will be replicated (the UUID of the rows, for instance), any * columns not selected for replication will remain unreplicated. * This can be useful because it allows 'idl' to keep track of what rows in the * table actually exist, which in turn allows columns that reference the table * to have accurate contents. (The IDL presents the database with references to * rows that do not exist removed.) * * This function is only useful if 'monitor_everything_by_default' was false in * the call to ovsdb_idl_create(). This function should be called between * ovsdb_idl_create() and the first call to ovsdb_idl_run(). */ void ovsdb_idl_add_table(struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *tc) { size_t i; for (i = 0; i < idl->class->n_tables; i++) { struct ovsdb_idl_table *table = &idl->tables[i]; if (table->class == tc) { table->need_table = true; return; } } OVS_NOT_REACHED(); } /* Turns off OVSDB_IDL_ALERT for 'column' in 'idl'. * * This function should be called between ovsdb_idl_create() and the first call * to ovsdb_idl_run(). */ void ovsdb_idl_omit_alert(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { *ovsdb_idl_get_mode(idl, column) &= ~OVSDB_IDL_ALERT; } /* Sets the mode for 'column' in 'idl' to 0. See the big comment above * OVSDB_IDL_MONITOR for details. * * This function should be called between ovsdb_idl_create() and the first call * to ovsdb_idl_run(). */ void ovsdb_idl_omit(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { *ovsdb_idl_get_mode(idl, column) = 0; } /* Returns the most recent IDL change sequence number that caused a * insert, modify or delete update to the table with class 'table_class'. */ unsigned int ovsdb_idl_table_get_seqno(const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *table_class) { struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, table_class); unsigned int max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_INSERT]; if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]) { max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]; } if (max_seqno < table->change_seqno[OVSDB_IDL_CHANGE_DELETE]) { max_seqno = table->change_seqno[OVSDB_IDL_CHANGE_DELETE]; } return max_seqno; } /* For each row that contains tracked columns, IDL stores the most * recent IDL change sequence numbers associateed with insert, modify * and delete updates to the table. */ unsigned int ovsdb_idl_row_get_seqno(const struct ovsdb_idl_row *row, enum ovsdb_idl_change change) { return row->change_seqno[change]; } /* Turns on OVSDB_IDL_TRACK for 'column' in 'idl', ensuring that * all rows whose 'column' is modified are traced. Similarly, insert * or delete of rows having 'column' are tracked. Clients are able * to retrive the tracked rows with the ovsdb_idl_track_get_*() * functions. * * This function should be called between ovsdb_idl_create() and * the first call to ovsdb_idl_run(). The column to be tracked * should have OVSDB_IDL_ALERT turned on. */ void ovsdb_idl_track_add_column(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { if (!(*ovsdb_idl_get_mode(idl, column) & OVSDB_IDL_ALERT)) { ovsdb_idl_add_column(idl, column); } *ovsdb_idl_get_mode(idl, column) |= OVSDB_IDL_TRACK; } void ovsdb_idl_track_add_all(struct ovsdb_idl *idl) { size_t i, j; for (i = 0; i < idl->class->n_tables; i++) { const struct ovsdb_idl_table_class *tc = &idl->class->tables[i]; for (j = 0; j < tc->n_columns; j++) { const struct ovsdb_idl_column *column = &tc->columns[j]; ovsdb_idl_track_add_column(idl, column); } } } /* Returns true if 'table' has any tracked column. */ static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table) { size_t i; for (i = 0; i < table->class->n_columns; i++) { if (table->modes[i] & OVSDB_IDL_TRACK) { return true; } } return false; } /* Returns the first tracked row in table with class 'table_class' * for the specified 'idl'. Returns NULL if there are no tracked rows */ const struct ovsdb_idl_row * ovsdb_idl_track_get_first(const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *table_class) { struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, table_class); if (!list_is_empty(&table->track_list)) { return CONTAINER_OF(list_front(&table->track_list), struct ovsdb_idl_row, track_node); } return NULL; } /* Returns the next tracked row in table after the specified 'row' * (in no particular order). Returns NULL if there are no tracked rows */ const struct ovsdb_idl_row * ovsdb_idl_track_get_next(const struct ovsdb_idl_row *row) { if (row->track_node.next != &row->table->track_list) { return CONTAINER_OF(row->track_node.next, struct ovsdb_idl_row, track_node); } return NULL; } /* Flushes the tracked rows. Client calls this function after calling * ovsdb_idl_run() and read all tracked rows with the ovsdb_idl_track_get_*() * functions. This is usually done at the end of the client's processing * loop when it is ready to do ovsdb_idl_run() again. */ void ovsdb_idl_track_clear(const struct ovsdb_idl *idl) { size_t i; for (i = 0; i < idl->class->n_tables; i++) { struct ovsdb_idl_table *table = &idl->tables[i]; if (!list_is_empty(&table->track_list)) { struct ovsdb_idl_row *row, *next; LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) { list_remove(&row->track_node); list_init(&row->track_node); if (ovsdb_idl_row_is_orphan(row)) { ovsdb_idl_row_clear_old(row); free(row); } } } } } static void ovsdb_idl_send_schema_request(struct ovsdb_idl *idl) { struct jsonrpc_msg *msg; json_destroy(idl->request_id); msg = jsonrpc_create_request( "get_schema", json_array_create_1(json_string_create(idl->class->database)), &idl->request_id); jsonrpc_session_send(idl->session, msg); } static void log_error(struct ovsdb_error *error) { char *s = ovsdb_error_to_string(error); VLOG_WARN("error parsing database schema: %s", s); free(s); ovsdb_error_destroy(error); } /* Frees 'schema', which is in the format returned by parse_schema(). */ static void free_schema(struct shash *schema) { if (schema) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, schema) { struct sset *sset = node->data; sset_destroy(sset); free(sset); shash_delete(schema, node); } shash_destroy(schema); free(schema); } } /* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC * 7047, to obtain the names of its rows and columns. If successful, returns * an shash whose keys are table names and whose values are ssets, where each * sset contains the names of its table's columns. On failure (due to a parse * error), returns NULL. * * It would also be possible to use the general-purpose OVSDB schema parser in * ovsdb-server, but that's overkill, possibly too strict for the current use * case, and would require restructuring ovsdb-server to separate the schema * code from the rest. */ static struct shash * parse_schema(const struct json *schema_json) { struct ovsdb_parser parser; const struct json *tables_json; struct ovsdb_error *error; struct shash_node *node; struct shash *schema; ovsdb_parser_init(&parser, schema_json, "database schema"); tables_json = ovsdb_parser_member(&parser, "tables", OP_OBJECT); error = ovsdb_parser_destroy(&parser); if (error) { log_error(error); return NULL; } schema = xmalloc(sizeof *schema); shash_init(schema); SHASH_FOR_EACH (node, json_object(tables_json)) { const char *table_name = node->name; const struct json *json = node->data; const struct json *columns_json; ovsdb_parser_init(&parser, json, "table schema for table %s", table_name); columns_json = ovsdb_parser_member(&parser, "columns", OP_OBJECT); error = ovsdb_parser_destroy(&parser); if (error) { log_error(error); free_schema(schema); return NULL; } struct sset *columns = xmalloc(sizeof *columns); sset_init(columns); struct shash_node *node2; SHASH_FOR_EACH (node2, json_object(columns_json)) { const char *column_name = node2->name; sset_add(columns, column_name); } shash_add(schema, table_name, columns); } return schema; } static void ovsdb_idl_send_monitor_request(struct ovsdb_idl *idl, const struct json *schema_json) { struct shash *schema = parse_schema(schema_json); struct json *monitor_requests; struct jsonrpc_msg *msg; size_t i; monitor_requests = json_object_create(); for (i = 0; i < idl->class->n_tables; i++) { const struct ovsdb_idl_table *table = &idl->tables[i]; const struct ovsdb_idl_table_class *tc = table->class; struct json *monitor_request, *columns; const struct sset *table_schema; size_t j; table_schema = (schema ? shash_find_data(schema, table->class->name) : NULL); columns = table->need_table ? json_array_create_empty() : NULL; for (j = 0; j < tc->n_columns; j++) { const struct ovsdb_idl_column *column = &tc->columns[j]; if (table->modes[j] & OVSDB_IDL_MONITOR) { if (table_schema && !sset_contains(table_schema, column->name)) { VLOG_WARN("%s table in %s database lacks %s column " "(database needs upgrade?)", table->class->name, idl->class->database, column->name); continue; } if (!columns) { columns = json_array_create_empty(); } json_array_add(columns, json_string_create(column->name)); } } if (columns) { if (schema && !table_schema) { VLOG_WARN("%s database lacks %s table " "(database needs upgrade?)", idl->class->database, table->class->name); json_destroy(columns); continue; } monitor_request = json_object_create(); json_object_put(monitor_request, "columns", columns); json_object_put(monitor_requests, tc->name, monitor_request); } } free_schema(schema); json_destroy(idl->request_id); msg = jsonrpc_create_request( "monitor", json_array_create_3(json_string_create(idl->class->database), json_null_create(), monitor_requests), &idl->request_id); jsonrpc_session_send(idl->session, msg); } static void ovsdb_idl_parse_update(struct ovsdb_idl *idl, const struct json *table_updates) { struct ovsdb_error *error = ovsdb_idl_parse_update__(idl, table_updates); if (error) { if (!VLOG_DROP_WARN(&syntax_rl)) { char *s = ovsdb_error_to_string(error); VLOG_WARN_RL(&syntax_rl, "%s", s); free(s); } ovsdb_error_destroy(error); } } static struct ovsdb_error * ovsdb_idl_parse_update__(struct ovsdb_idl *idl, const struct json *table_updates) { const struct shash_node *tables_node; if (table_updates->type != JSON_OBJECT) { return ovsdb_syntax_error(table_updates, NULL, " is not an object"); } SHASH_FOR_EACH (tables_node, json_object(table_updates)) { const struct json *table_update = tables_node->data; const struct shash_node *table_node; struct ovsdb_idl_table *table; table = shash_find_data(&idl->table_by_name, tables_node->name); if (!table) { return ovsdb_syntax_error( table_updates, NULL, " includes unknown table \"%s\"", tables_node->name); } if (table_update->type != JSON_OBJECT) { return ovsdb_syntax_error(table_update, NULL, " for table \"%s\" is " "not an object", table->class->name); } SHASH_FOR_EACH (table_node, json_object(table_update)) { const struct json *row_update = table_node->data; const struct json *old_json, *new_json; struct uuid uuid; if (!uuid_from_string(&uuid, table_node->name)) { return ovsdb_syntax_error(table_update, NULL, " for table \"%s\" " "contains bad UUID " "\"%s\" as member name", table->class->name, table_node->name); } if (row_update->type != JSON_OBJECT) { return ovsdb_syntax_error(row_update, NULL, " for table \"%s\" " "contains for %s that " "is not an object", table->class->name, table_node->name); } old_json = shash_find_data(json_object(row_update), "old"); new_json = shash_find_data(json_object(row_update), "new"); if (old_json && old_json->type != JSON_OBJECT) { return ovsdb_syntax_error(old_json, NULL, "\"old\" is not object"); } else if (new_json && new_json->type != JSON_OBJECT) { return ovsdb_syntax_error(new_json, NULL, "\"new\" is not object"); } else if ((old_json != NULL) + (new_json != NULL) != shash_count(json_object(row_update))) { return ovsdb_syntax_error(row_update, NULL, " contains unexpected " "member"); } else if (!old_json && !new_json) { return ovsdb_syntax_error(row_update, NULL, " missing \"old\" " "and \"new\" members"); } if (ovsdb_idl_process_update(table, &uuid, old_json, new_json)) { idl->change_seqno++; } } } return NULL; } static struct ovsdb_idl_row * ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid) { struct ovsdb_idl_row *row; HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &table->rows) { if (uuid_equals(&row->uuid, uuid)) { return row; } } return NULL; } /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false * otherwise. */ static bool ovsdb_idl_process_update(struct ovsdb_idl_table *table, const struct uuid *uuid, const struct json *old, const struct json *new) { struct ovsdb_idl_row *row; row = ovsdb_idl_get_row(table, uuid); if (!new) { /* Delete row. */ if (row && !ovsdb_idl_row_is_orphan(row)) { /* XXX perhaps we should check the 'old' values? */ ovsdb_idl_delete_row(row); } else { VLOG_WARN_RL(&semantic_rl, "cannot delete missing row "UUID_FMT" " "from table %s", UUID_ARGS(uuid), table->class->name); return false; } } else if (!old) { /* Insert row. */ if (!row) { ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); } else if (ovsdb_idl_row_is_orphan(row)) { ovsdb_idl_insert_row(row, new); } else { VLOG_WARN_RL(&semantic_rl, "cannot add existing row "UUID_FMT" to " "table %s", UUID_ARGS(uuid), table->class->name); return ovsdb_idl_modify_row(row, new); } } else { /* Modify row. */ if (row) { /* XXX perhaps we should check the 'old' values? */ if (!ovsdb_idl_row_is_orphan(row)) { return ovsdb_idl_modify_row(row, new); } else { VLOG_WARN_RL(&semantic_rl, "cannot modify missing but " "referenced row "UUID_FMT" in table %s", UUID_ARGS(uuid), table->class->name); ovsdb_idl_insert_row(row, new); } } else { VLOG_WARN_RL(&semantic_rl, "cannot modify missing row "UUID_FMT" " "in table %s", UUID_ARGS(uuid), table->class->name); ovsdb_idl_insert_row(ovsdb_idl_row_create(table, uuid), new); } } return true; } /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false * otherwise. */ static bool ovsdb_idl_row_update(struct ovsdb_idl_row *row, const struct json *row_json, enum ovsdb_idl_change change) { struct ovsdb_idl_table *table = row->table; struct shash_node *node; bool changed = false; SHASH_FOR_EACH (node, json_object(row_json)) { const char *column_name = node->name; const struct ovsdb_idl_column *column; struct ovsdb_datum datum; struct ovsdb_error *error; column = shash_find_data(&table->columns, column_name); if (!column) { VLOG_WARN_RL(&syntax_rl, "unknown column %s updating row "UUID_FMT, column_name, UUID_ARGS(&row->uuid)); continue; } error = ovsdb_datum_from_json(&datum, &column->type, node->data, NULL); if (!error) { unsigned int column_idx = column - table->class->columns; struct ovsdb_datum *old = &row->old[column_idx]; if (!ovsdb_datum_equals(old, &datum, &column->type)) { ovsdb_datum_swap(old, &datum); if (table->modes[column_idx] & OVSDB_IDL_ALERT) { changed = true; row->change_seqno[change] = row->table->change_seqno[change] = row->table->idl->change_seqno + 1; if (table->modes[column_idx] & OVSDB_IDL_TRACK) { if (list_is_empty(&row->track_node)) { list_push_front(&row->table->track_list, &row->track_node); } } } } else { /* Didn't really change but the OVSDB monitor protocol always * includes every value in a row. */ } ovsdb_datum_destroy(&datum, &column->type); } else { char *s = ovsdb_error_to_string(error); VLOG_WARN_RL(&syntax_rl, "error parsing column %s in row "UUID_FMT " in table %s: %s", column_name, UUID_ARGS(&row->uuid), table->class->name, s); free(s); ovsdb_error_destroy(error); } } return changed; } /* When a row A refers to row B through a column with a "refTable" constraint, * but row B does not exist, row B is called an "orphan row". Orphan rows * should not persist, because the database enforces referential integrity, but * they can appear transiently as changes from the database are received (the * database doesn't try to topologically sort them and circular references mean * it isn't always possible anyhow). * * This function returns true if 'row' is an orphan row, otherwise false. */ static bool ovsdb_idl_row_is_orphan(const struct ovsdb_idl_row *row) { return !row->old && !row->new; } /* Returns true if 'row' is conceptually part of the database as modified by * the current transaction (if any), false otherwise. * * This function will return true if 'row' is not an orphan (see the comment on * ovsdb_idl_row_is_orphan()) and: * * - 'row' exists in the database and has not been deleted within the * current transaction (if any). * * - 'row' was inserted within the current transaction and has not been * deleted. (In the latter case you should not have passed 'row' in at * all, because ovsdb_idl_txn_delete() freed it.) * * This function will return false if 'row' is an orphan or if 'row' was * deleted within the current transaction. */ static bool ovsdb_idl_row_exists(const struct ovsdb_idl_row *row) { return row->new != NULL; } static void ovsdb_idl_row_parse(struct ovsdb_idl_row *row) { const struct ovsdb_idl_table_class *class = row->table->class; size_t i; for (i = 0; i < class->n_columns; i++) { const struct ovsdb_idl_column *c = &class->columns[i]; (c->parse)(row, &row->old[i]); } } static void ovsdb_idl_row_unparse(struct ovsdb_idl_row *row) { const struct ovsdb_idl_table_class *class = row->table->class; size_t i; for (i = 0; i < class->n_columns; i++) { const struct ovsdb_idl_column *c = &class->columns[i]; (c->unparse)(row); } } static void ovsdb_idl_row_clear_old(struct ovsdb_idl_row *row) { ovs_assert(row->old == row->new); if (!ovsdb_idl_row_is_orphan(row)) { const struct ovsdb_idl_table_class *class = row->table->class; size_t i; for (i = 0; i < class->n_columns; i++) { ovsdb_datum_destroy(&row->old[i], &class->columns[i].type); } free(row->old); row->old = row->new = NULL; } } static void ovsdb_idl_row_clear_new(struct ovsdb_idl_row *row) { if (row->old != row->new) { if (row->new) { const struct ovsdb_idl_table_class *class = row->table->class; size_t i; if (row->written) { BITMAP_FOR_EACH_1 (i, class->n_columns, row->written) { ovsdb_datum_destroy(&row->new[i], &class->columns[i].type); } } free(row->new); free(row->written); row->written = NULL; } row->new = row->old; } } static void ovsdb_idl_row_clear_arcs(struct ovsdb_idl_row *row, bool destroy_dsts) { struct ovsdb_idl_arc *arc, *next; /* Delete all forward arcs. If 'destroy_dsts', destroy any orphaned rows * that this causes to be unreferenced, if tracking is not enabled. * If tracking is enabled, orphaned nodes are removed from hmap but not * freed. */ LIST_FOR_EACH_SAFE (arc, next, src_node, &row->src_arcs) { list_remove(&arc->dst_node); if (destroy_dsts && ovsdb_idl_row_is_orphan(arc->dst) && list_is_empty(&arc->dst->dst_arcs)) { ovsdb_idl_row_destroy(arc->dst); } free(arc); } list_init(&row->src_arcs); } /* Force nodes that reference 'row' to reparse. */ static void ovsdb_idl_row_reparse_backrefs(struct ovsdb_idl_row *row) { struct ovsdb_idl_arc *arc, *next; /* This is trickier than it looks. ovsdb_idl_row_clear_arcs() will destroy * 'arc', so we need to use the "safe" variant of list traversal. However, * calling an ovsdb_idl_column's 'parse' function will add an arc * equivalent to 'arc' to row->arcs. That could be a problem for * traversal, but it adds it at the beginning of the list to prevent us * from stumbling upon it again. * * (If duplicate arcs were possible then we would need to make sure that * 'next' didn't also point into 'arc''s destination, but we forbid * duplicate arcs.) */ LIST_FOR_EACH_SAFE (arc, next, dst_node, &row->dst_arcs) { struct ovsdb_idl_row *ref = arc->src; ovsdb_idl_row_unparse(ref); ovsdb_idl_row_clear_arcs(ref, false); ovsdb_idl_row_parse(ref); } } static struct ovsdb_idl_row * ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class) { struct ovsdb_idl_row *row = xzalloc(class->allocation_size); class->row_init(row); list_init(&row->src_arcs); list_init(&row->dst_arcs); hmap_node_nullify(&row->txn_node); list_init(&row->track_node); return row; } static struct ovsdb_idl_row * ovsdb_idl_row_create(struct ovsdb_idl_table *table, const struct uuid *uuid) { struct ovsdb_idl_row *row = ovsdb_idl_row_create__(table->class); hmap_insert(&table->rows, &row->hmap_node, uuid_hash(uuid)); row->uuid = *uuid; row->table = table; return row; } static void ovsdb_idl_row_destroy(struct ovsdb_idl_row *row) { if (row) { ovsdb_idl_row_clear_old(row); hmap_remove(&row->table->rows, &row->hmap_node); if (ovsdb_idl_track_is_set(row->table)) { row->change_seqno[OVSDB_IDL_CHANGE_DELETE] = row->table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = row->table->idl->change_seqno + 1; } if (list_is_empty(&row->track_node)) { list_push_front(&row->table->track_list, &row->track_node); } } } static void ovsdb_idl_row_destroy_postprocess(struct ovsdb_idl *idl) { size_t i; for (i = 0; i < idl->class->n_tables; i++) { struct ovsdb_idl_table *table = &idl->tables[i]; if (!list_is_empty(&table->track_list)) { struct ovsdb_idl_row *row, *next; LIST_FOR_EACH_SAFE(row, next, track_node, &table->track_list) { if (!ovsdb_idl_track_is_set(row->table)) { list_remove(&row->track_node); free(row); } } } } } static void ovsdb_idl_insert_row(struct ovsdb_idl_row *row, const struct json *row_json) { const struct ovsdb_idl_table_class *class = row->table->class; size_t i; ovs_assert(!row->old && !row->new); row->old = row->new = xmalloc(class->n_columns * sizeof *row->old); for (i = 0; i < class->n_columns; i++) { ovsdb_datum_init_default(&row->old[i], &class->columns[i].type); } ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_INSERT); ovsdb_idl_row_parse(row); ovsdb_idl_row_reparse_backrefs(row); } static void ovsdb_idl_delete_row(struct ovsdb_idl_row *row) { ovsdb_idl_row_unparse(row); ovsdb_idl_row_clear_arcs(row, true); ovsdb_idl_row_clear_old(row); if (list_is_empty(&row->dst_arcs)) { ovsdb_idl_row_destroy(row); } else { ovsdb_idl_row_reparse_backrefs(row); } } /* Returns true if a column with mode OVSDB_IDL_MODE_RW changed, false * otherwise. */ static bool ovsdb_idl_modify_row(struct ovsdb_idl_row *row, const struct json *row_json) { bool changed; ovsdb_idl_row_unparse(row); ovsdb_idl_row_clear_arcs(row, true); changed = ovsdb_idl_row_update(row, row_json, OVSDB_IDL_CHANGE_MODIFY); ovsdb_idl_row_parse(row); return changed; } static bool may_add_arc(const struct ovsdb_idl_row *src, const struct ovsdb_idl_row *dst) { const struct ovsdb_idl_arc *arc; /* No self-arcs. */ if (src == dst) { return false; } /* No duplicate arcs. * * We only need to test whether the first arc in dst->dst_arcs originates * at 'src', since we add all of the arcs from a given source in a clump * (in a single call to ovsdb_idl_row_parse()) and new arcs are always * added at the front of the dst_arcs list. */ if (list_is_empty(&dst->dst_arcs)) { return true; } arc = CONTAINER_OF(dst->dst_arcs.next, struct ovsdb_idl_arc, dst_node); return arc->src != src; } static struct ovsdb_idl_table * ovsdb_idl_table_from_class(const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *table_class) { return &idl->tables[table_class - idl->class->tables]; } /* Called by ovsdb-idlc generated code. */ struct ovsdb_idl_row * ovsdb_idl_get_row_arc(struct ovsdb_idl_row *src, struct ovsdb_idl_table_class *dst_table_class, const struct uuid *dst_uuid) { struct ovsdb_idl *idl = src->table->idl; struct ovsdb_idl_table *dst_table; struct ovsdb_idl_arc *arc; struct ovsdb_idl_row *dst; dst_table = ovsdb_idl_table_from_class(idl, dst_table_class); dst = ovsdb_idl_get_row(dst_table, dst_uuid); if (idl->txn) { /* We're being called from ovsdb_idl_txn_write(). We must not update * any arcs, because the transaction will be backed out at commit or * abort time and we don't want our graph screwed up. * * Just return the destination row, if there is one and it has not been * deleted. */ if (dst && (hmap_node_is_null(&dst->txn_node) || dst->new)) { return dst; } return NULL; } else { /* We're being called from some other context. Update the graph. */ if (!dst) { dst = ovsdb_idl_row_create(dst_table, dst_uuid); } /* Add a new arc, if it wouldn't be a self-arc or a duplicate arc. */ if (may_add_arc(src, dst)) { /* The arc *must* be added at the front of the dst_arcs list. See * ovsdb_idl_row_reparse_backrefs() for details. */ arc = xmalloc(sizeof *arc); list_push_front(&src->src_arcs, &arc->src_node); list_push_front(&dst->dst_arcs, &arc->dst_node); arc->src = src; arc->dst = dst; } return !ovsdb_idl_row_is_orphan(dst) ? dst : NULL; } } /* Searches 'tc''s table in 'idl' for a row with UUID 'uuid'. Returns a * pointer to the row if there is one, otherwise a null pointer. */ const struct ovsdb_idl_row * ovsdb_idl_get_row_for_uuid(const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *tc, const struct uuid *uuid) { return ovsdb_idl_get_row(ovsdb_idl_table_from_class(idl, tc), uuid); } static struct ovsdb_idl_row * next_real_row(struct ovsdb_idl_table *table, struct hmap_node *node) { for (; node; node = hmap_next(&table->rows, node)) { struct ovsdb_idl_row *row; row = CONTAINER_OF(node, struct ovsdb_idl_row, hmap_node); if (ovsdb_idl_row_exists(row)) { return row; } } return NULL; } /* Returns a row in 'table_class''s table in 'idl', or a null pointer if that * table is empty. * * Database tables are internally maintained as hash tables, so adding or * removing rows while traversing the same table can cause some rows to be * visited twice or not at apply. */ const struct ovsdb_idl_row * ovsdb_idl_first_row(const struct ovsdb_idl *idl, const struct ovsdb_idl_table_class *table_class) { struct ovsdb_idl_table *table = ovsdb_idl_table_from_class(idl, table_class); return next_real_row(table, hmap_first(&table->rows)); } /* Returns a row following 'row' within its table, or a null pointer if 'row' * is the last row in its table. */ const struct ovsdb_idl_row * ovsdb_idl_next_row(const struct ovsdb_idl_row *row) { struct ovsdb_idl_table *table = row->table; return next_real_row(table, hmap_next(&table->rows, &row->hmap_node)); } /* Reads and returns the value of 'column' within 'row'. If an ongoing * transaction has changed 'column''s value, the modified value is returned. * * The caller must not modify or free the returned value. * * Various kinds of changes can invalidate the returned value: writing to the * same 'column' in 'row' (e.g. with ovsdb_idl_txn_write()), deleting 'row' * (e.g. with ovsdb_idl_txn_delete()), or completing an ongoing transaction * (e.g. with ovsdb_idl_txn_commit() or ovsdb_idl_txn_abort()). If the * returned value is needed for a long time, it is best to make a copy of it * with ovsdb_datum_clone(). */ const struct ovsdb_datum * ovsdb_idl_read(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column) { const struct ovsdb_idl_table_class *class; size_t column_idx; ovs_assert(!ovsdb_idl_row_is_synthetic(row)); class = row->table->class; column_idx = column - class->columns; ovs_assert(row->new != NULL); ovs_assert(column_idx < class->n_columns); if (row->written && bitmap_is_set(row->written, column_idx)) { return &row->new[column_idx]; } else if (row->old) { return &row->old[column_idx]; } else { return ovsdb_datum_default(&column->type); } } /* Same as ovsdb_idl_read(), except that it also asserts that 'column' has key * type 'key_type' and value type 'value_type'. (Scalar and set types will * have a value type of OVSDB_TYPE_VOID.) * * This is useful in code that "knows" that a particular column has a given * type, so that it will abort if someone changes the column's type without * updating the code that uses it. */ const struct ovsdb_datum * ovsdb_idl_get(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column, enum ovsdb_atomic_type key_type OVS_UNUSED, enum ovsdb_atomic_type value_type OVS_UNUSED) { ovs_assert(column->type.key.type == key_type); ovs_assert(column->type.value.type == value_type); return ovsdb_idl_read(row, column); } /* Returns true if the field represented by 'column' in 'row' may be modified, * false if it is immutable. * * Normally, whether a field is mutable is controlled by its column's schema. * However, an immutable column can be set to any initial value at the time of * insertion, so if 'row' is a new row (one that is being added as part of the * current transaction, supposing that a transaction is in progress) then even * its "immutable" fields are actually mutable. */ bool ovsdb_idl_is_mutable(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column) { return column->mutable || (row->new && !row->old); } /* Returns false if 'row' was obtained from the IDL, true if it was initialized * to all-zero-bits by some other entity. If 'row' was set up some other way * then the return value is indeterminate. */ bool ovsdb_idl_row_is_synthetic(const struct ovsdb_idl_row *row) { return row->table == NULL; } /* Transactions. */ static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, enum ovsdb_idl_txn_status); /* Returns a string representation of 'status'. The caller must not modify or * free the returned string. * * The return value is probably useful only for debug log messages and unit * tests. */ const char * ovsdb_idl_txn_status_to_string(enum ovsdb_idl_txn_status status) { switch (status) { case TXN_UNCOMMITTED: return "uncommitted"; case TXN_UNCHANGED: return "unchanged"; case TXN_INCOMPLETE: return "incomplete"; case TXN_ABORTED: return "aborted"; case TXN_SUCCESS: return "success"; case TXN_TRY_AGAIN: return "try again"; case TXN_NOT_LOCKED: return "not locked"; case TXN_ERROR: return "error"; } return ""; } /* Starts a new transaction on 'idl'. A given ovsdb_idl may only have a single * active transaction at a time. See the large comment in ovsdb-idl.h for * general information on transactions. */ struct ovsdb_idl_txn * ovsdb_idl_txn_create(struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; ovs_assert(!idl->txn); idl->txn = txn = xmalloc(sizeof *txn); txn->request_id = NULL; txn->idl = idl; hmap_init(&txn->txn_rows); txn->status = TXN_UNCOMMITTED; txn->error = NULL; txn->dry_run = false; ds_init(&txn->comment); txn->inc_table = NULL; txn->inc_column = NULL; hmap_init(&txn->inserted_rows); return txn; } /* Appends 's', which is treated as a printf()-type format string, to the * comments that will be passed to the OVSDB server when 'txn' is committed. * (The comment will be committed to the OVSDB log, which "ovsdb-tool * show-log" can print in a relatively human-readable form.) */ void ovsdb_idl_txn_add_comment(struct ovsdb_idl_txn *txn, const char *s, ...) { va_list args; if (txn->comment.length) { ds_put_char(&txn->comment, '\n'); } va_start(args, s); ds_put_format_valist(&txn->comment, s, args); va_end(args); } /* Marks 'txn' as a transaction that will not actually modify the database. In * almost every way, the transaction is treated like other transactions. It * must be committed or aborted like other transactions, it will be sent to the * database server like other transactions, and so on. The only difference is * that the operations sent to the database server will include, as the last * step, an "abort" operation, so that any changes made by the transaction will * not actually take effect. */ void ovsdb_idl_txn_set_dry_run(struct ovsdb_idl_txn *txn) { txn->dry_run = true; } /* Causes 'txn', when committed, to increment the value of 'column' within * 'row' by 1. 'column' must have an integer type. After 'txn' commits * successfully, the client may retrieve the final (incremented) value of * 'column' with ovsdb_idl_txn_get_increment_new_value(). * * The client could accomplish something similar with ovsdb_idl_read(), * ovsdb_idl_txn_verify() and ovsdb_idl_txn_write(), or with ovsdb-idlc * generated wrappers for these functions. However, ovsdb_idl_txn_increment() * will never (by itself) fail because of a verify error. * * The intended use is for incrementing the "next_cfg" column in the * Open_vSwitch table. */ void ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn, const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column) { ovs_assert(!txn->inc_table); ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER); ovs_assert(column->type.value.type == OVSDB_TYPE_VOID); txn->inc_table = row->table->class->name; txn->inc_column = column->name; txn->inc_row = row->uuid; } /* Destroys 'txn' and frees all associated memory. If ovsdb_idl_txn_commit() * has been called for 'txn' but the commit is still incomplete (that is, the * last call returned TXN_INCOMPLETE) then the transaction may or may not still * end up committing at the database server, but the client will not be able to * get any further status information back. */ void ovsdb_idl_txn_destroy(struct ovsdb_idl_txn *txn) { struct ovsdb_idl_txn_insert *insert, *next; json_destroy(txn->request_id); if (txn->status == TXN_INCOMPLETE) { hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); } ovsdb_idl_txn_abort(txn); ds_destroy(&txn->comment); free(txn->error); HMAP_FOR_EACH_SAFE (insert, next, hmap_node, &txn->inserted_rows) { free(insert); } hmap_destroy(&txn->inserted_rows); free(txn); } /* Causes poll_block() to wake up if 'txn' has completed committing. */ void ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *txn) { if (txn->status != TXN_UNCOMMITTED && txn->status != TXN_INCOMPLETE) { poll_immediate_wake(); } } static struct json * where_uuid_equals(const struct uuid *uuid) { return json_array_create_1( json_array_create_3( json_string_create("_uuid"), json_string_create("=="), json_array_create_2( json_string_create("uuid"), json_string_create_nocopy( xasprintf(UUID_FMT, UUID_ARGS(uuid)))))); } static char * uuid_name_from_uuid(const struct uuid *uuid) { char *name; char *p; name = xasprintf("row"UUID_FMT, UUID_ARGS(uuid)); for (p = name; *p != '\0'; p++) { if (*p == '-') { *p = '_'; } } return name; } static const struct ovsdb_idl_row * ovsdb_idl_txn_get_row(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) { const struct ovsdb_idl_row *row; HMAP_FOR_EACH_WITH_HASH (row, txn_node, uuid_hash(uuid), &txn->txn_rows) { if (uuid_equals(&row->uuid, uuid)) { return row; } } return NULL; } /* XXX there must be a cleaner way to do this */ static struct json * substitute_uuids(struct json *json, const struct ovsdb_idl_txn *txn) { if (json->type == JSON_ARRAY) { struct uuid uuid; size_t i; if (json->u.array.n == 2 && json->u.array.elems[0]->type == JSON_STRING && json->u.array.elems[1]->type == JSON_STRING && !strcmp(json->u.array.elems[0]->u.string, "uuid") && uuid_from_string(&uuid, json->u.array.elems[1]->u.string)) { const struct ovsdb_idl_row *row; row = ovsdb_idl_txn_get_row(txn, &uuid); if (row && !row->old && row->new) { json_destroy(json); return json_array_create_2( json_string_create("named-uuid"), json_string_create_nocopy(uuid_name_from_uuid(&uuid))); } } for (i = 0; i < json->u.array.n; i++) { json->u.array.elems[i] = substitute_uuids(json->u.array.elems[i], txn); } } else if (json->type == JSON_OBJECT) { struct shash_node *node; SHASH_FOR_EACH (node, json_object(json)) { node->data = substitute_uuids(node->data, txn); } } return json; } static void ovsdb_idl_txn_disassemble(struct ovsdb_idl_txn *txn) { struct ovsdb_idl_row *row, *next; /* This must happen early. Otherwise, ovsdb_idl_row_parse() will call an * ovsdb_idl_column's 'parse' function, which will call * ovsdb_idl_get_row_arc(), which will seen that the IDL is in a * transaction and fail to update the graph. */ txn->idl->txn = NULL; HMAP_FOR_EACH_SAFE (row, next, txn_node, &txn->txn_rows) { if (row->old) { if (row->written) { ovsdb_idl_row_unparse(row); ovsdb_idl_row_clear_arcs(row, false); ovsdb_idl_row_parse(row); } } else { ovsdb_idl_row_unparse(row); } ovsdb_idl_row_clear_new(row); free(row->prereqs); row->prereqs = NULL; free(row->written); row->written = NULL; hmap_remove(&txn->txn_rows, &row->txn_node); hmap_node_nullify(&row->txn_node); if (!row->old) { hmap_remove(&row->table->rows, &row->hmap_node); free(row); } } hmap_destroy(&txn->txn_rows); hmap_init(&txn->txn_rows); } /* Attempts to commit 'txn'. Returns the status of the commit operation, one * of the following TXN_* constants: * * TXN_INCOMPLETE: * * The transaction is in progress, but not yet complete. The caller * should call again later, after calling ovsdb_idl_run() to let the IDL * do OVSDB protocol processing. * * TXN_UNCHANGED: * * The transaction is complete. (It didn't actually change the database, * so the IDL didn't send any request to the database server.) * * TXN_ABORTED: * * The caller previously called ovsdb_idl_txn_abort(). * * TXN_SUCCESS: * * The transaction was successful. The update made by the transaction * (and possibly other changes made by other database clients) should * already be visible in the IDL. * * TXN_TRY_AGAIN: * * The transaction failed for some transient reason, e.g. because a * "verify" operation reported an inconsistency or due to a network * problem. The caller should wait for a change to the database, then * compose a new transaction, and commit the new transaction. * * Use the return value of ovsdb_idl_get_seqno() to wait for a change in * the database. It is important to use its return value *before* the * initial call to ovsdb_idl_txn_commit() as the baseline for this * purpose, because the change that one should wait for can happen after * the initial call but before the call that returns TXN_TRY_AGAIN, and * using some other baseline value in that situation could cause an * indefinite wait if the database rarely changes. * * TXN_NOT_LOCKED: * * The transaction failed because the IDL has been configured to require * a database lock (with ovsdb_idl_set_lock()) but didn't get it yet or * has already lost it. * * Committing a transaction rolls back all of the changes that it made to the * IDL's copy of the database. If the transaction commits successfully, then * the database server will send an update and, thus, the IDL will be updated * with the committed changes. */ enum ovsdb_idl_txn_status ovsdb_idl_txn_commit(struct ovsdb_idl_txn *txn) { struct ovsdb_idl_row *row; struct json *operations; bool any_updates; if (txn != txn->idl->txn) { goto coverage_out; } /* If we need a lock but don't have it, give up quickly. */ if (txn->idl->lock_name && !ovsdb_idl_has_lock(txn->idl)) { txn->status = TXN_NOT_LOCKED; goto disassemble_out; } operations = json_array_create_1( json_string_create(txn->idl->class->database)); /* Assert that we have the required lock (avoiding a race). */ if (txn->idl->lock_name) { struct json *op = json_object_create(); json_array_add(operations, op); json_object_put_string(op, "op", "assert"); json_object_put_string(op, "lock", txn->idl->lock_name); } /* Add prerequisites and declarations of new rows. */ HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { /* XXX check that deleted rows exist even if no prereqs? */ if (row->prereqs) { const struct ovsdb_idl_table_class *class = row->table->class; size_t n_columns = class->n_columns; struct json *op, *columns, *row_json; size_t idx; op = json_object_create(); json_array_add(operations, op); json_object_put_string(op, "op", "wait"); json_object_put_string(op, "table", class->name); json_object_put(op, "timeout", json_integer_create(0)); json_object_put(op, "where", where_uuid_equals(&row->uuid)); json_object_put_string(op, "until", "=="); columns = json_array_create_empty(); json_object_put(op, "columns", columns); row_json = json_object_create(); json_object_put(op, "rows", json_array_create_1(row_json)); BITMAP_FOR_EACH_1 (idx, n_columns, row->prereqs) { const struct ovsdb_idl_column *column = &class->columns[idx]; json_array_add(columns, json_string_create(column->name)); json_object_put(row_json, column->name, ovsdb_datum_to_json(&row->old[idx], &column->type)); } } } /* Add updates. */ any_updates = false; HMAP_FOR_EACH (row, txn_node, &txn->txn_rows) { const struct ovsdb_idl_table_class *class = row->table->class; if (!row->new) { if (class->is_root) { struct json *op = json_object_create(); json_object_put_string(op, "op", "delete"); json_object_put_string(op, "table", class->name); json_object_put(op, "where", where_uuid_equals(&row->uuid)); json_array_add(operations, op); any_updates = true; } else { /* Let ovsdb-server decide whether to really delete it. */ } } else if (row->old != row->new) { struct json *row_json; struct json *op; size_t idx; op = json_object_create(); json_object_put_string(op, "op", row->old ? "update" : "insert"); json_object_put_string(op, "table", class->name); if (row->old) { json_object_put(op, "where", where_uuid_equals(&row->uuid)); } else { struct ovsdb_idl_txn_insert *insert; any_updates = true; json_object_put(op, "uuid-name", json_string_create_nocopy( uuid_name_from_uuid(&row->uuid))); insert = xmalloc(sizeof *insert); insert->dummy = row->uuid; insert->op_index = operations->u.array.n - 1; uuid_zero(&insert->real); hmap_insert(&txn->inserted_rows, &insert->hmap_node, uuid_hash(&insert->dummy)); } row_json = json_object_create(); json_object_put(op, "row", row_json); if (row->written) { BITMAP_FOR_EACH_1 (idx, class->n_columns, row->written) { const struct ovsdb_idl_column *column = &class->columns[idx]; if (row->old || !ovsdb_datum_is_default(&row->new[idx], &column->type)) { json_object_put(row_json, column->name, substitute_uuids( ovsdb_datum_to_json(&row->new[idx], &column->type), txn)); /* If anything really changed, consider it an update. * We can't suppress not-really-changed values earlier * or transactions would become nonatomic (see the big * comment inside ovsdb_idl_txn_write()). */ if (!any_updates && row->old && !ovsdb_datum_equals(&row->old[idx], &row->new[idx], &column->type)) { any_updates = true; } } } } if (!row->old || !shash_is_empty(json_object(row_json))) { json_array_add(operations, op); } else { json_destroy(op); } } } /* Add increment. */ if (txn->inc_table && any_updates) { struct json *op; txn->inc_index = operations->u.array.n - 1; op = json_object_create(); json_object_put_string(op, "op", "mutate"); json_object_put_string(op, "table", txn->inc_table); json_object_put(op, "where", substitute_uuids(where_uuid_equals(&txn->inc_row), txn)); json_object_put(op, "mutations", json_array_create_1( json_array_create_3( json_string_create(txn->inc_column), json_string_create("+="), json_integer_create(1)))); json_array_add(operations, op); op = json_object_create(); json_object_put_string(op, "op", "select"); json_object_put_string(op, "table", txn->inc_table); json_object_put(op, "where", substitute_uuids(where_uuid_equals(&txn->inc_row), txn)); json_object_put(op, "columns", json_array_create_1(json_string_create( txn->inc_column))); json_array_add(operations, op); } if (txn->comment.length) { struct json *op = json_object_create(); json_object_put_string(op, "op", "comment"); json_object_put_string(op, "comment", ds_cstr(&txn->comment)); json_array_add(operations, op); } if (txn->dry_run) { struct json *op = json_object_create(); json_object_put_string(op, "op", "abort"); json_array_add(operations, op); } if (!any_updates) { txn->status = TXN_UNCHANGED; json_destroy(operations); } else if (!jsonrpc_session_send( txn->idl->session, jsonrpc_create_request( "transact", operations, &txn->request_id))) { hmap_insert(&txn->idl->outstanding_txns, &txn->hmap_node, json_hash(txn->request_id, 0)); txn->status = TXN_INCOMPLETE; } else { txn->status = TXN_TRY_AGAIN; } disassemble_out: ovsdb_idl_txn_disassemble(txn); coverage_out: switch (txn->status) { case TXN_UNCOMMITTED: COVERAGE_INC(txn_uncommitted); break; case TXN_UNCHANGED: COVERAGE_INC(txn_unchanged); break; case TXN_INCOMPLETE: COVERAGE_INC(txn_incomplete); break; case TXN_ABORTED: COVERAGE_INC(txn_aborted); break; case TXN_SUCCESS: COVERAGE_INC(txn_success); break; case TXN_TRY_AGAIN: COVERAGE_INC(txn_try_again); break; case TXN_NOT_LOCKED: COVERAGE_INC(txn_not_locked); break; case TXN_ERROR: COVERAGE_INC(txn_error); break; } return txn->status; } /* Attempts to commit 'txn', blocking until the commit either succeeds or * fails. Returns the final commit status, which may be any TXN_* value other * than TXN_INCOMPLETE. * * This function calls ovsdb_idl_run() on 'txn''s IDL, so it may cause the * return value of ovsdb_idl_get_seqno() to change. */ enum ovsdb_idl_txn_status ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *txn) { enum ovsdb_idl_txn_status status; fatal_signal_run(); while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { ovsdb_idl_run(txn->idl); ovsdb_idl_wait(txn->idl); ovsdb_idl_txn_wait(txn); poll_block(); } return status; } /* Returns the final (incremented) value of the column in 'txn' that was set to * be incremented by ovsdb_idl_txn_increment(). 'txn' must have committed * successfully. */ int64_t ovsdb_idl_txn_get_increment_new_value(const struct ovsdb_idl_txn *txn) { ovs_assert(txn->status == TXN_SUCCESS); return txn->inc_new_value; } /* Aborts 'txn' without sending it to the database server. This is effective * only if ovsdb_idl_txn_commit() has not yet been called for 'txn'. * Otherwise, it has no effect. * * Aborting a transaction doesn't free its memory. Use * ovsdb_idl_txn_destroy() to do that. */ void ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn) { ovsdb_idl_txn_disassemble(txn); if (txn->status == TXN_UNCOMMITTED || txn->status == TXN_INCOMPLETE) { txn->status = TXN_ABORTED; } } /* Returns a string that reports the error status for 'txn'. The caller must * not modify or free the returned string. A call to ovsdb_idl_txn_destroy() * for 'txn' may free the returned string. * * The return value is ordinarily one of the strings that * ovsdb_idl_txn_status_to_string() would return, but if the transaction failed * due to an error reported by the database server, the return value is that * error. */ const char * ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *txn) { if (txn->status != TXN_ERROR) { return ovsdb_idl_txn_status_to_string(txn->status); } else if (txn->error) { return txn->error; } else { return "no error details available"; } } static void ovsdb_idl_txn_set_error_json(struct ovsdb_idl_txn *txn, const struct json *json) { if (txn->error == NULL) { txn->error = json_to_string(json, JSSF_SORT); } } /* For transaction 'txn' that completed successfully, finds and returns the * permanent UUID that the database assigned to a newly inserted row, given the * 'uuid' that ovsdb_idl_txn_insert() assigned locally to that row. * * Returns NULL if 'uuid' is not a UUID assigned by ovsdb_idl_txn_insert() or * if it was assigned by that function and then deleted by * ovsdb_idl_txn_delete() within the same transaction. (Rows that are inserted * and then deleted within a single transaction are never sent to the database * server, so it never assigns them a permanent UUID.) */ const struct uuid * ovsdb_idl_txn_get_insert_uuid(const struct ovsdb_idl_txn *txn, const struct uuid *uuid) { const struct ovsdb_idl_txn_insert *insert; ovs_assert(txn->status == TXN_SUCCESS || txn->status == TXN_UNCHANGED); HMAP_FOR_EACH_IN_BUCKET (insert, hmap_node, uuid_hash(uuid), &txn->inserted_rows) { if (uuid_equals(uuid, &insert->dummy)) { return &insert->real; } } return NULL; } static void ovsdb_idl_txn_complete(struct ovsdb_idl_txn *txn, enum ovsdb_idl_txn_status status) { txn->status = status; hmap_remove(&txn->idl->outstanding_txns, &txn->hmap_node); } /* Writes 'datum' to the specified 'column' in 'row_'. Updates both 'row_' * itself and the structs derived from it (e.g. the "struct ovsrec_*", for * ovs-vswitchd). * * 'datum' must have the correct type for its column. The IDL does not check * that it meets schema constraints, but ovsdb-server will do so at commit time * so it had better be correct. * * A transaction must be in progress. Replication of 'column' must not have * been disabled (by calling ovsdb_idl_omit()). * * Usually this function is used indirectly through one of the "set" functions * generated by ovsdb-idlc. * * Takes ownership of what 'datum' points to (and in some cases destroys that * data before returning) but makes a copy of 'datum' itself. (Commonly * 'datum' is on the caller's stack.) */ static void ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_, const struct ovsdb_idl_column *column, struct ovsdb_datum *datum, bool owns_datum) { struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); const struct ovsdb_idl_table_class *class; size_t column_idx; bool write_only; if (ovsdb_idl_row_is_synthetic(row)) { goto discard_datum; } class = row->table->class; column_idx = column - class->columns; write_only = row->table->modes[column_idx] == OVSDB_IDL_MONITOR; ovs_assert(row->new != NULL); ovs_assert(column_idx < class->n_columns); ovs_assert(row->old == NULL || row->table->modes[column_idx] & OVSDB_IDL_MONITOR); if (row->table->idl->verify_write_only && !write_only) { VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when" " explicitly configured not to.", class->name, column->name); goto discard_datum; } /* If this is a write-only column and the datum being written is the same * as the one already there, just skip the update entirely. This is worth * optimizing because we have a lot of columns that get periodically * refreshed into the database but don't actually change that often. * * We don't do this for read/write columns because that would break * atomicity of transactions--some other client might have written a * different value in that column since we read it. (But if a whole * transaction only does writes of existing values, without making any real * changes, we will drop the whole transaction later in * ovsdb_idl_txn_commit().) */ if (write_only && ovsdb_datum_equals(ovsdb_idl_read(row, column), datum, &column->type)) { goto discard_datum; } if (hmap_node_is_null(&row->txn_node)) { hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); } if (row->old == row->new) { row->new = xmalloc(class->n_columns * sizeof *row->new); } if (!row->written) { row->written = bitmap_allocate(class->n_columns); } if (bitmap_is_set(row->written, column_idx)) { ovsdb_datum_destroy(&row->new[column_idx], &column->type); } else { bitmap_set1(row->written, column_idx); } if (owns_datum) { row->new[column_idx] = *datum; } else { ovsdb_datum_clone(&row->new[column_idx], datum, &column->type); } (column->unparse)(row); (column->parse)(row, &row->new[column_idx]); return; discard_datum: if (owns_datum) { ovsdb_datum_destroy(datum, &column->type); } } void ovsdb_idl_txn_write(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column, struct ovsdb_datum *datum) { ovsdb_idl_txn_write__(row, column, datum, true); } void ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row, const struct ovsdb_idl_column *column, const struct ovsdb_datum *datum) { ovsdb_idl_txn_write__(row, column, CONST_CAST(struct ovsdb_datum *, datum), false); } /* Causes the original contents of 'column' in 'row_' to be verified as a * prerequisite to completing the transaction. That is, if 'column' in 'row_' * changed (or if 'row_' was deleted) between the time that the IDL originally * read its contents and the time that the transaction commits, then the * transaction aborts and ovsdb_idl_txn_commit() returns TXN_AGAIN_WAIT or * TXN_AGAIN_NOW (depending on whether the database change has already been * received). * * The intention is that, to ensure that no transaction commits based on dirty * reads, an application should call ovsdb_idl_txn_verify() on each data item * read as part of a read-modify-write operation. * * In some cases ovsdb_idl_txn_verify() reduces to a no-op, because the current * value of 'column' is already known: * * - If 'row_' is a row created by the current transaction (returned by * ovsdb_idl_txn_insert()). * * - If 'column' has already been modified (with ovsdb_idl_txn_write()) * within the current transaction. * * Because of the latter property, always call ovsdb_idl_txn_verify() *before* * ovsdb_idl_txn_write() for a given read-modify-write. * * A transaction must be in progress. * * Usually this function is used indirectly through one of the "verify" * functions generated by ovsdb-idlc. */ void ovsdb_idl_txn_verify(const struct ovsdb_idl_row *row_, const struct ovsdb_idl_column *column) { struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); const struct ovsdb_idl_table_class *class; size_t column_idx; if (ovsdb_idl_row_is_synthetic(row)) { return; } class = row->table->class; column_idx = column - class->columns; ovs_assert(row->new != NULL); ovs_assert(row->old == NULL || row->table->modes[column_idx] & OVSDB_IDL_MONITOR); if (!row->old || (row->written && bitmap_is_set(row->written, column_idx))) { return; } if (hmap_node_is_null(&row->txn_node)) { hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); } if (!row->prereqs) { row->prereqs = bitmap_allocate(class->n_columns); } bitmap_set1(row->prereqs, column_idx); } /* Deletes 'row_' from its table. May free 'row_', so it must not be * accessed afterward. * * A transaction must be in progress. * * Usually this function is used indirectly through one of the "delete" * functions generated by ovsdb-idlc. */ void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_) { struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_); if (ovsdb_idl_row_is_synthetic(row)) { return; } ovs_assert(row->new != NULL); if (!row->old) { ovsdb_idl_row_unparse(row); ovsdb_idl_row_clear_new(row); ovs_assert(!row->prereqs); hmap_remove(&row->table->rows, &row->hmap_node); hmap_remove(&row->table->idl->txn->txn_rows, &row->txn_node); free(row); return; } if (hmap_node_is_null(&row->txn_node)) { hmap_insert(&row->table->idl->txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); } ovsdb_idl_row_clear_new(row); row->new = NULL; } /* Inserts and returns a new row in the table with the specified 'class' in the * database with open transaction 'txn'. * * The new row is assigned a provisional UUID. If 'uuid' is null then one is * randomly generated; otherwise 'uuid' should specify a randomly generated * UUID not otherwise in use. ovsdb-server will assign a different UUID when * 'txn' is committed, but the IDL will replace any uses of the provisional * UUID in the data to be to be committed by the UUID assigned by * ovsdb-server. * * Usually this function is used indirectly through one of the "insert" * functions generated by ovsdb-idlc. */ const struct ovsdb_idl_row * ovsdb_idl_txn_insert(struct ovsdb_idl_txn *txn, const struct ovsdb_idl_table_class *class, const struct uuid *uuid) { struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class); if (uuid) { ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid)); row->uuid = *uuid; } else { uuid_generate(&row->uuid); } row->table = ovsdb_idl_table_from_class(txn->idl, class); row->new = xmalloc(class->n_columns * sizeof *row->new); hmap_insert(&row->table->rows, &row->hmap_node, uuid_hash(&row->uuid)); hmap_insert(&txn->txn_rows, &row->txn_node, uuid_hash(&row->uuid)); return row; } static void ovsdb_idl_txn_abort_all(struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; HMAP_FOR_EACH (txn, hmap_node, &idl->outstanding_txns) { ovsdb_idl_txn_complete(txn, TXN_TRY_AGAIN); } } static struct ovsdb_idl_txn * ovsdb_idl_txn_find(struct ovsdb_idl *idl, const struct json *id) { struct ovsdb_idl_txn *txn; HMAP_FOR_EACH_WITH_HASH (txn, hmap_node, json_hash(id, 0), &idl->outstanding_txns) { if (json_equal(id, txn->request_id)) { return txn; } } return NULL; } static bool check_json_type(const struct json *json, enum json_type type, const char *name) { if (!json) { VLOG_WARN_RL(&syntax_rl, "%s is missing", name); return false; } else if (json->type != type) { VLOG_WARN_RL(&syntax_rl, "%s is %s instead of %s", name, json_type_to_string(json->type), json_type_to_string(type)); return false; } else { return true; } } static bool ovsdb_idl_txn_process_inc_reply(struct ovsdb_idl_txn *txn, const struct json_array *results) { struct json *count, *rows, *row, *column; struct shash *mutate, *select; if (txn->inc_index + 2 > results->n) { VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " "for increment (has %"PRIuSIZE", needs %u)", results->n, txn->inc_index + 2); return false; } /* We know that this is a JSON object because the loop in * ovsdb_idl_txn_process_reply() checked. */ mutate = json_object(results->elems[txn->inc_index]); count = shash_find_data(mutate, "count"); if (!check_json_type(count, JSON_INTEGER, "\"mutate\" reply \"count\"")) { return false; } if (count->u.integer != 1) { VLOG_WARN_RL(&syntax_rl, "\"mutate\" reply \"count\" is %lld instead of 1", count->u.integer); return false; } select = json_object(results->elems[txn->inc_index + 1]); rows = shash_find_data(select, "rows"); if (!check_json_type(rows, JSON_ARRAY, "\"select\" reply \"rows\"")) { return false; } if (rows->u.array.n != 1) { VLOG_WARN_RL(&syntax_rl, "\"select\" reply \"rows\" has %"PRIuSIZE" elements " "instead of 1", rows->u.array.n); return false; } row = rows->u.array.elems[0]; if (!check_json_type(row, JSON_OBJECT, "\"select\" reply row")) { return false; } column = shash_find_data(json_object(row), txn->inc_column); if (!check_json_type(column, JSON_INTEGER, "\"select\" reply inc column")) { return false; } txn->inc_new_value = column->u.integer; return true; } static bool ovsdb_idl_txn_process_insert_reply(struct ovsdb_idl_txn_insert *insert, const struct json_array *results) { static const struct ovsdb_base_type uuid_type = OVSDB_BASE_UUID_INIT; struct ovsdb_error *error; struct json *json_uuid; union ovsdb_atom uuid; struct shash *reply; if (insert->op_index >= results->n) { VLOG_WARN_RL(&syntax_rl, "reply does not contain enough operations " "for insert (has %"PRIuSIZE", needs %u)", results->n, insert->op_index); return false; } /* We know that this is a JSON object because the loop in * ovsdb_idl_txn_process_reply() checked. */ reply = json_object(results->elems[insert->op_index]); json_uuid = shash_find_data(reply, "uuid"); if (!check_json_type(json_uuid, JSON_ARRAY, "\"insert\" reply \"uuid\"")) { return false; } error = ovsdb_atom_from_json(&uuid, &uuid_type, json_uuid, NULL); if (error) { char *s = ovsdb_error_to_string(error); VLOG_WARN_RL(&syntax_rl, "\"insert\" reply \"uuid\" is not a JSON " "UUID: %s", s); free(s); ovsdb_error_destroy(error); return false; } insert->real = uuid.uuid; return true; } static bool ovsdb_idl_txn_process_reply(struct ovsdb_idl *idl, const struct jsonrpc_msg *msg) { struct ovsdb_idl_txn *txn; enum ovsdb_idl_txn_status status; txn = ovsdb_idl_txn_find(idl, msg->id); if (!txn) { return false; } if (msg->type == JSONRPC_ERROR) { status = TXN_ERROR; } else if (msg->result->type != JSON_ARRAY) { VLOG_WARN_RL(&syntax_rl, "reply to \"transact\" is not JSON array"); status = TXN_ERROR; } else { struct json_array *ops = &msg->result->u.array; int hard_errors = 0; int soft_errors = 0; int lock_errors = 0; size_t i; for (i = 0; i < ops->n; i++) { struct json *op = ops->elems[i]; if (op->type == JSON_NULL) { /* This isn't an error in itself but indicates that some prior * operation failed, so make sure that we know about it. */ soft_errors++; } else if (op->type == JSON_OBJECT) { struct json *error; error = shash_find_data(json_object(op), "error"); if (error) { if (error->type == JSON_STRING) { if (!strcmp(error->u.string, "timed out")) { soft_errors++; } else if (!strcmp(error->u.string, "not owner")) { lock_errors++; } else if (strcmp(error->u.string, "aborted")) { hard_errors++; ovsdb_idl_txn_set_error_json(txn, op); } } else { hard_errors++; ovsdb_idl_txn_set_error_json(txn, op); VLOG_WARN_RL(&syntax_rl, "\"error\" in reply is not JSON string"); } } } else { hard_errors++; ovsdb_idl_txn_set_error_json(txn, op); VLOG_WARN_RL(&syntax_rl, "operation reply is not JSON null or object"); } } if (!soft_errors && !hard_errors && !lock_errors) { struct ovsdb_idl_txn_insert *insert; if (txn->inc_table && !ovsdb_idl_txn_process_inc_reply(txn, ops)) { hard_errors++; } HMAP_FOR_EACH (insert, hmap_node, &txn->inserted_rows) { if (!ovsdb_idl_txn_process_insert_reply(insert, ops)) { hard_errors++; } } } status = (hard_errors ? TXN_ERROR : lock_errors ? TXN_NOT_LOCKED : soft_errors ? TXN_TRY_AGAIN : TXN_SUCCESS); } ovsdb_idl_txn_complete(txn, status); return true; } /* Returns the transaction currently active for 'row''s IDL. A transaction * must currently be active. */ struct ovsdb_idl_txn * ovsdb_idl_txn_get(const struct ovsdb_idl_row *row) { struct ovsdb_idl_txn *txn = row->table->idl->txn; ovs_assert(txn != NULL); return txn; } /* Returns the IDL on which 'txn' acts. */ struct ovsdb_idl * ovsdb_idl_txn_get_idl (struct ovsdb_idl_txn *txn) { return txn->idl; } /* Blocks until 'idl' successfully connects to the remote database and * retrieves its contents. */ void ovsdb_idl_get_initial_snapshot(struct ovsdb_idl *idl) { while (1) { ovsdb_idl_run(idl); if (ovsdb_idl_has_ever_connected(idl)) { return; } ovsdb_idl_wait(idl); poll_block(); } } /* If 'lock_name' is nonnull, configures 'idl' to obtain the named lock from * the database server and to avoid modifying the database when the lock cannot * be acquired (that is, when another client has the same lock). * * If 'lock_name' is NULL, drops the locking requirement and releases the * lock. */ void ovsdb_idl_set_lock(struct ovsdb_idl *idl, const char *lock_name) { ovs_assert(!idl->txn); ovs_assert(hmap_is_empty(&idl->outstanding_txns)); if (idl->lock_name && (!lock_name || strcmp(lock_name, idl->lock_name))) { /* Release previous lock. */ ovsdb_idl_send_unlock_request(idl); free(idl->lock_name); idl->lock_name = NULL; idl->is_lock_contended = false; } if (lock_name && !idl->lock_name) { /* Acquire new lock. */ idl->lock_name = xstrdup(lock_name); ovsdb_idl_send_lock_request(idl); } } /* Returns true if 'idl' is configured to obtain a lock and owns that lock. * * Locking and unlocking happens asynchronously from the database client's * point of view, so the information is only useful for optimization (e.g. if * the client doesn't have the lock then there's no point in trying to write to * the database). */ bool ovsdb_idl_has_lock(const struct ovsdb_idl *idl) { return idl->has_lock; } /* Returns true if 'idl' is configured to obtain a lock but the database server * has indicated that some other client already owns the requested lock. */ bool ovsdb_idl_is_lock_contended(const struct ovsdb_idl *idl) { return idl->is_lock_contended; } static void ovsdb_idl_update_has_lock(struct ovsdb_idl *idl, bool new_has_lock) { if (new_has_lock && !idl->has_lock) { if (idl->state == IDL_S_MONITORING) { idl->change_seqno++; } else { /* We're setting up a session, so don't signal that the database * changed. Finalizing the session will increment change_seqno * anyhow. */ } idl->is_lock_contended = false; } idl->has_lock = new_has_lock; } static void ovsdb_idl_send_lock_request__(struct ovsdb_idl *idl, const char *method, struct json **idp) { ovsdb_idl_update_has_lock(idl, false); json_destroy(idl->lock_request_id); idl->lock_request_id = NULL; if (jsonrpc_session_is_connected(idl->session)) { struct json *params; params = json_array_create_1(json_string_create(idl->lock_name)); jsonrpc_session_send(idl->session, jsonrpc_create_request(method, params, idp)); } } static void ovsdb_idl_send_lock_request(struct ovsdb_idl *idl) { ovsdb_idl_send_lock_request__(idl, "lock", &idl->lock_request_id); } static void ovsdb_idl_send_unlock_request(struct ovsdb_idl *idl) { ovsdb_idl_send_lock_request__(idl, "unlock", NULL); } static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *idl, const struct json *result) { bool got_lock; json_destroy(idl->lock_request_id); idl->lock_request_id = NULL; if (result->type == JSON_OBJECT) { const struct json *locked; locked = shash_find_data(json_object(result), "locked"); got_lock = locked && locked->type == JSON_TRUE; } else { got_lock = false; } ovsdb_idl_update_has_lock(idl, got_lock); if (!got_lock) { idl->is_lock_contended = true; } } static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *idl, const struct json *params, bool new_has_lock) { if (idl->lock_name && params->type == JSON_ARRAY && json_array(params)->n > 0 && json_array(params)->elems[0]->type == JSON_STRING) { const char *lock_name = json_string(json_array(params)->elems[0]); if (!strcmp(idl->lock_name, lock_name)) { ovsdb_idl_update_has_lock(idl, new_has_lock); if (!new_has_lock) { idl->is_lock_contended = true; } } } } void ovsdb_idl_loop_destroy(struct ovsdb_idl_loop *loop) { if (loop) { ovsdb_idl_destroy(loop->idl); } } struct ovsdb_idl_txn * ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop) { ovsdb_idl_run(loop->idl); loop->open_txn = (loop->committing_txn || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno ? NULL : ovsdb_idl_txn_create(loop->idl)); return loop->open_txn; } void ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop) { if (loop->open_txn) { loop->committing_txn = loop->open_txn; loop->open_txn = NULL; loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl); } struct ovsdb_idl_txn *txn = loop->committing_txn; if (txn) { enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn); if (status != TXN_INCOMPLETE) { switch (status) { case TXN_TRY_AGAIN: /* We want to re-evaluate the database when it's changed from * the contents that it had when we started the commit. (That * might have already happened.) */ loop->skip_seqno = loop->precommit_seqno; if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) { poll_immediate_wake(); } break; case TXN_SUCCESS: /* If the database has already changed since we started the * commit, re-evaluate it immediately to avoid missing a change * for a while. */ if (ovsdb_idl_get_seqno(loop->idl) != loop->precommit_seqno) { poll_immediate_wake(); } break; case TXN_UNCHANGED: case TXN_ABORTED: case TXN_NOT_LOCKED: case TXN_ERROR: break; case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); } ovsdb_idl_txn_destroy(txn); loop->committing_txn = NULL; } } ovsdb_idl_wait(loop->idl); } openvswitch-2.5.9/lib/PaxHeaders.82075/socket-util-unix.c0000644000000000000000000000013113534540071020001 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801424.969854812 openvswitch-2.5.9/lib/socket-util-unix.c0000644000175000017500000003047313534540071021477 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "socket-util.h" #include #include #include #include #include #include #include #include #include #include "fatal-signal.h" #include "random.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(socket_util_unix); /* #ifdefs make it a pain to maintain code: you have to try to build both ways. * Thus, this file compiles all of the code regardless of the target, by * writing "if (LINUX)" instead of "#ifdef __linux__". */ #ifdef __linux__ #define LINUX 1 #else #define LINUX 0 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 0 #endif /* Maximum length of the sun_path member in a struct sockaddr_un, excluding * space for a null terminator. */ #define MAX_UN_LEN (sizeof(((struct sockaddr_un *) 0)->sun_path) - 1) void xpipe(int fds[2]) { if (pipe(fds)) { VLOG_FATAL("failed to create pipe (%s)", ovs_strerror(errno)); } } void xpipe_nonblocking(int fds[2]) { xpipe(fds); xset_nonblocking(fds[0]); xset_nonblocking(fds[1]); } /* Drain all the data currently in the receive queue of a datagram socket (and * possibly additional data). There is no way to know how many packets are in * the receive queue, but we do know that the total number of bytes queued does * not exceed the receive buffer size, so we pull packets until none are left * or we've read that many bytes. */ int drain_rcvbuf(int fd) { int rcvbuf; rcvbuf = get_socket_rcvbuf(fd); if (rcvbuf < 0) { return -rcvbuf; } while (rcvbuf > 0) { /* In Linux, specifying MSG_TRUNC in the flags argument causes the * datagram length to be returned, even if that is longer than the * buffer provided. Thus, we can use a 1-byte buffer to discard the * incoming datagram and still be able to account how many bytes were * removed from the receive buffer. * * On other Unix-like OSes, MSG_TRUNC has no effect in the flags * argument. */ char buffer[LINUX ? 1 : 2048]; ssize_t n_bytes = recv(fd, buffer, sizeof buffer, MSG_TRUNC | MSG_DONTWAIT); if (n_bytes <= 0 || n_bytes >= rcvbuf) { break; } rcvbuf -= n_bytes; } return 0; } /* Attempts to shorten 'name' by opening a file descriptor for the directory * part of the name and indirecting through /proc/self/fd//. * On systems with Linux-like /proc, this works as long as isn't too * long. * * On success, returns 0 and stores the short name in 'short_name' and a * directory file descriptor to eventually be closed in '*dirfpd'. */ static int shorten_name_via_proc(const char *name, char short_name[MAX_UN_LEN + 1], int *dirfdp) { char *dir, *base; int dirfd; int len; if (!LINUX) { return ENAMETOOLONG; } dir = dir_name(name); dirfd = open(dir, O_DIRECTORY | O_RDONLY); if (dirfd < 0) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); int error = errno; VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, ovs_strerror(error)); free(dir); return error; } free(dir); base = base_name(name); len = snprintf(short_name, MAX_UN_LEN + 1, "/proc/self/fd/%d/%s", dirfd, base); free(base); if (len >= 0 && len <= MAX_UN_LEN) { *dirfdp = dirfd; return 0; } else { close(dirfd); return ENAMETOOLONG; } } /* Attempts to shorten 'name' by creating a symlink for the directory part of * the name and indirecting through /. This works on * systems that support symlinks, as long as isn't too long. * * On success, returns 0 and stores the short name in 'short_name' and the * symbolic link to eventually delete in 'linkname'. */ static int shorten_name_via_symlink(const char *name, char short_name[MAX_UN_LEN + 1], char linkname[MAX_UN_LEN + 1]) { char *abs, *dir, *base; const char *tmpdir; int error; int i; abs = abs_file_name(NULL, name); dir = dir_name(abs); base = base_name(abs); free(abs); tmpdir = getenv("TMPDIR"); if (tmpdir == NULL) { tmpdir = "/tmp"; } for (i = 0; i < 1000; i++) { int len; len = snprintf(linkname, MAX_UN_LEN + 1, "%s/ovs-un-c-%"PRIu32, tmpdir, random_uint32()); error = (len < 0 || len > MAX_UN_LEN ? ENAMETOOLONG : symlink(dir, linkname) ? errno : 0); if (error != EEXIST) { break; } } if (!error) { int len; fatal_signal_add_file_to_unlink(linkname); len = snprintf(short_name, MAX_UN_LEN + 1, "%s/%s", linkname, base); if (len < 0 || len > MAX_UN_LEN) { fatal_signal_unlink_file_now(linkname); error = ENAMETOOLONG; } } if (error) { linkname[0] = '\0'; } free(dir); free(base); return error; } /* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in * '*un_len' the size of the sockaddr_un. * * Returns 0 on success, otherwise a positive errno value. * * Uses '*dirfdp' and 'linkname' to store references to data when the caller no * longer needs to use 'un'. On success, freeing these references with * free_sockaddr_un() is mandatory to avoid a leak; on failure, freeing them is * unnecessary but harmless. */ static int make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len, int *dirfdp, char linkname[MAX_UN_LEN + 1]) { char short_name[MAX_UN_LEN + 1]; *dirfdp = -1; linkname[0] = '\0'; if (strlen(name) > MAX_UN_LEN) { /* 'name' is too long to fit in a sockaddr_un. Try a workaround. */ int error = shorten_name_via_proc(name, short_name, dirfdp); if (error == ENAMETOOLONG) { error = shorten_name_via_symlink(name, short_name, linkname); } if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "Unix socket name %s is longer than maximum " "%"PRIuSIZE" bytes", name, MAX_UN_LEN); return error; } name = short_name; } un->sun_family = AF_UNIX; ovs_strzcpy(un->sun_path, name, sizeof un->sun_path); *un_len = (offsetof(struct sockaddr_un, sun_path) + strlen (un->sun_path) + 1); return 0; } /* Clean up after make_sockaddr_un(). */ static void free_sockaddr_un(int dirfd, const char *linkname) { if (dirfd >= 0) { close(dirfd); } if (linkname[0]) { fatal_signal_unlink_file_now(linkname); } } /* Binds Unix domain socket 'fd' to a file with permissions 0700. */ static int bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len) { const mode_t mode = 0770; /* Allow both user and group access. */ if (LINUX) { /* On Linux, the fd's permissions become the file's permissions. * fchmod() does not affect other files, like umask() does. */ if (fchmod(fd, mode)) { return errno; } /* Must be after fchmod(). */ if (bind(fd, sun, sun_len)) { return errno; } return 0; } else { /* On FreeBSD and NetBSD, only the umask affects permissions. The * umask is process-wide rather than thread-specific, so we have to use * a subprocess for safety. */ pid_t pid = fork(); if (!pid) { umask(mode ^ 0777); _exit(bind(fd, sun, sun_len) ? errno : 0); } else if (pid > 0) { int status; int error; do { error = waitpid(pid, &status, 0) < 0 ? errno : 0; } while (error == EINTR); return (error ? error : WIFEXITED(status) ? WEXITSTATUS(status) : WIFSIGNALED(status) ? EINTR : ECHILD /* WTF? */); } else { return errno; } } } /* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or * SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and * connected to '*connect_path' (if 'connect_path' is non-null). If 'nonblock' * is true, the socket is made non-blocking. * * Returns the socket's fd if successful, otherwise a negative errno value. */ int make_unix_socket(int style, bool nonblock, const char *bind_path, const char *connect_path) { int error; int fd; fd = socket(PF_UNIX, style, 0); if (fd < 0) { return -errno; } /* Set nonblocking mode right away, if we want it. This prevents blocking * in connect(), if connect_path != NULL. (In turn, that's a corner case: * it will only happen if style is SOCK_STREAM or SOCK_SEQPACKET, and only * if a backlog of un-accepted connections has built up in the kernel.) */ if (nonblock) { error = set_nonblocking(fd); if (error) { goto error; } } if (bind_path) { char linkname[MAX_UN_LEN + 1]; struct sockaddr_un un; socklen_t un_len; int dirfd; if (unlink(bind_path) && errno != ENOENT) { VLOG_WARN("unlinking \"%s\": %s\n", bind_path, ovs_strerror(errno)); } fatal_signal_add_file_to_unlink(bind_path); error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd, linkname); if (!error) { error = bind_unix_socket(fd, (struct sockaddr *) &un, un_len); } free_sockaddr_un(dirfd, linkname); if (error) { goto error; } } if (connect_path) { char linkname[MAX_UN_LEN + 1]; struct sockaddr_un un; socklen_t un_len; int dirfd; error = make_sockaddr_un(connect_path, &un, &un_len, &dirfd, linkname); if (!error && connect(fd, (struct sockaddr*) &un, un_len) && errno != EINPROGRESS) { error = errno; } free_sockaddr_un(dirfd, linkname); if (error) { goto error; } } return fd; error: if (error == EAGAIN) { error = EPROTO; } if (bind_path) { fatal_signal_unlink_file_now(bind_path); } close(fd); return -error; } int get_unix_name_len(socklen_t sun_len) { return (sun_len >= offsetof(struct sockaddr_un, sun_path) ? sun_len - offsetof(struct sockaddr_un, sun_path) : 0); } /* Calls ioctl() on an AF_INET sock, passing the specified 'command' and * 'arg'. Returns 0 if successful, otherwise a positive errno value. */ int af_inet_ioctl(unsigned long int command, const void *arg) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int sock; if (ovsthread_once_start(&once)) { sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { int error = sock_errno(); VLOG_ERR("failed to create inet socket: %s", sock_strerror(error)); sock = -error; } ovsthread_once_done(&once); } return (sock < 0 ? -sock : ioctl(sock, command, arg) == -1 ? errno : 0); } int af_inet_ifreq_ioctl(const char *name, struct ifreq *ifr, unsigned long int cmd, const char *cmd_name) { int error; ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name); error = af_inet_ioctl(cmd, ifr); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name, ovs_strerror(error)); } return error; } openvswitch-2.5.9/lib/PaxHeaders.82075/netlink-socket.h0000644000000000000000000000013213534540071017515 xustar0030 mtime=1567801401.465681639 30 atime=1567801402.081686164 30 ctime=1567801424.985854931 openvswitch-2.5.9/lib/netlink-socket.h0000644000175000017500000003172213534540071021210 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef NETLINK_SOCKET_H #define NETLINK_SOCKET_H 1 /* Netlink socket definitions. * * This header file defines functions for working with Netlink sockets. Only * Linux natively supports Netlink sockets, but Netlink is well suited as a * basis for extensible low-level protocols, so it can make sense to implement * a Netlink layer on other systems. This doesn't have to be done in exactly * the same way as on Linux, as long as the implementation can support the * semantics that are important to Open vSwitch. See "Usage concepts" below * for more information. * * For Netlink protocol definitions, see netlink-protocol.h. For helper * functions for working with Netlink messages, see netlink.h. * * * Usage concepts * ============== * * Netlink is a datagram-based network protocol primarily for communication * between user processes and the kernel. Netlink is specified in RFC 3549, * "Linux Netlink as an IP Services Protocol". * * Netlink is not suitable for use in physical networks of heterogeneous * machines because host byte order is used throughout. * * The AF_NETLINK socket namespace is subdivided into statically numbered * protocols, e.g. NETLINK_ROUTE, NETLINK_NETFILTER, provided as the third * argument to the socket() function. Maintaining the assigned numbers became * a bit of a problem, so the "Generic Netlink" NETLINK_GENERIC protocol was * introduced to map between human-readable names and dynamically assigned * numbers. All recently introduced Netlink protocol messages in Linux * (including all of the Open vSwitch specific messages) fall under * NETLINK_GENERIC. The Netlink library provides the nl_lookup_genl_family() * function for translating a Generic Netlink name to a number. On Linux, this * queries the kernel Generic Netlink implementation, but on other systems it * might be easier to statically assign each of the names used by Open vSwitch * and then implement this function entirely in userspace. * * Each Netlink socket is distinguished by its Netlink PID, a 32-bit integer * that is analogous to a TCP or UDP port number. The kernel has PID 0. * * Most Netlink messages manage a kernel table of some kind, e.g. the kernel * routing table, ARP table, etc. Open vSwitch specific messages manage tables * of datapaths, ports within datapaths ("vports"), and flows within * datapaths. Open vSwitch also has messages related to network packets * received on vports, which aren't really a table. * * Datagram protocols over a physical network are typically unreliable: in UDP, * for example, messages can be dropped, delivered more than once, or delivered * out of order. In Linux, Netlink does not deliver messages out of order or * multiple times. In some cases it can drop messages, but the kernel * indicates when a message has been dropped. The description below of each * way Open vSwitch uses Netlink also explains how to work around dropped * messages. * * Open vSwitch uses Netlink in four characteristic ways: * * 1. Transactions. A transaction is analogous to a system call, an ioctl, * or an RPC: userspace sends a request to the kernel, which processes * the request synchronously and returns a reply to userspace. * (Sometimes there is no explicit reply, but even in that case userspace * will receive an immediate reply if there is an error.) * * nl_transact() is the primary interface for transactions over Netlink. * This function doesn't take a socket as a parameter because sockets do * not have any state related to transactions. * * Netlink uses 16-bit "length" fields extensively, which effectively * limits requests and replies to 64 kB. "Dumps" (see below) are one way * to work around this limit for replies. * * In the Linux implementation of Netlink transactions, replies can * sometimes be lost. When this happens, nl_transact() automatically * executes the transaction again. This means that it is important that * transactions be idempotent, or that the client be prepared to tolerate * that a transaction might actually execute more than once. * * The Linux implementation can execute several transactions at the same * time more efficiently than individually. nl_transact_multiple() * allows for this. The semantics are no different from executing each * of the transactions individually with nl_transact(). * * 2. Dumps. A dump asks the kernel to provide all of the information in a * table. It consists of a request and a reply, where the reply consists * of an arbitrary number of messages. Each message in the reply is * limited to 64 kB, as is the request, but the total size of the reply * can be many times larger. * * The reply to a dump is usually generated piece by piece, not * atomically. The reply can represent an inconsistent snapshot of the * table. This is especially likely if entries in the table were being * added or deleted or changing during the dump. * * nl_dump_start() begins a dump based on the caller-provided request and * initializes a "struct nl_dump" to identify the dump. Subsequent calls * to nl_dump_next() then obtain the reply, one message at a time. * Usually, each message gives information about some entry in a table, * e.g. one flow in the Open vSwitch flow table, or one route in a * routing table. nl_dump_done() ends the dump. * * Linux implements dumps so that messages in a reply do not get lost. * * 3. Multicast subscriptions. Most kernel Netlink implementations allow a * process to monitor changes to its table, by subscribing to a Netlink * multicast group dedicated to that table. Whenever the table's content * changes (e.g. an entry is added or deleted or modified), the Netlink * implementation sends a message to all sockets that subscribe to its * multicast group notifying it of details of the change. (This doesn't * require much extra work by the Netlink implementer because the message * is generally identical to the one sent as a reply to the request that * changed the table.) * * nl_sock_join_mcgroup() subscribes a socket to a multicast group, and * nl_sock_recv() reads notifications. * * If userspace doesn't read messages from a socket subscribed to a * multicast group quickly enough, then notification messages can pile up * in the socket's receive buffer. If this continues long enough, the * receive buffer will fill up and notifications will be lost. In that * case, nl_sock_recv() will return ENOBUFS. The client can then use a * dump to resynchronize with the table state. (A simple implementation * of multicast groups might take advantage of this by simply returning * ENOBUFS whenever a table changes, without implementing actual * notifications. This would cause lots of extra dumps, so it may not be * suitable as a production implementation.) * * 4. Unicast subscriptions (Open vSwitch specific). Userspace can assign * one or more Netlink PIDs to a vport as "upcall PIDs". When a packet * received on the vport does not match any flow in its datapath's flow * table, the kernel hashes some of the packet's headers, uses the hash * to select one of the PIDs, and sends the packet (encapsulated in an * Open vSwitch Netlink message) to the socket with the selected PID. * * nl_sock_recv() reads notifications sent this way. * * Specifically on Windows platform, the datapath needs to allocate a * queue for packets, and it does so only when userspace "subscribe"'s to * packets on that netlink socket. Before closing the netlink socket, * userspace needs to "unsubscribe" packets on that netlink socket. * * nl_sock_subscribe_packets() and nl_sock_unsubscribe_packets() are * Windows specific. * * Messages received this way can overflow, just like multicast * subscription messages, and they are reported the same way. Because * packet notification messages do not report the state of a table, there * is no way to recover the dropped packets; they are simply lost. * * The main reason to support multiple PIDs per vport is to increase * fairness, that is, to make it harder for a single high-flow-rate * sender to drown out lower rate sources. Multiple PIDs per vport might * also improve packet handling latency or flow setup rate, but that is * not the main goal. * * Old versions of the Linux kernel module supported only one PID per * vport, and userspace still copes with this, so a simple or early * implementation might only support one PID per vport too. * * * Thread-safety * ============= * * Most of the netlink functions are not fully thread-safe: Only a single * thread may use a given nl_sock or nl_dump at one time. The exceptions are: * * - nl_sock_recv() is conditionally thread-safe: it may be called from * different threads with the same nl_sock, but each caller must provide * an independent receive buffer. * * - nl_dump_next() is conditionally thread-safe: it may be called from * different threads with the same nl_dump, but each caller must provide * independent buffers. */ #include #include #include #include "ofpbuf.h" #include "ovs-atomic.h" #include "ovs-thread.h" struct nl_sock; #ifndef HAVE_NETLINK #ifndef _WIN32 #error "netlink-socket.h is only for hosts that support Netlink sockets" #endif #endif /* Netlink sockets. */ int nl_sock_create(int protocol, struct nl_sock **); int nl_sock_clone(const struct nl_sock *, struct nl_sock **); void nl_sock_destroy(struct nl_sock *); int nl_sock_join_mcgroup(struct nl_sock *, unsigned int multicast_group); int nl_sock_leave_mcgroup(struct nl_sock *, unsigned int multicast_group); #ifdef _WIN32 int nl_sock_subscribe_packets(struct nl_sock *sock); int nl_sock_unsubscribe_packets(struct nl_sock *sock); #endif int nl_sock_send(struct nl_sock *, const struct ofpbuf *, bool wait); int nl_sock_send_seq(struct nl_sock *, const struct ofpbuf *, uint32_t nlmsg_seq, bool wait); int nl_sock_recv(struct nl_sock *, struct ofpbuf *, bool wait); int nl_sock_drain(struct nl_sock *); void nl_sock_wait(const struct nl_sock *, short int events); #ifndef _WIN32 int nl_sock_fd(const struct nl_sock *); #endif uint32_t nl_sock_pid(const struct nl_sock *); /* Batching transactions. */ struct nl_transaction { /* Filled in by client. */ struct ofpbuf *request; /* Request to send. */ /* The client must initialize 'reply' to one of: * * - NULL, if it does not care to examine the reply. * * - Otherwise, to an ofpbuf with a memory allocation of at least * NLMSG_HDRLEN bytes. */ struct ofpbuf *reply; /* Reply (empty if reply was an error code). */ int error; /* Positive errno value, 0 if no error. */ }; /* Transactions without an allocated socket. */ int nl_transact(int protocol, const struct ofpbuf *request, struct ofpbuf **replyp); void nl_transact_multiple(int protocol, struct nl_transaction **, size_t n); /* Table dumping. */ #define NL_DUMP_BUFSIZE 4096 struct nl_dump { /* These members are immutable during the lifetime of the nl_dump. */ struct nl_sock *sock; /* Socket being dumped. */ uint32_t nl_seq; /* Expected nlmsg_seq for replies. */ /* 'mutex' protects 'status' and serializes access to 'sock'. */ struct ovs_mutex mutex; /* Protects 'status', synchronizes recv(). */ int status OVS_GUARDED; /* 0: dump in progress, * positive errno: dump completed with error, * EOF: dump completed successfully. */ }; void nl_dump_start(struct nl_dump *, int protocol, const struct ofpbuf *request); bool nl_dump_next(struct nl_dump *, struct ofpbuf *reply, struct ofpbuf *buf); int nl_dump_done(struct nl_dump *); /* Miscellaneous */ int nl_lookup_genl_family(const char *name, int *number); int nl_lookup_genl_mcgroup(const char *family_name, const char *group_name, unsigned int *multicast_group); #endif /* netlink-socket.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/sflow_agent.c0000644000000000000000000000013113534540071017065 xustar0029 mtime=1567801401.58968255 30 atime=1567801402.097686281 30 ctime=1567801425.009855106 openvswitch-2.5.9/lib/sflow_agent.c0000644000175000017500000004250313534540071020560 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #include "sflow_api.h" #include "util.h" static void * sflAlloc(SFLAgent *agent, size_t bytes); static void sflFree(SFLAgent *agent, void *obj); static void sfl_agent_jumpTableAdd(SFLAgent *agent, SFLSampler *sampler); static void sfl_agent_jumpTableRemove(SFLAgent *agent, SFLSampler *sampler); /*________________--------------------------__________________ ________________ sfl_agent_init __________________ ----------------__________________________------------------ */ void sfl_agent_init(SFLAgent *agent, SFLAddress *myIP, /* IP address of this agent in net byte order */ u_int32_t subId, /* agent_sub_id */ time_t bootTime, /* agent boot time */ time_t now, /* time now */ void *magic, /* ptr to pass back in logging and alloc fns */ allocFn_t allocFn, freeFn_t freeFn, errorFn_t errorFn, sendFn_t sendFn) { /* first clear everything */ memset(agent, 0, sizeof(*agent)); /* now copy in the parameters */ agent->myIP = *myIP; /* structure copy */ agent->subId = subId; agent->bootTime = bootTime; agent->now = now; agent->magic = magic; agent->allocFn = allocFn; agent->freeFn = freeFn; agent->errorFn = errorFn; agent->sendFn = sendFn; #ifdef SFLOW_DO_SOCKET if(sendFn == NULL) { /* open the socket - really need one for v4 and another for v6? */ if((agent->receiverSocket4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) sfl_agent_sysError(agent, "agent", "IPv4 socket open failed"); if((agent->receiverSocket6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) sfl_agent_sysError(agent, "agent", "IPv6 socket open failed"); } #endif } /*_________________---------------------------__________________ _________________ sfl_agent_release __________________ -----------------___________________________------------------ */ void sfl_agent_release(SFLAgent *agent) { /* release and free the samplers, pollers and receivers */ SFLSampler *sm = agent->samplers; SFLPoller *pl = agent->pollers; SFLReceiver *rcv = agent->receivers; for(; sm != NULL; ) { SFLSampler *nextSm = sm->nxt; sflFree(agent, sm); sm = nextSm; } agent->samplers = NULL; for(; pl != NULL; ) { SFLPoller *nextPl = pl->nxt; sflFree(agent, pl); pl = nextPl; } agent->pollers = NULL; for(; rcv != NULL; ) { SFLReceiver *nextRcv = rcv->nxt; sflFree(agent, rcv); rcv = nextRcv; } agent->receivers = NULL; #ifdef SFLOW_DO_SOCKET /* close the sockets */ if(agent->receiverSocket4 > 0) close(agent->receiverSocket4); if(agent->receiverSocket6 > 0) close(agent->receiverSocket6); #endif } /*_________________---------------------------__________________ _________________ sfl_agent_set_* __________________ -----------------___________________________------------------ */ void sfl_agent_set_agentAddress(SFLAgent *agent, SFLAddress *addr) { if(addr && memcmp(addr, &agent->myIP, sizeof(agent->myIP)) != 0) { /* change of address */ agent->myIP = *addr; /* structure copy */ /* reset sequence numbers here? */ } } void sfl_agent_set_agentSubId(SFLAgent *agent, u_int32_t subId) { if(subId != agent->subId) { /* change of subId */ agent->subId = subId; /* reset sequence numbers here? */ } } /*_________________---------------------------__________________ _________________ sfl_agent_tick __________________ -----------------___________________________------------------ */ void sfl_agent_tick(SFLAgent *agent, time_t now) { SFLReceiver *rcv = agent->receivers; SFLSampler *sm = agent->samplers; SFLPoller *pl = agent->pollers; agent->now = now; /* receivers use ticks to flush send data */ for(; rcv != NULL; rcv = rcv->nxt) sfl_receiver_tick(rcv, now); /* samplers use ticks to decide when they are sampling too fast */ for(; sm != NULL; sm = sm->nxt) sfl_sampler_tick(sm, now); /* pollers use ticks to decide when to ask for counters */ for(; pl != NULL; pl = pl->nxt) sfl_poller_tick(pl, now); } /*_________________---------------------------__________________ _________________ sfl_agent_addReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_addReceiver(SFLAgent *agent) { SFLReceiver *rcv = (SFLReceiver *)sflAlloc(agent, sizeof(SFLReceiver)); sfl_receiver_init(rcv, agent); /* add to end of list - to preserve the receiver index numbers for existing receivers */ { SFLReceiver *r, *prev = NULL; for(r = agent->receivers; r != NULL; prev = r, r = r->nxt); if(prev) prev->nxt = rcv; else agent->receivers = rcv; rcv->nxt = NULL; } return rcv; } /*_________________---------------------------__________________ _________________ sfl_dsi_compare __________________ -----------------___________________________------------------ Note that if there is a mixture of ds_classes for this agent, then the simple numeric comparison may not be correct - the sort order (for the purposes of the SNMP MIB) should really be determined by the OID that these numeric ds_class numbers are a shorthand for. For example, ds_class == 0 means ifIndex, which is the oid "1.3.6.1.2.1.2.2.1" */ static inline int sfl_dsi_compare(SFLDataSource_instance *pdsi1, SFLDataSource_instance *pdsi2) { /* could have used just memcmp(), but not sure if that would give the right answer on little-endian platforms. Safer to be explicit... */ int cmp = pdsi2->ds_class - pdsi1->ds_class; if(cmp == 0) cmp = pdsi2->ds_index - pdsi1->ds_index; if(cmp == 0) cmp = pdsi2->ds_instance - pdsi1->ds_instance; return cmp; } /*_________________---------------------------__________________ _________________ sfl_agent_addSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_addSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* Keep the list sorted. */ SFLSampler *prev = NULL, *sm = agent->samplers; for(; sm != NULL; prev = sm, sm = sm->nxt) { int64_t cmp = sfl_dsi_compare(pdsi, &sm->dsi); if(cmp == 0) return sm; /* found - return existing one */ if(cmp < 0) break; /* insert here */ } /* either we found the insert point, or reached the end of the list...*/ { SFLSampler *newsm = (SFLSampler *)sflAlloc(agent, sizeof(SFLSampler)); sfl_sampler_init(newsm, agent, pdsi); if(prev) prev->nxt = newsm; else agent->samplers = newsm; newsm->nxt = sm; /* see if we should go in the ifIndex jumpTable */ if(SFL_DS_CLASS(newsm->dsi) == 0) { SFLSampler *test = sfl_agent_getSamplerByIfIndex(agent, SFL_DS_INDEX(newsm->dsi)); if(test && (SFL_DS_INSTANCE(newsm->dsi) < SFL_DS_INSTANCE(test->dsi))) { /* replace with this new one because it has a lower ds_instance number */ sfl_agent_jumpTableRemove(agent, test); test = NULL; } if(test == NULL) sfl_agent_jumpTableAdd(agent, newsm); } return newsm; } } /*_________________---------------------------__________________ _________________ sfl_agent_addPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_addPoller(SFLAgent *agent, SFLDataSource_instance *pdsi, void *magic, /* ptr to pass back in getCountersFn() */ getCountersFn_t getCountersFn) { /* keep the list sorted */ SFLPoller *prev = NULL, *pl = agent->pollers; for(; pl != NULL; prev = pl, pl = pl->nxt) { int64_t cmp = sfl_dsi_compare(pdsi, &pl->dsi); if(cmp == 0) return pl; /* found - return existing one */ if(cmp < 0) break; /* insert here */ } /* either we found the insert point, or reached the end of the list... */ { SFLPoller *newpl = (SFLPoller *)sflAlloc(agent, sizeof(SFLPoller)); sfl_poller_init(newpl, agent, pdsi, magic, getCountersFn); if(prev) prev->nxt = newpl; else agent->pollers = newpl; newpl->nxt = pl; return newpl; } } /*_________________---------------------------__________________ _________________ sfl_agent_removeSampler __________________ -----------------___________________________------------------ */ int sfl_agent_removeSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* find it, unlink it and free it */ SFLSampler *prev = NULL, *sm = agent->samplers; for(; sm != NULL; prev = sm, sm = sm->nxt) { if(sfl_dsi_compare(pdsi, &sm->dsi) == 0) { if(prev == NULL) agent->samplers = sm->nxt; else prev->nxt = sm->nxt; sfl_agent_jumpTableRemove(agent, sm); sflFree(agent, sm); return 1; } } /* not found */ return 0; } /*_________________---------------------------__________________ _________________ sfl_agent_removePoller __________________ -----------------___________________________------------------ */ int sfl_agent_removePoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* find it, unlink it and free it */ SFLPoller *prev = NULL, *pl = agent->pollers; for(; pl != NULL; prev = pl, pl = pl->nxt) { if(sfl_dsi_compare(pdsi, &pl->dsi) == 0) { if(prev == NULL) agent->pollers = pl->nxt; else prev->nxt = pl->nxt; sflFree(agent, pl); return 1; } } /* not found */ return 0; } /*_________________--------------------------------__________________ _________________ sfl_agent_jumpTableAdd __________________ -----------------________________________________------------------ */ static void sfl_agent_jumpTableAdd(SFLAgent *agent, SFLSampler *sampler) { u_int32_t hashIndex = SFL_DS_INDEX(sampler->dsi) % SFL_HASHTABLE_SIZ; sampler->hash_nxt = agent->jumpTable[hashIndex]; agent->jumpTable[hashIndex] = sampler; } /*_________________--------------------------------__________________ _________________ sfl_agent_jumpTableRemove __________________ -----------------________________________________------------------ */ static void sfl_agent_jumpTableRemove(SFLAgent *agent, SFLSampler *sampler) { u_int32_t hashIndex = SFL_DS_INDEX(sampler->dsi) % SFL_HASHTABLE_SIZ; SFLSampler *search = agent->jumpTable[hashIndex], *prev = NULL; for( ; search != NULL; prev = search, search = search->hash_nxt) if(search == sampler) break; if(search) { // found - unlink if(prev) prev->hash_nxt = search->hash_nxt; else agent->jumpTable[hashIndex] = search->hash_nxt; search->hash_nxt = NULL; } } /*_________________--------------------------------__________________ _________________ sfl_agent_getSamplerByIfIndex __________________ -----------------________________________________------------------ fast lookup (pointers cached in hash table). If there are multiple sampler instances for a given ifIndex, then this fn will return the one with the lowest instance number. Since the samplers list is sorted, this means the other instances will be accesible by following the sampler->nxt pointer (until the ds_class or ds_index changes). This is helpful if you need to offer the same flowSample to multiple samplers. */ SFLSampler *sfl_agent_getSamplerByIfIndex(SFLAgent *agent, u_int32_t ifIndex) { SFLSampler *search = agent->jumpTable[ifIndex % SFL_HASHTABLE_SIZ]; for( ; search != NULL; search = search->hash_nxt) if(SFL_DS_INDEX(search->dsi) == ifIndex) break; return search; } /*_________________---------------------------__________________ _________________ sfl_agent_getSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_getSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* find it and return it */ SFLSampler *sm = agent->samplers; for(; sm != NULL; sm = sm->nxt) if(sfl_dsi_compare(pdsi, &sm->dsi) == 0) return sm; /* not found */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_getPoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* find it and return it */ SFLPoller *pl = agent->pollers; for(; pl != NULL; pl = pl->nxt) if(sfl_dsi_compare(pdsi, &pl->dsi) == 0) return pl; /* not found */ return NULL; } /*_________________-----------------------------------__________________ _________________ sfl_agent_getPollerByBridgePort __________________ -----------------___________________________________------------------ */ SFLPoller *sfl_agent_getPollerByBridgePort(SFLAgent *agent, uint32_t port_no) { /* find it and return it */ SFLPoller *pl = agent->pollers; for(; pl != NULL; pl = pl->nxt) if(pl->bridgePort == port_no) return pl; /* not found */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_getReceiver(SFLAgent *agent, u_int32_t receiverIndex) { u_int32_t rcvIdx = 0; SFLReceiver *rcv = agent->receivers; for(; rcv != NULL; rcv = rcv->nxt) if(receiverIndex == ++rcvIdx) return rcv; /* not found - ran off the end of the table */ return NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextSampler __________________ -----------------___________________________------------------ */ SFLSampler *sfl_agent_getNextSampler(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* return the one lexograpically just after it - assume they are sorted correctly according to the lexographical ordering of the object ids */ SFLSampler *sm = sfl_agent_getSampler(agent, pdsi); return sm ? sm->nxt : NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextPoller __________________ -----------------___________________________------------------ */ SFLPoller *sfl_agent_getNextPoller(SFLAgent *agent, SFLDataSource_instance *pdsi) { /* return the one lexograpically just after it - assume they are sorted correctly according to the lexographical ordering of the object ids */ SFLPoller *pl = sfl_agent_getPoller(agent, pdsi); return pl ? pl->nxt : NULL; } /*_________________---------------------------__________________ _________________ sfl_agent_getNextReceiver __________________ -----------------___________________________------------------ */ SFLReceiver *sfl_agent_getNextReceiver(SFLAgent *agent, u_int32_t receiverIndex) { return sfl_agent_getReceiver(agent, receiverIndex + 1); } /*_________________---------------------------__________________ _________________ sfl_agent_resetReceiver __________________ -----------------___________________________------------------ */ void sfl_agent_resetReceiver(SFLAgent *agent, SFLReceiver *receiver) { /* tell samplers and pollers to stop sending to this receiver */ /* first get his receiverIndex */ u_int32_t rcvIdx = 0; SFLReceiver *rcv = agent->receivers; for(; rcv != NULL; rcv = rcv->nxt) { rcvIdx++; /* thanks to Diego Valverde for pointing out this bugfix */ if(rcv == receiver) { /* now tell anyone that is using it to stop */ SFLSampler *sm = agent->samplers; SFLPoller *pl = agent->pollers; for(; sm != NULL; sm = sm->nxt) if(sfl_sampler_get_sFlowFsReceiver(sm) == rcvIdx) sfl_sampler_set_sFlowFsReceiver(sm, 0); for(; pl != NULL; pl = pl->nxt) if(sfl_poller_get_sFlowCpReceiver(pl) == rcvIdx) sfl_poller_set_sFlowCpReceiver(pl, 0); break; } } } /*_________________---------------------------__________________ _________________ sfl_agent_error __________________ -----------------___________________________------------------ */ #define MAX_ERRMSG_LEN 1000 void sfl_agent_error(SFLAgent *agent, char *modName, char *msg) { char errm[MAX_ERRMSG_LEN]; snprintf(errm, sizeof errm, "sfl_agent_error: %s: %s\n", modName, msg); if(agent->errorFn) (*agent->errorFn)(agent->magic, agent, errm); else { fprintf(stderr, "%s\n", errm); fflush(stderr); } } /*_________________---------------------------__________________ _________________ sfl_agent_sysError __________________ -----------------___________________________------------------ */ void sfl_agent_sysError(SFLAgent *agent, char *modName, char *msg) { char errm[MAX_ERRMSG_LEN]; snprintf(errm, sizeof errm, "sfl_agent_sysError: %s: %s (errno = %d - %s)\n", modName, msg, errno, ovs_strerror(errno)); if(agent->errorFn) (*agent->errorFn)(agent->magic, agent, errm); else { fprintf(stderr, "%s\n", errm); fflush(stderr); } } /*_________________---------------------------__________________ _________________ alloc and free __________________ -----------------___________________________------------------ */ static void * sflAlloc(SFLAgent *agent, size_t bytes) { if(agent->allocFn) return (*agent->allocFn)(agent->magic, agent, bytes); else return SFL_ALLOC(bytes); } static void sflFree(SFLAgent *agent, void *obj) { if(agent->freeFn) (*agent->freeFn)(agent->magic, agent, obj); else SFL_FREE(obj); } openvswitch-2.5.9/lib/PaxHeaders.82075/sflow.h0000644000000000000000000000013213534540071015715 xustar0030 mtime=1567801401.585682521 30 atime=1567801402.097686281 30 ctime=1567801425.009855106 openvswitch-2.5.9/lib/sflow.h0000644000175000017500000005543413534540071017416 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2002-2009 InMon Corp. Licensed under the terms of either the * Sun Industry Standards Source License 1.1, that is available at: * http://host-sflow.sourceforge.net/sissl.html * or the InMon sFlow License, that is available at: * http://www.inmon.com/technology/sflowlicense.txt */ #ifndef SFLOW_H #define SFLOW_H 1 #ifdef _WIN32 #include "windefs.h" #endif #include "openvswitch/types.h" typedef enum { SFL_DSCLASS_IFINDEX = 0, SFL_DSCLASS_VLAN = 1, SFL_DSCLASS_PHYSICAL_ENTITY = 2, SFL_DSCLASS_LOGICAL_ENTITY = 3 } SFL_DSCLASS; enum SFLAddress_type { SFLADDRESSTYPE_IP_V4 = 1, SFLADDRESSTYPE_IP_V6 = 2 }; typedef struct { u_int32_t addr; } SFLIPv4; typedef struct { u_char addr[16]; } SFLIPv6; typedef union _SFLAddress_value { SFLIPv4 ip_v4; SFLIPv6 ip_v6; } SFLAddress_value; typedef struct _SFLAddress { u_int32_t type; /* enum SFLAddress_type */ SFLAddress_value address; } SFLAddress; /* Packet header data */ #define SFL_DEFAULT_HEADER_SIZE 128 #define SFL_DEFAULT_COLLECTOR_PORT 6343 #define SFL_DEFAULT_SAMPLING_RATE 400 #define SFL_DEFAULT_POLLING_INTERVAL 30 /* The header protocol describes the format of the sampled header */ enum SFLHeader_protocol { SFLHEADER_ETHERNET_ISO8023 = 1, SFLHEADER_ISO88024_TOKENBUS = 2, SFLHEADER_ISO88025_TOKENRING = 3, SFLHEADER_FDDI = 4, SFLHEADER_FRAME_RELAY = 5, SFLHEADER_X25 = 6, SFLHEADER_PPP = 7, SFLHEADER_SMDS = 8, SFLHEADER_AAL5 = 9, SFLHEADER_AAL5_IP = 10, /* e.g. Cisco AAL5 mux */ SFLHEADER_IPv4 = 11, SFLHEADER_IPv6 = 12, SFLHEADER_MPLS = 13 }; /* raw sampled header */ typedef struct _SFLSampled_header { u_int32_t header_protocol; /* (enum SFLHeader_protocol) */ u_int32_t frame_length; /* Original length of packet before sampling */ u_int32_t stripped; /* header/trailer bytes stripped by sender */ u_int32_t header_length; /* length of sampled header bytes to follow */ u_int8_t *header_bytes; /* Header bytes */ } SFLSampled_header; /* decoded ethernet header */ typedef struct _SFLSampled_ethernet { u_int32_t eth_len; /* The length of the MAC packet excluding lower layer encapsulations */ struct eth_addr src_mac; /* 6 bytes */ u_int8_t pad1[2]; /* 2 pad */ struct eth_addr dst_mac; u_int8_t pad2[2]; u_int32_t eth_type; } SFLSampled_ethernet; /* decoded IP version 4 header */ typedef struct _SFLSampled_ipv4 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ SFLIPv4 src_ip; /* Source IP Address */ SFLIPv4 dst_ip; /* Destination IP Address */ u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t tos; /* IP type of service */ } SFLSampled_ipv4; /* decoded IP version 6 data */ typedef struct _SFLSampled_ipv6 { u_int32_t length; /* The length of the IP packet excluding lower layer encapsulations */ u_int32_t protocol; /* IP Protocol type (for example, TCP = 6, UDP = 17) */ SFLIPv6 src_ip; /* Source IP Address */ SFLIPv6 dst_ip; /* Destination IP Address */ u_int32_t src_port; /* TCP/UDP source port number or equivalent */ u_int32_t dst_port; /* TCP/UDP destination port number or equivalent */ u_int32_t tcp_flags; /* TCP flags */ u_int32_t priority; /* IP priority */ } SFLSampled_ipv6; /* Extended data types */ /* Extended switch data */ typedef struct _SFLExtended_switch { u_int32_t src_vlan; /* The 802.1Q VLAN id of incomming frame */ u_int32_t src_priority; /* The 802.1p priority */ u_int32_t dst_vlan; /* The 802.1Q VLAN id of outgoing frame */ u_int32_t dst_priority; /* The 802.1p priority */ } SFLExtended_switch; /* Extended router data */ typedef struct _SFLExtended_router { SFLAddress nexthop; /* IP address of next hop router */ u_int32_t src_mask; /* Source address prefix mask bits */ u_int32_t dst_mask; /* Destination address prefix mask bits */ } SFLExtended_router; /* Extended gateway data */ enum SFLExtended_as_path_segment_type { SFLEXTENDED_AS_SET = 1, /* Unordered set of ASs */ SFLEXTENDED_AS_SEQUENCE = 2 /* Ordered sequence of ASs */ }; typedef struct _SFLExtended_as_path_segment { u_int32_t type; /* enum SFLExtended_as_path_segment_type */ u_int32_t length; /* number of AS numbers in set/sequence */ union { u_int32_t *set; u_int32_t *seq; } as; } SFLExtended_as_path_segment; typedef struct _SFLExtended_gateway { SFLAddress nexthop; /* Address of the border router that should be used for the destination network */ u_int32_t as; /* AS number for this gateway */ u_int32_t src_as; /* AS number of source (origin) */ u_int32_t src_peer_as; /* AS number of source peer */ u_int32_t dst_as_path_segments; /* number of segments in path */ SFLExtended_as_path_segment *dst_as_path; /* list of seqs or sets */ u_int32_t communities_length; /* number of communities */ u_int32_t *communities; /* set of communities */ u_int32_t localpref; /* LocalPref associated with this route */ } SFLExtended_gateway; typedef struct _SFLString { u_int32_t len; char *str; } SFLString; /* Extended user data */ typedef struct _SFLExtended_user { u_int32_t src_charset; /* MIBEnum value of character set used to encode a string - See RFC 2978 Where possible UTF-8 encoding (MIBEnum=106) should be used. A value of zero indicates an unknown encoding. */ SFLString src_user; u_int32_t dst_charset; SFLString dst_user; } SFLExtended_user; /* Extended URL data */ enum SFLExtended_url_direction { SFLEXTENDED_URL_SRC = 1, /* URL is associated with source address */ SFLEXTENDED_URL_DST = 2 /* URL is associated with destination address */ }; typedef struct _SFLExtended_url { u_int32_t direction; /* enum SFLExtended_url_direction */ SFLString url; /* URL associated with the packet flow. Must be URL encoded */ SFLString host; /* The host field from the HTTP header */ } SFLExtended_url; /* Extended MPLS data */ typedef struct _SFLLabelStack { u_int32_t depth; u_int32_t *stack; /* first entry is top of stack - see RFC 3032 for encoding */ } SFLLabelStack; typedef struct _SFLExtended_mpls { SFLAddress nextHop; /* Address of the next hop */ SFLLabelStack in_stack; SFLLabelStack out_stack; } SFLExtended_mpls; /* Extended NAT data Packet header records report addresses as seen at the sFlowDataSource. The extended_nat structure reports on translated source and/or destination addesses for this packet. If an address was not translated it should be equal to that reported for the header. */ typedef struct _SFLExtended_nat { SFLAddress src; /* Source address */ SFLAddress dst; /* Destination address */ } SFLExtended_nat; /* additional Extended MPLS stucts */ typedef struct _SFLExtended_mpls_tunnel { SFLString tunnel_lsp_name; /* Tunnel name */ u_int32_t tunnel_id; /* Tunnel ID */ u_int32_t tunnel_cos; /* Tunnel COS value */ } SFLExtended_mpls_tunnel; typedef struct _SFLExtended_mpls_vc { SFLString vc_instance_name; /* VC instance name */ u_int32_t vll_vc_id; /* VLL/VC instance ID */ u_int32_t vc_label_cos; /* VC Label COS value */ } SFLExtended_mpls_vc; /* Extended MPLS FEC - Definitions from MPLS-FTN-STD-MIB mplsFTNTable */ typedef struct _SFLExtended_mpls_FTN { SFLString mplsFTNDescr; u_int32_t mplsFTNMask; } SFLExtended_mpls_FTN; /* Extended MPLS LVP FEC - Definition from MPLS-LDP-STD-MIB mplsFecTable Note: mplsFecAddrType, mplsFecAddr information available from packet header */ typedef struct _SFLExtended_mpls_LDP_FEC { u_int32_t mplsFecAddrPrefixLength; } SFLExtended_mpls_LDP_FEC; /* Extended VLAN tunnel information Record outer VLAN encapsulations that have been stripped. extended_vlantunnel information should only be reported if all the following conditions are satisfied: 1. The packet has nested vlan tags, AND 2. The reporting device is VLAN aware, AND 3. One or more VLAN tags have been stripped, either because they represent proprietary encapsulations, or because switch hardware automatically strips the outer VLAN encapsulation. Reporting extended_vlantunnel information is not a substitute for reporting extended_switch information. extended_switch data must always be reported to describe the ingress/egress VLAN information for the packet. The extended_vlantunnel information only applies to nested VLAN tags, and then only when one or more tags has been stripped. */ typedef SFLLabelStack SFLVlanStack; typedef struct _SFLExtended_vlan_tunnel { SFLVlanStack stack; /* List of stripped 802.1Q TPID/TCI layers. Each TPID,TCI pair is represented as a single 32 bit integer. Layers listed from outermost to innermost. */ } SFLExtended_vlan_tunnel; typedef struct _SFLExtended_vni { uint32_t vni; /* virtual network identifier */ } SFLExtended_vni; enum SFLFlow_type_tag { /* enterprise = 0, format = ... */ SFLFLOW_HEADER = 1, /* Packet headers are sampled */ SFLFLOW_ETHERNET = 2, /* MAC layer information */ SFLFLOW_IPV4 = 3, /* IP version 4 data */ SFLFLOW_IPV6 = 4, /* IP version 6 data */ SFLFLOW_EX_SWITCH = 1001, /* Extended switch information */ SFLFLOW_EX_ROUTER = 1002, /* Extended router information */ SFLFLOW_EX_GATEWAY = 1003, /* Extended gateway router information */ SFLFLOW_EX_USER = 1004, /* Extended TACAS/RADIUS user information */ SFLFLOW_EX_URL = 1005, /* Extended URL information */ SFLFLOW_EX_MPLS = 1006, /* Extended MPLS information */ SFLFLOW_EX_NAT = 1007, /* Extended NAT information */ SFLFLOW_EX_MPLS_TUNNEL = 1008, /* additional MPLS information */ SFLFLOW_EX_MPLS_VC = 1009, SFLFLOW_EX_MPLS_FTN = 1010, SFLFLOW_EX_MPLS_LDP_FEC = 1011, SFLFLOW_EX_VLAN_TUNNEL = 1012, /* VLAN stack */ SFLFLOW_EX_IPV4_TUNNEL_EGRESS = 1023, /* http://sflow.org/sflow_tunnels.txt */ SFLFLOW_EX_IPV4_TUNNEL_INGRESS = 1024, SFLFLOW_EX_VNI_EGRESS = 1029, SFLFLOW_EX_VNI_INGRESS = 1030, }; typedef union _SFLFlow_type { SFLSampled_header header; SFLSampled_ethernet ethernet; SFLSampled_ipv4 ipv4; SFLSampled_ipv6 ipv6; SFLExtended_switch sw; SFLExtended_router router; SFLExtended_gateway gateway; SFLExtended_user user; SFLExtended_url url; SFLExtended_mpls mpls; SFLExtended_nat nat; SFLExtended_mpls_tunnel mpls_tunnel; SFLExtended_mpls_vc mpls_vc; SFLExtended_mpls_FTN mpls_ftn; SFLExtended_mpls_LDP_FEC mpls_ldp_fec; SFLExtended_vlan_tunnel vlan_tunnel; SFLExtended_vni tunnel_vni; } SFLFlow_type; typedef struct _SFLFlow_sample_element { struct _SFLFlow_sample_element *nxt; u_int32_t tag; /* SFLFlow_type_tag */ u_int32_t length; SFLFlow_type flowType; } SFLFlow_sample_element; enum SFL_sample_tag { SFLFLOW_SAMPLE = 1, /* enterprise = 0 : format = 1 */ SFLCOUNTERS_SAMPLE = 2, /* enterprise = 0 : format = 2 */ SFLFLOW_SAMPLE_EXPANDED = 3, /* enterprise = 0 : format = 3 */ SFLCOUNTERS_SAMPLE_EXPANDED = 4 /* enterprise = 0 : format = 4 */ }; /* Format of a single flow sample */ typedef struct _SFLFlow_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t source_id; /* fsSourceId */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. Set most significant bit to indicate multiple destination interfaces (i.e. in case of broadcast or multicast) and set lower order bits to indicate number of destination interfaces. Examples: 0x00000002 indicates ifIndex = 2 0x00000000 ifIndex unknown. 0x80000007 indicates a packet sent to 7 interfaces. 0x80000000 indicates a packet sent to an unknown number of interfaces greater than 1.*/ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample; /* same thing, but the expanded version (for full 32-bit ifIndex numbers) */ typedef struct _SFLFlow_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 1 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each flow sample generated */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t sampling_rate; /* fsPacketSamplingRate */ u_int32_t sample_pool; /* Total number of packets that could have been sampled (i.e. packets skipped by sampling process + total number of samples) */ u_int32_t drops; /* Number of times a packet was dropped due to lack of resources */ u_int32_t inputFormat; /* EXPANDED */ u_int32_t input; /* SNMP ifIndex of input interface. 0 if interface is not known. */ u_int32_t outputFormat; /* EXPANDED */ u_int32_t output; /* SNMP ifIndex of output interface, 0 if interface is not known. */ u_int32_t num_elements; SFLFlow_sample_element *elements; } SFLFlow_sample_expanded; /* Counter types */ #define SFL_UNDEF_COUNTER(c) c=-1 #define SFL_UNDEF_GAUGE(c) c=0 /* Generic interface counters - see RFC 1573, 2233 */ typedef struct _SFLIf_counters { u_int32_t ifIndex; u_int32_t ifType; u_int64_t ifSpeed; u_int32_t ifDirection; /* Derived from MAU MIB (RFC 2668) 0 = unknown, 1 = full-duplex, 2 = half-duplex, 3 = in, 4 = out */ u_int32_t ifStatus; /* bit field with the following bits assigned: bit 0 = ifAdminStatus (0 = down, 1 = up) bit 1 = ifOperStatus (0 = down, 1 = up) */ u_int64_t ifInOctets; u_int32_t ifInUcastPkts; u_int32_t ifInMulticastPkts; u_int32_t ifInBroadcastPkts; u_int32_t ifInDiscards; u_int32_t ifInErrors; u_int32_t ifInUnknownProtos; u_int64_t ifOutOctets; u_int32_t ifOutUcastPkts; u_int32_t ifOutMulticastPkts; u_int32_t ifOutBroadcastPkts; u_int32_t ifOutDiscards; u_int32_t ifOutErrors; u_int32_t ifPromiscuousMode; } SFLIf_counters; #define SFL_CTR_GENERIC_XDR_SIZE 88 /* Ethernet interface counters - see RFC 2358 */ typedef struct _SFLEthernet_counters { u_int32_t dot3StatsAlignmentErrors; u_int32_t dot3StatsFCSErrors; u_int32_t dot3StatsSingleCollisionFrames; u_int32_t dot3StatsMultipleCollisionFrames; u_int32_t dot3StatsSQETestErrors; u_int32_t dot3StatsDeferredTransmissions; u_int32_t dot3StatsLateCollisions; u_int32_t dot3StatsExcessiveCollisions; u_int32_t dot3StatsInternalMacTransmitErrors; u_int32_t dot3StatsCarrierSenseErrors; u_int32_t dot3StatsFrameTooLongs; u_int32_t dot3StatsInternalMacReceiveErrors; u_int32_t dot3StatsSymbolErrors; } SFLEthernet_counters; #define SFL_CTR_ETHERNET_XDR_SIZE 52 /* Token ring counters - see RFC 1748 */ typedef struct _SFLTokenring_counters { u_int32_t dot5StatsLineErrors; u_int32_t dot5StatsBurstErrors; u_int32_t dot5StatsACErrors; u_int32_t dot5StatsAbortTransErrors; u_int32_t dot5StatsInternalErrors; u_int32_t dot5StatsLostFrameErrors; u_int32_t dot5StatsReceiveCongestions; u_int32_t dot5StatsFrameCopiedErrors; u_int32_t dot5StatsTokenErrors; u_int32_t dot5StatsSoftErrors; u_int32_t dot5StatsHardErrors; u_int32_t dot5StatsSignalLoss; u_int32_t dot5StatsTransmitBeacons; u_int32_t dot5StatsRecoverys; u_int32_t dot5StatsLobeWires; u_int32_t dot5StatsRemoves; u_int32_t dot5StatsSingles; u_int32_t dot5StatsFreqErrors; } SFLTokenring_counters; /* 100 BaseVG interface counters - see RFC 2020 */ typedef struct _SFLVg_counters { u_int32_t dot12InHighPriorityFrames; u_int64_t dot12InHighPriorityOctets; u_int32_t dot12InNormPriorityFrames; u_int64_t dot12InNormPriorityOctets; u_int32_t dot12InIPMErrors; u_int32_t dot12InOversizeFrameErrors; u_int32_t dot12InDataErrors; u_int32_t dot12InNullAddressedFrames; u_int32_t dot12OutHighPriorityFrames; u_int64_t dot12OutHighPriorityOctets; u_int32_t dot12TransitionIntoTrainings; u_int64_t dot12HCInHighPriorityOctets; u_int64_t dot12HCInNormPriorityOctets; u_int64_t dot12HCOutHighPriorityOctets; } SFLVg_counters; typedef struct _SFLVlan_counters { u_int32_t vlan_id; u_int64_t octets; u_int32_t ucastPkts; u_int32_t multicastPkts; u_int32_t broadcastPkts; u_int32_t discards; } SFLVlan_counters; /* OpenFlow port */ typedef struct { u_int64_t datapath_id; u_int32_t port_no; } SFLOpenFlowPort; #define SFL_CTR_OPENFLOWPORT_XDR_SIZE 12 /* port name */ typedef struct { SFLString portName; } SFLPortName; #define SFL_MAX_PORTNAME_LEN 255 /* LAG Port Statistics - see http://sflow.org/sflow_lag.txt */ /* opaque = counter_data; enterprise = 0; format = 7 */ typedef union _SFLLACP_portState { uint32_t all; struct { uint8_t actorAdmin; uint8_t actorOper; uint8_t partnerAdmin; uint8_t partnerOper; } v; } SFLLACP_portState; typedef struct _SFLLACP_counters { struct eth_addr actorSystemID; /* 6 bytes */ uint8_t pad1[2]; struct eth_addr partnerSystemID; /* 6 bytes */ uint8_t pad2[2]; uint32_t attachedAggID; SFLLACP_portState portState; uint32_t LACPDUsRx; uint32_t markerPDUsRx; uint32_t markerResponsePDUsRx; uint32_t unknownRx; uint32_t illegalRx; uint32_t LACPDUsTx; uint32_t markerPDUsTx; uint32_t markerResponsePDUsTx; } SFLLACP_counters; #define SFL_CTR_LACP_XDR_SIZE 56 /* Application resource counters */ typedef struct _SFLAPPResources_counters { uint32_t user_time; /* in milliseconds */ uint32_t system_time; /* in milliseconds */ uint64_t mem_used; uint64_t mem_max; uint32_t fd_open; uint32_t fd_max; uint32_t conn_open; uint32_t conn_max; } SFLAPPResources_counters; #define SFL_CTR_APP_RESOURCES_XDR_SIZE 40 /* OVS datapath stats */ typedef struct _SFLOVSDP_counters { uint32_t n_hit; uint32_t n_missed; uint32_t n_lost; uint32_t n_mask_hit; uint32_t n_flows; uint32_t n_masks; } SFLOVSDP_counters; #define SFL_CTR_OVSDP_XDR_SIZE 24 /* Counters data */ enum SFLCounters_type_tag { /* enterprise = 0, format = ... */ SFLCOUNTERS_GENERIC = 1, SFLCOUNTERS_ETHERNET = 2, SFLCOUNTERS_TOKENRING = 3, SFLCOUNTERS_VG = 4, SFLCOUNTERS_VLAN = 5, SFLCOUNTERS_LACP = 7, SFLCOUNTERS_OPENFLOWPORT = 1004, SFLCOUNTERS_PORTNAME = 1005, SFLCOUNTERS_APP_RESOURCES = 2203, SFLCOUNTERS_OVSDP = 2207 }; typedef union _SFLCounters_type { SFLIf_counters generic; SFLEthernet_counters ethernet; SFLTokenring_counters tokenring; SFLVg_counters vg; SFLVlan_counters vlan; SFLLACP_counters lacp; SFLOpenFlowPort ofPort; SFLPortName portName; SFLAPPResources_counters appResources; SFLOVSDP_counters ovsdp; } SFLCounters_type; typedef struct _SFLCounters_sample_element { struct _SFLCounters_sample_element *nxt; /* linked list */ u_int32_t tag; /* SFLCounters_type_tag */ u_int32_t length; SFLCounters_type counterBlock; } SFLCounters_sample_element; typedef struct _SFLCounters_sample { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t source_id; /* fsSourceId */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample; /* same thing, but the expanded version, so ds_index can be a full 32 bits */ typedef struct _SFLCounters_sample_expanded { /* u_int32_t tag; */ /* SFL_sample_tag -- enterprise = 0 : format = 2 */ /* u_int32_t length; */ u_int32_t sequence_number; /* Incremented with each counters sample generated by this source_id */ u_int32_t ds_class; /* EXPANDED */ u_int32_t ds_index; /* EXPANDED */ u_int32_t num_elements; SFLCounters_sample_element *elements; } SFLCounters_sample_expanded; #define SFLADD_ELEMENT(_sm, _el) do { (_el)->nxt = (_sm)->elements; (_sm)->elements = (_el); } while(0) /* Format of a sample datagram */ enum SFLDatagram_version { SFLDATAGRAM_VERSION2 = 2, SFLDATAGRAM_VERSION4 = 4, SFLDATAGRAM_VERSION5 = 5 }; typedef struct _SFLSample_datagram_hdr { u_int32_t datagram_version; /* (enum SFLDatagram_version) = VERSION5 = 5 */ SFLAddress agent_address; /* IP address of sampling agent */ u_int32_t sub_agent_id; /* Used to distinguishing between datagram streams from separate agent sub entities within an device. */ u_int32_t sequence_number; /* Incremented with each sample datagram generated */ u_int32_t uptime; /* Current time (in milliseconds since device last booted). Should be set as close to datagram transmission time as possible.*/ u_int32_t num_records; /* Number of tag-len-val flow/counter records to follow */ } SFLSample_datagram_hdr; #define SFL_MAX_DATAGRAM_SIZE 1500 #define SFL_MIN_DATAGRAM_SIZE 200 #define SFL_DEFAULT_DATAGRAM_SIZE 1400 #define SFL_DATA_PAD 400 #endif /* SFLOW_H */ openvswitch-2.5.9/lib/PaxHeaders.82075/unixctl.c0000644000000000000000000000013213534540071016244 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.929854517 openvswitch-2.5.9/lib/unixctl.c0000644000175000017500000003617013534540071017741 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "unixctl.h" #include #include #include "coverage.h" #include "dirs.h" #include "dynamic-string.h" #include "json.h" #include "jsonrpc.h" #include "list.h" #include "poll-loop.h" #include "shash.h" #include "stream.h" #include "stream-provider.h" #include "svec.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(unixctl); COVERAGE_DEFINE(unixctl_received); COVERAGE_DEFINE(unixctl_replied); struct unixctl_command { const char *usage; int min_args, max_args; unixctl_cb_func *cb; void *aux; }; struct unixctl_conn { struct ovs_list node; struct jsonrpc *rpc; /* Only one request can be in progress at a time. While the request is * being processed, 'request_id' is populated, otherwise it is null. */ struct json *request_id; /* ID of the currently active request. */ }; /* Server for control connection. */ struct unixctl_server { struct pstream *listener; struct ovs_list conns; }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); static struct shash commands = SHASH_INITIALIZER(&commands); static void unixctl_list_commands(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct shash_node **nodes = shash_sort(&commands); size_t i; ds_put_cstr(&ds, "The available commands are:\n"); for (i = 0; i < shash_count(&commands); i++) { const struct shash_node *node = nodes[i]; const struct unixctl_command *command = node->data; ds_put_format(&ds, " %-23s %s\n", node->name, command->usage); } free(nodes); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void unixctl_version(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { unixctl_command_reply(conn, ovs_get_program_version()); } /* Registers a unixctl command with the given 'name'. 'usage' describes the * arguments to the command; it is used only for presentation to the user in * "list-commands" output. * * 'cb' is called when the command is received. It is passed an array * containing the command name and arguments, plus a copy of 'aux'. Normally * 'cb' should reply by calling unixctl_command_reply() or * unixctl_command_reply_error() before it returns, but if the command cannot * be handled immediately then it can defer the reply until later. A given * connection can only process a single request at a time, so a reply must be * made eventually to avoid blocking that connection. */ void unixctl_command_register(const char *name, const char *usage, int min_args, int max_args, unixctl_cb_func *cb, void *aux) { struct unixctl_command *command; struct unixctl_command *lookup = shash_find_data(&commands, name); ovs_assert(!lookup || lookup->cb == cb); if (lookup) { return; } command = xmalloc(sizeof *command); command->usage = usage; command->min_args = min_args; command->max_args = max_args; command->cb = cb; command->aux = aux; shash_add(&commands, name, command); } static void unixctl_command_reply__(struct unixctl_conn *conn, bool success, const char *body) { struct json *body_json; struct jsonrpc_msg *reply; COVERAGE_INC(unixctl_replied); ovs_assert(conn->request_id); if (!body) { body = ""; } if (body[0] && body[strlen(body) - 1] != '\n') { body_json = json_string_create_nocopy(xasprintf("%s\n", body)); } else { body_json = json_string_create(body); } if (success) { reply = jsonrpc_create_reply(body_json, conn->request_id); } else { reply = jsonrpc_create_error(body_json, conn->request_id); } /* If jsonrpc_send() returns an error, the run loop will take care of the * problem eventually. */ jsonrpc_send(conn->rpc, reply); json_destroy(conn->request_id); conn->request_id = NULL; } /* Replies to the active unixctl connection 'conn'. 'result' is sent to the * client indicating the command was processed successfully. Only one call to * unixctl_command_reply() or unixctl_command_reply_error() may be made per * request. */ void unixctl_command_reply(struct unixctl_conn *conn, const char *result) { unixctl_command_reply__(conn, true, result); } /* Replies to the active unixctl connection 'conn'. 'error' is sent to the * client indicating an error occurred processing the command. Only one call to * unixctl_command_reply() or unixctl_command_reply_error() may be made per * request. */ void unixctl_command_reply_error(struct unixctl_conn *conn, const char *error) { unixctl_command_reply__(conn, false, error); } /* Creates a unixctl server listening on 'path', which for POSIX may be: * * - NULL, in which case /..ctl is used. * * - A name that does not start with '/', in which case it is put in * . * * - An absolute path (starting with '/') that gives the exact name of * the Unix domain socket to listen on. * * For Windows, a kernel assigned TCP port is used and written in 'path' * which may be: * * - NULL, in which case /.ctl is used. * * - An absolute path that gives the name of the file. * * For both POSIX and Windows, if the path is "none", the function will * return successfully but no socket will actually be created. * * A program that (optionally) daemonizes itself should call this function * *after* daemonization, so that the socket name contains the pid of the * daemon instead of the pid of the program that exited. (Otherwise, * "ovs-appctl --target=" will fail.) * * Returns 0 if successful, otherwise a positive errno value. If successful, * sets '*serverp' to the new unixctl_server (or to NULL if 'path' was "none"), * otherwise to NULL. */ int unixctl_server_create(const char *path, struct unixctl_server **serverp) { struct unixctl_server *server; struct pstream *listener; char *punix_path; int error; *serverp = NULL; if (path && !strcmp(path, "none")) { return 0; } if (path) { char *abs_path; #ifndef _WIN32 abs_path = abs_file_name(ovs_rundir(), path); #else abs_path = xstrdup(path); #endif punix_path = xasprintf("punix:%s", abs_path); free(abs_path); } else { #ifndef _WIN32 punix_path = xasprintf("punix:%s/%s.%ld.ctl", ovs_rundir(), program_name, (long int) getpid()); #else punix_path = xasprintf("punix:%s/%s.ctl", ovs_rundir(), program_name); #endif } error = pstream_open(punix_path, &listener, 0); if (error) { ovs_error(error, "could not initialize control socket %s", punix_path); goto exit; } unixctl_command_register("list-commands", "", 0, 0, unixctl_list_commands, NULL); unixctl_command_register("version", "", 0, 0, unixctl_version, NULL); server = xmalloc(sizeof *server); server->listener = listener; list_init(&server->conns); *serverp = server; exit: free(punix_path); return error; } static void process_command(struct unixctl_conn *conn, struct jsonrpc_msg *request) { char *error = NULL; struct unixctl_command *command; struct json_array *params; COVERAGE_INC(unixctl_received); conn->request_id = json_clone(request->id); params = json_array(request->params); command = shash_find_data(&commands, request->method); if (!command) { error = xasprintf("\"%s\" is not a valid command", request->method); } else if (params->n < command->min_args) { error = xasprintf("\"%s\" command requires at least %d arguments", request->method, command->min_args); } else if (params->n > command->max_args) { error = xasprintf("\"%s\" command takes at most %d arguments", request->method, command->max_args); } else { struct svec argv = SVEC_EMPTY_INITIALIZER; int i; svec_add(&argv, request->method); for (i = 0; i < params->n; i++) { if (params->elems[i]->type != JSON_STRING) { error = xasprintf("\"%s\" command has non-string argument", request->method); break; } svec_add(&argv, json_string(params->elems[i])); } svec_terminate(&argv); if (!error) { command->cb(conn, argv.n, (const char **) argv.names, command->aux); } svec_destroy(&argv); } if (error) { unixctl_command_reply_error(conn, error); free(error); } } static int run_connection(struct unixctl_conn *conn) { int error, i; jsonrpc_run(conn->rpc); error = jsonrpc_get_status(conn->rpc); if (error || jsonrpc_get_backlog(conn->rpc)) { return error; } for (i = 0; i < 10; i++) { struct jsonrpc_msg *msg; if (error || conn->request_id) { break; } jsonrpc_recv(conn->rpc, &msg); if (msg) { if (msg->type == JSONRPC_REQUEST) { process_command(conn, msg); } else { VLOG_WARN_RL(&rl, "%s: received unexpected %s message", jsonrpc_get_name(conn->rpc), jsonrpc_msg_type_to_string(msg->type)); error = EINVAL; } jsonrpc_msg_destroy(msg); } error = error ? error : jsonrpc_get_status(conn->rpc); } return error; } static void kill_connection(struct unixctl_conn *conn) { list_remove(&conn->node); jsonrpc_close(conn->rpc); json_destroy(conn->request_id); free(conn); } void unixctl_server_run(struct unixctl_server *server) { struct unixctl_conn *conn, *next; int i; if (!server) { return; } for (i = 0; i < 10; i++) { struct stream *stream; int error; error = pstream_accept(server->listener, &stream); if (!error) { struct unixctl_conn *conn = xzalloc(sizeof *conn); list_push_back(&server->conns, &conn->node); conn->rpc = jsonrpc_open(stream); } else if (error == EAGAIN) { break; } else { VLOG_WARN_RL(&rl, "%s: accept failed: %s", pstream_get_name(server->listener), ovs_strerror(error)); } } LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) { int error = run_connection(conn); if (error && error != EAGAIN) { kill_connection(conn); } } } void unixctl_server_wait(struct unixctl_server *server) { struct unixctl_conn *conn; if (!server) { return; } pstream_wait(server->listener); LIST_FOR_EACH (conn, node, &server->conns) { jsonrpc_wait(conn->rpc); if (!jsonrpc_get_backlog(conn->rpc)) { jsonrpc_recv_wait(conn->rpc); } } } /* Destroys 'server' and stops listening for connections. */ void unixctl_server_destroy(struct unixctl_server *server) { if (server) { struct unixctl_conn *conn, *next; LIST_FOR_EACH_SAFE (conn, next, node, &server->conns) { kill_connection(conn); } pstream_close(server->listener); free(server); } } /* On POSIX based systems, connects to a unixctl server socket. 'path' should * be the name of a unixctl server socket. If it does not start with '/', it * will be prefixed with the rundir (e.g. /usr/local/var/run/openvswitch). * * On Windows, connects to a localhost TCP port as written inside 'path'. * 'path' should be an absolute path of the file. * * Returns 0 if successful, otherwise a positive errno value. If successful, * sets '*client' to the new jsonrpc, otherwise to NULL. */ int unixctl_client_create(const char *path, struct jsonrpc **client) { char *abs_path, *unix_path; struct stream *stream; int error; #ifdef _WIN32 abs_path = xstrdup(path); #else abs_path = abs_file_name(ovs_rundir(), path); #endif unix_path = xasprintf("unix:%s", abs_path); *client = NULL; error = stream_open_block(stream_open(unix_path, &stream, DSCP_DEFAULT), &stream); free(unix_path); free(abs_path); if (error) { VLOG_WARN("failed to connect to %s", path); return error; } *client = jsonrpc_open(stream); return 0; } /* Executes 'command' on the server with an argument vector 'argv' containing * 'argc' elements. If successfully communicated with the server, returns 0 * and sets '*result', or '*err' (not both) to the result or error the server * returned. Otherwise, sets '*result' and '*err' to NULL and returns a * positive errno value. The caller is responsible for freeing '*result' or * '*err' if not NULL. */ int unixctl_client_transact(struct jsonrpc *client, const char *command, int argc, char *argv[], char **result, char **err) { struct jsonrpc_msg *request, *reply; struct json **json_args, *params; int error, i; *result = NULL; *err = NULL; json_args = xmalloc(argc * sizeof *json_args); for (i = 0; i < argc; i++) { json_args[i] = json_string_create(argv[i]); } params = json_array_create(json_args, argc); request = jsonrpc_create_request(command, params, NULL); error = jsonrpc_transact_block(client, request, &reply); if (error) { VLOG_WARN("error communicating with %s: %s", jsonrpc_get_name(client), ovs_retval_to_string(error)); return error; } if (reply->error) { if (reply->error->type == JSON_STRING) { *err = xstrdup(json_string(reply->error)); } else { VLOG_WARN("%s: unexpected error type in JSON RPC reply: %s", jsonrpc_get_name(client), json_type_to_string(reply->error->type)); error = EINVAL; } } else if (reply->result) { if (reply->result->type == JSON_STRING) { *result = xstrdup(json_string(reply->result)); } else { VLOG_WARN("%s: unexpected result type in JSON rpc reply: %s", jsonrpc_get_name(client), json_type_to_string(reply->result->type)); error = EINVAL; } } jsonrpc_msg_destroy(reply); return error; } openvswitch-2.5.9/lib/PaxHeaders.82075/coverage-unixctl.man0000644000000000000000000000013213534540071020366 xustar0030 mtime=1567801401.325680611 30 atime=1567801402.069686075 30 ctime=1567801423.725845641 openvswitch-2.5.9/lib/coverage-unixctl.man0000644000175000017500000000113713534540071022056 0ustar00jpettitjpettit00000000000000.SS "COVERAGE COMMANDS" These commands manage \fB\*(PN\fR's ``coverage counters,'' which count the number of times particular events occur during a daemon's runtime. In addition to these commands, \fB\*(PN\fR automatically logs coverage counter values, at \fBINFO\fR level, when it detects that the daemon's main loop takes unusually long to run. .PP Coverage counters are useful mainly for performance analysis and debugging. .IP "\fBcoverage/show\fR" Displays the averaged per-second rates for the last few seconds, the last minute and the last hour, and the total counts of all of the coverage counters. openvswitch-2.5.9/lib/PaxHeaders.82075/vconn-passive.man0000644000000000000000000000013213534540071017702 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801423.749845818 openvswitch-2.5.9/lib/vconn-passive.man0000644000175000017500000000110513534540071021365 0ustar00jpettitjpettit00000000000000.IP "\fBpssl:\fR[\fIport\fR][\fB:\fIip\fR]" .IQ "\fBptcp:\fR[\fIport\fR][\fB:\fIip\fR]" Listens for OpenFlow connections on \fIport\fR. The default \fIport\fR is 6653. By default, connections are allowed from any IPv4 address. Specify \fIip\fR as an IPv4 address or a bracketed IPv6 address (e.g. \fBptcp:6653:[::1]\fR). DNS names may not be used. For \fBpssl\fR, the \fB\-\-private\-key\fR,\fB\-\-certificate\fR, and \fB\-\-ca\-cert\fR options are mandatory. .IP . .IP "\fBpunix:\fIfile\fR" Listens for OpenFlow connections on the Unix domain server socket named \fIfile\fR. openvswitch-2.5.9/lib/PaxHeaders.82075/tnl-neigh-cache.c0000644000000000000000000000013213534540071017504 xustar0030 mtime=1567801401.601682639 30 atime=1567801402.101686312 30 ctime=1567801424.913854399 openvswitch-2.5.9/lib/tnl-neigh-cache.c0000644000175000017500000002067313534540071021202 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "tnl-neigh-cache.h" #include #include #include #include #include #include "bitmap.h" #include "cmap.h" #include "coverage.h" #include "dpif-netdev.h" #include "dynamic-string.h" #include "errno.h" #include "flow.h" #include "netdev.h" #include "ovs-thread.h" #include "packets.h" #include "poll-loop.h" #include "seq.h" #include "socket-util.h" #include "timeval.h" #include "unaligned.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" /* In seconds */ #define NEIGH_ENTRY_DEFAULT_IDLE_TIME (15 * 60) struct tnl_neigh_entry { struct cmap_node cmap_node; struct in6_addr ip; struct eth_addr mac; time_t expires; /* Expiration time. */ char br_name[IFNAMSIZ]; }; static struct cmap table; static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static uint32_t tnl_neigh_hash(const struct in6_addr *ip) { return hash_bytes(ip->s6_addr, 16, 0); } static struct tnl_neigh_entry * tnl_neigh_lookup__(const char br_name[IFNAMSIZ], const struct in6_addr *dst) { struct tnl_neigh_entry *neigh; uint32_t hash; hash = tnl_neigh_hash(dst); CMAP_FOR_EACH_WITH_HASH (neigh, cmap_node, hash, &table) { if (ipv6_addr_equals(&neigh->ip, dst) && !strcmp(neigh->br_name, br_name)) { neigh->expires = time_now() + NEIGH_ENTRY_DEFAULT_IDLE_TIME; return neigh; } } return NULL; } int tnl_neigh_lookup(const char br_name[IFNAMSIZ], const struct in6_addr *dst, struct eth_addr *mac) { struct tnl_neigh_entry *neigh; int res = ENOENT; neigh = tnl_neigh_lookup__(br_name, dst); if (neigh) { *mac = neigh->mac; res = 0; } return res; } static void neigh_entry_free(struct tnl_neigh_entry *neigh) { free(neigh); } static void tnl_neigh_delete(struct tnl_neigh_entry *neigh) { uint32_t hash = tnl_neigh_hash(&neigh->ip); cmap_remove(&table, &neigh->cmap_node, hash); ovsrcu_postpone(neigh_entry_free, neigh); } static void tnl_neigh_set__(const char name[IFNAMSIZ], const struct in6_addr *dst, const struct eth_addr mac) { ovs_mutex_lock(&mutex); struct tnl_neigh_entry *neigh = tnl_neigh_lookup__(name, dst); if (neigh) { if (eth_addr_equals(neigh->mac, mac)) { neigh->expires = time_now() + NEIGH_ENTRY_DEFAULT_IDLE_TIME; ovs_mutex_unlock(&mutex); return; } tnl_neigh_delete(neigh); } seq_change(tnl_conf_seq); neigh = xmalloc(sizeof *neigh); neigh->ip = *dst; neigh->mac = mac; neigh->expires = time_now() + NEIGH_ENTRY_DEFAULT_IDLE_TIME; ovs_strlcpy(neigh->br_name, name, sizeof neigh->br_name); cmap_insert(&table, &neigh->cmap_node, tnl_neigh_hash(&neigh->ip)); ovs_mutex_unlock(&mutex); } static void tnl_arp_set(const char name[IFNAMSIZ], ovs_be32 dst, const struct eth_addr mac) { struct in6_addr dst6 = in6_addr_mapped_ipv4(dst); tnl_neigh_set__(name, &dst6, mac); } static int tnl_arp_snoop(const struct flow *flow, struct flow_wildcards *wc, const char name[IFNAMSIZ]) { if (flow->dl_type != htons(ETH_TYPE_ARP)) { return EINVAL; } /* Exact Match on all ARP flows. */ memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha); tnl_arp_set(name, flow->nw_src, flow->arp_sha); return 0; } static int tnl_nd_snoop(const struct flow *flow, struct flow_wildcards *wc, const char name[IFNAMSIZ]) { if (flow->dl_type != htons(ETH_TYPE_IPV6) || FLOW_WC_GET_AND_MASK_WC(flow, wc, nw_proto) != IPPROTO_ICMPV6 || FLOW_WC_GET_AND_MASK_WC(flow, wc, tp_dst) != htons(0) || FLOW_WC_GET_AND_MASK_WC(flow, wc, tp_src) != htons(ND_NEIGHBOR_ADVERT)) { return EINVAL; } memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src); memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst); memset(&wc->masks.nd_target, 0xff, sizeof wc->masks.nd_target); memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha); tnl_neigh_set__(name, &flow->nd_target, flow->arp_tha); return 0; } int tnl_neigh_snoop(const struct flow *flow, struct flow_wildcards *wc, const char name[IFNAMSIZ]) { int res; res = tnl_arp_snoop(flow, wc, name); if (res != EINVAL) { return res; } return tnl_nd_snoop(flow, wc, name); } void tnl_neigh_cache_run(void) { struct tnl_neigh_entry *neigh; bool changed = false; ovs_mutex_lock(&mutex); CMAP_FOR_EACH(neigh, cmap_node, &table) { if (neigh->expires <= time_now()) { tnl_neigh_delete(neigh); changed = true; } } ovs_mutex_unlock(&mutex); if (changed) { seq_change(tnl_conf_seq); } } static void tnl_neigh_cache_flush(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct tnl_neigh_entry *neigh; bool changed = false; ovs_mutex_lock(&mutex); CMAP_FOR_EACH(neigh, cmap_node, &table) { tnl_neigh_delete(neigh); changed = true; } ovs_mutex_unlock(&mutex); if (changed) { seq_change(tnl_conf_seq); } unixctl_command_reply(conn, "OK"); } static int lookup_any(const char *host_name, struct in6_addr *address) { if (addr_is_ipv6(host_name)) { return lookup_ipv6(host_name, address); } else { int r; struct in_addr ip; r = lookup_ip(host_name, &ip); if (r == 0) { in6_addr_set_mapped_ipv4(address, ip.s_addr); } return r; } return ENOENT; } static void tnl_neigh_cache_add(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { const char *br_name = argv[1]; struct eth_addr mac; struct in6_addr ip6; if (lookup_any(argv[2], &ip6) != 0) { unixctl_command_reply_error(conn, "bad IP address"); return; } if (!eth_addr_from_string(argv[3], &mac)) { unixctl_command_reply_error(conn, "bad MAC address"); return; } tnl_neigh_set__(br_name, &ip6, mac); unixctl_command_reply(conn, "OK"); } static void tnl_neigh_cache_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct tnl_neigh_entry *neigh; ds_put_cstr(&ds, "IP MAC Bridge\n"); ds_put_cstr(&ds, "==========================================================================\n"); ovs_mutex_lock(&mutex); CMAP_FOR_EACH(neigh, cmap_node, &table) { int start_len, need_ws; start_len = ds.length; ipv6_format_mapped(&neigh->ip, &ds); need_ws = INET6_ADDRSTRLEN - (ds.length - start_len); ds_put_char_multiple(&ds, ' ', need_ws); ds_put_format(&ds, ETH_ADDR_FMT" %s\n", ETH_ADDR_ARGS(neigh->mac), neigh->br_name); } ovs_mutex_unlock(&mutex); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } void tnl_neigh_cache_init(void) { cmap_init(&table); unixctl_command_register("tnl/arp/show", "", 0, 0, tnl_neigh_cache_show, NULL); unixctl_command_register("tnl/arp/set", "BRIDGE IP MAC", 3, 3, tnl_neigh_cache_add, NULL); unixctl_command_register("tnl/arp/flush", "", 0, 0, tnl_neigh_cache_flush, NULL); unixctl_command_register("tnl/neigh/show", "", 0, 0, tnl_neigh_cache_show, NULL); unixctl_command_register("tnl/neigh/set", "BRIDGE IP MAC", 3, 3, tnl_neigh_cache_add, NULL); unixctl_command_register("tnl/neigh/flush", "", 0, 0, tnl_neigh_cache_flush, NULL); } openvswitch-2.5.9/lib/PaxHeaders.82075/unicode.h0000644000000000000000000000013213534540071016211 xustar0030 mtime=1567801401.605682667 30 atime=1567801402.101686312 30 ctime=1567801424.929854517 openvswitch-2.5.9/lib/unicode.h0000644000175000017500000000304513534540071017701 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef UNICODE_H #define UNICODE_H 1 #include #include #include "compiler.h" /* Returns true if 'c' is a Unicode code point, otherwise false. */ static inline bool uc_is_code_point(int c) { return c >= 0 && c <= 0x10ffff; } /* Returns true if 'c' is a Unicode code point for a leading surrogate. */ static inline bool uc_is_leading_surrogate(int c) { return c >= 0xd800 && c <= 0xdbff; } /* Returns true if 'c' is a Unicode code point for a trailing surrogate. */ static inline bool uc_is_trailing_surrogate(int c) { return c >= 0xdc00 && c <= 0xdfff; } /* Returns true if 'c' is a Unicode code point for a leading or trailing * surrogate. */ static inline bool uc_is_surrogate(int c) { return c >= 0xd800 && c <= 0xdfff; } int utf16_decode_surrogate_pair(int leading, int trailing); size_t utf8_length(const char *); char *utf8_validate(const char *, size_t *lengthp) OVS_WARN_UNUSED_RESULT; #endif /* unicode.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/ovsdb-parser.c0000644000000000000000000000013213534540071017165 xustar0030 mtime=1567801401.569682404 30 atime=1567801402.093686252 30 ctime=1567801424.841853868 openvswitch-2.5.9/lib/ovsdb-parser.c0000644000175000017500000001064213534540071020656 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovsdb-parser.h" #include #include #include "ovsdb-error.h" void ovsdb_parser_init(struct ovsdb_parser *parser, const struct json *json, const char *name, ...) { va_list args; va_start(args, name); parser->name = xvasprintf(name, args); va_end(args); sset_init(&parser->used); parser->error = NULL; parser->json = (json && json->type == JSON_OBJECT ? json : NULL); if (!parser->json) { ovsdb_parser_raise_error(parser, "Object expected."); } } bool ovsdb_parser_is_id(const char *string) { unsigned char c; c = *string; if (!isalpha(c) && c != '_') { return false; } for (;;) { c = *++string; if (c == '\0') { return true; } else if (!isalpha(c) && !isdigit(c) && c != '_') { return false; } } } const struct json * ovsdb_parser_member(struct ovsdb_parser *parser, const char *name, enum ovsdb_parser_types types) { struct json *value; if (!parser->json) { return NULL; } value = shash_find_data(json_object(parser->json), name); if (!value) { if (!(types & OP_OPTIONAL)) { ovsdb_parser_raise_error(parser, "Required '%s' member is missing.", name); } return NULL; } if (((int) value->type >= 0 && value->type < JSON_N_TYPES && types & (1u << value->type)) || (types & OP_ID && value->type == JSON_STRING && ovsdb_parser_is_id(value->u.string))) { sset_add(&parser->used, name); return value; } else { ovsdb_parser_raise_error(parser, "Type mismatch for member '%s'.", name); return NULL; } } void ovsdb_parser_raise_error(struct ovsdb_parser *parser, const char *format, ...) { if (!parser->error) { struct ovsdb_error *error; va_list args; char *message; va_start(args, format); message = xvasprintf(format, args); va_end(args); error = ovsdb_syntax_error(parser->json, NULL, "Parsing %s failed: %s", parser->name, message); free(message); parser->error = error; } } struct ovsdb_error * ovsdb_parser_get_error(const struct ovsdb_parser *parser) { return parser->error ? ovsdb_error_clone(parser->error) : NULL; } bool ovsdb_parser_has_error(const struct ovsdb_parser *parser) { return parser->error != NULL; } struct ovsdb_error * ovsdb_parser_destroy(struct ovsdb_parser *parser) { free(parser->name); sset_destroy(&parser->used); return parser->error; } struct ovsdb_error * ovsdb_parser_finish(struct ovsdb_parser *parser) { if (!parser->error) { const struct shash *object = json_object(parser->json); size_t n_unused; n_unused = shash_count(object) - sset_count(&parser->used); if (n_unused) { struct shash_node *node; SHASH_FOR_EACH (node, object) { if (!sset_contains(&parser->used, node->name)) { if (n_unused > 1) { ovsdb_parser_raise_error( parser, "Member '%s' and %"PRIuSIZE" other member%s " "are present but not allowed here.", node->name, n_unused - 1, n_unused > 2 ? "s" : ""); } else { ovsdb_parser_raise_error( parser, "Member '%s' is present but not allowed here.", node->name); } break; } } } } return ovsdb_parser_destroy(parser); } openvswitch-2.5.9/lib/PaxHeaders.82075/geneve.h0000644000000000000000000000013213534540071016034 xustar0030 mtime=1567801401.389681081 30 atime=1567801402.073686105 30 ctime=1567801424.725853013 openvswitch-2.5.9/lib/geneve.h0000644000175000017500000000264313534540071017527 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef GENEVE_H #define GENEVE_H 1 #include "openvswitch/types.h" #define TLV_MAX_OPT_SIZE 124 #define TLV_TOT_OPT_SIZE 252 #define GENEVE_CRIT_OPT_TYPE (1 << 7) struct geneve_opt { ovs_be16 opt_class; uint8_t type; #ifdef WORDS_BIGENDIAN uint8_t r1:1; uint8_t r2:1; uint8_t r3:1; uint8_t length:5; #else uint8_t length:5; uint8_t r3:1; uint8_t r2:1; uint8_t r1:1; #endif /* Option data */ }; struct genevehdr { #ifdef WORDS_BIGENDIAN uint8_t ver:2; uint8_t opt_len:6; uint8_t oam:1; uint8_t critical:1; uint8_t rsvd1:6; #else uint8_t opt_len:6; uint8_t ver:2; uint8_t rsvd1:6; uint8_t critical:1; uint8_t oam:1; #endif ovs_be16 proto_type; ovs_16aligned_be32 vni; struct geneve_opt options[]; }; #endif /* geneve.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/dh4096.pem0000644000000000000000000000013213534540071016033 xustar0030 mtime=1567801401.333680671 30 atime=1567801402.069686075 30 ctime=1567801423.793846143 openvswitch-2.5.9/lib/dh4096.pem0000644000175000017500000000177013534540071017526 0ustar00jpettitjpettit00000000000000-----BEGIN DH PARAMETERS----- MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98 VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9 ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI= -----END DH PARAMETERS----- These are the 4096 bit DH parameters from "Assigned Number for SKIP Protocols" (http://www.skip-vpn.org/spec/numbers.html). See there for how they were generated. Note that g is not a generator, but this is not a problem since p is a safe prime. openvswitch-2.5.9/lib/PaxHeaders.82075/learning-switch.c0000644000000000000000000000013113534540071017653 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.753853219 openvswitch-2.5.9/lib/learning-switch.c0000644000175000017500000005452613534540071021356 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "learning-switch.h" #include #include #include #include #include #include "byte-order.h" #include "classifier.h" #include "dp-packet.h" #include "flow.h" #include "hmap.h" #include "mac-learning.h" #include "ofpbuf.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-parse.h" #include "ofp-print.h" #include "ofp-util.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "rconn.h" #include "shash.h" #include "simap.h" #include "timeval.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(learning_switch); struct lswitch_port { struct hmap_node hmap_node; /* Hash node for port number. */ ofp_port_t port_no; /* OpenFlow port number. */ uint32_t queue_id; /* OpenFlow queue number. */ }; enum lswitch_state { S_CONNECTING, /* Waiting for connection to complete. */ S_FEATURES_REPLY, /* Waiting for features reply. */ S_SWITCHING, /* Switching flows. */ }; struct lswitch { struct rconn *rconn; enum lswitch_state state; /* If nonnegative, the switch sets up flows that expire after the given * number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT). * Otherwise, the switch processes every packet. */ int max_idle; enum ofputil_protocol protocol; unsigned long long int datapath_id; struct mac_learning *ml; /* NULL to act as hub instead of switch. */ struct flow_wildcards wc; /* Wildcards to apply to flows. */ bool action_normal; /* Use OFPP_NORMAL? */ /* Queue distribution. */ uint32_t default_queue; /* Default OpenFlow queue, or UINT32_MAX. */ struct hmap queue_numbers; /* Map from port number to lswitch_port. */ struct shash queue_names; /* Map from port name to lswitch_port. */ /* Number of outgoing queued packets on the rconn. */ struct rconn_packet_counter *queued; /* If true, do not reply to any messages from the switch (for debugging * fail-open mode). */ bool mute; /* Optional "flow mod" requests to send to the switch at connection time, * to set up the flow table. */ const struct ofputil_flow_mod *default_flows; size_t n_default_flows; enum ofputil_protocol usable_protocols; }; /* The log messages here could actually be useful in debugging, so keep the * rate limit relatively high. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); static void queue_tx(struct lswitch *, struct ofpbuf *); static void send_features_request(struct lswitch *); static void lswitch_process_packet(struct lswitch *, const struct ofpbuf *); static enum ofperr process_switch_features(struct lswitch *, struct ofp_header *); static void process_packet_in(struct lswitch *, const struct ofp_header *); static void process_echo_request(struct lswitch *, const struct ofp_header *); static ofp_port_t get_mac_entry_ofp_port(const struct mac_learning *ml, const struct mac_entry *) OVS_REQ_RDLOCK(ml->rwlock); static void set_mac_entry_ofp_port(struct mac_learning *ml, struct mac_entry *, ofp_port_t) OVS_REQ_WRLOCK(ml->rwlock); /* Creates and returns a new learning switch whose configuration is given by * 'cfg'. * * 'rconn' is used to send out an OpenFlow features request. */ struct lswitch * lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) { struct lswitch *sw; uint32_t ofpfw; sw = xzalloc(sizeof *sw); sw->rconn = rconn; sw->state = S_CONNECTING; sw->max_idle = cfg->max_idle; sw->datapath_id = 0; sw->ml = (cfg->mode == LSW_LEARN ? mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME) : NULL); sw->action_normal = cfg->mode == LSW_NORMAL; switch (cfg->wildcards) { case 0: ofpfw = 0; break; case UINT32_MAX: /* Try to wildcard as many fields as possible, but we cannot * wildcard all fields. We need in_port to detect moves. We need * Ethernet source and dest and VLAN VID to do L2 learning. */ ofpfw = (OFPFW10_DL_TYPE | OFPFW10_DL_VLAN_PCP | OFPFW10_NW_SRC_ALL | OFPFW10_NW_DST_ALL | OFPFW10_NW_TOS | OFPFW10_NW_PROTO | OFPFW10_TP_SRC | OFPFW10_TP_DST); break; default: ofpfw = cfg->wildcards; break; } ofputil_wildcard_from_ofpfw10(ofpfw, &sw->wc); sw->default_queue = cfg->default_queue; hmap_init(&sw->queue_numbers); shash_init(&sw->queue_names); if (cfg->port_queues) { struct simap_node *node; SIMAP_FOR_EACH (node, cfg->port_queues) { struct lswitch_port *port = xmalloc(sizeof *port); hmap_node_nullify(&port->hmap_node); port->queue_id = node->data; shash_add(&sw->queue_names, node->name, port); } } sw->default_flows = cfg->default_flows; sw->n_default_flows = cfg->n_default_flows; sw->usable_protocols = cfg->usable_protocols; sw->queued = rconn_packet_counter_create(); return sw; } static void lswitch_handshake(struct lswitch *sw) { enum ofputil_protocol protocol; enum ofp_version version; send_features_request(sw); version = rconn_get_version(sw->rconn); protocol = ofputil_protocol_from_ofp_version(version); if (version >= OFP13_VERSION) { /* OpenFlow 1.3 and later by default drop packets that miss in the flow * table. Set up a flow to send packets to the controller by * default. */ struct ofputil_flow_mod fm; struct ofpact_output output; struct ofpbuf *msg; int error; ofpact_init_OUTPUT(&output); output.port = OFPP_CONTROLLER; output.max_len = OFP_DEFAULT_MISS_SEND_LEN; match_init_catchall(&fm.match); fm.priority = 0; fm.cookie = 0; fm.cookie_mask = 0; fm.new_cookie = 0; fm.modify_cookie = false; fm.table_id = 0; fm.command = OFPFC_ADD; fm.idle_timeout = 0; fm.hard_timeout = 0; fm.importance = 0; fm.buffer_id = UINT32_MAX; fm.out_port = OFPP_NONE; fm.out_group = OFPG_ANY; fm.flags = 0; fm.ofpacts = &output.ofpact; fm.ofpacts_len = sizeof output; fm.delete_reason = 0; msg = ofputil_encode_flow_mod(&fm, protocol); error = rconn_send(sw->rconn, msg, NULL); if (error) { VLOG_INFO_RL(&rl, "%s: failed to add default flow (%s)", rconn_get_name(sw->rconn), ovs_strerror(error)); } } if (sw->default_flows) { struct ofpbuf *msg = NULL; int error = 0; size_t i; /* If the initial protocol isn't good enough for default_flows, then * pick one that will work and encode messages to set up that * protocol. * * This could be improved by actually negotiating a mutually acceptable * flow format with the switch, but that would require an asynchronous * state machine. This version ought to work fine in practice. */ if (!(protocol & sw->usable_protocols)) { enum ofputil_protocol want = rightmost_1bit(sw->usable_protocols); while (!error) { msg = ofputil_encode_set_protocol(protocol, want, &protocol); if (!msg) { break; } error = rconn_send(sw->rconn, msg, NULL); } } if (protocol & sw->usable_protocols) { for (i = 0; !error && i < sw->n_default_flows; i++) { msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol); error = rconn_send(sw->rconn, msg, NULL); } if (error) { VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)", rconn_get_name(sw->rconn), ovs_strerror(error)); } } else { VLOG_INFO_RL(&rl, "%s: failed to set usable protocol", rconn_get_name(sw->rconn)); } } sw->protocol = protocol; } bool lswitch_is_alive(const struct lswitch *sw) { return rconn_is_alive(sw->rconn); } /* Destroys 'sw'. */ void lswitch_destroy(struct lswitch *sw) { if (sw) { struct lswitch_port *node, *next; rconn_destroy(sw->rconn); HMAP_FOR_EACH_SAFE (node, next, hmap_node, &sw->queue_numbers) { hmap_remove(&sw->queue_numbers, &node->hmap_node); free(node); } shash_destroy(&sw->queue_names); mac_learning_unref(sw->ml); rconn_packet_counter_destroy(sw->queued); free(sw); } } /* Takes care of necessary 'sw' activity, except for receiving packets (which * the caller must do). */ void lswitch_run(struct lswitch *sw) { int i; if (sw->ml) { ovs_rwlock_wrlock(&sw->ml->rwlock); mac_learning_run(sw->ml); ovs_rwlock_unlock(&sw->ml->rwlock); } rconn_run(sw->rconn); if (sw->state == S_CONNECTING) { if (rconn_is_connected(sw->rconn)) { lswitch_handshake(sw); sw->state = S_FEATURES_REPLY; } return; } for (i = 0; i < 50; i++) { struct ofpbuf *msg; msg = rconn_recv(sw->rconn); if (!msg) { break; } if (!sw->mute) { lswitch_process_packet(sw, msg); } ofpbuf_delete(msg); } } void lswitch_wait(struct lswitch *sw) { if (sw->ml) { ovs_rwlock_rdlock(&sw->ml->rwlock); mac_learning_wait(sw->ml); ovs_rwlock_unlock(&sw->ml->rwlock); } rconn_run_wait(sw->rconn); rconn_recv_wait(sw->rconn); } /* Processes 'msg', which should be an OpenFlow received on 'rconn', according * to the learning switch state in 'sw'. The most likely result of processing * is that flow-setup and packet-out OpenFlow messages will be sent out on * 'rconn'. */ static void lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg) { enum ofptype type; struct ofpbuf b; b = *msg; if (ofptype_pull(&type, &b)) { return; } if (sw->state == S_FEATURES_REPLY && type != OFPTYPE_ECHO_REQUEST && type != OFPTYPE_FEATURES_REPLY) { return; } switch (type) { case OFPTYPE_ECHO_REQUEST: process_echo_request(sw, msg->data); break; case OFPTYPE_FEATURES_REPLY: if (sw->state == S_FEATURES_REPLY) { if (!process_switch_features(sw, msg->data)) { sw->state = S_SWITCHING; } else { rconn_disconnect(sw->rconn); } } break; case OFPTYPE_PACKET_IN: process_packet_in(sw, msg->data); break; case OFPTYPE_FLOW_REMOVED: /* Nothing to do. */ break; case OFPTYPE_HELLO: case OFPTYPE_ERROR: case OFPTYPE_ECHO_REPLY: case OFPTYPE_FEATURES_REQUEST: case OFPTYPE_GET_CONFIG_REQUEST: case OFPTYPE_GET_CONFIG_REPLY: case OFPTYPE_SET_CONFIG: case OFPTYPE_PORT_STATUS: case OFPTYPE_PACKET_OUT: case OFPTYPE_FLOW_MOD: case OFPTYPE_GROUP_MOD: case OFPTYPE_PORT_MOD: case OFPTYPE_TABLE_MOD: case OFPTYPE_BARRIER_REQUEST: case OFPTYPE_BARRIER_REPLY: case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: case OFPTYPE_QUEUE_GET_CONFIG_REPLY: case OFPTYPE_DESC_STATS_REQUEST: case OFPTYPE_DESC_STATS_REPLY: case OFPTYPE_FLOW_STATS_REQUEST: case OFPTYPE_FLOW_STATS_REPLY: case OFPTYPE_AGGREGATE_STATS_REQUEST: case OFPTYPE_AGGREGATE_STATS_REPLY: case OFPTYPE_TABLE_STATS_REQUEST: case OFPTYPE_TABLE_STATS_REPLY: case OFPTYPE_PORT_STATS_REQUEST: case OFPTYPE_PORT_STATS_REPLY: case OFPTYPE_QUEUE_STATS_REQUEST: case OFPTYPE_QUEUE_STATS_REPLY: case OFPTYPE_PORT_DESC_STATS_REQUEST: case OFPTYPE_PORT_DESC_STATS_REPLY: case OFPTYPE_ROLE_REQUEST: case OFPTYPE_ROLE_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: case OFPTYPE_SET_FLOW_FORMAT: case OFPTYPE_FLOW_MOD_TABLE_ID: case OFPTYPE_SET_PACKET_IN_FORMAT: case OFPTYPE_FLOW_AGE: case OFPTYPE_SET_CONTROLLER_ID: case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: case OFPTYPE_FLOW_MONITOR_STATS_REPLY: case OFPTYPE_FLOW_MONITOR_CANCEL: case OFPTYPE_FLOW_MONITOR_PAUSED: case OFPTYPE_FLOW_MONITOR_RESUMED: case OFPTYPE_GET_ASYNC_REQUEST: case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_SET_ASYNC_CONFIG: case OFPTYPE_METER_MOD: case OFPTYPE_GROUP_STATS_REQUEST: case OFPTYPE_GROUP_STATS_REPLY: case OFPTYPE_GROUP_DESC_STATS_REQUEST: case OFPTYPE_GROUP_DESC_STATS_REPLY: case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: case OFPTYPE_GROUP_FEATURES_STATS_REPLY: case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_STATS_REPLY: case OFPTYPE_METER_CONFIG_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REPLY: case OFPTYPE_METER_FEATURES_STATS_REQUEST: case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_DESC_REQUEST: case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_BUNDLE_CONTROL: case OFPTYPE_BUNDLE_ADD_MESSAGE: case OFPTYPE_NXT_TLV_TABLE_MOD: case OFPTYPE_NXT_TLV_TABLE_REQUEST: case OFPTYPE_NXT_TLV_TABLE_REPLY: default: if (VLOG_IS_DBG_ENABLED()) { char *s = ofp_to_string(msg->data, msg->size, 2); VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s", sw->datapath_id, s); free(s); } } } static void send_features_request(struct lswitch *sw) { struct ofpbuf *b; struct ofp_switch_config *osc; int ofp_version = rconn_get_version(sw->rconn); ovs_assert(ofp_version > 0 && ofp_version < 0xff); /* Send OFPT_FEATURES_REQUEST. */ b = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0); queue_tx(sw, b); /* Send OFPT_SET_CONFIG. */ b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, ofp_version, sizeof *osc); osc = ofpbuf_put_zeros(b, sizeof *osc); osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); queue_tx(sw, b); } static void queue_tx(struct lswitch *sw, struct ofpbuf *b) { int retval = rconn_send_with_limit(sw->rconn, b, sw->queued, 10); if (retval && retval != ENOTCONN) { if (retval == EAGAIN) { VLOG_INFO_RL(&rl, "%016llx: %s: tx queue overflow", sw->datapath_id, rconn_get_name(sw->rconn)); } else { VLOG_WARN_RL(&rl, "%016llx: %s: send: %s", sw->datapath_id, rconn_get_name(sw->rconn), ovs_strerror(retval)); } } } static enum ofperr process_switch_features(struct lswitch *sw, struct ofp_header *oh) { struct ofputil_switch_features features; struct ofputil_phy_port port; enum ofperr error; struct ofpbuf b; error = ofputil_decode_switch_features(oh, &features, &b); if (error) { VLOG_ERR("received invalid switch feature reply (%s)", ofperr_to_string(error)); return error; } sw->datapath_id = features.datapath_id; while (!ofputil_pull_phy_port(oh->version, &b, &port)) { struct lswitch_port *lp = shash_find_data(&sw->queue_names, port.name); if (lp && hmap_node_is_null(&lp->hmap_node)) { lp->port_no = port.port_no; hmap_insert(&sw->queue_numbers, &lp->hmap_node, hash_ofp_port(lp->port_no)); } } return 0; } static ofp_port_t lswitch_choose_destination(struct lswitch *sw, const struct flow *flow) { ofp_port_t out_port; /* Learn the source MAC. */ if (sw->ml) { ovs_rwlock_wrlock(&sw->ml->rwlock); if (mac_learning_may_learn(sw->ml, flow->dl_src, 0)) { struct mac_entry *mac = mac_learning_insert(sw->ml, flow->dl_src, 0); if (get_mac_entry_ofp_port(sw->ml, mac) != flow->in_port.ofp_port) { VLOG_DBG_RL(&rl, "%016llx: learned that "ETH_ADDR_FMT" is on " "port %"PRIu16, sw->datapath_id, ETH_ADDR_ARGS(flow->dl_src), flow->in_port.ofp_port); set_mac_entry_ofp_port(sw->ml, mac, flow->in_port.ofp_port); } } ovs_rwlock_unlock(&sw->ml->rwlock); } /* Drop frames for reserved multicast addresses. */ if (eth_addr_is_reserved(flow->dl_dst)) { return OFPP_NONE; } out_port = OFPP_FLOOD; if (sw->ml) { struct mac_entry *mac; ovs_rwlock_rdlock(&sw->ml->rwlock); mac = mac_learning_lookup(sw->ml, flow->dl_dst, 0); if (mac) { out_port = get_mac_entry_ofp_port(sw->ml, mac); if (out_port == flow->in_port.ofp_port) { /* Don't send a packet back out its input port. */ ovs_rwlock_unlock(&sw->ml->rwlock); return OFPP_NONE; } } ovs_rwlock_unlock(&sw->ml->rwlock); } /* Check if we need to use "NORMAL" action. */ if (sw->action_normal && out_port != OFPP_FLOOD) { return OFPP_NORMAL; } return out_port; } static uint32_t get_queue_id(const struct lswitch *sw, ofp_port_t in_port) { const struct lswitch_port *port; HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_ofp_port(in_port), &sw->queue_numbers) { if (port->port_no == in_port) { return port->queue_id; } } return sw->default_queue; } static void process_packet_in(struct lswitch *sw, const struct ofp_header *oh) { struct ofputil_packet_in pi; uint32_t queue_id; ofp_port_t out_port; uint64_t ofpacts_stub[64 / 8]; struct ofpbuf ofpacts; struct ofputil_packet_out po; enum ofperr error; struct dp_packet pkt; struct flow flow; error = ofputil_decode_packet_in(&pi, oh); if (error) { VLOG_WARN_RL(&rl, "failed to decode packet-in: %s", ofperr_to_string(error)); return; } /* Ignore packets sent via output to OFPP_CONTROLLER. This library never * uses such an action. You never know what experiments might be going on, * though, and it seems best not to interfere with them. */ if (pi.reason != OFPR_NO_MATCH) { return; } /* Extract flow data from 'opi' into 'flow'. */ dp_packet_use_const(&pkt, pi.packet, pi.packet_len); flow_extract(&pkt, &flow); flow.in_port.ofp_port = pi.flow_metadata.flow.in_port.ofp_port; flow.tunnel.tun_id = pi.flow_metadata.flow.tunnel.tun_id; /* Choose output port. */ out_port = lswitch_choose_destination(sw, &flow); /* Make actions. */ queue_id = get_queue_id(sw, pi.flow_metadata.flow.in_port.ofp_port); ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); if (out_port == OFPP_NONE) { /* No actions. */ } else if (queue_id == UINT32_MAX || ofp_to_u16(out_port) >= ofp_to_u16(OFPP_MAX)) { ofpact_put_OUTPUT(&ofpacts)->port = out_port; } else { struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts); enqueue->port = out_port; enqueue->queue = queue_id; } ofpact_pad(&ofpacts); /* Prepare packet_out in case we need one. */ po.buffer_id = pi.buffer_id; if (po.buffer_id == UINT32_MAX) { po.packet = dp_packet_data(&pkt); po.packet_len = dp_packet_size(&pkt); } else { po.packet = NULL; po.packet_len = 0; } po.in_port = pi.flow_metadata.flow.in_port.ofp_port; po.ofpacts = ofpacts.data; po.ofpacts_len = ofpacts.size; /* Send the packet, and possibly the whole flow, to the output port. */ if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) { struct ofputil_flow_mod fm; struct ofpbuf *buffer; /* The output port is known, or we always flood everything, so add a * new flow. */ memset(&fm, 0, sizeof fm); match_init(&fm.match, &flow, &sw->wc); ofputil_normalize_match_quiet(&fm.match); fm.priority = 1; /* Must be > 0 because of table-miss flow entry. */ fm.table_id = 0xff; fm.command = OFPFC_ADD; fm.idle_timeout = sw->max_idle; fm.buffer_id = pi.buffer_id; fm.out_port = OFPP_NONE; fm.ofpacts = ofpacts.data; fm.ofpacts_len = ofpacts.size; buffer = ofputil_encode_flow_mod(&fm, sw->protocol); queue_tx(sw, buffer); /* If the switch didn't buffer the packet, we need to send a copy. */ if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) { queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol)); } } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) { queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol)); } } } static void process_echo_request(struct lswitch *sw, const struct ofp_header *rq) { queue_tx(sw, make_echo_reply(rq)); } static ofp_port_t get_mac_entry_ofp_port(const struct mac_learning *ml, const struct mac_entry *e) OVS_REQ_RDLOCK(ml->rwlock) { void *port = mac_entry_get_port(ml, e); return (OVS_FORCE ofp_port_t) (uintptr_t) port; } static void set_mac_entry_ofp_port(struct mac_learning *ml, struct mac_entry *e, ofp_port_t ofp_port) OVS_REQ_WRLOCK(ml->rwlock) { mac_entry_set_port(ml, e, (void *) (OVS_FORCE uintptr_t) ofp_port); } openvswitch-2.5.9/lib/PaxHeaders.82075/ovs-thread.c0000644000000000000000000000013213534540071016632 xustar0030 mtime=1567801401.549682257 30 atime=1567801402.089686223 30 ctime=1567801424.829853781 openvswitch-2.5.9/lib/ovs-thread.c0000644000175000017500000006123013534540071020322 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovs-thread.h" #include #include #ifndef _WIN32 #include #endif #include #include #include "compiler.h" #include "fatal-signal.h" #include "hash.h" #include "list.h" #include "netdev-dpdk.h" #include "ovs-rcu.h" #include "poll-loop.h" #include "seq.h" #include "socket-util.h" #include "util.h" #ifdef __CHECKER__ /* Omit the definitions in this file because they are somewhat difficult to * write without prompting "sparse" complaints, without ugliness or * cut-and-paste. Since "sparse" is just a checker, not a compiler, it * doesn't matter that we don't define them. */ #else #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovs_thread); /* If there is a reason that we cannot fork anymore (unless the fork will be * immediately followed by an exec), then this points to a string that * explains why. */ static const char *must_not_fork; /* True if we created any threads beyond the main initial thread. */ static bool multithreaded; #define LOCK_FUNCTION(TYPE, FUN) \ void \ ovs_##TYPE##_##FUN##_at(const struct ovs_##TYPE *l_, \ const char *where) \ OVS_NO_THREAD_SAFETY_ANALYSIS \ { \ struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \ int error; \ \ /* Verify that 'l' was initialized. */ \ if (OVS_UNLIKELY(!l->where)) { \ ovs_abort(0, "%s: %s() passed uninitialized ovs_"#TYPE, \ where, __func__); \ } \ \ error = pthread_##TYPE##_##FUN(&l->lock); \ if (OVS_UNLIKELY(error)) { \ ovs_abort(error, "%s: pthread_%s_%s failed", where, #TYPE, #FUN); \ } \ l->where = where; \ } LOCK_FUNCTION(mutex, lock); LOCK_FUNCTION(rwlock, rdlock); LOCK_FUNCTION(rwlock, wrlock); #define TRY_LOCK_FUNCTION(TYPE, FUN) \ int \ ovs_##TYPE##_##FUN##_at(const struct ovs_##TYPE *l_, \ const char *where) \ OVS_NO_THREAD_SAFETY_ANALYSIS \ { \ struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \ int error; \ \ /* Verify that 'l' was initialized. */ \ if (OVS_UNLIKELY(!l->where)) { \ ovs_abort(0, "%s: %s() passed uninitialized ovs_"#TYPE, \ where, __func__); \ } \ \ error = pthread_##TYPE##_##FUN(&l->lock); \ if (OVS_UNLIKELY(error) && error != EBUSY) { \ ovs_abort(error, "%s: pthread_%s_%s failed", where, #TYPE, #FUN); \ } \ if (!error) { \ l->where = where; \ } \ return error; \ } TRY_LOCK_FUNCTION(mutex, trylock); TRY_LOCK_FUNCTION(rwlock, tryrdlock); TRY_LOCK_FUNCTION(rwlock, trywrlock); #define UNLOCK_FUNCTION(TYPE, FUN, WHERE) \ void \ ovs_##TYPE##_##FUN(const struct ovs_##TYPE *l_) \ OVS_NO_THREAD_SAFETY_ANALYSIS \ { \ struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \ int error; \ \ /* Verify that 'l' was initialized. */ \ ovs_assert(l->where); \ \ l->where = WHERE; \ error = pthread_##TYPE##_##FUN(&l->lock); \ if (OVS_UNLIKELY(error)) { \ ovs_abort(error, "pthread_%s_%sfailed", #TYPE, #FUN); \ } \ } UNLOCK_FUNCTION(mutex, unlock, ""); UNLOCK_FUNCTION(mutex, destroy, NULL); UNLOCK_FUNCTION(rwlock, unlock, ""); UNLOCK_FUNCTION(rwlock, destroy, NULL); #define XPTHREAD_FUNC1(FUNCTION, PARAM1) \ void \ x##FUNCTION(PARAM1 arg1) \ { \ int error = FUNCTION(arg1); \ if (OVS_UNLIKELY(error)) { \ ovs_abort(error, "%s failed", #FUNCTION); \ } \ } #define XPTHREAD_FUNC2(FUNCTION, PARAM1, PARAM2) \ void \ x##FUNCTION(PARAM1 arg1, PARAM2 arg2) \ { \ int error = FUNCTION(arg1, arg2); \ if (OVS_UNLIKELY(error)) { \ ovs_abort(error, "%s failed", #FUNCTION); \ } \ } #define XPTHREAD_FUNC3(FUNCTION, PARAM1, PARAM2, PARAM3)\ void \ x##FUNCTION(PARAM1 arg1, PARAM2 arg2, PARAM3 arg3) \ { \ int error = FUNCTION(arg1, arg2, arg3); \ if (OVS_UNLIKELY(error)) { \ ovs_abort(error, "%s failed", #FUNCTION); \ } \ } XPTHREAD_FUNC1(pthread_mutex_lock, pthread_mutex_t *); XPTHREAD_FUNC1(pthread_mutex_unlock, pthread_mutex_t *); XPTHREAD_FUNC1(pthread_mutexattr_init, pthread_mutexattr_t *); XPTHREAD_FUNC1(pthread_mutexattr_destroy, pthread_mutexattr_t *); XPTHREAD_FUNC2(pthread_mutexattr_settype, pthread_mutexattr_t *, int); XPTHREAD_FUNC2(pthread_mutexattr_gettype, pthread_mutexattr_t *, int *); XPTHREAD_FUNC1(pthread_rwlockattr_init, pthread_rwlockattr_t *); XPTHREAD_FUNC1(pthread_rwlockattr_destroy, pthread_rwlockattr_t *); #ifdef PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP XPTHREAD_FUNC2(pthread_rwlockattr_setkind_np, pthread_rwlockattr_t *, int); #endif XPTHREAD_FUNC2(pthread_cond_init, pthread_cond_t *, pthread_condattr_t *); XPTHREAD_FUNC1(pthread_cond_destroy, pthread_cond_t *); XPTHREAD_FUNC1(pthread_cond_signal, pthread_cond_t *); XPTHREAD_FUNC1(pthread_cond_broadcast, pthread_cond_t *); XPTHREAD_FUNC2(pthread_join, pthread_t, void **); typedef void destructor_func(void *); XPTHREAD_FUNC2(pthread_key_create, pthread_key_t *, destructor_func *); XPTHREAD_FUNC1(pthread_key_delete, pthread_key_t); XPTHREAD_FUNC2(pthread_setspecific, pthread_key_t, const void *); #ifndef _WIN32 XPTHREAD_FUNC3(pthread_sigmask, int, const sigset_t *, sigset_t *); #endif static void ovs_mutex_init__(const struct ovs_mutex *l_, int type) { struct ovs_mutex *l = CONST_CAST(struct ovs_mutex *, l_); pthread_mutexattr_t attr; int error; l->where = ""; xpthread_mutexattr_init(&attr); xpthread_mutexattr_settype(&attr, type); error = pthread_mutex_init(&l->lock, &attr); if (OVS_UNLIKELY(error)) { ovs_abort(error, "pthread_mutex_init failed"); } xpthread_mutexattr_destroy(&attr); } /* Initializes 'mutex' as a normal (non-recursive) mutex. */ void ovs_mutex_init(const struct ovs_mutex *mutex) { ovs_mutex_init__(mutex, PTHREAD_MUTEX_ERRORCHECK); } /* Initializes 'mutex' as a recursive mutex. */ void ovs_mutex_init_recursive(const struct ovs_mutex *mutex) { ovs_mutex_init__(mutex, PTHREAD_MUTEX_RECURSIVE); } /* Initializes 'mutex' as a recursive mutex. */ void ovs_mutex_init_adaptive(const struct ovs_mutex *mutex) { #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP ovs_mutex_init__(mutex, PTHREAD_MUTEX_ADAPTIVE_NP); #else ovs_mutex_init(mutex); #endif } void ovs_rwlock_init(const struct ovs_rwlock *l_) { struct ovs_rwlock *l = CONST_CAST(struct ovs_rwlock *, l_); pthread_rwlockattr_t attr; int error; l->where = ""; xpthread_rwlockattr_init(&attr); #ifdef PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP xpthread_rwlockattr_setkind_np( &attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif error = pthread_rwlock_init(&l->lock, NULL); if (OVS_UNLIKELY(error)) { ovs_abort(error, "pthread_rwlock_init failed"); } xpthread_rwlockattr_destroy(&attr); } void ovs_mutex_cond_wait(pthread_cond_t *cond, const struct ovs_mutex *mutex_) { struct ovs_mutex *mutex = CONST_CAST(struct ovs_mutex *, mutex_); int error; ovsrcu_quiesce_start(); error = pthread_cond_wait(cond, &mutex->lock); ovsrcu_quiesce_end(); if (OVS_UNLIKELY(error)) { ovs_abort(error, "pthread_cond_wait failed"); } } /* Initializes the 'barrier'. 'size' is the number of threads * expected to hit the barrier. */ void ovs_barrier_init(struct ovs_barrier *barrier, uint32_t size) { barrier->size = size; atomic_count_init(&barrier->count, 0); barrier->seq = seq_create(); } /* Destroys the 'barrier'. */ void ovs_barrier_destroy(struct ovs_barrier *barrier) { seq_destroy(barrier->seq); } /* Makes the calling thread block on the 'barrier' until all * 'barrier->size' threads hit the barrier. * ovs_barrier provides the necessary acquire-release semantics to make * the effects of prior memory accesses of all the participating threads * visible on return and to prevent the following memory accesses to be * reordered before the ovs_barrier_block(). */ void ovs_barrier_block(struct ovs_barrier *barrier) { uint64_t seq = seq_read(barrier->seq); uint32_t orig; orig = atomic_count_inc(&barrier->count); if (orig + 1 == barrier->size) { atomic_count_set(&barrier->count, 0); /* seq_change() serves as a release barrier against the other threads, * so the zeroed count is visible to them as they continue. */ seq_change(barrier->seq); } else { /* To prevent thread from waking up by other event, * keeps waiting for the change of 'barrier->seq'. */ while (seq == seq_read(barrier->seq)) { seq_wait(barrier->seq, seq); poll_block(); } } } DEFINE_EXTERN_PER_THREAD_DATA(ovsthread_id, 0); struct ovsthread_aux { void *(*start)(void *); void *arg; char name[16]; }; static void * ovsthread_wrapper(void *aux_) { static atomic_count next_id = ATOMIC_COUNT_INIT(1); struct ovsthread_aux *auxp = aux_; struct ovsthread_aux aux; unsigned int id; id = atomic_count_inc(&next_id); *ovsthread_id_get() = id; aux = *auxp; free(auxp); /* The order of the following calls is important, because * ovsrcu_quiesce_end() saves a copy of the thread name. */ char *subprogram_name = xasprintf("%s%u", aux.name, id); set_subprogram_name(subprogram_name); free(subprogram_name); ovsrcu_quiesce_end(); return aux.start(aux.arg); } static void set_min_stack_size(pthread_attr_t *attr, size_t min_stacksize) { size_t stacksize; int error; error = pthread_attr_getstacksize(attr, &stacksize); if (error) { ovs_abort(error, "pthread_attr_getstacksize failed"); } if (stacksize < min_stacksize) { error = pthread_attr_setstacksize(attr, min_stacksize); if (error) { ovs_abort(error, "pthread_attr_setstacksize failed"); } } } /* Starts a thread that calls 'start(arg)'. Sets the thread's name to 'name' * (suffixed by its ovsthread_id()). Returns the new thread's pthread_t. */ pthread_t ovs_thread_create(const char *name, void *(*start)(void *), void *arg) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct ovsthread_aux *aux; pthread_t thread; int error; forbid_forking("multiple threads exist"); multithreaded = true; if (ovsthread_once_start(&once)) { /* The first call to this function has to happen in the main thread. * Before the process becomes multithreaded we make sure that the * main thread is considered non quiescent. * * For other threads this is done in ovs_thread_wrapper(), but the * main thread has no such wrapper. * * There's no reason to call ovsrcu_quiesce_end() in subsequent * invocations of this function and it might introduce problems * for other threads. */ ovsrcu_quiesce_end(); ovsthread_once_done(&once); } aux = xmalloc(sizeof *aux); aux->start = start; aux->arg = arg; ovs_strlcpy(aux->name, name, sizeof aux->name); /* Some small systems use a default stack size as small as 80 kB, but OVS * requires approximately 384 kB according to the following analysis: * http://openvswitch.org/pipermail/dev/2016-January/065049.html * * We use 512 kB to give us some margin of error. */ pthread_attr_t attr; pthread_attr_init(&attr); set_min_stack_size(&attr, 512 * 1024); error = pthread_create(&thread, &attr, ovsthread_wrapper, aux); if (error) { ovs_abort(error, "pthread_create failed"); } pthread_attr_destroy(&attr); return thread; } bool ovsthread_once_start__(struct ovsthread_once *once) { ovs_mutex_lock(&once->mutex); /* Mutex synchronizes memory, so we get the current value of 'done'. */ if (!once->done) { return true; } ovs_mutex_unlock(&once->mutex); return false; } void ovsthread_once_done(struct ovsthread_once *once) { /* We need release semantics here, so that the following store may not * be moved ahead of any of the preceding initialization operations. * A release atomic_thread_fence provides that prior memory accesses * will not be reordered to take place after the following store. */ atomic_thread_fence(memory_order_release); once->done = true; ovs_mutex_unlock(&once->mutex); } bool single_threaded(void) { return !multithreaded; } /* Asserts that the process has not yet created any threads (beyond the initial * thread). * * ('where' is used in logging. Commonly one would use * assert_single_threaded() to automatically provide the caller's source file * and line number for 'where'.) */ void assert_single_threaded_at(const char *where) { if (multithreaded) { VLOG_FATAL("%s: attempted operation not allowed when multithreaded", where); } } #ifndef _WIN32 /* Forks the current process (checking that this is allowed). Aborts with * VLOG_FATAL if fork() returns an error, and otherwise returns the value * returned by fork(). * * ('where' is used in logging. Commonly one would use xfork() to * automatically provide the caller's source file and line number for * 'where'.) */ pid_t xfork_at(const char *where) { pid_t pid; if (must_not_fork) { VLOG_FATAL("%s: attempted to fork but forking not allowed (%s)", where, must_not_fork); } pid = fork(); if (pid < 0) { VLOG_FATAL("%s: fork failed (%s)", where, ovs_strerror(errno)); } return pid; } #endif /* Notes that the process must not call fork() from now on, for the specified * 'reason'. (The process may still fork() if it execs itself immediately * afterward.) */ void forbid_forking(const char *reason) { ovs_assert(reason != NULL); must_not_fork = reason; } /* Returns true if the process is allowed to fork, false otherwise. */ bool may_fork(void) { return !must_not_fork; } /* ovsthread_stats. */ void ovsthread_stats_init(struct ovsthread_stats *stats) { int i; ovs_mutex_init(&stats->mutex); for (i = 0; i < ARRAY_SIZE(stats->buckets); i++) { stats->buckets[i] = NULL; } } void ovsthread_stats_destroy(struct ovsthread_stats *stats) { ovs_mutex_destroy(&stats->mutex); } void * ovsthread_stats_bucket_get(struct ovsthread_stats *stats, void *(*new_bucket)(void)) { unsigned int idx = ovsthread_id_self() & (ARRAY_SIZE(stats->buckets) - 1); void *bucket = stats->buckets[idx]; if (!bucket) { ovs_mutex_lock(&stats->mutex); bucket = stats->buckets[idx]; if (!bucket) { bucket = stats->buckets[idx] = new_bucket(); } ovs_mutex_unlock(&stats->mutex); } return bucket; } size_t ovs_thread_stats_next_bucket(const struct ovsthread_stats *stats, size_t i) { for (; i < ARRAY_SIZE(stats->buckets); i++) { if (stats->buckets[i]) { break; } } return i; } /* Parses /proc/cpuinfo for the total number of physical cores on this system * across all CPU packages, not counting hyper-threads. * * Sets *n_cores to the total number of cores on this system, or 0 if the * number cannot be determined. */ static void parse_cpuinfo(long int *n_cores) { static const char file_name[] = "/proc/cpuinfo"; char line[128]; uint64_t cpu = 0; /* Support up to 64 CPU packages on a single system. */ long int cores = 0; FILE *stream; stream = fopen(file_name, "r"); if (!stream) { VLOG_DBG("%s: open failed (%s)", file_name, ovs_strerror(errno)); return; } while (fgets(line, sizeof line, stream)) { unsigned int id; /* Find the next CPU package. */ if (ovs_scan(line, "physical id%*[^:]: %u", &id)) { if (id > 63) { VLOG_WARN("Counted over 64 CPU packages on this system. " "Parsing %s for core count may be inaccurate.", file_name); cores = 0; break; } if (cpu & (1ULL << id)) { /* We've already counted this package's cores. */ continue; } cpu |= 1ULL << id; /* Find the number of cores for this package. */ while (fgets(line, sizeof line, stream)) { int count; if (ovs_scan(line, "cpu cores%*[^:]: %u", &count)) { cores += count; break; } } } } fclose(stream); *n_cores = cores; } /* Returns the total number of cores on this system, or 0 if the number cannot * be determined. * * Tries not to count hyper-threads, but may be inaccurate - particularly on * platforms that do not provide /proc/cpuinfo, but also if /proc/cpuinfo is * formatted different to the layout that parse_cpuinfo() expects. */ int count_cpu_cores(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static long int n_cores; if (ovsthread_once_start(&once)) { #ifndef _WIN32 parse_cpuinfo(&n_cores); if (!n_cores) { n_cores = sysconf(_SC_NPROCESSORS_ONLN); } #else SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); n_cores = sysinfo.dwNumberOfProcessors; #endif ovsthread_once_done(&once); } return n_cores > 0 ? n_cores : 0; } /* Returns 'true' if current thread is PMD thread. */ bool thread_is_pmd(void) { const char *name = get_subprogram_name(); return !strncmp(name, "pmd", 3); } /* ovsthread_key. */ #define L1_SIZE 1024 #define L2_SIZE 1024 #define MAX_KEYS (L1_SIZE * L2_SIZE) /* A piece of thread-specific data. */ struct ovsthread_key { struct ovs_list list_node; /* In 'inuse_keys' or 'free_keys'. */ void (*destructor)(void *); /* Called at thread exit. */ /* Indexes into the per-thread array in struct ovsthread_key_slots. * This key's data is stored in p1[index / L2_SIZE][index % L2_SIZE]. */ unsigned int index; }; /* Per-thread data structure. */ struct ovsthread_key_slots { struct ovs_list list_node; /* In 'slots_list'. */ void **p1[L1_SIZE]; }; /* Contains "struct ovsthread_key_slots *". */ static pthread_key_t tsd_key; /* Guards data structures below. */ static struct ovs_mutex key_mutex = OVS_MUTEX_INITIALIZER; /* 'inuse_keys' holds "struct ovsthread_key"s that have been created and not * yet destroyed. * * 'free_keys' holds "struct ovsthread_key"s that have been deleted and are * ready for reuse. (We keep them around only to be able to easily locate * free indexes.) * * Together, 'inuse_keys' and 'free_keys' hold an ovsthread_key for every index * from 0 to n_keys - 1, inclusive. */ static struct ovs_list inuse_keys OVS_GUARDED_BY(key_mutex) = OVS_LIST_INITIALIZER(&inuse_keys); static struct ovs_list free_keys OVS_GUARDED_BY(key_mutex) = OVS_LIST_INITIALIZER(&free_keys); static unsigned int n_keys OVS_GUARDED_BY(key_mutex); /* All existing struct ovsthread_key_slots. */ static struct ovs_list slots_list OVS_GUARDED_BY(key_mutex) = OVS_LIST_INITIALIZER(&slots_list); static void * clear_slot(struct ovsthread_key_slots *slots, unsigned int index) { void **p2 = slots->p1[index / L2_SIZE]; if (p2) { void **valuep = &p2[index % L2_SIZE]; void *value = *valuep; *valuep = NULL; return value; } else { return NULL; } } static void ovsthread_key_destruct__(void *slots_) { struct ovsthread_key_slots *slots = slots_; struct ovsthread_key *key; unsigned int n; int i; ovs_mutex_lock(&key_mutex); list_remove(&slots->list_node); LIST_FOR_EACH (key, list_node, &inuse_keys) { void *value = clear_slot(slots, key->index); if (value && key->destructor) { key->destructor(value); } } n = n_keys; ovs_mutex_unlock(&key_mutex); for (i = 0; i < DIV_ROUND_UP(n, L2_SIZE); i++) { free(slots->p1[i]); } free(slots); } /* Cancels the callback to ovsthread_key_destruct__(). * * Cancelling the call to the destructor during the main thread exit * is needed while using pthreads-win32 library in Windows. It has been * observed that in pthreads-win32, a call to the destructor during * main thread exit causes undefined behavior. */ static void ovsthread_cancel_ovsthread_key_destruct__(void *aux OVS_UNUSED) { pthread_setspecific(tsd_key, NULL); } /* Initializes '*keyp' as a thread-specific data key. The data items are * initially null in all threads. * * If a thread exits with non-null data, then 'destructor', if nonnull, will be * called passing the final data value as its argument. 'destructor' must not * call any thread-specific data functions in this API. * * This function is similar to xpthread_key_create(). */ void ovsthread_key_create(ovsthread_key_t *keyp, void (*destructor)(void *)) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct ovsthread_key *key; if (ovsthread_once_start(&once)) { xpthread_key_create(&tsd_key, ovsthread_key_destruct__); fatal_signal_add_hook(ovsthread_cancel_ovsthread_key_destruct__, NULL, NULL, true); ovsthread_once_done(&once); } ovs_mutex_lock(&key_mutex); if (list_is_empty(&free_keys)) { key = xmalloc(sizeof *key); key->index = n_keys++; if (key->index >= MAX_KEYS) { abort(); } } else { key = CONTAINER_OF(list_pop_back(&free_keys), struct ovsthread_key, list_node); } list_push_back(&inuse_keys, &key->list_node); key->destructor = destructor; ovs_mutex_unlock(&key_mutex); *keyp = key; } /* Frees 'key'. The destructor supplied to ovsthread_key_create(), if any, is * not called. * * This function is similar to xpthread_key_delete(). */ void ovsthread_key_delete(ovsthread_key_t key) { struct ovsthread_key_slots *slots; ovs_mutex_lock(&key_mutex); /* Move 'key' from 'inuse_keys' to 'free_keys'. */ list_remove(&key->list_node); list_push_back(&free_keys, &key->list_node); /* Clear this slot in all threads. */ LIST_FOR_EACH (slots, list_node, &slots_list) { clear_slot(slots, key->index); } ovs_mutex_unlock(&key_mutex); } static void ** ovsthread_key_lookup__(const struct ovsthread_key *key) { struct ovsthread_key_slots *slots; void **p2; slots = pthread_getspecific(tsd_key); if (!slots) { slots = xzalloc(sizeof *slots); ovs_mutex_lock(&key_mutex); pthread_setspecific(tsd_key, slots); list_push_back(&slots_list, &slots->list_node); ovs_mutex_unlock(&key_mutex); } p2 = slots->p1[key->index / L2_SIZE]; if (!p2) { p2 = xzalloc(L2_SIZE * sizeof *p2); slots->p1[key->index / L2_SIZE] = p2; } return &p2[key->index % L2_SIZE]; } /* Sets the value of thread-specific data item 'key', in the current thread, to * 'value'. * * This function is similar to pthread_setspecific(). */ void ovsthread_setspecific(ovsthread_key_t key, const void *value) { *ovsthread_key_lookup__(key) = CONST_CAST(void *, value); } /* Returns the value of thread-specific data item 'key' in the current thread. * * This function is similar to pthread_getspecific(). */ void * ovsthread_getspecific(ovsthread_key_t key) { return *ovsthread_key_lookup__(key); } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/list.h0000644000000000000000000000013113534540071015535 xustar0029 mtime=1567801401.40168117 30 atime=1567801402.077686135 30 ctime=1567801424.753853219 openvswitch-2.5.9/lib/list.h0000644000175000017500000002230013534540071017221 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LIST_H #define LIST_H 1 /* Doubly linked list. */ #include #include #include "util.h" #include "openvswitch/list.h" /* "struct ovs_list" with pointers that will (probably) cause segfaults if * dereferenced and, better yet, show up clearly in a debugger. */ #define OVS_LIST_POISON \ (struct ovs_list) { (struct ovs_list *) (uintptr_t) 0xccccccccccccccccULL, \ (struct ovs_list *) (uintptr_t) 0xccccccccccccccccULL } static inline void list_init(struct ovs_list *); static inline void list_poison(struct ovs_list *); /* List insertion. */ static inline void list_insert(struct ovs_list *, struct ovs_list *); static inline void list_splice(struct ovs_list *before, struct ovs_list *first, struct ovs_list *last); static inline void list_push_front(struct ovs_list *, struct ovs_list *); static inline void list_push_back(struct ovs_list *, struct ovs_list *); static inline void list_replace(struct ovs_list *, const struct ovs_list *); static inline void list_moved(struct ovs_list *, const struct ovs_list *orig); static inline void list_move(struct ovs_list *dst, struct ovs_list *src); /* List removal. */ static inline struct ovs_list *list_remove(struct ovs_list *); static inline struct ovs_list *list_pop_front(struct ovs_list *); static inline struct ovs_list *list_pop_back(struct ovs_list *); /* List elements. */ static inline struct ovs_list *list_front(const struct ovs_list *); static inline struct ovs_list *list_back(const struct ovs_list *); /* List properties. */ static inline size_t list_size(const struct ovs_list *); static inline bool list_is_empty(const struct ovs_list *); static inline bool list_is_singleton(const struct ovs_list *); static inline bool list_is_short(const struct ovs_list *); #define LIST_FOR_EACH(ITER, MEMBER, LIST) \ for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER); \ &(ITER)->MEMBER != (LIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER)) #define LIST_FOR_EACH_CONTINUE(ITER, MEMBER, LIST) \ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER); \ &(ITER)->MEMBER != (LIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.next, MEMBER)) #define LIST_FOR_EACH_REVERSE(ITER, MEMBER, LIST) \ for (INIT_CONTAINER(ITER, (LIST)->prev, MEMBER); \ &(ITER)->MEMBER != (LIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) #define LIST_FOR_EACH_REVERSE_CONTINUE(ITER, MEMBER, LIST) \ for (ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER); \ &(ITER)->MEMBER != (LIST); \ ASSIGN_CONTAINER(ITER, (ITER)->MEMBER.prev, MEMBER)) #define LIST_FOR_EACH_SAFE(ITER, NEXT, MEMBER, LIST) \ for (INIT_CONTAINER(ITER, (LIST)->next, MEMBER); \ (&(ITER)->MEMBER != (LIST) \ ? INIT_CONTAINER(NEXT, (ITER)->MEMBER.next, MEMBER), 1 \ : 0); \ (ITER) = (NEXT)) #define LIST_FOR_EACH_POP(ITER, MEMBER, LIST) \ while (!list_is_empty(LIST) \ && (INIT_CONTAINER(ITER, list_pop_front(LIST), MEMBER), 1)) /* Inline implementations. */ /* Initializes 'list' as an empty list. */ static inline void list_init(struct ovs_list *list) { list->next = list->prev = list; } /* Initializes 'list' with pointers that will (probably) cause segfaults if * dereferenced and, better yet, show up clearly in a debugger. */ static inline void list_poison(struct ovs_list *list) { *list = OVS_LIST_POISON; } /* Inserts 'elem' just before 'before'. */ static inline void list_insert(struct ovs_list *before, struct ovs_list *elem) { elem->prev = before->prev; elem->next = before; before->prev->next = elem; before->prev = elem; } /* Removes elements 'first' though 'last' (exclusive) from their current list, then inserts them just before 'before'. */ static inline void list_splice(struct ovs_list *before, struct ovs_list *first, struct ovs_list *last) { if (first == last) { return; } last = last->prev; /* Cleanly remove 'first'...'last' from its current list. */ first->prev->next = last->next; last->next->prev = first->prev; /* Splice 'first'...'last' into new list. */ first->prev = before->prev; last->next = before; before->prev->next = first; before->prev = last; } /* Inserts 'elem' at the beginning of 'list', so that it becomes the front in 'list'. */ static inline void list_push_front(struct ovs_list *list, struct ovs_list *elem) { list_insert(list->next, elem); } /* Inserts 'elem' at the end of 'list', so that it becomes the back in * 'list'. */ static inline void list_push_back(struct ovs_list *list, struct ovs_list *elem) { list_insert(list, elem); } /* Puts 'elem' in the position currently occupied by 'position'. * Afterward, 'position' is not part of a list. */ static inline void list_replace(struct ovs_list *element, const struct ovs_list *position) { element->next = position->next; element->next->prev = element; element->prev = position->prev; element->prev->next = element; } /* Adjusts pointers around 'list' to compensate for 'list' having been moved * around in memory (e.g. as a consequence of realloc()), with original * location 'orig'. * * ('orig' likely points to freed memory, but this function does not * dereference 'orig', it only compares it to 'list'. In a very pedantic * language lawyer sense, this still yields undefined behavior, but it works * with actual compilers.) */ static inline void list_moved(struct ovs_list *list, const struct ovs_list *orig) { if (list->next == orig) { list_init(list); } else { list->prev->next = list->next->prev = list; } } /* Initializes 'dst' with the contents of 'src', compensating for moving it * around in memory. The effect is that, if 'src' was the head of a list, now * 'dst' is the head of a list containing the same elements. */ static inline void list_move(struct ovs_list *dst, struct ovs_list *src) { *dst = *src; list_moved(dst, src); } /* Removes 'elem' from its list and returns the element that followed it. Undefined behavior if 'elem' is not in a list. */ static inline struct ovs_list * list_remove(struct ovs_list *elem) { elem->prev->next = elem->next; elem->next->prev = elem->prev; return elem->next; } /* Removes the front element from 'list' and returns it. Undefined behavior if 'list' is empty before removal. */ static inline struct ovs_list * list_pop_front(struct ovs_list *list) { struct ovs_list *front = list->next; list_remove(front); return front; } /* Removes the back element from 'list' and returns it. Undefined behavior if 'list' is empty before removal. */ static inline struct ovs_list * list_pop_back(struct ovs_list *list) { struct ovs_list *back = list->prev; list_remove(back); return back; } /* Returns the front element in 'list_'. Undefined behavior if 'list_' is empty. */ static inline struct ovs_list * list_front(const struct ovs_list *list_) { struct ovs_list *list = CONST_CAST(struct ovs_list *, list_); ovs_assert(!list_is_empty(list)); return list->next; } /* Returns the back element in 'list_'. Undefined behavior if 'list_' is empty. */ static inline struct ovs_list * list_back(const struct ovs_list *list_) { struct ovs_list *list = CONST_CAST(struct ovs_list *, list_); ovs_assert(!list_is_empty(list)); return list->prev; } /* Returns the number of elements in 'list'. Runs in O(n) in the number of elements. */ static inline size_t list_size(const struct ovs_list *list) { const struct ovs_list *e; size_t cnt = 0; for (e = list->next; e != list; e = e->next) { cnt++; } return cnt; } /* Returns true if 'list' is empty, false otherwise. */ static inline bool list_is_empty(const struct ovs_list *list) { return list->next == list; } /* Returns true if 'list' has exactly 1 element, false otherwise. */ static inline bool list_is_singleton(const struct ovs_list *list) { return list_is_short(list) && !list_is_empty(list); } /* Returns true if 'list' has 0 or 1 elements, false otherwise. */ static inline bool list_is_short(const struct ovs_list *list) { return list->next == list->prev; } #endif /* list.h */ openvswitch-2.5.9/lib/PaxHeaders.82075/util.c0000644000000000000000000000013213534540071015533 xustar0030 mtime=1567801401.609682697 30 atime=1567801402.101686312 30 ctime=1567801424.933854546 openvswitch-2.5.9/lib/util.c0000644000175000017500000015024013534540071017223 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #include "bitmap.h" #include "byte-order.h" #include "coverage.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "socket-util.h" #include "openvswitch/vlog.h" #ifdef HAVE_PTHREAD_SET_NAME_NP #include #endif VLOG_DEFINE_THIS_MODULE(util); COVERAGE_DEFINE(util_xalloc); /* argv[0] without directory names. */ char *program_name; /* Name for the currently running thread or process, for log messages, process * listings, and debuggers. */ DEFINE_PER_THREAD_MALLOCED_DATA(char *, subprogram_name); /* --version option output. */ static char *program_version; /* Buffer used by ovs_strerror() and ovs_format_message(). */ DEFINE_STATIC_PER_THREAD_DATA(struct { char s[128]; }, strerror_buffer, { "" }); static char *xreadlink(const char *filename); void ovs_assert_failure(const char *where, const char *function, const char *condition) { /* Prevent an infinite loop (or stack overflow) in case VLOG_ABORT happens * to trigger an assertion failure of its own. */ static int reentry = 0; switch (reentry++) { case 0: VLOG_ABORT("%s: assertion %s failed in %s()", where, condition, function); OVS_NOT_REACHED(); case 1: fprintf(stderr, "%s: assertion %s failed in %s()", where, condition, function); abort(); default: abort(); } } void out_of_memory(void) { ovs_abort(0, "virtual memory exhausted"); } void * xcalloc(size_t count, size_t size) { void *p = count && size ? calloc(count, size) : malloc(1); COVERAGE_INC(util_xalloc); if (p == NULL) { out_of_memory(); } return p; } void * xzalloc(size_t size) { return xcalloc(1, size); } void * xmalloc(size_t size) { void *p = malloc(size ? size : 1); COVERAGE_INC(util_xalloc); if (p == NULL) { out_of_memory(); } return p; } void * xrealloc(void *p, size_t size) { p = realloc(p, size ? size : 1); COVERAGE_INC(util_xalloc); if (p == NULL) { out_of_memory(); } return p; } void * xmemdup(const void *p_, size_t size) { void *p = xmalloc(size); memcpy(p, p_, size); return p; } char * xmemdup0(const char *p_, size_t length) { char *p = xmalloc(length + 1); memcpy(p, p_, length); p[length] = '\0'; return p; } char * xstrdup(const char *s) { return xmemdup0(s, strlen(s)); } char * xvasprintf(const char *format, va_list args) { va_list args2; size_t needed; char *s; va_copy(args2, args); needed = vsnprintf(NULL, 0, format, args); s = xmalloc(needed + 1); vsnprintf(s, needed + 1, format, args2); va_end(args2); return s; } void * x2nrealloc(void *p, size_t *n, size_t s) { *n = *n == 0 ? 1 : 2 * *n; return xrealloc(p, *n * s); } /* The desired minimum alignment for an allocated block of memory. */ #define MEM_ALIGN MAX(sizeof(void *), 8) BUILD_ASSERT_DECL(IS_POW2(MEM_ALIGN)); BUILD_ASSERT_DECL(CACHE_LINE_SIZE >= MEM_ALIGN); /* Allocates and returns 'size' bytes of memory in dedicated cache lines. That * is, the memory block returned will not share a cache line with other data, * avoiding "false sharing". (The memory returned will not be at the start of * a cache line, though, so don't assume such alignment.) * * Use free_cacheline() to free the returned memory block. */ void * xmalloc_cacheline(size_t size) { #ifdef HAVE_POSIX_MEMALIGN void *p; int error; COVERAGE_INC(util_xalloc); error = posix_memalign(&p, CACHE_LINE_SIZE, size ? size : 1); if (error != 0) { out_of_memory(); } return p; #else void **payload; void *base; /* Allocate room for: * * - Up to CACHE_LINE_SIZE - 1 bytes before the payload, so that the * start of the payload doesn't potentially share a cache line. * * - A payload consisting of a void *, followed by padding out to * MEM_ALIGN bytes, followed by 'size' bytes of user data. * * - Space following the payload up to the end of the cache line, so * that the end of the payload doesn't potentially share a cache line * with some following block. */ base = xmalloc((CACHE_LINE_SIZE - 1) + ROUND_UP(MEM_ALIGN + size, CACHE_LINE_SIZE)); /* Locate the payload and store a pointer to the base at the beginning. */ payload = (void **) ROUND_UP((uintptr_t) base, CACHE_LINE_SIZE); *payload = base; return (char *) payload + MEM_ALIGN; #endif } /* Like xmalloc_cacheline() but clears the allocated memory to all zero * bytes. */ void * xzalloc_cacheline(size_t size) { void *p = xmalloc_cacheline(size); memset(p, 0, size); return p; } /* Frees a memory block allocated with xmalloc_cacheline() or * xzalloc_cacheline(). */ void free_cacheline(void *p) { #ifdef HAVE_POSIX_MEMALIGN free(p); #else if (p) { free(*(void **) ((uintptr_t) p - MEM_ALIGN)); } #endif } char * xasprintf(const char *format, ...) { va_list args; char *s; va_start(args, format); s = xvasprintf(format, args); va_end(args); return s; } /* Similar to strlcpy() from OpenBSD, but it never reads more than 'size - 1' * bytes from 'src' and doesn't return anything. */ void ovs_strlcpy(char *dst, const char *src, size_t size) { if (size > 0) { size_t len = strnlen(src, size - 1); memcpy(dst, src, len); dst[len] = '\0'; } } /* Copies 'src' to 'dst'. Reads no more than 'size - 1' bytes from 'src'. * Always null-terminates 'dst' (if 'size' is nonzero), and writes a zero byte * to every otherwise unused byte in 'dst'. * * Except for performance, the following call: * ovs_strzcpy(dst, src, size); * is equivalent to these two calls: * memset(dst, '\0', size); * ovs_strlcpy(dst, src, size); * * (Thus, ovs_strzcpy() is similar to strncpy() without some of the pitfalls.) */ void ovs_strzcpy(char *dst, const char *src, size_t size) { if (size > 0) { size_t len = strnlen(src, size - 1); memcpy(dst, src, len); memset(dst + len, '\0', size - len); } } /* Prints 'format' on stderr, formatting it like printf() does. If 'err_no' is * nonzero, then it is formatted with ovs_retval_to_string() and appended to * the message inside parentheses. Then, terminates with abort(). * * This function is preferred to ovs_fatal() in a situation where it would make * sense for a monitoring process to restart the daemon. * * 'format' should not end with a new-line, because this function will add one * itself. */ void ovs_abort(int err_no, const char *format, ...) { va_list args; va_start(args, format); ovs_abort_valist(err_no, format, args); } /* Same as ovs_abort() except that the arguments are supplied as a va_list. */ void ovs_abort_valist(int err_no, const char *format, va_list args) { ovs_error_valist(err_no, format, args); abort(); } /* Prints 'format' on stderr, formatting it like printf() does. If 'err_no' is * nonzero, then it is formatted with ovs_retval_to_string() and appended to * the message inside parentheses. Then, terminates with EXIT_FAILURE. * * 'format' should not end with a new-line, because this function will add one * itself. */ void ovs_fatal(int err_no, const char *format, ...) { va_list args; va_start(args, format); ovs_fatal_valist(err_no, format, args); } /* Same as ovs_fatal() except that the arguments are supplied as a va_list. */ void ovs_fatal_valist(int err_no, const char *format, va_list args) { ovs_error_valist(err_no, format, args); exit(EXIT_FAILURE); } /* Prints 'format' on stderr, formatting it like printf() does. If 'err_no' is * nonzero, then it is formatted with ovs_retval_to_string() and appended to * the message inside parentheses. * * 'format' should not end with a new-line, because this function will add one * itself. */ void ovs_error(int err_no, const char *format, ...) { va_list args; va_start(args, format); ovs_error_valist(err_no, format, args); va_end(args); } /* Same as ovs_error() except that the arguments are supplied as a va_list. */ void ovs_error_valist(int err_no, const char *format, va_list args) { const char *subprogram_name = get_subprogram_name(); int save_errno = errno; if (subprogram_name[0]) { fprintf(stderr, "%s(%s): ", program_name, subprogram_name); } else { fprintf(stderr, "%s: ", program_name); } vfprintf(stderr, format, args); if (err_no != 0) { fprintf(stderr, " (%s)", ovs_retval_to_string(err_no)); } putc('\n', stderr); errno = save_errno; } /* Many OVS functions return an int which is one of: * - 0: no error yet * - >0: errno value * - EOF: end of file (not necessarily an error; depends on the function called) * * Returns the appropriate human-readable string. The caller must copy the * string if it wants to hold onto it, as the storage may be overwritten on * subsequent function calls. */ const char * ovs_retval_to_string(int retval) { return (!retval ? "" : retval == EOF ? "End of file" : ovs_strerror(retval)); } /* This function returns the string describing the error number in 'error' * for POSIX platforms. For Windows, this function can be used for C library * calls. For socket calls that are also used in Windows, use sock_strerror() * instead. For WINAPI calls, look at ovs_lasterror_to_string(). */ const char * ovs_strerror(int error) { enum { BUFSIZE = sizeof strerror_buffer_get()->s }; int save_errno; char *buffer; char *s; save_errno = errno; buffer = strerror_buffer_get()->s; #if STRERROR_R_CHAR_P /* GNU style strerror_r() might return an immutable static string, or it * might write and return 'buffer', but in either case we can pass the * returned string directly to the caller. */ s = strerror_r(error, buffer, BUFSIZE); #else /* strerror_r() returns an int. */ s = buffer; if (strerror_r(error, buffer, BUFSIZE)) { /* strerror_r() is only allowed to fail on ERANGE (because the buffer * is too short). We don't check the actual failure reason because * POSIX requires strerror_r() to return the error but old glibc * (before 2.13) returns -1 and sets errno. */ snprintf(buffer, BUFSIZE, "Unknown error %d", error); } #endif errno = save_errno; return s; } /* Sets global "program_name" and "program_version" variables. Should * be called at the beginning of main() with "argv[0]" as the argument * to 'argv0'. * * 'version' should contain the version of the caller's program. If 'version' * is the same as the VERSION #define, the caller is assumed to be part of Open * vSwitch. Otherwise, it is assumed to be an external program linking against * the Open vSwitch libraries. * * The 'date' and 'time' arguments should likely be called with * "__DATE__" and "__TIME__" to use the time the binary was built. * Alternatively, the "ovs_set_program_name" macro may be called to do this * automatically. */ void ovs_set_program_name__(const char *argv0, const char *version, const char *date, const char *time) { char *basename; #ifdef _WIN32 size_t max_len = strlen(argv0) + 1; SetErrorMode(GetErrorMode() | SEM_NOGPFAULTERRORBOX); _set_output_format(_TWO_DIGIT_EXPONENT); basename = xmalloc(max_len); _splitpath_s(argv0, NULL, 0, NULL, 0, basename, max_len, NULL, 0); #else const char *slash = strrchr(argv0, '/'); basename = xstrdup(slash ? slash + 1 : argv0); #endif assert_single_threaded(); free(program_name); /* Remove libtool prefix, if it is there */ if (strncmp(basename, "lt-", 3) == 0) { char *tmp_name = basename; basename = xstrdup(basename + 3); free(tmp_name); } program_name = basename; free(program_version); if (!strcmp(version, VERSION)) { program_version = xasprintf("%s (Open vSwitch) "VERSION"\n" "Compiled %s %s\n", program_name, date, time); } else { program_version = xasprintf("%s %s\n" "Open vSwitch Library "VERSION"\n" "Compiled %s %s\n", program_name, version, date, time); } } /* Returns the name of the currently running thread or process. */ const char * get_subprogram_name(void) { const char *name = subprogram_name_get(); return name ? name : ""; } /* Sets 'subprogram_name' as the name of the currently running thread or * process. (This appears in log messages and may also be visible in system * process listings and debuggers.) */ void set_subprogram_name(const char *subprogram_name) { char *pname = xstrdup(subprogram_name ? subprogram_name : program_name); free(subprogram_name_set(pname)); #if HAVE_GLIBC_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), pname); #elif HAVE_NETBSD_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "%s", pname); #elif HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(pthread_self(), pname); #endif } /* Returns a pointer to a string describing the program version. The * caller must not modify or free the returned string. */ const char * ovs_get_program_version(void) { return program_version; } /* Returns a pointer to a string describing the program name. The * caller must not modify or free the returned string. */ const char * ovs_get_program_name(void) { return program_name; } /* Print the version information for the program. */ void ovs_print_version(uint8_t min_ofp, uint8_t max_ofp) { printf("%s", program_version); if (min_ofp || max_ofp) { printf("OpenFlow versions %#x:%#x\n", min_ofp, max_ofp); } } /* Writes the 'size' bytes in 'buf' to 'stream' as hex bytes arranged 16 per * line. Numeric offsets are also included, starting at 'ofs' for the first * byte in 'buf'. If 'ascii' is true then the corresponding ASCII characters * are also rendered alongside. */ void ovs_hex_dump(FILE *stream, const void *buf_, size_t size, uintptr_t ofs, bool ascii) { const uint8_t *buf = buf_; const size_t per_line = 16; /* Maximum bytes per line. */ while (size > 0) { size_t start, end, n; size_t i; /* Number of bytes on this line. */ start = ofs % per_line; end = per_line; if (end - start > size) end = start + size; n = end - start; /* Print line. */ fprintf(stream, "%08"PRIxMAX" ", (uintmax_t) ROUND_DOWN(ofs, per_line)); for (i = 0; i < start; i++) fprintf(stream, " "); for (; i < end; i++) fprintf(stream, "%02x%c", buf[i - start], i == per_line / 2 - 1? '-' : ' '); if (ascii) { for (; i < per_line; i++) fprintf(stream, " "); fprintf(stream, "|"); for (i = 0; i < start; i++) fprintf(stream, " "); for (; i < end; i++) { int c = buf[i - start]; putc(c >= 32 && c < 127 ? c : '.', stream); } for (; i < per_line; i++) fprintf(stream, " "); fprintf(stream, "|"); } fprintf(stream, "\n"); ofs += n; buf += n; size -= n; } } bool str_to_int(const char *s, int base, int *i) { long long ll; bool ok = str_to_llong(s, base, &ll); *i = ll; return ok; } bool str_to_long(const char *s, int base, long *li) { long long ll; bool ok = str_to_llong(s, base, &ll); *li = ll; return ok; } bool str_to_llong(const char *s, int base, long long *x) { int save_errno = errno; char *tail; errno = 0; *x = strtoll(s, &tail, base); if (errno == EINVAL || errno == ERANGE || tail == s || *tail != '\0') { errno = save_errno; *x = 0; return false; } else { errno = save_errno; return true; } } bool str_to_uint(const char *s, int base, unsigned int *u) { long long ll; bool ok = str_to_llong(s, base, &ll); if (!ok || ll < 0 || ll > UINT_MAX) { *u = 0; return false; } else { *u = ll; return true; } } /* Converts floating-point string 's' into a double. If successful, stores * the double in '*d' and returns true; on failure, stores 0 in '*d' and * returns false. * * Underflow (e.g. "1e-9999") is not considered an error, but overflow * (e.g. "1e9999)" is. */ bool str_to_double(const char *s, double *d) { int save_errno = errno; char *tail; errno = 0; *d = strtod(s, &tail); if (errno == EINVAL || (errno == ERANGE && *d != 0) || tail == s || *tail != '\0') { errno = save_errno; *d = 0; return false; } else { errno = save_errno; return true; } } /* Returns the value of 'c' as a hexadecimal digit. */ int hexit_value(int c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'A': return 0xa; case 'b': case 'B': return 0xb; case 'c': case 'C': return 0xc; case 'd': case 'D': return 0xd; case 'e': case 'E': return 0xe; case 'f': case 'F': return 0xf; default: return -1; } } /* Returns the integer value of the 'n' hexadecimal digits starting at 's', or * UINTMAX_MAX if one of those "digits" is not really a hex digit. Sets '*ok' * to true if the conversion succeeds or to false if a non-hex digit is * detected. */ uintmax_t hexits_value(const char *s, size_t n, bool *ok) { uintmax_t value; size_t i; value = 0; for (i = 0; i < n; i++) { int hexit = hexit_value(s[i]); if (hexit < 0) { *ok = false; return UINTMAX_MAX; } value = (value << 4) + hexit; } *ok = true; return value; } /* Parses the string in 's' as an integer in either hex or decimal format and * puts the result right justified in the array 'valuep' that is 'field_width' * big. If the string is in hex format, the value may be arbitrarily large; * integers are limited to 64-bit values. (The rationale is that decimal is * likely to represent a number and 64 bits is a reasonable maximum whereas * hex could either be a number or a byte string.) * * On return 'tail' points to the first character in the string that was * not parsed as part of the value. ERANGE is returned if the value is too * large to fit in the given field. */ int parse_int_string(const char *s, uint8_t *valuep, int field_width, char **tail) { unsigned long long int integer; int i; if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) { uint8_t *hexit_str; int len = 0; int val_idx; int err = 0; s += 2; hexit_str = xmalloc(field_width * 2); for (;;) { uint8_t hexit; bool ok; s += strspn(s, " \t\r\n"); hexit = hexits_value(s, 1, &ok); if (!ok) { *tail = CONST_CAST(char *, s); break; } if (hexit != 0 || len) { if (DIV_ROUND_UP(len + 1, 2) > field_width) { err = ERANGE; goto free; } hexit_str[len] = hexit; len++; } s++; } val_idx = field_width; for (i = len - 1; i >= 0; i -= 2) { val_idx--; valuep[val_idx] = hexit_str[i]; if (i > 0) { valuep[val_idx] += hexit_str[i - 1] << 4; } } memset(valuep, 0, val_idx); free: free(hexit_str); return err; } errno = 0; integer = strtoull(s, tail, 0); if (errno) { return errno; } for (i = field_width - 1; i >= 0; i--) { valuep[i] = integer; integer >>= 8; } if (integer) { return ERANGE; } return 0; } /* Returns the current working directory as a malloc()'d string, or a null * pointer if the current working directory cannot be determined. */ char * get_cwd(void) { long int path_max; size_t size; /* Get maximum path length or at least a reasonable estimate. */ #ifndef _WIN32 path_max = pathconf(".", _PC_PATH_MAX); #else path_max = MAX_PATH; #endif size = (path_max < 0 ? 1024 : path_max > 10240 ? 10240 : path_max); /* Get current working directory. */ for (;;) { char *buf = xmalloc(size); if (getcwd(buf, size)) { return xrealloc(buf, strlen(buf) + 1); } else { int error = errno; free(buf); if (error != ERANGE) { VLOG_WARN("getcwd failed (%s)", ovs_strerror(error)); return NULL; } size *= 2; } } } static char * all_slashes_name(const char *s) { return xstrdup(s[0] == '/' && s[1] == '/' && s[2] != '/' ? "//" : s[0] == '/' ? "/" : "."); } #ifndef _WIN32 /* Returns the directory name portion of 'file_name' as a malloc()'d string, * similar to the POSIX dirname() function but thread-safe. */ char * dir_name(const char *file_name) { size_t len = strlen(file_name); while (len > 0 && file_name[len - 1] == '/') { len--; } while (len > 0 && file_name[len - 1] != '/') { len--; } while (len > 0 && file_name[len - 1] == '/') { len--; } return len ? xmemdup0(file_name, len) : all_slashes_name(file_name); } /* Returns the file name portion of 'file_name' as a malloc()'d string, * similar to the POSIX basename() function but thread-safe. */ char * base_name(const char *file_name) { size_t end, start; end = strlen(file_name); while (end > 0 && file_name[end - 1] == '/') { end--; } if (!end) { return all_slashes_name(file_name); } start = end; while (start > 0 && file_name[start - 1] != '/') { start--; } return xmemdup0(file_name + start, end - start); } #endif /* _WIN32 */ /* If 'file_name' starts with '/', returns a copy of 'file_name'. Otherwise, * returns an absolute path to 'file_name' considering it relative to 'dir', * which itself must be absolute. 'dir' may be null or the empty string, in * which case the current working directory is used. * * Returns a null pointer if 'dir' is null and getcwd() fails. */ char * abs_file_name(const char *dir, const char *file_name) { if (file_name[0] == '/') { return xstrdup(file_name); } else if (dir && dir[0]) { char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/"; return xasprintf("%s%s%s", dir, separator, file_name); } else { char *cwd = get_cwd(); if (cwd) { char *abs_name = xasprintf("%s/%s", cwd, file_name); free(cwd); return abs_name; } else { return NULL; } } } /* Like readlink(), but returns the link name as a null-terminated string in * allocated memory that the caller must eventually free (with free()). * Returns NULL on error, in which case errno is set appropriately. */ static char * xreadlink(const char *filename) { size_t size; for (size = 64; ; size *= 2) { char *buf = xmalloc(size); ssize_t retval = readlink(filename, buf, size); int error = errno; if (retval >= 0 && retval < size) { buf[retval] = '\0'; return buf; } free(buf); if (retval < 0) { errno = error; return NULL; } } } /* Returns a version of 'filename' with symlinks in the final component * dereferenced. This differs from realpath() in that: * * - 'filename' need not exist. * * - If 'filename' does exist as a symlink, its referent need not exist. * * - Only symlinks in the final component of 'filename' are dereferenced. * * For Windows platform, this function returns a string that has the same * value as the passed string. * * The caller must eventually free the returned string (with free()). */ char * follow_symlinks(const char *filename) { #ifndef _WIN32 struct stat s; char *fn; int i; fn = xstrdup(filename); for (i = 0; i < 10; i++) { char *linkname; char *next_fn; if (lstat(fn, &s) != 0 || !S_ISLNK(s.st_mode)) { return fn; } linkname = xreadlink(fn); if (!linkname) { VLOG_WARN("%s: readlink failed (%s)", filename, ovs_strerror(errno)); return fn; } if (linkname[0] == '/') { /* Target of symlink is absolute so use it raw. */ next_fn = linkname; } else { /* Target of symlink is relative so add to 'fn''s directory. */ char *dir = dir_name(fn); if (!strcmp(dir, ".")) { next_fn = linkname; } else { char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/"; next_fn = xasprintf("%s%s%s", dir, separator, linkname); free(linkname); } free(dir); } free(fn); fn = next_fn; } VLOG_WARN("%s: too many levels of symlinks", filename); free(fn); #endif return xstrdup(filename); } /* Pass a value to this function if it is marked with * __attribute__((warn_unused_result)) and you genuinely want to ignore * its return value. (Note that every scalar type can be implicitly * converted to bool.) */ void ignore(bool x OVS_UNUSED) { } /* Returns an appropriate delimiter for inserting just before the 0-based item * 'index' in a list that has 'total' items in it. */ const char * english_list_delimiter(size_t index, size_t total) { return (index == 0 ? "" : index < total - 1 ? ", " : total > 2 ? ", and " : " and "); } /* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ #if __GNUC__ >= 4 || _MSC_VER /* Defined inline in util.h. */ #else /* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */ int raw_ctz(uint64_t n) { uint64_t k; int count = 63; #define CTZ_STEP(X) \ k = n << (X); \ if (k) { \ count -= X; \ n = k; \ } CTZ_STEP(32); CTZ_STEP(16); CTZ_STEP(8); CTZ_STEP(4); CTZ_STEP(2); CTZ_STEP(1); #undef CTZ_STEP return count; } /* Returns the number of leading 0-bits in 'n'. Undefined if 'n' == 0. */ int raw_clz64(uint64_t n) { uint64_t k; int count = 63; #define CLZ_STEP(X) \ k = n >> (X); \ if (k) { \ count -= X; \ n = k; \ } CLZ_STEP(32); CLZ_STEP(16); CLZ_STEP(8); CLZ_STEP(4); CLZ_STEP(2); CLZ_STEP(1); #undef CLZ_STEP return count; } #endif #if NEED_COUNT_1BITS_8 #define INIT1(X) \ ((((X) & (1 << 0)) != 0) + \ (((X) & (1 << 1)) != 0) + \ (((X) & (1 << 2)) != 0) + \ (((X) & (1 << 3)) != 0) + \ (((X) & (1 << 4)) != 0) + \ (((X) & (1 << 5)) != 0) + \ (((X) & (1 << 6)) != 0) + \ (((X) & (1 << 7)) != 0)) #define INIT2(X) INIT1(X), INIT1((X) + 1) #define INIT4(X) INIT2(X), INIT2((X) + 2) #define INIT8(X) INIT4(X), INIT4((X) + 4) #define INIT16(X) INIT8(X), INIT8((X) + 8) #define INIT32(X) INIT16(X), INIT16((X) + 16) #define INIT64(X) INIT32(X), INIT32((X) + 32) const uint8_t count_1bits_8[256] = { INIT64(0), INIT64(64), INIT64(128), INIT64(192) }; #endif /* Returns true if the 'n' bytes starting at 'p' are zeros. */ bool is_all_zeros(const void *p_, size_t n) { const uint8_t *p = p_; size_t i; for (i = 0; i < n; i++) { if (p[i] != 0x00) { return false; } } return true; } /* Returns true if the 'n' bytes starting at 'p' are 0xff. */ bool is_all_ones(const void *p_, size_t n) { const uint8_t *p = p_; size_t i; for (i = 0; i < n; i++) { if (p[i] != 0xff) { return false; } } return true; } /* Copies 'n_bits' bits starting from bit 'src_ofs' in 'src' to the 'n_bits' * starting from bit 'dst_ofs' in 'dst'. 'src' is 'src_len' bytes long and * 'dst' is 'dst_len' bytes long. * * If you consider all of 'src' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in src[src_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in src[src_len - * 2], and so on. Similarly for 'dst'. * * Required invariants: * src_ofs + n_bits <= src_len * 8 * dst_ofs + n_bits <= dst_len * 8 * 'src' and 'dst' must not overlap. */ void bitwise_copy(const void *src_, unsigned int src_len, unsigned int src_ofs, void *dst_, unsigned int dst_len, unsigned int dst_ofs, unsigned int n_bits) { const uint8_t *src = src_; uint8_t *dst = dst_; src += src_len - (src_ofs / 8 + 1); src_ofs %= 8; dst += dst_len - (dst_ofs / 8 + 1); dst_ofs %= 8; if (src_ofs == 0 && dst_ofs == 0) { unsigned int n_bytes = n_bits / 8; if (n_bytes) { dst -= n_bytes - 1; src -= n_bytes - 1; memcpy(dst, src, n_bytes); n_bits %= 8; src--; dst--; } if (n_bits) { uint8_t mask = (1 << n_bits) - 1; *dst = (*dst & ~mask) | (*src & mask); } } else { while (n_bits > 0) { unsigned int max_copy = 8 - MAX(src_ofs, dst_ofs); unsigned int chunk = MIN(n_bits, max_copy); uint8_t mask = ((1 << chunk) - 1) << dst_ofs; *dst &= ~mask; *dst |= ((*src >> src_ofs) << dst_ofs) & mask; src_ofs += chunk; if (src_ofs == 8) { src--; src_ofs = 0; } dst_ofs += chunk; if (dst_ofs == 8) { dst--; dst_ofs = 0; } n_bits -= chunk; } } } /* Zeros the 'n_bits' bits starting from bit 'dst_ofs' in 'dst'. 'dst' is * 'dst_len' bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[dst_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in dst[dst_len - * 2], and so on. * * Required invariant: * dst_ofs + n_bits <= dst_len * 8 */ void bitwise_zero(void *dst_, unsigned int dst_len, unsigned dst_ofs, unsigned int n_bits) { uint8_t *dst = dst_; if (!n_bits) { return; } dst += dst_len - (dst_ofs / 8 + 1); dst_ofs %= 8; if (dst_ofs) { unsigned int chunk = MIN(n_bits, 8 - dst_ofs); *dst &= ~(((1 << chunk) - 1) << dst_ofs); n_bits -= chunk; if (!n_bits) { return; } dst--; } while (n_bits >= 8) { *dst-- = 0; n_bits -= 8; } if (n_bits) { *dst &= ~((1 << n_bits) - 1); } } /* Sets to 1 all of the 'n_bits' bits starting from bit 'dst_ofs' in 'dst'. * 'dst' is 'dst_len' bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[dst_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in dst[dst_len - * 2], and so on. * * Required invariant: * dst_ofs + n_bits <= dst_len * 8 */ void bitwise_one(void *dst_, unsigned int dst_len, unsigned dst_ofs, unsigned int n_bits) { uint8_t *dst = dst_; if (!n_bits) { return; } dst += dst_len - (dst_ofs / 8 + 1); dst_ofs %= 8; if (dst_ofs) { unsigned int chunk = MIN(n_bits, 8 - dst_ofs); *dst |= ((1 << chunk) - 1) << dst_ofs; n_bits -= chunk; if (!n_bits) { return; } dst--; } while (n_bits >= 8) { *dst-- = 0xff; n_bits -= 8; } if (n_bits) { *dst |= (1 << n_bits) - 1; } } /* Scans the 'n_bits' bits starting from bit 'dst_ofs' in 'dst' for 1-bits. * Returns false if any 1-bits are found, otherwise true. 'dst' is 'dst_len' * bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[dst_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in dst[dst_len - * 2], and so on. * * Required invariant: * dst_ofs + n_bits <= dst_len * 8 */ bool bitwise_is_all_zeros(const void *p_, unsigned int len, unsigned int ofs, unsigned int n_bits) { const uint8_t *p = p_; if (!n_bits) { return true; } p += len - (ofs / 8 + 1); ofs %= 8; if (ofs) { unsigned int chunk = MIN(n_bits, 8 - ofs); if (*p & (((1 << chunk) - 1) << ofs)) { return false; } n_bits -= chunk; if (!n_bits) { return true; } p--; } while (n_bits >= 8) { if (*p) { return false; } n_bits -= 8; p--; } if (n_bits && *p & ((1 << n_bits) - 1)) { return false; } return true; } /* Scans the bits in 'p' that have bit offsets 'start' (inclusive) through * 'end' (exclusive) for the first bit with value 'target'. If one is found, * returns its offset, otherwise 'end'. 'p' is 'len' bytes long. * * If you consider all of 'p' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in p[len - 1], bit 1 is the bit with value 2, bit 2 is the bit * with value 4, ..., bit 8 is the bit with value 1 in p[len - 2], and so on. * * Required invariant: * start <= end */ unsigned int bitwise_scan(const void *p, unsigned int len, bool target, unsigned int start, unsigned int end) { unsigned int ofs; for (ofs = start; ofs < end; ofs++) { if (bitwise_get_bit(p, len, ofs) == target) { break; } } return ofs; } /* Scans the bits in 'p' that have bit offsets 'start' (inclusive) through * 'end' (exclusive) for the first bit with value 'target', in reverse order. * If one is found, returns its offset, otherwise 'end'. 'p' is 'len' bytes * long. * * If you consider all of 'p' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in p[len - 1], bit 1 is the bit with value 2, bit 2 is the bit * with value 4, ..., bit 8 is the bit with value 1 in p[len - 2], and so on. * * To scan an entire bit array in reverse order, specify start == len * 8 - 1 * and end == -1, in which case the return value is nonnegative if successful * and -1 if no 'target' match is found. * * Required invariant: * start >= end */ int bitwise_rscan(const void *p, unsigned int len, bool target, int start, int end) { int ofs; for (ofs = start; ofs > end; ofs--) { if (bitwise_get_bit(p, len, ofs) == target) { break; } } return ofs; } /* Copies the 'n_bits' low-order bits of 'value' into the 'n_bits' bits * starting at bit 'dst_ofs' in 'dst', which is 'dst_len' bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[dst_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in dst[dst_len - * 2], and so on. * * Required invariants: * dst_ofs + n_bits <= dst_len * 8 * n_bits <= 64 */ void bitwise_put(uint64_t value, void *dst, unsigned int dst_len, unsigned int dst_ofs, unsigned int n_bits) { ovs_be64 n_value = htonll(value); bitwise_copy(&n_value, sizeof n_value, 0, dst, dst_len, dst_ofs, n_bits); } /* Returns the value of the 'n_bits' bits starting at bit 'src_ofs' in 'src', * which is 'src_len' bytes long. * * If you consider all of 'src' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in src[src_len - 1], bit 1 is the bit with value 2, bit 2 is * the bit with value 4, ..., bit 8 is the bit with value 1 in src[src_len - * 2], and so on. * * Required invariants: * src_ofs + n_bits <= src_len * 8 * n_bits <= 64 */ uint64_t bitwise_get(const void *src, unsigned int src_len, unsigned int src_ofs, unsigned int n_bits) { ovs_be64 value = htonll(0); bitwise_copy(src, src_len, src_ofs, &value, sizeof value, 0, n_bits); return ntohll(value); } /* Returns the value of the bit with offset 'ofs' in 'src', which is 'len' * bytes long. * * If you consider all of 'src' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in src[len - 1], bit 1 is the bit with value 2, bit 2 is the * bit with value 4, ..., bit 8 is the bit with value 1 in src[len - 2], and so * on. * * Required invariants: * ofs < len * 8 */ bool bitwise_get_bit(const void *src_, unsigned int len, unsigned int ofs) { const uint8_t *src = src_; return (src[len - (ofs / 8 + 1)] & (1u << (ofs % 8))) != 0; } /* Sets the bit with offset 'ofs' in 'dst', which is 'len' bytes long, to 0. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[len - 1], bit 1 is the bit with value 2, bit 2 is the * bit with value 4, ..., bit 8 is the bit with value 1 in dst[len - 2], and so * on. * * Required invariants: * ofs < len * 8 */ void bitwise_put0(void *dst_, unsigned int len, unsigned int ofs) { uint8_t *dst = dst_; dst[len - (ofs / 8 + 1)] &= ~(1u << (ofs % 8)); } /* Sets the bit with offset 'ofs' in 'dst', which is 'len' bytes long, to 1. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[len - 1], bit 1 is the bit with value 2, bit 2 is the * bit with value 4, ..., bit 8 is the bit with value 1 in dst[len - 2], and so * on. * * Required invariants: * ofs < len * 8 */ void bitwise_put1(void *dst_, unsigned int len, unsigned int ofs) { uint8_t *dst = dst_; dst[len - (ofs / 8 + 1)] |= 1u << (ofs % 8); } /* Sets the bit with offset 'ofs' in 'dst', which is 'len' bytes long, to 'b'. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[len - 1], bit 1 is the bit with value 2, bit 2 is the * bit with value 4, ..., bit 8 is the bit with value 1 in dst[len - 2], and so * on. * * Required invariants: * ofs < len * 8 */ void bitwise_put_bit(void *dst, unsigned int len, unsigned int ofs, bool b) { if (b) { bitwise_put1(dst, len, ofs); } else { bitwise_put0(dst, len, ofs); } } /* Flips the bit with offset 'ofs' in 'dst', which is 'len' bytes long. * * If you consider all of 'dst' to be a single unsigned integer in network byte * order, then bit N is the bit with value 2**N. That is, bit 0 is the bit * with value 1 in dst[len - 1], bit 1 is the bit with value 2, bit 2 is the * bit with value 4, ..., bit 8 is the bit with value 1 in dst[len - 2], and so * on. * * Required invariants: * ofs < len * 8 */ void bitwise_toggle_bit(void *dst_, unsigned int len, unsigned int ofs) { uint8_t *dst = dst_; dst[len - (ofs / 8 + 1)] ^= 1u << (ofs % 8); } /* ovs_scan */ struct scan_spec { unsigned int width; enum { SCAN_DISCARD, SCAN_CHAR, SCAN_SHORT, SCAN_INT, SCAN_LONG, SCAN_LLONG, SCAN_INTMAX_T, SCAN_PTRDIFF_T, SCAN_SIZE_T } type; }; static const char * skip_spaces(const char *s) { while (isspace((unsigned char) *s)) { s++; } return s; } static const char * scan_int(const char *s, const struct scan_spec *spec, int base, va_list *args) { const char *start = s; uintmax_t value; bool negative; int n_digits; negative = *s == '-'; s += *s == '-' || *s == '+'; if ((!base || base == 16) && *s == '0' && (s[1] == 'x' || s[1] == 'X')) { base = 16; s += 2; } else if (!base) { base = *s == '0' ? 8 : 10; } if (s - start >= spec->width) { return NULL; } value = 0; n_digits = 0; while (s - start < spec->width) { int digit = hexit_value(*s); if (digit < 0 || digit >= base) { break; } value = value * base + digit; n_digits++; s++; } if (!n_digits) { return NULL; } if (negative) { value = -value; } switch (spec->type) { case SCAN_DISCARD: break; case SCAN_CHAR: *va_arg(*args, char *) = value; break; case SCAN_SHORT: *va_arg(*args, short int *) = value; break; case SCAN_INT: *va_arg(*args, int *) = value; break; case SCAN_LONG: *va_arg(*args, long int *) = value; break; case SCAN_LLONG: *va_arg(*args, long long int *) = value; break; case SCAN_INTMAX_T: *va_arg(*args, intmax_t *) = value; break; case SCAN_PTRDIFF_T: *va_arg(*args, ptrdiff_t *) = value; break; case SCAN_SIZE_T: *va_arg(*args, size_t *) = value; break; } return s; } static const char * skip_digits(const char *s) { while (*s >= '0' && *s <= '9') { s++; } return s; } static const char * scan_float(const char *s, const struct scan_spec *spec, va_list *args) { const char *start = s; long double value; char *tail; char *copy; bool ok; s += *s == '+' || *s == '-'; s = skip_digits(s); if (*s == '.') { s = skip_digits(s + 1); } if (*s == 'e' || *s == 'E') { s++; s += *s == '+' || *s == '-'; s = skip_digits(s); } if (s - start > spec->width) { s = start + spec->width; } copy = xmemdup0(start, s - start); value = strtold(copy, &tail); ok = *tail == '\0'; free(copy); if (!ok) { return NULL; } switch (spec->type) { case SCAN_DISCARD: break; case SCAN_INT: *va_arg(*args, float *) = value; break; case SCAN_LONG: *va_arg(*args, double *) = value; break; case SCAN_LLONG: *va_arg(*args, long double *) = value; break; case SCAN_CHAR: case SCAN_SHORT: case SCAN_INTMAX_T: case SCAN_PTRDIFF_T: case SCAN_SIZE_T: OVS_NOT_REACHED(); } return s; } static void scan_output_string(const struct scan_spec *spec, const char *s, size_t n, va_list *args) { if (spec->type != SCAN_DISCARD) { char *out = va_arg(*args, char *); memcpy(out, s, n); out[n] = '\0'; } } static const char * scan_string(const char *s, const struct scan_spec *spec, va_list *args) { size_t n; for (n = 0; n < spec->width; n++) { if (!s[n] || isspace((unsigned char) s[n])) { break; } } if (!n) { return NULL; } scan_output_string(spec, s, n, args); return s + n; } static const char * parse_scanset(const char *p_, unsigned long *set, bool *complemented) { const uint8_t *p = (const uint8_t *) p_; *complemented = *p == '^'; p += *complemented; if (*p == ']') { bitmap_set1(set, ']'); p++; } while (*p && *p != ']') { if (p[1] == '-' && p[2] != ']' && p[2] > *p) { bitmap_set_multiple(set, *p, p[2] - *p + 1, true); p += 3; } else { bitmap_set1(set, *p++); } } if (*p == ']') { p++; } return (const char *) p; } static const char * scan_set(const char *s, const struct scan_spec *spec, const char **pp, va_list *args) { unsigned long set[BITMAP_N_LONGS(UCHAR_MAX + 1)]; bool complemented; unsigned int n; /* Parse the scan set. */ memset(set, 0, sizeof set); *pp = parse_scanset(*pp, set, &complemented); /* Parse the data. */ n = 0; while (s[n] && bitmap_is_set(set, (unsigned char) s[n]) == !complemented && n < spec->width) { n++; } if (!n) { return NULL; } scan_output_string(spec, s, n, args); return s + n; } static const char * scan_chars(const char *s, const struct scan_spec *spec, va_list *args) { unsigned int n = spec->width == UINT_MAX ? 1 : spec->width; if (strlen(s) < n) { return NULL; } if (spec->type != SCAN_DISCARD) { memcpy(va_arg(*args, char *), s, n); } return s + n; } static bool ovs_scan__(const char *s, int *n, const char *format, va_list *args) { const char *const start = s; bool ok = false; const char *p; p = format; while (*p != '\0') { struct scan_spec spec; unsigned char c = *p++; bool discard; if (isspace(c)) { s = skip_spaces(s); continue; } else if (c != '%') { if (*s != c) { goto exit; } s++; continue; } else if (*p == '%') { if (*s++ != '%') { goto exit; } p++; continue; } /* Parse '*' flag. */ discard = *p == '*'; p += discard; /* Parse field width. */ spec.width = 0; while (*p >= '0' && *p <= '9') { spec.width = spec.width * 10 + (*p++ - '0'); } if (spec.width == 0) { spec.width = UINT_MAX; } /* Parse type modifier. */ switch (*p) { case 'h': if (p[1] == 'h') { spec.type = SCAN_CHAR; p += 2; } else { spec.type = SCAN_SHORT; p++; } break; case 'j': spec.type = SCAN_INTMAX_T; p++; break; case 'l': if (p[1] == 'l') { spec.type = SCAN_LLONG; p += 2; } else { spec.type = SCAN_LONG; p++; } break; case 'L': case 'q': spec.type = SCAN_LLONG; p++; break; case 't': spec.type = SCAN_PTRDIFF_T; p++; break; case 'z': spec.type = SCAN_SIZE_T; p++; break; default: spec.type = SCAN_INT; break; } if (discard) { spec.type = SCAN_DISCARD; } c = *p++; if (c != 'c' && c != 'n' && c != '[') { s = skip_spaces(s); } switch (c) { case 'd': s = scan_int(s, &spec, 10, args); break; case 'i': s = scan_int(s, &spec, 0, args); break; case 'o': s = scan_int(s, &spec, 8, args); break; case 'u': s = scan_int(s, &spec, 10, args); break; case 'x': case 'X': s = scan_int(s, &spec, 16, args); break; case 'e': case 'f': case 'g': case 'E': case 'G': s = scan_float(s, &spec, args); break; case 's': s = scan_string(s, &spec, args); break; case '[': s = scan_set(s, &spec, &p, args); break; case 'c': s = scan_chars(s, &spec, args); break; case 'n': if (spec.type != SCAN_DISCARD) { *va_arg(*args, int *) = s - start; } break; } if (!s) { goto exit; } } if (n) { *n = s - start; } ok = true; exit: return ok; } /* This is an implementation of the standard sscanf() function, with the * following exceptions: * * - It returns true if the entire format was successfully scanned and * converted, false if any conversion failed. * * - The standard doesn't define sscanf() behavior when an out-of-range value * is scanned, e.g. if a "%"PRIi8 conversion scans "-1" or "0x1ff". Some * implementations consider this an error and stop scanning. This * implementation never considers an out-of-range value an error; instead, * it stores the least-significant bits of the converted value in the * destination, e.g. the value 255 for both examples earlier. * * - Only single-byte characters are supported, that is, the 'l' modifier * on %s, %[, and %c is not supported. The GNU extension 'a' modifier is * also not supported. * * - %p is not supported. */ bool ovs_scan(const char *s, const char *format, ...) { va_list args; bool res; va_start(args, format); res = ovs_scan__(s, NULL, format, &args); va_end(args); return res; } /* * This function is similar to ovs_scan(), with an extra parameter `n` added to * return the number of scanned characters. */ bool ovs_scan_len(const char *s, int *n, const char *format, ...) { va_list args; bool success; int n1; va_start(args, format); success = ovs_scan__(s + *n, &n1, format, &args); va_end(args); if (success) { *n = *n + n1; } return success; } void xsleep(unsigned int seconds) { ovsrcu_quiesce_start(); #ifdef _WIN32 Sleep(seconds * 1000); #else sleep(seconds); #endif ovsrcu_quiesce_end(); } #ifdef _WIN32 char * ovs_format_message(int error) { enum { BUFSIZE = sizeof strerror_buffer_get()->s }; char *buffer = strerror_buffer_get()->s; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, buffer, BUFSIZE, NULL); return buffer; } /* Returns a null-terminated string that explains the last error. * Use this function to get the error string for WINAPI calls. */ char * ovs_lasterror_to_string(void) { return ovs_format_message(GetLastError()); } int ftruncate(int fd, off_t length) { int error; error = _chsize_s(fd, length); if (error) { return -1; } return 0; } OVS_CONSTRUCTOR(winsock_start) { WSADATA wsaData; int error; error = WSAStartup(MAKEWORD(2, 2), &wsaData); if (error != 0) { VLOG_FATAL("WSAStartup failed: %s", sock_strerror(sock_errno())); } } #endif openvswitch-2.5.9/lib/PaxHeaders.82075/json.c0000644000000000000000000000013213534540071015527 xustar0030 mtime=1567801401.397681141 30 atime=1567801402.077686135 30 ctime=1567801424.745853161 openvswitch-2.5.9/lib/json.c0000644000175000017500000011455613534540071017231 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "json.h" #include #include #include #include #include #include "dynamic-string.h" #include "hash.h" #include "shash.h" #include "unicode.h" #include "util.h" /* The type of a JSON token. */ enum json_token_type { T_EOF = 0, T_BEGIN_ARRAY = '[', T_END_ARRAY = ']', T_BEGIN_OBJECT = '{', T_END_OBJECT = '}', T_NAME_SEPARATOR = ':', T_VALUE_SEPARATOR = ',', T_FALSE = UCHAR_MAX + 1, T_NULL, T_TRUE, T_INTEGER, T_REAL, T_STRING }; /* A JSON token. * * RFC 4627 doesn't define a lexical structure for JSON but I believe this to * be compliant with the standard. */ struct json_token { enum json_token_type type; union { double real; long long int integer; const char *string; } u; }; enum json_lex_state { JSON_LEX_START, /* Not inside a token. */ JSON_LEX_NUMBER, /* Reading a number. */ JSON_LEX_KEYWORD, /* Reading a keyword. */ JSON_LEX_STRING, /* Reading a quoted string. */ JSON_LEX_ESCAPE /* In a quoted string just after a "\". */ }; enum json_parse_state { JSON_PARSE_START, /* Beginning of input. */ JSON_PARSE_END, /* End of input. */ /* Objects. */ JSON_PARSE_OBJECT_INIT, /* Expecting '}' or an object name. */ JSON_PARSE_OBJECT_NAME, /* Expecting an object name. */ JSON_PARSE_OBJECT_COLON, /* Expecting ':'. */ JSON_PARSE_OBJECT_VALUE, /* Expecting an object value. */ JSON_PARSE_OBJECT_NEXT, /* Expecting ',' or '}'. */ /* Arrays. */ JSON_PARSE_ARRAY_INIT, /* Expecting ']' or a value. */ JSON_PARSE_ARRAY_VALUE, /* Expecting a value. */ JSON_PARSE_ARRAY_NEXT /* Expecting ',' or ']'. */ }; struct json_parser_node { struct json *json; }; /* A JSON parser. */ struct json_parser { int flags; /* Lexical analysis. */ enum json_lex_state lex_state; struct ds buffer; /* Buffer for accumulating token text. */ int line_number; int column_number; int byte_number; /* Parsing. */ enum json_parse_state parse_state; #define JSON_MAX_HEIGHT 1000 struct json_parser_node *stack; size_t height, allocated_height; char *member_name; /* Parse status. */ bool done; char *error; /* Error message, if any, null if none yet. */ }; static struct json *json_create(enum json_type type); static void json_parser_input(struct json_parser *, struct json_token *); static void json_error(struct json_parser *p, const char *format, ...) OVS_PRINTF_FORMAT(2, 3); const char * json_type_to_string(enum json_type type) { switch (type) { case JSON_NULL: return "null"; case JSON_FALSE: return "false"; case JSON_TRUE: return "true"; case JSON_OBJECT: return "object"; case JSON_ARRAY: return "array"; case JSON_INTEGER: case JSON_REAL: return "number"; case JSON_STRING: return "string"; case JSON_N_TYPES: default: return ""; } } /* Functions for manipulating struct json. */ struct json * json_null_create(void) { return json_create(JSON_NULL); } struct json * json_boolean_create(bool b) { return json_create(b ? JSON_TRUE : JSON_FALSE); } struct json * json_string_create_nocopy(char *s) { struct json *json = json_create(JSON_STRING); json->u.string = s; return json; } struct json * json_string_create(const char *s) { return json_string_create_nocopy(xstrdup(s)); } struct json * json_array_create_empty(void) { struct json *json = json_create(JSON_ARRAY); json->u.array.elems = NULL; json->u.array.n = 0; json->u.array.n_allocated = 0; return json; } void json_array_add(struct json *array_, struct json *element) { struct json_array *array = json_array(array_); if (array->n >= array->n_allocated) { array->elems = x2nrealloc(array->elems, &array->n_allocated, sizeof *array->elems); } array->elems[array->n++] = element; } void json_array_trim(struct json *array_) { struct json_array *array = json_array(array_); if (array->n < array->n_allocated){ array->n_allocated = array->n; array->elems = xrealloc(array->elems, array->n * sizeof *array->elems); } } struct json * json_array_create(struct json **elements, size_t n) { struct json *json = json_create(JSON_ARRAY); json->u.array.elems = elements; json->u.array.n = n; json->u.array.n_allocated = n; return json; } struct json * json_array_create_1(struct json *elem0) { struct json **elems = xmalloc(sizeof *elems); elems[0] = elem0; return json_array_create(elems, 1); } struct json * json_array_create_2(struct json *elem0, struct json *elem1) { struct json **elems = xmalloc(2 * sizeof *elems); elems[0] = elem0; elems[1] = elem1; return json_array_create(elems, 2); } struct json * json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2) { struct json **elems = xmalloc(3 * sizeof *elems); elems[0] = elem0; elems[1] = elem1; elems[2] = elem2; return json_array_create(elems, 3); } struct json * json_object_create(void) { struct json *json = json_create(JSON_OBJECT); json->u.object = xmalloc(sizeof *json->u.object); shash_init(json->u.object); return json; } struct json * json_integer_create(long long int integer) { struct json *json = json_create(JSON_INTEGER); json->u.integer = integer; return json; } struct json * json_real_create(double real) { struct json *json = json_create(JSON_REAL); json->u.real = real; return json; } void json_object_put(struct json *json, const char *name, struct json *value) { json_destroy(shash_replace(json->u.object, name, value)); } void json_object_put_string(struct json *json, const char *name, const char *value) { json_object_put(json, name, json_string_create(value)); } const char * json_string(const struct json *json) { ovs_assert(json->type == JSON_STRING); return json->u.string; } struct json_array * json_array(const struct json *json) { ovs_assert(json->type == JSON_ARRAY); return CONST_CAST(struct json_array *, &json->u.array); } struct shash * json_object(const struct json *json) { ovs_assert(json->type == JSON_OBJECT); return CONST_CAST(struct shash *, json->u.object); } bool json_boolean(const struct json *json) { ovs_assert(json->type == JSON_TRUE || json->type == JSON_FALSE); return json->type == JSON_TRUE; } double json_real(const struct json *json) { ovs_assert(json->type == JSON_REAL || json->type == JSON_INTEGER); return json->type == JSON_REAL ? json->u.real : json->u.integer; } int64_t json_integer(const struct json *json) { ovs_assert(json->type == JSON_INTEGER); return json->u.integer; } static void json_destroy_object(struct shash *object); static void json_destroy_array(struct json_array *array); /* Frees 'json' and everything it points to, recursively. */ void json_destroy(struct json *json) { if (json) { switch (json->type) { case JSON_OBJECT: json_destroy_object(json->u.object); break; case JSON_ARRAY: json_destroy_array(&json->u.array); break; case JSON_STRING: free(json->u.string); break; case JSON_NULL: case JSON_FALSE: case JSON_TRUE: case JSON_INTEGER: case JSON_REAL: break; case JSON_N_TYPES: OVS_NOT_REACHED(); } free(json); } } static void json_destroy_object(struct shash *object) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, object) { struct json *value = node->data; json_destroy(value); shash_delete(object, node); } shash_destroy(object); free(object); } static void json_destroy_array(struct json_array *array) { size_t i; for (i = 0; i < array->n; i++) { json_destroy(array->elems[i]); } free(array->elems); } static struct json *json_clone_object(const struct shash *object); static struct json *json_clone_array(const struct json_array *array); /* Returns a deep copy of 'json'. */ struct json * json_clone(const struct json *json) { switch (json->type) { case JSON_OBJECT: return json_clone_object(json->u.object); case JSON_ARRAY: return json_clone_array(&json->u.array); case JSON_STRING: return json_string_create(json->u.string); case JSON_NULL: case JSON_FALSE: case JSON_TRUE: return json_create(json->type); case JSON_INTEGER: return json_integer_create(json->u.integer); case JSON_REAL: return json_real_create(json->u.real); case JSON_N_TYPES: default: OVS_NOT_REACHED(); } } static struct json * json_clone_object(const struct shash *object) { struct shash_node *node; struct json *json; json = json_object_create(); SHASH_FOR_EACH (node, object) { struct json *value = node->data; json_object_put(json, node->name, json_clone(value)); } return json; } static struct json * json_clone_array(const struct json_array *array) { struct json **elems; size_t i; elems = xmalloc(array->n * sizeof *elems); for (i = 0; i < array->n; i++) { elems[i] = json_clone(array->elems[i]); } return json_array_create(elems, array->n); } static size_t json_hash_object(const struct shash *object, size_t basis) { const struct shash_node **nodes; size_t n, i; nodes = shash_sort(object); n = shash_count(object); for (i = 0; i < n; i++) { const struct shash_node *node = nodes[i]; basis = hash_string(node->name, basis); basis = json_hash(node->data, basis); } free(nodes); return basis; } static size_t json_hash_array(const struct json_array *array, size_t basis) { size_t i; basis = hash_int(array->n, basis); for (i = 0; i < array->n; i++) { basis = json_hash(array->elems[i], basis); } return basis; } size_t json_hash(const struct json *json, size_t basis) { switch (json->type) { case JSON_OBJECT: return json_hash_object(json->u.object, basis); case JSON_ARRAY: return json_hash_array(&json->u.array, basis); case JSON_STRING: return hash_string(json->u.string, basis); case JSON_NULL: case JSON_FALSE: case JSON_TRUE: return hash_int(json->type << 8, basis); case JSON_INTEGER: return hash_int(json->u.integer, basis); case JSON_REAL: return hash_double(json->u.real, basis); case JSON_N_TYPES: default: OVS_NOT_REACHED(); } } static bool json_equal_object(const struct shash *a, const struct shash *b) { struct shash_node *a_node; if (shash_count(a) != shash_count(b)) { return false; } SHASH_FOR_EACH (a_node, a) { struct shash_node *b_node = shash_find(b, a_node->name); if (!b_node || !json_equal(a_node->data, b_node->data)) { return false; } } return true; } static bool json_equal_array(const struct json_array *a, const struct json_array *b) { size_t i; if (a->n != b->n) { return false; } for (i = 0; i < a->n; i++) { if (!json_equal(a->elems[i], b->elems[i])) { return false; } } return true; } bool json_equal(const struct json *a, const struct json *b) { if (a->type != b->type) { return false; } switch (a->type) { case JSON_OBJECT: return json_equal_object(a->u.object, b->u.object); case JSON_ARRAY: return json_equal_array(&a->u.array, &b->u.array); case JSON_STRING: return !strcmp(a->u.string, b->u.string); case JSON_NULL: case JSON_FALSE: case JSON_TRUE: return true; case JSON_INTEGER: return a->u.integer == b->u.integer; case JSON_REAL: return a->u.real == b->u.real; case JSON_N_TYPES: default: OVS_NOT_REACHED(); } } /* Lexical analysis. */ static void json_lex_keyword(struct json_parser *p) { struct json_token token; const char *s; s = ds_cstr(&p->buffer); if (!strcmp(s, "false")) { token.type = T_FALSE; } else if (!strcmp(s, "true")) { token.type = T_TRUE; } else if (!strcmp(s, "null")) { token.type = T_NULL; } else { json_error(p, "invalid keyword '%s'", s); return; } json_parser_input(p, &token); } static void json_lex_number(struct json_parser *p) { const char *cp = ds_cstr(&p->buffer); unsigned long long int significand = 0; struct json_token token; bool imprecise = false; bool negative = false; int pow10 = 0; /* Leading minus sign. */ if (*cp == '-') { negative = true; cp++; } /* At least one integer digit, but 0 may not be used as a leading digit for * a longer number. */ significand = 0; if (*cp == '0') { cp++; if (isdigit((unsigned char) *cp)) { json_error(p, "leading zeros not allowed"); return; } } else if (isdigit((unsigned char) *cp)) { do { if (significand <= ULLONG_MAX / 10) { significand = significand * 10 + (*cp - '0'); } else { pow10++; if (*cp != '0') { imprecise = true; } } cp++; } while (isdigit((unsigned char) *cp)); } else { json_error(p, "'-' must be followed by digit"); return; } /* Optional fraction. */ if (*cp == '.') { cp++; if (!isdigit((unsigned char) *cp)) { json_error(p, "decimal point must be followed by digit"); return; } do { if (significand <= ULLONG_MAX / 10) { significand = significand * 10 + (*cp - '0'); pow10--; } else if (*cp != '0') { imprecise = true; } cp++; } while (isdigit((unsigned char) *cp)); } /* Optional exponent. */ if (*cp == 'e' || *cp == 'E') { bool negative_exponent = false; int exponent; cp++; if (*cp == '+') { cp++; } else if (*cp == '-') { negative_exponent = true; cp++; } if (!isdigit((unsigned char) *cp)) { json_error(p, "exponent must contain at least one digit"); return; } exponent = 0; do { if (exponent >= INT_MAX / 10) { json_error(p, "exponent outside valid range"); return; } exponent = exponent * 10 + (*cp - '0'); cp++; } while (isdigit((unsigned char) *cp)); if (negative_exponent) { pow10 -= exponent; } else { pow10 += exponent; } } if (*cp != '\0') { json_error(p, "syntax error in number"); return; } /* Figure out number. * * We suppress negative zeros as a matter of policy. */ if (!significand) { token.type = T_INTEGER; token.u.integer = 0; json_parser_input(p, &token); return; } if (!imprecise) { while (pow10 > 0 && significand < ULLONG_MAX / 10) { significand *= 10; pow10--; } while (pow10 < 0 && significand % 10 == 0) { significand /= 10; pow10++; } if (pow10 == 0 && significand <= (negative ? (unsigned long long int) LLONG_MAX + 1 : LLONG_MAX)) { token.type = T_INTEGER; token.u.integer = negative ? -significand : significand; json_parser_input(p, &token); return; } } token.type = T_REAL; if (!str_to_double(ds_cstr(&p->buffer), &token.u.real)) { json_error(p, "number outside valid range"); return; } /* Suppress negative zero. */ if (token.u.real == 0) { token.u.real = 0; } json_parser_input(p, &token); } static const char * json_lex_4hex(const char *cp, const char *end, int *valuep) { unsigned int value; bool ok; if (cp + 4 > end) { return "quoted string ends within \\u escape"; } value = hexits_value(cp, 4, &ok); if (!ok) { return "malformed \\u escape"; } if (!value) { return "null bytes not supported in quoted strings"; } *valuep = value; return NULL; } static const char * json_lex_unicode(const char *cp, const char *end, struct ds *out) { const char *error; int c0, c1; error = json_lex_4hex(cp, end, &c0); if (error) { ds_clear(out); ds_put_cstr(out, error); return NULL; } cp += 4; if (!uc_is_leading_surrogate(c0)) { ds_put_utf8(out, c0); return cp; } if (cp + 2 > end || *cp++ != '\\' || *cp++ != 'u') { ds_clear(out); ds_put_cstr(out, "malformed escaped surrogate pair"); return NULL; } error = json_lex_4hex(cp, end, &c1); if (error) { ds_clear(out); ds_put_cstr(out, error); return NULL; } cp += 4; if (!uc_is_trailing_surrogate(c1)) { ds_clear(out); ds_put_cstr(out, "second half of escaped surrogate pair is not " "trailing surrogate"); return NULL; } ds_put_utf8(out, utf16_decode_surrogate_pair(c0, c1)); return cp; } bool json_string_unescape(const char *in, size_t in_len, char **outp) { const char *end = in + in_len; bool ok = false; struct ds out; ds_init(&out); ds_reserve(&out, in_len); while (in < end) { if (*in == '"') { ds_clear(&out); ds_put_cstr(&out, "quoted string may not include unescaped \""); goto exit; } if (*in != '\\') { ds_put_char(&out, *in++); continue; } in++; if (in >= end) { /* The JSON parser will never trigger this message, because its * lexer will never pass in a string that ends in a single * backslash, but json_string_unescape() has other callers that * are not as careful.*/ ds_clear(&out); ds_put_cstr(&out, "quoted string may not end with backslash"); goto exit; } switch (*in++) { case '"': case '\\': case '/': ds_put_char(&out, in[-1]); break; case 'b': ds_put_char(&out, '\b'); break; case 'f': ds_put_char(&out, '\f'); break; case 'n': ds_put_char(&out, '\n'); break; case 'r': ds_put_char(&out, '\r'); break; case 't': ds_put_char(&out, '\t'); break; case 'u': in = json_lex_unicode(in, end, &out); if (!in) { goto exit; } break; default: ds_clear(&out); ds_put_format(&out, "bad escape \\%c", in[-1]); goto exit; } } ok = true; exit: *outp = ds_cstr(&out); return ok; } void json_string_escape(const char *in, struct ds *out) { struct json json = { .type = JSON_STRING, .u.string = CONST_CAST(char *, in), }; json_to_ds(&json, 0, out); } static void json_parser_input_string(struct json_parser *p, const char *s) { struct json_token token; token.type = T_STRING; token.u.string = s; json_parser_input(p, &token); } static void json_lex_string(struct json_parser *p) { const char *raw = ds_cstr(&p->buffer); if (!strchr(raw, '\\')) { json_parser_input_string(p, raw); } else { char *cooked; if (json_string_unescape(raw, strlen(raw), &cooked)) { json_parser_input_string(p, cooked); } else { json_error(p, "%s", cooked); } free(cooked); } } static bool json_lex_input(struct json_parser *p, unsigned char c) { struct json_token token; switch (p->lex_state) { case JSON_LEX_START: switch (c) { case ' ': case '\t': case '\n': case '\r': /* Nothing to do. */ return true; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': p->lex_state = JSON_LEX_KEYWORD; break; case '[': case '{': case ']': case '}': case ':': case ',': token.type = c; json_parser_input(p, &token); return true; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p->lex_state = JSON_LEX_NUMBER; break; case '"': p->lex_state = JSON_LEX_STRING; return true; default: if (isprint(c)) { json_error(p, "invalid character '%c'", c); } else { json_error(p, "invalid character U+%04x", c); } return true; } break; case JSON_LEX_KEYWORD: if (!isalpha((unsigned char) c)) { json_lex_keyword(p); return false; } break; case JSON_LEX_NUMBER: if (!strchr(".0123456789eE-+", c)) { json_lex_number(p); return false; } break; case JSON_LEX_STRING: if (c == '\\') { p->lex_state = JSON_LEX_ESCAPE; } else if (c == '"') { json_lex_string(p); return true; } else if (c < 0x20) { json_error(p, "U+%04X must be escaped in quoted string", c); return true; } break; case JSON_LEX_ESCAPE: p->lex_state = JSON_LEX_STRING; break; default: abort(); } ds_put_char(&p->buffer, c); return true; } /* Parsing. */ /* Parses 'string' as a JSON object or array and returns a newly allocated * 'struct json'. The caller must free the returned structure with * json_destroy() when it is no longer needed. * * 'string' must be encoded in UTF-8. * * If 'string' is valid JSON, then the returned 'struct json' will be either an * object (JSON_OBJECT) or an array (JSON_ARRAY). * * If 'string' is not valid JSON, then the returned 'struct json' will be a * string (JSON_STRING) that describes the particular error encountered during * parsing. (This is an acceptable means of error reporting because at its top * level JSON must be either an object or an array; a bare string is not * valid.) */ struct json * json_from_string(const char *string) { struct json_parser *p = json_parser_create(JSPF_TRAILER); json_parser_feed(p, string, strlen(string)); return json_parser_finish(p); } /* Reads the file named 'file_name', parses its contents as a JSON object or * array, and returns a newly allocated 'struct json'. The caller must free * the returned structure with json_destroy() when it is no longer needed. * * The file must be encoded in UTF-8. * * See json_from_string() for return value semantics. */ struct json * json_from_file(const char *file_name) { struct json *json; FILE *stream; stream = fopen(file_name, "r"); if (!stream) { return json_string_create_nocopy( xasprintf("error opening \"%s\": %s", file_name, ovs_strerror(errno))); } json = json_from_stream(stream); fclose(stream); return json; } /* Parses the contents of 'stream' as a JSON object or array, and returns a * newly allocated 'struct json'. The caller must free the returned structure * with json_destroy() when it is no longer needed. * * The file must be encoded in UTF-8. * * See json_from_string() for return value semantics. */ struct json * json_from_stream(FILE *stream) { struct json_parser *p; struct json *json; p = json_parser_create(JSPF_TRAILER); for (;;) { char buffer[BUFSIZ]; size_t n; n = fread(buffer, 1, sizeof buffer, stream); if (!n || json_parser_feed(p, buffer, n) != n) { break; } } json = json_parser_finish(p); if (ferror(stream)) { json_destroy(json); json = json_string_create_nocopy( xasprintf("error reading JSON stream: %s", ovs_strerror(errno))); } return json; } struct json_parser * json_parser_create(int flags) { struct json_parser *p = xzalloc(sizeof *p); p->flags = flags; return p; } size_t json_parser_feed(struct json_parser *p, const char *input, size_t n) { size_t i; for (i = 0; !p->done && i < n; ) { if (json_lex_input(p, input[i])) { p->byte_number++; if (input[i] == '\n') { p->column_number = 0; p->line_number++; } else { p->column_number++; } i++; } } return i; } bool json_parser_is_done(const struct json_parser *p) { return p->done; } struct json * json_parser_finish(struct json_parser *p) { struct json *json; switch (p->lex_state) { case JSON_LEX_START: break; case JSON_LEX_STRING: case JSON_LEX_ESCAPE: json_error(p, "unexpected end of input in quoted string"); break; case JSON_LEX_NUMBER: case JSON_LEX_KEYWORD: json_lex_input(p, ' '); break; } if (p->parse_state == JSON_PARSE_START) { json_error(p, "empty input stream"); } else if (p->parse_state != JSON_PARSE_END) { json_error(p, "unexpected end of input"); } if (!p->error) { ovs_assert(p->height == 1); ovs_assert(p->stack[0].json != NULL); json = p->stack[--p->height].json; } else { json = json_string_create_nocopy(p->error); p->error = NULL; } json_parser_abort(p); return json; } void json_parser_abort(struct json_parser *p) { if (p) { ds_destroy(&p->buffer); if (p->height) { json_destroy(p->stack[0].json); } free(p->stack); free(p->member_name); free(p->error); free(p); } } static struct json_parser_node * json_parser_top(struct json_parser *p) { return &p->stack[p->height - 1]; } static void json_parser_put_value(struct json_parser *p, struct json *value) { struct json_parser_node *node = json_parser_top(p); if (node->json->type == JSON_OBJECT) { json_object_put(node->json, p->member_name, value); free(p->member_name); p->member_name = NULL; } else if (node->json->type == JSON_ARRAY) { json_array_add(node->json, value); } else { OVS_NOT_REACHED(); } } static void json_parser_push(struct json_parser *p, struct json *new_json, enum json_parse_state new_state) { if (p->height < JSON_MAX_HEIGHT) { struct json_parser_node *node; if (p->height >= p->allocated_height) { p->stack = x2nrealloc(p->stack, &p->allocated_height, sizeof *p->stack); } if (p->height > 0) { json_parser_put_value(p, new_json); } node = &p->stack[p->height++]; node->json = new_json; p->parse_state = new_state; } else { json_destroy(new_json); json_error(p, "input exceeds maximum nesting depth %d", JSON_MAX_HEIGHT); } } static void json_parser_push_object(struct json_parser *p) { json_parser_push(p, json_object_create(), JSON_PARSE_OBJECT_INIT); } static void json_parser_push_array(struct json_parser *p) { json_parser_push(p, json_array_create_empty(), JSON_PARSE_ARRAY_INIT); } static void json_parse_value(struct json_parser *p, struct json_token *token, enum json_parse_state next_state) { struct json *value; switch (token->type) { case T_FALSE: value = json_boolean_create(false); break; case T_NULL: value = json_null_create(); break; case T_TRUE: value = json_boolean_create(true); break; case '{': json_parser_push_object(p); return; case '[': json_parser_push_array(p); return; case T_INTEGER: value = json_integer_create(token->u.integer); break; case T_REAL: value = json_real_create(token->u.real); break; case T_STRING: value = json_string_create(token->u.string); break; case T_EOF: case '}': case ']': case ':': case ',': default: json_error(p, "syntax error expecting value"); return; } json_parser_put_value(p, value); p->parse_state = next_state; } static void json_parser_pop(struct json_parser *p) { struct json_parser_node *node; /* Conserve memory. */ node = json_parser_top(p); if (node->json->type == JSON_ARRAY) { json_array_trim(node->json); } /* Pop off the top-of-stack. */ if (p->height == 1) { p->parse_state = JSON_PARSE_END; if (!(p->flags & JSPF_TRAILER)) { p->done = true; } } else { p->height--; node = json_parser_top(p); if (node->json->type == JSON_ARRAY) { p->parse_state = JSON_PARSE_ARRAY_NEXT; } else if (node->json->type == JSON_OBJECT) { p->parse_state = JSON_PARSE_OBJECT_NEXT; } else { OVS_NOT_REACHED(); } } } static void json_parser_input(struct json_parser *p, struct json_token *token) { switch (p->parse_state) { case JSON_PARSE_START: if (token->type == '{') { json_parser_push_object(p); } else if (token->type == '[') { json_parser_push_array(p); } else { json_error(p, "syntax error at beginning of input"); } break; case JSON_PARSE_END: json_error(p, "trailing garbage at end of input"); break; case JSON_PARSE_OBJECT_INIT: if (token->type == '}') { json_parser_pop(p); break; } /* Fall through. */ case JSON_PARSE_OBJECT_NAME: if (token->type == T_STRING) { p->member_name = xstrdup(token->u.string); p->parse_state = JSON_PARSE_OBJECT_COLON; } else { json_error(p, "syntax error parsing object expecting string"); } break; case JSON_PARSE_OBJECT_COLON: if (token->type == ':') { p->parse_state = JSON_PARSE_OBJECT_VALUE; } else { json_error(p, "syntax error parsing object expecting ':'"); } break; case JSON_PARSE_OBJECT_VALUE: json_parse_value(p, token, JSON_PARSE_OBJECT_NEXT); break; case JSON_PARSE_OBJECT_NEXT: if (token->type == ',') { p->parse_state = JSON_PARSE_OBJECT_NAME; } else if (token->type == '}') { json_parser_pop(p); } else { json_error(p, "syntax error expecting '}' or ','"); } break; case JSON_PARSE_ARRAY_INIT: if (token->type == ']') { json_parser_pop(p); break; } /* Fall through. */ case JSON_PARSE_ARRAY_VALUE: json_parse_value(p, token, JSON_PARSE_ARRAY_NEXT); break; case JSON_PARSE_ARRAY_NEXT: if (token->type == ',') { p->parse_state = JSON_PARSE_ARRAY_VALUE; } else if (token->type == ']') { json_parser_pop(p); } else { json_error(p, "syntax error expecting ']' or ','"); } break; default: abort(); } p->lex_state = JSON_LEX_START; ds_clear(&p->buffer); } static struct json * json_create(enum json_type type) { struct json *json = xmalloc(sizeof *json); json->type = type; return json; } static void json_error(struct json_parser *p, const char *format, ...) { if (!p->error) { struct ds msg; va_list args; ds_init(&msg); ds_put_format(&msg, "line %d, column %d, byte %d: ", p->line_number, p->column_number, p->byte_number); va_start(args, format); ds_put_format_valist(&msg, format, args); va_end(args); p->error = ds_steal_cstr(&msg); p->done = true; } } #define SPACES_PER_LEVEL 2 struct json_serializer { struct ds *ds; int depth; int flags; }; static void json_serialize(const struct json *, struct json_serializer *); static void json_serialize_object(const struct shash *object, struct json_serializer *); static void json_serialize_array(const struct json_array *, struct json_serializer *); static void json_serialize_string(const char *, struct ds *); /* Converts 'json' to a string in JSON format, encoded in UTF-8, and returns * that string. The caller is responsible for freeing the returned string, * with free(), when it is no longer needed. * * If 'flags' contains JSSF_PRETTY, the output is pretty-printed with each * nesting level introducing an additional indentation. Otherwise, the * returned string does not contain any new-line characters. * * If 'flags' contains JSSF_SORT, members of objects in the output are sorted * in bytewise lexicographic order for reproducibility. Otherwise, members of * objects are output in an indeterminate order. * * The returned string is valid JSON only if 'json' represents an array or an * object, since a bare literal does not satisfy the JSON grammar. */ char * json_to_string(const struct json *json, int flags) { struct ds ds; ds_init(&ds); json_to_ds(json, flags, &ds); return ds_steal_cstr(&ds); } /* Same as json_to_string(), but the output is appended to 'ds'. */ void json_to_ds(const struct json *json, int flags, struct ds *ds) { struct json_serializer s; s.ds = ds; s.depth = 0; s.flags = flags; json_serialize(json, &s); } static void json_serialize(const struct json *json, struct json_serializer *s) { struct ds *ds = s->ds; switch (json->type) { case JSON_NULL: ds_put_cstr(ds, "null"); break; case JSON_FALSE: ds_put_cstr(ds, "false"); break; case JSON_TRUE: ds_put_cstr(ds, "true"); break; case JSON_OBJECT: json_serialize_object(json->u.object, s); break; case JSON_ARRAY: json_serialize_array(&json->u.array, s); break; case JSON_INTEGER: ds_put_format(ds, "%lld", json->u.integer); break; case JSON_REAL: ds_put_format(ds, "%.*g", DBL_DIG, json->u.real); break; case JSON_STRING: json_serialize_string(json->u.string, ds); break; case JSON_N_TYPES: default: OVS_NOT_REACHED(); } } static void indent_line(struct json_serializer *s) { if (s->flags & JSSF_PRETTY) { ds_put_char(s->ds, '\n'); ds_put_char_multiple(s->ds, ' ', SPACES_PER_LEVEL * s->depth); } } static void json_serialize_object_member(size_t i, const struct shash_node *node, struct json_serializer *s) { struct ds *ds = s->ds; if (i) { ds_put_char(ds, ','); indent_line(s); } json_serialize_string(node->name, ds); ds_put_char(ds, ':'); if (s->flags & JSSF_PRETTY) { ds_put_char(ds, ' '); } json_serialize(node->data, s); } static void json_serialize_object(const struct shash *object, struct json_serializer *s) { struct ds *ds = s->ds; ds_put_char(ds, '{'); s->depth++; indent_line(s); if (s->flags & JSSF_SORT) { const struct shash_node **nodes; size_t n, i; nodes = shash_sort(object); n = shash_count(object); for (i = 0; i < n; i++) { json_serialize_object_member(i, nodes[i], s); } free(nodes); } else { struct shash_node *node; size_t i; i = 0; SHASH_FOR_EACH (node, object) { json_serialize_object_member(i++, node, s); } } ds_put_char(ds, '}'); s->depth--; } static void json_serialize_array(const struct json_array *array, struct json_serializer *s) { struct ds *ds = s->ds; size_t i; ds_put_char(ds, '['); s->depth++; if (array->n > 0) { indent_line(s); for (i = 0; i < array->n; i++) { if (i) { ds_put_char(ds, ','); indent_line(s); } json_serialize(array->elems[i], s); } } s->depth--; ds_put_char(ds, ']'); } static void json_serialize_string(const char *string, struct ds *ds) { uint8_t c; ds_put_char(ds, '"'); while ((c = *string++) != '\0') { switch (c) { case '"': ds_put_cstr(ds, "\\\""); break; case '\\': ds_put_cstr(ds, "\\\\"); break; case '\b': ds_put_cstr(ds, "\\b"); break; case '\f': ds_put_cstr(ds, "\\f"); break; case '\n': ds_put_cstr(ds, "\\n"); break; case '\r': ds_put_cstr(ds, "\\r"); break; case '\t': ds_put_cstr(ds, "\\t"); break; default: if (c >= 32) { ds_put_char(ds, c); } else { ds_put_format(ds, "\\u%04x", c); } break; } } ds_put_char(ds, '"'); } openvswitch-2.5.9/PaxHeaders.82075/boot.sh0000644000000000000000000000013213534540071015143 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.713845553 openvswitch-2.5.9/boot.sh0000755000175000017500000000005013534540071016627 0ustar00jpettitjpettit00000000000000#! /bin/sh autoreconf --install --force openvswitch-2.5.9/PaxHeaders.82075/m40000644000000000000000000000013213534540120014102 xustar0030 mtime=1567801424.605852128 30 atime=1567801425.625859648 30 ctime=1567801424.605852128 openvswitch-2.5.9/m4/0000755000175000017500000000000013534540120015645 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/m4/PaxHeaders.82075/absolute-header.m40000644000000000000000000000013213534540071017472 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.633844963 openvswitch-2.5.9/m4/absolute-header.m40000644000175000017500000001034713534540071021165 0ustar00jpettitjpettit00000000000000# absolute-header.m4 serial 16 dnl Copyright (C) 2006-2013 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Derek Price. # gl_ABSOLUTE_HEADER(HEADER1 HEADER2 ...) # --------------------------------------- # Find the absolute name of a header file, testing first if the header exists. # If the header were sys/inttypes.h, this macro would define # ABSOLUTE_SYS_INTTYPES_H to the '""' quoted absolute name of sys/inttypes.h # in config.h # (e.g. '#define ABSOLUTE_SYS_INTTYPES_H "///usr/include/sys/inttypes.h"'). # The three "///" are to pacify Sun C 5.8, which otherwise would say # "warning: #include of /usr/include/... may be non-portable". # Use '""', not '<>', so that the /// cannot be confused with a C99 comment. # Note: This macro assumes that the header file is not empty after # preprocessing, i.e. it does not only define preprocessor macros but also # provides some type/enum definitions or function/variable declarations. AC_DEFUN([gl_ABSOLUTE_HEADER], [AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PREPROC_REQUIRE()dnl dnl FIXME: gl_absolute_header and ac_header_exists must be used unquoted dnl until we can assume autoconf 2.64 or newer. m4_foreach_w([gl_HEADER_NAME], [$1], [AS_VAR_PUSHDEF([gl_absolute_header], [gl_cv_absolute_]m4_defn([gl_HEADER_NAME]))dnl AC_CACHE_CHECK([absolute name of <]m4_defn([gl_HEADER_NAME])[>], m4_defn([gl_absolute_header]), [AS_VAR_PUSHDEF([ac_header_exists], [ac_cv_header_]m4_defn([gl_HEADER_NAME]))dnl AC_CHECK_HEADERS_ONCE(m4_defn([gl_HEADER_NAME]))dnl if test AS_VAR_GET(ac_header_exists) = yes; then gl_ABSOLUTE_HEADER_ONE(m4_defn([gl_HEADER_NAME])) fi AS_VAR_POPDEF([ac_header_exists])dnl ])dnl AC_DEFINE_UNQUOTED(AS_TR_CPP([ABSOLUTE_]m4_defn([gl_HEADER_NAME])), ["AS_VAR_GET(gl_absolute_header)"], [Define this to an absolute name of <]m4_defn([gl_HEADER_NAME])[>.]) AS_VAR_POPDEF([gl_absolute_header])dnl ])dnl ])# gl_ABSOLUTE_HEADER # gl_ABSOLUTE_HEADER_ONE(HEADER) # ------------------------------ # Like gl_ABSOLUTE_HEADER, except that: # - it assumes that the header exists, # - it uses the current CPPFLAGS, # - it does not cache the result, # - it is silent. AC_DEFUN([gl_ABSOLUTE_HEADER_ONE], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_CONFTEST([AC_LANG_SOURCE([[#include <]]m4_dquote([$1])[[>]])]) dnl AIX "xlc -E" and "cc -E" omit #line directives for header files dnl that contain only a #include of other header files and no dnl non-comment tokens of their own. This leads to a failure to dnl detect the absolute name of , , dnl and others. The workaround is to force preservation of comments dnl through option -C. This ensures all necessary #line directives dnl are present. GCC supports option -C as well. case "$host_os" in aix*) gl_absname_cpp="$ac_cpp -C" ;; *) gl_absname_cpp="$ac_cpp" ;; esac changequote(,) case "$host_os" in mingw*) dnl For the sake of native Windows compilers (excluding gcc), dnl treat backslash as a directory separator, like /. dnl Actually, these compilers use a double-backslash as dnl directory separator, inside the dnl # line "filename" dnl directives. gl_dirsep_regex='[/\\]' ;; *) gl_dirsep_regex='\/' ;; esac dnl A sed expression that turns a string into a basic regular dnl expression, for use within "/.../". gl_make_literal_regex_sed='s,[]$^\\.*/[],\\&,g' gl_header_literal_regex=`echo '$1' \ | sed -e "$gl_make_literal_regex_sed"` gl_absolute_header_sed="/${gl_dirsep_regex}${gl_header_literal_regex}/"'{ s/.*"\(.*'"${gl_dirsep_regex}${gl_header_literal_regex}"'\)".*/\1/ s|^/[^/]|//&| p q }' changequote([,]) dnl eval is necessary to expand gl_absname_cpp. dnl Ultrix and Pyramid sh refuse to redirect output of eval, dnl so use subshell. AS_VAR_SET([gl_cv_absolute_]AS_TR_SH([[$1]]), [`(eval "$gl_absname_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD | sed -n "$gl_absolute_header_sed"`]) ]) openvswitch-2.5.9/m4/PaxHeaders.82075/ltversion.m40000644000000000000000000000013213534540075016457 xustar0030 mtime=1567801405.869714035 30 atime=1567801406.005715035 30 ctime=1567801423.645845051 openvswitch-2.5.9/m4/ltversion.m40000644000175000017500000000127313534540075020150 0ustar00jpettitjpettit00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 4179 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.6]) m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) openvswitch-2.5.9/m4/PaxHeaders.82075/ax_func_posix_memalign.m40000644000000000000000000000013213534540071021144 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.637844993 openvswitch-2.5.9/m4/ax_func_posix_memalign.m40000644000175000017500000000277113534540071022641 0ustar00jpettitjpettit00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_func_posix_memalign.html # =========================================================================== # # SYNOPSIS # # AX_FUNC_POSIX_MEMALIGN # # DESCRIPTION # # Some versions of posix_memalign (notably glibc 2.2.5) incorrectly apply # their power-of-two check to the size argument, not the alignment # argument. AX_FUNC_POSIX_MEMALIGN defines HAVE_POSIX_MEMALIGN if the # power-of-two check is correctly applied to the alignment argument. # # LICENSE # # Copyright (c) 2008 Scott Pakin # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 7 AC_DEFUN([AX_FUNC_POSIX_MEMALIGN], [AC_CACHE_CHECK([for working posix_memalign], [ax_cv_func_posix_memalign_works], [AC_TRY_RUN([ #include int main () { void *buffer; /* Some versions of glibc incorrectly perform the alignment check on * the size word. */ exit (posix_memalign (&buffer, sizeof(void *), 123) != 0); } ], [ax_cv_func_posix_memalign_works=yes], [ax_cv_func_posix_memalign_works=no], [ax_cv_func_posix_memalign_works=no])]) if test "$ax_cv_func_posix_memalign_works" = "yes" ; then AC_DEFINE([HAVE_POSIX_MEMALIGN], [1], [Define to 1 if `posix_memalign' works.]) fi ]) openvswitch-2.5.9/m4/PaxHeaders.82075/compat.at0000644000000000000000000000013213534540071015775 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.649845081 openvswitch-2.5.9/m4/compat.at0000644000175000017500000000216213534540071017464 0ustar00jpettitjpettit00000000000000# -*- autoconf -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. m4_ifndef([AT_CHECK_UNQUOTED], [m4_define([AT_CHECK_UNQUOTED], [_AT_CHECK([$1], [$2], AS_ESCAPE(m4_dquote(m4_expand([$3])), [""]), AS_ESCAPE(m4_dquote(m4_expand([$4])),[""]), [$5], [$6])])]) m4_ifndef([AT_SKIP_IF], [m4_define([AT_SKIP_IF], [AT_CHECK([($1) \ && exit 77 || exit 0], [0], [ignore], [ignore])])]) m4_ifndef([AT_FAIL_IF], [m4_define([AT_FAIL_IF], [AT_CHECK([($1) \ && exit 99 || exit 0], [0], [ignore], [ignore])])]) m4_ifndef([AS_VAR_COPY], [m4_define([AS_VAR_COPY], [AS_LITERAL_IF([$1[]$2], [$1=$$2], [eval $1=\$$2])])]) openvswitch-2.5.9/m4/PaxHeaders.82075/ltoptions.m40000644000000000000000000000013213534540075016465 xustar0030 mtime=1567801405.789713445 30 atime=1567801406.005715035 30 ctime=1567801423.641845021 openvswitch-2.5.9/m4/ltoptions.m40000644000175000017500000003426213534540075020162 0ustar00jpettitjpettit00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) openvswitch-2.5.9/m4/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071016323 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801424.605852128 openvswitch-2.5.9/m4/automake.mk0000644000175000017500000000052313534540071020011 0ustar00jpettitjpettit00000000000000# Copyright (C) 2013 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ m4/absolute-header.m4 \ m4/include_next.m4 openvswitch-2.5.9/m4/PaxHeaders.82075/openvswitch.m40000644000000000000000000000013213534540071016777 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.645845051 openvswitch-2.5.9/m4/openvswitch.m40000644000175000017500000004277013534540071020477 0ustar00jpettitjpettit00000000000000# -*- autoconf -*- # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # m4_include([m4/compat.at]) dnl Checks for --enable-coverage and updates CFLAGS and LDFLAGS appropriately. AC_DEFUN([OVS_CHECK_COVERAGE], [AC_REQUIRE([AC_PROG_CC]) AC_ARG_ENABLE( [coverage], [AC_HELP_STRING([--enable-coverage], [Enable gcov coverage tool.])], [case "${enableval}" in (yes) coverage=true ;; (no) coverage=false ;; (*) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;; esac], [coverage=false]) if $coverage; then # Autoconf by default puts "-g -O2" in CFLAGS. We need to remove the -O2 # option for coverage to be useful. This does it without otherwise # interfering with anything that the user might have put there. old_CFLAGS=$CFLAGS CFLAGS= for option in $old_CFLAGS; do case $option in (-O2) ;; (*) CFLAGS="$CFLAGS $option" ;; esac done OVS_CFLAGS="$OVS_CFLAGS --coverage" OVS_LDFLAGS="$OVS_LDFLAGS --coverage" fi]) dnl Checks for --enable-ndebug and defines NDEBUG if it is specified. AC_DEFUN([OVS_CHECK_NDEBUG], [AC_ARG_ENABLE( [ndebug], [AC_HELP_STRING([--enable-ndebug], [Disable debugging features for max performance])], [case "${enableval}" in (yes) ndebug=true ;; (no) ndebug=false ;; (*) AC_MSG_ERROR([bad value ${enableval} for --enable-ndebug]) ;; esac], [ndebug=false]) AM_CONDITIONAL([NDEBUG], [test x$ndebug = xtrue])]) dnl Checks for ESX. AC_DEFUN([OVS_CHECK_ESX], [AC_CHECK_HEADER([vmware.h], [ESX=yes], [ESX=no]) AM_CONDITIONAL([ESX], [test "$ESX" = yes]) if test "$ESX" = yes; then AC_DEFINE([ESX], [1], [Define to 1 if building on ESX.]) fi]) dnl Checks for MSVC x64 compiler. AC_DEFUN([OVS_CHECK_WIN64], [AC_CACHE_CHECK( [for MSVC x64 compiler], [cl_cv_x64], [dnl "cl" writes x64 output to stdin: if (cl) 2>&1 | grep 'x64' >/dev/null 2>&1; then cl_cv_x64=yes MSVC64_LDFLAGS=" /MACHINE:X64 " else cl_cv_x64=no MSVC64_LDFLAGS="" fi]) AC_SUBST([MSVC64_LDFLAGS]) ]) dnl Checks for WINDOWS. AC_DEFUN([OVS_CHECK_WIN32], [AC_CHECK_HEADER([windows.h], [WIN32=yes], [WIN32=no]) AM_CONDITIONAL([WIN32], [test "$WIN32" = yes]) if test "$WIN32" = yes; then AC_ARG_WITH([pthread], [AS_HELP_STRING([--with-pthread=DIR], [root of the pthread-win32 directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-pthread value]) ;; *) if (cl) 2>&1 | grep 'x64' >/dev/null 2>&1; then cl_cv_x64=yes else cl_cv_x64=no fi if test "$cl_cv_x64" = yes; then PTHREAD_WIN32_DIR=$withval/lib/x64 PTHREAD_WIN32_DIR_DLL=/$(echo ${withval} | ${SED} -e 's/://')/dll/x64 PTHREAD_WIN32_DIR_DLL_WIN_FORM=$withval/dll/x64 else PTHREAD_WIN32_DIR=$withval/lib/x86 PTHREAD_WIN32_DIR_DLL=/$(echo ${withval} | ${SED} -e 's/://')/dll/x86 PTHREAD_WIN32_DIR_DLL_WIN_FORM=$withval/dll/x86 fi PTHREAD_INCLUDES=-I$withval/include PTHREAD_LDFLAGS=-L$PTHREAD_WIN32_DIR PTHREAD_LIBS="-lpthreadVC2" AC_SUBST([PTHREAD_WIN32_DIR_DLL_WIN_FORM]) AC_SUBST([PTHREAD_WIN32_DIR_DLL]) AC_SUBST([PTHREAD_INCLUDES]) AC_SUBST([PTHREAD_LDFLAGS]) AC_SUBST([PTHREAD_LIBS]) ;; esac ], [ AC_MSG_ERROR([pthread directory not specified]) ] ) AC_ARG_WITH([debug], [AS_HELP_STRING([--with-debug], [Build without compiler optimizations])], [ MSVC_CFLAGS="-O0" AC_SUBST([MSVC_CFLAGS]) ], [ MSVC_CFLAGS="-O2" AC_SUBST([MSVC_CFLAGS]) ] ) AC_DEFINE([WIN32], [1], [Define to 1 if building on WIN32.]) AH_BOTTOM([#ifdef WIN32 #include "include/windows/windefs.h" #endif]) fi]) dnl OVS_CHECK_WINDOWS dnl dnl Configure Visual Studio solution build AC_DEFUN([OVS_CHECK_VISUAL_STUDIO_DDK], [ AC_ARG_WITH([vstudiotarget], [AS_HELP_STRING([--with-vstudiotarget=target_type], [Target type: Debug/Release])], [ case "$withval" in "Release") ;; "Debug") ;; *) AC_MSG_ERROR([No valid Visual Studio configuration found]) ;; esac VSTUDIO_CONFIG=$withval ], [ VSTUDIO_CONFIG= ] ) AC_SUBST([VSTUDIO_CONFIG]) AC_DEFINE([VSTUDIO_DDK], [1], [System uses the Visual Studio build target.]) AM_CONDITIONAL([VSTUDIO_DDK], [test -n "$VSTUDIO_CONFIG"]) ]) dnl Checks for Netlink support. AC_DEFUN([OVS_CHECK_NETLINK], [AC_CHECK_HEADER([linux/netlink.h], [HAVE_NETLINK=yes], [HAVE_NETLINK=no], [#include ]) AM_CONDITIONAL([HAVE_NETLINK], [test "$HAVE_NETLINK" = yes]) if test "$HAVE_NETLINK" = yes; then AC_DEFINE([HAVE_NETLINK], [1], [Define to 1 if Netlink protocol is available.]) fi]) dnl Checks for libcap-ng. AC_DEFUN([OVS_CHECK_LIBCAPNG], [AC_ARG_ENABLE( [libcapng], [AC_HELP_STRING([--disable-libcapng], [Disable Linux capability support])], [case "${enableval}" in (yes) libcapng=true ;; (no) libcapng=false ;; (*) AC_MSG_ERROR([bad value ${enableval} for --enable-libcapng]) ;; esac], [libcapng=check]) if test "$libcapng" != false; then AC_CHECK_LIB([cap-ng], [capng_clear], [HAVE_LIBCAPNG=yes]) if test "$HAVE_LIBCAPNG" != yes; then if test "$libcapng" = true ; then AC_MSG_ERROR([libcap-ng support requested, but not found]) fi if test "$libcapng" = check ; then AC_MSG_WARN([cannot find libcap-ng. --user option will not be supported on Linux. (you may use --disable-libcapng to suppress this warning). ]) fi fi fi AC_SUBST([HAVE_LIBCAPNG]) AM_CONDITIONAL([HAVE_LIBCAPNG], [test "$HAVE_LIBCAPNG" = yes]) if test "$HAVE_LIBCAPNG" = yes; then AC_DEFINE([HAVE_LIBCAPNG], [1], [Define to 1 if libcap-ng is available.]) CAPNG_LDADD="-lcap-ng" AC_SUBST([CAPNG_LDADD]) fi]) dnl Checks for OpenSSL. AC_DEFUN([OVS_CHECK_OPENSSL], [AC_ARG_ENABLE( [ssl], [AC_HELP_STRING([--disable-ssl], [Disable OpenSSL support])], [case "${enableval}" in (yes) ssl=true ;; (no) ssl=false ;; (*) AC_MSG_ERROR([bad value ${enableval} for --enable-ssl]) ;; esac], [ssl=check]) if test "$ssl" != false; then AX_CHECK_OPENSSL( [HAVE_OPENSSL=yes], [HAVE_OPENSSL=no if test "$ssl" = check; then AC_MSG_WARN([Cannot find openssl: $SSL_PKG_ERRORS OpenFlow connections over SSL will not be supported. (You may use --disable-ssl to suppress this warning.)]) else AC_MSG_ERROR([Cannot find openssl (use --disable-ssl to configure without SSL support)]) fi]) else HAVE_OPENSSL=no fi AC_SUBST([HAVE_OPENSSL]) AM_CONDITIONAL([HAVE_OPENSSL], [test "$HAVE_OPENSSL" = yes]) if test "$HAVE_OPENSSL" = yes; then AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL is installed.]) fi]) dnl Checks for libraries needed by lib/socket-util.c. AC_DEFUN([OVS_CHECK_SOCKET_LIBS], [AC_CHECK_LIB([socket], [connect]) AC_SEARCH_LIBS([gethostbyname], [resolv])]) dnl Checks for the directory in which to store the PKI. AC_DEFUN([OVS_CHECK_PKIDIR], [AC_ARG_WITH( [pkidir], AC_HELP_STRING([--with-pkidir=DIR], [PKI hierarchy directory [[LOCALSTATEDIR/lib/openvswitch/pki]]]), [PKIDIR=$withval], [PKIDIR='${localstatedir}/lib/openvswitch/pki']) AC_SUBST([PKIDIR])]) dnl Checks for the directory in which to store pidfiles. AC_DEFUN([OVS_CHECK_RUNDIR], [AC_ARG_WITH( [rundir], AC_HELP_STRING([--with-rundir=DIR], [directory used for pidfiles [[LOCALSTATEDIR/run/openvswitch]]]), [RUNDIR=$withval], [RUNDIR='${localstatedir}/run/openvswitch']) AC_SUBST([RUNDIR])]) dnl Checks for the directory in which to store logs. AC_DEFUN([OVS_CHECK_LOGDIR], [AC_ARG_WITH( [logdir], AC_HELP_STRING([--with-logdir=DIR], [directory used for logs [[LOCALSTATEDIR/log/PACKAGE]]]), [LOGDIR=$withval], [LOGDIR='${localstatedir}/log/${PACKAGE}']) AC_SUBST([LOGDIR])]) dnl Checks for the directory in which to store the Open vSwitch database. AC_DEFUN([OVS_CHECK_DBDIR], [AC_ARG_WITH( [dbdir], AC_HELP_STRING([--with-dbdir=DIR], [directory used for conf.db [[SYSCONFDIR/PACKAGE]]]), [DBDIR=$withval], [DBDIR='${sysconfdir}/${PACKAGE}']) AC_SUBST([DBDIR])]) dnl Defines HAVE_BACKTRACE if backtrace() is found. AC_DEFUN([OVS_CHECK_BACKTRACE], [AC_SEARCH_LIBS([backtrace], [execinfo ubacktrace], [AC_DEFINE([HAVE_BACKTRACE], [1], [Define to 1 if you have backtrace(3).])])]) dnl Defines HAVE_PERF_EVENT if linux/perf_event.h is found. AC_DEFUN([OVS_CHECK_PERF_EVENT], [AC_CHECK_HEADERS([linux/perf_event.h])]) dnl Checks for valgrind/valgrind.h. AC_DEFUN([OVS_CHECK_VALGRIND], [AC_CHECK_HEADERS([valgrind/valgrind.h])]) dnl Checks for Python 2.x, x >= 7. AC_DEFUN([OVS_CHECK_PYTHON], [AC_CACHE_CHECK( [for Python 2.x for x >= 7], [ovs_cv_python], [if test -n "$PYTHON"; then ovs_cv_python=$PYTHON else ovs_cv_python=no for binary in python python2.7; do ovs_save_IFS=$IFS; IFS=$PATH_SEPARATOR for dir in $PATH; do IFS=$ovs_save_IFS test -z "$dir" && dir=. if test -x "$dir"/"$binary" && "$dir"/"$binary" -c 'import sys if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000: sys.exit(0) else: sys.exit(1)'; then ovs_cv_python=$dir/$binary break 2 fi done done fi]) AC_SUBST([HAVE_PYTHON]) AM_MISSING_PROG([PYTHON], [python]) if test $ovs_cv_python != no; then PYTHON=$ovs_cv_python HAVE_PYTHON=yes else HAVE_PYTHON=no fi AM_CONDITIONAL([HAVE_PYTHON], [test "$HAVE_PYTHON" = yes])]) dnl Checks for dot. AC_DEFUN([OVS_CHECK_DOT], [AC_CACHE_CHECK( [for dot], [ovs_cv_dot], [dnl "dot" writes -V output to stderr: if (dot -V) 2>&1 | grep '^dot - [[gG]]raphviz version' >/dev/null 2>&1; then ovs_cv_dot=yes else ovs_cv_dot=no fi]) AM_CONDITIONAL([HAVE_DOT], [test "$ovs_cv_dot" = yes])]) dnl Checks for groff. AC_DEFUN([OVS_CHECK_GROFF], [AC_CACHE_CHECK( [for groff], [ovs_cv_groff], [if (groff -v) >/dev/null 2>&1; then ovs_cv_groff=yes else ovs_cv_groff=no fi]) AM_CONDITIONAL([HAVE_GROFF], [test "$ovs_cv_groff" = yes])]) dnl Checks for thread-local storage support. dnl dnl Checks whether the compiler and linker support the C11 dnl thread_local macro from , and if so defines dnl HAVE_THREAD_LOCAL. If not, checks whether the compiler and linker dnl support the GCC __thread extension, and if so defines dnl HAVE___THREAD. AC_DEFUN([OVS_CHECK_TLS], [AC_CACHE_CHECK( [whether $CC has that supports thread_local], [ovs_cv_thread_local], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include static thread_local int var;], [return var;])], [ovs_cv_thread_local=yes], [ovs_cv_thread_local=no])]) if test $ovs_cv_thread_local = yes; then AC_DEFINE([HAVE_THREAD_LOCAL], [1], [Define to 1 if the C compiler and linker supports the C11 thread_local matcro defined in .]) else AC_CACHE_CHECK( [whether $CC supports __thread], [ovs_cv___thread], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([static __thread int var;], [return var;])], [ovs_cv___thread=yes], [ovs_cv___thread=no])]) if test $ovs_cv___thread = yes; then AC_DEFINE([HAVE___THREAD], [1], [Define to 1 if the C compiler and linker supports the GCC __thread extenions.]) fi fi]) dnl OVS_CHECK_ATOMIC_LIBS dnl dnl Check to see if -latomic is need for GCC atomic built-ins. AC_DEFUN([OVS_CHECK_ATOMIC_LIBS], [AC_SEARCH_LIBS([__atomic_load_8], [atomic])]) dnl OVS_CHECK_GCC4_ATOMICS dnl dnl Checks whether the compiler and linker support GCC 4.0+ atomic built-ins. dnl A compile-time only check is not enough because the compiler defers dnl unimplemented built-ins to libgcc, which sometimes also lacks dnl implementations. AC_DEFUN([OVS_CHECK_GCC4_ATOMICS], [AC_CACHE_CHECK( [whether $CC supports GCC 4.0+ atomic built-ins], [ovs_cv_gcc4_atomics], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include #define ovs_assert(expr) if (!(expr)) abort(); #define TEST_ATOMIC_TYPE(TYPE) \ { \ TYPE x = 1; \ TYPE orig; \ \ __sync_synchronize(); \ ovs_assert(x == 1); \ \ __sync_synchronize(); \ x = 3; \ __sync_synchronize(); \ ovs_assert(x == 3); \ \ orig = __sync_fetch_and_add(&x, 1); \ ovs_assert(orig == 3); \ __sync_synchronize(); \ ovs_assert(x == 4); \ \ orig = __sync_fetch_and_sub(&x, 2); \ ovs_assert(orig == 4); \ __sync_synchronize(); \ ovs_assert(x == 2); \ \ orig = __sync_fetch_and_or(&x, 6); \ ovs_assert(orig == 2); \ __sync_synchronize(); \ ovs_assert(x == 6); \ \ orig = __sync_fetch_and_and(&x, 10); \ ovs_assert(orig == 6); \ __sync_synchronize(); \ ovs_assert(x == 2); \ \ orig = __sync_fetch_and_xor(&x, 10); \ ovs_assert(orig == 2); \ __sync_synchronize(); \ ovs_assert(x == 8); \ }]], [dnl TEST_ATOMIC_TYPE(char); TEST_ATOMIC_TYPE(unsigned char); TEST_ATOMIC_TYPE(signed char); TEST_ATOMIC_TYPE(short); TEST_ATOMIC_TYPE(unsigned short); TEST_ATOMIC_TYPE(int); TEST_ATOMIC_TYPE(unsigned int); TEST_ATOMIC_TYPE(long int); TEST_ATOMIC_TYPE(unsigned long int); TEST_ATOMIC_TYPE(long long int); TEST_ATOMIC_TYPE(unsigned long long int); ])], [ovs_cv_gcc4_atomics=yes], [ovs_cv_gcc4_atomics=no])]) if test $ovs_cv_gcc4_atomics = yes; then AC_DEFINE([HAVE_GCC4_ATOMICS], [1], [Define to 1 if the C compiler and linker supports the GCC 4.0+ atomic built-ins.]) fi]) dnl OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE(SIZE) dnl dnl Checks __atomic_always_lock_free(SIZE, 0) AC_DEFUN([OVS_CHECK_ATOMIC_ALWAYS_LOCK_FREE], [AC_CACHE_CHECK( [value of __atomic_always_lock_free($1)], [ovs_cv_atomic_always_lock_free_$1], [AC_COMPUTE_INT( [ovs_cv_atomic_always_lock_free_$1], [__atomic_always_lock_free($1, 0)], [], [ovs_cv_atomic_always_lock_free_$1=unsupported])]) if test ovs_cv_atomic_always_lock_free_$1 != unsupported; then AC_DEFINE_UNQUOTED( [ATOMIC_ALWAYS_LOCK_FREE_$1B], [$ovs_cv_atomic_always_lock_free_$1], [If the C compiler is GCC 4.7 or later, define to the return value of __atomic_always_lock_free($1, 0). If the C compiler is not GCC or is an older version of GCC, the value does not matter.]) fi]) dnl OVS_CHECK_POSIX_AIO AC_DEFUN([OVS_CHECK_POSIX_AIO], [AC_SEARCH_LIBS([aio_write], [rt]) AM_CONDITIONAL([HAVE_POSIX_AIO], [test "$ac_cv_search_aio_write" != no])]) dnl OVS_CHECK_INCLUDE_NEXT AC_DEFUN([OVS_CHECK_INCLUDE_NEXT], [AC_REQUIRE([gl_CHECK_NEXT_HEADERS]) gl_CHECK_NEXT_HEADERS([$1])]) dnl OVS_CHECK_PRAGMA_MESSAGE AC_DEFUN([OVS_CHECK_PRAGMA_MESSAGE], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[_Pragma("message(\"Checking for pragma message\")") ]])], [AC_DEFINE(HAVE_PRAGMA_MESSAGE,1,[Define if compiler supports #pragma message directive])]) ]) openvswitch-2.5.9/m4/PaxHeaders.82075/include_next.m40000644000000000000000000000013213534540071017107 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.637844993 openvswitch-2.5.9/m4/include_next.m40000644000175000017500000002077313534540071020606 0ustar00jpettitjpettit00000000000000# include_next.m4 serial 23 dnl Copyright (C) 2006-2013 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Paul Eggert and Derek Price. dnl Sets INCLUDE_NEXT and PRAGMA_SYSTEM_HEADER. dnl dnl INCLUDE_NEXT expands to 'include_next' if the compiler supports it, or to dnl 'include' otherwise. dnl dnl INCLUDE_NEXT_AS_FIRST_DIRECTIVE expands to 'include_next' if the compiler dnl supports it in the special case that it is the first include directive in dnl the given file, or to 'include' otherwise. dnl dnl PRAGMA_SYSTEM_HEADER can be used in files that contain #include_next, dnl so as to avoid GCC warnings when the gcc option -pedantic is used. dnl '#pragma GCC system_header' has the same effect as if the file was found dnl through the include search path specified with '-isystem' options (as dnl opposed to the search path specified with '-I' options). Namely, gcc dnl does not warn about some things, and on some systems (Solaris and Interix) dnl __STDC__ evaluates to 0 instead of to 1. The latter is an undesired side dnl effect; we are therefore careful to use 'defined __STDC__' or '1' instead dnl of plain '__STDC__'. dnl dnl PRAGMA_COLUMNS can be used in files that override system header files, so dnl as to avoid compilation errors on HP NonStop systems when the gnulib file dnl is included by a system header file that does a "#pragma COLUMNS 80" (which dnl has the effect of truncating the lines of that file and all files that it dnl includes to 80 columns) and the gnulib file has lines longer than 80 dnl columns. AC_DEFUN([gl_INCLUDE_NEXT], [ AC_LANG_PREPROC_REQUIRE() AC_CACHE_CHECK([whether the preprocessor supports include_next], [gl_cv_have_include_next], [rm -rf conftestd1a conftestd1b conftestd2 mkdir conftestd1a conftestd1b conftestd2 dnl IBM C 9.0, 10.1 (original versions, prior to the 2009-01 updates) on dnl AIX 6.1 support include_next when used as first preprocessor directive dnl in a file, but not when preceded by another include directive. Check dnl for this bug by including . dnl Additionally, with this same compiler, include_next is a no-op when dnl used in a header file that was included by specifying its absolute dnl file name. Despite these two bugs, include_next is used in the dnl compiler's . By virtue of the second bug, we need to use dnl include_next as well in this case. cat < conftestd1a/conftest.h #define DEFINED_IN_CONFTESTD1 #include_next #ifdef DEFINED_IN_CONFTESTD2 int foo; #else #error "include_next doesn't work" #endif EOF cat < conftestd1b/conftest.h #define DEFINED_IN_CONFTESTD1 #include #include_next #ifdef DEFINED_IN_CONFTESTD2 int foo; #else #error "include_next doesn't work" #endif EOF cat < conftestd2/conftest.h #ifndef DEFINED_IN_CONFTESTD1 #error "include_next test doesn't work" #endif #define DEFINED_IN_CONFTESTD2 EOF gl_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1b -Iconftestd2" dnl We intentionally avoid using AC_LANG_SOURCE here. AC_COMPILE_IFELSE([AC_LANG_DEFINES_PROVIDED[#include ]], [gl_cv_have_include_next=yes], [CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1a -Iconftestd2" AC_COMPILE_IFELSE([AC_LANG_DEFINES_PROVIDED[#include ]], [gl_cv_have_include_next=buggy], [gl_cv_have_include_next=no]) ]) CPPFLAGS="$gl_save_CPPFLAGS" rm -rf conftestd1a conftestd1b conftestd2 ]) PRAGMA_SYSTEM_HEADER= if test $gl_cv_have_include_next = yes; then INCLUDE_NEXT=include_next INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next if test -n "$GCC"; then PRAGMA_SYSTEM_HEADER='#pragma GCC system_header' fi else if test $gl_cv_have_include_next = buggy; then INCLUDE_NEXT=include INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next else INCLUDE_NEXT=include INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include fi fi AC_SUBST([INCLUDE_NEXT]) AC_SUBST([INCLUDE_NEXT_AS_FIRST_DIRECTIVE]) AC_SUBST([PRAGMA_SYSTEM_HEADER]) AC_CACHE_CHECK([whether system header files limit the line length], [gl_cv_pragma_columns], [dnl HP NonStop systems, which define __TANDEM, have this misfeature. AC_EGREP_CPP([choke me], [ #ifdef __TANDEM choke me #endif ], [gl_cv_pragma_columns=yes], [gl_cv_pragma_columns=no]) ]) if test $gl_cv_pragma_columns = yes; then PRAGMA_COLUMNS="#pragma COLUMNS 10000" else PRAGMA_COLUMNS= fi AC_SUBST([PRAGMA_COLUMNS]) ]) # gl_CHECK_NEXT_HEADERS(HEADER1 HEADER2 ...) # ------------------------------------------ # For each arg foo.h, if #include_next works, define NEXT_FOO_H to be # ''; otherwise define it to be # '"///usr/include/foo.h"', or whatever other absolute file name is suitable. # Also, if #include_next works as first preprocessing directive in a file, # define NEXT_AS_FIRST_DIRECTIVE_FOO_H to be ''; otherwise define it to # be # '"///usr/include/foo.h"', or whatever other absolute file name is suitable. # That way, a header file with the following line: # #@INCLUDE_NEXT@ @NEXT_FOO_H@ # or # #@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_FOO_H@ # behaves (after sed substitution) as if it contained # #include_next # even if the compiler does not support include_next. # The three "///" are to pacify Sun C 5.8, which otherwise would say # "warning: #include of /usr/include/... may be non-portable". # Use '""', not '<>', so that the /// cannot be confused with a C99 comment. # Note: This macro assumes that the header file is not empty after # preprocessing, i.e. it does not only define preprocessor macros but also # provides some type/enum definitions or function/variable declarations. # # This macro also checks whether each header exists, by invoking # AC_CHECK_HEADERS_ONCE or AC_CHECK_HEADERS on each argument. AC_DEFUN([gl_CHECK_NEXT_HEADERS], [ gl_NEXT_HEADERS_INTERNAL([$1], [check]) ]) # gl_NEXT_HEADERS(HEADER1 HEADER2 ...) # ------------------------------------ # Like gl_CHECK_NEXT_HEADERS, except do not check whether the headers exist. # This is suitable for headers like that are standardized by C89 # and therefore can be assumed to exist. AC_DEFUN([gl_NEXT_HEADERS], [ gl_NEXT_HEADERS_INTERNAL([$1], [assume]) ]) # The guts of gl_CHECK_NEXT_HEADERS and gl_NEXT_HEADERS. AC_DEFUN([gl_NEXT_HEADERS_INTERNAL], [ AC_REQUIRE([gl_INCLUDE_NEXT]) AC_REQUIRE([AC_CANONICAL_HOST]) m4_if([$2], [check], [AC_CHECK_HEADERS_ONCE([$1]) ]) dnl FIXME: gl_next_header and gl_header_exists must be used unquoted dnl until we can assume autoconf 2.64 or newer. m4_foreach_w([gl_HEADER_NAME], [$1], [AS_VAR_PUSHDEF([gl_next_header], [gl_cv_next_]m4_defn([gl_HEADER_NAME])) if test $gl_cv_have_include_next = yes; then AS_VAR_SET(gl_next_header, ['<'gl_HEADER_NAME'>']) else AC_CACHE_CHECK( [absolute name of <]m4_defn([gl_HEADER_NAME])[>], m4_defn([gl_next_header]), [m4_if([$2], [check], [AS_VAR_PUSHDEF([gl_header_exists], [ac_cv_header_]m4_defn([gl_HEADER_NAME])) if test AS_VAR_GET(gl_header_exists) = yes; then AS_VAR_POPDEF([gl_header_exists]) ]) gl_ABSOLUTE_HEADER_ONE(gl_HEADER_NAME) AS_VAR_COPY([gl_header], [gl_cv_absolute_]AS_TR_SH(gl_HEADER_NAME)) AS_VAR_SET(gl_next_header, ['"'$gl_header'"']) m4_if([$2], [check], [else AS_VAR_SET(gl_next_header, ['<'gl_HEADER_NAME'>']) fi ]) ]) fi AC_SUBST( AS_TR_CPP([NEXT_]m4_defn([gl_HEADER_NAME])), [AS_VAR_GET(gl_next_header)]) if test $gl_cv_have_include_next = yes || test $gl_cv_have_include_next = buggy; then # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include_next' gl_next_as_first_directive='<'gl_HEADER_NAME'>' else # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include' gl_next_as_first_directive=AS_VAR_GET(gl_next_header) fi AC_SUBST( AS_TR_CPP([NEXT_AS_FIRST_DIRECTIVE_]m4_defn([gl_HEADER_NAME])), [$gl_next_as_first_directive]) AS_VAR_POPDEF([gl_next_header])]) ]) # Autoconf 2.68 added warnings for our use of AC_COMPILE_IFELSE; # this fallback is safe for all earlier autoconf versions. m4_define_default([AC_LANG_DEFINES_PROVIDED]) openvswitch-2.5.9/m4/PaxHeaders.82075/lt~obsolete.m40000644000000000000000000000013213534540075017004 xustar0030 mtime=1567801405.909714328 30 atime=1567801406.005715035 30 ctime=1567801423.645845051 openvswitch-2.5.9/m4/lt~obsolete.m40000644000175000017500000001377413534540075020506 0ustar00jpettitjpettit00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) openvswitch-2.5.9/m4/PaxHeaders.82075/ltsugar.m40000644000000000000000000000013113534540075016112 xustar0029 mtime=1567801405.82571371 30 atime=1567801406.005715035 30 ctime=1567801423.641845021 openvswitch-2.5.9/m4/ltsugar.m40000644000175000017500000001044013534540075017600 0ustar00jpettitjpettit00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) openvswitch-2.5.9/m4/PaxHeaders.82075/libtool.m40000644000000000000000000000013213534540075016076 xustar0030 mtime=1567801405.745713122 30 atime=1567801406.009715065 30 ctime=1567801423.641845021 openvswitch-2.5.9/m4/libtool.m40000644000175000017500000112617113534540075017575 0ustar00jpettitjpettit00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi _LT_TAGVAR(link_all_deplibs, $1)=no else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS openvswitch-2.5.9/m4/PaxHeaders.82075/ax_check_openssl.m40000644000000000000000000000013213534540071017736 xustar0030 mtime=1567801401.613682727 30 atime=1567801402.101686312 30 ctime=1567801423.637844993 openvswitch-2.5.9/m4/ax_check_openssl.m40000644000175000017500000001025313534540071021425 0ustar00jpettitjpettit00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_openssl.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]]) # # DESCRIPTION # # Look for OpenSSL in a number of default spots, or in a user-selected # spot (via --with-openssl). Sets # # SSL_INCLUDES to the include directives required # SSL_LIBS to the -l directives required # SSL_LDFLAGS to the -L or -R flags required # # and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately # # This macro sets SSL_INCLUDES such that source files should use the # openssl/ directory in include directives: # # #include # # LICENSE # # Copyright (c) 2009,2010 Zmanda Inc. # Copyright (c) 2009,2010 Dustin J. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 8 AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL]) AC_DEFUN([AX_CHECK_OPENSSL], [ found=false AC_ARG_WITH([openssl], [AS_HELP_STRING([--with-openssl=DIR], [root of the OpenSSL directory])], [ case "$withval" in "" | y | ye | yes | n | no) AC_MSG_ERROR([Invalid --with-openssl value]) ;; *) ssldirs="$withval" ;; esac ], [ # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs AC_PATH_PROG([PKG_CONFIG], [pkg-config]) if test x"$PKG_CONFIG" != x""; then SSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` if test $? = 0; then SSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` SSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi ] ) # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then SSL_INCLUDES= for ssldir in $ssldirs; do AC_MSG_CHECKING([for openssl/ssl.h in $ssldir]) if test -f "$ssldir/include/openssl/ssl.h"; then SSL_INCLUDES="-I$ssldir/include" SSL_LDFLAGS="-L$ssldir/lib" if test "$WIN32" = "yes"; then SSL_LIBS="-lssleay32 -llibeay32" else SSL_LIBS="-lssl -lcrypto" fi found=true AC_MSG_RESULT([yes]) break else AC_MSG_RESULT([no]) fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS AC_MSG_CHECKING([whether compiling and linking against OpenSSL works]) echo "Trying link with SSL_LDFLAGS=$SSL_LDFLAGS;" \ "SSL_LIBS=$SSL_LIBS; SSL_INCLUDES=$SSL_INCLUDES" >&AS_MESSAGE_LOG_FD save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $SSL_LDFLAGS" LIBS="$SSL_LIBS $LIBS" CPPFLAGS="$SSL_INCLUDES $CPPFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([#include ], [SSL_CTX *ctx=NULL;SSL_new(ctx)])], [ AC_MSG_RESULT([yes]) $1 ], [ AC_MSG_RESULT([no]) $2 ]) CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" AC_SUBST([SSL_INCLUDES]) AC_SUBST([SSL_LIBS]) AC_SUBST([SSL_LDFLAGS]) ]) openvswitch-2.5.9/PaxHeaders.82075/FAQ.md0000644000000000000000000000013213534540071014575 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801423.661845169 openvswitch-2.5.9/FAQ.md0000644000175000017500000026144113534540071016273 0ustar00jpettitjpettit00000000000000Frequently Asked Questions ========================== Open vSwitch General ------- ### Q: What is Open vSwitch? A: Open vSwitch is a production quality open source software switch designed to be used as a vswitch in virtualized server environments. A vswitch forwards traffic between different VMs on the same physical host and also forwards traffic between VMs and the physical network. Open vSwitch supports standard management interfaces (e.g. sFlow, NetFlow, IPFIX, RSPAN, CLI), and is open to programmatic extension and control using OpenFlow and the OVSDB management protocol. Open vSwitch as designed to be compatible with modern switching chipsets. This means that it can be ported to existing high-fanout switches allowing the same flexible control of the physical infrastructure as the virtual infrastructure. It also means that Open vSwitch will be able to take advantage of on-NIC switching chipsets as their functionality matures. ### Q: What virtualization platforms can use Open vSwitch? A: Open vSwitch can currently run on any Linux-based virtualization platform (kernel 2.6.32 and newer), including: KVM, VirtualBox, Xen, Xen Cloud Platform, XenServer. As of Linux 3.3 it is part of the mainline kernel. The bulk of the code is written in platform- independent C and is easily ported to other environments. We welcome inquires about integrating Open vSwitch with other virtualization platforms. ### Q: How can I try Open vSwitch? A: The Open vSwitch source code can be built on a Linux system. You can build and experiment with Open vSwitch on any Linux machine. Packages for various Linux distributions are available on many platforms, including: Debian, Ubuntu, Fedora. You may also download and run a virtualization platform that already has Open vSwitch integrated. For example, download a recent ISO for XenServer or Xen Cloud Platform. Be aware that the version integrated with a particular platform may not be the most recent Open vSwitch release. ### Q: Does Open vSwitch only work on Linux? A: No, Open vSwitch has been ported to a number of different operating systems and hardware platforms. Most of the development work occurs on Linux, but the code should be portable to any POSIX system. We've seen Open vSwitch ported to a number of different platforms, including FreeBSD, Windows, and even non-POSIX embedded systems. By definition, the Open vSwitch Linux kernel module only works on Linux and will provide the highest performance. However, a userspace datapath is available that should be very portable. ### Q: What's involved with porting Open vSwitch to a new platform or switching ASIC? A: The [PORTING.md] document describes how one would go about porting Open vSwitch to a new operating system or hardware platform. ### Q: Why would I use Open vSwitch instead of the Linux bridge? A: Open vSwitch is specially designed to make it easier to manage VM network configuration and monitor state spread across many physical hosts in dynamic virtualized environments. Please see [WHY-OVS.md] for a more detailed description of how Open vSwitch relates to the Linux Bridge. ### Q: How is Open vSwitch related to distributed virtual switches like the VMware vNetwork distributed switch or the Cisco Nexus 1000V? A: Distributed vswitch applications (e.g., VMware vNetwork distributed switch, Cisco Nexus 1000V) provide a centralized way to configure and monitor the network state of VMs that are spread across many physical hosts. Open vSwitch is not a distributed vswitch itself, rather it runs on each physical host and supports remote management in a way that makes it easier for developers of virtualization/cloud management platforms to offer distributed vswitch capabilities. To aid in distribution, Open vSwitch provides two open protocols that are specially designed for remote management in virtualized network environments: OpenFlow, which exposes flow-based forwarding state, and the OVSDB management protocol, which exposes switch port state. In addition to the switch implementation itself, Open vSwitch includes tools (ovs-ofctl, ovs-vsctl) that developers can script and extend to provide distributed vswitch capabilities that are closely integrated with their virtualization management platform. ### Q: Why doesn't Open vSwitch support distribution? A: Open vSwitch is intended to be a useful component for building flexible network infrastructure. There are many different approaches to distribution which balance trade-offs between simplicity, scalability, hardware compatibility, convergence times, logical forwarding model, etc. The goal of Open vSwitch is to be able to support all as a primitive building block rather than choose a particular point in the distributed design space. ### Q: How can I contribute to the Open vSwitch Community? A: You can start by joining the mailing lists and helping to answer questions. You can also suggest improvements to documentation. If you have a feature or bug you would like to work on, send a mail to one of the mailing lists: http://openvswitch.org/mlists/ ### Q: Why can I no longer connect to my OpenFlow controller or OVSDB manager? A: Starting in OVS 2.4, we switched the default ports to the IANA-specified port numbers for OpenFlow (6633->6653) and OVSDB (6632->6640). We recommend using these port numbers, but if you cannot, all the programs allow overriding the default port. See the appropriate man page. Releases -------- ### Q: What does it mean for an Open vSwitch release to be LTS (long-term support)? A: All official releases have been through a comprehensive testing process and are suitable for production use. Planned releases will occur several times a year. If a significant bug is identified in an LTS release, we will provide an updated release that includes the fix. Releases that are not LTS may not be fixed and may just be supplanted by the next major release. The current LTS release is 2.5.x. ### Q: What Linux kernel versions does each Open vSwitch release work with? A: The following table lists the Linux kernel versions against which the given versions of the Open vSwitch kernel module will successfully build. The Linux kernel versions are upstream kernel versions, so Linux kernels modified from the upstream sources may not build in some cases even if they are based on a supported version. This is most notably true of Red Hat Enterprise Linux (RHEL) kernels, which are extensively modified from upstream. | Open vSwitch | Linux kernel |:------------:|:-------------: | 1.4.x | 2.6.18 to 3.2 | 1.5.x | 2.6.18 to 3.2 | 1.6.x | 2.6.18 to 3.2 | 1.7.x | 2.6.18 to 3.3 | 1.8.x | 2.6.18 to 3.4 | 1.9.x | 2.6.18 to 3.8 | 1.10.x | 2.6.18 to 3.8 | 1.11.x | 2.6.18 to 3.8 | 2.0.x | 2.6.32 to 3.10 | 2.1.x | 2.6.32 to 3.11 | 2.3.x | 2.6.32 to 3.14 | 2.4.x | 2.6.32 to 4.0 | 2.5.x | 2.6.32 to 4.3 Open vSwitch userspace should also work with the Linux kernel module built into Linux 3.3 and later. Open vSwitch userspace is not sensitive to the Linux kernel version. It should build against almost any kernel, certainly against 2.6.32 and later. ### Q: Are all features available with all datapaths? A: Open vSwitch supports different datapaths on different platforms. Each datapath has a different feature set: the following tables try to summarize the status. Supported datapaths: * *Linux upstream*: The datapath implemented by the kernel module shipped with Linux upstream. Since features have been gradually introduced into the kernel, the table mentions the first Linux release whose OVS module supports the feature. * *Linux OVS tree*: The datapath implemented by the Linux kernel module distributed with the OVS source tree. Some features of this module rely on functionality not available in older kernels: in this case the minumum Linux version (against which the feature can be compiled) is listed. * *Userspace*: Also known as DPDK, dpif-netdev or dummy datapath. It is the only datapath that works on NetBSD and FreeBSD. * *Hyper-V*: Also known as the Windows datapath. The following table lists the datapath supported features from an Open vSwitch user's perspective. Feature | Linux upstream | Linux OVS tree | Userspace | Hyper-V | ----------------------|:--------------:|:--------------:|:---------:|:-------:| Connection tracking | 4.3 | 3.10 | NO | NO | Tunnel - LISP | NO | YES | NO | NO | Tunnel - STT | NO | 3.5 | NO | YES | Tunnel - GRE | 3.11 | YES | YES | YES | Tunnel - VXLAN | 3.12 | YES | YES | YES | Tunnel - Geneve | 3.18 | YES | YES | NO | QoS - Policing | YES | YES | NO | NO | QoS - Shaping | YES | YES | NO | NO | sFlow | YES | YES | YES | NO | Set action | YES | YES | YES | PARTIAL | NIC Bonding | YES | YES | YES | NO | Multiple VTEPs | YES | YES | YES | NO | **Notes:** * Only a limited set of flow fields is modifiable via the set action by the Hyper-V datapath. * The Hyper-V datapath only supports one physical NIC per datapath. This is why bonding is not supported. * The Hyper-V datapath can have at most one IP address configured as a tunnel endpoint. The following table lists features that do not *directly* impact an Open vSwitch user, e.g. because their absence can be hidden by the ofproto layer (usually this comes with a performance penalty). Feature | Linux upstream | Linux OVS tree | Userspace | Hyper-V | ----------------------|:--------------:|:--------------:|:---------:|:-------:| SCTP flows | 3.12 | YES | YES | YES | MPLS | 3.19 | YES | YES | NO | UFID | 4.0 | YES | YES | NO | Megaflows | 3.12 | YES | YES | NO | Masked set action | 4.0 | YES | YES | NO | Recirculation | 3.19 | YES | YES | NO | TCP flags matching | 3.13 | YES | YES | NO | Validate flow actions | YES | YES | N/A | NO | Multiple datapaths | YES | YES | YES | NO | Tunnel TSO - STT | N/A | YES | NO | YES | ### Q: I get an error like this when I configure Open vSwitch: configure: error: Linux kernel in is version , but version newer than is not supported (please refer to the FAQ for advice) What should I do? A: You have the following options: - Use the Linux kernel module supplied with the kernel that you are using. (See also the following FAQ.) - If there is a newer released version of Open vSwitch, consider building that one, because it may support the kernel that you are building against. (To find out, consult the table in the previous FAQ.) - The Open vSwitch "master" branch may support the kernel that you are using, so consider building the kernel module from "master". All versions of Open vSwitch userspace are compatible with all versions of the Open vSwitch kernel module, so you do not have to use the kernel module from one source along with the userspace programs from the same source. ### Q: What features are not available in the Open vSwitch kernel datapath that ships as part of the upstream Linux kernel? A: The kernel module in upstream Linux does not include support for LISP. Work is in progress to add support for LISP to the upstream Linux version of the Open vSwitch kernel module. For now, if you need this feature, use the kernel module from the Open vSwitch distribution instead of the upstream Linux kernel module. Certain features require kernel support to function or to have reasonable performance. If the ovs-vswitchd log file indicates that a feature is not supported, consider upgrading to a newer upstream Linux release or using the kernel module paired with the userspace distribution. ### Q: Why do tunnels not work when using a kernel module other than the one packaged with Open vSwitch? A: Support for tunnels was added to the upstream Linux kernel module after the rest of Open vSwitch. As a result, some kernels may contain support for Open vSwitch but not tunnels. The minimum kernel version that supports each tunnel protocol is: | Protocol | Linux Kernel |:--------:|:-------------: | GRE | 3.11 | VXLAN | 3.12 | Geneve | 3.18 | LISP | | STT | If you are using a version of the kernel that is older than the one listed above, it is still possible to use that tunnel protocol. However, you must compile and install the kernel module included with the Open vSwitch distribution rather than the one on your machine. If problems persist after doing this, check to make sure that the module that is loaded is the one you expect. ### Q: Why are UDP tunnel checksums not computed for VXLAN or Geneve? A: Generating outer UDP checksums requires kernel support that was not part of the initial implementation of these protocols. If using the upstream Linux Open vSwitch module, you must use kernel 4.0 or newer. The out-of-tree modules from Open vSwitch release 2.4 and later support UDP checksums. ### Q: What features are not available when using the userspace datapath? A: Tunnel virtual ports are not supported, as described in the previous answer. It is also not possible to use queue-related actions. On Linux kernels before 2.6.39, maximum-sized VLAN packets may not be transmitted. ### Q: What Linux kernel versions does IPFIX flow monitoring work with? A: IPFIX flow monitoring requires the Linux kernel module from Linux 3.10 or later, or the out-of-tree module from Open vSwitch version 1.10.90 or later. ### Q: Should userspace or kernel be upgraded first to minimize downtime? In general, the Open vSwitch userspace should be used with the kernel version included in the same release or with the version from upstream Linux. However, when upgrading between two releases of Open vSwitch it is best to migrate userspace first to reduce the possibility of incompatibilities. ### Q: What happened to the bridge compatibility feature? A: Bridge compatibility was a feature of Open vSwitch 1.9 and earlier. When it was enabled, Open vSwitch imitated the interface of the Linux kernel "bridge" module. This allowed users to drop Open vSwitch into environments designed to use the Linux kernel bridge module without adapting the environment to use Open vSwitch. Open vSwitch 1.10 and later do not support bridge compatibility. The feature was dropped because version 1.10 adopted a new internal architecture that made bridge compatibility difficult to maintain. Now that many environments use OVS directly, it would be rarely useful in any case. To use bridge compatibility, install OVS 1.9 or earlier, including the accompanying kernel modules (both the main and bridge compatibility modules), following the instructions that come with the release. Be sure to start the ovs-brcompatd daemon. Terminology ----------- ### Q: I thought Open vSwitch was a virtual Ethernet switch, but the documentation keeps talking about bridges. What's a bridge? A: In networking, the terms "bridge" and "switch" are synonyms. Open vSwitch implements an Ethernet switch, which means that it is also an Ethernet bridge. ### Q: What's a VLAN? A: See the "VLAN" section below. Basic Configuration ------------------- ### Q: How do I configure a port as an access port? A: Add "tag=VLAN" to your "ovs-vsctl add-port" command. For example, the following commands configure br0 with eth0 as a trunk port (the default) and tap0 as an access port for VLAN 9: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 tag=9 If you want to configure an already added port as an access port, use "ovs-vsctl set", e.g.: ovs-vsctl set port tap0 tag=9 ### Q: How do I configure a port as a SPAN port, that is, enable mirroring of all traffic to that port? A: The following commands configure br0 with eth0 and tap0 as trunk ports. All traffic coming in or going out on eth0 or tap0 is also mirrored to tap1; any traffic arriving on tap1 is dropped: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 ovs-vsctl add-port br0 tap1 \ -- --id=@p get port tap1 \ -- --id=@m create mirror name=m0 select-all=true output-port=@p \ -- set bridge br0 mirrors=@m To later disable mirroring, run: ovs-vsctl clear bridge br0 mirrors ### Q: Does Open vSwitch support configuring a port in promiscuous mode? A: Yes. How you configure it depends on what you mean by "promiscuous mode": - Conventionally, "promiscuous mode" is a feature of a network interface card. Ordinarily, a NIC passes to the CPU only the packets actually destined to its host machine. It discards the rest to avoid wasting memory and CPU cycles. When promiscuous mode is enabled, however, it passes every packet to the CPU. On an old-style shared-media or hub-based network, this allows the host to spy on all packets on the network. But in the switched networks that are almost everywhere these days, promiscuous mode doesn't have much effect, because few packets not destined to a host are delivered to the host's NIC. This form of promiscuous mode is configured in the guest OS of the VMs on your bridge, e.g. with "ifconfig". - The VMware vSwitch uses a different definition of "promiscuous mode". When you configure promiscuous mode on a VMware vNIC, the vSwitch sends a copy of every packet received by the vSwitch to that vNIC. That has a much bigger effect than just enabling promiscuous mode in a guest OS. Rather than getting a few stray packets for which the switch does not yet know the correct destination, the vNIC gets every packet. The effect is similar to replacing the vSwitch by a virtual hub. This "promiscuous mode" is what switches normally call "port mirroring" or "SPAN". For information on how to configure SPAN, see "How do I configure a port as a SPAN port, that is, enable mirroring of all traffic to that port?" ### Q: How do I configure a DPDK port as an access port? A: Firstly, you must have a DPDK-enabled version of Open vSwitch. If your version is DPDK-enabled it will support the --dpdk argument on the command line and will display lines with "EAL:..." during startup when --dpdk is supplied. Secondly, when adding a DPDK port, unlike a system port, the type for the interface must be specified. For example; ovs-vsctl add-br br0 ovs-vsctl add-port br0 dpdk0 -- set Interface dpdk0 type=dpdk Finally, it is required that DPDK port names begin with 'dpdk'. See [INSTALL.DPDK.md] for more information on enabling and using DPDK with Open vSwitch. ### Q: How do I configure a VLAN as an RSPAN VLAN, that is, enable mirroring of all traffic to that VLAN? A: The following commands configure br0 with eth0 as a trunk port and tap0 as an access port for VLAN 10. All traffic coming in or going out on tap0, as well as traffic coming in or going out on eth0 in VLAN 10, is also mirrored to VLAN 15 on eth0. The original tag for VLAN 10, in cases where one is present, is dropped as part of mirroring: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 tag=10 ovs-vsctl \ -- --id=@m create mirror name=m0 select-all=true select-vlan=10 \ output-vlan=15 \ -- set bridge br0 mirrors=@m To later disable mirroring, run: ovs-vsctl clear bridge br0 mirrors Mirroring to a VLAN can disrupt a network that contains unmanaged switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred. ### Q: Can I mirror more than one input VLAN to an RSPAN VLAN? A: Yes, but mirroring to a VLAN strips the original VLAN tag in favor of the specified output-vlan. This loss of information may make the mirrored traffic too hard to interpret. To mirror multiple VLANs, use the commands above, but specify a comma-separated list of VLANs as the value for select-vlan. To mirror every VLAN, use the commands above, but omit select-vlan and its value entirely. When a packet arrives on a VLAN that is used as a mirror output VLAN, the mirror is disregarded. Instead, in standalone mode, OVS floods the packet across all the ports for which the mirror output VLAN is configured. (If an OpenFlow controller is in use, then it can override this behavior through the flow table.) If OVS is used as an intermediate switch, rather than an edge switch, this ensures that the RSPAN traffic is distributed through the network. Mirroring to a VLAN can disrupt a network that contains unmanaged switches. See ovs-vswitchd.conf.db(5) for details. Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred. ### Q: How do I configure mirroring of all traffic to a GRE tunnel? A: The following commands configure br0 with eth0 and tap0 as trunk ports. All traffic coming in or going out on eth0 or tap0 is also mirrored to gre0, a GRE tunnel to the remote host 192.168.1.10; any traffic arriving on gre0 is dropped: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 ovs-vsctl add-port br0 gre0 \ -- set interface gre0 type=gre options:remote_ip=192.168.1.10 \ -- --id=@p get port gre0 \ -- --id=@m create mirror name=m0 select-all=true output-port=@p \ -- set bridge br0 mirrors=@m To later disable mirroring and destroy the GRE tunnel: ovs-vsctl clear bridge br0 mirrors ovs-vsctl del-port br0 gre0 ### Q: Does Open vSwitch support ERSPAN? A: No. ERSPAN is an undocumented proprietary protocol. As an alternative, Open vSwitch supports mirroring to a GRE tunnel (see above). ### Q: How do I connect two bridges? A: First, why do you want to do this? Two connected bridges are not much different from a single bridge, so you might as well just have a single bridge with all your ports on it. If you still want to connect two bridges, you can use a pair of patch ports. The following example creates bridges br0 and br1, adds eth0 and tap0 to br0, adds tap1 to br1, and then connects br0 and br1 with a pair of patch ports. ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 ovs-vsctl add-br br1 ovs-vsctl add-port br1 tap1 ovs-vsctl \ -- add-port br0 patch0 \ -- set interface patch0 type=patch options:peer=patch1 \ -- add-port br1 patch1 \ -- set interface patch1 type=patch options:peer=patch0 Bridges connected with patch ports are much like a single bridge. For instance, if the example above also added eth1 to br1, and both eth0 and eth1 happened to be connected to the same next-hop switch, then you could loop your network just as you would if you added eth0 and eth1 to the same bridge (see the "Configuration Problems" section below for more information). If you are using Open vSwitch 1.9 or an earlier version, then you need to be using the kernel module bundled with Open vSwitch rather than the one that is integrated into Linux 3.3 and later, because Open vSwitch 1.9 and earlier versions need kernel support for patch ports. This also means that in Open vSwitch 1.9 and earlier, patch ports will not work with the userspace datapath, only with the kernel module. ### Q: How do I configure a bridge without an OpenFlow local port? (Local port in the sense of OFPP_LOCAL) A: Open vSwitch does not support such a configuration. Bridges always have their local ports. Implementation Details ---------------------- ### Q: I hear OVS has a couple of kinds of flows. Can you tell me about them? A: Open vSwitch uses different kinds of flows for different purposes: - OpenFlow flows are the most important kind of flow. OpenFlow controllers use these flows to define a switch's policy. OpenFlow flows support wildcards, priorities, and multiple tables. When in-band control is in use, Open vSwitch sets up a few "hidden" flows, with priority higher than a controller or the user can configure, that are not visible via OpenFlow. (See the "Controller" section of the FAQ for more information about hidden flows.) - The Open vSwitch software switch implementation uses a second kind of flow internally. These flows, called "datapath" or "kernel" flows, do not support priorities and comprise only a single table, which makes them suitable for caching. (Like OpenFlow flows, datapath flows do support wildcarding, in Open vSwitch 1.11 and later.) OpenFlow flows and datapath flows also support different actions and number ports differently. Datapath flows are an implementation detail that is subject to change in future versions of Open vSwitch. Even with the current version of Open vSwitch, hardware switch implementations do not necessarily use this architecture. Users and controllers directly control only the OpenFlow flow table. Open vSwitch manages the datapath flow table itself, so users should not normally be concerned with it. ### Q: Why are there so many different ways to dump flows? A: Open vSwitch has two kinds of flows (see the previous question), so it has commands with different purposes for dumping each kind of flow: - `ovs-ofctl dump-flows
    ` dumps OpenFlow flows, excluding hidden flows. This is the most commonly useful form of flow dump. (Unlike the other commands, this should work with any OpenFlow switch, not just Open vSwitch.) - `ovs-appctl bridge/dump-flows
    ` dumps OpenFlow flows, including hidden flows. This is occasionally useful for troubleshooting suspected issues with in-band control. - `ovs-dpctl dump-flows [dp]` dumps the datapath flow table entries for a Linux kernel-based datapath. In Open vSwitch 1.10 and later, ovs-vswitchd merges multiple switches into a single datapath, so it will show all the flows on all your kernel-based switches. This command can occasionally be useful for debugging. - `ovs-appctl dpif/dump-flows
    `, new in Open vSwitch 1.10, dumps datapath flows for only the specified bridge, regardless of the type. ### Q: How does multicast snooping works with VLANs? A: Open vSwitch maintains snooping tables for each VLAN. ### Q: Can OVS populate the kernel flow table in advance instead of in reaction to packets? A: No. There are several reasons: - Kernel flows are not as sophisticated as OpenFlow flows, which means that some OpenFlow policies could require a large number of kernel flows. The "conjunctive match" feature is an extreme example: the number of kernel flows it requires is the product of the number of flows in each dimension. - With multiple OpenFlow flow tables and simple sets of actions, the number of kernel flows required can be as large as the product of the number of flows in each dimension. With more sophisticated actions, the number of kernel flows could be even larger. - Open vSwitch is designed so that any version of OVS userspace interoperates with any version of the OVS kernel module. This forward and backward compatibility requires that userspace observe how the kernel module parses received packets. This is only possible in a straightforward way when userspace adds kernel flows in reaction to received packets. For more relevant information on the architecture of Open vSwitch, please read "The Design and Implementation of Open vSwitch", published in USENIX NSDI 2015. Performance ----------- ### Q: I just upgraded and I see a performance drop. Why? A: The OVS kernel datapath may have been updated to a newer version than the OVS userspace components. Sometimes new versions of OVS kernel module add functionality that is backwards compatible with older userspace components but may cause a drop in performance with them. Especially, if a kernel module from OVS 2.1 or newer is paired with OVS userspace 1.10 or older, there will be a performance drop for TCP traffic. Updating the OVS userspace components to the latest released version should fix the performance degradation. To get the best possible performance and functionality, it is recommended to pair the same versions of the kernel module and OVS userspace. Configuration Problems ---------------------- ### Q: I created a bridge and added my Ethernet port to it, using commands like these: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 and as soon as I ran the "add-port" command I lost all connectivity through eth0. Help! A: A physical Ethernet device that is part of an Open vSwitch bridge should not have an IP address. If one does, then that IP address will not be fully functional. You can restore functionality by moving the IP address to an Open vSwitch "internal" device, such as the network device named after the bridge itself. For example, assuming that eth0's IP address is 192.168.128.5, you could run the commands below to fix up the situation: ifconfig eth0 0.0.0.0 ifconfig br0 192.168.128.5 (If your only connection to the machine running OVS is through the IP address in question, then you would want to run all of these commands on a single command line, or put them into a script.) If there were any additional routes assigned to eth0, then you would also want to use commands to adjust these routes to go through br0. If you use DHCP to obtain an IP address, then you should kill the DHCP client that was listening on the physical Ethernet interface (e.g. eth0) and start one listening on the internal interface (e.g. br0). You might still need to manually clear the IP address from the physical interface (e.g. with "ifconfig eth0 0.0.0.0"). There is no compelling reason why Open vSwitch must work this way. However, this is the way that the Linux kernel bridge module has always worked, so it's a model that those accustomed to Linux bridging are already used to. Also, the model that most people expect is not implementable without kernel changes on all the versions of Linux that Open vSwitch supports. By the way, this issue is not specific to physical Ethernet devices. It applies to all network devices except Open vSwitch "internal" devices. ### Q: I created a bridge and added a couple of Ethernet ports to it, ### using commands like these: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 eth1 and now my network seems to have melted: connectivity is unreliable (even connectivity that doesn't go through Open vSwitch), all the LEDs on my physical switches are blinking, wireshark shows duplicated packets, and CPU usage is very high. A: More than likely, you've looped your network. Probably, eth0 and eth1 are connected to the same physical Ethernet switch. This yields a scenario where OVS receives a broadcast packet on eth0 and sends it out on eth1, then the physical switch connected to eth1 sends the packet back on eth0, and so on forever. More complicated scenarios, involving a loop through multiple switches, are possible too. The solution depends on what you are trying to do: - If you added eth0 and eth1 to get higher bandwidth or higher reliability between OVS and your physical Ethernet switch, use a bond. The following commands create br0 and then add eth0 and eth1 as a bond: ovs-vsctl add-br br0 ovs-vsctl add-bond br0 bond0 eth0 eth1 Bonds have tons of configuration options. Please read the documentation on the Port table in ovs-vswitchd.conf.db(5) for all the details. Configuration for DPDK-enabled interfaces is slightly less straightforward: see [INSTALL.DPDK.md]. - Perhaps you don't actually need eth0 and eth1 to be on the same bridge. For example, if you simply want to be able to connect each of them to virtual machines, then you can put each of them on a bridge of its own: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-br br1 ovs-vsctl add-port br1 eth1 and then connect VMs to br0 and br1. (A potential disadvantage is that traffic cannot directly pass between br0 and br1. Instead, it will go out eth0 and come back in eth1, or vice versa.) - If you have a redundant or complex network topology and you want to prevent loops, turn on spanning tree protocol (STP). The following commands create br0, enable STP, and add eth0 and eth1 to the bridge. The order is important because you don't want have to have a loop in your network even transiently: ovs-vsctl add-br br0 ovs-vsctl set bridge br0 stp_enable=true ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 eth1 The Open vSwitch implementation of STP is not well tested. Please report any bugs you observe, but if you'd rather avoid acting as a beta tester then another option might be your best shot. ### Q: I can't seem to use Open vSwitch in a wireless network. A: Wireless base stations generally only allow packets with the source MAC address of NIC that completed the initial handshake. Therefore, without MAC rewriting, only a single device can communicate over a single wireless link. This isn't specific to Open vSwitch, it's enforced by the access point, so the same problems will show up with the Linux bridge or any other way to do bridging. ### Q: I can't seem to add my PPP interface to an Open vSwitch bridge. A: PPP most commonly carries IP packets, but Open vSwitch works only with Ethernet frames. The correct way to interface PPP to an Ethernet network is usually to use routing instead of switching. ### Q: Is there any documentation on the database tables and fields? A: Yes. ovs-vswitchd.conf.db(5) is a comprehensive reference. ### Q: When I run ovs-dpctl I no longer see the bridges I created. Instead, I only see a datapath called "ovs-system". How can I see datapath information about a particular bridge? A: In version 1.9.0, OVS switched to using a single datapath that is shared by all bridges of that type. The "ovs-appctl dpif/*" commands provide similar functionality that is scoped by the bridge. ### Q: I created a GRE port using ovs-vsctl so why can't I send traffic or see the port in the datapath? A: On Linux kernels before 3.11, the OVS GRE module and Linux GRE module cannot be loaded at the same time. It is likely that on your system the Linux GRE module is already loaded and blocking OVS (to confirm, check dmesg for errors regarding GRE registration). To fix this, unload all GRE modules that appear in lsmod as well as the OVS kernel module. You can then reload the OVS module following the directions in [INSTALL.md], which will ensure that dependencies are satisfied. ### Q: Open vSwitch does not seem to obey my packet filter rules. A: It depends on mechanisms and configurations you want to use. You cannot usefully use typical packet filters, like iptables, on physical Ethernet ports that you add to an Open vSwitch bridge. This is because Open vSwitch captures packets from the interface at a layer lower below where typical packet-filter implementations install their hooks. (This actually applies to any interface of type "system" that you might add to an Open vSwitch bridge.) You can usefully use typical packet filters on Open vSwitch internal ports as they are mostly ordinary interfaces from the point of view of packet filters. For example, suppose you create a bridge br0 and add Ethernet port eth0 to it. Then you can usefully add iptables rules to affect the internal interface br0, but not the physical interface eth0. (br0 is also where you would add an IP address, as discussed elsewhere in the FAQ.) For simple filtering rules, it might be possible to achieve similar results by installing appropriate OpenFlow flows instead. If the use of a particular packet filter setup is essential, Open vSwitch might not be the best choice for you. On Linux, you might want to consider using the Linux Bridge. (This is the only choice if you want to use ebtables rules.) On NetBSD, you might want to consider using the bridge(4) with BRIDGE_IPF option. ### Q: It seems that Open vSwitch does nothing when I removed a port and then immediately put it back. For example, consider that p1 is a port of type=internal: ovs-vsctl del-port br0 p1 -- \ add-port br0 p1 -- \ set interface p1 type=internal A: It's an expected behaviour. If del-port and add-port happen in a single OVSDB transaction as your example, Open vSwitch always "skips" the intermediate steps. Even if they are done in multiple transactions, it's still allowed for Open vSwitch to skip the intermediate steps and just implement the overall effect. In both cases, your example would be turned into a no-op. If you want to make Open vSwitch actually destroy and then re-create the port for some side effects like resetting kernel setting for the corresponding interface, you need to separate operations into multiple OVSDB transactions and ensure that at least the first one does not have --no-wait. In the following example, the first ovs-vsctl will block until Open vSwitch reloads the new configuration and removes the port: ovs-vsctl del-port br0 p1 ovs-vsctl add-port br0 p1 -- \ set interface p1 type=internal ### Q: I want to add thousands of ports to an Open vSwitch bridge, but it takes too long (minutes or hours) to do it with ovs-vsctl. How can I do it faster? A: If you add them one at a time with ovs-vsctl, it can take a long time to add thousands of ports to an Open vSwitch bridge. This is because every invocation of ovs-vsctl first reads the current configuration from OVSDB. As the number of ports grows, this starts to take an appreciable amount of time, and when it is repeated thousands of times the total time becomes significant. The solution is to add the ports in one invocation of ovs-vsctl (or a small number of them). For example, using bash: ovs-vsctl add-br br0 cmds=; for i in {1..5000}; do cmds+=" -- add-port br0 p$i"; done ovs-vsctl $cmds takes seconds, not minutes or hours, in the OVS sandbox environment. ### Q: I created a bridge named br0. My bridge shows up in "ovs-vsctl show", but "ovs-ofctl show br0" just prints "br0 is not a bridge or a socket". A: Open vSwitch wasn't able to create the bridge. Check the ovs-vswitchd log for details (Debian and Red Hat packaging for Open vSwitch put it in /var/log/openvswitch/ovs-vswitchd.log). In general, the Open vSwitch database reflects the desired configuration state. ovs-vswitchd monitors the database and, when it changes, reconfigures the system to reflect the new desired state. This normally happens very quickly. Thus, a discrepancy between the database and the actual state indicates that ovs-vswitchd could not implement the configuration, and so one should check the log to find out why. (Another possible cause is that ovs-vswitchd is not running. This will make "ovs-vsctl" commands hang, if they change the configuration, unless one specifies "--no-wait".) ### Q: I have a bridge br0. I added a new port vif1.0, and it shows up in "ovs-vsctl show", but "ovs-vsctl list port" says that it has OpenFlow port ("ofport") -1, and "ovs-ofctl show br0" doesn't show vif1.0 at all. A: Open vSwitch wasn't able to create the port. Check the ovs-vswitchd log for details (Debian and Red Hat packaging for Open vSwitch put it in /var/log/openvswitch/ovs-vswitchd.log). Please see the previous question for more information. You may want to upgrade to Open vSwitch 2.3 (or later), in which ovs-vsctl will immediately report when there is an issue creating a port. ### Q: I created a tap device tap0, configured an IP address on it, and added it to a bridge, like this: tunctl -t tap0 ifconfig tap0 192.168.0.123 ovs-vsctl add-br br0 ovs-vsctl add-port br0 tap0 I expected that I could then use this IP address to contact other hosts on the network, but it doesn't work. Why not? A: The short answer is that this is a misuse of a "tap" device. Use an "internal" device implemented by Open vSwitch, which works differently and is designed for this use. To solve this problem with an internal device, instead run: ovs-vsctl add-br br0 ovs-vsctl add-port br0 int0 -- set Interface int0 type=internal ifconfig int0 192.168.0.123 Even more simply, you can take advantage of the internal port that every bridge has under the name of the bridge: ovs-vsctl add-br br0 ifconfig br0 192.168.0.123 In more detail, a "tap" device is an interface between the Linux (or *BSD) network stack and a user program that opens it as a socket. When the "tap" device transmits a packet, it appears in the socket opened by the userspace program. Conversely, when the userspace program writes to the "tap" socket, the kernel TCP/IP stack processes the packet as if it had been received by the "tap" device. Consider the configuration above. Given this configuration, if you "ping" an IP address in the 192.168.0.x subnet, the Linux kernel routing stack will transmit an ARP on the tap0 device. Open vSwitch userspace treats "tap" devices just like any other network device; that is, it doesn't open them as "tap" sockets. That means that the ARP packet will simply get dropped. You might wonder why the Open vSwitch kernel module doesn't intercept the ARP packet and bridge it. After all, Open vSwitch intercepts packets on other devices. The answer is that Open vSwitch only intercepts *received* packets, but this is a packet being transmitted. The same thing happens for all other types of network devices, except for Open vSwitch "internal" ports. If you, for example, add a physical Ethernet port to an OVS bridge, configure an IP address on a physical Ethernet port, and then issue a "ping" to an address in that subnet, the same thing happens: an ARP gets transmitted on the physical Ethernet port and Open vSwitch never sees it. (You should not do that, as documented at the beginning of this section.) It can make sense to add a "tap" device to an Open vSwitch bridge, if some userspace program (other than Open vSwitch) has opened the tap socket. This is the case, for example, if the "tap" device was created by KVM (or QEMU) to simulate a virtual NIC. In such a case, when OVS bridges a packet to the "tap" device, the kernel forwards that packet to KVM in userspace, which passes it along to the VM, and in the other direction, when the VM sends a packet, KVM writes it to the "tap" socket, which causes OVS to receive it and bridge it to the other OVS ports. Please note that in such a case no IP address is configured on the "tap" device (there is normally an IP address configured in the virtual NIC inside the VM, but this is not visible to the host Linux kernel or to Open vSwitch). There is one special case in which Open vSwitch does directly read and write "tap" sockets. This is an implementation detail of the Open vSwitch userspace switch, which implements its "internal" ports as Linux (or *BSD) "tap" sockets. In such a userspace switch, OVS receives packets sent on the "tap" device used to implement an "internal" port by reading the associated "tap" socket, and bridges them to the rest of the switch. In the other direction, OVS transmits packets bridged to the "internal" port by writing them to the "tap" socket, causing them to be processed by the kernel TCP/IP stack as if they had been received on the "tap" device. Users should not need to be concerned with this implementation detail. Open vSwitch has a network device type called "tap". This is intended only for implementing "internal" ports in the OVS userspace switch and should not be used otherwise. In particular, users should not configure KVM "tap" devices as type "tap" (use type "system", the default, instead). Quality of Service (QoS) ------------------------ ### Q: Does OVS support Quality of Service (QoS)? A: Yes. For traffic that egresses from a switch, OVS supports traffic shaping; for traffic that ingresses into a switch, OVS support policing. Policing is a simple form of quality-of-service that simply drops packets received in excess of the configured rate. Due to its simplicity, policing is usually less accurate and less effective than egress traffic shaping, which queues packets. Keep in mind that ingress and egress are from the perspective of the switch. That means that egress shaping limits the rate at which traffic is allowed to transmit from a physical interface, but the rate at which traffic will be received on a virtual machine's VIF. For ingress policing, the behavior is the opposite. ### Q: How do I configure egress traffic shaping? A: Suppose that you want to set up bridge br0 connected to physical Ethernet port eth0 (a 1 Gbps device) and virtual machine interfaces vif1.0 and vif2.0, and that you want to limit traffic from vif1.0 to eth0 to 10 Mbps and from vif2.0 to eth0 to 20 Mbps. Then, you could configure the bridge this way: ovs-vsctl -- \ add-br br0 -- \ add-port br0 eth0 -- \ add-port br0 vif1.0 -- set interface vif1.0 ofport_request=5 -- \ add-port br0 vif2.0 -- set interface vif2.0 ofport_request=6 -- \ set port eth0 qos=@newqos -- \ --id=@newqos create qos type=linux-htb \ other-config:max-rate=1000000000 \ queues:123=@vif10queue \ queues:234=@vif20queue -- \ --id=@vif10queue create queue other-config:max-rate=10000000 -- \ --id=@vif20queue create queue other-config:max-rate=20000000 At this point, bridge br0 is configured with the ports and eth0 is configured with the queues that you need for QoS, but nothing is actually directing packets from vif1.0 or vif2.0 to the queues that we have set up for them. That means that all of the packets to eth0 are going to the "default queue", which is not what we want. We use OpenFlow to direct packets from vif1.0 and vif2.0 to the queues reserved for them: ovs-ofctl add-flow br0 in_port=5,actions=set_queue:123,normal ovs-ofctl add-flow br0 in_port=6,actions=set_queue:234,normal Each of the above flows matches on the input port, sets up the appropriate queue (123 for vif1.0, 234 for vif2.0), and then executes the "normal" action, which performs the same switching that Open vSwitch would have done without any OpenFlow flows being present. (We know that vif1.0 and vif2.0 have OpenFlow port numbers 5 and 6, respectively, because we set their ofport_request columns above. If we had not done that, then we would have needed to find out their port numbers before setting up these flows.) Now traffic going from vif1.0 or vif2.0 to eth0 should be rate-limited. By the way, if you delete the bridge created by the above commands, with: ovs-vsctl del-br br0 then that will leave one unreferenced QoS record and two unreferenced Queue records in the Open vSwich database. One way to clear them out, assuming you don't have other QoS or Queue records that you want to keep, is: ovs-vsctl -- --all destroy QoS -- --all destroy Queue If you do want to keep some QoS or Queue records, or the Open vSwitch you are using is older than version 1.8 (which added the --all option), then you will have to destroy QoS and Queue records individually. ### Q: How do I configure ingress policing? A: A policing policy can be configured on an interface to drop packets that arrive at a higher rate than the configured value. For example, the following commands will rate-limit traffic that vif1.0 may generate to 10Mbps: ovs-vsctl set interface vif1.0 ingress_policing_rate=10000 ovs-vsctl set interface vif1.0 ingress_policing_burst=8000 Traffic policing can interact poorly with some network protocols and can have surprising results. The "Ingress Policing" section of ovs-vswitchd.conf.db(5) discusses the issues in greater detail. ### Q: I configured Quality of Service (QoS) in my OpenFlow network by adding records to the QoS and Queue table, but the results aren't what I expect. A: Did you install OpenFlow flows that use your queues? This is the primary way to tell Open vSwitch which queues you want to use. If you don't do this, then the default queue will be used, which will probably not have the effect you want. Refer to the previous question for an example. ### Q: I'd like to take advantage of some QoS feature that Open vSwitch doesn't yet support. How do I do that? A: Open vSwitch does not implement QoS itself. Instead, it can configure some, but not all, of the QoS features built into the Linux kernel. If you need some QoS feature that OVS cannot configure itself, then the first step is to figure out whether Linux QoS supports that feature. If it does, then you can submit a patch to support Open vSwitch configuration for that feature, or you can use "tc" directly to configure the feature in Linux. (If Linux QoS doesn't support the feature you want, then first you have to add that support to Linux.) ### Q: I configured QoS, correctly, but my measurements show that it isn't working as well as I expect. A: With the Linux kernel, the Open vSwitch implementation of QoS has two aspects: - Open vSwitch configures a subset of Linux kernel QoS features, according to what is in OVSDB. It is possible that this code has bugs. If you believe that this is so, then you can configure the Linux traffic control (QoS) stack directly with the "tc" program. If you get better results that way, you can send a detailed bug report to bugs@openvswitch.org. It is certain that Open vSwitch cannot configure every Linux kernel QoS feature. If you need some feature that OVS cannot configure, then you can also use "tc" directly (or add that feature to OVS). - The Open vSwitch implementation of OpenFlow allows flows to be directed to particular queues. This is pretty simple and unlikely to have serious bugs at this point. However, most problems with QoS on Linux are not bugs in Open vSwitch at all. They tend to be either configuration errors (please see the earlier questions in this section) or issues with the traffic control (QoS) stack in Linux. The Open vSwitch developers are not experts on Linux traffic control. We suggest that, if you believe you are encountering a problem with Linux traffic control, that you consult the tc manpages (e.g. tc(8), tc-htb(8), tc-hfsc(8)), web resources (e.g. http://lartc.org/), or mailing lists (e.g. http://vger.kernel.org/vger-lists.html#netdev). ### Q: Does Open vSwitch support OpenFlow meters? A: Since version 2.0, Open vSwitch has OpenFlow protocol support for OpenFlow meters. There is no implementation of meters in the Open vSwitch software switch (neither the kernel-based nor userspace switches). VLANs ----- ### Q: What's a VLAN? A: At the simplest level, a VLAN (short for "virtual LAN") is a way to partition a single switch into multiple switches. Suppose, for example, that you have two groups of machines, group A and group B. You want the machines in group A to be able to talk to each other, and you want the machine in group B to be able to talk to each other, but you don't want the machines in group A to be able to talk to the machines in group B. You can do this with two switches, by plugging the machines in group A into one switch and the machines in group B into the other switch. If you only have one switch, then you can use VLANs to do the same thing, by configuring the ports for machines in group A as VLAN "access ports" for one VLAN and the ports for group B as "access ports" for a different VLAN. The switch will only forward packets between ports that are assigned to the same VLAN, so this effectively subdivides your single switch into two independent switches, one for each group of machines. So far we haven't said anything about VLAN headers. With access ports, like we've described so far, no VLAN header is present in the Ethernet frame. This means that the machines (or switches) connected to access ports need not be aware that VLANs are involved, just like in the case where we use two different physical switches. Now suppose that you have a whole bunch of switches in your network, instead of just one, and that some machines in group A are connected directly to both switches 1 and 2. To allow these machines to talk to each other, you could add an access port for group A's VLAN to switch 1 and another to switch 2, and then connect an Ethernet cable between those ports. That works fine, but it doesn't scale well as the number of switches and the number of VLANs increases, because you use up a lot of valuable switch ports just connecting together your VLANs. This is where VLAN headers come in. Instead of using one cable and two ports per VLAN to connect a pair of switches, we configure a port on each switch as a VLAN "trunk port". Packets sent and received on a trunk port carry a VLAN header that says what VLAN the packet belongs to, so that only two ports total are required to connect the switches, regardless of the number of VLANs in use. Normally, only switches (either physical or virtual) are connected to a trunk port, not individual hosts, because individual hosts don't expect to see a VLAN header in the traffic that they receive. None of the above discussion says anything about particular VLAN numbers. This is because VLAN numbers are completely arbitrary. One must only ensure that a given VLAN is numbered consistently throughout a network and that different VLANs are given different numbers. (That said, VLAN 0 is usually synonymous with a packet that has no VLAN header, and VLAN 4095 is reserved.) ### Q: VLANs don't work. A: Many drivers in Linux kernels before version 3.3 had VLAN-related bugs. If you are having problems with VLANs that you suspect to be driver related, then you have several options: - Upgrade to Linux 3.3 or later. - Build and install a fixed version of the particular driver that is causing trouble, if one is available. - Use a NIC whose driver does not have VLAN problems. - Use "VLAN splinters", a feature in Open vSwitch 1.4 and later that works around bugs in kernel drivers. To enable VLAN splinters on interface eth0, use the command: ovs-vsctl set interface eth0 other-config:enable-vlan-splinters=true For VLAN splinters to be effective, Open vSwitch must know which VLANs are in use. See the "VLAN splinters" section in the Interface table in ovs-vswitchd.conf.db(5) for details on how Open vSwitch infers in-use VLANs. VLAN splinters increase memory use and reduce performance, so use them only if needed. - Apply the "vlan workaround" patch from the XenServer kernel patch queue, build Open vSwitch against this patched kernel, and then use ovs-vlan-bug-workaround(8) to enable the VLAN workaround for each interface whose driver is buggy. (This is a nontrivial exercise, so this option is included only for completeness.) It is not always easy to tell whether a Linux kernel driver has buggy VLAN support. The ovs-vlan-test(8) and ovs-test(8) utilities can help you test. See their manpages for details. Of the two utilities, ovs-test(8) is newer and more thorough, but ovs-vlan-test(8) may be easier to use. ### Q: VLANs still don't work. I've tested the driver so I know that it's OK. A: Do you have VLANs enabled on the physical switch that OVS is attached to? Make sure that the port is configured to trunk the VLAN or VLANs that you are using with OVS. ### Q: Outgoing VLAN-tagged traffic goes through OVS to my physical switch and to its destination host, but OVS seems to drop incoming return traffic. A: It's possible that you have the VLAN configured on your physical switch as the "native" VLAN. In this mode, the switch treats incoming packets either tagged with the native VLAN or untagged as part of the native VLAN. It may also send outgoing packets in the native VLAN without a VLAN tag. If this is the case, you have two choices: - Change the physical switch port configuration to tag packets it forwards to OVS with the native VLAN instead of forwarding them untagged. - Change the OVS configuration for the physical port to a native VLAN mode. For example, the following sets up a bridge with port eth0 in "native-tagged" mode in VLAN 9: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 tag=9 vlan_mode=native-tagged In this situation, "native-untagged" mode will probably work equally well. Refer to the documentation for the Port table in ovs-vswitchd.conf.db(5) for more information. ### Q: I added a pair of VMs on different VLANs, like this: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 tag=9 ovs-vsctl add-port br0 tap1 tag=10 but the VMs can't access each other, the external network, or the Internet. A: It is to be expected that the VMs can't access each other. VLANs are a means to partition a network. When you configured tap0 and tap1 as access ports for different VLANs, you indicated that they should be isolated from each other. As for the external network and the Internet, it seems likely that the machines you are trying to access are not on VLAN 9 (or 10) and that the Internet is not available on VLAN 9 (or 10). ### Q: I added a pair of VMs on the same VLAN, like this: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 tag=9 ovs-vsctl add-port br0 tap1 tag=9 The VMs can access each other, but not the external network or the Internet. A: It seems likely that the machines you are trying to access in the external network are not on VLAN 9 and that the Internet is not available on VLAN 9. Also, ensure VLAN 9 is set up as an allowed trunk VLAN on the upstream switch port to which eth0 is connected. ### Q: Can I configure an IP address on a VLAN? A: Yes. Use an "internal port" configured as an access port. For example, the following configures IP address 192.168.0.7 on VLAN 9. That is, OVS will forward packets from eth0 to 192.168.0.7 only if they have an 802.1Q header with VLAN 9. Conversely, traffic forwarded from 192.168.0.7 to eth0 will be tagged with an 802.1Q header with VLAN 9: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal ifconfig vlan9 192.168.0.7 See also the following question. ### Q: I configured one IP address on VLAN 0 and another on VLAN 9, like this: ovs-vsctl add-br br0 ovs-vsctl add-port br0 eth0 ifconfig br0 192.168.0.5 ovs-vsctl add-port br0 vlan9 tag=9 -- set interface vlan9 type=internal ifconfig vlan9 192.168.0.9 but other hosts that are only on VLAN 0 can reach the IP address configured on VLAN 9. What's going on? A: RFC 1122 section 3.3.4.2 "Multihoming Requirements" describes two approaches to IP address handling in Internet hosts: - In the "Strong ES Model", where an ES is a host ("End System"), an IP address is primarily associated with a particular interface. The host discards packets that arrive on interface A if they are destined for an IP address that is configured on interface B. The host never sends packets from interface A using a source address configured on interface B. - In the "Weak ES Model", an IP address is primarily associated with a host. The host accepts packets that arrive on any interface if they are destined for any of the host's IP addresses, even if the address is configured on some interface other than the one on which it arrived. The host does not restrict itself to sending packets from an IP address associated with the originating interface. Linux uses the weak ES model. That means that when packets destined to the VLAN 9 IP address arrive on eth0 and are bridged to br0, the kernel IP stack accepts them there for the VLAN 9 IP address, even though they were not received on vlan9, the network device for vlan9. To simulate the strong ES model on Linux, one may add iptables rule to filter packets based on source and destination address and adjust ARP configuration with sysctls. BSD uses the strong ES model. ### Q: My OpenFlow controller doesn't see the VLANs that I expect. A: The configuration for VLANs in the Open vSwitch database (e.g. via ovs-vsctl) only affects traffic that goes through Open vSwitch's implementation of the OpenFlow "normal switching" action. By default, when Open vSwitch isn't connected to a controller and nothing has been manually configured in the flow table, all traffic goes through the "normal switching" action. But, if you set up OpenFlow flows on your own, through a controller or using ovs-ofctl or through other means, then you have to implement VLAN handling yourself. You can use "normal switching" as a component of your OpenFlow actions, e.g. by putting "normal" into the lists of actions on ovs-ofctl or by outputting to OFPP_NORMAL from an OpenFlow controller. In situations where this is not suitable, you can implement VLAN handling yourself, e.g.: - If a packet comes in on an access port, and the flow table needs to send it out on a trunk port, then the flow can add the appropriate VLAN tag with the "mod_vlan_vid" action. - If a packet comes in on a trunk port, and the flow table needs to send it out on an access port, then the flow can strip the VLAN tag with the "strip_vlan" action. ### Q: I configured ports on a bridge as access ports with different VLAN tags, like this: ovs-vsctl add-br br0 ovs-vsctl set-controller br0 tcp:192.168.0.10:6653 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 tap0 tag=9 ovs-vsctl add-port br0 tap1 tag=10 but the VMs running behind tap0 and tap1 can still communicate, that is, they are not isolated from each other even though they are on different VLANs. A: Do you have a controller configured on br0 (as the commands above do)? If so, then this is a variant on the previous question, "My OpenFlow controller doesn't see the VLANs that I expect," and you can refer to the answer there for more information. ### Q: How MAC learning works with VLANs? A: Open vSwitch implements Independent VLAN Learning (IVL) for OFPP_NORMAL action. I.e. it logically has separate learning tables for each VLANs. VXLANs ----- ### Q: What's a VXLAN? A: VXLAN stands for Virtual eXtensible Local Area Network, and is a means to solve the scaling challenges of VLAN networks in a multi-tenant environment. VXLAN is an overlay network which transports an L2 network over an existing L3 network. For more information on VXLAN, please see RFC 7348: http://tools.ietf.org/html/rfc7348 ### Q: How much of the VXLAN protocol does Open vSwitch currently support? A: Open vSwitch currently supports the framing format for packets on the wire. There is currently no support for the multicast aspects of VXLAN. To get around the lack of multicast support, it is possible to pre-provision MAC to IP address mappings either manually or from a controller. ### Q: What destination UDP port does the VXLAN implementation in Open vSwitch use? A: By default, Open vSwitch will use the assigned IANA port for VXLAN, which is 4789. However, it is possible to configure the destination UDP port manually on a per-VXLAN tunnel basis. An example of this configuration is provided below. ovs-vsctl add-br br0 ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=192.168.1.2 options:key=flow options:dst_port=8472 Using OpenFlow (Manually or Via Controller) ------------------------------------------- ### Q: What versions of OpenFlow does Open vSwitch support? A: The following table lists the versions of OpenFlow supported by each version of Open vSwitch: Open vSwitch OF1.0 OF1.1 OF1.2 OF1.3 OF1.4 OF1.5 ###============ ===== ===== ===== ===== ===== ===== 1.9 and earlier yes --- --- --- --- --- 1.10 yes --- [*] [*] --- --- 1.11 yes --- [*] [*] --- --- 2.0 yes [*] [*] [*] --- --- 2.1 yes [*] [*] [*] --- --- 2.2 yes [*] [*] [*] [%] [*] 2.3 yes yes yes yes [*] [*] [*] Supported, with one or more missing features. [%] Experimental, unsafe implementation. Open vSwitch 2.3 enables OpenFlow 1.0, 1.1, 1.2, and 1.3 by default in ovs-vswitchd. In Open vSwitch 1.10 through 2.2, OpenFlow 1.1, 1.2, and 1.3 must be enabled manually in ovs-vswitchd. OpenFlow 1.4 and 1.5 are also supported, with missing features, in Open vSwitch 2.3 and later, but not enabled by default. In any case, the user may override the default: - To enable OpenFlow 1.0, 1.1, 1.2, and 1.3 on bridge br0: ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13 - To enable OpenFlow 1.0, 1.1, 1.2, 1.3, 1.4, and 1.5 on bridge br0: ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15 - To enable only OpenFlow 1.0 on bridge br0: ovs-vsctl set bridge br0 protocols=OpenFlow10 All current versions of ovs-ofctl enable only OpenFlow 1.0 by default. Use the -O option to enable support for later versions of OpenFlow in ovs-ofctl. For example: ovs-ofctl -O OpenFlow13 dump-flows br0 (Open vSwitch 2.2 had an experimental implementation of OpenFlow 1.4 that could cause crashes. We don't recommend enabling it.) [OPENFLOW-1.1+.md] in the Open vSwitch source tree tracks support for OpenFlow 1.1 and later features. When support for OpenFlow 1.4 and 1.5 is solidly implemented, Open vSwitch will enable those version by default. ### Q: Does Open vSwitch support MPLS? A: Before version 1.11, Open vSwitch did not support MPLS. That is, these versions can match on MPLS Ethernet types, but they cannot match, push, or pop MPLS labels, nor can they look past MPLS labels into the encapsulated packet. Open vSwitch versions 1.11, 2.0, and 2.1 have very minimal support for MPLS. With the userspace datapath only, these versions can match, push, or pop a single MPLS label, but they still cannot look past MPLS labels (even after popping them) into the encapsulated packet. Kernel datapath support is unchanged from earlier versions. Open vSwitch version 2.3 can match, push, or pop a single MPLS label and look past the MPLS label into the encapsulated packet. Both userspace and kernel datapaths will be supported, but MPLS processing always happens in userspace either way, so kernel datapath performance will be disappointing. Open vSwitch version 2.4 can match, push, or pop up to 3 MPLS labels and look past the MPLS label into the encapsulated packet. It will have kernel support for MPLS, yielding improved performance. ### Q: I'm getting "error type 45250 code 0". What's that? A: This is a Open vSwitch extension to OpenFlow error codes. Open vSwitch uses this extension when it must report an error to an OpenFlow controller but no standard OpenFlow error code is suitable. Open vSwitch logs the errors that it sends to controllers, so the easiest thing to do is probably to look at the ovs-vswitchd log to find out what the error was. If you want to dissect the extended error message yourself, the format is documented in include/openflow/nicira-ext.h in the Open vSwitch source distribution. The extended error codes are documented in lib/ofp-errors.h. Q1: Some of the traffic that I'd expect my OpenFlow controller to see doesn't actually appear through the OpenFlow connection, even though I know that it's going through. Q2: Some of the OpenFlow flows that my controller sets up don't seem to apply to certain traffic, especially traffic between OVS and the controller itself. A: By default, Open vSwitch assumes that OpenFlow controllers are connected "in-band", that is, that the controllers are actually part of the network that is being controlled. In in-band mode, Open vSwitch sets up special "hidden" flows to make sure that traffic can make it back and forth between OVS and the controllers. These hidden flows are higher priority than any flows that can be set up through OpenFlow, and they are not visible through normal OpenFlow flow table dumps. Usually, the hidden flows are desirable and helpful, but occasionally they can cause unexpected behavior. You can view the full OpenFlow flow table, including hidden flows, on bridge br0 with the command: ovs-appctl bridge/dump-flows br0 to help you debug. The hidden flows are those with priorities greater than 65535 (the maximum priority that can be set with OpenFlow). The DESIGN file at the top level of the Open vSwitch source distribution describes the in-band model in detail. If your controllers are not actually in-band (e.g. they are on localhost via 127.0.0.1, or on a separate network), then you should configure your controllers in "out-of-band" mode. If you have one controller on bridge br0, then you can configure out-of-band mode on it with: ovs-vsctl set controller br0 connection-mode=out-of-band ### Q: I configured all my controllers for out-of-band control mode but "ovs-appctl bridge/dump-flows" still shows some hidden flows. A: You probably have a remote manager configured (e.g. with "ovs-vsctl set-manager"). By default, Open vSwitch assumes that managers need in-band rules set up on every bridge. You can disable these rules on bridge br0 with: ovs-vsctl set bridge br0 other-config:disable-in-band=true This actually disables in-band control entirely for the bridge, as if all the bridge's controllers were configured for out-of-band control. ### Q: My OpenFlow controller doesn't see the VLANs that I expect. A: See answer under "VLANs", above. ### Q: I ran "ovs-ofctl add-flow br0 nw_dst=192.168.0.1,actions=drop" but I got a funny message like this: ofp_util|INFO|normalization changed ofp_match, details: ofp_util|INFO| pre: nw_dst=192.168.0.1 ofp_util|INFO|post: and when I ran "ovs-ofctl dump-flows br0" I saw that my nw_dst match had disappeared, so that the flow ends up matching every packet. A: The term "normalization" in the log message means that a flow cannot match on an L3 field without saying what L3 protocol is in use. The "ovs-ofctl" command above didn't specify an L3 protocol, so the L3 field match was dropped. In this case, the L3 protocol could be IP or ARP. A correct command for each possibility is, respectively: ovs-ofctl add-flow br0 ip,nw_dst=192.168.0.1,actions=drop and ovs-ofctl add-flow br0 arp,nw_dst=192.168.0.1,actions=drop Similarly, a flow cannot match on an L4 field without saying what L4 protocol is in use. For example, the flow match "tp_src=1234" is, by itself, meaningless and will be ignored. Instead, to match TCP source port 1234, write "tcp,tp_src=1234", or to match UDP source port 1234, write "udp,tp_src=1234". ### Q: How can I figure out the OpenFlow port number for a given port? A: The OFPT_FEATURES_REQUEST message requests an OpenFlow switch to respond with an OFPT_FEATURES_REPLY that, among other information, includes a mapping between OpenFlow port names and numbers. From a command prompt, "ovs-ofctl show br0" makes such a request and prints the response for switch br0. The Interface table in the Open vSwitch database also maps OpenFlow port names to numbers. To print the OpenFlow port number associated with interface eth0, run: ovs-vsctl get Interface eth0 ofport You can print the entire mapping with: ovs-vsctl -- --columns=name,ofport list Interface but the output mixes together interfaces from all bridges in the database, so it may be confusing if more than one bridge exists. In the Open vSwitch database, ofport value -1 means that the interface could not be created due to an error. (The Open vSwitch log should indicate the reason.) ofport value [] (the empty set) means that the interface hasn't been created yet. The latter is normally an intermittent condition (unless ovs-vswitchd is not running). ### Q: I added some flows with my controller or with ovs-ofctl, but when I run "ovs-dpctl dump-flows" I don't see them. A: ovs-dpctl queries a kernel datapath, not an OpenFlow switch. It won't display the information that you want. You want to use "ovs-ofctl dump-flows" instead. ### Q: It looks like each of the interfaces in my bonded port shows up as an individual OpenFlow port. Is that right? A: Yes, Open vSwitch makes individual bond interfaces visible as OpenFlow ports, rather than the bond as a whole. The interfaces are treated together as a bond for only a few purposes: - Sending a packet to the OFPP_NORMAL port. (When an OpenFlow controller is not configured, this happens implicitly to every packet.) - Mirrors configured for output to a bonded port. It would make a lot of sense for Open vSwitch to present a bond as a single OpenFlow port. If you want to contribute an implementation of such a feature, please bring it up on the Open vSwitch development mailing list at dev@openvswitch.org. ### Q: I have a sophisticated network setup involving Open vSwitch, VMs or multiple hosts, and other components. The behavior isn't what I expect. Help! A: To debug network behavior problems, trace the path of a packet, hop-by-hop, from its origin in one host to a remote host. If that's correct, then trace the path of the response packet back to the origin. The open source tool called "plotnetcfg" can help to understand the relationship between the networking devices on a single host. Usually a simple ICMP echo request and reply ("ping") packet is good enough. Start by initiating an ongoing "ping" from the origin host to a remote host. If you are tracking down a connectivity problem, the "ping" will not display any successful output, but packets are still being sent. (In this case the packets being sent are likely ARP rather than ICMP.) Tools available for tracing include the following: - "tcpdump" and "wireshark" for observing hops across network devices, such as Open vSwitch internal devices and physical wires. - "ovs-appctl dpif/dump-flows
    " in Open vSwitch 1.10 and later or "ovs-dpctl dump-flows
    " in earlier versions. These tools allow one to observe the actions being taken on packets in ongoing flows. See ovs-vswitchd(8) for "ovs-appctl dpif/dump-flows" documentation, ovs-dpctl(8) for "ovs-dpctl dump-flows" documentation, and "Why are there so many different ways to dump flows?" above for some background. - "ovs-appctl ofproto/trace" to observe the logic behind how ovs-vswitchd treats packets. See ovs-vswitchd(8) for documentation. You can out more details about a given flow that "ovs-dpctl dump-flows" displays, by cutting and pasting a flow from the output into an "ovs-appctl ofproto/trace" command. - SPAN, RSPAN, and ERSPAN features of physical switches, to observe what goes on at these physical hops. Starting at the origin of a given packet, observe the packet at each hop in turn. For example, in one plausible scenario, you might: 1. "tcpdump" the "eth" interface through which an ARP egresses a VM, from inside the VM. 2. "tcpdump" the "vif" or "tap" interface through which the ARP ingresses the host machine. 3. Use "ovs-dpctl dump-flows" to spot the ARP flow and observe the host interface through which the ARP egresses the physical machine. You may need to use "ovs-dpctl show" to interpret the port numbers. If the output seems surprising, you can use "ovs-appctl ofproto/trace" to observe details of how ovs-vswitchd determined the actions in the "ovs-dpctl dump-flows" output. 4. "tcpdump" the "eth" interface through which the ARP egresses the physical machine. 5. "tcpdump" the "eth" interface through which the ARP ingresses the physical machine, at the remote host that receives the ARP. 6. Use "ovs-dpctl dump-flows" to spot the ARP flow on the remote host that receives the ARP and observe the VM "vif" or "tap" interface to which the flow is directed. Again, "ovs-dpctl show" and "ovs-appctl ofproto/trace" might help. 7. "tcpdump" the "vif" or "tap" interface to which the ARP is directed. 8. "tcpdump" the "eth" interface through which the ARP ingresses a VM, from inside the VM. It is likely that during one of these steps you will figure out the problem. If not, then follow the ARP reply back to the origin, in reverse. ### Q: How do I make a flow drop packets? A: To drop a packet is to receive it without forwarding it. OpenFlow explicitly specifies forwarding actions. Thus, a flow with an empty set of actions does not forward packets anywhere, causing them to be dropped. You can specify an empty set of actions with "actions=" on the ovs-ofctl command line. For example: ovs-ofctl add-flow br0 priority=65535,actions= would cause every packet entering switch br0 to be dropped. You can write "drop" explicitly if you like. The effect is the same. Thus, the following command also causes every packet entering switch br0 to be dropped: ovs-ofctl add-flow br0 priority=65535,actions=drop "drop" is not an action, either in OpenFlow or Open vSwitch. Rather, it is only a way to say that there are no actions. ### Q: I added a flow to send packets out the ingress port, like this: ovs-ofctl add-flow br0 in_port=2,actions=2 but OVS drops the packets instead. A: Yes, OpenFlow requires a switch to ignore attempts to send a packet out its ingress port. The rationale is that dropping these packets makes it harder to loop the network. Sometimes this behavior can even be convenient, e.g. it is often the desired behavior in a flow that forwards a packet to several ports ("floods" the packet). Sometimes one really needs to send a packet out its ingress port ("hairpin"). In this case, output to OFPP_IN_PORT, which in ovs-ofctl syntax is expressed as just "in_port", e.g.: ovs-ofctl add-flow br0 in_port=2,actions=in_port This also works in some circumstances where the flow doesn't match on the input port. For example, if you know that your switch has five ports numbered 2 through 6, then the following will send every received packet out every port, even its ingress port: ovs-ofctl add-flow br0 actions=2,3,4,5,6,in_port or, equivalently: ovs-ofctl add-flow br0 actions=all,in_port Sometimes, in complicated flow tables with multiple levels of "resubmit" actions, a flow needs to output to a particular port that may or may not be the ingress port. It's difficult to take advantage of OFPP_IN_PORT in this situation. To help, Open vSwitch provides, as an OpenFlow extension, the ability to modify the in_port field. Whatever value is currently in the in_port field is the port to which outputs will be dropped, as well as the destination for OFPP_IN_PORT. This means that the following will reliably output to port 2 or to ports 2 through 6, respectively: ovs-ofctl add-flow br0 in_port=2,actions=load:0->NXM_OF_IN_PORT[],2 ovs-ofctl add-flow br0 actions=load:0->NXM_OF_IN_PORT[],2,3,4,5,6 If the input port is important, then one may save and restore it on the stack: ovs-ofctl add-flow br0 actions=push:NXM_OF_IN_PORT[],\ load:0->NXM_OF_IN_PORT[],\ 2,3,4,5,6,\ pop:NXM_OF_IN_PORT[] ### Q: My bridge br0 has host 192.168.0.1 on port 1 and host 192.168.0.2 on port 2. I set up flows to forward only traffic destined to the other host and drop other traffic, like this: priority=5,in_port=1,ip,nw_dst=192.168.0.2,actions=2 priority=5,in_port=2,ip,nw_dst=192.168.0.1,actions=1 priority=0,actions=drop But it doesn't work--I don't get any connectivity when I do this. Why? A: These flows drop the ARP packets that IP hosts use to establish IP connectivity over Ethernet. To solve the problem, add flows to allow ARP to pass between the hosts: priority=5,in_port=1,arp,actions=2 priority=5,in_port=2,arp,actions=1 This issue can manifest other ways, too. The following flows that match on Ethernet addresses instead of IP addresses will also drop ARP packets, because ARP requests are broadcast instead of being directed to a specific host: priority=5,in_port=1,dl_dst=54:00:00:00:00:02,actions=2 priority=5,in_port=2,dl_dst=54:00:00:00:00:01,actions=1 priority=0,actions=drop The solution already described above will also work in this case. It may be better to add flows to allow all multicast and broadcast traffic: priority=5,in_port=1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=2 priority=5,in_port=2,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00,actions=1 ### Q: My bridge disconnects from my controller on add-port/del-port. A: Reconfiguring your bridge can change your bridge's datapath-id because Open vSwitch generates datapath-id from the MAC address of one of its ports. In that case, Open vSwitch disconnects from controllers because there's no graceful way to notify controllers about the change of datapath-id. To avoid the behaviour, you can configure datapath-id manually. ovs-vsctl set bridge br0 other-config:datapath-id=0123456789abcdef ### Q: My controller is getting errors about "buffers". What's going on? A: When a switch sends a packet to an OpenFlow controller using a "packet-in" message, it can also keep a copy of that packet in a "buffer", identified by a 32-bit integer "buffer_id". There are two advantages to buffering. First, when the controller wants to tell the switch to do something with the buffered packet (with a "packet-out" OpenFlow request), it does not need to send another copy of the packet back across the OpenFlow connection, which reduces the bandwidth cost of the connection and improves latency. This enables the second advantage: the switch can optionally send only the first part of the packet to the controller (assuming that the switch only needs to look at the first few bytes of the packet), further reducing bandwidth and improving latency. However, buffering introduces some issues of its own. First, any switch has limited resources, so if the controller does not use a buffered packet, the switch has to decide how long to keep it buffered. When many packets are sent to a controller and buffered, Open vSwitch can discard buffered packets that the controller has not used after as little as 5 seconds. This means that controllers, if they make use of packet buffering, should use the buffered packets promptly. (This includes sending a "packet-out" with no actions if the controller does not want to do anything with a buffered packet, to clear the packet buffer and effectively "drop" its packet.) Second, packet buffers are one-time-use, meaning that a controller cannot use a single packet buffer in two or more "packet-out" commands. Open vSwitch will respond with an error to the second and subsequent "packet-out"s in such a case. Finally, a common error early in controller development is to try to use buffer_id 0 in a "packet-out" message as if 0 represented "no buffered packet". This is incorrect usage: the buffer_id with this meaning is actually 0xffffffff. ovs-vswitchd(8) describes some details of Open vSwitch packet buffering that the OpenFlow specification requires implementations to document. Development ----------- ### Q: How do I implement a new OpenFlow message? A: Add your new message to "enum ofpraw" and "enum ofptype" in lib/ofp-msgs.h, following the existing pattern. Then recompile and fix all of the new warnings, implementing new functionality for the new message as needed. (If you configure with --enable-Werror, as described in [INSTALL.md], then it is impossible to miss any warnings.) If you need to add an OpenFlow vendor extension message for a vendor that doesn't yet have any extension messages, then you will also need to edit build-aux/extract-ofp-msgs. ### Q: How do I add support for a new field or header? A: Add new members for your field to "struct flow" in lib/flow.h, and add new enumerations for your new field to "enum mf_field_id" in lib/meta-flow.h, following the existing pattern. Also, add support to miniflow_extract() in lib/flow.c for extracting your new field from a packet into struct miniflow. Then recompile and fix all of the new warnings, implementing new functionality for the new field or header as needed. (If you configure with --enable-Werror, as described in [INSTALL.md], then it is impossible to miss any warnings.) If you want kernel datapath support for your new field, you also need to modify the kernel module for the operating systems you are interested in. This isn't mandatory, since fields understood only by userspace work too (with a performance penalty), so it's reasonable to start development without it. If you implement kernel module support for Linux, then the Linux kernel "netdev" mailing list is the place to submit that support first; please read up on the Linux kernel development process separately. The Windows datapath kernel module support, on the other hand, is maintained within the OVS tree, so patches for that can go directly to ovs-dev. ### Q: How do I add support for a new OpenFlow action? A: Add your new action to "enum ofp_raw_action_type" in lib/ofp-actions.c, following the existing pattern. Then recompile and fix all of the new warnings, implementing new functionality for the new action as needed. (If you configure with --enable-Werror, as described in [INSTALL.md], then it is impossible to miss any warnings.) If you need to add an OpenFlow vendor extension action for a vendor that doesn't yet have any extension actions, then you will also need to edit build-aux/extract-ofp-actions. Contact ------- bugs@openvswitch.org http://openvswitch.org/ [PORTING.md]:PORTING.md [WHY-OVS.md]:WHY-OVS.md [INSTALL.md]:INSTALL.md [OPENFLOW-1.1+.md]:OPENFLOW-1.1+.md [INSTALL.DPDK.md]:INSTALL.DPDK.md openvswitch-2.5.9/PaxHeaders.82075/config.h.in0000644000000000000000000000013213534540100015660 xustar0030 mtime=1567801408.597734129 30 atime=1567801415.973788494 30 ctime=1567801424.593852041 openvswitch-2.5.9/config.h.in0000644000175000017500000001715413534540100017356 0ustar00jpettitjpettit00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* If the C compiler is GCC 4.7 or later, define to the return value of __atomic_always_lock_free(1, 0). If the C compiler is not GCC or is an older version of GCC, the value does not matter. */ #undef ATOMIC_ALWAYS_LOCK_FREE_1B /* If the C compiler is GCC 4.7 or later, define to the return value of __atomic_always_lock_free(2, 0). If the C compiler is not GCC or is an older version of GCC, the value does not matter. */ #undef ATOMIC_ALWAYS_LOCK_FREE_2B /* If the C compiler is GCC 4.7 or later, define to the return value of __atomic_always_lock_free(4, 0). If the C compiler is not GCC or is an older version of GCC, the value does not matter. */ #undef ATOMIC_ALWAYS_LOCK_FREE_4B /* If the C compiler is GCC 4.7 or later, define to the return value of __atomic_always_lock_free(8, 0). If the C compiler is not GCC or is an older version of GCC, the value does not matter. */ #undef ATOMIC_ALWAYS_LOCK_FREE_8B /* System uses the DPDK module. */ #undef DPDK_NETDEV /* Define to 1 if building on ESX. */ #undef ESX /* Define to 1 if you have backtrace(3). */ #undef HAVE_BACKTRACE /* Define to 1 if you have the declaration of `strerror_r', and to 0 if you don't. */ #undef HAVE_DECL_STRERROR_R /* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you don't. */ #undef HAVE_DECL_SYS_SIGLIST /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if the C compiler and linker supports the GCC 4.0+ atomic built-ins. */ #undef HAVE_GCC4_ATOMICS /* Define to 1 if you have the `getloadavg' function. */ #undef HAVE_GETLOADAVG /* Define to 1 if you have the `getmntent_r' function. */ #undef HAVE_GETMNTENT_R /* Define to 1 if pthread_setname_np() is available and takes 2 parameters (like glibc). */ #undef HAVE_GLIBC_PTHREAD_SETNAME_NP /* Define to 1 if net/if_dl.h is available. */ #undef HAVE_IF_DL /* Define to 1 if net/if_packet.h is available. */ #undef HAVE_IF_PACKET /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if libcap-ng is available. */ #undef HAVE_LIBCAPNG /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_IF_ETHER_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_PERF_EVENT_H /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mlockall' function. */ #undef HAVE_MLOCKALL /* Define to 1 if you have the header file. */ #undef HAVE_MNTENT_H /* Define to 1 if pthread_setname_np() is available and takes 3 parameters (like NetBSD). */ #undef HAVE_NETBSD_PTHREAD_SETNAME_NP /* Define to 1 if Netlink protocol is available. */ #undef HAVE_NETLINK /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_MIB_H /* Define to 1 if OpenSSL is installed. */ #undef HAVE_OPENSSL /* Define to 1 if `posix_memalign' works. */ #undef HAVE_POSIX_MEMALIGN /* Define if compiler supports #pragma message directive */ #undef HAVE_PRAGMA_MESSAGE /* Define to 1 if you have the `pthread_set_name_np' function. */ #undef HAVE_PTHREAD_SET_NAME_NP /* Define to 1 if you have the `statvfs' function. */ #undef HAVE_STATVFS /* Define to 1 if you have the header file. */ #undef HAVE_STDATOMIC_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror_r' function. */ #undef HAVE_STRERROR_R /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strnlen' function. */ #undef HAVE_STRNLEN /* Define if strtok_r macro segfaults on some inputs */ #undef HAVE_STRTOK_R_BUG /* Define to 1 if `ifr_flagshigh' is a member of `struct ifreq'. */ #undef HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH /* Define to 1 if `st_mtimensec' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_MTIMENSEC /* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STATVFS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if the C compiler and linker supports the C11 thread_local matcro defined in . */ #undef HAVE_THREAD_LOCAL /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_VALGRIND_VALGRIND_H /* Define to 1 if the C compiler and linker supports the GCC __thread extenions. */ #undef HAVE___THREAD /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to 1 if strerror_r returns char *. */ #undef STRERROR_R_CHAR_P /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Version number of package */ #undef VERSION /* DPDK vhost-cuse support enabled, vhost-user disabled. */ #undef VHOST_CUSE /* System uses the Visual Studio build target. */ #undef VSTUDIO_DDK /* Define to 1 if building on WIN32. */ #undef WIN32 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define to 1 if on MINIX. */ #undef _MINIX /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE #ifdef WIN32 #include "include/windows/windefs.h" #endif openvswitch-2.5.9/PaxHeaders.82075/README-lisp.md0000644000000000000000000000013213534540071016070 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.045685899 30 ctime=1567801423.693845405 openvswitch-2.5.9/README-lisp.md0000644000175000017500000000757213534540071017571 0ustar00jpettitjpettit00000000000000Using LISP tunneling ==================== LISP is a layer 3 tunneling mechanism, meaning that encapsulated packets do not carry Ethernet headers, and ARP requests shouldn't be sent over the tunnel. Because of this, there are some additional steps required for setting up LISP tunnels in Open vSwitch, until support for L3 tunnels will improve. This guide assumes tunneling between two VMs connected to OVS bridges on different hypervisors reachable over IPv4. Of course, more than one VM may be connected to any of the hypervisors, and a hypervisor may communicate with several different hypervisors over the same lisp tunneling interface. A LISP "map-cache" can be implemented using flows, see example at the bottom of this file. There are several scenarios: 1) the VMs have IP addresses in the same subnet and the hypervisors are also in a single subnet (although one different from the VM's); 2) the VMs have IP addresses in the same subnet but the hypervisors are separated by a router; 3) the VMs are in different subnets. In cases 1) and 3) ARP resolution can work as normal: ARP traffic is configured not to go through the LISP tunnel. For case 1) ARP is able to reach the other VM, if both OVS instances default to MAC address learning. Case 3) requires the hypervisor be configured as the default router for the VMs. In case 2) the VMs expect ARP replies from each other, but this is not possible over a layer 3 tunnel. One solution is to have static MAC address entries preconfigured on the VMs (e.g., `arp -f /etc/ethers` on startup on Unix based VMs), or have the hypervisor do proxy ARP. In this scenario, the eth0 interfaces need not be added to the br0 bridge in the examples below. On the receiving side, the packet arrives without the original MAC header. The LISP tunneling code attaches a header with harcoded source and destination MAC address 02:00:00:00:00:00. This address has all bits set to 0, except the locally administered bit, in order to avoid potential collisions with existing allocations. In order for packets to reach their intended destination, the destination MAC address needs to be rewritten. This can be done using the flow table. See below for an example setup, and the associated flow rules to enable LISP tunneling. +---+ +---+ |VM1| |VM2| +---+ +---+ | | +--[tap0]--+ +--[tap0]---+ | | | | [lisp0] OVS1 [eth0]-----------------[eth0] OVS2 [lisp0] | | | | +----------+ +-----------+ On each hypervisor, interfaces tap0, eth0, and lisp0 are added to a single bridge instance, and become numbered 1, 2, and 3 respectively: ovs-vsctl add-br br0 ovs-vsctl add-port br0 tap0 ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 lisp0 -- set Interface lisp0 type=lisp options:remote_ip=flow options:key=flow The last command sets up flow based tunneling on the lisp0 interface. From the LISP point of view, this is like having the Tunnel Router map cache implemented as flow rules. Flows on br0 should be configured as follows: priority=3,dl_dst=02:00:00:00:00:00,action=mod_dl_dst:,output:1 priority=2,in_port=1,dl_type=0x0806,action=NORMAL priority=1,in_port=1,dl_type=0x0800,vlan_tci=0,nw_src=,action=set_field:->tun_dst,output:3 priority=0,action=NORMAL The third rule is like a map cache entry: the specified by the nw_src match field is mapped to the RLOC , which is set as the tunnel destination for this particular flow. Optionally, if you want to use Instance ID in a flow, you can add "set_tunnel:" to the action list. openvswitch-2.5.9/PaxHeaders.82075/INSTALL.Fedora.md0000644000000000000000000000013213534540071016473 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.037685841 30 ctime=1567801423.669845228 openvswitch-2.5.9/INSTALL.Fedora.md0000644000175000017500000000605613534540071020170 0ustar00jpettitjpettit00000000000000How to Install Open vSwitch on Fedora Linux =========================================== This document describes how to build and install Open vSwitch on a Fedora Linux host. If you want to install Open vSwitch on a generic Linux host, see [INSTALL.md] instead. We have tested these instructions with Fedora 16 and Fedora 17. Building Open vSwitch for Fedora -------------------------------- You may build from an Open vSwitch distribution tarball or from an Open vSwitch Git tree. The default RPM build directory (_topdir) has five directories in the top-level: 1. BUILD/ Where the software is unpacked and built. 2. RPMS/ Where the newly created binary package files are written. 3. SOURCES/ Contains the original sources, patches, and icon files. 4. SPECS/ Contains the spec files for each package to be built. 5. SRPMS/ Where the newly created source package files are written. Before you begin, note the RPM sources directory on your version of Fedora. The command "rpmbuild --showrc" will show the configuration for each of those directories. Alternatively, the command "rpm --eval '%{_topdir}'" shows the current configuration for the top level directory and the command "rpm --eval '%{_sourcedir}'" does the same for the sources directory. On Fedora 17, the default RPM _topdir is $HOME/rpmbuild and the default RPM sources directory is $HOME/rpmbuild/SOURCES. 1. If you are building from a distribution tarball, skip to step 2. Otherwise, you must be building from an Open vSwitch Git tree. Create a distribution tarball from the root of the Git tree by running: ``` ./boot.sh ./configure make dist ``` 2. Now you have a distribution tarball, named something like openvswitch-x.y.z.tar.gz. Copy this file into the RPM sources directory, e.g.: `cp openvswitch-x.y.z.tar.gz $HOME/rpmbuild/SOURCES` 3. Make another copy of the distribution tarball in a temporary directory. Then unpack the tarball and "cd" into its root, e.g.: ``` tar xzf openvswitch-x.y.z.tar.gz cd openvswitch-x.y.z ``` 4. To build Open vSwitch userspace, run: `rpmbuild -bb rhel/openvswitch-fedora.spec` This produces one RPM: "openvswitch". To enable DPDK support in the resulting openvswitch package, add `--with dpdk` to the build command. The above command automatically runs the Open vSwitch unit tests. To disable the unit tests, run: `rpmbuild -bb --without check rhel/openvswitch-fedora.spec` 5. On Fedora 17, to build the Open vSwitch kernel module, run: `rpmbuild -bb rhel/openvswitch-kmod-fedora.spec` You might have to specify a kernel version and/or variants, e.g.: ``` rpmbuild -bb \ -D "kversion 2.6.32-131.6.1.el6.x86_64" \ -D "kflavors default debug kdump" \ rhel/openvswitch-kmod-rhel6.spec ``` This produces an "kmod-openvswitch" RPM for each kernel variant, in this example: "kmod-openvswitch", "kmod-openvswitch-debug", and "kmod-openvswitch-kdump". Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/selinux0000644000000000000000000000013213534540120015251 xustar0030 mtime=1567801424.617852217 30 atime=1567801425.625859648 30 ctime=1567801424.617852217 openvswitch-2.5.9/selinux/0000755000175000017500000000000013534540120017014 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/selinux/PaxHeaders.82075/openvswitch-custom.te0000644000000000000000000000013113534540071021545 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.521851509 openvswitch-2.5.9/selinux/openvswitch-custom.te0000644000175000017500000000044413534540071023236 0ustar00jpettitjpettit00000000000000module openvswitch-custom 1.0; require { type openvswitch_t; class netlink_socket { setopt getopt create connect getattr write read }; } #============= openvswitch_t ============== allow openvswitch_t self:netlink_socket { setopt getopt create connect getattr write read }; openvswitch-2.5.9/selinux/PaxHeaders.82075/automake.mk0000644000000000000000000000013113534540071017471 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.617852217 openvswitch-2.5.9/selinux/automake.mk0000644000175000017500000000051413534540071021160 0ustar00jpettitjpettit00000000000000# Copyright (C) 2016 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ selinux/openvswitch-custom.te openvswitch-2.5.9/PaxHeaders.82075/datapath-windows0000644000000000000000000000013213534540120017040 xustar0030 mtime=1567801424.597852069 30 atime=1567801425.625859648 30 ctime=1567801424.597852069 openvswitch-2.5.9/datapath-windows/0000755000175000017500000000000013534540120020603 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/include0000644000000000000000000000013213534540120020463 xustar0030 mtime=1567801424.597852069 30 atime=1567801425.625859648 30 ctime=1567801424.597852069 openvswitch-2.5.9/datapath-windows/include/0000755000175000017500000000000013534540120022226 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/include/PaxHeaders.82075/OvsDpInterfaceExt.h0000644000000000000000000000013213534540071024253 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.345850211 openvswitch-2.5.9/datapath-windows/include/OvsDpInterfaceExt.h0000644000175000017500000001437513534540071025753 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OVS_DP_INTERFACE_EXT_H_ #define __OVS_DP_INTERFACE_EXT_H_ 1 /* Windows kernel datapath extensions to the standard datapath interface. */ /* Version number of the datapath interface extensions. */ #define OVS_DATAPATH_EXT_VERSION 1 /* Name of the device. */ #define OVS_DEVICE_NAME_NT L"\\Device\\OpenvSwitchDevice" #define OVS_DEVICE_NAME_DOS L"\\DosDevices\\OpenvSwitchDevice" #define OVS_DEVICE_NAME_USER TEXT("\\\\.\\OpenvSwitchDevice") #define OVS_IOCTL_DEVICE_TYPE 45000 #define OVS_IOCTL_START 0x100 /* We used Direct I/O (zero copy) for the buffers. */ /* Non-Netlink-based IOCTLs. */ #define OVS_IOCTL_GET_PID \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x0, METHOD_BUFFERED,\ FILE_WRITE_ACCESS) /* Netlink-based IOCTLs. */ #define OVS_IOCTL_READ \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x1, METHOD_OUT_DIRECT,\ FILE_READ_ACCESS) #define OVS_IOCTL_READ_EVENT \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x2, METHOD_OUT_DIRECT, \ FILE_READ_ACCESS) #define OVS_IOCTL_READ_PACKET \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x3, METHOD_OUT_DIRECT, \ FILE_READ_ACCESS) #define OVS_IOCTL_WRITE \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x4, METHOD_IN_DIRECT,\ FILE_READ_ACCESS) #define OVS_IOCTL_TRANSACT \ CTL_CODE (OVS_IOCTL_DEVICE_TYPE, OVS_IOCTL_START + 0x5, METHOD_OUT_DIRECT,\ FILE_WRITE_ACCESS) /* * On platforms that support netlink natively, the operating system assigns a * dynamic value to a netlink family when it is registered. In the absense of * such mechanism, defined hard-coded values that are known both to userspace * and kernel. */ #define OVS_WIN_NL_INVALID_FAMILY_ID 0 #define OVS_WIN_NL_CTRL_FAMILY_ID (NLMSG_MIN_TYPE + 1) #define OVS_WIN_NL_DATAPATH_FAMILY_ID (NLMSG_MIN_TYPE + 2) #define OVS_WIN_NL_PACKET_FAMILY_ID (NLMSG_MIN_TYPE + 3) #define OVS_WIN_NL_VPORT_FAMILY_ID (NLMSG_MIN_TYPE + 4) #define OVS_WIN_NL_FLOW_FAMILY_ID (NLMSG_MIN_TYPE + 5) #define OVS_WIN_NL_NETDEV_FAMILY_ID (NLMSG_MIN_TYPE + 6) #define OVS_WIN_NL_INVALID_MCGRP_ID 0 #define OVS_WIN_NL_MCGRP_START_ID 100 #define OVS_WIN_NL_VPORT_MCGRP_ID (OVS_WIN_NL_MCGRP_START_ID + 1) /* * Define a family of netlink command specific to Windows. This is part of the * extensions. */ #define OVS_WIN_CONTROL_FAMILY "ovs_win_control" #define OVS_WIN_CONTROL_MCGROUP "ovs_win_control" #define OVS_WIN_CONTROL_VERSION 1 /* Commands available under the OVS_WIN_CONTROL_FAMILY. */ enum ovs_win_control_cmd { OVS_CTRL_CMD_WIN_PEND_REQ, OVS_CTRL_CMD_WIN_PEND_PACKET_REQ, OVS_CTRL_CMD_MC_SUBSCRIBE_REQ, OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ, /* This command is logically belong to the Vport family */ OVS_CTRL_CMD_EVENT_NOTIFY, OVS_CTRL_CMD_READ_NOTIFY }; /* NL Attributes for joining/unjoining an MC group */ enum ovs_nl_mcast_attr { OVS_NL_ATTR_MCAST_GRP, /* (UINT32) Join an MC group */ OVS_NL_ATTR_MCAST_JOIN, /* (UINT8) 1/0 - Join/Unjoin */ OVS_NL_ATTR_PACKET_SUBSCRIBE, /* (UNINT8): 1/0 - subscribe/unsubscribe */ OVS_NL_ATTR_PACKET_PID, /* (UNINT32) netlink PID to receive upcalls */ __OVS_NL_ATTR_CTRL_MAX }; #define OVS_WIN_CONTROL_ATTR_MAX (__OVS_NL_ATTR_CTRL_MAX - 1) /* * Netdev family of commands specific to Windows. */ #define OVS_WIN_NETDEV_FAMILY "ovs_win_netdev" #define OVS_WIN_NETDEV_MCGROUP "ovs_win_netdev" #define OVS_WIN_NETDEV_VERSION 1 enum ovs_win_netdev_cmd { OVS_WIN_NETDEV_CMD_UNSPEC, OVS_WIN_NETDEV_CMD_GET, /* information about the netdev. */ }; /** * For every vport on the datapath, there is a corresponding netdev. General * network device attributes of a vport that are not specific to OVS, such as * MTU are represented using a netdev. For convenience, some of the vport * attributes are also included as netdev attributes. * * enum ovs_win_netdev_attr - attributes for %OVS_WIN_NETDEV_* commands. * @OVS_WIN_NETDEV_ATTR_PORT_NO: 32-bit port number of the vport within the * datapath. * @OVS_WIN_NETDEV_ATTR_TYPE: 32-bit %OVS_VPORT_TYPE_* constant describing * the type of vport. * @OVS_WIN_NETDEV_ATTR_NAME: Name of vport. Maximum length %IFNAMSIZ-1 bytes * plus a null terminator. * @OVS_WIN_NETDEV_ATTR_MAC_ADDR: MAC address of the vport. %ETH_ADDR_LEN bytes * long. * @OVS_WIN_NETDEV_ATTR_MTU : 32-bit MTU of the vport. * @OVS_WIN_NETDEV_ATTR_IF_FLAGS: 32-bit %OVS_WIN_NETDEV_IFF_* interface flags * of the vport. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_WIN_NETDEV_* commands. * * For all requests, if %OVS_WIN_NETDEV_ATTR_NAME is specified then it is used * to look up the netdev to operate on; dp_idx from the &struct * ovs_header is not relevant for the look up. */ enum ovs_win_netdev_attr { OVS_WIN_NETDEV_ATTR_UNSPEC, OVS_WIN_NETDEV_ATTR_PORT_NO, /* u32 port number within datapath. */ OVS_WIN_NETDEV_ATTR_TYPE, /* u32 OVS_NETDEV_TYPE_* constant. */ OVS_WIN_NETDEV_ATTR_NAME, /* string name, up to IFNAMSIZ bytes long. */ OVS_WIN_NETDEV_ATTR_MAC_ADDR, /* MAC address of the vport. */ OVS_WIN_NETDEV_ATTR_MTU, /* MTU of the vport. */ OVS_WIN_NETDEV_ATTR_IF_FLAGS, /* Interface flags o the vport. */ __OVS_WIN_NETDEV_ATTR_MAX }; #define OVS_WIN_NETDEV_ATTR_MAX (__OVS_WIN_NETDEV_ATTR_MAX - 1) #define OVS_WIN_NETDEV_IFF_UP (1 << 0) #define OVS_WIN_NETDEV_IFF_PROMISC (1 << 1) typedef struct ovs_dp_stats OVS_DP_STATS; typedef enum ovs_vport_type OVS_VPORT_TYPE; #endif /* __OVS_DP_INTERFACE_EXT_H_ */ openvswitch-2.5.9/datapath-windows/include/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071022704 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.597852069 openvswitch-2.5.9/datapath-windows/include/automake.mk0000644000175000017500000000072613534540071024377 0ustar00jpettitjpettit00000000000000if WIN32 BUILT_SOURCES += $(srcdir)/datapath-windows/include/OvsDpInterface.h endif $(srcdir)/datapath-windows/include/OvsDpInterface.h: \ datapath/linux/compat/include/linux/openvswitch.h \ build-aux/extract-odp-netlink-windows-dp-h $(AM_V_GEN)sed -f $(srcdir)/build-aux/extract-odp-netlink-windows-dp-h < $< > $@ EXTRA_DIST += $(srcdir)/build-aux/extract-odp-netlink-windows-dp-h CLEANFILES += $(srcdir)/datapath-windows/include/OvsDpInterface.h openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/Package0000644000000000000000000000013213534540120020373 xustar0030 mtime=1567801424.341850183 30 atime=1567801425.625859648 30 ctime=1567801424.341850183 openvswitch-2.5.9/datapath-windows/Package/0000755000175000017500000000000013534540120022136 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/Package/PaxHeaders.82075/package.VcxProj.user0000644000000000000000000000013213534540071024342 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.341850183 openvswitch-2.5.9/datapath-windows/Package/package.VcxProj.user0000644000175000017500000000124713534540071026034 0ustar00jpettitjpettit00000000000000 TestSign TestSign TestSign TestSign openvswitch-2.5.9/datapath-windows/Package/PaxHeaders.82075/package.VcxProj0000644000000000000000000000013213534540071023365 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.341850183 openvswitch-2.5.9/datapath-windows/Package/package.VcxProj0000644000175000017500000001146013534540071025055 0ustar00jpettitjpettit00000000000000 Win8.1 Debug x64 Win8 Debug x64 Win8.1 Release x64 Win8 Release x64 Utility Package true Win8 Debug $(VCTargetsPath11) {911D7389-3E61-449F-B8F3-14AD7EE9A0F2} {E753AF15-94DC-4773-AED9-06A6636E6E67} $(MSBuildProjectName) WindowsV6.3 true WindowsKernelModeDriver8.1 Windows8 true WindowsKernelModeDriver8.1 WindowsV6.3 false WindowsKernelModeDriver8.1 Windows8 false WindowsKernelModeDriver8.1 DbgengKernelDebugger False False None %PathToInf% False False True 133563 true true true true {63FE215D-98BE-4440-8081-C6160EFB80FA} openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/CodingStyle0000644000000000000000000000013213534540071021271 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801424.337850153 openvswitch-2.5.9/datapath-windows/CodingStyle0000644000175000017500000001335213534540071022763 0ustar00jpettitjpettit00000000000000 Open vSwitch Windows Datapath Coding Style ========================================== The coding style described in the Open vSwitch distribution gives the flexiblity for each platform to use its own coding style for the kernel datapath. This file describes the specific coding style used in most of the C files in the Windows kernel datapath of the Open vSwitch distribution. Most of the coding conventions applicable for the Open vSwitch distribution are applicable to the Windows kernel datapath as well. There are some exceptions and new guidlines owing to the commonly followed practices in Windows kernel/driver code. They are noted as follows: BASICS Limit lines to 79 characters. Many times, this is not possible due to long names of functions and it is fine to go beyond the characters limit. One common example is when calling into NDIS functions. TYPES Use data types defined by Windows for most of the code. This is a common practice in Windows driver code, and it makes integrating with the data structures and functions defined by Windows easier. Example: DWORD and BOOLEAN. Use caution in portions of the code that interface with the OVS userspace. OVS userspace does not use Windows specific data types, and when copying data back and forth between kernel and userspace, care should be exercised. NAMING It is common practice to use camel casing for naming variables, functions and files in Windows. For types, especially structures, unions and enums, using all upper case letters with words seprated by '_' is common. These practices can be used for OVS Windows datapath. However, use the following guidelines: Use lower case to begin the name of a variable. Do not use '_' to begin the name of the variable. '_' is to be used to begin the parameters of a pre-processor macro. Use upper case to begin the name of a function, enum, file name etc. Static functions whose scope is limited to the file they are defined in can be prefixed with '_'. This is not mandatory though. For types, use all upper case for all letters with words separated by '_'. If camel casing is preferred, use upper case for the first letter. It is a common practice to define a pointer type by prefixing the letter 'P' to a data type. The same practice can be followed here as well. Example: static __inline BOOLEAN OvsDetectTunnelRxPkt(POVS_FORWARDING_CONTEXT ovsFwdCtx, POVS_FLOW_KEY flowKey) { POVS_VPORT_ENTRY tunnelVport = NULL; if (!flowKey->ipKey.nwFrag && flowKey->ipKey.nwProto == IPPROTO_UDP && flowKey->ipKey.l4.tpDst == VXLAN_UDP_PORT_NBO) { tunnelVport = OvsGetTunnelVport(OVSWIN_VPORT_TYPE_VXLAN); ovsActionStats.rxVxlan++; } if (tunnelVport) { ASSERT(ovsFwdCtx->tunnelRxNic == NULL); ovsFwdCtx->tunnelRxNic = tunnelVport; return TRUE; } return FALSE; } For declaring variables of pointer type, use of the pointer data type prefixed with 'P' is preferred over using '*'. This is not mandatory though, and is only prescribed since it is a common practice in Windows. Example, #1 is preferred over #2 though #2 is also equally correct: 1. PNET_BUFFER_LIST curNbl; 2. NET_BUFFER_LIST *curNbl; COMMENTS Comments should be written as full sentences that start with a capital letter and end with a period. Putting two spaces between sentances is not necessary. // can be used for comments as long as the comment is a single line comment. For block comments, use /* */ comments FUNCTIONS Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. Before each function definition, write a comment that describes the function's purpose, including each parameter, the return value, and side effects. References to argument names should be given in single-quotes, e.g. 'arg'. The comment should not include the function name, nor need it follow any formal structure. The comment does not need to describe how a function does its work, unless this information is needed to use the function correctly (this is often better done with comments *inside* the function). Mention any side effects that the function has that are not obvious based on the name of the function or based on the workflow it is called from. In the interest of keeping comments describing functions similar in structure, use the following template. /* *---------------------------------------------------------------------------- * Any description of the function, arguments, return types, assumptions and * side effects. *---------------------------------------------------------------------------- */ SOURCE FILES Each source file should state its license in a comment at the very top, followed by a comment explaining the purpose of the code that is in that file. The comment should explain how the code in the file relates to code in other files. The goal is to allow a programmer to quickly figure out where a given module fits into the larger system. The first non-comment line in a .c source file should be: #include #include directives should appear in the following order: 1. #include 2. The module's own headers, if any. Including this before any other header (besides ) ensures that the module's header file is self-contained (see HEADER FILES) below. 3. Standard C library headers and other system headers, preferably in alphabetical order. (Occasionally one encounters a set of system headers that must be included in a particular order, in which case that order must take precedence.) 4. Open vSwitch headers, in alphabetical order. Use "", not <>, to specify Open vSwitch header names. openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/DESIGN0000644000000000000000000000013213534540071020016 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.341850183 openvswitch-2.5.9/datapath-windows/DESIGN0000644000175000017500000005625113534540071021515 0ustar00jpettitjpettit00000000000000 OVS-on-Hyper-V Design Document ============================== There has been a community effort to develop Open vSwitch on Microsoft Hyper-V. In this document, we provide details of the development effort. We believe this document should give enough information to understand the overall design. The userspace portion of the OVS has been ported to Hyper-V in a separate effort, and committed to the openvswitch repo. So, this document will mostly emphasize on the kernel driver, though we touch upon some of the aspects of userspace as well. We cover the following topics: 1. Background into relevant Hyper-V architecture 2. Design of the OVS Windows implementation a. Kernel module (datapath) b. Userspace components c. Kernel-Userspace interface d. Flow of a packet 3. Build/Deployment environment For more questions, please contact dev@openvswitch.org 1) Background into relevant Hyper-V architecture ------------------------------------------------ Microsoft’s hypervisor solution - Hyper-V[1] implements a virtual switch that is extensible and provides opportunities for other vendors to implement functional extensions[2]. The extensions need to be implemented as NDIS drivers that bind within the extensible switch driver stack provided. The extensions can broadly provide the functionality of monitoring, modifying and forwarding packets to destination ports on the Hyper-V extensible switch. Correspondingly, the extensions can be categorized into the following types and provide the functionality noted: * Capturing extensions: monitoring packets * Filtering extensions: monitoring, modifying packets * Forwarding extensions: monitoring, modifying, forwarding packets As can be expected, the kernel portion (datapath) of OVS on Hyper-V solution will be implemented as a forwarding extension. In Hyper-V, the virtual machine is called the Child Partition. Each VIF or physical NIC on the Hyper-V extensible switch is attached via a port. Each port is both on the ingress path or the egress path of the switch. The ingress path is used for packets being sent out of a port, and egress is used for packet being received on a port. By design, NDIS provides a layered interface. In this layered interface, higher level layers call into lower level layers, in the ingress path. In the egress path, it is the other way round. In addition, there is a object identifier (OID) interface for control operations Eg. addition of a port. The workflow for the calls is similar in nature to the packets, where higher level layers call into the lower level layers. A good representational diagram of this architecture is in [4]. Windows Filtering Platform (WFP)[5] is a platform implemented on Hyper-V that provides APIs and services for filtering packets. WFP has been utilized to filter on some of the packets that OVS is not equipped to handle directly. More details in later sections. IP Helper [6] is a set of API available on Hyper-V to retrieve information related to the network configuration information on the host machine. IP Helper has been used to retrieve some of the configuration information that OVS needs. 2) Design of the OVS Windows implementation ------------------------------------------- +-------------------------------+ | | | CHILD PARTITION | | | +------+ +--------------+ | +-----------+ +------------+ | | | | | | | | | | | | ovs- | | OVS- | | | Virtual | | Virtual | | | *ctl | | USERSPACE | | | Machine #1| | Machine #2 | | | | | DAEMON | | | | | | | +------+-++---+---------+ | +--+------+-+ +----+------++ | +--------+ | dpif- | | netdev- | | |VIF #1| |VIF #2| | |Physical| | netlink | | windows | | +------+ +------+ | | NIC | +---------+ +---------+ | || /\ | +--------+ User /\ /\ | || *#1* *#4* || | /\ =========||=========||============+------||-------------------||--+ || Kernel || || \/ || ||=====/ \/ \/ +-----+ +-----+ *#5* +-------------------------------+ | | | | | +----------------------+ | | | | | | | OVS Pseudo Device | | | | | | | +----------------------+ | | | | | | | Netlink Impl. | | | | | | | ----------------- | | I | | | | +------------+ | | N | | E | | | Flowtable | +------------+ | | G | | G | | +------------+ | Packet | |*#2*| R | | R | | +--------+ | Processing | |<=> | E | | E | | | WFP | | | | | S | | S | | | Driver | +------------+ | | S | | S | | +--------+ | | | | | | | | | | | | OVS FORWARDING EXTENSION | | | | | +-------------------------------+ +-----+-----------------+-----+ |HYPER-V Extensible Switch *#3| +-----------------------------+ NDIS STACK Fig 2. Various blocks of the OVS Windows implementation Figure 2 shows the various blocks involved in the OVS Windows implementation, along with some of the components available in the NDIS stack, and also the virtual machines. The workflow of a packet being transmitted from a VIF out and into another VIF and to a physical NIC is also shown. Later on in this section, we will discuss the flow of a packet at a high level. The figure gives a general idea of where the OVS userspace and the kernel components fit in, and how they interface with each other. The kernel portion (datapath) of OVS on Hyper-V solution has be implemented as a forwarding extension roughly implementing the following sub-modules/functionality. Details of each of these sub-components in the kernel are contained in later sections: * Interfacing with the NDIS stack * Netlink message parser * Netlink sockets * Switch/Datapath management * Interfacing with userspace portion of the OVS solution to implement the necessary functionality that userspace needs * Port management * Flowtable/Actions/packet forwarding * Tunneling * Event notifications The datapath for the OVS on Linux is a kernel module, and cannot be directly ported since there are significant differences in architecture even though the end functionality provided would be similar. Some examples of the differences are: * Interfacing with the NDIS stack to hook into the NDIS callbacks for functionality such as receiving and sending packets, packet completions, OIDs used for events such as a new port appearing on the virtual switch. * Interface between the userspace and the kernel module. * Event notifications are significantly different. * The communication interface between DPIF and the kernel module need not be implemented in the way OVS on Linux does. That said, it would be advantageous to have a similar interface to the kernel module for reasons of readability and maintainability. * Any licensing issues of using Linux kernel code directly. Due to these differences, it was a straightforward decision to develop the datapath for OVS on Hyper-V from scratch rather than porting the one on Linux. A re-development focused on the following goals: * Adhere to the existing requirements of userspace portion of OVS (such as ovs-vswitchd), to minimize changes in the userspace workflow. * Fit well into the typical workflow of a Hyper-V extensible switch forwarding extension. The userspace portion of the OVS solution is mostly POSIX code, and not very Linux specific. Majority of the userspace code does not interface directly with the kernel datapath and was ported independently of the kernel datapath effort. As explained in the OVS porting design document [7], DPIF is the portion of userspace that interfaces with the kernel portion of the OVS. The interface that each DPIF provider has to implement is defined in dpif-provider.h [3]. Though each platform is allowed to have its own implementation of the DPIF provider, it was found, via community feedback, that it is desired to share code whenever possible. Thus, the DPIF provider for OVS on Hyper-V shares code with the DPIF provider on Linux. This interface is implemented in dpif-netlink.c, formerly dpif-linux.c. We'll elaborate more on kernel-userspace interface in a dedicated section below. Here it suffices to say that the DPIF provider implementation for Windows is netlink-based and shares code with the Linux one. 2.a) Kernel module (datapath) ----------------------------- Interfacing with the NDIS stack ------------------------------- For each virtual switch on Hyper-V, the OVS extensible switch extension can be enabled/disabled. We support enabling the OVS extension on only one switch. This is consistent with using a single datapath in the kernel on Linux. All the physical adapters are connected as external adapters to the extensible switch. When the OVS switch extension registers itself as a filter driver, it also registers callbacks for the switch/port management and datapath functions. In other words, when a switch is created on the Hyper-V root partition (host), the extension gets an activate callback upon which it can initialize the data structures necessary for OVS to function. Similarly, there are callbacks for when a port gets added to the Hyper-V switch, and an External Network adapter or a VM Network adapter is connected/disconnected to the port. There are also callbacks for when a VIF (NIC of a child partition) send out a packet, or a packet is received on an external NIC. As shown in the figures, an extensible switch extension gets to see a packet sent by the VM (VIF) twice - once on the ingress path and once on the egress path. Forwarding decisions are to be made on the ingress path. Correspondingly, we will be hooking onto the following interfaces: * Ingress send indication: intercept packets for performing flow based forwarding.This includes straight forwarding to output ports. Any packet modifications needed to be performed are done here either inline or by creating a new packet. A forwarding action is performed as the flow actions dictate. * Ingress completion indication: cleanup and free packets that we generated on the ingress send path, pass-through for packets that we did not generate. * Egress receive indication: pass-through. * Egress completion indication: pass-through. Interfacing with OVS userspace ------------------------------ We have implemented a pseudo device interface for letting OVS userspace talk to the OVS kernel module. This is equivalent to the typical character device interface on POSIX platforms where we can register custom functions for read, write and ioctl functionality. The pseudo device supports a whole bunch of ioctls that netdev and DPIF on OVS userspace make use of. Netlink message parser ---------------------- The communication between OVS userspace and OVS kernel datapath is in the form of Netlink messages [1]. More details about this are provided in #2.c section, kernel-userspace interface. In the kernel, a full fledged netlink message parser has been implemented along the lines of the netlink message parser in OVS userspace. In fact, a lot of the code is ported code. On the lines of 'struct ofpbuf' in OVS userspace, a managed buffer has been implemented in the kernel datapath to make it easier to parse and construct netlink messages. Netlink sockets --------------- On Linux, OVS userspace utilizes netlink sockets to pass back and forth netlink messages. Since much of userspace code including DPIF provider in dpif-netlink.c (formerly dpif-linux.c) has been reused, pseudo-netlink sockets have been implemented in OVS userspace. As it is known, Windows lacks native netlink socket support, and also the socket family is not extensible either. Hence it is not possible to provide a native implementation of netlink socket. We emulate netlink sockets in lib/netlink-socket.c and support all of the nl_* APIs to higher levels. The implementation opens a handle to the pseudo device for each netlink socket. Some more details on this topic are provided in the userspace section on netlink sockets. Typical netlink semantics of read message, write message, dump, and transaction have been implemented so that higher level layers are not affected by the netlink implementation not being native. Switch/Datapath management -------------------------- As explained above, we hook onto the management callback functions in the NDIS interface for when to initialize the OVS data structures, flow tables etc. Some of this code is also driven by OVS userspace code which sends down ioctls for operations like creating a tunnel port etc. Port management --------------- As explained above, we hook onto the management callback functions in the NDIS interface to know when a port is added/connected to the Hyper-V switch. We use these callbacks to initialize the port related data structures in OVS. Also, some of the ports are tunnel ports that don’t exist on the Hyper-V switch and get added from OVS userspace. In order to identify a Hyper-V port, we use the value of 'FriendlyName' field in each Hyper-V port. We call this the "OVS-port-name". The idea is that OVS userspace sets 'OVS-port-name' in each Hyper-V port to the same value as the 'name' field of the 'Interface' table in OVSDB. When OVS userspace calls into the kernel datapath to add a port, we match the name of the port with the 'OVS-port-name' of a Hyper-V port. We maintain separate hash tables, and separate counters for ports that have been added from the Hyper-V switch, and for ports that have been added from OVS userspace. Flowtable/Actions/packet forwarding ----------------------------------- The flowtable and flow actions based packet forwarding is the core of the OVS datapath functionality. For each packet on the ingress path, we consult the flowtable and execute the corresponding actions. The actions can be limited to simple forwarding to a particular destination port(s), or more commonly involves modifying the packet to insert a tunnel context or a VLAN ID, and thereafter forwarding to the external port to send the packet to a destination host. Tunneling --------- We make use of the Internal Port on a Hyper-V switch for implementing tunneling. The Internal Port is a virtual adapter that is exposed on the Hyper- V host, and connected to the Hyper-V switch. Basically, it is an interface between the host and the virtual switch. The Internal Port acts as the Tunnel end point for the host (aka VTEP), and holds the VTEP IP address. Tunneling ports are not actual ports on the Hyper-V switch. These are virtual ports that OVS maintains and while executing actions, if the outport is a tunnel port, we short circuit by performing the encapsulation action based on the tunnel context. The encapsulated packet gets forwarded to the external port, and appears to the outside world as though it was set from the VTEP. Similarly, when a tunneled packet enters the OVS from the external port bound to the internal port (VTEP), and if yes, we short circuit the path, and directly forward the inner packet to the destination port (mostly a VIF, but dictated by the flow). We leverage the Windows Filtering Platform (WFP) framework to be able to receive tunneled packets that cannot be decapsulated by OVS right away. Currently, fragmented IP packets fall into that category, and we leverage the code in the host IP stack to reassemble the packet, and performing decapsulation on the reassembled packet. We’ll also be using the IP helper library to provide us IP address and other information corresponding to the Internal port. Event notifications ------------------- The pseudo device interface described above is also used for providing event notifications back to OVS userspace. A shared memory/overlapped IO model is used. 2.b) Userspace components ------------------------- The userspace portion of the OVS solution is mostly POSIX code, and not very Linux specific. Majority of the userspace code does not interface directly with the kernel datapath and was ported independently of the kernel datapath effort. In this section, we cover the userspace components that interface with the kernel datapath. As explained earlier, OVS on Hyper-V shares the DPIF provider implementation with Linux. The DPIF provider on Linux uses netlink sockets and netlink messages. Netlink sockets and messages are extensively used on Linux to exchange information between userspace and kernel. In order to satisfy these dependencies, netlink socket (pseudo and non-native) and netlink messages are implemented on Hyper-V. The following are the major advantages of sharing DPIF provider code: 1. Maintenance is simpler: Any change made to the interface defined in dpif-provider.h need not be propagated to multiple implementations. Also, developers familiar with the Linux implementation of the DPIF provider can easily ramp on the Hyper-V implementation as well. 2. Netlink messages provides inherent advantages: Netlink messages are known for their extensibility. Each message is versioned, so the provided data structures offer a mechanism to perform version checking and forward/backward compatibility with the kernel module. Netlink sockets --------------- As explained in other sections, an emulation of netlink sockets has been implemented in lib/netlink-socket.c for Windows. The implementation creates a handle to the OVS pseudo device, and emulates netlink socket semantics of receive message, send message, dump, and transact. Most of the nl_* functions are supported. The fact that the implementation is non-native manifests in various ways. One example is that PID for the netlink socket is not automatically assigned in userspace when a handle is created to the OVS pseudo device. There's an extra command (defined in OvsDpInterfaceExt.h) that is used to grab the PID generated in the kernel. DPIF provider -------------- As has been mentioned in earlier sections, the netlink socket and netlink message based DPIF provider on Linux has been ported to Windows. Correspondingly, the file is called lib/dpif-netlink.c now from its former name of lib/dpif-linux.c. Most of the code is common. Some divergence is in the code to receive packets. The Linux implementation uses epoll() which is not natively supported on Windows. Netdev-Windows -------------- We have a Windows implementation of the interface defined in lib/netdev-provider.h. The implementation provides functionality to get extended information about an interface. It is limited in functionality compared to the Linux implementation of the netdev provider and cannot be used to add any interfaces in the kernel such as a tap interface or to send/receive packets. The netdev-windows implementation uses the datapath interface extensions defined in: datapath-windows/include/OvsDpInterfaceExt.h Powershell extensions to set "OVS-port-name" -------------------------------------------- As explained in the section on "Port management", each Hyper-V port has a 'FriendlyName' field, which we call as the "OVS-port-name" field. We have implemented powershell command extensions to be able to set the "OVS-port-name" of a Hyper-V port. 2.c) Kernel-Userspace interface ------------------------------- openvswitch.h and OvsDpInterfaceExt.h ------------------------------------- Since the DPIF provider is shared with Linux, the kernel datapath provides the same interface as the Linux datapath. The interface is defined in datapath/linux/compat/include/linux/openvswitch.h. Derivatives of this interface file are created during OVS userspace compilation. The derivative for the kernel datapath on Hyper-V is provided in the following location: datapath-windows/include/OvsDpInterface.h That said, there are Windows specific extensions that are defined in the interface file: datapath-windows/include/OvsDpInterfaceExt.h 2.d) Flow of a packet --------------------- Figure 2 shows the numbered steps in which a packets gets sent out of a VIF and is forwarded to another VIF or a physical NIC. As mentioned earlier, each VIF is attached to the switch via a port, and each port is both on the ingress and egress path of the switch, and depending on whether a packet is being transmitted or received, one of the paths gets used. In the figure, each step n is annotated as *#n* The steps are as follows: 1. When a packet is sent out of a VIF or an physical NIC or an internal port, the packet is part of the ingress path. 2. The OVS kernel driver gets to intercept this packet. a. OVS looks up the flows in the flowtable for this packet, and executes the corresponding action. b. If there is not action, the packet is sent up to OVS userspace to examine the packet and figure out the actions. v. Userspace executes the packet by specifying the actions, and might also insert a flow for such a packet in the future. d. The destination ports are added to the packet and sent down to the Hyper- V switch. 3. The Hyper-V forwards the packet to the destination ports specified in the packet, and sends it out on the egress path. 4. The packet gets forwarded to the destination VIF. 5. It might also get forwarded to a physical NIC as well, if the physical NIC has been added as a destination port by OVS. 3) Build/Deployment: -------------------- The userspace components added as part of OVS Windows implementation have been integrated with autoconf, and can be built using the steps mentioned in the BUILD.Windows file. Additional targets need to be specified to make. The OVS kernel code is part of a Visual Studio 2013 solution, and is compiled from the IDE. There are plans in the future to move this to a compilation mode such that we can compile it without an IDE as well. Once compiled, we have an install script that can be used to load the kernel driver. Reference list: =============== 1. Hyper-V Extensible Switch http://msdn.microsoft.com/en-us/library/windows/hardware/hh598161(v=vs.85).aspx 2. Hyper-V Extensible Switch Extensions http://msdn.microsoft.com/en-us/library/windows/hardware/hh598169(v=vs.85).aspx 3. DPIF Provider http://openvswitch.sourcearchive.com/documentation/1.1.0-1/dpif- provider_8h_source.html 4. Hyper-V Extensible Switch Components http://msdn.microsoft.com/en-us/library/windows/hardware/hh598163(v=vs.85).aspx 5. Windows Filtering Platform http://msdn.microsoft.com/en-us/library/windows/desktop/aa366510(v=vs.85).aspx 6. IP Helper http://msdn.microsoft.com/en-us/library/windows/hardware/ff557015(v=vs.85).aspx 7. How to Port Open vSwitch to New Software or Hardware http://git.openvswitch.org/cgi-bin/gitweb.cgi?p=openvswitch;a=blob;f=PORTING 8. Netlink http://en.wikipedia.org/wiki/Netlink 9. epoll http://en.wikipedia.org/wiki/Epoll openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/ovsext0000644000000000000000000000013213534540120020370 xustar0030 mtime=1567801424.477851186 30 atime=1567801425.625859648 30 ctime=1567801424.477851186 openvswitch-2.5.9/datapath-windows/ovsext/0000755000175000017500000000000013534540120022133 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Driver.c0000644000000000000000000000013113534540071022050 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 30 ctime=1567801424.409850683 openvswitch-2.5.9/datapath-windows/ovsext/Driver.c0000644000175000017500000001455213534540071023546 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Switch.h" #include "User.h" #include "Datapath.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_DRIVER #include "Debug.h" /* Global handles. XXX: Some of them need not be global. */ /* * Maps to DriverObject and FilterDriverContext parameters in the NDIS filter * driver functions. * DriverObject is specified by NDIS. * FilterDriverContext is specified by the filter driver. */ NDIS_HANDLE gOvsExtDriverObject; /* * Maps to NdisFilterHandle parameter in the NDIS filter driver functions. * NdisFilterHandle is returned by NDISFRegisterFilterDriver. */ NDIS_HANDLE gOvsExtDriverHandle; /* * Maps to FilterModuleContext parameter in the NDIS filter driver functions. * FilterModuleContext is a allocated by the driver in the FilterAttach * function. */ extern POVS_SWITCH_CONTEXT gOvsSwitchContext; static PWCHAR ovsExtFriendlyName = L"Open vSwitch Extension"; static PWCHAR ovsExtServiceName = L"OVSExt"; NDIS_STRING ovsExtGuidUC; NDIS_STRING ovsExtFriendlyNameUC; static PWCHAR ovsExtGuidStr = L"{583CC151-73EC-4A6A-8B47-578297AD7623}"; static const GUID ovsExtGuid = { 0x583cc151, 0x73ec, 0x4a6a, {0x8b, 0x47, 0x57, 0x82, 0x97, 0xad, 0x76, 0x23} }; /* Declarations of callback functions for the filter driver. */ DRIVER_UNLOAD OvsExtUnload; FILTER_NET_PNP_EVENT OvsExtNetPnPEvent; FILTER_STATUS OvsExtStatus; FILTER_ATTACH OvsExtAttach; FILTER_DETACH OvsExtDetach; FILTER_RESTART OvsExtRestart; FILTER_PAUSE OvsExtPause; FILTER_SEND_NET_BUFFER_LISTS OvsExtSendNBL; FILTER_SEND_NET_BUFFER_LISTS_COMPLETE OvsExtSendNBLComplete; FILTER_CANCEL_SEND_NET_BUFFER_LISTS OvsExtCancelSendNBL; FILTER_RECEIVE_NET_BUFFER_LISTS OvsExtReceiveNBL; FILTER_RETURN_NET_BUFFER_LISTS OvsExtReturnNBL; FILTER_OID_REQUEST OvsExtOidRequest; FILTER_OID_REQUEST_COMPLETE OvsExtOidRequestComplete; FILTER_CANCEL_OID_REQUEST OvsExtCancelOidRequest; /* * -------------------------------------------------------------------------- * Init/Load function for the OVSEXT filter Driver. * -------------------------------------------------------------------------- */ NTSTATUS DriverEntry(PDRIVER_OBJECT driverObject, PUNICODE_STRING registryPath) { NDIS_STATUS status; NDIS_FILTER_DRIVER_CHARACTERISTICS driverChars; UNREFERENCED_PARAMETER(registryPath); /* Initialize driver associated data structures. */ OvsInit(); gOvsExtDriverObject = driverObject; RtlZeroMemory(&driverChars, sizeof driverChars); driverChars.Header.Type = NDIS_OBJECT_TYPE_FILTER_DRIVER_CHARACTERISTICS; driverChars.Header.Size = sizeof driverChars; driverChars.Header.Revision = NDIS_FILTER_CHARACTERISTICS_REVISION_2; driverChars.MajorNdisVersion = NDIS_FILTER_MAJOR_VERSION; driverChars.MinorNdisVersion = NDIS_FILTER_MINOR_VERSION; driverChars.MajorDriverVersion = 1; driverChars.MinorDriverVersion = 0; driverChars.Flags = 0; RtlInitUnicodeString(&driverChars.ServiceName, ovsExtServiceName); RtlInitUnicodeString(&ovsExtFriendlyNameUC, ovsExtFriendlyName); RtlInitUnicodeString(&ovsExtGuidUC, ovsExtGuidStr); driverChars.FriendlyName = ovsExtFriendlyNameUC; driverChars.UniqueName = ovsExtGuidUC; driverChars.AttachHandler = OvsExtAttach; driverChars.DetachHandler = OvsExtDetach; driverChars.RestartHandler = OvsExtRestart; driverChars.PauseHandler = OvsExtPause; driverChars.SendNetBufferListsHandler = OvsExtSendNBL; driverChars.SendNetBufferListsCompleteHandler = OvsExtSendNBLComplete; driverChars.CancelSendNetBufferListsHandler = OvsExtCancelSendNBL; driverChars.ReceiveNetBufferListsHandler = NULL; driverChars.ReturnNetBufferListsHandler = NULL; driverChars.OidRequestHandler = OvsExtOidRequest; driverChars.OidRequestCompleteHandler = OvsExtOidRequestComplete; driverChars.CancelOidRequestHandler = OvsExtCancelOidRequest; driverChars.DevicePnPEventNotifyHandler = NULL; driverChars.NetPnPEventHandler = OvsExtNetPnPEvent; driverChars.StatusHandler = NULL; driverObject->DriverUnload = OvsExtUnload; status = NdisFRegisterFilterDriver(driverObject, (NDIS_HANDLE)gOvsExtDriverObject, &driverChars, &gOvsExtDriverHandle); if (status != NDIS_STATUS_SUCCESS) { goto cleanup; } /* Create the communication channel for userspace. */ status = OvsCreateDeviceObject(gOvsExtDriverHandle); if (status != NDIS_STATUS_SUCCESS) { goto cleanup; } cleanup: if (status != NDIS_STATUS_SUCCESS){ OvsCleanup(); if (gOvsExtDriverHandle) { NdisFDeregisterFilterDriver(gOvsExtDriverHandle); gOvsExtDriverHandle = NULL; } } return status; } /* * -------------------------------------------------------------------------- * Un-init/Unload function for the OVS intermediate Driver. * -------------------------------------------------------------------------- */ VOID OvsExtUnload(struct _DRIVER_OBJECT *driverObject) { UNREFERENCED_PARAMETER(driverObject); /* Release driver associated data structures. */ OvsCleanup(); OvsDeleteDeviceObject(); NdisFDeregisterFilterDriver(gOvsExtDriverHandle); } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterStatus function. * -------------------------------------------------------------------------- */ VOID OvsExtStatus(NDIS_HANDLE filterModuleContext, PNDIS_STATUS_INDICATION statusIndication) { UNREFERENCED_PARAMETER(statusIndication); POVS_SWITCH_CONTEXT switchObject = (POVS_SWITCH_CONTEXT)filterModuleContext; NdisFIndicateStatus(switchObject->NdisFilterHandle, statusIndication); return; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Netlink0000644000000000000000000000013213534540120021774 xustar0030 mtime=1567801424.437850891 30 atime=1567801425.625859648 30 ctime=1567801424.437850891 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/0000755000175000017500000000000013534540120023537 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/NetlinkBuf.c0000644000000000000000000000013213534540071024263 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.429850831 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/NetlinkBuf.c0000644000175000017500000002331013534540071025750 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* ========================================================================== * This is a simple buffer mangement framework specific for netlink protocol. * The name could be confused with ovsext/BufferMgmt.c. Ovsext/BufferMgmt.c * deals with buffer mgmt for NBLs. Where as this framework deals with * management of buffer that holds a netlink message. * * This framework provides APIs for putting/accessing data in a buffer. These * APIs are used by driver's netlink protocol implementation. * * We can see this framework as a subset of ofpbuf in ovs userspace. * * This framework is NOT a generic buffer management framework (ofpbuf * is a generic buffer mgmt framework) and provides only the functioanlities * which would be useful for netlink protocol. Some of the key features are: * * a. It DOES NOT support automatic buffer reallocation. * i. A netlink input/output message is a static buffer. * b. The unused space is at the tail. * c. There is no notion of headdroom. * ========================================================================== */ #include #include #include #include #include #include #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_NETLINK #include "Debug.h" #include "NetlinkBuf.h" /* Returns used space in the buffer */ #define NL_BUF_USED_SPACE(_buf) (_buf->bufLen - \ _buf->bufRemLen) /* Validates that offset is within buffer boundaries and will not * create holes in the buffer.*/ #define NL_BUF_IS_VALID_OFFSET(_buf, _offset) (_offset <= \ NL_BUF_TAIL_OFFSET(_buf) ? 1 : 0) /* Validates if new data of size _size can be added at offset _offset. * This macor assumes that offset validation has been done.*/ #define NL_BUF_CAN_ADD(_buf, _size, _offset) (((_offset + _size <= \ _buf->bufLen) && (_size \ <= _buf->bufRemLen)) ? \ 1 : 0) /* Returns the offset of tail wrt buffer head */ #define NL_BUF_TAIL_OFFSET(_buf) (_buf->tail - _buf->head) static __inline VOID _NlBufCopyAtTailUnsafe(PNL_BUFFER nlBuf, PCHAR data, UINT32 len); static __inline VOID _NlBufCopyAtOffsetUnsafe(PNL_BUFFER nlBuf, PCHAR data, UINT32 len, UINT32 offset); /* * -------------------------------------------------------------------------- * NlBufInit -- * * Initializes NL_BUF with buffer pointer and length. * -------------------------------------------------------------------------- */ VOID NlBufInit(PNL_BUFFER nlBuf, PCHAR base, UINT32 size) { ASSERT(nlBuf); nlBuf->head = nlBuf->tail = base; nlBuf->bufLen = nlBuf->bufRemLen = size; } /* * -------------------------------------------------------------------------- * NlBufDeInit -- * * Resets the buffer variables to NULL. * -------------------------------------------------------------------------- */ VOID NlBufDeInit(PNL_BUFFER nlBuf) { ASSERT(nlBuf); nlBuf->head = nlBuf->tail = NULL; nlBuf->bufLen = nlBuf->bufRemLen = 0; } /* * -------------------------------------------------------------------------- * NlBufCopyAtTail -- * * Copies the data to the tail end of the buffer. * -------------------------------------------------------------------------- */ BOOLEAN NlBufCopyAtTail(PNL_BUFFER nlBuf, PCHAR data, UINT32 len) { BOOLEAN ret = TRUE; ASSERT(nlBuf); /* Check if we have enough space */ if (!NL_BUF_CAN_ADD(nlBuf, len, NL_BUF_TAIL_OFFSET(nlBuf))) { ret = FALSE; goto done; } _NlBufCopyAtTailUnsafe(nlBuf, data, len); done: return ret; } /* * -------------------------------------------------------------------------- * NlBufCopyAtHead -- * * Copies the data to the head of the buffer. * It can be seen as special case of NlBufCopyAtOffset with input * offset zero. * -------------------------------------------------------------------------- */ BOOLEAN NlBufCopyAtHead(PNL_BUFFER nlBuf, PCHAR data, UINT32 len) { BOOLEAN ret = TRUE; ASSERT(nlBuf); /* Check if we have enough space */ if (!NL_BUF_CAN_ADD(nlBuf, len, 0)) { ret = FALSE; goto done; } if (nlBuf->head == nlBuf->tail) { /* same as inserting in tail */ _NlBufCopyAtTailUnsafe(nlBuf, data, len); goto done; } _NlBufCopyAtOffsetUnsafe(nlBuf, data, len, 0); done: return ret; } /* * -------------------------------------------------------------------------- * NlBufCopyAtOffset -- * * Inserts data at input offset in the buffer. * If the offset is earlier then tail end then it first creates * space of size input length at input offset by moving the existing * data forward. * -------------------------------------------------------------------------- */ BOOLEAN NlBufCopyAtOffset(PNL_BUFFER nlBuf, PCHAR data, UINT32 len, UINT32 offset) { PCHAR dest = NULL; BOOLEAN ret = TRUE; ASSERT(nlBuf); /* Check if input offset is valid and has enough space */ if ((!NL_BUF_IS_VALID_OFFSET(nlBuf, offset)) || (!NL_BUF_CAN_ADD(nlBuf, len, offset))) { ret = FALSE; goto done; } dest = nlBuf->head + offset; if (dest == nlBuf->tail) { /* same as inserting in tail */ _NlBufCopyAtTailUnsafe(nlBuf, data, len); goto done; } _NlBufCopyAtOffsetUnsafe(nlBuf, data, len, offset); done: return ret; } /* * -------------------------------------------------------------------------- * NlBufCopyAtTailUninit -- * * Memsets the buffer portion of length len at tail end with zero. * -------------------------------------------------------------------------- */ PCHAR NlBufCopyAtTailUninit(PNL_BUFFER nlBuf, UINT32 len) { PCHAR ret; ret = nlBuf->tail; if ((NlBufCopyAtTail(nlBuf, NULL, len)) == FALSE) { ret = NULL; } return ret; } /* * -------------------------------------------------------------------------- * NlBufCopyAtHeadUninit -- * * Memsets the buffer portion of length len at head with zero. * -------------------------------------------------------------------------- */ PCHAR NlBufCopyAtHeadUninit(PNL_BUFFER nlBuf, UINT32 len) { PCHAR ret = NULL; if ((NlBufCopyAtHead(nlBuf, NULL, len)) == FALSE) { goto done; } ret = nlBuf->head; done: return ret; } /* * -------------------------------------------------------------------------- * NlBufCopyAtOffsetUninit -- * * Memsets the buffer portion of length len at head with zero. * * If the offset is earlier then tail end then it first creates * space of size input length at input offset by moving the existing * data forward. * -------------------------------------------------------------------------- */ PCHAR NlBufCopyAtOffsetUninit(PNL_BUFFER nlBuf, UINT32 len, UINT32 offset) { PCHAR ret = NULL; if ((NlBufCopyAtOffset(nlBuf, NULL, len, offset)) == FALSE) { goto done; } ret = nlBuf->head + offset; done: return ret; } /* * -------------------------------------------------------------------------- * NlBufAt -- * * Returns pointer to buffer at input offset. * bufLen is used to verify that expected data length * is within valid boundaries. Here by boundaries we mean * within head and tail. * -------------------------------------------------------------------------- */ PCHAR NlBufAt(PNL_BUFFER nlBuf, UINT32 offset, UINT32 bufLen) { PCHAR ret = NULL; ASSERT(nlBuf); if ((!NL_BUF_IS_VALID_OFFSET(nlBuf, offset))) { goto done; } /* Check if requested buffer is within head and tail */ if ((offset + bufLen) > NL_BUF_USED_SPACE(nlBuf)) { goto done; } ret = nlBuf->head + offset; done: return ret; } /* *_Unsafe functions does not do any validation. */ /* * -------------------------------------------------------------------------- * _NlBufCopyAtTailUnsafe -- * * Helper function for NlBufCopyAtTail. * -------------------------------------------------------------------------- */ static __inline VOID _NlBufCopyAtTailUnsafe(PNL_BUFFER nlBuf, PCHAR data, UINT32 len) { if (data) { RtlCopyMemory(nlBuf->tail, data, len); } else { RtlZeroMemory(nlBuf->tail, len); } nlBuf->tail += len; nlBuf->bufRemLen -= len; } /* * -------------------------------------------------------------------------- * _NlBufCopyAtOffsetUnsafe -- * * Helper function for NlBufCopyAtOffset. * -------------------------------------------------------------------------- */ static __inline VOID _NlBufCopyAtOffsetUnsafe(PNL_BUFFER nlBuf, PCHAR data, UINT32 len, UINT32 offset) { PCHAR dest = NULL; dest = nlBuf->head + offset; RtlMoveMemory(dest+len, dest, NL_BUF_USED_SPACE(nlBuf) - offset); if (data) { RtlCopyMemory(dest, data, len); } else { RtlZeroMemory(dest, len); } nlBuf->tail += len; nlBuf->bufRemLen -= len; } openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/Netlink.h0000644000000000000000000000013213534540071023633 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.429850831 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/Netlink.h0000644000175000017500000002011713534540071025322 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETLINK_H_ #define __NETLINK_H_ 1 #include "Types.h" #include "NetlinkProto.h" #include "NetlinkBuf.h" #include "..\..\include\OvsDpInterface.h" /* * Structure of any message passed between userspace and kernel. */ typedef struct _OVS_MESSAGE { NL_MSG_HDR nlMsg; GENL_MSG_HDR genlMsg; OVS_HDR ovsHdr; /* Variable length nl_attrs follow. */ } OVS_MESSAGE, *POVS_MESSAGE; /* * Structure of an error message sent as a reply from kernel. */ typedef struct _OVS_MESSAGE_ERROR { NL_MSG_HDR nlMsg; NL_MSG_ERR errorMsg; } OVS_MESSAGE_ERROR, *POVS_MESSAGE_ERROR; /* Netlink attribute types. */ typedef enum { NL_A_NO_ATTR = 0, NL_A_VAR_LEN, NL_A_UNSPEC, NL_A_U8, NL_A_U16, NL_A_BE16 = NL_A_U16, NL_A_U32, NL_A_BE32 = NL_A_U32, NL_A_U64, NL_A_BE64 = NL_A_U64, NL_A_STRING, NL_A_FLAG, NL_A_NESTED, N_NL_ATTR_TYPES } NL_ATTR_TYPE; /* Netlink attribute policy. * Specifies the policy for parsing for netlink attribute. */ typedef struct _NL_POLICY { NL_ATTR_TYPE type; UINT32 minLen; UINT32 maxLen; BOOLEAN optional; } NL_POLICY, *PNL_POLICY; /* This macro is careful to check for attributes with bad lengths. */ #define NL_ATTR_FOR_EACH(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ NlAttrIsValid(ITER, LEFT); \ (LEFT) -= NlAttrLenPad(ITER, LEFT), (ITER) = NlAttrNext(ITER)) /* This macro does not check for attributes with bad lengths. It should only * be used with messages from trusted sources or with messages that have * already been validated (e.g. with NL_ATTR_FOR_EACH). */ #define NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, ATTRS, ATTRS_LEN) \ for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \ (LEFT) > 0; \ (LEFT) -= NLA_ALIGN((ITER)->nlaLen), (ITER) = NlAttrNext(ITER)) #define NL_ATTR_GET_AS(NLA, TYPE) \ (*(TYPE*) NlAttrGetUnspec(nla, sizeof(TYPE))) BOOLEAN NlFillOvsMsg(PNL_BUFFER nlBuf, UINT16 nlmsgType, UINT16 nlmsgFlags, UINT32 nlmsgSeq, UINT32 nlmsgPid, UINT8 genlCmd, UINT8 genlVer, UINT32 dpNo); BOOLEAN NlFillNlHdr(PNL_BUFFER nlBuf, UINT16 nlmsgType, UINT16 nlmsgFlags, UINT32 nlmsgSeq, UINT32 nlmsgPid); VOID NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgOut, UINT errorCode); /* Netlink message accessing the payload */ PVOID NlMsgAt(const PNL_MSG_HDR nlh, UINT32 offset); UINT32 NlMsgSize(const PNL_MSG_HDR nlh); VOID NlMsgAlignSize(const PNL_MSG_HDR nlh); VOID NlMsgSetSize(const PNL_MSG_HDR nlh, UINT32 msgLen); PCHAR NlHdrPayload(const PNL_MSG_HDR nlh); UINT32 NlHdrPayloadLen(const PNL_MSG_HDR nlh); PNL_ATTR NlMsgAttrs(const PNL_MSG_HDR nlh); UINT32 NlMsgAttrsLen(const PNL_MSG_HDR nlh); /* Netlink message parse */ PNL_MSG_HDR NlMsgNext(const PNL_MSG_HDR nlh); INT NlAttrIsValid(const PNL_ATTR nla, UINT32 maxlen); UINT32 NlAttrLenPad(const PNL_ATTR nla, UINT32 maxlen); /* Netlink attribute parsing. */ UINT32 NlAttrMinLen(NL_ATTR_TYPE type); UINT32 NlAttrMinLen(NL_ATTR_TYPE type); PNL_ATTR NlAttrNext(const PNL_ATTR nla); UINT16 NlAttrType(const PNL_ATTR nla); PVOID NlAttrData(const PNL_ATTR nla); UINT32 NlAttrGetSize(const PNL_ATTR nla); const PVOID NlAttrGet(const PNL_ATTR nla); const PVOID NlAttrGetUnspec(const PNL_ATTR nla, UINT32 size); BE64 NlAttrGetBe64(const PNL_ATTR nla); BE32 NlAttrGetBe32(const PNL_ATTR nla); UINT8 NlAttrGetU8(const PNL_ATTR nla); UINT16 NlAttrGetU16(const PNL_ATTR nla); UINT32 NlAttrGetU32(const PNL_ATTR nla); UINT64 NlAttrGetU64(const PNL_ATTR nla); const PNL_ATTR NlAttrFind__(const PNL_ATTR attrs, UINT32 size, UINT16 type); const PNL_ATTR NlAttrFindNested(const PNL_ATTR nla, UINT16 type); BOOLEAN NlAttrParse(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, UINT32 totalAttrLen, const NL_POLICY policy[], const UINT32 numPolicy, PNL_ATTR attrs[], UINT32 numAttrs); BOOLEAN NlAttrParseNested(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, UINT32 totalAttrLen, const NL_POLICY policy[], const UINT32 numPolicy, PNL_ATTR attrs[], UINT32 numAttrs); /* * -------------------------------------------------------------------------- * Returns the length of attribute. * -------------------------------------------------------------------------- */ static __inline UINT16 NlAttrLen(const PNL_ATTR nla) { return nla->nlaLen; } /* * --------------------------------------------------------------------------- * Default maximum payload size for each type of attribute. * --------------------------------------------------------------------------- */ UINT32 static __inline NlAttrSize(UINT32 payloadSize) { return NLA_HDRLEN + payloadSize; } /* * --------------------------------------------------------------------------- * Total length including padding. * --------------------------------------------------------------------------- */ UINT32 static __inline NlAttrTotalSize(UINT32 payloadSize) { return NLA_ALIGN(NlAttrSize(payloadSize)); } /* Netlink attribute validation */ BOOLEAN NlAttrValidate(const PNL_ATTR, const PNL_POLICY); /* Put APis */ BOOLEAN NlMsgPutNlHdr(PNL_BUFFER buf, PNL_MSG_HDR nlMsg); BOOLEAN NlMsgPutGenlHdr(PNL_BUFFER buf, PGENL_MSG_HDR genlMsg); BOOLEAN NlMsgPutOvsHdr(PNL_BUFFER buf, POVS_HDR ovsHdr); BOOLEAN NlMsgPutTail(PNL_BUFFER buf, const PCHAR data, UINT32 len); PCHAR NlMsgPutTailUninit(PNL_BUFFER buf, UINT32 len); PCHAR NlMsgPutTailUnspecUninit(PNL_BUFFER buf, UINT16 type, UINT16 len); BOOLEAN NlMsgPutTailUnspec(PNL_BUFFER buf, UINT16 type, PCHAR data, UINT16 len); BOOLEAN NlMsgPutTailFlag(PNL_BUFFER buf, UINT16 type); BOOLEAN NlMsgPutTailU8(PNL_BUFFER buf, UINT16 type, UINT8 value); BOOLEAN NlMsgPutTailU16(PNL_BUFFER buf, UINT16 type, UINT16 value); BOOLEAN NlMsgPutTailU32(PNL_BUFFER buf, UINT16 type, UINT32 value); BOOLEAN NlMsgPutTailU64(PNL_BUFFER buf, UINT16 type, UINT64 value); BOOLEAN NlMsgPutTailString(PNL_BUFFER buf, UINT16 type, PCHAR value); BOOLEAN NlMsgPutHead(PNL_BUFFER buf, const PCHAR data, UINT32 len); PCHAR NlMsgPutHeadUninit(PNL_BUFFER buf, UINT32 len); PCHAR NlMsgPutHeadUnspecUninit(PNL_BUFFER buf, UINT16 type, UINT16 len); BOOLEAN NlMsgPutHeadUnspec(PNL_BUFFER buf, UINT16 type, PCHAR data, UINT16 len); BOOLEAN NlMsgPutHeadFlag(PNL_BUFFER buf, UINT16 type); BOOLEAN NlMsgPutHeadU8(PNL_BUFFER buf, UINT16 type, UINT8 value); BOOLEAN NlMsgPutHeadU16(PNL_BUFFER buf, UINT16 type, UINT16 value); BOOLEAN NlMsgPutHeadU32(PNL_BUFFER buf, UINT16 type, UINT32 value); BOOLEAN NlMsgPutHeadU64(PNL_BUFFER buf, UINT16 type, UINT64 value); BOOLEAN NlMsgPutHeadString(PNL_BUFFER buf, UINT16 type, PCHAR value); UINT32 NlMsgStartNested(PNL_BUFFER buf, UINT16 type); VOID NlMsgEndNested(PNL_BUFFER buf, UINT32 offset); BOOLEAN NlMsgPutNested(PNL_BUFFER buf, UINT16 type, const PVOID data, UINT32 size); /* These variants are convenient for iterating nested attributes. */ #define NL_NESTED_FOR_EACH(ITER, LEFT, A) \ NL_ATTR_FOR_EACH(ITER, LEFT, NlAttrGet(A), NlAttrGetSize(A)) #define NL_NESTED_FOR_EACH_UNSAFE(ITER, LEFT, A) \ NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, NlAttrGet(A), NlAttrGetSize(A)) #endif /* __NETLINK_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/NetlinkProto.h0000644000000000000000000000013213534540071024657 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.437850891 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/NetlinkProto.h0000644000175000017500000000704113534540071026347 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2010, 2011, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETLINK_PROTO_H_ #define __NETLINK_PROTO_H_ 1 /* Netlink protocol definitions. * * Netlink is a message framing format described in RFC 3549 and used heavily * in Linux to access the network stack. Open vSwitch uses AF_NETLINK sockets * for this purpose on Linux. On Windows platform too, Open vSwitch uses * netlink message format for userspace-kernelspace communication. * * This header provides access to the Netlink message framing definitions * regardless of platform. */ #include "Types.h" #define BUILD_ASSERT(EXPR) \ typedef char AssertOnCompileFailed[(EXPR) ? 1: -1] #define BUILD_ASSERT_DECL(EXPR) BUILD_ASSERT(EXPR) /* Returns X / Y, rounding up. X must be nonnegative to round correctly. */ #define DIV_ROUND_UP(X, Y) (((X) + ((Y) - 1)) / (Y)) /* Returns X rounded up to the nearest multiple of Y. */ #define ROUND_UP(X, Y) (DIV_ROUND_UP(X, Y) * (Y)) /* Returns the least number that, when added to X, yields a multiple of Y. */ #define PAD_SIZE(X, Y) (ROUND_UP(X, Y) - (X)) /* Netlink message */ /* nlmsg_flags bits. */ #define NLM_F_REQUEST 0x001 #define NLM_F_MULTI 0x002 #define NLM_F_ACK 0x004 #define NLM_F_ECHO 0x008 #define NLM_F_ROOT 0x100 #define NLM_F_MATCH 0x200 #define NLM_F_EXCL 0x200 #define NLM_F_ATOMIC 0x400 #define NLM_F_CREATE 0x400 #define NLM_F_DUMP (NLM_F_ROOT | NLM_F_MATCH) /* nlmsg_type values. */ #define NLMSG_NOOP 1 #define NLMSG_ERROR 2 #define NLMSG_DONE 3 #define NLMSG_OVERRUN 4 #define NLMSG_MIN_TYPE 0x10 #define MAX_LINKS 32 #define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(SIZE) ROUND_UP(SIZE, NLMSG_ALIGNTO) #define NLA_ALIGNTO 4 #define NLA_ALIGN(SIZE) ROUND_UP(SIZE, NLA_ALIGNTO) typedef struct ovs_header OVS_HDR, *POVS_HDR; typedef struct _NL_MSG_HDR { UINT32 nlmsgLen; UINT16 nlmsgType; UINT16 nlmsgFlags; UINT32 nlmsgSeq; UINT32 nlmsgPid; } NL_MSG_HDR, *PNL_MSG_HDR; BUILD_ASSERT_DECL(sizeof(NL_MSG_HDR) == 16); typedef struct _NlMsgErr { INT error; NL_MSG_HDR nlMsg; } NL_MSG_ERR, *PNL_MSG_ERR; BUILD_ASSERT_DECL(sizeof(NL_MSG_ERR) == 20); typedef struct _GENL_MSG_HDR { UINT8 cmd; UINT8 version; UINT16 reserved; } GENL_MSG_HDR, *PGENL_MSG_HDR; BUILD_ASSERT_DECL(sizeof(GENL_MSG_HDR) == 4); /* Netlink attributes */ typedef struct _NL_ATTR { UINT16 nlaLen; UINT16 nlaType; } NL_ATTR, *PNL_ATTR; BUILD_ASSERT_DECL(sizeof(NL_ATTR) == 4); #ifndef NLA_TYPE_MASK #define NLA_F_NESTED (1 << 15) #define NLA_F_NET_BYTEORDER (1 << 14) #define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) #endif #define NLMSG_HDRLEN ((INT) NLMSG_ALIGN(sizeof(NL_MSG_HDR))) #define GENL_HDRLEN NLMSG_ALIGN(sizeof(GENL_MSG_HDR)) #define OVS_HDRLEN NLMSG_ALIGN(sizeof(OVS_HDR)) #define NLA_HDRLEN ((INT) NLA_ALIGN(sizeof(NL_ATTR))) #endif /* NetlinProto.h */ openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/NetlinkBuf.h0000644000000000000000000000013213534540071024270 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.433850861 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/NetlinkBuf.h0000644000175000017500000000430213534540071025755 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NETLINK_BUF_H_ #define __NETLINK_BUF_H_ 1 typedef struct _NL_BUF { PCHAR head; /* start address of the buffer */ PCHAR tail; /* first empty byte of the buffer */ UINT32 bufLen; /* original length of buffer */ UINT32 bufRemLen; /* remaining length of buffer */ } NL_BUFFER, *PNL_BUFFER; VOID NlBufInit(PNL_BUFFER nlBuf, PCHAR base, UINT32 size); VOID NlBufDeInit(PNL_BUFFER nlBuf); BOOLEAN NlBufCopyAtTail(PNL_BUFFER nlBuf, PCHAR data, UINT32 len); BOOLEAN NlBufCopyAtHead(PNL_BUFFER nlBuf, PCHAR data, UINT32 len); BOOLEAN NlBufCopyAtOffset(PNL_BUFFER nlBuf, PCHAR data, UINT32 len, UINT32 offset); PCHAR NlBufCopyAtTailUninit(PNL_BUFFER nlBuf, UINT32 len); PCHAR NlBufCopyAtHeadUninit(PNL_BUFFER nlBuf, UINT32 len); PCHAR NlBufCopyAtOffsetUninit(PNL_BUFFER nlBuf, UINT32 len, UINT32 offset); PCHAR NlBufAt(PNL_BUFFER nlBuf, UINT32 offset, UINT32 len); /* * -------------------------------------------------------------------------- * NlBufSize -- * * Returns the used size of buffer. * -------------------------------------------------------------------------- */ static __inline UINT32 NlBufSize(PNL_BUFFER nlBuf) { ASSERT(nlBuf); return (nlBuf->bufLen - nlBuf->bufRemLen); } /* * -------------------------------------------------------------------------- * NlBufRemLen -- * * Returns the unused size of buffer. * -------------------------------------------------------------------------- */ static __inline UINT32 NlBufRemLen(PNL_BUFFER nlBuf) { ASSERT(nlBuf); return (nlBuf->bufRemLen); } #endif /* __NETLINK_BUF_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/NetlinkError.h0000644000000000000000000000013213534540071024645 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.433850861 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/NetlinkError.h0000644000175000017500000001757413534540071026351 0ustar00jpettitjpettit00000000000000/* * Copyright 2014 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "precomp.h" /* * These are error codes to be used by netlink transactional operations. * The error code is assigned to the "error" field (INT) of the NL_MSG_ERR * struct. */ typedef enum _NL_ERROR_ { NL_ERROR_SUCCESS = 0, /* The operation is not permitted */ NL_ERROR_PERM = ((ULONG)-1), /* There is no such file or directory */ NL_ERROR_NOENT = ((ULONG)-2), /* There is no such process */ NL_ERROR_SRCH = ((ULONG)-3), /* An interrupted system call / interrupted function */ NL_ERROR_INTR = ((ULONG)-4), /* An I/O error */ NL_ERROR_IO = ((ULONG)-5), /* There is no such device or address */ NL_ERROR_NXIO = ((ULONG)-6), /* The argument list is too long */ NL_ERROR_2BIG = ((ULONG)-7), /* Executable file format error */ NL_ERROR_NOEXEC = ((ULONG)-8), /* A bad file descriptor / number */ NL_ERROR_BADF = ((ULONG)-9), /* Have no child processes */ NL_ERROR_CHILD = ((ULONG)-10), /* resource unavailable => try again later */ NL_ERROR_AGAIN = ((ULONG)-11), /* We're out of memory */ NL_ERROR_NOMEM = ((ULONG)-12), /* Permission is denied */ NL_ERROR_ACCES = ((ULONG)-13), /* A bad address */ NL_ERROR_FAULT = ((ULONG)-14), /* The device or the resource is busy */ NL_ERROR_BUSY = ((ULONG)-16), /* The file exists */ NL_ERROR_EXIST = ((ULONG)-17), /* A cross-device link */ NL_ERROR_XDEV = ((ULONG)-18), /* There is no such device */ NL_ERROR_NODEV = ((ULONG)-19), /* It is not a directory, nor a symbolic link to a directory. */ NL_ERROR_NOTDIR = ((ULONG)-20), /* This is a directory */ NL_ERROR_ISDIR = ((ULONG)-21), /* An invalid argument */ NL_ERROR_INVAL = ((ULONG)-22), /* * There are too many files open in system (i.e. no room for another file * descriptor) */ NL_ERROR_NFILE = ((ULONG)-23), /* The file descriptor value is too large. */ NL_ERROR_MFILE = ((ULONG)-24), /* And Inappropriate I/O control operation. Or, this is not a typewriter */ NL_ERROR_NOTTY = ((ULONG)-25), /* The file is too large */ NL_ERROR_FBIG = ((ULONG)-27), /* There is no space left on the device */ NL_ERROR_NOSPC = ((ULONG)-28), /* This is an invalid seek */ NL_ERROR_SPIPE = ((ULONG)-29), /* A read-only file system */ NL_ERROR_ROFS = ((ULONG)-30), /* There are too many links */ NL_ERROR_MLINK = ((ULONG)-31), /* A broken pipe */ NL_ERROR_PIPE = ((ULONG)-32), /* The mathematics argument is out of the domain of the function. */ NL_ERROR_DOM = ((ULONG)-33), /* The result is too large / cannot be represented */ NL_ERROR_RANGE = ((ULONG)-34), /* A resource deadlock would occur */ NL_ERROR_DEADLK = ((ULONG)-36), /* The file name is too long */ NL_ERROR_NAMETOOLONG = ((ULONG)-38), /* There are no locks available */ NL_ERROR_NOLCK = ((ULONG)-39), /* The function is not implemented / not supported */ NL_ERROR_NOSYS = ((ULONG)-40), /* The directory is not empty */ NL_ERROR_NOTEMPTY = ((ULONG)-41), /* The byte sequence is illegal */ NL_ERROR_ILSEQ = ((ULONG)-42), NL_ERROR_STRUNCATE = ((ULONG)-80), /* The address is already in use */ NL_ERROR_ADDRINUSE = ((ULONG)-100), /* The requested address cannot be assigned: is is not available */ NL_ERROR_ADDRNOTAVAIL = ((ULONG)-101), /* the address family is not supported by the protocol */ NL_ERROR_AFNOSUPPORT = ((ULONG)-102), /* The operation / connection is already in progress */ NL_ERROR_ALREADY = ((ULONG)-103), /* The message is bad */ NL_ERROR_BADMSG = ((ULONG)-104), /* The operation was canceled */ NL_ERROR_CANCELED = ((ULONG)-105), /* The software has caused a connection abort */ NL_ERROR_CONNABORTED = ((ULONG)-106), /*The connection was refused */ NL_ERROR_CONNREFUSED = ((ULONG)-107), /* The connection was reset by the peer */ NL_ERROR_CONNRESET = ((ULONG)-108), /* The destination address is required */ NL_ERROR_DESTADDRREQ = ((ULONG)-109), /*The host is unreachable */ NL_ERROR_HOSTUNREACH = ((ULONG)-110), /* The identifier was removed */ NL_ERROR_IDRM = ((ULONG)-111), /* The operations is in progress */ NL_ERROR_INPROGRESS = ((ULONG)-112), /* The socket is already connected */ NL_ERROR_ISCONN = ((ULONG)-113), /* There are too many levels of symbolic links. */ NL_ERROR_LOOP = ((ULONG)-114), /*The message is too large */ NL_ERROR_MSGSIZE = ((ULONG)-115), /* The network is down */ NL_ERROR_NETDOWN = ((ULONG)-116), /* The network has dropped connection because of a reset (i.e. the * connection was aborted by the network) */ NL_ERROR_NETRESET = ((ULONG)-117), /* The network is unreachable */ NL_ERROR_NETUNREACH = ((ULONG)-118), /* There is no buffer space available */ NL_ERROR_NOBUFS = ((ULONG)-119), /* There is no data available (on the stream head read queue) */ NL_ERROR_NODATA = ((ULONG)-120), /* The link has been severed (it's reserved in posix) */ NL_ERROR_NOLINK = ((ULONG)-121), /* There is no message of the desired type */ NL_ERROR_NOMSG = ((ULONG)-122), /* The protocol is not available */ NL_ERROR_NOPROTOOPT = ((ULONG)-123), /* We're out of streams resources */ NL_ERROR_NOSR = ((ULONG)-124), /* This is not a stream */ NL_ERROR_NOSTR = ((ULONG)-125), /* The socket is not connected */ NL_ERROR_NOTCONN = ((ULONG)-126), /* The state is not recoverable */ NL_ERROR_NOTRECOVERABLE = ((ULONG)-127), /* This is not a socket */ NL_ERROR_NOTSOCK = ((ULONG)-128), /* The operation is not supported */ NL_ERROR_NOTSUPP = ((ULONG)-129), /* The operation is not supported on socket */ NL_ERROR_OPNOTSUPP = ((ULONG)-130), NL_ERROR_OTHER = ((ULONG)-131), /* The value is too large for the data type */ NL_ERROR_OVERFLOW = ((ULONG)-132), /* The previous owner died */ NL_ERROR_OWNERDEAD = ((ULONG)-133), /* A protocol error */ NL_ERROR_PROTO = ((ULONG)-134), /* The protocol is not supported */ NL_ERROR_PROTONOSUPPORT = ((ULONG)-135), /* This is a wrong protocol type for the socket */ NL_ERROR_PROTOTYPE = ((ULONG)-136), /* The timer has expired (or, the stream ioctl has timed out) */ NL_ERROR_TIME = ((ULONG)-137), /* The connection has timed out */ NL_ERROR_TIMEDOUT = ((ULONG)-138), /* The given text file is busy */ NL_ERROR_TXTBSY = ((ULONG)-139), /* The operation would block */ NL_ERROR_WOULDBLOCK = ((ULONG)-140), /* The operation is not finished */ NL_ERROR_PENDING = ((ULONG)-141), } NL_ERROR; static __inline NlMapStatusToNlErr(NTSTATUS status) { NL_ERROR ret; switch (status) { case STATUS_NOT_SUPPORTED: ret = NL_ERROR_NOTSUPP; break; case STATUS_INSUFFICIENT_RESOURCES: ret = NL_ERROR_NOMEM; break; case STATUS_SUCCESS: ret = NL_ERROR_SUCCESS; break; case STATUS_PENDING: ret = NL_ERROR_PENDING; break; case STATUS_CANCELLED: ret = NL_ERROR_CANCELED; break; case STATUS_INVALID_PARAMETER: ret = NL_ERROR_INVAL; break; case STATUS_OBJECT_NAME_EXISTS: ret = NL_ERROR_EXIST; break; default: ret = NL_ERROR_OTHER; break; } return ret; } openvswitch-2.5.9/datapath-windows/ovsext/Netlink/PaxHeaders.82075/Netlink.c0000644000000000000000000000013213534540071023626 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.429850831 openvswitch-2.5.9/datapath-windows/ovsext/Netlink/Netlink.c0000644000175000017500000010042613534540071025317 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "NetlinkProto.h" #include "Netlink.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_NETLINK #include "Debug.h" /* ========================================================================== * This file provides simple netlink get, put and validation APIs. * Most of the code is on similar lines as userspace netlink implementation. * * TODO: Convert these methods to inline. * ========================================================================== */ /* * --------------------------------------------------------------------------- * Prepare netlink message headers. This API adds * NL_MSG_HDR + GENL_HDR + OVS_HDR to the tail of input NLBuf. * Attributes should be added by caller. * --------------------------------------------------------------------------- */ BOOLEAN NlFillOvsMsg(PNL_BUFFER nlBuf, UINT16 nlmsgType, UINT16 nlmsgFlags, UINT32 nlmsgSeq, UINT32 nlmsgPid, UINT8 genlCmd, UINT8 genlVer, UINT32 dpNo) { BOOLEAN writeOk; OVS_MESSAGE msgOut; UINT32 offset = NlBufSize(nlBuf); /* To keep compiler happy for release build. */ UNREFERENCED_PARAMETER(offset); ASSERT(NlBufAt(nlBuf, offset, 0) != 0); msgOut.nlMsg.nlmsgType = nlmsgType; msgOut.nlMsg.nlmsgFlags = nlmsgFlags; msgOut.nlMsg.nlmsgSeq = nlmsgSeq; msgOut.nlMsg.nlmsgPid = nlmsgPid; msgOut.nlMsg.nlmsgLen = sizeof(struct _OVS_MESSAGE); msgOut.genlMsg.cmd = genlCmd; msgOut.genlMsg.version = genlVer; msgOut.genlMsg.reserved = 0; msgOut.ovsHdr.dp_ifindex = dpNo; writeOk = NlMsgPutTail(nlBuf, (PCHAR)(&msgOut), sizeof (struct _OVS_MESSAGE)); return writeOk; } /* * --------------------------------------------------------------------------- * Prepare NL_MSG_HDR only. This API appends a NL_MSG_HDR to the tail of * input NlBuf. * --------------------------------------------------------------------------- */ BOOLEAN NlFillNlHdr(PNL_BUFFER nlBuf, UINT16 nlmsgType, UINT16 nlmsgFlags, UINT32 nlmsgSeq, UINT32 nlmsgPid) { BOOLEAN writeOk; NL_MSG_HDR msgOut; UINT32 offset = NlBufSize(nlBuf); /* To keep compiler happy for release build. */ UNREFERENCED_PARAMETER(offset); ASSERT(NlBufAt(nlBuf, offset, 0) != 0); msgOut.nlmsgType = nlmsgType; msgOut.nlmsgFlags = nlmsgFlags; msgOut.nlmsgSeq = nlmsgSeq; msgOut.nlmsgPid = nlmsgPid; msgOut.nlmsgLen = sizeof(struct _NL_MSG_HDR); writeOk = NlMsgPutTail(nlBuf, (PCHAR)(&msgOut), sizeof(struct _NL_MSG_HDR)); return writeOk; } /* * --------------------------------------------------------------------------- * Prepare a 'OVS_MESSAGE_ERROR' message. * --------------------------------------------------------------------------- */ VOID NlBuildErrorMsg(POVS_MESSAGE msgIn, POVS_MESSAGE_ERROR msgError, UINT errorCode) { NL_BUFFER nlBuffer; ASSERT(errorCode != NL_ERROR_PENDING); NlBufInit(&nlBuffer, (PCHAR)msgError, sizeof *msgError); NlFillNlHdr(&nlBuffer, NLMSG_ERROR, 0, msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid); msgError->errorMsg.error = errorCode; msgError->errorMsg.nlMsg = msgIn->nlMsg; msgError->nlMsg.nlmsgLen = sizeof(OVS_MESSAGE_ERROR); } /* * --------------------------------------------------------------------------- * Adds Netlink Header to the NL_BUF. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutNlHdr(PNL_BUFFER buf, PNL_MSG_HDR nlMsg) { if ((NlBufCopyAtOffset(buf, (PCHAR)nlMsg, NLMSG_HDRLEN, 0))) { return TRUE; } return FALSE; } /* * --------------------------------------------------------------------------- * Adds Genl Header to the NL_BUF. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutGenlHdr(PNL_BUFFER buf, PGENL_MSG_HDR genlMsg) { if ((NlBufCopyAtOffset(buf, (PCHAR)genlMsg, GENL_HDRLEN, NLMSG_HDRLEN))) { return TRUE; } return FALSE; } /* * --------------------------------------------------------------------------- * Adds OVS Header to the NL_BUF. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutOvsHdr(PNL_BUFFER buf, POVS_HDR ovsHdr) { if ((NlBufCopyAtOffset(buf, (PCHAR)ovsHdr, OVS_HDRLEN, GENL_HDRLEN + NLMSG_HDRLEN))) { return TRUE; } return FALSE; } /* * --------------------------------------------------------------------------- * Adds data of length 'len' to the tail end of NL_BUF. * Refer nl_msg_put for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTail(PNL_BUFFER buf, const PCHAR data, UINT32 len) { len = NLMSG_ALIGN(len); if (NlBufCopyAtTail(buf, data, len)) { return TRUE; } return FALSE; } /* * --------------------------------------------------------------------------- * memsets length 'len' at tail end of NL_BUF. * Refer nl_msg_put_uninit for more details. * --------------------------------------------------------------------------- */ PCHAR NlMsgPutTailUninit(PNL_BUFFER buf, UINT32 len) { len = NLMSG_ALIGN(len); return NlBufCopyAtTailUninit(buf, len); } /* * --------------------------------------------------------------------------- * Adds an attribute to the tail end of buffer. It does * not copy the attribute payload. * Refer nl_msg_put_unspec_uninit for more details. * --------------------------------------------------------------------------- */ PCHAR NlMsgPutTailUnspecUninit(PNL_BUFFER buf, UINT16 type, UINT16 len) { PCHAR ret = NULL; UINT16 totalLen = NLA_HDRLEN + len; PNL_ATTR nla = (PNL_ATTR)(NlMsgPutTailUninit(buf, totalLen)); if (!nla) { goto done; } ret = (PCHAR)(nla + 1); nla->nlaLen = totalLen; nla->nlaType = type; done: return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute to the tail end of buffer. It copies attribute * payload as well. * Refer nl_msg_put_unspec for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailUnspec(PNL_BUFFER buf, UINT16 type, PCHAR data, UINT16 len) { BOOLEAN ret = TRUE; PCHAR nlaData = NlMsgPutTailUnspecUninit(buf, type, len); if (!nlaData) { ret = FALSE; goto done; } RtlCopyMemory(nlaData, data, len); done: return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and no payload at the tail end of buffer. * Refer nl_msg_put_flag for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailFlag(PNL_BUFFER buf, UINT16 type) { BOOLEAN ret = TRUE; PCHAR nlaData = NlMsgPutTailUnspecUninit(buf, type, 0); if (!nlaData) { ret = FALSE; } return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 8 bit payload at the tail end of buffer. * Refer nl_msg_put_u8 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailU8(PNL_BUFFER buf, UINT16 type, UINT8 value) { return (NlMsgPutTailUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 16 bit payload at the tail end of buffer. * Refer nl_msg_put_u16 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailU16(PNL_BUFFER buf, UINT16 type, UINT16 value) { return (NlMsgPutTailUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 32 bit payload at the tail end of buffer. * Refer nl_msg_put_u32 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailU32(PNL_BUFFER buf, UINT16 type, UINT32 value) { return (NlMsgPutTailUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 64 bit payload at the tail end of buffer. * Refer nl_msg_put_u64 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailU64(PNL_BUFFER buf, UINT16 type, UINT64 value) { return (NlMsgPutTailUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and string payload. * Refer nl_msg_put_string for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutTailString(PNL_BUFFER buf, UINT16 type, PCHAR value) { size_t strLen = strlen(value) + 1; #ifdef DBG /* Attribute length should come within 16 bits (NL_ATTR). * Not a likely case, hence validation only in debug mode. */ if ((strLen + PAD_SIZE(strLen, NLA_ALIGNTO)) > MAXUINT16) { return FALSE; } #endif /* typecast to keep compiler happy */ return (NlMsgPutTailUnspec(buf, type, value, (UINT16)strLen)); } /* * --------------------------------------------------------------------------- * Adds data of length 'len' to the head of NL_BUF. * Refer nl_msg_push for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHead(PNL_BUFFER buf, const PCHAR data, UINT32 len) { len = NLMSG_ALIGN(len); if (NlBufCopyAtHead(buf, data, len)) { return TRUE; } return FALSE; } /* * --------------------------------------------------------------------------- * memsets length 'len' at head of NL_BUF. * Refer nl_msg_push_uninit for more details. * --------------------------------------------------------------------------- */ PCHAR NlMsgPutHeadUninit(PNL_BUFFER buf, UINT32 len) { len = NLMSG_ALIGN(len); return NlBufCopyAtHeadUninit(buf, len); } /* * --------------------------------------------------------------------------- * Adds an attribute to the head of buffer. It does * not copy the attribute payload. * Refer nl_msg_push_unspec_uninit for more details. * --------------------------------------------------------------------------- */ PCHAR NlMsgPutHeadUnspecUninit(PNL_BUFFER buf, UINT16 type, UINT16 len) { PCHAR ret = NULL; UINT16 totalLen = NLA_HDRLEN + len; PNL_ATTR nla = (PNL_ATTR)(NlMsgPutHeadUninit(buf, totalLen)); if (!nla) { goto done; } ret = (PCHAR)(nla + 1); nla->nlaLen = totalLen; nla->nlaType = type; done: return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute to the head of buffer. It copies attribute * payload as well. * Refer nl_msg_push_unspec for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadUnspec(PNL_BUFFER buf, UINT16 type, PCHAR data, UINT16 len) { BOOLEAN ret = TRUE; PCHAR nlaData = NlMsgPutHeadUnspecUninit(buf, type, len); if (!nlaData) { ret = FALSE; goto done; } RtlCopyMemory(nlaData, data, len); done: return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and no payload at the head of buffer. * Refer nl_msg_push_flag for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadFlag(PNL_BUFFER buf, UINT16 type) { BOOLEAN ret = TRUE; PCHAR nlaData = NlMsgPutHeadUnspecUninit(buf, type, 0); if (!nlaData) { ret = FALSE; } return ret; } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 8 bit payload at the head of buffer. * Refer nl_msg_push_u8 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadU8(PNL_BUFFER buf, UINT16 type, UINT8 value) { return (NlMsgPutHeadUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 16 bit payload at the head of buffer. * Refer nl_msg_push_u16 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadU16(PNL_BUFFER buf, UINT16 type, UINT16 value) { return (NlMsgPutHeadUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 32 bit payload at the head of buffer. * Refer nl_msg_push_u32 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadU32(PNL_BUFFER buf, UINT16 type, UINT32 value) { return (NlMsgPutHeadUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and 64 bit payload at the head of buffer. * Refer nl_msg_push_u64 for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadU64(PNL_BUFFER buf, UINT16 type, UINT64 value) { return (NlMsgPutHeadUnspec(buf, type, (PCHAR)(&value), sizeof(value))); } /* * --------------------------------------------------------------------------- * Adds an attribute of 'type' and string payload. * Refer nl_msg_push_string for more details. * --------------------------------------------------------------------------- */ BOOLEAN NlMsgPutHeadString(PNL_BUFFER buf, UINT16 type, PCHAR value) { size_t strLen = strlen(value) + 1; #ifdef DBG /* Attribute length should come within 16 bits (NL_ATTR). * Not a likely case, hence validation only in debug mode. */ if ((strLen + PAD_SIZE(strLen, NLA_ALIGNTO)) > MAXUINT16) { return FALSE; } #endif /* typecast to keep compiler happy */ return (NlMsgPutHeadUnspec(buf, type, value, (UINT16)strLen)); } /* * --------------------------------------------------------------------------- * Adds the header for nested netlink attributes. It * returns the offset of this header. If addition of header fails * then returned value of offset will be zero. * Refer nl_msg_start_nested for more details. * --------------------------------------------------------------------------- */ UINT32 NlMsgStartNested(PNL_BUFFER buf, UINT16 type) { UINT32 offset = NlBufSize(buf); PCHAR nlaData = NULL; nlaData = NlMsgPutTailUnspecUninit(buf, type, 0); if (!nlaData) { /* Value zero must be reated as error by the caller. * This is because an attribute can never be added * at offset zero, it will always come after NL_MSG_HDR, * GENL_HDR and OVS_HEADER. */ offset = 0; } return offset; } /* * --------------------------------------------------------------------------- * Finalizes the nested netlink attribute by updating the nla_len. * offset should be the one returned by NlMsgStartNested. * Refer nl_msg_end_nested for more details. * --------------------------------------------------------------------------- */ VOID NlMsgEndNested(PNL_BUFFER buf, UINT32 offset) { PNL_ATTR attr = (PNL_ATTR)(NlBufAt(buf, offset, sizeof *attr)); /* Typecast to keep compiler happy. * Attribute length would never exceed MAX UINT16.*/ attr->nlaLen = (UINT16)(NlBufSize(buf) - offset); } /* * -------------------------------------------------------------------------- * Appends a nested Netlink attribute of the given 'type', with the 'size' * bytes of content starting at 'data', to 'msg'. * Refer nl_msg_put_nested for more details. * -------------------------------------------------------------------------- */ BOOLEAN NlMsgPutNested(PNL_BUFFER buf, UINT16 type, const PVOID data, UINT32 size) { UINT32 offset = NlMsgStartNested(buf, type); BOOLEAN ret = FALSE; ASSERT(offset); ret = NlMsgPutTail(buf, data, size); ASSERT(ret); NlMsgEndNested(buf, offset); return ret; } /* Accessing netlink message payload */ /* * --------------------------------------------------------------------------- * Netlink message accessing the payload. * --------------------------------------------------------------------------- */ PVOID NlMsgAt(const PNL_MSG_HDR nlh, UINT32 offset) { return ((PCHAR)nlh + offset); } /* * --------------------------------------------------------------------------- * Returns the size of netlink message. * --------------------------------------------------------------------------- */ UINT32 NlMsgSize(const PNL_MSG_HDR nlh) { return nlh->nlmsgLen; } /* * --------------------------------------------------------------------------- * Aligns the size of Netlink message. * --------------------------------------------------------------------------- */ VOID NlMsgAlignSize(const PNL_MSG_HDR nlh) { nlh->nlmsgLen = NLMSG_ALIGN(nlh->nlmsgLen); return; } /* * --------------------------------------------------------------------------- * Sets the size of Netlink message. * --------------------------------------------------------------------------- */ VOID NlMsgSetSize(const PNL_MSG_HDR nlh, UINT32 msgLen) { nlh->nlmsgLen = msgLen; } /* * --------------------------------------------------------------------------- * Returns pointer to nlmsg payload. * --------------------------------------------------------------------------- */ PCHAR NlHdrPayload(const PNL_MSG_HDR nlh) { return ((PCHAR)nlh + NLMSG_HDRLEN); } /* * --------------------------------------------------------------------------- * Returns length of nlmsg payload. * --------------------------------------------------------------------------- */ UINT32 NlHdrPayloadLen(const PNL_MSG_HDR nlh) { return nlh->nlmsgLen - NLMSG_HDRLEN; } /* * --------------------------------------------------------------------------- * Returns pointer to nlmsg attributes. * --------------------------------------------------------------------------- */ PNL_ATTR NlMsgAttrs(const PNL_MSG_HDR nlh) { return (PNL_ATTR) (NlHdrPayload(nlh) + GENL_HDRLEN + OVS_HDRLEN); } /* * --------------------------------------------------------------------------- * Returns size of to nlmsg attributes. * --------------------------------------------------------------------------- */ UINT32 NlMsgAttrsLen(const PNL_MSG_HDR nlh) { return NlHdrPayloadLen(nlh) - GENL_HDRLEN - OVS_HDRLEN; } /* Netlink message parse. */ /* * --------------------------------------------------------------------------- * Returns next netlink message in the stream. * --------------------------------------------------------------------------- */ PNL_MSG_HDR NlMsgNext(const PNL_MSG_HDR nlh) { return (PNL_MSG_HDR)((PCHAR)nlh + NLMSG_ALIGN(nlh->nlmsgLen)); } /* * --------------------------------------------------------------------------- * Netlink Attr helper APIs. * --------------------------------------------------------------------------- */ INT NlAttrIsValid(const PNL_ATTR nla, UINT32 maxlen) { return (maxlen >= sizeof *nla && nla->nlaLen >= sizeof *nla && nla->nlaLen <= maxlen); } /* * --------------------------------------------------------------------------- * Returns alligned length of the attribute. * --------------------------------------------------------------------------- */ UINT32 NlAttrLenPad(const PNL_ATTR nla, UINT32 maxlen) { UINT32 len = NLA_ALIGN(nla->nlaLen); return len <= maxlen ? len : nla->nlaLen; } /* * --------------------------------------------------------------------------- * Default minimum payload size for each type of attribute. * --------------------------------------------------------------------------- */ UINT32 NlAttrMinLen(NL_ATTR_TYPE type) { switch (type) { case NL_A_NO_ATTR: return 0; case NL_A_UNSPEC: return 0; case NL_A_U8: return 1; case NL_A_U16: return 2; case NL_A_U32: return 4; case NL_A_U64: return 8; case NL_A_STRING: return 1; case NL_A_FLAG: return 0; case NL_A_NESTED: return 0; case N_NL_ATTR_TYPES: default: OVS_LOG_WARN("Unsupprted attribute type: %d", type); ASSERT(0); } /* To keep compiler happy */ return 0; } /* * --------------------------------------------------------------------------- * Default maximum payload size for each type of attribute. * --------------------------------------------------------------------------- */ UINT32 NlAttrMaxLen(NL_ATTR_TYPE type) { switch (type) { case NL_A_NO_ATTR: return SIZE_MAX; case NL_A_UNSPEC: return SIZE_MAX; case NL_A_U8: return 1; case NL_A_U16: return 2; case NL_A_U32: return 4; case NL_A_U64: return 8; case NL_A_STRING: return MAXUINT16; case NL_A_FLAG: return SIZE_MAX; case NL_A_NESTED: return SIZE_MAX; case N_NL_ATTR_TYPES: default: OVS_LOG_WARN("Unsupprted attribute type: %d", type); ASSERT(0); } /* To keep compiler happy */ return 0; } /* Netlink attribute iteration. */ /* * --------------------------------------------------------------------------- * Returns the next attribute. * --------------------------------------------------------------------------- */ PNL_ATTR NlAttrNext(const PNL_ATTR nla) { return (PNL_ATTR)((UINT8 *)nla + NLA_ALIGN(nla->nlaLen)); } /* * -------------------------------------------------------------------------- * Returns the bits of 'nla->nlaType' that are significant for determining * its type. * -------------------------------------------------------------------------- */ UINT16 NlAttrType(const PNL_ATTR nla) { return nla->nlaType & NLA_TYPE_MASK; } /* * -------------------------------------------------------------------------- * Returns the netlink attribute data. * -------------------------------------------------------------------------- */ PVOID NlAttrData(const PNL_ATTR nla) { return ((PCHAR)nla + NLA_HDRLEN); } /* * --------------------------------------------------------------------------- * Returns the number of bytes in the payload of attribute 'nla'. * --------------------------------------------------------------------------- */ UINT32 NlAttrGetSize(const PNL_ATTR nla) { return nla->nlaLen - NLA_HDRLEN; } /* * --------------------------------------------------------------------------- * Returns the first byte in the payload of attribute 'nla'. * --------------------------------------------------------------------------- */ const PVOID NlAttrGet(const PNL_ATTR nla) { ASSERT(nla->nlaLen >= NLA_HDRLEN); return nla + 1; } /* * --------------------------------------------------------------------------- * Asserts that 'nla''s payload is at least 'size' bytes long, and returns the * first byte of the payload. * --------------------------------------------------------------------------- */ const PVOID NlAttrGetUnspec(const PNL_ATTR nla, UINT32 size) { UNREFERENCED_PARAMETER(size); ASSERT(nla->nlaLen >= NLA_HDRLEN + size); return nla + 1; } /* * --------------------------------------------------------------------------- * Returns the 64-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 8 bytes long. * --------------------------------------------------------------------------- */ BE64 NlAttrGetBe64(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, BE64); } /* * --------------------------------------------------------------------------- * Returns the 32-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 4 bytes long. * --------------------------------------------------------------------------- */ BE32 NlAttrGetBe32(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, BE32); } /* * --------------------------------------------------------------------------- * Returns the 16-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 2 bytes long. * --------------------------------------------------------------------------- */ BE16 NlAttrGetBe16(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, BE16); } /* * --------------------------------------------------------------------------- * Returns the 8-bit network byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 1 byte long. * --------------------------------------------------------------------------- */ BE8 NlAttrGetBe8(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, BE8); } /* * --------------------------------------------------------------------------- * Returns the 8-bit value in 'nla''s payload. * --------------------------------------------------------------------------- */ UINT8 NlAttrGetU8(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, UINT8); } /* * --------------------------------------------------------------------------- * Returns the 16-bit host byte order value in 'nla''s payload. * Asserts that 'nla''s payload is at least 2 bytes long. * --------------------------------------------------------------------------- */ UINT16 NlAttrGetU16(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, UINT16); } /* * --------------------------------------------------------------------------- * Returns the 32-bit host byte order value in 'nla''s payload. * Asserts that 'nla''s payload is at least 4 bytes long. * --------------------------------------------------------------------------- */ UINT32 NlAttrGetU32(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, UINT32); } /* * --------------------------------------------------------------------------- * Returns the 64-bit host byte order value in 'nla''s payload. * Asserts that 'nla''s payload is at least 8 bytes long. * --------------------------------------------------------------------------- */ UINT64 NlAttrGetU64(const PNL_ATTR nla) { return NL_ATTR_GET_AS(nla, UINT64); } /* * --------------------------------------------------------------------------- * Validate the netlink attribute against the policy * --------------------------------------------------------------------------- */ BOOLEAN NlAttrValidate(const PNL_ATTR nla, const PNL_POLICY policy) { UINT32 minLen; UINT32 maxLen; UINT32 len; BOOLEAN ret = FALSE; if ((policy->type == NL_A_NO_ATTR) || (policy->type == NL_A_VAR_LEN) || (policy->type == NL_A_NESTED)) { /* Do not validate anything for attributes of type var length */ ret = TRUE; goto done; } /* Figure out min and max length. */ minLen = policy->minLen; if (!minLen) { minLen = NlAttrMinLen(policy->type); } maxLen = policy->maxLen; if (!maxLen) { maxLen = NlAttrMaxLen(policy->type); } /* Verify length. */ len = NlAttrGetSize(nla); if (len < minLen || len > maxLen) { OVS_LOG_WARN("Attribute: %p, len: %d, not in valid range, " "min: %d, max: %d", nla, len, minLen, maxLen); goto done; } /* Strings must be null terminated and must not have embedded nulls. */ if (policy->type == NL_A_STRING) { if (((PCHAR) nla)[nla->nlaLen - 1]) { OVS_LOG_WARN("Attributes %p lacks null at the end", nla); goto done; } if (memchr(nla + 1, '\0', len - 1) != NULL) { OVS_LOG_WARN("Attributes %p has bad length", nla); goto done; } } ret = TRUE; done: return ret; } /* * --------------------------------------------------------------------------- * Returns an attribute of type 'type' from a series of * attributes. * --------------------------------------------------------------------------- */ const PNL_ATTR NlAttrFind__(const PNL_ATTR attrs, UINT32 size, UINT16 type) { PNL_ATTR iter = NULL; PNL_ATTR ret = NULL; UINT32 left; NL_ATTR_FOR_EACH (iter, left, attrs, size) { if (NlAttrType(iter) == type) { ret = iter; goto done; } } done: return ret; } /* * --------------------------------------------------------------------------- * Returns the first Netlink attribute within 'nla' with the specified * 'type'. * * This function does not validate the attribute's length. * --------------------------------------------------------------------------- */ const PNL_ATTR NlAttrFindNested(const PNL_ATTR nla, UINT16 type) { return NlAttrFind__((const PNL_ATTR)(NlAttrGet(nla)), NlAttrGetSize(nla), type); } /* *---------------------------------------------------------------------------- * Parses the netlink message at a given offset (attrOffset) * as a series of attributes. A pointer to the attribute with type * 'type' is stored in attrs at index 'type'. policy is used to define the * attribute type validation parameters. * 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN + OVS_HEADER * * Returns BOOLEAN to indicate success/failure. *---------------------------------------------------------------------------- */ BOOLEAN NlAttrParse(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, UINT32 totalAttrLen, const NL_POLICY policy[], const UINT32 numPolicy, PNL_ATTR attrs[], UINT32 numAttrs) { PNL_ATTR nla; UINT32 left; UINT32 iter; BOOLEAN ret = FALSE; UINT32 numPolicyAttr = MIN(numPolicy, numAttrs); RtlZeroMemory(attrs, numAttrs * sizeof *attrs); /* There is nothing to parse */ if (!(NlMsgAttrsLen(nlMsg))) { ret = TRUE; goto done; } if ((NlMsgSize(nlMsg) < attrOffset)) { OVS_LOG_WARN("No attributes in nlMsg: %p at offset: %d", nlMsg, attrOffset); goto done; } NL_ATTR_FOR_EACH (nla, left, NlMsgAt(nlMsg, attrOffset), totalAttrLen) { UINT16 type = NlAttrType(nla); if (type < numPolicyAttr && policy[type].type != NL_A_NO_ATTR) { /* Typecasting to keep the compiler happy */ const PNL_POLICY e = (const PNL_POLICY)(&policy[type]); if (!NlAttrValidate(nla, e)) { goto done; } if (attrs[type]) { OVS_LOG_WARN("Duplicate attribute in nlMsg: %p, " "type: %u", nlMsg, type); } attrs[type] = nla; } } if (left) { OVS_LOG_ERROR("Attributes followed by garbage"); goto done; } for (iter = 0; iter < numPolicyAttr; iter++) { const PNL_POLICY e = (const PNL_POLICY)(&policy[iter]); if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[iter]) { OVS_LOG_ERROR("Required attr:%d missing", iter); goto done; } } ret = TRUE; done: return ret; } /* *---------------------------------------------------------------------------- * Parses the netlink message for nested attributes. attrOffset must be the * offset of nla which is the header of the nested attribute series. * Refer nl_parse_nested for more details. * * Returns BOOLEAN to indicate success/failure. *---------------------------------------------------------------------------- */ BOOLEAN NlAttrParseNested(const PNL_MSG_HDR nlMsg, UINT32 attrOffset, UINT32 totalAttrLen, const NL_POLICY policy[], const UINT32 numPolicy, PNL_ATTR attrs[], UINT32 numAttrs) { return NlAttrParse(nlMsg, attrOffset + NLA_HDRLEN, totalAttrLen - NLA_HDRLEN, policy, numPolicy, attrs, numAttrs); } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/IpHelper.c0000644000000000000000000000013213534540071022326 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.421850772 openvswitch-2.5.9/datapath-windows/ovsext/IpHelper.c0000644000175000017500000014432713534540071024027 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "IpHelper.h" #include "Switch.h" #include "Jhash.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_IPHELPER #include "Debug.h" /* * Fow now, we assume only one internal adapter */ KSTART_ROUTINE OvsStartIpHelper; /* * Only when the internal IP is configured and virtual * internal port is connected, the IP helper request can be * queued. */ static BOOLEAN ovsInternalIPConfigured; static BOOLEAN ovsInternalAdapterUp; static GUID ovsInternalNetCfgId; static MIB_IF_ROW2 ovsInternalRow; static MIB_IPINTERFACE_ROW ovsInternalIPRow; /* we only keep one internal IP for reference, it will not be used for * determining SRC IP of Tunnel */ static UINT32 ovsInternalIP; /* * FWD_ENTRY --------> IPFORWARD_ENTRY * | * |--------------------------------------> IPENIGH_ENTRY * * IPFORWARD_ENTRY ------> FWD_ENTRY LIST with same IPFORWARD * * IPNEIGH_ENTRY ------> FWD_ENTRY LIST with same IPNEIGH * */ static PLIST_ENTRY ovsFwdHashTable; // based on DST IP static PLIST_ENTRY ovsRouteHashTable; // based on DST PREFIX static PLIST_ENTRY ovsNeighHashTable; // based on DST IP static LIST_ENTRY ovsSortedIPNeighList; static UINT32 ovsNumFwdEntries; static PNDIS_RW_LOCK_EX ovsTableLock; static NDIS_SPIN_LOCK ovsIpHelperLock; static LIST_ENTRY ovsIpHelperRequestList; static UINT32 ovsNumIpHelperRequests; static HANDLE ipInterfaceNotificationHandle; static HANDLE ipRouteNotificationHandle; static HANDLE unicastIPNotificationHandle; static OVS_IP_HELPER_THREAD_CONTEXT ovsIpHelperThreadContext; static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix); static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf); static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr); static VOID OvsCleanupIpHelperRequestList(VOID); static VOID OvsCleanupFwdTable(VOID); static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn); static VOID OvsDumpIfRow(PMIB_IF_ROW2 ifRow) { OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ifRow->InterfaceLuid.Info.NetLuidIndex, ifRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ifRow->InterfaceIndex); OVS_LOG_INFO("Interface GUID: %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", ifRow->InterfaceGuid.Data1, ifRow->InterfaceGuid.Data2, ifRow->InterfaceGuid.Data3, *(UINT16 *)ifRow->InterfaceGuid.Data4, ifRow->InterfaceGuid.Data4[2], ifRow->InterfaceGuid.Data4[3], ifRow->InterfaceGuid.Data4[4], ifRow->InterfaceGuid.Data4[5], ifRow->InterfaceGuid.Data4[6], ifRow->InterfaceGuid.Data4[7]); OVS_LOG_INFO("Perm MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", ifRow->PermanentPhysicalAddress[0], ifRow->PermanentPhysicalAddress[1], ifRow->PermanentPhysicalAddress[2], ifRow->PermanentPhysicalAddress[3], ifRow->PermanentPhysicalAddress[4], ifRow->PermanentPhysicalAddress[5]); } static VOID OvsDumpIfTable(PMIB_IF_TABLE2 ifTable) { PMIB_IF_ROW2 ifRow; UINT32 i; OVS_LOG_INFO("======Number of entries: %d========", ifTable->NumEntries); for (i = 0; i < ifTable->NumEntries; i++) { ifRow = &ifTable->Table[i]; OvsDumpIfRow(ifRow); } } NTSTATUS OvsGetIfEntry(GUID *interfaceGuid, PMIB_IF_ROW2 ifEntry) { NTSTATUS status; PMIB_IF_TABLE2 ifTable; UINT32 i; if (interfaceGuid == NULL || ifEntry == NULL) { return STATUS_INVALID_PARAMETER; } status = GetIfTable2Ex(MibIfTableNormal, &ifTable); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get if table, status: %x", status); return status; } status = STATUS_NOT_FOUND; for (i = 0; i < ifTable->NumEntries; i++) { PMIB_IF_ROW2 ifRow; ifRow = &ifTable->Table[i]; if (!memcmp(interfaceGuid, &ifRow->InterfaceGuid, sizeof (GUID))) { RtlCopyMemory(ifEntry, ifRow, sizeof (MIB_IF_ROW2)); status = STATUS_SUCCESS; OvsDumpIfRow(ifEntry); break; } } FreeMibTable(ifTable); return status; } static VOID OvsDumpIPInterfaceEntry(PMIB_IPINTERFACE_ROW ipRow) { OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex); OVS_LOG_INFO("MaxReassembleSize: %u", ipRow->MaxReassemblySize); } NTSTATUS OvsGetIPInterfaceEntry(NET_LUID luid, PMIB_IPINTERFACE_ROW ipRow) { NTSTATUS status; if (ipRow == NULL) { return STATUS_INVALID_PARAMETER; } ipRow->Family = AF_INET; ipRow->InterfaceLuid.Value = luid.Value; status = GetIpInterfaceEntry(ipRow); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get internal IP Interface mib row, status: %x", status); return status; } OvsDumpIPInterfaceEntry(ipRow); return status; } static VOID OvsDumpIPEntry(PMIB_UNICASTIPADDRESS_ROW ipRow) { UINT32 ipAddr; OVS_LOG_INFO("InterfaceLuid: NetLuidIndex: %d, type: %d", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); OVS_LOG_INFO("InterfaceIndex: %d", ipRow->InterfaceIndex); ASSERT(ipRow->Address.si_family == AF_INET); ipAddr = ipRow->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Unicast Address: %d.%d.%d.%d\n", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, ipAddr >> 24); } NTSTATUS OvsGetIPEntry(NET_LUID interfaceLuid, PMIB_UNICASTIPADDRESS_ROW ipEntry) { PMIB_UNICASTIPADDRESS_TABLE ipTable; NTSTATUS status; UINT32 i; if (ipEntry == NULL || ipEntry == NULL) { return STATUS_INVALID_PARAMETER; } status = GetUnicastIpAddressTable(AF_INET, &ipTable); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get unicast address table, status: %x", status); return status; } status = STATUS_NOT_FOUND; for (i = 0; i < ipTable->NumEntries; i++) { PMIB_UNICASTIPADDRESS_ROW ipRow; ipRow = &ipTable->Table[i]; if (ipRow->InterfaceLuid.Value == interfaceLuid.Value) { RtlCopyMemory(ipEntry, ipRow, sizeof (*ipRow)); OvsDumpIPEntry(ipEntry); status = STATUS_SUCCESS; break; } } FreeMibTable(ipTable); return status; } #ifdef OVS_ENABLE_IPPATH static VOID OvsDumpIPPath(PMIB_IPPATH_ROW ipPath) { UINT32 ipAddr = ipPath->Source.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Source: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Destination: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = ipPath->CurrentNextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("NextHop: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } NTSTATUS OvsGetIPPathEntry(PMIB_IPPATH_ROW ipPath) { NTSTATUS status; UINT32 ipAddr = ipPath->Destination.Ipv4.sin_addr.s_addr; status = GetIpPathEntry(ipPath); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to get IP path to %d.%d.%d.%d, status:%x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } OvsDumpIPPath(ipPath); return status; } #endif static VOID OvsDumpRoute(const SOCKADDR_INET *sourceAddress, const SOCKADDR_INET *destinationAddress, PMIB_IPFORWARD_ROW2 route) { UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Destination: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = sourceAddress->Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Source: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); ipAddr = route->NextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("NextHop: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } NTSTATUS OvsGetRoute(NET_LUID interfaceLuid, const SOCKADDR_INET *destinationAddress, PMIB_IPFORWARD_ROW2 route, SOCKADDR_INET *sourceAddress) { NTSTATUS status; if (destinationAddress == NULL || route == NULL) { return STATUS_INVALID_PARAMETER; } status = GetBestRoute2(&interfaceLuid, 0, NULL, destinationAddress, 0, route, sourceAddress); if (status != STATUS_SUCCESS) { UINT32 ipAddr = destinationAddress->Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to get route to %d.%d.%d.%d, status: %x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } OvsDumpRoute(sourceAddress, destinationAddress, route); return status; } static VOID OvsDumpIPNeigh(PMIB_IPNET_ROW2 ipNeigh) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Neigh: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); OVS_LOG_INFO("MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", ipNeigh->PhysicalAddress[0], ipNeigh->PhysicalAddress[1], ipNeigh->PhysicalAddress[2], ipNeigh->PhysicalAddress[3], ipNeigh->PhysicalAddress[4], ipNeigh->PhysicalAddress[5]); } NTSTATUS OvsGetIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); status = GetIpNetEntry2(ipNeigh); if (status != STATUS_SUCCESS) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to get ARP entry: %d.%d.%d.%d, status: %x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } if (ipNeigh->State == NlnsReachable || ipNeigh->State == NlnsPermanent) { OvsDumpIPNeigh(ipNeigh); return STATUS_SUCCESS; } return STATUS_FWP_TCPIP_NOT_READY; } NTSTATUS OvsResolveIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); status = ResolveIpNetEntry2(ipNeigh, NULL); if (status != STATUS_SUCCESS) { UINT32 ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("Fail to resolve ARP entry: %d.%d.%d.%d, status: %x", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, status); return status; } if (ipNeigh->State == NlnsReachable || ipNeigh->State == NlnsPermanent) { OvsDumpIPNeigh(ipNeigh); return STATUS_SUCCESS; } return STATUS_FWP_TCPIP_NOT_READY; } NTSTATUS OvsGetOrResolveIPNeigh(UINT32 ipAddr, PMIB_IPNET_ROW2 ipNeigh) { NTSTATUS status; ASSERT(ipNeigh); RtlZeroMemory(ipNeigh, sizeof (*ipNeigh)); ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value; ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex; ipNeigh->Address.si_family = AF_INET; ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr; status = OvsGetIPNeighEntry(ipNeigh); if (status != STATUS_SUCCESS) { RtlZeroMemory(ipNeigh, sizeof (*ipNeigh)); ipNeigh->InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value; ipNeigh->InterfaceIndex = ovsInternalRow.InterfaceIndex; ipNeigh->Address.si_family = AF_INET; ipNeigh->Address.Ipv4.sin_addr.s_addr = ipAddr; status = OvsResolveIPNeighEntry(ipNeigh); } return status; } static VOID OvsChangeCallbackIpInterface(PVOID context, PMIB_IPINTERFACE_ROW ipRow, MIB_NOTIFICATION_TYPE notificationType) { UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibParameterNotification: case MibAddInstance: if (ipRow->InterfaceLuid.Info.NetLuidIndex == ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && ipRow->InterfaceLuid.Info.IfType == ovsInternalRow.InterfaceLuid.Info.IfType && ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { /* * Update the IP Interface Row */ NdisAcquireSpinLock(&ovsIpHelperLock); RtlCopyMemory(&ovsInternalIPRow, ipRow, sizeof (PMIB_IPINTERFACE_ROW)); ovsInternalIPConfigured = TRUE; NdisReleaseSpinLock(&ovsIpHelperLock); } OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d is %s", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType, notificationType == MibAddInstance ? "added" : "modified"); break; case MibDeleteInstance: OVS_LOG_INFO("IP Interface with NetLuidIndex: %d, type: %d, deleted", ipRow->InterfaceLuid.Info.NetLuidIndex, ipRow->InterfaceLuid.Info.IfType); if (ipRow->InterfaceLuid.Info.NetLuidIndex == ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && ipRow->InterfaceLuid.Info.IfType == ovsInternalRow.InterfaceLuid.Info.IfType && ipRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { NdisAcquireSpinLock(&ovsIpHelperLock); ovsInternalIPConfigured = FALSE; NdisReleaseSpinLock(&ovsIpHelperLock); OvsCleanupIpHelperRequestList(); OvsCleanupFwdTable(); } break; case MibInitialNotification: OVS_LOG_INFO("Get Initial notification for IP Interface change."); default: return; } } static VOID OvsChangeCallbackIpRoute(PVOID context, PMIB_IPFORWARD_ROW2 ipRoute, MIB_NOTIFICATION_TYPE notificationType) { UINT32 ipAddr, nextHop; UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibAddInstance: ASSERT(ipRoute); ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr; nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d added", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, ipRoute->DestinationPrefix.PrefixLength, nextHop & 0xff, (nextHop >> 8) & 0xff, (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff); break; case MibParameterNotification: case MibDeleteInstance: ASSERT(ipRoute); ipAddr = ipRoute->DestinationPrefix.Prefix.Ipv4.sin_addr.s_addr; nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("IPRoute: To %d.%d.%d.%d/%d through %d.%d.%d.%d %s.", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, ipRoute->DestinationPrefix.PrefixLength, nextHop & 0xff, (nextHop >> 8) & 0xff, (nextHop >> 16) & 0xff, (nextHop >> 24) & 0xff, notificationType == MibDeleteInstance ? "deleted" : "modified"); if (ipRoute->InterfaceLuid.Info.NetLuidIndex == ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && ipRoute->InterfaceLuid.Info.IfType == ovsInternalRow.InterfaceLuid.Info.IfType && ipRoute->InterfaceIndex == ovsInternalRow.InterfaceIndex) { POVS_IPFORWARD_ENTRY ipf; LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipf = OvsLookupIPForwardEntry(&ipRoute->DestinationPrefix); if (ipf != NULL) { OvsRemoveIPForwardEntry(ipf); } NdisReleaseRWLock(ovsTableLock, &lockState); } break; case MibInitialNotification: OVS_LOG_INFO("Get Initial notification for IP Route change."); default: return; } } static VOID OvsChangeCallbackUnicastIpAddress(PVOID context, PMIB_UNICASTIPADDRESS_ROW unicastRow, MIB_NOTIFICATION_TYPE notificationType) { UINT32 ipAddr; UNREFERENCED_PARAMETER(context); switch (notificationType) { case MibParameterNotification: case MibAddInstance: ASSERT(unicastRow); ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; if (unicastRow->InterfaceLuid.Info.NetLuidIndex == ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && unicastRow->InterfaceLuid.Info.IfType == ovsInternalRow.InterfaceLuid.Info.IfType && unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { ovsInternalIP = ipAddr; } OVS_LOG_INFO("IP Address: %d.%d.%d.%d is %s", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff, notificationType == MibAddInstance ? "added": "modified"); break; case MibDeleteInstance: ASSERT(unicastRow); ipAddr = unicastRow->Address.Ipv4.sin_addr.s_addr; OVS_LOG_INFO("IP Address removed: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); if (unicastRow->InterfaceLuid.Info.NetLuidIndex == ovsInternalRow.InterfaceLuid.Info.NetLuidIndex && unicastRow->InterfaceLuid.Info.IfType == ovsInternalRow.InterfaceLuid.Info.IfType && unicastRow->InterfaceIndex == ovsInternalRow.InterfaceIndex) { LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); OvsRemoveAllFwdEntriesWithSrc(ipAddr); NdisReleaseRWLock(ovsTableLock, &lockState); } break; case MibInitialNotification: OVS_LOG_INFO("Get Initial notification for Unicast IP Address change."); default: return; } } static VOID OvsCancelChangeNotification() { if (ipInterfaceNotificationHandle != NULL) { CancelMibChangeNotify2(ipInterfaceNotificationHandle); ipInterfaceNotificationHandle = NULL; } if (ipRouteNotificationHandle != NULL) { CancelMibChangeNotify2(ipRouteNotificationHandle); ipRouteNotificationHandle = NULL; } if (unicastIPNotificationHandle != NULL) { CancelMibChangeNotify2(unicastIPNotificationHandle); unicastIPNotificationHandle = NULL; } } static NTSTATUS OvsRegisterChangeNotification() { NTSTATUS status; status = NotifyIpInterfaceChange(AF_INET, OvsChangeCallbackIpInterface, NULL, TRUE, &ipInterfaceNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Fail to register Notify IP interface change, status:%x.", status); return status; } status = NotifyRouteChange2(AF_INET, OvsChangeCallbackIpRoute, NULL, TRUE, &ipRouteNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Fail to regiter ip route change, status: %x.", status); goto register_cleanup; } status = NotifyUnicastIpAddressChange(AF_INET, OvsChangeCallbackUnicastIpAddress, NULL, TRUE, &unicastIPNotificationHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Fail to regiter unicast ip change, status: %x.", status); } register_cleanup: if (status != STATUS_SUCCESS) { OvsCancelChangeNotification(); } return status; } static POVS_IPNEIGH_ENTRY OvsLookupIPNeighEntry(UINT32 ipAddr) { PLIST_ENTRY link; POVS_IPNEIGH_ENTRY entry; UINT32 hash = OvsJhashWords(&ipAddr, 1, OVS_HASH_BASIS); LIST_FORALL(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], link) { entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, link); if (entry->ipAddr == ipAddr) { return entry; } } return NULL; } static UINT32 OvsHashIPPrefix(PIP_ADDRESS_PREFIX prefix) { UINT64 words = (UINT64)prefix->Prefix.Ipv4.sin_addr.s_addr << 32 | (UINT32)prefix->PrefixLength; return OvsJhashWords((UINT32 *)&words, 2, OVS_HASH_BASIS); } static POVS_IPFORWARD_ENTRY OvsLookupIPForwardEntry(PIP_ADDRESS_PREFIX prefix) { PLIST_ENTRY link; POVS_IPFORWARD_ENTRY ipfEntry; UINT32 hash; ASSERT(prefix->Prefix.si_family == AF_INET); hash = RtlUlongByteSwap(prefix->Prefix.Ipv4.sin_addr.s_addr); ASSERT(prefix->PrefixLength >= 32 || (hash & (((UINT32)1 << (32 - prefix->PrefixLength)) - 1)) == 0); hash = OvsHashIPPrefix(prefix); LIST_FORALL(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], link) { ipfEntry = CONTAINING_RECORD(link, OVS_IPFORWARD_ENTRY, link); if (ipfEntry->prefix.PrefixLength == prefix->PrefixLength && ipfEntry->prefix.Prefix.Ipv4.sin_addr.s_addr == prefix->Prefix.Ipv4.sin_addr.s_addr) { return ipfEntry; } } return NULL; } static POVS_FWD_ENTRY OvsLookupIPFwdEntry(UINT32 dstIp) { PLIST_ENTRY link; POVS_FWD_ENTRY entry; UINT32 hash = OvsJhashWords(&dstIp, 1, OVS_HASH_BASIS); LIST_FORALL(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], link) { entry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); if (entry->info.dstIpAddr == dstIp) { return entry; } } return NULL; } NTSTATUS OvsLookupIPFwdInfo(UINT32 dstIp, POVS_FWD_INFO info) { POVS_FWD_ENTRY entry; LOCK_STATE_EX lockState; NTSTATUS status = STATUS_NOT_FOUND; NdisAcquireRWLockRead(ovsTableLock, &lockState, 0); entry = OvsLookupIPFwdEntry(dstIp); if (entry) { info->value[0] = entry->info.value[0]; info->value[1] = entry->info.value[1]; info->value[2] = entry->info.value[2]; status = STATUS_SUCCESS; } NdisReleaseRWLock(ovsTableLock, &lockState); return status; } static POVS_IPNEIGH_ENTRY OvsCreateIPNeighEntry(PMIB_IPNET_ROW2 ipNeigh) { POVS_IPNEIGH_ENTRY entry; UINT64 timeVal; ASSERT(ipNeigh != NULL); entry = (POVS_IPNEIGH_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_IPNEIGH_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_IPNEIGH_ENTRY)); entry->ipAddr = ipNeigh->Address.Ipv4.sin_addr.s_addr; KeQuerySystemTime((LARGE_INTEGER *)&timeVal); entry->timeout = timeVal + OVS_IPNEIGH_TIMEOUT; RtlCopyMemory(entry->macAddr, ipNeigh->PhysicalAddress, ETH_ADDR_LEN); InitializeListHead(&entry->fwdList); return entry; } static POVS_IPFORWARD_ENTRY OvsCreateIPForwardEntry(PMIB_IPFORWARD_ROW2 ipRoute) { POVS_IPFORWARD_ENTRY entry; ASSERT(ipRoute); entry = (POVS_IPFORWARD_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_IPFORWARD_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_IPFORWARD_ENTRY)); RtlCopyMemory(&entry->prefix, &ipRoute->DestinationPrefix, sizeof (IP_ADDRESS_PREFIX)); entry->nextHop = ipRoute->NextHop.Ipv4.sin_addr.s_addr; InitializeListHead(&entry->fwdList); return entry; } static POVS_FWD_ENTRY OvsCreateFwdEntry(POVS_FWD_INFO fwdInfo) { POVS_FWD_ENTRY entry; entry = (POVS_FWD_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_FWD_ENTRY), OVS_IPHELPER_POOL_TAG); if (entry == NULL) { return NULL; } RtlZeroMemory(entry, sizeof (OVS_FWD_ENTRY)); RtlCopyMemory(&entry->info, fwdInfo, sizeof (OVS_FWD_INFO)); return entry; } static VOID OvsRemoveFwdEntry(POVS_FWD_ENTRY fwdEntry) { POVS_IPFORWARD_ENTRY ipf; POVS_IPNEIGH_ENTRY ipn; ipf = fwdEntry->ipf; ipn = fwdEntry->ipn; RemoveEntryList(&fwdEntry->link); ovsNumFwdEntries--; RemoveEntryList(&fwdEntry->ipfLink); ipf->refCount--; RemoveEntryList(&fwdEntry->ipnLink); ipn->refCount--; if (ipf->refCount == 0) { ASSERT(IsListEmpty(&ipf->fwdList)); RemoveEntryList(&ipf->link); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } if (ipn->refCount == 0) { ASSERT(IsListEmpty(&ipn->fwdList)); RemoveEntryList(&ipn->link); NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); NdisReleaseSpinLock(&ovsIpHelperLock); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } OvsFreeMemoryWithTag(fwdEntry, OVS_IPHELPER_POOL_TAG); } static VOID OvsRemoveIPForwardEntry(POVS_IPFORWARD_ENTRY ipf) { POVS_FWD_ENTRY fwdEntry; PLIST_ENTRY link, next; ipf->refCount++; LIST_FORALL_SAFE(&ipf->fwdList, link, next) { fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink); OvsRemoveFwdEntry(fwdEntry); } ASSERT(ipf->refCount == 1); RemoveEntryList(&ipf->link); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } static VOID OvsRemoveIPNeighEntry(POVS_IPNEIGH_ENTRY ipn) { PLIST_ENTRY link, next; POVS_FWD_ENTRY fwdEntry; ipn->refCount++; LIST_FORALL_SAFE(&ipn->fwdList, link, next) { fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); OvsRemoveFwdEntry(fwdEntry); } if (ipn->refCount == 1) { RemoveEntryList(&ipn->link); NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); NdisReleaseSpinLock(&ovsIpHelperLock); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } } static VOID OvsAddToSortedNeighList(POVS_IPNEIGH_ENTRY ipn) { PLIST_ENTRY link; POVS_IPNEIGH_ENTRY entry; if (!IsListEmpty(&ovsSortedIPNeighList)) { link = ovsSortedIPNeighList.Blink; entry = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); if (entry->timeout > ipn->timeout) { ipn->timeout++; } } InsertTailList(&ovsSortedIPNeighList, &ipn->slink); } static VOID OvsAddIPFwdCache(POVS_FWD_ENTRY fwdEntry, POVS_IPFORWARD_ENTRY ipf, POVS_IPNEIGH_ENTRY ipn) { UINT32 hash; if (ipn->refCount == 0) { NdisAcquireSpinLock(&ovsIpHelperLock); OvsAddToSortedNeighList(ipn); NdisReleaseSpinLock(&ovsIpHelperLock); hash = OvsJhashWords(&ipn->ipAddr, 1, OVS_HASH_BASIS); InsertHeadList(&ovsNeighHashTable[hash & OVS_NEIGH_HASH_TABLE_MASK], &ipn->link); } if (ipf->refCount == 0) { hash = OvsHashIPPrefix(&ipf->prefix); InsertHeadList(&ovsRouteHashTable[hash & OVS_ROUTE_HASH_TABLE_MASK], &ipf->link); } InsertHeadList(&ipf->fwdList, &fwdEntry->ipfLink); ipf->refCount++; fwdEntry->ipf = ipf; InsertHeadList(&ipn->fwdList, &fwdEntry->ipnLink); ipn->refCount++; fwdEntry->ipn = ipn; hash = OvsJhashWords(&fwdEntry->info.dstIpAddr, 1, OVS_HASH_BASIS); InsertHeadList(&ovsFwdHashTable[hash & OVS_FWD_HASH_TABLE_MASK], &fwdEntry->link); ovsNumFwdEntries++; } static VOID OvsRemoveAllFwdEntriesWithSrc(UINT32 ipAddr) { UINT32 i; POVS_FWD_ENTRY fwdEntry; PLIST_ENTRY link, next; for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { LIST_FORALL_SAFE(&ovsFwdHashTable[i], link, next) { fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, link); if (fwdEntry->info.srcIpAddr == ipAddr) { OvsRemoveFwdEntry(fwdEntry); } } } } static VOID OvsCleanupFwdTable(VOID) { PLIST_ENTRY link, next; POVS_IPNEIGH_ENTRY ipn; UINT32 i; LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); if (ovsNumFwdEntries) { LIST_FORALL_SAFE(&ovsSortedIPNeighList, link, next) { ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); OvsRemoveIPNeighEntry(ipn); } } for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { ASSERT(IsListEmpty(&ovsFwdHashTable[i])); } for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) { ASSERT(IsListEmpty(&ovsRouteHashTable[i])); } NdisReleaseRWLock(ovsTableLock, &lockState); } static VOID OvsCleanupIpHelperRequestList(VOID) { LIST_ENTRY list; PLIST_ENTRY next, link; POVS_IP_HELPER_REQUEST request; NdisAcquireSpinLock(&ovsIpHelperLock); if (ovsNumIpHelperRequests == 0) { NdisReleaseSpinLock(&ovsIpHelperLock); return; } InitializeListHead(&list); OvsAppendList(&list, &ovsIpHelperRequestList); ovsNumIpHelperRequests = 0; NdisReleaseSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&list, link, next) { request = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (request->command == OVS_IP_HELPER_FWD_REQUEST && request->fwdReq.cb) { request->fwdReq.cb(request->fwdReq.nbl, request->fwdReq.inPort, &request->fwdReq.tunnelKey, request->fwdReq.cbData1, request->fwdReq.cbData2, STATUS_DEVICE_NOT_READY, NULL); } OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); } } static VOID OvsWakeupIPHelper(VOID) { KeSetEvent(&ovsIpHelperThreadContext.event, 0, FALSE); } VOID OvsInternalAdapterDown(VOID) { NdisAcquireSpinLock(&ovsIpHelperLock); ovsInternalAdapterUp = FALSE; ovsInternalIPConfigured = FALSE; NdisReleaseSpinLock(&ovsIpHelperLock); OvsCleanupIpHelperRequestList(); OvsCleanupFwdTable(); } VOID OvsInternalAdapterUp(GUID *netCfgInstanceId) { POVS_IP_HELPER_REQUEST request; RtlCopyMemory(&ovsInternalNetCfgId, netCfgInstanceId, sizeof (GUID)); RtlZeroMemory(&ovsInternalRow, sizeof (MIB_IF_ROW2)); request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag( sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG); if (request == NULL) { OVS_LOG_ERROR("Fail to initialize Internal Adapter"); return; } RtlZeroMemory(request, sizeof (OVS_IP_HELPER_REQUEST)); request->command = OVS_IP_HELPER_INTERNAL_ADAPTER_UP; NdisAcquireSpinLock(&ovsIpHelperLock); ovsInternalAdapterUp = TRUE; InsertHeadList(&ovsIpHelperRequestList, &request->link); ovsNumIpHelperRequests++; if (ovsNumIpHelperRequests == 1) { OvsWakeupIPHelper(); } NdisReleaseSpinLock(&ovsIpHelperLock); } static VOID OvsHandleInternalAdapterUp(POVS_IP_HELPER_REQUEST request) { NTSTATUS status; MIB_UNICASTIPADDRESS_ROW ipEntry; GUID *netCfgInstanceId = &ovsInternalNetCfgId; OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); status = OvsGetIfEntry(&ovsInternalNetCfgId, &ovsInternalRow); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Fali to get IF entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", netCfgInstanceId->Data1, netCfgInstanceId->Data2, netCfgInstanceId->Data3, *(UINT16 *)netCfgInstanceId->Data4, netCfgInstanceId->Data4[2], netCfgInstanceId->Data4[3], netCfgInstanceId->Data4[4], netCfgInstanceId->Data4[5], netCfgInstanceId->Data4[6], netCfgInstanceId->Data4[7]); return; } status = OvsGetIPInterfaceEntry(ovsInternalRow.InterfaceLuid, &ovsInternalIPRow); if (status == STATUS_SUCCESS) { NdisAcquireSpinLock(&ovsIpHelperLock); ovsInternalIPConfigured = TRUE; NdisReleaseSpinLock(&ovsIpHelperLock); } else { return; } status = OvsGetIPEntry(ovsInternalRow.InterfaceLuid, &ipEntry); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fali to get IP entry for internal port with GUID" " %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", netCfgInstanceId->Data1, netCfgInstanceId->Data2, netCfgInstanceId->Data3, *(UINT16 *)netCfgInstanceId->Data4, netCfgInstanceId->Data4[2], netCfgInstanceId->Data4[3], netCfgInstanceId->Data4[4], netCfgInstanceId->Data4[5], netCfgInstanceId->Data4[6], netCfgInstanceId->Data4[7]); } } static NTSTATUS OvsEnqueueIpHelperRequest(POVS_IP_HELPER_REQUEST request) { NdisAcquireSpinLock(&ovsIpHelperLock); if (ovsInternalAdapterUp == FALSE || ovsInternalIPConfigured == FALSE) { NdisReleaseSpinLock(&ovsIpHelperLock); OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); return STATUS_NDIS_ADAPTER_NOT_READY; } else { InsertHeadList(&ovsIpHelperRequestList, &request->link); ovsNumIpHelperRequests++; if (ovsNumIpHelperRequests == 1) { OvsWakeupIPHelper(); } NdisReleaseSpinLock(&ovsIpHelperLock); return STATUS_SUCCESS; } } NTSTATUS OvsFwdIPHelperRequest(PNET_BUFFER_LIST nbl, UINT32 inPort, const OvsIPv4TunnelKey *tunnelKey, OvsIPHelperCallback cb, PVOID cbData1, PVOID cbData2) { POVS_IP_HELPER_REQUEST request; request = (POVS_IP_HELPER_REQUEST)OvsAllocateMemoryWithTag( sizeof(OVS_IP_HELPER_REQUEST), OVS_IPHELPER_POOL_TAG); if (request == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } request->command = OVS_IP_HELPER_FWD_REQUEST; request->fwdReq.nbl = nbl; request->fwdReq.inPort = inPort; RtlCopyMemory(&request->fwdReq.tunnelKey, tunnelKey, sizeof (*tunnelKey)); request->fwdReq.cb = cb; request->fwdReq.cbData1 = cbData1; request->fwdReq.cbData2 = cbData2; return OvsEnqueueIpHelperRequest(request); } static VOID OvsHandleFwdRequest(POVS_IP_HELPER_REQUEST request) { SOCKADDR_INET dst, src; NTSTATUS status = STATUS_SUCCESS; MIB_IPFORWARD_ROW2 ipRoute; MIB_IPNET_ROW2 ipNeigh; OVS_FWD_INFO fwdInfo; UINT32 ipAddr; UINT32 srcAddr; POVS_FWD_ENTRY fwdEntry = NULL; POVS_IPFORWARD_ENTRY ipf = NULL; POVS_IPNEIGH_ENTRY ipn = NULL; LOCK_STATE_EX lockState; BOOLEAN newIPF = FALSE; BOOLEAN newIPN = FALSE; BOOLEAN newFWD = FALSE; status = OvsLookupIPFwdInfo(request->fwdReq.tunnelKey.dst, &fwdInfo); if (status == STATUS_SUCCESS) { goto fwd_handle_nbl; } /* find IPRoute */ RtlZeroMemory(&dst, sizeof(dst)); RtlZeroMemory(&src, sizeof(src)); RtlZeroMemory(&ipRoute, sizeof (MIB_IPFORWARD_ROW2)); dst.si_family = AF_INET; dst.Ipv4.sin_addr.s_addr = request->fwdReq.tunnelKey.dst; status = OvsGetRoute(ovsInternalRow.InterfaceLuid, &dst, &ipRoute, &src); if (status != STATUS_SUCCESS) { goto fwd_handle_nbl; } srcAddr = src.Ipv4.sin_addr.s_addr; /* find IPNeigh */ ipAddr = ipRoute.NextHop.Ipv4.sin_addr.s_addr; if (ipAddr != 0) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn) { goto fwd_request_done; } NdisReleaseRWLock(ovsTableLock, &lockState); } RtlZeroMemory(&ipNeigh, sizeof (ipNeigh)); ipNeigh.InterfaceLuid.Value = ovsInternalRow.InterfaceLuid.Value; if (ipAddr == 0) { ipAddr = request->fwdReq.tunnelKey.dst; } status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh); if (status != STATUS_SUCCESS) { goto fwd_handle_nbl; } NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); fwd_request_done: /* * Initialize ipf */ ipf = OvsLookupIPForwardEntry(&ipRoute.DestinationPrefix); if (ipf == NULL) { ipf = OvsCreateIPForwardEntry(&ipRoute); if (ipf == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newIPF = TRUE; } else { PLIST_ENTRY link; link = ipf->fwdList.Flink; fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipfLink); srcAddr = fwdEntry->info.srcIpAddr; } /* * initialize ipn */ if (ipn == NULL) { ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { ipn = OvsCreateIPNeighEntry(&ipNeigh); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newIPN = TRUE; } } /* * initialize fwdEntry */ fwdInfo.dstIpAddr = request->fwdReq.tunnelKey.dst; fwdInfo.srcIpAddr = srcAddr; RtlCopyMemory(fwdInfo.dstMacAddr, ipn->macAddr, ETH_ADDR_LEN); RtlCopyMemory(fwdInfo.srcMacAddr, ovsInternalRow.PhysicalAddress, ETH_ADDR_LEN); fwdInfo.srcPortNo = request->fwdReq.inPort; fwdEntry = OvsCreateFwdEntry(&fwdInfo); if (fwdEntry == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); status = STATUS_INSUFFICIENT_RESOURCES; goto fwd_handle_nbl; } newFWD = TRUE; /* * Cache the result */ OvsAddIPFwdCache(fwdEntry, ipf, ipn); NdisReleaseRWLock(ovsTableLock, &lockState); fwd_handle_nbl: if (status != STATUS_SUCCESS) { if (newFWD) { ASSERT(fwdEntry != NULL); OvsFreeMemoryWithTag(fwdEntry, OVS_IPHELPER_POOL_TAG); } if (newIPF) { ASSERT(ipf && ipf->refCount == 0); OvsFreeMemoryWithTag(ipf, OVS_IPHELPER_POOL_TAG); } if (newIPN) { ASSERT(ipn && ipn->refCount == 0); OvsFreeMemoryWithTag(ipn, OVS_IPHELPER_POOL_TAG); } ipAddr = request->fwdReq.tunnelKey.dst; OVS_LOG_INFO("Fail to handle IP helper request for dst: %d.%d.%d.%d", ipAddr & 0xff, (ipAddr >> 8) & 0xff, (ipAddr >> 16) & 0xff, (ipAddr >> 24) & 0xff); } if (request->fwdReq.cb) { request->fwdReq.cb(request->fwdReq.nbl, request->fwdReq.inPort, &request->fwdReq.tunnelKey, request->fwdReq.cbData1, request->fwdReq.cbData2, status, status == STATUS_SUCCESS ? &fwdInfo : NULL); } OvsFreeMemoryWithTag(request, OVS_IPHELPER_POOL_TAG); } static VOID OvsUpdateIPNeighEntry(UINT32 ipAddr, PMIB_IPNET_ROW2 ipNeigh, NTSTATUS status) { UINT64 timeVal; POVS_IPNEIGH_ENTRY ipn; LOCK_STATE_EX lockState; KeQuerySystemTime((LARGE_INTEGER *)&timeVal); /* * if mac changed, update all relevant fwdEntry */ if (status != STATUS_SUCCESS) { NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); } else { NdisAcquireRWLockRead(ovsTableLock, &lockState, 0); } ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); return; } if (status != STATUS_SUCCESS) { OvsRemoveIPNeighEntry(ipn); NdisReleaseRWLock(ovsTableLock, &lockState); return; } if (memcmp((const PVOID)ipn->macAddr, (const PVOID)ipNeigh->PhysicalAddress, (size_t)ETH_ADDR_LEN)) { PLIST_ENTRY link; POVS_FWD_ENTRY fwdEntry; NdisReleaseRWLock(ovsTableLock, &lockState); /* * need update, release and acquire write lock * This is not the common case. */ NdisAcquireRWLockWrite(ovsTableLock, &lockState, 0); ipn = OvsLookupIPNeighEntry(ipAddr); if (ipn == NULL) { NdisReleaseRWLock(ovsTableLock, &lockState); return; } LIST_FORALL(&ipn->fwdList, link) { fwdEntry = CONTAINING_RECORD(link, OVS_FWD_ENTRY, ipnLink); RtlCopyMemory(fwdEntry->info.dstMacAddr, ipNeigh->PhysicalAddress, ETH_ADDR_LEN); } } /* * update timeout and move to the end of * the sorted list */ NdisAcquireSpinLock(&ovsIpHelperLock); RemoveEntryList(&ipn->slink); ipn->timeout = timeVal + OVS_IPNEIGH_TIMEOUT; OvsAddToSortedNeighList(ipn); NdisReleaseSpinLock(&ovsIpHelperLock); NdisReleaseRWLock(ovsTableLock, &lockState); } static VOID OvsHandleIPNeighTimeout(UINT32 ipAddr) { MIB_IPNET_ROW2 ipNeigh; NTSTATUS status; status = OvsGetOrResolveIPNeigh(ipAddr, &ipNeigh); OvsUpdateIPNeighEntry(ipAddr, &ipNeigh, status); } /* *---------------------------------------------------------------------------- * IP Helper system threash handle following request * 1. Intialize Internal port row when internal port is connected * 2. Handle FWD request * 3. Handle IP Neigh timeout * * IP Interface, unicast address, and IP route change will be handled * by the revelant callback. *---------------------------------------------------------------------------- */ VOID OvsStartIpHelper(PVOID data) { POVS_IP_HELPER_THREAD_CONTEXT context = (POVS_IP_HELPER_THREAD_CONTEXT)data; POVS_IP_HELPER_REQUEST req; POVS_IPNEIGH_ENTRY ipn; PLIST_ENTRY link; UINT64 timeVal, timeout; OVS_LOG_INFO("Start the IP Helper Thread, context: %p", context); NdisAcquireSpinLock(&ovsIpHelperLock); while (!context->exit) { timeout = 0; while (!IsListEmpty(&ovsIpHelperRequestList)) { if (context->exit) { goto ip_helper_wait; } link = ovsIpHelperRequestList.Flink; RemoveEntryList(link); NdisReleaseSpinLock(&ovsIpHelperLock); req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); switch (req->command) { case OVS_IP_HELPER_INTERNAL_ADAPTER_UP: OvsHandleInternalAdapterUp(req); break; case OVS_IP_HELPER_FWD_REQUEST: OvsHandleFwdRequest(req); break; default: OvsFreeMemoryWithTag(req, OVS_IPHELPER_POOL_TAG); } NdisAcquireSpinLock(&ovsIpHelperLock); } /* for now, let us hold the lock here, if this cause any issue * we will change to use IpHelper lock only to protect * IPN */ while (!IsListEmpty(&ovsSortedIPNeighList)) { UINT32 ipAddr; if (context->exit) { goto ip_helper_wait; } link = ovsSortedIPNeighList.Flink; ipn = CONTAINING_RECORD(link, OVS_IPNEIGH_ENTRY, slink); KeQuerySystemTime((LARGE_INTEGER *)&timeVal); if (ipn->timeout > timeVal) { timeout = ipn->timeout; break; } ipAddr = ipn->ipAddr; NdisReleaseSpinLock(&ovsIpHelperLock); OvsHandleIPNeighTimeout(ipAddr); NdisAcquireSpinLock(&ovsIpHelperLock); } if (!IsListEmpty(&ovsIpHelperRequestList)) { continue; } ip_helper_wait: if (context->exit) { break; } KeClearEvent(&context->event); NdisReleaseSpinLock(&ovsIpHelperLock); KeWaitForSingleObject(&context->event, Executive, KernelMode, FALSE, (LARGE_INTEGER *)&timeout); NdisAcquireSpinLock(&ovsIpHelperLock); } NdisReleaseSpinLock(&ovsIpHelperLock); OvsCleanupFwdTable(); OvsCleanupIpHelperRequestList(); OVS_LOG_INFO("Terminating the OVS IP Helper system thread"); PsTerminateSystemThread(STATUS_SUCCESS); } NTSTATUS OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle) { NTSTATUS status; HANDLE threadHandle; UINT32 i; ovsFwdHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_FWD_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); ovsRouteHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_ROUTE_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); ovsNeighHashTable = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_NEIGH_HASH_TABLE_SIZE, OVS_IPHELPER_POOL_TAG); RtlZeroMemory(&ovsInternalRow, sizeof(MIB_IF_ROW2)); RtlZeroMemory(&ovsInternalIPRow, sizeof (MIB_IPINTERFACE_ROW)); ovsInternalIP = 0; ovsInternalAdapterUp = FALSE; InitializeListHead(&ovsSortedIPNeighList); ovsTableLock = NdisAllocateRWLock(ndisFilterHandle); NdisAllocateSpinLock(&ovsIpHelperLock); InitializeListHead(&ovsIpHelperRequestList); ovsNumIpHelperRequests = 0; ipInterfaceNotificationHandle = NULL; ipRouteNotificationHandle = NULL; unicastIPNotificationHandle = NULL; if (ovsFwdHashTable == NULL || ovsRouteHashTable == NULL || ovsNeighHashTable == NULL || ovsTableLock == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto init_cleanup; } for (i = 0; i < OVS_FWD_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsFwdHashTable[i]); } for (i = 0; i < OVS_ROUTE_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsRouteHashTable[i]); } for (i = 0; i < OVS_NEIGH_HASH_TABLE_SIZE; i++) { InitializeListHead(&ovsNeighHashTable[i]); } KeInitializeEvent(&ovsIpHelperThreadContext.event, NotificationEvent, FALSE); status = OvsRegisterChangeNotification(); ovsIpHelperThreadContext.exit = 0; if (status == STATUS_SUCCESS) { status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL, NULL, OvsStartIpHelper, &ovsIpHelperThreadContext); if (status != STATUS_SUCCESS) { goto init_cleanup; } ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode, &ovsIpHelperThreadContext.threadObject, NULL); ZwClose(threadHandle); } init_cleanup: if (status != STATUS_SUCCESS) { OvsCancelChangeNotification(); if (ovsFwdHashTable) { OvsFreeMemoryWithTag(ovsFwdHashTable, OVS_IPHELPER_POOL_TAG); ovsFwdHashTable = NULL; } if (ovsRouteHashTable) { OvsFreeMemoryWithTag(ovsRouteHashTable, OVS_IPHELPER_POOL_TAG); ovsRouteHashTable = NULL; } if (ovsNeighHashTable) { OvsFreeMemoryWithTag(ovsNeighHashTable, OVS_IPHELPER_POOL_TAG); ovsNeighHashTable = NULL; } if (ovsTableLock) { NdisFreeRWLock(ovsTableLock); ovsTableLock = NULL; } NdisFreeSpinLock(&ovsIpHelperLock); } return STATUS_SUCCESS; } VOID OvsCleanupIpHelper(VOID) { OvsCancelChangeNotification(); NdisAcquireSpinLock(&ovsIpHelperLock); ovsIpHelperThreadContext.exit = 1; OvsWakeupIPHelper(); NdisReleaseSpinLock(&ovsIpHelperLock); KeWaitForSingleObject(ovsIpHelperThreadContext.threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(ovsIpHelperThreadContext.threadObject); OvsFreeMemoryWithTag(ovsFwdHashTable, OVS_IPHELPER_POOL_TAG); OvsFreeMemoryWithTag(ovsRouteHashTable, OVS_IPHELPER_POOL_TAG); OvsFreeMemoryWithTag(ovsNeighHashTable, OVS_IPHELPER_POOL_TAG); NdisFreeRWLock(ovsTableLock); NdisFreeSpinLock(&ovsIpHelperLock); } VOID OvsCancelFwdIpHelperRequest(PNET_BUFFER_LIST nbl) { PLIST_ENTRY link, next; POVS_IP_HELPER_REQUEST req; LIST_ENTRY list; InitializeListHead(&list); NdisAcquireSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&ovsIpHelperRequestList, link, next) { req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (req->command == OVS_IP_HELPER_FWD_REQUEST && (nbl == NULL || req->fwdReq.nbl == nbl)) { RemoveEntryList(link); InsertHeadList(&list, link); if (nbl != NULL) { break; } } } NdisReleaseSpinLock(&ovsIpHelperLock); LIST_FORALL_SAFE(&list, link, next) { req = CONTAINING_RECORD(link, OVS_IP_HELPER_REQUEST, link); if (req->fwdReq.cb) { req->fwdReq.cb(req->fwdReq.nbl, req->fwdReq.inPort, &req->fwdReq.tunnelKey, req->fwdReq.cbData1, req->fwdReq.cbData2, STATUS_DEVICE_NOT_READY, NULL); } OvsFreeMemoryWithTag(req, OVS_IPHELPER_POOL_TAG); } } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Ethernet.h0000644000000000000000000000013113534540071022400 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 30 ctime=1567801424.409850683 openvswitch-2.5.9/datapath-windows/ovsext/Ethernet.h0000644000175000017500000003643013534540071024075 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ETHERNET_H_ #define __ETHERNET_H_ 1 #define ETH_LADRF_LEN 2 #define ETH_ADDR_LENGTH 6 typedef UINT8 Eth_Address[ETH_ADDR_LENGTH]; #define ETH_ADDR_FMT_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define ETH_ADDR_FMT_ARGS(a) ((UINT8 *)a)[0], ((UINT8 *)a)[1], ((UINT8 *)a)[2], \ ((UINT8 *)a)[3], ((UINT8 *)a)[4], ((UINT8 *)a)[5] #define ETH_MAX_EXACT_MULTICAST_ADDRS 32 typedef enum Eth_RxMode { ETH_FILTER_UNICAST = 0x0001, /* pass unicast (directed) frames */ ETH_FILTER_MULTICAST = 0x0002, /* pass some multicast frames */ ETH_FILTER_ALLMULTI = 0x0004, /* pass *all* multicast frames */ ETH_FILTER_BROADCAST = 0x0008, /* pass broadcast frames */ ETH_FILTER_PROMISC = 0x0010, /* pass all frames (ie no filter) */ ETH_FILTER_USE_LADRF = 0x0020, /* use the LADRF for multicast filtering */ ETH_FILTER_SINK = 0x10000 /* pass not-matched unicast frames */ } Eth_RxMode; /* filter flags printf helpers */ #define ETH_FILTER_FLAG_FMT_STR "%s%s%s%s%s%s%s" #define ETH_FILTER_FLAG_FMT_ARGS(f) (f) & ETH_FILTER_UNICAST ? " UNICAST" : "", \ (f) & ETH_FILTER_MULTICAST ? " MULTICAST" : "", \ (f) & ETH_FILTER_ALLMULTI ? " ALLMULTI" : "", \ (f) & ETH_FILTER_BROADCAST ? " BROADCAST" : "", \ (f) & ETH_FILTER_PROMISC ? " PROMISC" : "", \ (f) & ETH_FILTER_USE_LADRF ? " USE_LADRF" : "", \ (f) & ETH_FILTER_SINK ? " SINK" : "" /* Ethernet header type */ typedef enum { ETH_HEADER_TYPE_DIX, ETH_HEADER_TYPE_802_1PQ, ETH_HEADER_TYPE_802_3, ETH_HEADER_TYPE_802_1PQ_802_3, } Eth_HdrType; /* DIX type fields we care about */ typedef enum { ETH_TYPE_IPV4 = 0x0800, ETH_TYPE_IPV6 = 0x86DD, ETH_TYPE_ARP = 0x0806, ETH_TYPE_RARP = 0x8035, ETH_TYPE_LLDP = 0x88CC, ETH_TYPE_CDP = 0x2000, ETH_TYPE_802_1PQ = 0x8100, // not really a DIX type, but used as such ETH_TYPE_LLC = 0xFFFF, // 0xFFFF is IANA reserved, used to mark LLC } Eth_DixType; typedef enum { ETH_TYPE_IPV4_NBO = 0x0008, ETH_TYPE_IPV6_NBO = 0xDD86, ETH_TYPE_ARP_NBO = 0x0608, ETH_TYPE_RARP_NBO = 0x3580, ETH_TYPE_LLDP_NBO = 0xCC88, ETH_TYPE_CDP_NBO = 0x0020, ETH_TYPE_AKIMBI_NBO = 0xDE88, ETH_TYPE_802_1PQ_NBO = 0x0081, // not really a DIX type, but used as such } Eth_DixTypeNBO; /* low two bits of the LLC control byte */ typedef enum { ETH_LLC_CONTROL_IFRAME = 0x0, // both 0x0 and 0x2, only low bit of 0 needed ETH_LLC_CONTROL_SFRAME = 0x1, ETH_LLC_CONTROL_UFRAME = 0x3, } Eth_LLCControlBits; #define ETH_LLC_CONTROL_UFRAME_MASK (0x3) typedef struct Eth_DIX { UINT16 typeNBO; // indicates the higher level protocol } Eth_DIX; /* * LLC header come in two varieties: 8 bit control and 16 bit control. * when the lower two bits of the first byte's control are '11', this * indicated the 8 bit control field. */ typedef struct Eth_LLC8 { UINT8 dsap; UINT8 ssap; UINT8 control; } Eth_LLC8; typedef struct Eth_LLC16 { UINT8 dsap; UINT8 ssap; UINT16 control; } Eth_LLC16; typedef struct Eth_SNAP { UINT8 snapOrg[3]; Eth_DIX snapType; } Eth_SNAP; typedef struct Eth_802_3 { UINT16 lenNBO; // length of the frame Eth_LLC8 llc; // LLC header Eth_SNAP snap; // SNAP header } Eth_802_3; // 802.1p QOS/priority tags enum { ETH_802_1_P_BEST_EFFORT = 0, ETH_802_1_P_BACKGROUND = 1, ETH_802_1_P_EXCELLENT_EFFORT = 2, ETH_802_1_P_CRITICAL_APPS = 3, ETH_802_1_P_VIDEO = 4, ETH_802_1_P_VOICE = 5, ETH_802_1_P_INTERNETWORK_CONROL = 6, ETH_802_1_P_NETWORK_CONTROL = 7 }; typedef struct Eth_802_1pq_Tag { UINT16 typeNBO; // always ETH_TYPE_802_1PQ UINT16 vidHi:4, // 802.1q vlan ID high nibble canonical:1, // bit order? (should always be 0) priority:3, // 802.1p priority tag vidLo:8; // 802.1q vlan ID low byte } Eth_802_1pq_Tag; typedef struct Eth_802_1pq { Eth_802_1pq_Tag tag; // VLAN/QOS tag union { Eth_DIX dix; // DIX header follows Eth_802_3 e802_3; // or 802.3 header follows }; } Eth_802_1pq; typedef struct Eth_Header { Eth_Address dst; // all types of ethernet frame have dst first Eth_Address src; // and the src next (at least all the ones we'll see) union { Eth_DIX dix; // followed by a DIX header... Eth_802_3 e802_3; // ...or an 802.3 header Eth_802_1pq e802_1pq; // ...or an 802.1[pq] tag and a header }; } Eth_Header; #define ETH_BROADCAST_ADDRESS { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } static Eth_Address netEthBroadcastAddr = ETH_BROADCAST_ADDRESS; /* * simple predicate for 1536 boundary. * the parameter is a network ordered UINT16, which is compared to 0x06, * testing for "length" values greater than or equal to 0x0600 (1536) */ #define ETH_TYPENOT8023(x) (((x) & 0xff) >= 0x06) /* * header length macros * * first two are typical: ETH_HEADER_LEN_DIX, ETH_HEADER_LEN_802_1PQ * last two are suspicious, due to 802.3 incompleteness */ #define ETH_HEADER_LEN_DIX (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_DIX)) #define ETH_HEADER_LEN_802_1PQ (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_802_1pq_Tag) + \ sizeof(Eth_DIX)) #define ETH_HEADER_LEN_802_2_LLC (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(UINT16) + \ sizeof(Eth_LLC8)) #define ETH_HEADER_LEN_802_2_LLC16 (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(UINT16) + \ sizeof(Eth_LLC16)) #define ETH_HEADER_LEN_802_3 (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_802_3)) #define ETH_HEADER_LEN_802_1PQ_LLC (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_802_1pq_Tag) + \ sizeof(UINT16) + \ sizeof(Eth_LLC8)) #define ETH_HEADER_LEN_802_1PQ_LLC16 (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_802_1pq_Tag) + \ sizeof(UINT16) + \ sizeof(Eth_LLC16)) #define ETH_HEADER_LEN_802_1PQ_802_3 (sizeof(Eth_Address) + \ sizeof(Eth_Address) + \ sizeof(Eth_802_1pq_Tag) + \ sizeof(Eth_802_3)) #define ETH_MIN_HEADER_LEN (ETH_HEADER_LEN_DIX) #define ETH_MAX_HEADER_LEN (ETH_HEADER_LEN_802_1PQ_802_3) #define ETH_MIN_FRAME_LEN 60 #define ETH_MAX_STD_MTU 1500 #define ETH_MAX_STD_FRAMELEN (ETH_MAX_STD_MTU + ETH_MAX_HEADER_LEN) #define ETH_MAX_JUMBO_MTU 9000 #define ETH_MAX_JUMBO_FRAMELEN (ETH_MAX_JUMBO_MTU + ETH_MAX_HEADER_LEN) #define ETH_DEFAULT_MTU 1500 #define ETH_FCS_LEN 4 #define ETH_VLAN_LEN sizeof(Eth_802_1pq_Tag) /* *---------------------------------------------------------------------------- * Do the two ethernet addresses match? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsAddrMatch(const Eth_Address addr1, const Eth_Address addr2) { return !memcmp(addr1, addr2, ETH_ADDR_LENGTH); } /* *---------------------------------------------------------------------------- * Is the address the broadcast address? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsBroadcastAddr(const Eth_Address addr) { return Eth_IsAddrMatch(addr, netEthBroadcastAddr); } /* *---------------------------------------------------------------------------- * Is the address a unicast address? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsUnicastAddr(const Eth_Address addr) { // broadcast and multicast frames always have the low bit set in byte 0 return !(((CHAR *)addr)[0] & 0x1); } /* *---------------------------------------------------------------------------- * Is the address the all-zeros address? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsNullAddr(const Eth_Address addr) { return ((addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]) == 0); } /* *---------------------------------------------------------------------------- * * Eth_HeaderType -- * return an Eth_HdrType depending on the eth header * contents. will not work in all cases, especially since it * requres ETH_HEADER_LEN_802_1PQ bytes to determine the type * * HeaderType isn't sufficient to determine the length of * the eth header. for 802.3 header, its not clear without * examination, whether a SNAP is included * * returned type: * * ETH_HEADER_TYPE_DIX: typical 14 byte eth header * ETH_HEADER_TYPE_802_1PQ: DIX+vlan tagging * ETH_HEADER_TYPE_802_3: 802.3 eth header * ETH_HEADER_TYPE_802_1PQ_802_3: 802.3 + vlan tag * * the test for DIX was moved from a 1500 boundary to a 1536 * boundary, since the vmxnet2 MTU was updated to 1514. when * W2K8 attempted to send LLC frames, these were interpreted * as DIX frames instead of the correct 802.3 type * * these links may help if they're valid: * * http://standards.ieee.org/regauth/ethertype/type-tut.html * http://standards.ieee.org/regauth/ethertype/type-pub.html * * Results: * Eth_HdrType value * *---------------------------------------------------------------------------- */ static __inline Eth_HdrType Eth_HeaderType(const Eth_Header *eh) { /* * we use 1536 (IEEE 802.3-std mentions 1536, but iana indicates * type of 0-0x5dc are 802.3) instead of some #def symbol to prevent * inadvertant reuse of the same macro for buffer size decls. */ if (ETH_TYPENOT8023(eh->dix.typeNBO)) { if (eh->dix.typeNBO != ETH_TYPE_802_1PQ_NBO) { /* typical case */ return ETH_HEADER_TYPE_DIX; } /* some type of 802.1pq tagged frame */ if (ETH_TYPENOT8023(eh->e802_1pq.dix.typeNBO)) { /* vlan tagging with dix style type */ return ETH_HEADER_TYPE_802_1PQ; } /* vlan tagging with 802.3 header */ return ETH_HEADER_TYPE_802_1PQ_802_3; } /* assume 802.3 */ return ETH_HEADER_TYPE_802_3; } /* *---------------------------------------------------------------------------- * * Eth_EncapsulatedPktType -- * Get the encapsulated (layer 3) frame type. * for LLC frames without SNAP, we don't have * an encapsulated type, and return ETH_TYPE_LLC. * * IANA reserves 0xFFFF, which we reuse to indicate * ETH_TYPE_LLC. * * Results: * NBO frame type. * *---------------------------------------------------------------------------- */ static __inline UINT16 Eth_EncapsulatedPktType(const Eth_Header *eh) { Eth_HdrType type = Eth_HeaderType(eh); switch (type) { case ETH_HEADER_TYPE_DIX: return eh->dix.typeNBO; case ETH_HEADER_TYPE_802_1PQ: return eh->e802_1pq.dix.typeNBO; case ETH_HEADER_TYPE_802_3: /* * Documentation describes SNAP headers as having ONLY * 0x03 as the control fields, not just the lower two bits * This prevents the use of Eth_IsLLCControlUFormat. */ if ((eh->e802_3.llc.dsap == 0xaa) && (eh->e802_3.llc.ssap == 0xaa) && (eh->e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) { return eh->e802_3.snap.snapType.typeNBO; } else { // LLC, no snap header, then no type return ETH_TYPE_LLC; } case ETH_HEADER_TYPE_802_1PQ_802_3: if ((eh->e802_1pq.e802_3.llc.dsap == 0xaa) && (eh->e802_1pq.e802_3.llc.ssap == 0xaa) && (eh->e802_1pq.e802_3.llc.control == ETH_LLC_CONTROL_UFRAME)) { return eh->e802_1pq.e802_3.snap.snapType.typeNBO; } else { // tagged LLC, no snap header, then no type return ETH_TYPE_LLC; } } ASSERT(FALSE); return 0; } /* *---------------------------------------------------------------------------- * Is the frame of the requested protocol type or is it an 802.1[pq] * encapsulation of such a frame? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsDixType(const Eth_Header *eh, const Eth_DixTypeNBO type) { return Eth_EncapsulatedPktType(eh) == type; } /* *---------------------------------------------------------------------------- * Is the frame an IPV4 frame? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsIPV4(const Eth_Header *eh) { return Eth_IsDixType(eh, ETH_TYPE_IPV4_NBO); } /* *---------------------------------------------------------------------------- * Is the frame an IPV6 frame? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsIPV6(const Eth_Header *eh) { return Eth_IsDixType(eh, ETH_TYPE_IPV6_NBO); } /* *---------------------------------------------------------------------------- * Is the frame an ARP frame? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsARP(const Eth_Header *eh) { return Eth_IsDixType(eh, ETH_TYPE_ARP_NBO); } /* *---------------------------------------------------------------------------- * Does the frame contain an 802.1[pq] tag? *---------------------------------------------------------------------------- */ static __inline BOOLEAN Eth_IsFrameTagged(const Eth_Header *eh) { return (eh->dix.typeNBO == ETH_TYPE_802_1PQ_NBO); } #endif /* __ETHERNET_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/ovsext.vcxproj.user0000644000000000000000000000013213534540071024374 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.473851155 openvswitch-2.5.9/datapath-windows/ovsext/ovsext.vcxproj.user0000644000175000017500000000124713534540071026066 0ustar00jpettitjpettit00000000000000 TestSign TestSign TestSign TestSign openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/ovsext.vcxproj0000644000000000000000000000013213534540071023417 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.469851126 openvswitch-2.5.9/datapath-windows/ovsext/ovsext.vcxproj0000644000175000017500000002427213534540071025114 0ustar00jpettitjpettit00000000000000 Win8.1 Debug x64 Win8 Debug x64 Win8.1 Release x64 Win8 Release x64 WDM Driver $(VCTargetsPath11) Win8 Debug Win32 {0D37F250-E766-44C7-90B4-D7E07E77D1AA} {63FE215D-98BE-4440-8081-C6160EFB80FA} $(MSBuildProjectName) WindowsV6.3 True WindowsKernelModeDriver8.1 Win8 True WindowsKernelModeDriver8.1 WindowsV6.3 False WindowsKernelModeDriver8.1 Win8 False WindowsKernelModeDriver8.1 $(IntDir) OVSExt %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS630=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(PreprocessorDefinitions);NDIS_WDM=1;NDIS640=1 %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;$(DDK_LIB_PATH)\netio.lib true Level4 $(IntDir);%(AdditionalIncludeDirectories);..\.. $(IntDir);%(AdditionalIncludeDirectories);..\.. $(IntDir);%(AdditionalIncludeDirectories);..\.. $(IntDir);%(AdditionalIncludeDirectories);..\.. ;%(AdditionalIncludeDirectories) precomp.h Create $(IntDir)\precomp.h.pch openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Datapath.h0000644000000000000000000000013013534540071022347 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 29 ctime=1567801424.36185033 openvswitch-2.5.9/datapath-windows/ovsext/Datapath.h0000644000175000017500000001066013534540071024042 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface * alive while we transition over to the netlink based interface. * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c */ #ifndef __DATAPATH_H_ #define __DATAPATH_H_ 1 /* * Device operations to tag netlink commands with. This is a bitmask since it * is possible that a particular command can be invoked via different device * operations. */ #define OVS_READ_DEV_OP (1 << 0) #define OVS_WRITE_DEV_OP (1 << 1) #define OVS_TRANSACTION_DEV_OP (1 << 2) typedef struct _OVS_DEVICE_EXTENSION { INT numberOpenInstance; INT pidCount; } OVS_DEVICE_EXTENSION, *POVS_DEVICE_EXTENSION; // forward declaration typedef struct _OVS_USER_PACKET_QUEUE OVS_USER_PACKET_QUEUE, *POVS_USER_PACKET_QUEUE; /* * Private context for each handle on the device. */ typedef struct _OVS_OPEN_INSTANCE { UINT32 cookie; PFILE_OBJECT fileObject; PVOID eventQueue; POVS_USER_PACKET_QUEUE packetQueue; UINT32 pid; struct { POVS_MESSAGE ovsMsg; /* OVS message passed during dump start. */ UINT32 index[2]; /* markers to continue dump from. One or more * of them may be used. Eg. in flow dump, the * markers can store the row and the column * indices. */ } dumpState; /* data to support dump commands. */ LIST_ENTRY pidLink; /* Links the instance to * pidHashArray */ } OVS_OPEN_INSTANCE, *POVS_OPEN_INSTANCE; NDIS_STATUS OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle); VOID OvsDeleteDeviceObject(); VOID OvsInit(); VOID OvsCleanup(); POVS_OPEN_INSTANCE OvsGetOpenInstance(PFILE_OBJECT fileObject, UINT32 dpNo); NTSTATUS OvsCompleteIrpRequest(PIRP irp, ULONG_PTR infoPtr, NTSTATUS status); VOID OvsAcquireCtrlLock(); VOID OvsReleaseCtrlLock(); /* * Utility structure and functions to collect in one place all the parameters * passed during a call from userspace. */ typedef struct _OVS_USER_PARAMS_CONTEXT { PIRP irp; /* The IRP used for the userspace call. */ POVS_OPEN_INSTANCE ovsInstance; /* Private data of the device handle. */ UINT32 devOp; /* Device operation of the userspace call. */ POVS_MESSAGE ovsMsg; /* OVS message that userspace passed down. */ PVOID inputBuffer; /* Input data specified by userspace. Maybe NULL. */ UINT32 inputLength; /* Length of input buffer. */ PVOID outputBuffer; /* Output buffer specified by userspace for reading * data. Maybe NULL. */ UINT32 outputLength; /* Length of output buffer. */ } OVS_USER_PARAMS_CONTEXT, *POVS_USER_PARAMS_CONTEXT; static __inline VOID InitUserParamsCtx(PIRP irp, POVS_OPEN_INSTANCE ovsInstance, UINT32 devOp, POVS_MESSAGE ovsMsg, PVOID inputBuffer, UINT32 inputLength, PVOID outputBuffer, UINT32 outputLength, POVS_USER_PARAMS_CONTEXT usrParamsCtx) { usrParamsCtx->irp = irp; usrParamsCtx->ovsInstance = ovsInstance; usrParamsCtx->devOp = devOp; usrParamsCtx->ovsMsg = ovsMsg; usrParamsCtx->inputBuffer = inputBuffer; usrParamsCtx->inputLength = inputLength; usrParamsCtx->outputBuffer = outputBuffer; usrParamsCtx->outputLength = outputLength; } NTSTATUS InitUserDumpState(POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg); VOID FreeUserDumpState(POVS_OPEN_INSTANCE instance); NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx); #endif /* __DATAPATH_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Vport.c0000644000000000000000000000013213534540071021730 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.049685929 30 ctime=1567801424.465851096 openvswitch-2.5.9/datapath-windows/ovsext/Vport.c0000644000175000017500000026730413534540071023432 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Datapath.h" #include "Event.h" #include "Gre.h" #include "IpHelper.h" #include "Jhash.h" #include "Oid.h" #include "Stt.h" #include "Switch.h" #include "User.h" #include "Vport.h" #include "Vxlan.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_VPORT #include "Debug.h" #define VPORT_NIC_ENTER(_nic) \ OVS_LOG_TRACE("Enter: PortId: %x, NicIndex: %d", _nic->PortId, \ _nic->NicIndex) #define VPORT_NIC_EXIT(_nic) \ OVS_LOG_TRACE("Exit: PortId: %x, NicIndex: %d", _nic->PortId, \ _nic->NicIndex) #define VPORT_PORT_ENTER(_port) \ OVS_LOG_TRACE("Enter: PortId: %x", _port->PortId) #define VPORT_PORT_EXIT(_port) \ OVS_LOG_TRACE("Exit: PortId: %x", _port->PortId) #define OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC 100 /* Context structure used to pass back and forth information to the tunnel * filter threads. */ typedef struct _OVS_TUNFLT_INIT_CONTEXT { POVS_SWITCH_CONTEXT switchContext; UINT32 outputLength; PVOID outputBuffer; PVOID inputBuffer; POVS_VPORT_ENTRY vport; BOOLEAN hvSwitchPort; BOOLEAN hvDelete; BOOLEAN ovsDelete; } OVS_TUNFLT_INIT_CONTEXT, *POVS_TUNFLT_INIT_CONTEXT; extern POVS_SWITCH_CONTEXT gOvsSwitchContext; static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport, PNDIS_SWITCH_PORT_PARAMETERS portParam); static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam); static VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport, PNDIS_SWITCH_PORT_PARAMETERS portParam); static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec); static NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet, POVS_VPORT_EXT_INFO extInfo); static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info, POVS_MESSAGE msgIn, PVOID outBuffer, UINT32 outBufLen, int dpIfIndex); static POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext, PWSTR wsName, SIZE_T wstrSize); static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN newPort); static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx, POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN hvDelete, BOOLEAN ovsDelete); static VOID OvsTunnelVportPendingInit(PVOID context, NTSTATUS status, UINT32 *replyLen); static VOID OvsTunnelVportPendingRemove(PVOID context, NTSTATUS status, UINT32 *replyLen); static NTSTATUS GetNICAlias(GUID *netCfgInstanceId, IF_COUNTED_STRING *portFriendlyName); /* * -------------------------------------------------------------------------- * Creates a Vport entry for a Hyper-V switch port. 'nicIndex' is typically * associated with a NIC than a port. We use it here for the special case * where we need to create a Vport for an external NIC with NicIndex > 0. * -------------------------------------------------------------------------- */ NDIS_STATUS HvCreatePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam, NDIS_SWITCH_NIC_INDEX nicIndex) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; NDIS_STATUS status = NDIS_STATUS_SUCCESS; BOOLEAN newPort = FALSE; VPORT_PORT_ENTER(portParam); NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); /* Lookup by port ID. */ vport = OvsFindVportByPortIdAndNicIndex(switchContext, portParam->PortId, nicIndex); if (vport != NULL) { OVS_LOG_ERROR("Port add failed due to duplicate port name, " "port Id: %u", portParam->PortId); status = STATUS_DATA_NOT_ACCEPTED; goto create_port_done; } /* * Lookup by port name to see if this port with this name had been added * (and deleted) previously. */ vport = OvsFindVportByHvNameW(gOvsSwitchContext, portParam->PortFriendlyName.String, portParam->PortFriendlyName.Length); if (vport && vport->isAbsentOnHv == FALSE) { OVS_LOG_ERROR("Port add failed since a port already exists on " "the specified port Id: %u, ovsName: %s", portParam->PortId, vport->ovsName); status = STATUS_DATA_NOT_ACCEPTED; goto create_port_done; } if (vport != NULL) { ASSERT(vport->isAbsentOnHv); ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID); /* * It should be possible to simply just mark this port as "not deleted" * given that the port Id and the name are the same and also provided * that the other properties that we cache have not changed. */ if (vport->portType != portParam->PortType) { OVS_LOG_INFO("Port add failed due to PortType change, port Id: %u" " old: %u, new: %u", portParam->PortId, vport->portType, portParam->PortType); status = STATUS_DATA_NOT_ACCEPTED; goto create_port_done; } vport->isAbsentOnHv = FALSE; } else { vport = (POVS_VPORT_ENTRY)OvsAllocateVport(); if (vport == NULL) { status = NDIS_STATUS_RESOURCES; goto create_port_done; } newPort = TRUE; } OvsInitVportWithPortParam(vport, portParam); vport->nicIndex = nicIndex; UpdateSwitchCtxWithVport(switchContext, vport, newPort); create_port_done: NdisReleaseRWLock(switchContext->dispatchLock, &lockState); VPORT_PORT_EXIT(portParam); return status; } /* * -------------------------------------------------------------------------- * Function to process updates to a port on the Hyper-Vs witch. * -------------------------------------------------------------------------- */ NDIS_STATUS HvUpdatePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; OVS_VPORT_STATE ovsState; NDIS_SWITCH_NIC_STATE nicState; VPORT_PORT_ENTER(portParam); NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, portParam->PortId, 0); /* * Update properties only for NETDEV ports for supprting PS script */ if (vport == NULL) { goto update_port_done; } /* Store the nic and the OVS states as Nic Create won't be called */ ovsState = vport->ovsState; nicState = vport->nicState; /* * Currently only the port friendly name is being updated * Make sure that no other properties are changed */ ASSERT(portParam->PortId == vport->portId); ASSERT(portParam->PortState == vport->portState); ASSERT(portParam->PortType == vport->portType); /* * Call the set parameters function the handle all properties * change in a single place in case future version supports change of * other properties */ OvsInitVportWithPortParam(vport, portParam); /* Retore the nic and OVS states */ vport->nicState = nicState; vport->ovsState = ovsState; update_port_done: NdisReleaseRWLock(switchContext->dispatchLock, &lockState); VPORT_PORT_EXIT(portParam); /* Must always return success */ return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Function to process teardown of a port on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvTeardownPort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; VPORT_PORT_ENTER(portParam); NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, portParam->PortId, 0); if (vport) { /* add assertion here */ vport->portState = NdisSwitchPortStateTeardown; vport->ovsState = OVS_STATE_PORT_TEAR_DOWN; } else { OVS_LOG_WARN("Vport not present."); } NdisReleaseRWLock(switchContext->dispatchLock, &lockState); VPORT_PORT_EXIT(portParam); } /* * -------------------------------------------------------------------------- * Function to process deletion of a port on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvDeletePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParams) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; VPORT_PORT_ENTER(portParams); NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, portParams->PortId, 0); /* * XXX: we can only destroy and remove the port if its datapath port * counterpart was deleted. If the datapath port counterpart is present, * we only mark the vport for deletion, so that a netlink command vport * delete will delete the vport. */ if (vport) { OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE); } else { OVS_LOG_WARN("Vport not present."); } NdisReleaseRWLock(switchContext->dispatchLock, &lockState); VPORT_PORT_EXIT(portParams); } /* * -------------------------------------------------------------------------- * Function to process addition of a NIC connection on the Hyper-V switch. * XXX: Posting an event to DPIF is incorrect here. However, it might be useful * to post an event to netdev-windows.c. * -------------------------------------------------------------------------- */ NDIS_STATUS HvCreateNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { POVS_VPORT_ENTRY vport; NDIS_STATUS status = NDIS_STATUS_SUCCESS; IF_COUNTED_STRING portFriendlyName = {0}; LOCK_STATE_EX lockState; VPORT_NIC_ENTER(nicParam); /* Wait for lists to be initialized. */ OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC); if (!switchContext->isActivated) { OVS_LOG_WARN("Switch is not activated yet."); /* Veto the creation of nic */ status = NDIS_STATUS_NOT_SUPPORTED; goto done; } if (OvsIsInternalNIC(nicParam->NicType) || OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) { GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName); } NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); /* * There can be one or more NICs for the external port. We create a vport * structure for each such NIC, and each NIC inherits a lot of properties * from the parent external port. */ if (OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) { NDIS_SWITCH_PORT_PARAMETERS portParam; POVS_VPORT_ENTRY virtExtVport = (POVS_VPORT_ENTRY)switchContext->virtualExternalVport; ASSERT(virtExtVport); ASSERT(OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex) == NULL); OvsCopyPortParamsFromVport(virtExtVport, &portParam); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); status = HvCreatePort(switchContext, &portParam, nicParam->NicIndex); NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); if (status != NDIS_STATUS_SUCCESS) { goto add_nic_done; } } vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex); if (vport == NULL) { OVS_LOG_ERROR("Create NIC without Switch Port," " PortId: %x, NicIndex: %d", nicParam->PortId, nicParam->NicIndex); status = NDIS_STATUS_INVALID_PARAMETER; goto add_nic_done; } OvsInitVportWithNicParam(switchContext, vport, nicParam); if (OvsIsInternalNIC(nicParam->NicType) || OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) { RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName, sizeof portFriendlyName); } add_nic_done: NdisReleaseRWLock(switchContext->dispatchLock, &lockState); done: VPORT_NIC_EXIT(nicParam); OVS_LOG_TRACE("Exit: status %8x.\n", status); return status; } /* * -------------------------------------------------------------------------- * Function to process connection event of a NIC on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvConnectNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { LOCK_STATE_EX lockState; POVS_VPORT_ENTRY vport; UINT32 portNo; VPORT_NIC_ENTER(nicParam); /* Wait for lists to be initialized. */ OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC); if (!switchContext->isActivated) { OVS_LOG_WARN("Switch is not activated yet."); goto done; } NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex); if (!vport) { OVS_LOG_WARN("Vport not present."); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); ASSERT(0); goto done; } vport->ovsState = OVS_STATE_CONNECTED; vport->nicState = NdisSwitchNicStateConnected; portNo = vport->portNo; NdisReleaseRWLock(switchContext->dispatchLock, &lockState); if (nicParam->NicType == NdisSwitchNicTypeInternal) { OvsInternalAdapterUp(&nicParam->NetCfgInstanceId); } done: VPORT_NIC_EXIT(nicParam); } /* * -------------------------------------------------------------------------- * Function to process updates to a NIC on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvUpdateNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; UINT32 event = 0; IF_COUNTED_STRING portFriendlyName = {0}; BOOLEAN nameChanged = FALSE; BOOLEAN aliasLookup = FALSE; VPORT_NIC_ENTER(nicParam); /* Wait for lists to be initialized. */ OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC); if (!switchContext->isActivated) { OVS_LOG_WARN("Switch is not activated yet."); goto update_nic_done; } /* GetNICAlias() must be called outside of a lock. */ if (nicParam->NicType == NdisSwitchNicTypeInternal || OvsIsRealExternalNIC(nicParam->NicType, nicParam->NicIndex)) { GetNICAlias(&nicParam->NetCfgInstanceId, &portFriendlyName); aliasLookup = TRUE; } NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex); if (vport == NULL) { NdisReleaseRWLock(switchContext->dispatchLock, &lockState); OVS_LOG_WARN("Vport search failed."); goto update_nic_done; } switch (nicParam->NicType) { case NdisSwitchNicTypeExternal: case NdisSwitchNicTypeInternal: RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId, sizeof (GUID)); if (aliasLookup) { if (RtlCompareMemory(&vport->portFriendlyName, &portFriendlyName, vport->portFriendlyName.Length) != vport->portFriendlyName.Length) { RtlCopyMemory(&vport->portFriendlyName, &portFriendlyName, sizeof portFriendlyName); nameChanged = TRUE; } } break; case NdisSwitchNicTypeSynthetic: case NdisSwitchNicTypeEmulated: if (!RtlEqualMemory(vport->vmMacAddress, nicParam->VMMacAddress, sizeof (vport->vmMacAddress))) { event |= OVS_EVENT_MAC_CHANGE; RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress, sizeof (vport->vmMacAddress)); } break; default: ASSERT(0); } if (!RtlEqualMemory(vport->permMacAddress, nicParam->PermanentMacAddress, sizeof (vport->permMacAddress))) { RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress, sizeof (vport->permMacAddress)); event |= OVS_EVENT_MAC_CHANGE; } if (!RtlEqualMemory(vport->currMacAddress, nicParam->CurrentMacAddress, sizeof (vport->currMacAddress))) { RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress, sizeof (vport->currMacAddress)); event |= OVS_EVENT_MAC_CHANGE; } if (vport->mtu != nicParam->MTU) { vport->mtu = nicParam->MTU; event |= OVS_EVENT_MTU_CHANGE; } vport->numaNodeId = nicParam->NumaNodeId; if (nameChanged) { OVS_EVENT_ENTRY event; event.portNo = vport->portNo; event.ovsType = vport->ovsType; event.upcallPid = vport->upcallPid; RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName); event.type = OVS_EVENT_LINK_DOWN; OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE); OvsPostEvent(&event); } NdisReleaseRWLock(switchContext->dispatchLock, &lockState); /* * XXX: Not sure what kind of event to post here. DPIF is not interested in * changes to MAC address. Netdev-windows might be intrested, though. * That said, if the name chagnes, not clear what kind of event to be * posted. We might have to delete the vport, and have userspace recreate * it. */ update_nic_done: VPORT_NIC_EXIT(nicParam); } /* * -------------------------------------------------------------------------- * Function to process disconnect event of a NIC on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { POVS_VPORT_ENTRY vport; LOCK_STATE_EX lockState; BOOLEAN isInternalPort = FALSE; OVS_EVENT_ENTRY event; VPORT_NIC_ENTER(nicParam); /* Wait for lists to be initialized. */ OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC); if (!switchContext->isActivated) { OVS_LOG_WARN("Switch is not activated yet."); goto done; } NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex); if (!vport) { OVS_LOG_WARN("Vport not present."); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); goto done; } vport->nicState = NdisSwitchNicStateDisconnected; vport->ovsState = OVS_STATE_NIC_CREATED; if (vport->ovsType == OVS_VPORT_TYPE_INTERNAL) { isInternalPort = TRUE; } event.portNo = vport->portNo; event.ovsType = vport->ovsType; event.upcallPid = vport->upcallPid; RtlCopyMemory(&event.ovsName, &vport->ovsName, sizeof event.ovsName); event.type = OVS_EVENT_LINK_DOWN; /* * Delete the port from the hash tables accessible to userspace. After this * point, userspace should not be able to access this port. */ if (OvsIsRealExternalVport(vport)) { OvsRemoveAndDeleteVport(NULL, switchContext, vport, FALSE, TRUE); OvsPostEvent(&event); } NdisReleaseRWLock(switchContext->dispatchLock, &lockState); if (isInternalPort) { OvsInternalAdapterDown(); } done: VPORT_NIC_EXIT(nicParam); } /* * -------------------------------------------------------------------------- * Function to process delete event of a NIC on the Hyper-V switch. * -------------------------------------------------------------------------- */ VOID HvDeleteNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { LOCK_STATE_EX lockState; POVS_VPORT_ENTRY vport; VPORT_NIC_ENTER(nicParam); /* Wait for lists to be initialized. */ OvsWaitActivate(switchContext, OVS_VPORT_DEFAULT_WAIT_TIME_MICROSEC); if (!switchContext->isActivated) { OVS_LOG_WARN("Switch is not activated yet."); goto done; } NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByPortIdAndNicIndex(switchContext, nicParam->PortId, nicParam->NicIndex); if (!vport) { OVS_LOG_WARN("Vport not present."); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); goto done; } vport->nicState = NdisSwitchNicStateUnknown; vport->ovsState = OVS_STATE_PORT_CREATED; if (OvsIsRealExternalVport(vport)) { /* This vport was created in HvCreateNic(). */ OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, FALSE); } NdisReleaseRWLock(switchContext->dispatchLock, &lockState); done: VPORT_NIC_EXIT(nicParam); } /* * OVS Vport related functionality. */ POVS_VPORT_ENTRY OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext, UINT32 portNo) { POVS_VPORT_ENTRY vport; PLIST_ENTRY head, link; UINT32 hash = OvsJhashBytes((const VOID *)&portNo, sizeof(portNo), OVS_HASH_BASIS); head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink); if (vport->portNo == portNo) { return vport; } } return NULL; } POVS_VPORT_ENTRY OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext, UINT16 dstPort, OVS_VPORT_TYPE ovsPortType) { POVS_VPORT_ENTRY vport; PLIST_ENTRY head, link; UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort), OVS_HASH_BASIS); head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink); if (GetPortFromPriv(vport) == dstPort && vport->ovsType == ovsPortType) { return vport; } } return NULL; } POVS_VPORT_ENTRY OvsFindTunnelVportByPortType(POVS_SWITCH_CONTEXT switchContext, OVS_VPORT_TYPE ovsPortType) { POVS_VPORT_ENTRY vport; PLIST_ENTRY head, link; UINT16 dstPort = 0; UINT32 hash = OvsJhashBytes((const VOID *)&dstPort, sizeof(dstPort), OVS_HASH_BASIS); head = &(switchContext->tunnelVportsArray[hash & OVS_VPORT_MASK]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, tunnelVportLink); if (vport->ovsType == ovsPortType) { return vport; } } return NULL; } POVS_VPORT_ENTRY OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext, PSTR name) { POVS_VPORT_ENTRY vport; PLIST_ENTRY head, link; UINT32 hash; SIZE_T length = strlen(name) + 1; hash = OvsJhashBytes((const VOID *)name, length, OVS_HASH_BASIS); head = &(switchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, ovsNameLink); if (!strcmp(name, vport->ovsName)) { return vport; } } return NULL; } /* OvsFindVportByHvName: "name" is assumed to be null-terminated */ POVS_VPORT_ENTRY OvsFindVportByHvNameW(POVS_SWITCH_CONTEXT switchContext, PWSTR wsName, SIZE_T wstrSize) { POVS_VPORT_ENTRY vport = NULL; PLIST_ENTRY head, link; UINT i; for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) { head = &(switchContext->portIdHashArray[i]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink); /* * NOTE about portFriendlyName: * If the string is NULL-terminated, the Length member does not * include the terminating NULL character. */ if (vport->portFriendlyName.Length == wstrSize && RtlEqualMemory(wsName, vport->portFriendlyName.String, vport->portFriendlyName.Length)) { goto Cleanup; } vport = NULL; } } /* * Look in the list of ports that were added from the Hyper-V switch and * deleted. */ if (vport == NULL) { for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) { head = &(switchContext->portNoHashArray[i]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink); if (vport->portFriendlyName.Length == wstrSize && RtlEqualMemory(wsName, vport->portFriendlyName.String, vport->portFriendlyName.Length)) { goto Cleanup; } vport = NULL; } } } Cleanup: return vport; } POVS_VPORT_ENTRY OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext, PSTR name) { POVS_VPORT_ENTRY vport = NULL; /* 'portFriendlyName' is not NUL-terminated. */ SIZE_T length = strlen(name); SIZE_T wstrSize = length * sizeof(WCHAR); UINT i; PWSTR wsName = OvsAllocateMemoryWithTag(wstrSize, OVS_VPORT_POOL_TAG); if (!wsName) { return NULL; } for (i = 0; i < length; i++) { wsName[i] = name[i]; } vport = OvsFindVportByHvNameW(switchContext, wsName, wstrSize); OvsFreeMemoryWithTag(wsName, OVS_VPORT_POOL_TAG); return vport; } POVS_VPORT_ENTRY OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext, NDIS_SWITCH_PORT_ID portId, NDIS_SWITCH_NIC_INDEX index) { if (switchContext->virtualExternalVport && portId == switchContext->virtualExternalPortId && index == switchContext->virtualExternalVport->nicIndex) { return (POVS_VPORT_ENTRY)switchContext->virtualExternalVport; } else if (switchContext->internalVport && portId == switchContext->internalPortId && index == switchContext->internalVport->nicIndex) { return (POVS_VPORT_ENTRY)switchContext->internalVport; } else { PLIST_ENTRY head, link; POVS_VPORT_ENTRY vport; UINT32 hash; hash = OvsJhashWords((UINT32 *)&portId, 1, OVS_HASH_BASIS); head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL(head, link) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink); if (portId == vport->portId && index == vport->nicIndex) { return vport; } } return NULL; } } POVS_VPORT_ENTRY OvsAllocateVport(VOID) { POVS_VPORT_ENTRY vport; vport = (POVS_VPORT_ENTRY)OvsAllocateMemoryWithTag( sizeof(OVS_VPORT_ENTRY), OVS_VPORT_POOL_TAG); if (vport == NULL) { return NULL; } RtlZeroMemory(vport, sizeof (OVS_VPORT_ENTRY)); vport->ovsState = OVS_STATE_UNKNOWN; vport->isAbsentOnHv = FALSE; vport->portNo = OVS_DPPORT_NUMBER_INVALID; InitializeListHead(&vport->ovsNameLink); InitializeListHead(&vport->portIdLink); InitializeListHead(&vport->portNoLink); return vport; } static VOID OvsInitVportWithPortParam(POVS_VPORT_ENTRY vport, PNDIS_SWITCH_PORT_PARAMETERS portParam) { vport->portType = portParam->PortType; vport->portState = portParam->PortState; vport->portId = portParam->PortId; vport->nicState = NdisSwitchNicStateUnknown; vport->isExternal = FALSE; vport->isBridgeInternal = FALSE; switch (vport->portType) { case NdisSwitchPortTypeExternal: vport->isExternal = TRUE; vport->ovsType = OVS_VPORT_TYPE_NETDEV; break; case NdisSwitchPortTypeInternal: vport->ovsType = OVS_VPORT_TYPE_INTERNAL; break; case NdisSwitchPortTypeSynthetic: case NdisSwitchPortTypeEmulated: vport->ovsType = OVS_VPORT_TYPE_NETDEV; break; } RtlCopyMemory(&vport->hvPortName, &portParam->PortName, sizeof (NDIS_SWITCH_PORT_NAME)); /* For external and internal ports, 'portFriendlyName' is overwritten * later. */ RtlCopyMemory(&vport->portFriendlyName, &portParam->PortFriendlyName, sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME)); switch (vport->portState) { case NdisSwitchPortStateCreated: vport->ovsState = OVS_STATE_PORT_CREATED; break; case NdisSwitchPortStateTeardown: vport->ovsState = OVS_STATE_PORT_TEAR_DOWN; break; case NdisSwitchPortStateDeleted: vport->ovsState = OVS_STATE_PORT_DELETED; break; } } static VOID OvsInitVportWithNicParam(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, PNDIS_SWITCH_NIC_PARAMETERS nicParam) { ASSERT(vport->portId == nicParam->PortId); ASSERT(vport->ovsState == OVS_STATE_PORT_CREATED); UNREFERENCED_PARAMETER(switchContext); RtlCopyMemory(vport->permMacAddress, nicParam->PermanentMacAddress, sizeof (nicParam->PermanentMacAddress)); RtlCopyMemory(vport->currMacAddress, nicParam->CurrentMacAddress, sizeof (nicParam->CurrentMacAddress)); if (nicParam->NicType == NdisSwitchNicTypeSynthetic || nicParam->NicType == NdisSwitchNicTypeEmulated) { RtlCopyMemory(vport->vmMacAddress, nicParam->VMMacAddress, sizeof (nicParam->VMMacAddress)); RtlCopyMemory(&vport->vmName, &nicParam->VmName, sizeof (nicParam->VmName)); } else { RtlCopyMemory(&vport->netCfgInstanceId, &nicParam->NetCfgInstanceId, sizeof (nicParam->NetCfgInstanceId)); } RtlCopyMemory(&vport->nicName, &nicParam->NicName, sizeof (nicParam->NicName)); vport->mtu = nicParam->MTU; vport->nicState = nicParam->NicState; vport->nicIndex = nicParam->NicIndex; vport->nicType = nicParam->NicType; vport->numaNodeId = nicParam->NumaNodeId; switch (vport->nicState) { case NdisSwitchNicStateCreated: vport->ovsState = OVS_STATE_NIC_CREATED; break; case NdisSwitchNicStateConnected: vport->ovsState = OVS_STATE_CONNECTED; break; case NdisSwitchNicStateDisconnected: vport->ovsState = OVS_STATE_NIC_CREATED; break; case NdisSwitchNicStateDeleted: vport->ovsState = OVS_STATE_PORT_CREATED; break; } } /* * -------------------------------------------------------------------------- * Populates 'portParam' based on 'vport'. * -------------------------------------------------------------------------- */ static VOID OvsCopyPortParamsFromVport(POVS_VPORT_ENTRY vport, PNDIS_SWITCH_PORT_PARAMETERS portParam) { portParam->Flags = 0; portParam->PortId = vport->portId; RtlCopyMemory(&portParam->PortName, &vport->hvPortName, sizeof (NDIS_SWITCH_PORT_NAME)); RtlCopyMemory(&portParam->PortFriendlyName, &vport->portFriendlyName, sizeof(NDIS_SWITCH_PORT_FRIENDLYNAME)); portParam->PortType = vport->portType; portParam->IsValidationPort = FALSE; portParam->PortState = vport->portState; } /* * -------------------------------------------------------------------------- * Initializes a tunnel vport. * -------------------------------------------------------------------------- */ NTSTATUS OvsInitTunnelVport(PVOID userContext, POVS_VPORT_ENTRY vport, OVS_VPORT_TYPE ovsType, UINT16 dstPort) { NTSTATUS status = STATUS_SUCCESS; POVS_USER_PARAMS_CONTEXT usrParamsCtx = (POVS_USER_PARAMS_CONTEXT)userContext; vport->isBridgeInternal = FALSE; vport->ovsType = ovsType; vport->ovsState = OVS_STATE_PORT_CREATED; switch (ovsType) { case OVS_VPORT_TYPE_GRE: status = OvsInitGreTunnel(vport); break; case OVS_VPORT_TYPE_VXLAN: { POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL; tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext), OVS_VPORT_POOL_TAG); if (tunnelContext == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } tunnelContext->inputBuffer = usrParamsCtx->inputBuffer; tunnelContext->outputBuffer = usrParamsCtx->outputBuffer; tunnelContext->outputLength = usrParamsCtx->outputLength; tunnelContext->vport = vport; status = OvsInitVxlanTunnel(usrParamsCtx->irp, vport, dstPort, OvsTunnelVportPendingInit, (PVOID)tunnelContext); if (status != STATUS_PENDING) { OvsFreeMemoryWithTag(tunnelContext, OVS_VPORT_POOL_TAG); tunnelContext = NULL; } break; } case OVS_VPORT_TYPE_STT: status = OvsInitSttTunnel(vport, dstPort); break; default: ASSERT(0); } return status; } /* * -------------------------------------------------------------------------- * Initializes a bridge internal vport ie. a port of type * OVS_VPORT_TYPE_INTERNAL but not present on the Hyper-V switch. * -------------------------------------------------------------------------- */ NTSTATUS OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport) { vport->isBridgeInternal = TRUE; vport->ovsType = OVS_VPORT_TYPE_INTERNAL; /* Mark the status to be connected, since there is no other initialization * for this port. */ vport->ovsState = OVS_STATE_CONNECTED; return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * For external and internal vports 'portFriendlyName' parameter, provided by * Hyper-V, is overwritten with the interface alias name. * -------------------------------------------------------------------------- */ static NTSTATUS GetNICAlias(GUID *netCfgInstanceId, IF_COUNTED_STRING *portFriendlyName) { NTSTATUS status = STATUS_SUCCESS; WCHAR interfaceName[IF_MAX_STRING_SIZE] = { 0 }; NET_LUID interfaceLuid = { 0 }; size_t len = 0; status = ConvertInterfaceGuidToLuid(netCfgInstanceId, &interfaceLuid); if (status == STATUS_SUCCESS) { /* * Must be called from PASSIVE_LEVEL. Resulted in a * STATUS_INVALID_DEVICE_REQUEST if not. */ status = ConvertInterfaceLuidToAlias(&interfaceLuid, interfaceName, IF_MAX_STRING_SIZE + 1); if (status == STATUS_SUCCESS) { RtlStringCbPrintfW(portFriendlyName->String, IF_MAX_STRING_SIZE, L"%s", interfaceName); RtlStringCbLengthW(portFriendlyName->String, IF_MAX_STRING_SIZE, &len); portFriendlyName->Length = (USHORT)len; } else { OVS_LOG_ERROR("Fail to convert interface LUID to alias, status: %x", status); } } else { OVS_LOG_ERROR("Fail to convert interface GUID to LUID, status: %x", status); } return status; } /* * -------------------------------------------------------------------------- * Functionality common to any port on the Hyper-V switch. This function is not * to be called for a port that is not on the Hyper-V switch. * * Inserts the port into 'portIdHashArray' and caches the pointer in the * 'switchContext' if needed. * -------------------------------------------------------------------------- */ static VOID UpdateSwitchCtxWithVport(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN newPort) { UINT32 hash; switch (vport->portType) { case NdisSwitchPortTypeExternal: if (vport->nicIndex == 0) { switchContext->virtualExternalPortId = vport->portId; switchContext->virtualExternalVport = vport; } else { switchContext->numPhysicalNics++; } break; case NdisSwitchPortTypeInternal: ASSERT(vport->isBridgeInternal == FALSE); switchContext->internalPortId = vport->portId; switchContext->internalVport = vport; break; case NdisSwitchPortTypeSynthetic: case NdisSwitchPortTypeEmulated: break; } /* * It is important to not insert vport corresponding to virtual external * port into the 'portIdHashArray' since the port should not be exposed to * OVS userspace. */ if (vport->portType == NdisSwitchPortTypeExternal && vport->nicIndex == 0) { return; } /* * NOTE: OvsJhashWords has portId as "1" word. This should be ok, even * though sizeof(NDIS_SWITCH_PORT_ID) = 4, not 2, because the * hyper-v switch seems to use only 2 bytes out of 4. */ hash = OvsJhashWords(&vport->portId, 1, OVS_HASH_BASIS); InsertHeadList(&switchContext->portIdHashArray[hash & OVS_VPORT_MASK], &vport->portIdLink); if (newPort) { switchContext->numHvVports++; } return; } /* * -------------------------------------------------------------------------- * Functionality common to any port added from OVS userspace. * * Inserts the port into 'portNoHashArray', 'ovsPortNameHashArray' and in * 'tunnelVportsArray' if appropriate. * -------------------------------------------------------------------------- */ NDIS_STATUS InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport) { UINT32 hash; switch(vport->ovsType) { case OVS_VPORT_TYPE_GRE: case OVS_VPORT_TYPE_VXLAN: case OVS_VPORT_TYPE_STT: { UINT16 dstPort = GetPortFromPriv(vport); hash = OvsJhashBytes(&dstPort, sizeof(dstPort), OVS_HASH_BASIS); InsertHeadList( &gOvsSwitchContext->tunnelVportsArray[hash & OVS_VPORT_MASK], &vport->tunnelVportLink); switchContext->numNonHvVports++; break; } case OVS_VPORT_TYPE_INTERNAL: if (vport->isBridgeInternal) { switchContext->numNonHvVports++; } default: break; } /* * Insert the port into the hash array of ports: by port number and ovs * and ovs (datapath) port name. * NOTE: OvsJhashWords has portNo as "1" word. This is ok, because the * portNo is stored in 2 bytes only (max port number = MAXUINT16). */ hash = OvsJhashWords(&vport->portNo, 1, OVS_HASH_BASIS); InsertHeadList(&gOvsSwitchContext->portNoHashArray[hash & OVS_VPORT_MASK], &vport->portNoLink); hash = OvsJhashBytes(vport->ovsName, strlen(vport->ovsName) + 1, OVS_HASH_BASIS); InsertHeadList( &gOvsSwitchContext->ovsPortNameHashArray[hash & OVS_VPORT_MASK], &vport->ovsNameLink); return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Provides functionality that is partly complementatry to * InitOvsVportCommon()/UpdateSwitchCtxWithVport(). * * 'hvDelete' indicates if caller is removing the vport as a result of the * port being removed on the Hyper-V switch. * 'ovsDelete' indicates if caller is removing the vport as a result of the * port being removed from OVS userspace. * -------------------------------------------------------------------------- */ NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsContext, POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN hvDelete, BOOLEAN ovsDelete) { POVS_USER_PARAMS_CONTEXT usrParamsCtx = (POVS_USER_PARAMS_CONTEXT)usrParamsContext; BOOLEAN hvSwitchPort = FALSE; BOOLEAN deletedOnOvs = FALSE; BOOLEAN deletedOnHv = FALSE; switch (vport->ovsType) { case OVS_VPORT_TYPE_INTERNAL: if (!vport->isBridgeInternal) { if (hvDelete && vport->isAbsentOnHv == FALSE) { switchContext->internalPortId = 0; switchContext->internalVport = NULL; OvsInternalAdapterDown(); } hvSwitchPort = TRUE; } break; case OVS_VPORT_TYPE_VXLAN: { NTSTATUS status; status = OvsRemoveTunnelVport(usrParamsCtx, switchContext, vport, hvDelete, ovsDelete); if (status != STATUS_SUCCESS) { return status; } } case OVS_VPORT_TYPE_STT: OvsCleanupSttTunnel(vport); break; case OVS_VPORT_TYPE_GRE: OvsCleanupGreTunnel(vport); break; case OVS_VPORT_TYPE_NETDEV: if (vport->isExternal) { if (vport->nicIndex == 0) { /* Such a vport is not part of any of the hash tables, since it * is not exposed to userspace. See Vport.h for explanation. */ ASSERT(hvDelete == TRUE); ASSERT(switchContext->numPhysicalNics == 0); switchContext->virtualExternalPortId = 0; switchContext->virtualExternalVport = NULL; OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG); return STATUS_SUCCESS; } } hvSwitchPort = TRUE; default: break; } /* * 'hvDelete' == TRUE indicates that the port should be removed from the * 'portIdHashArray', while 'ovsDelete' == TRUE indicates that the port * should be removed from 'portNoHashArray' and the 'ovsPortNameHashArray'. * * Both 'hvDelete' and 'ovsDelete' can be set to TRUE by the caller. */ if (vport->isAbsentOnHv == TRUE) { deletedOnHv = TRUE; } if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) { deletedOnOvs = TRUE; } if (hvDelete && !deletedOnHv) { vport->isAbsentOnHv = TRUE; if (vport->isExternal) { ASSERT(vport->nicIndex != 0); ASSERT(switchContext->numPhysicalNics); switchContext->numPhysicalNics--; } /* Remove the port from the relevant lists. */ RemoveEntryList(&vport->portIdLink); InitializeListHead(&vport->portIdLink); deletedOnHv = TRUE; } if (ovsDelete && !deletedOnOvs) { vport->portNo = OVS_DPPORT_NUMBER_INVALID; vport->ovsName[0] = '\0'; /* Remove the port from the relevant lists. */ RemoveEntryList(&vport->ovsNameLink); InitializeListHead(&vport->ovsNameLink); RemoveEntryList(&vport->portNoLink); InitializeListHead(&vport->portNoLink); if (OVS_VPORT_TYPE_VXLAN == vport->ovsType || OVS_VPORT_TYPE_STT == vport->ovsType || OVS_VPORT_TYPE_GRE == vport->ovsType) { RemoveEntryList(&vport->tunnelVportLink); InitializeListHead(&vport->tunnelVportLink); } deletedOnOvs = TRUE; } /* * Deallocate the port if it has been deleted on the Hyper-V switch as well * as OVS userspace. */ if (deletedOnHv && deletedOnOvs) { if (hvSwitchPort) { switchContext->numHvVports--; } else { switchContext->numNonHvVports--; } OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG); } return STATUS_SUCCESS; } static NTSTATUS OvsRemoveTunnelVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx, POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN hvDelete, BOOLEAN ovsDelete) { POVS_TUNFLT_INIT_CONTEXT tunnelContext = NULL; PIRP irp = NULL; tunnelContext = OvsAllocateMemoryWithTag(sizeof(*tunnelContext), OVS_VPORT_POOL_TAG); if (tunnelContext == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(tunnelContext, sizeof(*tunnelContext)); tunnelContext->switchContext = switchContext; tunnelContext->hvSwitchPort = FALSE; tunnelContext->hvDelete = hvDelete; tunnelContext->ovsDelete = ovsDelete; tunnelContext->vport = vport; if (usrParamsCtx) { tunnelContext->inputBuffer = usrParamsCtx->inputBuffer; tunnelContext->outputBuffer = usrParamsCtx->outputBuffer; tunnelContext->outputLength = usrParamsCtx->outputLength; irp = usrParamsCtx->irp; } return OvsCleanupVxlanTunnel(irp, vport, OvsTunnelVportPendingRemove, tunnelContext); } /* * -------------------------------------------------------------------------- * Enumerates the ports on the Hyper-V switch. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsAddConfiguredSwitchPorts(POVS_SWITCH_CONTEXT switchContext) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; ULONG arrIndex; PNDIS_SWITCH_PORT_PARAMETERS portParam; PNDIS_SWITCH_PORT_ARRAY portArray = NULL; OVS_LOG_TRACE("Enter: switchContext:%p", switchContext); status = OvsGetPortsOnSwitch(switchContext, &portArray); if (status != NDIS_STATUS_SUCCESS) { goto cleanup; } for (arrIndex = 0; arrIndex < portArray->NumElements; arrIndex++) { portParam = NDIS_SWITCH_PORT_AT_ARRAY_INDEX(portArray, arrIndex); if (portParam->IsValidationPort) { continue; } status = HvCreatePort(switchContext, portParam, 0); if (status != STATUS_SUCCESS && status != STATUS_DATA_NOT_ACCEPTED) { break; } } cleanup: if (status != NDIS_STATUS_SUCCESS) { OvsClearAllSwitchVports(switchContext); } OvsFreeSwitchPortsArray(portArray); OVS_LOG_TRACE("Exit: status: %x", status); return status; } /* * -------------------------------------------------------------------------- * Enumerates the NICs on the Hyper-V switch. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsInitConfiguredSwitchNics(POVS_SWITCH_CONTEXT switchContext) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; PNDIS_SWITCH_NIC_ARRAY nicArray = NULL; ULONG arrIndex; PNDIS_SWITCH_NIC_PARAMETERS nicParam; OVS_LOG_TRACE("Enter: switchContext: %p", switchContext); /* * Now, get NIC list. */ status = OvsGetNicsOnSwitch(switchContext, &nicArray); if (status != NDIS_STATUS_SUCCESS) { goto cleanup; } for (arrIndex = 0; arrIndex < nicArray->NumElements; ++arrIndex) { nicParam = NDIS_SWITCH_NIC_AT_ARRAY_INDEX(nicArray, arrIndex); /* * XXX: Check if the port is configured with a VLAN. Disallow such a * configuration, since we don't support tag-in-tag. * XXX: Check if the port is connected to a VF. Disconnect the VF in * such a case. */ status = HvCreateNic(switchContext, nicParam); if (status == NDIS_STATUS_SUCCESS) { HvConnectNic(switchContext, nicParam); } } cleanup: OvsFreeSwitchNicsArray(nicArray); OVS_LOG_TRACE("Exit: status: %x", status); return status; } /* * -------------------------------------------------------------------------- * Deletes ports added from the Hyper-V switch as well as OVS usersapce. The * function deletes ports in 'portIdHashArray'. This will delete most of the * ports that are in the 'portNoHashArray' as well. Any remaining ports * are deleted by walking the 'portNoHashArray'. * -------------------------------------------------------------------------- */ VOID OvsClearAllSwitchVports(POVS_SWITCH_CONTEXT switchContext) { for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) { PLIST_ENTRY head, link, next; head = &(switchContext->portIdHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL_SAFE(head, link, next) { POVS_VPORT_ENTRY vport; vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portIdLink); OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE); } } /* * Remove 'virtualExternalVport' as well. This port is not part of the * 'portIdHashArray'. */ if (switchContext->virtualExternalVport) { OvsRemoveAndDeleteVport(NULL, switchContext, (POVS_VPORT_ENTRY)switchContext->virtualExternalVport, TRUE, TRUE); } for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) { PLIST_ENTRY head, link, next; head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL_SAFE(head, link, next) { POVS_VPORT_ENTRY vport; vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink); ASSERT(OvsIsTunnelVportType(vport->ovsType) || (vport->ovsType == OVS_VPORT_TYPE_INTERNAL && vport->isBridgeInternal) || vport->isAbsentOnHv == TRUE); OvsRemoveAndDeleteVport(NULL, switchContext, vport, TRUE, TRUE); } } ASSERT(switchContext->virtualExternalVport == NULL); ASSERT(switchContext->internalVport == NULL); } NTSTATUS OvsConvertIfCountedStrToAnsiStr(PIF_COUNTED_STRING wStr, CHAR *str, UINT16 maxStrLen) { ANSI_STRING astr; UNICODE_STRING ustr; NTSTATUS status; UINT32 size; ustr.Buffer = wStr->String; ustr.Length = wStr->Length; ustr.MaximumLength = IF_MAX_STRING_SIZE; astr.Buffer = str; astr.MaximumLength = maxStrLen; astr.Length = 0; size = RtlUnicodeStringToAnsiSize(&ustr); if (size > maxStrLen) { return STATUS_BUFFER_OVERFLOW; } status = RtlUnicodeStringToAnsiString(&astr, &ustr, FALSE); ASSERT(status == STATUS_SUCCESS); if (status != STATUS_SUCCESS) { return status; } ASSERT(astr.Length <= maxStrLen); str[astr.Length] = 0; return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Utility function that populates a 'OVS_VPORT_EXT_INFO' structure for the * specified vport. * -------------------------------------------------------------------------- */ NTSTATUS OvsGetExtInfoIoctl(POVS_VPORT_GET vportGet, POVS_VPORT_EXT_INFO extInfo) { POVS_VPORT_ENTRY vport; size_t len; LOCK_STATE_EX lockState; NTSTATUS status = STATUS_SUCCESS; BOOLEAN doConvert = FALSE; RtlZeroMemory(extInfo, sizeof (POVS_VPORT_EXT_INFO)); NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); if (vportGet->portNo == 0) { StringCbLengthA(vportGet->name, OVS_MAX_PORT_NAME_LENGTH - 1, &len); vport = OvsFindVportByHvNameA(gOvsSwitchContext, vportGet->name); if (vport == NULL) { /* If the port is not a Hyper-V port and it has been added earlier, * we'll find it in 'ovsPortNameHashArray'. */ vport = OvsFindVportByOvsName(gOvsSwitchContext, vportGet->name); } } else { vport = OvsFindVportByPortNo(gOvsSwitchContext, vportGet->portNo); } if (vport == NULL || (vport->ovsState != OVS_STATE_CONNECTED && vport->ovsState != OVS_STATE_NIC_CREATED)) { NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); if (vportGet->portNo) { OVS_LOG_WARN("vport %u does not exist any more", vportGet->portNo); } else { OVS_LOG_WARN("vport %s does not exist any more", vportGet->name); } status = STATUS_DEVICE_DOES_NOT_EXIST; goto ext_info_done; } extInfo->dpNo = vportGet->dpNo; extInfo->portNo = vport->portNo; RtlCopyMemory(extInfo->macAddress, vport->currMacAddress, sizeof (vport->currMacAddress)); RtlCopyMemory(extInfo->permMACAddress, vport->permMacAddress, sizeof (vport->permMacAddress)); if (vport->ovsType == OVS_VPORT_TYPE_NETDEV) { RtlCopyMemory(extInfo->vmMACAddress, vport->vmMacAddress, sizeof (vport->vmMacAddress)); } extInfo->nicIndex = vport->nicIndex; extInfo->portId = vport->portId; extInfo->type = vport->ovsType; extInfo->mtu = vport->mtu; /* * TO be revisit XXX */ if (vport->ovsState == OVS_STATE_NIC_CREATED) { extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_DOWN; } else if (vport->ovsState == OVS_STATE_CONNECTED) { extInfo->status = OVS_EVENT_CONNECT | OVS_EVENT_LINK_UP; } else { extInfo->status = OVS_EVENT_DISCONNECT; } if (extInfo->type == OVS_VPORT_TYPE_NETDEV && (vport->ovsState == OVS_STATE_NIC_CREATED || vport->ovsState == OVS_STATE_CONNECTED)) { doConvert = TRUE; } else { extInfo->vmUUID[0] = 0; extInfo->vifUUID[0] = 0; } NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); if (doConvert) { status = OvsConvertIfCountedStrToAnsiStr(&vport->portFriendlyName, extInfo->name, OVS_MAX_PORT_NAME_LENGTH); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to convert NIC name."); extInfo->vmUUID[0] = 0; } status = OvsConvertIfCountedStrToAnsiStr(&vport->vmName, extInfo->vmUUID, OVS_MAX_VM_UUID_LEN); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to convert VM name."); extInfo->vmUUID[0] = 0; } status = OvsConvertIfCountedStrToAnsiStr(&vport->nicName, extInfo->vifUUID, OVS_MAX_VIF_UUID_LEN); if (status != STATUS_SUCCESS) { OVS_LOG_INFO("Fail to convert nic UUID"); extInfo->vifUUID[0] = 0; } /* * for now ignore status */ status = STATUS_SUCCESS; } ext_info_done: return status; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_WIN_NETDEV_CMD_GET'. * -------------------------------------------------------------------------- */ NTSTATUS OvsGetNetdevCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS status = STATUS_SUCCESS; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; NL_ERROR nlError = NL_ERROR_SUCCESS; OVS_VPORT_GET vportGet; OVS_VPORT_EXT_INFO info; static const NL_POLICY ovsNetdevPolicy[] = { [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .minLen = 2, .maxLen = IFNAMSIZ }, }; PNL_ATTR netdevAttrs[ARRAY_SIZE(ovsNetdevPolicy)]; /* input buffer has been validated while validating transaction dev op. */ ASSERT(usrParamsCtx->inputBuffer != NULL && usrParamsCtx->inputLength > sizeof *msgIn); if (msgOut == NULL || usrParamsCtx->outputLength < sizeof *msgOut) { return STATUS_INVALID_BUFFER_SIZE; } if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsNetdevPolicy, ARRAY_SIZE(ovsNetdevPolicy), netdevAttrs, ARRAY_SIZE(netdevAttrs))) { return STATUS_INVALID_PARAMETER; } vportGet.portNo = 0; RtlCopyMemory(&vportGet.name, NlAttrGet(netdevAttrs[OVS_VPORT_ATTR_NAME]), NlAttrGetSize(netdevAttrs[OVS_VPORT_ATTR_NAME])); status = OvsGetExtInfoIoctl(&vportGet, &info); if (status == STATUS_DEVICE_DOES_NOT_EXIST) { nlError = NL_ERROR_NODEV; goto cleanup; } status = CreateNetlinkMesgForNetdev(&info, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); if (status == STATUS_SUCCESS) { *replyLen = msgOut->nlMsg.nlmsgLen; } cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Utility function to construct an OVS_MESSAGE for the specified vport. The * OVS_MESSAGE contains the output of a netdev command. * -------------------------------------------------------------------------- */ static NTSTATUS CreateNetlinkMesgForNetdev(POVS_VPORT_EXT_INFO info, POVS_MESSAGE msgIn, PVOID outBuffer, UINT32 outBufLen, int dpIfIndex) { NL_BUFFER nlBuffer; BOOLEAN ok; PNL_MSG_HDR nlMsg; UINT32 netdevFlags = 0; NlBufInit(&nlBuffer, outBuffer, outBufLen); ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI, msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid, msgIn->genlMsg.cmd, msgIn->genlMsg.version, dpIfIndex); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_PORT_NO, info->portNo); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_TYPE, info->type); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailString(&nlBuffer, OVS_WIN_NETDEV_ATTR_NAME, info->name); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailUnspec(&nlBuffer, OVS_WIN_NETDEV_ATTR_MAC_ADDR, (PCHAR)info->macAddress, sizeof (info->macAddress)); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_MTU, info->mtu); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } if (info->status != OVS_EVENT_CONNECT) { netdevFlags = OVS_WIN_NETDEV_IFF_UP; } ok = NlMsgPutTailU32(&nlBuffer, OVS_WIN_NETDEV_ATTR_IF_FLAGS, netdevFlags); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } /* * XXX: add netdev_stats when we have the definition available in the * kernel. */ nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0); nlMsg->nlmsgLen = NlBufSize(&nlBuffer); return STATUS_SUCCESS; } static __inline VOID OvsWaitActivate(POVS_SWITCH_CONTEXT switchContext, ULONG sleepMicroSec) { while ((!switchContext->isActivated) && (!switchContext->isActivateFailed)) { /* Wait for the switch to be active and * the list of ports in OVS to be initialized. */ NdisMSleep(sleepMicroSec); } } static NTSTATUS OvsCreateMsgFromVport(POVS_VPORT_ENTRY vport, POVS_MESSAGE msgIn, PVOID outBuffer, UINT32 outBufLen, int dpIfIndex) { NL_BUFFER nlBuffer; OVS_VPORT_FULL_STATS vportStats; BOOLEAN ok; PNL_MSG_HDR nlMsg; NlBufInit(&nlBuffer, outBuffer, outBufLen); ok = NlFillOvsMsg(&nlBuffer, msgIn->nlMsg.nlmsgType, NLM_F_MULTI, msgIn->nlMsg.nlmsgSeq, msgIn->nlMsg.nlmsgPid, msgIn->genlMsg.cmd, msgIn->genlMsg.version, dpIfIndex); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_PORT_NO, vport->portNo); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_TYPE, vport->ovsType); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } ok = NlMsgPutTailString(&nlBuffer, OVS_VPORT_ATTR_NAME, vport->ovsName); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } /* * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath, * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set, * it means we have an array of pids, instead of a single pid. * ATM we assume we have one pid only. */ ok = NlMsgPutTailU32(&nlBuffer, OVS_VPORT_ATTR_UPCALL_PID, vport->upcallPid); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } /*stats*/ vportStats.rxPackets = vport->stats.rxPackets; vportStats.rxBytes = vport->stats.rxBytes; vportStats.txPackets = vport->stats.txPackets; vportStats.txBytes = vport->stats.txBytes; vportStats.rxErrors = vport->errStats.rxErrors; vportStats.txErrors = vport->errStats.txErrors; vportStats.rxDropped = vport->errStats.rxDropped; vportStats.txDropped = vport->errStats.txDropped; ok = NlMsgPutTailUnspec(&nlBuffer, OVS_VPORT_ATTR_STATS, (PCHAR)&vportStats, sizeof(OVS_VPORT_FULL_STATS)); if (!ok) { return STATUS_INVALID_BUFFER_SIZE; } /* * XXX: when vxlan udp dest port becomes configurable, we will also need * to add vport options */ nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuffer, 0, 0); nlMsg->nlmsgLen = NlBufSize(&nlBuffer); return STATUS_SUCCESS; } static NTSTATUS OvsGetVportDumpNext(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { POVS_MESSAGE msgIn; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; LOCK_STATE_EX lockState; UINT32 i = OVS_MAX_VPORT_ARRAY_SIZE; /* * XXX: this function shares some code with other dump command(s). * In the future, we will need to refactor the dump functions */ ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); if (instance->dumpState.ovsMsg == NULL) { ASSERT(FALSE); return STATUS_INVALID_DEVICE_STATE; } /* Output buffer has been validated while validating read dev op. */ ASSERT(usrParamsCtx->outputBuffer != NULL); msgIn = instance->dumpState.ovsMsg; /* * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath, * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set, * it means we have an array of pids, instead of a single pid. * ATM we assume we have one pid only. */ NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); if (gOvsSwitchContext->numHvVports > 0 || gOvsSwitchContext->numNonHvVports > 0) { /* inBucket: the bucket, used for lookup */ UINT32 inBucket = instance->dumpState.index[0]; /* inIndex: index within the given bucket, used for lookup */ UINT32 inIndex = instance->dumpState.index[1]; /* the bucket to be used for the next dump operation */ UINT32 outBucket = 0; /* the index within the outBucket to be used for the next dump */ UINT32 outIndex = 0; for (i = inBucket; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) { PLIST_ENTRY head, link; head = &(gOvsSwitchContext->portNoHashArray[i]); POVS_VPORT_ENTRY vport = NULL; outIndex = 0; LIST_FORALL(head, link) { /* * if one or more dumps were previously done on this same bucket, * inIndex will be > 0, so we'll need to reply with the * inIndex + 1 vport from the bucket. */ if (outIndex >= inIndex) { vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink); ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID); OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); ++outIndex; break; } ++outIndex; } if (vport) { break; } /* * if no vport was found above, check the next bucket, beginning * with the first (i.e. index 0) elem from within that bucket */ inIndex = 0; } outBucket = i; /* XXX: what about NLMSG_DONE (as msg type)? */ instance->dumpState.index[0] = outBucket; instance->dumpState.index[1] = outIndex; } NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); /* if i < OVS_MAX_VPORT_ARRAY_SIZE => vport was found */ if (i < OVS_MAX_VPORT_ARRAY_SIZE) { POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; *replyLen = msgOut->nlMsg.nlmsgLen; } else { /* * if i >= OVS_MAX_VPORT_ARRAY_SIZE => vport was not found => * it's dump done */ *replyLen = 0; /* Free up the dump state, since there's no more data to continue. */ FreeUserDumpState(instance); } return STATUS_SUCCESS; } static NTSTATUS OvsGetVport(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS status = STATUS_SUCCESS; LOCK_STATE_EX lockState; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_VPORT_ENTRY vport = NULL; NL_ERROR nlError = NL_ERROR_SUCCESS; PCHAR portName = NULL; UINT32 portNameLen = 0; UINT32 portNumber = OVS_DPPORT_NUMBER_INVALID; static const NL_POLICY ovsVportPolicy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .minLen = 2, .maxLen = IFNAMSIZ, .optional = TRUE}, }; PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)]; /* input buffer has been validated while validating write dev op. */ ASSERT(usrParamsCtx->inputBuffer != NULL); if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy), vportAttrs, ARRAY_SIZE(vportAttrs))) { return STATUS_INVALID_PARAMETER; } /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) { portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]); portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]); /* the port name is expected to be null-terminated */ ASSERT(portName[portNameLen - 1] == '\0'); vport = OvsFindVportByOvsName(gOvsSwitchContext, portName); } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) { portNumber = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]); vport = OvsFindVportByPortNo(gOvsSwitchContext, portNumber); } else { nlError = NL_ERROR_INVAL; NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); goto Cleanup; } if (!vport) { nlError = NL_ERROR_NODEV; NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); goto Cleanup; } status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); *replyLen = msgOut->nlMsg.nlmsgLen; Cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_VPORT_CMD_GET'. * * The function handles the initial call to setup the dump state, as well as * subsequent calls to continue dumping data. * -------------------------------------------------------------------------- */ NTSTATUS OvsGetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { *replyLen = 0; switch (usrParamsCtx->devOp) { case OVS_WRITE_DEV_OP: return OvsSetupDumpStart(usrParamsCtx); case OVS_READ_DEV_OP: return OvsGetVportDumpNext(usrParamsCtx, replyLen); case OVS_TRANSACTION_DEV_OP: return OvsGetVport(usrParamsCtx, replyLen); default: return STATUS_INVALID_DEVICE_REQUEST; } } static UINT32 OvsComputeVportNo(POVS_SWITCH_CONTEXT switchContext) { /* we are not allowed to create the port OVS_DPPORT_NUMBER_LOCAL */ for (ULONG i = OVS_DPPORT_NUMBER_LOCAL + 1; i < MAXUINT16; ++i) { POVS_VPORT_ENTRY vport; vport = OvsFindVportByPortNo(switchContext, i); if (!vport) { return i; } } return OVS_DPPORT_NUMBER_INVALID; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_VPORT_CMD_NEW'. * -------------------------------------------------------------------------- */ NTSTATUS OvsNewVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status = STATUS_SUCCESS; LOCK_STATE_EX lockState; NL_ERROR nlError = NL_ERROR_SUCCESS; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_VPORT_ENTRY vport = NULL; PCHAR portName; ULONG portNameLen; UINT32 portType; BOOLEAN vportAllocated = FALSE, vportInitialized = FALSE; static const NL_POLICY ovsVportPolicy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = FALSE}, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC, .optional = FALSE }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE }, }; PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)]; /* input buffer has been validated while validating write dev op. */ ASSERT(usrParamsCtx->inputBuffer != NULL); /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy), vportAttrs, ARRAY_SIZE(vportAttrs))) { return STATUS_INVALID_PARAMETER; } portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]); portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]); portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]); /* we are expecting null terminated strings to be passed */ ASSERT(portName[portNameLen - 1] == '\0'); NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0); vport = OvsFindVportByOvsName(gOvsSwitchContext, portName); if (vport) { nlError = NL_ERROR_EXIST; goto Cleanup; } if (portType == OVS_VPORT_TYPE_NETDEV) { /* External ports can also be looked up like VIF ports. */ vport = OvsFindVportByHvNameA(gOvsSwitchContext, portName); } else { ASSERT(OvsIsTunnelVportType(portType) || portType == OVS_VPORT_TYPE_INTERNAL); vport = (POVS_VPORT_ENTRY)OvsAllocateVport(); if (vport == NULL) { nlError = NL_ERROR_NOMEM; goto Cleanup; } vportAllocated = TRUE; if (OvsIsTunnelVportType(portType)) { UINT16 transportPortDest = 0; switch (portType) { case OVS_VPORT_TYPE_GRE: break; case OVS_VPORT_TYPE_VXLAN: transportPortDest = VXLAN_UDP_PORT; break; case OVS_VPORT_TYPE_STT: transportPortDest = STT_TCP_PORT; break; default: nlError = NL_ERROR_INVAL; goto Cleanup; } if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) { PNL_ATTR attr = NlAttrFindNested(vportAttrs[OVS_VPORT_ATTR_OPTIONS], OVS_TUNNEL_ATTR_DST_PORT); if (attr) { transportPortDest = NlAttrGetU16(attr); } } status = OvsInitTunnelVport(usrParamsCtx, vport, portType, transportPortDest); nlError = NlMapStatusToNlErr(status); } else { OvsInitBridgeInternalVport(vport); } vportInitialized = TRUE; if (nlError == NL_ERROR_SUCCESS) { vport->ovsState = OVS_STATE_CONNECTED; vport->nicState = NdisSwitchNicStateConnected; /* * Allow the vport to be deleted, because there is no * corresponding hyper-v switch part. */ vport->isAbsentOnHv = TRUE; } else { goto Cleanup; } } if (!vport) { nlError = NL_ERROR_INVAL; goto Cleanup; } if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) { nlError = NL_ERROR_EXIST; goto Cleanup; } if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) { /* * XXX: when we implement the limit for ovs port number to be * MAXUINT16, we'll need to check the port number received from the * userspace. */ vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]); } else { vport->portNo = OvsComputeVportNo(gOvsSwitchContext); if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) { nlError = NL_ERROR_NOMEM; goto Cleanup; } } /* The ovs port name must be uninitialized. */ ASSERT(vport->ovsName[0] == '\0'); ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH); RtlCopyMemory(vport->ovsName, portName, portNameLen); /* if we don't have options, then vport->portOptions will be NULL */ vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS]; /* * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath, * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set, * it means we have an array of pids, instead of a single pid. * ATM we assume we have one pid only. */ vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]); status = InitOvsVportCommon(gOvsSwitchContext, vport); ASSERT(status == STATUS_SUCCESS); status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); *replyLen = msgOut->nlMsg.nlmsgLen; Cleanup: NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; if (vport && vportAllocated == TRUE) { if (vportInitialized == TRUE) { if (OvsIsTunnelVportType(portType)) { switch (vport->ovsType) { case OVS_VPORT_TYPE_VXLAN: OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL); break; case OVS_VPORT_TYPE_STT: OvsCleanupSttTunnel(vport); break; default: ASSERT(!"Invalid tunnel port type"); } } } OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG); } NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_VPORT_CMD_SET'. * -------------------------------------------------------------------------- */ NTSTATUS OvsSetVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status = STATUS_SUCCESS; LOCK_STATE_EX lockState; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_VPORT_ENTRY vport = NULL; NL_ERROR nlError = NL_ERROR_SUCCESS; static const NL_POLICY ovsVportPolicy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE }, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC, .optional = TRUE }, [OVS_VPORT_ATTR_STATS] = { .type = NL_A_UNSPEC, .minLen = sizeof(OVS_VPORT_FULL_STATS), .maxLen = sizeof(OVS_VPORT_FULL_STATS), .optional = TRUE }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE }, }; PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)]; ASSERT(usrParamsCtx->inputBuffer != NULL); if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy), vportAttrs, ARRAY_SIZE(vportAttrs))) { return STATUS_INVALID_PARAMETER; } /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0); if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) { PSTR portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]); #ifdef DBG UINT32 portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]); #endif /* the port name is expected to be null-terminated */ ASSERT(portName[portNameLen - 1] == '\0'); vport = OvsFindVportByOvsName(gOvsSwitchContext, portName); } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) { vport = OvsFindVportByPortNo(gOvsSwitchContext, NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO])); } if (!vport) { nlError = NL_ERROR_NODEV; goto Cleanup; } /* * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath, * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set, * it means we have an array of pids, instead of a single pid. * Currently, we support only one pid. */ if (vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]) { vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]); } if (vportAttrs[OVS_VPORT_ATTR_TYPE]) { OVS_VPORT_TYPE type = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]); if (type != vport->ovsType) { nlError = NL_ERROR_INVAL; goto Cleanup; } } if (vportAttrs[OVS_VPORT_ATTR_OPTIONS]) { OVS_LOG_ERROR("Vport options not supported"); nlError = NL_ERROR_NOTSUPP; goto Cleanup; } status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); *replyLen = msgOut->nlMsg.nlmsgLen; Cleanup: NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_VPORT_CMD_DEL'. * -------------------------------------------------------------------------- */ NTSTATUS OvsDeleteVportCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status = STATUS_SUCCESS; LOCK_STATE_EX lockState; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_VPORT_ENTRY vport = NULL; NL_ERROR nlError = NL_ERROR_SUCCESS; PSTR portName = NULL; UINT32 portNameLen = 0; static const NL_POLICY ovsVportPolicy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = TRUE }, }; PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)]; ASSERT(usrParamsCtx->inputBuffer != NULL); if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy), vportAttrs, ARRAY_SIZE(vportAttrs))) { return STATUS_INVALID_PARAMETER; } /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NdisAcquireRWLockWrite(gOvsSwitchContext->dispatchLock, &lockState, 0); if (vportAttrs[OVS_VPORT_ATTR_NAME] != NULL) { portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]); portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]); /* the port name is expected to be null-terminated */ ASSERT(portName[portNameLen - 1] == '\0'); vport = OvsFindVportByOvsName(gOvsSwitchContext, portName); } else if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) { vport = OvsFindVportByPortNo(gOvsSwitchContext, NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO])); } if (!vport) { nlError = NL_ERROR_NODEV; goto Cleanup; } status = OvsCreateMsgFromVport(vport, msgIn, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, gOvsSwitchContext->dpNo); *replyLen = msgOut->nlMsg.nlmsgLen; /* * Mark the port as deleted from OVS userspace. If the port does not exist * on the Hyper-V switch, it gets deallocated. Otherwise, it stays. */ status = OvsRemoveAndDeleteVport(usrParamsCtx, gOvsSwitchContext, vport, FALSE, TRUE); if (status) { nlError = NlMapStatusToNlErr(status); } Cleanup: NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); if ((nlError != NL_ERROR_SUCCESS) && (nlError != NL_ERROR_PENDING)) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return (status == STATUS_PENDING) ? STATUS_PENDING : STATUS_SUCCESS; } static VOID OvsTunnelVportPendingRemove(PVOID context, NTSTATUS status, UINT32 *replyLen) { POVS_TUNFLT_INIT_CONTEXT tunnelContext = (POVS_TUNFLT_INIT_CONTEXT) context; POVS_SWITCH_CONTEXT switchContext = tunnelContext->switchContext; POVS_VPORT_ENTRY vport = tunnelContext->vport; POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer; NL_ERROR nlError = NlMapStatusToNlErr(status); LOCK_STATE_EX lockState; NdisAcquireRWLockWrite(switchContext->dispatchLock, &lockState, 0); if (msgIn && msgOut) { /* Check the received status to reply to the caller. */ if (STATUS_SUCCESS == status) { OvsCreateMsgFromVport(vport, msgIn, msgOut, tunnelContext->outputLength, switchContext->dpNo); *replyLen = msgOut->nlMsg.nlmsgLen; } else { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR)msgOut; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } } ASSERT(vport->isAbsentOnHv == TRUE); ASSERT(vport->portNo != OVS_DPPORT_NUMBER_INVALID); /* Remove the port from the relevant lists. */ switchContext->numNonHvVports--; RemoveEntryList(&vport->ovsNameLink); RemoveEntryList(&vport->portNoLink); RemoveEntryList(&vport->tunnelVportLink); if (vport->priv) { OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG); vport->priv = NULL; } OvsFreeMemoryWithTag(vport, OVS_VPORT_POOL_TAG); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); } static VOID OvsTunnelVportPendingInit(PVOID context, NTSTATUS status, UINT32 *replyLen) { POVS_TUNFLT_INIT_CONTEXT tunnelContext = (POVS_TUNFLT_INIT_CONTEXT) context; POVS_VPORT_ENTRY vport = tunnelContext->vport; POVS_MESSAGE msgIn = (POVS_MESSAGE)tunnelContext->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)tunnelContext->outputBuffer; PCHAR portName; ULONG portNameLen = 0; UINT32 portType = 0; NL_ERROR nlError = NL_ERROR_SUCCESS; BOOLEAN error = TRUE; do { if (!NT_SUCCESS(status)) { nlError = NlMapStatusToNlErr(status); break; } static const NL_POLICY ovsVportPolicy[] = { [OVS_VPORT_ATTR_PORT_NO] = { .type = NL_A_U32, .optional = TRUE }, [OVS_VPORT_ATTR_TYPE] = { .type = NL_A_U32, .optional = FALSE }, [OVS_VPORT_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ, .optional = FALSE }, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NL_A_UNSPEC, .optional = FALSE }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NL_A_NESTED, .optional = TRUE }, }; PNL_ATTR vportAttrs[ARRAY_SIZE(ovsVportPolicy)]; /* input buffer has been validated while validating write dev op. */ ASSERT(msgIn != NULL); /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && tunnelContext->outputLength >= sizeof *msgOut); if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsVportPolicy, ARRAY_SIZE(ovsVportPolicy), vportAttrs, ARRAY_SIZE(vportAttrs))) { nlError = NL_ERROR_INVAL; break; } portName = NlAttrGet(vportAttrs[OVS_VPORT_ATTR_NAME]); portNameLen = NlAttrGetSize(vportAttrs[OVS_VPORT_ATTR_NAME]); portType = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_TYPE]); if (vport->portNo != OVS_DPPORT_NUMBER_INVALID) { nlError = NL_ERROR_EXIST; break; } vport->ovsState = OVS_STATE_CONNECTED; vport->nicState = NdisSwitchNicStateConnected; /* * Allow the vport to be deleted, because there is no * corresponding hyper-v switch part. */ vport->isAbsentOnHv = TRUE; if (vportAttrs[OVS_VPORT_ATTR_PORT_NO] != NULL) { /* * XXX: when we implement the limit for OVS port number to be * MAXUINT16, we'll need to check the port number received from the * userspace. */ vport->portNo = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_PORT_NO]); } else { vport->portNo = OvsComputeVportNo(gOvsSwitchContext); if (vport->portNo == OVS_DPPORT_NUMBER_INVALID) { nlError = NL_ERROR_NOMEM; break; } } /* The ovs port name must be uninitialized. */ ASSERT(vport->ovsName[0] == '\0'); ASSERT(portNameLen <= OVS_MAX_PORT_NAME_LENGTH); RtlCopyMemory(vport->ovsName, portName, portNameLen); /* if we don't have options, then vport->portOptions will be NULL */ vport->portOptions = vportAttrs[OVS_VPORT_ATTR_OPTIONS]; /* * XXX: when we implement OVS_DP_ATTR_USER_FEATURES in datapath, * we'll need to check the OVS_DP_F_VPORT_PIDS flag: if it is set, * it means we have an array of pids, instead of a single pid. * ATM we assume we have one pid only. */ vport->upcallPid = NlAttrGetU32(vportAttrs[OVS_VPORT_ATTR_UPCALL_PID]); status = InitOvsVportCommon(gOvsSwitchContext, vport); ASSERT(status == STATUS_SUCCESS); OvsCreateMsgFromVport(vport, msgIn, msgOut, tunnelContext->outputLength, gOvsSwitchContext->dpNo); *replyLen = msgOut->nlMsg.nlmsgLen; error = FALSE; } while (error); if (error) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) msgOut; OvsCleanupVxlanTunnel(NULL, vport, NULL, NULL); OvsFreeMemory(vport); NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Switch.c0000644000000000000000000000013213534540071022057 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.449850978 openvswitch-2.5.9/datapath-windows/ovsext/Switch.c0000644000175000017500000005160213534540071023551 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file contains the implementation of the management functionality of the * OVS. */ #include "precomp.h" #include "Switch.h" #include "Vport.h" #include "Event.h" #include "Flow.h" #include "IpHelper.h" #include "Oid.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_SWITCH #include "Debug.h" POVS_SWITCH_CONTEXT gOvsSwitchContext; LONG volatile gOvsInAttach; UINT64 ovsTimeIncrementPerTick; extern NDIS_HANDLE gOvsExtDriverHandle; extern NDIS_HANDLE gOvsExtDriverObject; extern PDEVICE_OBJECT gOvsDeviceObject; /* * Reference count used to prevent premature deallocation of the global switch * context structure, gOvsSwitchContext. */ volatile LONG gOvsSwitchContextRefCount = 0; static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, POVS_SWITCH_CONTEXT *switchContextOut); static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext); static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext); static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext); static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext); /* * -------------------------------------------------------------------------- * Implements filter driver's FilterAttach function. * * This function allocates the switch context, and initializes its necessary * members. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsExtAttach(NDIS_HANDLE ndisFilterHandle, NDIS_HANDLE filterDriverContext, PNDIS_FILTER_ATTACH_PARAMETERS attachParameters) { NDIS_STATUS status = NDIS_STATUS_FAILURE; NDIS_FILTER_ATTRIBUTES ovsExtAttributes; POVS_SWITCH_CONTEXT switchContext = NULL; UNREFERENCED_PARAMETER(filterDriverContext); OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle); ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject); if (attachParameters->MiniportMediaType != NdisMedium802_3) { status = NDIS_STATUS_INVALID_PARAMETER; goto cleanup; } if (gOvsExtDriverHandle == NULL) { OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded."); ASSERT(FALSE); goto cleanup; } if (gOvsSwitchContext) { OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is" "supported, %p.", gOvsSwitchContext); goto cleanup; } if (InterlockedCompareExchange(&gOvsInAttach, 1, 0)) { /* Just fail the request. */ OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach" "instance is in attach process."); goto cleanup; } status = OvsInitIpHelper(ndisFilterHandle); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Exit: Failed to initialize IP helper."); goto cleanup; } status = OvsCreateSwitch(ndisFilterHandle, &switchContext); if (status != NDIS_STATUS_SUCCESS) { OvsCleanupIpHelper(); goto cleanup; } ASSERT(switchContext); /* * Register the switch context with NDIS so NDIS can pass it back to the * FilterXXX callback functions as the 'FilterModuleContext' parameter. */ RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES)); ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1; ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES); ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES; ovsExtAttributes.Flags = 0; NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT); status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_ERROR("Failed to set attributes."); OvsCleanupIpHelper(); goto cleanup; } /* Setup the state machine. */ switchContext->controlFlowState = OvsSwitchAttached; switchContext->dataFlowState = OvsSwitchPaused; gOvsSwitchContextRefCount = 1; gOvsSwitchContext = switchContext; KeMemoryBarrier(); cleanup: gOvsInAttach = FALSE; if (status != NDIS_STATUS_SUCCESS) { if (switchContext != NULL) { OvsDeleteSwitch(switchContext); } } OVS_LOG_TRACE("Exit: status %x", status); return status; } /* * -------------------------------------------------------------------------- * This function allocated the switch context, and initializes its necessary * members. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle, POVS_SWITCH_CONTEXT *switchContextOut) { NDIS_STATUS status; POVS_SWITCH_CONTEXT switchContext; NDIS_SWITCH_CONTEXT hostSwitchContext; NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler; OVS_LOG_TRACE("Enter: Create switch object"); switchContext = (POVS_SWITCH_CONTEXT) OvsAllocateMemoryWithTag( sizeof(OVS_SWITCH_CONTEXT), OVS_SWITCH_POOL_TAG); if (switchContext == NULL) { status = NDIS_STATUS_RESOURCES; goto create_switch_done; } RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT)); /* Initialize the switch. */ hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS; hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1; hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1; status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle, &hostSwitchContext, &hostSwitchHandler); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_ERROR("OvsExtAttach: Extension is running in " "non-switch environment."); OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG); goto create_switch_done; } switchContext->NdisFilterHandle = ndisFilterHandle; switchContext->NdisSwitchContext = hostSwitchContext; RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler, sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS)); status = OvsInitSwitchContext(switchContext); if (status != NDIS_STATUS_SUCCESS) { OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG); switchContext = NULL; goto create_switch_done; } status = OvsInitTunnelFilter(gOvsExtDriverObject, gOvsDeviceObject); if (status != NDIS_STATUS_SUCCESS) { OvsUninitSwitchContext(switchContext); goto create_switch_done; } status = OvsInitSttDefragmentation(); if (status != STATUS_SUCCESS) { OVS_LOG_ERROR("Exit: Failed to initialize Stt Defragmentation"); goto create_switch_done; } *switchContextOut = switchContext; create_switch_done: OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx", switchContext, status); return status; } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterDetach function. * -------------------------------------------------------------------------- */ _Use_decl_annotations_ VOID OvsExtDetach(NDIS_HANDLE filterModuleContext) { POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext; OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext); ASSERT(switchContext->dataFlowState == OvsSwitchPaused); switchContext->controlFlowState = OvsSwitchDetached; KeMemoryBarrier(); while(switchContext->pendingOidCount > 0) { NdisMSleep(1000); } OvsDeleteSwitch(switchContext); OvsCleanupIpHelper(); OvsCleanupSttDefragmentation(); /* This completes the cleanup, and a new attach can be handled now. */ OVS_LOG_TRACE("Exit: OvsDetach Successfully"); } /* * -------------------------------------------------------------------------- * This function deletes the switch by freeing all memory previously allocated. * XXX need synchronization with other path. * -------------------------------------------------------------------------- */ VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext) { UINT32 dpNo = (UINT32) -1; OVS_LOG_TRACE("Enter: switchContext:%p", switchContext); if (switchContext) { dpNo = switchContext->dpNo; OvsClearAllSwitchVports(switchContext); OvsUninitTunnelFilter(gOvsExtDriverObject); OvsUninitSwitchContext(switchContext); } OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo); } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterRestart function. * -------------------------------------------------------------------------- */ _Use_decl_annotations_ NDIS_STATUS OvsExtRestart(NDIS_HANDLE filterModuleContext, PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters) { POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext; NDIS_STATUS status = NDIS_STATUS_SUCCESS; BOOLEAN switchActive; UNREFERENCED_PARAMETER(filterRestartParameters); OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext); /* Activate the switch if this is the first restart. */ if (!switchContext->isActivated && !switchContext->isActivateFailed) { status = OvsQuerySwitchActivationComplete(switchContext, &switchActive); if (status != NDIS_STATUS_SUCCESS) { switchContext->isActivateFailed = TRUE; status = NDIS_STATUS_RESOURCES; goto cleanup; } if (switchActive) { status = OvsActivateSwitch(switchContext); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_WARN("Failed to activate switch, dpNo:%d", switchContext->dpNo); status = NDIS_STATUS_RESOURCES; goto cleanup; } } } ASSERT(switchContext->dataFlowState == OvsSwitchPaused); switchContext->dataFlowState = OvsSwitchRunning; cleanup: OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x", switchContext, switchContext->dpNo, status); return status; } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterPause function * -------------------------------------------------------------------------- */ NDIS_STATUS OvsExtPause(NDIS_HANDLE filterModuleContext, PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters) { POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext; UNREFERENCED_PARAMETER(pauseParameters); OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext); ASSERT(switchContext->dataFlowState == OvsSwitchRunning); switchContext->dataFlowState = OvsSwitchPaused; KeMemoryBarrier(); while(switchContext->pendingOidCount > 0) { NdisMSleep(1000); } OVS_LOG_TRACE("Exit: OvsDetach Successfully"); return NDIS_STATUS_SUCCESS; } static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext) { int i; NTSTATUS status; OVS_LOG_TRACE("Enter: switchContext: %p", switchContext); switchContext->dispatchLock = NdisAllocateRWLock(switchContext->NdisFilterHandle); switchContext->portNoHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG); switchContext->ovsPortNameHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG); switchContext->portIdHashArray= (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG); switchContext->pidHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE, OVS_SWITCH_POOL_TAG); switchContext->tunnelVportsArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag( sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG); status = OvsAllocateFlowTable(&switchContext->datapath, switchContext); if (status == NDIS_STATUS_SUCCESS) { status = OvsInitBufferPool(switchContext); } if (status != NDIS_STATUS_SUCCESS || switchContext->dispatchLock == NULL || switchContext->portNoHashArray == NULL || switchContext->ovsPortNameHashArray == NULL || switchContext->portIdHashArray== NULL || switchContext->pidHashArray == NULL || switchContext->tunnelVportsArray == NULL) { if (switchContext->dispatchLock) { NdisFreeRWLock(switchContext->dispatchLock); } if (switchContext->portNoHashArray) { OvsFreeMemoryWithTag(switchContext->portNoHashArray, OVS_SWITCH_POOL_TAG); } if (switchContext->ovsPortNameHashArray) { OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray, OVS_SWITCH_POOL_TAG); } if (switchContext->portIdHashArray) { OvsFreeMemoryWithTag(switchContext->portIdHashArray, OVS_SWITCH_POOL_TAG); } if (switchContext->pidHashArray) { OvsFreeMemoryWithTag(switchContext->pidHashArray, OVS_SWITCH_POOL_TAG); } if (switchContext->tunnelVportsArray) { OvsFreeMemory(switchContext->tunnelVportsArray); } OvsDeleteFlowTable(&switchContext->datapath); OvsCleanupBufferPool(switchContext); OVS_LOG_TRACE("Exit: Failed to init switchContext"); return NDIS_STATUS_RESOURCES; } for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) { InitializeListHead(&switchContext->ovsPortNameHashArray[i]); InitializeListHead(&switchContext->portIdHashArray[i]); InitializeListHead(&switchContext->portNoHashArray[i]); InitializeListHead(&switchContext->tunnelVportsArray[i]); } for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) { InitializeListHead(&switchContext->pidHashArray[i]); } NdisAllocateSpinLock(&(switchContext->pidHashLock)); switchContext->isActivated = FALSE; switchContext->isActivateFailed = FALSE; switchContext->dpNo = OVS_DP_NUMBER; ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000; OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p", switchContext); return NDIS_STATUS_SUCCESS; } static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext) { OvsReleaseSwitchContext(switchContext); } /* * -------------------------------------------------------------------------- * Frees up the contents of and also the switch context. * -------------------------------------------------------------------------- */ static VOID OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext) { OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext); /* We need to do cleanup for tunnel port here. */ ASSERT(switchContext->numHvVports == 0); ASSERT(switchContext->numNonHvVports == 0); NdisFreeRWLock(switchContext->dispatchLock); switchContext->dispatchLock = NULL; NdisFreeSpinLock(&(switchContext->pidHashLock)); OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray, OVS_SWITCH_POOL_TAG); switchContext->ovsPortNameHashArray = NULL; OvsFreeMemoryWithTag(switchContext->portIdHashArray, OVS_SWITCH_POOL_TAG); switchContext->portIdHashArray = NULL; OvsFreeMemoryWithTag(switchContext->portNoHashArray, OVS_SWITCH_POOL_TAG); switchContext->portNoHashArray = NULL; OvsFreeMemoryWithTag(switchContext->pidHashArray, OVS_SWITCH_POOL_TAG); switchContext->pidHashArray = NULL; OvsFreeMemory(switchContext->tunnelVportsArray); switchContext->tunnelVportsArray = NULL; OvsDeleteFlowTable(&switchContext->datapath); OvsCleanupBufferPool(switchContext); OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG); OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext); } VOID OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext) { LONG ref = 0; LONG newRef = 0; LONG icxRef = 0; do { ref = gOvsSwitchContextRefCount; newRef = (0 == ref) ? 0 : ref - 1; icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount, newRef, ref); } while (icxRef != ref); if (ref == 1) { OvsDeleteSwitchContext(switchContext); gOvsSwitchContext = NULL; } } BOOLEAN OvsAcquireSwitchContext(VOID) { LONG ref = 0; LONG newRef = 0; LONG icxRef = 0; BOOLEAN ret = FALSE; do { ref = gOvsSwitchContextRefCount; newRef = (0 == ref) ? 0 : ref + 1; icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount, newRef, ref); } while (icxRef != ref); if (ref != 0) { ret = TRUE; } return ret; } /* * -------------------------------------------------------------------------- * This function activates the switch by initializing it with all the runtime * state. First it queries all of the MAC addresses set as custom switch policy * to allow sends from, and adds tme to the property list. Then it queries the * NIC list and verifies it can support all of the NICs currently connected to * the switch, and adds the NICs to the NIC list. * -------------------------------------------------------------------------- */ static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext) { NDIS_STATUS status; ASSERT(!switchContext->isActivated); OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld", switchContext, switchContext->dpNo); switchContext->isActivated = TRUE; status = OvsAddConfiguredSwitchPorts(switchContext); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_WARN("Failed to add configured switch ports"); goto cleanup; } status = OvsInitConfiguredSwitchNics(switchContext); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_WARN("Failed to add configured vports"); OvsClearAllSwitchVports(switchContext); goto cleanup; } cleanup: if (status != NDIS_STATUS_SUCCESS) { switchContext->isActivated = TRUE; } OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx", switchContext, (switchContext->isActivated ? "TRUE" : "FALSE"), status); return status; } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterNetPnPEvent function. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext, PNET_PNP_EVENT_NOTIFICATION netPnPEvent) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext; BOOLEAN switchActive; OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d", filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent); /* * The only interesting event is the NetEventSwitchActivate. It provides * an asynchronous notification of the switch completing activation. */ if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) { status = OvsQuerySwitchActivationComplete(switchContext, &switchActive); if (status != NDIS_STATUS_SUCCESS) { switchContext->isActivateFailed = TRUE; } else { ASSERT(switchContext->isActivated == FALSE); if (switchContext->isActivated == FALSE && switchActive == TRUE) { status = OvsActivateSwitch(switchContext); OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p " "status: %s", switchContext, status ? "TRUE" : "FALSE"); } } } if (status == NDIS_STATUS_SUCCESS) { status = NdisFNetPnPEvent(switchContext->NdisFilterHandle, netPnPEvent); } OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent"); return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/PacketIO.c0000644000000000000000000000013213534540071022255 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.441850919 openvswitch-2.5.9/datapath-windows/ovsext/PacketIO.c0000644000175000017500000004636513534540071023761 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file contains the implementation of the datapath/forwarding * functionality of the OVS. */ #include "precomp.h" #include "Switch.h" #include "Vport.h" #include "NetProto.h" #include "User.h" #include "PacketIO.h" #include "Flow.h" #include "Event.h" #include "User.h" /* Due to an imported header file */ #pragma warning( disable:4505 ) #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_DISPATCH #include "Debug.h" extern NDIS_STRING ovsExtGuidUC; extern NDIS_STRING ovsExtFriendlyNameUC; static VOID OvsFinalizeCompletionList(OvsCompletionList *completionList); static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags); static NTSTATUS OvsCreateNewNBLsFromMultipleNBs( POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *curNbl, PNET_BUFFER_LIST *nextNbl); __inline VOID OvsInitCompletionList(OvsCompletionList *completionList, POVS_SWITCH_CONTEXT switchContext, ULONG sendCompleteFlags) { ASSERT(completionList); completionList->dropNbl = NULL; completionList->dropNblNext = &completionList->dropNbl; completionList->switchContext = switchContext; completionList->sendCompleteFlags = sendCompleteFlags; } /* Utility function used to complete an NBL. */ __inline VOID OvsAddPktCompletionList(OvsCompletionList *completionList, BOOLEAN incoming, NDIS_SWITCH_PORT_ID sourcePort, PNET_BUFFER_LIST netBufferList, UINT32 netBufferListCount, PNDIS_STRING filterReason) { POVS_BUFFER_CONTEXT ctx; /* XXX: We handle one NBL at a time. */ ASSERT(netBufferList->Next == NULL); /* Make sure it has a context. */ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(netBufferList); ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); completionList->switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists( completionList->switchContext->NdisSwitchContext, &ovsExtGuidUC, &ovsExtFriendlyNameUC, sourcePort, incoming ? NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING : 0, netBufferListCount, netBufferList, filterReason); *completionList->dropNblNext = netBufferList; completionList->dropNblNext = &netBufferList->Next; ASSERT(completionList->dropNbl); } static __inline VOID OvsReportNBLIngressError(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST nblList, PNDIS_STRING filterReason, NDIS_STATUS error) { PNET_BUFFER_LIST nbl = nblList; while (nbl) { PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl); nbl->Status = error; /* This can be optimized by batching NBL's from the same * SourcePortId. */ switchContext->NdisSwitchHandlers.ReportFilteredNetBufferLists( switchContext->NdisSwitchContext, &ovsExtGuidUC, &ovsExtFriendlyNameUC, fwdDetail->SourcePortId, NDIS_SWITCH_REPORT_FILTERED_NBL_FLAGS_IS_INCOMING, 1 /*Nbl count.*/, nbl, filterReason); nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } } static __inline ULONG OvsGetSendCompleteFlags(ULONG sendFlags) { BOOLEAN dispatch, sameSource; ULONG sendCompleteFlags; dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags); sendCompleteFlags = (dispatch ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL : 0); sameSource = NDIS_TEST_SEND_FLAG(sendFlags, NDIS_SEND_FLAGS_SWITCH_SINGLE_SOURCE); sendCompleteFlags |= (sameSource ? NDIS_SEND_COMPLETE_FLAGS_SWITCH_SINGLE_SOURCE : 0); return sendCompleteFlags; } VOID OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST netBufferLists, ULONG sendFlags) { if (switchContext->dataFlowState == OvsSwitchPaused) { /* If a filter module is in the Paused state, the filter driver must not * originate any send requests for that filter module. If NDIS calls * FilterSendNetBufferLists, the driver must not call * NdisFSendNetBufferLists to pass on the data until the driver is * restarted. The driver should call NdisFSendNetBufferListsComplete * immediately to complete the send operation. It should set the * complete status in each NET_BUFFER_LIST structure to * NDIS_STATUS_PAUSED. * * http://msdn.microsoft.com/en-us/library/windows/hardware/ * ff549966(v=vs.85).aspx */ NDIS_STRING filterReason; ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags); RtlInitUnicodeString(&filterReason, L"Switch state PAUSED, drop before FSendNBL."); OvsReportNBLIngressError(switchContext, netBufferLists, &filterReason, NDIS_STATUS_PAUSED); OvsCompleteNBLIngress(switchContext, netBufferLists, sendCompleteFlags); return; } ASSERT(switchContext->dataFlowState == OvsSwitchRunning); NdisFSendNetBufferLists(switchContext->NdisFilterHandle, netBufferLists, NDIS_DEFAULT_PORT_NUMBER, sendFlags); } static __inline VOID OvsStartNBLIngressError(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST nblList, ULONG sendCompleteFlags, PNDIS_STRING filterReason, NDIS_STATUS error) { ASSERT(error); OvsReportNBLIngressError(switchContext, nblList, filterReason, error); NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, nblList, sendCompleteFlags); } static VOID OvsAppendNativeForwardedPacket(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, PNET_BUFFER_LIST *nativeNbls, ULONG flags, BOOLEAN isRecv) { POVS_BUFFER_CONTEXT ctx = { 0 }; NDIS_STRING filterReason; *nativeNbls = curNbl; nativeNbls = &(curNbl->Next); ctx = OvsInitExternalNBLContext(switchContext, curNbl, isRecv); if (ctx == NULL) { RtlInitUnicodeString(&filterReason, L"Cannot allocate native NBL context."); OvsStartNBLIngressError(switchContext, curNbl, flags, &filterReason, NDIS_STATUS_RESOURCES); } } static VOID OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST netBufferLists, ULONG SendFlags) { NDIS_SWITCH_PORT_ID sourcePort = 0; NDIS_SWITCH_NIC_INDEX sourceIndex = 0; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL; ULONG sendCompleteFlags; UCHAR dispatch; LOCK_STATE_EX lockState, dpLockState; NDIS_STATUS status; NDIS_STRING filterReason; LIST_ENTRY missedPackets; UINT32 num = 0; OvsCompletionList completionList; #if (NDIS_SUPPORT_NDIS640) PNET_BUFFER_LIST nativeForwardedNbls = NULL; PNET_BUFFER_LIST *nextNativeForwardedNbl = &nativeForwardedNbls; #endif dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)? NDIS_RWL_AT_DISPATCH_LEVEL : 0; sendCompleteFlags = OvsGetSendCompleteFlags(SendFlags); SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP; InitializeListHead(&missedPackets); OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags); for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) { POVS_VPORT_ENTRY vport; UINT32 portNo; OVS_DATAPATH *datapath = &switchContext->datapath; OVS_PACKET_HDR_INFO layers; OvsFlowKey key; UINT64 hash; PNET_BUFFER curNb; POVS_BUFFER_CONTEXT ctx; nextNbl = curNbl->Next; curNbl->Next = NULL; fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); sourcePort = fwdDetail->SourcePortId; sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex; #if (NDIS_SUPPORT_NDIS640) if (fwdDetail->NativeForwardingRequired) { /* Add current NBL to those that require native forwarding. */ OvsAppendNativeForwardedPacket( switchContext, curNbl, nextNativeForwardedNbl, sendCompleteFlags, sourcePort == switchContext->virtualExternalPortId); continue; } #endif /* NDIS_SUPPORT_NDIS640 */ ctx = OvsInitExternalNBLContext(switchContext, curNbl, sourcePort == switchContext->virtualExternalPortId); if (ctx == NULL) { RtlInitUnicodeString(&filterReason, L"Cannot allocate external NBL context."); OvsStartNBLIngressError(switchContext, curNbl, sendCompleteFlags, &filterReason, NDIS_STATUS_RESOURCES); continue; } /* Ethernet Header is a guaranteed safe access. */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); if (curNb->Next != NULL) { /* Create a NET_BUFFER_LIST for each NET_BUFFER. */ status = OvsCreateNewNBLsFromMultipleNBs(switchContext, &curNbl, &nextNbl); if (!NT_SUCCESS(status)) { RtlInitUnicodeString(&filterReason, L"Cannot allocate NBLs with single NB."); OvsStartNBLIngressError(switchContext, curNbl, sendCompleteFlags, &filterReason, NDIS_STATUS_RESOURCES); continue; } } { OvsFlow *flow; /* Take the DispatchLock so none of the VPORTs disconnect while * we are setting destination ports. * * XXX: acquire/release the dispatch lock for a "batch" of packets * rather than for each packet. */ NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState, dispatch); vport = OvsFindVportByPortIdAndNicIndex(switchContext, sourcePort, sourceIndex); if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) { RtlInitUnicodeString(&filterReason, L"OVS-Cannot forward packet from unknown source port"); goto dropit; } else { portNo = vport->portNo; } vport->stats.rxPackets++; vport->stats.rxBytes += NET_BUFFER_DATA_LENGTH(curNb); status = OvsExtractFlow(curNbl, vport->portNo, &key, &layers, NULL); if (status != NDIS_STATUS_SUCCESS) { RtlInitUnicodeString(&filterReason, L"OVS-Flow extract failed"); goto dropit; } ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); OvsAcquireDatapathRead(datapath, &dpLockState, TRUE); flow = OvsLookupFlow(datapath, &key, &hash, FALSE); if (flow) { OvsFlowUsed(flow, curNbl, &layers); datapath->hits++; /* If successful, OvsActionsExecute() consumes the NBL. * Otherwise, it adds it to the completionList. No need to * check the return value. */ OvsActionsExecute(switchContext, &completionList, curNbl, portNo, SendFlags, &key, &hash, &layers, flow->actions, flow->actionsLen); OvsReleaseDatapath(datapath, &dpLockState); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); continue; } else { OvsReleaseDatapath(datapath, &dpLockState); datapath->misses++; status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport, &key, curNbl, sourcePort == switchContext->virtualExternalPortId, &layers, switchContext, &missedPackets, &num); if (status == NDIS_STATUS_SUCCESS) { /* Complete the packet since it was copied to user * buffer. */ RtlInitUnicodeString(&filterReason, L"OVS-Dropped since packet was copied to userspace"); } else { RtlInitUnicodeString(&filterReason, L"OVS-Dropped due to failure to queue to userspace"); } goto dropit; } dropit: OvsAddPktCompletionList(&completionList, TRUE, sourcePort, curNbl, 0, &filterReason); NdisReleaseRWLock(switchContext->dispatchLock, &lockState); } } #if (NDIS_SUPPORT_NDIS640) if (nativeForwardedNbls) { /* This is NVGRE encapsulated traffic and is forwarded to NDIS * in order to be handled by the HNV module. */ OvsSendNBLIngress(switchContext, nativeForwardedNbls, SendFlags); } #endif /* NDIS_SUPPORT_NDIS640 */ /* Queue the missed packets. */ OvsQueuePackets(&missedPackets, num); OvsFinalizeCompletionList(&completionList); } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterSendNetBufferLists Function. * -------------------------------------------------------------------------- */ VOID OvsExtSendNBL(NDIS_HANDLE filterModuleContext, PNET_BUFFER_LIST netBufferLists, NDIS_PORT_NUMBER portNumber, ULONG sendFlags) { UNREFERENCED_PARAMETER(portNumber); /* 'filterModuleContext' is the switch context that gets created in the * AttachHandler. */ POVS_SWITCH_CONTEXT switchContext; switchContext = (POVS_SWITCH_CONTEXT) filterModuleContext; if (switchContext->dataFlowState == OvsSwitchPaused) { NDIS_STRING filterReason; ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags); RtlInitUnicodeString(&filterReason, L"Switch state PAUSED, drop on ingress."); OvsStartNBLIngressError(switchContext, netBufferLists, sendCompleteFlags, &filterReason, NDIS_STATUS_PAUSED); return; } ASSERT(switchContext->dataFlowState == OvsSwitchRunning); OvsStartNBLIngress(switchContext, netBufferLists, sendFlags); } static VOID OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags) { PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL; OvsCompletionList newList; newList.dropNbl = NULL; newList.dropNblNext = &newList.dropNbl; for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) { nextNbl = curNbl->Next; curNbl->Next = NULL; curNbl = OvsCompleteNBL(switchContext, curNbl, TRUE); if (curNbl != NULL) { /* NBL originated from the upper layer. */ *newList.dropNblNext = curNbl; newList.dropNblNext = &curNbl->Next; } } /* Complete the NBL's that were sent by the upper layer. */ if (newList.dropNbl != NULL) { NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, newList.dropNbl, sendCompleteFlags); } } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterSendNetBufferListsComplete function. * -------------------------------------------------------------------------- */ VOID OvsExtSendNBLComplete(NDIS_HANDLE filterModuleContext, PNET_BUFFER_LIST netBufferLists, ULONG sendCompleteFlags) { OvsCompleteNBLIngress((POVS_SWITCH_CONTEXT)filterModuleContext, netBufferLists, sendCompleteFlags); } VOID OvsFinalizeCompletionList(OvsCompletionList *completionList) { if (completionList->dropNbl != NULL) { OvsCompleteNBLIngress(completionList->switchContext, completionList->dropNbl, completionList->sendCompleteFlags); completionList->dropNbl = NULL; completionList->dropNblNext = &completionList->dropNbl; } } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterCancelSendNetBufferLists function. * * "If a filter driver specifies a FilterSendNetBufferLists function and it * queues send requests, it must also specify a * FilterCancelSendNetBufferLists function." * * http://msdn.microsoft.com/en-us/library/windows/hardware/ * ff549966(v=vs.85).aspx * -------------------------------------------------------------------------- */ VOID OvsExtCancelSendNBL(NDIS_HANDLE filterModuleContext, PVOID CancelId) { UNREFERENCED_PARAMETER(filterModuleContext); UNREFERENCED_PARAMETER(CancelId); /* All send requests get completed synchronously, so there is no need to * implement this callback. */ } static NTSTATUS OvsCreateNewNBLsFromMultipleNBs(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *curNbl, PNET_BUFFER_LIST *nextNbl) { NTSTATUS status = STATUS_SUCCESS; PNET_BUFFER_LIST newNbls = NULL; PNET_BUFFER_LIST lastNbl = NULL; PNET_BUFFER_LIST nbl = NULL; BOOLEAN error = TRUE; do { /* Create new NBLs from curNbl with multiple net buffers. */ newNbls = OvsPartialCopyToMultipleNBLs(switchContext, *curNbl, 0, 0, TRUE); if (NULL == newNbls) { OVS_LOG_ERROR("Failed to allocate NBLs with single NB."); status = NDIS_STATUS_RESOURCES; break; } nbl = newNbls; while (nbl) { lastNbl = nbl; nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } lastNbl->Next = *nextNbl; *nextNbl = newNbls->Next; OvsCompleteNBL(switchContext, *curNbl, TRUE); *curNbl = newNbls; (*curNbl)->Next = NULL; error = FALSE; } while (error); return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/precompsrc.c0000644000000000000000000000013213534540071022773 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801424.473851155 openvswitch-2.5.9/datapath-windows/ovsext/precompsrc.c0000644000175000017500000000115713534540071024465 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/PacketIO.h0000644000000000000000000000013213534540071022262 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.441850919 openvswitch-2.5.9/datapath-windows/ovsext/PacketIO.h0000644000175000017500000000440213534540071023750 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PACKETIO_H_ #define __PACKETIO_H_ 1 typedef union _OVS_PACKET_HDR_INFO OVS_PACKET_HDR_INFO; /* * Data structures and utility functions to help manage a list of packets to be * completed (dropped). */ typedef struct OvsCompletionList { PNET_BUFFER_LIST dropNbl; PNET_BUFFER_LIST *dropNblNext; POVS_SWITCH_CONTEXT switchContext; ULONG sendCompleteFlags; } OvsCompletionList; VOID OvsInitCompletionList(OvsCompletionList *completionList, POVS_SWITCH_CONTEXT switchContext, ULONG sendCompleteFlags); VOID OvsAddPktCompletionList(OvsCompletionList *completionList, BOOLEAN incoming, NDIS_SWITCH_PORT_ID sourcePort, PNET_BUFFER_LIST netBufferList, UINT32 netBufferListCount, PNDIS_STRING filterReason); /* * Functions related to packet processing. */ VOID OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST netBufferLists, ULONG sendFlags); NDIS_STATUS OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext, OvsCompletionList *completionList, PNET_BUFFER_LIST curNbl, UINT32 srcVportNo, ULONG sendFlags, OvsFlowKey *key, UINT64 *hash, OVS_PACKET_HDR_INFO *layers, const PNL_ATTR actions, int actionsLen); VOID OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext, VOID *compList, PNET_BUFFER_LIST curNbl); #endif /* __PACKETIO_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Util.h0000644000000000000000000000013213534540071021540 xustar0030 mtime=1567801401.229679907 30 atime=1567801402.049685929 30 ctime=1567801424.461851067 openvswitch-2.5.9/datapath-windows/ovsext/Util.h0000644000175000017500000000723513534540071023235 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __UTIL_H_ #define __UTIL_H_ 1 #define OVS_MEMORY_TAG 'TSVO' #define OVS_FIX_SIZE_NBL_POOL_TAG 'FSVO' #define OVS_VARIABLE_SIZE_NBL_POOL_TAG 'VSVO' #define OVS_NBL_ONLY_POOL_TAG 'OSVO' #define OVS_NET_BUFFER_POOL_TAG 'NSVO' #define OVS_OTHER_POOL_TAG 'MSVO' #define OVS_MDL_POOL_TAG 'BSVO' #define OVS_DATAPATH_POOL_TAG 'DSVO' #define OVS_EVENT_POOL_TAG 'ESVO' #define OVS_FLOW_POOL_TAG 'LSVO' #define OVS_VXLAN_POOL_TAG 'XSVO' #define OVS_IPHELPER_POOL_TAG 'HSVO' #define OVS_OID_POOL_TAG 'ASVO' #define OVS_SWITCH_POOL_TAG 'SSVO' #define OVS_USER_POOL_TAG 'USVO' #define OVS_VPORT_POOL_TAG 'PSVO' #define OVS_STT_POOL_TAG 'RSVO' #define OVS_GRE_POOL_TAG 'GSVO' #define OVS_TUNFLT_POOL_TAG 'WSVO' VOID *OvsAllocateMemory(size_t size); VOID *OvsAllocateMemoryWithTag(size_t size, ULONG tag); VOID *OvsAllocateAlignedMemory(size_t size, UINT16 align); VOID OvsFreeMemory(VOID *ptr); VOID OvsFreeMemoryWithTag(VOID *ptr, ULONG tag); VOID OvsFreeAlignedMemory(VOID *ptr); #define LIST_FORALL(_headPtr, _itemPtr) \ for (_itemPtr = (_headPtr)->Flink; \ _itemPtr != _headPtr; \ _itemPtr = (_itemPtr)->Flink) #define LIST_FORALL_SAFE(_headPtr, _itemPtr, _nextPtr) \ for (_itemPtr = (_headPtr)->Flink, _nextPtr = (_itemPtr)->Flink; \ _itemPtr != _headPtr; \ _itemPtr = _nextPtr, _nextPtr = (_itemPtr)->Flink) #define LIST_FORALL_REVERSE(_headPtr, _itemPtr) \ for (_itemPtr = (_headPtr)->Blink; \ _itemPtr != _headPtr; \ _itemPtr = (_itemPtr)->Blink) #define LIST_FORALL_REVERSE_SAFE(_headPtr, _itemPtr, _nextPtr) \ for (_itemPtr = (_headPtr)->Blink, _nextPtr = (_itemPtr)->Blink; \ _itemPtr != _headPtr; \ _itemPtr = _nextPtr, _nextPtr = (_itemPtr)->Blink) VOID OvsAppendList(PLIST_ENTRY dst, PLIST_ENTRY src); #define MIN(_a, _b) ((_a) > (_b) ? (_b) : (_a)) #define ARRAY_SIZE(_x) ((sizeof(_x))/sizeof (_x)[0]) #define OVS_SWITCH_PORT_ID_INVALID (NDIS_SWITCH_PORT_ID)(-1) #ifndef htons #define htons(_x) _byteswap_ushort((USHORT)(_x)) #define ntohs(_x) _byteswap_ushort((USHORT)(_x)) #define htonl(_x) _byteswap_ulong((ULONG)(_x)) #define ntohl(_x) _byteswap_ulong((ULONG)(_x)) #endif #define OVS_INIT_OBJECT_HEADER(_obj, _type, _revision, _size) \ { \ PNDIS_OBJECT_HEADER hdrp = _obj; \ hdrp->Type = _type; \ hdrp->Revision = _revision; \ hdrp->Size = _size; \ } #define BIT16(_x) ((UINT16)0x1 << (_x)) #define BIT32(_x) ((UINT32)0x1 << (_x)) BOOLEAN OvsCompareString(PVOID string1, PVOID string2); #endif /* __UTIL_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Event.c0000644000000000000000000000013113534540071021676 xustar0029 mtime=1567801401.21367979 30 atime=1567801402.045685899 30 ctime=1567801424.413850713 openvswitch-2.5.9/datapath-windows/ovsext/Event.c0000644000175000017500000003100613534540071023365 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Switch.h" #include "User.h" #include "Datapath.h" #include "Vport.h" #include "Event.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_EVENT #include "Debug.h" LIST_ENTRY ovsEventQueue; static NDIS_SPIN_LOCK eventQueueLock; UINT32 ovsNumEventQueue; NTSTATUS OvsInitEventQueue() { InitializeListHead(&ovsEventQueue); NdisAllocateSpinLock(&eventQueueLock); return STATUS_SUCCESS; } VOID OvsCleanupEventQueue() { ASSERT(IsListEmpty(&ovsEventQueue)); ASSERT(ovsNumEventQueue == 0); NdisFreeSpinLock(&eventQueueLock); } static __inline VOID OvsAcquireEventQueueLock() { NdisAcquireSpinLock(&eventQueueLock); } static __inline VOID OvsReleaseEventQueueLock() { NdisReleaseSpinLock(&eventQueueLock); } /* * -------------------------------------------------------------------------- * Cleanup the event queue of the OpenInstance. * -------------------------------------------------------------------------- */ VOID OvsCleanupEvent(POVS_OPEN_INSTANCE instance) { POVS_EVENT_QUEUE queue; PIRP irp = NULL; queue = (POVS_EVENT_QUEUE)instance->eventQueue; if (queue) { POVS_EVENT_QUEUE_ELEM elem; PLIST_ENTRY link, next; OvsAcquireEventQueueLock(); RemoveEntryList(&queue->queueLink); ovsNumEventQueue--; if (queue->pendingIrp) { PDRIVER_CANCEL cancelRoutine; irp = queue->pendingIrp; cancelRoutine = IoSetCancelRoutine(irp, NULL); queue->pendingIrp = NULL; if (cancelRoutine == NULL) { irp = NULL; } } instance->eventQueue = NULL; OvsReleaseEventQueueLock(); if (irp) { OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); } LIST_FORALL_SAFE(&queue->elemList, link, next) { elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link); OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG); } OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG); } } /* * -------------------------------------------------------------------------- * When event is generated, we need to post the event to all * the event queues. If there is pending Irp waiting for event * complete the Irp to wakeup the user thread. * * Side effects: User thread may be woken up. * -------------------------------------------------------------------------- */ VOID OvsPostEvent(POVS_EVENT_ENTRY event) { POVS_EVENT_QUEUE_ELEM elem; POVS_EVENT_QUEUE queue; PLIST_ENTRY link; LIST_ENTRY list; PLIST_ENTRY entry; PIRP irp; InitializeListHead(&list); OVS_LOG_TRACE("Enter: portNo: %#x, status: %#x", event->portNo, event->type); OvsAcquireEventQueueLock(); LIST_FORALL(&ovsEventQueue, link) { queue = CONTAINING_RECORD(link, OVS_EVENT_QUEUE, queueLink); if ((event->type & queue->mask) == 0) { continue; } event->type &= queue->mask; elem = (POVS_EVENT_QUEUE_ELEM)OvsAllocateMemoryWithTag( sizeof(*elem), OVS_EVENT_POOL_TAG); RtlCopyMemory(&elem->event, event, sizeof elem->event); InsertTailList(&queue->elemList, &elem->link); queue->numElems++; OVS_LOG_INFO("Queue: %p, numElems: %d", queue, queue->numElems); if (queue->pendingIrp != NULL) { PDRIVER_CANCEL cancelRoutine; irp = queue->pendingIrp; queue->pendingIrp = NULL; cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine) { InsertTailList(&list, &irp->Tail.Overlay.ListEntry); } } } OvsReleaseEventQueueLock(); while (!IsListEmpty(&list)) { entry = RemoveHeadList(&list); irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); OVS_LOG_INFO("Wakeup thread with IRP: %p", irp); OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); } } /* * -------------------------------------------------------------------------- * Subscribe for event notification. * * Results: * STATUS_SUCCESS for valid request and enough resource. * STATUS_NO_RESOURCES for queue allocation failure * STATUS_INVALID_PARAMETER for invalid request * * Side effects: * Event queue is created for the current open instance. * -------------------------------------------------------------------------- */ NTSTATUS OvsSubscribeEventIoctl(PFILE_OBJECT fileObject, PVOID inputBuffer, UINT32 inputLength) { POVS_EVENT_SUBSCRIBE request = (POVS_EVENT_SUBSCRIBE)inputBuffer; NTSTATUS status = STATUS_SUCCESS; POVS_OPEN_INSTANCE instance; POVS_EVENT_QUEUE queue = NULL; OVS_LOG_TRACE("Enter: fileObject: %p, inputLength: %d", fileObject, inputLength); if (inputLength < sizeof (OVS_EVENT_SUBSCRIBE) || (request->mask & OVS_EVENT_MASK_ALL) == 0) { OVS_LOG_TRACE("Exit: subscribe failed with invalid request."); return STATUS_INVALID_PARAMETER; } OvsAcquireEventQueueLock(); instance = OvsGetOpenInstance(fileObject, request->dpNo); if (instance == NULL) { status = STATUS_INVALID_PARAMETER; OVS_LOG_WARN("can not find open instance"); goto done_event_subscribe; } /* * XXX for now, we don't allow change mask. */ queue = (POVS_EVENT_QUEUE)instance->eventQueue; if (request->subscribe && queue) { if (queue->mask != request->mask) { status = STATUS_INVALID_PARAMETER; OVS_LOG_WARN("Can not chnage mask when the queue is subscribed"); } status = STATUS_SUCCESS; goto done_event_subscribe; } else if (!request->subscribe && queue == NULL) { status = STATUS_SUCCESS; goto done_event_subscribe; } if (request->subscribe) { queue = (POVS_EVENT_QUEUE)OvsAllocateMemoryWithTag( sizeof(OVS_EVENT_QUEUE), OVS_EVENT_POOL_TAG); if (queue == NULL) { status = STATUS_NO_MEMORY; OVS_LOG_WARN("Fail to allocate event queue"); goto done_event_subscribe; } InitializeListHead(&queue->elemList); queue->mask = request->mask; queue->pendingIrp = NULL; queue->numElems = 0; InsertHeadList(&ovsEventQueue, &queue->queueLink); ovsNumEventQueue++; instance->eventQueue = queue; queue->instance = instance; } else { queue = (POVS_EVENT_QUEUE)instance->eventQueue; RemoveEntryList(&queue->queueLink); ovsNumEventQueue--; instance->eventQueue = NULL; } done_event_subscribe: if (!request->subscribe && queue) { POVS_EVENT_QUEUE_ELEM elem; PLIST_ENTRY link, next; PIRP irp = NULL; if (queue->pendingIrp) { PDRIVER_CANCEL cancelRoutine; irp = queue->pendingIrp; queue->pendingIrp = NULL; cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine == NULL) { irp = NULL; } } OvsReleaseEventQueueLock(); if (irp) { OvsCompleteIrpRequest(queue->pendingIrp, 0, STATUS_SUCCESS); } LIST_FORALL_SAFE(&queue->elemList, link, next) { elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link); OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG); } OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG); } else { OvsReleaseEventQueueLock(); } OVS_LOG_TRACE("Exit: subscribe event with status: %#x.", status); return status; } /* * -------------------------------------------------------------------------- * Cancel wait IRP for event * * Please note, when this routine is called, it is always guaranteed that * IRP is valid. * * Side effects: Pending IRP is completed. * -------------------------------------------------------------------------- */ VOID OvsCancelIrp(PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; POVS_EVENT_QUEUE queue; POVS_OPEN_INSTANCE instance; UNREFERENCED_PARAMETER(deviceObject); IoReleaseCancelSpinLock(irp->CancelIrql); irpSp = IoGetCurrentIrpStackLocation(irp); fileObject = irpSp->FileObject; if (fileObject == NULL) { goto done; } OvsAcquireEventQueueLock(); instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; if (instance == NULL || instance->eventQueue == NULL) { OvsReleaseEventQueueLock(); goto done; } queue = instance->eventQueue; if (queue->pendingIrp == irp) { queue->pendingIrp = NULL; } OvsReleaseEventQueueLock(); done: OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); } /* * -------------------------------------------------------------------------- * Wait for event. * * Results: * STATUS_SUCCESS for valid request * STATUS_DEVICE_BUSY if already in waiting state. * STATUS_INVALID_PARAMETER for invalid request * STATUS_PENDING wait for event * * Side effects: * May return pending to IO manager. * -------------------------------------------------------------------------- */ NTSTATUS OvsWaitEventIoctl(PIRP irp, PFILE_OBJECT fileObject, PVOID inputBuffer, UINT32 inputLength) { NTSTATUS status = STATUS_SUCCESS; POVS_EVENT_POLL poll; POVS_EVENT_QUEUE queue; POVS_OPEN_INSTANCE instance; BOOLEAN cancelled = FALSE; PDRIVER_CANCEL cancelRoutine; OVS_LOG_TRACE("Enter: inputLength: %u", inputLength); if (inputLength < sizeof (OVS_EVENT_POLL)) { OVS_LOG_TRACE("Exit: Invalid input buffer length."); return STATUS_INVALID_PARAMETER; } poll = (POVS_EVENT_POLL)inputBuffer; OvsAcquireEventQueueLock(); instance = OvsGetOpenInstance(fileObject, poll->dpNo); if (instance == NULL) { OVS_LOG_TRACE("Exit: Can not find open instance, dpNo: %d", poll->dpNo); return STATUS_INVALID_PARAMETER; } queue = (POVS_EVENT_QUEUE)instance->eventQueue; if (queue == NULL) { OVS_LOG_TRACE("Exit: Event queue does not exist"); status = STATUS_INVALID_PARAMETER; goto unlock; } if (queue->pendingIrp) { OVS_LOG_TRACE("Exit: Event queue already in pending state"); status = STATUS_DEVICE_BUSY; goto unlock; } IoMarkIrpPending(irp); IoSetCancelRoutine(irp, OvsCancelIrp); if (irp->Cancel) { cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine) { cancelled = TRUE; } } else { queue->pendingIrp = irp; status = STATUS_PENDING; } unlock: OvsReleaseEventQueueLock(); if (cancelled) { OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); OVS_LOG_INFO("Event IRP cancelled: %p", irp); } OVS_LOG_TRACE("Exit: return status: %#x", status); return status; } /* *-------------------------------------------------------------------------- * Poll event queued in the event queue.always synchronous. * * Results: * STATUS_SUCCESS event was dequeued * STATUS_UNSUCCESSFUL the queue is empty. * -------------------------------------------------------------------------- */ NTSTATUS OvsRemoveEventEntry(POVS_OPEN_INSTANCE instance, POVS_EVENT_ENTRY entry) { NTSTATUS status = STATUS_UNSUCCESSFUL; POVS_EVENT_QUEUE queue; POVS_EVENT_QUEUE_ELEM elem; OvsAcquireEventQueueLock(); queue = (POVS_EVENT_QUEUE)instance->eventQueue; if (queue == NULL) { ASSERT(queue); goto remove_event_done; } if (queue->numElems) { elem = (POVS_EVENT_QUEUE_ELEM)RemoveHeadList(&queue->elemList); *entry = elem->event; OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG); queue->numElems--; status = STATUS_SUCCESS; } remove_event_done: OvsReleaseEventQueueLock(); return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Switch.h0000644000000000000000000000013213534540071022064 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.449850978 openvswitch-2.5.9/datapath-windows/ovsext/Switch.h0000644000175000017500000001773713534540071023571 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This file contains the definition of the switch object for the OVS. */ #ifndef __SWITCH_H_ #define __SWITCH_H_ 1 #include "NetProto.h" #include "BufferMgmt.h" #include "TunnelIntf.h" #define OVS_MAX_VPORT_ARRAY_SIZE 1024 #define OVS_MAX_PID_ARRAY_SIZE 1024 #define OVS_VPORT_MASK (OVS_MAX_VPORT_ARRAY_SIZE - 1) #define OVS_PID_MASK (OVS_MAX_PID_ARRAY_SIZE - 1) #define OVS_INTERNAL_VPORT_DEFAULT_INDEX 0 //Tunnel port indicies #define RESERVED_START_INDEX1 1 #define OVS_TUNNEL_INDEX_START RESERVED_START_INDEX1 #define OVS_VXLAN_VPORT_INDEX 2 #define OVS_GRE_VPORT_INDEX 3 #define OVS_TUNNEL_INDEX_END OVS_GRE_VPORT_INDEX #define OVS_MAX_PHYS_ADAPTERS 32 #define OVS_MAX_IP_VPOR 32 #define OVS_HASH_BASIS 0x13578642 typedef struct _OVS_VPORT_ENTRY *POVS_VPORT_ENTRY; typedef struct _OVS_DATAPATH { PLIST_ENTRY flowTable; // Contains OvsFlows. UINT32 nFlows; // Number of entries in flowTable. // List_Links queues[64]; // Hash table of queue IDs. /* Statistics. */ UINT64 hits; // Number of flow table hits. UINT64 misses; // Number of flow table misses. UINT64 lost; // Number of dropped misses. /* Used to protect the flows in the flowtable. */ PNDIS_RW_LOCK_EX lock; } OVS_DATAPATH, *POVS_DATAPATH; /* * OVS_SWITCH_CONTEXT * * The context allocated per switch., For OVS, we only * support one switch which corresponding to one datapath. * Each datapath can have multiple logical bridges configured * which is maintained by vswitchd. */ typedef enum OVS_SWITCH_DATAFLOW_STATE { OvsSwitchPaused, OvsSwitchRunning } OVS_SWITCH_DATAFLOW_STATE, *POVS_SWITCH_DATAFLOW_STATE; typedef enum OVS_SWITCH_CONTROFLOW_STATE { OvsSwitchUnknown, OvsSwitchAttached, OvsSwitchDetached } OVS_SWITCH_CONTROLFLOW_STATE, *POVS_SWITCH_CONTROLFLOW_STATE; // XXX: Take care of alignment and grouping members by cacheline typedef struct _OVS_SWITCH_CONTEXT { /* Coarse and fine-grained switch states. */ OVS_SWITCH_DATAFLOW_STATE dataFlowState; OVS_SWITCH_CONTROLFLOW_STATE controlFlowState; BOOLEAN isActivated; BOOLEAN isActivateFailed; UINT32 dpNo; /* * 'virtualExternalVport' represents default external interface. This is * a virtual interface. The friendly name of such an interface has * been observed to be: "Microsoft Default External Interface". This NIC * has 'NicIndex' == 0. * * The "real" physical external NIC has 'NicIndex' > 0. For each * external interface, virtual or physical, NDIS gives an NIC level * OID callback. Note that, even though there are multile "NICs", * there's only one underlying Hyper-V port. Thus, we get a single * NDIS port-level callback, but multiple NDIS NIC-level callbacks. * * The virtual external NIC can be accessed at 'virtualExternalVport', and * is assigned the name "external.defaultAdapter". The virtual external * NIC is not inserted into the 'portIdHashArray' since the port must not * be exposed to OVS userspace. * * The physical external NICs are assigned names "external.%INDEX%", * where '%INDEX%' represents the 'NicIndex' of the NIC. * * While adding a physical external NIC in OvsInitConfiguredSwitchNics(), * some required properties of the vport are available only at the * NDIS port-level. So, these are copied from 'virtualExternalVport'. * The vport created for the physical external NIC is inserted into the * 'portIdHashArray'. * * When the virtual external NIC is torn down or deleted, the * corresponding physical external ports are also torn down or * deleted. The number of physical external NICs is tracked by * 'numPhysicalNics'. */ NDIS_SWITCH_PORT_ID virtualExternalPortId; NDIS_SWITCH_PORT_ID internalPortId; POVS_VPORT_ENTRY virtualExternalVport; // the virtual adapter vport POVS_VPORT_ENTRY internalVport; /* * 'portIdHashArray' ONLY contains ports that exist on the Hyper-V switch, * namely: VIF (vNIC) ports, external port and Hyper-V internal port. * 'numHvVports' counts the ports in 'portIdHashArray'. If a port got * deleted on the Hyper-V switch, it gets deleted from 'portIdHashArray'. * The port itself will not get deallocated if it has been added from OVS * userspace. 'numHvVports' is decremented when the port is deallocated. * * 'portNoHashArray' ONLY contains ports that are added from OVS userspace, * regardless of whether that port exists on the Hyper-V switch or not. * Tunnel ports and bridge-internal ports are examples of ports that do not * exist on the Hyper-V switch, and 'numNonHvVports' counts such ports in * 'portNoHashArray'. * * 'tunnelVportsArray' contains tunnel ports that are added from OVS * userspace. Currently only VXLAN tunnels are added in this list. * * 'ovsPortNameHashArray' contains the same entries as 'portNoHashArray' but * hashed on a different key. */ PLIST_ENTRY portIdHashArray; // based on Hyper-V portId PLIST_ENTRY portNoHashArray; // based on ovs port number PLIST_ENTRY tunnelVportsArray; // based on ovs dst port number PLIST_ENTRY ovsPortNameHashArray; // based on ovsName PLIST_ENTRY pidHashArray; // based on packet pids NDIS_SPIN_LOCK pidHashLock; // Lock for pidHash table UINT32 numPhysicalNics; // the number of physical // external NICs. UINT32 numHvVports; UINT32 numNonHvVports; /* Lock taken over the switch. This protects the ports on the switch. */ PNDIS_RW_LOCK_EX dispatchLock; /* The flowtable. */ OVS_DATAPATH datapath; /* Handle to the OVSExt filter driver. Same as 'gOvsExtDriverHandle'. */ NDIS_HANDLE NdisFilterHandle; /* Handle and callbacks exposed by the underlying hyper-v switch. */ NDIS_SWITCH_CONTEXT NdisSwitchContext; NDIS_SWITCH_OPTIONAL_HANDLERS NdisSwitchHandlers; volatile LONG pendingInjectedNblCount; volatile LONG pendingOidCount; OVS_NBL_POOL ovsPool; } OVS_SWITCH_CONTEXT, *POVS_SWITCH_CONTEXT; static __inline VOID OvsAcquireDatapathRead(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState, BOOLEAN dispatch) { ASSERT(datapath); NdisAcquireRWLockRead(datapath->lock, lockState, dispatch ? NDIS_RWL_AT_DISPATCH_LEVEL : 0); } static __inline VOID OvsAcquireDatapathWrite(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState, BOOLEAN dispatch) { ASSERT(datapath); NdisAcquireRWLockWrite(datapath->lock, lockState, dispatch ? NDIS_RWL_AT_DISPATCH_LEVEL : 0); } static __inline VOID OvsReleaseDatapath(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState) { ASSERT(datapath); NdisReleaseRWLock(datapath->lock, lockState); } BOOLEAN OvsAcquireSwitchContext(VOID); VOID OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext); #endif /* __SWITCH_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/precomp.h0000644000000000000000000000013213534540071022270 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801424.473851155 openvswitch-2.5.9/datapath-windows/ovsext/precomp.h0000644000175000017500000000171413534540071023761 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "Types.h" #include "..\include\OvsDpInterface.h" #include "Util.h" #include "Netlink/NetlinkError.h" #include "Netlink/Netlink.h" #include "Netlink/NetlinkProto.h" #include "..\include\OvsDpInterfaceExt.h" #include "DpInternal.h" openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Flow.c0000644000000000000000000000013213534540071021525 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.417850743 openvswitch-2.5.9/datapath-windows/ovsext/Flow.c0000644000175000017500000024143013534540071023217 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "NetProto.h" #include "Util.h" #include "Jhash.h" #include "Flow.h" #include "PacketParser.h" #include "Datapath.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_FLOW #include "Debug.h" #pragma warning( push ) #pragma warning( disable:4127 ) extern POVS_SWITCH_CONTEXT gOvsSwitchContext; extern UINT64 ovsTimeIncrementPerTick; static NTSTATUS ReportFlowInfo(OvsFlow *flow, UINT32 getFlags, OvsFlowInfo *info); static NTSTATUS HandleFlowPut(OvsFlowPut *put, OVS_DATAPATH *datapath, struct OvsFlowStats *stats); static NTSTATUS OvsPrepareFlow(OvsFlow **flow, const OvsFlowPut *put, UINT64 hash); static VOID RemoveFlow(OVS_DATAPATH *datapath, OvsFlow **flow); static VOID DeleteAllFlows(OVS_DATAPATH *datapath); static NTSTATUS AddFlow(OVS_DATAPATH *datapath, OvsFlow *flow); static VOID FreeFlow(OvsFlow *flow); static VOID __inline *GetStartAddrNBL(const NET_BUFFER_LIST *_pNB); static NTSTATUS _MapNlToFlowPut(POVS_MESSAGE msgIn, PNL_ATTR keyAttr, PNL_ATTR actionAttr, PNL_ATTR flowAttrClear, OvsFlowPut *mappedFlow); static VOID _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs, PNL_ATTR *tunnelAttrs, OvsFlowKey *destKey); static VOID _MapTunAttrToFlowPut(PNL_ATTR *keyAttrs, PNL_ATTR *tunnelAttrs, OvsFlowKey *destKey); static VOID _MapNlToFlowPutFlags(PGENL_MSG_HDR genlMsgHdr, PNL_ATTR flowAttrClear, OvsFlowPut *mappedFlow); static NTSTATUS _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); static NTSTATUS _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); static NTSTATUS _MapFlowInfoToNl(PNL_BUFFER nlBuf, OvsFlowInfo *flowInfo); static NTSTATUS _MapFlowStatsToNlStats(PNL_BUFFER nlBuf, OvsFlowStats *flowStats); static NTSTATUS _MapFlowActionToNlAction(PNL_BUFFER nlBuf, uint32_t actionsLen, PNL_ATTR actions); static NTSTATUS _MapFlowIpv4KeyToNlKey(PNL_BUFFER nlBuf, IpKey *ipv4FlowPutKey); static NTSTATUS _MapFlowIpv6KeyToNlKey(PNL_BUFFER nlBuf, Ipv6Key *ipv6FlowPutKey, Icmp6Key *ipv6FlowPutIcmpKey); static NTSTATUS _MapFlowArpKeyToNlKey(PNL_BUFFER nlBuf, ArpKey *arpFlowPutKey); static NTSTATUS OvsDoDumpFlows(OvsFlowDumpInput *dumpInput, OvsFlowDumpOutput *dumpOutput, UINT32 *replyLen); #define OVS_FLOW_TABLE_SIZE 2048 #define OVS_FLOW_TABLE_MASK (OVS_FLOW_TABLE_SIZE -1) #define HASH_BUCKET(hash) ((hash) & OVS_FLOW_TABLE_MASK) /* Flow family related netlink policies */ /* For Parsing attributes in FLOW_* commands */ const NL_POLICY nlFlowPolicy[] = { [OVS_FLOW_ATTR_KEY] = {.type = NL_A_NESTED, .optional = FALSE}, [OVS_FLOW_ATTR_MASK] = {.type = NL_A_NESTED, .optional = TRUE}, [OVS_FLOW_ATTR_ACTIONS] = {.type = NL_A_NESTED, .optional = TRUE}, [OVS_FLOW_ATTR_STATS] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_flow_stats), .maxLen = sizeof(struct ovs_flow_stats), .optional = TRUE}, [OVS_FLOW_ATTR_TCP_FLAGS] = {NL_A_U8, .optional = TRUE}, [OVS_FLOW_ATTR_USED] = {NL_A_U64, .optional = TRUE}, [OVS_FLOW_ATTR_PROBE] = {.type = NL_A_FLAG, .optional = TRUE} }; /* For Parsing nested OVS_FLOW_ATTR_KEY attributes. * Some of the attributes like OVS_KEY_ATTR_RECIRC_ID * & OVS_KEY_ATTR_MPLS are not supported yet. */ const NL_POLICY nlFlowKeyPolicy[] = { [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE}, [OVS_KEY_ATTR_PRIORITY] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = TRUE}, [OVS_KEY_ATTR_IN_PORT] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = FALSE}, [OVS_KEY_ATTR_ETHERNET] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_ethernet), .maxLen = sizeof(struct ovs_key_ethernet), .optional = TRUE}, [OVS_KEY_ATTR_VLAN] = {.type = NL_A_UNSPEC, .minLen = 2, .maxLen = 2, .optional = TRUE}, [OVS_KEY_ATTR_ETHERTYPE] = {.type = NL_A_UNSPEC, .minLen = 2, .maxLen = 2, .optional = TRUE}, [OVS_KEY_ATTR_IPV4] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_ipv4), .maxLen = sizeof(struct ovs_key_ipv4), .optional = TRUE}, [OVS_KEY_ATTR_IPV6] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_ipv6), .maxLen = sizeof(struct ovs_key_ipv6), .optional = TRUE}, [OVS_KEY_ATTR_TCP] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_tcp), .maxLen = sizeof(struct ovs_key_tcp), .optional = TRUE}, [OVS_KEY_ATTR_UDP] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_udp), .maxLen = sizeof(struct ovs_key_udp), .optional = TRUE}, [OVS_KEY_ATTR_ICMP] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_icmp), .maxLen = sizeof(struct ovs_key_icmp), .optional = TRUE}, [OVS_KEY_ATTR_ICMPV6] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_icmpv6), .maxLen = sizeof(struct ovs_key_icmpv6), .optional = TRUE}, [OVS_KEY_ATTR_ARP] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_arp), .maxLen = sizeof(struct ovs_key_arp), .optional = TRUE}, [OVS_KEY_ATTR_ND] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_nd), .maxLen = sizeof(struct ovs_key_nd), .optional = TRUE}, [OVS_KEY_ATTR_SKB_MARK] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = TRUE}, [OVS_KEY_ATTR_TUNNEL] = {.type = NL_A_VAR_LEN, .optional = TRUE}, [OVS_KEY_ATTR_SCTP] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_key_sctp), .maxLen = sizeof(struct ovs_key_sctp), .optional = TRUE}, [OVS_KEY_ATTR_TCP_FLAGS] = {.type = NL_A_UNSPEC, .minLen = 2, .maxLen = 2, .optional = TRUE}, [OVS_KEY_ATTR_DP_HASH] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = TRUE}, [OVS_KEY_ATTR_RECIRC_ID] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = TRUE}, [OVS_KEY_ATTR_MPLS] = {.type = NL_A_VAR_LEN, .optional = TRUE} }; const UINT32 nlFlowKeyPolicyLen = ARRAY_SIZE(nlFlowKeyPolicy); /* For Parsing nested OVS_KEY_ATTR_TUNNEL attributes */ const NL_POLICY nlFlowTunnelKeyPolicy[] = { [OVS_TUNNEL_KEY_ATTR_ID] = {.type = NL_A_UNSPEC, .minLen = 8, .maxLen = 8, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = {.type = NL_A_UNSPEC, .minLen = 4, .maxLen = 4, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = {.type = NL_A_UNSPEC, .minLen = 4 , .maxLen = 4, .optional = FALSE}, [OVS_TUNNEL_KEY_ATTR_TOS] = {.type = NL_A_UNSPEC, .minLen = 1, .maxLen = 1, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_TTL] = {.type = NL_A_UNSPEC, .minLen = 1, .maxLen = 1, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = {.type = NL_A_UNSPEC, .minLen = 0, .maxLen = 0, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_CSUM] = {.type = NL_A_UNSPEC, .minLen = 0, .maxLen = 0, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_OAM] = {.type = NL_A_UNSPEC, .minLen = 0, .maxLen = 0, .optional = TRUE}, [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = {.type = NL_A_VAR_LEN, .optional = TRUE} }; /* For Parsing nested OVS_FLOW_ATTR_ACTIONS attributes */ const NL_POLICY nlFlowActionPolicy[] = { [OVS_ACTION_ATTR_OUTPUT] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT32), .maxLen = sizeof(UINT32), .optional = TRUE}, [OVS_ACTION_ATTR_USERSPACE] = {.type = NL_A_VAR_LEN, .optional = TRUE}, [OVS_ACTION_ATTR_PUSH_VLAN] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_action_push_vlan), .maxLen = sizeof(struct ovs_action_push_vlan), .optional = TRUE}, [OVS_ACTION_ATTR_POP_VLAN] = {.type = NL_A_UNSPEC, .optional = TRUE}, [OVS_ACTION_ATTR_PUSH_MPLS] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_action_push_mpls), .maxLen = sizeof(struct ovs_action_push_mpls), .optional = TRUE}, [OVS_ACTION_ATTR_POP_MPLS] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT16), .maxLen = sizeof(UINT16), .optional = TRUE}, [OVS_ACTION_ATTR_RECIRC] = {.type = NL_A_UNSPEC, .minLen = sizeof(UINT32), .maxLen = sizeof(UINT32), .optional = TRUE}, [OVS_ACTION_ATTR_HASH] = {.type = NL_A_UNSPEC, .minLen = sizeof(struct ovs_action_hash), .maxLen = sizeof(struct ovs_action_hash), .optional = TRUE}, [OVS_ACTION_ATTR_SET] = {.type = NL_A_VAR_LEN, .optional = TRUE}, [OVS_ACTION_ATTR_SAMPLE] = {.type = NL_A_VAR_LEN, .optional = TRUE} }; /* *---------------------------------------------------------------------------- * Netlink interface for flow commands. *---------------------------------------------------------------------------- */ /* *---------------------------------------------------------------------------- * OvsFlowNewCmdHandler -- * Handler for OVS_FLOW_CMD_NEW/SET/DEL command. * It also handles FLUSH case (DEL w/o any key in input) *---------------------------------------------------------------------------- */ NTSTATUS OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS rc = STATUS_SUCCESS; BOOLEAN ok; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); POVS_HDR ovsHdr = &(msgIn->ovsHdr); PNL_ATTR flowAttrs[__OVS_FLOW_ATTR_MAX]; UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN; OvsFlowPut mappedFlow; OvsFlowStats stats; struct ovs_flow_stats replyStats; NL_ERROR nlError = NL_ERROR_SUCCESS; NL_BUFFER nlBuf; RtlZeroMemory(&mappedFlow, sizeof(OvsFlowPut)); RtlZeroMemory(&stats, sizeof(stats)); RtlZeroMemory(&replyStats, sizeof(replyStats)); if (!(usrParamsCtx->outputBuffer)) { /* No output buffer */ rc = STATUS_INVALID_BUFFER_SIZE; goto done; } /* Get all the top level Flow attributes */ if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr), nlFlowPolicy, ARRAY_SIZE(nlFlowPolicy), flowAttrs, ARRAY_SIZE(flowAttrs))) != TRUE) { OVS_LOG_ERROR("Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } /* FLOW_DEL command w/o any key input is a flush case. */ if ((genlMsgHdr->cmd == OVS_FLOW_CMD_DEL) && (!(flowAttrs[OVS_FLOW_ATTR_KEY]))) { rc = OvsFlushFlowIoctl(ovsHdr->dp_ifindex); if (rc == STATUS_SUCCESS) { /* XXX: refactor this code. */ /* So far so good. Prepare the reply for userspace */ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); /* Prepare nl Msg headers */ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, genlMsgHdr->cmd, OVS_FLOW_VERSION, ovsHdr->dp_ifindex); if (ok) { *replyLen = msgOut->nlMsg.nlmsgLen; } else { rc = STATUS_INVALID_BUFFER_SIZE; } } goto done; } if (flowAttrs[OVS_FLOW_ATTR_PROBE]) { OVS_LOG_ERROR("Attribute OVS_FLOW_ATTR_PROBE not supported"); goto done; } if ((rc = _MapNlToFlowPut(msgIn, flowAttrs[OVS_FLOW_ATTR_KEY], flowAttrs[OVS_FLOW_ATTR_ACTIONS], flowAttrs[OVS_FLOW_ATTR_CLEAR], &mappedFlow)) != STATUS_SUCCESS) { OVS_LOG_ERROR("Conversion to OvsFlowPut failed"); goto done; } rc = OvsPutFlowIoctl(&mappedFlow, sizeof (struct OvsFlowPut), &stats); if (rc != STATUS_SUCCESS) { OVS_LOG_ERROR("OvsPutFlowIoctl failed."); /* * Report back to the userspace the flow could not be modified, * created or deleted */ nlError = NL_ERROR_NOENT; goto done; } replyStats.n_packets = stats.packetCount; replyStats.n_bytes = stats.byteCount; /* So far so good. Prepare the reply for userspace */ NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); /* Prepare nl Msg headers */ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, genlMsgHdr->cmd, OVS_FLOW_VERSION, ovsHdr->dp_ifindex); if (!ok) { rc = STATUS_INVALID_BUFFER_SIZE; goto done; } else { rc = STATUS_SUCCESS; } /* Append OVS_FLOW_ATTR_KEY attribute. This is need i.e. for flow delete*/ if (!NlMsgPutNested(&nlBuf, OVS_FLOW_ATTR_KEY, NlAttrData(flowAttrs[OVS_FLOW_ATTR_KEY]), NlAttrGetSize(flowAttrs[OVS_FLOW_ATTR_KEY]))) { OVS_LOG_ERROR("Adding OVS_FLOW_ATTR_KEY attribute failed."); rc = STATUS_INVALID_BUFFER_SIZE; goto done; } /* Append OVS_FLOW_ATTR_STATS attribute */ if (!NlMsgPutTailUnspec(&nlBuf, OVS_FLOW_ATTR_STATS, (PCHAR)(&replyStats), sizeof(replyStats))) { OVS_LOG_ERROR("Adding OVS_FLOW_ATTR_STATS attribute failed."); rc = STATUS_INVALID_BUFFER_SIZE; goto done; } msgOut->nlMsg.nlmsgLen = NLMSG_ALIGN(NlBufSize(&nlBuf)); *replyLen = msgOut->nlMsg.nlmsgLen; done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; rc = STATUS_SUCCESS; } return rc; } /* *---------------------------------------------------------------------------- * OvsFlowNlGetCmdHandler -- * Handler for OVS_FLOW_CMD_GET/DUMP commands. *---------------------------------------------------------------------------- */ NTSTATUS OvsFlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS status = STATUS_SUCCESS; if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) { status = _FlowNlGetCmdHandler(usrParamsCtx, replyLen); } else { status = _FlowNlDumpCmdHandler(usrParamsCtx, replyLen); } return status; } /* *---------------------------------------------------------------------------- * _FlowNlGetCmdHandler -- * Handler for OVS_FLOW_CMD_GET command. *---------------------------------------------------------------------------- */ NTSTATUS _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS rc = STATUS_SUCCESS; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); POVS_HDR ovsHdr = &(msgIn->ovsHdr); PNL_MSG_HDR nlMsgOutHdr = NULL; UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN; PNL_ATTR nlAttrs[__OVS_FLOW_ATTR_MAX]; OvsFlowGetInput getInput; OvsFlowGetOutput getOutput; NL_BUFFER nlBuf; PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX]; PNL_ATTR tunnelAttrs[__OVS_TUNNEL_KEY_ATTR_MAX]; NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); RtlZeroMemory(&getInput, sizeof(OvsFlowGetInput)); RtlZeroMemory(&getOutput, sizeof(OvsFlowGetOutput)); UINT32 keyAttrOffset = 0; UINT32 tunnelKeyAttrOffset = 0; BOOLEAN ok; NL_ERROR nlError = NL_ERROR_SUCCESS; if (usrParamsCtx->inputLength > usrParamsCtx->outputLength) { /* Should not be the case. * We'll be copying the flow keys back from * input buffer to output buffer. */ rc = STATUS_INVALID_PARAMETER; OVS_LOG_ERROR("inputLength: %d GREATER THEN outputLength: %d", usrParamsCtx->inputLength, usrParamsCtx->outputLength); goto done; } /* Get all the top level Flow attributes */ if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr), nlFlowPolicy, ARRAY_SIZE(nlFlowPolicy), nlAttrs, ARRAY_SIZE(nlAttrs))) != TRUE) { OVS_LOG_ERROR("Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } keyAttrOffset = (UINT32)((PCHAR) nlAttrs[OVS_FLOW_ATTR_KEY] - (PCHAR)nlMsgHdr); /* Get flow keys attributes */ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, NlAttrLen(nlAttrs[OVS_FLOW_ATTR_KEY]), nlFlowKeyPolicy, ARRAY_SIZE(nlFlowKeyPolicy), keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) { OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) { tunnelKeyAttrOffset = (UINT32)((PCHAR) (keyAttrs[OVS_KEY_ATTR_TUNNEL]) - (PCHAR)nlMsgHdr); /* Get tunnel keys attributes */ if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset, NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]), nlFlowTunnelKeyPolicy, ARRAY_SIZE(nlFlowTunnelKeyPolicy), tunnelAttrs, ARRAY_SIZE(tunnelAttrs))) != TRUE) { OVS_LOG_ERROR("Tunnel key Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } } _MapKeyAttrToFlowPut(keyAttrs, tunnelAttrs, &(getInput.key)); getInput.dpNo = ovsHdr->dp_ifindex; getInput.getFlags = FLOW_GET_STATS | FLOW_GET_ACTIONS; /* 4th argument is a no op. * We are keeping this argument to be compatible * with our dpif-windows based interface. */ rc = OvsGetFlowIoctl(&getInput, &getOutput); if (rc != STATUS_SUCCESS) { OVS_LOG_ERROR("OvsGetFlowIoctl failed."); /* * Report back to the userspace the flow could not be found */ nlError = NL_ERROR_NOENT; goto done; } /* Lets prepare the reply. */ nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, 0, 0)); /* Input already has all the attributes for the flow key. * Lets copy the values back. */ ok = NlMsgPutTail(&nlBuf, (PCHAR)(usrParamsCtx->inputBuffer), usrParamsCtx->inputLength); if (!ok) { OVS_LOG_ERROR("Could not copy the data to the buffer tail"); goto done; } rc = _MapFlowStatsToNlStats(&nlBuf, &((getOutput.info).stats)); if (rc != STATUS_SUCCESS) { OVS_LOG_ERROR("_OvsFlowMapFlowKeyToNlStats failed."); goto done; } rc = _MapFlowActionToNlAction(&nlBuf, ((getOutput.info).actionsLen), getOutput.info.actions); if (rc != STATUS_SUCCESS) { OVS_LOG_ERROR("_MapFlowActionToNlAction failed."); goto done; } NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf)); NlMsgAlignSize(nlMsgOutHdr); *replyLen += NlMsgSize(nlMsgOutHdr); done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; rc = STATUS_SUCCESS; } return rc; } /* *---------------------------------------------------------------------------- * _FlowNlDumpCmdHandler -- * Handler for OVS_FLOW_CMD_DUMP command. *---------------------------------------------------------------------------- */ NTSTATUS _FlowNlDumpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS rc = STATUS_SUCCESS; UINT32 temp = 0; /* To keep compiler happy for calling OvsDoDumpFlows */ NL_ERROR nlError = NL_ERROR_SUCCESS; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE) (usrParamsCtx->ovsInstance); POVS_MESSAGE msgIn = instance->dumpState.ovsMsg; if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) { /* Dump Start */ OvsSetupDumpStart(usrParamsCtx); goto done; } PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); POVS_HDR ovsHdr = &(msgIn->ovsHdr); PNL_MSG_HDR nlMsgOutHdr = NULL; UINT32 hdrOffset = 0; /* Get Next */ OvsFlowDumpOutput dumpOutput; OvsFlowDumpInput dumpInput; NL_BUFFER nlBuf; NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); ASSERT(usrParamsCtx->outputLength); RtlZeroMemory(&dumpInput, sizeof(OvsFlowDumpInput)); RtlZeroMemory(&dumpOutput, sizeof(OvsFlowDumpOutput)); dumpInput.dpNo = ovsHdr->dp_ifindex; dumpInput.getFlags = FLOW_GET_KEY | FLOW_GET_STATS | FLOW_GET_ACTIONS; /* Lets provide as many flows to userspace as possible. */ do { dumpInput.position[0] = instance->dumpState.index[0]; dumpInput.position[1] = instance->dumpState.index[1]; rc = OvsDoDumpFlows(&dumpInput, &dumpOutput, &temp); if (rc != STATUS_SUCCESS) { OVS_LOG_ERROR("OvsDoDumpFlows failed with rc: %d", rc); /* * Report back to the userspace the flows could not be found */ nlError = NL_ERROR_NOENT; break; } /* Done with Dump, send NLMSG_DONE */ if (!(dumpOutput.n)) { BOOLEAN ok; OVS_LOG_INFO("Dump Done"); nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, NlBufSize(&nlBuf), 0)); ok = NlFillNlHdr(&nlBuf, NLMSG_DONE, NLM_F_MULTI, nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid); if (!ok) { rc = STATUS_INVALID_BUFFER_SIZE; OVS_LOG_ERROR("Unable to prepare DUMP_DONE reply."); break; } else { rc = STATUS_SUCCESS; } NlMsgAlignSize(nlMsgOutHdr); *replyLen += NlMsgSize(nlMsgOutHdr); FreeUserDumpState(instance); break; } else { BOOLEAN ok; hdrOffset = NlBufSize(&nlBuf); nlMsgOutHdr = (PNL_MSG_HDR)(NlBufAt(&nlBuf, hdrOffset, 0)); /* Netlink header */ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, NLM_F_MULTI, nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, genlMsgHdr->cmd, genlMsgHdr->version, ovsHdr->dp_ifindex); if (!ok) { /* Reset rc to success so that we can * send already added messages to user space. */ rc = STATUS_SUCCESS; break; } /* Time to add attributes */ rc = _MapFlowInfoToNl(&nlBuf, &(dumpOutput.flow)); if (rc != STATUS_SUCCESS) { /* Adding the attribute failed, we are out of space in the buffer, remove the appended OVS header */ NlMsgSetSize(nlMsgOutHdr, NlMsgSize(nlMsgOutHdr) - sizeof(struct _OVS_MESSAGE)); /* Reset rc to success so that we can * send already added messages to user space. */ rc = STATUS_SUCCESS; break; } NlMsgSetSize(nlMsgOutHdr, NlBufSize(&nlBuf) - hdrOffset); NlMsgAlignSize(nlMsgOutHdr); *replyLen += NlMsgSize(nlMsgOutHdr); instance->dumpState.index[0] = dumpOutput.position[0]; instance->dumpState.index[1] = dumpOutput.position[1]; } } while(TRUE); done: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; rc = STATUS_SUCCESS; } return rc; } /* *---------------------------------------------------------------------------- * _MapFlowInfoToNl -- * Maps OvsFlowInfo to Netlink attributes. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowInfoToNl(PNL_BUFFER nlBuf, OvsFlowInfo *flowInfo) { NTSTATUS rc = STATUS_SUCCESS; rc = MapFlowKeyToNlKey(nlBuf, &(flowInfo->key), OVS_FLOW_ATTR_KEY, OVS_KEY_ATTR_TUNNEL); if (rc != STATUS_SUCCESS) { goto done; } rc = _MapFlowStatsToNlStats(nlBuf, &(flowInfo->stats)); if (rc != STATUS_SUCCESS) { goto done; } rc = _MapFlowActionToNlAction(nlBuf, flowInfo->actionsLen, flowInfo->actions); if (rc != STATUS_SUCCESS) { goto done; } done: return rc; } /* *---------------------------------------------------------------------------- * _MapFlowStatsToNlStats -- * Maps OvsFlowStats to OVS_FLOW_ATTR_STATS attribute. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowStatsToNlStats(PNL_BUFFER nlBuf, OvsFlowStats *flowStats) { NTSTATUS rc = STATUS_SUCCESS; struct ovs_flow_stats replyStats; replyStats.n_packets = flowStats->packetCount; replyStats.n_bytes = flowStats->byteCount; if (!NlMsgPutTailU64(nlBuf, OVS_FLOW_ATTR_USED, flowStats->used)) { rc = STATUS_INVALID_BUFFER_SIZE; goto done; } if (!NlMsgPutTailUnspec(nlBuf, OVS_FLOW_ATTR_STATS, (PCHAR)(&replyStats), sizeof(struct ovs_flow_stats))) { rc = STATUS_INVALID_BUFFER_SIZE; goto done; } if (!NlMsgPutTailU8(nlBuf, OVS_FLOW_ATTR_TCP_FLAGS, flowStats->tcpFlags)) { rc = STATUS_INVALID_BUFFER_SIZE; goto done; } done: return rc; } /* *---------------------------------------------------------------------------- * _MapFlowActionToNlAction -- * Maps flow actions to OVS_FLOW_ATTR_ACTION attribute. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowActionToNlAction(PNL_BUFFER nlBuf, uint32_t actionsLen, PNL_ATTR actions) { NTSTATUS rc = STATUS_SUCCESS; UINT32 offset = 0; offset = NlMsgStartNested(nlBuf, OVS_FLOW_ATTR_ACTIONS); if (!offset) { /* Starting the nested attribute failed. */ rc = STATUS_INVALID_BUFFER_SIZE; goto error_nested_start; } if (!NlBufCopyAtTail(nlBuf, (PCHAR)actions, actionsLen)) { /* Adding a nested attribute failed. */ rc = STATUS_INVALID_BUFFER_SIZE; goto done; } done: NlMsgEndNested(nlBuf, offset); error_nested_start: return rc; } /* *---------------------------------------------------------------------------- * MapFlowKeyToNlKey -- * Maps OvsFlowKey to OVS_FLOW_ATTR_KEY attribute. *---------------------------------------------------------------------------- */ NTSTATUS MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey, UINT16 keyType, UINT16 tunKeyType) { NTSTATUS rc = STATUS_SUCCESS; struct ovs_key_ethernet ethKey; UINT32 offset = 0; offset = NlMsgStartNested(nlBuf, keyType); if (!offset) { /* Starting the nested attribute failed. */ rc = STATUS_UNSUCCESSFUL; goto error_nested_start; } /* Ethernet header */ RtlCopyMemory(&(ethKey.eth_src), flowKey->l2.dlSrc, ETH_ADDR_LEN); RtlCopyMemory(&(ethKey.eth_dst), flowKey->l2.dlDst, ETH_ADDR_LEN); if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ETHERNET, (PCHAR)(ðKey), sizeof(struct ovs_key_ethernet))) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU32(nlBuf, OVS_KEY_ATTR_IN_PORT, flowKey->l2.inPort)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU16(nlBuf, OVS_KEY_ATTR_ETHERTYPE, flowKey->l2.dlType)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (flowKey->l2.vlanTci) { if (!NlMsgPutTailU16(nlBuf, OVS_KEY_ATTR_VLAN, flowKey->l2.vlanTci)) { rc = STATUS_UNSUCCESSFUL; goto done; } } /* ==== L3 + L4 ==== */ switch (ntohs(flowKey->l2.dlType)) { case ETH_TYPE_IPV4: { IpKey *ipv4FlowPutKey = &(flowKey->ipKey); rc = _MapFlowIpv4KeyToNlKey(nlBuf, ipv4FlowPutKey); break; } case ETH_TYPE_IPV6: { Ipv6Key *ipv6FlowPutKey = &(flowKey->ipv6Key); Icmp6Key *icmpv6FlowPutKey = &(flowKey->icmp6Key); rc = _MapFlowIpv6KeyToNlKey(nlBuf, ipv6FlowPutKey, icmpv6FlowPutKey); break; } case ETH_TYPE_ARP: case ETH_TYPE_RARP: { ArpKey *arpFlowPutKey = &(flowKey->arpKey); rc = _MapFlowArpKeyToNlKey(nlBuf, arpFlowPutKey); break; } default: break; } if (rc != STATUS_SUCCESS) { goto done; } if (flowKey->tunKey.dst) { rc = MapFlowTunKeyToNlKey(nlBuf, &(flowKey->tunKey), tunKeyType); if (rc != STATUS_SUCCESS) { goto done; } } done: NlMsgEndNested(nlBuf, offset); error_nested_start: return rc; } /* *---------------------------------------------------------------------------- * MapFlowTunKeyToNlKey -- * Maps OvsIPv4TunnelKey to OVS_TUNNEL_KEY_ATTR_ID attribute. *---------------------------------------------------------------------------- */ NTSTATUS MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf, OvsIPv4TunnelKey *tunKey, UINT16 tunKeyType) { NTSTATUS rc = STATUS_SUCCESS; UINT32 offset = 0; offset = NlMsgStartNested(nlBuf, tunKeyType); if (!offset) { /* Starting the nested attribute failed. */ rc = STATUS_UNSUCCESSFUL; goto error_nested_start; } if (!NlMsgPutTailU64(nlBuf, OVS_TUNNEL_KEY_ATTR_ID, tunKey->tunnelId)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU32(nlBuf, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tunKey->dst)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU32(nlBuf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tunKey->src)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU8(nlBuf, OVS_TUNNEL_KEY_ATTR_TOS, tunKey->tos)) { rc = STATUS_UNSUCCESSFUL; goto done; } if (!NlMsgPutTailU8(nlBuf, OVS_TUNNEL_KEY_ATTR_TTL, tunKey->ttl)) { rc = STATUS_UNSUCCESSFUL; goto done; } done: NlMsgEndNested(nlBuf, offset); error_nested_start: return rc; } /* *---------------------------------------------------------------------------- * _MapFlowTunKeyToNlKey -- * Maps OvsIPv4FlowPutKey to OVS_KEY_ATTR_IPV4 attribute. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowIpv4KeyToNlKey(PNL_BUFFER nlBuf, IpKey *ipv4FlowPutKey) { NTSTATUS rc = STATUS_SUCCESS; struct ovs_key_ipv4 ipv4Key; ipv4Key.ipv4_src = ipv4FlowPutKey->nwSrc; ipv4Key.ipv4_dst = ipv4FlowPutKey->nwDst; ipv4Key.ipv4_proto = ipv4FlowPutKey->nwProto; ipv4Key.ipv4_tos = ipv4FlowPutKey->nwTos; ipv4Key.ipv4_ttl = ipv4FlowPutKey->nwTtl; ipv4Key.ipv4_frag = ipv4FlowPutKey->nwFrag; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_IPV4, (PCHAR)(&ipv4Key), sizeof(struct ovs_key_ipv4))) { rc = STATUS_UNSUCCESSFUL; goto done; } switch (ipv4Key.ipv4_proto) { case IPPROTO_TCP: { struct ovs_key_tcp tcpKey; tcpKey.tcp_src = ipv4FlowPutKey->l4.tpSrc; tcpKey.tcp_dst = ipv4FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_TCP, (PCHAR)(&tcpKey), sizeof(tcpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_UDP: { struct ovs_key_udp udpKey; udpKey.udp_src = ipv4FlowPutKey->l4.tpSrc; udpKey.udp_dst = ipv4FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_UDP, (PCHAR)(&udpKey), sizeof(udpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_SCTP: { struct ovs_key_sctp sctpKey; sctpKey.sctp_src = ipv4FlowPutKey->l4.tpSrc; sctpKey.sctp_dst = ipv4FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_SCTP, (PCHAR)(&sctpKey), sizeof(sctpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_ICMP: { struct ovs_key_icmp icmpKey; /* XXX: revisit to see if htons is needed */ icmpKey.icmp_type = (__u8)(ipv4FlowPutKey->l4.tpSrc); icmpKey.icmp_code = (__u8)(ipv4FlowPutKey->l4.tpDst); if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ICMP, (PCHAR)(&icmpKey), sizeof(icmpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } default: break; } done: return rc; } /* *---------------------------------------------------------------------------- * _MapFlowIpv6KeyToNlKey -- * Maps _MapFlowIpv6KeyToNlKey to OVS_KEY_ATTR_IPV6 attribute. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowIpv6KeyToNlKey(PNL_BUFFER nlBuf, Ipv6Key *ipv6FlowPutKey, Icmp6Key *icmpv6FlowPutKey) { NTSTATUS rc = STATUS_SUCCESS; struct ovs_key_ipv6 ipv6Key; RtlCopyMemory(&(ipv6Key.ipv6_src), &ipv6FlowPutKey->ipv6Src, sizeof ipv6Key.ipv6_src); RtlCopyMemory(&(ipv6Key.ipv6_dst), &ipv6FlowPutKey->ipv6Dst, sizeof ipv6Key.ipv6_dst); ipv6Key.ipv6_label = ipv6FlowPutKey->ipv6Label; ipv6Key.ipv6_proto = ipv6FlowPutKey->nwProto; ipv6Key.ipv6_tclass = ipv6FlowPutKey->nwTos; ipv6Key.ipv6_hlimit = ipv6FlowPutKey->nwTtl; ipv6Key.ipv6_frag = ipv6FlowPutKey->nwFrag; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_IPV6, (PCHAR)(&ipv6Key), sizeof(ipv6Key))) { rc = STATUS_UNSUCCESSFUL; goto done; } switch (ipv6Key.ipv6_proto) { case IPPROTO_TCP: { struct ovs_key_tcp tcpKey; tcpKey.tcp_src = ipv6FlowPutKey->l4.tpSrc; tcpKey.tcp_dst = ipv6FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_TCP, (PCHAR)(&tcpKey), sizeof(tcpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_UDP: { struct ovs_key_udp udpKey; udpKey.udp_src = ipv6FlowPutKey->l4.tpSrc; udpKey.udp_dst = ipv6FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_UDP, (PCHAR)(&udpKey), sizeof(udpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_SCTP: { struct ovs_key_sctp sctpKey; sctpKey.sctp_src = ipv6FlowPutKey->l4.tpSrc; sctpKey.sctp_dst = ipv6FlowPutKey->l4.tpDst; if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_SCTP, (PCHAR)(&sctpKey), sizeof(sctpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } case IPPROTO_ICMPV6: { struct ovs_key_icmpv6 icmpV6Key; struct ovs_key_nd ndKey; /* XXX: revisit to see if htons is needed */ icmpV6Key.icmpv6_type = (__u8)(icmpv6FlowPutKey->l4.tpSrc); icmpV6Key.icmpv6_code = (__u8)(icmpv6FlowPutKey->l4.tpDst); if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ICMPV6, (PCHAR)(&icmpV6Key), sizeof(icmpV6Key))) { rc = STATUS_UNSUCCESSFUL; goto done; } RtlCopyMemory(&(ndKey.nd_target), &icmpv6FlowPutKey->ndTarget, sizeof(icmpv6FlowPutKey->ndTarget)); RtlCopyMemory(&(ndKey.nd_sll), &icmpv6FlowPutKey->arpSha, ETH_ADDR_LEN); RtlCopyMemory(&(ndKey.nd_tll), &icmpv6FlowPutKey->arpTha, ETH_ADDR_LEN); if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ND, (PCHAR)(&ndKey), sizeof(ndKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } break; } default: break; } done: return rc; } /* *---------------------------------------------------------------------------- * _MapFlowArpKeyToNlKey -- * Maps _MapFlowArpKeyToNlKey to OVS_KEY_ATTR_ARP attribute. *---------------------------------------------------------------------------- */ static NTSTATUS _MapFlowArpKeyToNlKey(PNL_BUFFER nlBuf, ArpKey *arpFlowPutKey) { NTSTATUS rc = STATUS_SUCCESS; struct ovs_key_arp arpKey; arpKey.arp_sip = arpFlowPutKey->nwSrc; arpKey.arp_tip = arpFlowPutKey->nwDst; RtlCopyMemory(&(arpKey.arp_sha), arpFlowPutKey->arpSha, ETH_ADDR_LEN); RtlCopyMemory(&(arpKey.arp_tha), arpFlowPutKey->arpTha, ETH_ADDR_LEN); /* * Flow_Extract() stores 'nwProto' in host order for ARP since 'nwProto' is * 1 byte field and the ARP opcode is 2 bytes, and all of the kernel code * understand this while looking at an ARP key. * While we pass up the ARP key to userspace, convert from host order to * network order. Likewise, when processing an ARP key from userspace, * convert from network order to host order. * * It is important to note that the flow table stores the ARP opcode field * in host order. */ arpKey.arp_op = htons(arpFlowPutKey->nwProto); if (!NlMsgPutTailUnspec(nlBuf, OVS_KEY_ATTR_ARP, (PCHAR)(&arpKey), sizeof(arpKey))) { rc = STATUS_UNSUCCESSFUL; goto done; } done: return rc; } /* *---------------------------------------------------------------------------- * _MapNlToFlowPut -- * Maps input netlink message to OvsFlowPut. *---------------------------------------------------------------------------- */ static NTSTATUS _MapNlToFlowPut(POVS_MESSAGE msgIn, PNL_ATTR keyAttr, PNL_ATTR actionAttr, PNL_ATTR flowAttrClear, OvsFlowPut *mappedFlow) { NTSTATUS rc = STATUS_SUCCESS; PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); POVS_HDR ovsHdr = &(msgIn->ovsHdr); UINT32 keyAttrOffset = (UINT32)((PCHAR)keyAttr - (PCHAR)nlMsgHdr); UINT32 tunnelKeyAttrOffset; PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = {NULL}; PNL_ATTR tunnelAttrs[__OVS_TUNNEL_KEY_ATTR_MAX] = {NULL}; /* Get flow keys attributes */ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, NlAttrLen(keyAttr), nlFlowKeyPolicy, ARRAY_SIZE(nlFlowKeyPolicy), keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) { OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) { tunnelKeyAttrOffset = (UINT32)((PCHAR) (keyAttrs[OVS_KEY_ATTR_TUNNEL]) - (PCHAR)nlMsgHdr); /* Get tunnel keys attributes */ if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset, NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]), nlFlowTunnelKeyPolicy, ARRAY_SIZE(nlFlowTunnelKeyPolicy), tunnelAttrs, ARRAY_SIZE(tunnelAttrs))) != TRUE) { OVS_LOG_ERROR("Tunnel key Attr Parsing failed for msg: %p", nlMsgHdr); rc = STATUS_INVALID_PARAMETER; goto done; } } _MapKeyAttrToFlowPut(keyAttrs, tunnelAttrs, &(mappedFlow->key)); /* Map the action */ if (actionAttr) { mappedFlow->actionsLen = NlAttrGetSize(actionAttr); mappedFlow->actions = NlAttrGet(actionAttr); } mappedFlow->dpNo = ovsHdr->dp_ifindex; _MapNlToFlowPutFlags(genlMsgHdr, flowAttrClear, mappedFlow); done: return rc; } /* *---------------------------------------------------------------------------- * _MapNlToFlowPutFlags -- * Maps netlink message to OvsFlowPut->flags. *---------------------------------------------------------------------------- */ static VOID _MapNlToFlowPutFlags(PGENL_MSG_HDR genlMsgHdr, PNL_ATTR flowAttrClear, OvsFlowPut *mappedFlow) { uint32_t flags = 0; switch (genlMsgHdr->cmd) { case OVS_FLOW_CMD_NEW: flags |= OVSWIN_FLOW_PUT_CREATE; break; case OVS_FLOW_CMD_DEL: flags |= OVSWIN_FLOW_PUT_DELETE; break; case OVS_FLOW_CMD_SET: flags |= OVSWIN_FLOW_PUT_MODIFY; break; default: ASSERT(0); } if (flowAttrClear) { flags |= OVSWIN_FLOW_PUT_CLEAR; } mappedFlow->flags = flags; } /* *---------------------------------------------------------------------------- * _MapKeyAttrToFlowPut -- * Converts FLOW_KEY attribute to OvsFlowPut->key. *---------------------------------------------------------------------------- */ static VOID _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs, PNL_ATTR *tunnelAttrs, OvsFlowKey *destKey) { _MapTunAttrToFlowPut(keyAttrs, tunnelAttrs, destKey); /* ===== L2 headers ===== */ destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]); if (keyAttrs[OVS_KEY_ATTR_ETHERNET]) { const struct ovs_key_ethernet *eth_key; eth_key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ETHERNET]); RtlCopyMemory(destKey->l2.dlSrc, eth_key->eth_src, ETH_ADDR_LEN); RtlCopyMemory(destKey->l2.dlDst, eth_key->eth_dst, ETH_ADDR_LEN); } /* TODO: Ideally ETHERTYPE should not be optional. * But during vswitchd bootup we are seeing FLOW_ADD * requests with no ETHERTYPE attributes. * Need to verify this. */ if (keyAttrs[OVS_KEY_ATTR_ETHERTYPE]) { destKey->l2.dlType = (NlAttrGetU16(keyAttrs [OVS_KEY_ATTR_ETHERTYPE])); } if (keyAttrs[OVS_KEY_ATTR_VLAN]) { destKey->l2.vlanTci = NlAttrGetU16(keyAttrs[OVS_KEY_ATTR_VLAN]); } /* ==== L3 + L4. ==== */ destKey->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + OVS_L2_KEY_SIZE - destKey->l2.offset; switch (ntohs(destKey->l2.dlType)) { case ETH_TYPE_IPV4: { if (keyAttrs[OVS_KEY_ATTR_IPV4]) { const struct ovs_key_ipv4 *ipv4Key; ipv4Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_IPV4]); IpKey *ipv4FlowPutKey = &(destKey->ipKey); ipv4FlowPutKey->nwSrc = ipv4Key->ipv4_src; ipv4FlowPutKey->nwDst = ipv4Key->ipv4_dst; ipv4FlowPutKey->nwProto = ipv4Key->ipv4_proto; ipv4FlowPutKey->nwTos = ipv4Key->ipv4_tos; ipv4FlowPutKey->nwTtl = ipv4Key->ipv4_ttl; ipv4FlowPutKey->nwFrag = ipv4Key->ipv4_frag; if (keyAttrs[OVS_KEY_ATTR_TCP]) { const struct ovs_key_tcp *tcpKey; tcpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_TCP]); ipv4FlowPutKey->l4.tpSrc = tcpKey->tcp_src; ipv4FlowPutKey->l4.tpDst = tcpKey->tcp_dst; } if (keyAttrs[OVS_KEY_ATTR_UDP]) { const struct ovs_key_udp *udpKey; udpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_UDP]); ipv4FlowPutKey->l4.tpSrc = udpKey->udp_src; ipv4FlowPutKey->l4.tpDst = udpKey->udp_dst; } if (keyAttrs[OVS_KEY_ATTR_SCTP]) { const struct ovs_key_sctp *sctpKey; sctpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_SCTP]); ipv4FlowPutKey->l4.tpSrc = sctpKey->sctp_src; ipv4FlowPutKey->l4.tpDst = sctpKey->sctp_dst; } destKey->l2.keyLen += OVS_IP_KEY_SIZE; } break; } case ETH_TYPE_IPV6: { if (keyAttrs[OVS_KEY_ATTR_IPV6]) { const struct ovs_key_ipv6 *ipv6Key; ipv6Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_IPV6]); Ipv6Key *ipv6FlowPutKey = &(destKey->ipv6Key); RtlCopyMemory(&ipv6FlowPutKey->ipv6Src, ipv6Key->ipv6_src, sizeof ipv6Key->ipv6_src); RtlCopyMemory(&ipv6FlowPutKey->ipv6Dst, ipv6Key->ipv6_dst, sizeof ipv6Key->ipv6_dst); ipv6FlowPutKey->ipv6Label = ipv6Key->ipv6_label; ipv6FlowPutKey->nwProto = ipv6Key->ipv6_proto; ipv6FlowPutKey->nwTos = ipv6Key->ipv6_tclass; ipv6FlowPutKey->nwTtl = ipv6Key->ipv6_hlimit; ipv6FlowPutKey->nwFrag = ipv6Key->ipv6_frag; if (keyAttrs[OVS_KEY_ATTR_TCP]) { const struct ovs_key_tcp *tcpKey; tcpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_TCP]); ipv6FlowPutKey->l4.tpSrc = tcpKey->tcp_src; ipv6FlowPutKey->l4.tpDst = tcpKey->tcp_dst; } if (keyAttrs[OVS_KEY_ATTR_UDP]) { const struct ovs_key_udp *udpKey; udpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_UDP]); ipv6FlowPutKey->l4.tpSrc = udpKey->udp_src; ipv6FlowPutKey->l4.tpDst = udpKey->udp_dst; } if (keyAttrs[OVS_KEY_ATTR_SCTP]) { const struct ovs_key_sctp *sctpKey; sctpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_SCTP]); ipv6FlowPutKey->l4.tpSrc = sctpKey->sctp_src; ipv6FlowPutKey->l4.tpDst = sctpKey->sctp_dst; } if (keyAttrs[OVS_KEY_ATTR_ICMPV6]) { const struct ovs_key_icmpv6 *icmpv6Key; Icmp6Key *icmp6FlowPutKey= &(destKey->icmp6Key); icmpv6Key = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ICMPV6]); icmp6FlowPutKey->l4.tpSrc = icmpv6Key->icmpv6_type; icmp6FlowPutKey->l4.tpDst = icmpv6Key->icmpv6_code; if (keyAttrs[OVS_KEY_ATTR_ND]) { const struct ovs_key_nd *ndKey; ndKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ND]); RtlCopyMemory(&icmp6FlowPutKey->ndTarget, ndKey->nd_target, sizeof (icmp6FlowPutKey->ndTarget)); RtlCopyMemory(icmp6FlowPutKey->arpSha, ndKey->nd_sll, ETH_ADDR_LEN); RtlCopyMemory(icmp6FlowPutKey->arpTha, ndKey->nd_tll, ETH_ADDR_LEN); } destKey->l2.keyLen += OVS_ICMPV6_KEY_SIZE; } else { destKey->l2.keyLen += OVS_IPV6_KEY_SIZE; } ipv6FlowPutKey->pad = 0; } break; } case ETH_TYPE_ARP: case ETH_TYPE_RARP: { if (keyAttrs[OVS_KEY_ATTR_ARP]) { ArpKey *arpFlowPutKey = &destKey->arpKey; const struct ovs_key_arp *arpKey; arpKey = NlAttrGet(keyAttrs[OVS_KEY_ATTR_ARP]); arpFlowPutKey->nwSrc = arpKey->arp_sip; arpFlowPutKey->nwDst = arpKey->arp_tip; RtlCopyMemory(arpFlowPutKey->arpSha, arpKey->arp_sha, ETH_ADDR_LEN); RtlCopyMemory(arpFlowPutKey->arpTha, arpKey->arp_tha, ETH_ADDR_LEN); /* Kernel datapath assumes 'arpFlowPutKey->nwProto' to be in host * order. */ arpFlowPutKey->nwProto = (UINT8)ntohs((arpKey->arp_op)); arpFlowPutKey->pad[0] = 0; arpFlowPutKey->pad[1] = 0; arpFlowPutKey->pad[2] = 0; destKey->l2.keyLen += OVS_ARP_KEY_SIZE; break; } } } } /* *---------------------------------------------------------------------------- * _MapTunAttrToFlowPut -- * Converts FLOW_TUNNEL_KEY attribute to OvsFlowKey->tunKey. *---------------------------------------------------------------------------- */ static VOID _MapTunAttrToFlowPut(PNL_ATTR *keyAttrs, PNL_ATTR *tunAttrs, OvsFlowKey *destKey) { if (keyAttrs[OVS_KEY_ATTR_TUNNEL]) { if (tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]) { destKey->tunKey.tunnelId = NlAttrGetU64 (tunAttrs[OVS_TUNNEL_KEY_ATTR_ID]); destKey->tunKey.flags |= OVS_TNL_F_KEY; } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_DST]) { destKey->tunKey.dst = NlAttrGetU32 (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_DST]); } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_SRC]) { destKey->tunKey.src = NlAttrGetU32 (tunAttrs[OVS_TUNNEL_KEY_ATTR_IPV4_SRC]); } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT]) { destKey->tunKey.flags |= OVS_TNL_F_DONT_FRAGMENT; } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_CSUM]) { destKey->tunKey.flags |= OVS_TNL_F_CSUM; } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_TOS]) { destKey->tunKey.tos = NlAttrGetU8 (tunAttrs[OVS_TUNNEL_KEY_ATTR_TOS]); } if (tunAttrs[OVS_TUNNEL_KEY_ATTR_TTL]) { destKey->tunKey.ttl = NlAttrGetU8 (tunAttrs[OVS_TUNNEL_KEY_ATTR_TTL]); } destKey->tunKey.pad = 0; destKey->l2.offset = 0; } else { destKey->tunKey.attr[0] = 0; destKey->tunKey.attr[1] = 0; destKey->tunKey.attr[2] = 0; destKey->l2.offset = sizeof destKey->tunKey; } } /* *---------------------------------------------------------------------------- * OvsDeleteFlowTable -- * Results: * NDIS_STATUS_SUCCESS always. *---------------------------------------------------------------------------- */ NDIS_STATUS OvsDeleteFlowTable(OVS_DATAPATH *datapath) { if (datapath == NULL || datapath->flowTable == NULL) { return NDIS_STATUS_SUCCESS; } DeleteAllFlows(datapath); OvsFreeMemoryWithTag(datapath->flowTable, OVS_FLOW_POOL_TAG); datapath->flowTable = NULL; if (datapath->lock == NULL) { return NDIS_STATUS_SUCCESS; } NdisFreeRWLock(datapath->lock); return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsAllocateFlowTable -- * Results: * NDIS_STATUS_SUCCESS on success. * NDIS_STATUS_RESOURCES if memory couldn't be allocated *---------------------------------------------------------------------------- */ NDIS_STATUS OvsAllocateFlowTable(OVS_DATAPATH *datapath, POVS_SWITCH_CONTEXT switchContext) { PLIST_ENTRY bucket; int i; datapath->flowTable = OvsAllocateMemoryWithTag( OVS_FLOW_TABLE_SIZE * sizeof(LIST_ENTRY), OVS_FLOW_POOL_TAG); if (!datapath->flowTable) { return NDIS_STATUS_RESOURCES; } for (i = 0; i < OVS_FLOW_TABLE_SIZE; i++) { bucket = &(datapath->flowTable[i]); InitializeListHead(bucket); } datapath->lock = NdisAllocateRWLock(switchContext->NdisFilterHandle); if (!datapath->lock) { return NDIS_STATUS_RESOURCES; } return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * GetStartAddrNBL -- * Get the virtual address of the frame. * * Results: * Virtual address of the frame. *---------------------------------------------------------------------------- */ static __inline VOID * GetStartAddrNBL(const NET_BUFFER_LIST *_pNB) { PMDL curMdl; PUINT8 curBuffer; PEthHdr curHeader; ASSERT(_pNB); // Ethernet Header is a guaranteed safe access. curMdl = (NET_BUFFER_LIST_FIRST_NB(_pNB))->CurrentMdl; curBuffer = MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!curBuffer) { return NULL; } curHeader = (PEthHdr) (curBuffer + (NET_BUFFER_LIST_FIRST_NB(_pNB))->CurrentMdlOffset); return (VOID *) curHeader; } VOID OvsFlowUsed(OvsFlow *flow, const NET_BUFFER_LIST *packet, const POVS_PACKET_HDR_INFO layers) { LARGE_INTEGER tickCount; KeQueryTickCount(&tickCount); flow->used = tickCount.QuadPart * ovsTimeIncrementPerTick; flow->packetCount++; flow->byteCount += OvsPacketLenNBL(packet); flow->tcpFlags |= OvsGetTcpFlags(packet, &flow->key, layers); } VOID DeleteAllFlows(OVS_DATAPATH *datapath) { INT i; PLIST_ENTRY bucket; for (i = 0; i < OVS_FLOW_TABLE_SIZE; i++) { PLIST_ENTRY next; bucket = &(datapath->flowTable[i]); while (!IsListEmpty(bucket)) { OvsFlow *flow; next = bucket->Flink; flow = CONTAINING_RECORD(next, OvsFlow, ListEntry); RemoveFlow(datapath, &flow); } } } /* *---------------------------------------------------------------------------- * Initializes 'flow' members from 'packet', 'skb_priority', 'tun_id', and * 'ofp_in_port'. * * Initializes 'packet' header pointers as follows: * * - packet->l2 to the start of the Ethernet header. * * - packet->l3 to just past the Ethernet header, or just past the * vlan_header if one is present, to the first byte of the payload of the * Ethernet frame. * * - packet->l4 to just past the IPv4 header, if one is present and has a * correct length, and otherwise NULL. * * - packet->l7 to just past the TCP, UDP, SCTP or ICMP header, if one is * present and has a correct length, and otherwise NULL. * * Returns NDIS_STATUS_SUCCESS normally. Fails only if packet data cannot be accessed * (e.g. if Pkt_CopyBytesOut() returns an error). *---------------------------------------------------------------------------- */ NDIS_STATUS OvsExtractFlow(const NET_BUFFER_LIST *packet, UINT32 inPort, OvsFlowKey *flow, POVS_PACKET_HDR_INFO layers, OvsIPv4TunnelKey *tunKey) { struct Eth_Header *eth; UINT8 offset = 0; PVOID vlanTagValue; layers->value = 0; if (tunKey) { ASSERT(tunKey->dst != 0); RtlMoveMemory(&flow->tunKey, tunKey, sizeof flow->tunKey); flow->l2.offset = 0; } else { flow->tunKey.dst = 0; flow->l2.offset = OVS_WIN_TUNNEL_KEY_SIZE; } flow->l2.inPort = inPort; if ( OvsPacketLenNBL(packet) < ETH_HEADER_LEN_DIX) { flow->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + 8 - flow->l2.offset; return NDIS_STATUS_SUCCESS; } /* Link layer. */ eth = (Eth_Header *)GetStartAddrNBL((NET_BUFFER_LIST *)packet); memcpy(flow->l2.dlSrc, eth->src, ETH_ADDR_LENGTH); memcpy(flow->l2.dlDst, eth->dst, ETH_ADDR_LENGTH); /* * vlan_tci. */ vlanTagValue = NET_BUFFER_LIST_INFO(packet, Ieee8021QNetBufferListInfo); if (vlanTagValue) { PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue; flow->l2.vlanTci = htons(vlanTag->TagHeader.VlanId | OVSWIN_VLAN_CFI | (vlanTag->TagHeader.UserPriority << 13)); } else { if (eth->dix.typeNBO == ETH_TYPE_802_1PQ_NBO) { Eth_802_1pq_Tag *tag= (Eth_802_1pq_Tag *)ð->dix.typeNBO; flow->l2.vlanTci = ((UINT16)tag->priority << 13) | OVSWIN_VLAN_CFI | ((UINT16)tag->vidHi << 8) | tag->vidLo; offset = sizeof (Eth_802_1pq_Tag); } else { flow->l2.vlanTci = 0; } /* * XXX * Please note after this point, src mac and dst mac should * not be accessed through eth */ eth = (Eth_Header *)((UINT8 *)eth + offset); } /* * dl_type. * * XXX assume that at least the first * 12 bytes of received packets are mapped. This code has the stronger * assumption that at least the first 22 bytes of 'packet' is mapped (if my * arithmetic is right). */ if (ETH_TYPENOT8023(eth->dix.typeNBO)) { flow->l2.dlType = eth->dix.typeNBO; layers->l3Offset = ETH_HEADER_LEN_DIX + offset; } else if (OvsPacketLenNBL(packet) >= ETH_HEADER_LEN_802_3 && eth->e802_3.llc.dsap == 0xaa && eth->e802_3.llc.ssap == 0xaa && eth->e802_3.llc.control == ETH_LLC_CONTROL_UFRAME && eth->e802_3.snap.snapOrg[0] == 0x00 && eth->e802_3.snap.snapOrg[1] == 0x00 && eth->e802_3.snap.snapOrg[2] == 0x00) { flow->l2.dlType = eth->e802_3.snap.snapType.typeNBO; layers->l3Offset = ETH_HEADER_LEN_802_3 + offset; } else { flow->l2.dlType = htons(OVSWIN_DL_TYPE_NONE); layers->l3Offset = ETH_HEADER_LEN_DIX + offset; } flow->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + OVS_L2_KEY_SIZE - flow->l2.offset; /* Network layer. */ if (flow->l2.dlType == htons(ETH_TYPE_IPV4)) { struct IPHdr ip_storage; const struct IPHdr *nh; IpKey *ipKey = &flow->ipKey; flow->l2.keyLen += OVS_IP_KEY_SIZE; layers->isIPv4 = 1; nh = OvsGetIp(packet, layers->l3Offset, &ip_storage); if (nh) { layers->l4Offset = layers->l3Offset + nh->ihl * 4; ipKey->nwSrc = nh->saddr; ipKey->nwDst = nh->daddr; ipKey->nwProto = nh->protocol; ipKey->nwTos = nh->tos; if (nh->frag_off & htons(IP_MF | IP_OFFSET)) { ipKey->nwFrag = OVS_FRAG_TYPE_FIRST; if (nh->frag_off & htons(IP_OFFSET)) { ipKey->nwFrag = OVS_FRAG_TYPE_LATER; } } else { ipKey->nwFrag = OVS_FRAG_TYPE_NONE; } ipKey->nwTtl = nh->ttl; ipKey->l4.tpSrc = 0; ipKey->l4.tpDst = 0; if (!(nh->frag_off & htons(IP_OFFSET))) { if (ipKey->nwProto == SOCKET_IPPROTO_TCP) { OvsParseTcp(packet, &ipKey->l4, layers); } else if (ipKey->nwProto == SOCKET_IPPROTO_UDP) { OvsParseUdp(packet, &ipKey->l4, layers); } else if (ipKey->nwProto == SOCKET_IPPROTO_SCTP) { OvsParseSctp(packet, &ipKey->l4, layers); } else if (ipKey->nwProto == SOCKET_IPPROTO_ICMP) { ICMPHdr icmpStorage; const ICMPHdr *icmp; icmp = OvsGetIcmp(packet, layers->l4Offset, &icmpStorage); if (icmp) { ipKey->l4.tpSrc = htons(icmp->type); ipKey->l4.tpDst = htons(icmp->code); layers->l7Offset = layers->l4Offset + sizeof *icmp; } } } } else { ((UINT64 *)ipKey)[0] = 0; ((UINT64 *)ipKey)[1] = 0; } } else if (flow->l2.dlType == htons(ETH_TYPE_IPV6)) { NDIS_STATUS status; flow->l2.keyLen += OVS_IPV6_KEY_SIZE; status = OvsParseIPv6(packet, flow, layers); if (status != NDIS_STATUS_SUCCESS) { memset(&flow->ipv6Key, 0, sizeof (Ipv6Key)); return status; } layers->isIPv6 = 1; flow->ipv6Key.l4.tpSrc = 0; flow->ipv6Key.l4.tpDst = 0; flow->ipv6Key.pad = 0; if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_TCP) { OvsParseTcp(packet, &(flow->ipv6Key.l4), layers); } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_UDP) { OvsParseUdp(packet, &(flow->ipv6Key.l4), layers); } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_SCTP) { OvsParseSctp(packet, &flow->ipv6Key.l4, layers); } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_ICMPV6) { OvsParseIcmpV6(packet, flow, layers); flow->l2.keyLen += (OVS_ICMPV6_KEY_SIZE - OVS_IPV6_KEY_SIZE); } } else if (flow->l2.dlType == htons(ETH_TYPE_ARP)) { EtherArp arpStorage; const EtherArp *arp; ArpKey *arpKey = &flow->arpKey; ((UINT64 *)arpKey)[0] = 0; ((UINT64 *)arpKey)[1] = 0; ((UINT64 *)arpKey)[2] = 0; flow->l2.keyLen += OVS_ARP_KEY_SIZE; arp = OvsGetArp(packet, layers->l3Offset, &arpStorage); if (arp && arp->ea_hdr.ar_hrd == htons(1) && arp->ea_hdr.ar_pro == htons(ETH_TYPE_IPV4) && arp->ea_hdr.ar_hln == ETH_ADDR_LENGTH && arp->ea_hdr.ar_pln == 4) { /* We only match on the lower 8 bits of the opcode. */ if (ntohs(arp->ea_hdr.ar_op) <= 0xff) { arpKey->nwProto = (UINT8)ntohs(arp->ea_hdr.ar_op); } if (arpKey->nwProto == ARPOP_REQUEST || arpKey->nwProto == ARPOP_REPLY) { memcpy(&arpKey->nwSrc, arp->arp_spa, 4); memcpy(&arpKey->nwDst, arp->arp_tpa, 4); memcpy(arpKey->arpSha, arp->arp_sha, ETH_ADDR_LENGTH); memcpy(arpKey->arpTha, arp->arp_tha, ETH_ADDR_LENGTH); } } } return NDIS_STATUS_SUCCESS; } __inline BOOLEAN FlowEqual(UINT64 *src, UINT64 *dst, UINT32 size) { UINT32 i; ASSERT((size & 0x7) == 0); ASSERT(((UINT64)src & 0x7) == 0); ASSERT(((UINT64)dst & 0x7) == 0); for (i = 0; i < (size >> 3); i++) { if (src[i] != dst[i]) { return FALSE; } } return TRUE; } /* * ---------------------------------------------------------------------------- * AddFlow -- * Add a flow to flow table. * * Results: * NDIS_STATUS_SUCCESS if no same flow in the flow table. * ---------------------------------------------------------------------------- */ NTSTATUS AddFlow(OVS_DATAPATH *datapath, OvsFlow *flow) { PLIST_ENTRY head; if (OvsLookupFlow(datapath, &flow->key, &flow->hash, TRUE) != NULL) { return STATUS_INVALID_HANDLE; } head = &(datapath->flowTable[HASH_BUCKET(flow->hash)]); /* * We need fence here to make sure flow's nextPtr is updated before * head->nextPtr is updated. */ KeMemoryBarrier(); //KeAcquireSpinLock(&FilterDeviceExtension->NblQueueLock, &oldIrql); InsertTailList(head, &flow->ListEntry); //KeReleaseSpinLock(&FilterDeviceExtension->NblQueueLock, oldIrql); datapath->nFlows++; return STATUS_SUCCESS; } /* ---------------------------------------------------------------------------- * RemoveFlow -- * Remove a flow from flow table, and added to wait list * ---------------------------------------------------------------------------- */ VOID RemoveFlow(OVS_DATAPATH *datapath, OvsFlow **flow) { OvsFlow *f = *flow; *flow = NULL; ASSERT(datapath->nFlows); datapath->nFlows--; // Remove the flow from queue RemoveEntryList(&f->ListEntry); FreeFlow(f); } /* * ---------------------------------------------------------------------------- * OvsLookupFlow -- * * Find flow from flow table based on flow key. * Caller should either hold portset handle or should * have a flowRef in datapath or Acquired datapath. * * Results: * Flow pointer if lookup successful. * NULL if not exists. * ---------------------------------------------------------------------------- */ OvsFlow * OvsLookupFlow(OVS_DATAPATH *datapath, const OvsFlowKey *key, UINT64 *hash, BOOLEAN hashValid) { PLIST_ENTRY link, head; UINT16 offset = key->l2.offset; UINT16 size = key->l2.keyLen; UINT8 *start; ASSERT(key->tunKey.dst || offset == sizeof (OvsIPv4TunnelKey)); ASSERT(!key->tunKey.dst || offset == 0); start = (UINT8 *)key + offset; if (!hashValid) { *hash = OvsJhashBytes(start, size, 0); } head = &datapath->flowTable[HASH_BUCKET(*hash)]; link = head->Flink; while (link != head) { OvsFlow *flow = CONTAINING_RECORD(link, OvsFlow, ListEntry); if (flow->hash == *hash && flow->key.l2.val == key->l2.val && FlowEqual((UINT64 *)((uint8 *)&flow->key + offset), (UINT64 *)start, size)) { return flow; } link = link->Flink; } return NULL; } /* * ---------------------------------------------------------------------------- * OvsHashFlow -- * Calculate the hash for the given flow key. * ---------------------------------------------------------------------------- */ UINT64 OvsHashFlow(const OvsFlowKey *key) { UINT16 offset = key->l2.offset; UINT16 size = key->l2.keyLen; UINT8 *start; ASSERT(key->tunKey.dst || offset == sizeof (OvsIPv4TunnelKey)); ASSERT(!key->tunKey.dst || offset == 0); start = (UINT8 *)key + offset; return OvsJhashBytes(start, size, 0); } /* * ---------------------------------------------------------------------------- * FreeFlow -- * Free a flow and its actions. * ---------------------------------------------------------------------------- */ VOID FreeFlow(OvsFlow *flow) { ASSERT(flow); OvsFreeMemoryWithTag(flow, OVS_FLOW_POOL_TAG); } NTSTATUS OvsDoDumpFlows(OvsFlowDumpInput *dumpInput, OvsFlowDumpOutput *dumpOutput, UINT32 *replyLen) { UINT32 dpNo; OVS_DATAPATH *datapath = NULL; OvsFlow *flow; PLIST_ENTRY node, head; UINT32 column = 0; UINT32 rowIndex, columnIndex; LOCK_STATE_EX dpLockState; NTSTATUS status = STATUS_SUCCESS; BOOLEAN findNextNonEmpty = FALSE; dpNo = dumpInput->dpNo; if (gOvsSwitchContext->dpNo != dpNo) { status = STATUS_INVALID_PARAMETER; goto exit; } rowIndex = dumpInput->position[0]; if (rowIndex >= OVS_FLOW_TABLE_SIZE) { dumpOutput->n = 0; *replyLen = sizeof(*dumpOutput); goto exit; } columnIndex = dumpInput->position[1]; datapath = &gOvsSwitchContext->datapath; ASSERT(datapath); OvsAcquireDatapathRead(datapath, &dpLockState, FALSE); head = &datapath->flowTable[rowIndex]; node = head->Flink; while (column < columnIndex) { if (node == head) { break; } node = node->Flink; column++; } if (node == head) { findNextNonEmpty = TRUE; columnIndex = 0; } if (findNextNonEmpty) { while (head == node) { if (++rowIndex >= OVS_FLOW_TABLE_SIZE) { dumpOutput->n = 0; goto dp_unlock; } head = &datapath->flowTable[rowIndex]; node = head->Flink; } } ASSERT(node != head); ASSERT(rowIndex < OVS_FLOW_TABLE_SIZE); flow = CONTAINING_RECORD(node, OvsFlow, ListEntry); status = ReportFlowInfo(flow, dumpInput->getFlags, &dumpOutput->flow); if (status == STATUS_BUFFER_TOO_SMALL) { dumpOutput->n = sizeof(OvsFlowDumpOutput) + flow->actionsLen; *replyLen = sizeof(*dumpOutput); } else { dumpOutput->n = 1; //one flow reported. *replyLen = sizeof(*dumpOutput) + dumpOutput->flow.actionsLen; } dumpOutput->position[0] = rowIndex; dumpOutput->position[1] = ++columnIndex; dp_unlock: OvsReleaseDatapath(datapath, &dpLockState); exit: return status; } static NTSTATUS ReportFlowInfo(OvsFlow *flow, UINT32 getFlags, OvsFlowInfo *info) { NTSTATUS status = STATUS_SUCCESS; if (getFlags & FLOW_GET_KEY) { // always copy the tunnel key part RtlCopyMemory(&info->key, &flow->key, flow->key.l2.keyLen + flow->key.l2.offset); } if (getFlags & FLOW_GET_STATS) { OvsFlowStats *stats = &info->stats; stats->packetCount = flow->packetCount; stats->byteCount = flow->byteCount; stats->used = (UINT32)flow->used; stats->tcpFlags = flow->tcpFlags; } if (getFlags & FLOW_GET_ACTIONS) { if (flow->actionsLen == 0) { info->actionsLen = 0; } else { info->actions = flow->actions; info->actionsLen = flow->actionsLen; } } return status; } NTSTATUS OvsPutFlowIoctl(PVOID inputBuffer, UINT32 inputLength, struct OvsFlowStats *stats) { NTSTATUS status = STATUS_SUCCESS; OVS_DATAPATH *datapath = NULL; ULONG actionsLen; OvsFlowPut *put; UINT32 dpNo; LOCK_STATE_EX dpLockState; if ((inputLength < sizeof(OvsFlowPut)) || (inputBuffer == NULL)) { return STATUS_INFO_LENGTH_MISMATCH; } put = (OvsFlowPut *)inputBuffer; if (put->actionsLen > 0) { actionsLen = put->actionsLen; } else { actionsLen = 0; } dpNo = put->dpNo; if (gOvsSwitchContext->dpNo != dpNo) { status = STATUS_INVALID_PARAMETER; goto exit; } datapath = &gOvsSwitchContext->datapath; ASSERT(datapath); OvsAcquireDatapathWrite(datapath, &dpLockState, FALSE); status = HandleFlowPut(put, datapath, stats); OvsReleaseDatapath(datapath, &dpLockState); exit: return status; } /* Handles flow add, modify as well as delete */ static NTSTATUS HandleFlowPut(OvsFlowPut *put, OVS_DATAPATH *datapath, struct OvsFlowStats *stats) { BOOLEAN mayCreate, mayModify, mayDelete; OvsFlow *KernelFlow; UINT64 hash; NTSTATUS status = STATUS_SUCCESS; mayCreate = (put->flags & OVSWIN_FLOW_PUT_CREATE) != 0; mayModify = (put->flags & OVSWIN_FLOW_PUT_MODIFY) != 0; mayDelete = (put->flags & OVSWIN_FLOW_PUT_DELETE) != 0; if ((mayCreate || mayModify) == mayDelete) { return STATUS_INVALID_PARAMETER; } KernelFlow = OvsLookupFlow(datapath, &put->key, &hash, FALSE); if (!KernelFlow) { if (!mayCreate) { return STATUS_INVALID_PARAMETER; } status = OvsPrepareFlow(&KernelFlow, put, hash); if (status != STATUS_SUCCESS) { return STATUS_UNSUCCESSFUL; } status = AddFlow(datapath, KernelFlow); if (status != STATUS_SUCCESS) { FreeFlow(KernelFlow); return STATUS_UNSUCCESSFUL; } /* Validate the flow addition */ { UINT64 newHash; OvsFlow *flow = OvsLookupFlow(datapath, &put->key, &newHash, FALSE); ASSERT(flow); ASSERT(newHash == hash); if (!flow || newHash != hash) { return STATUS_UNSUCCESSFUL; } } } else { stats->packetCount = KernelFlow->packetCount; stats->byteCount = KernelFlow->byteCount; stats->tcpFlags = KernelFlow->tcpFlags; stats->used = (UINT32)KernelFlow->used; if (mayModify) { OvsFlow *newFlow; status = OvsPrepareFlow(&newFlow, put, hash); if (status != STATUS_SUCCESS) { return STATUS_UNSUCCESSFUL; } KernelFlow = OvsLookupFlow(datapath, &put->key, &hash, TRUE); if (KernelFlow) { if ((put->flags & OVSWIN_FLOW_PUT_CLEAR) == 0) { newFlow->packetCount = KernelFlow->packetCount; newFlow->byteCount = KernelFlow->byteCount; newFlow->tcpFlags = KernelFlow->tcpFlags; } RemoveFlow(datapath, &KernelFlow); } else { if ((put->flags & OVSWIN_FLOW_PUT_CLEAR) == 0) { newFlow->packetCount = stats->packetCount; newFlow->byteCount = stats->byteCount; newFlow->tcpFlags = stats->tcpFlags; } } status = AddFlow(datapath, newFlow); ASSERT(status == STATUS_SUCCESS); /* Validate the flow addition */ { UINT64 newHash; OvsFlow *testflow = OvsLookupFlow(datapath, &put->key, &newHash, FALSE); ASSERT(testflow); ASSERT(newHash == hash); if (!testflow || newHash != hash) { FreeFlow(newFlow); return STATUS_UNSUCCESSFUL; } } } else { if (mayDelete) { if (KernelFlow) { RemoveFlow(datapath, &KernelFlow); } } else { /* Return success if an identical flow already exists. */ /* XXX: should we return EEXIST in a netlink error? */ return STATUS_SUCCESS; } } } return STATUS_SUCCESS; } static NTSTATUS OvsPrepareFlow(OvsFlow **flow, const OvsFlowPut *put, UINT64 hash) { OvsFlow *localFlow = *flow; NTSTATUS status = STATUS_SUCCESS; do { *flow = localFlow = OvsAllocateMemoryWithTag(sizeof(OvsFlow) + put->actionsLen, OVS_FLOW_POOL_TAG); if (localFlow == NULL) { status = STATUS_NO_MEMORY; break; } localFlow->key = put->key; localFlow->actionsLen = put->actionsLen; if (put->actionsLen) { NdisMoveMemory((PUCHAR)localFlow->actions, put->actions, put->actionsLen); } localFlow->userActionsLen = 0; // 0 indicate no conversion is made localFlow->used = 0; localFlow->packetCount = 0; localFlow->byteCount = 0; localFlow->tcpFlags = 0; localFlow->hash = hash; } while(FALSE); return status; } NTSTATUS OvsGetFlowIoctl(PVOID inputBuffer, PVOID outputBuffer) { NTSTATUS status = STATUS_SUCCESS; OVS_DATAPATH *datapath = NULL; OvsFlow *flow; UINT32 getFlags, getActionsLen; OvsFlowGetInput *getInput; OvsFlowGetOutput *getOutput; UINT64 hash; UINT32 dpNo; LOCK_STATE_EX dpLockState; getInput = (OvsFlowGetInput *) inputBuffer; getFlags = getInput->getFlags; getActionsLen = getInput->actionsLen; if (outputBuffer == NULL) { return STATUS_INFO_LENGTH_MISMATCH; } dpNo = getInput->dpNo; if (gOvsSwitchContext->dpNo != dpNo) { status = STATUS_INVALID_PARAMETER; goto exit; } datapath = &gOvsSwitchContext->datapath; ASSERT(datapath); OvsAcquireDatapathRead(datapath, &dpLockState, FALSE); flow = OvsLookupFlow(datapath, &getInput->key, &hash, FALSE); if (!flow) { status = STATUS_INVALID_PARAMETER; goto dp_unlock; } getOutput = (OvsFlowGetOutput *)outputBuffer; ReportFlowInfo(flow, getFlags, &getOutput->info); dp_unlock: OvsReleaseDatapath(datapath, &dpLockState); exit: return status; } NTSTATUS OvsFlushFlowIoctl(UINT32 dpNo) { NTSTATUS status = STATUS_SUCCESS; OVS_DATAPATH *datapath = NULL; LOCK_STATE_EX dpLockState; if (gOvsSwitchContext->dpNo != dpNo) { status = STATUS_INVALID_PARAMETER; goto exit; } datapath = &gOvsSwitchContext->datapath; ASSERT(datapath); OvsAcquireDatapathWrite(datapath, &dpLockState, FALSE); DeleteAllFlows(datapath); OvsReleaseDatapath(datapath, &dpLockState); exit: return status; } UINT32 OvsFlowKeyAttrSize(void) { return NlAttrTotalSize(4) /* OVS_KEY_ATTR_PRIORITY */ + NlAttrTotalSize(0) /* OVS_KEY_ATTR_TUNNEL */ + OvsTunKeyAttrSize() + NlAttrTotalSize(4) /* OVS_KEY_ATTR_IN_PORT */ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_SKB_MARK */ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_DP_HASH */ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_RECIRC_ID */ + NlAttrTotalSize(12) /* OVS_KEY_ATTR_ETHERNET */ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ETHERTYPE */ + NlAttrTotalSize(4) /* OVS_KEY_ATTR_VLAN */ + NlAttrTotalSize(0) /* OVS_KEY_ATTR_ENCAP */ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ETHERTYPE */ + NlAttrTotalSize(40) /* OVS_KEY_ATTR_IPV6 */ + NlAttrTotalSize(2) /* OVS_KEY_ATTR_ICMPV6 */ + NlAttrTotalSize(28); /* OVS_KEY_ATTR_ND */ } UINT32 OvsTunKeyAttrSize(void) { /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider * updating this function. */ return NlAttrTotalSize(8) /* OVS_TUNNEL_KEY_ATTR_ID */ + NlAttrTotalSize(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */ + NlAttrTotalSize(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */ + NlAttrTotalSize(1) /* OVS_TUNNEL_KEY_ATTR_TOS */ + NlAttrTotalSize(1) /* OVS_TUNNEL_KEY_ATTR_TTL */ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */ + NlAttrTotalSize(0) /* OVS_TUNNEL_KEY_ATTR_OAM */ + NlAttrTotalSize(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */ + NlAttrTotalSize(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */ + NlAttrTotalSize(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */ } #pragma warning( pop ) openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Flow.h0000644000000000000000000000013213534540071021532 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.417850743 openvswitch-2.5.9/datapath-windows/ovsext/Flow.h0000644000175000017500000000630513534540071023224 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __FLOW_H_ #define __FLOW_H_ 1 #include "precomp.h" #include "Switch.h" #include "User.h" #include "NetProto.h" #include "Datapath.h" typedef struct _OvsFlow { LIST_ENTRY ListEntry; // In Datapath's flowTable. OvsFlowKey key; UINT64 hash; UINT32 actionsLen; UINT8 tcpFlags; UINT64 used; UINT64 packetCount; UINT64 byteCount; UINT32 userActionsLen; // used for flow query UINT32 actionBufferLen; // used for flow reuse NL_ATTR actions[1]; } OvsFlow; typedef struct _OvsLayers { UINT32 l3Ofs; // IPv4, IPv6, ARP, or other L3 header. UINT32 l4Ofs; // TCP, UDP, SCTP, ICMP, ICMPv6, or other L4 header. UINT32 l7Ofs; // L4 protocol's payload. } OvsLayers; extern UINT64 ovsUserTimestampDelta; extern UINT64 ovsTimeIncrementPerTick; NDIS_STATUS OvsDeleteFlowTable(OVS_DATAPATH *datapath); NDIS_STATUS OvsAllocateFlowTable(OVS_DATAPATH *datapath, POVS_SWITCH_CONTEXT switchContext); NDIS_STATUS OvsExtractFlow(const NET_BUFFER_LIST *pkt, UINT32 inPort, OvsFlowKey *flow, POVS_PACKET_HDR_INFO layers, OvsIPv4TunnelKey *tunKey); OvsFlow *OvsLookupFlow(OVS_DATAPATH *datapath, const OvsFlowKey *key, UINT64 *hash, BOOLEAN hashValid); UINT64 OvsHashFlow(const OvsFlowKey *key); VOID OvsFlowUsed(OvsFlow *flow, const NET_BUFFER_LIST *pkt, const POVS_PACKET_HDR_INFO layers); NTSTATUS OvsDumpFlowIoctl(PVOID inputBuffer, UINT32 inputLength, PVOID outputBuffer, UINT32 outputLength, UINT32 *replyLen); NTSTATUS OvsPutFlowIoctl(PVOID inputBuffer, UINT32 inputLength, struct OvsFlowStats *stats); NTSTATUS OvsGetFlowIoctl(PVOID inputBuffer, PVOID outputBuffer); NTSTATUS OvsFlushFlowIoctl(UINT32 dpNo); NTSTATUS OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); NTSTATUS OvsFlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); NTSTATUS MapFlowKeyToNlKey(PNL_BUFFER nlBuf, OvsFlowKey *flowKey, UINT16 keyType, UINT16 tunKeyType); NTSTATUS MapFlowTunKeyToNlKey(PNL_BUFFER nlBuf, OvsIPv4TunnelKey *tunKey, UINT16 tunKeyType); UINT32 OvsFlowKeyAttrSize(void); UINT32 OvsTunKeyAttrSize(void); /* Flags for tunneling */ #define OVS_TNL_F_DONT_FRAGMENT (1 << 0) #define OVS_TNL_F_CSUM (1 << 1) #define OVS_TNL_F_KEY (1 << 2) #endif /* __FLOW_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Vxlan.c0000644000000000000000000000013213534540071021706 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.465851096 openvswitch-2.5.9/datapath-windows/ovsext/Vxlan.c0000644000175000017500000005346213534540071023406 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Atomic.h" #include "NetProto.h" #include "Switch.h" #include "Vport.h" #include "Flow.h" #include "Vxlan.h" #include "IpHelper.h" #include "Checksum.h" #include "User.h" #include "PacketIO.h" #include "Flow.h" #include "PacketParser.h" #pragma warning( push ) #pragma warning( disable:4127 ) #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_VXLAN #include "Debug.h" /* Helper macro to check if a VXLAN ID is valid. */ #define VXLAN_ID_IS_VALID(vxlanID) (0 < (vxlanID) && (vxlanID) <= 0xffffff) #define VXLAN_TUNNELID_TO_VNI(_tID) (UINT32)(((UINT64)(_tID)) >> 40) #define VXLAN_VNI_TO_TUNNELID(_vni) (((UINT64)(_vni)) << 40) #define IP_DF_NBO 0x0040 #define VXLAN_DEFAULT_TTL 64 #define VXLAN_MULTICAST_TTL 64 #define VXLAN_DEFAULT_INSTANCE_ID 1 /* Move to a header file */ extern POVS_SWITCH_CONTEXT gOvsSwitchContext; /* *---------------------------------------------------------------------------- * This function verifies if the VXLAN tunnel already exists, in order to * avoid sending a duplicate request to the WFP base filtering engine. *---------------------------------------------------------------------------- */ static BOOLEAN OvsIsTunnelFilterCreated(POVS_SWITCH_CONTEXT switchContext, UINT16 udpPortDest) { for (UINT hash = 0; hash < OVS_MAX_VPORT_ARRAY_SIZE; hash++) { PLIST_ENTRY head, link, next; head = &(switchContext->portNoHashArray[hash & OVS_VPORT_MASK]); LIST_FORALL_SAFE(head, link, next) { POVS_VPORT_ENTRY vport = NULL; POVS_VXLAN_VPORT vxlanPort = NULL; vport = CONTAINING_RECORD(link, OVS_VPORT_ENTRY, portNoLink); vxlanPort = (POVS_VXLAN_VPORT)vport->priv; if (vxlanPort) { if ((udpPortDest == vxlanPort->dstPort)) { /* The VXLAN tunnel was already created. */ return TRUE; } } } } return FALSE; } /* *---------------------------------------------------------------------------- * This function allocates and initializes the OVS_VXLAN_VPORT. The function * also creates a WFP tunnel filter for the necessary destination port. The * tunnel filter create request is passed to the tunnel filter threads that * will complete the request at a later time when IRQL is lowered to * PASSIVE_LEVEL. * * udpDestPort: the vxlan is set as payload to a udp frame. If the destination * port of an udp frame is udpDestPort, we understand it to be vxlan. *---------------------------------------------------------------------------- */ NTSTATUS OvsInitVxlanTunnel(PIRP irp, POVS_VPORT_ENTRY vport, UINT16 udpDestPort, PFNTunnelVportPendingOp callback, PVOID tunnelContext) { NTSTATUS status = STATUS_SUCCESS; POVS_VXLAN_VPORT vxlanPort = NULL; vxlanPort = OvsAllocateMemoryWithTag(sizeof (*vxlanPort), OVS_VXLAN_POOL_TAG); if (vxlanPort == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(vxlanPort, sizeof(*vxlanPort)); vxlanPort->dstPort = udpDestPort; vport->priv = (PVOID)vxlanPort; if (!OvsIsTunnelFilterCreated(gOvsSwitchContext, udpDestPort)) { status = OvsTunnelFilterCreate(irp, udpDestPort, &vxlanPort->filterID, callback, tunnelContext); } else { status = STATUS_OBJECT_NAME_EXISTS; } return status; } /* *---------------------------------------------------------------------------- * This function releases the OVS_VXLAN_VPORT. The function also deletes the * WFP tunnel filter previously created. The tunnel filter delete request is * passed to the tunnel filter threads that will complete the request at a * later time when IRQL is lowered to PASSIVE_LEVEL. *---------------------------------------------------------------------------- */ NTSTATUS OvsCleanupVxlanTunnel(PIRP irp, POVS_VPORT_ENTRY vport, PFNTunnelVportPendingOp callback, PVOID tunnelContext) { NTSTATUS status = STATUS_SUCCESS; POVS_VXLAN_VPORT vxlanPort = NULL; if (vport->ovsType != OVS_VPORT_TYPE_VXLAN || vport->priv == NULL) { return STATUS_SUCCESS; } vxlanPort = (POVS_VXLAN_VPORT)vport->priv; if (vxlanPort->filterID != 0) { status = OvsTunnelFilterDelete(irp, vxlanPort->filterID, callback, tunnelContext); } else { OvsFreeMemoryWithTag(vport->priv, OVS_VXLAN_POOL_TAG); vport->priv = NULL; } return status; } /* *---------------------------------------------------------------------------- * OvsDoEncapVxlan * Encapsulates the packet. *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsDoEncapVxlan(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status; PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; EthHdr *ethHdr; IPHdr *ipHdr; UDPHdr *udpHdr; VXLANHdr *vxlanHdr; POVS_VXLAN_VPORT vportVxlan; UINT32 headRoom = OvsGetVxlanTunHdrSize(); UINT32 packetLength; ULONG mss = 0; /* * XXX: the assumption currently is that the NBL is owned by OVS, and * headroom has already been allocated as part of allocating the NBL and * MDL. */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); if (layers->isTcp) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; tsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo); switch (tsoInfo.Transmit.Type) { case NDIS_TCP_LARGE_SEND_OFFLOAD_V1_TYPE: mss = tsoInfo.LsoV1Transmit.MSS; break; case NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE: mss = tsoInfo.LsoV2Transmit.MSS; break; default: OVS_LOG_ERROR("Unknown LSO transmit type:%d", tsoInfo.Transmit.Type); } OVS_LOG_TRACE("MSS %u packet len %u", mss, packetLength); if (mss) { OVS_LOG_TRACE("l4Offset %d", layers->l4Offset); *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers, mss, headRoom); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to segment NBL"); return NDIS_STATUS_FAILURE; } /* Clear out LSO flags after this point */ NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0; } } vportVxlan = (POVS_VXLAN_VPORT) GetOvsVportPriv(vport); ASSERT(vportVxlan); /* If we didn't split the packet above, make a copy now */ if (*newNbl == NULL) { *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom, FALSE /*NBL info*/); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to copy NBL"); return NDIS_STATUS_FAILURE; } /* * To this point we do not have VXLAN offloading. * Apply defined checksums */ curNb = NET_BUFFER_LIST_FIRST_NB(*newNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (layers->isIPv4) { IPHdr *ip = (IPHdr *)(bufferStart + layers->l3Offset); if (csumInfo.Transmit.IpHeaderChecksum) { ip->check = 0; ip->check = IPChecksum((UINT8 *)ip, 4 * ip->ihl, 0); } if (layers->isTcp && csumInfo.Transmit.TcpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset); tcp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr, IPPROTO_TCP, csumLength); tcp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } else if (layers->isUdp && csumInfo.Transmit.UdpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip); udp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr, IPPROTO_UDP, csumLength); udp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } } else if (layers->isIPv6) { IPv6Hdr *ip = (IPv6Hdr *)(bufferStart + layers->l3Offset); if (layers->isTcp && csumInfo.Transmit.TcpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset); tcp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr, (UINT32 *) &ip->daddr, IPPROTO_TCP, csumLength); tcp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } else if (layers->isUdp && csumInfo.Transmit.UdpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip); udp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr, (UINT32 *) &ip->daddr, IPPROTO_UDP, csumLength); udp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } } /* Clear out TcpIpChecksumNetBufferListInfo flag */ NET_BUFFER_LIST_INFO(*newNbl, TcpIpChecksumNetBufferListInfo) = 0; } curNbl = *newNbl; for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL; curNb = curNb->Next) { status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { goto ret_error; } curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (NET_BUFFER_NEXT_NB(curNb)) { OVS_LOG_TRACE("nb length %u next %u", NET_BUFFER_DATA_LENGTH(curNb), NET_BUFFER_DATA_LENGTH(curNb->Next)); } /* L2 header */ ethHdr = (EthHdr *)bufferStart; ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) == (PCHAR)&fwdInfo->srcMacAddr); NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr, sizeof ethHdr->Destination + sizeof ethHdr->Source); ethHdr->Type = htons(ETH_TYPE_IPV4); /* IP header */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); ipHdr->ihl = sizeof *ipHdr / 4; ipHdr->version = IPPROTO_IPV4; ipHdr->tos = tunKey->tos; ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr); ipHdr->id = (uint16)atomic_add64(&vportVxlan->ipId, NET_BUFFER_DATA_LENGTH(curNb)); ipHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ? IP_DF_NBO : 0; ipHdr->ttl = tunKey->ttl ? tunKey->ttl : VXLAN_DEFAULT_TTL; ipHdr->protocol = IPPROTO_UDP; ASSERT(tunKey->dst == fwdInfo->dstIpAddr); ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0); ipHdr->saddr = fwdInfo->srcIpAddr; ipHdr->daddr = fwdInfo->dstIpAddr; ipHdr->check = 0; ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0); /* UDP header */ udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr); udpHdr->source = htons(tunKey->flow_hash | MAXINT16); udpHdr->dest = htons(vportVxlan->dstPort); udpHdr->len = htons(NET_BUFFER_DATA_LENGTH(curNb) - headRoom + sizeof *udpHdr + sizeof *vxlanHdr); udpHdr->check = 0; /* VXLAN header */ vxlanHdr = (VXLANHdr *)((PCHAR)udpHdr + sizeof *udpHdr); vxlanHdr->flags1 = 0; vxlanHdr->locallyReplicate = 0; vxlanHdr->flags2 = 0; vxlanHdr->reserved1 = 0; if (tunKey->flags | OVS_TNL_F_KEY) { vxlanHdr->vxlanID = VXLAN_TUNNELID_TO_VNI(tunKey->tunnelId); vxlanHdr->instanceID = 1; } vxlanHdr->reserved2 = 0; } return STATUS_SUCCESS; ret_error: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; } /* *---------------------------------------------------------------------------- * OvsEncapVxlan -- * Encapsulates the packet if L2/L3 for destination resolves. Otherwise, * enqueues a callback that does encapsulatation after resolution. *---------------------------------------------------------------------------- */ NDIS_STATUS OvsEncapVxlan(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl) { NTSTATUS status; OVS_FWD_INFO fwdInfo; status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo); if (status != STATUS_SUCCESS) { OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL); // return NDIS_STATUS_PENDING; /* * XXX: Don't know if the completionList will make any sense when * accessed in the callback. Make sure the caveats are known. * * XXX: This code will work once we are able to grab locks in the * callback. */ return NDIS_STATUS_FAILURE; } return OvsDoEncapVxlan(vport, curNbl, tunKey, &fwdInfo, layers, switchContext, newNbl); } /* *---------------------------------------------------------------------------- * OvsCalculateUDPChecksum * Calculate UDP checksum *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsCalculateUDPChecksum(PNET_BUFFER_LIST curNbl, PNET_BUFFER curNb, IPHdr *ipHdr, UDPHdr *udpHdr, UINT32 packetLength) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; UINT16 checkSum; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); /* Next check if UDP checksum has been calculated. */ if (!csumInfo.Receive.UdpChecksumSucceeded) { UINT32 l4Payload; checkSum = udpHdr->check; l4Payload = packetLength - sizeof(EthHdr) - ipHdr->ihl * 4; udpHdr->check = 0; udpHdr->check = IPPseudoChecksum((UINT32 *)&ipHdr->saddr, (UINT32 *)&ipHdr->daddr, IPPROTO_UDP, (UINT16)l4Payload); udpHdr->check = CalculateChecksumNB(curNb, (UINT16)l4Payload, sizeof(EthHdr) + ipHdr->ihl * 4); if (checkSum != udpHdr->check) { OVS_LOG_TRACE("UDP checksum incorrect."); return NDIS_STATUS_INVALID_PACKET; } } csumInfo.Receive.UdpChecksumSucceeded = 1; NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsDecapVxlan * Decapsulates to tunnel header in 'curNbl' and puts into 'tunKey'. *---------------------------------------------------------------------------- */ NDIS_STATUS OvsDecapVxlan(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl) { PNET_BUFFER curNb; PMDL curMdl; EthHdr *ethHdr; IPHdr *ipHdr; UDPHdr *udpHdr; VXLANHdr *vxlanHdr; UINT32 tunnelSize = 0, packetLength = 0; PUINT8 bufferStart; NDIS_STATUS status; /* Check the length of the UDP payload */ curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); tunnelSize = OvsGetVxlanTunHdrSize(); if (packetLength <= tunnelSize) { return NDIS_STATUS_INVALID_LENGTH; } /* * Create a copy of the NBL so that we have all the headers in one MDL. */ *newNbl = OvsPartialCopyNBL(switchContext, curNbl, tunnelSize + OVS_DEFAULT_COPY_SIZE, 0, TRUE /*copy NBL info */); if (*newNbl == NULL) { return NDIS_STATUS_RESOURCES; } /* XXX: Handle VLAN header. */ curNbl = *newNbl; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority) + NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto dropNbl; } ethHdr = (EthHdr *)bufferStart; /* XXX: Handle IP options. */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); tunKey->src = ipHdr->saddr; tunKey->dst = ipHdr->daddr; tunKey->tos = ipHdr->tos; tunKey->ttl = ipHdr->ttl; tunKey->pad = 0; udpHdr = (UDPHdr *)((PCHAR)ipHdr + sizeof *ipHdr); /* Validate if NIC has indicated checksum failure. */ status = OvsValidateUDPChecksum(curNbl, udpHdr->check == 0); if (status != NDIS_STATUS_SUCCESS) { goto dropNbl; } /* Calculate and verify UDP checksum if NIC didn't do it. */ if (udpHdr->check != 0) { status = OvsCalculateUDPChecksum(curNbl, curNb, ipHdr, udpHdr, packetLength); if (status != NDIS_STATUS_SUCCESS) { goto dropNbl; } } vxlanHdr = (VXLANHdr *)((PCHAR)udpHdr + sizeof *udpHdr); if (vxlanHdr->instanceID) { tunKey->flags = OVS_TNL_F_KEY; tunKey->tunnelId = VXLAN_VNI_TO_TUNNELID(vxlanHdr->vxlanID); } else { tunKey->flags = 0; tunKey->tunnelId = 0; } /* Clear out the receive flag for the inner packet. */ NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0; NdisAdvanceNetBufferDataStart(curNb, tunnelSize, FALSE, NULL); return NDIS_STATUS_SUCCESS; dropNbl: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; } NDIS_STATUS OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet, OvsIPv4TunnelKey *tunnelKey) { NDIS_STATUS status = NDIS_STATUS_FAILURE; UDPHdr udpStorage; const UDPHdr *udp; VXLANHdr *VxlanHeader; VXLANHdr VxlanHeaderBuffer; struct IPHdr ip_storage; const struct IPHdr *nh; OVS_PACKET_HDR_INFO layers; layers.value = 0; do { nh = OvsGetIp(packet, layers.l3Offset, &ip_storage); if (nh) { layers.l4Offset = layers.l3Offset + nh->ihl * 4; } else { break; } /* make sure it's a VXLAN packet */ udp = OvsGetUdp(packet, layers.l4Offset, &udpStorage); if (udp) { layers.l7Offset = layers.l4Offset + sizeof *udp; } else { break; } VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(packet, sizeof(*VxlanHeader), layers.l7Offset, &VxlanHeaderBuffer); if (VxlanHeader) { tunnelKey->src = nh->saddr; tunnelKey->dst = nh->daddr; tunnelKey->ttl = nh->ttl; tunnelKey->tos = nh->tos; if (VxlanHeader->instanceID) { tunnelKey->flags = OVS_TNL_F_KEY; tunnelKey->tunnelId = VXLAN_VNI_TO_TUNNELID(VxlanHeader->vxlanID); } else { tunnelKey->flags = 0; tunnelKey->tunnelId = 0; } } else { break; } status = NDIS_STATUS_SUCCESS; } while(FALSE); return status; } #pragma warning( pop ) openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/User.h0000644000000000000000000000013213534540071021541 xustar0030 mtime=1567801401.229679907 30 atime=1567801402.049685929 30 ctime=1567801424.461851067 openvswitch-2.5.9/datapath-windows/ovsext/User.h0000644000175000017500000001051313534540071023227 0ustar00jpettitjpettit00000000000000 /* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This file contains structures and function definitions necessary for * forwarding packet to user space. */ #ifndef __USER_H_ #define __USER_H_ 1 #include "Datapath.h" /* * Even we have more cores, I don't think we need * more than 32 queues for processing packets to * userspace */ #define OVS_DEFAULT_PACKET_QUEUE 1 #define OVS_MAX_PACKET_QUEUE_LEN 4096 /* * Only when OVS_PER_VPORT_QUEUE_CTRL is defined * we will apply this constraint */ #define OVS_MAX_PACKETS_PER_VPORT 128 #define OVS_MAX_PACKETS_PER_TUNNEL 1024 typedef struct _OVS_USER_PACKET_QUEUE { UINT32 pid; UINT32 numPackets; LIST_ENTRY packetList; PVOID instance; PIRP pendingIrp; NDIS_SPIN_LOCK queueLock; } OVS_USER_PACKET_QUEUE, *POVS_USER_PACKET_QUEUE; typedef struct _OVS_PACKET_QUEUE_ELEM { UINT32 upcallPid; LIST_ENTRY link; OVS_PACKET_HDR_INFO hdrInfo; OVS_PACKET_INFO packet; } OVS_PACKET_QUEUE_ELEM, *POVS_PACKET_QUEUE_ELEM; struct _OVS_OPEN_INSTANCE; typedef struct _OVS_USER_STATS { UINT64 miss; UINT64 action; UINT32 dropDuetoResource; UINT32 dropDuetoChecksum; UINT32 ipCsum; UINT32 recalTcpCsum; UINT32 vlanInsert; UINT32 l4Csum; } OVS_USER_STATS, *POVS_USER_STATS; VOID OvsCleanupPacketQueue(struct _OVS_OPEN_INSTANCE *instance); POVS_PACKET_QUEUE_ELEM OvsCreateQueueNlPacket(PVOID userData, UINT32 userDataLen, UINT32 cmd, POVS_VPORT_ENTRY vport, OvsFlowKey *key, PNET_BUFFER_LIST nbl, PNET_BUFFER nb, BOOLEAN isRecv, POVS_PACKET_HDR_INFO hdrInfo); VOID OvsQueuePackets(PLIST_ENTRY packetList, UINT32 numElems); NTSTATUS OvsCreateAndAddPackets(PVOID userData, UINT32 userDataLen, UINT32 cmd, POVS_VPORT_ENTRY vport, OvsFlowKey *key, PNET_BUFFER_LIST nbl, BOOLEAN isRecv, POVS_PACKET_HDR_INFO hdrInfo, POVS_SWITCH_CONTEXT switchContext, LIST_ENTRY *list, UINT32 *num); NTSTATUS OvsSubscribeDpIoctl(PVOID instanceP, UINT32 pid, UINT8 join); NTSTATUS OvsReadDpIoctl(PFILE_OBJECT fileObject, PVOID outputBuffer, UINT32 outputLength, UINT32 *replyLen); NTSTATUS OvsExecuteDpIoctl(OvsPacketExecute *execute); NTSTATUS OvsPurgeDpIoctl(PFILE_OBJECT fileObject); NTSTATUS OvsWaitDpIoctl(PIRP irp, PFILE_OBJECT fileObject); NTSTATUS OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); POVS_OPEN_INSTANCE OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid); VOID OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid, POVS_OPEN_INSTANCE instance); VOID OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid); NTSTATUS OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); NTSTATUS OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); NTSTATUS OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); #endif /* __USER_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/TunnelFilter.c0000644000000000000000000000013213534540071023231 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.453851008 openvswitch-2.5.9/datapath-windows/ovsext/TunnelFilter.c0000644000175000017500000015270213534540071024726 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #pragma warning(push) #pragma warning(disable:4201) // unnamed struct/union #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_TUNFLT #include "Debug.h" #include #pragma warning(pop) #include #include #include #include #include "Tunnel.h" #include "Switch.h" #include "Vport.h" #include "Event.h" #include "User.h" #include "Vxlan.h" #define INITGUID #include /* Infinite timeout */ #define INFINITE 0xFFFFFFFF /* The provider name should always match the provider string from the install * file. */ #define OVS_TUNNEL_PROVIDER_NAME L"Open vSwitch" /* The provider description should always contain the OVS service description * string from the install file. */ #define OVS_TUNNEL_PROVIDER_DESC L"Open vSwitch Extension tunnel provider" /* The session name isn't required but it's useful for diagnostics. */ #define OVS_TUNNEL_SESSION_NAME L"OVS tunnel session" /* Maximum number of tunnel threads to be created. */ #define OVS_TUNFLT_MAX_THREADS 8 /* * Callout and sublayer GUIDs */ /* b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8 */ DEFINE_GUID( OVS_TUNNEL_CALLOUT_V4, 0xb16b0a6e, 0x2b2a, 0x41a3, 0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8 ); /* 0104fd7e-c825-414e-94c9-f0d525bbc169 */ DEFINE_GUID( OVS_TUNNEL_SUBLAYER, 0x0104fd7e, 0xc825, 0x414e, 0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69 ); /* 6fc957d7-14e7-47c7-812b-4668be994ba1 */ DEFINE_GUID( OVS_TUNNEL_PROVIDER_KEY, 0x6fc957d7, 0x14e7, 0x47c7, 0x81, 0x2b, 0x46, 0x68, 0xbe, 0x99, 0x4b, 0xa1 ); /* bfd4814c-9650-4de3-a536-1eedb9e9ba6a */ DEFINE_GUID( OVS_TUNNEL_FILTER_KEY, 0xbfd4814c, 0x9650, 0x4de3, 0xa5, 0x36, 0x1e, 0xed, 0xb9, 0xe9, 0xba, 0x6a ); /* * Callout driver type definitions */ typedef enum _OVS_TUNFLT_OPERATION { OVS_TUN_FILTER_CREATE = 0, OVS_TUN_FILTER_DELETE } OVS_TUNFLT_OPERATION; typedef struct _OVS_TUNFLT_REQUEST { LIST_ENTRY entry; /* Tunnel filter destination port. */ UINT16 port; /* XXX: We also need to specify the tunnel L4 protocol, because there are * different protocols that can use the same destination port.*/ union { /* Tunnel filter identification used for filter deletion. */ UINT64 delID; /* Pointer used to return filter ID to the caller on filter creation. */ PUINT64 addID; } filterID; /* Requested operation to be performed. */ OVS_TUNFLT_OPERATION operation; /* Current I/O request to be completed when requested * operation is finished. */ PIRP irp; /* Callback function called before completing the IRP. */ PFNTunnelVportPendingOp callback; /* Context passed to the callback function. */ PVOID context; } OVS_TUNFLT_REQUEST, *POVS_TUNFLT_REQUEST; typedef struct _OVS_TUNFLT_REQUEST_LIST { /* SpinLock for syncronizing access to the requests list. */ NDIS_SPIN_LOCK spinlock; /* Head of the requests list. */ LIST_ENTRY head; /* Number of requests in the list. This variable is used by * InterlockedCompareExchange function and needs to be aligned * at 32-bit boundaries. */ UINT32 numEntries; } OVS_TUNFLT_REQUEST_LIST, *POVS_TUNFLT_REQUEST_LIST; typedef struct _OVS_TUNFLT_THREAD_CONTEXT { /* Thread identification. */ UINT threadID; /* Thread initialization flag. */ UINT32 isInitialized; /* Thread's engine session handle. */ HANDLE engineSession; /* Reference of the thread object. */ PVOID threadObject; /* Requests queue list. */ OVS_TUNFLT_REQUEST_LIST listRequests; /* Event signaling that there are requests to process. */ KEVENT requestEvent; /* Event for stopping thread execution. */ KEVENT stopEvent; } OVS_TUNFLT_THREAD_CONTEXT, *POVS_TUNFLT_THREAD_CONTEXT; KSTART_ROUTINE OvsTunnelFilterThreadProc; static NTSTATUS OvsTunnelFilterStartThreads(); static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx); static VOID OvsTunnelFilterStopThreads(); static VOID OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx, BOOLEAN signalEvent); static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx); static VOID OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx); static VOID OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests, POVS_TUNFLT_REQUEST request); static VOID OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp); /* * Callout driver global variables */ /* Pointer to the device object that must be create before we can register our * callout to the base filtering engine. */ static PDEVICE_OBJECT gDeviceObject = NULL; /* Handle to an open session to the filter engine that is used for adding * tunnel's callout. */ static HANDLE gEngineHandle = NULL; /* A pointer to the received handle that is associated with the registration of * the OvsTunnelProviderBfeCallback callback. */ static HANDLE gTunnelProviderBfeHandle = NULL; /* A pointer to the received handle that is associated with the registration of * the OvsTunnelInitBfeCallback callback. */ static HANDLE gTunnelInitBfeHandle = NULL; /* Runtime identifier for tunnel's callout which is retrieved at tunnel * initialization phase when the callout is registered. This ID is then used * for removing the callout object from the system at tunnel * uninitialization phase. */ static UINT32 gCalloutIdV4 = 0; /* Array used for storing tunnel thread's private data. */ static OVS_TUNFLT_THREAD_CONTEXT gTunnelThreadCtx[OVS_TUNFLT_MAX_THREADS] = { 0 }; /* * Callout driver implementation. */ NTSTATUS OvsTunnelEngineOpen(HANDLE *engineSession) { NTSTATUS status = STATUS_SUCCESS; FWPM_SESSION session = { 0 }; /* * Set an infinite wait timeout, so we don't have to handle FWP_E_TIMEOUT * errors while waiting to acquire the transaction lock. */ session.txnWaitTimeoutInMSec = INFINITE; /* The authentication service should always be RPC_C_AUTHN_DEFAULT. */ status = FwpmEngineOpen(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session, engineSession); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to open filtering engine session, status: %x.", status); } return status; } VOID OvsTunnelEngineClose(HANDLE *engineSession) { if (*engineSession) { FwpmEngineClose(*engineSession); *engineSession = NULL; } } VOID OvsTunnelAddSystemProvider(HANDLE engineSession) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN inTransaction = FALSE; FWPM_PROVIDER provider = { 0 }; do { status = FwpmTransactionBegin(engineSession, 0); if (!NT_SUCCESS(status)) { break; } inTransaction = TRUE; memset(&provider, 0, sizeof(provider)); provider.providerKey = OVS_TUNNEL_PROVIDER_KEY; provider.displayData.name = OVS_TUNNEL_PROVIDER_NAME; provider.displayData.description = OVS_TUNNEL_PROVIDER_DESC; /* * Since we always want the provider to be present, it's easiest to add * it as persistent object during driver load. */ provider.flags = FWPM_PROVIDER_FLAG_PERSISTENT; status = FwpmProviderAdd(engineSession, &provider, NULL); if (!NT_SUCCESS(status)) { if (STATUS_FWP_ALREADY_EXISTS != status) { OVS_LOG_ERROR("Failed to add WFP provider, status: %x.", status); break; } } status = FwpmTransactionCommit(engineSession); if (!NT_SUCCESS(status)) { break; } inTransaction = FALSE; } while (inTransaction); if (inTransaction){ FwpmTransactionAbort(engineSession); } } VOID OvsTunnelRemoveSystemProvider(HANDLE engineSession) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN inTransaction = FALSE; do { status = FwpmTransactionBegin(engineSession, 0); if (!NT_SUCCESS(status)) { break; } inTransaction = TRUE; status = FwpmProviderDeleteByKey(engineSession, &OVS_TUNNEL_PROVIDER_KEY); if (!NT_SUCCESS(status)) { break; } status = FwpmTransactionCommit(engineSession); if (!NT_SUCCESS(status)) { break; } inTransaction = FALSE; } while (inTransaction); if (inTransaction){ FwpmTransactionAbort(engineSession); } } NTSTATUS OvsTunnelAddFilter(HANDLE engineSession, PWSTR filterName, const PWSTR filterDesc, USHORT remotePort, FWP_DIRECTION direction, UINT64 context, const GUID *filterKey, const GUID *layerKey, const GUID *calloutKey, UINT64 *filterID) { NTSTATUS status = STATUS_SUCCESS; FWPM_FILTER filter = {0}; FWPM_FILTER_CONDITION filterConditions[3] = {0}; UINT conditionIndex; if (filterKey) { filter.filterKey = *filterKey; } filter.layerKey = *layerKey; filter.displayData.name = (wchar_t*)filterName; filter.displayData.description = (wchar_t*)filterDesc; filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; filter.action.calloutKey = *calloutKey; filter.filterCondition = filterConditions; filter.subLayerKey = OVS_TUNNEL_SUBLAYER; filter.weight.type = FWP_EMPTY; // auto-weight. filter.rawContext = context; conditionIndex = 0; filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION; filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; filterConditions[conditionIndex].conditionValue.type = FWP_UINT32; filterConditions[conditionIndex].conditionValue.uint32 = direction; conditionIndex++; filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL; filterConditions[conditionIndex].conditionValue.type = FWP_UINT16; filterConditions[conditionIndex].conditionValue.uint16 = remotePort; conditionIndex++; filter.numFilterConditions = conditionIndex; status = FwpmFilterAdd(engineSession, &filter, NULL, filterID); return status; } /* * -------------------------------------------------------------------------- * This function registers callouts for intercepting UDP traffic at WFP * FWPM_LAYER_DATAGRAM_DATA_V4 layer. * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelRegisterDatagramDataCallouts(const GUID *layerKey, const GUID *calloutKey, VOID *deviceObject, UINT32 *calloutId) { NTSTATUS status = STATUS_SUCCESS; FWPS_CALLOUT sCallout = {0}; FWPM_CALLOUT mCallout = {0}; FWPM_DISPLAY_DATA displayData = {0}; BOOLEAN calloutRegistered = FALSE; sCallout.calloutKey = *calloutKey; sCallout.classifyFn = OvsTunnelClassify; sCallout.notifyFn = OvsTunnelNotify; #if FLOW_CONTEXT /* Currently we don't associate a context with the flow */ sCallout.flowDeleteFn = OvsTunnelFlowDelete; sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW; #endif status = FwpsCalloutRegister(deviceObject, &sCallout, calloutId); if (!NT_SUCCESS(status)) { goto Exit; } calloutRegistered = TRUE; displayData.name = L"Datagram-Data OVS Callout"; displayData.description = L"Proxies destination address/port for UDP"; mCallout.calloutKey = *calloutKey; mCallout.displayData = displayData; mCallout.applicableLayer = *layerKey; status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL); if (!NT_SUCCESS(status)) { if (STATUS_FWP_ALREADY_EXISTS != status) { OVS_LOG_ERROR("Failed to add WFP callout, status: %x.", status); goto Exit; } status = STATUS_SUCCESS; } Exit: if (!NT_SUCCESS(status)){ if (calloutRegistered) { FwpsCalloutUnregisterById(*calloutId); *calloutId = 0; } } return status; } /* * -------------------------------------------------------------------------- * This function registers non-dynamic callouts for intercepting UDP traffic. * Callouts will be removed during un-initializing phase. * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelRegisterCallouts(VOID *deviceObject) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN inTransaction = FALSE; FWPM_SUBLAYER OvsTunnelSubLayer; status = FwpmTransactionBegin(gEngineHandle, 0); if (!NT_SUCCESS(status)) { goto Exit; } inTransaction = TRUE; RtlZeroMemory(&OvsTunnelSubLayer, sizeof(FWPM_SUBLAYER)); OvsTunnelSubLayer.subLayerKey = OVS_TUNNEL_SUBLAYER; OvsTunnelSubLayer.displayData.name = L"Datagram-Data OVS Sub-Layer"; OvsTunnelSubLayer.displayData.description = L"Sub-Layer for use by Datagram-Data OVS callouts"; OvsTunnelSubLayer.flags = 0; OvsTunnelSubLayer.weight = FWP_EMPTY; /* auto-weight */ status = FwpmSubLayerAdd(gEngineHandle, &OvsTunnelSubLayer, NULL); if (!NT_SUCCESS(status)) { if (STATUS_FWP_ALREADY_EXISTS != status) { OVS_LOG_ERROR("Failed to add WFP sublayer, status: %x.", status); goto Exit; } } /* In order to use this callout a socket must be opened. */ status = OvsTunnelRegisterDatagramDataCallouts(&FWPM_LAYER_DATAGRAM_DATA_V4, &OVS_TUNNEL_CALLOUT_V4, deviceObject, &gCalloutIdV4); if (!NT_SUCCESS(status)) { goto Exit; } status = FwpmTransactionCommit(gEngineHandle); if (!NT_SUCCESS(status)){ goto Exit; } inTransaction = FALSE; Exit: if (!NT_SUCCESS(status)) { if (inTransaction) { FwpmTransactionAbort(gEngineHandle); } } return status; } VOID OvsTunnelUnregisterCallouts() { FwpsCalloutUnregisterById(gCalloutIdV4); FwpmSubLayerDeleteByKey(gEngineHandle, &OVS_TUNNEL_SUBLAYER); FwpmCalloutDeleteById(gEngineHandle, gCalloutIdV4); } VOID OvsTunnelFilterUninitialize(PDRIVER_OBJECT driverObject) { UNREFERENCED_PARAMETER(driverObject); OvsTunnelFilterStopThreads(); OvsTunnelUnregisterCallouts(); OvsTunnelEngineClose(&gEngineHandle); if (gDeviceObject) { IoDeleteDevice(gDeviceObject); } } NTSTATUS OvsTunnelFilterInitialize(PDRIVER_OBJECT driverObject) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING deviceName; RtlInitUnicodeString(&deviceName, L"\\Device\\OvsTunnelFilter"); status = IoCreateDevice(driverObject, 0, &deviceName, FILE_DEVICE_NETWORK, 0, FALSE, &gDeviceObject); if (!NT_SUCCESS(status)){ OVS_LOG_ERROR("Failed to create tunnel filter device, status: %x.", status); goto Exit; } status = OvsTunnelFilterStartThreads(); if (!NT_SUCCESS(status)){ goto Exit; } status = OvsTunnelEngineOpen(&gEngineHandle); if (!NT_SUCCESS(status)){ goto Exit; } status = OvsTunnelRegisterCallouts(gDeviceObject); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to register callout, status: %x.", status); } Exit: if (!NT_SUCCESS(status)){ OvsTunnelFilterUninitialize(driverObject); } return status; } /* * -------------------------------------------------------------------------- * This function adds OVS system provider to the system if the BFE (Base * Filtering Engine) is running. * -------------------------------------------------------------------------- */ VOID NTAPI OvsTunnelProviderBfeCallback(PVOID context, FWPM_SERVICE_STATE bfeState) { HANDLE engineSession = NULL; DBG_UNREFERENCED_PARAMETER(context); if (FWPM_SERVICE_RUNNING == bfeState) { OvsTunnelEngineOpen(&engineSession); if (engineSession) { OvsTunnelAddSystemProvider(engineSession); } OvsTunnelEngineClose(&engineSession); } } /* * -------------------------------------------------------------------------- * This function registers the OvsTunnelProviderBfeCallback callback that is * called whenever there is a change to the state of base filtering engine. * -------------------------------------------------------------------------- */ NTSTATUS OvsSubscribeTunnelProviderBfeStateChanges(PVOID deviceObject) { NTSTATUS status = STATUS_SUCCESS; if (!gTunnelProviderBfeHandle) { status = FwpmBfeStateSubscribeChanges(deviceObject, OvsTunnelProviderBfeCallback, NULL, &gTunnelProviderBfeHandle); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR( "Failed to subscribe BFE tunnel provider callback, status: %x.", status); } } return status; } /* * -------------------------------------------------------------------------- * This function unregisters the OvsTunnelProviderBfeCallback callback that * was previously registered by OvsSubscribeTunnelProviderBfeStateChanges * function. * -------------------------------------------------------------------------- */ VOID OvsUnsubscribeTunnelProviderBfeStateChanges() { NTSTATUS status = STATUS_SUCCESS; if (gTunnelProviderBfeHandle) { status = FwpmBfeStateUnsubscribeChanges(gTunnelProviderBfeHandle); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR( "Failed to unsubscribe BFE tunnel provider callback, status: %x.", status); } gTunnelProviderBfeHandle = NULL; } } /* * -------------------------------------------------------------------------- * This function registers the OVS system provider if the BFE (Base Filtering * Engine) is running. * Otherwise, it will register the OvsTunnelProviderBfeCallback callback. * Note: Before calling FwpmBfeStateGet, the callout driver must call the * FwpmBfeStateSubscribeChanges function to register the callback function * to be called whenever the state of the filter engine changes. * * Register WFP system provider call hierarchy: * * * * * --> registers OvsTunnelProviderBfeCallback callback * * --> if BFE is running: * * --> if BFE is running: * * * --> unregisters OvsTunnelProviderBfeCallback callback * * -------------------------------------------------------------------------- */ VOID OvsRegisterSystemProvider(PVOID deviceObject) { NTSTATUS status = STATUS_SUCCESS; HANDLE engineSession = NULL; status = OvsSubscribeTunnelProviderBfeStateChanges(deviceObject); if (NT_SUCCESS(status)) { if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) { OvsTunnelEngineOpen(&engineSession); if (engineSession) { OvsTunnelAddSystemProvider(engineSession); } OvsTunnelEngineClose(&engineSession); OvsUnsubscribeTunnelProviderBfeStateChanges(); } } } /* * -------------------------------------------------------------------------- * This function removes the OVS system provider and unregisters the * OvsTunnelProviderBfeCallback callback from BFE (Base Filtering Engine). * * Unregister WFP system provider call hierarchy: * * * * * * --> unregisters OvsTunnelProviderBfeCallback callback * * -------------------------------------------------------------------------- */ VOID OvsUnregisterSystemProvider() { HANDLE engineSession = NULL; OvsTunnelEngineOpen(&engineSession); if (engineSession) { OvsTunnelRemoveSystemProvider(engineSession); } OvsTunnelEngineClose(&engineSession); OvsUnsubscribeTunnelProviderBfeStateChanges(); } /* * -------------------------------------------------------------------------- * This function initializes the tunnel filter if the BFE is running. * -------------------------------------------------------------------------- */ VOID NTAPI OvsTunnelInitBfeCallback(PVOID context, FWPM_SERVICE_STATE bfeState) { NTSTATUS status = STATUS_SUCCESS; PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) context; if (FWPM_SERVICE_RUNNING == bfeState) { status = OvsTunnelFilterInitialize(driverObject); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR( "Failed to initialize tunnel filter, status: %x.", status); } } } /* * -------------------------------------------------------------------------- * This function registers the OvsTunnelInitBfeCallback callback that is * called whenever there is a change to the state of base filtering engine. * -------------------------------------------------------------------------- */ NTSTATUS OvsSubscribeTunnelInitBfeStateChanges(PDRIVER_OBJECT driverObject, PVOID deviceObject) { NTSTATUS status = STATUS_SUCCESS; if (!gTunnelInitBfeHandle) { status = FwpmBfeStateSubscribeChanges(deviceObject, OvsTunnelInitBfeCallback, driverObject, &gTunnelInitBfeHandle); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR( "Failed to subscribe BFE tunnel init callback, status: %x.", status); } } return status; } /* * -------------------------------------------------------------------------- * This function unregisters the OvsTunnelInitBfeCallback callback that * was previously registered by OvsSubscribeTunnelInitBfeStateChanges * function. * -------------------------------------------------------------------------- */ VOID OvsUnsubscribeTunnelInitBfeStateChanges() { NTSTATUS status = STATUS_SUCCESS; if (gTunnelInitBfeHandle) { status = FwpmBfeStateUnsubscribeChanges(gTunnelInitBfeHandle); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR( "Failed to unsubscribe BFE tunnel init callback, status: %x.", status); } gTunnelInitBfeHandle = NULL; } } /* * -------------------------------------------------------------------------- * This function initializes the OVS tunnel filter if the BFE (Base Filtering * Engine) is running. * Otherwise, it will register the OvsTunnelInitBfeCallback callback. * Note: Before calling FwpmBfeStateGet, the callout driver must call the * FwpmBfeStateSubscribeChanges function to register the callback function * to be called whenever the state of the filter engine changes. * * Initialize OVS tunnel filter call hierarchy: * * * * * --> registers OvsTunnelInitBfeCallback callback * * --> if BFE is running: * * * * * --> if BFE is running: * * * * * * --> unregisters OvsTunnelInitBfeCallback callback * * -------------------------------------------------------------------------- */ NTSTATUS OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject) { NTSTATUS status = STATUS_SUCCESS; if (deviceObject) { status = OvsSubscribeTunnelInitBfeStateChanges(driverObject, deviceObject); if (NT_SUCCESS(status)) { if (FWPM_SERVICE_RUNNING == FwpmBfeStateGet()) { status = OvsTunnelFilterInitialize(driverObject); if (!NT_SUCCESS(status)) { /* XXX: We need to decide what actions to take in case of * failure to initialize tunnel filter. */ ASSERT(status == NDIS_STATUS_SUCCESS); OVS_LOG_ERROR( "Failed to initialize tunnel filter, status: %x.", status); } OvsUnsubscribeTunnelInitBfeStateChanges(); } } } else { status = OvsTunnelFilterInitialize(driverObject); } return status; } /* * -------------------------------------------------------------------------- * This function uninitializes the OVS tunnel filter and unregisters the * OvsTunnelInitBfeCallback callback from BFE. * * Uninitialize OVS tunnel filter call hierarchy: * * * * * * * * * --> unregisters OvsTunnelInitBfeCallback callback * * -------------------------------------------------------------------------- */ VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject) { OvsTunnelFilterUninitialize(driverObject); OvsUnsubscribeTunnelInitBfeStateChanges(); } NTSTATUS OvsTunnelAddFilterEx(HANDLE engineSession, UINT32 filterPort, UINT64 *filterID) { NTSTATUS status = STATUS_SUCCESS; status = OvsTunnelAddFilter(engineSession, L"Datagram-Data OVS Filter (Inbound)", L"address/port for UDP", (USHORT)filterPort, FWP_DIRECTION_INBOUND, 0, NULL, &FWPM_LAYER_DATAGRAM_DATA_V4, &OVS_TUNNEL_CALLOUT_V4, filterID); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to add tunnel filter for port: %d, status: %x.", filterPort, status); } else { OVS_LOG_INFO("Filter added, filter port: %d, filter ID: %d.", filterPort, *filterID); } return status; } NTSTATUS OvsTunnelRemoveFilterEx(HANDLE engineSession, UINT64 filterID) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN error = TRUE; do { if (filterID == 0) { OVS_LOG_INFO("No tunnel filter to remove."); break; } status = FwpmFilterDeleteById(engineSession, filterID); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to remove tunnel with filter ID: %d,\ status: %x.", filterID, status); break; } OVS_LOG_INFO("Filter removed, filter ID: %d.", filterID); error = FALSE; } while (error); return status; } NTSTATUS OvsTunnelFilterExecuteAction(HANDLE engineSession, POVS_TUNFLT_REQUEST request) { NTSTATUS status = STATUS_SUCCESS; switch (request->operation) { case OVS_TUN_FILTER_CREATE: status = OvsTunnelAddFilterEx(engineSession, request->port, request->filterID.addID); break; case OVS_TUN_FILTER_DELETE: status = OvsTunnelRemoveFilterEx(engineSession, request->filterID.delID); break; default: status = STATUS_NOT_SUPPORTED; break; } return status; } /* * -------------------------------------------------------------------------- * This function pops the head request from the queue while holding the * queue lock. If the request has already been cancelled or is about to be * cancelled, the function retrieves the next valid request. * * Returns a pointer to the OVS_TUNFLT_REQUEST_LIST request object retrieved * from the queue. * -------------------------------------------------------------------------- */ POVS_TUNFLT_REQUEST OvsTunnelFilterRequestPop(POVS_TUNFLT_REQUEST_LIST listRequests) { POVS_TUNFLT_REQUEST request = NULL; PLIST_ENTRY link, next, head; NdisAcquireSpinLock(&listRequests->spinlock); if (!IsListEmpty(&listRequests->head)) { head = &listRequests->head; LIST_FORALL_SAFE(head, link, next) { PDRIVER_CANCEL oldCancelRoutine; request = CONTAINING_RECORD(link, OVS_TUNFLT_REQUEST, entry); if (request->irp) { oldCancelRoutine = IoSetCancelRoutine(request->irp, NULL); if (oldCancelRoutine == NULL) { /* * The Cancel routine for the current IRP is running. The * request is to be completed by the Cancel routine. Leave * this request alone and go to the next one. */ continue; } else { /* * The Cancel routine cannot run now and cannot already have * started to run. This request can be processed. */ } } RemoveEntryList(&request->entry); listRequests->numEntries--; break; } } NdisReleaseSpinLock(&listRequests->spinlock); return request; } /* * -------------------------------------------------------------------------- * This function pushes the received request to the queue, marks the IRP as * pending and sets its Cancel routine, while holding the queue lock. * * Returns STATUS_CANCELLED if the IRP has already been cancelled. Otherwise, * STATUS_SUCCESS is returned. * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelFilterRequestPush(POVS_TUNFLT_REQUEST_LIST listRequests, POVS_TUNFLT_REQUEST request) { NTSTATUS status = STATUS_SUCCESS; PIRP irp = request->irp; PDRIVER_CANCEL oldCancelRoutine; BOOLEAN cancelled = FALSE; NdisAcquireSpinLock(&listRequests->spinlock); if (irp) { /* * Mark the IRP pending to indicate that the request may complete on * a different thread. */ IoMarkIrpPending(irp); /* * Set the Cancel routine for the pending IRP, before checking the * Cancel flag. */ oldCancelRoutine = IoSetCancelRoutine(irp, OvsTunnelFilterCancelIrp); ASSERT(oldCancelRoutine == NULL); if (irp->Cancel) { /* * The IRP has already been cancelled. * Determine wheather the Cancel routine has started to run. */ oldCancelRoutine = IoSetCancelRoutine(irp, NULL); if (oldCancelRoutine) { /* * The I/O Manager has not called the Cancel routine and it * won't be called anymore, because we just set it to NULL. * Return STATUS_CANCELLED and complete the request after * releasing the lock. */ status = STATUS_CANCELLED; cancelled = TRUE; } else { /* * The Cancel routine has already started to run, but it is * blocked while it waits for the queue lock. Release the lock * and return STATUS_SUCCESS to avoid completing the request. * It will be completed in the Cancel routine. */ } } else { /* * The IRP has not been cancelled, so set its context used in the * Cancel routine. */ OvsTunnelFilterSetIrpContext(listRequests, request); } } if (!cancelled) { InsertTailList(&listRequests->head, &(request->entry)); listRequests->numEntries++; } NdisReleaseSpinLock(&listRequests->spinlock); return status; } /* * -------------------------------------------------------------------------- * This function pushes the received request to the corresponding thread * request queue. The arrival of the new request is signaled to the thread, * in order to start processing it. * * Note: * If the thread is not initialized, no operation is performed. * * For a uniform distribution of requests to thread queues, a thread index is * calculated based on the received destination port. * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelFilterThreadPush(POVS_TUNFLT_REQUEST request) { NTSTATUS status = STATUS_REQUEST_ABORTED; UINT32 count = OVS_TUNFLT_MAX_THREADS; UINT32 threadIndex; threadIndex = request->port % OVS_TUNFLT_MAX_THREADS; while (count--) { if (gTunnelThreadCtx[threadIndex].isInitialized) { status = OvsTunnelFilterRequestPush( &gTunnelThreadCtx[threadIndex].listRequests, request); if (NT_SUCCESS(status)) { KeSetEvent(&gTunnelThreadCtx[threadIndex].requestEvent, IO_NO_INCREMENT, FALSE); } break; } else { OVS_LOG_INFO("OVS tunnel filter thread %d not initialized.", threadIndex); } threadIndex = (threadIndex + 1) % OVS_TUNFLT_MAX_THREADS; } return status; } VOID OvsTunnelFilterCompleteRequest(PIRP irp, PFNTunnelVportPendingOp callback, PVOID context, NTSTATUS status) { UINT32 replyLen = 0; if (callback) { callback(context, status, &replyLen); /* Release the context passed to the callback function. */ OvsFreeMemory(context); } if (irp) { OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status); } } VOID OvsTunnelFilterRequestListProcess(POVS_TUNFLT_THREAD_CONTEXT threadCtx) { POVS_TUNFLT_REQUEST request = NULL; NTSTATUS status = STATUS_SUCCESS; BOOLEAN inTransaction = FALSE; do { if (!InterlockedCompareExchange( (LONG volatile *)&threadCtx->listRequests.numEntries, 0, 0)) { OVS_LOG_INFO("Nothing to do... request list is empty."); break; } status = FwpmTransactionBegin(threadCtx->engineSession, 0); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to start transaction, status: %x.", status); break; } inTransaction = TRUE; while (NULL != (request = OvsTunnelFilterRequestPop(&threadCtx->listRequests))) { status = OvsTunnelFilterExecuteAction(threadCtx->engineSession, request); /* Complete the IRP with the last operation status. */ OvsTunnelFilterCompleteRequest(request->irp, request->callback, request->context, status); OvsFreeMemory(request); request = NULL; } status = FwpmTransactionCommit(threadCtx->engineSession); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to commit transaction, status: %x.", status); break; } inTransaction = FALSE; } while (inTransaction); if (inTransaction) { FwpmTransactionAbort(threadCtx->engineSession); OVS_LOG_ERROR("Failed to execute request, status: %x.\ Transaction aborted.", status); } } /* *---------------------------------------------------------------------------- * System thread routine that processes thread's requests queue. The thread * routine initializes thread's necessary data and waits on two events, * requestEvent and stopEvent. Whenever a request is pushed to the thread's * queue, the requestEvent is signaled and the thread routine starts processing * the arrived requests. When stopEvent is signaled, all subsequent requests * are completed with STATUS_CANCELED, without being added to the thread's * queue, and the routine finishes processing all existing requests from the * queue before uninitializing the thread and exiting. *---------------------------------------------------------------------------- */ _Use_decl_annotations_ VOID OvsTunnelFilterThreadProc(PVOID context) { NTSTATUS status = STATUS_SUCCESS; POVS_TUNFLT_THREAD_CONTEXT threadCtx = (POVS_TUNFLT_THREAD_CONTEXT)context; PKEVENT eventArray[2] = { 0 }; ULONG count = 0; BOOLEAN exit = FALSE; BOOLEAN error = TRUE; OVS_LOG_INFO("Starting OVS Tunnel system thread %d.", threadCtx->threadID); eventArray[0] = &threadCtx->stopEvent; eventArray[1] = &threadCtx->requestEvent; count = ARRAY_SIZE(eventArray); do { status = OvsTunnelFilterThreadInit(threadCtx); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to initialize tunnel filter thread %d.", threadCtx->threadID); break; } do { status = KeWaitForMultipleObjects(count, (PVOID)eventArray, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); switch (status) { case STATUS_WAIT_1: /* Start processing requests. */ OvsTunnelFilterRequestListProcess(threadCtx); break; default: /* Finish processing the remaining requests and exit. */ OvsTunnelFilterRequestListProcess(threadCtx); exit = TRUE; break; } } while (!exit); OvsTunnelFilterThreadUninit(threadCtx); error = FALSE; } while (error); OVS_LOG_INFO("Terminating OVS Tunnel system thread %d.", threadCtx->threadID); PsTerminateSystemThread(STATUS_SUCCESS); }; static NTSTATUS OvsTunnelFilterStartThreads() { NTSTATUS status = STATUS_SUCCESS; for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) { gTunnelThreadCtx[index].threadID = index; status = OvsTunnelFilterThreadStart(&gTunnelThreadCtx[index]); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to start tunnel filter thread %d.", index); break; } } return status; } static NTSTATUS OvsTunnelFilterThreadStart(POVS_TUNFLT_THREAD_CONTEXT threadCtx) { NTSTATUS status = STATUS_SUCCESS; HANDLE threadHandle = NULL; BOOLEAN error = TRUE; do { status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL, NULL, OvsTunnelFilterThreadProc, threadCtx); if (!NT_SUCCESS(status)) { OVS_LOG_ERROR("Failed to create tunnel thread, status: %x.", status); break; } ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode, &threadCtx->threadObject, NULL); ZwClose(threadHandle); threadHandle = NULL; error = FALSE; } while (error); return status; } static VOID OvsTunnelFilterStopThreads() { /* Signal all threads to stop and ignore all subsequent requests. */ for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) { OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], TRUE); } /* Wait for all threads to finish processing the requests. */ for (UINT index = 0; index < OVS_TUNFLT_MAX_THREADS; index++) { OvsTunnelFilterThreadStop(&gTunnelThreadCtx[index], FALSE); } } static VOID OvsTunnelFilterThreadStop(POVS_TUNFLT_THREAD_CONTEXT threadCtx, BOOLEAN signalEvent) { if (threadCtx->isInitialized) { if (signalEvent) { /* Signal stop thread event. */ OVS_LOG_INFO("Received stop event for OVS Tunnel system thread %d.", threadCtx->threadID); KeSetEvent(&threadCtx->stopEvent, IO_NO_INCREMENT, FALSE); } else { /* Wait for the tunnel thread to finish. */ KeWaitForSingleObject(threadCtx->threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(threadCtx->threadObject); } } } /* * -------------------------------------------------------------------------- * This function initializes thread's necessary data. Each thread has its own * session object to the BFE that is used for processing the requests from * the thread's queue. * -------------------------------------------------------------------------- */ static NTSTATUS OvsTunnelFilterThreadInit(POVS_TUNFLT_THREAD_CONTEXT threadCtx) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN error = TRUE; do { /* Create thread's engine session object. */ status = OvsTunnelEngineOpen(&threadCtx->engineSession); if (!NT_SUCCESS(status)) { break; } NdisAllocateSpinLock(&threadCtx->listRequests.spinlock); InitializeListHead(&threadCtx->listRequests.head); KeInitializeEvent(&threadCtx->stopEvent, NotificationEvent, FALSE); KeInitializeEvent(&threadCtx->requestEvent, SynchronizationEvent, FALSE); threadCtx->isInitialized = TRUE; error = FALSE; } while (error); return status; } /* * -------------------------------------------------------------------------- * This function uninitializes thread's private data. Thread's engine session * handle is closed and set to NULL. * -------------------------------------------------------------------------- */ static VOID OvsTunnelFilterThreadUninit(POVS_TUNFLT_THREAD_CONTEXT threadCtx) { if (threadCtx->engineSession) { /* Close thread's FWPM session. */ OvsTunnelEngineClose(&threadCtx->engineSession); NdisFreeSpinLock(&threadCtx->listRequests.spinlock); threadCtx->isInitialized = FALSE; } } /* * -------------------------------------------------------------------------- * This function creates a new tunnel filter request and push it to a thread * queue. If the thread stop event is signaled, the request is completed with * STATUS_REQUEST_ABORTED without pushing it to any queue. * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelFilterQueueRequest(PIRP irp, UINT16 remotePort, UINT64 *filterID, OVS_TUNFLT_OPERATION operation, PFNTunnelVportPendingOp callback, PVOID tunnelContext) { POVS_TUNFLT_REQUEST request = NULL; NTSTATUS status = STATUS_PENDING; NTSTATUS result = STATUS_SUCCESS; BOOLEAN error = TRUE; UINT64 timeout = 0; do { /* Verify if the stop event was signaled. */ if (STATUS_SUCCESS == KeWaitForSingleObject( &gTunnelThreadCtx[0].stopEvent, Executive, KernelMode, FALSE, (LARGE_INTEGER *)&timeout)) { /* The stop event is signaled. Completed the IRP with * STATUS_REQUEST_ABORTED. */ status = STATUS_REQUEST_ABORTED; break; } if (NULL == filterID) { OVS_LOG_ERROR("Invalid request."); status = STATUS_INVALID_PARAMETER; break; } request = (POVS_TUNFLT_REQUEST) OvsAllocateMemoryWithTag(sizeof(*request), OVS_TUNFLT_POOL_TAG); if (NULL == request) { OVS_LOG_ERROR("Failed to allocate list item."); status = STATUS_INSUFFICIENT_RESOURCES; break; } request->port = remotePort; request->operation = operation; switch (operation) { case OVS_TUN_FILTER_CREATE: request->filterID.addID = filterID; break; case OVS_TUN_FILTER_DELETE: request->filterID.delID = *filterID; break; } request->irp = irp; request->callback = callback; request->context = tunnelContext; result = OvsTunnelFilterThreadPush(request); if (!NT_SUCCESS(result)) { status = result; break; } error = FALSE; } while (error); if (error) { OvsTunnelFilterCompleteRequest(irp, callback, tunnelContext, status); if (request) { OvsFreeMemory(request); request = NULL; } } return status; } /* * -------------------------------------------------------------------------- * This function adds a new WFP filter for the received port and returns the * ID of the created WFP filter. * * Note: * All necessary calls to the WFP filtering engine must be running at IRQL = * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL, * we register an OVS_TUN_FILTER_CREATE request that will be processed by * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL. * * OVS VXLAN port add call hierarchy: * * * * * * --> if thread STOP event is signalled: * --> Complete request with STATUS_CANCELLED * --> EXIT * * --> add the request to one of tunnel thread queues * * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelFilterCreate(PIRP irp, UINT16 filterPort, UINT64 *filterID, PFNTunnelVportPendingOp callback, PVOID tunnelContext) { return OvsTunnelFilterQueueRequest(irp, filterPort, filterID, OVS_TUN_FILTER_CREATE, callback, tunnelContext); } /* * -------------------------------------------------------------------------- * This function removes a WFP filter using the received filter ID. * * Note: * All necessary calls to the WFP filtering engine must be running at IRQL = * PASSIVE_LEVEL. Because the function is called at IRQL = DISPATCH_LEVEL, * we register an OVS_TUN_FILTER_DELETE request that will be processed by * the tunnel filter thread routine at IRQL = PASSIVE_LEVEL. * * OVS VXLAN port delete call hierarchy: * * * * * * --> if thread STOP event is signalled: * --> Complete request with STATUS_CANCELLED * --> EXIT * * --> add the request to one of tunnel thread queues * * -------------------------------------------------------------------------- */ NTSTATUS OvsTunnelFilterDelete(PIRP irp, UINT64 filterID, PFNTunnelVportPendingOp callback, PVOID tunnelContext) { return OvsTunnelFilterQueueRequest(irp, 0, &filterID, OVS_TUN_FILTER_DELETE, callback, tunnelContext); } /* * -------------------------------------------------------------------------- * This function sets the context for the IRP. The context is used by the * Cancel routine, in order to identify the request object, corresponding to * the IRP, to be completed and to have access to the queue lock to remove * the request link from the queue. * -------------------------------------------------------------------------- */ VOID OvsTunnelFilterSetIrpContext(POVS_TUNFLT_REQUEST_LIST listRequests, POVS_TUNFLT_REQUEST request) { PIRP irp = request->irp; if (irp) { /* Set the IRP's DriverContext to be used for later. */ irp->Tail.Overlay.DriverContext[0] = (PVOID)request; irp->Tail.Overlay.DriverContext[1] = (PVOID)listRequests; } } /* * -------------------------------------------------------------------------- * This function is the Cancel routine to be called by the I/O Manager in the * case when the IRP is cancelled. * -------------------------------------------------------------------------- */ VOID OvsTunnelFilterCancelIrp(PDEVICE_OBJECT DeviceObject, PIRP irp) { POVS_TUNFLT_REQUEST request = (POVS_TUNFLT_REQUEST)irp->Tail.Overlay.DriverContext[0]; POVS_TUNFLT_REQUEST_LIST listRequests = (POVS_TUNFLT_REQUEST_LIST)irp->Tail.Overlay.DriverContext[1]; DBG_UNREFERENCED_PARAMETER(DeviceObject); /* Release the global cancel spinlock. */ IoReleaseCancelSpinLock(irp->CancelIrql); /* Clear the cancel routine from the IRP. */ IoSetCancelRoutine(irp, NULL); NdisAcquireSpinLock(&listRequests->spinlock); /* Remove the request from the corresponding tunnel filter thread queue. */ RemoveEntryList(&request->entry); listRequests->numEntries--; NdisReleaseSpinLock(&listRequests->spinlock); /* We are done with this IRP, so complete it with STATUS_CANCELLED. */ OvsTunnelFilterCompleteRequest(request->irp, request->callback, request->context, STATUS_CANCELLED); OvsFreeMemory(request); } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Actions.c0000644000000000000000000000013113534540071022215 xustar0029 mtime=1567801401.20567973 30 atime=1567801402.045685899 30 ctime=1567801424.353850271 openvswitch-2.5.9/datapath-windows/ovsext/Actions.c0000644000175000017500000016621213534540071023714 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Checksum.h" #include "Event.h" #include "Flow.h" #include "Gre.h" #include "NetProto.h" #include "PacketIO.h" #include "Stt.h" #include "Switch.h" #include "User.h" #include "Vport.h" #include "Vxlan.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_ACTION #include "Debug.h" typedef struct _OVS_ACTION_STATS { UINT64 rxGre; UINT64 txGre; UINT64 rxVxlan; UINT64 txVxlan; UINT64 rxStt; UINT64 txStt; UINT64 flowMiss; UINT64 flowUserspace; UINT64 txTcp; UINT32 failedFlowMiss; UINT32 noVport; UINT32 failedFlowExtract; UINT32 noResource; UINT32 noCopiedNbl; UINT32 failedEncap; UINT32 failedDecap; UINT32 cannotGrowDest; UINT32 zeroActionLen; UINT32 failedChecksum; } OVS_ACTION_STATS, *POVS_ACTION_STATS; OVS_ACTION_STATS ovsActionStats; /* * There a lot of data that needs to be maintained while executing the pipeline * as dictated by the actions of a flow, across different functions at different * levels. Such data is put together in a 'context' structure. Care should be * exercised while adding new members to the structure - only add ones that get * used across multiple stages in the pipeline/get used in multiple functions. */ #define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2 typedef struct OvsForwardingContext { POVS_SWITCH_CONTEXT switchContext; /* The NBL currently used in the pipeline. */ PNET_BUFFER_LIST curNbl; /* NDIS forwarding detail for 'curNbl'. */ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; /* Array of destination ports for 'curNbl'. */ PNDIS_SWITCH_FORWARDING_DESTINATION_ARRAY destinationPorts; /* send flags while sending 'curNbl' into NDIS. */ ULONG sendFlags; /* Total number of output ports, used + unused, in 'curNbl'. */ UINT32 destPortsSizeIn; /* Total number of used output ports in 'curNbl'. */ UINT32 destPortsSizeOut; /* * If 'curNbl' is not owned by OVS, they need to be tracked, if they need to * be freed/completed. */ OvsCompletionList *completionList; /* * vport number of 'curNbl' when it is passed from the PIF bridge to the INT * bridge. ie. during tunneling on the Rx side. */ UINT32 srcVportNo; /* * Tunnel key: * - specified in actions during tunneling Tx * - extracted from an NBL during tunneling Rx */ OvsIPv4TunnelKey tunKey; /* * Tunneling - Tx: * To store the output port, when it is a tunneled port. We don't foresee * multiple tunneled ports as outport for any given NBL. */ POVS_VPORT_ENTRY tunnelTxNic; /* * Tunneling - Rx: * Points to the Internal port on the PIF Bridge, if the packet needs to be * de-tunneled. */ POVS_VPORT_ENTRY tunnelRxNic; /* header information */ OVS_PACKET_HDR_INFO layers; } OvsForwardingContext; /* * -------------------------------------------------------------------------- * OvsInitForwardingCtx -- * Function to init/re-init the 'ovsFwdCtx' context as the actions pipeline * is being executed. * * Result: * NDIS_STATUS_SUCCESS on success * Other NDIS_STATUS upon failure. Upon failure, it is safe to call * OvsCompleteNBLForwardingCtx(), since 'ovsFwdCtx' has been initialized * enough for OvsCompleteNBLForwardingCtx() to do its work. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsInitForwardingCtx(OvsForwardingContext *ovsFwdCtx, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, UINT32 srcVportNo, ULONG sendFlags, PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail, OvsCompletionList *completionList, OVS_PACKET_HDR_INFO *layers, BOOLEAN resetTunnelInfo) { ASSERT(ovsFwdCtx); ASSERT(switchContext); ASSERT(curNbl); ASSERT(fwdDetail); /* * Set values for curNbl and switchContext so upon failures, we have enough * information to do cleanup. */ ovsFwdCtx->curNbl = curNbl; ovsFwdCtx->switchContext = switchContext; ovsFwdCtx->completionList = completionList; ovsFwdCtx->fwdDetail = fwdDetail; if (fwdDetail->NumAvailableDestinations > 0) { /* * XXX: even though MSDN says GetNetBufferListDestinations() returns * NDIS_STATUS, the header files say otherwise. */ switchContext->NdisSwitchHandlers.GetNetBufferListDestinations( switchContext->NdisSwitchContext, curNbl, &ovsFwdCtx->destinationPorts); ASSERT(ovsFwdCtx->destinationPorts); /* Ensure that none of the elements are consumed yet. */ ASSERT(ovsFwdCtx->destinationPorts->NumElements == fwdDetail->NumAvailableDestinations); } else { ovsFwdCtx->destinationPorts = NULL; } ovsFwdCtx->destPortsSizeIn = fwdDetail->NumAvailableDestinations; ovsFwdCtx->destPortsSizeOut = 0; ovsFwdCtx->srcVportNo = srcVportNo; ovsFwdCtx->sendFlags = sendFlags; if (layers) { ovsFwdCtx->layers = *layers; } else { RtlZeroMemory(&ovsFwdCtx->layers, sizeof ovsFwdCtx->layers); } if (resetTunnelInfo) { ovsFwdCtx->tunnelTxNic = NULL; ovsFwdCtx->tunnelRxNic = NULL; RtlZeroMemory(&ovsFwdCtx->tunKey, sizeof ovsFwdCtx->tunKey); } return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsDetectTunnelRxPkt -- * Utility function for an RX packet to detect its tunnel type. * * Result: * True - if the tunnel type was detected. * False - if not a tunnel packet or tunnel type not supported. * -------------------------------------------------------------------------- */ static __inline BOOLEAN OvsDetectTunnelRxPkt(OvsForwardingContext *ovsFwdCtx, const OvsFlowKey *flowKey) { POVS_VPORT_ENTRY tunnelVport = NULL; /* XXX: we should also check for the length of the UDP payload to pick * packets only if they are at least VXLAN header size. */ if (!flowKey->ipKey.nwFrag) { UINT16 dstPort = htons(flowKey->ipKey.l4.tpDst); switch (flowKey->ipKey.nwProto) { case IPPROTO_GRE: tunnelVport = OvsFindTunnelVportByPortType(ovsFwdCtx->switchContext, OVS_VPORT_TYPE_GRE); if (tunnelVport) { ovsActionStats.rxGre++; } break; case IPPROTO_TCP: tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext, dstPort, OVS_VPORT_TYPE_STT); if (tunnelVport) { ovsActionStats.rxStt++; } break; case IPPROTO_UDP: tunnelVport = OvsFindTunnelVportByDstPort(ovsFwdCtx->switchContext, dstPort, OVS_VPORT_TYPE_VXLAN); if (tunnelVport) { ovsActionStats.rxVxlan++; } break; } } // We might get tunnel packets even before the tunnel gets initialized. if (tunnelVport) { ASSERT(ovsFwdCtx->tunnelRxNic == NULL); ovsFwdCtx->tunnelRxNic = tunnelVport; return TRUE; } return FALSE; } /* * -------------------------------------------------------------------------- * OvsDetectTunnelPkt -- * Utility function to detect if a packet is to be subjected to * tunneling (Tx) or de-tunneling (Rx). Various factors such as source * port, destination port, packet contents, and previously setup tunnel * context are used. * * Result: * True - If the packet is to be subjected to tunneling. * In case of invalid tunnel context, the tunneling functionality is * a no-op and is completed within this function itself by consuming * all of the tunneling context. * False - If not a tunnel packet or tunnel type not supported. Caller should * process the packet as a non-tunnel packet. * -------------------------------------------------------------------------- */ static __inline BOOLEAN OvsDetectTunnelPkt(OvsForwardingContext *ovsFwdCtx, const POVS_VPORT_ENTRY dstVport, const OvsFlowKey *flowKey) { if (OvsIsInternalVportType(dstVport->ovsType)) { /* * Rx: * The source of NBL during tunneling Rx could be the external * port or if it is being executed from userspace, the source port is * default port. */ BOOLEAN validSrcPort = (ovsFwdCtx->fwdDetail->SourcePortId == ovsFwdCtx->switchContext->virtualExternalPortId) || (ovsFwdCtx->fwdDetail->SourcePortId == NDIS_SWITCH_DEFAULT_PORT_ID); if (validSrcPort && OvsDetectTunnelRxPkt(ovsFwdCtx, flowKey)) { ASSERT(ovsFwdCtx->tunnelTxNic == NULL); ASSERT(ovsFwdCtx->tunnelRxNic != NULL); return TRUE; } } else if (OvsIsTunnelVportType(dstVport->ovsType)) { ASSERT(ovsFwdCtx->tunnelTxNic == NULL); ASSERT(ovsFwdCtx->tunnelRxNic == NULL); /* * Tx: * The destination port is a tunnel port. Encapsulation must be * performed only on packets that originate from: * - a VIF port * - a bridge-internal port (packets generated from userspace) * - no port. * * If the packet will not be encapsulated, consume the tunnel context * by clearing it. */ if (ovsFwdCtx->srcVportNo != OVS_DPPORT_NUMBER_INVALID) { POVS_VPORT_ENTRY vport = OvsFindVportByPortNo( ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo); if (!vport || (vport->ovsType != OVS_VPORT_TYPE_NETDEV && !OvsIsBridgeInternalVport(vport))) { ovsFwdCtx->tunKey.dst = 0; } } /* Tunnel the packet only if tunnel context is set. */ if (ovsFwdCtx->tunKey.dst != 0) { switch(dstVport->ovsType) { case OVS_VPORT_TYPE_GRE: ovsActionStats.txGre++; break; case OVS_VPORT_TYPE_VXLAN: ovsActionStats.txVxlan++; break; case OVS_VPORT_TYPE_STT: ovsActionStats.txStt++; break; } ovsFwdCtx->tunnelTxNic = dstVport; } return TRUE; } return FALSE; } /* * -------------------------------------------------------------------------- * OvsAddPorts -- * Add the specified destination vport into the forwarding context. If the * vport is a VIF/external port, it is added directly to the NBL. If it is * a tunneling port, it is NOT added to the NBL. * * Result: * NDIS_STATUS_SUCCESS on success * Other NDIS_STATUS upon failure. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsAddPorts(OvsForwardingContext *ovsFwdCtx, OvsFlowKey *flowKey, NDIS_SWITCH_PORT_ID dstPortId, BOOLEAN preserveVLAN, BOOLEAN preservePriority) { POVS_VPORT_ENTRY vport; PNDIS_SWITCH_PORT_DESTINATION fwdPort; NDIS_STATUS status; POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext; /* * We hold the dispatch lock that protects the list of vports, so vports * validated here can be added as destinations safely before we call into * NDIS. * * Some of the vports can be tunnelled ports as well in which case * they should be added to a separate list of tunnelled destination ports * instead of the VIF ports. The context for the tunnel is settable * in OvsForwardingContext. */ vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, dstPortId); if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) { /* * There may be some latency between a port disappearing, and userspace * updating the recalculated flows. In the meantime, handle invalid * ports gracefully. */ ovsActionStats.noVport++; return NDIS_STATUS_SUCCESS; } ASSERT(vport->nicState == NdisSwitchNicStateConnected); vport->stats.txPackets++; vport->stats.txBytes += NET_BUFFER_DATA_LENGTH(NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl)); if (OvsIsBridgeInternalVport(vport)) { return NDIS_STATUS_SUCCESS; } if (OvsDetectTunnelPkt(ovsFwdCtx, vport, flowKey)) { return NDIS_STATUS_SUCCESS; } if (ovsFwdCtx->destPortsSizeOut == ovsFwdCtx->destPortsSizeIn) { if (ovsFwdCtx->destPortsSizeIn == 0) { ASSERT(ovsFwdCtx->destinationPorts == NULL); ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0); status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations( switchContext->NdisSwitchContext, ovsFwdCtx->curNbl, OVS_DEST_PORTS_ARRAY_MIN_SIZE, &ovsFwdCtx->destinationPorts); if (status != NDIS_STATUS_SUCCESS) { ovsActionStats.cannotGrowDest++; return status; } ovsFwdCtx->destPortsSizeIn = ovsFwdCtx->fwdDetail->NumAvailableDestinations; ASSERT(ovsFwdCtx->destinationPorts); } else { ASSERT(ovsFwdCtx->destinationPorts != NULL); /* * NumElements: * A ULONG value that specifies the total number of * NDIS_SWITCH_PORT_DESTINATION elements in the * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure. * * NumDestinations: * A ULONG value that specifies the number of * NDIS_SWITCH_PORT_DESTINATION elements in the * NDIS_SWITCH_FORWARDING_DESTINATION_ARRAY structure that * specify port destinations. * * NumAvailableDestinations: * A value that specifies the number of unused extensible switch * destination ports elements within an NET_BUFFER_LIST structure. */ ASSERT(ovsFwdCtx->destinationPorts->NumElements == ovsFwdCtx->destPortsSizeIn); ASSERT(ovsFwdCtx->destinationPorts->NumDestinations == ovsFwdCtx->destPortsSizeOut - ovsFwdCtx->fwdDetail->NumAvailableDestinations); ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations > 0); /* * Before we grow the array of destination ports, the current set * of ports needs to be committed. Only the ports added since the * last commit need to be part of the new update. */ status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations( switchContext->NdisSwitchContext, ovsFwdCtx->curNbl, ovsFwdCtx->fwdDetail->NumAvailableDestinations, ovsFwdCtx->destinationPorts); if (status != NDIS_STATUS_SUCCESS) { ovsActionStats.cannotGrowDest++; return status; } ASSERT(ovsFwdCtx->destinationPorts->NumElements == ovsFwdCtx->destPortsSizeIn); ASSERT(ovsFwdCtx->destinationPorts->NumDestinations == ovsFwdCtx->destPortsSizeOut); ASSERT(ovsFwdCtx->fwdDetail->NumAvailableDestinations == 0); status = switchContext->NdisSwitchHandlers.GrowNetBufferListDestinations( switchContext->NdisSwitchContext, ovsFwdCtx->curNbl, ovsFwdCtx->destPortsSizeIn, &ovsFwdCtx->destinationPorts); if (status != NDIS_STATUS_SUCCESS) { ovsActionStats.cannotGrowDest++; return status; } ASSERT(ovsFwdCtx->destinationPorts != NULL); ovsFwdCtx->destPortsSizeIn <<= 1; } } ASSERT(ovsFwdCtx->destPortsSizeOut < ovsFwdCtx->destPortsSizeIn); fwdPort = NDIS_SWITCH_PORT_DESTINATION_AT_ARRAY_INDEX(ovsFwdCtx->destinationPorts, ovsFwdCtx->destPortsSizeOut); fwdPort->PortId = vport->portId; fwdPort->NicIndex = vport->nicIndex; fwdPort->IsExcluded = 0; fwdPort->PreserveVLAN = preserveVLAN; fwdPort->PreservePriority = preservePriority; ovsFwdCtx->destPortsSizeOut += 1; return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsClearTunTxCtx -- * Utility function to clear tx tunneling context. * -------------------------------------------------------------------------- */ static __inline VOID OvsClearTunTxCtx(OvsForwardingContext *ovsFwdCtx) { ovsFwdCtx->tunnelTxNic = NULL; ovsFwdCtx->tunKey.dst = 0; } /* * -------------------------------------------------------------------------- * OvsClearTunRxCtx -- * Utility function to clear rx tunneling context. * -------------------------------------------------------------------------- */ static __inline VOID OvsClearTunRxCtx(OvsForwardingContext *ovsFwdCtx) { ovsFwdCtx->tunnelRxNic = NULL; ovsFwdCtx->tunKey.dst = 0; } /* * -------------------------------------------------------------------------- * OvsCompleteNBLForwardingCtx -- * This utility function is responsible for freeing/completing an NBL - either * by adding it to a completion list or by freeing it. * * Side effects: * It also resets the necessary fields in 'ovsFwdCtx'. * -------------------------------------------------------------------------- */ static __inline VOID OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx, PCWSTR dropReason) { NDIS_STRING filterReason; RtlInitUnicodeString(&filterReason, dropReason); if (ovsFwdCtx->completionList) { OvsAddPktCompletionList(ovsFwdCtx->completionList, TRUE, ovsFwdCtx->fwdDetail->SourcePortId, ovsFwdCtx->curNbl, 1, &filterReason); ovsFwdCtx->curNbl = NULL; } else { /* If there is no completionList, we assume this is ovs created NBL */ ovsFwdCtx->curNbl = OvsCompleteNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, TRUE); ASSERT(ovsFwdCtx->curNbl == NULL); } /* XXX: these can be made debug only to save cycles. Ideally the pipeline * using these fields should reset the values at the end of the pipeline. */ ovsFwdCtx->destPortsSizeOut = 0; ovsFwdCtx->tunnelTxNic = NULL; ovsFwdCtx->tunnelRxNic = NULL; } /* * -------------------------------------------------------------------------- * OvsDoFlowLookupOutput -- * Function to be used for the second stage of a tunneling workflow, ie.: * - On the encapsulated packet on Tx path, to do a flow extract, flow * lookup and excuting the actions. * - On the decapsulated packet on Rx path, to do a flow extract, flow * lookup and excuting the actions. * * XXX: It is assumed that the NBL in 'ovsFwdCtx' is owned by OVS. This is * until the new buffer management framework is adopted. * * Side effects: * The NBL in 'ovsFwdCtx' is consumed. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx) { OvsFlowKey key; OvsFlow *flow; UINT64 hash; NDIS_STATUS status; POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo); if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) { OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to internal/tunnel port removal"); ovsActionStats.noVport++; return NDIS_STATUS_SUCCESS; } ASSERT(vport->nicState == NdisSwitchNicStateConnected); /* Assert that in the Rx direction, key is always setup. */ ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0); status = OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo, &key, &ovsFwdCtx->layers, ovsFwdCtx->tunKey.dst != 0 ? &ovsFwdCtx->tunKey : NULL); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Flow extract failed"); ovsActionStats.failedFlowExtract++; return status; } flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, FALSE); if (flow) { OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers); ovsFwdCtx->switchContext->datapath.hits++; status = OvsActionsExecute(ovsFwdCtx->switchContext, ovsFwdCtx->completionList, ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags, &key, &hash, &ovsFwdCtx->layers, flow->actions, flow->actionsLen); ovsFwdCtx->curNbl = NULL; } else { LIST_ENTRY missedPackets; UINT32 num = 0; ovsFwdCtx->switchContext->datapath.misses++; InitializeListHead(&missedPackets); status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS, vport, &key,ovsFwdCtx->curNbl, FALSE, &ovsFwdCtx->layers, ovsFwdCtx->switchContext, &missedPackets, &num); if (num) { OvsQueuePackets(&missedPackets, num); } if (status == NDIS_STATUS_SUCCESS) { /* Complete the packet since it was copied to user buffer. */ OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped since packet was copied to userspace"); ovsActionStats.flowMiss++; status = NDIS_STATUS_SUCCESS; } else { OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to failure to queue to userspace"); status = NDIS_STATUS_FAILURE; ovsActionStats.failedFlowMiss++; } } return status; } /* * -------------------------------------------------------------------------- * OvsTunnelPortTx -- * The start function for Tx tunneling - encapsulates the packet, and * outputs the packet on the PIF bridge. * * Side effects: * The NBL in 'ovsFwdCtx' is consumed. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx) { NDIS_STATUS status = NDIS_STATUS_FAILURE; PNET_BUFFER_LIST newNbl = NULL; /* * Setup the source port to be the internal port to as to facilitate the * second OvsLookupFlow. */ if (ovsFwdCtx->switchContext->internalVport == NULL || ovsFwdCtx->switchContext->virtualExternalVport == NULL) { OvsClearTunTxCtx(ovsFwdCtx); OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped since either internal or external port is absent"); return NDIS_STATUS_FAILURE; } ovsFwdCtx->srcVportNo = ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->portNo; ovsFwdCtx->fwdDetail->SourcePortId = ovsFwdCtx->switchContext->internalPortId; ovsFwdCtx->fwdDetail->SourceNicIndex = ((POVS_VPORT_ENTRY)ovsFwdCtx->switchContext->internalVport)->nicIndex; /* Do the encap. Encap function does not consume the NBL. */ switch(ovsFwdCtx->tunnelTxNic->ovsType) { case OVS_VPORT_TYPE_GRE: status = OvsEncapGre(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext, &ovsFwdCtx->layers, &newNbl); break; case OVS_VPORT_TYPE_VXLAN: status = OvsEncapVxlan(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext, &ovsFwdCtx->layers, &newNbl); break; case OVS_VPORT_TYPE_STT: status = OvsEncapStt(ovsFwdCtx->tunnelTxNic, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, ovsFwdCtx->switchContext, &ovsFwdCtx->layers, &newNbl); break; default: ASSERT(! "Tx: Unhandled tunnel type"); } /* Reset the tunnel context so that it doesn't get used after this point. */ OvsClearTunTxCtx(ovsFwdCtx); if (status == NDIS_STATUS_SUCCESS) { ASSERT(newNbl); OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"Complete after cloning NBL for encapsulation"); ovsFwdCtx->curNbl = newNbl; status = OvsDoFlowLookupOutput(ovsFwdCtx); ASSERT(ovsFwdCtx->curNbl == NULL); } else { /* * XXX: Temporary freeing of the packet until we register a * callback to IP helper. */ OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to encap failure"); ovsActionStats.failedEncap++; status = NDIS_STATUS_SUCCESS; } return status; } /* * -------------------------------------------------------------------------- * OvsTunnelPortRx -- * Decapsulate the incoming NBL based on the tunnel type and goes through * the flow lookup for the inner packet. * * Note: IP checksum is validate here, but L4 checksum validation needs * to be done by the corresponding tunnel types. * * Side effects: * The NBL in 'ovsFwdCtx' is consumed. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsTunnelPortRx(OvsForwardingContext *ovsFwdCtx) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; PNET_BUFFER_LIST newNbl = NULL; POVS_VPORT_ENTRY tunnelRxVport = ovsFwdCtx->tunnelRxNic; PCWSTR dropReason = L"OVS-dropped due to new decap packet"; if (OvsValidateIPChecksum(ovsFwdCtx->curNbl, &ovsFwdCtx->layers) != NDIS_STATUS_SUCCESS) { ovsActionStats.failedChecksum++; OVS_LOG_INFO("Packet dropped due to IP checksum failure."); goto dropNbl; } /* * Decap port functions should return a new NBL if it was copied, and * this new NBL should be setup as the ovsFwdCtx->curNbl. */ switch(tunnelRxVport->ovsType) { case OVS_VPORT_TYPE_GRE: status = OvsDecapGre(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, &newNbl); break; case OVS_VPORT_TYPE_VXLAN: status = OvsDecapVxlan(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, &newNbl); break; case OVS_VPORT_TYPE_STT: status = OvsDecapStt(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, &ovsFwdCtx->tunKey, &newNbl); if (status == NDIS_STATUS_SUCCESS && newNbl == NULL) { /* This was an STT-LSO Fragment */ dropReason = L"OVS-STT segment is cached"; } break; default: OVS_LOG_ERROR("Rx: Unhandled tunnel type: %d\n", tunnelRxVport->ovsType); ASSERT(! "Rx: Unhandled tunnel type"); status = NDIS_STATUS_NOT_SUPPORTED; } if (status != NDIS_STATUS_SUCCESS) { ovsActionStats.failedDecap++; goto dropNbl; } /* * tunnelRxNic and other fields will be cleared, re-init the context * before usage. */ OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason); if (newNbl) { /* Decapsulated packet is in a new NBL */ ovsFwdCtx->tunnelRxNic = tunnelRxVport; OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl, tunnelRxVport->portNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), ovsFwdCtx->completionList, &ovsFwdCtx->layers, FALSE); /* * Set the NBL's SourcePortId and SourceNicIndex to default values to * keep NDIS happy when we forward the packet. */ ovsFwdCtx->fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; ovsFwdCtx->fwdDetail->SourceNicIndex = 0; status = OvsDoFlowLookupOutput(ovsFwdCtx); } ASSERT(ovsFwdCtx->curNbl == NULL); OvsClearTunRxCtx(ovsFwdCtx); return status; dropNbl: OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-dropped due to decap failure"); OvsClearTunRxCtx(ovsFwdCtx); return status; } /* * -------------------------------------------------------------------------- * OvsOutputForwardingCtx -- * This function outputs an NBL to NDIS or to a tunneling pipeline based on * the ports added so far into 'ovsFwdCtx'. * * Side effects: * This function consumes the NBL - either by forwarding it successfully to * NDIS, or adding it to the completion list in 'ovsFwdCtx', or freeing it. * * Also makes sure that the list of destination ports - tunnel or otherwise is * drained. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsOutputForwardingCtx(OvsForwardingContext *ovsFwdCtx) { NDIS_STATUS status = STATUS_SUCCESS; POVS_SWITCH_CONTEXT switchContext = ovsFwdCtx->switchContext; PCWSTR dropReason; /* * Handle the case where the some of the destination ports are tunneled * ports - the non-tunneled ports get a unmodified copy of the NBL, and the * tunneling pipeline starts when we output the packet to tunneled port. */ if (ovsFwdCtx->destPortsSizeOut > 0) { PNET_BUFFER_LIST newNbl = NULL; PNET_BUFFER nb; UINT32 portsToUpdate = ovsFwdCtx->fwdDetail->NumAvailableDestinations - (ovsFwdCtx->destPortsSizeIn - ovsFwdCtx->destPortsSizeOut); ASSERT(ovsFwdCtx->destinationPorts != NULL); /* * Create a copy of the packet in order to do encap on it later. Also, * don't copy the offload context since the encap'd packet has a * different set of headers. This will change when we implement offloads * before doing encapsulation. */ if (ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL) { nb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, 0, 0, TRUE /*copy NBL info*/); if (newNbl == NULL) { status = NDIS_STATUS_RESOURCES; ovsActionStats.noCopiedNbl++; dropReason = L"Dropped due to failure to create NBL copy."; goto dropit; } } /* It does not seem like we'll get here unless 'portsToUpdate' > 0. */ ASSERT(portsToUpdate > 0); status = switchContext->NdisSwitchHandlers.UpdateNetBufferListDestinations( switchContext->NdisSwitchContext, ovsFwdCtx->curNbl, portsToUpdate, ovsFwdCtx->destinationPorts); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE); ovsActionStats.cannotGrowDest++; dropReason = L"Dropped due to failure to update destinations."; goto dropit; } OvsSendNBLIngress(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, ovsFwdCtx->sendFlags); /* End this pipeline by resetting the corresponding context. */ ovsFwdCtx->destPortsSizeOut = 0; ovsFwdCtx->curNbl = NULL; if (newNbl) { status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl, ovsFwdCtx->srcVportNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), ovsFwdCtx->completionList, &ovsFwdCtx->layers, FALSE); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"Dropped due to resouces."; goto dropit; } } } if (ovsFwdCtx->tunnelTxNic != NULL) { status = OvsTunnelPortTx(ovsFwdCtx); ASSERT(ovsFwdCtx->tunnelTxNic == NULL); ASSERT(ovsFwdCtx->tunKey.dst == 0); } else if (ovsFwdCtx->tunnelRxNic != NULL) { status = OvsTunnelPortRx(ovsFwdCtx); ASSERT(ovsFwdCtx->tunnelRxNic == NULL); ASSERT(ovsFwdCtx->tunKey.dst == 0); } ASSERT(ovsFwdCtx->curNbl == NULL); return status; dropit: if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBLForwardingCtx(ovsFwdCtx, dropReason); } return status; } /* * -------------------------------------------------------------------------- * OvsLookupFlowOutput -- * Utility function for external callers to do flow extract, lookup, * actions execute on a given NBL. * * Note: If this is being used from a callback function, make sure that the * arguments specified are still valid in the asynchronous context. * * Side effects: * This function consumes the NBL. * -------------------------------------------------------------------------- */ VOID OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext, VOID *compList, PNET_BUFFER_LIST curNbl) { NDIS_STATUS status; OvsForwardingContext ovsFwdCtx; POVS_VPORT_ENTRY internalVport = (POVS_VPORT_ENTRY)switchContext->internalVport; /* XXX: make sure comp list was not a stack variable previously. */ OvsCompletionList *completionList = (OvsCompletionList *)compList; /* * XXX: can internal port disappear while we are busy doing ARP resolution? * It could, but will we get this callback from IP helper in that case. Need * to check. */ ASSERT(switchContext->internalVport); status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, internalVport->portNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl), completionList, NULL, TRUE); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBLForwardingCtx(&ovsFwdCtx, L"OVS-Dropped due to resources"); return; } ASSERT(FALSE); /* * XXX: We need to acquire the dispatch lock and the datapath lock. */ OvsDoFlowLookupOutput(&ovsFwdCtx); } /* * -------------------------------------------------------------------------- * OvsOutputBeforeSetAction -- * Function to be called to complete one set of actions on an NBL, before * we start the next one. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsOutputBeforeSetAction(OvsForwardingContext *ovsFwdCtx) { PNET_BUFFER_LIST newNbl; NDIS_STATUS status = NDIS_STATUS_SUCCESS; /* * Create a copy and work on the copy after this point. The original NBL is * forwarded. One reason to not use the copy for forwarding is that * ports have already been added to the original NBL, and it might be * inefficient/impossible to remove/re-add them to the copy. There's no * notion of removing the ports, the ports need to be marked as * "isExcluded". There's seems no real advantage to retaining the original * and sending out the copy instead. * * XXX: We are copying the offload context here. This is to handle actions * such as: * outport, pop_vlan(), outport, push_vlan(), outport * * copy size needs to include inner ether + IP + TCP, need to revisit * if we support IP options. * XXX Head room needs to include the additional encap. * XXX copySize check is not considering multiple NBs. */ newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, 0, 0, TRUE /*copy NBL info*/); ASSERT(ovsFwdCtx->destPortsSizeOut > 0 || ovsFwdCtx->tunnelTxNic != NULL || ovsFwdCtx->tunnelRxNic != NULL); /* Send the original packet out and save the original source port number */ UINT32 tempVportNo = ovsFwdCtx->srcVportNo; status = OvsOutputForwardingCtx(ovsFwdCtx); ASSERT(ovsFwdCtx->curNbl == NULL); ASSERT(ovsFwdCtx->destPortsSizeOut == 0); ASSERT(ovsFwdCtx->tunnelRxNic == NULL); ASSERT(ovsFwdCtx->tunnelTxNic == NULL); /* If we didn't make a copy, can't continue. */ if (newNbl == NULL) { ovsActionStats.noCopiedNbl++; return NDIS_STATUS_RESOURCES; } /* Finish the remaining actions with the new NBL */ if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE); } else { status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl, tempVportNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), ovsFwdCtx->completionList, &ovsFwdCtx->layers, FALSE); } return status; } /* * -------------------------------------------------------------------------- * OvsPopVlanInPktBuf -- * Function to pop a VLAN tag when the tag is in the packet buffer. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsPopVlanInPktBuf(OvsForwardingContext *ovsFwdCtx) { PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; ULONG dataLength = sizeof (DL_EUI48) + sizeof (DL_EUI48); UINT32 packetLen, mdlLen; PNET_BUFFER_LIST newNbl; NDIS_STATUS status; /* * Declare a dummy vlanTag structure since we need to compute the size * of shiftLength. The NDIS one is a unionized structure. */ NDIS_PACKET_8021Q_INFO vlanTag = {0}; ULONG shiftLength = sizeof (vlanTag.TagHeader); PUINT8 tempBuffer[sizeof (DL_EUI48) + sizeof (DL_EUI48)]; newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, 0, 0, TRUE /* copy NBL info */); if (!newNbl) { ovsActionStats.noCopiedNbl++; return NDIS_STATUS_RESOURCES; } /* Complete the original NBL and create a copy to modify. */ OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to copy"); status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl, ovsFwdCtx->srcVportNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), NULL, &ovsFwdCtx->layers, FALSE); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"Dropped due to resouces"); return NDIS_STATUS_RESOURCES; } curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); packetLen = NET_BUFFER_DATA_LENGTH(curNb); ASSERT(curNb->Next == NULL); curMdl = NET_BUFFER_CURRENT_MDL(curNb); NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); if (!bufferStart) { return NDIS_STATUS_RESOURCES; } mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb); /* Bail out if L2 + VLAN header is not contiguous in the first buffer. */ if (MIN(packetLen, mdlLen) < sizeof (EthHdr) + shiftLength) { ASSERT(FALSE); return NDIS_STATUS_FAILURE; } bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); RtlCopyMemory(tempBuffer, bufferStart, dataLength); RtlCopyMemory(bufferStart + shiftLength, tempBuffer, dataLength); NdisAdvanceNetBufferDataStart(curNb, shiftLength, FALSE, NULL); return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsTunnelAttrToIPv4TunnelKey -- * Convert tunnel attribute to OvsIPv4TunnelKey. * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsTunnelAttrToIPv4TunnelKey(PNL_ATTR attr, OvsIPv4TunnelKey *tunKey) { PNL_ATTR a; INT rem; tunKey->attr[0] = 0; tunKey->attr[1] = 0; tunKey->attr[2] = 0; ASSERT(NlAttrType(attr) == OVS_KEY_ATTR_TUNNEL); NL_ATTR_FOR_EACH_UNSAFE (a, rem, NlAttrData(attr), NlAttrGetSize(attr)) { switch (NlAttrType(a)) { case OVS_TUNNEL_KEY_ATTR_ID: tunKey->tunnelId = NlAttrGetBe64(a); tunKey->flags |= OVS_TNL_F_KEY; break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: tunKey->src = NlAttrGetBe32(a); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: tunKey->dst = NlAttrGetBe32(a); break; case OVS_TUNNEL_KEY_ATTR_TOS: tunKey->tos = NlAttrGetU8(a); break; case OVS_TUNNEL_KEY_ATTR_TTL: tunKey->ttl = NlAttrGetU8(a); break; case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: tunKey->flags |= OVS_TNL_F_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: tunKey->flags |= OVS_TNL_F_CSUM; break; default: ASSERT(0); } } return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsUpdateEthHeader -- * Updates the ethernet header in ovsFwdCtx.curNbl inline based on the * specified key. *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsUpdateEthHeader(OvsForwardingContext *ovsFwdCtx, const struct ovs_key_ethernet *ethAttr) { PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; EthHdr *ethHdr; UINT32 packetLen, mdlLen; curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); ASSERT(curNb->Next == NULL); packetLen = NET_BUFFER_DATA_LENGTH(curNb); curMdl = NET_BUFFER_CURRENT_MDL(curNb); NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); if (!bufferStart) { ovsActionStats.noResource++; return NDIS_STATUS_RESOURCES; } mdlLen -= NET_BUFFER_CURRENT_MDL_OFFSET(curNb); ASSERT(mdlLen > 0); /* Bail out if the L2 header is not in a contiguous buffer. */ if (MIN(packetLen, mdlLen) < sizeof *ethHdr) { ASSERT(FALSE); return NDIS_STATUS_FAILURE; } ethHdr = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(curNb)); RtlCopyMemory(ethHdr->Destination, ethAttr->eth_dst, sizeof ethHdr->Destination); RtlCopyMemory(ethHdr->Source, ethAttr->eth_src, sizeof ethHdr->Source); return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsUpdateIPv4Header -- * Updates the IPv4 header in ovsFwdCtx.curNbl inline based on the * specified key. *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsUpdateIPv4Header(OvsForwardingContext *ovsFwdCtx, const struct ovs_key_ipv4 *ipAttr) { PNET_BUFFER curNb; PMDL curMdl; ULONG curMdlOffset; PUINT8 bufferStart; UINT32 mdlLen, hdrSize, packetLen; OVS_PACKET_HDR_INFO *layers = &ovsFwdCtx->layers; NDIS_STATUS status; IPHdr *ipHdr; TCPHdr *tcpHdr = NULL; UDPHdr *udpHdr = NULL; ASSERT(layers->value != 0); /* * Peek into the MDL to get a handle to the IP header and if required * the TCP/UDP header as well. We check if the required headers are in one * contiguous MDL, and if not, we copy them over to one MDL. */ curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); ASSERT(curNb->Next == NULL); packetLen = NET_BUFFER_DATA_LENGTH(curNb); curMdl = NET_BUFFER_CURRENT_MDL(curNb); NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); if (!bufferStart) { ovsActionStats.noResource++; return NDIS_STATUS_RESOURCES; } curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb); mdlLen -= curMdlOffset; ASSERT((INT)mdlLen >= 0); if (layers->isTcp || layers->isUdp) { hdrSize = layers->l4Offset + layers->isTcp ? sizeof (*tcpHdr) : sizeof (*udpHdr); } else { hdrSize = layers->l3Offset + sizeof (*ipHdr); } /* Count of number of bytes of valid data there are in the first MDL. */ mdlLen = MIN(packetLen, mdlLen); if (mdlLen < hdrSize) { PNET_BUFFER_LIST newNbl; newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl, hdrSize, 0, TRUE /*copy NBL info*/); if (!newNbl) { ovsActionStats.noCopiedNbl++; return NDIS_STATUS_RESOURCES; } OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"Complete after partial copy."); status = OvsInitForwardingCtx(ovsFwdCtx, ovsFwdCtx->switchContext, newNbl, ovsFwdCtx->srcVportNo, 0, NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl), NULL, &ovsFwdCtx->layers, FALSE); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBLForwardingCtx(ovsFwdCtx, L"OVS-Dropped due to resources"); return NDIS_STATUS_RESOURCES; } curNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl); ASSERT(curNb->Next == NULL); curMdl = NET_BUFFER_CURRENT_MDL(curNb); NdisQueryMdl(curMdl, &bufferStart, &mdlLen, LowPagePriority); if (!curMdl) { ovsActionStats.noResource++; return NDIS_STATUS_RESOURCES; } curMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(curNb); mdlLen -= curMdlOffset; ASSERT(mdlLen >= hdrSize); } ipHdr = (IPHdr *)(bufferStart + curMdlOffset + layers->l3Offset); if (layers->isTcp) { tcpHdr = (TCPHdr *)(bufferStart + curMdlOffset + layers->l4Offset); } else if (layers->isUdp) { udpHdr = (UDPHdr *)(bufferStart + curMdlOffset + layers->l4Offset); } /* * Adjust the IP header inline as dictated by the action, nad also update * the IP and the TCP checksum for the data modified. * * In the future, this could be optimized to make one call to * ChecksumUpdate32(). Ignoring this for now, since for the most common * case, we only update the TTL. */ if (ipHdr->saddr != ipAttr->ipv4_src) { if (tcpHdr) { tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->saddr, ipAttr->ipv4_src); } else if (udpHdr && udpHdr->check) { udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->saddr, ipAttr->ipv4_src); } if (ipHdr->check != 0) { ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->saddr, ipAttr->ipv4_src); } ipHdr->saddr = ipAttr->ipv4_src; } if (ipHdr->daddr != ipAttr->ipv4_dst) { if (tcpHdr) { tcpHdr->check = ChecksumUpdate32(tcpHdr->check, ipHdr->daddr, ipAttr->ipv4_dst); } else if (udpHdr && udpHdr->check) { udpHdr->check = ChecksumUpdate32(udpHdr->check, ipHdr->daddr, ipAttr->ipv4_dst); } if (ipHdr->check != 0) { ipHdr->check = ChecksumUpdate32(ipHdr->check, ipHdr->daddr, ipAttr->ipv4_dst); } ipHdr->daddr = ipAttr->ipv4_dst; } if (ipHdr->protocol != ipAttr->ipv4_proto) { UINT16 oldProto = (ipHdr->protocol << 16) & 0xff00; UINT16 newProto = (ipAttr->ipv4_proto << 16) & 0xff00; if (tcpHdr) { tcpHdr->check = ChecksumUpdate16(tcpHdr->check, oldProto, newProto); } else if (udpHdr && udpHdr->check) { udpHdr->check = ChecksumUpdate16(udpHdr->check, oldProto, newProto); } if (ipHdr->check != 0) { ipHdr->check = ChecksumUpdate16(ipHdr->check, oldProto, newProto); } ipHdr->protocol = ipAttr->ipv4_proto; } if (ipHdr->ttl != ipAttr->ipv4_ttl) { UINT16 oldTtl = (ipHdr->ttl) & 0xff; UINT16 newTtl = (ipAttr->ipv4_ttl) & 0xff; if (ipHdr->check != 0) { ipHdr->check = ChecksumUpdate16(ipHdr->check, oldTtl, newTtl); } ipHdr->ttl = ipAttr->ipv4_ttl; } return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsExecuteSetAction -- * Executes a set() action, but storing the actions into 'ovsFwdCtx' * -------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx, OvsFlowKey *key, UINT64 *hash, const PNL_ATTR a) { enum ovs_key_attr type = NlAttrType(a); NDIS_STATUS status = NDIS_STATUS_SUCCESS; switch (type) { case OVS_KEY_ATTR_ETHERNET: status = OvsUpdateEthHeader(ovsFwdCtx, NlAttrGetUnspec(a, sizeof(struct ovs_key_ethernet))); break; case OVS_KEY_ATTR_IPV4: status = OvsUpdateIPv4Header(ovsFwdCtx, NlAttrGetUnspec(a, sizeof(struct ovs_key_ipv4))); break; case OVS_KEY_ATTR_TUNNEL: { OvsIPv4TunnelKey tunKey; status = OvsTunnelAttrToIPv4TunnelKey((PNL_ATTR)a, &tunKey); ASSERT(status == NDIS_STATUS_SUCCESS); tunKey.flow_hash = (uint16)(hash ? *hash : OvsHashFlow(key)); tunKey.dst_port = key->ipKey.l4.tpDst; RtlCopyMemory(&ovsFwdCtx->tunKey, &tunKey, sizeof ovsFwdCtx->tunKey); break; } case OVS_KEY_ATTR_SKB_MARK: /* XXX: Not relevant to Hyper-V. Return OK */ break; case OVS_KEY_ATTR_UNSPEC: case OVS_KEY_ATTR_ENCAP: case OVS_KEY_ATTR_ETHERTYPE: case OVS_KEY_ATTR_IN_PORT: case OVS_KEY_ATTR_VLAN: case OVS_KEY_ATTR_ICMP: case OVS_KEY_ATTR_ICMPV6: case OVS_KEY_ATTR_ARP: case OVS_KEY_ATTR_ND: case __OVS_KEY_ATTR_MAX: default: OVS_LOG_INFO("Unhandled attribute %#x", type); ASSERT(FALSE); } return status; } /* * -------------------------------------------------------------------------- * OvsActionsExecute -- * Interpret and execute the specified 'actions' on the specifed packet * 'curNbl'. The expectation is that if the packet needs to be dropped * (completed) for some reason, it is added to 'completionList' so that the * caller can complete the packet. If 'completionList' is NULL, the NBL is * assumed to be generated by OVS and freed up. Otherwise, the function * consumes the NBL by generating a NDIS send indication for the packet. * * There are one or more of "clone" NBLs that may get generated while * executing the actions. Upon any failures, the "cloned" NBLs are freed up, * and the caller does not have to worry about them. * * Success or failure is returned based on whether the specified actions * were executed successfully on the packet or not. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext, OvsCompletionList *completionList, PNET_BUFFER_LIST curNbl, UINT32 portNo, ULONG sendFlags, OvsFlowKey *key, UINT64 *hash, OVS_PACKET_HDR_INFO *layers, const PNL_ATTR actions, INT actionsLen) { PNL_ATTR a; INT rem; UINT32 dstPortID; OvsForwardingContext ovsFwdCtx; PCWSTR dropReason = L""; NDIS_STATUS status; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl); /* XXX: ASSERT that the flow table lock is held. */ status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo, sendFlags, fwdDetail, completionList, layers, TRUE); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-initing destination port list failed"; goto dropit; } if (actionsLen == 0) { dropReason = L"OVS-Dropped due to Flow action"; ovsActionStats.zeroActionLen++; goto dropit; } NL_ATTR_FOR_EACH_UNSAFE (a, rem, actions, actionsLen) { switch(NlAttrType(a)) { case OVS_ACTION_ATTR_OUTPUT: dstPortID = NlAttrGetU32(a); status = OvsAddPorts(&ovsFwdCtx, key, dstPortID, TRUE, TRUE); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-adding destination port failed"; goto dropit; } break; case OVS_ACTION_ATTR_PUSH_VLAN: { struct ovs_action_push_vlan *vlan; PVOID vlanTagValue; PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag; if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL || ovsFwdCtx.tunnelRxNic != NULL) { status = OvsOutputBeforeSetAction(&ovsFwdCtx); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-adding destination failed"; goto dropit; } } vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl, Ieee8021QNetBufferListInfo); if (vlanTagValue != NULL) { /* * XXX: We don't support double VLAN tag offload. In such cases, * we need to insert the existing one into the packet buffer, * and add the new one as offload. This will take care of * guest tag-in-tag case as well as OVS rules that specify * tag-in-tag. */ } else { vlanTagValue = 0; vlanTag = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue; vlan = (struct ovs_action_push_vlan *)NlAttrGet((const PNL_ATTR)a); vlanTag->TagHeader.VlanId = ntohs(vlan->vlan_tci) & 0xfff; vlanTag->TagHeader.UserPriority = ntohs(vlan->vlan_tci) >> 13; NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl, Ieee8021QNetBufferListInfo) = vlanTagValue; } break; } case OVS_ACTION_ATTR_POP_VLAN: { if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL || ovsFwdCtx.tunnelRxNic != NULL) { status = OvsOutputBeforeSetAction(&ovsFwdCtx); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-adding destination failed"; goto dropit; } } if (NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl, Ieee8021QNetBufferListInfo) != 0) { NET_BUFFER_LIST_INFO(ovsFwdCtx.curNbl, Ieee8021QNetBufferListInfo) = 0; } else { /* * The VLAN tag is inserted into the packet buffer. Pop the tag * by packet buffer modification. */ status = OvsPopVlanInPktBuf(&ovsFwdCtx); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-pop vlan action failed"; goto dropit; } } break; } case OVS_ACTION_ATTR_USERSPACE: { PNL_ATTR userdataAttr; PNL_ATTR queueAttr; POVS_PACKET_QUEUE_ELEM elem; BOOLEAN isRecv = FALSE; POVS_VPORT_ENTRY vport = OvsFindVportByPortNo(switchContext, portNo); if (vport) { if (vport->isExternal || OvsIsTunnelVportType(vport->ovsType)) { isRecv = TRUE; } } queueAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_PID); userdataAttr = NlAttrFindNested(a, OVS_USERSPACE_ATTR_USERDATA); elem = OvsCreateQueueNlPacket((PVOID)userdataAttr, userdataAttr->nlaLen, OVS_PACKET_CMD_ACTION, vport, key, ovsFwdCtx.curNbl, NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx.curNbl), isRecv, layers); if (elem) { LIST_ENTRY missedPackets; InitializeListHead(&missedPackets); InsertTailList(&missedPackets, &elem->link); OvsQueuePackets(&missedPackets, 1); dropReason = L"OVS-Completed since packet was copied to " L"userspace"; } else { dropReason = L"OVS-Dropped due to failure to queue to " L"userspace"; goto dropit; } break; } case OVS_ACTION_ATTR_SET: { if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL || ovsFwdCtx.tunnelRxNic != NULL) { status = OvsOutputBeforeSetAction(&ovsFwdCtx); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-adding destination failed"; goto dropit; } } status = OvsExecuteSetAction(&ovsFwdCtx, key, hash, (const PNL_ATTR)NlAttrGet ((const PNL_ATTR)a)); if (status != NDIS_STATUS_SUCCESS) { dropReason = L"OVS-set action failed"; goto dropit; } break; } case OVS_ACTION_ATTR_SAMPLE: default: status = NDIS_STATUS_NOT_SUPPORTED; break; } } if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL || ovsFwdCtx.tunnelRxNic != NULL) { status = OvsOutputForwardingCtx(&ovsFwdCtx); ASSERT(ovsFwdCtx.curNbl == NULL); } ASSERT(ovsFwdCtx.destPortsSizeOut == 0); ASSERT(ovsFwdCtx.tunnelRxNic == NULL); ASSERT(ovsFwdCtx.tunnelTxNic == NULL); dropit: /* * If curNbl != NULL, it implies the NBL has not been not freed up so far. */ if (ovsFwdCtx.curNbl) { OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason); } return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/User.c0000644000000000000000000000013213534540071021534 xustar0030 mtime=1567801401.229679907 30 atime=1567801402.049685929 30 ctime=1567801424.457851038 openvswitch-2.5.9/datapath-windows/ovsext/User.c0000644000175000017500000011625213534540071023231 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * OvsUser.c * Manage packet queue for packet miss for userAction. */ #include "precomp.h" #include "Switch.h" #include "Vport.h" #include "Event.h" #include "User.h" #include "Datapath.h" #include "PacketIO.h" #include "Checksum.h" #include "NetProto.h" #include "Flow.h" #include "TunnelIntf.h" #include "Jhash.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_USER #include "Debug.h" POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance); extern PNDIS_SPIN_LOCK gOvsCtrlLock; extern POVS_SWITCH_CONTEXT gOvsSwitchContext; OVS_USER_STATS ovsUserStats; static VOID _MapNlAttrToOvsPktExec(PNL_ATTR *nlAttrs, PNL_ATTR *keyAttrs, OvsPacketExecute *execute); extern NL_POLICY nlFlowKeyPolicy[]; extern UINT32 nlFlowKeyPolicyLen; static __inline VOID OvsAcquirePidHashLock() { NdisAcquireSpinLock(&(gOvsSwitchContext->pidHashLock)); } static __inline VOID OvsReleasePidHashLock() { NdisReleaseSpinLock(&(gOvsSwitchContext->pidHashLock)); } static VOID OvsPurgePacketQueue(POVS_USER_PACKET_QUEUE queue, POVS_OPEN_INSTANCE instance) { PLIST_ENTRY link, next; LIST_ENTRY tmp; POVS_PACKET_QUEUE_ELEM elem; InitializeListHead(&tmp); NdisAcquireSpinLock(&queue->queueLock); if (queue->instance != instance) { NdisReleaseSpinLock(&queue->queueLock); return; } if (queue->numPackets) { OvsAppendList(&tmp, &queue->packetList); queue->numPackets = 0; } NdisReleaseSpinLock(&queue->queueLock); LIST_FORALL_SAFE(&tmp, link, next) { RemoveEntryList(link); elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); } } VOID OvsCleanupPacketQueue(POVS_OPEN_INSTANCE instance) { POVS_USER_PACKET_QUEUE queue; POVS_PACKET_QUEUE_ELEM elem; PLIST_ENTRY link, next; LIST_ENTRY tmp; PIRP irp = NULL; ASSERT(instance); InitializeListHead(&tmp); queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; if (queue) { PDRIVER_CANCEL cancelRoutine; NdisAcquireSpinLock(&queue->queueLock); ASSERT(queue->instance == instance); /* XXX Should not happen */ if (queue->instance != instance) { NdisReleaseSpinLock(&queue->queueLock); NdisFreeSpinLock(&queue->queueLock); return; } if (queue->numPackets) { OvsAppendList(&tmp, &queue->packetList); queue->numPackets = 0; } queue->instance = NULL; instance->packetQueue = NULL; irp = queue->pendingIrp; queue->pendingIrp = NULL; if (irp) { cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine == NULL) { irp = NULL; } } NdisReleaseSpinLock(&queue->queueLock); NdisFreeSpinLock(&queue->queueLock); } LIST_FORALL_SAFE(&tmp, link, next) { RemoveEntryList(link); elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); } if (irp) { OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); } if (queue) { OvsFreeMemoryWithTag(queue, OVS_USER_POOL_TAG); } /* Verify if gOvsSwitchContext exists. */ if (gOvsSwitchContext) { /* Remove the instance from pidHashArray */ OvsAcquirePidHashLock(); OvsDelPidInstance(gOvsSwitchContext, instance->pid); OvsReleasePidHashLock(); } } NTSTATUS OvsSubscribeDpIoctl(PVOID instanceP, UINT32 pid, UINT8 join) { POVS_USER_PACKET_QUEUE queue; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)instanceP; if (instance->packetQueue && !join) { /* unsubscribe */ OvsCleanupPacketQueue(instance); } else if (instance->packetQueue == NULL && join) { queue = (POVS_USER_PACKET_QUEUE) OvsAllocateMemoryWithTag( sizeof *queue, OVS_USER_POOL_TAG); if (queue == NULL) { return STATUS_NO_MEMORY; } InitializeListHead(&(instance->pidLink)); instance->packetQueue = queue; RtlZeroMemory(queue, sizeof (*queue)); NdisAllocateSpinLock(&queue->queueLock); NdisAcquireSpinLock(&queue->queueLock); InitializeListHead(&queue->packetList); queue->pid = pid; queue->instance = instance; instance->packetQueue = queue; NdisReleaseSpinLock(&queue->queueLock); OvsAcquirePidHashLock(); /* Insert the instance to pidHashArray */ OvsAddPidInstance(gOvsSwitchContext, pid, instance); OvsReleasePidHashLock(); } else { /* user mode should call only once for subscribe */ return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } NTSTATUS OvsReadDpIoctl(PFILE_OBJECT fileObject, PVOID outputBuffer, UINT32 outputLength, UINT32 *replyLen) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; POVS_PACKET_QUEUE_ELEM elem; UINT32 len; #define TCP_CSUM_OFFSET 16 #define UDP_CSUM_OFFSET 6 ASSERT(instance); if (instance->packetQueue == NULL) { return STATUS_INVALID_PARAMETER; } if (outputLength < (sizeof (OVS_PACKET_INFO) + OVS_MIN_PACKET_SIZE)) { return STATUS_BUFFER_TOO_SMALL; } elem = OvsGetNextPacket(instance); if (elem) { /* * XXX revisit this later */ len = elem->packet.totalLen > outputLength ? outputLength : elem->packet.totalLen; if ((elem->hdrInfo.tcpCsumNeeded || elem->hdrInfo.udpCsumNeeded) && len == elem->packet.totalLen) { UINT16 sum, *ptr; UINT16 size = (UINT16)(elem->packet.payload - elem->packet.data + elem->hdrInfo.l4Offset); RtlCopyMemory(outputBuffer, &elem->packet.data, size); ASSERT(len - size >= elem->hdrInfo.l4PayLoad); sum = CopyAndCalculateChecksum((UINT8 *)outputBuffer + size, (UINT8 *)&elem->packet.data + size, elem->hdrInfo.l4PayLoad, 0); ptr =(UINT16 *)((UINT8 *)outputBuffer + size + (elem->hdrInfo.tcpCsumNeeded ? TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); *ptr = sum; ovsUserStats.l4Csum++; } else { RtlCopyMemory(outputBuffer, &elem->packet.data, len); } *replyLen = len; OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); } return STATUS_SUCCESS; } /* Helper function to allocate a Forwarding Context for an NBL */ NTSTATUS OvsAllocateForwardingContextForNBL(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST nbl) { return switchContext->NdisSwitchHandlers. AllocateNetBufferListForwardingContext( switchContext->NdisSwitchContext, nbl); } /* *---------------------------------------------------------------------------- * OvsNlExecuteCmdHandler -- * Handler for OVS_PACKET_CMD_EXECUTE command. *---------------------------------------------------------------------------- */ NTSTATUS OvsNlExecuteCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS status = STATUS_SUCCESS; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg); PGENL_MSG_HDR genlMsgHdr = &(msgIn->genlMsg); POVS_HDR ovsHdr = &(msgIn->ovsHdr); PNL_ATTR nlAttrs[__OVS_PACKET_ATTR_MAX]; PNL_ATTR keyAttrs[__OVS_KEY_ATTR_MAX] = {NULL}; UINT32 attrOffset = NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN; UINT32 keyAttrOffset = 0; OvsPacketExecute execute; NL_ERROR nlError = NL_ERROR_SUCCESS; NL_BUFFER nlBuf; static const NL_POLICY nlPktExecPolicy[] = { [OVS_PACKET_ATTR_PACKET] = {.type = NL_A_UNSPEC, .optional = FALSE}, [OVS_PACKET_ATTR_KEY] = {.type = NL_A_UNSPEC, .optional = FALSE}, [OVS_PACKET_ATTR_ACTIONS] = {.type = NL_A_UNSPEC, .optional = FALSE}, [OVS_PACKET_ATTR_USERDATA] = {.type = NL_A_UNSPEC, .optional = TRUE}, [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = {.type = NL_A_UNSPEC, .optional = TRUE} }; RtlZeroMemory(&execute, sizeof(OvsPacketExecute)); /* Get all the top level Flow attributes */ if ((NlAttrParse(nlMsgHdr, attrOffset, NlMsgAttrsLen(nlMsgHdr), nlPktExecPolicy, ARRAY_SIZE(nlPktExecPolicy), nlAttrs, ARRAY_SIZE(nlAttrs))) != TRUE) { OVS_LOG_ERROR("Attr Parsing failed for msg: %p", nlMsgHdr); status = STATUS_UNSUCCESSFUL; goto done; } keyAttrOffset = (UINT32)((PCHAR)nlAttrs[OVS_PACKET_ATTR_KEY] - (PCHAR)nlMsgHdr); /* Get flow keys attributes */ if ((NlAttrParseNested(nlMsgHdr, keyAttrOffset, NlAttrLen(nlAttrs[OVS_PACKET_ATTR_KEY]), nlFlowKeyPolicy, nlFlowKeyPolicyLen, keyAttrs, ARRAY_SIZE(keyAttrs))) != TRUE) { OVS_LOG_ERROR("Key Attr Parsing failed for msg: %p", nlMsgHdr); status = STATUS_UNSUCCESSFUL; goto done; } execute.dpNo = ovsHdr->dp_ifindex; _MapNlAttrToOvsPktExec(nlAttrs, keyAttrs, &execute); status = OvsExecuteDpIoctl(&execute); /* Default reply that we want to send */ if (status == STATUS_SUCCESS) { BOOLEAN ok; NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); /* Prepare nl Msg headers */ ok = NlFillOvsMsg(&nlBuf, nlMsgHdr->nlmsgType, 0, nlMsgHdr->nlmsgSeq, nlMsgHdr->nlmsgPid, genlMsgHdr->cmd, OVS_PACKET_VERSION, ovsHdr->dp_ifindex); if (ok) { *replyLen = msgOut->nlMsg.nlmsgLen; } else { status = STATUS_INVALID_BUFFER_SIZE; } } else { /* Map NTSTATUS to NL_ERROR */ nlError = NlMapStatusToNlErr(status); /* As of now there are no transactional errors in the implementation. * Once we have them then we need to map status to correct * nlError value, so that below mentioned code gets hit. */ if ((nlError != NL_ERROR_SUCCESS) && (usrParamsCtx->outputBuffer)) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; status = STATUS_SUCCESS; goto done; } } done: return status; } /* *---------------------------------------------------------------------------- * _MapNlAttrToOvsPktExec -- * Maps input Netlink attributes to OvsPacketExecute. *---------------------------------------------------------------------------- */ static VOID _MapNlAttrToOvsPktExec(PNL_ATTR *nlAttrs, PNL_ATTR *keyAttrs, OvsPacketExecute *execute) { execute->packetBuf = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_PACKET]); execute->packetLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_PACKET]); execute->actions = NlAttrGet(nlAttrs[OVS_PACKET_ATTR_ACTIONS]); execute->actionsLen = NlAttrGetSize(nlAttrs[OVS_PACKET_ATTR_ACTIONS]); execute->inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]); } NTSTATUS OvsExecuteDpIoctl(OvsPacketExecute *execute) { NTSTATUS status = STATUS_SUCCESS; NTSTATUS ndisStatus; LOCK_STATE_EX lockState; PNET_BUFFER_LIST pNbl; PNL_ATTR actions; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; OvsFlowKey key; OVS_PACKET_HDR_INFO layers; POVS_VPORT_ENTRY vport; if (execute->packetLen == 0) { status = STATUS_INVALID_PARAMETER; goto exit; } actions = execute->actions; ASSERT(actions); /* * Allocate the NBL, copy the data from the userspace buffer. Allocate * also, the forwarding context for the packet. */ pNbl = OvsAllocateNBLFromBuffer(gOvsSwitchContext, execute->packetBuf, execute->packetLen); if (pNbl == NULL) { status = STATUS_NO_MEMORY; goto exit; } fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl); vport = OvsFindVportByPortNo(gOvsSwitchContext, execute->inPort); if (vport) { fwdDetail->SourcePortId = vport->portId; fwdDetail->SourceNicIndex = vport->nicIndex; } else { fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; fwdDetail->SourceNicIndex = 0; } // XXX: Figure out if any of the other members of fwdDetail need to be set. ndisStatus = OvsExtractFlow(pNbl, fwdDetail->SourcePortId, &key, &layers, NULL); if (ndisStatus == NDIS_STATUS_SUCCESS) { NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 0); ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl, vport ? vport->portNo : OVS_DPPORT_NUMBER_INVALID, NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP, &key, NULL, &layers, actions, execute->actionsLen); pNbl = NULL; NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); } if (ndisStatus != NDIS_STATUS_SUCCESS) { if (ndisStatus == NDIS_STATUS_NOT_SUPPORTED) { status = STATUS_NOT_SUPPORTED; } else { status = STATUS_UNSUCCESSFUL; } } if (pNbl) { OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE); } exit: return status; } NTSTATUS OvsPurgeDpIoctl(PFILE_OBJECT fileObject) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; POVS_USER_PACKET_QUEUE queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; if (queue == NULL) { return STATUS_INVALID_PARAMETER; } OvsPurgePacketQueue(queue, instance); return STATUS_SUCCESS; } VOID OvsCancelIrpDatapath(PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; POVS_OPEN_INSTANCE instance; POVS_USER_PACKET_QUEUE queue = NULL; UNREFERENCED_PARAMETER(deviceObject); IoReleaseCancelSpinLock(irp->CancelIrql); irpSp = IoGetCurrentIrpStackLocation(irp); fileObject = irpSp->FileObject; if (fileObject == NULL) { goto done; } NdisAcquireSpinLock(gOvsCtrlLock); instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; if (instance) { queue = instance->packetQueue; } if (instance == NULL || queue == NULL) { NdisReleaseSpinLock(gOvsCtrlLock); goto done; } NdisReleaseSpinLock(gOvsCtrlLock); NdisAcquireSpinLock(&queue->queueLock); if (queue->pendingIrp == irp) { queue->pendingIrp = NULL; } NdisReleaseSpinLock(&queue->queueLock); done: OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); } NTSTATUS OvsWaitDpIoctl(PIRP irp, PFILE_OBJECT fileObject) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; POVS_USER_PACKET_QUEUE queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; NTSTATUS status = STATUS_SUCCESS; BOOLEAN cancelled = FALSE; if (queue == NULL) { return STATUS_INVALID_PARAMETER; } NdisAcquireSpinLock(&queue->queueLock); if (queue->instance != instance) { NdisReleaseSpinLock(&queue->queueLock); return STATUS_INVALID_PARAMETER; } if (queue->pendingIrp) { NdisReleaseSpinLock(&queue->queueLock); return STATUS_DEVICE_BUSY; } if (queue->numPackets == 0) { PDRIVER_CANCEL cancelRoutine; IoMarkIrpPending(irp); IoSetCancelRoutine(irp, OvsCancelIrpDatapath); if (irp->Cancel) { cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine) { cancelled = TRUE; } } else { queue->pendingIrp = irp; } status = STATUS_PENDING; } NdisReleaseSpinLock(&queue->queueLock); if (cancelled) { OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED); OVS_LOG_INFO("Datapath IRP cancelled: %p", irp); } return status; } POVS_PACKET_QUEUE_ELEM OvsGetNextPacket(POVS_OPEN_INSTANCE instance) { POVS_USER_PACKET_QUEUE queue; PLIST_ENTRY link; queue = (POVS_USER_PACKET_QUEUE)instance->packetQueue; if (queue == NULL) { return NULL; } NdisAcquireSpinLock(&queue->queueLock); if (queue->instance != instance || queue->numPackets == 0) { NdisReleaseSpinLock(&queue->queueLock); return NULL; } link = RemoveHeadList(&queue->packetList); queue->numPackets--; NdisReleaseSpinLock(&queue->queueLock); return CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); } /* * --------------------------------------------------------------------------- * Given a pid, returns the corresponding USER_PACKET_QUEUE. * --------------------------------------------------------------------------- */ POVS_USER_PACKET_QUEUE OvsGetQueue(UINT32 pid) { POVS_OPEN_INSTANCE instance; POVS_USER_PACKET_QUEUE ret = NULL; instance = OvsGetPidInstance(gOvsSwitchContext, pid); if (instance) { ret = instance->packetQueue; } return ret; } /* * --------------------------------------------------------------------------- * Given a pid, returns the corresponding instance. * pidHashLock must be acquired before calling this API. * --------------------------------------------------------------------------- */ POVS_OPEN_INSTANCE OvsGetPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid) { POVS_OPEN_INSTANCE instance; PLIST_ENTRY head, link; UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid), OVS_HASH_BASIS); head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]); LIST_FORALL(head, link) { instance = CONTAINING_RECORD(link, OVS_OPEN_INSTANCE, pidLink); if (instance->pid == pid) { return instance; } } return NULL; } /* * --------------------------------------------------------------------------- * Given a pid and an instance. This API adds instance to pidHashArray. * pidHashLock must be acquired before calling this API. * --------------------------------------------------------------------------- */ VOID OvsAddPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid, POVS_OPEN_INSTANCE instance) { PLIST_ENTRY head; UINT32 hash = OvsJhashBytes((const VOID *)&pid, sizeof(pid), OVS_HASH_BASIS); head = &(switchContext->pidHashArray[hash & OVS_PID_MASK]); InsertHeadList(head, &(instance->pidLink)); } /* * --------------------------------------------------------------------------- * Given a pid and an instance. This API removes instance from pidHashArray. * pidHashLock must be acquired before calling this API. * --------------------------------------------------------------------------- */ VOID OvsDelPidInstance(POVS_SWITCH_CONTEXT switchContext, UINT32 pid) { POVS_OPEN_INSTANCE instance = OvsGetPidInstance(switchContext, pid); if (instance) { RemoveEntryList(&(instance->pidLink)); } } VOID OvsQueuePackets(PLIST_ENTRY packetList, UINT32 numElems) { POVS_USER_PACKET_QUEUE upcallQueue = NULL; POVS_PACKET_QUEUE_ELEM elem; PLIST_ENTRY link; UINT32 num = 0; LIST_ENTRY dropPackets; OVS_LOG_LOUD("Enter: numELems: %u", numElems); InitializeListHead(&dropPackets); while (!IsListEmpty(packetList)) { link = RemoveHeadList(packetList); elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); ASSERT(elem); OvsAcquirePidHashLock(); upcallQueue = OvsGetQueue(elem->upcallPid); if (!upcallQueue) { /* No upcall queue found, drop this packet. */ InsertTailList(&dropPackets, &elem->link); } else { NdisAcquireSpinLock(&upcallQueue->queueLock); if (upcallQueue->instance == NULL) { InsertTailList(&dropPackets, &elem->link); } else { InsertTailList(&upcallQueue->packetList, &elem->link); upcallQueue->numPackets++; if (upcallQueue->pendingIrp) { PIRP irp = upcallQueue->pendingIrp; PDRIVER_CANCEL cancelRoutine; upcallQueue->pendingIrp = NULL; cancelRoutine = IoSetCancelRoutine(irp, NULL); if (cancelRoutine != NULL) { OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS); } } } NdisReleaseSpinLock(&upcallQueue->queueLock); } OvsReleasePidHashLock(); } while (!IsListEmpty(&dropPackets)) { link = RemoveHeadList(&dropPackets); elem = CONTAINING_RECORD(link, OVS_PACKET_QUEUE_ELEM, link); OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); num++; } OVS_LOG_LOUD("Exit: drop %u packets", num); } /* *---------------------------------------------------------------------------- * OvsCreateAndAddPackets -- * * Create a packet and forwarded to user space. * * This function would fragment packet if needed, and queue * each segment to user space. *---------------------------------------------------------------------------- */ NTSTATUS OvsCreateAndAddPackets(PVOID userData, UINT32 userDataLen, UINT32 cmd, POVS_VPORT_ENTRY vport, OvsFlowKey *key, PNET_BUFFER_LIST nbl, BOOLEAN isRecv, POVS_PACKET_HDR_INFO hdrInfo, POVS_SWITCH_CONTEXT switchContext, LIST_ENTRY *list, UINT32 *num) { POVS_PACKET_QUEUE_ELEM elem; PNET_BUFFER_LIST newNbl = NULL; PNET_BUFFER nb; if (hdrInfo->isTcp) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; UINT32 packetLength; tsoInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpLargeSendNetBufferListInfo); nb = NET_BUFFER_LIST_FIRST_NB(nbl); packetLength = NET_BUFFER_DATA_LENGTH(nb); OVS_LOG_TRACE("MSS %u packet len %u", tsoInfo.LsoV1Transmit.MSS, packetLength); if (tsoInfo.LsoV1Transmit.MSS) { OVS_LOG_TRACE("l4Offset %d", hdrInfo->l4Offset); newNbl = OvsTcpSegmentNBL(switchContext, nbl, hdrInfo, tsoInfo.LsoV1Transmit.MSS , 0); if (newNbl == NULL) { return NDIS_STATUS_FAILURE; } nbl = newNbl; } } nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { elem = OvsCreateQueueNlPacket(userData, userDataLen, cmd, vport, key, nbl, nb, isRecv, hdrInfo); if (elem) { InsertTailList(list, &elem->link); (*num)++; } nb = NET_BUFFER_NEXT_NB(nb); } if (newNbl) { OvsCompleteNBL(switchContext, newNbl, TRUE); } return NDIS_STATUS_SUCCESS; } static __inline UINT32 OvsGetUpcallMsgSize(PVOID userData, UINT32 userDataLen, OvsIPv4TunnelKey *tunnelKey, UINT32 payload) { UINT32 size = NLMSG_ALIGN(sizeof(struct ovs_header)) + NlAttrSize(payload) + NlAttrSize(OvsFlowKeyAttrSize()); /* OVS_PACKET_ATTR_USERDATA */ if (userData) { size += NlAttrTotalSize(userDataLen); } /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */ /* Is it included in the flow key attr XXX */ if (tunnelKey) { size += NlAttrTotalSize(OvsTunKeyAttrSize()); } return size; } /* *---------------------------------------------------------------------------- * This function completes the IP Header csum. record the L4 payload offset and * if there is a need to calculate the TCP or UDP csum. The actual csum will be * caluculated simopultaneossly with the copy of the payload to the destination * buffer when the packet is read. *---------------------------------------------------------------------------- */ static VOID OvsCompletePacketHeader(UINT8 *packet, BOOLEAN isRecv, NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo, POVS_PACKET_HDR_INFO hdrInfoIn, POVS_PACKET_HDR_INFO hdrInfoOut) { if ((isRecv && csumInfo.Receive.IpChecksumValueInvalid) || (!isRecv && csumInfo.Transmit.IsIPv4 && csumInfo.Transmit.IpHeaderChecksum)) { PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoOut->l3Offset); ASSERT(hdrInfoIn->isIPv4); ASSERT(ipHdr->Version == 4); ipHdr->HeaderChecksum = IPChecksum((UINT8 *)ipHdr, ipHdr->HeaderLength << 2, (UINT16)~ipHdr->HeaderChecksum); ovsUserStats.ipCsum++; } ASSERT(hdrInfoIn->tcpCsumNeeded == 0 && hdrInfoOut->udpCsumNeeded == 0); /* * calculate TCP/UDP pseudo checksum */ if (isRecv && csumInfo.Receive.TcpChecksumValueInvalid) { /* * Only this case, we need to reclaculate pseudo checksum * all other cases, it is assumed the pseudo checksum is * filled already. * */ PTCP_HDR tcpHdr = (PTCP_HDR)(packet + hdrInfoIn->l4Offset); if (hdrInfoIn->isIPv4) { PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoIn->l3Offset); hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - (ipHdr->HeaderLength << 2)); tcpHdr->th_sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, (UINT32 *)&ipHdr->DestinationAddress, IPPROTO_TCP, hdrInfoOut->l4PayLoad); } else { PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet + hdrInfoIn->l3Offset); hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipv6Hdr->PayloadLength) + hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)- hdrInfoIn->l4Offset); ASSERT(hdrInfoIn->isIPv6); tcpHdr->th_sum = IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, (UINT32 *)&ipv6Hdr->DestinationAddress, IPPROTO_TCP, hdrInfoOut->l4PayLoad); } hdrInfoOut->tcpCsumNeeded = 1; ovsUserStats.recalTcpCsum++; } else if (!isRecv) { if (csumInfo.Transmit.TcpChecksum) { hdrInfoOut->tcpCsumNeeded = 1; } else if (csumInfo.Transmit.UdpChecksum) { hdrInfoOut->udpCsumNeeded = 1; } if (hdrInfoOut->tcpCsumNeeded || hdrInfoOut->udpCsumNeeded) { #ifdef DBG UINT16 sum, *ptr; UINT8 proto = hdrInfoOut->tcpCsumNeeded ? IPPROTO_TCP : IPPROTO_UDP; #endif if (hdrInfoIn->isIPv4) { PIPV4_HEADER ipHdr = (PIPV4_HEADER)(packet + hdrInfoIn->l3Offset); hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipHdr->TotalLength) - (ipHdr->HeaderLength << 2)); #ifdef DBG sum = IPPseudoChecksum((UINT32 *)&ipHdr->SourceAddress, (UINT32 *)&ipHdr->DestinationAddress, proto, hdrInfoOut->l4PayLoad); #endif } else { PIPV6_HEADER ipv6Hdr = (PIPV6_HEADER)(packet + hdrInfoIn->l3Offset); hdrInfoOut->l4PayLoad = (UINT16)(ntohs(ipv6Hdr->PayloadLength) + hdrInfoIn->l3Offset + sizeof(IPV6_HEADER)- hdrInfoIn->l4Offset); ASSERT(hdrInfoIn->isIPv6); #ifdef DBG sum = IPv6PseudoChecksum((UINT32 *)&ipv6Hdr->SourceAddress, (UINT32 *)&ipv6Hdr->DestinationAddress, proto, hdrInfoOut->l4PayLoad); #endif } #ifdef DBG ptr = (UINT16 *)(packet + hdrInfoIn->l4Offset + (hdrInfoOut->tcpCsumNeeded ? TCP_CSUM_OFFSET : UDP_CSUM_OFFSET)); ASSERT(*ptr == sum); #endif } } } static NTSTATUS OvsGetPid(POVS_VPORT_ENTRY vport, PNET_BUFFER nb, UINT32 *pid) { UNREFERENCED_PARAMETER(nb); ASSERT(vport); /* XXX select a pid from an array of pids using a flow based hash */ *pid = vport->upcallPid; return STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsCreateQueueNlPacket -- * * Create a packet which will be forwarded to user space. * * InputParameter: * userData: when cmd is user action, this field contain * user action data. * userDataLen: as name indicated * cmd: either miss or user action * inPort: datapath port id from which the packet is received. * key: flow Key with a tunnel key if available * nbl: the NET_BUFFER_LIST which contain the packet * nb: the packet * isRecv: This is used to decide how to interprete the csum info * hdrInfo: include hdr info initialized during flow extraction. * * Results: * NULL if fail to create the packet * The packet element otherwise *---------------------------------------------------------------------------- */ POVS_PACKET_QUEUE_ELEM OvsCreateQueueNlPacket(PVOID userData, UINT32 userDataLen, UINT32 cmd, POVS_VPORT_ENTRY vport, OvsFlowKey *key, PNET_BUFFER_LIST nbl, PNET_BUFFER nb, BOOLEAN isRecv, POVS_PACKET_HDR_INFO hdrInfo) { #define VLAN_TAG_SIZE 4 UINT32 allocLen, dataLen, extraLen; POVS_PACKET_QUEUE_ELEM elem; UINT8 *src, *dst; NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; NDIS_NET_BUFFER_LIST_8021Q_INFO vlanInfo; OvsIPv4TunnelKey *tunnelKey = (OvsIPv4TunnelKey *)&key->tunKey; UINT32 pid; UINT32 nlMsgSize; NL_BUFFER nlBuf; PNL_MSG_HDR nlMsg; if (vport == NULL){ /* No vport is not fatal. */ return NULL; } OvsGetPid(vport, nb, &pid); if (!pid) { /* * There is no userspace queue created yet, so there is no point for * creating a new packet to be queued. */ return NULL; } csumInfo.Value = NET_BUFFER_LIST_INFO(nbl, TcpIpChecksumNetBufferListInfo); if (isRecv && (csumInfo.Receive.TcpChecksumFailed || (csumInfo.Receive.UdpChecksumFailed && !hdrInfo->udpCsumZero) || csumInfo.Receive.IpChecksumFailed)) { OVS_LOG_INFO("Packet dropped due to checksum failure."); ovsUserStats.dropDuetoChecksum++; return NULL; } vlanInfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); extraLen = vlanInfo.TagHeader.VlanId ? VLAN_TAG_SIZE : 0; dataLen = NET_BUFFER_DATA_LENGTH(nb); if (NlAttrSize(dataLen) > MAXUINT16) { return NULL; } nlMsgSize = OvsGetUpcallMsgSize(userData, userDataLen, tunnelKey, dataLen + extraLen); allocLen = sizeof (OVS_PACKET_QUEUE_ELEM) + nlMsgSize; elem = (POVS_PACKET_QUEUE_ELEM)OvsAllocateMemoryWithTag(allocLen, OVS_USER_POOL_TAG); if (elem == NULL) { ovsUserStats.dropDuetoResource++; return NULL; } elem->hdrInfo.value = hdrInfo->value; elem->upcallPid = pid; elem->packet.totalLen = nlMsgSize; /* XXX remove queueid */ elem->packet.queue = 0; /* XXX no need as the length is already in the NL attrib */ elem->packet.userDataLen = userDataLen; elem->packet.inPort = vport->portNo; elem->packet.cmd = cmd; if (cmd == (UINT32)OVS_PACKET_CMD_MISS) { ovsUserStats.miss++; } else if (cmd == (UINT32)OVS_PACKET_CMD_ACTION) { ovsUserStats.action++; } else { ASSERT(FALSE); goto fail; } /* XXX Should we have both packetLen and TotalLen*/ elem->packet.packetLen = dataLen + extraLen; NlBufInit(&nlBuf, (PCHAR)elem->packet.data, nlMsgSize); /* * Initialize the OVS header * Since we are pre allocating memory for the NL buffer * the attribute settings should not fail */ if (!NlFillOvsMsg(&nlBuf, OVS_WIN_NL_PACKET_FAMILY_ID, 0, 0, pid, (UINT8)cmd, OVS_PACKET_VERSION, gOvsSwitchContext->dpNo)) { goto fail; } if (MapFlowKeyToNlKey(&nlBuf, key, OVS_PACKET_ATTR_KEY, OVS_KEY_ATTR_TUNNEL) != STATUS_SUCCESS) { goto fail; } /* XXX must send OVS_PACKET_ATTR_EGRESS_TUN_KEY if set by vswtchd */ if (userData){ if (!NlMsgPutTailUnspec(&nlBuf, OVS_PACKET_ATTR_USERDATA, userData, (UINT16)userDataLen)) { goto fail; } } /* * Make space for the payload to be copied and set the attribute * XXX Uninit set initilizes the buffer with xero, we don't actually need * that the payload to be initailized */ dst = (UINT8 *)NlMsgPutTailUnspecUninit(&nlBuf, OVS_PACKET_ATTR_PACKET, (UINT16)(dataLen + extraLen)); if (!dst) { goto fail; } /* Store the payload for csum calculation when packet is read */ elem->packet.payload = dst; dst += extraLen; src = NdisGetDataBuffer(nb, dataLen, dst, 1, 0); if (src == NULL) { ovsUserStats.dropDuetoResource++; goto fail; } else if (src != dst) { /* Copy the data from the NDIS buffer to dst. */ RtlCopyMemory(dst, src, dataLen); } /* Set csum if was offloaded */ OvsCompletePacketHeader(dst, isRecv, csumInfo, hdrInfo, &elem->hdrInfo); /* * Finally insert VLAN tag */ if (extraLen) { dst = elem->packet.payload; src = dst + extraLen; ((UINT32 *)dst)[0] = ((UINT32 *)src)[0]; ((UINT32 *)dst)[1] = ((UINT32 *)src)[1]; ((UINT32 *)dst)[2] = ((UINT32 *)src)[2]; dst += 12; ((UINT16 *)dst)[0] = htons(0x8100); ((UINT16 *)dst)[1] = htons(vlanInfo.TagHeader.VlanId | (vlanInfo.TagHeader.UserPriority << 13)); elem->hdrInfo.l3Offset += VLAN_TAG_SIZE; elem->hdrInfo.l4Offset += VLAN_TAG_SIZE; ovsUserStats.vlanInsert++; } nlMsg = (PNL_MSG_HDR)NlBufAt(&nlBuf, 0, 0); nlMsg->nlmsgLen = NlBufSize(&nlBuf); /* 'totalLen' should be size of valid data. */ elem->packet.totalLen = nlMsg->nlmsgLen; return elem; fail: OvsFreeMemoryWithTag(elem, OVS_USER_POOL_TAG); return NULL; } /* * -------------------------------------------------------------------------- * Handler for the subscription for a packet queue * -------------------------------------------------------------------------- */ NTSTATUS OvsSubscribePacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status; BOOLEAN rc; UINT8 join; UINT32 pid; const NL_POLICY policy[] = { [OVS_NL_ATTR_PACKET_PID] = {.type = NL_A_U32 }, [OVS_NL_ATTR_PACKET_SUBSCRIBE] = {.type = NL_A_U8 } }; PNL_ATTR attrs[ARRAY_SIZE(policy)]; UNREFERENCED_PARAMETER(replyLen); POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn), NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, ARRAY_SIZE(policy), attrs, ARRAY_SIZE(attrs)); if (!rc) { status = STATUS_INVALID_PARAMETER; goto done; } join = NlAttrGetU8(attrs[OVS_NL_ATTR_PACKET_SUBSCRIBE]); pid = NlAttrGetU32(attrs[OVS_NL_ATTR_PACKET_PID]); /* The socket subscribed with must be the same socket we perform receive*/ ASSERT(pid == instance->pid); status = OvsSubscribeDpIoctl(instance, pid, join); /* * XXX Need to add this instance to a global data structure * which hold all packet based instances. The data structure (hash) * should be searched through the pid field of the instance for * placing the missed packet into the correct queue */ done: return status; } /* * -------------------------------------------------------------------------- * Handler for queueing an IRP used for missed packet notification. The IRP is * completed when a packet received and mismatched. STATUS_PENDING is returned * on success. User mode keep a pending IRP at all times. * -------------------------------------------------------------------------- */ NTSTATUS OvsPendPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { UNREFERENCED_PARAMETER(replyLen); POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; /* * XXX access to packet queue must be through acquiring a lock as user mode * could unsubscribe and the instnace will be freed. */ return OvsWaitDpIoctl(usrParamsCtx->irp, instance->fileObject); } /* * -------------------------------------------------------------------------- * Handler for reading missed pacckets from the driver event queue. This * handler is executed when user modes issues a socket receive on a socket * -------------------------------------------------------------------------- */ NTSTATUS OvsReadPacketCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { #ifdef DBG POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; #endif POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; NTSTATUS status; ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); /* Should never read events with a dump socket */ ASSERT(instance->dumpState.ovsMsg == NULL); /* Must have an packet queue */ ASSERT(instance->packetQueue != NULL); /* Output buffer has been validated while validating read dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); /* Read a packet from the instance queue */ status = OvsReadDpIoctl(instance->fileObject, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength, replyLen); return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/BufferMgmt.c0000644000000000000000000000013113534540071022653 xustar0029 mtime=1567801401.20567973 30 atime=1567801402.045685899 30 ctime=1567801424.353850271 openvswitch-2.5.9/datapath-windows/ovsext/BufferMgmt.c0000644000175000017500000014625213534540071024354 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * **************************************************************************** * * Simple Buffer Management framework for OVS * * It introduces four NDIS buffer pools * **Fix size net buffer list pool--this is used for small buffer * One allocation will include NBL + NB + MDL + Data + CONTEXT. * * **Variable size net buffer list pool--this is used for variable size * buffer. The allocation of net buffer list will include NBL + NB + * CONTEXT, a separate allocation of MDL + data buffer is required. * * **NBL only net buffer list pool-- this is used for partial copy * (or clone). In this case we can not allocate net buffer list and * net buffer at the same time. * * **Net buffer pool-- this is required when net buffer need to be * allocated separately. * * A Buffer context is defined to track the buffer specific information * so that during NBL completion, proper action can be taken. Please see * code for details. * * Here is the usage of the management API * All external NBL should be initialized its NBL context by calling * OvsInitExternalNBLContext() * * After the external NBL context is initialized, it can call the following * API to allocate, copy or partial copy NBL. * * OvsAllocateFixSizeNBL() * OvsAllocateVariableSizeNBL() * * OvsPartialCopyNBL() * OvsPartialCopyToMultipleNBLs() * * OvsFullCopyNBL() * OvsFullCopyToMultipleNBLs() * * See code comments for detail description of the functions. * * All NBLs is completed through * OvsCompleteNBL() * If this API return non NULL value, then the returned NBL should be * returned to upper layer by calling * NdisFSendNetBufferListsComplete() if the buffer is from upper * layer. In case of WFP, it can call the corresponding completion routine * to return the NBL to the framework. * * NOTE: * 1. Copy or partial copy will not copy destination port array * 2. Copy or partial copy will copy src port id and index * 3. New Allocated NBL will have src port set to default port id * 4. If original packet has direction flag set, the copied or partial * copied NBL will still be in same direction. * 5. When you advance or retreate the buffer, you may need to update * relevant meta data to keep it consistent. * * **************************************************************************** */ #include "precomp.h" #include "Switch.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_BUFMGMT #include "Debug.h" #include "NetProto.h" #include "Flow.h" #include "Checksum.h" #include "PacketParser.h" #include "Vport.h" /* * -------------------------------------------------------------------------- * OvsInitBufferPool -- * * Allocate NBL and NB pool * * XXX: more optimization may be done for buffer management include local cache * of NBL, NB, data, context, MDL. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsInitBufferPool(PVOID ovsContext) { POVS_NBL_POOL ovsPool; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; NET_BUFFER_LIST_POOL_PARAMETERS nblParam; NET_BUFFER_POOL_PARAMETERS nbParam; C_ASSERT(MEMORY_ALLOCATION_ALIGNMENT >= 8); OVS_LOG_TRACE("Enter: context: %p", context); ovsPool = &context->ovsPool; RtlZeroMemory(ovsPool, sizeof (OVS_NBL_POOL)); ovsPool->ndisHandle = context->NdisFilterHandle; ovsPool->ndisContext = context->NdisSwitchContext; /* * fix size NBL pool includes * NBL + NB + MDL + DATA + Context * This is mainly used for Packet execute or slow path when copy is * required and size is less than OVS_DEFAULT_DATA_SIZE. We expect * Most of packet from user space will use this Pool. (This is * true for all bfd and cfm packet. */ RtlZeroMemory(&nblParam, sizeof (nblParam)); OVS_INIT_OBJECT_HEADER(&nblParam.Header, NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1); nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE; nblParam.PoolTag = OVS_FIX_SIZE_NBL_POOL_TAG; nblParam.fAllocateNetBuffer = TRUE; nblParam.DataSize = OVS_DEFAULT_DATA_SIZE + OVS_DEFAULT_HEADROOM_SIZE; ovsPool->fixSizePool = NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam); if (ovsPool->fixSizePool == NULL) { goto pool_cleanup; } /* * Zero Size NBL Pool includes * NBL + NB + Context * This is mainly for packet with large data Size, in this case MDL and * Data will be allocate separately. */ RtlZeroMemory(&nblParam, sizeof (nblParam)); OVS_INIT_OBJECT_HEADER(&nblParam.Header, NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1); nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE; nblParam.PoolTag = OVS_VARIABLE_SIZE_NBL_POOL_TAG; nblParam.fAllocateNetBuffer = TRUE; nblParam.DataSize = 0; ovsPool->zeroSizePool = NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam); if (ovsPool->zeroSizePool == NULL) { goto pool_cleanup; } /* * NBL only pool just includes * NBL (+ context) * This is mainly used for clone and partial copy */ RtlZeroMemory(&nblParam, sizeof (nblParam)); OVS_INIT_OBJECT_HEADER(&nblParam.Header, NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1); nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE; nblParam.PoolTag = OVS_NBL_ONLY_POOL_TAG; nblParam.fAllocateNetBuffer = FALSE; nblParam.DataSize = 0; ovsPool->nblOnlyPool = NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam); if (ovsPool->nblOnlyPool == NULL) { goto pool_cleanup; } /* nb Pool * NB only pool, used for copy */ OVS_INIT_OBJECT_HEADER(&nbParam.Header, NDIS_OBJECT_TYPE_DEFAULT, NET_BUFFER_POOL_PARAMETERS_REVISION_1, NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1); nbParam.PoolTag = OVS_NET_BUFFER_POOL_TAG; nbParam.DataSize = 0; ovsPool->nbPool = NdisAllocateNetBufferPool(context->NdisSwitchContext, &nbParam); if (ovsPool->nbPool == NULL) { goto pool_cleanup; } OVS_LOG_TRACE("Exit: fixSizePool: %p zeroSizePool: %p nblOnlyPool: %p" "nbPool: %p", ovsPool->fixSizePool, ovsPool->zeroSizePool, ovsPool->nblOnlyPool, ovsPool->nbPool); return NDIS_STATUS_SUCCESS; pool_cleanup: OvsCleanupBufferPool(context); OVS_LOG_TRACE("Exit: Fail to initialize ovs buffer pool"); return NDIS_STATUS_RESOURCES; } /* * -------------------------------------------------------------------------- * OvsCleanupBufferPool -- * Free Buffer pool for NBL and NB. * -------------------------------------------------------------------------- */ VOID OvsCleanupBufferPool(PVOID ovsContext) { POVS_NBL_POOL ovsPool; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; ovsPool = &context->ovsPool; OVS_LOG_TRACE("Enter: context: %p", context); #ifdef DBG ASSERT(ovsPool->fixNBLCount == 0); ASSERT(ovsPool->zeroNBLCount == 0); ASSERT(ovsPool->nblOnlyCount == 0); ASSERT(ovsPool->nbCount == 0); ASSERT(ovsPool->sysNBLCount == 0); ASSERT(ovsPool->fragNBLCount == 0); #endif if (ovsPool->fixSizePool) { NdisFreeNetBufferListPool(ovsPool->fixSizePool); ovsPool->fixSizePool = NULL; } if (ovsPool->zeroSizePool) { NdisFreeNetBufferListPool(ovsPool->zeroSizePool); ovsPool->zeroSizePool = NULL; } if (ovsPool->nblOnlyPool) { NdisFreeNetBufferListPool(ovsPool->nblOnlyPool); ovsPool->nblOnlyPool = NULL; } if (ovsPool->nbPool) { NdisFreeNetBufferPool(ovsPool->nbPool); ovsPool->nbPool = NULL; } OVS_LOG_TRACE("Exit: cleanup OVS Buffer pool"); } static VOID OvsInitNBLContext(POVS_BUFFER_CONTEXT ctx, UINT16 flags, UINT32 origDataLength, UINT32 srcPortNo) { ctx->magic = OVS_CTX_MAGIC; ctx->refCount = 1; ctx->flags = flags; ctx->srcPortNo = srcPortNo; ctx->origDataLength = origDataLength; } static VOID OvsDumpForwardingDetails(PNET_BUFFER_LIST nbl) { PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info; info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl); if (info == NULL) { return; } OVS_LOG_INFO("nbl: %p, numAvailableDest: %d, srcId:%d, srcIndex: %d " "isDataSafe: %s, safeDataSize: %d", nbl, info->NumAvailableDestinations, info->SourcePortId, info->SourceNicIndex, info->IsPacketDataSafe ? "TRUE" : "FALSE", info->IsPacketDataSafe ? 0 : info->SafePacketDataSize); } static VOID OvsDumpNBLContext(PNET_BUFFER_LIST nbl) { PNET_BUFFER_LIST_CONTEXT ctx = nbl->Context; if (ctx == NULL) { OVS_LOG_INFO("No Net Buffer List context"); return; } while (ctx) { OVS_LOG_INFO("nbl: %p, ctx: %p, TotalSize: %d, Offset: %d", nbl, ctx, ctx->Size, ctx->Offset); ctx = ctx->Next; } } static VOID OvsDumpMDLChain(PMDL mdl) { PMDL tmp; tmp = mdl; while (tmp) { OVS_LOG_INFO("MDL: %p, Size: %d, MappedSystemVa: %p, StartVa: %p" " ByteCount: %d, ByteOffset: %d", tmp, tmp->Size, tmp->MappedSystemVa, tmp->StartVa, tmp->ByteCount, tmp->ByteOffset); tmp = tmp->Next; } } static VOID OvsDumpNetBuffer(PNET_BUFFER nb) { OVS_LOG_INFO("NET_BUFFER: %p, ChecksumBias: %d Handle: %p, MDLChain: %p " "CurrMDL: %p, CurrOffset: %d, DataLen: %d, Offset: %d", nb, NET_BUFFER_CHECKSUM_BIAS(nb), nb->NdisPoolHandle, NET_BUFFER_FIRST_MDL(nb), NET_BUFFER_CURRENT_MDL(nb), NET_BUFFER_CURRENT_MDL_OFFSET(nb), NET_BUFFER_DATA_LENGTH(nb), NET_BUFFER_DATA_OFFSET(nb)); OvsDumpMDLChain(NET_BUFFER_FIRST_MDL(nb)); } static VOID OvsDumpNetBufferList(PNET_BUFFER_LIST nbl) { PNET_BUFFER nb; OVS_LOG_INFO("NBL: %p, parent: %p, SrcHandle: %p, ChildCount:%d " "poolHandle: %p", nbl, nbl->ParentNetBufferList, nbl->SourceHandle, nbl->ChildRefCount, nbl->NdisPoolHandle); OvsDumpNBLContext(nbl); nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { OvsDumpNetBuffer(nb); nb = NET_BUFFER_NEXT_NB(nb); } } /* * -------------------------------------------------------------------------- * OvsAllocateFixSizeNBL -- * * Allocate fix size NBL which include * NBL + NB + MBL + Data + Context * Please note: * * Forwarding Context is allocated, but forwarding detail information * is not initailized. * * The headroom can not be larger than OVS_DEFAULT_HEADROOM_SIZE(128 * byte). * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsAllocateFixSizeNBL(PVOID ovsContext, UINT32 size, UINT32 headRoom) { PNET_BUFFER_LIST nbl = NULL; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; POVS_BUFFER_CONTEXT ctx; POVS_NBL_POOL ovsPool = &context->ovsPool; NDIS_STATUS status; UINT32 line; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info; if ((headRoom + size) > OVS_FIX_NBL_DATA_SIZE || size == 0) { line = __LINE__; goto allocate_done; } nbl = NdisAllocateNetBufferList(ovsPool->fixSizePool, (UINT16)sizeof (OVS_BUFFER_CONTEXT), (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL); if (nbl == NULL) { line = __LINE__; goto allocate_done; } nbl->SourceHandle = ovsPool->ndisHandle; status = context->NdisSwitchHandlers. AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl); if (status != NDIS_STATUS_SUCCESS) { NdisFreeNetBufferList(nbl); nbl = NULL; line = __LINE__; goto allocate_done; } info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl); ASSERT(info); info->IsPacketDataSafe = TRUE; info->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(nbl), size, 0, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->fixNBLCount); OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); #endif ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); ASSERT(ctx); OvsInitNBLContext(ctx, OVS_BUFFER_FROM_FIX_SIZE_POOL | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT, size, OVS_DPPORT_NUMBER_INVALID); line = __LINE__; allocate_done: OVS_LOG_LOUD("Allocate Fix NBL: %p, line: %d", nbl, line); return nbl; } static PMDL OvsAllocateMDLAndData(NDIS_HANDLE ndisHandle, UINT32 dataSize) { PMDL mdl; PVOID data; data = OvsAllocateMemoryWithTag(dataSize, OVS_MDL_POOL_TAG); if (data == NULL) { return NULL; } mdl = NdisAllocateMdl(ndisHandle, data, dataSize); if (mdl == NULL) { OvsFreeMemoryWithTag(data, OVS_MDL_POOL_TAG); } return mdl; } static VOID OvsFreeMDLAndData(PMDL mdl) { PVOID data; data = MmGetMdlVirtualAddress(mdl); NdisFreeMdl(mdl); OvsFreeMemoryWithTag(data, OVS_MDL_POOL_TAG); } /* * -------------------------------------------------------------------------- * OvsAllocateVariableSizeNBL -- * * Allocate variable size NBL, the NBL looks like * NBL + NB + Context * MDL + Data * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsAllocateVariableSizeNBL(PVOID ovsContext, UINT32 size, UINT32 headRoom) { PNET_BUFFER_LIST nbl = NULL; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; POVS_NBL_POOL ovsPool = &context->ovsPool; POVS_BUFFER_CONTEXT ctx; UINT32 realSize; PMDL mdl; NDIS_STATUS status; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info; if (size == 0) { return NULL; } realSize = MEM_ALIGN_SIZE(size + headRoom); mdl = OvsAllocateMDLAndData(ovsPool->ndisHandle, realSize); if (mdl == NULL) { return NULL; } nbl = NdisAllocateNetBufferAndNetBufferList(ovsPool->zeroSizePool, (UINT16)sizeof (OVS_BUFFER_CONTEXT), (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL, mdl, realSize, 0); if (nbl == NULL) { OvsFreeMDLAndData(mdl); return NULL; } nbl->SourceHandle = ovsPool->ndisHandle; status = context->NdisSwitchHandlers. AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl); if (status != NDIS_STATUS_SUCCESS) { /* * do we need to remove mdl from nbl XXX */ OvsFreeMDLAndData(mdl); NdisFreeNetBufferList(nbl); return NULL; } info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl); ASSERT(info); info->IsPacketDataSafe = TRUE; info->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(nbl), size, 0, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->zeroNBLCount); OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); #endif ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); OvsInitNBLContext(ctx, OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT | OVS_BUFFER_FROM_ZERO_SIZE_POOL, size, OVS_DPPORT_NUMBER_INVALID); OVS_LOG_LOUD("Allocate variable size NBL: %p", nbl); return nbl; } /* * -------------------------------------------------------------------------- * OvsInitExternalNBLContext -- * * For NBL not allocated by OVS, it will allocate and initialize * the NBL context. * -------------------------------------------------------------------------- */ POVS_BUFFER_CONTEXT OvsInitExternalNBLContext(PVOID ovsContext, PNET_BUFFER_LIST nbl, BOOLEAN isRecv) { NDIS_HANDLE poolHandle; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; POVS_BUFFER_CONTEXT ctx; PNET_BUFFER nb; NDIS_STATUS status; UINT16 flags; poolHandle = NdisGetPoolFromNetBufferList(nbl); if (poolHandle == context->ovsPool.ndisHandle || nbl->SourceHandle == context->ovsPool.ndisHandle) { return (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); } status = NdisAllocateNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT), OVS_DEFAULT_NBL_CONTEXT_FILL, OVS_OTHER_POOL_TAG); if (status != NDIS_STATUS_SUCCESS) { return NULL; } #ifdef DBG OvsDumpNBLContext(nbl); InterlockedIncrement((LONG volatile *)&context->ovsPool.sysNBLCount); #endif flags = isRecv ? OVS_BUFFER_RECV_BUFFER : OVS_BUFFER_SEND_BUFFER; flags |= OVS_BUFFER_NEED_COMPLETE | OVS_BUFFER_PRIVATE_CONTEXT; ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); nb = NET_BUFFER_LIST_FIRST_NB(nbl); /* * we use first nb to decide whether we need advance or retreat during * complete. */ OvsInitNBLContext(ctx, flags, NET_BUFFER_DATA_LENGTH(nb), OVS_DPPORT_NUMBER_INVALID); return ctx; } /* * -------------------------------------------------------------------------- * OvsAllocateNBLContext * * Create NBL buffer context and forwarding context. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsAllocateNBLContext(POVS_SWITCH_CONTEXT context, PNET_BUFFER_LIST nbl) { POVS_NBL_POOL ovsPool = &context->ovsPool; NDIS_STATUS status; status = NdisAllocateNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT), OVS_DEFAULT_NBL_CONTEXT_FILL, OVS_OTHER_POOL_TAG); if (status != NDIS_STATUS_SUCCESS) { return NDIS_STATUS_FAILURE; } nbl->SourceHandle = ovsPool->ndisHandle; status = context->NdisSwitchHandlers. AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl); if (status != NDIS_STATUS_SUCCESS) { NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT)); return NDIS_STATUS_FAILURE; } return status; } /* * -------------------------------------------------------------------------- * OvsFreeNBLContext * * Free the NBL buffer context and forwarding context. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsFreeNBLContext(POVS_SWITCH_CONTEXT context, PNET_BUFFER_LIST nbl) { POVS_NBL_POOL ovsPool = &context->ovsPool; context->NdisSwitchHandlers. FreeNetBufferListForwardingContext(ovsPool->ndisContext, nbl); NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT)); return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsCopyNBLInfo * * Copy NBL info from src to dst * -------------------------------------------------------------------------- */ NDIS_STATUS OvsCopyNBLInfo(PNET_BUFFER_LIST srcNbl, PNET_BUFFER_LIST dstNbl, POVS_BUFFER_CONTEXT srcCtx, UINT32 copySize, BOOLEAN copyNblInfo) { PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO srcInfo, dstInfo; NDIS_STATUS status = NDIS_STATUS_SUCCESS; srcInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(srcNbl); dstInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(dstNbl); if (srcInfo) { #ifdef OVS_USE_COPY_NET_BUFFER_LIST_INFO status = context->NdisSwitchHandlers. CopyNetBufferListInfo(ovsPool->ndisContext, dstNbl, srcNbl, 0); if (status != NDIS_STATUS_SUCCESS) { return status; } #else dstInfo->SourcePortId = srcInfo->SourcePortId; dstInfo->SourceNicIndex = srcInfo->SourceNicIndex; if (copyNblInfo) { if (srcCtx->flags & OVS_BUFFER_RECV_BUFFER) { NdisCopyReceiveNetBufferListInfo(dstNbl, srcNbl); } else if (srcCtx->flags & OVS_BUFFER_SEND_BUFFER) { NdisCopySendNetBufferListInfo(dstNbl, srcNbl); } } #endif dstInfo->IsPacketDataSafe = srcInfo->IsPacketDataSafe; if (!srcInfo->IsPacketDataSafe && copySize > srcInfo->SafePacketDataSize) { srcInfo->SafePacketDataSize = copySize; } } else { /* * Assume all data are safe */ dstInfo->IsPacketDataSafe = TRUE; dstInfo->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID; } return status; } /* * -------------------------------------------------------------------------- * OvsPartialCopyNBL -- * * Partial copy NBL, if there is multiple NB in NBL, each one will be * copied. We also reserve headroom for the new NBL. * * Please note, * NBL should have OVS_BUFFER_CONTEXT setup before calling * this function. * The NBL should already have ref to itself so that during copy * it will not be freed. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsPartialCopyNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 copySize, UINT32 headRoom, BOOLEAN copyNblInfo) { PNET_BUFFER_LIST newNbl; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; NDIS_STATUS status; PNET_BUFFER srcNb, dstNb; ULONG byteCopied; POVS_NBL_POOL ovsPool = &context->ovsPool; POVS_BUFFER_CONTEXT srcCtx, dstCtx; UINT16 flags; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } if (copySize) { NdisAdvanceNetBufferListDataStart(nbl, copySize, FALSE, NULL); } newNbl = NdisAllocateCloneNetBufferList(nbl, ovsPool->nblOnlyPool, NULL, 0); if (copySize) { status = NdisRetreatNetBufferListDataStart(nbl, copySize, 0, NULL, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); } if (newNbl == NULL) { return NULL; } /* * Allocate private memory for copy */ if (copySize + headRoom) { status = NdisRetreatNetBufferListDataStart(newNbl, copySize + headRoom, 0, NULL, NULL); if (status != NDIS_STATUS_SUCCESS) { goto retreat_error; } if (headRoom) { NdisAdvanceNetBufferListDataStart(newNbl, headRoom, FALSE, NULL); } if (copySize) { srcNb = NET_BUFFER_LIST_FIRST_NB(nbl); dstNb = NET_BUFFER_LIST_FIRST_NB(newNbl); while (srcNb) { status = NdisCopyFromNetBufferToNetBuffer(dstNb, 0, copySize, srcNb, 0, &byteCopied); if (status != NDIS_STATUS_SUCCESS || copySize != byteCopied) { goto nbl_context_error; } srcNb = NET_BUFFER_NEXT_NB(srcNb); dstNb = NET_BUFFER_NEXT_NB(dstNb); } } } status = OvsAllocateNBLContext(context, newNbl); if (status != NDIS_STATUS_SUCCESS) { goto nbl_context_error; } status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, copySize, copyNblInfo); if (status != NDIS_STATUS_SUCCESS) { goto copy_list_info_error; } #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->nblOnlyCount); #endif newNbl->ParentNetBufferList = nbl; dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); ASSERT(dstCtx != NULL); flags = srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER); flags |= OVS_BUFFER_FROM_NBL_ONLY_POOL | OVS_BUFFER_PRIVATE_CONTEXT | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT; srcNb = NET_BUFFER_LIST_FIRST_NB(nbl); OvsInitNBLContext(dstCtx, flags, NET_BUFFER_DATA_LENGTH(srcNb) - copySize, OVS_DPPORT_NUMBER_INVALID); InterlockedIncrement((LONG volatile *)&srcCtx->refCount); #ifdef DBG OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); OvsDumpNetBufferList(newNbl); OvsDumpForwardingDetails(newNbl); #endif OVS_LOG_LOUD("Partial Copy new NBL: %p", newNbl); return newNbl; copy_list_info_error: OvsFreeNBLContext(context, newNbl); nbl_context_error: if (copySize) { NdisAdvanceNetBufferListDataStart(newNbl, copySize, TRUE, NULL); } retreat_error: NdisFreeCloneNetBufferList(newNbl, 0); return NULL; } /* * -------------------------------------------------------------------------- * OvsPartialCopyToMultipleNBLs -- * * This is similar to OvsPartialCopyNBL() except that each NB will * have its own NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsPartialCopyToMultipleNBLs(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 copySize, UINT32 headRoom, BOOLEAN copyNblInfo) { PNET_BUFFER nb, nextNb = NULL, firstNb, prevNb; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST firstNbl = NULL, newNbl, prevNbl = NULL; nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (NET_BUFFER_NEXT_NB(nb) == NULL) { return OvsPartialCopyNBL(context, nbl, copySize, headRoom, copyNblInfo); } firstNb = nb; prevNb = nb; while (nb) { nextNb = NET_BUFFER_NEXT_NB(nb); NET_BUFFER_NEXT_NB(nb) = NULL; NET_BUFFER_LIST_FIRST_NB(nbl) = nb; newNbl = OvsPartialCopyNBL(context, nbl, copySize, headRoom, copyNblInfo); if (newNbl == NULL) { goto cleanup; } if (prevNbl == NULL) { firstNbl = newNbl; } else { NET_BUFFER_LIST_NEXT_NBL(prevNbl) = newNbl; NET_BUFFER_NEXT_NB(prevNb) = nb; } prevNbl = newNbl; prevNb = nb; nb = nextNb; } NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb; return firstNbl; cleanup: NET_BUFFER_NEXT_NB(prevNb) = nb; NET_BUFFER_NEXT_NB(nb) = nextNb; NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb; newNbl = firstNbl; while (newNbl) { firstNbl = NET_BUFFER_LIST_NEXT_NBL(newNbl); NET_BUFFER_LIST_NEXT_NBL(newNbl) = NULL; OvsCompleteNBL(context, newNbl, TRUE); newNbl = firstNbl; } return NULL; } static PNET_BUFFER_LIST OvsCopySinglePacketNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, PNET_BUFFER nb, UINT32 headRoom, BOOLEAN copyNblInfo) { UINT32 size; ULONG copiedSize; POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST newNbl; PNET_BUFFER newNb; NDIS_STATUS status; POVS_BUFFER_CONTEXT srcCtx, dstCtx; size = NET_BUFFER_DATA_LENGTH(nb); if ((size + headRoom) <= OVS_FIX_NBL_DATA_SIZE) { newNbl = OvsAllocateFixSizeNBL(context, size, headRoom); } else { newNbl = OvsAllocateVariableSizeNBL(context, size, headRoom); } if (newNbl == NULL) { return NULL; } newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0, &copiedSize); srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (status == NDIS_STATUS_SUCCESS) { status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, copiedSize, copyNblInfo); } if (status != NDIS_STATUS_SUCCESS || copiedSize != size) { OvsCompleteNBL(context, newNbl, TRUE); return NULL; } dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); ASSERT(dstCtx && srcCtx); ASSERT(srcCtx->magic == OVS_CTX_MAGIC && dstCtx->magic == OVS_CTX_MAGIC); dstCtx->flags |= srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER); #ifdef DBG OvsDumpNetBufferList(newNbl); OvsDumpForwardingDetails(newNbl); #endif OVS_LOG_LOUD("Copy single nb to new NBL: %p", newNbl); return newNbl; } /* * -------------------------------------------------------------------------- * OvsFullCopyNBL -- * * Copy the NBL to a new NBL including data. * * Notes: * The NBL can have multiple NBs, but the final result is one NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsFullCopyNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; POVS_NBL_POOL ovsPool = &context->ovsPool; PNET_BUFFER_LIST newNbl; PNET_BUFFER nb, newNb, firstNb = NULL, prevNb = NULL; POVS_BUFFER_CONTEXT dstCtx, srcCtx; PMDL mdl; NDIS_STATUS status; UINT32 size, totalSize; ULONG copiedSize; UINT16 flags; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO dstInfo; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (NET_BUFFER_NEXT_NB(nb) == NULL) { return OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); } newNbl = NdisAllocateNetBufferList(ovsPool->nblOnlyPool, (UINT16)sizeof (OVS_BUFFER_CONTEXT), (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL); if (newNbl == NULL) { return NULL; } while (nb) { size = NET_BUFFER_DATA_LENGTH(nb); totalSize = MEM_ALIGN_SIZE(size + headRoom); mdl = OvsAllocateMDLAndData(ovsPool->ndisHandle, totalSize); if (mdl == NULL) { goto nblcopy_error; } newNb = NdisAllocateNetBuffer(ovsPool->nbPool, mdl, totalSize, 0); if (newNb == NULL) { OvsFreeMDLAndData(mdl); goto nblcopy_error; } if (firstNb == NULL) { firstNb = newNb; } else { NET_BUFFER_NEXT_NB(prevNb) = newNb; } prevNb = newNb; #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->nbCount); #endif status = NdisRetreatNetBufferDataStart(newNb, size, 0, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0, &copiedSize); if (status != NDIS_STATUS_SUCCESS || size != copiedSize) { goto nblcopy_error; } nb = NET_BUFFER_NEXT_NB(nb); } NET_BUFFER_LIST_FIRST_NB(newNbl) = firstNb; newNbl->SourceHandle = ovsPool->ndisHandle; status = context->NdisSwitchHandlers. AllocateNetBufferListForwardingContext(ovsPool->ndisContext, newNbl); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, 0, copyNblInfo); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } dstInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl); dstInfo->IsPacketDataSafe = TRUE; dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); flags = srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER); flags |= OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA | OVS_BUFFER_PRIVATE_NET_BUFFER | OVS_BUFFER_FROM_NBL_ONLY_POOL | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT; OvsInitNBLContext(dstCtx, flags, NET_BUFFER_DATA_LENGTH(firstNb), OVS_DPPORT_NUMBER_INVALID); #ifdef DBG OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); InterlockedIncrement((LONG volatile *)&ovsPool->nblOnlyCount); #endif OVS_LOG_LOUD("newNbl: %p", newNbl); return newNbl; nblcopy_error: while (firstNb) { #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nbCount); #endif prevNb = firstNb; firstNb = NET_BUFFER_NEXT_NB(prevNb); mdl = NET_BUFFER_FIRST_MDL(prevNb); NET_BUFFER_FIRST_MDL(prevNb) = NULL; NdisFreeNetBuffer(prevNb); OvsFreeMDLAndData(mdl); } NdisFreeNetBufferList(newNbl); OVS_LOG_ERROR("OvsFullCopyNBL failed"); return NULL; } /* * -------------------------------------------------------------------------- * GetSegmentHeaderInfo * * Extract header size and sequence number for the segment. * -------------------------------------------------------------------------- */ static NDIS_STATUS GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl, const POVS_PACKET_HDR_INFO hdrInfo, UINT32 *hdrSize, UINT32 *seqNumber) { TCPHdr tcpStorage; const TCPHdr *tcp; /* Parse the orginal Eth/IP/TCP header */ tcp = OvsGetPacketBytes(nbl, sizeof *tcp, hdrInfo->l4Offset, &tcpStorage); if (tcp == NULL) { return NDIS_STATUS_FAILURE; } *seqNumber = ntohl(tcp->seq); *hdrSize = hdrInfo->l4Offset + TCP_HDR_LEN(tcp); return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * FixSegmentHeader * * Fix IP length, IP checksum, TCP sequence number and TCP checksum * in the segment. * -------------------------------------------------------------------------- */ static NDIS_STATUS FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber, BOOLEAN lastPacket, UINT16 packetCounter) { EthHdr *dstEth; IPHdr *dstIP; TCPHdr *dstTCP; PMDL mdl; PUINT8 bufferStart; mdl = NET_BUFFER_FIRST_MDL(nb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority); if (!bufferStart) { return NDIS_STATUS_RESOURCES; } dstEth = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(nb)); ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb) >= sizeof(EthHdr) + sizeof(IPHdr) + sizeof(TCPHdr)); dstIP = (IPHdr *)((PCHAR)dstEth + sizeof *dstEth); dstTCP = (TCPHdr *)((PCHAR)dstIP + dstIP->ihl * 4); ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb) >= sizeof(EthHdr) + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP)); /* Fix IP length and checksum */ ASSERT(dstIP->protocol == IPPROTO_TCP); dstIP->tot_len = htons(segmentSize + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP)); dstIP->id += packetCounter; dstIP->check = 0; dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0); /* Fix TCP checksum */ dstTCP->seq = htonl(seqNumber); /* * Set the TCP FIN and PSH bit only for the last packet * More information can be found under: * https://msdn.microsoft.com/en-us/library/windows/hardware/ff568840%28v=vs.85%29.aspx */ if (dstTCP->fin) { dstTCP->fin = lastPacket; } if (dstTCP->psh) { dstTCP->psh = lastPacket; } UINT16 csumLength = segmentSize + TCP_HDR_LEN(dstTCP); dstTCP->check = IPPseudoChecksum(&dstIP->saddr, &dstIP->daddr, IPPROTO_TCP, csumLength); dstTCP->check = CalculateChecksumNB(nb, csumLength, sizeof *dstEth + dstIP->ihl * 4); return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsTcpSegmentyNBL -- * * Segment TCP payload, and prepend each segment with ether/IP/TCP header. * Leave headRoom for additional encap. * * Please note, * NBL should have OVS_BUFFER_CONTEXT setup before calling * this function. * The NBL should already have ref to itself so that during copy * it will not be freed. * Currently this API assert there is only one NB in an NBL, it needs * to be fixed if we receive multiple NBs in an NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsTcpSegmentNBL(PVOID ovsContext, PNET_BUFFER_LIST nbl, POVS_PACKET_HDR_INFO hdrInfo, UINT32 mss, UINT32 headRoom) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; #ifdef DBG POVS_NBL_POOL ovsPool = &context->ovsPool; #endif POVS_BUFFER_CONTEXT dstCtx, srcCtx; UINT32 size, hdrSize, seqNumber; PNET_BUFFER_LIST newNbl; PNET_BUFFER nb, newNb; NDIS_STATUS status; UINT16 segmentSize; ULONG copiedSize; UINT16 packetCounter = 0; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); ASSERT(NET_BUFFER_NEXT_NB(nb) == NULL); /* Figure out the segment header size */ status = GetSegmentHeaderInfo(nbl, hdrInfo, &hdrSize, &seqNumber); if (status != NDIS_STATUS_SUCCESS) { OVS_LOG_INFO("Cannot parse NBL header"); return NULL; } size = NET_BUFFER_DATA_LENGTH(nb) - hdrSize; /* XXX add to ovsPool counters? */ newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL, NULL, hdrSize, mss, hdrSize + headRoom , 0, 0); if (newNbl == NULL) { return NULL; } /* Now deal with TCP payload */ for (newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); newNb != NULL; newNb = NET_BUFFER_NEXT_NB(newNb)) { segmentSize = (size > mss ? mss : size) & 0xffff; if (headRoom) { NdisAdvanceNetBufferDataStart(newNb, headRoom, FALSE, NULL); } /* Now copy the eth/IP/TCP header and fix up */ status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize, nb, 0, &copiedSize); if (status != NDIS_STATUS_SUCCESS || hdrSize != copiedSize) { goto nblcopy_error; } status = FixSegmentHeader(newNb, segmentSize, seqNumber, NET_BUFFER_NEXT_NB(newNb) == NULL, packetCounter); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } /* Move on to the next segment */ size -= segmentSize; seqNumber += segmentSize; packetCounter++; } status = OvsAllocateNBLContext(context, newNbl); if (status != NDIS_STATUS_SUCCESS) { goto nblcopy_error; } status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, hdrSize + headRoom, FALSE); if (status != NDIS_STATUS_SUCCESS) { goto nbl_context_error; } newNbl->ParentNetBufferList = nbl; /* Remember it's a fragment NBL so we can free it properly */ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl); ASSERT(dstCtx != NULL); dstCtx->flags = OVS_BUFFER_FRAGMENT | OVS_BUFFER_PRIVATE_CONTEXT | OVS_BUFFER_PRIVATE_FORWARD_CONTEXT | OVS_BUFFER_SEND_BUFFER; dstCtx->refCount = 1; dstCtx->magic = OVS_CTX_MAGIC; dstCtx->dataOffsetDelta = hdrSize + headRoom; InterlockedIncrement((LONG volatile *)&srcCtx->refCount); #ifdef DBG InterlockedIncrement((LONG volatile *)&ovsPool->fragNBLCount); OvsDumpNetBufferList(nbl); OvsDumpForwardingDetails(nbl); OvsDumpNetBufferList(newNbl); OvsDumpForwardingDetails(newNbl); #endif OVS_LOG_TRACE("Segment nbl %p to newNbl: %p", nbl, newNbl); return newNbl; nbl_context_error: OvsFreeNBLContext(context, newNbl); nblcopy_error: #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount); #endif NdisFreeFragmentNetBufferList(newNbl, hdrSize + headRoom, 0); return NULL; } /* * -------------------------------------------------------------------------- * OvsAllocateNBLFromBuffer -- * * This function allocates all the stuff necessary for creating an NBL from the * input buffer of specified length, namely, a nonpaged data buffer of size * length, an MDL from it, and a NB and NBL from it. It does not allocate an NBL * context yet. It also copies data from the specified buffer to the NBL. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsAllocateNBLFromBuffer(PVOID context, PVOID buffer, ULONG length) { POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)context; UINT8 *data = NULL; PNET_BUFFER_LIST nbl = NULL; PNET_BUFFER nb; PMDL mdl; if (length > OVS_DEFAULT_DATA_SIZE) { nbl = OvsAllocateVariableSizeNBL(switchContext, length, OVS_DEFAULT_HEADROOM_SIZE); } else { nbl = OvsAllocateFixSizeNBL(switchContext, length, OVS_DEFAULT_HEADROOM_SIZE); } if (nbl == NULL) { return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); mdl = NET_BUFFER_CURRENT_MDL(nb); data = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority) + NET_BUFFER_CURRENT_MDL_OFFSET(nb); if (!data) { OvsCompleteNBL(switchContext, nbl, TRUE); return NULL; } NdisMoveMemory(data, buffer, length); return nbl; } /* * -------------------------------------------------------------------------- * OvsFullCopyToMultipleNBLs -- * * Copy NBL to multiple NBLs, each NB will have its own NBL * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsFullCopyToMultipleNBLs(PVOID ovsContext, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo) { POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext; PNET_BUFFER_LIST firstNbl, currNbl, newNbl; PNET_BUFFER nb; POVS_BUFFER_CONTEXT srcCtx; srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) { OVS_LOG_INFO("src nbl must have ctx initialized"); ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC); return NULL; } nb = NET_BUFFER_LIST_FIRST_NB(nbl); newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL || NET_BUFFER_NEXT_NB(nb) == NULL) { return newNbl; } else { firstNbl = newNbl; currNbl = newNbl; } while (nb) { newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo); if (newNbl == NULL) { goto copymultiple_error; } NET_BUFFER_LIST_NEXT_NBL(currNbl) = newNbl; currNbl = newNbl; nb = NET_BUFFER_NEXT_NB(nb); } return firstNbl; copymultiple_error: while (firstNbl) { currNbl = firstNbl; firstNbl = NET_BUFFER_LIST_NEXT_NBL(firstNbl); NET_BUFFER_LIST_NEXT_NBL(currNbl) = NULL; OvsCompleteNBL(context, currNbl, TRUE); } return NULL; } /* * -------------------------------------------------------------------------- * OvsCompleteNBL -- * * This function tries to free the NBL allocated by OVS buffer * management module. If it trigger the completion of the parent * NBL, it will recursively call itself. If it trigger the completion * of external NBL, it will be returned to the caller. The caller * is responsible to call API to return to upper layer. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsCompleteNBL(POVS_SWITCH_CONTEXT context, PNET_BUFFER_LIST nbl, BOOLEAN updateRef) { POVS_BUFFER_CONTEXT ctx; UINT16 flags; UINT32 dataOffsetDelta; PNET_BUFFER_LIST parent; NDIS_STATUS status; NDIS_HANDLE poolHandle; LONG value; POVS_NBL_POOL ovsPool = &context->ovsPool; PNET_BUFFER nb; ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); OVS_LOG_TRACE("Enter: nbl: %p, ctx: %p, refCount: %d, updateRef:%d", nbl, ctx, ctx->refCount, updateRef); if (updateRef) { value = InterlockedDecrement((LONG volatile *)&ctx->refCount); if (value != 0) { return NULL; } } else { /* * This is a special case, the refCount must be zero */ ASSERT(ctx->refCount == 0); } nb = NET_BUFFER_LIST_FIRST_NB(nbl); flags = ctx->flags; dataOffsetDelta = ctx->dataOffsetDelta; if (!(flags & OVS_BUFFER_FRAGMENT) && NET_BUFFER_DATA_LENGTH(nb) != ctx->origDataLength) { UINT32 diff; if (NET_BUFFER_DATA_LENGTH(nb) < ctx->origDataLength) { diff = ctx->origDataLength -NET_BUFFER_DATA_LENGTH(nb); status = NdisRetreatNetBufferListDataStart(nbl, diff, 0, NULL, NULL); ASSERT(status == NDIS_STATUS_SUCCESS); } else { diff = NET_BUFFER_DATA_LENGTH(nb) - ctx->origDataLength; NdisAdvanceNetBufferListDataStart(nbl, diff, TRUE, NULL); } } if (flags & OVS_BUFFER_PRIVATE_CONTEXT) { NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT)); } if (flags & OVS_BUFFER_NEED_COMPLETE) { /* * return to caller for completion */ #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->sysNBLCount); #endif return nbl; } if (flags & OVS_BUFFER_PRIVATE_FORWARD_CONTEXT) { context->NdisSwitchHandlers. FreeNetBufferListForwardingContext(ovsPool->ndisContext, nbl); } if (flags & (OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA)) { PNET_BUFFER nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { PMDL mdl = NET_BUFFER_FIRST_MDL(nb); NET_BUFFER_FIRST_MDL(nb) = NULL; ASSERT(mdl->Next == NULL); OvsFreeMDLAndData(mdl); nb = NET_BUFFER_NEXT_NB(nb); } } if (flags & OVS_BUFFER_PRIVATE_NET_BUFFER) { PNET_BUFFER nb, nextNb; nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { nextNb = NET_BUFFER_NEXT_NB(nb); NdisFreeNetBuffer(nb); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nbCount); #endif nb = nextNb; } NET_BUFFER_LIST_FIRST_NB(nbl) = NULL; } parent = nbl->ParentNetBufferList; poolHandle = NdisGetPoolFromNetBufferList(nbl); if (flags & OVS_BUFFER_FROM_FIX_SIZE_POOL) { ASSERT(poolHandle == ovsPool->fixSizePool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fixNBLCount); #endif NdisFreeNetBufferList(nbl); } else if (flags & OVS_BUFFER_FROM_ZERO_SIZE_POOL) { ASSERT(poolHandle == ovsPool->zeroSizePool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->zeroNBLCount); #endif NdisFreeNetBufferList(nbl); } else if (flags & OVS_BUFFER_FROM_NBL_ONLY_POOL) { ASSERT(poolHandle == ovsPool->nblOnlyPool); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->nblOnlyCount); #endif NdisFreeCloneNetBufferList(nbl, 0); } else if (flags & OVS_BUFFER_FRAGMENT) { OVS_LOG_TRACE("Free fragment %p parent %p", nbl, parent); #ifdef DBG InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount); #endif NdisFreeFragmentNetBufferList(nbl, dataOffsetDelta, 0); } if (parent != NULL) { ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(parent); ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); value = InterlockedDecrement((LONG volatile *)&ctx->refCount); if (value == 0) { return OvsCompleteNBL(context, parent, FALSE); } } return NULL; } /* * -------------------------------------------------------------------------- * OvsSetCtxSourcePortNo -- * Setter function which stores the source port of an NBL in the NBL * Context Info. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsSetCtxSourcePortNo(PNET_BUFFER_LIST nbl, UINT32 portNo) { POVS_BUFFER_CONTEXT ctx; ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (ctx == NULL) { ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); return STATUS_INVALID_PARAMETER; } ctx->srcPortNo = portNo; return NDIS_STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsGetCtxSourcePortNo -- * Get source port of an NBL from its Context Info. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsGetCtxSourcePortNo(PNET_BUFFER_LIST nbl, UINT32 *portNo) { POVS_BUFFER_CONTEXT ctx; ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl); if (ctx == NULL || portNo == NULL) { ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC); return STATUS_INVALID_PARAMETER; } *portNo = ctx->srcPortNo; return NDIS_STATUS_SUCCESS; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/DpInternal.h0000644000000000000000000000013113534540071022662 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 30 ctime=1567801424.409850683 openvswitch-2.5.9/datapath-windows/ovsext/DpInternal.h0000644000175000017500000002217013534540071024353 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __DP_INTERNAL_H_ #define __DP_INTERNAL_H_ 1 #include #define IFNAMSIZ IF_NAMESIZE #include "../ovsext/Netlink/Netlink.h" #define OVS_DP_NUMBER ((uint32_t) 0) typedef __declspec(align(8)) uint64_t Ovs64AlignedU64; typedef __declspec(align(8)) ovs_be64 Ovs64AlignedBe64; #pragma pack(push, 1) #define OVS_MAX_PORT_NAME_LENGTH IFNAMSIZ typedef struct _OVS_VPORT_GET { uint32_t dpNo; uint32_t portNo; char name[OVS_MAX_PORT_NAME_LENGTH]; } OVS_VPORT_GET, *POVS_VPORT_GET; #define OVS_MAX_VM_UUID_LEN 128 #define OVS_MAX_VIF_UUID_LEN 128 typedef struct _OVS_VPORT_EXT_INFO { uint32_t dpNo; uint32_t portNo; uint8_t macAddress[ETH_ADDR_LEN]; uint8_t permMACAddress[ETH_ADDR_LEN]; uint8_t vmMACAddress[ETH_ADDR_LEN]; uint16_t nicIndex; uint32_t portId; uint32_t type; uint32_t mtu; char name[OVS_MAX_PORT_NAME_LENGTH]; uint32_t status; char vmUUID[OVS_MAX_VM_UUID_LEN]; char vifUUID[OVS_MAX_VIF_UUID_LEN]; } OVS_VPORT_EXT_INFO, *POVS_VPORT_EXT_INFO; /* Flows. */ #define OVSWIN_VLAN_CFI 0x1000 /* Used for OvsFlowKey's dlType member for frames that have no Ethernet type, * that is, pure 802.2 frames. */ #define OVSWIN_DL_TYPE_NONE 0x5ff typedef struct L4Key { ovs_be16 tpSrc; /* TCP/UDP/SCTP source port. */ ovs_be16 tpDst; /* TCP/UDP/SCTP destination port. */ } L4Key; typedef struct Ipkey { ovs_be32 nwSrc; /* IPv4 source address. */ ovs_be32 nwDst; /* IPv4 destination address. */ uint8_t nwProto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t nwTos; /* IP ToS (including DSCP and ECN). */ uint8_t nwTtl; /* IP TTL/Hop Limit. */ uint8_t nwFrag; /* FLOW_FRAG_* flags. */ L4Key l4; } IpKey; /* Size of 16 byte. */ typedef struct ArpKey { ovs_be32 nwSrc; /* IPv4 source address. */ ovs_be32 nwDst; /* IPv4 destination address. */ uint8_t arpSha[6]; /* ARP/ND source hardware address. */ uint8_t arpTha[6]; /* ARP/ND target hardware address. */ uint8_t nwProto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t pad[3]; } ArpKey; /* Size of 24 byte. */ typedef struct Ipv6Key { struct in6_addr ipv6Src; /* IPv6 source address. */ struct in6_addr ipv6Dst; /* IPv6 destination address. */ ovs_be32 ipv6Label; /* IPv6 flow label. */ uint8_t nwProto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t nwTos; /* IP ToS (including DSCP and ECN). */ uint8_t nwTtl; /* IP TTL/Hop Limit. */ uint8_t nwFrag; /* FLOW_FRAG_* flags. */ L4Key l4; uint32_t pad; } Ipv6Key; /* Size of 48 byte. */ typedef struct Icmp6Key { struct in6_addr ipv6Src; /* IPv6 source address. */ struct in6_addr ipv6Dst; /* IPv6 destination address. */ ovs_be32 ipv6Label; /* IPv6 flow label. */ uint8_t nwProto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t nwTos; /* IP ToS (including DSCP and ECN). */ uint8_t nwTtl; /* IP TTL/Hop Limit. */ uint8_t nwFrag; /* FLOW_FRAG_* flags. */ L4Key l4; uint8_t arpSha[6]; /* ARP/ND source hardware address. */ uint8_t arpTha[6]; /* ARP/ND target hardware address. */ struct in6_addr ndTarget; /* IPv6 neighbor discovery (ND) target. */ } Icmp6Key; /* Size of 72 byte. */ typedef struct L2Key { uint32_t inPort; /* Port number of input port. */ union { struct { uint16_t offset; uint16_t keyLen; }; uint32_t val; }; uint8_t dlSrc[6]; /* Ethernet source address. */ uint8_t dlDst[6]; /* Ethernet destination address. */ ovs_be16 vlanTci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */ ovs_be16 dlType; /* Ethernet frame type. */ } L2Key; /* Size of 24 byte. */ /* Number of packet attributes required to store OVS tunnel key. */ #define NUM_PKT_ATTR_REQUIRED 3 typedef union OvsIPv4TunnelKey { struct { ovs_be32 dst; ovs_be32 src; ovs_be64 tunnelId; uint16_t flags; uint8_t tos; uint8_t ttl; union { uint32_t pad; struct { ovs_be16 dst_port; uint16_t flow_hash; }; }; }; uint64_t attr[NUM_PKT_ATTR_REQUIRED]; } OvsIPv4TunnelKey; typedef __declspec(align(8)) struct OvsFlowKey { OvsIPv4TunnelKey tunKey; /* 24 bytes */ L2Key l2; /* 24 bytes */ union { IpKey ipKey; /* size 16 */ ArpKey arpKey; /* size 24 */ Ipv6Key ipv6Key; /* size 48 */ Icmp6Key icmp6Key; /* size 72 */ }; } OvsFlowKey; #define OVS_WIN_TUNNEL_KEY_SIZE (sizeof (OvsIPv4TunnelKey)) #define OVS_L2_KEY_SIZE (sizeof (L2Key)) #define OVS_IP_KEY_SIZE (sizeof (IpKey)) #define OVS_IPV6_KEY_SIZE (sizeof (Ipv6Key)) #define OVS_ARP_KEY_SIZE (sizeof (ArpKey)) #define OVS_ICMPV6_KEY_SIZE (sizeof (Icmp6Key)) typedef struct OvsFlowStats { Ovs64AlignedU64 packetCount; Ovs64AlignedU64 byteCount; uint32_t used; uint8_t tcpFlags; } OvsFlowStats; typedef struct OvsFlowInfo { OvsFlowKey key; struct OvsFlowStats stats; uint32_t actionsLen; PNL_ATTR actions; } OvsFlowInfo; enum GetFlags { FLOW_GET_KEY = 0x00000001, FLOW_GET_STATS = 0x00000010, FLOW_GET_ACTIONS = 0x00000100, }; typedef struct OvsFlowDumpInput { uint32_t dpNo; uint32_t position[2]; /* Offset hint to the start of flow dump. */ /* 0 - index of the hash table. * 1 - nth element in the hash table index. */ uint32_t getFlags; /* Information to get in addition to keys. */ uint32_t actionsLen; } OvsFlowDumpInput; typedef struct OvsFlowDumpOutput { /* Hint for the next flow dump operation. */ uint32_t position[2]; /* #flows (currently 0 or 1). In case the buffer is too small to output all * actions, this field indicates actual size needed to dump all actions. */ uint32_t n; OvsFlowInfo flow; } OvsFlowDumpOutput; typedef struct OvsFlowGetInput { uint32_t dpNo; OvsFlowKey key; uint32_t getFlags; /* Information to get in addition to keys. */ uint32_t actionsLen; /* Sizeof of buffer for actions. */ } OvsFlowGetInput; typedef struct OvsFlowGetOutput { OvsFlowInfo info; /* Variable length. */ } OvsFlowGetOutput; typedef enum OvsFlowPutFlags { OVSWIN_FLOW_PUT_CREATE = 1 << 0, OVSWIN_FLOW_PUT_MODIFY = 1 << 1, OVSWIN_FLOW_PUT_DELETE = 1 << 2, OVSWIN_FLOW_PUT_CLEAR = 1 << 3 } OvsFlowPutFlags; typedef struct OvsFlowPut { uint32_t dpNo; uint32_t actionsLen; OvsFlowKey key; uint32_t flags; PNL_ATTR actions; } OvsFlowPut; #define OVS_MIN_PACKET_SIZE 60 typedef struct _OVS_PACKET_INFO { uint32_t totalLen; uint32_t userDataLen; uint32_t packetLen; uint32_t queue; uint32_t inPort; uint32_t cmd; OvsIPv4TunnelKey tunnelKey; uint8_t *payload; /* Includes user data defined as chain of netlink attributes followed by the * packet data. */ uint8_t data[0]; } OVS_PACKET_INFO, *POVS_PACKET_INFO; typedef struct OvsPacketExecute { uint32_t dpNo; uint32_t inPort; uint32_t packetLen; uint32_t actionsLen; PCHAR packetBuf; PNL_ATTR actions; } OvsPacketExecute; typedef struct _OVS_EVENT_SUBSCRIBE { uint32_t cookie; uint32_t dpNo; uint32_t subscribe; uint32_t mask; } OVS_EVENT_SUBSCRIBE, *POVS_EVENT_SUBSCRIBE; typedef struct _OVS_EVENT_POLL { uint32_t cookie; uint32_t dpNo; } OVS_EVENT_POLL, *POVS_EVENT_POLL; enum { OVS_EVENT_CONNECT = ((uint32_t)0x1 << 0), OVS_EVENT_DISCONNECT = ((uint32_t)0x1 << 1), OVS_EVENT_LINK_UP = ((uint32_t)0x1 << 2), OVS_EVENT_LINK_DOWN = ((uint32_t)0x1 << 3), OVS_EVENT_MAC_CHANGE = ((uint32_t)0x1 << 4), OVS_EVENT_MTU_CHANGE = ((uint32_t)0x1 << 5), OVS_EVENT_MASK_ALL = 0x3f, }; typedef struct _OVS_EVENT_ENTRY { UINT32 portNo; OVS_VPORT_TYPE ovsType; UINT32 upcallPid; CHAR ovsName[OVS_MAX_PORT_NAME_LENGTH]; UINT32 type; } OVS_EVENT_ENTRY, *POVS_EVENT_ENTRY; typedef struct _OVS_EVENT_STATUS { uint32_t numberEntries; OVS_EVENT_ENTRY eventEntries[0]; } OVS_EVENT_STATUS, *POVS_EVENT_STATUS; #pragma pack(pop) #endif /* __DP_INTERNAL_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Stt.c0000644000000000000000000000013213534540071021370 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.445850949 openvswitch-2.5.9/datapath-windows/ovsext/Stt.c0000644000175000017500000006726613534540071023077 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Atomic.h" #include "Checksum.h" #include "Flow.h" #include "IpHelper.h" #include "NetProto.h" #include "PacketIO.h" #include "PacketParser.h" #include "Stt.h" #include "Switch.h" #include "User.h" #include "Util.h" #include "Vport.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_STT #include "Debug.h" #include "Jhash.h" KSTART_ROUTINE OvsSttDefragCleaner; static PLIST_ENTRY OvsSttPktFragHash; static NDIS_SPIN_LOCK OvsSttSpinLock; static OVS_STT_THREAD_CTX sttDefragThreadCtx; static NDIS_STATUS OvsDoEncapStt(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, const OvsIPv4TunnelKey *tunKey, const POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl); /* * -------------------------------------------------------------------------- * OvsInitSttTunnel -- * Initialize STT tunnel module. * -------------------------------------------------------------------------- */ NTSTATUS OvsInitSttTunnel(POVS_VPORT_ENTRY vport, UINT16 tcpDestPort) { POVS_STT_VPORT sttPort; sttPort = (POVS_STT_VPORT) OvsAllocateMemoryWithTag(sizeof(*sttPort), OVS_STT_POOL_TAG); if (!sttPort) { OVS_LOG_ERROR("Insufficient memory, can't allocate STT_VPORT"); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(sttPort, sizeof(*sttPort)); sttPort->dstPort = tcpDestPort; vport->priv = (PVOID) sttPort; return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsCleanupSttTunnel -- * Cleanup STT Tunnel module. * -------------------------------------------------------------------------- */ void OvsCleanupSttTunnel(POVS_VPORT_ENTRY vport) { if (vport->ovsType != OVS_VPORT_TYPE_STT || vport->priv == NULL) { return; } OvsFreeMemoryWithTag(vport->priv, OVS_STT_POOL_TAG); vport->priv = NULL; } /* * -------------------------------------------------------------------------- * OvsEncapStt -- * Encapsulates a packet with an STT header. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsEncapStt(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl) { OVS_FWD_INFO fwdInfo; NDIS_STATUS status; UNREFERENCED_PARAMETER(switchContext); status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo); if (status != STATUS_SUCCESS) { OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL); /* * XXX This case where the ARP table is not populated is * currently not handled */ return NDIS_STATUS_FAILURE; } status = OvsDoEncapStt(vport, curNbl, tunKey, &fwdInfo, layers, switchContext, newNbl); return status; } /* * -------------------------------------------------------------------------- * OvsDoEncapStt -- * Internal utility function which actually does the STT encap. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsDoEncapStt(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, const OvsIPv4TunnelKey *tunKey, const POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; PMDL curMdl = NULL; PNET_BUFFER curNb; PUINT8 buf = NULL; EthHdr *outerEthHdr; IPHdr *outerIpHdr; TCPHdr *outerTcpHdr; SttHdr *sttHdr; UINT32 innerFrameLen, ipTotalLen; POVS_STT_VPORT vportStt; UINT32 headRoom = OvsGetSttTunHdrSize(); UINT32 tcpChksumLen; PUINT8 bufferStart; ULONG mss = 0; NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO lsoInfo; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); /* Verify if inner checksum is verified */ BOOLEAN innerChecksumVerified = FALSE; BOOLEAN innerPartialChecksum = FALSE; if (layers->isTcp) { lsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo); switch (lsoInfo.Transmit.Type) { case NDIS_TCP_LARGE_SEND_OFFLOAD_V1_TYPE: mss = lsoInfo.LsoV1Transmit.MSS; break; case NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE: mss = lsoInfo.LsoV2Transmit.MSS; break; default: OVS_LOG_ERROR("Unknown LSO transmit type:%d", lsoInfo.Transmit.Type); } } vportStt = (POVS_STT_VPORT) GetOvsVportPriv(vport); ASSERT(vportStt); NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom, FALSE /*copy NblInfo*/); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to copy NBL"); return NDIS_STATUS_FAILURE; } curNbl = *newNbl; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); /* NB Chain should be split before */ ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL); innerFrameLen = NET_BUFFER_DATA_LENGTH(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (layers->isIPv4) { IPHdr *ip = (IPHdr *)(bufferStart + layers->l3Offset); if (!ip->tot_len) { ip->tot_len = htons(innerFrameLen - sizeof(EthHdr)); } if (!ip->check) { ip->check = IPChecksum((UINT8 *)ip, ip->ihl * 4, 0); } } if (layers->isTcp) { if (mss) { innerPartialChecksum = TRUE; } else { if (!csumInfo.Transmit.TcpChecksum) { innerChecksumVerified = TRUE; } else { innerPartialChecksum = TRUE; } } } else if (layers->isUdp) { if(!csumInfo.Transmit.UdpChecksum) { innerChecksumVerified = TRUE; } else { innerPartialChecksum = TRUE; } } status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { ASSERT(!"Unable to NdisRetreatNetBufferDataStart(headroom)"); OVS_LOG_ERROR("Unable to NdisRetreatNetBufferDataStart(headroom)"); goto ret_error; } /* * Make sure that the headroom for the tunnel header is continguous in * memory. */ curMdl = NET_BUFFER_CURRENT_MDL(curNb); ASSERT((int) (MmGetMdlByteCount(curMdl) - NET_BUFFER_CURRENT_MDL_OFFSET(curNb)) >= (int) headRoom); buf = (PUINT8) MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!buf) { ASSERT(!"MmGetSystemAddressForMdlSafe failed"); OVS_LOG_ERROR("MmGetSystemAddressForMdlSafe failed"); status = NDIS_STATUS_RESOURCES; goto ret_error; } buf += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); outerEthHdr = (EthHdr *)buf; outerIpHdr = (IPHdr *) (outerEthHdr + 1); outerTcpHdr = (TCPHdr *) (outerIpHdr + 1); sttHdr = (SttHdr *) (outerTcpHdr + 1); /* L2 header */ ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) == (PCHAR)&fwdInfo->srcMacAddr); NdisMoveMemory(outerEthHdr->Destination, fwdInfo->dstMacAddr, sizeof outerEthHdr->Destination + sizeof outerEthHdr->Source); outerEthHdr->Type = htons(ETH_TYPE_IPV4); /* L3 header */ outerIpHdr->ihl = sizeof(IPHdr) >> 2; outerIpHdr->version = IPPROTO_IPV4; outerIpHdr->tos = tunKey->tos; ipTotalLen = sizeof(IPHdr) + sizeof(TCPHdr) + STT_HDR_LEN + innerFrameLen; outerIpHdr->tot_len = htons(ipTotalLen); ASSERT(ipTotalLen < 65536); outerIpHdr->id = (uint16) atomic_add64(&vportStt->ipId, innerFrameLen); outerIpHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ? IP_DF_NBO : 0; outerIpHdr->ttl = tunKey->ttl? tunKey->ttl : 64; outerIpHdr->protocol = IPPROTO_TCP; outerIpHdr->check = 0; outerIpHdr->saddr = fwdInfo->srcIpAddr; outerIpHdr->daddr = tunKey->dst; /* L4 header */ RtlZeroMemory(outerTcpHdr, sizeof *outerTcpHdr); outerTcpHdr->source = htons(tunKey->flow_hash | 32768); outerTcpHdr->dest = htons(vportStt->dstPort); outerTcpHdr->seq = htonl((STT_HDR_LEN + innerFrameLen) << STT_SEQ_LEN_SHIFT); outerTcpHdr->ack_seq = htonl(atomic_inc64(&vportStt->ackNo)); outerTcpHdr->doff = sizeof(TCPHdr) >> 2; outerTcpHdr->psh = 1; outerTcpHdr->ack = 1; outerTcpHdr->window = (uint16) ~0; /* Calculate pseudo header chksum */ tcpChksumLen = sizeof(TCPHdr) + STT_HDR_LEN + innerFrameLen; ASSERT(tcpChksumLen < 65535); outerTcpHdr->check = IPPseudoChecksum(&fwdInfo->srcIpAddr,(uint32 *) &tunKey->dst, IPPROTO_TCP, (uint16) tcpChksumLen); sttHdr->version = 0; /* Set STT Header */ sttHdr->flags = 0; if (innerPartialChecksum) { sttHdr->flags |= STT_CSUM_PARTIAL; if (layers->isIPv4) { sttHdr->flags |= STT_PROTO_IPV4; } if (layers->isTcp) { sttHdr->flags |= STT_PROTO_TCP; } sttHdr->l4Offset = (UINT8) layers->l4Offset; sttHdr->mss = (UINT16) htons(mss); } else if (innerChecksumVerified) { sttHdr->flags = STT_CSUM_VERIFIED; sttHdr->l4Offset = 0; sttHdr->mss = 0; } sttHdr->reserved = 0; sttHdr->vlanTCI = 0; sttHdr->key = tunKey->tunnelId; /* Zero out stt padding */ *(uint16 *)(sttHdr + 1) = 0; /* Offload IP and TCP checksum */ ULONG tcpHeaderOffset = sizeof *outerEthHdr + outerIpHdr->ihl * 4; csumInfo.Value = 0; csumInfo.Transmit.IpHeaderChecksum = 1; csumInfo.Transmit.TcpChecksum = 1; csumInfo.Transmit.IsIPv4 = 1; csumInfo.Transmit.TcpHeaderOffset = tcpHeaderOffset; NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; UINT32 encapMss = OvsGetExternalMtu(switchContext) - sizeof(IPHdr) - sizeof(TCPHdr); if (ipTotalLen > encapMss) { lsoInfo.Value = 0; lsoInfo.LsoV2Transmit.TcpHeaderOffset = tcpHeaderOffset; lsoInfo.LsoV2Transmit.MSS = encapMss; lsoInfo.LsoV2Transmit.Type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; lsoInfo.LsoV2Transmit.IPVersion = NDIS_TCP_LARGE_SEND_OFFLOAD_IPv4; NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo) = lsoInfo.Value; } return STATUS_SUCCESS; ret_error: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; } /* *---------------------------------------------------------------------------- * OvsValidateTCPChecksum * Validate TCP checksum *---------------------------------------------------------------------------- */ static __inline NDIS_STATUS OvsValidateTCPChecksum(PNET_BUFFER_LIST curNbl, PNET_BUFFER curNb) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); /* Check if NIC has indicated TCP checksum failure */ if (csumInfo.Receive.TcpChecksumFailed) { return NDIS_STATUS_INVALID_PACKET; } UINT16 checkSum; /* Check if TCP Checksum has been calculated by NIC */ if (csumInfo.Receive.TcpChecksumSucceeded) { return NDIS_STATUS_SUCCESS; } EthHdr *eth = (EthHdr *)NdisGetDataBuffer(curNb, sizeof(EthHdr), NULL, 1, 0); if (eth->Type == ntohs(NDIS_ETH_TYPE_IPV4)) { IPHdr *ip = (IPHdr *)((PCHAR)eth + sizeof *eth); UINT32 l4Payload = ntohs(ip->tot_len) - ip->ihl * 4; TCPHdr *tcp = (TCPHdr *)((PCHAR)ip + ip->ihl * 4); checkSum = tcp->check; tcp->check = 0; tcp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr, IPPROTO_TCP, (UINT16)l4Payload); tcp->check = CalculateChecksumNB(curNb, (UINT16)(l4Payload), sizeof(EthHdr) + ip->ihl * 4); if (checkSum != tcp->check) { return NDIS_STATUS_INVALID_PACKET; } } else { OVS_LOG_ERROR("IPv6 on STT is not supported"); return NDIS_STATUS_INVALID_PACKET; } csumInfo.Receive.TcpChecksumSucceeded = 1; NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsInitSttDefragmentation * Initialize the components used by the stt lso defragmentation *---------------------------------------------------------------------------- */ NTSTATUS OvsInitSttDefragmentation() { NTSTATUS status; HANDLE threadHandle = NULL; /* Init the sync-lock */ NdisAllocateSpinLock(&OvsSttSpinLock); /* Init the Hash Buffer */ OvsSttPktFragHash = OvsAllocateMemoryWithTag(sizeof(LIST_ENTRY) * STT_HASH_TABLE_SIZE, OVS_STT_POOL_TAG); if (OvsSttPktFragHash == NULL) { NdisFreeSpinLock(&OvsSttSpinLock); return STATUS_INSUFFICIENT_RESOURCES; } for (int i = 0; i < STT_HASH_TABLE_SIZE; i++) { InitializeListHead(&OvsSttPktFragHash[i]); } /* Init Defrag Cleanup Thread */ KeInitializeEvent(&sttDefragThreadCtx.event, NotificationEvent, FALSE); status = PsCreateSystemThread(&threadHandle, SYNCHRONIZE, NULL, NULL, NULL, OvsSttDefragCleaner, &sttDefragThreadCtx); if (status != STATUS_SUCCESS) { OvsCleanupSttDefragmentation(); return status; } ObReferenceObjectByHandle(threadHandle, SYNCHRONIZE, NULL, KernelMode, &sttDefragThreadCtx.threadObject, NULL); ZwClose(threadHandle); threadHandle = NULL; return STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsCleanupSttDefragmentation * Cleanup memory and thread that were spawned for STT LSO defragmentation *---------------------------------------------------------------------------- */ VOID OvsCleanupSttDefragmentation(VOID) { NdisAcquireSpinLock(&OvsSttSpinLock); sttDefragThreadCtx.exit = 1; KeSetEvent(&sttDefragThreadCtx.event, 0, FALSE); NdisReleaseSpinLock(&OvsSttSpinLock); KeWaitForSingleObject(sttDefragThreadCtx.threadObject, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(sttDefragThreadCtx.threadObject); if (OvsSttPktFragHash) { OvsFreeMemoryWithTag(OvsSttPktFragHash, OVS_STT_POOL_TAG); OvsSttPktFragHash = NULL; } NdisFreeSpinLock(&OvsSttSpinLock); } /* *---------------------------------------------------------------------------- * OvsSttDefragCleaner * Runs periodically and cleans up the buffer to remove expired segments *---------------------------------------------------------------------------- */ VOID OvsSttDefragCleaner(PVOID data) { POVS_STT_THREAD_CTX context = (POVS_STT_THREAD_CTX)data; PLIST_ENTRY link, next; POVS_STT_PKT_ENTRY entry; BOOLEAN success = TRUE; while (success) { NdisAcquireSpinLock(&OvsSttSpinLock); if (context->exit) { NdisReleaseSpinLock(&OvsSttSpinLock); break; } /* Set the timeout for the thread and cleanup */ UINT64 currentTime, threadSleepTimeout; NdisGetCurrentSystemTime((LARGE_INTEGER *)¤tTime); threadSleepTimeout = currentTime + STT_CLEANUP_INTERVAL; for (int i = 0; i < STT_HASH_TABLE_SIZE; i++) { LIST_FORALL_SAFE(&OvsSttPktFragHash[i], link, next) { entry = CONTAINING_RECORD(link, OVS_STT_PKT_ENTRY, link); if (entry->timeout < currentTime) { RemoveEntryList(&entry->link); OvsFreeMemoryWithTag(entry->packetBuf, OVS_STT_POOL_TAG); OvsFreeMemoryWithTag(entry, OVS_STT_POOL_TAG); } } } NdisReleaseSpinLock(&OvsSttSpinLock); KeWaitForSingleObject(&context->event, Executive, KernelMode, FALSE, (LARGE_INTEGER *)&threadSleepTimeout); } PsTerminateSystemThread(STATUS_SUCCESS); } static OVS_STT_PKT_KEY OvsGeneratePacketKey(IPHdr *ipHdr, TCPHdr *tcpHdr) { OVS_STT_PKT_KEY key; key.sAddr = ipHdr->saddr; key.dAddr = ipHdr->daddr; key.ackSeq = ntohl(tcpHdr->ack_seq); return key; } static UINT32 OvsSttGetPktHash(OVS_STT_PKT_KEY *pktKey) { UINT32 arr[3]; arr[0] = pktKey->ackSeq; arr[1] = pktKey->dAddr; arr[2] = pktKey->sAddr; return OvsJhashWords(arr, 3, OVS_HASH_BASIS); } static VOID * OvsLookupPktFrag(OVS_STT_PKT_KEY *pktKey, UINT32 hash) { PLIST_ENTRY link; POVS_STT_PKT_ENTRY entry; LIST_FORALL(&OvsSttPktFragHash[hash & STT_HASH_TABLE_MASK], link) { entry = CONTAINING_RECORD(link, OVS_STT_PKT_ENTRY, link); if (entry->ovsPktKey.ackSeq == pktKey->ackSeq && entry->ovsPktKey.dAddr == pktKey->dAddr && entry->ovsPktKey.sAddr == pktKey->sAddr) { return entry; } } return NULL; } /* * -------------------------------------------------------------------------- * OvsSttReassemble -- * Reassemble an LSO packet from multiple STT-Fragments. * -------------------------------------------------------------------------- */ PNET_BUFFER_LIST OvsSttReassemble(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, IPHdr *ipHdr, TCPHdr *tcp, SttHdr *newSttHdr, UINT16 payloadLen) { UINT32 seq = ntohl(tcp->seq); UINT32 innerPacketLen = (seq >> STT_SEQ_LEN_SHIFT) - STT_HDR_LEN; UINT32 segOffset = STT_SEGMENT_OFF(seq); UINT32 offset = segOffset == 0 ? 0 : segOffset - STT_HDR_LEN; UINT32 startOffset = 0; OVS_STT_PKT_ENTRY *pktFragEntry; PNET_BUFFER_LIST targetPNbl = NULL; BOOLEAN lastPacket = FALSE; PNET_BUFFER sourceNb; UINT32 fragmentLength = payloadLen; SttHdr stt; SttHdr *sttHdr = NULL; sourceNb = NET_BUFFER_LIST_FIRST_NB(curNbl); /* XXX optimize this lock */ NdisAcquireSpinLock(&OvsSttSpinLock); /* If this is the first fragment, copy the STT header */ if (segOffset == 0) { sttHdr = NdisGetDataBuffer(sourceNb, sizeof(SttHdr), &stt, 1, 0); if (sttHdr == NULL) { OVS_LOG_ERROR("Unable to retrieve STT header"); return NULL; } fragmentLength = fragmentLength - STT_HDR_LEN; startOffset = startOffset + STT_HDR_LEN; } /* Lookup fragment */ OVS_STT_PKT_KEY pktKey = OvsGeneratePacketKey(ipHdr, tcp); UINT32 hash = OvsSttGetPktHash(&pktKey); pktFragEntry = OvsLookupPktFrag(&pktKey, hash); if (pktFragEntry == NULL) { /* Create a new Packet Entry */ POVS_STT_PKT_ENTRY entry; entry = OvsAllocateMemoryWithTag(sizeof(OVS_STT_PKT_ENTRY), OVS_STT_POOL_TAG); RtlZeroMemory(entry, sizeof (OVS_STT_PKT_ENTRY)); /* Update Key, timestamp and recvdLen */ NdisMoveMemory(&entry->ovsPktKey, &pktKey, sizeof (OVS_STT_PKT_KEY)); entry->recvdLen = fragmentLength; UINT64 currentTime; NdisGetCurrentSystemTime((LARGE_INTEGER *) ¤tTime); entry->timeout = currentTime + STT_ENTRY_TIMEOUT; if (segOffset == 0) { entry->sttHdr = *sttHdr; } /* Copy the data from Source to new buffer */ entry->packetBuf = OvsAllocateMemoryWithTag(innerPacketLen, OVS_STT_POOL_TAG); if (OvsGetPacketBytes(curNbl, fragmentLength, startOffset, entry->packetBuf + offset) == NULL) { OVS_LOG_ERROR("Error when obtaining bytes from Packet"); goto handle_error; } /* Insert the entry in the Static Buffer */ InsertHeadList(&OvsSttPktFragHash[hash & STT_HASH_TABLE_MASK], &entry->link); } else { /* Add to recieved length to identify if this is the last fragment */ pktFragEntry->recvdLen += fragmentLength; lastPacket = (pktFragEntry->recvdLen == innerPacketLen); if (segOffset == 0) { pktFragEntry->sttHdr = *sttHdr; } /* Copy the fragment data from Source to existing buffer */ if (OvsGetPacketBytes(curNbl, fragmentLength, startOffset, pktFragEntry->packetBuf + offset) == NULL) { OVS_LOG_ERROR("Error when obtaining bytes from Packet"); goto handle_error; } } handle_error: if (lastPacket) { /* Retrieve the original STT header */ NdisMoveMemory(newSttHdr, &pktFragEntry->sttHdr, sizeof (SttHdr)); targetPNbl = OvsAllocateNBLFromBuffer(switchContext, pktFragEntry->packetBuf, innerPacketLen); /* Delete this entry and free up the memory/ */ RemoveEntryList(&pktFragEntry->link); OvsFreeMemoryWithTag(pktFragEntry->packetBuf, OVS_STT_POOL_TAG); OvsFreeMemoryWithTag(pktFragEntry, OVS_STT_POOL_TAG); } NdisReleaseSpinLock(&OvsSttSpinLock); return lastPacket ? targetPNbl : NULL; } VOID OvsDecapSetOffloads(PNET_BUFFER_LIST curNbl, SttHdr *sttHdr) { if ((sttHdr->flags & STT_CSUM_VERIFIED) || !(sttHdr->flags & STT_CSUM_PARTIAL)) { return; } UINT8 protoType; NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = 0; csumInfo.Transmit.IpHeaderChecksum = 0; csumInfo.Transmit.TcpHeaderOffset = sttHdr->l4Offset; protoType = sttHdr->flags & STT_PROTO_TYPES; switch (protoType) { case (STT_PROTO_IPV4 | STT_PROTO_TCP): /* TCP/IPv4 */ csumInfo.Transmit.IsIPv4 = 1; csumInfo.Transmit.TcpChecksum = 1; break; case STT_PROTO_TCP: /* TCP/IPv6 */ csumInfo.Transmit.IsIPv6 = 1; csumInfo.Transmit.TcpChecksum = 1; break; case STT_PROTO_IPV4: /* UDP/IPv4 */ csumInfo.Transmit.IsIPv4 = 1; csumInfo.Transmit.UdpChecksum = 1; break; default: /* UDP/IPv6 */ csumInfo.Transmit.IsIPv6 = 1; csumInfo.Transmit.UdpChecksum = 1; } NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; if (sttHdr->mss) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO lsoInfo; lsoInfo.Value = 0; lsoInfo.LsoV2Transmit.TcpHeaderOffset = sttHdr->l4Offset; lsoInfo.LsoV2Transmit.MSS = ETH_DEFAULT_MTU - sizeof(IPHdr) - sizeof(TCPHdr); lsoInfo.LsoV2Transmit.Type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; if (sttHdr->flags & STT_PROTO_IPV4) { lsoInfo.LsoV2Transmit.IPVersion = NDIS_TCP_LARGE_SEND_OFFLOAD_IPv4; } else { lsoInfo.LsoV2Transmit.IPVersion = NDIS_TCP_LARGE_SEND_OFFLOAD_IPv6; } NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo) = lsoInfo.Value; } } /* * -------------------------------------------------------------------------- * OvsDecapStt -- * Decapsulates an STT packet. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsDecapStt(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status = NDIS_STATUS_FAILURE; PNET_BUFFER curNb, newNb; IPHdr *ipHdr; char *ipBuf[sizeof(IPHdr)]; SttHdr stt; SttHdr *sttHdr; char *sttBuf[STT_HDR_LEN]; UINT32 advanceCnt, hdrLen; BOOLEAN isLsoPacket = FALSE; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); ASSERT(NET_BUFFER_NEXT_NB(curNb) == NULL); /* Validate the TCP Checksum */ status = OvsValidateTCPChecksum(curNbl, curNb); if (status != NDIS_STATUS_SUCCESS) { return status; } /* Skip Eth header */ hdrLen = sizeof(EthHdr); NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL); advanceCnt = hdrLen; ipHdr = NdisGetDataBuffer(curNb, sizeof *ipHdr, (PVOID) &ipBuf, 1 /*no align*/, 0); ASSERT(ipHdr); TCPHdr *tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4); /* Skip IP & TCP headers */ hdrLen = sizeof(IPHdr) + sizeof(TCPHdr), NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL); advanceCnt += hdrLen; UINT32 seq = ntohl(tcp->seq); UINT32 totalLen = (seq >> STT_SEQ_LEN_SHIFT); UINT16 payloadLen = (UINT16)ntohs(ipHdr->tot_len) - (ipHdr->ihl * 4) - (sizeof * tcp); /* Check if incoming packet requires reassembly */ if (totalLen != payloadLen) { sttHdr = &stt; PNET_BUFFER_LIST pNbl = OvsSttReassemble(switchContext, curNbl, ipHdr, tcp, sttHdr, payloadLen); if (pNbl == NULL) { return NDIS_STATUS_SUCCESS; } *newNbl = pNbl; isLsoPacket = TRUE; } else { /* STT Header */ sttHdr = NdisGetDataBuffer(curNb, sizeof *sttHdr, (PVOID) &sttBuf, 1 /*no align*/, 0); /* Skip stt header, DataOffset points to inner pkt now. */ hdrLen = STT_HDR_LEN; NdisAdvanceNetBufferDataStart(curNb, hdrLen, FALSE, NULL); advanceCnt += hdrLen; *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, 0, FALSE /*copy NBL info*/); } if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to allocate a new cloned NBL"); return NDIS_STATUS_RESOURCES; } status = NdisRetreatNetBufferDataStart(curNb, advanceCnt, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { OvsCompleteNBL(switchContext, *newNbl, TRUE); return NDIS_STATUS_FAILURE; } newNb = NET_BUFFER_LIST_FIRST_NB(*newNbl); ASSERT(sttHdr); /* Initialize the tunnel key */ tunKey->dst = ipHdr->daddr; tunKey->src = ipHdr->saddr; tunKey->tunnelId = sttHdr->key; tunKey->flags = OVS_TNL_F_KEY; tunKey->tos = ipHdr->tos; tunKey->ttl = ipHdr->ttl; tunKey->pad = 0; /* Set Checksum and LSO offload flags */ OvsDecapSetOffloads(*newNbl, sttHdr); return NDIS_STATUS_SUCCESS; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Tunnel.c0000644000000000000000000000013213534540071022063 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.453851008 openvswitch-2.5.9/datapath-windows/ovsext/Tunnel.c0000644000175000017500000002660013534540071023555 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * OvsTunnel.c * WFP Classified callback function and Action code for injecting a packet to the vswitch */ #include "precomp.h" #pragma warning(push) #pragma warning(disable:4201) // unnamed struct/union #include #pragma warning(pop) #pragma warning( push ) #pragma warning( disable:4127 ) #include #include "Tunnel.h" #include "Switch.h" #include "Vport.h" #include "Event.h" #include "User.h" #include "Vxlan.h" #include "PacketIO.h" #include "NetProto.h" #include "Flow.h" extern POVS_SWITCH_CONTEXT gOvsSwitchContext; static NTSTATUS OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl, OVS_TUNNEL_PENDED_PACKET *packet); VOID OvsAcquireDatapathRead(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState, BOOLEAN dispatch); VOID OvsAcquireDatapathWrite(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState, BOOLEAN dispatch); VOID OvsReleaseDatapath(OVS_DATAPATH *datapath, LOCK_STATE_EX *lockState); NTSTATUS OvsTunnelNotify(FWPS_CALLOUT_NOTIFY_TYPE notifyType, const GUID *filterKey, const FWPS_FILTER *filter) { UNREFERENCED_PARAMETER(notifyType); UNREFERENCED_PARAMETER(filterKey); UNREFERENCED_PARAMETER(filter); return STATUS_SUCCESS; } static NTSTATUS OvsTunnelAnalyzePacket(OVS_TUNNEL_PENDED_PACKET *packet) { NTSTATUS status = STATUS_SUCCESS; UINT32 packetLength = 0; ULONG bytesCopied = 0; NET_BUFFER_LIST *copiedNBL = NULL; NET_BUFFER *netBuffer; NDIS_STATUS ndisStatus; /* * For inbound net buffer list, we can assume it contains only one * net buffer (unless it was an re-assembeled fragments). in both cases * the first net buffer should include all headers, we assert if the retreat fails */ netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList); /* Drop the packet from the host stack */ packet->classifyOut->actionType = FWP_ACTION_BLOCK; packet->classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; /* Adjust the net buffer list offset to the start of the IP header */ ndisStatus = NdisRetreatNetBufferDataStart(netBuffer, packet->ipHeaderSize + packet->transportHeaderSize, 0, NULL); ASSERT(ndisStatus == NDIS_STATUS_SUCCESS); /* Single NBL element for WFP */ ASSERT(packet->netBufferList->Next == NULL); /* Note that the copy will inherit the original net buffer list's offset */ packetLength = NET_BUFFER_DATA_LENGTH(netBuffer); copiedNBL = OvsAllocateVariableSizeNBL(gOvsSwitchContext, packetLength, OVS_DEFAULT_HEADROOM_SIZE); if (copiedNBL == NULL) { goto analyzeDone; } status = NdisCopyFromNetBufferToNetBuffer(NET_BUFFER_LIST_FIRST_NB(copiedNBL), 0, packetLength, netBuffer, 0, &bytesCopied); if (status != NDIS_STATUS_SUCCESS || packetLength != bytesCopied) { goto analyzeFreeNBL; } status = OvsInjectPacketThroughActions(copiedNBL, packet); goto analyzeDone; /* Undo the adjustment on the original net buffer list */ analyzeFreeNBL: OvsCompleteNBL(gOvsSwitchContext, copiedNBL, TRUE); analyzeDone: NdisAdvanceNetBufferDataStart(netBuffer, packet->transportHeaderSize + packet->ipHeaderSize, FALSE, NULL); return status; } /* * -------------------------------------------------------------------------- * This is the classifyFn function of the datagram-data callout. It * allocates a packet structure to store the classify and meta data and * it references the net buffer list for out-of-band modification and * re-injection. The packet structure will be queued to the global packet * queue. The worker thread will then be signaled, if idle, to process * the queue. * -------------------------------------------------------------------------- */ VOID OvsTunnelClassify(const FWPS_INCOMING_VALUES *inFixedValues, const FWPS_INCOMING_METADATA_VALUES *inMetaValues, VOID *layerData, const VOID *classifyContext, const FWPS_FILTER *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT *classifyOut) { OVS_TUNNEL_PENDED_PACKET packetStorage; OVS_TUNNEL_PENDED_PACKET *packet = &packetStorage; FWP_DIRECTION direction; UNREFERENCED_PARAMETER(classifyContext); UNREFERENCED_PARAMETER(filter); UNREFERENCED_PARAMETER(flowContext); ASSERT(layerData != NULL); /* We don't have the necessary right to alter the packet flow */ if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0) { /* XXX TBD revisit protect against other filters owning this packet */ ASSERT(FALSE); goto Exit; } RtlZeroMemory(packet, sizeof(OVS_TUNNEL_PENDED_PACKET)); /* classifyOut cannot be accessed from a different thread context */ packet->classifyOut = classifyOut; if (inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4) { direction = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].\ value.uint32; } else { ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6); direction = inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].\ value.uint32; } packet->netBufferList = layerData; ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_COMPARTMENT_ID)); ASSERT(direction == FWP_DIRECTION_INBOUND); ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( inMetaValues, FWPS_METADATA_FIELD_IP_HEADER_SIZE)); ASSERT(FWPS_IS_METADATA_FIELD_PRESENT( inMetaValues, FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)); packet->ipHeaderSize = inMetaValues->ipHeaderSize; packet->transportHeaderSize = inMetaValues->transportHeaderSize; ASSERT(inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8 == IPPROTO_UDP ); OvsTunnelAnalyzePacket(packet); Exit: ; } static NTSTATUS OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl, OVS_TUNNEL_PENDED_PACKET *packet) { NTSTATUS status = STATUS_SUCCESS; OvsIPv4TunnelKey tunnelKey; NET_BUFFER *pNb; ULONG sendCompleteFlags = 0; BOOLEAN dispatch; PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail; LOCK_STATE_EX lockState, dpLockState; LIST_ENTRY missedPackets; OvsCompletionList completionList; KIRQL irql; ULONG SendFlags = NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP; OVS_DATAPATH *datapath = NULL; ASSERT(gOvsSwitchContext); datapath = &gOvsSwitchContext->datapath; /* Fill the tunnel key */ status = OvsSlowPathDecapVxlan(pNbl, &tunnelKey); if(!NT_SUCCESS(status)) { goto dropit; } pNb = NET_BUFFER_LIST_FIRST_NB(pNbl); NdisAdvanceNetBufferDataStart(pNb, packet->transportHeaderSize + packet->ipHeaderSize + sizeof(VXLANHdr), FALSE, NULL); /* Most likely (always) dispatch irql */ irql = KeGetCurrentIrql(); /* dispatch is used for datapath lock as well */ dispatch = (irql == DISPATCH_LEVEL) ? NDIS_RWL_AT_DISPATCH_LEVEL : 0; if (dispatch) { sendCompleteFlags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL; } InitializeListHead(&missedPackets); OvsInitCompletionList(&completionList, gOvsSwitchContext, sendCompleteFlags); { POVS_VPORT_ENTRY vport; UINT32 portNo; OVS_PACKET_HDR_INFO layers; OvsFlowKey key; UINT64 hash; PNET_BUFFER curNb; OvsFlow *flow; fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl); /* * XXX WFP packets contain a single NBL structure. * Reassembeled packet "may" have multiple NBs, however, a simple test shows * that the packet still has a single NB (after reassemble) * We still need to check if the Ethernet header of the innet packet is in a single MD */ curNb = NET_BUFFER_LIST_FIRST_NB(pNbl); ASSERT(curNb->Next == NULL); NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, dispatch); /* Lock the flowtable for the duration of accessing the flow */ OvsAcquireDatapathRead(datapath, &dpLockState, NDIS_RWL_AT_DISPATCH_LEVEL); SendFlags |= NDIS_SEND_FLAGS_DISPATCH_LEVEL; vport = OvsFindTunnelVportByDstPort(gOvsSwitchContext, htons(tunnelKey.dst_port), OVS_VPORT_TYPE_VXLAN); if (vport == NULL){ status = STATUS_UNSUCCESSFUL; goto unlockAndDrop; } ASSERT(vport->ovsType == OVS_VPORT_TYPE_VXLAN); portNo = vport->portNo; status = OvsExtractFlow(pNbl, portNo, &key, &layers, &tunnelKey); if (status != NDIS_STATUS_SUCCESS) { goto unlockAndDrop; } flow = OvsLookupFlow(datapath, &key, &hash, FALSE); if (flow) { OvsFlowUsed(flow, pNbl, &layers); datapath->hits++; OvsActionsExecute(gOvsSwitchContext, &completionList, pNbl, portNo, SendFlags, &key, &hash, &layers, flow->actions, flow->actionsLen); OvsReleaseDatapath(datapath, &dpLockState); } else { POVS_PACKET_QUEUE_ELEM elem; datapath->misses++; elem = OvsCreateQueueNlPacket(NULL, 0, OVS_PACKET_CMD_MISS, vport, &key, pNbl, curNb, TRUE, &layers); if (elem) { /* Complete the packet since it was copied to user buffer. */ InsertTailList(&missedPackets, &elem->link); OvsQueuePackets(&missedPackets, 1); } else { status = STATUS_INSUFFICIENT_RESOURCES; } goto unlockAndDrop; } NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); } return status; unlockAndDrop: OvsReleaseDatapath(datapath, &dpLockState); NdisReleaseRWLock(gOvsSwitchContext->dispatchLock, &lockState); dropit: pNbl = OvsCompleteNBL(gOvsSwitchContext, pNbl, TRUE); ASSERT(pNbl == NULL); return status; } #pragma warning(pop) openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Stt.h0000644000000000000000000000013213534540071021375 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.449850978 openvswitch-2.5.9/datapath-windows/ovsext/Stt.h0000644000175000017500000000637113534540071023072 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OVS_STT_H_ #define __OVS_STT_H_ 1 #define STT_TCP_PORT 7471 #define STT_TCP_PORT_NBO 0x2f1d #define MAX_IP_TOTAL_LEN 65535 // STT defines. #define STT_SEQ_LEN_SHIFT 16 #define STT_SEQ_OFFSET_MASK ((1 << STT_SEQ_LEN_SHIFT) - 1) #define STT_FRAME_LEN(seq) ((seq) >> STT_SEQ_LEN_SHIFT) #define STT_SEGMENT_OFF(seq) ((seq) & STT_SEQ_OFFSET_MASK) #define STT_CSUM_VERIFIED (1 << 0) #define STT_CSUM_PARTIAL (1 << 1) #define STT_PROTO_IPV4 (1 << 2) #define STT_PROTO_TCP (1 << 3) #define STT_PROTO_TYPES (STT_PROTO_IPV4 | STT_PROTO_TCP) #define STT_HASH_TABLE_SIZE ((UINT32)1 << 10) #define STT_HASH_TABLE_MASK (STT_HASH_TABLE_SIZE - 1) #define STT_ENTRY_TIMEOUT 300000000 // 30s #define STT_CLEANUP_INTERVAL 300000000 // 30s #define STT_ETH_PAD 2 typedef struct SttHdr { UINT8 version; UINT8 flags; UINT8 l4Offset; UINT8 reserved; UINT16 mss; UINT16 vlanTCI; UINT64 key; } SttHdr, *PSttHdr; #define STT_HDR_LEN (sizeof(SttHdr) + STT_ETH_PAD) typedef struct _OVS_STT_VPORT { UINT16 dstPort; UINT64 ackNo; UINT64 ipId; UINT64 inPkts; UINT64 outPkts; UINT64 slowInPkts; UINT64 slowOutPkts; } OVS_STT_VPORT, *POVS_STT_VPORT; typedef struct _OVS_STT_PKT_KEY { UINT32 sAddr; UINT32 dAddr; UINT32 ackSeq; } OVS_STT_PKT_KEY, *POVS_STT_PKT_KEY; typedef struct _OVS_STT_PKT_ENTRY { OVS_STT_PKT_KEY ovsPktKey; UINT64 timeout; UINT32 recvdLen; SttHdr sttHdr; PCHAR packetBuf; LIST_ENTRY link; } OVS_STT_PKT_ENTRY, *POVS_STT_PKT_ENTRY; typedef struct _OVS_STT_THREAD_CTX { KEVENT event; PVOID threadObject; UINT32 exit; } OVS_STT_THREAD_CTX, *POVS_STT_THREAD_CTX; NTSTATUS OvsInitSttTunnel(POVS_VPORT_ENTRY vport, UINT16 udpDestPort); VOID OvsCleanupSttTunnel(POVS_VPORT_ENTRY vport); NDIS_STATUS OvsEncapStt(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl); NDIS_STATUS OvsDecapStt(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl); NTSTATUS OvsInitSttDefragmentation(); VOID OvsCleanupSttDefragmentation(VOID); static __inline UINT32 OvsGetSttTunHdrSize(VOID) { return sizeof (EthHdr) + sizeof(IPHdr) + sizeof(TCPHdr) + STT_HDR_LEN; } #endif /*__OVS_STT_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Vport.h0000644000000000000000000000013213534540071021735 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.465851096 openvswitch-2.5.9/datapath-windows/ovsext/Vport.h0000644000175000017500000002411313534540071023424 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __VPORT_H_ #define __VPORT_H_ 1 #include "Gre.h" #include "Stt.h" #include "Switch.h" #include "VxLan.h" #define OVS_MAX_DPPORTS MAXUINT16 #define OVS_DPPORT_NUMBER_INVALID OVS_MAX_DPPORTS /* * The local port (0) is a reserved port, that is not allowed to be be * created by the netlink command vport add. On linux, this port is created * at netlink command datapath new. However, on windows, we do not need to * create it, and more, we shouldn't. The userspace attempts to create two * internal vports, the LOCAL port (0) and the internal port (with any other * port number). The non-LOCAL internal port is used in the userspace when it * requests the internal port. */ #define OVS_DPPORT_NUMBER_LOCAL 0 /* * A Vport, or Virtual Port, is a port on the OVS. It can be one of the * following types. Some of the Vports are "real" ports on the hyper-v switch, * and some are not: * - VIF port (VM's NIC) * - External Adapters (physical NIC) * - Internal Adapter (Virtual adapter exposed on the host). * - Tunnel ports created by OVS userspace. */ typedef enum { OVS_STATE_UNKNOWN, OVS_STATE_PORT_CREATED, OVS_STATE_NIC_CREATED, OVS_STATE_CONNECTED, OVS_STATE_PORT_TEAR_DOWN, OVS_STATE_PORT_DELETED, } OVS_VPORT_STATE; typedef struct _OVS_VPORT_STATS { UINT64 rxPackets; UINT64 txPackets; UINT64 rxBytes; UINT64 txBytes; } OVS_VPORT_STATS; typedef struct _OVS_VPORT_ERR_STATS { UINT64 rxErrors; UINT64 txErrors; UINT64 rxDropped; UINT64 txDropped; } OVS_VPORT_ERR_STATS; /* used for vport netlink commands. */ typedef struct _OVS_VPORT_FULL_STATS { OVS_VPORT_STATS; OVS_VPORT_ERR_STATS; }OVS_VPORT_FULL_STATS; /* * Each internal, external adapter or vritual adapter has * one vport entry. In addition, we have one vport for each * tunnel type, such as vxlan, gre */ typedef struct _OVS_VPORT_ENTRY { LIST_ENTRY ovsNameLink; LIST_ENTRY portIdLink; LIST_ENTRY portNoLink; LIST_ENTRY tunnelVportLink; OVS_VPORT_STATE ovsState; OVS_VPORT_TYPE ovsType; OVS_VPORT_STATS stats; OVS_VPORT_ERR_STATS errStats; UINT32 portNo; UINT32 mtu; /* ovsName is the ovs (datapath) port name - it is null terminated. */ CHAR ovsName[OVS_MAX_PORT_NAME_LENGTH]; PVOID priv; NDIS_SWITCH_PORT_ID portId; NDIS_SWITCH_NIC_INDEX nicIndex; NDIS_SWITCH_NIC_TYPE nicType; UINT16 numaNodeId; NDIS_SWITCH_PORT_STATE portState; NDIS_SWITCH_NIC_STATE nicState; NDIS_SWITCH_PORT_TYPE portType; UINT8 permMacAddress[ETH_ADDR_LEN]; UINT8 currMacAddress[ETH_ADDR_LEN]; UINT8 vmMacAddress[ETH_ADDR_LEN]; NDIS_SWITCH_PORT_NAME hvPortName; IF_COUNTED_STRING portFriendlyName; NDIS_SWITCH_NIC_NAME nicName; NDIS_VM_NAME vmName; GUID netCfgInstanceId; /* * OVS userpace has a notion of bridges which basically defines an * L2-domain. Each "bridge" has an "internal" port of type * OVS_VPORT_TYPE_INTERNAL. Such a port is connected to the OVS datapath in * one end, and the other end is a virtual adapter on the hypervisor host. * This is akin to the Hyper-V "internal" NIC. It is intuitive to map the * Hyper-V "internal" NIC to the OVS bridge's "internal" port, but there's * only one Hyper-V NIC but multiple bridges. To support multiple OVS bridge * "internal" ports, we use the flag 'isBridgeInternal' in each vport. We * support addition of multiple bridge-internal ports. A vport with * 'isBridgeInternal' == TRUE is a dummy port and has no backing currently. * If a flow actions specifies the output port to be a bridge-internal port, * the port is silently ignored. */ BOOLEAN isBridgeInternal; BOOLEAN isExternal; UINT32 upcallPid; /* netlink upcall port id */ PNL_ATTR portOptions; BOOLEAN isAbsentOnHv; /* Is this port present on the Hyper-V switch? */ } OVS_VPORT_ENTRY, *POVS_VPORT_ENTRY; struct _OVS_SWITCH_CONTEXT; POVS_VPORT_ENTRY OvsFindVportByPortNo(POVS_SWITCH_CONTEXT switchContext, UINT32 portNo); /* "name" is null-terminated */ POVS_VPORT_ENTRY OvsFindVportByOvsName(POVS_SWITCH_CONTEXT switchContext, PSTR name); POVS_VPORT_ENTRY OvsFindVportByHvNameA(POVS_SWITCH_CONTEXT switchContext, PSTR name); POVS_VPORT_ENTRY OvsFindVportByPortIdAndNicIndex(POVS_SWITCH_CONTEXT switchContext, NDIS_SWITCH_PORT_ID portId, NDIS_SWITCH_NIC_INDEX index); POVS_VPORT_ENTRY OvsFindTunnelVportByDstPort(POVS_SWITCH_CONTEXT switchContext, UINT16 dstPort, OVS_VPORT_TYPE ovsVportType); POVS_VPORT_ENTRY OvsFindTunnelVportByPortType(POVS_SWITCH_CONTEXT switchContext, OVS_VPORT_TYPE ovsPortType); NDIS_STATUS OvsAddConfiguredSwitchPorts(struct _OVS_SWITCH_CONTEXT *switchContext); NDIS_STATUS OvsInitConfiguredSwitchNics(struct _OVS_SWITCH_CONTEXT *switchContext); VOID OvsClearAllSwitchVports(struct _OVS_SWITCH_CONTEXT *switchContext); NDIS_STATUS HvCreateNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam); NDIS_STATUS HvCreatePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam, NDIS_SWITCH_NIC_INDEX nicIndex); NDIS_STATUS HvUpdatePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam); VOID HvTeardownPort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam); VOID HvDeletePort(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_PARAMETERS portParam); VOID HvConnectNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam); VOID HvUpdateNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam); VOID HvDeleteNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam); VOID HvDisconnectNic(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_PARAMETERS nicParam); static __inline BOOLEAN OvsIsTunnelVportType(OVS_VPORT_TYPE ovsType) { return ovsType == OVS_VPORT_TYPE_VXLAN || ovsType == OVS_VPORT_TYPE_STT || ovsType == OVS_VPORT_TYPE_GRE; } static __inline PVOID GetOvsVportPriv(POVS_VPORT_ENTRY ovsVport) { return ovsVport->priv; } static __inline BOOLEAN OvsIsInternalVportType(OVS_VPORT_TYPE ovsType) { return ovsType == OVS_VPORT_TYPE_INTERNAL; } static __inline BOOLEAN OvsIsVirtualExternalVport(POVS_VPORT_ENTRY vport) { return vport->nicType == NdisSwitchNicTypeExternal && vport->nicIndex == 0; } static __inline BOOLEAN OvsIsRealExternalVport(POVS_VPORT_ENTRY vport) { return vport->nicType == NdisSwitchNicTypeExternal && vport->nicIndex != 0; } static __inline BOOLEAN OvsIsBridgeInternalVport(POVS_VPORT_ENTRY vport) { ASSERT(vport->isBridgeInternal != TRUE || vport->ovsType == OVS_VPORT_TYPE_INTERNAL); return vport->isBridgeInternal == TRUE; } static __inline BOOLEAN OvsIsInternalNIC(NDIS_SWITCH_NIC_TYPE nicType) { return nicType == NdisSwitchNicTypeInternal; } static __inline BOOLEAN OvsIsRealExternalNIC(NDIS_SWITCH_NIC_TYPE nicType, NDIS_SWITCH_NIC_INDEX nicIndex) { return nicType == NdisSwitchNicTypeExternal && nicIndex != 0; } NTSTATUS OvsRemoveAndDeleteVport(PVOID usrParamsCtx, POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport, BOOLEAN hvDelete, BOOLEAN ovsDelete); static __inline POVS_VPORT_ENTRY OvsGetExternalVport(POVS_SWITCH_CONTEXT switchContext) { return switchContext->virtualExternalVport; } static __inline UINT32 OvsGetExternalMtu(POVS_SWITCH_CONTEXT switchContext) { ASSERT(OvsGetExternalVport(switchContext)); return ((POVS_VPORT_ENTRY) OvsGetExternalVport(switchContext))->mtu; } static __inline UINT16 GetPortFromPriv(POVS_VPORT_ENTRY vport) { UINT16 dstPort = 0; PVOID vportPriv = GetOvsVportPriv(vport); /* XXX would better to have a commom tunnel "parent" structure */ ASSERT(vportPriv); switch(vport->ovsType) { case OVS_VPORT_TYPE_GRE: break; case OVS_VPORT_TYPE_STT: dstPort = ((POVS_STT_VPORT)vportPriv)->dstPort; break; case OVS_VPORT_TYPE_VXLAN: dstPort = ((POVS_VXLAN_VPORT)vportPriv)->dstPort; break; default: ASSERT(! "Port is not a tunnel port"); } ASSERT(dstPort || vport->ovsType == OVS_VPORT_TYPE_GRE); return dstPort; } NDIS_STATUS InitOvsVportCommon(POVS_SWITCH_CONTEXT switchContext, POVS_VPORT_ENTRY vport); NTSTATUS OvsInitTunnelVport(PVOID usrParamsCtx, POVS_VPORT_ENTRY vport, OVS_VPORT_TYPE ovsType, UINT16 dstport); NTSTATUS OvsInitBridgeInternalVport(POVS_VPORT_ENTRY vport); POVS_VPORT_ENTRY OvsAllocateVport(VOID); #endif /* __VPORT_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Event.h0000644000000000000000000000013113534540071021703 xustar0029 mtime=1567801401.21367979 30 atime=1567801402.049685929 30 ctime=1567801424.413850713 openvswitch-2.5.9/datapath-windows/ovsext/Event.h0000644000175000017500000000333713534540071023400 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __EVENT_H_ #define __EVENT_H_ 1 typedef struct _OVS_EVENT_QUEUE_ELEM { LIST_ENTRY link; OVS_EVENT_ENTRY event; } OVS_EVENT_QUEUE_ELEM, *POVS_EVENT_QUEUE_ELEM; typedef struct _OVS_EVENT_QUEUE { LIST_ENTRY queueLink; LIST_ENTRY elemList; UINT32 mask; UINT16 numElems; BOOLEAN pollAll; PIRP pendingIrp; PVOID instance; } OVS_EVENT_QUEUE, *POVS_EVENT_QUEUE; NTSTATUS OvsInitEventQueue(VOID); VOID OvsCleanupEventQueue(VOID); struct _OVS_OPEN_INSTANCE; VOID OvsCleanupEvent(struct _OVS_OPEN_INSTANCE *instance); VOID OvsPostEvent(POVS_EVENT_ENTRY event); NTSTATUS OvsSubscribeEventIoctl(PFILE_OBJECT fileObject, PVOID inputBuffer, UINT32 inputLength); NTSTATUS OvsPollEventIoctl(PFILE_OBJECT fileObject, PVOID inputBuffer, UINT32 inputLength, PVOID outputBuffer, UINT32 outputLength, UINT32 *replyLen); NTSTATUS OvsWaitEventIoctl(PIRP irp, PFILE_OBJECT fileObject, PVOID inputBuffer, UINT32 inputLength); NTSTATUS OvsRemoveEventEntry(PVOID instance, POVS_EVENT_ENTRY entry); #endif /* __EVENT_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/PacketParser.c0000644000000000000000000000013213534540071023202 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.445850949 openvswitch-2.5.9/datapath-windows/ovsext/PacketParser.c0000644000175000017500000002334013534540071024672 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "PacketParser.h" //XXX consider moving to NdisGetDataBuffer. const VOID * OvsGetPacketBytes(const NET_BUFFER_LIST *nbl, UINT32 len, UINT32 srcOffset, VOID *storage) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(nbl); PMDL currentMdl; BOOLEAN firstMDL = TRUE; ULONG destOffset = 0; VOID *dest = storage; const UINT32 copyLen = len; ULONG packetLen; packetLen = NET_BUFFER_DATA_LENGTH(netBuffer); // Start copy from current MDL currentMdl = NET_BUFFER_CURRENT_MDL(netBuffer); // Data on current MDL may be offset from start of MDL while (destOffset < copyLen && currentMdl) { PUCHAR srcMemory = MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority); ULONG length = MmGetMdlByteCount(currentMdl); if (!srcMemory) { status = NDIS_STATUS_RESOURCES; break; } if (firstMDL) { ULONG mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(netBuffer); srcMemory += mdlOffset; length -= mdlOffset; firstMDL = FALSE; } length = MIN(length, packetLen); packetLen -= length; ASSERT((INT)packetLen >= 0); if (srcOffset >= length) { currentMdl = NDIS_MDL_LINKAGE(currentMdl); srcOffset -= length; continue; } else { srcMemory += srcOffset; length -= srcOffset; srcOffset = 0; } length = min(length, copyLen-destOffset); NdisMoveMemory((PUCHAR)dest+destOffset, srcMemory, length); destOffset += length; currentMdl = NDIS_MDL_LINKAGE(currentMdl); } if (destOffset == copyLen) { ASSERT(status == NDIS_STATUS_SUCCESS); return storage; } return NULL; } NDIS_STATUS OvsParseIPv6(const NET_BUFFER_LIST *packet, OvsFlowKey *key, POVS_PACKET_HDR_INFO layers) { UINT16 ofs = layers->l3Offset; IPv6Hdr ipv6HdrStorage; const IPv6Hdr *nh; UINT32 nextHdr; Ipv6Key *flow= &key->ipv6Key; ofs = layers->l3Offset; nh = OvsGetPacketBytes(packet, sizeof *nh, ofs, &ipv6HdrStorage); if (!nh) { return NDIS_STATUS_FAILURE; } nextHdr = nh->nexthdr; memcpy(&flow->ipv6Src, nh->saddr.s6_addr, 16); memcpy(&flow->ipv6Dst, nh->daddr.s6_addr, 16); flow->nwTos = ((nh->flow_lbl[0] & 0xF0) >> 4) | (nh->priority << 4); flow->ipv6Label = ((nh->flow_lbl[0] & 0x0F) << 16) | (nh->flow_lbl[1] << 8) | nh->flow_lbl[2]; flow->nwTtl = nh->hop_limit; flow->nwProto = SOCKET_IPPROTO_NONE; flow->nwFrag = OVS_FRAG_TYPE_NONE; // Parse extended headers and compute L4 offset ofs += sizeof(IPv6Hdr); for (;;) { if ((nextHdr != SOCKET_IPPROTO_HOPOPTS) && (nextHdr != SOCKET_IPPROTO_ROUTING) && (nextHdr != SOCKET_IPPROTO_DSTOPTS) && (nextHdr != SOCKET_IPPROTO_AH) && (nextHdr != SOCKET_IPPROTO_FRAGMENT)) { /* * It's either a terminal header (e.g., TCP, UDP) or one we * don't understand. In either case, we're done with the * packet, so use it to fill in 'nw_proto'. */ break; } if (nextHdr == SOCKET_IPPROTO_HOPOPTS || nextHdr == SOCKET_IPPROTO_ROUTING || nextHdr == SOCKET_IPPROTO_DSTOPTS || nextHdr == SOCKET_IPPROTO_AH) { IPv6ExtHdr extHdrStorage; const IPv6ExtHdr *extHdr; UINT8 len; extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs, &extHdrStorage); if (!extHdr) { return NDIS_STATUS_FAILURE; } len = extHdr->hdrExtLen; ofs += nextHdr == SOCKET_IPPROTO_AH ? (len + 2) * 4 : (len + 1) * 8; nextHdr = extHdr->nextHeader; if (OvsPacketLenNBL(packet) < ofs) { return NDIS_STATUS_FAILURE; } } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) { IPv6FragHdr fragHdrStorage; const IPv6FragHdr *fragHdr; fragHdr = OvsGetPacketBytes(packet, sizeof *fragHdr, ofs, &fragHdrStorage); if (!fragHdr) { return NDIS_STATUS_FAILURE; } nextHdr = fragHdr->nextHeader; ofs += sizeof *fragHdr; /* We only process the first fragment. */ if (fragHdr->offlg != htons(0)) { if ((fragHdr->offlg & IP6F_OFF_HOST_ORDER_MASK) == htons(0)) { flow->nwFrag = OVS_FRAG_TYPE_FIRST; } else { flow->nwFrag = OVS_FRAG_TYPE_LATER; nextHdr = SOCKET_IPPROTO_FRAGMENT; break; } } } } flow->nwProto = (UINT8)nextHdr; layers->l4Offset = ofs; return NDIS_STATUS_SUCCESS; } VOID OvsParseTcp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { TCPHdr tcpStorage; const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage); if (tcp) { flow->tpSrc = tcp->source; flow->tpDst = tcp->dest; layers->isTcp = 1; layers->l7Offset = layers->l4Offset + 4 * tcp->doff; } } VOID OvsParseSctp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { SCTPHdr sctpStorage; const SCTPHdr *sctp = OvsGetSctp(packet, layers->l4Offset, &sctpStorage); if (sctp) { flow->tpSrc = sctp->source; flow->tpDst = sctp->dest; layers->isSctp = 1; layers->l7Offset = layers->l4Offset + sizeof *sctp; } } VOID OvsParseUdp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers) { UDPHdr udpStorage; const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage); if (udp) { flow->tpSrc = udp->source; flow->tpDst = udp->dest; layers->isUdp = 1; if (udp->check == 0) { layers->udpCsumZero = 1; } layers->l7Offset = layers->l4Offset + sizeof *udp; } } NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER_LIST *packet, OvsFlowKey *key, POVS_PACKET_HDR_INFO layers) { UINT16 ofs = layers->l4Offset; ICMPHdr icmpStorage; const ICMPHdr *icmp; Icmp6Key *flow = &key->icmp6Key; memset(&flow->ndTarget, 0, sizeof(flow->ndTarget)); memset(flow->arpSha, 0, sizeof(flow->arpSha)); memset(flow->arpTha, 0, sizeof(flow->arpTha)); icmp = OvsGetIcmp(packet, ofs, &icmpStorage); if (!icmp) { return NDIS_STATUS_FAILURE; } ofs += sizeof *icmp; /* * The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ key->ipv6Key.l4.tpSrc = htons(icmp->type); key->ipv6Key.l4.tpDst = htons(icmp->code); if (icmp->code == 0 && (icmp->type == ND_NEIGHBOR_SOLICIT || icmp->type == ND_NEIGHBOR_ADVERT)) { struct in6_addr ndTargetStorage; const struct in6_addr *ndTarget; ndTarget = OvsGetPacketBytes(packet, sizeof *ndTarget, ofs, &ndTargetStorage); if (!ndTarget) { return NDIS_STATUS_FAILURE; } flow->ndTarget = *ndTarget; while ((UINT32)(ofs + 8) <= OvsPacketLenNBL(packet)) { /* * The minimum size of an option is 8 bytes, which also is * the size of Ethernet link-layer options. */ IPv6NdOptHdr ndOptStorage; const IPv6NdOptHdr *ndOpt; UINT16 optLen; ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs, &ndOptStorage); if (!ndOpt) { return NDIS_STATUS_FAILURE; } optLen = ndOpt->len * 8; if (!optLen || (UINT32)(ofs + optLen) > OvsPacketLenNBL(packet)) { goto invalid; } /* * Store the link layer address if the appropriate option is * provided. It is considered an error if the same link * layer option is specified twice. */ if (ndOpt->type == ND_OPT_SOURCE_LINKADDR && optLen == 8) { if (Eth_IsNullAddr(flow->arpSha)) { memcpy(flow->arpSha, ndOpt + 1, ETH_ADDR_LENGTH); } else { goto invalid; } } else if (ndOpt->type == ND_OPT_TARGET_LINKADDR && optLen == 8) { if (Eth_IsNullAddr(flow->arpTha)) { memcpy(flow->arpTha, ndOpt + 1, ETH_ADDR_LENGTH); } else { goto invalid; } } ofs += optLen; } } layers->l7Offset = ofs; return NDIS_STATUS_SUCCESS; invalid: memset(&flow->ndTarget, 0, sizeof(flow->ndTarget)); memset(flow->arpSha, 0, sizeof(flow->arpSha)); memset(flow->arpTha, 0, sizeof(flow->arpTha)); return NDIS_STATUS_FAILURE; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Checksum.h0000644000000000000000000000013013534540071022363 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 29 ctime=1567801424.36185033 openvswitch-2.5.9/datapath-windows/ovsext/Checksum.h0000644000175000017500000000325413534540071024057 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __CHECKSUM_H_ #define __CHECKSUM_H_ 1 typedef union _OVS_PACKET_HDR_INFO *POVS_PACKET_HDR_INFO; UINT16 CalculateChecksum(UINT8 *ptr, UINT16 length, UINT16 initial); UINT16 CopyAndCalculateChecksum(UINT8 *dst, UINT8 *src, UINT16 length, UINT16 initial); UINT16 IPChecksum(UINT8 *ipHdr, UINT16 length, UINT16 initial); UINT16 IPPseudoChecksum(UINT32 *src, UINT32 *dst, UINT8 protocol, UINT16 totalLength); UINT16 IPv6PseudoChecksum(UINT32 *src, UINT32 *dst, UINT8 protocol, UINT16 totalLength); UINT16 ChecksumUpdate32(UINT16 oldSum, UINT32 prev, UINT32 newValue); UINT16 ChecksumUpdate16(UINT16 oldSum, UINT16 prev, UINT16 newValue); UINT16 CalculateChecksumNB(const PNET_BUFFER nb, UINT16 csumDataLen, UINT32 offset); NDIS_STATUS OvsValidateIPChecksum(PNET_BUFFER_LIST curNbl, POVS_PACKET_HDR_INFO hdrInfo); NDIS_STATUS OvsValidateUDPChecksum(PNET_BUFFER_LIST curNbl, BOOLEAN udpCsumZero); #endif /* __CHECKSUM_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/resource.h0000644000000000000000000000013213534540071022452 xustar0030 mtime=1567801401.237679965 30 atime=1567801402.053685959 30 ctime=1567801424.477851186 openvswitch-2.5.9/datapath-windows/ovsext/resource.h0000644000175000017500000021446413534540071024153 0ustar00jpettitjpettit00000000000000//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by ovsext.rc // #define SW_HIDE 0 #define HIDE_WINDOW 0 #define WM_NULL 0x0000 #define WA_INACTIVE 0 #define HTNOWHERE 0 #define SMTO_NORMAL 0x0000 #define ICON_SMALL 0 #define SIZE_RESTORED 0 #define BN_CLICKED 0 #define BST_UNCHECKED 0x0000 #define HDS_HORZ 0x0000 #define TBSTYLE_BUTTON 0x0000 #define TBS_HORZ 0x0000 #define TBS_BOTTOM 0x0000 #define TBS_RIGHT 0x0000 #define LVS_ICON 0x0000 #define LVS_ALIGNTOP 0x0000 #define TCS_TABS 0x0000 #define TCS_SINGLELINE 0x0000 #define TCS_RIGHTJUSTIFY 0x0000 #define DTS_SHORTDATEFORMAT 0x0000 #define PGS_VERT 0x00000000 #define LANG_NEUTRAL 0x00 #define SUBLANG_NEUTRAL 0x00 #define SORT_DEFAULT 0x0 #define SORT_JAPANESE_XJIS 0x0 #define SORT_CHINESE_BIG5 0x0 #define SORT_CHINESE_PRCP 0x0 #define SORT_KOREAN_KSC 0x0 #define SORT_HUNGARIAN_DEFAULT 0x0 #define SORT_GEORGIAN_TRADITIONAL 0x0 #define _USE_DECLSPECS_FOR_SAL 0 #define _USE_ATTRIBUTES_FOR_SAL 0 #define __drv_typeConst 0 #define VER_DEBUG 0 #define VER_PRERELEASE 0 #define WINAPI_PARTITION_APP 1 #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 #define MINIMUM_RESERVED_MANIFEST_RESOURCE_ID 1 #define SW_SHOWNORMAL 1 #define SW_NORMAL 1 #define SHOW_OPENWINDOW 1 #define SW_PARENTCLOSING 1 #define VK_LBUTTON 0x01 #define WM_CREATE 0x0001 #define WA_ACTIVE 1 #define PWR_OK 1 #define PWR_SUSPENDREQUEST 1 #define NFR_ANSI 1 #define UIS_SET 1 #define UISF_HIDEFOCUS 0x1 #define XBUTTON1 0x0001 #define WMSZ_LEFT 1 #define HTCLIENT 1 #define SMTO_BLOCK 0x0001 #define MA_ACTIVATE 1 #define ICON_BIG 1 #define SIZE_MINIMIZED 1 #define MK_LBUTTON 0x0001 #define TME_HOVER 0x00000001 #define CS_VREDRAW 0x0001 #define CF_TEXT 1 #define SCF_ISSECURE 0x00000001 #define IDOK 1 #define BN_PAINT 1 #define BST_CHECKED 0x0001 #define TBSTYLE_SEP 0x0001 #define TTS_ALWAYSTIP 0x01 #define TBS_AUTOTICKS 0x0001 #define UDS_WRAP 0x0001 #define PBS_SMOOTH 0x01 #define LWS_TRANSPARENT 0x0001 #define LVS_REPORT 0x0001 #define TVS_HASBUTTONS 0x0001 #define TVS_EX_NOSINGLECOLLAPSE 0x0001 #define TCS_SCROLLOPPOSITE 0x0001 #define ACS_CENTER 0x0001 #define MCS_DAYSTATE 0x0001 #define DTS_UPDOWN 0x0001 #define PGS_HORZ 0x00000001 #define NFS_EDIT 0x0001 #define BCSIF_GLYPH 0x0001 #define BCSS_NOSPLIT 0x0001 #define LANG_ARABIC 0x01 #define SUBLANG_DEFAULT 0x01 #define SUBLANG_AFRIKAANS_SOUTH_AFRICA 0x01 #define SUBLANG_ALBANIAN_ALBANIA 0x01 #define SUBLANG_ALSATIAN_FRANCE 0x01 #define SUBLANG_AMHARIC_ETHIOPIA 0x01 #define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 #define SUBLANG_ARMENIAN_ARMENIA 0x01 #define SUBLANG_ASSAMESE_INDIA 0x01 #define SUBLANG_AZERI_LATIN 0x01 #define SUBLANG_AZERBAIJANI_AZERBAIJAN_LATIN 0x01 #define SUBLANG_BANGLA_INDIA 0x01 #define SUBLANG_BASHKIR_RUSSIA 0x01 #define SUBLANG_BASQUE_BASQUE 0x01 #define SUBLANG_BELARUSIAN_BELARUS 0x01 #define SUBLANG_BENGALI_INDIA 0x01 #define SUBLANG_BRETON_FRANCE 0x01 #define SUBLANG_BULGARIAN_BULGARIA 0x01 #define SUBLANG_CATALAN_CATALAN 0x01 #define SUBLANG_CENTRAL_KURDISH_IRAQ 0x01 #define SUBLANG_CHEROKEE_CHEROKEE 0x01 #define SUBLANG_CHINESE_TRADITIONAL 0x01 #define SUBLANG_CORSICAN_FRANCE 0x01 #define SUBLANG_CZECH_CZECH_REPUBLIC 0x01 #define SUBLANG_CROATIAN_CROATIA 0x01 #define SUBLANG_DANISH_DENMARK 0x01 #define SUBLANG_DARI_AFGHANISTAN 0x01 #define SUBLANG_DIVEHI_MALDIVES 0x01 #define SUBLANG_DUTCH 0x01 #define SUBLANG_ENGLISH_US 0x01 #define SUBLANG_ESTONIAN_ESTONIA 0x01 #define SUBLANG_FAEROESE_FAROE_ISLANDS 0x01 #define SUBLANG_FILIPINO_PHILIPPINES 0x01 #define SUBLANG_FINNISH_FINLAND 0x01 #define SUBLANG_FRENCH 0x01 #define SUBLANG_FRISIAN_NETHERLANDS 0x01 #define SUBLANG_GALICIAN_GALICIAN 0x01 #define SUBLANG_GEORGIAN_GEORGIA 0x01 #define SUBLANG_GERMAN 0x01 #define SUBLANG_GREEK_GREECE 0x01 #define SUBLANG_GREENLANDIC_GREENLAND 0x01 #define SUBLANG_GUJARATI_INDIA 0x01 #define SUBLANG_HAUSA_NIGERIA_LATIN 0x01 #define SUBLANG_HAWAIIAN_US 0x01 #define SUBLANG_HEBREW_ISRAEL 0x01 #define SUBLANG_HINDI_INDIA 0x01 #define SUBLANG_HUNGARIAN_HUNGARY 0x01 #define SUBLANG_ICELANDIC_ICELAND 0x01 #define SUBLANG_IGBO_NIGERIA 0x01 #define SUBLANG_INDONESIAN_INDONESIA 0x01 #define SUBLANG_INUKTITUT_CANADA 0x01 #define SUBLANG_ITALIAN 0x01 #define SUBLANG_JAPANESE_JAPAN 0x01 #define SUBLANG_KANNADA_INDIA 0x01 #define SUBLANG_KAZAK_KAZAKHSTAN 0x01 #define SUBLANG_KHMER_CAMBODIA 0x01 #define SUBLANG_KICHE_GUATEMALA 0x01 #define SUBLANG_KINYARWANDA_RWANDA 0x01 #define SUBLANG_KONKANI_INDIA 0x01 #define SUBLANG_KOREAN 0x01 #define SUBLANG_KYRGYZ_KYRGYZSTAN 0x01 #define SUBLANG_LAO_LAO 0x01 #define SUBLANG_LATVIAN_LATVIA 0x01 #define SUBLANG_LITHUANIAN 0x01 #define SUBLANG_LUXEMBOURGISH_LUXEMBOURG 0x01 #define SUBLANG_MACEDONIAN_MACEDONIA 0x01 #define SUBLANG_MALAY_MALAYSIA 0x01 #define SUBLANG_MALAYALAM_INDIA 0x01 #define SUBLANG_MALTESE_MALTA 0x01 #define SUBLANG_MAORI_NEW_ZEALAND 0x01 #define SUBLANG_MAPUDUNGUN_CHILE 0x01 #define SUBLANG_MARATHI_INDIA 0x01 #define SUBLANG_MOHAWK_MOHAWK 0x01 #define SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA 0x01 #define SUBLANG_NEPALI_NEPAL 0x01 #define SUBLANG_NORWEGIAN_BOKMAL 0x01 #define SUBLANG_OCCITAN_FRANCE 0x01 #define SUBLANG_ODIA_INDIA 0x01 #define SUBLANG_ORIYA_INDIA 0x01 #define SUBLANG_PASHTO_AFGHANISTAN 0x01 #define SUBLANG_PERSIAN_IRAN 0x01 #define SUBLANG_POLISH_POLAND 0x01 #define SUBLANG_PORTUGUESE_BRAZILIAN 0x01 #define SUBLANG_PUNJABI_INDIA 0x01 #define SUBLANG_QUECHUA_BOLIVIA 0x01 #define SUBLANG_ROMANIAN_ROMANIA 0x01 #define SUBLANG_ROMANSH_SWITZERLAND 0x01 #define SUBLANG_RUSSIAN_RUSSIA 0x01 #define SUBLANG_SAKHA_RUSSIA 0x01 #define SUBLANG_SAMI_NORTHERN_NORWAY 0x01 #define SUBLANG_SANSKRIT_INDIA 0x01 #define SUBLANG_SCOTTISH_GAELIC 0x01 #define SUBLANG_SERBIAN_CROATIA 0x01 #define SUBLANG_SINDHI_INDIA 0x01 #define SUBLANG_SINHALESE_SRI_LANKA 0x01 #define SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA 0x01 #define SUBLANG_SLOVAK_SLOVAKIA 0x01 #define SUBLANG_SLOVENIAN_SLOVENIA 0x01 #define SUBLANG_SPANISH 0x01 #define SUBLANG_SWAHILI_KENYA 0x01 #define SUBLANG_SWEDISH 0x01 #define SUBLANG_SYRIAC_SYRIA 0x01 #define SUBLANG_TAJIK_TAJIKISTAN 0x01 #define SUBLANG_TAMIL_INDIA 0x01 #define SUBLANG_TATAR_RUSSIA 0x01 #define SUBLANG_TELUGU_INDIA 0x01 #define SUBLANG_THAI_THAILAND 0x01 #define SUBLANG_TIBETAN_PRC 0x01 #define SUBLANG_TIGRINYA_ETHIOPIA 0x01 #define SUBLANG_TSWANA_SOUTH_AFRICA 0x01 #define SUBLANG_TURKISH_TURKEY 0x01 #define SUBLANG_TURKMEN_TURKMENISTAN 0x01 #define SUBLANG_UIGHUR_PRC 0x01 #define SUBLANG_UKRAINIAN_UKRAINE 0x01 #define SUBLANG_UPPER_SORBIAN_GERMANY 0x01 #define SUBLANG_URDU_PAKISTAN 0x01 #define SUBLANG_UZBEK_LATIN 0x01 #define SUBLANG_VIETNAMESE_VIETNAM 0x01 #define SUBLANG_WELSH_UNITED_KINGDOM 0x01 #define SUBLANG_WOLOF_SENEGAL 0x01 #define SUBLANG_XHOSA_SOUTH_AFRICA 0x01 #define SUBLANG_YAKUT_RUSSIA 0x01 #define SUBLANG_YI_PRC 0x01 #define SUBLANG_YORUBA_NIGERIA 0x01 #define SUBLANG_ZULU_SOUTH_AFRICA 0x01 #define SORT_INVARIANT_MATH 0x1 #define SORT_JAPANESE_UNICODE 0x1 #define SORT_CHINESE_UNICODE 0x1 #define SORT_KOREAN_UNICODE 0x1 #define SORT_GERMAN_PHONE_BOOK 0x1 #define SORT_HUNGARIAN_TECHNICAL 0x1 #define SORT_GEORGIAN_MODERN 0x1 #define __drv_typeCond 1 #define VS_VERSION_INFO 1 #define VFFF_ISSHAREDFILE 0x0001 #define VFF_CURNEDEST 0x0001 #define VIFF_FORCEINSTALL 0x0001 #define WINAPI_FAMILY_PC_APP 2 #define ISOLATIONAWARE_MANIFEST_RESOURCE_ID 2 #define SW_SHOWMINIMIZED 2 #define SHOW_ICONWINDOW 2 #define SW_OTHERZOOM 2 #define VK_RBUTTON 0x02 #define WM_DESTROY 0x0002 #define WA_CLICKACTIVE 2 #define PWR_SUSPENDRESUME 2 #define NFR_UNICODE 2 #define UIS_CLEAR 2 #define UISF_HIDEACCEL 0x2 #define XBUTTON2 0x0002 #define WMSZ_RIGHT 2 #define HTCAPTION 2 #define SMTO_ABORTIFHUNG 0x0002 #define MA_ACTIVATEANDEAT 2 #define ICON_SMALL2 2 #define SIZE_MAXIMIZED 2 #define MK_RBUTTON 0x0002 #define TME_LEAVE 0x00000002 #define CS_HREDRAW 0x0002 #define CF_BITMAP 2 #define IDCANCEL 2 #define BN_HILITE 2 #define BST_INDETERMINATE 0x0002 #define HDS_BUTTONS 0x0002 #define TBSTYLE_CHECK 0x0002 #define TTS_NOPREFIX 0x02 #define TBS_VERT 0x0002 #define UDS_SETBUDDYINT 0x0002 #define LWS_IGNORERETURN 0x0002 #define LVS_SMALLICON 0x0002 #define TVS_HASLINES 0x0002 #define TVS_EX_MULTISELECT 0x0002 #define TCS_BOTTOM 0x0002 #define TCS_RIGHT 0x0002 #define ACS_TRANSPARENT 0x0002 #define MCS_MULTISELECT 0x0002 #define DTS_SHOWNONE 0x0002 #define PGS_AUTOSCROLL 0x00000002 #define NFS_STATIC 0x0002 #define BCSIF_IMAGE 0x0002 #define BCSS_STRETCH 0x0002 #define LANG_BULGARIAN 0x02 #define SUBLANG_SYS_DEFAULT 0x02 #define SUBLANG_ARABIC_IRAQ 0x02 #define SUBLANG_AZERI_CYRILLIC 0x02 #define SUBLANG_AZERBAIJANI_AZERBAIJAN_CYRILLIC 0x02 #define SUBLANG_BANGLA_BANGLADESH 0x02 #define SUBLANG_BENGALI_BANGLADESH 0x02 #define SUBLANG_CHINESE_SIMPLIFIED 0x02 #define SUBLANG_DUTCH_BELGIAN 0x02 #define SUBLANG_ENGLISH_UK 0x02 #define SUBLANG_FRENCH_BELGIAN 0x02 #define SUBLANG_FULAH_SENEGAL 0x02 #define SUBLANG_GERMAN_SWISS 0x02 #define SUBLANG_INUKTITUT_CANADA_LATIN 0x02 #define SUBLANG_IRISH_IRELAND 0x02 #define SUBLANG_ITALIAN_SWISS 0x02 #define SUBLANG_KASHMIRI_SASIA 0x02 #define SUBLANG_KASHMIRI_INDIA 0x02 #define SUBLANG_LOWER_SORBIAN_GERMANY 0x02 #define SUBLANG_MALAY_BRUNEI_DARUSSALAM 0x02 #define SUBLANG_MONGOLIAN_PRC 0x02 #define SUBLANG_NEPALI_INDIA 0x02 #define SUBLANG_NORWEGIAN_NYNORSK 0x02 #define SUBLANG_PORTUGUESE 0x02 #define SUBLANG_PULAR_SENEGAL 0x02 #define SUBLANG_PUNJABI_PAKISTAN 0x02 #define SUBLANG_QUECHUA_ECUADOR 0x02 #define SUBLANG_SAMI_NORTHERN_SWEDEN 0x02 #define SUBLANG_SERBIAN_LATIN 0x02 #define SUBLANG_SINDHI_PAKISTAN 0x02 #define SUBLANG_SINDHI_AFGHANISTAN 0x02 #define SUBLANG_SPANISH_MEXICAN 0x02 #define SUBLANG_SWEDISH_FINLAND 0x02 #define SUBLANG_TAMAZIGHT_ALGERIA_LATIN 0x02 #define SUBLANG_TAMIL_SRI_LANKA 0x02 #define SUBLANG_TIGRIGNA_ERITREA 0x02 #define SUBLANG_TIGRINYA_ERITREA 0x02 #define SUBLANG_TSWANA_BOTSWANA 0x02 #define SUBLANG_URDU_INDIA 0x02 #define SUBLANG_UZBEK_CYRILLIC 0x02 #define SUBLANG_VALENCIAN_VALENCIA 0x02 #define SORT_CHINESE_PRC 0x2 #define __drv_typeBitset 2 #define VFF_FILEINUSE 0x0002 #define VIFF_DONTDELETEOLD 0x0002 #define WINAPI_FAMILY_PHONE_APP 3 #define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID 3 #define SW_SHOWMAXIMIZED 3 #define SW_MAXIMIZE 3 #define SHOW_FULLSCREEN 3 #define SW_PARENTOPENING 3 #define VK_CANCEL 0x03 #define WM_MOVE 0x0003 #define PWR_CRITICALRESUME 3 #define NF_QUERY 3 #define UIS_INITIALIZE 3 #define WMSZ_TOP 3 #define HTSYSMENU 3 #define MA_NOACTIVATE 3 #define SIZE_MAXSHOW 3 #define CF_METAFILEPICT 3 #define IDABORT 3 #define BN_UNHILITE 3 #define LVS_LIST 0x0003 #define LVS_TYPEMASK 0x0003 #define LANG_CATALAN 0x03 #define LANG_VALENCIAN 0x03 #define SUBLANG_CUSTOM_DEFAULT 0x03 #define SUBLANG_ARABIC_EGYPT 0x03 #define SUBLANG_CHINESE_HONGKONG 0x03 #define SUBLANG_ENGLISH_AUS 0x03 #define SUBLANG_FRENCH_CANADIAN 0x03 #define SUBLANG_GERMAN_AUSTRIAN 0x03 #define SUBLANG_QUECHUA_PERU 0x03 #define SUBLANG_SAMI_NORTHERN_FINLAND 0x03 #define SUBLANG_SERBIAN_CYRILLIC 0x03 #define SUBLANG_SPANISH_MODERN 0x03 #define SORT_CHINESE_BOPOMOFO 0x3 #define __drv_typeExpr 3 #define VER_PRODUCTMINORVERSION 3 #define SW_SHOWNOACTIVATE 4 #define SHOW_OPENNOACTIVATE 4 #define SW_OTHERUNZOOM 4 #define VK_MBUTTON 0x04 #define NF_REQUERY 4 #define UISF_ACTIVE 0x4 #define WMSZ_TOPLEFT 4 #define HTGROWBOX 4 #define MA_NOACTIVATEANDEAT 4 #define SIZE_MAXHIDE 4 #define MK_SHIFT 0x0004 #define CF_SYLK 4 #define IDRETRY 4 #define BN_DISABLE 4 #define BST_PUSHED 0x0004 #define HDS_HOTTRACK 0x0004 #define TBSTYLE_GROUP 0x0004 #define TBS_TOP 0x0004 #define TBS_LEFT 0x0004 #define UDS_ALIGNRIGHT 0x0004 #define PBS_VERTICAL 0x04 #define LWS_NOPREFIX 0x0004 #define LVS_SINGLESEL 0x0004 #define TVS_LINESATROOT 0x0004 #define TVS_EX_DOUBLEBUFFER 0x0004 #define TCS_MULTISELECT 0x0004 #define ACS_AUTOPLAY 0x0004 #define MCS_WEEKNUMBERS 0x0004 #define DTS_LONGDATEFORMAT 0x0004 #define PGS_DRAGNDROP 0x00000004 #define NFS_LISTCOMBO 0x0004 #define BCSIF_STYLE 0x0004 #define BCSS_ALIGNLEFT 0x0004 #define LANG_CHINESE 0x04 #define LANG_CHINESE_SIMPLIFIED 0x04 #define SUBLANG_CUSTOM_UNSPECIFIED 0x04 #define SUBLANG_ARABIC_LIBYA 0x04 #define SUBLANG_CHINESE_SINGAPORE 0x04 #define SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN 0x04 #define SUBLANG_ENGLISH_CAN 0x04 #define SUBLANG_FRENCH_SWISS 0x04 #define SUBLANG_GERMAN_LUXEMBOURG 0x04 #define SUBLANG_SAMI_LULE_NORWAY 0x04 #define SUBLANG_SPANISH_GUATEMALA 0x04 #define SUBLANG_TAMAZIGHT_MOROCCO_TIFINAGH 0x04 #define SORT_JAPANESE_RADICALSTROKE 0x4 #define SORT_CHINESE_RADICALSTROKE 0x4 #define VFF_BUFFTOOSMALL 0x0004 #define SW_SHOW 5 #define VK_XBUTTON1 0x05 #define WM_SIZE 0x0005 #define WMSZ_TOPRIGHT 5 #define HTMENU 5 #define CF_DIF 5 #define IDIGNORE 5 #define BN_DOUBLECLICKED 5 #define LANG_CZECH 0x05 #define SUBLANG_UI_CUSTOM_DEFAULT 0x05 #define SUBLANG_ARABIC_ALGERIA 0x05 #define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN 0x05 #define SUBLANG_CHINESE_MACAU 0x05 #define SUBLANG_ENGLISH_NZ 0x05 #define SUBLANG_FRENCH_LUXEMBOURG 0x05 #define SUBLANG_GERMAN_LIECHTENSTEIN 0x05 #define SUBLANG_SAMI_LULE_SWEDEN 0x05 #define SUBLANG_SPANISH_COSTA_RICA 0x05 #define SW_MINIMIZE 6 #define VK_XBUTTON2 0x06 #define WM_ACTIVATE 0x0006 #define WMSZ_BOTTOM 6 #define HTHSCROLL 6 #define CF_TIFF 6 #define IDYES 6 #define BN_SETFOCUS 6 #define LANG_DANISH 0x06 #define SUBLANG_ARABIC_MOROCCO 0x06 #define SUBLANG_ENGLISH_EIRE 0x06 #define SUBLANG_FRENCH_MONACO 0x06 #define SUBLANG_SAMI_SOUTHERN_NORWAY 0x06 #define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN 0x06 #define SUBLANG_SPANISH_PANAMA 0x06 #define VER_PRODUCTMAJORVERSION 6 #define SW_SHOWMINNOACTIVE 7 #define WM_SETFOCUS 0x0007 #define WMSZ_BOTTOMLEFT 7 #define HTVSCROLL 7 #define CF_OEMTEXT 7 #define IDNO 7 #define BN_KILLFOCUS 7 #define LANG_GERMAN 0x07 #define SUBLANG_ARABIC_TUNISIA 0x07 #define SUBLANG_ENGLISH_SOUTH_AFRICA 0x07 #define SUBLANG_SAMI_SOUTHERN_SWEDEN 0x07 #define SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x07 #define SUBLANG_SPANISH_DOMINICAN_REPUBLIC 0x07 #define SW_SHOWNA 8 #define VK_BACK 0x08 #define WM_KILLFOCUS 0x0008 #define WMSZ_BOTTOMRIGHT 8 #define HTMINBUTTON 8 #define SMTO_NOTIMEOUTIFNOTHUNG 0x0008 #define MK_CONTROL 0x0008 #define CS_DBLCLKS 0x0008 #define CF_DIB 8 #define IDCLOSE 8 #define BST_FOCUS 0x0008 #define HDS_HIDDEN 0x0008 #define TBSTYLE_DROPDOWN 0x0008 #define TBS_BOTH 0x0008 #define UDS_ALIGNLEFT 0x0008 #define PBS_MARQUEE 0x08 #define LWS_USEVISUALSTYLE 0x0008 #define LVS_SHOWSELALWAYS 0x0008 #define TVS_EDITLABELS 0x0008 #define TVS_EX_NOINDENTSTATE 0x0008 #define TCS_FLATBUTTONS 0x0008 #define ACS_TIMER 0x0008 #define MCS_NOTODAYCIRCLE 0x0008 #define NFS_BUTTON 0x0008 #define BCSIF_SIZE 0x0008 #define BCSS_IMAGE 0x0008 #define LANG_GREEK 0x08 #define SUBLANG_ARABIC_OMAN 0x08 #define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC 0x08 #define SUBLANG_ENGLISH_JAMAICA 0x08 #define SUBLANG_SAMI_SKOLT_FINLAND 0x08 #define SUBLANG_SPANISH_VENEZUELA 0x08 #define SW_RESTORE 9 #define VK_TAB 0x09 #define HTMAXBUTTON 9 #define CF_PALETTE 9 #define IDHELP 9 #define DTS_TIMEFORMAT 0x0009 #define LANG_ENGLISH 0x09 #define SUBLANG_ARABIC_YEMEN 0x09 #define SUBLANG_ENGLISH_CARIBBEAN 0x09 #define SUBLANG_SAMI_INARI_FINLAND 0x09 #define SUBLANG_SERBIAN_SERBIA_LATIN 0x09 #define SUBLANG_SPANISH_COLOMBIA 0x09 #define SW_SHOWDEFAULT 10 #define WM_ENABLE 0x000A #define HTLEFT 10 #define CF_PENDATA 10 #define IDTRYAGAIN 10 #define HELP_CONTEXTMENU 0x000a #define LANG_SPANISH 0x0a #define SUBLANG_ARABIC_SYRIA 0x0a #define SUBLANG_ENGLISH_BELIZE 0x0a #define SUBLANG_SERBIAN_SERBIA_CYRILLIC 0x0a #define SUBLANG_SPANISH_PERU 0x0a #define SW_FORCEMINIMIZE 11 #define SW_MAX 11 #define WM_SETREDRAW 0x000B #define HTRIGHT 11 #define CF_RIFF 11 #define IDCONTINUE 11 #define HELP_FINDER 0x000b #define LANG_FINNISH 0x0b #define SUBLANG_ARABIC_JORDAN 0x0b #define SUBLANG_ENGLISH_TRINIDAD 0x0b #define SUBLANG_SERBIAN_MONTENEGRO_LATIN 0x0b #define SUBLANG_SPANISH_ARGENTINA 0x0b #define VK_CLEAR 0x0C #define WM_SETTEXT 0x000C #define HTTOP 12 #define CF_WAVE 12 #define HELP_WM_HELP 0x000c #define DTS_SHORTDATECENTURYFORMAT 0x000C #define LANG_FRENCH 0x0c #define SUBLANG_ARABIC_LEBANON 0x0c #define SUBLANG_ENGLISH_ZIMBABWE 0x0c #define SUBLANG_SERBIAN_MONTENEGRO_CYRILLIC 0x0c #define SUBLANG_SPANISH_ECUADOR 0x0c #define VK_RETURN 0x0D #define WM_GETTEXT 0x000D #define HTTOPLEFT 13 #define CF_UNICODETEXT 13 #define HELP_SETPOPUP_POS 0x000d #define LANG_HEBREW 0x0d #define SUBLANG_ARABIC_KUWAIT 0x0d #define SUBLANG_ENGLISH_PHILIPPINES 0x0d #define SUBLANG_SPANISH_CHILE 0x0d #define WM_GETTEXTLENGTH 0x000E #define HTTOPRIGHT 14 #define CF_ENHMETAFILE 14 #define LANG_HUNGARIAN 0x0e #define SUBLANG_ARABIC_UAE 0x0e #define SUBLANG_SPANISH_URUGUAY 0x0e #define WM_PAINT 0x000F #define HTBOTTOM 15 #define CF_HDROP 15 #define LANG_ICELANDIC 0x0f #define SUBLANG_ARABIC_BAHRAIN 0x0f #define SUBLANG_SPANISH_PARAGUAY 0x0f #define MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID 16 #define VK_SHIFT 0x10 #define WM_CLOSE 0x0010 #define HTBOTTOMLEFT 16 #define WVR_ALIGNTOP 0x0010 #define MK_MBUTTON 0x0010 #define TME_NONCLIENT 0x00000010 #define CF_LOCALE 16 #define HELP_TCARD_DATA 0x0010 #define TBSTYLE_AUTOSIZE 0x0010 #define TTS_NOANIMATE 0x10 #define TBS_NOTICKS 0x0010 #define UDS_AUTOBUDDY 0x0010 #define PBS_SMOOTHREVERSE 0x10 #define LWS_USECUSTOMTEXT 0x0010 #define LVS_SORTASCENDING 0x0010 #define TVS_DISABLEDRAGDROP 0x0010 #define TVS_EX_RICHTOOLTIP 0x0010 #define TCS_FORCEICONLEFT 0x0010 #define MCS_NOTODAY 0x0010 #define DTS_APPCANPARSE 0x0010 #define NFS_ALL 0x0010 #define LANG_ITALIAN 0x10 #define SUBLANG_ARABIC_QATAR 0x10 #define SUBLANG_ENGLISH_INDIA 0x10 #define SUBLANG_SPANISH_BOLIVIA 0x10 #define VK_CONTROL 0x11 #define WM_QUERYENDSESSION 0x0011 #define HTBOTTOMRIGHT 17 #define CF_DIBV5 17 #define HELP_TCARD_OTHER_CALLER 0x0011 #define LANG_JAPANESE 0x11 #define SUBLANG_ENGLISH_MALAYSIA 0x11 #define SUBLANG_SPANISH_EL_SALVADOR 0x11 #define VK_MENU 0x12 #define WM_QUIT 0x0012 #define HTBORDER 18 #define CF_MAX 18 #define LANG_KOREAN 0x12 #define SUBLANG_ENGLISH_SINGAPORE 0x12 #define SUBLANG_SPANISH_HONDURAS 0x12 #define VK_PAUSE 0x13 #define WM_QUERYOPEN 0x0013 #define HTOBJECT 19 #define LANG_DUTCH 0x13 #define SUBLANG_SPANISH_NICARAGUA 0x13 #define VK_CAPITAL 0x14 #define WM_ERASEBKGND 0x0014 #define HTCLOSE 20 #define LANG_NORWEGIAN 0x14 #define SUBLANG_SPANISH_PUERTO_RICO 0x14 #define _SAL_VERSION 20 #define VK_KANA 0x15 #define VK_HANGEUL 0x15 #define VK_HANGUL 0x15 #define WM_SYSCOLORCHANGE 0x0015 #define HTHELP 21 #define LANG_POLISH 0x15 #define SUBLANG_SPANISH_US 0x15 #define WM_ENDSESSION 0x0016 #define LANG_PORTUGUESE 0x16 #define VK_JUNJA 0x17 #define LANG_ROMANSH 0x17 #define RT_MANIFEST 24 #define VK_FINAL 0x18 #define WM_SHOWWINDOW 0x0018 #define LANG_ROMANIAN 0x18 #define VK_HANJA 0x19 #define VK_KANJI 0x19 #define LANG_RUSSIAN 0x19 #define WM_WININICHANGE 0x001A #define LANG_BOSNIAN 0x1a #define LANG_CROATIAN 0x1a #define LANG_SERBIAN 0x1a #define VK_ESCAPE 0x1B #define WM_DEVMODECHANGE 0x001B #define LANG_SLOVAK 0x1b #define VK_CONVERT 0x1C #define WM_ACTIVATEAPP 0x001C #define LANG_ALBANIAN 0x1c #define VK_NONCONVERT 0x1D #define WM_FONTCHANGE 0x001D #define LANG_SWEDISH 0x1d #define VK_ACCEPT 0x1E #define WM_TIMECHANGE 0x001E #define LANG_THAI 0x1e #define VK_MODECHANGE 0x1F #define WM_CANCELMODE 0x001F #define LANG_TURKISH 0x1f #define VK_SPACE 0x20 #define WM_SETCURSOR 0x0020 #define SMTO_ERRORONEXIT 0x0020 #define WVR_ALIGNLEFT 0x0020 #define MK_XBUTTON1 0x0020 #define CS_OWNDC 0x0020 #define TBSTYLE_NOPREFIX 0x0020 #define TTS_NOFADE 0x20 #define TBS_ENABLESELRANGE 0x0020 #define UDS_ARROWKEYS 0x0020 #define LWS_RIGHT 0x0020 #define LVS_SORTDESCENDING 0x0020 #define TVS_SHOWSELALWAYS 0x0020 #define TVS_EX_AUTOHSCROLL 0x0020 #define TCS_FORCELABELLEFT 0x0020 #define DTS_RIGHTALIGN 0x0020 #define NFS_USEFONTASSOC 0x0020 #define LANG_URDU 0x20 #define VK_PRIOR 0x21 #define WM_MOUSEACTIVATE 0x0021 #define LANG_INDONESIAN 0x21 #define VK_NEXT 0x22 #define WM_CHILDACTIVATE 0x0022 #define LANG_UKRAINIAN 0x22 #define VK_END 0x23 #define WM_QUEUESYNC 0x0023 #define LANG_BELARUSIAN 0x23 #define VK_HOME 0x24 #define WM_GETMINMAXINFO 0x0024 #define LANG_SLOVENIAN 0x24 #define VK_LEFT 0x25 #define LANG_ESTONIAN 0x25 #define VK_UP 0x26 #define WM_PAINTICON 0x0026 #define LANG_LATVIAN 0x26 #define VK_RIGHT 0x27 #define WM_ICONERASEBKGND 0x0027 #define LANG_LITHUANIAN 0x27 #define VK_DOWN 0x28 #define WM_NEXTDLGCTL 0x0028 #define LANG_TAJIK 0x28 #define VK_SELECT 0x29 #define LANG_FARSI 0x29 #define LANG_PERSIAN 0x29 #define VK_PRINT 0x2A #define WM_SPOOLERSTATUS 0x002A #define LANG_VIETNAMESE 0x2a #define VK_EXECUTE 0x2B #define WM_DRAWITEM 0x002B #define LANG_ARMENIAN 0x2b #define VK_SNAPSHOT 0x2C #define WM_MEASUREITEM 0x002C #define LANG_AZERI 0x2c #define LANG_AZERBAIJANI 0x2c #define VK_INSERT 0x2D #define WM_DELETEITEM 0x002D #define LANG_BASQUE 0x2d #define VK_DELETE 0x2E #define WM_VKEYTOITEM 0x002E #define LANG_LOWER_SORBIAN 0x2e #define LANG_UPPER_SORBIAN 0x2e #define VK_HELP 0x2F #define WM_CHARTOITEM 0x002F #define LANG_MACEDONIAN 0x2f #define WM_SETFONT 0x0030 #define WM_GETFONT 0x0031 #define WM_SETHOTKEY 0x0032 #define LANG_TSWANA 0x32 #define WM_GETHOTKEY 0x0033 #define LANG_XHOSA 0x34 #define LANG_ZULU 0x35 #define LANG_AFRIKAANS 0x36 #define WM_QUERYDRAGICON 0x0037 #define LANG_GEORGIAN 0x37 #define LANG_FAEROESE 0x38 #define WM_COMPAREITEM 0x0039 #define LANG_HINDI 0x39 #define LANG_MALTESE 0x3a #define LANG_SAMI 0x3b #define LANG_IRISH 0x3c #define WM_GETOBJECT 0x003D #define LANG_MALAY 0x3e #define LANG_KAZAK 0x3f #define WVR_ALIGNBOTTOM 0x0040 #define MK_XBUTTON2 0x0040 #define CS_CLASSDC 0x0040 #define HDS_DRAGDROP 0x0040 #define BTNS_SHOWTEXT 0x0040 #define TTS_BALLOON 0x40 #define TBS_FIXEDLENGTH 0x0040 #define UDS_HORZ 0x0040 #define LVS_SHAREIMAGELISTS 0x0040 #define TVS_RTLREADING 0x0040 #define TVS_EX_FADEINOUTEXPANDOS 0x0040 #define TCS_HOTTRACK 0x0040 #define MCS_NOTRAILINGDATES 0x0040 #define LANG_KYRGYZ 0x40 #define WM_COMPACTING 0x0041 #define LANG_SWAHILI 0x41 #define LANG_TURKMEN 0x42 #define LANG_UZBEK 0x43 #define WM_COMMNOTIFY 0x0044 #define LANG_TATAR 0x44 #define LANG_BANGLA 0x45 #define LANG_BENGALI 0x45 #define WM_WINDOWPOSCHANGING 0x0046 #define LANG_PUNJABI 0x46 #define WM_WINDOWPOSCHANGED 0x0047 #define LANG_GUJARATI 0x47 #define WM_POWER 0x0048 #define LANG_ODIA 0x48 #define LANG_ORIYA 0x48 #define LANG_TAMIL 0x49 #define WM_COPYDATA 0x004A #define LANG_TELUGU 0x4a #define WM_CANCELJOURNAL 0x004B #define LANG_KANNADA 0x4b #define LANG_MALAYALAM 0x4c #define LANG_ASSAMESE 0x4d #define WM_NOTIFY 0x004E #define LANG_MARATHI 0x4e #define LANG_SANSKRIT 0x4f #define WM_INPUTLANGCHANGEREQUEST 0x0050 #define LANG_MONGOLIAN 0x50 #define WM_INPUTLANGCHANGE 0x0051 #define LANG_TIBETAN 0x51 #define WM_TCARD 0x0052 #define LANG_WELSH 0x52 #define WM_HELP 0x0053 #define LANG_KHMER 0x53 #define WM_USERCHANGED 0x0054 #define LANG_LAO 0x54 #define WM_NOTIFYFORMAT 0x0055 #define LANG_GALICIAN 0x56 #define LANG_KONKANI 0x57 #define LANG_MANIPURI 0x58 #define LANG_SINDHI 0x59 #define LANG_SYRIAC 0x5a #define VK_LWIN 0x5B #define LANG_SINHALESE 0x5b #define VK_RWIN 0x5C #define LANG_CHEROKEE 0x5c #define VK_APPS 0x5D #define LANG_INUKTITUT 0x5d #define LANG_AMHARIC 0x5e #define VK_SLEEP 0x5F #define LANG_TAMAZIGHT 0x5f #define VK_NUMPAD0 0x60 #define LANG_KASHMIRI 0x60 #define VK_NUMPAD1 0x61 #define LANG_NEPALI 0x61 #define VK_NUMPAD2 0x62 #define LANG_FRISIAN 0x62 #define VK_NUMPAD3 0x63 #define LANG_PASHTO 0x63 #define WINAPI_FAMILY_DESKTOP_APP 100 #define VK_NUMPAD4 0x64 #define LANG_FILIPINO 0x64 #define VS_USER_DEFINED 100 #define VK_NUMPAD5 0x65 #define LANG_DIVEHI 0x65 #define VK_NUMPAD6 0x66 #define VK_NUMPAD7 0x67 #define LANG_FULAH 0x67 #define LANG_PULAR 0x67 #define VK_NUMPAD8 0x68 #define LANG_HAUSA 0x68 #define VK_NUMPAD9 0x69 #define VK_MULTIPLY 0x6A #define LANG_YORUBA 0x6a #define VK_ADD 0x6B #define LANG_QUECHUA 0x6b #define VK_SEPARATOR 0x6C #define LANG_SOTHO 0x6c #define VK_SUBTRACT 0x6D #define LANG_BASHKIR 0x6d #define VK_DECIMAL 0x6E #define LANG_LUXEMBOURGISH 0x6e #define VK_DIVIDE 0x6F #define LANG_GREENLANDIC 0x6f #define VK_F1 0x70 #define LANG_IGBO 0x70 #define VK_F2 0x71 #define VK_F3 0x72 #define VK_F4 0x73 #define LANG_TIGRIGNA 0x73 #define LANG_TIGRINYA 0x73 #define VK_F5 0x74 #define VK_F6 0x75 #define LANG_HAWAIIAN 0x75 #define VK_F7 0x76 #define VK_F8 0x77 #define VK_F9 0x78 #define WHEEL_DELTA 120 #define LANG_YI 0x78 #define VK_F10 0x79 #define VK_F11 0x7A #define LANG_MAPUDUNGUN 0x7a #define VK_F12 0x7B #define WM_CONTEXTMENU 0x007B #define VK_F13 0x7C #define WM_STYLECHANGING 0x007C #define LANG_MOHAWK 0x7c #define VK_F14 0x7D #define WM_STYLECHANGED 0x007D #define VK_F15 0x7E #define WM_DISPLAYCHANGE 0x007E #define LANG_BRETON 0x7e #define VK_F16 0x7F #define WM_GETICON 0x007F #define LANG_INVARIANT 0x7f #define VK_F17 0x80 #define WM_SETICON 0x0080 #define WVR_ALIGNRIGHT 0x0080 #define CS_PARENTDC 0x0080 #define CF_OWNERDISPLAY 0x0080 #define HDS_FULLDRAG 0x0080 #define BTNS_WHOLEDROPDOWN 0x0080 #define TTS_CLOSE 0x80 #define TBS_NOTHUMB 0x0080 #define UDS_NOTHOUSANDS 0x0080 #define LVS_NOLABELWRAP 0x0080 #define TVS_NOTOOLTIPS 0x0080 #define TVS_EX_PARTIALCHECKBOXES 0x0080 #define TCS_VERTICAL 0x0080 #define MCS_SHORTDAYSOFWEEK 0x0080 #define LANG_UIGHUR 0x80 #define VK_F18 0x81 #define WM_NCCREATE 0x0081 #define CF_DSPTEXT 0x0081 #define LANG_MAORI 0x81 #define VK_F19 0x82 #define WM_NCDESTROY 0x0082 #define CF_DSPBITMAP 0x0082 #define LANG_OCCITAN 0x82 #define VK_F20 0x83 #define WM_NCCALCSIZE 0x0083 #define CF_DSPMETAFILEPICT 0x0083 #define LANG_CORSICAN 0x83 #define VK_F21 0x84 #define WM_NCHITTEST 0x0084 #define LANG_ALSATIAN 0x84 #define VK_F22 0x85 #define WM_NCPAINT 0x0085 #define LANG_SAKHA 0x85 #define LANG_YAKUT 0x85 #define VK_F23 0x86 #define WM_NCACTIVATE 0x0086 #define LANG_KICHE 0x86 #define VK_F24 0x87 #define WM_GETDLGCODE 0x0087 #define LANG_KINYARWANDA 0x87 #define WM_SYNCPAINT 0x0088 #define LANG_WOLOF 0x88 #define LANG_DARI 0x8c #define CF_DSPENHMETAFILE 0x008E #define VK_NUMLOCK 0x90 #define VK_SCROLL 0x91 #define LANG_SCOTTISH_GAELIC 0x91 #define VK_OEM_NEC_EQUAL 0x92 #define VK_OEM_FJ_JISHO 0x92 #define LANG_CENTRAL_KURDISH 0x92 #define VK_OEM_FJ_MASSHOU 0x93 #define VK_OEM_FJ_TOUROKU 0x94 #define VK_OEM_FJ_LOYA 0x95 #define VK_OEM_FJ_ROYA 0x96 #define VK_LSHIFT 0xA0 #define WM_NCMOUSEMOVE 0x00A0 #define VK_RSHIFT 0xA1 #define WM_NCLBUTTONDOWN 0x00A1 #define VK_LCONTROL 0xA2 #define WM_NCLBUTTONUP 0x00A2 #define VK_RCONTROL 0xA3 #define WM_NCLBUTTONDBLCLK 0x00A3 #define VK_LMENU 0xA4 #define WM_NCRBUTTONDOWN 0x00A4 #define VK_RMENU 0xA5 #define WM_NCRBUTTONUP 0x00A5 #define VK_BROWSER_BACK 0xA6 #define WM_NCRBUTTONDBLCLK 0x00A6 #define VK_BROWSER_FORWARD 0xA7 #define WM_NCMBUTTONDOWN 0x00A7 #define VK_BROWSER_REFRESH 0xA8 #define WM_NCMBUTTONUP 0x00A8 #define VK_BROWSER_STOP 0xA9 #define WM_NCMBUTTONDBLCLK 0x00A9 #define VK_BROWSER_SEARCH 0xAA #define VK_BROWSER_FAVORITES 0xAB #define WM_NCXBUTTONDOWN 0x00AB #define VK_BROWSER_HOME 0xAC #define WM_NCXBUTTONUP 0x00AC #define VK_VOLUME_MUTE 0xAD #define WM_NCXBUTTONDBLCLK 0x00AD #define VK_VOLUME_DOWN 0xAE #define VK_VOLUME_UP 0xAF #define VK_MEDIA_NEXT_TRACK 0xB0 #define EM_GETSEL 0x00B0 #define VK_MEDIA_PREV_TRACK 0xB1 #define EM_SETSEL 0x00B1 #define VK_MEDIA_STOP 0xB2 #define EM_GETRECT 0x00B2 #define VK_MEDIA_PLAY_PAUSE 0xB3 #define EM_SETRECT 0x00B3 #define VK_LAUNCH_MAIL 0xB4 #define EM_SETRECTNP 0x00B4 #define VK_LAUNCH_MEDIA_SELECT 0xB5 #define EM_SCROLL 0x00B5 #define VK_LAUNCH_APP1 0xB6 #define EM_LINESCROLL 0x00B6 #define VK_LAUNCH_APP2 0xB7 #define EM_SCROLLCARET 0x00B7 #define EM_GETMODIFY 0x00B8 #define EM_SETMODIFY 0x00B9 #define VK_OEM_1 0xBA #define EM_GETLINECOUNT 0x00BA #define VK_OEM_PLUS 0xBB #define EM_LINEINDEX 0x00BB #define VK_OEM_COMMA 0xBC #define EM_SETHANDLE 0x00BC #define VK_OEM_MINUS 0xBD #define EM_GETHANDLE 0x00BD #define VK_OEM_PERIOD 0xBE #define EM_GETTHUMB 0x00BE #define VK_OEM_2 0xBF #define VK_OEM_3 0xC0 #define EM_LINELENGTH 0x00C1 #define EM_REPLACESEL 0x00C2 #define EM_GETLINE 0x00C4 #define EM_LIMITTEXT 0x00C5 #define EM_CANUNDO 0x00C6 #define EM_UNDO 0x00C7 #define EM_FMTLINES 0x00C8 #define EM_LINEFROMCHAR 0x00C9 #define EM_SETTABSTOPS 0x00CB #define EM_SETPASSWORDCHAR 0x00CC #define EM_EMPTYUNDOBUFFER 0x00CD #define EM_GETFIRSTVISIBLELINE 0x00CE #define EM_SETREADONLY 0x00CF #define EM_SETWORDBREAKPROC 0x00D0 #define EM_GETWORDBREAKPROC 0x00D1 #define EM_GETPASSWORDCHAR 0x00D2 #define EM_SETMARGINS 0x00D3 #define EM_GETMARGINS 0x00D4 #define EM_GETLIMITTEXT 0x00D5 #define EM_POSFROMCHAR 0x00D6 #define EM_CHARFROMPOS 0x00D7 #define EM_SETIMESTATUS 0x00D8 #define EM_GETIMESTATUS 0x00D9 #define VK_OEM_4 0xDB #define VK_OEM_5 0xDC #define VK_OEM_6 0xDD #define VK_OEM_7 0xDE #define VK_OEM_8 0xDF #define VK_OEM_AX 0xE1 #define VK_OEM_102 0xE2 #define VK_ICO_HELP 0xE3 #define VK_ICO_00 0xE4 #define VK_PROCESSKEY 0xE5 #define VK_ICO_CLEAR 0xE6 #define VK_PACKET 0xE7 #define VK_OEM_RESET 0xE9 #define VK_OEM_JUMP 0xEA #define VK_OEM_PA1 0xEB #define VK_OEM_PA2 0xEC #define VK_OEM_PA3 0xED #define VK_OEM_WSCTRL 0xEE #define VK_OEM_CUSEL 0xEF #define VK_OEM_ATTN 0xF0 #define BM_GETCHECK 0x00F0 #define VK_OEM_FINISH 0xF1 #define BM_SETCHECK 0x00F1 #define VK_OEM_COPY 0xF2 #define BM_GETSTATE 0x00F2 #define VK_OEM_AUTO 0xF3 #define BM_SETSTATE 0x00F3 #define VK_OEM_ENLW 0xF4 #define BM_SETSTYLE 0x00F4 #define VK_OEM_BACKTAB 0xF5 #define BM_CLICK 0x00F5 #define VK_ATTN 0xF6 #define BM_GETIMAGE 0x00F6 #define VK_CRSEL 0xF7 #define BM_SETIMAGE 0x00F7 #define VK_EXSEL 0xF8 #define BM_SETDONTCLICK 0x00F8 #define VK_EREOF 0xF9 #define VK_PLAY 0xFA #define VK_ZOOM 0xFB #define VK_NONAME 0xFC #define VK_PA1 0xFD #define VK_OEM_CLEAR 0xFE #define WM_INPUT_DEVICE_CHANGE 0x00FE #define SUBVERSION_MASK 0x000000FF #define WM_INPUT 0x00FF #define WM_KEYFIRST 0x0100 #define WM_KEYDOWN 0x0100 #define WVR_HREDRAW 0x0100 #define HDS_FILTERBAR 0x0100 #define TBSTYLE_TOOLTIPS 0x0100 #define RBS_TOOLTIPS 0x00000100 #define TTS_USEVISUALSTYLE 0x100 #define SBARS_SIZEGRIP 0x0100 #define TBS_TOOLTIPS 0x0100 #define UDS_HOTTRACK 0x0100 #define LVS_AUTOARRANGE 0x0100 #define TVS_CHECKBOXES 0x0100 #define TVS_EX_EXCLUSIONCHECKBOXES 0x0100 #define TCS_BUTTONS 0x0100 #define MCS_NOSELCHANGEONNAV 0x0100 #define WM_KEYUP 0x0101 #define WM_CHAR 0x0102 #define WM_DEADCHAR 0x0103 #define WM_SYSKEYDOWN 0x0104 #define WM_SYSKEYUP 0x0105 #define WM_SYSCHAR 0x0106 #define WM_SYSDEADCHAR 0x0107 #define WM_UNICHAR 0x0109 #define WM_KEYLAST 0x0109 #define WM_IME_STARTCOMPOSITION 0x010D #define WM_IME_ENDCOMPOSITION 0x010E #define WM_IME_COMPOSITION 0x010F #define WM_IME_KEYLAST 0x010F #define WM_INITDIALOG 0x0110 #define WM_COMMAND 0x0111 #define WM_SYSCOMMAND 0x0112 #define WM_TIMER 0x0113 #define WM_HSCROLL 0x0114 #define WM_VSCROLL 0x0115 #define WM_INITMENU 0x0116 #define WM_INITMENUPOPUP 0x0117 #define WM_GESTURE 0x0119 #define WM_GESTURENOTIFY 0x011A #define WM_MENUSELECT 0x011F #define WM_MENUCHAR 0x0120 #define WM_ENTERIDLE 0x0121 #define WM_MENURBUTTONUP 0x0122 #define WM_MENUDRAG 0x0123 #define WM_MENUGETOBJECT 0x0124 #define WM_UNINITMENUPOPUP 0x0125 #define WM_MENUCOMMAND 0x0126 #define WM_CHANGEUISTATE 0x0127 #define WM_UPDATEUISTATE 0x0128 #define WM_QUERYUISTATE 0x0129 #define WM_CTLCOLORMSGBOX 0x0132 #define WM_CTLCOLOREDIT 0x0133 #define WM_CTLCOLORLISTBOX 0x0134 #define WM_CTLCOLORBTN 0x0135 #define WM_CTLCOLORDLG 0x0136 #define WM_CTLCOLORSCROLLBAR 0x0137 #define WM_CTLCOLORSTATIC 0x0138 #define MN_GETHMENU 0x01E1 #define _WIN32_IE_IE20 0x0200 #define WM_MOUSEFIRST 0x0200 #define WM_MOUSEMOVE 0x0200 #define WVR_VREDRAW 0x0200 #define CS_NOCLOSE 0x0200 #define CF_PRIVATEFIRST 0x0200 #define HDS_FLAT 0x0200 #define TBSTYLE_WRAPABLE 0x0200 #define RBS_VARHEIGHT 0x00000200 #define TBS_REVERSED 0x0200 #define LVS_EDITLABELS 0x0200 #define TVS_TRACKSELECT 0x0200 #define TVS_EX_DIMMEDCHECKBOXES 0x0200 #define TCS_MULTILINE 0x0200 #define WM_LBUTTONDOWN 0x0201 #define WM_LBUTTONUP 0x0202 #define WM_LBUTTONDBLCLK 0x0203 #define WM_RBUTTONDOWN 0x0204 #define WM_RBUTTONUP 0x0205 #define WM_RBUTTONDBLCLK 0x0206 #define WM_MBUTTONDOWN 0x0207 #define WM_MBUTTONUP 0x0208 #define WM_MBUTTONDBLCLK 0x0209 #define WM_MOUSEWHEEL 0x020A #define WM_XBUTTONDOWN 0x020B #define WM_XBUTTONUP 0x020C #define WM_XBUTTONDBLCLK 0x020D #define WM_MOUSEHWHEEL 0x020E #define WM_MOUSELAST 0x020E #define WM_PARENTNOTIFY 0x0210 #define WM_ENTERMENULOOP 0x0211 #define WM_EXITMENULOOP 0x0212 #define WM_NEXTMENU 0x0213 #define WM_SIZING 0x0214 #define WM_CAPTURECHANGED 0x0215 #define WM_MOVING 0x0216 #define WM_POWERBROADCAST 0x0218 #define WM_DEVICECHANGE 0x0219 #define WM_MDICREATE 0x0220 #define WM_MDIDESTROY 0x0221 #define WM_MDIACTIVATE 0x0222 #define WM_MDIRESTORE 0x0223 #define WM_MDINEXT 0x0224 #define WM_MDIMAXIMIZE 0x0225 #define WM_MDITILE 0x0226 #define WM_MDICASCADE 0x0227 #define WM_MDIICONARRANGE 0x0228 #define WM_MDIGETACTIVE 0x0229 #define WM_MDISETMENU 0x0230 #define WM_ENTERSIZEMOVE 0x0231 #define WM_EXITSIZEMOVE 0x0232 #define WM_DROPFILES 0x0233 #define WM_MDIREFRESHMENU 0x0234 #define WM_POINTERDEVICECHANGE 0x238 #define WM_POINTERDEVICEINRANGE 0x239 #define WM_POINTERDEVICEOUTOFRANGE 0x23A #define WM_TOUCH 0x0240 #define WM_NCPOINTERUPDATE 0x0241 #define WM_NCPOINTERDOWN 0x0242 #define WM_NCPOINTERUP 0x0243 #define WM_POINTERUPDATE 0x0245 #define WM_POINTERDOWN 0x0246 #define WM_POINTERUP 0x0247 #define WM_POINTERENTER 0x0249 #define WM_POINTERLEAVE 0x024A #define WM_POINTERACTIVATE 0x024B #define WM_POINTERCAPTURECHANGED 0x024C #define WM_TOUCHHITTESTING 0x024D #define WM_POINTERWHEEL 0x024E #define WM_POINTERHWHEEL 0x024F #define DM_POINTERHITTEST 0x0250 #define WM_IME_SETCONTEXT 0x0281 #define WM_IME_NOTIFY 0x0282 #define WM_IME_CONTROL 0x0283 #define WM_IME_COMPOSITIONFULL 0x0284 #define WM_IME_SELECT 0x0285 #define WM_IME_CHAR 0x0286 #define WM_IME_REQUEST 0x0288 #define WM_IME_KEYDOWN 0x0290 #define WM_IME_KEYUP 0x0291 #define WM_NCMOUSEHOVER 0x02A0 #define WM_MOUSEHOVER 0x02A1 #define WM_NCMOUSELEAVE 0x02A2 #define WM_MOUSELEAVE 0x02A3 #define WM_WTSSESSION_CHANGE 0x02B1 #define WM_TABLET_FIRST 0x02c0 #define WM_TABLET_LAST 0x02df #define WM_DPICHANGED 0x02E0 #define CF_PRIVATELAST 0x02FF #define _WIN32_IE_IE30 0x0300 #define WM_CUT 0x0300 #define CF_GDIOBJFIRST 0x0300 #define WM_COPY 0x0301 #define _WIN32_IE_IE302 0x0302 #define WM_PASTE 0x0302 #define WM_CLEAR 0x0303 #define WM_UNDO 0x0304 #define WM_RENDERFORMAT 0x0305 #define WM_RENDERALLFORMATS 0x0306 #define WM_DESTROYCLIPBOARD 0x0307 #define WM_DRAWCLIPBOARD 0x0308 #define WM_PAINTCLIPBOARD 0x0309 #define WM_VSCROLLCLIPBOARD 0x030A #define WM_SIZECLIPBOARD 0x030B #define WM_ASKCBFORMATNAME 0x030C #define WM_CHANGECBCHAIN 0x030D #define WM_HSCROLLCLIPBOARD 0x030E #define WM_QUERYNEWPALETTE 0x030F #define WM_PALETTEISCHANGING 0x0310 #define WM_PALETTECHANGED 0x0311 #define WM_HOTKEY 0x0312 #define WM_PRINT 0x0317 #define WM_PRINTCLIENT 0x0318 #define WM_APPCOMMAND 0x0319 #define WM_THEMECHANGED 0x031A #define WM_CLIPBOARDUPDATE 0x031D #define WM_DWMCOMPOSITIONCHANGED 0x031E #define WM_DWMNCRENDERINGCHANGED 0x031F #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 #define WM_DWMWINDOWMAXIMIZEDCHANGE 0x0321 #define WM_DWMSENDICONICTHUMBNAIL 0x0323 #define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326 #define WM_GETTITLEBARINFOEX 0x033F #define WM_HANDHELDFIRST 0x0358 #define WM_HANDHELDLAST 0x035F #define WM_AFXFIRST 0x0360 #define WM_AFXLAST 0x037F #define WM_PENWINFIRST 0x0380 #define WM_PENWINLAST 0x038F #define WM_DDE_FIRST 0x03E0 #define CF_GDIOBJLAST 0x03FF #define _WIN32_WINNT_NT4 0x0400 #define _WIN32_IE_IE40 0x0400 #define WM_USER 0x0400 #define WVR_VALIDRECTS 0x0400 #define HDS_CHECKBOXES 0x0400 #define TBSTYLE_ALTDRAG 0x0400 #define RBS_BANDBORDERS 0x00000400 #define TBS_DOWNISLEFT 0x0400 #define LVS_OWNERDRAWFIXED 0x0400 #define TVS_SINGLEEXPAND 0x0400 #define TVS_EX_DRAWIMAGEASYNC 0x0400 #define TCS_FIXEDWIDTH 0x0400 #define ctlFirst 0x0400 #define psh1 0x0400 #define _WIN32_IE_IE401 0x0401 #define psh2 0x0401 #define psh3 0x0402 #define psh4 0x0403 #define psh5 0x0404 #define psh6 0x0405 #define psh7 0x0406 #define psh8 0x0407 #define psh9 0x0408 #define psh10 0x0409 #define psh11 0x040a #define psh12 0x040b #define psh13 0x040c #define psh14 0x040d #define psh15 0x040e #define psh16 0x040f #define _WIN32_WINDOWS 0x0410 #define chx1 0x0410 #define chx2 0x0411 #define chx3 0x0412 #define chx4 0x0413 #define chx5 0x0414 #define chx6 0x0415 #define chx7 0x0416 #define chx8 0x0417 #define chx9 0x0418 #define chx10 0x0419 #define chx11 0x041a #define chx12 0x041b #define chx13 0x041c #define chx14 0x041d #define chx15 0x041e #define chx16 0x041f #define rad1 0x0420 #define rad2 0x0421 #define rad3 0x0422 #define rad4 0x0423 #define rad5 0x0424 #define rad6 0x0425 #define rad7 0x0426 #define rad8 0x0427 #define rad9 0x0428 #define rad10 0x0429 #define rad11 0x042a #define rad12 0x042b #define rad13 0x042c #define rad14 0x042d #define rad15 0x042e #define rad16 0x042f #define grp1 0x0430 #define grp2 0x0431 #define grp3 0x0432 #define grp4 0x0433 #define frm1 0x0434 #define frm2 0x0435 #define frm3 0x0436 #define frm4 0x0437 #define rct1 0x0438 #define rct2 0x0439 #define rct3 0x043a #define rct4 0x043b #define ico1 0x043c #define ico2 0x043d #define ico3 0x043e #define ico4 0x043f #define stc1 0x0440 #define stc2 0x0441 #define stc3 0x0442 #define stc4 0x0443 #define stc5 0x0444 #define stc6 0x0445 #define stc7 0x0446 #define stc8 0x0447 #define stc9 0x0448 #define stc10 0x0449 #define stc11 0x044a #define stc12 0x044b #define stc13 0x044c #define stc14 0x044d #define stc15 0x044e #define stc16 0x044f #define stc17 0x0450 #define stc18 0x0451 #define stc19 0x0452 #define stc20 0x0453 #define stc21 0x0454 #define stc22 0x0455 #define stc23 0x0456 #define stc24 0x0457 #define stc25 0x0458 #define stc26 0x0459 #define stc27 0x045a #define stc28 0x045b #define stc29 0x045c #define stc30 0x045d #define stc31 0x045e #define stc32 0x045f #define lst1 0x0460 #define lst2 0x0461 #define lst3 0x0462 #define lst4 0x0463 #define lst5 0x0464 #define lst6 0x0465 #define lst7 0x0466 #define lst8 0x0467 #define lst9 0x0468 #define lst10 0x0469 #define lst11 0x046a #define lst12 0x046b #define lst13 0x046c #define lst14 0x046d #define lst15 0x046e #define lst16 0x046f #define cmb1 0x0470 #define cmb2 0x0471 #define cmb3 0x0472 #define cmb4 0x0473 #define cmb5 0x0474 #define cmb6 0x0475 #define cmb7 0x0476 #define cmb8 0x0477 #define cmb9 0x0478 #define cmb10 0x0479 #define cmb11 0x047a #define cmb12 0x047b #define cmb13 0x047c #define cmb14 0x047d #define cmb15 0x047e #define cmb16 0x047f #define edt1 0x0480 #define edt2 0x0481 #define edt3 0x0482 #define edt4 0x0483 #define edt5 0x0484 #define edt6 0x0485 #define edt7 0x0486 #define edt8 0x0487 #define edt9 0x0488 #define edt10 0x0489 #define edt11 0x048a #define edt12 0x048b #define edt13 0x048c #define edt14 0x048d #define edt15 0x048e #define edt16 0x048f #define scr1 0x0490 #define scr2 0x0491 #define scr3 0x0492 #define scr4 0x0493 #define scr5 0x0494 #define scr6 0x0495 #define scr7 0x0496 #define scr8 0x0497 #define ctl1 0x04A0 #define ctlLast 0x04ff #define _WIN32_WINNT_WIN2K 0x0500 #define _WIN32_IE_IE50 0x0500 #define _WIN32_WINNT_WINXP 0x0501 #define _WIN32_IE_IE501 0x0501 #define _WIN32_WINNT_WS03 0x0502 #define _WIN32_IE_IE55 0x0550 #define _WIN32_WINNT_WIN6 0x0600 #define _WIN32_WINNT_VISTA 0x0600 #define _WIN32_WINNT_WS08 0x0600 #define _WIN32_WINNT_LONGHORN 0x0600 #define _WIN32_IE_IE60 0x0600 #define FILEOPENORD 1536 #define _WIN32_WINNT_WIN7 0x0601 #define _WIN32_IE_IE60SP1 0x0601 #define MULTIFILEOPENORD 1537 #define _WIN32_WINNT_WIN8 0x0602 #define _WIN32_IE_WS03 0x0602 #define PRINTDLGORD 1538 #define _WIN32_WINNT_WINBLUE 0x0603 #define _WIN32_IE_IE60SP2 0x0603 #define PRNSETUPDLGORD 1539 #define VER_PRODUCTVERSION_W 0x0603 #define FINDDLGORD 1540 #define REPLACEDLGORD 1541 #define FONTDLGORD 1542 #define FORMATDLGORD31 1543 #define FORMATDLGORD30 1544 #define RUNDLGORD 1545 #define PAGESETUPDLGORD 1546 #define NEWFILEOPENORD 1547 #define PRINTDLGEXORD 1549 #define PAGESETUPDLGORDMOTIF 1550 #define COLORMGMTDLGORD 1551 #define NEWFILEOPENV2ORD 1552 #define NEWFILEOPENV3ORD 1553 #define NEWFORMATDLGWITHLINK 1591 #define IDC_MANAGE_LINK 1592 #define _WIN32_IE_IE70 0x0700 #define _WIN32_IE_IE80 0x0800 #define CS_SAVEBITS 0x0800 #define HDS_NOSIZING 0x0800 #define TBSTYLE_FLAT 0x0800 #define RBS_FIXEDORDER 0x00000800 #define SBARS_TOOLTIPS 0x0800 #define SBT_TOOLTIPS 0x0800 #define TBS_NOTIFYBEFOREMOVE 0x0800 #define LVS_ALIGNLEFT 0x0800 #define TVS_INFOTIP 0x0800 #define TCS_RAGGEDRIGHT 0x0800 #define _WIN32_IE_IE90 0x0900 #define _WIN32_IE_IE100 0x0A00 #define _WIN32_IE 0x0A00 #define LVS_ALIGNMASK 0x0c00 #define CS_BYTEALIGNCLIENT 0x1000 #define HDS_OVERFLOW 0x1000 #define TBSTYLE_LIST 0x1000 #define RBS_REGISTERDROP 0x00001000 #define TBS_TRANSPARENTBKGND 0x1000 #define LVS_OWNERDATA 0x1000 #define TVS_FULLROWSELECT 0x1000 #define TCS_FOCUSONBUTTONDOWN 0x1000 #define CS_BYTEALIGNWINDOW 0x2000 #define TBSTYLE_CUSTOMERASE 0x2000 #define RBS_AUTOSIZE 0x00002000 #define LVS_NOSCROLL 0x2000 #define TVS_NOSCROLL 0x2000 #define TCS_OWNERDRAWFIXED 0x2000 #define VER_PRODUCTBUILD 9600 #define CS_GLOBALCLASS 0x4000 #define TBSTYLE_REGISTERDROP 0x4000 #define RBS_VERTICALGRIPPER 0x00004000 #define LVS_NOCOLUMNHEADER 0x4000 #define TVS_NONEVENHEIGHT 0x4000 #define TCS_TOOLTIPS 0x4000 #define VER_PRODUCTBUILD_QFE 17298 #define VER_PACKAGEBUILD_QFE 17298 #define IDH_NO_HELP 28440 #define IDH_MISSING_CONTEXT 28441 #define IDH_GENERIC_HELP_BUTTON 28442 #define IDH_OK 28443 #define IDH_CANCEL 28444 #define IDH_HELP 28445 #define LANG_BOSNIAN_NEUTRAL 0x781a #define LANG_CHINESE_TRADITIONAL 0x7c04 #define LANG_SERBIAN_NEUTRAL 0x7c1a #define IDTIMEOUT 32000 #define OCR_NORMAL 32512 #define OIC_SAMPLE 32512 #define IDI_APPLICATION 32512 #define OCR_IBEAM 32513 #define OIC_HAND 32513 #define IDI_HAND 32513 #define OCR_WAIT 32514 #define OIC_QUES 32514 #define IDI_QUESTION 32514 #define OCR_CROSS 32515 #define OIC_BANG 32515 #define IDI_EXCLAMATION 32515 #define OCR_UP 32516 #define OIC_NOTE 32516 #define IDI_ASTERISK 32516 #define OIC_WINLOGO 32517 #define IDI_WINLOGO 32517 #define OIC_SHIELD 32518 #define IDI_SHIELD 32518 #define OCR_SIZE 32640 #define OCR_ICON 32641 #define OCR_SIZENWSE 32642 #define OCR_SIZENESW 32643 #define OCR_SIZEWE 32644 #define OCR_SIZENS 32645 #define OCR_SIZEALL 32646 #define OCR_ICOCUR 32647 #define OCR_NO 32648 #define OCR_HAND 32649 #define OCR_APPSTARTING 32650 #define OBM_LFARROWI 32734 #define OBM_RGARROWI 32735 #define OBM_DNARROWI 32736 #define OBM_UPARROWI 32737 #define OBM_COMBO 32738 #define OBM_MNARROW 32739 #define OBM_LFARROWD 32740 #define OBM_RGARROWD 32741 #define OBM_DNARROWD 32742 #define OBM_UPARROWD 32743 #define OBM_RESTORED 32744 #define OBM_ZOOMD 32745 #define OBM_REDUCED 32746 #define OBM_RESTORE 32747 #define OBM_ZOOM 32748 #define OBM_REDUCE 32749 #define OBM_LFARROW 32750 #define OBM_RGARROW 32751 #define OBM_DNARROW 32752 #define OBM_UPARROW 32753 #define OBM_CLOSE 32754 #define OBM_OLD_RESTORE 32755 #define OBM_OLD_ZOOM 32756 #define OBM_OLD_REDUCE 32757 #define OBM_BTNCORNERS 32758 #define OBM_CHECKBOXES 32759 #define OBM_CHECK 32760 #define OBM_BTSIZE 32761 #define OBM_OLD_LFARROW 32762 #define OBM_OLD_RGARROW 32763 #define OBM_OLD_DNARROW 32764 #define OBM_OLD_UPARROW 32765 #define OBM_SIZE 32766 #define OBM_OLD_CLOSE 32767 #define WM_APP 0x8000 #define HELP_TCARD 0x8000 #define TBSTYLE_TRANSPARENT 0x8000 #define RBS_DBLCLKTOGGLE 0x00008000 #define LVS_NOSORTHEADER 0x8000 #define TVS_NOHSCROLL 0x8000 #define TCS_FOCUSNEVER 0x8000 #define SC_SIZE 0xF000 #define SC_SEPARATOR 0xF00F #define SC_MOVE 0xF010 #define SC_MINIMIZE 0xF020 #define SC_MAXIMIZE 0xF030 #define SC_NEXTWINDOW 0xF040 #define SC_PREVWINDOW 0xF050 #define SC_CLOSE 0xF060 #define SC_VSCROLL 0xF070 #define SC_HSCROLL 0xF080 #define SC_MOUSEMENU 0xF090 #define SC_KEYMENU 0xF100 #define SC_ARRANGE 0xF110 #define SC_RESTORE 0xF120 #define SC_TASKLIST 0xF130 #define SC_SCREENSAVE 0xF140 #define SC_HOTKEY 0xF150 #define SC_DEFAULT 0xF160 #define SC_MONITORPOWER 0xF170 #define SC_CONTEXTHELP 0xF180 #define LVS_TYPESTYLEMASK 0xfc00 #define SPVERSION_MASK 0x0000FF00 #define HTERROR -2 #define PWR_FAIL -1 #define UNICODE_NOCHAR 0xFFFF #define HTTRANSPARENT -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Checksum.c0000644000000000000000000000012713534540071022364 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 28 ctime=1567801424.3578503 openvswitch-2.5.9/datapath-windows/ovsext/Checksum.c0000644000175000017500000004273713534540071024063 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Checksum.h" #include "Flow.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_CHECKSUM #include "Debug.h" #include "PacketParser.h" #ifndef htons #define htons(_x) (((UINT16)(_x) >> 8) + (((UINT16)(_x) << 8) & 0xff00)) #endif #ifndef swap64 #define swap64(_x) ((((UINT64)(_x) >> 8) & 0x00ff00ff00ff00ff) + \ (((UINT64)(_x) << 8) & 0xff00ff00ff00ff00)) #endif #define fold64(_x) \ _x = ((_x) >> 32) + ((_x) & 0xffffffff); \ _x = (UINT32)(((_x) >> 32) + (_x)); \ _x = ((_x) >> 16) + ((_x) & 0xffff); \ _x = (UINT16)(((_x) >> 16) + (_x)) #define fold32(_x) \ _x = ((_x) >> 16) + ((_x) & 0xffff); \ _x = (UINT16)(((_x) >> 16) + (_x)) /* *---------------------------------------------------------------------------- * CalculateOnesComplement -- * * Given the start address and buffer length, calculate the 1's complement * This routine can be used when multiple buffers are used for a packets. * * PLEASE NOTE, even though the last parameter is UINT64, but the assumption * is it will not overflowed after adding the extra data. * ------------------------------------------------ * * Result: * As name indicate, the final data is not 1's complemnent *---------------------------------------------------------------------------- */ UINT64 CalculateOnesComplement(UINT8 *start, UINT16 totalLength, UINT64 initial, BOOLEAN isEvenStart) { UINT64 sum = 0, val; UINT64 *src = (UINT64 *)start; while (totalLength > 7) { val = *src; sum += val; if (sum < val) sum++; src++; totalLength -= 8; } start = (UINT8 *)src; if (totalLength > 3) { UINT32 val = *(UINT32 *)start; sum += val; if (sum < val) sum++; start += 4; totalLength -= 4; } if (totalLength > 1) { UINT16 val = *(UINT16 *)start; sum += val; if (sum < val) sum++; start += 2; totalLength -= 2; } if (totalLength > 0) { UINT8 val = *start; sum += val; if (sum < val) sum++; start += 1; totalLength -= 1; } ASSERT(totalLength == 0); if (!isEvenStart) { sum = _byteswap_uint64(sum); } sum += initial; if (sum < initial) sum++; return sum; } /* *---------------------------------------------------------------------------- * CalculateChecksum -- * * Given the start point, and length, calculate the checksum * as 1's complement of 1's comlement. * * This assume the checksum field is initailized properly. * * Input Parameter: * ptr: point to the data to be checksumed * totalLength: total length of the data * initial: inital value to remit the checksum. Please note this * value should be network byte order value. * * The last parameter may be useful where you don't want to set * checksum field to zero, in that case you can pass ~checksum, * this is equivalent of set checksum field to zero. * * Result: * The result can be assigned to checksum field directly. *---------------------------------------------------------------------------- */ UINT16 CalculateChecksum(UINT8 *ptr, UINT16 totalLength, UINT16 initial) { UINT64 sum = CalculateOnesComplement(ptr, totalLength, initial, TRUE); fold64(sum); return (UINT16)~sum; } /* *---------------------------------------------------------------------------- * CopyAndCalculateOnesComplement -- * * Given the start address and buffer length, calculate the 1's complement * at same time, copt the data from src to dst. * * This routine can be used when multiple buffers are used for a packets. * * PLEASE NOTE, even though the last parameter is UINT64, but the assumption * is it will not overflowed after adding the extra data. * ------------------------------------------------ * * Result: * As name indicate, the final data is not 1's complemnent *---------------------------------------------------------------------------- */ UINT64 CopyAndCalculateOnesComplement(UINT8 *dst, UINT8 *src, UINT16 length, UINT64 initial, BOOLEAN isEvenStart) { UINT64 sum =0, val; UINT64 *src64, *dst64; union { UINT32 val; UINT8 b8[4]; } tmp; src64 = (UINT64 *)src; dst64 = (UINT64 *)dst; while (length > 7) { val = *src64; *dst64 = val; sum += (val >> 32) + (val & 0xffffffff); src64++; dst64++; length -= 8; } if (length > 3) { val = *(UINT32 *)src64; *(UINT32 *)dst64 = (UINT32)val; sum += (UINT32)val; dst64 = (UINT64 *)((UINT8 *)dst64 + 4); src64 = (UINT64 *)((UINT8 *)src64 + 4); length -= 4; } src = (UINT8 *)src64; dst = (UINT8 *)dst64; tmp.val = 0; switch (length) { case 3: dst[2] = src[2]; tmp.b8[2] = src[2]; case 2: dst[1] = src[1]; tmp.b8[1] = src[1]; case 1: dst[0] = src[0]; tmp.b8[0] = src[0]; sum += tmp.val; } sum = (isEvenStart ? sum : swap64(sum)) + initial; return sum; } /* *---------------------------------------------------------------------------- * CopyAndCalculateChecksum -- * * This is similar to CalculateChecksum, except it will also copy data to * destination address. *---------------------------------------------------------------------------- */ UINT16 CopyAndCalculateChecksum(UINT8 *dst, UINT8 *src, UINT16 length, UINT16 initial) { UINT64 sum = CopyAndCalculateOnesComplement(dst, src, length, initial, TRUE); fold64(sum); return (UINT16)~sum; } /* *---------------------------------------------------------------------------- * IPChecksum -- * * Give IP header, calculate the IP checksum. * We assume IP checksum field is initialized properly * * Input Pramater: * ipHdr: IP header start point * length: IP header length (potentially include IP options) * initial: same as CalculateChecksum * * Result: * The result is already 1's complement, so can be assigned * to checksum field directly *---------------------------------------------------------------------------- */ UINT16 IPChecksum(UINT8 *ipHdr, UINT16 length, UINT16 initial) { UINT32 sum = initial; UINT16 *ptr = (UINT16 *)ipHdr; ASSERT((length & 0x3) == 0); while (length > 1) { sum += ptr[0]; ptr++; length -= 2; } fold32(sum); return (UINT16)~sum; } /* *---------------------------------------------------------------------------- * IPPseudoChecksum -- * * Give src and dst IP address, protocol value and total * upper layer length(not include IP header, but include * upller layer protocol header, for example it include * TCP header for TCP checksum), calculate the pseudo * checksum, please note this checksum is just 1's complement * addition. * * Input Parameter: * src: please note it is in network byte order * dst: same as src * protocol: protocol value in IP header * totalLength: total length of upper layer data including * header. * * Result: * * This value should be put in TCP checksum field before * calculating TCP checksum using CalculateChecksum with * initial value of 0. *---------------------------------------------------------------------------- */ UINT16 IPPseudoChecksum(UINT32 *src, UINT32 *dst, UINT8 protocol, UINT16 totalLength) { UINT32 sum = (UINT32)htons(totalLength) + htons(protocol); sum += (*src >> 16) + (*src & 0xffff); sum += (*dst >> 16) + (*dst & 0xffff); fold32(sum); return (UINT16)sum; } /* *---------------------------------------------------------------------------- * IPv6PseudoChecksum -- * * Given IPv6 src and dst address, upper layer protocol and total * upper layer protocol data length including upper layer header * part, calculate the pseudo checksum for upper layer protocol * checksum. * * please note this checksum is just 1's complement addition. * * Input Parameter: * src: src IPv6 address in network byte order * dst: dst IPv6 address. * protocol: upper layer protocol * totalLength: total length of upper layer data. Please note this is * in host byte order. * * Result: * * Place in upper layer checksum field before calculate upper layer * checksum. *---------------------------------------------------------------------------- */ UINT16 IPv6PseudoChecksum(UINT32 *src, UINT32 *dst, UINT8 protocol, UINT16 totalLength) { UINT64 sum = (UINT32)htons(totalLength) + htons(protocol); sum += (UINT64)src[0] + src[1] + src[2] + src[3]; sum += (UINT64)dst[0] + dst[1] + dst[2] + dst[3]; fold64(sum); return (UINT16)sum; } /* *---------------------------------------------------------------------------- * ChecksumUpdate32 -- * * Given old checksum value (as it is in checksum field), * prev value of the relevant field in network byte order * new value of the relevant field in the network byte order * calculate the new checksum. * Please check relevant RFC for reference. * * Input Pramater: * oldSum: old checksum value in checksum field * prev: previous value of relevant 32 bit feld in network * byte order. * new: new value of the relevant 32 bit field in network * byte order. * * Result: * new checksum value to be placed in the checksum field. *---------------------------------------------------------------------------- */ UINT16 ChecksumUpdate32(UINT16 oldSum, UINT32 prev, UINT32 newValue) { UINT32 sum = ~prev; sum = (sum >> 16) + (sum & 0xffff); sum += (newValue >> 16) + (newValue & 0xffff); sum += (UINT16)~oldSum; fold32(sum); return (UINT16)~sum; } /* *---------------------------------------------------------------------------- * ChecksumUpdate16 -- * * Given old checksum value (as it is in checksum field), * prev value of the relevant field in network byte order * new value of the relevant field in the network byte order * calculate the new checksum. * Please check relevant RFC for reference. * * Input Pramater: * oldSum: old checksum value in checksum field * prev: previous value of relevant 32 bit feld in network * byte order. * new: new value of the relevant 32 bit field in network * byte order. * * Result: * new checksum value to be placed in the checksum field. *---------------------------------------------------------------------------- */ UINT16 ChecksumUpdate16(UINT16 oldSum, UINT16 prev, UINT16 newValue) { UINT32 sum = (UINT16)~oldSum; sum += (UINT32)((UINT16)~prev) + newValue; fold32(sum); return (UINT16)~sum; } /* *---------------------------------------------------------------------------- * CalculateChecksumNB -- * * Calculates checksum over a length of bytes contained in an NB. * * nb : NB which contains the packet bytes. * csumDataLen : Length of bytes to be checksummed. * offset : offset to the first bytes of the data stream to be * checksumed. * * Result: * return 0, if there is a failure. *---------------------------------------------------------------------------- */ UINT16 CalculateChecksumNB(const PNET_BUFFER nb, UINT16 csumDataLen, UINT32 offset) { ULONG mdlLen; UINT16 csLen; PUCHAR src; UINT64 csum = 0; PMDL currentMdl; ULONG firstMdlLen; /* Running count of bytes in remainder of the MDLs including current. */ ULONG packetLen; BOOLEAN swapEnd = 1 & csumDataLen; if ((nb == NULL) || (csumDataLen == 0) || (offset >= NET_BUFFER_DATA_LENGTH(nb)) || (offset + csumDataLen > NET_BUFFER_DATA_LENGTH(nb))) { OVS_LOG_ERROR("Invalid parameters - csum length %u, offset %u," "pkt%s len %u", csumDataLen, offset, nb? "":"(null)", nb? NET_BUFFER_DATA_LENGTH(nb) : 0); return 0; } currentMdl = NET_BUFFER_CURRENT_MDL(nb); packetLen = NET_BUFFER_DATA_LENGTH(nb); firstMdlLen = MmGetMdlByteCount(currentMdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb); firstMdlLen = MIN(firstMdlLen, packetLen); if (offset < firstMdlLen) { src = (PUCHAR) MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority); if (!src) { return 0; } src += (NET_BUFFER_CURRENT_MDL_OFFSET(nb) + offset); mdlLen = firstMdlLen - offset; packetLen -= firstMdlLen; ASSERT((INT)packetLen >= 0); } else { offset -= firstMdlLen; packetLen -= firstMdlLen; ASSERT((INT)packetLen >= 0); currentMdl = NDIS_MDL_LINKAGE(currentMdl); mdlLen = MmGetMdlByteCount(currentMdl); mdlLen = MIN(mdlLen, packetLen); while (offset >= mdlLen) { offset -= mdlLen; packetLen -= mdlLen; ASSERT((INT)packetLen >= 0); currentMdl = NDIS_MDL_LINKAGE(currentMdl); mdlLen = MmGetMdlByteCount(currentMdl); mdlLen = MIN(mdlLen, packetLen); } src = (PUCHAR)MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority); if (!src) { return 0; } src += offset; mdlLen -= offset; } while (csumDataLen && (currentMdl != NULL)) { ASSERT(mdlLen < 65536); csLen = MIN((UINT16) mdlLen, csumDataLen); csum = CalculateOnesComplement(src, csLen, csum, !(1 & csumDataLen)); fold64(csum); csumDataLen -= csLen; currentMdl = NDIS_MDL_LINKAGE(currentMdl); if (csumDataLen && currentMdl) { src = MmGetSystemAddressForMdlSafe(currentMdl, LowPagePriority); if (!src) { return 0; } mdlLen = MmGetMdlByteCount(currentMdl); mdlLen = MIN(mdlLen, packetLen); /* packetLen does not include the current MDL from here on. */ packetLen -= mdlLen; ASSERT((INT)packetLen >= 0); } } fold64(csum); ASSERT(csumDataLen == 0); ASSERT((csum & ~0xffff) == 0); csum = (UINT16)~csum; if (swapEnd) { return _byteswap_ushort((UINT16)csum); } return (UINT16)csum; } /* * -------------------------------------------------------------------------- * OvsValidateIPChecksum * -------------------------------------------------------------------------- */ NDIS_STATUS OvsValidateIPChecksum(PNET_BUFFER_LIST curNbl, POVS_PACKET_HDR_INFO hdrInfo) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; uint16_t checksum, hdrChecksum; struct IPHdr ip_storage; const IPHdr *ipHdr; if (!hdrInfo->isIPv4) { return NDIS_STATUS_SUCCESS; } /* First check if NIC has indicated checksum failure. */ csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); if (csumInfo.Receive.IpChecksumFailed) { return NDIS_STATUS_FAILURE; } /* Next, check if the NIC did not validate the RX checksum. */ if (!csumInfo.Receive.IpChecksumSucceeded) { ipHdr = OvsGetIp(curNbl, hdrInfo->l3Offset, &ip_storage); if (ipHdr) { ip_storage = *ipHdr; hdrChecksum = ipHdr->check; ip_storage.check = 0; checksum = IPChecksum((uint8 *)&ip_storage, ipHdr->ihl * 4, 0); if (checksum != hdrChecksum) { return NDIS_STATUS_FAILURE; } } } return NDIS_STATUS_SUCCESS; } /* *---------------------------------------------------------------------------- * OvsValidateUDPChecksum *---------------------------------------------------------------------------- */ NDIS_STATUS OvsValidateUDPChecksum(PNET_BUFFER_LIST curNbl, BOOLEAN udpCsumZero) { NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); if (udpCsumZero) { /* Zero is valid checksum. */ csumInfo.Receive.UdpChecksumFailed = 0; NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = csumInfo.Value; return NDIS_STATUS_SUCCESS; } /* First check if NIC has indicated UDP checksum failure. */ if (csumInfo.Receive.UdpChecksumFailed) { return NDIS_STATUS_INVALID_PACKET; } return NDIS_STATUS_SUCCESS; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Gre.h0000644000000000000000000000013213534540071021340 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.417850743 openvswitch-2.5.9/datapath-windows/ovsext/Gre.h0000644000175000017500000000565313534540071023037 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __GRE_H_ #define __GRE_H_ 1 #include "NetProto.h" #include "Flow.h" typedef struct _OVS_GRE_VPORT { UINT64 ipId; /* * To be filled */ } OVS_GRE_VPORT, *POVS_GRE_VPORT; /* GRE RFC 2890 header based on http://tools.ietf.org/html/rfc2890 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |C| |K|S| Reserved0 | Ver | Protocol Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Checksum (optional) | Reserved1 (Optional) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Key (optional) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Sequence Number (Optional) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ typedef struct GREHdr { UINT16 flags; UINT16 protocolType; } GREHdr, *PGREHdr; /* Transparent Ethernet Bridging */ #define GRE_NET_TEB 0x5865 /* GRE Flags*/ #define GRE_CSUM 0x0080 #define GRE_KEY 0x0020 NTSTATUS OvsInitGreTunnel(POVS_VPORT_ENTRY vport); VOID OvsCleanupGreTunnel(POVS_VPORT_ENTRY vport); void OvsCleanupGreTunnel(POVS_VPORT_ENTRY vport); NDIS_STATUS OvsEncapGre(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl); NDIS_STATUS OvsDecapGre(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl); static __inline UINT16 OvsTunnelFlagsToGreFlags(UINT16 tunnelflags) { UINT16 flags = 0; if (tunnelflags & OVS_TNL_F_CSUM) flags |= GRE_CSUM; if (tunnelflags & OVS_TNL_F_KEY) flags |= GRE_KEY; return flags; } static __inline UINT32 GreTunHdrSize(UINT16 flags) { UINT32 sum = sizeof(EthHdr) + sizeof(IPHdr) + sizeof(GREHdr); sum += (flags & OVS_TNL_F_CSUM) ? 4 : 0; sum += (flags & OVS_TNL_F_KEY) ? 4 : 0; return sum; } #endif /*__GRE_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Atomic.h0000644000000000000000000000013113534540071022036 xustar0029 mtime=1567801401.20567973 30 atime=1567801402.045685899 30 ctime=1567801424.353850271 openvswitch-2.5.9/datapath-windows/ovsext/Atomic.h0000644000175000017500000000163413534540071023531 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __ATOMIC_H_ #define __ATOMIC_H_ 1 static __inline UINT64 atomic_add64(UINT64 *ptr, UINT32 val) { return InterlockedAdd64((LONGLONG volatile *) ptr, (LONGLONG) val); } static __inline UINT64 atomic_inc64(UINT64 *ptr) { return InterlockedIncrement64((LONGLONG volatile *) ptr); } #endif /* __ATOMIC_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Debug.h0000644000000000000000000000013113534540071021650 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 30 ctime=1567801424.365850359 openvswitch-2.5.9/datapath-windows/ovsext/Debug.h0000644000175000017500000000560113534540071023341 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __DEBUG_H_ #define __DEBUG_H_ 1 #define OVS_DBG_INIT BIT32(0) #define OVS_DBG_SWITCH BIT32(1) #define OVS_DBG_VPORT BIT32(2) #define OVS_DBG_FLOW BIT32(3) #define OVS_DBG_QOS BIT32(4) #define OVS_DBG_USER BIT32(5) #define OVS_DBG_EXECUTE BIT32(6) #define OVS_DBG_EVENT BIT32(7) #define OVS_DBG_DISPATCH BIT32(8) #define OVS_DBG_OID BIT32(9) #define OVS_DBG_STATUS BIT32(10) #define OVS_DBG_CHECKSUM BIT32(11) #define OVS_DBG_VXLAN BIT32(12) #define OVS_DBG_GRE BIT32(13) #define OVS_DBG_ACTION BIT32(14) #define OVS_DBG_DATAPATH BIT32(15) #define OVS_DBG_PROPERTY BIT32(16) #define OVS_DBG_IPHELPER BIT32(17) #define OVS_DBG_BUFMGMT BIT32(18) #define OVS_DBG_OTHERS BIT32(19) #define OVS_DBG_NETLINK BIT32(20) #define OVS_DBG_TUNFLT BIT32(21) #define OVS_DBG_STT BIT32(22) #define OVS_DBG_RESERVED BIT32(31) //Please add above OVS_DBG_RESERVED. #define OVS_DBG_ERROR DPFLTR_ERROR_LEVEL #define OVS_DBG_WARN DPFLTR_WARNING_LEVEL #define OVS_DBG_TRACE DPFLTR_TRACE_LEVEL #define OVS_DBG_INFO DPFLTR_INFO_LEVEL #define OVS_DBG_LOUD (DPFLTR_INFO_LEVEL + 1) VOID OvsLog(UINT32 level, UINT32 flag, CHAR *funcName, UINT32 line, CHAR *format, ...); #define OVS_LOG_LOUD(_format, ...) \ OvsLog(OVS_DBG_LOUD, OVS_DBG_MOD, __FUNCTION__, __LINE__, _format, __VA_ARGS__) #define OVS_LOG_INFO(_format, ...) \ OvsLog(OVS_DBG_INFO, OVS_DBG_MOD, __FUNCTION__, __LINE__, _format, __VA_ARGS__) #define OVS_LOG_TRACE(_format, ...) \ OvsLog(OVS_DBG_TRACE, OVS_DBG_MOD, __FUNCTION__, __LINE__, _format, __VA_ARGS__) #define OVS_LOG_ERROR(_format, ...) \ OvsLog(OVS_DBG_ERROR, OVS_DBG_MOD, __FUNCTION__, __LINE__, _format, __VA_ARGS__) #define OVS_LOG_WARN(_format, ...) \ OvsLog(OVS_DBG_WARN, OVS_DBG_MOD, __FUNCTION__, __LINE__, _format, __VA_ARGS__) #if DBG #define OVS_VERIFY_IRQL(_x) \ if (KeGetCurrentIrql() != (KIRQL)_x) { \ OVS_LOG_WARN("expected IRQL %u, actual IRQL: %u", \ _x, KeGetCurrentIrql()); \ } #define OVS_VERIFY_IRQL_LE(_x) \ if (KeGetCurrentIrql() > (KIRQL)_x) { \ OVS_LOG_WARN("expected IRQL <= %u, actual IRQL: %u", \ _x, KeGetCurrentIrql()); \ } #else #define OVS_VERIFY_IRQL(_x) #define OVS_VERIFY_IRQL_LE(_x) #endif #endif /* __DEBUG_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Datapath.c0000644000000000000000000000013013534540071022342 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 29 ctime=1567801424.36185033 openvswitch-2.5.9/datapath-windows/ovsext/Datapath.c0000644000175000017500000014605013534540071024040 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * XXX: OVS_USE_NL_INTERFACE is being used to keep the legacy DPIF interface * alive while we transition over to the netlink based interface. * OVS_USE_NL_INTERFACE = 0 => legacy inteface to use with dpif-windows.c * OVS_USE_NL_INTERFACE = 1 => netlink inteface to use with ported dpif-linux.c */ #include "precomp.h" #include "Switch.h" #include "User.h" #include "Datapath.h" #include "Jhash.h" #include "Vport.h" #include "Event.h" #include "User.h" #include "PacketIO.h" #include "NetProto.h" #include "Flow.h" #include "User.h" #include "Vxlan.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_DATAPATH #include "Debug.h" #define NETLINK_FAMILY_NAME_LEN 48 /* * Netlink messages are grouped by family (aka type), and each family supports * a set of commands, and can be passed both from kernel -> userspace or * vice-versa. To call into the kernel, userspace uses a device operation which * is outside of a netlink message. * * Each command results in the invocation of a handler function to implement the * request functionality. * * Expectedly, only certain combinations of (device operation, netlink family, * command) are valid. * * Here, we implement the basic infrastructure to perform validation on the * incoming message, version checking, and also to invoke the corresponding * handler to do the heavy-lifting. */ /* * Handler for a given netlink command. Not all the parameters are used by all * the handlers. */ typedef NTSTATUS(NetlinkCmdHandler)(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); typedef struct _NETLINK_CMD { UINT16 cmd; NetlinkCmdHandler *handler; UINT32 supportedDevOp; /* Supported device operations. */ BOOLEAN validateDpIndex; /* Does command require a valid DP argument. */ } NETLINK_CMD, *PNETLINK_CMD; /* A netlink family is a group of commands. */ typedef struct _NETLINK_FAMILY { CHAR *name; UINT16 id; UINT8 version; UINT8 pad1; UINT16 maxAttr; UINT16 pad2; NETLINK_CMD *cmds; /* Array of netlink commands and handlers. */ UINT16 opsCount; } NETLINK_FAMILY, *PNETLINK_FAMILY; /* Handlers for the various netlink commands. */ static NetlinkCmdHandler OvsPendEventCmdHandler, OvsSubscribeEventCmdHandler, OvsReadEventCmdHandler, OvsNewDpCmdHandler, OvsGetDpCmdHandler, OvsSetDpCmdHandler; NetlinkCmdHandler OvsGetNetdevCmdHandler, OvsGetVportCmdHandler, OvsSetVportCmdHandler, OvsNewVportCmdHandler, OvsDeleteVportCmdHandler, OvsPendPacketCmdHandler, OvsSubscribePacketCmdHandler, OvsReadPacketCmdHandler; static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); static NTSTATUS HandleDpTransactionCommon( POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); static NTSTATUS OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen); /* * The various netlink families, along with the supported commands. Most of * these families and commands are part of the openvswitch specification for a * netlink datapath. In addition, each platform can implement a few families * and commands as extensions. */ /* Netlink control family: this is a Windows specific family. */ NETLINK_CMD nlControlFamilyCmdOps[] = { { .cmd = OVS_CTRL_CMD_WIN_PEND_REQ, .handler = OvsPendEventCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP, .validateDpIndex = TRUE, }, { .cmd = OVS_CTRL_CMD_WIN_PEND_PACKET_REQ, .handler = OvsPendPacketCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP, .validateDpIndex = TRUE, }, { .cmd = OVS_CTRL_CMD_MC_SUBSCRIBE_REQ, .handler = OvsSubscribeEventCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP, .validateDpIndex = TRUE, }, { .cmd = OVS_CTRL_CMD_PACKET_SUBSCRIBE_REQ, .handler = OvsSubscribePacketCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP, .validateDpIndex = TRUE, }, { .cmd = OVS_CTRL_CMD_EVENT_NOTIFY, .handler = OvsReadEventCmdHandler, .supportedDevOp = OVS_READ_DEV_OP, .validateDpIndex = FALSE, }, { .cmd = OVS_CTRL_CMD_READ_NOTIFY, .handler = OvsReadPacketCmdHandler, .supportedDevOp = OVS_READ_DEV_OP, .validateDpIndex = FALSE, } }; NETLINK_FAMILY nlControlFamilyOps = { .name = OVS_WIN_CONTROL_FAMILY, .id = OVS_WIN_NL_CTRL_FAMILY_ID, .version = OVS_WIN_CONTROL_VERSION, .maxAttr = OVS_WIN_CONTROL_ATTR_MAX, .cmds = nlControlFamilyCmdOps, .opsCount = ARRAY_SIZE(nlControlFamilyCmdOps) }; /* Netlink datapath family. */ NETLINK_CMD nlDatapathFamilyCmdOps[] = { { .cmd = OVS_DP_CMD_NEW, .handler = OvsNewDpCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = FALSE }, { .cmd = OVS_DP_CMD_GET, .handler = OvsGetDpCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP | OVS_TRANSACTION_DEV_OP, .validateDpIndex = FALSE }, { .cmd = OVS_DP_CMD_SET, .handler = OvsSetDpCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP | OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE } }; NETLINK_FAMILY nlDatapathFamilyOps = { .name = OVS_DATAPATH_FAMILY, .id = OVS_WIN_NL_DATAPATH_FAMILY_ID, .version = OVS_DATAPATH_VERSION, .maxAttr = OVS_DP_ATTR_MAX, .cmds = nlDatapathFamilyCmdOps, .opsCount = ARRAY_SIZE(nlDatapathFamilyCmdOps) }; /* Netlink packet family. */ NETLINK_CMD nlPacketFamilyCmdOps[] = { { .cmd = OVS_PACKET_CMD_EXECUTE, .handler = OvsNlExecuteCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE } }; NETLINK_FAMILY nlPacketFamilyOps = { .name = OVS_PACKET_FAMILY, .id = OVS_WIN_NL_PACKET_FAMILY_ID, .version = OVS_PACKET_VERSION, .maxAttr = OVS_PACKET_ATTR_MAX, .cmds = nlPacketFamilyCmdOps, .opsCount = ARRAY_SIZE(nlPacketFamilyCmdOps) }; /* Netlink vport family. */ NETLINK_CMD nlVportFamilyCmdOps[] = { { .cmd = OVS_VPORT_CMD_GET, .handler = OvsGetVportCmdHandler, .supportedDevOp = OVS_WRITE_DEV_OP | OVS_READ_DEV_OP | OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_VPORT_CMD_NEW, .handler = OvsNewVportCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_VPORT_CMD_SET, .handler = OvsSetVportCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_VPORT_CMD_DEL, .handler = OvsDeleteVportCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, }; NETLINK_FAMILY nlVportFamilyOps = { .name = OVS_VPORT_FAMILY, .id = OVS_WIN_NL_VPORT_FAMILY_ID, .version = OVS_VPORT_VERSION, .maxAttr = OVS_VPORT_ATTR_MAX, .cmds = nlVportFamilyCmdOps, .opsCount = ARRAY_SIZE(nlVportFamilyCmdOps) }; /* Netlink flow family. */ NETLINK_CMD nlFlowFamilyCmdOps[] = { { .cmd = OVS_FLOW_CMD_NEW, .handler = OvsFlowNlCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_FLOW_CMD_SET, .handler = OvsFlowNlCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_FLOW_CMD_DEL, .handler = OvsFlowNlCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = TRUE }, { .cmd = OVS_FLOW_CMD_GET, .handler = OvsFlowNlGetCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP | OVS_WRITE_DEV_OP | OVS_READ_DEV_OP, .validateDpIndex = TRUE }, }; NETLINK_FAMILY nlFLowFamilyOps = { .name = OVS_FLOW_FAMILY, .id = OVS_WIN_NL_FLOW_FAMILY_ID, .version = OVS_FLOW_VERSION, .maxAttr = OVS_FLOW_ATTR_MAX, .cmds = nlFlowFamilyCmdOps, .opsCount = ARRAY_SIZE(nlFlowFamilyCmdOps) }; /* Netlink netdev family. */ NETLINK_CMD nlNetdevFamilyCmdOps[] = { { .cmd = OVS_WIN_NETDEV_CMD_GET, .handler = OvsGetNetdevCmdHandler, .supportedDevOp = OVS_TRANSACTION_DEV_OP, .validateDpIndex = FALSE }, }; NETLINK_FAMILY nlNetdevFamilyOps = { .name = OVS_WIN_NETDEV_FAMILY, .id = OVS_WIN_NL_NETDEV_FAMILY_ID, .version = OVS_WIN_NETDEV_VERSION, .maxAttr = OVS_WIN_NETDEV_ATTR_MAX, .cmds = nlNetdevFamilyCmdOps, .opsCount = ARRAY_SIZE(nlNetdevFamilyCmdOps) }; static NTSTATUS MapIrpOutputBuffer(PIRP irp, UINT32 bufferLength, UINT32 requiredLength, PVOID *buffer); static NTSTATUS ValidateNetlinkCmd(UINT32 devOp, POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg, NETLINK_FAMILY *nlFamilyOps); static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, NETLINK_FAMILY *nlFamilyOps, UINT32 *replyLen); /* Handles to the device object for communication with userspace. */ NDIS_HANDLE gOvsDeviceHandle; PDEVICE_OBJECT gOvsDeviceObject; _Dispatch_type_(IRP_MJ_CREATE) _Dispatch_type_(IRP_MJ_CLOSE) DRIVER_DISPATCH OvsOpenCloseDevice; _Dispatch_type_(IRP_MJ_CLEANUP) DRIVER_DISPATCH OvsCleanupDevice; _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH OvsDeviceControl; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT, OvsCreateDeviceObject) #pragma alloc_text(PAGE, OvsOpenCloseDevice) #pragma alloc_text(PAGE, OvsCleanupDevice) #pragma alloc_text(PAGE, OvsDeviceControl) #endif // ALLOC_PRAGMA /* * We might hit this limit easily since userspace opens a netlink descriptor for * each thread, and at least one descriptor per vport. Revisit this later. */ #define OVS_MAX_OPEN_INSTANCES 512 #define OVS_SYSTEM_DP_NAME "ovs-system" POVS_OPEN_INSTANCE ovsOpenInstanceArray[OVS_MAX_OPEN_INSTANCES]; UINT32 ovsNumberOfOpenInstances; extern POVS_SWITCH_CONTEXT gOvsSwitchContext; NDIS_SPIN_LOCK ovsCtrlLockObj; PNDIS_SPIN_LOCK gOvsCtrlLock; NTSTATUS InitUserDumpState(POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg) { /* Clear the dumpState from a previous dump sequence. */ ASSERT(instance->dumpState.ovsMsg == NULL); ASSERT(ovsMsg); instance->dumpState.ovsMsg = (POVS_MESSAGE)OvsAllocateMemoryWithTag(sizeof(OVS_MESSAGE), OVS_DATAPATH_POOL_TAG); if (instance->dumpState.ovsMsg == NULL) { return STATUS_NO_MEMORY; } RtlCopyMemory(instance->dumpState.ovsMsg, ovsMsg, sizeof *instance->dumpState.ovsMsg); RtlZeroMemory(instance->dumpState.index, sizeof instance->dumpState.index); return STATUS_SUCCESS; } VOID FreeUserDumpState(POVS_OPEN_INSTANCE instance) { if (instance->dumpState.ovsMsg != NULL) { OvsFreeMemoryWithTag(instance->dumpState.ovsMsg, OVS_DATAPATH_POOL_TAG); RtlZeroMemory(&instance->dumpState, sizeof instance->dumpState); } } VOID OvsInit() { gOvsCtrlLock = &ovsCtrlLockObj; NdisAllocateSpinLock(gOvsCtrlLock); OvsInitEventQueue(); } VOID OvsCleanup() { OvsCleanupEventQueue(); if (gOvsCtrlLock) { NdisFreeSpinLock(gOvsCtrlLock); gOvsCtrlLock = NULL; } } VOID OvsAcquireCtrlLock() { NdisAcquireSpinLock(gOvsCtrlLock); } VOID OvsReleaseCtrlLock() { NdisReleaseSpinLock(gOvsCtrlLock); } /* * -------------------------------------------------------------------------- * Creates the communication device between user and kernel, and also * initializes the data associated data structures. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsCreateDeviceObject(NDIS_HANDLE ovsExtDriverHandle) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; UNICODE_STRING deviceName; UNICODE_STRING symbolicDeviceName; PDRIVER_DISPATCH dispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1]; NDIS_DEVICE_OBJECT_ATTRIBUTES deviceAttributes; OVS_LOG_TRACE("ovsExtDriverHandle: %p", ovsExtDriverHandle); RtlZeroMemory(dispatchTable, (IRP_MJ_MAXIMUM_FUNCTION + 1) * sizeof (PDRIVER_DISPATCH)); dispatchTable[IRP_MJ_CREATE] = OvsOpenCloseDevice; dispatchTable[IRP_MJ_CLOSE] = OvsOpenCloseDevice; dispatchTable[IRP_MJ_CLEANUP] = OvsCleanupDevice; dispatchTable[IRP_MJ_DEVICE_CONTROL] = OvsDeviceControl; NdisInitUnicodeString(&deviceName, OVS_DEVICE_NAME_NT); NdisInitUnicodeString(&symbolicDeviceName, OVS_DEVICE_NAME_DOS); RtlZeroMemory(&deviceAttributes, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES)); OVS_INIT_OBJECT_HEADER(&deviceAttributes.Header, NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES, NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1, sizeof (NDIS_DEVICE_OBJECT_ATTRIBUTES)); deviceAttributes.DeviceName = &deviceName; deviceAttributes.SymbolicName = &symbolicDeviceName; deviceAttributes.MajorFunctions = dispatchTable; deviceAttributes.ExtensionSize = sizeof (OVS_DEVICE_EXTENSION); status = NdisRegisterDeviceEx(ovsExtDriverHandle, &deviceAttributes, &gOvsDeviceObject, &gOvsDeviceHandle); if (status != NDIS_STATUS_SUCCESS) { POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(gOvsDeviceObject); ASSERT(gOvsDeviceObject != NULL); ASSERT(gOvsDeviceHandle != NULL); if (ovsExt) { ovsExt->numberOpenInstance = 0; } } else { OvsRegisterSystemProvider((PVOID)gOvsDeviceObject); } OVS_LOG_TRACE("DeviceObject: %p", gOvsDeviceObject); return status; } VOID OvsDeleteDeviceObject() { if (gOvsDeviceHandle) { #ifdef DBG POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION) NdisGetDeviceReservedExtension(gOvsDeviceObject); if (ovsExt) { ASSERT(ovsExt->numberOpenInstance == 0); } #endif ASSERT(gOvsDeviceObject); NdisDeregisterDeviceEx(gOvsDeviceHandle); gOvsDeviceHandle = NULL; gOvsDeviceObject = NULL; OvsUnregisterSystemProvider(); } } POVS_OPEN_INSTANCE OvsGetOpenInstance(PFILE_OBJECT fileObject, UINT32 dpNo) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; ASSERT(instance); ASSERT(instance->fileObject == fileObject); if (gOvsSwitchContext->dpNo != dpNo) { return NULL; } return instance; } POVS_OPEN_INSTANCE OvsFindOpenInstance(PFILE_OBJECT fileObject) { UINT32 i, j; for (i = 0, j = 0; i < OVS_MAX_OPEN_INSTANCES && j < ovsNumberOfOpenInstances; i++) { if (ovsOpenInstanceArray[i]) { if (ovsOpenInstanceArray[i]->fileObject == fileObject) { return ovsOpenInstanceArray[i]; } j++; } } return NULL; } NTSTATUS OvsAddOpenInstance(POVS_DEVICE_EXTENSION ovsExt, PFILE_OBJECT fileObject) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)OvsAllocateMemoryWithTag(sizeof(OVS_OPEN_INSTANCE), OVS_DATAPATH_POOL_TAG); UINT32 i; if (instance == NULL) { return STATUS_NO_MEMORY; } OvsAcquireCtrlLock(); ASSERT(OvsFindOpenInstance(fileObject) == NULL); if (ovsNumberOfOpenInstances >= OVS_MAX_OPEN_INSTANCES) { OvsReleaseCtrlLock(); OvsFreeMemoryWithTag(instance, OVS_DATAPATH_POOL_TAG); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(instance, sizeof (OVS_OPEN_INSTANCE)); for (i = 0; i < OVS_MAX_OPEN_INSTANCES; i++) { if (ovsOpenInstanceArray[i] == NULL) { ovsOpenInstanceArray[i] = instance; ovsNumberOfOpenInstances++; instance->cookie = i; break; } } ASSERT(i < OVS_MAX_OPEN_INSTANCES); instance->fileObject = fileObject; ASSERT(fileObject->FsContext == NULL); instance->pid = (UINT32)InterlockedIncrement((LONG volatile *)&ovsExt->pidCount); if (instance->pid == 0) { /* XXX: check for rollover. */ } fileObject->FsContext = instance; OvsReleaseCtrlLock(); return STATUS_SUCCESS; } static VOID OvsCleanupOpenInstance(PFILE_OBJECT fileObject) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; ASSERT(instance); ASSERT(fileObject == instance->fileObject); OvsCleanupEvent(instance); OvsCleanupPacketQueue(instance); } VOID OvsRemoveOpenInstance(PFILE_OBJECT fileObject) { POVS_OPEN_INSTANCE instance; ASSERT(fileObject->FsContext); instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; ASSERT(instance->cookie < OVS_MAX_OPEN_INSTANCES); OvsAcquireCtrlLock(); fileObject->FsContext = NULL; ASSERT(ovsOpenInstanceArray[instance->cookie] == instance); ovsOpenInstanceArray[instance->cookie] = NULL; ovsNumberOfOpenInstances--; OvsReleaseCtrlLock(); ASSERT(instance->eventQueue == NULL); ASSERT (instance->packetQueue == NULL); FreeUserDumpState(instance); OvsFreeMemoryWithTag(instance, OVS_DATAPATH_POOL_TAG); } NTSTATUS OvsCompleteIrpRequest(PIRP irp, ULONG_PTR infoPtr, NTSTATUS status) { irp->IoStatus.Information = infoPtr; irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); return status; } NTSTATUS OvsOpenCloseDevice(PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT fileObject; POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject); ASSERT(deviceObject == gOvsDeviceObject); ASSERT(ovsExt != NULL); irpSp = IoGetCurrentIrpStackLocation(irp); fileObject = irpSp->FileObject; OVS_LOG_TRACE("DeviceObject: %p, fileObject:%p, instance: %u", deviceObject, fileObject, ovsExt->numberOpenInstance); switch (irpSp->MajorFunction) { case IRP_MJ_CREATE: status = OvsAddOpenInstance(ovsExt, fileObject); if (STATUS_SUCCESS == status) { InterlockedIncrement((LONG volatile *)&ovsExt->numberOpenInstance); } break; case IRP_MJ_CLOSE: ASSERT(ovsExt->numberOpenInstance > 0); OvsRemoveOpenInstance(fileObject); InterlockedDecrement((LONG volatile *)&ovsExt->numberOpenInstance); break; default: ASSERT(0); } return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status); } _Use_decl_annotations_ NTSTATUS OvsCleanupDevice(PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp; PFILE_OBJECT fileObject; NTSTATUS status = STATUS_SUCCESS; #ifdef DBG POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject); if (ovsExt) { ASSERT(ovsExt->numberOpenInstance > 0); } #else UNREFERENCED_PARAMETER(deviceObject); #endif ASSERT(deviceObject == gOvsDeviceObject); irpSp = IoGetCurrentIrpStackLocation(irp); fileObject = irpSp->FileObject; ASSERT(irpSp->MajorFunction == IRP_MJ_CLEANUP); OvsCleanupOpenInstance(fileObject); return OvsCompleteIrpRequest(irp, (ULONG_PTR)0, status); } /* * -------------------------------------------------------------------------- * IOCTL function handler for the device. * -------------------------------------------------------------------------- */ NTSTATUS OvsDeviceControl(PDEVICE_OBJECT deviceObject, PIRP irp) { PIO_STACK_LOCATION irpSp; NTSTATUS status = STATUS_SUCCESS; PFILE_OBJECT fileObject; PVOID inputBuffer = NULL; PVOID outputBuffer = NULL; UINT32 inputBufferLen, outputBufferLen; UINT32 code, replyLen = 0; POVS_OPEN_INSTANCE instance; UINT32 devOp; OVS_MESSAGE ovsMsgReadOp; POVS_MESSAGE ovsMsg; NETLINK_FAMILY *nlFamilyOps; OVS_USER_PARAMS_CONTEXT usrParamsCtx; #ifdef DBG POVS_DEVICE_EXTENSION ovsExt = (POVS_DEVICE_EXTENSION)NdisGetDeviceReservedExtension(deviceObject); ASSERT(deviceObject == gOvsDeviceObject); ASSERT(ovsExt); ASSERT(ovsExt->numberOpenInstance > 0); #else UNREFERENCED_PARAMETER(deviceObject); #endif irpSp = IoGetCurrentIrpStackLocation(irp); ASSERT(irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); ASSERT(irpSp->FileObject != NULL); fileObject = irpSp->FileObject; instance = (POVS_OPEN_INSTANCE)fileObject->FsContext; code = irpSp->Parameters.DeviceIoControl.IoControlCode; inputBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength; outputBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength; inputBuffer = irp->AssociatedIrp.SystemBuffer; /* Check if the extension is enabled. */ if (NULL == gOvsSwitchContext) { status = STATUS_NOT_FOUND; goto exit; } if (!OvsAcquireSwitchContext()) { status = STATUS_NOT_FOUND; goto exit; } /* * Validate the input/output buffer arguments depending on the type of the * operation. */ switch (code) { case OVS_IOCTL_GET_PID: /* Both input buffer and output buffer use the same location. */ outputBuffer = irp->AssociatedIrp.SystemBuffer; if (outputBufferLen != 0) { InitUserParamsCtx(irp, instance, 0, NULL, inputBuffer, inputBufferLen, outputBuffer, outputBufferLen, &usrParamsCtx); ASSERT(outputBuffer); } else { status = STATUS_NDIS_INVALID_LENGTH; goto done; } status = OvsGetPidHandler(&usrParamsCtx, &replyLen); goto done; case OVS_IOCTL_TRANSACT: /* Both input buffer and output buffer are mandatory. */ if (outputBufferLen != 0) { status = MapIrpOutputBuffer(irp, outputBufferLen, sizeof *ovsMsg, &outputBuffer); if (status != STATUS_SUCCESS) { goto done; } ASSERT(outputBuffer); } else { status = STATUS_NDIS_INVALID_LENGTH; goto done; } if (inputBufferLen < sizeof (*ovsMsg)) { status = STATUS_NDIS_INVALID_LENGTH; goto done; } ovsMsg = inputBuffer; devOp = OVS_TRANSACTION_DEV_OP; break; case OVS_IOCTL_READ_EVENT: case OVS_IOCTL_READ_PACKET: /* * Output buffer is mandatory. These IOCTLs are used to read events and * packets respectively. It is convenient to have separate ioctls. */ if (outputBufferLen != 0) { status = MapIrpOutputBuffer(irp, outputBufferLen, sizeof *ovsMsg, &outputBuffer); if (status != STATUS_SUCCESS) { goto done; } ASSERT(outputBuffer); } else { status = STATUS_NDIS_INVALID_LENGTH; goto done; } inputBuffer = NULL; inputBufferLen = 0; ovsMsg = &ovsMsgReadOp; RtlZeroMemory(ovsMsg, sizeof *ovsMsg); ovsMsg->nlMsg.nlmsgLen = sizeof *ovsMsg; ovsMsg->nlMsg.nlmsgType = nlControlFamilyOps.id; ovsMsg->nlMsg.nlmsgPid = instance->pid; /* An "artificial" command so we can use NL family function table*/ ovsMsg->genlMsg.cmd = (code == OVS_IOCTL_READ_EVENT) ? OVS_CTRL_CMD_EVENT_NOTIFY : OVS_CTRL_CMD_READ_NOTIFY; ovsMsg->genlMsg.version = nlControlFamilyOps.version; devOp = OVS_READ_DEV_OP; break; case OVS_IOCTL_READ: /* Output buffer is mandatory. */ if (outputBufferLen != 0) { status = MapIrpOutputBuffer(irp, outputBufferLen, sizeof *ovsMsg, &outputBuffer); if (status != STATUS_SUCCESS) { goto done; } ASSERT(outputBuffer); } else { status = STATUS_NDIS_INVALID_LENGTH; goto done; } /* * Operate in the mode that read ioctl is similar to ReadFile(). This * might change as the userspace code gets implemented. */ inputBuffer = NULL; inputBufferLen = 0; /* * For implementing read (ioctl or otherwise), we need to store some * state in the instance to indicate the command that started the dump * operation. The state can setup 'ovsMsgReadOp' appropriately. Note * that 'ovsMsgReadOp' is needed only in this function to call into the * appropriate handler. The handler itself can access the state in the * instance. * * In the absence of a dump start, return 0 bytes. */ if (instance->dumpState.ovsMsg == NULL) { replyLen = 0; status = STATUS_SUCCESS; goto done; } RtlCopyMemory(&ovsMsgReadOp, instance->dumpState.ovsMsg, sizeof (ovsMsgReadOp)); /* Create an NL message for consumption. */ ovsMsg = &ovsMsgReadOp; devOp = OVS_READ_DEV_OP; break; case OVS_IOCTL_WRITE: /* Input buffer is mandatory. */ if (inputBufferLen < sizeof (*ovsMsg)) { status = STATUS_NDIS_INVALID_LENGTH; goto done; } ovsMsg = inputBuffer; devOp = OVS_WRITE_DEV_OP; break; default: status = STATUS_INVALID_DEVICE_REQUEST; goto done; } ASSERT(ovsMsg); switch (ovsMsg->nlMsg.nlmsgType) { case OVS_WIN_NL_CTRL_FAMILY_ID: nlFamilyOps = &nlControlFamilyOps; break; case OVS_WIN_NL_DATAPATH_FAMILY_ID: nlFamilyOps = &nlDatapathFamilyOps; break; case OVS_WIN_NL_FLOW_FAMILY_ID: nlFamilyOps = &nlFLowFamilyOps; break; case OVS_WIN_NL_PACKET_FAMILY_ID: nlFamilyOps = &nlPacketFamilyOps; break; case OVS_WIN_NL_VPORT_FAMILY_ID: nlFamilyOps = &nlVportFamilyOps; break; case OVS_WIN_NL_NETDEV_FAMILY_ID: nlFamilyOps = &nlNetdevFamilyOps; break; default: status = STATUS_INVALID_PARAMETER; goto done; } /* * For read operation, avoid duplicate validation since 'ovsMsg' is either * "artificial" or was copied from a previously validated 'ovsMsg'. */ if (devOp != OVS_READ_DEV_OP) { status = ValidateNetlinkCmd(devOp, instance, ovsMsg, nlFamilyOps); if (status != STATUS_SUCCESS) { goto done; } } InitUserParamsCtx(irp, instance, devOp, ovsMsg, inputBuffer, inputBufferLen, outputBuffer, outputBufferLen, &usrParamsCtx); status = InvokeNetlinkCmdHandler(&usrParamsCtx, nlFamilyOps, &replyLen); done: OvsReleaseSwitchContext(gOvsSwitchContext); exit: /* Should not complete a pending IRP unless proceesing is completed. */ if (status == STATUS_PENDING) { return status; } return OvsCompleteIrpRequest(irp, (ULONG_PTR)replyLen, status); } /* * -------------------------------------------------------------------------- * Function to validate a netlink command. Only certain combinations of * (device operation, netlink family, command) are valid. * -------------------------------------------------------------------------- */ static NTSTATUS ValidateNetlinkCmd(UINT32 devOp, POVS_OPEN_INSTANCE instance, POVS_MESSAGE ovsMsg, NETLINK_FAMILY *nlFamilyOps) { NTSTATUS status = STATUS_INVALID_PARAMETER; UINT16 i; for (i = 0; i < nlFamilyOps->opsCount; i++) { if (nlFamilyOps->cmds[i].cmd == ovsMsg->genlMsg.cmd) { /* Validate if the command is valid for the device operation. */ if ((devOp & nlFamilyOps->cmds[i].supportedDevOp) == 0) { status = STATUS_INVALID_PARAMETER; goto done; } /* Validate the version. */ if (nlFamilyOps->version > ovsMsg->genlMsg.version) { status = STATUS_INVALID_PARAMETER; goto done; } /* Validate the DP for commands that require a DP. */ if (nlFamilyOps->cmds[i].validateDpIndex == TRUE) { if (ovsMsg->ovsHdr.dp_ifindex != (INT)gOvsSwitchContext->dpNo) { status = STATUS_INVALID_PARAMETER; goto done; } } /* Validate the PID. */ if (ovsMsg->nlMsg.nlmsgPid != instance->pid) { status = STATUS_INVALID_PARAMETER; goto done; } status = STATUS_SUCCESS; break; } } done: return status; } /* * -------------------------------------------------------------------------- * Function to invoke the netlink command handler. The function also stores * the return value of the handler function to construct a 'NL_ERROR' message, * and in turn returns success to the caller. * -------------------------------------------------------------------------- */ static NTSTATUS InvokeNetlinkCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, NETLINK_FAMILY *nlFamilyOps, UINT32 *replyLen) { NTSTATUS status = STATUS_INVALID_PARAMETER; UINT16 i; for (i = 0; i < nlFamilyOps->opsCount; i++) { if (nlFamilyOps->cmds[i].cmd == usrParamsCtx->ovsMsg->genlMsg.cmd) { NetlinkCmdHandler *handler = nlFamilyOps->cmds[i].handler; ASSERT(handler); if (handler) { status = handler(usrParamsCtx, replyLen); } break; } } /* * Netlink socket semantics dictate that the return value of the netlink * function should be an error ONLY under fatal conditions. If the message * made it all the way to the handler function, it is not a fatal condition. * Absorb the error returned by the handler function into a 'struct * NL_ERROR' and populate the 'output buffer' to return to userspace. * * This behavior is obviously applicable only to netlink commands that * specify an 'output buffer'. For other commands, we return the error as * is. * * 'STATUS_PENDING' is a special return value and userspace is equipped to * handle it. */ if (status != STATUS_SUCCESS && status != STATUS_PENDING) { if (usrParamsCtx->devOp != OVS_WRITE_DEV_OP && *replyLen == 0) { NL_ERROR nlError = NlMapStatusToNlErr(status); OVS_MESSAGE msgInTmp = { 0 }; POVS_MESSAGE msgIn = NULL; POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_CTRL_CMD_EVENT_NOTIFY || usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_CTRL_CMD_READ_NOTIFY) { /* There's no input buffer associated with such requests. */ NL_BUFFER nlBuffer; msgIn = &msgInTmp; NlBufInit(&nlBuffer, (PCHAR)msgIn, sizeof *msgIn); NlFillNlHdr(&nlBuffer, nlFamilyOps->id, 0, 0, usrParamsCtx->ovsInstance->pid); } else { msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; } ASSERT(msgIn); ASSERT(msgError); NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } if (*replyLen != 0) { status = STATUS_SUCCESS; } } #ifdef DBG if (usrParamsCtx->devOp != OVS_WRITE_DEV_OP) { ASSERT(status == STATUS_PENDING || *replyLen != 0 || status == STATUS_SUCCESS); } #endif return status; } /* * -------------------------------------------------------------------------- * Handler for 'OVS_IOCTL_GET_PID'. * * Each handle on the device is assigned a unique PID when the handle is * created. This function passes the PID to userspace using METHOD_BUFFERED * method. * -------------------------------------------------------------------------- */ static NTSTATUS OvsGetPidHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NTSTATUS status = STATUS_SUCCESS; PUINT32 msgOut = (PUINT32)usrParamsCtx->outputBuffer; if (usrParamsCtx->outputLength >= sizeof *msgOut) { POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; RtlZeroMemory(msgOut, sizeof *msgOut); RtlCopyMemory(msgOut, &instance->pid, sizeof(*msgOut)); *replyLen = sizeof *msgOut; } else { *replyLen = sizeof *msgOut; status = STATUS_NDIS_INVALID_LENGTH; } return status; } /* * -------------------------------------------------------------------------- * Utility function to fill up information about the datapath in a reply to * userspace. * -------------------------------------------------------------------------- */ static NTSTATUS OvsDpFillInfo(POVS_SWITCH_CONTEXT ovsSwitchContext, POVS_MESSAGE msgIn, PNL_BUFFER nlBuf) { BOOLEAN writeOk; OVS_MESSAGE msgOutTmp; OVS_DATAPATH *datapath = &ovsSwitchContext->datapath; PNL_MSG_HDR nlMsg; ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && NlBufRemLen(nlBuf) >= sizeof *msgIn); msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_DATAPATH_FAMILY_ID; msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */ msgOutTmp.nlMsg.nlmsgSeq = msgIn->nlMsg.nlmsgSeq; msgOutTmp.nlMsg.nlmsgPid = msgIn->nlMsg.nlmsgPid; msgOutTmp.genlMsg.cmd = OVS_DP_CMD_GET; msgOutTmp.genlMsg.version = nlDatapathFamilyOps.version; msgOutTmp.genlMsg.reserved = 0; msgOutTmp.ovsHdr.dp_ifindex = ovsSwitchContext->dpNo; writeOk = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp); if (writeOk) { writeOk = NlMsgPutTailString(nlBuf, OVS_DP_ATTR_NAME, OVS_SYSTEM_DP_NAME); } if (writeOk) { OVS_DP_STATS dpStats; dpStats.n_hit = datapath->hits; dpStats.n_missed = datapath->misses; dpStats.n_lost = datapath->lost; dpStats.n_flows = datapath->nFlows; writeOk = NlMsgPutTailUnspec(nlBuf, OVS_DP_ATTR_STATS, (PCHAR)&dpStats, sizeof dpStats); } nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0); nlMsg->nlmsgLen = NlBufSize(nlBuf); return writeOk ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE; } /* * -------------------------------------------------------------------------- * Handler for queueing an IRP used for event notification. The IRP is * completed when a port state changes. STATUS_PENDING is returned on * success. User mode keep a pending IRP at all times. * -------------------------------------------------------------------------- */ static NTSTATUS OvsPendEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status; UNREFERENCED_PARAMETER(replyLen); POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; OVS_EVENT_POLL poll; poll.dpNo = msgIn->ovsHdr.dp_ifindex; status = OvsWaitEventIoctl(usrParamsCtx->irp, instance->fileObject, &poll, sizeof poll); return status; } /* * -------------------------------------------------------------------------- * Handler for the subscription for the event queue * -------------------------------------------------------------------------- */ static NTSTATUS OvsSubscribeEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { NDIS_STATUS status; OVS_EVENT_SUBSCRIBE request; BOOLEAN rc; UINT8 join; PNL_ATTR attrs[2]; const NL_POLICY policy[] = { [OVS_NL_ATTR_MCAST_GRP] = {.type = NL_A_U32 }, [OVS_NL_ATTR_MCAST_JOIN] = {.type = NL_A_U8 }, }; UNREFERENCED_PARAMETER(replyLen); POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; rc = NlAttrParse(&msgIn->nlMsg, sizeof (*msgIn), NlMsgAttrsLen((PNL_MSG_HDR)msgIn), policy, ARRAY_SIZE(policy), attrs, ARRAY_SIZE(attrs)); if (!rc) { status = STATUS_INVALID_PARAMETER; goto done; } /* XXX Ignore the MC group for now */ join = NlAttrGetU8(attrs[OVS_NL_ATTR_MCAST_JOIN]); request.dpNo = msgIn->ovsHdr.dp_ifindex; request.subscribe = join; request.mask = OVS_EVENT_MASK_ALL; status = OvsSubscribeEventIoctl(instance->fileObject, &request, sizeof request); done: return status; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_DP_CMD_NEW'. * -------------------------------------------------------------------------- */ static NTSTATUS OvsNewDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { return HandleDpTransactionCommon(usrParamsCtx, replyLen); } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_DP_CMD_GET'. * * The function handles both the dump based as well as the transaction based * 'OVS_DP_CMD_GET' command. In the dump command, it handles the initial * call to setup dump state, as well as subsequent calls to continue dumping * data. * -------------------------------------------------------------------------- */ static NTSTATUS OvsGetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { if (usrParamsCtx->devOp == OVS_TRANSACTION_DEV_OP) { return HandleDpTransactionCommon(usrParamsCtx, replyLen); } else { return HandleGetDpDump(usrParamsCtx, replyLen); } } /* * -------------------------------------------------------------------------- * Function for handling the transaction based 'OVS_DP_CMD_GET' command. * -------------------------------------------------------------------------- */ static NTSTATUS HandleGetDpTransaction(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { return HandleDpTransactionCommon(usrParamsCtx, replyLen); } /* * -------------------------------------------------------------------------- * Function for handling the dump-based 'OVS_DP_CMD_GET' command. * -------------------------------------------------------------------------- */ static NTSTATUS HandleGetDpDump(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; if (usrParamsCtx->devOp == OVS_WRITE_DEV_OP) { *replyLen = 0; OvsSetupDumpStart(usrParamsCtx); } else { NL_BUFFER nlBuf; NTSTATUS status; POVS_MESSAGE msgIn = instance->dumpState.ovsMsg; ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); if (instance->dumpState.ovsMsg == NULL) { ASSERT(FALSE); return STATUS_INVALID_DEVICE_STATE; } /* Dump state must have been deleted after previous dump operation. */ ASSERT(instance->dumpState.index[0] == 0); /* Output buffer has been validated while validating read dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf); if (status != STATUS_SUCCESS) { *replyLen = 0; FreeUserDumpState(instance); return status; } /* Increment the dump index. */ instance->dumpState.index[0] = 1; *replyLen = msgOut->nlMsg.nlmsgLen; /* Free up the dump state, since there's no more data to continue. */ FreeUserDumpState(instance); } return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Command Handler for 'OVS_DP_CMD_SET'. * -------------------------------------------------------------------------- */ static NTSTATUS OvsSetDpCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { return HandleDpTransactionCommon(usrParamsCtx, replyLen); } /* * -------------------------------------------------------------------------- * Function for handling transaction based 'OVS_DP_CMD_NEW', 'OVS_DP_CMD_GET' * and 'OVS_DP_CMD_SET' commands. * * 'OVS_DP_CMD_NEW' is implemented to keep userspace code happy. Creation of a * new datapath is not supported currently. * -------------------------------------------------------------------------- */ static NTSTATUS HandleDpTransactionCommon(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; NTSTATUS status = STATUS_SUCCESS; NL_BUFFER nlBuf; NL_ERROR nlError = NL_ERROR_SUCCESS; static const NL_POLICY ovsDatapathSetPolicy[] = { [OVS_DP_ATTR_NAME] = { .type = NL_A_STRING, .maxLen = IFNAMSIZ }, [OVS_DP_ATTR_UPCALL_PID] = { .type = NL_A_U32, .optional = TRUE }, [OVS_DP_ATTR_USER_FEATURES] = { .type = NL_A_U32, .optional = TRUE }, }; PNL_ATTR dpAttrs[ARRAY_SIZE(ovsDatapathSetPolicy)]; UNREFERENCED_PARAMETER(msgOut); /* input buffer has been validated while validating write dev op. */ ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn); /* Parse any attributes in the request. */ if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET || usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) { if (!NlAttrParse((PNL_MSG_HDR)msgIn, NLMSG_HDRLEN + GENL_HDRLEN + OVS_HDRLEN, NlMsgAttrsLen((PNL_MSG_HDR)msgIn), ovsDatapathSetPolicy, ARRAY_SIZE(ovsDatapathSetPolicy), dpAttrs, ARRAY_SIZE(dpAttrs))) { return STATUS_INVALID_PARAMETER; } /* * XXX: Not clear at this stage if there's any role for the * OVS_DP_ATTR_UPCALL_PID and OVS_DP_ATTR_USER_FEATURES attributes passed * from userspace. */ } else { RtlZeroMemory(dpAttrs, sizeof dpAttrs); } /* Output buffer has been validated while validating transact dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); if (dpAttrs[OVS_DP_ATTR_NAME] != NULL) { if (!OvsCompareString(NlAttrGet(dpAttrs[OVS_DP_ATTR_NAME]), OVS_SYSTEM_DP_NAME)) { /* Creation of new datapaths is not supported. */ if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_SET) { nlError = NL_ERROR_NOTSUPP; goto cleanup; } nlError = NL_ERROR_NODEV; goto cleanup; } } else if ((UINT32)msgIn->ovsHdr.dp_ifindex != gOvsSwitchContext->dpNo) { nlError = NL_ERROR_NODEV; goto cleanup; } if (usrParamsCtx->ovsMsg->genlMsg.cmd == OVS_DP_CMD_NEW) { nlError = NL_ERROR_EXIST; goto cleanup; } status = OvsDpFillInfo(gOvsSwitchContext, msgIn, &nlBuf); *replyLen = NlBufSize(&nlBuf); cleanup: if (nlError != NL_ERROR_SUCCESS) { POVS_MESSAGE_ERROR msgError = (POVS_MESSAGE_ERROR) usrParamsCtx->outputBuffer; NlBuildErrorMsg(msgIn, msgError, nlError); *replyLen = msgError->nlMsg.nlmsgLen; } return STATUS_SUCCESS; } NTSTATUS OvsSetupDumpStart(POVS_USER_PARAMS_CONTEXT usrParamsCtx) { POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; /* input buffer has been validated while validating write dev op. */ ASSERT(msgIn != NULL && usrParamsCtx->inputLength >= sizeof *msgIn); /* A write operation that does not indicate dump start is invalid. */ if ((msgIn->nlMsg.nlmsgFlags & NLM_F_DUMP) != NLM_F_DUMP) { return STATUS_INVALID_PARAMETER; } /* XXX: Handle other NLM_F_* flags in the future. */ /* * This operation should be setting up the dump state. If there's any * previous state, clear it up so as to set it up afresh. */ FreeUserDumpState(instance); return InitUserDumpState(instance, msgIn); } /* * -------------------------------------------------------------------------- * Utility function to map the output buffer in an IRP. The buffer is assumed * to have been passed down using METHOD_OUT_DIRECT (Direct I/O). * -------------------------------------------------------------------------- */ static NTSTATUS MapIrpOutputBuffer(PIRP irp, UINT32 bufferLength, UINT32 requiredLength, PVOID *buffer) { ASSERT(irp); ASSERT(buffer); ASSERT(bufferLength); ASSERT(requiredLength); if (!buffer || !irp || bufferLength == 0 || requiredLength == 0) { return STATUS_INVALID_PARAMETER; } if (bufferLength < requiredLength) { return STATUS_NDIS_INVALID_LENGTH; } if (irp->MdlAddress == NULL) { return STATUS_INVALID_PARAMETER; } *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority); if (*buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * Utility function to fill up information about the state of a port in a reply * to* userspace. * -------------------------------------------------------------------------- */ static NTSTATUS OvsPortFillInfo(POVS_USER_PARAMS_CONTEXT usrParamsCtx, POVS_EVENT_ENTRY eventEntry, PNL_BUFFER nlBuf) { NTSTATUS status; BOOLEAN ok; OVS_MESSAGE msgOutTmp; PNL_MSG_HDR nlMsg; ASSERT(NlBufAt(nlBuf, 0, 0) != 0 && nlBuf->bufRemLen >= sizeof msgOutTmp); msgOutTmp.nlMsg.nlmsgType = OVS_WIN_NL_VPORT_FAMILY_ID; msgOutTmp.nlMsg.nlmsgFlags = 0; /* XXX: ? */ /* driver intiated messages should have zerp seq number*/ msgOutTmp.nlMsg.nlmsgSeq = 0; msgOutTmp.nlMsg.nlmsgPid = usrParamsCtx->ovsInstance->pid; msgOutTmp.genlMsg.version = nlVportFamilyOps.version; msgOutTmp.genlMsg.reserved = 0; /* we don't have netdev yet, treat link up/down a adding/removing a port*/ if (eventEntry->type & (OVS_EVENT_LINK_UP | OVS_EVENT_CONNECT)) { msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_NEW; } else if (eventEntry->type & (OVS_EVENT_LINK_DOWN | OVS_EVENT_DISCONNECT)) { msgOutTmp.genlMsg.cmd = OVS_VPORT_CMD_DEL; } else { ASSERT(FALSE); return STATUS_UNSUCCESSFUL; } msgOutTmp.ovsHdr.dp_ifindex = gOvsSwitchContext->dpNo; ok = NlMsgPutHead(nlBuf, (PCHAR)&msgOutTmp, sizeof msgOutTmp); if (!ok) { status = STATUS_INVALID_BUFFER_SIZE; goto cleanup; } ok = NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_PORT_NO, eventEntry->portNo) && NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_TYPE, eventEntry->ovsType) && NlMsgPutTailU32(nlBuf, OVS_VPORT_ATTR_UPCALL_PID, eventEntry->upcallPid) && NlMsgPutTailString(nlBuf, OVS_VPORT_ATTR_NAME, eventEntry->ovsName); if (!ok) { status = STATUS_INVALID_BUFFER_SIZE; goto cleanup; } /* XXXX Should we add the port stats attributes?*/ nlMsg = (PNL_MSG_HDR)NlBufAt(nlBuf, 0, 0); nlMsg->nlmsgLen = NlBufSize(nlBuf); status = STATUS_SUCCESS; cleanup: return status; } /* * -------------------------------------------------------------------------- * Handler for reading events from the driver event queue. This handler is * executed when user modes issues a socket receive on a socket assocaited * with the MC group for events. * XXX user mode should read multiple events in one system call * -------------------------------------------------------------------------- */ static NTSTATUS OvsReadEventCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx, UINT32 *replyLen) { #ifdef DBG POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer; POVS_OPEN_INSTANCE instance = (POVS_OPEN_INSTANCE)usrParamsCtx->ovsInstance; #endif NL_BUFFER nlBuf; NTSTATUS status; OVS_EVENT_ENTRY eventEntry; ASSERT(usrParamsCtx->devOp == OVS_READ_DEV_OP); /* Should never read events with a dump socket */ ASSERT(instance->dumpState.ovsMsg == NULL); /* Must have an event queue */ ASSERT(instance->eventQueue != NULL); /* Output buffer has been validated while validating read dev op. */ ASSERT(msgOut != NULL && usrParamsCtx->outputLength >= sizeof *msgOut); NlBufInit(&nlBuf, usrParamsCtx->outputBuffer, usrParamsCtx->outputLength); /* remove an event entry from the event queue */ status = OvsRemoveEventEntry(usrParamsCtx->ovsInstance, &eventEntry); if (status != STATUS_SUCCESS) { /* If there were not elements, read should return no data. */ status = STATUS_SUCCESS; *replyLen = 0; goto cleanup; } status = OvsPortFillInfo(usrParamsCtx, &eventEntry, &nlBuf); if (status == NDIS_STATUS_SUCCESS) { *replyLen = NlBufSize(&nlBuf); } cleanup: return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Tunnel.h0000644000000000000000000000013213534540071022070 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.453851008 openvswitch-2.5.9/datapath-windows/ovsext/Tunnel.h0000644000175000017500000000330013534540071023552 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TUNNEL_H_ #define __TUNNEL_H_ 1 // // OVS_TUNNEL_PENDED_PACKET is the object type we used to store all information // needed for out-of-band packet modification and re-injection. This type // also points back to the flow context the packet belongs to. typedef struct OVS_TUNNEL_PENDED_PACKET_ { /* Common fields for inbound and outbound traffic */ NET_BUFFER_LIST *netBufferList; UINT32 ipHeaderSize; UINT32 transportHeaderSize; FWPS_CLASSIFY_OUT *classifyOut; } OVS_TUNNEL_PENDED_PACKET; // // Shared function prototypes // VOID OvsTunnelClassify(const FWPS_INCOMING_VALUES *inFixedValues, const FWPS_INCOMING_METADATA_VALUES *inMetaValues, VOID *layerData, const VOID *classifyContext, const FWPS_FILTER *filter, UINT64 flowContext, FWPS_CLASSIFY_OUT *classifyOut); NTSTATUS OvsTunnelNotify(FWPS_CALLOUT_NOTIFY_TYPE notifyType, const GUID *filterKey, const FWPS_FILTER *filter); #endif /* __TUNNEL_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Jhash.h0000644000000000000000000000013213534540071021660 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.425850801 openvswitch-2.5.9/datapath-windows/ovsext/Jhash.h0000644000175000017500000000216013534540071023345 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __JHASH_H_ #define __JHASH_H_ 1 /* This is the public domain lookup3 hash by Bob Jenkins from * http://burtleburtle.net/bob/c/lookup3.c, modified for style. * * Use the functions in hash.h instead if you can. These are here just for * places where we've exposed a hash function "on the wire" and don't want it * to change. */ uint32_t OvsJhashWords(const uint32_t *, size_t n_word, uint32_t basis); uint32_t OvsJhashBytes(const void *, size_t n_bytes, uint32_t basis); #endif /* __JHASH_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/PacketParser.h0000644000000000000000000000013213534540071023207 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.445850949 openvswitch-2.5.9/datapath-windows/ovsext/PacketParser.h0000644000175000017500000001060413534540071024676 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __PACKET_PARSER_H_ #define __PACKET_PARSER_H_ 1 #include "precomp.h" #include "NetProto.h" const VOID* OvsGetPacketBytes(const NET_BUFFER_LIST *_pNB, UINT32 len, UINT32 SrcOffset, VOID *storage); NDIS_STATUS OvsParseIPv6(const NET_BUFFER_LIST *packet, OvsFlowKey *key, POVS_PACKET_HDR_INFO layers); VOID OvsParseTcp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers); VOID OvsParseUdp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers); VOID OvsParseSctp(const NET_BUFFER_LIST *packet, L4Key *flow, POVS_PACKET_HDR_INFO layers); NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER_LIST *packet, OvsFlowKey *key, POVS_PACKET_HDR_INFO layers); static __inline ULONG OvsPacketLenNBL(const NET_BUFFER_LIST *_pNB) { INT length = 0; NET_BUFFER *nb; nb = NET_BUFFER_LIST_FIRST_NB(_pNB); ASSERT(nb); while(nb) { length += NET_BUFFER_DATA_LENGTH(nb); nb = NET_BUFFER_NEXT_NB(nb); } return length; } /* * Returns the ctl field from the TCP header in 'packet', or 0 if the field * can't be read. The caller must have ensured that 'packet' contains a TCP * header. * * We can't just use TCPHdr, from netProto.h, for this because that * breaks the flags down into individual bit-fields. We can't even use * offsetof because that will try to take the address of a bit-field, * which C does not allow. */ static UINT16 OvsGetTcpCtl(const NET_BUFFER_LIST *packet, // IN const POVS_PACKET_HDR_INFO layers) // IN { #define TCP_CTL_OFS 12 // Offset of "ctl" field in TCP header. #define TCP_FLAGS(CTL) ((CTL) & 0x3f) // Obtain TCP flags from CTL. const UINT16 *ctl; UINT16 storage; ctl = OvsGetPacketBytes(packet, sizeof *ctl, layers->l4Offset + TCP_CTL_OFS, &storage); return ctl ? *ctl : 0; } static UINT8 OvsGetTcpFlags(const NET_BUFFER_LIST *packet, // IN const OvsFlowKey *key, // IN const POVS_PACKET_HDR_INFO layers) // IN { UNREFERENCED_PARAMETER(key); // should be removed later if (layers->isTcp) { return TCP_FLAGS(OvsGetTcpCtl(packet, layers)); } else { return 0; } } static const EtherArp * OvsGetArp(const NET_BUFFER_LIST *packet, UINT32 ofs, EtherArp *storage) { return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); } static const IPHdr * OvsGetIp(const NET_BUFFER_LIST *packet, UINT32 ofs, IPHdr *storage) { const IPHdr *ip = OvsGetPacketBytes(packet, sizeof *ip, ofs, storage); if (ip) { int ipLen = ip->ihl * 4; if (ipLen >= sizeof *ip && OvsPacketLenNBL(packet) >= ofs + ipLen) { return ip; } } return NULL; } static const TCPHdr * OvsGetTcp(const NET_BUFFER_LIST *packet, UINT32 ofs, TCPHdr *storage) { const TCPHdr *tcp = OvsGetPacketBytes(packet, sizeof *tcp, ofs, storage); if (tcp) { int tcpLen = tcp->doff * 4; if (tcpLen >= sizeof *tcp && OvsPacketLenNBL(packet) >= ofs + tcpLen) { return tcp; } } return NULL; } static const UDPHdr * OvsGetUdp(const NET_BUFFER_LIST *packet, UINT32 ofs, UDPHdr *storage) { return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); } static const SCTPHdr * OvsGetSctp(const NET_BUFFER_LIST *packet, UINT32 ofs, SCTPHdr *storage) { return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); } static const ICMPHdr * OvsGetIcmp(const NET_BUFFER_LIST *packet, UINT32 ofs, ICMPHdr *storage) { return OvsGetPacketBytes(packet, sizeof *storage, ofs, storage); } #endif /* __PACKET_PARSER_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Oid.c0000644000000000000000000000013213534540071021331 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.437850891 openvswitch-2.5.9/datapath-windows/ovsext/Oid.c0000644000175000017500000007142013534540071023023 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Switch.h" #include "Vport.h" #include "NetProto.h" #include "User.h" #include "Flow.h" #include "Event.h" #include "User.h" #include "Oid.h" /* Due to an imported header file */ #pragma warning( disable:4505 ) #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_DISPATCH #include "Debug.h" typedef struct _OVS_OID_CONTEXT { NDIS_EVENT oidComplete; NDIS_STATUS status; } OVS_OID_CONTEXT, *POVS_OID_CONTEXT; VOID OvsExtOidRequestComplete(NDIS_HANDLE filterModuleContext, PNDIS_OID_REQUEST oidRequest, NDIS_STATUS status); static VOID OvsOidRequestCompleteMethod(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status); static VOID OvsOidRequestCompleteSetInfo(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status); static VOID OvsOidRequestCompleteQuery(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status); static NDIS_STATUS OvsProcessSetOidPortProp(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest); static NDIS_STATUS OvsProcessSetOidPort(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest); static NDIS_STATUS OvsProcessSetOidNic(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest); __inline BOOLEAN OvsCheckOidHeaderFunc(PNDIS_OBJECT_HEADER header, LONG propRev, LONG propSize) { return header->Type != NDIS_OBJECT_TYPE_DEFAULT || header->Revision < propRev || header->Size < propSize; } #define OvsCheckOidHeader(_hdr, _rev) \ OvsCheckOidHeaderFunc(_hdr, _rev, ##NDIS_SIZEOF_##_rev) static __inline VOID OvsOidSetOrigRequest(PNDIS_OID_REQUEST clonedRequest, PNDIS_OID_REQUEST origRequest) { *(PVOID*)(&clonedRequest->SourceReserved[0]) = origRequest; } static __inline PNDIS_OID_REQUEST OvsOidGetOrigRequest(PNDIS_OID_REQUEST clonedRequest) { return *((PVOID*)(&clonedRequest->SourceReserved[0])); } static __inline VOID OvsOidSetContext(PNDIS_OID_REQUEST clonedRequest, POVS_OID_CONTEXT origRequest) { *(PVOID*)(&clonedRequest->SourceReserved[8]) = origRequest; } static __inline POVS_OID_CONTEXT OvsOidGetContext(PNDIS_OID_REQUEST clonedRequest) { return *((PVOID*)(&clonedRequest->SourceReserved[8])); } static NDIS_STATUS OvsProcessSetOidPortProp(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; struct _SET *setInfo = &(oidRequest->DATA.SET_INFORMATION); PNDIS_SWITCH_PORT_PROPERTY_PARAMETERS portPropParam = setInfo->InformationBuffer; BOOLEAN checkFailed = TRUE; UNREFERENCED_PARAMETER(switchObject); if (setInfo->Oid == OID_SWITCH_PORT_PROPERTY_DELETE) { checkFailed = OvsCheckOidHeader( (PNDIS_OBJECT_HEADER)portPropParam, NDIS_SWITCH_PORT_PROPERTY_DELETE_PARAMETERS_REVISION_1); } else { /* it must be a add or update request */ checkFailed = OvsCheckOidHeader( (PNDIS_OBJECT_HEADER)portPropParam, NDIS_SWITCH_PORT_PROPERTY_PARAMETERS_REVISION_1); } if (checkFailed) { status = NDIS_STATUS_INVALID_PARAMETER; goto done; } if (portPropParam->PropertyType == NdisSwitchPortPropertyTypeVlan) { status = NDIS_STATUS_NOT_SUPPORTED; goto done; } done: return status; } static NDIS_STATUS OvsProcessSetOidPort(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; struct _SET *setInfo = &(oidRequest->DATA.SET_INFORMATION); PNDIS_SWITCH_PORT_PARAMETERS portParam = setInfo->InformationBuffer; if (OvsCheckOidHeader((PNDIS_OBJECT_HEADER)portParam, NDIS_SWITCH_PORT_PARAMETERS_REVISION_1)) { status = NDIS_STATUS_NOT_SUPPORTED; goto done; } if (portParam->IsValidationPort) { /* Validation ports are used internally by the Hyper-V switch * to validate and verify settings. We must skip handling them, * and return STATUS_SUCCESS as the OID result */ return NDIS_STATUS_SUCCESS; } switch(setInfo->Oid) { case OID_SWITCH_PORT_CREATE: status = HvCreatePort(switchObject, portParam, 0); break; case OID_SWITCH_PORT_UPDATED: status = HvUpdatePort(switchObject, portParam); break; case OID_SWITCH_PORT_TEARDOWN: HvTeardownPort(switchObject, portParam); break; case OID_SWITCH_PORT_DELETE: HvDeletePort(switchObject, portParam); break; default: break; } done: return status; } static NDIS_STATUS OvsProcessSetOidNic(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; struct _SET *setInfo = &(oidRequest->DATA.SET_INFORMATION); PNDIS_SWITCH_NIC_PARAMETERS nicParam = setInfo->InformationBuffer; if (OvsCheckOidHeader((PNDIS_OBJECT_HEADER)nicParam, NDIS_SWITCH_NIC_PARAMETERS_REVISION_1)) { status = NDIS_STATUS_NOT_SUPPORTED; goto done; } switch(setInfo->Oid) { case OID_SWITCH_NIC_CREATE: status = HvCreateNic(switchObject, nicParam); break; case OID_SWITCH_NIC_CONNECT: HvConnectNic(switchObject, nicParam); break; case OID_SWITCH_NIC_UPDATED: HvUpdateNic(switchObject, nicParam); break; case OID_SWITCH_NIC_DISCONNECT: HvDisconnectNic(switchObject, nicParam); break; case OID_SWITCH_NIC_DELETE: HvDeleteNic(switchObject, nicParam); break; default: break; } done: return status; } static NDIS_STATUS OvsProcessSetOid(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PBOOLEAN complete) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; struct _SET *setInfo = &(oidRequest->DATA.SET_INFORMATION); *complete = FALSE; OVS_LOG_TRACE("Enter: oidRequest %p, Oid: %lu", oidRequest, setInfo->Oid); /* Verify the basic Oid paramters first */ if (setInfo->InformationBufferLength && (setInfo->InformationBufferLength < sizeof(NDIS_OBJECT_HEADER))) { status = NDIS_STATUS_INVALID_OID; OVS_LOG_INFO("Invalid input %d", setInfo->InformationBufferLength); goto error; } /* Documentation does not specify what should be done * if informationBuffer is not present. Although it mentions the * structure type informationBUffer points to for each oid request, * but it does not explicitly mention that it is a MUST. * hence we are following this scenario same way as what sample code * mentions. */ if (!(setInfo->InformationBufferLength)) { /* We cannot do anything about this oid request, * lets just pass it down. */ OVS_LOG_INFO("Buffer Length Zero"); goto done; } switch(setInfo->Oid) { case OID_SWITCH_PORT_PROPERTY_ADD: case OID_SWITCH_PORT_PROPERTY_UPDATE: case OID_SWITCH_PORT_PROPERTY_DELETE: status = OvsProcessSetOidPortProp(switchObject, oidRequest); break; case OID_SWITCH_PORT_CREATE: case OID_SWITCH_PORT_UPDATED: case OID_SWITCH_PORT_TEARDOWN: case OID_SWITCH_PORT_DELETE: status = OvsProcessSetOidPort(switchObject, oidRequest); break; case OID_SWITCH_NIC_CREATE: case OID_SWITCH_NIC_CONNECT: case OID_SWITCH_NIC_UPDATED: case OID_SWITCH_NIC_DISCONNECT: case OID_SWITCH_NIC_DELETE: status = OvsProcessSetOidNic(switchObject, oidRequest); break; default: /* Non handled OID request */ break; } if (status != NDIS_STATUS_SUCCESS) { goto error; } goto done; error: *complete = TRUE; done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } static NDIS_STATUS OvsProcessMethodOid(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PBOOLEAN complete, PULONG bytesNeededParam) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; struct _METHOD *methodInfo = &(oidRequest->DATA.METHOD_INFORMATION); struct _SET *nicReqSetInfo = NULL; PNDIS_OBJECT_HEADER header = NULL; PNDIS_OID_REQUEST nicOidRequest = NULL; UNREFERENCED_PARAMETER(switchObject); OVS_LOG_TRACE("Enter: oidRequest %p, Oid: %lu", oidRequest, methodInfo->Oid); *complete = FALSE; *bytesNeededParam = 0; header = methodInfo->InformationBuffer; switch(methodInfo->Oid) { /* We deal with only OID_SWITCH_NIC_REQUEST as of now */ case OID_SWITCH_NIC_REQUEST: if (OvsCheckOidHeader(header, NDIS_SWITCH_NIC_OID_REQUEST_REVISION_1)) { OVS_LOG_INFO("Check Header failed"); status = NDIS_STATUS_NOT_SUPPORTED; *complete = TRUE; goto done; } nicOidRequest = (((PNDIS_SWITCH_NIC_OID_REQUEST)header)->OidRequest); nicReqSetInfo = &(nicOidRequest->DATA.SET_INFORMATION); /* Fail the SR-IOV VF case */ if ((nicOidRequest->RequestType == NdisRequestSetInformation) && (nicReqSetInfo->Oid == OID_NIC_SWITCH_ALLOCATE_VF)) { OVS_LOG_INFO("We do not support Oid: " "OID_NIC_SWITCH_ALLOCATE_VF"); status = NDIS_STATUS_FAILURE; *complete = TRUE; } break; default: /* No op */ break; } done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterOidRequest function. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsExtOidRequest(NDIS_HANDLE filterModuleContext, PNDIS_OID_REQUEST oidRequest) { POVS_SWITCH_CONTEXT switchObject = (POVS_SWITCH_CONTEXT)filterModuleContext; NDIS_STATUS status = NDIS_STATUS_SUCCESS; PNDIS_OID_REQUEST clonedOidRequest = NULL; struct _METHOD *methodInfo = &(oidRequest->DATA.METHOD_INFORMATION); BOOLEAN completeOid = FALSE; ULONG bytesNeeded = 0; OVS_LOG_TRACE("Enter: oidRequest %p, reqType: %d", oidRequest, oidRequest->RequestType); status = NdisAllocateCloneOidRequest(switchObject->NdisFilterHandle, oidRequest, OVS_MEMORY_TAG, &clonedOidRequest); if (status != NDIS_STATUS_SUCCESS) { goto done; } NdisInterlockedIncrement(&(switchObject->pendingOidCount)); /* set the original oid request in cloned one. */ OvsOidSetOrigRequest(clonedOidRequest, oidRequest); OvsOidSetContext(clonedOidRequest, NULL); switch(clonedOidRequest->RequestType) { case NdisRequestSetInformation: status = OvsProcessSetOid(switchObject, clonedOidRequest, &completeOid); break; case NdisRequestMethod: status = OvsProcessMethodOid(switchObject, clonedOidRequest, &completeOid, &bytesNeeded); break; default: /* We do not handle other request types as of now. * We are just a passthrough for those. */ break; } if (completeOid == TRUE) { /* dont leave any reference back to original request, * even if we are freeing it up. */ OVS_LOG_INFO("Complete True oidRequest %p.", oidRequest); OvsOidSetOrigRequest(clonedOidRequest, NULL); NdisFreeCloneOidRequest(switchObject->NdisFilterHandle, clonedOidRequest); methodInfo->BytesNeeded = bytesNeeded; NdisInterlockedDecrement(&switchObject->pendingOidCount); goto done; } /* pass the request down */ status = NdisFOidRequest(switchObject->NdisFilterHandle, clonedOidRequest); if (status != NDIS_STATUS_PENDING) { OvsExtOidRequestComplete(switchObject, clonedOidRequest, status); /* sample code says so */ status = NDIS_STATUS_PENDING; } done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterOidRequestComplete function. * -------------------------------------------------------------------------- */ VOID OvsExtOidRequestComplete(NDIS_HANDLE filterModuleContext, PNDIS_OID_REQUEST oidRequest, NDIS_STATUS status) { POVS_SWITCH_CONTEXT switchObject = (POVS_SWITCH_CONTEXT)filterModuleContext; PNDIS_OID_REQUEST origReq = OvsOidGetOrigRequest(oidRequest); POVS_OID_CONTEXT oidContext = OvsOidGetContext(oidRequest); /* Only one of the two should be set */ ASSERT(origReq != NULL || oidContext != NULL); ASSERT(oidContext != NULL || origReq != NULL); OVS_LOG_TRACE("Enter: oidRequest %p, reqType: %d", oidRequest, oidRequest->RequestType); if (origReq == NULL) { NdisInterlockedDecrement(&(switchObject->pendingOidCount)); oidContext->status = status; NdisSetEvent(&oidContext->oidComplete); OVS_LOG_INFO("Internally generated request"); goto done; } switch(oidRequest->RequestType) { case NdisRequestMethod: OvsOidRequestCompleteMethod(switchObject, oidRequest, origReq, status); break; case NdisRequestSetInformation: OvsOidRequestCompleteSetInfo(switchObject, oidRequest, origReq, status); break; case NdisRequestQueryInformation: case NdisRequestQueryStatistics: default: OvsOidRequestCompleteQuery(switchObject, oidRequest, origReq, status); break; } OvsOidSetOrigRequest(oidRequest, NULL); NdisFreeCloneOidRequest(switchObject->NdisFilterHandle, oidRequest); NdisFOidRequestComplete(switchObject->NdisFilterHandle, origReq, status); NdisInterlockedDecrement(&(switchObject->pendingOidCount)); done: OVS_LOG_TRACE("Exit"); } static VOID OvsOidRequestCompleteMethod(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status) { UNREFERENCED_PARAMETER(status); UNREFERENCED_PARAMETER(switchObject); struct _METHOD *methodInfo = &(oidRequest->DATA.METHOD_INFORMATION); struct _METHOD *origMethodInfo = &(origOidRequest->DATA. METHOD_INFORMATION); OVS_LOG_TRACE("Enter: oidRequest %p, Oid: %lu", oidRequest, methodInfo->Oid); origMethodInfo->OutputBufferLength = methodInfo->OutputBufferLength; origMethodInfo->BytesRead = methodInfo->BytesRead; origMethodInfo->BytesNeeded = methodInfo->BytesNeeded; origMethodInfo->BytesWritten = methodInfo->BytesWritten; OVS_LOG_TRACE("Exit"); } static VOID OvsOidRequestCompleteSetInfo(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status) { struct _SET *setInfo = &(oidRequest->DATA.SET_INFORMATION); struct _SET *origSetInfo = &(origOidRequest->DATA.SET_INFORMATION); PNDIS_OBJECT_HEADER origHeader = origSetInfo->InformationBuffer; OVS_LOG_TRACE("Enter: oidRequest %p, Oid: %lu", oidRequest, setInfo->Oid); origSetInfo->BytesRead = setInfo->BytesRead; origSetInfo->BytesNeeded = setInfo->BytesNeeded; if (status != NDIS_STATUS_SUCCESS) { switch(setInfo->Oid) { case OID_SWITCH_PORT_CREATE: HvDeletePort(switchObject, (PNDIS_SWITCH_PORT_PARAMETERS)origHeader); break; case OID_SWITCH_NIC_CREATE: HvDeleteNic(switchObject, (PNDIS_SWITCH_NIC_PARAMETERS)origHeader); break; default: break; } } OVS_LOG_TRACE("Exit"); } static VOID OvsOidRequestCompleteQuery(POVS_SWITCH_CONTEXT switchObject, PNDIS_OID_REQUEST oidRequest, PNDIS_OID_REQUEST origOidRequest, NDIS_STATUS status) { UNREFERENCED_PARAMETER(switchObject); UNREFERENCED_PARAMETER(status); struct _QUERY *queryInfo = &((oidRequest->DATA).QUERY_INFORMATION); struct _QUERY *origQueryInfo = &((origOidRequest->DATA).QUERY_INFORMATION); OVS_LOG_TRACE("Enter: oidRequest %p, Oid: %lu", oidRequest, queryInfo->Oid); origQueryInfo->BytesWritten = queryInfo->BytesWritten; origQueryInfo->BytesNeeded = queryInfo->BytesNeeded; OVS_LOG_TRACE("Exit"); } /* * -------------------------------------------------------------------------- * Implements filter driver's FilterCancelOidRequest function. * -------------------------------------------------------------------------- */ VOID OvsExtCancelOidRequest(NDIS_HANDLE filterModuleContext, PVOID requestId) { OVS_LOG_TRACE("Enter: requestId: %p", requestId); UNREFERENCED_PARAMETER(filterModuleContext); UNREFERENCED_PARAMETER(requestId); } /* * -------------------------------------------------------------------------- * Utility function to issue the specified OID to the NDIS stack. The OID is * directed towards the miniport edge of the extensible switch. * An OID that gets issued may not complete immediately, and in such cases, the * function waits for the OID to complete. Thus, this function must not be * called at the PASSIVE_LEVEL. * -------------------------------------------------------------------------- */ static NDIS_STATUS OvsIssueOidRequest(POVS_SWITCH_CONTEXT switchContext, NDIS_REQUEST_TYPE oidType, UINT32 oidRequestEnum, PVOID oidInputBuffer, UINT32 inputSize, PVOID oidOutputBuffer, UINT32 outputSize, UINT32 *outputSizeNeeded) { NDIS_STATUS status; PNDIS_OID_REQUEST oidRequest; POVS_OID_CONTEXT oidContext; ULONG OvsExtOidRequestId = 'ISVO'; DBG_UNREFERENCED_PARAMETER(inputSize); DBG_UNREFERENCED_PARAMETER(oidInputBuffer); OVS_LOG_TRACE("Enter: switchContext: %p, oidType: %d", switchContext, oidType); ASSERT(oidInputBuffer == NULL || inputSize != 0); ASSERT(oidOutputBuffer == NULL || outputSize != 0); ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); oidRequest = OvsAllocateMemoryWithTag(sizeof *oidRequest, OVS_OID_POOL_TAG); if (!oidRequest) { status = NDIS_STATUS_RESOURCES; goto done; } oidContext = OvsAllocateMemoryWithTag(sizeof *oidContext, OVS_OID_POOL_TAG); if (!oidContext) { OvsFreeMemoryWithTag(oidRequest, OVS_OID_POOL_TAG); status = NDIS_STATUS_RESOURCES; goto done; } RtlZeroMemory(oidRequest, sizeof *oidRequest); RtlZeroMemory(oidContext, sizeof *oidContext); oidRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; oidRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; oidRequest->Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1; oidRequest->RequestType = oidType; oidRequest->PortNumber = 0; oidRequest->Timeout = 0; oidRequest->RequestId = (PVOID)OvsExtOidRequestId; switch(oidType) { case NdisRequestQueryInformation: oidRequest->DATA.QUERY_INFORMATION.Oid = oidRequestEnum; oidRequest->DATA.QUERY_INFORMATION.InformationBuffer = oidOutputBuffer; oidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = outputSize; break; default: ASSERT(FALSE); status = NDIS_STATUS_INVALID_PARAMETER; break; } /* * We make use of the SourceReserved field in the OID request to store * pointers to the original OID (if any), and also context for completion * (if any). */ oidContext->status = NDIS_STATUS_SUCCESS; NdisInitializeEvent(&oidContext->oidComplete); OvsOidSetOrigRequest(oidRequest, NULL); OvsOidSetContext(oidRequest, oidContext); NdisInterlockedIncrement(&(switchContext->pendingOidCount)); status = NdisFOidRequest(switchContext->NdisFilterHandle, oidRequest); if (status == NDIS_STATUS_PENDING) { NdisWaitEvent(&oidContext->oidComplete, 0); } else { NdisInterlockedDecrement(&(switchContext->pendingOidCount)); } if (status == NDIS_STATUS_INVALID_LENGTH || oidContext->status == NDIS_STATUS_INVALID_LENGTH) { switch(oidType) { case NdisRequestQueryInformation: *outputSizeNeeded = oidRequest->DATA.QUERY_INFORMATION.BytesNeeded; } } status = oidContext->status; ASSERT(status != NDIS_STATUS_PENDING); OvsFreeMemoryWithTag(oidRequest, OVS_OID_POOL_TAG); OvsFreeMemoryWithTag(oidContext, OVS_OID_POOL_TAG); done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } /* * -------------------------------------------------------------------------- * Utility function to query if the extensible switch has completed activation * successfully. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsQuerySwitchActivationComplete(POVS_SWITCH_CONTEXT switchContext, BOOLEAN *switchActive) { NDIS_STATUS status; PNDIS_SWITCH_PARAMETERS switchParams; UINT32 outputSizeNeeded; OVS_LOG_TRACE("Enter: switchContext: %p, switchActive: %p", switchContext, switchActive); switchParams = OvsAllocateMemoryWithTag(sizeof *switchParams, OVS_OID_POOL_TAG); if (!switchParams) { status = NDIS_STATUS_RESOURCES; goto done; } /* * Even though 'switchParms' is supposed to be populated by the OID, it * needs to be initialized nevertheless. Otherwise, OID returns * NDIS_STATUS_INVALID_PARAMETER. This is not clear in the documentation. */ RtlZeroMemory(switchParams, sizeof *switchParams); switchParams->Header.Revision = NDIS_SWITCH_PARAMETERS_REVISION_1; switchParams->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; switchParams->Header.Size = NDIS_SIZEOF_NDIS_SWITCH_PARAMETERS_REVISION_1; status = OvsIssueOidRequest(switchContext, NdisRequestQueryInformation, OID_SWITCH_PARAMETERS, NULL, 0, (PVOID)switchParams, sizeof *switchParams, &outputSizeNeeded); ASSERT(status != NDIS_STATUS_INVALID_LENGTH); ASSERT(status != NDIS_STATUS_PENDING); if (status == NDIS_STATUS_SUCCESS) { ASSERT(switchParams->Header.Type == NDIS_OBJECT_TYPE_DEFAULT); ASSERT(switchParams->Header.Revision == NDIS_SWITCH_PARAMETERS_REVISION_1); ASSERT(switchParams->Header.Size == NDIS_SIZEOF_NDIS_SWITCH_PARAMETERS_REVISION_1); *switchActive = switchParams->IsActive; } OvsFreeMemoryWithTag(switchParams, OVS_OID_POOL_TAG); done: OVS_LOG_TRACE("Exit: status %8x, switchActive: %d.", status, *switchActive); return status; } /* * -------------------------------------------------------------------------- * Utility function to get the array of ports on the extensible switch. Upon * success, the caller needs to free the returned array. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsGetPortsOnSwitch(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_ARRAY *portArrayOut) { PNDIS_SWITCH_PORT_ARRAY portArray; UINT32 arraySize = sizeof *portArray; NDIS_STATUS status = NDIS_STATUS_FAILURE; OVS_LOG_TRACE("Enter: switchContext: %p, portArray: %p", switchContext, portArrayOut); do { UINT32 reqdArraySize; portArray = OvsAllocateMemoryWithTag(arraySize, OVS_OID_POOL_TAG); if (!portArray) { status = NDIS_STATUS_RESOURCES; goto done; } /* * Even though 'portArray' is supposed to be populated by the OID, it * needs to be initialized nevertheless. Otherwise, OID returns * NDIS_STATUS_INVALID_PARAMETER. This is not clear in the documentation. */ RtlZeroMemory(portArray, sizeof *portArray); portArray->Header.Revision = NDIS_SWITCH_PORT_ARRAY_REVISION_1; portArray->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; portArray->Header.Size = NDIS_SIZEOF_NDIS_SWITCH_PORT_ARRAY_REVISION_1; status = OvsIssueOidRequest(switchContext, NdisRequestQueryInformation, OID_SWITCH_PORT_ARRAY, NULL, 0, (PVOID)portArray, arraySize, &reqdArraySize); if (status == NDIS_STATUS_SUCCESS) { *portArrayOut = portArray; break; } OvsFreeMemoryWithTag(portArray, OVS_OID_POOL_TAG); arraySize = reqdArraySize; if (status != NDIS_STATUS_INVALID_LENGTH) { break; } } while(status == NDIS_STATUS_INVALID_LENGTH); done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } /* * -------------------------------------------------------------------------- * Utility function to get the array of nics on the extensible switch. Upon * success, the caller needs to free the returned array. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsGetNicsOnSwitch(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_ARRAY *nicArrayOut) { PNDIS_SWITCH_NIC_ARRAY nicArray; UINT32 arraySize = sizeof *nicArray; NDIS_STATUS status = NDIS_STATUS_FAILURE; OVS_LOG_TRACE("Enter: switchContext: %p, nicArray: %p", switchContext, nicArrayOut); do { UINT32 reqdArraySize; nicArray = OvsAllocateMemoryWithTag(arraySize, OVS_OID_POOL_TAG); if (!nicArray) { status = NDIS_STATUS_RESOURCES; goto done; } /* * Even though 'nicArray' is supposed to be populated by the OID, it * needs to be initialized nevertheless. Otherwise, OID returns * NDIS_STATUS_INVALID_PARAMETER. This is not clear in the documentation. */ RtlZeroMemory(nicArray, sizeof *nicArray); nicArray->Header.Revision = NDIS_SWITCH_NIC_ARRAY_REVISION_1; nicArray->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; nicArray->Header.Size = NDIS_SIZEOF_NDIS_SWITCH_NIC_ARRAY_REVISION_1; status = OvsIssueOidRequest(switchContext, NdisRequestQueryInformation, OID_SWITCH_NIC_ARRAY, NULL, 0, (PVOID)nicArray, arraySize, &reqdArraySize); if (status == NDIS_STATUS_SUCCESS) { *nicArrayOut = nicArray; break; } OvsFreeMemoryWithTag(nicArray, OVS_OID_POOL_TAG); arraySize = reqdArraySize; if (status != NDIS_STATUS_INVALID_LENGTH) { break; } } while(status == NDIS_STATUS_INVALID_LENGTH); done: OVS_LOG_TRACE("Exit: status %8x.", status); return status; } VOID OvsFreeSwitchPortsArray(PNDIS_SWITCH_PORT_ARRAY portsArray) { if (portsArray) { OvsFreeMemoryWithTag(portsArray, OVS_OID_POOL_TAG); } } VOID OvsFreeSwitchNicsArray(PNDIS_SWITCH_NIC_ARRAY nicsArray) { if (nicsArray) { OvsFreeMemoryWithTag(nicsArray, OVS_OID_POOL_TAG); } } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/ovsext.inf0000644000000000000000000000013213534540071022500 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.469851126 openvswitch-2.5.9/datapath-windows/ovsext/ovsext.inf0000644000175000017500000000436413534540071024175 0ustar00jpettitjpettit00000000000000; ; Copyright (c) VMware. All Rights Reserved. ; [version] Signature = "$Windows NT$" Class = NetService ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} Provider = %OVS% CatalogFile = ovsext.cat DriverVer = 10/10/2013,1.0 [Manufacturer] %OVS%=OVS,NTx86,NTia64,NTamd64 [OVS.NTx86] %OVSExt_Desc%=Install, OVSExt [OVS.NTia64] %OVSExt_Desc%=Install, OVSExt [OVS.NTamd64] %OVSExt_Desc%=Install, OVSExt ;------------------------------------------------------------------------- ; Installation Section ;------------------------------------------------------------------------- [Install] AddReg=Inst_Ndi Characteristics=0x40000 NetCfgInstanceId="{583CC151-73EC-4A6A-8B47-578297AD7623}" Copyfiles = OVSExt.copyfiles.sys [SourceDisksNames] 1=%OVSExt_Desc%,"",, [SourceDisksFiles] OVSExt.sys=1 [DestinationDirs] DefaultDestDir=12 OVSExt.copyfiles.sys=12 [OVSExt.copyfiles.sys] OVSExt.sys,,,2 ;------------------------------------------------------------------------- ; Ndi installation support ;------------------------------------------------------------------------- [Inst_Ndi] HKR, Ndi,Service,,"OVSExt" HKR, Ndi,CoServices,0x00010000,"OVSExt" HKR, Ndi,HelpText,,%OVSExt_HelpText% HKR, Ndi,FilterClass,,"ms_switch_forward" HKR, Ndi,FilterType,0x00010001,0x00000002 HKR, Ndi\Interfaces,UpperRange,,"noupper" HKR, Ndi\Interfaces,LowerRange,,"nolower" HKR, Ndi\Interfaces, FilterMediaTypes,,"vmnetextension" HKR, Ndi,FilterRunType, 0x00010001, 2 ; optional ;------------------------------------------------------------------------- ; Service installation support, common.EventLog here is to demonstrate how to ; write an enent log ;------------------------------------------------------------------------- [Install.Services] AddService=OVSExt,,OVSExt_Service_Inst;, common.EventLog [OVSExt_Service_Inst] DisplayName = %OVSExt_Desc% ServiceType = 1 ;SERVICE_KERNEL_DRIVER StartType = 1 ;SERVICE_SYSTEM_START ErrorControl = 1 ;SERVICE_ERROR_NORMAL ServiceBinary = %12%\OVSExt.sys LoadOrderGroup = NDIS Description = %OVSExt_Desc% AddReg = Common.Params.reg [Install.Remove.Services] DelService=OVSExt,0x200 [Strings] OVS = "Open vSwitch" OVSExt_Desc = "Open vSwitch Extension" OVSExt_HelpText = "Open vSwitch forwarding switch extension" openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Types.h0000644000000000000000000000013213534540071021727 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.457851038 openvswitch-2.5.9/datapath-windows/ovsext/Types.h0000644000175000017500000000246413534540071023423 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TYPES_H_ #define __TYPES_H_ 1 /* Defines the userspace specific data types * for files included from user space. */ typedef unsigned long long uint64, uint64_t, ovs_be64, u64; typedef long long int64, int64_t; typedef unsigned int uint32, uint32_t, ovs_be32, u32; typedef unsigned short uint16, uint16_t, ovs_be16, u16; typedef unsigned char uint8, uint8_t, u8; typedef uint64 __u64, __be64; typedef uint32 __u32, __be32; typedef uint16 __u16, __be16; typedef uint8 __u8; /* Defines the userspace specific data types for file * included within kernel only. */ typedef UINT8 BE8; typedef UINT16 BE16; typedef UINT32 BE32; typedef UINT64 BE64; #define ETH_ALEN 6 #define SIZE_MAX MAXUINT32 #endif /* __TYPES_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Debug.c0000644000000000000000000000013113534540071021643 xustar0029 mtime=1567801401.20967976 30 atime=1567801402.045685899 30 ctime=1567801424.365850359 openvswitch-2.5.9/datapath-windows/ovsext/Debug.c0000644000175000017500000000301713534540071023333 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Debug.h" #ifdef DBG #define OVS_DBG_DEFAULT OVS_DBG_INFO #else #define OVS_DBG_DEFAULT OVS_DBG_ERROR #endif UINT32 ovsLogFlags = 0xffffffff; UINT32 ovsLogLevel = OVS_DBG_DEFAULT; #define OVS_LOG_BUFFER_SIZE 384 /* * -------------------------------------------------------------------------- * OvsLog -- * Utility function to log to the Windows debug console. * -------------------------------------------------------------------------- */ VOID OvsLog(UINT32 level, UINT32 flag, CHAR *funcName, UINT32 line, CHAR *format, ...) { va_list args; CHAR buf[OVS_LOG_BUFFER_SIZE]; if (level > ovsLogLevel || (ovsLogFlags & flag) == 0) { return; } buf[0] = 0; va_start(args, format); RtlStringCbVPrintfA(buf, sizeof (buf), format, args); va_end(args); DbgPrintEx(DPFLTR_IHVNETWORK_ID, level, "%s:%lu %s\n", funcName, line, buf); } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Vxlan.h0000644000000000000000000000013213534540071021713 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.469851126 openvswitch-2.5.9/datapath-windows/ovsext/Vxlan.h0000644000175000017500000000535513534540071023411 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __VXLAN_H_ #define __VXLAN_H_ 1 #include "NetProto.h" typedef struct _OVS_VXLAN_VPORT { UINT16 dstPort; UINT64 inPkts; UINT64 outPkts; UINT64 slowInPkts; UINT64 slowOutPkts; UINT64 filterID; UINT64 ipId; /* * To be filled */ } OVS_VXLAN_VPORT, *POVS_VXLAN_VPORT; /* VXLAN header. */ typedef struct VXLANHdr { /* Flags. */ UINT32 flags1:2; /* Packet needs replication to multicast group (used for multicast proxy). */ UINT32 locallyReplicate:1; /* Instance ID flag, must be set to 1. */ UINT32 instanceID:1; /* Flags. */ UINT32 flags2:4; /* Reserved. */ UINT32 reserved1:24; /* VXLAN ID. */ UINT32 vxlanID:24; /* Reserved. */ UINT32 reserved2:8; } VXLANHdr; NTSTATUS OvsInitVxlanTunnel(PIRP irp, POVS_VPORT_ENTRY vport, UINT16 udpDestPort, PFNTunnelVportPendingOp callback, PVOID tunnelContext); NTSTATUS OvsCleanupVxlanTunnel(PIRP irp, POVS_VPORT_ENTRY vport, PFNTunnelVportPendingOp callback, PVOID tunnelContext); NDIS_STATUS OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet, OvsIPv4TunnelKey *tunnelKey); NDIS_STATUS OvsEncapVxlan(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl); NDIS_STATUS OvsDecapVxlan(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl); static __inline UINT32 OvsGetVxlanTunHdrSize(VOID) { /* XXX: Can L2 include VLAN at all? */ return sizeof (EthHdr) + sizeof (IPHdr) + sizeof (UDPHdr) + sizeof (VXLANHdr); } #define VXLAN_UDP_PORT 4789 #define VXLAN_UDP_PORT_NBO 0xB512 #endif /* __VXLAN_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Gre.c0000644000000000000000000000013213534540071021333 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.421850772 openvswitch-2.5.9/datapath-windows/ovsext/Gre.c0000644000175000017500000003624113534540071023027 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Cloudbase Solutions Srl * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #include "Atomic.h" #include "Checksum.h" #include "Flow.h" #include "Gre.h" #include "IpHelper.h" #include "NetProto.h" #include "PacketIO.h" #include "PacketParser.h" #include "Switch.h" #include "User.h" #include "Util.h" #include "Vport.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_GRE #include "Debug.h" static NDIS_STATUS OvsDoEncapGre(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, const OvsIPv4TunnelKey *tunKey, const POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl); /* * -------------------------------------------------------------------------- * OvsInitGreTunnel -- * Initialize GRE tunnel module. * -------------------------------------------------------------------------- */ NTSTATUS OvsInitGreTunnel(POVS_VPORT_ENTRY vport) { POVS_GRE_VPORT grePort; grePort = (POVS_GRE_VPORT)OvsAllocateMemoryWithTag(sizeof(*grePort), OVS_GRE_POOL_TAG); if (!grePort) { OVS_LOG_ERROR("Insufficient memory, can't allocate OVS_GRE_VPORT"); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(grePort, sizeof(*grePort)); vport->priv = (PVOID)grePort; return STATUS_SUCCESS; } /* * -------------------------------------------------------------------------- * OvsCleanupGreTunnel -- * Cleanup GRE Tunnel module. * -------------------------------------------------------------------------- */ void OvsCleanupGreTunnel(POVS_VPORT_ENTRY vport) { if (vport->ovsType != OVS_VPORT_TYPE_GRE || vport->priv == NULL) { return; } OvsFreeMemoryWithTag(vport->priv, OVS_GRE_POOL_TAG); vport->priv = NULL; } /* * -------------------------------------------------------------------------- * OvsEncapGre -- * Encapsulates a packet with an GRE header. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsEncapGre(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, POVS_SWITCH_CONTEXT switchContext, POVS_PACKET_HDR_INFO layers, PNET_BUFFER_LIST *newNbl) { OVS_FWD_INFO fwdInfo; NDIS_STATUS status; status = OvsLookupIPFwdInfo(tunKey->dst, &fwdInfo); if (status != STATUS_SUCCESS) { OvsFwdIPHelperRequest(NULL, 0, tunKey, NULL, NULL, NULL); return NDIS_STATUS_FAILURE; } status = OvsDoEncapGre(vport, curNbl, tunKey, &fwdInfo, layers, switchContext, newNbl); return status; } /* * -------------------------------------------------------------------------- * OvsDoEncapGre -- * Internal utility function which actually does the GRE encap. * -------------------------------------------------------------------------- */ NDIS_STATUS OvsDoEncapGre(POVS_VPORT_ENTRY vport, PNET_BUFFER_LIST curNbl, const OvsIPv4TunnelKey *tunKey, const POVS_FWD_INFO fwdInfo, POVS_PACKET_HDR_INFO layers, POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST *newNbl) { NDIS_STATUS status; PNET_BUFFER curNb; PMDL curMdl; PUINT8 bufferStart; EthHdr *ethHdr; IPHdr *ipHdr; PGREHdr greHdr; POVS_GRE_VPORT vportGre; UINT32 headRoom = GreTunHdrSize(tunKey->flags); #if DBG UINT32 counterHeadRoom; #endif UINT32 packetLength; ULONG mss = 0; ASSERT(*newNbl == NULL); curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); if (layers->isTcp) { NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo; tsoInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpLargeSendNetBufferListInfo); switch (tsoInfo.Transmit.Type) { case NDIS_TCP_LARGE_SEND_OFFLOAD_V1_TYPE: mss = tsoInfo.LsoV1Transmit.MSS; break; case NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE: mss = tsoInfo.LsoV2Transmit.MSS; break; default: OVS_LOG_ERROR("Unknown LSO transmit type:%d", tsoInfo.Transmit.Type); } OVS_LOG_TRACE("MSS %u packet len %u", mss, packetLength); if (mss) { OVS_LOG_TRACE("l4Offset %d", layers->l4Offset); *newNbl = OvsTcpSegmentNBL(switchContext, curNbl, layers, mss, headRoom); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to segment NBL"); return NDIS_STATUS_FAILURE; } /* Clear out LSO flags after this point */ NET_BUFFER_LIST_INFO(*newNbl, TcpLargeSendNetBufferListInfo) = 0; } } vportGre = (POVS_GRE_VPORT)GetOvsVportPriv(vport); ASSERT(vportGre); /* If we didn't split the packet above, make a copy now */ if (*newNbl == NULL) { *newNbl = OvsPartialCopyNBL(switchContext, curNbl, 0, headRoom, FALSE /*NBL info*/); if (*newNbl == NULL) { OVS_LOG_ERROR("Unable to copy NBL"); return NDIS_STATUS_FAILURE; } /* * To this point we do not have GRE hardware offloading. * Apply defined checksums */ curNb = NET_BUFFER_LIST_FIRST_NB(*newNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO csumInfo; csumInfo.Value = NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo); bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (layers->isIPv4) { IPHdr *ip = (IPHdr *)(bufferStart + layers->l3Offset); if (csumInfo.Transmit.IpHeaderChecksum) { ip->check = 0; ip->check = IPChecksum((UINT8 *)ip, 4 * ip->ihl, 0); } if (layers->isTcp && csumInfo.Transmit.TcpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset); tcp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr, IPPROTO_TCP, csumLength); tcp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } else if (layers->isUdp && csumInfo.Transmit.UdpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip); udp->check = IPPseudoChecksum(&ip->saddr, &ip->daddr, IPPROTO_UDP, csumLength); udp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } } else if (layers->isIPv6) { IPv6Hdr *ip = (IPv6Hdr *)(bufferStart + layers->l3Offset); if (layers->isTcp && csumInfo.Transmit.TcpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); TCPHdr *tcp = (TCPHdr *)(bufferStart + layers->l4Offset); tcp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr, (UINT32 *) &ip->daddr, IPPROTO_TCP, csumLength); tcp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } else if (layers->isUdp && csumInfo.Transmit.UdpChecksum) { UINT16 csumLength = (UINT16)(packetLength - layers->l4Offset); UDPHdr *udp = (UDPHdr *)((PCHAR)ip + sizeof *ip); udp->check = IPv6PseudoChecksum((UINT32 *) &ip->saddr, (UINT32 *) &ip->daddr, IPPROTO_UDP, csumLength); udp->check = CalculateChecksumNB(curNb, csumLength, (UINT32)(layers->l4Offset)); } } /* Clear out TcpIpChecksumNetBufferListInfo flag */ NET_BUFFER_LIST_INFO(*newNbl, TcpIpChecksumNetBufferListInfo) = 0; } curNbl = *newNbl; for (curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curNb != NULL; curNb = curNb->Next) { #if DBG counterHeadRoom = headRoom; #endif status = NdisRetreatNetBufferDataStart(curNb, headRoom, 0, NULL); if (status != NDIS_STATUS_SUCCESS) { goto ret_error; } curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto ret_error; } bufferStart += NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (NET_BUFFER_NEXT_NB(curNb)) { OVS_LOG_TRACE("nb length %u next %u", NET_BUFFER_DATA_LENGTH(curNb), NET_BUFFER_DATA_LENGTH(curNb->Next)); } /* L2 header */ ethHdr = (EthHdr *)bufferStart; ASSERT(((PCHAR)&fwdInfo->dstMacAddr + sizeof fwdInfo->dstMacAddr) == (PCHAR)&fwdInfo->srcMacAddr); NdisMoveMemory(ethHdr->Destination, fwdInfo->dstMacAddr, sizeof ethHdr->Destination + sizeof ethHdr->Source); ethHdr->Type = htons(ETH_TYPE_IPV4); #if DBG counterHeadRoom -= sizeof *ethHdr; #endif /* IP header */ ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); ipHdr->ihl = sizeof *ipHdr / 4; ipHdr->version = IPPROTO_IPV4; ipHdr->tos = tunKey->tos; ipHdr->tot_len = htons(NET_BUFFER_DATA_LENGTH(curNb) - sizeof *ethHdr); ipHdr->id = (uint16)atomic_add64(&vportGre->ipId, NET_BUFFER_DATA_LENGTH(curNb)); ipHdr->frag_off = (tunKey->flags & OVS_TNL_F_DONT_FRAGMENT) ? IP_DF_NBO : 0; ipHdr->ttl = tunKey->ttl ? tunKey->ttl : 64; ipHdr->protocol = IPPROTO_GRE; ASSERT(tunKey->dst == fwdInfo->dstIpAddr); ASSERT(tunKey->src == fwdInfo->srcIpAddr || tunKey->src == 0); ipHdr->saddr = fwdInfo->srcIpAddr; ipHdr->daddr = fwdInfo->dstIpAddr; ipHdr->check = 0; ipHdr->check = IPChecksum((UINT8 *)ipHdr, sizeof *ipHdr, 0); #if DBG counterHeadRoom -= sizeof *ipHdr; #endif /* GRE header */ greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr); greHdr->flags = OvsTunnelFlagsToGreFlags(tunKey->flags); greHdr->protocolType = GRE_NET_TEB; #if DBG counterHeadRoom -= sizeof *greHdr; #endif PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr; if (tunKey->flags & OVS_TNL_F_CSUM) { RtlZeroMemory(currentOffset, 4); currentOffset += 4; #if DBG counterHeadRoom -= 4; #endif } if (tunKey->flags & OVS_TNL_F_KEY) { RtlZeroMemory(currentOffset, 4); UINT32 key = (tunKey->tunnelId >> 32); RtlCopyMemory(currentOffset, &key, sizeof key); currentOffset += 4; #if DBG counterHeadRoom -= 4; #endif } #if DBG ASSERT(counterHeadRoom == 0); #endif } return STATUS_SUCCESS; ret_error: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; } NDIS_STATUS OvsDecapGre(POVS_SWITCH_CONTEXT switchContext, PNET_BUFFER_LIST curNbl, OvsIPv4TunnelKey *tunKey, PNET_BUFFER_LIST *newNbl) { PNET_BUFFER curNb; PMDL curMdl; EthHdr *ethHdr; IPHdr *ipHdr; GREHdr *greHdr; UINT32 tunnelSize = 0, packetLength = 0; UINT32 headRoom = 0; PUINT8 bufferStart; NDIS_STATUS status; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); packetLength = NET_BUFFER_DATA_LENGTH(curNb); tunnelSize = GreTunHdrSize(tunKey->flags); if (packetLength <= tunnelSize) { return NDIS_STATUS_INVALID_LENGTH; } /* * Create a copy of the NBL so that we have all the headers in one MDL. */ *newNbl = OvsPartialCopyNBL(switchContext, curNbl, tunnelSize + OVS_DEFAULT_COPY_SIZE, 0, TRUE /*copy NBL info */); if (*newNbl == NULL) { return NDIS_STATUS_RESOURCES; } curNbl = *newNbl; curNb = NET_BUFFER_LIST_FIRST_NB(curNbl); curMdl = NET_BUFFER_CURRENT_MDL(curNb); bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority) + NET_BUFFER_CURRENT_MDL_OFFSET(curNb); if (!bufferStart) { status = NDIS_STATUS_RESOURCES; goto dropNbl; } ethHdr = (EthHdr *)bufferStart; headRoom += sizeof *ethHdr; ipHdr = (IPHdr *)((PCHAR)ethHdr + sizeof *ethHdr); tunKey->src = ipHdr->saddr; tunKey->dst = ipHdr->daddr; tunKey->tos = ipHdr->tos; tunKey->ttl = ipHdr->ttl; tunKey->pad = 0; headRoom += sizeof *ipHdr; greHdr = (GREHdr *)((PCHAR)ipHdr + sizeof *ipHdr); headRoom += sizeof *greHdr; /* Validate if GRE header protocol type. */ if (greHdr->protocolType != GRE_NET_TEB) { status = STATUS_NDIS_INVALID_PACKET; goto dropNbl; } PCHAR currentOffset = (PCHAR)greHdr + sizeof *greHdr; if (greHdr->flags & GRE_CSUM) { tunKey->flags |= OVS_TNL_F_CSUM; currentOffset += 4; headRoom += 4; } if (greHdr->flags & GRE_KEY) { tunKey->flags |= OVS_TNL_F_KEY; UINT32 key = 0; RtlCopyMemory(&key, currentOffset, 4); tunKey->tunnelId = (UINT64)key << 32; currentOffset += 4; headRoom += 4; } /* Clear out the receive flag for the inner packet. */ NET_BUFFER_LIST_INFO(curNbl, TcpIpChecksumNetBufferListInfo) = 0; NdisAdvanceNetBufferDataStart(curNb, GreTunHdrSize(tunKey->flags), FALSE, NULL); ASSERT(headRoom == GreTunHdrSize(tunKey->flags)); return NDIS_STATUS_SUCCESS; dropNbl: OvsCompleteNBL(switchContext, *newNbl, TRUE); *newNbl = NULL; return status; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/NetProto.h0000644000000000000000000000013213534540071022375 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.425850801 openvswitch-2.5.9/datapath-windows/ovsext/NetProto.h0000644000175000017500000002672213534540071024074 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __NET_PROTO_H_ #define __NET_PROTO_H_ 1 #include "precomp.h" #include "Ethernet.h" #define ETH_ADDR_LENGTH 6 /* * There is a more inclusive definition of ethernet header (Eth_Header) in * OvsEth.h that is used for packet parsing. For simple cases, , use the following definition. */ typedef struct EthHdr { UINT8 Destination[ETH_ADDR_LENGTH]; UINT8 Source[ETH_ADDR_LENGTH]; UINT16 Type; } EthHdr, *PEthHdr; #define IPV4 4 #define IPV6 6 #define IP_HDR_MIN_LENGTH 20 #define TCP_HDR_MIN_LENGTH 20 #define TCP_CSUM_OFFSET 16 #define UDP_CSUM_OFFSET 6 #define ICMP_CSUM_OFFSET 2 #define INET_CSUM_LENGTH (sizeof(UINT16)) #define IP4_UNITS_TO_BYTES(x) ((x) << 2) #define IP4_BYTES_TO_UNITS(x) ((x) >> 2) // length unit for ip->ihl, tcp->doff typedef UINT32 IP4UnitLength; #define IP4_LENGTH_UNIT (sizeof(IP4UnitLength)) #define IP4_HDR_MIN_LENGTH_IN_UNITS (IP_HDR_MIN_LENGTH / IP4_LENGTH_UNIT) #define TCP_HDR_MIN_LENGTH_IN_UNITS (TCP_HDR_MIN_LENGTH / IP4_LENGTH_UNIT) #define IP4_IHL_NO_OPTIONS IP4_HDR_MIN_LENGTH_IN_UNITS #define IP4_HDR_LEN(iph) IP4_UNITS_TO_BYTES((iph)->ihl) // length unit for ip->frag_off typedef UINT64 IP4FragUnitLength; #define IP4_FRAG_UNIT_LENGTH (sizeof(IP4FragUnitLength)) // length UINT for ipv6 header length. typedef UINT64 IP6UnitLength; #define TCP_HDR_LEN(tcph) IP4_UNITS_TO_BYTES((tcph)->doff) #define TCP_DATA_LENGTH(iph, tcph) (ntohs(iph->tot_len) - \ IP4_HDR_LEN(iph) - TCP_HDR_LEN(tcph)) #define TCP_DATA_OFFSET_NO_OPTIONS TCP_HDR_MIN_LENGTH_IN_UNITS #define TCP_DATA_OFFSET_WITH_TIMESTAMP 8 /* * This is the maximum value for the length field in the IP header. The meaning * varies with IP protocols: * IPv4: the total ip length (including ip header and extention) * IPv6: the IP payload length (including IP extensions) */ #define IP_MAX_PACKET 0xFFFF #define IPPROTO_ICMP 1 #define IPPROTO_IGMP 2 #define IPPROTO_UDP 17 #define IPPROTO_GRE 47 #define IPPROTO_TCP 6 #define IPPROTO_SCTP 132 #define IPPROTO_RSVD 0xff #define IPPROTO_HOPOPTS 0 /* Hop-by-hop option header */ #define IPPROTO_IPV6 41 /* IPv6 in IPv6 */ #define IPPROTO_ROUTING 43 /* Routing header */ #define IPPROTO_FRAGMENT 44 /* Fragmentation/reassembly header */ #define IPPROTO_GRE 47 /* General Routing Encapsulation */ #define IPPROTO_ESP 50 /* Encap. Security Payload */ #define IPPROTO_AH 51 /* Authentication header */ #define IPPROTO_ICMPV6 58 /* ICMP for IPv6 */ #define IPPROTO_NONE 59 /* No next header */ #define IPPROTO_DSTOPTS 60 /* Destination options header */ #define IPPROTO_ETHERIP 97 /* etherIp tunneled protocol */ /* ICMPv6 types. */ #define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */ #define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisment */ /* IPv6 Neighbor discovery option header. */ #define ND_OPT_SOURCE_LINKADDR 1 #define ND_OPT_TARGET_LINKADDR 2 /* Collides with MS definition (opposite order) */ #define IP6F_OFF_HOST_ORDER_MASK 0xfff8 #define ARPOP_REQUEST 1 /* ARP request. */ #define ARPOP_REPLY 2 /* ARP reply. */ #define RARPOP_REQUEST 3 /* RARP request. */ #define RARPOP_REPLY 4 /* RARP reply. */ /* all ARP NBO's assume short ar_op */ #define ARPOP_REQUEST_NBO 0x0100 /* NBO ARP request. */ #define ARPOP_REPLY_NBO 0x0200 /* NBO ARP reply. */ #define RARPOP_REQUEST_NBO 0x0300 /* NBO RARP request. */ #define RARPOP_REPLY_NBO 0x0300 /* NBO RARP reply. */ #define ICMP_ECHO 8 /* Echo Request */ #define ICMP_ECHOREPLY 0 /* Echo Reply */ #define ICMP_DEST_UNREACH 3 /* Destination Unreachable */ /* IGMP related constants */ #define IGMP_UNKNOWN 0x00 /* For IGMP packets where we don't know the type */ /* Eg: Fragmented packets without the header */ /* Constants from RFC 3376 */ #define IGMP_QUERY 0x11 /* IGMP Host Membership Query. */ #define IGMP_V1REPORT 0x12 /* IGMPv1 Host Membership Report. */ #define IGMP_V2REPORT 0x16 /* IGMPv2 Host Membership Report. */ #define IGMP_V3REPORT 0x22 /* IGMPv3 Host Membership Report. */ #define IGMP_V2LEAVE 0x17 /* IGMPv2 Leave. */ /* Constants from RFC 2710 and RFC 3810 */ #define MLD_QUERY 0x82 /* Multicast Listener Query. */ #define MLD_V1REPORT 0x83 /* Multicast Listener V1 Report. */ #define MLD_V2REPORT 0x8F /* Multicast Listener V2 Report. */ #define MLD_DONE 0x84 /* Multicast Listener Done. */ /* IPv4 offset flags */ #define IP_CE 0x8000 /* Flag: "Congestion" */ #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ #define IP_MF 0x2000 /* Flag: "More Fragments" */ #define IP_OFFSET 0x1FFF /* "Fragment Offset" part */ #define IP_OFFSET_NBO 0xFF1F /* "Fragment Offset" part, NBO */ #define IP_DF_NBO 0x0040 /* NBO version of don't fragment */ #define IP_MF_NBO 0x0020 /* NBO version of more fragments */ #define IPOPT_RTRALT 0x94 /* IP Explicit Congestion Notification bits (TOS field) */ #define IP_ECN_NOT_ECT 0 #define IP_ECN_ECT_1 1 #define IP_ECN_ECT_0 2 #define IP_ECN_CE 3 #define IP_ECN_MASK 3 /* TCP options */ #define TCP_OPT_NOP 1 /* Padding */ #define TCP_OPT_EOL 0 /* End of options */ #define TCP_OPT_MSS 2 /* Segment size negotiating */ #define TCP_OPT_WINDOW 3 /* Window scaling */ #define TCP_OPT_SACK_PERM 4 /* SACK Permitted */ #define TCP_OPT_SACK 5 /* SACK Block */ #define TCP_OPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCP_OPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ #define TCP_OPT_LEN_MSS 4 #define TCP_OPT_LEN_WINDOW 3 #define TCP_OPT_LEN_SACK_PERM 2 #define TCP_OPT_LEN_TIMESTAMP 10 #define TCP_OPT_LEN_MD5SIG 18 #define SOCKET_IPPROTO_HOPOPTS IPPROTO_HOPOPTS #define SOCKET_IPPROTO_ROUTING IPPROTO_ROUTING #define SOCKET_IPPROTO_FRAGMENT IPPROTO_FRAGMENT #define SOCKET_IPPROTO_AH IPPROTO_AH #define SOCKET_IPPROTO_ICMPV6 IPPROTO_ICMPV6 #define SOCKET_IPPROTO_NONE IPPROTO_NONE #define SOCKET_IPPROTO_DSTOPTS IPPROTO_DSTOPTS #define SOCKET_IPPROTO_EON 80 #define SOCKET_IPPROTO_ETHERIP IPPROTO_ETHERIP #define SOCKET_IPPROTO_ENCAP 98 #define SOCKET_IPPROTO_PIM 103 #define SOCKET_IPPROTO_IPCOMP 108 #define SOCKET_IPPROTO_CARP 112 #define SOCKET_IPPROTO_PFSYNC 240 #define SOCKET_IPPROTO_RAW IPPROTO_RSVD typedef union _OVS_PACKET_HDR_INFO { struct { UINT16 l3Offset; UINT16 l4Offset; union { UINT16 l7Offset; UINT16 l4PayLoad; }; UINT16 isIPv4:1; UINT16 isIPv6:1; UINT16 isTcp:1; UINT16 isUdp:1; UINT16 isSctp:1; UINT16 tcpCsumNeeded:1; UINT16 udpCsumNeeded:1; UINT16 udpCsumZero:1; UINT16 pad:8; } ; UINT64 value; } OVS_PACKET_HDR_INFO, *POVS_PACKET_HDR_INFO; typedef struct IPHdr { UINT8 ihl:4, version:4; UINT8 tos; UINT16 tot_len; UINT16 id; UINT16 frag_off; UINT8 ttl; UINT8 protocol; UINT16 check; UINT32 saddr; UINT32 daddr; } IPHdr; /* * IPv6 fixed header * * BEWARE, it is incorrect. The first 4 bits of flow_lbl * are glued to priority now, forming "class". */ typedef struct IPv6Hdr { UINT8 priority:4, version:4; UINT8 flow_lbl[3]; UINT16 payload_len; UINT8 nexthdr; UINT8 hop_limit; struct in6_addr saddr; struct in6_addr daddr; } IPv6Hdr; // Generic IPv6 extension header typedef struct IPv6ExtHdr { UINT8 nextHeader; // type of the next header UINT8 hdrExtLen; // length of header extensions (beyond 8 bytes) UINT16 optPad1; UINT32 optPad2; } IPv6ExtHdr; typedef struct IPv6FragHdr { UINT8 nextHeader; UINT8 reserved; UINT16 offlg; UINT32 ident; } IPv6FragHdr; typedef struct IPv6NdOptHdr { UINT8 type; UINT8 len; } IPv6NdOptHdr; typedef struct ICMPHdr { UINT8 type; UINT8 code; UINT16 checksum; } ICMPHdr; typedef struct ICMPEcho { UINT16 id; UINT16 seq; } ICMPEcho; typedef struct UDPHdr { UINT16 source; UINT16 dest; UINT16 len; UINT16 check; } UDPHdr; typedef struct TCPHdr { UINT16 source; UINT16 dest; UINT32 seq; UINT32 ack_seq; UINT16 res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; UINT16 window; UINT16 check; UINT16 urg_ptr; } TCPHdr; typedef struct SCTPHdr { UINT16 source; UINT16 dest; UINT32 vtag; UINT32 check; } SCTPHdr; typedef struct PseudoHdr { UINT32 sourceIPAddr; UINT32 destIPAddr; UINT8 zero; UINT8 protocol; UINT16 length; } PseudoHdr; typedef struct PseudoHdrIPv6 { UINT8 sourceIPAddr[16]; UINT8 destIPAddr[16]; UINT8 zero; UINT8 protocol; UINT16 length; } PseudoHdrIPv6; struct ArpHdr { UINT16 ar_hrd; /* Format of hardware address. */ UINT16 ar_pro; /* Format of protocol address. */ UINT8 ar_hln; /* Length of hardware address. */ UINT8 ar_pln; /* Length of protocol address. */ UINT16 ar_op; /* ARP opcode (command). */ }; typedef struct EtherArp { struct ArpHdr ea_hdr; /* fixed-size header */ Eth_Address arp_sha; /* sender hardware address */ UINT8 arp_spa[4]; /* sender protocol address */ Eth_Address arp_tha; /* target hardware address */ UINT8 arp_tpa[4]; /* target protocol address */ } EtherArp; typedef struct IGMPHdr { UINT8 type; UINT8 maxResponseTime; UINT16 csum; UINT8 groupAddr[4]; } IGMPHdr; typedef struct IGMPV3Trailer { UINT8 qrv:3, s:1, resv:4; UINT8 qqic; UINT16 numSources; } IGMPV3Trailer; typedef struct IPOpt { UINT8 type; UINT8 length; UINT16 value; } IPOpt; /* * IP protocol types */ #define SOCKET_IPPROTO_IP 0 #define SOCKET_IPPROTO_ICMP 1 #define SOCKET_IPPROTO_TCP 6 #define SOCKET_IPPROTO_UDP 17 #define SOCKET_IPPROTO_GRE 47 #define SOCKET_IPPROTO_SCTP 132 #endif /* __NET_PROTO_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/TunnelIntf.h0000644000000000000000000000013213534540071022711 xustar0030 mtime=1567801401.225679877 30 atime=1567801402.049685929 30 ctime=1567801424.457851038 openvswitch-2.5.9/datapath-windows/ovsext/TunnelIntf.h0000644000175000017500000000306313534540071024401 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __TUNNEL_INTF_H_ #define __TUNNEL_INTF_H_ 1 typedef VOID(*PFNTunnelVportPendingOp)(PVOID context, NTSTATUS status, UINT32 *replyLen); /* Tunnel callout driver load/unload functions */ NTSTATUS OvsInitTunnelFilter(PDRIVER_OBJECT driverObject, PVOID deviceObject); VOID OvsUninitTunnelFilter(PDRIVER_OBJECT driverObject); VOID OvsRegisterSystemProvider(PVOID deviceObject); VOID OvsUnregisterSystemProvider(); NTSTATUS OvsTunnelFilterCreate(PIRP irp, UINT16 filterPort, UINT64 *filterID, PFNTunnelVportPendingOp callback, PVOID context); NTSTATUS OvsTunnelFilterDelete(PIRP irp, UINT64 filterID, PFNTunnelVportPendingOp callback, PVOID context); #endif /* __TUNNEL_INTF_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Oid.h0000644000000000000000000000013213534540071021336 xustar0030 mtime=1567801401.221679848 30 atime=1567801402.049685929 30 ctime=1567801424.441850919 openvswitch-2.5.9/datapath-windows/ovsext/Oid.h0000644000175000017500000000227613534540071023033 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __OID_H_ #define __OID_H_ 1 NDIS_STATUS OvsQuerySwitchActivationComplete(POVS_SWITCH_CONTEXT switchContext, BOOLEAN *switchActive); NDIS_STATUS OvsGetPortsOnSwitch(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_PORT_ARRAY *portArrayOut); NDIS_STATUS OvsGetNicsOnSwitch(POVS_SWITCH_CONTEXT switchContext, PNDIS_SWITCH_NIC_ARRAY *nicArrayOut); VOID OvsFreeSwitchPortsArray(PNDIS_SWITCH_PORT_ARRAY portsArray); VOID OvsFreeSwitchNicsArray(PNDIS_SWITCH_NIC_ARRAY nicsArray); #endif /* __OID_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Jhash.c0000644000000000000000000000013213534540071021653 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.425850801 openvswitch-2.5.9/datapath-windows/ovsext/Jhash.c0000644000175000017500000000647113534540071023351 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" static __inline UINT32 GetUnalignedU32(const UINT32 *p_) { const UINT8 *p = (const UINT8 *)p_; return ntohl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } /* This is the public domain lookup3 hash by Bob Jenkins from * http://burtleburtle.net/bob/c/lookup3.c, modified for style. */ static __inline UINT32 JhashRot(UINT32 x, INT k) { return (x << k) | (x >> (32 - k)); } static __inline VOID JhashMix(UINT32 *a, UINT32 *b, UINT32 *c) { *a -= *c; *a ^= JhashRot(*c, 4); *c += *b; *b -= *a; *b ^= JhashRot(*a, 6); *a += *c; *c -= *b; *c ^= JhashRot(*b, 8); *b += *a; *a -= *c; *a ^= JhashRot(*c, 16); *c += *b; *b -= *a; *b ^= JhashRot(*a, 19); *a += *c; *c -= *b; *c ^= JhashRot(*b, 4); *b += *a; } static __inline VOID JhashFinal(UINT32 *a, UINT32 *b, UINT32 *c) { *c ^= *b; *c -= JhashRot(*b, 14); *a ^= *c; *a -= JhashRot(*c, 11); *b ^= *a; *b -= JhashRot(*a, 25); *c ^= *b; *c -= JhashRot(*b, 16); *a ^= *c; *a -= JhashRot(*c, 4); *b ^= *a; *b -= JhashRot(*a, 14); *c ^= *b; *c -= JhashRot(*b, 24); } /* Returns the Jenkins hash of the 'n' 32-bit words at 'p', starting from * 'basis'. 'p' must be properly aligned. * * Use hash_words() instead, unless you're computing a hash function whose * value is exposed "on the wire" so we don't want to change it. */ UINT32 OvsJhashWords(const UINT32 *p, SIZE_T n, UINT32 basis) { UINT32 a, b, c; a = b = c = 0xdeadbeef + (((UINT32) n) << 2) + basis; while (n > 3) { a += p[0]; b += p[1]; c += p[2]; JhashMix(&a, &b, &c); n -= 3; p += 3; } switch (n) { case 3: c += p[2]; /* fall through */ case 2: b += p[1]; /* fall through */ case 1: a += p[0]; JhashFinal(&a, &b, &c); /* fall through */ case 0: break; } return c; } /* Returns the Jenkins hash of the 'n' bytes at 'p', starting from 'basis'. * * Use hash_bytes() instead, unless you're computing a hash function whose * value is exposed "on the wire" so we don't want to change it. */ UINT32 OvsJhashBytes(const VOID *p_, SIZE_T n, UINT32 basis) { const UINT32 *p = p_; UINT32 a, b, c; a = b = c = 0xdeadbeef + (UINT32)n + basis; while (n >= 12) { a += GetUnalignedU32(p); b += GetUnalignedU32(p + 1); c += GetUnalignedU32(p + 2); JhashMix(&a, &b, &c); n -= 12; p += 3; } if (n) { UINT32 tmp[3]; tmp[0] = tmp[1] = tmp[2] = 0; memcpy(tmp, p, n); a += tmp[0]; b += tmp[1]; c += tmp[2]; JhashFinal(&a, &b, &c); } return c; } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/BufferMgmt.h0000644000000000000000000000012713534540071022665 xustar0029 mtime=1567801401.20567973 30 atime=1567801402.045685899 28 ctime=1567801424.3578503 openvswitch-2.5.9/datapath-windows/ovsext/BufferMgmt.h0000644000175000017500000001172713534540071024357 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __BUFFER_MGMT_H_ #define __BUFFER_MGMT_H_ 1 #define MEM_ALIGN MEMORY_ALLOCATION_ALIGNMENT #define MEM_ALIGN_SIZE(_x) ((MEM_ALIGN - 1 + (_x))/MEM_ALIGN * MEM_ALIGN) #define OVS_CTX_MAGIC 0xabcd #define OVS_DEFAULT_NBL_CONTEXT_SIZE MEM_ALIGN_SIZE(64) #define OVS_DEFAULT_NBL_CONTEXT_FILL \ (OVS_DEFAULT_NBL_CONTEXT_SIZE - sizeof (OVS_BUFFER_CONTEXT)) #define OVS_DEFAULT_DATA_SIZE 256 #define OVS_DEFAULT_HEADROOM_SIZE 128 #define OVS_FIX_NBL_DATA_SIZE (OVS_DEFAULT_DATA_SIZE + OVS_DEFAULT_HEADROOM_SIZE) /* Default we copy 18 bytes, to make sure ethernet header and vlan is in * continuous buffer */ #define OVS_DEFAULT_COPY_SIZE 18 enum { OVS_BUFFER_NEED_COMPLETE = BIT16(0), OVS_BUFFER_PRIVATE_MDL = BIT16(1), OVS_BUFFER_PRIVATE_DATA = BIT16(2), OVS_BUFFER_PRIVATE_NET_BUFFER = BIT16(3), OVS_BUFFER_PRIVATE_FORWARD_CONTEXT = BIT16(4), OVS_BUFFER_PRIVATE_CONTEXT = BIT16(5), OVS_BUFFER_FROM_FIX_SIZE_POOL = BIT16(6), OVS_BUFFER_FROM_ZERO_SIZE_POOL = BIT16(7), OVS_BUFFER_FROM_NBL_ONLY_POOL = BIT16(8), OVS_BUFFER_RECV_BUFFER = BIT16(9), OVS_BUFFER_SEND_BUFFER = BIT16(10), OVS_BUFFER_FRAGMENT = BIT16(11), }; typedef union _OVS_BUFFER_CONTEXT { struct { UINT16 magic; UINT16 flags; UINT32 srcPortNo; UINT32 refCount; union { UINT32 origDataLength; UINT32 dataOffsetDelta; }; }; UINT64 value[MEM_ALIGN_SIZE(16) >> 3]; } OVS_BUFFER_CONTEXT, *POVS_BUFFER_CONTEXT; typedef struct _OVS_NBL_POOL { NDIS_SWITCH_CONTEXT ndisContext; NDIS_HANDLE ndisHandle; NDIS_HANDLE fixSizePool; // data size of 256 NDIS_HANDLE zeroSizePool; // no data, NBL + NB + Context NDIS_HANDLE nblOnlyPool; // NBL + context for clone NDIS_HANDLE nbPool; // NB for clone #ifdef DBG LONG fixNBLCount; LONG zeroNBLCount; LONG nblOnlyCount; LONG nbCount; LONG sysNBLCount; LONG fragNBLCount; #endif } OVS_NBL_POOL, *POVS_NBL_POOL; NDIS_STATUS OvsInitBufferPool(PVOID context); VOID OvsCleanupBufferPool(PVOID context); PNET_BUFFER_LIST OvsAllocateFixSizeNBL(PVOID context, UINT32 size, UINT32 headRoom); PNET_BUFFER_LIST OvsAllocateVariableSizeNBL(PVOID context, UINT32 size, UINT32 headRoom); POVS_BUFFER_CONTEXT OvsInitExternalNBLContext(PVOID context, PNET_BUFFER_LIST nbl, BOOLEAN isRecv); PNET_BUFFER_LIST OvsPartialCopyNBL(PVOID context, PNET_BUFFER_LIST nbl, UINT32 copySize, UINT32 headRoom, BOOLEAN copyNblInfo); PNET_BUFFER_LIST OvsPartialCopyToMultipleNBLs(PVOID context, PNET_BUFFER_LIST nbl, UINT32 copySize, UINT32 headRoom, BOOLEAN copyNblInfo); PNET_BUFFER_LIST OvsFullCopyNBL(PVOID context, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo); PNET_BUFFER_LIST OvsTcpSegmentNBL(PVOID context, PNET_BUFFER_LIST nbl, POVS_PACKET_HDR_INFO hdrInfo, UINT32 MSS, UINT32 headRoom); PNET_BUFFER_LIST OvsAllocateNBLFromBuffer(PVOID context, PVOID buffer, ULONG length); PNET_BUFFER_LIST OvsFullCopyToMultipleNBLs(PVOID context, PNET_BUFFER_LIST nbl, UINT32 headRoom, BOOLEAN copyNblInfo); PNET_BUFFER_LIST OvsCompleteNBL(PVOID context, PNET_BUFFER_LIST nbl, BOOLEAN updateRef); NDIS_STATUS OvsSetCtxSourcePortNo(PNET_BUFFER_LIST nbl, UINT32 portNo); NDIS_STATUS OvsGetCtxSourcePortNo(PNET_BUFFER_LIST nbl, UINT32 *portNo); #endif /* __BUFFER_MGMT_H_ */ openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/Util.c0000644000000000000000000000013213534540071021533 xustar0030 mtime=1567801401.229679907 30 atime=1567801402.049685929 30 ctime=1567801424.461851067 openvswitch-2.5.9/datapath-windows/ovsext/Util.c0000644000175000017500000000553413534540071023230 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "precomp.h" #ifdef OVS_DBG_MOD #undef OVS_DBG_MOD #endif #define OVS_DBG_MOD OVS_DBG_OTHERS #include "Debug.h" extern NDIS_HANDLE gOvsExtDriverHandle; VOID* OvsAllocateMemoryWithTag(size_t size, ULONG tag) { OVS_VERIFY_IRQL_LE(DISPATCH_LEVEL); return NdisAllocateMemoryWithTagPriority(gOvsExtDriverHandle, (UINT32)size, tag, NormalPoolPriority); } VOID OvsFreeMemoryWithTag(VOID *ptr, ULONG tag) { ASSERT(ptr); NdisFreeMemoryWithTagPriority(gOvsExtDriverHandle, ptr, tag); } VOID * OvsAllocateMemory(size_t size) { OVS_VERIFY_IRQL_LE(DISPATCH_LEVEL); return NdisAllocateMemoryWithTagPriority(gOvsExtDriverHandle, (UINT32)size, OVS_MEMORY_TAG, NormalPoolPriority); } VOID * OvsAllocateAlignedMemory(size_t size, UINT16 align) { OVS_VERIFY_IRQL_LE(DISPATCH_LEVEL); ASSERT((align == 8) || (align == 16)); if ((align == 8) || (align == 16)) { /* * XXX: NdisAllocateMemory*() functions don't talk anything about * alignment. Hence using ExAllocatePool*(); */ return (VOID *)ExAllocatePoolWithTagPriority(NonPagedPool, size, OVS_MEMORY_TAG, NormalPoolPriority); } /* Invalid user input. */ return NULL; } VOID OvsFreeMemory(VOID *ptr) { ASSERT(ptr); NdisFreeMemoryWithTagPriority(gOvsExtDriverHandle, ptr, OVS_MEMORY_TAG); } VOID OvsFreeAlignedMemory(VOID *ptr) { ASSERT(ptr); ExFreePoolWithTag(ptr, OVS_MEMORY_TAG); } VOID OvsAppendList(PLIST_ENTRY dst, PLIST_ENTRY src) { PLIST_ENTRY srcFirst, srcLast, dstLast; if (IsListEmpty(src)) { return; } srcFirst = src->Flink; srcLast = src->Blink; dstLast = dst->Blink; dstLast->Flink = srcFirst; srcFirst->Blink = dstLast; srcLast->Flink = dst; dst->Blink = srcLast; src->Flink = src; src->Blink = src; } BOOLEAN OvsCompareString(PVOID string1, PVOID string2) { /* * Not a super-efficient string compare since we walk over the strings * twice: to initialize, and then to do the comparison. */ STRING str1, str2; RtlInitString(&str1, string1); RtlInitString(&str2, string2); return RtlEqualString(&str1, &str2, FALSE); } openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/ovsext.rc0000644000000000000000000000013213534540071022330 xustar0030 mtime=1567801401.233679937 30 atime=1567801402.053685959 30 ctime=1567801424.469851126 openvswitch-2.5.9/datapath-windows/ovsext/ovsext.rc0000644000175000017500000000366713534540071024032 0ustar00jpettitjpettit00000000000000// Microsoft Visual C++ generated resource script. // #include "resource.h" ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 6,3,9600,17298 PRODUCTVERSION 6,3,9600,17298 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x9L #else FILEFLAGS 0x8L #endif FILEOS 0x40004L FILETYPE 0x3L FILESUBTYPE 0x6L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000004b0" BEGIN VALUE "CompanyName", "Open vSwitch" VALUE "FileDescription", "Open vSwitch Extension" VALUE "FileVersion", "6.3.9600.17298" VALUE "InternalName", "OVSExt.SYS" VALUE "LegalCopyright", "� Licensed under the Apache License, Version 2.0 (the ""License"")" VALUE "OriginalFilename", "OVSExt.SYS" VALUE "ProductName", "Open vSwitch 8/8.1 DDK driver" VALUE "ProductVersion", "6.3.9600.17298" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0, 1200 END END #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED openvswitch-2.5.9/datapath-windows/ovsext/PaxHeaders.82075/IpHelper.h0000644000000000000000000000013213534540071022333 xustar0030 mtime=1567801401.217679818 30 atime=1567801402.049685929 30 ctime=1567801424.421850772 openvswitch-2.5.9/datapath-windows/ovsext/IpHelper.h0000644000175000017500000000763113534540071024030 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014 VMware, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __IP_HELPER_H_ #define __IP_HELPER_H_ 1 #include #include #define OVS_FWD_HASH_TABLE_SIZE ((UINT32)1 << 10) #define OVS_FWD_HASH_TABLE_MASK (OVS_FWD_HASH_TABLE_SIZE - 1) #define OVS_ROUTE_HASH_TABLE_SIZE ((UINT32)1 << 8) #define OVS_ROUTE_HASH_TABLE_MASK (OVS_ROUTE_HASH_TABLE_SIZE - 1) #define OVS_NEIGH_HASH_TABLE_SIZE ((UINT32)1 << 8) #define OVS_NEIGH_HASH_TABLE_MASK (OVS_NEIGH_HASH_TABLE_SIZE - 1) #define OVS_IPNEIGH_TIMEOUT 100000000 // 10 s typedef struct _OVS_IPNEIGH_ENTRY { UINT8 macAddr[ETH_ADDR_LEN]; UINT16 refCount; UINT32 ipAddr; UINT32 pad; UINT64 timeout; LIST_ENTRY link; LIST_ENTRY slink; LIST_ENTRY fwdList; } OVS_IPNEIGH_ENTRY, *POVS_IPNEIGH_ENTRY; typedef struct _OVS_IPFORWARD_ENTRY { IP_ADDRESS_PREFIX prefix; UINT32 nextHop; UINT16 refCount; LIST_ENTRY link; LIST_ENTRY fwdList; } OVS_IPFORWARD_ENTRY, *POVS_IPFORWARD_ENTRY; typedef union _OVS_FWD_INFO { struct { UINT32 dstIpAddr; UINT32 srcIpAddr; UINT8 dstMacAddr[ETH_ADDR_LEN]; UINT8 srcMacAddr[ETH_ADDR_LEN]; UINT32 srcPortNo; }; UINT64 value[3]; } OVS_FWD_INFO, *POVS_FWD_INFO; typedef struct _OVS_FWD_ENTRY { OVS_FWD_INFO info; POVS_IPFORWARD_ENTRY ipf; POVS_IPNEIGH_ENTRY ipn; LIST_ENTRY link; LIST_ENTRY ipfLink; LIST_ENTRY ipnLink; } OVS_FWD_ENTRY, *POVS_FWD_ENTRY; enum { OVS_IP_HELPER_INTERNAL_ADAPTER_UP, OVS_IP_HELPER_FWD_REQUEST, }; typedef VOID (*OvsIPHelperCallback)(PNET_BUFFER_LIST nbl, UINT32 inPort, PVOID tunnelKey, PVOID cbData1, PVOID cbData2, NTSTATUS status, POVS_FWD_INFO fwdInfo); typedef struct _OVS_FWD_REQUEST_INFO { PNET_BUFFER_LIST nbl; UINT32 inPort; OvsIPv4TunnelKey tunnelKey; OvsIPHelperCallback cb; PVOID cbData1; PVOID cbData2; } OVS_FWD_REQUEST_INFO, *POVS_FWD_REQUEST_INFO; typedef struct _OVS_IP_HELPER_REQUEST { LIST_ENTRY link; UINT32 command; union { OVS_FWD_REQUEST_INFO fwdReq; UINT32 dummy; }; } OVS_IP_HELPER_REQUEST, *POVS_IP_HELPER_REQUEST; typedef struct _OVS_IP_HELPER_THREAD_CONTEXT { KEVENT event; PVOID threadObject; UINT32 exit; } OVS_IP_HELPER_THREAD_CONTEXT, *POVS_IP_HELPER_THREAD_CONTEXT; NTSTATUS OvsInitIpHelper(NDIS_HANDLE ndisFilterHandle); VOID OvsCleanupIpHelper(VOID); VOID OvsInternalAdapterUp(GUID *netCfgInstanceId); VOID OvsInternalAdapterDown(VOID); NTSTATUS OvsFwdIPHelperRequest(PNET_BUFFER_LIST nbl, UINT32 inPort, const PVOID tunnelKey, OvsIPHelperCallback cb, PVOID cbData1, PVOID cbData2); NTSTATUS OvsLookupIPFwdInfo(UINT32 dstIp, POVS_FWD_INFO info); VOID OvsCancelFwdIpHelperRequest(PNET_BUFFER_LIST nbl); #endif /* __IP_HELPER_H_ */ openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/misc0000644000000000000000000000013213534540120017773 xustar0030 mtime=1567801424.349850241 30 atime=1567801425.625859648 30 ctime=1567801424.349850241 openvswitch-2.5.9/datapath-windows/misc/0000755000175000017500000000000013534540120021536 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/datapath-windows/misc/PaxHeaders.82075/uninstall.cmd0000644000000000000000000000013213534540071022553 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.349850241 openvswitch-2.5.9/datapath-windows/misc/uninstall.cmd0000644000175000017500000000002113534540071024232 0ustar00jpettitjpettit00000000000000netcfg -u OVSExt openvswitch-2.5.9/datapath-windows/misc/PaxHeaders.82075/OVS.psm10000644000000000000000000000013213534540071021326 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.345850211 openvswitch-2.5.9/datapath-windows/misc/OVS.psm10000644000175000017500000001306313534540071023017 0ustar00jpettitjpettit00000000000000<# Copyright 2014, 2015 Cloudbase Solutions Srl Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. #> $WMI_JOB_STATUS_STARTED = 4096 $WMI_JOB_STATE_RUNNING = 4 $WMI_JOB_STATE_COMPLETED = 7 $hvassembly = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.HyperV.PowerShell") function Set-VMNetworkAdapterOVSPort { [CmdletBinding()] param ( [parameter(Mandatory=$true, ValueFromPipeline=$true)] [Microsoft.HyperV.PowerShell.VMNetworkAdapter]$VMNetworkAdapter, [parameter(Mandatory=$true)] [ValidateLength(1, 48)] [string]$OVSPortName ) process { $ns = "root\virtualization\v2" $EscapedId = $VMNetworkAdapter.Id.Replace('\', '\\') $sd = gwmi -namespace $ns -class Msvm_EthernetPortAllocationSettingData -Filter "ElementName = '$OVSPortName'" if($sd) { if($sd.InstanceId.Contains($VMNetworkAdapter.Id)) { throw "The OVS port name '$OVSPortName' is already assigned to this port." } throw "Cannot assign the OVS port name '$OVSPortName' as it is already assigned to an other port." } $sd = gwmi -namespace $ns -class Msvm_EthernetPortAllocationSettingData -Filter "InstanceId like '$EscapedId%'" if($sd) { $sd.ElementName = $OVSPortName $vsms = gwmi -namespace $ns -class Msvm_VirtualSystemManagementService $retVal = $vsms.ModifyResourceSettings(@($sd.GetText(1))) try { CheckWMIReturnValue $retVal } catch { throw "Assigning OVS port '$OVSPortName' failed" } } } } function Get-VMNetworkAdapterByOVSPort { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [ValidateLength(1, 48)] [string]$OVSPortName ) process { $ns = "root\virtualization\v2" $sd = gwmi -namespace $ns -class Msvm_EthernetPortAllocationSettingData -Filter "ElementName = '$OVSPortName'" if($sd) { return $sd } } } function Get-VMByOVSPort { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [ValidateLength(1, 48)] [string]$OVSPortName ) process { $ns = "root\virtualization\v2" $vms = gwmi -namespace $ns -class Msvm_VirtualSystemSettingData ForEach($vm in $vms) { $ports = gwmi -Namespace $ns -Query " Associators of {$vm} Where ResultClass = Msvm_EthernetPortAllocationSettingData" if ($ports.ElementName -eq $OVSPortName) { return $vm } } } } #This function returns the Msvm_VirtualSystemSettingData given a VMName function Get-VMNetworkAdapterWithOVSPort { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [ValidateLength(1, 1024)] [string]$vmName ) process { $ns = "root\virtualization\v2" $vm = {} $ports = {} $vm = gwmi -namespace $ns -class Msvm_VirtualSystemSettingData -Filter "ElementName = '$VMName'" $ports = gwmi -Namespace $ns -Query " Associators of {$vm} Where ResultClass = Msvm_EthernetPortAllocationSettingData" return $ports } } function CheckWMIReturnValue($retVal) { if ($retVal.ReturnValue -ne 0) { if ($retVal.ReturnValue -eq $WMI_JOB_STATUS_STARTED) { do { $job = [wmi]$retVal.Job } while ($job.JobState -eq $WMI_JOB_STATE_RUNNING) if ($job.JobState -ne $WMI_JOB_STATE_COMPLETED) { echo $job.ReturnValue $errorString = "Job Failed. Job State: " + $job.JobState.ToString() if ($job.__CLASS -eq "Msvm_ConcreteJob") { $errorString += " Error Code: " + $job.ErrorCode.ToString() $errorString += " Error Details: " + $job.ErrorDescription } else { $error = $job.GetError() if ($error.Error) { $errorString += " Error:" + $error.Error } } throw $errorString } } else { throw "Job Failed. Return Value: {0}" -f $job.ReturnValue } } } function Set-VMNetworkAdapterOVSPortDirect { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [ValidateLength(1, 1024)] [string]$vmName, [parameter(Mandatory=$true)] [ValidateLength(1, 48)] [string]$OVSPortName ) process { $vnic = 0 if ($vmName) { $vnic = Get-VMNetworkAdapter -VMName $vmName } # XXX the vnic index should be provided by the caller $vnic[0] | Set-VMNetworkAdapterOVSPort -OVSPortName $OVSPortName } } Export-ModuleMember -function Set-*, Get-* openvswitch-2.5.9/datapath-windows/misc/PaxHeaders.82075/install.cmd0000644000000000000000000000013213534540071022210 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.349850241 openvswitch-2.5.9/datapath-windows/misc/install.cmd0000644000175000017500000000010413534540071023671 0ustar00jpettitjpettit00000000000000netcfg -l .\ovsext.inf -c s -i OVSExt net stop vmms net start vmms openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071021261 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.597852069 openvswitch-2.5.9/datapath-windows/automake.mk0000644000175000017500000000510213534540071022745 0ustar00jpettitjpettit00000000000000EXTRA_DIST += \ datapath-windows/CodingStyle \ datapath-windows/DESIGN \ datapath-windows/Package/package.VcxProj \ datapath-windows/Package/package.VcxProj.user \ datapath-windows/include/OvsDpInterfaceExt.h \ datapath-windows/misc/OVS.psm1 \ datapath-windows/misc/install.cmd \ datapath-windows/misc/uninstall.cmd \ datapath-windows/ovsext.sln \ datapath-windows/ovsext/Actions.c \ datapath-windows/ovsext/Atomic.h \ datapath-windows/ovsext/BufferMgmt.c \ datapath-windows/ovsext/BufferMgmt.h \ datapath-windows/ovsext/Checksum.c \ datapath-windows/ovsext/Checksum.h \ datapath-windows/ovsext/Datapath.c \ datapath-windows/ovsext/Datapath.h \ datapath-windows/ovsext/Debug.c \ datapath-windows/ovsext/Debug.h \ datapath-windows/ovsext/DpInternal.h\ datapath-windows/ovsext/Driver.c \ datapath-windows/ovsext/Ethernet.h \ datapath-windows/ovsext/Event.c \ datapath-windows/ovsext/Event.h \ datapath-windows/ovsext/Flow.c \ datapath-windows/ovsext/Flow.h \ datapath-windows/ovsext/Gre.h \ datapath-windows/ovsext/Gre.c \ datapath-windows/ovsext/IpHelper.c \ datapath-windows/ovsext/IpHelper.h \ datapath-windows/ovsext/Jhash.c \ datapath-windows/ovsext/Jhash.h \ datapath-windows/ovsext/NetProto.h \ datapath-windows/ovsext/Netlink/Netlink.c \ datapath-windows/ovsext/Netlink/Netlink.h \ datapath-windows/ovsext/Netlink/NetlinkBuf.c \ datapath-windows/ovsext/Netlink/NetlinkBuf.h \ datapath-windows/ovsext/Netlink/NetlinkError.h \ datapath-windows/ovsext/Netlink/NetlinkProto.h \ datapath-windows/ovsext/Oid.c \ datapath-windows/ovsext/Oid.h \ datapath-windows/ovsext/PacketIO.c \ datapath-windows/ovsext/PacketIO.h \ datapath-windows/ovsext/PacketParser.c \ datapath-windows/ovsext/PacketParser.h \ datapath-windows/ovsext/Stt.c \ datapath-windows/ovsext/Stt.h \ datapath-windows/ovsext/Switch.c \ datapath-windows/ovsext/Switch.h \ datapath-windows/ovsext/Tunnel.c \ datapath-windows/ovsext/Tunnel.h \ datapath-windows/ovsext/TunnelFilter.c \ datapath-windows/ovsext/TunnelIntf.h \ datapath-windows/ovsext/Types.h \ datapath-windows/ovsext/User.c \ datapath-windows/ovsext/User.h \ datapath-windows/ovsext/Util.c \ datapath-windows/ovsext/Util.h \ datapath-windows/ovsext/Vport.c \ datapath-windows/ovsext/Vport.h \ datapath-windows/ovsext/Vxlan.c \ datapath-windows/ovsext/Vxlan.h \ datapath-windows/ovsext/ovsext.inf \ datapath-windows/ovsext/ovsext.rc \ datapath-windows/ovsext/ovsext.vcxproj \ datapath-windows/ovsext/ovsext.vcxproj.user \ datapath-windows/ovsext/precomp.h \ datapath-windows/ovsext/precompsrc.c \ datapath-windows/ovsext/resource.h openvswitch-2.5.9/datapath-windows/PaxHeaders.82075/ovsext.sln0000644000000000000000000000013213534540071021170 xustar0030 mtime=1567801401.201679702 30 atime=1567801402.045685899 30 ctime=1567801424.349850241 openvswitch-2.5.9/datapath-windows/ovsext.sln0000644000175000017500000000572513534540071022667 0ustar00jpettitjpettit00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{6BA8554E-AE50-49B0-9C98-4592447FEF8D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OVSExt", "OVSExt", "{4A841675-477D-40A2-9CC6-128C3B8714F8}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{911D7389-3E61-449F-B8F3-14AD7EE9A0F2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ovsext", "ovsext\ovsext.vcxproj", "{63FE215D-98BE-4440-8081-C6160EFB80FA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Win8.1Debug|x64 = Win8.1Debug|x64 Win8.1Release|x64 = Win8.1Release|x64 Win8Debug|x64 = Win8Debug|x64 Win8Release|x64 = Win8Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8.1Debug|x64.ActiveCfg = Win8.1 Debug|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8.1Debug|x64.Build.0 = Win8.1 Debug|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8.1Release|x64.ActiveCfg = Win8.1 Release|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8.1Release|x64.Build.0 = Win8.1 Release|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8Debug|x64.ActiveCfg = Win8 Debug|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8Debug|x64.Build.0 = Win8 Debug|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8Release|x64.ActiveCfg = Win8 Release|x64 {911D7389-3E61-449F-B8F3-14AD7EE9A0F2}.Win8Release|x64.Build.0 = Win8 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Debug|x64.ActiveCfg = Win8.1 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Debug|x64.Build.0 = Win8.1 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Debug|x64.Deploy.0 = Win8.1 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Release|x64.ActiveCfg = Win8.1 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Release|x64.Build.0 = Win8.1 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8.1Release|x64.Deploy.0 = Win8.1 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Debug|x64.ActiveCfg = Win8 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Debug|x64.Build.0 = Win8 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Debug|x64.Deploy.0 = Win8 Debug|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Release|x64.ActiveCfg = Win8 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Release|x64.Build.0 = Win8 Release|x64 {63FE215D-98BE-4440-8081-C6160EFB80FA}.Win8Release|x64.Deploy.0 = Win8 Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {911D7389-3E61-449F-B8F3-14AD7EE9A0F2} = {6BA8554E-AE50-49B0-9C98-4592447FEF8D} {63FE215D-98BE-4440-8081-C6160EFB80FA} = {4A841675-477D-40A2-9CC6-128C3B8714F8} EndGlobalSection EndGlobal openvswitch-2.5.9/PaxHeaders.82075/ovn0000644000000000000000000000013213534540120014364 xustar0030 mtime=1567801424.609852158 30 atime=1567801425.625859648 30 ctime=1567801424.609852158 openvswitch-2.5.9/ovn/0000755000175000017500000000000013534540120016127 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/PaxHeaders.82075/TODO0000644000000000000000000000013013534540071015134 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.501851361 openvswitch-2.5.9/ovn/TODO0000644000175000017500000003246513534540071016636 0ustar00jpettitjpettit00000000000000-*- outline -*- * L3 support ** New OVN logical actions *** arp Generates an ARP packet based on the current IPv4 packet and allows it to be processed as part of the current pipeline (and then pop back to processing the original IPv4 packet). TCP/IP stacks typically limit the rate at which ARPs are sent, e.g. to one per second for a given target. We might need to do this too. We probably need to buffer the packet that generated the ARP. I don't know where to do that. *** icmp4 { action... } Generates an ICMPv4 packet based on the current IPv4 packet and processes it according to each nested action (and then pops back to processing the original IPv4 packet). The intended use case is for generating "time exceeded" and "destination unreachable" errors. ovn-sb.xml includes a tentative specification for this action. Tentatively, the icmp4 action sets a default icmp_type and icmp_code and lets the nested actions override it. This means that we'd have to make icmp_type and icmp_code writable. Because changing icmp_type and icmp_code can change the interpretation of the rest of the data in the ICMP packet, we would want to think this through carefully. If it seems like a bad idea then we could instead make the type and code a parameter to the action: icmp4(type, code) { action... } It is worth considering what should be considered the ingress port for the ICMPv4 packet. It's quite likely that the ICMPv4 packet is going to go back out the ingress port. Maybe the icmp4 action, therefore, should clear the inport, so that output to the original inport won't be discarded. *** tcp_reset Transforms the current TCP packet into a RST reply. ovn-sb.xml includes a tentative specification for this action. *** Other actions for IPv6. IPv6 will probably need an action or actions for ND that is similar to the "arp" action, and an action for generating *** ovn-controller translation to OpenFlow The following two translation strategies come to mind. Some of the new actions we might want to implement one way, some of them the other, depending on the details. *** Implementation strategies One way to do this is to define new actions as Open vSwitch extensions to OpenFlow, emit those actions in ovn-controller, and implement them in ovs-vswitchd (possibly pushing the implementations into the Linux and DPDK datapaths as well). This is the only acceptable way for actions that need high performance. None of these actions obviously need high performance, but it might be necessary to have fairness in handling e.g. a flood of incoming packets that require these actions. The main disadvantage of this approach is that it ties ovs-vswitchd (and the Linux kernel module) to supporting these actions essentially forever, which means that we'd want to make sure that they are general-purpose, well designed, maintainable, and supportable. The other way to do this is to send the packets across an OpenFlow channel to ovn-controller and have ovn-controller process them. This is acceptable for actions that don't need high performance, and it means that we don't add anything permanently to ovs-vswitchd or the kernel (so we can be more casual about the design). The big disadvantage is that it becomes necessary to add a way to resume the OpenFlow pipeline when it is interrupted in the middle by sending a packet to the controller. This is not as simple as doing a new flow table lookup and resuming from that point. Instead, it is equivalent to the (very complicated) recirculation logic in ofproto-dpif-xlate.c. Much of this logic can be translated into OpenFlow actions (e.g. the call stack and data stack), but some of it is entirely outside OpenFlow (e.g. the state of mirrors). To implement it properly, it seems that we'll have to introduce a new Open vSwitch extension to OpenFlow, a "send-to-controller" action that causes extra data to be sent to the controller, where the extra data packages up the state necessary to resume the pipeline. Maybe the bits of the state that can be represented in OpenFlow can be embedded in this extra data in a controller-readable form, but other bits we might want to be opaque. It's also likely that we'll want to change and extend the form of this opaque data over time, so this should be allowed for, e.g. by including a nonce in the extra data that is newly generated every time ovs-vswitchd starts. *** OpenFlow action definitions Define OpenFlow wire structures for each new OpenFlow action and implement them in lib/ofp-actions.[ch]. *** OVS implementation Add code for action translation. Possibly add datapath code for action implementation. However, none of these new actions should require high-bandwidth processing so we could at least start with them implemented in userspace only. (ARP field modification is already userspace-only and no one has complained yet.) ** IPv6 *** ND versus ARP *** IPv6 routing *** ICMPv6 ** Dynamic IP to MAC bindings Some bindings from IP address to MAC will undoubtedly need to be discovered dynamically through ARP requests. It's straightforward enough for a logical L3 router to generate ARP requests and forward them to the appropriate switch. It's more difficult to figure out where the reply should be processed and stored. It might seem at first that a first-cut implementation could just keep track of the binding on the hypervisor that needs to know, but that can't happen easily because the VM that sends the reply might not be on the same HV as the VM that needs the answer (that is, the VM that sent the packet that needs the binding to be resolved) and there isn't an easy way for it to know which HV needs the answer. Thus, the HV that processes the ARP reply (which is unknown when the ARP is sent) has to tell all the HVs the binding. The most obvious place for this in the OVN_Southbound database. Details need to be worked out, including: *** OVN_Southbound schema changes. Possibly bindings could be added to the Port_Binding table by adding or modifying columns. Another possibility is that another table should be added. *** Logical_Flow representation It would be really nice to maintain the general-purpose nature of logical flows, but these bindings might have to include some hard-coded special cases, especially when it comes to the relationship with populating the bindings into the OVN_Southbound table. *** Tracking queries It's probably best to only record in the database responses to queries actually issued by an L3 logical router, so somehow they have to be tracked, probably by putting a tentative binding without a MAC address into the database. *** Renewal and expiration. Something needs to make sure that bindings remain valid and expire those that become stale. ** MTU handling (fragmentation on output) ** Ratelimiting. *** ARP. *** ICMP error generation, TCP reset, UDP unreachable, protocol unreachable, ... As a point of comparison, Linux doesn't ratelimit TCP resets but I think it does everything else. * ovn-controller ** ovn-controller parameters and configuration. *** SSL configuration. Can probably get this from Open_vSwitch database. ** Security *** Limiting the impact of a compromised chassis. Every instance of ovn-controller has the same full access to the central OVN_Southbound database. This means that a compromised chassis can interfere with the normal operation of the rest of the deployment. Some specific examples include writing to the logical flow table to alter traffic handling or updating the port binding table to claim ports that are actually present on a different chassis. In practice, the compromised host would be fighting against ovn-northd and other instances of ovn-controller that would be trying to restore the correct state. The impact could include at least temporarily redirecting traffic (so the compromised host could receive traffic that it shouldn't) and potentially a more general denial of service. There are different potential improvements to this area. The first would be to add some sort of ACL scheme to ovsdb-server. A proposal for this should first include an ACL scheme for ovn-controller. An example policy would be to make Logical_Flow read-only. Table-level control is needed, but is not enough. For example, ovn-controller must be able to update the Chassis and Encap tables, but should only be able to modify the rows associated with that chassis and no others. A more complex example is the Port_Binding table. Currently, ovn-controller is the source of truth of where a port is located. There seems to be no policy that can prevent malicious behavior of a compromised host with this table. An alternative scheme for port bindings would be to provide an optional mode where an external entity controls port bindings and make them read-only to ovn-controller. This is actually how OpenStack works today, for example. The part of OpenStack that manages VMs (Nova) tells the networking component (Neutron) where a port will be located, as opposed to the networking component discovering it. * ovsdb-server ovsdb-server should have adequate features for OVN but it probably needs work for scale and possibly for availability as deployments grow. Here are some thoughts. Andy Zhou is looking at these issues. *** Reducing amount of data sent to clients. Currently, whenever a row monitored by a client changes, ovsdb-server sends the client every monitored column in the row, even if only one column changes. It might be valuable to reduce this only to the columns that changes. Also, whenever a column changes, ovsdb-server sends the entire contents of the column. It might be valuable, for columns that are sets or maps, to send only added or removed values or key-values pairs. Currently, clients monitor the entire contents of a table. It might make sense to allow clients to monitor only rows that satisfy specific criteria, e.g. to allow an ovn-controller to receive only Logical_Flow rows for logical networks on its hypervisor. *** Reducing redundant data and code within ovsdb-server. Currently, ovsdb-server separately composes database update information to send to each of its clients. This is fine for a small number of clients, but it wastes time and memory when hundreds of clients all want the same updates (as will be in the case in OVN). (This is somewhat opposed to the idea of letting a client monitor only some rows in a table, since that would increase the diversity among clients.) *** Multithreading. If it turns out that other changes don't let ovsdb-server scale adequately, we can multithread ovsdb-server. Initially one might only break protocol handling into separate threads, leaving the actual database work serialized through a lock. ** Increasing availability. Database availability might become an issue. The OVN system shouldn't grind to a halt if the database becomes unavailable, but it would become impossible to bring VIFs up or down, etc. My current thought on how to increase availability is to add clustering to ovsdb-server, probably via the Raft consensus algorithm. As an experiment, I wrote an implementation of Raft for Open vSwitch that you can clone from: https://github.com/blp/ovs-reviews.git raft ** Reducing startup time. As-is, if ovsdb-server restarts, every client will fetch a fresh copy of the part of the database that it cares about. With hundreds of clients, this could cause heavy CPU load on ovsdb-server and use excessive network bandwidth. It would be better to allow incremental updates even across connection loss. One way might be to use "Difference Digests" as described in Epstein et al., "What's the Difference? Efficient Set Reconciliation Without Prior Context". (I'm not yet aware of previous non-academic use of this technique.) ** Support multiple tunnel encapsulations in Chassis. So far, both ovn-controller and ovn-controller-vtep only allow chassis to have one tunnel encapsulation entry. We should extend the implementation to support multiple tunnel encapsulations. ** Update learned MAC addresses from VTEP to OVN The VTEP gateway stores all MAC addresses learned from its physical interfaces in the 'Ucast_Macs_Local' and the 'Mcast_Macs_Local' tables. ovn-controller-vtep should be able to update that information back to ovn-sb database, so that other chassis know where to send packets destined to the extended external network instead of broadcasting. ** Translate ovn-sb Multicast_Group table into VTEP config The ovn-controller-vtep daemon should be able to translate the Multicast_Group table entry in ovn-sb database into Mcast_Macs_Remote table configuration in VTEP database. * Consider the use of BFD as tunnel monitor. The use of BFD for hypervisor-to-hypervisor tunnels is probably not worth it, since there's no alternative to switch to if a tunnel goes down. It could make sense at a slow rate if someone does OVN monitoring system integration, but not otherwise. When OVN gets to supporting HA for gateways (see ovn/OVN-GW-HA.md), BFD is likely needed as a part of that solution. There's more commentary in this ML post: http://openvswitch.org/pipermail/dev/2015-November/062385.html * ACL ** Support FTP ALGs. ** Support reject action. ** Support log option. openvswitch-2.5.9/ovn/PaxHeaders.82075/CONTAINERS.OpenStack.md0000644000000000000000000000013013534540071020321 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.505851391 openvswitch-2.5.9/ovn/CONTAINERS.OpenStack.md0000644000175000017500000001355413534540071022021 0ustar00jpettitjpettit00000000000000Integration of Containers with OVN and OpenStack ------------------------------------------------ Isolation between containers is weaker than isolation between VMs, so some environments deploy containers for different tenants in separate VMs as an additional security measure. This document describes creation of containers inside VMs and how they can be made part of the logical networks securely. The created logical network can include VMs, containers and physical machines as endpoints. To better understand the proposed integration of containers with OVN and OpenStack, this document describes the end to end workflow with an example. * A OpenStack tenant creates a VM (say VM-A) with a single network interface that belongs to a management logical network. The VM is meant to host containers. OpenStack Nova chooses the hypervisor on which VM-A is created. * A Neutron port may have been created in advance and passed in to Nova with the request to create a new VM. If not, Nova will issue a request to Neutron to create a new port. The ID of the logical port from Neutron will also be used as the vif-id for the virtual network interface (VIF) of VM-A. * When VM-A is created on a hypervisor, its VIF gets added to the Open vSwitch integration bridge. This creates a row in the Interface table of the Open_vSwitch database. As explained in the [IntegrationGuide.md], the vif-id associated with the VM network interface gets added in the external_ids:iface-id column of the newly created row in the Interface table. * Since VM-A belongs to a logical network, it gets an IP address. This IP address is used to spawn containers (either manually or through container orchestration systems) inside that VM and to monitor the health of the created containers. * The vif-id associated with the VM's network interface can be obtained by making a call to Neutron using tenant credentials. * This flow assumes a component called a "container network plugin". If you take Docker as an example for containers, you could envision the plugin to be either a wrapper around Docker or a feature of Docker itself that understands how to perform part of this workflow to get a container connected to a logical network managed by Neutron. The rest of the flow refers to this logical component that does not yet exist as the "container network plugin". * All the calls to Neutron will need tenant credentials. These calls can either be made from inside the tenant VM as part of a container network plugin or from outside the tenant VM (if the tenant is not comfortable using temporary Keystone tokens from inside the tenant VMs). For simplicity, this document explains the work flow using the former method. * The container hosting VM will need Open vSwitch installed in it. The only work for Open vSwitch inside the VM is to tag network traffic coming from containers. * When a container needs to be created inside the VM with a container network interface that is expected to be attached to a particular logical switch, the network plugin in that VM chooses any unused VLAN (This VLAN tag only needs to be unique inside that VM. This limits the number of container interfaces to 4096 inside a single VM). This VLAN tag is stripped out in the hypervisor by OVN and is only useful as a context (or metadata) for OVN. * The container network plugin then makes a call to Neutron to create a logical port. In addition to all the inputs that a call to create a port in Neutron that are currently needed, it sends the vif-id and the VLAN tag as inputs. * Neutron in turn will verify that the vif-id belongs to the tenant in question and then uses the OVN specific plugin to create a new row in the Logical_Port table of the OVN Northbound Database. Neutron responds back with an IP address and MAC address for that network interface. So Neutron becomes the IPAM system and provides unique IP and MAC addresses across VMs and containers in the same logical network. * The Neutron API call above to create a logical port for the container could add a relatively significant amount of time to container creation. However, an optimization is possible here. Logical ports could be created in advance and reused by the container system doing container orchestration. Additional Neutron API calls would only be needed if the port needs to be attached to a different logical network. * When a container is eventually deleted, the network plugin in that VM may make a call to Neutron to delete that port. Neutron in turn will delete the entry in the Logical_Port table of the OVN Northbound Database. As an example, consider Docker containers. Since Docker currently does not have a network plugin feature, this example uses a hypothetical wrapper around Docker to make calls to Neutron. * Create a Logical switch, e.g.: ``` % ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f create network LS1 ``` The above command will make a call to Neutron with the credentials to create a logical switch. The above is optional if the logical switch has already been created from outside the VM. * List networks available to the tenant. ``` % ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f list networks ``` * Create a container and attach a interface to the previously created switch as a logical port. ``` % ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f --vif-id=$VIF_ID \ --network=LS1 run -d --net=none ubuntu:14.04 /bin/sh -c \ "while true; do echo hello world; sleep 1; done" ``` The above command will make a call to Neutron with all the inputs it currently needs to create a logical port. In addition, it passes the $VIF_ID and a unused VLAN. Neutron will add that information in OVN and return back a MAC address and IP address for that interface. ovn-docker will then create a veth pair, insert one end inside the container as 'eth0' and the other end as a port of a local OVS bridge as an access port of the chosen VLAN. [IntegrationGuide.md]:IntegrationGuide.md openvswitch-2.5.9/ovn/PaxHeaders.82075/controller-vtep0000644000000000000000000000013213534540121017524 xustar0030 mtime=1567801425.109855844 30 atime=1567801425.625859648 30 ctime=1567801425.109855844 openvswitch-2.5.9/ovn/controller-vtep/0000755000175000017500000000000013534540121021267 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/ovn-controller-vtep.c0000644000000000000000000000013013534540071023706 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.109855844 openvswitch-2.5.9/ovn/controller-vtep/ovn-controller-vtep.c0000644000175000017500000001623113534540071025401 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "poll-loop.h" #include "stream.h" #include "stream-ssl.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "vtep/vtep-idl.h" #include "binding.h" #include "gateway.h" #include "vtep.h" #include "ovn-controller-vtep.h" static unixctl_cb_func ovn_controller_vtep_exit; static void parse_options(int argc, char *argv[]); OVS_NO_RETURN static void usage(void); static char *vtep_remote; static char *ovnsb_remote; static char *default_db_; int main(int argc, char *argv[]) { struct unixctl_server *unixctl; bool exiting; int retval; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); fatal_ignore_sigpipe(); daemonize_start(false); retval = unixctl_server_create(NULL, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, ovn_controller_vtep_exit, &exiting); daemonize_complete(); vteprec_init(); sbrec_init(); /* Connect to VTEP database. */ struct ovsdb_idl_loop vtep_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(vtep_remote, &vteprec_idl_class, true, true)); ovsdb_idl_get_initial_snapshot(vtep_idl_loop.idl); /* Connect to OVN SB database. */ struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true)); ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl); /* Main loop. */ exiting = false; while (!exiting) { struct controller_vtep_ctx ctx = { .vtep_idl = vtep_idl_loop.idl, .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; gateway_run(&ctx); binding_run(&ctx); vtep_run(&ctx); unixctl_server_run(unixctl); unixctl_server_wait(unixctl); if (exiting) { poll_immediate_wake(); } ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop); ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); poll_block(); if (should_service_stop()) { exiting = true; } } /* It's time to exit. Clean up the databases. */ bool done = false; while (!done) { struct controller_vtep_ctx ctx = { .vtep_idl = vtep_idl_loop.idl, .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; /* Run all of the cleanup functions, even if one of them returns false. * We're done if all of them return true. */ done = binding_cleanup(&ctx); done = gateway_cleanup(&ctx) && done; done = vtep_cleanup(&ctx) && done; if (done) { poll_immediate_wake(); } ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop); ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); poll_block(); } unixctl_server_destroy(unixctl); ovsdb_idl_loop_destroy(&vtep_idl_loop); ovsdb_idl_loop_destroy(&ovnsb_idl_loop); free(ovnsb_remote); free(vtep_remote); free(default_db_); service_stop(); exit(retval); } static const char * default_db(void) { if (!default_db_) { default_db_ = xasprintf("unix:%s/db.sock", ovs_rundir()); } return default_db_; } static void parse_options(int argc, char *argv[]) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, OPT_BOOTSTRAP_CA_CERT, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; static struct option long_options[] = { {"ovnsb-db", required_argument, NULL, 'd'}, {"vtep-db", required_argument, NULL, 'D'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, DAEMON_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {NULL, 0, NULL, 0} }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'd': ovnsb_remote = xstrdup(optarg); break; case 'D': vtep_remote = xstrdup(optarg); break; case 'h': usage(); case 'V': ovs_print_version(OFP13_VERSION, OFP13_VERSION); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); argc -= optind; argv += optind; if (!ovnsb_remote) { ovnsb_remote = xstrdup(default_db()); } if (!vtep_remote) { vtep_remote = xstrdup(default_db()); } } static void usage(void) { printf("\ %s: OVN controller VTEP\n\ usage %s [OPTIONS]\n\ \n\ Options:\n\ --vtep-db=DATABASE connect to vtep database at DATABASE\n\ (default: %s)\n\ --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\ (default: %s)\n\ -h, --help display this help message\n\ -o, --options list available options\n\ -V, --version display version information\n\ ", program_name, program_name, default_db(), default_db()); stream_usage("database", true, false, false); daemon_usage(); vlog_usage(); exit(EXIT_SUCCESS); } static void ovn_controller_vtep_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/gateway.c0000644000000000000000000000013013534540071021410 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.105855814 openvswitch-2.5.9/ovn/controller-vtep/gateway.c0000644000175000017500000001744513534540071023113 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "gateway.h" #include "lib/poll-loop.h" #include "lib/simap.h" #include "lib/sset.h" #include "lib/util.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "vtep/vtep-idl.h" #include "ovn-controller-vtep.h" VLOG_DEFINE_THIS_MODULE(gateway); /* * Registers the physical switches in vtep to ovnsb as chassis. For each * physical switch in the vtep database, finds all vtep logical switches that * are associated with the physical switch, and updates the corresponding * chassis's 'vtep_logical_switches' column. * */ /* Global revalidation sequence number, incremented at each call to * 'revalidate_gateway()'. */ static unsigned int gw_reval_seq; /* Maps all chassis created by the gateway module to their own reval_seq. */ static struct simap gw_chassis_map = SIMAP_INITIALIZER(&gw_chassis_map); /* Creates and returns a new instance of 'struct sbrec_chassis'. */ static const struct sbrec_chassis * create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name, const char *encap_ip) { const struct sbrec_chassis *chassis_rec; struct sbrec_encap *encap_rec; VLOG_INFO("add Chassis row for VTEP physical switch (%s)", name); chassis_rec = sbrec_chassis_insert(txn); sbrec_chassis_set_name(chassis_rec, name); encap_rec = sbrec_encap_insert(txn); sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE); sbrec_encap_set_ip(encap_rec, encap_ip); sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1); return chassis_rec; } /* Revalidates chassis in ovnsb against vtep database. Creates chassis for * new vtep physical switch. And removes chassis which no longer have * physical switch in vtep. * * xxx: Support multiple tunnel encaps. * * */ static void revalidate_gateway(struct controller_vtep_ctx *ctx) { const struct vteprec_physical_switch *pswitch; /* Increments the global revalidation sequence number. */ gw_reval_seq++; ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: updating vtep chassis"); VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { const struct sbrec_chassis *chassis_rec; struct simap_node *gw_node; const char *encap_ip; encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : ""; gw_node = simap_find(&gw_chassis_map, pswitch->name); chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); if (chassis_rec) { if (!gw_node && (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE) || strcmp(chassis_rec->encaps[0]->ip, encap_ip))) { VLOG_WARN("Chassis config changing on startup, make sure " "multiple chassis are not configured : %s/%s->%s/%s", chassis_rec->encaps[0]->type, chassis_rec->encaps[0]->ip, OVN_SB_ENCAP_TYPE, encap_ip); } /* Updates chassis's encap if anything changed. */ if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) { VLOG_WARN("Chassis for VTEP physical switch (%s) can only have " "encap type \"%s\"", pswitch->name, OVN_SB_ENCAP_TYPE); sbrec_encap_set_type(chassis_rec->encaps[0], OVN_SB_ENCAP_TYPE); } if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) { sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip); } } else { if (gw_node) { VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, " "maybe deleted by ovn-sbctl, adding it back", pswitch->name); } /* Creates a new chassis for the VTEP physical switch. */ create_chassis_rec(ctx->ovnsb_idl_txn, pswitch->name, encap_ip); } /* Updates or creates the simap node for 'pswitch->name'. */ simap_put(&gw_chassis_map, pswitch->name, gw_reval_seq); } struct simap_node *iter, *next; /* For 'gw_node' in 'gw_chassis_map' whose data is not * 'gw_reval_seq', it means the corresponding physical switch no * longer exist. So, garbage collects them. */ SIMAP_FOR_EACH_SAFE (iter, next, &gw_chassis_map) { if (iter->data != gw_reval_seq) { const struct sbrec_chassis *chassis_rec; chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name); if (chassis_rec) { sbrec_chassis_delete(chassis_rec); } simap_delete(&gw_chassis_map, iter); } } } /* Updates the 'vtep_logical_switches' column in the Chassis table based * on vtep database configuration. */ static void update_vtep_logical_switches(struct controller_vtep_ctx *ctx) { const struct vteprec_physical_switch *pswitch; ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: " "updating chassis's vtep_logical_switches"); VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { const struct sbrec_chassis *chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); struct sset lswitches = SSET_INITIALIZER(&lswitches); size_t i; for (i = 0; i < pswitch->n_ports; i++) { const struct vteprec_physical_port *port = pswitch->ports[i]; size_t j; for (j = 0; j < port->n_vlan_bindings; j++) { const struct vteprec_logical_switch *vtep_lswitch; vtep_lswitch = port->value_vlan_bindings[j]; /* If not already in 'lswitches', records it. */ if (!sset_find(&lswitches, vtep_lswitch->name)) { sset_add(&lswitches, vtep_lswitch->name); } } } const char **ls_arr = sset_array(&lswitches); sbrec_chassis_set_vtep_logical_switches(chassis_rec, ls_arr, sset_count(&lswitches)); free(ls_arr); sset_destroy(&lswitches); } } void gateway_run(struct controller_vtep_ctx *ctx) { if (!ctx->ovnsb_idl_txn) { return; } revalidate_gateway(ctx); update_vtep_logical_switches(ctx); } /* Destroys the chassis table entries for vtep physical switches. * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'), * otherwise returns false. */ bool gateway_cleanup(struct controller_vtep_ctx *ctx) { static bool simap_destroyed = false; const struct vteprec_physical_switch *pswitch; if (!ctx->ovnsb_idl_txn) { return false; } bool all_done = true; ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: " "unregistering vtep chassis"); VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { const struct sbrec_chassis *chassis_rec; chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); if (!chassis_rec) { continue; } all_done = false; sbrec_chassis_delete(chassis_rec); } if (!simap_destroyed) { simap_destroy(&gw_chassis_map); simap_destroyed = true; } return all_done; } openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/binding.h0000644000000000000000000000013013534540071021366 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.105855814 openvswitch-2.5.9/ovn/controller-vtep/binding.h0000644000175000017500000000151013534540071023053 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_BINDING_H #define OVN_BINDING_H 1 #include struct controller_vtep_ctx; void binding_run(struct controller_vtep_ctx *); bool binding_cleanup(struct controller_vtep_ctx *); #endif /* ovn/controller-gw/binding.h */ openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/automake.mk0000644000000000000000000000013013534540071021742 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.609852158 openvswitch-2.5.9/ovn/controller-vtep/automake.mk0000644000175000017500000000123213534540071023430 0ustar00jpettitjpettit00000000000000bin_PROGRAMS += ovn/controller-vtep/ovn-controller-vtep ovn_controller_vtep_ovn_controller_vtep_SOURCES = \ ovn/controller-vtep/binding.c \ ovn/controller-vtep/binding.h \ ovn/controller-vtep/gateway.c \ ovn/controller-vtep/gateway.h \ ovn/controller-vtep/ovn-controller-vtep.c \ ovn/controller-vtep/ovn-controller-vtep.h \ ovn/controller-vtep/vtep.c \ ovn/controller-vtep/vtep.h ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la vtep/libvtep.la man_MANS += ovn/controller-vtep/ovn-controller-vtep.8 EXTRA_DIST += ovn/controller-vtep/ovn-controller-vtep.8.xml DISTCLEANFILES += ovn/controller-vtep/ovn-controller-vtep.8 openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/vtep.c0000644000000000000000000000013013534540071020725 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.109855844 openvswitch-2.5.9/ovn/controller-vtep/vtep.c0000644000175000017500000003534113534540071022423 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "vtep.h" #include "lib/hash.h" #include "lib/hmap.h" #include "lib/shash.h" #include "lib/smap.h" #include "lib/sset.h" #include "lib/util.h" #include "ovn-controller-vtep.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "vtep/vtep-idl.h" VLOG_DEFINE_THIS_MODULE(vtep); /* * Scans through the Binding table in ovnsb, and updates the vtep logical * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP * database. * */ /* Searches the 'chassis_rec->encaps' for the first vtep tunnel * configuration, returns the 'ip'. Unless duplicated, the returned * pointer cannot live past current vtep_run() execution. */ static const char * get_chassis_vtep_ip(const struct sbrec_chassis *chassis_rec) { if (chassis_rec) { size_t i; for (i = 0; i < chassis_rec->n_encaps; i++) { if (!strcmp(chassis_rec->encaps[i]->type, "vxlan")) { return chassis_rec->encaps[i]->ip; } } } return NULL; } /* Creates a new 'Ucast_Macs_Remote'. */ static struct vteprec_ucast_macs_remote * create_umr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac, const struct vteprec_logical_switch *vtep_ls) { struct vteprec_ucast_macs_remote *new_umr; new_umr = vteprec_ucast_macs_remote_insert(vtep_idl_txn); vteprec_ucast_macs_remote_set_MAC(new_umr, mac); vteprec_ucast_macs_remote_set_logical_switch(new_umr, vtep_ls); return new_umr; } /* Creates a new 'Physical_Locator'. */ static struct vteprec_physical_locator * create_pl(struct ovsdb_idl_txn *vtep_idl_txn, const char *chassis_ip) { struct vteprec_physical_locator *new_pl; new_pl = vteprec_physical_locator_insert(vtep_idl_txn); vteprec_physical_locator_set_dst_ip(new_pl, chassis_ip); vteprec_physical_locator_set_encapsulation_type(new_pl, VTEP_ENCAP_TYPE); return new_pl; } /* Updates the vtep Logical_Switch table entries' tunnel keys based * on the port bindings. */ static void vtep_lswitch_run(struct shash *vtep_pbs, struct sset *vtep_pswitches, struct shash *vtep_lswitches) { struct sset used_ls = SSET_INITIALIZER(&used_ls); struct shash_node *node; /* Collects the logical switch bindings from port binding entries. * Since the binding module has already guaranteed that each vtep * logical switch is bound only to one ovn-sb logical datapath, * we can just iterate and assign tunnel key to vtep logical switch. */ SHASH_FOR_EACH (node, vtep_pbs) { const struct sbrec_port_binding *port_binding_rec = node->data; const char *pswitch_name = smap_get(&port_binding_rec->options, "vtep-physical-switch"); const char *lswitch_name = smap_get(&port_binding_rec->options, "vtep-logical-switch"); const struct vteprec_logical_switch *vtep_ls; /* If 'port_binding_rec->chassis' exists then 'pswitch_name' * and 'lswitch_name' must also exist. */ if (!pswitch_name || !lswitch_name) { /* This could only happen when someone directly modifies the * database. (e.g. using ovn-sbctl) */ VLOG_ERR("logical port (%s) with no 'options:vtep-physical-" "switch' or 'options:vtep-logical-switch' specified " "is bound to chassis (%s).", port_binding_rec->logical_port, port_binding_rec->chassis->name); continue; } vtep_ls = shash_find_data(vtep_lswitches, lswitch_name); /* Also checks 'pswitch_name' since the same 'lswitch_name' could * exist in multiple vtep database instances and be bound to different * ovn logical networks. */ if (vtep_ls && sset_find(vtep_pswitches, pswitch_name)) { int64_t tnl_key; if (sset_find(&used_ls, lswitch_name)) { continue; } tnl_key = port_binding_rec->datapath->tunnel_key; if (vtep_ls->n_tunnel_key && vtep_ls->tunnel_key[0] != tnl_key) { VLOG_DBG("set vtep logical switch (%s) tunnel key from " "(%"PRId64") to (%"PRId64")", vtep_ls->name, vtep_ls->tunnel_key[0], tnl_key); } vteprec_logical_switch_set_tunnel_key(vtep_ls, &tnl_key, 1); sset_add(&used_ls, lswitch_name); } } /* Resets the tunnel keys for unused vtep logical switches. */ SHASH_FOR_EACH (node, vtep_lswitches) { if (!sset_find(&used_ls, node->name)) { int64_t tnl_key = 0; vteprec_logical_switch_set_tunnel_key(node->data, &tnl_key, 1); } } sset_destroy(&used_ls); } /* Updates the vtep 'Ucast_Macs_Remote' table based on non-vtep port * bindings. */ static void vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct shash *ucast_macs_rmts, struct shash *physical_locators, struct shash *vtep_lswitches, struct shash *non_vtep_pbs) { struct shash_node *node; struct hmap ls_map; /* Maps from ovn logical datapath tunnel key (which is also the vtep * logical switch tunnel key) to the corresponding vtep logical switch * instance. Also, the shash map 'added_macs' is used for checking * duplicated MAC addresses in the same ovn logical datapath. */ struct ls_hash_node { struct hmap_node hmap_node; const struct vteprec_logical_switch *vtep_ls; struct shash added_macs; }; hmap_init(&ls_map); SHASH_FOR_EACH (node, vtep_lswitches) { const struct vteprec_logical_switch *vtep_ls = node->data; struct ls_hash_node *ls_node; if (!vtep_ls->n_tunnel_key) { continue; } ls_node = xmalloc(sizeof *ls_node); ls_node->vtep_ls = vtep_ls; shash_init(&ls_node->added_macs); hmap_insert(&ls_map, &ls_node->hmap_node, hash_uint64((uint64_t) vtep_ls->tunnel_key[0])); } SHASH_FOR_EACH (node, non_vtep_pbs) { const struct sbrec_port_binding *port_binding_rec = node->data; const struct sbrec_chassis *chassis_rec; struct ls_hash_node *ls_node; const char *chassis_ip; int64_t tnl_key; size_t i; chassis_rec = port_binding_rec->chassis; if (!chassis_rec) { continue; } tnl_key = port_binding_rec->datapath->tunnel_key; HMAP_FOR_EACH_WITH_HASH (ls_node, hmap_node, hash_uint64((uint64_t) tnl_key), &ls_map) { if (ls_node->vtep_ls->tunnel_key[0] == tnl_key) { break; } } /* If 'ls_node' is NULL, that means no vtep logical switch is * attached to the corresponding ovn logical datapath, so pass. */ if (!ls_node) { continue; } chassis_ip = get_chassis_vtep_ip(chassis_rec); /* Unreachable chassis, continue. */ if (!chassis_ip) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_INFO_RL(&rl, "VTEP tunnel encap on chassis (%s) not found", chassis_rec->name); continue; } for (i = 0; i < port_binding_rec->n_mac; i++) { const struct vteprec_ucast_macs_remote *umr; const struct vteprec_physical_locator *pl; const struct sbrec_port_binding *conflict; char *mac = port_binding_rec->mac[i]; /* xxx Need to address this later when we support * update of 'Mcast_Macs_Remote' table in VTEP. */ if (!strcmp(mac, "unknown")) { continue; } /* Checks for duplicate MAC in the same vtep logical switch. */ conflict = shash_find_data(&ls_node->added_macs, mac); if (conflict) { VLOG_WARN("MAC address (%s) has already been known to be " "on logical port (%s) in the same logical " "datapath, so just ignore this logical port (%s)", mac, conflict->logical_port, port_binding_rec->logical_port); continue; } shash_add(&ls_node->added_macs, mac, port_binding_rec); char *mac_ip_tnlkey = xasprintf("%s_%s_%"PRId64, mac, chassis_ip, tnl_key); umr = shash_find_data(ucast_macs_rmts, mac_ip_tnlkey); /* If finds the 'umr' entry for the mac, ip, and tnl_key, deletes * the entry from shash so that it is not gargage collected. * * If not found, creates a new 'umr' entry. */ if (umr && umr->logical_switch == ls_node->vtep_ls) { shash_find_and_delete(ucast_macs_rmts, mac_ip_tnlkey); } else { const struct vteprec_ucast_macs_remote *new_umr; new_umr = create_umr(vtep_idl_txn, mac, ls_node->vtep_ls); pl = shash_find_data(physical_locators, chassis_ip); if (pl) { vteprec_ucast_macs_remote_set_locator(new_umr, pl); } else { const struct vteprec_physical_locator *new_pl; new_pl = create_pl(vtep_idl_txn, chassis_ip); vteprec_ucast_macs_remote_set_locator(new_umr, new_pl); /* Updates the 'physical_locators'. */ shash_add(physical_locators, chassis_ip, new_pl); } } free(mac_ip_tnlkey); } } /* Removes all remaining 'umr's, since they do not exist anymore. */ SHASH_FOR_EACH (node, ucast_macs_rmts) { vteprec_ucast_macs_remote_delete(node->data); } struct ls_hash_node *iter, *next; HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &ls_map) { hmap_remove(&ls_map, &iter->hmap_node); shash_destroy(&iter->added_macs); free(iter); } hmap_destroy(&ls_map); } /* Resets all logical switches' 'tunnel_key' to NULL */ static bool vtep_lswitch_cleanup(struct ovsdb_idl *vtep_idl) { const struct vteprec_logical_switch *vtep_ls; bool done = true; VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, vtep_idl) { if (vtep_ls->n_tunnel_key) { vteprec_logical_switch_set_tunnel_key(vtep_ls, NULL, 0); done = false; } } return done; } /* Removes all entries in the 'Ucast_Macs_Remote' table in vtep database. * Returns true when all done (no entry to remove). */ static bool vtep_macs_cleanup(struct ovsdb_idl *vtep_idl) { const struct vteprec_ucast_macs_remote *umr; VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, vtep_idl) { vteprec_ucast_macs_remote_delete(umr); return false; } return true; } /* Updates vtep logical switch tunnel keys. */ void vtep_run(struct controller_vtep_ctx *ctx) { if (!ctx->vtep_idl_txn) { return; } struct sset vtep_pswitches = SSET_INITIALIZER(&vtep_pswitches); struct shash vtep_lswitches = SHASH_INITIALIZER(&vtep_lswitches); struct shash ucast_macs_rmts = SHASH_INITIALIZER(&ucast_macs_rmts); struct shash physical_locators = SHASH_INITIALIZER(&physical_locators); struct shash vtep_pbs = SHASH_INITIALIZER(&vtep_pbs); struct shash non_vtep_pbs = SHASH_INITIALIZER(&non_vtep_pbs); const struct vteprec_physical_switch *vtep_ps; const struct vteprec_logical_switch *vtep_ls; const struct vteprec_ucast_macs_remote *umr; const struct vteprec_physical_locator *pl; const struct sbrec_port_binding *port_binding_rec; /* Collects 'Physical_Switch's. */ VTEPREC_PHYSICAL_SWITCH_FOR_EACH (vtep_ps, ctx->vtep_idl) { sset_add(&vtep_pswitches, vtep_ps->name); } /* Collects 'Logical_Switch's. */ VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, ctx->vtep_idl) { shash_add(&vtep_lswitches, vtep_ls->name, vtep_ls); } /* Collects 'Ucast_Macs_Remote's. */ VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, ctx->vtep_idl) { char *mac_ip_tnlkey = xasprintf("%s_%s_%"PRId64, umr->MAC, umr->locator ? umr->locator->dst_ip : "", umr->logical_switch && umr->logical_switch->n_tunnel_key ? umr->logical_switch->tunnel_key[0] : INT64_MAX); shash_add(&ucast_macs_rmts, mac_ip_tnlkey, umr); free(mac_ip_tnlkey); } /* Collects 'Physical_Locator's. */ VTEPREC_PHYSICAL_LOCATOR_FOR_EACH (pl, ctx->vtep_idl) { shash_add(&physical_locators, pl->dst_ip, pl); } /* Collects and classifies 'Port_Binding's. */ SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) { struct shash *target = !strcmp(port_binding_rec->type, "vtep") ? &vtep_pbs : &non_vtep_pbs; if (!port_binding_rec->chassis) { continue; } shash_add(target, port_binding_rec->logical_port, port_binding_rec); } ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn, "ovn-controller-vtep: update logical switch " "tunnel keys and 'ucast_macs_remote's"); vtep_lswitch_run(&vtep_pbs, &vtep_pswitches, &vtep_lswitches); vtep_macs_run(ctx->vtep_idl_txn, &ucast_macs_rmts, &physical_locators, &vtep_lswitches, &non_vtep_pbs); sset_destroy(&vtep_pswitches); shash_destroy(&vtep_lswitches); shash_destroy(&ucast_macs_rmts); shash_destroy(&physical_locators); shash_destroy(&vtep_pbs); shash_destroy(&non_vtep_pbs); } /* Cleans up all related entries in vtep. Returns true when done (i.e. * there is no change made to 'ctx->vtep_idl'), otherwise returns false. */ bool vtep_cleanup(struct controller_vtep_ctx *ctx) { if (!ctx->vtep_idl_txn) { return false; } bool all_done; ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn, "ovn-controller-vtep: cleaning up vtep " "configuration"); all_done = vtep_lswitch_cleanup(ctx->vtep_idl); all_done = vtep_macs_cleanup(ctx->vtep_idl) && all_done; return all_done; } openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/ovn-controller-vtep.8.xml0000644000000000000000000000013013534540071024432 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.509851421 openvswitch-2.5.9/ovn/controller-vtep/ovn-controller-vtep.8.xml0000644000175000017500000000512513534540071026125 0ustar00jpettitjpettit00000000000000

    Name

    ovn-controller-vtep -- Open Virtual Network local controller for vtep enabled physical switches.

    Synopsis

    ovn-controller-vtep [options] [--vtep-db=vtep-database] [--ovnsb-db=ovnsb-database]

    Description

    ovn-controller-vtep is the local controller daemon in OVN, the Open Virtual Network, for VTEP enabled physical switches. It connects up to the OVN Southbound database (see ovn-sb(5)) over the OVSDB protocol, and down to the VTEP database (see vtep(5)) over the OVSDB protocol.

    Configuration

    ovn-controller-vtep retrieves its configuration information from both the ovnsb and the vtep database. If the database locations are not given from command line, the default is the db.sock in local OVSDB's 'run' directory. The datapath location must take one of the following forms:

    • ssl:ip:port

      The specified SSL port on the host at the given ip, which must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: ssl:[::1]:6640. The --private-key, --certificate and either of --ca-cert or --bootstrap-ca-cert options are mandatory when this form is used.

    • tcp:ip:port

      Connect to the given TCP port on ip, where ip can be IPv4 or IPv6 address. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: tcp:[::1]:6640.

    • unix:file

      On POSIX, connect to the Unix domain server socket named file.

      On Windows, connect to a localhost TCP port whose value is written in file.

    openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/vtep.h0000644000000000000000000000013013534540071020732 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.109855844 openvswitch-2.5.9/ovn/controller-vtep/vtep.h0000644000175000017500000000147313534540071022427 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_VTEP_H #define OVN_VTEP_H 1 #include struct controller_vtep_ctx; void vtep_run(struct controller_vtep_ctx *); bool vtep_cleanup(struct controller_vtep_ctx *); #endif /* ovn/controller-vtep/vtep.h */ openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/binding.c0000644000000000000000000000013013534540071021361 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.101855786 openvswitch-2.5.9/ovn/controller-vtep/binding.c0000644000175000017500000002367413534540071023065 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "binding.h" #include "lib/shash.h" #include "lib/smap.h" #include "lib/util.h" #include "openvswitch/vlog.h" #include "ovn-controller-vtep.h" #include "ovn/lib/ovn-sb-idl.h" #include "vtep/vtep-idl.h" VLOG_DEFINE_THIS_MODULE(binding); /* * This module scans through the Port_Binding table in ovnsb. If there is a * logical port binding entry for logical switch in vtep gateway chassis's * 'vtep_logical_switches' column, sets the binding's chassis column to the * corresponding vtep gateway chassis. * */ /* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec' * has already been bound to another port binding entry, and resets * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_pb' * and returns false. */ static bool check_pb_conflict(struct shash *ls_to_pb, const struct sbrec_port_binding *port_binding_rec, const char *chassis_name, const char *vtep_lswitch) { const struct sbrec_port_binding *pb_conflict = shash_find_data(ls_to_pb, vtep_lswitch); if (pb_conflict) { VLOG_WARN("logical switch (%s), on vtep gateway chassis " "(%s) has already been associated with logical " "port (%s), ignore logical port (%s)", vtep_lswitch, chassis_name, pb_conflict->logical_port, port_binding_rec->logical_port); sbrec_port_binding_set_chassis(port_binding_rec, NULL); return true; } shash_add(ls_to_pb, vtep_lswitch, port_binding_rec); return false; } /* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec' * has already been bound to a different datapath, and resets * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_db' and * returns false. */ static bool check_db_conflict(struct shash *ls_to_db, const struct sbrec_port_binding *port_binding_rec, const char *chassis_name, const char *vtep_lswitch) { const struct sbrec_datapath_binding *db_conflict = shash_find_data(ls_to_db, vtep_lswitch); if (db_conflict && db_conflict != port_binding_rec->datapath) { VLOG_WARN("logical switch (%s), on vtep gateway chassis " "(%s) has already been associated with logical " "datapath (with tunnel key %"PRId64"), ignore " "logical port (%s) which belongs to logical " "datapath (with tunnel key %"PRId64")", vtep_lswitch, chassis_name, db_conflict->tunnel_key, port_binding_rec->logical_port, port_binding_rec->datapath->tunnel_key); sbrec_port_binding_set_chassis(port_binding_rec, NULL); return true; } shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath); return false; } /* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */ static void update_pb_chassis(const struct sbrec_port_binding *port_binding_rec, const struct sbrec_chassis *chassis_rec) { if (port_binding_rec->chassis != chassis_rec) { if (chassis_rec && port_binding_rec->chassis) { VLOG_DBG("Changing chassis association of logical " "port (%s) from (%s) to (%s)", port_binding_rec->logical_port, port_binding_rec->chassis->name, chassis_rec->name); } sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec); } } /* Checks and updates logical port to vtep logical switch bindings for each * physical switch in VTEP. */ void binding_run(struct controller_vtep_ctx *ctx) { if (!ctx->ovnsb_idl_txn) { return; } /* 'ls_to_db' * * Maps vtep logical switch name to the datapath binding entry. This is * used to guarantee that each vtep logical switch is only included * in only one ovn datapath (ovn logical switch). See check_db_conflict() * for details. * * 'ls_to_pb' * * Maps vtep logical switch name to the port binding entry. This is used * to guarantee that each vtep logical switch on a vtep physical switch * is only bound to one logical port. See check_pb_conflict() for * details. * */ struct shash ls_to_db = SHASH_INITIALIZER(&ls_to_db); /* Stores the 'chassis' and the 'ls_to_pb' map related to * a vtep physcial switch. */ struct ps { const struct sbrec_chassis *chassis_rec; struct shash ls_to_pb; }; struct shash ps_map = SHASH_INITIALIZER(&ps_map); const struct vteprec_physical_switch *pswitch; VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { const struct sbrec_chassis *chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); struct ps *ps = xmalloc(sizeof *ps); size_t i; /* 'chassis_rec' must exist. */ ovs_assert(chassis_rec); ps->chassis_rec = chassis_rec; shash_init(&ps->ls_to_pb); for (i = 0; i < chassis_rec->n_vtep_logical_switches; i++) { shash_add(&ps->ls_to_pb, chassis_rec->vtep_logical_switches[i], NULL); } shash_add(&ps_map, chassis_rec->name, ps); } ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: updating bindings"); const struct sbrec_port_binding *port_binding_rec; /* Port binding for vtep gateway chassis must have type "vtep", * and matched physical switch name and logical switch name. */ SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) { const char *type = port_binding_rec->type; const char *vtep_pswitch = smap_get(&port_binding_rec->options, "vtep-physical-switch"); const char *vtep_lswitch = smap_get(&port_binding_rec->options, "vtep-logical-switch"); struct ps *ps = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) : NULL; bool found_ls = ps && vtep_lswitch && shash_find(&ps->ls_to_pb, vtep_lswitch); if (!strcmp(type, "vtep") && found_ls) { bool pb_conflict, db_conflict; pb_conflict = check_pb_conflict(&ps->ls_to_pb, port_binding_rec, ps->chassis_rec->name, vtep_lswitch); db_conflict = check_db_conflict(&ls_to_db, port_binding_rec, ps->chassis_rec->name, vtep_lswitch); /* Updates port binding's chassis column when there * is no conflict. */ if (!pb_conflict && !db_conflict) { update_pb_chassis(port_binding_rec, ps->chassis_rec); } } else if (port_binding_rec->chassis && shash_find(&ps_map, port_binding_rec->chassis->name)) { /* Resets 'port_binding_rec' since it is no longer bound to * any vtep logical switch. */ update_pb_chassis(port_binding_rec, NULL); } } struct shash_node *iter, *next; SHASH_FOR_EACH_SAFE (iter, next, &ps_map) { struct ps *ps = iter->data; struct shash_node *node; SHASH_FOR_EACH (node, &ps->ls_to_pb) { if (!node->data) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_DBG_RL(&rl, "No port binding entry for logical switch (%s)" "on vtep gateway chassis (%s)", node->name, ps->chassis_rec->name); } } shash_delete(&ps_map, iter); shash_destroy(&ps->ls_to_pb); free(ps); } shash_destroy(&ls_to_db); shash_destroy(&ps_map); } /* Removes all port binding association with vtep gateway chassis. * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'), * otherwise returns false. */ bool binding_cleanup(struct controller_vtep_ctx *ctx) { if (!ctx->ovnsb_idl_txn) { return false; } struct shash ch_to_pb = SHASH_INITIALIZER(&ch_to_pb); const struct sbrec_port_binding *port_binding_rec; bool all_done = true; /* Hashs all port binding entries using the associated chassis name. */ SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) { if (port_binding_rec->chassis) { shash_add(&ch_to_pb, port_binding_rec->chassis->name, port_binding_rec); } } ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: removing bindings"); const struct vteprec_physical_switch *pswitch; VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { const struct sbrec_chassis *chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); if (!chassis_rec) { continue; } for (;;) { port_binding_rec = shash_find_and_delete(&ch_to_pb, chassis_rec->name); if (!port_binding_rec) { break; } all_done = false; update_pb_chassis(port_binding_rec, NULL); } } shash_destroy(&ch_to_pb); return all_done; } openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/ovn-controller-vtep.h0000644000000000000000000000013013534540071023713 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.109855844 openvswitch-2.5.9/ovn/controller-vtep/ovn-controller-vtep.h0000644000175000017500000000257113534540071025410 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_CONTROLLER_VTEP_H #define OVN_CONTROLLER_VTEP_H 1 #include "ovn/lib/ovn-sb-idl.h" struct ovsdb_idl; struct ovsdb_idl_txn; struct controller_vtep_ctx { struct ovsdb_idl *ovnsb_idl; struct ovsdb_idl_txn *ovnsb_idl_txn; struct ovsdb_idl *vtep_idl; struct ovsdb_idl_txn *vtep_idl_txn; }; /* VTEP needs what VTEP needs. */ #define OVN_SB_ENCAP_TYPE "vxlan" #define VTEP_ENCAP_TYPE "vxlan_over_ipv4" static inline const struct sbrec_chassis * get_chassis_by_name(struct ovsdb_idl *ovnsb_idl, const char *chassis_id) { const struct sbrec_chassis *chassis_rec; SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) { if (!strcmp(chassis_rec->name, chassis_id)) { break; } } return chassis_rec; } #endif /* ovn/ovn-controller-vtep.h */ openvswitch-2.5.9/ovn/controller-vtep/PaxHeaders.82075/gateway.h0000644000000000000000000000013013534540071021415 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.105855814 openvswitch-2.5.9/ovn/controller-vtep/gateway.h0000644000175000017500000000150713534540071023110 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_GATEWAY_H #define OVN_GATEWAY_H 1 #include struct controller_vtep_ctx; void gateway_run(struct controller_vtep_ctx *); bool gateway_cleanup(struct controller_vtep_ctx *); #endif /* ovn/controller-gw/gateway.h */ openvswitch-2.5.9/ovn/PaxHeaders.82075/lib0000644000000000000000000000013213534540121015133 xustar0030 mtime=1567801425.073855578 30 atime=1567801425.625859648 30 ctime=1567801425.073855578 openvswitch-2.5.9/ovn/lib/0000755000175000017500000000000013534540121016676 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/logical-fields.h0000644000000000000000000000013113534540071020242 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801425.073855578 openvswitch-2.5.9/ovn/lib/logical-fields.h0000644000175000017500000000260413534540071021733 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_LOGICAL_FIELDS_H #define OVN_LOGICAL_FIELDS_H 1 #include "meta-flow.h" /* Logical fields. * * These values are documented in ovn-architecture(7), please update the * documentation if you change any of them. */ #define MFF_LOG_DATAPATH MFF_METADATA /* Logical datapath (64 bits). */ #define MFF_LOG_CT_ZONE MFF_REG5 /* Logical conntrack zone (32 bits). */ #define MFF_LOG_INPORT MFF_REG6 /* Logical input port (32 bits). */ #define MFF_LOG_OUTPORT MFF_REG7 /* Logical output port (32 bits). */ /* Logical registers. * * Make sure these don't overlap with the logical fields! */ #define MFF_LOG_REGS \ MFF_LOG_REG(MFF_REG0) \ MFF_LOG_REG(MFF_REG1) \ MFF_LOG_REG(MFF_REG2) \ MFF_LOG_REG(MFF_REG3) \ MFF_LOG_REG(MFF_REG4) #endif /* ovn/lib/logical-fields.h */ openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/actions.h0000644000000000000000000000013113534540071017024 xustar0030 mtime=1567801401.709683432 30 atime=1567801402.117686428 29 ctime=1567801425.06585552 openvswitch-2.5.9/ovn/lib/actions.h0000644000175000017500000000304413534540071020514 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_ACTIONS_H #define OVN_ACTIONS_H 1 #include #include "compiler.h" struct expr; struct lexer; struct ofpbuf; struct shash; struct simap; char *actions_parse(struct lexer *, const struct shash *symtab, const struct simap *ports, const struct simap *ct_zones, uint8_t first_ptable, uint8_t n_tables, uint8_t cur_ltable, uint8_t output_ptable, struct ofpbuf *ofpacts, struct expr **prereqsp) OVS_WARN_UNUSED_RESULT; char *actions_parse_string(const char *s, const struct shash *symtab, const struct simap *ports, const struct simap *ct_zones, uint8_t first_ptable, uint8_t n_tables, uint8_t cur_ltable, uint8_t output_ptable, struct ofpbuf *ofpacts, struct expr **prereqsp) OVS_WARN_UNUSED_RESULT; #endif /* ovn/actions.h */ openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/ovn-sb-idl.ann0000644000000000000000000000013113534540071017663 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801424.509851421 openvswitch-2.5.9/ovn/lib/ovn-sb-idl.ann0000644000175000017500000000054213534540071021353 0ustar00jpettitjpettit00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates vswitch.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "sbrec_" s["idlHeader"] = "\"ovn/lib/ovn-sb-idl.h\"" openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/lex.c0000644000000000000000000000013113534540071016147 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801425.069855549 openvswitch-2.5.9/ovn/lib/lex.c0000644000175000017500000005041613534540071017644 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "lex.h" #include #include #include #include "dynamic-string.h" #include "json.h" #include "util.h" /* Returns a string that represents 'format'. */ const char * lex_format_to_string(enum lex_format format) { switch (format) { case LEX_F_DECIMAL: return "decimal"; case LEX_F_HEXADECIMAL: return "hexadecimal"; case LEX_F_IPV4: return "IPv4"; case LEX_F_IPV6: return "IPv6"; case LEX_F_ETHERNET: return "Ethernet"; default: abort(); } } /* Initializes 'token'. */ void lex_token_init(struct lex_token *token) { token->type = LEX_T_END; token->s = NULL; } /* Frees memory owned by 'token'. */ void lex_token_destroy(struct lex_token *token) { free(token->s); } /* Exchanges 'a' and 'b'. */ void lex_token_swap(struct lex_token *a, struct lex_token *b) { struct lex_token tmp = *a; *a = *b; *b = tmp; } /* lex_token_format(). */ static size_t lex_token_n_zeros(enum lex_format format) { switch (format) { case LEX_F_DECIMAL: return offsetof(union mf_subvalue, integer); case LEX_F_HEXADECIMAL: return 0; case LEX_F_IPV4: return offsetof(union mf_subvalue, ipv4); case LEX_F_IPV6: return offsetof(union mf_subvalue, ipv6); case LEX_F_ETHERNET: return offsetof(union mf_subvalue, mac); default: OVS_NOT_REACHED(); } } /* Returns the effective format for 'token', that is, the format in which it * should actually be printed. This is ordinarily the same as 'token->format', * but it's always possible that someone sets up a token with a format that * won't work for a value, e.g. 'token->value' is wider than 32 bits but the * format is LEX_F_IPV4. (The lexer itself won't do that; this is an attempt * to avoid confusion in the future.) */ static enum lex_format lex_token_get_format(const struct lex_token *token) { size_t n_zeros = lex_token_n_zeros(token->format); return (is_all_zeros(&token->value, n_zeros) && (token->type != LEX_T_MASKED_INTEGER || is_all_zeros(&token->mask, n_zeros)) ? token->format : LEX_F_HEXADECIMAL); } static void lex_token_format_value(const union mf_subvalue *value, enum lex_format format, struct ds *s) { switch (format) { case LEX_F_DECIMAL: ds_put_format(s, "%"PRIu64, ntohll(value->integer)); break; case LEX_F_HEXADECIMAL: mf_format_subvalue(value, s); break; case LEX_F_IPV4: ds_put_format(s, IP_FMT, IP_ARGS(value->ipv4)); break; case LEX_F_IPV6: ipv6_format_addr(&value->ipv6, s); break; case LEX_F_ETHERNET: ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(value->mac)); break; default: OVS_NOT_REACHED(); } } static void lex_token_format_masked_integer(const struct lex_token *token, struct ds *s) { enum lex_format format = lex_token_get_format(token); lex_token_format_value(&token->value, format, s); ds_put_char(s, '/'); const union mf_subvalue *mask = &token->mask; if (format == LEX_F_IPV4 && ip_is_cidr(mask->ipv4)) { ds_put_format(s, "%d", ip_count_cidr_bits(mask->ipv4)); } else if (token->format == LEX_F_IPV6 && ipv6_is_cidr(&mask->ipv6)) { ds_put_format(s, "%d", ipv6_count_cidr_bits(&mask->ipv6)); } else { lex_token_format_value(&token->mask, format, s); } } /* Appends a string representation of 'token' to 's', in a format that can be * losslessly parsed back by the lexer. (LEX_T_END and LEX_T_ERROR can't be * parsed back.) */ void lex_token_format(const struct lex_token *token, struct ds *s) { switch (token->type) { case LEX_T_END: ds_put_cstr(s, "$"); break; case LEX_T_ID: ds_put_cstr(s, token->s); break; case LEX_T_ERROR: ds_put_cstr(s, "error("); json_string_escape(token->s, s); ds_put_char(s, ')'); break; case LEX_T_STRING: json_string_escape(token->s, s); break; case LEX_T_INTEGER: lex_token_format_value(&token->value, lex_token_get_format(token), s); break; case LEX_T_MASKED_INTEGER: lex_token_format_masked_integer(token, s); break; case LEX_T_LPAREN: ds_put_cstr(s, "("); break; case LEX_T_RPAREN: ds_put_cstr(s, ")"); break; case LEX_T_LCURLY: ds_put_cstr(s, "{"); break; case LEX_T_RCURLY: ds_put_cstr(s, "}"); break; case LEX_T_LSQUARE: ds_put_cstr(s, "["); break; case LEX_T_RSQUARE: ds_put_cstr(s, "]"); break; case LEX_T_EQ: ds_put_cstr(s, "=="); break; case LEX_T_NE: ds_put_cstr(s, "!="); break; case LEX_T_LT: ds_put_cstr(s, "<"); break; case LEX_T_LE: ds_put_cstr(s, "<="); break; case LEX_T_GT: ds_put_cstr(s, ">"); break; case LEX_T_GE: ds_put_cstr(s, ">="); break; case LEX_T_LOG_NOT: ds_put_cstr(s, "!"); break; case LEX_T_LOG_AND: ds_put_cstr(s, "&&"); break; case LEX_T_LOG_OR: ds_put_cstr(s, "||"); break; case LEX_T_ELLIPSIS: ds_put_cstr(s, ".."); break; case LEX_T_COMMA: ds_put_cstr(s, ","); break; case LEX_T_SEMICOLON: ds_put_cstr(s, ";"); break; case LEX_T_EQUALS: ds_put_cstr(s, "="); break; case LEX_T_EXCHANGE: ds_put_cstr(s, "<->"); break; case LEX_T_DECREMENT: ds_put_cstr(s, "--"); break; default: OVS_NOT_REACHED(); } } /* lex_token_parse(). */ static void OVS_PRINTF_FORMAT(2, 3) lex_error(struct lex_token *token, const char *message, ...) { ovs_assert(!token->s); token->type = LEX_T_ERROR; va_list args; va_start(args, message); token->s = xvasprintf(message, args); va_end(args); } static void lex_parse_hex_integer(const char *start, size_t len, struct lex_token *token) { const char *in = start + (len - 1); uint8_t *out = token->value.u8 + (sizeof token->value.u8 - 1); for (int i = 0; i < len; i++) { int hexit = hexit_value(in[-i]); if (hexit < 0) { lex_error(token, "Invalid syntax in hexadecimal constant."); return; } else if (hexit) { /* Check within loop to ignore any number of leading zeros. */ if (i / 2 >= sizeof token->value.u8) { lex_error(token, "Hexadecimal constant requires more than " "%"PRIuSIZE" bits.", 8 * sizeof token->value.u8); return; } out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit; } } token->format = LEX_F_HEXADECIMAL; } static const char * lex_parse_integer__(const char *p, struct lex_token *token) { lex_token_init(token); token->type = LEX_T_INTEGER; memset(&token->value, 0, sizeof token->value); const char *start = p; const char *end = start; while (isalnum((unsigned char) *end) || *end == ':' || (*end == '.' && end[1] != '.')) { end++; } size_t len = end - start; int n; struct eth_addr mac; if (!len) { lex_error(token, "Integer constant expected."); } else if (len == 17 && ovs_scan(start, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(mac), &n) && n == len) { token->value.mac = mac; token->format = LEX_F_ETHERNET; } else if (start + strspn(start, "0123456789") == end) { if (p[0] == '0' && len > 1) { lex_error(token, "Decimal constants must not have leading zeros."); } else { unsigned long long int integer; char *tail; errno = 0; integer = strtoull(p, &tail, 10); if (tail != end || errno == ERANGE) { lex_error(token, "Decimal constants must be less than 2**64."); } else { token->value.integer = htonll(integer); token->format = LEX_F_DECIMAL; } } } else if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { if (len > 2) { lex_parse_hex_integer(start + 2, len - 2, token); } else { lex_error(token, "Hex digits expected following 0%c.", p[1]); } } else if (len < INET6_ADDRSTRLEN) { char copy[INET6_ADDRSTRLEN]; memcpy(copy, p, len); copy[len] = '\0'; struct in_addr ipv4; struct in6_addr ipv6; if (inet_pton(AF_INET, copy, &ipv4) == 1) { token->value.ipv4 = ipv4.s_addr; token->format = LEX_F_IPV4; } else if (inet_pton(AF_INET6, copy, &ipv6) == 1) { token->value.ipv6 = ipv6; token->format = LEX_F_IPV6; } else { lex_error(token, "Invalid numeric constant."); } } else { lex_error(token, "Invalid numeric constant."); } ovs_assert(token->type == LEX_T_INTEGER || token->type == LEX_T_ERROR); return end; } static const char * lex_parse_mask(const char *p, struct lex_token *token) { struct lex_token mask; /* Parse just past the '/' as a second integer. Handle errors. */ p = lex_parse_integer__(p + 1, &mask); if (mask.type == LEX_T_ERROR) { lex_token_swap(&mask, token); lex_token_destroy(&mask); return p; } ovs_assert(mask.type == LEX_T_INTEGER); /* Now convert the value and mask into a masked integer token. * We have a few special cases. */ token->type = LEX_T_MASKED_INTEGER; memset(&token->mask, 0, sizeof token->mask); uint32_t prefix_bits = ntohll(mask.value.integer); if (token->format == mask.format) { /* Same format value and mask is always OK. */ token->mask = mask.value; } else if (token->format == LEX_F_IPV4 && mask.format == LEX_F_DECIMAL && prefix_bits <= 32) { /* IPv4 address with decimal mask is a CIDR prefix. */ token->mask.integer = htonll(ntohl(be32_prefix_mask(prefix_bits))); } else if (token->format == LEX_F_IPV6 && mask.format == LEX_F_DECIMAL && prefix_bits <= 128) { /* IPv6 address with decimal mask is a CIDR prefix. */ token->mask.ipv6 = ipv6_create_mask(prefix_bits); } else if (token->format == LEX_F_DECIMAL && mask.format == LEX_F_HEXADECIMAL && token->value.integer == 0) { /* Special case for e.g. 0/0x1234. */ token->format = LEX_F_HEXADECIMAL; token->mask = mask.value; } else { lex_error(token, "Value and mask have incompatible formats."); return p; } /* Check invariant that a 1-bit in the value corresponds to a 1-bit in the * mask. */ for (int i = 0; i < ARRAY_SIZE(token->mask.be32); i++) { ovs_be32 v = token->value.be32[i]; ovs_be32 m = token->mask.be32[i]; if (v & ~m) { lex_error(token, "Value contains unmasked 1-bits."); break; } } /* Done! */ lex_token_destroy(&mask); return p; } static const char * lex_parse_integer(const char *p, struct lex_token *token) { p = lex_parse_integer__(p, token); if (token->type == LEX_T_INTEGER && *p == '/') { p = lex_parse_mask(p, token); } return p; } static const char * lex_parse_string(const char *p, struct lex_token *token) { const char *start = ++p; for (;;) { switch (*p) { case '\0': lex_error(token, "Input ends inside quoted string."); return p; case '"': token->type = (json_string_unescape(start, p - start, &token->s) ? LEX_T_STRING : LEX_T_ERROR); return p + 1; case '\\': p++; if (*p) { p++; } break; default: p++; break; } } } static bool lex_is_id1(unsigned char c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '.'); } static bool lex_is_idn(unsigned char c) { return lex_is_id1(c) || (c >= '0' && c <= '9'); } static const char * lex_parse_id(const char *p, struct lex_token *token) { const char *start = p; do { p++; } while (lex_is_idn(*p)); token->type = LEX_T_ID; token->s = xmemdup0(start, p - start); return p; } /* Initializes 'token' and parses the first token from the beginning of * null-terminated string 'p' into 'token'. Stores a pointer to the start of * the token (after skipping white space and comments, if any) into '*startp'. * Returns the character position at which to begin parsing the next token. */ const char * lex_token_parse(struct lex_token *token, const char *p, const char **startp) { lex_token_init(token); next: *startp = p; switch (*p) { case '\0': token->type = LEX_T_END; return p; case ' ': case '\t': case '\n': case '\r': p++; goto next; case '/': p++; if (*p == '/') { do { p++; } while (*p != '\0' && *p != '\n'); goto next; } else if (*p == '*') { p++; for (;;) { if (*p == '*' && p[1] == '/') { p += 2; goto next; } else if (*p == '\0' || *p == '\n') { lex_error(token, "`/*' without matching `*/'."); return p; } else { p++; } } goto next; } else { lex_error(token, "`/' is only valid as part of `//' or `/*'."); } break; case '(': token->type = LEX_T_LPAREN; p++; break; case ')': token->type = LEX_T_RPAREN; p++; break; case '{': token->type = LEX_T_LCURLY; p++; break; case '}': token->type = LEX_T_RCURLY; p++; break; case '[': token->type = LEX_T_LSQUARE; p++; break; case ']': token->type = LEX_T_RSQUARE; p++; break; case '=': p++; if (*p == '=') { token->type = LEX_T_EQ; p++; } else { token->type = LEX_T_EQUALS; } break; case '!': p++; if (*p == '=') { token->type = LEX_T_NE; p++; } else { token->type = LEX_T_LOG_NOT; } break; case '&': p++; if (*p == '&') { token->type = LEX_T_LOG_AND; p++; } else { lex_error(token, "`&' is only valid as part of `&&'."); } break; case '|': p++; if (*p == '|') { token->type = LEX_T_LOG_OR; p++; } else { lex_error(token, "`|' is only valid as part of `||'."); } break; case '<': p++; if (*p == '=') { token->type = LEX_T_LE; p++; } else if (*p == '-' && p[1] == '>') { token->type = LEX_T_EXCHANGE; p += 2; } else { token->type = LEX_T_LT; } break; case '>': p++; if (*p == '=') { token->type = LEX_T_GE; p++; } else { token->type = LEX_T_GT; } break; case '.': p++; if (*p == '.') { token->type = LEX_T_ELLIPSIS; p++; } else { lex_error(token, "`.' is only valid as part of `..' or a number."); } break; case ',': p++; token->type = LEX_T_COMMA; break; case ';': p++; token->type = LEX_T_SEMICOLON; break; case '-': p++; if (*p == '-') { token->type = LEX_T_DECREMENT; p++; } else { lex_error(token, "`-' is only valid as part of `--'."); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case ':': p = lex_parse_integer(p, token); break; case '"': p = lex_parse_string(p, token); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': /* We need to distinguish an Ethernet address or IPv6 address from an * identifier. Fortunately, Ethernet addresses and IPv6 addresses that * are ambiguous based on the first character, always start with hex * digits followed by a colon, but identifiers never do. */ p = (p[strspn(p, "0123456789abcdefABCDEF")] == ':' ? lex_parse_integer(p, token) : lex_parse_id(p, token)); break; default: if (lex_is_id1(*p)) { p = lex_parse_id(p, token); } else { if (isprint((unsigned char) *p)) { lex_error(token, "Invalid character `%c' in input.", *p); } else { lex_error(token, "Invalid byte 0x%d in input.", *p); } p++; } break; } return p; } /* Initializes 'lexer' for parsing 'input'. * * While the lexer is in use, 'input' must remain available, but the caller * otherwise retains ownership of 'input'. * * The caller must call lexer_get() to obtain the first token. */ void lexer_init(struct lexer *lexer, const char *input) { lexer->input = input; lexer->start = NULL; lex_token_init(&lexer->token); } /* Frees storage associated with 'lexer'. */ void lexer_destroy(struct lexer *lexer) { lex_token_destroy(&lexer->token); } /* Obtains the next token from 'lexer' into 'lexer->token', and returns the * token's type. The caller may examine 'lexer->token' directly to obtain full * information about the token. */ enum lex_type lexer_get(struct lexer *lexer) { lex_token_destroy(&lexer->token); lexer->input = lex_token_parse(&lexer->token, lexer->input, &lexer->start); return lexer->token.type; } /* Returns the type of the next token that will be fetched by lexer_get(), * without advancing 'lexer->token' to that token. */ enum lex_type lexer_lookahead(const struct lexer *lexer) { struct lex_token next; enum lex_type type; const char *start; lex_token_parse(&next, lexer->input, &start); type = next.type; lex_token_destroy(&next); return type; } /* If 'lexer''s current token has the given 'type', advances 'lexer' to the * next token and returns true. Otherwise returns false. */ bool lexer_match(struct lexer *lexer, enum lex_type type) { if (lexer->token.type == type) { lexer_get(lexer); return true; } else { return false; } } /* If 'lexer''s current token is the identifier given in 'id', advances 'lexer' * to the next token and returns true. Otherwise returns false. */ bool lexer_match_id(struct lexer *lexer, const char *id) { if (lexer->token.type == LEX_T_ID && !strcmp(lexer->token.s, id)) { lexer_get(lexer); return true; } else { return false; } } bool lexer_is_int(const struct lexer *lexer) { return (lexer->token.type == LEX_T_INTEGER && lexer->token.format == LEX_F_DECIMAL && ntohll(lexer->token.value.integer) <= INT_MAX); } bool lexer_get_int(struct lexer *lexer, int *value) { if (lexer_is_int(lexer)) { *value = ntohll(lexer->token.value.integer); lexer_get(lexer); return true; } else { *value = 0; return false; } } openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/libovn.sym.in0000644000000000000000000000013113534540071017643 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801424.645852423 openvswitch-2.5.9/ovn/lib/libovn.sym.in0000644000175000017500000000005413534540071021331 0ustar00jpettitjpettit00000000000000libovn_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017353 xustar0030 mtime=1567801401.709683432 30 atime=1567801402.117686428 30 ctime=1567801424.613852188 openvswitch-2.5.9/ovn/lib/automake.mk0000644000175000017500000000233413534540071021043 0ustar00jpettitjpettit00000000000000lib_LTLIBRARIES += ovn/lib/libovn.la ovn_lib_libovn_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ovn/lib/libovn.sym \ $(AM_LDFLAGS) ovn_lib_libovn_la_SOURCES = \ ovn/lib/actions.c \ ovn/lib/actions.h \ ovn/lib/expr.c \ ovn/lib/expr.h \ ovn/lib/lex.c \ ovn/lib/lex.h \ ovn/lib/logical-fields.h nodist_ovn_lib_libovn_la_SOURCES = \ ovn/lib/ovn-nb-idl.c \ ovn/lib/ovn-nb-idl.h \ ovn/lib/ovn-sb-idl.c \ ovn/lib/ovn-sb-idl.h # ovn-sb IDL OVSIDL_BUILT += \ ovn/lib/ovn-sb-idl.c \ ovn/lib/ovn-sb-idl.h \ ovn/lib/ovn-sb-idl.ovsidl EXTRA_DIST += ovn/lib/ovn-sb-idl.ann OVN_SB_IDL_FILES = \ $(srcdir)/ovn/ovn-sb.ovsschema \ $(srcdir)/ovn/lib/ovn-sb-idl.ann ovn/lib/ovn-sb-idl.ovsidl: $(OVN_SB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_SB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ # ovn-nb IDL OVSIDL_BUILT += \ ovn/lib/ovn-nb-idl.c \ ovn/lib/ovn-nb-idl.h \ ovn/lib/ovn-nb-idl.ovsidl EXTRA_DIST += ovn/lib/ovn-nb-idl.ann OVN_NB_IDL_FILES = \ $(srcdir)/ovn/ovn-nb.ovsschema \ $(srcdir)/ovn/lib/ovn-nb-idl.ann ovn/lib/ovn-nb-idl.ovsidl: $(OVN_NB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_NB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/actions.c0000644000000000000000000000013113534540071017017 xustar0030 mtime=1567801401.709683432 30 atime=1567801402.117686428 29 ctime=1567801425.06585552 openvswitch-2.5.9/ovn/lib/actions.c0000644000175000017500000003016113534540071020507 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "actions.h" #include #include #include "compiler.h" #include "dynamic-string.h" #include "expr.h" #include "lex.h" #include "logical-fields.h" #include "ofp-actions.h" #include "ofpbuf.h" #include "simap.h" /* Context maintained during actions_parse(). */ struct action_context { /* Input. */ struct lexer *lexer; /* Lexer for pulling more tokens. */ const struct simap *ports; /* Map from port name to number. */ const struct shash *symtab; /* Symbol table. */ /* OVN maps each logical flow table (ltable), one-to-one, onto a physical * OpenFlow flow table (ptable). These members describe the mapping and * data related to flow tables. */ uint8_t n_tables; /* Number of flow tables. */ uint8_t first_ptable; /* First OpenFlow table. */ uint8_t cur_ltable; /* 0 <= cur_ltable < n_tables. */ uint8_t output_ptable; /* OpenFlow table for 'output' to resubmit. */ const struct simap *ct_zones; /* Map from port name to conntrack zone. */ /* State. */ char *error; /* Error, if any, otherwise NULL. */ /* Output. */ struct ofpbuf *ofpacts; /* Actions. */ struct expr *prereqs; /* Prerequisites to apply to match. */ }; static bool action_error_handle_common(struct action_context *ctx) { if (ctx->error) { /* Already have an error, suppress this one since the cascade seems * unlikely to be useful. */ return true; } else if (ctx->lexer->token.type == LEX_T_ERROR) { /* The lexer signaled an error. Nothing at the action level * accepts an error token, so we'll inevitably end up here with some * meaningless parse error. Report the lexical error instead. */ ctx->error = xstrdup(ctx->lexer->token.s); return true; } else { return false; } } static void OVS_PRINTF_FORMAT(2, 3) action_error(struct action_context *ctx, const char *message, ...) { if (action_error_handle_common(ctx)) { return; } va_list args; va_start(args, message); ctx->error = xvasprintf(message, args); va_end(args); } static void OVS_PRINTF_FORMAT(2, 3) action_syntax_error(struct action_context *ctx, const char *message, ...) { if (action_error_handle_common(ctx)) { return; } struct ds s; ds_init(&s); ds_put_cstr(&s, "Syntax error"); if (ctx->lexer->token.type == LEX_T_END) { ds_put_cstr(&s, " at end of input"); } else if (ctx->lexer->start) { ds_put_format(&s, " at `%.*s'", (int) (ctx->lexer->input - ctx->lexer->start), ctx->lexer->start); } if (message) { ds_put_char(&s, ' '); va_list args; va_start(args, message); ds_put_format_valist(&s, message, args); va_end(args); } ds_put_char(&s, '.'); ctx->error = ds_steal_cstr(&s); } /* Parses an assignment or exchange action. */ static void parse_set_action(struct action_context *ctx) { struct expr *prereqs; char *error; error = expr_parse_assignment(ctx->lexer, ctx->symtab, ctx->ports, ctx->ofpacts, &prereqs); if (error) { action_error(ctx, "%s", error); free(error); return; } ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs); } static void emit_resubmit(struct action_context *ctx, uint8_t table_id) { struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ctx->ofpacts); resubmit->in_port = OFPP_IN_PORT; resubmit->table_id = table_id; } static bool action_get_int(struct action_context *ctx, int *value) { bool ok = lexer_get_int(ctx->lexer, value); if (!ok) { action_syntax_error(ctx, "expecting small integer"); } return ok; } static void parse_next_action(struct action_context *ctx) { if (!ctx->n_tables) { action_error(ctx, "\"next\" action not allowed here."); } else if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { int ltable; if (!action_get_int(ctx, <able)) { return; } if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { action_syntax_error(ctx, "expecting `)'"); return; } if (ltable >= ctx->n_tables) { action_error(ctx, "\"next\" argument must be in range 0 to %d.", ctx->n_tables - 1); return; } emit_resubmit(ctx, ctx->first_ptable + ltable); } else { if (ctx->cur_ltable < ctx->n_tables) { emit_resubmit(ctx, ctx->first_ptable + ctx->cur_ltable + 1); } else { action_error(ctx, "\"next\" action not allowed in last table."); } } } static void emit_ct(struct action_context *ctx, bool recirc_next, bool commit) { struct ofpact_conntrack *ct = ofpact_put_CT(ctx->ofpacts); ct->flags |= commit ? NX_CT_F_COMMIT : 0; /* If "recirc" is set, we automatically go to the next table. */ if (recirc_next) { if (ctx->cur_ltable < ctx->n_tables) { ct->recirc_table = ctx->first_ptable + ctx->cur_ltable + 1; } else { action_error(ctx, "\"ct_next\" action not allowed in last table."); return; } } else { ct->recirc_table = NX_CT_RECIRC_NONE; } ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE); ct->zone_src.ofs = 0; ct->zone_src.n_bits = 16; /* We do not support ALGs yet. */ ct->alg = 0; /* CT only works with IP, so set up a prerequisite. */ struct expr *expr; char *error; expr = expr_parse_string("ip", ctx->symtab, &error); ovs_assert(!error); ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr); } static void parse_actions(struct action_context *ctx) { /* "drop;" by itself is a valid (empty) set of actions, but it can't be * combined with other actions because that doesn't make sense. */ if (ctx->lexer->token.type == LEX_T_ID && !strcmp(ctx->lexer->token.s, "drop") && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) { lexer_get(ctx->lexer); /* Skip "drop". */ lexer_get(ctx->lexer); /* Skip ";". */ if (ctx->lexer->token.type != LEX_T_END) { action_syntax_error(ctx, "expecting end of input"); } return; } while (ctx->lexer->token.type != LEX_T_END) { if (ctx->lexer->token.type != LEX_T_ID) { action_syntax_error(ctx, NULL); break; } enum lex_type lookahead = lexer_lookahead(ctx->lexer); if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE || lookahead == LEX_T_LSQUARE) { parse_set_action(ctx); } else if (lexer_match_id(ctx->lexer, "next")) { parse_next_action(ctx); } else if (lexer_match_id(ctx->lexer, "output")) { emit_resubmit(ctx, ctx->output_ptable); } else if (lexer_match_id(ctx->lexer, "ip.ttl")) { if (lexer_match(ctx->lexer, LEX_T_DECREMENT)) { struct expr *e = expr_parse_string("ip", ctx->symtab, &ctx->error); ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, e); ofpact_put_DEC_TTL(ctx->ofpacts); } else { action_syntax_error(ctx, "expecting `--'"); } } else if (lexer_match_id(ctx->lexer, "ct_next")) { emit_ct(ctx, true, false); } else if (lexer_match_id(ctx->lexer, "ct_commit")) { emit_ct(ctx, false, true); } else { action_syntax_error(ctx, "expecting action"); } if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) { action_syntax_error(ctx, "expecting ';'"); } if (ctx->error) { return; } } } /* Parses OVN actions, in the format described for the "actions" column in the * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the * actions to 'ofpacts' as "struct ofpact"s. * * 'symtab' provides a table of "struct expr_symbol"s to support (as one would * provide to expr_parse()). * * 'ports' must be a map from strings (presumably names of ports) to integers * (as one would provide to expr_to_matches()). Strings used in the actions * that are not in 'ports' are translated to zero. * * 'ct_zones' provides a map from a port name to its connection tracking zone. * * OVN maps each logical flow table (ltable), one-to-one, onto a physical * OpenFlow flow table (ptable). A number of parameters describe this mapping * and data related to flow tables: * * - 'first_ptable' and 'n_tables' define the range of OpenFlow tables to * which the logical "next" action should be able to jump. Logical table * 0 maps to OpenFlow table 'first_ptable', logical table 1 to * 'first_ptable + 1', and so on. If 'n_tables' is 0 then "next" is * disallowed entirely. * * - 'cur_ltable' is an offset from 'first_ptable' (e.g. 0 <= cur_ltable < * n_ptables) of the logical flow that contains the actions. If * cur_ltable + 1 < n_tables, then this defines the default table that * "next" will jump to. * * 'next_table_id' should be the OpenFlow table to which the "next" action will * resubmit, or 0 to disable "next". * * - 'output_ptable' should be the OpenFlow table to which the logical * "output" action will resubmit * * Some actions add extra requirements (prerequisites) to the flow's match. If * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise, * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must * eventually free it. * * Returns NULL on success, otherwise a malloc()'d error message that the * caller must free. On failure, 'ofpacts' has the same contents and * '*prereqsp' is set to NULL, but some tokens may have been consumed from * 'lexer'. */ char * OVS_WARN_UNUSED_RESULT actions_parse(struct lexer *lexer, const struct shash *symtab, const struct simap *ports, const struct simap *ct_zones, uint8_t first_ptable, uint8_t n_tables, uint8_t cur_ltable, uint8_t output_ptable, struct ofpbuf *ofpacts, struct expr **prereqsp) { size_t ofpacts_start = ofpacts->size; struct action_context ctx; ctx.lexer = lexer; ctx.symtab = symtab; ctx.ports = ports; ctx.ct_zones = ct_zones; ctx.first_ptable = first_ptable; ctx.n_tables = n_tables; ctx.cur_ltable = cur_ltable; ctx.output_ptable = output_ptable; ctx.error = NULL; ctx.ofpacts = ofpacts; ctx.prereqs = NULL; parse_actions(&ctx); if (!ctx.error) { *prereqsp = ctx.prereqs; return NULL; } else { ofpacts->size = ofpacts_start; expr_destroy(ctx.prereqs); *prereqsp = NULL; return ctx.error; } } /* Like actions_parse(), but the actions are taken from 's'. */ char * OVS_WARN_UNUSED_RESULT actions_parse_string(const char *s, const struct shash *symtab, const struct simap *ports, const struct simap *ct_zones, uint8_t first_table, uint8_t n_tables, uint8_t cur_table, uint8_t output_table, struct ofpbuf *ofpacts, struct expr **prereqsp) { struct lexer lexer; char *error; lexer_init(&lexer, s); lexer_get(&lexer); error = actions_parse(&lexer, symtab, ports, ct_zones, first_table, n_tables, cur_table, output_table, ofpacts, prereqsp); lexer_destroy(&lexer); return error; } openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/ovn-nb-idl.ann0000644000000000000000000000013113534540071017656 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801424.509851421 openvswitch-2.5.9/ovn/lib/ovn-nb-idl.ann0000644000175000017500000000054213534540071021346 0ustar00jpettitjpettit00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates vswitch.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "nbrec_" s["idlHeader"] = "\"ovn/lib/ovn-nb-idl.h\"" openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/expr.c0000644000000000000000000000013013534540071016334 xustar0029 mtime=1567801401.71768349 30 atime=1567801402.117686428 29 ctime=1567801425.06585552 openvswitch-2.5.9/ovn/lib/expr.c0000644000175000017500000025047713534540071020043 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "expr.h" #include "dynamic-string.h" #include "json.h" #include "lex.h" #include "logical-fields.h" #include "match.h" #include "ofp-actions.h" #include "shash.h" #include "simap.h" #include "sset.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(expr); /* Returns the name of measurement level 'level'. */ const char * expr_level_to_string(enum expr_level level) { switch (level) { case EXPR_L_NOMINAL: return "nominal"; case EXPR_L_BOOLEAN: return "Boolean"; case EXPR_L_ORDINAL: return "ordinal"; default: OVS_NOT_REACHED(); } } /* Relational operators. */ /* Returns a string form of relational operator 'relop'. */ const char * expr_relop_to_string(enum expr_relop relop) { switch (relop) { case EXPR_R_EQ: return "=="; case EXPR_R_NE: return "!="; case EXPR_R_LT: return "<"; case EXPR_R_LE: return "<="; case EXPR_R_GT: return ">"; case EXPR_R_GE: return ">="; default: OVS_NOT_REACHED(); } } bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop) { enum expr_relop r; switch ((int) type) { case LEX_T_EQ: r = EXPR_R_EQ; break; case LEX_T_NE: r = EXPR_R_NE; break; case LEX_T_LT: r = EXPR_R_LT; break; case LEX_T_LE: r = EXPR_R_LE; break; case LEX_T_GT: r = EXPR_R_GT; break; case LEX_T_GE: r = EXPR_R_GE; break; default: return false; } if (relop) { *relop = r; } return true; } /* Returns the relational operator that 'relop' becomes if you turn the * relation's operands around, e.g. EXPR_R_EQ does not change because "a == b" * and "b == a" are equivalent, but EXPR_R_LE becomes EXPR_R_GE because "a <= * b" is equivalent to "b >= a". */ static enum expr_relop expr_relop_turn(enum expr_relop relop) { switch (relop) { case EXPR_R_EQ: return EXPR_R_EQ; case EXPR_R_NE: return EXPR_R_NE; case EXPR_R_LT: return EXPR_R_GT; case EXPR_R_LE: return EXPR_R_GE; case EXPR_R_GT: return EXPR_R_LT; case EXPR_R_GE: return EXPR_R_LE; default: OVS_NOT_REACHED(); } } /* Returns the relational operator that is the opposite of 'relop'. */ static enum expr_relop expr_relop_invert(enum expr_relop relop) { switch (relop) { case EXPR_R_EQ: return EXPR_R_NE; case EXPR_R_NE: return EXPR_R_EQ; case EXPR_R_LT: return EXPR_R_GE; case EXPR_R_LE: return EXPR_R_GT; case EXPR_R_GT: return EXPR_R_LE; case EXPR_R_GE: return EXPR_R_LT; default: OVS_NOT_REACHED(); } } /* Constructing and manipulating expressions. */ /* Creates and returns a logical AND or OR expression (according to 'type', * which must be EXPR_T_AND or EXPR_T_OR) that initially has no * sub-expressions. (To satisfy the invariants for expressions, the caller * must add at least two sub-expressions whose types are different from * 'type'.) */ struct expr * expr_create_andor(enum expr_type type) { struct expr *e = xmalloc(sizeof *e); e->type = type; list_init(&e->andor); return e; } /* Returns a logical AND or OR expression (according to 'type', which must be * EXPR_T_AND or EXPR_T_OR) whose sub-expressions are 'a' and 'b', with some * flexibility: * * - If 'a' or 'b' is NULL, just returns the other one (which means that if * that other one is not of the given 'type', then the returned * expression is not either). * * - If 'a' or 'b', or both, have type 'type', then they are combined into * a single node that satisfies the invariants for expressions. */ struct expr * expr_combine(enum expr_type type, struct expr *a, struct expr *b) { if (!a) { return b; } else if (!b) { return a; } else if (a->type == type) { if (b->type == type) { list_splice(&a->andor, b->andor.next, &b->andor); free(b); } else { list_push_back(&a->andor, &b->node); } return a; } else if (b->type == type) { list_push_front(&b->andor, &a->node); return b; } else { struct expr *e = expr_create_andor(type); list_push_back(&e->andor, &a->node); list_push_back(&e->andor, &b->node); return e; } } static void expr_insert_andor(struct expr *andor, struct expr *before, struct expr *new) { if (new->type == andor->type) { if (andor->type == EXPR_T_AND) { /* Conjunction junction, what's your function? */ } list_splice(&before->node, new->andor.next, &new->andor); free(new); } else { list_insert(&before->node, &new->node); } } /* Returns an EXPR_T_BOOLEAN expression with value 'b'. */ struct expr * expr_create_boolean(bool b) { struct expr *e = xmalloc(sizeof *e); e->type = EXPR_T_BOOLEAN; e->boolean = b; return e; } static void expr_not(struct expr *expr) { struct expr *sub; switch (expr->type) { case EXPR_T_CMP: expr->cmp.relop = expr_relop_invert(expr->cmp.relop); break; case EXPR_T_AND: case EXPR_T_OR: LIST_FOR_EACH (sub, node, &expr->andor) { expr_not(sub); } expr->type = expr->type == EXPR_T_AND ? EXPR_T_OR : EXPR_T_AND; break; case EXPR_T_BOOLEAN: expr->boolean = !expr->boolean; break; default: OVS_NOT_REACHED(); } } static struct expr * expr_fix_andor(struct expr *expr, bool short_circuit) { struct expr *sub, *next; LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { if (sub->type == EXPR_T_BOOLEAN) { if (sub->boolean == short_circuit) { expr_destroy(expr); return expr_create_boolean(short_circuit); } else { list_remove(&sub->node); expr_destroy(sub); } } } if (list_is_short(&expr->andor)) { if (list_is_empty(&expr->andor)) { free(expr); return expr_create_boolean(!short_circuit); } else { sub = expr_from_node(list_front(&expr->andor)); free(expr); return sub; } } else { return expr; } } /* Returns 'expr' modified so that top-level oddities are fixed up: * * - Eliminates any EXPR_T_BOOLEAN operands at the top level. * * - Replaces one-operand EXPR_T_AND or EXPR_T_OR by its subexpression. */ static struct expr * expr_fix(struct expr *expr) { switch (expr->type) { case EXPR_T_CMP: return expr; case EXPR_T_AND: return expr_fix_andor(expr, false); case EXPR_T_OR: return expr_fix_andor(expr, true); case EXPR_T_BOOLEAN: return expr; default: OVS_NOT_REACHED(); } } /* Formatting. */ static void find_bitwise_range(const union mf_subvalue *sv, int width, int *startp, int *n_bitsp) { unsigned int start = bitwise_scan(sv, sizeof *sv, true, 0, width); if (start < width) { unsigned int end = bitwise_scan(sv, sizeof *sv, false, start, width); if (end >= width || bitwise_scan(sv, sizeof *sv, true, end, width) >= width) { *startp = start; *n_bitsp = end - start; return; } } *startp = *n_bitsp = 0; } static void expr_format_cmp(const struct expr *e, struct ds *s) { /* The common case is numerical comparisons. * Handle string comparisons as a special case. */ if (!e->cmp.symbol->width) { ds_put_format(s, "%s %s ", e->cmp.symbol->name, expr_relop_to_string(e->cmp.relop)); json_string_escape(e->cmp.string, s); return; } int ofs, n; find_bitwise_range(&e->cmp.mask, e->cmp.symbol->width, &ofs, &n); if (n == 1 && (e->cmp.relop == EXPR_R_EQ || e->cmp.relop == EXPR_R_NE)) { bool positive; positive = bitwise_get_bit(&e->cmp.value, sizeof e->cmp.value, ofs); positive ^= e->cmp.relop == EXPR_R_NE; if (!positive) { ds_put_char(s, '!'); } ds_put_cstr(s, e->cmp.symbol->name); if (e->cmp.symbol->width > 1) { ds_put_format(s, "[%d]", ofs); } return; } ds_put_cstr(s, e->cmp.symbol->name); if (n > 0 && n < e->cmp.symbol->width) { if (n > 1) { ds_put_format(s, "[%d..%d]", ofs, ofs + n - 1); } else { ds_put_format(s, "[%d]", ofs); } } ds_put_format(s, " %s ", expr_relop_to_string(e->cmp.relop)); if (n) { union mf_subvalue value; memset(&value, 0, sizeof value); bitwise_copy(&e->cmp.value, sizeof e->cmp.value, ofs, &value, sizeof value, 0, n); mf_format_subvalue(&value, s); } else { mf_format_subvalue(&e->cmp.value, s); ds_put_char(s, '/'); mf_format_subvalue(&e->cmp.mask, s); } } static void expr_format_andor(const struct expr *e, const char *op, struct ds *s) { struct expr *sub; int i = 0; LIST_FOR_EACH (sub, node, &e->andor) { if (i++) { ds_put_format(s, " %s ", op); } if (sub->type == EXPR_T_AND || sub->type == EXPR_T_OR) { ds_put_char(s, '('); expr_format(sub, s); ds_put_char(s, ')'); } else { expr_format(sub, s); } } } /* Appends a string form of 'e' to 's'. The string form is acceptable for * parsing back into an equivalent expression. */ void expr_format(const struct expr *e, struct ds *s) { switch (e->type) { case EXPR_T_CMP: expr_format_cmp(e, s); break; case EXPR_T_AND: expr_format_andor(e, "&&", s); break; case EXPR_T_OR: expr_format_andor(e, "||", s); break; case EXPR_T_BOOLEAN: ds_put_char(s, e->boolean ? '1' : '0'); break; } } /* Prints a string form of 'e' on stdout, followed by a new-line. */ void expr_print(const struct expr *e) { struct ds output; ds_init(&output); expr_format(e, &output); puts(ds_cstr(&output)); ds_destroy(&output); } /* Parsing. */ /* Type of a "union expr_constant" or "struct expr_constant_set". */ enum expr_constant_type { EXPR_C_INTEGER, EXPR_C_STRING }; /* A string or integer constant (one must know which from context). */ union expr_constant { /* Integer constant. * * The width of a constant isn't always clear, e.g. if you write "1", * there's no way to tell whether you mean for that to be a 1-bit constant * or a 128-bit constant or somewhere in between. */ struct { union mf_subvalue value; union mf_subvalue mask; /* Only initialized if 'masked'. */ bool masked; enum lex_format format; /* From the constant's lex_token. */ }; /* Null-terminated string constant. */ char *string; }; /* A collection of "union expr_constant"s of the same type. */ struct expr_constant_set { union expr_constant *values; /* Constants. */ size_t n_values; /* Number of constants. */ enum expr_constant_type type; /* Type of the constants. */ bool in_curlies; /* Whether the constants were in {}. */ }; /* A reference to a symbol or a subfield of a symbol. * * For string fields, ofs and n_bits are 0. */ struct expr_field { const struct expr_symbol *symbol; /* The symbol. */ int ofs; /* Starting bit offset. */ int n_bits; /* Number of bits. */ }; /* Context maintained during expr_parse(). */ struct expr_context { struct lexer *lexer; /* Lexer for pulling more tokens. */ const struct shash *symtab; /* Symbol table. */ char *error; /* Error, if any, otherwise NULL. */ bool not; /* True inside odd number of NOT operators. */ }; struct expr *expr_parse__(struct expr_context *); static void expr_not(struct expr *); static void expr_constant_set_destroy(struct expr_constant_set *); static bool parse_field(struct expr_context *, struct expr_field *); static bool expr_error_handle_common(struct expr_context *ctx) { if (ctx->error) { /* Already have an error, suppress this one since the cascade seems * unlikely to be useful. */ return true; } else if (ctx->lexer->token.type == LEX_T_ERROR) { /* The lexer signaled an error. Nothing at the expression level * accepts an error token, so we'll inevitably end up here with some * meaningless parse error. Report the lexical error instead. */ ctx->error = xstrdup(ctx->lexer->token.s); return true; } else { return false; } } static void OVS_PRINTF_FORMAT(2, 3) expr_error(struct expr_context *ctx, const char *message, ...) { if (expr_error_handle_common(ctx)) { return; } va_list args; va_start(args, message); ctx->error = xvasprintf(message, args); va_end(args); } static void OVS_PRINTF_FORMAT(2, 3) expr_syntax_error(struct expr_context *ctx, const char *message, ...) { if (expr_error_handle_common(ctx)) { return; } struct ds s; ds_init(&s); ds_put_cstr(&s, "Syntax error "); if (ctx->lexer->token.type == LEX_T_END) { ds_put_cstr(&s, "at end of input "); } else if (ctx->lexer->start) { ds_put_format(&s, "at `%.*s' ", (int) (ctx->lexer->input - ctx->lexer->start), ctx->lexer->start); } va_list args; va_start(args, message); ds_put_format_valist(&s, message, args); va_end(args); ctx->error = ds_steal_cstr(&s); } static struct expr * make_cmp__(const struct expr_field *f, enum expr_relop r, const union expr_constant *c) { struct expr *e = xzalloc(sizeof *e); e->type = EXPR_T_CMP; e->cmp.symbol = f->symbol; e->cmp.relop = r; if (f->symbol->width) { bitwise_copy(&c->value, sizeof c->value, 0, &e->cmp.value, sizeof e->cmp.value, f->ofs, f->n_bits); if (c->masked) { bitwise_copy(&c->mask, sizeof c->mask, 0, &e->cmp.mask, sizeof e->cmp.mask, f->ofs, f->n_bits); } else { bitwise_one(&e->cmp.mask, sizeof e->cmp.mask, f->ofs, f->n_bits); } } else { e->cmp.string = xstrdup(c->string); } return e; } /* Returns the minimum reasonable width for integer constant 'c'. */ static int expr_constant_width(const union expr_constant *c) { if (c->masked) { return mf_subvalue_width(&c->mask); } switch (c->format) { case LEX_F_DECIMAL: case LEX_F_HEXADECIMAL: return mf_subvalue_width(&c->value); case LEX_F_IPV4: return 32; case LEX_F_IPV6: return 128; case LEX_F_ETHERNET: return 48; default: OVS_NOT_REACHED(); } } static bool type_check(struct expr_context *ctx, const struct expr_field *f, struct expr_constant_set *cs) { if (cs->type != (f->symbol->width ? EXPR_C_INTEGER : EXPR_C_STRING)) { expr_error(ctx, "%s field %s is not compatible with %s constant.", f->symbol->width ? "Integer" : "String", f->symbol->name, cs->type == EXPR_C_INTEGER ? "integer" : "string"); return false; } if (f->symbol->width) { for (size_t i = 0; i < cs->n_values; i++) { int w = expr_constant_width(&cs->values[i]); if (w > f->symbol->width) { expr_error(ctx, "%d-bit constant is not compatible with " "%d-bit field %s.", w, f->symbol->width, f->symbol->name); return false; } } } return true; } static struct expr * make_cmp(struct expr_context *ctx, const struct expr_field *f, enum expr_relop r, struct expr_constant_set *cs) { struct expr *e = NULL; if (!type_check(ctx, f, cs)) { goto exit; } if (r != EXPR_R_EQ && r != EXPR_R_NE) { if (cs->in_curlies) { expr_error(ctx, "Only == and != operators may be used " "with value sets."); goto exit; } if (f->symbol->level == EXPR_L_NOMINAL || f->symbol->level == EXPR_L_BOOLEAN) { expr_error(ctx, "Only == and != operators may be used " "with %s field %s.", expr_level_to_string(f->symbol->level), f->symbol->name); goto exit; } if (cs->values[0].masked) { expr_error(ctx, "Only == and != operators may be used with " "masked constants. Consider using subfields instead " "(e.g. eth.src[0..15] > 0x1111 in place of " "eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff)."); goto exit; } } if (f->symbol->level == EXPR_L_NOMINAL) { if (f->symbol->expansion) { ovs_assert(f->symbol->width > 0); for (size_t i = 0; i < cs->n_values; i++) { const union mf_subvalue *value = &cs->values[i].value; bool positive = (value->integer & htonll(1)) != 0; positive ^= r == EXPR_R_NE; positive ^= ctx->not; if (!positive) { const char *name = f->symbol->name; expr_error(ctx, "Nominal predicate %s may only be tested " "positively, e.g. `%s' or `%s == 1' but not " "`!%s' or `%s == 0'.", name, name, name, name, name); goto exit; } } } else if (r != (ctx->not ? EXPR_R_NE : EXPR_R_EQ)) { expr_error(ctx, "Nominal field %s may only be tested for " "equality (taking enclosing `!' operators into " "account).", f->symbol->name); goto exit; } } e = make_cmp__(f, r, &cs->values[0]); for (size_t i = 1; i < cs->n_values; i++) { e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND, e, make_cmp__(f, r, &cs->values[i])); } exit: expr_constant_set_destroy(cs); return e; } static bool expr_get_int(struct expr_context *ctx, int *value) { bool ok = lexer_get_int(ctx->lexer, value); if (!ok) { expr_syntax_error(ctx, "expecting small integer."); } return ok; } static bool parse_field(struct expr_context *ctx, struct expr_field *f) { const struct expr_symbol *symbol; if (ctx->lexer->token.type != LEX_T_ID) { expr_syntax_error(ctx, "expecting field name."); return false; } symbol = shash_find_data(ctx->symtab, ctx->lexer->token.s); if (!symbol) { expr_syntax_error(ctx, "expecting field name."); return false; } lexer_get(ctx->lexer); f->symbol = symbol; if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) { int low, high; if (!symbol->width) { expr_error(ctx, "Cannot select subfield of string field %s.", symbol->name); return false; } if (!expr_get_int(ctx, &low)) { return false; } if (lexer_match(ctx->lexer, LEX_T_ELLIPSIS)) { if (!expr_get_int(ctx, &high)) { return false; } } else { high = low; } if (!lexer_match(ctx->lexer, LEX_T_RSQUARE)) { expr_syntax_error(ctx, "expecting `]'."); return false; } if (low > high) { expr_error(ctx, "Invalid bit range %d to %d.", low, high); return false; } else if (high >= symbol->width) { expr_error(ctx, "Cannot select bits %d to %d of %d-bit field %s.", low, high, symbol->width, symbol->name); return false; } else if (symbol->level == EXPR_L_NOMINAL && (low != 0 || high != symbol->width - 1)) { expr_error(ctx, "Cannot select subfield of nominal field %s.", symbol->name); return false; } f->ofs = low; f->n_bits = high - low + 1; } else { f->ofs = 0; f->n_bits = symbol->width; } return true; } static bool parse_relop(struct expr_context *ctx, enum expr_relop *relop) { if (expr_relop_from_token(ctx->lexer->token.type, relop)) { lexer_get(ctx->lexer); return true; } else { expr_syntax_error(ctx, "expecting relational operator."); return false; } } static bool assign_constant_set_type(struct expr_context *ctx, struct expr_constant_set *cs, enum expr_constant_type type) { if (!cs->n_values || cs->type == type) { cs->type = type; return true; } else { expr_syntax_error(ctx, "expecting %s.", cs->type == EXPR_C_INTEGER ? "integer" : "string"); return false; } } static bool parse_constant(struct expr_context *ctx, struct expr_constant_set *cs, size_t *allocated_values) { if (cs->n_values >= *allocated_values) { cs->values = x2nrealloc(cs->values, allocated_values, sizeof *cs->values); } if (ctx->lexer->token.type == LEX_T_STRING) { if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) { return false; } cs->values[cs->n_values++].string = xstrdup(ctx->lexer->token.s); lexer_get(ctx->lexer); return true; } else if (ctx->lexer->token.type == LEX_T_INTEGER || ctx->lexer->token.type == LEX_T_MASKED_INTEGER) { if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) { return false; } union expr_constant *c = &cs->values[cs->n_values++]; c->value = ctx->lexer->token.value; c->format = ctx->lexer->token.format; c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER; if (c->masked) { c->mask = ctx->lexer->token.mask; } lexer_get(ctx->lexer); return true; } else { expr_syntax_error(ctx, "expecting constant."); return false; } } /* Parses a single or {}-enclosed set of integer or string constants into 'cs', * which the caller need not have initialized. Returns true on success, in * which case the caller owns 'cs', false on failure, in which case 'cs' is * indeterminate. */ static bool parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs) { size_t allocated_values = 0; bool ok; memset(cs, 0, sizeof *cs); if (lexer_match(ctx->lexer, LEX_T_LCURLY)) { ok = true; cs->in_curlies = true; do { if (!parse_constant(ctx, cs, &allocated_values)) { ok = false; break; } lexer_match(ctx->lexer, LEX_T_COMMA); } while (!lexer_match(ctx->lexer, LEX_T_RCURLY)); } else { ok = parse_constant(ctx, cs, &allocated_values); } if (!ok) { expr_constant_set_destroy(cs); } return ok; } static void expr_constant_set_destroy(struct expr_constant_set *cs) { if (cs) { if (cs->type == EXPR_C_STRING) { for (size_t i = 0; i < cs->n_values; i++) { free(cs->values[i].string); } } free(cs->values); } } static struct expr * expr_parse_primary(struct expr_context *ctx, bool *atomic) { *atomic = false; if (lexer_match(ctx->lexer, LEX_T_LPAREN)) { struct expr *e = expr_parse__(ctx); if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) { expr_destroy(e); expr_syntax_error(ctx, "expecting `)'."); return NULL; } *atomic = true; return e; } if (ctx->lexer->token.type == LEX_T_ID) { struct expr_field f; enum expr_relop r; struct expr_constant_set c; if (!parse_field(ctx, &f)) { return NULL; } if (!expr_relop_from_token(ctx->lexer->token.type, &r)) { if (f.n_bits > 1 && !ctx->not) { expr_error(ctx, "Explicit `!= 0' is required for inequality " "test of multibit field against 0."); return NULL; } *atomic = true; union expr_constant *cst = xzalloc(sizeof *cst); cst->format = LEX_F_HEXADECIMAL; cst->masked = false; c.type = EXPR_C_INTEGER; c.values = cst; c.n_values = 1; c.in_curlies = false; return make_cmp(ctx, &f, EXPR_R_NE, &c); } else if (parse_relop(ctx, &r) && parse_constant_set(ctx, &c)) { return make_cmp(ctx, &f, r, &c); } else { return NULL; } } else { struct expr_constant_set c1; if (!parse_constant_set(ctx, &c1)) { return NULL; } if (!expr_relop_from_token(ctx->lexer->token.type, NULL) && c1.n_values == 1 && c1.type == EXPR_C_INTEGER && c1.values[0].format == LEX_F_DECIMAL && !c1.values[0].masked && !c1.in_curlies) { uint64_t x = ntohll(c1.values[0].value.integer); if (x <= 1) { *atomic = true; expr_constant_set_destroy(&c1); return expr_create_boolean(x); } } enum expr_relop r1; struct expr_field f; if (!parse_relop(ctx, &r1) || !parse_field(ctx, &f)) { expr_constant_set_destroy(&c1); return NULL; } if (!expr_relop_from_token(ctx->lexer->token.type, NULL)) { return make_cmp(ctx, &f, expr_relop_turn(r1), &c1); } enum expr_relop r2; struct expr_constant_set c2; if (!parse_relop(ctx, &r2) || !parse_constant_set(ctx, &c2)) { expr_constant_set_destroy(&c1); return NULL; } else { /* Reject "1 == field == 2", "1 < field > 2", and so on. */ if (!(((r1 == EXPR_R_LT || r1 == EXPR_R_LE) && (r2 == EXPR_R_LT || r2 == EXPR_R_LE)) || ((r1 == EXPR_R_GT || r1 == EXPR_R_GE) && (r2 == EXPR_R_GT || r2 == EXPR_R_GE)))) { expr_error(ctx, "Range expressions must have the form " "`x < field < y' or `x > field > y', with each " "`<' optionally replaced by `<=' or `>' by `>=')."); expr_constant_set_destroy(&c1); expr_constant_set_destroy(&c2); return NULL; } struct expr *e1 = make_cmp(ctx, &f, expr_relop_turn(r1), &c1); struct expr *e2 = make_cmp(ctx, &f, r2, &c2); if (ctx->error) { expr_destroy(e1); expr_destroy(e2); return NULL; } return expr_combine(EXPR_T_AND, e1, e2); } } } static struct expr * expr_parse_not(struct expr_context *ctx) { bool atomic; if (lexer_match(ctx->lexer, LEX_T_LOG_NOT)) { ctx->not = !ctx->not; struct expr *expr = expr_parse_primary(ctx, &atomic); ctx->not = !ctx->not; if (expr) { if (!atomic) { expr_error(ctx, "Missing parentheses around operand of !."); expr_destroy(expr); return NULL; } expr_not(expr); } return expr; } else { return expr_parse_primary(ctx, &atomic); } } struct expr * expr_parse__(struct expr_context *ctx) { struct expr *e = expr_parse_not(ctx); if (!e) { return NULL; } enum lex_type lex_type = ctx->lexer->token.type; if (lex_type == LEX_T_LOG_AND || lex_type == LEX_T_LOG_OR) { enum expr_type expr_type = lex_type == LEX_T_LOG_AND ? EXPR_T_AND : EXPR_T_OR; lexer_get(ctx->lexer); do { struct expr *e2 = expr_parse_not(ctx); if (!e2) { expr_destroy(e); return NULL; } e = expr_combine(expr_type, e, e2); } while (lexer_match(ctx->lexer, lex_type)); if (ctx->lexer->token.type == LEX_T_LOG_AND || ctx->lexer->token.type == LEX_T_LOG_OR) { expr_destroy(e); expr_error(ctx, "&& and || must be parenthesized when used together."); return NULL; } } return e; } /* Parses an expression using the symbols in 'symtab' from 'lexer'. If * successful, returns the new expression and sets '*errorp' to NULL. On * failure, returns NULL and sets '*errorp' to an explanatory error message. * The caller must eventually free the returned expression (with * expr_destroy()) or error (with free()). */ struct expr * expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp) { struct expr_context ctx; ctx.lexer = lexer; ctx.symtab = symtab; ctx.error = NULL; ctx.not = false; struct expr *e = expr_parse__(&ctx); *errorp = ctx.error; ovs_assert((ctx.error != NULL) != (e != NULL)); return e; } /* Like expr_parse(), but the expression is taken from 's'. */ struct expr * expr_parse_string(const char *s, const struct shash *symtab, char **errorp) { struct lexer lexer; struct expr *expr; lexer_init(&lexer, s); lexer_get(&lexer); expr = expr_parse(&lexer, symtab, errorp); if (!*errorp && lexer.token.type != LEX_T_END) { *errorp = xstrdup("Extra tokens at end of input."); expr_destroy(expr); expr = NULL; } lexer_destroy(&lexer); return expr; } static struct expr_symbol * add_symbol(struct shash *symtab, const char *name, int width, const char *prereqs, enum expr_level level, bool must_crossproduct) { struct expr_symbol *symbol = xzalloc(sizeof *symbol); symbol->name = xstrdup(name); symbol->prereqs = prereqs && prereqs[0] ? xstrdup(prereqs) : NULL; symbol->width = width; symbol->level = level; symbol->must_crossproduct = must_crossproduct; shash_add_assert(symtab, symbol->name, symbol); return symbol; } /* Adds field 'id' to symbol table 'symtab' under the given 'name'. Whenever * 'name' is referenced, expression annotation (see expr_annotate()) will * ensure that 'prereqs' are also true. If 'must_crossproduct' is true, then * conversion to flows will never attempt to use the field as a conjunctive * match dimension (see "Crossproducting" in the large comment on struct * expr_symbol in expr.h for an example). * * A given field 'id' must only be used for a single symbol in a symbol table. * Use subfields to duplicate or subset a field (you can even make a subfield * include all the bits of the "parent" field if you like). */ struct expr_symbol * expr_symtab_add_field(struct shash *symtab, const char *name, enum mf_field_id id, const char *prereqs, bool must_crossproduct) { const struct mf_field *field = mf_from_id(id); struct expr_symbol *symbol; symbol = add_symbol(symtab, name, field->n_bits, prereqs, (field->maskable == MFM_FULLY ? EXPR_L_ORDINAL : EXPR_L_NOMINAL), must_crossproduct); symbol->field = field; return symbol; } static bool parse_field_from_string(const char *s, const struct shash *symtab, struct expr_field *field, char **errorp) { struct lexer lexer; lexer_init(&lexer, s); lexer_get(&lexer); struct expr_context ctx; ctx.lexer = &lexer; ctx.symtab = symtab; ctx.error = NULL; ctx.not = false; bool ok = parse_field(&ctx, field); if (!ok) { *errorp = ctx.error; } else if (lexer.token.type != LEX_T_END) { *errorp = xstrdup("Extra tokens at end of input."); ok = false; } lexer_destroy(&lexer); return ok; } /* Adds 'name' as a subfield of a larger field in 'symtab'. Whenever * 'name' is referenced, expression annotation (see expr_annotate()) will * ensure that 'prereqs' are also true. * * 'subfield' must describe the subfield as a string, e.g. "vlan.tci[0..11]" * for the low 12 bits of a larger field named "vlan.tci". */ struct expr_symbol * expr_symtab_add_subfield(struct shash *symtab, const char *name, const char *prereqs, const char *subfield) { struct expr_symbol *symbol; struct expr_field f; char *error; if (!parse_field_from_string(subfield, symtab, &f, &error)) { VLOG_WARN("%s: error parsing %s subfield (%s)", subfield, name, error); free(error); return NULL; } enum expr_level level = f.symbol->level; if (level != EXPR_L_ORDINAL) { VLOG_WARN("can't define %s as subfield of %s field %s", name, expr_level_to_string(level), f.symbol->name); } symbol = add_symbol(symtab, name, f.n_bits, prereqs, level, false); symbol->expansion = xstrdup(subfield); return symbol; } /* Adds a string-valued symbol named 'name' to 'symtab' with the specified * 'prereqs'. */ struct expr_symbol * expr_symtab_add_string(struct shash *symtab, const char *name, enum mf_field_id id, const char *prereqs) { const struct mf_field *field = mf_from_id(id); struct expr_symbol *symbol; symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false); symbol->field = field; return symbol; } static enum expr_level expr_get_level(const struct expr *expr) { const struct expr *sub; enum expr_level level = EXPR_L_ORDINAL; switch (expr->type) { case EXPR_T_CMP: return (expr->cmp.symbol->level == EXPR_L_NOMINAL ? EXPR_L_NOMINAL : EXPR_L_BOOLEAN); case EXPR_T_AND: case EXPR_T_OR: LIST_FOR_EACH (sub, node, &expr->andor) { enum expr_level sub_level = expr_get_level(sub); level = MIN(level, sub_level); } return level; case EXPR_T_BOOLEAN: return EXPR_L_BOOLEAN; default: OVS_NOT_REACHED(); } } static enum expr_level expr_parse_level(const char *s, const struct shash *symtab, char **errorp) { struct expr *expr = expr_parse_string(s, symtab, errorp); enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL; expr_destroy(expr); return level; } /* Adds a predicate symbol, whose value is the given Boolean 'expression', * named 'name' to 'symtab'. For example, "ip4 && ip4.proto == 6" might be an * appropriate predicate named "tcp4". */ struct expr_symbol * expr_symtab_add_predicate(struct shash *symtab, const char *name, const char *expansion) { struct expr_symbol *symbol; enum expr_level level; char *error; level = expr_parse_level(expansion, symtab, &error); if (error) { VLOG_WARN("%s: error parsing %s expansion (%s)", expansion, name, error); free(error); return NULL; } symbol = add_symbol(symtab, name, 1, NULL, level, false); symbol->expansion = xstrdup(expansion); return symbol; } /* Destroys 'symtab' and all of its symbols. */ void expr_symtab_destroy(struct shash *symtab) { struct shash_node *node, *next; SHASH_FOR_EACH_SAFE (node, next, symtab) { struct expr_symbol *symbol = node->data; shash_delete(symtab, node); free(symbol->name); free(symbol->prereqs); free(symbol->expansion); free(symbol); } } /* Cloning. */ static struct expr * expr_clone_cmp(struct expr *expr) { struct expr *new = xmemdup(expr, sizeof *expr); if (!new->cmp.symbol->width) { new->cmp.string = xstrdup(new->cmp.string); } return new; } static struct expr * expr_clone_andor(struct expr *expr) { struct expr *new = expr_create_andor(expr->type); struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { struct expr *new_sub = expr_clone(sub); list_push_back(&new->andor, &new_sub->node); } return new; } /* Returns a clone of 'expr'. This is a "deep copy": neither the returned * expression nor any of its substructure will be shared with 'expr'. */ struct expr * expr_clone(struct expr *expr) { switch (expr->type) { case EXPR_T_CMP: return expr_clone_cmp(expr); case EXPR_T_AND: case EXPR_T_OR: return expr_clone_andor(expr); case EXPR_T_BOOLEAN: return expr_create_boolean(expr->boolean); } OVS_NOT_REACHED(); } /* Destroys 'expr' and all of the sub-expressions it references. */ void expr_destroy(struct expr *expr) { if (!expr) { return; } struct expr *sub, *next; switch (expr->type) { case EXPR_T_CMP: if (!expr->cmp.symbol->width) { free(expr->cmp.string); } break; case EXPR_T_AND: case EXPR_T_OR: LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); expr_destroy(sub); } break; case EXPR_T_BOOLEAN: break; } free(expr); } /* Annotation. */ /* An element in a linked list of symbols. * * Used to detect when a symbol is being expanded recursively, to allow * flagging an error. */ struct annotation_nesting { struct ovs_list node; const struct expr_symbol *symbol; }; struct expr *expr_annotate__(struct expr *, const struct shash *symtab, struct ovs_list *nesting, char **errorp); static struct expr * parse_and_annotate(const char *s, const struct shash *symtab, struct ovs_list *nesting, char **errorp) { char *error; struct expr *expr; expr = expr_parse_string(s, symtab, &error); if (expr) { expr = expr_annotate__(expr, symtab, nesting, &error); } if (expr) { *errorp = NULL; } else { *errorp = xasprintf("Error parsing expression `%s' encountered as " "prerequisite or predicate of initial expression: " "%s", s, error); free(error); } return expr; } static struct expr * expr_annotate_cmp(struct expr *expr, const struct shash *symtab, struct ovs_list *nesting, char **errorp) { const struct expr_symbol *symbol = expr->cmp.symbol; const struct annotation_nesting *iter; LIST_FOR_EACH (iter, node, nesting) { if (iter->symbol == symbol) { *errorp = xasprintf("Recursive expansion of symbol `%s'.", symbol->name); expr_destroy(expr); return NULL; } } struct annotation_nesting an; an.symbol = symbol; list_push_back(nesting, &an.node); struct expr *prereqs = NULL; if (symbol->prereqs) { prereqs = parse_and_annotate(symbol->prereqs, symtab, nesting, errorp); if (!prereqs) { goto error; } } if (symbol->expansion) { if (symbol->level == EXPR_L_ORDINAL) { struct expr_field field; if (!parse_field_from_string(symbol->expansion, symtab, &field, errorp)) { goto error; } expr->cmp.symbol = field.symbol; mf_subvalue_shift(&expr->cmp.value, field.ofs); mf_subvalue_shift(&expr->cmp.mask, field.ofs); } else { struct expr *expansion; expansion = parse_and_annotate(symbol->expansion, symtab, nesting, errorp); if (!expansion) { goto error; } bool positive = (expr->cmp.value.integer & htonll(1)) != 0; positive ^= expr->cmp.relop == EXPR_R_NE; if (!positive) { expr_not(expansion); } expr_destroy(expr); expr = expansion; } } list_remove(&an.node); return prereqs ? expr_combine(EXPR_T_AND, expr, prereqs) : expr; error: expr_destroy(expr); expr_destroy(prereqs); list_remove(&an.node); return NULL; } struct expr * expr_annotate__(struct expr *expr, const struct shash *symtab, struct ovs_list *nesting, char **errorp) { switch (expr->type) { case EXPR_T_CMP: return expr_annotate_cmp(expr, symtab, nesting, errorp); case EXPR_T_AND: case EXPR_T_OR: { struct expr *sub, *next; LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); struct expr *new_sub = expr_annotate__(sub, symtab, nesting, errorp); if (!new_sub) { expr_destroy(expr); return NULL; } expr_insert_andor(expr, next, new_sub); } *errorp = NULL; return expr; } case EXPR_T_BOOLEAN: *errorp = NULL; return expr; default: OVS_NOT_REACHED(); } } /* "Annotates" 'expr', which does the following: * * - Applies prerequisites, by locating each comparison operator whose * field has a prerequisite and adding a logical AND against those * prerequisites. * * - Expands references to subfield symbols, by replacing them by * references to their underlying field symbols (suitably shifted). * * - Expands references to predicate symbols, by replacing them by the * expressions that they expand to. * * In each case, annotation occurs recursively as necessary. * * On failure, returns NULL and sets '*errorp' to an explanatory error * message, which the caller must free. */ struct expr * expr_annotate(struct expr *expr, const struct shash *symtab, char **errorp) { struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting); return expr_annotate__(expr, symtab, &nesting, errorp); } static struct expr * expr_simplify_ne(struct expr *expr) { struct expr *new = NULL; const union mf_subvalue *value = &expr->cmp.value; const union mf_subvalue *mask = &expr->cmp.mask; int w = expr->cmp.symbol->width; int i; for (i = 0; (i = bitwise_scan(mask, sizeof *mask, true, i, w)) < w; i++) { struct expr *e; e = xzalloc(sizeof *e); e->type = EXPR_T_CMP; e->cmp.symbol = expr->cmp.symbol; e->cmp.relop = EXPR_R_EQ; bitwise_put_bit(&e->cmp.value, sizeof e->cmp.value, i, !bitwise_get_bit(value, sizeof *value, i)); bitwise_put1(&e->cmp.mask, sizeof e->cmp.mask, i); new = expr_combine(EXPR_T_OR, new, e); } ovs_assert(new); expr_destroy(expr); return new; } static struct expr * expr_simplify_relational(struct expr *expr) { const union mf_subvalue *value = &expr->cmp.value; int start, n_bits, end; find_bitwise_range(&expr->cmp.mask, expr->cmp.symbol->width, &start, &n_bits); ovs_assert(n_bits > 0); end = start + n_bits; struct expr *new; if (expr->cmp.relop == EXPR_R_LE || expr->cmp.relop == EXPR_R_GE) { new = xmemdup(expr, sizeof *expr); new->cmp.relop = EXPR_R_EQ; } else { new = NULL; } bool b = expr->cmp.relop == EXPR_R_LT || expr->cmp.relop == EXPR_R_LE; for (int z = bitwise_scan(value, sizeof *value, b, start, end); z < end; z = bitwise_scan(value, sizeof *value, b, z + 1, end)) { struct expr *e; e = xmemdup(expr, sizeof *expr); e->cmp.relop = EXPR_R_EQ; bitwise_toggle_bit(&e->cmp.value, sizeof e->cmp.value, z); bitwise_zero(&e->cmp.value, sizeof e->cmp.value, start, z - start); bitwise_zero(&e->cmp.mask, sizeof e->cmp.mask, start, z - start); new = expr_combine(EXPR_T_OR, new, e); } expr_destroy(expr); return new ? new : expr_create_boolean(false); } /* Takes ownership of 'expr' and returns an equivalent expression whose * EXPR_T_CMP nodes use only tests for equality (EXPR_R_EQ). */ struct expr * expr_simplify(struct expr *expr) { struct expr *sub, *next; switch (expr->type) { case EXPR_T_CMP: return (expr->cmp.relop == EXPR_R_EQ || !expr->cmp.symbol->width ? expr : expr->cmp.relop == EXPR_R_NE ? expr_simplify_ne(expr) : expr_simplify_relational(expr)); case EXPR_T_AND: case EXPR_T_OR: LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); expr_insert_andor(expr, next, expr_simplify(sub)); } return expr_fix(expr); case EXPR_T_BOOLEAN: return expr; } OVS_NOT_REACHED(); } static const struct expr_symbol * expr_is_cmp(const struct expr *expr) { switch (expr->type) { case EXPR_T_CMP: return expr->cmp.symbol; case EXPR_T_AND: case EXPR_T_OR: { const struct expr_symbol *prev = NULL; struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { const struct expr_symbol *symbol = expr_is_cmp(sub); if (!symbol || (prev && symbol != prev)) { return NULL; } prev = symbol; } return prev; } case EXPR_T_BOOLEAN: return NULL; default: OVS_NOT_REACHED(); } } struct expr_sort { struct expr *expr; const struct expr_symbol *relop; enum expr_type type; }; static int compare_expr_sort(const void *a_, const void *b_) { const struct expr_sort *a = a_; const struct expr_sort *b = b_; if (a->type != b->type) { return a->type < b->type ? -1 : 1; } else if (a->relop) { int cmp = strcmp(a->relop->name, b->relop->name); if (cmp) { return cmp; } enum expr_type a_type = a->expr->type; enum expr_type b_type = a->expr->type; return a_type < b_type ? -1 : a_type > b_type; } else if (a->type == EXPR_T_AND || a->type == EXPR_T_OR) { size_t a_len = list_size(&a->expr->andor); size_t b_len = list_size(&b->expr->andor); return a_len < b_len ? -1 : a_len > b_len; } else { return 0; } } static struct expr *crush_cmps(struct expr *, const struct expr_symbol *); static bool disjunction_matches_string(const struct expr *or, const char *s) { const struct expr *sub; LIST_FOR_EACH (sub, node, &or->andor) { if (!strcmp(sub->cmp.string, s)) { return true; } } return false; } /* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a * string-typed 'symbol'. */ static struct expr * crush_and_string(struct expr *expr, const struct expr_symbol *symbol) { ovs_assert(!list_is_short(&expr->andor)); struct expr *singleton = NULL; /* First crush each subexpression into either a single EXPR_T_CMP or an * EXPR_T_OR with EXPR_T_CMP subexpressions. */ struct expr *sub, *next = NULL; LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); struct expr *new = crush_cmps(sub, symbol); switch (new->type) { case EXPR_T_CMP: if (!singleton) { list_insert(&next->node, &new->node); singleton = new; } else { bool match = !strcmp(new->cmp.string, singleton->cmp.string); expr_destroy(new); if (!match) { expr_destroy(expr); return expr_create_boolean(false); } } break; case EXPR_T_AND: OVS_NOT_REACHED(); case EXPR_T_OR: list_insert(&next->node, &new->node); break; case EXPR_T_BOOLEAN: if (!new->boolean) { expr_destroy(expr); return new; } free(new); break; } } /* If we have a singleton, then the result is either the singleton itself * (if the ORs allow the singleton) or false. */ if (singleton) { LIST_FOR_EACH (sub, node, &expr->andor) { if (sub->type == EXPR_T_OR && !disjunction_matches_string(sub, singleton->cmp.string)) { expr_destroy(expr); return expr_create_boolean(false); } } list_remove(&singleton->node); expr_destroy(expr); return singleton; } /* Otherwise the result is the intersection of all of the ORs. */ struct sset result = SSET_INITIALIZER(&result); LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { struct sset strings = SSET_INITIALIZER(&strings); const struct expr *s; LIST_FOR_EACH (s, node, &sub->andor) { sset_add(&strings, s->cmp.string); } if (sset_is_empty(&result)) { sset_swap(&result, &strings); } else { sset_intersect(&result, &strings); } sset_destroy(&strings); if (sset_is_empty(&result)) { expr_destroy(expr); sset_destroy(&result); return expr_create_boolean(false); } } expr_destroy(expr); expr = expr_create_andor(EXPR_T_OR); const char *string; SSET_FOR_EACH (string, &result) { sub = xmalloc(sizeof *sub); sub->type = EXPR_T_CMP; sub->cmp.symbol = symbol; sub->cmp.string = xstrdup(string); list_push_back(&expr->andor, &sub->node); } return expr_fix(expr); } /* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a * numeric-typed 'symbol'. */ static struct expr * crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol) { ovs_assert(!list_is_short(&expr->andor)); union mf_subvalue value, mask; memset(&value, 0, sizeof value); memset(&mask, 0, sizeof mask); struct expr *sub, *next = NULL; LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); struct expr *new = crush_cmps(sub, symbol); switch (new->type) { case EXPR_T_CMP: if (!mf_subvalue_intersect(&value, &mask, &new->cmp.value, &new->cmp.mask, &value, &mask)) { expr_destroy(new); expr_destroy(expr); return expr_create_boolean(false); } expr_destroy(new); break; case EXPR_T_AND: OVS_NOT_REACHED(); case EXPR_T_OR: list_insert(&next->node, &new->node); break; case EXPR_T_BOOLEAN: if (!new->boolean) { expr_destroy(expr); return new; } free(new); break; } } if (list_is_empty(&expr->andor)) { if (is_all_zeros(&mask, sizeof mask)) { expr_destroy(expr); return expr_create_boolean(true); } else { struct expr *cmp; cmp = xmalloc(sizeof *cmp); cmp->type = EXPR_T_CMP; cmp->cmp.symbol = symbol; cmp->cmp.relop = EXPR_R_EQ; cmp->cmp.value = value; cmp->cmp.mask = mask; expr_destroy(expr); return cmp; } } else if (list_is_short(&expr->andor)) { /* Transform "a && (b || c || d)" into "ab || ac || ad" where "ab" is * computed as "a && b", etc. */ struct expr *disjuncts = expr_from_node(list_pop_front(&expr->andor)); struct expr *or; or = xmalloc(sizeof *or); or->type = EXPR_T_OR; list_init(&or->andor); ovs_assert(disjuncts->type == EXPR_T_OR); LIST_FOR_EACH_SAFE (sub, next, node, &disjuncts->andor) { ovs_assert(sub->type == EXPR_T_CMP); list_remove(&sub->node); if (mf_subvalue_intersect(&value, &mask, &sub->cmp.value, &sub->cmp.mask, &sub->cmp.value, &sub->cmp.mask)) { list_push_back(&or->andor, &sub->node); } else { free(sub); } } free(disjuncts); free(expr); if (list_is_empty(&or->andor)) { free(or); return expr_create_boolean(false); } else if (list_is_short(&or->andor)) { struct expr *cmp = expr_from_node(list_pop_front(&or->andor)); free(or); return cmp; } else { return or; } } else { /* Transform "x && (a0 || a1) && (b0 || b1) && ..." into * "(xa0b0 || xa0b1 || xa1b0 || xa1b1) && ...". */ struct expr *as = expr_from_node(list_pop_front(&expr->andor)); struct expr *bs = expr_from_node(list_pop_front(&expr->andor)); struct expr *new = NULL; struct expr *or; or = xmalloc(sizeof *or); or->type = EXPR_T_OR; list_init(&or->andor); struct expr *a; LIST_FOR_EACH (a, node, &as->andor) { union mf_subvalue a_value, a_mask; ovs_assert(a->type == EXPR_T_CMP); if (!mf_subvalue_intersect(&value, &mask, &a->cmp.value, &a->cmp.mask, &a_value, &a_mask)) { continue; } struct expr *b; LIST_FOR_EACH (b, node, &bs->andor) { ovs_assert(b->type == EXPR_T_CMP); if (!new) { new = xmalloc(sizeof *new); new->type = EXPR_T_CMP; new->cmp.symbol = symbol; new->cmp.relop = EXPR_R_EQ; } if (mf_subvalue_intersect(&a_value, &a_mask, &b->cmp.value, &b->cmp.mask, &new->cmp.value, &new->cmp.mask)) { list_push_back(&or->andor, &new->node); new = NULL; } } } expr_destroy(as); expr_destroy(bs); free(new); if (list_is_empty(&or->andor)) { expr_destroy(expr); free(or); return expr_create_boolean(false); } else if (list_is_short(&or->andor)) { struct expr *cmp = expr_from_node(list_pop_front(&or->andor)); free(or); if (list_is_empty(&expr->andor)) { expr_destroy(expr); return crush_cmps(cmp, symbol); } else { return crush_cmps(expr_combine(EXPR_T_AND, cmp, expr), symbol); } } else if (!list_is_empty(&expr->andor)) { struct expr *e = expr_combine(EXPR_T_AND, or, expr); ovs_assert(!list_is_short(&e->andor)); return crush_cmps(e, symbol); } else { expr_destroy(expr); return crush_cmps(or, symbol); } } } static int compare_cmps_3way(const struct expr *a, const struct expr *b) { ovs_assert(a->cmp.symbol == b->cmp.symbol); if (!a->cmp.symbol->width) { return strcmp(a->cmp.string, b->cmp.string); } else { int d = memcmp(&a->cmp.value, &b->cmp.value, sizeof a->cmp.value); if (!d) { d = memcmp(&a->cmp.mask, &b->cmp.mask, sizeof a->cmp.mask); } return d; } } static int compare_cmps_cb(const void *a_, const void *b_) { const struct expr *const *ap = a_; const struct expr *const *bp = b_; const struct expr *a = *ap; const struct expr *b = *bp; return compare_cmps_3way(a, b); } /* Implementation of crush_cmps() for expr->type == EXPR_T_OR. */ static struct expr * crush_or(struct expr *expr, const struct expr_symbol *symbol) { struct expr *sub, *next = NULL; /* First, crush all the subexpressions. That might eliminate the * OR-expression entirely; if so, return the result. Otherwise, 'expr' * is now a disjunction of cmps over the same symbol. */ LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { list_remove(&sub->node); expr_insert_andor(expr, next, crush_cmps(sub, symbol)); } expr = expr_fix(expr); if (expr->type != EXPR_T_OR) { return expr; } /* Sort subexpressions by value and mask, to bring together duplicates. */ size_t n = list_size(&expr->andor); struct expr **subs = xmalloc(n * sizeof *subs); size_t i = 0; LIST_FOR_EACH (sub, node, &expr->andor) { subs[i++] = sub; } ovs_assert(i == n); qsort(subs, n, sizeof *subs, compare_cmps_cb); /* Eliminate duplicates. */ list_init(&expr->andor); list_push_back(&expr->andor, &subs[0]->node); for (i = 1; i < n; i++) { struct expr *a = expr_from_node(list_back(&expr->andor)); struct expr *b = subs[i]; if (compare_cmps_3way(a, b)) { list_push_back(&expr->andor, &b->node); } else { free(b); } } free(subs); return expr_fix(expr); } /* Takes ownership of 'expr', which must be a cmp in the sense determined by * 'expr_is_cmp(expr)', where 'symbol' is the symbol returned by that function. * Returns an equivalent expression owned by the caller that is a single * EXPR_T_CMP or a disjunction of them or a EXPR_T_BOOLEAN. */ static struct expr * crush_cmps(struct expr *expr, const struct expr_symbol *symbol) { switch (expr->type) { case EXPR_T_OR: return crush_or(expr, symbol); case EXPR_T_AND: return (symbol->width ? crush_and_numeric(expr, symbol) : crush_and_string(expr, symbol)); case EXPR_T_CMP: return expr; case EXPR_T_BOOLEAN: return expr; default: OVS_NOT_REACHED(); } } static struct expr * expr_sort(struct expr *expr) { size_t n = list_size(&expr->andor); struct expr_sort *subs = xmalloc(n * sizeof *subs); struct expr *sub; size_t i; i = 0; LIST_FOR_EACH (sub, node, &expr->andor) { subs[i].expr = sub; subs[i].relop = expr_is_cmp(sub); subs[i].type = subs[i].relop ? EXPR_T_CMP : sub->type; i++; } ovs_assert(i == n); qsort(subs, n, sizeof *subs, compare_expr_sort); list_init(&expr->andor); for (int i = 0; i < n; ) { if (subs[i].relop) { int j; for (j = i + 1; j < n; j++) { if (subs[i].relop != subs[j].relop) { break; } } struct expr *crushed; if (j == i + 1) { crushed = crush_cmps(subs[i].expr, subs[i].relop); } else { struct expr *combined = subs[i].expr; for (int k = i + 1; k < j; k++) { combined = expr_combine(EXPR_T_AND, combined, subs[k].expr); } ovs_assert(!list_is_short(&combined->andor)); crushed = crush_cmps(combined, subs[i].relop); } if (crushed->type == EXPR_T_BOOLEAN) { if (!crushed->boolean) { for (int k = j; k < n; k++) { expr_destroy(subs[k].expr); } expr_destroy(expr); expr = crushed; break; } else { free(crushed); } } else { expr = expr_combine(EXPR_T_AND, expr, crushed); } i = j; } else { expr = expr_combine(EXPR_T_AND, expr, subs[i++].expr); } } free(subs); return expr; } static struct expr *expr_normalize_or(struct expr *expr); /* Returns 'expr', which is an AND, reduced to OR(AND(clause)) where * a clause is a cmp or a disjunction of cmps on a single field. */ static struct expr * expr_normalize_and(struct expr *expr) { ovs_assert(expr->type == EXPR_T_AND); expr = expr_sort(expr); if (expr->type != EXPR_T_AND) { ovs_assert(expr->type == EXPR_T_BOOLEAN); return expr; } struct expr *a, *b; LIST_FOR_EACH_SAFE (a, b, node, &expr->andor) { if (&b->node == &expr->andor || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP || a->cmp.symbol != b->cmp.symbol) { continue; } else if (a->cmp.symbol->width ? mf_subvalue_intersect(&a->cmp.value, &a->cmp.mask, &b->cmp.value, &b->cmp.mask, &b->cmp.value, &b->cmp.mask) : !strcmp(a->cmp.string, b->cmp.string)) { list_remove(&a->node); expr_destroy(a); } else { expr_destroy(expr); return expr_create_boolean(false); } } if (list_is_short(&expr->andor)) { struct expr *sub = expr_from_node(list_front(&expr->andor)); free(expr); return sub; } struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { if (sub->type == EXPR_T_CMP) { continue; } ovs_assert(sub->type == EXPR_T_OR); const struct expr_symbol *symbol = expr_is_cmp(sub); if (!symbol || symbol->must_crossproduct) { struct expr *or = expr_create_andor(EXPR_T_OR); struct expr *k; LIST_FOR_EACH (k, node, &sub->andor) { struct expr *and = expr_create_andor(EXPR_T_AND); struct expr *m; LIST_FOR_EACH (m, node, &expr->andor) { struct expr *term = m == sub ? k : m; if (term->type == EXPR_T_AND) { struct expr *p; LIST_FOR_EACH (p, node, &term->andor) { struct expr *new = expr_clone(p); list_push_back(&and->andor, &new->node); } } else { struct expr *new = expr_clone(term); list_push_back(&and->andor, &new->node); } } list_push_back(&or->andor, &and->node); } expr_destroy(expr); return expr_normalize_or(or); } } return expr; } static struct expr * expr_normalize_or(struct expr *expr) { struct expr *sub, *next; LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) { if (sub->type == EXPR_T_AND) { list_remove(&sub->node); struct expr *new = expr_normalize_and(sub); if (new->type == EXPR_T_BOOLEAN) { if (new->boolean) { expr_destroy(expr); return new; } free(new); } else { expr_insert_andor(expr, next, new); } } else { ovs_assert(sub->type == EXPR_T_CMP); } } if (list_is_empty(&expr->andor)) { free(expr); return expr_create_boolean(false); } if (list_is_short(&expr->andor)) { struct expr *sub = expr_from_node(list_pop_front(&expr->andor)); free(expr); return sub; } return expr; } /* Takes ownership of 'expr', which is either a constant "true" or "false" or * an expression in terms of only relationals, AND, and OR. Returns either a * constant "true" or "false" or 'expr' reduced to OR(AND(clause)) where a * clause is a cmp or a disjunction of cmps on a single field. This form is * significant because it is a form that can be directly converted to OpenFlow * flows with the Open vSwitch "conjunctive match" extension. * * 'expr' must already have been simplified, with expr_simplify(). */ struct expr * expr_normalize(struct expr *expr) { switch (expr->type) { case EXPR_T_CMP: return expr; case EXPR_T_AND: return expr_normalize_and(expr); case EXPR_T_OR: return expr_normalize_or(expr); case EXPR_T_BOOLEAN: return expr; } OVS_NOT_REACHED(); } /* Creates, initializes, and returns a new 'struct expr_match'. If 'm' is * nonnull then it is copied into the new expr_match, otherwise the new * expr_match's 'match' member is initialized to a catch-all match for the * caller to refine in-place. * * If 'conj_id' is nonzero, adds one conjunction based on 'conj_id', 'clause', * and 'n_clauses' to the returned 'struct expr_match', otherwise the * expr_match will not have any conjunctions. * * The caller should use expr_match_add() to add the expr_match to a hash table * after it is finalized. */ static struct expr_match * expr_match_new(const struct match *m, uint8_t clause, uint8_t n_clauses, uint32_t conj_id) { struct expr_match *match = xmalloc(sizeof *match); if (m) { match->match = *m; } else { match_init_catchall(&match->match); } if (conj_id) { match->conjunctions = xmalloc(sizeof *match->conjunctions); match->conjunctions[0].id = conj_id; match->conjunctions[0].clause = clause; match->conjunctions[0].n_clauses = n_clauses; match->n = 1; match->allocated = 1; } else { match->conjunctions = NULL; match->n = 0; match->allocated = 0; } return match; } /* Adds 'match' to hash table 'matches', which becomes the new owner of * 'match'. * * This might actually destroy 'match' because it gets merged together with * some existing conjunction.*/ static void expr_match_add(struct hmap *matches, struct expr_match *match) { uint32_t hash = match_hash(&match->match, 0); struct expr_match *m; HMAP_FOR_EACH_WITH_HASH (m, hmap_node, hash, matches) { if (match_equal(&m->match, &match->match)) { if (!m->n || !match->n) { free(m->conjunctions); m->conjunctions = NULL; m->n = 0; m->allocated = 0; } else { ovs_assert(match->n == 1); if (m->n >= m->allocated) { m->conjunctions = x2nrealloc(m->conjunctions, &m->allocated, sizeof *m->conjunctions); } m->conjunctions[m->n++] = match->conjunctions[0]; } free(match->conjunctions); free(match); return; } } hmap_insert(matches, &match->hmap_node, hash); } static bool constrain_match(const struct expr *expr, const struct simap *ports, struct match *m) { ovs_assert(expr->type == EXPR_T_CMP); if (expr->cmp.symbol->width) { mf_mask_subfield(expr->cmp.symbol->field, &expr->cmp.value, &expr->cmp.mask, m); } else { const struct simap_node *node; node = ports ? simap_find(ports, expr->cmp.string) : NULL; if (!node) { return false; } struct mf_subfield sf; sf.field = expr->cmp.symbol->field; sf.ofs = 0; sf.n_bits = expr->cmp.symbol->field->n_bits; union mf_subvalue x; memset(&x, 0, sizeof x); x.integer = htonll(node->data); mf_write_subfield(&sf, &x, m); } return true; } static bool add_disjunction(const struct expr *or, const struct simap *ports, struct match *m, uint8_t clause, uint8_t n_clauses, uint32_t conj_id, struct hmap *matches) { struct expr *sub; int n = 0; ovs_assert(or->type == EXPR_T_OR); LIST_FOR_EACH (sub, node, &or->andor) { struct expr_match *match = expr_match_new(m, clause, n_clauses, conj_id); if (constrain_match(sub, ports, &match->match)) { expr_match_add(matches, match); n++; } else { free(match->conjunctions); free(match); } } /* If n == 1, then this didn't really need to be a disjunction. Oh well, * that shouldn't happen much. */ return n > 0; } static void add_conjunction(const struct expr *and, const struct simap *ports, uint32_t *n_conjsp, struct hmap *matches) { struct match match; int n_clauses = 0; struct expr *sub; match_init_catchall(&match); ovs_assert(and->type == EXPR_T_AND); LIST_FOR_EACH (sub, node, &and->andor) { switch (sub->type) { case EXPR_T_CMP: if (!constrain_match(sub, ports, &match)) { return; } break; case EXPR_T_OR: n_clauses++; break; case EXPR_T_AND: case EXPR_T_BOOLEAN: OVS_NOT_REACHED(); } } if (!n_clauses) { expr_match_add(matches, expr_match_new(&match, 0, 0, 0)); } else if (n_clauses == 1) { LIST_FOR_EACH (sub, node, &and->andor) { if (sub->type == EXPR_T_OR) { add_disjunction(sub, ports, &match, 0, 0, 0, matches); } } } else { int clause = 0; (*n_conjsp)++; LIST_FOR_EACH (sub, node, &and->andor) { if (sub->type == EXPR_T_OR) { if (!add_disjunction(sub, ports, &match, clause++, n_clauses, *n_conjsp, matches)) { /* This clause can't ever match, so we might as well skip * adding the other clauses--the overall disjunctive flow * can't ever match. Ideally we would also back out all of * the clauses we already added, but that seems like a lot * of trouble for a case that might never occur in * practice. */ return; } } } /* Add the flow that matches on conj_id. */ match_set_conj_id(&match, *n_conjsp); expr_match_add(matches, expr_match_new(&match, 0, 0, 0)); } } static void add_cmp_flow(const struct expr *cmp, const struct simap *ports, struct hmap *matches) { struct expr_match *m = expr_match_new(NULL, 0, 0, 0); if (constrain_match(cmp, ports, &m->match)) { expr_match_add(matches, m); } else { free(m); } } /* Converts 'expr', which must be in the form returned by expr_normalize(), to * a collection of Open vSwitch flows in 'matches', which this function * initializes to an hmap of "struct expr_match" structures. Returns the * number of conjunctive match IDs consumed by 'matches', which uses * conjunctive match IDs beginning with 0; the caller must offset or remap them * into the desired range as necessary. * * The matches inserted into 'matches' will be of three distinct kinds: * * - Ordinary flows. The caller should add these OpenFlow flows with * its desired actions. * * - Conjunctive flows, distinguished by 'n > 0' in the expr_match * structure. The caller should add these OpenFlow flows with the * conjunction(id, k/n) actions as specified in the 'conjunctions' array, * remapping the ids. * * - conj_id flows, distinguished by matching on the "conj_id" field. The * caller should remap the conj_id and add the OpenFlow flow with its * desired actions. * * 'ports' must be a map from strings (presumably names of ports) to integers. * Any comparisons against string fields in 'expr' are translated into integers * through this map. A comparison against a string that is not in 'ports' acts * like a Boolean "false"; that is, it will always fail to match. For a simple * expression, this means that the overall expression always fails to match, * but an expression with a disjunction on the string field might still match * on other port names. * * (This treatment of string fields might be too simplistic in general, but it * seems reasonable for now when string fields are used only for ports.) */ uint32_t expr_to_matches(const struct expr *expr, const struct simap *ports, struct hmap *matches) { uint32_t n_conjs = 0; hmap_init(matches); switch (expr->type) { case EXPR_T_CMP: add_cmp_flow(expr, ports, matches); break; case EXPR_T_AND: add_conjunction(expr, ports, &n_conjs, matches); break; case EXPR_T_OR: if (expr_is_cmp(expr)) { struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { add_cmp_flow(sub, ports, matches); } } else { struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { if (sub->type == EXPR_T_AND) { add_conjunction(sub, ports, &n_conjs, matches); } else { add_cmp_flow(sub, ports, matches); } } } break; case EXPR_T_BOOLEAN: if (expr->boolean) { struct expr_match *m = expr_match_new(NULL, 0, 0, 0); expr_match_add(matches, m); } else { /* No match. */ } break; } return n_conjs; } /* Destroys all of the 'struct expr_match'es in 'matches', as well as the * 'matches' hmap itself. */ void expr_matches_destroy(struct hmap *matches) { struct expr_match *m, *n; HMAP_FOR_EACH_SAFE (m, n, hmap_node, matches) { hmap_remove(matches, &m->hmap_node); free(m->conjunctions); free(m); } hmap_destroy(matches); } /* Prints a representation of the 'struct expr_match'es in 'matches' to * 'stream'. */ void expr_matches_print(const struct hmap *matches, FILE *stream) { if (hmap_is_empty(matches)) { fputs("(no flows)\n", stream); return; } const struct expr_match *m; HMAP_FOR_EACH (m, hmap_node, matches) { char *s = match_to_string(&m->match, OFP_DEFAULT_PRIORITY); fputs(s, stream); free(s); if (m->n) { for (int i = 0; i < m->n; i++) { const struct cls_conjunction *c = &m->conjunctions[i]; fprintf(stream, "%c conjunction(%"PRIu32", %d/%d)", i == 0 ? ':' : ',', c->id, c->clause, c->n_clauses); } } putc('\n', stream); } } /* Returns true if 'expr' honors the invariants for expressions (see the large * comment above "struct expr" in expr.h), false otherwise. */ bool expr_honors_invariants(const struct expr *expr) { const struct expr *sub; switch (expr->type) { case EXPR_T_CMP: if (expr->cmp.symbol->width) { for (int i = 0; i < ARRAY_SIZE(expr->cmp.value.be64); i++) { if (expr->cmp.value.be64[i] & ~expr->cmp.mask.be64[i]) { return false; } } } return true; case EXPR_T_AND: case EXPR_T_OR: if (list_is_short(&expr->andor)) { return false; } LIST_FOR_EACH (sub, node, &expr->andor) { if (sub->type == expr->type || !expr_honors_invariants(sub)) { return false; } } return true; case EXPR_T_BOOLEAN: return true; default: OVS_NOT_REACHED(); } } static bool expr_is_normalized_and(const struct expr *expr) { /* XXX should also check that no symbol is repeated. */ const struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { if (!expr_is_cmp(sub)) { return false; } } return true; } /* Returns true if 'expr' is in the form returned by expr_normalize(), false * otherwise. */ bool expr_is_normalized(const struct expr *expr) { switch (expr->type) { case EXPR_T_CMP: return true; case EXPR_T_AND: return expr_is_normalized_and(expr); case EXPR_T_OR: if (!expr_is_cmp(expr)) { const struct expr *sub; LIST_FOR_EACH (sub, node, &expr->andor) { if (!expr_is_cmp(sub) && !expr_is_normalized_and(sub)) { return false; } } } return true; case EXPR_T_BOOLEAN: return true; default: OVS_NOT_REACHED(); } } /* Action parsing helper. */ /* Expands 'f' repeatedly as long as it has an expansion, that is, as long as * it is a subfield or a predicate. Adds any prerequisites for 'f' to * '*prereqs'. * * If 'rw', verifies that 'f' is a read/write field. * * 'exchange' should be true if an exchange action is being parsed. This is * only used to improve error message phrasing. * * Returns true if successful, false if an error was encountered (in which case * 'ctx->error' reports the particular error). */ static bool expand_symbol(struct expr_context *ctx, bool rw, bool exchange, struct expr_field *f, struct expr **prereqsp) { const struct expr_symbol *orig_symbol = f->symbol; if (f->symbol->expansion && f->symbol->level != EXPR_L_ORDINAL) { expr_error(ctx, "Predicate symbol %s cannot be used in %s.", f->symbol->name, exchange ? "exchange" : "assignment"); return false; } for (;;) { /* Accumulate prerequisites. */ if (f->symbol->prereqs) { struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting); char *error; struct expr *e; e = parse_and_annotate(f->symbol->prereqs, ctx->symtab, &nesting, &error); if (error) { expr_error(ctx, "%s", error); free(error); return false; } *prereqsp = expr_combine(EXPR_T_AND, *prereqsp, e); } /* If there's no expansion, we're done. */ if (!f->symbol->expansion) { break; } /* Expand. */ struct expr_field expansion; char *error; if (!parse_field_from_string(f->symbol->expansion, ctx->symtab, &expansion, &error)) { expr_error(ctx, "%s", error); free(error); return false; } f->symbol = expansion.symbol; f->ofs += expansion.ofs; } if (rw && !f->symbol->field->writable) { expr_error(ctx, "Field %s is not modifiable.", orig_symbol->name); return false; } return true; } static void mf_subfield_from_expr_field(const struct expr_field *f, struct mf_subfield *sf) { sf->field = f->symbol->field; sf->ofs = f->ofs; sf->n_bits = f->n_bits ? f->n_bits : f->symbol->field->n_bits; } static void init_stack_action(const struct expr_field *f, struct ofpact_stack *stack) { mf_subfield_from_expr_field(f, &stack->subfield); } static struct expr * parse_assignment(struct expr_context *ctx, const struct simap *ports, struct ofpbuf *ofpacts) { struct expr *prereqs = NULL; /* Parse destination and do basic checking. */ struct expr_field dst; if (!parse_field(ctx, &dst)) { goto exit; } bool exchange = lexer_match(ctx->lexer, LEX_T_EXCHANGE); if (!exchange && !lexer_match(ctx->lexer, LEX_T_EQUALS)) { expr_syntax_error(ctx, "expecting `='."); goto exit; } const struct expr_symbol *orig_dst = dst.symbol; if (!expand_symbol(ctx, true, exchange, &dst, &prereqs)) { goto exit; } if (exchange || ctx->lexer->token.type == LEX_T_ID) { struct expr_field src; if (!parse_field(ctx, &src)) { goto exit; } const struct expr_symbol *orig_src = src.symbol; if (!expand_symbol(ctx, exchange, exchange, &src, &prereqs)) { goto exit; } if ((dst.symbol->width != 0) != (src.symbol->width != 0)) { if (exchange) { expr_error(ctx, "Can't exchange %s field (%s) with %s field (%s).", orig_dst->width ? "integer" : "string", orig_dst->name, orig_src->width ? "integer" : "string", orig_src->name); } else { expr_error(ctx, "Can't assign %s field (%s) to %s field (%s).", orig_src->width ? "integer" : "string", orig_src->name, orig_dst->width ? "integer" : "string", orig_dst->name); } goto exit; } if (dst.n_bits != src.n_bits) { if (exchange) { expr_error(ctx, "Can't exchange %d-bit field with %d-bit field.", dst.n_bits, src.n_bits); } else { expr_error(ctx, "Can't assign %d-bit value to %d-bit destination.", src.n_bits, dst.n_bits); } goto exit; } else if (!dst.n_bits && dst.symbol->field->n_bits != src.symbol->field->n_bits) { expr_error(ctx, "String fields %s and %s are incompatible for " "%s.", orig_dst->name, orig_src->name, exchange ? "exchange" : "assignment"); goto exit; } if (exchange) { init_stack_action(&src, ofpact_put_STACK_PUSH(ofpacts)); init_stack_action(&dst, ofpact_put_STACK_PUSH(ofpacts)); init_stack_action(&src, ofpact_put_STACK_POP(ofpacts)); init_stack_action(&dst, ofpact_put_STACK_POP(ofpacts)); } else { struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); mf_subfield_from_expr_field(&src, &move->src); mf_subfield_from_expr_field(&dst, &move->dst); } } else { struct expr_constant_set cs; if (!parse_constant_set(ctx, &cs)) { goto exit; } if (!type_check(ctx, &dst, &cs)) { goto exit_destroy_cs; } if (cs.in_curlies) { expr_error(ctx, "Assignments require a single value."); goto exit_destroy_cs; } union expr_constant *c = cs.values; struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); sf->field = dst.symbol->field; if (dst.symbol->width) { mf_subvalue_shift(&c->value, dst.ofs); if (!c->masked) { memset(&c->mask, 0, sizeof c->mask); bitwise_one(&c->mask, sizeof c->mask, dst.ofs, dst.n_bits); } else { mf_subvalue_shift(&c->mask, dst.ofs); } memcpy(&sf->value, &c->value.u8[sizeof c->value - sf->field->n_bytes], sf->field->n_bytes); memcpy(&sf->mask, &c->mask.u8[sizeof c->mask - sf->field->n_bytes], sf->field->n_bytes); } else { uint32_t port = simap_get(ports, c->string); bitwise_put(port, &sf->value, sf->field->n_bytes, 0, sf->field->n_bits); bitwise_one(&sf->mask, sf->field->n_bytes, 0, sf->field->n_bits); /* If the logical input port is being zeroed, clear the OpenFlow * ingress port also, to allow a packet to be sent back to its * origin. */ if (!port && sf->field->id == MFF_LOG_INPORT) { sf = ofpact_put_SET_FIELD(ofpacts); sf->field = mf_from_id(MFF_IN_PORT); bitwise_one(&sf->mask, sf->field->n_bytes, 0, sf->field->n_bits); } } exit_destroy_cs: expr_constant_set_destroy(&cs); } exit: return prereqs; } /* A helper for actions_parse(), to parse an OVN assignment action in the form * "field = value" or "field1 = field2", or a "exchange" action in the form * "field1 <-> field2", into 'ofpacts'. The parameters and return value match * those for actions_parse(). */ char * expr_parse_assignment(struct lexer *lexer, const struct shash *symtab, const struct simap *ports, struct ofpbuf *ofpacts, struct expr **prereqsp) { struct expr_context ctx; ctx.lexer = lexer; ctx.symtab = symtab; ctx.error = NULL; ctx.not = false; struct expr *prereqs = parse_assignment(&ctx, ports, ofpacts); if (ctx.error) { expr_destroy(prereqs); prereqs = NULL; } *prereqsp = prereqs; return ctx.error; } openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/expr.h0000644000000000000000000000013113534540071016342 xustar0029 mtime=1567801401.71768349 30 atime=1567801402.117686428 30 ctime=1567801425.069855549 openvswitch-2.5.9/ovn/lib/expr.h0000644000175000017500000003244013534540071020034 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_EXPR_H #define OVN_EXPR_H 1 /* OVN matching expression tree * ============================ * * The data structures here form an abstract expression tree for matching * expressions in OVN. * * The abstract syntax tree representation of a matching expression is one of: * * - A Boolean literal ("true" or "false"). * * - A comparison of a field (or part of a field) against a constant * with one of the operators == != < <= > >=. * * - The logical AND or OR of two or more matching expressions. * * Literals and comparisons are called "terminal" nodes, logical AND and OR * nodes are "nonterminal" nodes. * * The syntax for expressions includes a few other concepts that are not part * of the abstract syntax tree. In these examples, x is a field, a, b, and c * are constants, and e1 and e2 are arbitrary expressions: * * - Logical NOT. The parser implements NOT by inverting the sense of the * operand: !(x == a) becomes x != a, !(e1 && e2) becomes !e1 || !e2, and * so on. * * - Set membership. The parser translates x == {a, b, c} into * x == a || x == b || x == c. * * - Reversed comparisons. The parser translates a < x into x > a. * * - Range expressions. The parser translates a < x < b into * x > a && x < b. */ #include "classifier.h" #include "lex.h" #include "hmap.h" #include "list.h" #include "match.h" #include "meta-flow.h" struct ds; struct ofpbuf; struct shash; struct simap; /* "Measurement level" of a field. See "Level of Measurement" in the large * comment on struct expr_symbol below for more information. */ enum expr_level { EXPR_L_NOMINAL, /* Boolean values are nominal, however because of their simple nature OVN * can allow both equality and inequality tests on them. */ EXPR_L_BOOLEAN, /* Ordinal values can at least be ordered on a scale. OVN allows equality * and inequality and relational tests on ordinal values. These are the * fields on which OVS allows bitwise matching. */ EXPR_L_ORDINAL }; const char *expr_level_to_string(enum expr_level); /* A symbol. * * * Name * ==== * * Every symbol must have a name. To be useful, the name must satisfy the * lexer's syntax for an identifier. * * * Width * ===== * * Every symbol has a width. For integer symbols, this is the number of bits * in the value; for string symbols, this is 0. * * * Types * ===== * * There are three kinds of symbols: * * Fields: * * One might, for example, define a field named "vlan.tci" to refer to * MFF_VLAN_TCI. For integer fields, 'field' specifies the referent; for * string fields, 'field' is NULL. * * 'expansion' is NULL. * * Integer fields can be nominal or ordinal (see below). String fields are * always nominal. * * Subfields: * * 'expansion' is a string that specifies a subfield of some larger field, * e.g. "vlan.tci[0..11]" for a field that represents a VLAN VID. * * 'field' is NULL. * * Only ordinal fields (see below) may have subfields, and subfields are * always ordinal. * * Predicates: * * A predicate is an arbitrary Boolean expression that can be used in an * expression much like a 1-bit field. 'expansion' specifies the Boolean * expression, e.g. "ip4" might expand to "eth.type == 0x800". The * expansion of a predicate might refer to other predicates, e.g. "icmp4" * might expand to "ip4 && ip4.proto == 1". * * 'field' is NULL. * * A predicate whose expansion refers to any nominal field or predicate * (see below) is nominal; other predicates have Boolean level of * measurement. * * * Level of Measurement * ==================== * * See http://en.wikipedia.org/wiki/Level_of_measurement for the statistical * concept on which this classification is based. There are three levels: * * Ordinal: * * In statistics, ordinal values can be ordered on a scale. Here, we * consider a field (or subfield) to be ordinal if its bits can be examined * individually. This is true for the OpenFlow fields that OpenFlow or * Open vSwitch makes "maskable". * * OVN supports all the usual arithmetic relations (== != < <= > >=) on * ordinal fields and their subfields, because all of these can be * implemented as collections of bitwise tests. * * Nominal: * * In statistics, nominal values cannot be usefully compared except for * equality. This is true of OpenFlow port numbers, Ethernet types, and IP * protocols are examples: all of these are just identifiers assigned * arbitrarily with no deeper meaning. In OpenFlow and Open vSwitch, bits * in these fields generally aren't individually addressable. * * OVN only supports arithmetic tests for equality on nominal fields, * because OpenFlow and Open vSwitch provide no way for a flow to * efficiently implement other comparisons on them. (A test for inequality * can be sort of built out of two flows with different priorities, but OVN * matching expressions always generate flows with a single priority.) * * String fields are always nominal. * * Boolean: * * A nominal field that has only two values, 0 and 1, is somewhat * exceptional, since it is easy to support both equality and inequality * tests on such a field: either one can be implemented as a test for 0 or * 1. * * Only predicates (see above) have a Boolean level of measurement. * * This isn't a standard level of measurement. * * * Prerequisites * ============= * * Any symbol can have prerequisites, which are specified as a string giving an * additional expression that must be true whenever the symbol is referenced. * For example, the "icmp4.type" symbol might have prerequisite "icmp4", which * would cause an expression "icmp4.type == 0" to be interpreted as "icmp4.type * == 0 && icmp4", which would in turn expand to "icmp4.type == 0 && eth.type * == 0x800 && ip4.proto == 1" (assuming "icmp4" is a predicate defined as * suggested under "Types" above). * * * Crossproducting * =============== * * Ordinarily OVN is willing to consider using any field as a dimension in the * Open vSwitch "conjunctive match" extension (see ovs-ofctl(8)). However, * some fields can't actually be used that way because they are necessary as * prerequisites. For example, from an expression like "tcp.src == {1,2,3} * && tcp.dst == {4,5,6}", OVN might naturally generate flows like this: * * conj_id=1,actions=... * ip,actions=conjunction(1,1/3) * ip6,actions=conjunction(1,1/3) * tp_src=1,actions=conjunction(1,2/3) * tp_src=2,actions=conjunction(1,2/3) * tp_src=3,actions=conjunction(1,2/3) * tp_dst=4,actions=conjunction(1,3/3) * tp_dst=5,actions=conjunction(1,3/3) * tp_dst=6,actions=conjunction(1,3/3) * * but that's not valid because any flow that matches on tp_src or tp_dst must * also match on either ip or ip6. Thus, one would mark eth.type as "must * crossproduct", to force generating flows like this: * * conj_id=1,actions=... * ip,tp_src=1,actions=conjunction(1,1/2) * ip,tp_src=2,actions=conjunction(1,1/2) * ip,tp_src=3,actions=conjunction(1,1/2) * ip6,tp_src=1,actions=conjunction(1,1/2) * ip6,tp_src=2,actions=conjunction(1,1/2) * ip6,tp_src=3,actions=conjunction(1,1/2) * ip,tp_dst=4,actions=conjunction(1,2/2) * ip,tp_dst=5,actions=conjunction(1,2/2) * ip,tp_dst=6,actions=conjunction(1,2/2) * ip6,tp_dst=4,actions=conjunction(1,2/2) * ip6,tp_dst=5,actions=conjunction(1,2/2) * ip6,tp_dst=6,actions=conjunction(1,2/2) * * which are acceptable. */ struct expr_symbol { char *name; int width; const struct mf_field *field; char *expansion; enum expr_level level; char *prereqs; bool must_crossproduct; }; struct expr_symbol *expr_symtab_add_field(struct shash *symtab, const char *name, enum mf_field_id, const char *prereqs, bool must_crossproduct); struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab, const char *name, const char *prereqs, const char *subfield); struct expr_symbol *expr_symtab_add_string(struct shash *symtab, const char *name, enum mf_field_id, const char *prereqs); struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab, const char *name, const char *expansion); void expr_symtab_destroy(struct shash *symtab); /* Expression type. */ enum expr_type { EXPR_T_CMP, /* Compare symbol with constant. */ EXPR_T_AND, /* Logical AND of 2 or more subexpressions. */ EXPR_T_OR, /* Logical OR of 2 or more subexpressions. */ EXPR_T_BOOLEAN, /* True or false constant. */ }; /* Relational operator. */ enum expr_relop { EXPR_R_EQ, /* == */ EXPR_R_NE, /* != */ EXPR_R_LT, /* < */ EXPR_R_LE, /* <= */ EXPR_R_GT, /* > */ EXPR_R_GE, /* >= */ }; const char *expr_relop_to_string(enum expr_relop); bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop); /* An abstract syntax tree for a matching expression. * * The expression code maintains and relies on a few important invariants: * * - An EXPR_T_AND or EXPR_T_OR node never has a child of the same type. * (Any such children could be merged into their parent.) A node may * have grandchildren of its own type. * * As a consequence, every nonterminal node at the same distance from the * root has the same type. * * - EXPR_T_AND and EXPR_T_OR nodes must have at least two children. * * - An EXPR_T_CMP node always has a nonzero mask, and never has a 1-bit * in its value in a position where the mask is a 0-bit. * * The expr_honors_invariants() function can check invariants. */ struct expr { struct ovs_list node; /* In parent EXPR_T_AND or EXPR_T_OR if any. */ enum expr_type type; /* Expression type. */ union { /* EXPR_T_CMP. * * The symbol is on the left, e.g. "field < constant". */ struct { const struct expr_symbol *symbol; enum expr_relop relop; union { char *string; struct { union mf_subvalue value; union mf_subvalue mask; }; }; } cmp; /* EXPR_T_AND, EXPR_T_OR. */ struct ovs_list andor; /* EXPR_T_BOOLEAN. */ bool boolean; }; }; struct expr *expr_create_boolean(bool b); struct expr *expr_create_andor(enum expr_type); struct expr *expr_combine(enum expr_type, struct expr *a, struct expr *b); static inline struct expr * expr_from_node(const struct ovs_list *node) { return CONTAINER_OF(node, struct expr, node); } void expr_format(const struct expr *, struct ds *); void expr_print(const struct expr *); struct expr *expr_parse(struct lexer *, const struct shash *symtab, char **errorp); struct expr *expr_parse_string(const char *, const struct shash *symtab, char **errorp); struct expr *expr_clone(struct expr *); void expr_destroy(struct expr *); struct expr *expr_annotate(struct expr *, const struct shash *symtab, char **errorp); struct expr *expr_simplify(struct expr *); struct expr *expr_normalize(struct expr *); bool expr_honors_invariants(const struct expr *); bool expr_is_simplified(const struct expr *); bool expr_is_normalized(const struct expr *); /* Converting expressions to OpenFlow flows. */ /* An OpenFlow match generated from a Boolean expression. See * expr_to_matches() for more information. */ struct expr_match { struct hmap_node hmap_node; struct match match; struct cls_conjunction *conjunctions; size_t n, allocated; }; uint32_t expr_to_matches(const struct expr *, const struct simap *ports, struct hmap *matches); void expr_matches_destroy(struct hmap *matches); void expr_matches_print(const struct hmap *matches, FILE *); /* Action parsing helper. */ char *expr_parse_assignment(struct lexer *lexer, const struct shash *symtab, const struct simap *ports, struct ofpbuf *ofpacts, struct expr **prereqsp); #endif /* ovn/expr.h */ openvswitch-2.5.9/ovn/lib/PaxHeaders.82075/lex.h0000644000000000000000000000013113534540071016154 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801425.069855549 openvswitch-2.5.9/ovn/lib/lex.h0000644000175000017500000000770213534540071017651 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_LEX_H #define OVN_LEX_H 1 /* OVN lexical analyzer * ==================== * * This is a simple lexical analyzer (or tokenizer) for OVN match expressions * and ACLs. */ #include "meta-flow.h" struct ds; /* Token type. */ enum lex_type { LEX_T_END, /* end of input */ /* Tokens with auxiliary data. */ LEX_T_ID, /* foo */ LEX_T_STRING, /* "foo" */ LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */ LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */ LEX_T_ERROR, /* invalid input */ /* Bare tokens. */ LEX_T_LPAREN, /* ( */ LEX_T_RPAREN, /* ) */ LEX_T_LCURLY, /* { */ LEX_T_RCURLY, /* } */ LEX_T_LSQUARE, /* [ */ LEX_T_RSQUARE, /* ] */ LEX_T_EQ, /* == */ LEX_T_NE, /* != */ LEX_T_LT, /* < */ LEX_T_LE, /* <= */ LEX_T_GT, /* > */ LEX_T_GE, /* >= */ LEX_T_LOG_NOT, /* ! */ LEX_T_LOG_AND, /* && */ LEX_T_LOG_OR, /* || */ LEX_T_ELLIPSIS, /* .. */ LEX_T_COMMA, /* , */ LEX_T_SEMICOLON, /* ; */ LEX_T_EQUALS, /* = */ LEX_T_EXCHANGE, /* <-> */ LEX_T_DECREMENT, /* -- */ }; /* Subtype for LEX_T_INTEGER and LEX_T_MASKED_INTEGER tokens. * * These do not change the semantics of a token; instead, they determine the * format used when a token is serialized back to a text form. That's * important because 3232268289 is meaningless to a human whereas 192.168.128.1 * has some actual significance. */ enum lex_format { LEX_F_DECIMAL, LEX_F_HEXADECIMAL, LEX_F_IPV4, LEX_F_IPV6, LEX_F_ETHERNET, }; const char *lex_format_to_string(enum lex_format); /* A token. * * 's' is owned by the token. */ struct lex_token { enum lex_type type; /* One of LEX_*. */ char *s; /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR only. */ enum lex_format format; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */ }; void lex_token_init(struct lex_token *); void lex_token_destroy(struct lex_token *); void lex_token_swap(struct lex_token *, struct lex_token *); void lex_token_format(const struct lex_token *, struct ds *); const char *lex_token_parse(struct lex_token *, const char *input, const char **startp); /* A lexical analyzer. */ struct lexer { const char *input; /* Remaining input (not owned by lexer). */ const char *start; /* Start of current token in 'input'. */ struct lex_token token; /* Current token (owned by lexer). */ }; void lexer_init(struct lexer *, const char *input); void lexer_destroy(struct lexer *); enum lex_type lexer_get(struct lexer *); enum lex_type lexer_lookahead(const struct lexer *); bool lexer_match(struct lexer *, enum lex_type); bool lexer_match_id(struct lexer *, const char *id); bool lexer_is_int(const struct lexer *); bool lexer_get_int(struct lexer *, int *value); #endif /* ovn/lex.h */ openvswitch-2.5.9/ovn/PaxHeaders.82075/northd0000644000000000000000000000013213534540121015663 xustar0030 mtime=1567801425.129855991 30 atime=1567801425.625859648 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/northd/0000755000175000017500000000000013534540121017426 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/northd/PaxHeaders.82075/ovn-northd.8.xml0000644000000000000000000000013213534540071020732 xustar0030 mtime=1567801401.729683578 30 atime=1567801402.117686428 30 ctime=1567801424.509851421 openvswitch-2.5.9/ovn/northd/ovn-northd.8.xml0000644000175000017500000005155113534540071022427 0ustar00jpettitjpettit00000000000000

    Name

    ovn-northd -- Open Virtual Network central control daemon

    Synopsis

    ovn-northd [options]

    Description

    ovn-northd is a centralized daemon responsible for translating the high-level OVN configuration into logical configuration consumable by daemons such as ovn-controller. It translates the logical network configuration in terms of conventional network concepts, taken from the OVN Northbound Database (see ovn-nb(5)), into logical datapath flows in the OVN Southbound Database (see ovn-sb(5)) below it.

    Configuration

    ovn-northd requires a connection to the Northbound and Southbound databases. The default is db.sock in the local Open vSwitch's "run" directory. This may be overridden with the following commands:

    • --ovnnb-db=database

      The database containing the OVN Northbound Database.

    • --ovsnb-db=database

      The database containing the OVN Southbound Database.

    The database argument must take one of the following forms:

    • ssl:ip:port

      The specified SSL port on the host at the given ip, which must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: ssl:[::1]:6640. The --private-key, --certificate, and --ca-cert options are mandatory when this form is used.

    • tcp:ip:port

      Connect to the given TCP port on ip, where ip can be IPv4 or IPv6 address. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: tcp:[::1]:6640.

    • unix:file

      On POSIX, connect to the Unix domain server socket named file.

      On Windows, connect to a localhost TCP port whose value is written in file.

    Runtime Management Commands

    ovs-appctl can send commands to a running ovn-northd process. The currently supported commands are described below.

    exit
    Causes ovn-northd to gracefully terminate.

    Logical Flow Table Structure

    One of the main purposes of ovn-northd is to populate the Logical_Flow table in the OVN_Southbound database. This section describes how ovn-northd does this for switch and router logical datapaths.

    Logical Switch Datapaths

    Ingress Table 0: Admission Control and Ingress Port Security

    Ingress table 0 contains these logical flows:

    • Priority 100 flows to drop packets with VLAN tags or multicast Ethernet source addresses.
    • Priority 50 flows that implement ingress port security for each enabled logical port. For logical ports on which port security is enabled, these match the inport and the valid eth.src address(es) and advance only those packets to the next flow table. For logical ports on which port security is not enabled, these advance all packets that match the inport.

    There are no flows for disabled logical ports because the default-drop behavior of logical flow tables causes packets that ingress from them to be dropped.

    Ingress Table 1: from-lport Pre-ACLs

    Ingress table 1 prepares flows for possible stateful ACL processing in table 2. It contains a priority-0 flow that simply moves traffic to table 2. If stateful ACLs are used in the logical datapath, a priority-100 flow is added that sends IP packets to the connection tracker before advancing to table 2.

    Ingress table 2: from-lport ACLs

    Logical flows in this table closely reproduce those in the ACL table in the OVN_Northbound database for the from-lport direction. allow ACLs translate into logical flows with the next; action, allow-related ACLs translate into logical flows with the ct_next; action, other ACLs translate to drop;. The priority values from the ACL table are used directly.

    Ingress table 2 also contains a priority 0 flow with action next;, so that ACLs allow packets by default. If the logical datapath has a statetful ACL, the following flows will also be added:

    • A priority-1 flow to commit IP traffic to the connection tracker. This is needed for the default allow policy because, while the initiater's direction may not have any stateful rules, the server's may and then its return traffic would not be known and marked as invalid.
    • A priority-65535 flow that allows any traffic that has been committed to the connection tracker (i.e., established flows).
    • A priority-65535 flow that allows any traffic that is considered related to a committed flow in the connection tracker (e.g., an ICMP Port Unreachable from a non-listening UDP port).
    • A priority-65535 flow that drops all traffic marked by the connection tracker as invalid.

    Ingress Table 3: Destination Lookup

    This table implements switching behavior. It contains these logical flows:

    • A priority-100 flow that outputs all packets with an Ethernet broadcast or multicast eth.dst to the MC_FLOOD multicast group, which ovn-northd populates with all enabled logical ports.
    • One priority-50 flow that matches each known Ethernet address against eth.dst and outputs the packet to the single associated output port.
    • One priority-0 fallback flow that matches all packets and outputs them to the MC_UNKNOWN multicast group, which ovn-northd populates with all enabled logical ports that accept unknown destination packets. As a small optimization, if no logical ports accept unknown destination packets, ovn-northd omits this multicast group and logical flow.

    Egress Table 0: to-lport Pre-ACLs

    This is similar to ingress table 1 except for to-lport traffic.

    Egress Table 1: to-lport ACLs

    This is similar to ingress table 2 except for to-lport ACLs.

    Egress Table 2: Egress Port Security

    This is similar to the ingress port security logic in ingress table 0, but with important differences. Most obviously, outport and eth.dst are checked instead of inport and eth.src. Second, packets directed to broadcast or multicast eth.dst are always accepted instead of being subject to the port security rules; this is implemented through a priority-100 flow that matches on eth.mcast with action output;. Finally, to ensure that even broadcast and multicast packets are not delivered to disabled logical ports, a priority-150 flow for each disabled logical outport overrides the priority-100 flow with a drop; action.

    Logical Router Datapaths

    Ingress Table 0: L2 Admission Control

    This table drops packets that the router shouldn't see at all based on their Ethernet headers. It contains the following flows:

    • Priority-100 flows to drop packets with VLAN tags or multicast Ethernet source addresses.
    • For each enabled router port P with Ethernet address E, a priority-50 flow that matches inport == P && (eth.mcast || eth.dst == E), with action next;.

    Other packets are implicitly dropped.

    Ingress Table 1: IP Input

    This table is the core of the logical router datapath functionality. It contains the following flows to implement very basic IP host functionality.

    • L3 admission control: A priority-100 flow drops packets that match any of the following:

      • ip4.src[28..31] == 0xe (multicast source)
      • ip4.src == 255.255.255.255 (broadcast source)
      • ip4.src == 127.0.0.0/8 || ip4.dst == 127.0.0.0/8 (localhost source or destination)
      • ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8 (zero network source or destination)
      • ip4.src is any IP address owned by the router.
      • ip4.src is the broadcast address of any IP network known to the router.
    • ICMP echo reply. These flows reply to ICMP echo requests received for the router's IP address. Let A be an IP address or broadcast address owned by a router port. Then, for each A, a priority-90 flow matches on ip4.dst == A and icmp4.type == 8 && icmp4.code == 0 (ICMP echo request). These flows use the following actions where, if A is unicast, then S is A, and if A is broadcast, S is the router's IP address in A's network:

      ip4.dst = ip4.src;
      ip4.src = S;
      ip.ttl = 255;
      icmp4.type = 0;
      inport = ""; /* Allow sending out inport. */
      next;
              

      Similar flows match on ip4.dst == 255.255.255.255 and each individual inport, and use the same actions in which S is a function of inport.

    • ARP reply. These flows reply to ARP requests for the router's own IP address. For each router port P that owns IP address A and Ethernet address E, a priority-90 flow matches inport == P && arp.tpa == A && arp.op == 1 (ARP request) with the following actions:

      eth.dst = eth.src;
      eth.src = E;
      arp.op = 2; /* ARP reply. */
      arp.tha = arp.sha;
      arp.sha = E;
      arp.tpa = arp.spa;
      arp.spa = A;
      outport = P;
      inport = ""; /* Allow sending out inport. */
      output;
              
    • UDP port unreachable. Priority-80 flows generate ICMP port unreachable messages in reply to UDP datagrams directed to the router's IP address. The logical router doesn't accept any UDP traffic so it always generates such a reply.

      These flows should not match IP fragments with nonzero offset.

      Details TBD. Not yet implemented.

    • TCP reset. Priority-80 flows generate TCP reset messages in reply to TCP datagrams directed to the router's IP address. The logical router doesn't accept any TCP traffic so it always generates such a reply.

      These flows should not match IP fragments with nonzero offset.

      Details TBD. Not yet implemented.

    • Protocol unreachable. Priority-70 flows generate ICMP protocol unreachable messages in reply to packets directed to the router's IP address on IP protocols other than UDP, TCP, and ICMP.

      These flows should not match IP fragments with nonzero offset.

      Details TBD. Not yet implemented.

    • Drop other IP traffic to this router. These flows drop any other traffic destined to an IP address of this router that is not already handled by one of the flows above, which amounts to ICMP (other than echo requests) and fragments with nonzero offsets. For each IP address A owned by the router, a priority-60 flow matches ip4.dst == A and drops the traffic.

    The flows above handle all of the traffic that might be directed to the router itself. The following flows (with lower priorities) handle the remaining traffic, potentially for forwarding:

    • Drop Ethernet local broadcast. A priority-50 flow with match eth.bcast drops traffic destined to the local Ethernet broadcast address. By definition this traffic should not be forwarded.
    • Drop IP multicast. A priority-50 flow with match ip4.mcast drops IP multicast traffic.
    • ICMP time exceeded. For each router port P, whose IP address is A, a priority-40 flow with match inport == P && ip.ttl == {0, 1} && !ip.later_frag matches packets whose TTL has expired, with the following actions to send an ICMP time exceeded reply:

      icmp4 {
          icmp4.type = 11; /* Time exceeded. */
          icmp4.code = 0;  /* TTL exceeded in transit. */
          ip4.dst = ip4.src;
          ip4.src = A;
          ip.ttl = 255;
          next;
      };
              

      Not yet implemented.

    • TTL discard. A priority-30 flow with match ip.ttl == {0, 1} and actions drop; drops other packets whose TTL has expired, that should not receive a ICMP error reply (i.e. fragments with nonzero offset).
    • Next table. A priority-0 flows match all packets that aren't already handled and uses actions next; to feed them to the ingress table for routing.

    Ingress Table 2: IP Routing

    A packet that arrives at this table is an IP packet that should be routed to the address in ip4.dst. This table implements IP routing, setting reg0 to the next-hop IP address (leaving ip4.dst, the packet's final destination, unchanged) and advances to the next table for ARP resolution.

    This table contains the following logical flows:

    • Routing table. For each route to IPv4 network N with netmask M, a logical flow with match ip4.dst == N/M, whose priority is the number of 1-bits in M, has the following actions:

      ip.ttl--;
      reg0 = G;
      next;
              

      (Ingress table 1 already verified that ip.ttl--; will not yield a TTL exceeded error.)

      If the route has a gateway, G is the gateway IP address, otherwise it is ip4.dst.

    • Destination unreachable. For each router port P, which owns IP address A, a priority-0 logical flow with match in_port == P && !ip.later_frag && !icmp has the following actions:

      icmp4 {
          icmp4.type = 3; /* Destination unreachable. */
          icmp4.code = 0; /* Network unreachable. */
          ip4.dst = ip4.src;
          ip4.src = A;
          ip.ttl = 255;
          next(2);
      };
              

      (The !icmp check prevents recursion if the destination unreachable message itself cannot be routed.)

      These flows are omitted if the logical router has a default route, that is, a route with netmask 0.0.0.0.

    Ingress Table 3: ARP Resolution

    Any packet that reaches this table is an IP packet whose next-hop IP address is in reg0. (ip4.dst is the final destination.) This table resolves the IP address in reg0 into an output port in outport and an Ethernet address in eth.dst, using the following flows:

    • Known MAC bindings. For each IP address A whose host is known to have Ethernet address HE and reside on router port P with Ethernet address PE, a priority-200 flow with match reg0 == A has the following actions:

      eth.src = PE;
      eth.dst = HE;
      outport = P;
      output;
              

      MAC bindings can be known statically based on data in the OVN_Northbound database. For router ports connected to logical switches, MAC bindings can be known statically from the addresses column in the Logical_Port table. For router ports connected to other logical routers, MAC bindings can be known statically from the mac and network column in the Logical_Router_Port table.

    • Unknown MAC bindings. For each non-gateway route to IPv4 network N with netmask M on router port P that owns IP address A and Ethernet address E, a logical flow with match ip4.dst == N/M, whose priority is the number of 1-bits in M, has the following actions:

      arp {
          eth.dst = ff:ff:ff:ff:ff:ff;
          eth.src = E;
          arp.sha = E;
          arp.tha = 00:00:00:00:00:00;
          arp.spa = A;
          arp.tpa = ip4.dst;
          arp.op = 1;  /* ARP request. */
          outport = P;
          output;
      };
              

      TBD: How to install MAC bindings when an ARP response comes back. (Implement a "learn" action?)

      Not yet implemented.

    Egress Table 0: Delivery

    Packets that reach this table are ready for delivery. It contains priority-100 logical flows that match packets on each enabled logical router port, with action output;.

    openvswitch-2.5.9/ovn/northd/PaxHeaders.82075/automake.mk0000644000000000000000000000013113534540071020102 xustar0029 mtime=1567801401.72168352 30 atime=1567801402.117686428 30 ctime=1567801424.613852188 openvswitch-2.5.9/ovn/northd/automake.mk0000644000175000017500000000050413534540071021570 0ustar00jpettitjpettit00000000000000# ovn-northd bin_PROGRAMS += ovn/northd/ovn-northd ovn_northd_ovn_northd_SOURCES = ovn/northd/ovn-northd.c ovn_northd_ovn_northd_LDADD = \ ovn/lib/libovn.la \ ovsdb/libovsdb.la \ lib/libopenvswitch.la man_MANS += ovn/northd/ovn-northd.8 EXTRA_DIST += ovn/northd/ovn-northd.8.xml DISTCLEANFILES += ovn/northd/ovn-northd.8 openvswitch-2.5.9/ovn/northd/PaxHeaders.82075/ovn-northd.c0000644000000000000000000000013213534540071020206 xustar0030 mtime=1567801401.753683755 30 atime=1567801402.117686428 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/northd/ovn-northd.c0000644000175000017500000020441213534540071021677 0ustar00jpettitjpettit00000000000000/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "command-line.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "hmap.h" #include "json.h" #include "ovn/lib/lex.h" #include "ovn/lib/ovn-nb-idl.h" #include "ovn/lib/ovn-sb-idl.h" #include "poll-loop.h" #include "smap.h" #include "stream.h" #include "stream-ssl.h" #include "unixctl.h" #include "util.h" #include "uuid.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ovn_northd); static unixctl_cb_func ovn_northd_exit; struct northd_context { struct ovsdb_idl *ovnnb_idl; struct ovsdb_idl *ovnsb_idl; struct ovsdb_idl_txn *ovnnb_txn; struct ovsdb_idl_txn *ovnsb_txn; }; static const char *ovnnb_db; static const char *ovnsb_db; static const char *default_db(void); /* Pipeline stages. */ /* The two pipelines in an OVN logical flow table. */ enum ovn_pipeline { P_IN, /* Ingress pipeline. */ P_OUT /* Egress pipeline. */ }; /* The two purposes for which ovn-northd uses OVN logical datapaths. */ enum ovn_datapath_type { DP_SWITCH, /* OVN logical switch. */ DP_ROUTER /* OVN logical router. */ }; /* Returns an "enum ovn_stage" built from the arguments. * * (It's better to use ovn_stage_build() for type-safety reasons, but inline * functions can't be used in enums or switch cases.) */ #define OVN_STAGE_BUILD(DP_TYPE, PIPELINE, TABLE) \ (((DP_TYPE) << 9) | ((PIPELINE) << 8) | (TABLE)) /* A stage within an OVN logical switch or router. * * An "enum ovn_stage" indicates whether the stage is part of a logical switch * or router, whether the stage is part of the ingress or egress pipeline, and * the table within that pipeline. The first three components are combined to * form the stage's full name, e.g. S_SWITCH_IN_PORT_SEC, * S_ROUTER_OUT_DELIVERY. */ enum ovn_stage { #define PIPELINE_STAGES \ /* Logical switch ingress stages. */ \ PIPELINE_STAGE(SWITCH, IN, PORT_SEC, 0, "ls_in_port_sec") \ PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 1, "ls_in_pre_acl") \ PIPELINE_STAGE(SWITCH, IN, ACL, 2, "ls_in_acl") \ PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 3, "ls_in_l2_lkup") \ \ /* Logical switch egress stages. */ \ PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 0, "ls_out_pre_acl") \ PIPELINE_STAGE(SWITCH, OUT, ACL, 1, "ls_out_acl") \ PIPELINE_STAGE(SWITCH, OUT, PORT_SEC, 2, "ls_out_port_sec") \ \ /* Logical router ingress stages. */ \ PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \ PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \ PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 2, "lr_in_ip_routing") \ PIPELINE_STAGE(ROUTER, IN, ARP, 3, "lr_in_arp") \ \ /* Logical router egress stages. */ \ PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 0, "lr_out_delivery") #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \ S_##DP_TYPE##_##PIPELINE##_##STAGE \ = OVN_STAGE_BUILD(DP_##DP_TYPE, P_##PIPELINE, TABLE), PIPELINE_STAGES #undef PIPELINE_STAGE }; /* Due to various hard-coded priorities need to implement ACLs, the * northbound database supports a smaller range of ACL priorities than * are available to logical flows. This value is added to an ACL * priority to determine the ACL's logical flow priority. */ #define OVN_ACL_PRI_OFFSET 1000 /* Returns an "enum ovn_stage" built from the arguments. */ static enum ovn_stage ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline, uint8_t table) { return OVN_STAGE_BUILD(dp_type, pipeline, table); } /* Returns the pipeline to which 'stage' belongs. */ static enum ovn_pipeline ovn_stage_get_pipeline(enum ovn_stage stage) { return (stage >> 8) & 1; } /* Returns the table to which 'stage' belongs. */ static uint8_t ovn_stage_get_table(enum ovn_stage stage) { return stage & 0xff; } /* Returns a string name for 'stage'. */ static const char * ovn_stage_to_str(enum ovn_stage stage) { switch (stage) { #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \ case S_##DP_TYPE##_##PIPELINE##_##STAGE: return NAME; PIPELINE_STAGES #undef PIPELINE_STAGE default: return ""; } } static void usage(void) { printf("\ %s: OVN northbound management daemon\n\ usage: %s [OPTIONS]\n\ \n\ Options:\n\ --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\ (default: %s)\n\ --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\ (default: %s)\n\ -h, --help display this help message\n\ -o, --options list available options\n\ -V, --version display version information\n\ ", program_name, program_name, default_db(), default_db()); daemon_usage(); vlog_usage(); stream_usage("database", true, true, false); } struct tnlid_node { struct hmap_node hmap_node; uint32_t tnlid; }; static void destroy_tnlids(struct hmap *tnlids) { struct tnlid_node *node, *next; HMAP_FOR_EACH_SAFE (node, next, hmap_node, tnlids) { hmap_remove(tnlids, &node->hmap_node); free(node); } hmap_destroy(tnlids); } static void add_tnlid(struct hmap *set, uint32_t tnlid) { struct tnlid_node *node = xmalloc(sizeof *node); hmap_insert(set, &node->hmap_node, hash_int(tnlid, 0)); node->tnlid = tnlid; } static bool tnlid_in_use(const struct hmap *set, uint32_t tnlid) { const struct tnlid_node *node; HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_int(tnlid, 0), set) { if (node->tnlid == tnlid) { return true; } } return false; } static uint32_t next_tnlid(uint32_t tnlid, uint32_t max) { return tnlid + 1 <= max ? tnlid + 1 : 1; } static uint32_t allocate_tnlid(struct hmap *set, const char *name, uint32_t max, uint32_t *hint) { for (uint32_t tnlid = next_tnlid(*hint, max); tnlid != *hint; tnlid = next_tnlid(tnlid, max)) { if (!tnlid_in_use(set, tnlid)) { add_tnlid(set, tnlid); *hint = tnlid; return tnlid; } } static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "all %s tunnel ids exhausted", name); return 0; } /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or * sb->external_ids:logical-switch. */ struct ovn_datapath { struct hmap_node key_node; /* Index on 'key'. */ struct uuid key; /* (nbs/nbr)->header_.uuid. */ const struct nbrec_logical_switch *nbs; /* May be NULL. */ const struct nbrec_logical_router *nbr; /* May be NULL. */ const struct sbrec_datapath_binding *sb; /* May be NULL. */ struct ovs_list list; /* In list of similar records. */ /* Logical router data (digested from nbr). */ ovs_be32 gateway; /* Logical switch data. */ struct ovn_port **router_ports; size_t n_router_ports; struct hmap port_tnlids; uint32_t port_key_hint; bool has_unknown; }; static struct ovn_datapath * ovn_datapath_create(struct hmap *datapaths, const struct uuid *key, const struct nbrec_logical_switch *nbs, const struct nbrec_logical_router *nbr, const struct sbrec_datapath_binding *sb) { struct ovn_datapath *od = xzalloc(sizeof *od); od->key = *key; od->sb = sb; od->nbs = nbs; od->nbr = nbr; hmap_init(&od->port_tnlids); od->port_key_hint = 0; hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key)); return od; } static void ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od) { if (od) { /* Don't remove od->list. It is used within build_datapaths() as a * private list and once we've exited that function it is not safe to * use it. */ hmap_remove(datapaths, &od->key_node); destroy_tnlids(&od->port_tnlids); free(od->router_ports); free(od); } } static struct ovn_datapath * ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid) { struct ovn_datapath *od; HMAP_FOR_EACH_WITH_HASH (od, key_node, uuid_hash(uuid), datapaths) { if (uuid_equals(uuid, &od->key)) { return od; } } return NULL; } static struct ovn_datapath * ovn_datapath_from_sbrec(struct hmap *datapaths, const struct sbrec_datapath_binding *sb) { struct uuid key; if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) && !smap_get_uuid(&sb->external_ids, "logical-router", &key)) { return NULL; } return ovn_datapath_find(datapaths, &key); } static void join_datapaths(struct northd_context *ctx, struct hmap *datapaths, struct ovs_list *sb_only, struct ovs_list *nb_only, struct ovs_list *both) { hmap_init(datapaths); list_init(sb_only); list_init(nb_only); list_init(both); const struct sbrec_datapath_binding *sb, *sb_next; SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) { struct uuid key; if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) && !smap_get_uuid(&sb->external_ids, "logical-router", &key)) { ovsdb_idl_txn_add_comment( ctx->ovnsb_txn, "deleting Datapath_Binding "UUID_FMT" that lacks " "external-ids:logical-switch and " "external-ids:logical-router", UUID_ARGS(&sb->header_.uuid)); sbrec_datapath_binding_delete(sb); continue; } if (ovn_datapath_find(datapaths, &key)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_INFO_RL( &rl, "deleting Datapath_Binding "UUID_FMT" with " "duplicate external-ids:logical-switch/router "UUID_FMT, UUID_ARGS(&sb->header_.uuid), UUID_ARGS(&key)); sbrec_datapath_binding_delete(sb); continue; } struct ovn_datapath *od = ovn_datapath_create(datapaths, &key, NULL, NULL, sb); list_push_back(sb_only, &od->list); } const struct nbrec_logical_switch *nbs; NBREC_LOGICAL_SWITCH_FOR_EACH (nbs, ctx->ovnnb_idl) { struct ovn_datapath *od = ovn_datapath_find(datapaths, &nbs->header_.uuid); if (od) { od->nbs = nbs; list_remove(&od->list); list_push_back(both, &od->list); } else { od = ovn_datapath_create(datapaths, &nbs->header_.uuid, nbs, NULL, NULL); list_push_back(nb_only, &od->list); } } const struct nbrec_logical_router *nbr; NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) { struct ovn_datapath *od = ovn_datapath_find(datapaths, &nbr->header_.uuid); if (od) { if (!od->nbs) { od->nbr = nbr; list_remove(&od->list); list_push_back(both, &od->list); } else { /* Can't happen! */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "duplicate UUID "UUID_FMT" in OVN_Northbound", UUID_ARGS(&nbr->header_.uuid)); continue; } } else { od = ovn_datapath_create(datapaths, &nbr->header_.uuid, NULL, nbr, NULL); list_push_back(nb_only, &od->list); } od->gateway = 0; if (nbr->default_gw) { ovs_be32 ip, mask; char *error = ip_parse_masked(nbr->default_gw, &ip, &mask); if (error || !ip || mask != OVS_BE32_MAX) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "bad 'gateway' %s", nbr->default_gw); free(error); } else { od->gateway = ip; } } } } static uint32_t ovn_datapath_allocate_key(struct hmap *dp_tnlids) { static uint32_t hint; return allocate_tnlid(dp_tnlids, "datapath", (1u << 24) - 1, &hint); } static void build_datapaths(struct northd_context *ctx, struct hmap *datapaths) { struct ovs_list sb_only, nb_only, both; join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both); if (!list_is_empty(&nb_only)) { /* First index the in-use datapath tunnel IDs. */ struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids); struct ovn_datapath *od; LIST_FOR_EACH (od, list, &both) { add_tnlid(&dp_tnlids, od->sb->tunnel_key); } /* Add southbound record for each unmatched northbound record. */ LIST_FOR_EACH (od, list, &nb_only) { uint16_t tunnel_key = ovn_datapath_allocate_key(&dp_tnlids); if (!tunnel_key) { break; } od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn); char uuid_s[UUID_LEN + 1]; sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key)); const char *key = od->nbs ? "logical-switch" : "logical-router"; const struct smap id = SMAP_CONST1(&id, key, uuid_s); sbrec_datapath_binding_set_external_ids(od->sb, &id); sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key); } destroy_tnlids(&dp_tnlids); } /* Delete southbound records without northbound matches. */ struct ovn_datapath *od, *next; LIST_FOR_EACH_SAFE (od, next, list, &sb_only) { list_remove(&od->list); sbrec_datapath_binding_delete(od->sb); ovn_datapath_destroy(datapaths, od); } } struct ovn_port { struct hmap_node key_node; /* Index on 'key'. */ char *key; /* nbs->name, nbr->name, sb->logical_port. */ char *json_key; /* 'key', quoted for use in JSON. */ const struct nbrec_logical_port *nbs; /* May be NULL. */ const struct nbrec_logical_router_port *nbr; /* May be NULL. */ const struct sbrec_port_binding *sb; /* May be NULL. */ /* Logical router port data. */ ovs_be32 ip, mask; /* 192.168.10.123/24. */ ovs_be32 network; /* 192.168.10.0. */ ovs_be32 bcast; /* 192.168.10.255. */ struct eth_addr mac; struct ovn_port *peer; struct ovn_datapath *od; struct ovs_list list; /* In list of similar records. */ }; static struct ovn_port * ovn_port_create(struct hmap *ports, const char *key, const struct nbrec_logical_port *nbs, const struct nbrec_logical_router_port *nbr, const struct sbrec_port_binding *sb) { struct ovn_port *op = xzalloc(sizeof *op); struct ds json_key = DS_EMPTY_INITIALIZER; json_string_escape(key, &json_key); op->json_key = ds_steal_cstr(&json_key); op->key = xstrdup(key); op->sb = sb; op->nbs = nbs; op->nbr = nbr; hmap_insert(ports, &op->key_node, hash_string(op->key, 0)); return op; } static void ovn_port_destroy(struct hmap *ports, struct ovn_port *port) { if (port) { /* Don't remove port->list. It is used within build_ports() as a * private list and once we've exited that function it is not safe to * use it. */ hmap_remove(ports, &port->key_node); free(port->json_key); free(port->key); free(port); } } static struct ovn_port * ovn_port_find(struct hmap *ports, const char *name) { struct ovn_port *op; HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports) { if (!strcmp(op->key, name)) { return op; } } return NULL; } static uint32_t ovn_port_allocate_key(struct ovn_datapath *od) { return allocate_tnlid(&od->port_tnlids, "port", (1u << 15) - 1, &od->port_key_hint); } static void join_logical_ports(struct northd_context *ctx, struct hmap *datapaths, struct hmap *ports, struct ovs_list *sb_only, struct ovs_list *nb_only, struct ovs_list *both) { hmap_init(ports); list_init(sb_only); list_init(nb_only); list_init(both); const struct sbrec_port_binding *sb; SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) { struct ovn_port *op = ovn_port_create(ports, sb->logical_port, NULL, NULL, sb); list_push_back(sb_only, &op->list); } struct ovn_datapath *od; HMAP_FOR_EACH (od, key_node, datapaths) { if (od->nbs) { for (size_t i = 0; i < od->nbs->n_ports; i++) { const struct nbrec_logical_port *nbs = od->nbs->ports[i]; struct ovn_port *op = ovn_port_find(ports, nbs->name); if (op) { if (op->nbs || op->nbr) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "duplicate logical port %s", nbs->name); continue; } op->nbs = nbs; list_remove(&op->list); list_push_back(both, &op->list); } else { op = ovn_port_create(ports, nbs->name, nbs, NULL, NULL); list_push_back(nb_only, &op->list); } op->od = od; } } else { for (size_t i = 0; i < od->nbr->n_ports; i++) { const struct nbrec_logical_router_port *nbr = od->nbr->ports[i]; struct eth_addr mac; if (!eth_addr_from_string(nbr->mac, &mac)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "bad 'mac' %s", nbr->mac); continue; } ovs_be32 ip, mask; char *error = ip_parse_masked(nbr->network, &ip, &mask); if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "bad 'network' %s", nbr->network); free(error); continue; } struct ovn_port *op = ovn_port_find(ports, nbr->name); if (op) { if (op->nbs || op->nbr) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, "duplicate logical router port %s", nbr->name); continue; } op->nbr = nbr; list_remove(&op->list); list_push_back(both, &op->list); } else { op = ovn_port_create(ports, nbr->name, NULL, nbr, NULL); list_push_back(nb_only, &op->list); } op->ip = ip; op->mask = mask; op->network = ip & mask; op->bcast = ip | ~mask; op->mac = mac; op->od = od; } } } /* Connect logical router ports, and logical switch ports of type "router", * to their peers. */ struct ovn_port *op; HMAP_FOR_EACH (op, key_node, ports) { if (op->nbs && !strcmp(op->nbs->type, "router")) { const char *peer_name = smap_get(&op->nbs->options, "router-port"); if (!peer_name) { continue; } struct ovn_port *peer = ovn_port_find(ports, peer_name); if (!peer || !peer->nbr) { continue; } peer->peer = op; op->peer = peer; op->od->router_ports = xrealloc( op->od->router_ports, sizeof *op->od->router_ports * (op->od->n_router_ports + 1)); op->od->router_ports[op->od->n_router_ports++] = op; } else if (op->nbr && op->nbr->peer) { op->peer = ovn_port_find(ports, op->nbr->name); } } } static void ovn_port_update_sbrec(const struct ovn_port *op) { sbrec_port_binding_set_datapath(op->sb, op->od->sb); if (op->nbr) { sbrec_port_binding_set_type(op->sb, "patch"); const char *peer = op->peer ? op->peer->key : ""; const struct smap ids = SMAP_CONST1(&ids, "peer", peer); sbrec_port_binding_set_options(op->sb, &ids); sbrec_port_binding_set_parent_port(op->sb, NULL); sbrec_port_binding_set_tag(op->sb, NULL, 0); sbrec_port_binding_set_mac(op->sb, NULL, 0); } else { if (strcmp(op->nbs->type, "router")) { sbrec_port_binding_set_type(op->sb, op->nbs->type); sbrec_port_binding_set_options(op->sb, &op->nbs->options); } else { sbrec_port_binding_set_type(op->sb, "patch"); const char *router_port = smap_get(&op->nbs->options, "router-port"); if (!router_port) { router_port = ""; } const struct smap ids = SMAP_CONST1(&ids, "peer", router_port); sbrec_port_binding_set_options(op->sb, &ids); } sbrec_port_binding_set_parent_port(op->sb, op->nbs->parent_name); sbrec_port_binding_set_tag(op->sb, op->nbs->tag, op->nbs->n_tag); sbrec_port_binding_set_mac(op->sb, (const char **) op->nbs->addresses, op->nbs->n_addresses); } } static void build_ports(struct northd_context *ctx, struct hmap *datapaths, struct hmap *ports) { struct ovs_list sb_only, nb_only, both; join_logical_ports(ctx, datapaths, ports, &sb_only, &nb_only, &both); /* For logical ports that are in both databases, update the southbound * record based on northbound data. Also index the in-use tunnel_keys. */ struct ovn_port *op, *next; LIST_FOR_EACH_SAFE (op, next, list, &both) { ovn_port_update_sbrec(op); add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key); if (op->sb->tunnel_key > op->od->port_key_hint) { op->od->port_key_hint = op->sb->tunnel_key; } } /* Add southbound record for each unmatched northbound record. */ LIST_FOR_EACH_SAFE (op, next, list, &nb_only) { uint16_t tunnel_key = ovn_port_allocate_key(op->od); if (!tunnel_key) { continue; } op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn); ovn_port_update_sbrec(op); sbrec_port_binding_set_logical_port(op->sb, op->key); sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key); } /* Delete southbound records without northbound matches. */ LIST_FOR_EACH_SAFE(op, next, list, &sb_only) { list_remove(&op->list); sbrec_port_binding_delete(op->sb); ovn_port_destroy(ports, op); } } #define OVN_MIN_MULTICAST 32768 #define OVN_MAX_MULTICAST 65535 struct multicast_group { const char *name; uint16_t key; /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */ }; #define MC_FLOOD "_MC_flood" static const struct multicast_group mc_flood = { MC_FLOOD, 65535 }; #define MC_UNKNOWN "_MC_unknown" static const struct multicast_group mc_unknown = { MC_UNKNOWN, 65534 }; static bool multicast_group_equal(const struct multicast_group *a, const struct multicast_group *b) { return !strcmp(a->name, b->name) && a->key == b->key; } /* Multicast group entry. */ struct ovn_multicast { struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */ struct ovn_datapath *datapath; const struct multicast_group *group; struct ovn_port **ports; size_t n_ports, allocated_ports; }; static uint32_t ovn_multicast_hash(const struct ovn_datapath *datapath, const struct multicast_group *group) { return hash_pointer(datapath, group->key); } static struct ovn_multicast * ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath, const struct multicast_group *group) { struct ovn_multicast *mc; HMAP_FOR_EACH_WITH_HASH (mc, hmap_node, ovn_multicast_hash(datapath, group), mcgroups) { if (mc->datapath == datapath && multicast_group_equal(mc->group, group)) { return mc; } } return NULL; } static void ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group, struct ovn_port *port) { struct ovn_datapath *od = port->od; struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group); if (!mc) { mc = xmalloc(sizeof *mc); hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group)); mc->datapath = od; mc->group = group; mc->n_ports = 0; mc->allocated_ports = 4; mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports); } if (mc->n_ports >= mc->allocated_ports) { mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports, sizeof *mc->ports); } mc->ports[mc->n_ports++] = port; } static void ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc) { if (mc) { hmap_remove(mcgroups, &mc->hmap_node); free(mc->ports); free(mc); } } static void ovn_multicast_update_sbrec(const struct ovn_multicast *mc, const struct sbrec_multicast_group *sb) { struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports); for (size_t i = 0; i < mc->n_ports; i++) { ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb); } sbrec_multicast_group_set_ports(sb, ports, mc->n_ports); free(ports); } /* Logical flow generation. * * This code generates the Logical_Flow table in the southbound database, as a * function of most of the northbound database. */ struct ovn_lflow { struct hmap_node hmap_node; struct ovn_datapath *od; enum ovn_stage stage; uint16_t priority; char *match; char *actions; }; static size_t ovn_lflow_hash(const struct ovn_lflow *lflow) { size_t hash = uuid_hash(&lflow->od->key); hash = hash_2words((lflow->stage << 16) | lflow->priority, hash); hash = hash_string(lflow->match, hash); return hash_string(lflow->actions, hash); } static bool ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b) { return (a->od == b->od && a->stage == b->stage && a->priority == b->priority && !strcmp(a->match, b->match) && !strcmp(a->actions, b->actions)); } static void ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, char *match, char *actions) { lflow->od = od; lflow->stage = stage; lflow->priority = priority; lflow->match = match; lflow->actions = actions; } /* Adds a row with the specified contents to the Logical_Flow table. */ static void ovn_lflow_add(struct hmap *lflow_map, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, const char *match, const char *actions) { struct ovn_lflow *lflow = xmalloc(sizeof *lflow); ovn_lflow_init(lflow, od, stage, priority, xstrdup(match), xstrdup(actions)); hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow)); } static struct ovn_lflow * ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od, enum ovn_stage stage, uint16_t priority, const char *match, const char *actions) { struct ovn_lflow target; ovn_lflow_init(&target, od, stage, priority, CONST_CAST(char *, match), CONST_CAST(char *, actions)); struct ovn_lflow *lflow; HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, ovn_lflow_hash(&target), lflows) { if (ovn_lflow_equal(lflow, &target)) { return lflow; } } return NULL; } static void ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow) { if (lflow) { hmap_remove(lflows, &lflow->hmap_node); free(lflow->match); free(lflow->actions); free(lflow); } } /* Appends port security constraints on L2 address field 'eth_addr_field' * (e.g. "eth.src" or "eth.dst") to 'match'. 'port_security', with * 'n_port_security' elements, is the collection of port_security constraints * from an OVN_NB Logical_Port row. */ static void build_port_security(const char *eth_addr_field, char **port_security, size_t n_port_security, struct ds *match) { size_t base_len = match->length; ds_put_format(match, " && %s == {", eth_addr_field); size_t n = 0; for (size_t i = 0; i < n_port_security; i++) { struct eth_addr ea; if (eth_addr_from_string(port_security[i], &ea)) { ds_put_format(match, ETH_ADDR_FMT, ETH_ADDR_ARGS(ea)); ds_put_char(match, ' '); n++; } } ds_chomp(match, ' '); ds_put_cstr(match, "}"); if (!n) { match->length = base_len; } } static bool lport_is_enabled(const struct nbrec_logical_port *lport) { return !lport->enabled || *lport->enabled; } static bool has_stateful_acl(struct ovn_datapath *od) { for (size_t i = 0; i < od->nbs->n_acls; i++) { struct nbrec_acl *acl = od->nbs->acls[i]; if (!strcmp(acl->action, "allow-related")) { return true; } } return false; } static void build_acls(struct ovn_datapath *od, struct hmap *lflows, struct hmap *ports) { bool has_stateful = has_stateful_acl(od); struct ovn_port *op; struct ds match_in, match_out; /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are * allowed by default. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;"); /* Ingress and Egress ACL Table (Priority 0): Packets are allowed by * default. A related rule at priority 1 is added below if there * are any stateful ACLs in this datapath. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 0, "1", "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 0, "1", "next;"); /* If there are any stateful ACL rules in this dapapath, we must * send all IP packets through the conntrack action, which handles * defragmentation, in order to match L4 headers. */ if (has_stateful) { HMAP_FOR_EACH (op, key_node, ports) { if (op->od == od && !strcmp(op->nbs->type, "router")) { /* Can't use ct() for router ports. Consider the following configuration: lp1(10.0.0.2) on hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a ping from lp1 to lp2, First, the response will go through ct() with a zone for lp2 in the ls2 ingress pipeline on hostB. That ct zone knows about this connection. Next, it goes through ct() with the zone for the router port in the egress pipeline of ls2 on hostB. This zone does not know about the connection, as the icmp request went through the logical router on hostA, not hostB. This would only work with distributed conntrack state across all chassis. */ ds_init(&match_in); ds_init(&match_out); ds_put_format(&match_in, "ip && inport == %s", op->json_key); ds_put_format(&match_out, "ip && outport == %s", op->json_key); ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, ds_cstr(&match_in), "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, ds_cstr(&match_out), "next;"); ds_destroy(&match_in); ds_destroy(&match_out); } } /* Ingress and Egress Pre-ACL Table (Priority 100). * * Regardless of whether the ACL is "from-lport" or "to-lport", * we need rules in both the ingress and egress table, because * the return traffic needs to be followed. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 100, "ip", "ct_next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 100, "ip", "ct_next;"); /* Ingress and Egress ACL Table (Priority 1). * * By default, traffic is allowed. This is partially handled by * the Priority 0 ACL flows added earlier, but we also need to * commit IP flows. This is because, while the initiater's * direction may not have any stateful rules, the server's may * and then its return traffic would not have an associated * conntrack entry and would return "+invalid". */ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1, "ip", "ct_commit; next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1, "ip", "ct_commit; next;"); /* Ingress and Egress ACL Table (Priority 65535). * * Always drop traffic that's in an invalid state. This is * enforced at a higher priority than ACLs can be defined. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "ct.inv", "drop;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "ct.inv", "drop;"); /* Ingress and Egress ACL Table (Priority 65535). * * Always allow traffic that is established to a committed * conntrack entry. This is enforced at a higher priority than * ACLs can be defined. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "ct.est && !ct.rel && !ct.new && !ct.inv", "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "ct.est && !ct.rel && !ct.new && !ct.inv", "next;"); /* Ingress and Egress ACL Table (Priority 65535). * * Always allow traffic that is related to an existing conntrack * entry. This is enforced at a higher priority than ACLs can * be defined. * * NOTE: This does not support related data sessions (eg, * a dynamically negotiated FTP data channel), but will allow * related traffic such as an ICMP Port Unreachable through * that's generated from a non-listening UDP port. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "!ct.est && ct.rel && !ct.new && !ct.inv", "next;"); ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "!ct.est && ct.rel && !ct.new && !ct.inv", "next;"); } /* Ingress or Egress ACL Table (Various priorities). */ for (size_t i = 0; i < od->nbs->n_acls; i++) { struct nbrec_acl *acl = od->nbs->acls[i]; bool ingress = !strcmp(acl->direction, "from-lport") ? true :false; enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL; if (!strcmp(acl->action, "allow")) { /* If there are any stateful flows, we must even commit "allow" * actions. This is because, while the initiater's * direction may not have any stateful rules, the server's * may and then its return traffic would not have an * associated conntrack entry and would return "+invalid". */ const char *actions = has_stateful ? "ct_commit; next;" : "next;"; ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, acl->match, actions); } else if (!strcmp(acl->action, "allow-related")) { struct ds match = DS_EMPTY_INITIALIZER; /* Commit the connection tracking entry, which allows all * other traffic related to this entry to flow due to the * 65535 priority flow defined earlier. */ ds_put_format(&match, "ct.new && (%s)", acl->match); ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, ds_cstr(&match), "ct_commit; next;"); ds_destroy(&match); } else if (!strcmp(acl->action, "drop")) { ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, acl->match, "drop;"); } else if (!strcmp(acl->action, "reject")) { /* xxx Need to support "reject". */ VLOG_INFO("reject is not a supported action"); ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET, acl->match, "drop;"); } } } static void build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, struct hmap *lflows, struct hmap *mcgroups) { /* This flow table structure is documented in ovn-northd(8), so please * update ovn-northd.8.xml if you change anything. */ /* Build pre-ACL and ACL tables for both ingress and egress. * Ingress tables 1 and 2. Egress tables 0 and 1. */ struct ovn_datapath *od; HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; } build_acls(od, lflows, ports); } /* Logical switch ingress table 0: Admission control framework (priority * 100). */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; } /* Logical VLANs not supported. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC, 100, "vlan.present", "drop;"); /* Broadcast/multicast source address is invalid. */ ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC, 100, "eth.src[40]", "drop;"); /* Port security flows have priority 50 (see below) and will continue * to the next table if packet source is acceptable. */ } /* Logical switch ingress table 0: Ingress port security (priority 50). */ struct ovn_port *op; HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { continue; } if (!lport_is_enabled(op->nbs)) { /* Drop packets from disabled logical ports (since logical flow * tables are default-drop). */ continue; } struct ds match = DS_EMPTY_INITIALIZER; ds_put_format(&match, "inport == %s", op->json_key); build_port_security("eth.src", op->nbs->port_security, op->nbs->n_port_security, &match); ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC, 50, ds_cstr(&match), "next;"); ds_destroy(&match); } /* Ingress table 3: Destination lookup, broadcast and multicast handling * (priority 100). */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { continue; } if (lport_is_enabled(op->nbs)) { ovn_multicast_add(mcgroups, &mc_flood, op); } } HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; } ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100, "eth.mcast", "outport = \""MC_FLOOD"\"; output;"); } /* Ingress table 3: Destination lookup, unicast handling (priority 50), */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { continue; } for (size_t i = 0; i < op->nbs->n_addresses; i++) { struct eth_addr mac; if (eth_addr_from_string(op->nbs->addresses[i], &mac)) { struct ds match, actions; ds_init(&match); ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); ds_init(&actions); ds_put_format(&actions, "outport = %s; output;", op->json_key); ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50, ds_cstr(&match), ds_cstr(&actions)); ds_destroy(&actions); ds_destroy(&match); } else if (!strcmp(op->nbs->addresses[i], "unknown")) { if (lport_is_enabled(op->nbs)) { ovn_multicast_add(mcgroups, &mc_unknown, op); op->od->has_unknown = true; } } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "%s: invalid syntax '%s' in addresses column", op->nbs->name, op->nbs->addresses[i]); } } } /* Ingress table 3: Destination lookup for unknown MACs (priority 0). */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; } if (od->has_unknown) { ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1", "outport = \""MC_UNKNOWN"\"; output;"); } } /* Egress table 2: Egress port security multicast/broadcast (priority * 100). */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; } ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC, 100, "eth.mcast", "output;"); } /* Egress table 2: Egress port security (priorities 50 and 150). * * Priority 50 rules implement port security for enabled logical port. * * Priority 150 rules drop packets to disabled logical ports, so that they * don't even receive multicast or broadcast packets. */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { continue; } struct ds match = DS_EMPTY_INITIALIZER; ds_put_format(&match, "outport == %s", op->json_key); if (lport_is_enabled(op->nbs)) { build_port_security("eth.dst", op->nbs->port_security, op->nbs->n_port_security, &match); ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC, 50, ds_cstr(&match), "output;"); } else { ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC, 150, ds_cstr(&match), "drop;"); } ds_destroy(&match); } } static bool lrport_is_enabled(const struct nbrec_logical_router_port *lrport) { return !lrport->enabled || *lrport->enabled; } static void add_route(struct hmap *lflows, struct ovn_datapath *od, ovs_be32 network, ovs_be32 mask, ovs_be32 gateway) { char *match = xasprintf("ip4.dst == "IP_FMT"/"IP_FMT, IP_ARGS(network), IP_ARGS(mask)); struct ds actions = DS_EMPTY_INITIALIZER; ds_put_cstr(&actions, "ip.ttl--; reg0 = "); if (gateway) { ds_put_format(&actions, IP_FMT, IP_ARGS(gateway)); } else { ds_put_cstr(&actions, "ip4.dst"); } ds_put_cstr(&actions, "; next;"); /* The priority here is calculated to implement longest-prefix-match * routing. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, count_1bits(ntohl(mask)), match, ds_cstr(&actions)); ds_destroy(&actions); free(match); } static void build_lrouter_flows(struct hmap *datapaths, struct hmap *ports, struct hmap *lflows) { /* This flow table structure is documented in ovn-northd(8), so please * update ovn-northd.8.xml if you change anything. */ /* Logical router ingress table 0: Admission control framework. */ struct ovn_datapath *od; HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbr) { continue; } /* Logical VLANs not supported. * Broadcast/multicast source address is invalid. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 100, "vlan.present || eth.src[40]", "drop;"); } /* Logical router ingress table 0: match (priority 50). */ struct ovn_port *op; HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbr) { continue; } if (!lrport_is_enabled(op->nbr)) { /* Drop packets from disabled logical ports (since logical flow * tables are default-drop). */ continue; } char *match = xasprintf( "(eth.mcast || eth.dst == "ETH_ADDR_FMT") && inport == %s", ETH_ADDR_ARGS(op->mac), op->json_key); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50, match, "next;"); free(match); } /* Logical router ingress table 1: IP Input. */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbr) { continue; } /* L3 admission control: drop multicast and broadcast source, localhost * source or destination, and zero network source or destination * (priority 100). */ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100, "ip4.mcast || " "ip4.src == 255.255.255.255 || " "ip4.src == 127.0.0.0/8 || " "ip4.dst == 127.0.0.0/8 || " "ip4.src == 0.0.0.0/8 || " "ip4.dst == 0.0.0.0/8", "drop;"); /* Drop Ethernet local broadcast. By definition this traffic should * not be forwarded.*/ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50, "eth.bcast", "drop;"); /* Drop IP multicast. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50, "ip4.mcast", "drop;"); /* TTL discard. * * XXX Need to send ICMP time exceeded if !ip.later_frag. */ char *match = xasprintf("ip4 && ip.ttl == {0, 1}"); ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30, match, "drop;"); free(match); /* Pass other traffic not already handled to the next table for * routing. */ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;"); } HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbr) { continue; } /* L3 admission control: drop packets that originate from an IP address * owned by the router or a broadcast address known to the router * (priority 100). */ char *match = xasprintf("ip4.src == {"IP_FMT", "IP_FMT"}", IP_ARGS(op->ip), IP_ARGS(op->bcast)); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100, match, "drop;"); free(match); /* ICMP echo reply. These flows reply to ICMP echo requests * received for the router's IP address. */ match = xasprintf( "inport == %s && (ip4.dst == "IP_FMT" || ip4.dst == "IP_FMT") && " "icmp4.type == 8 && icmp4.code == 0", op->json_key, IP_ARGS(op->ip), IP_ARGS(op->bcast)); char *actions = xasprintf( "ip4.dst = ip4.src; " "ip4.src = "IP_FMT"; " "ip.ttl = 255; " "icmp4.type = 0; " "inport = \"\"; /* Allow sending out inport. */ " "next; ", IP_ARGS(op->ip)); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, match, actions); free(match); free(actions); /* ARP reply. These flows reply to ARP requests for the router's own * IP address. */ match = xasprintf( "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1", op->json_key, IP_ARGS(op->ip)); actions = xasprintf( "eth.dst = eth.src; " "eth.src = "ETH_ADDR_FMT"; " "arp.op = 2; /* ARP reply */ " "arp.tha = arp.sha; " "arp.sha = "ETH_ADDR_FMT"; " "arp.tpa = arp.spa; " "arp.spa = "IP_FMT"; " "outport = %s; " "inport = \"\"; /* Allow sending out inport. */ " "output;", ETH_ADDR_ARGS(op->mac), ETH_ADDR_ARGS(op->mac), IP_ARGS(op->ip), op->json_key); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90, match, actions); free(match); free(actions); /* Drop IP traffic to this router. */ match = xasprintf("ip4.dst == "IP_FMT, IP_ARGS(op->ip)); ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60, match, "drop;"); free(match); } /* Logical router ingress table 2: IP Routing. * * A packet that arrives at this table is an IP packet that should be * routed to the address in ip4.dst. This table sets reg0 to the next-hop * IP address (leaving ip4.dst, the packet’s final destination, unchanged) * and advances to the next table for ARP resolution. */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbr) { continue; } add_route(lflows, op->od, op->network, op->mask, 0); } HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbr) { continue; } if (od->gateway) { add_route(lflows, od, 0, 0, od->gateway); } } /* XXX destination unreachable */ /* Local router ingress table 3: ARP Resolution. * * Any packet that reaches this table is an IP packet whose next-hop IP * address is in reg0. (ip4.dst is the final destination.) This table * resolves the IP address in reg0 into an output port in outport and an * Ethernet address in eth.dst. */ HMAP_FOR_EACH (op, key_node, ports) { if (op->nbr) { /* XXX ARP for neighboring router */ } else if (op->od->n_router_ports) { for (size_t i = 0; i < op->nbs->n_addresses; i++) { struct eth_addr ea; ovs_be32 ip; if (ovs_scan(op->nbs->addresses[i], ETH_ADDR_SCAN_FMT" "IP_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea), IP_SCAN_ARGS(&ip))) { for (size_t j = 0; j < op->od->n_router_ports; j++) { /* Get the Logical_Router_Port that the Logical_Port is * connected to, as 'peer'. */ const char *peer_name = smap_get( &op->od->router_ports[j]->nbs->options, "router-port"); if (!peer_name) { continue; } struct ovn_port *peer = ovn_port_find(ports, peer_name); if (!peer || !peer->nbr) { continue; } /* Make sure that 'ip' is in 'peer''s network. */ if ((ip ^ peer->network) & peer->mask) { continue; } char *match = xasprintf("reg0 == "IP_FMT, IP_ARGS(ip)); char *actions = xasprintf("eth.src = "ETH_ADDR_FMT"; " "eth.dst = "ETH_ADDR_FMT"; " "outport = %s; " "output;", ETH_ADDR_ARGS(peer->mac), ETH_ADDR_ARGS(ea), peer->json_key); ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP, 200, match, actions); free(actions); free(match); break; } } } } } /* Logical router egress table 0: Delivery (priority 100). * * Priority 100 rules deliver packets to enabled logical ports. */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbr) { continue; } if (!lrport_is_enabled(op->nbr)) { /* Drop packets to disabled logical ports (since logical flow * tables are default-drop). */ continue; } char *match = xasprintf("outport == %s", op->json_key); ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100, match, "output;"); free(match); } } /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database, * constructing their contents based on the OVN_NB database. */ static void build_lflows(struct northd_context *ctx, struct hmap *datapaths, struct hmap *ports) { struct hmap lflows = HMAP_INITIALIZER(&lflows); struct hmap mcgroups = HMAP_INITIALIZER(&mcgroups); build_lswitch_flows(datapaths, ports, &lflows, &mcgroups); build_lrouter_flows(datapaths, ports, &lflows); /* Push changes to the Logical_Flow table to database. */ const struct sbrec_logical_flow *sbflow, *next_sbflow; SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) { struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath); if (!od) { sbrec_logical_flow_delete(sbflow); continue; } enum ovn_datapath_type dp_type = od->nbs ? DP_SWITCH : DP_ROUTER; enum ovn_pipeline pipeline = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT; struct ovn_lflow *lflow = ovn_lflow_find( &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id), sbflow->priority, sbflow->match, sbflow->actions); if (lflow) { ovn_lflow_destroy(&lflows, lflow); } else { sbrec_logical_flow_delete(sbflow); } } struct ovn_lflow *lflow, *next_lflow; HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) { enum ovn_pipeline pipeline = ovn_stage_get_pipeline(lflow->stage); uint8_t table = ovn_stage_get_table(lflow->stage); sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn); sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb); sbrec_logical_flow_set_pipeline( sbflow, pipeline == P_IN ? "ingress" : "egress"); sbrec_logical_flow_set_table_id(sbflow, table); sbrec_logical_flow_set_priority(sbflow, lflow->priority); sbrec_logical_flow_set_match(sbflow, lflow->match); sbrec_logical_flow_set_actions(sbflow, lflow->actions); const struct smap ids = SMAP_CONST1(&ids, "stage-name", ovn_stage_to_str(lflow->stage)); sbrec_logical_flow_set_external_ids(sbflow, &ids); ovn_lflow_destroy(&lflows, lflow); } hmap_destroy(&lflows); /* Push changes to the Multicast_Group table to database. */ const struct sbrec_multicast_group *sbmc, *next_sbmc; SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) { struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths, sbmc->datapath); if (!od) { sbrec_multicast_group_delete(sbmc); continue; } struct multicast_group group = { .name = sbmc->name, .key = sbmc->tunnel_key }; struct ovn_multicast *mc = ovn_multicast_find(&mcgroups, od, &group); if (mc) { ovn_multicast_update_sbrec(mc, sbmc); ovn_multicast_destroy(&mcgroups, mc); } else { sbrec_multicast_group_delete(sbmc); } } struct ovn_multicast *mc, *next_mc; HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, &mcgroups) { sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn); sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb); sbrec_multicast_group_set_name(sbmc, mc->group->name); sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key); ovn_multicast_update_sbrec(mc, sbmc); ovn_multicast_destroy(&mcgroups, mc); } hmap_destroy(&mcgroups); } static void ovnnb_db_run(struct northd_context *ctx) { if (!ctx->ovnsb_txn) { return; } VLOG_DBG("ovn-nb db contents may have changed."); struct hmap datapaths, ports; build_datapaths(ctx, &datapaths); build_ports(ctx, &datapaths, &ports); build_lflows(ctx, &datapaths, &ports); struct ovn_datapath *dp, *next_dp; HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, &datapaths) { ovn_datapath_destroy(&datapaths, dp); } hmap_destroy(&datapaths); struct ovn_port *port, *next_port; HMAP_FOR_EACH_SAFE (port, next_port, key_node, &ports) { ovn_port_destroy(&ports, port); } hmap_destroy(&ports); } /* * The only change we get notified about is if the 'chassis' column of the * 'Port_Binding' table changes. When this column is not empty, it means we * need to set the corresponding logical port as 'up' in the northbound DB. */ static void ovnsb_db_run(struct northd_context *ctx) { if (!ctx->ovnnb_txn) { return; } struct hmap lports_hmap; const struct sbrec_port_binding *sb; const struct nbrec_logical_port *nb; struct lport_hash_node { struct hmap_node node; const struct nbrec_logical_port *nb; } *hash_node, *hash_node_next; VLOG_DBG("Recalculating port up states for ovn-nb db."); hmap_init(&lports_hmap); NBREC_LOGICAL_PORT_FOR_EACH(nb, ctx->ovnnb_idl) { hash_node = xzalloc(sizeof *hash_node); hash_node->nb = nb; hmap_insert(&lports_hmap, &hash_node->node, hash_string(nb->name, 0)); } SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) { nb = NULL; HMAP_FOR_EACH_WITH_HASH(hash_node, node, hash_string(sb->logical_port, 0), &lports_hmap) { if (!strcmp(sb->logical_port, hash_node->nb->name)) { nb = hash_node->nb; break; } } if (!nb) { /* The logical port doesn't exist for this port binding. This can * happen under normal circumstances when ovn-northd hasn't gotten * around to pruning the Port_Binding yet. */ continue; } if (sb->chassis && (!nb->up || !*nb->up)) { bool up = true; nbrec_logical_port_set_up(nb, &up, 1); } else if (!sb->chassis && (!nb->up || *nb->up)) { bool up = false; nbrec_logical_port_set_up(nb, &up, 1); } } HMAP_FOR_EACH_SAFE(hash_node, hash_node_next, node, &lports_hmap) { hmap_remove(&lports_hmap, &hash_node->node); free(hash_node); } hmap_destroy(&lports_hmap); } static char *default_db_; static const char * default_db(void) { if (!default_db_) { default_db_ = xasprintf("unix:%s/db.sock", ovs_rundir()); } return default_db_; } static void parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { enum { DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS, }; static const struct option long_options[] = { {"ovnsb-db", required_argument, NULL, 'd'}, {"ovnnb-db", required_argument, NULL, 'D'}, {"help", no_argument, NULL, 'h'}, {"options", no_argument, NULL, 'o'}, {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { DAEMON_OPTION_HANDLERS; VLOG_OPTION_HANDLERS; STREAM_SSL_OPTION_HANDLERS; case 'd': ovnsb_db = optarg; break; case 'D': ovnnb_db = optarg; break; case 'h': usage(); exit(EXIT_SUCCESS); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); default: break; } } if (!ovnsb_db) { ovnsb_db = default_db(); } if (!ovnnb_db) { ovnnb_db = default_db(); } free(short_options); } static void add_column_noalert(struct ovsdb_idl *idl, const struct ovsdb_idl_column *column) { ovsdb_idl_add_column(idl, column); ovsdb_idl_omit_alert(idl, column); } int main(int argc, char *argv[]) { unsigned int ovnnb_seqno, ovnsb_seqno; int res = EXIT_SUCCESS; struct unixctl_server *unixctl; int retval; bool exiting; fatal_ignore_sigpipe(); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); daemonize_start(false); retval = unixctl_server_create(NULL, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting); daemonize_complete(); nbrec_init(); sbrec_init(); /* We want to detect all changes to the ovn-nb db. */ struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true)); struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true)); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_logical_datapath); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_datapath); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_tunnel_key); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_datapath_binding_col_tunnel_key); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_datapath_binding_col_external_ids); ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_logical_port); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tunnel_key); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_parent_port); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis); ovnnb_seqno = ovsdb_idl_get_seqno(ovnnb_idl_loop.idl); ovnsb_seqno = ovsdb_idl_get_seqno(ovnsb_idl_loop.idl); /* Main loop. */ exiting = false; while (!exiting) { struct northd_context ctx = { .ovnnb_idl = ovnnb_idl_loop.idl, .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; if (ovnnb_seqno != ovsdb_idl_get_seqno(ctx.ovnnb_idl)) { ovnnb_seqno = ovsdb_idl_get_seqno(ctx.ovnnb_idl); ovnnb_db_run(&ctx); } if (ovnsb_seqno != ovsdb_idl_get_seqno(ctx.ovnsb_idl)) { ovnsb_seqno = ovsdb_idl_get_seqno(ctx.ovnsb_idl); ovnsb_db_run(&ctx); } unixctl_server_run(unixctl); unixctl_server_wait(unixctl); if (exiting) { poll_immediate_wake(); } ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop); ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); poll_block(); if (should_service_stop()) { exiting = true; } } unixctl_server_destroy(unixctl); ovsdb_idl_loop_destroy(&ovnnb_idl_loop); ovsdb_idl_loop_destroy(&ovnsb_idl_loop); service_stop(); free(default_db_); exit(res); } static void ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } openvswitch-2.5.9/ovn/PaxHeaders.82075/automake.mk0000644000000000000000000000013013534540071016603 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.609852158 openvswitch-2.5.9/ovn/automake.mk0000644000175000017500000000556413534540071020305 0ustar00jpettitjpettit00000000000000# OVN southbound schema and IDL EXTRA_DIST += ovn/ovn-sb.ovsschema pkgdata_DATA += ovn/ovn-sb.ovsschema # OVN southbound E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_PYTHON if HAVE_DOT ovn/ovn-sb.gv: ovsdb/ovsdb-dot.in ovn/ovn-sb.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-sb.ovsschema > $@ ovn/ovn-sb.pic: ovn/ovn-sb.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < ovn/ovn-sb.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ OVN_SB_PIC = ovn/ovn-sb.pic OVN_SB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_SB_PIC) DISTCLEANFILES += ovn/ovn-sb.gv ovn/ovn-sb.pic endif endif # OVN southbound schema documentation EXTRA_DIST += ovn/ovn-sb.xml DISTCLEANFILES += ovn/ovn-sb.5 man_MANS += ovn/ovn-sb.5 ovn/ovn-sb.5: \ ovsdb/ovsdb-doc ovn/ovn-sb.xml ovn/ovn-sb.ovsschema $(OVN_SB_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(OVN_SB_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/ovn/ovn-sb.ovsschema \ $(srcdir)/ovn/ovn-sb.xml > $@.tmp && \ mv $@.tmp $@ # OVN northbound schema and IDL EXTRA_DIST += ovn/ovn-nb.ovsschema pkgdata_DATA += ovn/ovn-nb.ovsschema # OVN northbound E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_PYTHON if HAVE_DOT ovn/ovn-nb.gv: ovsdb/ovsdb-dot.in ovn/ovn-nb.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-nb.ovsschema > $@ ovn/ovn-nb.pic: ovn/ovn-nb.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < ovn/ovn-nb.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ OVN_NB_PIC = ovn/ovn-nb.pic OVN_NB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_NB_PIC) DISTCLEANFILES += ovn/ovn-nb.gv ovn/ovn-nb.pic endif endif # OVN northbound schema documentation EXTRA_DIST += ovn/ovn-nb.xml DISTCLEANFILES += ovn/ovn-nb.5 man_MANS += ovn/ovn-nb.5 ovn/ovn-nb.5: \ ovsdb/ovsdb-doc ovn/ovn-nb.xml ovn/ovn-nb.ovsschema $(OVN_NB_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(OVN_NB_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/ovn/ovn-nb.ovsschema \ $(srcdir)/ovn/ovn-nb.xml > $@.tmp && \ mv $@.tmp $@ man_MANS += ovn/ovn-architecture.7 EXTRA_DIST += ovn/ovn-architecture.7.xml DISTCLEANFILES += ovn/ovn-architecture.7 EXTRA_DIST += \ ovn/TODO \ ovn/CONTAINERS.OpenStack.md \ ovn/OVN-GW-HA.md # Version checking for ovn-nb.ovsschema. ALL_LOCAL += ovn/ovn-nb.ovsschema.stamp ovn/ovn-nb.ovsschema.stamp: ovn/ovn-nb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += ovn/ovn-nb.ovsschema.stamp # Version checking for ovn-sb.ovsschema. ALL_LOCAL += ovn/ovn-sb.ovsschema.stamp ovn/ovn-sb.ovsschema.stamp: ovn/ovn-sb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += ovn/ovn-sb.ovsschema.stamp include ovn/controller/automake.mk include ovn/controller-vtep/automake.mk include ovn/lib/automake.mk include ovn/northd/automake.mk include ovn/utilities/automake.mk openvswitch-2.5.9/ovn/PaxHeaders.82075/utilities0000644000000000000000000000013213534540121016400 xustar0030 mtime=1567801425.133856021 30 atime=1567801425.625859648 30 ctime=1567801425.133856021 openvswitch-2.5.9/ovn/utilities/0000755000175000017500000000000013534540121020143 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-ctl.8.xml0000644000000000000000000000013113534540071020732 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 29 ctime=1567801424.51385145 openvswitch-2.5.9/ovn/utilities/ovn-ctl.8.xml0000644000175000017500000000354013534540071022423 0ustar00jpettitjpettit00000000000000

    Name

    ovn-ctl -- Open Virtual Network northbound daemon lifecycle utility

    Synopsys

    ovn-ctl [options] command

    Description

    This program is intended to be invoked internally by Open Virtual Network startup scripts. System administrators should not normally invoke it directly.

    Commands

    start_northd
    start_controller
    stop_northd
    stop_controller
    restart_northd
    restart_controller

    Options

    --ovn-northd-priority=NICE

    --ovn-northd-wrapper=WRAPPER

    --ovn-controller-priority=NICE

    --ovn-controller-wrapper=WRAPPER

    -h | --help

    File location options

    --db-sock==SOCKET

    --db-nb-file==FILE

    --db-sb-file==FILE

    --db-nb-schema==FILE

    --db-sb-schema==FILE

    Example Usage

    Run ovn-controller on a host already running OVS

    # ovn-ctl start_controller

    Run ovn-northd on a host already running OVS

    # ovn-ctl start_northd

    All-in-one OVS+OVN for testing

    # ovs-ctl start --system-id="random"

    # ovn-ctl start_northd

    # ovn-ctl start_controller

    openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-docker-underlay-driver0000644000000000000000000000013113534540071023564 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 29 ctime=1567801424.51385145 openvswitch-2.5.9/ovn/utilities/ovn-docker-underlay-driver0000755000175000017500000005017713534540071025270 0ustar00jpettitjpettit00000000000000#! /usr/bin/python # Copyright (C) 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import atexit import getpass import json import os import re import shlex import subprocess import sys import time import uuid import ovs.dirs import ovs.util import ovs.daemon import ovs.unixctl.server import ovs.vlog from neutronclient.v2_0 import client from flask import Flask, jsonify from flask import request, abort app = Flask(__name__) vlog = ovs.vlog.Vlog("ovn-docker-underlay-driver") AUTH_STRATEGY = "" AUTH_URL = "" ENDPOINT_URL = "" OVN_BRIDGE = "" PASSWORD = "" PLUGIN_DIR = "/etc/docker/plugins" PLUGIN_FILE = "/etc/docker/plugins/openvswitch.spec" TENANT_ID = "" USERNAME = "" VIF_ID = "" def call_popen(cmd): child = subprocess.Popen(cmd, stdout=subprocess.PIPE) output = child.communicate() if child.returncode: raise RuntimeError("Fatal error executing %s" % (cmd)) if len(output) == 0 or output[0] == None: output = "" else: output = output[0].strip() return output def call_prog(prog, args_list): cmd = [prog, "--timeout=5", "-vconsole:off"] + args_list return call_popen(cmd) def ovs_vsctl(*args): return call_prog("ovs-vsctl", list(args)) def cleanup(): if os.path.isfile(PLUGIN_FILE): os.remove(PLUGIN_FILE) def ovn_init_underlay(args): global USERNAME, PASSWORD, TENANT_ID, AUTH_URL, AUTH_STRATEGY, VIF_ID global OVN_BRIDGE if not args.bridge: sys.exit("OVS bridge name not provided") OVN_BRIDGE = args.bridge VIF_ID = os.environ.get('OS_VIF_ID', '') if not VIF_ID: sys.exit("env OS_VIF_ID not set") USERNAME = os.environ.get('OS_USERNAME', '') if not USERNAME: sys.exit("env OS_USERNAME not set") TENANT_ID = os.environ.get('OS_TENANT_ID', '') if not TENANT_ID: sys.exit("env OS_TENANT_ID not set") AUTH_URL = os.environ.get('OS_AUTH_URL', '') if not AUTH_URL: sys.exit("env OS_AUTH_URL not set") AUTH_STRATEGY = "keystone" PASSWORD = os.environ.get('OS_PASSWORD', '') if not PASSWORD: PASSWORD = getpass.getpass() def prepare(): parser = argparse.ArgumentParser() parser.add_argument('--bridge', help="The Bridge to which containers " "interfaces connect to.") ovs.vlog.add_args(parser) ovs.daemon.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) ovs.daemon.handle_args(args) ovn_init_underlay(args) if not os.path.isdir(PLUGIN_DIR): os.makedirs(PLUGIN_DIR) ovs.daemon.daemonize() try: fo = open(PLUGIN_FILE, "w") fo.write("tcp://127.0.0.1:5000") fo.close() except Exception as e: ovs.util.ovs_fatal(0, "Failed to write to spec file (%s)" % str(e), vlog) atexit.register(cleanup) @app.route('/Plugin.Activate', methods=['POST']) def plugin_activate(): return jsonify({"Implements": ["NetworkDriver"]}) @app.route('/NetworkDriver.GetCapabilities', methods=['POST']) def get_capability(): return jsonify({"Scope": "global"}) @app.route('/NetworkDriver.DiscoverNew', methods=['POST']) def new_discovery(): return jsonify({}) @app.route('/NetworkDriver.DiscoverDelete', methods=['POST']) def delete_discovery(): return jsonify({}) def neutron_login(): try: neutron = client.Client(username=USERNAME, password=PASSWORD, tenant_id=TENANT_ID, auth_url=AUTH_URL, endpoint_url=ENDPOINT_URL, auth_strategy=AUTH_STRATEGY) except Exception as e: raise RuntimeError("Failed to login into Neutron(%s)" % str(e)) return neutron def get_networkuuid_by_name(neutron, name): param = {'fields': 'id', 'name': name} ret = neutron.list_networks(**param) if len(ret['networks']) > 1: raise RuntimeError("More than one network for the given name") elif len(ret['networks']) == 0: network = None else: network = ret['networks'][0]['id'] return network def get_subnetuuid_by_name(neutron, name): param = {'fields': 'id', 'name': name} ret = neutron.list_subnets(**param) if len(ret['subnets']) > 1: raise RuntimeError("More than one subnet for the given name") elif len(ret['subnets']) == 0: subnet = None else: subnet = ret['subnets'][0]['id'] return subnet @app.route('/NetworkDriver.CreateNetwork', methods=['POST']) def create_network(): if not request.data: abort(400) data = json.loads(request.data) # NetworkID will have docker generated network uuid and it # becomes 'name' in a neutron network record. network = data.get("NetworkID", "") if not network: abort(400) # Limit subnet handling to ipv4 till ipv6 usecase is clear. ipv4_data = data.get("IPv4Data", "") if not ipv4_data: error = "create_network: No ipv4 subnet provided" return jsonify({'Err': error}) subnet = ipv4_data[0].get("Pool", "") if not subnet: error = "create_network: no subnet in ipv4 data from libnetwork" return jsonify({'Err': error}) gateway_ip = ipv4_data[0].get("Gateway", "").rsplit('/', 1)[0] if not gateway_ip: error = "create_network: no gateway in ipv4 data from libnetwork" return jsonify({'Err': error}) try: neutron = neutron_login() except Exception as e: error = "create_network: neutron login. (%s)" % (str(e)) return jsonify({'Err': error}) try: if get_networkuuid_by_name(neutron, network): error = "create_network: network has already been created" return jsonify({'Err': error}) except Exception as e: error = "create_network: neutron network uuid by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: body = {'network': {'name': network, 'admin_state_up': True}} ret = neutron.create_network(body) network_id = ret['network']['id'] except Exception as e: error = "create_network: neutron net-create call. (%s)" % str(e) return jsonify({'Err': error}) subnet_name = "docker-%s" % (network) try: body = {'subnet': {'network_id': network_id, 'ip_version': 4, 'cidr': subnet, 'gateway_ip': gateway_ip, 'name': subnet_name}} created_subnet = neutron.create_subnet(body) except Exception as e: error = "create_network: neutron subnet-create call. (%s)" % str(e) return jsonify({'Err': error}) return jsonify({}) @app.route('/NetworkDriver.DeleteNetwork', methods=['POST']) def delete_network(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) try: neutron = neutron_login() except Exception as e: error = "delete_network: neutron login. (%s)" % (str(e)) return jsonify({'Err': error}) try: network = get_networkuuid_by_name(neutron, nid) if not network: error = "delete_network: failed in network by name. (%s)" % (nid) return jsonify({'Err': error}) except Exception as e: error = "delete_network: network uuid by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: neutron.delete_network(network) except Exception as e: error = "delete_network: neutron net-delete. (%s)" % str(e) return jsonify({'Err': error}) return jsonify({}) def reserve_vlan(): reserved_vlan = 0 vlans = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:vlans").strip('"') if not vlans: reserved_vlan = 1 ovs_vsctl("set", "Open_vSwitch", ".", "external_ids:vlans=" + str(reserved_vlan)) return reserved_vlan vlan_set = str(vlans).split(',') for vlan in range(1, 4095): if str(vlan) not in vlan_set: vlan_set.append(str(vlan)) reserved_vlan = vlan vlans = re.sub(r'[ \[\]\']', '', str(vlan_set)) ovs_vsctl("set", "Open_vSwitch", ".", "external_ids:vlans=" + vlans) return reserved_vlan if not reserved_vlan: raise RuntimeError("No more vlans available on this host") def unreserve_vlan(reserved_vlan): vlans = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:vlans").strip('"') if not vlans: return vlan_set = str(vlans).split(',') if str(reserved_vlan) not in vlan_set: return vlan_set.remove(str(reserved_vlan)) vlans = re.sub(r'[ \[\]\']', '', str(vlan_set)) if vlans: ovs_vsctl("set", "Open_vSwitch", ".", "external_ids:vlans=" + vlans) else: ovs_vsctl("remove", "Open_vSwitch", ".", "external_ids", "vlans") def create_port_underlay(neutron, network, eid, ip_address, mac_address): reserved_vlan = reserve_vlan() if mac_address: body = {'port': {'network_id': network, 'binding:profile': {'parent_name': VIF_ID, 'tag': int(reserved_vlan)}, 'mac_address': mac_address, 'fixed_ips': [{'ip_address': ip_address}], 'name': eid, 'admin_state_up': True}} else: body = {'port': {'network_id': network, 'binding:profile': {'parent_name': VIF_ID, 'tag': int(reserved_vlan)}, 'fixed_ips': [{'ip_address': ip_address}], 'name': eid, 'admin_state_up': True}} try: ret = neutron.create_port(body) mac_address = ret['port']['mac_address'] except Exception as e: unreserve_vlan(reserved_vlan) raise RuntimeError("Failed in creation of neutron port (%s)." % str(e)) ovs_vsctl("set", "Open_vSwitch", ".", "external_ids:" + eid + "_vlan=" + str(reserved_vlan)) return mac_address def get_endpointuuid_by_name(neutron, name): param = {'fields': 'id', 'name': name} ret = neutron.list_ports(**param) if len(ret['ports']) > 1: raise RuntimeError("More than one endpoint for the given name") elif len(ret['ports']) == 0: endpoint = None else: endpoint = ret['ports'][0]['id'] return endpoint @app.route('/NetworkDriver.CreateEndpoint', methods=['POST']) def create_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) interface = data.get("Interface", "") if not interface: error = "create_endpoint: no interfaces supplied by libnetwork" return jsonify({'Err': error}) ip_address_and_mask = interface.get("Address", "") if not ip_address_and_mask: error = "create_endpoint: ip address not provided by libnetwork" return jsonify({'Err': error}) ip_address = ip_address_and_mask.rsplit('/', 1)[0] mac_address_input = interface.get("MacAddress", "") mac_address_output = "" try: neutron = neutron_login() except Exception as e: error = "create_endpoint: neutron login. (%s)" % (str(e)) return jsonify({'Err': error}) try: endpoint = get_endpointuuid_by_name(neutron, eid) if endpoint: error = "create_endpoint: Endpoint has already been created" return jsonify({'Err': error}) except Exception as e: error = "create_endpoint: endpoint uuid by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: network = get_networkuuid_by_name(neutron, nid) if not network: error = "Failed to get neutron network record for (%s)" % (nid) return jsonify({'Err': error}) except Exception as e: error = "create_endpoint: network uuid by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: mac_address = create_port_underlay(neutron, network, eid, ip_address, mac_address_input) except Exception as e: error = "create_endpoint: neutron port-create (%s)" % (str(e)) return jsonify({'Err': error}) if not mac_address_input: mac_address_output = mac_address return jsonify({"Interface": { "Address": "", "AddressIPv6": "", "MacAddress": mac_address_output }}) @app.route('/NetworkDriver.EndpointOperInfo', methods=['POST']) def show_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) try: neutron = neutron_login() except Exception as e: error = "%s" % (str(e)) return jsonify({'Err': error}) try: endpoint = get_endpointuuid_by_name(neutron, eid) if not endpoint: error = "show_endpoint: Failed to get endpoint by name" return jsonify({'Err': error}) except Exception as e: error = "show_endpoint: get endpoint by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: ret = neutron.show_port(endpoint) mac_address = ret['port']['mac_address'] ip_address = ret['port']['fixed_ips'][0]['ip_address'] except Exception as e: error = "show_endpoint: show port (%s)" % (str(e)) return jsonify({'Err': error}) veth_outside = eid[0:15] return jsonify({"Value": {"ip_address": ip_address, "mac_address": mac_address, "veth_outside": veth_outside }}) @app.route('/NetworkDriver.DeleteEndpoint', methods=['POST']) def delete_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) try: neutron = neutron_login() except Exception as e: error = "delete_endpoint: neutron login (%s)" % (str(e)) return jsonify({'Err': error}) endpoint = get_endpointuuid_by_name(neutron, eid) if not endpoint: return jsonify({}) reserved_vlan = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:" + eid + "_vlan").strip('"') if reserved_vlan: unreserve_vlan(reserved_vlan) ovs_vsctl("remove", "Open_vSwitch", ".", "external_ids", eid + "_vlan") try: neutron.delete_port(endpoint) except Exception as e: error = "delete_endpoint: neutron port-delete. (%s)" % (str(e)) return jsonify({'Err': error}) return jsonify({}) @app.route('/NetworkDriver.Join', methods=['POST']) def network_join(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) sboxkey = data.get("SandboxKey", "") if not sboxkey: abort(400) # sboxkey is of the form: /var/run/docker/netns/CONTAINER_ID vm_id = sboxkey.rsplit('/')[-1] try: neutron = neutron_login() except Exception as e: error = "network_join: neutron login. (%s)" % (str(e)) return jsonify({'Err': error}) subnet_name = "docker-%s" % (nid) try: subnet = get_subnetuuid_by_name(neutron, subnet_name) if not subnet: error = "network_join: can't find subnet in neutron" return jsonify({'Err': error}) except Exception as e: error = "network_join: subnet uuid by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: ret = neutron.show_subnet(subnet) gateway_ip = ret['subnet']['gateway_ip'] if not gateway_ip: error = "network_join: no gateway_ip for the subnet" return jsonify({'Err': error}) except Exception as e: error = "network_join: neutron show subnet. (%s)" % (str(e)) return jsonify({'Err': error}) try: endpoint = get_endpointuuid_by_name(neutron, eid) if not endpoint: error = "network_join: Failed to get endpoint by name" return jsonify({'Err': error}) except Exception as e: error = "network_join: neutron endpoint by name. (%s)" % (str(e)) return jsonify({'Err': error}) try: ret = neutron.show_port(endpoint) mac_address = ret['port']['mac_address'] except Exception as e: error = "network_join: neutron show port. (%s)" % (str(e)) return jsonify({'Err': error}) veth_outside = eid[0:15] veth_inside = eid[0:13] + "_c" command = "ip link add %s type veth peer name %s" \ % (veth_inside, veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to create veth pair. (%s)" % (str(e)) return jsonify({'Err': error}) command = "ip link set dev %s address %s" \ % (veth_inside, mac_address) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to set veth mac address. (%s)" % (str(e)) return jsonify({'Err': error}) command = "ip link set %s up" % (veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to up the veth iface. (%s)" % (str(e)) return jsonify({'Err': error}) try: reserved_vlan = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:" + eid + "_vlan").strip('"') if not reserved_vlan: error = "network_join: no reserved vlan for this endpoint" return jsonify({'Err': error}) ovs_vsctl("add-port", OVN_BRIDGE, veth_outside, "tag=" + reserved_vlan) except Exception as e: error = "network_join: failed to create a OVS port. (%s)" % (str(e)) return jsonify({'Err': error}) return jsonify({"InterfaceName": { "SrcName": veth_inside, "DstPrefix": "eth" }, "Gateway": gateway_ip, "GatewayIPv6": ""}) @app.route('/NetworkDriver.Leave', methods=['POST']) def network_leave(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) veth_outside = eid[0:15] command = "ip link delete %s" % (veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_leave: failed to delete veth pair. (%s)" % (str(e)) return jsonify({'Err': error}) try: ovs_vsctl("--if-exists", "del-port", veth_outside) except Exception as e: error = "network_leave: Failed to delete port (%s)" % (str(e)) return jsonify({'Err': error}) return jsonify({}) if __name__ == '__main__': prepare() app.run(host='127.0.0.1') openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-docker-overlay-driver0000644000000000000000000000013113534540071023422 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 29 ctime=1567801424.51385145 openvswitch-2.5.9/ovn/utilities/ovn-docker-overlay-driver0000755000175000017500000003047613534540071025126 0ustar00jpettitjpettit00000000000000#! /usr/bin/python # Copyright (C) 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import ast import atexit import json import os import random import re import shlex import subprocess import sys import ovs.dirs import ovs.util import ovs.daemon import ovs.vlog from flask import Flask, jsonify from flask import request, abort app = Flask(__name__) vlog = ovs.vlog.Vlog("ovn-docker-overlay-driver") OVN_BRIDGE = "br-int" OVN_REMOTE = "" PLUGIN_DIR = "/etc/docker/plugins" PLUGIN_FILE = "/etc/docker/plugins/openvswitch.spec" def call_popen(cmd): child = subprocess.Popen(cmd, stdout=subprocess.PIPE) output = child.communicate() if child.returncode: raise RuntimeError("Fatal error executing %s" % (cmd)) if len(output) == 0 or output[0] == None: output = "" else: output = output[0].strip() return output def call_prog(prog, args_list): cmd = [prog, "--timeout=5", "-vconsole:off"] + args_list return call_popen(cmd) def ovs_vsctl(*args): return call_prog("ovs-vsctl", list(args)) def ovn_nbctl(*args): args_list = list(args) database_option = "%s=%s" % ("--db", OVN_REMOTE) args_list.insert(0, database_option) return call_prog("ovn-nbctl", args_list) def cleanup(): if os.path.isfile(PLUGIN_FILE): os.remove(PLUGIN_FILE) def ovn_init_overlay(): br_list = ovs_vsctl("list-br").split() if OVN_BRIDGE not in br_list: ovs_vsctl("--", "--may-exist", "add-br", OVN_BRIDGE, "--", "set", "bridge", OVN_BRIDGE, "external_ids:bridge-id=" + OVN_BRIDGE, "other-config:disable-in-band=true", "fail-mode=secure") global OVN_REMOTE OVN_REMOTE = ovs_vsctl("get", "Open_vSwitch", ".", "external_ids:ovn-remote").strip('"') if not OVN_REMOTE: sys.exit("OVN central database's ip address not set") ovs_vsctl("set", "open_vswitch", ".", "external_ids:ovn-bridge=" + OVN_BRIDGE) def prepare(): parser = argparse.ArgumentParser() ovs.vlog.add_args(parser) ovs.daemon.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) ovs.daemon.handle_args(args) ovn_init_overlay() if not os.path.isdir(PLUGIN_DIR): os.makedirs(PLUGIN_DIR) ovs.daemon.daemonize() try: fo = open(PLUGIN_FILE, "w") fo.write("tcp://0.0.0.0:5000") fo.close() except Exception as e: ovs.util.ovs_fatal(0, "Failed to write to spec file (%s)" % str(e), vlog) atexit.register(cleanup) @app.route('/Plugin.Activate', methods=['POST']) def plugin_activate(): return jsonify({"Implements": ["NetworkDriver"]}) @app.route('/NetworkDriver.GetCapabilities', methods=['POST']) def get_capability(): return jsonify({"Scope": "global"}) @app.route('/NetworkDriver.DiscoverNew', methods=['POST']) def new_discovery(): return jsonify({}) @app.route('/NetworkDriver.DiscoverDelete', methods=['POST']) def delete_discovery(): return jsonify({}) @app.route('/NetworkDriver.CreateNetwork', methods=['POST']) def create_network(): if not request.data: abort(400) data = json.loads(request.data) # NetworkID will have docker generated network uuid and it # becomes 'name' in a OVN Logical switch record. network = data.get("NetworkID", "") if not network: abort(400) # Limit subnet handling to ipv4 till ipv6 usecase is clear. ipv4_data = data.get("IPv4Data", "") if not ipv4_data: error = "create_network: No ipv4 subnet provided" return jsonify({'Err': error}) subnet = ipv4_data[0].get("Pool", "") if not subnet: error = "create_network: no subnet in ipv4 data from libnetwork" return jsonify({'Err': error}) gateway_ip = ipv4_data[0].get("Gateway", "").rsplit('/', 1)[0] if not gateway_ip: error = "create_network: no gateway in ipv4 data from libnetwork" return jsonify({'Err': error}) try: ovn_nbctl("lswitch-add", network, "--", "set", "Logical_Switch", network, "external_ids:subnet=" + subnet, "external_ids:gateway_ip=" + gateway_ip) except Exception as e: error = "create_network: lswitch-add %s" % (str(e)) return jsonify({'Err': error}) return jsonify({}) @app.route('/NetworkDriver.DeleteNetwork', methods=['POST']) def delete_network(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) try: ovn_nbctl("lswitch-del", nid) except Exception as e: error = "delete_network: lswitch-del %s" % (str(e)) return jsonify({'Err': error}) return jsonify({}) @app.route('/NetworkDriver.CreateEndpoint', methods=['POST']) def create_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) interface = data.get("Interface", "") if not interface: error = "create_endpoint: no interfaces structure supplied by " \ "libnetwork" return jsonify({'Err': error}) ip_address_and_mask = interface.get("Address", "") if not ip_address_and_mask: error = "create_endpoint: ip address not provided by libnetwork" return jsonify({'Err': error}) ip_address = ip_address_and_mask.rsplit('/', 1)[0] mac_address_input = interface.get("MacAddress", "") mac_address_output = "" try: ovn_nbctl("lport-add", nid, eid) except Exception as e: error = "create_endpoint: lport-add (%s)" % (str(e)) return jsonify({'Err': error}) if not mac_address_input: mac_address = "02:%02x:%02x:%02x:%02x:%02x" % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) else: mac_address = mac_address_input try: ovn_nbctl("lport-set-addresses", eid, mac_address + " " + ip_address) except Exception as e: error = "create_endpoint: lport-set-addresses (%s)" % (str(e)) return jsonify({'Err': error}) # Only return a mac address if one did not come as request. mac_address_output = "" if not mac_address_input: mac_address_output = mac_address return jsonify({"Interface": { "Address": "", "AddressIPv6": "", "MacAddress": mac_address_output }}) def get_logical_port_addresses(eid): ret = ovn_nbctl("--if-exists", "get", "Logical_port", eid, "addresses") if not ret: error = "endpoint not found in OVN database" return (None, None, error) addresses = ast.literal_eval(ret) if len(addresses) == 0: error = "unexpected return while fetching addresses" return (None, None, error) (mac_address, ip_address) = addresses[0].split() return (mac_address, ip_address, None) @app.route('/NetworkDriver.EndpointOperInfo', methods=['POST']) def show_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) try: (mac_address, ip_address, error) = get_logical_port_addresses(eid) if error: jsonify({'Err': error}) except Exception as e: error = "show_endpoint: get Logical_port addresses. (%s)" % (str(e)) return jsonify({'Err': error}) veth_outside = eid[0:15] return jsonify({"Value": {"ip_address": ip_address, "mac_address": mac_address, "veth_outside": veth_outside }}) @app.route('/NetworkDriver.DeleteEndpoint', methods=['POST']) def delete_endpoint(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) try: ovn_nbctl("lport-del", eid) except Exception as e: error = "delete_endpoint: lport-del %s" % (str(e)) return jsonify({'Err': error}) return jsonify({}) @app.route('/NetworkDriver.Join', methods=['POST']) def network_join(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) sboxkey = data.get("SandboxKey", "") if not sboxkey: abort(400) # sboxkey is of the form: /var/run/docker/netns/CONTAINER_ID vm_id = sboxkey.rsplit('/')[-1] try: (mac_address, ip_address, error) = get_logical_port_addresses(eid) if error: jsonify({'Err': error}) except Exception as e: error = "network_join: %s" % (str(e)) return jsonify({'Err': error}) veth_outside = eid[0:15] veth_inside = eid[0:13] + "_c" command = "ip link add %s type veth peer name %s" \ % (veth_inside, veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to create veth pair (%s)" % (str(e)) return jsonify({'Err': error}) command = "ip link set dev %s address %s" \ % (veth_inside, mac_address) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to set veth mac address (%s)" % (str(e)) return jsonify({'Err': error}) command = "ip link set %s up" % (veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_join: failed to up the veth interface (%s)" % (str(e)) return jsonify({'Err': error}) try: ovs_vsctl("add-port", OVN_BRIDGE, veth_outside) ovs_vsctl("set", "interface", veth_outside, "external_ids:attached-mac=" + mac_address, "external_ids:iface-id=" + eid, "external_ids:vm-id=" + vm_id, "external_ids:iface-status=active") except Exception as e: error = "network_join: failed to create a port (%s)" % (str(e)) return jsonify({'Err': error}) return jsonify({"InterfaceName": { "SrcName": veth_inside, "DstPrefix": "eth" }, "Gateway": "", "GatewayIPv6": ""}) @app.route('/NetworkDriver.Leave', methods=['POST']) def network_leave(): if not request.data: abort(400) data = json.loads(request.data) nid = data.get("NetworkID", "") if not nid: abort(400) eid = data.get("EndpointID", "") if not eid: abort(400) veth_outside = eid[0:15] command = "ip link delete %s" % (veth_outside) try: call_popen(shlex.split(command)) except Exception as e: error = "network_leave: failed to delete veth pair (%s)" % (str(e)) return jsonify({'Err': error}) try: ovs_vsctl("--if-exists", "del-port", veth_outside) except Exception as e: error = "network_leave: failed to delete port (%s)" % (str(e)) return jsonify({'Err': error}) return jsonify({}) if __name__ == '__main__': prepare() app.run(host='0.0.0.0') openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-sbctl.8.in0000644000000000000000000000013213534540071021066 xustar0030 mtime=1567801401.797684078 30 atime=1567801402.117686428 30 ctime=1567801423.785846083 openvswitch-2.5.9/ovn/utilities/ovn-sbctl.8.in0000644000175000017500000001410613534540071022556 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .TH ovn\-sbctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovn\-sbctl . .SH NAME ovn\-sbctl \- utility for querying and configuring \fBOVN_Southbound\fR database . .SH SYNOPSIS \fBovn\-sbctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]... . .SH DESCRIPTION The command should only be used for advanced debugging and troubleshooting of the \fBOVN_Southbound\fR database; and should never be used in normal operation. .PP The \fBovn\-sbctl\fR program configures the \fBOVN_Southbound\fR database by providing a high\-level interface to its configuration database. See \fBovn\-sb\fR(5) for comprehensive documentation of the database schema. .PP \fBovn\-sbctl\fR connects to an \fBovsdb\-server\fR process that maintains an OVN_Southbound configuration database. Using this connection, it queries and possibly applies changes to the database, depending on the supplied commands. .PP \fBovn\-sbctl\fR can perform any number of commands in a single run, implemented as a single atomic transaction against the database. .PP The \fBovn\-sbctl\fR command line begins with global options (see \fBOPTIONS\fR below for details). The global options are followed by one or more commands. Each command should begin with \fB\-\-\fR by itself as a command-line argument, to separate it from the following commands. (The \fB\-\-\fR before the first command is optional.) The command itself starts with command-specific options, if any, followed by the command name and any arguments. . .SH OPTIONS . The following options affect the behavior of \fBovn\-sbctl\fR as a whole. Some individual commands also accept their own options, which are given just before the command name. If the first command on the command line has options, then those options must be separated from the global options by \fB\-\-\fR. . .IP "\fB\-\-db=\fIserver\fR" The OVSDB database remote to contact. If the \fBOVN_SB_DB\fR environment variable is set, its value is used as the default. Otherwise, the default is \fBunix:@RUNDIR@/db.sock\fR, but this default is unlikely to be useful outside of single-machine OVN test environments. .IP \fIserver\fR must take one of the following forms: .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE . .IP "\fB\-\-no\-syslog\fR" By default, \fBovn\-sbctl\fR logs its arguments and the details of any changes that it makes to the system log. This option disables this logging. .IP This option is equivalent to \fB\-\-verbose=sbctl:syslog:warn\fR. . .IP "\fB\-\-oneline\fR" Modifies the output format so that the output for each command is printed on a single line. New-line characters that would otherwise separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that would otherwise appear in the output are doubled. Prints a blank line for each command that has no output. This option does not affect the formatting of output from the \fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR below. . .IP "\fB\-\-dry\-run\fR" Prevents \fBovn\-sbctl\fR from actually modifying the database. . .IP "\fB\-t \fIsecs\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" By default, or with a \fIsecs\fR of \fB0\fR, \fBovn\-sbctl\fR waits forever for a response from the database. This option limits runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBovn\-sbctl\fR will exit with a \fBSIGALRM\fR signal. (A timeout would normally happen only if the database cannot be contacted, or if the system is overloaded.) . .SS "Table Formatting Options" These options control the format of output from the \fBlist\fR and \fBfind\fR commands. .so lib/table.man . .SH COMMANDS The commands implemented by \fBovn\-sbctl\fR are described in the sections below. .SS "OVN_Southbound Commands" These commands work with an \fBOVN_Southbound\fR database as a whole. . .IP "\fBshow\fR" Prints a brief overview of the database contents. . .SS "Chassis Commands" These commands manipulate \fBOVN_Southbound\fR chassis. . .IP "[\fB\-\-may\-exist\fR] \fBchassis\-add \fIchassis\fR \fIencap\-type\fR \fIencap-ip\fR" Creates a new chassis named \fIchassis\fR. \fIencap\-type\fR is a comma-separated list of tunnel types. The chassis will have one encap entry for each specified tunnel type with \fIencap-ip\fR as the destination IP for each. .IP Without \fB\-\-may\-exist\fR, attempting to create a chassis that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIchassis\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBchassis\-del \fIchassis\fR" Deletes \fIchassis\fR and its \fIencaps\fR and \fIgateway_ports\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a chassis that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a chassis that does not exist has no effect. . .SS "Port binding Commands" . These commands manipulate \fBOVN_Southbound\fR port bindings. . .IP "[\fB\-\-may\-exist\fR] \fBlport\-bind \fIlogical\-port\fR \fIchassis\fR" Binds the logical port named \fIlogical\-port\fR to \fIchassis\fR. .IP Without \fB\-\-may\-exist\fR, attempting to bind a logical port that has already been bound is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIlogical\-port\fR has already been bound to a chassis. . .IP "[\fB\-\-if\-exists\fR] \fBlport\-unbind\fR \fIlogical\-port\fR" Resets the binding of \fIlogical\-port\fR to \fINULL\fR. .IP Without \fB\-\-if\-exists\fR, attempting to unbind a logical port that is not bound is an error. With \fB\-\-if\-exists\fR, attempting to unbind logical port that is not bound has no effect. . .SS "Logical Flow Commands" . .IP "\fBlflow\-list\fR [\fIlogical\-datapath\fR]" List logical flows. If \fIlogical\-datapath\fR is specified, only list flows for that logical datapath. . .IP "\fBdump\-flows\fR [\fIlogical\-datapath\fR]" Alias for \fBlflow\-list\fB. . .so lib/db-ctl-base.man .SH "EXIT STATUS" .IP "0" Successful program execution. .IP "1" Usage, syntax, or configuration file error. .SH "SEE ALSO" . .BR ovn\-sb (5). openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-sbctl.c0000644000000000000000000000013213534540071020534 xustar0030 mtime=1567801401.801684107 30 atime=1567801402.117686428 30 ctime=1567801425.133856021 openvswitch-2.5.9/ovn/utilities/ovn-sbctl.c0000644000175000017500000007426113534540071022234 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "json.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "poll-loop.h" #include "process.h" #include "sset.h" #include "shash.h" #include "stream-ssl.h" #include "stream.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" VLOG_DEFINE_THIS_MODULE(sbctl); struct sbctl_context; /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --timeout: Time to wait for a connection to 'db'. */ static int timeout; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; /* The IDL we're using and the current transaction, if any. * This is for use by sbctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void sbctl_exit(int status); static void sbctl_cmd_init(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static const char *sbctl_default_db(void); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static void do_sbctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); int main(int argc, char *argv[]) { extern struct vlog_module VLM_reconnect; struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; char *args; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN); sbrec_init(); sbctl_cmd_init(); /* Log our arguments. This is often valuable for debugging systems. */ args = process_escape_args(argv); VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); /* Parse command line. */ shash_init(&local_options); parse_options(argc, argv, &local_options); commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, &n_commands); if (timeout) { time_alarm(timeout); } /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { seqno = ovsdb_idl_get_seqno(idl); do_sbctl(args, commands, n_commands, idl); } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static const char * sbctl_default_db(void) { static char *def; if (!def) { def = getenv("OVN_SB_DB"); if (!def) { def = ctl_default_db(); } } return def; } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_ONELINE, OPT_NO_SYSLOG, OPT_DRY_RUN, OPT_PEER_CA_CERT, OPT_LOCAL, OPT_COMMANDS, OPT_OPTIONS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"commands", no_argument, NULL, OPT_COMMANDS}, {"options", no_argument, NULL, OPT_OPTIONS}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, TABLE_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); table_style.format = TF_LIST; for (;;) { int idx; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&VLM_sbctl, VLF_SYSLOG, VLL_WARN); break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), optarg ? xstrdup(optarg) : NULL); break; case 'h': usage(); case OPT_COMMANDS: ctl_print_commands(); case OPT_OPTIONS: ctl_print_options(global_long_options); case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", sbrec_get_db_version()); exit(EXIT_SUCCESS); case 't': timeout = strtoul(optarg, NULL, 10); if (timeout < 0) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!db) { db = sbctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } static void usage(void) { printf("\ %s: OVN southbound DB management utility\n\ \n\ For debugging and testing only, not for use in production.\n\ \n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ General commands:\n\ show print overview of database contents\n\ \n\ Chassis commands:\n\ chassis-add CHASSIS ENCAP-TYPE ENCAP-IP create a new chassis named\n\ CHASSIS with ENCAP-TYPE tunnels\n\ and ENCAP-IP\n\ chassis-del CHASSIS delete CHASSIS and all of its encaps\n\ and gateway_ports\n\ \n\ Port binding commands:\n\ lport-bind LPORT CHASSIS bind logical port LPORT to CHASSIS\n\ lport-unbind LPORT reset the port binding of logical port LPORT\n\ \n\ Logical flow commands:\n\ lflow-list [DATAPATH] List logical flows for all or a single datapath\n\ dump-flows [DATAPATH] Alias for lflow-list\n\ \n\ %s\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ -t, --timeout=SECS wait at most SECS seconds\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db()); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=sbctl:syslog:warn\n"); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); stream_usage("database", true, true, false); exit(EXIT_SUCCESS); } /* ovs-sbctl specific context. Inherits the 'struct ctl_context' as base. */ struct sbctl_context { struct ctl_context base; /* A cache of the contents of the database. * * A command that needs to use any of this information must first call * sbctl_context_populate_cache(). A command that changes anything that * could invalidate the cache must either call * sbctl_context_invalidate_cache() or manually update the cache to * maintain its correctness. */ bool cache_valid; /* Maps from chassis name to struct sbctl_chassis. */ struct shash chassis; /* Maps from lport name to struct sbctl_port_binding. */ struct shash port_bindings; }; /* Casts 'base' into 'struct sbctl_context'. */ static struct sbctl_context * sbctl_context_cast(struct ctl_context *base) { return CONTAINER_OF(base, struct sbctl_context, base); } struct sbctl_chassis { const struct sbrec_chassis *ch_cfg; }; struct sbctl_port_binding { const struct sbrec_port_binding *bd_cfg; }; static void sbctl_context_invalidate_cache(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); if (!sbctl_ctx->cache_valid) { return; } sbctl_ctx->cache_valid = false; shash_destroy_free_data(&sbctl_ctx->chassis); shash_destroy_free_data(&sbctl_ctx->port_bindings); } static void sbctl_context_populate_cache(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); const struct sbrec_chassis *chassis_rec; const struct sbrec_port_binding *port_binding_rec; struct sset chassis, port_bindings; if (sbctl_ctx->cache_valid) { /* Cache is already populated. */ return; } sbctl_ctx->cache_valid = true; shash_init(&sbctl_ctx->chassis); shash_init(&sbctl_ctx->port_bindings); sset_init(&chassis); SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) { struct sbctl_chassis *ch; if (!sset_add(&chassis, chassis_rec->name)) { VLOG_WARN("database contains duplicate chassis name (%s)", chassis_rec->name); continue; } ch = xmalloc(sizeof *ch); ch->ch_cfg = chassis_rec; shash_add(&sbctl_ctx->chassis, chassis_rec->name, ch); } sset_destroy(&chassis); sset_init(&port_bindings); SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->idl) { struct sbctl_port_binding *bd; if (!sset_add(&port_bindings, port_binding_rec->logical_port)) { VLOG_WARN("database contains duplicate port binding for logical " "port (%s)", port_binding_rec->logical_port); continue; } bd = xmalloc(sizeof *bd); bd->bd_cfg = port_binding_rec; shash_add(&sbctl_ctx->port_bindings, port_binding_rec->logical_port, bd); } sset_destroy(&port_bindings); } static void check_conflicts(struct sbctl_context *sbctl_ctx, const char *name, char *msg) { if (shash_find(&sbctl_ctx->chassis, name)) { ctl_fatal("%s because a chassis named %s already exists", msg, name); } free(msg); } static struct sbctl_chassis * find_chassis(struct sbctl_context *sbctl_ctx, const char *name, bool must_exist) { struct sbctl_chassis *sbctl_ch; ovs_assert(sbctl_ctx->cache_valid); sbctl_ch = shash_find_data(&sbctl_ctx->chassis, name); if (must_exist && !sbctl_ch) { ctl_fatal("no chassis named %s", name); } return sbctl_ch; } static struct sbctl_port_binding * find_port_binding(struct sbctl_context *sbctl_ctx, const char *name, bool must_exist) { struct sbctl_port_binding *bd; ovs_assert(sbctl_ctx->cache_valid); bd = shash_find_data(&sbctl_ctx->port_bindings, name); if (must_exist && !bd) { ctl_fatal("no port named %s", name); } return bd; } static void pre_get_info(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_name); ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_encaps); ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type); ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip); ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_logical_port); ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_logical_datapath); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_pipeline); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_actions); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_priority); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_table_id); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_match); ovsdb_idl_add_column(ctx->idl, &sbrec_logical_flow_col_external_ids); } static struct cmd_show_table cmd_show_tables[] = { {&sbrec_table_chassis, &sbrec_chassis_col_name, {&sbrec_chassis_col_encaps, NULL, NULL}, {&sbrec_table_port_binding, &sbrec_port_binding_col_logical_port, &sbrec_port_binding_col_chassis}}, {&sbrec_table_encap, &sbrec_encap_col_type, {&sbrec_encap_col_ip, &sbrec_encap_col_options, NULL}, {NULL, NULL, NULL}}, {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}}, }; static void cmd_chassis_add(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; const char *ch_name, *encap_types, *encap_ip; ch_name = ctx->argv[1]; encap_types = ctx->argv[2]; encap_ip = ctx->argv[3]; sbctl_context_populate_cache(ctx); if (may_exist) { struct sbctl_chassis *sbctl_ch; sbctl_ch = find_chassis(sbctl_ctx, ch_name, false); if (sbctl_ch) { return; } } check_conflicts(sbctl_ctx, ch_name, xasprintf("cannot create a chassis named %s", ch_name)); char *tokstr = xstrdup(encap_types); char *token, *save_ptr = NULL; struct sset encap_set = SSET_INITIALIZER(&encap_set); for (token = strtok_r(tokstr, ",", &save_ptr); token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { sset_add(&encap_set, token); } free(tokstr); size_t n_encaps = sset_count(&encap_set); struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps); const char *encap_type; int i = 0; SSET_FOR_EACH (encap_type, &encap_set){ encaps[i] = sbrec_encap_insert(ctx->txn); sbrec_encap_set_type(encaps[i], encap_type); sbrec_encap_set_ip(encaps[i], encap_ip); i++; } sset_destroy(&encap_set); struct sbrec_chassis *ch = sbrec_chassis_insert(ctx->txn); sbrec_chassis_set_name(ch, ch_name); sbrec_chassis_set_encaps(ch, encaps, n_encaps); free(encaps); sbctl_context_invalidate_cache(ctx); } static void cmd_chassis_del(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct sbctl_chassis *sbctl_ch; sbctl_context_populate_cache(ctx); sbctl_ch = find_chassis(sbctl_ctx, ctx->argv[1], must_exist); if (sbctl_ch) { if (sbctl_ch->ch_cfg) { size_t i; for (i = 0; i < sbctl_ch->ch_cfg->n_encaps; i++) { sbrec_encap_delete(sbctl_ch->ch_cfg->encaps[i]); } sbrec_chassis_delete(sbctl_ch->ch_cfg); } shash_find_and_delete(&sbctl_ctx->chassis, ctx->argv[1]); free(sbctl_ch); } } static void cmd_lport_bind(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct sbctl_chassis *sbctl_ch; struct sbctl_port_binding *sbctl_bd; char *lport_name, *ch_name; /* port_binding must exist, chassis must exist! */ lport_name = ctx->argv[1]; ch_name = ctx->argv[2]; sbctl_context_populate_cache(ctx); sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true); sbctl_ch = find_chassis(sbctl_ctx, ch_name, true); if (sbctl_bd->bd_cfg->chassis) { if (may_exist && sbctl_bd->bd_cfg->chassis == sbctl_ch->ch_cfg) { return; } else { ctl_fatal("lport (%s) has already been binded to chassis (%s)", lport_name, sbctl_bd->bd_cfg->chassis->name); } } sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg); sbctl_context_invalidate_cache(ctx); } static void cmd_lport_unbind(struct ctl_context *ctx) { struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct sbctl_port_binding *sbctl_bd; char *lport_name; lport_name = ctx->argv[1]; sbctl_context_populate_cache(ctx); sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist); if (sbctl_bd) { sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, NULL); } } enum { PL_INGRESS, PL_EGRESS, }; /* Help ensure we catch any future pipeline values */ static int pipeline_encode(const char *pl) { if (!strcmp(pl, "ingress")) { return PL_INGRESS; } else if (!strcmp(pl, "egress")) { return PL_EGRESS; } OVS_NOT_REACHED(); } static int lflow_cmp(const void *lf1_, const void *lf2_) { const struct sbrec_logical_flow *const *lf1p = lf1_; const struct sbrec_logical_flow *const *lf2p = lf2_; const struct sbrec_logical_flow *lf1 = *lf1p; const struct sbrec_logical_flow *lf2 = *lf2p; int pl1 = pipeline_encode(lf1->pipeline); int pl2 = pipeline_encode(lf2->pipeline); #define CMP(expr) \ do { \ int res; \ res = (expr); \ if (res) { \ return res; \ } \ } while (0) CMP(uuid_compare_3way(&lf1->logical_datapath->header_.uuid, &lf2->logical_datapath->header_.uuid)); CMP(pl1 - pl2); CMP(lf1->table_id > lf2->table_id ? 1 : (lf1->table_id < lf2->table_id ? -1 : 0)); CMP(lf1->priority > lf2->priority ? -1 : (lf1->priority < lf2->priority ? 1 : 0)); CMP(strcmp(lf1->match, lf2->match)); #undef CMP return 0; } static void cmd_lflow_list(struct ctl_context *ctx) { const char *datapath = ctx->argc == 2 ? ctx->argv[1] : NULL; struct uuid datapath_uuid = { .parts = { 0, }}; const struct sbrec_logical_flow **lflows; const struct sbrec_logical_flow *lflow; size_t n_flows = 0, n_capacity = 64; if (datapath && !uuid_from_string(&datapath_uuid, datapath)) { VLOG_ERR("Invalid format of datapath UUID"); return; } lflows = xmalloc(sizeof *lflows * n_capacity); SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) { if (n_flows == n_capacity) { lflows = x2nrealloc(lflows, &n_capacity, sizeof *lflows); } lflows[n_flows] = lflow; n_flows++; } qsort(lflows, n_flows, sizeof *lflows, lflow_cmp); const char *cur_pipeline = ""; size_t i; for (i = 0; i < n_flows; i++) { lflow = lflows[i]; if (datapath && !uuid_equals(&datapath_uuid, &lflow->logical_datapath->header_.uuid)) { continue; } if (strcmp(cur_pipeline, lflow->pipeline)) { printf("Datapath: " UUID_FMT " Pipeline: %s\n", UUID_ARGS(&lflow->logical_datapath->header_.uuid), lflow->pipeline); cur_pipeline = lflow->pipeline; } const char *table_name = smap_get(&lflow->external_ids, "stage-name"); printf(" table=%" PRId64 "(%16s), priority=%5" PRId64 ", match=(%s), action=(%s)\n", lflow->table_id, table_name ? table_name : "", lflow->priority, lflow->match, lflow->actions); } free(lflows); } static const struct ctl_table_class tables[] = { {&sbrec_table_chassis, {{&sbrec_table_chassis, &sbrec_chassis_col_name, NULL}, {NULL, NULL, NULL}}}, {&sbrec_table_encap, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&sbrec_table_logical_flow, {{&sbrec_table_logical_flow, NULL, &sbrec_logical_flow_col_logical_datapath}, {NULL, NULL, NULL}}}, {&sbrec_table_multicast_group, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&sbrec_table_datapath_binding, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&sbrec_table_port_binding, {{&sbrec_table_port_binding, &sbrec_port_binding_col_logical_port, NULL}, {NULL, NULL, NULL}}}, {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; static void sbctl_context_init_command(struct sbctl_context *sbctl_ctx, struct ctl_command *command) { ctl_context_init_command(&sbctl_ctx->base, command); } static void sbctl_context_init(struct sbctl_context *sbctl_ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, struct ovsdb_symbol_table *symtab) { ctl_context_init(&sbctl_ctx->base, command, idl, txn, symtab, sbctl_context_invalidate_cache); sbctl_ctx->cache_valid = false; } static void sbctl_context_done_command(struct sbctl_context *sbctl_ctx, struct ctl_command *command) { ctl_context_done_command(&sbctl_ctx->base, command); } static void sbctl_context_done(struct sbctl_context *sbctl_ctx, struct ctl_command *command) { ctl_context_done(&sbctl_ctx->base, command); } static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct sbctl_context sbctl_ctx; ds_init(&c->output); c->table = NULL; sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL); (c->syntax->prerequisites)(&sbctl_ctx.base); sbctl_context_done(&sbctl_ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static void do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct sbctl_context sbctl_ctx; struct ctl_command *c; struct shash_node *node; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ovsdb_idl_txn_add_comment(txn, "ovs-sbctl: %s", args); symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } sbctl_context_init(&sbctl_ctx, NULL, idl, txn, symtab); for (c = commands; c < &commands[n_commands]; c++) { sbctl_context_init_command(&sbctl_ctx, c); if (c->syntax->run) { (c->syntax->run)(&sbctl_ctx.base); } sbctl_context_done_command(&sbctl_ctx, c); if (sbctl_ctx.base.try_again) { sbctl_context_done(&sbctl_ctx, NULL); goto try_again; } } sbctl_context_done(&sbctl_ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created (e.g. " "with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab); (c->syntax->postprocess)(&sbctl_ctx.base); sbctl_context_done(&sbctl_ctx, c); } } } error = xstrdup(ovsdb_idl_txn_get_error(txn)); switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", error); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } free(error); ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); ovsdb_idl_txn_destroy(txn); ovsdb_idl_destroy(idl); exit(EXIT_SUCCESS); try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ if (txn) { ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); the_idl_txn = NULL; } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } free(error); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void sbctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } static const struct ctl_command_syntax sbctl_commands[] = { /* Chassis commands. */ {"chassis-add", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info, cmd_chassis_add, NULL, "--may-exist", RW}, {"chassis-del", 1, 1, "CHASSIS", pre_get_info, cmd_chassis_del, NULL, "--if-exists", RW}, /* Port binding commands. */ {"lport-bind", 2, 2, "LPORT CHASSIS", pre_get_info, cmd_lport_bind, NULL, "--may-exist", RW}, {"lport-unbind", 1, 1, "LPORT", pre_get_info, cmd_lport_unbind, NULL, "--if-exists", RW}, /* Logical flow commands */ {"lflow-list", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL, "", RO}, {"dump-flows", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL, "", RO}, /* Friendly alias for lflow-list */ /* SSL commands (To Be Added). */ {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; /* Registers sbctl and common db commands. */ static void sbctl_cmd_init(void) { ctl_init(tables, cmd_show_tables, sbctl_exit); ctl_register_commands(sbctl_commands); } openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-nbctl.8.xml0000644000000000000000000000013213534540071021253 xustar0030 mtime=1567801401.777683932 30 atime=1567801402.117686428 30 ctime=1567801424.521851509 openvswitch-2.5.9/ovn/utilities/ovn-nbctl.8.xml0000644000175000017500000002062213534540071022743 0ustar00jpettitjpettit00000000000000

    Name

    ovn-nbctl -- Open Virtual Network northbound db management utility

    Synopsys

    ovn-nbctl [options] command [arg...]

    Description

    This utility can be used to manage the OVN northbound database.

    General Commands

    show [lswitch]
    Prints a brief overview of the database contents. If lswitch is provided, only records related to that logical switch are shown.

    Logical Switch Commands

    lswitch-add [lswitch]
    Creates a new logical switch named lswitch. If lswitch is not provided, the switch will not have a name so other commands must refer to this switch by its UUID. Initially the switch will have no ports.
    lswitch-del lswitch
    Deletes lswitch.
    lswitch-list
    Lists all existing switches on standard output, one per line.

    ACL Commands

    [--log] acl-add lswitch direction priority match action
    Adds the specified ACL to lswitch. direction must be either from-lport or to-lport. priority must be between 1 and 65534, inclusive. If --log is specified, packet logging is enabled for the ACL. A full description of the fields are in ovn-nb(5).
    acl-del lswitch [direction [priority match]]
    Deletes ACLs from lswitch. If only lswitch is supplied, all the ACLs from the logical switch are deleted. If direction is also specified, then all the flows in that direction will be deleted from the logical switch. If all the fields are given, then a single flow that matches all the fields will be deleted.
    acl-list lswitch
    Lists the ACLs on lswitch.

    Logical Port Commands

    lport-add lswitch lport
    Creates on lswitch a new logical port named lport.
    lport-add lswitch lport parent tag
    Creates on lswitch a logical port named lport that is a child of parent that is identifed with VLAN ID tag. This is useful in cases such as virtualized container environments where Open vSwitch does not have a direct connection to the container's port and it must be shared with the virtual machine's port.
    lport-del lport
    Deletes lport.
    lport-list lswitch
    Lists all the logical ports within lswitch on standard output, one per line.
    lport-get-parent lport
    If set, get the parent port of lport. If not set, print nothing.
    lport-get-tag lport
    If set, get the tag for lport traffic. If not set, print nothing.
    lport-set-addresses lport [address]...
    Sets the addresses associated with lport to address. Each address should be either an Ethernet address or an Ethernet address followed by an IP address (separated by a space and quoted to form a single command-line argument). The special form unknown is also valid. Multiple Ethernet addresses or Ethernet+IP pairs may be set. If no address argument is given, lport will have no addresses associated with it.
    lport-get-addresses lport
    Lists all the addresses associated with lport on standard output, one per line.
    lport-set-port-security lport [addrs]...

    Sets the port security addresses associated with lport to addrs. Multiple sets of addresses may be set by using multiple addrs arguments. If no addrs argument is given, lport will not have port security enabled.

    Port security limits the addresses from which a logical port may send packets and to which it may receive packets. See the ovn-nb(5) documentation for the column in the table for details.

    lport-get-port-security lport
    Lists all the port security addresses associated with lport on standard output, one per line.
    lport-get-up lport
    Prints the state of lport, either up or down.
    lport-set-enabled lport state
    Set the administrative state of lport, either enabled or disabled. When a port is disabled, no traffic is allowed into or out of the port.
    lport-get-enabled lport
    Prints the administrative state of lport, either enabled or disabled.
    lport-set-type lport type
    Set the type for the logical port. No special types have been implemented yet.
    lport-get-type lport
    Get the type for the logical port.
    lport-set-options lport [key=value]...
    Set type-specific key-value options for the logical port.
    lport-get-options lport
    Get the type-specific options for the logical port.

    Options

    --db database
    The OVSDB database remote to contact. If the OVN_NB_DB environment variable is set, its value is used as the default. Otherwise, the default is unix:@RUNDIR@/db.sock, but this default is unlikely to be useful outside of single-machine OVN test environments.
    -h | --help
    -o | --options
    -V | --version

    Logging options

    -vspec, --verbose=spec
    -v, --verbose
    --log-file[=file]
    --syslog-target=host:port

    PKI configuration (required to use SSL)

    -p, --private-key=file file with private key
    -c, --certificate=file file with certificate for private key
    -C, --ca-cert=file file with peer CA certificate
    openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020620 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 30 ctime=1567801424.613852188 openvswitch-2.5.9/ovn/utilities/automake.mk0000644000175000017500000000203013534540071022301 0ustar00jpettitjpettit00000000000000scripts_SCRIPTS += \ ovn/utilities/ovn-ctl man_MANS += \ ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 \ ovn/utilities/ovn-sbctl.8 MAN_ROOTS += ovn/utilities/ovn-sbctl.8.in # Docker drivers bin_SCRIPTS += \ ovn/utilities/ovn-docker-overlay-driver \ ovn/utilities/ovn-docker-underlay-driver EXTRA_DIST += \ ovn/utilities/ovn-ctl \ ovn/utilities/ovn-ctl.8.xml \ ovn/utilities/ovn-docker-overlay-driver \ ovn/utilities/ovn-docker-underlay-driver \ ovn/utilities/ovn-nbctl.8.xml DISTCLEANFILES += \ ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 \ ovn/utilities/ovn-sbctl.8 # ovn-nbctl bin_PROGRAMS += ovn/utilities/ovn-nbctl ovn_utilities_ovn_nbctl_SOURCES = ovn/utilities/ovn-nbctl.c ovn_utilities_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la # ovn-sbctl bin_PROGRAMS += ovn/utilities/ovn-sbctl ovn_utilities_ovn_sbctl_SOURCES = ovn/utilities/ovn-sbctl.c ovn_utilities_ovn_sbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-nbctl.c0000644000000000000000000000013213534540071020527 xustar0030 mtime=1567801401.793684048 30 atime=1567801402.117686428 30 ctime=1567801425.133856021 openvswitch-2.5.9/ovn/utilities/ovn-nbctl.c0000644000175000017500000011643613534540071022230 0ustar00jpettitjpettit00000000000000/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "command-line.h" #include "db-ctl-base.h" #include "dirs.h" #include "fatal-signal.h" #include "json.h" #include "ovn/lib/ovn-nb-idl.h" #include "packets.h" #include "poll-loop.h" #include "process.h" #include "smap.h" #include "stream.h" #include "stream-ssl.h" #include "svec.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(nbctl); /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --timeout: Time to wait for a connection to 'db'. */ static int timeout; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; /* The IDL we're using and the current transaction, if any. * This is for use by nbctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void nbctl_exit(int status); static void nbctl_cmd_init(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static const char *nbctl_default_db(void); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static void do_nbctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); int main(int argc, char *argv[]) { extern struct vlog_module VLM_reconnect; struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; char *args; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN); nbrec_init(); nbctl_cmd_init(); /* Log our arguments. This is often valuable for debugging systems. */ args = process_escape_args(argv); VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); /* Parse command line. */ shash_init(&local_options); parse_options(argc, argv, &local_options); commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, &n_commands); if (timeout) { time_alarm(timeout); } /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { seqno = ovsdb_idl_get_seqno(idl); do_nbctl(args, commands, n_commands, idl); } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static const char * nbctl_default_db(void) { static char *def; if (!def) { def = getenv("OVN_NB_DB"); if (!def) { def = ctl_default_db(); } } return def; } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_NO_SYSLOG, OPT_DRY_RUN, OPT_ONELINE, OPT_LOCAL, OPT_COMMANDS, OPT_OPTIONS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"commands", no_argument, NULL, OPT_COMMANDS}, {"options", no_argument, NULL, OPT_OPTIONS}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, TABLE_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); table_style.format = TF_LIST; for (;;) { int idx; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&VLM_nbctl, VLF_SYSLOG, VLL_WARN); break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), optarg ? xstrdup(optarg) : NULL); break; case 'h': usage(); exit(EXIT_SUCCESS); case OPT_COMMANDS: ctl_print_commands(); case OPT_OPTIONS: ctl_print_options(global_long_options); case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", nbrec_get_db_version()); exit(EXIT_SUCCESS); case 't': timeout = strtoul(optarg, NULL, 10); if (timeout < 0) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!db) { db = nbctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } static void usage(void) { printf("\ %s: OVN northbound DB management utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ General commands:\n\ show print overview of database contents\n\ show LSWITCH print overview of database contents for LSWITCH\n\ \n\ Logical switch commands:\n\ lswitch-add [LSWITCH] create a logical switch named LSWITCH\n\ lswitch-del LSWITCH delete LSWITCH and all its ports\n\ lswitch-list print the names of all logical switches\n\ \n\ ACL commands:\n\ acl-add LSWITCH DIRECTION PRIORITY MATCH ACTION [log]\n\ add an ACL to LSWITCH\n\ acl-del LSWITCH [DIRECTION [PRIORITY MATCH]]\n\ remove ACLs from LSWITCH\n\ acl-list LSWITCH print ACLs for LSWITCH\n\ \n\ Logical port commands:\n\ lport-add LSWITCH LPORT add logical port LPORT on LSWITCH\n\ lport-add LSWITCH LPORT PARENT TAG\n\ add logical port LPORT on LSWITCH with PARENT\n\ on TAG\n\ lport-del LPORT delete LPORT from its attached switch\n\ lport-list LSWITCH print the names of all logical ports on LSWITCH\n\ lport-get-parent LPORT get the parent of LPORT if set\n\ lport-get-tag LPORT get the LPORT's tag if set\n\ lport-set-addresses LPORT [ADDRESS]...\n\ set MAC or MAC+IP addresses for LPORT.\n\ lport-get-addresses LPORT get a list of MAC addresses on LPORT\n\ lport-set-port-security LPORT [ADDRS]...\n\ set port security addresses for LPORT.\n\ lport-get-port-security LPORT get LPORT's port security addresses\n\ lport-get-up LPORT get state of LPORT ('up' or 'down')\n\ lport-set-enabled LPORT STATE\n\ set administrative state LPORT\n\ ('enabled' or 'disabled')\n\ lport-get-enabled LPORT get administrative state LPORT\n\ ('enabled' or 'disabled')\n\ lport-set-type LPORT TYPE Set the type for LPORT\n\ lport-get-type LPORT Get the type for LPORT\n\ lport-set-options LPORT KEY=VALUE [KEY=VALUE]...\n\ Set options related to the type of LPORT\n\ lport-get-options LPORT Get the type specific options for LPORT\n\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ -t, --timeout=SECS wait at most SECS seconds\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, nbctl_default_db()); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=nbctl:syslog:warn\n"); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); exit(EXIT_SUCCESS); } static const struct nbrec_logical_switch * lswitch_by_name_or_uuid(struct ctl_context *ctx, const char *id) { const struct nbrec_logical_switch *lswitch = NULL; bool is_uuid = false; bool duplicate = false; struct uuid lswitch_uuid; if (uuid_from_string(&lswitch_uuid, id)) { is_uuid = true; lswitch = nbrec_logical_switch_get_for_uuid(ctx->idl, &lswitch_uuid); } if (!lswitch) { const struct nbrec_logical_switch *iter; NBREC_LOGICAL_SWITCH_FOR_EACH(iter, ctx->idl) { if (strcmp(iter->name, id)) { continue; } if (lswitch) { VLOG_WARN("There is more than one logical switch named '%s'. " "Use a UUID.", id); lswitch = NULL; duplicate = true; break; } lswitch = iter; } } if (!lswitch && !duplicate) { VLOG_WARN("lswitch not found for %s: '%s'", is_uuid ? "UUID" : "name", id); } return lswitch; } static void print_lswitch(const struct nbrec_logical_switch *lswitch, struct ds *s) { ds_put_format(s, " lswitch "UUID_FMT" (%s)\n", UUID_ARGS(&lswitch->header_.uuid), lswitch->name); for (size_t i = 0; i < lswitch->n_ports; i++) { const struct nbrec_logical_port *lport = lswitch->ports[i]; ds_put_format(s, " lport %s\n", lport->name); if (lport->parent_name) { ds_put_format(s, " parent: %s\n", lport->parent_name); } if (lport->n_tag) { ds_put_format(s, " tag: %"PRIu64"\n", lport->tag[0]); } if (lport->n_addresses) { ds_put_cstr(s, " addresses: ["); for (size_t j = 0; j < lport->n_addresses; j++) { ds_put_format(s, "%s\"%s\"", j == 0 ? "" : ", ", lport->addresses[j]); } ds_put_cstr(s, "]\n"); } } } static void nbctl_show(struct ctl_context *ctx) { const struct nbrec_logical_switch *lswitch; if (ctx->argc == 2) { lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); if (lswitch) { print_lswitch(lswitch, &ctx->output); } } else { NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) { print_lswitch(lswitch, &ctx->output); } } } static void nbctl_lswitch_add(struct ctl_context *ctx) { struct nbrec_logical_switch *lswitch; lswitch = nbrec_logical_switch_insert(ctx->txn); if (ctx->argc == 2) { nbrec_logical_switch_set_name(lswitch, ctx->argv[1]); } } static void nbctl_lswitch_del(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_switch *lswitch; lswitch = lswitch_by_name_or_uuid(ctx, id); if (!lswitch) { return; } nbrec_logical_switch_delete(lswitch); } static void nbctl_lswitch_list(struct ctl_context *ctx) { const struct nbrec_logical_switch *lswitch; struct smap lswitches; smap_init(&lswitches); NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, ctx->idl) { smap_add_format(&lswitches, lswitch->name, UUID_FMT " (%s)", UUID_ARGS(&lswitch->header_.uuid), lswitch->name); } const struct smap_node **nodes = smap_sort(&lswitches); for (size_t i = 0; i < smap_count(&lswitches); i++) { const struct smap_node *node = nodes[i]; ds_put_format(&ctx->output, "%s\n", node->value); } smap_destroy(&lswitches); free(nodes); } static const struct nbrec_logical_port * lport_by_name_or_uuid(struct ctl_context *ctx, const char *id) { const struct nbrec_logical_port *lport = NULL; bool is_uuid = false; struct uuid lport_uuid; if (uuid_from_string(&lport_uuid, id)) { is_uuid = true; lport = nbrec_logical_port_get_for_uuid(ctx->idl, &lport_uuid); } if (!lport) { NBREC_LOGICAL_PORT_FOR_EACH(lport, ctx->idl) { if (!strcmp(lport->name, id)) { break; } } } if (!lport) { VLOG_WARN("lport not found for %s: '%s'", is_uuid ? "UUID" : "name", id); } return lport; } static void nbctl_lport_add(struct ctl_context *ctx) { struct nbrec_logical_port *lport; const struct nbrec_logical_switch *lswitch; int64_t tag; lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); if (!lswitch) { return; } if (ctx->argc != 3 && ctx->argc != 5) { /* If a parent_name is specified, a tag must be specified as well. */ VLOG_WARN("Invalid arguments to lport-add."); return; } if (ctx->argc == 5) { /* Validate tag. */ if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) || tag < 0 || tag > 4095) { VLOG_WARN("Invalid tag '%s'", ctx->argv[4]); return; } } /* Create the logical port. */ lport = nbrec_logical_port_insert(ctx->txn); nbrec_logical_port_set_name(lport, ctx->argv[2]); if (ctx->argc == 5) { nbrec_logical_port_set_parent_name(lport, ctx->argv[3]); nbrec_logical_port_set_tag(lport, &tag, 1); } /* Insert the logical port into the logical switch. */ nbrec_logical_switch_verify_ports(lswitch); struct nbrec_logical_port **new_ports = xmalloc(sizeof *new_ports * (lswitch->n_ports + 1)); memcpy(new_ports, lswitch->ports, sizeof *new_ports * lswitch->n_ports); new_ports[lswitch->n_ports] = lport; nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports + 1); free(new_ports); } /* Removes lport 'lswitch->ports[idx]'. */ static void remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx) { const struct nbrec_logical_port *lport = lswitch->ports[idx]; /* First remove 'lport' from the array of ports. This is what will * actually cause the logical port to be deleted when the transaction is * sent to the database server (due to garbage collection). */ struct nbrec_logical_port **new_ports = xmemdup(lswitch->ports, sizeof *new_ports * lswitch->n_ports); new_ports[idx] = new_ports[lswitch->n_ports - 1]; nbrec_logical_switch_verify_ports(lswitch); nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports - 1); free(new_ports); /* Delete 'lport' from the IDL. This won't have a real effect on the * database server (the IDL will suppress it in fact) but it means that it * won't show up when we iterate with NBREC_LOGICAL_PORT_FOR_EACH later. */ nbrec_logical_port_delete(lport); } static void nbctl_lport_del(struct ctl_context *ctx) { const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, ctx->argv[1]); if (!lport) { return; } /* Find the switch that contains 'lport', then delete it. */ const struct nbrec_logical_switch *lswitch; NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, ctx->idl) { for (size_t i = 0; i < lswitch->n_ports; i++) { if (lswitch->ports[i] == lport) { remove_lport(lswitch, i); return; } } } VLOG_WARN("logical port %s is not part of any logical switch", ctx->argv[1]); } static void nbctl_lport_list(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_switch *lswitch; struct smap lports; size_t i; lswitch = lswitch_by_name_or_uuid(ctx, id); if (!lswitch) { return; } smap_init(&lports); for (i = 0; i < lswitch->n_ports; i++) { const struct nbrec_logical_port *lport = lswitch->ports[i]; smap_add_format(&lports, lport->name, UUID_FMT " (%s)", UUID_ARGS(&lport->header_.uuid), lport->name); } const struct smap_node **nodes = smap_sort(&lports); for (i = 0; i < smap_count(&lports); i++) { const struct smap_node *node = nodes[i]; ds_put_format(&ctx->output, "%s\n", node->value); } smap_destroy(&lports); free(nodes); } static void nbctl_lport_get_parent(struct ctl_context *ctx) { const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, ctx->argv[1]); if (!lport) { return; } if (lport->parent_name) { ds_put_format(&ctx->output, "%s\n", lport->parent_name); } } static void nbctl_lport_get_tag(struct ctl_context *ctx) { const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, ctx->argv[1]); if (!lport) { return; } if (lport->n_tag > 0) { ds_put_format(&ctx->output, "%"PRId64"\n", lport->tag[0]); } } static void nbctl_lport_set_addresses(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } int i; for (i = 2; i < ctx->argc; i++) { struct eth_addr ea; if (strcmp(ctx->argv[i], "unknown") && !ovs_scan(ctx->argv[i], ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) { VLOG_ERR("Invalid address format (%s). See ovn-nb(5). " "Hint: An Ethernet address must be " "listed before an IP address, together as a single " "argument.", ctx->argv[i]); return; } } nbrec_logical_port_set_addresses(lport, (const char **) ctx->argv + 2, ctx->argc - 2); } static void nbctl_lport_get_addresses(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; struct svec addresses; const char *mac; size_t i; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } svec_init(&addresses); for (i = 0; i < lport->n_addresses; i++) { svec_add(&addresses, lport->addresses[i]); } svec_sort(&addresses); SVEC_FOR_EACH(i, mac, &addresses) { ds_put_format(&ctx->output, "%s\n", mac); } svec_destroy(&addresses); } static void nbctl_lport_set_port_security(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } nbrec_logical_port_set_port_security(lport, (const char **) ctx->argv + 2, ctx->argc - 2); } static void nbctl_lport_get_port_security(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; struct svec addrs; const char *addr; size_t i; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } svec_init(&addrs); for (i = 0; i < lport->n_port_security; i++) { svec_add(&addrs, lport->port_security[i]); } svec_sort(&addrs); SVEC_FOR_EACH(i, addr, &addrs) { ds_put_format(&ctx->output, "%s\n", addr); } svec_destroy(&addrs); } static void nbctl_lport_get_up(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } ds_put_format(&ctx->output, "%s\n", (lport->up && *lport->up) ? "up" : "down"); } static void nbctl_lport_set_enabled(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const char *state = ctx->argv[2]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } if (!strcasecmp(state, "enabled")) { bool enabled = true; nbrec_logical_port_set_enabled(lport, &enabled, 1); } else if (!strcasecmp(state, "disabled")) { bool enabled = false; nbrec_logical_port_set_enabled(lport, &enabled, 1); } else { VLOG_ERR("Invalid state '%s' provided to lport-set-enabled", state); } } static void nbctl_lport_get_enabled(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } ds_put_format(&ctx->output, "%s\n", !lport->enabled || *lport->enabled ? "enabled" : "disabled"); } static void nbctl_lport_set_type(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const char *type = ctx->argv[2]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } nbrec_logical_port_set_type(lport, type); } static void nbctl_lport_get_type(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } ds_put_format(&ctx->output, "%s\n", lport->type); } static void nbctl_lport_set_options(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; size_t i; struct smap options = SMAP_INITIALIZER(&options); lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } for (i = 2; i < ctx->argc; i++) { char *key, *value; value = xstrdup(ctx->argv[i]); key = strsep(&value, "="); if (value) { smap_add(&options, key, value); } free(key); } nbrec_logical_port_set_options(lport, &options); smap_destroy(&options); } static void nbctl_lport_get_options(struct ctl_context *ctx) { const char *id = ctx->argv[1]; const struct nbrec_logical_port *lport; struct smap_node *node; lport = lport_by_name_or_uuid(ctx, id); if (!lport) { return; } SMAP_FOR_EACH(node, &lport->options) { ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value); } } enum { DIR_FROM_LPORT, DIR_TO_LPORT }; static int dir_encode(const char *dir) { if (!strcmp(dir, "from-lport")) { return DIR_FROM_LPORT; } else if (!strcmp(dir, "to-lport")) { return DIR_TO_LPORT; } OVS_NOT_REACHED(); } static int acl_cmp(const void *acl1_, const void *acl2_) { const struct nbrec_acl *const *acl1p = acl1_; const struct nbrec_acl *const *acl2p = acl2_; const struct nbrec_acl *acl1 = *acl1p; const struct nbrec_acl *acl2 = *acl2p; int dir1 = dir_encode(acl1->direction); int dir2 = dir_encode(acl2->direction); if (dir1 != dir2) { return dir1 < dir2 ? -1 : 1; } else if (acl1->priority != acl2->priority) { return acl1->priority > acl2->priority ? -1 : 1; } else { return strcmp(acl1->match, acl2->match); } } static void nbctl_acl_list(struct ctl_context *ctx) { const struct nbrec_logical_switch *lswitch; const struct nbrec_acl **acls; size_t i; lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); if (!lswitch) { return; } acls = xmalloc(sizeof *acls * lswitch->n_acls); for (i = 0; i < lswitch->n_acls; i++) { acls[i] = lswitch->acls[i]; } qsort(acls, lswitch->n_acls, sizeof *acls, acl_cmp); for (i = 0; i < lswitch->n_acls; i++) { const struct nbrec_acl *acl = acls[i]; printf("%10s %5"PRId64" (%s) %s%s\n", acl->direction, acl->priority, acl->match, acl->action, acl->log ? " log" : ""); } free(acls); } static void nbctl_acl_add(struct ctl_context *ctx) { const struct nbrec_logical_switch *lswitch; const char *action = ctx->argv[5]; const char *direction; int64_t priority; lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); if (!lswitch) { return; } /* Validate direction. Only require the first letter. */ if (ctx->argv[2][0] == 't') { direction = "to-lport"; } else if (ctx->argv[2][0] == 'f') { direction = "from-lport"; } else { VLOG_WARN("Invalid direction '%s'", ctx->argv[2]); return; } /* Validate priority. */ if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 0 || priority > 32767) { VLOG_WARN("Invalid priority '%s'", ctx->argv[3]); return; } /* Validate action. */ if (strcmp(action, "allow") && strcmp(action, "allow-related") && strcmp(action, "drop") && strcmp(action, "reject")) { VLOG_WARN("Invalid action '%s'", action); return; } /* Create the acl. */ struct nbrec_acl *acl = nbrec_acl_insert(ctx->txn); nbrec_acl_set_priority(acl, priority); nbrec_acl_set_direction(acl, direction); nbrec_acl_set_match(acl, ctx->argv[4]); nbrec_acl_set_action(acl, action); if (shash_find(&ctx->options, "--log") != NULL) { nbrec_acl_set_log(acl, true); } /* Insert the acl into the logical switch. */ nbrec_logical_switch_verify_acls(lswitch); struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * (lswitch->n_acls + 1)); memcpy(new_acls, lswitch->acls, sizeof *new_acls * lswitch->n_acls); new_acls[lswitch->n_acls] = acl; nbrec_logical_switch_set_acls(lswitch, new_acls, lswitch->n_acls + 1); free(new_acls); } static void nbctl_acl_del(struct ctl_context *ctx) { const struct nbrec_logical_switch *lswitch; const char *direction; int64_t priority = 0; lswitch = lswitch_by_name_or_uuid(ctx, ctx->argv[1]); if (!lswitch) { return; } if (ctx->argc != 2 && ctx->argc != 3 && ctx->argc != 5) { VLOG_WARN("Invalid number of arguments"); return; } if (ctx->argc == 2) { /* If direction, priority, and match are not specified, delete * all ACLs. */ nbrec_logical_switch_verify_acls(lswitch); nbrec_logical_switch_set_acls(lswitch, NULL, 0); return; } /* Validate direction. Only require first letter. */ if (ctx->argv[2][0] == 't') { direction = "to-lport"; } else if (ctx->argv[2][0] == 'f') { direction = "from-lport"; } else { VLOG_WARN("Invalid direction '%s'", ctx->argv[2]); return; } /* If priority and match are not specified, delete all ACLs with the * specified direction. */ if (ctx->argc == 3) { struct nbrec_acl **new_acls = xmalloc(sizeof *new_acls * lswitch->n_acls); int n_acls = 0; for (size_t i = 0; i < lswitch->n_acls; i++) { if (strcmp(direction, lswitch->acls[i]->direction)) { new_acls[n_acls++] = lswitch->acls[i]; } } nbrec_logical_switch_verify_acls(lswitch); nbrec_logical_switch_set_acls(lswitch, new_acls, n_acls); free(new_acls); return; } /* Validate priority. */ if (!ovs_scan(ctx->argv[3], "%"SCNd64, &priority) || priority < 0 || priority > 32767) { VLOG_WARN("Invalid priority '%s'", ctx->argv[3]); return; } /* Remove the matching rule. */ for (size_t i = 0; i < lswitch->n_acls; i++) { struct nbrec_acl *acl = lswitch->acls[i]; if (priority == acl->priority && !strcmp(ctx->argv[4], acl->match) && !strcmp(direction, acl->direction)) { struct nbrec_acl **new_acls = xmemdup(lswitch->acls, sizeof *new_acls * lswitch->n_acls); new_acls[i] = lswitch->acls[lswitch->n_acls - 1]; nbrec_logical_switch_verify_acls(lswitch); nbrec_logical_switch_set_acls(lswitch, new_acls, lswitch->n_acls - 1); free(new_acls); return; } } } static const struct ctl_table_class tables[] = { {&nbrec_table_logical_switch, {{&nbrec_table_logical_switch, &nbrec_logical_switch_col_name, NULL}, {NULL, NULL, NULL}}}, {&nbrec_table_logical_port, {{&nbrec_table_logical_port, &nbrec_logical_port_col_name, NULL}, {NULL, NULL, NULL}}}, {&nbrec_table_acl, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&nbrec_table_logical_router, {{&nbrec_table_logical_router, &nbrec_logical_router_col_name, NULL}, {NULL, NULL, NULL}}}, {&nbrec_table_logical_router_port, {{&nbrec_table_logical_router_port, &nbrec_logical_router_port_col_name, NULL}, {NULL, NULL, NULL}}}, {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct ctl_context ctx; ds_init(&c->output); c->table = NULL; ctl_context_init(&ctx, c, idl, NULL, NULL, NULL); (c->syntax->prerequisites)(&ctx); ctl_context_done(&ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static void do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct ctl_context ctx; struct ctl_command *c; struct shash_node *node; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args); symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } ctl_context_init(&ctx, NULL, idl, txn, symtab, NULL); for (c = commands; c < &commands[n_commands]; c++) { ctl_context_init_command(&ctx, c); if (c->syntax->run) { (c->syntax->run)(&ctx); } ctl_context_done_command(&ctx, c); if (ctx.try_again) { ctl_context_done(&ctx, NULL); goto try_again; } } ctl_context_done(&ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created (e.g. " "with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { ctl_context_init(&ctx, c, idl, txn, symtab, NULL); (c->syntax->postprocess)(&ctx); ctl_context_done(&ctx, c); } } } error = xstrdup(ovsdb_idl_txn_get_error(txn)); switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", error); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } free(error); ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); ovsdb_idl_txn_destroy(txn); ovsdb_idl_destroy(idl); exit(EXIT_SUCCESS); try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ if (txn) { ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); the_idl_txn = NULL; } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } free(error); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void nbctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } static const struct ctl_command_syntax nbctl_commands[] = { { "show", 0, 1, "[LSWITCH]", NULL, nbctl_show, NULL, "", RO }, /* lswitch commands. */ { "lswitch-add", 0, 1, "[LSWITCH]", NULL, nbctl_lswitch_add, NULL, "", RW }, { "lswitch-del", 1, 1, "LSWITCH", NULL, nbctl_lswitch_del, NULL, "", RW }, { "lswitch-list", 0, 0, "", NULL, nbctl_lswitch_list, NULL, "", RO }, /* acl commands. */ { "acl-add", 5, 5, "LSWITCH DIRECTION PRIORITY MATCH ACTION", NULL, nbctl_acl_add, NULL, "--log", RW }, { "acl-del", 1, 4, "LSWITCH [DIRECTION [PRIORITY MATCH]]", NULL, nbctl_acl_del, NULL, "", RW }, { "acl-list", 1, 1, "LSWITCH", NULL, nbctl_acl_list, NULL, "", RO }, /* lport commands. */ { "lport-add", 2, 4, "LSWITCH LPORT [PARENT] [TAG]", NULL, nbctl_lport_add, NULL, "", RW }, { "lport-del", 1, 1, "LPORT", NULL, nbctl_lport_del, NULL, "", RO }, { "lport-list", 1, 1, "LSWITCH", NULL, nbctl_lport_list, NULL, "", RO }, { "lport-get-parent", 1, 1, "LPORT", NULL, nbctl_lport_get_parent, NULL, "", RO }, { "lport-get-tag", 1, 1, "LPORT", NULL, nbctl_lport_get_tag, NULL, "", RO }, { "lport-set-addresses", 1, INT_MAX, "LPORT [ADDRESS]...", NULL, nbctl_lport_set_addresses, NULL, "", RW }, { "lport-get-addresses", 1, 1, "LPORT", NULL, nbctl_lport_get_addresses, NULL, "", RO }, { "lport-set-port-security", 0, INT_MAX, "LPORT [ADDRS]...", NULL, nbctl_lport_set_port_security, NULL, "", RW }, { "lport-get-port-security", 1, 1, "LPORT", NULL, nbctl_lport_get_port_security, NULL, "", RO }, { "lport-get-up", 1, 1, "LPORT", NULL, nbctl_lport_get_up, NULL, "", RO }, { "lport-set-enabled", 2, 2, "LPORT STATE", NULL, nbctl_lport_set_enabled, NULL, "", RW }, { "lport-get-enabled", 1, 1, "LPORT", NULL, nbctl_lport_get_enabled, NULL, "", RO }, { "lport-set-type", 2, 2, "LPORT TYPE", NULL, nbctl_lport_set_type, NULL, "", RW }, { "lport-get-type", 1, 1, "LPORT", NULL, nbctl_lport_get_type, NULL, "", RO }, { "lport-set-options", 1, INT_MAX, "LPORT KEY=VALUE [KEY=VALUE]...", NULL, nbctl_lport_set_options, NULL, "", RW }, { "lport-get-options", 1, 1, "LPORT", NULL, nbctl_lport_get_options, NULL, "", RO }, {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, }; /* Registers nbctl and common db commands. */ static void nbctl_cmd_init(void) { ctl_init(tables, NULL, nbctl_exit); ctl_register_commands(nbctl_commands); } openvswitch-2.5.9/ovn/utilities/PaxHeaders.82075/ovn-ctl0000644000000000000000000000013113534540071017765 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 29 ctime=1567801424.51385145 openvswitch-2.5.9/ovn/utilities/ovn-ctl0000755000175000017500000001522713534540071021466 0ustar00jpettitjpettit00000000000000#!/bin/sh # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do case :$PATH: in *:$dir:*) ;; *) PATH=$PATH:$dir ;; esac done ## ----- ## ## start ## ## ----- ## upgrade_ovn_dbs () { ovn_dbs=$(ovs-appctl -t ovsdb-server ovsdb-server/list-dbs 2>/dev/null) for db in $ovn_dbs; do case $db in OVN*) action "Removing $db from ovsdb-server" \ ovs-appctl -t ovsdb-server ovsdb-server/remove-db $db ;; esac done upgrade_db "$DB_NB_FILE" "$DB_NB_SCHEMA" upgrade_db "$DB_SB_FILE" "$DB_SB_SCHEMA" for db in $DB_NB_FILE $DB_SB_FILE; do action "Adding $db to ovsdb-server" \ ovs-appctl -t ovsdb-server ovsdb-server/add-db $db || exit 1 done } start_northd () { # We expect ovn-northd to be co-located with ovsdb-server handling both the # OVN_Northbound and OVN_Southbound dbs. upgrade_ovn_dbs set ovn-northd set "$@" -vconsole:emer -vsyslog:err -vfile:info OVS_RUNDIR=${OVN_RUNDIR} start_daemon "$OVN_NORTHD_PRIORITY" "$OVN_NORTHD_WRAPPER" "$@" } start_controller () { set ovn-controller "unix:$DB_SOCK" set "$@" -vconsole:emer -vsyslog:err -vfile:info OVS_RUNDIR=${OVN_RUNDIR} start_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@" } ## ---- ## ## stop ## ## ---- ## stop_northd () { OVS_RUNDIR=${OVN_RUNDIR} stop_daemon ovn-northd } stop_controller () { OVS_RUNDIR=${OVN_RUNDIR} stop_daemon ovn-controller } ## ------- ## ## restart ## ## ------- ## restart_northd () { stop_northd start_northd } restart_controller () { stop_controller start_controller } ## ---- ## ## main ## ## ---- ## set_defaults () { DB_SOCK=$rundir/db.sock DB_NB_FILE=$dbdir/ovnnb.db DB_SB_FILE=$dbdir/ovnsb.db DB_NB_SCHEMA=$datadir/ovn-nb.ovsschema DB_SB_SCHEMA=$datadir/ovn-sb.ovsschema OVN_NORTHD_PRIORITY=-10 OVN_NORTHD_WRAPPER= OVN_CONTROLLER_PRIORITY=-10 OVN_CONTROLLER_WRAPPER= OVS_RUNDIR=${OVS_RUNDIR:-${rundir}} OVN_RUNDIR=${OVN_RUNDIR:-${OVS_RUNDIR}} } set_option () { var=`echo "$option" | tr abcdefghijklmnopqrstuvwxyz- ABCDEFGHIJKLMNOPQRSTUVWXYZ_` eval set=\${$var+yes} eval old_value=\$$var if test X$set = X || \ (test $type = bool && \ test X"$old_value" != Xno && test X"$old_value" != Xyes); then echo >&2 "$0: unknown option \"$arg\" (use --help for help)" return fi eval $var=\$value } usage () { set_defaults cat << EOF $0: controls Open Virtual Network daemons usage: $0 [OPTIONS] COMMAND This program is intended to be invoked internally by Open Virtual Network startup scripts. System administrators should not normally invoke it directly. Commands: start_northd start ovn-northd start_controller start ovn-controller stop_northd stop ovn-northd stop_controller stop ovn-controller restart_northd restart ovn-northd restart_controller restart ovn-controller Options: --ovn-northd-priority=NICE set ovn-northd's niceness (default: $OVN_NORTHD_PRIORITY) --ovn-northd-wrapper=WRAPPER run with a wrapper like valgrind for debugging --ovn-controller-priority=NICE set ovn-northd's niceness (default: $OVN_CONTROLLER_PRIORITY) --ovn-controller-wrapper=WRAPPER run with a wrapper like valgrind for debugging -h, --help display this help message File location options: --db-sock=SOCKET JSON-RPC socket name (default: $DB_SOCK) --db-nb-file=FILE OVN_Northbound db file (default: $DB_NB_FILE) --db-sb-file=FILE OVN_Southbound db file (default: $DB_SB_FILE) --db-nb-schema=FILE OVN_Northbound db file (default: $DB_NB_SCHEMA) --db-sb-schema=FILE OVN_Southbound db file (default: $DB_SB_SCHEMA) Default directories with "configure" option and environment variable override: logs: /usr/local/var/log/openvswitch (--with-logdir, OVS_LOGDIR) pidfiles and sockets: /usr/local/var/run/openvswitch (--with-rundir, OVS_RUNDIR) ovn-nb.db: /usr/local/etc/openvswitch (--with-dbdir, OVS_DBDIR) ovn-sb.db: /usr/local/etc/openvswitch (--with-dbdir, OVS_DBDIR) system configuration: /usr/local/etc (--sysconfdir, OVS_SYSCONFDIR) data files: /usr/local/share/openvswitch (--pkgdatadir, OVS_PKGDATADIR) user binaries: /usr/local/bin (--bindir, OVS_BINDIR) system binaries: /usr/local/sbin (--sbindir, OVS_SBINDIR) EOF } set_defaults command= for arg do case $arg in -h | --help) usage ;; --[a-z]*=*) option=`expr X"$arg" : 'X--\([^=]*\)'` value=`expr X"$arg" : 'X[^=]*=\(.*\)'` type=string set_option ;; --no-[a-z]*) option=`expr X"$arg" : 'X--no-\(.*\)'` value=no type=bool set_option ;; --[a-z]*) option=`expr X"$arg" : 'X--\(.*\)'` value=yes type=bool set_option ;; -*) echo >&2 "$0: unknown option \"$arg\" (use --help for help)" exit 1 ;; *) if test X"$command" = X; then command=$arg else echo >&2 "$0: exactly one non-option argument required (use --help for help)" exit 1 fi ;; esac done case $command in start_northd) start_northd ;; start_controller) start_controller ;; stop_northd) stop_northd ;; stop_controller) stop_controller ;; restart_northd) restart_northd ;; restart_controller) restart_controller ;; help) usage ;; preheat) echo >&2 "$0: preheating ovn to 350 degrees F." exit 1 ;; '') echo >&2 "$0: missing command name (use --help for help)" exit 1 ;; *) echo >&2 "$0: unknown command \"$command\" (use --help for help)" exit 1 ;; esac openvswitch-2.5.9/ovn/PaxHeaders.82075/ovn-sb.xml0000644000000000000000000000013213534540071016374 xustar0030 mtime=1567801401.773683902 30 atime=1567801402.117686428 30 ctime=1567801424.497851333 openvswitch-2.5.9/ovn/ovn-sb.xml0000644000175000017500000015302013534540071020063 0ustar00jpettitjpettit00000000000000

    This database holds logical and physical configuration and state for the Open Virtual Network (OVN) system to support virtual network abstraction. For an introduction to OVN, please see ovn-architecture(7).

    The OVN Southbound database sits at the center of the OVN architecture. It is the one component that speaks both southbound directly to all the hypervisors and gateways, via ovn-controller/ovn-controller-vtep, and northbound to the Cloud Management System, via ovn-northd:

    Database Structure

    The OVN Southbound database contains three classes of data with different properties, as described in the sections below.

    Physical Network (PN) data

    PN tables contain information about the chassis nodes in the system. This contains all the information necessary to wire the overlay, such as IP addresses, supported tunnel types, and security keys.

    The amount of PN data is small (O(n) in the number of chassis) and it changes infrequently, so it can be replicated to every chassis.

    The table comprises the PN tables.

    Logical Network (LN) data

    LN tables contain the topology of logical switches and routers, ACLs, firewall rules, and everything needed to describe how packets traverse a logical network, represented as logical datapath flows (see Logical Datapath Flows, below).

    LN data may be large (O(n) in the number of logical ports, ACL rules, etc.). Thus, to improve scaling, each chassis should receive only data related to logical networks in which that chassis participates. Past experience shows that in the presence of large logical networks, even finer-grained partitioning of data, e.g. designing logical flows so that only the chassis hosting a logical port needs related flows, pays off scale-wise. (This is not necessary initially but it is worth bearing in mind in the design.)

    The LN is a slave of the cloud management system running northbound of OVN. That CMS determines the entire OVN logical configuration and therefore the LN's content at any given time is a deterministic function of the CMS's configuration, although that happens indirectly via the database and ovn-northd.

    LN data is likely to change more quickly than PN data. This is especially true in a container environment where VMs are created and destroyed (and therefore added to and deleted from logical switches) quickly.

    and contain LN data.

    Bindings data

    Bindings data link logical and physical components. They show the current placement of logical components (such as VMs and VIFs) onto chassis, and map logical entities to the values that represent them in tunnel encapsulations.

    Bindings change frequently, at least every time a VM powers up or down or migrates, and especially quickly in a container environment. The amount of data per VM (or VIF) is small.

    Each chassis is authoritative about the VMs and VIFs that it hosts at any given time and can efficiently flood that state to a central location, so the consistency needs are minimal.

    The and tables contain binding data.

    Common Columns

    Some tables contain a special column named external_ids. This column has the same form and purpose each place that it appears, so we describe it here to save space later.

    external_ids: map of string-string pairs
    Key-value pairs for use by the software that manages the OVN Southbound database rather than by ovn-controller/ovn-controller-vtep. In particular, ovn-northd can use key-value pairs in this column to relate entities in the southbound database to higher-level entities (such as entities in the OVN Northbound database). Individual key-value pairs in this column may be documented in some cases to aid in understanding and troubleshooting, but the reader should not mistake such documentation as comprehensive.

    Each row in this table represents a hypervisor or gateway (a chassis) in the physical network (PN). Each chassis, via ovn-controller/ovn-controller-vtep, adds and updates its own row, and keeps a copy of the remaining rows to determine how to reach other hypervisors.

    When a chassis shuts down gracefully, it should remove its own row. (This is not critical because resources hosted on the chassis are equally unreachable regardless of whether the row is present.) If a chassis shuts down permanently without removing its row, some kind of manual or automatic cleanup is eventually needed; we can devise a process for that as necessary.

    A chassis name, taken from in the Open_vSwitch database's table. OVN does not prescribe a particular format for chassis names.

    OVN uses encapsulation to transmit logical dataplane packets between chassis.

    Points to supported encapsulation configurations to transmit logical dataplane packets to this chassis. Each entry is a record that describes the configuration.

    A gateway is a chassis that forwards traffic between the OVN-managed part of a logical network and a physical VLAN, extending a tunnel-based logical network into a physical network. Gateways are typically dedicated nodes that do not host VMs and will be controlled by ovn-controller-vtep.

    Stores all VTEP logical switch names connected by this gateway chassis. The table entry with :vtep-physical-switch equal , and :vtep-logical-switch value in , will be associated with this .

    The column in the table refers to rows in this table to identify how OVN may transmit logical dataplane packets to this chassis. Each chassis, via ovn-controller(8) or ovn-controller-vtep(8), adds and updates its own rows and keeps a copy of the remaining rows to determine how to reach other chassis.

    The encapsulation to use to transmit packets to this chassis. Hypervisors must use either geneve or stt. Gateways may use vxlan, geneve, or stt. Options for configuring the encapsulation, e.g. IPsec parameters when IPsec support is introduced. No options are currently defined. The IPv4 address of the encapsulation tunnel endpoint.

    Each row in this table represents one logical flow. ovn-northd populates this table with logical flows that implement the L2 and L3 topologies specified in the database. Each hypervisor, via ovn-controller, translates the logical flows into OpenFlow flows specific to its hypervisor and installs them into Open vSwitch.

    Logical flows are expressed in an OVN-specific format, described here. A logical datapath flow is much like an OpenFlow flow, except that the flows are written in terms of logical ports and logical datapaths instead of physical ports and physical datapaths. Translation between logical and physical flows helps to ensure isolation between logical datapaths. (The logical flow abstraction also allows the OVN centralized components to do less work, since they do not have to separately compute and push out physical flows to each chassis.)

    The default action when no flow matches is to drop packets.

    Architectural Logical Life Cycle of a Packet

    This following description focuses on the life cycle of a packet through a logical datapath, ignoring physical details of the implementation. Please refer to Architectural Physical Life Cycle of a Packet in ovn-architecture(7) for the physical information.

    The description here is written as if OVN itself executes these steps, but in fact OVN (that is, ovn-controller) programs Open vSwitch, via OpenFlow and OVSDB, to execute them on its behalf.

    At a high level, OVN passes each packet through the logical datapath's logical ingress pipeline, which may output the packet to one or more logical port or logical multicast groups. For each such logical output port, OVN passes the packet through the datapath's logical egress pipeline, which may either drop the packet or deliver it to the destination. Between the two pipelines, outputs to logical multicast groups are expanded into logical ports, so that the egress pipeline only processes a single logical output port at a time. Between the two pipelines is also where, when necessary, OVN encapsulates a packet in a tunnel (or tunnels) to transmit to remote hypervisors.

    In more detail, to start, OVN searches the table for a row with correct , a of ingress, a of 0, and a that is true for the packet. If none is found, OVN drops the packet. If OVN finds more than one, it chooses the match with the highest . Then OVN executes each of the actions specified in the row's column, in the order specified. Some actions, such as those to modify packet headers, require no further details. The next and output actions are special.

    The next action causes the above process to be repeated recursively, except that OVN searches for of 1 instead of 0. Similarly, any next action in a row found in that table would cause a further search for a of 2, and so on. When recursive processing completes, flow control returns to the action following next.

    The output action also introduces recursion. Its effect depends on the current value of the outport field. Suppose outport designates a logical port. First, OVN compares inport to outport; if they are equal, it treats the output as a no-op. In the common case, where they are different, the packet enters the egress pipeline. This transition to the egress pipeline discards register data, e.g. reg0 ... reg4 and connection tracking state, to achieve uniform behavior regardless of whether the egress pipeline is on a different hypervisor (because registers aren't preserve across tunnel encapsulation).

    To execute the egress pipeline, OVN again searches the table for a row with correct , a of 0, a that is true for the packet, but now looking for a of egress. If no matching row is found, the output becomes a no-op. Otherwise, OVN executes the actions for the matching flow (which is chosen from multiple, if necessary, as already described).

    In the egress pipeline, the next action acts as already described, except that it, of course, searches for egress flows. The output action, however, now directly outputs the packet to the output port (which is now fixed, because outport is read-only within the egress pipeline).

    The description earlier assumed that outport referred to a logical port. If it instead designates a logical multicast group, then the description above still applies, with the addition of fan-out from the logical multicast group to each logical port in the group. For each member of the group, OVN executes the logical pipeline as described, with the logical output port replaced by the group member.

    Pipeline Stages

    ovn-northd is responsible for populating the table, so the stages are an implementation detail and subject to change. This section describes the current logical flow table.

    The ingress pipeline consists of the following stages:

    • Port Security (Table 0): Validates the source address, drops packets with a VLAN tag, and, if configured, verifies that the logical port is allowed to send with the source address.
    • L2 Destination Lookup (Table 1): Forwards known unicast addresses to the appropriate logical port. Unicast packets to unknown hosts are forwarded to logical ports configured with the special unknown mac address. Broadcast, and multicast are flooded to all ports in the logical switch.

    The egress pipeline consists of the following stages:

    • ACL (Table 0): Applies any specified access control lists.
    • Port Security (Table 1): If configured, verifies that the logical port is allowed to receive packets with the destination address.
    The logical datapath to which the logical flow belongs.

    The primary flows used for deciding on a packet's destination are the ingress flows. The egress flows implement ACLs. See Logical Life Cycle of a Packet, above, for details.

    The stage in the logical pipeline, analogous to an OpenFlow table number. The flow's priority. Flows with numerically higher priority take precedence over those with lower. If two logical datapath flows with the same priority both match, then the one actually applied to the packet is undefined.

    A matching expression. OVN provides a superset of OpenFlow matching capabilities, using a syntax similar to Boolean expressions in a programming language.

    The most important components of match expression are comparisons between symbols and constants, e.g. ip4.dst == 192.168.0.1, ip.proto == 6, arp.op == 1, eth.type == 0x800. The logical AND operator && and logical OR operator || can combine comparisons into a larger expression.

    Matching expressions also support parentheses for grouping, the logical NOT prefix operator !, and literals 0 and 1 to express ``false'' or ``true,'' respectively. The latter is useful by itself as a catch-all expression that matches every packet.

    Symbols

    Type. Symbols have integer or string type. Integer symbols have a width in bits.

    Kinds. There are three kinds of symbols:

    • Fields. A field symbol represents a packet header or metadata field. For example, a field named vlan.tci might represent the VLAN TCI field in a packet.

      A field symbol can have integer or string type. Integer fields can be nominal or ordinal (see Level of Measurement, below).

    • Subfields. A subfield represents a subset of bits from a larger field. For example, a field vlan.vid might be defined as an alias for vlan.tci[0..11]. Subfields are provided for syntactic convenience, because it is always possible to instead refer to a subset of bits from a field directly.

      Only ordinal fields (see Level of Measurement, below) may have subfields. Subfields are always ordinal.

    • Predicates. A predicate is shorthand for a Boolean expression. Predicates may be used much like 1-bit fields. For example, ip4 might expand to eth.type == 0x800. Predicates are provided for syntactic convenience, because it is always possible to instead specify the underlying expression directly.

      A predicate whose expansion refers to any nominal field or predicate (see Level of Measurement, below) is nominal; other predicates have Boolean level of measurement.

    Level of Measurement. See http://en.wikipedia.org/wiki/Level_of_measurement for the statistical concept on which this classification is based. There are three levels:

    • Ordinal. In statistics, ordinal values can be ordered on a scale. OVN considers a field (or subfield) to be ordinal if its bits can be examined individually. This is true for the OpenFlow fields that OpenFlow or Open vSwitch makes ``maskable.''

      Any use of a ordinal field may specify a single bit or a range of bits, e.g. vlan.tci[13..15] refers to the PCP field within the VLAN TCI, and eth.dst[40] refers to the multicast bit in the Ethernet destination address.

      OVN supports all the usual arithmetic relations (==, !=, <, <=, >, and >=) on ordinal fields and their subfields, because OVN can implement these in OpenFlow and Open vSwitch as collections of bitwise tests.

    • Nominal. In statistics, nominal values cannot be usefully compared except for equality. This is true of OpenFlow port numbers, Ethernet types, and IP protocols are examples: all of these are just identifiers assigned arbitrarily with no deeper meaning. In OpenFlow and Open vSwitch, bits in these fields generally aren't individually addressable.

      OVN only supports arithmetic tests for equality on nominal fields, because OpenFlow and Open vSwitch provide no way for a flow to efficiently implement other comparisons on them. (A test for inequality can be sort of built out of two flows with different priorities, but OVN matching expressions always generate flows with a single priority.)

      String fields are always nominal.

    • Boolean. A nominal field that has only two values, 0 and 1, is somewhat exceptional, since it is easy to support both equality and inequality tests on such a field: either one can be implemented as a test for 0 or 1.

      Only predicates (see above) have a Boolean level of measurement.

      This isn't a standard level of measurement.

    Prerequisites. Any symbol can have prerequisites, which are additional condition implied by the use of the symbol. For example, For example, icmp4.type symbol might have prerequisite icmp4, which would cause an expression icmp4.type == 0 to be interpreted as icmp4.type == 0 && icmp4, which would in turn expand to icmp4.type == 0 && eth.type == 0x800 && ip4.proto == 1 (assuming icmp4 is a predicate defined as suggested under Types above).

    Relational operators

    All of the standard relational operators ==, !=, <, <=, >, and >= are supported. Nominal fields support only == and !=, and only in a positive sense when outer ! are taken into account, e.g. given string field inport, inport == "eth0" and !(inport != "eth0") are acceptable, but not inport != "eth0".

    The implementation of == (or != when it is negated), is more efficient than that of the other relational operators.

    Constants

    Integer constants may be expressed in decimal, hexadecimal prefixed by 0x, or as dotted-quad IPv4 addresses, IPv6 addresses in their standard forms, or Ethernet addresses as colon-separated hex digits. A constant in any of these forms may be followed by a slash and a second constant (the mask) in the same form, to form a masked constant. IPv4 and IPv6 masks may be given as integers, to express CIDR prefixes.

    String constants have the same syntax as quoted strings in JSON (thus, they are Unicode strings).

    Some operators support sets of constants written inside curly braces { ... }. Commas between elements of a set, and after the last elements, are optional. With ==, ``field == { constant1, constant2, ... }'' is syntactic sugar for ``field == constant1 || field == constant2 || .... Similarly, ``field != { constant1, constant2, ... }'' is equivalent to ``field != constant1 && field != constant2 && ...''.

    Miscellaneous

    Comparisons may name the symbol or the constant first, e.g. tcp.src == 80 and 80 == tcp.src are both acceptable.

    Tests for a range may be expressed using a syntax like 1024 <= tcp.src <= 49151, which is equivalent to 1024 <= tcp.src && tcp.src <= 49151.

    For a one-bit field or predicate, a mention of its name is equivalent to symobl == 1, e.g. vlan.present is equivalent to vlan.present == 1. The same is true for one-bit subfields, e.g. vlan.tci[12]. There is no technical limitation to implementing the same for ordinal fields of all widths, but the implementation is expensive enough that the syntax parser requires writing an explicit comparison against zero to make mistakes less likely, e.g. in tcp.src != 0 the comparison against 0 is required.

    Operator precedence is as shown below, from highest to lowest. There are two exceptions where parentheses are required even though the table would suggest that they are not: && and || require parentheses when used together, and ! requires parentheses when applied to a relational expression. Thus, in (eth.type == 0x800 || eth.type == 0x86dd) && ip.proto == 6 or !(arp.op == 1), the parentheses are mandatory.

    • ()
    • == != < <= > >=
    • !
    • && ||

    Comments may be introduced by //, which extends to the next new-line. Comments within a line may be bracketed by /* and */. Multiline comments are not supported.

    Symbols

    Most of the symbols below have integer type. Only inport and outport have string type. inport names a logical port. Thus, its value is a name from the table. outport may name a logical port, as inport, or a logical multicast group defined in the table. For both symbols, only names within the flow's logical datapath may be used.

    • reg0...reg4
    • inport outport
    • eth.src eth.dst eth.type
    • vlan.tci vlan.vid vlan.pcp vlan.present
    • ip.proto ip.dscp ip.ecn ip.ttl ip.frag
    • ip4.src ip4.dst
    • ip6.src ip6.dst ip6.label
    • arp.op arp.spa arp.tpa arp.sha arp.tha
    • tcp.src tcp.dst tcp.flags
    • udp.src udp.dst
    • sctp.src sctp.dst
    • icmp4.type icmp4.code
    • icmp6.type icmp6.code
    • nd.target nd.sll nd.tll
    • ct_state, which has the following Boolean subfields:

      • ct.new: True for a new flow
      • ct.est: True for an established flow
      • ct.rel: True for a related flow
      • ct.rpl: True for a reply flow
      • ct.inv: True for a connection entry in a bad state

      ct_state and its subfields are initialized by the ct_next action, described below.

    The following predicates are supported:

    • eth.bcast expands to eth.dst == ff:ff:ff:ff:ff:ff
    • eth.mcast expands to eth.dst[40]
    • vlan.present expands to vlan.tci[12]
    • ip4 expands to eth.type == 0x800
    • ip4.mcast expands to ip4.dst[28..31] == 0xe
    • ip6 expands to eth.type == 0x86dd
    • ip expands to ip4 || ip6
    • icmp4 expands to ip4 && ip.proto == 1
    • icmp6 expands to ip6 && ip.proto == 58
    • icmp expands to icmp4 || icmp6
    • ip.is_frag expands to ip.frag[0]
    • ip.later_frag expands to ip.frag[1]
    • ip.first_frag expands to ip.is_frag && !ip.later_frag
    • arp expands to eth.type == 0x806
    • nd expands to icmp6.type == {135, 136} && icmp6.code == 0
    • tcp expands to ip.proto == 6
    • udp expands to ip.proto == 17
    • sctp expands to ip.proto == 132

    Logical datapath actions, to be executed when the logical flow represented by this row is the highest-priority match.

    Actions share lexical syntax with the column. An empty set of actions (or one that contains just white space or comments), or a set of actions that consists of just drop;, causes the matched packets to be dropped. Otherwise, the column should contain a sequence of actions, each terminated by a semicolon.

    The following actions are defined:

    output;

    In the ingress pipeline, this action executes the egress pipeline as a subroutine. If outport names a logical port, the egress pipeline executes once; if it is a multicast group, the egress pipeline runs once for each logical port in the group.

    In the egress pipeline, this action performs the actual output to the outport logical port. (In the egress pipeline, outport never names a multicast group.)

    Output to the input port is implicitly dropped, that is, output becomes a no-op if outport == inport. Occasionally it may be useful to override this behavior, e.g. to send an ARP reply to an ARP request; to do so, use inport = ""; to set the logical input port to an empty string (which should not be used as the name of any logical port).

    next;
    next(table);
    Executes another logical datapath table as a subroutine. By default, the table after the current one is executed. Specify table to jump to a specific table in the same pipeline.
    field = constant;

    Sets data or metadata field field to constant value constant, e.g. outport = "vif0"; to set the logical output port. To set only a subset of bits in a field, specify a subfield for field or a masked constant, e.g. one may use vlan.pcp[2] = 1; or vlan.pcp = 4/4; to set the most sigificant bit of the VLAN PCP.

    Assigning to a field with prerequisites implicitly adds those prerequisites to ; thus, for example, a flow that sets tcp.dst applies only to TCP flows, regardless of whether its mentions any TCP field.

    Not all fields are modifiable (e.g. eth.type and ip.proto are read-only), and not all modifiable fields may be partially modified (e.g. ip.ttl must assigned as a whole). The outport field is modifiable in the ingress pipeline but not in the egress pipeline.

    field1 = field2;

    Sets data or metadata field field1 to the value of data or metadata field field2, e.g. reg0 = ip4.src; copies ip4.src into reg0. To modify only a subset of a field's bits, specify a subfield for field1 or field2 or both, e.g. vlan.pcp = reg0[0..2]; copies the least-significant bits of reg0 into the VLAN PCP.

    field1 and field2 must be the same type, either both string or both integer fields. If they are both integer fields, they must have the same width.

    If field1 or field2 has prerequisites, they are added implicitly to . It is possible to write an assignment with contradictory prerequisites, such as ip4.src = ip6.src[0..31];, but the contradiction means that a logical flow with such an assignment will never be matched.

    field1 <-> field2;

    Similar to field1 = field2; except that the two values are exchanged instead of copied. Both field1 and field2 must modifiable.

    ip.ttl--;

    Decrements the IPv4 or IPv6 TTL. If this would make the TTL zero or negative, then processing of the packet halts; no further actions are processed. (To properly handle such cases, a higher-priority flow should match on ip.ttl == {0, 1};.)

    Prerequisite: ip

    ct_next;

    Apply connection tracking to the flow, initializing ct_state for matching in later tables. Automatically moves on to the next table, as if followed by next.

    As a side effect, IP fragments will be reassembled for matching. If a fragmented packet is output, then it will be sent with any overlapping fragments squashed. The connection tracking state is scoped by the logical port, so overlapping addresses may be used. To allow traffic related to the matched flow, execute ct_commit.

    It is possible to have actions follow ct_next, but they will not have access to any of its side-effects and is not generally useful.

    ct_commit;
    Commit the flow to the connection tracking entry associated with it by a previous call to ct_next.

    The following actions will likely be useful later, but they have not been thought out carefully.

    arp { action; ... };

    Temporarily replaces the IPv4 packet being processed by an ARP packet and executes each nested action on the ARP packet. Actions following the arp action, if any, apply to the original, unmodified packet.

    The ARP packet that this action operates on is initialized based on the IPv4 packet being processed, as follows. These are default values that the nested actions will probably want to change:

    • eth.src unchanged
    • eth.dst unchanged
    • eth.type = 0x0806
    • arp.op = 1 (ARP request)
    • arp.sha copied from eth.src
    • arp.spa copied from ip4.src
    • arp.tha = 00:00:00:00:00:00
    • arp.tpa copied from ip4.dst

    Prerequisite: ip4

    icmp4 { action; ... };

    Temporarily replaces the IPv4 packet being processed by an ICMPv4 packet and executes each nested action on the ICMPv4 packet. Actions following the icmp4 action, if any, apply to the original, unmodified packet.

    The ICMPv4 packet that this action operates on is initialized based on the IPv4 packet being processed, as follows. These are default values that the nested actions will probably want to change. Ethernet and IPv4 fields not listed here are not changed:

    • ip.proto = 1 (ICMPv4)
    • ip.frag = 0 (not a fragment)
    • icmp4.type = 3 (destination unreachable)
    • icmp4.code = 1 (host unreachable)

    Details TBD.

    Prerequisite: ip4

    tcp_reset;

    This action transforms the current TCP packet according to the following pseudocode:

    if (tcp.ack) {
            tcp.seq = tcp.ack;
    } else {
            tcp.ack = tcp.seq + length(tcp.payload);
            tcp.seq = 0;
    }
    tcp.flags = RST;
    

    Then, the action drops all TCP options and payload data, and updates the TCP checksum.

    Details TBD.

    Prerequisite: tcp

    Human-readable name for this flow's stage in the pipeline. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    The rows in this table define multicast groups of logical ports. Multicast groups allow a single packet transmitted over a tunnel to a hypervisor to be delivered to multiple VMs on that hypervisor, which uses bandwidth more efficiently.

    Each row in this table defines a logical multicast group numbered within , whose logical ports are listed in the column.

    The logical datapath in which the multicast group resides. The value used to designate this logical egress port in tunnel encapsulations. An index forces the key to be unique within the . The unusual range ensures that multicast group IDs do not overlap with logical port IDs.

    The logical multicast group's name. An index forces the name to be unique within the . Logical flows in the ingress pipeline may output to the group just as for individual logical ports, by assigning the group's name to outport and executing an output action.

    Multicast group names and logical port names share a single namespace and thus should not overlap (but the database schema cannot enforce this). To try to avoid conflicts, ovn-northd uses names that begin with _MC_.

    The logical ports included in the multicast group. All of these ports must be in the logical datapath (but the database schema cannot enforce this).

    Each row in this table identifies physical bindings of a logical datapath. A logical datapath implements a logical pipeline among the ports in the table associated with it. In practice, the pipeline in a given logical datapath implements either a logical switch or a logical router.

    The tunnel key value to which the logical datapath is bound. The Tunnel Encapsulation section in ovn-architecture(7) describes how tunnel keys are constructed for each supported encapsulation.

    Each row in is associated with some logical datapath. ovn-northd uses these keys to track the association of a logical datapath with concepts in the database.

    For a logical datapath that represents a logical switch, ovn-northd stores in this key the UUID of the corresponding row in the database. For a logical datapath that represents a logical router, ovn-northd stores in this key the UUID of the corresponding row in the database.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Most rows in this table identify the physical location of a logical port. (The exceptions are logical patch ports, which do not have any physical location.)

    For every Logical_Port record in OVN_Northbound database, ovn-northd creates a record in this table. ovn-northd populates and maintains every column except the chassis column, which it leaves empty in new records.

    ovn-controller/ovn-controller-vtep populates the chassis column for the records that identify the logical ports that are located on its hypervisor/gateway, which ovn-controller/ovn-controller-vtep in turn finds out by monitoring the local hypervisor's Open_vSwitch database, which identifies logical ports via the conventions described in IntegrationGuide.md.

    When a chassis shuts down gracefully, it should clean up the chassis column that it previously had populated. (This is not critical because resources hosted on the chassis are equally unreachable regardless of whether their rows are present.) To handle the case where a VM is shut down abruptly on one chassis, then brought up again on a different one, ovn-controller/ovn-controller-vtep must overwrite the chassis column with new information.

    The logical datapath to which the logical port belongs. A logical port, taken from in the OVN_Northbound database's table. OVN does not prescribe a particular format for the logical port ID. The physical location of the logical port. To successfully identify a chassis, this column must be a record. This is populated by ovn-controller/ovn-controller-vtep.

    A number that represents the logical port in the key (e.g. STT key or Geneve TLV) field carried within tunnel protocol packets.

    The tunnel ID must be unique within the scope of a logical datapath.

    The Ethernet address or addresses used as a source address on the logical port, each in the form xx:xx:xx:xx:xx:xx. The string unknown is also allowed to indicate that the logical port has an unknown set of (additional) source addresses.

    A VM interface would ordinarily have a single Ethernet address. A gateway port might initially only have unknown, and then add MAC addresses to the set as it learns new source addresses.

    A type for this logical port. Logical ports can be used to model other types of connectivity into an OVN logical switch. The following types are defined:

    (empty string)
    VM (or VIF) interface.
    patch
    One of a pair of logical ports that act as if connected by a patch cable. Useful for connecting two logical datapaths, e.g. to connect a logical router to a logical switch or to another logical router.
    localnet
    A connection to a locally accessible network from each ovn-controller instance. A logical switch can only have a single localnet port attached and at most one regular logical port. This is used to model direct connectivity to an existing network.
    vtep
    A port to a logical switch on a VTEP gateway chassis. In order to get this port correctly recognized by the OVN controller, the :vtep-physical-switch and :vtep-logical-switch must also be defined.

    These options apply to logical ports with of patch.

    The in the record for the other side of the patch. The named must specify this in its own peer option. That is, the two patch logical ports must have reversed and peer values.

    These options apply to logical ports with of localnet.

    Required. ovn-controller uses the configuration entry ovn-bridge-mappings to determine how to connect to this network. ovn-bridge-mappings is a list of network names mapped to a local OVS bridge that provides access to that network. An example of configuring ovn-bridge-mappings would be:
    $ ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet1:br-eth0,physnet2:br-eth1

    When a logical switch has a localnet port attached, every chassis that may have a local vif attached to that logical switch must have a bridge mapping configured to reach that localnet. Traffic that arrives on a localnet port is never forwarded over a tunnel to another chassis.

    If set, indicates that the port represents a connection to a specific VLAN on a locally accessible network. The VLAN ID is used to match incoming traffic and is also added to outgoing traffic.

    These options apply to logical ports with of vtep.

    Required. The name of the VTEP gateway. Required. A logical switch name connected by the VTEP gateway. Must be set when is vtep.

    These columns support containers nested within a VM. Specifically, they are used when is empty and identifies the interface of a container spawned inside a VM. They are empty for containers or VMs that run directly on a hypervisor.

    This is taken from in the OVN_Northbound database's table.

    Identifies the VLAN tag in the network traffic associated with that container's network interface.

    This column is used for a different purpose when is localnet (see Localnet Options, above).

    openvswitch-2.5.9/ovn/PaxHeaders.82075/ovn-nb.xml0000644000000000000000000000013213534540071016367 xustar0030 mtime=1567801401.769683872 30 atime=1567801402.117686428 30 ctime=1567801424.501851361 openvswitch-2.5.9/ovn/ovn-nb.xml0000644000175000017500000004560113534540071020063 0ustar00jpettitjpettit00000000000000

    This database is the interface between OVN and the cloud management system (CMS), such as OpenStack, running above it. The CMS produces almost all of the contents of the database. The ovn-northd program monitors the database contents, transforms it, and stores it into the database.

    We generally speak of ``the'' CMS, but one can imagine scenarios in which multiple CMSes manage different parts of an OVN deployment.

    External IDs

    Each of the tables in this database contains a special column, named external_ids. This column has the same form and purpose each place it appears.

    external_ids: map of string-string pairs
    Key-value pairs for use by the CMS. The CMS might use certain pairs, for example, to identify entities in its own configuration that correspond to those in this database.

    Each row represents one L2 logical switch.

    A name for the logical switch. This name has no special meaning or purpose other than to provide convenience for human interaction with the ovn-nb database. There is no requirement for the name to be unique. The logical switch's UUID should be used as the unique identifier.

    The logical ports connected to the logical switch.

    It is an error for multiple logical switches to include the same logical port.

    Access control rules that apply to packets within the logical switch. See External IDs at the beginning of this document.

    A port within an L2 logical switch.

    The logical port name.

    For entities (VMs or containers) that are spawned in the hypervisor, the name used here must match those used in the in the database's table, because hypervisors use as a lookup key to identify the network interface of that entity.

    For containers that share a VIF within a VM, the name can be any unique identifier. See Containers, below, for more information.

    Specify a type for this logical port. Logical ports can be used to model other types of connectivity into an OVN logical switch. The following types are defined:

    (empty string)
    A VM (or VIF) interface.
    router
    A connection to a logical router.
    localnet
    A connection to a locally accessible network from each ovn-controller instance. A logical switch can only have a single localnet port attached and at most one regular logical port. This is used to model direct connectivity to an existing network.
    vtep
    A port to a logical switch on a VTEP gateway.
    This column provides key/value settings specific to the logical port . The type-specific options are described individually below.

    These options apply when is router.

    If a given logical switch has multiple router ports, the rows that they reference must be all on the same (for different subnets).

    Required. The of the to which this logical switch port is connected.

    These options apply when is localnet.

    Required. The name of the network to which the localnet port is connected. Each hypervisor, via ovn-controller, uses its local configuration to determine exactly how to connect to this locally accessible network.

    These options apply when is vtep.

    Required. The name of the VTEP gateway. Required. A logical switch name connected by the VTEP gateway.

    When a large number of containers are nested within a VM, it may be too expensive to dedicate a VIF to each container. OVN can use VLAN tags to support such cases. Each container is assigned a VLAN ID and each packet that passes between the hypervisor and the VM is tagged with the appropriate ID for the container. Such VLAN IDs never appear on a physical wire, even inside a tunnel, so they need not be unique except relative to a single VM on a hypervisor.

    These columns are used for VIFs that represent nested containers using shared VIFs. For VMs and for containers that have dedicated VIFs, they are empty.

    The VM interface through which the nested container sends its network traffic. This must match the column for some other .

    The VLAN tag in the network traffic associated with a container's network interface.

    When is set to localnet, this can be set to indicate that the port represents a connection to a specific VLAN on a locally accessible network. The VLAN ID is used to match incoming traffic and is also added to outgoing traffic.

    This column is populated by ovn-northd, rather than by the CMS plugin as is most of this database. When a logical port is bound to a physical location in the OVN Southbound database table, ovn-northd sets this column to true; otherwise, or if the port becomes unbound later, it sets it to false. This allows the CMS to wait for a VM's (or container's) networking to become active before it allows the VM (or container) to start. This column is used to administratively set port state. If this column is empty or is set to true, the port is enabled. If this column is set to false, the port is disabled. A disabled port has all ingress and egress traffic dropped.

    Addresses owned by the logical port.

    Each element in the set must take one of the following forms:

    xx:xx:xx:xx:xx:xx

    An Ethernet address owned by the logical port. Like a physical Ethernet NIC, a logical port ordinarily has a single fixed Ethernet address.

    When a OVN logical switch processes a unicast Ethernet frame whose destination MAC address is in a logical port's column, it delivers it only to that port, as if a MAC learning process had learned that MAC address on the port.

    xx:xx:xx:xx:xx:xx a.b.c.d

    This form has all the effects of the previous form. It also indicates that the logical port owns the given IPv4 address.

    The OVN logical switch uses this information to synthesize responses to ARP requests without traversing the physical network. The OVN logical router connected to the logical switch, if any, uses this information to avoid issuing ARP requests for logical switch ports.

    Note that the order here is important. The Ethernet address must be listed before the IP address.

    unknown
    This indicates that the logical port has an unknown set of Ethernet addresses. When an OVN logical switch processes a unicast Ethernet frame whose destination MAC address is not in any logical port's column, it delivers it to the port (or ports) whose columns include unknown.

    A set of L2 (Ethernet) addresses from which the logical port is allowed to send packets and to which it is allowed to receive packets. If this column is empty, all addresses are permitted. Logical ports are always allowed to receive packets addressed to multicast and broadcast addresses.

    Each member of the set is an Ethernet address in the form xx:xx:xx:xx:xx:xx.

    This specification will be extended to support L3 port security.

    See External IDs at the beginning of this document.

    Each row in this table represents one ACL rule for a logical switch that points to it through its column. The column for the highest- matching row in this table determines a packet's treatment. If no row matches, packets are allowed by default. (Default-deny treatment is possible: add a rule with 0, 0 as , and deny as .)

    The ACL rule's priority. Rules with numerically higher priority take precedence over those with lower. If two ACL rules with the same priority both match, then the one actually applied to a packet is undefined.

    Return traffic from an allow-related flow is always allowed and cannot be changed through an ACL.

    Direction of the traffic to which this rule should apply:

    • from-lport: Used to implement filters on traffic arriving from a logical port. These rules are applied to the logical switch's ingress pipeline.
    • to-lport: Used to implement filters on traffic forwarded to a logical port. These rules are applied to the logical switch's egress pipeline.

    The packets that the ACL should match, in the same expression language used for the column in the OVN Southbound database's table. The outport logical port is only available in the to-lport direction (the inport is available in both directions).

    By default all traffic is allowed. When writing a more restrictive policy, it is important to remember to allow flows such as ARP and IPv6 neighbor discovery packets.

    Note that you can not create an ACL matching on a port with type=router.

    The action to take when the ACL rule matches:

    • allow: Forward the packet.
    • allow-related: Forward the packet and related traffic (e.g. inbound replies to an outbound connection).
    • drop: Silently drop the packet.
    • reject: Drop the packet, replying with a RST for TCP or ICMP unreachable message for other IP-based protocols. Not implemented--currently treated as drop

    If set to true, packets that match the ACL will trigger a log message on the transport node or nodes that perform ACL processing. Logging may be combined with any .

    Logging is not yet implemented.

    See External IDs at the beginning of this document.

    Each row represents one L3 logical router.

    A name for the logical router. This name has no special meaning or purpose other than to provide convenience for human interaction with the ovn-nb database. There is no requirement for the name to be unique. The logical router's UUID should be used as the unique identifier.

    The router's ports. IP address to use as default gateway, if any. See External IDs at the beginning of this document.

    A port within an L3 logical router.

    Exactly one row must reference a given logical router port.

    A name for the logical router port.

    In addition to provide convenience for human interaction with the ovn-nb database, this column is used as reference by its patch port in or another logical router port in .

    The IP address of the router and the netmask. For example, 192.168.0.1/24 indicates that the router's IP address is 192.168.0.1 and that packets destined to 192.168.0.x should be routed to this port. The Ethernet address that belongs to this router port. This column is used to administratively set port state. If this column is empty or is set to true, the port is enabled. If this column is set to false, the port is disabled. A disabled port has all ingress and egress traffic dropped.

    A given router port serves one of two purposes:

    • To attach a logical switch to a logical router. A logical router port of this type is referenced by exactly one of type router. The value of is set as router-port in column of . In this case column is empty.
    • To connect one logical router to another. This requires a pair of logical router ports, each connected to a different router. Each router port in the pair specifies the other in its column. No refers to the router port.

    For a router port used to connect two logical routers, this identifies the other router port in the pair by .

    For a router port attached to a logical switch, this column is empty.

    See External IDs at the beginning of this document.
    openvswitch-2.5.9/ovn/PaxHeaders.82075/ovn-nb.ovsschema0000644000000000000000000000013213534540071017557 xustar0030 mtime=1567801401.757683784 30 atime=1567801402.117686428 30 ctime=1567801424.501851361 openvswitch-2.5.9/ovn/ovn-nb.ovsschema0000644000175000017500000001105113534540071021243 0ustar00jpettitjpettit00000000000000{ "name": "OVN_Northbound", "version": "2.0.1", "cksum": "660370796 4618", "tables": { "Logical_Switch": { "columns": { "name": {"type": "string"}, "ports": {"type": {"key": {"type": "uuid", "refTable": "Logical_Port", "refType": "strong"}, "min": 0, "max": "unlimited"}}, "acls": {"type": {"key": {"type": "uuid", "refTable": "ACL", "refType": "strong"}, "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Logical_Port": { "columns": { "name": {"type": "string"}, "type": {"type": "string"}, "options": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "parent_name": {"type": {"key": "string", "min": 0, "max": 1}}, "tag": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4095}, "min": 0, "max": 1}}, "addresses": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "port_security": {"type": {"key": "string", "min": 0, "max": "unlimited"}}, "up": {"type": {"key": "boolean", "min": 0, "max": 1}}, "enabled": {"type": {"key": "boolean", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["name"]], "isRoot": false}, "ACL": { "columns": { "priority": {"type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 32767}}}, "direction": {"type": {"key": {"type": "string", "enum": ["set", ["from-lport", "to-lport"]]}}}, "match": {"type": "string"}, "action": {"type": {"key": {"type": "string", "enum": ["set", ["allow", "allow-related", "drop", "reject"]]}}}, "log": {"type": "boolean"}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": false}, "Logical_Router": { "columns": { "name": {"type": "string"}, "ports": {"type": {"key": {"type": "uuid", "refTable": "Logical_Router_Port", "refType": "strong"}, "min": 0, "max": "unlimited"}}, "default_gw": {"type": {"key": "string", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Logical_Router_Port": { "columns": { "name": {"type": "string"}, "network": {"type": "string"}, "mac": {"type": "string"}, "peer": {"type": {"key": {"type": "uuid", "refTable": "Logical_Router_Port", "refType": "strong"}, "min": 0, "max": 1}}, "enabled": {"type": {"key": "boolean", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["name"]], "isRoot": false} } } openvswitch-2.5.9/ovn/PaxHeaders.82075/controller0000644000000000000000000000013213534540121016550 xustar0030 mtime=1567801425.129855991 30 atime=1567801425.625859648 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/controller/0000755000175000017500000000000013534540121020313 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/lflow.c0000644000000000000000000000013013534540071020116 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.117855903 openvswitch-2.5.9/ovn/controller/lflow.c0000644000175000017500000003444413534540071021617 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "lflow.h" #include "dynamic-string.h" #include "ofctrl.h" #include "ofp-actions.h" #include "ofpbuf.h" #include "openvswitch/vlog.h" #include "ovn/controller/ovn-controller.h" #include "ovn/lib/actions.h" #include "ovn/lib/expr.h" #include "ovn/lib/ovn-sb-idl.h" #include "simap.h" VLOG_DEFINE_THIS_MODULE(lflow); /* Symbol table. */ /* Contains "struct expr_symbol"s for fields supported by OVN lflows. */ static struct shash symtab; static void add_logical_register(struct shash *symtab, enum mf_field_id id) { char name[8]; snprintf(name, sizeof name, "reg%d", id - MFF_REG0); expr_symtab_add_field(symtab, name, id, NULL, false); } static void symtab_init(void) { shash_init(&symtab); /* Reserve a pair of registers for the logical inport and outport. A full * 32-bit register each is bigger than we need, but the expression code * doesn't yet support string fields that occupy less than a full OXM. */ expr_symtab_add_string(&symtab, "inport", MFF_LOG_INPORT, NULL); expr_symtab_add_string(&symtab, "outport", MFF_LOG_OUTPORT, NULL); /* Logical registers. */ #define MFF_LOG_REG(ID) add_logical_register(&symtab, ID); MFF_LOG_REGS; #undef MFF_LOG_REG /* Connection tracking state. See CS_* in lib/packets.h. */ expr_symtab_add_field(&symtab, "ct_state", MFF_CT_STATE, NULL, false); expr_symtab_add_predicate(&symtab, "ct.trk", "ct_state[5]"); expr_symtab_add_subfield(&symtab, "ct.new", "ct.trk", "ct_state[0]"); expr_symtab_add_subfield(&symtab, "ct.est", "ct.trk", "ct_state[1]"); expr_symtab_add_subfield(&symtab, "ct.rel", "ct.trk", "ct_state[2]"); expr_symtab_add_subfield(&symtab, "ct.rpl", "ct.trk", "ct_state[3]"); expr_symtab_add_subfield(&symtab, "ct.inv", "ct.trk", "ct_state[4]"); /* Data fields. */ expr_symtab_add_field(&symtab, "eth.src", MFF_ETH_SRC, NULL, false); expr_symtab_add_field(&symtab, "eth.dst", MFF_ETH_DST, NULL, false); expr_symtab_add_field(&symtab, "eth.type", MFF_ETH_TYPE, NULL, true); expr_symtab_add_predicate(&symtab, "eth.bcast", "eth.dst == ff:ff:ff:ff:ff:ff"); expr_symtab_add_subfield(&symtab, "eth.mcast", NULL, "eth.dst[40]"); expr_symtab_add_field(&symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false); expr_symtab_add_predicate(&symtab, "vlan.present", "vlan.tci[12]"); expr_symtab_add_subfield(&symtab, "vlan.pcp", "vlan.present", "vlan.tci[13..15]"); expr_symtab_add_subfield(&symtab, "vlan.vid", "vlan.present", "vlan.tci[0..11]"); expr_symtab_add_predicate(&symtab, "ip4", "eth.type == 0x800"); expr_symtab_add_predicate(&symtab, "ip6", "eth.type == 0x86dd"); expr_symtab_add_predicate(&symtab, "ip", "ip4 || ip6"); expr_symtab_add_field(&symtab, "ip.proto", MFF_IP_PROTO, "ip", true); expr_symtab_add_field(&symtab, "ip.dscp", MFF_IP_DSCP, "ip", false); expr_symtab_add_field(&symtab, "ip.ecn", MFF_IP_ECN, "ip", false); expr_symtab_add_field(&symtab, "ip.ttl", MFF_IP_TTL, "ip", false); expr_symtab_add_field(&symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false); expr_symtab_add_field(&symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false); expr_symtab_add_predicate(&symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe"); expr_symtab_add_predicate(&symtab, "icmp4", "ip4 && ip.proto == 1"); expr_symtab_add_field(&symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4", false); expr_symtab_add_field(&symtab, "icmp4.code", MFF_ICMPV4_CODE, "icmp4", false); expr_symtab_add_field(&symtab, "ip6.src", MFF_IPV6_SRC, "ip6", false); expr_symtab_add_field(&symtab, "ip6.dst", MFF_IPV6_DST, "ip6", false); expr_symtab_add_field(&symtab, "ip6.label", MFF_IPV6_LABEL, "ip6", false); expr_symtab_add_predicate(&symtab, "icmp6", "ip6 && ip.proto == 58"); expr_symtab_add_field(&symtab, "icmp6.type", MFF_ICMPV6_TYPE, "icmp6", true); expr_symtab_add_field(&symtab, "icmp6.code", MFF_ICMPV6_CODE, "icmp6", true); expr_symtab_add_predicate(&symtab, "icmp", "icmp4 || icmp6"); expr_symtab_add_field(&symtab, "ip.frag", MFF_IP_FRAG, "ip", false); expr_symtab_add_predicate(&symtab, "ip.is_frag", "ip.frag[0]"); expr_symtab_add_predicate(&symtab, "ip.later_frag", "ip.frag[1]"); expr_symtab_add_predicate(&symtab, "ip.first_frag", "ip.is_frag && !ip.later_frag"); expr_symtab_add_predicate(&symtab, "arp", "eth.type == 0x806"); expr_symtab_add_field(&symtab, "arp.op", MFF_ARP_OP, "arp", false); expr_symtab_add_field(&symtab, "arp.spa", MFF_ARP_SPA, "arp", false); expr_symtab_add_field(&symtab, "arp.sha", MFF_ARP_SHA, "arp", false); expr_symtab_add_field(&symtab, "arp.tpa", MFF_ARP_TPA, "arp", false); expr_symtab_add_field(&symtab, "arp.tha", MFF_ARP_THA, "arp", false); expr_symtab_add_predicate(&symtab, "nd", "icmp6.type == {135, 136} && icmp6.code == 0"); expr_symtab_add_field(&symtab, "nd.target", MFF_ND_TARGET, "nd", false); expr_symtab_add_field(&symtab, "nd.sll", MFF_ND_SLL, "nd && icmp6.type == 135", false); expr_symtab_add_field(&symtab, "nd.tll", MFF_ND_TLL, "nd && icmp6.type == 136", false); expr_symtab_add_predicate(&symtab, "tcp", "ip.proto == 6"); expr_symtab_add_field(&symtab, "tcp.src", MFF_TCP_SRC, "tcp", false); expr_symtab_add_field(&symtab, "tcp.dst", MFF_TCP_DST, "tcp", false); expr_symtab_add_field(&symtab, "tcp.flags", MFF_TCP_FLAGS, "tcp", false); expr_symtab_add_predicate(&symtab, "udp", "ip.proto == 17"); expr_symtab_add_field(&symtab, "udp.src", MFF_UDP_SRC, "udp", false); expr_symtab_add_field(&symtab, "udp.dst", MFF_UDP_DST, "udp", false); expr_symtab_add_predicate(&symtab, "sctp", "ip.proto == 132"); expr_symtab_add_field(&symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false); expr_symtab_add_field(&symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false); } /* Logical datapaths and logical port numbers. */ /* A logical datapath. * * 'ports' maps 'logical_port' names to 'tunnel_key' values in the OVN_SB * Port_Binding table within the logical datapath. */ struct logical_datapath { struct hmap_node hmap_node; /* Indexed on 'uuid'. */ struct uuid uuid; /* UUID from Datapath_Binding row. */ uint32_t tunnel_key; /* 'tunnel_key' from Datapath_Binding row. */ struct simap ports; /* Logical port name to port number. */ }; /* Contains "struct logical_datapath"s. */ static struct hmap logical_datapaths = HMAP_INITIALIZER(&logical_datapaths); /* Finds and returns the logical_datapath for 'binding', or NULL if no such * logical_datapath exists. */ static struct logical_datapath * ldp_lookup(const struct sbrec_datapath_binding *binding) { struct logical_datapath *ldp; HMAP_FOR_EACH_IN_BUCKET (ldp, hmap_node, uuid_hash(&binding->header_.uuid), &logical_datapaths) { if (uuid_equals(&ldp->uuid, &binding->header_.uuid)) { return ldp; } } return NULL; } /* Creates a new logical_datapath for the given 'binding'. */ static struct logical_datapath * ldp_create(const struct sbrec_datapath_binding *binding) { struct logical_datapath *ldp; ldp = xmalloc(sizeof *ldp); hmap_insert(&logical_datapaths, &ldp->hmap_node, uuid_hash(&binding->header_.uuid)); ldp->uuid = binding->header_.uuid; ldp->tunnel_key = binding->tunnel_key; simap_init(&ldp->ports); return ldp; } static struct logical_datapath * ldp_lookup_or_create(const struct sbrec_datapath_binding *binding) { struct logical_datapath *ldp = ldp_lookup(binding); return ldp ? ldp : ldp_create(binding); } static void ldp_free(struct logical_datapath *ldp) { simap_destroy(&ldp->ports); hmap_remove(&logical_datapaths, &ldp->hmap_node); free(ldp); } /* Iterates through all of the records in the Port_Binding table, updating the * table of logical_datapaths to match the values found in active * Port_Bindings. */ static void ldp_run(struct controller_ctx *ctx) { struct logical_datapath *ldp; HMAP_FOR_EACH (ldp, hmap_node, &logical_datapaths) { simap_clear(&ldp->ports); } const struct sbrec_port_binding *binding; SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { struct logical_datapath *ldp = ldp_lookup_or_create(binding->datapath); simap_put(&ldp->ports, binding->logical_port, binding->tunnel_key); } const struct sbrec_multicast_group *mc; SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) { struct logical_datapath *ldp = ldp_lookup_or_create(mc->datapath); simap_put(&ldp->ports, mc->name, mc->tunnel_key); } struct logical_datapath *next_ldp; HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) { if (simap_is_empty(&ldp->ports)) { ldp_free(ldp); } } } static void ldp_destroy(void) { struct logical_datapath *ldp, *next_ldp; HMAP_FOR_EACH_SAFE (ldp, next_ldp, hmap_node, &logical_datapaths) { ldp_free(ldp); } } void lflow_init(void) { symtab_init(); } /* Translates logical flows in the Logical_Flow table in the OVN_SB database * into OpenFlow flows. See ovn-architecture(7) for more information. */ void lflow_run(struct controller_ctx *ctx, struct hmap *flow_table, const struct simap *ct_zones) { struct hmap flows = HMAP_INITIALIZER(&flows); uint32_t conj_id_ofs = 1; ldp_run(ctx); const struct sbrec_logical_flow *lflow; SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) { /* Find the "struct logical_datapath" asssociated with this * Logical_Flow row. If there's no such struct, that must be because * no logical ports are bound to that logical datapath, so there's no * point in maintaining any flows for it anyway, so skip it. */ const struct logical_datapath *ldp; ldp = ldp_lookup(lflow->logical_datapath); if (!ldp) { continue; } /* Determine translation of logical table IDs to physical table IDs. */ bool ingress = !strcmp(lflow->pipeline, "ingress"); uint8_t first_ptable = (ingress ? OFTABLE_LOG_INGRESS_PIPELINE : OFTABLE_LOG_EGRESS_PIPELINE); uint8_t ptable = first_ptable + lflow->table_id; uint8_t output_ptable = (ingress ? OFTABLE_REMOTE_OUTPUT : OFTABLE_LOG_TO_PHY); /* Translate OVN actions into OpenFlow actions. * * XXX Deny changes to 'outport' in egress pipeline. */ uint64_t ofpacts_stub[64 / 8]; struct ofpbuf ofpacts; struct expr *prereqs; char *error; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); error = actions_parse_string(lflow->actions, &symtab, &ldp->ports, ct_zones, first_ptable, LOG_PIPELINE_LEN, lflow->table_id, output_ptable, &ofpacts, &prereqs); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s", lflow->actions, error); free(error); continue; } /* Translate OVN match into table of OpenFlow matches. */ struct hmap matches; struct expr *expr; expr = expr_parse_string(lflow->match, &symtab, &error); if (!error) { if (prereqs) { expr = expr_combine(EXPR_T_AND, expr, prereqs); prereqs = NULL; } expr = expr_annotate(expr, &symtab, &error); } if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s", lflow->match, error); expr_destroy(prereqs); ofpbuf_uninit(&ofpacts); free(error); continue; } expr = expr_simplify(expr); expr = expr_normalize(expr); uint32_t n_conjs = expr_to_matches(expr, &ldp->ports, &matches); expr_destroy(expr); /* Prepare the OpenFlow matches for adding to the flow table. */ struct expr_match *m; HMAP_FOR_EACH (m, hmap_node, &matches) { match_set_metadata(&m->match, htonll(ldp->tunnel_key)); if (m->match.wc.masks.conj_id) { m->match.flow.conj_id += conj_id_ofs; } if (!m->n) { ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match, &ofpacts); } else { uint64_t conj_stubs[64 / 8]; struct ofpbuf conj; ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs); for (int i = 0; i < m->n; i++) { const struct cls_conjunction *src = &m->conjunctions[i]; struct ofpact_conjunction *dst; dst = ofpact_put_CONJUNCTION(&conj); dst->id = src->id + conj_id_ofs; dst->clause = src->clause; dst->n_clauses = src->n_clauses; } ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match, &conj); ofpbuf_uninit(&conj); } } /* Clean up. */ expr_matches_destroy(&matches); ofpbuf_uninit(&ofpacts); conj_id_ofs += n_conjs; } } void lflow_destroy(void) { expr_symtab_destroy(&symtab); ldp_destroy(); } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/ovn-controller.h0000644000000000000000000000013013534540071021763 xustar0030 mtime=1567801401.689683285 28 atime=1567801402.1136864 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/controller/ovn-controller.h0000644000175000017500000000275013534540071023457 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_CONTROLLER_H #define OVN_CONTROLLER_H 1 #include "simap.h" #include "ovn/lib/ovn-sb-idl.h" /* Linux supports a maximum of 64K zones, which seems like a fine default. */ #define MAX_CT_ZONES 65535 struct controller_ctx { struct ovsdb_idl *ovnsb_idl; struct ovsdb_idl_txn *ovnsb_idl_txn; struct ovsdb_idl *ovs_idl; struct ovsdb_idl_txn *ovs_idl_txn; }; const struct ovsrec_bridge *get_bridge(struct ovsdb_idl *, const char *br_name); const struct sbrec_chassis *get_chassis(struct ovsdb_idl *, const char *chassis_id); /* Must be a bit-field ordered from most-preferred (higher number) to * least-preferred (lower number). */ enum chassis_tunnel_type { GENEVE = 1 << 2, STT = 1 << 1, VXLAN = 1 << 0 }; uint32_t get_tunnel_type(const char *name); #endif /* ovn/ovn-controller.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/binding.h0000644000000000000000000000013013534540071020412 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.113855873 openvswitch-2.5.9/ovn/controller/binding.h0000644000175000017500000000210113534540071022074 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_BINDING_H #define OVN_BINDING_H 1 #include struct controller_ctx; struct ovsdb_idl; struct ovsrec_bridge; struct simap; void binding_register_ovs_idl(struct ovsdb_idl *); void binding_run(struct controller_ctx *, const struct ovsrec_bridge *br_int, const char *chassis_id, struct simap *ct_zones, unsigned long *ct_zone_bitmap); bool binding_cleanup(struct controller_ctx *, const char *chassis_id); #endif /* ovn/binding.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/pinctrl.h0000644000000000000000000000013213534540071020455 xustar0030 mtime=1567801401.705683402 30 atime=1567801402.117686428 30 ctime=1567801425.121855933 openvswitch-2.5.9/ovn/controller/pinctrl.h0000644000175000017500000000170113534540071022142 0ustar00jpettitjpettit00000000000000 /* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef DHCP_H #define DHCP_H 1 #include #include "meta-flow.h" struct ovsrec_bridge; struct controller_ctx; /* Interface for OVN main loop. */ void pinctrl_init(void); void pinctrl_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int); void pinctrl_wait(void); void pinctrl_destroy(void); #endif /* ovn/dhcp.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/chassis.h0000644000000000000000000000013013534540071020435 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.113855873 openvswitch-2.5.9/ovn/controller/chassis.h0000644000175000017500000000166513534540071022135 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_CHASSIS_H #define OVN_CHASSIS_H 1 #include struct controller_ctx; struct ovsdb_idl; struct ovsrec_bridge; void chassis_register_ovs_idl(struct ovsdb_idl *); void chassis_run(struct controller_ctx *, const char *chassis_id); bool chassis_cleanup(struct controller_ctx *, const char *chassis_id); #endif /* ovn/chassis.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/ovn-controller.8.xml0000644000000000000000000000013013534540071022502 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801424.505851391 openvswitch-2.5.9/ovn/controller/ovn-controller.8.xml0000644000175000017500000001730713534540071024202 0ustar00jpettitjpettit00000000000000

    Name

    ovn-controller -- Open Virtual Network local controller

    Synopsis

    ovn-controller [options] [ovs-database]

    Description

    ovn-controller is the local controller daemon for OVN, the Open Virtual Network. It connects up to the OVN Southbound database (see ovn-sb(5)) over the OVSDB protocol, and down to the Open vSwitch database (see ovs-vswitchd.conf.db(5)) over the OVSDB protocol and to ovs-vswitchd(8) via OpenFlow. Each hypervisor and software gateway in an OVN deployment runs its own independent copy of ovn-controller; thus, ovn-controller's downward connections are machine-local and do not run over a physical network.

    Configuration

    ovn-controller retrieves most of its configuration information from the local Open vSwitch's ovsdb-server instance. The default location is db.sock in the local Open vSwitch's "run" directory. It may be overridden by specifying the ovs-database argument in one of the following forms:

    • ssl:ip:port

      The specified SSL port on the host at the given ip, which must be expressed as an IP address (not a DNS name) in IPv4 or IPv6 address format. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: ssl:[::1]:6640. The --private-key, --certificate and either of --ca-cert or --bootstrap-ca-cert options are mandatory when this form is used.

    • tcp:ip:port

      Connect to the given TCP port on ip, where ip can be IPv4 or IPv6 address. If ip is an IPv6 address, then wrap ip with square brackets, e.g.: tcp:[::1]:6640.

    • unix:file

      On POSIX, connect to the Unix domain server socket named file.

      On Windows, connect to a localhost TCP port whose value is written in file.

    ovn-controller assumes it gets configuration information from the following keys in the Open_vSwitch table of the local OVS instance:

    external_ids:system-id
    The chassis name to use in the Chassis table.
    external_ids:ovn-bridge
    The integration bridge to which logical ports are attached. The default is br-int. If this bridge does not exist when ovn-controller starts, it will be created automatically with the default configuration suggested in ovn-architecture(7).
    external_ids:ovn-remote

    The OVN database that this system should connect to for its configuration.

    Currently, ovn-controller does not support changing this setting mid-run. If the value needs to change, the daemon must be restarted. (This behavior should be improved.)

    external_ids:ovn-encap-type

    The encapsulation type that a chassis should use to connect to this node. Multiple encapsulation types may be specified with a comma-separated list. Each listed encapsulation type will be paired with ovn-encap-ip.

    Supported tunnel types for connecting hypervisors are geneve and stt. Gateways may use geneve, vxlan, or stt.

    Due to the limited amount of metadata in vxlan, the capabilities and performance of connected gateways will be reduced versus other tunnel formats.

    external_ids:ovn-encap-ip
    The IP address that a chassis should use to connect to this node using encapsulation types specified by external_ids:ovn-encap-type.
    external_ids:ovn-bridge-mappings
    A list of key-value pairs that map a physical network name to a local ovs bridge that provides connectivity to that network. An example value mapping two physical network names to two ovs bridges would be: physnet1:br-eth0,physnet2:br-eth1.

    Open vSwitch Database Usage

    ovn-controller uses a number of external-ids keys in the Open vSwitch database to keep track of ports and interfaces. For proper operation, users should not change or clear these keys:

    external_ids:ovn-chassis-id in the Port table
    The presence of this key identifies a tunnel port within the integration bridge as one created by ovn-controller to reach a remote chassis. Its value is the chassis ID of the remote chassis.
    external-ids:ovn-localnet-port in the Port table

    The presence of this key identifies a patch port as one created by ovn-controller to connect the integration bridge and another bridge to implement a localnet logical port. Its value is the name of the physical network that the port implements. See external_ids:ovn-bridge-mappings, above, for more information.

    Each localnet logical port is implemented as a pair of patch ports, one in the integration bridge, one in a different bridge, with the same external-ids:ovn-localnet-port value.

    external-ids:ovn-logical-patch-port in the Port table

    This key identifies a patch port as one created by ovn-controller to implement an OVN logical patch port within the integration bridge. Its value is the name of the OVN logical patch port that it implements.

    Runtime Management Commands

    ovs-appctl can send commands to a running ovn-controller process. The currently supported commands are described below.

    exit
    Causes ovn-controller to gracefully terminate.
    ct-zone-list
    Lists each local logical port and its connection tracking zone.

    openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/automake.mk0000644000000000000000000000013013534540071020766 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.609852158 openvswitch-2.5.9/ovn/controller/automake.mk0000644000175000017500000000146013534540071022457 0ustar00jpettitjpettit00000000000000bin_PROGRAMS += ovn/controller/ovn-controller ovn_controller_ovn_controller_SOURCES = \ ovn/controller/binding.c \ ovn/controller/binding.h \ ovn/controller/chassis.c \ ovn/controller/chassis.h \ ovn/controller/encaps.c \ ovn/controller/encaps.h \ ovn/controller/lflow.c \ ovn/controller/lflow.h \ ovn/controller/ofctrl.c \ ovn/controller/ofctrl.h \ ovn/controller/pinctrl.c \ ovn/controller/pinctrl.h \ ovn/controller/patch.c \ ovn/controller/patch.h \ ovn/controller/ovn-controller.c \ ovn/controller/ovn-controller.h \ ovn/controller/physical.c \ ovn/controller/physical.h ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la man_MANS += ovn/controller/ovn-controller.8 EXTRA_DIST += ovn/controller/ovn-controller.8.xml DISTCLEANFILES += ovn/controller/ovn-controller.8 openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/physical.c0000644000000000000000000000013013534540071020607 xustar0030 mtime=1567801401.693683314 28 atime=1567801402.1136864 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/controller/physical.c0000644000175000017500000007601013534540071022303 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "physical.h" #include "lflow.h" #include "match.h" #include "ofctrl.h" #include "ofp-actions.h" #include "ofpbuf.h" #include "ovn-controller.h" #include "ovn/lib/ovn-sb-idl.h" #include "openvswitch/vlog.h" #include "shash.h" #include "simap.h" #include "smap.h" #include "sset.h" #include "vswitch-idl.h" VLOG_DEFINE_THIS_MODULE(physical); void physical_register_ovs_idl(struct ovsdb_idl *ovs_idl) { ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_ofport); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); } /* Maps from a chassis to the OpenFlow port number of the tunnel that can be * used to reach that chassis. */ struct chassis_tunnel { struct hmap_node hmap_node; const char *chassis_id; ofp_port_t ofport; enum chassis_tunnel_type type; }; static struct chassis_tunnel * chassis_tunnel_find(struct hmap *tunnels, const char *chassis_id) { struct chassis_tunnel *tun; HMAP_FOR_EACH_WITH_HASH (tun, hmap_node, hash_string(chassis_id, 0), tunnels) { if (!strcmp(tun->chassis_id, chassis_id)) { return tun; } } return NULL; } static void put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits, struct ofpbuf *ofpacts) { struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts); sf->field = mf_from_id(dst); sf->flow_has_vlan = false; ovs_be64 n_value = htonll(value); bitwise_copy(&n_value, 8, 0, &sf->value, sf->field->n_bytes, ofs, n_bits); bitwise_one(&sf->mask, sf->field->n_bytes, ofs, n_bits); } static void put_move(enum mf_field_id src, int src_ofs, enum mf_field_id dst, int dst_ofs, int n_bits, struct ofpbuf *ofpacts) { struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts); move->src.field = mf_from_id(src); move->src.ofs = src_ofs; move->src.n_bits = n_bits; move->dst.field = mf_from_id(dst); move->dst.ofs = dst_ofs; move->dst.n_bits = n_bits; } static void put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts) { struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts); resubmit->in_port = OFPP_IN_PORT; resubmit->table_id = table_id; } static void put_encapsulation(enum mf_field_id mff_ovn_geneve, const struct chassis_tunnel *tun, const struct sbrec_datapath_binding *datapath, uint16_t outport, struct ofpbuf *ofpacts) { if (tun->type == GENEVE) { put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts); put_load(outport, mff_ovn_geneve, 0, 32, ofpacts); put_move(MFF_LOG_INPORT, 0, mff_ovn_geneve, 16, 15, ofpacts); } else if (tun->type == STT) { put_load(datapath->tunnel_key | (outport << 24), MFF_TUN_ID, 0, 64, ofpacts); put_move(MFF_LOG_INPORT, 0, MFF_TUN_ID, 40, 15, ofpacts); } else if (tun->type == VXLAN) { put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts); } else { OVS_NOT_REACHED(); } } static void put_stack(enum mf_field_id field, struct ofpact_stack *stack) { stack->subfield.field = mf_from_id(field); stack->subfield.ofs = 0; stack->subfield.n_bits = stack->subfield.field->n_bits; } void physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const char *this_chassis_id, const struct simap *ct_zones, struct hmap *flow_table) { struct simap localvif_to_ofport = SIMAP_INITIALIZER(&localvif_to_ofport); struct hmap tunnels = HMAP_INITIALIZER(&tunnels); struct simap localnet_to_ofport = SIMAP_INITIALIZER(&localnet_to_ofport); for (int i = 0; i < br_int->n_ports; i++) { const struct ovsrec_port *port_rec = br_int->ports[i]; if (!strcmp(port_rec->name, br_int->name)) { continue; } const char *chassis_id = smap_get(&port_rec->external_ids, "ovn-chassis-id"); if (chassis_id && !strcmp(chassis_id, this_chassis_id)) { continue; } const char *localnet = smap_get(&port_rec->external_ids, "ovn-localnet-port"); const char *logpatch = smap_get(&port_rec->external_ids, "ovn-logical-patch-port"); for (int j = 0; j < port_rec->n_interfaces; j++) { const struct ovsrec_interface *iface_rec = port_rec->interfaces[j]; /* Get OpenFlow port number. */ if (!iface_rec->n_ofport) { continue; } int64_t ofport = iface_rec->ofport[0]; if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) { continue; } /* Record as patch to local net, logical patch port, chassis, or * local logical port. */ bool is_patch = !strcmp(iface_rec->type, "patch"); if (is_patch && localnet) { simap_put(&localnet_to_ofport, localnet, ofport); break; } else if (is_patch && logpatch) { /* Logical patch ports can be handled just like VIFs. */ simap_put(&localvif_to_ofport, logpatch, ofport); break; } else if (chassis_id) { enum chassis_tunnel_type tunnel_type; if (!strcmp(iface_rec->type, "geneve")) { tunnel_type = GENEVE; if (!mff_ovn_geneve) { continue; } } else if (!strcmp(iface_rec->type, "stt")) { tunnel_type = STT; } else if (!strcmp(iface_rec->type, "vxlan")) { tunnel_type = VXLAN; } else { continue; } struct chassis_tunnel *tun = xmalloc(sizeof *tun); hmap_insert(&tunnels, &tun->hmap_node, hash_string(chassis_id, 0)); tun->chassis_id = chassis_id; tun->ofport = u16_to_ofp(ofport); tun->type = tunnel_type; break; } else { const char *iface_id = smap_get(&iface_rec->external_ids, "iface-id"); if (iface_id) { simap_put(&localvif_to_ofport, iface_id, ofport); } } } } struct ofpbuf ofpacts; ofpbuf_init(&ofpacts, 0); struct binding_elem { struct ovs_list list_elem; const struct sbrec_port_binding *binding; }; /* The bindings for a given VLAN on a localnet port. */ struct localnet_vlan { struct hmap_node node; int tag; struct ovs_list bindings; }; /* A hash of localnet_vlans, hashed on VLAN ID, for a localnet port */ struct localnet_bindings { ofp_port_t ofport; struct hmap vlans; }; /* Maps from network name to "struct localnet_bindings". */ struct shash localnet_inputs = SHASH_INITIALIZER(&localnet_inputs); /* Contains bare "struct hmap_node"s whose hash values are the tunnel_key * of datapaths with at least one local port binding. */ struct hmap local_datapaths = HMAP_INITIALIZER(&local_datapaths); /* Set up flows in table 0 for physical-to-logical translation and in table * 64 for logical-to-physical translation. */ const struct sbrec_port_binding *binding; SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { /* Find the OpenFlow port for the logical port, as 'ofport'. This is * one of: * * - If the port is a VIF on the chassis we're managing, the * OpenFlow port for the VIF. 'tun' will be NULL. * * In this or the next case, for a container nested inside a VM * and accessible via a VLAN, 'tag' is the VLAN ID; otherwise * 'tag' is 0. * * The same logic handles logical patch ports. * * - If the port is on a remote chassis, the OpenFlow port for a * tunnel to the VIF's remote chassis. 'tun' identifies that * tunnel. * * - If the port is a "localnet" port for a network that is * attached to the chassis we're managing, the OpenFlow port for * the localnet port (a patch port). * * The "localnet" port may be configured with a VLAN ID. If so, * 'tag' will be set to that VLAN ID; otherwise 'tag' is 0. */ int tag = 0; ofp_port_t ofport; if (!strcmp(binding->type, "localnet")) { const char *network = smap_get(&binding->options, "network_name"); if (!network) { continue; } ofport = u16_to_ofp(simap_get(&localnet_to_ofport, network)); if (ofport && binding->tag) { tag = *binding->tag; } } else if (binding->parent_port && *binding->parent_port) { if (!binding->tag) { continue; } ofport = u16_to_ofp(simap_get(&localvif_to_ofport, binding->parent_port)); if (ofport) { tag = *binding->tag; } } else { ofport = u16_to_ofp(simap_get(&localvif_to_ofport, binding->logical_port)); } const struct chassis_tunnel *tun = NULL; if (!ofport) { if (!binding->chassis) { continue; } tun = chassis_tunnel_find(&tunnels, binding->chassis->name); if (!tun) { continue; } ofport = tun->ofport; } struct match match; if (!tun) { int zone_id = simap_get(ct_zones, binding->logical_port); /* Packets that arrive from a vif can belong to a VM or * to a container located inside that VM. Packets that * arrive from containers have a tag (vlan) associated with them. */ /* Table 0, Priority 150 and 100. * ============================== * * Priority 150 is for tagged traffic. This may be containers in a * VM or a VLAN on a local network. For such traffic, match on the * tags and then strip the tag. * * Priority 100 is for traffic belonging to VMs or untagged locally * connected networks. * * For both types of traffic: set MFF_LOG_INPORT to the logical * input port, MFF_LOG_DATAPATH to the logical datapath, and * resubmit into the logical ingress pipeline starting at table * 16. */ if (!strcmp(binding->type, "localnet")) { /* The same OpenFlow port may correspond to localnet ports * attached to more than one logical datapath, so keep track of * all associated bindings and add a flow at the end. */ const char *network = smap_get(&binding->options, "network_name"); struct localnet_bindings *ln_bindings; struct hmap_node *node; struct localnet_vlan *ln_vlan; ln_bindings = shash_find_data(&localnet_inputs, network); if (!ln_bindings) { ln_bindings = xmalloc(sizeof *ln_bindings); ln_bindings->ofport = ofport; hmap_init(&ln_bindings->vlans); shash_add(&localnet_inputs, network, ln_bindings); } node = hmap_first_with_hash(&ln_bindings->vlans, tag); if (node) { ASSIGN_CONTAINER(ln_vlan, node, node); } else { ln_vlan = xmalloc(sizeof *ln_vlan); ln_vlan->tag = tag; list_init(&ln_vlan->bindings); hmap_insert(&ln_bindings->vlans, &ln_vlan->node, tag); } struct binding_elem *b = xmalloc(sizeof *b); b->binding = binding; list_insert(&ln_vlan->bindings, &b->list_elem); } else { struct hmap_node *ld; ld = hmap_first_with_hash(&local_datapaths, binding->datapath->tunnel_key); if (!ld) { ld = xmalloc(sizeof *ld); hmap_insert(&local_datapaths, ld, binding->datapath->tunnel_key); } ofpbuf_clear(&ofpacts); match_init_catchall(&match); match_set_in_port(&match, ofport); if (tag) { match_set_dl_vlan(&match, htons(tag)); } if (zone_id) { put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); } /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ put_load(binding->datapath->tunnel_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 32, &ofpacts); /* Strip vlans. */ if (tag) { ofpact_put_STRIP_VLAN(&ofpacts); } /* Resubmit to first logical ingress pipeline table. */ put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, tag ? 150 : 100, &match, &ofpacts); } /* Table 33, priority 100. * ======================= * * Implements output to local hypervisor. Each flow matches a * logical output port on the local hypervisor, and resubmits to * table 34. */ match_init_catchall(&match); ofpbuf_clear(&ofpacts); /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ match_set_metadata(&match, htonll(binding->datapath->tunnel_key)); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, binding->tunnel_key); if (zone_id) { put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); } /* Resubmit to table 34. */ put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, &match, &ofpacts); /* Table 64, Priority 100. * ======================= * * Deliver the packet to the local vif. */ match_init_catchall(&match); ofpbuf_clear(&ofpacts); match_set_metadata(&match, htonll(binding->datapath->tunnel_key)); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, binding->tunnel_key); if (tag) { /* For containers sitting behind a local vif, tag the packets * before delivering them. */ struct ofpact_vlan_vid *vlan_vid; vlan_vid = ofpact_put_SET_VLAN_VID(&ofpacts); vlan_vid->vlan_vid = tag; vlan_vid->push_vlan_if_needed = true; /* A packet might need to hair-pin back into its ingress * OpenFlow port (to a different logical port, which we already * checked back in table 34), so set the in_port to zero. */ put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(&ofpacts)); put_load(0, MFF_IN_PORT, 0, 16, &ofpacts); } ofpact_put_OUTPUT(&ofpacts)->port = ofport; if (tag) { /* Revert the tag added to the packets headed to containers * in the previous step. If we don't do this, the packets * that are to be broadcasted to a VM in the same logical * switch will also contain the tag. Also revert the zero'd * in_port. */ ofpact_put_STRIP_VLAN(&ofpacts); put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(&ofpacts)); } ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, &match, &ofpacts); } else { /* Table 32, priority 100. * ======================= * * Implements output to remote hypervisors. Each flow matches an * output port that includes a logical port on a remote hypervisor, * and tunnels the packet to that hypervisor. */ match_init_catchall(&match); ofpbuf_clear(&ofpacts); /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */ match_set_metadata(&match, htonll(binding->datapath->tunnel_key)); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, binding->tunnel_key); put_encapsulation(mff_ovn_geneve, tun, binding->datapath, binding->tunnel_key, &ofpacts); /* Output to tunnel. */ ofpact_put_OUTPUT(&ofpacts)->port = ofport; ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, &match, &ofpacts); } /* Table 34, Priority 100. * ======================= * * Drop packets whose logical inport and outport are the same. */ match_init_catchall(&match); ofpbuf_clear(&ofpacts); match_set_metadata(&match, htonll(binding->datapath->tunnel_key)); match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, binding->tunnel_key); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, binding->tunnel_key); ofctrl_add_flow(flow_table, OFTABLE_DROP_LOOPBACK, 100, &match, &ofpacts); } /* Handle output to multicast groups, in tables 32 and 33. */ const struct sbrec_multicast_group *mc; struct ofpbuf remote_ofpacts; ofpbuf_init(&remote_ofpacts, 0); SBREC_MULTICAST_GROUP_FOR_EACH (mc, ctx->ovnsb_idl) { struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis); struct match match; match_init_catchall(&match); match_set_metadata(&match, htonll(mc->datapath->tunnel_key)); match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key); /* Go through all of the ports in the multicast group: * * - For remote ports, add the chassis to 'remote_chassis'. * * - For local ports (other than logical patch ports), add actions * to 'ofpacts' to set the output port and resubmit. * * - For logical patch ports, add actions to 'remote_ofpacts' * instead. (If we put them in 'ofpacts', then the output * would happen on every hypervisor in the multicast group, * effectively duplicating the packet.) */ ofpbuf_clear(&ofpacts); ofpbuf_clear(&remote_ofpacts); for (size_t i = 0; i < mc->n_ports; i++) { struct sbrec_port_binding *port = mc->ports[i]; if (port->datapath != mc->datapath) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports " "in wrong datapath", UUID_ARGS(&mc->header_.uuid)); continue; } int zone_id = simap_get(ct_zones, port->logical_port); if (zone_id) { put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts); } if (!strcmp(port->type, "patch")) { put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &remote_ofpacts); put_resubmit(OFTABLE_DROP_LOOPBACK, &remote_ofpacts); } else if (simap_contains(&localvif_to_ofport, (port->parent_port && *port->parent_port) ? port->parent_port : port->logical_port)) { put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts); } else if (port->chassis) { sset_add(&remote_chassis, port->chassis->name); } else if (!strcmp(port->type, "localnet")) { const char *network = smap_get(&port->options, "network_name"); if (!network) { continue; } if (!simap_contains(&localnet_to_ofport, network)) { continue; } put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); put_resubmit(OFTABLE_DROP_LOOPBACK, &ofpacts); } } /* Table 33, priority 100. * ======================= * * Handle output to the local logical ports in the multicast group, if * any. */ bool local_ports = ofpacts.size > 0; if (local_ports) { /* Following delivery to local logical ports, restore the multicast * group as the logical output port. */ put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, &match, &ofpacts); } /* Table 32, priority 100. * ======================= * * Handle output to the remote chassis in the multicast group, if * any. */ if (!sset_is_empty(&remote_chassis) || remote_ofpacts.size > 0) { if (remote_ofpacts.size > 0) { /* Following delivery to logical patch ports, restore the * multicast group as the logical output port. */ put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &remote_ofpacts); } const char *chassis; const struct chassis_tunnel *prev = NULL; SSET_FOR_EACH (chassis, &remote_chassis) { const struct chassis_tunnel *tun = chassis_tunnel_find(&tunnels, chassis); if (!tun) { continue; } if (!prev || tun->type != prev->type) { put_encapsulation(mff_ovn_geneve, tun, mc->datapath, mc->tunnel_key, &remote_ofpacts); prev = tun; } ofpact_put_OUTPUT(&remote_ofpacts)->port = tun->ofport; } if (remote_ofpacts.size) { if (local_ports) { put_resubmit(OFTABLE_LOCAL_OUTPUT, &remote_ofpacts); } ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, &match, &remote_ofpacts); } } sset_destroy(&remote_chassis); } ofpbuf_uninit(&remote_ofpacts); /* Table 0, priority 100. * ====================== * * Process packets that arrive from a remote hypervisor (by matching * on tunnel in_port). */ /* Add flows for Geneve and STT encapsulations. These * encapsulations have metadata about the ingress and egress logical * ports. We set MFF_LOG_DATAPATH, MFF_LOG_INPORT, and * MFF_LOG_OUTPORT from the tunnel key data, then resubmit to table * 33 to handle packets to the local hypervisor. */ struct chassis_tunnel *tun; HMAP_FOR_EACH (tun, hmap_node, &tunnels) { struct match match = MATCH_CATCHALL_INITIALIZER; match_set_in_port(&match, tun->ofport); ofpbuf_clear(&ofpacts); if (tun->type == GENEVE) { put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); put_move(mff_ovn_geneve, 16, MFF_LOG_INPORT, 0, 15, &ofpacts); put_move(mff_ovn_geneve, 0, MFF_LOG_OUTPORT, 0, 16, &ofpacts); } else if (tun->type == STT) { put_move(MFF_TUN_ID, 40, MFF_LOG_INPORT, 0, 15, &ofpacts); put_move(MFF_TUN_ID, 24, MFF_LOG_OUTPORT, 0, 16, &ofpacts); put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); } else if (tun->type == VXLAN) { /* We'll handle VXLAN later. */ continue; } else { OVS_NOT_REACHED(); } put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts); } /* Add flows for VXLAN encapsulations. Due to the limited amount of * metadata, we only support VXLAN for connections to gateways. The * VNI is used to populate MFF_LOG_DATAPATH. The gateway's logical * port is set to MFF_LOG_INPORT. Then the packet is resubmitted to * table 16 to determine the logical egress port. * * xxx Due to resubmitting to table 16, broadcasts will be re-sent to * xxx all logical ports, including non-local ones which could cause * xxx duplicate packets to be received by multiply-connected gateways. */ HMAP_FOR_EACH (tun, hmap_node, &tunnels) { if (tun->type != VXLAN) { continue; } SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { struct match match = MATCH_CATCHALL_INITIALIZER; if (!binding->chassis || strcmp(tun->chassis_id, binding->chassis->name)) { continue; } match_set_in_port(&match, tun->ofport); match_set_tun_id(&match, htonll(binding->datapath->tunnel_key)); ofpbuf_clear(&ofpacts); put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts); put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 15, &ofpacts); put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts); } } /* Table 32, Priority 0. * ======================= * * Resubmit packets that are not directed at tunnels or part of a * multicast group to the local output table. */ struct match match; match_init_catchall(&match); ofpbuf_clear(&ofpacts); put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts); /* Table 34, Priority 0. * ======================= * * Resubmit packets that don't output to the ingress port (already checked * in table 33) to the logical egress pipeline, clearing the logical * registers (for consistent behavior with packets that get tunneled). */ match_init_catchall(&match); ofpbuf_clear(&ofpacts); #define MFF_LOG_REG(ID) put_load(0, ID, 0, 32, &ofpacts); MFF_LOG_REGS; #undef MFF_LOG_REGS put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts); ofctrl_add_flow(flow_table, OFTABLE_DROP_LOOPBACK, 0, &match, &ofpacts); ofpbuf_uninit(&ofpacts); simap_destroy(&localvif_to_ofport); struct chassis_tunnel *tun_next; HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) { hmap_remove(&tunnels, &tun->hmap_node); free(tun); } hmap_destroy(&tunnels); /* Table 0, Priority 150 and 100. * ============================== * * We have now determined the full set of port bindings associated with * each "localnet" network. Only create flows for datapaths that have * another local binding. Otherwise, we know it would just be dropped. * * Use priority 150 for inputs that match both the network and a VLAN tag. * Use priority 100 for matching untagged traffic from the local network. */ struct shash_node *ln_bindings_node, *ln_bindings_node_next; SHASH_FOR_EACH_SAFE (ln_bindings_node, ln_bindings_node_next, &localnet_inputs) { struct localnet_bindings *ln_bindings = ln_bindings_node->data; struct localnet_vlan *ln_vlan, *ln_vlan_next; HMAP_FOR_EACH_SAFE (ln_vlan, ln_vlan_next, node, &ln_bindings->vlans) { struct match match; match_init_catchall(&match); match_set_in_port(&match, ln_bindings->ofport); if (ln_vlan->tag) { match_set_dl_vlan(&match, htons(ln_vlan->tag)); } struct ofpbuf ofpacts; ofpbuf_init(&ofpacts, 0); if (ln_vlan->tag) { ofpact_put_STRIP_VLAN(&ofpacts); } uint32_t ofpacts_orig_size = ofpacts.size; struct binding_elem *b; LIST_FOR_EACH_POP (b, list_elem, &ln_vlan->bindings) { struct hmap_node *ld; ld = hmap_first_with_hash(&local_datapaths, b->binding->datapath->tunnel_key); if (ld) { /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */ put_load(b->binding->datapath->tunnel_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts); put_load(b->binding->tunnel_key, MFF_LOG_INPORT, 0, 32, &ofpacts); put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts); } free(b); } if (ofpacts.size > ofpacts_orig_size) { ofctrl_add_flow(flow_table, 0, ln_vlan->tag ? 150 : 100, &match, &ofpacts); } ofpbuf_uninit(&ofpacts); hmap_remove(&ln_bindings->vlans, &ln_vlan->node); free(ln_vlan); } shash_delete(&localnet_inputs, ln_bindings_node); hmap_destroy(&ln_bindings->vlans); free(ln_bindings); } shash_destroy(&localnet_inputs); struct hmap_node *node; while ((node = hmap_first(&local_datapaths))) { hmap_remove(&local_datapaths, node); free(node); } hmap_destroy(&local_datapaths); simap_destroy(&localnet_to_ofport); } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/lflow.h0000644000000000000000000000013013534540071020123 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.117855903 openvswitch-2.5.9/ovn/controller/lflow.h0000644000175000017500000000430413534540071021614 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_LFLOW_H #define OVN_LFLOW_H 1 #include "ovn/lib/logical-fields.h" /* Logical_Flow table translation to OpenFlow * ========================================== * * The Logical_Flow table obtained from the OVN_Southbound database works in * terms of logical entities, that is, logical flows among logical datapaths * and logical ports. This code translates these logical flows into OpenFlow * flows that, again, work in terms of logical entities implemented through * OpenFlow extensions (e.g. registers represent the logical input and output * ports). * * Physical-to-logical and logical-to-physical translation are implemented in * physical.[ch] as separate OpenFlow tables that run before and after, * respectively, the logical pipeline OpenFlow tables. */ #include struct controller_ctx; struct hmap; struct simap; struct uuid; /* OpenFlow table numbers. * * These are heavily documented in ovn-architecture(7), please update it if * you make any changes. */ #define OFTABLE_PHY_TO_LOG 0 #define OFTABLE_LOG_INGRESS_PIPELINE 16 /* First of LOG_PIPELINE_LEN tables. */ #define OFTABLE_REMOTE_OUTPUT 32 #define OFTABLE_LOCAL_OUTPUT 33 #define OFTABLE_DROP_LOOPBACK 34 #define OFTABLE_LOG_EGRESS_PIPELINE 48 /* First of LOG_PIPELINE_LEN tables. */ #define OFTABLE_LOG_TO_PHY 64 /* The number of tables for the ingress and egress pipelines. */ #define LOG_PIPELINE_LEN 16 void lflow_init(void); void lflow_run(struct controller_ctx *, struct hmap *flow_table, const struct simap *ct_zones); void lflow_destroy(void); #endif /* ovn/lflow.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/chassis.c0000644000000000000000000000013013534540071020430 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.113855873 openvswitch-2.5.9/ovn/controller/chassis.c0000644000175000017500000001204413534540071022121 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "chassis.h" #include "lib/dynamic-string.h" #include "lib/vswitch-idl.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn-controller.h" VLOG_DEFINE_THIS_MODULE(chassis); void chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl) { ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch); ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids); } static const char * pop_tunnel_name(uint32_t *type) { if (*type & GENEVE) { *type &= ~GENEVE; return "geneve"; } else if (*type & STT) { *type &= ~STT; return "stt"; } else if (*type & VXLAN) { *type &= ~VXLAN; return "vxlan"; } OVS_NOT_REACHED(); } void chassis_run(struct controller_ctx *ctx, const char *chassis_id) { if (!ctx->ovnsb_idl_txn) { return; } const struct sbrec_chassis *chassis_rec; const struct ovsrec_open_vswitch *cfg; const char *encap_type, *encap_ip; static bool inited = false; chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id); cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); if (!cfg) { VLOG_INFO("No Open_vSwitch row defined."); return; } encap_type = smap_get(&cfg->external_ids, "ovn-encap-type"); encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip"); if (!encap_type || !encap_ip) { VLOG_INFO("Need to specify an encap type and ip"); return; } char *tokstr = xstrdup(encap_type); char *save_ptr = NULL; char *token; uint32_t req_tunnels = 0; for (token = strtok_r(tokstr, ",", &save_ptr); token != NULL; token = strtok_r(NULL, ",", &save_ptr)) { uint32_t type = get_tunnel_type(token); if (!type) { VLOG_INFO("Unknown tunnel type: %s", token); } req_tunnels |= type; } free(tokstr); if (chassis_rec) { /* Compare desired tunnels against those currently in the database. */ uint32_t cur_tunnels = 0; bool same = true; for (int i = 0; i < chassis_rec->n_encaps; i++) { cur_tunnels |= get_tunnel_type(chassis_rec->encaps[i]->type); same = same && !strcmp(chassis_rec->encaps[i]->ip, encap_ip); } same = same && req_tunnels == cur_tunnels; if (same) { /* Nothing changed. */ inited = true; return; } else if (!inited) { struct ds cur_encaps = DS_EMPTY_INITIALIZER; for (int i = 0; i < chassis_rec->n_encaps; i++) { ds_put_format(&cur_encaps, "%s,", chassis_rec->encaps[i]->type); } ds_chomp(&cur_encaps, ','); VLOG_WARN("Chassis config changing on startup, make sure " "multiple chassis are not configured : %s/%s->%s/%s", ds_cstr(&cur_encaps), chassis_rec->encaps[0]->ip, encap_type, encap_ip); ds_destroy(&cur_encaps); } } ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: registering chassis '%s'", chassis_id); if (!chassis_rec) { chassis_rec = sbrec_chassis_insert(ctx->ovnsb_idl_txn); sbrec_chassis_set_name(chassis_rec, chassis_id); } int n_encaps = count_1bits(req_tunnels); struct sbrec_encap **encaps = xmalloc(n_encaps * sizeof *encaps); for (int i = 0; i < n_encaps; i++) { const char *type = pop_tunnel_name(&req_tunnels); encaps[i] = sbrec_encap_insert(ctx->ovnsb_idl_txn); sbrec_encap_set_type(encaps[i], type); sbrec_encap_set_ip(encaps[i], encap_ip); } sbrec_chassis_set_encaps(chassis_rec, encaps, n_encaps); free(encaps); inited = true; } /* Returns true if the database is all cleaned up, false if more work is * required. */ bool chassis_cleanup(struct controller_ctx *ctx, const char *chassis_id) { if (!chassis_id) { return true; } /* Delete Chassis row. */ const struct sbrec_chassis *chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id); if (!chassis_rec) { return true; } if (ctx->ovnsb_idl_txn) { ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller: unregistering chassis '%s'", chassis_id); sbrec_chassis_delete(chassis_rec); } return false; } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/binding.c0000644000000000000000000000013013534540071020405 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801425.113855873 openvswitch-2.5.9/ovn/controller/binding.c0000644000175000017500000001577013534540071022107 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "binding.h" #include "lib/bitmap.h" #include "lib/sset.h" #include "lib/util.h" #include "lib/vswitch-idl.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn-controller.h" VLOG_DEFINE_THIS_MODULE(binding); void binding_register_ovs_idl(struct ovsdb_idl *ovs_idl) { ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch); ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids); } static void get_local_iface_ids(const struct ovsrec_bridge *br_int, struct sset *lports) { int i; for (i = 0; i < br_int->n_ports; i++) { const struct ovsrec_port *port_rec = br_int->ports[i]; const char *iface_id; int j; if (!strcmp(port_rec->name, br_int->name)) { continue; } for (j = 0; j < port_rec->n_interfaces; j++) { const struct ovsrec_interface *iface_rec; iface_rec = port_rec->interfaces[j]; iface_id = smap_get(&iface_rec->external_ids, "iface-id"); if (!iface_id) { continue; } sset_add(lports, iface_id); } } } static void update_ct_zones(struct sset *lports, struct simap *ct_zones, unsigned long *ct_zone_bitmap) { struct simap_node *ct_zone, *ct_zone_next; const char *iface_id; int scan_start = 1; /* xxx This is wasteful to assign a zone to each port--even if no * xxx security policy is applied. */ /* Delete any zones that are associated with removed ports. */ SIMAP_FOR_EACH_SAFE(ct_zone, ct_zone_next, ct_zones) { if (!sset_contains(lports, ct_zone->name)) { bitmap_set0(ct_zone_bitmap, ct_zone->data); simap_delete(ct_zones, ct_zone); } } /* Assign a unique zone id for each logical port. */ SSET_FOR_EACH(iface_id, lports) { size_t zone; if (simap_contains(ct_zones, iface_id)) { continue; } /* We assume that there are 64K zones and that we own them all. */ zone = bitmap_scan(ct_zone_bitmap, 0, scan_start, MAX_CT_ZONES + 1); if (zone == MAX_CT_ZONES + 1) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "exhausted all ct zones"); return; } scan_start = zone + 1; bitmap_set1(ct_zone_bitmap, zone); simap_put(ct_zones, iface_id, zone); /* xxx We should erase any old entries for this * xxx zone, but we need a generic interface to the conntrack * xxx table. */ } } void binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, const char *chassis_id, struct simap *ct_zones, unsigned long *ct_zone_bitmap) { const struct sbrec_chassis *chassis_rec; const struct sbrec_port_binding *binding_rec; struct sset lports, all_lports; const char *name; if (!ctx->ovnsb_idl_txn) { return; } chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id); if (!chassis_rec) { return; } sset_init(&lports); sset_init(&all_lports); if (br_int) { get_local_iface_ids(br_int, &lports); } else { /* We have no integration bridge, therefore no local logical ports. * We'll remove our chassis from all port binding records below. */ } sset_clone(&all_lports, &lports); ovsdb_idl_txn_add_comment( ctx->ovnsb_idl_txn,"ovn-controller: updating port bindings for '%s'", chassis_id); /* Run through each binding record to see if it is resident on this * chassis and update the binding accordingly. This includes both * directly connected logical ports and children of those ports. */ SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { if (sset_find_and_delete(&lports, binding_rec->logical_port) || (binding_rec->parent_port && binding_rec->parent_port[0] && sset_contains(&all_lports, binding_rec->parent_port))) { if (binding_rec->parent_port && binding_rec->parent_port[0]) { /* Add child logical port to the set of all local ports. */ sset_add(&all_lports, binding_rec->logical_port); } if (binding_rec->chassis == chassis_rec) { continue; } if (binding_rec->chassis) { VLOG_INFO("Changing chassis for lport %s from %s to %s", binding_rec->logical_port, binding_rec->chassis->name, chassis_rec->name); } sbrec_port_binding_set_chassis(binding_rec, chassis_rec); } else if (binding_rec->chassis == chassis_rec) { sbrec_port_binding_set_chassis(binding_rec, NULL); } } SSET_FOR_EACH (name, &lports) { VLOG_DBG("No port binding record for lport %s", name); } update_ct_zones(&all_lports, ct_zones, ct_zone_bitmap); sset_destroy(&lports); sset_destroy(&all_lports); } /* Returns true if the database is all cleaned up, false if more work is * required. */ bool binding_cleanup(struct controller_ctx *ctx, const char *chassis_id) { if (!ctx->ovnsb_idl_txn) { return false; } if (!chassis_id) { return true; } const struct sbrec_chassis *chassis_rec = get_chassis(ctx->ovnsb_idl, chassis_id); if (!chassis_rec) { return true; } ovsdb_idl_txn_add_comment( ctx->ovnsb_idl_txn, "ovn-controller: removing all port bindings for '%s'", chassis_id); const struct sbrec_port_binding *binding_rec; bool any_changes = false; SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { if (binding_rec->chassis == chassis_rec) { sbrec_port_binding_set_chassis(binding_rec, NULL); any_changes = true; } } return !any_changes; } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/encaps.c0000644000000000000000000000013013534540071020244 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.117855903 openvswitch-2.5.9/ovn/controller/encaps.c0000644000175000017500000002357113534540071021744 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "encaps.h" #include "lib/hash.h" #include "lib/sset.h" #include "lib/util.h" #include "lib/vswitch-idl.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn-controller.h" VLOG_DEFINE_THIS_MODULE(encaps); void encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl) { ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge); ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids); ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type); ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options); } /* Enough context to create a new tunnel, using tunnel_add(). */ struct tunnel_ctx { /* Contains "struct port_hash_node"s. Used to figure out what * existing tunnels should be deleted: we index all of the OVN encap * rows into this data structure, then as existing rows are * generated we remove them. After generating all the rows, any * remaining in 'tunnel_hmap' must be deleted from the database. */ struct hmap tunnel_hmap; /* Names of all ports in the bridge, to allow checking uniqueness when * adding a new tunnel. */ struct sset port_names; struct ovsdb_idl_txn *ovs_txn; const struct ovsrec_bridge *br_int; }; struct port_hash_node { struct hmap_node node; const struct ovsrec_port *port; const struct ovsrec_bridge *bridge; }; static size_t port_hash(const char *chassis_id, const char *type, const char *ip) { size_t hash = hash_string(chassis_id, 0); hash = hash_string(type, hash); return hash_string(ip, hash); } static size_t port_hash_rec(const struct ovsrec_port *port) { const char *chassis_id, *ip; const struct ovsrec_interface *iface; chassis_id = smap_get(&port->external_ids, "ovn-chassis-id"); if (!chassis_id || !port->n_interfaces) { /* This should not happen for an OVN-created port. */ return 0; } iface = port->interfaces[0]; ip = smap_get(&iface->options, "remote_ip"); return port_hash(chassis_id, iface->type, ip); } static char * tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id) { int i; for (i = 0; i < UINT16_MAX; i++) { char *port_name; port_name = xasprintf("ovn-%.6s-%x", chassis_id, i); if (!sset_contains(&tc->port_names, port_name)) { return port_name; } free(port_name); } return NULL; } static void tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id, const struct sbrec_encap *encap) { struct port_hash_node *hash_node; /* Check whether such a row already exists in OVS. If so, remove it * from 'tc->tunnel_hmap' and we're done. */ HMAP_FOR_EACH_WITH_HASH (hash_node, node, port_hash(new_chassis_id, encap->type, encap->ip), &tc->tunnel_hmap) { const struct ovsrec_port *port = hash_node->port; const char *chassis_id = smap_get(&port->external_ids, "ovn-chassis-id"); const struct ovsrec_interface *iface; const char *ip; if (!chassis_id || !port->n_interfaces) { continue; } iface = port->interfaces[0]; ip = smap_get(&iface->options, "remote_ip"); if (!ip) { continue; } if (!strcmp(new_chassis_id, chassis_id) && !strcmp(encap->type, iface->type) && !strcmp(encap->ip, ip)) { hmap_remove(&tc->tunnel_hmap, &hash_node->node); free(hash_node); return; } } /* No such port, so add one. */ struct smap options = SMAP_INITIALIZER(&options); struct ovsrec_port *port, **ports; struct ovsrec_interface *iface; char *port_name; size_t i; port_name = tunnel_create_name(tc, new_chassis_id); if (!port_name) { VLOG_WARN("Unable to allocate unique name for '%s' tunnel", new_chassis_id); return; } iface = ovsrec_interface_insert(tc->ovs_txn); ovsrec_interface_set_name(iface, port_name); ovsrec_interface_set_type(iface, encap->type); smap_add(&options, "remote_ip", encap->ip); smap_add(&options, "key", "flow"); ovsrec_interface_set_options(iface, &options); smap_destroy(&options); port = ovsrec_port_insert(tc->ovs_txn); ovsrec_port_set_name(port, port_name); ovsrec_port_set_interfaces(port, &iface, 1); const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id); ovsrec_port_set_external_ids(port, &id); ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1)); for (i = 0; i < tc->br_int->n_ports; i++) { ports[i] = tc->br_int->ports[i]; } ports[tc->br_int->n_ports] = port; ovsrec_bridge_verify_ports(tc->br_int); ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1); sset_add(&tc->port_names, port_name); free(port_name); free(ports); } static void bridge_delete_port(const struct ovsrec_bridge *br, const struct ovsrec_port *port) { struct ovsrec_port **ports; size_t i, n; ports = xmalloc(sizeof *br->ports * br->n_ports); for (i = n = 0; i < br->n_ports; i++) { if (br->ports[i] != port) { ports[n++] = br->ports[i]; } } ovsrec_bridge_verify_ports(br); ovsrec_bridge_set_ports(br, ports, n); free(ports); } static struct sbrec_encap * preferred_encap(const struct sbrec_chassis *chassis_rec) { struct sbrec_encap *best_encap = NULL; uint32_t best_type = 0; for (int i = 0; i < chassis_rec->n_encaps; i++) { uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type); if (tun_type > best_type) { best_type = tun_type; best_encap = chassis_rec->encaps[i]; } } return best_encap; } void encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, const char *chassis_id) { if (!ctx->ovs_idl_txn || !br_int) { return; } const struct sbrec_chassis *chassis_rec; const struct ovsrec_bridge *br; struct tunnel_ctx tc = { .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap), .port_names = SSET_INITIALIZER(&tc.port_names), .br_int = br_int }; tc.ovs_txn = ctx->ovs_idl_txn; ovsdb_idl_txn_add_comment(tc.ovs_txn, "ovn-controller: modifying OVS tunnels '%s'", chassis_id); /* Collect all port names into tc.port_names. * * Collect all the OVN-created tunnels into tc.tunnel_hmap. */ OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) { size_t i; for (i = 0; i < br->n_ports; i++) { const struct ovsrec_port *port = br->ports[i]; sset_add(&tc.port_names, port->name); if (smap_get(&port->external_ids, "ovn-chassis-id")) { struct port_hash_node *hash_node = xzalloc(sizeof *hash_node); hash_node->bridge = br; hash_node->port = port; hmap_insert(&tc.tunnel_hmap, &hash_node->node, port_hash_rec(port)); } } } SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) { if (strcmp(chassis_rec->name, chassis_id)) { /* Create tunnels to the other chassis. */ const struct sbrec_encap *encap = preferred_encap(chassis_rec); if (!encap) { VLOG_INFO("No supported encaps for '%s'", chassis_rec->name); continue; } tunnel_add(&tc, chassis_rec->name, encap); } } /* Delete any existing OVN tunnels that were not still around. */ struct port_hash_node *hash_node, *next_hash_node; HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &tc.tunnel_hmap) { hmap_remove(&tc.tunnel_hmap, &hash_node->node); bridge_delete_port(hash_node->bridge, hash_node->port); free(hash_node); } hmap_destroy(&tc.tunnel_hmap); sset_destroy(&tc.port_names); } /* Returns true if the database is all cleaned up, false if more work is * required. */ bool encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int) { if (!br_int) { return true; } /* Delete all the OVS-created tunnels from the integration bridge. */ struct ovsrec_port **ports = xmalloc(sizeof *br_int->ports * br_int->n_ports); size_t n = 0; for (size_t i = 0; i < br_int->n_ports; i++) { if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) { ports[n++] = br_int->ports[i]; } } bool any_changes = n != br_int->n_ports; if (any_changes && ctx->ovs_idl_txn) { ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn, "ovn-controller: destroying tunnels"); ovsrec_bridge_verify_ports(br_int); ovsrec_bridge_set_ports(br_int, ports, n); } free(ports); return !any_changes; } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/patch.c0000644000000000000000000000013013534540071020072 xustar0030 mtime=1567801401.689683285 28 atime=1567801402.1136864 30 ctime=1567801425.125855962 openvswitch-2.5.9/ovn/controller/patch.c0000644000175000017500000002365213534540071021572 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "patch.h" #include "hash.h" #include "lib/vswitch-idl.h" #include "openvswitch/vlog.h" #include "ovn-controller.h" VLOG_DEFINE_THIS_MODULE(patch); static char * patch_port_name(const char *src, const char *dst) { return xasprintf("patch-%s-to-%s", src, dst); } /* Return true if 'port' is a patch port with the specified 'peer'. */ static bool match_patch_port(const struct ovsrec_port *port, const char *peer) { for (size_t i = 0; i < port->n_interfaces; i++) { struct ovsrec_interface *iface = port->interfaces[i]; if (strcmp(iface->type, "patch")) { continue; } const char *iface_peer = smap_get(&iface->options, "peer"); if (peer && !strcmp(iface_peer, peer)) { return true; } } return false; } /* Creates a patch port in bridge 'src' named 'src_name', whose peer is * 'dst_name' in bridge 'dst'. Initializes the patch port's external-ids:'key' * to 'key'. * * If such a patch port already exists, removes it from 'existing_ports'. */ static void create_patch_port(struct controller_ctx *ctx, const char *key, const char *value, const struct ovsrec_bridge *src, const char *src_name, const struct ovsrec_bridge *dst, const char *dst_name, struct shash *existing_ports) { for (size_t i = 0; i < src->n_ports; i++) { if (match_patch_port(src->ports[i], dst_name)) { /* Patch port already exists on 'src'. */ shash_find_and_delete(existing_ports, src->ports[i]->name); return; } } ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn, "ovn-controller: creating patch port '%s' from '%s' to '%s'", src_name, src->name, dst->name); struct ovsrec_interface *iface; iface = ovsrec_interface_insert(ctx->ovs_idl_txn); ovsrec_interface_set_name(iface, src_name); ovsrec_interface_set_type(iface, "patch"); const struct smap options = SMAP_CONST1(&options, "peer", dst_name); ovsrec_interface_set_options(iface, &options); struct ovsrec_port *port; port = ovsrec_port_insert(ctx->ovs_idl_txn); ovsrec_port_set_name(port, src_name); ovsrec_port_set_interfaces(port, &iface, 1); const struct smap ids = SMAP_CONST1(&ids, key, value); ovsrec_port_set_external_ids(port, &ids); struct ovsrec_port **ports; ports = xmalloc(sizeof *ports * (src->n_ports + 1)); memcpy(ports, src->ports, sizeof *ports * src->n_ports); ports[src->n_ports] = port; ovsrec_bridge_verify_ports(src); ovsrec_bridge_set_ports(src, ports, src->n_ports + 1); free(ports); } /* Creates a pair of patch ports that connect bridges 'b1' and 'b2', using a * port named 'name1' and 'name2' in each respective bridge. * external-ids:'key' in each port is initialized to 'value'. * * If one or both of the ports already exists, leaves it there and removes it * from 'existing_ports'. */ static void create_patch_ports(struct controller_ctx *ctx, const char *key, const char *value, const struct ovsrec_bridge *b1, const struct ovsrec_bridge *b2, struct shash *existing_ports) { char *name1 = patch_port_name(b1->name, b2->name); char *name2 = patch_port_name(b2->name, b1->name); create_patch_port(ctx, key, value, b1, name1, b2, name2, existing_ports); create_patch_port(ctx, key, value, b2, name2, b1, name1, existing_ports); free(name2); free(name1); } static void remove_port(struct controller_ctx *ctx, const struct ovsrec_port *port) { const struct ovsrec_bridge *bridge; /* We know the port we want to delete, but we have to find the bridge its * on to do so. Note this only runs on a config change that should be * pretty rare. */ OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) { size_t i; for (i = 0; i < bridge->n_ports; i++) { if (bridge->ports[i] != port) { continue; } struct ovsrec_port **new_ports; new_ports = xmemdup(bridge->ports, sizeof *new_ports * (bridge->n_ports - 1)); if (i != bridge->n_ports - 1) { /* Removed port was not last */ new_ports[i] = bridge->ports[bridge->n_ports - 1]; } ovsrec_bridge_verify_ports(bridge); ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1); free(new_ports); ovsrec_port_delete(port); return; } } } /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for * the local bridge mappings. Removes any patch ports for bridge mappings that * already existed from 'existing_ports'. */ static void add_bridge_mappings(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, struct shash *existing_ports) { /* Get ovn-bridge-mappings. */ const char *mappings_cfg = ""; const struct ovsrec_open_vswitch *cfg; cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); if (cfg) { mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings"); if (!mappings_cfg) { mappings_cfg = ""; } } /* Create patch ports. */ char *cur, *next, *start; next = start = xstrdup(mappings_cfg); while ((cur = strsep(&next, ",")) && *cur) { char *network, *bridge = cur; const struct ovsrec_bridge *ovs_bridge; network = strsep(&bridge, ":"); if (!bridge || !*network || !*bridge) { VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'", mappings_cfg); break; } ovs_bridge = get_bridge(ctx->ovs_idl, bridge); if (!ovs_bridge) { VLOG_WARN("Bridge '%s' not found for network '%s'", bridge, network); continue; } create_patch_ports(ctx, "ovn-localnet-port", network, br_int, ovs_bridge, existing_ports); } free(start); } /* Add one OVS patch port for each OVN logical patch port. * * This is suboptimal for several reasons. First, it creates an OVS port for * every OVN logical patch port, not just for the ones that are actually useful * on this hypervisor. Second, it's wasteful to create an OVS patch port per * OVN logical patch port, when really there's no benefit to them beyond a way * to identify how a packet ingressed into a logical datapath. * * There are two obvious ways to improve the situation here, by modifying * OVS: * * 1. Add a way to configure in OVS which fields are preserved on a hop * across an OVS patch port. If MFF_LOG_DATAPATH and MFF_LOG_INPORT * were preserved, then only a single pair of OVS patch ports would be * required regardless of the number of OVN logical patch ports. * * 2. Add a new OpenFlow extension action modeled on "resubmit" that also * saves and restores the packet data and metadata (the inability to do * this is the only reason that "resubmit" can't be used already). Or * add OpenFlow extension actions to otherwise save and restore packet * data and metadata. */ static void add_logical_patch_ports(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int, struct shash *existing_ports) { const struct sbrec_port_binding *binding; SBREC_PORT_BINDING_FOR_EACH (binding, ctx->ovnsb_idl) { if (!strcmp(binding->type, "patch")) { const char *local = binding->logical_port; const char *peer = smap_get(&binding->options, "peer"); if (!peer) { continue; } char *src_name = patch_port_name(local, peer); char *dst_name = patch_port_name(peer, local); create_patch_port(ctx, "ovn-logical-patch-port", local, br_int, src_name, br_int, dst_name, existing_ports); free(dst_name); free(src_name); } } } void patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int) { if (!ctx->ovs_idl_txn) { return; } /* Figure out what patch ports already exist. */ struct shash existing_ports = SHASH_INITIALIZER(&existing_ports); const struct ovsrec_port *port; OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) { if (smap_get(&port->external_ids, "ovn-localnet-port") || smap_get(&port->external_ids, "ovn-logical-patch-port")) { shash_add(&existing_ports, port->name, port); } } /* Create in the database any patch ports that should exist. Remove from * 'existing_ports' any patch ports that do exist in the database and * should be there. */ add_bridge_mappings(ctx, br_int, &existing_ports); add_logical_patch_ports(ctx, br_int, &existing_ports); /* Now 'existing_ports' only still contains patch ports that exist in the * database but shouldn't. Delete them from the database. */ struct shash_node *port_node, *port_next_node; SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) { struct ovsrec_port *port = port_node->data; shash_delete(&existing_ports, port_node); remove_port(ctx, port); } shash_destroy(&existing_ports); } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/ofctrl.c0000644000000000000000000000013013534540071020264 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.121855933 openvswitch-2.5.9/ovn/controller/ofctrl.c0000644000175000017500000005070313534540071021761 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofctrl.h" #include "dirs.h" #include "dynamic-string.h" #include "hmap.h" #include "match.h" #include "ofp-actions.h" #include "ofp-msgs.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "openvswitch/vlog.h" #include "ovn-controller.h" #include "physical.h" #include "rconn.h" #include "socket-util.h" #include "vswitch-idl.h" VLOG_DEFINE_THIS_MODULE(ofctrl); /* An OpenFlow flow. */ struct ovn_flow { /* Key. */ struct hmap_node hmap_node; uint8_t table_id; uint16_t priority; struct match match; /* Data. */ struct ofpact *ofpacts; size_t ofpacts_len; }; static uint32_t ovn_flow_hash(const struct ovn_flow *); static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target); static char *ovn_flow_to_string(const struct ovn_flow *); static void ovn_flow_log(const struct ovn_flow *, const char *action); static void ovn_flow_destroy(struct ovn_flow *); static ovs_be32 queue_msg(struct ofpbuf *); static void queue_flow_mod(struct ofputil_flow_mod *); /* OpenFlow connection to the switch. */ static struct rconn *swconn; /* Last seen sequence number for 'swconn'. When this differs from * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ static unsigned int seqno; /* Connection state machine. */ #define STATES \ STATE(S_NEW) \ STATE(S_TLV_TABLE_REQUESTED) \ STATE(S_TLV_TABLE_MOD_SENT) \ STATE(S_CLEAR_FLOWS) \ STATE(S_UPDATE_FLOWS) enum ofctrl_state { #define STATE(NAME) NAME, STATES #undef STATE }; /* Current state. */ static enum ofctrl_state state; /* Transaction IDs for messages in flight to the switch. */ static ovs_be32 xid, xid2; /* Counter for in-flight OpenFlow messages on 'swconn'. We only send a new * round of flow table modifications to the switch when the counter falls to * zero, to avoid unbounded buffering. */ static struct rconn_packet_counter *tx_counter; /* Flow table of "struct ovn_flow"s, that holds the flow table currently * installed in the switch. */ static struct hmap installed_flows; /* MFF_* field ID for our Geneve option. In S_TLV_TABLE_MOD_SENT, this is * the option we requested (we don't know whether we obtained it yet). In * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */ static enum mf_field_id mff_ovn_geneve; static void ovn_flow_table_clear(struct hmap *flow_table); static void ovn_flow_table_destroy(struct hmap *flow_table); static void ofctrl_recv(const struct ofp_header *, enum ofptype); void ofctrl_init(void) { swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); tx_counter = rconn_packet_counter_create(); hmap_init(&installed_flows); } /* S_NEW, for a new connection. * * Sends NXT_TLV_TABLE_REQUEST and transitions to * S_TLV_TABLE_REQUESTED. */ static void run_S_NEW(void) { struct ofpbuf *buf = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_REQUEST, rconn_get_version(swconn), 0); xid = queue_msg(buf); state = S_TLV_TABLE_REQUESTED; } static void recv_S_NEW(const struct ofp_header *oh OVS_UNUSED, enum ofptype type OVS_UNUSED) { OVS_NOT_REACHED(); } /* S_TLV_TABLE_REQUESTED, when NXT_TLV_TABLE_REQUEST has been sent * and we're waiting for a reply. * * If we receive an NXT_TLV_TABLE_REPLY: * * - If it contains our tunnel metadata option, assign its field ID to * mff_ovn_geneve and transition to S_CLEAR_FLOWS. * * - Otherwise, if there is an unused tunnel metadata field ID, send * NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST, and transition to * S_TLV_TABLE_MOD_SENT. * * - Otherwise, log an error, disable Geneve, and transition to * S_CLEAR_FLOWS. * * If we receive an OFPT_ERROR: * * - Log an error, disable Geneve, and transition to S_CLEAR_FLOWS. */ static void run_S_TLV_TABLE_REQUESTED(void) { } static void recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type) { if (oh->xid != xid) { ofctrl_recv(oh, type); } else if (type == OFPTYPE_NXT_TLV_TABLE_REPLY) { struct ofputil_tlv_table_reply reply; enum ofperr error = ofputil_decode_tlv_table_reply(oh, &reply); if (error) { VLOG_ERR("failed to decode TLV table request (%s)", ofperr_to_string(error)); goto error; } const struct ofputil_tlv_map *map; uint64_t md_free = UINT64_MAX; BUILD_ASSERT(TUN_METADATA_NUM_OPTS == 64); LIST_FOR_EACH (map, list_node, &reply.mappings) { if (map->option_class == OVN_GENEVE_CLASS && map->option_type == OVN_GENEVE_TYPE && map->option_len == OVN_GENEVE_LEN) { if (map->index >= TUN_METADATA_NUM_OPTS) { VLOG_ERR("desired Geneve tunnel option 0x%"PRIx16"," "%"PRIu8",%"PRIu8" already in use with " "unsupported index %"PRIu16, map->option_class, map->option_type, map->option_len, map->index); goto error; } else { mff_ovn_geneve = MFF_TUN_METADATA0 + map->index; state = S_CLEAR_FLOWS; return; } } if (map->index < TUN_METADATA_NUM_OPTS) { md_free &= ~(UINT64_C(1) << map->index); } } VLOG_DBG("OVN Geneve option not found"); if (!md_free) { VLOG_ERR("no Geneve options free for use by OVN"); goto error; } unsigned int index = rightmost_1bit_idx(md_free); mff_ovn_geneve = MFF_TUN_METADATA0 + index; struct ofputil_tlv_map tm; tm.option_class = OVN_GENEVE_CLASS; tm.option_type = OVN_GENEVE_TYPE; tm.option_len = OVN_GENEVE_LEN; tm.index = index; struct ofputil_tlv_table_mod ttm; ttm.command = NXTTMC_ADD; list_init(&ttm.mappings); list_push_back(&ttm.mappings, &tm.list_node); xid = queue_msg(ofputil_encode_tlv_table_mod(OFP13_VERSION, &ttm)); xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION)); state = S_TLV_TABLE_MOD_SENT; } else if (type == OFPTYPE_ERROR) { VLOG_ERR("switch refused to allocate Geneve option (%s)", ofperr_to_string(ofperr_decode_msg(oh, NULL))); goto error; } else { char *s = ofp_to_string(oh, ntohs(oh->length), 1); VLOG_ERR("unexpected reply to TLV table request (%s)", s); free(s); goto error; } return; error: mff_ovn_geneve = 0; state = S_CLEAR_FLOWS; } /* S_TLV_TABLE_MOD_SENT, when NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST * have been sent and we're waiting for a reply to one or the other. * * If we receive an OFPT_ERROR: * * - If the error is NXTTMFC_ALREADY_MAPPED or NXTTMFC_DUP_ENTRY, we * raced with some other controller. Transition to S_NEW. * * - Otherwise, log an error, disable Geneve, and transition to * S_CLEAR_FLOWS. * * If we receive OFPT_BARRIER_REPLY: * * - Set the tunnel metadata field ID to the one that we requested. * Transition to S_CLEAR_FLOWS. */ static void run_S_TLV_TABLE_MOD_SENT(void) { } static void recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type) { if (oh->xid != xid && oh->xid != xid2) { ofctrl_recv(oh, type); } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) { state = S_CLEAR_FLOWS; } else if (oh->xid == xid && type == OFPTYPE_ERROR) { enum ofperr error = ofperr_decode_msg(oh, NULL); if (error == OFPERR_NXTTMFC_ALREADY_MAPPED || error == OFPERR_NXTTMFC_DUP_ENTRY) { VLOG_INFO("raced with another controller adding " "Geneve option (%s); trying again", ofperr_to_string(error)); state = S_NEW; } else { VLOG_ERR("error adding Geneve option (%s)", ofperr_to_string(error)); goto error; } } else { char *s = ofp_to_string(oh, ntohs(oh->length), 1); VLOG_ERR("unexpected reply to Geneve option allocation request (%s)", s); free(s); goto error; } return; error: state = S_CLEAR_FLOWS; } /* S_CLEAR_FLOWS, after we've established a Geneve metadata field ID and it's * time to set up some flows. * * Sends an OFPT_TABLE_MOD to clear all flows, then transitions to * S_UPDATE_FLOWS. */ static void run_S_CLEAR_FLOWS(void) { /* Send a flow_mod to delete all flows. */ struct ofputil_flow_mod fm = { .match = MATCH_CATCHALL_INITIALIZER, .table_id = OFPTT_ALL, .command = OFPFC_DELETE, }; queue_flow_mod(&fm); VLOG_DBG("clearing all flows"); /* Clear installed_flows, to match the state of the switch. */ ovn_flow_table_clear(&installed_flows); state = S_UPDATE_FLOWS; } static void recv_S_CLEAR_FLOWS(const struct ofp_header *oh, enum ofptype type) { ofctrl_recv(oh, type); } /* S_UPDATE_FLOWS, for maintaining the flow table over time. * * Compare the installed flows to the ones we want. Send OFPT_FLOW_MOD as * necessary. * * This is a terminal state. We only transition out of it if the connection * drops. */ static void run_S_UPDATE_FLOWS(void) { /* Nothing to do here. * * Being in this state enables ofctrl_put() to work, however. */ } static void recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type) { ofctrl_recv(oh, type); } /* Runs the OpenFlow state machine against 'br_int', which is local to the * hypervisor on which we are running. Attempts to negotiate a Geneve option * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. If successful, * returns the MFF_* field ID for the option, otherwise returns 0. */ enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int) { if (br_int) { char *target; target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name); if (strcmp(target, rconn_get_target(swconn))) { VLOG_INFO("%s: connecting to switch", target); rconn_connect(swconn, target, target); } free(target); } else { rconn_disconnect(swconn); } rconn_run(swconn); if (!rconn_is_connected(swconn)) { return 0; } if (seqno != rconn_get_connection_seqno(swconn)) { seqno = rconn_get_connection_seqno(swconn); state = S_NEW; } enum ofctrl_state old_state; do { old_state = state; switch (state) { #define STATE(NAME) case NAME: run_##NAME(); break; STATES #undef STATE default: OVS_NOT_REACHED(); } } while (state != old_state); for (int i = 0; state == old_state && i < 50; i++) { struct ofpbuf *msg = rconn_recv(swconn); if (!msg) { break; } const struct ofp_header *oh = msg->data; enum ofptype type; enum ofperr error; error = ofptype_decode(&type, oh); if (!error) { switch (state) { #define STATE(NAME) case NAME: recv_##NAME(oh, type); break; STATES #undef STATE default: OVS_NOT_REACHED(); } } else { char *s = ofp_to_string(oh, ntohs(oh->length), 1); VLOG_WARN("could not decode OpenFlow message (%s): %s", ofperr_to_string(error), s); free(s); } ofpbuf_delete(msg); } return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS ? mff_ovn_geneve : 0); } void ofctrl_wait(void) { rconn_run_wait(swconn); rconn_recv_wait(swconn); } void ofctrl_destroy(void) { rconn_destroy(swconn); ovn_flow_table_destroy(&installed_flows); rconn_packet_counter_destroy(tx_counter); } static ovs_be32 queue_msg(struct ofpbuf *msg) { const struct ofp_header *oh = msg->data; ovs_be32 xid = oh->xid; rconn_send(swconn, msg, tx_counter); return xid; } static void ofctrl_recv(const struct ofp_header *oh, enum ofptype type) { if (type == OFPTYPE_ECHO_REQUEST) { queue_msg(make_echo_reply(oh)); } else if (type != OFPTYPE_ECHO_REPLY && type != OFPTYPE_BARRIER_REPLY && type != OFPTYPE_PACKET_IN && type != OFPTYPE_PORT_STATUS && type != OFPTYPE_FLOW_REMOVED) { if (VLOG_IS_DBG_ENABLED()) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); char *s = ofp_to_string(oh, ntohs(oh->length), 2); VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s); free(s); } } } /* Flow table interface to the rest of ovn-controller. */ /* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to * the OpenFlow table numbered 'table_id' with the given 'priority'. The * caller retains ownership of 'match' and 'actions'. * * This just assembles the desired flow table in memory. Nothing is actually * sent to the switch until a later call to ofctrl_run(). * * The caller should initialize its own hmap to hold the flows. */ void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id, uint16_t priority, const struct match *match, const struct ofpbuf *actions) { struct ovn_flow *f = xmalloc(sizeof *f); f->table_id = table_id; f->priority = priority; f->match = *match; f->ofpacts = xmemdup(actions->data, actions->size); f->ofpacts_len = actions->size; f->hmap_node.hash = ovn_flow_hash(f); if (ovn_flow_lookup(desired_flows, f)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); if (!VLOG_DROP_INFO(&rl)) { char *s = ovn_flow_to_string(f); VLOG_INFO("dropping duplicate flow: %s", s); free(s); } ovn_flow_destroy(f); return; } hmap_insert(desired_flows, &f->hmap_node, f->hmap_node.hash); } /* ovn_flow. */ /* Returns a hash of the key in 'f'. */ static uint32_t ovn_flow_hash(const struct ovn_flow *f) { return hash_2words((f->table_id << 16) | f->priority, match_hash(&f->match, 0)); } /* Finds and returns an ovn_flow in 'flow_table' whose key is identical to * 'target''s key, or NULL if there is none. */ static struct ovn_flow * ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target) { struct ovn_flow *f; HMAP_FOR_EACH_WITH_HASH (f, hmap_node, target->hmap_node.hash, flow_table) { if (f->table_id == target->table_id && f->priority == target->priority && match_equal(&f->match, &target->match)) { return f; } } return NULL; } static char * ovn_flow_to_string(const struct ovn_flow *f) { struct ds s = DS_EMPTY_INITIALIZER; ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id); ds_put_format(&s, "priority=%"PRIu16", ", f->priority); match_format(&f->match, &s, OFP_DEFAULT_PRIORITY); ds_put_cstr(&s, ", actions="); ofpacts_format(f->ofpacts, f->ofpacts_len, &s); return ds_steal_cstr(&s); } static void ovn_flow_log(const struct ovn_flow *f, const char *action) { if (VLOG_IS_DBG_ENABLED()) { char *s = ovn_flow_to_string(f); VLOG_DBG("%s flow: %s", action, s); free(s); } } static void ovn_flow_destroy(struct ovn_flow *f) { if (f) { free(f->ofpacts); free(f); } } /* Flow tables of struct ovn_flow. */ static void ovn_flow_table_clear(struct hmap *flow_table) { struct ovn_flow *f, *next; HMAP_FOR_EACH_SAFE (f, next, hmap_node, flow_table) { hmap_remove(flow_table, &f->hmap_node); ovn_flow_destroy(f); } } static void ovn_flow_table_destroy(struct hmap *flow_table) { ovn_flow_table_clear(flow_table); hmap_destroy(flow_table); } /* Flow table update. */ static void queue_flow_mod(struct ofputil_flow_mod *fm) { fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_ANY; fm->out_group = OFPG_ANY; queue_msg(ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM)); } /* Replaces the flow table on the switch, if possible, by the flows in * 'flow_table', which should have been added with ofctrl_add_flow(). * Regardless of whether the flow table is updated, this deletes all of the * flows from 'flow_table' and frees them. (The hmap itself isn't * destroyed.) * * This called be called be ofctrl_run() within the main loop. */ void ofctrl_put(struct hmap *flow_table) { /* The flow table can be updated if the connection to the switch is up and * in the correct state and not backlogged with existing flow_mods. (Our * criteria for being backlogged appear very conservative, but the socket * between ovn-controller and OVS provides some buffering.) Otherwise, * discard the flows. A solution to either of those problems will cause us * to wake up and retry. */ if (state != S_UPDATE_FLOWS || rconn_packet_counter_n_packets(tx_counter)) { ovn_flow_table_clear(flow_table); return; } /* Iterate through all of the installed flows. If any of them are no * longer desired, delete them; if any of them should have different * actions, update them. */ struct ovn_flow *i, *next; HMAP_FOR_EACH_SAFE (i, next, hmap_node, &installed_flows) { struct ovn_flow *d = ovn_flow_lookup(flow_table, i); if (!d) { /* Installed flow is no longer desirable. Delete it from the * switch and from installed_flows. */ struct ofputil_flow_mod fm = { .match = i->match, .priority = i->priority, .table_id = i->table_id, .command = OFPFC_DELETE_STRICT, }; queue_flow_mod(&fm); ovn_flow_log(i, "removing"); hmap_remove(&installed_flows, &i->hmap_node); ovn_flow_destroy(i); } else { if (!ofpacts_equal(i->ofpacts, i->ofpacts_len, d->ofpacts, d->ofpacts_len)) { /* Update actions in installed flow. */ struct ofputil_flow_mod fm = { .match = i->match, .priority = i->priority, .table_id = i->table_id, .ofpacts = d->ofpacts, .ofpacts_len = d->ofpacts_len, .command = OFPFC_MODIFY_STRICT, }; queue_flow_mod(&fm); ovn_flow_log(i, "updating"); /* Replace 'i''s actions by 'd''s. */ free(i->ofpacts); i->ofpacts = d->ofpacts; i->ofpacts_len = d->ofpacts_len; d->ofpacts = NULL; d->ofpacts_len = 0; } hmap_remove(flow_table, &d->hmap_node); ovn_flow_destroy(d); } } /* The previous loop removed from 'flow_table' all of the flows that are * already installed. Thus, any flows remaining in 'flow_table' need to * be added to the flow table. */ struct ovn_flow *d; HMAP_FOR_EACH_SAFE (d, next, hmap_node, flow_table) { /* Send flow_mod to add flow. */ struct ofputil_flow_mod fm = { .match = d->match, .priority = d->priority, .table_id = d->table_id, .ofpacts = d->ofpacts, .ofpacts_len = d->ofpacts_len, .command = OFPFC_ADD, }; queue_flow_mod(&fm); ovn_flow_log(d, "adding"); /* Move 'd' from 'flow_table' to installed_flows. */ hmap_remove(flow_table, &d->hmap_node); hmap_insert(&installed_flows, &d->hmap_node, d->hmap_node.hash); } } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/ofctrl.h0000644000000000000000000000013013534540071020271 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.121855933 openvswitch-2.5.9/ovn/controller/ofctrl.h0000644000175000017500000000230413534540071021760 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFCTRL_H #define OFCTRL_H 1 #include #include "meta-flow.h" struct controller_ctx; struct hmap; struct match; struct ofpbuf; struct ovsrec_bridge; /* Interface for OVN main loop. */ void ofctrl_init(void); enum mf_field_id ofctrl_run(const struct ovsrec_bridge *br_int); void ofctrl_put(struct hmap *flows); void ofctrl_wait(void); void ofctrl_destroy(void); /* Flow table interface to the rest of ovn-controller. */ void ofctrl_add_flow(struct hmap *flows, uint8_t table_id, uint16_t priority, const struct match *, const struct ofpbuf *ofpacts); #endif /* ovn/ofctrl.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/ovn-controller.c0000644000000000000000000000013013534540071021756 xustar0030 mtime=1567801401.689683285 28 atime=1567801402.1136864 30 ctime=1567801425.125855962 openvswitch-2.5.9/ovn/controller/ovn-controller.c0000644000175000017500000003400313534540071023446 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ovn-controller.h" #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "poll-loop.h" #include "fatal-signal.h" #include "lib/vswitch-idl.h" #include "smap.h" #include "stream.h" #include "stream-ssl.h" #include "unixctl.h" #include "util.h" #include "ofctrl.h" #include "pinctrl.h" #include "binding.h" #include "chassis.h" #include "encaps.h" #include "patch.h" #include "physical.h" #include "lflow.h" VLOG_DEFINE_THIS_MODULE(main); static unixctl_cb_func ovn_controller_exit; static unixctl_cb_func ct_zone_list; #define DEFAULT_BRIDGE_NAME "br-int" static void parse_options(int argc, char *argv[]); OVS_NO_RETURN static void usage(void); static char *ovs_remote; const struct sbrec_chassis * get_chassis(struct ovsdb_idl *ovnsb_idl, const char *chassis_id) { const struct sbrec_chassis *chassis_rec; SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) { if (!strcmp(chassis_rec->name, chassis_id)) { break; } } return chassis_rec; } uint32_t get_tunnel_type(const char *name) { if (!strcmp(name, "geneve")) { return GENEVE; } else if (!strcmp(name, "stt")) { return STT; } else if (!strcmp(name, "vxlan")) { return VXLAN; } return 0; } const struct ovsrec_bridge * get_bridge(struct ovsdb_idl *ovs_idl, const char *br_name) { const struct ovsrec_bridge *br; OVSREC_BRIDGE_FOR_EACH (br, ovs_idl) { if (!strcmp(br->name, br_name)) { return br; } } return NULL; } static const struct ovsrec_bridge * create_br_int(struct controller_ctx *ctx, const struct ovsrec_open_vswitch *cfg, const char *bridge_name) { if (!ctx->ovs_idl_txn) { return NULL; } ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn, "ovn-controller: creating integration bridge '%s'", bridge_name); struct ovsrec_interface *iface; iface = ovsrec_interface_insert(ctx->ovs_idl_txn); ovsrec_interface_set_name(iface, bridge_name); ovsrec_interface_set_type(iface, "internal"); struct ovsrec_port *port; port = ovsrec_port_insert(ctx->ovs_idl_txn); ovsrec_port_set_name(port, bridge_name); ovsrec_port_set_interfaces(port, &iface, 1); struct ovsrec_bridge *bridge; bridge = ovsrec_bridge_insert(ctx->ovs_idl_txn); ovsrec_bridge_set_name(bridge, bridge_name); ovsrec_bridge_set_fail_mode(bridge, "secure"); const struct smap oc = SMAP_CONST1(&oc, "disable-in-band", "true"); ovsrec_bridge_set_other_config(bridge, &oc); ovsrec_bridge_set_ports(bridge, &port, 1); struct ovsrec_bridge **bridges; size_t bytes = sizeof *bridges * cfg->n_bridges; bridges = xmalloc(bytes + sizeof *bridges); memcpy(bridges, cfg->bridges, bytes); bridges[cfg->n_bridges] = bridge; ovsrec_open_vswitch_verify_bridges(cfg); ovsrec_open_vswitch_set_bridges(cfg, bridges, cfg->n_bridges + 1); return bridge; } static const struct ovsrec_bridge * get_br_int(struct controller_ctx *ctx) { const struct ovsrec_open_vswitch *cfg; cfg = ovsrec_open_vswitch_first(ctx->ovs_idl); if (!cfg) { return NULL; } const char *br_int_name = smap_get(&cfg->external_ids, "ovn-bridge"); if (!br_int_name) { br_int_name = DEFAULT_BRIDGE_NAME; } const struct ovsrec_bridge *br; br = get_bridge(ctx->ovs_idl, br_int_name); if (!br) { return create_br_int(ctx, cfg, br_int_name); } return br; } static const char * get_chassis_id(const struct ovsdb_idl *ovs_idl) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); return cfg ? smap_get(&cfg->external_ids, "system-id") : NULL; } /* Retrieves the OVN Southbound remote location from the * "external-ids:ovn-remote" key in 'ovs_idl' and returns a copy of it. * * XXX ovn-controller does not support this changing mid-run, but that should * be addressed later. */ static char * get_ovnsb_remote(struct ovsdb_idl *ovs_idl) { while (1) { ovsdb_idl_run(ovs_idl); const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl); if (cfg) { const char *remote = smap_get(&cfg->external_ids, "ovn-remote"); if (remote) { return xstrdup(remote); } } VLOG_INFO("OVN OVSDB remote not specified. Waiting..."); ovsdb_idl_wait(ovs_idl); poll_block(); } } int main(int argc, char *argv[]) { struct unixctl_server *unixctl; bool exiting; int retval; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); fatal_ignore_sigpipe(); daemonize_start(false); retval = unixctl_server_create(NULL, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting); daemonize_complete(); ovsrec_init(); sbrec_init(); ofctrl_init(); pinctrl_init(); lflow_init(); /* Connect to OVS OVSDB instance. We do not monitor all tables by * default, so modules must register their interest explicitly. */ struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true)); ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_open_vswitch); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_external_ids); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_open_vswitch_col_bridges); ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_interface); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_type); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_interface_col_options); ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_port); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_port_col_external_ids); ovsdb_idl_add_table(ovs_idl_loop.idl, &ovsrec_table_bridge); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_name); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_fail_mode); ovsdb_idl_add_column(ovs_idl_loop.idl, &ovsrec_bridge_col_other_config); chassis_register_ovs_idl(ovs_idl_loop.idl); encaps_register_ovs_idl(ovs_idl_loop.idl); binding_register_ovs_idl(ovs_idl_loop.idl); physical_register_ovs_idl(ovs_idl_loop.idl); ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl); /* Connect to OVN SB database. */ char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl); struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER( ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true)); ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl); /* Initialize connection tracking zones. */ struct simap ct_zones = SIMAP_INITIALIZER(&ct_zones); unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)]; bitmap_set1(ct_zone_bitmap, 0); /* Zone 0 is reserved. */ unixctl_command_register("ct-zone-list", "", 0, 0, ct_zone_list, &ct_zones); /* Main loop. */ exiting = false; while (!exiting) { struct controller_ctx ctx = { .ovs_idl = ovs_idl_loop.idl, .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; const struct ovsrec_bridge *br_int = get_br_int(&ctx); const char *chassis_id = get_chassis_id(ctx.ovs_idl); /* Map bridges to local nets from ovn-bridge-mappings */ if (br_int) { patch_run(&ctx, br_int); } if (chassis_id) { chassis_run(&ctx, chassis_id); encaps_run(&ctx, br_int, chassis_id); binding_run(&ctx, br_int, chassis_id, &ct_zones, ct_zone_bitmap); } if (br_int) { enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int); pinctrl_run(&ctx, br_int); struct hmap flow_table = HMAP_INITIALIZER(&flow_table); lflow_run(&ctx, &flow_table, &ct_zones); if (chassis_id) { physical_run(&ctx, mff_ovn_geneve, br_int, chassis_id, &ct_zones, &flow_table); } ofctrl_put(&flow_table); hmap_destroy(&flow_table); } unixctl_server_run(unixctl); unixctl_server_wait(unixctl); if (exiting) { poll_immediate_wake(); } ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop); if (br_int) { ofctrl_wait(); pinctrl_wait(); } poll_block(); if (should_service_stop()) { exiting = true; } } /* It's time to exit. Clean up the databases. */ bool done = false; while (!done) { struct controller_ctx ctx = { .ovs_idl = ovs_idl_loop.idl, .ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop), .ovnsb_idl = ovnsb_idl_loop.idl, .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop), }; const struct ovsrec_bridge *br_int = get_br_int(&ctx); const char *chassis_id = get_chassis_id(ctx.ovs_idl); /* Run all of the cleanup functions, even if one of them returns false. * We're done if all of them return true. */ done = binding_cleanup(&ctx, chassis_id); done = chassis_cleanup(&ctx, chassis_id) && done; done = encaps_cleanup(&ctx, br_int) && done; if (done) { poll_immediate_wake(); } ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop); ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop); poll_block(); } unixctl_server_destroy(unixctl); lflow_destroy(); ofctrl_destroy(); pinctrl_destroy(); simap_destroy(&ct_zones); ovsdb_idl_loop_destroy(&ovs_idl_loop); ovsdb_idl_loop_destroy(&ovnsb_idl_loop); free(ovnsb_remote); free(ovs_remote); service_stop(); exit(retval); } static void parse_options(int argc, char *argv[]) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, OPT_BOOTSTRAP_CA_CERT, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, DAEMON_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {NULL, 0, NULL, 0} }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(OFP13_VERSION, OFP13_VERSION); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); argc -= optind; argv += optind; if (argc == 0) { ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir()); } else if (argc == 1) { ovs_remote = xstrdup(argv[0]); } else { VLOG_FATAL("exactly zero or one non-option argument required; " "use --help for usage"); } } static void usage(void) { printf("%s: OVN controller\n" "usage %s [OPTIONS] [OVS-DATABASE]\n" "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n", program_name, program_name); stream_usage("OVS-DATABASE", true, false, false); daemon_usage(); vlog_usage(); printf("\nOther options:\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *ct_zones_) { struct simap *ct_zones = ct_zones_; struct ds ds = DS_EMPTY_INITIALIZER; struct simap_node *zone; SIMAP_FOR_EACH(zone, ct_zones) { ds_put_format(&ds, "%s %d\n", zone->name, zone->data); } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/encaps.h0000644000000000000000000000013013534540071020251 xustar0030 mtime=1567801401.681683226 28 atime=1567801402.1136864 30 ctime=1567801425.117855903 openvswitch-2.5.9/ovn/controller/encaps.h0000644000175000017500000000200313534540071021734 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_ENCAPS_H #define OVN_ENCAPS_H 1 #include struct controller_ctx; struct ovsdb_idl; struct ovsrec_bridge; void encaps_register_ovs_idl(struct ovsdb_idl *); void encaps_run(struct controller_ctx *, const struct ovsrec_bridge *br_int, const char *chassis_id); bool encaps_cleanup(struct controller_ctx *, const struct ovsrec_bridge *br_int); #endif /* ovn/encaps.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/physical.h0000644000000000000000000000013213534540071020616 xustar0030 mtime=1567801401.693683314 30 atime=1567801402.117686428 30 ctime=1567801425.129855991 openvswitch-2.5.9/ovn/controller/physical.h0000644000175000017500000000312113534540071022301 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_PHYSICAL_H #define OVN_PHYSICAL_H 1 /* Logical/Physical Translation * ============================ * * This module implements physical-to-logical and logical-to-physical * translation as separate OpenFlow tables that run before the ingress pipeline * and after the egress pipeline, respectively, as well as to connect the * two pipelines. */ #include "meta-flow.h" struct controller_ctx; struct hmap; struct ovsdb_idl; struct ovsrec_bridge; struct simap; /* OVN Geneve option information. * * Keep these in sync with the documentation in ovn-architecture(7). */ #define OVN_GENEVE_CLASS 0x0102 /* Assigned Geneve class for OVN. */ #define OVN_GENEVE_TYPE 0 #define OVN_GENEVE_LEN 4 void physical_register_ovs_idl(struct ovsdb_idl *); void physical_run(struct controller_ctx *, enum mf_field_id mff_ovn_geneve, const struct ovsrec_bridge *br_int, const char *chassis_id, const struct simap *ct_zones, struct hmap *flow_table); #endif /* ovn/physical.h */ openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/pinctrl.c0000644000000000000000000000013213534540071020450 xustar0030 mtime=1567801401.705683402 30 atime=1567801402.117686428 30 ctime=1567801425.121855933 openvswitch-2.5.9/ovn/controller/pinctrl.c0000644000175000017500000001036413534540071022142 0ustar00jpettitjpettit00000000000000 /* Copyright (c) 2015 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "dirs.h" #include "pinctrl.h" #include "ofp-msgs.h" #include "ofp-print.h" #include "ofp-util.h" #include "rconn.h" #include "openvswitch/vlog.h" #include "socket-util.h" #include "vswitch-idl.h" VLOG_DEFINE_THIS_MODULE(pinctrl); /* OpenFlow connection to the switch. */ static struct rconn *swconn; /* Last seen sequence number for 'swconn'. When this differs from * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */ static unsigned int conn_seq_no; void pinctrl_init(void) { swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION); conn_seq_no = 0; } static ovs_be32 queue_msg(struct ofpbuf *msg) { const struct ofp_header *oh = msg->data; ovs_be32 xid = oh->xid; rconn_send(swconn, msg, NULL); return xid; } static void get_switch_config(struct rconn *swconn) { struct ofpbuf *request; request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, rconn_get_version(swconn), 0); queue_msg(request); } static void set_switch_config(struct rconn *swconn, const struct ofp_switch_config *config) { struct ofpbuf *request; request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, rconn_get_version(swconn), 0); ofpbuf_put(request, config, sizeof *config); queue_msg(request); } static void process_packet_in(struct controller_ctx *ctx OVS_UNUSED, const struct ofp_header *msg) { struct ofputil_packet_in pin; if (ofputil_decode_packet_in(&pin, msg) != 0) { return; } if (pin.reason != OFPR_ACTION) { return; } /* XXX : process the received packet */ } static void pinctrl_recv(struct controller_ctx *ctx, const struct ofp_header *oh, enum ofptype type) { if (type == OFPTYPE_ECHO_REQUEST) { queue_msg(make_echo_reply(oh)); } else if (type == OFPTYPE_GET_CONFIG_REPLY) { struct ofpbuf rq_buf; struct ofp_switch_config *config_, config; ofpbuf_use_const(&rq_buf, oh, ntohs(oh->length)); config_ = ofpbuf_pull(&rq_buf, sizeof *config_); config = *config_; config.miss_send_len = htons(UINT16_MAX); set_switch_config(swconn, &config); } else if (type == OFPTYPE_PACKET_IN) { process_packet_in(ctx, oh); } else if (type != OFPTYPE_ECHO_REPLY && type != OFPTYPE_BARRIER_REPLY) { if (VLOG_IS_DBG_ENABLED()) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); char *s = ofp_to_string(oh, ntohs(oh->length), 2); VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s); free(s); } } } void pinctrl_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int) { if (br_int) { char *target; target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name); if (strcmp(target, rconn_get_target(swconn))) { VLOG_INFO("%s: connecting to switch", target); rconn_connect(swconn, target, target); } free(target); } else { rconn_disconnect(swconn); } rconn_run(swconn); if (!rconn_is_connected(swconn)) { return; } if (conn_seq_no != rconn_get_connection_seqno(swconn)) { get_switch_config(swconn); conn_seq_no = rconn_get_connection_seqno(swconn); } struct ofpbuf *msg = rconn_recv(swconn); if (!msg) { return; } const struct ofp_header *oh = msg->data; enum ofptype type; ofptype_decode(&type, oh); pinctrl_recv(ctx, oh, type); ofpbuf_delete(msg); } void pinctrl_wait(void) { rconn_run_wait(swconn); rconn_recv_wait(swconn); } void pinctrl_destroy(void) { rconn_destroy(swconn); } openvswitch-2.5.9/ovn/controller/PaxHeaders.82075/patch.h0000644000000000000000000000013013534540071020077 xustar0030 mtime=1567801401.689683285 28 atime=1567801402.1136864 30 ctime=1567801425.125855962 openvswitch-2.5.9/ovn/controller/patch.h0000644000175000017500000000172113534540071021570 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OVN_PATCH_H #define OVN_PATCH_H 1 /* Patch Ports * =========== * * This module adds and removes patch ports between the integration bridge and * physical bridges, as directed by other-config:ovn-bridge-mappings. */ struct controller_ctx; struct ovsrec_bridge; void patch_run(struct controller_ctx *, const struct ovsrec_bridge *br_int); #endif /* ovn/patch.h */ openvswitch-2.5.9/ovn/PaxHeaders.82075/ovn-sb.ovsschema0000644000000000000000000000013213534540071017564 xustar0030 mtime=1567801401.769683872 30 atime=1567801402.117686428 30 ctime=1567801424.497851333 openvswitch-2.5.9/ovn/ovn-sb.ovsschema0000644000175000017500000001174413534540071021261 0ustar00jpettitjpettit00000000000000{ "name": "OVN_Southbound", "version": "1.0.0", "cksum": "1392129391 5060", "tables": { "Chassis": { "columns": { "name": {"type": "string"}, "encaps": {"type": {"key": {"type": "uuid", "refTable": "Encap"}, "min": 1, "max": "unlimited"}}, "vtep_logical_switches" : {"type": {"key": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true, "indexes": [["name"]]}, "Encap": { "columns": { "type": {"type": {"key": { "type": "string", "enum": ["set", ["geneve", "stt", "vxlan"]]}}}, "options": {"type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ip": {"type": "string"}}}, "Logical_Flow": { "columns": { "logical_datapath": {"type": {"key": {"type": "uuid", "refTable": "Datapath_Binding"}}}, "pipeline": {"type": {"key": {"type": "string", "enum": ["set", ["ingress", "egress"]]}}}, "table_id": {"type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 15}}}, "priority": {"type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 65535}}}, "match": {"type": "string"}, "actions": {"type": "string"}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Multicast_Group": { "columns": { "datapath": {"type": {"key": {"type": "uuid", "refTable": "Datapath_Binding"}}}, "name": {"type": "string"}, "tunnel_key": { "type": {"key": {"type": "integer", "minInteger": 32768, "maxInteger": 65535}}}, "ports": {"type": {"key": {"type": "uuid", "refTable": "Port_Binding", "refType": "weak"}, "min": 1, "max": "unlimited"}}}, "indexes": [["datapath", "tunnel_key"], ["datapath", "name"]], "isRoot": true}, "Datapath_Binding": { "columns": { "tunnel_key": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 16777215}}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["tunnel_key"]], "isRoot": true}, "Port_Binding": { "columns": { "logical_port": {"type": "string"}, "type": {"type": "string"}, "options": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "datapath": {"type": {"key": {"type": "uuid", "refTable": "Datapath_Binding"}}}, "tunnel_key": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 32767}}}, "parent_port": {"type": {"key": "string", "min": 0, "max": 1}}, "tag": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4095}, "min": 0, "max": 1}}, "chassis": {"type": {"key": {"type": "uuid", "refTable": "Chassis", "refType": "weak"}, "min": 0, "max": 1}}, "mac": {"type": {"key": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["datapath", "tunnel_key"], ["logical_port"]], "isRoot": true} } } openvswitch-2.5.9/ovn/PaxHeaders.82075/ovn-architecture.7.xml0000644000000000000000000000013213534540071020617 xustar0030 mtime=1567801401.757683784 30 atime=1567801402.117686428 30 ctime=1567801424.501851361 openvswitch-2.5.9/ovn/ovn-architecture.7.xml0000644000175000017500000012744113534540071022316 0ustar00jpettitjpettit00000000000000

    Name

    ovn-architecture -- Open Virtual Network architecture

    Description

    OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. Services such as DHCP are also desirable features. Just like OVS, OVN's design goal is to have a production-quality implementation that can operate at significant scale.

    An OVN deployment consists of several components:

    • A Cloud Management System (CMS), which is OVN's ultimate client (via its users and administrators). OVN integration requires installing a CMS-specific plugin and related software (see below). OVN initially targets OpenStack as CMS.

      We generally speak of ``the'' CMS, but one can imagine scenarios in which multiple CMSes manage different parts of an OVN deployment.

    • An OVN Database physical or virtual node (or, eventually, cluster) installed in a central location.
    • One or more (usually many) hypervisors. Hypervisors must run Open vSwitch and implement the interface described in IntegrationGuide.md in the OVS source tree. Any hypervisor platform supported by Open vSwitch is acceptable.
    • Zero or more gateways. A gateway extends a tunnel-based logical network into a physical network by bidirectionally forwarding packets between tunnels and a physical Ethernet port. This allows non-virtualized machines to participate in logical networks. A gateway may be a physical host, a virtual machine, or an ASIC-based hardware switch that supports the vtep(5) schema. (Support for the latter will come later in OVN implementation.)

      Hypervisors and gateways are together called transport node or chassis.

    The diagram below shows how the major components of OVN and related software interact. Starting at the top of the diagram, we have:

    • The Cloud Management System, as defined above.
    • The OVN/CMS Plugin is the component of the CMS that interfaces to OVN. In OpenStack, this is a Neutron plugin. The plugin's main purpose is to translate the CMS's notion of logical network configuration, stored in the CMS's configuration database in a CMS-specific format, into an intermediate representation understood by OVN.

      This component is necessarily CMS-specific, so a new plugin needs to be developed for each CMS that is integrated with OVN. All of the components below this one in the diagram are CMS-independent.

    • The OVN Northbound Database receives the intermediate representation of logical network configuration passed down by the OVN/CMS Plugin. The database schema is meant to be ``impedance matched'' with the concepts used in a CMS, so that it directly supports notions of logical switches, routers, ACLs, and so on. See ovn-nb(5) for details.

      The OVN Northbound Database has only two clients: the OVN/CMS Plugin above it and ovn-northd below it.

    • ovn-northd(8) connects to the OVN Northbound Database above it and the OVN Southbound Database below it. It translates the logical network configuration in terms of conventional network concepts, taken from the OVN Northbound Database, into logical datapath flows in the OVN Southbound Database below it.
    • The OVN Southbound Database is the center of the system. Its clients are ovn-northd(8) above it and ovn-controller(8) on every transport node below it.

      The OVN Southbound Database contains three kinds of data: Physical Network (PN) tables that specify how to reach hypervisor and other nodes, Logical Network (LN) tables that describe the logical network in terms of ``logical datapath flows,'' and Binding tables that link logical network components' locations to the physical network. The hypervisors populate the PN and Port_Binding tables, whereas ovn-northd(8) populates the LN tables.

      OVN Southbound Database performance must scale with the number of transport nodes. This will likely require some work on ovsdb-server(1) as we encounter bottlenecks. Clustering for availability may be needed.

    The remaining components are replicated onto each hypervisor:

    • ovn-controller(8) is OVN's agent on each hypervisor and software gateway. Northbound, it connects to the OVN Southbound Database to learn about OVN configuration and status and to populate the PN table and the Chassis column in Binding table with the hypervisor's status. Southbound, it connects to ovs-vswitchd(8) as an OpenFlow controller, for control over network traffic, and to the local ovsdb-server(1) to allow it to monitor and control Open vSwitch configuration.
    • ovs-vswitchd(8) and ovsdb-server(1) are conventional components of Open vSwitch.
                                      CMS
                                       |
                                       |
                           +-----------|-----------+
                           |           |           |
                           |     OVN/CMS Plugin    |
                           |           |           |
                           |           |           |
                           |   OVN Northbound DB   |
                           |           |           |
                           |           |           |
                           |       ovn-northd      |
                           |           |           |
                           +-----------|-----------+
                                       |
                                       |
                             +-------------------+
                             | OVN Southbound DB |
                             +-------------------+
                                       |
                                       |
                    +------------------+------------------+
                    |                  |                  |
      HV 1          |                  |    HV n          |
    +---------------|---------------+  .  +---------------|---------------+
    |               |               |  .  |               |               |
    |        ovn-controller         |  .  |        ovn-controller         |
    |         |          |          |  .  |         |          |          |
    |         |          |          |     |         |          |          |
    |  ovs-vswitchd   ovsdb-server  |     |  ovs-vswitchd   ovsdb-server  |
    |                               |     |                               |
    +-------------------------------+     +-------------------------------+
      

    Chassis Setup

    Each chassis in an OVN deployment must be configured with an Open vSwitch bridge dedicated for OVN's use, called the integration bridge. System startup scripts may create this bridge prior to starting ovn-controller if desired. If this bridge does not exist when ovn-controller starts, it will be created automatically with the default configuration suggested below. The ports on the integration bridge include:

    • On any chassis, tunnel ports that OVN uses to maintain logical network connectivity. ovn-controller adds, updates, and removes these tunnel ports.
    • On a hypervisor, any VIFs that are to be attached to logical networks. The hypervisor itself, or the integration between Open vSwitch and the hypervisor (described in IntegrationGuide.md) takes care of this. (This is not part of OVN or new to OVN; this is pre-existing integration work that has already been done on hypervisors that support OVS.)
    • On a gateway, the physical port used for logical network connectivity. System startup scripts add this port to the bridge prior to starting ovn-controller. This can be a patch port to another bridge, instead of a physical port, in more sophisticated setups.

    Other ports should not be attached to the integration bridge. In particular, physical ports attached to the underlay network (as opposed to gateway ports, which are physical ports attached to logical networks) must not be attached to the integration bridge. Underlay physical ports should instead be attached to a separate Open vSwitch bridge (they need not be attached to any bridge at all, in fact).

    The integration bridge should be configured as described below. The effect of each of these settings is documented in ovs-vswitchd.conf.db(5):

    fail-mode=secure
    Avoids switching packets between isolated logical networks before ovn-controller starts up. See Controller Failure Settings in ovs-vsctl(8) for more information.
    other-config:disable-in-band=true
    Suppresses in-band control flows for the integration bridge. It would be unusual for such flows to show up anyway, because OVN uses a local controller (over a Unix domain socket) instead of a remote controller. It's possible, however, for some other bridge in the same system to have an in-band remote controller, and in that case this suppresses the flows that in-band control would ordinarily set up. See In-Band Control in DESIGN.md for more information.

    The customary name for the integration bridge is br-int, but another name may be used.

    Logical Networks

    A logical network implements the same concepts as physical networks, but they are insulated from the physical network with tunnels or other encapsulations. This allows logical networks to have separate IP and other address spaces that overlap, without conflicting, with those used for physical networks. Logical network topologies can be arranged without regard for the topologies of the physical networks on which they run.

    Logical network concepts in OVN include:

    • Logical switches, the logical version of Ethernet switches.
    • Logical routers, the logical version of IP routers. Logical switches and routers can be connected into sophisticated topologies.
    • Logical datapaths are the logical version of an OpenFlow switch. Logical switches and routers are both implemented as logical datapaths.

    Life Cycle of a VIF

    Tables and their schemas presented in isolation are difficult to understand. Here's an example.

    A VIF on a hypervisor is a virtual network interface attached either to a VM or a container running directly on that hypervisor (This is different from the interface of a container running inside a VM).

    The steps in this example refer often to details of the OVN and OVN Northbound database schemas. Please see ovn-sb(5) and ovn-nb(5), respectively, for the full story on these databases.

    1. A VIF's life cycle begins when a CMS administrator creates a new VIF using the CMS user interface or API and adds it to a switch (one implemented by OVN as a logical switch). The CMS updates its own configuration. This includes associating unique, persistent identifier vif-id and Ethernet address mac with the VIF.
    2. The CMS plugin updates the OVN Northbound database to include the new VIF, by adding a row to the Logical_Port table. In the new row, name is vif-id, mac is mac, switch points to the OVN logical switch's Logical_Switch record, and other columns are initialized appropriately.
    3. ovn-northd receives the OVN Northbound database update. In turn, it makes the corresponding updates to the OVN Southbound database, by adding rows to the OVN Southbound database Logical_Flow table to reflect the new port, e.g. add a flow to recognize that packets destined to the new port's MAC address should be delivered to it, and update the flow that delivers broadcast and multicast packets to include the new port. It also creates a record in the Binding table and populates all its columns except the column that identifies the chassis.
    4. On every hypervisor, ovn-controller receives the Logical_Flow table updates that ovn-northd made in the previous step. As long as the VM that owns the VIF is powered off, ovn-controller cannot do much; it cannot, for example, arrange to send packets to or receive packets from the VIF, because the VIF does not actually exist anywhere.
    5. Eventually, a user powers on the VM that owns the VIF. On the hypervisor where the VM is powered on, the integration between the hypervisor and Open vSwitch (described in IntegrationGuide.md) adds the VIF to the OVN integration bridge and stores vif-id in external-ids:iface-id to indicate that the interface is an instantiation of the new VIF. (None of this code is new in OVN; this is pre-existing integration work that has already been done on hypervisors that support OVS.)
    6. On the hypervisor where the VM is powered on, ovn-controller notices external-ids:iface-id in the new Interface. In response, it updates the local hypervisor's OpenFlow tables so that packets to and from the VIF are properly handled. Afterward, in the OVN Southbound DB, it updates the Binding table's chassis column for the row that links the logical port from external-ids:iface-id to the hypervisor.
    7. Some CMS systems, including OpenStack, fully start a VM only when its networking is ready. To support this, ovn-northd notices the chassis column updated for the row in Binding table and pushes this upward by updating the column in the OVN Northbound database's table to indicate that the VIF is now up. The CMS, if it uses this feature, can then react by allowing the VM's execution to proceed.
    8. On every hypervisor but the one where the VIF resides, ovn-controller notices the completely populated row in the Binding table. This provides ovn-controller the physical location of the logical port, so each instance updates the OpenFlow tables of its switch (based on logical datapath flows in the OVN DB Logical_Flow table) so that packets to and from the VIF can be properly handled via tunnels.
    9. Eventually, a user powers off the VM that owns the VIF. On the hypervisor where the VM was powered off, the VIF is deleted from the OVN integration bridge.
    10. On the hypervisor where the VM was powered off, ovn-controller notices that the VIF was deleted. In response, it removes the Chassis column content in the Binding table for the logical port.
    11. On every hypervisor, ovn-controller notices the empty Chassis column in the Binding table's row for the logical port. This means that ovn-controller no longer knows the physical location of the logical port, so each instance updates its OpenFlow table to reflect that.
    12. Eventually, when the VIF (or its entire VM) is no longer needed by anyone, an administrator deletes the VIF using the CMS user interface or API. The CMS updates its own configuration.
    13. The CMS plugin removes the VIF from the OVN Northbound database, by deleting its row in the Logical_Port table.
    14. ovn-northd receives the OVN Northbound update and in turn updates the OVN Southbound database accordingly, by removing or updating the rows from the OVN Southbound database Logical_Flow table and Binding table that were related to the now-destroyed VIF.
    15. On every hypervisor, ovn-controller receives the Logical_Flow table updates that ovn-northd made in the previous step. ovn-controller updates OpenFlow tables to reflect the update, although there may not be much to do, since the VIF had already become unreachable when it was removed from the Binding table in a previous step.

    Life Cycle of a Container Interface Inside a VM

    OVN provides virtual network abstractions by converting information written in OVN_NB database to OpenFlow flows in each hypervisor. Secure virtual networking for multi-tenants can only be provided if OVN controller is the only entity that can modify flows in Open vSwitch. When the Open vSwitch integration bridge resides in the hypervisor, it is a fair assumption to make that tenant workloads running inside VMs cannot make any changes to Open vSwitch flows.

    If the infrastructure provider trusts the applications inside the containers not to break out and modify the Open vSwitch flows, then containers can be run in hypervisors. This is also the case when containers are run inside the VMs and Open vSwitch integration bridge with flows added by OVN controller resides in the same VM. For both the above cases, the workflow is the same as explained with an example in the previous section ("Life Cycle of a VIF").

    This section talks about the life cycle of a container interface (CIF) when containers are created in the VMs and the Open vSwitch integration bridge resides inside the hypervisor. In this case, even if a container application breaks out, other tenants are not affected because the containers running inside the VMs cannot modify the flows in the Open vSwitch integration bridge.

    When multiple containers are created inside a VM, there are multiple CIFs associated with them. The network traffic associated with these CIFs need to reach the Open vSwitch integration bridge running in the hypervisor for OVN to support virtual network abstractions. OVN should also be able to distinguish network traffic coming from different CIFs. There are two ways to distinguish network traffic of CIFs.

    One way is to provide one VIF for every CIF (1:1 model). This means that there could be a lot of network devices in the hypervisor. This would slow down OVS because of all the additional CPU cycles needed for the management of all the VIFs. It would also mean that the entity creating the containers in a VM should also be able to create the corresponding VIFs in the hypervisor.

    The second way is to provide a single VIF for all the CIFs (1:many model). OVN could then distinguish network traffic coming from different CIFs via a tag written in every packet. OVN uses this mechanism and uses VLAN as the tagging mechanism.

    1. A CIF's life cycle begins when a container is spawned inside a VM by the either the same CMS that created the VM or a tenant that owns that VM or even a container Orchestration System that is different than the CMS that initially created the VM. Whoever the entity is, it will need to know the vif-id that is associated with the network interface of the VM through which the container interface's network traffic is expected to go through. The entity that creates the container interface will also need to choose an unused VLAN inside that VM.
    2. The container spawning entity (either directly or through the CMS that manages the underlying infrastructure) updates the OVN Northbound database to include the new CIF, by adding a row to the Logical_Port table. In the new row, name is any unique identifier, parent_name is the vif-id of the VM through which the CIF's network traffic is expected to go through and the tag is the VLAN tag that identifies the network traffic of that CIF.
    3. ovn-northd receives the OVN Northbound database update. In turn, it makes the corresponding updates to the OVN Southbound database, by adding rows to the OVN Southbound database's Logical_Flow table to reflect the new port and also by creating a new row in the Binding table and populating all its columns except the column that identifies the chassis.
    4. On every hypervisor, ovn-controller subscribes to the changes in the Binding table. When a new row is created by ovn-northd that includes a value in parent_port column of Binding table, the ovn-controller in the hypervisor whose OVN integration bridge has that same value in vif-id in external-ids:iface-id updates the local hypervisor's OpenFlow tables so that packets to and from the VIF with the particular VLAN tag are properly handled. Afterward it updates the chassis column of the Binding to reflect the physical location.
    5. One can only start the application inside the container after the underlying network is ready. To support this, ovn-northd notices the updated chassis column in Binding table and updates the column in the OVN Northbound database's table to indicate that the CIF is now up. The entity responsible to start the container application queries this value and starts the application.
    6. Eventually the entity that created and started the container, stops it. The entity, through the CMS (or directly) deletes its row in the Logical_Port table.
    7. ovn-northd receives the OVN Northbound update and in turn updates the OVN Southbound database accordingly, by removing or updating the rows from the OVN Southbound database Logical_Flow table that were related to the now-destroyed CIF. It also deletes the row in the Binding table for that CIF.
    8. On every hypervisor, ovn-controller receives the Logical_Flow table updates that ovn-northd made in the previous step. ovn-controller updates OpenFlow tables to reflect the update.

    Architectural Physical Life Cycle of a Packet

    This section describes how a packet travels from one virtual machine or container to another through OVN. This description focuses on the physical treatment of a packet; for a description of the logical life cycle of a packet, please refer to the Logical_Flow table in ovn-sb(5).

    This section mentions several data and metadata fields, for clarity summarized here:

    tunnel key
    When OVN encapsulates a packet in Geneve or another tunnel, it attaches extra data to it to allow the receiving OVN instance to process it correctly. This takes different forms depending on the particular encapsulation, but in each case we refer to it here as the ``tunnel key.'' See Tunnel Encapsulations, below, for details.
    logical datapath field
    A field that denotes the logical datapath through which a packet is being processed. OVN uses the field that OpenFlow 1.1+ simply (and confusingly) calls ``metadata'' to store the logical datapath. (This field is passed across tunnels as part of the tunnel key.)
    logical input port field

    A field that denotes the logical port from which the packet entered the logical datapath. OVN stores this in Nicira extension register number 6.

    Geneve and STT tunnels pass this field as part of the tunnel key. Although VXLAN tunnels do not explicitly carry a logical input port, OVN only uses VXLAN to communicate with gateways that from OVN's perspective consist of only a single logical port, so that OVN can set the logical input port field to this one on ingress to the OVN logical pipeline.

    logical output port field

    A field that denotes the logical port from which the packet will leave the logical datapath. This is initialized to 0 at the beginning of the logical ingress pipeline. OVN stores this in Nicira extension register number 7.

    Geneve and STT tunnels pass this field as part of the tunnel key. VXLAN tunnels do not transmit the logical output port field.

    conntrack zone field
    A field that denotes the connection tracking zone. The value only has local significance and is not meaningful between chassis. This is initialized to 0 at the beginning of the logical ingress pipeline. OVN stores this in Nicira extension register number 5.
    VLAN ID
    The VLAN ID is used as an interface between OVN and containers nested inside a VM (see Life Cycle of a container interface inside a VM, above, for more information).

    Initially, a VM or container on the ingress hypervisor sends a packet on a port attached to the OVN integration bridge. Then:

    1. OpenFlow table 0 performs physical-to-logical translation. It matches the packet's ingress port. Its actions annotate the packet with logical metadata, by setting the logical datapath field to identify the logical datapath that the packet is traversing and the logical input port field to identify the ingress port. Then it resubmits to table 16 to enter the logical ingress pipeline.

      It's possible that a single ingress physical port maps to multiple logical ports with a type of localnet. The logical datapath and logical input port fields will be reset and the packet will be resubmitted to table 16 multiple times.

      Packets that originate from a container nested within a VM are treated in a slightly different way. The originating container can be distinguished based on the VIF-specific VLAN ID, so the physical-to-logical translation flows additionally match on VLAN ID and the actions strip the VLAN header. Following this step, OVN treats packets from containers just like any other packets.

      Table 0 also processes packets that arrive from other chassis. It distinguishes them from other packets by ingress port, which is a tunnel. As with packets just entering the OVN pipeline, the actions annotate these packets with logical datapath and logical ingress port metadata. In addition, the actions set the logical output port field, which is available because in OVN tunneling occurs after the logical output port is known. These three pieces of information are obtained from the tunnel encapsulation metadata (see Tunnel Encapsulations for encoding details). Then the actions resubmit to table 33 to enter the logical egress pipeline.

    2. OpenFlow tables 16 through 31 execute the logical ingress pipeline from the Logical_Flow table in the OVN Southbound database. These tables are expressed entirely in terms of logical concepts like logical ports and logical datapaths. A big part of ovn-controller's job is to translate them into equivalent OpenFlow (in particular it translates the table numbers: Logical_Flow tables 0 through 15 become OpenFlow tables 16 through 31). For a given packet, the logical ingress pipeline eventually executes zero or more output actions:

      • If the pipeline executes no output actions at all, the packet is effectively dropped.
      • Most commonly, the pipeline executes one output action, which ovn-controller implements by resubmitting the packet to table 32.
      • If the pipeline can execute more than one output action, then each one is separately resubmitted to table 32. This can be used to send multiple copies of the packet to multiple ports. (If the packet was not modified between the output actions, and some of the copies are destined to the same hypervisor, then using a logical multicast output port would save bandwidth between hypervisors.)
    3. OpenFlow tables 32 through 47 implement the output action in the logical ingress pipeline. Specifically, table 32 handles packets to remote hypervisors, table 33 handles packets to the local hypervisor, and table 34 discards packets whose logical ingress and egress port are the same.

      Logical patch ports are a special case. Logical patch ports do not have a physical location and effectively reside on every hypervisor. Thus, flow table 33, for output to ports on the local hypervisor, naturally implements output to unicast logical patch ports too. However, applying the same logic to a logical patch port that is part of a logical multicast group yields packet duplication, because each hypervisor that contains a logical port in the multicast group will also output the packet to the logical patch port. Thus, multicast groups implement output to logical patch ports in table 32.

      Each flow in table 32 matches on a logical output port for unicast or multicast logical ports that include a logical port on a remote hypervisor. Each flow's actions implement sending a packet to the port it matches. For unicast logical output ports on remote hypervisors, the actions set the tunnel key to the correct value, then send the packet on the tunnel port to the correct hypervisor. (When the remote hypervisor receives the packet, table 0 there will recognize it as a tunneled packet and pass it along to table 33.) For multicast logical output ports, the actions send one copy of the packet to each remote hypervisor, in the same way as for unicast destinations. If a multicast group includes a logical port or ports on the local hypervisor, then its actions also resubmit to table 33. Table 32 also includes a fallback flow that resubmits to table 33 if there is no other match.

      Flows in table 33 resemble those in table 32 but for logical ports that reside locally rather than remotely. For unicast logical output ports on the local hypervisor, the actions just resubmit to table 34. For multicast output ports that include one or more logical ports on the local hypervisor, for each such logical port P, the actions change the logical output port to P, then resubmit to table 34.

      Table 34 matches and drops packets for which the logical input and output ports are the same. It resubmits other packets to table 48.

    4. OpenFlow tables 48 through 63 execute the logical egress pipeline from the Logical_Flow table in the OVN Southbound database. The egress pipeline can perform a final stage of validation before packet delivery. Eventually, it may execute an output action, which ovn-controller implements by resubmitting to table 64. A packet for which the pipeline never executes output is effectively dropped (although it may have been transmitted through a tunnel across a physical network).

      The egress pipeline cannot change the logical output port or cause further tunneling.

    5. OpenFlow table 64 performs logical-to-physical translation, the opposite of table 0. It matches the packet's logical egress port. Its actions output the packet to the port attached to the OVN integration bridge that represents that logical port. If the logical egress port is a container nested with a VM, then before sending the packet the actions push on a VLAN header with an appropriate VLAN ID.

      If the logical egress port is a logical patch port, then table 64 outputs to an OVS patch port that represents the logical patch port. The packet re-enters the OpenFlow flow table from the OVS patch port's peer in table 0, which identifies the logical datapath and logical input port based on the OVS patch port's OpenFlow port number.

    Life Cycle of a VTEP gateway

    A gateway is a chassis that forwards traffic between the OVN-managed part of a logical network and a physical VLAN, extending a tunnel-based logical network into a physical network.

    The steps below refer often to details of the OVN and VTEP database schemas. Please see ovn-sb(5), ovn-nb(5) and vtep(5), respectively, for the full story on these databases.

    1. A VTEP gateway's life cycle begins with the administrator registering the VTEP gateway as a Physical_Switch table entry in the VTEP database. The ovn-controller-vtep connected to this VTEP database, will recognize the new VTEP gateway and create a new Chassis table entry for it in the OVN_Southbound database.
    2. The administrator can then create a new Logical_Switch table entry, and bind a particular vlan on a VTEP gateway's port to any VTEP logical switch. Once a VTEP logical switch is bound to a VTEP gateway, the ovn-controller-vtep will detect it and add its name to the vtep_logical_switches column of the Chassis table in the OVN_Southbound database. Note, the tunnel_key column of VTEP logical switch is not filled at creation. The ovn-controller-vtep will set the column when the correponding vtep logical switch is bound to an OVN logical network.
    3. Now, the administrator can use the CMS to add a VTEP logical switch to the OVN logical network. To do that, the CMS must first create a new Logical_Port table entry in the OVN_Northbound database. Then, the type column of this entry must be set to "vtep". Next, the vtep-logical-switch and vtep-physical-switch keys in the options column must also be specified, since multiple VTEP gateways can attach to the same VTEP logical switch.
    4. The newly created logical port in the OVN_Northbound database and its configuration will be passed down to the OVN_Southbound database as a new Port_Binding table entry. The ovn-controller-vtep will recognize the change and bind the logical port to the corresponding VTEP gateway chassis. Configuration of binding the same VTEP logical switch to a different OVN logical networks is not allowed and a warning will be generated in the log.
    5. Beside binding to the VTEP gateway chassis, the ovn-controller-vtep will update the tunnel_key column of the VTEP logical switch to the corresponding Datapath_Binding table entry's tunnel_key for the bound OVN logical network.
    6. Next, the ovn-controller-vtep will keep reacting to the configuration change in the Port_Binding in the OVN_Northbound database, and updating the Ucast_Macs_Remote table in the VTEP database. This allows the VTEP gateway to understand where to forward the unicast traffic coming from the extended external network.
    7. Eventually, the VTEP gateway's life cycle ends when the administrator unregisters the VTEP gateway from the VTEP database. The ovn-controller-vtep will recognize the event and remove all related configurations (Chassis table entry and port bindings) in the OVN_Southbound database.
    8. When the ovn-controller-vtep is terminated, all related configurations in the OVN_Southbound database and the VTEP database will be cleaned, including Chassis table entries for all registered VTEP gateways and their port bindings, and all Ucast_Macs_Remote table entries and the Logical_Switch tunnel keys.

    Design Decisions

    Tunnel Encapsulations

    OVN annotates logical network packets that it sends from one hypervisor to another with the following three pieces of metadata, which are encoded in an encapsulation-specific fashion:

    • 24-bit logical datapath identifier, from the tunnel_key column in the OVN Southbound Datapath_Binding table.
    • 15-bit logical ingress port identifier. ID 0 is reserved for internal use within OVN. IDs 1 through 32767, inclusive, may be assigned to logical ports (see the tunnel_key column in the OVN Southbound Port_Binding table).
    • 16-bit logical egress port identifier. IDs 0 through 32767 have the same meaning as for logical ingress ports. IDs 32768 through 65535, inclusive, may be assigned to logical multicast groups (see the tunnel_key column in the OVN Southbound Multicast_Group table).

    For hypervisor-to-hypervisor traffic, OVN supports only Geneve and STT encapsulations, for the following reasons:

    • Only STT and Geneve support the large amounts of metadata (over 32 bits per packet) that OVN uses (as described above).
    • STT and Geneve use randomized UDP or TCP source ports that allows efficient distribution among multiple paths in environments that use ECMP in their underlay.
    • NICs are available to offload STT and Geneve encapsulation and decapsulation.

    Due to its flexibility, the preferred encapsulation between hypervisors is Geneve. For Geneve encapsulation, OVN transmits the logical datapath identifier in the Geneve VNI. OVN transmits the logical ingress and logical egress ports in a TLV with class 0x0102, type 0, and a 32-bit value encoded as follows, from MSB to LSB:

    Environments whose NICs lack Geneve offload may prefer STT encapsulation for performance reasons. For STT encapsulation, OVN encodes all three pieces of logical metadata in the STT 64-bit tunnel ID as follows, from MSB to LSB:

    For connecting to gateways, in addition to Geneve and STT, OVN supports VXLAN, because only VXLAN support is common on top-of-rack (ToR) switches. Currently, gateways have a feature set that matches the capabilities as defined by the VTEP schema, so fewer bits of metadata are necessary. In the future, gateways that do not support encapsulations with large amounts of metadata may continue to have a reduced feature set.

    openvswitch-2.5.9/ovn/PaxHeaders.82075/OVN-GW-HA.md0000644000000000000000000000013013534540071016231 xustar0030 mtime=1567801401.677683197 28 atime=1567801402.1136864 30 ctime=1567801424.505851391 openvswitch-2.5.9/ovn/OVN-GW-HA.md0000644000175000017500000004363113534540071017730 0ustar00jpettitjpettit00000000000000OVN Gateway High Availability Plan ================================== ``` +---------------------------+ | | | External Network | | | +-------------^-------------+ | | +-----------+ | | | Gateway | | | +-----------+ ^ | | +-------------v-------------+ | | | OVN Virtual Network | | | +---------------------------+ OVN Gateway ``` The OVN gateway is responsible for shuffling traffic between the tunneled overlay network (governed by ovn-northd), and the legacy physical network. In a naive implementation, the gateway is a single x86 server, or hardware VTEP. For most deployments, a single system has enough forwarding capacity to service the entire virtualized network, however, it introduces a single point of failure. If this system dies, the entire OVN deployment becomes unavailable. To mitigate this risk, an HA solution is critical -- by spreading responsibility across multiple systems, no single server failure can take down the network. An HA solution is both critical to the manageability of the system, and extremely difficult to get right. The purpose of this document, is to propose a plan for OVN Gateway High Availability which takes into account our past experience building similar systems. It should be considered a fluid changing proposal, not a set-in-stone decree. Basic Architecture ------------------ In an OVN deployment, the set of hypervisors and network elements operating under the guidance of ovn-northd are in what's called "logical space". These servers use VXLAN, STT, or Geneve to communicate, oblivious to the details of the underlying physical network. When these systems need to communicate with legacy networks, traffic must be routed through a Gateway which translates from OVN controlled tunnel traffic, to raw physical network traffic. Since the gateway is typically the only system with a connection to the physical network all traffic between logical space and the WAN must travel through it. This makes it a critical single point of failure -- if the gateway dies, communication with the WAN ceases for all systems in logical space. To mitigate this risk, multiple gateways should be run in a "High Availability Cluster" or "HA Cluster". The HA cluster will be responsible for performing the duties of a gateways, while being able to recover gracefully from individual member failures. ``` +---------------------------+ | | | External Network | | | +-------------^-------------+ | | +----------------------v----------------------+ | | | High Availability Cluster | | | | +-----------+ +-----------+ +-----------+ | | | | | | | | | | | Gateway | | Gateway | | Gateway | | | | | | | | | | | +-----------+ +-----------+ +-----------+ | +----------------------^----------------------+ | | +-------------v-------------+ | | | OVN Virtual Network | | | +---------------------------+ OVN Gateway HA Cluster ``` ##### L2 vs L3 High Availability In order to achieve this goal, there are two broad approaches one can take. The HA cluster can appear to the network like a giant Layer 2 Ethernet Switch, or like a giant IP Router. These approaches are called L2HA, and L3HA respectively. L2HA allows ethernet broadcast domains to extend into logical space, a significant advantage, but this comes at a cost. The need to avoid transient L2 loops during failover significantly complicates their design. On the other hand, L3HA works for most use cases, is simpler, and fails more gracefully. For these reasons, it is suggested that OVN supports an L3HA model, leaving L2HA for future work (or third party VTEP providers). Both models are discussed further below. L3HA ---- In this section, we'll work through a basic simple L3HA implementation, on top of which we'll gradually build more sophisticated features explaining their motivations and implementations as we go. ### Naive active-backup. Let's assume that there are a collection of logical routers which a tenant has asked for, our task is to schedule these logical routers on one of N gateways, and gracefully redistribute the routers on gateways which have failed. The absolute simplest way to achieve this is what we'll call "naive-active-backup". ``` +----------------+ +----------------+ | Leader | | Backup | | | | | | A B C | | | | | | | +----+-+-+-+----++ +-+--------------+ ^ ^ ^ ^ | | | | | | | | | | | | +-+------+---+ + + + + | ovn-northd | Traffic +------------+ Naive Active Backup HA Implementation ``` In a naive active-backup, one of the Gateways is chosen (arbitrarily) as a leader. All logical routers (A, B, C in the figure), are scheduled on this leader gateway and all traffic flows through it. ovn-northd monitors this gateway via OpenFlow echo requests (or some equivalent), and if the gateway dies, it recreates the routers on one of the backups. This approach basically works in most cases and should likely be the starting point for OVN -- it's strictly better than no HA solution and is a good foundation for more sophisticated solutions. That said, it's not without it's limitations. Specifically, this approach doesn't coordinate with the physical network to minimize disruption during failures, and it tightly couples failover to ovn-northd (we'll discuss why this is bad in a bit), and wastes resources by leaving backup gateways completely unutilized. ##### Router Failover When ovn-northd notices the leader has died and decides to migrate routers to a backup gateway, the physical network has to be notified to direct traffic to the new gateway. Otherwise, traffic could be blackholed for longer than necessary making failovers worse than they need to be. For now, let's assume that OVN requires all gateways to be on the same IP subnet on the physical network. If this isn't the case, gateways would need to participate in routing protocols to orchestrate failovers, something which is difficult and out of scope of this document. Since all gateways are on the same IP subnet, we simply need to worry about updating the MAC learning tables of the Ethernet switches on that subnet. Presumably, they all have entries for each logical router pointing to the old leader. If these entries aren't updated, all traffic will be sent to the (now defunct) old leader, instead of the new one. In order to mitigate this issue, it's recommended that the new gateway sends a Reverse ARP (RARP) onto the physical network for each logical router it now controls. A Reverse ARP is a benign protocol used by many hypervisors when virtual machines migrate to update L2 forwarding tables. In this case, the ethernet source address of the RARP is that of the logical router it corresponds to, and its destination is the broadcast address. This causes the RARP to travel to every L2 switch in the broadcast domain, updating forwarding tables accordingly. This strategy is recommended in all failover mechanisms discussed in this document -- when a router newly boots on a new leader, it should RARP its MAC address. ### Controller Independent Active-backup ``` +----------------+ +----------------+ | Leader | | Backup | | | | | | A B C | | | | | | | +----------------+ +----------------+ ^ ^ ^ ^ | | | | | | | | + + + + Traffic Controller Independent Active-Backup Implementation ``` The fundamental problem with naive active-backup, is it tightly couples the failover solution to ovn-northd. This can significantly increase downtime in the event of a failover as the (often already busy) ovn-northd controller has to recompute state for the new leader. Worse, if ovn-northd goes down, we can't perform gateway failover at all. This violates the principle that control plane outages should have no impact on dataplane functionality. In a controller independent active-backup configuration, ovn-northd is responsible for initial configuration while the HA cluster is responsible for monitoring the leader, and failing over to a backup if necessary. ovn-northd sets HA policy, but doesn't actively participate when failovers occur. Of course, in this model, ovn-northd is not without some responsibility. Its role is to pre-plan what should happen in the event of a failure, leaving it to the individual switches to execute this plan. It does this by assigning each gateway a unique leadership priority. Once assigned, it communicates this priority to each node it controls. Nodes use the leadership priority to determine which gateway in the cluster is the active leader by using a simple metric: the leader is the gateway that is healthy, with the highest priority. If that gateway goes down, leadership falls to the next highest priority, and conversely, if a new gateway comes up with a higher priority, it takes over leadership. Thus, in this model, leadership of the HA cluster is determined simply by the status of its members. Therefore if we can communicate the status of each gateway to each transport node, they can individually figure out which is the leader, and direct traffic accordingly. ##### Tunnel Monitoring. Since in this model leadership is determined exclusively by the health status of member gateways, a key problem is how do we communicate this information to the relevant transport nodes. Luckily, we can do this fairly cheaply using tunnel monitoring protocols like BFD. The basic idea is pretty straightforward. Each transport node maintains a tunnel to every gateway in the HA cluster (not just the leader). These tunnels are monitored using the BFD protocol to see which are alive. Given this information, hypervisors can trivially compute the highest priority live gateway, and thus the leader. In practice, this leadership computation can be performed trivially using the bundle or group action. Rather than using OpenFlow to simply output to the leader, all gateways could be listed in an active-backup bundle action ordered by their priority. The bundle action will automatically take into account the tunnel monitoring status to output the packet to the highest priority live gateway. ##### Inter-Gateway Monitoring One somewhat subtle aspect of this model, is that failovers are not globally atomic. When a failover occurs, it will take some time for all hypervisors to notice and adjust accordingly. Similarly, if a new high priority Gateway comes up, it may take some time for all hypervisors to switch over to the new leader. In order to avoid confusing the physical network, under these circumstances it's important for the backup gateways to drop traffic they've received erroneously. In order to do this, each Gateway must know whether or not it is, in fact active. This can be achieved by creating a mesh of tunnels between gateways. Each gateway monitors the other gateways its cluster to determine which are alive, and therefore whether or not that gateway happens to be the leader. If leading, the gateway forwards traffic normally, otherwise it drops all traffic. ##### Gateway Leadership Resignation Sometimes a gateway may be healthy, but still may not be suitable to lead the HA cluster. This could happen for several reasons including: * The physical network is unreachable. * BFD (or ping) has detected the next hop router is unreachable. * The Gateway recently booted and isn't fully configured. In this case, the Gateway should resign leadership by holding its tunnels down using the other_config:cpath_down flag. This indicates to participating hypervisors and Gateways that this gateway should be treated as if it's down, even though its tunnels are still healthy. ### Router Specific Active-Backup ``` +----------------+ +----------------+ | | | | | A C | | B D E | | | | | +----------------+ +----------------+ ^ ^ ^ ^ | | | | | | | | + + + + Traffic Router Specific Active-Backup ``` Controller independent active-backup is a great advance over naive active-backup, but it still has one glaring problem -- it under-utilizes the backup gateways. In ideal scenario, all traffic would split evenly among the live set of gateways. Getting all the way there is somewhat tricky, but as a step in the direction, one could use the "Router Specific Active-Backup" algorithm. This algorithm looks a lot like active-backup on a per logical router basis, with one twist. It chooses a different active Gateway for each logical router. Thus, in situations where there are several logical routers, all with somewhat balanced load, this algorithm performs better. Implementation of this strategy is quite straightforward if built on top of basic controller independent active-backup. On a per logical router basis, the algorithm is the same, leadership is determined by the liveness of the gateways. The key difference here is that the gateways must have a different leadership priority for each logical router. These leadership priorities can be computed by ovn-northd just as they had been in the controller independent active-backup model. Once we have these per logical router priorities, they simply need be communicated to the members of the gateway cluster and the hypervisors. The hypervisors in particular, need simply have an active-backup bundle action (or group action) per logical router listing the gateways in priority order for *that router*, rather than having a single bundle action shared for all the routers. Additionally, the gateways need to be updated to take into account individual router priorities. Specifically, each gateway should drop traffic of backup routers it's running, and forward traffic of active gateways, instead of simply dropping or forwarding everything. This should likely be done by having ovn-controller recompute OpenFlow for the gateway, though other options exist. The final complication is that ovn-northd's logic must be updated to choose these per logical router leadership priorities in a more sophisticated manner. It doesn't matter much exactly what algorithm it chooses to do this, beyond that it should provide good balancing in the common case. I.E. each logical routers priorities should be different enough that routers balance to different gateways even when failures occur. ##### Preemption In an active-backup setup, one issue that users will run into is that of gateway leader preemption. If a new Gateway is added to a cluster, or for some reason an existing gateway is rebooted, we could end up in a situation where the newly activated gateway has higher priority than any other in the HA cluster. In this case, as soon as that gateway appears, it will preempt leadership from the currently active leader causing an unnecessary failover. Since failover can be quite expensive, this preemption may be undesirable. The controller can optionally avoid preemption by cleverly tweaking the leadership priorities. For each router, new gateways should be assigned priorities that put them second in line or later when they eventually come up. Furthermore, if a gateway goes down for a significant period of time, its old leadership priorities should be revoked and new ones should be assigned as if it's a brand new gateway. Note that this should only happen if a gateway has been down for a while (several minutes), otherwise a flapping gateway could have wide ranging, unpredictable, consequences. Note that preemption avoidance should be optional depending on the deployment. One necessarily sacrifices optimal load balancing to satisfy these requirements as new gateways will get no traffic on boot. Thus, this feature represents a trade-off which must be made on a per installation basis. ### Fully Active-Active HA ``` +----------------+ +----------------+ | | | | | A B C D E | | A B C D E | | | | | +----------------+ +----------------+ ^ ^ ^ ^ | | | | | | | | + + + + Traffic ``` The final step in L3HA is to have true active-active HA. In this scenario each router has an instance on each Gateway, and a mechanism similar to ECMP is used to distribute traffic evenly among all instances. This mechanism would require Gateways to participate in routing protocols with the physical network to attract traffic and alert of failures. It is out of scope of this document, but may eventually be necessary. L2HA ---- L2HA is very difficult to get right. Unlike L3HA, where the consequences of problems are minor, in L2HA if two gateways are both transiently active, an L2 loop triggers and a broadcast storm results. In practice to get around this, gateways end up implementing an overly conservative "when in doubt drop all traffic" policy, or they implement something like MLAG. MLAG has multiple gateways work together to pretend to be a single L2 switch with a large LACP bond. In principle, it's the right solution to the problem as it solves the broadcast storm problem, and has been deployed successfully in other contexts. That said, it's difficult to get right and not recommended. openvswitch-2.5.9/PaxHeaders.82075/third-party0000644000000000000000000000013213534540120016031 xustar0030 mtime=1567801424.621852246 30 atime=1567801425.625859648 30 ctime=1567801424.621852246 openvswitch-2.5.9/third-party/0000755000175000017500000000000013534540120017574 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/third-party/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020252 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.621852246 openvswitch-2.5.9/third-party/automake.mk0000644000175000017500000000011213534540071021732 0ustar00jpettitjpettit00000000000000docs += third-party/README.md EXTRA_DIST += third-party/ofp-tcpdump.patch openvswitch-2.5.9/third-party/PaxHeaders.82075/README.md0000644000000000000000000000013213534540071017372 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801423.701845464 openvswitch-2.5.9/third-party/README.md0000644000175000017500000000242213534540071021060 0ustar00jpettitjpettit00000000000000Third-party software integration ================================ This directory contains third-party software that may be useful for debugging. tcpdump ------- The "ofp-tcpdump.patch" patch adds the ability to parse OpenFlow messages to tcpdump. These instructions assume that tcpdump 4.3.0 is going to be used, but it should work with other versions that are not substantially different. To begin, download tcpdump and apply the patch: wget http://www.tcpdump.org/release/tcpdump-4.3.0.tar.gz tar xzf tcpdump-4.3.0.tar.gz ln -s tcpdump-4.3.0 tcpdump patch -p0 < ofp-tcpdump.patch Then build the new version of tcpdump: cd tcpdump ./configure make Clearly, tcpdump can only parse unencrypted packets, so you will need to connect the controller and datapath using plain TCP. To look at the traffic, tcpdump will be started in a manner similar to the following: sudo ./tcpdump -s0 -i eth0 port 6653 The "-s0" flag indicates that tcpdump should capture the entire packet. If the OpenFlow message is not received in its entirety, "[|openflow]" will be printed instead of the OpenFlow message contents. The verbosity of the output may be increased by adding additional "-v" flags. If "-vvv" is used, the raw OpenFlow data is also printed in hex and ASCII. openvswitch-2.5.9/third-party/PaxHeaders.82075/ofp-tcpdump.patch0000644000000000000000000000013213534540071021372 xustar0030 mtime=1567801401.937685107 30 atime=1567801402.133686546 30 ctime=1567801424.109848472 openvswitch-2.5.9/third-party/ofp-tcpdump.patch0000644000175000017500000001036213534540071023062 0ustar00jpettitjpettit00000000000000--- tcpdump/interface.h 2007-06-13 18:03:20.000000000 -0700 +++ tcpdump/interface.h 2008-04-15 18:28:55.000000000 -0700 @@ -130,7 +130,8 @@ extern const char *dnaddr_string(u_short); -extern void error(const char *, ...) +#define error(fmt, args...) tcpdump_error(fmt, ## args) +extern void tcpdump_error(const char *, ...) __attribute__((noreturn, format (printf, 1, 2))); extern void warning(const char *, ...) __attribute__ ((format (printf, 1, 2))); @@ -163,6 +164,7 @@ extern void hex_print_with_offset(const char *, const u_char *, u_int, u_int); extern void hex_print(const char *, const u_char *, u_int); extern void telnet_print(const u_char *, u_int); +extern void openflow_print(const u_char *, u_int); extern int llc_print(const u_char *, u_int, u_int, const u_char *, const u_char *, u_short *); extern int snap_print(const u_char *, u_int, u_int, u_int); --- tcpdump/Makefile.in 2012-06-13 04:56:20.000000000 +1200 +++ tcpdump/Makefile.in 2012-08-29 21:36:37.000000000 +1200 @@ -43,7 +43,7 @@ CC = @CC@ PROG = tcpdump CCOPT = @V_CCOPT@ -INCLS = -I. @V_INCLS@ +INCLS = -I. @V_INCLS@ -I../../include DEFS = @DEFS@ @CPPFLAGS@ @V_DEFS@ # Standard CFLAGS @@ -51,10 +51,10 @@ FULL_CFLAGS = $(CCOPT) $(DEFS) $(INCLS) $(CFLAGS) # Standard LDFLAGS -LDFLAGS = @LDFLAGS@ +LDFLAGS = @LDFLAGS@ -L../../lib # Standard LIBS -LIBS = @LIBS@ +LIBS = @LIBS@ -lopenvswitch -lssl -lrt -lm INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -93,7 +93,8 @@ print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \ print-timed.c print-tipc.c print-token.c print-udld.c print-udp.c \ print-usb.c print-vjc.c print-vqp.c print-vrrp.c print-vtp.c \ - print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c + print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c \ + print-openflow.c LIBNETDISSECT_SRC=print-isakmp.c LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o) @@ -363,7 +364,7 @@ all: $(PROG) $(PROG): $(OBJ) @rm -f $@ - $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) + libtool --mode=link $(CC) $(FULL_CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(LIBNETDISSECT): $(LIBNETDISSECT_OBJ) @rm -f $@ --- tcpdump/print-openflow.c 1969-12-31 16:00:00.000000000 -0800 +++ tcpdump/print-openflow.c 2009-05-11 15:38:41.000000000 -0700 @@ -0,0 +1,45 @@ +/* Copyright (C) 2007, 2008, 2009 Nicira, Inc. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "interface.h" +#include "openflow/openflow.h" +#include "../../lib/ofp-print.h" + +void +openflow_print(const u_char *sp, u_int length) +{ + const struct ofp_header *ofp = (struct ofp_header *)sp; + + if (!TTEST2(*sp, ntohs(ofp->length))) + goto trunc; + + ofp_print(stdout, sp, length, vflag); + return; + +trunc: + printf("[|openflow]"); +} --- tcpdump/print-tcp.c 2006-09-19 12:07:57.000000000 -0700 +++ tcpdump/print-tcp.c 2009-05-11 15:38:25.000000000 -0700 @@ -56,6 +56,8 @@ #include "nameser.h" +#include "openflow/openflow.h" + #ifdef HAVE_LIBCRYPTO #include #include @@ -669,7 +672,9 @@ } else if (length > 0 && (sport == LDP_PORT || dport == LDP_PORT)) { ldp_print(bp, length); - } + } else if (sport == OFP_PORT || dport == OFP_PORT) { + openflow_print(bp, length); + } return; bad: openvswitch-2.5.9/PaxHeaders.82075/.travis.yml0000644000000000000000000000013213534540071015755 xustar0030 mtime=1567801401.177679525 30 atime=1567801402.037685841 30 ctime=1567801423.709845523 openvswitch-2.5.9/.travis.yml0000644000175000017500000000131113534540071017437 0ustar00jpettitjpettit00000000000000language: c compiler: - gcc - clang addons: apt: packages: - bc - gcc-multilib - libssl-dev - llvm-dev before_install: ./.travis/prepare.sh before_script: export PATH=$PATH:$HOME/bin env: - OPTS="--disable-ssl" - TESTSUITE=1 KERNEL=3.18.1 - TESTSUITE=1 OPTS="--enable-shared" - BUILD_ENV="-m32" OPTS="--disable-ssl" - KERNEL=3.17.7 DPDK=1 - KERNEL=3.17.7 DPDK=1 OPTS="--enable-shared" - KERNEL=4.3.5 - KERNEL=4.1.17 - KERNEL=3.18.26 - KERNEL=3.14.60 - KERNEL=3.12.53 - KERNEL=3.10.96 - KERNEL=3.4.110 - KERNEL=3.2.76 - KERNEL=2.6.32.70 script: ./.travis/build.sh $OPTS notifications: email: recipients: - ovs-build@openvswitch.org openvswitch-2.5.9/PaxHeaders.82075/WHY-OVS.md0000644000000000000000000000013213534540071015302 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.697845435 openvswitch-2.5.9/WHY-OVS.md0000644000175000017500000001262213534540071016773 0ustar00jpettitjpettit00000000000000Why Open vSwitch? ================= Hypervisors need the ability to bridge traffic between VMs and with the outside world. On Linux-based hypervisors, this used to mean using the built-in L2 switch (the Linux bridge), which is fast and reliable. So, it is reasonable to ask why Open vSwitch is used. The answer is that Open vSwitch is targeted at multi-server virtualization deployments, a landscape for which the previous stack is not well suited. These environments are often characterized by highly dynamic end-points, the maintenance of logical abstractions, and (sometimes) integration with or offloading to special purpose switching hardware. The following characteristics and design considerations help Open vSwitch cope with the above requirements. * The mobility of state: All network state associated with a network entity (say a virtual machine) should be easily identifiable and migratable between different hosts. This may include traditional "soft state" (such as an entry in an L2 learning table), L3 forwarding state, policy routing state, ACLs, QoS policy, monitoring configuration (e.g. NetFlow, IPFIX, sFlow), etc. Open vSwitch has support for both configuring and migrating both slow (configuration) and fast network state between instances. For example, if a VM migrates between end-hosts, it is possible to not only migrate associated configuration (SPAN rules, ACLs, QoS) but any live network state (including, for example, existing state which may be difficult to reconstruct). Further, Open vSwitch state is typed and backed by a real data-model allowing for the development of structured automation systems. * Responding to network dynamics: Virtual environments are often characterized by high-rates of change. VMs coming and going, VMs moving backwards and forwards in time, changes to the logical network environments, and so forth. Open vSwitch supports a number of features that allow a network control system to respond and adapt as the environment changes. This includes simple accounting and visibility support such as NetFlow, IPFIX, and sFlow. But perhaps more useful, Open vSwitch supports a network state database (OVSDB) that supports remote triggers. Therefore, a piece of orchestration software can "watch" various aspects of the network and respond if/when they change. This is used heavily today, for example, to respond to and track VM migrations. Open vSwitch also supports OpenFlow as a method of exporting remote access to control traffic. There are a number of uses for this including global network discovery through inspection of discovery or link-state traffic (e.g. LLDP, CDP, OSPF, etc.). * Maintenance of logical tags: Distributed virtual switches (such as VMware vDS and Cisco's Nexus 1000V) often maintain logical context within the network through appending or manipulating tags in network packets. This can be used to uniquely identify a VM (in a manner resistant to hardware spoofing), or to hold some other context that is only relevant in the logical domain. Much of the problem of building a distributed virtual switch is to efficiently and correctly manage these tags. Open vSwitch includes multiple methods for specifying and maintaining tagging rules, all of which are accessible to a remote process for orchestration. Further, in many cases these tagging rules are stored in an optimized form so they don't have to be coupled with a heavyweight network device. This allows, for example, thousands of tagging or address remapping rules to be configured, changed, and migrated. In a similar vein, Open vSwitch supports a GRE implementation that can handle thousands of simultaneous GRE tunnels and supports remote configuration for tunnel creation, configuration, and tear-down. This, for example, can be used to connect private VM networks in different data centers. * Hardware integration: Open vSwitch's forwarding path (the in-kernel datapath) is designed to be amenable to "offloading" packet processing to hardware chipsets, whether housed in a classic hardware switch chassis or in an end-host NIC. This allows for the Open vSwitch control path to be able to both control a pure software implementation or a hardware switch. There are many ongoing efforts to port Open vSwitch to hardware chipsets. These include multiple merchant silicon chipsets (Broadcom and Marvell), as well as a number of vendor-specific platforms. (The PORTING file discusses how one would go about making such a port.) The advantage of hardware integration is not only performance within virtualized environments. If physical switches also expose the Open vSwitch control abstractions, both bare-metal and virtualized hosting environments can be managed using the same mechanism for automated network control. In many ways, Open vSwitch targets a different point in the design space than previous hypervisor networking stacks, focusing on the need for automated and dynamic network control in large-scale Linux-based virtualization environments. The goal with Open vSwitch is to keep the in-kernel code as small as possible (as is necessary for performance) and to re-use existing subsystems when applicable (for example Open vSwitch uses the existing QoS stack). As of Linux 3.3, Open vSwitch is included as a part of the kernel and packaging for the userspace utilities are available on most popular distributions. openvswitch-2.5.9/PaxHeaders.82075/INSTALL.Libvirt.md0000644000000000000000000000013213534540071016706 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.037685841 30 ctime=1567801423.673845258 openvswitch-2.5.9/INSTALL.Libvirt.md0000644000175000017500000000435413534540071020402 0ustar00jpettitjpettit00000000000000How to Use Open vSwitch with Libvirt ==================================== This document describes how to use Open vSwitch with Libvirt 0.9.11 or later. This document assumes that you followed [INSTALL.md] or installed Open vSwitch from distribution packaging such as a .deb or .rpm. The Open vSwitch support is included by default in Libvirt 0.9.11. Consult www.libvirt.org for instructions on how to build the latest Libvirt, if your Linux distribution by default comes with an older Libvirt release. Limitations ----------- Currently there is no Open vSwitch support for networks that are managed by libvirt (e.g. NAT). As of now, only bridged networks are supported (those where the user has to manually create the bridge). Setup ----- First, create the Open vSwitch bridge by using the ovs-vsctl utility (this must be done with administrative privileges): % ovs-vsctl add-br ovsbr Once that is done, create a VM, if necessary, and edit its Domain XML file: % virsh edit Lookup in the Domain XML file the `` section. There should be one such XML section for each interface the VM has. ```
    ``` And change it to something like this: ```
    ``` The interface type must be set to "bridge". The `` XML element specifies to which bridge this interface will be attached to. The `` element indicates that the bridge in `` element is an Open vSwitch bridge. Then (re)start the VM and verify if the guest's vnet interface is attached to the ovsbr bridge. % ovs-vsctl show Troubleshooting --------------- If the VM does not want to start, then try to run the libvirtd process either from the terminal, so that all errors are printed in console, or inspect Libvirt/Open vSwitch log files for possible root cause. Bug Reporting ------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/manpages.mk0000644000000000000000000000013113534540110015761 xustar0029 mtime=1567801416.63379336 30 atime=1567801416.925795513 30 ctime=1567801424.605852128 openvswitch-2.5.9/manpages.mk0000644000175000017500000001515113534540110017453 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- ovn/utilities/ovn-sbctl.8: \ ovn/utilities/ovn-sbctl.8.in \ lib/db-ctl-base.man \ lib/table.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovn/utilities/ovn-sbctl.8.in: lib/db-ctl-base.man: lib/table.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-client.1: \ ovsdb/ovsdb-client.1.in \ lib/common-syn.man \ lib/common.man \ lib/daemon-syn.man \ lib/daemon.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-syn.man \ lib/ssl.man \ lib/table.man \ lib/vlog-syn.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovsdb/ovsdb-client.1.in: lib/common-syn.man: lib/common.man: lib/daemon-syn.man: lib/daemon.man: lib/ssl-bootstrap-syn.man: lib/ssl-bootstrap.man: lib/ssl-syn.man: lib/ssl.man: lib/table.man: lib/vlog-syn.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-server.1: \ ovsdb/ovsdb-server.1.in \ lib/common-syn.man \ lib/common.man \ lib/coverage-unixctl.man \ lib/daemon-syn.man \ lib/daemon.man \ lib/memory-unixctl.man \ lib/service-syn.man \ lib/service.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert-syn.man \ lib/ssl-peer-ca-cert.man \ lib/ssl-syn.man \ lib/ssl.man \ lib/unixctl-syn.man \ lib/unixctl.man \ lib/vlog-syn.man \ lib/vlog-unixctl.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovsdb/ovsdb-server.1.in: lib/common-syn.man: lib/common.man: lib/coverage-unixctl.man: lib/daemon-syn.man: lib/daemon.man: lib/memory-unixctl.man: lib/service-syn.man: lib/service.man: lib/ssl-bootstrap-syn.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert-syn.man: lib/ssl-peer-ca-cert.man: lib/ssl-syn.man: lib/ssl.man: lib/unixctl-syn.man: lib/unixctl.man: lib/vlog-syn.man: lib/vlog-unixctl.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-tool.1: \ ovsdb/ovsdb-tool.1.in \ lib/common-syn.man \ lib/common.man \ lib/vlog-syn.man \ lib/vlog.man ovsdb/ovsdb-tool.1.in: lib/common-syn.man: lib/common.man: lib/vlog-syn.man: lib/vlog.man: utilities/bugtool/ovs-bugtool.8: \ utilities/bugtool/ovs-bugtool.8.in utilities/bugtool/ovs-bugtool.8.in: utilities/ovs-appctl.8: \ utilities/ovs-appctl.8.in \ lib/common.man utilities/ovs-appctl.8.in: lib/common.man: utilities/ovs-benchmark.1: \ utilities/ovs-benchmark.1.in \ lib/ovs.tmac utilities/ovs-benchmark.1.in: lib/ovs.tmac: utilities/ovs-dpctl-top.8: \ utilities/ovs-dpctl-top.8.in utilities/ovs-dpctl-top.8.in: utilities/ovs-dpctl.8: \ utilities/ovs-dpctl.8.in \ lib/common.man \ lib/dpctl.man \ lib/vlog.man utilities/ovs-dpctl.8.in: lib/common.man: lib/dpctl.man: lib/vlog.man: utilities/ovs-l3ping.8: \ utilities/ovs-l3ping.8.in \ lib/common-syn.man \ lib/common.man utilities/ovs-l3ping.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-ofctl.8: \ utilities/ovs-ofctl.8.in \ lib/common.man \ lib/daemon.man \ lib/ofp-version.man \ lib/ssl.man \ lib/unixctl.man \ lib/vconn-active.man \ lib/vlog.man utilities/ovs-ofctl.8.in: lib/common.man: lib/daemon.man: lib/ofp-version.man: lib/ssl.man: lib/unixctl.man: lib/vconn-active.man: lib/vlog.man: utilities/ovs-pcap.1: \ utilities/ovs-pcap.1.in \ lib/common-syn.man \ lib/common.man utilities/ovs-pcap.1.in: lib/common-syn.man: lib/common.man: utilities/ovs-pki.8: \ utilities/ovs-pki.8.in utilities/ovs-pki.8.in: utilities/ovs-tcpundump.1: \ utilities/ovs-tcpundump.1.in \ lib/common-syn.man \ lib/common.man utilities/ovs-tcpundump.1.in: lib/common-syn.man: lib/common.man: utilities/ovs-test.8: \ utilities/ovs-test.8.in \ lib/common-syn.man \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-test.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-testcontroller.8: \ utilities/ovs-testcontroller.8.in \ lib/common.man \ lib/daemon.man \ lib/ofp-version.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/unixctl.man \ lib/vconn-active.man \ lib/vconn-passive.man \ lib/vlog.man utilities/ovs-testcontroller.8.in: lib/common.man: lib/daemon.man: lib/ofp-version.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/unixctl.man: lib/vconn-active.man: lib/vconn-passive.man: lib/vlog.man: utilities/ovs-vlan-bug-workaround.8: \ utilities/ovs-vlan-bug-workaround.8.in \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-vlan-bug-workaround.8.in: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-vlan-test.8: \ utilities/ovs-vlan-test.8.in \ lib/common-syn.man \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-vlan-test.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-vsctl.8: \ utilities/ovs-vsctl.8.in \ lib/common.man \ lib/db-ctl-base.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/table.man \ lib/vconn-active.man \ lib/vconn-passive.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man \ ovsdb/remote-passive.man utilities/ovs-vsctl.8.in: lib/common.man: lib/db-ctl-base.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/table.man: lib/vconn-active.man: lib/vconn-passive.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/remote-passive.man: vswitchd/ovs-vswitchd.8: \ vswitchd/ovs-vswitchd.8.in \ lib/common.man \ lib/coverage-unixctl.man \ lib/daemon.man \ lib/dpctl.man \ lib/memory-unixctl.man \ lib/service.man \ lib/ssl-bootstrap.man \ lib/ssl.man \ lib/unixctl.man \ lib/vlog-unixctl.man \ lib/vlog.man \ ofproto/ofproto-dpif-unixctl.man \ ofproto/ofproto-tnl-unixctl.man \ ofproto/ofproto-unixctl.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man vswitchd/ovs-vswitchd.8.in: lib/common.man: lib/coverage-unixctl.man: lib/daemon.man: lib/dpctl.man: lib/memory-unixctl.man: lib/service.man: lib/ssl-bootstrap.man: lib/ssl.man: lib/unixctl.man: lib/vlog-unixctl.man: lib/vlog.man: ofproto/ofproto-dpif-unixctl.man: ofproto/ofproto-tnl-unixctl.man: ofproto/ofproto-unixctl.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: vtep/vtep-ctl.8: \ vtep/vtep-ctl.8.in \ lib/common.man \ lib/db-ctl-base.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/table.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man \ ovsdb/remote-passive.man vtep/vtep-ctl.8.in: lib/common.man: lib/db-ctl-base.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/table.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/remote-passive.man: openvswitch-2.5.9/PaxHeaders.82075/INSTALL.userspace.md0000644000000000000000000000013213534540071017265 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.681845316 openvswitch-2.5.9/INSTALL.userspace.md0000644000175000017500000000605113534540071020755 0ustar00jpettitjpettit00000000000000Using Open vSwitch without kernel support ========================================= Open vSwitch can operate, at a cost in performance, entirely in userspace, without assistance from a kernel module. This file explains how to install Open vSwitch in such a mode. The userspace-only mode of Open vSwitch is considered experimental. It has not been thoroughly tested. This version of Open vSwitch should be built manually with `configure` and `make`. Debian packaging for Open vSwitch is also included, but it has not been recently tested, and so Debian packages are not a recommended way to use this version of Open vSwitch. Building and Installing ----------------------- The requirements and procedure for building, installing, and configuring Open vSwitch are the same as those given in [INSTALL.md]. You may omit configuring, building, and installing the kernel module, and the related requirements. On Linux, the userspace switch additionally requires the kernel TUN/TAP driver to be available, either built into the kernel or loaded as a module. If you are not sure, check for a directory named /sys/class/misc/tun. If it does not exist, then attempt to load the module with `modprobe tun`. The tun device must also exist as `/dev/net/tun`. If it does not exist, then create /dev/net (if necessary) with `mkdir /dev/net`, then create `/dev/net/tun` with `mknod /dev/net/tun c 10 200`. On FreeBSD and NetBSD, the userspace switch additionally requires the kernel tap(4) driver to be available, either built into the kernel or loaded as a module. Using the Userspace Datapath with ovs-vswitchd ---------------------------------------------- To use ovs-vswitchd in userspace mode, create a bridge with datapath_type "netdev" in the configuration database. For example: ovs-vsctl add-br br0 ovs-vsctl set bridge br0 datapath_type=netdev ovs-vsctl add-port br0 eth0 ovs-vsctl add-port br0 eth1 ovs-vsctl add-port br0 eth2 ovs-vswitchd will create a TAP device as the bridge's local interface, named the same as the bridge, as well as for each configured internal interface. Currently, on FreeBSD, the functionality required for in-band control support is not implemented. To avoid related errors, you can disable the in-band support with the following command. ovs-vsctl set bridge br0 other_config:disable-in-band=true Firewall Rules -------------- On Linux, when a physical interface is in use by the userspace datapath, packets received on the interface still also pass into the kernel TCP/IP stack. This can cause surprising and incorrect behavior. You can use "iptables" to avoid this behavior, by using it to drop received packets. For example, to drop packets received on eth0: iptables -A INPUT -i eth0 -j DROP iptables -A FORWARD -i eth0 -j DROP Other settings -------------- On NetBSD, depending on your network topology and applications, the following configuration might help. See sysctl(7). sysctl -w net.inet.ip.checkinterface=1 Bug Reporting ------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/IntegrationGuide.md0000644000000000000000000000013213534540071017427 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.685845346 openvswitch-2.5.9/IntegrationGuide.md0000644000175000017500000001727713534540071021133 0ustar00jpettitjpettit00000000000000Integration Guide for Centralized Control ========================================= This document describes how to integrate Open vSwitch onto a new platform to expose the state of the switch and attached devices for centralized control. (If you are looking to port the switching components of Open vSwitch to a new platform, please see the PORTING document.) The focus of this guide is on hypervisors, but many of the interfaces are useful for hardware switches, as well. The XenServer integration is the most mature implementation, so most of the examples are drawn from it. The externally visible interface to this integration is platform-agnostic. We encourage anyone who integrates Open vSwitch to use the same interface, because keeping a uniform interface means that controllers require less customization for individual platforms (and perhaps no customization at all). Integration centers around the Open vSwitch database and mostly involves the 'external_ids' columns in several of the tables. These columns are not interpreted by Open vSwitch itself. Instead, they provide information to a controller that permits it to associate a database record with a more meaningful entity. In contrast, the 'other_config' column is used to configure behavior of the switch. The main job of the integrator, then, is to ensure that these values are correctly populated and maintained. An integrator sets the columns in the database by talking to the ovsdb-server daemon. A few of the columns can be set during startup by calling the ovs-ctl tool from inside the startup scripts. The 'xenserver/etc_init.d_openvswitch' script provides examples of its use, and the ovs-ctl(8) manpage contains complete documentation. At runtime, ovs-vsctl can be be used to set columns in the database. The script 'xenserver/etc_xensource_scripts_vif' contains examples of its use, and ovs-vsctl(8) manpage contains complete documentation. Python and C bindings to the database are provided if deeper integration with a program are needed. The XenServer ovs-xapi-sync daemon ('xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync') provides an example of using the Python bindings. More information on the python bindings is available at 'python/ovs/db/idl.py'. Information on the C bindings is available at 'lib/ovsdb-idl.h'. The following diagram shows how integration scripts fit into the Open vSwitch architecture: +----------------------------------------+ | Controller Cluster + +----------------------------------------+ | | +----------------------------------------------------------+ | | | | +--------------+---------------+ | | | | | | +-------------------+ +------------------+ | | | ovsdb-server |-----------| ovs-vswitchd | | | +-------------------+ +------------------+ | | | | | | +---------------------+ | | | | Integration scripts | | | | | (ex: ovs-xapi-sync) | | | | +---------------------+ | | | | Userspace | |----------------------------------------------------------| | | Kernel | | | | | +---------------------+ | | | OVS Kernel Module | | | +---------------------+ | +----------------------------------------------------------+ A description of the most relevant fields for integration follows. By setting these values, controllers are able to understand the network and manage it more dynamically and precisely. For more details about the database and each individual column, please refer to the ovs-vswitchd.conf.db(5) manpage. Open_vSwitch table ------------------ The Open_vSwitch table describes the switch as a whole. The 'system_type' and 'system_version' columns identify the platform to the controller. The 'external_ids:system-id' key uniquely identifies the physical host. In XenServer, the system-id will likely be the same as the UUID returned by 'xe host-list'. This key allows controllers to distinguish between multiple hypervisors. Most of this configuration can be done with the ovs-ctl command at startup. For example: ovs-ctl --system-type="XenServer" --system-version="6.0.0-50762p" \ --system-id="${UUID}" "${other_options}" start Alternatively, the ovs-vsctl command may be used to set a particular value at runtime. For example: ovs-vsctl set open_vswitch . external-ids:system-id='"${UUID}"' The 'other_config:enable-statistics' key may be set to "true" to have OVS populate the database with statistics (e.g., number of CPUs, memory, system load) for the controller's use. Bridge table ------------ The Bridge table describes individual bridges within an Open vSwitch instance. The 'external-ids:bridge-id' key uniquely identifies a particular bridge. In XenServer, this will likely be the same as the UUID returned by 'xe network-list' for that particular bridge. For example, to set the identifier for bridge "br0", the following command can be used: ovs-vsctl set Bridge br0 external-ids:bridge-id='"${UUID}"' The MAC address of the bridge may be manually configured by setting it with the "other_config:hwaddr" key. For example: ovs-vsctl set Bridge br0 other_config:hwaddr="12:34:56:78:90:ab" Interface table --------------- The Interface table describes an interface under the control of Open vSwitch. The 'external_ids' column contains keys that are used to provide additional information about the interface: attached-mac This field contains the MAC address of the device attached to the interface. On a hypervisor, this is the MAC address of the interface as seen inside a VM. It does not necessarily correlate to the host-side MAC address. For example, on XenServer, the MAC address on a VIF in the hypervisor is always FE:FF:FF:FF:FF:FF, but inside the VM a normal MAC address is seen. iface-id This field uniquely identifies the interface. In hypervisors, this allows the controller to follow VM network interfaces as VMs migrate. A well-chosen identifier should also allow an administrator or a controller to associate the interface with the corresponding object in the VM management system. For example, the Open vSwitch integration with XenServer by default uses the XenServer assigned UUID for a VIF record as the iface-id. iface-status In a hypervisor, there are situations where there are multiple interface choices for a single virtual ethernet interface inside a VM. Valid values are "active" and "inactive". A complete description is available in the ovs-vswitchd.conf.db(5) manpage. vm-id This field uniquely identifies the VM to which this interface belongs. A single VM may have multiple interfaces attached to it. As in the previous tables, the ovs-vsctl command may be used to configure the values. For example, to set the 'iface-id' on eth0, the following command can be used: ovs-vsctl set Interface eth0 external-ids:iface-id='"${UUID}"' openvswitch-2.5.9/PaxHeaders.82075/Makefile.in0000644000000000000000000000013213534540111015704 xustar0030 mtime=1567801417.129797016 30 atime=1567801417.293798225 30 ctime=1567801424.593852041 openvswitch-2.5.9/Makefile.in0000644000175000017500000113716413534540111017407 0ustar00jpettitjpettit00000000000000# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Copyright (C) 2007-2016 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Copyright (C) 2013 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright (C) 2013 Nicira, Inc. # # Copying and distribution of this file, with or without modification # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # Copyright 2015 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License.You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the # License for the specific language governing permissions and limitations # under the License. # Copyright (C) 2016 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @WIN32_TRUE@am__append_1 = -I $(top_srcdir)/include/windows -I \ @WIN32_TRUE@ $(top_srcdir)/datapath-windows/include \ @WIN32_TRUE@ $(PTHREAD_INCLUDES) $(MSVC_CFLAGS) @WIN32_TRUE@am__append_2 = $(PTHREAD_LDFLAGS) $(MSVC64_LDFLAGS) @DPDK_NETDEV_TRUE@am__append_3 = -D_FILE_OFFSET_BITS=64 @NDEBUG_TRUE@am__append_4 = -DNDEBUG @NDEBUG_TRUE@am__append_5 = -fomit-frame-pointer bin_PROGRAMS = utilities/ovs-appctl$(EXEEXT) \ utilities/ovs-testcontroller$(EXEEXT) \ utilities/ovs-dpctl$(EXEEXT) utilities/ovs-ofctl$(EXEEXT) \ utilities/ovs-vsctl$(EXEEXT) utilities/ovs-benchmark$(EXEEXT) \ ovsdb/ovsdb-tool$(EXEEXT) ovsdb/ovsdb-client$(EXEEXT) \ vtep/vtep-ctl$(EXEEXT) ovn/controller/ovn-controller$(EXEEXT) \ ovn/controller-vtep/ovn-controller-vtep$(EXEEXT) \ ovn/northd/ovn-northd$(EXEEXT) \ ovn/utilities/ovn-nbctl$(EXEEXT) \ ovn/utilities/ovn-sbctl$(EXEEXT) sbin_PROGRAMS = $(am__EXEEXT_3) vswitchd/ovs-vswitchd$(EXEEXT) \ ovsdb/ovsdb-server$(EXEEXT) noinst_PROGRAMS = $(am__EXEEXT_1) tests/test-ovsdb$(EXEEXT) \ tests/test-lib$(EXEEXT) $(am__EXEEXT_2) tests/ovstest$(EXEEXT) \ tests/test-strtok_r$(EXEEXT) tests/test-type-props$(EXEEXT) # If we're checked out from a Git repository, make sure that every # file that is in Git is distributed. # # We only enable this check when GNU make is in use because the # Makefile in datapath/linux, needed to get the list of files to # distribute, requires GNU make extensions. @GNU_MAKE_TRUE@am__append_6 = dist-hook-git @GNU_MAKE_TRUE@am__append_7 = all-distfiles all-gitfiles \ @GNU_MAKE_TRUE@ missing-distfiles distfiles @HAVE_GROFF_TRUE@am__append_8 = manpage-check @HAVE_GROFF_TRUE@am__append_9 = manpage-check @VSTUDIO_DDK_TRUE@am__append_10 = ovsext_make @VSTUDIO_DDK_TRUE@am__append_11 = ovsext_clean @WIN32_TRUE@am__append_12 = ${PTHREAD_LIBS} @WIN32_TRUE@am__append_13 = \ @WIN32_TRUE@ lib/daemon-windows.c \ @WIN32_TRUE@ lib/getopt_long.c \ @WIN32_TRUE@ lib/getrusage-windows.c \ @WIN32_TRUE@ lib/latch-windows.c \ @WIN32_TRUE@ lib/route-table-stub.c \ @WIN32_TRUE@ lib/if-notifier-stub.c \ @WIN32_TRUE@ lib/strsep.c @WIN32_FALSE@am__append_14 = \ @WIN32_FALSE@ lib/daemon-unix.c \ @WIN32_FALSE@ lib/latch-unix.c \ @WIN32_FALSE@ lib/signals.c \ @WIN32_FALSE@ lib/signals.h \ @WIN32_FALSE@ lib/socket-util-unix.c \ @WIN32_FALSE@ lib/stream-unix.c @HAVE_WNO_UNUSED_TRUE@am__append_15 = -Wno-unused @HAVE_WNO_UNUSED_PARAMETER_TRUE@am__append_16 = -Wno-unused-parameter @LINUX_TRUE@am__append_17 = \ @LINUX_TRUE@ lib/dpif-netlink.c \ @LINUX_TRUE@ lib/dpif-netlink.h \ @LINUX_TRUE@ lib/if-notifier.c \ @LINUX_TRUE@ lib/if-notifier.h \ @LINUX_TRUE@ lib/netdev-linux.c \ @LINUX_TRUE@ lib/netdev-linux.h \ @LINUX_TRUE@ lib/netlink-conntrack.c \ @LINUX_TRUE@ lib/netlink-conntrack.h \ @LINUX_TRUE@ lib/netlink-notifier.c \ @LINUX_TRUE@ lib/netlink-notifier.h \ @LINUX_TRUE@ lib/netlink-protocol.h \ @LINUX_TRUE@ lib/netlink-socket.c \ @LINUX_TRUE@ lib/netlink-socket.h \ @LINUX_TRUE@ lib/ovs-numa.c \ @LINUX_TRUE@ lib/ovs-numa.h \ @LINUX_TRUE@ lib/rtnetlink.c \ @LINUX_TRUE@ lib/rtnetlink.h \ @LINUX_TRUE@ lib/route-table.c \ @LINUX_TRUE@ lib/route-table.h @DPDK_NETDEV_TRUE@am__append_18 = \ @DPDK_NETDEV_TRUE@ lib/netdev-dpdk.c \ @DPDK_NETDEV_TRUE@ lib/netdev-dpdk.h @WIN32_TRUE@am__append_19 = \ @WIN32_TRUE@ lib/dpif-netlink.c \ @WIN32_TRUE@ lib/dpif-netlink.h \ @WIN32_TRUE@ lib/netdev-windows.c \ @WIN32_TRUE@ lib/netlink-notifier.c \ @WIN32_TRUE@ lib/netlink-notifier.h \ @WIN32_TRUE@ lib/netlink-protocol.h \ @WIN32_TRUE@ lib/netlink-socket.c \ @WIN32_TRUE@ lib/netlink-socket.h @HAVE_POSIX_AIO_TRUE@am__append_20 = lib/async-append-aio.c @HAVE_POSIX_AIO_FALSE@am__append_21 = lib/async-append-null.c @ESX_TRUE@am__append_22 = \ @ESX_TRUE@ lib/route-table-stub.c \ @ESX_TRUE@ lib/if-notifier-stub.c @HAVE_IF_DL_TRUE@am__append_23 = \ @HAVE_IF_DL_TRUE@ lib/if-notifier-bsd.c \ @HAVE_IF_DL_TRUE@ lib/netdev-bsd.c \ @HAVE_IF_DL_TRUE@ lib/rtbsd.c \ @HAVE_IF_DL_TRUE@ lib/rtbsd.h \ @HAVE_IF_DL_TRUE@ lib/route-table-bsd.c @HAVE_OPENSSL_TRUE@am__append_24 = lib/stream-ssl.c @HAVE_OPENSSL_TRUE@am__append_25 = lib/dhparams.c @HAVE_OPENSSL_FALSE@am__append_26 = lib/stream-nossl.c @WIN32_TRUE@am__append_27 = ${PTHREAD_LIBS} @HAVE_PYTHON_TRUE@am__append_28 = \ @HAVE_PYTHON_TRUE@ utilities/ovs-dpctl-top \ @HAVE_PYTHON_TRUE@ utilities/ovs-l3ping \ @HAVE_PYTHON_TRUE@ utilities/ovs-parse-backtrace \ @HAVE_PYTHON_TRUE@ utilities/ovs-pcap \ @HAVE_PYTHON_TRUE@ utilities/ovs-tcpundump \ @HAVE_PYTHON_TRUE@ utilities/ovs-test \ @HAVE_PYTHON_TRUE@ utilities/ovs-vlan-test @LINUX_TRUE@am__append_29 = utilities/ovs-vlan-bug-workaround @LINUX_TRUE@am__append_30 = utilities/nlmon @HAVE_PYTHON_TRUE@am__append_31 = utilities/bugtool/ovs-bugtool @HAVE_PYTHON_TRUE@am__append_32 = utilities/bugtool/ovs-bugtool @HAVE_PYTHON_TRUE@am__append_33 = utilities/bugtool/ovs-bugtool.8 @HAVE_PYTHON_TRUE@am__append_34 = utilities/bugtool/ovs-bugtool.8.in @HAVE_PYTHON_TRUE@am__append_35 = utilities/bugtool/ovs-bugtool.8 @HAVE_PYTHON_TRUE@am__append_36 = $(bugtool_scripts) @HAVE_PYTHON_TRUE@am__append_37 = bugtool-install-data-local @HAVE_PYTHON_TRUE@am__append_38 = bugtool-uninstall-local @DPDK_NETDEV_TRUE@am__append_39 = tests/test-dpdkr @WIN32_FALSE@am__append_40 = \ @WIN32_FALSE@ tests/test-unix-socket.c @LINUX_TRUE@am__append_41 = \ @LINUX_TRUE@ tests/test-netlink-conntrack.c @HAVE_OPENSSL_TRUE@am__append_42 = $(TESTPKI_FILES) @HAVE_OPENSSL_TRUE@am__append_43 = $(TESTPKI_FILES) tests/ovs-pki.log @HAVE_OPENSSL_TRUE@am__append_44 = clean-pki @HAVE_PYTHON_TRUE@am__append_45 = .h .hstamp @HAVE_PYTHON_TRUE@am__append_46 = $(HSTAMP_FILES) @HAVE_PYTHON_TRUE@am__append_47 = $(HSTAMP_FILES) @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@am__append_48 = vswitchd/vswitch.gv vswitchd/vswitch.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@am__append_49 = vtep/vtep.gv vtep/vtep.pic @WIN32_TRUE@am__append_50 = $(srcdir)/datapath-windows/include/OvsDpInterface.h @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@am__append_51 = ovn/ovn-sb.gv ovn/ovn-sb.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@am__append_52 = ovn/ovn-nb.gv ovn/ovn-nb.pic subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ $(top_srcdir)/m4/ax_check_openssl.m4 \ $(top_srcdir)/m4/ax_func_posix_memalign.m4 \ $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/openvswitch.m4 $(top_srcdir)/m4/compat.at \ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_check_SCRIPTS) \ $(dist_noinst_SCRIPTS) $(dist_pkgdata_SCRIPTS) \ $(dist_sbin_SCRIPTS) $(dist_scripts_SCRIPTS) \ $(dist_pkgdata_DATA) $(dist_scripts_DATA) \ $(am__noinst_HEADERS_DIST) $(openflowinclude_HEADERS) \ $(openvswitchinclude_HEADERS) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = lib/stdio.h lib/string.h ovsdb/libovsdb.sym \ ofproto/libofproto.sym lib/libsflow.sym lib/libopenvswitch.sym \ ovn/lib/libovn.sym vtep/libvtep.sym datapath/linux/Kbuild \ datapath/linux/Makefile datapath/linux/Makefile.main \ tests/atlocal lib/libopenvswitch.pc lib/libsflow.pc \ ofproto/libofproto.pc ovsdb/libovsdb.pc \ include/openvswitch/version.h CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(completiondir)" "$(DESTDIR)$(pkgdatadir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(scriptsdir)" \ "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(scriptsdir)" \ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)" \ "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(scriptsdir)" \ "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(scriptsdir)" \ "$(DESTDIR)$(openflowincludedir)" \ "$(DESTDIR)$(openvswitchincludedir)" LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = @WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) lib_libopenvswitch_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) am__lib_libopenvswitch_la_SOURCES_DIST = lib/aes128.c lib/aes128.h \ lib/async-append.h lib/backtrace.c lib/backtrace.h lib/bfd.c \ lib/bfd.h lib/bitmap.h lib/bundle.c lib/bundle.h \ lib/byte-order.h lib/byteq.c lib/byteq.h lib/cfm.c lib/cfm.h \ lib/classifier.c lib/classifier.h lib/classifier-private.h \ lib/cmap.c lib/cmap.h lib/command-line.c lib/command-line.h \ lib/compiler.h lib/connectivity.c lib/connectivity.h \ lib/coverage.c lib/coverage.h lib/crc32c.c lib/crc32c.h \ lib/csum.c lib/csum.h lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c \ lib/daemon.h lib/daemon-private.h lib/db-ctl-base.c \ lib/db-ctl-base.h lib/dhcp.h lib/dummy.c lib/dummy.h \ lib/dhparams.h lib/dirs.h lib/dpctl.c lib/dpctl.h \ lib/dp-packet.h lib/dp-packet.c lib/dpif-netdev.c \ lib/dpif-netdev.h lib/dpif-provider.h lib/dpif.c lib/dpif.h \ lib/heap.c lib/heap.h lib/dynamic-string.c \ lib/dynamic-string.h lib/entropy.c lib/entropy.h \ lib/fat-rwlock.c lib/fat-rwlock.h lib/fatal-signal.c \ lib/fatal-signal.h lib/flow.c lib/flow.h lib/geneve.h \ lib/guarded-list.c lib/guarded-list.h lib/hash.c lib/hash.h \ lib/hindex.c lib/hindex.h lib/hmap.c lib/hmap.h lib/hmapx.c \ lib/hmapx.h lib/id-pool.c lib/id-pool.h lib/jhash.c \ lib/jhash.h lib/json.c lib/json.h lib/jsonrpc.c lib/jsonrpc.h \ lib/lacp.c lib/lacp.h lib/latch.h lib/learn.c lib/learn.h \ lib/learning-switch.c lib/learning-switch.h lib/list.h \ lib/lockfile.c lib/lockfile.h lib/mac-learning.c \ lib/mac-learning.h lib/match.c lib/match.h \ lib/mcast-snooping.c lib/mcast-snooping.h lib/memory.c \ lib/memory.h lib/meta-flow.c lib/meta-flow.h lib/multipath.c \ lib/multipath.h lib/netdev-dummy.c lib/netdev-provider.h \ lib/netdev-vport.c lib/netdev-vport.h lib/netdev.c \ lib/netdev.h lib/netflow.h lib/netlink.c lib/netlink.h \ lib/nx-match.c lib/nx-match.h lib/odp-execute.c \ lib/odp-execute.h lib/odp-util.c lib/odp-util.h \ lib/ofp-actions.c lib/ofp-actions.h lib/ofp-errors.c \ lib/ofp-errors.h lib/ofp-msgs.c lib/ofp-msgs.h lib/ofp-parse.c \ lib/ofp-parse.h lib/ofp-print.c lib/ofp-print.h lib/ofp-util.c \ lib/ofp-util.h lib/ofp-version-opt.h lib/ofp-version-opt.c \ lib/ofpbuf.c lib/ofpbuf.h lib/ovs-atomic-c11.h \ lib/ovs-atomic-clang.h lib/ovs-atomic-flag-gcc4.7+.h \ lib/ovs-atomic-gcc4+.h lib/ovs-atomic-gcc4.7+.h \ lib/ovs-atomic-i586.h lib/ovs-atomic-locked.c \ lib/ovs-atomic-locked.h lib/ovs-atomic-msvc.h \ lib/ovs-atomic-pthreads.h lib/ovs-atomic-x86_64.h \ lib/ovs-atomic.h lib/ovs-lldp.c lib/ovs-lldp.h lib/ovs-rcu.c \ lib/ovs-rcu.h lib/ovs-router.h lib/ovs-router.c \ lib/ovs-thread.c lib/ovs-thread.h lib/ovsdb-data.c \ lib/ovsdb-data.h lib/ovsdb-error.c lib/ovsdb-error.h \ lib/ovsdb-idl-provider.h lib/ovsdb-idl.c lib/ovsdb-idl.h \ lib/ovsdb-parser.c lib/ovsdb-parser.h lib/ovsdb-types.c \ lib/ovsdb-types.h lib/packets.c lib/packets.h lib/pcap-file.c \ lib/pcap-file.h lib/perf-counter.h lib/perf-counter.c \ lib/poll-loop.c lib/poll-loop.h lib/process.c lib/process.h \ lib/pvector.c lib/pvector.h lib/random.c lib/random.h \ lib/rconn.c lib/rconn.h lib/rculist.h lib/reconnect.c \ lib/reconnect.h lib/rstp.c lib/rstp.h lib/rstp-common.h \ lib/rstp-state-machines.c lib/rstp-state-machines.h \ lib/sat-math.h lib/seq.c lib/seq.h lib/sha1.c lib/sha1.h \ lib/shash.c lib/shash.h lib/simap.c lib/simap.h lib/smap.c \ lib/smap.h lib/socket-util.c lib/socket-util.h lib/sort.c \ lib/sort.h lib/sset.c lib/sset.h lib/stp.c lib/stp.h \ lib/stream-fd.c lib/stream-fd.h lib/stream-provider.h \ lib/stream-ssl.h lib/stream-tcp.c lib/stream.c lib/stream.h \ lib/stdio.c lib/string.c lib/svec.c lib/svec.h \ lib/syslog-direct.c lib/syslog-direct.h lib/syslog-libc.c \ lib/syslog-libc.h lib/syslog-provider.h lib/table.c \ lib/table.h lib/timer.c lib/timer.h lib/timeval.c \ lib/timeval.h lib/tnl-neigh-cache.c lib/tnl-neigh-cache.h \ lib/tnl-ports.c lib/tnl-ports.h lib/token-bucket.c \ lib/tun-metadata.c lib/tun-metadata.h lib/type-props.h \ lib/unaligned.h lib/unicode.c lib/unicode.h lib/unixctl.c \ lib/unixctl.h lib/util.c lib/util.h lib/uuid.c lib/uuid.h \ lib/valgrind.h lib/vconn-provider.h lib/vconn-stream.c \ lib/vconn.c lib/vlan-bitmap.c lib/vlan-bitmap.h lib/vlandev.c \ lib/vlandev.h lib/vlog.c lib/lldp/aa-structs.h lib/lldp/lldp.c \ lib/lldp/lldp-const.h lib/lldp/lldp-tlv.h lib/lldp/lldpd.c \ lib/lldp/lldpd.h lib/lldp/lldpd-structs.c \ lib/lldp/lldpd-structs.h lib/daemon-windows.c \ lib/getopt_long.c lib/getrusage-windows.c lib/latch-windows.c \ lib/route-table-stub.c lib/if-notifier-stub.c lib/strsep.c \ lib/daemon-unix.c lib/latch-unix.c lib/signals.c lib/signals.h \ lib/socket-util-unix.c lib/stream-unix.c lib/dpif-netlink.c \ lib/dpif-netlink.h lib/if-notifier.c lib/if-notifier.h \ lib/netdev-linux.c lib/netdev-linux.h lib/netlink-conntrack.c \ lib/netlink-conntrack.h lib/netlink-notifier.c \ lib/netlink-notifier.h lib/netlink-protocol.h \ lib/netlink-socket.c lib/netlink-socket.h lib/ovs-numa.c \ lib/ovs-numa.h lib/rtnetlink.c lib/rtnetlink.h \ lib/route-table.c lib/route-table.h lib/netdev-dpdk.c \ lib/netdev-dpdk.h lib/netdev-windows.c lib/async-append-aio.c \ lib/async-append-null.c lib/if-notifier-bsd.c lib/netdev-bsd.c \ lib/rtbsd.c lib/rtbsd.h lib/route-table-bsd.c lib/stream-ssl.c \ lib/stream-nossl.c am__dirstamp = $(am__leading_dot)dirstamp @WIN32_TRUE@am__objects_1 = lib/daemon-windows.lo lib/getopt_long.lo \ @WIN32_TRUE@ lib/getrusage-windows.lo lib/latch-windows.lo \ @WIN32_TRUE@ lib/route-table-stub.lo lib/if-notifier-stub.lo \ @WIN32_TRUE@ lib/strsep.lo @WIN32_FALSE@am__objects_2 = lib/daemon-unix.lo lib/latch-unix.lo \ @WIN32_FALSE@ lib/signals.lo lib/socket-util-unix.lo \ @WIN32_FALSE@ lib/stream-unix.lo @LINUX_TRUE@am__objects_3 = lib/dpif-netlink.lo lib/if-notifier.lo \ @LINUX_TRUE@ lib/netdev-linux.lo lib/netlink-conntrack.lo \ @LINUX_TRUE@ lib/netlink-notifier.lo lib/netlink-socket.lo \ @LINUX_TRUE@ lib/ovs-numa.lo lib/rtnetlink.lo \ @LINUX_TRUE@ lib/route-table.lo @DPDK_NETDEV_TRUE@am__objects_4 = lib/netdev-dpdk.lo @WIN32_TRUE@am__objects_5 = lib/dpif-netlink.lo lib/netdev-windows.lo \ @WIN32_TRUE@ lib/netlink-notifier.lo lib/netlink-socket.lo @HAVE_POSIX_AIO_TRUE@am__objects_6 = lib/async-append-aio.lo @HAVE_POSIX_AIO_FALSE@am__objects_7 = lib/async-append-null.lo @ESX_TRUE@am__objects_8 = lib/route-table-stub.lo \ @ESX_TRUE@ lib/if-notifier-stub.lo @HAVE_IF_DL_TRUE@am__objects_9 = lib/if-notifier-bsd.lo \ @HAVE_IF_DL_TRUE@ lib/netdev-bsd.lo lib/rtbsd.lo \ @HAVE_IF_DL_TRUE@ lib/route-table-bsd.lo @HAVE_OPENSSL_TRUE@am__objects_10 = lib/stream-ssl.lo @HAVE_OPENSSL_FALSE@am__objects_11 = lib/stream-nossl.lo am_lib_libopenvswitch_la_OBJECTS = lib/aes128.lo lib/backtrace.lo \ lib/bfd.lo lib/bundle.lo lib/byteq.lo lib/cfm.lo \ lib/classifier.lo lib/cmap.lo lib/command-line.lo \ lib/connectivity.lo lib/coverage.lo lib/crc32c.lo lib/csum.lo \ lib/ct-dpif.lo lib/daemon.lo lib/db-ctl-base.lo lib/dummy.lo \ lib/dpctl.lo lib/dp-packet.lo lib/dpif-netdev.lo lib/dpif.lo \ lib/heap.lo lib/dynamic-string.lo lib/entropy.lo \ lib/fat-rwlock.lo lib/fatal-signal.lo lib/flow.lo \ lib/guarded-list.lo lib/hash.lo lib/hindex.lo lib/hmap.lo \ lib/hmapx.lo lib/id-pool.lo lib/jhash.lo lib/json.lo \ lib/jsonrpc.lo lib/lacp.lo lib/learn.lo lib/learning-switch.lo \ lib/lockfile.lo lib/mac-learning.lo lib/match.lo \ lib/mcast-snooping.lo lib/memory.lo lib/meta-flow.lo \ lib/multipath.lo lib/netdev-dummy.lo lib/netdev-vport.lo \ lib/netdev.lo lib/netlink.lo lib/nx-match.lo \ lib/odp-execute.lo lib/odp-util.lo lib/ofp-actions.lo \ lib/ofp-errors.lo lib/ofp-msgs.lo lib/ofp-parse.lo \ lib/ofp-print.lo lib/ofp-util.lo lib/ofp-version-opt.lo \ lib/ofpbuf.lo lib/ovs-atomic-locked.lo lib/ovs-lldp.lo \ lib/ovs-rcu.lo lib/ovs-router.lo lib/ovs-thread.lo \ lib/ovsdb-data.lo lib/ovsdb-error.lo lib/ovsdb-idl.lo \ lib/ovsdb-parser.lo lib/ovsdb-types.lo lib/packets.lo \ lib/pcap-file.lo lib/perf-counter.lo lib/poll-loop.lo \ lib/process.lo lib/pvector.lo lib/random.lo lib/rconn.lo \ lib/reconnect.lo lib/rstp.lo lib/rstp-state-machines.lo \ lib/seq.lo lib/sha1.lo lib/shash.lo lib/simap.lo lib/smap.lo \ lib/socket-util.lo lib/sort.lo lib/sset.lo lib/stp.lo \ lib/stream-fd.lo lib/stream-tcp.lo lib/stream.lo lib/stdio.lo \ lib/string.lo lib/svec.lo lib/syslog-direct.lo \ lib/syslog-libc.lo lib/table.lo lib/timer.lo lib/timeval.lo \ lib/tnl-neigh-cache.lo lib/tnl-ports.lo lib/token-bucket.lo \ lib/tun-metadata.lo lib/unicode.lo lib/unixctl.lo lib/util.lo \ lib/uuid.lo lib/vconn-stream.lo lib/vconn.lo \ lib/vlan-bitmap.lo lib/vlandev.lo lib/vlog.lo lib/lldp/lldp.lo \ lib/lldp/lldpd.lo lib/lldp/lldpd-structs.lo $(am__objects_1) \ $(am__objects_2) $(am__objects_3) $(am__objects_4) \ $(am__objects_5) $(am__objects_6) $(am__objects_7) \ $(am__objects_8) $(am__objects_9) $(am__objects_10) \ $(am__objects_11) @HAVE_OPENSSL_TRUE@am__objects_12 = lib/dhparams.lo nodist_lib_libopenvswitch_la_OBJECTS = lib/dirs.lo lib/vswitch-idl.lo \ $(am__objects_12) lib_libopenvswitch_la_OBJECTS = $(am_lib_libopenvswitch_la_OBJECTS) \ $(nodist_lib_libopenvswitch_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = lib_libopenvswitch_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(lib_libopenvswitch_la_LDFLAGS) \ $(LDFLAGS) -o $@ lib_libsflow_la_LIBADD = am_lib_libsflow_la_OBJECTS = lib/lib_libsflow_la-sflow_agent.lo \ lib/lib_libsflow_la-sflow_sampler.lo \ lib/lib_libsflow_la-sflow_poller.lo \ lib/lib_libsflow_la-sflow_receiver.lo lib_libsflow_la_OBJECTS = $(am_lib_libsflow_la_OBJECTS) lib_libsflow_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(lib_libsflow_la_CFLAGS) $(CFLAGS) $(lib_libsflow_la_LDFLAGS) \ $(LDFLAGS) -o $@ ofproto_libofproto_la_DEPENDENCIES = lib/libsflow.la \ $(am__DEPENDENCIES_2) am_ofproto_libofproto_la_OBJECTS = \ ofproto/ofproto_libofproto_la-bond.lo \ ofproto/ofproto_libofproto_la-collectors.lo \ ofproto/ofproto_libofproto_la-connmgr.lo \ ofproto/ofproto_libofproto_la-fail-open.lo \ ofproto/ofproto_libofproto_la-in-band.lo \ ofproto/ofproto_libofproto_la-names.lo \ ofproto/ofproto_libofproto_la-netflow.lo \ ofproto/ofproto_libofproto_la-ofproto.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo \ ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo \ ofproto/ofproto_libofproto_la-pktbuf.lo \ ofproto/ofproto_libofproto_la-pinsched.lo \ ofproto/ofproto_libofproto_la-tunnel.lo \ ofproto/ofproto_libofproto_la-bundles.lo ofproto_libofproto_la_OBJECTS = $(am_ofproto_libofproto_la_OBJECTS) ofproto_libofproto_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) \ $(ofproto_libofproto_la_LDFLAGS) $(LDFLAGS) -o $@ ovn_lib_libovn_la_LIBADD = am_ovn_lib_libovn_la_OBJECTS = ovn/lib/actions.lo ovn/lib/expr.lo \ ovn/lib/lex.lo nodist_ovn_lib_libovn_la_OBJECTS = ovn/lib/ovn-nb-idl.lo \ ovn/lib/ovn-sb-idl.lo ovn_lib_libovn_la_OBJECTS = $(am_ovn_lib_libovn_la_OBJECTS) \ $(nodist_ovn_lib_libovn_la_OBJECTS) ovn_lib_libovn_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(ovn_lib_libovn_la_LDFLAGS) $(LDFLAGS) \ -o $@ ovsdb_libovsdb_la_LIBADD = am_ovsdb_libovsdb_la_OBJECTS = ovsdb/ovsdb_libovsdb_la-column.lo \ ovsdb/ovsdb_libovsdb_la-condition.lo \ ovsdb/ovsdb_libovsdb_la-execution.lo \ ovsdb/ovsdb_libovsdb_la-file.lo \ ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo \ ovsdb/ovsdb_libovsdb_la-log.lo \ ovsdb/ovsdb_libovsdb_la-mutation.lo \ ovsdb/ovsdb_libovsdb_la-ovsdb.lo \ ovsdb/ovsdb_libovsdb_la-monitor.lo \ ovsdb/ovsdb_libovsdb_la-query.lo \ ovsdb/ovsdb_libovsdb_la-row.lo \ ovsdb/ovsdb_libovsdb_la-server.lo \ ovsdb/ovsdb_libovsdb_la-table.lo \ ovsdb/ovsdb_libovsdb_la-trigger.lo \ ovsdb/ovsdb_libovsdb_la-transaction.lo ovsdb_libovsdb_la_OBJECTS = $(am_ovsdb_libovsdb_la_OBJECTS) ovsdb_libovsdb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) \ $(ovsdb_libovsdb_la_LDFLAGS) $(LDFLAGS) -o $@ vtep_libvtep_la_LIBADD = nodist_vtep_libvtep_la_OBJECTS = vtep/vtep-idl.lo vtep_libvtep_la_OBJECTS = $(nodist_vtep_libvtep_la_OBJECTS) vtep_libvtep_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(vtep_libvtep_la_LDFLAGS) $(LDFLAGS) \ -o $@ @LINUX_TRUE@am__EXEEXT_1 = utilities/nlmon$(EXEEXT) @DPDK_NETDEV_TRUE@am__EXEEXT_2 = tests/test-dpdkr$(EXEEXT) @LINUX_TRUE@am__EXEEXT_3 = utilities/ovs-vlan-bug-workaround$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS) am_ovn_controller_vtep_ovn_controller_vtep_OBJECTS = \ ovn/controller-vtep/binding.$(OBJEXT) \ ovn/controller-vtep/gateway.$(OBJEXT) \ ovn/controller-vtep/ovn-controller-vtep.$(OBJEXT) \ ovn/controller-vtep/vtep.$(OBJEXT) ovn_controller_vtep_ovn_controller_vtep_OBJECTS = \ $(am_ovn_controller_vtep_ovn_controller_vtep_OBJECTS) ovn_controller_vtep_ovn_controller_vtep_DEPENDENCIES = \ ovn/lib/libovn.la lib/libopenvswitch.la vtep/libvtep.la am_ovn_controller_ovn_controller_OBJECTS = \ ovn/controller/binding.$(OBJEXT) \ ovn/controller/chassis.$(OBJEXT) \ ovn/controller/encaps.$(OBJEXT) ovn/controller/lflow.$(OBJEXT) \ ovn/controller/ofctrl.$(OBJEXT) \ ovn/controller/pinctrl.$(OBJEXT) \ ovn/controller/patch.$(OBJEXT) \ ovn/controller/ovn-controller.$(OBJEXT) \ ovn/controller/physical.$(OBJEXT) ovn_controller_ovn_controller_OBJECTS = \ $(am_ovn_controller_ovn_controller_OBJECTS) ovn_controller_ovn_controller_DEPENDENCIES = ovn/lib/libovn.la \ lib/libopenvswitch.la am_ovn_northd_ovn_northd_OBJECTS = ovn/northd/ovn-northd.$(OBJEXT) ovn_northd_ovn_northd_OBJECTS = $(am_ovn_northd_ovn_northd_OBJECTS) ovn_northd_ovn_northd_DEPENDENCIES = ovn/lib/libovn.la \ ovsdb/libovsdb.la lib/libopenvswitch.la am_ovn_utilities_ovn_nbctl_OBJECTS = \ ovn/utilities/ovn-nbctl.$(OBJEXT) ovn_utilities_ovn_nbctl_OBJECTS = \ $(am_ovn_utilities_ovn_nbctl_OBJECTS) ovn_utilities_ovn_nbctl_DEPENDENCIES = ovn/lib/libovn.la \ ovsdb/libovsdb.la lib/libopenvswitch.la am_ovn_utilities_ovn_sbctl_OBJECTS = \ ovn/utilities/ovn-sbctl.$(OBJEXT) ovn_utilities_ovn_sbctl_OBJECTS = \ $(am_ovn_utilities_ovn_sbctl_OBJECTS) ovn_utilities_ovn_sbctl_DEPENDENCIES = ovn/lib/libovn.la \ ovsdb/libovsdb.la lib/libopenvswitch.la am_ovsdb_ovsdb_client_OBJECTS = ovsdb/ovsdb-client.$(OBJEXT) ovsdb_ovsdb_client_OBJECTS = $(am_ovsdb_ovsdb_client_OBJECTS) ovsdb_ovsdb_client_DEPENDENCIES = ovsdb/libovsdb.la \ lib/libopenvswitch.la am_ovsdb_ovsdb_server_OBJECTS = ovsdb/ovsdb-server.$(OBJEXT) ovsdb_ovsdb_server_OBJECTS = $(am_ovsdb_ovsdb_server_OBJECTS) ovsdb_ovsdb_server_DEPENDENCIES = ovsdb/libovsdb.la \ lib/libopenvswitch.la am_ovsdb_ovsdb_tool_OBJECTS = ovsdb/ovsdb-tool.$(OBJEXT) ovsdb_ovsdb_tool_OBJECTS = $(am_ovsdb_ovsdb_tool_OBJECTS) ovsdb_ovsdb_tool_DEPENDENCIES = ovsdb/libovsdb.la \ lib/libopenvswitch.la am__tests_ovstest_SOURCES_DIST = tests/ovstest.c tests/ovstest.h \ tests/test-aes128.c tests/test-atomic.c tests/test-bundle.c \ tests/test-byte-order.c tests/test-classifier.c \ tests/test-cmap.c tests/test-csum.c tests/test-flows.c \ tests/test-hash.c tests/test-heap.c tests/test-hindex.c \ tests/test-hmap.c tests/test-json.c tests/test-jsonrpc.c \ tests/test-list.c tests/test-lockfile.c tests/test-multipath.c \ tests/test-netflow.c tests/test-odp.c tests/test-ofpbuf.c \ tests/test-ovn.c tests/test-packets.c tests/test-random.c \ tests/test-rcu.c tests/test-reconnect.c tests/test-rstp.c \ tests/test-sflow.c tests/test-sha1.c tests/test-stp.c \ tests/test-util.c tests/test-uuid.c tests/test-bitmap.c \ tests/test-vconn.c tests/test-aa.c tests/test-unix-socket.c \ tests/test-netlink-conntrack.c @WIN32_FALSE@am__objects_13 = tests/test-unix-socket.$(OBJEXT) @LINUX_TRUE@am__objects_14 = tests/test-netlink-conntrack.$(OBJEXT) am_tests_ovstest_OBJECTS = tests/ovstest.$(OBJEXT) \ tests/test-aes128.$(OBJEXT) tests/test-atomic.$(OBJEXT) \ tests/test-bundle.$(OBJEXT) tests/test-byte-order.$(OBJEXT) \ tests/test-classifier.$(OBJEXT) tests/test-cmap.$(OBJEXT) \ tests/test-csum.$(OBJEXT) tests/test-flows.$(OBJEXT) \ tests/test-hash.$(OBJEXT) tests/test-heap.$(OBJEXT) \ tests/test-hindex.$(OBJEXT) tests/test-hmap.$(OBJEXT) \ tests/test-json.$(OBJEXT) tests/test-jsonrpc.$(OBJEXT) \ tests/test-list.$(OBJEXT) tests/test-lockfile.$(OBJEXT) \ tests/test-multipath.$(OBJEXT) tests/test-netflow.$(OBJEXT) \ tests/test-odp.$(OBJEXT) tests/test-ofpbuf.$(OBJEXT) \ tests/test-ovn.$(OBJEXT) tests/test-packets.$(OBJEXT) \ tests/test-random.$(OBJEXT) tests/test-rcu.$(OBJEXT) \ tests/test-reconnect.$(OBJEXT) tests/test-rstp.$(OBJEXT) \ tests/test-sflow.$(OBJEXT) tests/test-sha1.$(OBJEXT) \ tests/test-stp.$(OBJEXT) tests/test-util.$(OBJEXT) \ tests/test-uuid.$(OBJEXT) tests/test-bitmap.$(OBJEXT) \ tests/test-vconn.$(OBJEXT) tests/test-aa.$(OBJEXT) \ $(am__objects_13) $(am__objects_14) tests_ovstest_OBJECTS = $(am_tests_ovstest_OBJECTS) tests_ovstest_DEPENDENCIES = lib/libopenvswitch.la ovn/lib/libovn.la am__tests_test_dpdkr_SOURCES_DIST = tests/dpdk/ring_client.c @DPDK_NETDEV_TRUE@am_tests_test_dpdkr_OBJECTS = \ @DPDK_NETDEV_TRUE@ tests/dpdk/ring_client.$(OBJEXT) tests_test_dpdkr_OBJECTS = $(am_tests_test_dpdkr_OBJECTS) @DPDK_NETDEV_TRUE@tests_test_dpdkr_DEPENDENCIES = \ @DPDK_NETDEV_TRUE@ lib/libopenvswitch.la $(am__DEPENDENCIES_1) am_tests_test_lib_OBJECTS = tests/test-lib.$(OBJEXT) tests_test_lib_OBJECTS = $(am_tests_test_lib_OBJECTS) tests_test_lib_DEPENDENCIES = lib/libopenvswitch.la am_tests_test_ovsdb_OBJECTS = tests/test-ovsdb.$(OBJEXT) nodist_tests_test_ovsdb_OBJECTS = tests/idltest.$(OBJEXT) tests_test_ovsdb_OBJECTS = $(am_tests_test_ovsdb_OBJECTS) \ $(nodist_tests_test_ovsdb_OBJECTS) tests_test_ovsdb_DEPENDENCIES = ovsdb/libovsdb.la \ lib/libopenvswitch.la am_tests_test_strtok_r_OBJECTS = tests/test-strtok_r.$(OBJEXT) tests_test_strtok_r_OBJECTS = $(am_tests_test_strtok_r_OBJECTS) tests_test_strtok_r_LDADD = $(LDADD) am_tests_test_type_props_OBJECTS = tests/test-type-props.$(OBJEXT) tests_test_type_props_OBJECTS = $(am_tests_test_type_props_OBJECTS) tests_test_type_props_LDADD = $(LDADD) am__utilities_nlmon_SOURCES_DIST = utilities/nlmon.c @LINUX_TRUE@am_utilities_nlmon_OBJECTS = utilities/nlmon.$(OBJEXT) utilities_nlmon_OBJECTS = $(am_utilities_nlmon_OBJECTS) @LINUX_TRUE@utilities_nlmon_DEPENDENCIES = lib/libopenvswitch.la am_utilities_ovs_appctl_OBJECTS = utilities/ovs-appctl.$(OBJEXT) utilities_ovs_appctl_OBJECTS = $(am_utilities_ovs_appctl_OBJECTS) utilities_ovs_appctl_DEPENDENCIES = lib/libopenvswitch.la am_utilities_ovs_benchmark_OBJECTS = \ utilities/ovs-benchmark.$(OBJEXT) utilities_ovs_benchmark_OBJECTS = \ $(am_utilities_ovs_benchmark_OBJECTS) utilities_ovs_benchmark_DEPENDENCIES = lib/libopenvswitch.la am_utilities_ovs_dpctl_OBJECTS = utilities/ovs-dpctl.$(OBJEXT) utilities_ovs_dpctl_OBJECTS = $(am_utilities_ovs_dpctl_OBJECTS) utilities_ovs_dpctl_DEPENDENCIES = lib/libopenvswitch.la am_utilities_ovs_ofctl_OBJECTS = utilities/ovs-ofctl.$(OBJEXT) utilities_ovs_ofctl_OBJECTS = $(am_utilities_ovs_ofctl_OBJECTS) utilities_ovs_ofctl_DEPENDENCIES = ofproto/libofproto.la \ lib/libopenvswitch.la am_utilities_ovs_testcontroller_OBJECTS = \ utilities/ovs-testcontroller.$(OBJEXT) utilities_ovs_testcontroller_OBJECTS = \ $(am_utilities_ovs_testcontroller_OBJECTS) utilities_ovs_testcontroller_DEPENDENCIES = lib/libopenvswitch.la \ $(am__DEPENDENCIES_1) am__utilities_ovs_vlan_bug_workaround_SOURCES_DIST = \ utilities/ovs-vlan-bug-workaround.c @LINUX_TRUE@am_utilities_ovs_vlan_bug_workaround_OBJECTS = \ @LINUX_TRUE@ utilities/ovs-vlan-bug-workaround.$(OBJEXT) utilities_ovs_vlan_bug_workaround_OBJECTS = \ $(am_utilities_ovs_vlan_bug_workaround_OBJECTS) @LINUX_TRUE@utilities_ovs_vlan_bug_workaround_DEPENDENCIES = \ @LINUX_TRUE@ lib/libopenvswitch.la am_utilities_ovs_vsctl_OBJECTS = utilities/ovs-vsctl.$(OBJEXT) utilities_ovs_vsctl_OBJECTS = $(am_utilities_ovs_vsctl_OBJECTS) utilities_ovs_vsctl_DEPENDENCIES = lib/libopenvswitch.la am_vswitchd_ovs_vswitchd_OBJECTS = vswitchd/bridge.$(OBJEXT) \ vswitchd/ovs-vswitchd.$(OBJEXT) \ vswitchd/system-stats.$(OBJEXT) vswitchd/xenserver.$(OBJEXT) vswitchd_ovs_vswitchd_OBJECTS = $(am_vswitchd_ovs_vswitchd_OBJECTS) vswitchd_ovs_vswitchd_DEPENDENCIES = ofproto/libofproto.la \ lib/libsflow.la lib/libopenvswitch.la vswitchd_ovs_vswitchd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(vswitchd_ovs_vswitchd_LDFLAGS) \ $(LDFLAGS) -o $@ am_vtep_vtep_ctl_OBJECTS = vtep/vtep-ctl.$(OBJEXT) vtep_vtep_ctl_OBJECTS = $(am_vtep_vtep_ctl_OBJECTS) vtep_vtep_ctl_DEPENDENCIES = vtep/libvtep.la lib/libopenvswitch.la SCRIPTS = $(bin_SCRIPTS) $(completion_SCRIPTS) $(dist_noinst_SCRIPTS) \ $(dist_pkgdata_SCRIPTS) $(dist_sbin_SCRIPTS) \ $(dist_scripts_SCRIPTS) $(noinst_SCRIPTS) $(sbin_SCRIPTS) \ $(scripts_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(lib_libopenvswitch_la_SOURCES) \ $(nodist_lib_libopenvswitch_la_SOURCES) \ $(lib_libsflow_la_SOURCES) $(ofproto_libofproto_la_SOURCES) \ $(ovn_lib_libovn_la_SOURCES) \ $(nodist_ovn_lib_libovn_la_SOURCES) \ $(ovsdb_libovsdb_la_SOURCES) $(nodist_vtep_libvtep_la_SOURCES) \ $(ovn_controller_vtep_ovn_controller_vtep_SOURCES) \ $(ovn_controller_ovn_controller_SOURCES) \ $(ovn_northd_ovn_northd_SOURCES) \ $(ovn_utilities_ovn_nbctl_SOURCES) \ $(ovn_utilities_ovn_sbctl_SOURCES) \ $(ovsdb_ovsdb_client_SOURCES) $(ovsdb_ovsdb_server_SOURCES) \ $(ovsdb_ovsdb_tool_SOURCES) $(tests_ovstest_SOURCES) \ $(tests_test_dpdkr_SOURCES) $(tests_test_lib_SOURCES) \ $(tests_test_ovsdb_SOURCES) $(nodist_tests_test_ovsdb_SOURCES) \ $(tests_test_strtok_r_SOURCES) \ $(tests_test_type_props_SOURCES) $(utilities_nlmon_SOURCES) \ $(utilities_ovs_appctl_SOURCES) \ $(utilities_ovs_benchmark_SOURCES) \ $(utilities_ovs_dpctl_SOURCES) $(utilities_ovs_ofctl_SOURCES) \ $(utilities_ovs_testcontroller_SOURCES) \ $(utilities_ovs_vlan_bug_workaround_SOURCES) \ $(utilities_ovs_vsctl_SOURCES) \ $(vswitchd_ovs_vswitchd_SOURCES) $(vtep_vtep_ctl_SOURCES) DIST_SOURCES = $(am__lib_libopenvswitch_la_SOURCES_DIST) \ $(lib_libsflow_la_SOURCES) $(ofproto_libofproto_la_SOURCES) \ $(ovn_lib_libovn_la_SOURCES) $(ovsdb_libovsdb_la_SOURCES) \ $(ovn_controller_vtep_ovn_controller_vtep_SOURCES) \ $(ovn_controller_ovn_controller_SOURCES) \ $(ovn_northd_ovn_northd_SOURCES) \ $(ovn_utilities_ovn_nbctl_SOURCES) \ $(ovn_utilities_ovn_sbctl_SOURCES) \ $(ovsdb_ovsdb_client_SOURCES) $(ovsdb_ovsdb_server_SOURCES) \ $(ovsdb_ovsdb_tool_SOURCES) $(am__tests_ovstest_SOURCES_DIST) \ $(am__tests_test_dpdkr_SOURCES_DIST) $(tests_test_lib_SOURCES) \ $(tests_test_ovsdb_SOURCES) $(tests_test_strtok_r_SOURCES) \ $(tests_test_type_props_SOURCES) \ $(am__utilities_nlmon_SOURCES_DIST) \ $(utilities_ovs_appctl_SOURCES) \ $(utilities_ovs_benchmark_SOURCES) \ $(utilities_ovs_dpctl_SOURCES) $(utilities_ovs_ofctl_SOURCES) \ $(utilities_ovs_testcontroller_SOURCES) \ $(am__utilities_ovs_vlan_bug_workaround_SOURCES_DIST) \ $(utilities_ovs_vsctl_SOURCES) \ $(vswitchd_ovs_vswitchd_SOURCES) $(vtep_vtep_ctl_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac man1dir = $(mandir)/man1 man5dir = $(mandir)/man5 man7dir = $(mandir)/man7 man8dir = $(mandir)/man8 NROFF = nroff MANS = $(dist_man_MANS) $(man_MANS) DATA = $(dist_pkgdata_DATA) $(dist_scripts_DATA) \ $(nobase_pkgdata_DATA) $(noinst_DATA) $(pkgconfig_DATA) \ $(pkgdata_DATA) $(scripts_DATA) am__noinst_HEADERS_DIST = CONTRIBUTING.md CodingStyle.md DESIGN.md \ FAQ.md INSTALL.md INSTALL.Debian.md INSTALL.Docker.md \ INSTALL.DPDK.md INSTALL.Fedora.md INSTALL.KVM.md \ INSTALL.Libvirt.md INSTALL.NetBSD.md INSTALL.RHEL.md \ INSTALL.SELinux.md INSTALL.SSL.md INSTALL.XenServer.md \ INSTALL.userspace.md INSTALL.Windows.md IntegrationGuide.md \ OPENFLOW-1.1+.md PORTING.md README.md README-lisp.md \ README-native-tunneling.md REPORTING-BUGS.md SECURITY.md \ TODO.md WHY-OVS.md utilities/ovs-command-bashcomp.INSTALL.md \ third-party/README.md tutorial/Tutorial.md \ tutorial/OVN-Tutorial.md vtep/README.ovs-vtep.md NOTICE \ .travis.yml .travis/build.sh .travis/prepare.sh appveyor.yml \ boot.sh build-aux/cccl build-aux/cksum-schema-check \ build-aux/dist-docs build-aux/sodepends.pl \ build-aux/soexpand.pl build-aux/xml2nroff lib/common.man \ lib/common-syn.man lib/coverage-unixctl.man lib/daemon.man \ lib/daemon-syn.man lib/db-ctl-base.man lib/dpctl.man \ lib/memory-unixctl.man lib/ofp-version.man lib/ovs.tmac \ lib/service.man lib/service-syn.man lib/ssl-bootstrap.man \ lib/ssl-bootstrap-syn.man lib/ssl-peer-ca-cert.man \ lib/ssl-peer-ca-cert-syn.man lib/ssl.man lib/ssl-syn.man \ lib/table.man lib/unixctl.man lib/unixctl-syn.man \ lib/vconn-active.man lib/vconn-passive.man \ lib/vlog-unixctl.man lib/vlog-syn.man lib/vlog.man \ ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man \ ofproto/ofproto-tnl-unixctl.man utilities/ovs-vlan-bugs.man \ ovsdb/remote-active.man ovsdb/remote-passive.man \ utilities/ovs-appctl.8.in utilities/ovs-benchmark.1.in \ utilities/ovs-testcontroller.8.in utilities/ovs-ctl.8 \ utilities/ovs-dpctl.8.in utilities/ovs-dpctl-top.8.in \ utilities/ovs-l3ping.8.in utilities/ovs-ofctl.8.in \ utilities/ovs-parse-backtrace.8 utilities/ovs-pcap.1.in \ utilities/ovs-pki.8.in utilities/ovs-tcpundump.1.in \ utilities/ovs-vlan-bug-workaround.8.in utilities/ovs-test.8.in \ utilities/ovs-vlan-test.8.in utilities/ovs-vsctl.8.in \ utilities/bugtool/ovs-bugtool.8.in vswitchd/ovs-vswitchd.8.in \ ovsdb/ovsdb-tool.1.in ovsdb/ovsdb-client.1.in \ ovsdb/ovsdb-server.1.in ovsdb/ovsdb-idlc.1 vtep/vtep-ctl.8.in \ ovn/utilities/ovn-sbctl.8.in Vagrantfile \ build-aux/thread-safety-blacklist \ Documentation/group-selection-method-property.txt \ m4/absolute-header.m4 m4/include_next.m4 lib/stdio.h.in \ lib/string.h.in lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem \ lib/dirs.c.in lib/vswitch-idl.ann build-aux/extract-ofp-fields \ build-aux/extract-ofp-actions build-aux/extract-ofp-errors \ build-aux/extract-ofp-msgs ofproto/ipfix.xml \ ofproto/ipfix-enterprise-entities.def utilities/ovs-sim.in \ utilities/ovs-sim.1.xml utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-check-dead-ifs.in utilities/ovs-ctl.in \ utilities/ovs-dev.py utilities/ovs-docker \ utilities/ovs-dpctl-top.in utilities/ovs-l3ping.in \ utilities/ovs-lib.in utilities/ovs-parse-backtrace.in \ utilities/ovs-pcap.in utilities/ovs-pipegen.py \ utilities/ovs-pki.in utilities/ovs-save \ utilities/ovs-tcpundump.in utilities/ovs-test.in \ utilities/ovs-vlan-test.in utilities/ovs-vsctl-bashcomp.bash \ utilities/qemu-wrap.py \ utilities/bugtool/plugins/kernel-info/openvswitch.xml \ utilities/bugtool/plugins/network-status/openvswitch.xml \ utilities/bugtool/plugins/system-configuration.xml \ utilities/bugtool/plugins/system-logs/openvswitch.xml \ utilities/bugtool/plugins/system-configuration/openvswitch.xml \ utilities/bugtool/ovs-bugtool-bfd-show \ utilities/bugtool/ovs-bugtool-cfm-show \ utilities/bugtool/ovs-bugtool-coverage-show \ utilities/bugtool/ovs-bugtool-fdb-show \ utilities/bugtool/ovs-bugtool-lacp-show \ utilities/bugtool/ovs-bugtool-list-dbs \ utilities/bugtool/ovs-bugtool-memory-show \ utilities/bugtool/ovs-bugtool-tc-class-show \ utilities/bugtool/ovs-bugtool-vsctl-show \ utilities/bugtool/ovs-bugtool-ovsdb-dump \ utilities/bugtool/ovs-bugtool-daemons-ver \ utilities/bugtool/ovs-bugtool-ovs-ofctl-show \ utilities/bugtool/ovs-bugtool-ovs-ofctl-dump-flows \ utilities/bugtool/ovs-bugtool-ovs-appctl-dpif \ utilities/bugtool/ovs-bugtool-bond-show \ utilities/bugtool/ovs-bugtool-conntrack-dump \ utilities/bugtool/ovs-bugtool.in tests/ovsdb-macros.at \ tests/ovs-macros.at tests/ofproto-macros.at tests/testsuite.at \ tests/completion.at tests/library.at tests/heap.at \ tests/bundle.at tests/classifier.at tests/check-structs.at \ tests/daemon.at tests/daemon-py.at tests/ofp-actions.at \ tests/ofp-print.at tests/ofp-util.at tests/ofp-errors.at \ tests/ovs-ofctl.at tests/odp.at tests/mpls-xlate.at \ tests/multipath.at tests/bfd.at tests/cfm.at tests/lacp.at \ tests/lib.at tests/learn.at tests/vconn.at tests/file_name.at \ tests/aes128.at tests/unixctl-py.at tests/uuid.at \ tests/json.at tests/jsonrpc.at tests/jsonrpc-py.at \ tests/tunnel.at tests/tunnel-push-pop.at \ tests/tunnel-push-pop-ipv6.at tests/lockfile.at \ tests/reconnect.at tests/ovs-vswitchd.at tests/dpif-netdev.at \ tests/dpctl.at tests/ofproto-dpif.at tests/bridge.at \ tests/vlan-splinters.at tests/ofproto.at tests/netdev-type.at \ tests/ovsdb.at tests/ovsdb-log.at tests/ovsdb-types.at \ tests/ovsdb-data.at tests/ovsdb-column.at tests/ovsdb-table.at \ tests/ovsdb-row.at tests/ovsdb-schema.at \ tests/ovsdb-condition.at tests/ovsdb-mutation.at \ tests/ovsdb-query.at tests/ovsdb-transaction.at \ tests/ovsdb-execution.at tests/ovsdb-trigger.at \ tests/ovsdb-tool.at tests/ovsdb-server.at \ tests/ovsdb-monitor.at tests/ovsdb-idl.at tests/ovs-vsctl.at \ tests/ovs-monitor-ipsec.at tests/ovs-xapi-sync.at tests/stp.at \ tests/rstp.at tests/interface-reconfigure.at tests/vlog.at \ tests/vtep-ctl.at tests/auto-attach.at tests/ovn.at \ tests/ovn-nbctl.at tests/ovn-sbctl.at tests/ovn-controller.at \ tests/ovn-controller-vtep.at tests/mcast-snooping.at \ tests/system-common-macros.at tests/system-traffic.at \ tests/system-kmod-testsuite.at tests/system-kmod-macros.at \ tests/system-userspace-testsuite.at \ tests/system-userspace-macros.at $(srcdir)/tests/testsuite \ $(srcdir)/tests/system-kmod-testsuite \ $(srcdir)/tests/system-userspace-testsuite tests/atlocal.in \ $(srcdir)/package.m4 $(srcdir)/tests/testsuite.patch \ tests/valgrind-wrapper.in tests/glibc.supp tests/openssl.supp \ tests/run-oftest tests/run-ryu tests/uuidfilt.pl \ tests/ovsdb-monitor-sort.pl tests/idltest.ovsschema \ tests/idltest.ann tests/idltest2.ovsschema tests/appctl.py \ tests/test-daemon.py tests/test-json.py tests/test-jsonrpc.py \ tests/test-l7.py tests/test-ovsdb.py tests/test-reconnect.py \ tests/MockXenAPI.py tests/test-unix-socket.py \ tests/test-unixctl.py tests/test-vlog.py \ build-aux/extract-odp-netlink-h build-aux/check-structs \ third-party/ofp-tcpdump.patch debian/changelog debian/compat \ debian/control debian/control.modules.in debian/copyright \ debian/copyright.in debian/dkms.conf.in debian/dirs \ debian/openvswitch-common.dirs debian/openvswitch-common.docs \ debian/openvswitch-common.install \ debian/openvswitch-common.manpages \ debian/openvswitch-datapath-module-_KVERS_.postinst.modules.in \ debian/openvswitch-datapath-dkms.postinst \ debian/openvswitch-datapath-dkms.prerm \ debian/openvswitch-datapath-source.README.Debian \ debian/openvswitch-datapath-source.copyright \ debian/openvswitch-datapath-source.dirs \ debian/openvswitch-datapath-source.install \ debian/openvswitch-ipsec.dirs debian/openvswitch-ipsec.init \ debian/openvswitch-ipsec.install debian/openvswitch-pki.dirs \ debian/openvswitch-pki.postinst debian/openvswitch-pki.postrm \ debian/openvswitch-switch.README.Debian \ debian/openvswitch-switch.dirs debian/openvswitch-switch.init \ debian/openvswitch-switch.install \ debian/openvswitch-switch.logrotate \ debian/openvswitch-switch.manpages \ debian/openvswitch-switch.postinst \ debian/openvswitch-switch.postrm \ debian/openvswitch-switch.template \ debian/openvswitch-switch.links debian/openvswitch-test.dirs \ debian/openvswitch-test.install \ debian/openvswitch-test.manpages \ debian/openvswitch-testcontroller.README.Debian \ debian/openvswitch-testcontroller.default \ debian/openvswitch-testcontroller.dirs \ debian/openvswitch-testcontroller.init \ debian/openvswitch-testcontroller.install \ debian/openvswitch-testcontroller.manpages \ debian/openvswitch-testcontroller.postinst \ debian/openvswitch-testcontroller.postrm \ debian/openvswitch-vtep.default debian/openvswitch-vtep.dirs \ debian/openvswitch-vtep.init debian/openvswitch-vtep.install \ debian/openvswitch-vtep.manpages debian/ovs-monitor-ipsec \ debian/python-openvswitch.dirs \ debian/python-openvswitch.install debian/rules \ debian/rules.modules debian/ifupdown.sh debian/source/format \ vswitchd/INTERNALS vswitchd/vswitch.ovsschema \ vswitchd/vswitch.xml ovsdb/ovsdb-idlc.in ovsdb/ovsdb-doc \ ovsdb/ovsdb-dot.in ovsdb/dot2pic rhel/README.RHEL \ rhel/automake.mk rhel/etc_init.d_openvswitch \ rhel/etc_logrotate.d_openvswitch \ rhel/etc_sysconfig_network-scripts_ifdown-ovs \ rhel/etc_sysconfig_network-scripts_ifup-ovs \ rhel/openvswitch-dkms.spec rhel/openvswitch-dkms.spec.in \ rhel/openvswitch-kmod-rhel6.spec \ rhel/openvswitch-kmod-rhel6.spec.in \ rhel/openvswitch-kmod.files rhel/openvswitch-kmod-fedora.spec \ rhel/openvswitch-kmod-fedora.spec.in rhel/openvswitch.spec \ rhel/openvswitch.spec.in rhel/openvswitch-fedora.spec \ rhel/openvswitch-fedora.spec.in \ rhel/usr_share_openvswitch_scripts_sysconfig.template \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ rhel/usr_lib_systemd_system_openvswitch.service \ rhel/usr_lib_systemd_system_ovsdb-server.service \ rhel/usr_lib_systemd_system_ovs-vswitchd.service \ rhel/usr_lib_systemd_system_ovn-controller.service \ rhel/usr_lib_systemd_system_ovn-controller-vtep.service \ rhel/usr_lib_systemd_system_ovn-northd.service xenserver/GPLv2 \ xenserver/LICENSE xenserver/README xenserver/automake.mk \ xenserver/etc_init.d_openvswitch \ xenserver/etc_init.d_openvswitch-xapi-update \ xenserver/etc_logrotate.d_openvswitch \ xenserver/etc_profile.d_openvswitch.sh \ xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \ xenserver/etc_xensource_scripts_vif \ xenserver/openvswitch-xen.spec \ xenserver/openvswitch-xen.spec.in \ xenserver/opt_xensource_libexec_InterfaceReconfigure.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \ xenserver/opt_xensource_libexec_interface-reconfigure \ xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \ xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \ xenserver/usr_share_openvswitch_scripts_sysconfig.template \ python/build/__init__.py python/build/nroff.py \ python/README.rst python/setup.py python/ovs/__init__.py \ python/ovs/daemon.py python/ovs/db/__init__.py \ python/ovs/db/data.py python/ovs/db/error.py \ python/ovs/db/idl.py python/ovs/db/parser.py \ python/ovs/db/schema.py python/ovs/db/types.py \ python/ovs/fatal_signal.py python/ovs/json.py \ python/ovs/jsonrpc.py python/ovs/ovsuuid.py \ python/ovs/poller.py python/ovs/process.py \ python/ovs/reconnect.py python/ovs/socket_util.py \ python/ovs/stream.py python/ovs/timeval.py \ python/ovs/unixctl/__init__.py python/ovs/unixctl/client.py \ python/ovs/unixctl/server.py python/ovs/util.py \ python/ovs/version.py python/ovs/vlog.py python/ovs/dirs.py \ python/ovstest/__init__.py python/ovstest/args.py \ python/ovstest/rpcserver.py python/ovstest/tcp.py \ python/ovstest/tests.py python/ovstest/udp.py \ python/ovstest/util.py python/ovstest/vswitch.py \ python/ovs/dirs.py.template tutorial/ovs-sandbox \ tutorial/t-setup tutorial/t-stage0 tutorial/t-stage1 \ tutorial/t-stage2 tutorial/t-stage3 tutorial/t-stage4 \ tutorial/ovn/env1/setup.sh tutorial/ovn/env1/packet1.sh \ tutorial/ovn/env1/packet2.sh \ tutorial/ovn/env1/add-third-port.sh tutorial/ovn/env2/setup.sh \ tutorial/ovn/env2/packet1.sh tutorial/ovn/env2/packet2.sh \ tutorial/ovn/env3/setup.sh tutorial/ovn/env3/packet1.sh \ tutorial/ovn/env3/packet2.sh tutorial/ovn/env4/setup1.sh \ tutorial/ovn/env4/setup2.sh tutorial/ovn/env4/packet1.sh \ tutorial/ovn/env4/packet2.sh tutorial/ovn/env4/packet3.sh \ tutorial/ovn/env4/packet4.sh tutorial/ovn/env4/packet5.sh \ tutorial/ovn/env5/setup.sh tutorial/ovn/env5/packet1.sh \ tutorial/ovn/env5/packet2.sh tutorial/ovn/env6/setup.sh \ tutorial/ovn/env6/add-acls.sh vtep/vtep-idl.ann vtep/ovs-vtep \ vtep/vtep.ovsschema vtep/vtep.xml datapath-windows/CodingStyle \ datapath-windows/DESIGN \ datapath-windows/Package/package.VcxProj \ datapath-windows/Package/package.VcxProj.user \ datapath-windows/include/OvsDpInterfaceExt.h \ datapath-windows/misc/OVS.psm1 \ datapath-windows/misc/install.cmd \ datapath-windows/misc/uninstall.cmd \ datapath-windows/ovsext.sln datapath-windows/ovsext/Actions.c \ datapath-windows/ovsext/Atomic.h \ datapath-windows/ovsext/BufferMgmt.c \ datapath-windows/ovsext/BufferMgmt.h \ datapath-windows/ovsext/Checksum.c \ datapath-windows/ovsext/Checksum.h \ datapath-windows/ovsext/Datapath.c \ datapath-windows/ovsext/Datapath.h \ datapath-windows/ovsext/Debug.c \ datapath-windows/ovsext/Debug.h \ datapath-windows/ovsext/DpInternal.h \ datapath-windows/ovsext/Driver.c \ datapath-windows/ovsext/Ethernet.h \ datapath-windows/ovsext/Event.c \ datapath-windows/ovsext/Event.h datapath-windows/ovsext/Flow.c \ datapath-windows/ovsext/Flow.h datapath-windows/ovsext/Gre.h \ datapath-windows/ovsext/Gre.c \ datapath-windows/ovsext/IpHelper.c \ datapath-windows/ovsext/IpHelper.h \ datapath-windows/ovsext/Jhash.c \ datapath-windows/ovsext/Jhash.h \ datapath-windows/ovsext/NetProto.h \ datapath-windows/ovsext/Netlink/Netlink.c \ datapath-windows/ovsext/Netlink/Netlink.h \ datapath-windows/ovsext/Netlink/NetlinkBuf.c \ datapath-windows/ovsext/Netlink/NetlinkBuf.h \ datapath-windows/ovsext/Netlink/NetlinkError.h \ datapath-windows/ovsext/Netlink/NetlinkProto.h \ datapath-windows/ovsext/Oid.c datapath-windows/ovsext/Oid.h \ datapath-windows/ovsext/PacketIO.c \ datapath-windows/ovsext/PacketIO.h \ datapath-windows/ovsext/PacketParser.c \ datapath-windows/ovsext/PacketParser.h \ datapath-windows/ovsext/Stt.c datapath-windows/ovsext/Stt.h \ datapath-windows/ovsext/Switch.c \ datapath-windows/ovsext/Switch.h \ datapath-windows/ovsext/Tunnel.c \ datapath-windows/ovsext/Tunnel.h \ datapath-windows/ovsext/TunnelFilter.c \ datapath-windows/ovsext/TunnelIntf.h \ datapath-windows/ovsext/Types.h datapath-windows/ovsext/User.c \ datapath-windows/ovsext/User.h datapath-windows/ovsext/Util.c \ datapath-windows/ovsext/Util.h datapath-windows/ovsext/Vport.c \ datapath-windows/ovsext/Vport.h \ datapath-windows/ovsext/Vxlan.c \ datapath-windows/ovsext/Vxlan.h \ datapath-windows/ovsext/ovsext.inf \ datapath-windows/ovsext/ovsext.rc \ datapath-windows/ovsext/ovsext.vcxproj \ datapath-windows/ovsext/ovsext.vcxproj.user \ datapath-windows/ovsext/precomp.h \ datapath-windows/ovsext/precompsrc.c \ datapath-windows/ovsext/resource.h \ $(srcdir)/build-aux/extract-odp-netlink-windows-dp-h \ windows/.gitignore windows/automake.mk windows/README.rst \ windows/ovs-windows-installer.sln \ windows/ovs-windows-installer/Actions/OVSActions.js \ windows/ovs-windows-installer/CustomActions.wxs \ windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs \ windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs \ windows/ovs-windows-installer/License.rtf \ windows/ovs-windows-installer/Product.wxs \ windows/ovs-windows-installer/UI.wxs \ windows/ovs-windows-installer/images/bannrbmp.bmp \ windows/ovs-windows-installer/images/dlgbmp.bmp \ windows/ovs-windows-installer/ovs-windows-installer.wixproj \ ovn/ovn-sb.ovsschema ovn/ovn-sb.xml ovn/ovn-nb.ovsschema \ ovn/ovn-nb.xml ovn/ovn-architecture.7.xml ovn/TODO \ ovn/CONTAINERS.OpenStack.md ovn/OVN-GW-HA.md \ ovn/controller/ovn-controller.8.xml \ ovn/controller-vtep/ovn-controller-vtep.8.xml \ ovn/lib/ovn-sb-idl.ann ovn/lib/ovn-nb-idl.ann \ ovn/northd/ovn-northd.8.xml ovn/utilities/ovn-ctl \ ovn/utilities/ovn-ctl.8.xml \ ovn/utilities/ovn-docker-overlay-driver \ ovn/utilities/ovn-docker-underlay-driver \ ovn/utilities/ovn-nbctl.8.xml selinux/openvswitch-custom.te \ include/sparse/arpa/inet.h include/sparse/assert.h \ include/sparse/bmi2intrin.h include/sparse/emmintrin.h \ include/sparse/math.h include/sparse/netinet/in.h \ include/sparse/netinet/ip6.h include/sparse/netpacket/packet.h \ include/sparse/pthread.h include/sparse/rte_atomic.h \ include/sparse/rte_lcore.h include/sparse/rte_vect.h \ include/sparse/sys/socket.h include/sparse/sys/wait.h \ include/windows/arpa/inet.h include/windows/dirent.h \ include/windows/getopt.h include/windows/linux/pkt_sched.h \ include/windows/linux/types.h include/windows/net/if.h \ include/windows/netdb.h include/windows/netpacket/packet.h \ include/windows/netinet/icmp6.h include/windows/netinet/in.h \ include/windows/netinet/in_systm.h \ include/windows/netinet/ip.h include/windows/netinet/ip6.h \ include/windows/netinet/tcp.h include/windows/poll.h \ include/windows/strings.h include/windows/syslog.h \ include/windows/sys/epoll.h include/windows/sys/ioctl.h \ include/windows/sys/resource.h include/windows/sys/socket.h \ include/windows/sys/time.h include/windows/sys/uio.h \ include/windows/sys/un.h include/windows/sys/wait.h \ include/windows/unistd.h include/windows/windefs.h HEADERS = $(noinst_HEADERS) $(openflowinclude_HEADERS) \ $(openvswitchinclude_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Documentation/automake.mk \ $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(srcdir)/datapath-windows/automake.mk \ $(srcdir)/datapath-windows/include/automake.mk \ $(srcdir)/debian/automake.mk $(srcdir)/include/automake.mk \ $(srcdir)/include/openflow/automake.mk \ $(srcdir)/include/openvswitch/automake.mk \ $(srcdir)/include/sparse/automake.mk \ $(srcdir)/include/windows/automake.mk \ $(srcdir)/lib/automake.mk $(srcdir)/m4/automake.mk \ $(srcdir)/manpages.mk $(srcdir)/ofproto/automake.mk \ $(srcdir)/ovn/automake.mk \ $(srcdir)/ovn/controller-vtep/automake.mk \ $(srcdir)/ovn/controller/automake.mk \ $(srcdir)/ovn/lib/automake.mk $(srcdir)/ovn/northd/automake.mk \ $(srcdir)/ovn/utilities/automake.mk \ $(srcdir)/ovsdb/automake.mk $(srcdir)/python/automake.mk \ $(srcdir)/rhel/automake.mk $(srcdir)/selinux/automake.mk \ $(srcdir)/tests/automake.mk $(srcdir)/third-party/automake.mk \ $(srcdir)/tutorial/automake.mk $(srcdir)/utilities/automake.mk \ $(srcdir)/utilities/bugtool/automake.mk \ $(srcdir)/vswitchd/automake.mk $(srcdir)/vtep/automake.mk \ $(srcdir)/windows/automake.mk $(srcdir)/xenserver/automake.mk \ $(top_srcdir)/build-aux/compile \ $(top_srcdir)/build-aux/config.guess \ $(top_srcdir)/build-aux/config.sub \ $(top_srcdir)/build-aux/depcomp \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/ltmain.sh \ $(top_srcdir)/build-aux/missing \ $(top_srcdir)/datapath/linux/Kbuild.in \ $(top_srcdir)/datapath/linux/Makefile.in \ $(top_srcdir)/datapath/linux/Makefile.main.in \ $(top_srcdir)/include/openvswitch/version.h.in \ $(top_srcdir)/lib/libopenvswitch.pc.in \ $(top_srcdir)/lib/libopenvswitch.sym.in \ $(top_srcdir)/lib/libsflow.pc.in \ $(top_srcdir)/lib/libsflow.sym.in $(top_srcdir)/lib/stdio.h.in \ $(top_srcdir)/lib/string.h.in \ $(top_srcdir)/ofproto/libofproto.pc.in \ $(top_srcdir)/ofproto/libofproto.sym.in \ $(top_srcdir)/ovn/lib/libovn.sym.in \ $(top_srcdir)/ovsdb/libovsdb.pc.in \ $(top_srcdir)/ovsdb/libovsdb.sym.in \ $(top_srcdir)/tests/atlocal.in \ $(top_srcdir)/vtep/libvtep.sym.in AUTHORS COPYING NEWS \ build-aux/compile build-aux/config.guess build-aux/config.sub \ build-aux/depcomp build-aux/install-sh build-aux/ltmain.sh \ build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOM4TE = @AUTOM4TE@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CAPNG_LDADD = @CAPNG_LDADD@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CGCCFLAGS = @CGCCFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DBDIR = @DBDIR@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DPDK_vswitchd_LDFLAGS = @DPDK_vswitchd_LDFLAGS@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ HAVE_LIBCAPNG = @HAVE_LIBCAPNG@ HAVE_OPENSSL = @HAVE_OPENSSL@ HAVE_PYTHON = @HAVE_PYTHON@ INCLUDE_NEXT = @INCLUDE_NEXT@ INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KARCH = @KARCH@ KBUILD = @KBUILD@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LOGDIR = @LOGDIR@ LTLIBOBJS = @LTLIBOBJS@ LT_AGE = @LT_AGE@ LT_CURRENT = @LT_CURRENT@ LT_REVISION = @LT_REVISION@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MSVC64_LDFLAGS = @MSVC64_LDFLAGS@ MSVC_CFLAGS = @MSVC_CFLAGS@ NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ NEXT_STDIO_H = @NEXT_STDIO_H@ NEXT_STRING_H = @NEXT_STRING_H@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ OVS_CFLAGS = @OVS_CFLAGS@ OVS_LDFLAGS = @OVS_LDFLAGS@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PKG_CONFIG = @PKG_CONFIG@ PKIDIR = @PKIDIR@ PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ PTHREAD_INCLUDES = @PTHREAD_INCLUDES@ PTHREAD_LDFLAGS = @PTHREAD_LDFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PTHREAD_WIN32_DIR_DLL = @PTHREAD_WIN32_DIR_DLL@ PTHREAD_WIN32_DIR_DLL_WIN_FORM = @PTHREAD_WIN32_DIR_DLL_WIN_FORM@ PYTHON = @PYTHON@ RANLIB = @RANLIB@ RUNDIR = @RUNDIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SPARSE = @SPARSE@ SPARSEFLAGS = @SPARSEFLAGS@ SPARSE_EXTRA_INCLUDES = @SPARSE_EXTRA_INCLUDES@ SSL_INCLUDES = @SSL_INCLUDES@ SSL_LDFLAGS = @SSL_LDFLAGS@ SSL_LIBS = @SSL_LIBS@ STRIP = @STRIP@ VERSION = @VERSION@ VSTUDIO_CONFIG = @VSTUDIO_CONFIG@ WARNING_FLAGS = @WARNING_FLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 SUBDIRS = datapath AM_CPPFLAGS = $(SSL_CFLAGS) $(am__append_1) -I $(top_srcdir)/include \ -I $(top_builddir)/include -I $(top_srcdir)/lib -I \ $(top_builddir)/lib $(SSL_INCLUDES) $(am__append_4) AM_LDFLAGS = $(SSL_LDFLAGS) $(OVS_LDFLAGS) $(am__append_2) AM_CFLAGS = -Wstrict-prototypes $(WARNING_FLAGS) $(OVS_CFLAGS) \ $(am__append_3) $(am__append_5) @WIN32_FALSE@psep = ":" @WIN32_TRUE@psep = ";" # PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo # files. Creating .py[co] works OK for any given version of Open # vSwitch, but it causes trouble if you switch from a version with # foo/__init__.py into an (older) version with plain foo.py, since # foo/__init__.pyc will cause Python to ignore foo.py. run_python = \ PYTHONPATH=$(top_srcdir)/python$(psep)$$PYTHONPATH \ PYTHONDONTWRITEBYTECODE=yes $(PYTHON) # Check that every .c file includes . # Check for printf() type modifiers that MSVC doesn't support. # Check that certain data structures are always declared "static". # Check that assert.h is not used outside a whitelist of files. # Check that LITTLE_ENDIAN and BIG_ENDIAN are not used unless BYTE_ORDER is # also mentioned. ( always defines the former two constants. They # must be compared to BYTE_ORDER to get the machine's correct endianness. But # it is better to use WORDS_BIGENDIAN.) # Version checking for vswitch.ovsschema. # Version checking for vtep.ovsschema. # Version checking for ovn-nb.ovsschema. # Version checking for ovn-sb.ovsschema. ALL_LOCAL = $(am__append_6) config-h-check printf-check static-check \ check-assert-h-usage check-endian thread-safety-check \ $(am__append_8) $(am__append_10) $(am__append_47) \ check-debian-changelog-version \ vswitchd/vswitch.ovsschema.stamp \ $(srcdir)/python/ovs/version.py $(srcdir)/python/ovs/dirs.py \ vtep/vtep.ovsschema.stamp ovn/ovn-nb.ovsschema.stamp \ ovn/ovn-sb.ovsschema.stamp BUILT_SOURCES = ofproto/ipfix-entities.def include/odp-netlink.h \ $(OVSIDL_BUILT) $(am__append_50) # Clean up generated files from older OVS versions. (This is important so that # #include "vswitch-idl.h" doesn't get the wrong copy.) CLEANFILES = $(am__append_7) $(am__append_9) manpage-dep-check \ $(nodist_lib_libopenvswitch_la_SOURCES) lib/meta-flow.inc \ lib/nx-match.inc lib/ofp-actions.inc1 lib/ofp-actions.inc2 \ lib/ofp-errors.inc lib/ofp-msgs.inc ofproto/ipfix-entities.def \ $(am__append_32) $(valgrind_wrappers) $(am__append_43) \ include/odp-netlink.h $(am__append_46) \ vswitchd/vswitch.ovsschema.stamp vswitchd/vswitch-idl.c \ vswitchd/vswitch-idl.h $(OVSIDL_BUILT) \ vtep/vtep.ovsschema.stamp \ $(srcdir)/datapath-windows/include/OvsDpInterface.h \ ovn/ovn-nb.ovsschema.stamp ovn/ovn-sb.ovsschema.stamp CLEAN_LOCAL = clean-pycov $(am__append_11) $(am__append_44) DISTCLEANFILES = utilities/ovs-appctl.8 utilities/ovs-ctl \ utilities/ovs-benchmark.1 utilities/ovs-check-dead-ifs \ utilities/ovs-testcontroller.8 utilities/ovs-dpctl.8 \ utilities/ovs-dpctl-top utilities/ovs-dpctl-top.8 \ utilities/ovs-l3ping utilities/ovs-l3ping.8 utilities/ovs-lib \ utilities/ovs-ofctl.8 utilities/ovs-parse-backtrace \ utilities/ovs-pcap utilities/ovs-pcap.1 utilities/ovs-pki \ utilities/ovs-pki.8 utilities/ovs-sim utilities/ovs-sim.1 \ utilities/ovs-tcpundump utilities/ovs-tcpundump.1 \ utilities/ovs-test utilities/ovs-test.8 \ utilities/ovs-vlan-test utilities/ovs-vlan-test.8 \ utilities/ovs-vlan-bug-workaround.8 utilities/ovs-vsctl.8 \ $(am__append_35) tests/atconfig tests/atlocal debian/copyright \ vswitchd/ovs-vswitchd.8 $(am__append_48) \ vswitchd/ovs-vswitchd.conf.db.5 ovsdb/ovsdb-tool.1 \ ovsdb/ovsdb-client.1 ovsdb/ovsdb-server.1 ovsdb/ovsdb-idlc \ ovsdb/ovsdb-dot vtep/vtep-ctl.8 $(am__append_49) vtep/vtep.5 \ $(am__append_51) ovn/ovn-sb.5 $(am__append_52) ovn/ovn-nb.5 \ ovn/ovn-architecture.7 ovn/controller/ovn-controller.8 \ ovn/controller-vtep/ovn-controller-vtep.8 \ ovn/northd/ovn-northd.8 ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 ovn/utilities/ovn-sbctl.8 PYCOV_CLEAN_FILES = build-aux/check-structs,cover \ $(CHECK_PYFILES:.py=.py,cover) .coverage \ $(PYFILES:.py=.py,cover) # A list of Markdown-formatted documentation that will automatically be # included in the "make dist-docs" output. docs = CONTRIBUTING.md CodingStyle.md DESIGN.md FAQ.md INSTALL.md \ INSTALL.Debian.md INSTALL.Docker.md INSTALL.DPDK.md \ INSTALL.Fedora.md INSTALL.KVM.md INSTALL.Libvirt.md \ INSTALL.NetBSD.md INSTALL.RHEL.md INSTALL.SELinux.md \ INSTALL.SSL.md INSTALL.XenServer.md INSTALL.userspace.md \ INSTALL.Windows.md IntegrationGuide.md OPENFLOW-1.1+.md \ PORTING.md README.md README-lisp.md README-native-tunneling.md \ REPORTING-BUGS.md SECURITY.md TODO.md WHY-OVS.md \ utilities/ovs-command-bashcomp.INSTALL.md \ third-party/README.md tutorial/Tutorial.md \ tutorial/OVN-Tutorial.md vtep/README.ovs-vtep.md # IPFIX entity definition macros generation from IANA's XML definition. # IPFIX enterprise entity definition macros. # vswitch schema and IDL # vswitch schema documentation # ovsdb-doc # ovsdb-dot # These python files are used at build time but not runtime, # so they are not installed. # PyPI support. # VTEP schema and IDL # VTEP schema documentation # OVN southbound schema and IDL # OVN southbound schema documentation # OVN northbound schema and IDL # OVN northbound schema documentation EXTRA_DIST = $(docs) NOTICE .travis.yml .travis/build.sh \ .travis/prepare.sh appveyor.yml boot.sh build-aux/cccl \ build-aux/cksum-schema-check build-aux/dist-docs \ build-aux/sodepends.pl build-aux/soexpand.pl \ build-aux/xml2nroff $(MAN_FRAGMENTS) $(MAN_ROOTS) Vagrantfile \ build-aux/thread-safety-blacklist \ Documentation/group-selection-method-property.txt \ m4/absolute-header.m4 m4/include_next.m4 lib/stdio.h.in \ lib/string.h.in lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem \ lib/dirs.c.in lib/vswitch-idl.ann build-aux/extract-ofp-fields \ build-aux/extract-ofp-actions build-aux/extract-ofp-errors \ build-aux/extract-ofp-msgs ofproto/ipfix.xml \ ofproto/ipfix-enterprise-entities.def utilities/ovs-sim.in \ utilities/ovs-sim.1.xml utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-command-bashcomp.INSTALL.md utilities/ovs-ctl.in \ utilities/ovs-dev.py utilities/ovs-docker \ utilities/ovs-dpctl-top.in utilities/ovs-l3ping.in \ utilities/ovs-lib.in utilities/ovs-parse-backtrace.in \ utilities/ovs-pcap.in utilities/ovs-pipegen.py \ utilities/ovs-pki.in utilities/ovs-save \ utilities/ovs-tcpundump.in utilities/ovs-test.in \ utilities/ovs-vlan-test.in utilities/ovs-vsctl-bashcomp.bash \ utilities/qemu-wrap.py $(bugtool_plugins) $(bugtool_scripts) \ utilities/bugtool/ovs-bugtool.in $(COMMON_MACROS_AT) \ $(TESTSUITE_AT) $(SYSTEM_TESTSUITE_AT) \ $(SYSTEM_KMOD_TESTSUITE_AT) $(SYSTEM_USERSPACE_TESTSUITE_AT) \ $(TESTSUITE) $(SYSTEM_KMOD_TESTSUITE) \ $(SYSTEM_USERSPACE_TESTSUITE) tests/atlocal.in \ $(srcdir)/package.m4 $(srcdir)/tests/testsuite \ $(srcdir)/tests/testsuite.patch tests/valgrind-wrapper.in \ tests/glibc.supp tests/openssl.supp tests/run-oftest \ tests/run-ryu tests/uuidfilt.pl tests/ovsdb-monitor-sort.pl \ $(IDLTEST_IDL_FILES) tests/idltest2.ovsschema $(CHECK_PYFILES) \ build-aux/extract-odp-netlink-h build-aux/check-structs \ third-party/ofp-tcpdump.patch debian/changelog debian/compat \ debian/control debian/control.modules.in debian/copyright \ debian/copyright.in debian/dkms.conf.in debian/dirs \ debian/openvswitch-common.dirs debian/openvswitch-common.docs \ debian/openvswitch-common.install \ debian/openvswitch-common.manpages \ debian/openvswitch-datapath-module-_KVERS_.postinst.modules.in \ debian/openvswitch-datapath-dkms.postinst \ debian/openvswitch-datapath-dkms.prerm \ debian/openvswitch-datapath-source.README.Debian \ debian/openvswitch-datapath-source.copyright \ debian/openvswitch-datapath-source.dirs \ debian/openvswitch-datapath-source.install \ debian/openvswitch-ipsec.dirs debian/openvswitch-ipsec.init \ debian/openvswitch-ipsec.install debian/openvswitch-pki.dirs \ debian/openvswitch-pki.postinst debian/openvswitch-pki.postrm \ debian/openvswitch-switch.README.Debian \ debian/openvswitch-switch.dirs debian/openvswitch-switch.init \ debian/openvswitch-switch.install \ debian/openvswitch-switch.logrotate \ debian/openvswitch-switch.manpages \ debian/openvswitch-switch.postinst \ debian/openvswitch-switch.postrm \ debian/openvswitch-switch.template \ debian/openvswitch-switch.links debian/openvswitch-test.dirs \ debian/openvswitch-test.install \ debian/openvswitch-test.manpages \ debian/openvswitch-testcontroller.README.Debian \ debian/openvswitch-testcontroller.default \ debian/openvswitch-testcontroller.dirs \ debian/openvswitch-testcontroller.init \ debian/openvswitch-testcontroller.install \ debian/openvswitch-testcontroller.manpages \ debian/openvswitch-testcontroller.postinst \ debian/openvswitch-testcontroller.postrm \ debian/openvswitch-vtep.default debian/openvswitch-vtep.dirs \ debian/openvswitch-vtep.init debian/openvswitch-vtep.install \ debian/openvswitch-vtep.manpages debian/ovs-monitor-ipsec \ debian/python-openvswitch.dirs \ debian/python-openvswitch.install debian/rules \ debian/rules.modules debian/ifupdown.sh debian/source/format \ vswitchd/INTERNALS vswitchd/vswitch.ovsschema \ vswitchd/vswitch.xml ovsdb/ovsdb-idlc.in ovsdb/ovsdb-doc \ ovsdb/ovsdb-dot.in ovsdb/dot2pic rhel/README.RHEL \ rhel/automake.mk rhel/etc_init.d_openvswitch \ rhel/etc_logrotate.d_openvswitch \ rhel/etc_sysconfig_network-scripts_ifdown-ovs \ rhel/etc_sysconfig_network-scripts_ifup-ovs \ rhel/openvswitch-dkms.spec rhel/openvswitch-dkms.spec.in \ rhel/openvswitch-kmod-rhel6.spec \ rhel/openvswitch-kmod-rhel6.spec.in \ rhel/openvswitch-kmod.files rhel/openvswitch-kmod-fedora.spec \ rhel/openvswitch-kmod-fedora.spec.in rhel/openvswitch.spec \ rhel/openvswitch.spec.in rhel/openvswitch-fedora.spec \ rhel/openvswitch-fedora.spec.in \ rhel/usr_share_openvswitch_scripts_sysconfig.template \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ rhel/usr_lib_systemd_system_openvswitch.service \ rhel/usr_lib_systemd_system_ovsdb-server.service \ rhel/usr_lib_systemd_system_ovs-vswitchd.service \ rhel/usr_lib_systemd_system_ovn-controller.service \ rhel/usr_lib_systemd_system_ovn-controller-vtep.service \ rhel/usr_lib_systemd_system_ovn-northd.service xenserver/GPLv2 \ xenserver/LICENSE xenserver/README xenserver/automake.mk \ xenserver/etc_init.d_openvswitch \ xenserver/etc_init.d_openvswitch-xapi-update \ xenserver/etc_logrotate.d_openvswitch \ xenserver/etc_profile.d_openvswitch.sh \ xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \ xenserver/etc_xensource_scripts_vif \ xenserver/openvswitch-xen.spec \ xenserver/openvswitch-xen.spec.in \ xenserver/opt_xensource_libexec_InterfaceReconfigure.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \ xenserver/opt_xensource_libexec_interface-reconfigure \ xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \ xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \ xenserver/usr_share_openvswitch_scripts_sysconfig.template \ python/build/__init__.py python/build/nroff.py \ python/README.rst python/setup.py $(PYFILES) \ python/ovs/dirs.py.template tutorial/ovs-sandbox \ tutorial/t-setup tutorial/t-stage0 tutorial/t-stage1 \ tutorial/t-stage2 tutorial/t-stage3 tutorial/t-stage4 \ tutorial/ovn/env1/setup.sh tutorial/ovn/env1/packet1.sh \ tutorial/ovn/env1/packet2.sh \ tutorial/ovn/env1/add-third-port.sh tutorial/ovn/env2/setup.sh \ tutorial/ovn/env2/packet1.sh tutorial/ovn/env2/packet2.sh \ tutorial/ovn/env3/setup.sh tutorial/ovn/env3/packet1.sh \ tutorial/ovn/env3/packet2.sh tutorial/ovn/env4/setup1.sh \ tutorial/ovn/env4/setup2.sh tutorial/ovn/env4/packet1.sh \ tutorial/ovn/env4/packet2.sh tutorial/ovn/env4/packet3.sh \ tutorial/ovn/env4/packet4.sh tutorial/ovn/env4/packet5.sh \ tutorial/ovn/env5/setup.sh tutorial/ovn/env5/packet1.sh \ tutorial/ovn/env5/packet2.sh tutorial/ovn/env6/setup.sh \ tutorial/ovn/env6/add-acls.sh vtep/vtep-idl.ann vtep/ovs-vtep \ vtep/vtep.ovsschema vtep/vtep.xml datapath-windows/CodingStyle \ datapath-windows/DESIGN \ datapath-windows/Package/package.VcxProj \ datapath-windows/Package/package.VcxProj.user \ datapath-windows/include/OvsDpInterfaceExt.h \ datapath-windows/misc/OVS.psm1 \ datapath-windows/misc/install.cmd \ datapath-windows/misc/uninstall.cmd \ datapath-windows/ovsext.sln datapath-windows/ovsext/Actions.c \ datapath-windows/ovsext/Atomic.h \ datapath-windows/ovsext/BufferMgmt.c \ datapath-windows/ovsext/BufferMgmt.h \ datapath-windows/ovsext/Checksum.c \ datapath-windows/ovsext/Checksum.h \ datapath-windows/ovsext/Datapath.c \ datapath-windows/ovsext/Datapath.h \ datapath-windows/ovsext/Debug.c \ datapath-windows/ovsext/Debug.h \ datapath-windows/ovsext/DpInternal.h \ datapath-windows/ovsext/Driver.c \ datapath-windows/ovsext/Ethernet.h \ datapath-windows/ovsext/Event.c \ datapath-windows/ovsext/Event.h datapath-windows/ovsext/Flow.c \ datapath-windows/ovsext/Flow.h datapath-windows/ovsext/Gre.h \ datapath-windows/ovsext/Gre.c \ datapath-windows/ovsext/IpHelper.c \ datapath-windows/ovsext/IpHelper.h \ datapath-windows/ovsext/Jhash.c \ datapath-windows/ovsext/Jhash.h \ datapath-windows/ovsext/NetProto.h \ datapath-windows/ovsext/Netlink/Netlink.c \ datapath-windows/ovsext/Netlink/Netlink.h \ datapath-windows/ovsext/Netlink/NetlinkBuf.c \ datapath-windows/ovsext/Netlink/NetlinkBuf.h \ datapath-windows/ovsext/Netlink/NetlinkError.h \ datapath-windows/ovsext/Netlink/NetlinkProto.h \ datapath-windows/ovsext/Oid.c datapath-windows/ovsext/Oid.h \ datapath-windows/ovsext/PacketIO.c \ datapath-windows/ovsext/PacketIO.h \ datapath-windows/ovsext/PacketParser.c \ datapath-windows/ovsext/PacketParser.h \ datapath-windows/ovsext/Stt.c datapath-windows/ovsext/Stt.h \ datapath-windows/ovsext/Switch.c \ datapath-windows/ovsext/Switch.h \ datapath-windows/ovsext/Tunnel.c \ datapath-windows/ovsext/Tunnel.h \ datapath-windows/ovsext/TunnelFilter.c \ datapath-windows/ovsext/TunnelIntf.h \ datapath-windows/ovsext/Types.h datapath-windows/ovsext/User.c \ datapath-windows/ovsext/User.h datapath-windows/ovsext/Util.c \ datapath-windows/ovsext/Util.h datapath-windows/ovsext/Vport.c \ datapath-windows/ovsext/Vport.h \ datapath-windows/ovsext/Vxlan.c \ datapath-windows/ovsext/Vxlan.h \ datapath-windows/ovsext/ovsext.inf \ datapath-windows/ovsext/ovsext.rc \ datapath-windows/ovsext/ovsext.vcxproj \ datapath-windows/ovsext/ovsext.vcxproj.user \ datapath-windows/ovsext/precomp.h \ datapath-windows/ovsext/precompsrc.c \ datapath-windows/ovsext/resource.h \ $(srcdir)/build-aux/extract-odp-netlink-windows-dp-h \ windows/.gitignore windows/automake.mk windows/README.rst \ windows/ovs-windows-installer.sln \ windows/ovs-windows-installer/Actions/OVSActions.js \ windows/ovs-windows-installer/CustomActions.wxs \ windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs \ windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs \ windows/ovs-windows-installer/License.rtf \ windows/ovs-windows-installer/Product.wxs \ windows/ovs-windows-installer/UI.wxs \ windows/ovs-windows-installer/images/bannrbmp.bmp \ windows/ovs-windows-installer/images/dlgbmp.bmp \ windows/ovs-windows-installer/ovs-windows-installer.wixproj \ ovn/ovn-sb.ovsschema ovn/ovn-sb.xml ovn/ovn-nb.ovsschema \ ovn/ovn-nb.xml ovn/ovn-architecture.7.xml ovn/TODO \ ovn/CONTAINERS.OpenStack.md ovn/OVN-GW-HA.md \ ovn/controller/ovn-controller.8.xml \ ovn/controller-vtep/ovn-controller-vtep.8.xml \ ovn/lib/ovn-sb-idl.ann ovn/lib/ovn-nb-idl.ann \ ovn/northd/ovn-northd.8.xml ovn/utilities/ovn-ctl \ ovn/utilities/ovn-ctl.8.xml \ ovn/utilities/ovn-docker-overlay-driver \ ovn/utilities/ovn-docker-underlay-driver \ ovn/utilities/ovn-nbctl.8.xml selinux/openvswitch-custom.te # Docker drivers bin_SCRIPTS = utilities/ovs-docker utilities/ovs-pki $(am__append_28) \ ovn/utilities/ovn-docker-overlay-driver \ ovn/utilities/ovn-docker-underlay-driver DIST_HOOKS = check-debian-changelog-version dist_man_MANS = dist_pkgdata_DATA = dist_pkgdata_SCRIPTS = dist_sbin_SCRIPTS = dist_scripts_SCRIPTS = dist_scripts_DATA = INSTALL_DATA_LOCAL = lib-install-data-local $(am__append_37) UNINSTALL_LOCAL = $(am__append_38) ovs-uninstall-local # ovsdb-tool.1 # ovsdb-client.1 # ovsdb-server.1 man_MANS = utilities/ovs-appctl.8 utilities/ovs-benchmark.1 \ utilities/ovs-ctl.8 utilities/ovs-testcontroller.8 \ utilities/ovs-dpctl.8 utilities/ovs-dpctl-top.8 \ utilities/ovs-l3ping.8 utilities/ovs-ofctl.8 \ utilities/ovs-parse-backtrace.8 utilities/ovs-pcap.1 \ utilities/ovs-pki.8 utilities/ovs-tcpundump.1 \ utilities/ovs-vlan-bug-workaround.8 utilities/ovs-test.8 \ utilities/ovs-vlan-test.8 utilities/ovs-vsctl.8 \ $(am__append_33) vswitchd/ovs-vswitchd.8 \ vswitchd/ovs-vswitchd.conf.db.5 ovsdb/ovsdb-tool.1 \ ovsdb/ovsdb-client.1 ovsdb/ovsdb-server.1 vtep/vtep-ctl.8 \ vtep/vtep.5 ovn/ovn-sb.5 ovn/ovn-nb.5 ovn/ovn-architecture.7 \ ovn/controller/ovn-controller.8 \ ovn/controller-vtep/ovn-controller-vtep.8 \ ovn/northd/ovn-northd.8 ovn/utilities/ovn-ctl.8 \ ovn/utilities/ovn-nbctl.8 ovn/utilities/ovn-sbctl.8 MAN_FRAGMENTS = lib/common.man lib/common-syn.man \ lib/coverage-unixctl.man lib/daemon.man lib/daemon-syn.man \ lib/db-ctl-base.man lib/dpctl.man lib/memory-unixctl.man \ lib/ofp-version.man lib/ovs.tmac lib/service.man \ lib/service-syn.man lib/ssl-bootstrap.man \ lib/ssl-bootstrap-syn.man lib/ssl-peer-ca-cert.man \ lib/ssl-peer-ca-cert-syn.man lib/ssl.man lib/ssl-syn.man \ lib/table.man lib/unixctl.man lib/unixctl-syn.man \ lib/vconn-active.man lib/vconn-passive.man \ lib/vlog-unixctl.man lib/vlog-syn.man lib/vlog.man \ ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man \ ofproto/ofproto-tnl-unixctl.man utilities/ovs-vlan-bugs.man \ ovsdb/remote-active.man ovsdb/remote-passive.man MAN_ROOTS = utilities/ovs-appctl.8.in utilities/ovs-benchmark.1.in \ utilities/ovs-testcontroller.8.in utilities/ovs-ctl.8 \ utilities/ovs-dpctl.8.in utilities/ovs-dpctl-top.8.in \ utilities/ovs-l3ping.8.in utilities/ovs-ofctl.8.in \ utilities/ovs-parse-backtrace.8 utilities/ovs-pcap.1.in \ utilities/ovs-pki.8.in utilities/ovs-tcpundump.1.in \ utilities/ovs-vlan-bug-workaround.8.in utilities/ovs-test.8.in \ utilities/ovs-vlan-test.8.in utilities/ovs-vsctl.8.in \ $(am__append_34) vswitchd/ovs-vswitchd.8.in \ ovsdb/ovsdb-tool.1.in ovsdb/ovsdb-client.1.in \ ovsdb/ovsdb-server.1.in ovsdb/ovsdb-idlc.1 vtep/vtep-ctl.8.in \ ovn/utilities/ovn-sbctl.8.in noinst_DATA = # This ensures that files added to EXTRA_DIST are always distributed, # even if they are inside an Automake if...endif conditional block that is # disabled by some particular "configure" run. For more information, see: # http://article.gmane.org/gmane.comp.sysutils.automake.general/10891 noinst_HEADERS = $(EXTRA_DIST) include/sparse/arpa/inet.h \ include/sparse/assert.h include/sparse/bmi2intrin.h \ include/sparse/emmintrin.h include/sparse/math.h \ include/sparse/netinet/in.h include/sparse/netinet/ip6.h \ include/sparse/netpacket/packet.h include/sparse/pthread.h \ include/sparse/rte_atomic.h include/sparse/rte_lcore.h \ include/sparse/rte_vect.h include/sparse/sys/socket.h \ include/sparse/sys/wait.h include/windows/arpa/inet.h \ include/windows/dirent.h include/windows/getopt.h \ include/windows/linux/pkt_sched.h \ include/windows/linux/types.h include/windows/net/if.h \ include/windows/netdb.h include/windows/netpacket/packet.h \ include/windows/netinet/icmp6.h include/windows/netinet/in.h \ include/windows/netinet/in_systm.h \ include/windows/netinet/ip.h include/windows/netinet/ip6.h \ include/windows/netinet/tcp.h include/windows/poll.h \ include/windows/strings.h include/windows/syslog.h \ include/windows/sys/epoll.h include/windows/sys/ioctl.h \ include/windows/sys/resource.h include/windows/sys/socket.h \ include/windows/sys/time.h include/windows/sys/uio.h \ include/windows/sys/un.h include/windows/sys/wait.h \ include/windows/unistd.h include/windows/windefs.h # libovsdb # libvtep lib_LTLIBRARIES = lib/libopenvswitch.la lib/libsflow.la \ ofproto/libofproto.la ovsdb/libovsdb.la vtep/libvtep.la \ ovn/lib/libovn.la noinst_man_MANS = utilities/ovs-sim.1 # ovsdb-idlc noinst_SCRIPTS = utilities/ovs-sim ovsdb/ovsdb-idlc ovsdb/ovsdb-dot # vswitch IDL # idltest schema and IDL # vtep IDL # ovn-sb IDL # ovn-nb IDL OVSIDL_BUILT = lib/vswitch-idl.c lib/vswitch-idl.h \ lib/vswitch-idl.ovsidl tests/idltest.c tests/idltest.h \ tests/idltest.ovsidl vtep/vtep-idl.c vtep/vtep-idl.h \ vtep/vtep-idl.ovsidl ovn/lib/ovn-sb-idl.c ovn/lib/ovn-sb-idl.h \ ovn/lib/ovn-sb-idl.ovsidl ovn/lib/ovn-nb-idl.c \ ovn/lib/ovn-nb-idl.h ovn/lib/ovn-nb-idl.ovsidl pkgdata_DATA = vswitchd/vswitch.ovsschema vtep/vtep.ovsschema \ ovn/ovn-sb.ovsschema ovn/ovn-nb.ovsschema sbin_SCRIPTS = $(am__append_31) # ovs-vtep scripts_SCRIPTS = utilities/ovs-check-dead-ifs utilities/ovs-ctl \ utilities/ovs-save $(am__append_36) vtep/ovs-vtep \ ovn/utilities/ovn-ctl completion_SCRIPTS = utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash scripts_DATA = utilities/ovs-lib SUFFIXES = .in .xml $(am__append_45) .ovsidl .ovsschema check_DATA = $(am__append_42) check_SCRIPTS = utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash pkgconfig_DATA = $(srcdir)/lib/libopenvswitch.pc \ $(srcdir)/lib/libsflow.pc $(srcdir)/ofproto/libofproto.pc \ $(srcdir)/ovsdb/libovsdb.pc scriptsdir = $(pkgdatadir)/scripts completiondir = $(sysconfdir)/bash_completion.d pkgconfigdir = $(libdir)/pkgconfig ro_c = echo '/* -*- mode: c; buffer-read-only: t -*- */' ro_shell = printf '\043 Generated automatically -- do not modify! -*- buffer-read-only: t -*-\n' lib_libopenvswitch_la_LIBADD = $(SSL_LIBS) $(CAPNG_LDADD) \ $(am__append_12) lib_libopenvswitch_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/lib/libopenvswitch.sym \ $(AM_LDFLAGS) lib_libopenvswitch_la_SOURCES = lib/aes128.c lib/aes128.h \ lib/async-append.h lib/backtrace.c lib/backtrace.h lib/bfd.c \ lib/bfd.h lib/bitmap.h lib/bundle.c lib/bundle.h \ lib/byte-order.h lib/byteq.c lib/byteq.h lib/cfm.c lib/cfm.h \ lib/classifier.c lib/classifier.h lib/classifier-private.h \ lib/cmap.c lib/cmap.h lib/command-line.c lib/command-line.h \ lib/compiler.h lib/connectivity.c lib/connectivity.h \ lib/coverage.c lib/coverage.h lib/crc32c.c lib/crc32c.h \ lib/csum.c lib/csum.h lib/ct-dpif.c lib/ct-dpif.h lib/daemon.c \ lib/daemon.h lib/daemon-private.h lib/db-ctl-base.c \ lib/db-ctl-base.h lib/dhcp.h lib/dummy.c lib/dummy.h \ lib/dhparams.h lib/dirs.h lib/dpctl.c lib/dpctl.h \ lib/dp-packet.h lib/dp-packet.c lib/dpif-netdev.c \ lib/dpif-netdev.h lib/dpif-provider.h lib/dpif.c lib/dpif.h \ lib/heap.c lib/heap.h lib/dynamic-string.c \ lib/dynamic-string.h lib/entropy.c lib/entropy.h \ lib/fat-rwlock.c lib/fat-rwlock.h lib/fatal-signal.c \ lib/fatal-signal.h lib/flow.c lib/flow.h lib/geneve.h \ lib/guarded-list.c lib/guarded-list.h lib/hash.c lib/hash.h \ lib/hindex.c lib/hindex.h lib/hmap.c lib/hmap.h lib/hmapx.c \ lib/hmapx.h lib/id-pool.c lib/id-pool.h lib/jhash.c \ lib/jhash.h lib/json.c lib/json.h lib/jsonrpc.c lib/jsonrpc.h \ lib/lacp.c lib/lacp.h lib/latch.h lib/learn.c lib/learn.h \ lib/learning-switch.c lib/learning-switch.h lib/list.h \ lib/lockfile.c lib/lockfile.h lib/mac-learning.c \ lib/mac-learning.h lib/match.c lib/match.h \ lib/mcast-snooping.c lib/mcast-snooping.h lib/memory.c \ lib/memory.h lib/meta-flow.c lib/meta-flow.h lib/multipath.c \ lib/multipath.h lib/netdev-dummy.c lib/netdev-provider.h \ lib/netdev-vport.c lib/netdev-vport.h lib/netdev.c \ lib/netdev.h lib/netflow.h lib/netlink.c lib/netlink.h \ lib/nx-match.c lib/nx-match.h lib/odp-execute.c \ lib/odp-execute.h lib/odp-util.c lib/odp-util.h \ lib/ofp-actions.c lib/ofp-actions.h lib/ofp-errors.c \ lib/ofp-errors.h lib/ofp-msgs.c lib/ofp-msgs.h lib/ofp-parse.c \ lib/ofp-parse.h lib/ofp-print.c lib/ofp-print.h lib/ofp-util.c \ lib/ofp-util.h lib/ofp-version-opt.h lib/ofp-version-opt.c \ lib/ofpbuf.c lib/ofpbuf.h lib/ovs-atomic-c11.h \ lib/ovs-atomic-clang.h lib/ovs-atomic-flag-gcc4.7+.h \ lib/ovs-atomic-gcc4+.h lib/ovs-atomic-gcc4.7+.h \ lib/ovs-atomic-i586.h lib/ovs-atomic-locked.c \ lib/ovs-atomic-locked.h lib/ovs-atomic-msvc.h \ lib/ovs-atomic-pthreads.h lib/ovs-atomic-x86_64.h \ lib/ovs-atomic.h lib/ovs-lldp.c lib/ovs-lldp.h lib/ovs-rcu.c \ lib/ovs-rcu.h lib/ovs-router.h lib/ovs-router.c \ lib/ovs-thread.c lib/ovs-thread.h lib/ovsdb-data.c \ lib/ovsdb-data.h lib/ovsdb-error.c lib/ovsdb-error.h \ lib/ovsdb-idl-provider.h lib/ovsdb-idl.c lib/ovsdb-idl.h \ lib/ovsdb-parser.c lib/ovsdb-parser.h lib/ovsdb-types.c \ lib/ovsdb-types.h lib/packets.c lib/packets.h lib/pcap-file.c \ lib/pcap-file.h lib/perf-counter.h lib/perf-counter.c \ lib/poll-loop.c lib/poll-loop.h lib/process.c lib/process.h \ lib/pvector.c lib/pvector.h lib/random.c lib/random.h \ lib/rconn.c lib/rconn.h lib/rculist.h lib/reconnect.c \ lib/reconnect.h lib/rstp.c lib/rstp.h lib/rstp-common.h \ lib/rstp-state-machines.c lib/rstp-state-machines.h \ lib/sat-math.h lib/seq.c lib/seq.h lib/sha1.c lib/sha1.h \ lib/shash.c lib/shash.h lib/simap.c lib/simap.h lib/smap.c \ lib/smap.h lib/socket-util.c lib/socket-util.h lib/sort.c \ lib/sort.h lib/sset.c lib/sset.h lib/stp.c lib/stp.h \ lib/stream-fd.c lib/stream-fd.h lib/stream-provider.h \ lib/stream-ssl.h lib/stream-tcp.c lib/stream.c lib/stream.h \ lib/stdio.c lib/string.c lib/svec.c lib/svec.h \ lib/syslog-direct.c lib/syslog-direct.h lib/syslog-libc.c \ lib/syslog-libc.h lib/syslog-provider.h lib/table.c \ lib/table.h lib/timer.c lib/timer.h lib/timeval.c \ lib/timeval.h lib/tnl-neigh-cache.c lib/tnl-neigh-cache.h \ lib/tnl-ports.c lib/tnl-ports.h lib/token-bucket.c \ lib/tun-metadata.c lib/tun-metadata.h lib/type-props.h \ lib/unaligned.h lib/unicode.c lib/unicode.h lib/unixctl.c \ lib/unixctl.h lib/util.c lib/util.h lib/uuid.c lib/uuid.h \ lib/valgrind.h lib/vconn-provider.h lib/vconn-stream.c \ lib/vconn.c lib/vlan-bitmap.c lib/vlan-bitmap.h lib/vlandev.c \ lib/vlandev.h lib/vlog.c lib/lldp/aa-structs.h lib/lldp/lldp.c \ lib/lldp/lldp-const.h lib/lldp/lldp-tlv.h lib/lldp/lldpd.c \ lib/lldp/lldpd.h lib/lldp/lldpd-structs.c \ lib/lldp/lldpd-structs.h $(am__append_13) $(am__append_14) \ $(am__append_17) $(am__append_18) $(am__append_19) \ $(am__append_20) $(am__append_21) $(am__append_22) \ $(am__append_23) $(am__append_24) $(am__append_26) nodist_lib_libopenvswitch_la_SOURCES = lib/dirs.c lib/vswitch-idl.c \ lib/vswitch-idl.h $(am__append_25) lib_libsflow_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/lib/libsflow.sym \ $(AM_LDFLAGS) lib_libsflow_la_SOURCES = \ lib/sflow_api.h \ lib/sflow.h \ lib/sflow_agent.c \ lib/sflow_sampler.c \ lib/sflow_poller.c \ lib/sflow_receiver.c lib_libsflow_la_CPPFLAGS = $(AM_CPPFLAGS) lib_libsflow_la_CFLAGS = $(AM_CFLAGS) $(am__append_15) \ $(am__append_16) ofproto_libofproto_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ofproto/libofproto.sym \ $(AM_LDFLAGS) # Distribute this generated file in order not to require Python at # build time if ofproto/ipfix.xml is not modified. ofproto_libofproto_la_SOURCES = ofproto/bond.c ofproto/bond.h \ ofproto/collectors.c ofproto/collectors.h ofproto/connmgr.c \ ofproto/connmgr.h ofproto/fail-open.c ofproto/fail-open.h \ ofproto/in-band.c ofproto/in-band.h ofproto/names.c \ ofproto/netflow.c ofproto/netflow.h ofproto/ofproto.c \ ofproto/ofproto.h ofproto/ofproto-dpif.c \ ofproto/ofproto-dpif.h ofproto/ofproto-dpif-ipfix.c \ ofproto/ofproto-dpif-ipfix.h ofproto/ofproto-dpif-mirror.c \ ofproto/ofproto-dpif-mirror.h ofproto/ofproto-dpif-monitor.c \ ofproto/ofproto-dpif-monitor.h ofproto/ofproto-dpif-rid.c \ ofproto/ofproto-dpif-rid.h ofproto/ofproto-dpif-sflow.c \ ofproto/ofproto-dpif-sflow.h ofproto/ofproto-dpif-upcall.c \ ofproto/ofproto-dpif-upcall.h ofproto/ofproto-dpif-xlate.c \ ofproto/ofproto-dpif-xlate.h ofproto/ofproto-provider.h \ ofproto/pktbuf.c ofproto/pktbuf.h ofproto/pinsched.c \ ofproto/pinsched.h ofproto/tunnel.c ofproto/tunnel.h \ ofproto/bundles.c ofproto/bundles.h ofproto/ipfix-entities.def ofproto_libofproto_la_CPPFLAGS = $(AM_CPPFLAGS) ofproto_libofproto_la_CFLAGS = $(AM_CFLAGS) ofproto_libofproto_la_LIBADD = lib/libsflow.la $(am__append_27) dist_noinst_SCRIPTS = ofproto/ipfix-gen-entities utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c utilities_ovs_appctl_LDADD = lib/libopenvswitch.la utilities_ovs_testcontroller_SOURCES = utilities/ovs-testcontroller.c utilities_ovs_testcontroller_LDADD = lib/libopenvswitch.la $(SSL_LIBS) utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c utilities_ovs_dpctl_LDADD = lib/libopenvswitch.la utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c utilities_ovs_ofctl_LDADD = \ ofproto/libofproto.la \ lib/libopenvswitch.la utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c utilities_ovs_vsctl_LDADD = lib/libopenvswitch.la @LINUX_TRUE@utilities_ovs_vlan_bug_workaround_SOURCES = utilities/ovs-vlan-bug-workaround.c @LINUX_TRUE@utilities_ovs_vlan_bug_workaround_LDADD = lib/libopenvswitch.la @LINUX_TRUE@utilities_nlmon_SOURCES = utilities/nlmon.c @LINUX_TRUE@utilities_nlmon_LDADD = lib/libopenvswitch.la utilities_ovs_benchmark_SOURCES = utilities/ovs-benchmark.c utilities_ovs_benchmark_LDADD = lib/libopenvswitch.la @HAVE_PYTHON_TRUE@bugtool_plugins = \ @HAVE_PYTHON_TRUE@ utilities/bugtool/plugins/kernel-info/openvswitch.xml \ @HAVE_PYTHON_TRUE@ utilities/bugtool/plugins/network-status/openvswitch.xml \ @HAVE_PYTHON_TRUE@ utilities/bugtool/plugins/system-configuration.xml \ @HAVE_PYTHON_TRUE@ utilities/bugtool/plugins/system-logs/openvswitch.xml \ @HAVE_PYTHON_TRUE@ utilities/bugtool/plugins/system-configuration/openvswitch.xml @HAVE_PYTHON_TRUE@bugtool_scripts = \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-bfd-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-cfm-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-coverage-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-fdb-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-lacp-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-list-dbs \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-memory-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-tc-class-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-vsctl-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-ovsdb-dump \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-daemons-ver \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-ovs-ofctl-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-ovs-ofctl-dump-flows \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-ovs-appctl-dpif \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-bond-show \ @HAVE_PYTHON_TRUE@ utilities/bugtool/ovs-bugtool-conntrack-dump @HAVE_PYTHON_TRUE@bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins COMMON_MACROS_AT = \ tests/ovsdb-macros.at \ tests/ovs-macros.at \ tests/ofproto-macros.at TESTSUITE_AT = \ tests/testsuite.at \ tests/completion.at \ tests/library.at \ tests/heap.at \ tests/bundle.at \ tests/classifier.at \ tests/check-structs.at \ tests/daemon.at \ tests/daemon-py.at \ tests/ofp-actions.at \ tests/ofp-print.at \ tests/ofp-util.at \ tests/ofp-errors.at \ tests/ovs-ofctl.at \ tests/odp.at \ tests/mpls-xlate.at \ tests/multipath.at \ tests/bfd.at \ tests/cfm.at \ tests/lacp.at \ tests/lib.at \ tests/learn.at \ tests/vconn.at \ tests/file_name.at \ tests/aes128.at \ tests/unixctl-py.at \ tests/uuid.at \ tests/json.at \ tests/jsonrpc.at \ tests/jsonrpc-py.at \ tests/tunnel.at \ tests/tunnel-push-pop.at \ tests/tunnel-push-pop-ipv6.at \ tests/lockfile.at \ tests/reconnect.at \ tests/ovs-vswitchd.at \ tests/dpif-netdev.at \ tests/dpctl.at \ tests/ofproto-dpif.at \ tests/bridge.at \ tests/vlan-splinters.at \ tests/ofproto.at \ tests/netdev-type.at \ tests/ovsdb.at \ tests/ovsdb-log.at \ tests/ovsdb-types.at \ tests/ovsdb-data.at \ tests/ovsdb-column.at \ tests/ovsdb-table.at \ tests/ovsdb-row.at \ tests/ovsdb-schema.at \ tests/ovsdb-condition.at \ tests/ovsdb-mutation.at \ tests/ovsdb-query.at \ tests/ovsdb-transaction.at \ tests/ovsdb-execution.at \ tests/ovsdb-trigger.at \ tests/ovsdb-tool.at \ tests/ovsdb-server.at \ tests/ovsdb-monitor.at \ tests/ovsdb-idl.at \ tests/ovs-vsctl.at \ tests/ovs-monitor-ipsec.at \ tests/ovs-xapi-sync.at \ tests/stp.at \ tests/rstp.at \ tests/interface-reconfigure.at \ tests/vlog.at \ tests/vtep-ctl.at \ tests/auto-attach.at \ tests/ovn.at \ tests/ovn-nbctl.at \ tests/ovn-sbctl.at \ tests/ovn-controller.at \ tests/ovn-controller-vtep.at \ tests/mcast-snooping.at SYSTEM_KMOD_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-kmod-testsuite.at \ tests/system-kmod-macros.at SYSTEM_USERSPACE_TESTSUITE_AT = \ tests/system-userspace-testsuite.at \ tests/system-userspace-macros.at SYSTEM_TESTSUITE_AT = \ tests/system-common-macros.at \ tests/system-traffic.at TESTSUITE = $(srcdir)/tests/testsuite TESTSUITE_PATCH = $(srcdir)/tests/testsuite.patch SYSTEM_KMOD_TESTSUITE = $(srcdir)/tests/system-kmod-testsuite SYSTEM_USERSPACE_TESTSUITE = $(srcdir)/tests/system-userspace-testsuite AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:$(PTHREAD_WIN32_DIR_DLL):ovn/controller-vtep:ovn/northd:ovn/utilities:ovn/controller # Python Coverage support. # Requires coverage.py http://nedbatchelder.com/code/coverage/. COVERAGE = coverage COVERAGE_FILE = '$(abs_srcdir)/.coverage' # valgrind support valgrind_wrappers = \ tests/valgrind/ovs-appctl \ tests/valgrind/ovs-ofctl \ tests/valgrind/ovstest \ tests/valgrind/ovs-vsctl \ tests/valgrind/ovs-vswitchd \ tests/valgrind/ovsdb-client \ tests/valgrind/ovsdb-server \ tests/valgrind/ovsdb-tool \ tests/valgrind/test-aes128 \ tests/valgrind/test-atomic \ tests/valgrind/test-bundle \ tests/valgrind/test-byte-order \ tests/valgrind/test-classifier \ tests/valgrind/test-cmap \ tests/valgrind/test-csum \ tests/valgrind/test-flows \ tests/valgrind/test-hash \ tests/valgrind/test-hindex \ tests/valgrind/test-hmap \ tests/valgrind/test-json \ tests/valgrind/test-jsonrpc \ tests/valgrind/test-list \ tests/valgrind/test-lockfile \ tests/valgrind/test-multipath \ tests/valgrind/test-odp \ tests/valgrind/test-ofpbuf \ tests/valgrind/test-ovsdb \ tests/valgrind/test-packets \ tests/valgrind/test-random \ tests/valgrind/test-reconnect \ tests/valgrind/test-rstp \ tests/valgrind/test-sha1 \ tests/valgrind/test-stp \ tests/valgrind/test-type-props \ tests/valgrind/test-unix-socket \ tests/valgrind/test-uuid \ tests/valgrind/test-vconn VALGRIND = valgrind --log-file=valgrind.%p --leak-check=full \ --suppressions=$(abs_top_srcdir)/tests/glibc.supp \ --suppressions=$(abs_top_srcdir)/tests/openssl.supp --num-callers=20 AUTOTEST = $(AUTOM4TE) --language=autotest tests_test_ovsdb_SOURCES = tests/test-ovsdb.c nodist_tests_test_ovsdb_SOURCES = tests/idltest.c tests/idltest.h tests_test_ovsdb_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la tests_test_lib_SOURCES = \ tests/test-lib.c tests_test_lib_LDADD = lib/libopenvswitch.la IDLTEST_IDL_FILES = tests/idltest.ovsschema tests/idltest.ann @DPDK_NETDEV_TRUE@tests_test_dpdkr_SOURCES = \ @DPDK_NETDEV_TRUE@ tests/dpdk/ring_client.c @DPDK_NETDEV_TRUE@tests_test_dpdkr_LDADD = lib/libopenvswitch.la $(LIBS) tests_ovstest_SOURCES = tests/ovstest.c tests/ovstest.h \ tests/test-aes128.c tests/test-atomic.c tests/test-bundle.c \ tests/test-byte-order.c tests/test-classifier.c \ tests/test-cmap.c tests/test-csum.c tests/test-flows.c \ tests/test-hash.c tests/test-heap.c tests/test-hindex.c \ tests/test-hmap.c tests/test-json.c tests/test-jsonrpc.c \ tests/test-list.c tests/test-lockfile.c tests/test-multipath.c \ tests/test-netflow.c tests/test-odp.c tests/test-ofpbuf.c \ tests/test-ovn.c tests/test-packets.c tests/test-random.c \ tests/test-rcu.c tests/test-reconnect.c tests/test-rstp.c \ tests/test-sflow.c tests/test-sha1.c tests/test-stp.c \ tests/test-util.c tests/test-uuid.c tests/test-bitmap.c \ tests/test-vconn.c tests/test-aa.c $(am__append_40) \ $(am__append_41) tests_ovstest_LDADD = lib/libopenvswitch.la ovn/lib/libovn.la dist_check_SCRIPTS = tests/flowgen.pl tests_test_strtok_r_SOURCES = tests/test-strtok_r.c tests_test_type_props_SOURCES = tests/test-type-props.c # Python tests. CHECK_PYFILES = \ tests/appctl.py \ tests/test-daemon.py \ tests/test-json.py \ tests/test-jsonrpc.py \ tests/test-l7.py \ tests/test-ovsdb.py \ tests/test-reconnect.py \ tests/MockXenAPI.py \ tests/test-unix-socket.py \ tests/test-unixctl.py \ tests/test-vlog.py @HAVE_OPENSSL_TRUE@TESTPKI_FILES = \ @HAVE_OPENSSL_TRUE@ tests/testpki-cacert.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-cert.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-privkey.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-req.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-cert2.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-privkey2.pem \ @HAVE_OPENSSL_TRUE@ tests/testpki-req2.pem @HAVE_OPENSSL_TRUE@OVS_PKI = $(SHELL) $(srcdir)/utilities/ovs-pki.in --dir=tests/pki --log=tests/ovs-pki.log openflowincludedir = $(includedir)/openflow openflowinclude_HEADERS = \ include/openflow/netronome-ext.h \ include/openflow/nicira-ext.h \ include/openflow/openflow-1.0.h \ include/openflow/openflow-1.1.h \ include/openflow/openflow-1.2.h \ include/openflow/openflow-1.3.h \ include/openflow/openflow-1.4.h \ include/openflow/openflow-1.5.h \ include/openflow/openflow-common.h \ include/openflow/openflow.h @HAVE_PYTHON_TRUE@HSTAMP_FILES = $(openflowinclude_HEADERS:.h=.hstamp) openvswitchincludedir = $(includedir)/openvswitch openvswitchinclude_HEADERS = \ include/openvswitch/compiler.h \ include/openvswitch/list.h \ include/openvswitch/thread.h \ include/openvswitch/token-bucket.h \ include/openvswitch/types.h \ include/openvswitch/util.h \ include/openvswitch/version.h \ include/openvswitch/vconn.h \ include/openvswitch/vlog.h vswitchd_ovs_vswitchd_SOURCES = \ vswitchd/bridge.c \ vswitchd/bridge.h \ vswitchd/ovs-vswitchd.c \ vswitchd/system-stats.c \ vswitchd/system-stats.h \ vswitchd/xenserver.c \ vswitchd/xenserver.h vswitchd_ovs_vswitchd_LDADD = \ ofproto/libofproto.la \ lib/libsflow.la \ lib/libopenvswitch.la vswitchd_ovs_vswitchd_LDFLAGS = $(AM_LDFLAGS) $(DPDK_vswitchd_LDFLAGS) @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@VSWITCH_PIC = vswitchd/vswitch.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@VSWITCH_DOT_DIAGRAM_ARG = --er-diagram=$(VSWITCH_PIC) ovsdb_libovsdb_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ovsdb/libovsdb.sym \ $(AM_LDFLAGS) ovsdb_libovsdb_la_SOURCES = \ ovsdb/column.c \ ovsdb/column.h \ ovsdb/condition.c \ ovsdb/condition.h \ ovsdb/execution.c \ ovsdb/file.c \ ovsdb/file.h \ ovsdb/jsonrpc-server.c \ ovsdb/jsonrpc-server.h \ ovsdb/log.c \ ovsdb/log.h \ ovsdb/mutation.c \ ovsdb/mutation.h \ ovsdb/ovsdb.c \ ovsdb/ovsdb.h \ ovsdb/monitor.c \ ovsdb/monitor.h \ ovsdb/query.c \ ovsdb/query.h \ ovsdb/row.c \ ovsdb/row.h \ ovsdb/server.c \ ovsdb/server.h \ ovsdb/table.c \ ovsdb/table.h \ ovsdb/trigger.c \ ovsdb/trigger.h \ ovsdb/transaction.c \ ovsdb/transaction.h ovsdb_libovsdb_la_CFLAGS = $(AM_CFLAGS) ovsdb_libovsdb_la_CPPFLAGS = $(AM_CPPFLAGS) ovsdb_ovsdb_tool_SOURCES = ovsdb/ovsdb-tool.c ovsdb_ovsdb_tool_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la ovsdb_ovsdb_client_SOURCES = ovsdb/ovsdb-client.c ovsdb_ovsdb_client_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la ovsdb_ovsdb_server_SOURCES = ovsdb/ovsdb-server.c ovsdb_ovsdb_server_LDADD = ovsdb/libovsdb.la lib/libopenvswitch.la OVSDB_IDLC = $(run_python) $(srcdir)/ovsdb/ovsdb-idlc.in OVSDB_DOC = $(run_python) $(srcdir)/ovsdb/ovsdb-doc OVSDB_DOT = $(run_python) $(srcdir)/ovsdb/ovsdb-dot.in update_rhel_spec = \ $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ < $(srcdir)/rhel/$(@F).in > $(@F).tmp || exit 1; \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi ovstest_pyfiles = \ python/ovstest/__init__.py \ python/ovstest/args.py \ python/ovstest/rpcserver.py \ python/ovstest/tcp.py \ python/ovstest/tests.py \ python/ovstest/udp.py \ python/ovstest/util.py \ python/ovstest/vswitch.py ovs_pyfiles = \ python/ovs/__init__.py \ python/ovs/daemon.py \ python/ovs/db/__init__.py \ python/ovs/db/data.py \ python/ovs/db/error.py \ python/ovs/db/idl.py \ python/ovs/db/parser.py \ python/ovs/db/schema.py \ python/ovs/db/types.py \ python/ovs/fatal_signal.py \ python/ovs/json.py \ python/ovs/jsonrpc.py \ python/ovs/ovsuuid.py \ python/ovs/poller.py \ python/ovs/process.py \ python/ovs/reconnect.py \ python/ovs/socket_util.py \ python/ovs/stream.py \ python/ovs/timeval.py \ python/ovs/unixctl/__init__.py \ python/ovs/unixctl/client.py \ python/ovs/unixctl/server.py \ python/ovs/util.py \ python/ovs/version.py \ python/ovs/vlog.py PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) @HAVE_PYTHON_TRUE@nobase_pkgdata_DATA = $(ovs_pyfiles) $(ovstest_pyfiles) VTEP_IDL_FILES = \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep-idl.ann vtep_libvtep_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/vtep/libvtep.sym \ $(AM_LDFLAGS) nodist_vtep_libvtep_la_SOURCES = \ vtep/vtep-idl.c \ vtep/vtep-idl.h vtep_vtep_ctl_SOURCES = vtep/vtep-ctl.c vtep_vtep_ctl_LDADD = vtep/libvtep.la lib/libopenvswitch.la @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@VTEP_PIC = vtep/vtep.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@VTEP_DOT_DIAGRAM_ARG = --er-diagram=$(VTEP_PIC) PTHREAD_TEMP_DIR = `echo "$(PTHREAD_LDFLAGS)" | sed 's|^.\(.*\).$:\1||'` @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@OVN_SB_PIC = ovn/ovn-sb.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@OVN_SB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_SB_PIC) @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@OVN_NB_PIC = ovn/ovn-nb.pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@OVN_NB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_NB_PIC) ovn_controller_ovn_controller_SOURCES = \ ovn/controller/binding.c \ ovn/controller/binding.h \ ovn/controller/chassis.c \ ovn/controller/chassis.h \ ovn/controller/encaps.c \ ovn/controller/encaps.h \ ovn/controller/lflow.c \ ovn/controller/lflow.h \ ovn/controller/ofctrl.c \ ovn/controller/ofctrl.h \ ovn/controller/pinctrl.c \ ovn/controller/pinctrl.h \ ovn/controller/patch.c \ ovn/controller/patch.h \ ovn/controller/ovn-controller.c \ ovn/controller/ovn-controller.h \ ovn/controller/physical.c \ ovn/controller/physical.h ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la ovn_controller_vtep_ovn_controller_vtep_SOURCES = \ ovn/controller-vtep/binding.c \ ovn/controller-vtep/binding.h \ ovn/controller-vtep/gateway.c \ ovn/controller-vtep/gateway.h \ ovn/controller-vtep/ovn-controller-vtep.c \ ovn/controller-vtep/ovn-controller-vtep.h \ ovn/controller-vtep/vtep.c \ ovn/controller-vtep/vtep.h ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la vtep/libvtep.la ovn_lib_libovn_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ovn/lib/libovn.sym \ $(AM_LDFLAGS) ovn_lib_libovn_la_SOURCES = \ ovn/lib/actions.c \ ovn/lib/actions.h \ ovn/lib/expr.c \ ovn/lib/expr.h \ ovn/lib/lex.c \ ovn/lib/lex.h \ ovn/lib/logical-fields.h nodist_ovn_lib_libovn_la_SOURCES = \ ovn/lib/ovn-nb-idl.c \ ovn/lib/ovn-nb-idl.h \ ovn/lib/ovn-sb-idl.c \ ovn/lib/ovn-sb-idl.h OVN_SB_IDL_FILES = \ $(srcdir)/ovn/ovn-sb.ovsschema \ $(srcdir)/ovn/lib/ovn-sb-idl.ann OVN_NB_IDL_FILES = \ $(srcdir)/ovn/ovn-nb.ovsschema \ $(srcdir)/ovn/lib/ovn-nb-idl.ann ovn_northd_ovn_northd_SOURCES = ovn/northd/ovn-northd.c ovn_northd_ovn_northd_LDADD = \ ovn/lib/libovn.la \ ovsdb/libovsdb.la \ lib/libopenvswitch.la ovn_utilities_ovn_nbctl_SOURCES = ovn/utilities/ovn-nbctl.c ovn_utilities_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la ovn_utilities_ovn_sbctl_SOURCES = ovn/utilities/ovn-sbctl.c ovn_utilities_ovn_sbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .in .xml .h .hstamp .ovsidl .ovsschema .c .lo .o .obj am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/manpages.mk $(srcdir)/Documentation/automake.mk $(srcdir)/m4/automake.mk $(srcdir)/lib/automake.mk $(srcdir)/ofproto/automake.mk $(srcdir)/utilities/automake.mk $(srcdir)/utilities/bugtool/automake.mk $(srcdir)/tests/automake.mk $(srcdir)/include/automake.mk $(srcdir)/include/openflow/automake.mk $(srcdir)/include/openvswitch/automake.mk $(srcdir)/include/sparse/automake.mk $(srcdir)/include/windows/automake.mk $(srcdir)/third-party/automake.mk $(srcdir)/debian/automake.mk $(srcdir)/vswitchd/automake.mk $(srcdir)/ovsdb/automake.mk $(srcdir)/rhel/automake.mk $(srcdir)/xenserver/automake.mk $(srcdir)/python/automake.mk $(srcdir)/tutorial/automake.mk $(srcdir)/vtep/automake.mk $(srcdir)/datapath-windows/automake.mk $(srcdir)/datapath-windows/include/automake.mk $(srcdir)/windows/automake.mk $(srcdir)/ovn/automake.mk $(srcdir)/ovn/controller/automake.mk $(srcdir)/ovn/controller-vtep/automake.mk $(srcdir)/ovn/lib/automake.mk $(srcdir)/ovn/northd/automake.mk $(srcdir)/ovn/utilities/automake.mk $(srcdir)/selinux/automake.mk $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(srcdir)/manpages.mk $(srcdir)/Documentation/automake.mk $(srcdir)/m4/automake.mk $(srcdir)/lib/automake.mk $(srcdir)/ofproto/automake.mk $(srcdir)/utilities/automake.mk $(srcdir)/utilities/bugtool/automake.mk $(srcdir)/tests/automake.mk $(srcdir)/include/automake.mk $(srcdir)/include/openflow/automake.mk $(srcdir)/include/openvswitch/automake.mk $(srcdir)/include/sparse/automake.mk $(srcdir)/include/windows/automake.mk $(srcdir)/third-party/automake.mk $(srcdir)/debian/automake.mk $(srcdir)/vswitchd/automake.mk $(srcdir)/ovsdb/automake.mk $(srcdir)/rhel/automake.mk $(srcdir)/xenserver/automake.mk $(srcdir)/python/automake.mk $(srcdir)/tutorial/automake.mk $(srcdir)/vtep/automake.mk $(srcdir)/datapath-windows/automake.mk $(srcdir)/datapath-windows/include/automake.mk $(srcdir)/windows/automake.mk $(srcdir)/ovn/automake.mk $(srcdir)/ovn/controller/automake.mk $(srcdir)/ovn/controller-vtep/automake.mk $(srcdir)/ovn/lib/automake.mk $(srcdir)/ovn/northd/automake.mk $(srcdir)/ovn/utilities/automake.mk $(srcdir)/selinux/automake.mk $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 lib/stdio.h: $(top_builddir)/config.status $(top_srcdir)/lib/stdio.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ lib/string.h: $(top_builddir)/config.status $(top_srcdir)/lib/string.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ ovsdb/libovsdb.sym: $(top_builddir)/config.status $(top_srcdir)/ovsdb/libovsdb.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ ofproto/libofproto.sym: $(top_builddir)/config.status $(top_srcdir)/ofproto/libofproto.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ lib/libsflow.sym: $(top_builddir)/config.status $(top_srcdir)/lib/libsflow.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ lib/libopenvswitch.sym: $(top_builddir)/config.status $(top_srcdir)/lib/libopenvswitch.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ ovn/lib/libovn.sym: $(top_builddir)/config.status $(top_srcdir)/ovn/lib/libovn.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ vtep/libvtep.sym: $(top_builddir)/config.status $(top_srcdir)/vtep/libvtep.sym.in cd $(top_builddir) && $(SHELL) ./config.status $@ datapath/linux/Kbuild: $(top_builddir)/config.status $(top_srcdir)/datapath/linux/Kbuild.in cd $(top_builddir) && $(SHELL) ./config.status $@ datapath/linux/Makefile: $(top_builddir)/config.status $(top_srcdir)/datapath/linux/Makefile.in cd $(top_builddir) && $(SHELL) ./config.status $@ datapath/linux/Makefile.main: $(top_builddir)/config.status $(top_srcdir)/datapath/linux/Makefile.main.in cd $(top_builddir) && $(SHELL) ./config.status $@ tests/atlocal: $(top_builddir)/config.status $(top_srcdir)/tests/atlocal.in cd $(top_builddir) && $(SHELL) ./config.status $@ lib/libopenvswitch.pc: $(top_builddir)/config.status $(top_srcdir)/lib/libopenvswitch.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ lib/libsflow.pc: $(top_builddir)/config.status $(top_srcdir)/lib/libsflow.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ ofproto/libofproto.pc: $(top_builddir)/config.status $(top_srcdir)/ofproto/libofproto.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ ovsdb/libovsdb.pc: $(top_builddir)/config.status $(top_srcdir)/ovsdb/libovsdb.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ include/openvswitch/version.h: $(top_builddir)/config.status $(top_srcdir)/include/openvswitch/version.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } lib/$(am__dirstamp): @$(MKDIR_P) lib @: > lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lib/$(DEPDIR) @: > lib/$(DEPDIR)/$(am__dirstamp) lib/aes128.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/backtrace.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/bfd.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/bundle.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/byteq.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/cfm.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/classifier.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/cmap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/command-line.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/connectivity.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/coverage.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/crc32c.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/csum.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ct-dpif.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/daemon.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/db-ctl-base.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dummy.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dpctl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dp-packet.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dpif-netdev.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dpif.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/heap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dynamic-string.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/entropy.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/fat-rwlock.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/fatal-signal.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/flow.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/guarded-list.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/hash.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/hindex.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/hmap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/hmapx.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/id-pool.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/jhash.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/json.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/jsonrpc.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/lacp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/learn.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/learning-switch.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/lockfile.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/mac-learning.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/match.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/mcast-snooping.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/memory.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/meta-flow.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/multipath.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-dummy.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-vport.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netlink.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/nx-match.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/odp-execute.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/odp-util.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-actions.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-errors.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-msgs.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-parse.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-print.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-util.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ofp-version-opt.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/ofpbuf.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-atomic-locked.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-lldp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-rcu.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-router.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-thread.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovsdb-data.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovsdb-error.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovsdb-idl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovsdb-parser.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/ovsdb-types.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/packets.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/pcap-file.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/perf-counter.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/poll-loop.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/process.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/pvector.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/random.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/rconn.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/reconnect.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/rstp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/rstp-state-machines.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/seq.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/sha1.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/shash.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/simap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/smap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/socket-util.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/sort.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/sset.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stream-fd.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stream-tcp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stream.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stdio.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/string.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/svec.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/syslog-direct.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/syslog-libc.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/table.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/timer.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/timeval.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/tnl-neigh-cache.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/tnl-ports.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/token-bucket.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/tun-metadata.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/unicode.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/unixctl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/util.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/uuid.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vconn-stream.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vconn.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vlan-bitmap.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vlandev.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vlog.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/lldp/$(am__dirstamp): @$(MKDIR_P) lib/lldp @: > lib/lldp/$(am__dirstamp) lib/lldp/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lib/lldp/$(DEPDIR) @: > lib/lldp/$(DEPDIR)/$(am__dirstamp) lib/lldp/lldp.lo: lib/lldp/$(am__dirstamp) \ lib/lldp/$(DEPDIR)/$(am__dirstamp) lib/lldp/lldpd.lo: lib/lldp/$(am__dirstamp) \ lib/lldp/$(DEPDIR)/$(am__dirstamp) lib/lldp/lldpd-structs.lo: lib/lldp/$(am__dirstamp) \ lib/lldp/$(DEPDIR)/$(am__dirstamp) lib/daemon-windows.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/getopt_long.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/getrusage-windows.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/latch-windows.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/route-table-stub.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/if-notifier-stub.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/strsep.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/daemon-unix.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/latch-unix.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/signals.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/socket-util-unix.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/stream-unix.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dpif-netlink.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/if-notifier.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-linux.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netlink-conntrack.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/netlink-notifier.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/netlink-socket.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/ovs-numa.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/rtnetlink.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/route-table.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-dpdk.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-windows.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/async-append-aio.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/async-append-null.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/if-notifier-bsd.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/netdev-bsd.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/rtbsd.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/route-table-bsd.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/stream-ssl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/stream-nossl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dirs.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/vswitch-idl.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/dhparams.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp) lib/libopenvswitch.la: $(lib_libopenvswitch_la_OBJECTS) $(lib_libopenvswitch_la_DEPENDENCIES) $(EXTRA_lib_libopenvswitch_la_DEPENDENCIES) lib/$(am__dirstamp) $(AM_V_CCLD)$(lib_libopenvswitch_la_LINK) -rpath $(libdir) $(lib_libopenvswitch_la_OBJECTS) $(lib_libopenvswitch_la_LIBADD) $(LIBS) lib/lib_libsflow_la-sflow_agent.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/lib_libsflow_la-sflow_sampler.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/lib_libsflow_la-sflow_poller.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/lib_libsflow_la-sflow_receiver.lo: lib/$(am__dirstamp) \ lib/$(DEPDIR)/$(am__dirstamp) lib/libsflow.la: $(lib_libsflow_la_OBJECTS) $(lib_libsflow_la_DEPENDENCIES) $(EXTRA_lib_libsflow_la_DEPENDENCIES) lib/$(am__dirstamp) $(AM_V_CCLD)$(lib_libsflow_la_LINK) -rpath $(libdir) $(lib_libsflow_la_OBJECTS) $(lib_libsflow_la_LIBADD) $(LIBS) ofproto/$(am__dirstamp): @$(MKDIR_P) ofproto @: > ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ofproto/$(DEPDIR) @: > ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-bond.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-collectors.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-connmgr.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-fail-open.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-in-band.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-names.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-netflow.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo: \ ofproto/$(am__dirstamp) ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-pktbuf.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-pinsched.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-tunnel.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/ofproto_libofproto_la-bundles.lo: ofproto/$(am__dirstamp) \ ofproto/$(DEPDIR)/$(am__dirstamp) ofproto/libofproto.la: $(ofproto_libofproto_la_OBJECTS) $(ofproto_libofproto_la_DEPENDENCIES) $(EXTRA_ofproto_libofproto_la_DEPENDENCIES) ofproto/$(am__dirstamp) $(AM_V_CCLD)$(ofproto_libofproto_la_LINK) -rpath $(libdir) $(ofproto_libofproto_la_OBJECTS) $(ofproto_libofproto_la_LIBADD) $(LIBS) ovn/lib/$(am__dirstamp): @$(MKDIR_P) ovn/lib @: > ovn/lib/$(am__dirstamp) ovn/lib/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovn/lib/$(DEPDIR) @: > ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/actions.lo: ovn/lib/$(am__dirstamp) \ ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/expr.lo: ovn/lib/$(am__dirstamp) \ ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/lex.lo: ovn/lib/$(am__dirstamp) \ ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/ovn-nb-idl.lo: ovn/lib/$(am__dirstamp) \ ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/ovn-sb-idl.lo: ovn/lib/$(am__dirstamp) \ ovn/lib/$(DEPDIR)/$(am__dirstamp) ovn/lib/libovn.la: $(ovn_lib_libovn_la_OBJECTS) $(ovn_lib_libovn_la_DEPENDENCIES) $(EXTRA_ovn_lib_libovn_la_DEPENDENCIES) ovn/lib/$(am__dirstamp) $(AM_V_CCLD)$(ovn_lib_libovn_la_LINK) -rpath $(libdir) $(ovn_lib_libovn_la_OBJECTS) $(ovn_lib_libovn_la_LIBADD) $(LIBS) ovsdb/$(am__dirstamp): @$(MKDIR_P) ovsdb @: > ovsdb/$(am__dirstamp) ovsdb/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovsdb/$(DEPDIR) @: > ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-column.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-condition.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-execution.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-file.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-log.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-mutation.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-ovsdb.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-monitor.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-query.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-row.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-server.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-table.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-trigger.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb_libovsdb_la-transaction.lo: ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/libovsdb.la: $(ovsdb_libovsdb_la_OBJECTS) $(ovsdb_libovsdb_la_DEPENDENCIES) $(EXTRA_ovsdb_libovsdb_la_DEPENDENCIES) ovsdb/$(am__dirstamp) $(AM_V_CCLD)$(ovsdb_libovsdb_la_LINK) -rpath $(libdir) $(ovsdb_libovsdb_la_OBJECTS) $(ovsdb_libovsdb_la_LIBADD) $(LIBS) vtep/$(am__dirstamp): @$(MKDIR_P) vtep @: > vtep/$(am__dirstamp) vtep/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) vtep/$(DEPDIR) @: > vtep/$(DEPDIR)/$(am__dirstamp) vtep/vtep-idl.lo: vtep/$(am__dirstamp) vtep/$(DEPDIR)/$(am__dirstamp) vtep/libvtep.la: $(vtep_libvtep_la_OBJECTS) $(vtep_libvtep_la_DEPENDENCIES) $(EXTRA_vtep_libvtep_la_DEPENDENCIES) vtep/$(am__dirstamp) $(AM_V_CCLD)$(vtep_libvtep_la_LINK) -rpath $(libdir) $(vtep_libvtep_la_OBJECTS) $(vtep_libvtep_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list ovn/controller-vtep/$(am__dirstamp): @$(MKDIR_P) ovn/controller-vtep @: > ovn/controller-vtep/$(am__dirstamp) ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovn/controller-vtep/$(DEPDIR) @: > ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) ovn/controller-vtep/binding.$(OBJEXT): \ ovn/controller-vtep/$(am__dirstamp) \ ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) ovn/controller-vtep/gateway.$(OBJEXT): \ ovn/controller-vtep/$(am__dirstamp) \ ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) ovn/controller-vtep/ovn-controller-vtep.$(OBJEXT): \ ovn/controller-vtep/$(am__dirstamp) \ ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) ovn/controller-vtep/vtep.$(OBJEXT): \ ovn/controller-vtep/$(am__dirstamp) \ ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) ovn/controller-vtep/ovn-controller-vtep$(EXEEXT): $(ovn_controller_vtep_ovn_controller_vtep_OBJECTS) $(ovn_controller_vtep_ovn_controller_vtep_DEPENDENCIES) $(EXTRA_ovn_controller_vtep_ovn_controller_vtep_DEPENDENCIES) ovn/controller-vtep/$(am__dirstamp) @rm -f ovn/controller-vtep/ovn-controller-vtep$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovn_controller_vtep_ovn_controller_vtep_OBJECTS) $(ovn_controller_vtep_ovn_controller_vtep_LDADD) $(LIBS) ovn/controller/$(am__dirstamp): @$(MKDIR_P) ovn/controller @: > ovn/controller/$(am__dirstamp) ovn/controller/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovn/controller/$(DEPDIR) @: > ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/binding.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/chassis.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/encaps.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/lflow.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/ofctrl.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/pinctrl.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/patch.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/ovn-controller.$(OBJEXT): \ ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/physical.$(OBJEXT): ovn/controller/$(am__dirstamp) \ ovn/controller/$(DEPDIR)/$(am__dirstamp) ovn/controller/ovn-controller$(EXEEXT): $(ovn_controller_ovn_controller_OBJECTS) $(ovn_controller_ovn_controller_DEPENDENCIES) $(EXTRA_ovn_controller_ovn_controller_DEPENDENCIES) ovn/controller/$(am__dirstamp) @rm -f ovn/controller/ovn-controller$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovn_controller_ovn_controller_OBJECTS) $(ovn_controller_ovn_controller_LDADD) $(LIBS) ovn/northd/$(am__dirstamp): @$(MKDIR_P) ovn/northd @: > ovn/northd/$(am__dirstamp) ovn/northd/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovn/northd/$(DEPDIR) @: > ovn/northd/$(DEPDIR)/$(am__dirstamp) ovn/northd/ovn-northd.$(OBJEXT): ovn/northd/$(am__dirstamp) \ ovn/northd/$(DEPDIR)/$(am__dirstamp) ovn/northd/ovn-northd$(EXEEXT): $(ovn_northd_ovn_northd_OBJECTS) $(ovn_northd_ovn_northd_DEPENDENCIES) $(EXTRA_ovn_northd_ovn_northd_DEPENDENCIES) ovn/northd/$(am__dirstamp) @rm -f ovn/northd/ovn-northd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovn_northd_ovn_northd_OBJECTS) $(ovn_northd_ovn_northd_LDADD) $(LIBS) ovn/utilities/$(am__dirstamp): @$(MKDIR_P) ovn/utilities @: > ovn/utilities/$(am__dirstamp) ovn/utilities/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ovn/utilities/$(DEPDIR) @: > ovn/utilities/$(DEPDIR)/$(am__dirstamp) ovn/utilities/ovn-nbctl.$(OBJEXT): ovn/utilities/$(am__dirstamp) \ ovn/utilities/$(DEPDIR)/$(am__dirstamp) ovn/utilities/ovn-nbctl$(EXEEXT): $(ovn_utilities_ovn_nbctl_OBJECTS) $(ovn_utilities_ovn_nbctl_DEPENDENCIES) $(EXTRA_ovn_utilities_ovn_nbctl_DEPENDENCIES) ovn/utilities/$(am__dirstamp) @rm -f ovn/utilities/ovn-nbctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovn_utilities_ovn_nbctl_OBJECTS) $(ovn_utilities_ovn_nbctl_LDADD) $(LIBS) ovn/utilities/ovn-sbctl.$(OBJEXT): ovn/utilities/$(am__dirstamp) \ ovn/utilities/$(DEPDIR)/$(am__dirstamp) ovn/utilities/ovn-sbctl$(EXEEXT): $(ovn_utilities_ovn_sbctl_OBJECTS) $(ovn_utilities_ovn_sbctl_DEPENDENCIES) $(EXTRA_ovn_utilities_ovn_sbctl_DEPENDENCIES) ovn/utilities/$(am__dirstamp) @rm -f ovn/utilities/ovn-sbctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovn_utilities_ovn_sbctl_OBJECTS) $(ovn_utilities_ovn_sbctl_LDADD) $(LIBS) ovsdb/ovsdb-client.$(OBJEXT): ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb-client$(EXEEXT): $(ovsdb_ovsdb_client_OBJECTS) $(ovsdb_ovsdb_client_DEPENDENCIES) $(EXTRA_ovsdb_ovsdb_client_DEPENDENCIES) ovsdb/$(am__dirstamp) @rm -f ovsdb/ovsdb-client$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovsdb_ovsdb_client_OBJECTS) $(ovsdb_ovsdb_client_LDADD) $(LIBS) ovsdb/ovsdb-server.$(OBJEXT): ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb-server$(EXEEXT): $(ovsdb_ovsdb_server_OBJECTS) $(ovsdb_ovsdb_server_DEPENDENCIES) $(EXTRA_ovsdb_ovsdb_server_DEPENDENCIES) ovsdb/$(am__dirstamp) @rm -f ovsdb/ovsdb-server$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovsdb_ovsdb_server_OBJECTS) $(ovsdb_ovsdb_server_LDADD) $(LIBS) ovsdb/ovsdb-tool.$(OBJEXT): ovsdb/$(am__dirstamp) \ ovsdb/$(DEPDIR)/$(am__dirstamp) ovsdb/ovsdb-tool$(EXEEXT): $(ovsdb_ovsdb_tool_OBJECTS) $(ovsdb_ovsdb_tool_DEPENDENCIES) $(EXTRA_ovsdb_ovsdb_tool_DEPENDENCIES) ovsdb/$(am__dirstamp) @rm -f ovsdb/ovsdb-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ovsdb_ovsdb_tool_OBJECTS) $(ovsdb_ovsdb_tool_LDADD) $(LIBS) tests/$(am__dirstamp): @$(MKDIR_P) tests @: > tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tests/$(DEPDIR) @: > tests/$(DEPDIR)/$(am__dirstamp) tests/ovstest.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-aes128.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-atomic.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-bundle.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-byte-order.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-classifier.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-cmap.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-csum.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-flows.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-hash.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-heap.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-hindex.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-hmap.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-json.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-jsonrpc.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-list.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-lockfile.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-multipath.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-netflow.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-odp.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-ofpbuf.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-ovn.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-packets.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-random.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-rcu.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-reconnect.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-rstp.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-sflow.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-sha1.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-stp.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-util.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-uuid.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-bitmap.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-vconn.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-aa.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-unix-socket.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-netlink-conntrack.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ovstest$(EXEEXT): $(tests_ovstest_OBJECTS) $(tests_ovstest_DEPENDENCIES) $(EXTRA_tests_ovstest_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ovstest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ovstest_OBJECTS) $(tests_ovstest_LDADD) $(LIBS) tests/dpdk/$(am__dirstamp): @$(MKDIR_P) tests/dpdk @: > tests/dpdk/$(am__dirstamp) tests/dpdk/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tests/dpdk/$(DEPDIR) @: > tests/dpdk/$(DEPDIR)/$(am__dirstamp) tests/dpdk/ring_client.$(OBJEXT): tests/dpdk/$(am__dirstamp) \ tests/dpdk/$(DEPDIR)/$(am__dirstamp) tests/test-dpdkr$(EXEEXT): $(tests_test_dpdkr_OBJECTS) $(tests_test_dpdkr_DEPENDENCIES) $(EXTRA_tests_test_dpdkr_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/test-dpdkr$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_test_dpdkr_OBJECTS) $(tests_test_dpdkr_LDADD) $(LIBS) tests/test-lib.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-lib$(EXEEXT): $(tests_test_lib_OBJECTS) $(tests_test_lib_DEPENDENCIES) $(EXTRA_tests_test_lib_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/test-lib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_test_lib_OBJECTS) $(tests_test_lib_LDADD) $(LIBS) tests/test-ovsdb.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/idltest.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-ovsdb$(EXEEXT): $(tests_test_ovsdb_OBJECTS) $(tests_test_ovsdb_DEPENDENCIES) $(EXTRA_tests_test_ovsdb_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/test-ovsdb$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_test_ovsdb_OBJECTS) $(tests_test_ovsdb_LDADD) $(LIBS) tests/test-strtok_r.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-strtok_r$(EXEEXT): $(tests_test_strtok_r_OBJECTS) $(tests_test_strtok_r_DEPENDENCIES) $(EXTRA_tests_test_strtok_r_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/test-strtok_r$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_test_strtok_r_OBJECTS) $(tests_test_strtok_r_LDADD) $(LIBS) tests/test-type-props.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/test-type-props$(EXEEXT): $(tests_test_type_props_OBJECTS) $(tests_test_type_props_DEPENDENCIES) $(EXTRA_tests_test_type_props_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/test-type-props$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_test_type_props_OBJECTS) $(tests_test_type_props_LDADD) $(LIBS) utilities/$(am__dirstamp): @$(MKDIR_P) utilities @: > utilities/$(am__dirstamp) utilities/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) utilities/$(DEPDIR) @: > utilities/$(DEPDIR)/$(am__dirstamp) utilities/nlmon.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/nlmon$(EXEEXT): $(utilities_nlmon_OBJECTS) $(utilities_nlmon_DEPENDENCIES) $(EXTRA_utilities_nlmon_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/nlmon$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_nlmon_OBJECTS) $(utilities_nlmon_LDADD) $(LIBS) utilities/ovs-appctl.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-appctl$(EXEEXT): $(utilities_ovs_appctl_OBJECTS) $(utilities_ovs_appctl_DEPENDENCIES) $(EXTRA_utilities_ovs_appctl_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-appctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_appctl_OBJECTS) $(utilities_ovs_appctl_LDADD) $(LIBS) utilities/ovs-benchmark.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-benchmark$(EXEEXT): $(utilities_ovs_benchmark_OBJECTS) $(utilities_ovs_benchmark_DEPENDENCIES) $(EXTRA_utilities_ovs_benchmark_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-benchmark$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_benchmark_OBJECTS) $(utilities_ovs_benchmark_LDADD) $(LIBS) utilities/ovs-dpctl.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-dpctl$(EXEEXT): $(utilities_ovs_dpctl_OBJECTS) $(utilities_ovs_dpctl_DEPENDENCIES) $(EXTRA_utilities_ovs_dpctl_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-dpctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_dpctl_OBJECTS) $(utilities_ovs_dpctl_LDADD) $(LIBS) utilities/ovs-ofctl.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-ofctl$(EXEEXT): $(utilities_ovs_ofctl_OBJECTS) $(utilities_ovs_ofctl_DEPENDENCIES) $(EXTRA_utilities_ovs_ofctl_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-ofctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_ofctl_OBJECTS) $(utilities_ovs_ofctl_LDADD) $(LIBS) utilities/ovs-testcontroller.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-testcontroller$(EXEEXT): $(utilities_ovs_testcontroller_OBJECTS) $(utilities_ovs_testcontroller_DEPENDENCIES) $(EXTRA_utilities_ovs_testcontroller_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-testcontroller$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_testcontroller_OBJECTS) $(utilities_ovs_testcontroller_LDADD) $(LIBS) utilities/ovs-vlan-bug-workaround.$(OBJEXT): \ utilities/$(am__dirstamp) utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-vlan-bug-workaround$(EXEEXT): $(utilities_ovs_vlan_bug_workaround_OBJECTS) $(utilities_ovs_vlan_bug_workaround_DEPENDENCIES) $(EXTRA_utilities_ovs_vlan_bug_workaround_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-vlan-bug-workaround$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_vlan_bug_workaround_OBJECTS) $(utilities_ovs_vlan_bug_workaround_LDADD) $(LIBS) utilities/ovs-vsctl.$(OBJEXT): utilities/$(am__dirstamp) \ utilities/$(DEPDIR)/$(am__dirstamp) utilities/ovs-vsctl$(EXEEXT): $(utilities_ovs_vsctl_OBJECTS) $(utilities_ovs_vsctl_DEPENDENCIES) $(EXTRA_utilities_ovs_vsctl_DEPENDENCIES) utilities/$(am__dirstamp) @rm -f utilities/ovs-vsctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(utilities_ovs_vsctl_OBJECTS) $(utilities_ovs_vsctl_LDADD) $(LIBS) vswitchd/$(am__dirstamp): @$(MKDIR_P) vswitchd @: > vswitchd/$(am__dirstamp) vswitchd/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) vswitchd/$(DEPDIR) @: > vswitchd/$(DEPDIR)/$(am__dirstamp) vswitchd/bridge.$(OBJEXT): vswitchd/$(am__dirstamp) \ vswitchd/$(DEPDIR)/$(am__dirstamp) vswitchd/ovs-vswitchd.$(OBJEXT): vswitchd/$(am__dirstamp) \ vswitchd/$(DEPDIR)/$(am__dirstamp) vswitchd/system-stats.$(OBJEXT): vswitchd/$(am__dirstamp) \ vswitchd/$(DEPDIR)/$(am__dirstamp) vswitchd/xenserver.$(OBJEXT): vswitchd/$(am__dirstamp) \ vswitchd/$(DEPDIR)/$(am__dirstamp) vswitchd/ovs-vswitchd$(EXEEXT): $(vswitchd_ovs_vswitchd_OBJECTS) $(vswitchd_ovs_vswitchd_DEPENDENCIES) $(EXTRA_vswitchd_ovs_vswitchd_DEPENDENCIES) vswitchd/$(am__dirstamp) @rm -f vswitchd/ovs-vswitchd$(EXEEXT) $(AM_V_CCLD)$(vswitchd_ovs_vswitchd_LINK) $(vswitchd_ovs_vswitchd_OBJECTS) $(vswitchd_ovs_vswitchd_LDADD) $(LIBS) vtep/vtep-ctl.$(OBJEXT): vtep/$(am__dirstamp) \ vtep/$(DEPDIR)/$(am__dirstamp) vtep/vtep-ctl$(EXEEXT): $(vtep_vtep_ctl_OBJECTS) $(vtep_vtep_ctl_DEPENDENCIES) $(EXTRA_vtep_vtep_ctl_DEPENDENCIES) vtep/$(am__dirstamp) @rm -f vtep/vtep-ctl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(vtep_vtep_ctl_OBJECTS) $(vtep_vtep_ctl_LDADD) $(LIBS) install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) install-completionSCRIPTS: $(completion_SCRIPTS) @$(NORMAL_INSTALL) @list='$(completion_SCRIPTS)'; test -n "$(completiondir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(completiondir)'"; \ $(MKDIR_P) "$(DESTDIR)$(completiondir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(completiondir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(completiondir)$$dir" || exit $$?; \ } \ ; done uninstall-completionSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(completion_SCRIPTS)'; test -n "$(completiondir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(completiondir)'; $(am__uninstall_files_from_dir) install-dist_pkgdataSCRIPTS: $(dist_pkgdata_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkgdatadir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkgdatadir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_pkgdataSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_pkgdata_SCRIPTS)'; test -n "$(pkgdatadir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) install-dist_sbinSCRIPTS: $(dist_sbin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) install-dist_scriptsSCRIPTS: $(dist_scripts_SCRIPTS) @$(NORMAL_INSTALL) @list='$(dist_scripts_SCRIPTS)'; test -n "$(scriptsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scriptsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scriptsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(scriptsdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(scriptsdir)$$dir" || exit $$?; \ } \ ; done uninstall-dist_scriptsSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(dist_scripts_SCRIPTS)'; test -n "$(scriptsdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(scriptsdir)'; $(am__uninstall_files_from_dir) install-sbinSCRIPTS: $(sbin_SCRIPTS) @$(NORMAL_INSTALL) @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) install-scriptsSCRIPTS: $(scripts_SCRIPTS) @$(NORMAL_INSTALL) @list='$(scripts_SCRIPTS)'; test -n "$(scriptsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scriptsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scriptsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n' \ -e 'h;s|.*|.|' \ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) { files[d] = files[d] " " $$1; \ if (++n[d] == $(am__install_max)) { \ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ else { print "f", d "/" $$4, $$1 } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(scriptsdir)$$dir'"; \ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(scriptsdir)$$dir" || exit $$?; \ } \ ; done uninstall-scriptsSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(scripts_SCRIPTS)'; test -n "$(scriptsdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ dir='$(DESTDIR)$(scriptsdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f lib/*.$(OBJEXT) -rm -f lib/*.lo -rm -f lib/lldp/*.$(OBJEXT) -rm -f lib/lldp/*.lo -rm -f ofproto/*.$(OBJEXT) -rm -f ofproto/*.lo -rm -f ovn/controller-vtep/*.$(OBJEXT) -rm -f ovn/controller/*.$(OBJEXT) -rm -f ovn/lib/*.$(OBJEXT) -rm -f ovn/lib/*.lo -rm -f ovn/northd/*.$(OBJEXT) -rm -f ovn/utilities/*.$(OBJEXT) -rm -f ovsdb/*.$(OBJEXT) -rm -f ovsdb/*.lo -rm -f tests/*.$(OBJEXT) -rm -f tests/dpdk/*.$(OBJEXT) -rm -f utilities/*.$(OBJEXT) -rm -f vswitchd/*.$(OBJEXT) -rm -f vtep/*.$(OBJEXT) -rm -f vtep/*.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/aes128.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/async-append-aio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/async-append-null.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/backtrace.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bfd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bundle.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/byteq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/cfm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/classifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/cmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/command-line.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/connectivity.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/coverage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/crc32c.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/csum.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ct-dpif.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/daemon-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/daemon-windows.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/daemon.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/db-ctl-base.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dhparams.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dirs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dp-packet.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dpctl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dpif-netdev.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dpif-netlink.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dpif.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dummy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/dynamic-string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/entropy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/fat-rwlock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/fatal-signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/flow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/getopt_long.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/getrusage-windows.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/guarded-list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/heap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hindex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hmapx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/id-pool.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/if-notifier-bsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/if-notifier-stub.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/if-notifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/jhash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/json.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/jsonrpc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lacp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/latch-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/latch-windows.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/learn.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/learning-switch.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lib_libsflow_la-sflow_agent.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lib_libsflow_la-sflow_poller.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lib_libsflow_la-sflow_receiver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lib_libsflow_la-sflow_sampler.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/lockfile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/mac-learning.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/mcast-snooping.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/memory.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/meta-flow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/multipath.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-bsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-dpdk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-dummy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-vport.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev-windows.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netdev.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netlink-conntrack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netlink-notifier.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netlink-socket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/netlink.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/nx-match.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/odp-execute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/odp-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-actions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-errors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-msgs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-parse.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-print.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofp-version-opt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ofpbuf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-atomic-locked.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-lldp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-numa.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-rcu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-router.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovs-thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovsdb-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovsdb-error.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovsdb-idl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovsdb-parser.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/ovsdb-types.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/packets.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/pcap-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/perf-counter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/poll-loop.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/process.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/pvector.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/random.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/rconn.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/reconnect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/route-table-bsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/route-table-stub.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/route-table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/rstp-state-machines.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/rstp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/rtbsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/rtnetlink.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/seq.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sha1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/shash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/signals.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/simap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/smap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/socket-util-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/socket-util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sort.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stdio.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream-fd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream-nossl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream-ssl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream-tcp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream-unix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/strsep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/svec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/syslog-direct.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/syslog-libc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/timer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/timeval.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/tnl-neigh-cache.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/tnl-ports.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/token-bucket.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/tun-metadata.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/unicode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/unixctl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/util.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/uuid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vconn-stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vconn.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vlan-bitmap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vlandev.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/vswitch-idl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/lldp/$(DEPDIR)/lldp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/lldp/$(DEPDIR)/lldpd-structs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lib/lldp/$(DEPDIR)/lldpd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-bond.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-bundles.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-collectors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-connmgr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-fail-open.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-in-band.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-names.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-netflow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-ipfix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-mirror.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-monitor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-rid.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-sflow.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-upcall.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-xlate.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-pinsched.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-pktbuf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ofproto/$(DEPDIR)/ofproto_libofproto_la-tunnel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller-vtep/$(DEPDIR)/binding.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller-vtep/$(DEPDIR)/gateway.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller-vtep/$(DEPDIR)/ovn-controller-vtep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller-vtep/$(DEPDIR)/vtep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/binding.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/chassis.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/encaps.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/lflow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/ofctrl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/ovn-controller.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/patch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/physical.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/controller/$(DEPDIR)/pinctrl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/lib/$(DEPDIR)/actions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/lib/$(DEPDIR)/expr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/lib/$(DEPDIR)/lex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/lib/$(DEPDIR)/ovn-nb-idl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/lib/$(DEPDIR)/ovn-sb-idl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/northd/$(DEPDIR)/ovn-northd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/utilities/$(DEPDIR)/ovn-nbctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovn/utilities/$(DEPDIR)/ovn-sbctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-column.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-condition.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-execution.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-jsonrpc-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-monitor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-mutation.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-ovsdb.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-query.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-row.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-table.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-transaction.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-trigger.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/idltest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ovstest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-aa.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-aes128.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-atomic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-bitmap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-bundle.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-byte-order.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-classifier.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-cmap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-csum.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-flows.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-hash.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-heap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-hindex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-hmap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-jsonrpc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-lib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-list.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-lockfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-multipath.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-netflow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-netlink-conntrack.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-odp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-ofpbuf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-ovn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-ovsdb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-packets.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-random.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-rcu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-reconnect.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-rstp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-sflow.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-sha1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-stp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-strtok_r.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-type-props.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-unix-socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-util.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-uuid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test-vconn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/dpdk/$(DEPDIR)/ring_client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/nlmon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-appctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-benchmark.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-dpctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-ofctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-testcontroller.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-vlan-bug-workaround.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@utilities/$(DEPDIR)/ovs-vsctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vswitchd/$(DEPDIR)/bridge.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vswitchd/$(DEPDIR)/ovs-vswitchd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vswitchd/$(DEPDIR)/system-stats.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vswitchd/$(DEPDIR)/xenserver.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vtep/$(DEPDIR)/vtep-ctl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@vtep/$(DEPDIR)/vtep-idl.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< lib/lib_libsflow_la-sflow_agent.lo: lib/sflow_agent.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -MT lib/lib_libsflow_la-sflow_agent.lo -MD -MP -MF lib/$(DEPDIR)/lib_libsflow_la-sflow_agent.Tpo -c -o lib/lib_libsflow_la-sflow_agent.lo `test -f 'lib/sflow_agent.c' || echo '$(srcdir)/'`lib/sflow_agent.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_libsflow_la-sflow_agent.Tpo lib/$(DEPDIR)/lib_libsflow_la-sflow_agent.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/sflow_agent.c' object='lib/lib_libsflow_la-sflow_agent.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -c -o lib/lib_libsflow_la-sflow_agent.lo `test -f 'lib/sflow_agent.c' || echo '$(srcdir)/'`lib/sflow_agent.c lib/lib_libsflow_la-sflow_sampler.lo: lib/sflow_sampler.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -MT lib/lib_libsflow_la-sflow_sampler.lo -MD -MP -MF lib/$(DEPDIR)/lib_libsflow_la-sflow_sampler.Tpo -c -o lib/lib_libsflow_la-sflow_sampler.lo `test -f 'lib/sflow_sampler.c' || echo '$(srcdir)/'`lib/sflow_sampler.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_libsflow_la-sflow_sampler.Tpo lib/$(DEPDIR)/lib_libsflow_la-sflow_sampler.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/sflow_sampler.c' object='lib/lib_libsflow_la-sflow_sampler.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -c -o lib/lib_libsflow_la-sflow_sampler.lo `test -f 'lib/sflow_sampler.c' || echo '$(srcdir)/'`lib/sflow_sampler.c lib/lib_libsflow_la-sflow_poller.lo: lib/sflow_poller.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -MT lib/lib_libsflow_la-sflow_poller.lo -MD -MP -MF lib/$(DEPDIR)/lib_libsflow_la-sflow_poller.Tpo -c -o lib/lib_libsflow_la-sflow_poller.lo `test -f 'lib/sflow_poller.c' || echo '$(srcdir)/'`lib/sflow_poller.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_libsflow_la-sflow_poller.Tpo lib/$(DEPDIR)/lib_libsflow_la-sflow_poller.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/sflow_poller.c' object='lib/lib_libsflow_la-sflow_poller.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -c -o lib/lib_libsflow_la-sflow_poller.lo `test -f 'lib/sflow_poller.c' || echo '$(srcdir)/'`lib/sflow_poller.c lib/lib_libsflow_la-sflow_receiver.lo: lib/sflow_receiver.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -MT lib/lib_libsflow_la-sflow_receiver.lo -MD -MP -MF lib/$(DEPDIR)/lib_libsflow_la-sflow_receiver.Tpo -c -o lib/lib_libsflow_la-sflow_receiver.lo `test -f 'lib/sflow_receiver.c' || echo '$(srcdir)/'`lib/sflow_receiver.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lib/$(DEPDIR)/lib_libsflow_la-sflow_receiver.Tpo lib/$(DEPDIR)/lib_libsflow_la-sflow_receiver.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lib/sflow_receiver.c' object='lib/lib_libsflow_la-sflow_receiver.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lib_libsflow_la_CPPFLAGS) $(CPPFLAGS) $(lib_libsflow_la_CFLAGS) $(CFLAGS) -c -o lib/lib_libsflow_la-sflow_receiver.lo `test -f 'lib/sflow_receiver.c' || echo '$(srcdir)/'`lib/sflow_receiver.c ofproto/ofproto_libofproto_la-bond.lo: ofproto/bond.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-bond.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-bond.Tpo -c -o ofproto/ofproto_libofproto_la-bond.lo `test -f 'ofproto/bond.c' || echo '$(srcdir)/'`ofproto/bond.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-bond.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-bond.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/bond.c' object='ofproto/ofproto_libofproto_la-bond.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-bond.lo `test -f 'ofproto/bond.c' || echo '$(srcdir)/'`ofproto/bond.c ofproto/ofproto_libofproto_la-collectors.lo: ofproto/collectors.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-collectors.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-collectors.Tpo -c -o ofproto/ofproto_libofproto_la-collectors.lo `test -f 'ofproto/collectors.c' || echo '$(srcdir)/'`ofproto/collectors.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-collectors.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-collectors.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/collectors.c' object='ofproto/ofproto_libofproto_la-collectors.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-collectors.lo `test -f 'ofproto/collectors.c' || echo '$(srcdir)/'`ofproto/collectors.c ofproto/ofproto_libofproto_la-connmgr.lo: ofproto/connmgr.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-connmgr.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-connmgr.Tpo -c -o ofproto/ofproto_libofproto_la-connmgr.lo `test -f 'ofproto/connmgr.c' || echo '$(srcdir)/'`ofproto/connmgr.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-connmgr.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-connmgr.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/connmgr.c' object='ofproto/ofproto_libofproto_la-connmgr.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-connmgr.lo `test -f 'ofproto/connmgr.c' || echo '$(srcdir)/'`ofproto/connmgr.c ofproto/ofproto_libofproto_la-fail-open.lo: ofproto/fail-open.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-fail-open.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-fail-open.Tpo -c -o ofproto/ofproto_libofproto_la-fail-open.lo `test -f 'ofproto/fail-open.c' || echo '$(srcdir)/'`ofproto/fail-open.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-fail-open.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-fail-open.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/fail-open.c' object='ofproto/ofproto_libofproto_la-fail-open.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-fail-open.lo `test -f 'ofproto/fail-open.c' || echo '$(srcdir)/'`ofproto/fail-open.c ofproto/ofproto_libofproto_la-in-band.lo: ofproto/in-band.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-in-band.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-in-band.Tpo -c -o ofproto/ofproto_libofproto_la-in-band.lo `test -f 'ofproto/in-band.c' || echo '$(srcdir)/'`ofproto/in-band.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-in-band.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-in-band.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/in-band.c' object='ofproto/ofproto_libofproto_la-in-band.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-in-band.lo `test -f 'ofproto/in-band.c' || echo '$(srcdir)/'`ofproto/in-band.c ofproto/ofproto_libofproto_la-names.lo: ofproto/names.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-names.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-names.Tpo -c -o ofproto/ofproto_libofproto_la-names.lo `test -f 'ofproto/names.c' || echo '$(srcdir)/'`ofproto/names.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-names.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-names.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/names.c' object='ofproto/ofproto_libofproto_la-names.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-names.lo `test -f 'ofproto/names.c' || echo '$(srcdir)/'`ofproto/names.c ofproto/ofproto_libofproto_la-netflow.lo: ofproto/netflow.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-netflow.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-netflow.Tpo -c -o ofproto/ofproto_libofproto_la-netflow.lo `test -f 'ofproto/netflow.c' || echo '$(srcdir)/'`ofproto/netflow.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-netflow.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-netflow.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/netflow.c' object='ofproto/ofproto_libofproto_la-netflow.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-netflow.lo `test -f 'ofproto/netflow.c' || echo '$(srcdir)/'`ofproto/netflow.c ofproto/ofproto_libofproto_la-ofproto.lo: ofproto/ofproto.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto.lo `test -f 'ofproto/ofproto.c' || echo '$(srcdir)/'`ofproto/ofproto.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto.c' object='ofproto/ofproto_libofproto_la-ofproto.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto.lo `test -f 'ofproto/ofproto.c' || echo '$(srcdir)/'`ofproto/ofproto.c ofproto/ofproto_libofproto_la-ofproto-dpif.lo: ofproto/ofproto-dpif.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif.lo `test -f 'ofproto/ofproto-dpif.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif.lo `test -f 'ofproto/ofproto-dpif.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif.c ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo: ofproto/ofproto-dpif-ipfix.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-ipfix.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo `test -f 'ofproto/ofproto-dpif-ipfix.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-ipfix.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-ipfix.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-ipfix.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-ipfix.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-ipfix.lo `test -f 'ofproto/ofproto-dpif-ipfix.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-ipfix.c ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo: ofproto/ofproto-dpif-mirror.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-mirror.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo `test -f 'ofproto/ofproto-dpif-mirror.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-mirror.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-mirror.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-mirror.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-mirror.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-mirror.lo `test -f 'ofproto/ofproto-dpif-mirror.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-mirror.c ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo: ofproto/ofproto-dpif-monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-monitor.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo `test -f 'ofproto/ofproto-dpif-monitor.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-monitor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-monitor.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-monitor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-monitor.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-monitor.lo `test -f 'ofproto/ofproto-dpif-monitor.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-monitor.c ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo: ofproto/ofproto-dpif-rid.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-rid.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo `test -f 'ofproto/ofproto-dpif-rid.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-rid.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-rid.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-rid.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-rid.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-rid.lo `test -f 'ofproto/ofproto-dpif-rid.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-rid.c ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo: ofproto/ofproto-dpif-sflow.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-sflow.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo `test -f 'ofproto/ofproto-dpif-sflow.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-sflow.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-sflow.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-sflow.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-sflow.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-sflow.lo `test -f 'ofproto/ofproto-dpif-sflow.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-sflow.c ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo: ofproto/ofproto-dpif-upcall.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-upcall.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo `test -f 'ofproto/ofproto-dpif-upcall.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-upcall.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-upcall.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-upcall.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-upcall.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-upcall.lo `test -f 'ofproto/ofproto-dpif-upcall.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-upcall.c ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo: ofproto/ofproto-dpif-xlate.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-xlate.Tpo -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo `test -f 'ofproto/ofproto-dpif-xlate.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-xlate.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-xlate.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-ofproto-dpif-xlate.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/ofproto-dpif-xlate.c' object='ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-ofproto-dpif-xlate.lo `test -f 'ofproto/ofproto-dpif-xlate.c' || echo '$(srcdir)/'`ofproto/ofproto-dpif-xlate.c ofproto/ofproto_libofproto_la-pktbuf.lo: ofproto/pktbuf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-pktbuf.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-pktbuf.Tpo -c -o ofproto/ofproto_libofproto_la-pktbuf.lo `test -f 'ofproto/pktbuf.c' || echo '$(srcdir)/'`ofproto/pktbuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-pktbuf.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-pktbuf.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/pktbuf.c' object='ofproto/ofproto_libofproto_la-pktbuf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-pktbuf.lo `test -f 'ofproto/pktbuf.c' || echo '$(srcdir)/'`ofproto/pktbuf.c ofproto/ofproto_libofproto_la-pinsched.lo: ofproto/pinsched.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-pinsched.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-pinsched.Tpo -c -o ofproto/ofproto_libofproto_la-pinsched.lo `test -f 'ofproto/pinsched.c' || echo '$(srcdir)/'`ofproto/pinsched.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-pinsched.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-pinsched.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/pinsched.c' object='ofproto/ofproto_libofproto_la-pinsched.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-pinsched.lo `test -f 'ofproto/pinsched.c' || echo '$(srcdir)/'`ofproto/pinsched.c ofproto/ofproto_libofproto_la-tunnel.lo: ofproto/tunnel.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-tunnel.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-tunnel.Tpo -c -o ofproto/ofproto_libofproto_la-tunnel.lo `test -f 'ofproto/tunnel.c' || echo '$(srcdir)/'`ofproto/tunnel.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-tunnel.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-tunnel.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/tunnel.c' object='ofproto/ofproto_libofproto_la-tunnel.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-tunnel.lo `test -f 'ofproto/tunnel.c' || echo '$(srcdir)/'`ofproto/tunnel.c ofproto/ofproto_libofproto_la-bundles.lo: ofproto/bundles.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -MT ofproto/ofproto_libofproto_la-bundles.lo -MD -MP -MF ofproto/$(DEPDIR)/ofproto_libofproto_la-bundles.Tpo -c -o ofproto/ofproto_libofproto_la-bundles.lo `test -f 'ofproto/bundles.c' || echo '$(srcdir)/'`ofproto/bundles.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ofproto/$(DEPDIR)/ofproto_libofproto_la-bundles.Tpo ofproto/$(DEPDIR)/ofproto_libofproto_la-bundles.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ofproto/bundles.c' object='ofproto/ofproto_libofproto_la-bundles.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ofproto_libofproto_la_CPPFLAGS) $(CPPFLAGS) $(ofproto_libofproto_la_CFLAGS) $(CFLAGS) -c -o ofproto/ofproto_libofproto_la-bundles.lo `test -f 'ofproto/bundles.c' || echo '$(srcdir)/'`ofproto/bundles.c ovsdb/ovsdb_libovsdb_la-column.lo: ovsdb/column.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-column.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-column.Tpo -c -o ovsdb/ovsdb_libovsdb_la-column.lo `test -f 'ovsdb/column.c' || echo '$(srcdir)/'`ovsdb/column.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-column.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-column.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/column.c' object='ovsdb/ovsdb_libovsdb_la-column.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-column.lo `test -f 'ovsdb/column.c' || echo '$(srcdir)/'`ovsdb/column.c ovsdb/ovsdb_libovsdb_la-condition.lo: ovsdb/condition.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-condition.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-condition.Tpo -c -o ovsdb/ovsdb_libovsdb_la-condition.lo `test -f 'ovsdb/condition.c' || echo '$(srcdir)/'`ovsdb/condition.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-condition.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-condition.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/condition.c' object='ovsdb/ovsdb_libovsdb_la-condition.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-condition.lo `test -f 'ovsdb/condition.c' || echo '$(srcdir)/'`ovsdb/condition.c ovsdb/ovsdb_libovsdb_la-execution.lo: ovsdb/execution.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-execution.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-execution.Tpo -c -o ovsdb/ovsdb_libovsdb_la-execution.lo `test -f 'ovsdb/execution.c' || echo '$(srcdir)/'`ovsdb/execution.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-execution.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-execution.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/execution.c' object='ovsdb/ovsdb_libovsdb_la-execution.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-execution.lo `test -f 'ovsdb/execution.c' || echo '$(srcdir)/'`ovsdb/execution.c ovsdb/ovsdb_libovsdb_la-file.lo: ovsdb/file.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-file.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-file.Tpo -c -o ovsdb/ovsdb_libovsdb_la-file.lo `test -f 'ovsdb/file.c' || echo '$(srcdir)/'`ovsdb/file.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-file.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-file.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/file.c' object='ovsdb/ovsdb_libovsdb_la-file.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-file.lo `test -f 'ovsdb/file.c' || echo '$(srcdir)/'`ovsdb/file.c ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo: ovsdb/jsonrpc-server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-jsonrpc-server.Tpo -c -o ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo `test -f 'ovsdb/jsonrpc-server.c' || echo '$(srcdir)/'`ovsdb/jsonrpc-server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-jsonrpc-server.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-jsonrpc-server.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/jsonrpc-server.c' object='ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-jsonrpc-server.lo `test -f 'ovsdb/jsonrpc-server.c' || echo '$(srcdir)/'`ovsdb/jsonrpc-server.c ovsdb/ovsdb_libovsdb_la-log.lo: ovsdb/log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-log.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-log.Tpo -c -o ovsdb/ovsdb_libovsdb_la-log.lo `test -f 'ovsdb/log.c' || echo '$(srcdir)/'`ovsdb/log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-log.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-log.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/log.c' object='ovsdb/ovsdb_libovsdb_la-log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-log.lo `test -f 'ovsdb/log.c' || echo '$(srcdir)/'`ovsdb/log.c ovsdb/ovsdb_libovsdb_la-mutation.lo: ovsdb/mutation.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-mutation.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-mutation.Tpo -c -o ovsdb/ovsdb_libovsdb_la-mutation.lo `test -f 'ovsdb/mutation.c' || echo '$(srcdir)/'`ovsdb/mutation.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-mutation.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-mutation.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/mutation.c' object='ovsdb/ovsdb_libovsdb_la-mutation.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-mutation.lo `test -f 'ovsdb/mutation.c' || echo '$(srcdir)/'`ovsdb/mutation.c ovsdb/ovsdb_libovsdb_la-ovsdb.lo: ovsdb/ovsdb.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-ovsdb.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-ovsdb.Tpo -c -o ovsdb/ovsdb_libovsdb_la-ovsdb.lo `test -f 'ovsdb/ovsdb.c' || echo '$(srcdir)/'`ovsdb/ovsdb.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-ovsdb.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-ovsdb.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/ovsdb.c' object='ovsdb/ovsdb_libovsdb_la-ovsdb.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-ovsdb.lo `test -f 'ovsdb/ovsdb.c' || echo '$(srcdir)/'`ovsdb/ovsdb.c ovsdb/ovsdb_libovsdb_la-monitor.lo: ovsdb/monitor.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-monitor.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-monitor.Tpo -c -o ovsdb/ovsdb_libovsdb_la-monitor.lo `test -f 'ovsdb/monitor.c' || echo '$(srcdir)/'`ovsdb/monitor.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-monitor.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-monitor.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/monitor.c' object='ovsdb/ovsdb_libovsdb_la-monitor.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-monitor.lo `test -f 'ovsdb/monitor.c' || echo '$(srcdir)/'`ovsdb/monitor.c ovsdb/ovsdb_libovsdb_la-query.lo: ovsdb/query.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-query.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-query.Tpo -c -o ovsdb/ovsdb_libovsdb_la-query.lo `test -f 'ovsdb/query.c' || echo '$(srcdir)/'`ovsdb/query.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-query.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-query.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/query.c' object='ovsdb/ovsdb_libovsdb_la-query.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-query.lo `test -f 'ovsdb/query.c' || echo '$(srcdir)/'`ovsdb/query.c ovsdb/ovsdb_libovsdb_la-row.lo: ovsdb/row.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-row.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-row.Tpo -c -o ovsdb/ovsdb_libovsdb_la-row.lo `test -f 'ovsdb/row.c' || echo '$(srcdir)/'`ovsdb/row.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-row.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-row.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/row.c' object='ovsdb/ovsdb_libovsdb_la-row.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-row.lo `test -f 'ovsdb/row.c' || echo '$(srcdir)/'`ovsdb/row.c ovsdb/ovsdb_libovsdb_la-server.lo: ovsdb/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-server.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-server.Tpo -c -o ovsdb/ovsdb_libovsdb_la-server.lo `test -f 'ovsdb/server.c' || echo '$(srcdir)/'`ovsdb/server.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-server.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-server.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/server.c' object='ovsdb/ovsdb_libovsdb_la-server.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-server.lo `test -f 'ovsdb/server.c' || echo '$(srcdir)/'`ovsdb/server.c ovsdb/ovsdb_libovsdb_la-table.lo: ovsdb/table.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-table.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-table.Tpo -c -o ovsdb/ovsdb_libovsdb_la-table.lo `test -f 'ovsdb/table.c' || echo '$(srcdir)/'`ovsdb/table.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-table.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-table.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/table.c' object='ovsdb/ovsdb_libovsdb_la-table.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-table.lo `test -f 'ovsdb/table.c' || echo '$(srcdir)/'`ovsdb/table.c ovsdb/ovsdb_libovsdb_la-trigger.lo: ovsdb/trigger.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-trigger.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-trigger.Tpo -c -o ovsdb/ovsdb_libovsdb_la-trigger.lo `test -f 'ovsdb/trigger.c' || echo '$(srcdir)/'`ovsdb/trigger.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-trigger.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-trigger.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/trigger.c' object='ovsdb/ovsdb_libovsdb_la-trigger.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-trigger.lo `test -f 'ovsdb/trigger.c' || echo '$(srcdir)/'`ovsdb/trigger.c ovsdb/ovsdb_libovsdb_la-transaction.lo: ovsdb/transaction.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -MT ovsdb/ovsdb_libovsdb_la-transaction.lo -MD -MP -MF ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-transaction.Tpo -c -o ovsdb/ovsdb_libovsdb_la-transaction.lo `test -f 'ovsdb/transaction.c' || echo '$(srcdir)/'`ovsdb/transaction.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-transaction.Tpo ovsdb/$(DEPDIR)/ovsdb_libovsdb_la-transaction.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ovsdb/transaction.c' object='ovsdb/ovsdb_libovsdb_la-transaction.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ovsdb_libovsdb_la_CPPFLAGS) $(CPPFLAGS) $(ovsdb_libovsdb_la_CFLAGS) $(CFLAGS) -c -o ovsdb/ovsdb_libovsdb_la-transaction.lo `test -f 'ovsdb/transaction.c' || echo '$(srcdir)/'`ovsdb/transaction.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf lib/.libs lib/_libs -rm -rf lib/lldp/.libs lib/lldp/_libs -rm -rf ofproto/.libs ofproto/_libs -rm -rf ovn/controller/.libs ovn/controller/_libs -rm -rf ovn/controller-vtep/.libs ovn/controller-vtep/_libs -rm -rf ovn/lib/.libs ovn/lib/_libs -rm -rf ovn/northd/.libs ovn/northd/_libs -rm -rf ovn/utilities/.libs ovn/utilities/_libs -rm -rf ovsdb/.libs ovsdb/_libs -rm -rf tests/.libs tests/_libs -rm -rf utilities/.libs utilities/_libs -rm -rf vswitchd/.libs vswitchd/_libs -rm -rf vtep/.libs vtep/_libs distclean-libtool: -rm -f libtool config.lt install-man1: $(dist_man_MANS) $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(dist_man_MANS) $(man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-man5: $(dist_man_MANS) $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(dist_man_MANS) $(man_MANS)'; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.5[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) install-man7: $(dist_man_MANS) $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(dist_man_MANS) $(man_MANS)'; \ test -n "$(man7dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man7dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man7dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.7[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man7dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man7dir)" || exit $$?; }; \ done; } uninstall-man7: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man7dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.7[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man7dir)'; $(am__uninstall_files_from_dir) install-man8: $(dist_man_MANS) $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(dist_man_MANS) $(man_MANS)'; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.8[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) install-dist_pkgdataDATA: $(dist_pkgdata_DATA) @$(NORMAL_INSTALL) @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \ done uninstall-dist_pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(dist_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) install-dist_scriptsDATA: $(dist_scripts_DATA) @$(NORMAL_INSTALL) @list='$(dist_scripts_DATA)'; test -n "$(scriptsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scriptsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scriptsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(scriptsdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(scriptsdir)" || exit $$?; \ done uninstall-dist_scriptsDATA: @$(NORMAL_UNINSTALL) @list='$(dist_scripts_DATA)'; test -n "$(scriptsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(scriptsdir)'; $(am__uninstall_files_from_dir) install-nobase_pkgdataDATA: $(nobase_pkgdata_DATA) @$(NORMAL_INSTALL) @list='$(nobase_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ fi; \ $(am__nobase_list) | while read dir files; do \ xfiles=; for file in $$files; do \ if test -f "$$file"; then xfiles="$$xfiles $$file"; \ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ test -z "$$xfiles" || { \ test "x$$dir" = x. || { \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)/$$dir'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)/$$dir"; }; \ echo " $(INSTALL_DATA) $$xfiles '$(DESTDIR)$(pkgdatadir)/$$dir'"; \ $(INSTALL_DATA) $$xfiles "$(DESTDIR)$(pkgdatadir)/$$dir" || exit $$?; }; \ done uninstall-nobase_pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(nobase_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) install-pkgdataDATA: $(pkgdata_DATA) @$(NORMAL_INSTALL) @list='$(pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgdatadir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgdatadir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgdatadir)" || exit $$?; \ done uninstall-pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) install-scriptsDATA: $(scripts_DATA) @$(NORMAL_INSTALL) @list='$(scripts_DATA)'; test -n "$(scriptsdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(scriptsdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(scriptsdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(scriptsdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(scriptsdir)" || exit $$?; \ done uninstall-scriptsDATA: @$(NORMAL_UNINSTALL) @list='$(scripts_DATA)'; test -n "$(scriptsdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(scriptsdir)'; $(am__uninstall_files_from_dir) install-openflowincludeHEADERS: $(openflowinclude_HEADERS) @$(NORMAL_INSTALL) @list='$(openflowinclude_HEADERS)'; test -n "$(openflowincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(openflowincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(openflowincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(openflowincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(openflowincludedir)" || exit $$?; \ done uninstall-openflowincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(openflowinclude_HEADERS)'; test -n "$(openflowincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(openflowincludedir)'; $(am__uninstall_files_from_dir) install-openvswitchincludeHEADERS: $(openvswitchinclude_HEADERS) @$(NORMAL_INSTALL) @list='$(openvswitchinclude_HEADERS)'; test -n "$(openvswitchincludedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(openvswitchincludedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(openvswitchincludedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(openvswitchincludedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(openvswitchincludedir)" || exit $$?; \ done uninstall-openvswitchincludeHEADERS: @$(NORMAL_UNINSTALL) @list='$(openvswitchinclude_HEADERS)'; test -n "$(openvswitchincludedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(openvswitchincludedir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_SCRIPTS) $(dist_check_SCRIPTS) \ $(check_DATA) $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-recursive all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) \ $(HEADERS) config.h all-local install-binPROGRAMS: install-libLTLIBRARIES installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(completiondir)" "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(pkgdatadir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(openflowincludedir)" "$(DESTDIR)$(openvswitchincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f lib/$(DEPDIR)/$(am__dirstamp) -rm -f lib/$(am__dirstamp) -rm -f lib/lldp/$(DEPDIR)/$(am__dirstamp) -rm -f lib/lldp/$(am__dirstamp) -rm -f ofproto/$(DEPDIR)/$(am__dirstamp) -rm -f ofproto/$(am__dirstamp) -rm -f ovn/controller-vtep/$(DEPDIR)/$(am__dirstamp) -rm -f ovn/controller-vtep/$(am__dirstamp) -rm -f ovn/controller/$(DEPDIR)/$(am__dirstamp) -rm -f ovn/controller/$(am__dirstamp) -rm -f ovn/lib/$(DEPDIR)/$(am__dirstamp) -rm -f ovn/lib/$(am__dirstamp) -rm -f ovn/northd/$(DEPDIR)/$(am__dirstamp) -rm -f ovn/northd/$(am__dirstamp) -rm -f ovn/utilities/$(DEPDIR)/$(am__dirstamp) -rm -f ovn/utilities/$(am__dirstamp) -rm -f ovsdb/$(DEPDIR)/$(am__dirstamp) -rm -f ovsdb/$(am__dirstamp) -rm -f tests/$(DEPDIR)/$(am__dirstamp) -rm -f tests/$(am__dirstamp) -rm -f tests/dpdk/$(DEPDIR)/$(am__dirstamp) -rm -f tests/dpdk/$(am__dirstamp) -rm -f utilities/$(DEPDIR)/$(am__dirstamp) -rm -f utilities/$(am__dirstamp) -rm -f vswitchd/$(DEPDIR)/$(am__dirstamp) -rm -f vswitchd/$(am__dirstamp) -rm -f vtep/$(DEPDIR)/$(am__dirstamp) -rm -f vtep/$(am__dirstamp) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-recursive clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool clean-local clean-noinstPROGRAMS \ clean-sbinPROGRAMS mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf lib/$(DEPDIR) lib/lldp/$(DEPDIR) ofproto/$(DEPDIR) ovn/controller-vtep/$(DEPDIR) ovn/controller/$(DEPDIR) ovn/lib/$(DEPDIR) ovn/northd/$(DEPDIR) ovn/utilities/$(DEPDIR) ovsdb/$(DEPDIR) tests/$(DEPDIR) tests/dpdk/$(DEPDIR) utilities/$(DEPDIR) vswitchd/$(DEPDIR) vtep/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-completionSCRIPTS install-data-local \ install-dist_pkgdataDATA install-dist_pkgdataSCRIPTS \ install-dist_scriptsDATA install-dist_scriptsSCRIPTS \ install-man install-nobase_pkgdataDATA \ install-openflowincludeHEADERS \ install-openvswitchincludeHEADERS install-pkgconfigDATA \ install-pkgdataDATA install-scriptsDATA install-scriptsSCRIPTS install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-binPROGRAMS install-binSCRIPTS \ install-dist_sbinSCRIPTS install-libLTLIBRARIES \ install-sbinPROGRAMS install-sbinSCRIPTS install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-man1 install-man5 install-man7 install-man8 install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -rf lib/$(DEPDIR) lib/lldp/$(DEPDIR) ofproto/$(DEPDIR) ovn/controller-vtep/$(DEPDIR) ovn/controller/$(DEPDIR) ovn/lib/$(DEPDIR) ovn/northd/$(DEPDIR) ovn/utilities/$(DEPDIR) ovsdb/$(DEPDIR) tests/$(DEPDIR) tests/dpdk/$(DEPDIR) utilities/$(DEPDIR) vswitchd/$(DEPDIR) vtep/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-completionSCRIPTS uninstall-dist_pkgdataDATA \ uninstall-dist_pkgdataSCRIPTS uninstall-dist_sbinSCRIPTS \ uninstall-dist_scriptsDATA uninstall-dist_scriptsSCRIPTS \ uninstall-libLTLIBRARIES uninstall-local uninstall-man \ uninstall-nobase_pkgdataDATA uninstall-openflowincludeHEADERS \ uninstall-openvswitchincludeHEADERS uninstall-pkgconfigDATA \ uninstall-pkgdataDATA uninstall-sbinPROGRAMS \ uninstall-sbinSCRIPTS uninstall-scriptsDATA \ uninstall-scriptsSCRIPTS uninstall-man: uninstall-man1 uninstall-man5 uninstall-man7 \ uninstall-man8 .MAKE: $(am__recursive_targets) all check check-am install install-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \ am--refresh check check-am check-local clean clean-binPROGRAMS \ clean-cscope clean-generic clean-libLTLIBRARIES clean-libtool \ clean-local clean-noinstPROGRAMS clean-sbinPROGRAMS cscope \ cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-hook dist-lzip dist-shar dist-tarZ dist-xz \ dist-zip distcheck distclean distclean-compile \ distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-binSCRIPTS \ install-completionSCRIPTS install-data install-data-am \ install-data-local install-dist_pkgdataDATA \ install-dist_pkgdataSCRIPTS install-dist_sbinSCRIPTS \ install-dist_scriptsDATA install-dist_scriptsSCRIPTS \ install-dvi install-dvi-am install-exec install-exec-am \ install-html install-html-am install-info install-info-am \ install-libLTLIBRARIES install-man install-man1 install-man5 \ install-man7 install-man8 install-nobase_pkgdataDATA \ install-openflowincludeHEADERS \ install-openvswitchincludeHEADERS install-pdf install-pdf-am \ install-pkgconfigDATA install-pkgdataDATA install-ps \ install-ps-am install-sbinPROGRAMS install-sbinSCRIPTS \ install-scriptsDATA install-scriptsSCRIPTS install-strip \ installcheck installcheck-am installdirs installdirs-am \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-completionSCRIPTS uninstall-dist_pkgdataDATA \ uninstall-dist_pkgdataSCRIPTS uninstall-dist_sbinSCRIPTS \ uninstall-dist_scriptsDATA uninstall-dist_scriptsSCRIPTS \ uninstall-libLTLIBRARIES uninstall-local uninstall-man \ uninstall-man1 uninstall-man5 uninstall-man7 uninstall-man8 \ uninstall-nobase_pkgdataDATA uninstall-openflowincludeHEADERS \ uninstall-openvswitchincludeHEADERS uninstall-pkgconfigDATA \ uninstall-pkgdataDATA uninstall-sbinPROGRAMS \ uninstall-sbinSCRIPTS uninstall-scriptsDATA \ uninstall-scriptsSCRIPTS .PRECIOUS: Makefile .in: $(AM_V_GEN)$(PERL) $(srcdir)/build-aux/soexpand.pl -I$(srcdir) < $< | \ sed \ -e 's,[@]PKIDIR[@],$(PKIDIR),g' \ -e 's,[@]LOGDIR[@],$(LOGDIR),g' \ -e 's,[@]DBDIR[@],$(DBDIR),g' \ -e 's,[@]PERL[@],$(PERL),g' \ -e 's,[@]PYTHON[@],$(PYTHON),g' \ -e 's,[@]RUNDIR[@],$(RUNDIR),g' \ -e 's,[@]VERSION[@],$(VERSION),g' \ -e 's,[@]localstatedir[@],$(localstatedir),g' \ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ -e 's,[@]bindir[@],$(bindir),g' \ -e 's,[@]sbindir[@],$(sbindir),g' \ -e 's,[@]abs_builddir[@],$(abs_builddir),g' \ -e 's,[@]abs_top_srcdir[@],$(abs_top_srcdir),g' \ > $@.tmp @if head -n 1 $@.tmp | grep '#!' > /dev/null; then \ chmod +x $@.tmp; \ fi $(AM_V_at) mv $@.tmp $@ %: %.xml $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/xml2nroff $< > $@.tmp \ --version=$(VERSION) \ PKIDIR='$(PKIDIR)' \ LOGDIR='$(LOGDIR)' \ DBDIR='$(DBDIR)' \ PERL='$(PERL)' \ PYTHON='$(PYTHON)' \ RUNDIR='$(RUNDIR)' \ VERSION='$(VERSION)' \ localstatedir='$(localstatedir)' \ pkgdatadir='$(pkgdatadir)' \ sysconfdir='$(sysconfdir)' \ bindir='$(bindir)' \ sbindir='$(sbindir)' $(AM_v_at)mv $@.tmp $@ .PHONY: clean-pycov clean-pycov: cd $(srcdir) && rm -f $(PYCOV_CLEAN_FILES) @GNU_MAKE_TRUE@dist-hook-git: distfiles @GNU_MAKE_TRUE@ @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1; then \ @GNU_MAKE_TRUE@ (cd datapath && $(MAKE) distfiles); \ @GNU_MAKE_TRUE@ (cat distfiles; sed 's|^|datapath/|' datapath/distfiles) | \ @GNU_MAKE_TRUE@ LC_ALL=C sort -u > all-distfiles; \ @GNU_MAKE_TRUE@ (cd $(srcdir) && git ls-files) | grep -v '\.gitignore$$' | \ @GNU_MAKE_TRUE@ LC_ALL=C sort -u > all-gitfiles; \ @GNU_MAKE_TRUE@ LC_ALL=C comm -1 -3 all-distfiles all-gitfiles > missing-distfiles; \ @GNU_MAKE_TRUE@ if test -s missing-distfiles; then \ @GNU_MAKE_TRUE@ echo "The following files are in git but not the distribution:"; \ @GNU_MAKE_TRUE@ cat missing-distfiles; \ @GNU_MAKE_TRUE@ exit 1; \ @GNU_MAKE_TRUE@ fi; \ @GNU_MAKE_TRUE@ fi # The following is based on commands for the Automake "distdir" target. @GNU_MAKE_TRUE@distfiles: Makefile @GNU_MAKE_TRUE@ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @GNU_MAKE_TRUE@ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @GNU_MAKE_TRUE@ list='$(DISTFILES)'; \ @GNU_MAKE_TRUE@ for file in $$list; do echo $$file; done | \ @GNU_MAKE_TRUE@ sed -e "s|^$$srcdirstrip/||;t" \ @GNU_MAKE_TRUE@ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t" | \ @GNU_MAKE_TRUE@ LC_ALL=C sort -u > $@ .PHONY: dist-hook-git config-h-check: @cd $(srcdir); \ if test -e .git && (git --version) >/dev/null 2>&1 && \ git --no-pager grep -L '#include ' `git ls-files | grep '\.c$$' | \ grep -vE '^datapath|^lib/sflow|^third-party|^datapath-windows'`; \ then \ echo "See above for list of violations of the rule that"; \ echo "every C source file must #include ."; \ exit 1; \ fi .PHONY: config-h-check printf-check: @cd $(srcdir); \ if test -e .git && (git --version) >/dev/null 2>&1 && \ git --no-pager grep -n -E -e '%[-+ #0-9.*]*([ztj]|hh)' --and --not -e 'ovs_scan' `git ls-files | grep '\.[ch]$$' | \ grep -vE '^datapath|^lib/sflow|^third-party'`; \ then \ echo "See above for list of violations of the rule that"; \ echo "'z', 't', 'j', 'hh' printf() type modifiers are"; \ echo "forbidden. See CodingStyle.md for replacements."; \ exit 1; \ fi .PHONY: printf-check static-check: @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \ git --no-pager grep -n -E '^[ ]+(struct vlog_rate_limit|pthread_once_t|struct ovsthread_once).*=' $(srcdir); \ then \ echo "See above for list of violations of the rule that "; \ echo "certain data structures must always be 'static'"; \ exit 1; \ fi .PHONY: static-check check-assert-h-usage: @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \ (cd $(srcdir) && git --no-pager grep -l -E '[<]assert.h[>]') | \ $(EGREP) -v '^lib/(sflow_receiver|vlog).c$$|^tests/'; \ then \ echo "Files listed above unexpectedly #include <""assert.h"">."; \ echo "Please use ovs_assert (from util.h) instead of assert."; \ exit 1; \ fi .PHONY: check-assert-h-usage check-endian: @if test -e $(srcdir)/.git && (git --version) >/dev/null 2>&1 && \ (cd $(srcdir) && git --no-pager grep -l -E \ -e 'BIG_ENDIAN|LITTLE_ENDIAN' --and --not -e 'BYTE_ORDER' | \ $(EGREP) -v '^datapath/'); \ then \ echo "See above for list of files that misuse LITTLE""_ENDIAN"; \ echo "or BIG""_ENDIAN. Please use WORDS_BIGENDIAN instead."; \ exit 1; \ fi .PHONY: check-endian thread-safety-check: @cd $(srcdir); \ if test -e .git && (git --version) >/dev/null 2>&1 && \ grep -n -f build-aux/thread-safety-blacklist \ `git ls-files | grep '\.[ch]$$' \ | $(EGREP) -v '^datapath|^lib/sflow|^third-party'` /dev/null \ | $(EGREP) -v ':[ ]*/?\*'; \ then \ echo "See above for list of calls to functions that are"; \ echo "blacklisted due to thread safety issues"; \ exit 1; \ fi .PHONY: thread-safety-check @HAVE_GROFF_TRUE@manpage-check: $(man_MANS) $(dist_man_MANS) $(noinst_man_MANS) @HAVE_GROFF_TRUE@ @error=false; \ @HAVE_GROFF_TRUE@ for manpage in $?; do \ @HAVE_GROFF_TRUE@ LANG=en_US.UTF-8 groff -w mac -w delim -w escape -w input -w missing -w tab -T utf8 -man -p -z $$manpage >$@.tmp 2>&1; \ @HAVE_GROFF_TRUE@ if grep warning: $@.tmp; then error=:; fi; \ @HAVE_GROFF_TRUE@ rm -f $@.tmp; \ @HAVE_GROFF_TRUE@ done; \ @HAVE_GROFF_TRUE@ if $$error; then exit 1; else touch $@; fi @HAVE_GROFF_TRUE@ $(AM_V_GEN) touch -c $@ ovn/utilities/ovn-sbctl.8: \ ovn/utilities/ovn-sbctl.8.in \ lib/db-ctl-base.man \ lib/table.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovn/utilities/ovn-sbctl.8.in: lib/db-ctl-base.man: lib/table.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-client.1: \ ovsdb/ovsdb-client.1.in \ lib/common-syn.man \ lib/common.man \ lib/daemon-syn.man \ lib/daemon.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-syn.man \ lib/ssl.man \ lib/table.man \ lib/vlog-syn.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovsdb/ovsdb-client.1.in: lib/common-syn.man: lib/common.man: lib/daemon-syn.man: lib/daemon.man: lib/ssl-bootstrap-syn.man: lib/ssl-bootstrap.man: lib/ssl-syn.man: lib/ssl.man: lib/table.man: lib/vlog-syn.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-server.1: \ ovsdb/ovsdb-server.1.in \ lib/common-syn.man \ lib/common.man \ lib/coverage-unixctl.man \ lib/daemon-syn.man \ lib/daemon.man \ lib/memory-unixctl.man \ lib/service-syn.man \ lib/service.man \ lib/ssl-bootstrap-syn.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert-syn.man \ lib/ssl-peer-ca-cert.man \ lib/ssl-syn.man \ lib/ssl.man \ lib/unixctl-syn.man \ lib/unixctl.man \ lib/vlog-syn.man \ lib/vlog-unixctl.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man ovsdb/ovsdb-server.1.in: lib/common-syn.man: lib/common.man: lib/coverage-unixctl.man: lib/daemon-syn.man: lib/daemon.man: lib/memory-unixctl.man: lib/service-syn.man: lib/service.man: lib/ssl-bootstrap-syn.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert-syn.man: lib/ssl-peer-ca-cert.man: lib/ssl-syn.man: lib/ssl.man: lib/unixctl-syn.man: lib/unixctl.man: lib/vlog-syn.man: lib/vlog-unixctl.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/ovsdb-tool.1: \ ovsdb/ovsdb-tool.1.in \ lib/common-syn.man \ lib/common.man \ lib/vlog-syn.man \ lib/vlog.man ovsdb/ovsdb-tool.1.in: lib/common-syn.man: lib/common.man: lib/vlog-syn.man: lib/vlog.man: utilities/bugtool/ovs-bugtool.8: \ utilities/bugtool/ovs-bugtool.8.in utilities/bugtool/ovs-bugtool.8.in: utilities/ovs-appctl.8: \ utilities/ovs-appctl.8.in \ lib/common.man utilities/ovs-appctl.8.in: lib/common.man: utilities/ovs-benchmark.1: \ utilities/ovs-benchmark.1.in \ lib/ovs.tmac utilities/ovs-benchmark.1.in: lib/ovs.tmac: utilities/ovs-dpctl-top.8: \ utilities/ovs-dpctl-top.8.in utilities/ovs-dpctl-top.8.in: utilities/ovs-dpctl.8: \ utilities/ovs-dpctl.8.in \ lib/common.man \ lib/dpctl.man \ lib/vlog.man utilities/ovs-dpctl.8.in: lib/common.man: lib/dpctl.man: lib/vlog.man: utilities/ovs-l3ping.8: \ utilities/ovs-l3ping.8.in \ lib/common-syn.man \ lib/common.man utilities/ovs-l3ping.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-ofctl.8: \ utilities/ovs-ofctl.8.in \ lib/common.man \ lib/daemon.man \ lib/ofp-version.man \ lib/ssl.man \ lib/unixctl.man \ lib/vconn-active.man \ lib/vlog.man utilities/ovs-ofctl.8.in: lib/common.man: lib/daemon.man: lib/ofp-version.man: lib/ssl.man: lib/unixctl.man: lib/vconn-active.man: lib/vlog.man: utilities/ovs-pcap.1: \ utilities/ovs-pcap.1.in \ lib/common-syn.man \ lib/common.man utilities/ovs-pcap.1.in: lib/common-syn.man: lib/common.man: utilities/ovs-pki.8: \ utilities/ovs-pki.8.in utilities/ovs-pki.8.in: utilities/ovs-tcpundump.1: \ utilities/ovs-tcpundump.1.in \ lib/common-syn.man \ lib/common.man utilities/ovs-tcpundump.1.in: lib/common-syn.man: lib/common.man: utilities/ovs-test.8: \ utilities/ovs-test.8.in \ lib/common-syn.man \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-test.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-testcontroller.8: \ utilities/ovs-testcontroller.8.in \ lib/common.man \ lib/daemon.man \ lib/ofp-version.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/unixctl.man \ lib/vconn-active.man \ lib/vconn-passive.man \ lib/vlog.man utilities/ovs-testcontroller.8.in: lib/common.man: lib/daemon.man: lib/ofp-version.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/unixctl.man: lib/vconn-active.man: lib/vconn-passive.man: lib/vlog.man: utilities/ovs-vlan-bug-workaround.8: \ utilities/ovs-vlan-bug-workaround.8.in \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-vlan-bug-workaround.8.in: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-vlan-test.8: \ utilities/ovs-vlan-test.8.in \ lib/common-syn.man \ lib/common.man \ utilities/ovs-vlan-bugs.man utilities/ovs-vlan-test.8.in: lib/common-syn.man: lib/common.man: utilities/ovs-vlan-bugs.man: utilities/ovs-vsctl.8: \ utilities/ovs-vsctl.8.in \ lib/common.man \ lib/db-ctl-base.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/table.man \ lib/vconn-active.man \ lib/vconn-passive.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man \ ovsdb/remote-passive.man utilities/ovs-vsctl.8.in: lib/common.man: lib/db-ctl-base.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/table.man: lib/vconn-active.man: lib/vconn-passive.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/remote-passive.man: vswitchd/ovs-vswitchd.8: \ vswitchd/ovs-vswitchd.8.in \ lib/common.man \ lib/coverage-unixctl.man \ lib/daemon.man \ lib/dpctl.man \ lib/memory-unixctl.man \ lib/service.man \ lib/ssl-bootstrap.man \ lib/ssl.man \ lib/unixctl.man \ lib/vlog-unixctl.man \ lib/vlog.man \ ofproto/ofproto-dpif-unixctl.man \ ofproto/ofproto-tnl-unixctl.man \ ofproto/ofproto-unixctl.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man vswitchd/ovs-vswitchd.8.in: lib/common.man: lib/coverage-unixctl.man: lib/daemon.man: lib/dpctl.man: lib/memory-unixctl.man: lib/service.man: lib/ssl-bootstrap.man: lib/ssl.man: lib/unixctl.man: lib/vlog-unixctl.man: lib/vlog.man: ofproto/ofproto-dpif-unixctl.man: ofproto/ofproto-tnl-unixctl.man: ofproto/ofproto-unixctl.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: vtep/vtep-ctl.8: \ vtep/vtep-ctl.8.in \ lib/common.man \ lib/db-ctl-base.man \ lib/ssl-bootstrap.man \ lib/ssl-peer-ca-cert.man \ lib/ssl.man \ lib/table.man \ lib/vlog.man \ ovsdb/remote-active.man \ ovsdb/remote-active.man \ ovsdb/remote-passive.man \ ovsdb/remote-passive.man vtep/vtep-ctl.8.in: lib/common.man: lib/db-ctl-base.man: lib/ssl-bootstrap.man: lib/ssl-peer-ca-cert.man: lib/ssl.man: lib/table.man: lib/vlog.man: ovsdb/remote-active.man: ovsdb/remote-active.man: ovsdb/remote-passive.man: ovsdb/remote-passive.man: $(srcdir)/manpages.mk: $(MAN_ROOTS) build-aux/sodepends.pl @$(PERL) $(srcdir)/build-aux/sodepends.pl -I. -I$(srcdir) $(MAN_ROOTS) >$(@F).tmp @if cmp -s $(@F).tmp $@; then \ touch $@; \ rm -f $(@F).tmp; \ else \ mv $(@F).tmp $@; \ fi @VSTUDIO_DDK_TRUE@ovsext_make: datapath-windows/ovsext.sln @VSTUDIO_DDK_TRUE@ MSBuild.exe datapath-windows/ovsext.sln /target:Build /property:Configuration="Win8$(VSTUDIO_CONFIG)" @VSTUDIO_DDK_TRUE@ MSBuild.exe datapath-windows/ovsext.sln /target:Build /property:Configuration="Win8.1$(VSTUDIO_CONFIG)" @VSTUDIO_DDK_TRUE@ovsext_clean: datapath-windows/ovsext.sln @VSTUDIO_DDK_TRUE@ MSBuild.exe datapath-windows/ovsext.sln /target:Clean /property:Configuration="Win8$(VSTUDIO_CONFIG)" @VSTUDIO_DDK_TRUE@ MSBuild.exe datapath-windows/ovsext.sln /target:Clean /property:Configuration="Win8.1$(VSTUDIO_CONFIG)" .PHONY: ovsext_make dist-hook: $(DIST_HOOKS) all-local: $(ALL_LOCAL) clean-local: $(CLEAN_LOCAL) install-data-local: $(INSTALL_DATA_LOCAL) uninstall-local: $(UNINSTALL_LOCAL) .PHONY: $(DIST_HOOKS) $(CLEAN_LOCAL) $(INSTALL_DATA_LOCAL) $(UNINSTALL_LOCAL) modules_install: @LINUX_ENABLED_TRUE@ cd datapath/linux && $(MAKE) modules_install dist-docs: VERSION=$(VERSION) $(srcdir)/build-aux/dist-docs $(srcdir) $(docs) .PHONY: dist-docs @HAVE_OPENSSL_TRUE@lib/dhparams.c: lib/dh1024.pem lib/dh2048.pem lib/dh4096.pem @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)(echo '#include "lib/dhparams.h"' && \ @HAVE_OPENSSL_TRUE@ openssl dhparam -C -in $(srcdir)/lib/dh1024.pem -noout && \ @HAVE_OPENSSL_TRUE@ openssl dhparam -C -in $(srcdir)/lib/dh2048.pem -noout && \ @HAVE_OPENSSL_TRUE@ openssl dhparam -C -in $(srcdir)/lib/dh4096.pem -noout) \ @HAVE_OPENSSL_TRUE@ | sed 's/\(get_dh[0-9]*\)()/\1(void)/' > lib/dhparams.c.tmp && \ @HAVE_OPENSSL_TRUE@ mv lib/dhparams.c.tmp lib/dhparams.c lib/vswitch-idl.ovsidl: vswitchd/vswitch.ovsschema lib/vswitch-idl.ann $(AM_V_GEN)$(OVSDB_IDLC) annotate $(srcdir)/vswitchd/vswitch.ovsschema $(srcdir)/lib/vswitch-idl.ann > $@.tmp && mv $@.tmp $@ lib/dirs.c: lib/dirs.c.in Makefile $(AM_V_GEN)($(ro_c) && sed < $(srcdir)/lib/dirs.c.in \ -e 's,[@]srcdir[@],$(srcdir),g' \ -e 's,[@]LOGDIR[@],"$(LOGDIR)",g' \ -e 's,[@]RUNDIR[@],"$(RUNDIR)",g' \ -e 's,[@]DBDIR[@],"$(DBDIR)",g' \ -e 's,[@]bindir[@],"$(bindir)",g' \ -e 's,[@]sysconfdir[@],"$(sysconfdir)",g' \ -e 's,[@]pkgdatadir[@],"$(pkgdatadir)",g') \ > lib/dirs.c.tmp && \ mv lib/dirs.c.tmp lib/dirs.c lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h $(AM_V_GEN)$(run_python) $^ --meta-flow > $@.tmp && mv $@.tmp $@ lib/meta-flow.lo: lib/meta-flow.inc lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields lib/meta-flow.h $(AM_V_GEN)$(run_python) $^ --nx-match > $@.tmp && mv $@.tmp $@ lib/nx-match.lo: lib/nx-match.inc lib/ofp-actions.inc1: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c $(AM_V_GEN)$(run_python) $^ --prototypes > $@.tmp && mv $@.tmp $@ lib/ofp-actions.inc2: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c $(AM_V_GEN)$(run_python) $^ --definitions > $@.tmp && mv $@.tmp $@ lib/ofp-actions.lo: lib/ofp-actions.inc1 lib/ofp-actions.inc2 lib/ofp-errors.inc: lib/ofp-errors.h include/openflow/openflow-common.h \ $(srcdir)/build-aux/extract-ofp-errors $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/extract-ofp-errors \ $(srcdir)/lib/ofp-errors.h \ $(srcdir)/include/openflow/openflow-common.h > $@.tmp && \ mv $@.tmp $@ lib/ofp-errors.lo: lib/ofp-errors.inc lib/ofp-msgs.inc: lib/ofp-msgs.h $(srcdir)/build-aux/extract-ofp-msgs $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/extract-ofp-msgs \ $(srcdir)/lib/ofp-msgs.h $@ > $@.tmp && mv $@.tmp $@ lib/ofp-msgs.lo: lib/ofp-msgs.inc lib-install-data-local: $(MKDIR_P) $(DESTDIR)$(RUNDIR) $(MKDIR_P) $(DESTDIR)$(PKIDIR) $(MKDIR_P) $(DESTDIR)$(LOGDIR) $(MKDIR_P) $(DESTDIR)$(DBDIR) ofproto/ipfix-entities.def: ofproto/ipfix.xml ofproto/ipfix-gen-entities $(AM_V_GEN)$(run_python) $(srcdir)/ofproto/ipfix-gen-entities $< > $@.tmp && \ mv $@.tmp $@ utilities/ovs-lib: $(top_builddir)/config.status @HAVE_PYTHON_TRUE@bugtool-install-data-local: @HAVE_PYTHON_TRUE@ for plugin in $(bugtool_plugins); do \ @HAVE_PYTHON_TRUE@ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ @HAVE_PYTHON_TRUE@ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ @HAVE_PYTHON_TRUE@ $(MKDIR_P) "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ @HAVE_PYTHON_TRUE@ $(INSTALL_DATA) "$(srcdir)/$$plugin" "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ @HAVE_PYTHON_TRUE@ done @HAVE_PYTHON_TRUE@bugtool-uninstall-local: @HAVE_PYTHON_TRUE@ for plugin in $(bugtool_plugins); do \ @HAVE_PYTHON_TRUE@ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ @HAVE_PYTHON_TRUE@ rm -f "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ @HAVE_PYTHON_TRUE@ done @HAVE_PYTHON_TRUE@ for plugin in $(bugtool_plugins); do \ @HAVE_PYTHON_TRUE@ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ @HAVE_PYTHON_TRUE@ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ @HAVE_PYTHON_TRUE@ rmdir "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ @HAVE_PYTHON_TRUE@ done; exit 0 check-local: tests/atconfig tests/atlocal $(TESTSUITE) set $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS); \ "$$@" || (test X'$(RECHECK)' = Xyes && "$$@" --recheck) check-pycov: all tests/atconfig tests/atlocal $(TESTSUITE) clean-pycov PYTHONDONTWRITEBYTECODE=yes COVERAGE_FILE=$(COVERAGE_FILE) PYTHON='$(COVERAGE) run -p' $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS) @cd $(srcdir) && $(COVERAGE) combine && COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) annotate @echo @echo '----------------------------------------------------------------------' @echo 'Annotated coverage source has the ",cover" extension.' @echo '----------------------------------------------------------------------' @echo @COVERAGE_FILE=$(COVERAGE_FILE) $(COVERAGE) report $(valgrind_wrappers): tests/valgrind-wrapper.in @$(MKDIR_P) tests/valgrind $(AM_V_GEN) sed -e 's,[@]wrap_program[@],$@,' \ $(top_srcdir)/tests/valgrind-wrapper.in > $@.tmp && \ chmod +x $@.tmp && \ mv $@.tmp $@ check-valgrind: all tests/atconfig tests/atlocal $(TESTSUITE) \ $(valgrind_wrappers) $(check_DATA) $(SHELL) '$(TESTSUITE)' -C tests CHECK_VALGRIND=true VALGRIND='$(VALGRIND)' AUTOTEST_PATH='tests/valgrind:$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) @echo @echo '----------------------------------------------------------------------' @echo 'Valgrind output can be found in tests/testsuite.dir/*/valgrind.*' @echo '----------------------------------------------------------------------' # OFTest support. check-oftest: all $(AM_V_at)srcdir='$(srcdir)' $(SHELL) $(srcdir)/tests/run-oftest # Ryu support. check-ryu: all $(AM_V_at)srcdir='$(srcdir)' $(SHELL) $(srcdir)/tests/run-ryu # Run kmod tests. Assume kernel modules has been installed or linked into the kernel check-kernel: all tests/atconfig tests/atlocal $(SYSTEM_KMOD_TESTSUITE) $(SHELL) '$(SYSTEM_KMOD_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)' -d $(TESTSUITEFLAGS) # Testing the out of tree Kernel module check-kmod: all tests/atconfig tests/atlocal $(SYSTEM_KMOD_TESTSUITE) $(MAKE) modules_install modprobe -r openvswitch $(MAKE) check-kernel check-system-userspace: all tests/atconfig tests/atlocal $(SYSTEM_USERSPACE_TESTSUITE) $(SHELL) '$(SYSTEM_USERSPACE_TESTSUITE)' -C tests AUTOTEST_PATH='$(AUTOTEST_PATH)' $(TESTSUITEFLAGS) clean-local: test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean @WIN32_TRUE@$(TESTSUITE): package.m4 $(TESTSUITE_AT) $(COMMON_MACROS_AT) $(TESTSUITE_PATCH) @WIN32_TRUE@ $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o testsuite.tmp $@.at @WIN32_TRUE@ patch -p0 testsuite.tmp $(TESTSUITE_PATCH) @WIN32_TRUE@ $(AM_V_at)mv testsuite.tmp $@ @WIN32_FALSE@$(TESTSUITE): package.m4 $(TESTSUITE_AT) $(COMMON_MACROS_AT) @WIN32_FALSE@ $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at @WIN32_FALSE@ $(AM_V_at)mv $@.tmp $@ $(SYSTEM_KMOD_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_KMOD_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ $(SYSTEM_USERSPACE_TESTSUITE): package.m4 $(SYSTEM_TESTSUITE_AT) $(SYSTEM_USERSPACE_TESTSUITE_AT) $(COMMON_MACROS_AT) $(AM_V_GEN)$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at $(AM_V_at)mv $@.tmp $@ # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac $(AM_V_GEN):;{ \ echo '# Signature of the current package.' && \ echo 'm4_define([AT_PACKAGE_NAME], [$(PACKAGE_NAME)])' && \ echo 'm4_define([AT_PACKAGE_TARNAME], [$(PACKAGE_TARNAME)])' && \ echo 'm4_define([AT_PACKAGE_VERSION], [$(PACKAGE_VERSION)])' && \ echo 'm4_define([AT_PACKAGE_STRING], [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT], [$(PACKAGE_BUGREPORT)])'; \ } >'$(srcdir)/package.m4' tests/idltest.ovsidl: $(IDLTEST_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) -C $(srcdir) annotate $(IDLTEST_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ tests/idltest.c: tests/idltest.h @HAVE_OPENSSL_TRUE@tests/testpki-cacert.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/switchca/cacert.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-cert.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test-cert.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-req.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test-req.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-privkey.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test-privkey.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-cert2.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test2-cert.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-req2.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test2-req.pem $@ @HAVE_OPENSSL_TRUE@tests/testpki-privkey2.pem: tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)cp tests/pki/test2-privkey.pem $@ @HAVE_OPENSSL_TRUE@tests/pki/stamp: @HAVE_OPENSSL_TRUE@ $(AM_V_at)rm -f tests/pki/stamp @HAVE_OPENSSL_TRUE@ $(AM_V_at)rm -rf tests/pki @HAVE_OPENSSL_TRUE@ $(AM_V_GEN)$(OVS_PKI) init && \ @HAVE_OPENSSL_TRUE@ $(OVS_PKI) req+sign tests/pki/test && \ @HAVE_OPENSSL_TRUE@ $(OVS_PKI) req+sign tests/pki/test2 && \ @HAVE_OPENSSL_TRUE@ : > tests/pki/stamp @HAVE_OPENSSL_TRUE@clean-pki: @HAVE_OPENSSL_TRUE@ rm -f tests/pki/stamp @HAVE_OPENSSL_TRUE@ rm -rf tests/pki include/odp-netlink.h: datapath/linux/compat/include/linux/openvswitch.h \ build-aux/extract-odp-netlink-h $(AM_V_GEN)sed -f $(srcdir)/build-aux/extract-odp-netlink-h < $< > $@ @HAVE_PYTHON_TRUE@.h.hstamp: @HAVE_PYTHON_TRUE@ $(AM_V_GEN)$(run_python) $(srcdir)/build-aux/check-structs -I$(srcdir)/include $< && \ @HAVE_PYTHON_TRUE@ touch $@ @HAVE_PYTHON_TRUE@$(HSTAMP_FILES): build-aux/check-structs $(openflowinclude_HEADERS) check-debian-changelog-version: @DEB_VERSION=`echo '$(VERSION)' | sed 's/pre/~pre/'`; \ if $(FGREP) '($(DEB_VERSION)' $(srcdir)/debian/changelog >/dev/null; \ then \ :; \ else \ echo "Update debian/changelog to mention version $(VERSION)"; \ exit 1; \ fi $(srcdir)/debian/copyright: AUTHORS debian/copyright.in $(AM_V_GEN) \ { sed -n -e '/%AUTHORS%/q' -e p < $(srcdir)/debian/copyright.in; \ sed '1,/^$$/d' $(srcdir)/AUTHORS | \ sed -n -e '/^$$/q' -e 's/^/ /p'; \ sed -e '1,/%AUTHORS%/d' $(srcdir)/debian/copyright.in; \ } > $@ # vswitch E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@vswitchd/vswitch.gv: ovsdb/ovsdb-dot.in vswitchd/vswitch.ovsschema @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vswitchd/vswitch.ovsschema > $@ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@vswitchd/vswitch.pic: vswitchd/vswitch.gv ovsdb/dot2pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)(dot -T plain < vswitchd/vswitch.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ mv $@.tmp $@ vswitchd/ovs-vswitchd.conf.db.5: \ ovsdb/ovsdb-doc vswitchd/vswitch.xml vswitchd/vswitch.ovsschema \ $(VSWITCH_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VSWITCH_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vswitchd/vswitch.ovsschema \ $(srcdir)/vswitchd/vswitch.xml > $@.tmp && \ mv $@.tmp $@ vswitchd/vswitch.ovsschema.stamp: vswitchd/vswitch.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ .ovsidl.c: $(AM_V_GEN)$(OVSDB_IDLC) c-idl-source $< > $@.tmp && mv $@.tmp $@ .ovsidl.h: $(AM_V_GEN)$(OVSDB_IDLC) c-idl-header $< > $@.tmp && mv $@.tmp $@ # This must be done late: macros in targets are expanded when the # target line is read, so if this file were to be included before some # other file that added to OVSIDL_BUILT, then those files wouldn't get # the dependency. # # However, current versions of Automake seem to output all variable # assignments before any targets, so it doesn't seem to be a problem, # at least for now. $(OVSIDL_BUILT): ovsdb/ovsdb-idlc.in $(srcdir)/rhel/openvswitch-dkms.spec: rhel/openvswitch-dkms.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-kmod-rhel6.spec: rhel/openvswitch-kmod-rhel6.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-kmod-fedora.spec: rhel/openvswitch-kmod-fedora.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch.spec: rhel/openvswitch.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-fedora.spec: rhel/openvswitch-fedora.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/xenserver/openvswitch-xen.spec: xenserver/openvswitch-xen.spec.in $(top_builddir)/config.status $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ < $(srcdir)/xenserver/$(@F).in > $(@F).tmp || exit 1; \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi @HAVE_PYTHON_TRUE@ovs-install-data-local: @HAVE_PYTHON_TRUE@ $(MKDIR_P) python/ovs @HAVE_PYTHON_TRUE@ sed \ @HAVE_PYTHON_TRUE@ -e '/^##/d' \ @HAVE_PYTHON_TRUE@ -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' \ @HAVE_PYTHON_TRUE@ -e 's,[@]RUNDIR[@],$(RUNDIR),g' \ @HAVE_PYTHON_TRUE@ -e 's,[@]LOGDIR[@],$(LOGDIR),g' \ @HAVE_PYTHON_TRUE@ -e 's,[@]bindir[@],$(bindir),g' \ @HAVE_PYTHON_TRUE@ -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ @HAVE_PYTHON_TRUE@ -e 's,[@]DBDIR[@],$(DBDIR),g' \ @HAVE_PYTHON_TRUE@ < $(srcdir)/python/ovs/dirs.py.template \ @HAVE_PYTHON_TRUE@ > python/ovs/dirs.py.tmp @HAVE_PYTHON_TRUE@ $(MKDIR_P) $(DESTDIR)$(pkgdatadir)/python/ovs @HAVE_PYTHON_TRUE@ $(INSTALL_DATA) python/ovs/dirs.py.tmp $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py @HAVE_PYTHON_TRUE@ rm python/ovs/dirs.py.tmp @HAVE_PYTHON_TRUE@python-sdist: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py @HAVE_PYTHON_TRUE@ (cd python/ && $(PYTHON) setup.py sdist) @HAVE_PYTHON_TRUE@pypi-upload: $(srcdir)/python/ovs/version.py $(ovs_pyfiles) python/ovs/dirs.py @HAVE_PYTHON_TRUE@ (cd python/ && $(PYTHON) setup.py sdist upload) @HAVE_PYTHON_FALSE@ovs-install-data-local: @HAVE_PYTHON_FALSE@ @: install-data-local: ovs-install-data-local ovs-uninstall-local: rm -f $(DESTDIR)$(pkgdatadir)/python/ovs/dirs.py $(srcdir)/python/ovs/version.py: config.status $(AM_V_GEN)$(ro_shell) > $(@F).tmp && \ echo 'VERSION = "$(VERSION)"' >> $(@F).tmp && \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi $(srcdir)/python/ovs/dirs.py: python/ovs/dirs.py.template $(AM_V_GEN)sed \ -e '/^##/d' \ -e 's,[@]pkgdatadir[@],/usr/local/share/openvswitch,g' \ -e 's,[@]RUNDIR[@],/var/run,g' \ -e 's,[@]LOGDIR[@],/usr/local/var/log,g' \ -e 's,[@]bindir[@],/usr/local/bin,g' \ -e 's,[@]sysconfdir[@],/usr/local/etc,g' \ -e 's,[@]DBDIR[@],/usr/local/etc/openvswitch,g' \ < $? > $@.tmp && \ mv $@.tmp $@ sandbox: all cd $(srcdir)/tutorial && MAKE=$(MAKE) ./ovs-sandbox -b $(abs_builddir) $(SANDBOXFLAGS) vtep/vtep-idl.ovsidl: $(VTEP_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(VTEP_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ # VTEP E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@vtep/vtep.gv: ovsdb/ovsdb-dot.in vtep/vtep.ovsschema @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vtep/vtep.ovsschema > $@ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@vtep/vtep.pic: vtep/vtep.gv ovsdb/dot2pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)(dot -T plain < vtep/vtep.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ mv $@.tmp $@ vtep/vtep.5: \ ovsdb/ovsdb-doc vtep/vtep.xml $(srcdir)/vtep/vtep.ovsschema $(VTEP_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VTEP_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep.xml > $@.tmp && \ mv $@.tmp $@ vtep/vtep.ovsschema.stamp: vtep/vtep.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ $(srcdir)/datapath-windows/include/OvsDpInterface.h: \ datapath/linux/compat/include/linux/openvswitch.h \ build-aux/extract-odp-netlink-windows-dp-h $(AM_V_GEN)sed -f $(srcdir)/build-aux/extract-odp-netlink-windows-dp-h < $< > $@ windows_installer: all #Userspace files needed for the installer cp -f $(top_srcdir)/datapath-windows/misc/OVS.psm1 windows/ovs-windows-installer/Services/OVS.psm1 cp -f $(top_srcdir)/vswitchd/vswitch.ovsschema windows/ovs-windows-installer/Services/vswitch.ovsschema cp -f $(top_srcdir)/vswitchd/ovs-vswitchd.exe windows/ovs-windows-installer/Services/ovs-vswitchd.exe cp -f $(top_srcdir)/ovsdb/ovsdb-server.exe windows/ovs-windows-installer/Services/ovsdb-server.exe cp -f $(top_srcdir)/utilities/*.exe windows/ovs-windows-installer/Binaries/ cp -f $(top_srcdir)/utilities/*.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-client.exe windows/ovs-windows-installer/Binaries/ovsdb-client.exe cp -f $(top_srcdir)/ovsdb/ovsdb-tool.exe windows/ovs-windows-installer/Binaries/ovsdb-tool.exe cp -f $(top_srcdir)/ovsdb/ovsdb-client.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-tool.pdb windows/ovs-windows-installer/Symbols/ #Third party files needed by the installer cp -f $(PTHREAD_WIN32_DIR_DLL_WIN_FORM)/*.dll windows/ovs-windows-installer/Binaries/ cp -f "/c/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC120_CRT_x86.msm" windows/ovs-windows-installer/Redist/Microsoft_VC120_CRT_x86.msm #Forwarding extension files needed for the installer cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/OVSExt.sys windows/ovs-windows-installer/Driver/Win8/OVSExt.sys cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8.1/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8.1/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.sys windows/ovs-windows-installer/Driver/Win8.1/ovsext.sys MSBuild.exe windows/ovs-windows-installer.sln /target:Build /property:Configuration="Release" # OVN southbound E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ovn/ovn-sb.gv: ovsdb/ovsdb-dot.in ovn/ovn-sb.ovsschema @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-sb.ovsschema > $@ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ovn/ovn-sb.pic: ovn/ovn-sb.gv ovsdb/dot2pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)(dot -T plain < ovn/ovn-sb.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ mv $@.tmp $@ ovn/ovn-sb.5: \ ovsdb/ovsdb-doc ovn/ovn-sb.xml ovn/ovn-sb.ovsschema $(OVN_SB_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(OVN_SB_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/ovn/ovn-sb.ovsschema \ $(srcdir)/ovn/ovn-sb.xml > $@.tmp && \ mv $@.tmp $@ # OVN northbound E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ovn/ovn-nb.gv: ovsdb/ovsdb-dot.in ovn/ovn-nb.ovsschema @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-nb.ovsschema > $@ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ovn/ovn-nb.pic: ovn/ovn-nb.gv ovsdb/dot2pic @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ $(AM_V_GEN)(dot -T plain < ovn/ovn-nb.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ @HAVE_DOT_TRUE@@HAVE_PYTHON_TRUE@ mv $@.tmp $@ ovn/ovn-nb.5: \ ovsdb/ovsdb-doc ovn/ovn-nb.xml ovn/ovn-nb.ovsschema $(OVN_NB_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(OVN_NB_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/ovn/ovn-nb.ovsschema \ $(srcdir)/ovn/ovn-nb.xml > $@.tmp && \ mv $@.tmp $@ ovn/ovn-nb.ovsschema.stamp: ovn/ovn-nb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ ovn/ovn-sb.ovsschema.stamp: ovn/ovn-sb.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ ovn/lib/ovn-sb-idl.ovsidl: $(OVN_SB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_SB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ ovn/lib/ovn-nb-idl.ovsidl: $(OVN_NB_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(OVN_NB_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: openvswitch-2.5.9/PaxHeaders.82075/vtep0000644000000000000000000000013213534540121014541 xustar0030 mtime=1567801425.205856551 30 atime=1567801425.625859648 30 ctime=1567801425.205856551 openvswitch-2.5.9/vtep/0000755000175000017500000000000013534540121016304 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/vtep/PaxHeaders.82075/vtep-idl.ann0000644000000000000000000000013213534540071017044 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.333850123 openvswitch-2.5.9/vtep/vtep-idl.ann0000644000175000017500000000053713534540071020537 0ustar00jpettitjpettit00000000000000# -*- python -*- # This code, when invoked by "ovsdb-idlc annotate" (by the build # process), annotates vswitch.ovsschema with additional data that give # the ovsdb-idl engine information about the types involved, so that # it can generate more programmer-friendly data structures. s["idlPrefix"] = "vteprec_" s["idlHeader"] = "\"vtep/vtep-idl.h\"" openvswitch-2.5.9/vtep/PaxHeaders.82075/vtep-ctl.c0000644000000000000000000000013213534540071016524 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801425.209856581 openvswitch-2.5.9/vtep/vtep-ctl.c0000644000175000017500000021546513534540071020227 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "json.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "poll-loop.h" #include "process.h" #include "stream.h" #include "stream-ssl.h" #include "smap.h" #include "sset.h" #include "svec.h" #include "vtep/vtep-idl.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(vtep_ctl); struct vtep_ctl_context; /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --timeout: Time to wait for a connection to 'db'. */ static int timeout; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; /* The IDL we're using and the current transaction, if any. * This is for use by vtep_ctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void vtep_ctl_exit(int status); static void vtep_ctl_cmd_init(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static void do_vtep_ctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); static struct vtep_ctl_lswitch *find_lswitch(struct vtep_ctl_context *, const char *name, bool must_exist); int main(int argc, char *argv[]) { extern struct vlog_module VLM_reconnect; struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; char *args; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN); vteprec_init(); vtep_ctl_cmd_init(); /* Log our arguments. This is often valuable for debugging systems. */ args = process_escape_args(argv); VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); /* Parse command line. */ shash_init(&local_options); parse_options(argc, argv, &local_options); commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, &n_commands); if (timeout) { time_alarm(timeout); } /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &vteprec_idl_class, false, false); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { seqno = ovsdb_idl_get_seqno(idl); do_vtep_ctl(args, commands, n_commands, idl); } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_ONELINE, OPT_NO_SYSLOG, OPT_DRY_RUN, OPT_PEER_CA_CERT, OPT_LOCAL, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, TABLE_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); table_style.format = TF_LIST; for (;;) { int idx; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&VLM_vtep_ctl, VLF_SYSLOG, VLL_WARN); break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), optarg ? xstrdup(optarg) : NULL); break; case 'h': usage(); case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", vteprec_get_db_version()); exit(EXIT_SUCCESS); case 't': timeout = strtoul(optarg, NULL, 10); if (timeout < 0) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!db) { db = ctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void vtep_ctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } static void usage(void) { printf("\ %s: VTEP configuration utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ VTEP commands:\n\ show print overview of database contents\n\ \n\ Manager commands:\n\ get-manager print the managers\n\ del-manager delete the managers\n\ set-manager TARGET... set the list of managers to TARGET...\n\ \n\ Physical Switch commands:\n\ add-ps PS create a new physical switch named PS\n\ del-ps PS delete PS and all of its ports\n\ list-ps print the names of all the physical switches\n\ ps-exists PS exit 2 if PS does not exist\n\ \n\ Port commands:\n\ list-ports PS print the names of all the ports on PS\n\ add-port PS PORT add network device PORT to PS\n\ del-port PS PORT delete PORT from PS\n\ \n\ Logical Switch commands:\n\ add-ls LS create a new logical switch named LS\n\ del-ls LS delete LS and all of its ports\n\ list-ls print the names of all the logical switches\n\ ls-exists LS exit 2 if LS does not exist\n\ bind-ls PS PORT VLAN LS bind LS to VLAN on PORT\n\ unbind-ls PS PORT VLAN unbind logical switch on VLAN from PORT\n\ list-bindings PS PORT list bindings for PORT on PS\n\ \n\ MAC binding commands:\n\ add-ucast-local LS MAC [ENCAP] IP add ucast local entry in LS\n\ del-ucast-local LS MAC del ucast local entry from LS\n\ add-mcast-local LS MAC [ENCAP] IP add mcast local entry in LS\n\ del-mcast-local LS MAC [ENCAP] IP del mcast local entry from LS\n\ clear-local-macs LS clear local mac entries\n\ list-local-macs LS list local mac entries\n\ add-ucast-remote LS MAC [ENCAP] IP add ucast remote entry in LS\n\ del-ucast-remote LS MAC del ucast remote entry from LS\n\ add-mcast-remote LS MAC [ENCAP] IP add mcast remote entry in LS\n\ del-mcast-remote LS MAC [ENCAP] IP del mcast remote entry from LS\n\ clear-remote-macs LS clear remote mac entries\n\ list-remote-macs LS list remote mac entries\n\ \n\ %s\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ -t, --timeout=SECS wait at most SECS seconds\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db()); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=vtep_ctl:syslog:warn\n"); stream_usage("database", true, true, false); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); exit(EXIT_SUCCESS); } static struct cmd_show_table cmd_show_tables[] = { {&vteprec_table_global, NULL, {&vteprec_global_col_managers, &vteprec_global_col_switches, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_manager, &vteprec_manager_col_target, {&vteprec_manager_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_physical_switch, &vteprec_physical_switch_col_name, {&vteprec_physical_switch_col_management_ips, &vteprec_physical_switch_col_tunnel_ips, &vteprec_physical_switch_col_ports}, {NULL, NULL, NULL} }, {&vteprec_table_physical_port, &vteprec_physical_port_col_name, {&vteprec_physical_port_col_vlan_bindings, NULL, NULL}, {NULL, NULL, NULL} }, {&vteprec_table_logical_switch, &vteprec_logical_switch_col_name, {NULL, NULL, NULL}, {NULL, NULL, NULL} }, {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}} }; /* vtep-ctl specific context. Inherits the 'struct ctl_context' as base. */ struct vtep_ctl_context { struct ctl_context base; /* Modifiable state. */ const struct vteprec_global *vtep_global; bool verified_ports; /* A cache of the contents of the database. * * A command that needs to use any of this information must first * call vtep_ctl_context_populate_cache(). A command that changes * anything that could invalidate the cache must either call * vtep_ctl_context_invalidate_cache() or manually update the cache * to maintain its correctness. */ bool cache_valid; struct shash pswitches; /* Maps from physical switch name to * struct vtep_ctl_pswitch. */ struct shash ports; /* Maps from port name to struct vtep_ctl_port. */ struct shash lswitches; /* Maps from logical switch name to * struct vtep_ctl_lswitch. */ struct shash plocs; /* Maps from "+" to * struct vteprec_physical_locator. */ }; /* Casts 'base' into 'struct vtep_ctl_context'. */ static struct vtep_ctl_context * vtep_ctl_context_cast(struct ctl_context *base) { return CONTAINER_OF(base, struct vtep_ctl_context, base); } struct vtep_ctl_pswitch { const struct vteprec_physical_switch *ps_cfg; char *name; struct ovs_list ports; /* Contains "struct vteprec_physical_port"s. */ }; struct vtep_ctl_port { struct ovs_list ports_node; /* In struct vtep_ctl_pswitch's 'ports' list. */ const struct vteprec_physical_port *port_cfg; struct vtep_ctl_pswitch *ps; struct shash bindings; /* Maps from vlan to vtep_ctl_lswitch. */ }; struct vtep_ctl_lswitch { const struct vteprec_logical_switch *ls_cfg; char *name; struct shash ucast_local; /* Maps from mac to vteprec_ucast_macs_local. */ struct shash ucast_remote; /* Maps from mac to vteprec_ucast_macs_remote.*/ struct shash mcast_local; /* Maps from mac to vtep_ctl_mcast_mac. */ struct shash mcast_remote; /* Maps from mac to vtep_ctl_mcast_mac. */ }; struct vtep_ctl_mcast_mac { const struct vteprec_mcast_macs_local *local_cfg; const struct vteprec_mcast_macs_remote *remote_cfg; const struct vteprec_physical_locator_set *ploc_set_cfg; struct ovs_list locators; /* Contains 'vtep_ctl_ploc's. */ }; struct vtep_ctl_ploc { struct ovs_list locators_node; /* In struct vtep_ctl_ploc_set's 'locators' list. */ const struct vteprec_physical_locator *ploc_cfg; }; static void verify_ports(struct vtep_ctl_context *vtepctl_ctx) { if (!vtepctl_ctx->verified_ports) { const struct vteprec_physical_switch *ps; vteprec_global_verify_switches(vtepctl_ctx->vtep_global); VTEPREC_PHYSICAL_SWITCH_FOR_EACH (ps, vtepctl_ctx->base.idl) { vteprec_physical_switch_verify_ports(ps); } vtepctl_ctx->verified_ports = true; } } static struct vtep_ctl_port * add_port_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps, struct vteprec_physical_port *port_cfg) { char *cache_name = xasprintf("%s+%s", ps->name, port_cfg->name); struct vtep_ctl_port *port; port = xmalloc(sizeof *port); list_push_back(&ps->ports, &port->ports_node); port->port_cfg = port_cfg; port->ps = ps; shash_add(&vtepctl_ctx->ports, cache_name, port); free(cache_name); shash_init(&port->bindings); return port; } static void del_cached_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port) { char *cache_name = xasprintf("%s+%s", port->ps->name, port->port_cfg->name); list_remove(&port->ports_node); shash_find_and_delete(&vtepctl_ctx->ports, cache_name); vteprec_physical_port_delete(port->port_cfg); free(cache_name); free(port); } static void add_pswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vteprec_physical_switch *ps_cfg) { struct vtep_ctl_pswitch *ps = xmalloc(sizeof *ps); ps->ps_cfg = ps_cfg; ps->name = xstrdup(ps_cfg->name); list_init(&ps->ports); shash_add(&vtepctl_ctx->pswitches, ps->name, ps); } static void vtep_delete_pswitch(const struct vteprec_global *vtep_global, const struct vteprec_physical_switch *ps) { struct vteprec_physical_switch **pswitches; size_t i, n; pswitches = xmalloc(sizeof *vtep_global->switches * vtep_global->n_switches); for (i = n = 0; i < vtep_global->n_switches; i++) { if (vtep_global->switches[i] != ps) { pswitches[n++] = vtep_global->switches[i]; } } vteprec_global_set_switches(vtep_global, pswitches, n); free(pswitches); } static void del_cached_pswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_pswitch *ps) { ovs_assert(list_is_empty(&ps->ports)); if (ps->ps_cfg) { vteprec_physical_switch_delete(ps->ps_cfg); vtep_delete_pswitch(ctx->vtep_global, ps->ps_cfg); } shash_find_and_delete(&ctx->pswitches, ps->name); free(ps->name); free(ps); } static struct vtep_ctl_lswitch * add_lswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx, const struct vteprec_logical_switch *ls_cfg) { struct vtep_ctl_lswitch *ls = xmalloc(sizeof *ls); ls->ls_cfg = ls_cfg; ls->name = xstrdup(ls_cfg->name); shash_add(&vtepctl_ctx->lswitches, ls->name, ls); shash_init(&ls->ucast_local); shash_init(&ls->ucast_remote); shash_init(&ls->mcast_local); shash_init(&ls->mcast_remote); return ls; } static void del_cached_lswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_lswitch *ls) { if (ls->ls_cfg) { vteprec_logical_switch_delete(ls->ls_cfg); } shash_find_and_delete(&ctx->lswitches, ls->name); free(ls->name); free(ls); } static void commit_ls_bindings(struct vtep_ctl_port *port) { struct vteprec_logical_switch **binding_values; int64_t *binding_keys; size_t n_bindings; struct shash_node *node; int i; n_bindings = shash_count(&port->bindings); binding_keys = xmalloc(n_bindings * sizeof *binding_keys); binding_values = xmalloc(n_bindings * sizeof *binding_values); i = 0; SHASH_FOR_EACH(node, &port->bindings) { struct vtep_ctl_lswitch *ls_entry = node->data; binding_keys[i] = strtoll(node->name, NULL, 0); binding_values[i] = (struct vteprec_logical_switch *)ls_entry->ls_cfg; i++; } vteprec_physical_port_set_vlan_bindings(port->port_cfg, binding_keys, binding_values, n_bindings); free(binding_values); free(binding_keys); } static void add_ls_binding_to_cache(struct vtep_ctl_port *port, const char *vlan, struct vtep_ctl_lswitch *ls) { if (shash_find(&port->bindings, vlan)) { ctl_fatal("multiple bindings for vlan %s", vlan); } shash_add(&port->bindings, vlan, ls); } static void del_cached_ls_binding(struct vtep_ctl_port *port, const char *vlan) { if (!shash_find(&port->bindings, vlan)) { ctl_fatal("no binding for vlan %s", vlan); } shash_find_and_delete(&port->bindings, vlan); } static struct vteprec_physical_locator * find_ploc(struct vtep_ctl_context *vtepctl_ctx, const char *encap, const char *dst_ip) { struct vteprec_physical_locator *ploc; char *name = xasprintf("%s+%s", encap, dst_ip); ovs_assert(vtepctl_ctx->cache_valid); ploc = shash_find_data(&vtepctl_ctx->plocs, name); free(name); return ploc; } static void add_ploc_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vteprec_physical_locator *ploc) { char *name = xasprintf("%s+%s", ploc->encapsulation_type, ploc->dst_ip); struct vteprec_physical_locator *orig_ploc; orig_ploc = find_ploc(vtepctl_ctx, ploc->encapsulation_type, ploc->dst_ip); if (!orig_ploc) { shash_add(&vtepctl_ctx->plocs, name, ploc); } free(name); } static void add_ploc_to_mcast_mac(struct vtep_ctl_mcast_mac *mcast_mac, struct vteprec_physical_locator *ploc_cfg) { struct vtep_ctl_ploc *ploc; ploc = xmalloc(sizeof *ploc); ploc->ploc_cfg = ploc_cfg; list_push_back(&mcast_mac->locators, &ploc->locators_node); } static void del_ploc_from_mcast_mac(struct vtep_ctl_mcast_mac *mcast_mac, struct vteprec_physical_locator *ploc_cfg) { struct vtep_ctl_ploc *ploc; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { if (ploc->ploc_cfg == ploc_cfg) { list_remove(&ploc->locators_node); free(ploc); return; } } } static struct vtep_ctl_mcast_mac * add_mcast_mac_to_cache(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lswitch *ls, const char *mac, struct vteprec_physical_locator_set *ploc_set_cfg, bool local) { struct vtep_ctl_mcast_mac *mcast_mac; struct shash *mcast_shash; size_t i; mcast_mac = xmalloc(sizeof *mcast_mac); mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; mcast_mac->ploc_set_cfg = ploc_set_cfg; list_init(&mcast_mac->locators); shash_add(mcast_shash, mac, mcast_mac); for (i = 0; i < ploc_set_cfg->n_locators; i++) { struct vteprec_physical_locator *ploc_cfg; ploc_cfg = ploc_set_cfg->locators[i]; add_ploc_to_mcast_mac(mcast_mac, ploc_cfg); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } return mcast_mac; } static void vtep_ctl_context_invalidate_cache(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; if (!vtepctl_ctx->cache_valid) { return; } vtepctl_ctx->cache_valid = false; SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) { struct vtep_ctl_pswitch *ps = node->data; free(ps->name); free(ps); } shash_destroy(&vtepctl_ctx->pswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->ports) { struct vtep_ctl_port *port = node->data; shash_destroy(&port->bindings); } shash_destroy_free_data(&vtepctl_ctx->ports); SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) { struct vtep_ctl_lswitch *ls = node->data; struct shash_node *node2, *next_node2; shash_destroy(&ls->ucast_local); shash_destroy(&ls->ucast_remote); SHASH_FOR_EACH_SAFE (node2, next_node2, &ls->mcast_local) { struct vtep_ctl_mcast_mac *mcast_mac = node2->data; struct vtep_ctl_ploc *ploc, *next_ploc; LIST_FOR_EACH_SAFE (ploc, next_ploc, locators_node, &mcast_mac->locators) { free(ploc); } free(mcast_mac); } shash_destroy(&ls->mcast_local); SHASH_FOR_EACH_SAFE (node2, next_node2, &ls->mcast_remote) { struct vtep_ctl_mcast_mac *mcast_mac = node2->data; struct vtep_ctl_ploc *ploc, *next_ploc; LIST_FOR_EACH_SAFE (ploc, next_ploc, locators_node, &mcast_mac->locators) { free(ploc); } free(mcast_mac); } shash_destroy(&ls->mcast_remote); free(ls->name); free(ls); } shash_destroy(&vtepctl_ctx->lswitches); shash_destroy(&vtepctl_ctx->plocs); } static void pre_get_info(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_switches); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_ports); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_switch_col_tunnels); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_port_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_port_col_vlan_bindings); ovsdb_idl_add_column(ctx->idl, &vteprec_logical_switch_col_name); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_locator); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_local_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_locator); ovsdb_idl_add_column(ctx->idl, &vteprec_ucast_macs_remote_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_locator_set); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_local_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_MAC); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_locator_set); ovsdb_idl_add_column(ctx->idl, &vteprec_mcast_macs_remote_col_logical_switch); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_set_col_locators); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_col_dst_ip); ovsdb_idl_add_column(ctx->idl, &vteprec_physical_locator_col_encapsulation_type); ovsdb_idl_add_column(ctx->idl, &vteprec_tunnel_col_local); ovsdb_idl_add_column(ctx->idl, &vteprec_tunnel_col_remote); } static void vtep_ctl_context_populate_cache(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; const struct vteprec_logical_switch *ls_cfg; const struct vteprec_ucast_macs_local *ucast_local_cfg; const struct vteprec_ucast_macs_remote *ucast_remote_cfg; const struct vteprec_mcast_macs_local *mcast_local_cfg; const struct vteprec_mcast_macs_remote *mcast_remote_cfg; const struct vteprec_tunnel *tunnel_cfg; struct sset pswitches, ports, lswitches; size_t i; if (vtepctl_ctx->cache_valid) { /* Cache is already populated. */ return; } vtepctl_ctx->cache_valid = true; shash_init(&vtepctl_ctx->pswitches); shash_init(&vtepctl_ctx->ports); shash_init(&vtepctl_ctx->lswitches); shash_init(&vtepctl_ctx->plocs); sset_init(&pswitches); sset_init(&ports); for (i = 0; i < vtep_global->n_switches; i++) { struct vteprec_physical_switch *ps_cfg = vtep_global->switches[i]; size_t j; if (!sset_add(&pswitches, ps_cfg->name)) { VLOG_WARN("%s: database contains duplicate physical switch name", ps_cfg->name); continue; } add_pswitch_to_cache(vtepctl_ctx, ps_cfg); for (j = 0; j < ps_cfg->n_ports; j++) { struct vteprec_physical_port *port_cfg = ps_cfg->ports[j]; if (!sset_add(&ports, port_cfg->name)) { /* Duplicate port name. (We will warn about that later.) */ continue; } } } sset_destroy(&pswitches); sset_destroy(&ports); sset_init(&lswitches); VTEPREC_LOGICAL_SWITCH_FOR_EACH (ls_cfg, ctx->idl) { if (!sset_add(&lswitches, ls_cfg->name)) { VLOG_WARN("%s: database contains duplicate logical switch name", ls_cfg->name); continue; } add_lswitch_to_cache(vtepctl_ctx, ls_cfg); } sset_destroy(&lswitches); VTEPREC_UCAST_MACS_LOCAL_FOR_EACH (ucast_local_cfg, ctx->idl) { struct vtep_ctl_lswitch *ls; if (!ucast_local_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, ucast_local_cfg->logical_switch->name, false); if (!ls) { continue; } if (ucast_local_cfg->locator) { add_ploc_to_cache(vtepctl_ctx, ucast_local_cfg->locator); } shash_add(&ls->ucast_local, ucast_local_cfg->MAC, ucast_local_cfg); } VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (ucast_remote_cfg, ctx->idl) { struct vtep_ctl_lswitch *ls; if (!ucast_remote_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, ucast_remote_cfg->logical_switch->name, false); if (!ls) { continue; } if (ucast_remote_cfg->locator) { add_ploc_to_cache(vtepctl_ctx, ucast_remote_cfg->locator); } shash_add(&ls->ucast_remote, ucast_remote_cfg->MAC, ucast_remote_cfg); } VTEPREC_MCAST_MACS_LOCAL_FOR_EACH (mcast_local_cfg, ctx->idl) { struct vtep_ctl_mcast_mac *mcast_mac; struct vtep_ctl_lswitch *ls; if (!mcast_local_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, mcast_local_cfg->logical_switch->name, false); if (!ls) { continue; } mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_local_cfg->MAC, mcast_local_cfg->locator_set, true); mcast_mac->local_cfg = mcast_local_cfg; } VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mcast_remote_cfg, ctx->idl) { struct vtep_ctl_mcast_mac *mcast_mac; struct vtep_ctl_lswitch *ls; if (!mcast_remote_cfg->logical_switch) { continue; } ls = find_lswitch(vtepctl_ctx, mcast_remote_cfg->logical_switch->name, false); if (!ls) { continue; } mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_remote_cfg->MAC, mcast_remote_cfg->locator_set, false); mcast_mac->remote_cfg = mcast_remote_cfg; } VTEPREC_TUNNEL_FOR_EACH (tunnel_cfg, ctx->idl) { if (tunnel_cfg->local) { add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->local); } if (tunnel_cfg->remote) { add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->remote); } } sset_init(&pswitches); for (i = 0; i < vtep_global->n_switches; i++) { struct vteprec_physical_switch *ps_cfg = vtep_global->switches[i]; struct vtep_ctl_pswitch *ps; size_t j; if (!sset_add(&pswitches, ps_cfg->name)) { continue; } ps = shash_find_data(&vtepctl_ctx->pswitches, ps_cfg->name); for (j = 0; j < ps_cfg->n_ports; j++) { struct vteprec_physical_port *port_cfg = ps_cfg->ports[j]; struct vtep_ctl_port *port; size_t k; port = shash_find_data(&vtepctl_ctx->ports, port_cfg->name); if (port) { if (port_cfg == port->port_cfg) { VLOG_WARN("%s: port is in multiple physical switches " "(%s and %s)", port_cfg->name, ps->name, port->ps->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server shouldn't * have allowed it. */ VLOG_ERR("%s: database contains duplicate port name", port_cfg->name); } continue; } port = add_port_to_cache(vtepctl_ctx, ps, port_cfg); for (k = 0; k < port_cfg->n_vlan_bindings; k++) { struct vteprec_logical_switch *ls_cfg; struct vtep_ctl_lswitch *ls; char *vlan; vlan = xasprintf("%"PRId64, port_cfg->key_vlan_bindings[k]); if (shash_find(&port->bindings, vlan)) { ctl_fatal("multiple bindings for vlan %s", vlan); } ls_cfg = port_cfg->value_vlan_bindings[k]; ls = find_lswitch(vtepctl_ctx, ls_cfg->name, true); shash_add_nocopy(&port->bindings, vlan, ls); } } } sset_destroy(&pswitches); } static struct vtep_ctl_pswitch * find_pswitch(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist) { struct vtep_ctl_pswitch *ps; ovs_assert(vtepctl_ctx->cache_valid); ps = shash_find_data(&vtepctl_ctx->pswitches, name); if (must_exist && !ps) { ctl_fatal("no physical switch named %s", name); } vteprec_global_verify_switches(vtepctl_ctx->vtep_global); return ps; } static struct vtep_ctl_port * find_port(struct vtep_ctl_context *vtepctl_ctx, const char *ps_name, const char *port_name, bool must_exist) { char *cache_name = xasprintf("%s+%s", ps_name, port_name); struct vtep_ctl_port *port; ovs_assert(vtepctl_ctx->cache_valid); port = shash_find_data(&vtepctl_ctx->ports, cache_name); if (port && !strcmp(port_name, port->ps->name)) { port = NULL; } free(cache_name); if (must_exist && !port) { ctl_fatal("no port named %s", port_name); } verify_ports(vtepctl_ctx); return port; } static void pswitch_insert_port(const struct vteprec_physical_switch *ps, struct vteprec_physical_port *port) { struct vteprec_physical_port **ports; size_t i; ports = xmalloc(sizeof *ps->ports * (ps->n_ports + 1)); for (i = 0; i < ps->n_ports; i++) { ports[i] = ps->ports[i]; } ports[ps->n_ports] = port; vteprec_physical_switch_set_ports(ps, ports, ps->n_ports + 1); free(ports); } static void pswitch_delete_port(const struct vteprec_physical_switch *ps, const struct vteprec_physical_port *port) { struct vteprec_physical_port **ports; size_t i, n; ports = xmalloc(sizeof *ps->ports * ps->n_ports); for (i = n = 0; i < ps->n_ports; i++) { if (ps->ports[i] != port) { ports[n++] = ps->ports[i]; } } vteprec_physical_switch_set_ports(ps, ports, n); free(ports); } static void vtep_insert_pswitch(const struct vteprec_global *vtep_global, struct vteprec_physical_switch *ps) { struct vteprec_physical_switch **pswitches; size_t i; pswitches = xmalloc(sizeof *vtep_global->switches * (vtep_global->n_switches + 1)); for (i = 0; i < vtep_global->n_switches; i++) { pswitches[i] = vtep_global->switches[i]; } pswitches[vtep_global->n_switches] = ps; vteprec_global_set_switches(vtep_global, pswitches, vtep_global->n_switches + 1); free(pswitches); } static void cmd_add_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const char *ps_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vteprec_physical_switch *ps; vtep_ctl_context_populate_cache(ctx); if (find_pswitch(vtepctl_ctx, ps_name, false)) { if (!may_exist) { ctl_fatal("cannot create physical switch %s because it " "already exists", ps_name); } return; } ps = vteprec_physical_switch_insert(ctx->txn); vteprec_physical_switch_set_name(ps, ps_name); vtep_insert_pswitch(vtepctl_ctx->vtep_global, ps); vtep_ctl_context_invalidate_cache(ctx); } static void del_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port) { pswitch_delete_port(port->ps->ps_cfg, port->port_cfg); del_cached_port(vtepctl_ctx, port); } static void del_pswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps) { struct vtep_ctl_port *port, *next_port; LIST_FOR_EACH_SAFE (port, next_port, ports_node, &ps->ports) { del_port(vtepctl_ctx, port); } del_cached_pswitch(vtepctl_ctx, ps); } static void cmd_del_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_pswitch *ps; vtep_ctl_context_populate_cache(ctx); ps = find_pswitch(vtepctl_ctx, ctx->argv[1], must_exist); if (ps) { del_pswitch(vtepctl_ctx, ps); } } static void output_sorted(struct svec *svec, struct ds *output) { const char *name; size_t i; svec_sort(svec); SVEC_FOR_EACH (i, name, svec) { ds_put_format(output, "%s\n", name); } } static void cmd_list_ps(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; struct svec pswitches; vtep_ctl_context_populate_cache(ctx); svec_init(&pswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) { struct vtep_ctl_pswitch *ps = node->data; svec_add(&pswitches, ps->name); } output_sorted(&pswitches, &ctx->output); svec_destroy(&pswitches); } static void cmd_ps_exists(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); vtep_ctl_context_populate_cache(ctx); if (!find_pswitch(vtepctl_ctx, ctx->argv[1], false)) { vtep_ctl_exit(2); } } static void cmd_list_ports(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_pswitch *ps; struct vtep_ctl_port *port; struct svec ports; vtep_ctl_context_populate_cache(ctx); ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true); vteprec_physical_switch_verify_ports(ps->ps_cfg); svec_init(&ports); LIST_FOR_EACH (port, ports_node, &ps->ports) { if (strcmp(port->port_cfg->name, ps->name)) { svec_add(&ports, port->port_cfg->name); } } output_sorted(&ports, &ctx->output); svec_destroy(&ports); } static void add_port(struct ctl_context *ctx, const char *ps_name, const char *port_name, bool may_exist) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_port *vtep_ctl_port; struct vtep_ctl_pswitch *ps; struct vteprec_physical_port *port; vtep_ctl_context_populate_cache(ctx); vtep_ctl_port = find_port(vtepctl_ctx, ps_name, port_name, false); if (vtep_ctl_port) { if (!may_exist) { ctl_fatal("cannot create a port named %s on %s because a " "port with that name already exists", port_name, ps_name); } return; } ps = find_pswitch(vtepctl_ctx, ps_name, true); port = vteprec_physical_port_insert(ctx->txn); vteprec_physical_port_set_name(port, port_name); pswitch_insert_port(ps->ps_cfg, port); add_port_to_cache(vtepctl_ctx, ps, port); } static void cmd_add_port(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist); } static void cmd_del_port(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_port *port; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], must_exist); if (port) { if (ctx->argc == 3) { struct vtep_ctl_pswitch *ps; ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true); if (port->ps != ps) { ctl_fatal("physical switch %s does not have a port %s", ctx->argv[1], ctx->argv[2]); } } del_port(vtepctl_ctx, port); } } static struct vtep_ctl_lswitch * find_lswitch(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist) { struct vtep_ctl_lswitch *ls; ovs_assert(vtepctl_ctx->cache_valid); ls = shash_find_data(&vtepctl_ctx->lswitches, name); if (must_exist && !ls) { ctl_fatal("no logical switch named %s", name); } return ls; } static void cmd_add_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const char *ls_name = ctx->argv[1]; bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; struct vteprec_logical_switch *ls; vtep_ctl_context_populate_cache(ctx); if (find_lswitch(vtepctl_ctx, ls_name, false)) { if (!may_exist) { ctl_fatal("cannot create logical switch %s because it " "already exists", ls_name); } return; } ls = vteprec_logical_switch_insert(ctx->txn); vteprec_logical_switch_set_name(ls, ls_name); vtep_ctl_context_invalidate_cache(ctx); } static void del_lswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lswitch *ls) { del_cached_lswitch(vtepctl_ctx, ls); } static void cmd_del_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vtep_ctl_lswitch *ls; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], must_exist); if (ls) { del_lswitch(vtepctl_ctx, ls); } } static void cmd_list_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash_node *node; struct svec lswitches; vtep_ctl_context_populate_cache(ctx); svec_init(&lswitches); SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) { struct vtep_ctl_lswitch *ls = node->data; svec_add(&lswitches, ls->name); } output_sorted(&lswitches, &ctx->output); svec_destroy(&lswitches); } static void cmd_ls_exists(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); vtep_ctl_context_populate_cache(ctx); if (!find_lswitch(vtepctl_ctx, ctx->argv[1], false)) { vtep_ctl_exit(2); } } static void cmd_list_bindings(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct shash_node *node; struct vtep_ctl_port *port; struct svec bindings; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); svec_init(&bindings); SHASH_FOR_EACH (node, &port->bindings) { struct vtep_ctl_lswitch *lswitch = node->data; char *binding; binding = xasprintf("%04lld %s", strtoll(node->name, NULL, 0), lswitch->name); svec_add_nocopy(&bindings, binding); } output_sorted(&bindings, &ctx->output); svec_destroy(&bindings); } static void cmd_bind_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; struct vtep_ctl_port *port; const char *vlan; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); vlan = ctx->argv[3]; ls = find_lswitch(vtepctl_ctx, ctx->argv[4], true); add_ls_binding_to_cache(port, vlan, ls); commit_ls_bindings(port); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_unbind_ls(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_port *port; const char *vlan; vtep_ctl_context_populate_cache(ctx); port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true); vlan = ctx->argv[3]; del_cached_ls_binding(port, vlan); commit_ls_bindings(port); vtep_ctl_context_invalidate_cache(ctx); } static void add_ucast_entry(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *mac; const char *encap; const char *dst_ip; struct vteprec_physical_locator *ploc_cfg; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); mac = ctx->argv[2]; if (ctx->argc == 4) { encap = "vxlan_over_ipv4"; dst_ip = ctx->argv[3]; } else { encap = ctx->argv[3]; dst_ip = ctx->argv[4]; } ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { ploc_cfg = vteprec_physical_locator_insert(ctx->txn); vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip); vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } if (local) { struct vteprec_ucast_macs_local *ucast_cfg; ucast_cfg = shash_find_data(&ls->ucast_local, mac); if (!ucast_cfg) { ucast_cfg = vteprec_ucast_macs_local_insert(ctx->txn); vteprec_ucast_macs_local_set_MAC(ucast_cfg, mac); vteprec_ucast_macs_local_set_logical_switch(ucast_cfg, ls->ls_cfg); shash_add(&ls->ucast_local, mac, ucast_cfg); } vteprec_ucast_macs_local_set_locator(ucast_cfg, ploc_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg; ucast_cfg = shash_find_data(&ls->ucast_remote, mac); if (!ucast_cfg) { ucast_cfg = vteprec_ucast_macs_remote_insert(ctx->txn); vteprec_ucast_macs_remote_set_MAC(ucast_cfg, mac); vteprec_ucast_macs_remote_set_logical_switch(ucast_cfg, ls->ls_cfg); shash_add(&ls->ucast_remote, mac, ucast_cfg); } vteprec_ucast_macs_remote_set_locator(ucast_cfg, ploc_cfg); } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_add_ucast_local(struct ctl_context *ctx) { add_ucast_entry(ctx, true); } static void cmd_add_ucast_remote(struct ctl_context *ctx) { add_ucast_entry(ctx, false); } static void del_ucast_entry(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; struct shash *ucast_shash; struct shash_node *node; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; node = shash_find(ucast_shash, ctx->argv[2]); if (!node) { return; } if (local) { struct vteprec_ucast_macs_local *ucast_cfg = node->data; vteprec_ucast_macs_local_delete(ucast_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg = node->data; vteprec_ucast_macs_remote_delete(ucast_cfg); } shash_delete(ucast_shash, node); vtep_ctl_context_invalidate_cache(ctx); } static void cmd_del_ucast_local(struct ctl_context *ctx) { del_ucast_entry(ctx, true); } static void cmd_del_ucast_remote(struct ctl_context *ctx) { del_ucast_entry(ctx, false); } static void commit_mcast_entries(struct vtep_ctl_mcast_mac *mcast_mac) { struct vtep_ctl_ploc *ploc; struct vteprec_physical_locator **locators = NULL; size_t n_locators; int i; n_locators = list_size(&mcast_mac->locators); ovs_assert(n_locators); locators = xmalloc(n_locators * sizeof *locators); i = 0; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { locators[i] = (struct vteprec_physical_locator *)ploc->ploc_cfg; i++; } vteprec_physical_locator_set_set_locators(mcast_mac->ploc_set_cfg, locators, n_locators); free(locators); } static void add_mcast_entry(struct ctl_context *ctx, struct vtep_ctl_lswitch *ls, const char *mac, const char *encap, const char *dst_ip, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct shash *mcast_shash; struct vtep_ctl_mcast_mac *mcast_mac; struct vteprec_physical_locator *ploc_cfg; struct vteprec_physical_locator_set *ploc_set_cfg; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; /* Physical locator sets are immutable, so allocate a new one. */ ploc_set_cfg = vteprec_physical_locator_set_insert(ctx->txn); mcast_mac = shash_find_data(mcast_shash, mac); if (!mcast_mac) { mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mac, ploc_set_cfg, local); if (local) { mcast_mac->local_cfg = vteprec_mcast_macs_local_insert(ctx->txn); vteprec_mcast_macs_local_set_MAC(mcast_mac->local_cfg, mac); vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); vteprec_mcast_macs_local_set_logical_switch(mcast_mac->local_cfg, ls->ls_cfg); mcast_mac->remote_cfg = NULL; } else { mcast_mac->remote_cfg = vteprec_mcast_macs_remote_insert(ctx->txn); vteprec_mcast_macs_remote_set_MAC(mcast_mac->remote_cfg, mac); vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); vteprec_mcast_macs_remote_set_logical_switch(mcast_mac->remote_cfg, ls->ls_cfg); mcast_mac->local_cfg = NULL; } } else { mcast_mac->ploc_set_cfg = ploc_set_cfg; if (local) { vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); } else { vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); } } ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { ploc_cfg = vteprec_physical_locator_insert(ctx->txn); vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip); vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap); add_ploc_to_cache(vtepctl_ctx, ploc_cfg); } add_ploc_to_mcast_mac(mcast_mac, ploc_cfg); commit_mcast_entries(mcast_mac); } static void del_mcast_entry(struct ctl_context *ctx, struct vtep_ctl_lswitch *ls, const char *mac, const char *encap, const char *dst_ip, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_mcast_mac *mcast_mac; struct shash *mcast_shash; struct vteprec_physical_locator *ploc_cfg; struct vteprec_physical_locator_set *ploc_set_cfg; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; mcast_mac = shash_find_data(mcast_shash, mac); if (!mcast_mac) { return; } ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip); if (!ploc_cfg) { /* Couldn't find the physical locator, so just ignore. */ return; } /* Physical locator sets are immutable, so allocate a new one. */ ploc_set_cfg = vteprec_physical_locator_set_insert(ctx->txn); mcast_mac->ploc_set_cfg = ploc_set_cfg; del_ploc_from_mcast_mac(mcast_mac, ploc_cfg); if (list_is_empty(&mcast_mac->locators)) { struct shash_node *node = shash_find(mcast_shash, mac); vteprec_physical_locator_set_delete(ploc_set_cfg); if (local) { vteprec_mcast_macs_local_delete(mcast_mac->local_cfg); } else { vteprec_mcast_macs_remote_delete(mcast_mac->remote_cfg); } free(node->data); shash_delete(mcast_shash, node); } else { if (local) { vteprec_mcast_macs_local_set_locator_set(mcast_mac->local_cfg, ploc_set_cfg); } else { vteprec_mcast_macs_remote_set_locator_set(mcast_mac->remote_cfg, ploc_set_cfg); } commit_mcast_entries(mcast_mac); } } static void add_del_mcast_entry(struct ctl_context *ctx, bool add, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const char *mac; const char *encap; const char *dst_ip; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); mac = ctx->argv[2]; if (ctx->argc == 4) { encap = "vxlan_over_ipv4"; dst_ip = ctx->argv[3]; } else { encap = ctx->argv[3]; dst_ip = ctx->argv[4]; } if (add) { add_mcast_entry(ctx, ls, mac, encap, dst_ip, local); } else { del_mcast_entry(ctx, ls, mac, encap, dst_ip, local); } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_add_mcast_local(struct ctl_context *ctx) { add_del_mcast_entry(ctx, true, true); } static void cmd_add_mcast_remote(struct ctl_context *ctx) { add_del_mcast_entry(ctx, true, false); } static void cmd_del_mcast_local(struct ctl_context *ctx) { add_del_mcast_entry(ctx, false, true); } static void cmd_del_mcast_remote(struct ctl_context *ctx) { add_del_mcast_entry(ctx, false, false); } static void clear_macs(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const struct shash_node *node; struct shash *ucast_shash; struct shash *mcast_shash; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; SHASH_FOR_EACH (node, ucast_shash) { if (local) { struct vteprec_ucast_macs_local *ucast_cfg = node->data; vteprec_ucast_macs_local_delete(ucast_cfg); } else { struct vteprec_ucast_macs_remote *ucast_cfg = node->data; vteprec_ucast_macs_remote_delete(ucast_cfg); } } SHASH_FOR_EACH (node, mcast_shash) { struct vtep_ctl_mcast_mac *mcast_mac = node->data; if (local) { vteprec_mcast_macs_local_delete(mcast_mac->local_cfg); } else { vteprec_mcast_macs_remote_delete(mcast_mac->remote_cfg); } } vtep_ctl_context_invalidate_cache(ctx); } static void cmd_clear_local_macs(struct ctl_context *ctx) { clear_macs(ctx, true); } static void cmd_clear_remote_macs(struct ctl_context *ctx) { clear_macs(ctx, false); } static void list_macs(struct ctl_context *ctx, bool local) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); struct vtep_ctl_lswitch *ls; const struct shash_node *node; struct shash *ucast_shash; struct svec ucast_macs; struct shash *mcast_shash; struct svec mcast_macs; vtep_ctl_context_populate_cache(ctx); ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true); ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote; mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote; svec_init(&ucast_macs); SHASH_FOR_EACH (node, ucast_shash) { struct vteprec_ucast_macs_local *ucast_local = node->data; struct vteprec_ucast_macs_remote *ucast_remote = node->data; struct vteprec_physical_locator *ploc_cfg; char *entry; ploc_cfg = local ? ucast_local->locator : ucast_remote->locator; entry = xasprintf(" %s -> %s/%s", node->name, ploc_cfg->encapsulation_type, ploc_cfg->dst_ip); svec_add_nocopy(&ucast_macs, entry); } ds_put_format(&ctx->output, "ucast-mac-%s\n", local ? "local" : "remote"); output_sorted(&ucast_macs, &ctx->output); ds_put_char(&ctx->output, '\n'); svec_destroy(&ucast_macs); svec_init(&mcast_macs); SHASH_FOR_EACH (node, mcast_shash) { struct vtep_ctl_mcast_mac *mcast_mac = node->data; struct vtep_ctl_ploc *ploc; char *entry; LIST_FOR_EACH (ploc, locators_node, &mcast_mac->locators) { entry = xasprintf(" %s -> %s/%s", node->name, ploc->ploc_cfg->encapsulation_type, ploc->ploc_cfg->dst_ip); svec_add_nocopy(&mcast_macs, entry); } } ds_put_format(&ctx->output, "mcast-mac-%s\n", local ? "local" : "remote"); output_sorted(&mcast_macs, &ctx->output); ds_put_char(&ctx->output, '\n'); svec_destroy(&mcast_macs); } static void cmd_list_local_macs(struct ctl_context *ctx) { list_macs(ctx, true); } static void cmd_list_remote_macs(struct ctl_context *ctx) { list_macs(ctx, false); } static void verify_managers(const struct vteprec_global *vtep_global) { size_t i; vteprec_global_verify_managers(vtep_global); for (i = 0; i < vtep_global->n_managers; ++i) { const struct vteprec_manager *mgr = vtep_global->managers[i]; vteprec_manager_verify_target(mgr); } } static void pre_manager(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_managers); ovsdb_idl_add_column(ctx->idl, &vteprec_manager_col_target); } static void cmd_get_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; struct svec targets; size_t i; verify_managers(vtep_global); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < vtep_global->n_managers; i++) { svec_add(&targets, vtep_global->managers[i]->target); } svec_sort_unique(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_managers(const struct vtep_ctl_context *vtepctl_ctx) { const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; size_t i; /* Delete Manager rows pointed to by 'managers' column. */ for (i = 0; i < vtep_global->n_managers; i++) { vteprec_manager_delete(vtep_global->managers[i]); } /* Delete 'Manager' row refs in 'managers' column. */ vteprec_global_set_managers(vtep_global, NULL, 0); } static void cmd_del_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global; verify_managers(vtep_global); delete_managers(vtepctl_ctx); } static void insert_managers(struct vtep_ctl_context *vtepctl_ctx, char *targets[], size_t n) { struct vteprec_manager **managers; size_t i; /* Insert each manager in a new row in Manager table. */ managers = xmalloc(n * sizeof *managers); for (i = 0; i < n; i++) { if (stream_verify_name(targets[i]) && pstream_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } managers[i] = vteprec_manager_insert(vtepctl_ctx->base.txn); vteprec_manager_set_target(managers[i], targets[i]); } /* Store uuids of new Manager rows in 'managers' column. */ vteprec_global_set_managers(vtepctl_ctx->vtep_global, managers, n); free(managers); } static void cmd_set_manager(struct ctl_context *ctx) { struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx); const size_t n = ctx->argc - 1; verify_managers(vtepctl_ctx->vtep_global); delete_managers(vtepctl_ctx); insert_managers(vtepctl_ctx, &ctx->argv[1], n); } /* Parameter commands. */ static const struct ctl_table_class tables[] = { {&vteprec_table_global, {{&vteprec_table_global, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_logical_binding_stats, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_logical_switch, {{&vteprec_table_logical_switch, &vteprec_logical_switch_col_name, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_ucast_macs_local, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_ucast_macs_remote, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_mcast_macs_local, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_mcast_macs_remote, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_manager, {{&vteprec_table_manager, &vteprec_manager_col_target, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_physical_locator, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_physical_locator_set, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_physical_port, {{&vteprec_table_physical_port, &vteprec_physical_port_col_name, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_physical_switch, {{&vteprec_table_physical_switch, &vteprec_physical_switch_col_name, NULL}, {NULL, NULL, NULL}}}, {&vteprec_table_tunnel, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; static void vtep_ctl_context_init_command(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command) { ctl_context_init_command(&vtepctl_ctx->base, command); vtepctl_ctx->verified_ports = false; } static void vtep_ctl_context_init(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, const struct vteprec_global *vtep_global, struct ovsdb_symbol_table *symtab) { ctl_context_init(&vtepctl_ctx->base, command, idl, txn, symtab, vtep_ctl_context_invalidate_cache); if (command) { vtepctl_ctx->verified_ports = false; } vtepctl_ctx->vtep_global = vtep_global; vtepctl_ctx->cache_valid = false; } static void vtep_ctl_context_done_command(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command) { ctl_context_done_command(&vtepctl_ctx->base, command); } static void vtep_ctl_context_done(struct vtep_ctl_context *vtepctl_ctx, struct ctl_command *command) { ctl_context_done(&vtepctl_ctx->base, command); } static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; ovsdb_idl_add_table(idl, &vteprec_table_global); for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct vtep_ctl_context vtepctl_ctx; ds_init(&c->output); c->table = NULL; vtep_ctl_context_init(&vtepctl_ctx, c, idl, NULL, NULL, NULL); (c->syntax->prerequisites)(&vtepctl_ctx.base); vtep_ctl_context_done(&vtepctl_ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static void do_vtep_ctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; const struct vteprec_global *vtep_global; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct vtep_ctl_context vtepctl_ctx; struct ctl_command *c; struct shash_node *node; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ovsdb_idl_txn_add_comment(txn, "vtep-ctl: %s", args); vtep_global = vteprec_global_first(idl); if (!vtep_global) { /* XXX add verification that table is empty */ vtep_global = vteprec_global_insert(txn); } symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } vtep_ctl_context_init(&vtepctl_ctx, NULL, idl, txn, vtep_global, symtab); for (c = commands; c < &commands[n_commands]; c++) { vtep_ctl_context_init_command(&vtepctl_ctx, c); if (c->syntax->run) { (c->syntax->run)(&vtepctl_ctx.base); } vtep_ctl_context_done_command(&vtepctl_ctx, c); if (vtepctl_ctx.base.try_again) { vtep_ctl_context_done(&vtepctl_ctx, NULL); goto try_again; } } vtep_ctl_context_done(&vtepctl_ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created " "(e.g. with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { vtep_ctl_context_init(&vtepctl_ctx, c, idl, txn, vtep_global, symtab); (c->syntax->postprocess)(&vtepctl_ctx.base); vtep_ctl_context_done(&vtepctl_ctx, c); } } } error = xstrdup(ovsdb_idl_txn_get_error(txn)); ovsdb_idl_txn_destroy(txn); txn = the_idl_txn = NULL; switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", error); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } free(error); ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); ovsdb_idl_destroy(idl); exit(EXIT_SUCCESS); try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ if (txn) { ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } free(error); } static const struct ctl_command_syntax vtep_commands[] = { /* Physical Switch commands. */ {"add-ps", 1, 1, NULL, pre_get_info, cmd_add_ps, NULL, "--may-exist", RW}, {"del-ps", 1, 1, NULL, pre_get_info, cmd_del_ps, NULL, "--if-exists", RW}, {"list-ps", 0, 0, NULL, pre_get_info, cmd_list_ps, NULL, "", RO}, {"ps-exists", 1, 1, NULL, pre_get_info, cmd_ps_exists, NULL, "", RO}, /* Port commands. */ {"list-ports", 1, 1, NULL, pre_get_info, cmd_list_ports, NULL, "", RO}, {"add-port", 2, 2, NULL, pre_get_info, cmd_add_port, NULL, "--may-exist", RW}, {"del-port", 2, 2, NULL, pre_get_info, cmd_del_port, NULL, "--if-exists", RW}, /* Logical Switch commands. */ {"add-ls", 1, 1, NULL, pre_get_info, cmd_add_ls, NULL, "--may-exist", RW}, {"del-ls", 1, 1, NULL, pre_get_info, cmd_del_ls, NULL, "--if-exists", RW}, {"list-ls", 0, 0, NULL, pre_get_info, cmd_list_ls, NULL, "", RO}, {"ls-exists", 1, 1, NULL, pre_get_info, cmd_ls_exists, NULL, "", RO}, {"list-bindings", 2, 2, NULL, pre_get_info, cmd_list_bindings, NULL, "", RO}, {"bind-ls", 4, 4, NULL, pre_get_info, cmd_bind_ls, NULL, "", RO}, {"unbind-ls", 3, 3, NULL, pre_get_info, cmd_unbind_ls, NULL, "", RO}, /* MAC binding commands. */ {"add-ucast-local", 3, 4, NULL, pre_get_info, cmd_add_ucast_local, NULL, "", RW}, {"del-ucast-local", 2, 2, NULL, pre_get_info, cmd_del_ucast_local, NULL, "", RW}, {"add-mcast-local", 3, 4, NULL, pre_get_info, cmd_add_mcast_local, NULL, "", RW}, {"del-mcast-local", 3, 4, NULL, pre_get_info, cmd_del_mcast_local, NULL, "", RW}, {"clear-local-macs", 1, 1, NULL, pre_get_info, cmd_clear_local_macs, NULL, "", RO}, {"list-local-macs", 1, 1, NULL, pre_get_info, cmd_list_local_macs, NULL, "", RO}, {"add-ucast-remote", 3, 4, NULL, pre_get_info, cmd_add_ucast_remote, NULL, "", RW}, {"del-ucast-remote", 2, 2, NULL, pre_get_info, cmd_del_ucast_remote, NULL, "", RW}, {"add-mcast-remote", 3, 4, NULL, pre_get_info, cmd_add_mcast_remote, NULL, "", RW}, {"del-mcast-remote", 3, 4, NULL, pre_get_info, cmd_del_mcast_remote, NULL, "", RW}, {"clear-remote-macs", 1, 1, NULL, pre_get_info, cmd_clear_remote_macs, NULL, "", RO}, {"list-remote-macs", 1, 1, NULL, pre_get_info, cmd_list_remote_macs, NULL, "", RO}, /* Manager commands. */ {"get-manager", 0, 0, NULL, pre_manager, cmd_get_manager, NULL, "", RO}, {"del-manager", 0, 0, NULL, pre_manager, cmd_del_manager, NULL, "", RW}, {"set-manager", 1, INT_MAX, NULL, pre_manager, cmd_set_manager, NULL, "", RW}, {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; /* Registers vsctl and common db commands. */ static void vtep_ctl_cmd_init(void) { ctl_init(tables, cmd_show_tables, vtep_ctl_exit); ctl_register_commands(vtep_commands); } openvswitch-2.5.9/vtep/PaxHeaders.82075/vtep.xml0000644000000000000000000000013213534540071016322 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.337850153 openvswitch-2.5.9/vtep/vtep.xml0000644000175000017500000013265613534540071020025 0ustar00jpettitjpettit00000000000000

    This schema specifies relations that a VTEP can use to integrate physical ports into logical switches maintained by a network virtualization controller such as NSX.

    Glossary:

    VTEP
    VXLAN Tunnel End Point, an entity which originates and/or terminates VXLAN tunnels.
    HSC
    Hardware Switch Controller.
    NVC
    Network Virtualization Controller, e.g. NSX.
    VRF
    Virtual Routing and Forwarding instance.
    Top-level configuration for a hardware VTEP. There must be exactly one record in the table.

    The physical switch or switches managed by the VTEP.

    When a physical switch integrates support for this VTEP schema, which is expected to be the most common case, this column should point to one record that represents the switch itself. In another possible implementation, a server or a VM presents a VTEP schema front-end interface to one or more physical switches, presumably communicating with those physical switches over a proprietary protocol. In that case, this column would point to one for each physical switch, and the set might change over time as the front-end server comes to represent a differing set of switches.

    These columns primarily configure the database server (ovsdb-server), not the hardware VTEP itself.

    Database clients to which the database server should connect or to which it should listen, along with options for how these connection should be configured. See the table for more information.

    Configuration for a database connection to an Open vSwitch Database (OVSDB) client.

    The database server can initiate and maintain active connections to remote clients. It can also listen for database connections.

    Connection method for managers.

    The following connection methods are currently supported:

    ssl:ip[:port]

    The specified SSL port (default: 6640) on the host at the given ip, which must be expressed as an IP address (not a DNS name).

    SSL key and certificate configuration happens outside the database.

    tcp:ip[:port]
    The specified TCP port (default: 6640) on the host at the given ip, which must be expressed as an IP address (not a DNS name).
    pssl:[port][:ip]

    Listens for SSL connections on the specified TCP port (default: 6640). If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address.

    ptcp:[port][:ip]
    Listens for connections on the specified TCP port (default: 6640). If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address.
    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to the client before sending an inactivity probe message. If the Open vSwitch database does not communicate with the client for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, the database server assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes. true if currently connected to this manager, false otherwise. A human-readable description of the last error on the connection to the manager; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the manager:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this manager last successfully connected to the database (in seconds). Value is empty if manager has never successfully connected. The amount of time since this manager last disconnected from the database (in seconds). Value is empty if manager has never disconnected. Space-separated list of the names of OVSDB locks that the connection holds. Omitted if the connection does not hold any locks. Space-separated list of the names of OVSDB locks that the connection is currently waiting to acquire. Omitted if the connection is not waiting for any locks. Space-separated list of the names of OVSDB locks that the connection has had stolen by another OVSDB client. Omitted if no locks have been stolen from this connection.

    When specifies a connection method that listens for inbound connections (e.g. ptcp: or pssl:) and more than one connection is actually active, the value is the number of active connections. Otherwise, this key-value pair is omitted.

    When multiple connections are active, status columns and key-value pairs (other than this one) report the status of one arbitrarily chosen connection.

    Additional configuration for a connection between the manager and the database server.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the manager and the database server. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    A physical switch that implements a VTEP. The physical ports within the switch. Tunnels created by this switch as instructed by the NVC. IPv4 or IPv6 addresses at which the switch may be contacted for management purposes.

    IPv4 or IPv6 addresses on which the switch may originate or terminate tunnels.

    This column is intended to allow a to determine the that terminates the tunnel represented by a .

    Symbolic name for the switch, such as its hostname. An extended description for the switch, such as its switch login banner.

    An entry in this column indicates to the NVC that this switch has encountered a fault. The switch must clear this column when the fault has been cleared.

    Indicates that the switch has been unable to process MAC entries requested by the NVC due to lack of table resources. Indicates that the switch has been unable to create tunnels requested by the NVC due to lack of resources. Indicates that an error has occurred in the switch but that no more specific information is available.
    A tunnel created by a . Tunnel end-point local to the physical switch. Tunnel end-point remote to the physical switch.

    BFD, defined in RFC 5880, allows point to point detection of connectivity failures by occasional transmission of BFD control messages. VTEPs are expected to implement BFD.

    BFD operates by regularly transmitting BFD control messages at a rate negotiated independently in each direction. Each endpoint specifies the rate at which it expects to receive control messages, and the rate at which it's willing to transmit them. An endpoint which fails to receive BFD control messages for a period of three times the expected reception rate will signal a connectivity fault. In the case of a unidirectional connectivity issue, the system not receiving BFD control messages will signal the problem to its peer in the messages it transmits.

    A hardware VTEP is expected to use BFD to determine reachability of devices at the end of the tunnels with which it exchanges data. This can enable the VTEP to choose a functioning service node among a set of service nodes providing high availability. It also enables the NVC to report the health status of tunnels.

    In many cases the BFD peer of a hardware VTEP will be an Open vSwitch instance. The Open vSwitch implementation of BFD aims to comply faithfully with the requirements put forth in RFC 5880. Open vSwitch does not implement the optional Authentication or ``Echo Mode'' features.

    The HSC writes the key-value pairs in the column to specify the local configurations to be used for BFD sessions on this tunnel.

    Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC expected as destination for received BFD packets. The default is 00:23:20:00:00:01. Set to an IPv4 address to set the IP address that is expected as destination for received BFD packets. The default is 169.254.1.0.

    The column is the remote counterpart of the column. The NVC writes the key-value pairs in this column.

    Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the destination MAC to be used for transmitted BFD packets. The default is 00:23:20:00:00:01. Set to an IPv4 address to set the IP address used as destination for transmitted BFD packets. The default is 169.254.1.1.

    The NVC sets up key-value pairs in the column to enable and configure BFD.

    True to enable BFD on this . If not specified, BFD will not be enabled by default. The shortest interval, in milliseconds, at which this BFD session offers to receive BFD control messages. The remote endpoint may choose to send messages at a slower rate. Defaults to 1000. The shortest interval, in milliseconds, at which this BFD session is willing to transmit BFD control messages. Messages will actually be transmitted at a slower rate if the remote endpoint is not willing to receive as quickly as specified. Defaults to 100. An alternate receive interval, in milliseconds, that must be greater than or equal to . The implementation should switch from to when there is no obvious incoming data traffic at the tunnel, to reduce the CPU and bandwidth cost of monitoring an idle tunnel. This feature may be disabled by setting a value of 0. This feature is reset whenever or changes. When true, traffic received on the is used to indicate the capability of packet I/O. BFD control packets are still transmitted and received. At least one BFD control packet must be received every 100 * amount of time. Otherwise, even if traffic is received, the will be false. Set to true to notify the remote endpoint that traffic should not be forwarded to this system for some reason other than a connectivity failure on the interface being monitored. The typical underlying reason is ``concatenated path down,'' that is, that connectivity beyond the local system is down. Defaults to false. Set to true to make BFD accept only control messages with a tunnel key of zero. By default, BFD accepts control messages with any tunnel key.

    The VTEP sets key-value pairs in the column to report the status of BFD on this tunnel. When BFD is not enabled, with , the HSC clears all key-value pairs from .

    Set to true if the BFD session has been successfully enabled. Set to false if the VTEP cannot support BFD or has insufficient resources to enable BFD on this tunnel. The NVC will disable the BFD monitoring on the other side of the tunnel once this value is set to false. Reports the state of the BFD session. The BFD session is fully healthy and negotiated if UP. Reports whether the BFD session believes this may be used to forward traffic. Typically this means the local session is signaling UP, and the remote system isn't signaling a problem such as concatenated path down. A diagnostic code specifying the local system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Reports the state of the remote endpoint's BFD session. A diagnostic code specifying the remote system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. A short message providing further information about the BFD status (possibly including reasons why BFD could not be enabled).
    A port within a . Identifies how VLANs on the physical port are bound to logical switches. If, for example, the map contains a (VLAN, logical switch) pair, a packet that arrives on the port in the VLAN is considered to belong to the paired logical switch. A value of zero in the VLAN field means that untagged traffic on the physical port is mapped to the logical switch.

    Attach Access Control Lists (ACLs) to the physical port. The column consists of a map of VLAN tags to s. If the value of the VLAN tag in the map is 0, this means that the ACL is associated with the entire physical port. Non-zero values mean that the ACL is to be applied only on packets carrying that VLAN tag value. Switches will not necessarily support matching on the VLAN tag for all ACLs, and unsupported ACL bindings will cause errors to be reported. The binding of an ACL to a specific VLAN and the binding of an ACL to the entire physical port should not be combined on a single physical port. That is, a mix of zero and non-zero keys in the map is not recommended.

    Statistics for VLANs bound to logical switches on the physical port. An implementation that fully supports such statistics would populate this column with a mapping for every VLAN that is bound in . An implementation that does not support such statistics or only partially supports them would not populate this column or partially populate it, respectively. A value of zero in the VLAN field refers to untagged traffic on the physical port. Symbolic name for the port. The name ought to be unique within a given , but the database is not capable of enforcing this. An extended description for the port.

    An entry in this column indicates to the NVC that the physical port has encountered a fault. The switch must clear this column when the error has been cleared.

    Indicates that a VLAN-to-logical-switch mapping requested by the controller could not be instantiated by the switch because of a conflict with local configuration.

    Indicates that an error has occurred in associating an ACL with a port.

    Indicates that an error has occurred on the port but that no more specific information is available.

    Reports statistics for the with which a VLAN on a is associated. These statistics count only packets to which the binding applies. Number of packets sent by the . Number of bytes in packets sent by the . Number of packets received by the . Number of bytes in packets received by the .
    A logical Ethernet switch, whose implementation may span physical and virtual media, possibly crossing L3 domains via tunnels; a logical layer-2 domain; an Ethernet broadcast domain.

    Tunnel protocols tend to have a field that allows the tunnel to be partitioned into sub-tunnels: VXLAN has a VNI, GRE and STT have a key, CAPWAP has a WSI, and so on. We call these generically ``tunnel keys.'' Given that one needs to use a tunnel key at all, there are at least two reasonable ways to assign their values:

    • Per + pair. That is, each logical switch may be assigned a different tunnel key on every . This model is especially flexible.

      In this model, carries the tunnel key. Therefore, one record will exist for each logical switch carried at a given IP destination.

    • Per . That is, every tunnel associated with a particular logical switch carries the same tunnel key, regardless of the to which the tunnel is addressed. This model may ease switch implementation because it imposes fewer requirements on the hardware datapath.

      In this model, carries the tunnel key. Therefore, one record will exist for each IP destination.

    This column is used only in the tunnel key per model (see above), because only in that model is there a tunnel key associated with a logical switch.

    For vxlan_over_ipv4 encapsulation, this column is the VXLAN VNI that identifies a logical switch. It must be in the range 0 to 16,777,215.

    Symbolic name for the logical switch. An extended description for the logical switch, such as its switch login banner.

    Mapping of unicast MAC addresses to tunnels (physical locators). This table is written by the HSC, so it contains the MAC addresses that have been learned on physical ports by a VTEP.

    A MAC address that has been learned by the VTEP. The Logical switch to which this mapping applies. The physical locator to be used to reach this MAC address. In this table, the physical locator will be one of the tunnel IP addresses of the appropriate VTEP. The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of unicast MAC addresses to tunnels (physical locators). This table is written by the NVC, so it contains the MAC addresses that the NVC has learned. These include VM MAC addresses, in which case the physical locators will be hypervisor IP addresses. The NVC will also report MACs that it has learned from other HSCs in the network, in which case the physical locators will be tunnel IP addresses of the corresponding VTEPs.

    A MAC address that has been learned by the NVC. The Logical switch to which this mapping applies. The physical locator to be used to reach this MAC address. In this table, the physical locator will be either a hypervisor IP address or a tunnel IP addresses of another VTEP. The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of multicast MAC addresses to tunnels (physical locators). This table is written by the HSC, so it contains the MAC addresses that have been learned on physical ports by a VTEP. These may be learned by IGMP snooping, for example. This table also specifies how to handle unknown unicast and broadcast packets.

    A MAC address that has been learned by the VTEP.

    The keyword unknown-dst is used as a special ``Ethernet address'' that indicates the locations to which packets in a logical switch whose destination addresses do not otherwise appear in (for unicast addresses) or (for multicast addresses) should be sent.

    The Logical switch to which this mapping applies. The physical locator set to be used to reach this MAC address. In this table, the physical locator set will be contain one or more tunnel IP addresses of the appropriate VTEP(s). The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    Mapping of multicast MAC addresses to tunnels (physical locators). This table is written by the NVC, so it contains the MAC addresses that the NVC has learned. This table also specifies how to handle unknown unicast and broadcast packets.

    Multicast packet replication may be handled by a service node, in which case the physical locators will be IP addresses of service nodes. If the VTEP supports replication onto multiple tunnels, then this may be used to replicate directly onto VTEP-hypervisor tunnels.

    A MAC address that has been learned by the NVC.

    The keyword unknown-dst is used as a special ``Ethernet address'' that indicates the locations to which packets in a logical switch whose destination addresses do not otherwise appear in (for unicast addresses) or (for multicast addresses) should be sent.

    The Logical switch to which this mapping applies. The physical locator set to be used to reach this MAC address. In this table, the physical locator set will be either a service node IP address or a set of tunnel IP addresses of hypervisors (and potentially other VTEPs). The IP address to which this MAC corresponds. Optional field for the purpose of ARP supression.

    A logical router, or VRF. A logical router may be connected to one or more logical switches. Subnet addresses and interface addresses may be configured on the interfaces.

    Maps from an IPv4 or IPv6 address prefix in CIDR notation to a logical switch. Multiple prefixes may map to the same switch. By writing a 32-bit (or 128-bit for v6) address with a /N prefix length, both the router's interface address and the subnet prefix can be configured. For example, 192.68.1.1/24 creates a /24 subnet for the logical switch attached to the interface and assigns the address 192.68.1.1 to the router interface. One or more static routes, mapping IP prefixes to next hop IP addresses. Maps ACLs to logical router interfaces. The router interfaces are indicated using IP address notation, and must be the same interfaces created in the column. For example, an ACL could be associated with the logical router interface with an address of 192.68.1.1 as defined in the example above. Symbolic name for the logical router. An extended description for the logical router.

    An entry in this column indicates to the NVC that the HSC has encountered a fault in configuring state related to the logical router.

    Indicates that an error has occurred in associating an ACL with a logical router port.

    Indicates that an error has occurred in configuring the logical router but that no more specific information is available.

    MAC address to be used when a VTEP issues ARP requests on behalf of a logical router.

    A distributed logical router is implemented by a set of VTEPs (both hardware VTEPs and vswitches). In order for a given VTEP to populate the local ARP cache for a logical router, it issues ARP requests with a source MAC address that is unique to the VTEP. A single per-VTEP MAC can be re-used across all logical networks. This table contains the MACs that are used by the VTEPs of a given HSC. The table provides the mapping from MAC to physical locator for each VTEP so that replies to the ARP requests can be sent back to the correct VTEP using the appropriate physical locator.

    The source MAC to be used by a given VTEP. The to use for replies to ARP requests from this MAC address.

    MAC address to be used when a remote VTEP issues ARP requests on behalf of a logical router.

    This table is the remote counterpart of . The NVC writes this table to notify the HSC of the MACs that will be used by remote VTEPs when they issue ARP requests on behalf of a distributed logical router.

    The source MAC to be used by a given VTEP. The to use for replies to ARP requests from this MAC address.

    A set of one or more s.

    This table exists only because OVSDB does not have a way to express the type ``map from string to one or more records.''

    Identifies an endpoint to which logical switch traffic may be encapsulated and forwarded.

    For the vxlan_over_ipv4 encapsulation, the only encapsulation defined so far, all endpoints associated with a given must use a common tunnel key, which is carried in the column of .

    For some encapsulations yet to be defined, we expect to identify both an endpoint and a tunnel key. When the first such encapsulation is defined, we expect to add a ``tunnel_key'' column to to allow the tunnel key to be defined.

    See the ``Per Logical-Switch Tunnel Key'' section in the table for further discussion of the model.

    The type of tunneling encapsulation.

    For vxlan_over_ipv4 encapsulation, the IPv4 address of the VXLAN tunnel endpoint.

    We expect that this column could be used for IPv4 or IPv6 addresses in encapsulations to be introduced later.

    Describes the individual entries that comprise an Access Control List.

    Each entry in the table is a single rule to match on certain header fields. While there are a large number of fields that can be matched on, most hardware cannot match on arbitrary combinations of fields. It is common to match on either L2 fields (described below in the L2 group of columns) or L3/L4 fields (the L3/L4 group of columns) but not both. The hardware switch controller may log an error if an ACL entry requires it to match on an incompatible mixture of fields.

    The sequence number for the ACL entry for the purpose of ordering entries in an ACL. Lower numbered entries are matched before higher numbered entries.

    Source MAC address, in the form xx:xx:xx:xx:xx:xx

    Destination MAC address, in the form xx:xx:xx:xx:xx:xx

    Ethertype in hexadecimal, in the form 0xAAAA

    Source IP address, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Mask that determines which bits of source_ip to match on, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Destination IP address, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Mask that determines which bits of dest_ip to match on, in the form xx.xx.xx.xx for IPv4 or appropriate colon-separated hexadecimal notation for IPv6.

    Protocol number in the IPv4 header, or value of the "next header" field in the IPv6 header.

    Lower end of the range of source port values. The value specified is included in the range.

    Upper end of the range of source port values. The value specified is included in the range.

    Lower end of the range of destination port values. The value specified is included in the range.

    Upper end of the range of destination port values. The value specified is included in the range.

    Integer representing the value of TCP flags to match. For example, the SYN flag is the second least significant bit in the TCP flags. Hence a value of 2 would indicate that the "SYN" flag should be set (assuming an appropriate mask).

    Integer representing the mask to apply when matching TCP flags. For example, a value of 2 would imply that the "SYN" flag should be matched and all other flags ignored.

    ICMP type to be matched.

    ICMP code to be matched.

    Direction of traffic to match on the specified port, either "ingress" (toward the logical switch or router) or "egress" (leaving the logical switch or router).

    Action to take for this rule, either "permit" or "deny".

    An entry in this column indicates to the NVC that the ACL could not be configured as requested. The switch must clear this column when the error has been cleared.

    Indicates that an ACL entry requested by the controller could not be instantiated by the switch, e.g. because it requires an unsupported combination of fields to be matched.

    Indicates that an error has occurred in configuring the ACL entry but no more specific information is available.

    Access Control List table. Each ACL is constructed as a set of entries from the table. Packets that are not matched by any entry in the ACL are allowed by default.

    A set of references to entries in the table.

    A human readable name for the ACL, which may (for example) be displayed on the switch CLI.

    An entry in this column indicates to the NVC that the ACL could not be configured as requested. The switch must clear this column when the error has been cleared.

    Indicates that an ACL requested by the controller could not be instantiated by the switch, e.g., because it requires an unsupported combination of fields to be matched.

    Indicates that an ACL requested by the controller could not be instantiated by the switch due to a shortage of resources (e.g. TCAM space).

    Indicates that an error has occurred in configuring the ACL but no more specific information is available.

    openvswitch-2.5.9/vtep/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071016761 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801424.625852276 openvswitch-2.5.9/vtep/automake.mk0000644000175000017500000000421213534540071020446 0ustar00jpettitjpettit00000000000000# vtep IDL OVSIDL_BUILT += \ vtep/vtep-idl.c \ vtep/vtep-idl.h \ vtep/vtep-idl.ovsidl EXTRA_DIST += vtep/vtep-idl.ann VTEP_IDL_FILES = \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep-idl.ann vtep/vtep-idl.ovsidl: $(VTEP_IDL_FILES) $(AM_V_GEN)$(OVSDB_IDLC) annotate $(VTEP_IDL_FILES) > $@.tmp && \ mv $@.tmp $@ # libvtep lib_LTLIBRARIES += vtep/libvtep.la vtep_libvtep_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/vtep/libvtep.sym \ $(AM_LDFLAGS) nodist_vtep_libvtep_la_SOURCES = \ vtep/vtep-idl.c \ vtep/vtep-idl.h bin_PROGRAMS += \ vtep/vtep-ctl MAN_ROOTS += \ vtep/vtep-ctl.8.in DISTCLEANFILES += \ vtep/vtep-ctl.8 man_MANS += \ vtep/vtep-ctl.8 vtep_vtep_ctl_SOURCES = vtep/vtep-ctl.c vtep_vtep_ctl_LDADD = vtep/libvtep.la lib/libopenvswitch.la # ovs-vtep scripts_SCRIPTS += \ vtep/ovs-vtep docs += vtep/README.ovs-vtep.md EXTRA_DIST += vtep/ovs-vtep # VTEP schema and IDL EXTRA_DIST += vtep/vtep.ovsschema pkgdata_DATA += vtep/vtep.ovsschema # VTEP E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_PYTHON if HAVE_DOT vtep/vtep.gv: ovsdb/ovsdb-dot.in vtep/vtep.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vtep/vtep.ovsschema > $@ vtep/vtep.pic: vtep/vtep.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < vtep/vtep.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ VTEP_PIC = vtep/vtep.pic VTEP_DOT_DIAGRAM_ARG = --er-diagram=$(VTEP_PIC) DISTCLEANFILES += vtep/vtep.gv vtep/vtep.pic endif endif # VTEP schema documentation EXTRA_DIST += vtep/vtep.xml DISTCLEANFILES += vtep/vtep.5 man_MANS += vtep/vtep.5 vtep/vtep.5: \ ovsdb/ovsdb-doc vtep/vtep.xml $(srcdir)/vtep/vtep.ovsschema $(VTEP_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VTEP_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vtep/vtep.ovsschema \ $(srcdir)/vtep/vtep.xml > $@.tmp && \ mv $@.tmp $@ # Version checking for vtep.ovsschema. ALL_LOCAL += vtep/vtep.ovsschema.stamp vtep/vtep.ovsschema.stamp: vtep/vtep.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += vtep/vtep.ovsschema.stamp openvswitch-2.5.9/vtep/PaxHeaders.82075/ovs-vtep0000644000000000000000000000013213534540071016330 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801424.333850123 openvswitch-2.5.9/vtep/ovs-vtep0000755000175000017500000006227013534540071020030 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # Copyright (C) 2013 Nicira, Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Limitations: # - Doesn't support multicast other than "unknown-dst" import argparse import re import shlex import subprocess import sys import time import types import ovs.dirs import ovs.util import ovs.daemon import ovs.unixctl.server import ovs.vlog VERSION = "0.99" root_prefix = "" __pychecker__ = 'no-reuseattr' # Remove in pychecker >= 0.8.19. vlog = ovs.vlog.Vlog("ovs-vtep") exiting = False ps_name = "" ps_type = "" Tunnel_Ip = "" Lswitches = {} Bindings = {} ls_count = 0 tun_id = 0 bfd_bridge = "vtep_bfd" bfd_ref = {} def call_prog(prog, args_list): cmd = [prog, "-vconsole:off"] + args_list output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate() if len(output) == 0 or output[0] == None: output = "" else: output = output[0].strip() return output def ovs_vsctl(args): return call_prog("ovs-vsctl", shlex.split(args)) def ovs_ofctl(args): return call_prog("ovs-ofctl", shlex.split(args)) def vtep_ctl(args): return call_prog("vtep-ctl", shlex.split(args)) def unixctl_exit(conn, unused_argv, unused_aux): global exiting exiting = True conn.reply(None) class Logical_Switch(object): def __init__(self, ls_name): global ls_count self.name = ls_name ls_count += 1 self.short_name = "vtep_ls" + str(ls_count) vlog.info("creating lswitch %s (%s)" % (self.name, self.short_name)) self.ports = {} self.tunnels = {} self.local_macs = set() self.remote_macs = {} self.unknown_dsts = set() self.tunnel_key = 0 self.setup_ls() def __del__(self): vlog.info("destroying lswitch %s" % self.name) def setup_ls(self): column = vtep_ctl("--columns=tunnel_key find logical_switch " "name=%s" % self.name) tunnel_key = column.partition(":")[2].strip() if (tunnel_key and type(eval(tunnel_key)) == types.IntType): self.tunnel_key = tunnel_key vlog.info("using tunnel key %s in %s" % (self.tunnel_key, self.name)) else: self.tunnel_key = 0 vlog.warn("invalid tunnel key for %s, using 0" % self.name) if ps_type: ovs_vsctl("--may-exist add-br %s -- set Bridge %s datapath_type=%s" % (self.short_name, self.short_name, ps_type)) else: ovs_vsctl("--may-exist add-br %s" % self.short_name) ovs_vsctl("br-set-external-id %s vtep_logical_switch true" % self.short_name) ovs_vsctl("br-set-external-id %s logical_switch_name %s" % (self.short_name, self.name)) vtep_ctl("clear-local-macs %s" % self.name) vtep_ctl("add-mcast-local %s unknown-dst %s" % (self.name, Tunnel_Ip)) ovs_ofctl("del-flows %s" % self.short_name) ovs_ofctl("add-flow %s priority=0,action=drop" % self.short_name) def cleanup_ls(self): for port_no, tun_name, remote_ip in self.tunnels.itervalues(): del_bfd(remote_ip) def update_flood(self): flood_ports = self.ports.values() # Traffic flowing from one 'unknown-dst' should not be flooded to # port belonging to another 'unknown-dst'. for tunnel in self.unknown_dsts: port_no = self.tunnels[tunnel][0] ovs_ofctl("add-flow %s table=1,priority=1,in_port=%s,action=%s" % (self.short_name, port_no, ",".join(flood_ports))) # Traffic coming from a VTEP physical port should only be flooded to # one 'unknown-dst' and to all other physical ports that belong to that # VTEP device and this logical switch. for tunnel in self.unknown_dsts: port_no = self.tunnels[tunnel][0] flood_ports.append(port_no) break ovs_ofctl("add-flow %s table=1,priority=0,action=%s" % (self.short_name, ",".join(flood_ports))) def add_lbinding(self, lbinding): vlog.info("adding %s binding to %s" % (lbinding, self.name)) port_no = ovs_vsctl("get Interface %s ofport" % lbinding) self.ports[lbinding] = port_no ovs_ofctl("add-flow %s in_port=%s,action=learn(table=1," "priority=1000,idle_timeout=15,cookie=0x5000," "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]," "output:NXM_OF_IN_PORT[]),resubmit(,1)" % (self.short_name, port_no)) self.update_flood() def del_lbinding(self, lbinding): vlog.info("removing %s binding from %s" % (lbinding, self.name)) port_no = self.ports[lbinding] ovs_ofctl("del-flows %s in_port=%s" % (self.short_name, port_no)); del self.ports[lbinding] self.update_flood() def add_tunnel(self, tunnel): global tun_id vlog.info("adding tunnel %s" % tunnel) encap, ip = tunnel.split("/") if encap != "vxlan_over_ipv4": vlog.warn("unsupported tunnel format %s" % encap) return tun_id += 1 tun_name = "vx" + str(tun_id) ovs_vsctl("add-port %s %s -- set Interface %s type=vxlan " "options:key=%s options:remote_ip=%s" % (self.short_name, tun_name, tun_name, self.tunnel_key, ip)) for i in range(10): port_no = ovs_vsctl("get Interface %s ofport" % tun_name) if port_no != "-1": break elif i == 9: vlog.warn("couldn't create tunnel %s" % tunnel) ovs_vsctl("del-port %s %s" % (self.short_name, tun_name)) return # Give the system a moment to allocate the port number time.sleep(0.5) self.tunnels[tunnel] = (port_no, tun_name, ip) add_bfd(ip) ovs_ofctl("add-flow %s table=0,priority=1000,in_port=%s," "actions=resubmit(,1)" % (self.short_name, port_no)) def del_tunnel(self, tunnel): vlog.info("removing tunnel %s" % tunnel) port_no, tun_name, remote_ip = self.tunnels[tunnel] ovs_ofctl("del-flows %s table=0,in_port=%s" % (self.short_name, port_no)) ovs_vsctl("del-port %s %s" % (self.short_name, tun_name)) del_bfd(remote_ip) del self.tunnels[tunnel] def update_local_macs(self): flows = ovs_ofctl("dump-flows %s cookie=0x5000/-1,table=1" % self.short_name).splitlines() macs = set() for f in flows: mac = re.split(r'.*dl_dst=(.*) .*', f) if len(mac) == 3: macs.add(mac[1]) for mac in macs.difference(self.local_macs): vlog.info("adding local ucast %s to %s" % (mac, self.name)) vtep_ctl("add-ucast-local %s %s %s" % (self.name, mac, Tunnel_Ip)) for mac in self.local_macs.difference(macs): vlog.info("removing local ucast %s from %s" % (mac, self.name)) vtep_ctl("del-ucast-local %s %s" % (self.name, mac)) self.local_macs = macs def add_remote_mac(self, mac, tunnel): port_no = self.tunnels.get(tunnel, (0,""))[0] if not port_no: return ovs_ofctl("add-flow %s table=1,priority=1000,dl_dst=%s,action=%s" % (self.short_name, mac, port_no)) def del_remote_mac(self, mac): ovs_ofctl("del-flows %s table=1,dl_dst=%s" % (self.short_name, mac)) def update_remote_macs(self): remote_macs = {} unknown_dsts = set() tunnels = set() parse_ucast = True mac_list = vtep_ctl("list-remote-macs %s" % self.name).splitlines() for line in mac_list: if (line.find("mcast-mac-remote") != -1): parse_ucast = False continue entry = re.split(r' (.*) -> (.*)', line) if len(entry) != 4: continue if parse_ucast: remote_macs[entry[1]] = entry[2] else: if entry[1] != "unknown-dst": continue unknown_dsts.add(entry[2]) tunnels.add(entry[2]) old_tunnels = set(self.tunnels.keys()) for tunnel in tunnels.difference(old_tunnels): self.add_tunnel(tunnel) for tunnel in old_tunnels.difference(tunnels): self.del_tunnel(tunnel) for mac in remote_macs.keys(): if (self.remote_macs.get(mac) != remote_macs[mac]): self.add_remote_mac(mac, remote_macs[mac]) for mac in self.remote_macs.keys(): if not remote_macs.has_key(mac): self.del_remote_mac(mac) self.remote_macs = remote_macs if (self.unknown_dsts != unknown_dsts): self.unknown_dsts = unknown_dsts self.update_flood() def update_stats(self): # Map Open_vSwitch's "interface:statistics" to columns of # vtep's logical_binding_stats. Since we are using the 'interface' from # the logical switch to collect stats, packets transmitted from it # is received in the physical switch and vice versa. stats_map = {'tx_packets':'packets_to_local', 'tx_bytes':'bytes_to_local', 'rx_packets':'packets_from_local', 'rx_bytes':'bytes_from_local'} # Go through all the logical switch's interfaces that end with "-l" # and copy the statistics to logical_binding_stats. for interface in self.ports.iterkeys(): if not interface.endswith("-l"): continue # Physical ports can have a '-' as part of its name. vlan, remainder = interface.split("-", 1) pp_name, logical = remainder.rsplit("-", 1) uuid = vtep_ctl("get physical_port %s vlan_stats:%s" % (pp_name, vlan)) if not uuid: continue for (mapfrom, mapto) in stats_map.iteritems(): value = ovs_vsctl("get interface %s statistics:%s" % (interface, mapfrom)).strip('"') vtep_ctl("set logical_binding_stats %s %s=%s" % (uuid, mapto, value)) def run(self): self.update_local_macs() self.update_remote_macs() self.update_stats() def get_vtep_tunnel(remote_ip): # Get the physical_locator record for the local tunnel end point. column = vtep_ctl("--columns=_uuid find physical_locator " "dst_ip=%s" % Tunnel_Ip) local = column.partition(":")[2].strip() if not local: return (None, None, None) # Get the physical_locator record for the remote tunnel end point. column = vtep_ctl("--columns=_uuid find physical_locator " "dst_ip=%s" % remote_ip) remote = column.partition(":")[2].strip() if not remote: return (None, None, None) column = vtep_ctl("--columns=_uuid find tunnel " "local=%s remote=%s" % (local, remote)) tunnel = column.partition(":")[2].strip() return (local, remote, tunnel) def create_vtep_tunnel(remote_ip): local, remote, tunnel = get_vtep_tunnel(remote_ip) if not local or not remote: return None if not tunnel: vlog.info("creating tunnel record in vtep for remote_ip:%s" % remote_ip) tunnel = vtep_ctl("add physical_switch %s tunnels @tun -- " "--id=@tun create Tunnel local=%s remote=%s" %(ps_name, local, remote)) return tunnel def destroy_vtep_tunnel(remote_ip): local, remote, tunnel = get_vtep_tunnel(remote_ip) if tunnel: vlog.info("destroying tunnel record in vtep for remote_ip:%s" % remote_ip) vtep_ctl("remove physical_switch %s tunnels %s " "-- --if-exists destroy tunnel %s" % (ps_name, tunnel, tunnel)) def add_bfd(remote_ip): # The VTEP emulator creates one OVS bridge for every logical switch. # Multiple logical switches can have multiple OVS tunnels to the # same machine (with different tunnel ids). But VTEP schema expects # a single BFD session between two physical locators. Therefore # create a separate bridge ('bfd_bridge') and create a single OVS tunnel # between two phsyical locators (using reference counter). if remote_ip in bfd_ref: bfd_ref[remote_ip] += 1 return vlog.info("adding bfd tunnel for remote_ip:%s" % remote_ip) port_name = "bfd" + remote_ip # Don't enable BFD yet. Enabling or disabling BFD is based on # the controller setting a value in VTEP DB's tunnel record. ovs_vsctl("--may-exist add-port %s %s " " -- set Interface %s type=vxlan options:remote_ip=%s" % (bfd_bridge, port_name, port_name, remote_ip)) bfd_ref[remote_ip] = 1 # Ideally, we should create a 'tunnel' record in the VTEP DB here. # To create a 'tunnel' record, we need 2 entries in 'physical_locator' # table (one for local and one for remote). But, 'physical_locator' # can be created/destroyed asynchronously when the remote controller # adds/removes entries in Ucast_Macs_Remote table. To prevent race # conditions, pass the responsibility of creating a 'tunnel' record # to run_bfd() which runs more often. def del_bfd(remote_ip): if remote_ip in bfd_ref: if bfd_ref[remote_ip] == 1: port_name = "bfd" + remote_ip vlog.info("deleting bfd tunnel for remote_ip:%s" % remote_ip) ovs_vsctl("--if-exists del-port %s" % port_name) destroy_vtep_tunnel(remote_ip) del bfd_ref[remote_ip] else: bfd_ref[remote_ip] -= 1 def run_bfd(): bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split() for port in bfd_ports: remote_ip = ovs_vsctl("get interface %s options:remote_ip" % port) tunnel = create_vtep_tunnel(remote_ip) if not tunnel: continue bfd_params_default = {'bfd_params:enable' : 'false', 'bfd_params:min_rx' : 1000, 'bfd_params:min_tx' : 100, 'bfd_params:decay_min_rx' : 0, 'bfd_params:cpath_down' : 'false', 'bfd_params:check_tnl_key' : 'false'} bfd_params_values = {} for key, default in bfd_params_default.iteritems(): column = vtep_ctl("--if-exists get tunnel %s %s" % (tunnel, key)) if not column: bfd_params_values[key] = default else: bfd_params_values[key] = column for key, value in bfd_params_values.iteritems(): new_key = key.replace('_params','') ovs_vsctl("set interface %s %s=%s" % (port, new_key, value)) bfd_status = ['bfd_status:state', 'bfd_status:forwarding', 'bfd_status:diagnostic', 'bfd_status:remote_state', 'bfd_status:remote_diagnostic'] for key in bfd_status: value = ovs_vsctl("--if-exists get interface %s %s" % (port, key)) if value: vtep_ctl("set tunnel %s %s=%s" %(tunnel, key, value)) else: new_key = key.replace('bfd_status:', '') vtep_ctl("remove tunnel %s bfd_status %s" % (tunnel, new_key)) vtep_ctl("set tunnel %s bfd_status:enabled=%s" % (tunnel, bfd_params_values['bfd_params:enable'])) # Add the defaults as described in VTEP schema to make it explicit. bfd_lconf_default = {'bfd_config_local:bfd_dst_ip' : '169.254.1.0', 'bfd_config_local:bfd_dst_mac' : '00:23:20:00:00:01'} for key, value in bfd_lconf_default.iteritems(): vtep_ctl("set tunnel %s %s=%s" %(tunnel, key, value)) # bfd_config_remote options from VTEP DB should be populated to # corresponding OVS DB values. bfd_dst_ip = vtep_ctl("--if-exists get tunnel %s " "bfd_config_remote:bfd_dst_ip" % (tunnel)) if not bfd_dst_ip: bfd_dst_ip = "169.254.1.1" bfd_dst_mac = vtep_ctl("--if-exists get tunnel %s " "bfd_config_remote:bfd_dst_mac" % (tunnel)) if not bfd_dst_mac: bfd_dst_mac = "00:23:20:00:00:01" ovs_vsctl("set interface %s bfd:bfd_dst_ip=%s " "bfd:bfd_remote_dst_mac=%s bfd:bfd_local_dst_mac=%s" % (port, bfd_dst_ip, bfd_lconf_default['bfd_config_local:bfd_dst_mac'], bfd_dst_mac)) def add_binding(binding, ls): vlog.info("adding binding %s" % binding) vlan, pp_name = binding.split("-", 1) pbinding = binding+"-p" lbinding = binding+"-l" # Create a patch port that connects the VLAN+port to the lswitch. # Do them as two separate calls so if one side already exists, the # other side is created. ovs_vsctl("add-port %s %s " " -- set Interface %s type=patch options:peer=%s" % (ps_name, pbinding, pbinding, lbinding)) ovs_vsctl("add-port %s %s " " -- set Interface %s type=patch options:peer=%s" % (ls.short_name, lbinding, lbinding, pbinding)) port_no = ovs_vsctl("get Interface %s ofport" % pp_name) patch_no = ovs_vsctl("get Interface %s ofport" % pbinding) vlan_ = vlan.lstrip('0') if vlan_: ovs_ofctl("add-flow %s in_port=%s,dl_vlan=%s,action=strip_vlan,%s" % (ps_name, port_no, vlan_, patch_no)) ovs_ofctl("add-flow %s in_port=%s,action=mod_vlan_vid:%s,%s" % (ps_name, patch_no, vlan_, port_no)) else: ovs_ofctl("add-flow %s in_port=%s,action=%s" % (ps_name, port_no, patch_no)) ovs_ofctl("add-flow %s in_port=%s,action=%s" % (ps_name, patch_no, port_no)) # Create a logical_bindings_stats record. if not vlan_: vlan_ = "0" vtep_ctl("set physical_port %s vlan_stats:%s=@stats --\ --id=@stats create logical_binding_stats packets_from_local=0"\ % (pp_name, vlan_)) ls.add_lbinding(lbinding) Bindings[binding] = ls.name def del_binding(binding, ls): vlog.info("removing binding %s" % binding) vlan, pp_name = binding.split("-", 1) pbinding = binding+"-p" lbinding = binding+"-l" port_no = ovs_vsctl("get Interface %s ofport" % pp_name) patch_no = ovs_vsctl("get Interface %s ofport" % pbinding) vlan_ = vlan.lstrip('0') if vlan_: ovs_ofctl("del-flows %s in_port=%s,dl_vlan=%s" % (ps_name, port_no, vlan_)) ovs_ofctl("del-flows %s in_port=%s" % (ps_name, patch_no)) else: ovs_ofctl("--strict del-flows %s in_port=%s" % (ps_name, port_no)) ovs_ofctl("--strict del-flows %s in_port=%s" % (ps_name, patch_no)) ls.del_lbinding(lbinding) # Destroy the patch port that connects the VLAN+port to the lswitch ovs_vsctl("del-port %s %s -- del-port %s %s" % (ps_name, pbinding, ls.short_name, lbinding)) # Remove the record that links vlan with stats in logical_binding_stats. vtep_ctl("remove physical_port %s vlan_stats %s" % (pp_name, vlan)) del Bindings[binding] def handle_physical(): # Gather physical ports except the patch ports we created ovs_ports = ovs_vsctl("list-ports %s" % ps_name).split() ovs_port_set = set([port for port in ovs_ports if port[-2:] != "-p"]) vtep_pp_set = set(vtep_ctl("list-ports %s" % ps_name).split()) for pp_name in ovs_port_set.difference(vtep_pp_set): vlog.info("adding %s to %s" % (pp_name, ps_name)) vtep_ctl("add-port %s %s" % (ps_name, pp_name)) for pp_name in vtep_pp_set.difference(ovs_port_set): vlog.info("deleting %s from %s" % (pp_name, ps_name)) vtep_ctl("del-port %s %s" % (ps_name, pp_name)) new_bindings = set() for pp_name in vtep_pp_set: binding_set = set(vtep_ctl("list-bindings %s %s" % (ps_name, pp_name)).splitlines()) for b in binding_set: vlan, ls_name = b.split() if ls_name not in Lswitches: Lswitches[ls_name] = Logical_Switch(ls_name) binding = "%s-%s" % (vlan, pp_name) ls = Lswitches[ls_name] new_bindings.add(binding) if Bindings.has_key(binding): if Bindings[binding] == ls_name: continue else: del_binding(binding, Lswitches[Bindings[binding]]) add_binding(binding, ls) dead_bindings = set(Bindings.keys()).difference(new_bindings) for binding in dead_bindings: ls_name = Bindings[binding] ls = Lswitches[ls_name] del_binding(binding, ls) if not len(ls.ports): ls.cleanup_ls() ovs_vsctl("del-br %s" % Lswitches[ls_name].short_name) vtep_ctl("clear-local-macs %s" % Lswitches[ls_name].name) del Lswitches[ls_name] def setup(): br_list = ovs_vsctl("list-br").split() if (ps_name not in br_list): ovs.util.ovs_fatal(0, "couldn't find OVS bridge %s" % ps_name, vlog) global ps_type ps_type = ovs_vsctl("get Bridge %s datapath_type" % ps_name).strip('"') call_prog("vtep-ctl", ["set", "physical_switch", ps_name, 'description="OVS VTEP Emulator"']) tunnel_ips = vtep_ctl("get physical_switch %s tunnel_ips" % ps_name).strip('[]"').split(", ") if len(tunnel_ips) != 1 or not tunnel_ips[0]: ovs.util.ovs_fatal(0, "exactly one 'tunnel_ips' should be set", vlog) global Tunnel_Ip Tunnel_Ip = tunnel_ips[0] ovs_ofctl("del-flows %s" % ps_name) # Remove any logical bridges from the previous run for br in br_list: if ovs_vsctl("br-get-external-id %s vtep_logical_switch" % br) == "true": # Remove the remote side of any logical switch ovs_ports = ovs_vsctl("list-ports %s" % br).split() for port in ovs_ports: port_type = ovs_vsctl("get Interface %s type" % port).strip('"') if port_type != "patch": continue peer = ovs_vsctl("get Interface %s options:peer" % port).strip('"') if (peer): ovs_vsctl("del-port %s" % peer) ovs_vsctl("del-br %s" % br) if br == bfd_bridge: bfd_ports = ovs_vsctl("list-ports %s" % bfd_bridge).split() for port in bfd_ports: remote_ip = ovs_vsctl("get interface %s options:remote_ip" % port) tunnel = destroy_vtep_tunnel(remote_ip) ovs_vsctl("del-br %s" % br) if ps_type: ovs_vsctl("add-br %s -- set Bridge %s datapath_type=%s" % (bfd_bridge, bfd_bridge, ps_type)) else: ovs_vsctl("add-br %s" % bfd_bridge) def main(): parser = argparse.ArgumentParser() parser.add_argument("ps_name", metavar="PS-NAME", help="Name of physical switch.") parser.add_argument("--root-prefix", metavar="DIR", help="Use DIR as alternate root directory" " (for testing).") parser.add_argument("--version", action="version", version="%s %s" % (ovs.util.PROGRAM_NAME, VERSION)) ovs.vlog.add_args(parser) ovs.daemon.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) ovs.daemon.handle_args(args) global root_prefix if args.root_prefix: root_prefix = args.root_prefix global ps_name ps_name = args.ps_name ovs.daemon.daemonize() ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None) error, unixctl = ovs.unixctl.server.UnixctlServer.create(None, version=VERSION) if error: ovs.util.ovs_fatal(error, "could not create unixctl server", vlog) setup() while True: unixctl.run() if exiting: break handle_physical() for ls_name, ls in Lswitches.items(): ls.run() run_bfd() poller = ovs.poller.Poller() unixctl.wait(poller) poller.timer_wait(1000) poller.block() unixctl.close() if __name__ == '__main__': try: main() except SystemExit: # Let system.exit() calls complete normally raise except: vlog.exception("traceback") sys.exit(ovs.daemon.RESTART_EXIT_CODE) openvswitch-2.5.9/vtep/PaxHeaders.82075/vtep.ovsschema0000644000000000000000000000013213534540071017512 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.333850123 openvswitch-2.5.9/vtep/vtep.ovsschema0000644000175000017500000002444213534540071021206 0ustar00jpettitjpettit00000000000000{ "name": "hardware_vtep", "cksum": "2177247725 10499", "tables": { "Global": { "columns": { "managers": { "type": {"key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}, "switches": { "type": {"key": {"type": "uuid", "refTable": "Physical_Switch"}, "min": 0, "max": "unlimited"}} }, "maxRows": 1, "isRoot": true}, "Physical_Switch": { "columns": { "ports": { "type": {"key": {"type": "uuid", "refTable": "Physical_Port"}, "min": 0, "max": "unlimited"}}, "name": {"type": "string"}, "description": {"type": "string"}, "management_ips": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "tunnel_ips": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "tunnels": { "type": {"key": {"type": "uuid", "refTable": "Tunnel"}, "min": 0, "max": "unlimited"}}, "switch_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["name"]]}, "Physical_Port": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "vlan_bindings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "Logical_Switch"}, "min": 0, "max": "unlimited"}}, "acl_bindings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "ACL"}, "min": 0, "max": "unlimited"}}, "vlan_stats": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "value": {"type": "uuid", "refTable": "Logical_Binding_Stats"}, "min": 0, "max": "unlimited"}, "ephemeral": true}, "port_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Tunnel": { "columns": { "local": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "remote": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "bfd_config_local": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_config_remote": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_params": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Logical_Binding_Stats": { "columns": { "bytes_from_local": {"type": "integer", "ephemeral": true}, "packets_from_local": {"type": "integer", "ephemeral": true}, "bytes_to_local": {"type": "integer", "ephemeral": true}, "packets_to_local": {"type": "integer", "ephemeral": true}}}, "Logical_Switch": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "tunnel_key": {"type": {"key": "integer", "min": 0, "max": 1}}}, "isRoot": true, "indexes": [["name"]]}, "Ucast_Macs_Local": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Ucast_Macs_Remote": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Mcast_Macs_Local": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator_set": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator_Set"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Mcast_Macs_Remote": { "columns": { "MAC": {"type": "string"}, "logical_switch": { "type": {"key": {"type": "uuid", "refTable": "Logical_Switch"}}}, "locator_set": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator_Set"}}}, "ipaddr": {"type": "string"}}, "isRoot": true}, "Logical_Router": { "columns": { "name": {"type": "string"}, "description": {"type": "string"}, "switch_binding": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "Logical_Switch"}, "min": 0, "max": "unlimited"}}, "static_routes": { "type": {"key": {"type": "string"}, "value": {"type" : "string"}, "min": 0, "max": "unlimited"}}, "acl_binding": { "type": {"key": {"type": "string"}, "value": {"type": "uuid", "refTable": "ACL"}, "min": 0, "max": "unlimited"}}, "LR_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "isRoot": true, "indexes": [["name"]]}, "Arp_Sources_Local": { "columns": { "src_mac": {"type": "string"}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}}, "isRoot": true}, "Arp_Sources_Remote": { "columns": { "src_mac": {"type": "string"}, "locator": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}}}}, "isRoot": true}, "Physical_Locator_Set": { "columns": { "locators": { "type": {"key": {"type": "uuid", "refTable": "Physical_Locator"}, "min": 1, "max": "unlimited"}, "mutable": false}}}, "Physical_Locator": { "columns": { "encapsulation_type": { "type": { "key": { "enum": ["set", ["vxlan_over_ipv4"]], "type": "string"}}, "mutable": false}, "dst_ip": {"type": "string", "mutable": false}}, "indexes": [["encapsulation_type", "dst_ip"]]}, "ACL_entry": { "columns": { "sequence": {"type": "integer"}, "source_mac": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_mac": { "type": { "key": "string", "min": 0, "max": 1}}, "ethertype": { "type": { "key": "string", "min": 0, "max": 1}}, "source_ip": { "type": { "key": "string", "min": 0, "max": 1}}, "source_mask": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_ip": { "type": { "key": "string", "min": 0, "max": 1}}, "dest_mask": { "type": { "key": "string", "min": 0, "max": 1}}, "protocol": { "type": { "key": "integer", "min": 0, "max": 1}}, "source_port_min": { "type": { "key": "integer", "min": 0, "max": 1}}, "source_port_max": { "type": { "key": "integer", "min": 0, "max": 1}}, "dest_port_min": { "type": { "key": "integer", "min": 0, "max": 1}}, "dest_port_max": { "type": { "key": "integer", "min": 0, "max": 1}}, "tcp_flags": { "type": { "key": "integer", "min": 0, "max": 1}}, "tcp_flags_mask": { "type": { "key": "integer", "min": 0, "max": 1}}, "icmp_code": { "type": { "key": "integer", "min": 0, "max": 1}}, "icmp_type": { "type": { "key": "integer", "min": 0, "max": 1}}, "direction": { "type": { "key": {"type": "string", "enum": ["set", ["ingress", "egress"]]}}}, "action": { "type": { "key": {"type": "string", "enum": ["set", ["permit", "deny"]]}}}, "acle_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "isRoot": true}, "ACL": { "columns": { "acl_entries": { "type": {"key": {"type": "uuid", "refTable": "ACL_entry"}, "min": 1, "max": "unlimited"}}, "acl_name": {"type": "string"}, "acl_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["acl_name"]], "isRoot": true}, "Manager": { "columns": { "target": {"type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["target"]], "isRoot": false}}, "version": "1.4.1"} openvswitch-2.5.9/vtep/PaxHeaders.82075/libvtep.sym.in0000644000000000000000000000013213534540071017426 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801424.649852453 openvswitch-2.5.9/vtep/libvtep.sym.in0000644000175000017500000000005513534540071021114 0ustar00jpettitjpettit00000000000000libvtep_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/vtep/PaxHeaders.82075/vtep-ctl.8.in0000644000000000000000000000013213534540071017056 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801423.781846054 openvswitch-2.5.9/vtep/vtep-ctl.8.in0000644000175000017500000003351513534540071020553 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .TH vtep\-ctl 8 "March 2013" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN vtep\-ctl . .SH NAME vtep\-ctl \- utility for querying and configuring a VTEP database . .SH SYNOPSIS \fBvtep\-ctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]... . .SH DESCRIPTION The \fBvtep\-ctl\fR program configures a VTEP database. See \fBvtep\fR(5) for comprehensive documentation of the database schema. .PP \fBvtep\-ctl\fR connects to an \fBovsdb\-server\fR process that maintains a VTEP configuration database. Using this connection, it queries and possibly applies changes to the database, depending on the supplied commands. .PP \fBvtep\-ctl\fR can perform any number of commands in a single run, implemented as a single atomic transaction against the database. .PP The \fBvtep\-ctl\fR command line begins with global options (see \fBOPTIONS\fR below for details). The global options are followed by one or more commands. Each command should begin with \fB\-\-\fR by itself as a command-line argument, to separate it from the following commands. (The \fB\-\-\fR before the first command is optional.) The command itself starts with command-specific options, if any, followed by the command name and any arguments. See \fBEXAMPLES\fR below for syntax examples. . .SH OPTIONS . The following options affect the behavior \fBvtep\-ctl\fR as a whole. Some individual commands also accept their own options, which are given just before the command name. If the first command on the command line has options, then those options must be separated from the global options by \fB\-\-\fR. . .IP "\fB\-\-db=\fIserver\fR" Sets \fIserver\fR as the database server that \fBvtep\-ctl\fR contacts to query or modify configuration. The default is \fBunix:@RUNDIR@/db.sock\fR. \fIserver\fR must take one of the following forms: .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE . .IP "\fB\-\-no\-syslog\fR" By default, \fBvtep\-ctl\fR logs its arguments and the details of any changes that it makes to the system log. This option disables this logging. .IP This option is equivalent to \fB\-\-verbose=vtep_ctl:syslog:warn\fR. . .IP "\fB\-\-oneline\fR" Modifies the output format so that the output for each command is printed on a single line. New-line characters that would otherwise separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that would otherwise appear in the output are doubled. Prints a blank line for each command that has no output. This option does not affect the formatting of output from the \fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR below. . .IP "\fB\-\-dry\-run\fR" Prevents \fBvtep\-ctl\fR from actually modifying the database. . .IP "\fB\-t \fIsecs\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" By default, or with a \fIsecs\fR of \fB0\fR, \fBvtep\-ctl\fR waits forever for a response from the database. This option limits runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBvtep\-ctl\fR will exit with a \fBSIGALRM\fR signal. (A timeout would normally happen only if the database cannot be contacted, or if the system is overloaded.) . .SS "Table Formatting Options" These options control the format of output from the \fBlist\fR and \fBfind\fR commands. .so lib/table.man . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .so lib/vlog.man .so lib/common.man . .SH COMMANDS The commands implemented by \fBvtep\-ctl\fR are described in the sections below. . .SS "Physical Switch Commands" These commands examine and manipulate physical switches. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-ps \fIpswitch\fR" Creates a new physical switch named \fIpswitch\fR. Initially the switch will have no ports. .IP Without \fB\-\-may\-exist\fR, attempting to create a switch that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIpswitch\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-ps \fIpswitch\fR" Deletes \fIpswitch\fR and all of its ports. .IP Without \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist has no effect. . .IP "\fBlist\-ps\fR" Lists all existing physical switches on standard output, one per line. . .IP "\fBps\-exists \fIpswitch\fR" Tests whether \fIpswitch\fR exists. If so, \fBvtep\-ctl\fR exits successfully with exit code 0. If not, \fBvtep\-ctl\fR exits unsuccessfully with exit code 2. . .SS "Port Commands" . These commands examine and manipulate VTEP physical ports. . .IP "\fBlist\-ports \fIpswitch\fR" Lists all of the ports within \fIpswitch\fR on standard output, one per line. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-port \fIpswitch port\fR" Creates on \fIpswitch\fR a new port named \fIport\fR from the network device of the same name. .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIpswitch\fR. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-port \fR[\fIpswitch\fR] \fIport\fR" Deletes \fIport\fR. If \fIpswitch\fR is omitted, \fIport\fR is removed from whatever switch contains it; if \fIpswitch\fR is specified, it must be the switch that contains \fIport\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a port that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a port that does not exist has no effect. . .SS "Logical Switch Commands" These commands examine and manipulate logical switches. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-ls \fIlswitch\fR" Creates a new logical switch named \fIlswitch\fR. Initially the switch will have no locator bindings. .IP Without \fB\-\-may\-exist\fR, attempting to create a switch that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIlswitch\fR already exists. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-ls \fIlswitch\fR" Deletes \fIlswitch\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a switch that does not exist has no effect. . .IP "\fBlist\-ls\fR" Lists all existing logical switches on standard output, one per line. . .IP "\fBls\-exists \fIlswitch\fR" Tests whether \fIlswitch\fR exists. If so, \fBvtep\-ctl\fR exits successfully with exit code 0. If not, \fBvtep\-ctl\fR exits unsuccessfully with exit code 2. . .IP "\fBbind\-ls \fIpswitch port vlan lswitch\fR" Bind logical switch \fIlswitch\fR to the \fIport\fR/\fIvlan\fR combination on the physical switch \fIpswitch\fR. . .IP "\fBunbind\-ls \fIpswitch port vlan\fR" Remove the logical switch binding from the \fIport\fR/\fIvlan\fR combination on the physical switch \fIpswitch\fR. . .IP "\fBlist\-bindings \fIpswitch port\fR" List the logical switch bindings for \fIport\fR on the physical switch \fIpswitch\fR. . .SS "Local MAC Binding Commands" These commands examine and manipulate local MAC bindings for the logical switch. The local maps are written by the VTEP to refer to MACs it has learned on its physical ports. . .IP "\fBadd\-ucast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Map the unicast Ethernet address \fImac\fR to the physical location \fIip\fR using encapsulation \fIencap\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBdel\-ucast\-local \fIlswitch mac\fR" Remove the local unicast Ethernet address \fImac\fR map from \fIlswitch\fR. The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBadd\-mcast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Add physical location \fIip\fR using encapsulation \fIencap\fR to the local mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBdel\-mcast\-local \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Remove physical location \fIip\fR using encapsulation \fIencap\fR from the local mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The local mappings are used by the VTEP to refer to MACs learned on its physical ports. . .IP "\fBclear\-local\-macs \fIlswitch\fR" Clear the local MAC bindings for \fIlswitch\fR. . .IP "\fBlist\-local\-macs \fIlswitch\fR" List the local MAC bindings for \fIlswitch\fR, one per line. . .SS "Remote MAC Binding Commands" These commands examine and manipulate local and remote MAC bindings for the logical switch. The remote maps are written by the network virtualization controller to refer to MACs that it has learned. . .IP "\fBadd\-ucast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Map the unicast Ethernet address \fImac\fR to the physical location \fIip\fR using encapsulation \fIencap\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBdel\-ucast\-remote \fIlswitch mac\fR" Remove the remote unicast Ethernet address \fImac\fR map from \fIlswitch\fR. The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBadd\-mcast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Add physical location \fIip\fR using encapsulation \fIencap\fR to the remote mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBdel\-mcast\-remote \fIlswitch mac\fR [\fIencap\fR] \fIip\fR" Remove physical location \fIip\fR using encapsulation \fIencap\fR from the remote mac binding table for multicast Ethernet address \fImac\fR on \fIlswitch\fR. If \fIencap\fR is not specified, the default is "vxlan_over_ipv4". The remote mappings are used by the network virtualization platform to refer to MACs that it has learned. . .IP "\fBclear\-remote\-macs \fIlswitch\fR" Clear the remote MAC bindings for \fIlswitch\fR. . .IP "\fBlist\-remote\-macs \fIlswitch\fR" List the remote MAC bindings for \fIlswitch\fR, one per line. . .SS "Manager Connectivity" . These commands manipulate the \fBmanagers\fR column in the \fBGlobal\fR table and rows in the \fBManagers\fR table. When \fBovsdb\-server\fR is configured to use the \fBmanagers\fR column for OVSDB connections (as described in \fBINSTALL.Linux\fR and in the startup scripts provided with Open vSwitch), this allows the administrator to use \fBvtep\-ctl\fR to configure database connections. . .IP "\fBget\-manager\fR" Prints the configured manager(s). . .IP "\fBdel\-manager\fR" Deletes the configured manager(s). . .IP "\fBset\-manager\fR \fItarget\fR\&..." Sets the configured manager target or targets. Each \fItarget\fR may use any of the following forms: . .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE . .SS "Database Commands" . These commands query and modify the contents of \fBovsdb\fR tables. They are a slight abstraction of the \fBovsdb\fR interface and as such they operate at a lower level than other \fBvtep\-ctl\fR commands. .PP .ST "Identifying Tables, Records, and Columns" .PP Each of these commands has a \fItable\fR parameter to identify a table within the database. Many of them also take a \fIrecord\fR parameter that identifies a particular record within a table. The \fIrecord\fR parameter may be the UUID for a record, and many tables offer additional ways to identify records. Some commands also take \fIcolumn\fR parameters that identify a particular field within the records in a table. .PP The following tables are currently defined: .IP "\fBGlobal\fR" Top-level configuration for a hardware VTEP. This table contains exactly one record, identified by specifying \fB.\fR as the record name. .IP "\fBManager\fR" Configuration for an OVSDB connection. Records may be identified by target (e.g. \fBtcp:1.2.3.4\fR). .IP "\fBPhysical_Switch\fR" A physical switch that implements a VTEP. Records may be identified by physical switch name. .IP "\fBPhysical_Port\fR" A port within a physical switch. .IP "\fBLogical_Binding_Stats\fR" Reports statistics for the logical switch with which a VLAN on a physical port is associated. .IP "\fBLogical_Switch\fR" A logical Ethernet switch. Records may be identified by logical switch name. .IP "\fBUcast_Macs_Local\fR" Mapping of locally discovered unicast MAC addresses to tunnels. .IP "\fBUcast_Macs_Remote\fR" Mapping of remotely programmed unicast MAC addresses to tunnels. .IP "\fBMcast_Macs_Local\fR" Mapping of locally discovered multicast MAC addresses to tunnels. .IP "\fBMcast_Macs_Remote\fR" Mapping of remotely programmed multicast MAC addresses to tunnels. .IP "\fBPhysical_Locator_Set\fR" A set of one or more physical locators. .IP "\fBPhysical_Locator\fR" Identifies an endpoint to which logical switch traffic may be encapsulated and forwarded. Records may be identified by physical locator name. .PP Record names must be specified in full and with correct capitalization. Names of tables and columns are not case-sensitive, and \fB\-\-\fR and \fB_\fR are treated interchangeably. Unique abbreviations are acceptable, e.g. \fBman\fR or \fBm\fR is sufficient to identify the \fBManager\fR table. . .so lib/db-ctl-base.man .PP .SH "EXIT STATUS" .IP "0" Successful program execution. .IP "1" Usage, syntax, or configuration file error. .IP "2" The \fIswitch\fR argument to \fBps\-exists\fR specified the name of a physical switch that does not exist. .SH "SEE ALSO" . .BR ovsdb\-server (1), .BR vtep (5). openvswitch-2.5.9/vtep/PaxHeaders.82075/README.ovs-vtep.md0000644000000000000000000000013213534540071017663 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801423.705845493 openvswitch-2.5.9/vtep/README.ovs-vtep.md0000644000175000017500000001330013534540071021346 0ustar00jpettitjpettit00000000000000How to Use the VTEP Emulator ============================ This document explains how to use ovs-vtep, a VTEP emulator that uses Open vSwitch for forwarding. Requirements ------------ The VTEP emulator is a Python script that invokes calls to tools like vtep-ctl and ovs-vsctl and is useful only when OVS daemons like ovsdb-server and ovs-vswitchd are running. So those components should be installed. This can be done by either of the following methods. 1. Follow the instructions in the INSTALL.md file of the Open vSwitch repository (don't start any daemons yet). 2. Follow the instructions in INSTALL.Debian.md file and then install the "openvswitch-vtep" package (if operating on a debian based machine). This will automatically start the daemons. Design ====== At the end of this process, you should have the following setup: +---------------------------------------------------+ | Host Machine | | | | | | +---------+ +---------+ | | | | | | | | | VM1 | | VM2 | | | | | | | | | +----o----+ +----o----+ | | | | | | br0 +------o-----------o--------------------o--+ | | p0 p1 br0 | | | | | | +------+ +------+ | +------------------------------| eth0 |---| eth1 |--+ +------+ +------+ 10.1.1.1 10.2.2.1 MANAGEMENT | | +-----------------o----+ | | DATA/TUNNEL | +-----------------o---+ Notes: 1. We will use Open vSwitch to create our "physical" switch labeled br0 2. Our "physical" switch br0 will have one internal port also named br0 and two "physical" ports, namely p0 and p1. 3. The host machine may have two external interfaces. We will use eth0 for management traffic and eth1 for tunnel traffic (One can use a single interface to achieve both). Please take note of their IP addresses in the diagram. You do not have to use exactly the same IP addresses. Just know that the above will be used in the steps below. 4. You can optionally connect physical machines instead of virtual machines to switch br0. In that case: 4.1. Make sure you have two extra physical interfaces in your host machine, eth2 and eth3. 4.2. In the rest of this doc, replace p0 with eth2 and p1 with eth3. 5. In addition to implementing p0 and p1 as physical interfaces, you can also optionally implement them as standalone TAP devices, or VM interfaces for simulation. 6. Creating and attaching the VMs is outside the scope of this document and is included in the diagram for reference purposes only. Startup ------- These instructions describe how to run with a single ovsdb-server instance that handles both the OVS and VTEP schema. You can skip steps 1-3 if you installed using the debian packages as mentioned in step 2 of the "Requirements" section. 1. Create the initial OVS and VTEP schemas: ``` ovsdb-tool create /etc/openvswitch/ovs.db vswitchd/vswitch.ovsschema ovsdb-tool create /etc/openvswitch/vtep.db vtep/vtep.ovsschema ``` 2. Start ovsdb-server and have it handle both databases: ``` ovsdb-server --pidfile --detach --log-file \ --remote punix:/var/run/openvswitch/db.sock \ --remote=db:hardware_vtep,Global,managers \ /etc/openvswitch/ovs.db /etc/openvswitch/vtep.db ``` 3. Start OVS as normal: ``` ovs-vswitchd --log-file --detach --pidfile \ unix:/var/run/openvswitch/db.sock ``` 4. Create a "physical" switch and its ports in OVS: ``` ovs-vsctl add-br br0 ovs-vsctl add-port br0 p0 ovs-vsctl add-port br0 p1 ``` 5. Configure the physical switch in the VTEP database: ``` vtep-ctl add-ps br0 vtep-ctl set Physical_Switch br0 tunnel_ips=10.2.2.1 ``` 6. Start the VTEP emulator. If you installed the components by reading the INSTALL.md file, run the following from the same directory as this README.md: ``` ./ovs-vtep --log-file=/var/log/openvswitch/ovs-vtep.log \ --pidfile=/var/run/openvswitch/ovs-vtep.pid \ --detach br0 ``` If the installation was done by installing the openvswitch-vtep package, you can find ovs-vtep at /usr/share/openvswitch/scripts. 7. Configure the VTEP database's manager to point at an NVC: ``` vtep-ctl set-manager tcp::6640 ``` Where CONTROLLER IP is your controller's IP address that is accessible via the Host Machine's eth0 interface. Simulating an NVC ----------------- A VTEP implementation expects to be driven by a Network Virtualization Controller (NVC), such as NSX. If one does not exist, it's possible to use vtep-ctl to simulate one: 1. Create a logical switch: ``` vtep-ctl add-ls ls0 ``` 2. Bind the logical switch to a port: ``` vtep-ctl bind-ls br0 p0 0 ls0 vtep-ctl set Logical_Switch ls0 tunnel_key=33 ``` 3. Direct unknown destinations out a tunnel: ``` vtep-ctl add-mcast-remote ls0 unknown-dst 10.2.2.2 ``` 4. Direct unicast destinations out a different tunnel: ``` vtep-ctl add-ucast-remote ls0 00:11:22:33:44:55 10.2.2.3 ``` openvswitch-2.5.9/PaxHeaders.82075/xenserver0000644000000000000000000000013213534540120015603 xustar0030 mtime=1567801424.233849386 30 atime=1567801425.625859648 30 ctime=1567801424.233849386 openvswitch-2.5.9/xenserver/0000755000175000017500000000000013534540120017346 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/xenserver/PaxHeaders.82075/usr_share_openvswitch_scripts_ovs-xapi-sync0000644000000000000000000000013013534540071026601 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.233849386 openvswitch-2.5.9/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync0000755000175000017500000003261613534540071030304 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A daemon to monitor the external_ids columns of the Bridge and # Interface OVSDB tables for changes that require interrogating XAPI. # Its responsibilities include: # # - Set the "bridge-id" key in the Bridge table. # - Set the "iface-id" key in the Interface table. # - Set the fail-mode on internal bridges. import argparse import os import sys import time import XenAPI import ovs.dirs from ovs.db import error from ovs.db import types import ovs.daemon import ovs.db.idl import ovs.unixctl import ovs.unixctl.server vlog = ovs.vlog.Vlog("ovs-xapi-sync") session = None flush_cache = False exiting = False xapi_down = False def unixctl_exit(conn, unused_argv, unused_aux): global exiting exiting = True conn.reply(None) def unixctl_flush_cache(conn, unused_argv, unused_aux): global flush_cache flush_cache = True conn.reply(None) # Set up a session to interact with XAPI. # # On system start-up, OVS comes up before XAPI, so we can't log into the # session until later. Try to do this on-demand, since we won't # actually do anything interesting until XAPI is up. def init_session(): global session if session is not None: return True try: session = XenAPI.xapi_local() session.xenapi.login_with_password("", "") except XenAPI.Failure, e: session = None vlog.warn("Couldn't login to XAPI (%s)" % e) return False return True def get_network_by_bridge(br_name): if not init_session(): vlog.warn("Failed to get bridge id %s because" " XAPI session could not be initialized" % br_name) return None recs = session.xenapi.network.get_all_records_where('field "bridge"="%s"' % br_name) if len(recs) > 0: return recs.values()[0] return None # There are possibilities when multiple xs-network-uuids are set for a bridge. # In cases like that, we should choose the bridge-id associated with the bridge # name. def get_single_bridge_id(bridge_ids, br_name, default=None): global xapi_down rec = get_network_by_bridge(br_name) if rec and rec['uuid'] in bridge_ids: return rec['uuid'] vlog.warn("Failed to get a single bridge id from Xapi.") xapi_down = True return default # By default, the "bridge-id" external id in the Bridge table is the # same as "xs-network-uuids". This may be overridden by defining a # "nicira-bridge-id" key in the "other_config" field of the network # record of XAPI. If nicira-bridge-id is undefined returns default. # On error returns None. def get_bridge_id(br_name, default=None): rec = get_network_by_bridge(br_name) if rec: return rec['other_config'].get('nicira-bridge-id', default) return None # By default, the "iface-id" external id in the Interface table is the # same as "xs-vif-uuid". This may be overridden by defining a # "nicira-iface-id" key in the "other_config" field of the VIF # record of XAPI. def get_iface_id(if_name, xs_vif_uuid): if not if_name.startswith("vif") and not if_name.startswith("tap"): # Treat whatever was passed into 'xs_vif_uuid' as a default # value for non-VIFs. return xs_vif_uuid if not init_session(): vlog.warn("Failed to get interface id %s because" " XAPI session could not be initialized" % if_name) return xs_vif_uuid try: vif = session.xenapi.VIF.get_by_uuid(xs_vif_uuid) rec = session.xenapi.VIF.get_record(vif) return rec['other_config'].get('nicira-iface-id', xs_vif_uuid) except XenAPI.Failure: vlog.warn("Could not find XAPI entry for VIF %s" % if_name) return xs_vif_uuid # By default, the "vm-id" external id in the Interface table is the # same as "xs-vm-uuid". This may be overridden by defining a # "nicira-vm-id" key in the "other_config" field of the VM # record of XAPI. def get_vm_id(if_name, xs_vm_uuid): if not if_name.startswith("vif") and not if_name.startswith("tap"): # Treat whatever was passed into 'xs_vm_uuid' as a default # value for non-VIFs. return xs_vm_uuid if not init_session(): vlog.warn("Failed to get vm id for interface id %s because" " XAPI session could not be initialized" % if_name) return xs_vm_uuid try: vm = session.xenapi.VM.get_by_uuid(xs_vm_uuid) rec = session.xenapi.VM.get_record(vm) return rec['other_config'].get('nicira-vm-id', xs_vm_uuid) except XenAPI.Failure: vlog.warn("Could not find XAPI entry for VIF %s" % if_name) return xs_vm_uuid def set_or_delete(d, key, value): if value is None: if key in d: del d[key] return True else: if d.get(key) != value: d[key] = value return True return False def set_external_id(row, key, value): row.verify("external_ids") external_ids = row.external_ids if set_or_delete(external_ids, key, value): row.external_ids = external_ids # XenServer does not call interface-reconfigure on internal networks, # which is where the fail-mode would normally be set. def update_fail_mode(row): rec = get_network_by_bridge(row.name) if not rec: return fail_mode = rec['other_config'].get('vswitch-controller-fail-mode') if not fail_mode: pools = session.xenapi.pool.get_all() if len(pools) == 1: prec = session.xenapi.pool.get_record(pools[0]) fail_mode = prec['other_config'].get( 'vswitch-controller-fail-mode') if fail_mode not in ['standalone', 'secure']: fail_mode = 'standalone' row.verify("fail_mode") if row.fail_mode != fail_mode: row.fail_mode = fail_mode def update_in_band_mgmt(row): rec = get_network_by_bridge(row.name) if not rec: return dib = rec['other_config'].get('vswitch-disable-in-band') row.verify("other_config") other_config = row.other_config if dib and dib not in ['true', 'false']: vlog.warn('"%s" isn\'t a valid setting for ' "other_config:disable-in-band on %s" % (dib, row.name)) elif set_or_delete(other_config, 'disable-in-band', dib): row.other_config = other_config def main(): global flush_cache, xapi_down parser = argparse.ArgumentParser() parser.add_argument("database", metavar="DATABASE", help="A socket on which ovsdb-server is listening.") parser.add_argument("--root-prefix", metavar="DIR", default='', help="Use DIR as alternate root directory" " (for testing).") ovs.vlog.add_args(parser) ovs.daemon.add_args(parser) args = parser.parse_args() ovs.vlog.handle_args(args) ovs.daemon.handle_args(args) remote = args.database schema_helper = ovs.db.idl.SchemaHelper() schema_helper.register_columns("Bridge", ["name", "external_ids", "other_config", "fail_mode"]) schema_helper.register_columns("Interface", ["name", "external_ids"]) idl = ovs.db.idl.Idl(remote, schema_helper) ovs.daemon.daemonize() ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None) ovs.unixctl.command_register("flush-cache", "", 0, 0, unixctl_flush_cache, None) error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None) if error: ovs.util.ovs_fatal(error, "could not create unixctl server", vlog) # This daemon is usually started before XAPI, but to complete our # tasks, we need it. Wait here until it's up. cookie_file = args.root_prefix + "/var/run/xapi_init_complete.cookie" while not os.path.exists(cookie_file): time.sleep(1) bridges = {} # Map from bridge name to nicira-bridge-id iface_ids = {} # Map from xs-vif-uuid to iface-id vm_ids = {} # Map from xs-vm-uuid to vm-id seqno = idl.change_seqno # Sequence number when we last processed the db while True: unixctl_server.run() if exiting: break; idl.run() if not xapi_down and not flush_cache and seqno == idl.change_seqno: poller = ovs.poller.Poller() unixctl_server.wait(poller) idl.wait(poller) poller.block() continue if xapi_down: vlog.warn("Xapi is probably down. Retry again after a second.") time.sleep(1) xapi_down = False if flush_cache: vlog.info("Flushing cache as the result of unixctl.") bridges = {} iface_ids = {} vm_ids = {} flush_cache = False seqno = idl.change_seqno txn = ovs.db.idl.Transaction(idl) new_bridges = {} for row in idl.tables["Bridge"].rows.itervalues(): bridge_id = bridges.get(row.name) if bridge_id is None: # Configure the new bridge. update_fail_mode(row) update_in_band_mgmt(row) # Get the correct bridge_id, if we can. bridge_id = get_bridge_id(row.name) if bridge_id is None: xs_network_uuids = row.external_ids.get("xs-network-uuids") if xs_network_uuids: bridge_ids = xs_network_uuids.split(";") if len(bridge_ids) == 1: bridge_id = bridge_ids[0] else: bridge_id = get_single_bridge_id(bridge_ids, row.name) set_external_id(row, "bridge-id", bridge_id) if bridge_id is not None: new_bridges[row.name] = bridge_id bridges = new_bridges iface_by_name = {} for row in idl.tables["Interface"].rows.itervalues(): iface_by_name[row.name] = row new_iface_ids = {} new_vm_ids = {} for row in idl.tables["Interface"].rows.itervalues(): # Match up paired vif and tap devices. if row.name.startswith("vif"): vif = row tap = iface_by_name.get("tap%s" % row.name[3:]) elif row.name.startswith("tap"): tap = row vif = iface_by_name.get("vif%s" % row.name[3:]) else: tap = vif = None # Several tap external-ids need to be copied from the vif. if row == tap and vif: keys = ["attached-mac", "xs-network-uuid", "xs-vif-uuid", "xs-vm-uuid"] for k in keys: set_external_id(row, k, vif.external_ids.get(k)) # Map from xs-vif-uuid to iface-id. # # (A tap's xs-vif-uuid comes from its vif. That falls out # naturally from the copy loop above.) xvu = row.external_ids.get("xs-vif-uuid") if xvu: iface_id = (new_iface_ids.get(xvu) or iface_ids.get(xvu) or get_iface_id(row.name, xvu)) new_iface_ids[xvu] = iface_id else: # No xs-vif-uuid therefore no iface-id. iface_id = None set_external_id(row, "iface-id", iface_id) # Map from xs-vm-uuid to vm-id. xvmu = row.external_ids.get("xs-vm-uuid") if xvmu: vm_id = (new_vm_ids.get(xvmu) or vm_ids.get(xvmu) or get_vm_id(row.name, xvmu)) new_vm_ids[xvmu] = vm_id else: vm_id = None set_external_id(row, "vm-id", vm_id) # When there's a vif and a tap, the tap is active (used for # traffic). When there's just a vif, the vif is active. # # A tap on its own shouldn't happen, and we don't know # anything about other kinds of devices, so we don't use # an iface-status for those devices at all. if vif and tap: set_external_id(tap, "iface-status", "active") set_external_id(vif, "iface-status", "inactive") elif vif: set_external_id(vif, "iface-status", "active") else: set_external_id(row, "iface-status", None) iface_ids = new_iface_ids vm_ids = new_vm_ids txn.add_comment("ovs-xapi-sync: Updating records from XAPI") txn.commit_block() unixctl_server.close() idl.close() if __name__ == '__main__': try: main() except SystemExit: # Let system.exit() calls complete normally raise except: vlog.exception("traceback") sys.exit(ovs.daemon.RESTART_EXIT_CODE) openvswitch-2.5.9/xenserver/PaxHeaders.82075/openvswitch-xen.spec.in0000644000000000000000000000013213534540071022307 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.225849328 openvswitch-2.5.9/xenserver/openvswitch-xen.spec.in0000644000175000017500000004735113534540071024007 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # For XenServer version < 6.5, when building, the rpmbuild command line # should define openvswitch_version, kernel_name, kernel_version and # kernel_flavor using -D arguments. # for example: # # rpmbuild -D "openvswitch_version 1.1.0+build123" # -D "kernel_name NAME-xen" # -D "kernel_version 2.6.32.12-0.7.1.xs5.6.100.323.170596" # -D "kernel_flavor xen" # -bb /usr/src/redhat/SPECS/openvswitch-xen.spec # # For XenServer version >= 6.5, use kernel_uname which should be # the `uname -r` output. # for example: # # rpmbuild -D "openvswitch_version 2.3.0+build123" # -D "kernel_uname 3.10.0+2" # -bb /usr/src/redhat/SPECS/openvswitch-xen.spec # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check xenserver/openvswitch-xen.spec %if %{?openvswitch_version:0}%{!?openvswitch_version:1} %define openvswitch_version @VERSION@ %endif %if %{?kernel_uname:1}%{!?kernel_uname:0} %define kernel_name kernel %define kernel_version %{kernel_uname} %endif %if %{?kernel_name:0}%{!?kernel_name:1} %define kernel %(rpm -qa 'kernel*xen-devel' | head -1) %define kernel_name %(rpm -q --queryformat "%%{Name}" %{kernel} | sed 's/-devel//' | sed 's/kernel-//') %define kernel_version %(rpm -q --queryformat "%%{Version}-%%{Release}" %{kernel}) %define kernel_flavor xen %endif %if %{?xen_version:0}%{!?xen_version:1} %define xen_version %{kernel_version}%{?kernel_flavor:%{kernel_flavor}} %endif # bump this when breaking compatibility with userspace %define module_abi_version 0 # build-supplemental-pack.sh requires this naming for kernel module packages %define module_package modules%{?kernel_flavor:-%{kernel_flavor}}-%{kernel_version} %bcond_without check Name: openvswitch Summary: Open vSwitch daemon/database/utilities Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: Nicira, Inc. Version: %{openvswitch_version} License: ASL 2.0 Release: 1 Source: openvswitch-%{openvswitch_version}.tar.gz Buildroot: /tmp/openvswitch-xen-rpm Requires: openvswitch.ko.%{module_abi_version} %description Open vSwitch provides standard network bridging functions augmented with support for the OpenFlow protocol for remote per-flow control of traffic. %package %{module_package} Summary: Open vSwitch kernel module Group: System Environment/Kernel License: GPLv2 Provides: %{name}-modules%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version}, openvswitch.ko.%{module_abi_version} %if %{?kernel_uname:0}%{!?kernel_uname:1} Requires: kernel%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version} %endif %if %{?kernel_uname:1}%{!?kernel_uname:0} Requires: kernel-uname-r = %{kernel_version} %endif %description %{module_package} Open vSwitch Linux kernel module compiled against kernel version %{kernel_version}%{?kernel_flavor:%{kernel_flavor}}. %prep %setup -q -n openvswitch-%{openvswitch_version} %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{xen_version}/build --enable-ssl CFLAGS='-g -O2 -msse -msse2' make %{_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT install -d -m 755 $RPM_BUILD_ROOT/etc install -d -m 755 $RPM_BUILD_ROOT/etc/init.d install -m 755 xenserver/etc_init.d_openvswitch \ $RPM_BUILD_ROOT/etc/init.d/openvswitch install -m 755 xenserver/etc_init.d_openvswitch-xapi-update \ $RPM_BUILD_ROOT/etc/init.d/openvswitch-xapi-update install -d -m 755 $RPM_BUILD_ROOT/etc/sysconfig install -d -m 755 $RPM_BUILD_ROOT/etc/logrotate.d install -m 755 xenserver/etc_logrotate.d_openvswitch \ $RPM_BUILD_ROOT/etc/logrotate.d/openvswitch install -d -m 755 $RPM_BUILD_ROOT/etc/profile.d install -m 755 xenserver/etc_profile.d_openvswitch.sh \ $RPM_BUILD_ROOT/etc/profile.d/openvswitch.sh install -d -m 755 $RPM_BUILD_ROOT/etc/xapi.d/plugins install -m 755 xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \ $RPM_BUILD_ROOT/etc/xapi.d/plugins/openvswitch-cfg-update install -d -m 755 $RPM_BUILD_ROOT/usr/share/openvswitch/scripts install -m 755 xenserver/opt_xensource_libexec_interface-reconfigure \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/interface-reconfigure install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigure.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigure.py install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py install -m 755 xenserver/etc_xensource_scripts_vif \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/vif install -m 755 xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/ovs-xapi-sync install -m 755 xenserver/usr_share_openvswitch_scripts_sysconfig.template \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/sysconfig.template install -d -m 755 $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base install -m 644 \ xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \ $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch find datapath/linux -name *.ko -exec install -m 755 \{\} $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch \; install -d -m 755 $RPM_BUILD_ROOT/etc/xensource/bugtool cp -rf $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/etc/xensource/bugtool # Get rid of stuff we don't want to make RPM happy. rm \ $RPM_BUILD_ROOT/usr/bin/ovs-benchmark \ $RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \ $RPM_BUILD_ROOT/usr/bin/ovs-pki \ $RPM_BUILD_ROOT/usr/bin/ovs-test \ $RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 (cd "$RPM_BUILD_ROOT" && rm -f usr/lib/lib*) (cd "$RPM_BUILD_ROOT" && rm -rf usr/include) (cd "$RPM_BUILD_ROOT" && rm -rf usr/lib/pkgconfig) install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %post # A list of Citrix XenServer scripts that we might need to replace # with our own versions. scripts=" /etc/xensource/scripts/vif /opt/xensource/libexec/InterfaceReconfigure.py /opt/xensource/libexec/InterfaceReconfigureBridge.py /opt/xensource/libexec/InterfaceReconfigureVswitch.py /opt/xensource/libexec/interface-reconfigure" # Calculate into $md5sums a comma-separated set of md5sums of the # Citrix XenServer scripts that we might need to replace. We might be # upgrading an older version of the package that moved the files out # of the way, so we need to look for the files in those out-of-the-way # locations first. md5sums= for script in $scripts; do b=$(basename "$script") if test -e /usr/lib/openvswitch/xs-saved/"$b"; then f=/usr/lib/openvswitch/xs-saved/"$b" elif test -e /usr/lib/openvswitch/xs-original/"$b"; then f=/usr/lib/openvswitch/xs-original/"$b" elif test -e "$script" && test ! -h "$script"; then f=$script else printf "\n$script: not found\n" f=/dev/null fi md5sums="$md5sums,$(md5sum $f | awk '{print $1}')" done md5sums=${md5sums#,} # Now check the md5sums against the known sets of md5sums: # # - If they are known to be a version of XenServer scripts that we should # replace, we replace them (by putting $scripts into $replace_files). # # - Otherwise, we guess that it's better not to replace them, because the # improvements that our versions of the scripts provide are minimal, so # it's better to avoid possibly breaking any changes made upstream by # Citrix. case $md5sums in cf09a68d9f8b434e79a4c83b01a3bb4b,395866df1b0b20c12c4dd2f7de0ecdb4,9d493545ae81463239d3162cbc798852,862d0939b441de9264a900628e950fe9,21f85db25599d7f026cd489385d58aa6) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 6.0.0.\n" ;; c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,f3feff30aa3b3f8b514664a96a8dc0ab) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 5.6-SP2.\n" ;; c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,24bae6906d182ba47668174f8e480cc6) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 5.6-FP1.\n" ;; *) keep_files=$scripts replace_files= cat </dev/null 2>&1; then :; else cat >>/etc/sysctl.conf < /dev/null fi # Create default or update existing /etc/sysconfig/openvswitch. SYSCONFIG=/etc/sysconfig/openvswitch TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template if [ ! -e $SYSCONFIG ]; then cp $TEMPLATE $SYSCONFIG else for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE) do if ! grep $var $SYSCONFIG >/dev/null 2>&1; then echo >> $SYSCONFIG sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG fi done fi # Deliberately break %postun in broken OVS builds that revert original # XenServer scripts during rpm -U by moving the directory where it thinks # they are saved. if [ -d /usr/lib/openvswitch/xs-original ]; then mkdir -p /usr/lib/openvswitch/xs-saved mv /usr/lib/openvswitch/xs-original/* /usr/lib/openvswitch/xs-saved/ && rmdir /usr/lib/openvswitch/xs-original fi # Replace XenServer files by our versions. mkdir -p /usr/lib/openvswitch/xs-saved \ || printf "Could not create script backup directory.\n" for f in $replace_files; do s=$(basename "$f") t=$(readlink "$f") if [ -f "$f" ] && [ "$t" != "/usr/share/openvswitch/scripts/$s" ]; then mv "$f" /usr/lib/openvswitch/xs-saved/ \ || printf "Could not save original XenServer $s script\n" ln -s "/usr/share/openvswitch/scripts/$s" "$f" \ || printf "Could not link to Open vSwitch $s script\n" fi done # Clean up dangling symlinks to removed OVS replacement scripts no longer # provided by OVS. Any time a replacement script is removed from OVS, # it should be added here to ensure correct reversion from old versions of # OVS that don't clean up dangling symlinks during the uninstall phase. for orig in /usr/sbin/xen-bugtool $keep_files; do saved=/usr/lib/openvswitch/xs-saved/$(basename "$orig") [ -e "$saved" ] && mv -f "$saved" "$orig" done # Ensure all required services are set to run for s in openvswitch openvswitch-xapi-update; do if chkconfig --list $s >/dev/null 2>&1; then chkconfig --del $s || printf "Could not remove $s init script.\n" fi chkconfig --add $s || printf "Could not add $s init script.\n" chkconfig $s on || printf "Could not enable $s init script.\n" done if [ "$1" = "1" ]; then # $1 = 1 for install # Configure system to use Open vSwitch /opt/xensource/bin/xe-switch-network-backend vswitch else # $1 = 2 for upgrade mode=$(cat /etc/xensource/network.conf) if [ "$mode" != "vswitch" ] && [ "$mode" != "openvswitch" ]; then printf "\nThe server is not configured to run Open vSwitch. To run in\n" printf "vswitch mode, you must run the following command:\n\n" printf "\txe-switch-network-backend vswitch" printf "\n\n" fi fi %posttrans %{module_package} # Ensure that modprobe will find our modules. # # This has to be in %posttrans instead of %post because older versions # installed modules into a different directory and "rpm -U" runs the # new version's %post before removing the old version's files, so if # we use %post then depmod may find the old versions that are about to # be removed. depmod %{xen_version} mode=$(cat /etc/xensource/network.conf) if [ "$mode" = "vswitch" ] || [ "$mode" = "openvswitch" ]; then printf "\nTo use the newly installed Open vSwitch kernel module, you\n" printf "will either have to reboot the hypervisor or follow any\n" printf "workarounds provided by your administration guide. Failure to do\n" printf "so may result in incorrect operation." printf "\n\n" fi %preun if [ "$1" = "0" ]; then # $1 = 0 for uninstall # Configure system to use bridge /opt/xensource/bin/xe-switch-network-backend bridge # The "openvswitch" service should have been removed from # "xe-switch-network-backend bridge". for s in openvswitch openvswitch-xapi-update; do if chkconfig --list $s >/dev/null 2>&1; then chkconfig --del $s || printf "Could not remove $s init script." fi done fi %postun # Restore original XenServer scripts if the OVS equivalent no longer exists. # This works both in the upgrade and erase cases. # This lists every file that every version of OVS has ever replaced. Never # remove old files that OVS no longer replaces, or upgrades from old versions # will fail to restore the XS originals, leaving the system in a broken state. # Also be sure to add removed script paths to the %post scriptlet above to # prevent the same problem when upgrading from old versions of OVS that lack # this restore-on-upgrade logic. for f in \ /etc/xensource/scripts/vif \ /usr/sbin/xen-bugtool \ /opt/xensource/libexec/interface-reconfigure \ /opt/xensource/libexec/InterfaceReconfigure.py \ /opt/xensource/libexec/InterfaceReconfigureBridge.py \ /opt/xensource/libexec/InterfaceReconfigureVswitch.py do # Only revert dangling symlinks. if [ -h "$f" ] && [ ! -e "$f" ]; then s=$(basename "$f") if [ ! -f "/usr/lib/openvswitch/xs-saved/$s" ]; then printf "Original XenServer $s script not present in /usr/lib/openvswitch/xs-saved\n" >&2 printf "Could not restore original XenServer script.\n" >&2 else (rm -f "$f" \ && mv "/usr/lib/openvswitch/xs-saved/$s" "$f") \ || printf "Could not restore original XenServer $s script.\n" >&2 fi fi done if [ "$1" = "0" ]; then # $1 = 0 for uninstall rm -f /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyc \ /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyo rm -f /usr/share/openvswitch/scripts/InterfaceReconfigure.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigure.pyo \ /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyo \ /usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyo # Remove all configuration files rm -f /etc/openvswitch/conf.db rm -f /etc/sysconfig/openvswitch rm -f /etc/openvswitch/vswitchd.cacert # Remove saved XenServer scripts directory, but only if it's empty rmdir -p /usr/lib/openvswitch/xs-saved 2>/dev/null fi exit 0 %files %defattr(-,root,root) /etc/bash_completion.d/ovs-appctl-bashcomp.bash /etc/bash_completion.d/ovs-vsctl-bashcomp.bash /etc/init.d/openvswitch /etc/init.d/openvswitch-xapi-update /etc/xapi.d/plugins/openvswitch-cfg-update /etc/xensource/bugtool/* /etc/logrotate.d/openvswitch /etc/profile.d/openvswitch.sh /usr/share/openvswitch/python/ /usr/share/openvswitch/bugtool-plugins/* /usr/share/openvswitch/scripts/ovs-check-dead-ifs /usr/share/openvswitch/scripts/ovs-xapi-sync /usr/share/openvswitch/scripts/interface-reconfigure /usr/share/openvswitch/scripts/InterfaceReconfigure.py /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py /usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py /usr/share/openvswitch/scripts/vif /usr/share/openvswitch/scripts/sysconfig.template /usr/share/openvswitch/scripts/ovs-bugtool-* /usr/share/openvswitch/scripts/ovs-save /usr/share/openvswitch/scripts/ovs-ctl /usr/share/openvswitch/scripts/ovs-lib /usr/share/openvswitch/scripts/ovs-vtep /usr/share/openvswitch/vswitch.ovsschema /usr/share/openvswitch/vtep.ovsschema /usr/sbin/ovs-bugtool /usr/sbin/ovs-vlan-bug-workaround /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server /usr/bin/ovs-appctl /usr/bin/ovs-dpctl /usr/bin/ovs-dpctl-top /usr/bin/ovs-docker /usr/bin/ovs-ofctl /usr/bin/ovs-parse-backtrace /usr/bin/ovs-pcap /usr/bin/ovs-tcpundump /usr/bin/ovs-vlan-test /usr/bin/ovs-vsctl /usr/bin/ovsdb-client /usr/bin/ovsdb-tool /usr/bin/vtep-ctl /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py /usr/share/man/man1/ovsdb-client.1.gz /usr/share/man/man1/ovsdb-server.1.gz /usr/share/man/man1/ovsdb-tool.1.gz /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz /usr/share/man/man5/vtep.5.gz /usr/share/man/man8/ovs-appctl.8.gz /usr/share/man/man8/ovs-bugtool.8.gz /usr/share/man/man8/ovs-ctl.8.gz /usr/share/man/man8/ovs-dpctl.8.gz /usr/share/man/man8/ovs-dpctl-top.8.gz /usr/share/man/man8/ovs-ofctl.8.gz /usr/share/man/man8/ovs-parse-backtrace.8.gz /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man8/ovs-vlan-bug-workaround.8.gz /usr/share/man/man8/ovs-vlan-test.8.gz /usr/share/man/man8/ovs-vsctl.8.gz /usr/share/man/man8/ovs-vswitchd.8.gz /usr/share/man/man8/vtep-ctl.8.gz /var/lib/openvswitch /var/log/openvswitch %exclude /usr/lib/xsconsole/plugins-base/*.py[co] %exclude /usr/share/openvswitch/scripts/*.py[co] %exclude /usr/share/openvswitch/python/*.py[co] %exclude /usr/share/openvswitch/python/ovs/*.py[co] %exclude /usr/share/openvswitch/python/ovs/db/*.py[co] %exclude /usr/bin/ovn-* %exclude /usr/share/man/man5/ovn-* %exclude /usr/share/man/man7/ovn-* %exclude /usr/share/man/man8/ovn-* %exclude /usr/share/openvswitch/ovn-* %exclude /usr/share/openvswitch/scripts/ovn-* %files %{module_package} /lib/modules/%{xen_version}/extra/openvswitch/openvswitch.ko /lib/modules/%{xen_version}/extra/openvswitch/vport-*.ko openvswitch-2.5.9/xenserver/PaxHeaders.82075/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py0000644000000000000000000000013013534540071027700 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.233849386 openvswitch-2.5.9/xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py0000644000175000017500000002676113534540071031404 0ustar00jpettitjpettit00000000000000# Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc. # Copyright (c) 2007-2011 Citrix Systems Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from XSConsoleLog import * import os import socket import subprocess vsctl="/usr/bin/ovs-vsctl" if __name__ == "__main__": raise Exception("This script is a plugin for xsconsole and cannot run independently") from XSConsoleStandard import * class VSwitchService: service = {} def __init__(self, name, processname=None): self.name = name self.processname = processname if self.processname == None: self.processname = name def version(self): try: output = ShellPipe(["service", self.name, "version"]).Stdout() except StandardError, e: XSLogError("vswitch version retrieval error: " + str(e)) return "" for line in output: if self.processname in line: return line.split()[-1] return "" def status(self): try: output = ShellPipe(["service", self.name, "status"]).Stdout() except StandardError, e: XSLogError("vswitch status retrieval error: " + str(e)) return "" if len(output) == 0: return "" for line in output: if self.processname not in line: continue elif "running" in line: return "Running" elif "stop" in line: return "Stopped" else: return "" return "" def restart(self): try: ShellPipe(["service", self.name, "restart"]).Call() except StandardError, e: XSLogError("vswitch restart error: " + str(e)) @classmethod def Inst(cls, name, processname=None): key = name if processname != None: key = key + "-" + processname if name not in cls.service: cls.service[key] = VSwitchService(name, processname) return cls.service[key] class VSwitchConfig: @staticmethod def Get(action): try: arg = [vsctl, "-vconsole:off"] + action.split() output = ShellPipe(arg).Stdout() except StandardError, e: XSLogError("config retrieval error: " + str(e)) return "" if len(output) == 0: output = "" else: output = output[0].strip() return output class VSwitchControllerDialogue(Dialogue): def __init__(self): Dialogue.__init__(self) data=Data.Inst() self.hostsInPool = 0 self.hostsUpdated = 0 self.xs_version = data.host.software_version.product_version('') pool = data.GetPoolForThisHost() if pool is not None: self.controller = pool.get("vswitch_controller", "") else: self.controller = "" choiceDefs = [ ChoiceDef(Lang("Set pool-wide controller"), lambda: self.getController()), ChoiceDef(Lang("Delete pool-wide controller"), lambda: self.deleteController()), ChoiceDef(Lang("Resync server controller config"), lambda: self.syncController()), # ChoiceDef(Lang("Restart ovs-vswitchd"), # lambda: self.restartService("vswitch")), ] self.menu = Menu(self, None, Lang("Configure Open vSwitch"), choiceDefs) self.ChangeState("INITIAL") def BuildPane(self): pane = self.NewPane(DialoguePane(self.parent)) pane.TitleSet(Lang("Configure Open vSwitch")) pane.AddBox() def ChangeState(self, inState): self.state = inState self.BuildPane() self.UpdateFields() def UpdateFields(self): self.Pane().ResetPosition() getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state def UpdateFieldsINITIAL(self): pane = self.Pane() pane.AddTitleField(Lang("Select an action")) pane.AddMenuField(self.menu) pane.AddKeyHelpField( { Lang("") : Lang("OK"), Lang("") : Lang("Cancel") } ) def UpdateFieldsGETCONTROLLER(self): pane = self.Pane() pane.ResetFields() pane.AddTitleField(Lang("Enter IP address of controller")) pane.AddInputField(Lang("Address", 16), self.controller, "address") pane.AddKeyHelpField( { Lang("") : Lang("OK"), Lang("") : Lang("Exit") } ) if pane.CurrentInput() is None: pane.InputIndexSet(0) def HandleKey(self, inKey): handled = False if hasattr(self, "HandleKey" + self.state): handled = getattr(self, "HandleKey" + self.state)(inKey) if not handled and inKey == 'KEY_ESCAPE': Layout.Inst().PopDialogue() handled = True return handled def HandleKeyINITIAL(self, inKey): return self.menu.HandleKey(inKey) def HandleKeyGETCONTROLLER(self, inKey): pane = self.Pane() if pane.CurrentInput() is None: pane.InputIndexSet(0) if inKey == 'KEY_ENTER': inputValues = pane.GetFieldValues() self.controller = inputValues['address'] Layout.Inst().PopDialogue() # Make sure the controller is specified as a valid dotted quad try: socket.inet_aton(self.controller) except socket.error: Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format"))) return True Layout.Inst().TransientBanner(Lang("Setting controller...")) try: self.SetController(self.controller) Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful"))) except Exception, e: Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed"))) self.ChangeState("INITIAL") return True else: return pane.CurrentInput().HandleKey(inKey) def restartService(self, name): s = VSwitchService.Inst(name) s.restart() Layout.Inst().PopDialogue() def getController(self): self.ChangeState("GETCONTROLLER") self.Pane().InputIndexSet(0) def deleteController(self): self.controller = "" Layout.Inst().PopDialogue() Layout.Inst().TransientBanner(Lang("Deleting controller...")) try: self.SetController(None) Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful"))) except Exception, e: Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed"))) def syncController(self): Layout.Inst().PopDialogue() Layout.Inst().TransientBanner(Lang("Resyncing controller setting...")) try: Task.Sync(lambda s: self._updateThisServer(s)) Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful"))) except Exception, e: Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed"))) def SetController(self, ip): self.hostsInPool = 0 self.hostsUpdated = 0 Task.Sync(lambda s: self._modifyPoolConfig(s, ip or "")) # Should be done asynchronously, maybe with an external script? Task.Sync(lambda s: self._updateActiveServers(s)) def _modifyPoolConfig(self, session, value): """Modify pool configuration. If value == "" then delete configuration, otherwise set to value. """ pools = session.xenapi.pool.get_all() # We assume there is only ever one pool... if len(pools) == 0: XSLogFatal(Lang("No pool found for host.")) return if len(pools) > 1: XSLogFatal(Lang("More than one pool for host.")) return session.xenapi.pool.set_vswitch_controller(value) Data.Inst().Update() def _updateActiveServers(self, session): hosts = session.xenapi.host.get_all() self.hostsUpdated = 0 self.hostsInPool = len(hosts) self.UpdateFields() for host in hosts: Layout.Inst().TransientBanner("Updating host %d out of %d" % (self.hostsUpdated + 1, self.hostsInPool)) session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {}) self.hostsUpdated = self.hostsUpdated + 1 def _updateThisServer(self, session): data = Data.Inst() host = data.host.opaqueref() session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {}) class XSFeatureVSwitch: @classmethod def StatusUpdateHandler(cls, inPane): data = Data.Inst() xs_version = data.host.software_version.product_version('') inPane.AddTitleField(Lang("Open vSwitch")) inPane.NewLine() inPane.AddStatusField(Lang("Version", 20), VSwitchService.Inst("openvswitch", "ovs-vswitchd").version()) inPane.NewLine() pool = data.GetPoolForThisHost() if pool is not None: dbController = pool.get("vswitch_controller", "") else: dbController = "" if dbController == "": dbController = Lang("") inPane.AddStatusField(Lang("Controller (config)", 20), dbController) controller = VSwitchConfig.Get("get-manager") if controller == "": controller = Lang("") elif controller[0:4] == "ssl:": controller = controller.split(':')[1] inPane.AddStatusField(Lang("Controller (in-use)", 20), controller) inPane.NewLine() inPane.AddStatusField(Lang("ovs-vswitchd status", 20), VSwitchService.Inst("openvswitch", "ovs-vswitchd").status()) inPane.AddStatusField(Lang("ovsdb-server status", 20), VSwitchService.Inst("openvswitch", "ovsdb-server").status()) inPane.AddKeyHelpField( { Lang("") : Lang("Reconfigure"), Lang("") : Lang("Refresh") }) @classmethod def ActivateHandler(cls): DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue())) def Register(self): Importer.RegisterNamedPlugIn( self, 'VSwitch', # Key of this plugin for replacement, etc. { 'menuname' : 'MENU_NETWORK', 'menupriority' : 800, 'menutext' : Lang('Open vSwitch') , 'statusupdatehandler' : self.StatusUpdateHandler, 'activatehandler' : self.ActivateHandler } ) # Register this plugin when module is imported, IFF vswitchd is running if os.path.exists('/var/run/openvswitch/ovs-vswitchd.pid'): XSFeatureVSwitch().Register() openvswitch-2.5.9/xenserver/PaxHeaders.82075/README0000644000000000000000000000013213534540071016545 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.213849239 openvswitch-2.5.9/xenserver/README0000644000175000017500000001352013534540071020234 0ustar00jpettitjpettit00000000000000This directory contains files for seamless integration of Open vSwitch on Citrix XenServer hosts managed by the Citrix management tools. Files in this directory are licensed on a file-by-file basis. Please refer to each file for details. Most of the files in this directory are installed on a XenServer system under the same name; underscores are replaced by slashes. The files are: etc_init.d_openvswitch Initializes Open vSwitch at boot and shuts it down at shutdown. etc_init.d_openvswitch-xapi-update Init script to ensure openvswitch-cfg-update is called for the current host at boot. etc_logrotate.d_openvswitch Ensures that logs in /var/log/openvswitch are rotated periodically and that appropriate daemons reopen their log files at that point. etc_profile.d_openvswitch.sh Open vSwitch-related shell functions for the administrator's convenience. etc_xapi.d_plugins_openvswitch-cfg-update xapi plugin script to update the cache of configuration items in the ovs-vswitchd configuration that are managed in the xapi database when integrated with Citrix management tools. etc_xensource_scripts_vif Open vSwitch-aware replacement for Citrix script of the same name. openvswitch-xen.spec spec file for building RPMs to install on a XenServer host. opt_xensource_libexec_interface-reconfigure opt_xensource_libexec_InterfaceReconfigureBridge.py opt_xensource_libexec_InterfaceReconfigure.py opt_xensource_libexec_InterfaceReconfigureVswitch.py Open vSwitch-aware replacements for Citrix script of the same names. usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py xsconsole plugin to configure the pool-wide configuration keys used to control Open vSwitch when integrated with Citrix management tools. usr_share_openvswitch_scripts_ovs-xapi-sync Daemon to monitor the external_ids columns of the Bridge and Interface OVSDB tables for changes that require interrogating XAPI. usr_share_openvswitch_scripts_sysconfig.template Template for Open vSwitch's /etc/sysconfig/openvswitch configuration file. Open vSwitch installs a number of xen-bugtool extensions in /etc/xensource/bugtool to gather additional information useful for debugging. The sources for the extensions are in ../utilities/bugtool/plugins: kernel-info/openvswitch.xml Collect kernel information relevant to Open vSwitch, such as slabinfo. network-status/openvswitch.xml Collect networking information relevant to Open vSwitch. Runs the following scripts, which are described below: * ovs-bugtool-bfd-show * ovs-bugtool-cfm-show * ovs-bugtool-fdb-show * ovs-bugtool-lacp-show * ovs-bugtool-list-dbs * ovs-bugtool-ovsdb-dump * ovs-bugtool-tc-class-show * ovs-bugtool-bond-show * ovs-bugtool-ovs-ofctl-show * ovs-bugtool-ovs-ofctl-dump-flows * ovs-bugtool-ovs-appctl-dpif * ovs-bugtool-coverage-show * ovs-bugtool-memory-show * ovs-bugtool-vsctl-show * ovs-bugtool-conntrack-dump system-configuration/openvswitch.xml Collect system configuration information relevant to Open vSwitch, including timezone. Runs the following script which is described below: * ovs-bugtool-daemons-ver system-configuration.xml Collect system configuration data. This category is configured to collect up to 1Mb of data, take up to 60 seconds to collect data, run every time and is hidden from display in XenCenter. A number of scripts are installed in /usr/share/openvswitch/scripts to assist Open vSwitch's xen-bugtool extensions. The sources for the scripts are located in ../utilities/bugtool: ovs-bugtool-bfd-show Script to dump detailed BFD information for all enabled interfaces. ovs-bugtool-cfm-show Script to dump detailed CFM information for all enabled interfaces. ovs-bugtool-fdb-show Script to collect a summary of learned MACs for each bridge. ovs-bugtool-lacp-show Script to dump detailed LACP information for all enabled ports. ovs-bugtool-list-dbs Script to list the databases controlled by ovsdb-server. ovs-bugtool-ovsdb-dump Script to dump contents of Open vSwitch configuration database in comma-separated value format. ovs-bugtool-tc-class-show Script to dump tc class configuration for all network interfaces. ovs-bugtool-ovs-ofctl-show Script to dump information about flow tables and ports of each bridge. ovs-bugtool-ovs-ofctl-dump-flows Script to dump openflow flows of each bridge. ovs-bugtool-ovs-appctl-dpif Script to collect a summary of configured datapaths and datapath flows. ovs-bugtool-coverage-show Script to count the number of times particular events occur during ovs-vswitchd's runtime. ovs-bugtool-memory-show Script to show some basic statistics about ovs-vswitchd's memory usage. ovs-bugtool-vsctl-show Script to show a brief overview of the database contents. ovs-bugtool-conntrack-dump Script to show all the connection entries in the tracker. ovs-bugtool-daemons-ver Script to dump version information for all Open vSwitch daemons. ---------------------------------------------------------------------- Copyright (C) 2009, 2010, 2011 Nicira, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_logrotate.d_openvswitch0000644000000000000000000000013213534540071023316 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.217849268 openvswitch-2.5.9/xenserver/etc_logrotate.d_openvswitch0000644000175000017500000000111113534540071024776 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. /var/log/openvswitch/*.log { daily compress sharedscripts missingok postrotate # Tell Open vSwitch daemons to reopen their log files for pidfile in `cd /var/run/openvswitch && echo *.pid`; do ovs-appctl -t "${pidfile%%.pid}" vlog/reopen done endscript } openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_xensource_scripts_vif0000644000000000000000000000013213534540071023071 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.221849298 openvswitch-2.5.9/xenserver/etc_xensource_scripts_vif0000755000175000017500000002074613534540071024573 0ustar00jpettitjpettit00000000000000#!/bin/sh # Copyright (C) 2008,2009 Citrix Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # CA-23900: Warning: when VIFs are added to windows guests with PV drivers the backend vif device is registered, # unregistered and then registered again. This causes the udev event to fire twice and this script runs twice. # Since the first invocation of the script races with the device unregistration, spurious errors are possible # which will be logged but are safe to ignore since the second script invocation should complete the operation. # Note that each script invocation is run synchronously from udev and so the scripts don't race with each other. # Keep other-config/ keys in sync with device.ml:vif_udev_keys BRCTL="/usr/sbin/brctl" IP="/sbin/ip" vsctl="/usr/bin/ovs-vsctl" handle_promiscuous() { local arg=$(xenstore-read "${PRIVATE}/other-config/promiscuous" 2>/dev/null) if [ $? -eq 0 -a -n "${arg}" ] ; then case $NETWORK_MODE in bridge) case "${arg}" in true|on) echo 1 > /sys/class/net/${dev}/brport/promisc ;; *) echo 0 > /sys/class/net/${dev}/brport/promisc ;; esac ;; openvswitch) logger -t script-vif "${dev}: Promiscuous ports are not supported via Open vSwitch." ;; esac fi } handle_ethtool() { local opt=$1 local arg=$(xenstore-read "${PRIVATE}/other-config/ethtool-${opt}" 2>/dev/null) if [ $? -eq 0 -a -n "${arg}" ] ; then case "${arg}" in true|on) /sbin/ethtool -K "${dev}" "${opt}" on ;; false|off) /sbin/ethtool -K "${dev}" "${opt}" off ;; *) logger -t scripts-vif "Unknown ethtool argument ${opt}=${arg} on ${dev}/${VIFUUID}" ;; esac fi } handle_mtu() { local mtu=$(xenstore-read "${PRIVATE}/MTU" 2>/dev/null) if [ $? -eq 0 -a -n "${mtu}" ]; then logger -t scripts-vif "Setting ${dev} MTU ${mtu}" ${IP} link set "${dev}" mtu ${mtu} || logger -t scripts-vif "Failed to ip link set ${dev} mtu ${mtu}. Error code $?" fi } set_vif_external_id() { local key=$1 local value=$2 logger -t scripts-vif "vif${DOMID}.${DEVID} external-ids:\"${key}\"=\"${value}\"" echo "-- set interface vif${DOMID}.${DEVID} external-ids:\"${key}\"=\"${value}\"" } handle_vswitch_vif_details() { local vm=$(xenstore-read "/local/domain/$DOMID/vm" 2>/dev/null) if [ $? -eq 0 -a -n "${vm}" ] ; then local vm_uuid=$(xenstore-read "$vm/uuid" 2>/dev/null) fi if [ -n "${vm_uuid}" ] ; then set_vif_external_id "xs-vm-uuid" "${vm_uuid}" fi local vif_uuid=$(xenstore-read "${PRIVATE}/vif-uuid" 2>/dev/null) if [ -n "${vif_uuid}" ] ; then set_vif_external_id "xs-vif-uuid" "${vif_uuid}" fi local vif_details= local net_uuid=$(xenstore-read "${PRIVATE}/network-uuid" 2>/dev/null) if [ -n "${net_uuid}" ] ; then set_vif_external_id "xs-network-uuid" "${net_uuid}" fi local address=$(xenstore-read "/local/domain/$DOMID/device/vif/$DEVID/mac" 2>/dev/null) if [ -n "${address}" ] ; then set_vif_external_id "attached-mac" "${address}" fi } add_to_bridge() { local address=$(xenstore-read "${PRIVATE}/bridge-MAC") if [ $? -ne 0 -o -z "${address}" ]; then logger -t scripts-vif "Failed to read ${PRIVATE}/bridge-MAC from xenstore" exit 1 fi local bridge=$(xenstore-read "${PRIVATE}/bridge") if [ $? -ne 0 -o -z "${bridge}" ]; then logger -t scripts-vif "Failed to read ${PRIVATE}/bridge from xenstore" exit 1 fi logger -t scripts-vif "Adding ${dev} to ${bridge} with address ${address}" ${IP} link set "${dev}" down || logger -t scripts-vif "Failed to ip link set ${dev} down" ${IP} link set "${dev}" arp off || logger -t scripts-vif "Failed to ip link set ${dev} arp off" ${IP} link set "${dev}" multicast off || logger -t scripts-vif "Failed to ip link set ${dev} multicast off" ${IP} link set "${dev}" address "${address}" || logger -t scripts-vif "Failed to ip link set ${dev} address ${address}" ${IP} addr flush "${dev}" || logger -t scripts-vif "Failed to ip addr flush ${dev}" case $NETWORK_MODE in bridge) ${BRCTL} setfd "${bridge}" 0 || logger -t scripts-vif "Failed to brctl setfd ${bridge} 0" ${BRCTL} addif "${bridge}" "${dev}" || logger -t scripts-vif "Failed to brctl addif ${bridge} ${dev}" ;; openvswitch) if [ "$TYPE" = "vif" ] ; then local vif_details=$(handle_vswitch_vif_details $bridge) fi $vsctl --timeout=30 -- --if-exists del-port $dev -- add-port $bridge $dev $vif_details ;; esac ${IP} link set "${dev}" up || logger -t scripts-vif "Failed to ip link set ${dev} up" } remove_from_bridge() { case $NETWORK_MODE in bridge) # Nothing to do ;; openvswitch) $vsctl --timeout=30 -- del-port $dev ;; esac } call_hook_script() { local domid=$1 local action=$2 # Call the VIF hotplug hook if present if [ -x /etc/xapi.d/vif-hotplug ]; then local vm=$(xenstore-read "/local/domain/$domid/vm" 2>/dev/null) if [ $? -eq 0 -a -n "${vm}" ] ; then local vm_uuid=$(xenstore-read "$vm/uuid" 2>/dev/null) fi if [ -n "${vm_uuid}" ] ; then logger -t scripts-vif "VM UUID ${vm_uuid}" fi local vif_uuid=$(xenstore-read "${PRIVATE}/vif-uuid" 2>/dev/null) if [ -n "${vif_uuid}" ] ; then logger -t scripts-vif "VIF UUID ${vif_uuid}" fi if [ -n "${vif_uuid}" -a -n "${vm_uuid}" ] ; then logger -t scripts-vif "Calling VIF hotplug hook for VM ${vm_uuid}, VIF ${vif_uuid}" /etc/xapi.d/vif-hotplug -action "${action}" -vifuuid "${vif_uuid}" -vmuuid "${vm_uuid}" fi fi } NETWORK_MODE=$(cat /etc/xensource/network.conf) ACTION=$1 # Older versions of XenServer do not pass in the type as an argument if [[ $# -lt 2 ]]; then TYPE=vif else TYPE=$2 fi case $NETWORK_MODE in bridge|openvswitch) ;; vswitch) NETWORK_MODE=openvswitch ;; *) logger -t scripts-vif "Unknown network mode $NETWORK_MODE" exit 1 ;; esac case ${TYPE} in vif) if [ -z ${XENBUS_PATH} ]; then DOMID=$3 DEVID=$4 else DOMID=`echo ${XENBUS_PATH} | cut -f 3 -d '/'` DEVID=`echo ${XENBUS_PATH} | cut -f 4 -d '/'` fi dev=vif${DOMID}.${DEVID} ;; tap) dev=$INTERFACE DOMID=`echo ${dev#tap} | cut -f 1 -d '.'` DEVID=`echo ${dev#tap} | cut -f 2 -d '.'` ;; *) logger -t scripts-vif "unknown interface type ${TYPE}" exit 1 ;; esac XAPI=/xapi/${DOMID}/hotplug/vif/${DEVID} HOTPLUG=/xapi/${DOMID}/hotplug/vif/${DEVID} PRIVATE=/xapi/${DOMID}/private/vif/${DEVID} logger -t scripts-vif "Called as \"$@\" domid:$DOMID devid:$DEVID mode:$NETWORK_MODE" case "${ACTION}" in online) if [ "${TYPE}" = "vif" ] ; then handle_ethtool rx handle_ethtool tx handle_ethtool sg handle_ethtool tso handle_ethtool ufo handle_ethtool gso handle_mtu add_to_bridge handle_promiscuous xenstore-write "${HOTPLUG}/vif" "${dev}" xenstore-write "${HOTPLUG}/hotplug" "online" # xs-xen.pq.hq:91e986b8e49f netback-wait-for-hotplug xenstore-write "/local/domain/0/backend/vif/${DOMID}/${DEVID}/hotplug-status" "connected" call_hook_script $DOMID "${ACTION}" fi ;; add) if [ "${TYPE}" = "tap" ] ; then add_to_bridge fi ;; remove) if [ "${TYPE}" = "vif" ] ;then xenstore-rm "${HOTPLUG}/hotplug" call_hook_script $DOMID "${ACTION}" fi logger -t scripts-vif "${dev} has been removed" remove_from_bridge ;; move) if [ "${TYPE}" = "vif" ] ;then add_to_bridge fi esac openvswitch-2.5.9/xenserver/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020024 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.213849239 openvswitch-2.5.9/xenserver/automake.mk0000644000175000017500000000270413534540071021515 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ xenserver/GPLv2 \ xenserver/LICENSE \ xenserver/README \ xenserver/automake.mk \ xenserver/etc_init.d_openvswitch \ xenserver/etc_init.d_openvswitch-xapi-update \ xenserver/etc_logrotate.d_openvswitch \ xenserver/etc_profile.d_openvswitch.sh \ xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \ xenserver/etc_xensource_scripts_vif \ xenserver/openvswitch-xen.spec \ xenserver/openvswitch-xen.spec.in \ xenserver/opt_xensource_libexec_InterfaceReconfigure.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \ xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \ xenserver/opt_xensource_libexec_interface-reconfigure \ xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \ xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \ xenserver/usr_share_openvswitch_scripts_sysconfig.template $(srcdir)/xenserver/openvswitch-xen.spec: xenserver/openvswitch-xen.spec.in $(top_builddir)/config.status $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ < $(srcdir)/xenserver/$(@F).in > $(@F).tmp || exit 1; \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi openvswitch-2.5.9/xenserver/PaxHeaders.82075/opt_xensource_libexec_InterfaceReconfigure.py0000644000000000000000000000013013534540071026776 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.229849356 openvswitch-2.5.9/xenserver/opt_xensource_libexec_InterfaceReconfigure.py0000644000175000017500000007776713534540071030516 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008,2009 Citrix Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # import sys import syslog import os from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import parse as parseXML the_root_prefix = "" def root_prefix(): """Returns a string to prefix to all file name references, which is useful for testing.""" return the_root_prefix def set_root_prefix(prefix): global the_root_prefix the_root_prefix = prefix log_destination = "syslog" def get_log_destination(): """Returns the current log destination. 'syslog' means "log to syslog". 'stderr' means "log to stderr".""" return log_destination def set_log_destination(dest): global log_destination log_destination = dest # # Logging. # def log(s): if get_log_destination() == 'syslog': syslog.syslog(s) else: print >>sys.stderr, s # # Exceptions. # class Error(Exception): def __init__(self, msg): Exception.__init__(self) self.msg = msg # # Run external utilities # def run_command(command): log("Running command: " + ' '.join(command)) rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command) if rc != 0: log("Command failed %d: " % rc + ' '.join(command)) return False return True # # Configuration File Handling. # class ConfigurationFile(object): """Write a file, tracking old and new versions. Supports writing a new version of a file and applying and reverting those changes. """ __STATE = {"OPEN":"OPEN", "NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED", "REVERTED":"REVERTED", "COMMITTED": "COMMITTED"} def __init__(self, path): dirname,basename = os.path.split(path) self.__state = self.__STATE['OPEN'] self.__children = [] self.__path = os.path.join(dirname, basename) self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old") self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new") self.__f = open(self.__newpath, "w") def attach_child(self, child): self.__children.append(child) def path(self): return self.__path def readlines(self): try: return open(self.path()).readlines() except: return "" def write(self, args): if self.__state != self.__STATE['OPEN']: raise Error("Attempt to write to file in state %s" % self.__state) self.__f.write(args) def close(self): if self.__state != self.__STATE['OPEN']: raise Error("Attempt to close file in state %s" % self.__state) self.__f.close() self.__state = self.__STATE['NOT-APPLIED'] def changed(self): if self.__state != self.__STATE['NOT-APPLIED']: raise Error("Attempt to compare file in state %s" % self.__state) return True def apply(self): if self.__state != self.__STATE['NOT-APPLIED']: raise Error("Attempt to apply configuration from state %s" % self.__state) for child in self.__children: child.apply() log("Applying changes to %s configuration" % self.__path) # Remove previous backup. if os.access(self.__oldpath, os.F_OK): os.unlink(self.__oldpath) # Save current configuration. if os.access(self.__path, os.F_OK): os.link(self.__path, self.__oldpath) os.unlink(self.__path) # Apply new configuration. assert(os.path.exists(self.__newpath)) os.link(self.__newpath, self.__path) # Remove temporary file. os.unlink(self.__newpath) self.__state = self.__STATE['APPLIED'] def revert(self): if self.__state != self.__STATE['APPLIED']: raise Error("Attempt to revert configuration from state %s" % self.__state) for child in self.__children: child.revert() log("Reverting changes to %s configuration" % self.__path) # Remove existing new configuration if os.access(self.__newpath, os.F_OK): os.unlink(self.__newpath) # Revert new configuration. if os.access(self.__path, os.F_OK): os.link(self.__path, self.__newpath) os.unlink(self.__path) # Revert to old configuration. if os.access(self.__oldpath, os.F_OK): os.link(self.__oldpath, self.__path) os.unlink(self.__oldpath) # Leave .*.xapi-new as an aid to debugging. self.__state = self.__STATE['REVERTED'] def commit(self): if self.__state != self.__STATE['APPLIED']: raise Error("Attempt to commit configuration from state %s" % self.__state) for child in self.__children: child.commit() log("Committing changes to %s configuration" % self.__path) if os.access(self.__oldpath, os.F_OK): os.unlink(self.__oldpath) if os.access(self.__newpath, os.F_OK): os.unlink(self.__newpath) self.__state = self.__STATE['COMMITTED'] # # Helper functions for encoding/decoding database attributes to/from XML. # def _str_to_xml(xml, parent, tag, val): e = xml.createElement(tag) parent.appendChild(e) v = xml.createTextNode(val) e.appendChild(v) def _str_from_xml(n): def getText(nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc = rc + node.data return rc return getText(n.childNodes).strip() def _bool_to_xml(xml, parent, tag, val): if val: _str_to_xml(xml, parent, tag, "True") else: _str_to_xml(xml, parent, tag, "False") def _bool_from_xml(n): s = _str_from_xml(n) if s == "True": return True elif s == "False": return False else: raise Error("Unknown boolean value %s" % s) def _strlist_to_xml(xml, parent, ltag, itag, val): e = xml.createElement(ltag) parent.appendChild(e) for v in val: c = xml.createElement(itag) e.appendChild(c) cv = xml.createTextNode(v) c.appendChild(cv) def _strlist_from_xml(n, ltag, itag): ret = [] for n in n.childNodes: if n.nodeName == itag: ret.append(_str_from_xml(n)) return ret def _map_to_xml(xml, parent, tag, val, attrs): e = xml.createElement(tag) parent.appendChild(e) for n,v in val.items(): if n in attrs: _str_to_xml(xml, e, n, v) else: log("Unknown other-config attribute: %s" % n) def _map_from_xml(n, attrs): ret = {} for n in n.childNodes: if n.nodeName in attrs: ret[n.nodeName] = _str_from_xml(n) return ret def _otherconfig_to_xml(xml, parent, val, attrs): return _map_to_xml(xml, parent, "other_config", val, attrs) def _otherconfig_from_xml(n, attrs): return _map_from_xml(n, attrs) # # Definitions of the database objects (and their attributes) used by interface-reconfigure. # # Each object is defined by a dictionary mapping an attribute name in # the xapi database to a tuple containing two items: # - a function which takes this attribute and encodes it as XML. # - a function which takes XML and decocdes it into a value. # # other-config attributes are specified as a simple array of strings _PIF_XML_TAG = "pif" _VLAN_XML_TAG = "vlan" _TUNNEL_XML_TAG = "tunnel" _BOND_XML_TAG = "bond" _NETWORK_XML_TAG = "network" _POOL_XML_TAG = "pool" _ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso', 'gro', 'lro' ] _PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \ [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier', 'hashing-algorithm' ] + \ [ 'vlan-bug-workaround' ] + \ _ETHTOOL_OTHERCONFIG_ATTRS _PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'management': (_bool_to_xml,_bool_from_xml), 'network': (_str_to_xml,_str_from_xml), 'device': (_str_to_xml,_str_from_xml), 'bond_master_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'bond_master_of', 'slave', v), lambda n: _strlist_from_xml(n, 'bond_master_of', 'slave')), 'bond_slave_of': (_str_to_xml,_str_from_xml), 'VLAN': (_str_to_xml,_str_from_xml), 'VLAN_master_of': (_str_to_xml,_str_from_xml), 'VLAN_slave_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v), lambda n: _strlist_from_xml(n, 'VLAN_slave_Of', 'master')), 'tunnel_access_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_access_PIF_of', 'pif', v), lambda n: _strlist_from_xml(n, 'tunnel_access_PIF_of', 'pif')), 'tunnel_transport_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_transport_PIF_of', 'pif', v), lambda n: _strlist_from_xml(n, 'tunnel_transport_PIF_of', 'pif')), 'ip_configuration_mode': (_str_to_xml,_str_from_xml), 'IP': (_str_to_xml,_str_from_xml), 'netmask': (_str_to_xml,_str_from_xml), 'gateway': (_str_to_xml,_str_from_xml), 'DNS': (_str_to_xml,_str_from_xml), 'MAC': (_str_to_xml,_str_from_xml), 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _PIF_OTHERCONFIG_ATTRS), lambda n: _otherconfig_from_xml(n, _PIF_OTHERCONFIG_ATTRS)), # Special case: We write the current value # PIF.currently-attached to the cache but since it will # not be valid when we come to use the cache later # (i.e. after a reboot) we always read it as False. 'currently_attached': (_bool_to_xml, lambda n: False), } _VLAN_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'tagged_PIF': (_str_to_xml,_str_from_xml), 'untagged_PIF': (_str_to_xml,_str_from_xml), } _TUNNEL_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'access_PIF': (_str_to_xml,_str_from_xml), 'transport_PIF': (_str_to_xml,_str_from_xml), } _BOND_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'master': (_str_to_xml,_str_from_xml), 'slaves': (lambda x, p, t, v: _strlist_to_xml(x, p, 'slaves', 'slave', v), lambda n: _strlist_from_xml(n, 'slaves', 'slave')), } _NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes', 'vswitch-controller-fail-mode', 'vswitch-disable-in-band' ] \ + _ETHTOOL_OTHERCONFIG_ATTRS _NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'bridge': (_str_to_xml,_str_from_xml), 'MTU': (_str_to_xml,_str_from_xml), 'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v), lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')), 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS), lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)), } _POOL_OTHERCONFIG_ATTRS = ['vswitch-controller-fail-mode'] _POOL_ATTRS = { 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _POOL_OTHERCONFIG_ATTRS), lambda n: _otherconfig_from_xml(n, _POOL_OTHERCONFIG_ATTRS)), } # # Database Cache object # _db = None def db(): assert(_db is not None) return _db def db_init_from_cache(cache): global _db assert(_db is None) _db = DatabaseCache(cache_file=cache) def db_init_from_xenapi(session): global _db assert(_db is None) _db = DatabaseCache(session_ref=session) class DatabaseCache(object): def __read_xensource_inventory(self): filename = root_prefix() + "/etc/xensource-inventory" f = open(filename, "r") lines = [x.strip("\n") for x in f.readlines()] f.close() defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ] defs = [ (a, b.strip("'")) for (a,b) in defs ] return dict(defs) def __pif_on_host(self,pif): return self.__pifs.has_key(pif) def __get_pif_records_from_xapi(self, session, host): self.__pifs = {} for (p,rec) in session.xenapi.PIF.get_all_records().items(): if rec['host'] != host: continue self.__pifs[p] = {} for f in _PIF_ATTRS: self.__pifs[p][f] = rec[f] self.__pifs[p]['other_config'] = {} for f in _PIF_OTHERCONFIG_ATTRS: if not rec['other_config'].has_key(f): continue self.__pifs[p]['other_config'][f] = rec['other_config'][f] def __get_vlan_records_from_xapi(self, session): self.__vlans = {} for (v,rec) in session.xenapi.VLAN.get_all_records().items(): if not self.__pif_on_host(rec['untagged_PIF']): continue self.__vlans[v] = {} for f in _VLAN_ATTRS: self.__vlans[v][f] = rec[f] def __get_tunnel_records_from_xapi(self, session): self.__tunnels = {} for t in session.xenapi.tunnel.get_all(): rec = session.xenapi.tunnel.get_record(t) if not self.__pif_on_host(rec['transport_PIF']): continue self.__tunnels[t] = {} for f in _TUNNEL_ATTRS: self.__tunnels[t][f] = rec[f] def __get_bond_records_from_xapi(self, session): self.__bonds = {} for (b,rec) in session.xenapi.Bond.get_all_records().items(): if not self.__pif_on_host(rec['master']): continue self.__bonds[b] = {} for f in _BOND_ATTRS: self.__bonds[b][f] = rec[f] def __get_network_records_from_xapi(self, session): self.__networks = {} for (n,rec) in session.xenapi.network.get_all_records().items(): self.__networks[n] = {} for f in _NETWORK_ATTRS: if f == "PIFs": # drop PIFs on other hosts self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)] elif f == "MTU" and f not in rec: # XenServer 5.5 network records did not have an # MTU field, so allow this to be missing. pass else: self.__networks[n][f] = rec[f] self.__networks[n]['other_config'] = {} for f in _NETWORK_OTHERCONFIG_ATTRS: if not rec['other_config'].has_key(f): continue self.__networks[n]['other_config'][f] = rec['other_config'][f] def __get_pool_records_from_xapi(self, session): self.__pools = {} for p in session.xenapi.pool.get_all(): rec = session.xenapi.pool.get_record(p) self.__pools[p] = {} for f in _POOL_ATTRS: self.__pools[p][f] = rec[f] for f in _POOL_OTHERCONFIG_ATTRS: if rec['other_config'].has_key(f): self.__pools[p]['other_config'][f] = rec['other_config'][f] def __to_xml(self, xml, parent, key, ref, rec, attrs): """Encode a database object as XML""" e = xml.createElement(key) parent.appendChild(e) if ref: e.setAttribute('ref', ref) for n,v in rec.items(): if attrs.has_key(n): h,_ = attrs[n] h(xml, e, n, v) else: raise Error("Unknown attribute %s" % n) def __from_xml(self, e, attrs): """Decode a database object from XML""" ref = e.attributes['ref'].value rec = {} for n in e.childNodes: if n.nodeName in attrs: _,h = attrs[n.nodeName] rec[n.nodeName] = h(n) return (ref,rec) def __init__(self, session_ref=None, cache_file=None): if session_ref and cache_file: raise Error("can't specify session reference and cache file") if cache_file == None: import XenAPI session = XenAPI.xapi_local() if not session_ref: log("No session ref given on command line, logging in.") session.xenapi.login_with_password("root", "") else: session._session = session_ref try: inventory = self.__read_xensource_inventory() assert(inventory.has_key('INSTALLATION_UUID')) log("host uuid is %s" % inventory['INSTALLATION_UUID']) host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID']) self.__get_pif_records_from_xapi(session, host) self.__get_pool_records_from_xapi(session) self.__get_tunnel_records_from_xapi(session) self.__get_vlan_records_from_xapi(session) self.__get_bond_records_from_xapi(session) self.__get_network_records_from_xapi(session) finally: if not session_ref: session.xenapi.session.logout() else: log("Loading xapi database cache from %s" % cache_file) xml = parseXML(root_prefix() + cache_file) self.__pifs = {} self.__bonds = {} self.__vlans = {} self.__pools = {} self.__tunnels = {} self.__networks = {} assert(len(xml.childNodes) == 1) toplevel = xml.childNodes[0] assert(toplevel.nodeName == "xenserver-network-configuration") for n in toplevel.childNodes: if n.nodeName == "#text": pass elif n.nodeName == _PIF_XML_TAG: (ref,rec) = self.__from_xml(n, _PIF_ATTRS) self.__pifs[ref] = rec elif n.nodeName == _BOND_XML_TAG: (ref,rec) = self.__from_xml(n, _BOND_ATTRS) self.__bonds[ref] = rec elif n.nodeName == _VLAN_XML_TAG: (ref,rec) = self.__from_xml(n, _VLAN_ATTRS) self.__vlans[ref] = rec elif n.nodeName == _TUNNEL_XML_TAG: (ref,rec) = self.__from_xml(n, _TUNNEL_ATTRS) self.__vlans[ref] = rec elif n.nodeName == _NETWORK_XML_TAG: (ref,rec) = self.__from_xml(n, _NETWORK_ATTRS) self.__networks[ref] = rec elif n.nodeName == _POOL_XML_TAG: (ref,rec) = self.__from_xml(n, _POOL_ATTRS) self.__pools[ref] = rec else: raise Error("Unknown XML element %s" % n.nodeName) def save(self, cache_file): xml = getDOMImplementation().createDocument( None, "xenserver-network-configuration", None) for (ref,rec) in self.__pifs.items(): self.__to_xml(xml, xml.documentElement, _PIF_XML_TAG, ref, rec, _PIF_ATTRS) for (ref,rec) in self.__bonds.items(): self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS) for (ref,rec) in self.__vlans.items(): self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS) for (ref,rec) in self.__tunnels.items(): self.__to_xml(xml, xml.documentElement, _TUNNEL_XML_TAG, ref, rec, _TUNNEL_ATTRS) for (ref,rec) in self.__networks.items(): self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec, _NETWORK_ATTRS) for (ref,rec) in self.__pools.items(): self.__to_xml(xml, xml.documentElement, _POOL_XML_TAG, ref, rec, _POOL_ATTRS) temp_file = cache_file + ".%d" % os.getpid() f = open(temp_file, 'w') f.write(xml.toprettyxml()) f.close() os.rename(temp_file, cache_file) def get_pif_by_uuid(self, uuid): pifs = map(lambda (ref,rec): ref, filter(lambda (ref,rec): uuid == rec['uuid'], self.__pifs.items())) if len(pifs) == 0: raise Error("Unknown PIF \"%s\"" % uuid) elif len(pifs) > 1: raise Error("Non-unique PIF \"%s\"" % uuid) return pifs[0] def get_pifs_by_device(self, device): return map(lambda (ref,rec): ref, filter(lambda (ref,rec): rec['device'] == device, self.__pifs.items())) def get_networks_with_bridge(self, bridge): return map(lambda (ref,rec): ref, filter(lambda (ref,rec): rec['bridge'] == bridge, self.__networks.items())) def get_network_by_bridge(self, bridge): #Assumes one network has bridge. try: return self.get_networks_with_bridge(bridge)[0] except KeyError: return None def get_pif_by_bridge(self, bridge): networks = self.get_networks_with_bridge(bridge) if len(networks) == 0: raise Error("No matching network \"%s\"" % bridge) answer = None for network in networks: nwrec = self.get_network_record(network) for pif in nwrec['PIFs']: pifrec = self.get_pif_record(pif) if answer: raise Error("Multiple PIFs on host for network %s" % (bridge)) answer = pif if not answer: raise Error("No PIF on host for network %s" % (bridge)) return answer def get_pif_record(self, pif): if self.__pifs.has_key(pif): return self.__pifs[pif] raise Error("Unknown PIF \"%s\"" % pif) def get_all_pifs(self): return self.__pifs def pif_exists(self, pif): return self.__pifs.has_key(pif) def get_management_pif(self): """ Returns the management pif on host """ all = self.get_all_pifs() for pif in all: pifrec = self.get_pif_record(pif) if pifrec['management']: return pif return None def get_network_record(self, network): if self.__networks.has_key(network): return self.__networks[network] raise Error("Unknown network \"%s\"" % network) def get_bond_record(self, bond): if self.__bonds.has_key(bond): return self.__bonds[bond] else: return None def get_vlan_record(self, vlan): if self.__vlans.has_key(vlan): return self.__vlans[vlan] else: return None def get_pool_record(self): if len(self.__pools) > 0: return self.__pools.values()[0] # # # PIF_OTHERCONFIG_DEFAULTS = {'gro': 'off', 'lro': 'off'} def ethtool_settings(oc, defaults = {}): settings = [] if oc.has_key('ethtool-speed'): val = oc['ethtool-speed'] if val in ["10", "100", "1000"]: settings += ['speed', val] else: log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val) if oc.has_key('ethtool-duplex'): val = oc['ethtool-duplex'] if val in ["half", "full"]: settings += ['duplex', val] else: log("Invalid value for ethtool-duplex = %s. Must be half|full." % val) if oc.has_key('ethtool-autoneg'): val = oc['ethtool-autoneg'] if val in ["true", "on"]: settings += ['autoneg', 'on'] elif val in ["false", "off"]: settings += ['autoneg', 'off'] else: log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val) offload = [] for opt in ("rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro"): if oc.has_key("ethtool-" + opt): val = oc["ethtool-" + opt] if val in ["true", "on"]: offload += [opt, 'on'] elif val in ["false", "off"]: offload += [opt, 'off'] else: log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val)) elif opt in defaults: offload += [opt, defaults[opt]] return settings,offload # By default the MTU is taken from the Network.MTU setting for VIF, # PIF and Bridge. However it is possible to override this by using # {VIF,PIF,Network}.other-config:mtu. # # type parameter is a string describing the object that the oc parameter # is from. e.g. "PIF", "Network" def mtu_setting(nw, type, oc): mtu = None nwrec = db().get_network_record(nw) if nwrec.has_key('MTU'): mtu = nwrec['MTU'] else: mtu = "1500" if oc.has_key('mtu'): log("Override Network.MTU setting on bridge %s from %s.MTU is %s" % \ (nwrec['bridge'], type, mtu)) mtu = oc['mtu'] if mtu is not None: try: int(mtu) # Check that the value is an integer return mtu except ValueError, x: log("Invalid value for mtu = %s" % mtu) return None # # IP Network Devices -- network devices with IP configuration # def pif_ipdev_name(pif): """Return the ipdev name associated with pif""" pifrec = db().get_pif_record(pif) nwrec = db().get_network_record(pifrec['network']) if nwrec['bridge']: # TODO: sanity check that nwrec['bridgeless'] != 'true' return nwrec['bridge'] else: # TODO: sanity check that nwrec['bridgeless'] == 'true' return pif_netdev_name(pif) # # Bare Network Devices -- network devices without IP configuration # def netdev_exists(netdev): return os.path.exists(root_prefix() + "/sys/class/net/" + netdev) def pif_netdev_name(pif): """Get the netdev name for a PIF.""" pifrec = db().get_pif_record(pif) if pif_is_vlan(pif): return "%(device)s.%(VLAN)s" % pifrec else: return pifrec['device'] # # Bridges # def pif_is_bridged(pif): pifrec = db().get_pif_record(pif) nwrec = db().get_network_record(pifrec['network']) if nwrec['bridge']: # TODO: sanity check that nwrec['bridgeless'] != 'true' return True else: # TODO: sanity check that nwrec['bridgeless'] == 'true' return False def pif_bridge_name(pif): """Return the bridge name of a pif. PIF must be a bridged PIF.""" pifrec = db().get_pif_record(pif) nwrec = db().get_network_record(pifrec['network']) if nwrec['bridge']: return nwrec['bridge'] else: raise Error("PIF %(uuid)s does not have a bridge name" % pifrec) # # Bonded PIFs # def pif_is_bond(pif): pifrec = db().get_pif_record(pif) return len(pifrec['bond_master_of']) > 0 def pif_get_bond_masters(pif): """Returns a list of PIFs which are bond masters of this PIF""" pifrec = db().get_pif_record(pif) bso = pifrec['bond_slave_of'] # bond-slave-of is currently a single reference but in principle a # PIF could be a member of several bonds which are not # concurrently attached. Be robust to this possibility. if not bso or bso == "OpaqueRef:NULL": bso = [] elif not type(bso) == list: bso = [bso] bondrecs = [db().get_bond_record(bond) for bond in bso] bondrecs = [rec for rec in bondrecs if rec] return [bond['master'] for bond in bondrecs] def pif_get_bond_slaves(pif): """Returns a list of PIFs which make up the given bonded pif.""" pifrec = db().get_pif_record(pif) bmo = pifrec['bond_master_of'] if len(bmo) > 1: raise Error("Bond-master-of contains too many elements") if len(bmo) == 0: return [] bondrec = db().get_bond_record(bmo[0]) if not bondrec: raise Error("No bond record for bond master PIF") return bondrec['slaves'] # # VLAN PIFs # def pif_is_vlan(pif): return db().get_pif_record(pif)['VLAN'] != '-1' def pif_get_vlan_slave(pif): """Find the PIF which is the VLAN slave of pif. Returns the 'physical' PIF underneath the a VLAN PIF @pif.""" pifrec = db().get_pif_record(pif) vlan = pifrec['VLAN_master_of'] if not vlan or vlan == "OpaqueRef:NULL": raise Error("PIF is not a VLAN master") vlanrec = db().get_vlan_record(vlan) if not vlanrec: raise Error("No VLAN record found for PIF") return vlanrec['tagged_PIF'] def pif_get_vlan_masters(pif): """Returns a list of PIFs which are VLANs on top of the given pif.""" pifrec = db().get_pif_record(pif) vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']] return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])] # # Tunnel PIFs # def pif_is_tunnel(pif): return len(db().get_pif_record(pif)['tunnel_access_PIF_of']) > 0 # # Datapath base class # class Datapath(object): """Object encapsulating the actions necessary to (de)configure the datapath for a given PIF. Does not include configuration of the IP address on the ipdev. """ def __init__(self, pif): self._pif = pif @classmethod def rewrite(cls): """Class method called when write action is called. Can be used to update any backend specific configuration.""" pass def configure_ipdev(self, cfg): """Write ifcfg TYPE field for an IPdev, plus any type specific fields to cfg """ raise NotImplementedError def preconfigure(self, parent): """Prepare datapath configuration for PIF, but do not actually apply any changes. Any configuration files should be attached to parent. """ raise NotImplementedError def bring_down_existing(self): """Tear down any existing network device configuration which needs to be undone in order to bring this PIF up. """ raise NotImplementedError def configure(self): """Apply the configuration prepared in the preconfigure stage. Should assume any configuration files changed attached in the preconfigure stage are applied and bring up the necessary devices to provide the datapath for the PIF. Should not bring up the IPdev. """ raise NotImplementedError def post(self): """Called after the IPdev has been brought up. Should do any final setup, including reinstating any devices which were taken down in the bring_down_existing hook. """ raise NotImplementedError def bring_down(self): """Tear down and deconfigure the datapath. Should assume the IPdev has already been brought down. """ raise NotImplementedError def DatapathFactory(): # XXX Need a datapath object for bridgeless PIFs try: network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r') network_backend = network_conf.readline().strip() network_conf.close() except Exception, e: raise Error("failed to determine network backend:" + e) if network_backend == "bridge": from InterfaceReconfigureBridge import DatapathBridge return DatapathBridge elif network_backend in ["openvswitch", "vswitch"]: from InterfaceReconfigureVswitch import DatapathVswitch return DatapathVswitch else: raise Error("unknown network backend %s" % network_backend) openvswitch-2.5.9/xenserver/PaxHeaders.82075/opt_xensource_libexec_interface-reconfigure0000644000000000000000000000013013534540071026524 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.229849356 openvswitch-2.5.9/xenserver/opt_xensource_libexec_interface-reconfigure0000755000175000017500000005741213534540071030230 0ustar00jpettitjpettit00000000000000#!/usr/bin/env python # # Copyright (c) 2008,2009 Citrix Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # """Usage: %(command-name)s up %(command-name)s down %(command-name)s rewrite %(command-name)s --force up %(command-name)s --force down %(command-name)s --force rewrite --device= --mac= where is one of: --session --pif --pif-uuid and is one of: --mode=dhcp --mode=static --ip= --netmask= [--gateway=] Options: --session A session reference to use to access the xapi DB --pif A PIF reference within the session. --pif-uuid The UUID of a PIF. --force An interface name. --root-prefix=DIR Use DIR as alternate root directory (for testing). --no-syslog Write log messages to stderr instead of system log. """ # Notes: # 1. Every pif belongs to exactly one network # 2. Every network has zero or one pifs # 3. A network may have an associated bridge, allowing vifs to be attached # 4. A network may be bridgeless (there's no point having a bridge over a storage pif) from InterfaceReconfigure import * import os, sys, getopt import syslog import traceback import re import random import syslog management_pif = None dbcache_file = "/var/xapi/network.dbcache" # # Logging. # def log_pif_action(action, pif): pifrec = db().get_pif_record(pif) rec = {} rec['uuid'] = pifrec['uuid'] rec['ip_configuration_mode'] = pifrec['ip_configuration_mode'] rec['action'] = action rec['pif_netdev_name'] = pif_netdev_name(pif) rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec) # # Exceptions. # class Usage(Exception): def __init__(self, msg): Exception.__init__(self) self.msg = msg # # Boot from Network filesystem or device. # def check_allowed(pif): """Determine whether interface-reconfigure should be manipulating this PIF. Used to prevent system PIFs (such as network root disk) from being interfered with. """ pifrec = db().get_pif_record(pif) try: f = open(root_prefix() + "/proc/ardence") macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines()) f.close() if len(macline) == 1: p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE) if p.match(macline[0]): log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec) return False except IOError: pass return True # # Bare Network Devices -- network devices without IP configuration # def netdev_remap_name(pif, already_renamed=[]): """Check whether 'pif' exists and has the correct MAC. If not, try to find a device with the correct MAC and rename it. 'already_renamed' is used to avoid infinite recursion. """ def read1(name): file = None try: file = open(name, 'r') return file.readline().rstrip('\n') finally: if file != None: file.close() def get_netdev_mac(device): try: return read1("%s/sys/class/net/%s/address" % (root_prefix(), device)) except: # Probably no such device. return None def get_netdev_tx_queue_len(device): try: return int(read1("%s/sys/class/net/%s/tx_queue_len" % (root_prefix(), device))) except: # Probably no such device. return None def get_netdev_by_mac(mac): for device in os.listdir(root_prefix() + "/sys/class/net"): dev_mac = get_netdev_mac(device) if (dev_mac and mac.lower() == dev_mac.lower() and get_netdev_tx_queue_len(device)): return device return None def rename_netdev(old_name, new_name): raise Error("Trying to rename %s to %s - This functionality has been removed" % (old_name, new_name)) # log("Changing the name of %s to %s" % (old_name, new_name)) # run_command(['/sbin/ifconfig', old_name, 'down']) # if not run_command(['/sbin/ip', 'link', 'set', old_name, 'name', new_name]): # raise Error("Could not rename %s to %s" % (old_name, new_name)) pifrec = db().get_pif_record(pif) device = pifrec['device'] mac = pifrec['MAC'] # Is there a network device named 'device' at all? device_exists = netdev_exists(device) if device_exists: # Yes. Does it have MAC 'mac'? found_mac = get_netdev_mac(device) if found_mac and mac.lower() == found_mac.lower(): # Yes, everything checks out the way we want. Nothing to do. return else: log("No network device %s" % device) # What device has MAC 'mac'? cur_device = get_netdev_by_mac(mac) if not cur_device: log("No network device has MAC %s" % mac) return # First rename 'device', if it exists, to get it out of the way # for 'cur_device' to replace it. if device_exists: rename_netdev(device, "dev%d" % random.getrandbits(24)) # Rename 'cur_device' to 'device'. rename_netdev(cur_device, device) # # IP Network Devices -- network devices with IP configuration # def ifdown(netdev): """Bring down a network interface""" if not netdev_exists(netdev): log("ifdown: device %s does not exist, ignoring" % netdev) return if not os.path.exists("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), netdev)): log("ifdown: device %s exists but ifcfg-%s does not" % (netdev,netdev)) run_command(["/sbin/ifconfig", netdev, 'down']) return run_command(["/sbin/ifdown", netdev]) def ifup(netdev): """Bring up a network interface""" if not os.path.exists(root_prefix() + "/etc/sysconfig/network-scripts/ifcfg-%s" % netdev): raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev)) d = os.getenv("DHCLIENTARGS","") if os.path.exists("/etc/firstboot.d/data/firstboot_in_progress"): os.putenv("DHCLIENTARGS", d + " -T 240 " ) run_command(["/sbin/ifup", netdev]) os.putenv("DHCLIENTARGS", d ) # # # def pif_rename_physical_devices(pif): if pif_is_tunnel(pif): return if pif_is_vlan(pif): pif = pif_get_vlan_slave(pif) if pif_is_bond(pif): pifs = pif_get_bond_slaves(pif) else: pifs = [pif] for pif in pifs: netdev_remap_name(pif) # # IP device configuration # def ipdev_configure_static_routes(interface, oc, f): """Open a route- file for static routes. Opens the static routes configuration file for interface and writes one line for each route specified in the network's other config "static-routes" value. E.g. if interface ( RO): xenbr1 other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;... Then route-xenbr1 should be 172.16.0.0/15 via 192.168.0.3 dev xenbr1 172.18.0.0/16 via 192.168.0.4 dev xenbr1 """ if oc.has_key('static-routes'): # The key is present - extract comma separates entries lines = oc['static-routes'].split(',') else: # The key is not present, i.e. there are no static routes lines = [] child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface)) child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ (os.path.basename(child.path()), os.path.basename(sys.argv[0]))) try: for l in lines: network, masklen, gateway = l.split('/') child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface)) f.attach_child(child) child.close() except ValueError, e: log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e)) def ipdev_open_ifcfg(pif): ipdev = pif_ipdev_name(pif) log("Writing network configuration for %s" % ipdev) f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev)) f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ (os.path.basename(f.path()), os.path.basename(sys.argv[0]))) f.write("XEMANAGED=yes\n") f.write("DEVICE=%s\n" % ipdev) f.write("ONBOOT=no\n") f.write("NOZEROCONF=yes\n") return f def ipdev_configure_network(pif, dp): """Write the configuration file for a network. Writes configuration derived from the network object into the relevant ifcfg file. The configuration file is passed in, but if the network is bridgeless it will be ifcfg-, otherwise it will be ifcfg-. This routine may also write ifcfg files of the networks corresponding to other PIFs in order to maintain consistency. params: pif: Opaque_ref of pif dp: Datapath object """ pifrec = db().get_pif_record(pif) nw = pifrec['network'] nwrec = db().get_network_record(nw) ipdev = pif_ipdev_name(pif) f = ipdev_open_ifcfg(pif) mode = pifrec['ip_configuration_mode'] log("Configuring %s using %s configuration" % (ipdev, mode)) oc = None if pifrec.has_key('other_config'): oc = pifrec['other_config'] dp.configure_ipdev(f) if pifrec['ip_configuration_mode'] == "DHCP": f.write("BOOTPROTO=dhcp\n") f.write("PERSISTENT_DHCLIENT=yes\n") elif pifrec['ip_configuration_mode'] == "Static": f.write("BOOTPROTO=none\n") f.write("NETMASK=%(netmask)s\n" % pifrec) f.write("IPADDR=%(IP)s\n" % pifrec) f.write("GATEWAY=%(gateway)s\n" % pifrec) elif pifrec['ip_configuration_mode'] == "None": f.write("BOOTPROTO=none\n") else: raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode']) if nwrec.has_key('other_config'): settings,offload = ethtool_settings(nwrec['other_config']) if len(settings): f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) if len(offload): f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) ipdev_configure_static_routes(ipdev, nwrec['other_config'], f) mtu = mtu_setting(nw, "Network", nwrec['other_config']) if mtu: f.write("MTU=%s\n" % mtu) if pifrec.has_key('DNS') and pifrec['DNS'] != "": ServerList = pifrec['DNS'].split(",") for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i])) if oc and oc.has_key('domain'): f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' ')) # There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network. # # The peerdns pif will be the one with # pif::other-config:peerdns=true, or the mgmt pif if none have # this set. # # The gateway pif will be the one with # pif::other-config:defaultroute=true, or the mgmt pif if none # have this set. # Work out which pif on this host should be the DNSDEV and which # should be the GATEWAYDEV # # Note: we prune out the bond master pif (if it exists). This is # because when we are called to bring up an interface with a bond # master, it is implicit that we should bring down that master. pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)] # now prune out bond slaves as they are not connected to the IP # stack and so cannot be used as gateway or DNS devices. pifs_on_host = [ p for p in pifs_on_host if len(pif_get_bond_masters(p)) == 0] # loop through all the pifs on this host looking for one with # other-config:peerdns = true, and one with # other-config:default-route=true peerdns_pif = None defaultroute_pif = None for __pif in pifs_on_host: __pifrec = db().get_pif_record(__pif) __oc = __pifrec['other_config'] if __oc.has_key('peerdns') and __oc['peerdns'] == 'true': if peerdns_pif == None: peerdns_pif = __pif else: log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \ (db().get_pif_record(peerdns_pif)['device'], __pifrec['device'])) if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true': if defaultroute_pif == None: defaultroute_pif = __pif else: log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \ (db().get_pif_record(defaultroute_pif)['device'], __pifrec['device'])) # If no pif is explicitly specified then use the mgmt pif for # peerdns/defaultroute. if peerdns_pif == None: peerdns_pif = management_pif if defaultroute_pif == None: defaultroute_pif = management_pif is_dnsdev = peerdns_pif == pif is_gatewaydev = defaultroute_pif == pif if is_dnsdev or is_gatewaydev: fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network") for line in fnetwork.readlines(): if is_dnsdev and line.lstrip().startswith('DNSDEV='): fnetwork.write('DNSDEV=%s\n' % ipdev) is_dnsdev = False elif is_gatewaydev and line.lstrip().startswith('GATEWAYDEV='): fnetwork.write('GATEWAYDEV=%s\n' % ipdev) is_gatewaydev = False else: fnetwork.write(line) if is_dnsdev: fnetwork.write('DNSDEV=%s\n' % ipdev) if is_gatewaydev: fnetwork.write('GATEWAYDEV=%s\n' % ipdev) fnetwork.close() f.attach_child(fnetwork) return f # # Toplevel actions # def action_up(pif, force): pifrec = db().get_pif_record(pif) ipdev = pif_ipdev_name(pif) dp = DatapathFactory()(pif) log("action_up: %s" % ipdev) f = ipdev_configure_network(pif, dp) dp.preconfigure(f) f.close() pif_rename_physical_devices(pif) # if we are not forcing the interface up then attempt to tear down # any existing devices which might interfere with brinign this one # up. if not force: ifdown(ipdev) dp.bring_down_existing() try: f.apply() dp.configure() ifup(ipdev) dp.post() # Update /etc/issue (which contains the IP address of the management interface) os.system(root_prefix() + "/sbin/update-issue") f.commit() except Error, e: log("failed to apply changes: %s" % e.msg) f.revert() raise def action_down(pif): ipdev = pif_ipdev_name(pif) dp = DatapathFactory()(pif) log("action_down: %s" % ipdev) ifdown(ipdev) dp.bring_down() def action_rewrite(): DatapathFactory().rewrite() # This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master def action_force_rewrite(bridge, config): def getUUID(): import subprocess uuid,_ = subprocess.Popen(['uuidgen'], stdout = subprocess.PIPE).communicate() return uuid.strip() # Notes: # 1. that this assumes the interface is bridged # 2. If --gateway is given it will make that the default gateway for the host # extract the configuration try: mode = config['mode'] mac = config['mac'] interface = config['device'] except: raise Usage("Please supply --mode, --mac and --device") if mode == 'static': try: netmask = config['netmask'] ip = config['ip'] except: raise Usage("Please supply --netmask and --ip") try: gateway = config['gateway'] except: gateway = None elif mode != 'dhcp': raise Usage("--mode must be either static or dhcp") if config.has_key('vlan'): is_vlan = True vlan_slave, vlan_vid = config['vlan'].split('.') else: is_vlan = False if is_vlan: raise Error("Force rewrite of VLAN not implemented") log("Configuring %s using %s configuration" % (bridge, mode)) f = ConfigurationFile(root_prefix() + dbcache_file) pif_uuid = getUUID() network_uuid = getUUID() f.write('\n') f.write('\n') f.write('\t\n' % pif_uuid) f.write('\t\tOpaqueRef:%s\n' % network_uuid) f.write('\t\tTrue\n') f.write('\t\t%sPif\n' % interface) f.write('\t\tOpaqueRef:NULL\n') f.write('\t\t\n') f.write('\t\t\n') f.write('\t\tOpaqueRef:NULL\n') f.write('\t\t-1\n') f.write('\t\t\n') f.write('\t\t\n') f.write('\t\t%s\n' % interface) f.write('\t\t%s\n' % mac) f.write('\t\t\n') if mode == 'dhcp': f.write('\t\tDHCP\n') f.write('\t\t\n') f.write('\t\t\n') f.write('\t\t\n') f.write('\t\t\n') elif mode == 'static': f.write('\t\tStatic\n') f.write('\t\t%s\n' % ip) f.write('\t\t%s\n' % netmask) if gateway is not None: f.write('\t\t%s\n' % gateway) f.write('\t\t\n') else: raise Error("Unknown mode %s" % mode) f.write('\t\n') f.write('\t\n' % network_uuid) f.write('\t\tInitialManagementNetwork\n') f.write('\t\t\n') f.write('\t\t\tOpaqueRef:%s\n' % pif_uuid) f.write('\t\t\n') f.write('\t\t%s\n' % bridge) f.write('\t\t\n') f.write('\t\n') f.write('\n') f.close() try: f.apply() f.commit() except Error, e: log("failed to apply changes: %s" % e.msg) f.revert() raise def main(argv=None): global management_pif session = None pif_uuid = None pif = None force_interface = None force_management = False if argv is None: argv = sys.argv try: try: shortops = "h" longops = [ "pif=", "pif-uuid=", "session=", "force=", "force-interface=", "management", "mac=", "device=", "mode=", "ip=", "netmask=", "gateway=", "root-prefix=", "no-syslog", "help" ] arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops) except getopt.GetoptError, msg: raise Usage(msg) force_rewrite_config = {} for o,a in arglist: if o == "--pif": pif = a elif o == "--pif-uuid": pif_uuid = a elif o == "--session": session = a elif o == "--force-interface" or o == "--force": force_interface = a elif o == "--management": force_management = True elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]: force_rewrite_config[o[2:]] = a elif o == "--root-prefix": set_root_prefix(a) elif o == "--no-syslog": set_log_destination("stderr") elif o == "-h" or o == "--help": print __doc__ % {'command-name': os.path.basename(argv[0])} return 0 if get_log_destination() == "syslog": syslog.openlog(os.path.basename(argv[0])) log("Called as " + str.join(" ", argv)) if len(args) < 1: raise Usage("Required option not present") if len(args) > 1: raise Usage("Too many arguments") action = args[0] if not action in ["up", "down", "rewrite", "rewrite-configuration"]: raise Usage("Unknown action \"%s\"" % action) # backwards compatibility if action == "rewrite-configuration": action = "rewrite" if ( session or pif ) and pif_uuid: raise Usage("--session/--pif and --pif-uuid are mutually exclusive.") if ( session and not pif ) or ( not session and pif ): raise Usage("--session and --pif must be used together.") if force_interface and ( session or pif or pif_uuid ): raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid") if len(force_rewrite_config) and not (force_interface and action == "rewrite"): raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway") if (action == "rewrite") and (pif or pif_uuid ): raise Usage("rewrite action does not take --pif or --pif-uuid") global db if force_interface: log("Force interface %s %s" % (force_interface, action)) if action == "rewrite": action_force_rewrite(force_interface, force_rewrite_config) elif action in ["up", "down"]: db_init_from_cache(dbcache_file) pif = db().get_pif_by_bridge(force_interface) management_pif = db().get_management_pif() if action == "up": action_up(pif, True) elif action == "down": action_down(pif) else: raise Error("Unknown action %s" % action) else: db_init_from_xenapi(session) if pif_uuid: pif = db().get_pif_by_uuid(pif_uuid) if action == "rewrite": action_rewrite() else: if not pif: raise Usage("No PIF given") if force_management: # pif is going to be the management pif management_pif = pif else: # pif is not going to be the management pif. # Search DB cache for pif on same host with management=true pifrec = db().get_pif_record(pif) management_pif = db().get_management_pif() log_pif_action(action, pif) if not check_allowed(pif): return 0 if action == "up": action_up(pif, False) elif action == "down": action_down(pif) else: raise Error("Unknown action %s" % action) # Save cache. db().save(dbcache_file) except Usage, err: print >>sys.stderr, err.msg print >>sys.stderr, "For help use --help." return 2 except Error, err: log(err.msg) return 1 return 0 if __name__ == "__main__": rc = 1 try: rc = main() except: ex = sys.exc_info() err = traceback.format_exception(*ex) for exline in err: log(exline) syslog.closelog() sys.exit(rc) openvswitch-2.5.9/xenserver/PaxHeaders.82075/openvswitch-xen.spec0000644000000000000000000000013213534540117021703 xustar0030 mtime=1567801423.441843548 30 atime=1567801423.441843548 30 ctime=1567801424.221849298 openvswitch-2.5.9/xenserver/openvswitch-xen.spec0000644000175000017500000004746013534540117023404 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # For XenServer version < 6.5, when building, the rpmbuild command line # should define openvswitch_version, kernel_name, kernel_version and # kernel_flavor using -D arguments. # for example: # # rpmbuild -D "openvswitch_version 1.1.0+build123" # -D "kernel_name NAME-xen" # -D "kernel_version 2.6.32.12-0.7.1.xs5.6.100.323.170596" # -D "kernel_flavor xen" # -bb /usr/src/redhat/SPECS/openvswitch-xen.spec # # For XenServer version >= 6.5, use kernel_uname which should be # the `uname -r` output. # for example: # # rpmbuild -D "openvswitch_version 2.3.0+build123" # -D "kernel_uname 3.10.0+2" # -bb /usr/src/redhat/SPECS/openvswitch-xen.spec # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check xenserver/openvswitch-xen.spec %if %{?openvswitch_version:0}%{!?openvswitch_version:1} %define openvswitch_version 2.5.9 %endif %if %{?kernel_uname:1}%{!?kernel_uname:0} %define kernel_name kernel %define kernel_version %{kernel_uname} %endif %if %{?kernel_name:0}%{!?kernel_name:1} %define kernel %(rpm -qa 'kernel*xen-devel' | head -1) %define kernel_name %(rpm -q --queryformat "%%{Name}" %{kernel} | sed 's/-devel//' | sed 's/kernel-//') %define kernel_version %(rpm -q --queryformat "%%{Version}-%%{Release}" %{kernel}) %define kernel_flavor xen %endif %if %{?xen_version:0}%{!?xen_version:1} %define xen_version %{kernel_version}%{?kernel_flavor:%{kernel_flavor}} %endif # bump this when breaking compatibility with userspace %define module_abi_version 0 # build-supplemental-pack.sh requires this naming for kernel module packages %define module_package modules%{?kernel_flavor:-%{kernel_flavor}}-%{kernel_version} %bcond_without check Name: openvswitch Summary: Open vSwitch daemon/database/utilities Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: Nicira, Inc. Version: %{openvswitch_version} License: ASL 2.0 Release: 1 Source: openvswitch-%{openvswitch_version}.tar.gz Buildroot: /tmp/openvswitch-xen-rpm Requires: openvswitch.ko.%{module_abi_version} %description Open vSwitch provides standard network bridging functions augmented with support for the OpenFlow protocol for remote per-flow control of traffic. %package %{module_package} Summary: Open vSwitch kernel module Group: System Environment/Kernel License: GPLv2 Provides: %{name}-modules%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version}, openvswitch.ko.%{module_abi_version} %if %{?kernel_uname:0}%{!?kernel_uname:1} Requires: kernel%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version} %endif %if %{?kernel_uname:1}%{!?kernel_uname:0} Requires: kernel-uname-r = %{kernel_version} %endif %description %{module_package} Open vSwitch Linux kernel module compiled against kernel version %{kernel_version}%{?kernel_flavor:%{kernel_flavor}}. %prep %setup -q -n openvswitch-%{openvswitch_version} %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{xen_version}/build --enable-ssl CFLAGS='-g -O2 -msse -msse2' make %{_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT install -d -m 755 $RPM_BUILD_ROOT/etc install -d -m 755 $RPM_BUILD_ROOT/etc/init.d install -m 755 xenserver/etc_init.d_openvswitch \ $RPM_BUILD_ROOT/etc/init.d/openvswitch install -m 755 xenserver/etc_init.d_openvswitch-xapi-update \ $RPM_BUILD_ROOT/etc/init.d/openvswitch-xapi-update install -d -m 755 $RPM_BUILD_ROOT/etc/sysconfig install -d -m 755 $RPM_BUILD_ROOT/etc/logrotate.d install -m 755 xenserver/etc_logrotate.d_openvswitch \ $RPM_BUILD_ROOT/etc/logrotate.d/openvswitch install -d -m 755 $RPM_BUILD_ROOT/etc/profile.d install -m 755 xenserver/etc_profile.d_openvswitch.sh \ $RPM_BUILD_ROOT/etc/profile.d/openvswitch.sh install -d -m 755 $RPM_BUILD_ROOT/etc/xapi.d/plugins install -m 755 xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \ $RPM_BUILD_ROOT/etc/xapi.d/plugins/openvswitch-cfg-update install -d -m 755 $RPM_BUILD_ROOT/usr/share/openvswitch/scripts install -m 755 xenserver/opt_xensource_libexec_interface-reconfigure \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/interface-reconfigure install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigure.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigure.py install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py install -m 755 xenserver/etc_xensource_scripts_vif \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/vif install -m 755 xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/ovs-xapi-sync install -m 755 xenserver/usr_share_openvswitch_scripts_sysconfig.template \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/sysconfig.template install -d -m 755 $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base install -m 644 \ xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \ $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch find datapath/linux -name *.ko -exec install -m 755 \{\} $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch \; install -d -m 755 $RPM_BUILD_ROOT/etc/xensource/bugtool cp -rf $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/etc/xensource/bugtool # Get rid of stuff we don't want to make RPM happy. rm \ $RPM_BUILD_ROOT/usr/bin/ovs-benchmark \ $RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \ $RPM_BUILD_ROOT/usr/bin/ovs-pki \ $RPM_BUILD_ROOT/usr/bin/ovs-test \ $RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 (cd "$RPM_BUILD_ROOT" && rm -f usr/lib/lib*) (cd "$RPM_BUILD_ROOT" && rm -rf usr/include) (cd "$RPM_BUILD_ROOT" && rm -rf usr/lib/pkgconfig) install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %post # A list of Citrix XenServer scripts that we might need to replace # with our own versions. scripts=" /etc/xensource/scripts/vif /opt/xensource/libexec/InterfaceReconfigure.py /opt/xensource/libexec/InterfaceReconfigureBridge.py /opt/xensource/libexec/InterfaceReconfigureVswitch.py /opt/xensource/libexec/interface-reconfigure" # Calculate into $md5sums a comma-separated set of md5sums of the # Citrix XenServer scripts that we might need to replace. We might be # upgrading an older version of the package that moved the files out # of the way, so we need to look for the files in those out-of-the-way # locations first. md5sums= for script in $scripts; do b=$(basename "$script") if test -e /usr/lib/openvswitch/xs-saved/"$b"; then f=/usr/lib/openvswitch/xs-saved/"$b" elif test -e /usr/lib/openvswitch/xs-original/"$b"; then f=/usr/lib/openvswitch/xs-original/"$b" elif test -e "$script" && test ! -h "$script"; then f=$script else printf "\n$script: not found\n" f=/dev/null fi md5sums="$md5sums,$(md5sum $f | awk '{print $1}')" done md5sums=${md5sums#,} # Now check the md5sums against the known sets of md5sums: # # - If they are known to be a version of XenServer scripts that we should # replace, we replace them (by putting $scripts into $replace_files). # # - Otherwise, we guess that it's better not to replace them, because the # improvements that our versions of the scripts provide are minimal, so # it's better to avoid possibly breaking any changes made upstream by # Citrix. case $md5sums in cf09a68d9f8b434e79a4c83b01a3bb4b,395866df1b0b20c12c4dd2f7de0ecdb4,9d493545ae81463239d3162cbc798852,862d0939b441de9264a900628e950fe9,21f85db25599d7f026cd489385d58aa6) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 6.0.0.\n" ;; c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,f3feff30aa3b3f8b514664a96a8dc0ab) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 5.6-SP2.\n" ;; c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,24bae6906d182ba47668174f8e480cc6) keep_files= replace_files=$scripts printf "\nVerified host scripts from XenServer 5.6-FP1.\n" ;; *) keep_files=$scripts replace_files= cat </dev/null 2>&1; then :; else cat >>/etc/sysctl.conf < /dev/null fi # Create default or update existing /etc/sysconfig/openvswitch. SYSCONFIG=/etc/sysconfig/openvswitch TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template if [ ! -e $SYSCONFIG ]; then cp $TEMPLATE $SYSCONFIG else for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE) do if ! grep $var $SYSCONFIG >/dev/null 2>&1; then echo >> $SYSCONFIG sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG fi done fi # Deliberately break %postun in broken OVS builds that revert original # XenServer scripts during rpm -U by moving the directory where it thinks # they are saved. if [ -d /usr/lib/openvswitch/xs-original ]; then mkdir -p /usr/lib/openvswitch/xs-saved mv /usr/lib/openvswitch/xs-original/* /usr/lib/openvswitch/xs-saved/ && rmdir /usr/lib/openvswitch/xs-original fi # Replace XenServer files by our versions. mkdir -p /usr/lib/openvswitch/xs-saved \ || printf "Could not create script backup directory.\n" for f in $replace_files; do s=$(basename "$f") t=$(readlink "$f") if [ -f "$f" ] && [ "$t" != "/usr/share/openvswitch/scripts/$s" ]; then mv "$f" /usr/lib/openvswitch/xs-saved/ \ || printf "Could not save original XenServer $s script\n" ln -s "/usr/share/openvswitch/scripts/$s" "$f" \ || printf "Could not link to Open vSwitch $s script\n" fi done # Clean up dangling symlinks to removed OVS replacement scripts no longer # provided by OVS. Any time a replacement script is removed from OVS, # it should be added here to ensure correct reversion from old versions of # OVS that don't clean up dangling symlinks during the uninstall phase. for orig in /usr/sbin/xen-bugtool $keep_files; do saved=/usr/lib/openvswitch/xs-saved/$(basename "$orig") [ -e "$saved" ] && mv -f "$saved" "$orig" done # Ensure all required services are set to run for s in openvswitch openvswitch-xapi-update; do if chkconfig --list $s >/dev/null 2>&1; then chkconfig --del $s || printf "Could not remove $s init script.\n" fi chkconfig --add $s || printf "Could not add $s init script.\n" chkconfig $s on || printf "Could not enable $s init script.\n" done if [ "$1" = "1" ]; then # $1 = 1 for install # Configure system to use Open vSwitch /opt/xensource/bin/xe-switch-network-backend vswitch else # $1 = 2 for upgrade mode=$(cat /etc/xensource/network.conf) if [ "$mode" != "vswitch" ] && [ "$mode" != "openvswitch" ]; then printf "\nThe server is not configured to run Open vSwitch. To run in\n" printf "vswitch mode, you must run the following command:\n\n" printf "\txe-switch-network-backend vswitch" printf "\n\n" fi fi %posttrans %{module_package} # Ensure that modprobe will find our modules. # # This has to be in %posttrans instead of %post because older versions # installed modules into a different directory and "rpm -U" runs the # new version's %post before removing the old version's files, so if # we use %post then depmod may find the old versions that are about to # be removed. depmod %{xen_version} mode=$(cat /etc/xensource/network.conf) if [ "$mode" = "vswitch" ] || [ "$mode" = "openvswitch" ]; then printf "\nTo use the newly installed Open vSwitch kernel module, you\n" printf "will either have to reboot the hypervisor or follow any\n" printf "workarounds provided by your administration guide. Failure to do\n" printf "so may result in incorrect operation." printf "\n\n" fi %preun if [ "$1" = "0" ]; then # $1 = 0 for uninstall # Configure system to use bridge /opt/xensource/bin/xe-switch-network-backend bridge # The "openvswitch" service should have been removed from # "xe-switch-network-backend bridge". for s in openvswitch openvswitch-xapi-update; do if chkconfig --list $s >/dev/null 2>&1; then chkconfig --del $s || printf "Could not remove $s init script." fi done fi %postun # Restore original XenServer scripts if the OVS equivalent no longer exists. # This works both in the upgrade and erase cases. # This lists every file that every version of OVS has ever replaced. Never # remove old files that OVS no longer replaces, or upgrades from old versions # will fail to restore the XS originals, leaving the system in a broken state. # Also be sure to add removed script paths to the %post scriptlet above to # prevent the same problem when upgrading from old versions of OVS that lack # this restore-on-upgrade logic. for f in \ /etc/xensource/scripts/vif \ /usr/sbin/xen-bugtool \ /opt/xensource/libexec/interface-reconfigure \ /opt/xensource/libexec/InterfaceReconfigure.py \ /opt/xensource/libexec/InterfaceReconfigureBridge.py \ /opt/xensource/libexec/InterfaceReconfigureVswitch.py do # Only revert dangling symlinks. if [ -h "$f" ] && [ ! -e "$f" ]; then s=$(basename "$f") if [ ! -f "/usr/lib/openvswitch/xs-saved/$s" ]; then printf "Original XenServer $s script not present in /usr/lib/openvswitch/xs-saved\n" >&2 printf "Could not restore original XenServer script.\n" >&2 else (rm -f "$f" \ && mv "/usr/lib/openvswitch/xs-saved/$s" "$f") \ || printf "Could not restore original XenServer $s script.\n" >&2 fi fi done if [ "$1" = "0" ]; then # $1 = 0 for uninstall rm -f /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyc \ /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyo rm -f /usr/share/openvswitch/scripts/InterfaceReconfigure.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigure.pyo \ /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyo \ /usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyc \ /usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyo # Remove all configuration files rm -f /etc/openvswitch/conf.db rm -f /etc/sysconfig/openvswitch rm -f /etc/openvswitch/vswitchd.cacert # Remove saved XenServer scripts directory, but only if it's empty rmdir -p /usr/lib/openvswitch/xs-saved 2>/dev/null fi exit 0 %files %defattr(-,root,root) /etc/bash_completion.d/ovs-appctl-bashcomp.bash /etc/bash_completion.d/ovs-vsctl-bashcomp.bash /etc/init.d/openvswitch /etc/init.d/openvswitch-xapi-update /etc/xapi.d/plugins/openvswitch-cfg-update /etc/xensource/bugtool/* /etc/logrotate.d/openvswitch /etc/profile.d/openvswitch.sh /usr/share/openvswitch/python/ /usr/share/openvswitch/bugtool-plugins/* /usr/share/openvswitch/scripts/ovs-check-dead-ifs /usr/share/openvswitch/scripts/ovs-xapi-sync /usr/share/openvswitch/scripts/interface-reconfigure /usr/share/openvswitch/scripts/InterfaceReconfigure.py /usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py /usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py /usr/share/openvswitch/scripts/vif /usr/share/openvswitch/scripts/sysconfig.template /usr/share/openvswitch/scripts/ovs-bugtool-* /usr/share/openvswitch/scripts/ovs-save /usr/share/openvswitch/scripts/ovs-ctl /usr/share/openvswitch/scripts/ovs-lib /usr/share/openvswitch/scripts/ovs-vtep /usr/share/openvswitch/vswitch.ovsschema /usr/share/openvswitch/vtep.ovsschema /usr/sbin/ovs-bugtool /usr/sbin/ovs-vlan-bug-workaround /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server /usr/bin/ovs-appctl /usr/bin/ovs-dpctl /usr/bin/ovs-dpctl-top /usr/bin/ovs-docker /usr/bin/ovs-ofctl /usr/bin/ovs-parse-backtrace /usr/bin/ovs-pcap /usr/bin/ovs-tcpundump /usr/bin/ovs-vlan-test /usr/bin/ovs-vsctl /usr/bin/ovsdb-client /usr/bin/ovsdb-tool /usr/bin/vtep-ctl /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py /usr/share/man/man1/ovsdb-client.1.gz /usr/share/man/man1/ovsdb-server.1.gz /usr/share/man/man1/ovsdb-tool.1.gz /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz /usr/share/man/man5/vtep.5.gz /usr/share/man/man8/ovs-appctl.8.gz /usr/share/man/man8/ovs-bugtool.8.gz /usr/share/man/man8/ovs-ctl.8.gz /usr/share/man/man8/ovs-dpctl.8.gz /usr/share/man/man8/ovs-dpctl-top.8.gz /usr/share/man/man8/ovs-ofctl.8.gz /usr/share/man/man8/ovs-parse-backtrace.8.gz /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man8/ovs-vlan-bug-workaround.8.gz /usr/share/man/man8/ovs-vlan-test.8.gz /usr/share/man/man8/ovs-vsctl.8.gz /usr/share/man/man8/ovs-vswitchd.8.gz /usr/share/man/man8/vtep-ctl.8.gz /var/lib/openvswitch /var/log/openvswitch %exclude /usr/lib/xsconsole/plugins-base/*.py[co] %exclude /usr/share/openvswitch/scripts/*.py[co] %exclude /usr/share/openvswitch/python/*.py[co] %exclude /usr/share/openvswitch/python/ovs/*.py[co] %exclude /usr/share/openvswitch/python/ovs/db/*.py[co] %exclude /usr/bin/ovn-* %exclude /usr/share/man/man5/ovn-* %exclude /usr/share/man/man7/ovn-* %exclude /usr/share/man/man8/ovn-* %exclude /usr/share/openvswitch/ovn-* %exclude /usr/share/openvswitch/scripts/ovn-* %files %{module_package} /lib/modules/%{xen_version}/extra/openvswitch/openvswitch.ko /lib/modules/%{xen_version}/extra/openvswitch/vport-*.ko openvswitch-2.5.9/xenserver/PaxHeaders.82075/LICENSE0000644000000000000000000000013213534540071016672 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.209849209 openvswitch-2.5.9/xenserver/LICENSE0000644000175000017500000006517613534540071020377 0ustar00jpettitjpettit00000000000000As a special exception to the GNU Lesser General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Lesser General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License. ------------ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! openvswitch-2.5.9/xenserver/PaxHeaders.82075/opt_xensource_libexec_InterfaceReconfigureVswitch.py0000644000000000000000000000013013534540071030346 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.229849356 openvswitch-2.5.9/xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py0000644000175000017500000006503513534540071032047 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008,2009,2011 Citrix Systems, Inc. # Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # from InterfaceReconfigure import * import os import re import subprocess # # Bare Network Devices -- network devices without IP configuration # def netdev_down(netdev): """Bring down a bare network device""" if not netdev_exists(netdev): log("netdev: down: device %s does not exist, ignoring" % netdev) return run_command(["/sbin/ifconfig", netdev, 'down']) def netdev_up(netdev, mtu=None): """Bring up a bare network device""" if not netdev_exists(netdev): raise Error("netdev: up: device %s does not exist" % netdev) if mtu: mtu = ["mtu", mtu] else: mtu = [] run_command(["/sbin/ifconfig", netdev, 'up'] + mtu) # This is a list of drivers that do support VLAN tx or rx acceleration, but # to which the VLAN bug workaround should not be applied. This could be # because these are known-good drivers (that is, they do not have any of # the bugs that the workaround avoids) or because the VLAN bug workaround # will not work for them and may cause other problems. # # This is a very short list because few drivers have been tested. NO_VLAN_WORKAROUND_DRIVERS = ( "bonding", ) def netdev_get_driver_name(netdev): """Returns the name of the driver for network device 'netdev'""" symlink = '%s/sys/class/net/%s/device/driver' % (root_prefix(), netdev) try: target = os.readlink(symlink) except OSError, e: log("%s: could not read netdev's driver name (%s)" % (netdev, e)) return None slash = target.rfind('/') if slash < 0: log("target %s of symbolic link %s does not contain slash" % (target, symlink)) return None return target[slash + 1:] def netdev_get_features(netdev): """Returns the features bitmap for the driver for 'netdev'. The features bitmap is a set of NETIF_F_ flags supported by its driver.""" try: features = open("%s/sys/class/net/%s/features" % (root_prefix(), netdev)).read().strip() return int(features, 0) except: return 0 # interface prolly doesn't exist def netdev_has_vlan_accel(netdev): """Returns True if 'netdev' supports VLAN acceleration, False otherwise.""" NETIF_F_HW_VLAN_TX = 128 NETIF_F_HW_VLAN_RX = 256 NETIF_F_VLAN = NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX return (netdev_get_features(netdev) & NETIF_F_VLAN) != 0 # # PIF miscellanea # def pif_currently_in_use(pif): """Determine if a PIF is currently in use. A PIF is determined to be currently in use if - PIF.currently-attached is true - Any bond master is currently attached - Any VLAN master is currently attached """ rec = db().get_pif_record(pif) if rec['currently_attached']: log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif))) return True for b in pif_get_bond_masters(pif): if pif_currently_in_use(b): log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b))) return True for v in pif_get_vlan_masters(pif): if pif_currently_in_use(v): log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v))) return True return False # # Datapath Configuration # def pif_datapath(pif): """Return the datapath PIF associated with PIF. A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has no datapath PIF at all. A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF. """ if pif_is_vlan(pif): return pif_datapath(pif_get_vlan_slave(pif)) pifrec = db().get_pif_record(pif) nwrec = db().get_network_record(pifrec['network']) if not nwrec['bridge']: return None else: return pif def datapath_get_physical_pifs(pif): """Return the PIFs for the physical network device(s) associated with a datapath PIF. For a bond master PIF, these are the bond slave PIFs. For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF. A VLAN PIF cannot be a datapath PIF. """ if pif_is_tunnel(pif): return [] elif pif_is_vlan(pif): # Seems like overkill... raise Error("get-physical-pifs should not get passed a VLAN") elif pif_is_bond(pif): return pif_get_bond_slaves(pif) else: return [pif] def datapath_deconfigure_physical(netdev): return ['--', '--with-iface', '--if-exists', 'del-port', netdev] def vsctl_escape(s): if s.isalnum(): return s def escape(match): c = match.group(0) if c == '\0': raise Error("strings may not contain null bytes") elif c == '\\': return r'\\' elif c == '\n': return r'\n' elif c == '\r': return r'\r' elif c == '\t': return r'\t' elif c == '\b': return r'\b' elif c == '\a': return r'\a' else: return r'\x%02x' % ord(c) return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"' def datapath_configure_tunnel(pif): pass def datapath_configure_bond(pif,slaves): bridge = pif_bridge_name(pif) pifrec = db().get_pif_record(pif) interface = pif_netdev_name(pif) argv = ['--', '--fake-iface', 'add-bond', bridge, interface] for slave in slaves: argv += [pif_netdev_name(slave)] # Bonding options. bond_options = { "mode": "balance-slb", "miimon": "100", "downdelay": "200", "updelay": "31000", "use_carrier": "1", "hashing-algorithm": "src_mac", } # override defaults with values from other-config whose keys # being with "bond-" oc = pifrec['other_config'] overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items()) overrides = map(lambda (key,val): (key[5:], val), overrides) bond_options.update(overrides) mode = None halgo = None argv += ['--', 'set', 'Port', interface] if pifrec['MAC'] != "": argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])] for (name,val) in bond_options.items(): if name in ['updelay', 'downdelay']: # updelay and downdelay have dedicated schema columns. # The value must be a nonnegative integer. try: value = int(val) if value < 0: raise ValueError argv += ['bond_%s=%d' % (name, value)] except ValueError: log("bridge %s has invalid %s '%s'" % (bridge, name, value)) elif name in ['miimon', 'use_carrier']: try: value = int(val) if value < 0: raise ValueError if name == 'use_carrier': if value: value = "carrier" else: value = "miimon" argv += ["other-config:bond-detect-mode=%s" % value] else: argv += ["other-config:bond-miimon-interval=%d" % value] except ValueError: log("bridge %s has invalid %s '%s'" % (bridge, name, value)) elif name == "mode": mode = val elif name == "hashing-algorithm": halgo = val else: # Pass other bond options into other_config. argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name), vsctl_escape(val))] if mode == 'lacp': argv += ['lacp=active'] if halgo == 'src_mac': argv += ['bond_mode=balance-slb'] elif halgo == "tcpudp_ports": argv += ['bond_mode=balance-tcp'] else: log("bridge %s has invalid bond-hashing-algorithm '%s'" % (bridge, halgo)) argv += ['bond_mode=balance-slb'] elif mode in ['balance-slb', 'active-backup']: argv += ['lacp=off', 'bond_mode=%s' % mode] else: log("bridge %s has invalid bond-mode '%s'" % (bridge, mode)) argv += ['lacp=off', 'bond_mode=balance-slb'] return argv def datapath_deconfigure_bond(netdev): return ['--', '--with-iface', '--if-exists', 'del-port', netdev] def datapath_deconfigure_ipdev(interface): return ['--', '--with-iface', '--if-exists', 'del-port', interface] def datapath_modify_config(commands): #log("modifying configuration:") #for c in commands: # log(" %s" % c) rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20'] + [c for c in commands if not c.startswith('#')]) if not rc: raise Error("Failed to modify vswitch configuration") return True # # Toplevel Datapath Configuration. # def configure_datapath(pif): """Bring up the configuration for 'pif', which must not be a VLAN PIF, by: - Tearing down other PIFs that use the same physical devices as 'pif'. - Ensuring that 'pif' itself is set up. - *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs on top of 'pif'. Returns a tuple containing - A list containing the necessary vsctl command line arguments - A list of additional devices which should be brought up after the configuration is applied. - A list containing flows to apply to the pif bridge, note that port numbers may need to be substituted once ofport is known """ vsctl_argv = [] extra_up_ports = [] bridge_flows = [] assert not pif_is_vlan(pif) bridge = pif_bridge_name(pif) physical_devices = datapath_get_physical_pifs(pif) vsctl_argv += ['## configuring datapath %s' % bridge] # Determine additional devices to deconfigure. # # Given all physical devices which are part of this PIF we need to # consider: # - any additional bond which a physical device is part of. # - any additional physical devices which are part of an additional bond. # # Any of these which are not currently in use should be brought # down and deconfigured. extra_down_bonds = [] extra_down_ports = [] for p in physical_devices: for bond in pif_get_bond_masters(p): if bond == pif: log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond)) continue if bond in extra_down_bonds: continue if db().get_pif_record(bond)['currently_attached']: log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond)) extra_down_bonds += [bond] for s in pif_get_bond_slaves(bond): if s in physical_devices: continue if s in extra_down_ports: continue if pif_currently_in_use(s): continue extra_down_ports += [s] log("configure_datapath: bridge - %s" % bridge) log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices]) log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports]) log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds]) # Need to fully deconfigure any bridge which any of the: # - physical devices # - bond devices # - sibling devices # refers to for brpif in physical_devices + extra_down_ports + extra_down_bonds: if brpif == pif: continue b = pif_bridge_name(brpif) #ifdown(b) # XXX netdev_down(b) vsctl_argv += ['# remove bridge %s' % b] vsctl_argv += ['--', '--if-exists', 'del-br', b] for n in extra_down_ports: dev = pif_netdev_name(n) vsctl_argv += ['# deconfigure sibling physical device %s' % dev] vsctl_argv += datapath_deconfigure_physical(dev) netdev_down(dev) for n in extra_down_bonds: dev = pif_netdev_name(n) vsctl_argv += ['# deconfigure bond device %s' % dev] vsctl_argv += datapath_deconfigure_bond(dev) netdev_down(dev) for p in physical_devices: dev = pif_netdev_name(p) vsctl_argv += ['# deconfigure physical port %s' % dev] vsctl_argv += datapath_deconfigure_physical(dev) vsctl_argv += ['--', '--may-exist', 'add-br', bridge] if len(physical_devices) > 1: vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)] vsctl_argv += datapath_configure_bond(pif, physical_devices) extra_up_ports += [pif_netdev_name(pif)] elif len(physical_devices) == 1: iface = pif_netdev_name(physical_devices[0]) vsctl_argv += ['# add physical device %s' % iface] vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface] elif pif_is_tunnel(pif): datapath_configure_tunnel(pif) vsctl_argv += ['# configure Bridge MAC'] vsctl_argv += ['--', 'set', 'Bridge', bridge, 'other-config:hwaddr=%s' % vsctl_escape(db().get_pif_record(pif)['MAC'])] pool = db().get_pool_record() network = db().get_network_by_bridge(bridge) network_rec = None fail_mode = None valid_fail_modes = ['standalone', 'secure'] if network: network_rec = db().get_network_record(network) fail_mode = network_rec['other_config'].get('vswitch-controller-fail-mode') if (fail_mode not in valid_fail_modes) and pool: fail_mode = pool['other_config'].get('vswitch-controller-fail-mode') # Add default flows to allow management traffic if fail-mode # transitions to secure based on pool fail-mode setting if fail_mode == 'secure' and db().get_pif_record(pif).get('management', False): prev_fail_mode = vswitchCfgQuery(['get-fail-mode', bridge]) if prev_fail_mode != 'secure': tp = 'idle_timeout=0,priority=0' host_mgmt_mac = db().get_pif_record(pif)['MAC'] # account for bond as management interface if len(physical_devices) > 1: bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)] bridge_flows += ['%s,in_port=local,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)] # we don't know slave ofports yet, substitute later bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)] bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)] else: bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)] bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)] bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)] bridge_flows += ['%s,in_port=local,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)] if fail_mode not in valid_fail_modes: fail_mode = 'standalone' vsctl_argv += ['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode] if network_rec: dib = network_rec['other_config'].get('vswitch-disable-in-band') if not dib: vsctl_argv += ['--', 'remove', 'Bridge', bridge, 'other_config', 'disable-in-band'] elif dib in ['true', 'false']: vsctl_argv += ['--', 'set', 'Bridge', bridge, 'other_config:disable-in-band=' + dib] else: log('"' + dib + '"' "isn't a valid setting for other_config:disable-in-band on " + bridge) vsctl_argv += set_br_external_ids(pif) vsctl_argv += ['## done configuring datapath %s' % bridge] return vsctl_argv,extra_up_ports,bridge_flows def deconfigure_bridge(pif): vsctl_argv = [] bridge = pif_bridge_name(pif) log("deconfigure_bridge: bridge - %s" % bridge) vsctl_argv += ['# deconfigure bridge %s' % bridge] vsctl_argv += ['--', '--if-exists', 'del-br', bridge] return vsctl_argv def set_br_external_ids(pif): pifrec = db().get_pif_record(pif) dp = pif_datapath(pif) dprec = db().get_pif_record(dp) xs_network_uuids = [] for nwpif in db().get_pifs_by_device(pifrec['device']): rec = db().get_pif_record(nwpif) # When state is read from dbcache PIF.currently_attached # is always assumed to be false... Err on the side of # listing even detached networks for the time being. #if nwpif != pif and not rec['currently_attached']: # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid'])) # continue nwrec = db().get_network_record(rec['network']) uuid = nwrec['uuid'] if pif_is_vlan(nwpif): xs_network_uuids.append(uuid) else: xs_network_uuids.insert(0, uuid) vsctl_argv = [] vsctl_argv += ['# configure xs-network-uuids'] vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif), 'xs-network-uuids', ';'.join(xs_network_uuids)] return vsctl_argv # # # class DatapathVswitch(Datapath): def __init__(self, pif): Datapath.__init__(self, pif) self._dp = pif_datapath(pif) self._ipdev = pif_ipdev_name(pif) self._bridge_flows = [] if pif_is_vlan(pif) and not self._dp: raise Error("Unbridged VLAN devices not implemented yet") log("Configured for Vswitch datapath") @classmethod def rewrite(cls): if not os.path.exists("/var/run/openvswitch/db.sock"): # ovsdb-server is not running, so we can't update the database. # Probably we are being called as part of system shutdown. Just # skip the update, since the external-ids will be updated on the # next boot anyhow. return vsctl_argv = [] for pif in db().get_all_pifs(): pifrec = db().get_pif_record(pif) if not pif_is_vlan(pif) and pifrec['currently_attached']: vsctl_argv += set_br_external_ids(pif) if vsctl_argv != []: datapath_modify_config(vsctl_argv) def configure_ipdev(self, cfg): cfg.write("TYPE=Ethernet\n") def preconfigure(self, parent): vsctl_argv = [] extra_ports = [] bridge_flows = [] pifrec = db().get_pif_record(self._pif) dprec = db().get_pif_record(self._dp) ipdev = self._ipdev c,e,f = configure_datapath(self._dp) bridge = pif_bridge_name(self._pif) vsctl_argv += c extra_ports += e bridge_flows += f dpname = pif_bridge_name(self._dp) if pif_is_vlan(self._pif): # In some cases XAPI may misguidedly leave an instance of # 'bridge' which should be deleted. vsctl_argv += ['--', '--if-exists', 'del-br', bridge] # configure_datapath() set up the underlying datapath bridge. # Stack a VLAN bridge on top of it. vsctl_argv += ['--', '--may-exist', 'add-br', bridge, dpname, pifrec['VLAN']] vsctl_argv += set_br_external_ids(self._pif) if ipdev != bridge: vsctl_argv += ["# deconfigure ipdev %s" % ipdev] vsctl_argv += datapath_deconfigure_ipdev(ipdev) vsctl_argv += ["# reconfigure ipdev %s" % ipdev] vsctl_argv += ['--', 'add-port', bridge, ipdev] if ipdev != dpname: vsctl_argv += ['# configure Interface MAC'] vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(self._pif), 'MAC=%s' % vsctl_escape(dprec['MAC'])] self._vsctl_argv = vsctl_argv self._extra_ports = extra_ports self._bridge_flows = bridge_flows def bring_down_existing(self): # interface-reconfigure is never explicitly called to down a # bond master. However, when we are called to up a slave it # is implicit that we are destroying the master. Conversely, # when we are called to up a bond is is implicit that we are # taking down the slaves. # # This is (only) important in the case where the device being # implicitly taken down uses DHCP. We need to kill the # dhclient process, otherwise performing the inverse operation # later later will fail because ifup will refuse to start a # duplicate dhclient. bond_masters = pif_get_bond_masters(self._pif) for master in bond_masters: log("action_up: bring down bond master %s" % (pif_netdev_name(master))) run_command(["/sbin/ifdown", pif_bridge_name(master)]) bond_slaves = pif_get_bond_slaves(self._pif) for slave in bond_slaves: log("action_up: bring down bond slave %s" % (pif_netdev_name(slave))) run_command(["/sbin/ifdown", pif_bridge_name(slave)]) def configure(self): # Bring up physical devices. ovs-vswitchd initially enables or # disables bond slaves based on whether carrier is detected # when they are added, and a network device that is down # always reports "no carrier". physical_devices = datapath_get_physical_pifs(self._dp) if pif_is_bond(self._dp): brec = db().get_pif_record(self._dp) bond_mtu = mtu_setting(brec['network'], "PIF", brec['other_config']) else: bond_mtu = None for p in physical_devices: prec = db().get_pif_record(p) oc = prec['other_config'] dev = pif_netdev_name(p) if bond_mtu: mtu = bond_mtu else: mtu = mtu_setting(prec['network'], "PIF", oc) netdev_up(dev, mtu) settings, offload = ethtool_settings(oc, PIF_OTHERCONFIG_DEFAULTS) if len(settings): run_command(['/sbin/ethtool', '-s', dev] + settings) if len(offload): run_command(['/sbin/ethtool', '-K', dev] + offload) driver = netdev_get_driver_name(dev) if 'vlan-bug-workaround' in oc: vlan_bug_workaround = oc['vlan-bug-workaround'] == 'true' elif driver in NO_VLAN_WORKAROUND_DRIVERS: vlan_bug_workaround = False else: vlan_bug_workaround = netdev_has_vlan_accel(dev) if vlan_bug_workaround: setting = 'on' else: setting = 'off' run_command(['/usr/sbin/ovs-vlan-bug-workaround', dev, setting]) datapath_modify_config(self._vsctl_argv) if self._bridge_flows: ofports = [] physical_devices = datapath_get_physical_pifs(self._dp) if len(physical_devices) > 1: for slave in physical_devices: name = pif_netdev_name(slave) ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport']) ofports.append(ofport) else: name = pif_netdev_name(self._dp) ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport']) ofports.append(ofport) dpname = pif_bridge_name(self._dp) for flow in self._bridge_flows: if flow.find('in_port=%s') != -1 or flow.find('actions=%s') != -1: for port in ofports: f = flow % (port) run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, f]) else: run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, flow]) def post(self): for p in self._extra_ports: log("action_up: bring up %s" % p) netdev_up(p) def bring_down(self): vsctl_argv = [] dp = self._dp ipdev = self._ipdev bridge = pif_bridge_name(dp) log("deconfigure ipdev %s on %s" % (ipdev,bridge)) vsctl_argv += ["# deconfigure ipdev %s" % ipdev] vsctl_argv += datapath_deconfigure_ipdev(ipdev) if pif_is_vlan(self._pif): # Delete the VLAN bridge. vsctl_argv += deconfigure_bridge(self._pif) # If the VLAN's slave is attached, leave datapath setup. slave = pif_get_vlan_slave(self._pif) if db().get_pif_record(slave)['currently_attached']: log("action_down: vlan slave is currently attached") dp = None # If the VLAN's slave has other VLANs that are attached, leave datapath setup. for master in pif_get_vlan_masters(slave): if master != self._pif and db().get_pif_record(master)['currently_attached']: log("action_down: vlan slave has other master: %s" % pif_netdev_name(master)) dp = None # Otherwise, take down the datapath too (fall through) if dp: log("action_down: no more masters, bring down slave %s" % bridge) else: # Stop here if this PIF has attached VLAN masters. masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']] if len(masters) > 0: log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters)) dp = None if dp: vsctl_argv += deconfigure_bridge(dp) physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)] log("action_down: bring down physical devices - %s" % physical_devices) for p in physical_devices: netdev_down(p) datapath_modify_config(vsctl_argv) # # utility methods # def vswitchCfgQuery(action_args): cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(), '-vconsole:off'] + action_args output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate() if len(output) == 0 or output[0] == None: output = "" else: output = output[0].strip() return output openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_profile.d_openvswitch.sh0000644000000000000000000000013213534540071023367 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.217849268 openvswitch-2.5.9/xenserver/etc_profile.d_openvswitch.sh0000644000175000017500000000231013534540071025051 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. alias vswitch='service openvswitch' alias openvswitch='service openvswitch' function watchdp { watch ovs-dpctl show "$@" } function watchdpflows { local grep="" local dp=$1 shift if [ $# -gt 0 ]; then grep="| grep $@" fi watch "ovs-dpctl dump-flows $dp $grep" } function watchflows { local grep="" local dp=$1 shift bridge=$(ovs-dpctl show $dp | grep 'port 0:' | cut -d' ' -f 3) if [ $# -gt 0 ]; then grep="| grep $@" fi watch "ovs-ofctl dump-flows unix:/var/run/$bridge.mgmt $grep" } function monitorlogs { local grep="" if [ $# -gt 0 ]; then grep="| grep --line-buffered '^==> .* <==$" for i in "$@"; do grep="$grep\|$i" done grep="$grep'" fi cmd="tail -F /var/log/messages /var/log/openvswitch/ovs-vswitchd.log /var/log/openvswitch/ovsdb-server /var/log/xensource.log $grep | tee /var/log/monitorlogs.out" printf "cmd: $cmd\n" eval "$cmd" } openvswitch-2.5.9/xenserver/PaxHeaders.82075/opt_xensource_libexec_InterfaceReconfigureBridge.py0000644000000000000000000000013013534540071030113 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.229849356 openvswitch-2.5.9/xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py0000644000175000017500000003613413534540071031612 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008,2009 Citrix Systems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; version 2.1 only. with the special # exception on linking described in file LICENSE. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # from InterfaceReconfigure import * import sys import time sysfs_bonding_masters = root_prefix() + "/sys/class/net/bonding_masters" def open_pif_ifcfg(pif): pifrec = db().get_pif_record(pif) interface = pif_netdev_name(pif) log("Configuring %s (%s)" % (interface, pifrec['MAC'])) f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), interface)) f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ (os.path.basename(f.path()), os.path.basename(sys.argv[0]))) f.write("XEMANAGED=yes\n") f.write("DEVICE=%s\n" % interface) f.write("ONBOOT=no\n") return f # # Bare Network Devices -- network devices without IP configuration # def netdev_down(netdev): """Bring down a bare network device""" if not netdev_exists(netdev): log("netdev: down: device %s does not exist, ignoring" % netdev) return run_command(["/sbin/ifdown", netdev]) def netdev_up(netdev, mtu=None): """Bring up a bare network device""" #if not netdev_exists(netdev): # raise Error("netdev: up: device %s does not exist" % netdev) run_command(["/sbin/ifup", netdev]) # # Bonding driver # def load_bonding_driver(): log("Loading bonding driver") run_command(["/sbin/modprobe", "bonding"]) try: # bond_device_exists() uses the contents of sysfs_bonding_masters to work out which devices # have already been created. Unfortunately the driver creates "bond0" automatically at # modprobe init. Get rid of this now or our accounting will go wrong. f = open(sysfs_bonding_masters, "w") f.write("-bond0") f.close() except IOError, e: log("Failed to load bonding driver: %s" % e) def bonding_driver_loaded(): lines = open(root_prefix() + "/proc/modules").read().split("\n") modules = [line.split(" ")[0] for line in lines] return "bonding" in modules def bond_device_exists(name): f = open(sysfs_bonding_masters, "r") bonds = f.readline().split() f.close() return name in bonds def __create_bond_device(name): if not bonding_driver_loaded(): load_bonding_driver() if bond_device_exists(name): log("bond master %s already exists, not creating" % name) else: log("Creating bond master %s" % name) try: f = open(sysfs_bonding_masters, "w") f.write("+" + name) f.close() except IOError, e: log("Failed to create %s: %s" % (name, e)) def create_bond_device(pif): """Ensures that a bond master device exists in the kernel.""" if not pif_is_bond(pif): return __create_bond_device(pif_netdev_name(pif)) def __destroy_bond_device(name): if bond_device_exists(name): retries = 10 # 10 * 0.5 seconds while retries > 0: retries = retries - 1 log("Destroying bond master %s (%d attempts remain)" % (name,retries)) try: f = open(sysfs_bonding_masters, "w") f.write("-" + name) f.close() retries = 0 except IOError, e: time.sleep(0.5) else: log("bond master %s does not exist, not destroying" % name) def destroy_bond_device(pif): """No, Mr. Bond, I expect you to die.""" pifrec = db().get_pif_record(pif) if not pif_is_bond(pif): return # If the bonding module isn't loaded then do nothing. if not os.access(sysfs_bonding_masters, os.F_OK): return name = pif_netdev_name(pif) __destroy_bond_device(name) # # Bring Interface up/down. # def bring_down_interface(pif, destroy=False): """Bring down the interface associated with PIF. Brings down the given interface as well as any physical interfaces which are bond slaves of this one. This is because they will be required when the bond is brought up.""" def destroy_bridge(pif): """Bring down the bridge associated with a PIF.""" #if not pif_is_bridged(pif): # return bridge = pif_bridge_name(pif) if not netdev_exists(bridge): log("destroy_bridge: bridge %s does not exist, ignoring" % bridge) return log("Destroy bridge %s" % bridge) netdev_down(bridge) run_command(["/usr/sbin/brctl", "delbr", bridge]) def destroy_vlan(pif): vlan = pif_netdev_name(pif) if not netdev_exists(vlan): log("vconfig del: vlan %s does not exist, ignoring" % vlan) return log("Destroy vlan device %s" % vlan) run_command(["/sbin/vconfig", "rem", vlan]) if pif_is_vlan(pif): interface = pif_netdev_name(pif) log("bring_down_interface: %s is a VLAN" % interface) netdev_down(interface) if destroy: destroy_vlan(pif) destroy_bridge(pif) else: return slave = pif_get_vlan_slave(pif) if db().get_pif_record(slave)['currently_attached']: log("bring_down_interface: vlan slave is currently attached") return masters = pif_get_vlan_masters(slave) masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']] if len(masters) > 0: log("bring_down_interface: vlan slave has other masters") return log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave)) pif = slave else: vlan_masters = pif_get_vlan_masters(pif) log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters])) if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0: log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif)) return # pif is now either a bond or a physical device which needs to be brought down # Need to bring down bond slaves first since the bond device # must be up to enslave/unenslave. bond_slaves = pif_get_bond_slaves_sorted(pif) log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves])) for slave in bond_slaves: slave_interface = pif_netdev_name(slave) if db().get_pif_record(slave)['currently_attached']: log("leave bond slave %s up (currently attached)" % slave_interface) continue log("bring down bond slave %s" % slave_interface) netdev_down(slave_interface) # Also destroy the bridge associated with the slave, since # it will carry the MAC address and possibly an IP address # leading to confusion. destroy_bridge(slave) interface = pif_netdev_name(pif) log("Bring interface %s down" % interface) netdev_down(interface) if destroy: destroy_bond_device(pif) destroy_bridge(pif) def interface_is_up(pif): try: interface = pif_netdev_name(pif) state = open("%s/sys/class/net/%s/operstate" % (root_prefix(), interface)).read().strip() return state == "up" except: return False # interface prolly doesn't exist def bring_up_interface(pif): """Bring up the interface associated with a PIF. Also bring up the interfaces listed in additional. """ # VLAN on bond seems to need bond brought up explicitly, but VLAN # on normal device does not. Might as well always bring it up. if pif_is_vlan(pif): slave = pif_get_vlan_slave(pif) if not interface_is_up(slave): bring_up_interface(slave) interface = pif_netdev_name(pif) create_bond_device(pif) log("Bring interface %s up" % interface) netdev_up(interface) # # Datapath topology configuration. # def _configure_physical_interface(pif): """Write the configuration for a physical interface. Writes the configuration file for the physical interface described by the pif object. Returns the open file handle for the interface configuration file. """ pifrec = db().get_pif_record(pif) log("Configuring physical interface %s" % pifrec['device']) f = open_pif_ifcfg(pif) f.write("TYPE=Ethernet\n") f.write("HWADDR=%(MAC)s\n" % pifrec) settings,offload = ethtool_settings(pifrec['other_config'], PIF_OTHERCONFIG_DEFAULTS) if len(settings): f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) if len(offload): f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) mtu = mtu_setting(pifrec['network'], "PIF", pifrec['other_config']) if mtu: f.write("MTU=%s\n" % mtu) return f def pif_get_bond_slaves_sorted(pif): pifrec = db().get_pif_record(pif) # build a list of slave's pifs slave_pifs = pif_get_bond_slaves(pif) # Ensure any currently attached slaves are listed in the opposite order to the order in # which they were attached. The first slave attached must be the last detached since # the bond is using its MAC address. try: attached_slaves = open("%s/sys/class/net/%s/bonding/slaves" % (root_prefix(), pifrec['device'])).readline().split() for slave in attached_slaves: pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)] slave_pif = pifs[0] slave_pifs.remove(slave_pif) slave_pifs.insert(0, slave_pif) except IOError: pass return slave_pifs def _configure_bond_interface(pif): """Write the configuration for a bond interface. Writes the configuration file for the bond interface described by the pif object. Handles writing the configuration for the slave interfaces. Returns the open file handle for the bond interface configuration file. """ pifrec = db().get_pif_record(pif) f = open_pif_ifcfg(pif) if pifrec['MAC'] != "": f.write("MACADDR=%s\n" % pifrec['MAC']) for slave in pif_get_bond_slaves(pif): s = _configure_physical_interface(slave) s.write("MASTER=%(device)s\n" % pifrec) s.write("SLAVE=yes\n") s.close() f.attach_child(s) settings,offload = ethtool_settings(pifrec['other_config']) if len(settings): f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) if len(offload): f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) mtu = mtu_setting(pifrec['network'], "Bond-PIF", pifrec['other_config']) if mtu: f.write("MTU=%s\n" % mtu) # The bond option defaults bond_options = { "mode": "balance-slb", "miimon": "100", "downdelay": "200", "updelay": "31000", "use_carrier": "1", "hashing-algorithm": "src_mac", } # override defaults with values from other-config whose keys being with "bond-" oc = pifrec['other_config'] overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items()) overrides = map(lambda (key,val): (key[5:], val), overrides) bond_options.update(overrides) # write the bond options to ifcfg-bondX f.write('BONDING_OPTS="') for (name,val) in bond_options.items(): f.write("%s=%s " % (name,val)) f.write('"\n') return f def _configure_vlan_interface(pif): """Write the configuration for a VLAN interface. Writes the configuration file for the VLAN interface described by the pif object. Handles writing the configuration for the master interface if necessary. Returns the open file handle for the VLAN interface configuration file. """ slave = _configure_pif(pif_get_vlan_slave(pif)) pifrec = db().get_pif_record(pif) f = open_pif_ifcfg(pif) f.write("VLAN=yes\n") settings,offload = ethtool_settings(pifrec['other_config']) if len(settings): f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) if len(offload): f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) mtu = mtu_setting(pifrec['network'], "VLAN-PIF", pifrec['other_config']) if mtu: f.write("MTU=%s\n" % mtu) f.attach_child(slave) return f def _configure_pif(pif): """Write the configuration for a PIF object. Writes the configuration file the PIF and all dependent interfaces (bond slaves and VLAN masters etc). Returns the open file handle for the interface configuration file. """ if pif_is_vlan(pif): f = _configure_vlan_interface(pif) elif pif_is_bond(pif): f = _configure_bond_interface(pif) else: f = _configure_physical_interface(pif) f.write("BRIDGE=%s\n" % pif_bridge_name(pif)) f.close() return f # # # class DatapathBridge(Datapath): def __init__(self, pif): if pif_is_tunnel(pif): raise Error("Tunnel PIFs are not supported in Bridge mode") Datapath.__init__(self, pif) log("Configured for Bridge datapath") def configure_ipdev(self, cfg): if pif_is_bridged(self._pif): cfg.write("TYPE=Bridge\n") cfg.write("DELAY=0\n") cfg.write("STP=off\n") cfg.write("PIFDEV=%s\n" % pif_netdev_name(self._pif)) else: cfg.write("TYPE=Ethernet\n") def preconfigure(self, parent): pf = _configure_pif(self._pif) parent.attach_child(pf) def bring_down_existing(self): # Bring down any VLAN masters so that we can reconfigure the slave. for master in pif_get_vlan_masters(self._pif): name = pif_netdev_name(master) log("action_up: bring down vlan master %s" % (name)) netdev_down(name) # interface-reconfigure is never explicitly called to down a bond master. # However, when we are called to up a slave it is implicit that we are destroying the master. bond_masters = pif_get_bond_masters(self._pif) for master in bond_masters: log("action_up: bring down bond master %s" % (pif_netdev_name(master))) # bring down master bring_down_interface(master, destroy=True) # No masters left - now its safe to reconfigure the slave. bring_down_interface(self._pif) def configure(self): bring_up_interface(self._pif) def post(self): # Bring back any currently-attached VLAN masters for master in [v for v in pif_get_vlan_masters(self._pif) if db().get_pif_record(v)['currently_attached']]: name = pif_netdev_name(master) log("action_up: bring up %s" % (name)) netdev_up(name) def bring_down(self): bring_down_interface(self._pif, destroy=True) openvswitch-2.5.9/xenserver/PaxHeaders.82075/usr_share_openvswitch_scripts_sysconfig.template0000644000000000000000000000013013534540071027677 xustar0028 mtime=1567801401.9776854 30 atime=1567801402.141686605 30 ctime=1567801424.233849386 openvswitch-2.5.9/xenserver/usr_share_openvswitch_scripts_sysconfig.template0000644000175000017500000000155213534540071031372 0ustar00jpettitjpettit00000000000000### Configuration options for openvswitch # Copyright (C) 2009, 2010, 2011 Nicira, Inc. # FORCE_COREFILES: If 'yes' then core files will be enabled. # FORCE_COREFILES=yes # OVSDB_SERVER_PRIORITY: "nice" priority at which to run ovsdb-server. # # OVSDB_SERVER_PRIORITY=-10 # VSWITCHD_PRIORITY: "nice" priority at which to run ovs-vswitchd. # VSWITCHD_PRIORITY=-10 # VSWITCHD_MLOCKALL: Whether to pass ovs-vswitchd the --mlockall option. # This option should be set to "yes" or "no". The default is "yes". # Enabling this option can avoid networking interruptions due to # system memory pressure in extraordinary situations, such as multiple # concurrent VM import operations. # VSWITCHD_MLOCKALL=yes # OVS_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, # a suitable place to specify --ovs-vswitchd-wrapper=valgrind. # OVS_CTL_OPTS= openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_xapi.d_plugins_openvswitch-cfg-update0000644000000000000000000000013213534540071026035 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.217849268 openvswitch-2.5.9/xenserver/etc_xapi.d_plugins_openvswitch-cfg-update0000755000175000017500000002251713534540071027535 0ustar00jpettitjpettit00000000000000#!/usr/bin/env python # # xapi plugin script to update the cache of configuration items in the # ovs-vswitchd configuration that are managed in the xapi database when # integrated with Citrix management tools. # Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TBD: - error handling needs to be improved. Currently this can leave # TBD: the system in a bad state if anything goes wrong. import XenAPIPlugin import os import subprocess import syslog import re vsctl = '/usr/bin/ovs-vsctl' ofctl = '/usr/bin/ovs-ofctl' cacert_filename = '/etc/openvswitch/vswitchd.cacert' ovsdb_port = '6640' # Delete the CA certificate, so that we go back to boot-strapping mode def delete_cacert(): try: os.remove(cacert_filename) except OSError: # Ignore error if file doesn't exist pass def update(session, args): # Refresh bridge network UUIDs in case this host joined or left a pool. script = '/opt/xensource/libexec/interface-reconfigure' try: retval = subprocess.call([script, 'rewrite']) if retval != 0: syslog.syslog('%s exited with status %d' % (script, retval)) except OSError, e: syslog.syslog('%s: failed to execute (%s)' % (script, e.strerror)) pools = session.xenapi.pool.get_all() # We assume there is only ever one pool... if len(pools) == 0: raise XenAPIPlugin.Failure('NO_POOL_FOR_HOST', []) if len(pools) > 1: raise XenAPIPlugin.Failure('MORE_THAN_ONE_POOL_FOR_HOST', []) new_controller = False pool = session.xenapi.pool.get_record(pools[0]) controller = pool.get('vswitch_controller') ret_str = '' currentControllers = vswitchCurrentControllers() if not controller and currentControllers: delete_cacert() try: emergency_reset(session, None) except: pass removeControllerCfg() ret_str += 'Successfully removed controller config. ' # controller cannot be empty, otherwise, this will always be True. elif controller and controller not in currentControllers: delete_cacert() try: emergency_reset(session, None) except: pass setControllerCfg(controller) new_controller = True ret_str += 'Successfully set controller to %s. ' % controller try: pool_fail_mode = pool['other_config']['vswitch-controller-fail-mode'] except KeyError, e: pool_fail_mode = None bton = {} for rec in session.xenapi.network.get_all_records().values(): try: bton[rec['bridge']] = rec except KeyError: pass # If new controller, get management MAC addresses from XAPI now # in case fail_mode set to secure which may affect XAPI access mgmt_bridge = None host_mgmt_mac = None host_mgmt_device = None pool_mgmt_macs = {} if new_controller: query = 'field "management"="true"' recs = session.xenapi.PIF.get_all_records_where(query) for rec in recs.itervalues(): pool_mgmt_macs[rec.get('MAC')] = rec.get('device') dib_changed = False fail_mode_changed = False for bridge in vswitchCfgQuery(['list-br']).split(): network = bton[bridge] bridge = vswitchCfgQuery(['br-to-parent', bridge]) xapi_dib = network['other_config'].get('vswitch-disable-in-band') if not xapi_dib: xapi_dib = '' ovs_dib = vswitchCfgQuery(['--', '--if-exists', 'get', 'Bridge', bridge, 'other_config:disable-in-band']).strip('"') # Do nothing if setting is invalid, and warn the user. if xapi_dib not in ['true', 'false', '']: ret_str += '"' + xapi_dib + '"' + \ ' is an invalid value for vswitch-disable-in-band on ' + \ bridge + ' ' # Change bridge disable-in-band option if XAPI and OVS states differ. elif xapi_dib != ovs_dib: # 'true' or 'false' if xapi_dib: vswitchCfgMod(['--', 'set', 'Bridge', bridge, 'other_config:disable-in-band=' + xapi_dib]) # '' or None else: vswitchCfgMod(['--', 'remove', 'Bridge', bridge, 'other_config', 'disable-in-band']) dib_changed = True # Change bridge fail_mode if XAPI state differs from OVS state. bridge_fail_mode = vswitchCfgQuery(['get', 'Bridge', bridge, 'fail_mode']).strip('[]"') try: other_config = bton[bridge]['other_config'] fail_mode = other_config['vswitch-controller-fail-mode'] except KeyError, e: fail_mode = None if fail_mode not in ['secure', 'standalone']: fail_mode = pool_fail_mode if fail_mode != 'secure': fail_mode = 'standalone' if bridge_fail_mode != fail_mode: vswitchCfgMod(['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode]) fail_mode_changed = True # Determine local mgmt MAC address if host being added to secure # pool so we can add default flows to allow management traffic if new_controller and fail_mode_changed and pool_fail_mode == 'secure': oc = vswitchCfgQuery(['get', 'Bridge', bridge, 'other-config']) m = re.match('.*hwaddr="([0-9a-fA-F:].*)".*', oc) if m and m.group(1) in pool_mgmt_macs.keys(): mgmt_bridge = bridge host_mgmt_mac = m.group(1) host_mgmt_device = pool_mgmt_macs[host_mgmt_mac] if (host_mgmt_mac is not None and mgmt_bridge is not None and host_mgmt_device is not None): tp = 'idle_timeout=0,priority=0' port = vswitchCfgQuery(['get', 'interface', host_mgmt_device, 'ofport']) addFlow(mgmt_bridge, '%s,in_port=%s,arp,nw_proto=1,actions=local' % (tp, port)) addFlow(mgmt_bridge, '%s,in_port=local,arp,dl_src=%s,actions=%s' % (tp, host_mgmt_mac, port)) addFlow(mgmt_bridge, '%s,in_port=%s,dl_dst=%s,actions=local' % (tp, port, host_mgmt_mac)) addFlow(mgmt_bridge, '%s,in_port=local,dl_src=%s,actions=%s' % (tp, host_mgmt_mac, port)) if dib_changed: ret_str += 'Updated in-band management. ' if fail_mode_changed: ret_str += 'Updated fail_mode. ' if ret_str != '': return ret_str else: return 'No change to configuration' def vswitchCurrentControllers(): controllers = vswitchCfgQuery(['get-manager']) def parse_controller(controller): if controller.startswith('ssl:'): return controller.split(':')[1] return controller.split(':')[0] return [parse_controller(controller) for controller in controllers.split('\n') if controller] def removeControllerCfg(): vswitchCfgMod(['--', 'del-manager', '--', 'del-ssl']) def setControllerCfg(controller): # /etc/xensource/xapi-ssl.pem is mentioned twice below because it # contains both the private key and the certificate. vswitchCfgMod(['--', 'del-manager', '--', 'del-ssl', '--', '--bootstrap', 'set-ssl', '/etc/xensource/xapi-ssl.pem', '/etc/xensource/xapi-ssl.pem', cacert_filename, '--', 'set-manager', 'ssl:' + controller + ':' + ovsdb_port]) def vswitchCfgQuery(action_args): cmd = [vsctl, '-vconsole:off'] + action_args output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate() if len(output) == 0 or output[0] is None: output = '' else: output = output[0].strip() return output def vswitchCfgMod(action_args): cmd = [vsctl, '--timeout=5', '-vconsole:off'] + action_args exitcode = subprocess.call(cmd) if exitcode != 0: raise XenAPIPlugin.Failure('VSWITCH_CONFIG_MOD_FAILURE', [str(exitcode), str(action_args)]) def emergency_reset(session, args): cmd = [vsctl, '--timeout=5', 'emer-reset'] exitcode = subprocess.call(cmd) if exitcode != 0: raise XenAPIPlugin.Failure('VSWITCH_EMER_RESET_FAILURE', [str(exitcode)]) return 'Successfully reset configuration' def addFlow(switch, flow): cmd = [ofctl, 'add-flow', switch, flow] exitcode = subprocess.call(cmd) if exitcode != 0: raise XenAPIPlugin.Failure('VSWITCH_ADD_FLOW_FAILURE', [str(exitcode), str(switch), str(flow)]) if __name__ == '__main__': XenAPIPlugin.dispatch({'update': update, 'emergency_reset': emergency_reset}) openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_init.d_openvswitch-xapi-update0000644000000000000000000000013213534540071024500 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.217849268 openvswitch-2.5.9/xenserver/etc_init.d_openvswitch-xapi-update0000755000175000017500000000417613534540071026201 0ustar00jpettitjpettit00000000000000#!/bin/bash # # openvswitch-xapi-update # # chkconfig: 2345 95 01 # description: Update Open vSwitch configuration from XAPI database at boot # Copyright (C) 2009, 2010 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ### BEGIN INIT INFO # Provides: openvswitch-xapi-update # Required-Start: $network $remote_fs # Required-Stop: $network # Default-Start: 3 5 # Default-Stop: # Short-Description: openvswitch-xapi-update # Description: reconfigures Open vSwitch based on XAPI configuration ### END INIT INFO . /etc/init.d/functions function do_host_call { xe host-call-plugin host-uuid="$INSTALLATION_UUID" plugin="openvswitch-cfg-update" fn="update" >/dev/null } function start { if [ ! -f /etc/xensource-inventory ]; then printf "openvswitch-xapi-update ERROR: XenSource inventory not present in /etc/xensource-inventory\n" exit 1 fi if test -e /etc/xensource/network.conf; then NETWORK_MODE=$(cat /etc/xensource/network.conf) fi case ${NETWORK_MODE:=openvswitch} in vswitch|openvswitch) ;; bridge) exit 0 ;; *) echo "Open vSwitch disabled (/etc/xensource/network.conf is invalid)" >&2 exit 0 ;; esac source /etc/xensource-inventory action "Updating configuration" do_host_call } case "$1" in start) start ;; stop) # Nothing to do here. ;; restart) start ;; help) printf "openvswitch-xapi-update [start|stop|restart]\n" ;; *) printf "Unknown command: $1\n" exit 1 ;; esac openvswitch-2.5.9/xenserver/PaxHeaders.82075/etc_init.d_openvswitch0000644000000000000000000000013213534540071022261 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.213849239 openvswitch-2.5.9/xenserver/etc_init.d_openvswitch0000755000175000017500000001016613534540071023756 0ustar00jpettitjpettit00000000000000#!/bin/sh # # openvswitch # # chkconfig: 2345 09 91 # description: Manage Open vSwitch kernel modules and user-space daemons # Copyright (C) 2009, 2010, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ### BEGIN INIT INFO # Provides: openvswitch-switch # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Open vSwitch switch ### END INIT INFO . /usr/share/openvswitch/scripts/ovs-lib || exit 1 . /etc/xensource-inventory test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch case `cat /etc/xensource/network.conf` in vswitch|openvswitch) ;; bridge) exit 0 ;; *) echo "Open vSwitch disabled (/etc/xensource/network.conf is invalid)" >&2 exit 0 ;; esac start_ovs_xapi_sync () { if daemon_is_running ovs-xapi-sync; then log_success_msg "ovs-xapi-sync is already running" else PYTHONPATH=/usr/share/openvswitch/python \ /usr/share/openvswitch/scripts/ovs-xapi-sync \ --log-file --pidfile --detach --monitor unix:/var/run/openvswitch/db.sock fi } start () { set ovs_ctl ${1-start} set "$@" --system-id="$INSTALLATION_UUID" set "$@" --system-type="$PRODUCT_BRAND" set "$@" --system-version="$PRODUCT_VERSION-$BUILD_NUMBER" set "$@" --external-id=xs-system-uuid="$INSTALLATION_UUID" set "$@" --daemon-cwd=/var/xen/openvswitch if test X"$FORCE_COREFILES" != X; then set "$@" --force-corefiles="$FORCE_COREFILES" fi if test X"$OVSDB_SERVER_PRIORITY" != X; then set "$@" --ovsdb-server-priority="$OVSDB_SERVER_PRIORITY" fi if test X"$VSWITCHD_PRIORITY" != X; then set "$@" --ovs-vswitchd-priority="$VSWITCHD_PRIORITY" fi if test X"$VSWITCHD_MLOCKALL" != X; then set "$@" --mlockall="$VSWITCHD_MLOCKALL" fi if test ! -e /var/run/openvswitch.booted; then touch /var/run/openvswitch.booted set "$@" --delete-bridges fi set "$@" $OVS_CTL_OPTS "$@" start_ovs_xapi_sync ovs_ctl --protocol=gre enable-protocol touch /var/lock/subsys/openvswitch } force_reload_kmod () { start force-reload-kmod # Restart the high-availability daemon if it is running. Otherwise # it loses its heartbeat and reboots the system after a few minutes. if pidof xhad >/dev/null && test -e /etc/xensource/xhad.conf; then PATH=$PATH:/opt/xensource/xha action "Stopping HA daemon" ha_stop_daemon action "Starting HA daemon" ha_start_daemon fi action "Stopping ovs-xapi-sync" stop_daemon ovs-xapi-sync action "Starting ovs-xapi-sync" start_ovs_xapi_sync } stop () { ovs_ctl stop stop_daemon ovs-xapi-sync rm -f /var/lock/subsys/openvswitch } restart () { if [ "$1" = "--save-flows=yes" ]; then stop_daemon ovs-xapi-sync start restart else stop start fi } case $1 in start) start ;; stop) stop ;; restart) shift restart "$@" ;; reload|force-reload) # The main OVS daemons keep up-to-date, but ovs-xapi-sync needs help. if daemon_is_running ovs-xapi-sync; then action "Configuring Open vSwitch external IDs" \ ovs-appctl -t ovs-xapi-sync flush-cache fi ;; status) ovs_ctl status && daemon_status ovs-xapi-sync ;; version) ovs_ctl version ;; force-reload-kmod) force_reload_kmod ;; help) printf "openvswitch [start|stop|restart|reload|force-reload|status|version]\n" ;; *) printf "Unknown command: $1\n" exit 1 ;; esac openvswitch-2.5.9/xenserver/PaxHeaders.82075/GPLv20000644000000000000000000000013213534540071016502 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.209849209 openvswitch-2.5.9/xenserver/GPLv20000644000175000017500000004310313534540071020171 0ustar00jpettitjpettit00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. openvswitch-2.5.9/PaxHeaders.82075/SECURITY.md0000644000000000000000000000013213534540071015435 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.045685899 30 ctime=1567801423.697845435 openvswitch-2.5.9/SECURITY.md0000644000175000017500000001465613534540071017137 0ustar00jpettitjpettit00000000000000Security Process ================ This is a proposed security vulnerability reporting and handling process for Open vSwitch. It is based on the OpenStack vulnerability management process described at https://wiki.openstack.org/wiki/Vulnerability_Management. The OVS security team coordinates vulnerability management using the ovs-security mailing list. Membership in the security team and subscription to its mailing list consists of a small number of trustworthy people, as determined by rough consensus of the Open vSwitch committers on the ovs-committers mailing list. The Open vSwitch security team should include Open vSwitch committers, to ensure prompt and accurate vulnerability assessments and patch review. We encourage everyone involved in the security process to GPG-sign their emails. We additionally encourage GPG-encrypting one-on-one conversations as part of the security process. What is a vulnerability? ------------------------ All vulnerabilities are bugs, but not every bug is a vulnerability. Vulnerabilities compromise one or more of: * Confidentiality (personal or corporate confidential data). * Integrity (trustworthiness and correctness). * Availability (uptime and service). Here are some examples of vulnerabilities to which one would expect to apply this process: * A crafted packet that causes a kernel or userspace crash (Availability). * A flow translation bug that misforwards traffic in a way likely to hop over security boundaries (Integrity). * An OpenFlow protocol bug that allows a controller to read arbitrary files from the file system (Confidentiality). * Misuse of the OpenSSL library that allows bypassing certificate checks (Integrity). * A bug (memory corruption, overflow, ...) that allows one to modify the behaviour of OVS through external configuration interfaces such as OVSDB (Integrity). * Privileged information is exposed to unprivileged users (Confidentiality). If in doubt, please do use the vulnerability management process. At worst, the response will be to report the bug through the usual channels. Step 1: Reception ----------------- To report an Open vSwitch vulnerability, send an email to the ovs-security mailing list (see "Contact" at the end of this document). A security team member should reply to the reporter acknowledging that the report has been received. Please consider reporting the information mentioned in REPORTING-BUGS.md, where relevant. Reporters may ask for a GPG key while initiating contact with the security team to deliver more sensitive reports. The Linux kernel has its own vulnerability management process: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/SecurityBugs Handling of vulnerabilities that affect both the Open vSwitch tree and the upstream Linux kernel should be reported through both processes. Please send your report as a single email to both the kernel and OVS security teams to allow those teams to most easily coordinate among themselves. Step 2: Assessment ------------------ The security team should discuss the vulnerability. The reporter should be included in the discussion (via "CC") to an appropriate degree. The assessment should determine which Open vSwitch versions are affected (e.g. every version, only the latest release, only unreleased versions), the privilege required to take advantage of the vulnerability (e.g. any network user, any local L2 network user, any local system user, connected OpenFlow controllers), the severity of the vulnerability, and how the vulnerability may be mitigated (e.g. by disabling a feature). The treatment of the vulnerability could end here if the team determines that it is not a realistic vulnerability. Step 3a: Document ---------------- The security team develops a security advisory document. The document credits the reporter and describes the vulnerability, including all of the relevant information from the assessment in step 2. The security team may, at its discretion, include the reporter (via "CC") in developing the security advisory document, but in any case should accept feedback from the reporter before finalizing the document. When the document is final, the security team should obtain a CVE for the vulnerability from a CNA (https://cve.mitre.org/cve/cna.html). Step 3b: Fix ------------ Steps 3a and 3b may proceed in parallel. The security team develops and obtains (private) reviews for patches that fix the vulnerability. If necessary, the security team pulls in additional developers, who must agree to maintain confidentiality. Step 4: Embargoed Disclosure ---------------------------- The security advisory and patches are sent to downstream stakeholders, with an embargo date and time set from the time sent. Downstream stakeholders are expected not to deploy or disclose patches until the embargo is passed. A disclosure date is negotiated by the security team working with the bug submitter as well as vendors. However, the Open vSwitch security team holds the final say when setting a disclosure date. The timeframe for disclosure is from immediate (esp. if it's already publicly known) to a few weeks. As a basic default policy, we expect report date to disclosure date to be 3~5 business days. Operating system vendors are obvious downstream stakeholders. It may not be necessary to be too choosy about who to include: any major Open vSwitch user who is interested and can be considered trustworthy enough could be included. To become a downstream stakeholder, email the ovs-security mailing list. If the vulnerability is already public, skip this step. Step 5: Public Disclosure ------------------------- When the embargo expires, push the (reviewed) patches to appropriate branches, post the patches to the ovs-dev mailing list (noting that they have already been reviewed and applied), post the security advisory to appropriate mailing lists (ovs-announce, ovs-discuss), and post the security advisory on the Open vSwitch webpage. When the patch is applied to LTS (long-term support) branches, a new version should be released. The security advisory should be GPG-signed by a security team member with a key that is in a public web of trust. Contact ======= Report security vulnerabilities to the ovs-security mailing list: security@openvswitch.org Report problems with this document to the ovs-bugs mailing list: bugs@openvswitch.org Visit http://openvswitch.org/ to learn more about Open vSwitch. openvswitch-2.5.9/PaxHeaders.82075/PORTING.md0000644000000000000000000000013213534540071015310 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.041685871 30 ctime=1567801423.685845346 openvswitch-2.5.9/PORTING.md0000644000175000017500000003425113534540071017003 0ustar00jpettitjpettit00000000000000How to Port Open vSwitch to New Software or Hardware ==================================================== Open vSwitch (OVS) is intended to be easily ported to new software and hardware platforms. This document describes the types of changes that are most likely to be necessary in porting OVS to Unix-like platforms. (Porting OVS to other kinds of platforms is likely to be more difficult.) Vocabulary ---------- For historical reasons, different words are used for essentially the same concept in different areas of the Open vSwitch source tree. Here is a concordance, indexed by the area of the source tree: datapath/ vport --- vswitchd/ iface port ofproto/ port bundle ofproto/bond.c slave bond lib/lacp.c slave lacp lib/netdev.c netdev --- database Interface Port Open vSwitch Architectural Overview ----------------------------------- The following diagram shows the very high-level architecture of Open vSwitch from a porter's perspective. +-------------------+ | ovs-vswitchd |<-->ovsdb-server +-------------------+ | ofproto |<-->OpenFlow controllers +--------+-+--------+ | netdev | | ofproto| +--------+ |provider| | netdev | +--------+ |provider| +--------+ Some of the components are generic. Modulo bugs or inadequacies, these components should not need to be modified as part of a port: - "ovs-vswitchd" is the main Open vSwitch userspace program, in vswitchd/. It reads the desired Open vSwitch configuration from the ovsdb-server program over an IPC channel and passes this configuration down to the "ofproto" library. It also passes certain status and statistical information from ofproto back into the database. - "ofproto" is the Open vSwitch library, in ofproto/, that implements an OpenFlow switch. It talks to OpenFlow controllers over the network and to switch hardware or software through an "ofproto provider", explained further below. - "netdev" is the Open vSwitch library, in lib/netdev.c, that abstracts interacting with network devices, that is, Ethernet interfaces. The netdev library is a thin layer over "netdev provider" code, explained further below. The other components may need attention during a port. You will almost certainly have to implement a "netdev provider". Depending on the type of port you are doing and the desired performance, you may also have to implement an "ofproto provider" or a lower-level component called a "dpif" provider. The following sections talk about these components in more detail. Writing a netdev Provider ------------------------- A "netdev provider" implements an operating system and hardware specific interface to "network devices", e.g. eth0 on Linux. Open vSwitch must be able to open each port on a switch as a netdev, so you will need to implement a "netdev provider" that works with your switch hardware and software. struct netdev_class, in lib/netdev-provider.h, defines the interfaces required to implement a netdev. That structure contains many function pointers, each of which has a comment that is meant to describe its behavior in detail. If the requirements are unclear, please report this as a bug. The netdev interface can be divided into a few rough categories: * Functions required to properly implement OpenFlow features. For example, OpenFlow requires the ability to report the Ethernet hardware address of a port. These functions must be implemented for minimally correct operation. * Functions required to implement optional Open vSwitch features. For example, the Open vSwitch support for in-band control requires netdev support for inspecting the TCP/IP stack's ARP table. These functions must be implemented if the corresponding OVS features are to work, but may be omitted initially. * Functions needed in some implementations but not in others. For example, most kinds of ports (see below) do not need functionality to receive packets from a network device. The existing netdev implementations may serve as useful examples during a port: * lib/netdev-linux.c implements netdev functionality for Linux network devices, using Linux kernel calls. It may be a good place to start for full-featured netdev implementations. * lib/netdev-vport.c provides support for "virtual ports" implemented by the Open vSwitch datapath module for the Linux kernel. This may serve as a model for minimal netdev implementations. * lib/netdev-dummy.c is a fake netdev implementation useful only for testing. Porting Strategies ------------------ After a netdev provider has been implemented for a system's network devices, you may choose among three basic porting strategies. The lowest-effort strategy is to use the "userspace switch" implementation built into Open vSwitch. This ought to work, without writing any more code, as long as the netdev provider that you implemented supports receiving packets. It yields poor performance, however, because every packet passes through the ovs-vswitchd process. See [INSTALL.userspace.md] for instructions on how to configure a userspace switch. If the userspace switch is not the right choice for your port, then you will have to write more code. You may implement either an "ofproto provider" or a "dpif provider". Which you should choose depends on a few different factors: * Only an ofproto provider can take full advantage of hardware with built-in support for wildcards (e.g. an ACL table or a TCAM). * A dpif provider can take advantage of the Open vSwitch built-in implementations of bonding, LACP, 802.1ag, 802.1Q VLANs, and other features. An ofproto provider has to provide its own implementations, if the hardware can support them at all. * A dpif provider is usually easier to implement, but most appropriate for software switching. It "explodes" wildcard rules into exact-match entries (with an optional wildcard mask). This allows fast hash lookups in software, but makes inefficient use of TCAMs in hardware that support wildcarding. The following sections describe how to implement each kind of port. ofproto Providers ----------------- An "ofproto provider" is what ofproto uses to directly monitor and control an OpenFlow-capable switch. struct ofproto_class, in ofproto/ofproto-provider.h, defines the interfaces to implement an ofproto provider for new hardware or software. That structure contains many function pointers, each of which has a comment that is meant to describe its behavior in detail. If the requirements are unclear, please report this as a bug. The ofproto provider interface is preliminary. Please let us know if it seems unsuitable for your purpose. We will try to improve it. Writing a dpif Provider ----------------------- Open vSwitch has a built-in ofproto provider named "ofproto-dpif", which is built on top of a library for manipulating datapaths, called "dpif". A "datapath" is a simple flow table, one that is only required to support exact-match flows, that is, flows without wildcards. When a packet arrives on a network device, the datapath looks for it in this table. If there is a match, then it performs the associated actions. If there is no match, the datapath passes the packet up to ofproto-dpif, which maintains the full OpenFlow flow table. If the packet matches in this flow table, then ofproto-dpif executes its actions and inserts a new entry into the dpif flow table. (Otherwise, ofproto-dpif passes the packet up to ofproto to send the packet to the OpenFlow controller, if one is configured.) When calculating the dpif flow, ofproto-dpif generates an exact-match flow that describes the missed packet. It makes an effort to figure out what fields can be wildcarded based on the switch's configuration and OpenFlow flow table. The dpif is free to ignore the suggested wildcards and only support the exact-match entry. However, if the dpif supports wildcarding, then it can use the masks to match multiple flows with fewer entries and potentially significantly reduce the number of flow misses handled by ofproto-dpif. The "dpif" library in turn delegates much of its functionality to a "dpif provider". The following diagram shows how dpif providers fit into the Open vSwitch architecture: _ | +-------------------+ | | ovs-vswitchd |<-->ovsdb-server | +-------------------+ | | ofproto |<-->OpenFlow controllers | +--------+-+--------+ _ | | netdev | |ofproto-| | userspace | +--------+ | dpif | | | | netdev | +--------+ | | |provider| | dpif | | | +---||---+ +--------+ | | || | dpif | | implementation of | || |provider| | ofproto provider |_ || +---||---+ | || || | _ +---||-----+---||---+ | | | |datapath| | kernel | | +--------+ _| | | | |_ +--------||---------+ || physical NIC struct dpif_class, in lib/dpif-provider.h, defines the interfaces required to implement a dpif provider for new hardware or software. That structure contains many function pointers, each of which has a comment that is meant to describe its behavior in detail. If the requirements are unclear, please report this as a bug. There are two existing dpif implementations that may serve as useful examples during a port: * lib/dpif-netlink.c is a Linux-specific dpif implementation that talks to an Open vSwitch-specific kernel module (whose sources are in the "datapath" directory). The kernel module performs all of the switching work, passing packets that do not match any flow table entry up to userspace. This dpif implementation is essentially a wrapper around calls into the kernel module. * lib/dpif-netdev.c is a generic dpif implementation that performs all switching internally. This is how the Open vSwitch userspace switch is implemented. Miscellaneous Notes ------------------- Open vSwitch source code uses uint16_t, uint32_t, and uint64_t as fixed-width types in host byte order, and ovs_be16, ovs_be32, and ovs_be64 as fixed-width types in network byte order. Each of the latter is equivalent to the one of the former, but the difference in name makes the intended use obvious. The default "fail-mode" for Open vSwitch bridges is "standalone", meaning that, when the OpenFlow controllers cannot be contacted, Open vSwitch acts as a regular MAC-learning switch. This works well in virtualization environments where there is normally just one uplink (either a single physical interface or a bond). In a more general environment, it can create loops. So, if you are porting to a general-purpose switch platform, you should consider changing the default "fail-mode" to "secure", which does not behave this way. See documentation for the "fail-mode" column in the Bridge table in ovs-vswitchd.conf.db(5) for more information. lib/entropy.c assumes that it can obtain high-quality random number seeds at startup by reading from /dev/urandom. You will need to modify it if this is not true on your platform. vswitchd/system-stats.c only knows how to obtain some statistics on Linux. Optionally you may implement them for your platform as well. Why OVS Does Not Support Hybrid Providers ----------------------------------------- The "Porting Strategies" section above describes the "ofproto provider" and "dpif provider" porting strategies. Only an ofproto provider can take advantage of hardware TCAM support, and only a dpif provider can take advantage of the OVS built-in implementations of various features. It is therefore tempting to suggest a hybrid approach that shares the advantages of both strategies. However, Open vSwitch does not support a hybrid approach. Doing so may be possible, with a significant amount of extra development work, but it does not yet seem worthwhile, for the reasons explained below. First, user surprise is likely when a switch supports a feature only with a high performance penalty. For example, one user questioned why adding a particular OpenFlow action to a flow caused a 1,058x slowdown on a hardware OpenFlow implementation [1]. The action required the flow to be implemented in software. Given that implementing a flow in software on the slow management CPU of a hardware switch causes a major slowdown, software-implemented flows would only make sense for very low-volume traffic. But many of the features built into the OVS software switch implementation would need to apply to every flow to be useful. There is no value, for example, in applying bonding or 802.1Q VLAN support only to low-volume traffic. Besides supporting features of OpenFlow actions, a hybrid approach could also support forms of matching not supported by particular switching hardware, by sending all packets that might match a rule to software. But again this can cause an unacceptable slowdown by forcing bulk traffic through software in the hardware switch's slow management CPU. Consider, for example, a hardware switch that can match on the IPv6 Ethernet type but not on fields in IPv6 headers. An OpenFlow table that matched on the IPv6 Ethernet type would perform well, but adding a rule that matched only UDPv6 would force every IPv6 packet to software, slowing down not just UDPv6 but all IPv6 processing. [1] Aaron Rosen, "Modify packet fields extremely slow", openflow-discuss mailing list, June 26, 2011, archived at https://mailman.stanford.edu/pipermail/openflow-discuss/2011-June/002386.html. Questions --------- Please direct porting questions to dev@openvswitch.org. We will try to use questions to improve this porting guide. [INSTALL.userspace.md]:INSTALL.userspace.md openvswitch-2.5.9/PaxHeaders.82075/utilities0000644000000000000000000000013213534540121015576 xustar0030 mtime=1567801425.197856493 30 atime=1567801425.625859648 30 ctime=1567801425.197856493 openvswitch-2.5.9/utilities/0000755000175000017500000000000013534540121017341 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-benchmark.1.in0000644000000000000000000000013213534540071021105 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.137686576 30 ctime=1567801423.757845878 openvswitch-2.5.9/utilities/ovs-benchmark.1.in0000644000175000017500000001676613534540071022613 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .so lib/ovs.tmac .TH ovs\-benchmark 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME ovs\-benchmark \- flow setup benchmark utility for Open vSwitch . .SH SYNOPSIS . .SY ovs\-benchmark\ latency \fB\-\-remote \fIip\fR[\fB:\fIports\fR] .OP \-\-sockets nsocks .OP \-\-batches nbatches .OP \-\-local \fR[\fIip\fR][\fB:\fIports\fR] .YS . .SY ovs\-benchmark\ rate \fB\-\-remote \fIip\fR[\fB:\fIports\fR] .OP \-\-max\-rate rate .OP \-\-timeout maxsecs .OP \-\-sockets nsocks .OP \-\-batches nbatches .OP \-\-local \fR[\fIip\fR][\fB:\fIports\fR] .YS . .SY ovs\-benchmark\ listen .OP \-\-local \fR[\fIip\fR]\fB:\fIports .YS . .SY ovs\-benchmark\ help .YS . .SH DESCRIPTION \fBovs\-benchmark\fR tests the performance of Open vSwitch flow setup by setting up a number of TCP connections and measuring the time required. It can also be used with the Linux bridge or without any bridging software, which allows one to measure the bandwidth and latency cost of bridging. .PP Each \fBovs\-benchmark\fR command is described separately below. . .SH "The ``latency'' command" . .PP This command initiates \fInsocks\fR TCP connections (by default, 100) as quickly as possible, waits for each one to complete with success or failure, and prints a bar chart of completion times on standard output, followed by a summary line. Each line in the bar chart lists a time to connection completion in milliseconds followed by a number of \fB.\fR or \fB!\fR symbols, one for each TCP connection that completed in that many milliseconds. A successful connection prints a \fB.\fR, and an unsuccessful connection (e.g. to a port on which no process is listening) prints a \fB!\fR. . .PP If \fInbatches\fR is given, the entire procedure is repeated the specified number of times. Only a single summary line is printed at the end. . .PP Results vary widely based on the number of sockets and whether the remote host is listening for connections on the specified ports. With a small number of sockets, all connection times typically remain within a handful of milliseconds. As the number of sockets increases, the distribution of connection times clusters around the sending TCP stack's SYN retransmission interval. (This pattern occurs with or without Open vSwitch on the network path.) . .SH "The ``rate'' command" . .PP This command initiates \fInsocks\fR TCP connections (by default, 100) as quickly as possible (limited by \fImaxrate\fR, if \fB\-\-max\-rate\fR is specified). Each time a connection completes with success or failure, it closes that connection and initiates a new one. It continues to do so either forever or, if \fB\-\-timeout\fR is specified, until \fImaxsecs\fR seconds have elapsed. During the test, it prints statistics about time elapsed, successful and unsuccessful connections, and the average number of completed (succeeded or failed) connections per second over the run. . .PP Without \fB\-\-max\-rate\fR, the \fBrate\fR command measures the maximum sustained flow setup rate for an Open vSwitch instance. This naturally tends to drive \fBovs\-vswitchd\fR CPU usage to 100% on the host receiving the traffic. . .PP When \fB\-\-max\-rate\fR is specified with a value below the maximum rate that an Open vSwitch instance can handle, then \fBrate\fR can also be used to measure the kernel and userspace CPU cost of flow setups at specific flow rates. . .PP Results tend to fluctuate greatly for the first few seconds of a run, then settle down. The displayed average is calculated over the entire run and so tends to converge asymptotically on the ``correct'' value. To converge more quickly, try running for 5 to 10 seconds, then killing and restarting the run. . .SH "The ``listen'' command" . .PP This command listens on one or more TCP ports for incoming connections. It accepts connections and immediately closes them. It can be paired with the \fBrate\fR or \fBlatency\fR commands for observing effects of successful vs. unsuccessful TCP connections. . .PP It is easier to reproduce and interpret \fBovs\-benchmark\fR results when there is no listener (see \fBNOTES\fR below). . .SH "The ``help'' command" . .PP Prints a usage message and exits successfully. . .SH OPTIONS . .IP "\fB\-r \fIip\fR[\fB:\fIports\fR]" .IQ "\fB\-\-remote \fIip\fR[\fB:\fIports\fR]" This option, required on \fBlatency\fR and \fBrate\fR commands, minimally specifies the remote host to connect to (as an IP address or DNS name) as \fIip\fR. . .IP A TCP port or range of ports (separated by \fB\-\fR) may also be specified. If a range is specified then each port in the range is used in round-robin order. The default port is 6630 if none is specified. . .IP "\fB\-l \fR[\fIip\fR][\fB:\fIports\fR]" .IQ "\fB\-\-local \fR[\fIip\fR][\fB:\fIports\fR]" On the \fBlatency\fR and \fBrate\fR, without this option, outgoing connections will not bind a specific TCP port. The local TCP stack will pick a local TCP port to bind. When this option is specified, the specified port or range of ports will be used in turn. (If a port range is specified on both \fB\-\-local\fR and \fB\-\-remote\fR, then each local port in its range will be used before the remote port is incremented to the next port in its range.) . .IP On the \fBlisten\fR command, this option specifies the local port or ports and IP addresses on which to listen. If it is omitted, port 6630 on any IP address is used. . .IP "\fB\-s \fInsocks\fR" .IQ "\fB\-\-sockets \fInsocks\fR" For \fBlatency\fR, sets the number of connections to initiate per batch. For \fBrate\fR, sets the number of outstanding connections attempts to maintain at any given time. The default is 100. . .IP "\fB\-b \fInbatches\fR" .IQ "\fB\-\-batches \fInbatches\fR" For \fBlatency\fR, sets the number of times to initiate and wait for all of the connections to complete. The default is 1. . .IP "\fB\-c \fImaxrate\fR" .IQ "\fB\-\-max\-rate \fImaxrate\fR" For \fBrate\fR, caps the maximum rate at which connections will be attempted to \fImaxrate\fR connections per second. By default there is no limit. . .IP "\fB\-T \fImaxsecs\fR" .IQ "\fB\-\-timeout \fImaxsecs\fR" For \fBrate\fR, stops the benchmark after \fImaxsecs\fR seconds have elapsed. By default, the benchmark continues until interrupted by a signal. . .SH NOTES .PP \fBovs\-benchmark\fR uses standard POSIX socket calls for network access, so it shares the strengths and limitations of TCP/IP and its implementations in the local and remote TCP/IP stacks. Particularly, TCP and its implementations limit the number of successfully completed and then closed TCP connections. This means that \fBovs\-benchmark\fR tests tend to slow down if run for long intervals or with large numbers of sockets or batches, if the remote system is listening on the port or ports being contacted. The problem does not occur when the remote system is not listening. \fBovs\-benchmark\fR results are therefore much more reliable and repeatable when the remote system is not listening on the port or ports being contacted. Even a single listening socket (e.g. range of ports 8000 to 9000 with one listener on port 8080) can cause anomalies in results. . .PP Be sure that the remote TCP/IP stack's firewall allows the benchmark's traffic to be processed. For Open vSwitch benchmarking purposes, you might want to disable the firewall with, e.g., \fBiptables \-F\fR. . .PP \fBovs\-benchmark\fR is single-threaded. A multithreaded process might be able to initiate connections more quickly. . .PP A TCP connection consists of two flows (one in each direction), so multiply the TCP connection statistics that \fBovs\-benchmark\fR reports by 2 to get flow statistics. openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vlan-bug-workaround.c0000644000000000000000000000013213534540071022534 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801425.193856463 openvswitch-2.5.9/utilities/ovs-vlan-bug-workaround.c0000644000175000017500000000734513534540071024233 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "util.h" #define ADD_ALL_VLANS_CMD 10 #define DEL_ALL_VLANS_CMD 11 static void usage(void); static void parse_options(int argc, char *argv[]); int main(int argc, char *argv[]) { struct vlan_ioctl_args vlan_args; const char *netdev, *setting; int fd; set_program_name(argv[0]); parse_options(argc, argv); if (argc - optind != 2) { ovs_fatal(0, "exactly two non-option arguments are required " "(use --help for help)"); } memset(&vlan_args, 0, sizeof vlan_args); /* Get command. */ setting = argv[optind + 1]; if (!strcmp(setting, "on")) { vlan_args.cmd = ADD_ALL_VLANS_CMD; } else if (!strcmp(setting, "off")) { vlan_args.cmd = DEL_ALL_VLANS_CMD; } else { ovs_fatal(0, "second command line argument must be \"on\" or \"off\" " "(not \"%s\")", setting); } /* Get network device name. */ netdev = argv[optind]; if (strlen(netdev) >= IFNAMSIZ) { ovs_fatal(0, "%s: network device name too long", netdev); } strcpy(vlan_args.device1, netdev); /* Execute operation. */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { ovs_fatal(errno, "socket creation failed"); } if (ioctl(fd, SIOCSIFVLAN, &vlan_args) < 0) { if (errno == ENOPKG) { ovs_fatal(0, "operation failed (8021q module not loaded)"); } else if (errno == EOPNOTSUPP) { ovs_fatal(0, "operation failed (kernel does not support the " "VLAN bug workaround)"); } else { ovs_fatal(errno, "operation failed"); } } close(fd); return 0; } static void usage(void) { printf("\ %s, for enabling or disabling the kernel VLAN bug workaround\n\ usage: %s NETDEV SETTING\n\ where NETDEV is a network device (e.g. \"eth0\")\n\ and SETTING is \"on\" to enable the workaround or \"off\" to disable it.\n\ \n\ Options:\n\ -h, --help Print this helpful information\n\ -V, --version Display version information\n", program_name, program_name); exit(EXIT_SUCCESS); } static void parse_options(int argc, char *argv[]) { static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int option; option = getopt_long(argc, argv, "+t:hVe", long_options, NULL); if (option == -1) { break; } switch (option) { case 'h': usage(); break; case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); case '?': exit(EXIT_FAILURE); default: OVS_NOT_REACHED(); } } free(short_options); } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-parse-backtrace.in0000644000000000000000000000013213534540071022043 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.817846319 openvswitch-2.5.9/utilities/ovs-parse-backtrace.in0000755000175000017500000000544313534540071023542 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Copyright (c) 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import optparse import os import re import subprocess import sys addr2line_cache = {} # None if addr2line is missing or broken. def addr2line(binary, addr): global addr2line_cache if addr2line_cache is None: return "" if addr in addr2line_cache: return addr2line_cache[addr] cmd = ["addr2line", "-f", "-s", "-e", binary, addr] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) lines = proc.stdout.readlines() failed = proc.returncode except OSError: failed = True if failed: addr2line_cache = None return "" lines = [l.strip() for l in lines] return " ".join(lines) def main(): parser = optparse.OptionParser(version='@VERSION@', usage="usage: %prog [binary]", description="""\ Parses the output of ovs-appctl backtrace producing a more human readable result. Expected usage is for ovs-appctl backtrace to be piped in.""") options, args = parser.parse_args() if len(args) > 1: parser.print_help() sys.exit(1) if len(args) == 1: binary = args[0] else: binary = "@sbindir@/ovs-vswitchd" debug = "/usr/lib/debug%s.debug" % binary if os.path.exists(debug): binary = debug print "Binary: %s\n" % binary stdin = sys.stdin.read() traces = [] for trace in stdin.strip().split("\n\n"): lines = trace.splitlines() match = re.search(r'Count (\d+)', lines[0]) if match: count = int(match.group(1)) else: count = 0 traces.append((lines[1:], count)) traces = sorted(traces, key=(lambda x: x[1]), reverse=True) for lines, count in traces: longest = max(len(l) for l in lines) print "Backtrace Count: %d" % count for line in lines: match = re.search(r'\[(0x.*)]', line) if match: print "%s %s" % (line.ljust(longest), addr2line(binary, match.group(1))) else: print line print if __name__ == "__main__": main() openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-check-dead-ifs.in0000644000000000000000000000013113534540071021542 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 29 ctime=1567801423.80984626 openvswitch-2.5.9/utilities/ovs-check-dead-ifs.in0000755000175000017500000000563513534540071023245 0ustar00jpettitjpettit00000000000000#! @PYTHON@ import os import re import stat import sys if "--help" in sys.argv: sys.stdout.write("""\ ovs-check-dead-ifs: Check for packet sockets for nonexistent network devices. One side effect of the "force-reload-kmod" command that reloads the Open vSwitch kernel module is that all the network devices that the Open vSwitch kernel module implemented get destroyed and then replaced by new instances with the same names. Unfortunately, programs that are listening for packets on the original network devices will not receive packets that arrive on the new instances. This causes some services, such as DHCP, to silently fail. This program looks for such problems and, if it finds any, prints information about programs that are in such a state. The system administrator should then take some action to fix the problem, such as restarting these programs. """) sys.exit(0) elif len(sys.argv) > 1: sys.stderr.write("ovs-check-dead-ifs: no arguments or options accepted " "(use --help for help)\n") sys.exit(1) # Get the set of all valid ifindexes. # # 0 is always valid for our purposes because it means "any interface". valid_ifindexes = set([]) for ifname in os.listdir("/sys/class/net"): fn = "/sys/class/net/%s/ifindex" % ifname try: valid_ifindexes.add(int(open(fn).readline())) except IOError: pass except ValueError: print "%s: unexpected format\n" % fn # Get inodes for all packet sockets whose ifindexes don't exist. invalid_inodes = set() f = open("/proc/net/packet") f.readline() # Skip header line. for line in f: fields = line.split() ifindex = int(fields[4]) if ifindex not in valid_ifindexes: invalid_inodes.add(int(fields[8])) f.close() if not invalid_inodes: sys.exit(0) # Now find the processes that are using those packet sockets. inode_re = re.compile(r'socket:\[([0-9]+)\]$') bad_pids = set() for pid in os.listdir("/proc"): try: pid = int(pid) except ValueError: continue try: fds = os.listdir("/proc/%d/fd" % pid) except OSError: continue for fd in fds: try: fd = int(fd) except ValueError: continue try: s = os.stat("/proc/%d/fd/%d" % (pid, fd)) except OSError: continue if not stat.S_ISSOCK(s.st_mode): continue try: linkname = os.readlink("/proc/%d/fd/%d" % (pid, fd)) except OSError: continue m = inode_re.match(linkname) if not m: continue inode = int(m.group(1)) if inode in invalid_inodes: bad_pids.add(pid) if bad_pids: print """ The following processes are listening for packets to arrive on network devices that no longer exist. You may want to restart them.""" sys.stdout.flush() os.execvp("ps", ["ps"] + ["%s" % pid for pid in bad_pids]) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-ofctl.c0000644000000000000000000000013213534540071017737 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801425.193856463 openvswitch-2.5.9/utilities/ovs-ofctl.c0000644000175000017500000040173713534540071021441 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "byte-order.h" #include "classifier.h" #include "command-line.h" #include "daemon.h" #include "compiler.h" #include "dirs.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "nx-match.h" #include "odp-util.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-parse.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofp-version-opt.h" #include "ofpbuf.h" #include "ofproto/ofproto.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "dp-packet.h" #include "packets.h" #include "pcap-file.h" #include "poll-loop.h" #include "random.h" #include "stream-ssl.h" #include "socket-util.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "meta-flow.h" #include "sort.h" VLOG_DEFINE_THIS_MODULE(ofctl); /* --bundle: Use OpenFlow 1.4 bundle for making the flow table change atomic. * NOTE: Also the flow mod will use OpenFlow 1.4, so the semantics may be * different (see the comment in parse_options() for details). */ static bool bundle = false; /* --strict: Use strict matching for flow mod commands? Additionally governs * use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match. */ static bool strict; /* --readd: If true, on replace-flows, re-add even flows that have not changed * (to reset flow counters). */ static bool readd; /* -F, --flow-format: Allowed protocols. By default, any protocol is * allowed. */ static enum ofputil_protocol allowed_protocols = OFPUTIL_P_ANY; /* -P, --packet-in-format: Packet IN format to use in monitor and snoop * commands. Either one of NXPIF_* to force a particular packet_in format, or * -1 to let ovs-ofctl choose the default. */ static int preferred_packet_in_format = -1; /* -m, --more: Additional verbosity for ofp-print functions. */ static int verbosity; /* --timestamp: Print a timestamp before each received packet on "monitor" and * "snoop" command? */ static bool timestamp; /* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop" commands. */ static char *unixctl_path; /* --sort, --rsort: Sort order. */ enum sort_order { SORT_ASC, SORT_DESC }; struct sort_criterion { const struct mf_field *field; /* NULL means to sort by priority. */ enum sort_order order; }; static struct sort_criterion *criteria; static size_t n_criteria, allocated_criteria; static const struct ovs_cmdl_command *get_all_commands(void); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[]); static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid, struct ofpbuf **replyp, struct ofputil_flow_stats *, struct ofpbuf *ofpacts); int main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; set_program_name(argv[0]); service_start(&argc, &argv); parse_options(argc, argv); fatal_ignore_sigpipe(); ctx.argc = argc - optind; ctx.argv = argv + optind; daemon_become_new_user(false); ovs_cmdl_run_command(&ctx, get_all_commands()); return 0; } static void add_sort_criterion(enum sort_order order, const char *field) { struct sort_criterion *sc; if (n_criteria >= allocated_criteria) { criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria); } sc = &criteria[n_criteria++]; if (!field || !strcasecmp(field, "priority")) { sc->field = NULL; } else { sc->field = mf_from_name(field); if (!sc->field) { ovs_fatal(0, "%s: unknown field name", field); } } sc->order = order; } static void parse_options(int argc, char *argv[]) { enum { OPT_STRICT = UCHAR_MAX + 1, OPT_READD, OPT_TIMESTAMP, OPT_SORT, OPT_RSORT, OPT_UNIXCTL, OPT_BUNDLE, DAEMON_OPTION_ENUMS, OFP_VERSION_OPTION_ENUMS, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"timeout", required_argument, NULL, 't'}, {"strict", no_argument, NULL, OPT_STRICT}, {"readd", no_argument, NULL, OPT_READD}, {"flow-format", required_argument, NULL, 'F'}, {"packet-in-format", required_argument, NULL, 'P'}, {"more", no_argument, NULL, 'm'}, {"timestamp", no_argument, NULL, OPT_TIMESTAMP}, {"sort", optional_argument, NULL, OPT_SORT}, {"rsort", optional_argument, NULL, OPT_RSORT}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"bundle", no_argument, NULL, OPT_BUNDLE}, DAEMON_LONG_OPTIONS, OFP_VERSION_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); uint32_t versions; enum ofputil_protocol version_protocols; /* For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is * because ovs-ofctl implements command such as "add-flow" as raw OpenFlow * requests, but those requests have subtly different semantics in * different OpenFlow versions. For example: * * - In OpenFlow 1.0, a "mod-flow" operation that does not find any * existing flow to modify adds a new flow. * * - In OpenFlow 1.1, a "mod-flow" operation that does not find any * existing flow to modify adds a new flow, but only if the mod-flow * did not match on the flow cookie. * * - In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a * new flow. */ set_allowed_ofp_versions("OpenFlow10"); for (;;) { unsigned long int timeout; int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 't': timeout = strtoul(optarg, NULL, 10); if (timeout <= 0) { ovs_fatal(0, "value %s on -t or --timeout is not at least 1", optarg); } else { time_alarm(timeout); } break; case 'F': allowed_protocols = ofputil_protocols_from_string(optarg); if (!allowed_protocols) { ovs_fatal(0, "%s: invalid flow format(s)", optarg); } break; case 'P': preferred_packet_in_format = ofputil_packet_in_format_from_string(optarg); if (preferred_packet_in_format < 0) { ovs_fatal(0, "unknown packet-in format `%s'", optarg); } break; case 'm': verbosity++; break; case 'h': usage(); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case OPT_BUNDLE: bundle = true; break; case OPT_STRICT: strict = true; break; case OPT_READD: readd = true; break; case OPT_TIMESTAMP: timestamp = true; break; case OPT_SORT: add_sort_criterion(SORT_ASC, optarg); break; case OPT_RSORT: add_sort_criterion(SORT_DESC, optarg); break; case OPT_UNIXCTL: unixctl_path = optarg; break; DAEMON_OPTION_HANDLERS OFP_VERSION_OPTION_HANDLERS VLOG_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } if (n_criteria) { /* Always do a final sort pass based on priority. */ add_sort_criterion(SORT_DESC, "priority"); } free(short_options); /* Implicit OpenFlow 1.4 with the '--bundle' option. */ if (bundle) { /* Add implicit allowance for OpenFlow 1.4. */ add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF14_OXM)); /* Remove all prior versions. */ mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( OFPUTIL_P_OF14_UP)); } versions = get_allowed_ofp_versions(); version_protocols = ofputil_protocols_from_version_bitmap(versions); if (!(allowed_protocols & version_protocols)) { char *protocols = ofputil_protocols_to_string(allowed_protocols); struct ds version_s = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&version_s, versions); ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports " "any of the enabled flow formats (%s). (Use -O to enable " "additional OpenFlow versions or -F to enable additional " "flow formats.)", ds_cstr(&version_s), protocols); } allowed_protocols &= version_protocols; mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap( allowed_protocols)); } static void usage(void) { printf("%s: OpenFlow switch management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" "\nFor OpenFlow switches:\n" " show SWITCH show OpenFlow information\n" " dump-desc SWITCH print switch description\n" " dump-tables SWITCH print table stats\n" " dump-table-features SWITCH print table features\n" " dump-table-desc SWITCH print table description (OF1.4+)\n" " mod-port SWITCH IFACE ACT modify port behavior\n" " mod-table SWITCH MOD modify flow table behavior\n" " OF1.1/1.2 MOD: controller, continue, drop\n" " OF1.4+ MOD: evict, noevict, vacancy:low,high, novacancy\n" " get-frags SWITCH print fragment handling behavior\n" " set-frags SWITCH FRAG_MODE set fragment handling behavior\n" " FRAG_MODE: normal, drop, reassemble, nx-match\n" " dump-ports SWITCH [PORT] print port statistics\n" " dump-ports-desc SWITCH [PORT] print port descriptions\n" " dump-flows SWITCH print all flow entries\n" " dump-flows SWITCH FLOW print matching FLOWs\n" " dump-aggregate SWITCH print aggregate flow statistics\n" " dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n" " queue-stats SWITCH [PORT [QUEUE]] dump queue stats\n" " add-flow SWITCH FLOW add flow described by FLOW\n" " add-flows SWITCH FILE add flows from FILE\n" " mod-flows SWITCH FLOW modify actions of matching FLOWs\n" " del-flows SWITCH [FLOW] delete matching FLOWs\n" " replace-flows SWITCH FILE replace flows with those in FILE\n" " diff-flows SOURCE1 SOURCE2 compare flows from two sources\n" " packet-out SWITCH IN_PORT ACTIONS PACKET...\n" " execute ACTIONS on PACKET\n" " monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n" " print packets received from SWITCH\n" " snoop SWITCH snoop on SWITCH and its controller\n" " add-group SWITCH GROUP add group described by GROUP\n" " add-groups SWITCH FILE add group from FILE\n" " mod-group SWITCH GROUP modify specific group\n" " del-groups SWITCH [GROUP] delete matching GROUPs\n" " insert-buckets SWITCH [GROUP] add buckets to GROUP\n" " remove-buckets SWITCH [GROUP] remove buckets from GROUP\n" " dump-group-features SWITCH print group features\n" " dump-groups SWITCH [GROUP] print group description\n" " dump-group-stats SWITCH [GROUP] print group statistics\n" " queue-get-config SWITCH PORT print queue information for port\n" " add-meter SWITCH METER add meter described by METER\n" " mod-meter SWITCH METER modify specific METER\n" " del-meter SWITCH METER delete METER\n" " del-meters SWITCH delete all meters\n" " dump-meter SWITCH METER print METER configuration\n" " dump-meters SWITCH print all meter configuration\n" " meter-stats SWITCH [METER] print meter statistics\n" " meter-features SWITCH print meter features\n" " add-tlv-map SWITCH MAP add TLV option MAPpings\n" " del-tlv-map SWITCH [MAP] delete TLV option MAPpings\n" " dump-tlv-map SWITCH print TLV option mappings\n" "\nFor OpenFlow switches and controllers:\n" " probe TARGET probe whether TARGET is up\n" " ping TARGET [N] latency of N-byte echos\n" " benchmark TARGET N COUNT bandwidth of COUNT N-byte echos\n" "SWITCH or TARGET is an active OpenFlow connection method.\n" "\nOther commands:\n" " ofp-parse FILE print messages read from FILE\n" " ofp-parse-pcap PCAP print OpenFlow read from PCAP\n", program_name, program_name); vconn_usage(true, false, false); daemon_usage(); ofp_version_usage(); vlog_usage(); printf("\nOther options:\n" " --strict use strict match for flow commands\n" " --readd replace flows that haven't changed\n" " -F, --flow-format=FORMAT force particular flow format\n" " -P, --packet-in-format=FRMT force particular packet in format\n" " -m, --more be more verbose printing OpenFlow\n" " --timestamp (monitor, snoop) print timestamps\n" " -t, --timeout=SECS give up after SECS seconds\n" " --sort[=field] sort in ascending order\n" " --rsort[=field] sort in descending order\n" " --unixctl=SOCKET set control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } static void run(int retval, const char *message, ...) OVS_PRINTF_FORMAT(2, 3); static void run(int retval, const char *message, ...) { if (retval) { va_list args; va_start(args, message); ovs_fatal_valist(retval, message, args); } } /* Generic commands. */ static int open_vconn_socket(const char *name, struct vconn **vconnp) { char *vconn_name = xasprintf("unix:%s", name); int error; error = vconn_open(vconn_name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp); if (error && error != ENOENT) { ovs_fatal(0, "%s: failed to open socket (%s)", name, ovs_strerror(error)); } free(vconn_name); return error; } enum open_target { MGMT, SNOOP }; static enum ofputil_protocol open_vconn__(const char *name, enum open_target target, struct vconn **vconnp) { const char *suffix = target == MGMT ? "mgmt" : "snoop"; char *datapath_name, *datapath_type, *socket_name; enum ofputil_protocol protocol; char *bridge_path; int ofp_version; int error; bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, suffix); ofproto_parse_name(name, &datapath_name, &datapath_type); socket_name = xasprintf("%s/%s.%s", ovs_rundir(), datapath_name, suffix); free(datapath_name); free(datapath_type); if (strchr(name, ':')) { run(vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp), "connecting to %s", name); } else if (!open_vconn_socket(name, vconnp)) { /* Fall Through. */ } else if (!open_vconn_socket(bridge_path, vconnp)) { /* Fall Through. */ } else if (!open_vconn_socket(socket_name, vconnp)) { /* Fall Through. */ } else { ovs_fatal(0, "%s is not a bridge or a socket", name); } if (target == SNOOP) { vconn_set_recv_any_version(*vconnp); } free(bridge_path); free(socket_name); VLOG_DBG("connecting to %s", vconn_get_name(*vconnp)); error = vconn_connect_block(*vconnp); if (error) { ovs_fatal(0, "%s: failed to connect to socket (%s)", name, ovs_strerror(error)); } ofp_version = vconn_get_version(*vconnp); protocol = ofputil_protocol_from_ofp_version(ofp_version); if (!protocol) { ovs_fatal(0, "%s: unsupported OpenFlow version 0x%02x", name, ofp_version); } return protocol; } static enum ofputil_protocol open_vconn(const char *name, struct vconn **vconnp) { return open_vconn__(name, MGMT, vconnp); } static void send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer) { run(vconn_send_block(vconn, buffer), "failed to send packet to switch"); } static void dump_transaction(struct vconn *vconn, struct ofpbuf *request) { struct ofpbuf *reply; run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); ofp_print(stdout, reply->data, reply->size, verbosity + 1); ofpbuf_delete(reply); } static void dump_trivial_transaction(const char *vconn_name, enum ofpraw raw) { struct ofpbuf *request; struct vconn *vconn; open_vconn(vconn_name, &vconn); request = ofpraw_alloc(raw, vconn_get_version(vconn), 0); dump_transaction(vconn, request); vconn_close(vconn); } static void dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request) { const struct ofp_header *request_oh = request->data; ovs_be32 send_xid = request_oh->xid; enum ofpraw request_raw; enum ofpraw reply_raw; bool done = false; ofpraw_decode_partial(&request_raw, request->data, request->size); reply_raw = ofpraw_stats_request_to_reply(request_raw, request_oh->version); send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { enum ofpraw raw; ofp_print(stdout, reply->data, reply->size, verbosity + 1); ofpraw_decode(&raw, reply->data); if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) { done = true; } else if (raw == reply_raw) { done = !ofpmp_more(reply->data); } else { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, verbosity + 1)); } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } } static void dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw) { struct ofpbuf *request; struct vconn *vconn; open_vconn(vconn_name, &vconn); request = ofpraw_alloc(raw, vconn_get_version(vconn), 0); dump_stats_transaction(vconn, request); vconn_close(vconn); } /* Sends all of the 'requests', which should be requests that only have replies * if an error occurs, and waits for them to succeed or fail. If an error does * occur, prints it and exits with an error. * * Destroys all of the 'requests'. */ static void transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests) { struct ofpbuf *reply; run(vconn_transact_multiple_noreply(vconn, requests, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { ofp_print(stderr, reply->data, reply->size, verbosity + 2); exit(1); } ofpbuf_delete(reply); } static void bundle_error_reporter(const struct ofp_header *oh) { ofp_print(stderr, oh, ntohs(oh->length), verbosity + 1); fflush(stderr); } static void bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags) { run(vconn_bundle_transact(vconn, requests, flags, bundle_error_reporter), "talking to %s", vconn_get_name(vconn)); } /* Sends 'request', which should be a request that only has a reply if an error * occurs, and waits for it to succeed or fail. If an error does occur, prints * it and exits with an error. * * Destroys 'request'. */ static void transact_noreply(struct vconn *vconn, struct ofpbuf *request) { struct ovs_list requests; list_init(&requests); list_push_back(&requests, &request->list_node); transact_multiple_noreply(vconn, &requests); } static void fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_) { struct ofp_switch_config *config; struct ofpbuf *request; struct ofpbuf *reply; enum ofptype type; request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST, vconn_get_version(vconn), 0); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) { ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn)); } config = ofpbuf_pull(reply, sizeof *config); *config_ = *config; ofpbuf_delete(reply); } static void set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config) { struct ofpbuf *request; request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, vconn_get_version(vconn), 0); ofpbuf_put(request, config, sizeof *config); transact_noreply(vconn, request); } static void ofctl_show(struct ovs_cmdl_context *ctx) { const char *vconn_name = ctx->argv[1]; enum ofp_version version; struct vconn *vconn; struct ofpbuf *request; struct ofpbuf *reply; bool has_ports; open_vconn(vconn_name, &vconn); version = vconn_get_version(vconn); request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name); has_ports = ofputil_switch_features_has_ports(reply); ofp_print(stdout, reply->data, reply->size, verbosity + 1); ofpbuf_delete(reply); if (!has_ports) { request = ofputil_encode_port_desc_stats_request(version, OFPP_ANY); dump_stats_transaction(vconn, request); } dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST); vconn_close(vconn); } static void ofctl_dump_desc(struct ovs_cmdl_context *ctx) { dump_trivial_stats_transaction(ctx->argv[1], OFPRAW_OFPST_DESC_REQUEST); } static void ofctl_dump_tables(struct ovs_cmdl_context *ctx) { dump_trivial_stats_transaction(ctx->argv[1], OFPRAW_OFPST_TABLE_REQUEST); } static void ofctl_dump_table_features(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_table_features_request(vconn_get_version(vconn)); /* The following is similar to dump_trivial_stats_transaction(), but it * maintains the previous 'ofputil_table_features' from one stats reply * message to the next, which allows duplication to be eliminated in the * output across messages. Otherwise the output is much larger and harder * to read, because only 17 or so ofputil_table_features elements fit in a * single 64 kB OpenFlow message and therefore you get a ton of repetition * (every 17th element is printed in full instead of abbreviated). */ const struct ofp_header *request_oh = request->data; ovs_be32 send_xid = request_oh->xid; bool done = false; struct ofputil_table_features prev; int n = 0; send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { enum ofptype type; enum ofperr error; error = ofptype_decode(&type, reply->data); if (error) { ovs_fatal(0, "decode error: %s", ofperr_get_name(error)); } else if (type == OFPTYPE_ERROR) { ofp_print(stdout, reply->data, reply->size, verbosity + 1); done = true; } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) { done = !ofpmp_more(reply->data); for (;;) { struct ofputil_table_features tf; int retval; retval = ofputil_decode_table_features(reply, &tf, true); if (retval) { if (retval != EOF) { ovs_fatal(0, "decode error: %s", ofperr_get_name(retval)); } break; } struct ds s = DS_EMPTY_INITIALIZER; ofp_print_table_features(&s, &tf, n ? &prev : NULL, NULL, NULL); puts(ds_cstr(&s)); ds_destroy(&s); prev = tf; n++; } } else { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, verbosity + 1)); } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } vconn_close(vconn); } static void ofctl_dump_table_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); if (request) { dump_stats_transaction(vconn, request); } vconn_close(vconn); } static bool fetch_port_by_stats(struct vconn *, const char *port_name, ofp_port_t port_no, struct ofputil_phy_port *); /* Uses OFPT_FEATURES_REQUEST to attempt to fetch information about the port * named 'port_name' or numbered 'port_no' into '*pp'. Returns true if * successful, false on failure. * * This is only appropriate for OpenFlow 1.0, 1.1, and 1.2, which include a * list of ports in OFPT_FEATURES_REPLY. */ static bool fetch_port_by_features(struct vconn *vconn, const char *port_name, ofp_port_t port_no, struct ofputil_phy_port *pp) { struct ofputil_switch_features features; const struct ofp_header *oh; struct ofpbuf *request, *reply; enum ofperr error; enum ofptype type; struct ofpbuf b; bool found = false; /* Fetch the switch's ofp_switch_features. */ request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, vconn_get_version(vconn), 0); run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); oh = reply->data; if (ofptype_decode(&type, reply->data) || type != OFPTYPE_FEATURES_REPLY) { ovs_fatal(0, "%s: received bad features reply", vconn_get_name(vconn)); } if (!ofputil_switch_features_has_ports(reply)) { /* The switch features reply does not contain a complete list of ports. * Probably, there are more ports than will fit into a single 64 kB * OpenFlow message. Use OFPST_PORT_DESC to get a complete list of * ports. */ ofpbuf_delete(reply); return fetch_port_by_stats(vconn, port_name, port_no, pp); } error = ofputil_decode_switch_features(oh, &features, &b); if (error) { ovs_fatal(0, "%s: failed to decode features reply (%s)", vconn_get_name(vconn), ofperr_to_string(error)); } while (!ofputil_pull_phy_port(oh->version, &b, pp)) { if (port_no != OFPP_NONE ? port_no == pp->port_no : !strcmp(pp->name, port_name)) { found = true; break; } } ofpbuf_delete(reply); return found; } /* Uses a OFPST_PORT_DESC request to attempt to fetch information about the * port named 'port_name' or numbered 'port_no' into '*pp'. Returns true if * successful, false on failure. * * This is most appropriate for OpenFlow 1.3 and later. Open vSwitch 1.7 and * later also implements OFPST_PORT_DESC, as an extension, for OpenFlow 1.0, * 1.1, and 1.2, so this can be used as a fallback in those versions when there * are too many ports than fit in an OFPT_FEATURES_REPLY. */ static bool fetch_port_by_stats(struct vconn *vconn, const char *port_name, ofp_port_t port_no, struct ofputil_phy_port *pp) { struct ofpbuf *request; ovs_be32 send_xid; bool done = false; bool found = false; request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn), port_no); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { struct ofp_header *oh = reply->data; enum ofptype type; struct ofpbuf b; uint16_t flags; ofpbuf_use_const(&b, oh, ntohs(oh->length)); if (ofptype_pull(&type, &b) || type != OFPTYPE_PORT_DESC_STATS_REPLY) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, verbosity + 1)); } flags = ofpmp_flags(oh); done = !(flags & OFPSF_REPLY_MORE); if (found) { /* We've already found the port, but we need to drain * the queue of any other replies for this request. */ continue; } while (!ofputil_pull_phy_port(oh->version, &b, pp)) { if (port_no != OFPP_NONE ? port_no == pp->port_no : !strcmp(pp->name, port_name)) { found = true; break; } } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } return found; } static bool str_to_ofp(const char *s, ofp_port_t *ofp_port) { bool ret; uint32_t port_; ret = str_to_uint(s, 10, &port_); *ofp_port = u16_to_ofp(port_); return ret; } /* Opens a connection to 'vconn_name', fetches the port structure for * 'port_name' (which may be a port name or number), and copies it into * '*pp'. */ static void fetch_ofputil_phy_port(const char *vconn_name, const char *port_name, struct ofputil_phy_port *pp) { struct vconn *vconn; ofp_port_t port_no; bool found; /* Try to interpret the argument as a port number. */ if (!str_to_ofp(port_name, &port_no)) { port_no = OFPP_NONE; } /* OpenFlow 1.0, 1.1, and 1.2 put the list of ports in the * OFPT_FEATURES_REPLY message. OpenFlow 1.3 and later versions put it * into the OFPST_PORT_DESC reply. Try it the correct way. */ open_vconn(vconn_name, &vconn); found = (vconn_get_version(vconn) < OFP13_VERSION ? fetch_port_by_features(vconn, port_name, port_no, pp) : fetch_port_by_stats(vconn, port_name, port_no, pp)); vconn_close(vconn); if (!found) { ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name); } } /* Returns the port number corresponding to 'port_name' (which may be a port * name or number) within the switch 'vconn_name'. */ static ofp_port_t str_to_port_no(const char *vconn_name, const char *port_name) { ofp_port_t port_no; if (ofputil_port_from_string(port_name, &port_no)) { return port_no; } else { struct ofputil_phy_port pp; fetch_ofputil_phy_port(vconn_name, port_name, &pp); return pp.port_no; } } static bool try_set_protocol(struct vconn *vconn, enum ofputil_protocol want, enum ofputil_protocol *cur) { for (;;) { struct ofpbuf *request, *reply; enum ofputil_protocol next; request = ofputil_encode_set_protocol(*cur, want, &next); if (!request) { return *cur == want; } run(vconn_transact_noreply(vconn, request, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { char *s = ofp_to_string(reply->data, reply->size, 2); VLOG_DBG("%s: failed to set protocol, switch replied: %s", vconn_get_name(vconn), s); free(s); ofpbuf_delete(reply); return false; } *cur = next; } } static enum ofputil_protocol set_protocol_for_flow_dump(struct vconn *vconn, enum ofputil_protocol cur_protocol, enum ofputil_protocol usable_protocols) { char *usable_s; int i; for (i = 0; i < ofputil_n_flow_dump_protocols; i++) { enum ofputil_protocol f = ofputil_flow_dump_protocols[i]; if (f & usable_protocols & allowed_protocols && try_set_protocol(vconn, f, &cur_protocol)) { return f; } } usable_s = ofputil_protocols_to_string(usable_protocols); if (usable_protocols & allowed_protocols) { ovs_fatal(0, "switch does not support any of the usable flow " "formats (%s)", usable_s); } else { char *allowed_s = ofputil_protocols_to_string(allowed_protocols); ovs_fatal(0, "none of the usable flow formats (%s) is among the " "allowed flow formats (%s)", usable_s, allowed_s); } } static struct vconn * prepare_dump_flows(int argc, char *argv[], bool aggregate, struct ofpbuf **requestp) { enum ofputil_protocol usable_protocols, protocol; struct ofputil_flow_stats_request fsr; struct vconn *vconn; char *error; error = parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "", &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } protocol = open_vconn(argv[1], &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); *requestp = ofputil_encode_flow_stats_request(&fsr, protocol); return vconn; } static void ofctl_dump_flows__(int argc, char *argv[], bool aggregate) { struct ofpbuf *request; struct vconn *vconn; vconn = prepare_dump_flows(argc, argv, aggregate, &request); dump_stats_transaction(vconn, request); vconn_close(vconn); } static void get_match_field(const struct mf_field *field, const struct match *match, union mf_value *value) { if (!match->tun_md.valid || (field->id < MFF_TUN_METADATA0 || field->id >= MFF_TUN_METADATA0 + TUN_METADATA_NUM_OPTS)) { mf_get_value(field, &match->flow, value); } else { const struct tun_metadata_loc *loc = &match->tun_md.entry[field->id - MFF_TUN_METADATA0].loc; /* Since we don't have a tunnel mapping table, extract the value * from the locally allocated location in the match. */ memset(value, 0, field->n_bytes - loc->len); memcpy(value->tun_metadata + field->n_bytes - loc->len, match->flow.tunnel.metadata.opts.u8 + loc->c.offset, loc->len); } } static int compare_flows(const void *afs_, const void *bfs_) { const struct ofputil_flow_stats *afs = afs_; const struct ofputil_flow_stats *bfs = bfs_; const struct match *a = &afs->match; const struct match *b = &bfs->match; const struct sort_criterion *sc; for (sc = criteria; sc < &criteria[n_criteria]; sc++) { const struct mf_field *f = sc->field; int ret; if (!f) { int a_pri = afs->priority; int b_pri = bfs->priority; ret = a_pri < b_pri ? -1 : a_pri > b_pri; } else { bool ina, inb; ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc); inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc); if (ina != inb) { /* Skip the test for sc->order, so that missing fields always * sort to the end whether we're sorting in ascending or * descending order. */ return ina ? -1 : 1; } else { union mf_value aval, bval; get_match_field(f, a, &aval); get_match_field(f, b, &bval); ret = memcmp(&aval, &bval, f->n_bytes); } } if (ret) { return sc->order == SORT_ASC ? ret : -ret; } } return 0; } static void ofctl_dump_flows(struct ovs_cmdl_context *ctx) { if (!n_criteria) { ofctl_dump_flows__(ctx->argc, ctx->argv, false); return; } else { struct ofputil_flow_stats *fses; size_t n_fses, allocated_fses; struct ofpbuf *request; struct ofpbuf ofpacts; struct ofpbuf *reply; struct vconn *vconn; ovs_be32 send_xid; struct ds s; size_t i; vconn = prepare_dump_flows(ctx->argc, ctx->argv, false, &request); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); fses = NULL; n_fses = allocated_fses = 0; reply = NULL; ofpbuf_init(&ofpacts, 0); for (;;) { struct ofputil_flow_stats *fs; if (n_fses >= allocated_fses) { fses = x2nrealloc(fses, &allocated_fses, sizeof *fses); } fs = &fses[n_fses]; if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs, &ofpacts)) { break; } fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len); n_fses++; } ofpbuf_uninit(&ofpacts); qsort(fses, n_fses, sizeof *fses, compare_flows); ds_init(&s); for (i = 0; i < n_fses; i++) { ds_clear(&s); ofp_print_flow_stats(&s, &fses[i]); puts(ds_cstr(&s)); } ds_destroy(&s); for (i = 0; i < n_fses; i++) { free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); } free(fses); vconn_close(vconn); } } static void ofctl_dump_aggregate(struct ovs_cmdl_context *ctx) { ofctl_dump_flows__(ctx->argc, ctx->argv, true); } static void ofctl_queue_stats(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; struct ofputil_queue_stats_request oqs; open_vconn(ctx->argv[1], &vconn); if (ctx->argc > 2 && ctx->argv[2][0] && strcasecmp(ctx->argv[2], "all")) { oqs.port_no = str_to_port_no(ctx->argv[1], ctx->argv[2]); } else { oqs.port_no = OFPP_ANY; } if (ctx->argc > 3 && ctx->argv[3][0] && strcasecmp(ctx->argv[3], "all")) { oqs.queue_id = atoi(ctx->argv[3]); } else { oqs.queue_id = OFPQ_ALL; } request = ofputil_encode_queue_stats_request(vconn_get_version(vconn), &oqs); dump_stats_transaction(vconn, request); vconn_close(vconn); } static void ofctl_queue_get_config(struct ovs_cmdl_context *ctx) { const char *vconn_name = ctx->argv[1]; const char *port_name = ctx->argv[2]; enum ofputil_protocol protocol; enum ofp_version version; struct ofpbuf *request; struct vconn *vconn; ofp_port_t port; port = str_to_port_no(vconn_name, port_name); protocol = open_vconn(vconn_name, &vconn); version = ofputil_protocol_to_ofp_version(protocol); request = ofputil_encode_queue_get_config_request(version, port); dump_transaction(vconn, request); vconn_close(vconn); } static enum ofputil_protocol open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp, enum ofputil_protocol usable_protocols) { enum ofputil_protocol cur_protocol; char *usable_s; int i; if (!(usable_protocols & allowed_protocols)) { char *allowed_s = ofputil_protocols_to_string(allowed_protocols); usable_s = ofputil_protocols_to_string(usable_protocols); ovs_fatal(0, "none of the usable flow formats (%s) is among the " "allowed flow formats (%s)", usable_s, allowed_s); } /* If the initial flow format is allowed and usable, keep it. */ cur_protocol = open_vconn(remote, vconnp); if (usable_protocols & allowed_protocols & cur_protocol) { return cur_protocol; } /* Otherwise try each flow format in turn. */ for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { enum ofputil_protocol f = 1 << i; if (f != cur_protocol && f & usable_protocols & allowed_protocols && try_set_protocol(*vconnp, f, &cur_protocol)) { return f; } } usable_s = ofputil_protocols_to_string(usable_protocols); ovs_fatal(0, "switch does not support any of the usable flow " "formats (%s)", usable_s); } static void bundle_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct vconn *vconn; struct ovs_list requests; size_t i; list_init(&requests); /* Bundles need OpenFlow 1.4+. */ usable_protocols &= OFPUTIL_P_OF14_UP; protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *request = ofputil_encode_flow_mod(fm, protocol); list_push_back(&requests, &request->list_node); free(CONST_CAST(struct ofpact *, fm->ofpacts)); } bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); vconn_close(vconn); } static void ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct vconn *vconn; size_t i; if (bundle) { bundle_flow_mod__(remote, fms, n_fms, usable_protocols); return; } protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol)); free(CONST_CAST(struct ofpact *, fm->ofpacts)); } vconn_close(vconn); } static void ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod *fms = NULL; size_t n_fms = 0; char *error; if (command == OFPFC_ADD) { /* Allow the file to specify a mix of commands. If none specified at * the beginning of any given line, then the default is OFPFC_ADD, so * this is backwards compatible. */ command = -2; } error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols); free(fms); } static void ofctl_flow_mod(int argc, char *argv[], uint16_t command) { if (argc > 2 && !strcmp(argv[2], "-")) { ofctl_flow_mod_file(argc, argv, command); } else { struct ofputil_flow_mod fm; char *error; enum ofputil_protocol usable_protocols; error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols); } } static void ofctl_add_flow(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, OFPFC_ADD); } static void ofctl_add_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod_file(ctx->argc, ctx->argv, OFPFC_ADD); } static void ofctl_mod_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY); } static void ofctl_del_flows(struct ovs_cmdl_context *ctx) { ofctl_flow_mod(ctx->argc, ctx->argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE); } static void set_packet_in_format(struct vconn *vconn, enum nx_packet_in_format packet_in_format) { struct ofpbuf *spif; spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn), packet_in_format); transact_noreply(vconn, spif); VLOG_DBG("%s: using user-specified packet in format %s", vconn_get_name(vconn), ofputil_packet_in_format_to_string(packet_in_format)); } static int monitor_set_invalid_ttl_to_controller(struct vconn *vconn) { struct ofp_switch_config config; enum ofp_config_flags flags; fetch_switch_config(vconn, &config); flags = ntohs(config.flags); if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) { /* Set the invalid ttl config. */ flags |= OFPC_INVALID_TTL_TO_CONTROLLER; config.flags = htons(flags); set_switch_config(vconn, &config); /* Then retrieve the configuration to see if it really took. OpenFlow * doesn't define error reporting for bad modes, so this is all we can * do. */ fetch_switch_config(vconn, &config); flags = ntohs(config.flags); if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) { ovs_fatal(0, "setting invalid_ttl_to_controller failed (this " "switch probably doesn't support mode)"); return -EOPNOTSUPP; } } return 0; } /* Converts hex digits in 'hex' to an OpenFlow message in '*msgp'. The * caller must free '*msgp'. On success, returns NULL. On failure, returns * an error message and stores NULL in '*msgp'. */ static const char * openflow_from_hex(const char *hex, struct ofpbuf **msgp) { struct ofp_header *oh; struct ofpbuf *msg; msg = ofpbuf_new(strlen(hex) / 2); *msgp = NULL; if (ofpbuf_put_hex(msg, hex, NULL)[0] != '\0') { ofpbuf_delete(msg); return "Trailing garbage in hex data"; } if (msg->size < sizeof(struct ofp_header)) { ofpbuf_delete(msg); return "Message too short for OpenFlow"; } oh = msg->data; if (msg->size != ntohs(oh->length)) { ofpbuf_delete(msg); return "Message size does not match length in OpenFlow header"; } *msgp = msg; return NULL; } static void ofctl_send(struct unixctl_conn *conn, int argc, const char *argv[], void *vconn_) { struct vconn *vconn = vconn_; struct ds reply; bool ok; int i; ok = true; ds_init(&reply); for (i = 1; i < argc; i++) { const char *error_msg; struct ofpbuf *msg; int error; error_msg = openflow_from_hex(argv[i], &msg); if (error_msg) { ds_put_format(&reply, "%s\n", error_msg); ok = false; continue; } fprintf(stderr, "send: "); ofp_print(stderr, msg->data, msg->size, verbosity); error = vconn_send_block(vconn, msg); if (error) { ofpbuf_delete(msg); ds_put_format(&reply, "%s\n", ovs_strerror(error)); ok = false; } else { ds_put_cstr(&reply, "sent\n"); } } if (ok) { unixctl_command_reply(conn, ds_cstr(&reply)); } else { unixctl_command_reply_error(conn, ds_cstr(&reply)); } ds_destroy(&reply); } struct barrier_aux { struct vconn *vconn; /* OpenFlow connection for sending barrier. */ struct unixctl_conn *conn; /* Connection waiting for barrier response. */ }; static void ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux_) { struct barrier_aux *aux = aux_; struct ofpbuf *msg; int error; if (aux->conn) { unixctl_command_reply_error(conn, "already waiting for barrier reply"); return; } msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn)); error = vconn_send_block(aux->vconn, msg); if (error) { ofpbuf_delete(msg); unixctl_command_reply_error(conn, ovs_strerror(error)); } else { aux->conn = conn; } } static void ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { int fd; fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666); if (fd < 0) { unixctl_command_reply_error(conn, ovs_strerror(errno)); return; } fflush(stderr); dup2(fd, STDERR_FILENO); close(fd); unixctl_command_reply(conn, NULL); } static void ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (!*blocked) { *blocked = true; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already blocking"); } } static void ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *blocked_) { bool *blocked = blocked_; if (*blocked) { *blocked = false; unixctl_command_reply(conn, NULL); } else { unixctl_command_reply(conn, "already unblocked"); } } /* Prints to stdout all of the messages received on 'vconn'. * * Iff 'reply_to_echo_requests' is true, sends a reply to any echo request * received on 'vconn'. */ static void monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests) { struct barrier_aux barrier_aux = { vconn, NULL }; struct unixctl_server *server; bool exiting = false; bool blocked = false; int error; daemon_save_fd(STDERR_FILENO); daemonize_start(false); error = unixctl_server_create(unixctl_path, &server); if (error) { ovs_fatal(error, "failed to create unixctl server"); } unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting); unixctl_command_register("ofctl/send", "OFMSG...", 1, INT_MAX, ofctl_send, vconn); unixctl_command_register("ofctl/barrier", "", 0, 0, ofctl_barrier, &barrier_aux); unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1, ofctl_set_output_file, NULL); unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked); unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock, &blocked); daemonize_complete(); for (;;) { struct ofpbuf *b; int retval; unixctl_server_run(server); while (!blocked) { enum ofptype type; retval = vconn_recv(vconn, &b); if (retval == EAGAIN) { break; } run(retval, "vconn_recv"); if (timestamp) { char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ", time_wall_msec(), true); fputs(s, stderr); free(s); } ofptype_decode(&type, b->data); ofp_print(stderr, b->data, b->size, verbosity + 2); fflush(stderr); switch ((int) type) { case OFPTYPE_BARRIER_REPLY: if (barrier_aux.conn) { unixctl_command_reply(barrier_aux.conn, NULL); barrier_aux.conn = NULL; } break; case OFPTYPE_ECHO_REQUEST: if (reply_to_echo_requests) { struct ofpbuf *reply; reply = make_echo_reply(b->data); retval = vconn_send_block(vconn, reply); if (retval) { ovs_fatal(retval, "failed to send echo reply"); } } break; } ofpbuf_delete(b); } if (exiting) { break; } vconn_run(vconn); vconn_run_wait(vconn); if (!blocked) { vconn_recv_wait(vconn); } unixctl_server_wait(server); poll_block(); } vconn_close(vconn); unixctl_server_destroy(server); } static void ofctl_monitor(struct ovs_cmdl_context *ctx) { struct vconn *vconn; int i; enum ofputil_protocol usable_protocols; open_vconn(ctx->argv[1], &vconn); for (i = 2; i < ctx->argc; i++) { const char *arg = ctx->argv[i]; if (isdigit((unsigned char) *arg)) { struct ofp_switch_config config; fetch_switch_config(vconn, &config); config.miss_send_len = htons(atoi(arg)); set_switch_config(vconn, &config); } else if (!strcmp(arg, "invalid_ttl")) { monitor_set_invalid_ttl_to_controller(vconn); } else if (!strncmp(arg, "watch:", 6)) { struct ofputil_flow_monitor_request fmr; struct ofpbuf *msg; char *error; error = parse_flow_monitor_request(&fmr, arg + 6, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } msg = ofpbuf_new(0); ofputil_append_flow_monitor_request(&fmr, msg); dump_stats_transaction(vconn, msg); fflush(stdout); } else { ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg); } } if (preferred_packet_in_format >= 0) { set_packet_in_format(vconn, preferred_packet_in_format); } else { enum ofp_version version = vconn_get_version(vconn); switch (version) { case OFP10_VERSION: { struct ofpbuf *spif, *reply; spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn), NXPIF_NXM); run(vconn_transact_noreply(vconn, spif, &reply), "talking to %s", vconn_get_name(vconn)); if (reply) { char *s = ofp_to_string(reply->data, reply->size, 2); VLOG_DBG("%s: failed to set packet in format to nxm, controller" " replied: %s. Falling back to the switch default.", vconn_get_name(vconn), s); free(s); ofpbuf_delete(reply); } break; } case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: break; default: OVS_NOT_REACHED(); } } monitor_vconn(vconn, true); } static void ofctl_snoop(struct ovs_cmdl_context *ctx) { struct vconn *vconn; open_vconn__(ctx->argv[1], SNOOP, &vconn); monitor_vconn(vconn, false); } static void ofctl_dump_ports(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; ofp_port_t port; open_vconn(ctx->argv[1], &vconn); port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY; request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port); dump_stats_transaction(vconn, request); vconn_close(vconn); } static void ofctl_dump_ports_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; ofp_port_t port; open_vconn(ctx->argv[1], &vconn); port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY; request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn), port); dump_stats_transaction(vconn, request); vconn_close(vconn); } static void ofctl_probe(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; struct ofpbuf *reply; open_vconn(ctx->argv[1], &vconn); request = make_echo_request(vconn_get_version(vconn)); run(vconn_transact(vconn, request, &reply), "talking to %s", ctx->argv[1]); if (reply->size != sizeof(struct ofp_header)) { ovs_fatal(0, "reply does not match request"); } ofpbuf_delete(reply); vconn_close(vconn); } static void ofctl_packet_out(struct ovs_cmdl_context *ctx) { enum ofputil_protocol protocol; struct ofputil_packet_out po; struct ofpbuf ofpacts; struct vconn *vconn; char *error; int i; enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */ ofpbuf_init(&ofpacts, 64); error = ofpacts_parse_actions(ctx->argv[3], &ofpacts, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } po.buffer_id = UINT32_MAX; po.in_port = str_to_port_no(ctx->argv[1], ctx->argv[2]); po.ofpacts = ofpacts.data; po.ofpacts_len = ofpacts.size; protocol = open_vconn(ctx->argv[1], &vconn); for (i = 4; i < ctx->argc; i++) { struct dp_packet *packet; struct ofpbuf *opo; const char *error_msg; error_msg = eth_from_hex(ctx->argv[i], &packet); if (error_msg) { ovs_fatal(0, "%s", error_msg); } po.packet = dp_packet_data(packet); po.packet_len = dp_packet_size(packet); opo = ofputil_encode_packet_out(&po, protocol); transact_noreply(vconn, opo); dp_packet_delete(packet); } vconn_close(vconn); ofpbuf_uninit(&ofpacts); } static void ofctl_mod_port(struct ovs_cmdl_context *ctx) { struct ofp_config_flag { const char *name; /* The flag's name. */ enum ofputil_port_config bit; /* Bit to turn on or off. */ bool on; /* Value to set the bit to. */ }; static const struct ofp_config_flag flags[] = { { "up", OFPUTIL_PC_PORT_DOWN, false }, { "down", OFPUTIL_PC_PORT_DOWN, true }, { "stp", OFPUTIL_PC_NO_STP, false }, { "receive", OFPUTIL_PC_NO_RECV, false }, { "receive-stp", OFPUTIL_PC_NO_RECV_STP, false }, { "flood", OFPUTIL_PC_NO_FLOOD, false }, { "forward", OFPUTIL_PC_NO_FWD, false }, { "packet-in", OFPUTIL_PC_NO_PACKET_IN, false }, }; const struct ofp_config_flag *flag; enum ofputil_protocol protocol; struct ofputil_port_mod pm; struct ofputil_phy_port pp; struct vconn *vconn; const char *command; bool not; fetch_ofputil_phy_port(ctx->argv[1], ctx->argv[2], &pp); pm.port_no = pp.port_no; pm.hw_addr = pp.hw_addr; pm.config = 0; pm.mask = 0; pm.advertise = 0; if (!strncasecmp(ctx->argv[3], "no-", 3)) { command = ctx->argv[3] + 3; not = true; } else if (!strncasecmp(ctx->argv[3], "no", 2)) { command = ctx->argv[3] + 2; not = true; } else { command = ctx->argv[3]; not = false; } for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) { if (!strcasecmp(command, flag->name)) { pm.mask = flag->bit; pm.config = flag->on ^ not ? flag->bit : 0; goto found; } } ovs_fatal(0, "unknown mod-port command '%s'", ctx->argv[3]); found: protocol = open_vconn(ctx->argv[1], &vconn); transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol)); vconn_close(vconn); } /* This function uses OFPMP14_TABLE_DESC request to get the current * table configuration from switch. The function then modifies * only that table-config property, which has been requested. */ static void fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm, struct ofputil_table_desc *td) { struct ofpbuf *request; ovs_be32 send_xid; bool done = false; bool found = false; request = ofputil_encode_table_desc_request(vconn_get_version(vconn)); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); while (!done) { ovs_be32 recv_xid; struct ofpbuf *reply; run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); recv_xid = ((struct ofp_header *) reply->data)->xid; if (send_xid == recv_xid) { struct ofp_header *oh = reply->data; enum ofptype type; struct ofpbuf b; uint16_t flags; ofpbuf_use_const(&b, oh, ntohs(oh->length)); if (ofptype_pull(&type, &b) || type != OFPTYPE_TABLE_DESC_REPLY) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, verbosity + 1)); } flags = ofpmp_flags(oh); done = !(flags & OFPSF_REPLY_MORE); if (found) { /* We've already found the table desc consisting of current * table configuration, but we need to drain the queue of * any other replies for this request. */ continue; } while (!ofputil_decode_table_desc(&b, td, oh->version)) { if (td->table_id == tm->table_id) { found = true; break; } } } else { VLOG_DBG("received reply with xid %08"PRIx32" " "!= expected %08"PRIx32, recv_xid, send_xid); } ofpbuf_delete(reply); } if (tm->eviction != OFPUTIL_TABLE_EVICTION_DEFAULT) { tm->vacancy = td->vacancy; tm->table_vacancy.vacancy_down = td->table_vacancy.vacancy_down; tm->table_vacancy.vacancy_up = td->table_vacancy.vacancy_up; } else if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { tm->eviction = td->eviction; tm->eviction_flags = td->eviction_flags; } } static void ofctl_mod_table(struct ovs_cmdl_context *ctx) { uint32_t usable_versions; struct ofputil_table_mod tm; struct vconn *vconn; char *error; int i; error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3], &usable_versions); if (error) { ovs_fatal(0, "%s", error); } uint32_t allowed_versions = get_allowed_ofp_versions(); if (!(allowed_versions & usable_versions)) { struct ds versions = DS_EMPTY_INITIALIZER; ofputil_format_version_bitmap_names(&versions, usable_versions); ovs_fatal(0, "table_mod '%s' requires one of the OpenFlow " "versions %s", ctx->argv[3], ds_cstr(&versions)); } mask_allowed_ofp_versions(usable_versions); enum ofputil_protocol protocol = open_vconn(ctx->argv[1], &vconn); /* For OpenFlow 1.4+, ovs-ofctl mod-table should not affect table-config * properties that the user didn't ask to change, so it is necessary to * restore the current configuration of table-config parameters using * OFPMP14_TABLE_DESC request. */ if ((allowed_versions & (1u << OFP14_VERSION)) || (allowed_versions & (1u << OFP15_VERSION))) { struct ofputil_table_desc td; if (tm.table_id == OFPTT_ALL) { for (i = 0; i < OFPTT_MAX; i++) { tm.table_id = i; fetch_table_desc(vconn, &tm, &td); transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } } else { fetch_table_desc(vconn, &tm, &td); transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } } else { transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol)); } vconn_close(vconn); } static void ofctl_get_frags(struct ovs_cmdl_context *ctx) { struct ofp_switch_config config; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); fetch_switch_config(vconn, &config); puts(ofputil_frag_handling_to_string(ntohs(config.flags))); vconn_close(vconn); } static void ofctl_set_frags(struct ovs_cmdl_context *ctx) { struct ofp_switch_config config; enum ofp_config_flags mode; struct vconn *vconn; ovs_be16 flags; if (!ofputil_frag_handling_from_string(ctx->argv[2], &mode)) { ovs_fatal(0, "%s: unknown fragment handling mode", ctx->argv[2]); } open_vconn(ctx->argv[1], &vconn); fetch_switch_config(vconn, &config); flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK)); if (flags != config.flags) { /* Set the configuration. */ config.flags = flags; set_switch_config(vconn, &config); /* Then retrieve the configuration to see if it really took. OpenFlow * doesn't define error reporting for bad modes, so this is all we can * do. */ fetch_switch_config(vconn, &config); if (flags != config.flags) { ovs_fatal(0, "%s: setting fragment handling mode failed (this " "switch probably doesn't support mode \"%s\")", ctx->argv[1], ofputil_frag_handling_to_string(mode)); } } vconn_close(vconn); } static void ofctl_ofp_parse(struct ovs_cmdl_context *ctx) { const char *filename = ctx->argv[1]; struct ofpbuf b; FILE *file; file = !strcmp(filename, "-") ? stdin : fopen(filename, "r"); if (file == NULL) { ovs_fatal(errno, "%s: open", filename); } ofpbuf_init(&b, 65536); for (;;) { struct ofp_header *oh; size_t length, tail_len; void *tail; size_t n; ofpbuf_clear(&b); oh = ofpbuf_put_uninit(&b, sizeof *oh); n = fread(oh, 1, sizeof *oh, file); if (n == 0) { break; } else if (n < sizeof *oh) { ovs_fatal(0, "%s: unexpected end of file mid-message", filename); } length = ntohs(oh->length); if (length < sizeof *oh) { ovs_fatal(0, "%s: %"PRIuSIZE"-byte message is too short for OpenFlow", filename, length); } tail_len = length - sizeof *oh; tail = ofpbuf_put_uninit(&b, tail_len); n = fread(tail, 1, tail_len, file); if (n < tail_len) { ovs_fatal(0, "%s: unexpected end of file mid-message", filename); } ofp_print(stdout, b.data, b.size, verbosity + 2); } ofpbuf_uninit(&b); if (file != stdin) { fclose(file); } } static bool is_openflow_port(ovs_be16 port_, char *ports[]) { uint16_t port = ntohs(port_); if (ports[0]) { int i; for (i = 0; ports[i]; i++) { if (port == atoi(ports[i])) { return true; } } return false; } else { return port == OFP_PORT || port == OFP_OLD_PORT; } } static void ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) { struct tcp_reader *reader; FILE *file; int error; bool first; file = ovs_pcap_open(ctx->argv[1], "rb"); if (!file) { ovs_fatal(errno, "%s: open failed", ctx->argv[1]); } reader = tcp_reader_open(); first = true; for (;;) { struct dp_packet *packet; long long int when; struct flow flow; error = ovs_pcap_read(file, &packet, &when); if (error) { break; } pkt_metadata_init(&packet->md, ODPP_NONE); flow_extract(packet, &flow); if (flow.dl_type == htons(ETH_TYPE_IP) && flow.nw_proto == IPPROTO_TCP && (is_openflow_port(flow.tp_src, ctx->argv + 2) || is_openflow_port(flow.tp_dst, ctx->argv + 2))) { struct dp_packet *payload = tcp_reader_run(reader, &flow, packet); if (payload) { while (dp_packet_size(payload) >= sizeof(struct ofp_header)) { const struct ofp_header *oh; void *data = dp_packet_data(payload); int length; /* Align OpenFlow on 8-byte boundary for safe access. */ dp_packet_shift(payload, -((intptr_t) data & 7)); oh = dp_packet_data(payload); length = ntohs(oh->length); if (dp_packet_size(payload) < length) { break; } if (!first) { putchar('\n'); } first = false; if (timestamp) { char *s = xastrftime_msec("%H:%M:%S.### ", when, true); fputs(s, stdout); free(s); } printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n", IP_ARGS(flow.nw_src), ntohs(flow.tp_src), IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst)); ofp_print(stdout, dp_packet_data(payload), length, verbosity + 1); dp_packet_pull(payload, length); } } } dp_packet_delete(packet); } tcp_reader_close(reader); } static void ofctl_ping(struct ovs_cmdl_context *ctx) { size_t max_payload = 65535 - sizeof(struct ofp_header); unsigned int payload; struct vconn *vconn; int i; payload = ctx->argc > 2 ? atoi(ctx->argv[2]) : 64; if (payload > max_payload) { ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload); } open_vconn(ctx->argv[1], &vconn); for (i = 0; i < 10; i++) { struct timeval start, end; struct ofpbuf *request, *reply; const struct ofp_header *rpy_hdr; enum ofptype type; request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, vconn_get_version(vconn), payload); random_bytes(ofpbuf_put_uninit(request, payload), payload); xgettimeofday(&start); run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact"); xgettimeofday(&end); rpy_hdr = reply->data; if (ofptype_pull(&type, reply) || type != OFPTYPE_ECHO_REPLY || reply->size != payload || memcmp(request->msg, reply->msg, payload)) { printf("Reply does not match request. Request:\n"); ofp_print(stdout, request, request->size, verbosity + 2); printf("Reply:\n"); ofp_print(stdout, reply, reply->size, verbosity + 2); } printf("%"PRIu32" bytes from %s: xid=%08"PRIx32" time=%.1f ms\n", reply->size, ctx->argv[1], ntohl(rpy_hdr->xid), (1000*(double)(end.tv_sec - start.tv_sec)) + (.001*(end.tv_usec - start.tv_usec))); ofpbuf_delete(request); ofpbuf_delete(reply); } vconn_close(vconn); } static void ofctl_benchmark(struct ovs_cmdl_context *ctx) { size_t max_payload = 65535 - sizeof(struct ofp_header); struct timeval start, end; unsigned int payload_size, message_size; struct vconn *vconn; double duration; int count; int i; payload_size = atoi(ctx->argv[2]); if (payload_size > max_payload) { ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload); } message_size = sizeof(struct ofp_header) + payload_size; count = atoi(ctx->argv[3]); printf("Sending %d packets * %u bytes (with header) = %u bytes total\n", count, message_size, count * message_size); open_vconn(ctx->argv[1], &vconn); xgettimeofday(&start); for (i = 0; i < count; i++) { struct ofpbuf *request, *reply; request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST, vconn_get_version(vconn), payload_size); ofpbuf_put_zeros(request, payload_size); run(vconn_transact(vconn, request, &reply), "transact"); ofpbuf_delete(reply); } xgettimeofday(&end); vconn_close(vconn); duration = ((1000*(double)(end.tv_sec - start.tv_sec)) + (.001*(end.tv_usec - start.tv_usec))); printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n", duration, count / (duration / 1000.0), count * message_size / (duration / 1000.0)); } static void ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms, size_t n_gms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol; struct ofputil_group_mod *gm; enum ofp_version version; struct ofpbuf *request; struct vconn *vconn; size_t i; protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); for (i = 0; i < n_gms; i++) { gm = &gms[i]; request = ofputil_encode_group_mod(version, gm); if (request) { transact_noreply(vconn, request); } } vconn_close(vconn); } static void ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command) { struct ofputil_group_mod *gms = NULL; enum ofputil_protocol usable_protocols; size_t n_gms = 0; char *error; int i; error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_group_mod__(argv[1], gms, n_gms, usable_protocols); for (i = 0; i < n_gms; i++) { ofputil_bucket_list_destroy(&gms[i].buckets); } free(gms); } static void ofctl_group_mod(int argc, char *argv[], uint16_t command) { if (argc > 2 && !strcmp(argv[2], "-")) { ofctl_group_mod_file(argc, argv, command); } else { enum ofputil_protocol usable_protocols; struct ofputil_group_mod gm; char *error; error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "", &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_group_mod__(argv[1], &gm, 1, usable_protocols); ofputil_bucket_list_destroy(&gm.buckets); } } static void ofctl_add_group(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC11_ADD); } static void ofctl_add_groups(struct ovs_cmdl_context *ctx) { ofctl_group_mod_file(ctx->argc, ctx->argv, OFPGC11_ADD); } static void ofctl_mod_group(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC11_MODIFY); } static void ofctl_del_groups(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC11_DELETE); } static void ofctl_insert_bucket(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC15_INSERT_BUCKET); } static void ofctl_remove_bucket(struct ovs_cmdl_context *ctx) { ofctl_group_mod(ctx->argc, ctx->argv, OFPGC15_REMOVE_BUCKET); } static void ofctl_dump_group_stats(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_group_mod gm; struct ofpbuf *request; struct vconn *vconn; uint32_t group_id; char *error; memset(&gm, 0, sizeof gm); error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE, ctx->argc > 2 ? ctx->argv[2] : "", &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } group_id = gm.group_id; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_group_stats_request(vconn_get_version(vconn), group_id); if (request) { dump_stats_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_dump_group_desc(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; uint32_t group_id; open_vconn(ctx->argv[1], &vconn); if (ctx->argc < 3 || !ofputil_group_from_string(ctx->argv[2], &group_id)) { group_id = OFPG_ALL; } request = ofputil_encode_group_desc_request(vconn_get_version(vconn), group_id); if (request) { dump_stats_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_dump_group_features(struct ovs_cmdl_context *ctx) { struct ofpbuf *request; struct vconn *vconn; open_vconn(ctx->argv[1], &vconn); request = ofputil_encode_group_features_request(vconn_get_version(vconn)); if (request) { dump_stats_transaction(vconn, request); } vconn_close(vconn); } static void ofctl_tlv_mod(struct ovs_cmdl_context *ctx, uint16_t command) { enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; struct ofputil_tlv_table_mod ttm; char *error; enum ofp_version version; struct ofpbuf *request; struct vconn *vconn; error = parse_ofp_tlv_table_mod_str(&ttm, command, ctx->argc > 2 ? ctx->argv[2] : "", &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); request = ofputil_encode_tlv_table_mod(version, &ttm); if (request) { transact_noreply(vconn, request); } vconn_close(vconn); ofputil_uninit_tlv_table(&ttm.mappings); } static void ofctl_add_tlv_map(struct ovs_cmdl_context *ctx) { ofctl_tlv_mod(ctx, NXTTMC_ADD); } static void ofctl_del_tlv_map(struct ovs_cmdl_context *ctx) { ofctl_tlv_mod(ctx, ctx->argc > 2 ? NXTTMC_DELETE : NXTTMC_CLEAR); } static void ofctl_dump_tlv_map(struct ovs_cmdl_context *ctx) { dump_trivial_transaction(ctx->argv[1], OFPRAW_NXT_TLV_TABLE_REQUEST); } static void ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } static void ofctl_list_commands(struct ovs_cmdl_context *ctx OVS_UNUSED) { ovs_cmdl_print_commands(get_all_commands()); } /* replace-flows and diff-flows commands. */ struct flow_tables { struct classifier tables[OFPTT_MAX + 1]; }; #define FOR_EACH_TABLE(CLS, TABLES) \ for ((CLS) = (TABLES)->tables; \ (CLS) < &(TABLES)->tables[ARRAY_SIZE((TABLES)->tables)]; \ (CLS)++) static void flow_tables_init(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_init(cls, NULL); } } static void flow_tables_defer(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_defer(cls); } } static void flow_tables_publish(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { classifier_publish(cls); } } /* A flow table entry, possibly with two different versions. */ struct fte { struct cls_rule rule; /* Within a "struct classifier". */ struct fte_version *versions[2]; }; /* One version of a Flow Table Entry. */ struct fte_version { ovs_be64 cookie; uint16_t idle_timeout; uint16_t hard_timeout; uint16_t importance; uint16_t flags; struct ofpact *ofpacts; size_t ofpacts_len; uint8_t table_id; }; /* Frees 'version' and the data that it owns. */ static void fte_version_free(struct fte_version *version) { if (version) { free(CONST_CAST(struct ofpact *, version->ofpacts)); free(version); } } /* Returns true if 'a' and 'b' are the same, false if they differ. * * Ignores differences in 'flags' because there's no way to retrieve flags from * an OpenFlow switch. We have to assume that they are the same. */ static bool fte_version_equals(const struct fte_version *a, const struct fte_version *b) { return (a->cookie == b->cookie && a->idle_timeout == b->idle_timeout && a->hard_timeout == b->hard_timeout && a->importance == b->importance && a->table_id == b->table_id && ofpacts_equal(a->ofpacts, a->ofpacts_len, b->ofpacts, b->ofpacts_len)); } /* Clears 's', then if 's' has a version 'index', formats 'fte' and version * 'index' into 's', followed by a new-line. */ static void fte_version_format(const struct fte *fte, int index, struct ds *s) { const struct fte_version *version = fte->versions[index]; ds_clear(s); if (!version) { return; } if (version->table_id) { ds_put_format(s, "table=%"PRIu8" ", version->table_id); } cls_rule_format(&fte->rule, s); if (version->cookie != htonll(0)) { ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie)); } if (version->idle_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout); } if (version->hard_timeout != OFP_FLOW_PERMANENT) { ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout); } if (version->importance != 0) { ds_put_format(s, " importance=%"PRIu16, version->importance); } ds_put_cstr(s, " actions="); ofpacts_format(version->ofpacts, version->ofpacts_len, s); ds_put_char(s, '\n'); } static struct fte * fte_from_cls_rule(const struct cls_rule *cls_rule) { return cls_rule ? CONTAINER_OF(cls_rule, struct fte, rule) : NULL; } /* Frees 'fte' and its versions. */ static void fte_free(struct fte *fte) { if (fte) { fte_version_free(fte->versions[0]); fte_version_free(fte->versions[1]); cls_rule_destroy(&fte->rule); free(fte); } } /* Frees all of the FTEs within 'tables'. */ static void fte_free_all(struct flow_tables *tables) { struct classifier *cls; FOR_EACH_TABLE (cls, tables) { struct fte *fte; classifier_defer(cls); CLS_FOR_EACH (fte, rule, cls) { classifier_remove(cls, &fte->rule); ovsrcu_postpone(fte_free, fte); } classifier_destroy(cls); } } /* Searches 'tables' for an FTE matching 'rule', inserting a new one if * necessary. Sets 'version' as the version of that rule with the given * 'index', replacing any existing version, if any. * * Takes ownership of 'version'. */ static void fte_insert(struct flow_tables *tables, const struct match *match, int priority, struct fte_version *version, int index) { struct classifier *cls = &tables->tables[version->table_id]; struct fte *old, *fte; fte = xzalloc(sizeof *fte); cls_rule_init(&fte->rule, match, priority); fte->versions[index] = version; old = fte_from_cls_rule(classifier_replace(cls, &fte->rule, CLS_MIN_VERSION, NULL, 0)); if (old) { fte->versions[!index] = old->versions[!index]; old->versions[!index] = NULL; ovsrcu_postpone(fte_free, old); } } /* A FTE entry that has been queued for later insertion after all * flows have been scanned to correctly allocation tunnel metadata. */ struct fte_pending { struct match *match; int priority; struct fte_version *version; int index; struct ovs_list list_node; }; /* Processing state during two stage processing of flow table entries. * Tracks the maximum size seen for each tunnel metadata entry as well * as a list of the pending FTE entries. */ struct fte_state { int tun_metadata_size[TUN_METADATA_NUM_OPTS]; struct ovs_list fte_pending_list; }; /* Given a list of the field sizes for each tunnel metadata entry, install * a mapping table for later operations. */ static void generate_tun_metadata(struct fte_state *state) { struct ofputil_tlv_table_mod ttm; int i; ttm.command = NXTTMC_ADD; list_init(&ttm.mappings); for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) { if (state->tun_metadata_size[i] != -1) { struct ofputil_tlv_map *map = xmalloc(sizeof *map); list_push_back(&ttm.mappings, &map->list_node); /* We don't care about the actual option class and type since there * won't be any lookup. We just need to make them unique. */ map->option_class = i / UINT8_MAX; map->option_type = i; map->option_len = ROUND_UP(state->tun_metadata_size[i], 4); map->index = i; } } tun_metadata_table_mod(&ttm); ofputil_uninit_tlv_table(&ttm.mappings); } /* Once we have created a tunnel mapping table with a consistent overall * allocation, we need to remap each flow to use this table from its own * allocation. Since the mapping table has already been installed, we * can just read the data from the match and rewrite it. On rewrite, it * will use the new table. */ static void remap_match(struct match *match) { int i; if (!match->tun_md.valid) { return; } struct tun_metadata flow = match->flow.tunnel.metadata; struct tun_metadata flow_mask = match->wc.masks.tunnel.metadata; memset(&match->flow.tunnel.metadata, 0, sizeof match->flow.tunnel.metadata); memset(&match->wc.masks.tunnel.metadata, 0, sizeof match->wc.masks.tunnel.metadata); match->tun_md.valid = false; ULLONG_FOR_EACH_1 (i, flow_mask.present.map) { const struct mf_field *field = mf_from_id(MFF_TUN_METADATA0 + i); int offset = match->tun_md.entry[i].loc.c.offset; int len = match->tun_md.entry[i].loc.len; union mf_value value, mask; memset(&value, 0, field->n_bytes - len); memset(&mask, match->tun_md.entry[i].masked ? 0 : 0xff, field->n_bytes - len); memcpy(value.tun_metadata + field->n_bytes - len, flow.opts.u8 + offset, len); memcpy(mask.tun_metadata + field->n_bytes - len, flow_mask.opts.u8 + offset, len); mf_set(field, &value, &mask, match, NULL); } } /* In order to correctly handle tunnel metadata, we need to have * two passes over the flows. This happens because tunnel metadata * doesn't have fixed locations in a flow entry but is instead dynamically * allocated space. In the case of flows coming from a file, we don't * even know the size of each field when we need to do the allocation. * When the flows come in, each flow has an individual allocation based * on its own fields. However, this allocation is not the same across * different flows and therefore fields are not directly comparable. * * In the first pass, we record the maximum size of each tunnel metadata * field as well as queue FTE entries for later processing. * * In the second pass, we use the metadata size information to create a * tunnel mapping table and set that through the tunnel metadata processing * code. We then remap all individual flows to use this common allocation * scheme. Finally, we load the queued entries into the classifier for * comparison. * * fte_state_init() should be called before processing any flows. */ static void fte_state_init(struct fte_state *state) { int i; for (i = 0; i < TUN_METADATA_NUM_OPTS; i++) { state->tun_metadata_size[i] = -1; } list_init(&state->fte_pending_list); } /* The first pass of the processing described in the comment about * fte_state_init(). fte_queue() is the first pass to be called as each * flow is read from its source. */ static void fte_queue(struct fte_state *state, const struct match *match, int priority, struct fte_version *version, int index) { struct fte_pending *pending = xmalloc(sizeof *pending); int i; pending->match = xmemdup(match, sizeof *match); pending->priority = priority; pending->version = version; pending->index = index; list_push_back(&state->fte_pending_list, &pending->list_node); if (!match->tun_md.valid) { return; } ULLONG_FOR_EACH_1 (i, match->wc.masks.tunnel.metadata.present.map) { if (match->tun_md.entry[i].loc.len > state->tun_metadata_size[i]) { state->tun_metadata_size[i] = match->tun_md.entry[i].loc.len; } } } /* The second pass of the processing described in the comment about * fte_state_init(). This should be called once all flows (from both * sides of the comparison) have been added through fte_queue(). */ static void fte_fill(struct fte_state *state, struct flow_tables *tables) { struct fte_pending *pending; generate_tun_metadata(state); flow_tables_init(tables); flow_tables_defer(tables); LIST_FOR_EACH_POP(pending, list_node, &state->fte_pending_list) { remap_match(pending->match); fte_insert(tables, pending->match, pending->priority, pending->version, pending->index); free(pending->match); free(pending); } flow_tables_publish(tables); } /* Reads the flows in 'filename' as flow table entries in 'tables' for the * version with the specified 'index'. Returns the flow formats able to * represent the flows that were read. */ static enum ofputil_protocol read_flows_from_file(const char *filename, struct fte_state *state, int index) { enum ofputil_protocol usable_protocols; int line_number; struct ds s; FILE *file; file = !strcmp(filename, "-") ? stdin : fopen(filename, "r"); if (file == NULL) { ovs_fatal(errno, "%s: open", filename); } ds_init(&s); usable_protocols = OFPUTIL_P_ANY; line_number = 0; while (!ds_get_preprocessed_line(&s, file, &line_number)) { struct fte_version *version; struct ofputil_flow_mod fm; char *error; enum ofputil_protocol usable; error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable); if (error) { ovs_fatal(0, "%s:%d: %s", filename, line_number, error); } usable_protocols &= usable; version = xmalloc(sizeof *version); version->cookie = fm.new_cookie; version->idle_timeout = fm.idle_timeout; version->hard_timeout = fm.hard_timeout; version->importance = fm.importance; version->flags = fm.flags & (OFPUTIL_FF_SEND_FLOW_REM | OFPUTIL_FF_EMERG); version->ofpacts = fm.ofpacts; version->ofpacts_len = fm.ofpacts_len; version->table_id = fm.table_id != OFPTT_ALL ? fm.table_id : 0; fte_queue(state, &fm.match, fm.priority, version, index); } ds_destroy(&s); if (file != stdin) { fclose(file); } return usable_protocols; } static bool recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid, struct ofpbuf **replyp, struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts) { struct ofpbuf *reply = *replyp; for (;;) { int retval; bool more; /* Get a flow stats reply message, if we don't already have one. */ if (!reply) { enum ofptype type; enum ofperr error; do { run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed"); } while (((struct ofp_header *) reply->data)->xid != send_xid); error = ofptype_decode(&type, reply->data); if (error || type != OFPTYPE_FLOW_STATS_REPLY) { ovs_fatal(0, "received bad reply: %s", ofp_to_string(reply->data, reply->size, verbosity + 1)); } } /* Pull an individual flow stats reply out of the message. */ retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts); switch (retval) { case 0: *replyp = reply; return true; case EOF: more = ofpmp_more(reply->header); ofpbuf_delete(reply); reply = NULL; if (!more) { *replyp = NULL; return false; } break; default: ovs_fatal(0, "parse error in reply (%s)", ofperr_to_string(retval)); } } } /* Reads the OpenFlow flow table from 'vconn', which has currently active flow * format 'protocol', and adds them as flow table entries in 'tables' for the * version with the specified 'index'. */ static void read_flows_from_switch(struct vconn *vconn, enum ofputil_protocol protocol, struct fte_state *state, int index) { struct ofputil_flow_stats_request fsr; struct ofputil_flow_stats fs; struct ofpbuf *request; struct ofpbuf ofpacts; struct ofpbuf *reply; ovs_be32 send_xid; fsr.aggregate = false; match_init_catchall(&fsr.match); fsr.out_port = OFPP_ANY; fsr.out_group = OFPG_ANY; fsr.table_id = 0xff; fsr.cookie = fsr.cookie_mask = htonll(0); request = ofputil_encode_flow_stats_request(&fsr, protocol); send_xid = ((struct ofp_header *) request->data)->xid; send_openflow_buffer(vconn, request); reply = NULL; ofpbuf_init(&ofpacts, 0); while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) { struct fte_version *version; version = xmalloc(sizeof *version); version->cookie = fs.cookie; version->idle_timeout = fs.idle_timeout; version->hard_timeout = fs.hard_timeout; version->importance = fs.importance; version->flags = 0; version->ofpacts_len = fs.ofpacts_len; version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len); version->table_id = fs.table_id; fte_queue(state, &fs.match, fs.priority, version, index); } ofpbuf_uninit(&ofpacts); } static void fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, enum ofputil_protocol protocol, struct ovs_list *packets) { const struct fte_version *version = fte->versions[index]; struct ofputil_flow_mod fm; struct ofpbuf *ofm; minimatch_expand(&fte->rule.match, &fm.match); fm.priority = fte->rule.priority; fm.cookie = htonll(0); fm.cookie_mask = htonll(0); fm.new_cookie = version->cookie; fm.modify_cookie = true; fm.table_id = version->table_id; fm.command = command; fm.idle_timeout = version->idle_timeout; fm.hard_timeout = version->hard_timeout; fm.importance = version->importance; fm.buffer_id = UINT32_MAX; fm.out_port = OFPP_ANY; fm.out_group = OFPG_ANY; fm.flags = version->flags; if (command == OFPFC_ADD || command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { fm.ofpacts = version->ofpacts; fm.ofpacts_len = version->ofpacts_len; } else { fm.ofpacts = NULL; fm.ofpacts_len = 0; } fm.delete_reason = OFPRR_DELETE; ofm = ofputil_encode_flow_mod(&fm, protocol); list_push_back(packets, &ofm->list_node); } static void ofctl_replace_flows(struct ovs_cmdl_context *ctx) { enum { FILE_IDX = 0, SWITCH_IDX = 1 }; enum ofputil_protocol usable_protocols, protocol; struct fte_state fte_state; struct flow_tables tables; struct classifier *cls; struct ovs_list requests; struct vconn *vconn; struct fte *fte; fte_state_init(&fte_state); usable_protocols = read_flows_from_file(ctx->argv[2], &fte_state, FILE_IDX); protocol = open_vconn(ctx->argv[1], &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols); read_flows_from_switch(vconn, protocol, &fte_state, SWITCH_IDX); fte_fill(&fte_state, &tables); list_init(&requests); FOR_EACH_TABLE (cls, &tables) { /* Delete flows that exist on the switch but not in the file. */ CLS_FOR_EACH (fte, rule, cls) { struct fte_version *file_ver = fte->versions[FILE_IDX]; struct fte_version *sw_ver = fte->versions[SWITCH_IDX]; if (sw_ver && !file_ver) { fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT, protocol, &requests); } } /* Add flows that exist in the file but not on the switch. * Update flows that exist in both places but differ. */ CLS_FOR_EACH (fte, rule, cls) { struct fte_version *file_ver = fte->versions[FILE_IDX]; struct fte_version *sw_ver = fte->versions[SWITCH_IDX]; if (file_ver && (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) { fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests); } } } if (bundle) { bundle_transact(vconn, &requests, OFPBF_ORDERED | OFPBF_ATOMIC); } else { transact_multiple_noreply(vconn, &requests); } vconn_close(vconn); fte_free_all(&tables); } static void read_flows_from_source(const char *source, struct fte_state *state, int index) { struct stat s; if (source[0] == '/' || source[0] == '.' || (!strchr(source, ':') && !stat(source, &s))) { read_flows_from_file(source, state, index); } else { enum ofputil_protocol protocol; struct vconn *vconn; protocol = open_vconn(source, &vconn); protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY); read_flows_from_switch(vconn, protocol, state, index); vconn_close(vconn); } } static void ofctl_diff_flows(struct ovs_cmdl_context *ctx) { bool differences = false; struct fte_state fte_state; struct flow_tables tables; struct classifier *cls; struct ds a_s, b_s; struct fte *fte; fte_state_init(&fte_state); read_flows_from_source(ctx->argv[1], &fte_state, 0); read_flows_from_source(ctx->argv[2], &fte_state, 1); fte_fill(&fte_state, &tables); ds_init(&a_s); ds_init(&b_s); FOR_EACH_TABLE (cls, &tables) { CLS_FOR_EACH (fte, rule, cls) { struct fte_version *a = fte->versions[0]; struct fte_version *b = fte->versions[1]; if (!a || !b || !fte_version_equals(a, b)) { fte_version_format(fte, 0, &a_s); fte_version_format(fte, 1, &b_s); if (strcmp(ds_cstr(&a_s), ds_cstr(&b_s))) { if (a_s.length) { printf("-%s", ds_cstr(&a_s)); } if (b_s.length) { printf("+%s", ds_cstr(&b_s)); } differences = true; } } } } ds_destroy(&a_s); ds_destroy(&b_s); fte_free_all(&tables); if (differences) { exit(2); } } static void ofctl_meter_mod__(const char *bridge, const char *str, int command) { struct ofputil_meter_mod mm; struct vconn *vconn; enum ofputil_protocol protocol; enum ofputil_protocol usable_protocols; enum ofp_version version; if (str) { char *error; error = parse_ofp_meter_mod_str(&mm, str, command, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } } else { usable_protocols = OFPUTIL_P_OF13_UP; mm.command = command; mm.meter.meter_id = OFPM13_ALL; } protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); transact_noreply(vconn, ofputil_encode_meter_mod(version, &mm)); vconn_close(vconn); } static void ofctl_meter_request__(const char *bridge, const char *str, enum ofputil_meter_request_type type) { struct ofputil_meter_mod mm; struct vconn *vconn; enum ofputil_protocol usable_protocols; enum ofputil_protocol protocol; enum ofp_version version; if (str) { char *error; error = parse_ofp_meter_mod_str(&mm, str, -1, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } } else { usable_protocols = OFPUTIL_P_OF13_UP; mm.meter.meter_id = OFPM13_ALL; } protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols); version = ofputil_protocol_to_ofp_version(protocol); dump_stats_transaction(vconn, ofputil_encode_meter_request(version, type, mm.meter.meter_id)); vconn_close(vconn); } static void ofctl_add_meter(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argv[2], OFPMC13_ADD); } static void ofctl_mod_meter(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argv[2], OFPMC13_MODIFY); } static void ofctl_del_meters(struct ovs_cmdl_context *ctx) { ofctl_meter_mod__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPMC13_DELETE); } static void ofctl_dump_meters(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPUTIL_METER_CONFIG); } static void ofctl_meter_stats(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], ctx->argc > 2 ? ctx->argv[2] : NULL, OFPUTIL_METER_STATS); } static void ofctl_meter_features(struct ovs_cmdl_context *ctx) { ofctl_meter_request__(ctx->argv[1], NULL, OFPUTIL_METER_FEATURES); } /* Undocumented commands for unit testing. */ static void ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms, enum ofputil_protocol usable_protocols) { enum ofputil_protocol protocol = 0; char *usable_s; size_t i; usable_s = ofputil_protocols_to_string(usable_protocols); printf("usable protocols: %s\n", usable_s); free(usable_s); if (!(usable_protocols & allowed_protocols)) { ovs_fatal(0, "no usable protocol"); } for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) { protocol = 1 << i; if (protocol & usable_protocols & allowed_protocols) { break; } } ovs_assert(is_pow2(protocol)); printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol)); for (i = 0; i < n_fms; i++) { struct ofputil_flow_mod *fm = &fms[i]; struct ofpbuf *msg; msg = ofputil_encode_flow_mod(fm, protocol); ofp_print(stdout, msg->data, msg->size, verbosity); ofpbuf_delete(msg); free(CONST_CAST(struct ofpact *, fm->ofpacts)); } } /* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints * it back to stdout. */ static void ofctl_parse_flow(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod fm; char *error; error = parse_ofp_flow_mod_str(&fm, ctx->argv[1], OFPFC_ADD, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_parse_flows__(&fm, 1, usable_protocols); } /* "parse-flows FILENAME": reads the named file as a sequence of flows (like * add-flows) and prints each of the flows back to stdout. */ static void ofctl_parse_flows(struct ovs_cmdl_context *ctx) { enum ofputil_protocol usable_protocols; struct ofputil_flow_mod *fms = NULL; size_t n_fms = 0; char *error; error = parse_ofp_flow_mod_file(ctx->argv[1], OFPFC_ADD, &fms, &n_fms, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } ofctl_parse_flows__(fms, n_fms, usable_protocols); free(fms); } static void ofctl_parse_nxm__(bool oxm, enum ofp_version version) { struct ds in; ds_init(&in); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf nx_match; struct match match; ovs_be64 cookie, cookie_mask; enum ofperr error; int match_len; /* Convert string to nx_match. */ ofpbuf_init(&nx_match, 0); if (oxm) { match_len = oxm_match_from_string(ds_cstr(&in), &nx_match); } else { match_len = nx_match_from_string(ds_cstr(&in), &nx_match); } /* Convert nx_match to match. */ if (strict) { if (oxm) { error = oxm_pull_match(&nx_match, &match); } else { error = nx_pull_match(&nx_match, match_len, &match, &cookie, &cookie_mask); } } else { if (oxm) { error = oxm_pull_match_loose(&nx_match, &match); } else { error = nx_pull_match_loose(&nx_match, match_len, &match, &cookie, &cookie_mask); } } if (!error) { char *out; /* Convert match back to nx_match. */ ofpbuf_uninit(&nx_match); ofpbuf_init(&nx_match, 0); if (oxm) { match_len = oxm_put_match(&nx_match, &match, version); out = oxm_match_to_string(&nx_match, match_len); } else { match_len = nx_put_match(&nx_match, &match, cookie, cookie_mask); out = nx_match_to_string(nx_match.data, match_len); } puts(out); free(out); if (verbosity > 0) { ovs_hex_dump(stdout, nx_match.data, nx_match.size, 0, false); } } else { printf("nx_pull_match() returned error %s\n", ofperr_get_name(error)); } ofpbuf_uninit(&nx_match); } ds_destroy(&in); } /* "parse-nxm": reads a series of NXM nx_match specifications as strings from * stdin, does some internal fussing with them, and then prints them back as * strings on stdout. */ static void ofctl_parse_nxm(struct ovs_cmdl_context *ctx OVS_UNUSED) { ofctl_parse_nxm__(false, 0); } /* "parse-oxm VERSION": reads a series of OXM nx_match specifications as * strings from stdin, does some internal fussing with them, and then prints * them back as strings on stdout. VERSION must specify an OpenFlow version, * e.g. "OpenFlow12". */ static void ofctl_parse_oxm(struct ovs_cmdl_context *ctx) { enum ofp_version version = ofputil_version_from_string(ctx->argv[1]); if (version < OFP12_VERSION) { ovs_fatal(0, "%s: not a valid version for OXM", ctx->argv[1]); } ofctl_parse_nxm__(true, version); } static void print_differences(const char *prefix, const void *a_, size_t a_len, const void *b_, size_t b_len) { const uint8_t *a = a_; const uint8_t *b = b_; size_t i; for (i = 0; i < MIN(a_len, b_len); i++) { if (a[i] != b[i]) { printf("%s%2"PRIuSIZE": %02"PRIx8" -> %02"PRIx8"\n", prefix, i, a[i], b[i]); } } for (i = a_len; i < b_len; i++) { printf("%s%2"PRIuSIZE": (none) -> %02"PRIx8"\n", prefix, i, b[i]); } for (i = b_len; i < a_len; i++) { printf("%s%2"PRIuSIZE": %02"PRIx8" -> (none)\n", prefix, i, a[i]); } } static void ofctl_parse_actions__(const char *version_s, bool instructions) { enum ofp_version version; struct ds in; version = ofputil_version_from_string(version_s); if (!version) { ovs_fatal(0, "%s: not a valid OpenFlow version", version_s); } ds_init(&in); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf of_out; struct ofpbuf of_in; struct ofpbuf ofpacts; const char *table_id; char *actions; enum ofperr error; size_t size; struct ds s; /* Parse table_id separated with the follow-up actions by ",", if * any. */ actions = ds_cstr(&in); table_id = NULL; if (strstr(actions, ",")) { table_id = strsep(&actions, ","); } /* Parse hex bytes. */ ofpbuf_init(&of_in, 0); if (ofpbuf_put_hex(&of_in, actions, NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } /* Convert to ofpacts. */ ofpbuf_init(&ofpacts, 0); size = of_in.size; error = (instructions ? ofpacts_pull_openflow_instructions : ofpacts_pull_openflow_actions)( &of_in, of_in.size, version, &ofpacts); if (!error && instructions) { /* Verify actions, enforce consistency. */ enum ofputil_protocol protocol; struct flow flow; memset(&flow, 0, sizeof flow); protocol = ofputil_protocols_from_ofp_version(version); error = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow, OFPP_MAX, table_id ? atoi(table_id) : 0, OFPTT_MAX + 1, protocol); } if (error) { printf("bad %s %s: %s\n\n", version_s, instructions ? "instructions" : "actions", ofperr_get_name(error)); ofpbuf_uninit(&ofpacts); ofpbuf_uninit(&of_in); continue; } ofpbuf_push_uninit(&of_in, size); /* Print cls_rule. */ ds_init(&s); ds_put_cstr(&s, "actions="); ofpacts_format(ofpacts.data, ofpacts.size, &s); puts(ds_cstr(&s)); ds_destroy(&s); /* Convert back to ofp10 actions and print differences from input. */ ofpbuf_init(&of_out, 0); if (instructions) { ofpacts_put_openflow_instructions(ofpacts.data, ofpacts.size, &of_out, version); } else { ofpacts_put_openflow_actions(ofpacts.data, ofpacts.size, &of_out, version); } print_differences("", of_in.data, of_in.size, of_out.data, of_out.size); putchar('\n'); ofpbuf_uninit(&ofpacts); ofpbuf_uninit(&of_in); ofpbuf_uninit(&of_out); } ds_destroy(&in); } /* "parse-actions VERSION": reads a series of action specifications for the * given OpenFlow VERSION as hex bytes from stdin, converts them to ofpacts, * prints them as strings on stdout, and then converts them back to hex bytes * and prints any differences from the input. */ static void ofctl_parse_actions(struct ovs_cmdl_context *ctx) { ofctl_parse_actions__(ctx->argv[1], false); } /* "parse-actions VERSION": reads a series of instruction specifications for * the given OpenFlow VERSION as hex bytes from stdin, converts them to * ofpacts, prints them as strings on stdout, and then converts them back to * hex bytes and prints any differences from the input. */ static void ofctl_parse_instructions(struct ovs_cmdl_context *ctx) { ofctl_parse_actions__(ctx->argv[1], true); } /* "parse-ofp10-match": reads a series of ofp10_match specifications as hex * bytes from stdin, converts them to cls_rules, prints them as strings on * stdout, and then converts them back to hex bytes and prints any differences * from the input. * * The input hex bytes may contain "x"s to represent "don't-cares", bytes whose * values are ignored in the input and will be set to zero when OVS converts * them back to hex bytes. ovs-ofctl actually sets "x"s to random bits when * it does the conversion to hex, to ensure that in fact they are ignored. */ static void ofctl_parse_ofp10_match(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct ds expout; struct ds in; ds_init(&in); ds_init(&expout); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf match_in, match_expout; struct ofp10_match match_out; struct ofp10_match match_normal; struct match match; char *p; /* Parse hex bytes to use for expected output. */ ds_clear(&expout); ds_put_cstr(&expout, ds_cstr(&in)); for (p = ds_cstr(&expout); *p; p++) { if (*p == 'x') { *p = '0'; } } ofpbuf_init(&match_expout, 0); if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_expout.size != sizeof(struct ofp10_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_expout.size, sizeof(struct ofp10_match)); } /* Parse hex bytes for input. */ for (p = ds_cstr(&in); *p; p++) { if (*p == 'x') { *p = "0123456789abcdef"[random_uint32() & 0xf]; } } ofpbuf_init(&match_in, 0); if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_in.size != sizeof(struct ofp10_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_in.size, sizeof(struct ofp10_match)); } /* Convert to cls_rule and print. */ ofputil_match_from_ofp10_match(match_in.data, &match); match_print(&match); /* Convert back to ofp10_match and print differences from input. */ ofputil_match_to_ofp10_match(&match, &match_out); print_differences("", match_expout.data, match_expout.size, &match_out, sizeof match_out); /* Normalize, then convert and compare again. */ ofputil_normalize_match(&match); ofputil_match_to_ofp10_match(&match, &match_normal); print_differences("normal: ", &match_out, sizeof match_out, &match_normal, sizeof match_normal); putchar('\n'); ofpbuf_uninit(&match_in); ofpbuf_uninit(&match_expout); } ds_destroy(&in); ds_destroy(&expout); } /* "parse-ofp11-match": reads a series of ofp11_match specifications as hex * bytes from stdin, converts them to "struct match"es, prints them as strings * on stdout, and then converts them back to hex bytes and prints any * differences from the input. */ static void ofctl_parse_ofp11_match(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct ds in; ds_init(&in); while (!ds_get_preprocessed_line(&in, stdin, NULL)) { struct ofpbuf match_in; struct ofp11_match match_out; struct match match; enum ofperr error; /* Parse hex bytes. */ ofpbuf_init(&match_in, 0); if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (match_in.size != sizeof(struct ofp11_match)) { ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE, match_in.size, sizeof(struct ofp11_match)); } /* Convert to match. */ error = ofputil_match_from_ofp11_match(match_in.data, &match); if (error) { printf("bad ofp11_match: %s\n\n", ofperr_get_name(error)); ofpbuf_uninit(&match_in); continue; } /* Print match. */ match_print(&match); /* Convert back to ofp11_match and print differences from input. */ ofputil_match_to_ofp11_match(&match, &match_out); print_differences("", match_in.data, match_in.size, &match_out, sizeof match_out); putchar('\n'); ofpbuf_uninit(&match_in); } ds_destroy(&in); } /* "parse-pcap PCAP": read packets from PCAP and print their flows. */ static void ofctl_parse_pcap(struct ovs_cmdl_context *ctx) { FILE *pcap; pcap = ovs_pcap_open(ctx->argv[1], "rb"); if (!pcap) { ovs_fatal(errno, "%s: open failed", ctx->argv[1]); } for (;;) { struct dp_packet *packet; struct flow flow; int error; error = ovs_pcap_read(pcap, &packet, NULL); if (error == EOF) { break; } else if (error) { ovs_fatal(error, "%s: read failed", ctx->argv[1]); } pkt_metadata_init(&packet->md, u32_to_odp(ofp_to_u16(OFPP_NONE))); flow_extract(packet, &flow); flow_print(stdout, &flow); putchar('\n'); dp_packet_delete(packet); } } /* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and * mask values to and from various formats and prints the results. */ static void ofctl_check_vlan(struct ovs_cmdl_context *ctx) { struct match match; char *string_s; struct ofputil_flow_mod fm; struct ofpbuf nxm; struct match nxm_match; int nxm_match_len; char *nxm_s; struct ofp10_match of10_raw; struct match of10_match; struct ofp11_match of11_raw; struct match of11_match; enum ofperr error; char *error_s; enum ofputil_protocol usable_protocols; /* Unused for now. */ match_init_catchall(&match); match.flow.vlan_tci = htons(strtoul(ctx->argv[1], NULL, 16)); match.wc.masks.vlan_tci = htons(strtoul(ctx->argv[2], NULL, 16)); /* Convert to and from string. */ string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY); printf("%s -> ", string_s); fflush(stdout); error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols); if (error_s) { ovs_fatal(0, "%s", error_s); } printf("%04"PRIx16"/%04"PRIx16"\n", ntohs(fm.match.flow.vlan_tci), ntohs(fm.match.wc.masks.vlan_tci)); free(string_s); /* Convert to and from NXM. */ ofpbuf_init(&nxm, 0); nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0)); nxm_s = nx_match_to_string(nxm.data, nxm_match_len); error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL); printf("NXM: %s -> ", nxm_s); if (error) { printf("%s\n", ofperr_to_string(error)); } else { printf("%04"PRIx16"/%04"PRIx16"\n", ntohs(nxm_match.flow.vlan_tci), ntohs(nxm_match.wc.masks.vlan_tci)); } free(nxm_s); ofpbuf_uninit(&nxm); /* Convert to and from OXM. */ ofpbuf_init(&nxm, 0); nxm_match_len = oxm_put_match(&nxm, &match, OFP12_VERSION); nxm_s = oxm_match_to_string(&nxm, nxm_match_len); error = oxm_pull_match(&nxm, &nxm_match); printf("OXM: %s -> ", nxm_s); if (error) { printf("%s\n", ofperr_to_string(error)); } else { uint16_t vid = ntohs(nxm_match.flow.vlan_tci) & (VLAN_VID_MASK | VLAN_CFI); uint16_t mask = ntohs(nxm_match.wc.masks.vlan_tci) & (VLAN_VID_MASK | VLAN_CFI); printf("%04"PRIx16"/%04"PRIx16",", vid, mask); if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlan_tci)) { printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_match.flow.vlan_tci)); } else { printf("--\n"); } } free(nxm_s); ofpbuf_uninit(&nxm); /* Convert to and from OpenFlow 1.0. */ ofputil_match_to_ofp10_match(&match, &of10_raw); ofputil_match_from_ofp10_match(&of10_raw, &of10_match); printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n", ntohs(of10_raw.dl_vlan), (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0, of10_raw.dl_vlan_pcp, (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0, ntohs(of10_match.flow.vlan_tci), ntohs(of10_match.wc.masks.vlan_tci)); /* Convert to and from OpenFlow 1.1. */ ofputil_match_to_ofp11_match(&match, &of11_raw); ofputil_match_from_ofp11_match(&of11_raw, &of11_match); printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n", ntohs(of11_raw.dl_vlan), (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0, of11_raw.dl_vlan_pcp, (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0, ntohs(of11_match.flow.vlan_tci), ntohs(of11_match.wc.masks.vlan_tci)); } /* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow * version. */ static void ofctl_print_error(struct ovs_cmdl_context *ctx) { enum ofperr error; int version; error = ofperr_from_name(ctx->argv[1]); if (!error) { ovs_fatal(0, "unknown error \"%s\"", ctx->argv[1]); } for (version = 0; version <= UINT8_MAX; version++) { const char *name = ofperr_domain_get_name(version); if (name) { int vendor = ofperr_get_vendor(error, version); int type = ofperr_get_type(error, version); int code = ofperr_get_code(error, version); if (vendor != -1 || type != -1 || code != -1) { printf("%s: vendor %#x, type %d, code %d\n", name, vendor, type, code); } } } } /* "encode-error-reply ENUM REQUEST": Encodes an error reply to REQUEST for the * error named ENUM and prints the error reply in hex. */ static void ofctl_encode_error_reply(struct ovs_cmdl_context *ctx) { const struct ofp_header *oh; struct ofpbuf request, *reply; enum ofperr error; error = ofperr_from_name(ctx->argv[1]); if (!error) { ovs_fatal(0, "unknown error \"%s\"", ctx->argv[1]); } ofpbuf_init(&request, 0); if (ofpbuf_put_hex(&request, ctx->argv[2], NULL)[0] != '\0') { ovs_fatal(0, "Trailing garbage in hex data"); } if (request.size < sizeof(struct ofp_header)) { ovs_fatal(0, "Request too short"); } oh = request.data; if (request.size != ntohs(oh->length)) { ovs_fatal(0, "Request size inconsistent"); } reply = ofperr_encode_reply(error, request.data); ofpbuf_uninit(&request); ovs_hex_dump(stdout, reply->data, reply->size, 0, false); ofpbuf_delete(reply); } /* "ofp-print HEXSTRING [VERBOSITY]": Converts the hex digits in HEXSTRING into * binary data, interpreting them as an OpenFlow message, and prints the * OpenFlow message on stdout, at VERBOSITY (level 2 by default). * * Alternative usage: "ofp-print [VERBOSITY] - < HEXSTRING_FILE", where * HEXSTRING_FILE contains the HEXSTRING. */ static void ofctl_ofp_print(struct ovs_cmdl_context *ctx) { struct ofpbuf packet; char *buffer; int verbosity = 2; struct ds line; ds_init(&line); if (!strcmp(ctx->argv[ctx->argc-1], "-")) { if (ds_get_line(&line, stdin)) { VLOG_FATAL("Failed to read stdin"); } buffer = line.string; verbosity = ctx->argc > 2 ? atoi(ctx->argv[1]) : verbosity; } else if (ctx->argc > 2) { buffer = ctx->argv[1]; verbosity = atoi(ctx->argv[2]); } else { buffer = ctx->argv[1]; } ofpbuf_init(&packet, strlen(buffer) / 2); if (ofpbuf_put_hex(&packet, buffer, NULL)[0] != '\0') { ovs_fatal(0, "trailing garbage following hex bytes"); } ofp_print(stdout, packet.data, packet.size, verbosity); ofpbuf_uninit(&packet); ds_destroy(&line); } /* "encode-hello BITMAP...": Encodes each BITMAP as an OpenFlow hello message * and dumps each message in hex. */ static void ofctl_encode_hello(struct ovs_cmdl_context *ctx) { uint32_t bitmap = strtol(ctx->argv[1], NULL, 0); struct ofpbuf *hello; hello = ofputil_encode_hello(bitmap); ovs_hex_dump(stdout, hello->data, hello->size, 0, false); ofp_print(stdout, hello->data, hello->size, verbosity); ofpbuf_delete(hello); } static void ofctl_parse_key_value(struct ovs_cmdl_context *ctx) { for (size_t i = 1; i < ctx->argc; i++) { char *s = ctx->argv[i]; char *key, *value; int j = 0; while (ofputil_parse_key_value(&s, &key, &value)) { if (j++) { fputs(", ", stdout); } fputs(key, stdout); if (value[0]) { printf("=%s", value); } } putchar('\n'); } } static const struct ovs_cmdl_command all_commands[] = { { "show", "switch", 1, 1, ofctl_show }, { "monitor", "switch [misslen] [invalid_ttl] [watch:[...]]", 1, 3, ofctl_monitor }, { "snoop", "switch", 1, 1, ofctl_snoop }, { "dump-desc", "switch", 1, 1, ofctl_dump_desc }, { "dump-tables", "switch", 1, 1, ofctl_dump_tables }, { "dump-table-features", "switch", 1, 1, ofctl_dump_table_features }, { "dump-table-desc", "switch", 1, 1, ofctl_dump_table_desc }, { "dump-flows", "switch", 1, 2, ofctl_dump_flows }, { "dump-aggregate", "switch", 1, 2, ofctl_dump_aggregate }, { "queue-stats", "switch [port [queue]]", 1, 3, ofctl_queue_stats }, { "queue-get-config", "switch port", 2, 2, ofctl_queue_get_config }, { "add-flow", "switch flow", 2, 2, ofctl_add_flow }, { "add-flows", "switch file", 2, 2, ofctl_add_flows }, { "mod-flows", "switch flow", 2, 2, ofctl_mod_flows }, { "del-flows", "switch [flow]", 1, 2, ofctl_del_flows }, { "replace-flows", "switch file", 2, 2, ofctl_replace_flows }, { "diff-flows", "source1 source2", 2, 2, ofctl_diff_flows }, { "add-meter", "switch meter", 2, 2, ofctl_add_meter }, { "mod-meter", "switch meter", 2, 2, ofctl_mod_meter }, { "del-meter", "switch meter", 2, 2, ofctl_del_meters }, { "del-meters", "switch", 1, 1, ofctl_del_meters }, { "dump-meter", "switch meter", 2, 2, ofctl_dump_meters }, { "dump-meters", "switch", 1, 1, ofctl_dump_meters }, { "meter-stats", "switch [meter]", 1, 2, ofctl_meter_stats }, { "meter-features", "switch", 1, 1, ofctl_meter_features }, { "packet-out", "switch in_port actions packet...", 4, INT_MAX, ofctl_packet_out }, { "dump-ports", "switch [port]", 1, 2, ofctl_dump_ports }, { "dump-ports-desc", "switch [port]", 1, 2, ofctl_dump_ports_desc }, { "mod-port", "switch iface act", 3, 3, ofctl_mod_port }, { "mod-table", "switch mod", 3, 3, ofctl_mod_table }, { "get-frags", "switch", 1, 1, ofctl_get_frags }, { "set-frags", "switch frag_mode", 2, 2, ofctl_set_frags }, { "probe", "target", 1, 1, ofctl_probe }, { "ping", "target [n]", 1, 2, ofctl_ping }, { "benchmark", "target n count", 3, 3, ofctl_benchmark }, { "ofp-parse", "file", 1, 1, ofctl_ofp_parse }, { "ofp-parse-pcap", "pcap", 1, INT_MAX, ofctl_ofp_parse_pcap }, { "add-group", "switch group", 1, 2, ofctl_add_group }, { "add-groups", "switch file", 1, 2, ofctl_add_groups }, { "mod-group", "switch group", 1, 2, ofctl_mod_group }, { "del-groups", "switch [group]", 1, 2, ofctl_del_groups }, { "insert-buckets", "switch [group]", 1, 2, ofctl_insert_bucket }, { "remove-buckets", "switch [group]", 1, 2, ofctl_remove_bucket }, { "dump-groups", "switch [group]", 1, 2, ofctl_dump_group_desc }, { "dump-group-stats", "switch [group]", 1, 2, ofctl_dump_group_stats }, { "dump-group-features", "switch", 1, 1, ofctl_dump_group_features }, { "add-tlv-map", "switch map", 2, 2, ofctl_add_tlv_map }, { "del-tlv-map", "switch [map]", 1, 2, ofctl_del_tlv_map }, { "dump-tlv-map", "switch", 1, 1, ofctl_dump_tlv_map }, { "help", NULL, 0, INT_MAX, ofctl_help }, { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands }, /* Undocumented commands for testing. */ { "parse-flow", NULL, 1, 1, ofctl_parse_flow }, { "parse-flows", NULL, 1, 1, ofctl_parse_flows }, { "parse-nx-match", NULL, 0, 0, ofctl_parse_nxm }, { "parse-nxm", NULL, 0, 0, ofctl_parse_nxm }, { "parse-oxm", NULL, 1, 1, ofctl_parse_oxm }, { "parse-actions", NULL, 1, 1, ofctl_parse_actions }, { "parse-instructions", NULL, 1, 1, ofctl_parse_instructions }, { "parse-ofp10-match", NULL, 0, 0, ofctl_parse_ofp10_match }, { "parse-ofp11-match", NULL, 0, 0, ofctl_parse_ofp11_match }, { "parse-pcap", NULL, 1, 1, ofctl_parse_pcap }, { "check-vlan", NULL, 2, 2, ofctl_check_vlan }, { "print-error", NULL, 1, 1, ofctl_print_error }, { "encode-error-reply", NULL, 2, 2, ofctl_encode_error_reply }, { "ofp-print", NULL, 1, 2, ofctl_ofp_print }, { "encode-hello", NULL, 1, 1, ofctl_encode_hello }, { "parse-key-value", NULL, 1, INT_MAX, ofctl_parse_key_value }, { NULL, NULL, 0, 0, NULL }, }; static const struct ovs_cmdl_command *get_all_commands(void) { return all_commands; } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-test.in0000644000000000000000000000013213534540071017773 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.821846349 openvswitch-2.5.9/utilities/ovs-test.in0000644000175000017500000001120413534540071021457 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ ovs test utility that allows to do tests between remote hosts """ import fcntl import math import os import select import signal import socket import subprocess import sys import time import xmlrpclib import argparse import twisted import ovstest.args as args import ovstest.rpcserver as rpcserver import ovstest.tests as tests import ovstest.util as util DEFAULT_TEST_BRIDGE = "ovstestbr0" DEFAULT_TEST_PORT = "ovstestport0" DEFAULT_TEST_TUN = "ovstestport1" def collect_information(node): """Print information about hosts that will do testing""" print "Node %s:%u " % (node[0], node[1]) server = util.rpc_client(node[0], node[1]) interface_name = server.get_interface(node[0]) phys_iface = None uname = server.uname() mtu = 1500 if not interface_name: print ("Could not find interface that has %s IP address." "Make sure that you specified correct Outer IP." % (node[0])) else: if server.is_ovs_bridge(interface_name): phys_iface = server.get_iface_from_bridge(interface_name) else: phys_iface = interface_name if phys_iface: driver = server.get_driver(phys_iface) mtu = server.get_interface_mtu(phys_iface) print "Will be using %s (%s) with MTU %u" % (phys_iface, node[0], mtu) if not driver: print "Unable to get driver information from ethtool." else: print "On this host %s has %s." % (phys_iface, driver) if not uname: print "Unable to retrieve kernel information. Is this Linux?" else: print "Running kernel %s." % uname print "\n" return mtu if __name__ == '__main__': local_server = None try: ovs_args = args.ovs_initialize_args() if ovs_args.port is not None: # Start in pure server mode rpcserver.start_rpc_server(ovs_args.port) elif ovs_args.servers is not None: # Run in client mode node1 = ovs_args.servers[0] node2 = ovs_args.servers[1] # Verify whether client will need to spawn a local instance of # ovs-test server by looking at the first OuterIP. if it is a # 127.0.0.1 then spawn local ovs-test server. if node1[0] == "127.0.0.1": local_server = util.start_local_server(node1[1]) # We must determine the IP address that local ovs-test server # will use: me = util.rpc_client(node1[0], node1[1]) my_ip = me.get_my_address_from(node2[0], node2[1]) node1 = (my_ip, node1[1], node1[2], node1[3]) mtu_node2 = collect_information(node2) mtu_node1 = collect_information(node1) bandwidth = ovs_args.targetBandwidth interval = ovs_args.testInterval ps = util.get_datagram_sizes(mtu_node1, mtu_node2) direct = ovs_args.direct vlan_tag = ovs_args.vlanTag tunnel_modes = ovs_args.tunnelModes if direct is not None: print "Performing direct tests" tests.do_direct_tests(node2, node1, bandwidth, interval, ps) if vlan_tag is not None: print "Performing VLAN tests" tests.do_vlan_tests(node2, node1, bandwidth, interval, ps, vlan_tag) for tmode in tunnel_modes: print "Performing", tmode, "tests" tests.do_l3_tests(node2, node1, bandwidth, interval, ps, tmode) except KeyboardInterrupt: pass except xmlrpclib.Fault: print "Couldn't establish XMLRPC control channel" except socket.error: print "Couldn't establish XMLRPC control channel" except xmlrpclib.ProtocolError: print "XMLRPC control channel was abruptly terminated" except twisted.internet.error.CannotListenError: print "Couldn't start XMLRPC server on port %u" % ovs_args.port finally: if local_server is not None: local_server.terminate() openvswitch-2.5.9/utilities/PaxHeaders.82075/qemu-wrap.py0000644000000000000000000000013213534540071020147 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.825846378 openvswitch-2.5.9/utilities/qemu-wrap.py0000755000175000017500000003022213534540071021637 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # # BSD LICENSE # # Copyright(c) 2010-2014 Intel Corporation. All rights reserved. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of Intel Corporation nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ##################################################################### # This script is designed to modify the call to the QEMU emulator # to support userspace vhost when starting a guest machine through # libvirt with vhost enabled. The steps to enable this are as follows # and should be run as root: # # 1. Place this script in a libvirtd's binary search PATH ($PATH) # A good location would be in the same directory that the QEMU # binary is located # # 2. Ensure that the script has the same owner/group and file # permissions as the QEMU binary # # 3. Update the VM xml file using "virsh edit VM.xml" # # 3.a) Set the VM to use the launch script # # Set the emulator path contained in the # tags # # e.g replace /usr/bin/qemu-kvm # with /usr/bin/qemu-wrap.py # # 3.b) Set the VM's device's to use vhost-net offload # # # # # # # 4. Enable libvirt to access our userpace device file by adding it to # controllers cgroup for libvirtd using the following steps # # 4.a) In /etc/libvirt/qemu.conf add/edit the following lines: # 1) cgroup_controllers = [ ... "devices", ... ] # 2) clear_emulator_capabilities = 0 # 3) user = "root" # 4) group = "root" # 5) cgroup_device_acl = [ # "/dev/null", "/dev/full", "/dev/zero", # "/dev/random", "/dev/urandom", # "/dev/ptmx", "/dev/kvm", "/dev/kqemu", # "/dev/rtc", "/dev/hpet", "/dev/net/tun", # "/dev/-", # "/dev/hugepages" # ] # # 4.b) Disable SELinux or set to permissive mode # # 4.c) Mount cgroup device controller # "mkdir /dev/cgroup" # "mount -t cgroup none /dev/cgroup -o devices" # # 4.d) Set hugetlbfs_mount variable - ( Optional ) # VMs using userspace vhost must use hugepage backed # memory. This can be enabled in the libvirt XML # config by adding a memory backing section to the # XML config e.g. # # # # This memory backing section should be added after the # and sections. This will add # flags "-mem-prealloc -mem-path " to the QEMU # command line. The hugetlbfs_mount variable can be used # to override the default passed through by libvirt. # # if "-mem-prealloc" or "-mem-path " are not passed # through and a vhost device is detected then these options will # be automatically added by this script. This script will detect # the system hugetlbfs mount point to be used for . The # default for this script can be overidden by the # hugetlbfs_dir variable in the configuration section of this script. # # # 4.e) Restart the libvirtd system process # e.g. on Fedora "systemctl restart libvirtd.service" # # # 4.f) Edit the Configuration Parameters section of this script # to point to the correct emulator location and set any # addition options # # The script modifies the libvirtd Qemu call by modifying/adding # options based on the configuration parameters below. # NOTE: # emul_path and us_vhost_path must be set # All other parameters are optional ##################################################################### ############################################# # Configuration Parameters ############################################# #Path to QEMU binary emul_path = "/usr/local/bin/qemu-system-x86_64" #Path to userspace vhost device file # This filename should match the --dev-basename --dev-index parameters of # the command used to launch the userspace vhost sample application e.g. # if the sample app lauch command is: # ./build/vhost-switch ..... --dev-basename usvhost --dev-index 1 # then this variable should be set to: # us_vhost_path = "/dev/usvhost-1" us_vhost_path = "/dev/usvhost-1" #List of additional user defined emulation options. These options will #be added to all Qemu calls emul_opts_user = [] #List of additional user defined emulation options for vhost only. #These options will only be added to vhost enabled guests emul_opts_user_vhost = [] #For all VHOST enabled VMs, the VM memory is preallocated from hugetlbfs # Set this variable to one to enable this option for all VMs use_huge_all = 0 #Instead of autodetecting, override the hugetlbfs directory by setting #this variable hugetlbfs_dir = "" ############################################# ############################################# # ****** Do Not Modify Below this Line ****** ############################################# import sys, os, subprocess import time import signal #List of open userspace vhost file descriptors fd_list = [] #additional virtio device flags when using userspace vhost vhost_flags = [ "csum=off", "gso=off", "guest_tso4=off", "guest_tso6=off", "guest_ecn=off" ] #String of the path to the Qemu process pid qemu_pid = "/tmp/%d-qemu.pid" % os.getpid() ############################################# # Signal haldler to kill Qemu subprocess ############################################# def kill_qemu_process(signum, stack): pidfile = open(qemu_pid, 'r') pid = int(pidfile.read()) os.killpg(pid, signal.SIGTERM) pidfile.close() ############################################# # Find the system hugefile mount point. # Note: # if multiple hugetlbfs mount points exist # then the first one found will be used ############################################# def find_huge_mount(): if (len(hugetlbfs_dir)): return hugetlbfs_dir huge_mount = "" if (os.access("/proc/mounts", os.F_OK)): f = open("/proc/mounts", "r") line = f.readline() while line: line_split = line.split(" ") if line_split[2] == 'hugetlbfs': huge_mount = line_split[1] break line = f.readline() else: print "/proc/mounts not found" exit (1) f.close if len(huge_mount) == 0: print "Failed to find hugetlbfs mount point" exit (1) return huge_mount ############################################# # Get a userspace Vhost file descriptor ############################################# def get_vhost_fd(): if (os.access(us_vhost_path, os.F_OK)): fd = os.open( us_vhost_path, os.O_RDWR) else: print ("US-Vhost file %s not found" %us_vhost_path) exit (1) return fd ############################################# # Check for vhostfd. if found then replace # with our own vhost fd and append any vhost # flags onto the end ############################################# def modify_netdev_arg(arg): global fd_list vhost_in_use = 0 s = '' new_opts = [] netdev_opts = arg.split(",") for opt in netdev_opts: #check if vhost is used if "vhost" == opt[:5]: vhost_in_use = 1 else: new_opts.append(opt) #if using vhost append vhost options if vhost_in_use == 1: #append vhost on option new_opts.append('vhost=on') #append vhostfd ption new_fd = get_vhost_fd() new_opts.append('vhostfd=' + str(new_fd)) fd_list.append(new_fd) #concatenate all options for opt in new_opts: if len(s) > 0: s+=',' s+=opt return s ############################################# # Main ############################################# def main(): global fd_list global vhost_in_use new_args = [] num_cmd_args = len(sys.argv) emul_call = '' mem_prealloc_set = 0 mem_path_set = 0 num = 0; #parse the parameters while (num < num_cmd_args): arg = sys.argv[num] #Check netdev +1 parameter for vhostfd if arg == '-netdev': num_vhost_devs = len(fd_list) new_args.append(arg) num+=1 arg = sys.argv[num] mod_arg = modify_netdev_arg(arg) new_args.append(mod_arg) #append vhost flags if this is a vhost device # and -device is the next arg # i.e -device -opt1,-opt2,...,-opt3,%vhost if (num_vhost_devs < len(fd_list)): num+=1 arg = sys.argv[num] if arg == '-device': new_args.append(arg) num+=1 new_arg = sys.argv[num] for flag in vhost_flags: new_arg = ''.join([new_arg,',',flag]) new_args.append(new_arg) else: new_args.append(arg) elif arg == '-mem-prealloc': mem_prealloc_set = 1 new_args.append(arg) elif arg == '-mem-path': mem_path_set = 1 new_args.append(arg) else: new_args.append(arg) num+=1 #Set Qemu binary location emul_call+=emul_path emul_call+=" " #Add prealloc mem options if using vhost and not already added if ((len(fd_list) > 0) and (mem_prealloc_set == 0)): emul_call += "-mem-prealloc " #Add mempath mem options if using vhost and not already added if ((len(fd_list) > 0) and (mem_path_set == 0)): #Detect and add hugetlbfs mount point mp = find_huge_mount() mp = "".join(["-mem-path ", mp]) emul_call += mp emul_call += " " #add user options for opt in emul_opts_user: emul_call += opt emul_call += " " #Add add user vhost only options if len(fd_list) > 0: for opt in emul_opts_user_vhost: emul_call += opt emul_call += " " #Add updated libvirt options iter_args = iter(new_args) #skip 1st arg i.e. call to this script next(iter_args) for arg in iter_args: emul_call+=str(arg) emul_call+= " " emul_call += "-pidfile %s " % qemu_pid #Call QEMU process = subprocess.Popen(emul_call, shell=True, preexec_fn=os.setsid) for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]: signal.signal(sig, kill_qemu_process) process.wait() #Close usvhost files for fd in fd_list: os.close(fd) #Cleanup temporary files if os.access(qemu_pid, os.F_OK): os.remove(qemu_pid) if __name__ == "__main__": main() openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-command-bashcomp.INSTALL.md0000644000000000000000000000013213534540071023323 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.701845464 openvswitch-2.5.9/utilities/ovs-command-bashcomp.INSTALL.md0000644000175000017500000000520413534540071025012 0ustar00jpettitjpettit00000000000000Using bash command-line completion scripts ------------------------------------------ There are two completion scripts available, ovs-appctl-bashcomp.bash and ovs-vsctl-bashcomp.bash respectively. ovs-appctl-bashcomp ------------------- ovs-appctl-bashcomp.bash adds bash command-line completion support for ovs-appctl, ovs-dpctl, ovs-ofctl and ovsdb-tool commands. Features: --------- display available completion or complete on unfinished user input (long option, subcommand, and argument). once the subcommand (e.g. ofproto/trace) has been given, the script will print the subcommand format. the script can convert between keywords like 'bridge/port/interface/dp' and the available record in ovsdb. Limitations: ------------ only support small set of important keywords (dp, datapath, bridge, switch, port, interface, iface). does not support parsing of nested options (e.g. ovsdb-tool create [db [schema]]). does not support expansion on repeatitive argument (e.g. ovs-dpctl show [dp...]). only support matching on long options, and only in the format (--option [arg], i.e. should not use --option=[arg]). ovs-vsctl-bashcomp ------------------- ovs-vsctl-bashcomp.bash adds bash command-line completion support for ovs-vsctl command. Features: --------- display available completion and complete on user input for global/local options, command, and argument. query database and expand keywords like 'table/record/column/key' to available completions. deal with argument relations like 'one and more', 'zero or one'. complete multiple ovs-vsctl commands cascaded via '--'. Limitations: ------------ completion of very long ovs-vsctl command can take up to several seconds. How to use: ----------- The bashcomp scripts should be placed at /etc/bash_completion.d/ to be available for all bash sessions. Running 'make install' will place the scripts to $(sysconfdir)/bash_completion.d/. So user should specify --sysconfdir=/etc at configuration. Meanwhile, if OVS is installed from packages, the scripts will automatically be placed inside /etc/bash_completion.d/. If you just want to run the scripts in one bash, you can remove them from /etc/bash_completion.d/ and run the scripts via '. ovs-appctl-bashcomp.bash' or '. ovs-vsctl-bashcomp.bash'. Test: ----- Unit tests are added in tests/completion.at and integrated into autotest framework. To run the tests, just do make check. Bug Reporting: -------------- Please report problems to bugs@openvswitch.org.openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-parse-backtrace.80000644000000000000000000000013213534540071021604 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.765845936 openvswitch-2.5.9/utilities/ovs-parse-backtrace.80000644000175000017500000000173513534540071023300 0ustar00jpettitjpettit00000000000000.TH ovs\-parse\-backtrace 8 "October 2012" "Open vSwitch" "Open vSwitch Manual" . .SH NAME ovs\-parse\-backtrace \- parses ovs-appctl backtrace output . .SH SYNOPSIS \fBovs\-appctl backtrace\fR | \fBovs\-parse\-backtrace\fR [\fIbinary\fR] .P \fBovs\-parse\-backtrace\fR [\fIbinary\fR] < \fIbacktrace\fR . .SH DESCRIPTION In some configurations, many Open vSwitch daemons can produce a series of backtraces using the \fBovs\-appctl backtrace\fR command. Users can analyze these backtraces to figure out what the given Open vSwitch daemon may be spending most of its time doing. \fBovs\-parse\-backtrace\fR makes this output easier to interpret. .PP The \fBovs\-appctl backtrace\fR output must be supplied on standard input. The binary that produced the output should be supplied as the sole non-option argument. For best results, the binary should have debug symbols. . .SH OPTIONS .TP \fB\-\-help\fR Prints a usage message and exits. .P \fB\-\-version\fR Prints the version and exits. openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-testcontroller.8.in0000644000000000000000000000013213534540071022245 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.761845906 openvswitch-2.5.9/utilities/ovs-testcontroller.8.in0000644000175000017500000001443313534540071023740 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-testcontroller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-testcontroller . .SH NAME ovs\-testcontroller \- simple OpenFlow controller for testing . .SH SYNOPSIS .B ovs\-testcontroller [\fIoptions\fR] \fImethod\fR \fB[\fImethod\fR]\&... . .SH DESCRIPTION \fBovs\-testcontroller\fR is a simple OpenFlow controller that manages any number of switches over the OpenFlow protocol, causing them to function as L2 MAC-learning switches or hubs. It is suitable for initial testing of OpenFlow networks. It is not a necessary or desirable part of a production OpenFlow deployment. .PP \fBovs\-testcontroller\fR controls one or more OpenFlow switches, specified as one or more of the following OpenFlow connection methods: . .RS .so lib/vconn-passive.man .so lib/vconn-active.man .RE . .SH OPTIONS .IP "\fB\-n\fR" .IQ "\fB\-\-noflow\fR" By default, \fBovs\-testcontroller\fR sets up a flow in each OpenFlow switch whenever it receives a packet whose destination is known due through MAC learning. This option disables flow setup, so that every packet in the network passes through the controller. .IP This option is most useful for debugging. It reduces switching performance, so it should not be used in production. . .TP \fB\-\-max\-idle=\fIsecs\fR|\fBpermanent\fR Sets \fIsecs\fR as the number of seconds that a flow set up by the controller will remain in the switch's flow table without any matching packets being seen. If \fBpermanent\fR is specified, which is not recommended, flows will never expire. The default is 60 seconds. .IP This option has no effect when \fB\-n\fR (or \fB\-\-noflow\fR) is in use (because the controller does not set up flows in that case). . .IP "\fB\-H\fR" .IQ "\fB\-\-hub\fR" By default, the controller acts as an L2 MAC-learning switch. This option changes its behavior to that of a hub that floods packets on all but the incoming port. .IP If \fB\-H\fR (or \fB\-\-hub\fR) and \fB\-n\fR (or \fB\-\-noflow\fR) are used together, then the cumulative effect is that every packet passes through the controller and every packet is flooded. .IP This option is most useful for debugging. It reduces switching performance, so it should not be used in production. . .IP "\fB\-w\fR[\fIwildcard_mask\fR]" .IQ "\fB\-\-wildcards\fR[\fB=\fIwildcard_mask\fR]\fR" By default, \fBovs\-testcontroller\fR sets up exact-match flows. This option allows it to set up wildcarded flows, which may reduce flow setup latency by causing less traffic to be sent up to the controller. .IP The optional \fIwildcard_mask\fR is an OpenFlow wildcard bitmask in hexadecimal that specifies the fields to wildcard. If no \fIwildcard_mask\fR is specified, the default value 0x2820F0 is used which specifies L2-only switching and wildcards L3 and L4 fields. Another interesting value is 0x2000EC, which specifies L3-only switching and wildcards L2 and L4 fields. .IP This option has no effect when \fB\-n\fR (or \fB\-\-noflow\fR) is in use (because the controller does not set up flows in that case). . .IP "\fB\-N\fR" .IQ "\fB\-\-normal\fR" By default, \fBovs\-testcontroller\fR directs packets to a particular port or floods them. This option causes it to direct non-flooded packets to the OpenFlow \fBOFPP_NORMAL\fR port. This allows the switch itself to make decisions about packet destinations. Support for \fBOFPP_NORMAL\fR is optional in OpenFlow, so this option may not well with some non-Open vSwitch switches. . .IP "\fB\-\-mute\fR" Prevents ovs\-testcontroller from replying to any OpenFlow messages sent to it by switches. .IP This option is only for debugging the Open vSwitch implementation of ``fail open'' mode. It must not be used in production. . .IP "\fB\-q \fIid\fR" .IQ "\fB\-\-queue=\fIid\fR" By default, \fBovs\-testcontroller\fR uses the default OpenFlow queue for sending packets and setting up flows. Use one of these options, supplying \fIid\fR as an OpenFlow queue ID as a decimal number, to instead use that specific queue. .IP This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and with \fB\-H\fR or \fB\-\-hub\fR. If more than one is specified then this option takes precedence. .IP This option may be useful for testing or debugging quality of service setups. . .IP "\fB\-Q \fIport-name\fB:\fIqueue-id\fR" .IP "\fB\-\-port\-queue \fIport-name\fB:\fIqueue-id\fR" Configures packets received on the port named \fIport-name\fR (e.g. \fBeth0\fR) to be output on OpenFlow queue ID \fIqueue-id\fR (specified as a decimal number). For the specified port, this option overrides the default specified on \fB\-q\fR or \fB\-\-queue\fR. .IP This option may be specified any number of times with different \fIport-name\fR arguments. .IP This option is incompatible with \fB\-N\fR or \fB\-\-normal\fR and with \fB\-H\fR or \fB\-\-hub\fR. If more than one is specified then this option takes precedence. .IP This option may be useful for testing or debugging quality of service setups. . .IP "\fB\-\-with\-flows \fIfile\fR" When a switch connects, push the flow entries as described in \fIfile\fR. Each line in \fIfile\fR is a flow entry in the format described for the \fBadd\-flows\fR command in the \fBFlow Syntax\fR section of the \fBovs\-ofctl\fR(8) man page. .IP Use this option more than once to add flows from multiple files. . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-peer-ca-cert.man .ds DD .so lib/daemon.man .so lib/vlog.man .so lib/unixctl.man .so lib/common.man .so lib/ofp-version.man . .SH EXAMPLES .PP To bind locally to port 6653 (the default) and wait for incoming connections from OpenFlow switches: .IP \fB% ovs\-testcontroller ptcp:\fR .SH "BUGS" .PP Configuring a Citrix XenServer to connect to a particular controller only points the remote OVSDB management connection to that controller. It does not also configure OpenFlow connections, because the manager is expected to do that over the management protocol. \fBovs\-testcontroller\fR is not an Open vSwitch manager and does not know how to do that. .PP As a stopgap workaround, \fBovs\-vsctl\fR can wait for an OVSDB connection and set the controller, e.g.: .IP \fB% ovs\-vsctl \-t0 \-\-db=pssl: \-\-certificate=cert.pem \-\-ca\-cert=none \-\-private\-key=privkey.pem \-\-peer\-ca\-cert=cacert.pem set\-controller ssl:\fIip\fR .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-ofctl (8), .BR ovs\-dpctl (8) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-pipegen.py0000644000000000000000000000013213534540071020465 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.817846319 openvswitch-2.5.9/utilities/ovs-pipegen.py0000755000175000017500000000744513534540071022170 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # Copyright (c) 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import random import sys import textwrap def flow_str(stage, match, action, priority=32768): mtd_match = "metadata=%d" % stage if match: mtd_match += "," + match return "priority=%d %s,actions=%s" % (priority, mtd_match, action) def resubmit(nxt): return "load:%d->OXM_OF_METADATA[],resubmit(,0)" % nxt def rand_ip_mask(): return ("%d.%d.%d.%d" % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)), random.choice([8, 16, 24, 32])) def rand_bool(): return bool(random.randint(0, 1)) def l2(stage, action): mac = ["%x" % random.randint(0, 2 ** 8 - 1) for x in range(6)] mac = [x.zfill(2) for x in mac] mac = ":".join(mac) return flow_str(stage, "dl_dst=%s" % mac, action) def l3(stage, action): ip, mask = rand_ip_mask() return flow_str(stage, "ip,ip_dst=%s/%d" % (ip, mask), action, priority=mask) def l4(stage, action): match = "tcp" if rand_bool(): match += ",ip_src=%s/%d" % rand_ip_mask() if rand_bool(): match += ",ip_dst=%s/%d" % rand_ip_mask() src_dst = "tp_src" if rand_bool() else "tp_dst" match += ",%s=%d" % (src_dst, random.randint(1024, 2**16 - 1)) return flow_str(stage, match, action) def pipeline(size): pipeline = [l2, l3, l4, l2] flows = [] for stage in xrange(len(pipeline)): action = resubmit(stage + 1) flows += [pipeline[stage](stage, action) for _ in xrange(size)] flows.append(flow_str(stage, "", action, priority=1)) flows.append(flow_str(len(pipeline), "", "in_port")) for f in flows: print f def main(): description = textwrap.dedent( """ Generate a test OpenFlow pipeline. Open vSwitch relies heavily on flow caching to get good performance for packet processing. While on average, this produces good results, performance is heavily depedent on the slow path OpenFlow tables, and how they're translated into datapath megaflows. For this reason, when doing performance testing it's important to run with "realistic" OpenFlow tables to ensure results will stand up in the real world. This script generates a simple OpenFlow pipeline intended to simulate realistic network virtualization workloads. All traffic received is run through a series of OpenFlow tables designed to simulate a logical switch, router, and firewall, before forwarded back on the in_port. """) epilog = textwrap.dedent( """ typical usage: ovs-ofctl del-flows bridge \\ && %s | ovs-ofctl add-flows bridge - \\ && ovs-ofctl dump-flows bridge """ % sys.argv[0]) parser = argparse.ArgumentParser(description=description, epilog=epilog, formatter_class=\ argparse.RawDescriptionHelpFormatter) parser.add_argument("--size", dest="size", default=1000, help="Size (rules) of each OpenFlow table.") args=parser.parse_args() pipeline(int(args.size)) if __name__ == "__main__": main() openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-dpctl.c0000644000000000000000000000013213534540071017736 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801425.193856463 openvswitch-2.5.9/utilities/ovs-dpctl.c0000644000175000017500000001426313534540071021432 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "dirs.h" #include "dpctl.h" #include "fatal-signal.h" #include "odp-util.h" #include "ofp-parse.h" #include "packets.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" static struct dpctl_params dpctl_p; OVS_NO_RETURN static void usage(void *userdata OVS_UNUSED); static void parse_options(int argc, char *argv[]); static void dpctl_print(void *userdata OVS_UNUSED, bool error, const char *msg) { FILE *outfile = error ? stderr : stdout; fputs(msg, outfile); } int main(int argc, char *argv[]) { int error; set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); dpctl_p.is_appctl = false; dpctl_p.output = dpctl_print; dpctl_p.usage = usage; error = dpctl_run_command(argc - optind, (const char **) argv + optind, &dpctl_p); return error ? EXIT_FAILURE : EXIT_SUCCESS; } static void parse_options(int argc, char *argv[]) { enum { OPT_CLEAR = UCHAR_MAX + 1, OPT_MAY_CREATE, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"statistics", no_argument, NULL, 's'}, {"clear", no_argument, NULL, OPT_CLEAR}, {"may-create", no_argument, NULL, OPT_MAY_CREATE}, {"more", no_argument, NULL, 'm'}, {"timeout", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { unsigned long int timeout; int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 's': dpctl_p.print_statistics = true; break; case OPT_CLEAR: dpctl_p.zero_statistics = true; break; case OPT_MAY_CREATE: dpctl_p.may_create = true; break; case 'm': dpctl_p.verbosity++; break; case 't': timeout = strtoul(optarg, NULL, 10); if (timeout <= 0) { ovs_fatal(0, "value %s on -t or --timeout is not at least 1", optarg); } else { time_alarm(timeout); } break; case 'h': usage(NULL); case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void *userdata OVS_UNUSED) { printf("%s: Open vSwitch datapath management utility\n" "usage: %s [OPTIONS] COMMAND [ARG...]\n" " add-dp DP [IFACE...] add new datapath DP (with IFACEs)\n" " del-dp DP delete local datapath DP\n" " add-if DP IFACE... add each IFACE as a port on DP\n" " set-if DP IFACE... reconfigure each IFACE within DP\n" " del-if DP IFACE... delete each IFACE from DP\n" " dump-dps display names of all datapaths\n" " show show basic info on all datapaths\n" " show DP... show basic info on each DP\n" " dump-flows [DP] display flows in DP\n" " add-flow [DP] FLOW ACTIONS add FLOW with ACTIONS to DP\n" " mod-flow [DP] FLOW ACTIONS change FLOW actions to ACTIONS in DP\n" " get-flow [DP] ufid:UFID fetch flow corresponding to UFID\n" " del-flow [DP] FLOW delete FLOW from DP\n" " del-flows [DP] delete all flows from DP\n" " dump-conntrack [DP] display conntrack entries\n" " flush-conntrack [DP] delete all conntrack entries\n" "Each IFACE on add-dp, add-if, and set-if may be followed by\n" "comma-separated options. See ovs-dpctl(8) for syntax, or the\n" "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n" "For COMMAND dump-flows, add-flow, mod-flow, del-flow and\n" "del-flows, DP is optional if there is only one datapath.\n", program_name, program_name); vlog_usage(); printf("\nOptions for show and mod-flow:\n" " -s, --statistics print statistics for port or flow\n" "\nOptions for dump-flows:\n" " -m, --more increase verbosity of output\n" "\nOptions for mod-flow:\n" " --may-create create flow if it doesn't exist\n" " --clear reset existing stats to zero\n" "\nOther options:\n" " -t, --timeout=SECS give up after SECS seconds\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } openvswitch-2.5.9/utilities/PaxHeaders.82075/nlmon.c0000644000000000000000000000013213534540071017146 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801425.189856434 openvswitch-2.5.9/utilities/nlmon.c0000644000175000017500000001127213534540071020637 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include "netlink.h" #include "netlink-socket.h" #include "ofpbuf.h" #include "poll-loop.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" static const struct nl_policy rtnlgrp_link_policy[] = { [IFLA_IFNAME] = { .type = NL_A_STRING, .optional = false }, [IFLA_MASTER] = { .type = NL_A_U32, .optional = true }, }; int main(int argc OVS_UNUSED, char *argv[]) { uint64_t buf_stub[4096 / 64]; struct nl_sock *sock; struct ofpbuf buf; int error; set_program_name(argv[0]); vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_DBG); error = nl_sock_create(NETLINK_ROUTE, &sock); if (error) { ovs_fatal(error, "could not create rtnetlink socket"); } error = nl_sock_join_mcgroup(sock, RTNLGRP_LINK); if (error) { ovs_fatal(error, "could not join RTNLGRP_LINK multicast group"); } ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); for (;;) { error = nl_sock_recv(sock, &buf, false); if (error == EAGAIN) { /* Nothing to do. */ } else if (error == ENOBUFS) { ovs_error(0, "network monitor socket overflowed"); } else if (error) { ovs_fatal(error, "error on network monitor socket"); } else { struct iff_flag { unsigned int flag; const char *name; }; static const struct iff_flag flags[] = { { IFF_UP, "UP", }, { IFF_BROADCAST, "BROADCAST", }, { IFF_DEBUG, "DEBUG", }, { IFF_LOOPBACK, "LOOPBACK", }, { IFF_POINTOPOINT, "POINTOPOINT", }, { IFF_NOTRAILERS, "NOTRAILERS", }, { IFF_RUNNING, "RUNNING", }, { IFF_NOARP, "NOARP", }, { IFF_PROMISC, "PROMISC", }, { IFF_ALLMULTI, "ALLMULTI", }, { IFF_MASTER, "MASTER", }, { IFF_SLAVE, "SLAVE", }, { IFF_MULTICAST, "MULTICAST", }, { IFF_PORTSEL, "PORTSEL", }, { IFF_AUTOMEDIA, "AUTOMEDIA", }, { IFF_DYNAMIC, "DYNAMIC", }, }; struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)]; struct nlmsghdr *nlh; struct ifinfomsg *iim; int i; nlh = ofpbuf_at(&buf, 0, NLMSG_HDRLEN); iim = ofpbuf_at(&buf, NLMSG_HDRLEN, sizeof *iim); if (!iim) { ovs_error(0, "received bad rtnl message (no ifinfomsg)"); continue; } if (!nl_policy_parse(&buf, NLMSG_HDRLEN + sizeof(struct ifinfomsg), rtnlgrp_link_policy, attrs, ARRAY_SIZE(rtnlgrp_link_policy))) { ovs_error(0, "received bad rtnl message (policy)"); continue; } printf("netdev %s changed (%s):\n", nl_attr_get_string(attrs[IFLA_IFNAME]), (nlh->nlmsg_type == RTM_NEWLINK ? "RTM_NEWLINK" : nlh->nlmsg_type == RTM_DELLINK ? "RTM_DELLINK" : nlh->nlmsg_type == RTM_GETLINK ? "RTM_GETLINK" : nlh->nlmsg_type == RTM_SETLINK ? "RTM_SETLINK" : "other")); printf("\tflags:"); for (i = 0; i < ARRAY_SIZE(flags); i++) { if (iim->ifi_flags & flags[i].flag) { printf(" %s", flags[i].name); } } printf("\n"); if (attrs[IFLA_MASTER]) { uint32_t idx = nl_attr_get_u32(attrs[IFLA_MASTER]); char ifname[IFNAMSIZ]; if (!if_indextoname(idx, ifname)) { strcpy(ifname, "unknown"); } printf("\tmaster=%"PRIu32" (%s)\n", idx, ifname); } } nl_sock_wait(sock, POLLIN); poll_block(); } } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-sim.1.xml0000644000000000000000000000013213534540071020135 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.805846231 openvswitch-2.5.9/utilities/ovs-sim.1.xml0000644000175000017500000002455113534540071021632 0ustar00jpettitjpettit00000000000000

    Name

    ovs-sim -- Open vSwitch simulator environment

    Synopsis

    ovs-sim [option]... [script]...

    Description

    ovs-sim provides a convenient environment for running one or more Open vSwitch instances and related software in a sandboxed simulation environment.

    To use ovs-sim, first build Open vSwitch, then invoke it directly from the build directory, e.g.:

    git clone https://github.com/openvswitch/ovs.git
    cd ovs
    ./configure
    make
    utilities/ovs-sim
        

    When invoked in the most ordinary way as shown above, ovs-sim does the following:

    1. Creates a directory sandbox as a subdirectory of the current directory (first destroying such a directory if it already exists) and cds into that directory.
    2. Installs all of the Open vSwitch manpages into a man subdirectory of sandbox and adjusts the MANPATH environment variable so that man and other manpage viewers can find them.
    3. Creates a simulated Open vSwitch named main and sets it up as the default target for OVS commands, as if the following ovs-sim commands had been run:

                sim_add main
                as main
              

      See Commands, below, for an explanation.

    4. Runs any scripts specified on the command line (see Options below). The scripts can use arbitrary Bash syntax, plus the additional commands described under Commands, below.
    5. If no scripts were specified, or if or was specified, invokes an interactive Bash subshell. The user can use arbitrary Bash commands, plus the additional commands described under Commands, below.

    ovs-sim and the sandbox environment that it creates does not require superuser or other special privileges. Generally, it should not be run with such privileges.

    Options

    ovs-sim accepts the following options and arguments:

    script
    Runs script, which should be a Bash script, within a subshell after initializing. If multiple script arguments are given, then they are run in the order given. If any script exits with a nonzero exit code, then ovs-sim exits immediately with the same exit code.
    By default, if any script is specified, ovs-sim exits as soon as the scripts finish executing. With this option, or if no scripts are specified, ovs-sim instead starts an interactive Bash session.

    Commands

    Scripts and interactive usage may use the following commands implemented by ovs-sim. They are implemented as Bash shell functions exported to subshells.

    Basic Commands

    These are the basic commands for working with sandboxed Open vSwitch instances.

    sim_add sandbox

    Starts a new simulated Open vSwitch instance named sandbox. Files related to the instance, such as logs, databases, sockets, and pidfiles, are created in a subdirectory also named sandbox. Afterward, the as command (see below) can be used to run Open vSwitch utilities in the context of the new sandbox.

    The new sandbox starts out without any bridges. Use ovs-vsctl in the context of the new sandbox to create a bridge, e.g.:

    sim_add hv0           # Create sandbox hv0.  
    as hv0                # Set hv0 as default sandbox.
    ovs-vsctl add-br br0  # Add bridge br0 inside hv0.
            

    The Open vSwitch instances that sim_add create enable ``dummy'' devices. This means that bridges and interfaces can be created with type dummy to indicate that they should be totally simulated, without any reference to system entities. In fact, ovs-sim also configures Open vSwitch so that the default system type of bridges and interfaces are replaced by dummy devices. Other types of devices, however, retain their usual functions, which means that, e.g., vxlan tunnels still act as tunnels (see README-native-tunneling.md).

    as sandbox

    Sets sandbox as the default simulation target for Open vSwitch commands (e.g. ovs-vsctl, ovs-ofctl, ovs-appctl).

    This command updates the beginning of the shell prompt to indicate the new default target.

    as sandbox command arg...
    Runs the given command with sandbox as the simulation target, e.g. as hv0 ovs-vsctl add-br br0 runs ovs-vsctl add-br br0 within sandbox hv0. The default target is unchanged.

    Interconnection Network Commands

    When multiple sandboxed Open vSwitch instances exist, one will inevitably want to connect them together. These commands allow for that. Conceptually, an interconnection network is a switch that ovs-sim makes it easy to plug into other switches in other sandboxed Open vSwitch instances. Interconnection networks are implemented as bridges in the main switch that ovs-sim creates by default, so to use interconnection networks please avoid working with main directly.

    net_add network
    Creates a new interconnection network named network.
    net_attach network bridge
    Adds a new port to bridge in the default sandbox (as set with as) and plugs it into the network interconnection network. network must already have been created by a previous invocation of net_add. The default sandbox must not be main.

    OVN Commands

    These commands interact with OVN, the Open Virtual Network.

    ovn_start
    Creates and initializes the central OVN databases (both ovn-sb(5) and ovn-nb) and starts an instance of ovsdb-server for each one. Also starts an instance of ovn-northd.
    ovn_attach network bridge ip [masklen]
    First, this command attaches bridge to interconnection network network, just like net_attach network bridge. Second, it configures (simulated) IP address ip (with network mask length masklen, which defaults to 24) on bridge. Finally, it configures the Open vSwitch database to work with OVN and starts ovn-controller.

    Examples

    The following creates a pair of Open vSwitch instances hv0 and hv1, adds a port named vif0 or vif1, respectively, to each one, and then connects the two through an interconnection network n1:

    net_add n1
    for i in 0 1; do
        sim_add hv$i
        as hv$i ovs-vsctl add-br br0 -- add-port br0 vif$i
        as hv$i net_attach n1 br0
    done
        

    Here's an extended version that also starts OVN:

    ovn_start
    ovn-nbctl lswitch-add lsw0
    
    net_add n1
    for i in 0 1; do
        sim_add hv$i
        as hv$i
        ovs-vsctl add-br br-phys
        ovn_attach n1 br-phys 192.168.0.`expr $i + 1`
        ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lp$i
        ovn-nbctl lport-add lsw0 lp$i
        ovn-nbctl lport-set-addresses lp$i f0:00:00:00:00:0$i
    done
        

    Here's a primitive OVN ``scale test'' (adjust the scale by changing n in the first line :

    n=200; export n
    ovn_start
    net_add n1
    ovn-nbctl lswitch-add br0
    for i in `seq $n`; do
        (sim_add hv$i
        as hv$i
        ovs-vsctl add-br br-phys
        y=$(expr $i / 256)
        x=$(expr $i % 256)
        ovn_attach n1 br-phys 192.168.$y.$x
        ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lp$i) &
        case $i in
            *50|*00) echo $i; wait ;;
        esac
    done
    wait
    for i in `seq $n`; do
        yy=$(printf %02x $(expr $i / 256))
        xx=$(printf $02x $(expr $i % 256))
        ovn-nbctl lport-add br0 lp$i
        ovn-nbctl lport-set-addresses lp$i f0:00:00:00:$yy:$xx
    done
        

    When the scale test has finished initializing, you can watch the logical ports come up with a command like this:

    watch 'for i in `seq $n`; do if test `ovn-nbctl lport-get-up lp$i` != up; then echo $i; fi; done'
        
    openvswitch-2.5.9/utilities/PaxHeaders.82075/bugtool0000644000000000000000000000013213534540120017250 xustar0030 mtime=1567801424.625852276 30 atime=1567801425.625859648 30 ctime=1567801424.625852276 openvswitch-2.5.9/utilities/bugtool/0000755000175000017500000000000013534540120021013 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-bond-show0000644000000000000000000000013213534540071023433 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.861846644 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-bond-show0000755000175000017500000000133413534540071025125 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012 Nicira, Inc. ovs-appctl bond/show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-lacp-show0000644000000000000000000000013213534540071023430 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.841846496 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-lacp-show0000755000175000017500000000133413534540071025122 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2011 Nicira, Inc. ovs-appctl lacp/show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-conntrack-dump0000644000000000000000000000013213534540071024460 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.869846703 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-conntrack-dump0000644000175000017500000000134713534540071026153 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2016 Nicira, Inc. ovs-appctl dpctl/dump-conntrack openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-ovs-ofctl-show0000644000000000000000000000013213534540071024425 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.857846614 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-ovs-ofctl-show0000755000175000017500000000147613534540071026126 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. for bridge in `ovs-vsctl list-br` do echo "ovs-ofctl show ${bridge}" ovs-ofctl show "$bridge" echo "" done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-fdb-show0000644000000000000000000000013213534540071023244 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.841846496 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-fdb-show0000755000175000017500000000152413534540071024737 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2014 Nicira, Inc. for bridge in `ovs-vsctl -- --real list-br` do echo "ovs-appctl fdb/show ${bridge}" ovs-appctl fdb/show "${bridge}" echo "" done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool.8.in0000644000000000000000000000013213534540071022310 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.773845995 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool.8.in0000644000175000017500000000507113534540071024001 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-bugtool 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-bugtool . .SH NAME ovs\-bugtool \- Open vSwitch bug reporting utility . .SH SYNOPSIS .B ovs\-bugtool . .SH DESCRIPTION Generates a debug bundle with useful information about Open vSwitch on this system and places it in \fB/var/log/ovs-bugtool\fR. . .SH "COLLECTION OPTIONS" .PP These options influence what categories of data \fBovs\-bugtool\fR collects. . .IP "\fB\-\-entries=\fIlist\fR" Collect the capabilities specified in the comma-separated \fIlist\fR. .IP "\fB\-\-all\fR" Collect all available capabilities. .IP "\fB\-\-ovs\fR" In addition to Open vSwitch configuration and status, \fBovs\-bugtool\fR can collect a variety of relevant system information. This option limits collection to Open vSwitch-specific categories. .IP "\fB\-\-log\-days=\fIdays\fR" Include the logs with last modification time in the previous \fIdays\fR days in the debug bundle. The number of log files included has a big impact on the eventual bundle size. The default value is 20 days. .IP "\fB\-y\fR" .IQ "\fB\-\-yestoall\fR" Answer yes to all prompts. .IP "\fB\-\-capabilities\fR" Writes the categories that \fBovs\-bugtool\fR can collect on stdout in XML, then exits. . .SH "OUTPUT OPTIONS" .PP These options influence the format and destination of \fBovs\-bugtool\fR output. . .IP "\fB\-\-output=\fIfiletype\fR" Generates a debug bundle with the specified file type. Options include \fBtar\fR, \fBtar.gz\fR, \fBtar.bz2\fR, and \fBzip\fR. .IP "\fB\-\-outfile=\fIfile\fR" Write output to \fIfile\fR. Mutually exclusive with \fB\-\-outfd\fR. .IP "\fB\-\-outfd=\fIfd\fR" Write output to file descriptor \fIfd\fR. This option must be used with \fB\-\-output=tar\fR. .IP "\fB\-\-unlimited\fR" Do not exclude files which are too large. Also skip checking free disk space. By default up to 90 percent of the free disk space can be used. .IP "\fB\-\-debug\fR" Print verbose debugging output. . .SH "OTHER OPTIONS" . .IP "\fB\-s\fR" .IQ "\fB\-\-silent\fR" Suppress most output to stdout. .IP "\fB\-\-help\fR" Print a summary of \fBovs\-bugtool\fR usage to stdout, then exit. . .SH EXAMPLES .PP Here's a collection of some commonly useful options: .PP \fBovs\-bugtool \-y \-s \-\-output=tar.gz \-\-outfile=/var/log/bugtool-report.tgz\fR . .SH BUGS \fBovs\-bugtool\fR makes many assumptions about file locations and the availability of system utilities. It has been tested on Debian and Red Hat and derived distributions. On other distributions it is likely to be less useful. openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-ovsdb-dump0000644000000000000000000000013213534540071023613 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.857846614 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-ovsdb-dump0000755000175000017500000000141713534540071025307 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2011 Nicira, Inc. ovsdb-client -f csv dump unix:/var/run/openvswitch/db.sock Open_vSwitch openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool.in0000644000000000000000000000013213534540071022142 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.873846733 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool.in0000755000175000017500000012733313534540071023644 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General Public # License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Copyright (c) 2005, 2007 XenSource Ltd. # Copyright (c) 2010, 2011, 2012, 2013, 2015 Nicira, Inc. # # To add new entries to the bugtool, you need to: # # Create a new capability. These declare the new entry to the GUI, including # the expected size, time to collect, privacy implications, and whether the # capability should be selected by default. One capability may refer to # multiple files, assuming that they can be reasonably grouped together, and # have the same privacy implications. You need: # # A new CAP_ constant. # A cap() invocation to declare the capability. # # You then need to add calls to main() to collect the files. These will # typically be calls to the helpers file_output(), tree_output(), cmd_output(), # or func_output(). # import warnings warnings.filterwarnings(action="ignore", category=DeprecationWarning) import getopt import re import os import StringIO import sys import tarfile import time import commands import pprint from xml.dom.minidom import parse, getDOMImplementation import zipfile from subprocess import Popen, PIPE from select import select from signal import SIGTERM, SIGUSR1 import md5 import platform import fcntl import glob import urllib import socket import base64 OS_RELEASE = platform.release() # # Files & directories # APT_SOURCES_LIST = "/etc/apt/sources.list" APT_SOURCES_LIST_D = "/etc/apt/sources.list.d" BUG_DIR = "/var/log/ovs-bugtool" PLUGIN_DIR = "@pkgdatadir@/bugtool-plugins" GRUB_CONFIG = '/boot/grub/menu.lst' BOOT_KERNEL = '/boot/vmlinuz-' + OS_RELEASE BOOT_INITRD = '/boot/initrd-' + OS_RELEASE + '.img' PROC_PARTITIONS = '/proc/partitions' FSTAB = '/etc/fstab' PROC_MOUNTS = '/proc/mounts' ISCSI_CONF = '/etc/iscsi/iscsid.conf' ISCSI_INITIATOR = '/etc/iscsi/initiatorname.iscsi' PROC_CPUINFO = '/proc/cpuinfo' PROC_MEMINFO = '/proc/meminfo' PROC_IOPORTS = '/proc/ioports' PROC_INTERRUPTS = '/proc/interrupts' PROC_SCSI = '/proc/scsi/scsi' PROC_VERSION = '/proc/version' PROC_MODULES = '/proc/modules' PROC_DEVICES = '/proc/devices' PROC_FILESYSTEMS = '/proc/filesystems' PROC_CMDLINE = '/proc/cmdline' PROC_CONFIG = '/proc/config.gz' PROC_USB_DEV = '/proc/bus/usb/devices' PROC_NET_BONDING_DIR = '/proc/net/bonding' IFCFG_RE = re.compile(r'^.*/ifcfg-.*') ROUTE_RE = re.compile(r'^.*/route-.*') SYSCONFIG_HWCONF = '/etc/sysconfig/hwconf' SYSCONFIG_NETWORK = '/etc/sysconfig/network' SYSCONFIG_NETWORK_SCRIPTS = '/etc/sysconfig/network-scripts' PROC_NET_VLAN_DIR = '/proc/net/vlan' PROC_NET_SOFTNET_STAT = '/proc/net/softnet_stat' MODPROBE_CONF = '/etc/modprobe.conf' MODPROBE_DIR = '/etc/modprobe.d' RESOLV_CONF = '/etc/resolv.conf' MPP_CONF = '/etc/mpp.conf' MULTIPATH_CONF = '/etc/multipath.conf' NSSWITCH_CONF = '/etc/nsswitch.conf' NTP_CONF = '/etc/ntp.conf' IPTABLES_CONFIG = '/etc/sysconfig/iptables-config' HOSTS = '/etc/hosts' HOSTS_ALLOW = '/etc/hosts.allow' HOSTS_DENY = '/etc/hosts.deny' DHCP_LEASE_DIR = ['/var/lib/dhclient', '/var/lib/dhcp3'] OPENVSWITCH_LOG_DIR = '@LOGDIR@/' OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch' # RHEL OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db' OPENVSWITCH_COMPACT_DB = '@DBDIR@/bugtool-compact-conf.db' OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid' VAR_LOG_DIR = '/var/log/' VAR_LOG_CORE_DIR = '/var/log/core' YUM_LOG = '/var/log/yum.log' YUM_REPOS_DIR = '/etc/yum.repos.d' # # External programs # os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:@pkgdatadir@/scripts' ARP = 'arp' CAT = 'cat' CHKCONFIG = 'chkconfig' DF = 'df' DMESG = 'dmesg' DMIDECODE = 'dmidecode' DMSETUP = 'dmsetup' DPKG_QUERY = 'dpkg-query' ETHTOOL = 'ethtool' FDISK = 'fdisk' FIND = 'find' IFCONFIG = 'ifconfig' IPTABLES = 'iptables' ISCSIADM = 'iscsiadm' LOSETUP = 'losetup' LS = 'ls' LSPCI = 'lspci' MD5SUM = 'md5sum' MODINFO = 'modinfo' MPPUTIL = 'mppUtil' MULTIPATHD = 'multipathd' NETSTAT = 'netstat' OVS_DPCTL = 'ovs-dpctl' OVS_OFCTL = 'ovs-ofctl' OVS_VSCTL = 'ovs-vsctl' PS = 'ps' ROUTE = 'route' RPM = 'rpm' SG_MAP = 'sg_map' SYSCTL = 'sysctl' TC = 'tc' UPTIME = 'uptime' ZCAT = 'zcat' # # PII -- Personally identifiable information. Of particular concern are # things that would identify customers, or their network topology. # Passwords are never to be included in any bug report, regardless of any PII # declaration. # # NO -- No PII will be in these entries. # YES -- PII will likely or certainly be in these entries. # MAYBE -- The user may wish to audit these entries for PII. # IF_CUSTOMIZED -- If the files are unmodified, then they will contain no PII, # but since we encourage customers to edit these files, PII may have been # introduced by the customer. This is used in particular for the networking # scripts in dom0. # PII_NO = 'no' PII_YES = 'yes' PII_MAYBE = 'maybe' PII_IF_CUSTOMIZED = 'if_customized' KEY = 0 PII = 1 MIN_SIZE = 2 MAX_SIZE = 3 MIN_TIME = 4 MAX_TIME = 5 MIME = 6 CHECKED = 7 HIDDEN = 8 MIME_DATA = 'application/data' MIME_TEXT = 'text/plain' INVENTORY_XML_ROOT = "system-status-inventory" INVENTORY_XML_SUMMARY = 'system-summary' INVENTORY_XML_ELEMENT = 'inventory-entry' CAP_XML_ROOT = "system-status-capabilities" CAP_XML_ELEMENT = 'capability' CAP_BOOT_LOADER = 'boot-loader' CAP_DISK_INFO = 'disk-info' CAP_HARDWARE_INFO = 'hardware-info' CAP_KERNEL_INFO = 'kernel-info' CAP_LOSETUP_A = 'loopback-devices' CAP_MULTIPATH = 'multipath' CAP_NETWORK_CONFIG = 'network-config' CAP_NETWORK_INFO = 'network-info' CAP_NETWORK_STATUS = 'network-status' CAP_OPENVSWITCH_LOGS = 'ovs-system-logs' CAP_PROCESS_LIST = 'process-list' CAP_SYSTEM_LOGS = 'system-logs' CAP_SYSTEM_SERVICES = 'system-services' CAP_YUM = 'yum' KB = 1024 MB = 1024 * 1024 caps = {} cap_sizes = {} unlimited_data = False dbg = False # Default value for the number of days to collect logs. log_days = 20 log_last_mod_time = None free_disk_space = None def cap(key, pii=PII_MAYBE, min_size=-1, max_size=-1, min_time=-1, max_time=-1, mime=MIME_TEXT, checked=True, hidden=False): caps[key] = (key, pii, min_size, max_size, min_time, max_time, mime, checked, hidden) cap_sizes[key] = 0 cap(CAP_BOOT_LOADER, PII_NO, max_size=3*KB, max_time=5) cap(CAP_DISK_INFO, PII_MAYBE, max_size=50*KB, max_time=20) cap(CAP_HARDWARE_INFO, PII_MAYBE, max_size=2*MB, max_time=20) cap(CAP_KERNEL_INFO, PII_MAYBE, max_size=120*KB, max_time=5) cap(CAP_LOSETUP_A, PII_MAYBE, max_size=KB, max_time=5) cap(CAP_MULTIPATH, PII_MAYBE, max_size=20*KB, max_time=10) cap(CAP_NETWORK_CONFIG, PII_IF_CUSTOMIZED, min_size=0, max_size=5*MB) cap(CAP_NETWORK_INFO, PII_YES, max_size=50*MB, max_time=30) cap(CAP_NETWORK_STATUS, PII_YES, max_size=-1, max_time=30) cap(CAP_OPENVSWITCH_LOGS, PII_MAYBE, max_size=-1, max_time=5) cap(CAP_PROCESS_LIST, PII_YES, max_size=30*KB, max_time=20) cap(CAP_SYSTEM_LOGS, PII_MAYBE, max_size=200*MB, max_time=5) cap(CAP_SYSTEM_SERVICES, PII_NO, max_size=5*KB, max_time=20) cap(CAP_YUM, PII_IF_CUSTOMIZED, max_size=10*KB, max_time=30) ANSWER_YES_TO_ALL = False SILENT_MODE = False entries = None data = {} dev_null = open('/dev/null', 'r+') def output(x): global SILENT_MODE if not SILENT_MODE: print x def output_ts(x): output("[%s] %s" % (time.strftime("%x %X %Z"), x)) def cmd_output(cap, args, label=None, filter=None, binary=False): if cap in entries: if not label: if isinstance(args, list): a = [aa for aa in args] a[0] = os.path.basename(a[0]) label = ' '.join(a) else: label = args data[label] = {'cap': cap, 'cmd_args': args, 'filter': filter, 'binary': binary} def file_output(cap, path_list, newest_first=False, last_mod_time=None): """ If newest_first is True, the list of files in path_list is sorted by file modification time in descending order, else its sorted in ascending order. """ if cap in entries: path_entries = [] for path in path_list: try: s = os.stat(path) except OSError, e: continue if last_mod_time is None or s.st_mtime >= last_mod_time: path_entries.append((path, s)) mtime = lambda(path, stat): stat.st_mtime path_entries.sort(key=mtime, reverse=newest_first) for p in path_entries: if check_space(cap, p[0], p[1].st_size): data[p] = {'cap': cap, 'filename': p[0]} def tree_output(cap, path, pattern=None, negate=False, newest_first=False, last_mod_time=None): """ Walks the directory tree rooted at path. Files in current dir are processed before files in sub-dirs. """ if cap in entries: if os.path.exists(path): for root, dirs, files in os.walk(path): fns = [fn for fn in [os.path.join(root, f) for f in files] if os.path.isfile(fn) and matches(fn, pattern, negate)] file_output(cap, fns, newest_first=newest_first, last_mod_time=last_mod_time) def prefix_output(cap, prefix, newest_first=False, last_mod_time=None): """ Output files with the same prefix. """ fns = [] for root, dirs, files in os.walk(os.path.dirname(prefix)): fns += [fn for fn in [os.path.join(root, f) for f in files] if fn.startswith(prefix)] file_output(cap, fns, newest_first=newest_first, last_mod_time=last_mod_time) def func_output(cap, label, func): if cap in entries: t = str(func).split() data[label] = {'cap': cap, 'func': func} def collect_data(): process_lists = {} for (k, v) in data.items(): cap = v['cap'] if v.has_key('cmd_args'): v['output'] = StringIOmtime() if not process_lists.has_key(cap): process_lists[cap] = [] process_lists[cap].append( ProcOutput(v['cmd_args'], caps[cap][MAX_TIME], v['output'], v['filter'], v['binary'])) elif v.has_key('filename') and v['filename'].startswith('/proc/'): # proc files must be read into memory try: f = open(v['filename'], 'r') s = f.read() f.close() if check_space(cap, v['filename'], len(s)): v['output'] = StringIOmtime(s) except: pass elif v.has_key('func'): try: s = v['func'](cap) except Exception, e: s = str(e) if check_space(cap, k, len(s)): v['output'] = StringIOmtime(s) run_procs(process_lists.values()) def main(argv=None): global ANSWER_YES_TO_ALL, SILENT_MODE global entries, data, dbg, unlimited_data, free_disk_space global log_days, log_last_mod_time # Filter flags only_ovs_info = False collect_all_info = True if '--help' in sys.argv: print """\ %(argv0)s: create status report bundles to assist in problem diagnosis usage: %(argv0)s OPTIONS By default, %(argv0)s prompts for permission to collect each form of status information and produces a .tar.gz file as output. The following options are available. --help display this help message, then exit -s, --silent suppress most output to stdout Options for categories of data to collect: --entries=CAP_A,CAP_B,... set categories of data to collect --all collect all categories --ovs collect only directly OVS-related info --log-days=DAYS collect DAYS worth of old logs -y, --yestoall suppress prompts to confirm collection --capabilities print categories as XML on stdout, then exit Output options: --output=FORMAT set output format to one of tar tar.bz2 tar.gz zip --outfile=FILE write output to FILE --outfd=FD write output to FD (requires --output=tar) --unlimited ignore default limits on sizes of data collected --debug print ovs-bugtool debug info on stdout\ """ % {'argv0': sys.argv[0]} sys.exit(0) # we need access to privileged files, exit if we are not running as root if os.getuid() != 0: print >>sys.stderr, "Error: ovs-bugtool must be run as root" return 1 output_file = None output_type = 'tar.gz' output_fd = -1 if argv is None: argv = sys.argv try: (options, params) = getopt.gnu_getopt( argv, 'sy', ['capabilities', 'silent', 'yestoall', 'entries=', 'output=', 'outfd=', 'outfile=', 'all', 'unlimited', 'debug', 'ovs', 'log-days=']) except getopt.GetoptError, opterr: print >>sys.stderr, opterr return 2 try: load_plugins(True) except: pass entries = [e for e in caps.keys() if caps[e][CHECKED]] for (k, v) in options: if k == '--capabilities': update_capabilities() print_capabilities() return 0 if k == '--output': if v in ['tar', 'tar.bz2', 'tar.gz', 'zip']: output_type = v else: print >>sys.stderr, "Invalid output format '%s'" % v return 2 # "-s" or "--silent" means suppress output (except for the final # output filename at the end) if k in ['-s', '--silent']: SILENT_MODE = True if k == '--entries' and v != '': entries = v.split(',') # If the user runs the script with "-y" or "--yestoall" we don't ask # all the really annoying questions. if k in ['-y', '--yestoall']: ANSWER_YES_TO_ALL = True if k == '--outfd': output_fd = int(v) try: old = fcntl.fcntl(output_fd, fcntl.F_GETFD) fcntl.fcntl(output_fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) except: print >>sys.stderr, "Invalid output file descriptor", output_fd return 2 if k == '--outfile': output_file = v elif k == '--all': entries = caps.keys() elif k == '--unlimited': unlimited_data = True elif k == '--debug': dbg = True ProcOutput.debug = True if k == '--ovs': only_ovs_info = True collect_all_info = False if k == '--log-days': log_days = int(v) if len(params) != 1: print >>sys.stderr, "Invalid additional arguments", str(params) return 2 if output_fd != -1 and output_type != 'tar': print >>sys.stderr, "Option '--outfd' only valid with '--output=tar'" return 2 if output_fd != -1 and output_file is not None: print >>sys.stderr, "Cannot set both '--outfd' and '--outfile'" return 2 if output_file is not None and not unlimited_data: free_disk_space = get_free_disk_space(output_file) * 90 / 100 log_last_mod_time = int(time.time()) - log_days * 86400 if ANSWER_YES_TO_ALL: output("Warning: '--yestoall' argument provided, will not prompt for individual files.") output(''' This application will collate dmesg output, details of the hardware configuration of your machine, information about the build of openvswitch that you are using, plus, if you allow it, various logs. The collated information will be saved as a .%s for archiving or sending to a Technical Support Representative. The logs may contain private information, and if you are at all worried about that, you should exit now, or you should explicitly exclude those logs from the archive. ''' % output_type) # assemble potential data file_output(CAP_BOOT_LOADER, [GRUB_CONFIG]) cmd_output(CAP_BOOT_LOADER, [LS, '-lR', '/boot']) cmd_output(CAP_BOOT_LOADER, [MD5SUM, BOOT_KERNEL, BOOT_INITRD], label='vmlinuz-initrd.md5sum') cmd_output(CAP_DISK_INFO, [FDISK, '-l']) file_output(CAP_DISK_INFO, [PROC_PARTITIONS, PROC_MOUNTS]) file_output(CAP_DISK_INFO, [FSTAB, ISCSI_CONF, ISCSI_INITIATOR]) cmd_output(CAP_DISK_INFO, [DF, '-alT']) cmd_output(CAP_DISK_INFO, [DF, '-alTi']) if len(pidof('iscsid')) != 0: cmd_output(CAP_DISK_INFO, [ISCSIADM, '-m', 'node']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_host']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/scsi_disk']) cmd_output(CAP_DISK_INFO, [LS, '-R', '/sys/class/fc_transport']) cmd_output(CAP_DISK_INFO, [SG_MAP, '-x']) func_output(CAP_DISK_INFO, 'scsi-hosts', dump_scsi_hosts) file_output(CAP_HARDWARE_INFO, [PROC_CPUINFO, PROC_MEMINFO, PROC_IOPORTS, PROC_INTERRUPTS]) cmd_output(CAP_HARDWARE_INFO, [DMIDECODE]) cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-n']) cmd_output(CAP_HARDWARE_INFO, [LSPCI, '-vv']) file_output(CAP_HARDWARE_INFO, [PROC_USB_DEV, PROC_SCSI]) file_output(CAP_HARDWARE_INFO, [SYSCONFIG_HWCONF]) cmd_output(CAP_HARDWARE_INFO, [LS, '-lR', '/dev']) file_output(CAP_KERNEL_INFO, [PROC_VERSION, PROC_MODULES, PROC_DEVICES, PROC_FILESYSTEMS, PROC_CMDLINE]) cmd_output(CAP_KERNEL_INFO, [ZCAT, PROC_CONFIG], label='config') cmd_output(CAP_KERNEL_INFO, [SYSCTL, '-A']) file_output(CAP_KERNEL_INFO, [MODPROBE_CONF]) tree_output(CAP_KERNEL_INFO, MODPROBE_DIR) func_output(CAP_KERNEL_INFO, 'modinfo', module_info) cmd_output(CAP_LOSETUP_A, [LOSETUP, '-a']) file_output(CAP_MULTIPATH, [MULTIPATH_CONF, MPP_CONF]) cmd_output(CAP_MULTIPATH, [DMSETUP, 'table']) func_output(CAP_MULTIPATH, 'multipathd_topology', multipathd_topology) cmd_output(CAP_MULTIPATH, [MPPUTIL, '-a']) if CAP_MULTIPATH in entries and collect_all_info: dump_rdac_groups(CAP_MULTIPATH) tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, IFCFG_RE) tree_output(CAP_NETWORK_CONFIG, SYSCONFIG_NETWORK_SCRIPTS, ROUTE_RE) file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS]) file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY]) file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_DEFAULT_SWITCH, OPENVSWITCH_SYSCONFIG_SWITCH]) cmd_output(CAP_NETWORK_INFO, [IFCONFIG, '-a']) cmd_output(CAP_NETWORK_INFO, [ROUTE, '-n']) cmd_output(CAP_NETWORK_INFO, [ARP, '-n']) cmd_output(CAP_NETWORK_INFO, [NETSTAT, '-an']) for dir in DHCP_LEASE_DIR: tree_output(CAP_NETWORK_INFO, dir) for table in ['filter', 'nat', 'mangle', 'raw', 'security']: cmd_output(CAP_NETWORK_INFO, [IPTABLES, '-t', table, '-nL']) for p in os.listdir('/sys/class/net/'): try: f = open('/sys/class/net/%s/type' % p, 'r') t = f.readline() f.close() if os.path.islink('/sys/class/net/%s/device' % p) and int(t) == 1: # ARPHRD_ETHER cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-S', p]) if not p.startswith('vif') and not p.startswith('tap'): cmd_output(CAP_NETWORK_INFO, [ETHTOOL, p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-k', p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-i', p]) cmd_output(CAP_NETWORK_INFO, [ETHTOOL, '-c', p]) if int(t) == 1: cmd_output(CAP_NETWORK_INFO, [TC, '-s', '-d', 'class', 'show', 'dev', p]) except: pass tree_output(CAP_NETWORK_INFO, PROC_NET_BONDING_DIR) tree_output(CAP_NETWORK_INFO, PROC_NET_VLAN_DIR) cmd_output(CAP_NETWORK_INFO, [TC, '-s', 'qdisc']) file_output(CAP_NETWORK_INFO, [PROC_NET_SOFTNET_STAT]) collect_ovsdb() if os.path.exists(OPENVSWITCH_VSWITCHD_PID): cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show', '-s']) for d in dp_list(): cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', '-m', d]) cmd_output(CAP_PROCESS_LIST, [PS, 'wwwaxf', '-eo', 'pid,tty,stat,time,nice,psr,pcpu,pmem,nwchan,wchan:25,args'], label='process-tree') func_output(CAP_PROCESS_LIST, 'fd_usage', fd_usage) system_logs = ([ VAR_LOG_DIR + x for x in ['crit.log', 'kern.log', 'daemon.log', 'user.log', 'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot']]) for log in system_logs: prefix_output(CAP_SYSTEM_LOGS, log, last_mod_time=log_last_mod_time) ovs_logs = ([ OPENVSWITCH_LOG_DIR + x for x in ['ovs-vswitchd.log', 'ovsdb-server.log', 'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log', 'ovs-ctl.log']]) for log in ovs_logs: prefix_output(CAP_OPENVSWITCH_LOGS, log, last_mod_time=log_last_mod_time) if not os.path.exists('/var/log/dmesg') and not os.path.exists('/var/log/boot'): cmd_output(CAP_SYSTEM_LOGS, [DMESG]) cmd_output(CAP_SYSTEM_SERVICES, [CHKCONFIG, '--list']) tree_output(CAP_SYSTEM_LOGS, VAR_LOG_CORE_DIR) file_output(CAP_YUM, [YUM_LOG]) tree_output(CAP_YUM, YUM_REPOS_DIR) cmd_output(CAP_YUM, [RPM, '-qa']) file_output(CAP_YUM, [APT_SOURCES_LIST]) tree_output(CAP_YUM, APT_SOURCES_LIST_D) cmd_output(CAP_YUM, [DPKG_QUERY, '-W', '-f=${Package} ${Version} ${Status}\n'], 'dpkg-packages') # Filter out ovs relevant information if --ovs option passed # else collect all information filters = set() if only_ovs_info: filters.add('ovs') ovs_info_caps = [CAP_NETWORK_STATUS, CAP_SYSTEM_LOGS, CAP_NETWORK_CONFIG] ovs_info_list = ['process-tree'] # We cannot use iteritems, since we modify 'data' as we pass through for (k, v) in data.items(): cap = v['cap'] if 'filename' in v: info = k[0] else: info = k if info not in ovs_info_list and cap not in ovs_info_caps: del data[k] if filters: filter = ",".join(filters) else: filter = None try: load_plugins(filter=filter) except: pass # permit the user to filter out data # We cannot use iteritems, since we modify 'data' as we pass through for (k, v) in sorted(data.items()): cap = v['cap'] if 'filename' in v: key = k[0] else: key = k if not ANSWER_YES_TO_ALL and not yes("Include '%s'? [Y/n]: " % key): del data[k] # collect selected data now output_ts('Running commands to collect data') collect_data() subdir = "bug-report-%s" % time.strftime("%Y%m%d%H%M%S") # include inventory data['inventory.xml'] = {'cap': None, 'output': StringIOmtime(make_inventory(data, subdir))} # create archive if output_fd == -1: if output_file is None: dirname = BUG_DIR else: dirname = os.path.dirname(output_file) if dirname and not os.path.exists(dirname): try: os.makedirs(dirname) except: pass if output_fd == -1: output_ts('Creating output file') if output_type.startswith('tar'): make_tar(subdir, output_type, output_fd, output_file) else: make_zip(subdir, output_file) if dbg: print >>sys.stderr, "Category sizes (max, actual):\n" for c in caps.keys(): print >>sys.stderr, " %s (%d, %d)" % (c, caps[c][MAX_SIZE], cap_sizes[c]) cleanup_ovsdb() return 0 def dump_scsi_hosts(cap): output = '' l = os.listdir('/sys/class/scsi_host') l.sort() for h in l: procname = '' try: f = open('/sys/class/scsi_host/%s/proc_name' % h) procname = f.readline().strip("\n") f.close() except: pass modelname = None try: f = open('/sys/class/scsi_host/%s/model_name' % h) modelname = f.readline().strip("\n") f.close() except: pass output += "%s:\n" %h output += " %s%s\n" % (procname, modelname and (" -> %s" % modelname) or '') return output def module_info(cap): output = StringIO.StringIO() modules = open(PROC_MODULES, 'r') procs = [] for line in modules: module = line.split()[0] procs.append(ProcOutput([MODINFO, module], caps[cap][MAX_TIME], output)) modules.close() run_procs([procs]) return output.getvalue() def multipathd_topology(cap): pipe = Popen([MULTIPATHD, '-k'], bufsize=1, stdin=PIPE, stdout=PIPE, stderr=dev_null) stdout, stderr = pipe.communicate('show topology') return stdout def dp_list(): output = StringIO.StringIO() procs = [ProcOutput([OVS_DPCTL, 'dump-dps'], caps[CAP_NETWORK_STATUS][MAX_TIME], output)] run_procs([procs]) if not procs[0].timed_out: return output.getvalue().splitlines() return [] def collect_ovsdb(): if not os.path.isfile(OPENVSWITCH_CONF_DB): return max_size = 10*MB try: if os.path.getsize(OPENVSWITCH_CONF_DB) > max_size: if os.path.isfile(OPENVSWITCH_COMPACT_DB): os.unlink(OPENVSWITCH_COMPACT_DB) output = StringIO.StringIO() max_time = 5 procs = [ProcOutput(['ovsdb-tool', 'compact', OPENVSWITCH_CONF_DB, OPENVSWITCH_COMPACT_DB], max_time, output)] run_procs([procs]) file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_COMPACT_DB]) else: file_output(CAP_NETWORK_STATUS, [OPENVSWITCH_CONF_DB]) except OSError, e: return def cleanup_ovsdb(): try: if os.path.isfile(OPENVSWITCH_COMPACT_DB): os.unlink(OPENVSWITCH_COMPACT_DB) except: return def fd_usage(cap): output = '' fd_dict = {} for d in [p for p in os.listdir('/proc') if p.isdigit()]: try: fh = open('/proc/'+d+'/cmdline') name = fh.readline() num_fds = len(os.listdir(os.path.join('/proc/'+d+'/fd'))) if num_fds > 0: if not num_fds in fd_dict: fd_dict[num_fds] = [] fd_dict[num_fds].append(name.replace('\0', ' ').strip()) finally: fh.close() keys = fd_dict.keys() keys.sort(lambda a, b: int(b) - int(a)) for k in keys: output += "%s: %s\n" % (k, str(fd_dict[k])) return output def dump_rdac_groups(cap): output = StringIO.StringIO() procs = [ProcOutput([MPPUTIL, '-a'], caps[cap][MAX_TIME], output)] run_procs([procs]) if not procs[0].timed_out: proc_line = 0 for line in output.getvalue().splitlines(): if line.startswith('ID'): proc_line = 2 elif line.startswith('----'): proc_line -= 1 elif proc_line > 0: group, _ = line.split(None, 1) cmd_output(cap, [MPPUTIL, '-g', group]) def load_plugins(just_capabilities=False, filter=None): global log_last_mod_time def getText(nodelist): rc = "" for node in nodelist: if node.nodeType == node.TEXT_NODE: rc += node.data return rc.encode() def getBoolAttr(el, attr, default=False): ret = default val = el.getAttribute(attr).lower() if val in ['true', 'false', 'yes', 'no']: ret = val in ['true', 'yes'] return ret for dir in [d for d in os.listdir(PLUGIN_DIR) if os.path.isdir(os.path.join(PLUGIN_DIR, d))]: if not caps.has_key(dir): if not os.path.exists("%s/%s.xml" % (PLUGIN_DIR, dir)): continue xmldoc = parse("%s/%s.xml" % (PLUGIN_DIR, dir)) assert xmldoc.documentElement.tagName == "capability" pii, min_size, max_size, min_time, max_time, mime = \ PII_MAYBE, -1,-1,-1,-1, MIME_TEXT if xmldoc.documentElement.getAttribute("pii") in [PII_NO, PII_YES, PII_MAYBE, PII_IF_CUSTOMIZED]: pii = xmldoc.documentElement.getAttribute("pii") if xmldoc.documentElement.getAttribute("min_size") != '': min_size = long(xmldoc.documentElement.getAttribute("min_size")) if xmldoc.documentElement.getAttribute("max_size") != '': max_size = long(xmldoc.documentElement.getAttribute("max_size")) if xmldoc.documentElement.getAttribute("min_time") != '': min_time = int(xmldoc.documentElement.getAttribute("min_time")) if xmldoc.documentElement.getAttribute("max_time") != '': max_time = int(xmldoc.documentElement.getAttribute("max_time")) if xmldoc.documentElement.getAttribute("mime") in [MIME_DATA, MIME_TEXT]: mime = xmldoc.documentElement.getAttribute("mime") checked = getBoolAttr(xmldoc.documentElement, 'checked', True) hidden = getBoolAttr(xmldoc.documentElement, 'hidden', False) cap(dir, pii, min_size, max_size, min_time, max_time, mime, checked, hidden) if just_capabilities: continue plugdir = os.path.join(PLUGIN_DIR, dir) for file in [f for f in os.listdir(plugdir) if f.endswith('.xml')]: xmldoc = parse(os.path.join(plugdir, file)) assert xmldoc.documentElement.tagName == "collect" for el in xmldoc.documentElement.getElementsByTagName("*"): filters_tmp = el.getAttribute("filters") if filters_tmp == '': filters = [] else: filters = filters_tmp.split(',') if not(filter is None or filter in filters): continue if el.tagName == "files": newest_first = getBoolAttr(el, 'newest_first') if el.getAttribute("type") == "logs": for fn in getText(el.childNodes).split(): prefix_output(dir, fn, newest_first=newest_first, last_mod_time=log_last_mod_time) else: file_output(dir, getText(el.childNodes).split(), newest_first=newest_first) elif el.tagName == "directory": pattern = el.getAttribute("pattern") if pattern == '': pattern = None negate = getBoolAttr(el, 'negate') newest_first = getBoolAttr(el, 'newest_first') if el.getAttribute("type") == "logs": tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate=negate, newest_first=newest_first, last_mod_time=log_last_mod_time) else: tree_output(dir, getText(el.childNodes), pattern and re.compile(pattern) or None, negate=negate, newest_first=newest_first) elif el.tagName == "command": label = el.getAttribute("label") if label == '': label = None binary = getBoolAttr(el, 'binary') cmd_output(dir, getText(el.childNodes), label, binary=binary) def make_tar(subdir, suffix, output_fd, output_file): global SILENT_MODE, data mode = 'w' if suffix == 'tar.bz2': mode = 'w:bz2' elif suffix == 'tar.gz': mode = 'w:gz' if output_fd == -1: if output_file is None: filename = "%s/%s.%s" % (BUG_DIR, subdir, suffix) else: filename = output_file old_umask = os.umask(0077) tf = tarfile.open(filename, mode) os.umask(old_umask) else: tf = tarfile.open(None, 'w', os.fdopen(output_fd, 'a')) try: for (k, v) in data.items(): try: tar_filename = os.path.join(subdir, construct_filename(k, v)) ti = tarfile.TarInfo(tar_filename) ti.uname = 'root' ti.gname = 'root' if v.has_key('output'): ti.mtime = v['output'].mtime ti.size = len(v['output'].getvalue()) v['output'].seek(0) tf.addfile(ti, v['output']) elif v.has_key('filename'): s = os.stat(v['filename']) ti.mtime = s.st_mtime ti.size = s.st_size tf.addfile(ti, file(v['filename'])) except: pass finally: tf.close() if output_fd == -1: output ('Writing tarball %s successful.' % filename) if SILENT_MODE: print filename def make_zip(subdir, output_file): global SILENT_MODE, data if output_file is None: filename = "%s/%s.zip" % (BUG_DIR, subdir) else: filename = output_file old_umask = os.umask(0077) zf = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) os.umask(old_umask) try: for (k, v) in data.items(): try: dest = os.path.join(subdir, construct_filename(k, v)) if v.has_key('output'): zf.writestr(dest, v['output'].getvalue()) else: if os.stat(v['filename']).st_size < 50: compress_type = zipfile.ZIP_STORED else: compress_type = zipfile.ZIP_DEFLATED zf.write(v['filename'], dest, compress_type) except: pass finally: zf.close() output ('Writing archive %s successful.' % filename) if SILENT_MODE: print filename def make_inventory(inventory, subdir): document = getDOMImplementation().createDocument( None, INVENTORY_XML_ROOT, None) # create summary entry s = document.createElement(INVENTORY_XML_SUMMARY) user = os.getenv('SUDO_USER', os.getenv('USER')) if user: s.setAttribute('user', user) s.setAttribute('date', time.strftime('%c')) s.setAttribute('hostname', platform.node()) s.setAttribute('uname', ' '.join(platform.uname())) s.setAttribute('uptime', commands.getoutput(UPTIME)) document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(s) map(lambda (k, v): inventory_entry(document, subdir, k, v), inventory.items()) return document.toprettyxml() def inventory_entry(document, subdir, k, v): try: el = document.createElement(INVENTORY_XML_ELEMENT) el.setAttribute('capability', v['cap']) el.setAttribute('filename', os.path.join(subdir, construct_filename(k, v))) el.setAttribute('md5sum', md5sum(v)) document.getElementsByTagName(INVENTORY_XML_ROOT)[0].appendChild(el) except: pass def md5sum(d): m = md5.new() if d.has_key('filename'): f = open(d['filename']) data = f.read(1024) while len(data) > 0: m.update(data) data = f.read(1024) f.close() elif d.has_key('output'): m.update(d['output'].getvalue()) return m.hexdigest() def construct_filename(k, v): if v.has_key('filename'): if v['filename'][0] == '/': return v['filename'][1:] else: return v['filename'] s = k.replace(' ', '-') s = s.replace('--', '-') s = s.replace('/', '%') if s.find('.') == -1: s += '.out' return s def update_capabilities(): pass def update_cap_size(cap, size): update_cap(cap, MIN_SIZE, size) update_cap(cap, MAX_SIZE, size) update_cap(cap, CHECKED, size > 0) def update_cap(cap, k, v): global caps l = list(caps[cap]) l[k] = v caps[cap] = tuple(l) def size_of_dir(d, pattern=None, negate=False): if os.path.isdir(d): return size_of_all([os.path.join(d, fn) for fn in os.listdir(d)], pattern, negate) else: return 0 def size_of_all(files, pattern=None, negate=False): return sum([size_of(f, pattern, negate) for f in files]) def matches(f, pattern, negate): if negate: return not matches(f, pattern, False) else: return pattern is None or pattern.match(f) def size_of(f, pattern, negate): if os.path.isfile(f) and matches(f, pattern, negate): return os.stat(f)[6] else: return size_of_dir(f, pattern, negate) def print_capabilities(): document = getDOMImplementation().createDocument( "ns", CAP_XML_ROOT, None) map(lambda key: capability(document, key), [k for k in caps.keys() if not caps[k][HIDDEN]]) print document.toprettyxml() def capability(document, key): c = caps[key] el = document.createElement(CAP_XML_ELEMENT) el.setAttribute('key', c[KEY]) el.setAttribute('pii', c[PII]) el.setAttribute('min-size', str(c[MIN_SIZE])) el.setAttribute('max-size', str(c[MAX_SIZE])) el.setAttribute('min-time', str(c[MIN_TIME])) el.setAttribute('max-time', str(c[MAX_TIME])) el.setAttribute('content-type', c[MIME]) el.setAttribute('default-checked', c[CHECKED] and 'yes' or 'no') document.getElementsByTagName(CAP_XML_ROOT)[0].appendChild(el) def prettyDict(d): format = '%%-%ds: %%s' % max(map(len, [k for k, _ in d.items()])) return '\n'.join([format % i for i in d.items()]) + '\n' def yes(prompt): yn = raw_input(prompt) return len(yn) == 0 or yn.lower()[0] == 'y' partition_re = re.compile(r'(.*[0-9]+$)|(^xvd)') def disk_list(): disks = [] try: f = open('/proc/partitions') f.readline() f.readline() for line in f.readlines(): (major, minor, blocks, name) = line.split() if int(major) < 254 and not partition_re.match(name): disks.append(name) f.close() except: pass return disks class ProcOutput: debug = False def __init__(self, command, max_time, inst=None, filter=None, binary=False): self.command = command self.max_time = max_time self.inst = inst self.running = False self.status = None self.timed_out = False self.failed = False self.timeout = int(time.time()) + self.max_time self.filter = filter self.filter_state = {} if binary: self.bufsize = 1048576 # 1MB buffer else: self.bufsize = 1 # line buffered def __del__(self): self.terminate() def cmdAsStr(self): return isinstance(self.command, list) and ' '.join(self.command) or self.command def run(self): self.timed_out = False try: if ProcOutput.debug: output_ts("Starting '%s'" % self.cmdAsStr()) self.proc = Popen(self.command, bufsize=self.bufsize, stdin=dev_null, stdout=PIPE, stderr=dev_null, shell=isinstance(self.command, str)) old = fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_GETFD) fcntl.fcntl(self.proc.stdout.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) self.running = True self.failed = False except: output_ts("'%s' failed" % self.cmdAsStr()) self.running = False self.failed = True def terminate(self): if self.running: try: self.proc.stdout.close() os.kill(self.proc.pid, SIGTERM) except: pass self.proc = None self.running = False self.status = SIGTERM def read_line(self): assert self.running if self.bufsize == 1: line = self.proc.stdout.readline() else: line = self.proc.stdout.read(self.bufsize) if line == '': # process exited self.proc.stdout.close() self.status = self.proc.wait() self.proc = None self.running = False else: if self.filter: line = self.filter(line, self.filter_state) if self.inst: self.inst.write(line) def run_procs(procs): while True: pipes = [] active_procs = [] for pp in procs: for p in pp: if p.running: active_procs.append(p) pipes.append(p.proc.stdout) break elif p.status == None and not p.failed and not p.timed_out: p.run() if p.running: active_procs.append(p) pipes.append(p.proc.stdout) break if len(pipes) == 0: # all finished break (i, o, x) = select(pipes, [], [], 1.0) now = int(time.time()) # handle process output for p in active_procs: if p.proc.stdout in i: p.read_line() # handle timeout if p.running and now > p.timeout: output_ts("'%s' timed out" % p.cmdAsStr()) if p.inst: p.inst.write("\n** timeout **\n") p.timed_out = True p.terminate() def pidof(name): pids = [] for d in [p for p in os.listdir('/proc') if p.isdigit()]: try: if os.path.basename(os.readlink('/proc/%s/exe' % d)) == name: pids.append(int(d)) except: pass return pids def check_space(cap, name, size): global free_disk_space if free_disk_space is not None and size > free_disk_space: output("Omitting %s, out of disk space (requested: %u, allowed: %u)" % (name, size, free_disk_space)) return False elif unlimited_data or caps[cap][MAX_SIZE] == -1 or \ cap_sizes[cap] < caps[cap][MAX_SIZE]: cap_sizes[cap] += size if free_disk_space is not None: free_disk_space -= size return True else: output("Omitting %s, size constraint of %s exceeded" % (name, cap)) return False def get_free_disk_space(path): path = os.path.abspath(path) while not os.path.exists(path): path = os.path.dirname(path) s = os.statvfs(path) return s.f_frsize * s.f_bfree class StringIOmtime(StringIO.StringIO): def __init__(self, buf=''): StringIO.StringIO.__init__(self, buf) self.mtime = time.time() def write(self, s): StringIO.StringIO.write(self, s) self.mtime = time.time() if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: print "\nInterrupted." sys.exit(3) openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-list-dbs0000644000000000000000000000013213534540071023254 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.845846526 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-list-dbs0000755000175000017500000000137013534540071024746 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. ovs-appctl -t ovsdb-server ovsdb-server/list-dbs openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071021471 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.625852276 openvswitch-2.5.9/utilities/bugtool/automake.mk0000644000175000017500000000442413534540071023163 0ustar00jpettitjpettit00000000000000if HAVE_PYTHON sbin_SCRIPTS += utilities/bugtool/ovs-bugtool CLEANFILES += utilities/bugtool/ovs-bugtool man_MANS += utilities/bugtool/ovs-bugtool.8 MAN_ROOTS += utilities/bugtool/ovs-bugtool.8.in DISTCLEANFILES += utilities/bugtool/ovs-bugtool.8 bugtool_plugins = \ utilities/bugtool/plugins/kernel-info/openvswitch.xml \ utilities/bugtool/plugins/network-status/openvswitch.xml \ utilities/bugtool/plugins/system-configuration.xml \ utilities/bugtool/plugins/system-logs/openvswitch.xml \ utilities/bugtool/plugins/system-configuration/openvswitch.xml bugtool_scripts = \ utilities/bugtool/ovs-bugtool-bfd-show \ utilities/bugtool/ovs-bugtool-cfm-show \ utilities/bugtool/ovs-bugtool-coverage-show \ utilities/bugtool/ovs-bugtool-fdb-show \ utilities/bugtool/ovs-bugtool-lacp-show \ utilities/bugtool/ovs-bugtool-list-dbs \ utilities/bugtool/ovs-bugtool-memory-show \ utilities/bugtool/ovs-bugtool-tc-class-show \ utilities/bugtool/ovs-bugtool-vsctl-show \ utilities/bugtool/ovs-bugtool-ovsdb-dump \ utilities/bugtool/ovs-bugtool-daemons-ver \ utilities/bugtool/ovs-bugtool-ovs-ofctl-show \ utilities/bugtool/ovs-bugtool-ovs-ofctl-dump-flows \ utilities/bugtool/ovs-bugtool-ovs-appctl-dpif \ utilities/bugtool/ovs-bugtool-bond-show \ utilities/bugtool/ovs-bugtool-conntrack-dump scripts_SCRIPTS += $(bugtool_scripts) bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins INSTALL_DATA_LOCAL += bugtool-install-data-local bugtool-install-data-local: for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ $(MKDIR_P) "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ $(INSTALL_DATA) "$(srcdir)/$$plugin" "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ done UNINSTALL_LOCAL += bugtool-uninstall-local bugtool-uninstall-local: for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ rm -f "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \ done for plugin in $(bugtool_plugins); do \ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \ dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \ rmdir "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \ done; exit 0 endif EXTRA_DIST += \ $(bugtool_plugins) \ $(bugtool_scripts) \ utilities/bugtool/ovs-bugtool.in openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-ovs-ofctl-dump-flows0000644000000000000000000000013213534540071025542 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.857846614 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-ovs-ofctl-dump-flows0000755000175000017500000000151213534540071027232 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. for bridge in `ovs-vsctl list-br` do echo "ovs-ofctl dump-flows ${bridge}" ovs-ofctl dump-flows "$bridge" echo "" done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-vsctl-show0000644000000000000000000000013213534540071023644 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.853846585 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-vsctl-show0000755000175000017500000000133413534540071025336 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012, 2013 Nicira, Inc. ovs-vsctl show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-tc-class-show0000644000000000000000000000013213534540071024222 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.849846555 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-tc-class-show0000755000175000017500000000170013534540071025711 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2011 Nicira, Inc. for iface in $(cd /sys/class/net && echo *); do if [ -d /sys/class/net/$iface ]; then echo Interface $iface: # indent tc output so it's clear which interface it pertains to /sbin/tc -s -d class show dev $iface | /bin/sed 's/^/ /' fi done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-coverage-show0000644000000000000000000000013213534540071024304 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.837846466 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-coverage-show0000755000175000017500000000134013534540071025773 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012 Nicira, Inc. ovs-appctl coverage/show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-memory-show0000644000000000000000000000013213534540071024021 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.845846526 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-memory-show0000755000175000017500000000133613534540071025515 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012 Nicira, Inc. ovs-appctl memory/show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-cfm-show0000644000000000000000000000013213534540071023256 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.837846466 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-cfm-show0000755000175000017500000000133313534540071024747 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2011 Nicira, Inc. ovs-appctl cfm/show openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/plugins0000644000000000000000000000013213534540117020737 xustar0030 mtime=1567801423.829846408 30 atime=1567801425.625859648 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/0000755000175000017500000000000013534540117022502 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/plugins/PaxHeaders.82075/kernel-info0000644000000000000000000000013213534540117023150 xustar0030 mtime=1567801423.829846408 30 atime=1567801425.625859648 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/kernel-info/0000755000175000017500000000000013534540117024713 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/plugins/kernel-info/PaxHeaders.82075/openvswitch.xml0000644000000000000000000000013213534540071026317 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/kernel-info/openvswitch.xml0000644000175000017500000000137013534540071030006 0ustar00jpettitjpettit00000000000000 /proc/slabinfo openvswitch-2.5.9/utilities/bugtool/plugins/PaxHeaders.82075/system-configuration0000644000000000000000000000013213534540117025130 xustar0030 mtime=1567801423.833846438 30 atime=1567801425.625859648 30 ctime=1567801423.833846438 openvswitch-2.5.9/utilities/bugtool/plugins/system-configuration/0000755000175000017500000000000013534540117026673 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/plugins/system-configuration/PaxHeaders.82075/openvswitch.xml0000644000000000000000000000013213534540071030277 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.833846438 openvswitch-2.5.9/utilities/bugtool/plugins/system-configuration/openvswitch.xml0000644000175000017500000000160413534540071031766 0ustar00jpettitjpettit00000000000000 date --rfc-3339=seconds /usr/share/openvswitch/scripts/ovs-bugtool-daemons-ver openvswitch-2.5.9/utilities/bugtool/plugins/PaxHeaders.82075/system-logs0000644000000000000000000000013213534540117023225 xustar0030 mtime=1567801423.829846408 30 atime=1567801425.625859648 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/system-logs/0000755000175000017500000000000013534540117024770 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/plugins/system-logs/PaxHeaders.82075/openvswitch.xml0000644000000000000000000000013213534540071026374 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/system-logs/openvswitch.xml0000644000175000017500000000170513534540071030065 0ustar00jpettitjpettit00000000000000 /etc/openvswitch /var/lib/openvswitch openvswitch-2.5.9/utilities/bugtool/plugins/PaxHeaders.82075/network-status0000644000000000000000000000013213534540117023751 xustar0030 mtime=1567801423.829846408 30 atime=1567801425.625859648 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/network-status/0000755000175000017500000000000013534540117025514 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/utilities/bugtool/plugins/network-status/PaxHeaders.82075/openvswitch.xml0000644000000000000000000000013213534540071027120 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/network-status/openvswitch.xml0000644000175000017500000000467113534540071030616 0ustar00jpettitjpettit00000000000000 /usr/share/openvswitch/scripts/ovs-bugtool-tc-class-show /usr/share/openvswitch/scripts/ovs-bugtool-vsctl-show /usr/share/openvswitch/scripts/ovs-bugtool-ovsdb-dump /usr/share/openvswitch/scripts/ovs-bugtool-fdb-show /usr/share/openvswitch/scripts/ovs-bugtool-lacp-show /usr/share/openvswitch/scripts/ovs-bugtool-cfm-show /usr/share/openvswitch/scripts/ovs-bugtool-bfd-show /usr/share/openvswitch/scripts/ovs-bugtool-conntrack-dump /usr/share/openvswitch/scripts/ovs-bugtool-coverage-show /usr/share/openvswitch/scripts/ovs-bugtool-bond-show /usr/share/openvswitch/scripts/ovs-bugtool-memory-show /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-show /usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-dump-flows /usr/share/openvswitch/scripts/ovs-bugtool-ovs-appctl-dpif /usr/share/openvswitch/scripts/ovs-bugtool-list-dbs openvswitch-2.5.9/utilities/bugtool/plugins/PaxHeaders.82075/system-configuration.xml0000644000000000000000000000013213534540071025726 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.829846408 openvswitch-2.5.9/utilities/bugtool/plugins/system-configuration.xml0000644000175000017500000000166013534540071027417 0ustar00jpettitjpettit00000000000000 openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-daemons-ver0000644000000000000000000000013213534540071023753 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.857846614 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-daemons-ver0000755000175000017500000000155013534540071025445 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2012 Nicira, Inc. for f in `cd /var/run/openvswitch/; ls *.pid 2>/dev/null` do if [ -n "${f%.pid}" ]; then ovs-appctl -t "${f%.pid}" version 2>&1 fi echo done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-ovs-appctl-dpif0000644000000000000000000000013213534540071024543 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.861846644 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-ovs-appctl-dpif0000755000175000017500000000162713534540071026242 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. echo "ovs-appctl dpif/show" ovs-appctl dpif/show for bridge in `ovs-vsctl -- --real list-br` do echo "ovs-appctl dpif/dump-flows -m ${bridge}" ovs-appctl dpif/dump-flows -m "$bridge" echo "" done openvswitch-2.5.9/utilities/bugtool/PaxHeaders.82075/ovs-bugtool-bfd-show0000644000000000000000000000013213534540071023244 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.833846438 openvswitch-2.5.9/utilities/bugtool/ovs-bugtool-bfd-show0000755000175000017500000000133313534540071024735 0ustar00jpettitjpettit00000000000000#! /bin/sh # This library is free software; you can redistribute it and/or # modify it under the terms of version 2.1 of the GNU Lesser General # Public License as published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # # Copyright (C) 2013 Nicira, Inc. ovs-appctl bfd/show openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vlan-test.8.in0000644000000000000000000000013213534540071021077 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.773845995 openvswitch-2.5.9/utilities/ovs-vlan-test.8.in0000644000175000017500000000635413534540071022575 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-vlan\-test 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME \fBovs\-vlan\-test\fR \- check Linux drivers for problems with vlan traffic . .SH SYNOPSIS \fBovs\-vlan\-test\fR [\fB\-s\fR | \fB\-\-server\fR] \fIcontrol_ip\fR \fIvlan_ip\fR .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-vlan\-test\fR utility has some limitations, for example, it does not use TCP in its tests. Also it does not take into account MTU to detect potential edge cases. To overcome those limitations a new tool was developed \- \fBovs\-test\fR. \fBovs\-test\fR is currently supported only on Debian so, if possible try to use that on instead of \fBovs\-vlan\-test\fR. .PP The \fBovs\-vlan\-test\fR program may be used to check for problems sending 802.1Q traffic which may occur when running Open vSwitch. These problems can occur when Open vSwitch is used to send 802.1Q traffic through physical interfaces running certain drivers of certain Linux kernel versions. To run a test, configure Open vSwitch to tag traffic originating from \fIvlan_ip\fR and forward it out the target interface. Then run the \fBovs\-vlan\-test\fR in client mode connecting to an \fBovs\-vlan\-test\fR server. \fBovs\-vlan\-test\fR will display "OK" if it did not detect problems. .PP Some examples of the types of problems that may be encountered are: .so utilities/ovs-vlan-bugs.man . .SS "Client Mode" An \fBovs\-vlan\-test\fR client may be run on a host to check for VLAN connectivity problems. The client must be able to establish HTTP connections with an \fBovs\-vlan\-test\fR server located at the specified \fIcontrol_ip\fR address. UDP traffic sourced at \fIvlan_ip\fR should be tagged and directed out the interface whose connectivity is being tested. . .SS "Server Mode" To conduct tests, an \fBovs\-vlan\-test\fR server must be running on a host known not to have VLAN connectivity problems. The server must have a \fIcontrol_ip\fR on a non\-VLAN network which clients can establish connectivity with. It must also have a \fIvlan_ip\fR address on a VLAN network which clients will use to test their VLAN connectivity. Multiple clients may test against a single \fBovs\-vlan\-test\fR server concurrently. . .SH OPTIONS . .IP "\fB\-s\fR" .IQ "\fB\-\-server\fR" Run in server mode. . .so lib/common.man .SH EXAMPLES Display the Linux kernel version and driver of \fBeth1\fR. .IP .B uname \-r .IP .B ethtool \-i eth1 . .PP Set up a bridge which forwards traffic originating from \fB1.2.3.4\fR out \fBeth1\fR with VLAN tag 10. .IP .B ovs\-vsctl \-\- add\-br vlan\-br \(rs .IP .B \-\- add\-port vlan\-br eth1 \(rs .IP .B \-\- add\-port vlan\-br vlan\-br\-tag tag=10 \(rs .IP .B \-\- set Interface vlan\-br\-tag type=internal .IP .B ifconfig vlan\-br\-tag up 1.2.3.4 . .PP Run an \fBovs\-vlan\-test\fR server listening for client control traffic on 172.16.0.142 port 8080 and VLAN traffic on the default port of 1.2.3.3. .IP .B ovs\-vlan\-test \-s 172.16.0.142:8080 1.2.3.3 . .PP Run an \fBovs\-vlan\-test\fR client with a control server located at 172.16.0.142 port 8080 and a local VLAN ip of 1.2.3.4. .IP .B ovs\-vlan\-test 172.16.0.142:8080 1.2.3.4 . .SH SEE ALSO . .BR ovs\-vswitchd (8), .BR ovs\-ofctl (8), .BR ovs\-vsctl (8), .BR ovs\-test (8), .BR ethtool (8), .BR uname (1) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vlan-bugs.man0000644000000000000000000000013213534540071021057 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.753845848 openvswitch-2.5.9/utilities/ovs-vlan-bugs.man0000644000175000017500000000173413534540071022552 0ustar00jpettitjpettit00000000000000.IP \(bu When NICs use VLAN stripping on receive they must pass a pointer to a \fBvlan_group\fR when reporting the stripped tag to the networking core. If no \fBvlan_group\fR is in use then some drivers just drop the extracted tag. Drivers are supposed to only enable stripping if a \fBvlan_group\fR is registered but not all of them do that. . .IP \(bu On receive, some drivers handle priority tagged packets specially and don't pass the tag onto the network stack at all, so Open vSwitch never has a chance to see it. . .IP \(bu Some drivers size their receive buffers based on whether a \fBvlan_group\fR is enabled, meaning that a maximum size packet with a VLAN tag will not fit if no \fBvlan_group\fR is configured. . .IP \(bu On transmit, some drivers expect that VLAN acceleration will be used if it is available, which can only be done if a \fBvlan_group\fR is configured. In these cases, the driver may fail to parse the packet and correctly setup checksum offloading or TSO. openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-ctl.in0000644000000000000000000000013113534540071017575 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 29 ctime=1567801423.80984626 openvswitch-2.5.9/utilities/ovs-ctl.in0000755000175000017500000005202413534540071021272 0ustar00jpettitjpettit00000000000000#! /bin/sh # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. case $0 in */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;; *) dir0=./ ;; esac . "$dir0/ovs-lib" || exit 1 for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do case :$PATH: in *:$dir:*) ;; *) PATH=$PATH:$dir ;; esac done ## ----- ## ## start ## ## ----- ## # Keep track of removed vports so we can reload them if needed removed_vports="" insert_mods () { # Try loading openvswitch again. action "Inserting openvswitch module" modprobe openvswitch for vport in $removed_vports; do # Don't treat failures to load vports as fatal error action "Inserting $vport module" modprobe $vport || true done } insert_mod_if_required () { # If this kernel has no module support, expect we're done. if test ! -e /proc/modules then log_success_msg "Kernel has no loadable module support. Skipping modprobe" return 0 fi # If openvswitch is already loaded then we're done. test -e /sys/module/openvswitch -o -e /sys/module/openvswitch_mod && \ return 0 # Load openvswitch. If that's successful then we're done. insert_mods && return 0 # If the bridge module is loaded, then that might be blocking # openvswitch. Try to unload it, if there are no bridges. test -e /sys/module/bridge || return 1 bridges=`echo /sys/class/net/*/bridge | sed 's,/sys/class/net/,,g;s,/bridge,,g'` if test "$bridges" != "*"; then log_warning_msg "not removing bridge module because bridges exist ($bridges)" return 1 fi action "removing bridge module" rmmod bridge || return 1 # Try loading openvswitch again. insert_mods } ovs_vsctl () { ovs-vsctl --no-wait "$@" } set_system_ids () { set ovs_vsctl set Open_vSwitch . OVS_VERSION=`ovs-vswitchd --version | sed 's/.*) //;1q'` set "$@" ovs-version="$OVS_VERSION" case $SYSTEM_ID in random) id_file=$etcdir/system-id.conf uuid_file=$etcdir/install_uuid.conf if test -e "$id_file"; then SYSTEM_ID=`cat "$id_file"` elif test -e "$uuid_file"; then # Migrate from old file name. . "$uuid_file" SYSTEM_ID=$INSTALLATION_UUID echo "$SYSTEM_ID" > "$id_file" elif SYSTEM_ID=`uuidgen`; then echo "$SYSTEM_ID" > "$id_file" else log_failure_msg "missing uuidgen, could not generate system ID" fi ;; '') log_failure_msg "system ID not configured, please use --system-id" ;; *) ;; esac set "$@" external-ids:system-id="\"$SYSTEM_ID\"" if test X"$SYSTEM_TYPE" != X; then set "$@" system-type="\"$SYSTEM_TYPE\"" else log_failure_msg "no default system type, please use --system-type" fi if test X"$SYSTEM_VERSION" != X; then set "$@" system-version="\"$SYSTEM_VERSION\"" else log_failure_msg "no default system version, please use --system-version" fi action "Configuring Open vSwitch system IDs" "$@" $extra_ids } check_force_cores () { if test X"$FORCE_COREFILES" = Xyes; then ulimit -c 67108864 fi } del_transient_ports () { for port in `ovs-vsctl --bare -- --columns=name find port other_config:transient=true`; do ovs_vsctl -- del-port "$port" done } do_start_ovsdb () { check_force_cores if daemon_is_running ovsdb-server; then log_success_msg "ovsdb-server is already running" else # Create initial database or upgrade database schema. upgrade_db $DB_FILE $DB_SCHEMA || return 1 # Start ovsdb-server. set ovsdb-server "$DB_FILE" for db in $EXTRA_DBS; do case $db in /*) ;; *) db=$dbdir/$db ;; esac if test ! -f "$db"; then log_warning_msg "$db (from \$EXTRA_DBS) does not exist." elif ovsdb-tool db-version "$db" >/dev/null; then set "$@" "$db" else log_warning_msg "$db (from \$EXTRA_DBS) cannot be read as a database (see error message above)" fi done set "$@" -vconsole:emer -vsyslog:err -vfile:info set "$@" --remote=punix:"$DB_SOCK" set "$@" --private-key=db:Open_vSwitch,SSL,private_key set "$@" --certificate=db:Open_vSwitch,SSL,certificate set "$@" --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert start_daemon "$OVSDB_SERVER_PRIORITY" "$OVSDB_SERVER_WRAPPER" "$@" \ || return 1 # Initialize database settings. ovs_vsctl -- init -- set Open_vSwitch . db-version="$schemaver" \ || return 1 set_system_ids || return 1 if test X"$DELETE_BRIDGES" = Xyes; then for bridge in `ovs_vsctl list-br`; do ovs_vsctl del-br $bridge done fi if test X"$DELETE_TRANSIENT_PORTS" = Xyes; then del_transient_ports fi fi } start_ovsdb() { if test X"$OVSDB_SERVER" = Xyes; then do_start_ovsdb fi } add_managers () { # Now that ovs-vswitchd has started and completed its initial # configuration, tell ovsdb-server to conenct to the remote managers. We # used to do this at ovsdb-server startup time, but waiting for # ovs-vswitchd to finish configuring means that remote managers see less # churn in the database at startup or restart. (For example, managers # won't briefly see empty datapath-id or ofport columns for records that # exist at startup.) if test X"$OVSDB_SERVER" = Xyes || test X"$OVS_VSWITCHD" = Xyes; then if daemon_is_running ovsdb-server \ && daemon_is_running ovs-vswitchd; then action "Enabling remote OVSDB managers" \ ovs-appctl -t ovsdb-server ovsdb-server/add-remote \ db:Open_vSwitch,Open_vSwitch,manager_options fi fi } do_start_forwarding () { check_force_cores insert_mod_if_required || return 1 if daemon_is_running ovs-vswitchd; then log_success_msg "ovs-vswitchd is already running" else # Increase the limit on the number of open file descriptors. # On Linux, ovs-vswitchd needs about three file descriptors # per bridge and "n-handler-threads" file descriptors per bridge # port, so this allows a very large number of bridges and ports. MAXFD=65535 if [ $(ulimit -n) -lt $MAXFD ]; then ulimit -n $MAXFD fi # Start ovs-vswitchd. set ovs-vswitchd unix:"$DB_SOCK" set "$@" -vconsole:emer -vsyslog:err -vfile:info if test X"$MLOCKALL" != Xno; then set "$@" --mlockall fi start_daemon "$OVS_VSWITCHD_PRIORITY" "$OVS_VSWITCHD_WRAPPER" "$@" fi } start_forwarding () { if test X"$OVS_VSWITCHD" = Xyes; then do_start_forwarding fi } ## ---- ## ## stop ## ## ---- ## stop_ovsdb () { if test X"$OVSDB_SERVER" = Xyes; then stop_daemon ovsdb-server fi } stop_forwarding () { if test X"$OVS_VSWITCHD" = Xyes; then stop_daemon ovs-vswitchd fi } ## ----------------- ## ## force-reload-kmod ## ## ----------------- ## internal_interfaces () { # Outputs a list of internal interfaces: # # - There is an internal interface for every bridge, whether it # has an Interface record or not and whether the Interface # record's 'type' is properly set or not. # # - There is an internal interface for each Interface record whose # 'type' is 'internal'. # # But ignore interfaces that don't really exist. for d in `(ovs_vsctl --bare \ -- --columns=name find Interface type=internal \ -- list-br) | sort -u` do if test -e "/sys/class/net/$d"; then printf "%s " "$d" fi done } ovs_save () { bridges=`ovs_vsctl -- --real list-br` if [ -n "${bridges}" ] && \ "$datadir/scripts/ovs-save" "$1" ${bridges} > "$2"; then chmod +x "$2" return 0 fi [ -z "${bridges}" ] && return 0 } save_flows_if_required () { if test X"$DELETE_BRIDGES" != Xyes; then action "Saving flows" ovs_save save-flows "${script_flows}" fi } save_interfaces () { "$datadir/scripts/ovs-save" save-interfaces ${ifaces} \ > "${script_interfaces}" } flow_restore_wait () { if test X"$OVS_VSWITCHD" = Xyes; then ovs_vsctl set open_vswitch . other_config:flow-restore-wait="true" fi } flow_restore_complete () { if test X"$OVS_VSWITCHD" = Xyes; then ovs_vsctl --if-exists remove open_vswitch . other_config \ flow-restore-wait="true" fi } restore_flows () { [ -x "${script_flows}" ] && \ action "Restoring saved flows" "${script_flows}" } restore_interfaces () { [ ! -x "${script_interfaces}" ] && return 0 action "Restoring interface configuration" "${script_interfaces}" rc=$? if test $rc = 0; then level=debug else level=err fi log="logger -p daemon.$level -t ovs-save" $log "interface restore script exited with status $rc:" $log -f "$script_interfaces" } init_restore_scripts () { script_interfaces=`mktemp` script_flows=`mktemp` trap 'rm -f "${script_interfaces}" "${script_flows}"' 0 } force_reload_kmod () { if test X"$OVS_VSWITCHD" != Xyes; then log_failure_msg "Reloading of kmod without ovs-vswitchd is an error" exit 1 fi ifaces=`internal_interfaces` action "Detected internal interfaces: $ifaces" true init_restore_scripts save_flows_if_required # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb stop_forwarding if action "Saving interface configuration" save_interfaces; then : else log_warning_msg "Failed to save configuration, not replacing kernel module" start_forwarding add_managers exit 1 fi chmod +x "$script_interfaces" for dp in `ovs-dpctl dump-dps`; do action "Removing datapath: $dp" ovs-dpctl del-dp "$dp" done for vport in `awk '/^vport_/ { print $1 }' /proc/modules`; do action "Removing $vport module" rmmod $vport if ! grep -q $vport /proc/modules; then removed_vports="$removed_vports $vport" fi done # try both old and new names in case this is post upgrade if test -e /sys/module/openvswitch_mod; then action "Removing openvswitch module" rmmod openvswitch_mod elif test -e /sys/module/openvswitch; then action "Removing openvswitch module" rmmod openvswitch fi # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers restore_interfaces "$datadir/scripts/ovs-check-dead-ifs" } ## ------- ## ## restart ## ## ------- ## restart () { if daemon_is_running ovsdb-server && daemon_is_running ovs-vswitchd; then init_restore_scripts if test X"$OVS_VSWITCHD" = Xyes; then save_flows_if_required fi fi # Restart the database first, since a large database may take a # while to load, and we want to minimize forwarding disruption. stop_ovsdb start_ovsdb stop_forwarding # Start vswitchd by asking it to wait till flow restore is finished. flow_restore_wait start_forwarding # Restore saved flows and inform vswitchd that we are done. restore_flows flow_restore_complete add_managers # Restore the interfaces if required. Return true even if restore fails. restore_interfaces || true } ## --------------- ## ## enable-protocol ## ## --------------- ## enable_protocol () { # Translate the protocol name to a number, because "iptables -n -L" prints # some protocols by name (despite the -n) and therefore we need to look for # both forms. # # (iptables -S output is more uniform but old iptables doesn't have it.) protonum=`grep "^$PROTOCOL[ ]" /etc/protocols | awk '{print $2}'` if expr X"$protonum" : X'[0-9]\{1,\}$' > /dev/null; then :; else log_failure_msg "unknown protocol $PROTOCOL" return 1 fi name=$PROTOCOL match="(\$2 == \"$PROTOCOL\" || \$2 == $protonum)" insert="iptables -I INPUT -p $PROTOCOL" if test X"$DPORT" != X; then name="$name to port $DPORT" match="$match && /dpt:$DPORT/" insert="$insert --dport $DPORT" fi if test X"$SPORT" != X; then name="$name from port $SPORT" match="$match && /spt:$SPORT/" insert="$insert --sport $SPORT" fi insert="$insert -j ACCEPT" if (iptables -n -L INPUT) >/dev/null 2>&1; then if iptables -n -L INPUT | awk "$match { n++ } END { exit n == 0 }" then # There's already a rule for this protocol. Don't override it. log_success_msg "iptables already has a rule for $name, not explicitly enabling" else action "Enabling $name with iptables" $insert fi elif (iptables --version) >/dev/null 2>&1; then action "cannot list iptables rules, not adding a rule for $name" else action "iptables binary not installed, not adding a rule for $name" fi } ## ---- ## ## main ## ## ---- ## set_defaults () { SYSTEM_ID= DELETE_BRIDGES=no DELETE_TRANSIENT_PORTS=no DAEMON_CWD=/ FORCE_COREFILES=yes MLOCKALL=yes MONITOR=yes OVSDB_SERVER=yes OVS_VSWITCHD=yes OVSDB_SERVER_PRIORITY=-10 OVS_VSWITCHD_PRIORITY=-10 OVSDB_SERVER_WRAPPER= OVS_VSWITCHD_WRAPPER= DB_FILE=$dbdir/conf.db DB_SOCK=$rundir/db.sock DB_SCHEMA=$datadir/vswitch.ovsschema EXTRA_DBS= PROTOCOL=gre DPORT= SPORT= type_file=$etcdir/system-type.conf version_file=$etcdir/system-version.conf if test -e "$type_file" ; then SYSTEM_TYPE=`cat $type_file` SYSTEM_VERSION=`cat $version_file` elif (lsb_release --id) >/dev/null 2>&1; then SYSTEM_TYPE=`lsb_release --id -s` system_release=`lsb_release --release -s` system_codename=`lsb_release --codename -s` SYSTEM_VERSION="${system_release}-${system_codename}" else SYSTEM_TYPE=unknown SYSTEM_VERSION=unknown fi } usage () { set_defaults cat <&2 "$0: unknown option \"$arg\" (use --help for help)" return fi eval $var=\$value } daemons () { echo ovsdb-server ovs-vswitchd } set_defaults extra_ids= command= for arg do case $arg in -h | --help) usage ;; -V | --version) echo "$0 (Open vSwitch) $VERSION" exit 0 ;; --external-id=*) value=`expr X"$arg" : 'X[^=]*=\(.*\)'` case $value in *=*) extra_ids="$extra_ids external-ids:$value" ;; *) echo >&2 "$0: --external-id argument not in the form \"key=value\"" exit 1 ;; esac ;; --[a-z]*=*) option=`expr X"$arg" : 'X--\([^=]*\)'` value=`expr X"$arg" : 'X[^=]*=\(.*\)'` type=string set_option ;; --no-[a-z]*) option=`expr X"$arg" : 'X--no-\(.*\)'` value=no type=bool set_option ;; --[a-z]*) option=`expr X"$arg" : 'X--\(.*\)'` value=yes type=bool set_option ;; -*) echo >&2 "$0: unknown option \"$arg\" (use --help for help)" exit 1 ;; *) if test X"$command" = X; then command=$arg else echo >&2 "$0: exactly one non-option argument required (use --help for help)" exit 1 fi ;; esac done case $command in start) start_ovsdb || exit 1 start_forwarding add_managers ;; stop) stop_forwarding stop_ovsdb ;; restart) restart ;; status) rc=0 for daemon in `daemons`; do daemon_status $daemon || rc=$? done exit $rc ;; version) for daemon in `daemons`; do $daemon --version done ;; force-reload-kmod) force_reload_kmod ;; load-kmod) insert_mod_if_required ;; enable-protocol) enable_protocol ;; help) usage ;; '') echo >&2 "$0: missing command name (use --help for help)" exit 1 ;; *) echo >&2 "$0: unknown command \"$command\" (use --help for help)" exit 1 ;; esac openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vsctl.8.in0000644000000000000000000000013213534540071020315 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.773845995 openvswitch-2.5.9/utilities/ovs-vsctl.8.in0000644000175000017500000010522113534540071022004 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .TH ovs\-vsctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-vsctl . .SH NAME ovs\-vsctl \- utility for querying and configuring \fBovs\-vswitchd\fR . .SH SYNOPSIS \fBovs\-vsctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]... . .SH DESCRIPTION The \fBovs\-vsctl\fR program configures \fBovs\-vswitchd\fR(8) by providing a high\-level interface to its configuration database. See \fBovs\-vswitchd.conf.db\fR(5) for comprehensive documentation of the database schema. .PP \fBovs\-vsctl\fR connects to an \fBovsdb\-server\fR process that maintains an Open vSwitch configuration database. Using this connection, it queries and possibly applies changes to the database, depending on the supplied commands. Then, if it applied any changes, by default it waits until \fBovs\-vswitchd\fR has finished reconfiguring itself before it exits. (If you use \fBovs\-vsctl\fR when \fBovs\-vswitchd\fR is not running, use \fB\-\-no\-wait\fR.) .PP \fBovs\-vsctl\fR can perform any number of commands in a single run, implemented as a single atomic transaction against the database. .PP The \fBovs\-vsctl\fR command line begins with global options (see \fBOPTIONS\fR below for details). The global options are followed by one or more commands. Each command should begin with \fB\-\-\fR by itself as a command-line argument, to separate it from the following commands. (The \fB\-\-\fR before the first command is optional.) The command itself starts with command-specific options, if any, followed by the command name and any arguments. See \fBEXAMPLES\fR below for syntax examples. . .SS "Linux VLAN Bridging Compatibility" The \fBovs\-vsctl\fR program supports the model of a bridge implemented by Open vSwitch, in which a single bridge supports ports on multiple VLANs. In this model, each port on a bridge is either a trunk port that potentially passes packets tagged with 802.1Q headers that designate VLANs or it is assigned a single implicit VLAN that is never tagged with an 802.1Q header. .PP For compatibility with software designed for the Linux bridge, \fBovs\-vsctl\fR also supports a model in which traffic associated with a given 802.1Q VLAN is segregated into a separate bridge. A special form of the \fBadd\-br\fR command (see below) creates a ``fake bridge'' within an Open vSwitch bridge to simulate this behavior. When such a ``fake bridge'' is active, \fBovs\-vsctl\fR will treat it much like a bridge separate from its ``parent bridge,'' but the actual implementation in Open vSwitch uses only a single bridge, with ports on the fake bridge assigned the implicit VLAN of the fake bridge of which they are members. (A fake bridge for VLAN 0 receives packets that have no 802.1Q tag or a tag with VLAN 0.) . .SH OPTIONS . The following options affect the behavior \fBovs\-vsctl\fR as a whole. Some individual commands also accept their own options, which are given just before the command name. If the first command on the command line has options, then those options must be separated from the global options by \fB\-\-\fR. . .IP "\fB\-\-db=\fIserver\fR" Sets \fIserver\fR as the database server that \fBovs\-vsctl\fR contacts to query or modify configuration. The default is \fBunix:@RUNDIR@/db.sock\fR. \fIserver\fR must take one of the following forms: .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE . .IP "\fB\-\-no\-wait\fR" Prevents \fBovs\-vsctl\fR from waiting for \fBovs\-vswitchd\fR to reconfigure itself according to the modified database. This option should be used if \fBovs\-vswitchd\fR is not running; otherwise, \fBovs\-vsctl\fR will not exit until \fBovs\-vswitchd\fR starts. .IP This option has no effect if the commands specified do not change the database. . .IP "\fB\-\-no\-syslog\fR" By default, \fBovs\-vsctl\fR logs its arguments and the details of any changes that it makes to the system log. This option disables this logging. .IP This option is equivalent to \fB\-\-verbose=vsctl:syslog:warn\fR. . .IP "\fB\-\-oneline\fR" Modifies the output format so that the output for each command is printed on a single line. New-line characters that would otherwise separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that would otherwise appear in the output are doubled. Prints a blank line for each command that has no output. This option does not affect the formatting of output from the \fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR below. . .IP "\fB\-\-dry\-run\fR" Prevents \fBovs\-vsctl\fR from actually modifying the database. . .IP "\fB\-t \fIsecs\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" By default, or with a \fIsecs\fR of \fB0\fR, \fBovs\-vsctl\fR waits forever for a response from the database. This option limits runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBovs\-vsctl\fR will exit with a \fBSIGALRM\fR signal. (A timeout would normally happen only if the database cannot be contacted, or if the system is overloaded.) . .IP "\fB\-\-retry\fR" Without this option, if \fBovs\-vsctl\fR connects outward to the database server (the default) then \fBovs\-vsctl\fR will try to connect once and exit with an error if the connection fails (which usually means that \fBovsdb\-server\fR is not running). .IP With this option, or if \fB\-\-db\fR specifies that \fBovs\-vsctl\fR should listen for an incoming connection from the database server, then \fBovs\-vsctl\fR will wait for a connection to the database forever. .IP Regardless of this setting, \fB\-\-timeout\fR always limits how long \fBovs\-vsctl\fR will wait. . .SS "Table Formatting Options" These options control the format of output from the \fBlist\fR and \fBfind\fR commands. .so lib/table.man . .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .so lib/ssl-peer-ca-cert.man .so lib/vlog.man .so lib/common.man . .SH COMMANDS The commands implemented by \fBovs\-vsctl\fR are described in the sections below. .SS "Open vSwitch Commands" These commands work with an Open vSwitch as a whole. . .IP "\fBinit\fR" Initializes the Open vSwitch database, if it is empty. If the database has already been initialized, this command has no effect. .IP Any successful \fBovs\-vsctl\fR command automatically initializes the Open vSwitch database if it is empty. This command is provided to initialize the database without executing any other command. . .IP "\fBshow\fR" Prints a brief overview of the database contents. . .IP "\fBemer\-reset\fR" Reset the configuration into a clean state. It deconfigures OpenFlow controllers, OVSDB servers, and SSL, and deletes port mirroring, \fBfail_mode\fR, NetFlow, sFlow, and IPFIX configuration. This command also removes all \fBother\-config\fR keys from all database records, except that \fBother\-config:hwaddr\fR is preserved if it is present in a Bridge record. Other networking configuration is left as-is. . .SS "Bridge Commands" These commands examine and manipulate Open vSwitch bridges. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-br \fIbridge\fR" Creates a new bridge named \fIbridge\fR. Initially the bridge will have no ports (other than \fIbridge\fR itself). .IP Without \fB\-\-may\-exist\fR, attempting to create a bridge that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIbridge\fR already exists as a real bridge. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-br \fIbridge parent vlan\fR" Creates a ``fake bridge'' named \fIbridge\fR within the existing Open vSwitch bridge \fIparent\fR, which must already exist and must not itself be a fake bridge. The new fake bridge will be on 802.1Q VLAN \fIvlan\fR, which must be an integer between 0 and 4095. The parent bridge must not already have a fake bridge for \fIvlan\fR. Initially \fIbridge\fR will have no ports (other than \fIbridge\fR itself). .IP Without \fB\-\-may\-exist\fR, attempting to create a bridge that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIbridge\fR already exists as a VLAN bridge under \fIparent\fR for \fIvlan\fR. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-br \fIbridge\fR" Deletes \fIbridge\fR and all of its ports. If \fIbridge\fR is a real bridge, this command also deletes any fake bridges that were created with \fIbridge\fR as parent, including all of their ports. .IP Without \fB\-\-if\-exists\fR, attempting to delete a bridge that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a bridge that does not exist has no effect. . .IP "[\fB\-\-real\fR|\fB\-\-fake\fR] \fBlist\-br\fR" Lists all existing real and fake bridges on standard output, one per line. With \fB\-\-real\fR or \fB\-\-fake\fR, only bridges of that type are returned. . .IP "\fBbr\-exists \fIbridge\fR" Tests whether \fIbridge\fR exists as a real or fake bridge. If so, \fBovs\-vsctl\fR exits successfully with exit code 0. If not, \fBovs\-vsctl\fR exits unsuccessfully with exit code 2. . .IP "\fBbr\-to\-vlan \fIbridge\fR" If \fIbridge\fR is a fake bridge, prints the bridge's 802.1Q VLAN as a decimal integer. If \fIbridge\fR is a real bridge, prints 0. . .IP "\fBbr\-to\-parent \fIbridge\fR" If \fIbridge\fR is a fake bridge, prints the name of its parent bridge. If \fIbridge\fR is a real bridge, print \fIbridge\fR. . .IP "\fBbr\-set\-external\-id \fIbridge key\fR [\fIvalue\fR]" Sets or clears an ``external ID'' value on \fIbridge\fR. These values are intended to identify entities external to Open vSwitch with which \fIbridge\fR is associated, e.g. the bridge's identifier in a virtualization management platform. The Open vSwitch database schema specifies well-known \fIkey\fR values, but \fIkey\fR and \fIvalue\fR are otherwise arbitrary strings. .IP If \fIvalue\fR is specified, then \fIkey\fR is set to \fIvalue\fR for \fIbridge\fR, overwriting any previous value. If \fIvalue\fR is omitted, then \fIkey\fR is removed from \fIbridge\fR's set of external IDs (if it was present). .IP For real bridges, the effect of this command is similar to that of a \fBset\fR or \fBremove\fR command in the \fBexternal\-ids\fR column of the \fBBridge\fR table. For fake bridges, it actually modifies keys with names prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table. . .IP "\fBbr\-get\-external\-id \fIbridge\fR [\fIkey\fR]" Queries the external IDs on \fIbridge\fR. If \fIkey\fR is specified, the output is the value for that \fIkey\fR or the empty string if \fIkey\fR is unset. If \fIkey\fR is omitted, the output is \fIkey\fB=\fIvalue\fR, one per line, for each key-value pair. .IP For real bridges, the effect of this command is similar to that of a \fBget\fR command in the \fBexternal\-ids\fR column of the \fBBridge\fR table. For fake bridges, it queries keys with names prefixed by \fBfake\-bridge\-\fR in the \fBPort\fR table. . .SS "Port Commands" . These commands examine and manipulate Open vSwitch ports. These commands treat a bonded port as a single entity. . .IP "\fBlist\-ports \fIbridge\fR" Lists all of the ports within \fIbridge\fR on standard output, one per line. The local port \fIbridge\fR is not included in the list. . .IP "[\fB\-\-may\-exist\fR] \fBadd\-port \fIbridge port \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fR=\fIvalue\fR]\&...\fR" Creates on \fIbridge\fR a new port named \fIport\fR from the network device of the same name. .IP Optional arguments set values of column in the Port record created by the command. For example, \fBtag=9\fR would make the port an access port for VLAN 9. The syntax is the same as that for the \fBset\fR command (see \fBDatabase Commands\fR below). .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIbridge\fR and is not a bonded port. . .IP "[\fB\-\-fake\-iface\fR] \fBadd\-bond \fIbridge port iface\fR\&... [\fIcolumn\fR[\fB:\fIkey\fR]\fR=\fIvalue\fR]\&...\fR" Creates on \fIbridge\fR a new port named \fIport\fR that bonds together the network devices given as each \fIiface\fR. At least two interfaces must be named. If the interfaces are DPDK enabled then the transaction will need to include operations to explicitly set the interface type to 'dpdk'. .IP Optional arguments set values of column in the Port record created by the command. The syntax is the same as that for the \fBset\fR command (see \fBDatabase Commands\fR below). .IP With \fB\-\-fake\-iface\fR, a fake interface with the name \fIport\fR is created. This should only be used for compatibility with legacy software that requires it. .IP Without \fB\-\-may\-exist\fR, attempting to create a port that exists is an error. With \fB\-\-may\-exist\fR, this command does nothing if \fIport\fR already exists on \fIbridge\fR and bonds together exactly the specified interfaces. . .IP "[\fB\-\-if\-exists\fR] \fBdel\-port \fR[\fIbridge\fR] \fIport\fR" Deletes \fIport\fR. If \fIbridge\fR is omitted, \fIport\fR is removed from whatever bridge contains it; if \fIbridge\fR is specified, it must be the real or fake bridge that contains \fIport\fR. .IP Without \fB\-\-if\-exists\fR, attempting to delete a port that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete a port that does not exist has no effect. . .IP "[\fB\-\-if\-exists\fR] \fB\-\-with\-iface del\-port \fR[\fIbridge\fR] \fIiface\fR" Deletes the port named \fIiface\fR or that has an interface named \fIiface\fR. If \fIbridge\fR is omitted, the port is removed from whatever bridge contains it; if \fIbridge\fR is specified, it must be the real or fake bridge that contains the port. .IP Without \fB\-\-if\-exists\fR, attempting to delete the port for an interface that does not exist is an error. With \fB\-\-if\-exists\fR, attempting to delete the port for an interface that does not exist has no effect. . .IP "\fBport\-to\-br \fIport\fR" Prints the name of the bridge that contains \fIport\fR on standard output. . .SS "Interface Commands" . These commands examine the interfaces attached to an Open vSwitch bridge. These commands treat a bonded port as a collection of two or more interfaces, rather than as a single port. . .IP "\fBlist\-ifaces \fIbridge\fR" Lists all of the interfaces within \fIbridge\fR on standard output, one per line. The local port \fIbridge\fR is not included in the list. . .IP "\fBiface\-to\-br \fIiface\fR" Prints the name of the bridge that contains \fIiface\fR on standard output. . .SS "OpenFlow Controller Connectivity" . \fBovs\-vswitchd\fR can perform all configured bridging and switching locally, or it can be configured to communicate with one or more external OpenFlow controllers. The switch is typically configured to connect to a primary controller that takes charge of the bridge's flow table to implement a network policy. In addition, the switch can be configured to listen to connections from service controllers. Service controllers are typically used for occasional support and maintenance, e.g. with \fBovs\-ofctl\fR. . .IP "\fBget\-controller\fR \fIbridge\fR" Prints the configured controller target. . .IP "\fBdel\-controller\fR \fIbridge\fR" Deletes the configured controller target. . .IP "\fBset\-controller\fR \fIbridge\fR \fItarget\fR\&..." Sets the configured controller target or targets. Each \fItarget\fR may use any of the following forms: . .RS .so lib/vconn-active.man .so lib/vconn-passive.man .RE . .ST "Controller Failure Settings" .PP When a controller is configured, it is, ordinarily, responsible for setting up all flows on the switch. Thus, if the connection to the controller fails, no new network connections can be set up. If the connection to the controller stays down long enough, no packets can pass through the switch at all. .PP If the value is \fBstandalone\fR, or if neither of these settings is set, \fBovs\-vswitchd\fR will take over responsibility for setting up flows when no message has been received from the controller for three times the inactivity probe interval. In this mode, \fBovs\-vswitchd\fR causes the datapath to act like an ordinary MAC-learning switch. \fBovs\-vswitchd\fR will continue to retry connecting to the controller in the background and, when the connection succeeds, it discontinues its standalone behavior. .PP If this option is set to \fBsecure\fR, \fBovs\-vswitchd\fR will not set up flows on its own when the controller connection fails. . .IP "\fBget\-fail\-mode\fR \fIbridge\fR" Prints the configured failure mode. . .IP "\fBdel\-fail\-mode\fR \fIbridge\fR" Deletes the configured failure mode. . .IP "\fBset\-fail\-mode\fR \fIbridge\fR \fBstandalone\fR|\fBsecure\fR" Sets the configured failure mode. . .SS "Manager Connectivity" . These commands manipulate the \fBmanager_options\fR column in the \fBOpen_vSwitch\fR table and rows in the \fBManagers\fR table. When \fBovsdb\-server\fR is configured to use the \fBmanager_options\fR column for OVSDB connections (as described in \fBINSTALL.Linux\fR and in the startup scripts provided with Open vSwitch), this allows the administrator to use \fBovs\-vsctl\fR to configure database connections. . .IP "\fBget\-manager\fR" Prints the configured manager(s). . .IP "\fBdel\-manager\fR" Deletes the configured manager(s). . .IP "\fBset\-manager\fR \fItarget\fR\&..." Sets the configured manager target or targets. Each \fItarget\fR may use any of the following forms: . .RS .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .RE . .SS "SSL Configuration" When \fBovs\-vswitchd\fR is configured to connect over SSL for management or controller connectivity, the following parameters are required: .TP \fIprivate-key\fR Specifies a PEM file containing the private key used as the virtual switch's identity for SSL connections to the controller. .TP \fIcertificate\fR Specifies a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the virtual switch's private key, identifying a trustworthy switch. .TP \fIca-cert\fR Specifies a PEM file containing the CA certificate used to verify that the virtual switch is connected to a trustworthy controller. .PP These files are read only once, at \fBovs\-vswitchd\fR startup time. If their contents change, \fBovs\-vswitchd\fR must be killed and restarted. .PP These SSL settings apply to all SSL connections made by the virtual switch. . .IP "\fBget\-ssl\fR" Prints the SSL configuration. . .IP "\fBdel\-ssl\fR" Deletes the current SSL configuration. . .IP "[\fB\-\-bootstrap\fR] \fBset\-ssl\fR \fIprivate-key\fR \fIcertificate\fR \fIca-cert\fR" Sets the SSL configuration. The \fB\-\-bootstrap\fR option is described below. . .ST "CA Certificate Bootstrap" .PP Ordinarily, all of the files named in the SSL configuration must exist when \fBovs\-vswitchd\fR starts. However, if the \fIca-cert\fR file does not exist and the \fB\-\-bootstrap\fR option is given, then \fBovs\-vswitchd\fR will attempt to obtain the CA certificate from the controller on its first SSL connection and save it to the named PEM file. If it is successful, it will immediately drop the connection and reconnect, and from then on all SSL connections must be authenticated by a certificate signed by the CA certificate thus obtained. .PP \fBThis option exposes the SSL connection to a man-in-the-middle attack obtaining the initial CA certificate\fR, but it may be useful for bootstrapping. .PP This option is only useful if the controller sends its CA certificate as part of the SSL certificate chain. The SSL protocol does not require the controller to send the CA certificate. . .SS "Auto-Attach Commands" . The IETF Auto-Attach SPBM draft standard describes a compact method of using IEEE 802.1AB Link Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq Shortest Path Bridging (SPB) network to automatically attach network devices to individual services in a SPB network. The intent here is to allow network applications and devices using OVS to be able to easily take advantage of features offered by industry standard SPB networks. A fundamental element of the Auto-Attach feature is to map traditional VLANs onto SPB I_SIDs. These commands manage the Auto-Attach I-SID/VLAN mappings. . .IP "\fBadd\-aa\-mapping \fIbridge i-sid vlan\fR" Creates a new Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR and \fIvlan\fR. . .IP "\fBdel\-aa\-mapping \fIbridge i-sid vlan\fR" Deletes an Auto-Attach mapping on \fIbridge\fR for \fIi-sid\fR and \fIvlan\fR. .IP "\fBget\-aa\-mapping \fIbridge\fR" Lists all of the Auto-Attach mappings within \fIbridge\fR on standard output. . .SS "Database Commands" . These commands query and modify the contents of \fBovsdb\fR tables. They are a slight abstraction of the \fBovsdb\fR interface and as such they operate at a lower level than other \fBovs\-vsctl\fR commands. .PP .ST "Identifying Tables, Records, and Columns" .PP Each of these commands has a \fItable\fR parameter to identify a table within the database. Many of them also take a \fIrecord\fR parameter that identifies a particular record within a table. The \fIrecord\fR parameter may be the UUID for a record, and many tables offer additional ways to identify records. Some commands also take \fIcolumn\fR parameters that identify a particular field within the records in a table. .PP The following tables are currently defined: .IP "\fBOpen_vSwitch\fR" Global configuration for an \fBovs\-vswitchd\fR. This table contains exactly one record, identified by specifying \fB.\fR as the record name. .IP "\fBBridge\fR" Configuration for a bridge within an Open vSwitch. Records may be identified by bridge name. .IP "\fBPort\fR" A bridge port. Records may be identified by port name. .IP "\fBInterface\fR" A network device attached to a port. Records may be identified by name. .IP "\fBFlow_Table\fR" Configuration for a particular OpenFlow flow table. Records may be identified by name. .IP "\fBQoS\fR" Quality-of-service configuration for a \fBPort\fR. Records may be identified by port name. .IP "\fBQueue\fR" Configuration for one queue within a \fBQoS\fR configuration. Records may only be identified by UUID. .IP "\fBMirror\fR" A port mirroring configuration attached to a bridge. Records may be identified by mirror name. .IP "\fBController\fR" Configuration for an OpenFlow controller. A controller attached to a particular bridge may be identified by the bridge's name. .IP "\fBManager\fR" Configuration for an OVSDB connection. Records may be identified by target (e.g. \fBtcp:1.2.3.4\fR). .IP "\fBNetFlow\fR" A NetFlow configuration attached to a bridge. Records may be identified by bridge name. .IP "\fBSSL\fR" The global SSL configuration for \fBovs\-vswitchd\fR. The record attached to the \fBOpen_vSwitch\fR table may be identified by specifying \fB.\fR as the record name. .IP "\fBsFlow\fR" An sFlow exporter configuration attached to a bridge. Records may be identified by bridge name. .IP "\fBIPFIX\fR" An IPFIX exporter configuration attached to a bridge. Records may be identified by bridge name. .IP "\fBFlow_Sample_Collector_Set\fR" An IPFIX exporter configuration attached to a bridge for sampling packets on a per-flow basis using OpenFlow \fBsample\fR actions. .IP "\fBAutoAttach\fR" Configuration for Auto Attach within a bridge. .PP Record names must be specified in full and with correct capitalization. Names of tables and columns are not case-sensitive, and \fB\-\-\fR and \fB_\fR are treated interchangeably. Unique abbreviations are acceptable, e.g. \fBnet\fR or \fBn\fR is sufficient to identify the \fBNetFlow\fR table. . .so lib/db-ctl-base.man .SH "EXAMPLES" Create a new bridge named br0 and add port eth0 to it: .IP .B "ovs\-vsctl add\-br br0" .br .B "ovs\-vsctl add\-port br0 eth0" .PP Alternatively, perform both operations in a single atomic transaction: .IP .B "ovs\-vsctl add\-br br0 \-\- add\-port br0 eth0" .PP Delete bridge \fBbr0\fR, reporting an error if it does not exist: .IP .B "ovs\-vsctl del\-br br0" .PP Delete bridge \fBbr0\fR if it exists: .IP .B "ovs\-vsctl \-\-if\-exists del\-br br0" .PP Set the \fBqos\fR column of the \fBPort\fR record for \fBeth0\fR to point to a new \fBQoS\fR record, which in turn points with its queue 0 to a new \fBQueue\fR record: .IP .B "ovs\-vsctl \-\- set port eth0 qos=@newqos \-\- \-\-id=@newqos create qos type=linux\-htb other\-config:max\-rate=1000000 queues:0=@newqueue \-\- \-\-id=@newqueue create queue other\-config:min\-rate=1000000 other\-config:max\-rate=1000000" .SH "CONFIGURATION COOKBOOK" .SS "Port Configuration" .PP Add an ``internal port'' \fBvlan10\fR to bridge \fBbr0\fR as a VLAN access port for VLAN 10, and configure it with an IP address: .IP .B "ovs\-vsctl add\-port br0 vlan10 tag=10 \-\- set Interface vlan10 type=internal" .IP .B "ifconfig vlan10 192.168.0.123" . .PP Add a GRE tunnel port \fBgre0\fR to remote IP address 1.2.3.4 to bridge \fBbr0\fR: .IP .B "ovs\-vsctl add\-port br0 gre0 \-\- set Interface gre0 type=gre options:remote_ip=1.2.3.4" . .SS "Port Mirroring" .PP Mirror all packets received or sent on \fBeth0\fR or \fBeth1\fR onto \fBeth2\fR, assuming that all of those ports exist on bridge \fBbr0\fR (as a side-effect this causes any packets received on \fBeth2\fR to be ignored): .IP .B "ovs\-vsctl \-\- set Bridge br0 mirrors=@m \(rs" .IP .B "\-\- \-\-id=@eth0 get Port eth0 \(rs" .IP .B "\-\- \-\-id=@eth1 get Port eth1 \(rs" .IP .B "\-\- \-\-id=@eth2 get Port eth2 \(rs" .IP .B "\-\- \-\-id=@m create Mirror name=mymirror select-dst-port=@eth0,@eth1 select-src-port=@eth0,@eth1 output-port=@eth2" .PP Remove the mirror created above from \fBbr0\fR, which also destroys the Mirror record (since it is now unreferenced): .IP .B "ovs\-vsctl \-\- \-\-id=@rec get Mirror mymirror \(rs" .IP .B "\-\- remove Bridge br0 mirrors @rec" .PP The following simpler command also works: .IP .B "ovs\-vsctl clear Bridge br0 mirrors" .SS "Quality of Service (QoS)" .PP Create a \fBlinux\-htb\fR QoS record that points to a few queues and use it on \fBeth0\fR and \fBeth1\fR: .IP .B "ovs\-vsctl \-\- set Port eth0 qos=@newqos \(rs" .IP .B "\-\- set Port eth1 qos=@newqos \(rs" .IP .B "\-\- \-\-id=@newqos create QoS type=linux\-htb other\-config:max\-rate=1000000000 queues=0=@q0,1=@q1 \(rs" .IP .B "\-\- \-\-id=@q0 create Queue other\-config:min\-rate=100000000 other\-config:max\-rate=100000000 \(rs" .IP .B "\-\- \-\-id=@q1 create Queue other\-config:min\-rate=500000000" .PP Deconfigure the QoS record above from \fBeth1\fR only: .IP .B "ovs\-vsctl clear Port eth1 qos" .PP To deconfigure the QoS record from both \fBeth0\fR and \fBeth1\fR and then delete the QoS record (which must be done explicitly because unreferenced QoS records are not automatically destroyed): .IP .B "ovs\-vsctl \-\- destroy QoS eth0 \-\- clear Port eth0 qos \-\- clear Port eth1 qos" .PP (This command will leave two unreferenced Queue records in the database. To delete them, use "\fBovs\-vsctl list Queue\fR" to find their UUIDs, then "\fBovs\-vsctl destroy Queue \fIuuid1\fR \fIuuid2\fR" to destroy each of them or use "\fBovs\-vsctl -- --all destroy Queue\fR" to delete all records.) .SS "Connectivity Monitoring" .PP Monitor connectivity to a remote maintenance point on eth0. .IP .B "ovs\-vsctl set Interface eth0 cfm_mpid=1" .PP Deconfigure connectivity monitoring from above: .IP .B "ovs\-vsctl clear Interface eth0 cfm_mpid" .SS "NetFlow" .PP Configure bridge \fBbr0\fR to send NetFlow records to UDP port 5566 on host 192.168.0.34, with an active timeout of 30 seconds: .IP .B "ovs\-vsctl \-\- set Bridge br0 netflow=@nf \(rs" .IP .B "\-\- \-\-id=@nf create NetFlow targets=\(rs\(dq192.168.0.34:5566\(rs\(dq active\-timeout=30" .PP Update the NetFlow configuration created by the previous command to instead use an active timeout of 60 seconds: .IP .B "ovs\-vsctl set NetFlow br0 active_timeout=60" .PP Deconfigure the NetFlow settings from \fBbr0\fR, which also destroys the NetFlow record (since it is now unreferenced): .IP .B "ovs\-vsctl clear Bridge br0 netflow" .SS "sFlow" .PP Configure bridge \fBbr0\fR to send sFlow records to a collector on 10.0.0.1 at port 6343, using \fBeth1\fR\'s IP address as the source, with specific sampling parameters: .IP .B "ovs\-vsctl \-\- \-\-id=@s create sFlow agent=eth1 target=\(rs\(dq10.0.0.1:6343\(rs\(dq header=128 sampling=64 polling=10 \(rs" .IP .B "\-\- set Bridge br0 sflow=@s" .PP Deconfigure sFlow from \fBbr0\fR, which also destroys the sFlow record (since it is now unreferenced): .IP .B "ovs\-vsctl \-\- clear Bridge br0 sflow" .SS "IPFIX" .PP Configure bridge \fBbr0\fR to send one IPFIX flow record per packet sample to UDP port 4739 on host 192.168.0.34, with Observation Domain ID 123 and Observation Point ID 456, a flow cache active timeout of 1 minute (60 seconds), maximum flow cache size of 13 flows, and flows sampled on output port with tunnel info(sampling on input and output port is enabled by default if not disabled) : .IP .B "ovs\-vsctl \-\- set Bridge br0 ipfix=@i \(rs" .IP .B "\-\- \-\-id=@i create IPFIX targets=\(rs\(dq192.168.0.34:4739\(rs\(dq obs_domain_id=123 obs_point_id=456 cache_active_timeout=60 cache_max_flows=13 \(rs" .IP .B "other_config:enable-input-sampling=false other_config:enable-tunnel-sampling=true" .PP Deconfigure the IPFIX settings from \fBbr0\fR, which also destroys the IPFIX record (since it is now unreferenced): .IP .B "ovs\-vsctl clear Bridge br0 ipfix" .SS "802.1D Spanning Tree Protocol (STP)" .PP Configure bridge \fBbr0\fR to participate in an 802.1D spanning tree: .IP .B "ovs\-vsctl set Bridge br0 stp_enable=true" .PP Set the bridge priority of \fBbr0\fR to 0x7800: .IP .B "ovs\-vsctl set Bridge br0 other_config:stp-priority=0x7800" .PP Set the path cost of port \fBeth0\fR to 10: .IP .B "ovs\-vsctl set Port eth0 other_config:stp-path-cost=10" .PP Deconfigure STP from above: .IP .B "ovs\-vsctl set Bridge br0 stp_enable=false" .PP .SS "Multicast Snooping" .PP Configure bridge \fBbr0\fR to enable multicast snooping: .IP .B "ovs\-vsctl set Bridge br0 mcast_snooping_enable=true" .PP Set the multicast snooping aging time \fBbr0\fR to 300 seconds: .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-aging-time=300" .PP Set the multicast snooping table size \fBbr0\fR to 2048 entries: .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-table-size=2048" .PP Disable flooding of unregistered multicast packets to all ports. When set to \fBtrue\fR, the switch will send unregistered multicast packets only to ports connected to multicast routers. When it is set to \fBfalse\fR, the switch will send them to all ports. This command disables the flood of unregistered packets on bridge \fBbr0\fR. .IP .B "ovs\-vsctl set Bridge br0 other_config:mcast-snooping-disable-flood-unregistered=true" .PP Enable flooding of multicast packets (except Reports) on a specific port. .IP .B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood=true" .PP Enable flooding of Reports on a specific port. .IP .B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood-reports=true" .PP Deconfigure multicasting snooping from above: .IP .B "ovs\-vsctl set Bridge br0 mcast_snooping_enable=false" .PP .SS "802.1D-2004 Rapid Spanning Tree Protocol (RSTP)" .PP Configure bridge \fBbr0\fR to participate in an 802.1D-2004 Rapid Spanning Tree: .IP .B "ovs\-vsctl set Bridge br0 rstp_enable=true" .PP Set the bridge address of \fBbr0\fR to 00:aa:aa:aa:aa:aa : .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-address=00:aa:aa:aa:aa:aa" .PP Set the bridge priority of \fBbr0\fR to 0x7000. The value must be specified in decimal notation and should be a multiple of 4096 (if not, it is rounded down to the nearest multiple of 4096). The default priority value is 0x800 (32768). .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-priority=28672" .PP Set the bridge ageing time of \fBbr0\fR to 1000 s. The ageing time value should be between 10 s and 1000000 s. The default value is 300 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-ageing-time=1000" .PP Set the bridge force protocol version of \fBbr0\fR to 0. The force protocol version has two acceptable values: 0 (STP compatibility mode) and 2 (normal operation). .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-force-protocol-version=0" .PP Set the bridge max age of \fBbr0\fR to 10 s. The max age value should be between 6 s and 40 s. The default value is 20 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-max-age=10" .PP Set the bridge forward delay of \fBbr0\fR to 15 s. This value should be between 4 s and 30 s. The default value is 15 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-forward-delay=15" .PP Set the bridge transmit hold count of \fBbr0\fR to 7 s. This value should be between 1 s and 10 s. The default value is 6 s. .IP .B "ovs\-vsctl set Bridge br0 other_config:rstp-transmit-hold-count=7" .PP Enable RSTP on the Port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-enable=true" .PP Disable RSTP on the Port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-enable=false" .PP Set the priority of port \fBeth0\fR to 32. The value must be specified in decimal notation and should be a multiple of 16 (if not, it is rounded down to the nearest multiple of 16). The default priority value is 0x80 (128). .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-priority=32" .PP Set the port number of port \fBeth0\fR to 3: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-num=3" .PP Set the path cost of port \fBeth0\fR to 150: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-path-cost=150" .PP Set the admin edge value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-admin-edge=true" .PP Set the auto edge value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-auto-edge=true" .PP Set the admin point to point MAC value of port \fBeth0\fR. Acceptable values are \fB0\fR (not point-to-point), \fB1\fR (point-to-point, the default value) or \fB2\fR (automatic detection). The auto-detection mode is not currently implemented, and the value \fB2\fR has the same effect of \fB0\fR (not point-to-point). .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-admin-p2p-mac=1" .PP Set the admin port state value of port \fBeth0\fR. \fBtrue\fR is the default value. .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-admin-port-state=false" .PP Set the mcheck value of port \fBeth0\fR: .IP .B "ovs\-vsctl set Port eth0 other_config:rstp-port-mcheck=true" .PP Deconfigure RSTP from above: .IP .B "ovs\-vsctl set Bridge br0 rstp_enable=false" .PP .SS "OpenFlow Version" .PP Configure bridge \fBbr0\fR to support OpenFlow versions 1.0, 1.2, and 1.3: .IP .B "ovs\-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13" . .SS "Flow Table Configuration" Limit flow table 0 on bridge br0 to a maximum of 100 flows: .IP .B "ovs\-vsctl \-\- \-\-id=@ft create Flow_Table flow_limit=100 overflow_policy=refuse \-\- set Bridge br0 flow_tables=0=@ft" .SH "EXIT STATUS" .IP "0" Successful program execution. .IP "1" Usage, syntax, or configuration file error. .IP "2" The \fIbridge\fR argument to \fBbr\-exists\fR specified the name of a bridge that does not exist. .SH "SEE ALSO" . .BR ovsdb\-server (1), .BR ovs\-vswitchd (8), .BR ovs\-vswitchd.conf.db (5). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-l3ping.in0000644000000000000000000000013213534540071020210 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.813846291 openvswitch-2.5.9/utilities/ovs-l3ping.in0000644000175000017500000000541013534540071021676 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ ovs L3 ping utility allows to do tests between two remote hosts without opening holes in the firewall for the XML RPC control connection. This is achieved by tunneling the control connection inside the tunnel itself. """ import socket import xmlrpclib import ovstest.args as args import ovstest.tests as tests import ovstest.util as util def get_packet_sizes(me, he, remote_ip): """ This function retrieves MTUs from both hosts and returns a list of packet sizes, that are more likely to uncover possible configuration issues. """ mtu_node1 = 1500 mtu_node2 = 1500 server1 = util.rpc_client(me[0], me[1]) server2 = util.rpc_client(he[0], he[1]) iface1 = server2.get_interface(remote_ip) iface2 = server1.get_interface_from_routing_decision(remote_ip) if iface1: mtu_node1 = server2.get_interface_mtu(iface1) if iface2: mtu_node2 = server1.get_interface_mtu(iface2) return util.get_datagram_sizes(mtu_node1, mtu_node2) if __name__ == '__main__': local_server = None try: args = args.l3_initialize_args() tunnel_mode = args.tunnelMode if args.server is not None: # Start in server mode local_server = tests.configure_l3(args.server, tunnel_mode) local_server.wait() elif args.client is not None: # Run in client mode bandwidth = args.targetBandwidth interval = args.testInterval me = (util.ip_from_cidr(args.client[1][0]), args.client[1][1], args.client[1][0], args.client[1][2]) he = (args.client[2][0], args.client[2][1], args.client[2][0], args.client[2][2]) local_server = tests. configure_l3(args.client, tunnel_mode) ps = get_packet_sizes(me, he, args.client[0]) tests.do_direct_tests(me, he, bandwidth, interval, ps) except KeyboardInterrupt: print "Terminating" except xmlrpclib.Fault: print "Couldn't contact peer" except socket.error: print "Couldn't contact peer" except xmlrpclib.ProtocolError: print "XMLRPC control channel was abruptly terminated" finally: if local_server is not None: local_server.terminate() openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-dpctl-top.in0000644000000000000000000000013213534540071020722 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.813846291 openvswitch-2.5.9/utilities/ovs-dpctl-top.in0000755000175000017500000016767213534540071022436 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Copyright (c) 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # The approximate_size code was copied from # http://getpython3.com/diveintopython3/your-first-python-program.html#divingin # which is licensed under # "Dive Into Python 3," Copyright 2011 Mark Pilgrim, # used under a Creative Commons Attribution-Share-Alike license: # http://creativecommons.org/licenses/by-sa/3.0/ # # """Top like behavior for ovs-dpctl dump-flows output. This program summarizes ovs-dpctl flow content by aggregating the number of packets, total bytes and occurrence of the following fields: - Datapath in_port - Ethernet type - Source and destination MAC addresses - IP protocol - Source and destination IPv4 addresses - Source and destination IPv6 addresses - UDP and TCP destination port - Tunnel source and destination addresses Output shows four values: - FIELDS: the flow fields for example in_port(1). - PACKETS: the total number of packets containing the flow field. - BYTES: the total number of bytes containing the flow field. If units are not present then values are in bytes. - AVERAGE: the average packets size (BYTES/PACKET). - COUNT: the number of lines in the dump-flow output contain the flow field. Top Behavior While in top mode, the default behavior, the following single character commands are supported: a - toggles top in accumulate and live mode. Accumulate mode is described below. s - toggles which column is used to sort content in decreasing order. A DESC title is placed over the column. _ - a space indicating to collect dump-flow content again h - halt output. Any character will restart sampling f - cycle through flow fields. The initial field is in_port q - q for quit. Accumulate Mode There are two supported modes: live and accumulate. The default is live. The parameter --accumulate or the 'a' character in top mode enables the latter. In live mode, recent dump-flow content is presented. Where as accumulate mode keeps track of the prior historical information until the flow is reset not when the flow is purged. Reset flows are determined when the packet count for a flow has decreased from its previous sample. There is one caveat, eventually the system will run out of memory if, after the accumulate-decay period any flows that have not been refreshed are purged. The goal here is to free memory of flows that are not active. Statistics are not decremented. Their purpose is to reflect the overall history of the flow fields. Debugging Errors Parsing errors are counted and displayed in the status line at the beginning of the output. Use the --verbose option with --script to see what output was not parsed, like this: $ ovs-dpctl dump-flows | ovs-dpctl-top --script --verbose Error messages will identify content that failed to parse. Access Remote Hosts The --host must follow the format user@hostname. This script simply calls 'ssh user@Hostname' without checking for login credentials therefore public keys should be installed on the system identified by hostname, such as: $ ssh-copy-id user@hostname Consult ssh-copy-id man pages for more details. Expected usage $ ovs-dpctl-top or to run as a script: $ ovs-dpctl dump-flows > dump-flows.log $ ovs-dpctl-top --script --flow-file dump-flows.log """ # pylint: disable-msg=C0103 # pylint: disable-msg=C0302 # pylint: disable-msg=R0902 # pylint: disable-msg=R0903 # pylint: disable-msg=R0904 # pylint: disable-msg=R0912 # pylint: disable-msg=R0913 # pylint: disable-msg=R0914 import sys import os try: ## # Arg parse is not installed on older Python distributions. # ovs ships with a version in the directory mentioned below. import argparse except ImportError: sys.path.append(os.path.join("@pkgdatadir@", "python")) import argparse import logging import re import unittest import copy import curses import operator import subprocess import fcntl import struct import termios import datetime import threading import time import socket ## # The following two definitions provide the necessary netaddr functionality. # Python netaddr module is not part of the core installation. Packaging # netaddr was involved and seems inappropriate given that only two # methods where used. def ipv4_to_network(ip_str): """ Calculate the network given a ipv4/mask value. If a mask is not present simply return ip_str. """ pack_length = '!HH' try: (ip, mask) = ip_str.split("/") except ValueError: # just an ip address no mask. return ip_str ip_p = socket.inet_pton(socket.AF_INET, ip) ip_t = struct.unpack(pack_length, ip_p) mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET, mask)) network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] return socket.inet_ntop(socket.AF_INET, struct.pack('!HH', network_n[0], network_n[1])) def ipv6_to_network(ip_str): """ Calculate the network given a ipv6/mask value. If a mask is not present simply return ip_str. """ pack_length = '!HHHHHHHH' try: (ip, mask) = ip_str.split("/") except ValueError: # just an ip address no mask. return ip_str ip_p = socket.inet_pton(socket.AF_INET6, ip) ip_t = struct.unpack(pack_length, ip_p) mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET6, mask)) network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)] return socket.inet_ntop(socket.AF_INET6, struct.pack(pack_length, network_n[0], network_n[1], network_n[2], network_n[3], network_n[4], network_n[5], network_n[6], network_n[7])) ## # columns displayed ## class Columns: """ Holds column specific content. Titles needs to be less than 8 characters. """ VALUE_WIDTH = 9 FIELDS = "fields" PACKETS = "packets" COUNT = "count" BYTES = "bytes" AVERAGE = "average" def __init__(self): pass @staticmethod def assoc_list(obj): """ Return a associated list. """ return [(Columns.FIELDS, repr(obj)), (Columns.PACKETS, obj.packets), (Columns.BYTES, obj.bytes), (Columns.COUNT, obj.count), (Columns.AVERAGE, obj.average), ] def element_eth_get(field_type, element, stats_dict): """ Extract eth frame src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element = fmt % (field_type, element["src"], element["dst"]) return SumData(field_type, element, stats_dict["packets"], stats_dict["bytes"], element) def element_ipv4_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element_show = fmt % (field_type, element["src"], element["dst"]) element_key = fmt % (field_type, ipv4_to_network(element["src"]), ipv4_to_network(element["dst"])) return SumData(field_type, element_show, stats_dict["packets"], stats_dict["bytes"], element_key) def element_tunnel_get(field_type, element, stats_dict): """ Extract src and dst from a tunnel.""" return element_ipv4_get(field_type, element, stats_dict) def element_ipv6_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" fmt = "%s(src=%s,dst=%s)" element_show = fmt % (field_type, element["src"], element["dst"]) element_key = fmt % (field_type, ipv6_to_network(element["src"]), ipv6_to_network(element["dst"])) return SumData(field_type, element_show, stats_dict["packets"], stats_dict["bytes"], element_key) def element_dst_port_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" element_key = "%s(dst=%s)" % (field_type, element["dst"]) return SumData(field_type, element_key, stats_dict["packets"], stats_dict["bytes"], element_key) def element_passthrough_get(field_type, element, stats_dict): """ Extract src and dst from a dump-flow element.""" element_key = "%s(%s)" % (field_type, element) return SumData(field_type, element_key, stats_dict["packets"], stats_dict["bytes"], element_key) # pylint: disable-msg=R0903 class OutputFormat: """ Holds field_type and function to extract element value. """ def __init__(self, field_type, generator): self.field_type = field_type self.generator = generator ## # The order below is important. The initial flow field depends on whether # --script or top mode is used. In top mode, the expected behavior, in_port # flow fields are shown first. A future feature will allow users to # filter output by selecting a row. Filtering by in_port is a natural # filtering starting point. # # In script mode, all fields are shown. The expectation is that users could # filter output by piping through grep. # # In top mode, the default flow field is in_port. In --script mode, # the default flow field is all. # # All is added to the end of the OUTPUT_FORMAT list. ## OUTPUT_FORMAT = [ OutputFormat("in_port", element_passthrough_get), OutputFormat("eth", element_eth_get), OutputFormat("eth_type", element_passthrough_get), OutputFormat("ipv4", element_ipv4_get), OutputFormat("ipv6", element_ipv6_get), OutputFormat("udp", element_dst_port_get), OutputFormat("tcp", element_dst_port_get), OutputFormat("tunnel", element_tunnel_get), ] ## ELEMENT_KEY = { "udp": "udp.dst", "tcp": "tcp.dst" } def top_input_get(args): """ Return subprocess stdout.""" cmd = [] if (args.host): cmd += ["ssh", args.host] cmd += ["ovs-dpctl", "dump-flows"] return subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).stdout def args_get(): """ read program parameters handle any necessary validation of input. """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__) ## # None is a special value indicating to read flows from stdin. # This handles the case # ovs-dpctl dump-flows | ovs-dpctl-flows.py parser.add_argument("-v", "--version", version="@VERSION@", action="version", help="show version") parser.add_argument("-f", "--flow-file", dest="flowFiles", default=None, action="append", help="file containing flows from ovs-dpctl dump-flow") parser.add_argument("-V", "--verbose", dest="verbose", default=logging.CRITICAL, action="store_const", const=logging.DEBUG, help="enable debug level verbosity") parser.add_argument("-s", "--script", dest="top", action="store_false", help="Run from a script (no user interface)") parser.add_argument("--host", dest="host", help="Specify a user@host for retrieving flows see" "Accessing Remote Hosts for more information") parser.add_argument("-a", "--accumulate", dest="accumulate", action="store_true", default=False, help="Accumulate dump-flow content") parser.add_argument("--accumulate-decay", dest="accumulateDecay", default=5.0 * 60, type=float, help="Decay old accumulated flows. " "The default is 5 minutes. " "A value of 0 disables decay.") parser.add_argument("-d", "--delay", dest="delay", type=int, default=1000, help="Delay in milliseconds to collect dump-flow " "content (sample rate).") args = parser.parse_args() logging.basicConfig(level=args.verbose) return args ### # Code to parse a single line in dump-flow ### # key(values) FIELDS_CMPND = re.compile("([\w]+)\((.+)\)") # key:value FIELDS_CMPND_ELEMENT = re.compile("([\w:]+)=([/\.\w:]+)") FIELDS_ELEMENT = re.compile("([\w]+):([-\.\w]+)") def flow_line_iter(line): """ iterate over flow dump elements. return tuples of (true, element) or (false, remaining element) """ # splits by , except for when in a (). Actions element was not # split properly but we don't need it. rc = [] element = "" paren_count = 0 for ch in line: if (ch == '('): paren_count += 1 elif (ch == ')'): paren_count -= 1 if (ch == ' '): # ignore white space. continue elif ((ch == ',') and (paren_count == 0)): rc.append(element) element = "" else: element += ch if (paren_count): raise ValueError(line) else: if (len(element) > 0): rc.append(element) return rc def flow_line_compound_parse(compound): """ Parse compound element for example src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03 which is in eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03) """ result = {} for element in flow_line_iter(compound): match = FIELDS_CMPND_ELEMENT.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = value match = FIELDS_CMPND.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = flow_line_compound_parse(value) continue if (len(result.keys()) == 0): return compound return result def flow_line_split(line): """ Convert a flow dump line into ([fields], [stats], actions) tuple. Where fields and stats are lists. This function relies on a the following ovs-dpctl dump-flow output characteristics: 1. The dumpe flow line consists of a list of frame fields, list of stats and action. 2. list of frame fields, each stat and action field are delimited by ', '. 3. That all other non stat field are not delimited by ', '. """ results = re.split(', ', line) (field, stats, action) = (results[0], results[1:-1], results[-1]) fields = flow_line_iter(field) return (fields, stats, action) def elements_to_dict(elements): """ Convert line to a hierarchy of dictionaries. """ result = {} for element in elements: match = FIELDS_CMPND.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = flow_line_compound_parse(value) continue match = FIELDS_ELEMENT.search(element) if (match): key = match.group(1) value = match.group(2) result[key] = value else: raise ValueError("can't parse >%s<" % element) return result # pylint: disable-msg=R0903 class SumData(object): """ Interface that all data going into SumDb must implement. Holds the flow field and its corresponding count, total packets, total bytes and calculates average. __repr__ is used as key into SumData singleton. __str__ is used as human readable output. """ def __init__(self, field_type, field, packets, flow_bytes, key): # Count is the number of lines in the dump-flow log. self.field_type = field_type self.field = field self.count = 1 self.packets = int(packets) self.bytes = int(flow_bytes) self.key = key def decrement(self, decr_packets, decr_bytes, decr_count): """ Decrement content to calculate delta from previous flow sample.""" self.packets -= decr_packets self.bytes -= decr_bytes self.count -= decr_count def __iadd__(self, other): """ Add two objects. """ if (self.key != other.key): raise ValueError("adding two unrelated types") self.count += other.count self.packets += other.packets self.bytes += other.bytes return self def __isub__(self, other): """ Decrement two objects. """ if (self.key != other.key): raise ValueError("adding two unrelated types") self.count -= other.count self.packets -= other.packets self.bytes -= other.bytes return self def __getattr__(self, name): """ Handle average. """ if (name == "average"): if (self.packets == 0): return float(0.0) else: return float(self.bytes) / float(self.packets) raise AttributeError(name) def __str__(self): """ Used for debugging. """ return "%s %s %s %s" % (self.field, self.count, self.packets, self.bytes) def __repr__(self): """ Used as key in the FlowDB table. """ return self.key def flow_aggregate(fields_dict, stats_dict): """ Search for content in a line. Passed the flow port of the dump-flows plus the current stats consisting of packets, bytes, etc """ result = [] for output_format in OUTPUT_FORMAT: field = fields_dict.get(output_format.field_type, None) if (field): obj = output_format.generator(output_format.field_type, field, stats_dict) result.append(obj) return result def flows_read(ihdl, flow_db): """ read flow content from ihdl and insert into flow_db. """ done = False while (not done): line = ihdl.readline() if (len(line) == 0): # end of input break try: flow_db.flow_line_add(line) except ValueError, arg: logging.error(arg) return flow_db def get_terminal_size(): """ return column width and height of the terminal """ for fd_io in [0, 1, 2]: try: result = struct.unpack('hh', fcntl.ioctl(fd_io, termios.TIOCGWINSZ, '1234')) except IOError: result = None continue if (result is None or result == (0, 0)): # Maybe we can't get the width. In that case assume (25, 80) result = (25, 80) return result ## # Content derived from: # http://getpython3.com/diveintopython3/your-first-python-program.html#divingin ## SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']} def approximate_size(size, a_kilobyte_is_1024_bytes=True): """Convert a file size to human-readable form. Keyword arguments: size -- file size in bytes a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024 if False, use multiples of 1000 Returns: string """ size = float(size) if size < 0: raise ValueError('number must be non-negative') if (a_kilobyte_is_1024_bytes): multiple = 1024 else: multiple = 1000 for suffix in SUFFIXES[multiple]: size /= multiple if size < multiple: return "%.1f %s" % (size, suffix) raise ValueError('number too large') ## # End copied content ## class ColMeta: """ Concepts about columns. """ def __init__(self, sortable, width): self.sortable = sortable self.width = width class RowMeta: """ How to render rows. """ def __init__(self, label, fmt): self.label = label self.fmt = fmt def fmt_packet(obj, width): """ Provide a string for packets that is appropriate for output.""" return str(obj.packets).rjust(width) def fmt_count(obj, width): """ Provide a string for average that is appropriate for output.""" return str(obj.count).rjust(width) def fmt_avg(obj, width): """ Provide a string for average that is appropriate for output.""" return str(int(obj.average)).rjust(width) def fmt_field(obj, width): """ truncate really long flow and insert ellipses to help make it clear. """ ellipses = " ... " value = obj.field if (len(obj.field) > width): value = value[:(width - len(ellipses))] + ellipses return value.ljust(width) def fmt_bytes(obj, width): """ Provide a string for average that is appropriate for output.""" if (len(str(obj.bytes)) <= width): value = str(obj.bytes) else: value = approximate_size(obj.bytes) return value.rjust(width) def title_center(value, width): """ Center a column title.""" return value.upper().center(width) def title_rjust(value, width): """ Right justify a column title. """ return value.upper().rjust(width) def column_picker(order, obj): """ return the column as specified by order. """ if (order == 1): return obj.count elif (order == 2): return obj.packets elif (order == 3): return obj.bytes elif (order == 4): return obj.average else: raise ValueError("order outside of range %s" % order) class Render: """ Renders flow data. The two FIELD_SELECT variables should be set to the actual field minus 1. During construction, an internal method increments and initializes this object. """ FLOW_FIELDS = [_field.field_type for _field in OUTPUT_FORMAT] + ["all"] FIELD_SELECT_SCRIPT = 7 FIELD_SELECT_TOP = -1 def __init__(self, console_width, field_select): """ Calculate column widths taking into account changes in format.""" self._start_time = datetime.datetime.now() self._cols = [ColMeta(False, 0), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH), ColMeta(True, Columns.VALUE_WIDTH)] self._console_width = console_width self.console_width_set(console_width) # Order in this array dictate the order of the columns. # The 0 width for the first entry is a place holder. This is # dynamically calculated. The first column is special. We need a # way to indicate which field are presented. self._descs = [RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust), RowMeta("", title_rjust)] self._column_sort_select = 0 self.column_select_event() self._titles = [ RowMeta(Columns.FIELDS, title_center), RowMeta(Columns.COUNT, title_rjust), RowMeta(Columns.PACKETS, title_rjust), RowMeta(Columns.BYTES, title_rjust), RowMeta(Columns.AVERAGE, title_rjust) ] self._datas = [ RowMeta(None, fmt_field), RowMeta(None, fmt_count), RowMeta(None, fmt_packet), RowMeta(None, fmt_bytes), RowMeta(None, fmt_avg) ] ## # _field_types hold which fields are displayed in the field # column, with the keyword all implying all fields. ## self._field_types = Render.FLOW_FIELDS ## # The default is to show all field types. ## self._field_type_select = field_select self.field_type_toggle() def _field_type_select_get(self): """ Return which field type to display. """ return self._field_types[self._field_type_select] def field_type_toggle(self): """ toggle which field types to show. """ self._field_type_select += 1 if (self._field_type_select >= len(self._field_types)): self._field_type_select = 0 value = Columns.FIELDS + " (%s)" % self._field_type_select_get() self._titles[0].label = value def column_select_event(self): """ Handles column select toggle. """ self._descs[self._column_sort_select].label = "" for _ in range(len(self._cols)): self._column_sort_select += 1 if (self._column_sort_select >= len(self._cols)): self._column_sort_select = 0 # Now look for the next sortable column if (self._cols[self._column_sort_select].sortable): break self._descs[self._column_sort_select].label = "DESC" def console_width_set(self, console_width): """ Adjust the output given the new console_width. """ self._console_width = console_width spaces = len(self._cols) - 1 ## # Calculating column width can be tedious but important. The # flow field value can be long. The goal here is to dedicate # fixed column space for packets, bytes, average and counts. Give the # remaining space to the flow column. When numbers get large # transition output to output generated by approximate_size which # limits output to ###.# XiB in other words 9 characters. ## # At this point, we know the maximum length values. We may # truncate the flow column to get everything to fit. self._cols[0].width = 0 values_max_length = sum([ii.width for ii in self._cols]) + spaces flow_max_length = console_width - values_max_length self._cols[0].width = flow_max_length def format(self, flow_db): """ shows flows based on --script parameter.""" rc = [] ## # Top output consists of # Title # Column title (2 rows) # data # statistics and status ## # Title ## rc.append("Flow Summary".center(self._console_width)) stats = " Total: %(flow_total)s errors: %(flow_errors)s " % \ flow_db.flow_stats_get() accumulate = flow_db.accumulate_get() if (accumulate): stats += "Accumulate: on " else: stats += "Accumulate: off " duration = datetime.datetime.now() - self._start_time stats += "Duration: %s " % str(duration) rc.append(stats.ljust(self._console_width)) ## # 2 rows for columns. ## # Indicate which column is in descending order. rc.append(" ".join([ii.fmt(ii.label, col.width) for (ii, col) in zip(self._descs, self._cols)])) rc.append(" ".join([ii.fmt(ii.label, col.width) for (ii, col) in zip(self._titles, self._cols)])) ## # Data. ## for dd in flow_db.field_values_in_order(self._field_type_select_get(), self._column_sort_select): rc.append(" ".join([ii.fmt(dd, col.width) for (ii, col) in zip(self._datas, self._cols)])) return rc def curses_screen_begin(): """ begin curses screen control. """ stdscr = curses.initscr() curses.cbreak() curses.noecho() stdscr.keypad(1) return stdscr def curses_screen_end(stdscr): """ end curses screen control. """ curses.nocbreak() stdscr.keypad(0) curses.echo() curses.endwin() class FlowDB: """ Implements live vs accumulate mode. Flows are stored as key value pairs. The key consists of the content prior to stat fields. The value portion consists of stats in a dictionary form. @ \todo future add filtering here. """ def __init__(self, accumulate): self._accumulate = accumulate self._error_count = 0 # Values are (stats, last update time.) # The last update time is used for aging. self._flow_lock = threading.Lock() # This dictionary holds individual flows. self._flows = {} # This dictionary holds aggregate of flow fields. self._fields = {} def accumulate_get(self): """ Return the current accumulate state. """ return self._accumulate def accumulate_toggle(self): """ toggle accumulate flow behavior. """ self._accumulate = not self._accumulate def begin(self): """ Indicate the beginning of processing flow content. if accumulate is false clear current set of flows. """ if (not self._accumulate): self._flow_lock.acquire() try: self._flows.clear() finally: self._flow_lock.release() self._fields.clear() def flow_line_add(self, line): """ Split a line from a ovs-dpctl dump-flow into key and stats. The order of the content in the flow should be: - flow content - stats for the flow - actions This method also assumes that the dump flow output does not change order of fields of the same flow. """ line = line.rstrip("\n") (fields, stats, _) = flow_line_split(line) try: fields_dict = elements_to_dict(fields) if (len(fields_dict) == 0): raise ValueError("flow fields are missing %s", line) stats_dict = elements_to_dict(stats) if (len(stats_dict) == 0): raise ValueError("statistics are missing %s.", line) ## # In accumulate mode, the Flow database can reach 10,000's of # persistent flows. The interaction of the script with this many # flows is too slow. Instead, delta are sent to the flow_db # database allow incremental changes to be done in O(m) time # where m is the current flow list, instead of iterating over # all flows in O(n) time where n is the entire history of flows. key = ",".join(fields) self._flow_lock.acquire() try: (stats_old_dict, _) = self._flows.get(key, (None, None)) finally: self._flow_lock.release() self.flow_event(fields_dict, stats_old_dict, stats_dict) except ValueError, arg: logging.error(arg) self._error_count += 1 raise self._flow_lock.acquire() try: self._flows[key] = (stats_dict, datetime.datetime.now()) finally: self._flow_lock.release() def decay(self, decayTimeInSeconds): """ Decay content. """ now = datetime.datetime.now() for (key, value) in self._flows.items(): (stats_dict, updateTime) = value delta = now - updateTime if (delta.seconds > decayTimeInSeconds): self._flow_lock.acquire() try: del self._flows[key] fields_dict = elements_to_dict(flow_line_iter(key)) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: self.field_dec(match) finally: self._flow_lock.release() def flow_stats_get(self): """ Return statistics in a form of a dictionary. """ rc = None self._flow_lock.acquire() try: rc = {"flow_total": len(self._flows), "flow_errors": self._error_count} finally: self._flow_lock.release() return rc def field_types_get(self): """ Return the set of types stored in the singleton. """ types = set((ii.field_type for ii in self._fields.values())) return types def field_add(self, data): """ Collect dump-flow data to sum number of times item appears. """ current = self._fields.get(repr(data), None) if (current is None): current = copy.copy(data) else: current += data self._fields[repr(current)] = current def field_dec(self, data): """ Collect dump-flow data to sum number of times item appears. """ current = self._fields.get(repr(data), None) if (current is None): raise ValueError("decrementing field missing %s" % repr(data)) current -= data self._fields[repr(current)] = current if (current.count == 0): del self._fields[repr(current)] def field_values_in_order(self, field_type_select, column_order): """ Return a list of items in order maximum first. """ values = self._fields.values() if (field_type_select != "all"): # If a field type other than "all" then reduce the list. values = [ii for ii in values if (ii.field_type == field_type_select)] values = [(column_picker(column_order, ii), ii) for ii in values] values.sort(key=operator.itemgetter(0)) values.reverse() values = [ii[1] for ii in values] return values def flow_event(self, fields_dict, stats_old_dict, stats_new_dict): """ Receives new flow information. """ # In order to avoid processing every flow at every sample # period, changes in flow packet count is used to determine the # delta in the flow statistics. This delta is used in the call # to self.decrement prior to self.field_add if (stats_old_dict is None): # This is a new flow matches = flow_aggregate(fields_dict, stats_new_dict) for match in matches: self.field_add(match) else: old_packets = int(stats_old_dict.get("packets", 0)) new_packets = int(stats_new_dict.get("packets", 0)) if (old_packets == new_packets): # ignore. same data. pass else: old_bytes = stats_old_dict.get("bytes", 0) # old_packets != new_packets # if old_packets > new_packets then we end up decrementing # packets and bytes. matches = flow_aggregate(fields_dict, stats_new_dict) for match in matches: match.decrement(int(old_packets), int(old_bytes), 1) self.field_add(match) class DecayThread(threading.Thread): """ Periodically call flow database to see if any flows are old. """ def __init__(self, flow_db, interval): """ Start decay thread. """ threading.Thread.__init__(self) self._interval = max(1, interval) self._min_interval = min(1, interval / 10) self._flow_db = flow_db self._event = threading.Event() self._running = True self.daemon = True def run(self): """ Worker thread which handles decaying accumulated flows. """ while(self._running): self._event.wait(self._min_interval) if (self._running): self._flow_db.decay(self._interval) def stop(self): """ Stop thread. """ self._running = False self._event.set() ## # Give the calling thread time to terminate but not too long. # this thread is a daemon so the application will terminate if # we timeout during the join. This is just a cleaner way to # release resources. self.join(2.0) def flow_top_command(stdscr, render, flow_db): """ Handle input while in top mode. """ ch = stdscr.getch() ## # Any character will restart sampling. if (ch == ord('h')): # halt output. ch = stdscr.getch() while (ch == -1): ch = stdscr.getch() if (ch == ord('s')): # toggle which column sorts data in descending order. render.column_select_event() elif (ch == ord('a')): flow_db.accumulate_toggle() elif (ch == ord('f')): render.field_type_toggle() elif (ch == ord(' ')): # resample pass return ch def decay_timer_start(flow_db, accumulateDecay): """ If accumulateDecay greater than zero then start timer. """ if (accumulateDecay > 0): decay_timer = DecayThread(flow_db, accumulateDecay) decay_timer.start() return decay_timer else: return None def flows_top(args): """ handles top like behavior when --script is not specified. """ flow_db = FlowDB(args.accumulate) render = Render(0, Render.FIELD_SELECT_TOP) decay_timer = decay_timer_start(flow_db, args.accumulateDecay) lines = [] try: stdscr = curses_screen_begin() try: ch = 'X' #stdscr.nodelay(1) stdscr.timeout(args.delay) while (ch != ord('q')): flow_db.begin() try: ihdl = top_input_get(args) try: flows_read(ihdl, flow_db) finally: ihdl.close() except OSError, arg: logging.critical(arg) break (console_height, console_width) = stdscr.getmaxyx() render.console_width_set(console_width) output_height = console_height - 1 line_count = range(output_height) line_output = render.format(flow_db) lines = zip(line_count, line_output[:output_height]) stdscr.erase() for (count, line) in lines: stdscr.addstr(count, 0, line[:console_width]) stdscr.refresh() ch = flow_top_command(stdscr, render, flow_db) finally: curses_screen_end(stdscr) except KeyboardInterrupt: pass if (decay_timer): decay_timer.stop() # repeat output for (count, line) in lines: print line def flows_script(args): """ handles --script option. """ flow_db = FlowDB(args.accumulate) flow_db.begin() if (args.flowFiles is None): logging.info("reading flows from stdin") ihdl = os.fdopen(sys.stdin.fileno(), 'r', 0) try: flow_db = flows_read(ihdl, flow_db) finally: ihdl.close() else: for flowFile in args.flowFiles: logging.info("reading flows from %s", flowFile) ihdl = open(flowFile, "r") try: flow_db = flows_read(ihdl, flow_db) finally: ihdl.close() (_, console_width) = get_terminal_size() render = Render(console_width, Render.FIELD_SELECT_SCRIPT) for line in render.format(flow_db): print line def main(): """ Return 0 on success or 1 on failure. Algorithm There are four stages to the process ovs-dpctl dump-flow content. 1. Retrieve current input 2. store in FlowDB and maintain history 3. Iterate over FlowDB and aggregating stats for each flow field 4. present data. Retrieving current input is currently trivial, the ovs-dpctl dump-flow is called. Future version will have more elaborate means for collecting dump-flow content. FlowDB returns all data as in the form of a hierarchical dictionary. Input will vary. In the case of accumulate mode, flows are not purged from the FlowDB manager. Instead at the very least, merely the latest statistics are kept. In the case, of live output the FlowDB is purged prior to sampling data. Aggregating results requires identify flow fields to aggregate out of the flow and summing stats. """ args = args_get() try: if (args.top): flows_top(args) else: flows_script(args) except KeyboardInterrupt: return 1 return 0 if __name__ == '__main__': sys.exit(main()) elif __name__ == 'ovs-dpctl-top': # pylint: disable-msg=R0915 ## # Test case beyond this point. # pylint: disable-msg=R0904 class TestsuiteFlowParse(unittest.TestCase): """ parse flow into hierarchy of dictionaries. """ def test_flow_parse(self): """ test_flow_parse. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:1, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) flow_dict = elements_to_dict(fields + stats) self.assertEqual(flow_dict["eth"]["src"], "00:50:56:b4:4e:f8") self.assertEqual(flow_dict["eth"]["dst"], "33:33:00:01:00:03") self.assertEqual(flow_dict["ipv6"]["src"], "fe80::55bf:fe42:bc96:2812") self.assertEqual(flow_dict["ipv6"]["dst"], "ff02::1:3") self.assertEqual(flow_dict["packets"], "1") self.assertEqual(flow_dict["bytes"], "92") line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:1, bytes:92, "\ "used:-0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) flow_dict = elements_to_dict(fields + stats) self.assertEqual(flow_dict["used"], "-0.703s") self.assertEqual(flow_dict["packets"], "1") self.assertEqual(flow_dict["bytes"], "92") def test_flow_sum(self): """ test_flow_sum. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, _) = flow_line_split(line) stats_dict = elements_to_dict(stats) fields_dict = elements_to_dict(fields) ## # Test simple case of one line. flow_db = FlowDB(False) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) flow_types = flow_db.field_types_get() expected_flow_types = ["eth", "eth_type", "udp", "in_port", "ipv6"] self.assert_(len(flow_types) == len(expected_flow_types)) for flow_type in flow_types: self.assertTrue(flow_type in expected_flow_types) for flow_type in flow_types: sum_value = flow_db.field_values_in_order("all", 1) self.assert_(len(sum_value) == 5) self.assert_(sum_value[0].packets == 2) self.assert_(sum_value[0].count == 1) self.assert_(sum_value[0].bytes == 92) ## # Add line again just to see counts go up. matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) flow_types = flow_db.field_types_get() self.assert_(len(flow_types) == len(expected_flow_types)) for flow_type in flow_types: self.assertTrue(flow_type in expected_flow_types) for flow_type in flow_types: sum_value = flow_db.field_values_in_order("all", 1) self.assert_(len(sum_value) == 5) self.assert_(sum_value[0].packets == 4) self.assert_(sum_value[0].count == 2) self.assert_(sum_value[0].bytes == 2 * 92) def test_assoc_list(self): """ test_assoc_list. """ line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" valid_flows = [ 'eth_type(0x86dd)', 'udp(dst=5355)', 'in_port(4)', 'ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3)', 'eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)' ] (fields, stats, _) = flow_line_split(line) stats_dict = elements_to_dict(stats) fields_dict = elements_to_dict(fields) ## # Test simple case of one line. flow_db = FlowDB(False) matches = flow_aggregate(fields_dict, stats_dict) for match in matches: flow_db.field_add(match) for sum_value in flow_db.field_values_in_order("all", 1): assoc_list = Columns.assoc_list(sum_value) for item in assoc_list: if (item[0] == "fields"): self.assertTrue(item[1] in valid_flows) elif (item[0] == "packets"): self.assertTrue(item[1] == 2) elif (item[0] == "count"): self.assertTrue(item[1] == 1) elif (item[0] == "average"): self.assertTrue(item[1] == 46.0) elif (item[0] == "bytes"): self.assertTrue(item[1] == 92) else: raise ValueError("unknown %s", item[0]) def test_human_format(self): """ test_assoc_list. """ self.assertEqual(approximate_size(0.0), "0.0 KiB") self.assertEqual(approximate_size(1024), "1.0 KiB") self.assertEqual(approximate_size(1024 * 1024), "1.0 MiB") self.assertEqual(approximate_size((1024 * 1024) + 100000), "1.1 MiB") value = (1024 * 1024 * 1024) + 100000000 self.assertEqual(approximate_size(value), "1.1 GiB") def test_flow_line_split(self): """ Splitting a flow line is not trivial. There is no clear delimiter. Comma is used liberally.""" expected_fields = ["in_port(4)", "eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)", "eth_type(0x86dd)", "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3," "label=0,proto=17,tclass=0,hlimit=1,frag=no)", "udp(src=61252,dst=5355)"] expected_stats = ["packets:2", "bytes:92", "used:0.703s"] expected_actions = "actions:3,8,11,14,17,20,23,26,29,32,35," \ "38,41,44,47,50,53,56,59,62,65" line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\ "dst=33:33:00:01:00:03),eth_type(0x86dd),"\ "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\ "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\ "udp(src=61252,dst=5355), packets:2, bytes:92, "\ "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\ "38,41,44,47,50,53,56,59,62,65" (fields, stats, actions) = flow_line_split(line) self.assertEqual(fields, expected_fields) self.assertEqual(stats, expected_stats) self.assertEqual(actions, expected_actions) def test_accumulate_decay(self): """ test_accumulate_decay: test accumulated decay. """ lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), " "packets:1, bytes:120, used:0.004s, actions:1"] flow_db = FlowDB(True) flow_db.begin() flow_db.flow_line_add(lines[0]) # Make sure we decay time.sleep(4) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.decay(1) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) flow_db.flow_line_add(lines[0]) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.decay(30) # Should not be deleted. self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) flow_db.flow_line_add(lines[0]) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1) timer = decay_timer_start(flow_db, 2) time.sleep(10) self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0) timer.stop() def test_accumulate(self): """ test_accumulate test that FlowDB supports accumulate. """ lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), " "packets:1, bytes:120, used:0.004s, actions:1", "in_port(2)," "eth(src=68:ef:bd:25:ef:c0,dst=33:33:00:00:00:66)," "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029), " "packets:2, bytes:5026, used:0.348s, actions:1", "in_port(1),eth(src=ee:ee:ee:ee:ee:ee," "dst=ff:ff:ff:ff:ff:ff)," "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255," "tip=10.24.104.230/255.255.255.255,op=1/0xff," "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00," "tha=00:00:00:00:00:00/00:00:00:00:00:00), packets:2, " "bytes:240, used:0.004s, actions:1"] lines = [ "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", "in_port(2),eth_type(0x0806), packets:2, bytes:126, actions:1", "in_port(1),eth_type(0x0806), packets:2, bytes:240, actions:1", "in_port(1),eth_type(0x0800), packets:1, bytes:120, actions:1", "in_port(1),eth_type(0x0800), packets:2, bytes:240, actions:1", "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1", ] # Turn on accumulate. flow_db = FlowDB(True) flow_db.begin() flow_db.flow_line_add(lines[0]) # Test one flow exist. sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 1) self.assertEqual(in_ports[0].bytes, 120) self.assertEqual(in_ports[0].count, 1) # simulate another sample # Test two different flows exist. flow_db.begin() flow_db.flow_line_add(lines[1]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 1) self.assertEqual(in_ports[0].bytes, 120) self.assertEqual(in_ports[0].count, 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Test first flow increments packets. flow_db.begin() flow_db.flow_line_add(lines[2]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 240) self.assertEqual(in_ports[0].count, 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Test third flow but with the same in_port(1) as the first flow. flow_db.begin() flow_db.flow_line_add(lines[3]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 3) self.assertEqual(in_ports[0].bytes, 360) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # Third flow has changes. flow_db.begin() flow_db.flow_line_add(lines[4]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 4) self.assertEqual(in_ports[0].bytes, 480) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) # First flow reset. flow_db.begin() flow_db.flow_line_add(lines[5]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 3) self.assertEqual(in_ports[0].bytes, 360) self.assertEqual(in_ports[0].count, 2) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 2) self.assertEqual(in_ports[0].bytes, 126) self.assertEqual(in_ports[0].count, 1) def test_parse_character_errors(self): """ test_parsing errors. The flow parses is purposely loose. Its not designed to validate input. Merely pull out what it can but there are situations that a parse error can be detected. """ lines = ["complete garbage", "in_port(2),eth(src=68:ef:bd:25:ef:c0," "dst=33:33:00:00:00:66)," "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::," "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0," "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029)," "packets:2,bytes:5026,actions:1"] flow_db = FlowDB(False) flow_db.begin() for line in lines: try: flow_db.flow_line_add(line) except ValueError: # We want an exception. That is how we know we have # correctly found a simple parsing error. We are not # looking to validate flow output just catch simple issues. continue self.assertTrue(False) def test_tunnel_parsing(self): """ test_tunnel_parsing test parse flows with tunnel. """ lines = [ "tunnel(tun_id=0x0,src=192.168.1.1,dst=192.168.1.10," "tos=0x0,ttl=64,flags(key)),in_port(1)," "eth(src=9e:40:f5:ef:ec:ee,dst=01:23:20:00:00:30)," "eth_type(0x8902), packets:6, bytes:534, used:0.128s, " "actions:userspace(pid=4294962691,slow_path(cfm))" ] flow_db = FlowDB(False) flow_db.begin() flow_db.flow_line_add(lines[0]) sum_values = flow_db.field_values_in_order("all", 1) in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")] self.assertEqual(len(in_ports), 1) self.assertEqual(in_ports[0].packets, 6) self.assertEqual(in_ports[0].bytes, 534) self.assertEqual(in_ports[0].count, 1) def test_flow_multiple_paren(self): """ test_flow_multiple_paren. """ line = "tunnel(tun_id=0x0,src=192.168.1.1,flags(key)),in_port(2)" valid = ["tunnel(tun_id=0x0,src=192.168.1.1,flags(key))", "in_port(2)"] rc = flow_line_iter(line) self.assertEqual(valid, rc) def test_to_network(self): """ test_to_network test ipv4_to_network and ipv6_to_network. """ ipv4s = [ ("192.168.0.1", "192.168.0.1"), ("192.168.0.1/255.255.255.255", "192.168.0.1"), ("192.168.0.1/255.255.255.0", "192.168.0.0"), ("192.168.0.1/255.255.0.0", "192.168.0.0"), ("192.168.0.1/255.0.0.0", "192.0.0.0"), ("192.168.0.1/0.0.0.0", "0.0.0.0"), ("10.24.106.230/255.255.255.255", "10.24.106.230"), ("10.24.106.230/255.255.255.0", "10.24.106.0"), ("10.24.106.0/255.255.255.0", "10.24.106.0"), ("10.24.106.0/255.255.252.0", "10.24.104.0") ] ipv6s = [ ("1::192:168:0:1", "1::192:168:0:1"), ("1::192:168:0:1/1::ffff:ffff:ffff:ffff", "1::192:168:0:1"), ("1::192:168:0:1/1::ffff:ffff:ffff:0", "1::192:168:0:0"), ("1::192:168:0:1/1::ffff:ffff:0:0", "1::192:168:0:0"), ("1::192:168:0:1/1::ffff:0:0:0", "1::192:0:0:0"), ("1::192:168:0:1/1::0:0:0:0", "1::"), ("1::192:168:0:1/::", "::") ] for (ipv4_test, ipv4_check) in ipv4s: self.assertEqual(ipv4_to_network(ipv4_test), ipv4_check) for (ipv6_test, ipv6_check) in ipv6s: self.assertEqual(ipv6_to_network(ipv6_test), ipv6_check) def test_ui(self): """ test_ui: test expected ui behavior. """ #pylint: disable=W0212 top_render = Render(80, Render.FIELD_SELECT_TOP) script_render = Render(80, Render.FIELD_SELECT_SCRIPT) self.assertEqual(top_render._field_type_select_get(), "in_port") self.assertEqual(script_render._field_type_select_get(), "all") #pylint: enable=W0212 openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-tcpundump.1.in0000644000000000000000000000013213534540071021172 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.769845965 openvswitch-2.5.9/utilities/ovs-tcpundump.1.in0000644000175000017500000000156013534540071022662 0ustar00jpettitjpettit00000000000000.TH ovs\-tcpundump 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME ovs\-tcpundump \- convert ``tcpdump \-xx'' output to hex strings . .SH SYNOPSIS \fBovs\-tcpundump < \fIfile\fR .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-tcpundump\fR program reads \fBtcpdump \-xx\fR output on stdin, looking for hexadecimal packet data, and dumps each Ethernet as a single hexadecimal string on stdout. This format is suitable for use with the \fBofproto/trace\fR command supported by \fBovs\-vswitchd\fR(8) via \fBovs\-appctl\fR(8). .PP At least two \fB\-x\fR or \fB\-X\fR options must be given, otherwise the output will omit the Ethernet header, which prevents the output from being using with \fBofproto/trace\fR. . .SH "OPTIONS" .so lib/common.man . .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-vswitchd (8), .BR ovs\-pcap (1), .BR tcpdump (8), .BR wireshark (8). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-tcpundump.in0000644000000000000000000000013213534540071021033 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.821846349 openvswitch-2.5.9/utilities/ovs-tcpundump.in0000755000175000017500000000415413534540071022530 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Copyright (c) 2010 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import getopt import re import sys argv0 = sys.argv[0] def usage(): print """\ %(argv0)s: print "tcpdump -xx" output as hex usage: %(argv0)s < FILE where FILE is output from "tcpdump -xx". The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['help', 'version']) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print "ovs-tcpundump (Open vSwitch) @VERSION@" else: sys.exit(0) if len(args) != 0: sys.stderr.write("%s: non-option argument not supported " "(use --help for help)\n" % argv0) sys.exit(1) packet = '' regex = re.compile(r'^\s+0x([0-9a-fA-F]+): ((?: [0-9a-fA-F]{4})+)') while True: line = sys.stdin.readline() if line == "": break m = regex.match(line) if m is None or int(m.group(1), 16) == 0: if packet != '': print packet packet = '' if m: packet += re.sub(r'\s', '', m.group(2), 0) if packet != '': print packet # Local variables: # mode: python # End: openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-appctl-bashcomp.bash0000644000000000000000000000013213534540071022400 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.805846231 openvswitch-2.5.9/utilities/ovs-appctl-bashcomp.bash0000755000175000017500000004265613534540071024106 0ustar00jpettitjpettit00000000000000# # A bash command completion script for ovs-appctl. # # # Right now, the script can do the following: # # - display available completion or complete on unfinished user input # (long option, subcommand, and argument). # # - once the subcommand (e.g. ofproto/trace) has been given, the # script will print the subcommand format. # # - the script can convert between keywords like 'bridge/port/interface/dp' # and the available record in ovsdb. # # The limitation are: # # - only support small set of important keywords # (dp, datapath, bridge, switch, port, interface, iface). # # - does not support parsing of nested option # (e.g. ovsdb-tool create [db [schema]]). # # - does not support expansion on repeatitive argument # (e.g. ovs-dpctl show [dp...]). # # - only support matching on long options, and only in the format # (--option [arg], i.e. should not use --option=[arg]). # # # # Keywords # ======== # # # # Expandable keywords. _KWORDS=(bridge switch port interface iface dp_name dp) # Command name. _COMMAND= # Printf enabler. _PRINTF_ENABLE= # Bash prompt. _BASH_PROMPT= # Output to the compgen. _COMP_WORDLIST= # # For ovs-appctl command only. # # Target in the current completion, default ovs-vswitchd. _APPCTL_TARGET= # Possible targets. _POSSIBLE_TARGETS="ovs-vswitchd ovsdb-server ovs-ofctl" # Command Extraction # ================== # # # # Extracts all subcommands of 'command'. # If fails, returns nothing. extract_subcmds() { local command=$_COMMAND local target= local subcmds error if [ -n "$_APPCTL_TARGET" ]; then target="--target $_APPCTL_TARGET" fi subcmds="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \ | cut -d ' ' -f1)" || error="TRUE" if [ -z "$error" ]; then echo "$subcmds" fi } # Extracts all long options of ovs-appctl. # If fails, returns nothing. extract_options() { local command=$_COMMAND local options error options="$($command --option 2>/dev/null | sort | sed -n '/^--.*/p' | cut -d '=' -f1)" \ || error="TRUE" if [ -z "$error" ]; then echo "$options" fi } # Returns the option format, if the option asks for an argument. # If fails, returns nothing. option_require_arg() { local command=$_COMMAND local option=$1 local require_arg error require_arg="$($command --option | sort | sed -n '/^--.*/p' | grep -- "$option" | grep -- "=")" \ || error="TRUE" if [ -z "$error" ]; then echo "$require_arg" fi } # Combination Discovery # ===================== # # # # Given the subcommand formats, finds all possible completions # at current completion level. find_possible_comps() { local combs="$@" local comps= local line while read line; do local arg= for arg in $line; do # If it is an optional argument, gets all completions, # and continues. if [ -n "$(sed -n '/^\[.*\]$/p' <<< "$arg")" ]; then local opt_arg="$(sed -e 's/^\[\(.*\)\]$/\1/' <<< "$arg")" local opt_args=() IFS='|' read -a opt_args <<< "$opt_arg" comps="${opt_args[@]} $comps" # If it is in format "\[*", it is a start of nested # option, do not parse. elif [ -n "$(sed -n "/^\[.*$/p" <<< "$arg")" ]; then break; # If it is a compulsory argument, adds it to the comps # and break, since all following args are for next stage. else local args=() IFS='|' read -a args <<< "$arg" comps="${args[@]} $comps" break; fi done done <<< "$combs" echo "$comps" } # Given the subcommand format, and the current command line input, # finds keywords of all possible completions. subcmd_find_keyword_based_on_input() { local format="$1" local cmd_line=($2) local mult= local combs= local comps= local arg line # finds all combinations by searching for '{}'. # there should only be one '{}', otherwise, the # command format should be changed to multiple commands. mult="$(sed -n 's/^.*{\(.*\)}.*$/ \1/p' <<< "$format" | tr '|' '\n' | cut -c1-)" if [ -n "$mult" ]; then while read line; do local tmp= tmp="$(sed -e "s@{\(.*\)}@$line@" <<< "$format")" combs="$combs@$tmp" done <<< "$mult" combs="$(tr '@' '\n' <<< "$combs")" else combs="$format" fi # Now, starts from the first argument, narrows down the # subcommand format combinations. for arg in "${subcmd_line[@]}"; do local kword possible_comps # Finds next level possible comps. possible_comps=$(find_possible_comps "$combs") # Finds the kword. kword="$(arg_to_kwords "$arg" "$possible_comps")" # Returns if could not find 'kword' if [ -z "$kword" ]; then return fi # Trims the 'combs', keeps context only after 'kword'. if [ -n "$combs" ]; then combs="$(sed -n "s@^.*\[\{0,1\}$kword|\{0,1\}[a-z_]*\]\{0,1\} @@p" <<< "$combs")" fi done comps="$(find_possible_comps "$combs")" echo "$comps" } # Helper # ====== # # # # Prints the input to stderr. $_PRINTF_ENABLE must be filled. printf_stderr() { local stderr_out="$@" if [ -n "$_PRINTF_ENABLE" ]; then printf "\n$stderr_out" 1>&2 fi } # Extracts the bash prompt PS1, outputs it with the input argument # via 'printf_stderr'. # # Original idea inspired by: # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2 # # The code below is taken from Peter Amidon. His change makes it more # robust. extract_bash_prompt() { local myPS1 v myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")" v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')" v="${v##*# Begin prompt}" _BASH_PROMPT="$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')" } # Keyword Conversion # ================== # # # # All completion functions. complete_bridge () { local result error result=$(ovs-vsctl list-br 2>/dev/null | grep -- "^$1") || error="TRUE" if [ -z "$error" ]; then echo "${result}" fi } complete_port () { local ports result error local all_ports all_ports=$(ovs-vsctl --format=table \ --no-headings \ --columns=name \ list Port 2>/dev/null) || error="TRUE" ports=$(printf "$all_ports" | sort | tr -d '"' | uniq -u) result=$(grep -- "^$1" <<< "$ports") if [ -z "$error" ]; then echo "${result}" fi } complete_iface () { local bridge bridges result error bridges=$(ovs-vsctl list-br 2>/dev/null) || error="TRUE" for bridge in $bridges; do local ifaces ifaces=$(ovs-vsctl list-ifaces "${bridge}" 2>/dev/null) || error="TRUE" result="${result} ${ifaces}" done if [ -z "$error" ]; then echo "${result}" fi } complete_dp () { local dps result error dps=$(ovs-appctl dpctl/dump-dps 2>/dev/null | cut -d '@' -f2) || error="TRUE" result=$(grep -- "^$1" <<< "$dps") if [ -z "$error" ]; then echo "${result}" fi } # Converts the argument (e.g. bridge/port/interface/dp name) to # the corresponding keywords. # Returns empty string if could not map the arg to any keyword. arg_to_kwords() { local arg="$1" local possible_kwords=($2) local non_parsables=() local match= local kword for kword in ${possible_kwords[@]}; do case "$kword" in bridge|switch) match="$(complete_bridge "$arg")" ;; port) match="$(complete_port "$arg")" ;; interface|iface) match="$(complete_iface "$arg")" ;; dp_name|dp) match="$(complete_dp "$arg")" ;; *) if [ "$arg" = "$kword" ]; then match="$kword" else non_parsables+=("$kword") continue fi ;; esac if [ -n "$match" ]; then echo "$kword" return fi done # If there is only one non-parsable kword, # just assumes the user input it. if [ "${#non_parsables[@]}" -eq "1" ]; then echo "$non_parsables" return fi } # Expands the keywords to the corresponding instance names. kwords_to_args() { local possible_kwords=($@) local args=() local printf_expand_once= local kword for kword in ${possible_kwords[@]}; do local match= case "${kword}" in bridge|switch) match="$(complete_bridge "")" ;; port) match="$(complete_port "")" ;; interface|iface) match="$(complete_iface "")" ;; dp_name|dp) match="$(complete_dp "")" ;; -*) # Treats option as kword as well. match="$kword" ;; *) match= ;; esac match=$(echo "$match" | tr '\n' ' ' | tr -s ' ' | sed -e 's/^[ \t]*//') args+=( $match ) if [ -n "$_PRINTF_ENABLE" ]; then local output_stderr= if [ -z "$printf_expand_once" ]; then printf_expand_once="once" printf -v output_stderr "\nArgument expansion:\n" fi printf -v output_stderr "$output_stderr available completions \ for keyword \"%s\": %s " "$kword" "$match" printf_stderr "$output_stderr" fi done echo "${args[@]}" } # Parse and Compgen # ================= # # # # This function takes the current command line arguments as input, # finds the command format and returns the possible completions. parse_and_compgen() { local command=$_COMMAND local subcmd_line=($@) local subcmd=${subcmd_line[0]} local target= local subcmd_format= local comp_keywords= local comp_wordlist= if [ -n "$_APPCTL_TARGET" ]; then target="--target $_APPCTL_TARGET" fi # Extracts the subcommand format. subcmd_format="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \ | awk -v opt=$subcmd '$1 == opt {print $0}' | tr -s ' ' )" # Finds the possible completions based on input argument. comp_keyword="$(subcmd_find_keyword_based_on_input "$subcmd_format" \ "${subcmd_line[@]}")" # Prints subcommand format and expands the keywords if 'comp_keyword' # is not empty. if [ -n "$comp_keyword" ]; then printf_stderr "$(printf "\nCommand format:\n%s" "$subcmd_format")" comp_wordlist="$(kwords_to_args "$comp_keyword")" # If there is no expanded completions, returns "NO_EXPAN" to # distinguish from the case of no available completions. if [ -z "$comp_wordlist" ]; then echo "NO_EXPAN" else echo "$comp_wordlist" fi fi } # Compgen Helper # ============== # # # # Takes the current command line arguments and returns the possible # completions. # # At the beginning, the options are checked and completed. For ovs-appctl # completion, The function looks for the --target option which gives the # target daemon name. If it is not provided, by default, 'ovs-vswitchd' # is used. # # Then, tries to locate and complete the subcommand. If the subcommand # is provided, the following arguments are passed to the 'parse_and_compgen' # function to figure out the corresponding completion of the subcommand. # # Returns the completion arguments on success. ovs_comp_helper() { local cmd_line_so_far=($@) local comp_wordlist _subcmd options i local j=-1 # Parse the command-line args till we find the subcommand. for i in "${!cmd_line_so_far[@]}"; do # if $i is not greater than $j, it means the previous iteration # skips not-visited args. so, do nothing and catch up. if [ $i -le $j ]; then continue; fi j=$i if [[ "${cmd_line_so_far[i]}" =~ ^--* ]]; then # If --target is found, locate the target daemon. # Else, it is an option command, fill the comp_wordlist with # all options. if [ "$_COMMAND" = "ovs-appctl" ] \ && [[ "${cmd_line_so_far[i]}" =~ ^--target$ ]]; then _APPCTL_TARGET="ovs-vswitchd" if [ -n "${cmd_line_so_far[j+1]}" ]; then local daemon for daemon in $_POSSIBLE_TARGETS; do # Greps "$daemon" in argument, since the argument may # be the path to the pid file. if [ "$daemon" = "${cmd_line_so_far[j+1]}" ]; then _APPCTL_TARGET="$daemon" ((j++)) break fi done continue else comp_wordlist="$_POSSIBLE_TARGETS" break fi else options="$(extract_options $_COMMAND)" # See if we could find the exact option. if [ "${cmd_line_so_far[i]}" = "$(grep -- "${cmd_line_so_far[i]}" <<< "$options")" ]; then # If an argument is required and next argument is non-empty, # skip it. Else, return directly. if [ -n "$(option_require_arg "${cmd_line_so_far[i]}")" ]; then ((j++)) if [ -z "${cmd_line_so_far[j]}" ]; then printf_stderr "\nOption requires an arugment." return fi fi continue # Else, need to keep completing on option. else comp_wordlist="$options" break fi fi fi # Takes the first non-option argument as subcmd. _subcmd="${cmd_line_so_far[i]}" break done if [ -z "$comp_wordlist" ]; then # If the subcommand is not found, provides all subcmds and options. if [ -z "$_subcmd" ]; then comp_wordlist="$(extract_subcmds) $(extract_options)" # Else parses the current arguments and finds the possible completions. else # $j stores the index of the subcmd in cmd_line_so_far. comp_wordlist="$(parse_and_compgen "${cmd_line_so_far[@]:$j}")" fi fi echo "$comp_wordlist" } # Compgen # ======= # # # # The compgen function. _ovs_command_complete() { local cur prev _COMMAND=${COMP_WORDS} # element 0 is the command. COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} # Do not print anything at first [TAB] execution. if [ "$COMP_TYPE" -eq "9" ]; then _PRINTF_ENABLE= else _PRINTF_ENABLE="enabled" fi # Extracts bash prompt PS1. if [ "$1" != "debug" ]; then extract_bash_prompt fi # Invokes the helper function to get all available completions. # Always not input the 'COMP_WORD' at 'COMP_CWORD', since it is # the one to be completed. _COMP_WORDLIST="$(ovs_comp_helper \ ${COMP_WORDS[@]:1:COMP_CWORD-1})" # This is a hack to prevent autocompleting when there is only one # available completion and printf disabled. if [ -z "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then _COMP_WORDLIST="$_COMP_WORDLIST none void no-op" fi if [ -n "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then if [ -n "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sed -e '/NO_EXPAN/d' | grep -- "^$cur")" ]; then printf_stderr "\nAvailable completions:\n" else if [ "$1" != "debug" ]; then # If there is no match between '$cur' and the '$_COMP_WORDLIST' # prints a bash prompt since the 'complete' will not print it. printf_stderr "\n$_BASH_PROMPT${COMP_WORDS[@]}" fi fi fi if [ "$1" = "debug" ]; then printf_stderr "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sort -u | sed -e '/NO_EXPAN/d' | grep -- "$cur")\n" else if [ -n "$_COMP_WORDLIST" ]; then COMPREPLY=( $(compgen -W "$(echo $_COMP_WORDLIST | tr ' ' '\n' \ | sort -u | sed -e '/NO_EXPAN/d')" -- $cur) ) else compopt -o nospace # If there is no completions, just complete on file path. _filedir fi fi return 0 } # Debug mode. if [ "$1" = "debug" ]; then shift COMP_TYPE=0 COMP_WORDS=($@) COMP_CWORD="$(expr $# - 1)" # If the last argument is TAB, it means that the previous # argument is already complete and script should complete # next argument which is not input yet. This hack is for # compromising the fact that bash cannot take unquoted # empty argument. if [ "${COMP_WORDS[$COMP_CWORD]}" = "TAB" ]; then COMP_WORDS[$COMP_CWORD]="" fi _ovs_command_complete "debug" # Normal compgen mode. else complete -F _ovs_command_complete ovs-appctl complete -F _ovs_command_complete ovs-ofctl complete -F _ovs_command_complete ovs-dpctl complete -F _ovs_command_complete ovsdb-tool fi openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-ofctl.8.in0000644000000000000000000000013213534540071020271 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.765845936 openvswitch-2.5.9/utilities/ovs-ofctl.8.in0000644000175000017500000036355313534540071021776 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-ofctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-ofctl . .SH NAME ovs\-ofctl \- administer OpenFlow switches . .SH SYNOPSIS .B ovs\-ofctl [\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...] . .SH DESCRIPTION The .B ovs\-ofctl program is a command line tool for monitoring and administering OpenFlow switches. It can also show the current state of an OpenFlow switch, including features, configuration, and table entries. It should work with any OpenFlow switch, not just Open vSwitch. . .SS "OpenFlow Switch Management Commands" .PP These commands allow \fBovs\-ofctl\fR to monitor and administer an OpenFlow switch. It is able to show the current state of a switch, including features, configuration, and table entries. .PP Most of these commands take an argument that specifies the method for connecting to an OpenFlow switch. The following connection methods are supported: . .RS .so lib/vconn-active.man . .IP "\fIfile\fR" This is short for \fBunix:\fIfile\fR, as long as \fIfile\fR does not contain a colon. . .IP \fIbridge\fR This is short for \fBunix:@RUNDIR@/\fIbridge\fB.mgmt\fR, as long as \fIbridge\fR does not contain a colon. . .IP [\fItype\fB@\fR]\fIdp\fR Attempts to look up the bridge associated with \fIdp\fR and open as above. If \fItype\fR is given, it specifies the datapath provider of \fIdp\fR, otherwise the default provider \fBsystem\fR is assumed. .RE . .TP \fBshow \fIswitch\fR Prints to the console information on \fIswitch\fR, including information on its flow tables and ports. . .TP \fBdump\-tables \fIswitch\fR Prints to the console statistics for each of the flow tables used by \fIswitch\fR. .TP \fBdump\-table\-features \fIswitch\fR Prints to the console features for each of the flow tables used by \fIswitch\fR. .TP \fBdump\-table\-desc \fIswitch\fR Prints to the console configuration for each of the flow tables used by \fIswitch\fR for OpenFlow 1.4+. .IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIsetting\fR" This command configures flow table settings for OpenFlow table \fItable_id\fR within \fIswitch\fR. The available settings depend on the OpenFlow version in use. In OpenFlow 1.1 and 1.2 (which must be enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures behavior when no flow is found when a packet is looked up in a flow table. The following \fIsetting\fR values are available: .RS .IP \fBdrop\fR Drop the packet. .IP \fBcontinue\fR Continue to the next table in the pipeline. (This is how an OpenFlow 1.0 switch always handles packets that do not match any flow, in tables other than the last one.) .IP \fBcontroller\fR Send to controller. (This is how an OpenFlow 1.0 switch always handles packets that do not match any flow in the last table.) .RE .IP In OpenFlow 1.4 and later (which must be enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures the behavior when a controller attempts to add a flow to a flow table that is full. The following \fIsetting\fR values are available: .RS .IP \fBevict\fR Delete some existing flow from the flow table, according to the algorithm described for the \fBFlow_Table\fR table in \fBovs-vswitchd.conf.db\fR(5). .IP \fBnoevict\fR Refuse to add the new flow. (Eviction might still be enabled through the \fBoverflow_policy\fR column in the \fBFlow_Table\fR table documented in \fBovs-vswitchd.conf.db\fR(5).) .IP \fBvacancy:\fIlow\fB,\fIhigh\fR Enables sending vacancy events to controllers using \fBTABLE_STATUS\fR messages, based on percentage thresholds \fIlow\fR and \fIhigh\fR. .IP \fBnovacancy\fR Disables vacancy events. .RE . .TP \fBdump\-ports \fIswitch\fR [\fInetdev\fR] Prints to the console statistics for network devices associated with \fIswitch\fR. If \fInetdev\fR is specified, only the statistics associated with that device will be printed. \fInetdev\fR can be an OpenFlow assigned port number or device name, e.g. \fBeth0\fR. . .IP "\fBdump\-ports\-desc \fIswitch\fR [\fIport\fR]" Prints to the console detailed information about network devices associated with \fIswitch\fR. To dump only a specific port, specify its number as \fIport\fR. Otherwise, if \fIport\fR is omitted, or if it is specified as \fBANY\fR, then all ports are printed. This is a subset of the information provided by the \fBshow\fR command. .IP If the connection to \fIswitch\fR negotiates OpenFlow 1.0, 1.2, or 1.2, this command uses an OpenFlow extension only implemented in Open vSwitch (version 1.7 and later). .IP Only OpenFlow 1.5 and later support dumping a specific port. Earlier versions of OpenFlow always dump all ports. . .IP "\fBmod\-port \fIswitch\fR \fIport\fR \fIaction\fR" Modify characteristics of port \fBport\fR in \fIswitch\fR. \fIport\fR may be an OpenFlow port number or name or the keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow local port). The \fIaction\fR may be any one of the following: . .RS .IQ \fBup\fR .IQ \fBdown\fR Enable or disable the interface. This is equivalent to \fBifconfig up\fR or \fBifconfig down\fR on a Unix system. . .IP \fBstp\fR .IQ \fBno\-stp\fR Enable or disable 802.1D spanning tree protocol (STP) on the interface. OpenFlow implementations that don't support STP will refuse to enable it. . .IP \fBreceive\fR .IQ \fBno\-receive\fR .IQ \fBreceive\-stp\fR .IQ \fBno\-receive\-stp\fR Enable or disable OpenFlow processing of packets received on this interface. When packet processing is disabled, packets will be dropped instead of being processed through the OpenFlow table. The \fBreceive\fR or \fBno\-receive\fR setting applies to all packets except 802.1D spanning tree packets, which are separately controlled by \fBreceive\-stp\fR or \fBno\-receive\-stp\fR. . .IP \fBforward\fR .IQ \fBno\-forward\fR Allow or disallow forwarding of traffic to this interface. By default, forwarding is enabled. . .IP \fBflood\fR .IQ \fBno\-flood\fR Controls whether an OpenFlow \fBflood\fR action will send traffic out this interface. By default, flooding is enabled. Disabling flooding is primarily useful to prevent loops when a spanning tree protocol is not in use. . .IP \fBpacket\-in\fR .IQ \fBno\-packet\-in\fR Controls whether packets received on this interface that do not match a flow table entry generate a ``packet in'' message to the OpenFlow controller. By default, ``packet in'' messages are enabled. .RE .IP The \fBshow\fR command displays (among other information) the configuration that \fBmod\-port\fR changes. . .IP "\fBget\-frags \fIswitch\fR" Prints \fIswitch\fR's fragment handling mode. See \fBset\-frags\fR, below, for a description of each fragment handling mode. .IP The \fBshow\fR command also prints the fragment handling mode among its other output. . .IP "\fBset\-frags \fIswitch frag_mode\fR" Configures \fIswitch\fR's treatment of IPv4 and IPv6 fragments. The choices for \fIfrag_mode\fR are: .RS .IP "\fBnormal\fR" Fragments pass through the flow table like non-fragmented packets. The TCP ports, UDP ports, and ICMP type and code fields are always set to 0, even for fragments where that information would otherwise be available (fragments with offset 0). This is the default fragment handling mode for an OpenFlow switch. .IP "\fBdrop\fR" Fragments are dropped without passing through the flow table. .IP "\fBreassemble\fR" The switch reassembles fragments into full IP packets before passing them through the flow table. Open vSwitch does not implement this fragment handling mode. .IP "\fBnx\-match\fR" Fragments pass through the flow table like non-fragmented packets. The TCP ports, UDP ports, and ICMP type and code fields are available for matching for fragments with offset 0, and set to 0 in fragments with nonzero offset. This mode is a Nicira extension. .RE .IP See the description of \fBip_frag\fR, below, for a way to match on whether a packet is a fragment and on its fragment offset. . .TP \fBdump\-flows \fIswitch \fR[\fIflows\fR] Prints to the console all flow entries in \fIswitch\fR's tables that match \fIflows\fR. If \fIflows\fR is omitted, all flows in the switch are retrieved. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR. The output format is described in \fBTable Entry Output\fR. . .IP By default, \fBovs\-ofctl\fR prints flow entries in the same order that the switch sends them, which is unlikely to be intuitive or consistent. See the description of \fB\-\-sort\fR and \fB\-\-rsort\fR, under \fBOPTIONS\fR below, to influence the display order. . .TP \fBdump\-aggregate \fIswitch \fR[\fIflows\fR] Prints to the console aggregate statistics for flows in \fIswitch\fR's tables that match \fIflows\fR. If \fIflows\fR is omitted, the statistics are aggregated across all flows in the switch's flow tables. See \fBFlow Syntax\fR, below, for the syntax of \fIflows\fR. The output format is described in \fBTable Entry Output\fR. . .IP "\fBqueue\-stats \fIswitch \fR[\fIport \fR[\fIqueue\fR]]" Prints to the console statistics for the specified \fIqueue\fR on \fIport\fR within \fIswitch\fR. \fIport\fR can be an OpenFlow port number or name, the keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow local port), or the keyword \fBALL\fR. Either of \fIport\fR or \fIqueue\fR or both may be omitted (or equivalently the keyword \fBALL\fR). If both are omitted, statistics are printed for all queues on all ports. If only \fIqueue\fR is omitted, then statistics are printed for all queues on \fIport\fR; if only \fIport\fR is omitted, then statistics are printed for \fIqueue\fR on every port where it exists. . .SS "OpenFlow 1.1+ Group Table Commands" . The following commands work only with switches that support OpenFlow 1.1 or later. Because support for OpenFlow 1.1 and later is still experimental in Open vSwitch, it is necessary to explicitly enable these protocol versions in \fBovs\-ofctl\fR (using \fB\-O\fR) and in the switch itself (with the \fBprotocols\fR column in the \fBBridge\fR table). For more information, see ``Q: What versions of OpenFlow does Open vSwitch support?'' in the Open vSwitch FAQ. . .IP "\fBdump\-groups \fIswitch\fR [\fIgroup\fR]" Prints group entries in \fIswitch\fR's tables to console. To dump only a specific group, specify its number as \fIgroup\fR. Otherwise, if \fIgroup\fR is omitted, or if it is specified as \fBALL\fR, then all groups are printed. Each line of output is a group entry as described in \fBGroup Syntax\fR below. .IP Only OpenFlow 1.5 and later support dumping a specific group. Earlier versions of OpenFlow always dump all groups. . .IP "\fBdump\-group\-features \fIswitch" Prints to the console the group features of the \fIswitch\fR. . .IP "\fBdump\-group-stats \fIswitch \fR[\fIgroups\fR]" Prints to the console statistics for the specified \fIgroups in the \fIswitch\fR's tables. If \fIgroups\fR is omitted then statistics for all groups are printed. See \fBGroup Syntax\fR, below, for the syntax of \fIgroups\fR. . .SS "OpenFlow 1.3+ Switch Meter Table Commands" . These commands manage the meter table in an OpenFlow switch. In each case, \fImeter\fR specifies a meter entry in the format described in \fBMeter Syntax\fR, below. . .PP OpenFlow 1.3 introduced support for meters, so these commands only work with switches that support OpenFlow 1.3 or later. The caveats described for groups in the previous section also apply to meters. . .IP "\fBadd\-meter \fIswitch meter\fR" Add a meter entry to \fIswitch\fR's tables. The \fImeter\fR syntax is described in section \fBMeter Syntax\fR, below. . .IP "\fBmod\-meter \fIswitch meter\fR" Modify an existing meter. . .IP "\fBdel\-meters \fIswitch\fR" .IQ "\fBdel\-meter \fIswitch\fR [\fImeter\fR]" Delete entries from \fIswitch\fR's meter table. \fImeter\fR can specify a single meter with syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR. . .IP "\fBdump\-meters \fIswitch\fR" .IQ "\fBdump\-meter \fIswitch\fR [\fImeter\fR]" Print meter configuration. \fImeter\fR can specify a single meter with syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR. . .IP "\fBmeter\-stats \fIswitch\fR [\fImeter\fR]" Print meter statistics. \fImeter\fR can specify a single meter with syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR. . .IP "\fBmeter\-features \fIswitch\fR" Print meter features. . .SS "OpenFlow Switch Flow Table Commands" . These commands manage the flow table in an OpenFlow switch. In each case, \fIflow\fR specifies a flow entry in the format described in \fBFlow Syntax\fR, below, \fIfile\fR is a text file that contains zero or more flows in the same syntax, one per line, and the optional \fB\-\-bundle\fR option operates the command as a single atomic transation, see option \fB\-\-bundle\fR, below. . .IP "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch flow\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch \fB\- < \fIfile\fR" .IQ "[\fB\-\-bundle\fR] \fBadd\-flows \fIswitch file\fR" Add each flow entry to \fIswitch\fR's tables. . Each flow specification (e.g., each line in \fIfile\fR) may start with \fBadd\fR, \fBmodify\fR, \fBdelete\fR, \fBmodify_strict\fR, or \fBdelete_strict\fR keyword to specify whether a flow is to be added, modified, or deleted, and whether the modify or delete is strict or not. For backwards compatibility a flow specification without one of these keywords is treated as a flow add. All flow mods are executed in the order specified. . .IP "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch flow\fR" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch \fB\- < \fIfile\fR" Modify the actions in entries from \fIswitch\fR's tables that match the specified flows. With \fB\-\-strict\fR, wildcards are not treated as active for matching purposes. . .IP "[\fB\-\-bundle\fR] \fBdel\-flows \fIswitch\fR" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fR[\fIflow\fR]" .IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fB\- < \fIfile\fR" Deletes entries from \fIswitch\fR's flow table. With only a \fIswitch\fR argument, deletes all flows. Otherwise, deletes flow entries that match the specified flows. With \fB\-\-strict\fR, wildcards are not treated as active for matching purposes. . .IP "[\fB\-\-bundle\fR] [\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR" Reads flow entries from \fIfile\fR (or \fBstdin\fR if \fIfile\fR is \fB\-\fR) and queries the flow table from \fIswitch\fR. Then it fixes up any differences, adding flows from \fIflow\fR that are missing on \fIswitch\fR, deleting flows from \fIswitch\fR that are not in \fIfile\fR, and updating flows in \fIswitch\fR whose actions, cookie, or timeouts differ in \fIfile\fR. . .IP With \fB\-\-readd\fR, \fBovs\-ofctl\fR adds all the flows from \fIfile\fR, even those that exist with the same actions, cookie, and timeout in \fIswitch\fR. This resets all the flow packet and byte counters to 0, which can be useful for debugging. . .IP "\fBdiff\-flows \fIsource1 source2\fR" Reads flow entries from \fIsource1\fR and \fIsource2\fR and prints the differences. A flow that is in \fIsource1\fR but not in \fIsource2\fR is printed preceded by a \fB\-\fR, and a flow that is in \fIsource2\fR but not in \fIsource1\fR is printed preceded by a \fB+\fR. If a flow exists in both \fIsource1\fR and \fIsource2\fR with different actions, cookie, or timeouts, then both versions are printed preceded by \fB\-\fR and \fB+\fR, respectively. .IP \fIsource1\fR and \fIsource2\fR may each name a file or a switch. If a name begins with \fB/\fR or \fB.\fR, then it is considered to be a file name. A name that contains \fB:\fR is considered to be a switch. Otherwise, it is a file if a file by that name exists, a switch if not. .IP For this command, an exit status of 0 means that no differences were found, 1 means that an error occurred, and 2 means that some differences were found. . .IP "\fBpacket\-out \fIswitch in_port actions packet\fR..." Connects to \fIswitch\fR and instructs it to execute the OpenFlow \fIactions\fR on each \fIpacket\fR. Each \fBpacket\fR is specified as a series of hex digits. For the purpose of executing the actions, the packets are considered to have arrived on \fIin_port\fR, which may be an OpenFlow port number or name (e.g. \fBeth0\fR), the keyword \fBLOCAL\fR (the preferred way to refer to the OpenFlow ``local'' port), or the keyword \fBNONE\fR to indicate that the packet was generated by the switch itself. . .SS "OpenFlow Switch Group Table Commands" . These commands manage the group table in an OpenFlow switch. In each case, \fIgroup\fR specifies a group entry in the format described in \fBGroup Syntax\fR, below, and \fIfile\fR is a text file that contains zero or more groups in the same syntax, one per line. .IP "\fBadd\-group \fIswitch group\fR" .IQ "\fBadd\-group \fIswitch \fB\- < \fIfile\fR" .IQ "\fBadd\-groups \fIswitch file\fR" Add each group entry to \fIswitch\fR's tables. . .IP "\fBmod\-group \fIswitch group\fR" .IQ "\fBmod\-group \fIswitch \fB\- < \fIfile\fR" Modify the action buckets in entries from \fIswitch\fR's tables for each group entry. . .IP "\fBdel\-groups \fIswitch\fR" .IQ "\fBdel\-groups \fIswitch \fR[\fIgroup\fR]" .IQ "\fBdel\-groups \fIswitch \fB\- < \fIfile\fR" Deletes entries from \fIswitch\fR's group table. With only a \fIswitch\fR argument, deletes all groups. Otherwise, deletes the group for each group entry. . .IP "\fBinsert\-buckets \fIswitch group\fR" .IQ "\fBinsert\-buckets \fIswitch \fB\- < \fIfile\fR" Add buckets to an existing group present in the \fIswitch\fR's group table. If no \fIcommand_bucket_id\fR is present in the group specification then all buckets of the group are removed. . .IP "\fBremove\-buckets \fIswitch group\fR" .IQ "\fBremove\-buckets \fIswitch \fB\- < \fIfile\fR" Remove buckets to an existing group present in the \fIswitch\fR's group table. If no \fIcommand_bucket_id\fR is present in the group specification then all buckets of the group are removed. . .SS "OpenFlow Switch Tunnel TLV Table Commands" . Open vSwitch maintains a mapping table between tunnel option TLVs (defined by ) and NXM fields \fBtun_metadata\fIn\fR, where \fIn\fR ranges from 0 to 63, that can be operated on for the purposes of matches, actions, etc. This TLV table can be used for Geneve option TLVs or other protocols with options in same TLV format as Geneve options. This mapping must be explicitly specified by the user through the following commands. A TLV mapping is specified with the syntax \fB{class=\fIclass\fB,type=\fItype\fB,len=\fIlength\fB}->tun_metadata\fIn\fR. When an option mapping exists for a given \fBtun_metadata\fIn\fR, matching on the defined field becomes possible, e.g.: .RS ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0" .PP ovs-ofctl add-flow br0 tun_metadata0=1234,actions=controller .RE A mapping should not be changed while it is in active use by a flow. The result of doing so is undefined. Currently, the TLV mapping table is shared between all OpenFlow switches in a given instance of Open vSwitch. This restriction will be lifted in the future to allow for easier management. These commands are Nicira extensions to OpenFlow and require Open vSwitch 2.5 or later. .IP "\fBadd\-tlv\-map \fIswitch option\fR[\fB,\fIoption\fR]..." Add each \fIoption\fR to \fIswitch\fR's tables. Duplicate fields are rejected. . .IP "\fBdel\-tlv\-map \fIswitch \fR[\fIoption\fR[\fB,\fIoption\fR]]..." Delete each \fIoption\fR from \fIswitch\fR's table, or all option TLV mapping if no \fIoption\fR is specified. Fields that aren't mapped are ignored. . .IP "\fBdump\-tlv\-map \fIswitch\fR" Show the currently mapped fields in the switch's option table as well as switch capabilities. . .SS "OpenFlow Switch Monitoring Commands" . .IP "\fBsnoop \fIswitch\fR" Connects to \fIswitch\fR and prints to the console all OpenFlow messages received. Unlike other \fBovs\-ofctl\fR commands, if \fIswitch\fR is the name of a bridge, then the \fBsnoop\fR command connects to a Unix domain socket named \fB@RUNDIR@/\fIswitch\fB.snoop\fR. \fBovs\-vswitchd\fR listens on such a socket for each bridge and sends to it all of the OpenFlow messages sent to or received from its configured OpenFlow controller. Thus, this command can be used to view OpenFlow protocol activity between a switch and its controller. .IP When a switch has more than one controller configured, only the traffic to and from a single controller is output. If none of the controllers is configured as a master or a slave (using a Nicira extension to OpenFlow 1.0 or 1.1, or a standard request in OpenFlow 1.2 or later), then a controller is chosen arbitrarily among them. If there is a master controller, it is chosen; otherwise, if there are any controllers that are not masters or slaves, one is chosen arbitrarily; otherwise, a slave controller is chosen arbitrarily. This choice is made once at connection time and does not change as controllers reconfigure their roles. .IP If a switch has no controller configured, or if the configured controller is disconnected, no traffic is sent, so monitoring will not show any traffic. . .IP "\fBmonitor \fIswitch\fR [\fImiss-len\fR] [\fBinvalid_ttl\fR] [\fBwatch:\fR[\fIspec\fR...]]" Connects to \fIswitch\fR and prints to the console all OpenFlow messages received. Usually, \fIswitch\fR should specify the name of a bridge in the \fBovs\-vswitchd\fR database. .IP If \fImiss-len\fR is provided, \fBovs\-ofctl\fR sends an OpenFlow ``set configuration'' message at connection setup time that requests \fImiss-len\fR bytes of each packet that misses the flow table. Open vSwitch does not send these and other asynchronous messages to an \fBovs\-ofctl monitor\fR client connection unless a nonzero value is specified on this argument. (Thus, if \fImiss\-len\fR is not specified, very little traffic will ordinarily be printed.) .IP If \fBinvalid_ttl\fR is passed, \fBovs\-ofctl\fR sends an OpenFlow ``set configuration'' message at connection setup time that requests \fBINVALID_TTL_TO_CONTROLLER\fR, so that \fBovs\-ofctl monitor\fR can receive ``packet-in'' messages when TTL reaches zero on \fBdec_ttl\fR action. .IP \fBwatch:\fR[\fB\fIspec\fR...] causes \fBovs\-ofctl\fR to send a ``monitor request'' Nicira extension message to the switch at connection setup time. This message causes the switch to send information about flow table changes as they occur. The following comma-separated \fIspec\fR syntax is available: .RS .IP "\fB!initial\fR" Do not report the switch's initial flow table contents. .IP "\fB!add\fR" Do not report newly added flows. .IP "\fB!delete\fR" Do not report deleted flows. .IP "\fB!modify\fR" Do not report modifications to existing flows. .IP "\fB!own\fR" Abbreviate changes made to the flow table by \fBovs\-ofctl\fR's own connection to the switch. (These could only occur using the \fBofctl/send\fR command described below under \fBRUNTIME MANAGEMENT COMMANDS\fR.) .IP "\fB!actions\fR" Do not report actions as part of flow updates. .IP "\fBtable=\fInumber\fR" Limits the monitoring to the table with the given \fInumber\fR between 0 and 254. By default, all tables are monitored. .IP "\fBout_port=\fIport\fR" If set, only flows that output to \fIport\fR are monitored. The \fIport\fR may be an OpenFlow port number or keyword (e.g. \fBLOCAL\fR). .IP "\fIfield\fB=\fIvalue\fR" Monitors only flows that have \fIfield\fR specified as the given \fIvalue\fR. Any syntax valid for matching on \fBdump\-flows\fR may be used. .RE .IP This command may be useful for debugging switch or controller implementations. With \fBwatch:\fR, it is particularly useful for observing how a controller updates flow tables. . .SS "OpenFlow Switch and Controller Commands" . The following commands, like those in the previous section, may be applied to OpenFlow switches, using any of the connection methods described in that section. Unlike those commands, these may also be applied to OpenFlow controllers. . .TP \fBprobe \fItarget\fR Sends a single OpenFlow echo-request message to \fItarget\fR and waits for the response. With the \fB\-t\fR or \fB\-\-timeout\fR option, this command can test whether an OpenFlow switch or controller is up and running. . .TP \fBping \fItarget \fR[\fIn\fR] Sends a series of 10 echo request packets to \fItarget\fR and times each reply. The echo request packets consist of an OpenFlow header plus \fIn\fR bytes (default: 64) of randomly generated payload. This measures the latency of individual requests. . .TP \fBbenchmark \fItarget n count\fR Sends \fIcount\fR echo request packets that each consist of an OpenFlow header plus \fIn\fR bytes of payload and waits for each response. Reports the total time required. This is a measure of the maximum bandwidth to \fItarget\fR for round-trips of \fIn\fR-byte messages. . .SS "Other Commands" . .IP "\fBofp\-parse\fR \fIfile\fR" Reads \fIfile\fR (or \fBstdin\fR if \fIfile\fR is \fB\-\fR) as a series of OpenFlow messages in the binary format used on an OpenFlow connection, and prints them to the console. This can be useful for printing OpenFlow messages captured from a TCP stream. . .IP "\fBofp\-parse\-pcap\fR \fIfile\fR [\fIport\fR...]" Reads \fIfile\fR, which must be in the PCAP format used by network capture tools such as \fBtcpdump\fR or \fBwireshark\fR, extracts all the TCP streams for OpenFlow connections, and prints the OpenFlow messages in those connections in human-readable format on \fBstdout\fR. .IP OpenFlow connections are distinguished by TCP port number. Non-OpenFlow packets are ignored. By default, data on TCP ports 6633 and 6653 are considered to be OpenFlow. Specify one or more \fIport\fR arguments to override the default. .IP This command cannot usefully print SSL encrypted traffic. It does not understand IPv6. . .SS "Flow Syntax" .PP Some \fBovs\-ofctl\fR commands accept an argument that describes a flow or flows. Such flow descriptions comprise a series \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a flow description normally requires quoting to prevent the shell from breaking the description into multiple arguments.) .PP Flow descriptions should be in \fBnormal form\fR. This means that a flow may only specify a value for an L3 field if it also specifies a particular L2 protocol, and that a flow may only specify an L4 field if it also specifies particular L2 and L3 protocol types. For example, if the L2 protocol type \fBdl_type\fR is wildcarded, then L3 fields \fBnw_src\fR, \fBnw_dst\fR, and \fBnw_proto\fR must also be wildcarded. Similarly, if \fBdl_type\fR or \fBnw_proto\fR (the L3 protocol type) is wildcarded, so must be the L4 fields \fBtcp_dst\fR and \fBtcp_src\fR. \fBovs\-ofctl\fR will warn about flows not in normal form. .PP The following field assignments describe how a flow matches a packet. If any of these assignments is omitted from the flow syntax, the field is treated as a wildcard; thus, if all of them are omitted, the resulting flow matches all packets. The string \fB*\fR may be specified to explicitly mark any of these fields as a wildcard. (\fB*\fR should be quoted to protect it from shell expansion.) . .IP \fBin_port=\fIport\fR Matches OpenFlow port \fIport\fR, which may be an OpenFlow port number or keyword (e.g. \fBLOCAL\fR). \fBovs\-ofctl show\fR. .IP (The \fBresubmit\fR action can search OpenFlow flow tables with arbitrary \fBin_port\fR values, so flows that match port numbers that do not exist from an OpenFlow perspective can still potentially be matched.) . .IP \fBdl_vlan=\fIvlan\fR Matches IEEE 802.1q Virtual LAN tag \fIvlan\fR. Specify \fB0xffff\fR as \fIvlan\fR to match packets that are not tagged with a Virtual LAN; otherwise, specify a number between 0 and 4095, inclusive, as the 12-bit VLAN ID to match. . .IP \fBdl_vlan_pcp=\fIpriority\fR Matches IEEE 802.1q Priority Code Point (PCP) \fIpriority\fR, which is specified as a value between 0 and 7, inclusive. A higher value indicates a higher frame priority level. . .IP \fBdl_src=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR .IQ \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR Matches an Ethernet source (or destination) address specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR). . .IP \fBdl_src=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR .IQ \fBdl_dst=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR Matches an Ethernet destination address specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR), with a wildcard mask following the slash. Open vSwitch 1.8 and later support arbitrary masks for source and/or destination. Earlier versions only support masking the destination with the following masks: .RS .IP \fB01:00:00:00:00:00\fR Match only the multicast bit. Thus, \fBdl_dst=01:00:00:00:00:00/01:00:00:00:00:00\fR matches all multicast (including broadcast) Ethernet packets, and \fBdl_dst=00:00:00:00:00:00/01:00:00:00:00:00\fR matches all unicast Ethernet packets. .IP \fBfe:ff:ff:ff:ff:ff\fR Match all bits except the multicast bit. This is probably not useful. .IP \fBff:ff:ff:ff:ff:ff\fR Exact match (equivalent to omitting the mask). .IP \fB00:00:00:00:00:00\fR Wildcard all bits (equivalent to \fBdl_dst=*\fR.) .RE . .IP \fBdl_type=\fIethertype\fR Matches Ethernet protocol type \fIethertype\fR, which is specified as an integer between 0 and 65535, inclusive, either in decimal or as a hexadecimal number prefixed by \fB0x\fR (e.g. \fB0x0806\fR to match ARP packets). . .IP \fBnw_src=\fIip\fR[\fB/\fInetmask\fR] .IQ \fBnw_dst=\fIip\fR[\fB/\fInetmask\fR] When \fBdl_type\fR is 0x0800 (possibly via shorthand, e.g. \fBip\fR or \fBtcp\fR), matches IPv4 source (or destination) address \fIip\fR, which may be specified as an IP address or host name (e.g. \fB192.168.1.1\fR or \fBwww.example.com\fR). The optional \fInetmask\fR allows restricting a match to an IPv4 address prefix. The netmask may be specified as a dotted quad (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block (e.g. \fB192.168.1.0/24\fR). Open vSwitch 1.8 and later support arbitrary dotted quad masks; earlier versions support only CIDR masks, that is, the dotted quads that are equivalent to some CIDR block. .IP When \fBdl_type=0x0806\fR or \fBarp\fR is specified, matches the \fBar_spa\fR or \fBar_tpa\fR field, respectively, in ARP packets for IPv4 and Ethernet. .IP When \fBdl_type=0x8035\fR or \fBrarp\fR is specified, matches the \fBar_spa\fR or \fBar_tpa\fR field, respectively, in RARP packets for IPv4 and Ethernet. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800, 0x0806, or 0x8035, the values of \fBnw_src\fR and \fBnw_dst\fR are ignored (see \fBFlow Syntax\fR above). . .IP \fBnw_proto=\fIproto\fR .IQ \fBip_proto=\fIproto\fR When \fBip\fR or \fBdl_type=0x0800\fR is specified, matches IP protocol type \fIproto\fR, which is specified as a decimal number between 0 and 255, inclusive (e.g. 1 to match ICMP packets or 6 to match TCP packets). .IP When \fBipv6\fR or \fBdl_type=0x86dd\fR is specified, matches IPv6 header type \fIproto\fR, which is specified as a decimal number between 0 and 255, inclusive (e.g. 58 to match ICMPv6 packets or 6 to match TCP). The header type is the terminal header as described in the \fBDESIGN\fR document. .IP When \fBarp\fR or \fBdl_type=0x0806\fR is specified, matches the lower 8 bits of the ARP opcode. ARP opcodes greater than 255 are treated as 0. .IP When \fBrarp\fR or \fBdl_type=0x8035\fR is specified, matches the lower 8 bits of the ARP opcode. ARP opcodes greater than 255 are treated as 0. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800, 0x0806, 0x8035 or 0x86dd, the value of \fBnw_proto\fR is ignored (see \fBFlow Syntax\fR above). . .IP \fBnw_tos=\fItos\fR Matches IP ToS/DSCP or IPv6 traffic class field \fItos\fR, which is specified as a decimal number between 0 and 255, inclusive. Note that the two lower reserved bits are ignored for matching purposes. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or 0x86dd, the value of \fBnw_tos\fR is ignored (see \fBFlow Syntax\fR above). . .IP \fBip_dscp=\fIdscp\fR Matches IP ToS/DSCP or IPv6 traffic class field \fIdscp\fR, which is specified as a decimal number between 0 and 63, inclusive. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or 0x86dd, the value of \fBip_dscp\fR is ignored (see \fBFlow Syntax\fR above). . .IP \fBnw_ecn=\fIecn\fR .IQ \fBip_ecn=\fIecn\fR Matches \fIecn\fR bits in IP ToS or IPv6 traffic class fields, which is specified as a decimal number between 0 and 3, inclusive. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or 0x86dd, the value of \fBnw_ecn\fR is ignored (see \fBFlow Syntax\fR above). . .IP \fBnw_ttl=\fIttl\fR Matches IP TTL or IPv6 hop limit value \fIttl\fR, which is specified as a decimal number between 0 and 255, inclusive. .IP When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or 0x86dd, the value of \fBnw_ttl\fR is ignored (see \fBFlow Syntax\fR above). .IP . .IP \fBtcp_src=\fIport\fR .IQ \fBtcp_dst=\fIport\fR .IQ \fBudp_src=\fIport\fR .IQ \fBudp_dst=\fIport\fR .IQ \fBsctp_src=\fIport\fR .IQ \fBsctp_dst=\fIport\fR Matches a TCP, UDP, or SCTP source or destination port \fIport\fR, which is specified as a decimal number between 0 and 65535, inclusive. .IP When \fBdl_type\fR and \fBnw_proto\fR are wildcarded or set to values that do not indicate an appropriate protocol, the values of these settings are ignored (see \fBFlow Syntax\fR above). . .IP \fBtcp_src=\fIport\fB/\fImask\fR .IQ \fBtcp_dst=\fIport\fB/\fImask\fR .IQ \fBudp_src=\fIport\fB/\fImask\fR .IQ \fBudp_dst=\fIport\fB/\fImask\fR .IQ \fBsctp_src=\fIport\fB/\fImask\fR .IQ \fBsctp_dst=\fIport\fB/\fImask\fR Bitwise match on TCP (or UDP or SCTP) source or destination port. The \fIport\fR and \fImask\fR are 16-bit numbers written in decimal or in hexadecimal prefixed by \fB0x\fR. Each 1-bit in \fImask\fR requires that the corresponding bit in \fIport\fR must match. Each 0-bit in \fImask\fR causes the corresponding bit to be ignored. .IP Bitwise matches on transport ports are rarely useful in isolation, but a group of them can be used to reduce the number of flows required to match on a range of transport ports. For example, suppose that the goal is to match TCP source ports 1000 to 1999, inclusive. One way is to insert 1000 flows, each of which matches on a single source port. Another way is to look at the binary representations of 1000 and 1999, as follows: .br .B "01111101000" .br .B "11111001111" .br and then to transform those into a series of bitwise matches that accomplish the same results: .br .B "01111101xxx" .br .B "0111111xxxx" .br .B "10xxxxxxxxx" .br .B "110xxxxxxxx" .br .B "1110xxxxxxx" .br .B "11110xxxxxx" .br .B "1111100xxxx" .br which become the following when written in the syntax required by \fBovs\-ofctl\fR: .br .B "tcp,tcp_src=0x03e8/0xfff8" .br .B "tcp,tcp_src=0x03f0/0xfff0" .br .B "tcp,tcp_src=0x0400/0xfe00" .br .B "tcp,tcp_src=0x0600/0xff00" .br .B "tcp,tcp_src=0x0700/0xff80" .br .B "tcp,tcp_src=0x0780/0xffc0" .br .B "tcp,tcp_src=0x07c0/0xfff0" .IP Only Open vSwitch 1.6 and later supports bitwise matching on transport ports. .IP Like the exact-match forms described above, the bitwise match forms apply only when \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP or SCTP. . .IP \fBtp_src=\fIport\fR .IQ \fBtp_dst=\fIport\fR These are deprecated generic forms of L4 port matches. In new code, please use the TCP-, UDP-, or SCTP-specific forms described above. . .IP \fBtcp_flags=\fIflags\fB/\fImask\fR .IQ \fBtcp_flags=\fR[\fB+\fIflag\fR...][\fB-\fIflag\fR...] Bitwise match on TCP flags. The \fIflags\fR and \fImask\fR are 16-bit numbers written in decimal or in hexadecimal prefixed by \fB0x\fR. Each 1-bit in \fImask\fR requires that the corresponding bit in \fIflags\fR must match. Each 0-bit in \fImask\fR causes the corresponding bit to be ignored. .IP Alternatively, the flags can be specified by their symbolic names (listed below), each preceded by either \fB+\fR for a flag that must be set, or \fB\-\fR for a flag that must be unset, without any other delimiters between the flags. Flags not mentioned are wildcarded. For example, \fBtcp,tcp_flags=+syn\-ack\fR matches TCP SYNs that are not ACKs. .IP TCP protocol currently defines 9 flag bits, and additional 3 bits are reserved (must be transmitted as zero), see RFCs 793, 3168, and 3540. The flag bits are, numbering from the least significant bit: .RS .IP "\fB0: fin\fR" No more data from sender. .IP "\fB1: syn\fR" Synchronize sequence numbers. .IP "\fB2: rst\fR" Reset the connection. .IP "\fB3: psh\fR" Push function. .IP "\fB4: ack\fR" Acknowledgement field significant. .IP "\fB5: urg\fR" Urgent pointer field significant. .IP "\fB6: ece\fR" ECN Echo. .IP "\fB7: cwr\fR" Congestion Windows Reduced. .IP "\fB8: ns\fR" Nonce Sum. .IP "\fB9-11:\fR" Reserved. .IP "\fB12-15:\fR" Not matchable, must be zero. .RE .IP \fBicmp_type=\fItype\fR .IQ \fBicmp_code=\fIcode\fR When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR matches the ICMP type and \fIcode\fR matches the ICMP code. Each is specified as a decimal number between 0 and 255, inclusive. .IP When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of these settings are ignored (see \fBFlow Syntax\fR above). . .IP \fBtable=\fInumber\fR For flow dump commands, limits the flows dumped to those in the table with the given \fInumber\fR between 0 and 254. If not specified (or if 255 is specified as \fInumber\fR), then flows in all tables are dumped. . .IP For flow table modification commands, behavior varies based on the OpenFlow version used to connect to the switch: . .RS .IP "OpenFlow 1.0" OpenFlow 1.0 does not support \fBtable\fR for modifying flows. \fBovs\-ofctl\fR will exit with an error if \fBtable\fR (other than \fBtable=255\fR) is specified for a switch that only supports OpenFlow 1.0. .IP In OpenFlow 1.0, the switch chooses the table into which to insert a new flow. The Open vSwitch software switch always chooses table 0. Other Open vSwitch datapaths and other OpenFlow implementations may choose different tables. .IP The OpenFlow 1.0 behavior in Open vSwitch for modifying or removing flows depends on whether \fB\-\-strict\fR is used. Without \fB\-\-strict\fR, the command applies to matching flows in all tables. With \fB\-\-strict\fR, the command will operate on any single matching flow in any table; it will do nothing if there are matches in more than one table. (The distinction between these behaviors only matters if non-OpenFlow 1.0 commands were also used, because OpenFlow 1.0 alone cannot add flows with the same matching criteria to multiple tables.) . .IP "OpenFlow 1.0 with table_id extension" Open vSwitch implements an OpenFlow extension that allows the controller to specify the table on which to operate. \fBovs\-ofctl\fR automatically enables the extension when \fBtable\fR is specified and OpenFlow 1.0 is used. \fBovs\-ofctl\fR automatically detects whether the switch supports the extension. As of this writing, this extension is only known to be implemented by Open vSwitch. . .IP With this extension, \fBovs\-ofctl\fR operates on the requested table when \fBtable\fR is specified, and acts as described for OpenFlow 1.0 above when no \fBtable\fR is specified (or for \fBtable=255\fR). . .IP "OpenFlow 1.1" OpenFlow 1.1 requires flow table modification commands to specify a table. When \fBtable\fR is not specified (or \fBtable=255\fR is specified), \fBovs\-ofctl\fR defaults to table 0. . .IP "OpenFlow 1.2 and later" OpenFlow 1.2 and later allow flow deletion commands, but not other flow table modification commands, to operate on all flow tables, with the behavior described above for OpenFlow 1.0. .RE . .IP \fBmetadata=\fIvalue\fR[\fB/\fImask\fR] Matches \fIvalue\fR either exactly or with optional \fImask\fR in the metadata field. \fIvalue\fR and \fImask\fR are 64-bit integers, by default in decimal (use a \fB0x\fR prefix to specify hexadecimal). Arbitrary \fImask\fR values are allowed: a 1-bit in \fImask\fR indicates that the corresponding bit in \fIvalue\fR must match exactly, and a 0-bit wildcards that bit. Matching on metadata was added in Open vSwitch 1.8. . .PP The following shorthand notations are also available: . .IP \fBip\fR Same as \fBdl_type=0x0800\fR. . .IP \fBipv6\fR Same as \fBdl_type=0x86dd\fR. . .IP \fBicmp\fR Same as \fBdl_type=0x0800,nw_proto=1\fR. . .IP \fBicmp6\fR Same as \fBdl_type=0x86dd,nw_proto=58\fR. . .IP \fBtcp\fR Same as \fBdl_type=0x0800,nw_proto=6\fR. . .IP \fBtcp6\fR Same as \fBdl_type=0x86dd,nw_proto=6\fR. . .IP \fBudp\fR Same as \fBdl_type=0x0800,nw_proto=17\fR. . .IP \fBudp6\fR Same as \fBdl_type=0x86dd,nw_proto=17\fR. . .IP \fBsctp\fR Same as \fBdl_type=0x0800,nw_proto=132\fR. . .IP \fBsctp6\fR Same as \fBdl_type=0x86dd,nw_proto=132\fR. . .IP \fBarp\fR Same as \fBdl_type=0x0806\fR. . .IP \fBrarp\fR Same as \fBdl_type=0x8035\fR. . .IP \fBmpls\fR Same as \fBdl_type=0x8847\fR. . .IP \fBmplsm\fR Same as \fBdl_type=0x8848\fR. . .PP The following field assignments require support for the NXM (Nicira Extended Match) extension to OpenFlow. When one of these is specified, \fBovs\-ofctl\fR will automatically attempt to negotiate use of this extension. If the switch does not support NXM, then \fBovs\-ofctl\fR will report a fatal error. . .IP \fBvlan_tci=\fItci\fR[\fB/\fImask\fR] Matches modified VLAN TCI \fItci\fR. If \fImask\fR is omitted, \fItci\fR is the exact VLAN TCI to match; if \fImask\fR is specified, then a 1-bit in \fImask\fR indicates that the corresponding bit in \fItci\fR must match exactly, and a 0-bit wildcards that bit. Both \fItci\fR and \fImask\fR are 16-bit values that are decimal by default; use a \fB0x\fR prefix to specify them in hexadecimal. . .IP The value that \fBvlan_tci\fR matches against is 0 for a packet that has no 802.1Q header. Otherwise, it is the TCI value from the 802.1Q header with the CFI bit (with value \fB0x1000\fR) forced to 1. .IP Examples: .RS .IP \fBvlan_tci=0\fR Match only packets without an 802.1Q header. .IP \fBvlan_tci=0xf123\fR Match packets tagged with priority 7 in VLAN 0x123. .IP \fBvlan_tci=0x1123/0x1fff\fR Match packets tagged with VLAN 0x123 (and any priority). .IP \fBvlan_tci=0x5000/0xf000\fR Match packets tagged with priority 2 (in any VLAN). .IP \fBvlan_tci=0/0xfff\fR Match packets with no 802.1Q header or tagged with VLAN 0 (and any priority). .IP \fBvlan_tci=0x5000/0xe000\fR Match packets with no 802.1Q header or tagged with priority 2 (in any VLAN). .IP \fBvlan_tci=0/0xefff\fR Match packets with no 802.1Q header or tagged with VLAN 0 and priority 0. .RE .IP Some of these matching possibilities can also be achieved with \fBdl_vlan\fR and \fBdl_vlan_pcp\fR. . .IP \fBip_frag=\fIfrag_type\fR When \fBdl_type\fR specifies IP or IPv6, \fIfrag_type\fR specifies what kind of IP fragments or non-fragments to match. The following values of \fIfrag_type\fR are supported: .RS .IP "\fBno\fR" Matches only non-fragmented packets. .IP "\fByes\fR" Matches all fragments. .IP "\fBfirst\fR" Matches only fragments with offset 0. .IP "\fBlater\fR" Matches only fragments with nonzero offset. .IP "\fBnot_later\fR" Matches non-fragmented packets and fragments with zero offset. .RE .IP The \fBip_frag\fR match type is likely to be most useful in \fBnx\-match\fR mode. See the description of the \fBset\-frags\fR command, above, for more details. . .IP \fBarp_spa=\fIip\fR[\fB/\fInetmask\fR] .IQ \fBarp_tpa=\fIip\fR[\fB/\fInetmask\fR] When \fBdl_type\fR specifies either ARP or RARP, \fBarp_spa\fR and \fBarp_tpa\fR match the source and target IPv4 address, respectively. An address may be specified as an IP address or host name (e.g. \fB192.168.1.1\fR or \fBwww.example.com\fR). The optional \fInetmask\fR allows restricting a match to an IPv4 address prefix. The netmask may be specified as a dotted quad (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block (e.g. \fB192.168.1.0/24\fR). . .IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR .IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and \fBarp_tha\fR match the source and target hardware address, respectively. An address is specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR). . .IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR .IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and \fBarp_tha\fR match the source and target hardware address, respectively. An address is specified as 6 pairs of hexadecimal digits delimited by colons (e.g. \fB00:0A:E4:25:6B:B0\fR), with a wildcard mask following the slash. . .IP \fBarp_op=\fIopcode\fR When \fBdl_type\fR specifies either ARP or RARP, \fBarp_op\fR matches the ARP opcode. Only ARP opcodes between 1 and 255 should be specified for matching. . .IP \fBipv6_src=\fIipv6\fR[\fB/\fInetmask\fR] .IQ \fBipv6_dst=\fIipv6\fR[\fB/\fInetmask\fR] When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR or \fBtcp6\fR), matches IPv6 source (or destination) address \fIipv6\fR, which may be specified as defined in RFC 2373. The preferred format is \fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fR, where \fIx\fR are the hexadecimal values of the eight 16-bit pieces of the address. A single instance of \fB::\fR may be used to indicate multiple groups of 16-bits of zeros. The optional \fInetmask\fR allows restricting a match to an IPv6 address prefix. A netmask is specified as an IPv6 address (e.g. \fB2001:db8:3c4d:1::/ffff:ffff:ffff:ffff::\fR) or a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR). Open vSwitch 1.8 and later support arbitrary masks; earlier versions support only CIDR masks, that is, CIDR block and IPv6 addresses that are equivalent to CIDR blocks. . .IP \fBipv6_label=\fIlabel\fR When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR or \fBtcp6\fR), matches IPv6 flow label \fIlabel\fR. . .IP \fBnd_target=\fIipv6\fR[\fB/\fInetmask\fR] When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6 Neighbor Discovery (ICMPv6 type 135 or 136), matches the target address \fIipv6\fR. \fIipv6\fR is in the same format described earlier for the \fBipv6_src\fR and \fBipv6_dst\fR fields. . .IP \fBnd_sll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6 Neighbor Solicitation (ICMPv6 type 135), matches the source link\-layer address option. An address is specified as 6 pairs of hexadecimal digits delimited by colons. . .IP \fBnd_tll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6 Neighbor Advertisement (ICMPv6 type 136), matches the target link\-layer address option. An address is specified as 6 pairs of hexadecimal digits delimited by colons. . .IP \fBmpls_bos=\fIbos\fR When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g., \fBmpls\fR or \fBmplsm\fR), matches the bottom-of-stack bit of the outer-most MPLS label stack entry. Valid values are 0 and 1. .IP If 1 then for a packet with a well-formed MPLS label stack the bottom-of-stack bit indicates that the outer label stack entry is also the inner-most label stack entry and thus that is that there is only one label stack entry present. Conversely, if 0 then for a packet with a well-formed MPLS label stack the bottom-of-stack bit indicates that the outer label stack entry is not the inner-most label stack entry and thus there is more than one label stack entry present. . .IP \fBmpls_label=\fIlabel\fR When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g., \fBmpls\fR or \fBmplsm\fR), matches the label of the outer MPLS label stack entry. The label is a 20-bit value that is decimal by default; use a \fB0x\fR prefix to specify them in hexadecimal. . .IP \fBmpls_tc=\fItc\fR When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g., \fBmpls\fR or \fBmplsm\fR), matches the traffic-class of the outer MPLS label stack entry. Valid values are between 0 (lowest) and 7 (highest). . .IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR] .IQ \fBtunnel_id=\fItunnel-id\fR[\fB/\fImask\fR] Matches tunnel identifier \fItunnel-id\fR. Only packets that arrive over a tunnel that carries a key (e.g. GRE with the RFC 2890 key extension and a nonzero key value) will have a nonzero tunnel ID. If \fImask\fR is omitted, \fItunnel-id\fR is the exact tunnel ID to match; if \fImask\fR is specified, then a 1-bit in \fImask\fR indicates that the corresponding bit in \fItunnel-id\fR must match exactly, and a 0-bit wildcards that bit. . .IP \fBtun_flags=\fIflags\fR Matches flags indicating various aspects of the tunnel encapsulation. Currently, there is only one flag defined: .IP \fBoam\fR: The tunnel protocol indicated that this is an OAM control packet. .IP Flags can be prefixed by \fB+\fR or \fB-\fR to indicate that the flag should be matched as either present or not present, respectively. In addition, flags can be specified without a prefix and separated by \fB|\fR to indicate an exact match. .IP Note that it is possible for newer version of Open vSwitch to introduce additional flags with varying meaning. It is therefore not recommended to use an exact match on this field since the behavior of these new flags is unknown and should be ignored. .IP For non-tunneled packets, the value is 0. .IP This field was introduced in Open vSwitch 2.5. . .IP \fBtun_src=\fIip\fR[\fB/\fInetmask\fR] .IQ \fBtun_dst=\fIip\fR[\fB/\fInetmask\fR] Matches tunnel IPv4 source (or destination) address \fIip\fR. Only packets that arrive over a tunnel will have nonzero tunnel addresses. The address may be specified as an IP address or host name (e.g. \fB192.168.1.1\fR or \fBwww.example.com\fR). The optional \fInetmask\fR allows restricting a match to a masked IPv4 address. The netmask may be specified as a dotted quad (e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block (e.g. \fB192.168.1.0/24\fR). . .IP \fBtun_gbp_id=\fIvalue\fR[\fB/\fImask\fR] .IQ \fBtun_gbp_flags=\fIvalue\fR[\fB/\fImask\fR] Matches the group policy identifier and flags in the VXLAN header. Only packets that arrive over a VXLAN tunnel with the "gbp" extension enabled can have this field set. The fields may also be referred to by NXM_NX_TUN_GBP_ID[] (16 bits) and NXM_NX_TUN_GBP_FLAGS[] (8 bits) in the context of field manipulation actions. If these fields are set and the packet matched by the flow is encapsulated in a VXLAN-GBP tunnel, then the policy identifier and flags are transmitted to the destination VXLAN tunnel endpoint. .IP The \fBtun_gbp_flags\fR field has the following format: .IP .in +2 \f(CR+-+-+-+-+-+-+-+-+\fR .br \f(CR|-|D|-|-|A|-|-|-|\fR .br \f(CR+-+-+-+-+-+-+-+-+\fR .B D := Don't Learn bit. When set, this bit indicates that the egress tunnel endpoint MUST NOT learn the source address of the encapsulated frame. .B A := Indicates that the group policy has already been applied to this packet. Policies MUST NOT be applied by devices when the A bit is set. .in -2 .IP For more information, please see the corresponding IETF draft: https://tools.ietf.org/html/draft-smith-vxlan-group-policy . .IP "\fBtun_metadata\fIidx\fR[\fB=\fIvalue\fR[\fB/\fImask\fR]]" Matches \fIvalue\fR either exactly or with optional \fImask\fR in tunnel metadata field number \fIidx\fR (numbered from 0 to 63). The act of specifying a field implies a match on the existence of that field in the packet in addition to the masked value. As a shorthand, it is possible to specify only the field name to simply match on an option being present. .IP Tunnel metadata fields can be dynamically assigned onto the data contained in the option TLVs of packets (e.g. Geneve variable options stores zero or more options in TLV format and tunnel metadata can be assigned onto these option TLVs) using the commands described in the section \fBOpenFlow Switch Tunnel TLV Table Commands\fR. Once assigned, the length of the field is variable according to the size of the option. Before updating a mapping in the option table, flows with references to it should be removed, otherwise the result is non-deterministic. .IP These fields were introduced in Open vSwitch 2.5. . .IP "\fBreg\fIidx\fB=\fIvalue\fR[\fB/\fImask\fR]" Matches \fIvalue\fR either exactly or with optional \fImask\fR in register number \fIidx\fR. The valid range of \fIidx\fR depends on the switch. \fIvalue\fR and \fImask\fR are 32-bit integers, by default in decimal (use a \fB0x\fR prefix to specify hexadecimal). Arbitrary \fImask\fR values are allowed: a 1-bit in \fImask\fR indicates that the corresponding bit in \fIvalue\fR must match exactly, and a 0-bit wildcards that bit. .IP When a packet enters an OpenFlow switch, all of the registers are set to 0. Only explicit actions change register values. . .IP "\fBxreg\fIidx\fB=\fIvalue\fR[\fB/\fImask\fR]" Matches \fIvalue\fR either exactly or with optional \fImask\fR in 64-bit ``extended register'' number \fIidx\fR. Each of the 64-bit extended registers overlays two of the 32-bit registers: \fBxreg0\fR overlays \fBreg0\fR and \fBreg1\fR, with \fBreg0\fR supplying the most-significant bits of \fBxreg0\fR and \fBreg1\fR the least-significant. \fBxreg1\fR similarly overlays \fBreg2\fR and \fBreg3\fR, and so on. .IP These fields were added in Open vSwitch 2.3 to conform with the OpenFlow 1.5 specification. OpenFlow 1.5 calls these fields just the ``packet registers,'' but Open vSwitch already had 32-bit registers by that name, which is why Open vSwitch refers to the standard registers as ``extended registers''. . .IP \fBpkt_mark=\fIvalue\fR[\fB/\fImask\fR] Matches packet metadata mark \fIvalue\fR either exactly or with optional \fImask\fR. The mark is associated data that may be passed into other system components in order to facilitate interaction between subsystems. On Linux this corresponds to the skb mark but the exact implementation is platform-dependent. . .IP \fBactset_output=\fIport\fR Matches the output port currently in the OpenFlow action set, where \fIport\fR may be an OpenFlow port number or keyword (e.g. \fBLOCAL\fR). If there is no output port in the OpenFlow action set, or if the output port will be ignored (e.g. because there is an output group in the OpenFlow action set), then the value will be \fBUNSET\fR. .IP This field was introduced in Open vSwitch 2.4 to conform with the OpenFlow 1.5 specification. . .IP \fBconj_id=\fIvalue\fR Matches the given 32-bit \fIvalue\fR against the conjunction ID. This is used only with the \fBconjunction\fR action (see below). .IP This field was introduced in Open vSwitch 2.4. . .IP \fBct_state=\fIflags\fB/\fImask\fR .IQ \fBct_state=\fR[\fB+\fIflag\fR...][\fB-\fIflag\fR...] Bitwise match on connection state flags. This is used with the \fBct\fR action (see below). .IP The \fBct_state\fR field provides information from a connection tracking module. It describes whether the packet has previously traversed the connection tracker (tracked, or trk) and, if it has been tracked, any additional information that the connection tracker was able to provide about the connection that the current packet belongs to. .IP Individual packets may be in one of two states: Untracked or tracked. When the \fBct\fR action is executed on a packet, it becomes tracked for the the remainder of OpenFlow pipeline processing. Once a packet has become tracked, the state of its corresponding connection may be determined. Note that the \fBct_state\fR is only significant for the current \fBct_zone\fR. .IP Connections may be in one of two states: uncommitted or committed. Connections are uncommitted by default. To determine ongoing information about a connection, like whether the connection is established or not, the connection must be committed. When the \fBct\fR action is executed on a packet with the \fBcommit\fR parameter, the connection will become committed and will remain in this state until the end of the connection. Committed connections store state beyond the duration of packet processing. .IP The \fIflags\fR and \fImask\fR are 32-bit numbers written in decimal or in hexadecimal prefixed by \fB0x\fR. Each 1-bit in \fImask\fR requires that the corresponding bit in \fIflags\fR must match. Each 0-bit in \fImask\fR causes the corresponding bit to be ignored. .IP Alternatively, the flags can be specified by their symbolic names (listed below), each preceded by either \fB+\fR for a flag that must be set, or \fB\-\fR for a flag that must be unset, without any other delimiters between the flags. Flags not mentioned are wildcarded. For example, \fBtcp,ct_state=+trk\-new\fR matches TCP packets that have been run through the connection tracker and do not establish a new connection. .IP The following flags describe the state of the tracking: .RS .IP "\fB0x01: new\fR" This is the beginning of a new connection. This flag may only be present for uncommitted connections. .IP "\fB0x02: est\fR" This is part of an already existing connection. This flag may only be present for committed connections. .IP "\fB0x04: rel\fR" This is a connection that is related to an existing connection, for instance ICMP "destination unreachable" messages or FTP data connections. This flag may only be present for committed connections. .IP "\fB0x08: rpl\fR" The flow is in the reply direction, meaning it did not initiate the connection. This flag may only be present for committed connections. .IP "\fB0x10: inv\fR" The state is invalid, meaning that the connection tracker couldn't identify the connection. This flag is a catch-all for any problems that the connection tracker may have, for example: .RS .PP - L3/L4 protocol handler is not loaded/unavailable. With the Linux kernel datapath, this may mean that the "nf_conntrack_ipv4" or "nf_conntrack_ipv6" modules are not loaded. .PP - L3/L4 protocol handler determines that the packet is malformed. .PP - Packets are unexpected length for protocol. .RE .IP "\fB0x20: trk\fR" This packet is tracked, meaning that it has previously traversed the connection tracker. If this flag is not set, then no other flags will be set. If this flag is set, then the packet is tracked and other flags may also be set. .PP This field was introduced in Open vSwitch 2.5. .RE . .PP The following fields are associated with the connection tracker and will only be populated for tracked packets. The \fBct\fR action will populate these fields, and allows modification of some of the below fields. .IP \fBct_zone=\fIzone Matches the given 16-bit connection \fIzone\fR exactly. This represents the most recent connection tracking context that \fBct\fR was executed in. Each zone is an independent connection tracking context, so if you wish to track the same packet in multiple contexts then you must use the \fBct\fR action multiple times. Introduced in Open vSwitch 2.5. . .IP \fBct_mark=\fIvalue\fR[\fB/\fImask\fR] Matches the given 32-bit connection mark \fIvalue\fR either exactly or with optional \fImask\fR. This represents metadata associated with the connection that the current packet is part of. Introduced in Open vSwitch 2.5. . .IP \fBct_label=\fIvalue\fR[\fB/\fImask\fR] Matches the given 128-bit connection labels \fIvalue\fR either exactly or with optional \fImask\fR. This represents metadata associated with the connection that the current packet is part of. Introduced in Open vSwitch 2.5. . .PP Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires support for NXM. The following shorthand notations are available for IPv6-related flows: . .IP \fBipv6\fR Same as \fBdl_type=0x86dd\fR. . .IP \fBtcp6\fR Same as \fBdl_type=0x86dd,nw_proto=6\fR. . .IP \fBudp6\fR Same as \fBdl_type=0x86dd,nw_proto=17\fR. . .IP \fBsctp6\fR Same as \fBdl_type=0x86dd,nw_proto=132\fR. . .IP \fBicmp6\fR Same as \fBdl_type=0x86dd,nw_proto=58\fR. . .PP Finally, field assignments to \fBduration\fR, \fBn_packets\fR, or \fBn_bytes\fR are ignored to allow output from the \fBdump\-flows\fR command to be used as input for other commands that parse flows. . .PP The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands require an additional field, which must be the final field specified: . .IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR Specifies a comma-separated list of actions to take on a packet when the flow entry matches. If no \fIaction\fR is specified, then packets matching the flow are dropped. The following forms of \fIaction\fR are supported: . .RS .IP \fIport\fR .IQ \fBoutput:\fIport\fR Outputs the packet to OpenFlow port number \fIport\fR. If \fIport\fR is the packet's input port, the packet is not output. . .IP \fBoutput:\fIsrc\fB[\fIstart\fB..\fIend\fB] Outputs the packet to the OpenFlow port number read from \fIsrc\fR, which must be an NXM field as described above. For example, \fBoutput:NXM_NX_REG0[16..31]\fR outputs to the OpenFlow port number written in the upper half of register 0. If the port number is the packet's input port, the packet is not output. .IP This form of \fBoutput\fR was added in Open vSwitch 1.3.0. This form of \fBoutput\fR uses an OpenFlow extension that is not supported by standard OpenFlow switches. . .IP \fBgroup:\fIgroup_id\fR Outputs the packet to the OpenFlow group \fIgroup_id\fR. Group tables are only supported in OpenFlow 1.1+. See Group Syntax for more details. . .IP \fBnormal\fR Subjects the packet to the device's normal L2/L3 processing. (This action is not implemented by all OpenFlow switches.) . .IP \fBflood\fR Outputs the packet on all switch physical ports other than the port on which it was received and any ports on which flooding is disabled (typically, these would be ports disabled by the IEEE 802.1D spanning tree protocol). . .IP \fBall\fR Outputs the packet on all switch physical ports other than the port on which it was received. . .IP \fBlocal\fR Outputs the packet on the ``local port,'' which corresponds to the network device that has the same name as the bridge. . .IP \fBin_port\fR Outputs the packet on the port from which it was received. . .IP \fBcontroller(\fIkey\fB=\fIvalue\fR...\fB) Sends the packet to the OpenFlow controller as a ``packet in'' message. The supported key-value pairs are: .RS .IP "\fBmax_len=\fInbytes\fR" Limit to \fInbytes\fR the number of bytes of the packet to send to the controller. By default the entire packet is sent. .IP "\fBreason=\fIreason\fR" Specify \fIreason\fR as the reason for sending the message in the ``packet in'' message. The supported reasons are \fBaction\fR (the default), \fBno_match\fR, and \fBinvalid_ttl\fR. .IP "\fBid=\fIcontroller-id\fR" Specify \fIcontroller-id\fR, a 16-bit integer, as the connection ID of the OpenFlow controller or controllers to which the ``packet in'' message should be sent. The default is zero. Zero is also the default connection ID for each controller connection, and a given controller connection will only have a nonzero connection ID if its controller uses the \fBNXT_SET_CONTROLLER_ID\fR Nicira extension to OpenFlow. .RE .IP Any \fIreason\fR other than \fBaction\fR and any nonzero \fIcontroller-id\fR uses a Nicira vendor extension that, as of this writing, is only known to be implemented by Open vSwitch (version 1.6 or later). . .IP \fBcontroller\fR .IQ \fBcontroller\fR[\fB:\fInbytes\fR] Shorthand for \fBcontroller()\fR or \fBcontroller(max_len=\fInbytes\fB)\fR, respectively. . .IP \fBenqueue(\fIport\fB,\fIqueue\fB)\fR Enqueues the packet on the specified \fIqueue\fR within port \fIport\fR, which must be an OpenFlow port number or keyword (e.g. \fBLOCAL\fR). The number of supported queues depends on the switch; some OpenFlow implementations do not support queuing at all. . .IP \fBdrop\fR Discards the packet, so no further processing or forwarding takes place. If a drop action is used, no other actions may be specified. . .IP \fBmod_vlan_vid\fR:\fIvlan_vid\fR Modifies the VLAN id on a packet. The VLAN tag is added or modified as necessary to match the value specified. If the VLAN tag is added, a priority of zero is used (see the \fBmod_vlan_pcp\fR action to set this). . .IP \fBmod_vlan_pcp\fR:\fIvlan_pcp\fR Modifies the VLAN priority on a packet. The VLAN tag is added or modified as necessary to match the value specified. Valid values are between 0 (lowest) and 7 (highest). If the VLAN tag is added, a vid of zero is used (see the \fBmod_vlan_vid\fR action to set this). . .IP \fBstrip_vlan\fR Strips the VLAN tag from a packet if it is present. . .IP \fBpush_vlan\fR:\fIethertype\fR Push a new VLAN tag onto the packet. Ethertype is used as the Ethertype for the tag. Only ethertype 0x8100 should be used. (0x88a8 which the spec allows isn't supported at the moment.) A priority of zero and the tag of zero are used for the new tag. . .IP \fBpush_mpls\fR:\fIethertype\fR Changes the packet's Ethertype to \fIethertype\fR, which must be either \fB0x8847\fR or \fB0x8848\fR, and pushes an MPLS LSE. .IP If the packet does not already contain any MPLS labels then an initial label stack entry is pushed. The label stack entry's label is 2 if the packet contains IPv6 and 0 otherwise, its default traffic control value is the low 3 bits of the packet's DSCP value (0 if the packet is not IP), and its TTL is copied from the IP TTL (64 if the packet is not IP). .IP If the packet does already contain an MPLS label, pushes a new outermost label as a copy of the existing outermost label. .IP A limitation of the implementation is that processing of actions will stop if \fBpush_mpls\fR follows another \fBpush_mpls\fR unless there is a \fBpop_mpls\fR in between. . .IP \fBpop_mpls\fR:\fIethertype\fR Strips the outermost MPLS label stack entry. Currently the implementation restricts \fIethertype\fR to a non-MPLS Ethertype and thus \fBpop_mpls\fR should only be applied to packets with an MPLS label stack depth of one. A further limitation is that processing of actions will stop if \fBpop_mpls\fR follows another \fBpop_mpls\fR unless there is a \fBpush_mpls\fR in between. . .IP \fBmod_dl_src\fB:\fImac\fR Sets the source Ethernet address to \fImac\fR. . .IP \fBmod_dl_dst\fB:\fImac\fR Sets the destination Ethernet address to \fImac\fR. . .IP \fBmod_nw_src\fB:\fIip\fR Sets the IPv4 source address to \fIip\fR. . .IP \fBmod_nw_dst\fB:\fIip\fR Sets the IPv4 destination address to \fIip\fR. . .IP \fBmod_tp_src\fB:\fIport\fR Sets the TCP or UDP or SCTP source port to \fIport\fR. . .IP \fBmod_tp_dst\fB:\fIport\fR Sets the TCP or UDP or SCTP destination port to \fIport\fR. . .IP \fBmod_nw_tos\fB:\fItos\fR Sets the DSCP bits in the IPv4 ToS/DSCP or IPv6 traffic class field to \fItos\fR, which must be a multiple of 4 between 0 and 255. This action does not modify the two least significant bits of the ToS field (the ECN bits). . .IP \fBmod_nw_ecn\fB:\fIecn\fR Sets the ECN bits in the IPv4 ToS or IPv6 traffic class field to \fIecn\fR, which must be a value between 0 and 3, inclusive. This action does not modify the six most significant bits of the field (the DSCP bits). .IP Requires OpenFlow 1.1 or later. . .IP \fBmod_nw_ttl\fB:\fIttl\fR Sets the IPv4 TTL or IPv6 hop limit field to \fIttl\fR, which is specified as a decimal number between 0 and 255, inclusive. Switch behavior when setting \fIttl\fR to zero is not well specified, though. .IP Requires OpenFlow 1.1 or later. .RE .IP The following actions are Nicira vendor extensions that, as of this writing, are only known to be implemented by Open vSwitch: . .RS . .IP \fBresubmit\fB:\fIport\fR .IQ \fBresubmit\fB(\fR[\fIport\fR]\fB,\fR[\fItable\fR]\fB) Re-searches this OpenFlow flow table (or the table whose number is specified by \fItable\fR) with the \fBin_port\fR field replaced by \fIport\fR (if \fIport\fR is specified) and executes the actions found, if any, in addition to any other actions in this flow entry. .IP Recursive \fBresubmit\fR actions are obeyed up to an implementation-defined maximum depth. Open vSwitch 1.0.1 and earlier did not support recursion; Open vSwitch before 1.2.90 did not support \fItable\fR. . .IP \fBset_tunnel\fB:\fIid\fR .IQ \fBset_tunnel64\fB:\fIid\fR If outputting to a port that encapsulates the packet in a tunnel and supports an identifier (such as GRE), sets the identifier to \fIid\fR. If the \fBset_tunnel\fR form is used and \fIid\fR fits in 32 bits, then this uses an action extension that is supported by Open vSwitch 1.0 and later. Otherwise, if \fIid\fR is a 64-bit value, it requires Open vSwitch 1.1 or later. . .IP \fBset_queue\fB:\fIqueue\fR Sets the queue that should be used to \fIqueue\fR when packets are output. The number of supported queues depends on the switch; some OpenFlow implementations do not support queuing at all. . .IP \fBpop_queue\fR Restores the queue to the value it was before any \fBset_queue\fR actions were applied. . .IP \fBct\fR .IQ \fBct\fB(\fR[\fIargument\fR][\fB,\fIargument\fR...]\fB) Send the packet through the connection tracker. Refer to the \fBct_state\fR documentation above for possible packet and connection states. The following arguments are supported: .RS .IP \fBcommit\fR .RS Commit the connection to the connection tracking module. Information about the connection will be stored beyond the lifetime of the packet in the pipeline. Some \fBct_state\fR flags are only available for committed connections. .RE .IP \fBtable=\fInumber\fR Fork pipeline processing in two. The original instance of the packet will continue processing the current actions list as an untracked packet. An additional instance of the packet will be sent to the connection tracker, which will be re-injected into the OpenFlow pipeline to resume processing in table \fInumber\fR, with the \fBct_state\fR and other ct match fields set. If the \fBtable\fR is not specified, then the packet which is submitted to the connection tracker is not re-injected into the OpenFlow pipeline. It is strongly recommended to specify a table later than the current table to prevent loops. .IP \fBzone=\fIvalue\fR .IQ \fBzone=\fIsrc\fB[\fIstart\fB..\fIend\fB]\fR A 16-bit context id that can be used to isolate connections into separate domains, allowing overlapping network addresses in different zones. If a zone is not provided, then the default is to use zone zero. The \fBzone\fR may be specified either as an immediate 16-bit \fIvalue\fR, or may be provided from an NXM field \fIsrc\fR. The \fIstart\fR and \fIend\fR pair are inclusive, and must specify a 16-bit range within the field. This value is copied to the \fBct_zone\fR match field for packets which are re-injected into the pipeline using the \fBtable\fR option. .IP \fBexec\fB(\fR[\fIaction\fR][\fB,\fIaction\fR...]\fB)\fR Perform actions within the context of connection tracking. This is a restricted set of actions which are in the same format as their specifications as part of a flow. Only actions which modify the \fBct_mark\fR or \fBct_label\fR fields are accepted within the \fBexec\fR action, and these fields may only be modified with this option. For example: . .RS .IP \fBset_field:\fIvalue\fR[\fB/\fImask\fR]->ct_mark\fR Store a 32-bit metadata value with the connection. If the connection is committed, then subsequent lookups for packets in this connection will populate the \fBct_mark\fR flow field when the packet is sent to the connection tracker with the \fBtable\fR specified. .IP \fBset_field:\fIvalue\fR[\fB/\fImask\fR]->ct_label\fR Store a 128-bit metadata value with the connection. If the connection is committed, then subsequent lookups for packets in this connection will populate the \fBct_label\fR flow field when the packet is sent to the connection tracker with the \fBtable\fR specified. .RE .IP The \fBcommit\fR parameter should be specified to use \fBexec(...)\fR. . .IP \fBalg=\fIalg\fR Specify application layer gateway \fIalg\fR to track specific connection types. Supported types include: .RS .IP \fBftp\fR Look for negotiation of FTP data connections. If a subsequent FTP data connection arrives which is related, the \fBct\fR action will set the \fBrel\fR flag in the \fBct_state\fR field for packets sent through \fBct\fR. .RE . .IP When committing related connections, the \fBct_mark\fR for that connection is inherited from the current \fBct_mark\fR stored with the original connection (ie, the connection created by \fBct(alg=...)\fR). .RE .IP The \fBct\fR action may be used as a primitive to construct stateful firewalls by selectively committing some traffic, then matching the \fBct_state\fR to allow established connections while denying new connections. The following flows provide an example of how to implement a simple firewall that allows new connections from port 1 to port 2, and only allows established connections to send traffic from port 2 to port 1: \fBtable=0,priority=1,action=drop table=0,priority=10,arp,action=normal table=0,priority=100,ip,ct_state=-trk,action=ct(table=1) table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit),2 table=1,in_port=1,ip,ct_state=+trk+est,action=2 table=1,in_port=2,ip,ct_state=+trk+new,action=drop table=1,in_port=2,ip,ct_state=+trk+est,action=1\fR .IP If \fBct\fR is executed on IP (or IPv6) fragments, then the message is implicitly reassembled before sending to the connection tracker and refragmented upon \fBoutput\fR, to the original maximum received fragment size. Reassembly occurs within the context of the \fBzone\fR, meaning that IP fragments in different zones are not assembled together. Pipeline processing for the initial fragments is halted; When the final fragment is received, the message is assembled and pipeline processing will continue for that flow. Because packet ordering is not guaranteed by IP protocols, it is not possible to determine which IP fragment will cause message reassembly (and therefore continue pipeline processing). As such, it is strongly recommended that multiple flows should not execute \fBct\fR to reassemble fragments from the same IP message. .IP Currently, connection tracking is only available on Linux kernels with the nf_conntrack module loaded. The \fBct\fR action was introduced in Open vSwitch 2.5. . .IP \fBdec_ttl\fR .IQ \fBdec_ttl(\fIid1\fR[\fB,\fIid2\fR]...\fB)\fR Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the TTL or hop limit is initially zero or decrementing would make it so, no decrement occurs, as packets reaching TTL zero must be rejected. Instead, a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is sent to each connected controller that has enabled receiving them, if any. Processing the current set of actions then stops. However, if the current set of actions was reached through ``resubmit'' then remaining actions in outer levels resume processing. .IP This action also optionally supports the ability to specify a list of valid controller ids. Each of the controllers in the list will receive the ``packet_in'' message only if they have registered to receive the invalid ttl packets. If controller ids are not specified, the ``packet_in'' message will be sent only to the controllers having controller id zero which have registered for the invalid ttl packets. . .IP \fBset_mpls_label\fR:\fIlabel\fR Set the label of the outer MPLS label stack entry of a packet. \fIlabel\fR should be a 20-bit value that is decimal by default; use a \fB0x\fR prefix to specify them in hexadecimal. . .IP \fBset_mpls_tc\fR:\fItc\fR Set the traffic-class of the outer MPLS label stack entry of a packet. \fItc\fR should be a in the range 0 to 7 inclusive. . .IP \fBset_mpls_ttl\fR:\fIttl\fR Set the TTL of the outer MPLS label stack entry of a packet. \fIttl\fR should be in the range 0 to 255 inclusive. . .IP \fBdec_mpls_ttl\fR Decrement TTL of the outer MPLS label stack entry of a packet. If the TTL is initially zero or decrementing would make it so, no decrement occurs. Instead, a ``packet-in'' message with reason code \fBOFPR_INVALID_TTL\fR is sent to the main controller (id zero), if it has enabled receiving them. Processing the current set of actions then stops. However, if the current set of actions was reached through ``resubmit'' then remaining actions in outer levels resume processing. . .IP \fBnote:\fR[\fIhh\fR]... Does nothing at all. Any number of bytes represented as hex digits \fIhh\fR may be included. Pairs of hex digits may be separated by periods for readability. The \fBnote\fR action's format doesn't include an exact length for its payload, so the provided bytes will be padded on the right by enough bytes with value 0 to make the total number 6 more than a multiple of 8. . .IP "\fBmove:\fIsrc\fB[\fIstart\fB..\fIend\fB]\->\fIdst\fB[\fIstart\fB..\fIend\fB]\fR" Copies the named bits from field \fIsrc\fR to field \fIdst\fR. \fIsrc\fR and \fIdst\fR must be NXM field names as defined in \fBnicira\-ext.h\fR, e.g. \fBNXM_OF_UDP_SRC\fR or \fBNXM_NX_REG0\fR. Each \fIstart\fR and \fIend\fR pair, which are inclusive, must specify the same number of bits and must fit within its respective field. Shorthands for \fB[\fIstart\fB..\fIend\fB]\fR exist: use \fB[\fIbit\fB]\fR to specify a single bit or \fB[]\fR to specify an entire field. .IP Examples: \fBmove:NXM_NX_REG0[0..5]\->NXM_NX_REG1[26..31]\fR copies the six bits numbered 0 through 5, inclusive, in register 0 into bits 26 through 31, inclusive; \fBmove:NXM_NX_REG0[0..15]\->NXM_OF_VLAN_TCI[]\fR copies the least significant 16 bits of register 0 into the VLAN TCI field. .IP In OpenFlow 1.0 through 1.4, \fBmove\fR ordinarily uses an Open vSwitch extension to OpenFlow. In OpenFlow 1.5, \fBmove\fR uses the OpenFlow 1.5 standard \fBcopy_field\fR action. The ONF has also made \fBcopy_field\fR available as an extension to OpenFlow 1.3. Open vSwitch 2.4 and later understands this extension and uses it if a controller uses it, but for backward compatibility with older versions of Open vSwitch, \fBovs\-ofctl\fR does not use it. . .IP "\fBset_field:\fIvalue\fR[/\fImask\fR]\fB\->\fIdst" .IQ "\fBload:\fIvalue\fB\->\fIdst\fB[\fIstart\fB..\fIend\fB]" Loads a literal value into a field or part of a field. With \fBset_field\fR, \fBvalue\fR and the optional \fBmask\fR are given in the customary syntax for field \fIdst\fR, which is expressed as a field name. For example, \fBset_field:00:11:22:33:44:55->eth_src\fR sets the Ethernet source address to 00:11:22:33:44:55. With \fBload\fR, \fIvalue\fR must be an integer value (in decimal or prefixed by \fB0x\fR for hexadecimal) and \fIdst\fR is the NXM or OXM name for the field. For example, \fBload:0x001122334455->OXM_OF_ETH_DST[]\fR has the same effect as the prior \fBset_field\fR example. .IP The two forms exist for historical reasons. Open vSwitch 1.1 introduced \fBNXAST_REG_LOAD\fR as a Nicira extension to OpenFlow 1.0 and used \fBload\fR to express it. Later, OpenFlow 1.2 introduced a standard \fBOFPAT_SET_FIELD\fR action that was restricted to loading entire fields, so Open vSwitch added the form \fBset_field\fR with this restriction. OpenFlow 1.5 extended \fBOFPAT_SET_FIELD\fR to the point that it became a superset of \fBNXAST_REG_LOAD\fR. Open vSwitch translates either syntax as necessary for the OpenFlow version in use: in OpenFlow 1.0 and 1.1, \fBNXAST_REG_LOAD\fR; in OpenFlow 1.2, 1.3, and 1.4, \fBNXAST_REG_LOAD\fR for \fBload\fR or for loading a subfield, \fBOFPAT_SET_FIELD\fR otherwise; and OpenFlow 1.5 and later, \fBOFPAT_SET_FIELD\fR. . .IP "\fBpush:\fIsrc\fB[\fIstart\fB..\fIend\fB]" Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields on top of the stack. .IP Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register 2 bits 0 through 5, inclusive, on to the internal stack. . .IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]" Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits inclusive, from the value popped and store them into the corresponding bits in \fIdst\fR. . .IP Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack. Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the value just popped. . . .IP "\fBmultipath(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIn_links\fB, \fIarg\fB, \fIdst\fB[\fIstart\fB..\fIend\fB])\fR" Hashes \fIfields\fR using \fIbasis\fR as a universal hash parameter, then the applies multipath link selection \fIalgorithm\fR (with parameter \fIarg\fR) to choose one of \fIn_links\fR output links numbered 0 through \fIn_links\fR minus 1, and stores the link into \fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as described above. .IP \fIfields\fR must be one of the following: .RS .IP \fBeth_src\fR Hashes Ethernet source address only. .IP \fBsymmetric_l4\fR Hashes Ethernet source, destination, and type, VLAN ID, IPv4/IPv6 source, destination, and protocol, and TCP or SCTP (but not UDP) ports. The hash is computed so that pairs of corresponding flows in each direction hash to the same value, in environments where L2 paths are the same in each direction. UDP ports are not included in the hash to support protocols such as VXLAN that use asymmetric ports in each direction. .IP \fBsymmetric_l3l4\fR Hashes IPv4/IPv6 source, destination, and protocol, and TCP or SCTP (but not UDP) ports. Like \fBsymmetric_l4\fR, this is a symmetric hash, but by excluding L2 headers it is more effective in environments with asymmetric L2 paths (e.g. paths involving VRRP IP addresses on a router). Not an effective hash function for protocols other than IPv4 and IPv6, which hash to a constant zero. .IP \fBsymmetric_l3l4+udp\fR Like \fBsymmetric_l3l4+udp\fR, but UDP ports are included in the hash. This is a more effective hash when asymmetric UDP protocols such as VXLAN are not a consideration. .RE .IP \fIalgorithm\fR must be one of \fBmodulo_n\fR, \fBhash_threshold\fR, \fBhrw\fR, and \fBiter_hash\fR. Only the \fBiter_hash\fR algorithm uses \fIarg\fR. .IP Refer to \fBnicira\-ext.h\fR for more details. . .IP "\fBbundle(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, slaves:[\fIs1\fB, \fIs2\fB, ...])\fR" Hashes \fIfields\fR using \fIbasis\fR as a universal hash parameter, then applies the bundle link selection \fIalgorithm\fR to choose one of the listed slaves represented as \fIslave_type\fR. Currently the only supported \fIslave_type\fR is \fBofport\fR. Thus, each \fIs1\fR through \fIsN\fR should be an OpenFlow port number. Outputs to the selected slave. .IP Currently, \fIfields\fR must be either \fBeth_src\fR, \fBsymmetric_l4\fR, \fBsymmetric_l3l4\fR, or \fBsymmetric_l3l4+udp\fR, and \fIalgorithm\fR must be one of \fBhrw\fR and \fBactive_backup\fR. .IP Example: \fBbundle(eth_src,0,hrw,ofport,slaves:4,8)\fR uses an Ethernet source hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest Random Weight algorithm. .IP Refer to \fBnicira\-ext.h\fR for more details. . .IP "\fBbundle_load(\fIfields\fB, \fIbasis\fB, \fIalgorithm\fB, \fIslave_type\fB, \fIdst\fB[\fIstart\fB..\fIend\fB], slaves:[\fIs1\fB, \fIs2\fB, ...])\fR" Has the same behavior as the \fBbundle\fR action, with one exception. Instead of outputting to the selected slave, it writes its selection to \fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as described above. .IP Example: \fBbundle_load(eth_src, 0, hrw, ofport, NXM_NX_REG0[], slaves:4, 8)\fR uses an Ethernet source hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest Random Weight algorithm, and writes the selection to \fBNXM_NX_REG0[]\fR. .IP Refer to \fBnicira\-ext.h\fR for more details. . .IP "\fBlearn(\fIargument\fR[\fB,\fIargument\fR]...\fB)\fR" This action adds or modifies a flow in an OpenFlow table, similar to \fBovs\-ofctl \-\-strict mod\-flows\fR. The arguments specify the flow's match fields, actions, and other properties, as follows. At least one match criterion and one action argument should ordinarily be specified. .RS .IP \fBidle_timeout=\fIseconds\fR .IQ \fBhard_timeout=\fIseconds\fR .IQ \fBpriority=\fIvalue\fR .IQ \fBcookie=\fIvalue\fR .IQ \fBsend_flow_rem\fR These arguments have the same meaning as in the usual \fBovs\-ofctl\fR flow syntax. . .IP \fBfin_idle_timeout=\fIseconds\fR .IQ \fBfin_hard_timeout=\fIseconds\fR Adds a \fBfin_timeout\fR action with the specified arguments to the new flow. This feature was added in Open vSwitch 1.5.90. . .IP \fBtable=\fInumber\fR The table in which the new flow should be inserted. Specify a decimal number between 0 and 254. The default, if \fBtable\fR is unspecified, is table 1. . .IP \fBdelete_learned\fR This flag enables deletion of the learned flows when the flow with the \fBlearn\fR action is removed. Specifically, when the last \fBlearn\fR action with this flag and particular \fBtable\fR and \fBcookie\fR values is removed, the switch deletes all of the flows in the specified table with the specified cookie. . .IP This flag was added in Open vSwitch 2.4. . .IP \fIfield\fB=\fIvalue\fR .IQ \fIfield\fB[\fIstart\fB..\fIend\fB]=\fIsrc\fB[\fIstart\fB..\fIend\fB]\fR .IQ \fIfield\fB[\fIstart\fB..\fIend\fB]\fR Adds a match criterion to the new flow. .IP The first form specifies that \fIfield\fR must match the literal \fIvalue\fR, e.g. \fBdl_type=0x0800\fR. All of the fields and values for \fBovs\-ofctl\fR flow syntax are available with their usual meanings. .IP The second form specifies that \fIfield\fB[\fIstart\fB..\fIend\fB]\fR in the new flow must match \fIsrc\fB[\fIstart\fB..\fIend\fB]\fR taken from the flow currently being processed. .IP The third form is a shorthand for the second form. It specifies that \fIfield\fB[\fIstart\fB..\fIend\fB]\fR in the new flow must match \fIfield\fB[\fIstart\fB..\fIend\fB]\fR taken from the flow currently being processed. . .IP \fBload:\fIvalue\fB\->\fIdst\fB[\fIstart\fB..\fIend\fB] .IQ \fBload:\fIsrc\fB[\fIstart\fB..\fIend\fB]\->\fIdst\fB[\fIstart\fB..\fIend\fB] . Adds a \fBload\fR action to the new flow. .IP The first form loads the literal \fIvalue\fR into bits \fIstart\fR through \fIend\fR, inclusive, in field \fIdst\fR. Its syntax is the same as the \fBload\fR action described earlier in this section. .IP The second form loads \fIsrc\fB[\fIstart\fB..\fIend\fB]\fR, a value from the flow currently being processed, into bits \fIstart\fR through \fIend\fR, inclusive, in field \fIdst\fR. . .IP \fBoutput:\fIfield\fB[\fIstart\fB..\fIend\fB]\fR Add an \fBoutput\fR action to the new flow's actions, that outputs to the OpenFlow port taken from \fIfield\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as described above. .RE .IP For best performance, segregate learned flows into a table (using \fBtable=\fInumber\fR) that is not used for any other flows except possibly for a lowest-priority ``catch-all'' flow, that is, a flow with no match criteria. (This is why the default \fBtable\fR is 1, to keep the learned flows separate from the primary flow table 0.) .RE . .RS . .IP \fBclear_actions\fR Clears all the actions in the action set immediately. . .IP \fBwrite_actions(\fR[\fIaction\fR][\fB,\fIaction\fR...]\fB) Add the specific actions to the action set. The syntax of \fIactions\fR is the same as in the \fBactions=\fR field. The action set is carried between flow tables and then executed at the end of the pipeline. . .IP The actions in the action set are applied in the following order, as required by the OpenFlow specification, regardless of the order in which they were added to the action set. Except as specified otherwise below, the action set only holds at most a single action of each type. When more than one action of a single type is written to the action set, the one written later replaces the earlier action: . .RS .IP 1. \fBstrip_vlan\fR .IQ \fBpop_mpls\fR . .IP 2. \fBpush_mpls\fR . .IP 3. \fBpush_vlan\fR . .IP 4. \fBdec_ttl\fR .IQ \fBdec_mpls_ttl\fR . .IP 5. \fBload\fR .IQ \fBmove\fR .IQ \fBmod_dl_dst\fR .IQ \fBmod_dl_src\fR .IQ \fBmod_nw_dst\fR .IQ \fBmod_nw_src\fR .IQ \fBmod_nw_tos\fR .IQ \fBmod_nw_ecn\fR .IQ \fBmod_nw_ttl\fR .IQ \fBmod_tp_dst\fR .IQ \fBmod_tp_src\fR .IQ \fBmod_vlan_pcp\fR .IQ \fBmod_vlan_vid\fR .IQ \fBset_field\fR .IQ \fBset_tunnel\fR .IQ \fBset_tunnel64\fR .IQ The action set can contain any number of these actions, with cumulative effect. They will be applied in the order as added. That is, when multiple actions modify the same part of a field, the later modification takes effect, and when they modify different parts of a field (or different fields), then both modifications are applied. . .IP 6. \fBset_queue\fR . .IP 7. \fBgroup\fR .IQ \fBoutput\fR .IQ \fBresubmit\fR .IQ If more than one of these actions is present, then the one listed earliest above is executed and the others are ignored, regardless of the order in which they were added to the action set. (If none of these actions is present, the action set has no real effect, because the modified packet is not sent anywhere and thus the modifications are not visible.) .RE .IP Only the actions listed above may be written to the action set. . .IP \fBwrite_metadata\fB:\fIvalue\fR[/\fImask\fR] Updates the metadata field for the flow. If \fImask\fR is omitted, the metadata field is set exactly to \fIvalue\fR; if \fImask\fR is specified, then a 1-bit in \fImask\fR indicates that the corresponding bit in the metadata field will be replaced with the corresponding bit from \fIvalue\fR. Both \fIvalue\fR and \fImask\fR are 64-bit values that are decimal by default; use a \fB0x\fR prefix to specify them in hexadecimal. . .IP \fBmeter\fR:\fImeter_id\fR Apply the \fImeter_id\fR before any other actions. If a meter band rate is exceeded, the packet may be dropped, or modified, depending on the meter band type. See the description of the \fBMeter Table Commands\fR, above, for more details. . .IP \fBgoto_table\fR:\fItable\fR Indicates the next table in the process pipeline. . .IP "\fBfin_timeout(\fIargument\fR[\fB,\fIargument\fR]\fB)" This action changes the idle timeout or hard timeout, or both, of this OpenFlow rule when the rule matches a TCP packet with the FIN or RST flag. When such a packet is observed, the action reduces the rule's timeouts to those specified on the action. If the rule's existing timeout is already shorter than the one that the action specifies, then that timeout is unaffected. .IP \fIargument\fR takes the following forms: .RS .IP "\fBidle_timeout=\fIseconds\fR" Causes the flow to expire after the given number of seconds of inactivity. . .IP "\fBhard_timeout=\fIseconds\fR" Causes the flow to expire after the given number of seconds, regardless of activity. (\fIseconds\fR specifies time since the flow's creation, not since the receipt of the FIN or RST.) .RE .IP This action was added in Open vSwitch 1.5.90. . .IP "\fBsample(\fIargument\fR[\fB,\fIargument\fR]...\fB)\fR" Samples packets and sends one sample for every sampled packet. .IP \fIargument\fR takes the following forms: .RS .IP "\fBprobability=\fIpackets\fR" The number of sampled packets out of 65535. Must be greater or equal to 1. .IP "\fBcollector_set_id=\fIid\fR" The unsigned 32-bit integer identifier of the set of sample collectors to send sampled packets to. Defaults to 0. .IP "\fBobs_domain_id=\fIid\fR" When sending samples to IPFIX collectors, the unsigned 32-bit integer Observation Domain ID sent in every IPFIX flow record. Defaults to 0. .IP "\fBobs_point_id=\fIid\fR" When sending samples to IPFIX collectors, the unsigned 32-bit integer Observation Point ID sent in every IPFIX flow record. Defaults to 0. .RE .IP Refer to \fBovs\-vswitchd.conf.db\fR(8) for more details on configuring sample collector sets. .IP This action was added in Open vSwitch 1.10.90. . .IP "\fBexit\fR" This action causes Open vSwitch to immediately halt execution of further actions. Those actions which have already been executed are unaffected. Any further actions, including those which may be in other tables, or different levels of the \fBresubmit\fR call stack, are ignored. Actions in the action set is still executed (specify \fBclear_actions\fR before \fBexit\fR to discard them). . .IP "\fBconjunction(\fIid\fB, \fIk\fB/\fIn\fR\fB)\fR" An individual OpenFlow flow can match only a single value for each field. However, situations often arise where one wants to match one of a set of values within a field or fields. For matching a single field against a set, it is straightforward and efficient to add multiple flows to the flow table, one for each value in the set. For example, one might use the following flows to send packets with IP source address \fIa\fR, \fIb\fR, \fIc\fR, or \fId\fR to the OpenFlow controller: .RS +1in .br \fBip,ip_src=\fIa\fB actions=controller\fR .br \fBip,ip_src=\fIb\fB actions=controller\fR .br \fBip,ip_src=\fIc\fB actions=controller\fR .br \fBip,ip_src=\fId\fB actions=controller\fR .br .RE .IP Similarly, these flows send packets with IP destination address \fIe\fR, \fIf\fR, \fIg\fR, or \fIh\fR to the OpenFlow controller: .RS +1in .br \fBip,ip_dst=\fIe\fB actions=controller\fR .br \fBip,ip_dst=\fIf\fB actions=controller\fR .br \fBip,ip_dst=\fIg\fB actions=controller\fR .br \fBip,ip_dst=\fIh\fB actions=controller\fR .br .RE .IP Installing all of the above flows in a single flow table yields a disjunctive effect: a packet is sent to the controller if \fBip_src\fR \[mo] {\fIa\fR,\fIb\fR,\fIc\fR,\fId\fR} or \fBip_dst\fR \[mo] {\fIe\fR,\fIf\fR,\fIg\fR,\fIh\fR} (or both). (Pedantically, if both of the above sets of flows are present in the flow table, they should have different priorities, because OpenFlow says that the results are undefined when two flows with same priority can both match a single packet.) .IP Suppose, on the other hand, one wishes to match conjunctively, that is, to send a packet to the controller only if both \fBip_src\fR \[mo] {\fIa\fR,\fIb\fR,\fIc\fR,\fId\fR} and \fBip_dst\fR \[mo] {\fIe\fR,\fIf\fR,\fIg\fR,\fIh\fR}. This requires 4 \[mu] 4 = 16 flows, one for each possible pairing of \fBip_src\fR and \fBip_dst\fR. That is acceptable for our small example, but it does not gracefully extend to larger sets or greater numbers of dimensions. .IP The \fBconjunction\fR action is a solution for conjunctive matches that is built into Open vSwitch. A \fBconjunction\fR action ties groups of individual OpenFlow flows into higher-level ``conjunctive flows''. Each group corresponds to one dimension, and each flow within the group matches one possible value for the dimension. A packet that matches one flow from each group matches the conjunctive flow. .IP To implement a conjunctive flow with \fBconjunction\fR, assign the conjunctive flow a 32-bit \fIid\fR, which must be unique within an OpenFlow table. Assign each of the \fIn\fR \[>=] 2 dimensions a unique number from 1 to \fIn\fR; the ordering is unimportant. Add one flow to the OpenFlow flow table for each possible value of each dimension with \fBconjunction(\fIid, \fIk\fB/\fIn\fB)\fR as the flow's actions, where \fIk\fR is the number assigned to the flow's dimension. Together, these flows specify the conjunctive flow's match condition. When the conjunctive match condition is met, Open vSwitch looks up one more flow that specifies the conjunctive flow's actions and receives its statistics. This flow is found by setting \fBconj_id\fR to the specified \fIid\fR and then again searching the flow table. .IP The following flows provide an example. Whenever the IP source is one of the values in the flows that match on the IP source (dimension 1 of 2), \fIand\fR the IP destination is one of the values in the flows that match on IP destination (dimension 2 of 2), Open vSwitch searches for a flow that matches \fBconj_id\fR against the conjunction ID (1234), finding the first flow listed below. .RS +1in .br .B "conj_id=1234 actions=controller" .br .B "ip,ip_src=10.0.0.1 actions=conjunction(1234, 1/2)" .br .B "ip,ip_src=10.0.0.4 actions=conjunction(1234, 1/2)" .br .B "ip,ip_src=10.0.0.6 actions=conjunction(1234, 1/2)" .br .B "ip,ip_src=10.0.0.7 actions=conjunction(1234, 1/2)" .br .B "ip,ip_dst=10.0.0.2 actions=conjunction(1234, 2/2)" .br .B "ip,ip_dst=10.0.0.5 actions=conjunction(1234, 2/2)" .br .B "ip,ip_dst=10.0.0.7 actions=conjunction(1234, 2/2)" .br .B "ip,ip_dst=10.0.0.8 actions=conjunction(1234, 2/2)" .RE .IP Many subtleties exist: .RS .IP \(bu In the example above, every flow in a single dimension has the same form, that is, dimension 1 matches on \fBip_src\fR, dimension 2 on \fBip_dst\fR, but this is not a requirement. Different flows within a dimension may match on different bits within a field (e.g. IP network prefixes of different lengths, or TCP/UDP port ranges as bitwise matches), or even on entirely different fields (e.g. to match packets for TCP source port 80 or TCP destination port 80). .IP \(bu The flows within a dimension can vary their matches across more than one field, e.g. to match only specific pairs of IP source and destination addresses or L4 port numbers. .IP \(bu A flow may have multiple \fBconjunction\fR actions, with different \fIid\fR values. This is useful for multiple conjunctive flows with overlapping sets. If one conjunctive flow matches packets with both \fBip_src\fR \[mo] {\fIa\fR,\fIb\fR} and \fBip_dst\fR \[mo] {\fId\fR,\fIe\fR} and a second conjunctive flow matches \fBip_src\fR \[mo] {\fIb\fR,\fIc\fR} and \fBip_dst\fR \[mo] {\fIf\fR,\fIg\fR}, for example, then the flow that matches \fBip_src=\fIb\fR would have two \fBconjunction\fR actions, one for each conjunctive flow. The order of \fBconjunction\fR actions within a list of actions is not significant. .IP \(bu A flow with \fBconjunction\fR actions may also include \fBnote\fR actions for annotations, but not any other kind of actions. (They would not be useful because they would never be executed.) .IP \(bu All of the flows that constitute a conjunctive flow with a given \fIid\fR must have the same priority. (Flows with the same \fIid\fR but different priorities are currently treated as different conjunctive flows, that is, currently \fIid\fR values need only be unique within an OpenFlow table at a given priority. This behavior isn't guaranteed to stay the same in later releases, so please use \fIid\fR values unique within an OpenFlow table.) .IP \(bu Conjunctive flows must not overlap with each other, at a given priority, that is, any given packet must be able to match at most one conjunctive flow at a given priority. Overlapping conjunctive flows yield unpredictable results. .IP \(bu Following a conjunctive flow match, the search for the flow with \fBconj_id=\fIid\fR is done in the same general-purpose way as other flow table searches, so one can use flows with \fBconj_id=\fIid\fR to act differently depending on circumstances. (One exception is that the search for the \fBconj_id=\fIid\fR flow itself ignores conjunctive flows, to avoid recursion.) If the search with \fBconj_id=\fIid\fR fails, Open vSwitch acts as if the conjunctive flow had not matched at all, and continues searching the flow table for other matching flows. .IP \(bu OpenFlow prerequisite checking occurs for the flow with \fBconj_id=\fIid\fR in the same way as any other flow, e.g. in an OpenFlow 1.1+ context, putting a \fBmod_nw_src\fR action into the example above would require adding an \fBip\fR match, like this: .RS +1in .br .B "conj_id=1234,ip actions=mod_nw_src:1.2.3.4,controller" .br .RE .IP \(bu OpenFlow prerequisite checking also occurs for the individual flows that comprise a conjunctive match in the same way as any other flow. .IP \(bu The flows that constitute a conjunctive flow do not have useful statistics. They are never updated with byte or packet counts, and so on. (For such a flow, therefore, the idle and hard timeouts work much the same way.) .IP \(bu Conjunctive flows can be a useful building block for negation, that is, inequality matches like \fBtcp_src\fR \[!=] 80. To implement an inequality match, convert it to a pair of range matches, e.g. 0 \[<=] \fBtcp_src\ < 80 and 80 < \fBtcp_src\fR \[<=] 65535, then convert each of the range matches into a collection of bitwise matches as explained above in the description of \fBtcp_src\fR. .IP \(bu Sometimes there is a choice of which flows include a particular match. For example, suppose that we added an extra constraint to our example, to match on \fBip_src\fR \[mo] {\fIa\fR,\fIb\fR,\fIc\fR,\fId\fR} and \fBip_dst\fR \[mo] {\fIe\fR,\fIf\fR,\fIg\fR,\fIh\fR} and \fBtcp_dst\fR = \fIi\fR. One way to implement this is to add the new constraint to the \fBconj_id\fR flow, like this: .RS +1in .br \fBconj_id=1234,tcp,tcp_dst=\fIi\fB actions=mod_nw_src:1.2.3.4,controller\fR .br .RE .IP \fIbut this is not recommended\fR because of the cost of the extra flow table lookup. Instead, add the constraint to the individual flows, either in one of the dimensions or (slightly better) all of them. .IP \(bu A conjunctive match must have \fIn\fR \[>=] 2 dimensions (otherwise a conjunctive match is not necessary). Open vSwitch enforces this. .IP \(bu Each dimension within a conjunctive match should ordinarily have more than one flow. Open vSwitch does not enforce this. .RE .IP The \fBconjunction\fR action and \fBconj_id\fR field were introduced in Open vSwitch 2.4. .RE . .PP An opaque identifier called a cookie can be used as a handle to identify a set of flows: . .IP \fBcookie=\fIvalue\fR . A cookie can be associated with a flow using the \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands. \fIvalue\fR can be any 64-bit number and need not be unique among flows. If this field is omitted, a default cookie value of 0 is used. . .IP \fBcookie=\fIvalue\fR\fB/\fImask\fR . When using NXM, the cookie can be used as a handle for querying, modifying, and deleting flows. \fIvalue\fR and \fImask\fR may be supplied for the \fBdel\-flows\fR, \fBmod\-flows\fR, \fBdump\-flows\fR, and \fBdump\-aggregate\fR commands to limit matching cookies. A 1-bit in \fImask\fR indicates that the corresponding bit in \fIcookie\fR must match exactly, and a 0-bit wildcards that bit. A mask of \-1 may be used to exactly match a cookie. .IP The \fBmod\-flows\fR command can update the cookies of flows that match a cookie by specifying the \fIcookie\fR field twice (once with a mask for matching and once without to indicate the new value): .RS .IP "\fBovs\-ofctl mod\-flows br0 cookie=1,actions=normal\fR" Change all flows' cookies to 1 and change their actions to \fBnormal\fR. .IP "\fBovs\-ofctl mod\-flows br0 cookie=1/\-1,cookie=2,actions=normal\fR" Update cookies with a value of 1 to 2 and change their actions to \fBnormal\fR. .RE .IP The ability to match on cookies was added in Open vSwitch 1.5.0. . .PP The following additional field sets the priority for flows added by the \fBadd\-flow\fR and \fBadd\-flows\fR commands. For \fBmod\-flows\fR and \fBdel\-flows\fR when \fB\-\-strict\fR is specified, priority must match along with the rest of the flow specification. For \fBmod-flows\fR without \fB\-\-strict\fR, priority is only significant if the command creates a new flow, that is, non-strict \fBmod\-flows\fR does not match on priority and will not change the priority of existing flows. Other commands do not allow priority to be specified. . .IP \fBpriority=\fIvalue\fR The priority at which a wildcarded entry will match in comparison to others. \fIvalue\fR is a number between 0 and 65535, inclusive. A higher \fIvalue\fR will match before a lower one. An exact-match entry will always have priority over an entry containing wildcards, so it has an implicit priority value of 65535. When adding a flow, if the field is not specified, the flow's priority will default to 32768. .IP OpenFlow leaves behavior undefined when two or more flows with the same priority can match a single packet. Some users expect ``sensible'' behavior, such as more specific flows taking precedence over less specific flows, but OpenFlow does not specify this and Open vSwitch does not implement it. Users should therefore take care to use priorities to ensure the behavior that they expect. . .PP The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands support the following additional options. These options affect only new flows. Thus, for \fBadd\-flow\fR and \fBadd\-flows\fR, these options are always significant, but for \fBmod\-flows\fR they are significant only if the command creates a new flow, that is, their values do not update or affect existing flows. . .IP "\fBidle_timeout=\fIseconds\fR" Causes the flow to expire after the given number of seconds of inactivity. A value of 0 (the default) prevents a flow from expiring due to inactivity. . .IP \fBhard_timeout=\fIseconds\fR Causes the flow to expire after the given number of seconds, regardless of activity. A value of 0 (the default) gives the flow no hard expiration deadline. . .IP "\fBimportance=\fIvalue\fR" Sets the importance of a flow. The flow entry eviction mechanism can use importance as a factor in deciding which flow to evict. A value of 0 (the default) makes the flow non-evictable on the basis of importance. Specify a value between 0 and 65535. .IP Only OpenFlow 1.4 and later support \fBimportance\fR. . .IP "\fBsend_flow_rem\fR" Marks the flow with a flag that causes the switch to generate a ``flow removed'' message and send it to interested controllers when the flow later expires or is removed. . .IP "\fBcheck_overlap\fR" Forces the switch to check that the flow match does not overlap that of any different flow with the same priority in the same table. (This check is expensive so it is best to avoid it.) . .PP The \fBdump\-flows\fR, \fBdump\-aggregate\fR, \fBdel\-flow\fR and \fBdel\-flows\fR commands support these additional optional fields: . .TP \fBout_port=\fIport\fR If set, a matching flow must include an output action to \fIport\fR, which must be an OpenFlow port number or name (e.g. \fBlocal\fR). . .TP \fBout_group=\fIport\fR If set, a matching flow must include an \fBgroup\fR action naming \fIgroup\fR, which must be an OpenFlow group number. This field is supported in Open vSwitch 2.5 and later and requires OpenFlow 1.1 or later. . .SS "Table Entry Output" . The \fBdump\-tables\fR and \fBdump\-aggregate\fR commands print information about the entries in a datapath's tables. Each line of output is a flow entry as described in \fBFlow Syntax\fR, above, plus some additional fields: . .IP \fBduration=\fIsecs\fR The time, in seconds, that the entry has been in the table. \fIsecs\fR includes as much precision as the switch provides, possibly to nanosecond resolution. . .IP \fBn_packets\fR The number of packets that have matched the entry. . .IP \fBn_bytes\fR The total number of bytes from packets that have matched the entry. . .PP The following additional fields are included only if the switch is Open vSwitch 1.6 or later and the NXM flow format is used to dump the flow (see the description of the \fB\-\-flow-format\fR option below). The values of these additional fields are approximations only and in particular \fBidle_age\fR will sometimes become nonzero even for busy flows. . .IP \fBhard_age=\fIsecs\fR The integer number of seconds since the flow was added or modified. \fBhard_age\fR is displayed only if it differs from the integer part of \fBduration\fR. (This is separate from \fBduration\fR because \fBmod\-flows\fR restarts the \fBhard_timeout\fR timer without zeroing \fBduration\fR.) . .IP \fBidle_age=\fIsecs\fR The integer number of seconds that have passed without any packets passing through the flow. . .SS "Group Syntax" .PP Some \fBovs\-ofctl\fR commands accept an argument that describes a group or groups. Such flow descriptions comprise a series \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a group description normally requires quoting to prevent the shell from breaking the description into multiple arguments.). Unless noted otherwise only the last instance of each field is honoured. .PP .IP \fBgroup_id=\fIid\fR The integer group id of group. When this field is specified in \fBdel\-groups\fR or \fBdump\-groups\fR, the keyword "all" may be used to designate all groups. . This field is required. .IP \fBtype=\fItype\fR The type of the group. The \fBadd-group\fR, \fBadd-groups\fR and \fBmod-groups\fR commands require this field. It is prohibited for other commands. The following keywords designated the allowed types: .RS .IP \fBall\fR Execute all buckets in the group. .IP \fBselect\fR Execute one bucket in the group. The switch should select the bucket in such a way that should implement equal load sharing is achieved. The switch may optionally select the bucket based on bucket weights. .IP \fBindirect\fR Executes the one bucket in the group. .IP \fBff\fR .IQ \fBfast_failover\fR Executes the first live bucket in the group which is associated with a live port or group. .RE .IP \fBcommand_bucket_id=\fIid\fR The bucket to operate on. The \fBinsert-buckets\fR and \fBremove-buckets\fR commands require this field. It is prohibited for other commands. \fIid\fR may be an integer or one of the following keywords: .RS .IP \fBall\fR Operate on all buckets in the group. Only valid when used with the \fBremove-buckets\fR command in which case the effect is to remove all buckets from the group. .IP \fBfirst\fR Operate on the first bucket present in the group. In the case of the \fBinsert-buckets\fR command the effect is to insert new bucets just before the first bucket already present in the group; or to replace the buckets of the group if there are no buckets already present in the group. In the case of the \fBremove-buckets\fR command the effect is to remove the first bucket of the group; or do nothing if there are no buckets present in the group. .IP \fBlast\fR Operate on the last bucket present in the group. In the case of the \fBinsert-buckets\fR command the effect is to insert new bucets just after the last bucket already present in the group; or to replace the buckets of the group if there are no buckets already present in the group. In the case of the \fBremove-buckets\fR command the effect is to remove the last bucket of the group; or do nothing if there are no buckets present in the group. .RE .IP If \fIid\fR is an integer then it should correspond to the \fBbucket_id\fR of a bucket present in the group. In case of the \fBinsert-buckets\fR command the effect is to insert buckets just before the bucket in the group whose \fBbucket_id\fR is \fIid\fR. In case of the \fBiremove-buckets\fR command the effect is to remove the in the group whose \fBbucket_id\fR is \fIid\fR. It is an error if there is no bucket persent group in whose \fBbucket_id\fR is \fIid\fR. .IP \fBselection_method\fR=\fImethod\fR The selection method used to select a bucket for a select group. This is a string of 1 to 15 bytes in length known to lower layers. This field is optional for \fBadd\-group\fR, \fBadd\-groups\fR and \fBmod\-group\fR commands on groups of type \fBselect\fR. Prohibited otherwise. The default value is the empty string. .IP Other than the empty string, \fBhash\fR is currently the only defined selection method. .IP This option will use a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBselection_method_param\fR=\fIparam\fR 64-bit integer parameter to the selection method selected by the \fBselection_method\fR field. The parameter's use is defined by the lower-layer that implements the \fBselection_method\fR. It is optional if the \fBselection_method\fR field is specified as a non-empty string. Prohibited otherwise. The default value is zero. .IP This option will use a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBfields\fR=\fIfield\fR .IQ \fBfields(\fIfield\fR[\fB=\fImask\fR]\fR...\fB)\fR The field parameters to selection method selected by the \fBselection_method\fR field. The syntax is described in \fBFlow Syntax\fR with the additional restrictions that if a value is provided it is treated as a wildcard mask and wildcard masks following a slash are prohibited. The pre-requisites of fields must be provided by any flows that output to the group. The use of the fields is defined by the lower-layer that implements the \fBselection_method\fR. They are optional if the \fBselection_method\fR field is specified as a non-empty string. Prohibited otherwise. The default is no fields. .IP This option will use a Netronome OpenFlow extension which is only supported when using Open vSwitch 2.4 and later with OpenFlow 1.5 and later. .IP \fBbucket\fR=\fIbucket_parameters\fR The \fBadd-group\fR, \fBadd-groups\fR and \fBmod-group\fR commands require at least one bucket field. Bucket fields must appear after all other fields. . Multiple bucket fields to specify multiple buckets. The order in which buckets are specified corresponds to their order in the group. If the type of the group is "indirect" then only one group may be specified. . \fIbucket_parameters\fR consists of a list of \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space followed by a comma-separated list of actions. The fields for \fIbucket_parameters\fR are: . .RS .IP \fBbucket_id=\fIid\fR The 32-bit integer group id of the bucket. Values greater than 0xffffff00 are reserved. . This field was added in Open vSwitch 2.4 to conform with the OpenFlow 1.5 specification. It is not supported when earlier versions of OpenFlow are used. Open vSwitch will automatically allocate bucket ids when they are not specified. .IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR The syntax of actions are identical to the \fBactions=\fR field described in \fBFlow Syntax\fR above. Specyfing \fBactions=\fR is optional, any unknown bucket parameter will be interpreted as an action. .IP \fBweight=\fIvalue\fR The relative weight of the bucket as an integer. This may be used by the switch during bucket select for groups whose \fBtype\fR is \fBselect\fR. .IP \fBwatch_port=\fIport\fR Port used to determine liveness of group. This or the \fBwatch_group\fR field is required for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR. .IP \fBwatch_group=\fIgroup_id\fR Group identifier of group used to determine liveness of group. This or the \fBwatch_port\fR field is required for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR. .RE . .SS "Meter Syntax" .PP The meter table commands accept an argument that describes a meter. Such meter descriptions comprise a series \fIfield\fB=\fIvalue\fR assignments, separated by commas or white space. (Embedding spaces into a group description normally requires quoting to prevent the shell from breaking the description into multiple arguments.). Unless noted otherwise only the last instance of each field is honoured. .PP .IP \fBmeter=\fIid\fR The integer meter id of the meter. When this field is specified in \fBdel-meter\fR, \fBdump-meter\fR, or \fBmeter-stats\fR, the keyword "all" may be used to designate all meters. . This field is required, exept for \fBmeter-stats\fR, which dumps all stats when this field is not specified. .IP \fBkbps\fR .IQ \fBpktps\fR The unit for the meter band rate parameters, either kilobits per second, or packets per second, respectively. One of these must be specified. The burst size unit corresponds to the rate unit by dropping the "per second", i.e., burst is in units of kilobits or packets, respectively. .IP \fBburst\fR Specify burst size for all bands, or none of them, if this flag is not given. .IP \fBstats\fR Collect meter and band statistics. .IP \fBbands\fR=\fIband_parameters\fR The \fBadd-meter\fR and \fBmod-meter\fR commands require at least one band specification. Bands must appear after all other fields. .RS .IP \fBtype=\fItype\fR The type of the meter band. This keyword starts a new band specification. Each band specifies a rate above which the band is to take some action. The action depends on the band type. If multiple bands' rate is exceeded, then the band with the highest rate among the exceeded bands is selected. The following keywords designate the allowed meter band types: .RS .IP \fBdrop\fR Drop packets exceeding the band's rate limit. .RE . .IP "The other \fIband_parameters\fR are:" .IP \fBrate=\fIvalue\fR The relative rate limit for this band, in kilobits per second or packets per second, depending on the meter flags defined above. .IP \fBburst_size=\fIsize\fR The maximum burst allowed for the band. If \fBpktps\fR is specified, then \fIsize\fR is a packet count, otherwise it is in kilobits. If unspecified, the switch is free to select some reasonable value depending on its configuration. .RE . .SH OPTIONS .TP \fB\-\-strict\fR Uses strict matching when running flow modification commands. . .IP "\fB\-\-bundle\fR" Execute flow mods as an OpenFlow 1.4 atomic bundle transaction. .RS .IP \(bu Within a bundle, all flow mods are processed in the order they appear and as a single atomic transaction, meaning that if one of them fails, the whole transaction fails and none of the changes are made to the \fIswitch\fR's flow table, and that each given datapath packet traversing the OpenFlow tables sees the flow tables either as before the transaction, or after all the flow mods in the bundle have been successfully applied. .IP \(bu The beginning and the end of the flow table modification commands in a bundle are delimited with OpenFlow 1.4 bundle control messages, which makes it possible to stream the included commands without explicit OpenFlow barriers, which are otherwise used after each flow table modification command. This may make large modifications execute faster as a bundle. .IP \(bu Bundles require OpenFlow 1.4 or higher. An explicit \fB-O OpenFlow14\fR option is not needed, but you may need to enable OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR column in the \fIbridge\fR table. .RE . .so lib/ofp-version.man . .IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]" .IQ "\fB\-\-flow\-format=\fIformat\fR[\fB,\fIformat\fR...]" \fBovs\-ofctl\fR supports the following individual flow formats, any number of which may be listed as \fIformat\fR: .RS .IP "\fBOpenFlow10\-table_id\fR" This is the standard OpenFlow 1.0 flow format. All OpenFlow switches and all versions of Open vSwitch support this flow format. . .IP "\fBOpenFlow10+table_id\fR" This is the standard OpenFlow 1.0 flow format plus a Nicira extension that allows \fBovs\-ofctl\fR to specify the flow table in which a particular flow should be placed. Open vSwitch 1.2 and later supports this flow format. . .IP "\fBNXM\-table_id\fR (Nicira Extended Match)" This Nicira extension to OpenFlow is flexible and extensible. It supports all of the Nicira flow extensions, such as \fBtun_id\fR and registers. Open vSwitch 1.1 and later supports this flow format. . .IP "\fBNXM+table_id\fR (Nicira Extended Match)" This combines Nicira Extended match with the ability to place a flow in a specific table. Open vSwitch 1.2 and later supports this flow format. . .IP "\fBOXM-OpenFlow12\fR" .IQ "\fBOXM-OpenFlow13\fR" .IQ "\fBOXM-OpenFlow14\fR" These are the standard OXM (OpenFlow Extensible Match) flow format in OpenFlow 1.2, 1.3, and 1.4, respectively. .RE . .IP \fBovs\-ofctl\fR also supports the following abbreviations for collections of flow formats: .RS .IP "\fBany\fR" Any supported flow format. .IP "\fBOpenFlow10\fR" \fBOpenFlow10\-table_id\fR or \fBOpenFlow10+table_id\fR. .IP "\fBNXM\fR" \fBNXM\-table_id\fR or \fBNXM+table_id\fR. .IP "\fBOXM\fR" \fBOXM-OpenFlow12\fR, \fBOXM-OpenFlow13\fR, or \fBOXM-OpenFlow14\fR. .RE . .IP For commands that modify the flow table, \fBovs\-ofctl\fR by default negotiates the most widely supported flow format that supports the flows being added. For commands that query the flow table, \fBovs\-ofctl\fR by default uses the most advanced format supported by the switch. .IP This option, where \fIformat\fR is a comma-separated list of one or more of the formats listed above, limits \fBovs\-ofctl\fR's choice of flow format. If a command cannot work as requested using one of the specified flow formats, \fBovs\-ofctl\fR will report a fatal error. . .IP "\fB\-P \fIformat\fR" .IQ "\fB\-\-packet\-in\-format=\fIformat\fR" \fBovs\-ofctl\fR supports the following packet_in formats, in order of increasing capability: .RS .IP "\fBopenflow10\fR" This is the standard OpenFlow 1.0 packet in format. It should be supported by all OpenFlow switches. . .IP "\fBnxm\fR (Nicira Extended Match)" This packet_in format includes flow metadata encoded using the NXM format. . .RE .IP Usually, \fBovs\-ofctl\fR prefers the \fBnxm\fR packet_in format, but will allow the switch to choose its default if \fBnxm\fR is unsupported. When \fIformat\fR is one of the formats listed in the above table, \fBovs\-ofctl\fR will insist on the selected format. If the switch does not support the requested format, \fBovs\-ofctl\fR will report a fatal error. This option only affects the \fBmonitor\fR command. . .IP "\fB\-\-timestamp\fR" Print a timestamp before each received packet. This option only affects the \fBmonitor\fR, \fBsnoop\fR, and \fBofp\-parse\-pcap\fR commands. . .IP "\fB\-m\fR" .IQ "\fB\-\-more\fR" Increases the verbosity of OpenFlow messages printed and logged by \fBovs\-ofctl\fR commands. Specify this option more than once to increase verbosity further. . .IP \fB\-\-sort\fR[\fB=\fIfield\fR] .IQ \fB\-\-rsort\fR[\fB=\fIfield\fR] Display output sorted by flow \fIfield\fR in ascending (\fB\-\-sort\fR) or descending (\fB\-\-rsort\fR) order, where \fIfield\fR is any of the fields that are allowed for matching or \fBpriority\fR to sort by priority. When \fIfield\fR is omitted, the output is sorted by priority. Specify these options multiple times to sort by multiple fields. .IP Any given flow will not necessarily specify a value for a given field. This requires special treatement: .RS .IP \(bu A flow that does not specify any part of a field that is used for sorting is sorted after all the flows that do specify the field. For example, \fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP source port in ascending order, followed by the flows that do not specify a TCP source port at all. .IP \(bu A flow that only specifies some bits in a field is sorted as if the wildcarded bits were zero. For example, \fB\-\-sort=nw_src\fR would sort a flow that specifies \fBnw_src=192.168.0.0/24\fR the same as \fBnw_src=192.168.0.0\fR. .RE .IP These options currently affect only \fBdump\-flows\fR output. . .ds DD \ \fBovs\-ofctl\fR detaches only when executing the \fBmonitor\fR or \ \fBsnoop\fR commands. .so lib/daemon.man .so lib/unixctl.man .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/vlog.man .so lib/common.man . .SH "RUNTIME MANAGEMENT COMMANDS" \fBovs\-appctl\fR(8) can send commands to a running \fBovs\-ofctl\fR process. The supported commands are listed below. . .IP "\fBexit\fR" Causes \fBovs\-ofctl\fR to gracefully terminate. This command applies only when executing the \fBmonitor\fR or \fBsnoop\fR commands. . .IP "\fBofctl/set\-output\-file \fIfile\fR" Causes all subsequent output to go to \fIfile\fR instead of stderr. This command applies only when executing the \fBmonitor\fR or \fBsnoop\fR commands. . .IP "\fBofctl/send \fIofmsg\fR..." Sends each \fIofmsg\fR, specified as a sequence of hex digits that express an OpenFlow message, on the OpenFlow connection. This command is useful only when executing the \fBmonitor\fR command. . .IP "\fBofctl/barrier\fR" Sends an OpenFlow barrier request on the OpenFlow connection and waits for a reply. This command is useful only for the \fBmonitor\fR command. . .SH EXAMPLES . The following examples assume that \fBovs\-vswitchd\fR has a bridge named \fBbr0\fR configured. . .TP \fBovs\-ofctl dump\-tables br0\fR Prints out the switch's table stats. (This is more interesting after some traffic has passed through.) . .TP \fBovs\-ofctl dump\-flows br0\fR Prints the flow entries in the switch. . .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-vswitchd (8) .BR ovs\-vswitchd.conf.db (8) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-dev.py0000644000000000000000000000013113534540071017613 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 29 ctime=1567801423.80984626 openvswitch-2.5.9/utilities/ovs-dev.py0000755000175000017500000003273713534540071021321 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # Copyright (c) 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import optparse import os import shutil import subprocess import sys import tempfile ENV = os.environ HOME = ENV["HOME"] PWD = os.getcwd() OVS_SRC = HOME + "/ovs" if os.path.exists(PWD + "/WHY-OVS.md"): OVS_SRC = PWD # Use current directory as OVS source tree RUNDIR = OVS_SRC + "/_run" BUILD_GCC = OVS_SRC + "/_build-gcc" BUILD_CLANG = OVS_SRC + "/_build-clang" options = None parser = None commands = [] def set_path(build): PATH = "%(ovs)s/utilities:%(ovs)s/ovsdb:%(ovs)s/vswitchd" % {"ovs": build} ENV["PATH"] = PATH + ":" + ENV["PATH"] def _sh(*args, **kwargs): print "------> " + " ".join(args) shell = len(args) == 1 if kwargs.get("capture", False): proc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell) return proc.stdout.readlines() elif kwargs.get("check", True): subprocess.check_call(args, shell=shell) else: subprocess.call(args, shell=shell) def uname(): return _sh("uname", "-r", capture=True)[0].strip() def sudo(): if os.geteuid() != 0: _sh(" ".join(["sudo"] + sys.argv), check=True) sys.exit(0) def conf(): tag() try: os.remove(OVS_SRC + "/Makefile") except OSError: pass configure = ["../configure", "--prefix=" + RUNDIR, "--localstatedir=" + RUNDIR, "--with-logdir=%s/log" % RUNDIR, "--with-rundir=%s/run" % RUNDIR, "--enable-silent-rules", "--with-dbdir=" + RUNDIR, "--silent"] cflags = "-g -fno-omit-frame-pointer" if options.werror: configure.append("--enable-Werror") if options.cache_time: configure.append("--enable-cache-time") if options.mandir: configure.append("--mandir=" + options.mandir) if options.with_dpdk: configure.append("--with-dpdk=" + options.with_dpdk) cflags += " -Wno-cast-align -Wno-bad-function-cast" # DPDK warnings. if options.optimize is None: options.optimize = 0 cflags += " -O%s" % str(options.optimize) ENV["CFLAGS"] = cflags _sh("./boot.sh") try: os.mkdir(BUILD_GCC) except OSError: pass # Directory exists. os.chdir(BUILD_GCC) _sh(*(configure + ["--with-linux=/lib/modules/%s/build" % uname()])) try: _sh("clang --version", check=True) clang = True except subprocess.CalledProcessError: clang = False try: _sh("sparse --version", check=True) sparse = True except subprocess.CalledProcessError: sparse = False if clang: try: os.mkdir(BUILD_CLANG) except OSError: pass # Directory exists. ENV["CC"] = "clang" os.chdir(BUILD_CLANG) _sh(*configure) if sparse: c1 = "C=1" else: c1 = "" os.chdir(OVS_SRC) make_str = "\t$(MAKE) -C %s $@\n" mf = open(OVS_SRC + "/Makefile", "w") mf.write("all:\n%:\n") if clang: mf.write(make_str % BUILD_CLANG) mf.write("\t$(MAKE) -C %s %s $@\n" % (BUILD_GCC, c1)) mf.write("\ncheck-valgrind:\n") mf.write("\ncheck:\n") mf.write(make_str % BUILD_GCC) mf.close() commands.append(conf) def make(args=""): make = "make -s -j 8 " + args _sh(make) commands.append(make) def check(): flags = "" if options.jobs: flags += "-j%d " % options.jobs else: flags += "-j8 " if options.tests: for arg in str.split(options.tests): if arg[0].isdigit(): flags += "%s " % arg else: flags += "-k %s " % arg ENV["TESTSUITEFLAGS"] = flags make("check") commands.append(check) def tag(): ctags = ['ctags', '-R', '-f', '.tags'] try: _sh(*(ctags + ['--exclude="datapath/"'])) except: try: _sh(*ctags) # Some versions of ctags don't have --exclude except: pass try: _sh('cscope', '-R', '-b') except: pass commands.append(tag) def kill(): sudo() for proc in ["ovs-vswitchd", "ovsdb-server"]: if os.path.exists("%s/run/openvswitch/%s.pid" % (RUNDIR, proc)): _sh("ovs-appctl", "-t", proc, "exit", check=False) time.sleep(.1) _sh("killall", "-q", "-2", proc, check=False) commands.append(kill) def reset(): sudo() kill() if os.path.exists(RUNDIR): shutil.rmtree(RUNDIR) for dp in _sh("ovs-dpctl dump-dps", capture=True): _sh("ovs-dpctl", "del-dp", dp.strip()) commands.append(reset) def run(): sudo() kill() for d in ["log", "run"]: d = "%s/%s" % (RUNDIR, d) shutil.rmtree(d, ignore_errors=True) os.makedirs(d) pki_dir = RUNDIR + "/pki" if not os.path.exists(pki_dir): os.mkdir(pki_dir) os.chdir(pki_dir) _sh("ovs-pki init") _sh("ovs-pki req+sign ovsclient") os.chdir(OVS_SRC) if not os.path.exists(RUNDIR + "/conf.db"): _sh("ovsdb-tool", "create", RUNDIR + "/conf.db", OVS_SRC + "/vswitchd/vswitch.ovsschema") opts = ["--pidfile", "--log-file"] if (options.user == "") or (options.user == "root:root"): _sh("chown", "root:root", "-R", RUNDIR) if '--user' in sys.argv: sys.argv.remove("--user") else: _sh("chown", options.user, "-R", RUNDIR); opts = ["--user", options.user] + opts if (options.monitor): opts = ["--monitor"] + opts _sh(*(["ovsdb-server", "--remote=punix:%s/run/db.sock" % RUNDIR, "--remote=db:Open_vSwitch,Open_vSwitch,manager_options", "--private-key=db:Open_vSwitch,SSL,private_key", "--certificate=db:Open_vSwitch,SSL,certificate", "--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert", "--detach", "-vconsole:off"] + opts)) _sh("ovs-vsctl --no-wait --bootstrap set-ssl %s/ovsclient-privkey.pem" \ " %s/ovsclient-cert.pem %s/vswitchd.cacert" % (pki_dir, pki_dir, pki_dir)) version = _sh("ovs-vsctl --no-wait --version", capture=True) version = version[0].strip().split()[3] root_uuid = _sh("ovs-vsctl --no-wait --bare list Open_vSwitch", capture=True)[0].strip() _sh("ovs-vsctl --no-wait set Open_vSwitch %s ovs_version=%s" % (root_uuid, version)) build = BUILD_CLANG if options.clang else BUILD_GCC cmd = [build + "/vswitchd/ovs-vswitchd"] if options.dpdk: cmd.append("--dpdk") cmd.extend(options.dpdk) cmd.append("--") if options.gdb: cmd = ["gdb", "--args"] + cmd elif options.valgrind: cmd = ["valgrind", "--track-origins=yes", "--leak-check=full", "--suppressions=%s/tests/glibc.supp" % OVS_SRC, "--suppressions=%s/tests/openssl.supp" % OVS_SRC] + cmd else: opts = opts + ["-vconsole:off", "--detach", "--enable-dummy"] _sh(*(cmd + opts)) commands.append(run) def modinst(): if not os.path.exists("/lib/modules"): print "Missing modules directory. Is this a Linux system?" sys.exit(1) sudo() try: _sh("rmmod", "openvswitch") except subprocess.CalledProcessError, e: pass # Module isn't loaded try: _sh("rm -f /lib/modules/%s/extra/openvswitch.ko" % uname()) _sh("rm -f /lib/modules/%s/extra/vport-*.ko" % uname()) except subprocess.CalledProcessError, e: pass # Module isn't installed conf() make() make("modules_install") _sh("modprobe", "openvswitch") _sh("dmesg | grep openvswitch | tail -1") _sh("find /lib/modules/%s/ -iname vport-*.ko -exec insmod '{}' \;" % uname()) commands.append(modinst) def env(): print "export PATH=" + ENV["PATH"] commands.append(env) def doc(): parser.print_help() print \ """ This program is designed to help developers build and run Open vSwitch without necessarily needing to know the gory details. Given some basic requirements (described below), it can be used to build and run Open vSwitch, keeping runtime files in the user's home directory. Basic Configuration: # This section can be run as a script on ubuntu systems. # First install the basic requirements needed to build Open vSwitch. sudo apt-get install git build-essential libtool autoconf pkg-config \\ libssl-dev gdb libcap-ng-dev linux-headers-`uname -r` # Next clone the Open vSwitch source. git clone https://github.com/openvswitch/ovs.git %(ovs)s # Setup environment variables. `%(v)s env` # Build the switch. %(v)s conf make # Install the kernel module sudo insmod %(ovs)s/datapath/linux/openvswitch.ko # If needed, manually load all required vport modules: sudo insmod %(ovs)s/datapath/linux/vport-vxlan.ko sudo insmod %(ovs)s/datapath/linux/vport-geneve.ko [...] # Run the switch. %(v)s run Commands: conf - Configure the ovs source. make - Build the source (must have been configured). check - Run the unit tests. tag - Run ctags and cscope over the source. kill - Kill all running instances of ovs. reset - Reset any runtime configuration in %(run)s. run - Run ovs. modinst - Build ovs and install the kernel module. env - Print the required path environment variable. doc - Print this message. Note: If running as non-root user, "kill", "reset", "run" and "modinst" will always run as the root user, by rerun the commands with "sudo". """ % {"ovs": OVS_SRC, "v": sys.argv[0], "run": RUNDIR} sys.exit(0) commands.append(doc) def parse_subargs(option, opt_str, value, parser): subopts = [] while parser.rargs: dpdkarg = parser.rargs.pop(0) if dpdkarg == "--": break subopts.append(dpdkarg) setattr(parser.values, option.dest, subopts) def main(): global options global parser description = "Open vSwitch developer configuration. Try `%prog doc`." cmd_names = [c.__name__ for c in commands] parser = optparse.OptionParser(usage="usage: %prog" + " [options] [%s] ..." % "|".join(cmd_names), description=description) group = optparse.OptionGroup(parser, "conf") group.add_option("--disable-Werror", dest="werror", action="store_false", default=True, help="compile without the Werror flag") group.add_option("--cache-time", dest="cache_time", action="store_true", help="configure with cached timing") group.add_option("--mandir", dest="mandir", metavar="MANDIR", help="configure the man documentation install directory") group.add_option("--with-dpdk", dest="with_dpdk", metavar="DPDK_BUILD", help="built with dpdk libraries located at DPDK_BUILD"); parser.add_option_group(group) group = optparse.OptionGroup(parser, "Optimization Flags") for i in ["s", "g"] + range(4) + ["fast"]: group.add_option("--O%s" % str(i), dest="optimize", action="store_const", const=i, help="compile with -O%s" % str(i)) parser.add_option_group(group) group = optparse.OptionGroup(parser, "check") group.add_option("-j", "--jobs", dest="jobs", metavar="N", type="int", help="Run N tests in parallel") group.add_option("--tests", dest="tests", metavar="FILTER", help="""run specific tests and/or a test category eg, --tests=\"1-10 megaflow\"""") parser.add_option_group(group) group = optparse.OptionGroup(parser, "run") group.add_option("-g", "--gdb", dest="gdb", action="store_true", help="run ovs-vswitchd under gdb") group.add_option("--valgrind", dest="valgrind", action="store_true", help="run ovs-vswitchd under valgrind") group.add_option("--dpdk", dest="dpdk", action="callback", callback=parse_subargs, help="run ovs-vswitchd with dpdk subopts (ended by --)") group.add_option("--clang", dest="clang", action="store_true", help="Use binaries built by clang") group.add_option("--user", dest="user", action="store", default="", help="run all daemons as a non root user") group.add_option("--monitor", dest="monitor", action="store_true", help="run daemons with --monitor option") parser.add_option_group(group) options, args = parser.parse_args() for arg in args: if arg not in cmd_names: print "Unknown argument " + arg doc() if options.clang: set_path(BUILD_CLANG) else: set_path(BUILD_GCC) try: os.chdir(OVS_SRC) except OSError: print "Missing %s." % OVS_SRC doc() for arg in args: for cmd in commands: if arg == cmd.__name__: cmd() if __name__ == '__main__': main() openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vsctl.c0000644000000000000000000000013213534540071017763 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801425.197856493 openvswitch-2.5.9/utilities/ovs-vsctl.c0000644000175000017500000024776713534540071021500 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "db-ctl-base.h" #include "command-line.h" #include "compiler.h" #include "dynamic-string.h" #include "fatal-signal.h" #include "hash.h" #include "json.h" #include "ovsdb-data.h" #include "ovsdb-idl.h" #include "poll-loop.h" #include "process.h" #include "stream.h" #include "stream-ssl.h" #include "smap.h" #include "sset.h" #include "svec.h" #include "lib/vswitch-idl.h" #include "table.h" #include "timeval.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(vsctl); struct vsctl_context; /* --db: The database server to contact. */ static const char *db; /* --oneline: Write each command's output as a single line? */ static bool oneline; /* --dry-run: Do not commit any changes. */ static bool dry_run; /* --no-wait: Wait for ovs-vswitchd to reload its configuration? */ static bool wait_for_reload = true; /* --timeout: Time to wait for a connection to 'db'. */ static int timeout; /* --retry: If true, ovs-vsctl will retry connecting to the database forever. * If false and --db says to use an active connection method (e.g. "unix:", * "tcp:", "ssl:"), then ovs-vsctl will try to connect once and exit with an * error if the database server cannot be contacted (e.g. ovsdb-server is not * running). * * Regardless of this setting, --timeout always limits how long ovs-vsctl will * wait. */ static bool retry; /* Format for table output. */ static struct table_style table_style = TABLE_STYLE_DEFAULT; static void vsctl_cmd_init(void); /* The IDL we're using and the current transaction, if any. * This is for use by vsctl_exit() only, to allow it to clean up. * Other code should use its context arguments. */ static struct ovsdb_idl *the_idl; static struct ovsdb_idl_txn *the_idl_txn; OVS_NO_RETURN static void vsctl_exit(int status); OVS_NO_RETURN static void usage(void); static void parse_options(int argc, char *argv[], struct shash *local_options); static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static void do_vsctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); /* post_db_reload_check frame work is to allow ovs-vsctl to do additional * checks after OVSDB transactions are successfully recorded and reload by * ovs-vswitchd. * * For example, When a new interface is added to OVSDB, ovs-vswitchd will * either store a positive values on successful implementing the new * interface, or -1 on failure. * * Unless -no-wait command line option is specified, * post_db_reload_do_checks() is called right after any configuration * changes is picked up (i.e. reload) by ovs-vswitchd. Any error detected * post OVSDB reload is reported as ovs-vsctl errors. OVS-vswitchd logs * more detailed messages about those errors. * * Current implementation only check for Post OVSDB reload failures on new * interface additions with 'add-br' and 'add-port' commands. * * post_db_reload_expect_iface() * * keep track of interfaces to be checked post OVSDB reload. */ static void post_db_reload_check_init(void); static void post_db_reload_do_checks(const struct vsctl_context *); static void post_db_reload_expect_iface(const struct ovsrec_interface *); static struct uuid *neoteric_ifaces; static size_t n_neoteric_ifaces; static size_t allocated_neoteric_ifaces; int main(int argc, char *argv[]) { extern struct vlog_module VLM_reconnect; struct ovsdb_idl *idl; struct ctl_command *commands; struct shash local_options; unsigned int seqno; size_t n_commands; char *args; set_program_name(argv[0]); fatal_ignore_sigpipe(); vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN); ovsrec_init(); vsctl_cmd_init(); /* Log our arguments. This is often valuable for debugging systems. */ args = process_escape_args(argv); VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args); /* Parse command line. */ shash_init(&local_options); parse_options(argc, argv, &local_options); commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, &n_commands); if (timeout) { time_alarm(timeout); } /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class, false, retry); run_prerequisites(commands, n_commands, idl); /* Execute the commands. * * 'seqno' is the database sequence number for which we last tried to * execute our transaction. There's no point in trying to commit more than * once for any given sequence number, because if the transaction fails * it's because the database changed and we need to obtain an up-to-date * view of the database before we try the transaction again. */ seqno = ovsdb_idl_get_seqno(idl); for (;;) { ovsdb_idl_run(idl); if (!ovsdb_idl_is_alive(idl)) { int retval = ovsdb_idl_get_last_error(idl); ctl_fatal("%s: database connection failed (%s)", db, ovs_retval_to_string(retval)); } if (seqno != ovsdb_idl_get_seqno(idl)) { seqno = ovsdb_idl_get_seqno(idl); do_vsctl(args, commands, n_commands, idl); } if (seqno == ovsdb_idl_get_seqno(idl)) { ovsdb_idl_wait(idl); poll_block(); } } } static void parse_options(int argc, char *argv[], struct shash *local_options) { enum { OPT_DB = UCHAR_MAX + 1, OPT_ONELINE, OPT_NO_SYSLOG, OPT_NO_WAIT, OPT_DRY_RUN, OPT_BOOTSTRAP_CA_CERT, OPT_PEER_CA_CERT, OPT_LOCAL, OPT_RETRY, OPT_COMMANDS, OPT_OPTIONS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS }; static const struct option global_long_options[] = { {"db", required_argument, NULL, OPT_DB}, {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, {"no-wait", no_argument, NULL, OPT_NO_WAIT}, {"dry-run", no_argument, NULL, OPT_DRY_RUN}, {"oneline", no_argument, NULL, OPT_ONELINE}, {"timeout", required_argument, NULL, 't'}, {"retry", no_argument, NULL, OPT_RETRY}, {"help", no_argument, NULL, 'h'}, {"commands", no_argument, NULL, OPT_COMMANDS}, {"options", no_argument, NULL, OPT_OPTIONS}, {"version", no_argument, NULL, 'V'}, VLOG_LONG_OPTIONS, TABLE_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; char *tmp, *short_options; struct option *options; size_t allocated_options; size_t n_options; size_t i; tmp = ovs_cmdl_long_options_to_short_options(global_long_options); short_options = xasprintf("+%s", tmp); free(tmp); /* We want to parse both global and command-specific options here, but * getopt_long() isn't too convenient for the job. We copy our global * options into a dynamic array, then append all of the command-specific * options. */ options = xmemdup(global_long_options, sizeof global_long_options); allocated_options = ARRAY_SIZE(global_long_options); n_options = n_global_long_options; ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL); table_style.format = TF_LIST; for (;;) { int idx; int c; c = getopt_long(argc, argv, short_options, options, &idx); if (c == -1) { break; } switch (c) { case OPT_DB: db = optarg; break; case OPT_ONELINE: oneline = true; break; case OPT_NO_SYSLOG: vlog_set_levels(&VLM_vsctl, VLF_SYSLOG, VLL_WARN); break; case OPT_NO_WAIT: wait_for_reload = false; break; case OPT_DRY_RUN: dry_run = true; break; case OPT_LOCAL: if (shash_find(local_options, options[idx].name)) { ctl_fatal("'%s' option specified multiple times", options[idx].name); } shash_add_nocopy(local_options, xasprintf("--%s", options[idx].name), optarg ? xstrdup(optarg) : NULL); break; case 'h': usage(); case OPT_COMMANDS: ctl_print_commands(); case OPT_OPTIONS: ctl_print_options(global_long_options); case 'V': ovs_print_version(0, 0); printf("DB Schema %s\n", ovsrec_get_db_version()); exit(EXIT_SUCCESS); case 't': timeout = strtoul(optarg, NULL, 10); if (timeout < 0) { ctl_fatal("value %s on -t or --timeout is invalid", optarg); } break; case OPT_RETRY: retry = true; break; VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!db) { db = ctl_default_db(); } for (i = n_global_long_options; options[i].name; i++) { free(CONST_CAST(char *, options[i].name)); } free(options); } static void usage(void) { printf("\ %s: ovs-vswitchd management utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ \n\ Open vSwitch commands:\n\ init initialize database, if not yet initialized\n\ show print overview of database contents\n\ emer-reset reset configuration to clean state\n\ \n\ Bridge commands:\n\ add-br BRIDGE create a new bridge named BRIDGE\n\ add-br BRIDGE PARENT VLAN create new fake BRIDGE in PARENT on VLAN\n\ del-br BRIDGE delete BRIDGE and all of its ports\n\ list-br print the names of all the bridges\n\ br-exists BRIDGE exit 2 if BRIDGE does not exist\n\ br-to-vlan BRIDGE print the VLAN which BRIDGE is on\n\ br-to-parent BRIDGE print the parent of BRIDGE\n\ br-set-external-id BRIDGE KEY VALUE set KEY on BRIDGE to VALUE\n\ br-set-external-id BRIDGE KEY unset KEY on BRIDGE\n\ br-get-external-id BRIDGE KEY print value of KEY on BRIDGE\n\ br-get-external-id BRIDGE list key-value pairs on BRIDGE\n\ \n\ Port commands (a bond is considered to be a single port):\n\ list-ports BRIDGE print the names of all the ports on BRIDGE\n\ add-port BRIDGE PORT add network device PORT to BRIDGE\n\ add-bond BRIDGE PORT IFACE... add bonded port PORT in BRIDGE from IFACES\n\ del-port [BRIDGE] PORT delete PORT (which may be bonded) from BRIDGE\n\ port-to-br PORT print name of bridge that contains PORT\n\ \n\ Interface commands (a bond consists of multiple interfaces):\n\ list-ifaces BRIDGE print the names of all interfaces on BRIDGE\n\ iface-to-br IFACE print name of bridge that contains IFACE\n\ \n\ Controller commands:\n\ get-controller BRIDGE print the controllers for BRIDGE\n\ del-controller BRIDGE delete the controllers for BRIDGE\n\ set-controller BRIDGE TARGET... set the controllers for BRIDGE\n\ get-fail-mode BRIDGE print the fail-mode for BRIDGE\n\ del-fail-mode BRIDGE delete the fail-mode for BRIDGE\n\ set-fail-mode BRIDGE MODE set the fail-mode for BRIDGE to MODE\n\ \n\ Manager commands:\n\ get-manager print the managers\n\ del-manager delete the managers\n\ set-manager TARGET... set the list of managers to TARGET...\n\ \n\ SSL commands:\n\ get-ssl print the SSL configuration\n\ del-ssl delete the SSL configuration\n\ set-ssl PRIV-KEY CERT CA-CERT set the SSL configuration\n\ \n\ Auto Attach commands:\n\ add-aa-mapping BRIDGE I-SID VLAN add Auto Attach mapping to BRIDGE\n\ del-aa-mapping BRIDGE I-SID VLAN delete Auto Attach mapping VLAN from BRIDGE\n\ get-aa-mapping BRIDGE get Auto Attach mappings from BRIDGE\n\ \n\ Switch commands:\n\ emer-reset reset switch to known good state\n\ \n\ %s\ \n\ Options:\n\ --db=DATABASE connect to DATABASE\n\ (default: %s)\n\ --no-wait do not wait for ovs-vswitchd to reconfigure\n\ --retry keep trying to connect to server forever\n\ -t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\ --dry-run do not commit changes to database\n\ --oneline print exactly one line of output per command\n", program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db()); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=vsctl:syslog:warn\n"); stream_usage("database", true, true, false); printf("\n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n"); exit(EXIT_SUCCESS); } /* ovs-vsctl specific context. Inherits the 'struct ctl_context' as base. */ struct vsctl_context { struct ctl_context base; /* Modifiable state. */ const struct ovsrec_open_vswitch *ovs; bool verified_ports; /* A cache of the contents of the database. * * A command that needs to use any of this information must first call * vsctl_context_populate_cache(). A command that changes anything that * could invalidate the cache must either call * vsctl_context_invalidate_cache() or manually update the cache to * maintain its correctness. */ bool cache_valid; struct shash bridges; /* Maps from bridge name to struct vsctl_bridge. */ struct shash ports; /* Maps from port name to struct vsctl_port. */ struct shash ifaces; /* Maps from port name to struct vsctl_iface. */ }; struct vsctl_bridge { struct ovsrec_bridge *br_cfg; char *name; struct ovs_list ports; /* Contains "struct vsctl_port"s. */ /* VLAN ("fake") bridge support. * * Use 'parent != NULL' to detect a fake bridge, because 'vlan' can be 0 * in either case. */ struct hmap children; /* VLAN bridges indexed by 'vlan'. */ struct hmap_node children_node; /* Node in parent's 'children' hmap. */ struct vsctl_bridge *parent; /* Real bridge, or NULL. */ int vlan; /* VLAN VID (0...4095), or 0. */ }; struct vsctl_port { struct ovs_list ports_node; /* In struct vsctl_bridge's 'ports' list. */ struct ovs_list ifaces; /* Contains "struct vsctl_iface"s. */ struct ovsrec_port *port_cfg; struct vsctl_bridge *bridge; }; struct vsctl_iface { struct ovs_list ifaces_node; /* In struct vsctl_port's 'ifaces' list. */ struct ovsrec_interface *iface_cfg; struct vsctl_port *port; }; /* Casts 'base' into 'struct vsctl_context'. */ static struct vsctl_context * vsctl_context_cast(struct ctl_context *base) { return CONTAINER_OF(base, struct vsctl_context, base); } static struct vsctl_bridge *find_vlan_bridge(struct vsctl_bridge *parent, int vlan); static char * vsctl_context_to_string(const struct ctl_context *ctx) { const struct shash_node *node; struct svec words; char *s; int i; svec_init(&words); SHASH_FOR_EACH (node, &ctx->options) { svec_add(&words, node->name); } for (i = 0; i < ctx->argc; i++) { svec_add(&words, ctx->argv[i]); } svec_terminate(&words); s = process_escape_args(words.names); svec_destroy(&words); return s; } static void verify_ports(struct vsctl_context *vsctl_ctx) { if (!vsctl_ctx->verified_ports) { const struct ovsrec_bridge *bridge; const struct ovsrec_port *port; ovsrec_open_vswitch_verify_bridges(vsctl_ctx->ovs); OVSREC_BRIDGE_FOR_EACH (bridge, vsctl_ctx->base.idl) { ovsrec_bridge_verify_ports(bridge); } OVSREC_PORT_FOR_EACH (port, vsctl_ctx->base.idl) { ovsrec_port_verify_interfaces(port); } vsctl_ctx->verified_ports = true; } } static struct vsctl_bridge * add_bridge_to_cache(struct vsctl_context *vsctl_ctx, struct ovsrec_bridge *br_cfg, const char *name, struct vsctl_bridge *parent, int vlan) { struct vsctl_bridge *br = xmalloc(sizeof *br); br->br_cfg = br_cfg; br->name = xstrdup(name); list_init(&br->ports); br->parent = parent; br->vlan = vlan; hmap_init(&br->children); if (parent) { struct vsctl_bridge *conflict = find_vlan_bridge(parent, vlan); if (conflict) { VLOG_WARN("%s: bridge has multiple VLAN bridges (%s and %s) " "for VLAN %d, but only one is allowed", parent->name, name, conflict->name, vlan); } else { hmap_insert(&parent->children, &br->children_node, hash_int(vlan, 0)); } } shash_add(&vsctl_ctx->bridges, br->name, br); return br; } static void ovs_delete_bridge(const struct ovsrec_open_vswitch *ovs, struct ovsrec_bridge *bridge) { struct ovsrec_bridge **bridges; size_t i, n; bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); for (i = n = 0; i < ovs->n_bridges; i++) { if (ovs->bridges[i] != bridge) { bridges[n++] = ovs->bridges[i]; } } ovsrec_open_vswitch_set_bridges(ovs, bridges, n); free(bridges); } static void del_cached_bridge(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *br) { ovs_assert(list_is_empty(&br->ports)); ovs_assert(hmap_is_empty(&br->children)); if (br->parent) { hmap_remove(&br->parent->children, &br->children_node); } if (br->br_cfg) { ovsrec_bridge_delete(br->br_cfg); ovs_delete_bridge(vsctl_ctx->ovs, br->br_cfg); } shash_find_and_delete(&vsctl_ctx->bridges, br->name); hmap_destroy(&br->children); free(br->name); free(br); } static bool port_is_fake_bridge(const struct ovsrec_port *port_cfg) { return (port_cfg->fake_bridge && port_cfg->tag && *port_cfg->tag >= 0 && *port_cfg->tag <= 4095); } static struct vsctl_bridge * find_vlan_bridge(struct vsctl_bridge *parent, int vlan) { struct vsctl_bridge *child; HMAP_FOR_EACH_IN_BUCKET (child, children_node, hash_int(vlan, 0), &parent->children) { if (child->vlan == vlan) { return child; } } return NULL; } static struct vsctl_port * add_port_to_cache(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *parent, struct ovsrec_port *port_cfg) { struct vsctl_port *port; if (port_cfg->tag && *port_cfg->tag >= 0 && *port_cfg->tag <= 4095) { struct vsctl_bridge *vlan_bridge; vlan_bridge = find_vlan_bridge(parent, *port_cfg->tag); if (vlan_bridge) { parent = vlan_bridge; } } port = xmalloc(sizeof *port); list_push_back(&parent->ports, &port->ports_node); list_init(&port->ifaces); port->port_cfg = port_cfg; port->bridge = parent; shash_add(&vsctl_ctx->ports, port_cfg->name, port); return port; } static void del_cached_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port) { ovs_assert(list_is_empty(&port->ifaces)); list_remove(&port->ports_node); shash_find_and_delete(&vsctl_ctx->ports, port->port_cfg->name); ovsrec_port_delete(port->port_cfg); free(port); } static struct vsctl_iface * add_iface_to_cache(struct vsctl_context *vsctl_ctx, struct vsctl_port *parent, struct ovsrec_interface *iface_cfg) { struct vsctl_iface *iface; iface = xmalloc(sizeof *iface); list_push_back(&parent->ifaces, &iface->ifaces_node); iface->iface_cfg = iface_cfg; iface->port = parent; shash_add(&vsctl_ctx->ifaces, iface_cfg->name, iface); return iface; } static void del_cached_iface(struct vsctl_context *vsctl_ctx, struct vsctl_iface *iface) { list_remove(&iface->ifaces_node); shash_find_and_delete(&vsctl_ctx->ifaces, iface->iface_cfg->name); ovsrec_interface_delete(iface->iface_cfg); free(iface); } static void vsctl_context_invalidate_cache(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct shash_node *node; if (!vsctl_ctx->cache_valid) { return; } vsctl_ctx->cache_valid = false; SHASH_FOR_EACH (node, &vsctl_ctx->bridges) { struct vsctl_bridge *bridge = node->data; hmap_destroy(&bridge->children); free(bridge->name); free(bridge); } shash_destroy(&vsctl_ctx->bridges); shash_destroy_free_data(&vsctl_ctx->ports); shash_destroy_free_data(&vsctl_ctx->ifaces); } static void pre_get_info(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_bridges); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ports); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_fake_bridge); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_tag); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_interfaces); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_name); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ofport); } static void vsctl_context_populate_cache(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; struct sset bridges, ports; size_t i; if (vsctl_ctx->cache_valid) { /* Cache is already populated. */ return; } vsctl_ctx->cache_valid = true; shash_init(&vsctl_ctx->bridges); shash_init(&vsctl_ctx->ports); shash_init(&vsctl_ctx->ifaces); sset_init(&bridges); sset_init(&ports); for (i = 0; i < ovs->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs->bridges[i]; struct vsctl_bridge *br; size_t j; if (!sset_add(&bridges, br_cfg->name)) { VLOG_WARN("%s: database contains duplicate bridge name", br_cfg->name); continue; } br = add_bridge_to_cache(vsctl_ctx, br_cfg, br_cfg->name, NULL, 0); for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; if (!sset_add(&ports, port_cfg->name)) { /* Duplicate port name. (We will warn about that later.) */ continue; } if (port_is_fake_bridge(port_cfg) && sset_add(&bridges, port_cfg->name)) { add_bridge_to_cache(vsctl_ctx, NULL, port_cfg->name, br, *port_cfg->tag); } } } sset_destroy(&bridges); sset_destroy(&ports); sset_init(&bridges); for (i = 0; i < ovs->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs->bridges[i]; struct vsctl_bridge *br; size_t j; if (!sset_add(&bridges, br_cfg->name)) { continue; } br = shash_find_data(&vsctl_ctx->bridges, br_cfg->name); for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; struct vsctl_port *port; size_t k; port = shash_find_data(&vsctl_ctx->ports, port_cfg->name); if (port) { if (port_cfg == port->port_cfg) { VLOG_WARN("%s: port is in multiple bridges (%s and %s)", port_cfg->name, br->name, port->bridge->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server shouldn't * have allowed it. */ VLOG_ERR("%s: database contains duplicate port name", port_cfg->name); } continue; } if (port_is_fake_bridge(port_cfg) && !sset_add(&bridges, port_cfg->name)) { continue; } port = add_port_to_cache(vsctl_ctx, br, port_cfg); for (k = 0; k < port_cfg->n_interfaces; k++) { struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; struct vsctl_iface *iface; iface = shash_find_data(&vsctl_ctx->ifaces, iface_cfg->name); if (iface) { if (iface_cfg == iface->iface_cfg) { VLOG_WARN("%s: interface is in multiple ports " "(%s and %s)", iface_cfg->name, iface->port->port_cfg->name, port->port_cfg->name); } else { /* Log as an error because this violates the database's * uniqueness constraints, so the database server * shouldn't have allowed it. */ VLOG_ERR("%s: database contains duplicate interface " "name", iface_cfg->name); } continue; } add_iface_to_cache(vsctl_ctx, port, iface_cfg); } } } sset_destroy(&bridges); } static void check_conflicts(struct vsctl_context *vsctl_ctx, const char *name, char *msg) { struct vsctl_iface *iface; struct vsctl_port *port; verify_ports(vsctl_ctx); if (shash_find(&vsctl_ctx->bridges, name)) { ctl_fatal("%s because a bridge named %s already exists", msg, name); } port = shash_find_data(&vsctl_ctx->ports, name); if (port) { ctl_fatal("%s because a port named %s already exists on " "bridge %s", msg, name, port->bridge->name); } iface = shash_find_data(&vsctl_ctx->ifaces, name); if (iface) { ctl_fatal("%s because an interface named %s already exists " "on bridge %s", msg, name, iface->port->bridge->name); } free(msg); } static struct vsctl_bridge * find_bridge(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_bridge *br; ovs_assert(vsctl_ctx->cache_valid); br = shash_find_data(&vsctl_ctx->bridges, name); if (must_exist && !br) { ctl_fatal("no bridge named %s", name); } ovsrec_open_vswitch_verify_bridges(vsctl_ctx->ovs); return br; } static struct vsctl_bridge * find_real_bridge(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_bridge *br = find_bridge(vsctl_ctx, name, must_exist); if (br && br->parent) { ctl_fatal("%s is a fake bridge", name); } return br; } static struct vsctl_port * find_port(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_port *port; ovs_assert(vsctl_ctx->cache_valid); port = shash_find_data(&vsctl_ctx->ports, name); if (port && !strcmp(name, port->bridge->name)) { port = NULL; } if (must_exist && !port) { ctl_fatal("no port named %s", name); } verify_ports(vsctl_ctx); return port; } static struct vsctl_iface * find_iface(struct vsctl_context *vsctl_ctx, const char *name, bool must_exist) { struct vsctl_iface *iface; ovs_assert(vsctl_ctx->cache_valid); iface = shash_find_data(&vsctl_ctx->ifaces, name); if (iface && !strcmp(name, iface->port->bridge->name)) { iface = NULL; } if (must_exist && !iface) { ctl_fatal("no interface named %s", name); } verify_ports(vsctl_ctx); return iface; } static void bridge_insert_port(struct ovsrec_bridge *br, struct ovsrec_port *port) { struct ovsrec_port **ports; size_t i; ports = xmalloc(sizeof *br->ports * (br->n_ports + 1)); for (i = 0; i < br->n_ports; i++) { ports[i] = br->ports[i]; } ports[br->n_ports] = port; ovsrec_bridge_set_ports(br, ports, br->n_ports + 1); free(ports); } static void bridge_delete_port(struct ovsrec_bridge *br, struct ovsrec_port *port) { struct ovsrec_port **ports; size_t i, n; ports = xmalloc(sizeof *br->ports * br->n_ports); for (i = n = 0; i < br->n_ports; i++) { if (br->ports[i] != port) { ports[n++] = br->ports[i]; } } ovsrec_bridge_set_ports(br, ports, n); free(ports); } static void ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs, struct ovsrec_bridge *bridge) { struct ovsrec_bridge **bridges; size_t i; bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1)); for (i = 0; i < ovs->n_bridges; i++) { bridges[i] = ovs->bridges[i]; } bridges[ovs->n_bridges] = bridge; ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1); free(bridges); } static void cmd_init(struct ctl_context *ctx OVS_UNUSED) { } static struct cmd_show_table cmd_show_tables[] = { {&ovsrec_table_open_vswitch, NULL, {&ovsrec_open_vswitch_col_manager_options, &ovsrec_open_vswitch_col_bridges, &ovsrec_open_vswitch_col_ovs_version}, {NULL, NULL, NULL} }, {&ovsrec_table_bridge, &ovsrec_bridge_col_name, {&ovsrec_bridge_col_controller, &ovsrec_bridge_col_fail_mode, &ovsrec_bridge_col_ports}, {NULL, NULL, NULL} }, {&ovsrec_table_port, &ovsrec_port_col_name, {&ovsrec_port_col_tag, &ovsrec_port_col_trunks, &ovsrec_port_col_interfaces}, {NULL, NULL, NULL} }, {&ovsrec_table_interface, &ovsrec_interface_col_name, {&ovsrec_interface_col_type, &ovsrec_interface_col_options, &ovsrec_interface_col_error}, {NULL, NULL, NULL} }, {&ovsrec_table_controller, &ovsrec_controller_col_target, {&ovsrec_controller_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {&ovsrec_table_manager, &ovsrec_manager_col_target, {&ovsrec_manager_col_is_connected, NULL, NULL}, {NULL, NULL, NULL} }, {NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}} }; static void pre_cmd_emer_reset(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_manager_options); ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_mirrors); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_netflow); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_sflow); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ipfix); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_flood_vlans); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_other_config); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_other_config); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ingress_policing_rate); ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ingress_policing_burst); } static void cmd_emer_reset(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsdb_idl *idl = ctx->idl; const struct ovsrec_bridge *br; const struct ovsrec_port *port; const struct ovsrec_interface *iface; const struct ovsrec_mirror *mirror, *next_mirror; const struct ovsrec_controller *ctrl, *next_ctrl; const struct ovsrec_manager *mgr, *next_mgr; const struct ovsrec_netflow *nf, *next_nf; const struct ovsrec_ssl *ssl, *next_ssl; const struct ovsrec_sflow *sflow, *next_sflow; const struct ovsrec_ipfix *ipfix, *next_ipfix; const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset; /* Reset the Open_vSwitch table. */ ovsrec_open_vswitch_set_manager_options(vsctl_ctx->ovs, NULL, 0); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, NULL); OVSREC_BRIDGE_FOR_EACH (br, idl) { const char *hwaddr; ovsrec_bridge_set_controller(br, NULL, 0); ovsrec_bridge_set_fail_mode(br, NULL); ovsrec_bridge_set_mirrors(br, NULL, 0); ovsrec_bridge_set_netflow(br, NULL); ovsrec_bridge_set_sflow(br, NULL); ovsrec_bridge_set_ipfix(br, NULL); ovsrec_bridge_set_flood_vlans(br, NULL, 0); /* We only want to save the "hwaddr" key from other_config. */ hwaddr = smap_get(&br->other_config, "hwaddr"); if (hwaddr) { const struct smap smap = SMAP_CONST1(&smap, "hwaddr", hwaddr); ovsrec_bridge_set_other_config(br, &smap); } else { ovsrec_bridge_set_other_config(br, NULL); } } OVSREC_PORT_FOR_EACH (port, idl) { ovsrec_port_set_other_config(port, NULL); } OVSREC_INTERFACE_FOR_EACH (iface, idl) { /* xxx What do we do about gre/patch devices created by mgr? */ ovsrec_interface_set_ingress_policing_rate(iface, 0); ovsrec_interface_set_ingress_policing_burst(iface, 0); } OVSREC_MIRROR_FOR_EACH_SAFE (mirror, next_mirror, idl) { ovsrec_mirror_delete(mirror); } OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, next_ctrl, idl) { ovsrec_controller_delete(ctrl); } OVSREC_MANAGER_FOR_EACH_SAFE (mgr, next_mgr, idl) { ovsrec_manager_delete(mgr); } OVSREC_NETFLOW_FOR_EACH_SAFE (nf, next_nf, idl) { ovsrec_netflow_delete(nf); } OVSREC_SSL_FOR_EACH_SAFE (ssl, next_ssl, idl) { ovsrec_ssl_delete(ssl); } OVSREC_SFLOW_FOR_EACH_SAFE (sflow, next_sflow, idl) { ovsrec_sflow_delete(sflow); } OVSREC_IPFIX_FOR_EACH_SAFE (ipfix, next_ipfix, idl) { ovsrec_ipfix_delete(ipfix); } OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset, idl) { ovsrec_flow_sample_collector_set_delete(fscset); } vsctl_context_invalidate_cache(ctx); } static void cmd_add_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; const char *br_name, *parent_name; struct ovsrec_interface *iface; int vlan; br_name = ctx->argv[1]; if (ctx->argc == 2) { parent_name = NULL; vlan = 0; } else if (ctx->argc == 4) { parent_name = ctx->argv[2]; vlan = atoi(ctx->argv[3]); if (vlan < 0 || vlan > 4095) { ctl_fatal("%s: vlan must be between 0 and 4095", ctx->argv[0]); } } else { ctl_fatal("'%s' command takes exactly 1 or 3 arguments", ctx->argv[0]); } vsctl_context_populate_cache(ctx); if (may_exist) { struct vsctl_bridge *br; br = find_bridge(vsctl_ctx, br_name, false); if (br) { if (!parent_name) { if (br->parent) { ctl_fatal("\"--may-exist add-br %s\" but %s is " "a VLAN bridge for VLAN %d", br_name, br_name, br->vlan); } } else { if (!br->parent) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "is not a VLAN bridge", br_name, parent_name, vlan, br_name); } else if (strcmp(br->parent->name, parent_name)) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "has the wrong parent %s", br_name, parent_name, vlan, br_name, br->parent->name); } else if (br->vlan != vlan) { ctl_fatal("\"--may-exist add-br %s %s %d\" but %s " "is a VLAN bridge for the wrong VLAN %d", br_name, parent_name, vlan, br_name, br->vlan); } } return; } } check_conflicts(vsctl_ctx, br_name, xasprintf("cannot create a bridge named %s", br_name)); if (!parent_name) { struct ovsrec_port *port; struct ovsrec_bridge *br; iface = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(iface, br_name); ovsrec_interface_set_type(iface, "internal"); port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, br_name); ovsrec_port_set_interfaces(port, &iface, 1); br = ovsrec_bridge_insert(ctx->txn); ovsrec_bridge_set_name(br, br_name); ovsrec_bridge_set_ports(br, &port, 1); ovs_insert_bridge(vsctl_ctx->ovs, br); } else { struct vsctl_bridge *conflict; struct vsctl_bridge *parent; struct ovsrec_port *port; struct ovsrec_bridge *br; int64_t tag = vlan; parent = find_bridge(vsctl_ctx, parent_name, false); if (parent && parent->parent) { ctl_fatal("cannot create bridge with fake bridge as parent"); } if (!parent) { ctl_fatal("parent bridge %s does not exist", parent_name); } conflict = find_vlan_bridge(parent, vlan); if (conflict) { ctl_fatal("bridge %s already has a child VLAN bridge %s " "on VLAN %d", parent_name, conflict->name, vlan); } br = parent->br_cfg; iface = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(iface, br_name); ovsrec_interface_set_type(iface, "internal"); port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, br_name); ovsrec_port_set_interfaces(port, &iface, 1); ovsrec_port_set_fake_bridge(port, true); ovsrec_port_set_tag(port, &tag, 1); bridge_insert_port(br, port); } post_db_reload_expect_iface(iface); vsctl_context_invalidate_cache(ctx); } static void del_port(struct vsctl_context *vsctl_ctx, struct vsctl_port *port) { struct vsctl_iface *iface, *next_iface; bridge_delete_port((port->bridge->parent ? port->bridge->parent->br_cfg : port->bridge->br_cfg), port->port_cfg); LIST_FOR_EACH_SAFE (iface, next_iface, ifaces_node, &port->ifaces) { del_cached_iface(vsctl_ctx, iface); } del_cached_port(vsctl_ctx, port); } static void del_bridge(struct vsctl_context *vsctl_ctx, struct vsctl_bridge *br) { struct vsctl_bridge *child, *next_child; struct vsctl_port *port, *next_port; const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset; HMAP_FOR_EACH_SAFE (child, next_child, children_node, &br->children) { del_bridge(vsctl_ctx, child); } LIST_FOR_EACH_SAFE (port, next_port, ports_node, &br->ports) { del_port(vsctl_ctx, port); } OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset, vsctl_ctx->base.idl) { if (fscset->bridge == br->br_cfg) { ovsrec_flow_sample_collector_set_delete(fscset); } } del_cached_bridge(vsctl_ctx, br); } static void cmd_del_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], must_exist); if (bridge) { del_bridge(vsctl_ctx, bridge); } } static void output_sorted(struct svec *svec, struct ds *output) { const char *name; size_t i; svec_sort(svec); SVEC_FOR_EACH (i, name, svec) { ds_put_format(output, "%s\n", name); } } static void cmd_list_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct shash_node *node; struct svec bridges; bool real = shash_find(&ctx->options, "--real"); bool fake = shash_find(&ctx->options, "--fake"); /* If neither fake nor real were requested, return both. */ if (!real && !fake) { real = fake = true; } vsctl_context_populate_cache(ctx); svec_init(&bridges); SHASH_FOR_EACH (node, &vsctl_ctx->bridges) { struct vsctl_bridge *br = node->data; if (br->parent ? fake : real) { svec_add(&bridges, br->name); } } output_sorted(&bridges, &ctx->output); svec_destroy(&bridges); } static void cmd_br_exists(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); vsctl_context_populate_cache(ctx); if (!find_bridge(vsctl_ctx, ctx->argv[1], false)) { vsctl_exit(2); } } static void set_external_id(struct smap *old, struct smap *new, char *key, char *value) { smap_clone(new, old); if (value) { smap_replace(new, key, value); } else { smap_remove(new, key); } } static void pre_cmd_br_set_external_id(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_external_ids); ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_external_ids); } static void cmd_br_set_external_id(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; struct smap new; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->br_cfg) { set_external_id(&bridge->br_cfg->external_ids, &new, ctx->argv[2], ctx->argc >= 4 ? ctx->argv[3] : NULL); ovsrec_bridge_verify_external_ids(bridge->br_cfg); ovsrec_bridge_set_external_ids(bridge->br_cfg, &new); } else { char *key = xasprintf("fake-bridge-%s", ctx->argv[2]); struct vsctl_port *port = shash_find_data(&vsctl_ctx->ports, ctx->argv[1]); set_external_id(&port->port_cfg->external_ids, &new, key, ctx->argc >= 4 ? ctx->argv[3] : NULL); ovsrec_port_verify_external_ids(port->port_cfg); ovsrec_port_set_external_ids(port->port_cfg, &new); free(key); } smap_destroy(&new); } static void get_external_id(struct smap *smap, const char *prefix, const char *key, struct ds *output) { if (key) { char *prefix_key = xasprintf("%s%s", prefix, key); const char *value = smap_get(smap, prefix_key); if (value) { ds_put_format(output, "%s\n", value); } free(prefix_key); } else { const struct smap_node **sorted = smap_sort(smap); size_t prefix_len = strlen(prefix); size_t i; for (i = 0; i < smap_count(smap); i++) { const struct smap_node *node = sorted[i]; if (!strncmp(node->key, prefix, prefix_len)) { ds_put_format(output, "%s=%s\n", node->key + prefix_len, node->value); } } free(sorted); } } static void pre_cmd_br_get_external_id(struct ctl_context *ctx) { pre_cmd_br_set_external_id(ctx); } static void cmd_br_get_external_id(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->br_cfg) { ovsrec_bridge_verify_external_ids(bridge->br_cfg); get_external_id(&bridge->br_cfg->external_ids, "", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output); } else { struct vsctl_port *port = shash_find_data(&vsctl_ctx->ports, ctx->argv[1]); ovsrec_port_verify_external_ids(port->port_cfg); get_external_id(&port->port_cfg->external_ids, "fake-bridge-", ctx->argc >= 3 ? ctx->argv[2] : NULL, &ctx->output); } } static void cmd_list_ports(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct vsctl_port *port; struct svec ports; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); ovsrec_bridge_verify_ports(br->br_cfg ? br->br_cfg : br->parent->br_cfg); svec_init(&ports); LIST_FOR_EACH (port, ports_node, &br->ports) { if (strcmp(port->port_cfg->name, br->name)) { svec_add(&ports, port->port_cfg->name); } } output_sorted(&ports, &ctx->output); svec_destroy(&ports); } static void add_port(struct ctl_context *ctx, const char *br_name, const char *port_name, bool may_exist, bool fake_iface, char *iface_names[], int n_ifaces, char *settings[], int n_settings) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_port *vsctl_port; struct vsctl_bridge *bridge; struct ovsrec_interface **ifaces; struct ovsrec_port *port; size_t i; vsctl_context_populate_cache(ctx); if (may_exist) { struct vsctl_port *vsctl_port; vsctl_port = find_port(vsctl_ctx, port_name, false); if (vsctl_port) { struct svec want_names, have_names; svec_init(&want_names); for (i = 0; i < n_ifaces; i++) { svec_add(&want_names, iface_names[i]); } svec_sort(&want_names); svec_init(&have_names); for (i = 0; i < vsctl_port->port_cfg->n_interfaces; i++) { svec_add(&have_names, vsctl_port->port_cfg->interfaces[i]->name); } svec_sort(&have_names); if (strcmp(vsctl_port->bridge->name, br_name)) { char *command = vsctl_context_to_string(ctx); ctl_fatal("\"%s\" but %s is actually attached to bridge %s", command, port_name, vsctl_port->bridge->name); } if (!svec_equal(&want_names, &have_names)) { char *have_names_string = svec_join(&have_names, ", ", ""); char *command = vsctl_context_to_string(ctx); ctl_fatal("\"%s\" but %s actually has interface(s) %s", command, port_name, have_names_string); } svec_destroy(&want_names); svec_destroy(&have_names); return; } } check_conflicts(vsctl_ctx, port_name, xasprintf("cannot create a port named %s", port_name)); for (i = 0; i < n_ifaces; i++) { check_conflicts(vsctl_ctx, iface_names[i], xasprintf("cannot create an interface named %s", iface_names[i])); } bridge = find_bridge(vsctl_ctx, br_name, true); ifaces = xmalloc(n_ifaces * sizeof *ifaces); for (i = 0; i < n_ifaces; i++) { ifaces[i] = ovsrec_interface_insert(ctx->txn); ovsrec_interface_set_name(ifaces[i], iface_names[i]); post_db_reload_expect_iface(ifaces[i]); } port = ovsrec_port_insert(ctx->txn); ovsrec_port_set_name(port, port_name); ovsrec_port_set_interfaces(port, ifaces, n_ifaces); ovsrec_port_set_bond_fake_iface(port, fake_iface); if (bridge->parent) { int64_t tag = bridge->vlan; ovsrec_port_set_tag(port, &tag, 1); } for (i = 0; i < n_settings; i++) { ctl_set_column("Port", &port->header_, settings[i], ctx->symtab); } bridge_insert_port((bridge->parent ? bridge->parent->br_cfg : bridge->br_cfg), port); vsctl_port = add_port_to_cache(vsctl_ctx, bridge, port); for (i = 0; i < n_ifaces; i++) { add_iface_to_cache(vsctl_ctx, vsctl_port, ifaces[i]); } free(ifaces); } static void cmd_add_port(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, false, &ctx->argv[2], 1, &ctx->argv[3], ctx->argc - 3); } static void cmd_add_bond(struct ctl_context *ctx) { bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; bool fake_iface = shash_find(&ctx->options, "--fake-iface"); int n_ifaces; int i; n_ifaces = ctx->argc - 3; for (i = 3; i < ctx->argc; i++) { if (strchr(ctx->argv[i], '=')) { n_ifaces = i - 3; break; } } if (n_ifaces < 2) { ctl_fatal("add-bond requires at least 2 interfaces, but only " "%d were specified", n_ifaces); } add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist, fake_iface, &ctx->argv[3], n_ifaces, &ctx->argv[n_ifaces + 3], ctx->argc - 3 - n_ifaces); } static void cmd_del_port(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool must_exist = !shash_find(&ctx->options, "--if-exists"); bool with_iface = shash_find(&ctx->options, "--with-iface") != NULL; const char *target = ctx->argv[ctx->argc - 1]; struct vsctl_port *port; vsctl_context_populate_cache(ctx); if (find_bridge(vsctl_ctx, target, false)) { if (must_exist) { ctl_fatal("cannot delete port %s because it is the local port " "for bridge %s (deleting this port requires deleting " "the entire bridge)", target, target); } port = NULL; } else if (!with_iface) { port = find_port(vsctl_ctx, target, must_exist); } else { struct vsctl_iface *iface; port = find_port(vsctl_ctx, target, false); if (!port) { iface = find_iface(vsctl_ctx, target, false); if (iface) { port = iface->port; } } if (must_exist && !port) { ctl_fatal("no port or interface named %s", target); } } if (port) { if (ctx->argc == 3) { struct vsctl_bridge *bridge; bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (port->bridge != bridge) { if (port->bridge->parent == bridge) { ctl_fatal("bridge %s does not have a port %s (although " "its child bridge %s does)", ctx->argv[1], ctx->argv[2], port->bridge->name); } else { ctl_fatal("bridge %s does not have a port %s", ctx->argv[1], ctx->argv[2]); } } } del_port(vsctl_ctx, port); } } static void cmd_port_to_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_port *port; vsctl_context_populate_cache(ctx); port = find_port(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%s\n", port->bridge->name); } static void cmd_br_to_vlan(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%d\n", bridge->vlan); } static void cmd_br_to_parent(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *bridge; vsctl_context_populate_cache(ctx); bridge = find_bridge(vsctl_ctx, ctx->argv[1], true); if (bridge->parent) { bridge = bridge->parent; } ds_put_format(&ctx->output, "%s\n", bridge->name); } static void cmd_list_ifaces(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct vsctl_port *port; struct svec ifaces; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); verify_ports(vsctl_ctx); svec_init(&ifaces); LIST_FOR_EACH (port, ports_node, &br->ports) { struct vsctl_iface *iface; LIST_FOR_EACH (iface, ifaces_node, &port->ifaces) { if (strcmp(iface->iface_cfg->name, br->name)) { svec_add(&ifaces, iface->iface_cfg->name); } } } output_sorted(&ifaces, &ctx->output); svec_destroy(&ifaces); } static void cmd_iface_to_br(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_iface *iface; vsctl_context_populate_cache(ctx); iface = find_iface(vsctl_ctx, ctx->argv[1], true); ds_put_format(&ctx->output, "%s\n", iface->port->bridge->name); } static void verify_controllers(struct ovsrec_bridge *bridge) { size_t i; ovsrec_bridge_verify_controller(bridge); for (i = 0; i < bridge->n_controller; i++) { ovsrec_controller_verify_target(bridge->controller[i]); } } static void pre_controller(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_controller_col_target); } static void cmd_get_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; struct svec targets; size_t i; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } verify_controllers(br->br_cfg); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < br->br_cfg->n_controller; i++) { svec_add(&targets, br->br_cfg->controller[i]->target); } svec_sort(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_controllers(struct ovsrec_controller **controllers, size_t n_controllers) { size_t i; for (i = 0; i < n_controllers; i++) { ovsrec_controller_delete(controllers[i]); } } static void cmd_del_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_bridge *br; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true)->br_cfg; verify_controllers(br); if (br->controller) { delete_controllers(br->controller, br->n_controller); ovsrec_bridge_set_controller(br, NULL, 0); } } static struct ovsrec_controller ** insert_controllers(struct ovsdb_idl_txn *txn, char *targets[], size_t n) { struct ovsrec_controller **controllers; size_t i; controllers = xmalloc(n * sizeof *controllers); for (i = 0; i < n; i++) { if (vconn_verify_name(targets[i]) && pvconn_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } controllers[i] = ovsrec_controller_insert(txn); ovsrec_controller_set_target(controllers[i], targets[i]); } return controllers; } static void cmd_set_controller(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_controller **controllers; struct ovsrec_bridge *br; size_t n; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true)->br_cfg; verify_controllers(br); delete_controllers(br->controller, br->n_controller); n = ctx->argc - 2; controllers = insert_controllers(ctx->txn, &ctx->argv[2], n); ovsrec_bridge_set_controller(br, controllers, n); free(controllers); } static void cmd_get_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; const char *fail_mode; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } ovsrec_bridge_verify_fail_mode(br->br_cfg); fail_mode = br->br_cfg->fail_mode; if (fail_mode && strlen(fail_mode)) { ds_put_format(&ctx->output, "%s\n", fail_mode); } } static void cmd_del_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true); ovsrec_bridge_set_fail_mode(br->br_cfg, NULL); } static void cmd_set_fail_mode(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; const char *fail_mode = ctx->argv[2]; vsctl_context_populate_cache(ctx); br = find_real_bridge(vsctl_ctx, ctx->argv[1], true); if (strcmp(fail_mode, "standalone") && strcmp(fail_mode, "secure")) { ctl_fatal("fail-mode must be \"standalone\" or \"secure\""); } ovsrec_bridge_set_fail_mode(br->br_cfg, fail_mode); } static void verify_managers(const struct ovsrec_open_vswitch *ovs) { size_t i; ovsrec_open_vswitch_verify_manager_options(ovs); for (i = 0; i < ovs->n_manager_options; ++i) { const struct ovsrec_manager *mgr = ovs->manager_options[i]; ovsrec_manager_verify_target(mgr); } } static void pre_manager(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_manager_options); ovsdb_idl_add_column(ctx->idl, &ovsrec_manager_col_target); } static void cmd_get_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; struct svec targets; size_t i; verify_managers(ovs); /* Print the targets in sorted order for reproducibility. */ svec_init(&targets); for (i = 0; i < ovs->n_manager_options; i++) { svec_add(&targets, ovs->manager_options[i]->target); } svec_sort_unique(&targets); for (i = 0; i < targets.n; i++) { ds_put_format(&ctx->output, "%s\n", targets.names[i]); } svec_destroy(&targets); } static void delete_managers(const struct ovsrec_open_vswitch *ovs) { size_t i; /* Delete Manager rows pointed to by 'manager_options' column. */ for (i = 0; i < ovs->n_manager_options; i++) { ovsrec_manager_delete(ovs->manager_options[i]); } /* Delete 'Manager' row refs in 'manager_options' column. */ ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0); } static void cmd_del_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const struct ovsrec_open_vswitch *ovs = vsctl_ctx->ovs; verify_managers(ovs); delete_managers(ovs); } static void insert_managers(struct vsctl_context *vsctl_ctx, char *targets[], size_t n) { struct ovsrec_manager **managers; size_t i; /* Insert each manager in a new row in Manager table. */ managers = xmalloc(n * sizeof *managers); for (i = 0; i < n; i++) { if (stream_verify_name(targets[i]) && pstream_verify_name(targets[i])) { VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]); } managers[i] = ovsrec_manager_insert(vsctl_ctx->base.txn); ovsrec_manager_set_target(managers[i], targets[i]); } /* Store uuids of new Manager rows in 'manager_options' column. */ ovsrec_open_vswitch_set_manager_options(vsctl_ctx->ovs, managers, n); free(managers); } static void cmd_set_manager(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); const size_t n = ctx->argc - 1; verify_managers(vsctl_ctx->ovs); delete_managers(vsctl_ctx->ovs); insert_managers(vsctl_ctx, &ctx->argv[1], n); } static void pre_cmd_get_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_private_key); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_certificate); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_ca_cert); ovsdb_idl_add_column(ctx->idl, &ovsrec_ssl_col_bootstrap_ca_cert); } static void cmd_get_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); if (ssl) { ovsrec_ssl_verify_private_key(ssl); ovsrec_ssl_verify_certificate(ssl); ovsrec_ssl_verify_ca_cert(ssl); ovsrec_ssl_verify_bootstrap_ca_cert(ssl); ds_put_format(&ctx->output, "Private key: %s\n", ssl->private_key); ds_put_format(&ctx->output, "Certificate: %s\n", ssl->certificate); ds_put_format(&ctx->output, "CA Certificate: %s\n", ssl->ca_cert); ds_put_format(&ctx->output, "Bootstrap: %s\n", ssl->bootstrap_ca_cert ? "true" : "false"); } } static void pre_cmd_del_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); } static void cmd_del_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; if (ssl) { ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); ovsrec_ssl_delete(ssl); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, NULL); } } static void pre_cmd_set_ssl(struct ctl_context *ctx) { ovsdb_idl_add_column(ctx->idl, &ovsrec_open_vswitch_col_ssl); } static void cmd_set_ssl(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); bool bootstrap = shash_find(&ctx->options, "--bootstrap"); struct ovsrec_ssl *ssl = vsctl_ctx->ovs->ssl; ovsrec_open_vswitch_verify_ssl(vsctl_ctx->ovs); if (ssl) { ovsrec_ssl_delete(ssl); } ssl = ovsrec_ssl_insert(ctx->txn); ovsrec_ssl_set_private_key(ssl, ctx->argv[1]); ovsrec_ssl_set_certificate(ssl, ctx->argv[2]); ovsrec_ssl_set_ca_cert(ssl, ctx->argv[3]); ovsrec_ssl_set_bootstrap_ca_cert(ssl, bootstrap); ovsrec_open_vswitch_set_ssl(vsctl_ctx->ovs, ssl); } static void autoattach_insert_mapping(struct ovsrec_autoattach *aa, int64_t isid, int64_t vlan) { int64_t *key_mappings, *value_mappings; size_t i; key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings + 1)); value_mappings = xmalloc(sizeof *aa->value_mappings * (aa->n_mappings + 1)); for (i = 0; i < aa->n_mappings; i++) { key_mappings[i] = aa->key_mappings[i]; value_mappings[i] = aa->value_mappings[i]; } key_mappings[aa->n_mappings] = isid; value_mappings[aa->n_mappings] = vlan; ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, aa->n_mappings + 1); free(key_mappings); free(value_mappings); } static void cmd_add_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; int64_t isid, vlan; char *nptr = NULL; isid = strtoull(ctx->argv[2], &nptr, 10); if (nptr == ctx->argv[2] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[2]); return; } vlan = strtoull(ctx->argv[3], &nptr, 10); if (nptr == ctx->argv[3] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[3]); return; } vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } if (br->br_cfg) { if (!br->br_cfg->auto_attach) { struct ovsrec_autoattach *aa = ovsrec_autoattach_insert(ctx->txn); ovsrec_bridge_set_auto_attach(br->br_cfg, aa); } autoattach_insert_mapping(br->br_cfg->auto_attach, isid, vlan); } } static void del_aa_mapping(struct ovsrec_autoattach *aa, int64_t isid, int64_t vlan) { int64_t *key_mappings, *value_mappings; size_t i, n; key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings)); value_mappings = xmalloc(sizeof *value_mappings * (aa->n_mappings)); for (i = n = 0; i < aa->n_mappings; i++) { if (aa->key_mappings[i] != isid && aa->value_mappings[i] != vlan) { key_mappings[n] = aa->key_mappings[i]; value_mappings[n++] = aa->value_mappings[i]; } } ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, n); free(key_mappings); free(value_mappings); } static void cmd_del_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; int64_t isid, vlan; char *nptr = NULL; isid = strtoull(ctx->argv[2], &nptr, 10); if (nptr == ctx->argv[2] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[2]); return; } vlan = strtoull(ctx->argv[3], &nptr, 10); if (nptr == ctx->argv[3] || nptr == NULL) { ctl_fatal("Invalid argument %s", ctx->argv[3]); return; } vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } if (br->br_cfg && br->br_cfg->auto_attach && br->br_cfg->auto_attach->key_mappings && br->br_cfg->auto_attach->value_mappings) { size_t i; for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) { if (br->br_cfg->auto_attach->key_mappings[i] == isid && br->br_cfg->auto_attach->value_mappings[i] == vlan) { del_aa_mapping(br->br_cfg->auto_attach, isid, vlan); break; } } } } static void pre_aa_mapping(struct ctl_context *ctx) { pre_get_info(ctx); ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_auto_attach); ovsdb_idl_add_column(ctx->idl, &ovsrec_autoattach_col_mappings); } static void verify_auto_attach(struct ovsrec_bridge *bridge) { if (bridge) { ovsrec_bridge_verify_auto_attach(bridge); if (bridge->auto_attach) { ovsrec_autoattach_verify_mappings(bridge->auto_attach); } } } static void cmd_get_aa_mapping(struct ctl_context *ctx) { struct vsctl_context *vsctl_ctx = vsctl_context_cast(ctx); struct vsctl_bridge *br; vsctl_context_populate_cache(ctx); br = find_bridge(vsctl_ctx, ctx->argv[1], true); if (br->parent) { br = br->parent; } verify_auto_attach(br->br_cfg); if (br->br_cfg && br->br_cfg->auto_attach && br->br_cfg->auto_attach->key_mappings && br->br_cfg->auto_attach->value_mappings) { size_t i; for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) { ds_put_format(&ctx->output, "%"PRId64" %"PRId64"\n", br->br_cfg->auto_attach->key_mappings[i], br->br_cfg->auto_attach->value_mappings[i]); } } } static const struct ctl_table_class tables[] = { {&ovsrec_table_bridge, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL}, {&ovsrec_table_flow_sample_collector_set, NULL, &ovsrec_flow_sample_collector_set_col_bridge}}}, {&ovsrec_table_controller, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, &ovsrec_bridge_col_controller}}}, {&ovsrec_table_interface, {{&ovsrec_table_interface, &ovsrec_interface_col_name, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_mirror, {{&ovsrec_table_mirror, &ovsrec_mirror_col_name, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_manager, {{&ovsrec_table_manager, &ovsrec_manager_col_target, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_netflow, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, &ovsrec_bridge_col_netflow}, {NULL, NULL, NULL}}}, {&ovsrec_table_open_vswitch, {{&ovsrec_table_open_vswitch, NULL, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_port, {{&ovsrec_table_port, &ovsrec_port_col_name, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_qos, {{&ovsrec_table_port, &ovsrec_port_col_name, &ovsrec_port_col_qos}, {NULL, NULL, NULL}}}, {&ovsrec_table_queue, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_ssl, {{&ovsrec_table_open_vswitch, NULL, &ovsrec_open_vswitch_col_ssl}}}, {&ovsrec_table_sflow, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, &ovsrec_bridge_col_sflow}, {NULL, NULL, NULL}}}, {&ovsrec_table_flow_table, {{&ovsrec_table_flow_table, &ovsrec_flow_table_col_name, NULL}, {NULL, NULL, NULL}}}, {&ovsrec_table_ipfix, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, &ovsrec_bridge_col_ipfix}, {&ovsrec_table_flow_sample_collector_set, NULL, &ovsrec_flow_sample_collector_set_col_ipfix}}}, {&ovsrec_table_autoattach, {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, &ovsrec_bridge_col_auto_attach}, {NULL, NULL, NULL}}}, {&ovsrec_table_flow_sample_collector_set, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}, {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; static void post_db_reload_check_init(void) { n_neoteric_ifaces = 0; } static void post_db_reload_expect_iface(const struct ovsrec_interface *iface) { if (n_neoteric_ifaces >= allocated_neoteric_ifaces) { neoteric_ifaces = x2nrealloc(neoteric_ifaces, &allocated_neoteric_ifaces, sizeof *neoteric_ifaces); } neoteric_ifaces[n_neoteric_ifaces++] = iface->header_.uuid; } static void post_db_reload_do_checks(const struct vsctl_context *vsctl_ctx) { struct ds dead_ifaces = DS_EMPTY_INITIALIZER; size_t i; for (i = 0; i < n_neoteric_ifaces; i++) { const struct uuid *uuid; uuid = ovsdb_idl_txn_get_insert_uuid(vsctl_ctx->base.txn, &neoteric_ifaces[i]); if (uuid) { const struct ovsrec_interface *iface; iface = ovsrec_interface_get_for_uuid(vsctl_ctx->base.idl, uuid); if (iface && (!iface->ofport || *iface->ofport == -1)) { ds_put_format(&dead_ifaces, "'%s', ", iface->name); } } } if (dead_ifaces.length) { dead_ifaces.length -= 2; /* Strip off trailing comma and space. */ ovs_error(0, "Error detected while setting up %s. See ovs-vswitchd " "log for details.", ds_cstr(&dead_ifaces)); } ds_destroy(&dead_ifaces); } static void vsctl_context_init_command(struct vsctl_context *vsctl_ctx, struct ctl_command *command) { ctl_context_init_command(&vsctl_ctx->base, command); vsctl_ctx->verified_ports = false; } static void vsctl_context_init(struct vsctl_context *vsctl_ctx, struct ctl_command *command, struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, const struct ovsrec_open_vswitch *ovs, struct ovsdb_symbol_table *symtab) { ctl_context_init(&vsctl_ctx->base, command, idl, txn, symtab, vsctl_context_invalidate_cache); if (command) { vsctl_ctx->verified_ports = false; } vsctl_ctx->ovs = ovs; vsctl_ctx->cache_valid = false; } static void vsctl_context_done_command(struct vsctl_context *vsctl_ctx, struct ctl_command *command) { ctl_context_done_command(&vsctl_ctx->base, command); } static void vsctl_context_done(struct vsctl_context *vsctl_ctx, struct ctl_command *command) { ctl_context_done(&vsctl_ctx->base, command); } static void run_prerequisites(struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ctl_command *c; ovsdb_idl_add_table(idl, &ovsrec_table_open_vswitch); if (wait_for_reload) { ovsdb_idl_add_column(idl, &ovsrec_open_vswitch_col_cur_cfg); } for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->prerequisites) { struct vsctl_context vsctl_ctx; ds_init(&c->output); c->table = NULL; vsctl_context_init(&vsctl_ctx, c, idl, NULL, NULL, NULL); (c->syntax->prerequisites)(&vsctl_ctx.base); vsctl_context_done(&vsctl_ctx, c); ovs_assert(!c->output.string); ovs_assert(!c->table); } } } static void do_vsctl(const char *args, struct ctl_command *commands, size_t n_commands, struct ovsdb_idl *idl) { struct ovsdb_idl_txn *txn; const struct ovsrec_open_vswitch *ovs; enum ovsdb_idl_txn_status status; struct ovsdb_symbol_table *symtab; struct vsctl_context vsctl_ctx; struct ctl_command *c; struct shash_node *node; int64_t next_cfg = 0; char *error = NULL; txn = the_idl_txn = ovsdb_idl_txn_create(idl); if (dry_run) { ovsdb_idl_txn_set_dry_run(txn); } ovsdb_idl_txn_add_comment(txn, "ovs-vsctl: %s", args); ovs = ovsrec_open_vswitch_first(idl); if (!ovs) { /* XXX add verification that table is empty */ ovs = ovsrec_open_vswitch_insert(txn); } if (wait_for_reload) { ovsdb_idl_txn_increment(txn, &ovs->header_, &ovsrec_open_vswitch_col_next_cfg); } post_db_reload_check_init(); symtab = ovsdb_symbol_table_create(); for (c = commands; c < &commands[n_commands]; c++) { ds_init(&c->output); c->table = NULL; } vsctl_context_init(&vsctl_ctx, NULL, idl, txn, ovs, symtab); for (c = commands; c < &commands[n_commands]; c++) { vsctl_context_init_command(&vsctl_ctx, c); if (c->syntax->run) { (c->syntax->run)(&vsctl_ctx.base); } vsctl_context_done_command(&vsctl_ctx, c); if (vsctl_ctx.base.try_again) { vsctl_context_done(&vsctl_ctx, NULL); goto try_again; } } vsctl_context_done(&vsctl_ctx, NULL); SHASH_FOR_EACH (node, &symtab->sh) { struct ovsdb_symbol *symbol = node->data; if (!symbol->created) { ctl_fatal("row id \"%s\" is referenced but never created (e.g. " "with \"-- --id=%s create ...\")", node->name, node->name); } if (!symbol->strong_ref) { if (!symbol->weak_ref) { VLOG_WARN("row id \"%s\" was created but no reference to it " "was inserted, so it will not actually appear in " "the database", node->name); } else { VLOG_WARN("row id \"%s\" was created but only a weak " "reference to it was inserted, so it will not " "actually appear in the database", node->name); } } } status = ovsdb_idl_txn_commit_block(txn); if (wait_for_reload && status == TXN_SUCCESS) { next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); } if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { for (c = commands; c < &commands[n_commands]; c++) { if (c->syntax->postprocess) { vsctl_context_init(&vsctl_ctx, c, idl, txn, ovs, symtab); (c->syntax->postprocess)(&vsctl_ctx.base); vsctl_context_done(&vsctl_ctx, c); } } } error = xstrdup(ovsdb_idl_txn_get_error(txn)); switch (status) { case TXN_UNCOMMITTED: case TXN_INCOMPLETE: OVS_NOT_REACHED(); case TXN_ABORTED: /* Should not happen--we never call ovsdb_idl_txn_abort(). */ ctl_fatal("transaction aborted"); case TXN_UNCHANGED: case TXN_SUCCESS: break; case TXN_TRY_AGAIN: goto try_again; case TXN_ERROR: ctl_fatal("transaction error: %s", error); case TXN_NOT_LOCKED: /* Should not happen--we never call ovsdb_idl_set_lock(). */ ctl_fatal("database not locked"); default: OVS_NOT_REACHED(); } free(error); ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { struct ds *ds = &c->output; if (c->table) { table_print(c->table, &table_style); } else if (oneline) { size_t j; ds_chomp(ds, '\n'); for (j = 0; j < ds->length; j++) { int ch = ds->string[j]; switch (ch) { case '\n': fputs("\\n", stdout); break; case '\\': fputs("\\\\", stdout); break; default: putchar(ch); } } putchar('\n'); } else { fputs(ds_cstr(ds), stdout); } ds_destroy(&c->output); table_destroy(c->table); free(c->table); shash_destroy_free_data(&c->options); } free(commands); if (wait_for_reload && status != TXN_UNCHANGED) { /* Even, if --retry flag was not specified, ovs-vsctl still * has to retry to establish OVSDB connection, if wait_for_reload * was set. Otherwise, ovs-vsctl would end up waiting forever * until cur_cfg would be updated. */ ovsdb_idl_enable_reconnect(idl); for (;;) { ovsdb_idl_run(idl); OVSREC_OPEN_VSWITCH_FOR_EACH (ovs, idl) { if (ovs->cur_cfg >= next_cfg) { post_db_reload_do_checks(&vsctl_ctx); goto done; } } ovsdb_idl_wait(idl); poll_block(); } done: ; } ovsdb_idl_txn_destroy(txn); ovsdb_idl_destroy(idl); exit(EXIT_SUCCESS); try_again: /* Our transaction needs to be rerun, or a prerequisite was not met. Free * resources and return so that the caller can try again. */ if (txn) { ovsdb_idl_txn_abort(txn); ovsdb_idl_txn_destroy(txn); the_idl_txn = NULL; } ovsdb_symbol_table_destroy(symtab); for (c = commands; c < &commands[n_commands]; c++) { ds_destroy(&c->output); table_destroy(c->table); free(c->table); } free(error); } /* Frees the current transaction and the underlying IDL and then calls * exit(status). * * Freeing the transaction and the IDL is not strictly necessary, but it makes * for a clean memory leak report from valgrind in the normal case. That makes * it easier to notice real memory leaks. */ static void vsctl_exit(int status) { if (the_idl_txn) { ovsdb_idl_txn_abort(the_idl_txn); ovsdb_idl_txn_destroy(the_idl_txn); } ovsdb_idl_destroy(the_idl); exit(status); } /* * Developers who add new commands to the 'struct ctl_command_syntax' must * define the 'arguments' member of the struct. The following keywords are * available for composing the argument format: * * TABLE RECORD BRIDGE PARENT PORT * KEY VALUE ARG KEY=VALUE ?KEY=VALUE * IFACE SYSIFACE COLUMN COLUMN?:KEY COLUMN?:KEY=VALUE * MODE CA-CERT CERTIFICATE PRIVATE-KEY * TARGET NEW-* (e.g. NEW-PORT) * * For argument types not listed above, just uses 'ARG' as place holder. * * Encloses the keyword with '[]' if it is optional. Appends '...' to * keyword or enclosed keyword to indicate that the argument can be specified * multiple times. * * */ static const struct ctl_command_syntax vsctl_commands[] = { /* Open vSwitch commands. */ {"init", 0, 0, "", NULL, cmd_init, NULL, "", RW}, /* Bridge commands. */ {"add-br", 1, 3, "NEW-BRIDGE [PARENT] [NEW-VLAN]", pre_get_info, cmd_add_br, NULL, "--may-exist", RW}, {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_del_br, NULL, "--if-exists", RW}, {"list-br", 0, 0, "", pre_get_info, cmd_list_br, NULL, "--real,--fake", RO}, {"br-exists", 1, 1, "BRIDGE", pre_get_info, cmd_br_exists, NULL, "", RO}, {"br-to-vlan", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_vlan, NULL, "", RO}, {"br-to-parent", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_parent, NULL, "", RO}, {"br-set-external-id", 2, 3, "BRIDGE KEY [VALUE]", pre_cmd_br_set_external_id, cmd_br_set_external_id, NULL, "", RW}, {"br-get-external-id", 1, 2, "BRIDGE [KEY]", pre_cmd_br_get_external_id, cmd_br_get_external_id, NULL, "", RO}, /* Port commands. */ {"list-ports", 1, 1, "BRIDGE", pre_get_info, cmd_list_ports, NULL, "", RO}, {"add-port", 2, INT_MAX, "BRIDGE NEW-PORT [COLUMN[:KEY]=VALUE]...", pre_get_info, cmd_add_port, NULL, "--may-exist", RW}, {"add-bond", 4, INT_MAX, "BRIDGE NEW-BOND-PORT SYSIFACE... [COLUMN[:KEY]=VALUE]...", pre_get_info, cmd_add_bond, NULL, "--may-exist,--fake-iface", RW}, {"del-port", 1, 2, "[BRIDGE] PORT|IFACE", pre_get_info, cmd_del_port, NULL, "--if-exists,--with-iface", RW}, {"port-to-br", 1, 1, "PORT", pre_get_info, cmd_port_to_br, NULL, "", RO}, /* Interface commands. */ {"list-ifaces", 1, 1, "BRIDGE", pre_get_info, cmd_list_ifaces, NULL, "", RO}, {"iface-to-br", 1, 1, "IFACE", pre_get_info, cmd_iface_to_br, NULL, "", RO}, /* Controller commands. */ {"get-controller", 1, 1, "BRIDGE", pre_controller, cmd_get_controller, NULL, "", RO}, {"del-controller", 1, 1, "BRIDGE", pre_controller, cmd_del_controller, NULL, "", RW}, {"set-controller", 1, INT_MAX, "BRIDGE TARGET...", pre_controller, cmd_set_controller, NULL, "", RW}, {"get-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_get_fail_mode, NULL, "", RO}, {"del-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_del_fail_mode, NULL, "", RW}, {"set-fail-mode", 2, 2, "BRIDGE MODE", pre_get_info, cmd_set_fail_mode, NULL, "", RW}, /* Manager commands. */ {"get-manager", 0, 0, "", pre_manager, cmd_get_manager, NULL, "", RO}, {"del-manager", 0, 0, "", pre_manager, cmd_del_manager, NULL, "", RW}, {"set-manager", 1, INT_MAX, "TARGET...", pre_manager, cmd_set_manager, NULL, "", RW}, /* SSL commands. */ {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", RW}, /* Auto Attach commands. */ {"add-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_add_aa_mapping, NULL, "", RW}, {"del-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_del_aa_mapping, NULL, "", RW}, {"get-aa-mapping", 1, 1, "BRIDGE", pre_aa_mapping, cmd_get_aa_mapping, NULL, "", RO}, /* Switch commands. */ {"emer-reset", 0, 0, "", pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW}, {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, }; /* Registers vsctl and common db commands. */ static void vsctl_cmd_init(void) { ctl_init(tables, cmd_show_tables, vsctl_exit); ctl_register_commands(vsctl_commands); } openvswitch-2.5.9/utilities/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020016 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801424.621852246 openvswitch-2.5.9/utilities/automake.mk0000644000175000017500000001044013534540071021503 0ustar00jpettitjpettit00000000000000bin_PROGRAMS += \ utilities/ovs-appctl \ utilities/ovs-testcontroller \ utilities/ovs-dpctl \ utilities/ovs-ofctl \ utilities/ovs-vsctl bin_SCRIPTS += utilities/ovs-docker \ utilities/ovs-pki if HAVE_PYTHON bin_SCRIPTS += \ utilities/ovs-dpctl-top \ utilities/ovs-l3ping \ utilities/ovs-parse-backtrace \ utilities/ovs-pcap \ utilities/ovs-tcpundump \ utilities/ovs-test \ utilities/ovs-vlan-test endif scripts_SCRIPTS += \ utilities/ovs-check-dead-ifs \ utilities/ovs-ctl \ utilities/ovs-save scripts_DATA += utilities/ovs-lib completion_SCRIPTS += \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash check_SCRIPTS += \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-vsctl-bashcomp.bash EXTRA_DIST += utilities/ovs-sim.in utilities/ovs-sim.1.xml noinst_man_MANS += utilities/ovs-sim.1 noinst_SCRIPTS += utilities/ovs-sim utilities/ovs-lib: $(top_builddir)/config.status docs += utilities/ovs-command-bashcomp.INSTALL.md EXTRA_DIST += \ utilities/ovs-appctl-bashcomp.bash \ utilities/ovs-check-dead-ifs.in \ utilities/ovs-command-bashcomp.INSTALL.md \ utilities/ovs-ctl.in \ utilities/ovs-dev.py \ utilities/ovs-docker \ utilities/ovs-dpctl-top.in \ utilities/ovs-l3ping.in \ utilities/ovs-lib.in \ utilities/ovs-parse-backtrace.in \ utilities/ovs-pcap.in \ utilities/ovs-pipegen.py \ utilities/ovs-pki.in \ utilities/ovs-save \ utilities/ovs-tcpundump.in \ utilities/ovs-test.in \ utilities/ovs-vlan-test.in \ utilities/ovs-vsctl-bashcomp.bash \ utilities/qemu-wrap.py MAN_ROOTS += \ utilities/ovs-appctl.8.in \ utilities/ovs-benchmark.1.in \ utilities/ovs-testcontroller.8.in \ utilities/ovs-ctl.8 \ utilities/ovs-dpctl.8.in \ utilities/ovs-dpctl-top.8.in \ utilities/ovs-l3ping.8.in \ utilities/ovs-ofctl.8.in \ utilities/ovs-parse-backtrace.8 \ utilities/ovs-pcap.1.in \ utilities/ovs-pki.8.in \ utilities/ovs-tcpundump.1.in \ utilities/ovs-vlan-bug-workaround.8.in \ utilities/ovs-test.8.in \ utilities/ovs-vlan-test.8.in \ utilities/ovs-vsctl.8.in MAN_FRAGMENTS += utilities/ovs-vlan-bugs.man DISTCLEANFILES += \ utilities/ovs-appctl.8 \ utilities/ovs-ctl \ utilities/ovs-benchmark.1 \ utilities/ovs-check-dead-ifs \ utilities/ovs-testcontroller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-dpctl-top \ utilities/ovs-dpctl-top.8 \ utilities/ovs-l3ping \ utilities/ovs-l3ping.8 \ utilities/ovs-lib \ utilities/ovs-ofctl.8 \ utilities/ovs-parse-backtrace \ utilities/ovs-pcap \ utilities/ovs-pcap.1 \ utilities/ovs-pki \ utilities/ovs-pki.8 \ utilities/ovs-sim \ utilities/ovs-sim.1 \ utilities/ovs-tcpundump \ utilities/ovs-tcpundump.1 \ utilities/ovs-test \ utilities/ovs-test.8 \ utilities/ovs-vlan-test \ utilities/ovs-vlan-test.8 \ utilities/ovs-vlan-bug-workaround.8 \ utilities/ovs-vsctl.8 man_MANS += \ utilities/ovs-appctl.8 \ utilities/ovs-benchmark.1 \ utilities/ovs-ctl.8 \ utilities/ovs-testcontroller.8 \ utilities/ovs-dpctl.8 \ utilities/ovs-dpctl-top.8 \ utilities/ovs-l3ping.8 \ utilities/ovs-ofctl.8 \ utilities/ovs-parse-backtrace.8 \ utilities/ovs-pcap.1 \ utilities/ovs-pki.8 \ utilities/ovs-tcpundump.1 \ utilities/ovs-vlan-bug-workaround.8 \ utilities/ovs-test.8 \ utilities/ovs-vlan-test.8 \ utilities/ovs-vsctl.8 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c utilities_ovs_appctl_LDADD = lib/libopenvswitch.la utilities_ovs_testcontroller_SOURCES = utilities/ovs-testcontroller.c utilities_ovs_testcontroller_LDADD = lib/libopenvswitch.la $(SSL_LIBS) utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c utilities_ovs_dpctl_LDADD = lib/libopenvswitch.la utilities_ovs_ofctl_SOURCES = utilities/ovs-ofctl.c utilities_ovs_ofctl_LDADD = \ ofproto/libofproto.la \ lib/libopenvswitch.la utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c utilities_ovs_vsctl_LDADD = lib/libopenvswitch.la if LINUX sbin_PROGRAMS += utilities/ovs-vlan-bug-workaround utilities_ovs_vlan_bug_workaround_SOURCES = utilities/ovs-vlan-bug-workaround.c utilities_ovs_vlan_bug_workaround_LDADD = lib/libopenvswitch.la noinst_PROGRAMS += utilities/nlmon utilities_nlmon_SOURCES = utilities/nlmon.c utilities_nlmon_LDADD = lib/libopenvswitch.la endif bin_PROGRAMS += utilities/ovs-benchmark utilities_ovs_benchmark_SOURCES = utilities/ovs-benchmark.c utilities_ovs_benchmark_LDADD = lib/libopenvswitch.la include utilities/bugtool/automake.mk openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-pki.8.in0000644000000000000000000000013213534540071017745 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.769845965 openvswitch-2.5.9/utilities/ovs-pki.8.in0000644000175000017500000002072213534540071021436 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-pki 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .SH NAME ovs\-pki \- OpenFlow public key infrastructure management utility .SH SYNOPSIS Each command takes the form: .sp \fBovs\-pki\fR [\fIoptions\fR] \fIcommand\fR [\fIargs\fR] .sp The implemented commands and their arguments are: .br \fBovs\-pki\fR \fBinit\fR .br \fBovs\-pki\fR \fBreq\fR \fIname\fR .br \fBovs\-pki\fR \fBsign\fR \fIname\fR [\fItype\fR] .br \fBovs\-pki\fR \fBreq+sign\fR \fIname\fR [\fItype\fR] .br \fBovs\-pki\fR \fBverify\fR \fIname\fR [\fItype\fR] .br \fBovs\-pki\fR \fBfingerprint\fR \fIfile\fR .br \fBovs\-pki\fR \fBself\-sign\fR \fIname\fR .sp Each \fItype\fR above is a certificate type, either \fBswitch\fR (default) or \fBcontroller\fR. .sp The available options are: .br [\fB\-k\fR \fItype\fR | \fB\-\^\-key=\fItype\fR] .br [\fB\-B\fR \fInbits\fR | \fB\-\^\-bits=\fInbits\fR] .br [\fB\-D\fR \fIfile\fR | \fB\-\^\-dsaparam=\fIfile\fR] .br [\fB\-b\fR | \fB\-\^\-batch\fR] .br [\fB\-f\fR | \fB\-\^\-force\fR] .br [\fB\-d\fR \fIdir\fR | \fB\-\^\-dir=\fR\fIdir\fR] .br [\fB\-l\fR \fIfile\fR | \fB\-\^\-log=\fIfile\fR] .br [\fB\-h\fR | \fB\-\^\-help\fR] .sp Some options do not apply to every command. .SH DESCRIPTION The \fBovs\-pki\fR program sets up and manages a public key infrastructure for use with OpenFlow. It is intended to be a simple interface for organizations that do not have an established public key infrastructure. Other PKI tools can substitute for or supplement the use of \fBovs\-pki\fR. \fBovs\-pki\fR uses \fBopenssl\fR(1) for certificate management and key generation. .SH "OFFLINE COMMANDS" The following \fBovs\-pki\fR commands support manual PKI administration: .TP \fBinit\fR Initializes a new PKI (by default in directory \fB@PKIDIR@\fR) and populates it with a pair of certificate authorities for controllers and switches. This command should ideally be run on a high\-security machine separate from any OpenFlow controller or switch, called the CA machine. The files \fBpki/controllerca/cacert.pem\fR and \fBpki/switchca/cacert.pem\fR that it produces will need to be copied over to the OpenFlow switches and controllers, respectively. Their contents may safely be made public. By default, \fBovs\-pki\fR generates 2048\-bit RSA keys. The \fB\-B\fR or \fB\-\^\-bits\fR option (see below) may be used to override the key length. The \fB\-k dsa\fR or \fB\-\^\-key=dsa\fR option may be used to use DSA in place of RSA. If DSA is selected, the \fBdsaparam.pem\fR file generated in the new PKI hierarchy must be copied to any machine on which the \fBreq\fR command (see below) will be executed. Its contents may safely be made public. Other files generated by \fBinit\fR may remain on the CA machine. The files \fBpki/controllerca/private/cakey.pem\fR and \fBpki/switchca/private/cakey.pem\fR have particularly sensitive contents that should not be exposed. .TP \fBreq\fR \fIname\fR Generates a new private key named \fIname\fR\fB\-privkey.pem\fR and corresponding certificate request named \fIname\fR\fB\-req.pem\fR. The private key can be intended for use by a switch or a controller. This command should ideally be run on the switch or controller that will use the private key to identify itself. The file \fIname\fR\fB\-req.pem\fR must be copied to the CA machine for signing with the \fBsign\fR command (below). This command will output a fingerprint to stdout as its final step. Write down the fingerprint and take it to the CA machine before continuing with the \fBsign\fR step. When RSA keys are in use (as is the default), \fBreq\fR, unlike the rest of \fBovs\-pki\fR's commands, does not need access to a PKI hierarchy created by \fBovs\-pki init\fR. The \fB\-B\fR or \fB\-\^\-bits\fR option (see below) may be used to specify the number of bits in the generated RSA key. When DSA keys are used (as specified with \fB\-\^\-key=dsa\fR), \fBreq\fR needs access to the \fBdsaparam.pem\fR file created as part of the PKI hierarchy (but not to other files in that tree). By default, \fBovs\-pki\fR looks for this file in \fB@PKIDIR@/dsaparam.pem\fR, but the \fB\-D\fR or \fB\-\^\-dsaparam\fR option (see below) may be used to specify an alternate location. \fIname\fR\fB\-privkey.pem\fR has sensitive contents that should not be exposed. \fIname\fR\fB\-req.pem\fR may be safely made public. .TP \fBsign\fR \fIname\fR [\fItype\fR] Signs the certificate request named \fIname\fR\fB\-req.pem\fR that was produced in the previous step, producing a certificate named \fIname\fR\fB\-cert.pem\fR. \fItype\fR, either \fBswitch\fR (default) or \fBcontroller\fR, indicates the use for which the key is being certified. This command must be run on the CA machine. The command will output a fingerprint to stdout and request that you verify that it is the same fingerprint output by the \fBreq\fR command. This ensures that the request being signed is the same one produced by \fBreq\fR. (The \fB\-b\fR or \fB\-\^\-batch\fR option suppresses the verification step.) The file \fIname\fR\fB\-cert.pem\fR will need to be copied back to the switch or controller for which it is intended. Its contents may safely be made public. .TP \fBreq+sign\fR \fIname\fR [\fItype\fR] Combines the \fBreq\fR and \fBsign\fR commands into a single step, outputting all the files produced by each. The \fIname\fR\fB\-privkey.pem\fR and \fIname\fR\fB\-cert.pem\fR files must be copied securely to the switch or controller. \fIname\fR\fB\-privkey.pem\fR has sensitive contents and must not be exposed in transit. Afterward, it should be deleted from the CA machine. This combined method is, theoretically, less secure than the individual steps performed separately on two different machines, because there is additional potential for exposure of the private key. However, it is also more convenient. .TP \fBverify\fR \fIname\fR [\fItype\fR] Verifies that \fIname\fR\fB\-cert.pem\fR is a valid certificate for the given \fItype\fR of use, either \fBswitch\fR (default) or \fBcontroller\fR. If the certificate is valid for this use, it prints the message ``\fIname\fR\fB\-cert.pem\fR: OK''; otherwise, it prints an error message. .TP \fBfingerprint\fR \fIfile\fR Prints the fingerprint for \fIfile\fR. If \fIfile\fR is a certificate, then this is the SHA\-1 digest of the DER encoded version of the certificate; otherwise, it is the SHA\-1 digest of the entire file. .TP \fBself\-sign\fR \fIname\fR Signs the certificate request named \fIname\fB\-req.pem\fR using the private key \fIname\fB\-privkey.pem\fR, producing a self-signed certificate named \fIname\fB\-cert.pem\fR. The input files should have been produced with \fBovs\-pki req\fR. Some controllers accept such self-signed certificates. .SH OPTIONS .IP "\fB\-k\fR \fItype\fR" .IQ "\fB\-\^\-key=\fItype\fR" For the \fBinit\fR command, sets the public key algorithm to use for the new PKI hierarchy. For the \fBreq\fR and \fBreq+sign\fR commands, sets the public key algorithm to use for the key to be generated, which must match the value specified on \fBinit\fR. With other commands, the value has no effect. The \fItype\fR may be \fBrsa\fR (the default) or \fBdsa\fR. .IP "\fB\-B\fR \fInbits\fR" .IQ "\fB\-\^\-bits=\fInbits\fR" Sets the number of bits in the key to be generated. When RSA keys are in use, this option affects only the \fBinit\fR, \fBreq\fR, and \fBreq+sign\fR commands, and the same value should be given each time. With DSA keys are in use, this option affects only the \fBinit\fR command. The value must be at least 1024. The default is 2048. .IP "\fB\-D\fR \fIfile\fR" .IQ "\fB\-\^\-dsaparam=\fIfile\fR" Specifies an alternate location for the \fBdsaparam.pem\fR file required by the \fBreq\fR and \fBreq+sign\fR commands. This option affects only these commands, and only when DSA keys are used. The default is \fBdsaparam.pem\fR under the PKI hierarchy. .IP "\fB\-b\fR" .IQ "\fB\-\^\-batch\fR" Suppresses the interactive verification of fingerprints that the \fBsign\fR command by default requires. .IP "\fB\-d\fR \fIdir\fR" .IQ "\fB\-\^\-dir=\fR\fIdir\fR" Specifies the location of the PKI hierarchy to be used or created by the command (default: \fB@PKIDIR@\fR). All commands, except \fBreq\fR, need access to a PKI hierarchy. .IP "\fB\-f\fR" .IQ "\fB\-\^\-force\fR" By default, \fBovs\-pki\fR will not overwrite existing files or directories. This option overrides this behavior. .IP "\fB\-l\fR \fIfile\fR" .IQ "\fB\-\^\-log=\fIfile\fR" Sets the log file to \fIfile\fR. Default: \fB@LOGDIR@/ovs\-pki.log\fR. .IP "\fB\-h\fR" .IQ "\fB\-\^\-help\fR" Prints a help usage message and exits. openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-appctl.c0000644000000000000000000000013213534540071020113 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.137686576 30 ctime=1567801425.189856434 openvswitch-2.5.9/utilities/ovs-appctl.c0000644000175000017500000001476513534540071021616 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "command-line.h" #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "jsonrpc.h" #include "process.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" static void usage(void); static const char *parse_command_line(int argc, char *argv[]); static struct jsonrpc *connect_to_target(const char *target); int main(int argc, char *argv[]) { char *cmd_result, *cmd_error; struct jsonrpc *client; char *cmd, **cmd_argv; const char *target; int cmd_argc; int error; set_program_name(argv[0]); /* Parse command line and connect to target. */ target = parse_command_line(argc, argv); client = connect_to_target(target); /* Transact request and process reply. */ cmd = argv[optind++]; cmd_argc = argc - optind; cmd_argv = cmd_argc ? argv + optind : NULL; error = unixctl_client_transact(client, cmd, cmd_argc, cmd_argv, &cmd_result, &cmd_error); if (error) { ovs_fatal(error, "%s: transaction error", target); } if (cmd_error) { jsonrpc_close(client); fputs(cmd_error, stderr); ovs_error(0, "%s: server returned an error", target); exit(2); } else if (cmd_result) { fputs(cmd_result, stdout); } else { OVS_NOT_REACHED(); } jsonrpc_close(client); free(cmd_result); free(cmd_error); return 0; } static void usage(void) { printf("\ %s, for querying and controlling Open vSwitch daemon\n\ usage: %s [TARGET] COMMAND [ARG...]\n\ Targets:\n\ -t, --target=TARGET pidfile or socket to contact\n\ Common commands:\n\ list-commands List commands supported by the target\n\ version Print version of the target\n\ vlog/list List current logging levels\n\ vlog/list-pattern List logging patterns for each destination.\n\ vlog/set [SPEC]\n\ Set log levels as detailed in SPEC, which may include:\n\ A valid module name (all modules, by default)\n\ 'syslog', 'console', 'file' (all destinations, by default))\n\ 'off', 'emer', 'err', 'warn', 'info', or 'dbg' ('dbg', bydefault)\n\ vlog/reopen Make the program reopen its log file\n\ Other options:\n\ --timeout=SECS wait at most SECS seconds for a response\n\ -h, --help Print this helpful information\n\ -V, --version Display ovs-appctl version information\n", program_name, program_name); exit(EXIT_SUCCESS); } static const char * parse_command_line(int argc, char *argv[]) { enum { OPT_START = UCHAR_MAX + 1, VLOG_OPTION_ENUMS }; static const struct option long_options[] = { {"target", required_argument, NULL, 't'}, {"execute", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"option", no_argument, NULL, 'o'}, {"version", no_argument, NULL, 'V'}, {"timeout", required_argument, NULL, 'T'}, VLOG_LONG_OPTIONS, {NULL, 0, NULL, 0}, }; char *short_options_ = ovs_cmdl_long_options_to_short_options(long_options); char *short_options = xasprintf("+%s", short_options_); const char *target; int e_options; target = NULL; e_options = 0; for (;;) { int option; option = getopt_long(argc, argv, short_options, long_options, NULL); if (option == -1) { break; } switch (option) { case 't': if (target) { ovs_fatal(0, "-t or --target may be specified only once"); } target = optarg; break; case 'e': /* We ignore -e for compatibility. Older versions specified the * command as the argument to -e. Since the current version takes * the command as non-option arguments and we say that -e has no * arguments, this just works in the common case. */ if (e_options++) { ovs_fatal(0, "-e or --execute may be speciifed only once"); } break; case 'h': usage(); break; case 'o': ovs_cmdl_print_options(long_options); exit(EXIT_SUCCESS); case 'T': time_alarm(atoi(optarg)); break; case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); VLOG_OPTION_HANDLERS case '?': exit(EXIT_FAILURE); default: OVS_NOT_REACHED(); } } free(short_options_); free(short_options); if (optind >= argc) { ovs_fatal(0, "at least one non-option argument is required " "(use --help for help)"); } return target ? target : "ovs-vswitchd"; } static struct jsonrpc * connect_to_target(const char *target) { struct jsonrpc *client; char *socket_name; int error; #ifndef _WIN32 if (target[0] != '/') { char *pidfile_name; pid_t pid; pidfile_name = xasprintf("%s/%s.pid", ovs_rundir(), target); pid = read_pidfile(pidfile_name); if (pid < 0) { ovs_fatal(-pid, "cannot read pidfile \"%s\"", pidfile_name); } free(pidfile_name); socket_name = xasprintf("%s/%s.%ld.ctl", ovs_rundir(), target, (long int) pid); #else /* On windows, if the 'target' contains ':', we make an assumption that * it is an absolute path. */ if (!strchr(target, ':')) { socket_name = xasprintf("%s/%s.ctl", ovs_rundir(), target); #endif } else { socket_name = xstrdup(target); } error = unixctl_client_create(socket_name, &client); if (error) { ovs_fatal(error, "cannot connect to \"%s\"", socket_name); } free(socket_name); return client; } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-dpctl.8.in0000644000000000000000000000013213534540071020270 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.761845906 openvswitch-2.5.9/utilities/ovs-dpctl.8.in0000644000175000017500000000445013534540071021761 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-dpctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-dpctl . .SH NAME ovs\-dpctl \- administer Open vSwitch datapaths . .SH SYNOPSIS .B ovs\-dpctl [\fIoptions\fR] \fIcommand \fR[\fIswitch\fR] [\fIargs\fR\&...] . .SH DESCRIPTION .PP The \fBovs\-dpctl\fR program can create, modify, and delete Open vSwitch datapaths. A single machine may host any number of datapaths. .PP This program works only with datapaths that are implemented outside of \fBovs\-vswitchd\fR itself, such as the Linux and Windows kernel-based datapaths. To manage datapaths that are integrated into \fBovs\-vswitchd\fR, such as the userspace (\fBnetdev\fR) datapath, use \fBovs\-appctl\fR(8) to invoke the \fBdpctl/*\fR commands, which are documented in \fBovs\-vswitchd\fR(8). .PP A newly created datapath is associated with only one network device, a virtual network device sometimes called the datapath's ``local port''. A newly created datapath is not, however, associated with any of the host's other network devices. To intercept and process traffic on a given network device, use the \fBadd\-if\fR command to explicitly add that network device to the datapath. .PP If \fBovs\-vswitchd\fR(8) is in use, use \fBovs\-vsctl\fR(8) instead of \fBovs\-dpctl\fR. .PP Most \fBovs\-dpctl\fR commands that work with datapaths take an argument that specifies the name of the datapath. Datapath names take the form [\fItype\fB@\fR]\fIname\fR, where \fIname\fR is the network device associated with the datapath's local port. If \fItype\fR is given, it specifies the datapath provider of \fIname\fR, otherwise the default provider \fBsystem\fR is assumed. .PP The following commands manage datapaths. . .ds DX .de DO \\$1 \\$2 \\$3 .. .so lib/dpctl.man . .SH OPTIONS .IP "\fB\-s\fR" .IQ "\fB\-\-statistics\fR" Causes the \fBshow\fR command to print packet and byte counters for each port within the datapaths that it shows. . .IP "\fB\-m\fR" .IQ "\fB\-\-more\fR" Increases the verbosity of \fBdump\-flows\fR output. . .IP "\fB\-t\fR" .IQ "\fB\-\-timeout=\fIsecs\fR" Limits \fBovs\-dpctl\fR runtime to approximately \fIsecs\fR seconds. If the timeout expires, \fBovs\-dpctl\fR will exit with a \fBSIGALRM\fR signal. . .so lib/vlog.man .so lib/common.man . .SH "SEE ALSO" . .BR ovs\-appctl (8), .BR ovs\-vswitchd (8) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-docker0000644000000000000000000000013213534540071017656 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.813846291 openvswitch-2.5.9/utilities/ovs-docker0000755000175000017500000001760013534540071021353 0ustar00jpettitjpettit00000000000000#!/bin/bash # Copyright (C) 2014 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Check for programs we'll need. search_path () { save_IFS=$IFS IFS=: for dir in $PATH; do IFS=$save_IFS if test -x "$dir/$1"; then return 0 fi done IFS=$save_IFS echo >&2 "$0: $1 not found in \$PATH, please install and try again" exit 1 } ovs_vsctl () { ovs-vsctl --timeout=60 "$@" } create_netns_link () { mkdir -p /var/run/netns if [ ! -e /var/run/netns/"$PID" ]; then ln -s /proc/"$PID"/ns/net /var/run/netns/"$PID" trap 'delete_netns_link' 0 for signal in 1 2 3 13 14 15; do trap 'delete_netns_link; trap - $signal; kill -$signal $$' $signal done fi } delete_netns_link () { rm -f /var/run/netns/"$PID" } get_port_for_container_interface () { CONTAINER="$1" INTERFACE="$2" PORT=`ovs_vsctl --data=bare --no-heading --columns=name find interface \ external_ids:container_id="$CONTAINER" \ external_ids:container_iface="$INTERFACE"` if [ -z "$PORT" ]; then echo >&2 "$UTIL: Failed to find any attached port" \ "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE" fi echo "$PORT" } add_port () { BRIDGE="$1" INTERFACE="$2" CONTAINER="$3" if [ -z "$BRIDGE" ] || [ -z "$INTERFACE" ] || [ -z "$CONTAINER" ]; then echo >&2 "$UTIL add-port: not enough arguments (use --help for help)" exit 1 fi shift 3 while [ $# -ne 0 ]; do case $1 in --ipaddress=*) ADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --macaddress=*) MACADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --gateway=*) GATEWAY=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; --mtu=*) MTU=`expr X"$1" : 'X[^=]*=\(.*\)'` shift ;; *) echo >&2 "$UTIL add-port: unknown option \"$1\"" exit 1 ;; esac done # Check if a port is already attached for the given container and interface PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE" \ 2>/dev/null` if [ -n "$PORT" ]; then echo >&2 "$UTIL: Port already attached" \ "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE" exit 1 fi if ovs_vsctl br-exists "$BRIDGE" || \ ovs_vsctl add-br "$BRIDGE"; then :; else echo >&2 "$UTIL: Failed to create bridge $BRIDGE" exit 1 fi if PID=`docker inspect -f '{{.State.Pid}}' "$CONTAINER"`; then :; else echo >&2 "$UTIL: Failed to get the PID of the container" exit 1 fi create_netns_link # Create a veth pair. ID=`uuidgen | sed 's/-//g'` PORTNAME="${ID:0:13}" ip link add "${PORTNAME}_l" type veth peer name "${PORTNAME}_c" # Add one end of veth to OVS bridge. if ovs_vsctl --may-exist add-port "$BRIDGE" "${PORTNAME}_l" \ -- set interface "${PORTNAME}_l" \ external_ids:container_id="$CONTAINER" \ external_ids:container_iface="$INTERFACE"; then :; else echo >&2 "$UTIL: Failed to add "${PORTNAME}_l" port to bridge $BRIDGE" ip link delete "${PORTNAME}_l" exit 1 fi ip link set "${PORTNAME}_l" up # Move "${PORTNAME}_c" inside the container and changes its name. ip link set "${PORTNAME}_c" netns "$PID" ip netns exec "$PID" ip link set dev "${PORTNAME}_c" name "$INTERFACE" ip netns exec "$PID" ip link set "$INTERFACE" up if [ -n "$MTU" ]; then ip netns exec "$PID" ip link set dev "$INTERFACE" mtu "$MTU" fi if [ -n "$ADDRESS" ]; then ip netns exec "$PID" ip addr add "$ADDRESS" dev "$INTERFACE" fi if [ -n "$MACADDRESS" ]; then ip netns exec "$PID" ip link set dev "$INTERFACE" address "$MACADDRESS" fi if [ -n "$GATEWAY" ]; then ip netns exec "$PID" ip route add default via "$GATEWAY" fi } del_port () { BRIDGE="$1" INTERFACE="$2" CONTAINER="$3" if [ "$#" -lt 3 ]; then usage exit 1 fi PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE"` if [ -z "$PORT" ]; then exit 1 fi ovs_vsctl --if-exists del-port "$PORT" ip link delete "$PORT" } del_ports () { BRIDGE="$1" CONTAINER="$2" if [ "$#" -lt 2 ]; then usage exit 1 fi PORTS=`ovs_vsctl --data=bare --no-heading --columns=name find interface \ external_ids:container_id="$CONTAINER"` if [ -z "$PORTS" ]; then exit 0 fi for PORT in $PORTS; do ovs_vsctl --if-exists del-port "$PORT" ip link delete "$PORT" done } set_vlan () { BRIDGE="$1" INTERFACE="$2" CONTAINER_ID="$3" VLAN="$4" if [ "$#" -lt 4 ]; then usage exit 1 fi PORT=`get_port_for_container_interface "$CONTAINER_ID" "$INTERFACE"` if [ -z "$PORT" ]; then exit 1 fi ovs_vsctl set port "$PORT" tag="$VLAN" } usage() { cat << EOF ${UTIL}: Performs integration of Open vSwitch with Docker. usage: ${UTIL} COMMAND Commands: add-port BRIDGE INTERFACE CONTAINER [--ipaddress="ADDRESS"] [--gateway=GATEWAY] [--macaddress="MACADDRESS"] [--mtu=MTU] Adds INTERFACE inside CONTAINER and connects it as a port in Open vSwitch BRIDGE. Optionally, sets ADDRESS on INTERFACE. ADDRESS can include a '/' to represent network prefix length. Optionally, sets a GATEWAY, MACADDRESS and MTU. e.g.: ${UTIL} add-port br-int eth1 c474a0e2830e --ipaddress=192.168.1.2/24 --gateway=192.168.1.1 --macaddress="a2:c3:0d:49:7f:f8" --mtu=1450 del-port BRIDGE INTERFACE CONTAINER Deletes INTERFACE inside CONTAINER and removes its connection to Open vSwitch BRIDGE. e.g.: ${UTIL} del-port br-int eth1 c474a0e2830e del-ports BRIDGE CONTAINER Removes all Open vSwitch interfaces from CONTAINER. e.g.: ${UTIL} del-ports br-int c474a0e2830e set-vlan BRIDGE INTERFACE CONTAINER VLAN Configures the INTERFACE of CONTAINER attached to BRIDGE to become an access port of VLAN. e.g.: ${UTIL} set-vlan br-int eth1 c474a0e2830e 5 Options: -h, --help display this help message. EOF } UTIL=$(basename $0) search_path ovs-vsctl search_path docker search_path uuidgen if (ip netns) > /dev/null 2>&1; then :; else echo >&2 "$UTIL: ip utility not found (or it does not support netns),"\ "cannot proceed" exit 1 fi if [ $# -eq 0 ]; then usage exit 0 fi case $1 in "add-port") shift add_port "$@" exit 0 ;; "del-port") shift del_port "$@" exit 0 ;; "del-ports") shift del_ports "$@" exit 0 ;; "set-vlan") shift set_vlan "$@" exit 0 ;; -h | --help) usage exit 0 ;; *) echo >&2 "$UTIL: unknown command \"$1\" (use --help for help)" exit 1 ;; esac openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-testcontroller.c0000644000000000000000000000013213534540071021713 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801425.193856463 openvswitch-2.5.9/utilities/ovs-testcontroller.c0000644000175000017500000003004213534540071023400 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "fatal-signal.h" #include "learning-switch.h" #include "ofp-parse.h" #include "ofp-version-opt.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "rconn.h" #include "simap.h" #include "stream-ssl.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "socket-util.h" #include "ofp-util.h" VLOG_DEFINE_THIS_MODULE(controller); #define MAX_SWITCHES 16 #define MAX_LISTENERS 16 struct switch_ { struct lswitch *lswitch; }; /* -H, --hub: Learn the ports on which MAC addresses appear? */ static bool learn_macs = true; /* -n, --noflow: Set up flows? (If not, every packet is processed at the * controller.) */ static bool set_up_flows = true; /* -N, --normal: Use "NORMAL" action instead of explicit port? */ static bool action_normal = false; /* -w, --wildcard: 0 to disable wildcard flow entries, an OFPFW10_* bitmask to * enable specific wildcards, or UINT32_MAX to use the default wildcards. */ static uint32_t wildcards = 0; /* --max-idle: Maximum idle time, in seconds, before flows expire. */ static int max_idle = 60; /* --mute: If true, accept connections from switches but do not reply to any * of their messages (for debugging fail-open mode). */ static bool mute = false; /* -q, --queue: default OpenFlow queue, none if UINT32_MAX. */ static uint32_t default_queue = UINT32_MAX; /* -Q, --port-queue: map from port name to port number. */ static struct simap port_queues = SIMAP_INITIALIZER(&port_queues); /* --with-flows: Flows to send to switch. */ static struct ofputil_flow_mod *default_flows; static size_t n_default_flows; static enum ofputil_protocol usable_protocols; /* --unixctl: Name of unixctl socket, or null to use the default. */ static char *unixctl_path = NULL; static void new_switch(struct switch_ *, struct vconn *); static void parse_options(int argc, char *argv[]); OVS_NO_RETURN static void usage(void); int main(int argc, char *argv[]) { struct unixctl_server *unixctl; struct switch_ switches[MAX_SWITCHES]; struct pvconn *listeners[MAX_LISTENERS]; int n_switches, n_listeners; int retval; int i; ovs_cmdl_proctitle_init(argc, argv); set_program_name(argv[0]); parse_options(argc, argv); fatal_ignore_sigpipe(); daemon_become_new_user(false); if (argc - optind < 1) { ovs_fatal(0, "at least one vconn argument required; " "use --help for usage"); } n_switches = n_listeners = 0; for (i = optind; i < argc; i++) { const char *name = argv[i]; struct vconn *vconn; retval = vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, &vconn); if (!retval) { if (n_switches >= MAX_SWITCHES) { ovs_fatal(0, "max %d switch connections", n_switches); } new_switch(&switches[n_switches++], vconn); continue; } else if (retval == EAFNOSUPPORT) { struct pvconn *pvconn; retval = pvconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, &pvconn); if (!retval) { if (n_listeners >= MAX_LISTENERS) { ovs_fatal(0, "max %d passive connections", n_listeners); } listeners[n_listeners++] = pvconn; } } if (retval) { VLOG_ERR("%s: connect: %s", name, ovs_strerror(retval)); } } if (n_switches == 0 && n_listeners == 0) { ovs_fatal(0, "no active or passive switch connections"); } daemonize_start(false); retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } daemonize_complete(); while (n_switches > 0 || n_listeners > 0) { /* Accept connections on listening vconns. */ for (i = 0; i < n_listeners && n_switches < MAX_SWITCHES; ) { struct vconn *new_vconn; retval = pvconn_accept(listeners[i], &new_vconn); if (!retval || retval == EAGAIN) { if (!retval) { new_switch(&switches[n_switches++], new_vconn); } i++; } else { pvconn_close(listeners[i]); listeners[i] = listeners[--n_listeners]; } } /* Do some switching work. . */ for (i = 0; i < n_switches; ) { struct switch_ *this = &switches[i]; lswitch_run(this->lswitch); if (lswitch_is_alive(this->lswitch)) { i++; } else { lswitch_destroy(this->lswitch); switches[i] = switches[--n_switches]; } } unixctl_server_run(unixctl); /* Wait for something to happen. */ if (n_switches < MAX_SWITCHES) { for (i = 0; i < n_listeners; i++) { pvconn_wait(listeners[i]); } } for (i = 0; i < n_switches; i++) { struct switch_ *sw = &switches[i]; lswitch_wait(sw->lswitch); } unixctl_server_wait(unixctl); poll_block(); } return 0; } static void new_switch(struct switch_ *sw, struct vconn *vconn) { struct lswitch_config cfg; struct rconn *rconn; rconn = rconn_create(60, 0, DSCP_DEFAULT, get_allowed_ofp_versions()); rconn_connect_unreliably(rconn, vconn, NULL); cfg.mode = (action_normal ? LSW_NORMAL : learn_macs ? LSW_LEARN : LSW_FLOOD); cfg.wildcards = wildcards; cfg.max_idle = set_up_flows ? max_idle : -1; cfg.default_flows = default_flows; cfg.n_default_flows = n_default_flows; cfg.usable_protocols = usable_protocols; cfg.default_queue = default_queue; cfg.port_queues = &port_queues; cfg.mute = mute; sw->lswitch = lswitch_create(rconn, &cfg); } static void add_port_queue(char *s) { char *save_ptr = NULL; char *port_name; char *queue_id; port_name = strtok_r(s, ":", &save_ptr); queue_id = strtok_r(NULL, "", &save_ptr); if (!queue_id) { ovs_fatal(0, "argument to -Q or --port-queue should take the form " "\":\""); } if (!simap_put(&port_queues, port_name, atoi(queue_id))) { ovs_fatal(0, " arguments for -Q or --port-queue must " "be unique"); } } static void parse_options(int argc, char *argv[]) { enum { OPT_MAX_IDLE = UCHAR_MAX + 1, OPT_PEER_CA_CERT, OPT_MUTE, OPT_WITH_FLOWS, OPT_UNIXCTL, VLOG_OPTION_ENUMS, DAEMON_OPTION_ENUMS, OFP_VERSION_OPTION_ENUMS }; static const struct option long_options[] = { {"hub", no_argument, NULL, 'H'}, {"noflow", no_argument, NULL, 'n'}, {"normal", no_argument, NULL, 'N'}, {"wildcards", optional_argument, NULL, 'w'}, {"max-idle", required_argument, NULL, OPT_MAX_IDLE}, {"mute", no_argument, NULL, OPT_MUTE}, {"queue", required_argument, NULL, 'q'}, {"port-queue", required_argument, NULL, 'Q'}, {"with-flows", required_argument, NULL, OPT_WITH_FLOWS}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"help", no_argument, NULL, 'h'}, DAEMON_LONG_OPTIONS, OFP_VERSION_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int indexptr; char *error; int c; c = getopt_long(argc, argv, short_options, long_options, &indexptr); if (c == -1) { break; } switch (c) { case 'H': learn_macs = false; break; case 'n': set_up_flows = false; break; case OPT_MUTE: mute = true; break; case 'N': action_normal = true; break; case 'w': wildcards = optarg ? strtol(optarg, NULL, 16) : UINT32_MAX; break; case OPT_MAX_IDLE: if (!strcmp(optarg, "permanent")) { max_idle = OFP_FLOW_PERMANENT; } else { max_idle = atoi(optarg); if (max_idle < 1 || max_idle > 65535) { ovs_fatal(0, "--max-idle argument must be between 1 and " "65535 or the word 'permanent'"); } } break; case 'q': default_queue = atoi(optarg); break; case 'Q': add_port_queue(optarg); break; case OPT_WITH_FLOWS: error = parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows, &n_default_flows, &usable_protocols); if (error) { ovs_fatal(0, "%s", error); } break; case OPT_UNIXCTL: unixctl_path = optarg; break; case 'h': usage(); VLOG_OPTION_HANDLERS OFP_VERSION_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); if (!simap_is_empty(&port_queues) || default_queue != UINT32_MAX) { if (action_normal) { ovs_error(0, "queue IDs are incompatible with -N or --normal; " "not using OFPP_NORMAL"); action_normal = false; } if (!learn_macs) { ovs_error(0, "queue IDs are incompatible with -H or --hub; " "not acting as hub"); learn_macs = true; } } } static void usage(void) { printf("%s: OpenFlow controller\n" "usage: %s [OPTIONS] METHOD\n" "where METHOD is any OpenFlow connection method.\n", program_name, program_name); vconn_usage(true, true, false); daemon_usage(); ofp_version_usage(); vlog_usage(); printf("\nOther options:\n" " -H, --hub act as hub instead of learning switch\n" " -n, --noflow pass traffic, but don't add flows\n" " --max-idle=SECS max idle time for new flows\n" " -N, --normal use OFPP_NORMAL action\n" " -w, --wildcards[=MASK] wildcard (specified) bits in flows\n" " -q, --queue=QUEUE-ID OpenFlow queue ID to use for output\n" " -Q PORT-NAME:QUEUE-ID use QUEUE-ID for frames from PORT-NAME\n" " --with-flows FILE use the flows from FILE\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-test.8.in0000644000000000000000000000013213534540071020141 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.773845995 openvswitch-2.5.9/utilities/ovs-test.8.in0000644000175000017500000001337313534540071021636 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-test 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME \fBovs\-test\fR \- check Linux drivers for performance, vlan and L3 tunneling problems . .SH SYNOPSIS \fBovs\-test\fR \fB\-s\fR \fIport\fR .PP \fBovs\-test\fR \fB\-c\fR \fIserver1\fR \fIserver2\fR [\fB\-b\fR \fItargetbandwidth\fR] [\fB\-i\fR \fItestinterval\fR] [\fB\-d\fR] [\fB\-l\fR \fIvlantag\fR] [\fB\-t\fR \fItunnelmodes\fR] .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-test\fR program may be used to check for problems sending 802.1Q or GRE traffic that Open vSwitch may uncover. These problems, for example, can occur when Open vSwitch is used to send 802.1Q traffic through physical interfaces running certain drivers of certain Linux kernel versions. To run a test, configure IP addresses on \fIserver1\fR and \fIserver2\fR for interfaces you intended to test. These interfaces could also be already configured OVS bridges that have a physical interface attached to them. Then, on one of the nodes, run \fBovs\-test\fR in server mode and on the other node run it in client mode. The client will connect to \fBovs\-test\fR server and schedule tests between both of them. The \fBovs\-test\fR client will perform UDP and TCP tests. .PP UDP tests can report packet loss and achieved bandwidth for various datagram sizes. By default target bandwidth for UDP tests is 1Mbit/s. .PP TCP tests report only achieved bandwidth, because kernel TCP stack takes care of flow control and packet loss. TCP tests are essential to detect potential TSO related issues. .PP To determine whether Open vSwitch is encountering any problems, the user must compare packet loss and achieved bandwidth in a setup where traffic is being directly sent and in one where it is not. If in the 802.1Q or L3 tunneled tests both \fBovs\-test\fR processes are unable to communicate or the achieved bandwidth is much lower compared to direct setup, then, most likely, Open vSwitch has encountered a pre-existing kernel or driver bug. .PP Some examples of the types of problems that may be encountered are: .so utilities/ovs-vlan-bugs.man . .SS "Client Mode" An \fBovs\-test\fR client will connect to two \fBovs\-test\fR servers and will ask them to exchange test traffic. It is also possible to spawn an \fBovs\-test\fR server automatically from the client. . .SS "Server Mode" To conduct tests, two \fBovs\-test\fR servers must be running on two different hosts where the client can connect. The actual test traffic is exchanged only between both \fBovs\-test\fR servers. It is recommended that both servers have their IP addresses in the same subnet, otherwise one would have to make sure that routing is set up correctly. . .SH OPTIONS . .IP "\fB\-s \fIport\fR" .IQ "\fB\-\-server\fR \fIport\fR" Run in server mode and wait for the client to establish XML RPC Control Connection on this TCP \fIport\fR. It is recommended to have \fBethtool\fR(8) installed on the server so that it could retrieve information about the NIC driver. . .IP "\fB\-c \fIserver1\fR \fIserver2\fR" .IQ "\fB\-\-client \fIserver1\fR \fIserver2\fR" Run in client mode and schedule tests between \fIserver1\fR and \fIserver2\fR, where each \fIserver\fR must be given in the following format - \fIOuterIP[:OuterPort],InnerIP[/Mask][:InnerPort]\fR. The \fIOuterIP\fR must be already assigned to the physical interface which is going to be tested. This is the IP address where client will try to establish XML RPC connection. If \fIOuterIP\fR is 127.0.0.1 then client will automatically spawn a local instance of \fBovs\-test\fR server. \fIOuterPort\fR is TCP port where server is listening for incoming XML/RPC control connections to schedule tests (by default it is 15531). The \fBovs\-test\fR will automatically assign \fIInnerIP[/Mask]\fR to the interfaces that will be created on the fly for testing purposes. It is important that \fIInnerIP[/Mask]\fR does not interfere with already existing IP addresses on both \fBovs\-test\fR servers and client. \fIInnerPort\fR is port which will be used by server to listen for test traffic that will be encapsulated (by default it is 15532). . .IP "\fB\-b \fItargetbandwidth\fR" .IQ "\fB\-\-bandwidth\fR \fItargetbandwidth\fR" Target bandwidth for UDP tests. The \fItargetbandwidth\fR must be given in bits per second. It is possible to use postfix M or K to alter the target bandwidth magnitude. . .IP "\fB\-i \fItestinterval\fR" .IQ "\fB\-\-interval\fR \fItestinterval\fR" How long each test should run. By default 5 seconds. . .so lib/common.man . .SH "Test Modes" The following test modes are supported by \fBovs\-test\fR. It is possible to combine multiple of them in a single \fBovs\-test\fR invocation. . .IP "\fB\-d \fR" .IQ "\fB\-\-direct\fR" Perform direct tests between both \fIOuterIP\fR addresses. These tests could be used as a reference to compare 802.1Q or L3 tunneling test results. . .IP "\fB\-l \fIvlantag\fR" .IQ "\fB\-\-vlan\-tag\fR \fIvlantag\fR" Perform 802.1Q tests between both servers. These tests will create a temporary OVS bridge, if necessary, and attach a VLAN tagged port to it for testing purposes. . .IP "\fB\-t \fItunnelmodes\fR" .IQ "\fB\-\-tunnel\-modes\fR \fItunnelmodes\fR" Perform L3 tunneling tests. The given argument is a comma separated string that specifies all the L3 tunnel modes that should be tested (e.g. gre). The L3 tunnels are terminated on interface that has the \fIOuterIP\fR address assigned. . .SH EXAMPLES .PP On host 1.2.3.4 start \fBovs\-test\fR in server mode: .IP .B ovs\-test \-s 15531 . .PP On host 1.2.3.5 start \fBovs\-test\fR in client mode and do direct, VLAN and GRE tests between both nodes: .IP .B ovs\-test \-c 127.0.0.1,1.1.1.1/30 1.2.3.4,1.1.1.2/30 -d -l 123 -t gre . .SH SEE ALSO . .BR ovs\-vswitchd (8), .BR ovs\-ofctl (8), .BR ovs\-vsctl (8), .BR ovs\-vlan\-test (8), .BR ethtool (8), .BR uname (1) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vlan-test.in0000644000000000000000000000013213534540071020731 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.825846378 openvswitch-2.5.9/utilities/ovs-vlan-test.in0000755000175000017500000002744213534540071022433 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Copyright (c) 2010 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import BaseHTTPServer import getopt import httplib import os import threading import time import signal #Causes keyboard interrupts to go to the main thread. import socket import sys print_safe_lock = threading.Lock() def print_safe(s): print_safe_lock.acquire() print(s) print_safe_lock.release() def start_thread(target, args): t = threading.Thread(target=target, args=args) t.setDaemon(True) t.start() return t #Caller is responsible for catching socket.error exceptions. def send_packet(key, length, dest_ip, dest_port): length -= 20 + 8 #IP and UDP headers. packet = str(key) packet += chr(0) * (length - len(packet)) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(packet, (dest_ip, dest_port)) sock.close() #UDP Receiver class UDPReceiver: def __init__(self, vlan_ip, vlan_port): self.vlan_ip = vlan_ip self.vlan_port = vlan_port self.recv_callbacks = {} self.udp_run = False def recv_packet(self, key, success_callback, timeout_callback): event = threading.Event() def timeout_cb(): timeout_callback() event.set() timer = threading.Timer(30, timeout_cb) timer.daemon = True def success_cb(): timer.cancel() success_callback() event.set() # Start the timer first to avoid a timer.cancel() race condition. timer.start() self.recv_callbacks[key] = success_cb return event def udp_receiver(self): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(1) try: sock.bind((self.vlan_ip, self.vlan_port)) except socket.error, e: print_safe('Failed to bind to %s:%d with error: %s' % (self.vlan_ip, self.vlan_port, e)) os._exit(1) #sys.exit only exits the current thread. while self.udp_run: try: data, _ = sock.recvfrom(4096) except socket.timeout: continue except socket.error, e: print_safe('Failed to receive from %s:%d with error: %s' % (self.vlan_ip, self.vlan_port, e)) os._exit(1) data_str = data.split(chr(0))[0] if not data_str.isdigit(): continue key = int(data_str) if key in self.recv_callbacks: self.recv_callbacks[key]() del self.recv_callbacks[key] def start(self): self.udp_run = True start_thread(self.udp_receiver, ()) def stop(self): self.udp_run = False #Server vlan_server = None class VlanServer: def __init__(self, server_ip, server_port, vlan_ip, vlan_port): global vlan_server vlan_server = self self.server_ip = server_ip self.server_port = server_port self.recv_response = '%s:%d:' % (vlan_ip, vlan_port) self.result = {} self.result_lock = threading.Lock() self._test_id = 0 self._test_id_lock = threading.Lock() self.udp_recv = UDPReceiver(vlan_ip, vlan_port) def get_test_id(self): self._test_id_lock.acquire() self._test_id += 1 ret = self._test_id self._test_id_lock.release() return ret def set_result(self, key, value): self.result_lock.acquire() if key not in self.result: self.result[key] = value self.result_lock.release() def recv(self, test_id): self.udp_recv.recv_packet(test_id, lambda : self.set_result(test_id, 'Success'), lambda : self.set_result(test_id, 'Timeout')) return self.recv_response + str(test_id) def send(self, test_id, data): try: ip, port, size = data.split(':') port = int(port) size = int(size) except ValueError: self.set_result(test_id, 'Server failed to parse send request: %s' % data) return def send_thread(): send_time = 10 for _ in range(send_time * 2): try: send_packet(test_id, size, ip, port) except socket.error, e: self.set_result(test_id, 'Failure: ' + str(e)) return time.sleep(.5) self.set_result(test_id, 'Success') start_thread(send_thread, ()) return str(test_id) def run(self): self.udp_recv.start() try: BaseHTTPServer.HTTPServer((self.server_ip, self.server_port), VlanServerHandler).serve_forever() except socket.error, e: print_safe('Failed to start control server: %s' % e) self.udp_recv.stop() return 1 class VlanServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): #Guarantee three arguments. path = (self.path.lower().lstrip('/') + '//').split('/') resp = 404 body = None if path[0] == 'start': test_id = vlan_server.get_test_id() if path[1] == 'recv': resp = 200 body = vlan_server.recv(test_id) elif path[1] == 'send': resp = 200 body = vlan_server.send(test_id, path[2]) elif (path[0] == 'result' and path[1].isdigit() and int(path[1]) in vlan_server.result): resp = 200 body = vlan_server.result[int(path[1])] elif path[0] == 'ping': resp = 200 body = 'pong' self.send_response(resp) self.end_headers() if body: self.wfile.write(body) #Client class VlanClient: def __init__(self, server_ip, server_port, vlan_ip, vlan_port): self.server_ip_port = '%s:%d' % (server_ip, server_port) self.vlan_ip_port = "%s:%d" % (vlan_ip, vlan_port) self.udp_recv = UDPReceiver(vlan_ip, vlan_port) def request(self, resource): conn = httplib.HTTPConnection(self.server_ip_port) conn.request('GET', resource) return conn def send(self, size): def error_msg(e): print_safe('Send size %d unsuccessful: %s' % (size, e)) try: conn = self.request('/start/recv') data = conn.getresponse().read() except (socket.error, httplib.HTTPException), e: error_msg(e) return False try: ip, port, test_id = data.split(':') port = int(port) test_id = int(test_id) except ValueError: error_msg("Received invalid response from control server (%s)" % data) return False send_time = 5 for _ in range(send_time * 4): try: send_packet(test_id, size, ip, port) resp = self.request('/result/%d' % test_id).getresponse() data = resp.read() except (socket.error, httplib.HTTPException), e: error_msg(e) return False if resp.status == 200 and data == 'Success': print_safe('Send size %d successful' % size) return True elif resp.status == 200: error_msg(data) return False time.sleep(.25) error_msg('Timeout') return False def recv(self, size): def error_msg(e): print_safe('Receive size %d unsuccessful: %s' % (size, e)) resource = '/start/send/%s:%d' % (self.vlan_ip_port, size) try: conn = self.request(resource) test_id = conn.getresponse().read() except (socket.error, httplib.HTTPException), e: error_msg(e) return False if not test_id.isdigit(): error_msg('Invalid response %s' % test_id) return False success = [False] #Primitive datatypes can't be set from closures. def success_cb(): success[0] = True def failure_cb(): success[0] = False self.udp_recv.recv_packet(int(test_id), success_cb, failure_cb).wait() if success[0]: print_safe('Receive size %d successful' % size) else: error_msg('Timeout') return success[0] def server_up(self): def error_msg(e): print_safe('Failed control server connectivity test: %s' % e) try: resp = self.request('/ping').getresponse() data = resp.read() except (socket.error, httplib.HTTPException), e: error_msg(e) return False if resp.status != 200: error_msg('Invalid status %d' % resp.status) elif data != 'pong': error_msg('Invalid response %s' % data) return True def run(self): if not self.server_up(): return 1 self.udp_recv.start() success = True for size in [50, 500, 1000, 1500]: success = self.send(size) and success success = self.recv(size) and success self.udp_recv.stop() if success: print_safe('OK') return 0 else: print_safe('FAILED') return 1 def usage(): print_safe("""\ %(argv0)s: Test vlan connectivity usage: %(argv0)s server vlan The following options are also available: -s, --server run in server mode -h, --help display this help message -V, --version display version information\ """ % {'argv0': sys.argv[0]}) def main(): try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hVs', ['help', 'version', 'server']) except getopt.GetoptError, geo: print_safe('%s: %s\n' % (sys.argv[0], geo.msg)) return 1 server = False for key, _ in options: if key in ['-h', '--help']: usage() return 0 elif key in ['-V', '--version']: print_safe('ovs-vlan-test (Open vSwitch) @VERSION@') return 0 elif key in ['-s', '--server']: server = True else: print_safe('Unexpected option %s. (use --help for help)' % key) return 1 if len(args) != 2: print_safe('Expecting two arguments. (use --help for help)') return 1 try: server_ip, server_port = args[0].split(':') server_port = int(server_port) except ValueError: server_ip = args[0] server_port = 80 try: vlan_ip, vlan_port = args[1].split(':') vlan_port = int(vlan_port) except ValueError: vlan_ip = args[1] vlan_port = 15213 if server: return VlanServer(server_ip, server_port, vlan_ip, vlan_port).run() else: return VlanClient(server_ip, server_port, vlan_ip, vlan_port).run() if __name__ == '__main__': main_ret = main() # Python can throw exceptions if threads are running at exit. for th in threading.enumerate(): if th != threading.currentThread(): th.join() sys.exit(main_ret) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-pcap.in0000644000000000000000000000013213534540071017737 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.817846319 openvswitch-2.5.9/utilities/ovs-pcap.in0000755000175000017500000000633213534540071021434 0ustar00jpettitjpettit00000000000000#! @PYTHON@ # # Copyright (c) 2010 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import binascii import getopt import struct import sys class PcapException(Exception): pass class PcapReader(object): def __init__(self, file_name): self.file = open(file_name, "rb") header = self.file.read(24) if len(header) != 24: raise PcapException("end of file reading pcap header") magic, version, thiszone, sigfigs, snaplen, network = \ struct.unpack(">6I", header) if magic == 0xa1b2c3d4: self.header_format = ">4I" elif magic == 0xd4c3b2a1: self.header_format = "<4I" else: raise PcapException("bad magic %u reading pcap file " "(expected 0xa1b2c3d4 or 0xd4c3b2a1)" % magic) def read(self): header = self.file.read(16) if len(header) == 0: return None elif len(header) != 16: raise PcapException("end of file within pcap record header") ts_sec, ts_usec, incl_len, orig_len = struct.unpack(self.header_format, header) packet = self.file.read(incl_len) if len(packet) != incl_len: raise PcapException("end of file reading pcap packet data") return packet argv0 = sys.argv[0] def usage(): print """\ %(argv0)s: print pcap file packet data as hex usage: %(argv0)s FILE where FILE is a PCAP file. The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['help', 'version']) except getopt.GetoptException, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print "ovs-pcap (Open vSwitch) @VERSION@" else: sys.exit(0) if len(args) != 1: sys.stderr.write("%s: exactly 1 non-option argument required " "(use --help for help)\n" % argv0) sys.exit(1) reader = PcapReader(args[0]) while True: packet = reader.read() if packet is None: break print binascii.hexlify(packet) except PcapException, e: sys.stderr.write("%s: %s\n" % (argv0, e)) sys.exit(1) # Local variables: # mode: python # End: openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-benchmark.c0000644000000000000000000000013213534540071020562 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.137686576 30 ctime=1567801425.189856434 openvswitch-2.5.9/utilities/ovs-benchmark.c0000644000175000017500000004170113534540071022253 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "command-line.h" #include "poll-loop.h" #include "socket-util.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" #define DEFAULT_PORT 6630 #define MAX_SOCKETS 65535 static int n_batches = 1; static int n_sockets = 100; static struct in_addr local_addr; static unsigned short int local_min_port, local_max_port; static struct in_addr remote_addr; static unsigned short int remote_min_port, remote_max_port; static double max_rate; static double timeout; static const struct ovs_cmdl_command *get_all_commands(void); static void parse_options(int argc, char *argv[]); static void usage(void); static int do_poll(struct pollfd *fds, int nfds, int timeout) { int retval; #ifndef _WIN32 do { retval = poll(fds, nfds, timeout); } while (retval < 0 && errno == EINTR); #else retval = WSAPoll(fds, nfds, timeout); #endif return retval; } static long long int time_in_msec(void) { struct timeval tv; xgettimeofday(&tv); return tv.tv_sec * 1000LL + tv.tv_usec / 1000; } int main(int argc, char *argv[]) { struct ovs_cmdl_context ctx = { .argc = 0, }; set_program_name(argv[0]); vlog_set_levels(NULL, VLF_ANY_DESTINATION, VLL_EMER); parse_options(argc, argv); ctx.argc = argc - optind; ctx.argv = argv + optind; ovs_cmdl_run_command(&ctx, get_all_commands()); return 0; } static void parse_target(const char *s_, struct in_addr *addr, unsigned short int *min, unsigned short int *max) { char *s = xstrdup(s_); char *colon; int error; colon = strchr(s, ':'); if (colon) { *colon = '\0'; } if (*s != '\0') { error = lookup_hostname(s, addr); if (error) { ovs_fatal(error, "failed to look up IP address for \"%s\"", s_); } } else { addr->s_addr = htonl(INADDR_ANY); } *min = *max = 0; if (colon && colon[1] != '\0') { const char *ports = colon + 1; if (ovs_scan(ports, "%hu-%hu", min, max)) { if (*min > *max) { ovs_fatal(0, "%s: minimum is greater than maximum", s_); } } else if (ovs_scan(ports, "%hu", min)) { *max = *min; } else { ovs_fatal(0, "%s: number or range expected", s_); } } free(s); } static void parse_options(int argc, char *argv[]) { static const struct option long_options[] = { {"local", required_argument, NULL, 'l'}, {"remote", required_argument, NULL, 'r'}, {"batches", required_argument, NULL, 'b'}, {"sockets", required_argument, NULL, 's'}, {"max-rate", required_argument, NULL, 'c'}, {"timeout", required_argument, NULL, 'T'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); local_addr.s_addr = htonl(INADDR_ANY); local_min_port = local_max_port = 0; remote_addr.s_addr = htonl(0); remote_min_port = remote_max_port = 0; for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'l': parse_target(optarg, &local_addr, &local_min_port, &local_max_port); break; case 'r': parse_target(optarg, &remote_addr, &remote_min_port, &remote_max_port); if (remote_addr.s_addr == htonl(INADDR_ANY)) { ovs_fatal(0, "remote IP address is required"); } break; case 'b': n_batches = atoi(optarg); if (n_batches < 0) { ovs_fatal(0, "--batches or -b argument must be at least 1"); } break; case 's': n_sockets = atoi(optarg); if (n_sockets < 1 || n_sockets > MAX_SOCKETS) { ovs_fatal(0, "--sockets or -s argument must be between 1 " "and %d (inclusive)", MAX_SOCKETS); } break; case 'c': max_rate = atof(optarg); if (max_rate <= 0.0) { ovs_fatal(0, "--max-rate or -c argument must be positive"); } break; case 'T': timeout = atoi(optarg); if (!timeout) { ovs_fatal(0, "-T or --timeout argument must be positive"); } break; case 'h': usage(); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); case '?': exit(EXIT_FAILURE); default: abort(); } } free(short_options); } static void usage(void) { printf("\ %s: Open vSwitch flow setup benchmark utility\n\ usage: %s [OPTIONS] COMMAND [ARG...]\n\ latency connect many times all at once\n\ rate measure sustained flow setup rate\n\ listen accept TCP connections\n\ help display this help message\n\ \n\ Command options:\n\ -l, --local [IP][:PORTS] use local IP and range of PORTS\n\ -r, --remote IP[:PORTS] connect to remote IP and PORTS\n\ -s, --sockets N number of sockets for \"rate\" or \"latency\"\n\ -b, --batches N number of connection batches for \"latency\"\n\ -c, --max-rate NPERSEC connection rate limit for \"rate\"\n\ -T, --timeout MAXSECS max number of seconds to run for \"rate\"\n\ \n\ Other options:\n\ -h, --help display this help message\n\ -V, --version display version information\n", program_name, program_name); exit(EXIT_SUCCESS); } static void cmd_listen(struct ovs_cmdl_context *ctx OVS_UNUSED) { struct pollfd *fds; int n_fds; int port; int i; if (!local_min_port && !local_max_port) { local_min_port = local_max_port = DEFAULT_PORT; } fds = xmalloc((1 + local_max_port - local_min_port) * sizeof *fds); n_fds = 0; for (port = local_min_port; port <= local_max_port; port++) { struct sockaddr_in sin; unsigned int yes = 1; int error; int fd; /* Create socket, set SO_REUSEADDR. */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { ovs_fatal(errno, "failed to create socket"); } error = set_nonblocking(fd); if (error) { ovs_fatal(error, "failed to set non-blocking mode"); } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) { ovs_fatal(errno, "setsockopt(SO_REUSEADDR) failed"); } /* Bind. */ sin.sin_family = AF_INET; sin.sin_addr = remote_addr; sin.sin_port = htons(port); if (bind(fd, (struct sockaddr *) &sin, sizeof sin) < 0) { ovs_fatal(errno, "bind failed"); } /* Listen. */ if (listen(fd, 10000) < 0) { ovs_fatal(errno, "listen failed"); } fds[n_fds].fd = fd; fds[n_fds].events = POLLIN; n_fds++; } for (;;) { int retval; retval = do_poll(fds, n_fds, -1); if (retval < 0) { ovs_fatal(errno, "poll failed"); } for (i = 0; i < n_fds; i++) { if (fds[i].revents & POLLIN) { int newfd; do { newfd = accept(fds[i].fd, NULL, NULL); } while (newfd < 0 && errno == EINTR); if (newfd >= 0) { close(newfd); } else if (errno != EAGAIN) { ovs_fatal(errno, "accept failed"); } } } } } /* Increments '*value' within the range 'min...max' inclusive. Returns true * if '*value' wraps around to 'min', otherwise false. */ static bool increment(unsigned short int *value, unsigned short int min, unsigned short int max) { if (*value < max) { ++*value; return false; } else { *value = min; return true; } } static void next_ports(unsigned short int *local_port, unsigned short int *remote_port) { if (increment(local_port, local_min_port, local_max_port)) { increment(remote_port, remote_min_port, remote_max_port); } } static void bind_local_port(int fd, unsigned short int *local_port, unsigned short int *remote_port) { int error; if (!local_min_port && !local_max_port) { next_ports(local_port, remote_port); return; } do { struct sockaddr_in local; memset(&local, 0, sizeof local); local.sin_family = AF_INET; local.sin_addr = local_addr; local.sin_port = htons(*local_port); error = (bind(fd, (struct sockaddr *) &local, sizeof local) < 0 ? errno : 0); next_ports(local_port, remote_port); } while (error == EADDRINUSE || error == EINTR); if (error) { ovs_fatal(error, "bind failed"); } } static void cmd_rate(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned short int local_port; unsigned short int remote_port; unsigned int completed = 0; unsigned int failures = 0; long long int start, prev; struct pollfd *fds; int n_fds; if (!remote_addr.s_addr) { ovs_fatal(0, "remote address must be specified with -r or --remote"); } if (!remote_min_port && !remote_max_port) { remote_min_port = remote_max_port = DEFAULT_PORT; } local_port = local_min_port; remote_port = remote_min_port; fds = xmalloc(n_sockets * sizeof *fds); n_fds = 0; start = prev = time_in_msec(); for (;;) { long long int now; long long int may_open; int delay; int retval; int j; if (max_rate > 0) { long long int cur_total = completed + n_fds; long long int max_total = (time_in_msec() - start) * (max_rate / 1000.0); if (max_total > cur_total) { may_open = MIN(n_sockets, max_total - cur_total); } else { may_open = 0; } delay = 1000.0 / max_rate; } else { may_open = n_sockets; delay = 1000; } while (may_open-- > 0 && n_fds < n_sockets) { struct sockaddr_in remote; int error; int fd; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { ovs_fatal(errno, "socket failed"); } error = set_nonblocking(fd); if (error) { ovs_fatal(error, "set_nonblocking failed"); } bind_local_port(fd, &local_port, &remote_port); memset(&remote, 0, sizeof remote); remote.sin_family = AF_INET; remote.sin_addr = remote_addr; remote.sin_port = htons(remote_port); if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) { if (errno == EINPROGRESS) { fds[n_fds].fd = fd; fds[n_fds].events = POLLOUT; fds[n_fds].revents = 0; n_fds++; } else if (errno != ECONNREFUSED) { ovs_fatal(errno, "connect"); } } else { /* Success, I guess. */ shutdown(fd, 2); close(fd); completed++; } } if (n_fds == n_sockets) { delay = 1000; } retval = do_poll(fds, n_fds, delay); if (retval < 0) { ovs_fatal(errno, "poll"); } for (j = 0; j < n_fds; ) { if (fds[j].revents) { if (fds[j].revents & POLLERR) { failures++; } shutdown(fds[j].fd, 2); close(fds[j].fd); fds[j] = fds[--n_fds]; completed++; } else { j++; } } now = time_in_msec(); if (now >= prev + 1000) { long long int elapsed = now - start; printf("%.3f s elapsed, %u OK, %u failed, avg %.1f/s\n", elapsed / 1000.0, completed - failures, failures, completed / (elapsed / 1000.0)); fflush(stdout); prev = now; if (timeout && elapsed > timeout * 1000LL) { break; } } } } static void timer_end(long long int start, bool error, int *min, int *max, unsigned long long int *total) { int elapsed = time_in_msec() - start; static int last_elapsed = INT_MIN; char c = error ? '!' : '.'; if (last_elapsed != elapsed) { if (last_elapsed != INT_MIN) { putchar('\n'); } printf("%5d %c", elapsed, c); fflush(stdout); last_elapsed = elapsed; } else { putchar(c); fflush(stdout); } if (elapsed < *min) { *min = elapsed; } if (elapsed > *max) { *max = elapsed; } *total += elapsed; } static void cmd_latency(struct ovs_cmdl_context *ctx OVS_UNUSED) { unsigned short int local_port; unsigned short int remote_port; int min = INT_MAX; int max = 0; unsigned long long int total = 0; int i; if (!remote_addr.s_addr) { ovs_fatal(0, "remote address must be specified with -r or --rate"); } if (!remote_min_port && !remote_max_port) { remote_min_port = remote_max_port = DEFAULT_PORT; } local_port = local_min_port; remote_port = remote_min_port; for (i = 0; i < n_batches; i++) { struct pollfd fds[MAX_SOCKETS]; long long int start; int n_fds; int j; start = time_in_msec(); n_fds = 0; for (j = 0; j < n_sockets; j++) { struct sockaddr_in remote; int error; int fd; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { ovs_fatal(errno, "socket failed"); } error = set_nonblocking(fd); if (error) { ovs_fatal(error, "set_nonblocking failed"); } bind_local_port(fd, &local_port, &remote_port); memset(&remote, 0, sizeof remote); remote.sin_family = AF_INET; remote.sin_addr = remote_addr; remote.sin_port = htons(remote_port); if (connect(fd, (struct sockaddr *) &remote, sizeof remote) < 0) { if (errno == EINPROGRESS) { fds[n_fds].fd = fd; fds[n_fds].events = POLLOUT; fds[n_fds].revents = 0; n_fds++; } else if (errno != ECONNREFUSED) { ovs_fatal(errno, "connect"); } } else { /* Success, I guess. */ close(fd); timer_end(start, 0, &min, &max, &total); } } while (n_fds > 0) { int retval; retval = do_poll(fds, n_fds, -1); if (retval < 0) { ovs_fatal(errno, "poll"); } for (j = 0; j < n_fds; ) { if (fds[j].revents) { timer_end(start, fds[j].revents & (POLLERR|POLLHUP) ? 1 : 0, &min, &max, &total); close(fds[j].fd); fds[j] = fds[--n_fds]; } else { j++; } } } putchar('\n'); } printf("min %d ms, max %d ms, avg %llu ms\n", min, max, total / (1ULL * n_sockets * n_batches)); } static void cmd_help(struct ovs_cmdl_context *ctx OVS_UNUSED) { usage(); } static const struct ovs_cmdl_command all_commands[] = { { "listen", NULL, 0, 0, cmd_listen }, { "rate", NULL, 0, 0, cmd_rate }, { "latency", NULL, 0, 0, cmd_latency }, { "help", NULL, 0, 0, cmd_help }, { NULL, NULL, 0, 0, NULL }, }; static const struct ovs_cmdl_command *get_all_commands(void) { return all_commands; } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-appctl.8.in0000644000000000000000000000013213534540071020445 xustar0030 mtime=1567801401.941685135 30 atime=1567801402.133686546 30 ctime=1567801423.757845878 openvswitch-2.5.9/utilities/ovs-appctl.8.in0000644000175000017500000002333613534540071022142 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-appctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-appctl . .SH NAME ovs\-appctl \- utility for configuring running Open vSwitch daemons . .SH SYNOPSIS \fBovs\-appctl\fR [\fB\-\-target=\fItarget\fR | \fB\-t\fR \fItarget\fR] \fIcommand \fR[\fIarg\fR...] .br \fBovs\-appctl\fR \-\-help .br \fBovs\-appctl\fR \-\-version .SH DESCRIPTION Open vSwitch daemons accept certain commands at runtime to control their behavior and query their settings. Every daemon accepts a common set of commands documented under \fBCOMMON COMMANDS\fR below. Some daemons support additional commands documented in their own manpages. \fBovs\-vswitchd\fR in particular accepts a number of additional commands documented in \fBovs\-vswitchd\fR(8). .PP The \fBovs\-appctl\fR program provides a simple way to invoke these commands. The command to be sent is specified on \fBovs\-appctl\fR's command line as non-option arguments. \fBovs\-appctl\fR sends the command and prints the daemon's response on standard output. .PP In normal use only a single option is accepted: .IP "\fB\-t \fItarget\fR" .IQ "\fB\-\-target=\fItarget\fR" Tells \fBovs\-appctl\fR which daemon to contact. .IP If \fItarget\fR begins with \fB/\fR it must name a Unix domain socket on which an Open vSwitch daemon is listening for control channel connections. By default, each daemon listens on a Unix domain socket named \fB@RUNDIR@/\fIprogram\fB.\fIpid\fB.ctl\fR, where \fIprogram\fR is the program's name and \fIpid\fR is its process ID. For example, if \fBovs\-vswitchd\fR has PID 123, it would listen on \fB@RUNDIR@/ovs\-vswitchd.123.ctl\fR. .IP Otherwise, \fBovs\-appctl\fR looks for a pidfile, that is, a file whose contents are the process ID of a running process as a decimal number, named \fB@RUNDIR@/\fItarget\fB.pid\fR. (The \fB\-\-pidfile\fR option makes an Open vSwitch daemon create a pidfile.) \fBovs\-appctl\fR reads the pidfile, then looks for a Unix socket named \fB@RUNDIR@/\fItarget\fB.\fIpid\fB.ctl\fR, where \fIpid\fR is replaced by the process ID read from the pidfile, and uses that file as if it had been specified directly as the target. .IP On Windows, \fItarget\fR can be an absolute path to a file that contains a localhost TCP port on which an Open vSwitch daemon is listening for control channel connections. By default, each daemon writes the TCP port on which it is listening for control connection into the file \fIprogram\fB.ctl\fR located inside the configured \fIOVS_RUNDIR\fR directory. If \fItarget\fR is not an absolute path, \fBovs\-appctl\fR looks for a file named \fItarget\fB.ctl\fR in the configured \fIOVS_RUNDIR\fR directory. .IP The default target is \fBovs\-vswitchd\fR. . .SH COMMON COMMANDS Every Open vSwitch daemon supports a common set of commands, which are documented in this section. . .SS GENERAL COMMANDS These commands display daemon-specific commands and the running version. Note that these commands are different from the \fB\-\-help\fR and \fB\-\-version\fR options that return information about the \fBovs\-appctl\fR utility itself. . .IP "\fBlist-commands\fR" Lists the commands supported by the target. . .IP "\fBversion\fR" Displays the version and compilation date of the target. . .SS LOGGING COMMANDS Open vSwitch has several log levels. The highest-severity log level is: . .IP "\fBoff\fR" No message is ever logged at this level, so setting a logging destination's log level to \fBoff\fR disables logging to that destination. . .PP The following log levels, in order of descending severity, are available: . .IP "\fBemer\fR" A major failure forced a process to abort. .IP "\fBerr\fR" A high-level operation or a subsystem failed. Attention is warranted. .IP "\fBwarn\fR" A low-level operation failed, but higher-level subsystems may be able to recover. .IP "\fBinfo\fR" Information that may be useful in retrospect when investigating a problem. .IP "\fBdbg\fR" Information useful only to someone with intricate knowledge of the system, or that would commonly cause too-voluminous log output. Log messages at this level are not logged by default. . .PP Every Open vSwitch daemon supports the following commands for examining and adjusting log levels. .IP "\fBvlog/list\fR" Lists the known logging modules and their current levels. . .IP "\fBvlog/list-pattern\fR" Lists logging pattern used for each destination. . .IP "\fBvlog/set\fR [\fIspec\fR]" Sets logging levels. Without any \fIspec\fR, sets the log level for every module and destination to \fBdbg\fR. Otherwise, \fIspec\fR is a list of words separated by spaces or commas or colons, up to one from each category below: . .RS .IP \(bu A valid module name, as displayed by the \fBvlog/list\fR command on \fBovs\-appctl\fR(8), limits the log level change to the specified module. . .IP \(bu \fBsyslog\fR, \fBconsole\fR, or \fBfile\fR, to limit the log level change to only to the system log, to the console, or to a file, respectively. .IP On Windows platform, \fBsyslog\fR is accepted as a word and is only useful if the \fItarget\fR was started with the \fB\-\-syslog\-target\fR option (the word has no effect otherwise). . .IP \(bu \fBoff\fR, \fBemer\fR, \fBerr\fR, \fBwarn\fR, \fBinfo\fR, or \fBdbg\fR, to control the log level. Messages of the given severity or higher will be logged, and messages of lower severity will be filtered out. \fBoff\fR filters out all messages. .RE . .IP Case is not significant within \fIspec\fR. .IP Regardless of the log levels set for \fBfile\fR, logging to a file will not take place unless the target application was invoked with the \fB\-\-log\-file\fR option. .IP For compatibility with older versions of OVS, \fBany\fR is accepted as a word but has no effect. . .IP "\fBvlog/set PATTERN:\fIdestination\fB:\fIpattern\fR" Sets the log pattern for \fIdestination\fR to \fIpattern\fR. Each time a message is logged to \fIdestination\fR, \fIpattern\fR determines the message's formatting. Most characters in \fIpattern\fR are copied literally to the log, but special escapes beginning with \fB%\fR are expanded as follows: . .RS .IP \fB%A\fR The name of the application logging the message, e.g. \fBovs\-vswitchd\fR. . .IP \fB%B\fR The RFC5424 syslog PRI of the message. . .IP \fB%c\fR The name of the module (as shown by \fBovs\-appctl \-\-list\fR) logging the message. . .IP \fB%d\fR The current date and time in ISO 8601 format (YYYY\-MM\-DD HH:MM:SS). . .IP \fB%d{\fIformat\fB}\fR The current date and time in the specified \fIformat\fR, which takes the same format as the \fItemplate\fR argument to \fBstrftime\fR(3). As an extension, any \fB#\fR characters in \fIformat\fR will be replaced by fractional seconds, e.g. use \fB%H:%M:%S.###\fR for the time to the nearest millisecond. Sub-second times are only approximate and currently decimal places after the third will always be reported as zero. . .IP \fB%D\fR The current UTC date and time in ISO 8601 format (YYYY\-MM\-DD HH:MM:SS). . .IP \fB%D{\fIformat\fB}\fR The current UTC date and time in the specified \fIformat\fR, which takes the same format as the \fItemplate\fR argument to \fBstrftime\fR(3). Supports the same extension for sub-second resolution as \fB%d{\fR...\fB}\fR. . .IP \fB%E\fR The hostname of the node running the application. . .IP \fB%m\fR The message being logged. . .IP \fB%N\fR A serial number for this message within this run of the program, as a decimal number. The first message a program logs has serial number 1, the second one has serial number 2, and so on. . .IP \fB%n\fR A new-line. . .IP \fB%p\fR The level at which the message is logged, e.g. \fBDBG\fR. . .IP \fB%P\fR The program's process ID (pid), as a decimal number. . .IP \fB%r\fR The number of milliseconds elapsed from the start of the application to the time the message was logged. . .IP \fB%t\fR The subprogram name, that is, an identifying name for the process or thread that emitted the log message, such as \fBmonitor\fR for the process used for \fB\-\-monitor\fR or \fBmain\fR for the primary process or thread in a program. . .IP \fB%T\fR The subprogram name enclosed in parentheses, e.g. \fB(monitor)\fR, or the empty string for the primary process or thread in a program. . .IP \fB%%\fR A literal \fB%\fR. .RE . .IP A few options may appear between the \fB%\fR and the format specifier character, in this order: . .RS .IP \fB\-\fR Left justify the escape's expansion within its field width. Right justification is the default. . .IP \fB0\fR Pad the field to the field width with \fB0\fRs. Padding with spaces is the default. . .IP \fIwidth\fR A number specifies the minimum field width. If the escape expands to fewer characters than \fIwidth\fR then it is padded to fill the field width. (A field wider than \fIwidth\fR is not truncated to fit.) .RE . .IP The default pattern for console and file output is \fB%D{%Y-%m-%dT %H:%M:%SZ}|%05N|%c|%p|%m\fR; for syslog output, \fB%05N|%c|%p|%m\fR. . .IP Daemons written in Python (e.g. \fBovs\-xapi\-sync\fR, \fBovs\-monitor\-ipsec) do not allow control over the log pattern. . .IP "\fBvlog/set\fR FACILITY:\fIfacility\fR" Sets the RFC5424 facility of the log message. \fIfacility\fR can be one of \fBkern\fR, \fBuser\fR, \fBmail\fR, \fBdaemon\fR, \fBauth\fR, \fBsyslog\fR, \fBlpr\fR, \fBnews\fR, \fBuucp\fR, \fBclock\fR, \fBftp\fR, \fBntp\fR, \fBaudit\fR, \fBalert\fR, \fBclock2\fR, \fBlocal0\fR, \fBlocal1\fR, \fBlocal2\fR, \fBlocal3\fR, \fBlocal4\fR, \fBlocal5\fR, \fBlocal6\fR or \fBlocal7\fR. . .IP "\fBvlog/reopen\fR" Causes the daemon to close and reopen its log file. (This is useful after rotating log files, to cause a new log file to be used.) .IP This has no effect if the target application was not invoked with the \fB\-\-log\-file\fR option. . .SH OPTIONS . .so lib/common.man . .SH "SEE ALSO" . \fBovs\-appctl\fR can control all Open vSwitch daemons, including: .BR ovs\-vswitchd (8), and .BR ovsdb\-server (8). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-l3ping.8.in0000644000000000000000000000013213534540071020356 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.765845936 openvswitch-2.5.9/utilities/ovs-l3ping.8.in0000644000175000017500000001100013534540071022034 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-l3ping 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME \fBovs\-l3ping\fR \- check network deployment for L3 tunneling problems . .SH SYNOPSIS \fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask]\fR \fB\-t\fR \fItunnelmode\fR .br \fBovs\-l3ping\fR \fB\-s\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR \fB\-t\fR \fItunnelmode\fR .PP \fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask],RemoteInnerIP\fR \fB\-t\fR \fItunnelmode\fR .br \fBovs\-l3ping\fR \fB\-c\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\ [:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR [\fB\-b\fR \fItargetbandwidth\fR] [\fB\-i\fR \fItestinterval\fR] \fB\-t\fR \fItunnelmode\fR .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-l3ping\fR program may be used to check for problems that could be caused by invalid routing policy, misconfigured firewall in the tunnel path or a bad NIC driver. On one of the nodes, run \fBovs\-l3ping\fR in server mode and on the other node run it in client mode. The client and server will establish L3 tunnel, over which client will give further testing instructions. The \fBovs\-l3ping\fR client will perform UDP and TCP tests. This tool is different from \fBovs\-test\fR that it encapsulates XML/RPC control connection over the tunnel, so there is no need to open special holes in firewall. .PP UDP tests can report packet loss and achieved bandwidth for various datagram sizes. By default target bandwidth for UDP tests is 1Mbit/s. .PP TCP tests report only achieved bandwidth, because kernel TCP stack takes care of flow control and packet loss. . .SS "Client Mode" An \fBovs\-l3ping\fR client will create a L3 tunnel and connect over it to the \fBovs\-l3ping\fR server to schedule the tests. \fITunnelRemoteIP\fR is the peer's IP address, where tunnel will be terminated. \fIInnerIP\fR is the address that will be temporarily assigned during testing. All test traffic originating from this IP address to the \fIRemoteInnerIP\fR will be tunneled. It is possible to override default \fIControlPort\fR and \fIDataPort\fR, if there is any other application that already listens on those two ports. . .SS "Server Mode" To conduct tests, \fBovs\-l3ping\fR server must be running. It is required that both client and server \fIInnerIP\fR addresses are in the same subnet. It is possible to specify \fIInnerIP\fR with netmask in CIDR format. . .SH OPTIONS One of \fB\-s\fR or \fB\-c\fR is required. The \fB\-t\fR option is also required. . .IP "\fB\-s \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR" .IQ "\fB\-\-server\fR \fITunnelRemoteIP,InnerIP[/mask][:ControlPort]\fR" Run in server mode and create L3 tunnel with the client that will be accepting tunnel at \fITunnelRemoteIP\fR address. The socket on \fIInnerIP[:ControlPort]\fR will be used to receive further instructions from the client. . .IP "\fB\-c \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\ [:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR" .IQ "\fB\-\-client \fITunnelRemoteIP,InnerIP[/mask][:ControlPort\ [:DataPort]],RemoteInnerIP[:ControlPort[:DataPort]]\fR" Run in client mode and create L3 tunnel with the server on \fITunnelRemoteIP\fR. The client will use \fIInnerIP\fR to generate test traffic with the server's \fIRemoteInnerIP\fR. . .IP "\fB\-b \fItargetbandwidth\fR" .IQ "\fB\-\-bandwidth\fR \fItargetbandwidth\fR" Target bandwidth for UDP tests. The \fItargetbandwidth\fR must be given in bits per second. It is possible to use postfix M or K to alter the target bandwidth magnitude. . .IP "\fB\-i \fItestinterval\fR" .IQ "\fB\-\-interval\fR \fItestinterval\fR" How long each test should run. By default 5 seconds. . .IP "\fB\-t \fItunnelmode\fR" .IQ "\fB\-\-tunnel\-mode\fR \fItunnelmode\fR" Specify the tunnel type. This option must match on server and client. . .so lib/common.man . .SH EXAMPLES .PP On host 192.168.122.220 start \fBovs\-l3ping\fR in server mode. This command will create a temporary GRE tunnel with the host 192.168.122.236 and assign 10.1.1.1/28 as the inner IP address, where client will have to connect: .IP .B ovs\-l3ping -s 192.168.122.236,10.1.1.1/28 -t gre . .PP On host 192.168.122.236 start \fBovs\-l3ping\fR in client mode. This command will use 10.1.1.2/28 as the local inner IP address and will connect over the L3 tunnel to the server's inner IP address at 10.1.1.1. .IP .B ovs\-l3ping -c 192.168.122.220,10.1.1.2/28,10.1.1.1 -t gre . .SH SEE ALSO . .BR ovs\-vswitchd (8), .BR ovs\-ofctl (8), .BR ovs\-vsctl (8), .BR ovs\-vlan\-test (8), .BR ovs\-test (8), .BR ethtool (8), .BR uname (1) openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-pcap.1.in0000644000000000000000000000013213534540071020076 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.769845965 openvswitch-2.5.9/utilities/ovs-pcap.1.in0000644000175000017500000000115213534540071021563 0ustar00jpettitjpettit00000000000000.TH ovs\-pcap 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME ovs\-pcap \- print packets from a pcap file as hex . .SH SYNOPSIS \fBovs\-pcap\fR \fIfile\fR .so lib/common-syn.man . .SH DESCRIPTION The \fBovs\-pcap\fR program reads the pcap \fIfile\fR named on the command line and prints each packet's contents as a sequence of hex digits on a line of its own. This format is suitable for use with the \fBofproto/trace\fR command supported by \fBovs\-vswitchd\fR(8). . .SH "OPTIONS" .so lib/common.man . .SH "SEE ALSO" . .BR ovs\-vswitchd (8), .BR ovs\-tcpundump (1), .BR tcpdump (8), .BR wireshark (8). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-ctl.80000644000000000000000000000013213534540071017337 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.761845906 openvswitch-2.5.9/utilities/ovs-ctl.80000644000175000017500000003671713534540071021043 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. .TH ovs\-ctl 8 "June 2011" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-ctl . .SH NAME ovs\-ctl \- OVS startup helper script . .SH SYNOPSIS \fBovs\-ctl\fR \fB\-\-system\-id=random\fR|\fIuuid\fR [\fIoptions\fR] \fBstart .br \fBovs\-ctl stop .br \fBovs\-ctl\fR \fB\-\-system\-id=random\fR|\fIuuid\fR [\fIoptions\fR] \fBrestart .br \fBovs\-ctl status .br \fBovs\-ctl version .br \fBovs\-ctl [\fIoptions\fR] \fBload\-kmod\fR .br \fBovs\-ctl \fB\-\-system\-id=random\fR|\fIuuid\fR [\fIoptions\fR] \fBforce\-reload\-kmod\fR .br \fBovs\-ctl \fR[\fB\-\-protocol=\fIprotocol\fR] [\fB\-\-sport=\fIsport\fR] [\fB\-\-dport=\fIdport\fR] \fBenable\-protocol\fR .br \fBovs\-ctl help \fR| \fB\-h \fR| \fB\-\-help .br \fBovs\-ctl \-\-version . .SH DESCRIPTION . .PP The \fBovs\-ctl\fR program starts, stops, and checks the status of Open vSwitch daemons. It is not meant to be invoked directly by system administrators but to be called internally by system startup scripts. . .PP Each of \fBovs\-ctl\fR's commands is described separately below. . .SH "The ``start'' command" . .PP The \fBstart\fR command starts Open vSwitch. It performs the following tasks: . .IP 1. Loads the Open vSwitch kernel module. If this fails, and the Linux bridge module is loaded but no bridges exist, it tries to unload the bridge module and tries loading the Open vSwitch kernel module again. (This is because the Open vSwitch kernel module cannot coexist with the Linux bridge module before 2.6.37.) . .PP The \fBstart\fR command skips the following steps if \fBovsdb\-server\fR is already running: .IP 2. If the Open vSwitch database file does not exist, it creates it. If the database does exist, but it has an obsolete version, it upgrades it to the latest schema. . .IP 3. Starts \fBovsdb-server\fR, unless the \fB\-\-no\-ovsdb\-server\fR command option is given. . .IP 4. Initializes a few values inside the database. . .IP 5. If the \fB\-\-delete\-bridges\fR option was used, deletes all of the bridges from the database. . .IP 6. If the \fB\-\-delete\-transient\-ports\fR option was used, deletes all ports that have \fBother_config:transient\fR set to true. . .PP The \fBstart\fR command skips the following step if \fBovs\-vswitchd\fR is already running, or if the \fB\-\-no\-ovs\-vswitchd\fR command option is given: .IP 7. Starts \fBovs\-vswitchd\fR. . .SS "Options" .PP Several command-line options influence the \fBstart\fR command's behavior. Some form of the following option should ordinarily be specified: . .IP "\fB\-\-system\-id=\fIuuid\fR" .IQ "\fB\-\-system\-id=random\fR" This specifies a unique system identifier to store into \fBexternal-ids:system-id\fR in the database's \fBOpen_vSwitch\fR table. Remote managers that talk to the Open vSwitch database server over network protocols use this value to identify and distinguish Open vSwitch instances, so it should be unique (at least) within OVS instances that will connect to a single controller. .IP When \fBrandom\fR is specified, \fBovs\-ctl\fR will generate a random ID that persists from one run to another (stored in a file). When another string is specified \fBovs\-ctl\fR uses it literally. . .PP The following options should be specified if the defaults are not suitable: . .IP "\fB\-\-system\-type=\fItype\fR" .IQ "\fB\-\-system\-version=\fIversion\fR" Sets the value to store in the \fBsystem-type\fR and \fBsystem-version\fR columns, respectively, in the database's \fBOpen_vSwitch\fR table. Remote managers may use these values to determine the kind of system to which they are connected (primarily for display to human administrators). .IP When not specified, \fBovs\-ctl\fR uses values from the optional \fBsystem\-type.conf\fR and \fBsystem\-version.conf\fR files(see section \fBFILES\fR) or it uses the \fBlsb_release\fR program, if present, to provide reasonable defaults. . .PP The following options are also likely to be useful: . .IP "\fB\-\-external\-id=\(dq\fIname\fB=\fIvalue\fB\(dq" Sets \fBexternal-ids:\fIname\fR to \fIvalue\fR in the database's \fBOpen_vSwitch\fR table. Specifying this option multiple times adds multiple key-value pairs. . .IP "\fB\-\-delete\-bridges\fR" Ordinarily Open vSwitch bridges persist from one system boot to the next, as long as the database is preserved. Some environments instead expect to re-create all of the bridges and other configuration state on every boot. This option supports that, by deleting all Open vSwitch bridges after starting \fBovsdb\-server\fR but before starting \fBovs\-vswitchd\fR. . .IP "\fB\-\-delete\-transient\-ports\fR" Deletes all ports that have the other_config:transient value set to true. This is important on certain environments where some ports are going to be recreated after reboot, but other ports need to be persisted in the database. . .PP The following options are less important: . .IP "\fB\-\-no\-monitor\fR" By default \fBovs\-ctl\fR passes \fB\-\-monitor\fR to \fBovs\-vswitchd\fR and \fBovsdb\-server\fR, requesting that it spawn a process monitor which will restart the daemon if it crashes. This option suppresses that behavior. . .IP "\fB\-\-daemon-cwd=\fIdirectory\fR" Specifies the current working directory that the OVS daemons should run from. The default is \fB/\fR (the root directory) if this option is not specified. (This option is useful because most systems create core files in a process's current working directory and because a file system that is in use as a process's current working directory cannot be unmounted.) . .IP "\fB\-\-no\-force\-corefiles\fR" By default, \fBovs\-ctl\fR enables core dumps for the OVS daemons. This option disables that behavior. . .IP "\fB\-\-no\-mlockall\fR" By default \fBovs\-ctl\fR passes \fB\-\-mlockall\fR to \fBovs\-vswitchd\fR, requesting that it lock all of its virtual memory, preventing it from being paged to disk. This option suppresses that behavior. . .IP "\fB\-\-ovsdb\-server\-priority=\fIniceness\fR" .IQ "\fB\-\-ovs\-vswitchd\-priority=\fIniceness\fR" Sets the \fBnice\fR(1) level used for each daemon. All of them default to \fB\-10\fR. . .IP "\fB\-\-ovsdb\-server\-wrapper=\fIwrapper\fR" .IQ "\fB\-\-ovs\-vswitchd\-wrapper=\fIwrapper\fR" . Configures the specified daemon to run under \fIwrapper\fR, which is one of the following: . .RS .IP "\fBvalgrind\fR" Run the daemon under \fBvalgrind\fR(1), if it is installed, logging to \fIdaemon\fB.valgrind.log.\fIpid\fR in the log directory. . .IP "\fBstrace\fR" Run the daemon under \fBstrace\fR(1), if it is installed, logging to \fIdaemon\fB.strace.log.\fIpid\fR in the log directory. . .IP "\fBglibc\fR" Enable GNU C library features designed to find memory errors. .RE . .IP By default, no wrapper is used. . .IP Each of the wrappers can expose bugs in Open vSwitch that lead to incorrect operation, including crashes. The \fBvalgrind\fR and \fBstrace\fR wrappers greatly slow daemon operations so they should not be used in production. They also produce voluminous logs that can quickly fill small disk partitions. The \fBglibc\fR wrapper is less resource-intensive but still somewhat slows the daemons. . .PP The following options control file locations. They should only be used if the default locations cannot be used. See \fBFILES\fR, below, for more information. . .IP "\fB\-\-db\-file=\fIfile\fR" Overrides the file name for the OVS database. . .IP "\fB\-\-db\-sock=\fIsocket\fR" Overrides the file name for the Unix domain socket used to connect to \fBovsdb\-server\fR. . .IP "\fB\-\-db\-schema=\fIschema\fR" Overrides the file name for the OVS database schema. . .IP "\fB\-\-extra-dbs=\fIfile\fR" Adds \fIfile\fR as an extra database for \fBovsdb\-server\fR to serve out. Multiple space-separated file names may also be specified. \fIfile\fR should begin with \fB/\fR; if it does not, then it will be taken as relative to \fIdbdir\fR. . .SH "The ``stop'' command" . .PP The \fBstop\fR command does not unload the Open vSwitch kernel modules. It can take the same \fB\-\-no\-ovsdb\-server\fR and \fB\-\-no\-ovs\-vswitchd\fR options as that of the \fBstart\fR command. . .PP This command does nothing and finishes successfully if the OVS daemons aren't running. . .SH "The ``restart'' command" . .PP The \fBrestart\fR command performs a \fBstop\fR followed by a \fBstart\fR command. The command can take the same options as that of the \fBstart\fR command. In addition, it saves and restores OpenFlow flows for each individual bridge. . .SH "The ``status'' command" . .PP The \fBstatus\fR command checks whether the OVS daemons \fBovs-vswitchd\fR and \fBovsdb\-server\fR are running and prints messages with that information. It exits with status 0 if the daemons are running, 1 otherwise. . .SH "The ``version'' command" . .PP The \fBversion\fR command runs \fBovsdb\-server \-\-version\fR and \fBovs\-vswitchd \-\-version\fR. . .SH "The ``force\-reload\-kmod'' command" . .PP The \fBforce\-reload\-kmod\fR command allows upgrading the Open vSwitch kernel module without rebooting. It performs the following tasks: . .IP 1. Gets a list of OVS ``internal'' interfaces, that is, network devices implemented by Open vSwitch. The most common examples of these are bridge ``local ports''. . .IP 2. Saves the OpenFlow flows of each bridge. . .IP 3. Stops the Open vSwitch daemons, as if by a call to \fBovs\-ctl stop\fR. . .IP 4. Saves the kernel configuration state of the OVS internal interfaces listed in step 1, including IP and IPv6 addresses and routing table entries. . .IP 5. Unloads the Open vSwitch kernel module (including the bridge compatibility module if it is loaded). . .IP 6. Starts OVS back up, as if by a call to \fBovs\-ctl start\fR. This reloads the kernel module, restarts the OVS daemons and finally restores the saved OpenFlow flows. . .IP 7. Restores the kernel configuration state that was saved in step 4. . .IP 8. Checks for daemons that may need to be restarted because they have packet sockets that are listening on old instances of Open vSwitch kernel interfaces and, if it finds any, prints a warning on stdout. DHCP is a common example: if the ISC DHCP client is running on an OVS internal interface, then it will have to be restarted after completing the above procedure. (It would be nice if \fBovs\-ctl\fR could restart daemons automatically, but the details are far too specific to a particular distribution and installation.) . .PP \fBforce\-kmod\-reload\fR internally stops and starts OVS, so it accepts all of the options accepted by the \fBstart\fR command except for the \fB\-\-no\-ovs\-vswitchd\fR option. . .SH "The ``load\-kmod'' command" . .PP The \fBload\-kmod\fR command loads the openvswitch kernel modules if they are not already loaded. This operation also occurs as part of the \fBstart\fR command. The motivation for providing the \fBload\-kmod\fR command is to allow errors when loading modules to be handled separatetly from other errors that may occur when running the \fBstart\fR command. . .PP By default the \fBload\-kmod\fR command attempts to load the openvswitch kernel module. . .SH "The ``enable\-protocol'' command" . .PP The \fBenable\-protocol\fR command checks for rules related to a specified protocol in the system's \fBiptables\fR(8) configuration. If there are no rules specifically related to that protocol, then it inserts a rule to accept the specified protocol. . .PP More specifically: . .IP \(bu If \fBiptables\fR is not installed or not enabled, this command does nothing, assuming that lack of filtering means that the protocol is enabled. . .IP \(bu If the \fBINPUT\fR chain has a rule that matches the specified protocol, then this command does nothing, assuming that whatever rule is installed reflects the system administrator's decisions. . .IP \(bu Otherwise, this command installs a rule that accepts traffic of the specified protocol. . .PP This command normally completes successfully, even if it does nothing. Only the failure of an attempt to insert a rule normally causes it to return an exit code other than 0. . The following options control the protocol to be enabled: . .IP "\fB\-\-protocol=\fIprotocol\fR" The name of the IP protocol to be enabled, such as \fBgre\fR or \fBtcp\fR. The default is \fBgre\fR. . .IP "\fB\-\-sport=\fIsport\fR" .IQ "\fB\-\-dport=\fIdport\fR" TCP or UDP source or destination port to match. These are optional and allowed only with \fB\-\-protocol=tcp\fR or \fB\-\-protocol=udp\fR. . .SH "The ``help'' command" . Prints a usage message and exits successfully. . .SH "OPTIONS" .PP In addition to the options listed for each command above, these options control the behavior of several of \fBovs\-ctl\fR's commands. . .PP By default, \fBovs\-ctl\fR will control the \fBovsdb\-server\fR, and the \fBovs\-vswitchd\fR daemons. The following options restrict that control to exclude one or the other: . .IP "\fB\-\-no\-ovsdb-server\fR" Specifies that the \fBovs\-ctl\fR commands \fBstart\fR, \fBstop\fR, and \fBrestart\fR should not modify the running status of \fBovsdb\-server\fR. . .IP "\fB\-\-no\-ovs\-vswitchd\fR" Specifies that the \fBovs\-ctl\fR commands \fBstart\fR, \fBstop\fR, and \fBrestart\fR should not modify the running status of \fBovs\-vswitchd\fR. It is an error to include this option with the \fBforce\-reload\-kmod\fR command. . .SH "EXIT STATUS" . \fBovs\-ctl\fR exits with status 0 on success and nonzero on failure. The \fBstart\fR command is considered to succeed if OVS is already started; the \fBstop\fR command is considered to succeed if OVS is already stopped. . .SH "ENVIRONMENT" . The following environment variables affect \fBovs\-ctl\fR: . .IP "\fBPATH\fR" \fBovs\-ctl\fR does not hardcode the location of any of the programs that it runs. \fBovs\-ctl\fR will add the \fIsbindir\fR and \fIbindir\fR that were specified at \fBconfigure\fR time to \fBPATH\fR, if they are not already present. . .IP "\fBOVS_LOGDIR\fR" .IQ "\fBOVS_RUNDIR\fR" .IQ "\fBOVS_DBDIR\fR" .IQ "\fBOVS_SYSCONFDIR\fR" .IQ "\fBOVS_PKGDATADIR\fR" .IQ "\fBOVS_BINDIR\fR" .IQ "\fBOVS_SBINDIR\fR" Setting one of these variables in the environment overrides the respective \fBconfigure\fR option, both for \fBovs\-ctl\fR itself and for the other Open vSwitch programs that it runs. . .SH "FILES" . \fBovs\-ctl\fR uses the following files: . .IP "\fBovs\-lib" Shell function library used internally by \fBovs\-ctl\fR. It must be installed in the same directory as \fBovs\-ctl\fR. . .IP "\fIlogdir\fB/\fIdaemon\fB.log\fR" Per-daemon logfiles. . .IP "\fIrundir\fB/\fIdaemon\fB.pid\fR" Per-daemon pidfiles to track whether a daemon is running and with what process ID. . .IP "\fIpkgdatadir\fB/vswitch.ovsschema\fR" The OVS database schema used to initialize the database (use \fB\-\-db\-schema to override this location). . .IP "\fIdbdir\fB/conf.db\fR" The OVS database (use \fB\-\-db\-file\fR to override this location). . .IP "\fIrundir\fB/openvswitch/db.sock\fR" The Unix domain socket used for local communication with \fBovsdb\-server\fR (use \fB\-\-db\-sock\fR to override this location). . .IP "\fIsysconfdir\fB/openvswitch/system-id.conf\fR" The persistent system UUID created and read by \fB\-\-system\-id=random\fR. . .IP "\fIsysconfdir\fB/openvswitch/system\-type.conf\fR" .IQ "\fIsysconfdir\fB/openvswitch/system\-version.conf\fR" The \fBsystem\-type\fR and \fBsystem\-version\fR values stored in the database's \fBOpen_vSwitch\fR table when not specified as a command-line option. . .SH "EXAMPLE" . .PP The files \fBdebian/openvswitch\-switch.init\fR and \fBxenserver/etc_init.d_openvswitch\fR in the Open vSwitch source distribution are good examples of how to use \fBovs\-ctl\fR. . .SH "SEE ALSO" . \fBREADME.md\fR, \fBINSTALL.Linux.md\fR, \fBovsdb\-server\fR(8), \fBovs\-vswitchd\fR(8). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-sim.in0000644000000000000000000000013213534540071017604 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.805846231 openvswitch-2.5.9/utilities/ovs-sim.in0000755000175000017500000002310713534540071021300 0ustar00jpettitjpettit00000000000000#! /usr/bin/env bash # # Copyright (c) 2013, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e sim_builddir='@abs_builddir@'; export sim_builddir sim_srcdir='@abs_top_srcdir@'; export sim_srcdir interactive=false scripts= for option; do case $option in -h|--help) cat <&2 exit 1 ;; *) case $option in /*) ;; *) option=`pwd`/$option ;; esac scripts="$scripts $option" ;; esac shift done if test -z "$scripts"; then interactive=: fi # Check that we've got proper builddir and srcdir. if test ! -e "$sim_builddir"/vswitchd/ovs-vswitchd; then echo "$sim_builddir/vswitchd/ovs-vswitchd does not exist (need to run \"make\"?)" >&2 exit 1 fi if test ! -e "$sim_srcdir"/WHY-OVS.md; then echo "$sim_srcdir/WHY-OVS.md does not exist" >&2 exit 1 fi # Put built tools early in $PATH. PATH=$sim_builddir/ovsdb:$sim_builddir/vswitchd:$sim_builddir/utilities:$PATH PATH=$sim_builddir/ovn/controller:$sim_builddir/ovn/northd:$sim_builddir/ovn/utilities:$PATH export PATH rm -rf sandbox mkdir sandbox cd sandbox sim_base=`pwd`; export sim_base trap_signals() { for signal in 0 1 2 3 13 14 15; do trap " set +e cd '$sim_base' && (kill \`cat */*.pid\`) >/dev/null 2>&1 trap - $signal kill -$signal $$" $signal done } export -f trap_signals trap_signals sim_setvars() { sandbox=$1 OVS_RUNDIR=$sim_base/$1; export OVS_RUNDIR OVS_LOGDIR=$sim_base/$1; export OVS_LOGDIR OVS_DBDIR=$sim_base/$1; export OVS_DBDIR OVS_SYSCONFDIR=$sim_base/$1; export OVS_SYSCONFDIR PS1="|$1: $sim_PS1" } export -f sim_setvars as() { case $# in 0) echo >&2 "$FUNCNAME: missing arguments (use --help for help)" return 1 ;; 1) if test "$1" != --help; then sim_setvars $1 else cat <&2 "$FUNCNAME: missing argument (use --help for help)" return 1 fi set X $1; shift if test $# != 1; then echo >&2 "$FUNCNAME: sandbox name must be a single word" return 1 fi if test -e "$sim_base/$1"; then echo >&2 "$1 already exists" return 1 fi # Create sandbox. mkdir "$sim_base"/$1 || return 1 daemon_opts="--detach --no-chdir --pidfile -vconsole:off --log-file" # Create database and start ovsdb-server. touch $sim_base/$1/.conf.db.~lock~ as $1 ovsdb-tool create $sim_base/$1/conf.db "$sim_srcdir/vswitchd/vswitch.ovsschema" as $1 ovsdb-server $daemon_opts --remote=punix:"$sim_base"/$1/db.sock # Initialize database. as $1 ovs-vsctl --no-wait -- init # Start ovs-vswitchd. as $1 ovs-vswitchd $daemon_opts --enable-dummy=system -vvconn -vnetdev_dummy } export -f sim_add net_add() { if test "$1" == --help; then cat <&2 "$FUNCNAME: missing argument (use --help for help)" return 1 fi as main ovs-vsctl add-br "$1" } export -f net_add net_attach() { if test "$1" == --help; then cat <&2 "$FUNCNAME: wrong number of arguments (use --help for help)" return 1 fi if test $sandbox = main; then echo >&2 "$FUNCNAME: can only attach interconnection networks to sandboxes other than main" return 1 fi local net=$1 bridge=$2 port=${sandbox}_$bridge as main ovs-vsctl \ -- add-port $net "$port" \ -- set Interface "$port" options:pstream="punix:$sim_base/main/$port.sock" options:rxq_pcap="$sim_base/main/$port-rx.pcap" options:tx_pcap="$sim_base/main/$port-tx.pcap" options:header=extended ovs-vsctl \ -- set Interface $bridge options:tx_pcap="$sim_base/$sandbox/$bridge-tx.pcap" options:rxq_pcap="$sim_base/$sandbox/$bridge-rx.pcap" \ -- add-port $bridge ${bridge}_$net \ -- set Interface ${bridge}_$net options:stream="unix:$sim_base/main/$port.sock" options:rxq_pcap="$sim_base/$sandbox/${bridge}_$net-rx.pcap" options:tx_pcap="$sim_base/$sandbox/${bridge}_$net-tx.pcap" options:header=extended } export -f net_attach ovn_start() { if test "$1" == --help; then cat <&2 "$FUNCNAME: no arguments accepted (use --help for help)" return 1 fi if test -d ovn-sb || test -d ovn-nb; then echo >&2 "OVN already started" exit 1 fi daemon_opts="--detach --no-chdir --pidfile -vconsole:off --log-file" for db in ovn-sb ovn-nb; do mkdir "$sim_base"/$db touch "$sim_base"/$db/.$db.db.~lock~ as $db ovsdb-tool create "$sim_base"/$db/$db.db "$sim_srcdir"/ovn/$db.ovsschema as $db ovsdb-server $daemon_opts --remote=punix:"$sim_base"/$db/$db.sock "$sim_base"/$db/$db.db done OVN_NB_DB=unix:$sim_base/ovn-nb/ovn-nb.sock; export OVN_NB_DB OVN_SB_DB=unix:$sim_base/ovn-sb/ovn-sb.sock; export OVN_SB_DB mkdir "$sim_base"/northd as northd ovn-northd $daemon_opts \ --ovnnb-db="$OVN_NB_DB" \ --ovnsb-db="$OVN_SB_DB" } export -f ovn_start ovn_attach() { if test "$1" == --help; then cat <&2 "$FUNCNAME: wrong number of arguments (use --help for help)" return 1 fi local net=$1 bridge=$2 ip=$3 masklen=${4-24} net_attach $net $bridge || return $? ovs-appctl netdev-dummy/ip4addr $bridge $ip/$masklen >/dev/null ovs-appctl ovs/route/add $ip/$masklen $bridge > /dev/null ovs-vsctl \ -- set Open_vSwitch . external-ids:system-id=$sandbox \ -- set Open_vSwitch . external-ids:ovn-remote=unix:$sim_base/ovn-sb/ovn-sb.sock \ -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ -- set Open_vSwitch . external-ids:ovn-encap-ip=$ip\ -- add-br br-int \ -- set bridge br-int fail-mode=secure other-config:disable-in-band=true ovn-controller --detach --no-chdir --pidfile -vconsole:off --log-file } export -f ovn_attach # Easy access to OVS manpages. mkdir $sim_base/man mandir=`cd $sim_base/man && pwd` (cd "$sim_builddir" && ${MAKE-make} install-man mandir=$mandir >/dev/null) MANPATH=$mandir:; export MANPATH export scripts export interactive rc=' if [ -f /etc/bashrc ]; then . /etc/bashrc fi if [ -f ~/.bashrc ]; then . ~/.bashrc fi trap_signals sim_PS1=$PS1 sim_add main as main for script in $scripts; do . $script || exit $? done $interactive || exit 0 cat < /dev/null 2>&1; then :; else echo "$0: ip not found in $PATH" >&2 exit 1 fi if test "$#" = 0; then exit 0 fi devs="$@" for dev in $devs; do state=`ip link show dev $dev` || continue echo "# $dev" # Link state (Ethernet addresses, up/down, ...) linkcmd= case $state in *"state UP"* | *[,\<]"UP"[,\>]* ) linkcmd="$linkcmd up" ;; *"state DOWN"*) linkcmd="$linkcmd down" ;; esac if expr "$state" : '.*\bdynamic\b' > /dev/null; then linkcmd="$linkcmd dynamic" fi if qlen=`expr "$state" : '.*qlen \([0-9]\+\)'`; then linkcmd="$linkcmd txqueuelen $qlen" fi if hwaddr=`expr "$state" : '.*link/ether \([^ ]*\)'`; then linkcmd="$linkcmd address $hwaddr" fi if brd=`expr "$state" : '.*brd \([^ ]*\)'`; then linkcmd="$linkcmd broadcast $brd" fi if mtu=`expr "$state" : '.*mtu \([0-9]\+\)'`; then linkcmd="$linkcmd mtu $mtu" fi if test -n "$linkcmd"; then echo ip link set dev $dev down # Required to change hwaddr. echo ip link set dev $dev $linkcmd fi move_ip_address $dev $dev move_ip_routes $dev $dev echo done if (iptables-save) > /dev/null 2>&1; then echo "# global" echo "iptables-restore <<'EOF'" iptables-save echo "EOF" else echo "# iptables-save not found in $PATH, not saving iptables state" fi } save_flows () { if (ovs-ofctl --version) > /dev/null 2>&1; then :; else echo "$0: ovs-ofctl not found in $PATH" >&2 exit 1 fi for bridge in "$@"; do echo -n "ovs-ofctl add-tlv-map ${bridge} '" ovs-ofctl dump-tlv-map ${bridge} | \ awk '/^ 0x/ {if (cnt != 0) printf ","; \ cnt++;printf "{class="$1",type="$2",len="$3"}->"$4}' echo "'" echo "ovs-ofctl add-flows ${bridge} - << EOF" ovs-ofctl dump-flows "${bridge}" | sed -e '/NXST_FLOW/d' \ -e 's/\(idle\|hard\)_age=[^,]*,//g' echo "EOF" done } while [ $# -ne 0 ] do case $1 in "save-flows") shift save_flows "$@" exit 0 ;; "save-interfaces") shift save_interfaces "$@" exit 0 ;; -h | --help) usage exit 0 ;; *) echo >&2 "$0: unknown command \"$1\" (use --help for help)" exit 1 ;; esac done exit 0 openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-pki.in0000644000000000000000000000013213534540071017577 xustar0030 mtime=1567801401.953685223 30 atime=1567801402.137686576 30 ctime=1567801423.821846349 openvswitch-2.5.9/utilities/ovs-pki.in0000755000175000017500000003425513534540071021301 0ustar00jpettitjpettit00000000000000#! /bin/sh # Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e pkidir='@PKIDIR@' command= prev= force=no batch=no log='@LOGDIR@/ovs-pki.log' keytype=rsa bits=2048 # OS-specific compatibility routines case $(uname -s) in FreeBSD|NetBSD) file_mod_epoch() { stat -r "$1" | awk '{print $10}' } file_mod_date() { stat -f '%Sm' "$1" } sha1sum() { sha1 "$@" } ;; *) file_mod_epoch() { date -r "$1" +%s } file_mod_date() { date -r "$1" } ;; esac for option; do # This option-parsing mechanism borrowed from a Autoconf-generated # configure script under the following license: # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2009 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # If the previous option needs an argument, assign it. if test -n "$prev"; then eval $prev=\$option prev= continue fi case $option in *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;; *) optarg=yes ;; esac case $dashdash$option in --) dashdash=yes ;; -h|--help) cat <&2 exit 1 ;; *) if test -z "$command"; then command=$option elif test -z "${arg1+set}"; then arg1=$option elif test -z "${arg2+set}"; then arg2=$option else echo "$option: only two arguments may be specified" >&2 exit 1 fi ;; esac shift done if test -n "$prev"; then option=--`echo $prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $option" >&2 { (exit 1); exit 1; }; } fi if test -z "$command"; then echo "$0: missing command name; use --help for help" >&2 exit 1 fi if test "$keytype" != rsa && test "$keytype" != dsa; then echo "$0: argument to -k or --key must be rsa or dsa" >&2 exit 1 fi if test "$bits" -lt 1024; then echo "$0: argument to -B or --bits must be at least 1024" >&2 exit 1 fi if test -z "$dsaparam"; then dsaparam=$pkidir/dsaparam.pem fi case $log in /* | ?:[\\/]*) ;; *) log=`pwd`/$log ;; esac logdir=$(dirname "$log") if test ! -d "$logdir"; then mkdir -p -m755 "$logdir" 2>/dev/null || true if test ! -d "$logdir"; then echo "$0: log directory $logdir does not exist and cannot be created" >&2 exit 1 fi fi if test "$command" = "init"; then if test -e "$pkidir" && test "$force" != "yes"; then echo "$0: $pkidir already exists and --force not specified" >&2 exit 1 fi if test ! -d "$pkidir"; then mkdir -p "$pkidir" fi cd "$pkidir" exec 3>>$log if test $keytype = dsa && test ! -e dsaparam.pem; then echo "Generating DSA parameters, please wait..." >&2 openssl dsaparam -out dsaparam.pem $bits 1>&3 2>&3 fi # Get the current date to add some uniqueness to this certificate curr_date=`date +"%Y %b %d %T"` # Create the CAs. for ca in controllerca switchca; do echo "Creating $ca..." >&2 oldpwd=`pwd` mkdir -p $ca cd $ca mkdir -p certs crl newcerts mkdir -p -m 0700 private touch index.txt test -e crlnumber || echo 01 > crlnumber test -e serial || echo 01 > serial # Put DSA parameters in directory. if test $keytype = dsa && test ! -e dsaparam.pem; then cp ../dsaparam.pem . fi # Write CA configuration file. if test ! -e ca.cnf; then sed "s/@ca@/$ca/g;s/@curr_date@/$curr_date/g" > ca.cnf <<'EOF' [ req ] prompt = no distinguished_name = req_distinguished_name [ req_distinguished_name ] C = US ST = CA L = Palo Alto O = Open vSwitch OU = @ca@ CN = OVS @ca@ CA Certificate (@curr_date@) [ ca ] default_ca = the_ca [ the_ca ] dir = . # top dir database = $dir/index.txt # index file. new_certs_dir = $dir/newcerts # new certs dir certificate = $dir/cacert.pem # The CA cert serial = $dir/serial # serial no file private_key = $dir/private/cakey.pem# CA private key RANDFILE = $dir/private/.rand # random number file default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha512 # message digest to use policy = policy # default policy email_in_dn = no # Don't add the email into cert DN name_opt = ca_default # Subject name display option cert_opt = ca_default # Certificate display option copy_extensions = none # Don't copy extensions from request unique_subject = no # Allow certs with duplicate subjects # For the CA policy [ policy ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional EOF fi # Create certificate authority. if test $keytype = dsa; then newkey=dsa:dsaparam.pem else newkey=rsa:$bits fi openssl req -config ca.cnf -nodes \ -newkey $newkey -keyout private/cakey.pem -out careq.pem \ 1>&3 2>&3 openssl ca -config ca.cnf -create_serial -out cacert.pem \ -days 3650 -batch -keyfile private/cakey.pem -selfsign \ -infiles careq.pem 1>&3 2>&3 chmod 0700 private/cakey.pem cd "$oldpwd" done exit 0 fi one_arg() { if test -z "$arg1" || test -n "$arg2"; then echo "$0: $command must have exactly one argument; use --help for help" >&2 exit 1 fi } one_or_two_args() { if test -z "$arg1"; then echo "$0: $command must have one or two arguments; use --help for help" >&2 exit 1 fi } must_not_exist() { if test -e "$1" && test "$force" != "yes"; then echo "$0: $1 already exists and --force not supplied" >&2 exit 1 fi } make_tmpdir() { TMP=/tmp/ovs-pki.tmp$$ rm -rf $TMP trap "rm -rf $TMP" 0 mkdir -m 0700 $TMP } fingerprint() { file=$1 name=${1-$2} date=$(file_mod_date "$file") if grep -e '-BEGIN CERTIFICATE-' "$file" > /dev/null; then fingerprint=$(openssl x509 -noout -in "$file" -fingerprint | sed 's/SHA1 Fingerprint=//' | tr -d ':') else fingerprint=$(sha1sum "$file" | awk '{print $1}') fi printf "$name\\t$date\\n" case $file in $fingerprint*) printf "\\t(correct fingerprint in filename)\\n" ;; *) printf "\\tfingerprint $fingerprint\\n" ;; esac } verify_fingerprint() { fingerprint "$@" if test $batch != yes; then echo "Does fingerprint match? (yes/no)" read answer if test "$answer" != yes; then echo "Match failure, aborting" >&2 exit 1 fi fi } check_type() { if test x = x"$1"; then type=switch elif test "$1" = switch || test "$1" = controller; then type=$1 else echo "$0: type argument must be 'switch' or 'controller'" >&2 exit 1 fi } parse_age() { number=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\1/') unit=$(echo $1 | sed 's/^\([0-9]\+\)\([[:alpha:]]\+\)/\2/') case $unit in s) factor=1 ;; min) factor=60 ;; h) factor=3600 ;; day) factor=86400 ;; *) echo "$1: age not in the form Ns, Nmin, Nh, Nday (e.g. 1day)" >&2 exit 1 ;; esac echo $(($number * $factor)) } must_exist() { if test ! -e "$1"; then echo "$0: $1 does not exist" >&2 exit 1 fi } pkidir_must_exist() { if test ! -e "$pkidir"; then echo "$0: $pkidir does not exist (need to run 'init' or use '--dir'?)" >&2 exit 1 elif test ! -d "$pkidir"; then echo "$0: $pkidir is not a directory" >&2 exit 1 fi } make_request() { must_not_exist "$arg1-privkey.pem" must_not_exist "$arg1-req.pem" make_tmpdir # Use uuidgen or date to create unique subject DNs. unique=`(uuidgen) 2>/dev/null` || unique=`date +"%Y %b %d %T"` cat > "$TMP/req.cnf" <&3 2>&3 \ || exit $? else must_exist "$dsaparam" (umask 077 && openssl gendsa -out "$1-privkey.pem" "$dsaparam") \ 1>&3 2>&3 || exit $? fi openssl req -config "$TMP/req.cnf" -new -text \ -key "$1-privkey.pem" -out "$1-req.pem" 1>&3 2>&3 } sign_request() { must_exist "$1" must_not_exist "$2" pkidir_must_exist case "$1" in /* | ?:[\\/]*) request_file="$1" ;; *) request_file="`pwd`/$1" ;; esac (cd "$pkidir/${type}ca" && openssl ca -config ca.cnf -batch -in "$request_file") \ > "$2.tmp$$" 2>&3 mv "$2.tmp$$" "$2" } glob() { files=$(echo $1) if test "$files" != "$1"; then echo "$files" fi } exec 3>>$log || true if test "$command" = req; then one_arg make_request "$arg1" fingerprint "$arg1-req.pem" elif test "$command" = sign; then one_or_two_args check_type "$arg2" verify_fingerprint "$arg1-req.pem" sign_request "$arg1-req.pem" "$arg1-cert.pem" elif test "$command" = req+sign; then one_or_two_args check_type "$arg2" pkidir_must_exist make_request "$arg1" sign_request "$arg1-req.pem" "$arg1-cert.pem" fingerprint "$arg1-req.pem" elif test "$command" = verify; then one_or_two_args must_exist "$arg1-cert.pem" check_type "$arg2" pkidir_must_exist openssl verify -CAfile "$pkidir/${type}ca/cacert.pem" "$arg1-cert.pem" elif test "$command" = fingerprint; then one_arg fingerprint "$arg1" elif test "$command" = self-sign; then one_arg must_exist "$arg1-req.pem" must_exist "$arg1-privkey.pem" must_not_exist "$arg1-cert.pem" # Create both the private key and certificate with restricted permissions. (umask 077 && \ openssl x509 -in "$arg1-req.pem" -out "$arg1-cert.pem.tmp" \ -signkey "$arg1-privkey.pem" -req -days 3650 -text) 2>&3 || exit $? # Reset the permissions on the certificate to the user's default. cat "$arg1-cert.pem.tmp" > "$arg1-cert.pem" rm -f "$arg1-cert.pem.tmp" else echo "$0: $command command unknown; use --help for help" >&2 exit 1 fi openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vlan-bug-workaround.8.in0000644000000000000000000000013213534540071023066 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.769845965 openvswitch-2.5.9/utilities/ovs-vlan-bug-workaround.8.in0000644000175000017500000000552713534540071024565 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-vlan\-bug\-workaround 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .ds PN ovs\-vlan\-bug\-workaround . .SH NAME ovs\-vlan\-bug\-workaround \- utility for configuring Linux VLAN driver bug workaround . .SH SYNOPSIS \fBovs\-vlan\-bug\-workaround \fInetdev\fR \fBon\fR .br \fBovs\-vlan\-bug\-workaround \fInetdev\fR \fBoff\fR .br \fBovs\-vlan\-bug\-workaround \-\-help .br \fBovs\-vlan\-bug\-workaround \-\-version .SH DESCRIPTION . .PP Some Linux network drivers support a feature called ``VLAN acceleration''. VLAN acceleration is associated with a data structure called a \fBvlan_group\fR that is, abstractly, a dictionary that maps from a VLAN ID (in the range 0 to 4095) to a VLAN device, that is, a Linux network device associated with a particular VLAN, e.g. \fBeth0.9\fR for VLAN 9 on \fBeth0\fR. .PP Some drivers that support VLAN acceleration have bugs that fall roughly into the categories listed below. \fBovs\-vlan\-test\fR(8) can test for these driver bugs. .so utilities/ovs-vlan-bugs.man .PP .PP The correct long term solution is to fix these driver bugs. .PP For now, \fBovs\-vlan\-bug\-workaround\fR can enable a special-purpose workaround for devices with buggy VLAN acceleration. A kernel patch must be applied for this workaround to work. .PP Use the command \fBovs\-vlan\-bug\-workaround \fInetdev\fR \fBon\fR to enable the VLAN driver bug workaround for network device \fInetdev\fR. Use the command \fBovs\-vlan\-bug\-workaround \fInetdev\fR \fBoff\fR to disable the VLAN driver bug workaround for network device \fInetdev\fR. .SH "DRIVER DETAILS" .PP The following drivers in Linux version 2.6.32.12-0.7.1.xs1.0.0.311.170586 implement VLAN acceleration and are relevant to Open vSwitch on XenServer. We have not tested any version of most of these drivers, so we do not know whether they have a VLAN problem that needs to be fixed. The drivers are listed by the name that they report in, e.g., \fBethtool \-i\fR output; in a few cases this differs slightly from the name of the module's \fB.ko\fR file: . .nf .ta T 1i \fB8139cp acenic amd8111e atl1c ATL1E atl1 atl2 be2net bna bnx2 bnx2x cnic cxgb cxgb3 e1000 e1000e enic forcedeth igb igbvf ixgb ixgbe jme ml4x_core ns83820 qlge r8169 S2IO sky2 starfire tehuti tg3 typhoon via-velocity vxge .fi .PP The following drivers use \fBvlan_group\fR but are irrelevant to Open vSwitch on XenServer: .IP "\fBbonding\fR" Not used with Open vSwitch on XenServer. .IP "\fBgianfar\fR" Not shipped with XenServer. A FreeScale CPU-integrated device. .IP "\fBehea\fR" Cannot be built on x86. IBM Power architecture only. .IP "\fBstmmac\fR" Cannot be built on x86. SH4 architecture only. .IP "\fBvmxnet3\fR" Not shipped with XenServer. For use inside VMware VMs only. . .SH OPTIONS . .so lib/common.man . .SH BUGS . Obviously. . .SH "SEE ALSO" . .BR ovs\-vlan\-test (8). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-dpctl-top.8.in0000644000000000000000000000013213534540071021070 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.765845936 openvswitch-2.5.9/utilities/ovs-dpctl-top.8.in0000644000175000017500000001024213534540071022555 0ustar00jpettitjpettit00000000000000.de IQ . br . ns . IP "\\$1" .. .TH ovs\-dpctl\-top "8" "@VERSION@" "Open vSwitch" "Open vSwitch Manual" . .SH NAME \fBovs\-dpctl\-top\fR \- Top like behavior for ovs\-dpctl dump\-flows . .SH SYNOPSIS \fBovs\-dpctl\-top\fR [\-h] [\-v] [\-f FLOWFILES] [\-V] [\-s] [\-\-host HOST] [\-a | \-\-accumulate] [\-\-accumulate\-decay ACCUMULATEDECAY] [\-d DELAY] . .SH DESCRIPTION .PP This program summarizes \fBovs\-dpctl\fR flow content by aggregating the number of packets, total bytes and occurrence of the following fields: .IP \- Datapath in_port .IP \- Ethernet type .IP \- Source and destination MAC addresses .IP \- IP protocol .IP \- Source and destination IPv4 addresses .IP \- Source and destination IPv6 addresses .IP \- UDP and TCP destination port .IP \- Tunnel source and destination addresses . .SS "Output shows four values:" .IP \- FIELDS: the flow fields for example in_port(1). .IP \- COUNT: the number of lines in the dump\-flow output contain the flow field. .IP \- PACKETS: the total number of packets containing the flow field. .IP \- BYTES: the total number of bytes containing the flow field. If units are not present then values are in bytes. .IP \- AVERAGE: the average packets size (BYTES/PACKET). .PP .SS "Top Behavior" .PP While in top mode, the default behavior, the following single character commands are supported: .IP a \- toggles top in accumulate and live mode. Accumulate mode is described below. .IP s \- toggles which column is used to sort content in decreasing order. A DESC title is placed over the column. .IP _ \- a space indicating to collect dump\-flow content again .IP h \- halt output. Any character will restart sampling .IP f \- cycle through flow fields .IP q \- q for quit. .PP .SS "Accumulate Mode" .PP There are two supported modes: live and accumulate. The default is live. The parameter \fB\-\-accumulate\fR or the 'a' character in top mode enables the latter. In live mode, recent dump\-flow content is presented. Where as accumulate mode keeps track of the prior historical information until the flow is reset not when the flow is purged. Reset flows are determined when the packet count for a flow has decreased from its previous sample. There is one caveat, eventually the system will run out of memory if, after the accumulate\-decay period any flows that have not been refreshed are purged. The goal here is to free memory of flows that are not active. Statistics are not decremented. Their purpose is to reflect the overall history of the flow fields. .PP .SS "Debugging Errors" .PP Parsing errors are counted and displayed in the status line at the beginning of the output. Use the \fB\-\-verbose\fR option with \fB\-\-script to see what output was not parsed, like this: .PP $ ovs\-dpctl dump\-flows | ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-verbose\fR .PP Error messages will identify content that failed to parse. .PP .SS "Access Remote Hosts" .PP The \fB\-\-host\fR must follow the format user@hostname. This script simply calls \&'ssh user@Hostname' without checking for login credentials therefore public keys should be installed on the system identified by hostname, such as: .PP $ ssh\-copy\-id user@hostname .PP Consult ssh\-copy\-id man pages for more details. .PP .SS "Expected usage" .PP $ ovs\-dpctl\-top .PP or to run as a script: .PP $ ovs\-dpctl dump\-flows > dump\-flows.log .PP $ ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-flow\-file\fR dump\-flows.log .SS "OPTIONS" .TP \fB\-h\fR, \fB\-\-help\fR show this help message and exit. .TP \fB\-v\fR, \fB\-\-version\fR show program's version number and exit. .TP \fB\-f\fR FLOWFILES, \fB\-\-flow\-file\fR FLOWFILES file containing flows from ovs\-dpctl dump\-flow. .TP \fB\-V\fR, \fB\-\-verbose\fR enable debug level verbosity. .TP \fB\-s\fR, \fB\-\-script\fR Run from a script (no user interface). .TP \fB\-\-host\fR HOST Specify a user@host for retrieving flows see Accessing Remote Hosts for more information. .TP \fB\-a\fR, \fB\-\-accumulate\fR Accumulate dump\-flow content. .TP \fB\-\-accumulate\-decay\fR ACCUMULATEDECAY Decay old accumulated flows. The default is 5 minutes. A value of 0 disables decay. .TP \fB\-d\fR DELAY, \fB\-\-delay\fR DELAY Delay in milliseconds to collect dump\-flow content (sample rate). openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-lib.in0000644000000000000000000000013213534540071017562 xustar0030 mtime=1567801401.945685165 30 atime=1567801402.137686576 30 ctime=1567801423.817846319 openvswitch-2.5.9/utilities/ovs-lib.in0000644000175000017500000002773413534540071021265 0ustar00jpettitjpettit00000000000000# This is a shell function library sourced by some Open vSwitch scripts. # It is not intended to be invoked on its own. # Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## ----------------- ## ## configure options ## ## ----------------- ## # All of these should be substituted by the Makefile at build time. logdir=${OVS_LOGDIR-'@LOGDIR@'} # /var/log/openvswitch rundir=${OVS_RUNDIR-'@RUNDIR@'} # /var/run/openvswitch sysconfdir=${OVS_SYSCONFDIR-'@sysconfdir@'} # /etc etcdir=$sysconfdir/openvswitch # /etc/openvswitch datadir=${OVS_PKGDATADIR-'@pkgdatadir@'} # /usr/share/openvswitch bindir=${OVS_BINDIR-'@bindir@'} # /usr/bin sbindir=${OVS_SBINDIR-'@sbindir@'} # /usr/sbin # /etc/openvswitch or /var/lib/openvswitch if test X"$OVS_DBDIR" != X; then dbdir=$OVS_DBDIR elif test X"$OVS_SYSCONFDIR" != X; then dbdir=$OVS_SYSCONFDIR/openvswitch else dbdir='@DBDIR@' fi ovs_ctl_log () { echo "$@" >> "${logdir}/ovs-ctl.log" } ovs_ctl () { case "$@" in *"=strace"*) # In case of running the daemon with strace, piping the o/p causes # the script to block (strace probably does not close the inherited # pipe). So, do not log the o/p to ovs-ctl.log. "${datadir}/scripts/ovs-ctl" "$@" ;; "status") # In case of the command 'status', we should return the exit status # of ovs-ctl. It is also useful to document the o/p in ovs-ctl.log. display=`"${datadir}/scripts/ovs-ctl" "$@" 2>&1` rc=$? if test -w "${logdir}/ovs-ctl.log"; then echo "${display}" | tee -a "${logdir}/ovs-ctl.log" else echo "${display}" fi return ${rc} ;; *) echo "`date -u`:$@" >> "${logdir}/ovs-ctl.log" "${datadir}/scripts/ovs-ctl" "$@" 2>&1 | tee -a "${logdir}/ovs-ctl.log" ;; esac } VERSION='@VERSION@' DAEMON_CWD=/ LC_ALL=C; export LC_ALL ## ------------- ## ## LSB functions ## ## ------------- ## # Use the system's own implementations if it has any. if test -e /etc/init.d/functions; then . /etc/init.d/functions elif test -e /etc/rc.d/init.d/functions; then . /etc/rc.d/init.d/functions elif test -e /lib/lsb/init-functions; then . /lib/lsb/init-functions fi # Implement missing functions (e.g. OpenSUSE lacks 'action'). if type log_success_msg >/dev/null 2>&1; then :; else log_success_msg () { printf '%s.\n' "$*" } fi if type log_failure_msg >/dev/null 2>&1; then :; else log_failure_msg () { printf '%s ... failed!\n' "$*" } fi if type log_warning_msg >/dev/null 2>&1; then :; else log_warning_msg () { printf '%s ... (warning).\n' "$*" } fi if type action >/dev/null 2>&1; then :; else action () { STRING=$1 shift "$@" rc=$? if test $rc = 0; then log_success_msg "$STRING" else log_failure_msg "$STRING" fi return $rc } fi ## ------- ## ## Daemons ## ## ------- ## pid_exists () { # This is better than "kill -0" because it doesn't require permission to # send a signal (so daemon_status in particular works as non-root). test -d /proc/"$1" } pid_comm_check () { [ "$1" = "`cat /proc/$2/comm`" ] } start_daemon () { priority=$1 wrapper=$2 shift; shift daemon=$1 strace="" # drop core files in a sensible place test -d "$DAEMON_CWD" || install -d -m 755 -o root -g root "$DAEMON_CWD" set "$@" --no-chdir cd "$DAEMON_CWD" # log file test -d "$logdir" || install -d -m 755 -o root -g root "$logdir" set "$@" --log-file="$logdir/$daemon.log" # pidfile and monitoring test -d "$rundir" || install -d -m 755 -o root -g root "$rundir" set "$@" --pidfile="$rundir/$daemon.pid" set "$@" --detach test X"$MONITOR" = Xno || set "$@" --monitor # wrapper case $wrapper in valgrind) if (valgrind --version) > /dev/null 2>&1; then set valgrind -q --leak-check=full --time-stamp=yes \ --log-file="$logdir/$daemon.valgrind.log.%p" "$@" else log_failure_msg "valgrind not installed, running $daemon without it" fi ;; strace) if (strace -V) > /dev/null 2>&1; then strace="strace -tt -T -s 256 -ff" if (strace -DV) > /dev/null 2>&1; then # Has the -D option. set $strace -D -o "$logdir/$daemon.strace.log" "$@" strace="" fi else log_failure_msg "strace not installed, running $daemon without it" fi ;; glibc) set env MALLOC_CHECK_=2 MALLOC_PERTURB_=165 "$@" ;; '') ;; *) log_failure_msg "unknown wrapper $wrapper, running $daemon without it" ;; esac # priority if test X"$priority" != X; then set nice -n "$priority" "$@" fi action "Starting $daemon" "$@" if test X"$strace" != X; then # Strace doesn't have the -D option so we attach after the fact. setsid $strace -o "$logdir/$daemon.strace.log" \ -p `cat $rundir/$daemon.pid` > /dev/null 2>&1 & fi } stop_daemon () { if test -e "$rundir/$1.pid"; then if pid=`cat "$rundir/$1.pid"`; then for action in EXIT .1 .25 .65 1 \ TERM .1 .25 .65 1 1 1 1 \ KILL 1 1 1 2 10 15 30 \ FAIL; do if pid_exists "$pid" >/dev/null 2>&1; then :; else return 0 fi case $action in EXIT) action "Exiting $1 ($pid)" \ ${bindir}/ovs-appctl -T 1 -t $rundir/$1.$pid.ctl exit ;; TERM) action "Killing $1 ($pid)" kill $pid ;; KILL) action "Killing $1 ($pid) with SIGKILL" kill -9 $pid ;; FAIL) log_failure_msg "Killing $1 ($pid) failed" return 1 ;; *) sleep $action ;; esac done fi fi log_success_msg "$1 is not running" } daemon_status () { pidfile=$rundir/$1.pid if test -e "$pidfile"; then if pid=`cat "$pidfile"`; then if pid_exists "$pid"; then echo "$1 is running with pid $pid" return 0 else echo "Pidfile for $1 ($pidfile) is stale" fi else echo "Pidfile for $1 ($pidfile) exists but cannot be read" fi else echo "$1 is not running" fi return 1 } daemon_is_running () { pidfile=$rundir/$1.pid test -e "$pidfile" && pid=`cat "$pidfile"` && pid_exists "$pid" && pid_comm_check $1 $pid } >/dev/null 2>&1 # Prints commands needed to move the ip address from interface $1 to interface # $2 move_ip_address () { if [ -z "$1" ] || [ -z "$2" ]; then return fi dev="$1" dst="$2" # IP addresses (including IPv6). echo "ip addr flush dev $dev 2>/dev/null" # Suppresses "Nothing to flush". ip addr show dev $dev | while read addr; do set -- $addr # Check and trim family. family=$1 shift case $family in inet | inet6) ;; *) continue ;; esac # Trim device off the end--"ip" insists on having "dev" precede it. addrcmd= while test $# != 0; do case $1 in dynamic) # Omit kernel-maintained route. continue 2 ;; scope) if test "$2" = link -a "$family" != inet6; then # Omit route derived from IP address, e.g. # 172.16.0.0/16 derived from 172.16.12.34, # but preserve IPv6 link-local address. continue 2 fi ;; "$dev"|"$dev:"*) # Address label string label=`echo $1 | sed "s/$dev/$dst/"` addrcmd="$addrcmd label $label" shift continue ;; esac addrcmd="$addrcmd $1" shift done if test "$1" != "$dev"; then addrcmd="$addrcmd $1" fi echo ip -f $family addr add $addrcmd dev $dst done } # Prints commands needed to move the ip route of interface $1 to interface $2 move_ip_routes () { if [ -z "$1" ] || [ -z "$2" ]; then return fi dev="$1" dst="$2" echo "ip route flush dev $dev proto boot 2>/dev/null" # Suppresses "Nothing to flush". ip route show dev $dev | while read route; do # "proto kernel" routes are installed by the kernel automatically. case $route in *" proto kernel "*) continue ;; esac echo "ip route add $route dev $dst" done } ovsdb_tool () { ovsdb-tool -vconsole:off "$@" } create_db () { DB_FILE="$1" DB_SCHEMA="$2" action "Creating empty database $DB_FILE" ovsdb_tool create "$DB_FILE" "$DB_SCHEMA" } upgrade_db () { DB_FILE="$1" DB_SCHEMA="$2" schemaver=`ovsdb_tool schema-version "$DB_SCHEMA"` if test ! -e "$DB_FILE"; then log_warning_msg "$DB_FILE does not exist" install -d -m 755 -o root -g root `dirname $DB_FILE` create_db "$DB_FILE" "$DB_SCHEMA" elif test X"`ovsdb_tool needs-conversion "$DB_FILE" "$DB_SCHEMA"`" != Xno; then # Back up the old version. version=`ovsdb_tool db-version "$DB_FILE"` cksum=`ovsdb_tool db-cksum "$DB_FILE" | awk '{print $1}'` backup=$DB_FILE.backup$version-$cksum action "Backing up database to $backup" cp "$DB_FILE" "$backup" || return 1 # Compact database. This is important if the old schema did not enable # garbage collection (i.e. if it did not have any tables with "isRoot": # true) but the new schema does. In that situation the old database # may contain a transaction that creates a record followed by a # transaction that creates the first use of the record. Replaying that # series of transactions against the new database schema (as "convert" # does) would cause the record to be dropped by the first transaction, # then the second transaction would cause a referential integrity # failure (for a strong reference). # # Errors might occur on an Open vSwitch downgrade if ovsdb-tool doesn't # understand some feature of the schema used in the OVSDB version that # we're downgrading from, so we don't give up on error. action "Compacting database" ovsdb_tool compact "$DB_FILE" # Upgrade or downgrade schema. if action "Converting database schema" ovsdb_tool convert "$DB_FILE" "$DB_SCHEMA"; then : else log_warning_msg "Schema conversion failed, using empty database instead" rm -f "$DB_FILE" create_db "$DB_FILE" "$DB_SCHEMA" fi fi } openvswitch-2.5.9/utilities/PaxHeaders.82075/ovs-vsctl-bashcomp.bash0000644000000000000000000000013213534540071022250 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801423.825846378 openvswitch-2.5.9/utilities/ovs-vsctl-bashcomp.bash0000755000175000017500000007005513534540071023750 0ustar00jpettitjpettit00000000000000SAVE_IFS=$IFS IFS=" " _OVSDB_SERVER_LOCATION="" # Run ovs-vsctl and make sure that ovs-vsctl is always called with # the correct --db argument. _ovs_vsctl () { local _db if [ -n "$_OVSDB_SERVER_LOCATION" ]; then _db="--db=$_OVSDB_SERVER_LOCATION" fi ovs-vsctl ${_db} "$@" } # ovs-vsctl --commands outputs in this format: # # main = ,, # localopts = ([] )* # localopt = --[^]]* # name = [^,]* # arguments = ((!argument|?argument|*argument|+argument) )* # argument = ([^ ]*|argument\|argument) # # The [] characters in local options are just delimiters. The # argument prefixes mean: # !argument :: The argument is required # ?argument :: The argument is optional # *argument :: The argument may appear any number (0 or more) times # +argument :: The argument may appear one or more times # A bar (|) character in an argument means thing before bar OR thing # after bar; for example, del-port can take a port or an interface. _OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)" # This doesn't complete on short arguments, so it filters them out. _OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \ | sed -e 's/\(.*\)=ARG/\1=/')" IFS=$SAVE_IFS declare -A _OVS_VSCTL_PARSED_ARGS declare -A _OVS_VSCTL_NEW_RECORDS # This is a convenience function to make sure that user input is # looked at as a fixed string when being compared to something. $1 is # the input; this behaves like 'grep "^$1"' but deals with regex # metacharacters in $1. _ovs_vsctl_check_startswith_string () { awk 'index($0, thearg)==1' thearg="$1" } # $1 = word to complete on. # Complete on global options. _ovs_vsctl_bashcomp_globalopt () { local options result options="" result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \ | _ovs_vsctl_check_startswith_string "${1%=*}") if [[ $result =~ "=" ]]; then options="NOSPACE" fi printf -- "${options}\nEO\n${result}" } # $1 = word to complete on. # Complete on local options. _ovs_vsctl_bashcomp_localopt () { local options result possible_opts possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 -d',') # This finds all options that could go together with the # already-seen ones for prefix_arg in $1; do possible_opts=$(printf "%s\n" "$possible_opts" \ | grep -- "\[${prefix_arg%%=*}=\?\]") done result=$(printf "%s\n" "${possible_opts}" \ | tr ' ' '\n' | tr -s '\n' | sort | uniq) # This removes the already-seen options from the list so that # users aren't completed for the same option twice. for prefix_arg in $1; do result=$(printf "%s\n" "${result}" \ | grep -v -- "\[${prefix_arg%%=*}=\?\]") done result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \ | _ovs_vsctl_check_startswith_string "$2") if [[ $result =~ "=" ]]; then options="NOSPACE" fi printf -- "${options}\nEO\n${result}" } # $1 = given local options. # $2 = word to complete on. # Complete on command that could contain the given local options. _ovs_vsctl_bashcomp_command () { local result possible_cmds possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}") for prefix_arg in $1; do possible_cmds=$(printf "%s\n" "$possible_cmds" \ | grep -- "\[$prefix_arg=\?\]") done result=$(printf "%s\n" "${possible_cmds}" \ | cut -f2 -d',' \ | _ovs_vsctl_check_startswith_string "$2") printf -- "${result}" } # $1 = completion result to check. # Return 0 if the completion result is non-empty, otherwise return 1. _ovs_vsctl_detect_nonzero_completions () { local tmp newarg newarg=${1#*EO} readarray tmp <<< "$newarg" if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then return 1 fi return 0 } # $1 = argument format to expand. # Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'. _ovs_vsctl_expand_command () { result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \ | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \ | awk '/\+.*/ { name=substr($0,2); print "!"name; print "*"name; next; } 1') printf -- "${result}\n!--" } # $1 = word to complete on. # Complete on table. _ovs_vsctl_complete_table () { local result result=$(ovsdb-client --no-heading list-tables $_OVSDB_SERVER_LOCATION Open_vSwitch \ | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on record. Provide both the name and uuid. _ovs_vsctl_complete_record () { local table uuids names new_record table="${_OVS_VSCTL_PARSED_ARGS[TABLE]}" new_record="${_OVS_VSCTL_NEW_RECORDS[${table^^}]}" # Tables should always have an _uuid column uuids=$(_ovs_vsctl --no-heading -f table -d bare --columns=_uuid \ list $table | _ovs_vsctl_check_startswith_string "$1") # Names don't always exist, silently ignore if the name column is # unavailable. names=$(_ovs_vsctl --no-heading -f table -d bare \ --columns=name list $table \ 2>/dev/null \ | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n%s\n%s\n" "${uuids}" "${names}" "${new_record}" } # $1 = word to complete on. # Complete on bridge. _ovs_vsctl_complete_bridge () { local result result=$(_ovs_vsctl list-br | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on port. If a bridge has already been specified, # just complete for that bridge. _ovs_vsctl_complete_port () { local ports result if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then ports=$(_ovs_vsctl list-ports "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") else local all_ports all_ports=$(_ovs_vsctl --format=table \ --no-headings \ --columns=name \ list Port) ports=$(printf "$all_ports" | tr -d '" ' | sort -u) fi result=$(_ovs_vsctl_check_startswith_string "$1" <<< "$ports") printf -- "EO\n%s\n" "${result}" } # $1: Atom to complete (as usual) # $2: Table to complete the key in # $3: Column to find keys in # $4: Prefix for each completion # Complete on key based on given table and column info. _ovs_vsctl_complete_key_given_table_column () { local keys keys=$(_ovs_vsctl --no-heading --columns="$3" list \ "$2" \ | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 \ | xargs printf "$4%s\n" | _ovs_vsctl_check_startswith_string "$4$1") result="${keys}" printf -- "%s\n" "${result}" } # $1 = word to complete on. # Complete on key. __complete_key () { # KEY is used in both br-set-external-id/br-get-external id (in # which case it is implicitly a key in the external-id column) and # in remove, where it is a table key. This checks to see if table # is set (the remove scenario), and then decides what to do. local result if [ -n "${_OVS_VSCTL_PARSED_ARGS[TABLE]}" ]; then local column=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["COLUMN"]}) result=$(_ovs_vsctl_complete_key_given_table_column \ "$1" \ ${_OVS_VSCTL_PARSED_ARGS["TABLE"]} \ $column \ "") else result=$(_ovs_vsctl br-get-external-id \ ${_OVS_VSCTL_PARSED_ARGS["BRIDGE"]} \ | cut -d'=' -f1 | _ovs_vsctl_check_startswith_string "$1") fi printf -- "%s" "${result}" } # $1 = word to complete on. # Complete on key. _ovs_vsctl_complete_key () { # KEY is used in both br-set-external-id/br-get-external id (in # which case it is implicitly a key in the external-id column) and # in remove, where it is a table key. This checks to see if table # is set (the remove scenario), and then decides what to do. local result result="$(__complete_key $1)" # If result is empty, just use user input as result. if [ -z "$result" ]; then result=$1 fi printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on value. _ovs_vsctl_complete_value () { local result # Just use user input as result. result=$1 printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on key=value. _ovs_vsctl_complete_key_value () { local orig_completions new_completions orig_completions=$(__complete_key "$1") for completion in ${orig_completions#*EO}; do new_completions="${new_completions} ${completion}=" done # If 'new_completions' is empty, just use user input as result. if [ -z "$new_completions" ]; then new_completions=$1 fi printf -- "NOSPACE\nEO\n%s" "${new_completions}" } # $1 = word to complete on. # Complete on column. _ovs_vsctl_complete_column () { local columns result columns=$(ovsdb-client --no-headings list-columns $_OVSDB_SERVER_LOCATION \ Open_vSwitch ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) result=$(printf "%s\n" "${columns}" \ | tr -d ':' | cut -d' ' -f1 \ | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) printf -- "EO\n%s\n" "${result}" } # Extract all system interfaces. _ovs_vsctl_get_sys_intf () { local result case "$(uname -o)" in *Linux*) result=$(ip -o link 2>/dev/null | cut -d':' -f2 \ | sed -e 's/^ \(.*\)/\1/') ;; *) result=$(ifconfig -a -s 2>/dev/null | cut -f1 -d' ' | tail -n +2) ;; esac printf "%s\n" "${result}" } # $1 = word to complete on. # Complete on system interface. _ovs_vsctl_complete_sysiface () { local result result=$(_ovs_vsctl_get_sys_intf | _ovs_vsctl_check_startswith_string "$1") printf -- "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on interface. If a bridge has already been specified, # just complete for that bridge. _ovs_vsctl_complete_iface () { local result if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then result=$(_ovs_vsctl list-ifaces "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") else for bridge in $(_ovs_vsctl list-br); do local ifaces ifaces=$(_ovs_vsctl list-ifaces "${bridge}") result="${result} ${ifaces}" done fi printf "EO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on COLUMN?:KEY=VALUE. _ovs_vsctl_complete_column_optkey_value () { local result column key value completion column=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -d':' -f1) key=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -s -d':' -f2) # The tr -d '\n' <<< makes sure that there are no leading or # trailing accidental newlines. table=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) # This might also be called after add-port or add-bond; in those # cases, the table should implicitly be assumed to be "Port". # This is done by checking if a NEW- parameter has been # encountered and, if it has, using that type without the NEW- as # the table. if [ -z "$table" ]; then if [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-PORT"]} ] \ || [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-BOND-PORT"]} ]; then table="Port" fi fi if [ -z "$key" ]; then local columns=$(ovsdb-client --no-headings list-columns \ $_OVSDB_SERVER_LOCATION Open_vSwitch $table) result=$(printf "%s\n" "${columns}" \ | awk '/key.*value/ { print $1":"; next } { print $1; next }' \ | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) fi if [[ $1 =~ ":" ]]; then result=$(_ovs_vsctl_complete_key_given_table_column \ "$key" "$table" "$column" "$column:") fi # If result is empty, just use user input as result. if [ -z "$result" ]; then result=$1 fi printf -- "NOSPACE\nEO\n%s\n" "${result}" } # $1 = word to complete on. # Complete on filename. _ovs_vsctl_complete_filename () { local result result=$(compgen -o filenames -A file "$1") printf -- "EO\n%s\n" "${result}" } _ovs_vsctl_complete_bridge_fail_mode () { printf -- "EO\nstandalone\nsecure" } # $1 = word to complete on. # Complete on target. _ovs_vsctl_complete_target () { local result if [[ "$1" =~ ^p?u ]]; then local protocol pathname expansion_base result protocol=$(cut -d':' -f1 <<< "$1") pathname=$(cut -s -d':' -f2 <<< "$1") expansion_base=$(compgen -W "unix punix" "$protocol") expansion_base="$expansion_base:" result=$(compgen -o filenames -A file \ -P $expansion_base "${pathname}") printf -- "NOSPACE\nEO\n%s\n" "${result}" else printf -- "NOSPACE\nEO\nssl:\ntcp:\nunix:\npssl:\nptcp:\npunix:" fi } # Extract PS1 prompt. _ovs_vsctl_get_PS1 () { if [ "$test" = "true" ]; then printf -- "> " return; fi # Original inspiration from # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2, # but changed quite a lot to make it more robust. # Make sure the PS1 used doesn't include any of the special # strings used to identify the prompt myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")" # Export the current environment in case the prompt uses any vars="$(env | cut -d'=' -f1)" for var in $vars; do export $var; done funcs="$(declare -F | cut -d' ' -f3)" for func in $funcs; do export -f $func; done # Get the prompt v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')" v="${v##*# Begin prompt}" printf -- "$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')" } # Request a new value from user. Nothing to complete on. _ovs_vsctl_complete_new () { local two_word_type message result if [ ! "$1" = "--" ]; then two_word_type="${2/-/ }" message="\nEnter a ${two_word_type,,}:\n$(_ovs_vsctl_get_PS1)$COMP_LINE" if [ -n "$1" ]; then result="$1" fi printf -- "NOCOMP\nBM%sEM\nEO\n%s\n" "${message}" "${result}" fi } _ovs_vsctl_complete_dashdash () { printf -- "EO\n%s\n" "--" } # These functions are given two arguments: # # $1 is the word being completed # # $2 is the type of completion --- only currently useful for the # NEW-* functions. # # Note that the NEW-* functions actually are ``completed''; currently # the completions are just used to save the fact that they have # appeared for later use (i.e. implicit table calculation). # # The output is of the form EO, where EO stands # for end options. Currently available options are: # - NOSPACE: Do not add a space at the end of each completion # - NOCOMP: Do not complete, but store the output of the completion # func in _OVS_VSCTL_PARSED_ARGS for later usage. # - BMEM: Print the declare -A _OVS_VSCTL_ARG_COMPLETION_FUNCS=( ["TABLE"]=_ovs_vsctl_complete_table ["RECORD"]=_ovs_vsctl_complete_record ["BRIDGE"]=_ovs_vsctl_complete_bridge ["PARENT"]=_ovs_vsctl_complete_bridge ["PORT"]=_ovs_vsctl_complete_port ["KEY"]=_ovs_vsctl_complete_key ["VALUE"]=_ovs_vsctl_complete_value ["ARG"]=_ovs_vsctl_complete_value ["IFACE"]=_ovs_vsctl_complete_iface ["SYSIFACE"]=_ovs_vsctl_complete_sysiface ["COLUMN"]=_ovs_vsctl_complete_column ["COLUMN?:KEY"]=_ovs_vsctl_complete_column_optkey_value ["COLUMN?:KEY=VALUE"]=_ovs_vsctl_complete_column_optkey_value ["KEY=VALUE"]=_ovs_vsctl_complete_key_value ["?KEY=VALUE"]=_ovs_vsctl_complete_key_value ["PRIVATE-KEY"]=_ovs_vsctl_complete_filename ["CERTIFICATE"]=_ovs_vsctl_complete_filename ["CA-CERT"]=_ovs_vsctl_complete_filename ["MODE"]=_ovs_vsctl_complete_bridge_fail_mode ["TARGET"]=_ovs_vsctl_complete_target ["NEW-BRIDGE"]=_ovs_vsctl_complete_new ["NEW-PORT"]=_ovs_vsctl_complete_new ["NEW-BOND-PORT"]=_ovs_vsctl_complete_new ["NEW-VLAN"]=_ovs_vsctl_complete_new ["--"]=_ovs_vsctl_complete_dashdash ) # $1: Argument type, may include vertical bars to mean OR # $2: Beginning of completion # # Note that this checks for existance in # _OVS_VSCTL_ARG_COMPLETION_FUNCS; if the argument type ($1) is not # there it will fail gracefully. _ovs_vsctl_possible_completions_of_argument () { local possible_types completions tmp completions="EO" possible_types=$(printf "%s\n" "$1" | tr '|' '\n') for type in $possible_types; do if [ ${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} ]; then tmp=$(${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} \ "$2" "${type^^}") tmp_noEO="${tmp#*EO}" tmp_EO="${tmp%%EO*}" completions=$(printf "%s%s\n%s" "${tmp_EO}" \ "${completions}" "${tmp_noEO}") fi done printf "%s\n" "${completions}" } # $1 = List of argument types # $2 = current pointer into said list # $3 = word to complete on # Outputs list of possible completions # The return value is the index in the cmd_args($1) list that should # next be matched, if only one of them did, or 254 if there are no # matches, so it doesn't know what comes next. _ovs_vsctl_complete_argument() { local cmd_args arg expansion index new=$(printf "%s\n" "$1" | grep -- '.\+') readarray -t cmd_args <<< "$new"; arg=${cmd_args[$2]} case ${arg:0:1} in !) expansion=$(_ovs_vsctl_possible_completions_of_argument \ "${arg:1}" $3) index=$(($2+1)) ;; \?|\*) local tmp1 tmp2 arg2_index tmp2_noEO tmp2_EO tmp1=$(_ovs_vsctl_possible_completions_of_argument "${arg:1}" $3) tmp2=$(_ovs_vsctl_complete_argument "$1" "$(($2+1))" "$3") arg2_index=$? if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then if [ "${arg:0:1}" = "*" ]; then index=$2; else index=$(($2+1)); fi fi if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then if [ "${arg:0:1}" = "*" ]; then index=$2; else index=$(($2+1)); fi fi if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then index=$arg2_index fi if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then index=254 fi # Don't allow secondary completions to inhibit primary # completions: if [[ $tmp2 =~ ^([^E]|E[^O])*NOCOMP ]]; then tmp2="" fi tmp2_noEO="${tmp2#*EO}" tmp2_EO="${tmp2%%EO*}" expansion=$(printf "%s%s\n%s" "${tmp2_EO}" \ "${tmp1}" "${tmp2_noEO}") ;; esac printf "%s\n" "$expansion" return $index } _ovs_vsctl_detect_nospace () { if [[ $1 =~ ^([^E]|E[^O])*NOSPACE ]]; then _OVS_VSCTL_COMP_NOSPACE=true fi } _ovs_vsctl_process_messages () { local message message="${1#*BM}" message="${message%%EM*}" if [ "$test" = "true" ]; then printf -- "--- BEGIN MESSAGE" fi printf "${message}" if [ "$test" = "true" ]; then printf -- "--- END MESSAGE" fi } # colon, equal sign will mess up the completion output, just # removes the colon-word and equal-word prefix from COMPREPLY items. # # Implementation of this function refers to the __ltrim_colon_completions # function defined in bash_completion module. # # $1: Current argument # $2: $COMP_WORDBREAKS # $3: ${COMPREPLY[@]} _ovs_vsctl_trim_compreply() { local cur comp_wordbreaks local compreply cur=$1 && shift comp_wordbreaks=$1 && shift compreply=( $@ ) if [[ "$cur" == *:* && "$comp_wordbreaks" == *:* ]]; then local colon_word=${cur%${cur##*:}} local i=${#compreply[*]} cur=${cur##*:} while [ $((--i)) -ge 0 ]; do compreply[$i]=${compreply[$i]#"$colon_word"} done fi if [[ "$cur" == *=* && "$comp_wordbreaks" == *=* ]]; then local equal_word=${cur%${cur##*=}} local i=${#compreply[*]} while [ $((--i)) -ge 0 ]; do compreply[$i]=${compreply[$i]#"$equal_word"} done fi printf "%s " "${compreply[@]}" } # The general strategy here is that the same functions that decide # completions can also capture the necessary context for later # completions. This means that there is no distinction between the # processing for words that are not the current word and words that # are the current word. # # Parsing up until the command word happens starts with everything # valid; as the syntax order of ovs-vsctl is fairly strict, when types # of words that preclude other words from happending can turn them # off; this is controlled by valid_globals, valid_opts, and # valid_commands. given_opts is used to narrow down which commands # are valid based on the previously given options. # # After the command has been detected, the parsing becomes more # complicated. The cmd_pos variable is set to 0 when the command is # detected; it is used as a pointer into an array of the argument # types for that given command. The argument types are stored in both # cmd_args and raw_cmd as the main loop uses properties of arrays to # detect certain conditions, but arrays cannot be passed to functions. # To be able to deal with optional or repeatable arguments, the exit # status of the function _ovs_vsctl_complete_argument represents where # it has determined that the next argument will be. _ovs_vsctl_bashcomp () { local words cword valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts local test="false" # Does not support BASH_VERSION < 4.0 if [ ${BASH_VERSINFO[0]} -lt 4 ]; then return 0 fi # Prepare the COMP_* variables based on input. if [ "$1" = "test" ]; then test="true" export COMP_LINE="ovs-vsctl $2" tmp="ovs-vsctl"$'\n'"$(tr ' ' '\n' <<< "${COMP_LINE}x")" tmp="${tmp%x}" readarray -t COMP_WORDS \ <<< "$tmp" export COMP_WORDS export COMP_CWORD="$((${#COMP_WORDS[@]}-1))" else # If not in test mode, reassembles the COMP_WORDS and COMP_CWORD # using just space as word break. _get_comp_words_by_ref -n "\"'><=;|&(:" -w words -i cword COMP_WORDS=( "${words[@]}" ) COMP_CWORD=${cword} fi # Extract the conf.db path. db=$(sed -n 's/.*--db=\([^ ]*\).*/\1/p' <<< "$COMP_LINE") if [ -n "$db" ]; then _OVSDB_SERVER_LOCATION="$db" fi # If having trouble accessing the database, return. if ! _ovs_vsctl get-manager 1>/dev/null 2>/dev/null; then return 1; fi _OVS_VSCTL_PARSED_ARGS=() _OVS_VSCTL_NEW_RECORDS=() cmd_pos=-1 valid_globals=true valid_opts=true valid_commands=true given_opts="" index=1 for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do _OVS_VSCTL_COMP_NOSPACE=false local completion completion="" if [ $cmd_pos -gt -1 ]; then local tmp tmp_noop arg possible_newindex tmp=$(_ovs_vsctl_complete_argument "$raw_cmd" "$cmd_pos" "$word") possible_newindex=$? # Check for nospace. _ovs_vsctl_detect_nospace $tmp # Remove all options. tmp_noop="${tmp#*EO}" # Allow commands to specify that they should not be # completed if ! [[ $tmp =~ ^([^E]|E[^O])*NOCOMP ]]; then # Directly assignment, since 'completion' is guaranteed to # to be empty. completion="$tmp_noop" # If intermediate completion is empty, it means that the current # argument is invalid. And we should not continue. if [ $index -lt $COMP_CWORD ] \ && (! _ovs_vsctl_detect_nonzero_completions "$completion"); then _ovs_vsctl_process_messages "BM\nCannot complete \'${COMP_WORDS[$index]}\' at index ${index}:\n$(_ovs_vsctl_get_PS1)${COMP_LINE}EM\nEO\n" return 1 fi else # Only allow messages when there is no completion # printout and when on the current word. if [ $index -eq $COMP_CWORD ]; then _ovs_vsctl_process_messages "${tmp}" fi # Append the new record to _OVS_VSCTL_NEW_RECORDS. _OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]="${_OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]} $tmp_noop" fi if [[ $cmd_pos -lt ${#cmd_args} ]]; then _OVS_VSCTL_PARSED_ARGS["${cmd_args[$cmd_pos]:1}"]=$word fi if [ $possible_newindex -lt 254 ]; then cmd_pos=$possible_newindex fi fi if [ $valid_globals == true ]; then tmp=$(_ovs_vsctl_bashcomp_globalopt $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" fi if [ $valid_opts == true ]; then tmp=$(_ovs_vsctl_bashcomp_localopt "$given_opts" $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" if [ $index -lt $COMP_CWORD ] \ && _ovs_vsctl_detect_nonzero_completions "$tmp"; then valid_globals=false given_opts="${given_opts} ${word}" fi fi if [ $valid_commands = true ]; then tmp=$(_ovs_vsctl_bashcomp_command "$given_opts" $word) _ovs_vsctl_detect_nospace $tmp completion="${completion} ${tmp#*EO}" if [ $index -lt $COMP_CWORD ] \ && _ovs_vsctl_detect_nonzero_completions "$tmp"; then valid_globals=false valid_opts=false valid_commands=false cmd_pos=0 raw_cmd=$(_ovs_vsctl_expand_command "$word") readarray -t cmd_args <<< "$raw_cmd" fi fi if [ "$word" = "--" ] && [ $index -lt $COMP_CWORD ]; then # Empty the parsed args array. _OVS_VSCTL_PARSED_AGS=() cmd_pos=-1 # No longer allow global options after '--'. valid_globals=false valid_opts=true valid_commands=true given_opts="" fi completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")" if [ $index -eq $COMP_CWORD ]; then if [ "$test" = "true" ]; then completion="$(_ovs_vsctl_trim_compreply "$word" ":=" ${completion} | \ tr ' ' '\n')" if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then printf "%s" "$completion" | sed -e '/^$/d' else printf "%s" "$completion" | sed -e '/^$/d; s/$/ /g' fi printf "\n" else if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then compopt -o nospace COMPREPLY=( $(compgen -W "${completion}" -- $word) ) else compopt +o nospace COMPREPLY=( $(compgen -W "${completion}" -- $word) ) fi COMPREPLY=( $(_ovs_vsctl_trim_compreply "$word" \ "${COMP_WORDBREAKS}" ${COMPREPLY[@]}) ) fi fi index=$(($index+1)) done } if [ "$1" = "test" ]; then _ovs_vsctl_bashcomp "$@" else complete -F _ovs_vsctl_bashcomp ovs-vsctl fi openvswitch-2.5.9/PaxHeaders.82075/Documentation0000644000000000000000000000013213534540120016373 xustar0030 mtime=1567801424.593852041 30 atime=1567801425.625859648 30 ctime=1567801424.593852041 openvswitch-2.5.9/Documentation/0000755000175000017500000000000013534540120020136 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/Documentation/PaxHeaders.82075/group-selection-method-property.txt0000644000000000000000000000013213534540071025475 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801423.785846083 openvswitch-2.5.9/Documentation/group-selection-method-property.txt0000644000175000017500000001142713534540071027170 0ustar00jpettitjpettit00000000000000Proposal for Group Selection Method Property Version: 0.0.3 Author: Simon Horman , et al. Initial Public Revision: September 2014 Contents ======== 1. Introduction 2. How it Works 3. Experimenter Id 4. Experimenter Messages 5. History 1. Introduction =============== This text describes a Netronome Extension to (draft) OpenFlow 1.5 that allows a controller to provide more information on the selection method for select groups. This proposal is in the form of an enhanced select group type. This may subsequently be proposed as an extension or update to the OpenFlow specification. 2. How it works =============== A new Netronome group experimenter property is defined which provides compatibility with the group mod message defined in draft Open Flow 1.5 (also known as ONF EXT-350) and allows parameters for the selection method of select groups to be passed by the controller. In particular it allows controllers to: * Specify the fields used for bucket selection by the select group. * Designate the selection method used. * Provide a non-field parameter to the selection method. 3. Experimenter ID ================== The Experimenter ID of this extension is: NTR_VENDOR_ID = 0x0000154d 4. Group Experimenter Property ============================== The following group property experimenter type defined by this extension. enum ntr_group_mod_subtype { NTRT_SELECTION_METHOD = 1, }; Modifications to the group table from the controller may be done with a OFPT_GROUP_MOD message described (draft) Open Flow 1.5. Group Entry Message. Of relevance here is that (draft) Open Flow 1.5 group messages have properties. This proposal is defined in terms of an implementation of struct ofp_group_prop_experimenter which is described in (draft) Open Flow 1.5. The implementation is: struct ntr_group_prop_selection_method { ovs_be16 type; /* OFPGPT_EXPERIMENTER. */ ovs_be16 length; /* Length in bytes of this property. */ ovs_be32 experimenter; /* NTR_VENDOR_ID. */ ovs_be32 exp_type; /* NTRT_SELECTION_METHOD. */ ovs_be32 pad; char selection_method[NTR_MAX_SELECTION_METHOD_LEN]; /* Null-terminated */ ovs_be64 selection_method_param; /* Non-Field parameter for * bucket selection. */ /* Followed by: * - Exactly (length - 40) (possibly 0) bytes containing OXM TLVs, then * - Exactly ((length + 7)/8*8 - length) (between 0 and 7) bytes of * all-zero bytes * In summary, ntr_group_prop_selection_method is padded as needed, * to make its overall size a multiple of 8, to preserve alignment * in structures using it. */ /* uint8_t field_array[0]; */ /* Zero or more fields encoded as * OXM TLVs where the has_mask bit must * be zero and the value it specifies is * a mask to apply to packet fields and * then input them to the selection * method of a select group. */ /* uint8_t pad2[0]; */ }; OFP_ASSERT(sizeof(struct ntr_group_mod) == 40); This property may only be used with group mod messages whose: * command is OFPGC_ADD or OFPGC_MODIFY; and * type is OFPGT_SELECT The type field is the OFPGPT_EXPERIMENTER which is defined in EXT-350 as 0xffff. The experimenter field is the Experimenter ID (see 3). The exp_type field is NTRT_SELECTION_METHOD. The group selection_method is a null-terminated string which if non-zero length specifies a selection method known to an underlying layer of the switch. The value of NTR_MAX_SELECTION_METHOD_LEN is 16. The group selection_method may be zero-length to request compatibility with Open Flow 1.4. The selection_method_param provides a non-field parameter for the group selection_method. It must be all-zeros unless the group selection_method is non-zero length. The selection_method_param may for example be used as an initial value for the hash of a hash group selection method. The fields field is an ofp_match structure which includes the fields which should be used as inputs to bucket selection. ofp_match is described in Open Flow 1.4 section 7.2.2 Flow Match Structures. Fields must not be specified unless the group selection_method is non-zero length. The pre-requisites for fields specified must be satisfied in the match for any flow that uses the group. Masking is allowed but not required for fields whose TLVs allow masking. The fields may for example be used as the fields that are hashed by a hash group selection method. 5. History ========== This proposal has been developed independently of any similar work in this area. No such work is known. openvswitch-2.5.9/Documentation/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071020614 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801424.593852041 openvswitch-2.5.9/Documentation/automake.mk0000644000175000017500000000010313534540071022274 0ustar00jpettitjpettit00000000000000EXTRA_DIST += \ Documentation/group-selection-method-property.txt openvswitch-2.5.9/PaxHeaders.82075/DESIGN.md0000644000000000000000000000013213534540071015137 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801423.661845169 openvswitch-2.5.9/DESIGN.md0000644000175000017500000013416613534540071016640 0ustar00jpettitjpettit00000000000000Design Decisions In Open vSwitch ================================ This document describes design decisions that went into implementing Open vSwitch. While we believe these to be reasonable decisions, it is impossible to predict how Open vSwitch will be used in all environments. Understanding assumptions made by Open vSwitch is critical to a successful deployment. The end of this document contains contact information that can be used to let us know how we can make Open vSwitch more generally useful. Asynchronous Messages ===================== Over time, Open vSwitch has added many knobs that control whether a given controller receives OpenFlow asynchronous messages. This section describes how all of these features interact. First, a service controller never receives any asynchronous messages unless it changes its miss_send_len from the service controller default of zero in one of the following ways: - Sending an OFPT_SET_CONFIG message with nonzero miss_send_len. - Sending any NXT_SET_ASYNC_CONFIG message: as a side effect, this message changes the miss_send_len to OFP_DEFAULT_MISS_SEND_LEN (128) for service controllers. Second, OFPT_FLOW_REMOVED and NXT_FLOW_REMOVED messages are generated only if the flow that was removed had the OFPFF_SEND_FLOW_REM flag set. Third, OFPT_PACKET_IN and NXT_PACKET_IN messages are sent only to OpenFlow controller connections that have the correct connection ID (see "struct nx_controller_id" and "struct nx_action_controller"): - For packet-in messages generated by a NXAST_CONTROLLER action, the controller ID specified in the action. - For other packet-in messages, controller ID zero. (This is the default ID when an OpenFlow controller does not configure one.) Finally, Open vSwitch consults a per-connection table indexed by the message type, reason code, and current role. The following table shows how this table is initialized by default when an OpenFlow connection is made. An entry labeled "yes" means that the message is sent, an entry labeled "---" means that the message is suppressed. ``` master/ message and reason code other slave ---------------------------------------- ------- ----- OFPT_PACKET_IN / NXT_PACKET_IN OFPR_NO_MATCH yes --- OFPR_ACTION yes --- OFPR_INVALID_TTL --- --- OFPR_ACTION_SET (OF1.4+) yes --- OFPR_GROUP (OF1.4+) yes --- OFPT_FLOW_REMOVED / NXT_FLOW_REMOVED OFPRR_IDLE_TIMEOUT yes --- OFPRR_HARD_TIMEOUT yes --- OFPRR_DELETE yes --- OFPRR_GROUP_DELETE (OF1.4+) yes --- OFPRR_METER_DELETE (OF1.4+) yes --- OFPRR_EVICTION (OF1.4+) yes --- OFPT_PORT_STATUS OFPPR_ADD yes yes OFPPR_DELETE yes yes OFPPR_MODIFY yes yes OFPT_ROLE_REQUEST / OFPT_ROLE_REPLY (OF1.4+) OFPCRR_MASTER_REQUEST --- --- OFPCRR_CONFIG --- --- OFPCRR_EXPERIMENTER --- --- OFPT_TABLE_STATUS (OF1.4+) OFPTR_VACANCY_DOWN --- --- OFPTR_VACANCY_UP --- --- OFPT_REQUESTFORWARD (OF1.4+) OFPRFR_GROUP_MOD --- --- OFPRFR_METER_MOD --- --- ``` The NXT_SET_ASYNC_CONFIG message directly sets all of the values in this table for the current connection. The OFPC_INVALID_TTL_TO_CONTROLLER bit in the OFPT_SET_CONFIG message controls the setting for OFPR_INVALID_TTL for the "master" role. OFPAT_ENQUEUE ============= The OpenFlow 1.0 specification requires the output port of the OFPAT_ENQUEUE action to "refer to a valid physical port (i.e. < OFPP_MAX) or OFPP_IN_PORT". Although OFPP_LOCAL is not less than OFPP_MAX, it is an 'internal' port which can have QoS applied to it in Linux. Since we allow the OFPAT_ENQUEUE to apply to 'internal' ports whose port numbers are less than OFPP_MAX, we interpret OFPP_LOCAL as a physical port and support OFPAT_ENQUEUE on it as well. OFPT_FLOW_MOD ============= The OpenFlow specification for the behavior of OFPT_FLOW_MOD is confusing. The following tables summarize the Open vSwitch implementation of its behavior in the following categories: - "match on priority": Whether the flow_mod acts only on flows whose priority matches that included in the flow_mod message. - "match on out_port": Whether the flow_mod acts only on flows that output to the out_port included in the flow_mod message (if out_port is not OFPP_NONE). OpenFlow 1.1 and later have a similar feature (not listed separately here) for out_group. - "match on flow_cookie": Whether the flow_mod acts only on flows whose flow_cookie matches an optional controller-specified value and mask. - "updates flow_cookie": Whether the flow_mod changes the flow_cookie of the flow or flows that it matches to the flow_cookie included in the flow_mod message. - "updates OFPFF_ flags": Whether the flow_mod changes the OFPFF_SEND_FLOW_REM flag of the flow or flows that it matches to the setting included in the flags of the flow_mod message. - "honors OFPFF_CHECK_OVERLAP": Whether the OFPFF_CHECK_OVERLAP flag in the flow_mod is significant. - "updates idle_timeout" and "updates hard_timeout": Whether the idle_timeout and hard_timeout in the flow_mod, respectively, have an effect on the flow or flows matched by the flow_mod. - "updates idle timer": Whether the flow_mod resets the per-flow timer that measures how long a flow has been idle. - "updates hard timer": Whether the flow_mod resets the per-flow timer that measures how long it has been since a flow was modified. - "zeros counters": Whether the flow_mod resets per-flow packet and byte counters to zero. - "may add a new flow": Whether the flow_mod may add a new flow to the flow table. (Obviously this is always true for "add" commands but in some OpenFlow versions "modify" and "modify-strict" can also add new flows.) - "sends flow_removed message": Whether the flow_mod generates a flow_removed message for the flow or flows that it affects. An entry labeled "yes" means that the flow mod type does have the indicated behavior, "---" means that it does not, an empty cell means that the property is not applicable, and other values are explained below the table. OpenFlow 1.0 ------------ ``` MODIFY DELETE ADD MODIFY STRICT DELETE STRICT === ====== ====== ====== ====== match on priority yes --- yes --- yes match on out_port --- --- --- yes yes match on flow_cookie --- --- --- --- --- match on table_id --- --- --- --- --- controller chooses table_id --- --- --- updates flow_cookie yes yes yes updates OFPFF_SEND_FLOW_REM yes + + honors OFPFF_CHECK_OVERLAP yes + + updates idle_timeout yes + + updates hard_timeout yes + + resets idle timer yes + + resets hard timer yes yes yes zeros counters yes + + may add a new flow yes yes yes sends flow_removed message --- --- --- % % (+) "modify" and "modify-strict" only take these actions when they create a new flow, not when they update an existing flow. (%) "delete" and "delete_strict" generates a flow_removed message if the deleted flow or flows have the OFPFF_SEND_FLOW_REM flag set. (Each controller can separately control whether it wants to receive the generated messages.) ``` OpenFlow 1.1 ------------ OpenFlow 1.1 makes these changes: - The controller now must specify the table_id of the flow match searched and into which a flow may be inserted. Behavior for a table_id of 255 is undefined. - A flow_mod, except an "add", can now match on the flow_cookie. - When a flow_mod matches on the flow_cookie, "modify" and "modify-strict" never insert a new flow. ``` MODIFY DELETE ADD MODIFY STRICT DELETE STRICT === ====== ====== ====== ====== match on priority yes --- yes --- yes match on out_port --- --- --- yes yes match on flow_cookie --- yes yes yes yes match on table_id yes yes yes yes yes controller chooses table_id yes yes yes updates flow_cookie yes --- --- updates OFPFF_SEND_FLOW_REM yes + + honors OFPFF_CHECK_OVERLAP yes + + updates idle_timeout yes + + updates hard_timeout yes + + resets idle timer yes + + resets hard timer yes yes yes zeros counters yes + + may add a new flow yes # # sends flow_removed message --- --- --- % % (+) "modify" and "modify-strict" only take these actions when they create a new flow, not when they update an existing flow. (%) "delete" and "delete_strict" generates a flow_removed message if the deleted flow or flows have the OFPFF_SEND_FLOW_REM flag set. (Each controller can separately control whether it wants to receive the generated messages.) (#) "modify" and "modify-strict" only add a new flow if the flow_mod does not match on any bits of the flow cookie ``` OpenFlow 1.2 ------------ OpenFlow 1.2 makes these changes: - Only "add" commands ever add flows, "modify" and "modify-strict" never do. - A new flag OFPFF_RESET_COUNTS now controls whether "modify" and "modify-strict" reset counters, whereas previously they never reset counters (except when they inserted a new flow). ``` MODIFY DELETE ADD MODIFY STRICT DELETE STRICT === ====== ====== ====== ====== match on priority yes --- yes --- yes match on out_port --- --- --- yes yes match on flow_cookie --- yes yes yes yes match on table_id yes yes yes yes yes controller chooses table_id yes yes yes updates flow_cookie yes --- --- updates OFPFF_SEND_FLOW_REM yes --- --- honors OFPFF_CHECK_OVERLAP yes --- --- updates idle_timeout yes --- --- updates hard_timeout yes --- --- resets idle timer yes --- --- resets hard timer yes yes yes zeros counters yes & & may add a new flow yes --- --- sends flow_removed message --- --- --- % % (%) "delete" and "delete_strict" generates a flow_removed message if the deleted flow or flows have the OFPFF_SEND_FLOW_REM flag set. (Each controller can separately control whether it wants to receive the generated messages.) (&) "modify" and "modify-strict" reset counters if the OFPFF_RESET_COUNTS flag is specified. ``` OpenFlow 1.3 ------------ OpenFlow 1.3 makes these changes: - Behavior for a table_id of 255 is now defined, for "delete" and "delete-strict" commands, as meaning to delete from all tables. A table_id of 255 is now explicitly invalid for other commands. - New flags OFPFF_NO_PKT_COUNTS and OFPFF_NO_BYT_COUNTS for "add" operations. The table for 1.3 is the same as the one shown above for 1.2. OpenFlow 1.4 ----------- OpenFlow 1.4 makes these changes: - Adds the "importance" field to flow_mods, but it does not explicitly specify which kinds of flow_mods set the importance. For consistency, Open vSwitch uses the same rule for importance as for idle_timeout and hard_timeout, that is, only an "ADD" flow_mod sets the importance. (This issue has been filed with the ONF as EXT-496.) - Eviction Mechanism to automatically delete entries of lower importance to make space for newer entries. OpenFlow 1.4 Bundles ==================== Open vSwitch makes all flow table modifications atomically, i.e., any datapath packet only sees flow table configurations either before or after any change made by any flow_mod. For example, if a controller removes all flows with a single OpenFlow "flow_mod", no packet sees an intermediate version of the OpenFlow pipeline where only some of the flows have been deleted. It should be noted that Open vSwitch caches datapath flows, and that the cached flows are NOT flushed immediately when a flow table changes. Instead, the datapath flows are revalidated against the new flow table as soon as possible, and usually within one second of the modification. This design amortizes the cost of datapath cache flushing across multiple flow table changes, and has a significant performance effect during simultaneous heavy flow table churn and high traffic load. This means that different cached datapath flows may have been computed based on a different flow table configurations, but each of the datapath flows is guaranteed to have been computed over a coherent view of the flow tables, as described above. With OpenFlow 1.4 bundles this atomicity can be extended across an arbitrary set of flow_mods. Bundles are supported for flow_mod and port_mod messages only. For flow_mods, both 'atomic' and 'ordered' bundle flags are trivially supported, as all bundled messages are executed in the order they were added and all flow table modifications are now atomic to the datapath. Port mods may not appear in atomic bundles, as port status modifications are not atomic. To support bundles, ovs-ofctl has a '--bundle' option that makes the flow mod commands ('add-flow', 'add-flows', 'mod-flows', 'del-flows', and 'replace-flows') use an OpenFlow 1.4 bundle to operate the modifications as a single atomic transaction. If any of the flow mods in a transaction fail, none of them are executed. All flow mods in a bundle appear to datapath lookups simultaneously. Furthermore, ovs-ofctl 'add-flow' and 'add-flows' commands now accept arbitrary flow mods as an input by allowing the flow specification to start with an explicit 'add', 'modify', 'modify_strict', 'delete', or 'delete_strict' keyword. A missing keyword is treated as 'add', so this is fully backwards compatible. With the new '--bundle' option all the flow mods are executed as a single atomic transaction using an OpenFlow 1.4 bundle. Without the '--bundle' option the flow mods are executed in order up to the first failing flow_mod, and in case of an error the earlier successful flow_mods are not rolled back. OFPT_PACKET_IN ============== The OpenFlow 1.1 specification for OFPT_PACKET_IN is confusing. The definition in OF1.1 openflow.h is[*]: ``` /* Packet received on port (datapath -> controller). */ struct ofp_packet_in { struct ofp_header header; uint32_t buffer_id; /* ID assigned by datapath. */ uint32_t in_port; /* Port on which frame was received. */ uint32_t in_phy_port; /* Physical Port on which frame was received. */ uint16_t total_len; /* Full length of frame. */ uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t table_id; /* ID of the table that was looked up */ uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word, so the IP header is 32-bit aligned. The amount of data is inferred from the length field in the header. Because of padding, offsetof(struct ofp_packet_in, data) == sizeof(struct ofp_packet_in) - 2. */ }; OFP_ASSERT(sizeof(struct ofp_packet_in) == 24); ``` The confusing part is the comment on the data[] member. This comment is a leftover from OF1.0 openflow.h, in which the comment was correct: sizeof(struct ofp_packet_in) is 20 in OF1.0 and offsetof(struct ofp_packet_in, data) is 18. When OF1.1 was written, the structure members were changed but the comment was carelessly not updated, and the comment became wrong: sizeof(struct ofp_packet_in) and offsetof(struct ofp_packet_in, data) are both 24 in OF1.1. That leaves the question of how to implement ofp_packet_in in OF1.1. The OpenFlow reference implementation for OF1.1 does not include any padding, that is, the first byte of the encapsulated frame immediately follows the 'table_id' member without a gap. Open vSwitch therefore implements it the same way for compatibility. For an earlier discussion, please see the thread archived at: https://mailman.stanford.edu/pipermail/openflow-discuss/2011-August/002604.html [*] The quoted definition is directly from OF1.1. Definitions used inside OVS omit the 8-byte ofp_header members, so the sizes in this discussion are 8 bytes larger than those declared in OVS header files. VLAN Matching ============= The 802.1Q VLAN header causes more trouble than any other 4 bytes in networking. More specifically, three versions of OpenFlow and Open vSwitch have among them four different ways to match the contents and presence of the VLAN header. The following table describes how each version works. Match NXM OF1.0 OF1.1 OF1.2 ----- --------- ----------- ----------- ------------ [1] 0000/0000 ????/1,??/? ????/1,??/? 0000/0000,-- [2] 0000/ffff ffff/0,??/? ffff/0,??/? 0000/ffff,-- [3] 1xxx/1fff 0xxx/0,??/1 0xxx/0,??/1 1xxx/ffff,-- [4] z000/f000 ????/1,0y/0 fffe/0,0y/0 1000/1000,0y [5] zxxx/ffff 0xxx/0,0y/0 0xxx/0,0y/0 1xxx/ffff,0y [6] 0000/0fff [7] 0000/f000 [8] 0000/efff [9] 1001/1001 1001/1001,-- [10] 3000/3000 Each column is interpreted as follows. - Match: See the list below. - NXM: xxxx/yyyy means NXM_OF_VLAN_TCI_W with value xxxx and mask yyyy. A mask of 0000 is equivalent to omitting NXM_OF_VLAN_TCI(_W), a mask of ffff is equivalent to NXM_OF_VLAN_TCI. - OF1.0 and OF1.1: wwww/x,yy/z means dl_vlan wwww, OFPFW_DL_VLAN x, dl_vlan_pcp yy, and OFPFW_DL_VLAN_PCP z. If OFPFW_DL_VLAN or OFPFW_DL_VLAN_PCP is 1, the corresponding field value is wildcarded, otherwise it is matched. ? means that the given bits are ignored (their conventional values are 0000/x,00/0 in OF1.0, 0000/x,00/1 in OF1.1; x is never ignored). means that the given match is not supported. - OF1.2: xxxx/yyyy,zz means OXM_OF_VLAN_VID_W with value xxxx and mask yyyy, and OXM_OF_VLAN_PCP (which is not maskable) with value zz. A mask of 0000 is equivalent to omitting OXM_OF_VLAN_VID(_W), a mask of ffff is equivalent to OXM_OF_VLAN_VID. -- means that OXM_OF_VLAN_PCP is omitted. means that the given match is not supported. The matches are: [1] Matches any packet, that is, one without an 802.1Q header or with an 802.1Q header with any TCI value. [2] Matches only packets without an 802.1Q header. NXM: Any match with (vlan_tci == 0) and (vlan_tci_mask & 0x1000) != 0 is equivalent to the one listed in the table. OF1.0: The spec doesn't define behavior if dl_vlan is set to 0xffff and OFPFW_DL_VLAN_PCP is not set. OF1.1: The spec says explicitly to ignore dl_vlan_pcp when dl_vlan is set to 0xffff. OF1.2: The spec doesn't say what should happen if (vlan_vid == 0) and (vlan_vid_mask & 0x1000) != 0 but (vlan_vid_mask != 0x1000), but it would be straightforward to also interpret as [2]. [3] Matches only packets that have an 802.1Q header with VID xxx (and any PCP). [4] Matches only packets that have an 802.1Q header with PCP y (and any VID). NXM: z is ((y << 1) | 1). OF1.0: The spec isn't very clear, but OVS implements it this way. OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff) == 0x1000 would also work, but the spec doesn't define their behavior. [5] Matches only packets that have an 802.1Q header with VID xxx and PCP y. NXM: z is ((y << 1) | 1). OF1.2: Presumably other masks such that (vlan_vid_mask & 0x1fff) == 0x1fff would also work. [6] Matches packets with no 802.1Q header or with an 802.1Q header with a VID of 0. Only possible with NXM. [7] Matches packets with no 802.1Q header or with an 802.1Q header with a PCP of 0. Only possible with NXM. [8] Matches packets with no 802.1Q header or with an 802.1Q header with both VID and PCP of 0. Only possible with NXM. [9] Matches only packets that have an 802.1Q header with an odd-numbered VID (and any PCP). Only possible with NXM and OF1.2. (This is just an example; one can match on any desired VID bit pattern.) [10] Matches only packets that have an 802.1Q header with an odd-numbered PCP (and any VID). Only possible with NXM. (This is just an example; one can match on any desired VID bit pattern.) Additional notes: - OF1.2: The top three bits of OXM_OF_VLAN_VID are fixed to zero, so bits 13, 14, and 15 in the masks listed in the table may be set to arbitrary values, as long as the corresponding value bits are also zero. The suggested ffff mask for [2], [3], and [5] allows a shorter OXM representation (the mask is omitted) than the minimal 1fff mask. Flow Cookies ============ OpenFlow 1.0 and later versions have the concept of a "flow cookie", which is a 64-bit integer value attached to each flow. The treatment of the flow cookie has varied greatly across OpenFlow versions, however. In OpenFlow 1.0: - OFPFC_ADD set the cookie in the flow that it added. - OFPFC_MODIFY and OFPFC_MODIFY_STRICT updated the cookie for the flow or flows that it modified. - OFPST_FLOW messages included the flow cookie. - OFPT_FLOW_REMOVED messages reported the cookie of the flow that was removed. OpenFlow 1.1 made the following changes: - Flow mod operations OFPFC_MODIFY, OFPFC_MODIFY_STRICT, OFPFC_DELETE, and OFPFC_DELETE_STRICT, plus flow stats requests and aggregate stats requests, gained the ability to match on flow cookies with an arbitrary mask. - OFPFC_MODIFY and OFPFC_MODIFY_STRICT were changed to add a new flow, in the case of no match, only if the flow table modification operation did not match on the cookie field. (In OpenFlow 1.0, modify operations always added a new flow when there was no match.) - OFPFC_MODIFY and OFPFC_MODIFY_STRICT no longer updated flow cookies. OpenFlow 1.2 made the following changes: - OFPC_MODIFY and OFPFC_MODIFY_STRICT were changed to never add a new flow, regardless of whether the flow cookie was used for matching. Open vSwitch support for OpenFlow 1.0 implements the OpenFlow 1.0 behavior with the following extensions: - An NXM extension field NXM_NX_COOKIE(_W) allows the NXM versions of OFPFC_MODIFY, OFPFC_MODIFY_STRICT, OFPFC_DELETE, and OFPFC_DELETE_STRICT flow_mods, plus flow stats requests and aggregate stats requests, to match on flow cookies with arbitrary masks. This is much like the equivalent OpenFlow 1.1 feature. - Like OpenFlow 1.1, OFPC_MODIFY and OFPFC_MODIFY_STRICT add a new flow if there is no match and the mask is zero (or not given). - The "cookie" field in OFPT_FLOW_MOD and NXT_FLOW_MOD messages is used as the cookie value for OFPFC_ADD commands, as described in OpenFlow 1.0. For OFPFC_MODIFY and OFPFC_MODIFY_STRICT commands, the "cookie" field is used as a new cookie for flows that match unless it is UINT64_MAX, in which case the flow's cookie is not updated. - NXT_PACKET_IN (the Nicira extended version of OFPT_PACKET_IN) reports the cookie of the rule that generated the packet, or all-1-bits if no rule generated the packet. (Older versions of OVS used all-0-bits instead of all-1-bits.) The following table shows the handling of different protocols when receiving OFPFC_MODIFY and OFPFC_MODIFY_STRICT messages. A mask of 0 indicates either an explicit mask of zero or an implicit one by not specifying the NXM_NX_COOKIE(_W) field. ``` Match Update Add on miss Add on miss cookie cookie mask!=0 mask==0 ====== ====== =========== =========== OpenFlow 1.0 no yes OpenFlow 1.1 yes no no yes OpenFlow 1.2 yes no no no NXM yes yes* no yes * Updates the flow's cookie unless the "cookie" field is UINT64_MAX. ``` Multiple Table Support ====================== OpenFlow 1.0 has only rudimentary support for multiple flow tables. Notably, OpenFlow 1.0 does not allow the controller to specify the flow table to which a flow is to be added. Open vSwitch adds an extension for this purpose, which is enabled on a per-OpenFlow connection basis using the NXT_FLOW_MOD_TABLE_ID message. When the extension is enabled, the upper 8 bits of the 'command' member in an OFPT_FLOW_MOD or NXT_FLOW_MOD message designates the table to which a flow is to be added. The Open vSwitch software switch implementation offers 255 flow tables. On packet ingress, only the first flow table (table 0) is searched, and the contents of the remaining tables are not considered in any way. Tables other than table 0 only come into play when an NXAST_RESUBMIT_TABLE action specifies another table to search. Tables 128 and above are reserved for use by the switch itself. Controllers should use only tables 0 through 127. OFPTC_* Table Configuration =========================== This section covers the history of the OFPTC_* table configuration bits across OpenFlow versions. OpenFlow 1.0 flow tables had fixed configurations. OpenFlow 1.1 enabled controllers to configure behavior upon flow table miss and added the OFPTC_MISS_* constants for that purpose. OFPTC_* did not control anything else but it was nevertheless conceptualized as a set of bit-fields instead of an enum. OF1.1 added the OFPT_TABLE_MOD message to set OFPTC_MISS_* for a flow table and added the 'config' field to the OFPST_TABLE reply to report the current setting. OpenFlow 1.2 did not change anything in this regard. OpenFlow 1.3 switched to another means to changing flow table miss behavior and deprecated OFPTC_MISS_* without adding any more OFPTC_* constants. This meant that OFPT_TABLE_MOD now had no purpose at all, but OF1.3 kept it around "for backward compatibility with older and newer versions of the specification." At the same time, OF1.3 introduced a new message OFPMP_TABLE_FEATURES that included a field 'config' documented as reporting the OFPTC_* values set with OFPT_TABLE_MOD; of course this served no real purpose because no OFPTC_* values are defined. OF1.3 did remove the OFPTC_* field from OFPMP_TABLE (previously named OFPST_TABLE). OpenFlow 1.4 defined two new OFPTC_* constants, OFPTC_EVICTION and OFPTC_VACANCY_EVENTS, using bits that did not overlap with OFPTC_MISS_* even though those bits had not been defined since OF1.2. OFPT_TABLE_MOD still controlled these settings. The field for OFPTC_* values in OFPMP_TABLE_FEATURES was renamed from 'config' to 'capabilities' and documented as reporting the flags that are supported in a OFPT_TABLE_MOD message. The OFPMP_TABLE_DESC message newly added in OF1.4 reported the OFPTC_* setting. OpenFlow 1.5 did not change anything in this regard. The following table summarizes. The columns say: - OpenFlow version(s). - The OFPTC_* flags defined in those versions. - Whether OFPT_TABLE_MOD can modify OFPTC_* flags. - Whether OFPST_TABLE/OFPMP_TABLE reports the OFPTC_* flags. - What OFPMP_TABLE_FEATURES reports (if it exists): either the current configuration or the switch's capabilities. - Whether OFPMP_TABLE_DESC reports the current configuration. OpenFlow OFPTC_* flags TABLE_MOD stats? TABLE_FEATURES TABLE_DESC --------- ----------------------- --------- ------ -------------- ---------- OF1.0 none no[*][+] no[*] nothing[*][+] no[*][+] OF1.1/1.2 MISS_* yes yes nothing[+] no[+] OF1.3 none yes[*] no[*] config[*] no[*][+] OF1.4/1.5 EVICTION/VACANCY_EVENTS yes no capabilities yes [*] Nothing to report/change anyway. [+] No such message. IPv6 ==== Open vSwitch supports stateless handling of IPv6 packets. Flows can be written to support matching TCP, UDP, and ICMPv6 headers within an IPv6 packet. Deeper matching of some Neighbor Discovery messages is also supported. IPv6 was not designed to interact well with middle-boxes. This, combined with Open vSwitch's stateless nature, have affected the processing of IPv6 traffic, which is detailed below. Extension Headers ----------------- The base IPv6 header is incredibly simple with the intention of only containing information relevant for routing packets between two endpoints. IPv6 relies heavily on the use of extension headers to provide any other functionality. Unfortunately, the extension headers were designed in such a way that it is impossible to move to the next header (including the layer-4 payload) unless the current header is understood. Open vSwitch will process the following extension headers and continue to the next header: * Fragment (see the next section) * AH (Authentication Header) * Hop-by-Hop Options * Routing * Destination Options When a header is encountered that is not in that list, it is considered "terminal". A terminal header's IPv6 protocol value is stored in "nw_proto" for matching purposes. If a terminal header is TCP, UDP, or ICMPv6, the packet will be further processed in an attempt to extract layer-4 information. Fragments --------- IPv6 requires that every link in the internet have an MTU of 1280 octets or greater (RFC 2460). As such, a terminal header (as described above in "Extension Headers") in the first fragment should generally be reachable. In this case, the terminal header's IPv6 protocol type is stored in the "nw_proto" field for matching purposes. If a terminal header cannot be found in the first fragment (one with a fragment offset of zero), the "nw_proto" field is set to 0. Subsequent fragments (those with a non-zero fragment offset) have the "nw_proto" field set to the IPv6 protocol type for fragments (44). Jumbograms ---------- An IPv6 jumbogram (RFC 2675) is a packet containing a payload longer than 65,535 octets. A jumbogram is only relevant in subnets with a link MTU greater than 65,575 octets, and are not required to be supported on nodes that do not connect to link with such large MTUs. Currently, Open vSwitch doesn't process jumbograms. In-Band Control =============== Motivation ---------- An OpenFlow switch must establish and maintain a TCP network connection to its controller. There are two basic ways to categorize the network that this connection traverses: either it is completely separate from the one that the switch is otherwise controlling, or its path may overlap the network that the switch controls. We call the former case "out-of-band control", the latter case "in-band control". Out-of-band control has the following benefits: - Simplicity: Out-of-band control slightly simplifies the switch implementation. - Reliability: Excessive switch traffic volume cannot interfere with control traffic. - Integrity: Machines not on the control network cannot impersonate a switch or a controller. - Confidentiality: Machines not on the control network cannot snoop on control traffic. In-band control, on the other hand, has the following advantages: - No dedicated port: There is no need to dedicate a physical switch port to control, which is important on switches that have few ports (e.g. wireless routers, low-end embedded platforms). - No dedicated network: There is no need to build and maintain a separate control network. This is important in many environments because it reduces proliferation of switches and wiring. Open vSwitch supports both out-of-band and in-band control. This section describes the principles behind in-band control. See the description of the Controller table in ovs-vswitchd.conf.db(5) to configure OVS for in-band control. Principles ---------- The fundamental principle of in-band control is that an OpenFlow switch must recognize and switch control traffic without involving the OpenFlow controller. All the details of implementing in-band control are special cases of this principle. The rationale for this principle is simple. If the switch does not handle in-band control traffic itself, then it will be caught in a contradiction: it must contact the controller, but it cannot, because only the controller can set up the flows that are needed to contact the controller. The following points describe important special cases of this principle. - In-band control must be implemented regardless of whether the switch is connected. It is tempting to implement the in-band control rules only when the switch is not connected to the controller, using the reasoning that the controller should have complete control once it has established a connection with the switch. This does not work in practice. Consider the case where the switch is connected to the controller. Occasionally it can happen that the controller forgets or otherwise needs to obtain the MAC address of the switch. To do so, the controller sends a broadcast ARP request. A switch that implements the in-band control rules only when it is disconnected will then send an OFPT_PACKET_IN message up to the controller. The controller will be unable to respond, because it does not know the MAC address of the switch. This is a deadlock situation that can only be resolved by the switch noticing that its connection to the controller has hung and reconnecting. - In-band control must override flows set up by the controller. It is reasonable to assume that flows set up by the OpenFlow controller should take precedence over in-band control, on the basis that the controller should be in charge of the switch. Again, this does not work in practice. Reasonable controller implementations may set up a "last resort" fallback rule that wildcards every field and, e.g., sends it up to the controller or discards it. If a controller does that, then it will isolate itself from the switch. - The switch must recognize all control traffic. The fundamental principle of in-band control states, in part, that a switch must recognize control traffic without involving the OpenFlow controller. More specifically, the switch must recognize *all* control traffic. "False negatives", that is, packets that constitute control traffic but that the switch does not recognize as control traffic, lead to control traffic storms. Consider an OpenFlow switch that only recognizes control packets sent to or from that switch. Now suppose that two switches of this type, named A and B, are connected to ports on an Ethernet hub (not a switch) and that an OpenFlow controller is connected to a third hub port. In this setup, control traffic sent by switch A will be seen by switch B, which will send it to the controller as part of an OFPT_PACKET_IN message. Switch A will then see the OFPT_PACKET_IN message's packet, re-encapsulate it in another OFPT_PACKET_IN, and send it to the controller. Switch B will then see that OFPT_PACKET_IN, and so on in an infinite loop. Incidentally, the consequences of "false positives", where packets that are not control traffic are nevertheless recognized as control traffic, are much less severe. The controller will not be able to control their behavior, but the network will remain in working order. False positives do constitute a security problem. - The switch should use echo-requests to detect disconnection. TCP will notice that a connection has hung, but this can take a considerable amount of time. For example, with default settings the Linux kernel TCP implementation will retransmit for between 13 and 30 minutes, depending on the connection's retransmission timeout, according to kernel documentation. This is far too long for a switch to be disconnected, so an OpenFlow switch should implement its own connection timeout. OpenFlow OFPT_ECHO_REQUEST messages are the best way to do this, since they test the OpenFlow connection itself. Implementation -------------- This section describes how Open vSwitch implements in-band control. Correctly implementing in-band control has proven difficult due to its many subtleties, and has thus gone through many iterations. Please read through and understand the reasoning behind the chosen rules before making modifications. Open vSwitch implements in-band control as "hidden" flows, that is, flows that are not visible through OpenFlow, and at a higher priority than wildcarded flows can be set up through OpenFlow. This is done so that the OpenFlow controller cannot interfere with them and possibly break connectivity with its switches. It is possible to see all flows, including in-band ones, with the ovs-appctl "bridge/dump-flows" command. The Open vSwitch implementation of in-band control can hide traffic to arbitrary "remotes", where each remote is one TCP port on one IP address. Currently the remotes are automatically configured as the in-band OpenFlow controllers plus the OVSDB managers, if any. (The latter is a requirement because OVSDB managers are responsible for configuring OpenFlow controllers, so if the manager cannot be reached then OpenFlow cannot be reconfigured.) The following rules (with the OFPP_NORMAL action) are set up on any bridge that has any remotes: (a) DHCP requests sent from the local port. (b) ARP replies to the local port's MAC address. (c) ARP requests from the local port's MAC address. In-band also sets up the following rules for each unique next-hop MAC address for the remotes' IPs (the "next hop" is either the remote itself, if it is on a local subnet, or the gateway to reach the remote): (d) ARP replies to the next hop's MAC address. (e) ARP requests from the next hop's MAC address. In-band also sets up the following rules for each unique remote IP address: (f) ARP replies containing the remote's IP address as a target. (g) ARP requests containing the remote's IP address as a source. In-band also sets up the following rules for each unique remote (IP,port) pair: (h) TCP traffic to the remote's IP and port. (i) TCP traffic from the remote's IP and port. The goal of these rules is to be as narrow as possible to allow a switch to join a network and be able to communicate with the remotes. As mentioned earlier, these rules have higher priority than the controller's rules, so if they are too broad, they may prevent the controller from implementing its policy. As such, in-band actively monitors some aspects of flow and packet processing so that the rules can be made more precise. In-band control monitors attempts to add flows into the datapath that could interfere with its duties. The datapath only allows exact match entries, so in-band control is able to be very precise about the flows it prevents. Flows that miss in the datapath are sent to userspace to be processed, so preventing these flows from being cached in the "fast path" does not affect correctness. The only type of flow that is currently prevented is one that would prevent DHCP replies from being seen by the local port. For example, a rule that forwarded all DHCP traffic to the controller would not be allowed, but one that forwarded to all ports (including the local port) would. As mentioned earlier, packets that miss in the datapath are sent to the userspace for processing. The userspace has its own flow table, the "classifier", so in-band checks whether any special processing is needed before the classifier is consulted. If a packet is a DHCP response to a request from the local port, the packet is forwarded to the local port, regardless of the flow table. Note that this requires L7 processing of DHCP replies to determine whether the 'chaddr' field matches the MAC address of the local port. It is interesting to note that for an L3-based in-band control mechanism, the majority of rules are devoted to ARP traffic. At first glance, some of these rules appear redundant. However, each serves an important role. First, in order to determine the MAC address of the remote side (controller or gateway) for other ARP rules, we must allow ARP traffic for our local port with rules (b) and (c). If we are between a switch and its connection to the remote, we have to allow the other switch's ARP traffic to through. This is done with rules (d) and (e), since we do not know the addresses of the other switches a priori, but do know the remote's or gateway's. Finally, if the remote is running in a local guest VM that is not reached through the local port, the switch that is connected to the VM must allow ARP traffic based on the remote's IP address, since it will not know the MAC address of the local port that is sending the traffic or the MAC address of the remote in the guest VM. With a few notable exceptions below, in-band should work in most network setups. The following are considered "supported" in the current implementation: - Locally Connected. The switch and remote are on the same subnet. This uses rules (a), (b), (c), (h), and (i). - Reached through Gateway. The switch and remote are on different subnets and must go through a gateway. This uses rules (a), (b), (c), (h), and (i). - Between Switch and Remote. This switch is between another switch and the remote, and we want to allow the other switch's traffic through. This uses rules (d), (e), (h), and (i). It uses (b) and (c) indirectly in order to know the MAC address for rules (d) and (e). Note that DHCP for the other switch will not work unless an OpenFlow controller explicitly lets this switch pass the traffic. - Between Switch and Gateway. This switch is between another switch and the gateway, and we want to allow the other switch's traffic through. This uses the same rules and logic as the "Between Switch and Remote" configuration described earlier. - Remote on Local VM. The remote is a guest VM on the system running in-band control. This uses rules (a), (b), (c), (h), and (i). - Remote on Local VM with Different Networks. The remote is a guest VM on the system running in-band control, but the local port is not used to connect to the remote. For example, an IP address is configured on eth0 of the switch. The remote's VM is connected through eth1 of the switch, but an IP address has not been configured for that port on the switch. As such, the switch will use eth0 to connect to the remote, and eth1's rules about the local port will not work. In the example, the switch attached to eth0 would use rules (a), (b), (c), (h), and (i) on eth0. The switch attached to eth1 would use rules (f), (g), (h), and (i). The following are explicitly *not* supported by in-band control: - Specify Remote by Name. Currently, the remote must be identified by IP address. A naive approach would be to permit all DNS traffic. Unfortunately, this would prevent the controller from defining any policy over DNS. Since switches that are located behind us need to connect to the remote, in-band cannot simply add a rule that allows DNS traffic from the local port. The "correct" way to support this is to parse DNS requests to allow all traffic related to a request for the remote's name through. Due to the potential security problems and amount of processing, we decided to hold off for the time-being. - Differing Remotes for Switches. All switches must know the L3 addresses for all the remotes that other switches may use, since rules need to be set up to allow traffic related to those remotes through. See rules (f), (g), (h), and (i). - Differing Routes for Switches. In order for the switch to allow other switches to connect to a remote through a gateway, it allows the gateway's traffic through with rules (d) and (e). If the routes to the remote differ for the two switches, we will not know the MAC address of the alternate gateway. Action Reproduction =================== It seems likely that many controllers, at least at startup, use the OpenFlow "flow statistics" request to obtain existing flows, then compare the flows' actions against the actions that they expect to find. Before version 1.8.0, Open vSwitch always returned exact, byte-for-byte copies of the actions that had been added to the flow table. The current version of Open vSwitch does not always do this in some exceptional cases. This section lists the exceptions that controller authors must keep in mind if they compare actual actions against desired actions in a bytewise fashion: - Open vSwitch zeros padding bytes in action structures, regardless of their values when the flows were added. - Open vSwitch "normalizes" the instructions in OpenFlow 1.1 (and later) in the following way: * OVS sorts the instructions into the following order: Apply-Actions, Clear-Actions, Write-Actions, Write-Metadata, Goto-Table. * OVS drops Apply-Actions instructions that have empty action lists. * OVS drops Write-Actions instructions that have empty action sets. Please report other discrepancies, if you notice any, so that we can fix or document them. Suggestions =========== Suggestions to improve Open vSwitch are welcome at discuss@openvswitch.org. openvswitch-2.5.9/PaxHeaders.82075/README.md0000644000000000000000000000013213534540071015123 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.045685899 30 ctime=1567801423.689845376 openvswitch-2.5.9/README.md0000644000175000017500000001117613534540071016617 0ustar00jpettitjpettit00000000000000Open vSwitch ============ Build Status: ------------- [![Build Status](https://travis-ci.org/openvswitch/ovs.png)](https://travis-ci.org/openvswitch/ovs) What is Open vSwitch? --------------------- Open vSwitch is a multilayer software switch licensed under the open source Apache 2 license. Our goal is to implement a production quality switch platform that supports standard management interfaces and opens the forwarding functions to programmatic extension and control. Open vSwitch is well suited to function as a virtual switch in VM environments. In addition to exposing standard control and visibility interfaces to the virtual networking layer, it was designed to support distribution across multiple physical servers. Open vSwitch supports multiple Linux-based virtualization technologies including Xen/XenServer, KVM, and VirtualBox. The bulk of the code is written in platform-independent C and is easily ported to other environments. The current release of Open vSwitch supports the following features: * Standard 802.1Q VLAN model with trunk and access ports * NIC bonding with or without LACP on upstream switch * NetFlow, sFlow(R), and mirroring for increased visibility * QoS (Quality of Service) configuration, plus policing * Geneve, GRE, GRE over IPSEC, VXLAN, and LISP tunneling * 802.1ag connectivity fault management * OpenFlow 1.0 plus numerous extensions * Transactional configuration database with C and Python bindings * High-performance forwarding using a Linux kernel module The included Linux kernel module supports Linux 2.6.32 and up, with testing focused on 2.6.32 with Centos and Xen patches. Open vSwitch also has special support for Citrix XenServer and Red Hat Enterprise Linux hosts. Open vSwitch can also operate, at a cost in performance, entirely in userspace, without assistance from a kernel module. This userspace implementation should be easier to port than the kernel-based switch. It is considered experimental. What's here? ------------ The main components of this distribution are: * ovs-vswitchd, a daemon that implements the switch, along with a companion Linux kernel module for flow-based switching. * ovsdb-server, a lightweight database server that ovs-vswitchd queries to obtain its configuration. * ovs-dpctl, a tool for configuring the switch kernel module. * Scripts and specs for building RPMs for Citrix XenServer and Red Hat Enterprise Linux. The XenServer RPMs allow Open vSwitch to be installed on a Citrix XenServer host as a drop-in replacement for its switch, with additional functionality. * ovs-vsctl, a utility for querying and updating the configuration of ovs-vswitchd. * ovs-appctl, a utility that sends commands to running Open vSwitch daemons. Open vSwitch also provides some tools: * ovs-ofctl, a utility for querying and controlling OpenFlow switches and controllers. * ovs-pki, a utility for creating and managing the public-key infrastructure for OpenFlow switches. * ovs-testcontroller, a simple OpenFlow controller that may be useful for testing (though not for production). * A patch to tcpdump that enables it to parse OpenFlow messages. What other documentation is available? -------------------------------------- To install Open vSwitch on a regular Linux or FreeBSD host, please read [INSTALL.md]. For specifics around installation on a specific platform, please see one of these files: - [INSTALL.Debian.md] - [INSTALL.Fedora.md] - [INSTALL.RHEL.md] - [INSTALL.XenServer.md] To use Open vSwitch... - ...with Docker on Linux, read [INSTALL.Docker.md] - ...with KVM on Linux, read [INSTALL.md], read [INSTALL.KVM.md] - ...with Libvirt, read [INSTALL.Libvirt.md]. - ...without using a kernel module, read [INSTALL.userspace.md]. - ...with SELinux, read [INSTALL.SELinux.md]. For answers to common questions, read [FAQ.md]. To learn how to set up SSL support for Open vSwitch, read [INSTALL.SSL.md]. To learn about some advanced features of the Open vSwitch software switch, read the [tutorial/Tutorial.md]. Each Open vSwitch userspace program is accompanied by a manpage. Many of the manpages are customized to your configuration as part of the build process, so we recommend building Open vSwitch before reading the manpages. Contact ------- bugs@openvswitch.org [INSTALL.md]:INSTALL.md [INSTALL.Debian.md]:INSTALL.Debian.md [INSTALL.Docker.md]:INSTALL.Docker.md [INSTALL.Fedora.md]:INSTALL.Fedora.md [INSTALL.KVM.md]:INSTALL.KVM.md [INSTALL.Libvirt.md]:INSTALL.Libvirt.md [INSTALL.RHEL.md]:INSTALL.RHEL.md [INSTALL.SSL.md]:INSTALL.SSL.md [INSTALL.userspace.md]:INSTALL.userspace.md [INSTALL.XenServer.md]:INSTALL.XenServer.md [FAQ.md]:FAQ.md [tutorial/Tutorial.md]:tutorial/Tutorial.md openvswitch-2.5.9/PaxHeaders.82075/COPYING0000644000000000000000000000013213534540071014677 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801424.653852483 openvswitch-2.5.9/COPYING0000644000175000017500000000320513534540071016365 0ustar00jpettitjpettit00000000000000This file is a summary of the licensing of files in this distribution. Some files may be marked specifically with a different license, in which case that license applies to the file in question. Most files are licensed under the Apache License, Version 2.0: Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Files under the datapath directory are licensed under the GNU General Public License, version 2. File build-aux/cccl is licensed under the GNU General Public License, version 2. The following files are licensed under the 2-clause BSD license. include/windows/getopt.h lib/getopt_long.c The following files are licensed under the 3-clause BSD-license include/windows/netinet/icmp6.h include/windows/netinet/ip6.h lib/strsep.c Files under the xenserver directory are licensed on a file-by-file basis. Refer to each file for details. Files lib/sflow*.[ch] are licensed under the terms of either the Sun Industry Standards Source License 1.1, that is available at: http://host-sflow.sourceforge.net/sissl.html or the InMon sFlow License, that is available at: http://www.inmon.com/technology/sflowlicense.txt openvswitch-2.5.9/PaxHeaders.82075/INSTALL.RHEL.md0000644000000000000000000000013213534540071016025 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.677845288 openvswitch-2.5.9/INSTALL.RHEL.md0000644000175000017500000001376113534540071017523 0ustar00jpettitjpettit00000000000000How to Install Open vSwitch on Red Hat Enterprise Linux ======================================================= This document describes how to build and install Open vSwitch on a Red Hat Enterprise Linux (RHEL) host. If you want to install Open vSwitch on a generic Linux host, see [INSTALL.md] instead. We have tested these instructions with RHEL 5.6 and RHEL 6.0. Building Open vSwitch for RHEL ------------------------------ You may build from an Open vSwitch distribution tarball or from an Open vSwitch Git tree. The default RPM build directory (_topdir) has five directories in the top-level: 1. BUILD/ Where the software is unpacked and built. 2. RPMS/ Where the newly created binary package files are written. 3. SOURCES/ Contains the original sources, patches, and icon files. 4. SPECS/ Contains the spec files for each package to be built. 5. SRPMS/ Where the newly created source package files are written. Before you begin, note the RPM sources directory on your version of RHEL. The command "rpmbuild --showrc" will show the configuration for each of those directories. Alternatively, the command "rpm --eval '%{_topdir}'" shows the current configuration for the top level directory and the command "rpm --eval '%{_sourcedir}'" does the same for the sources directory. On RHEL 5, the default RPM _topdir is /usr/src/redhat and the default RPM sources directory is /usr/src/redhat/SOURCES. On RHEL 6, the default _topdir is $HOME/rpmbuild and the default RPM sources directory is $HOME/rpmbuild/SOURCES. 1. Install build prerequisites: ``` yum install gcc make python-devel openssl-devel kernel-devel graphviz \ kernel-debug-devel autoconf automake rpm-build redhat-rpm-config \ libtool ``` 2. If you are building from a distribution tarball, skip to step 3. Otherwise, you must be building from an Open vSwitch Git tree. Determine what version of Autoconf is installed (e.g. run "autoconf --version"). If it is not at least version 2.63, then you have two choices: a. Install Autoconf 2.63 or later, one way or another. b. Create a distribution tarball on some other machine, by running "./boot.sh; ./configure; make dist" in the Git tree. You must run this on a machine that has the tools listed in [INSTALL.md] as prerequisites for building from a Git tree. Afterward, proceed with the rest of the instructions using the distribution tarball. 3. Some versions of the RHEL 6 kernel-devel package contain a broken "build" symlink. If you are using such a version, you must fix the problem before continuing. To find out whether you are affected, run: ``` cd /lib/modules/ ls -l build/ ``` where `` is the version number of the RHEL 6 kernel. (The trailing slash in the final command is important. Be sure to include it.) If the "ls" command produces a directory listing, your kernel-devel package is OK. If it produces a "No such file or directory" error, your kernel-devel package is buggy. If your kernel-devel package is buggy, then you can fix it with: ``` cd /lib/modules/ rm build ln -s /usr/src/kernels/ build ``` where `` is the name of an existing directory under /usr/src/kernels, whose name should be similar to `` but may contain some extra parts. Once you have done this, verify the fix with the same procedure you used above to check for the problem. 4. If you are building from a distribution tarball, skip to step 5. Otherwise, create a distribution tarball from the root of the Git tree by running: ``` ./boot.sh ./configure make dist ``` 5. Now you have a distribution tarball, named something like openvswitch-x.y.z.tar.gz. Copy this file into the RPM sources directory, e.g.: `cp openvswitch-x.y.z.tar.gz $HOME/rpmbuild/SOURCES` 6. Make another copy of the distribution tarball in a temporary directory. Then unpack the tarball and "cd" into its root, e.g.: ``` tar xzf openvswitch-x.y.z.tar.gz cd openvswitch-x.y.z ``` 7. To build Open vSwitch userspace, run: `rpmbuild -bb rhel/openvswitch.spec` This produces two RPMs: "openvswitch" and "openvswitch-debuginfo". The above command automatically runs the Open vSwitch unit tests. To disable the unit tests, run: `rpmbuild -bb --without check rhel/openvswitch.spec` If the build fails with "configure: error: source dir /lib/modules/2.6.32-279.el6.x86_64/build doesn't exist" or similar, then the kernel-devel package is missing or buggy. Go back to step 1 or 2 and fix the problem. 8. On RHEL 6, to build the Open vSwitch kernel module, copy rhel/openvswitch-kmod.files into the RPM sources directory and run: `rpmbuild -bb rhel/openvswitch-kmod-rhel6.spec` You might have to specify a kernel version and/or variants, e.g.: ``` rpmbuild -bb \ -D "kversion 2.6.32-131.6.1.el6.x86_64" \ -D "kflavors default debug kdump" \ rhel/openvswitch-kmod-rhel6.spec ``` This produces an "kmod-openvswitch" RPM for each kernel variant, in this example: "kmod-openvswitch", "kmod-openvswitch-debug", and "kmod-openvswitch-kdump". A RHEL host has default firewall rules that prevent any Open vSwitch tunnel traffic from passing through. If a user configures Open vSwitch tunnels like Geneve, GRE, VXLAN, LISP etc., they will either have to manually add iptables firewall rules to allow the tunnel traffic or add it through a startup script (Please refer to the "enable-protocol" command in the ovs-ctl(8) manpage). Red Hat Network Scripts Integration ----------------------------------- Simple integration with Red Hat network scripts has been implemented. Please read [rhel/README.RHEL] in the source tree or /usr/share/doc/openvswitch/README.RHEL in the installed openvswitch package for details. Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md [rhel/README.RHEL]:rhel/README.RHEL openvswitch-2.5.9/PaxHeaders.82075/INSTALL.SSL.md0000644000000000000000000000013213534540071015734 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.681845316 openvswitch-2.5.9/INSTALL.SSL.md0000644000175000017500000003065113534540071017427 0ustar00jpettitjpettit00000000000000Configuring Open vSwitch for SSL ================================ If you plan to configure Open vSwitch to connect across the network to an OpenFlow controller, then we recommend that you build Open vSwitch with OpenSSL. SSL support ensures integrity and confidentiality of the OpenFlow connections, increasing network security. This file explains how to configure an Open vSwitch to connect to an OpenFlow controller over SSL. Refer to [INSTALL.md] for instructions on building Open vSwitch with SSL support. Open vSwitch uses TLS version 1.0 or later (TLSv1), as specified by RFC 2246, which is very similar to SSL version 3.0. TLSv1 was released in January 1999, so all current software and hardware should implement it. This document assumes basic familiarity with public-key cryptography and public-key infrastructure. SSL Concepts for OpenFlow ------------------------- This section is an introduction to the public-key infrastructure architectures that Open vSwitch supports for SSL authentication. To connect over SSL, every Open vSwitch must have a unique private/public key pair and a certificate that signs that public key. Typically, the Open vSwitch generates its own public/private key pair. There are two common ways to obtain a certificate for a switch: * Self-signed certificates: The Open vSwitch signs its certificate with its own private key. In this case, each switch must be individually approved by the OpenFlow controller(s), since there is no central authority. This is the only switch PKI model currently supported by NOX (http://noxrepo.org). * Switch certificate authority: A certificate authority (the "switch CA") signs each Open vSwitch's public key. The OpenFlow controllers then check that any connecting switches' certificates are signed by that certificate authority. This is the only switch PKI model supported by the simple OpenFlow controller included with Open vSwitch. Each Open vSwitch must also have a copy of the CA certificate for the certificate authority that signs OpenFlow controllers' keys (the "controller CA" certificate). Typically, the same controller CA certificate is installed on all of the switches within a given administrative unit. There are two common ways for a switch to obtain the controller CA certificate: * Manually copy the certificate to the switch through some secure means, e.g. using a USB flash drive, or over the network with "scp", or even FTP or HTTP followed by manual verification. * Open vSwitch "bootstrap" mode, in which Open vSwitch accepts and saves the controller CA certificate that it obtains from the OpenFlow controller on its first connection. Thereafter the switch will only connect to controllers signed by the same CA certificate. Establishing a Public Key Infrastructure ---------------------------------------- Open vSwitch can make use of your existing public key infrastructure. If you already have a PKI, you may skip forward to the next section. Otherwise, if you do not have a PKI, the ovs-pki script included with Open vSwitch can help. To create an initial PKI structure, invoke it as: % ovs-pki init to create and populate a new PKI directory. The default location for the PKI directory depends on how the Open vSwitch tree was configured (to see the configured default, look for the --dir option description in the output of "ovs-pki --help"). The pki directory contains two important subdirectories. The controllerca subdirectory contains controller CA files, including the following: - cacert.pem: Root certificate for the controller certificate authority. Each Open vSwitch must have a copy of this file to allow it to authenticate valid controllers. - private/cakey.pem: Private signing key for the controller certificate authority. This file must be kept secret. There is no need for switches or controllers to have a copy of it. The switchca subdirectory contains switch CA files, analogous to those in the controllerca subdirectory: - cacert.pem: Root certificate for the switch certificate authority. The OpenFlow controller must have this file to enable it to authenticate valid switches. - private/cakey.pem: Private signing key for the switch certificate authority. This file must be kept secret. There is no need for switches or controllers to have a copy of it. After you create the initial structure, you can create keys and certificates for switches and controllers with ovs-pki. Refer to the ovs-pki(8) manage for complete details. A few examples of its use follow: CONTROLLER KEY GENERATION To create a controller private key and certificate in files named ctl-privkey.pem and ctl-cert.pem, run the following on the machine that contains the PKI structure: % ovs-pki req+sign ctl controller ctl-privkey.pem and ctl-cert.pem would need to be copied to the controller for its use at runtime. If, for testing purposes, you were to use ovs-testcontroller, the simple OpenFlow controller included with Open vSwitch, then the --private-key and --certificate options, respectively, would point to these files. It is very important to make sure that no stray copies of ctl-privkey.pem are created, because they could be used to impersonate the controller. SWITCH KEY GENERATION WITH SELF-SIGNED CERTIFICATES If you are using self-signed certificates (see "SSL Concepts for OpenFlow"), this is one way to create an acceptable certificate for your controller to approve. 1. Run the following command on the Open vSwitch itself: % ovs-pki self-sign sc (This command does not require a copy of any of the PKI files generated by "ovs-pki init", and you should not copy them to the switch because some of them have contents that must remain secret for security.) The "ovs-pki self-sign" command has the following output: * sc-privkey.pem, the switch private key file. For security, the contents of this file must remain secret. There is ordinarily no need to copy this file off the Open vSwitch. * sc-cert.pem, the switch certificate, signed by the switch's own private key. Its contents are not a secret. 2. Optionally, copy controllerca/cacert.pem from the machine that has the OpenFlow PKI structure and verify that it is correct. (Otherwise, you will have to use CA certificate bootstrapping when you configure Open vSwitch in the next step.) 3. Configure Open vSwitch to use the keys and certificates (see "Configuring SSL Support", below). SWITCH KEY GENERATION WITH A SWITCH PKI (EASY METHOD) If you are using a switch PKI (see "SSL Concepts for OpenFlow", above), this method of switch key generation is a little easier than the alternate method described below, but it is also a little less secure because it requires copying a sensitive private key from file from the machine hosting the PKI to the switch. 1. Run the following on the machine that contains the PKI structure: % ovs-pki req+sign sc switch This command has the following output: * sc-privkey.pem, the switch private key file. For security, the contents of this file must remain secret. * sc-cert.pem, the switch certificate. Its contents are not a secret. 2. Copy sc-privkey.pem and sc-cert.pem, plus controllerca/cacert.pem, to the Open vSwitch. 3. Delete the copies of sc-privkey.pem and sc-cert.pem on the PKI machine and any other copies that may have been made in transit. It is very important to make sure that there are no stray copies of sc-privkey.pem, because they could be used to impersonate the switch. (Don't delete controllerca/cacert.pem! It is not security-sensitive and you will need it to configure additional switches.) 4. Configure Open vSwitch to use the keys and certificates (see "Configuring SSL Support", below). SWITCH KEY GENERATION WITH A SWITCH PKI (MORE SECURE) If you are using a switch PKI (see "SSL Concepts for OpenFlow", above), then, compared to the previous method, the method described here takes a little more work, but it does not involve copying the private key from one machine to another, so it may also be a little more secure. 1. Run the following command on the Open vSwitch itself: % ovs-pki req sc (This command does not require a copy of any of the PKI files generated by "ovs-pki init", and you should not copy them to the switch because some of them have contents that must remain secret for security.) The "ovs-pki req" command has the following output: * sc-privkey.pem, the switch private key file. For security, the contents of this file must remain secret. There is ordinarily no need to copy this file off the Open vSwitch. * sc-req.pem, the switch "certificate request", which is essentially the switch's public key. Its contents are not a secret. * A fingerprint, on stdout. 2. Write the fingerprint down on a slip of paper and copy sc-req.pem to the machine that contains the PKI structure. 3. On the machine that contains the PKI structure, run: % ovs-pki sign sc switch This command will output a fingerprint to stdout and request that you verify it. Check that it is the same as the fingerprint that you wrote down on the slip of paper before you answer "yes". "ovs-pki sign" creates a file named sc-cert.pem, which is the switch certificate. Its contents are not a secret. 4. Copy the generated sc-cert.pem, plus controllerca/cacert.pem from the PKI structure, to the Open vSwitch, and verify that they were copied correctly. You may delete sc-cert.pem from the machine that hosts the PKI structure now, although it is not important that you do so. (Don't delete controllerca/cacert.pem! It is not security-sensitive and you will need it to configure additional switches.) 5. Configure Open vSwitch to use the keys and certificates (see "Configuring SSL Support", below). Configuring SSL Support ----------------------- SSL configuration requires three additional configuration files. The first two of these are unique to each Open vSwitch. If you used the instructions above to build your PKI, then these files will be named sc-privkey.pem and sc-cert.pem, respectively: - A private key file, which contains the private half of an RSA or DSA key. This file can be generated on the Open vSwitch itself, for the greatest security, or it can be generated elsewhere and copied to the Open vSwitch. The contents of the private key file are secret and must not be exposed. - A certificate file, which certifies that the private key is that of a trustworthy Open vSwitch. This file has to be generated on a machine that has the private key for the switch certification authority, which should not be an Open vSwitch; ideally, it should be a machine that is not networked at all. The certificate file itself is not a secret. The third configuration file is typically the same across all the switches in a given administrative unit. If you used the instructions above to build your PKI, then this file will be named cacert.pem: - The root certificate for the controller certificate authority. The Open vSwitch verifies it that is authorized to connect to an OpenFlow controller by verifying a signature against this CA certificate. Once you have these files, configure ovs-vswitchd to use them using the ovs-vsctl "set-ssl" command, e.g.: ovs-vsctl set-ssl /etc/openvswitch/sc-privkey.pem /etc/openvswitch/sc-cert.pem /etc/openvswitch/cacert.pem Substitute the correct file names, of course, if they differ from the ones used above. You should use absolute file names (ones that begin with "/"), because ovs-vswitchd's current directory is unrelated to the one from which you run ovs-vsctl. If you are using self-signed certificates (see "SSL Concepts for OpenFlow") and you did not copy controllerca/cacert.pem from the PKI machine to the Open vSwitch, then add the --bootstrap option, e.g.: ovs-vsctl -- --bootstrap set-ssl /etc/openvswitch/sc-privkey.pem /etc/openvswitch/sc-cert.pem /etc/openvswitch/cacert.pem After you have added all of these configuration keys, you may specify "ssl:" connection methods elsewhere in the configuration database. "tcp:" connection methods are still allowed even after SSL has been configured, so for security you should use only "ssl:" connections. Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/windows0000644000000000000000000000013213534540120015254 xustar0030 mtime=1567801424.481851214 30 atime=1567801425.625859648 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/0000755000175000017500000000000013534540120017017 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/windows/PaxHeaders.82075/README.rst0000644000000000000000000000013213534540071017025 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/README.rst0000644000175000017500000000253213534540071020515 0ustar00jpettitjpettit00000000000000Open vSwitch Windows installer ============================== This project generates a MSI installer for Open vSwitch on Windows, including CLI executables, services and the Hyper-V vswitch forwarding extension. Requirements ------------ Visual Studio 2013 community, professional, premium or ultimate edition ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Visual Studio Community 2013 is freely available at: https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx WiX Toolset 3.9 ^^^^^^^^^^^^^^^ Download and install from: http://wixtoolset.org/releases/v3.9/stable Microsoft_VC120_CRT_x86.msm ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This Windows merge module is available with Visual Studio and contains the Visual C++ 2013 x86 runtime redistributables files. Copy the file in the *Redist* directory. Open vSwitch installer ---------------------- The installer will be generated under the following path: * windows\ovs-windows-installer\bin\Release\OpenvSwitch.msi Note: the kernel driver needs to be signed. Build instructions ------------------ Build the solution in the Visual Studio IDE or via command line: msbuild ovs-windows-installer.sln /p:Platform=x86 /p:Configuration=Release Silent installation ------------------- msiexec /i OpenvSwitch.msi ADDLOCAL=OpenvSwitchCLI,OpenvSwitchDriver /l*v log.txt openvswitch-2.5.9/windows/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017475 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/automake.mk0000644000175000017500000000712613534540071021171 0ustar00jpettitjpettit00000000000000# Copyright 2015 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License.You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the # License for the specific language governing permissions and limitations # under the License. PTHREAD_TEMP_DIR=`echo "$(PTHREAD_LDFLAGS)" | sed 's|^.\(.*\).$:\1||'` windows_installer: all #Userspace files needed for the installer cp -f $(top_srcdir)/datapath-windows/misc/OVS.psm1 windows/ovs-windows-installer/Services/OVS.psm1 cp -f $(top_srcdir)/vswitchd/vswitch.ovsschema windows/ovs-windows-installer/Services/vswitch.ovsschema cp -f $(top_srcdir)/vswitchd/ovs-vswitchd.exe windows/ovs-windows-installer/Services/ovs-vswitchd.exe cp -f $(top_srcdir)/ovsdb/ovsdb-server.exe windows/ovs-windows-installer/Services/ovsdb-server.exe cp -f $(top_srcdir)/utilities/*.exe windows/ovs-windows-installer/Binaries/ cp -f $(top_srcdir)/utilities/*.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-client.exe windows/ovs-windows-installer/Binaries/ovsdb-client.exe cp -f $(top_srcdir)/ovsdb/ovsdb-tool.exe windows/ovs-windows-installer/Binaries/ovsdb-tool.exe cp -f $(top_srcdir)/ovsdb/ovsdb-client.pdb windows/ovs-windows-installer/Symbols/ cp -f $(top_srcdir)/ovsdb/ovsdb-tool.pdb windows/ovs-windows-installer/Symbols/ #Third party files needed by the installer cp -f $(PTHREAD_WIN32_DIR_DLL_WIN_FORM)/*.dll windows/ovs-windows-installer/Binaries/ cp -f "/c/Program Files (x86)/Common Files/Merge Modules/Microsoft_VC120_CRT_x86.msm" windows/ovs-windows-installer/Redist/Microsoft_VC120_CRT_x86.msm #Forwarding extension files needed for the installer cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8$(VSTUDIO_CONFIG)/package/OVSExt.sys windows/ovs-windows-installer/Driver/Win8/OVSExt.sys cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.cat windows/ovs-windows-installer/Driver/Win8.1/ovsext.cat cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.inf windows/ovs-windows-installer/Driver/Win8.1/ovsext.inf cp -f $(top_srcdir)/datapath-windows/x64/Win8.1$(VSTUDIO_CONFIG)/package/ovsext.sys windows/ovs-windows-installer/Driver/Win8.1/ovsext.sys MSBuild.exe windows/ovs-windows-installer.sln /target:Build /property:Configuration="Release" EXTRA_DIST += \ windows/.gitignore \ windows/automake.mk \ windows/README.rst \ windows/ovs-windows-installer.sln \ windows/ovs-windows-installer/Actions/OVSActions.js \ windows/ovs-windows-installer/CustomActions.wxs \ windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs \ windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs \ windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs \ windows/ovs-windows-installer/License.rtf \ windows/ovs-windows-installer/Product.wxs \ windows/ovs-windows-installer/UI.wxs \ windows/ovs-windows-installer/images/bannrbmp.bmp \ windows/ovs-windows-installer/images/dlgbmp.bmp \ windows/ovs-windows-installer/ovs-windows-installer.wixproj openvswitch-2.5.9/windows/PaxHeaders.82075/ovs-windows-installer0000644000000000000000000000013213534540120021546 xustar0030 mtime=1567801424.497851333 30 atime=1567801425.625859648 30 ctime=1567801424.497851333 openvswitch-2.5.9/windows/ovs-windows-installer/0000755000175000017500000000000013534540120023311 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/CustomActions.wxs0000644000000000000000000000013213534540071025166 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.485851244 openvswitch-2.5.9/windows/ovs-windows-installer/CustomActions.wxs0000644000175000017500000000715313534540071026662 0ustar00jpettitjpettit00000000000000 openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/ovs-windows-installer.wixproj0000644000000000000000000000013213534540071027546 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.497851333 openvswitch-2.5.9/windows/ovs-windows-installer/ovs-windows-installer.wixproj0000644000175000017500000001413613534540071031241 0ustar00jpettitjpettit00000000000000 Debug x86 3.8 259905a2-7434-4190-8a33-8fba67171dd6 2.0 OpenvSwitch Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets bin\$(Configuration)\ obj\$(Configuration)\ Debug bin\$(Configuration)\ obj\$(Configuration)\ BinariesPath=Binaries;SymbolsPath=Symbols; False False 1076; Debug bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ BinariesPath=Binaries;SymbolsPath=Symbols; False False 1076; bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ Debug bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ BinariesPath=Binaries;SymbolsPath=Symbols; False False 1076; bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ Debug bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ BinariesPath=Binaries;SymbolsPath=Symbols; False False 1076; bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ $(WixExtDir)\WixUtilExtension.dll WixUtilExtension $(WixExtDir)\WixUIExtension.dll WixUIExtension openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/Dialogs0000644000000000000000000000013213534540120023130 xustar0030 mtime=1567801424.489851273 30 atime=1567801425.625859648 30 ctime=1567801424.489851273 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/0000755000175000017500000000000013534540120024673 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/PaxHeaders.82075/UserFinishDialog.wxs0000644000000000000000000000013213534540071027154 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.489851273 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/UserFinishDialog.wxs0000644000175000017500000000423713534540071030650 0ustar00jpettitjpettit00000000000000 1 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/PaxHeaders.82075/BeginningDialog.wxs0000644000000000000000000000013213534540071026775 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.485851244 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/BeginningDialog.wxs0000644000175000017500000000515713534540071030473 0ustar00jpettitjpettit00000000000000 NOT Installed OR NOT PATCH Installed AND PATCH Installed AND PATCH NOT Installed OR NOT PATCH Installed AND PATCH 1 NOT Installed OR PATCH openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/PaxHeaders.82075/MyTroubleshootDialog.wxs0000644000000000000000000000013213534540071030074 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.489851273 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/MyTroubleshootDialog.wxs0000644000175000017500000000441013534540071031561 0ustar00jpettitjpettit00000000000000 1 !(wix.WixUICostingPopupOptOut) OR CostingComplete = 1 Installed AND NOT RESUME AND NOT Preselected AND NOT PATCH openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/PaxHeaders.82075/MyEndDialog.wxs0000644000000000000000000000013213534540071026111 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.485851244 openvswitch-2.5.9/windows/ovs-windows-installer/Dialogs/MyEndDialog.wxs0000644000175000017500000000466213534540071027607 0ustar00jpettitjpettit00000000000000 openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/UI.wxs0000644000000000000000000000013213534540071022710 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.493851303 openvswitch-2.5.9/windows/ovs-windows-installer/UI.wxs0000644000175000017500000001002713534540071024376 0ustar00jpettitjpettit00000000000000 1 NOT Installed Installed AND PATCH 1 LicenseAccepted = "1" NOT Installed OR WixUI_InstallMode = "Change" Installed AND NOT PATCH Installed AND PATCH 1 1 1 1 1 1 1 1 Installed NOT Installed 1 1 openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/images0000644000000000000000000000013213534540120023013 xustar0030 mtime=1567801424.493851303 30 atime=1567801425.625859648 30 ctime=1567801424.493851303 openvswitch-2.5.9/windows/ovs-windows-installer/images/0000755000175000017500000000000013534540120024556 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/windows/ovs-windows-installer/images/PaxHeaders.82075/dlgbmp.bmp0000644000000000000000000000013213534540071025042 xustar0030 mtime=1567801401.973685371 30 atime=1567801402.141686605 30 ctime=1567801424.497851333 openvswitch-2.5.9/windows/ovs-windows-installer/images/dlgbmp.bmp0000644000175000017500000160343013534540071026537 0ustar00jpettitjpettit00000000000000BM6(ì8â  ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææGGG***ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææGGG***ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææGGG***ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææGGG***æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÄÄÄggg000***[[[³³³ææææææææææææGGG***¸¸¸AAA<<<¦¦¦æææææææææºººIII***nnnÚÚÚæææ°°°VVVËËËææææææ¹¹¹VVVÂÂÂææææææææææææææææææææææææ†††áááææææææææææææ¶¶¶GGG)))nnnÛÛÛææææææææææææœœœÍÍÍæææææææææææææææææææææŒŒŒ___ææææææ•••VVVæææææææææ¯¯¯EEE(((kkkæææžžžVVVæææææææææ§§§VVVÔÔÔæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååsss777TTT>>>UUUßßßææææææGGGZZZlll æææ¹¹¹jjjXXX'''ØØØ»»»ææææææžžž¬¬¬æææææææææææææææææææææÔÔÔ ˆˆˆæææææææææ«««HHH<<<,,,ÞÞÞææææææäää%%%jjjææææææ±±±ÆÆÆæææææææææVVVæææææædddææææææ```JJJæææsssæææææææææÉÉÉææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææænnn===ÈÈÈæææææææææÕÕÕWWWHHHåååæææGGG¡¡¡ææææææØØØ!!! æææ@@@ÑÑÑææææææ¨¨¨666ÄÄÄ»»»ææææææžžž¬¬¬ææææææææææææææææææææænnnâââææææææ&&&ËËËææææææ………€€€ææææææ———ÜÜÜæææKKKVVVæææææææææVVVæææææædddæææ××× 999ßßßææææææµµµæææsssæææææææææÉÉÉææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ»»»;;;âââææææææææææææææææææ^^^•••æææGGGææææææææææææuuuÍÍÍPPPØØØØØØØØØØØØØØØààà»»»ææææææžžž¬¬¬ææææææææææææææææææØØØ(((æææÚÚÚ666qqqæææææææææããã EEEæææäää%%%)))………ÉÉÉ333ÆÆÆææææææVVVæææææædddæææœœœ¨¨¨æææææææææææææææsssæææææææææÉÉÉææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææYYYÅÅÅæææææææææææææææææææææÛÛÛ333æææGGG+++ææææææææææææ†††¿¿¿ŠŠŠ»»»ææææææžžž¬¬¬ææææææææææææææææææsss¾¾¾/// âââææææææææææææææææææááá FFFæææžžž›››[[[!!!fffÒÒÒ VVVææææææVVVæææææædddæææ”””···æææææææææææææææsssÛÛÛææææææÉÉÉææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ""",,,æææææææææææææææææææææææææææTTTàààGGGÓÓÓæææææææææPPPÝÝÝ )))ÉÉÉÉÉÉÉÉɨ¨¨žžžææææææ˜˜˜³³³æææææææææææææææÛÛÛCCCæææšššæææææææææææææææÝÝÝVVV„„„ååå+++!!!äää½½½xxxæææcccÆÆÆæææVVVæææææædddæææ¾¾¾oooæææææææææáááæææsssÀÀÀæææææævvvÕÕÕææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææHHHæææææææææææææææææææææææææææqqqÍÍÍGGG888¾¾¾ÐÐÐwww[[[æææwwwsssÖÖÖ¼¼¼'''ááá444ÅÅÅÍÍÍ>>>ÒÒÒæææææææææææææææzzz®®®æææäää %%%äääææææææÔÔÔsssFFFááážžž‰‰‰ææææææ:::ÓÓÓæææÊÊÊWWWæææVVVæææËËËEEEžžžÆÆÆIIIxxxÄÄÄ«««AAAæææsssKKKÍÍÍÆÆÆ&&&æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ666æææææææææææææææææææææææææææ]]]ÞÞÞGGG***eeeDDDÜÜÜæææáááSSS´´´æææMMMgggæææææææææææææææÛÛÛ111æææææææææŠŠŠ———æææ ///———åååååå+++ÞÞÞææææææUUUæææææææææPPPËËËVVVæææÚÚÚOOOæææsssWWW†††æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææSSSÔÔÔæææææææææææææææææææææâââ,,,æææÒÒÒÏÏÏæææÆÆÆ   ½½½æææææææææææææææÆÆÆ¡¡¡¬¬¬ßßßææææææÛÛÛÉÉÉáááÆÆÆŸŸŸ¼¼¼ææææææææææææææææææàààÉÉÉ×××æææææææææâââÉÉÉÔÔÔ­­­,,,¶¶¶æææææææææäääÉÉÉÒÒÒæææææææææãããÆÆÆæææææææææÛÛÛÉÉÉÛÛÛÔÔÔËËËæææÛÛÛWWWÉÉÉÙÙÙææææææËËË¢¢¢ªªªÜÜÜæææsssæææ¼¼¼ŸŸŸÆÆÆææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ°°°[[[æææææææææææææææææææææ‹‹‹æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææiiiÄÄÄæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææædddæææææææææææææææææææææææææææsssæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ___iiißßßæææææææææäää………;;;âââææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææævvv¬¬¬ææææææËËËåååæææææææææææææææææææææææææææææææææææææææ¶¶¶———æææææædddæææææææææææææææææææææææææææsssæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææáááZZZ]]]wwweee""">>>ØØØææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÊÊÊ aaaooo ÇÇÇæææææææææææææææææææææææææææææææææææææææ$$$ÃÃÃæææÞÞÞØØØæææææææææææææææææææææææææææsssæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææªªªFFF :::–––åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ½½½<<<333µµµææææææææææææææææææææææææææææææææææææææææææ•••kkkâââææææææææææææææææææææææææææææææææææææsssææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÞÞÞææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââáááæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææsssææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ———GGGææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææáááÊÊÊææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒšššccc=== ---OOO}}}···ãããææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææŒŒŒVVVææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå³³³jjj444BBByyyÂÂÂææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÛÛÛˆˆˆ777aaa···æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââ ÆÆÆæææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå•••'''222   ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØppp;;;«««æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææWWWææææææææææææææææææææææææææææææææææææææææææææææææææææææÉÉÉ777===ÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååLLLÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææäää$$$ËËËææææææææææææææææææææææææææææææææææææææææææææææææ®®®°°°ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÖÖÖFFFœœœæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ———]]]æææææææææææææææææææææææææææææææææææææææææææææ¯¯¯¬¬¬ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÁÁÁmmmåååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå%%%ËËËæææææææææææææææææææææææææææææææææææææææÊÊÊÆÆÆææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ´´´WWWãããæææææææææææææææææææææææææææææææææææææææææææææææææææææææææžžžbbbææææææææææææææææææææææææææææææææææææååå888333åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¿¿¿ 222999)))aaaææææææææææææææææææææææææææææææææææææææææææææææææææææææååå+++ÏÏÏæææææææææææææææææææææææææææææææææ”””444&&&æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÔÔÔjjj®®®àààæææææææææææææææËËË???~~~æææææææææææææææææææææææææææææææææææææææææææææææææææ¡¡¡dddææææææææææææææææææææææææææææææäää$$$kkkËËËæææææææææÖÖÖvvv###åååæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååAAAâââæææææææææææææææææææææææææææææææææÆÆÆRRR°°°ææææææææææææææææææææææææææææææææææææææææææææææææ222ÔÔÔæææææææææææææææææææææææææææ¬¬¬¶¶¶æææææææææææææææææææææ¸¸¸±±±æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææˆˆˆ]]]ÛÛÛæææææææææææææææææææææææææææææææææææææææææææææ«««ßßßææææææææææææææææææææææææææææææææææææææææææ¥¥¥kkkæææææææææææææææææææææææææææ```­­­æææææææææææææææææææææææææææ¡¡¡kkkææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒ111sssææææææææææææææææææææææææææææææææææææææææææ777 ÔÔÔææææææææææææææææææææææææ%%%LLLæææææææææææææææææææææææææææææææææ999777ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææiiišššææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÜÜÜ222 ÔÔÔææææææææææææææææææææææææææææææææææææ¬¬¬mmmæææææææææææææææææææææßßß™™™æææææææææææææææææææææææææææææææææŒŒŒæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÔÔÔ }}}ææææææææææææËØÃæææææææææææææææææææææææææææææææææææææææææææææææææææÓÓÓoooææææææææææææææææææææææææææææææææææææ:::ØØØææææææææææææææææææ¿¿¿¿¿¿æææææææææææææææææææææææææææææææææ¶¶¶æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ@@@åååæææææææææ¤ÅPš"ææææææææææææææææææææææææææææææææææææææææææææææææææææææ®®®âââææææææææææææææææææææææææææææææ³³³sssææææææææææææææææææÚÚÚ˜˜˜hhh777 ÉÉÉæææææææææææææææææææææææææææææææææÄÄÄæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ---ÆÆÆææææææäåㇶj6K˜æææææææææææææææææææææææææææææææææææææææææææææææææææææææææWWW£££ææææææææææææææææææææææææææææææ@@@ØØØæææææææææææææææææææææææææææããã½½½]]],,,ËËËæææææææææææææææææææææææææææææææææ©©©ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÌÌÌ]]]ææææææÜáÙj¨E66K˜æææææææææææææææææææææææææææææææææææææææææææææææææææææææææËËËYYYæææææææææææææææææææææææææææ¶¶¶zzzææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææYYYææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÀÀÀæææÏÚÈSœ&6668ŽK˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜¶Î§ææææææææææææææææææææææææJJJ æææææææææææææææææææææææææææGGG XXXÛÛÛææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¯¯¯EEEææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ\\\***æææºÐ¬C”6666666666666666¯ÊžææææææææææææææææææææææææšššÒÒÒæææææææææææææææææææææººº‰‰‰ÃÃÃzzzæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ§§§ €€€ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ999hhhÑÛÊ= 66666666666666666¯ÊžææææææææææææææææææææææææÖÖÖ®®®æææææææææææææææææææææMMMÞÞÞæææJJJÞÞÞææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÏÏÏ[[[ÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ–––æææ»Ð­D”6666666666666666¯ÊžÏÚÈÕÝÏæææææææææææææææææææææ ŽŽŽææææææææææææææææææÀÀÀwwwææææææµµµææææææææææææææææææææææææææææææææææææææææææææææææææææææÃÃÃ```PPPæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ¶¶¶ææææææÐÛÉV*666@’޹s޹s޹s޹s޹s޹s޹s޹s޹s޹s޹sÊØÂºÐ¬I–ÃÔ¸ææææææææææææææææææAAA€€€ææææææææææææææææææNNN ×××æææææææææ:::ÞÞÞææææææææææææææææææææææææææææææææææææææææææää䟟Ÿ>>> ËËËæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÅÅÅæææææææææÞâÜq«M66K˜ææææææææææææææææææææææææææææææææææææºÐ¬6<ªÈ˜æææææææææææææææNNNsssæææææææææææææææÆÆÆfffææææææææææææ¤¤¤………ææææææææææææææææææææææææææææææææææææÞÞÞ………”””ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÆÆÆææææææææææææåæå‘»w7K˜ææææææææææææææææææææææææææææææææææææºÐ¬666޹såæåæææææææææNNNsssæææææææææææææææVVVËËËææææææææææææååå)))áááæææææææææææææææææææææææææææãã㊊Šzzzæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ···ææææææææææææææææææ°Ë U)æææm©G666666666666666oªKßãÝææææææBBB€€€ææææææææææææÈÈÈSSSææææææææææææææææææ•••ˆˆˆææææææææææææææææææææææææÂÂÂ222‡‡‡ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ›››æææææææææææææææææææææÔÝÏæææm©G6666666666666666Xž,ÓÜÍæææ###ææææææææææææ]]]½½½ææææææææææææææææææââââââææææææææææææææææææššš )))···æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ<<<pppæææææææææææææææææææææææææææm©G66666666666666666u­RÝÝݲ²²æææææææææËËËBBBææææææææææææææææææææææææ………æææææææææææææææ”””‚‚‚áááææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææaaa888æææææææææææææææææææææææææææm©G6666666666666666ƒ´eäåã§§§×××æææææææææaaa¬¬¬ææææææææææææææææææææææææÞÞÞ âââæææææææææ¶¶¶ ÛÛÛææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ™™™ÏÏÏææææææææææææææææææææææææ¸Ïª¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Äˆ·k666:¤Åææææææ\\\'''æææææææææÏÏÏ000æææææææææææææææææææææææææææææætttææææææâââ%%%NNN¨¨¨åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÓÓÓuuuæææææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬66G•ÀÓ´ææææææØØØ eeeææææææææædddšššææææææææææææææææææææææææææææææÕÕÕ %%%äääæææ•••ÕÕÕææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ<<<ØØØææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬6[ 1ÕÝÏæææææææææuuu²²²ææææææÛÛÛ'''333äääæææææææææææææææææææææææææææææææææooo§§§æææ@@@ŠŠŠâââææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææcccææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬w®UáäàæææææææææÊÊÊ åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææãããÄÄÄææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææààদ¦æææææææææææææææææææææææææææææææææææææææææææææææææææàãÞææææææææææææááá444‡‡‡æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÈÈÈ   ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ½½½ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååVVVÞÞÞæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ»»»ãããææææææææææææææææææææææææææææææÛÛÛæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææààà!!!³³³ææææææææææææææææææææææææææææææææææææææææææææææææææææææáááUUU‘‘‘ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ½½½ÜÜÜæææææææææææææææææææææææææææááá%%%NNNÎÎÎææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ©©©ƒƒƒäääæææææææææææææææææææææææææææææææææææææææææææææÆÆÆ333999åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒ‹‹‹æææææææææææææææææææææææææææwww |||áááæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææhhh111¨¨¨ææææææææææææææææææææææææææææææææææææÔÔÔmmmËËËææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææºººæææææææææææææææææææææ¡¡¡###¨¨¨ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææááá555'''yyy···âââæææææææææææææææÒÒÒšššOOO ¥¥¥æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææUUUtttÓÓÓæææææææææÕÕÕrrrLLLÙÙÙæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ(((!!!222999)))ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¬¬¬###999''')))äääææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÏÏÏ)))‡‡‡ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ888°°°ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ:::šššæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÁÁÁdddææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââânnn$$$½½½æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ™™™<<<áááææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ®®®)))kkkÝÝÝæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ˜˜˜<<<ØØØææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââ≉‰RRRÃÃÃææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¹¹¹'''dddáááæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââšššFFFqqqÉÉÉæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââ∈ˆ @@@±±±ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ¡¡¡iiiCCC 222TTT………¾¾¾åååæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå±±±mmm;;;AAAvvvÂÂÂæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææopenvswitch-2.5.9/windows/ovs-windows-installer/images/PaxHeaders.82075/bannrbmp.bmp0000644000000000000000000000013213534540071025374 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.493851303 openvswitch-2.5.9/windows/ovs-windows-installer/images/bannrbmp.bmp0000644000175000017500000040745613534540071027102 0ustar00jpettitjpettit00000000000000BM.6(hIø  ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææáááÊÊÊææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒšššccc=== ---OOO}}}···ãããææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææŒŒŒVVVææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå³³³jjj444BBByyyÂÂÂææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÛÛÛˆˆˆ777aaa···æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââ ÆÆÆæææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå•••'''222   ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØppp;;;«««æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææWWWææææææææææææææææææææææææææææææææææææææææææææææææææææææÉÉÉ777===ÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååLLLÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææäää$$$ËËËææææææææææææææææææææææææææææææææææææææææææææææææ®®®°°°ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÖÖÖFFFœœœæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ———]]]æææææææææææææææææææææææææææææææææææææææææææææ¯¯¯¬¬¬ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÁÁÁmmmåååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå%%%ËËËæææææææææææææææææææææææææææææææææææææææÊÊÊÆÆÆææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ´´´WWWãããæææææææææææææææææææææææææææææææææææææææææææææææææææææææææžžžbbbææææææææææææææææææææææææææææææææææææååå888333åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¿¿¿ 222999)))aaaææææææææææææææææææææææææææææææææææææææææææææææææææææææååå+++ÏÏÏæææææææææææææææææææææææææææææææææ”””444&&&æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÔÔÔjjj®®®àààæææææææææææææææËËË???~~~æææææææææææææææææææææææææææææææææææææææææææææææææææ¡¡¡dddææææææææææææææææææææææææææææææäää$$$kkkËËËæææææææææÖÖÖvvv###åååæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååAAAâââæææææææææææææææææææææææææææææææææÆÆÆRRR°°°ææææææææææææææææææææææææææææææææææææææææææææææææ222ÔÔÔæææææææææææææææææææææææææææ¬¬¬¶¶¶æææææææææææææææææææææ¸¸¸±±±æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææˆˆˆ]]]ÛÛÛæææææææææææææææææææææææææææææææææææææææææææææ«««ßßßææææææææææææææææææææææææææææææææææææææææææ¥¥¥kkkæææææææææææææææææææææææææææ```­­­æææææææææææææææææææææææææææ¡¡¡kkkææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒ111sssææææææææææææææææææææææææææææææææææææææææææ777 ÔÔÔææææææææææææææææææææææææ%%%LLLæææææææææææææææææææææææææææææææææ999777ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææiiišššææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÜÜÜ222 ÔÔÔææææææææææææææææææææææææææææææææææææ¬¬¬mmmæææææææææææææææææææææßßß™™™æææææææææææææææææææææææææææææææææŒŒŒæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÔÔÔ }}}ææææææææææææËØÃæææææææææææææææææææææææææææææææææææææææææææææææææææÓÓÓoooææææææææææææææææææææææææææææææææææææ:::ØØØææææææææææææææææææ¿¿¿¿¿¿æææææææææææææææææææææææææææææææææ¶¶¶æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ@@@åååæææææææææ¤ÅPš"ææææææææææææææææææææææææææææææææææææææææææææææææææææææ®®®âââææææææææææææææææææææææææææææææ³³³sssææææææææææææææææææÚÚÚ˜˜˜hhh777 ÉÉÉæææææææææææææææææææææææææææææææææÄÄÄæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ---ÆÆÆææææææäåㇶj6K˜æææææææææææææææææææææææææææææææææææææææææææææææææææææææææWWW£££ææææææææææææææææææææææææææææææ@@@ØØØæææææææææææææææææææææææææææããã½½½]]],,,ËËËæææææææææææææææææææææææææææææææææ©©©ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÌÌÌ]]]ææææææÜáÙj¨E66K˜æææææææææææææææææææææææææææææææææææææææææææææææææææææææææËËËYYYæææææææææææææææææææææææææææ¶¶¶zzzææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææYYYææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÀÀÀæææÏÚÈSœ&6668ŽK˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜¶Î§ææææææææææææææææææææææææJJJ æææææææææææææææææææææææææææGGG XXXÛÛÛææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¯¯¯EEEææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ\\\***æææºÐ¬C”6666666666666666¯ÊžææææææææææææææææææææææææšššÒÒÒæææææææææææææææææææææººº‰‰‰ÃÃÃzzzæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ§§§ €€€ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ999hhhÑÛÊ= 66666666666666666¯ÊžææææææææææææææææææææææææÖÖÖ®®®æææææææææææææææææææææMMMÞÞÞæææJJJÞÞÞææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÏÏÏ[[[ÍÍÍææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ–––æææ»Ð­D”6666666666666666¯ÊžÏÚÈÕÝÏæææææææææææææææææææææ ŽŽŽææææææææææææææææææÀÀÀwwwææææææµµµææææææææææææææææææææææææææææææææææææææææææææææææææææææÃÃÃ```PPPæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ¶¶¶ææææææÐÛÉV*666@’޹s޹s޹s޹s޹s޹s޹s޹s޹s޹s޹sÊØÂºÐ¬I–ÃÔ¸ææææææææææææææææææAAA€€€ææææææææææææææææææNNN ×××æææææææææ:::ÞÞÞææææææææææææææææææææææææææææææææææææææææææää䟟Ÿ>>> ËËËæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÅÅÅæææææææææÞâÜq«M66K˜ææææææææææææææææææææææææææææææææææææºÐ¬6<ªÈ˜æææææææææææææææNNNsssæææææææææææææææÆÆÆfffææææææææææææ¤¤¤………ææææææææææææææææææææææææææææææææææææÞÞÞ………”””ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÆÆÆææææææææææææåæå‘»w7K˜ææææææææææææææææææææææææææææææææææææºÐ¬666޹såæåæææææææææNNNsssæææææææææææææææVVVËËËææææææææææææååå)))áááæææææææææææææææææææææææææææãã㊊Šzzzæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ···ææææææææææææææææææ°Ë U)æææm©G666666666666666oªKßãÝææææææBBB€€€ææææææææææææÈÈÈSSSææææææææææææææææææ•••ˆˆˆææææææææææææææææææææææææÂÂÂ222‡‡‡ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ›››æææææææææææææææææææææÔÝÏæææm©G6666666666666666Xž,ÓÜÍæææ###ææææææææææææ]]]½½½ææææææææææææææææææââââââææææææææææææææææææššš )))···æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ<<<pppæææææææææææææææææææææææææææm©G66666666666666666u­RÝÝݲ²²æææææææææËËËBBBææææææææææææææææææææææææ………æææææææææææææææ”””‚‚‚áááææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææaaa888æææææææææææææææææææææææææææm©G6666666666666666ƒ´eäåã§§§×××æææææææææaaa¬¬¬ææææææææææææææææææææææææÞÞÞ âââæææææææææ¶¶¶ ÛÛÛææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ™™™ÏÏÏææææææææææææææææææææææææ¸Ïª¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Ä¤Äˆ·k666:¤Åææææææ\\\'''æææææææææÏÏÏ000æææææææææææææææææææææææææææææætttææææææâââ%%%NNN¨¨¨åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÓÓÓuuuæææææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬66G•ÀÓ´ææææææØØØ eeeææææææææædddšššææææææææææææææææææææææææææææææÕÕÕ %%%äääæææ•••ÕÕÕææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ<<<ØØØææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬6[ 1ÕÝÏæææææææææuuu²²²ææææææÛÛÛ'''333äääæææææææææææææææææææææææææææææææææooo§§§æææ@@@ŠŠŠâââææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææcccææææææææææææææææææææææææææææææææææææææææææææææææææææææºÐ¬w®UáäàæææææææææÊÊÊ åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææãããÄÄÄææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææààদ¦æææææææææææææææææææææææææææææææææææææææææææææææææææàãÞææææææææææææááá444‡‡‡æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÈÈÈ   ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ ½½½ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææåååVVVÞÞÞæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ»»»ãããææææææææææææææææææææææææææææææÛÛÛæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææààà!!!³³³ææææææææææææææææææææææææææææææææææææææææææææææææææææææáááUUU‘‘‘ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ½½½ÜÜÜæææææææææææææææææææææææææææááá%%%NNNÎÎÎææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ©©©ƒƒƒäääæææææææææææææææææææææææææææææææææææææææææææææÆÆÆ333999åååææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÒÒÒ‹‹‹æææææææææææææææææææææææææææwww |||áááæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææhhh111¨¨¨ææææææææææææææææææææææææææææææææææææÔÔÔmmmËËËææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææºººæææææææææææææææææææææ¡¡¡###¨¨¨ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææááá555'''yyy···âââæææææææææææææææÒÒÒšššOOO ¥¥¥æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææUUUtttÓÓÓæææææææææÕÕÕrrrLLLÙÙÙæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ(((!!!222999)))ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¬¬¬###999''')))äääææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÏÏÏ)))‡‡‡ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ888°°°ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ:::šššæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææÁÁÁdddææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââânnn$$$½½½æææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ™™™<<<áááææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ®®®)))kkkÝÝÝæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ˜˜˜<<<ØØØææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââ≉‰RRRÃÃÃææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ¹¹¹'''dddáááæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææâââšššFFFqqqÉÉÉæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææââ∈ˆ @@@±±±ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææØØØ¡¡¡iiiCCC 222TTT………¾¾¾åååæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææååå±±±mmm;;;AAAvvvÂÂÂæææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææopenvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/Product.wxs0000644000000000000000000000013213534540071024013 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.493851303 openvswitch-2.5.9/windows/ovs-windows-installer/Product.wxs0000644000175000017500000003022313534540071025501 0ustar00jpettitjpettit00000000000000 = 602)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> "ALL" AND (&OpenvSwitchDriver = 3)]]> openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/License.rtf0000644000000000000000000000013213534540071023727 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.489851273 openvswitch-2.5.9/windows/ovs-windows-installer/License.rtf0000644000175000017500000011754013534540071025425 0ustar00jpettitjpettit00000000000000{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} {\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f49\fbidi \fswiss\fcharset238\fprq2 Arial CE;}{\f50\fbidi \fswiss\fcharset204\fprq2 Arial Cyr;} {\f52\fbidi \fswiss\fcharset161\fprq2 Arial Greek;}{\f53\fbidi \fswiss\fcharset162\fprq2 Arial Tur;}{\f54\fbidi \fswiss\fcharset177\fprq2 Arial (Hebrew);}{\f55\fbidi \fswiss\fcharset178\fprq2 Arial (Arabic);} {\f56\fbidi \fswiss\fcharset186\fprq2 Arial Baltic;}{\f57\fbidi \fswiss\fcharset163\fprq2 Arial (Vietnamese);}{\f379\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f380\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;} {\f382\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f383\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f386\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;}{\f387\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);} {\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} {\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} {\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} {\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} {\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} {\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} {\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} {\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} {\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} {\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} {\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; \red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\upr{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}{\*\ud\uc0{\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}}}}{\*\rsidtbl \rsid2365717\rsid7145912\rsid7612545}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator alin.cloudbase} {\creatim\yr2015\mo4\dy2\hr16\min46}{\revtim\yr2015\mo5\dy25\hr20\min39}{\version3}{\edmins0}{\nofpages1}{\nofwords86}{\nofchars492}{\nofcharsws577}{\vern57439}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} \paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 \dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot7145912 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1 \pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5 \pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\qj \li0\ri0\nowidctlpar\tx959\tx1918\tx2877\tx3836\tx4795\tx5754\tx6713\tx7672\tx8631\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 Licensed under the Apache License, Version 2.0 (the "License"); \par \hich\af1\dbch\af31505\loch\f1 you may not use this file except in compliance with the License. \par \hich\af1\dbch\af31505\loch\f1 You may obtain a copy of the License at \par \par }{\field{\*\fldinst {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 HYPERLINK http://www.apache.org/licenses/LICENSE-2.0 }{\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid7145912 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6e00000068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f004c004900430045004e00530045002d0032002e00 30000000795881f43b1d7f48af2c825dc485276300000000a5ab000069}}}{\fldrslt {\rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \hich\af1\dbch\af31505\loch\f1 http://www.apache.org/licenses/LICENSE-2.0}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj { \rtlch\fcs1 \af1\afs18 \ltrch\fcs0 \f1\fs18\insrsid2365717 \par \par \hich\af1\dbch\af31505\loch\f1 Un\hich\af1\dbch\af31505\loch\f1 less required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, \par \hich\af1\dbch\af31505\loch\f1 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \par \hich\af1\dbch\af31505\loch\f1 See the License for the specific language governing\hich\af1\dbch\af31505\loch\f1 permissions and \par \hich\af1\dbch\af31505\loch\f1 limitations under the License. \par }\pard \ltrpar\qj \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af1\afs20 \ltrch\fcs0 \f1\fs20\lang16\langfe1033\langnp16\insrsid2365717 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100e67505bed10600008b1a0000160000007468656d652f7468656d652f 7468656d65312e786d6cec59cf8b1b3714be17fa3f0c7377fc6bc63f9638c11edb499b6c1262272547d9963dca6a466624efc68440498e8542695a7a68a0b71e 4adb4002bda4b7fe276953da14fa2ff449331e4bb6dcdd2c292c256b58c6f2f79e3ebdf7e67b9ad1f98bf722ea1ce2841316b7dcf2b992ebe078cc26249eb5dc 5bc37ea1e13a5ca07882288b71cb5d62ee5ebcf0fe7be7d19e0871841db08ff91e6ab9a110f3bd62918f6118f1736c8e63f86dca920809f89acc8a93041d81df 88162ba552ad182112bb4e8c22703bc4d12f8f9cebd3291963f7c2ca798fc20cb1e072604c9381748d330b0d3b39284b045ff28026ce21a22d17e699b0a321be 275c87222ee087965b527f6ef1c2f922dacb8ca8d861abd9f5d55f6697194c0e2a6ace6436ca27f53cdfabb573ff0a40c536ae57efd57ab5dc9f02a0f118569a 72d17dfa9d66a7eb67580d945e5a7c77ebdd6ad9c06bfeab5b9cdbbefc1878054afd7b5bf87e3f80281a78054af1fe16def3ea95c033f00a94e26b5bf87aa9dd f5ea065e81424ae2832d74c9af5583d56a73c894d1cb5678d3f7faf54ae67c8d826ac8ab4b4e3165b1d8556b11bacb923e002490224162472ce7788ac650c501 a2649410e72a998550787314330ec3a54aa95faac27ff9f1d4958a08dac348b396bc8009df1a927c1c3e4ec85cb4dc0fc1abab41fe7ef1fddf2f9e392f1f3e7f f9f0a7978f1ebd7cf863eac8b0ba8ce2996ef5fadbcffe7af2b1f3e7b36f5e3ffec28ee73afeb71f3ef9f5e7cfed4058e93a04afbe7cfafbf3a7afbefaf48fef 1e5be0ed048d74f89044983bd7f091739345b030150293391e256f66310c11d12ddaf18ca318c9592cfe7b2234d0d79688220bae83cd08de4e40626cc04b8bbb 06e141982c04b178bc124606709f31da6189350a57e45c5a98878b78669f3c59e8b89b081ddae60e506ce4b7b79883b6129bcb20c406cd1b14c502cd708c8523 7f6307185b5677871023aefb649c30cea6c2b9439c0e22d6900cc9c8a8a6b5d16512415e963682906f2336fbb79d0ea3b65577f1a18984bb02510bf921a64618 2fa1854091cde51045540ff85524421bc9c13219ebb81e1790e919a6cce94d30e7369beb09ac574bfa1590177bdaf7e9323291892007369f5711633ab2cb0e82 1045731b7640e250c77ec00fa044917383091b7c9f997788fc0e7940f1ce74df26d848f7f16a700b9455a7b42e10f9cb22b1e4f2126646fd0e96748ab0921a10 7e43cf23121f2bee1bb2eeffb7b20e42faeaeb2796559d55416f27c47a475dde90f15db84df10e583221675fbbbb6811dfc070bb6c37b077d2fd4ebaddffbd74 efba9fdfbe60af351ae45b6e15d3adbadab8473bf7ed5342e9402c29becad5d69d43679af46150daa967569c3fc7cd43b89477324c60e0660952364ec2c44744 848310cd617f5f76a59319cf5ccfb833671cb6fd6ad8ea5be2e922da6793f471b55c968fa6a9787024d6e3253f1f87470d91a26bf5f52358ee5eb19da947e515 0169fb2624b4c94c12550b89fa6a5006493d9843d02c24d4cade0a8ba6854543ba5fa56a8b0550cbb3025b2707365c2dd7f7c0048ce0890a513c91794a53bdca ae4ae6dbccf4ae601a1500fb885505ac33dd945c772e4fae2e2db51364da20a1959b49424546f5301ea209ceaa538e9e84c69be6bab94ea9414f8642cd07a5b5 a6516ffc1b8bd3e61aec36b581c6ba52d0d8396ab9b5aa0f253346f3963b85c77eb88ce6503b5c6e79119dc1bbb3b148d21bfe34ca324fb8e8221ea60157a293 aa4144044e1c4aa2962b979fa781c64a4314b7720504e1cc926b82ac9c3572907433c9783ac563a1a75d1b91914ebf82c2a75a61fd55999f1e2c2dd902d23d08 2747ce882e929b084accaf9765002784c3db9f721acd0981d799b990adeb6fa33165b2abbf4f5435948e233a0f51d65174314fe14aca733aea5b1e03ed5bb666 08a81692ac118e66b2c1ea4135ba69de35520e3bbbeef14632729a68ae7ba6a12ab26bda55cc9861d506366279ba26afb15a8518344deff0a9746f4a6e73a575 1bfb84bc4b40c0f3f859baee091a82466d3d99414d32de9661a9d9d9a8d93b560b3c86da499a84a6fab595db8db8e53dc23a1d0c9eaaf383dd66d5c2d074b5af 549156e71efad1041bdd05f1e8c24be005155ca5120e1e12041ba281da93a4b201b7c83d91dd1a70e52c12d272ef97fcb61754fca0506af8bd8257f54a8586df ae16dabe5f2df7fc72a9dba93c80c622c2a8eca7672e7d78154597d9c98b1adf3a7d89566fdbce8d595464ea5ca5a888abd39772c5387d49cf529ca13c5e711d 02a273bf56e937abcd4eadd0acb6fb05afdb69149a41ad53e8d6827ab7df0dfc46b3ffc0750e15d86b5703afd66b146ae5202878b592a4df6816ea5ea5d2f6ea ed46cf6b3fc8b631b0f2543eb258407815af0bff000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f74 68656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f24 51eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198 720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528 a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000 000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000000000000000 00300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468 656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100e67505bed10600008b1a000016000000000000000000 00000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000270000000000 0000000000000000db0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000d60a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} {\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; \lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; \lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; \lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}{\*\datastore 010500000200000018000000 4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000000ca 63ca1197d001feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}}openvswitch-2.5.9/windows/ovs-windows-installer/PaxHeaders.82075/Actions0000644000000000000000000000013213534540120023146 xustar0030 mtime=1567801424.481851214 30 atime=1567801425.625859648 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/ovs-windows-installer/Actions/0000755000175000017500000000000013534540120024711 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/windows/ovs-windows-installer/Actions/PaxHeaders.82075/OVSActions.js0000644000000000000000000000013213534540071025556 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/ovs-windows-installer/Actions/OVSActions.js0000644000175000017500000001626613534540071027257 0ustar00jpettitjpettit00000000000000/* Copyright 2015 Cloudbase Solutions Srl All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx var Buttons = { OkOnly: 0, OkCancel: 1, AbortRetryIgnore: 2, YesNoCancel: 3 }; var Icons = { Critical: 16, Question: 32, Exclamation: 48, Information: 64 } var MsgKind = { Error: 0x01000000, Warning: 0x02000000, User: 0x03000000, Log: 0x04000000 }; // http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx var MsiActionStatus = { None: 0, Ok: 1, // success Cancel: 2, Abort: 3, Retry: 4, // aka suspend? Ignore: 5 // skip remaining actions; this is not an error. }; var ServiceStartAction = { Stop: "Stop", Start: "Start", Restart: "Restart" }; var ServiceStartMode = { Boot: "Boot", System: "System", Auto: "Auto", Manual: "Manual", Disabled: "Disabled" }; function throwException(num, msg) { throw { number: num, message: msg }; } function decimalToHexString(number) { if (number < 0) number = 0xFFFFFFFF + number + 1; return number.toString(16).toUpperCase(); } function logMessage(msg) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = "CustomActions: " + msg; Session.Message(MsgKind.Log, record); } function logMessageEx(msg, type) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = msg; Session.Message(type, record); } function logException(exc) { var record = Session.Installer.CreateRecord(0); record.StringData(0) = exc.message == "" ? "An exception occurred: 0x" + decimalToHexString(exc.number) : exc.message; Session.Message(MsgKind.Error + Icons.Critical + Buttons.OkOnly, record); // Log the full exception as well record.StringData(0) = "CustomAction exception details: 0x" + decimalToHexString(exc.number) + " : " + exc.message; Session.Message(MsgKind.Log, record); } function runCommand(cmd, expectedReturnValue, envVars, windowStyle, waitOnReturn, workingDir) { var shell = new ActiveXObject("WScript.Shell"); logMessage("Running command: " + cmd); if (envVars) { var env = shell.Environment("Process"); for (var k in envVars) env(k) = envVars[k]; } if (typeof windowStyle == 'undefined') windowStyle = 0; if (typeof waitOnReturn == 'undefined') waitOnReturn = true; if (typeof workingDir == 'undefined') workingDir = null; if (workingDir) { shell.CurrentDirectory = workingDir; } var retVal = shell.run(cmd, windowStyle, waitOnReturn); if (waitOnReturn && expectedReturnValue != undefined && expectedReturnValue != null && retVal != expectedReturnValue) throwException(-1, "Command failed. Return value: " + retVal.toString()); logMessage("Command completed. Return value: " + retVal); return retVal; } function getWmiCimV2Svc() { return GetObject("winmgmts:\\\\.\\root\\cimv2"); } function getSafeArray(jsArr) { var dict = new ActiveXObject("Scripting.Dictionary"); for (var i = 0; i < jsArr.length; i++) dict.add(i, jsArr[i]); return dict.Items(); } function invokeWMIMethod(svc, methodName, inParamsValues, wmiSvc, jobOutParamName) { logMessage("Invoking " + methodName); var inParams = null; if (inParamsValues) { for (var k in inParamsValues) { if (!inParams) inParams = svc.Methods_(methodName).InParameters.SpawnInstance_(); var val = inParamsValues[k]; if (val instanceof Array) inParams[k] = getSafeArray(val); else inParams[k] = val; } } var outParams = svc.ExecMethod_(methodName, inParams); if (outParams.ReturnValue == 4096) { var job = wmiSvc.Get(outParams[jobOutParamName]); waitForJob(wmiSvc, job); } else if (outParams.ReturnValue != 0) throwException(-1, methodName + " failed. Return value: " + outParams.ReturnValue.toString()); return outParams; } function sleep(interval) { // WScript.Sleep is not supported in MSI's WSH. Here's a workaround for the moment. // interval is ignored var numPings = 2; cmd = "ping -n " + numPings + " 127.0.0.1"; var shell = new ActiveXObject("WScript.Shell"); shell.run(cmd, 0, true); } function getService(serviceName) { var wmiSvc = getWmiCimV2Svc(); return wmiSvc.ExecQuery("SELECT * FROM Win32_Service WHERE Name='" + serviceName + "'").ItemIndex(0); } function changeService(serviceName, startMode, startAction) { var svc = getService(serviceName); if ((startAction == ServiceStartAction.Stop || startAction == ServiceStartAction.Restart) && svc.Started) invokeWMIMethod(svc, "StopService"); if (startMode && svc.StartMode != startMode) invokeWMIMethod(svc, "ChangeStartMode", { "StartMode": (startMode == ServiceStartMode.Auto ? "Automatic" : startMode) }); if (startAction == ServiceStartAction.Restart && svc.Started) { var wmiSvc = getWmiCimV2Svc(); do { sleep(200); svc = wmiSvc.Get(svc.Path_); } while (svc.Started); } if ((startAction == ServiceStartAction.Start || startAction == ServiceStartAction.Restart) && !svc.Started) invokeWMIMethod(svc, "StartService"); } function runCommandAction() { var exceptionMsg = null; try { var data = Session.Property("CustomActionData").split('|'); var i = 0; var cmd = data[i++]; var expectedRetValue = data.length > i ? data[i++] : 0; var exceptionMsg = data.length > i ? data[i++] : null; var workingDir = data.length > i ? data[i++] : null; runCommand(cmd, expectedRetValue, null, 0, true, workingDir); return MsiActionStatus.Ok; } catch (ex) { if (exceptionMsg) { logMessageEx(exceptionMsg, MsgKind.Error + Icons.Critical + Buttons.OkOnly); // log also the original exception logMessage(ex.message); } else logException(ex); return MsiActionStatus.Abort; } } function changeServiceAction() { try { var data = Session.Property("CustomActionData").split('|'); var serviceName = data[0]; var startMode = data[1]; var startAction = data[2]; logMessage("Changing service " + serviceName + ", startMode: " + startMode + ", startAction: " + startAction); changeService(serviceName, startMode, startAction); return MsiActionStatus.Ok; } catch (ex) { logMessage(ex.message); return MsiActionStatus.Abort; } }openvswitch-2.5.9/windows/PaxHeaders.82075/.gitignore0000644000000000000000000000013213534540071017326 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.477851186 openvswitch-2.5.9/windows/.gitignore0000644000175000017500000000540413534540071021017 0ustar00jpettitjpettit00000000000000## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates ovs-windows-installer/Binaries.wxs ovs-windows-installer/Symbols.wxs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ x64/ build/ bld/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* #NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding addin-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* _NCrunch_* .*crunch*.local.xml # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # NuGet Packages Directory packages/* ## TODO: If the tool you use requires repositories.config uncomment the next line #!packages/repositories.config # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) !packages/build/ # Windows Azure Build Output csx/ *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # ========================= # Windows detritus # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ openvswitch-2.5.9/windows/PaxHeaders.82075/ovs-windows-installer.sln0000644000000000000000000000013213534540071022346 xustar0030 mtime=1567801401.969685341 30 atime=1567801402.141686605 30 ctime=1567801424.481851214 openvswitch-2.5.9/windows/ovs-windows-installer.sln0000644000175000017500000000171313534540071024036 0ustar00jpettitjpettit00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ovs-windows-installer", "ovs-windows-installer\ovs-windows-installer.wixproj", "{259905A2-7434-4190-8A33-8FBA67171DD6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x64.ActiveCfg = Release|x86 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x64.Build.0 = Release|x86 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x86.ActiveCfg = Release|x86 {259905A2-7434-4190-8A33-8FBA67171DD6}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal openvswitch-2.5.9/PaxHeaders.82075/AUTHORS0000644000000000000000000000013213534540071014714 xustar0030 mtime=1567801401.177679525 30 atime=1567801402.037685841 30 ctime=1567801424.649852453 openvswitch-2.5.9/AUTHORS0000644000175000017500000004675113534540071016417 0ustar00jpettitjpettit00000000000000The following people authored or signed off on commits in the Open vSwitch source code or webpage version control repository. Aaron Conole aconole@redhat.com Aaron Rosen arosen@clemson.edu Alexander Duyck alexander.h.duyck@redhat.com Alexandru Copot alex.mihai.c@gmail.com Alexei Starovoitov ast@plumgrid.com Alexey I. Froloff raorn@raorn.name Alex Wang ee07b291@gmail.com Alfredo Finelli alf@computationes.de Alin Serdean aserdean@cloudbasesolutions.com Ambika Arora ambika.arora@tcs.com Amit Bose bose@noironetworks.com Andrew Evans aevans@nicira.com Andrew Kampjes a.kampjes@gmail.com Andrew Lambeth wal@nicira.com Andy Hill hillad@gmail.com Andy Southgate andy.southgate@citrix.com Andy Zhou azhou@ovn.org Ankur Sharma ankursharma@vmware.com Anoob Soman anoob.soman@citrix.com Ansis Atteka aatteka@nicira.com Anupam Chanda achanda@nicira.com Ariel Tubaltsev atubaltsev@vmware.com Arun Sharma arun.sharma@calsoftinc.com Aryan TaheriMonfared aryan.taherimonfared@uis.no Ashwin Swaminathan ashwinds@arista.com Babu Shanmugam bschanmu@redhat.com Ben Pfaff blp@ovn.org Benli Ye daniely@vmware.com Bert Vermeulen bert@biot.com Billy O'Mahony billy.o.mahony@intel.com Brian Kruger bkruger+ovsdev@gmail.com Bruce Davie bsd@nicira.com Bryan Phillippe bp@toroki.com Casey Barker crbarker@google.com Christoph Jaeger cj@linux.com Chris Wright chrisw@sous-sol.org Chuck Short zulcss@ubuntu.com Ciara Loftus ciara.loftus@intel.com Cong Wang amwang@redhat.com Damien Millescamps damien.millescamps@6wind.com Dan Carpenter dan.carpenter@oracle.com Dan McGregor dan.mcgregor@usask.ca Dan Wendlandt dan@nicira.com Daniel Borkmann dborkman@redhat.com Daniel Hiltgen daniel@netkine.com Daniel Roman droman@nicira.com Daniele Di Proietto daniele.di.proietto@gmail.com Daniele Venturino daniele.venturino@m3s.it Danny Kukawka danny.kukawka@bisect.de Dave Tucker dave@dtucker.co.uk David Erickson derickso@stanford.edu David Hill dhill@redhat.com David S. Miller davem@davemloft.net David Yang davidy@vmware.com Devendra Naga devendra.aaru@gmail.com Dmitry Krivenok krivenok.dmitry@gmail.com Dominic Curran dominic.curran@citrix.com Dongdong dongdong1@huawei.com Duffie Cooley dcooley@nicira.com Ed Maste emaste@freebsd.org Ed Swierk eswierk@skyportsystems.com Edouard Bourguignon madko@linuxed.net Aymerich Edward edward.aymerich@hpe.com Edward Tomasz NapieraÅ‚a trasz@freebsd.org Eitan Eliahu eliahue@vmware.com Eohyung Lee liquidnuker@gmail.com Eric Sesterhenn eric.sesterhenn@lsexperts.de Ethan J. Jackson ejj@eecs.berkeley.edu Flavio Leitner fbl@redhat.com Francesco Fusco ffusco@redhat.com FUJITA Tomonori fujita.tomonori@lab.ntt.co.jp Gaetano Catalli gaetano.catalli@gmail.com Gal Sagie gal.sagie@gmail.com Geoffrey Wossum gwossum@acm.org Gianluca Merlo gianluca.merlo@gmail.com Giuseppe Lettieri g.lettieri@iet.unipi.it Glen Gibb grg@stanford.edu Guolin Yang gyang@nicira.com Guru Chaitanya Perakam gperakam@Brocade.com Gurucharan Shetty guru@ovn.org Han Zhou zhouhan@gmail.com Henry Mai hmai@nicira.com Hao Zheng hzheng@nicira.com Helmut Schaa helmut.schaa@googlemail.com Hiteshi Kalra hiteshi.kalra@tcs.com Huanle Han hanxueluo@gmail.com Ian Campbell Ian.Campbell@citrix.com Ian Stokes ian.stokes@intel.com Ilya Maximets i.maximets@samsung.com Isaku Yamahata yamahata@valinux.co.jp James P. roampune@gmail.com James Page james.page@ubuntu.com Jan Scheurich jan.scheurich@ericsson.com Jan Vansteenkiste jan@vstone.eu Jarno Rajahalme jarno@ovn.org Jason Kölker jason@koelker.net Jasper Capel jasper@capel.tv Jean Tourrilhes jt@hpl.hp.com Jeremy Stribling strib@nicira.com Jeroen van Bemmel jvb127@gmail.com Jesse Gross jesse@kernel.org Jing Ai jinga@google.com Jiri Benc jbenc@redhat.com Joe Perches joe@perches.com Joe Stringer joe@ovn.org Jonathan Vestin jonavest@kau.se Jun Nakajima jun.nakajima@intel.com Justin Pettit jpettit@ovn.org Keith Amidon keith@nicira.com Ken Ajiro ajiro@mxw.nes.nec.co.jp Kenneth Duda kduda@arista.com Kentaro Ebisawa ebiken.g@gmail.com Kevin Lo kevlo@FreeBSD.org Kevin Traynor kevin.traynor@intel.com Kmindg G kmindg@gmail.com Krishna Kondaka kkondaka@vmware.com Kyle Mestery mestery@mestery.com Kyle Upton kupton@baymicrosystems.com Lars Kellogg-Stedman lars@redhat.com Lei Huang huang.f.lei@gmail.com Leo Alterman lalterman@nicira.com Lilijun jerry.lilijun@huawei.com Linda Sun lsun@vmware.com Lior Neudorfer lior@guardicore.com Lorand Jakab lojakab@cisco.com Luca Giraudo lgiraudo@nicira.com Lucian Petrut lpetrut@cloudbasesolutions.com Luigi Rizzo rizzo@iet.unipi.it Luis E. P. l31g@hotmail.com Madhu Challa challa@noironetworks.com Mark D. Gray mark.d.gray@intel.com Mark Hamilton mhamilton@nicira.com Mark Kavanagh mark.b.kavanagh@intel.com Mark Maglana mmaglana@gmail.com Martin Casado casado@nicira.com Martino Fornasa mf@fornasa.it Maryam Tahhan maryam.tahhan@intel.com Mauricio Vásquez mauricio.vasquezbernal@studenti.polito.it Mehak Mahajan mmahajan@nicira.com Michal Weglicki michalx.weglicki@intel.com Mika Vaisanen mika.vaisanen@gmail.com Mijo Safradin mijo@linux.vnet.ibm.com Minoru TAKAHASHI takahashi.minoru7@gmail.com Murphy McCauley murphy.mccauley@gmail.com Natasha Gude natasha@nicira.com Neil McKee neil.mckee@inmon.com Neil Zhu zhuj@centecnetworks.com Nithin Raju nithin@vmware.com Niti Rohilla niti.rohilla@tcs.com Numan Siddique nusiddiq@redhat.com Padmanabhan Krishnan kprad1@yahoo.com Panu Matilainen pmatilai@redhat.com Paraneetharan Chandrasekaran paraneetharanc@gmail.com Paul Fazzone pfazzone@nicira.com Paul Ingram paul@nicira.com Paul-Emmanuel Raoul skyper@skyplabs.net Pavithra Ramesh paramesh@vmware.com Philippe Jung phil.jung@free.fr Pim van den Berg pim@nethuis.nl pritesh pritesh.kothari@cisco.com Pravin B Shelar pshelar@nicira.com Raju Subramanian rsubramanian@nicira.com Rami Rosen ramirose@gmail.com Randall Sharo andall.sharo@navy.mil Ravi Kerur Ravi.Kerur@telekom.com Reid Price reid@nicira.com Remko Tronçon git@el-tramo.be Rich Lane rlane@bigswitch.com Rishi Bamba rishi.bamba@tcs.com Rob Adams readams@readams.net Robert Ã…kerblom-Andersson Robert.nr1@gmail.com Rob Hoes rob.hoes@citrix.com Romain Lenglet romain.lenglet@berabera.info Russell Bryant russell@ovn.org Ryan Wilson wryan@nicira.com Sairam Venugopal vsairam@vmware.com Sajjad Lateef slateef@nicira.com Saloni Jain saloni.jain@tcs.com Samuel Ghinet sghinet@cloudbasesolutions.com Sanjay Sane ssane@nicira.com Saurabh Mohan saurabh@cplanenetworks.com Saurabh Shah ssaurabh@nicira.com Scott Lowe scott.lowe@scottlowe.org Scott Mann sdmnix@gmail.com Selvamuthukumar smkumar@merunetworks.com Shad Ansari shad.ansari@hpe.com Shan Wei davidshan@tencent.com Shashwat Srivastava shashwat.srivastava@tcs.com Shih-Hao Li shli@nicira.com Shu Shen shu.shen@radisys.com Simon Horman horms@verge.net.au Simon Horman simon.horman@netronome.com Sorin Vinturis svinturis@cloudbasesolutions.com Steffen Gebert steffen.gebert@informatik.uni-wuerzburg.de Sten Spans sten@blinkenlights.nl Stephane A. Sezer sas@cd80.net Stephen Finucane stephen.finucane@intel.com SUGYO Kazushi sugyo.org@gmail.com Tadaaki Nagao nagao@stratosphere.co.jp Terry Wilson twilson@redhat.com Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp Thadeu Lima de Souza Cascardo cascardo@redhat.com Thomas F. Herbert thomasfherbert@gmail.com Thomas Goirand zigo@debian.org Thomas Graf tgraf@noironetworks.com Thomas Lacroix thomas.lacroix@citrix.com Timo Puha timox.puha@intel.com Todd Deshane deshantm@gmail.com Tom Everman teverman@google.com Torgny Lindberg torgny.lindberg@ericsson.com Tsvi Slonim tsvi@toroki.com Tuan Nguyen tuan.nguyen@veriksystems.com Tyler Coumbes coumbes@gmail.com Valient Gough vgough@pobox.com Vivien Bernet-Rollande vbr@soprive.net Wang Sheng-Hui shhuiw@gmail.com Wei Li liw@dtdream.com Wei Yongjun yjwei@cn.fujitsu.com Wenyu Zhang wenyuz@vmware.com William Fulton William Tu u9012063@gmail.com YAMAMOTO Takashi yamamoto@midokura.com Yasuhito Takamiya yasuhito@gmail.com Yin Lin linyi@vmware.com Yu Zhiguo yuzg@cn.fujitsu.com Yuanhan Liu yuanhan.liu@linux.intel.com ZhengLingyun konghuarukhr@163.com Zoltán Balogh zoltan.balogh@ericsson.com Zoltan Kiss zoltan.kiss@citrix.com Zhi Yong Wu zwu.kernel@gmail.com Zang MingJie zealot0630@gmail.com xushengping shengping.xu@huawei.com yinpeijun yinpeijun@huawei.com The following additional people are mentioned in commit logs as having provided helpful bug reports or suggestions. Aaron M. Ucko ucko@debian.org Abhinav Singhal Abhinav.Singhal@spirent.com Adam Heath doogie@brainfood.com Ahmed Bilal numan252@gmail.com Alan Shieh ashieh@nicira.com Alban Browaeys prahal@yahoo.com Alex Yip alex@nicira.com Alexey I. Froloff raorn@altlinux.org Amar Padmanabhan amar@nicira.com Amey Bhide abhide@nicira.com Amre Shakimov ashakimov@vmware.com André Ruß andre.russ@hybris.com Andreas Beckmann debian@abeckmann.de Andrei Andone andrei.andone@softvision.ro Andrey Korolyov andrey@xdel.ru Anshuman Manral anshuman.manral@outlook.com Anton Matsiuk anton.matsiuk@gmail.com Anup Khadka khadka.py@gmail.com Anuprem Chalvadi achalvadi@vmware.com Ariel Tubaltsev atubaltsev@vmware.com Arkajit Ghosh arkajit.ghosh@tcs.com Atzm Watanabe atzm@stratosphere.co.jp Aurélien Poulain aurepoulain@viacesi.fr Bastian Blank waldi@debian.org Ben Basler bbasler@nicira.com Bob Ball bob.ball@citrix.com Brad Hall brad@nicira.com Brandon Heller brandonh@stanford.edu Brendan Kelley bkelley@nicira.com Brent Salisbury brent.salisbury@gmail.com Brian Field Brian_Field@cable.comcast.com Bryan Fulton bryan@nicira.com Bryan Osoro bosoro@nicira.com Cedric Hobbs cedric@nicira.com Chris Hydon chydon@aristanetworks.com Christian Stigen Larsen cslarsen@gmail.com Christopher Paggen cpaggen@cisco.com Chunhe Li lichunhe@huawei.com Daniel Badea daniel.badea@windriver.com Darragh O'Reilly darragh.oreilly@hpe.com Dave Walker DaveWalker@ubuntu.com David Evans davidjoshuaevans@gmail.com David Palma palma@onesource.pt Derek Cormier derek.cormier@lab.ntt.co.jp Dhaval Badiani dbadiani@vmware.com DK Moon dkmoon@nicira.com Ding Zhi zhi.ding@6wind.com Edwin Chiu echiu@vmware.com Eivind Bulie Haanaes Eric Lopez elopez@nicira.com Frido Roose fr.roose@gmail.com Gaetano Catalli gaetano.catalli@gmail.com Gavin Remaley gavin_remaley@selinc.com George Shuklin amarao@desunote.ru Gerald Rogers gerald.rogers@intel.com Ghanem Bahri bahri.ghanem@gmail.com Giuseppe de Candia giuseppe.decandia@gmail.com Gordon Good ggood@nicira.com Greg Dahlman gdahlman@hotmail.com Gregor Schaffrath grsch@net.t-labs.tu-berlin.de Gregory Smith gasmith@nutanix.com Guolin Yang gyang@vmware.com Gur Stavi gstavi@mrv.com Hari Sasank Bhamidipalli hbhamidi@cisco.com Hassan Khan hassan.khan@seecs.edu.pk Hector Oron hector.oron@gmail.com Hemanth Kumar Mantri mantri@nutanix.com Henrik Amren henrik@nicira.com Hiroshi Tanaka htanaka@nicira.com Hiroshi Miyata miyahiro.dazu@gmail.com Hyojoon Kim joonk@gatech.edu Igor Ganichev iganichev@nicira.com Igor Sever igor@xorops.com Jacob Cherkas jcherkas@nicira.com Jad Naous jnaous@gmail.com Jamal Hadi Salim hadi@cyberus.ca James Schmidt jschmidt@nicira.com Jan Medved jmedved@juniper.net Janis Hamme janis.hamme@student.kit.edu Jari Sundell sundell.software@gmail.com Javier Albornz javier.albornoz@hpe.com Jed Daniels openvswitch@jeddaniels.com Jeff Merrick jmerrick@vmware.com Jeongkeun Lee jklee@hp.com Jian Qiu swordqiu@gmail.com Joan Cirer joan@ev0.net John Darrington john@darrington.wattle.id.au John Galgay john@galgay.net John Hurley john.hurley@netronome.com John Reumann nofutznetworks@gmail.com Keith Holleman hollemanietf@gmail.com K è¯ k940545@hotmail.com Kevin Mancuso kevin.mancuso@rackspace.com Kiran Shanbhog kiran@vmware.com Kirill Kabardin Kirkland Spector kspector@salesforce.com Koichi Yagishita yagishita.koichi@jrc.co.jp Konstantin Khorenko khorenko@openvz.org Kris zhang zhang.kris@gmail.com Krishna Miriyala krishna@nicira.com Len Gao leng@vmware.com Logan Rosen logatronico@gmail.com Luca Falavigna dktrkranz@debian.org Luiz Henrique Ozaki luiz.ozaki@gmail.com Manpreet Singh er.manpreet25@gmail.com Marco d'Itri md@Linux.IT Martin Vizvary vizvary@ics.muni.cz Marvin Pascual marvin@pascual.com.ph Maxime Brun m.brun@alphalink.fr Madhu Venugopal mavenugo@gmail.com Michael A. Collins mike.a.collins@ark-net.org Michael Hu mhu@nicira.com Michael J. Smalley michaeljsmalley@gmail.com Michael Mao mmao@nicira.com Michael Shigorin mike@osdn.org.ua Mihir Gangar gangarm@vmware.com Mike Bursell mike.bursell@citrix.com Mike Kruze mkruze@nicira.com Mike Qing mqing@vmware.com Min Chen ustcer.tonychan@gmail.com Mikael Doverhag mdoverhag@nicira.com Mrinmoy Das mrdas@ixiacom.com Murali R muralirdev@gmail.com Nagi Reddy Jonnala njonnala@Brocade.com Niels van Adrichem N.L.M.vanAdrichem@tudelft.nl Niklas Andersson nandersson@nicira.com Pankaj Thakkar thakkar@nicira.com Pasi Kärkkäinen pasik@iki.fi Patrik Andersson R patrik.r.andersson@ericsson.com Paulo Cravero pcravero@as2594.net Pawan Shukla shuklap@vmware.com Peter Amidon peter@picnicpark.org Peter Balland peter@nicira.com Peter Phaal peter.phaal@inmon.com Prabina Pattnaik Prabina.Pattnaik@nechclst.in Pratap Reddy preddy@nicira.com Ralf Heiringhoff ralf@frosty-geek.net Ram Jothikumar rjothikumar@nicira.com Ramana Reddy gtvrreddy@gmail.com Ray Li rayli1107@gmail.com RishiRaj Maulick rishi.raj2509@gmail.com Rob Sherwood rob.sherwood@bigswitch.com Robert Strickler anomalyst@gmail.com Roger Leigh rleigh@codelibre.net Rogério Vinhal Nunes Roman Sokolkov rsokolkov@gmail.com Ronaldo A. Ferreira ronaldof@CS.Princeton.EDU Ronny L. Bull bullrl@clarkson.edu Sandeep Kumar sandeep.kumar16@tcs.com Sander Eikelenboom linux@eikelenboom.it Saul St. John sstjohn@cs.wisc.edu Saurabh Shrivastava (सौरभ शà¥à¤°à¥€à¤µà¤¾à¤¸à¥à¤¤à¤µ) saurabh@gmail.com Scott Hendricks shendricks@nicira.com Sean Brady sbrady@gtfservices.com Sebastian Andrzej Siewior sebastian@breakpoint.cc Sébastien RICCIO sr@swisscenter.com Simon Jouet simon.jouet@gmail.com Spiro Kourtessis spiro@vmware.com Sridhar Samudrala samudrala.sridhar@gmail.com Srini Seetharaman seethara@stanford.edu Sabyasachi Sengupta Sabyasachi.Sengupta@alcatel-lucent.com Salvatore Cambria salvatore.cambria@citrix.com Soner Sevinc sevincs@vmware.com Stephen Hemminger shemminger@vyatta.com Suganya Ramachandran suganyar@vmware.com Takayuki HAMA t-hama@cb.jp.nec.com Teemu Koponen koponen@nicira.com Thomas Morin thomas.morin@orange.com Timothy Chen tchen@nicira.com Torbjorn Tornkvist kruskakli@gmail.com Tytus Kurek Tytus.Kurek@pega.com Valentin Bud valentin@hackaserver.com Vasiliy Tolstov v.tolstov@selfip.ru Vasu Dasari vdasari@gmail.com Vinllen Chen cvinllen@gmail.com Vishal Swarankar vishal.swarnkar@gmail.com Vjekoslav Brajkovic balkan@cs.washington.edu Voravit T. voravit@kth.se Yeming Zhao zhaoyeming@gmail.com Yi Ba yby.developer@yahoo.com Ying Chen yingchen@vmware.com Yongqiang Liu liuyq7809@gmail.com ZHANG Zhiming zhangzhiming@yunshan.net.cn Zhangguanghui zhang.guanghui@h3c.com Ziyou Wang ziyouw@vmware.com Zoltán Balogh zoltan.balogh@ericsson.com ankur dwivedi ankurengg2003@gmail.com chen zhang 3zhangchen9211@gmail.com james hopper jameshopper@email.com kk yap yapkke@stanford.edu likunyun kunyunli@hotmail.com meishengxin meishengxin@huawei.com neeraj mehta mehtaneeraj07@gmail.com rahim entezari rahim.entezari@gmail.com weizj 34965317@qq.com 俊 èµµ zhaojun12@outlook.com 冯全树(Crab) fqs888@126.com 张东亚 fortitude.zhang@gmail.com 胡é–飞 hujingfei914@msn.com 张伟 zhangwqh@126.com 张强 zhangqiang@meizu.com Thanks to all Open vSwitch contributors. If you are not listed above but believe that you should be, please write to dev@openvswitch.org. openvswitch-2.5.9/PaxHeaders.82075/package.m40000644000000000000000000000013213534540111015474 xustar0030 mtime=1567801417.397798992 30 atime=1567801417.465799494 30 ctime=1567801424.081848266 openvswitch-2.5.9/package.m40000644000175000017500000000044213534540111017162 0ustar00jpettitjpettit00000000000000# Signature of the current package. m4_define([AT_PACKAGE_NAME], [openvswitch]) m4_define([AT_PACKAGE_TARNAME], [openvswitch]) m4_define([AT_PACKAGE_VERSION], [2.5.9]) m4_define([AT_PACKAGE_STRING], [openvswitch 2.5.9]) m4_define([AT_PACKAGE_BUGREPORT], [bugs@openvswitch.org]) openvswitch-2.5.9/PaxHeaders.82075/README-native-tunneling.md0000644000000000000000000000013213534540071020410 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.045685899 30 ctime=1567801423.693845405 openvswitch-2.5.9/README-native-tunneling.md0000644000175000017500000000646613534540071022112 0ustar00jpettitjpettit00000000000000Native Tunneling in Open vSwitch userspace ------------------------------------------ Open vSwitch supports tunneling in userspace. Tunneling is implemented in platform independent way. Setup: ====== Setup physical bridges for all physical interfaces. Create integration bridge. Add VXLAN port to int-bridge. Assign IP address to physical bridge where VXLAN traffic is expected. Example: ======== Connect to VXLAN tunnel endpoint logical ip: 192.168.1.2 and 192.168.1.1. Configure OVS bridges as follows. 1. Lets assume 172.168.1.2/24 network is reachable via eth1 create physical bridge br-eth1 assign ip address (172.168.1.1/24) to br-eth1, Add eth1 to br-eth1 2. Check ovs cached routes using appctl command ovs-appctl ovs/route/show Add tunnel route if not present in OVS route table. ovs-appctl ovs/route/add 172.168.1.1/24 br-eth1 3. Add integration bridge int-br and add tunnel port using standard syntax. ovs-vsctl add-port int-br vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=172.168.1.2 4. Assign IP address to int-br, So final topology looks like: 192.168.1.1/24 +--------------+ | int-br | 192.168.1.2/24 +--------------+ +--------------+ | vxlan0 | | vxlan0 | +--------------+ +--------------+ | | | | | | 172.168.1.1/24 | +--------------+ | | br-eth1 | 172.168.1.2/24 +--------------+ +---------------+ | eth1 |----------------------------------| eth1 | +--------------+ +---------------+ Host A with OVS. Remote host. With this setup, ping to VXLAN target device (192.168.1.2) should work There are following commands that shows internal tables: Tunneling related commands: =========================== Tunnel routing table: To Add route: ovs-appctl ovs/route/add / To see all routes configured: ovs-appctl ovs/route/show To del route: ovs-appctl ovs/route/del / To look up and display the route for a destination: ovs-appctl ovs/route/lookup ARP: To see arp cache content: ovs-appctl tnl/arp/show To flush arp cache: ovs-appctl tnl/arp/flush To check tunnel ports listening in vswitchd: ovs-appctl tnl/ports/show To set range for VxLan udp source port: To set: ovs-appctl tnl/egress_port_range Shows Current range: ovs-appctl tnl/egress_port_range To check datapath ports: ovs-appctl dpif/show To check datapath flows: ovs-appctl dpif/dump-flows Contact ======= bugs@openvswitch.org openvswitch-2.5.9/PaxHeaders.82075/vswitchd0000644000000000000000000000013213534540121015416 xustar0030 mtime=1567801425.205856551 30 atime=1567801425.625859648 30 ctime=1567801425.205856551 openvswitch-2.5.9/vswitchd/0000755000175000017500000000000013534540121017161 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/vswitchd/PaxHeaders.82075/ovs-vswitchd.c0000644000000000000000000000013213534540071020303 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801425.201856523 openvswitch-2.5.9/vswitchd/ovs-vswitchd.c0000644000175000017500000001665213534540071022003 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #ifdef HAVE_MLOCKALL #include #endif #include "bridge.h" #include "command-line.h" #include "compiler.h" #include "daemon.h" #include "dirs.h" #include "dpif.h" #include "dummy.h" #include "fatal-signal.h" #include "memory.h" #include "netdev.h" #include "openflow/openflow.h" #include "ovsdb-idl.h" #include "poll-loop.h" #include "simap.h" #include "stream-ssl.h" #include "stream.h" #include "svec.h" #include "timeval.h" #include "unixctl.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "lib/vswitch-idl.h" #include "lib/netdev-dpdk.h" VLOG_DEFINE_THIS_MODULE(vswitchd); /* --mlockall: If set, locks all process memory into physical RAM, preventing * the kernel from paging any of its memory to disk. */ static bool want_mlockall; static unixctl_cb_func ovs_vswitchd_exit; static char *parse_options(int argc, char *argv[], char **unixctl_path); OVS_NO_RETURN static void usage(void); int main(int argc, char *argv[]) { char *unixctl_path = NULL; struct unixctl_server *unixctl; char *remote; bool exiting; int retval; set_program_name(argv[0]); retval = dpdk_init(argc,argv); if (retval < 0) { return retval; } argc -= retval; argv += retval; ovs_cmdl_proctitle_init(argc, argv); service_start(&argc, &argv); remote = parse_options(argc, argv, &unixctl_path); fatal_ignore_sigpipe(); ovsrec_init(); daemonize_start(true); if (want_mlockall) { #ifdef HAVE_MLOCKALL if (mlockall(MCL_CURRENT | MCL_FUTURE)) { VLOG_ERR("mlockall failed: %s", ovs_strerror(errno)); } #else VLOG_ERR("mlockall not supported on this system"); #endif } retval = unixctl_server_create(unixctl_path, &unixctl); if (retval) { exit(EXIT_FAILURE); } unixctl_command_register("exit", "", 0, 0, ovs_vswitchd_exit, &exiting); bridge_init(remote); free(remote); exiting = false; while (!exiting) { memory_run(); if (memory_should_report()) { struct simap usage; simap_init(&usage); bridge_get_memory_usage(&usage); memory_report(&usage); simap_destroy(&usage); } bridge_run(); unixctl_server_run(unixctl); netdev_run(); memory_wait(); bridge_wait(); unixctl_server_wait(unixctl); netdev_wait(); if (exiting) { poll_immediate_wake(); } poll_block(); if (should_service_stop()) { exiting = true; } } bridge_exit(); unixctl_server_destroy(unixctl); service_stop(); return 0; } static char * parse_options(int argc, char *argv[], char **unixctl_pathp) { enum { OPT_PEER_CA_CERT = UCHAR_MAX + 1, OPT_MLOCKALL, OPT_UNIXCTL, VLOG_OPTION_ENUMS, OPT_BOOTSTRAP_CA_CERT, OPT_ENABLE_DUMMY, OPT_DISABLE_SYSTEM, DAEMON_OPTION_ENUMS, OPT_DPDK, }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"mlockall", no_argument, NULL, OPT_MLOCKALL}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT}, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY}, {"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM}, {"dpdk", required_argument, NULL, OPT_DPDK}, {NULL, 0, NULL, 0}, }; char *short_options = ovs_cmdl_long_options_to_short_options(long_options); for (;;) { int c; c = getopt_long(argc, argv, short_options, long_options, NULL); if (c == -1) { break; } switch (c) { case 'h': usage(); case 'V': ovs_print_version(0, 0); exit(EXIT_SUCCESS); case OPT_MLOCKALL: want_mlockall = true; break; case OPT_UNIXCTL: *unixctl_pathp = optarg; break; VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS STREAM_SSL_OPTION_HANDLERS case OPT_PEER_CA_CERT: stream_ssl_set_peer_ca_cert_file(optarg); break; case OPT_BOOTSTRAP_CA_CERT: stream_ssl_set_ca_cert_file(optarg, true); break; case OPT_ENABLE_DUMMY: dummy_enable(optarg); break; case OPT_DISABLE_SYSTEM: dp_blacklist_provider("system"); break; case '?': exit(EXIT_FAILURE); case OPT_DPDK: ovs_fatal(0, "--dpdk must be given at beginning of command line."); break; default: abort(); } } free(short_options); argc -= optind; argv += optind; switch (argc) { case 0: return xasprintf("unix:%s/db.sock", ovs_rundir()); case 1: return xstrdup(argv[0]); default: VLOG_FATAL("at most one non-option argument accepted; " "use --help for usage"); } } static void usage(void) { printf("%s: Open vSwitch daemon\n" "usage: %s [OPTIONS] [DATABASE]\n" "where DATABASE is a socket on which ovsdb-server is listening\n" " (default: \"unix:%s/db.sock\").\n", program_name, program_name, ovs_rundir()); stream_usage("DATABASE", true, false, true); daemon_usage(); vlog_usage(); printf("\nDPDK options:\n" " --dpdk [VHOST] [DPDK] Initialize DPDK datapath.\n" " where DPDK are options for initializing DPDK lib and VHOST is\n" #ifdef VHOST_CUSE " option to override default character device name used for\n" " for use with userspace vHost\n" " -cuse_dev_name NAME\n" #else " option to override default directory where vhost-user\n" " sockets are created.\n" " -vhost_sock_dir DIR\n" #endif ); printf("\nOther options:\n" " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); exit(EXIT_SUCCESS); } static void ovs_vswitchd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *exiting_) { bool *exiting = exiting_; *exiting = true; unixctl_command_reply(conn, NULL); } openvswitch-2.5.9/vswitchd/PaxHeaders.82075/bridge.c0000644000000000000000000000013213534540071017077 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801425.197856493 openvswitch-2.5.9/vswitchd/bridge.c0000644000175000017500000051447513534540071020605 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "bridge.h" #include #include #include #include "async-append.h" #include "bfd.h" #include "bitmap.h" #include "cfm.h" #include "connectivity.h" #include "coverage.h" #include "daemon.h" #include "dirs.h" #include "dpif.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "hmapx.h" #include "jsonrpc.h" #include "lacp.h" #include "list.h" #include "ovs-lldp.h" #include "mac-learning.h" #include "mcast-snooping.h" #include "meta-flow.h" #include "netdev.h" #include "nx-match.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto/bond.h" #include "ofproto/ofproto.h" #include "ovs-numa.h" #include "poll-loop.h" #include "if-notifier.h" #include "seq.h" #include "sha1.h" #include "shash.h" #include "smap.h" #include "socket-util.h" #include "stream.h" #include "stream-ssl.h" #include "sset.h" #include "system-stats.h" #include "timeval.h" #include "util.h" #include "unixctl.h" #include "vlandev.h" #include "lib/vswitch-idl.h" #include "xenserver.h" #include "openvswitch/vlog.h" #include "sflow_api.h" #include "vlan-bitmap.h" #include "packets.h" VLOG_DEFINE_THIS_MODULE(bridge); COVERAGE_DEFINE(bridge_reconfigure); struct iface { /* These members are always valid. * * They are immutable: they never change between iface_create() and * iface_destroy(). */ struct ovs_list port_elem; /* Element in struct port's "ifaces" list. */ struct hmap_node name_node; /* In struct bridge's "iface_by_name" hmap. */ struct hmap_node ofp_port_node; /* In struct bridge's "ifaces" hmap. */ struct port *port; /* Containing port. */ char *name; /* Host network device name. */ struct netdev *netdev; /* Network device. */ ofp_port_t ofp_port; /* OpenFlow port number. */ uint64_t change_seq; /* These members are valid only within bridge_reconfigure(). */ const char *type; /* Usually same as cfg->type. */ const char *netdev_type; /* type that should be used for netdev_open. */ const struct ovsrec_interface *cfg; }; struct mirror { struct uuid uuid; /* UUID of this "mirror" record in database. */ struct hmap_node hmap_node; /* In struct bridge's "mirrors" hmap. */ struct bridge *bridge; char *name; const struct ovsrec_mirror *cfg; }; struct port { struct hmap_node hmap_node; /* Element in struct bridge's "ports" hmap. */ struct bridge *bridge; char *name; const struct ovsrec_port *cfg; /* An ordinary bridge port has 1 interface. * A bridge port for bonding has at least 2 interfaces. */ struct ovs_list ifaces; /* List of "struct iface"s. */ }; struct bridge { struct hmap_node node; /* In 'all_bridges'. */ char *name; /* User-specified arbitrary name. */ char *type; /* Datapath type. */ struct eth_addr ea; /* Bridge Ethernet Address. */ struct eth_addr default_ea; /* Default MAC. */ const struct ovsrec_bridge *cfg; /* OpenFlow switch processing. */ struct ofproto *ofproto; /* OpenFlow switch. */ /* Bridge ports. */ struct hmap ports; /* "struct port"s indexed by name. */ struct hmap ifaces; /* "struct iface"s indexed by ofp_port. */ struct hmap iface_by_name; /* "struct iface"s indexed by name. */ /* Port mirroring. */ struct hmap mirrors; /* "struct mirror" indexed by UUID. */ /* Auto Attach */ struct hmap mappings; /* "struct" indexed by UUID */ /* Used during reconfiguration. */ struct shash wanted_ports; /* Synthetic local port if necessary. */ struct ovsrec_port synth_local_port; struct ovsrec_interface synth_local_iface; struct ovsrec_interface *synth_local_ifacep; }; struct aa_mapping { struct hmap_node hmap_node; /* In struct bridge's "mappings" hmap. */ struct bridge *bridge; uint32_t isid; uint16_t vlan; char *br_name; }; /* All bridges, indexed by name. */ static struct hmap all_bridges = HMAP_INITIALIZER(&all_bridges); /* OVSDB IDL used to obtain configuration. */ static struct ovsdb_idl *idl; /* We want to complete daemonization, fully detaching from our parent process, * only after we have completed our initial configuration, committed our state * to the database, and received confirmation back from the database server * that it applied the commit. This allows our parent process to know that, * post-detach, ephemeral fields such as datapath-id and ofport are very likely * to have already been filled in. (It is only "very likely" rather than * certain because there is always a slim possibility that the transaction will * fail or that some other client has added new bridges, ports, etc. while * ovs-vswitchd was configuring using an old configuration.) * * We only need to do this once for our initial configuration at startup, so * 'initial_config_done' tracks whether we've already done it. While we are * waiting for a response to our commit, 'daemonize_txn' tracks the transaction * itself and is otherwise NULL. */ static bool initial_config_done; static struct ovsdb_idl_txn *daemonize_txn; /* Most recently processed IDL sequence number. */ static unsigned int idl_seqno; /* Track changes to port connectivity. */ static uint64_t connectivity_seqno = LLONG_MIN; /* Status update to database. * * Some information in the database must be kept as up-to-date as possible to * allow controllers to respond rapidly to network outages. Those status are * updated via the 'status_txn'. * * We use the global connectivity sequence number to detect the status change. * Also, to prevent the status update from sending too much to the database, * we check the return status of each update transaction and do not start new * update if the previous transaction status is 'TXN_INCOMPLETE'. * * 'statux_txn' is NULL if there is no ongoing status update. * * If the previous database transaction was failed (is not 'TXN_SUCCESS', * 'TXN_UNCHANGED' or 'TXN_INCOMPLETE'), 'status_txn_try_again' is set to true, * which will cause the main thread wake up soon and retry the status update. */ static struct ovsdb_idl_txn *status_txn; static bool status_txn_try_again; /* When the status update transaction returns 'TXN_INCOMPLETE', should register a * timeout in 'STATUS_CHECK_AGAIN_MSEC' to check again. */ #define STATUS_CHECK_AGAIN_MSEC 100 /* Statistics update to database. */ static struct ovsdb_idl_txn *stats_txn; /* Each time this timer expires, the bridge fetches interface and mirror * statistics and pushes them into the database. */ static int stats_timer_interval; static long long int stats_timer = LLONG_MIN; /* Each time this timer expires, the bridge fetches the list of port/VLAN * membership that has been modified by the AA. */ #define AA_REFRESH_INTERVAL (1000) /* In milliseconds. */ static long long int aa_refresh_timer = LLONG_MIN; /* Whenever system interfaces are added, removed or change state, the bridge * will be reconfigured. */ static struct if_notifier *ifnotifier; static struct seq *ifaces_changed; static uint64_t last_ifaces_changed; static void add_del_bridges(const struct ovsrec_open_vswitch *); static void bridge_run__(void); static void bridge_create(const struct ovsrec_bridge *); static void bridge_destroy(struct bridge *, bool del); static struct bridge *bridge_lookup(const char *name); static unixctl_cb_func bridge_unixctl_dump_flows; static unixctl_cb_func bridge_unixctl_reconnect; static size_t bridge_get_controllers(const struct bridge *br, struct ovsrec_controller ***controllersp); static void bridge_collect_wanted_ports(struct bridge *, const unsigned long *splinter_vlans, struct shash *wanted_ports); static void bridge_delete_ofprotos(void); static void bridge_delete_or_reconfigure_ports(struct bridge *); static void bridge_del_ports(struct bridge *, const struct shash *wanted_ports); static void bridge_add_ports(struct bridge *, const struct shash *wanted_ports); static void bridge_configure_datapath_id(struct bridge *); static void bridge_configure_netflow(struct bridge *); static void bridge_configure_forward_bpdu(struct bridge *); static void bridge_configure_mac_table(struct bridge *); static void bridge_configure_mcast_snooping(struct bridge *); static void bridge_configure_sflow(struct bridge *, int *sflow_bridge_number); static void bridge_configure_ipfix(struct bridge *); static void bridge_configure_spanning_tree(struct bridge *); static void bridge_configure_tables(struct bridge *); static void bridge_configure_dp_desc(struct bridge *); static void bridge_configure_aa(struct bridge *); static void bridge_aa_refresh_queued(struct bridge *); static bool bridge_aa_need_refresh(struct bridge *); static void bridge_configure_remotes(struct bridge *, const struct sockaddr_in *managers, size_t n_managers); static void bridge_pick_local_hw_addr(struct bridge *, struct eth_addr *ea, struct iface **hw_addr_iface); static uint64_t bridge_pick_datapath_id(struct bridge *, const struct eth_addr bridge_ea, struct iface *hw_addr_iface); static uint64_t dpid_from_hash(const void *, size_t nbytes); static bool bridge_has_bond_fake_iface(const struct bridge *, const char *name); static bool port_is_bond_fake_iface(const struct port *); static unixctl_cb_func qos_unixctl_show; static struct port *port_create(struct bridge *, const struct ovsrec_port *); static void port_del_ifaces(struct port *); static void port_destroy(struct port *); static struct port *port_lookup(const struct bridge *, const char *name); static void port_configure(struct port *); static struct lacp_settings *port_configure_lacp(struct port *, struct lacp_settings *); static void port_configure_bond(struct port *, struct bond_settings *); static bool port_is_synthetic(const struct port *); static void reconfigure_system_stats(const struct ovsrec_open_vswitch *); static void run_system_stats(void); static void bridge_configure_mirrors(struct bridge *); static struct mirror *mirror_create(struct bridge *, const struct ovsrec_mirror *); static void mirror_destroy(struct mirror *); static bool mirror_configure(struct mirror *); static void mirror_refresh_stats(struct mirror *); static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *); static bool iface_create(struct bridge *, const struct ovsrec_interface *, const struct ovsrec_port *); static bool iface_is_internal(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br); static const char *iface_get_type(const struct ovsrec_interface *, const struct ovsrec_bridge *); static void iface_destroy(struct iface *); static void iface_destroy__(struct iface *); static struct iface *iface_lookup(const struct bridge *, const char *name); static struct iface *iface_find(const char *name); static struct iface *iface_from_ofp_port(const struct bridge *, ofp_port_t ofp_port); static void iface_set_mac(const struct bridge *, const struct port *, struct iface *); static void iface_set_ofport(const struct ovsrec_interface *, ofp_port_t ofport); static void iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp); static void iface_configure_qos(struct iface *, const struct ovsrec_qos *); static void iface_configure_cfm(struct iface *); static void iface_refresh_cfm_stats(struct iface *); static void iface_refresh_stats(struct iface *); static void iface_refresh_netdev_status(struct iface *); static void iface_refresh_ofproto_status(struct iface *); static bool iface_is_synthetic(const struct iface *); static ofp_port_t iface_get_requested_ofp_port( const struct ovsrec_interface *); static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *); /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ /* True if VLAN splinters are enabled on any interface, false otherwise.*/ static bool vlan_splinters_enabled_anywhere; static bool vlan_splinters_is_enabled(const struct ovsrec_interface *); static unsigned long int *collect_splinter_vlans( const struct ovsrec_open_vswitch *); static void configure_splinter_port(struct port *); static void add_vlan_splinter_ports(struct bridge *, const unsigned long int *splinter_vlans, struct shash *ports); static void discover_types(const struct ovsrec_open_vswitch *cfg); static void bridge_init_ofproto(const struct ovsrec_open_vswitch *cfg) { struct shash iface_hints; static bool initialized = false; int i; if (initialized) { return; } shash_init(&iface_hints); if (cfg) { for (i = 0; i < cfg->n_bridges; i++) { const struct ovsrec_bridge *br_cfg = cfg->bridges[i]; int j; for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; int k; for (k = 0; k < port_cfg->n_interfaces; k++) { struct ovsrec_interface *if_cfg = port_cfg->interfaces[k]; struct iface_hint *iface_hint; iface_hint = xmalloc(sizeof *iface_hint); iface_hint->br_name = br_cfg->name; iface_hint->br_type = br_cfg->datapath_type; iface_hint->ofp_port = iface_pick_ofport(if_cfg); shash_add(&iface_hints, if_cfg->name, iface_hint); } } } } ofproto_init(&iface_hints); shash_destroy_free_data(&iface_hints); initialized = true; } static void if_change_cb(void *aux OVS_UNUSED) { seq_change(ifaces_changed); } static bool if_notifier_changed(struct if_notifier *notifier OVS_UNUSED) { uint64_t new_seq; bool changed = false; new_seq = seq_read(ifaces_changed); if (new_seq != last_ifaces_changed) { changed = true; last_ifaces_changed = new_seq; } seq_wait(ifaces_changed, last_ifaces_changed); return changed; } /* Public functions. */ /* Initializes the bridge module, configuring it to obtain its configuration * from an OVSDB server accessed over 'remote', which should be a string in a * form acceptable to ovsdb_idl_create(). */ void bridge_init(const char *remote) { /* Create connection to database. */ idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true, true); idl_seqno = ovsdb_idl_get_seqno(idl); ovsdb_idl_set_lock(idl, "ovs_vswitchd"); ovsdb_idl_verify_write_only(idl); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_cur_cfg); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_datapath_types); ovsdb_idl_omit_alert(idl, &ovsrec_open_vswitch_col_iface_types); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_ovs_version); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_db_version); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_type); ovsdb_idl_omit(idl, &ovsrec_open_vswitch_col_system_version); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_id); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_datapath_version); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_status); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_stp_enable); ovsdb_idl_omit_alert(idl, &ovsrec_bridge_col_rstp_enable); ovsdb_idl_omit(idl, &ovsrec_bridge_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_status); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_rstp_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_bond_active_slave); ovsdb_idl_omit(idl, &ovsrec_port_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_trunks); ovsdb_idl_omit_alert(idl, &ovsrec_port_col_vlan_mode); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_admin_state); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_duplex); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_resets); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mac_in_use); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ifindex); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_flap_count); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_health); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_opstate); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_bfd_status); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_lacp_current); ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_error); ovsdb_idl_omit(idl, &ovsrec_interface_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_is_connected); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_role); ovsdb_idl_omit_alert(idl, &ovsrec_controller_col_status); ovsdb_idl_omit(idl, &ovsrec_controller_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_qos_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_queue_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_mirror_col_external_ids); ovsdb_idl_omit_alert(idl, &ovsrec_mirror_col_statistics); ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_sflow_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_ipfix_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_flow_sample_collector_set_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_manager_col_external_ids); ovsdb_idl_omit(idl, &ovsrec_manager_col_inactivity_probe); ovsdb_idl_omit(idl, &ovsrec_manager_col_is_connected); ovsdb_idl_omit(idl, &ovsrec_manager_col_max_backoff); ovsdb_idl_omit(idl, &ovsrec_manager_col_status); ovsdb_idl_omit(idl, &ovsrec_ssl_col_external_ids); /* Register unixctl commands. */ unixctl_command_register("qos/show", "interface", 1, 1, qos_unixctl_show, NULL); unixctl_command_register("bridge/dump-flows", "bridge", 1, 1, bridge_unixctl_dump_flows, NULL); unixctl_command_register("bridge/reconnect", "[bridge]", 0, 1, bridge_unixctl_reconnect, NULL); lacp_init(); bond_init(); cfm_init(); bfd_init(); ovs_numa_init(); stp_init(); lldp_init(); rstp_init(); ifaces_changed = seq_create(); last_ifaces_changed = seq_read(ifaces_changed); ifnotifier = if_notifier_create(if_change_cb, NULL); } void bridge_exit(void) { struct bridge *br, *next_br; if_notifier_destroy(ifnotifier); seq_destroy(ifaces_changed); HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) { bridge_destroy(br, false); } ovsdb_idl_destroy(idl); } /* Looks at the list of managers in 'ovs_cfg' and extracts their remote IP * addresses and ports into '*managersp' and '*n_managersp'. The caller is * responsible for freeing '*managersp' (with free()). * * You may be asking yourself "why does ovs-vswitchd care?", because * ovsdb-server is responsible for connecting to the managers, and ovs-vswitchd * should not be and in fact is not directly involved in that. But * ovs-vswitchd needs to make sure that ovsdb-server can reach the managers, so * it has to tell in-band control where the managers are to enable that. * (Thus, only managers connected in-band and with non-loopback addresses * are collected.) */ static void collect_in_band_managers(const struct ovsrec_open_vswitch *ovs_cfg, struct sockaddr_in **managersp, size_t *n_managersp) { struct sockaddr_in *managers = NULL; size_t n_managers = 0; struct sset targets; size_t i; /* Collect all of the potential targets from the "targets" columns of the * rows pointed to by "manager_options", excluding any that are * out-of-band. */ sset_init(&targets); for (i = 0; i < ovs_cfg->n_manager_options; i++) { struct ovsrec_manager *m = ovs_cfg->manager_options[i]; if (m->connection_mode && !strcmp(m->connection_mode, "out-of-band")) { sset_find_and_delete(&targets, m->target); } else { sset_add(&targets, m->target); } } /* Now extract the targets' IP addresses. */ if (!sset_is_empty(&targets)) { const char *target; managers = xmalloc(sset_count(&targets) * sizeof *managers); SSET_FOR_EACH (target, &targets) { union { struct sockaddr_storage ss; struct sockaddr_in in; } sa; /* Ignore loopback. */ if (stream_parse_target_with_default_port(target, OVSDB_PORT, &sa.ss) && sa.ss.ss_family == AF_INET && sa.in.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { managers[n_managers++] = sa.in; } } } sset_destroy(&targets); *managersp = managers; *n_managersp = n_managers; } static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { unsigned long int *splinter_vlans; struct sockaddr_in *managers; struct bridge *br, *next; int sflow_bridge_number; size_t n_managers; COVERAGE_INC(bridge_reconfigure); ofproto_set_flow_limit(smap_get_int(&ovs_cfg->other_config, "flow-limit", OFPROTO_FLOW_LIMIT_DEFAULT)); ofproto_set_max_idle(smap_get_int(&ovs_cfg->other_config, "max-idle", OFPROTO_MAX_IDLE_DEFAULT)); ofproto_set_n_dpdk_rxqs(smap_get_int(&ovs_cfg->other_config, "n-dpdk-rxqs", 0)); ofproto_set_cpu_mask(smap_get(&ovs_cfg->other_config, "pmd-cpu-mask")); ofproto_set_threads( smap_get_int(&ovs_cfg->other_config, "n-handler-threads", 0), smap_get_int(&ovs_cfg->other_config, "n-revalidator-threads", 0)); /* Destroy "struct bridge"s, "struct port"s, and "struct iface"s according * to 'ovs_cfg', with only very minimal configuration otherwise. * * This is mostly an update to bridge data structures. Nothing is pushed * down to ofproto or lower layers. */ add_del_bridges(ovs_cfg); splinter_vlans = collect_splinter_vlans(ovs_cfg); HMAP_FOR_EACH (br, node, &all_bridges) { bridge_collect_wanted_ports(br, splinter_vlans, &br->wanted_ports); bridge_del_ports(br, &br->wanted_ports); } free(splinter_vlans); /* Start pushing configuration changes down to the ofproto layer: * * - Delete ofprotos that are no longer configured. * * - Delete ports that are no longer configured. * * - Reconfigure existing ports to their desired configurations, or * delete them if not possible. * * We have to do all the deletions before we can do any additions, because * the ports to be added might require resources that will be freed up by * deletions (they might especially overlap in name). */ bridge_delete_ofprotos(); HMAP_FOR_EACH (br, node, &all_bridges) { if (br->ofproto) { bridge_delete_or_reconfigure_ports(br); } } /* Finish pushing configuration changes to the ofproto layer: * * - Create ofprotos that are missing. * * - Add ports that are missing. */ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) { if (!br->ofproto) { int error; error = ofproto_create(br->name, br->type, &br->ofproto); if (error) { VLOG_ERR("failed to create bridge %s: %s", br->name, ovs_strerror(error)); shash_destroy(&br->wanted_ports); bridge_destroy(br, true); } else { /* Trigger storing datapath version. */ seq_change(connectivity_seq_get()); } } } HMAP_FOR_EACH (br, node, &all_bridges) { bridge_add_ports(br, &br->wanted_ports); shash_destroy(&br->wanted_ports); } reconfigure_system_stats(ovs_cfg); /* Complete the configuration. */ sflow_bridge_number = 0; collect_in_band_managers(ovs_cfg, &managers, &n_managers); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; /* We need the datapath ID early to allow LACP ports to use it as the * default system ID. */ bridge_configure_datapath_id(br); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; port_configure(port); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_set_ofport(iface->cfg, iface->ofp_port); /* Clear eventual previous errors */ ovsrec_interface_set_error(iface->cfg, NULL); iface_configure_cfm(iface); iface_configure_qos(iface, port->cfg->qos); iface_set_mac(br, port, iface); ofproto_port_set_bfd(br->ofproto, iface->ofp_port, &iface->cfg->bfd); ofproto_port_set_lldp(br->ofproto, iface->ofp_port, &iface->cfg->lldp); } } bridge_configure_mirrors(br); bridge_configure_forward_bpdu(br); bridge_configure_mac_table(br); bridge_configure_mcast_snooping(br); bridge_configure_remotes(br, managers, n_managers); bridge_configure_netflow(br); bridge_configure_sflow(br, &sflow_bridge_number); bridge_configure_ipfix(br); bridge_configure_spanning_tree(br); bridge_configure_tables(br); bridge_configure_dp_desc(br); bridge_configure_aa(br); } free(managers); /* The ofproto-dpif provider does some final reconfiguration in its * ->type_run() function. We have to call it before notifying the database * client that reconfiguration is complete, otherwise there is a very * narrow race window in which e.g. ofproto/trace will not recognize the * new configuration (sometimes this causes unit test failures). */ bridge_run__(); } /* Delete ofprotos which aren't configured or have the wrong type. Create * ofprotos which don't exist but need to. */ static void bridge_delete_ofprotos(void) { struct bridge *br; struct sset names; struct sset types; const char *type; /* Delete ofprotos with no bridge or with the wrong type. */ sset_init(&names); sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { const char *name; ofproto_enumerate_names(type, &names); SSET_FOR_EACH (name, &names) { br = bridge_lookup(name); if (!br || strcmp(type, br->type)) { ofproto_delete(name, type); } } } sset_destroy(&names); sset_destroy(&types); } static ofp_port_t * add_ofp_port(ofp_port_t port, ofp_port_t *ports, size_t *n, size_t *allocated) { if (*n >= *allocated) { ports = x2nrealloc(ports, allocated, sizeof *ports); } ports[(*n)++] = port; return ports; } static void bridge_delete_or_reconfigure_ports(struct bridge *br) { struct ofproto_port ofproto_port; struct ofproto_port_dump dump; struct sset ofproto_ports; struct port *port, *port_next; /* List of "ofp_port"s to delete. We make a list instead of deleting them * right away because ofproto implementations aren't necessarily able to * iterate through a changing list of ports in an entirely robust way. */ ofp_port_t *del; size_t n, allocated; size_t i; del = NULL; n = allocated = 0; sset_init(&ofproto_ports); /* Main task: Iterate over the ports in 'br->ofproto' and remove the ports * that are not configured in the database. (This commonly happens when * ports have been deleted, e.g. with "ovs-vsctl del-port".) * * Side tasks: Reconfigure the ports that are still in 'br'. Delete ports * that have the wrong OpenFlow port number (and arrange to add them back * with the correct OpenFlow port number). */ OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, br->ofproto) { ofp_port_t requested_ofp_port; struct iface *iface; sset_add(&ofproto_ports, ofproto_port.name); iface = iface_lookup(br, ofproto_port.name); if (!iface) { /* No such iface is configured, so we should delete this * ofproto_port. * * As a corner case exception, keep the port if it's a bond fake * interface. */ if (bridge_has_bond_fake_iface(br, ofproto_port.name) && !strcmp(ofproto_port.type, "internal")) { continue; } goto delete; } if (strcmp(ofproto_port.type, iface->netdev_type) || netdev_set_config(iface->netdev, &iface->cfg->options, NULL)) { /* The interface is the wrong type or can't be configured. * Delete it. */ goto delete; } /* If the requested OpenFlow port for 'iface' changed, and it's not * already the correct port, then we might want to temporarily delete * this interface, so we can add it back again with the new OpenFlow * port number. */ requested_ofp_port = iface_get_requested_ofp_port(iface->cfg); if (iface->ofp_port != OFPP_LOCAL && requested_ofp_port != OFPP_NONE && requested_ofp_port != iface->ofp_port) { ofp_port_t victim_request; struct iface *victim; /* Check for an existing OpenFlow port currently occupying * 'iface''s requested port number. If there isn't one, then * delete this port. Otherwise we need to consider further. */ victim = iface_from_ofp_port(br, requested_ofp_port); if (!victim) { goto delete; } /* 'victim' is a port currently using 'iface''s requested port * number. Unless 'victim' specifically requested that port * number, too, then we can delete both 'iface' and 'victim' * temporarily. (We'll add both of them back again later with new * OpenFlow port numbers.) * * If 'victim' did request port number 'requested_ofp_port', just * like 'iface', then that's a configuration inconsistency that we * can't resolve. We might as well let it keep its current port * number. */ victim_request = iface_get_requested_ofp_port(victim->cfg); if (victim_request != requested_ofp_port) { del = add_ofp_port(victim->ofp_port, del, &n, &allocated); iface_destroy(victim); goto delete; } } /* Keep it. */ continue; delete: iface_destroy(iface); del = add_ofp_port(ofproto_port.ofp_port, del, &n, &allocated); } for (i = 0; i < n; i++) { ofproto_port_del(br->ofproto, del[i]); } free(del); /* Iterate over this module's idea of interfaces in 'br'. Remove any ports * that we didn't see when we iterated through the datapath, i.e. ports * that disappeared underneath use. This is an unusual situation, but it * can happen in some cases: * * - An admin runs a command like "ovs-dpctl del-port" (which is a bad * idea but could happen). * * - The port represented a device that disappeared, e.g. a tuntap * device destroyed via "tunctl -d", a physical Ethernet device * whose module was just unloaded via "rmmod", or a virtual NIC for a * VM whose VM was just terminated. */ HMAP_FOR_EACH_SAFE (port, port_next, hmap_node, &br->ports) { struct iface *iface, *iface_next; LIST_FOR_EACH_SAFE (iface, iface_next, port_elem, &port->ifaces) { if (!sset_contains(&ofproto_ports, iface->name)) { iface_destroy__(iface); } } if (list_is_empty(&port->ifaces)) { port_destroy(port); } } sset_destroy(&ofproto_ports); } static void bridge_add_ports__(struct bridge *br, const struct shash *wanted_ports, bool with_requested_port) { struct shash_node *port_node; SHASH_FOR_EACH (port_node, wanted_ports) { const struct ovsrec_port *port_cfg = port_node->data; size_t i; for (i = 0; i < port_cfg->n_interfaces; i++) { const struct ovsrec_interface *iface_cfg = port_cfg->interfaces[i]; ofp_port_t requested_ofp_port; requested_ofp_port = iface_get_requested_ofp_port(iface_cfg); if ((requested_ofp_port != OFPP_NONE) == with_requested_port) { struct iface *iface = iface_lookup(br, iface_cfg->name); if (!iface) { iface_create(br, iface_cfg, port_cfg); } } } } } static void bridge_add_ports(struct bridge *br, const struct shash *wanted_ports) { /* First add interfaces that request a particular port number. */ bridge_add_ports__(br, wanted_ports, true); /* Then add interfaces that want automatic port number assignment. * We add these afterward to avoid accidentally taking a specifically * requested port number. */ bridge_add_ports__(br, wanted_ports, false); } static void port_configure(struct port *port) { const struct ovsrec_port *cfg = port->cfg; struct bond_settings bond_settings; struct lacp_settings lacp_settings; struct ofproto_bundle_settings s; struct iface *iface; if (cfg->vlan_mode && !strcmp(cfg->vlan_mode, "splinter")) { configure_splinter_port(port); return; } /* Get name. */ s.name = port->name; /* Get slaves. */ s.n_slaves = 0; s.slaves = xmalloc(list_size(&port->ifaces) * sizeof *s.slaves); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { s.slaves[s.n_slaves++] = iface->ofp_port; } /* Get VLAN tag. */ s.vlan = -1; if (cfg->tag && *cfg->tag >= 0 && *cfg->tag <= 4095) { s.vlan = *cfg->tag; } /* Get VLAN trunks. */ s.trunks = NULL; if (cfg->n_trunks) { s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks); } /* Get VLAN mode. */ if (cfg->vlan_mode) { if (!strcmp(cfg->vlan_mode, "access")) { s.vlan_mode = PORT_VLAN_ACCESS; } else if (!strcmp(cfg->vlan_mode, "trunk")) { s.vlan_mode = PORT_VLAN_TRUNK; } else if (!strcmp(cfg->vlan_mode, "native-tagged")) { s.vlan_mode = PORT_VLAN_NATIVE_TAGGED; } else if (!strcmp(cfg->vlan_mode, "native-untagged")) { s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED; } else { /* This "can't happen" because ovsdb-server should prevent it. */ VLOG_WARN("port %s: unknown VLAN mode %s, falling " "back to trunk mode", port->name, cfg->vlan_mode); s.vlan_mode = PORT_VLAN_TRUNK; } } else { if (s.vlan >= 0) { s.vlan_mode = PORT_VLAN_ACCESS; if (cfg->n_trunks) { VLOG_WARN("port %s: ignoring trunks in favor of implicit vlan", port->name); } } else { s.vlan_mode = PORT_VLAN_TRUNK; } } s.use_priority_tags = smap_get_bool(&cfg->other_config, "priority-tags", false); /* Get LACP settings. */ s.lacp = port_configure_lacp(port, &lacp_settings); if (s.lacp) { size_t i = 0; s.lacp_slaves = xmalloc(s.n_slaves * sizeof *s.lacp_slaves); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_configure_lacp(iface, &s.lacp_slaves[i++]); } } else { s.lacp_slaves = NULL; } /* Get bond settings. */ if (s.n_slaves > 1) { s.bond = &bond_settings; port_configure_bond(port, &bond_settings); } else { s.bond = NULL; LIST_FOR_EACH (iface, port_elem, &port->ifaces) { netdev_set_miimon_interval(iface->netdev, 0); } } /* Register. */ ofproto_bundle_register(port->bridge->ofproto, port, &s); /* Clean up. */ free(s.slaves); free(s.trunks); free(s.lacp_slaves); } /* Pick local port hardware address and datapath ID for 'br'. */ static void bridge_configure_datapath_id(struct bridge *br) { struct eth_addr ea; uint64_t dpid; struct iface *local_iface; struct iface *hw_addr_iface; char *dpid_string; bridge_pick_local_hw_addr(br, &ea, &hw_addr_iface); local_iface = iface_from_ofp_port(br, OFPP_LOCAL); if (local_iface) { int error = netdev_set_etheraddr(local_iface->netdev, ea); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_ERR_RL(&rl, "bridge %s: failed to set bridge " "Ethernet address: %s", br->name, ovs_strerror(error)); } } br->ea = ea; dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface); if (dpid != ofproto_get_datapath_id(br->ofproto)) { VLOG_INFO("bridge %s: using datapath ID %016"PRIx64, br->name, dpid); ofproto_set_datapath_id(br->ofproto, dpid); } dpid_string = xasprintf("%016"PRIx64, dpid); ovsrec_bridge_set_datapath_id(br->cfg, dpid_string); free(dpid_string); } /* Returns a bitmap of "enum ofputil_protocol"s that are allowed for use with * 'br'. */ static uint32_t bridge_get_allowed_versions(struct bridge *br) { if (!br->cfg->n_protocols) { return 0; } return ofputil_versions_from_strings(br->cfg->protocols, br->cfg->n_protocols); } /* Set NetFlow configuration on 'br'. */ static void bridge_configure_netflow(struct bridge *br) { struct ovsrec_netflow *cfg = br->cfg->netflow; struct netflow_options opts; if (!cfg) { ofproto_set_netflow(br->ofproto, NULL); return; } memset(&opts, 0, sizeof opts); /* Get default NetFlow configuration from datapath. * Apply overrides from 'cfg'. */ ofproto_get_netflow_ids(br->ofproto, &opts.engine_type, &opts.engine_id); if (cfg->engine_type) { opts.engine_type = *cfg->engine_type; } if (cfg->engine_id) { opts.engine_id = *cfg->engine_id; } /* Configure active timeout interval. */ opts.active_timeout = cfg->active_timeout; if (!opts.active_timeout) { opts.active_timeout = -1; } else if (opts.active_timeout < 0) { VLOG_WARN("bridge %s: active timeout interval set to negative " "value, using default instead (%d seconds)", br->name, NF_ACTIVE_TIMEOUT_DEFAULT); opts.active_timeout = -1; } /* Add engine ID to interface number to disambiguate bridgs? */ opts.add_id_to_iface = cfg->add_id_to_interface; if (opts.add_id_to_iface) { if (opts.engine_id > 0x7f) { VLOG_WARN("bridge %s: NetFlow port mangling may conflict with " "another vswitch, choose an engine id less than 128", br->name); } if (hmap_count(&br->ports) > 508) { VLOG_WARN("bridge %s: NetFlow port mangling will conflict with " "another port when more than 508 ports are used", br->name); } } /* Collectors. */ sset_init(&opts.collectors); sset_add_array(&opts.collectors, cfg->targets, cfg->n_targets); /* Configure. */ if (ofproto_set_netflow(br->ofproto, &opts)) { VLOG_ERR("bridge %s: problem setting netflow collectors", br->name); } sset_destroy(&opts.collectors); } /* Set sFlow configuration on 'br'. */ static void bridge_configure_sflow(struct bridge *br, int *sflow_bridge_number) { const struct ovsrec_sflow *cfg = br->cfg->sflow; struct ovsrec_controller **controllers; struct ofproto_sflow_options oso; size_t n_controllers; size_t i; if (!cfg) { ofproto_set_sflow(br->ofproto, NULL); return; } memset(&oso, 0, sizeof oso); sset_init(&oso.targets); sset_add_array(&oso.targets, cfg->targets, cfg->n_targets); oso.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; if (cfg->sampling) { oso.sampling_rate = *cfg->sampling; } oso.polling_interval = SFL_DEFAULT_POLLING_INTERVAL; if (cfg->polling) { oso.polling_interval = *cfg->polling; } oso.header_len = SFL_DEFAULT_HEADER_SIZE; if (cfg->header) { oso.header_len = *cfg->header; } oso.sub_id = (*sflow_bridge_number)++; oso.agent_device = cfg->agent; oso.control_ip = NULL; n_controllers = bridge_get_controllers(br, &controllers); for (i = 0; i < n_controllers; i++) { if (controllers[i]->local_ip) { oso.control_ip = controllers[i]->local_ip; break; } } ofproto_set_sflow(br->ofproto, &oso); sset_destroy(&oso.targets); } /* Returns whether a IPFIX row is valid. */ static bool ovsrec_ipfix_is_valid(const struct ovsrec_ipfix *ipfix) { return ipfix && ipfix->n_targets > 0; } /* Returns whether a Flow_Sample_Collector_Set row is valid. */ static bool ovsrec_fscs_is_valid(const struct ovsrec_flow_sample_collector_set *fscs, const struct bridge *br) { return ovsrec_ipfix_is_valid(fscs->ipfix) && fscs->bridge == br->cfg; } /* Set IPFIX configuration on 'br'. */ static void bridge_configure_ipfix(struct bridge *br) { const struct ovsrec_ipfix *be_cfg = br->cfg->ipfix; bool valid_be_cfg = ovsrec_ipfix_is_valid(be_cfg); const struct ovsrec_flow_sample_collector_set *fe_cfg; struct ofproto_ipfix_bridge_exporter_options be_opts; struct ofproto_ipfix_flow_exporter_options *fe_opts = NULL; size_t n_fe_opts = 0; OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) { if (ovsrec_fscs_is_valid(fe_cfg, br)) { n_fe_opts++; } } if (!valid_be_cfg && n_fe_opts == 0) { ofproto_set_ipfix(br->ofproto, NULL, NULL, 0); return; } if (valid_be_cfg) { memset(&be_opts, 0, sizeof be_opts); sset_init(&be_opts.targets); sset_add_array(&be_opts.targets, be_cfg->targets, be_cfg->n_targets); if (be_cfg->sampling) { be_opts.sampling_rate = *be_cfg->sampling; } else { be_opts.sampling_rate = SFL_DEFAULT_SAMPLING_RATE; } if (be_cfg->obs_domain_id) { be_opts.obs_domain_id = *be_cfg->obs_domain_id; } if (be_cfg->obs_point_id) { be_opts.obs_point_id = *be_cfg->obs_point_id; } if (be_cfg->cache_active_timeout) { be_opts.cache_active_timeout = *be_cfg->cache_active_timeout; } if (be_cfg->cache_max_flows) { be_opts.cache_max_flows = *be_cfg->cache_max_flows; } be_opts.enable_tunnel_sampling = smap_get_bool(&be_cfg->other_config, "enable-tunnel-sampling", true); be_opts.enable_input_sampling = !smap_get_bool(&be_cfg->other_config, "enable-input-sampling", false); be_opts.enable_output_sampling = !smap_get_bool(&be_cfg->other_config, "enable-output-sampling", false); } if (n_fe_opts > 0) { struct ofproto_ipfix_flow_exporter_options *opts; fe_opts = xcalloc(n_fe_opts, sizeof *fe_opts); opts = fe_opts; OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH(fe_cfg, idl) { if (ovsrec_fscs_is_valid(fe_cfg, br)) { opts->collector_set_id = fe_cfg->id; sset_init(&opts->targets); sset_add_array(&opts->targets, fe_cfg->ipfix->targets, fe_cfg->ipfix->n_targets); opts->cache_active_timeout = fe_cfg->ipfix->cache_active_timeout ? *fe_cfg->ipfix->cache_active_timeout : 0; opts->cache_max_flows = fe_cfg->ipfix->cache_max_flows ? *fe_cfg->ipfix->cache_max_flows : 0; opts++; } } } ofproto_set_ipfix(br->ofproto, valid_be_cfg ? &be_opts : NULL, fe_opts, n_fe_opts); if (valid_be_cfg) { sset_destroy(&be_opts.targets); } if (n_fe_opts > 0) { struct ofproto_ipfix_flow_exporter_options *opts = fe_opts; size_t i; for (i = 0; i < n_fe_opts; i++) { sset_destroy(&opts->targets); opts++; } free(fe_opts); } } static void port_configure_stp(const struct ofproto *ofproto, struct port *port, struct ofproto_port_stp_settings *port_s, int *port_num_counter, unsigned long *port_num_bitmap) { const char *config_str; struct iface *iface; if (!smap_get_bool(&port->cfg->other_config, "stp-enable", true)) { port_s->enable = false; return; } else { port_s->enable = true; } /* STP over bonds is not supported. */ if (!list_is_singleton(&port->ifaces)) { VLOG_ERR("port %s: cannot enable STP on bonds, disabling", port->name); port_s->enable = false; return; } iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); /* Internal ports shouldn't participate in spanning tree, so * skip them. */ if (!strcmp(iface->type, "internal")) { VLOG_DBG("port %s: disable STP on internal ports", port->name); port_s->enable = false; return; } /* STP on mirror output ports is not supported. */ if (ofproto_is_mirror_output_bundle(ofproto, port)) { VLOG_DBG("port %s: disable STP on mirror ports", port->name); port_s->enable = false; return; } config_str = smap_get(&port->cfg->other_config, "stp-port-num"); if (config_str) { unsigned long int port_num = strtoul(config_str, NULL, 0); int port_idx = port_num - 1; if (port_num < 1 || port_num > STP_MAX_PORTS) { VLOG_ERR("port %s: invalid stp-port-num", port->name); port_s->enable = false; return; } if (bitmap_is_set(port_num_bitmap, port_idx)) { VLOG_ERR("port %s: duplicate stp-port-num %lu, disabling", port->name, port_num); port_s->enable = false; return; } bitmap_set1(port_num_bitmap, port_idx); port_s->port_num = port_idx; } else { if (*port_num_counter >= STP_MAX_PORTS) { VLOG_ERR("port %s: too many STP ports, disabling", port->name); port_s->enable = false; return; } port_s->port_num = (*port_num_counter)++; } config_str = smap_get(&port->cfg->other_config, "stp-path-cost"); if (config_str) { port_s->path_cost = strtoul(config_str, NULL, 10); } else { enum netdev_features current; unsigned int mbps; netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL); mbps = netdev_features_to_bps(current, 100 * 1000 * 1000) / 1000000; port_s->path_cost = stp_convert_speed_to_cost(mbps); } config_str = smap_get(&port->cfg->other_config, "stp-port-priority"); if (config_str) { port_s->priority = strtoul(config_str, NULL, 0); } else { port_s->priority = STP_DEFAULT_PORT_PRIORITY; } } static void port_configure_rstp(const struct ofproto *ofproto, struct port *port, struct ofproto_port_rstp_settings *port_s, int *port_num_counter) { const char *config_str; struct iface *iface; if (!smap_get_bool(&port->cfg->other_config, "rstp-enable", true)) { port_s->enable = false; return; } else { port_s->enable = true; } /* RSTP over bonds is not supported. */ if (!list_is_singleton(&port->ifaces)) { VLOG_ERR("port %s: cannot enable RSTP on bonds, disabling", port->name); port_s->enable = false; return; } iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); /* Internal ports shouldn't participate in spanning tree, so * skip them. */ if (!strcmp(iface->type, "internal")) { VLOG_DBG("port %s: disable RSTP on internal ports", port->name); port_s->enable = false; return; } /* RSTP on mirror output ports is not supported. */ if (ofproto_is_mirror_output_bundle(ofproto, port)) { VLOG_DBG("port %s: disable RSTP on mirror ports", port->name); port_s->enable = false; return; } config_str = smap_get(&port->cfg->other_config, "rstp-port-num"); if (config_str) { unsigned long int port_num = strtoul(config_str, NULL, 0); if (port_num < 1 || port_num > RSTP_MAX_PORTS) { VLOG_ERR("port %s: invalid rstp-port-num", port->name); port_s->enable = false; return; } port_s->port_num = port_num; } else { if (*port_num_counter >= RSTP_MAX_PORTS) { VLOG_ERR("port %s: too many RSTP ports, disabling", port->name); port_s->enable = false; return; } /* If rstp-port-num is not specified, use 0. * rstp_port_set_port_number() will look for the first free one. */ port_s->port_num = 0; } /* Increment the port num counter, because we only support * RSTP_MAX_PORTS rstp ports. */ (*port_num_counter)++; config_str = smap_get(&port->cfg->other_config, "rstp-path-cost"); if (config_str) { port_s->path_cost = strtoul(config_str, NULL, 10); } else { enum netdev_features current; unsigned int mbps; netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL); mbps = netdev_features_to_bps(current, 100 * 1000 * 1000) / 1000000; port_s->path_cost = rstp_convert_speed_to_cost(mbps); } config_str = smap_get(&port->cfg->other_config, "rstp-port-priority"); if (config_str) { port_s->priority = strtoul(config_str, NULL, 0); } else { port_s->priority = RSTP_DEFAULT_PORT_PRIORITY; } config_str = smap_get(&port->cfg->other_config, "rstp-admin-p2p-mac"); if (config_str) { port_s->admin_p2p_mac_state = strtoul(config_str, NULL, 0); } else { port_s->admin_p2p_mac_state = RSTP_ADMIN_P2P_MAC_FORCE_TRUE; } port_s->admin_port_state = smap_get_bool(&port->cfg->other_config, "rstp-admin-port-state", true); port_s->admin_edge_port = smap_get_bool(&port->cfg->other_config, "rstp-port-admin-edge", false); port_s->auto_edge = smap_get_bool(&port->cfg->other_config, "rstp-port-auto-edge", true); port_s->mcheck = smap_get_bool(&port->cfg->other_config, "rstp-port-mcheck", false); } /* Set spanning tree configuration on 'br'. */ static void bridge_configure_stp(struct bridge *br, bool enable_stp) { if (!enable_stp) { ofproto_set_stp(br->ofproto, NULL); } else { struct ofproto_stp_settings br_s; const char *config_str; struct port *port; int port_num_counter; unsigned long *port_num_bitmap; config_str = smap_get(&br->cfg->other_config, "stp-system-id"); if (config_str) { struct eth_addr ea; if (eth_addr_from_string(config_str, &ea)) { br_s.system_id = eth_addr_to_uint64(ea); } else { br_s.system_id = eth_addr_to_uint64(br->ea); VLOG_ERR("bridge %s: invalid stp-system-id, defaulting " "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea)); } } else { br_s.system_id = eth_addr_to_uint64(br->ea); } config_str = smap_get(&br->cfg->other_config, "stp-priority"); if (config_str) { br_s.priority = strtoul(config_str, NULL, 0); } else { br_s.priority = STP_DEFAULT_BRIDGE_PRIORITY; } config_str = smap_get(&br->cfg->other_config, "stp-hello-time"); if (config_str) { br_s.hello_time = strtoul(config_str, NULL, 10) * 1000; } else { br_s.hello_time = STP_DEFAULT_HELLO_TIME; } config_str = smap_get(&br->cfg->other_config, "stp-max-age"); if (config_str) { br_s.max_age = strtoul(config_str, NULL, 10) * 1000; } else { br_s.max_age = STP_DEFAULT_MAX_AGE; } config_str = smap_get(&br->cfg->other_config, "stp-forward-delay"); if (config_str) { br_s.fwd_delay = strtoul(config_str, NULL, 10) * 1000; } else { br_s.fwd_delay = STP_DEFAULT_FWD_DELAY; } /* Configure STP on the bridge. */ if (ofproto_set_stp(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable STP", br->name); return; } /* Users must either set the port number with the "stp-port-num" * configuration on all ports or none. If manual configuration * is not done, then we allocate them sequentially. */ port_num_counter = 0; port_num_bitmap = bitmap_allocate(STP_MAX_PORTS); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_port_stp_settings port_s; struct iface *iface; port_configure_stp(br->ofproto, port, &port_s, &port_num_counter, port_num_bitmap); /* As bonds are not supported, just apply configuration to * all interfaces. */ LIST_FOR_EACH (iface, port_elem, &port->ifaces) { if (ofproto_port_set_stp(br->ofproto, iface->ofp_port, &port_s)) { VLOG_ERR("port %s: could not enable STP", port->name); continue; } } } if (bitmap_scan(port_num_bitmap, 1, 0, STP_MAX_PORTS) != STP_MAX_PORTS && port_num_counter) { VLOG_ERR("bridge %s: must manually configure all STP port " "IDs or none, disabling", br->name); ofproto_set_stp(br->ofproto, NULL); } bitmap_free(port_num_bitmap); } } static void bridge_configure_rstp(struct bridge *br, bool enable_rstp) { if (!enable_rstp) { ofproto_set_rstp(br->ofproto, NULL); } else { struct ofproto_rstp_settings br_s; const char *config_str; struct port *port; int port_num_counter; config_str = smap_get(&br->cfg->other_config, "rstp-address"); if (config_str) { struct eth_addr ea; if (eth_addr_from_string(config_str, &ea)) { br_s.address = eth_addr_to_uint64(ea); } else { br_s.address = eth_addr_to_uint64(br->ea); VLOG_ERR("bridge %s: invalid rstp-address, defaulting " "to "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(br->ea)); } } else { br_s.address = eth_addr_to_uint64(br->ea); } config_str = smap_get(&br->cfg->other_config, "rstp-priority"); if (config_str) { br_s.priority = strtoul(config_str, NULL, 0); } else { br_s.priority = RSTP_DEFAULT_PRIORITY; } config_str = smap_get(&br->cfg->other_config, "rstp-ageing-time"); if (config_str) { br_s.ageing_time = strtoul(config_str, NULL, 0); } else { br_s.ageing_time = RSTP_DEFAULT_AGEING_TIME; } config_str = smap_get(&br->cfg->other_config, "rstp-force-protocol-version"); if (config_str) { br_s.force_protocol_version = strtoul(config_str, NULL, 0); } else { br_s.force_protocol_version = FPV_DEFAULT; } config_str = smap_get(&br->cfg->other_config, "rstp-max-age"); if (config_str) { br_s.bridge_max_age = strtoul(config_str, NULL, 10); } else { br_s.bridge_max_age = RSTP_DEFAULT_BRIDGE_MAX_AGE; } config_str = smap_get(&br->cfg->other_config, "rstp-forward-delay"); if (config_str) { br_s.bridge_forward_delay = strtoul(config_str, NULL, 10); } else { br_s.bridge_forward_delay = RSTP_DEFAULT_BRIDGE_FORWARD_DELAY; } config_str = smap_get(&br->cfg->other_config, "rstp-transmit-hold-count"); if (config_str) { br_s.transmit_hold_count = strtoul(config_str, NULL, 10); } else { br_s.transmit_hold_count = RSTP_DEFAULT_TRANSMIT_HOLD_COUNT; } /* Configure RSTP on the bridge. */ if (ofproto_set_rstp(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable RSTP", br->name); return; } port_num_counter = 0; HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_port_rstp_settings port_s; struct iface *iface; port_configure_rstp(br->ofproto, port, &port_s, &port_num_counter); /* As bonds are not supported, just apply configuration to * all interfaces. */ LIST_FOR_EACH (iface, port_elem, &port->ifaces) { if (ofproto_port_set_rstp(br->ofproto, iface->ofp_port, &port_s)) { VLOG_ERR("port %s: could not enable RSTP", port->name); continue; } } } } } static void bridge_configure_spanning_tree(struct bridge *br) { bool enable_rstp = br->cfg->rstp_enable; bool enable_stp = br->cfg->stp_enable; if (enable_rstp && enable_stp) { VLOG_WARN("%s: RSTP and STP are mutually exclusive but both are " "configured; enabling RSTP", br->name); enable_stp = false; } bridge_configure_stp(br, enable_stp); bridge_configure_rstp(br, enable_rstp); } static bool bridge_has_bond_fake_iface(const struct bridge *br, const char *name) { const struct port *port = port_lookup(br, name); return port && port_is_bond_fake_iface(port); } static bool port_is_bond_fake_iface(const struct port *port) { return port->cfg->bond_fake_iface && !list_is_short(&port->ifaces); } static void add_del_bridges(const struct ovsrec_open_vswitch *cfg) { struct bridge *br, *next; struct shash_node *node; struct shash new_br; size_t i; /* Collect new bridges' names and types. */ shash_init(&new_br); for (i = 0; i < cfg->n_bridges; i++) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); const struct ovsrec_bridge *br_cfg = cfg->bridges[i]; if (strchr(br_cfg->name, '/')) { /* Prevent remote ovsdb-server users from accessing arbitrary * directories, e.g. consider a bridge named "../../../etc/". */ VLOG_WARN_RL(&rl, "ignoring bridge with invalid name \"%s\"", br_cfg->name); } else if (!shash_add_once(&new_br, br_cfg->name, br_cfg)) { VLOG_WARN_RL(&rl, "bridge %s specified twice", br_cfg->name); } } /* Get rid of deleted bridges or those whose types have changed. * Update 'cfg' of bridges that still exist. */ HMAP_FOR_EACH_SAFE (br, next, node, &all_bridges) { br->cfg = shash_find_data(&new_br, br->name); if (!br->cfg || strcmp(br->type, ofproto_normalize_type( br->cfg->datapath_type))) { bridge_destroy(br, true); } } /* Add new bridges. */ SHASH_FOR_EACH(node, &new_br) { const struct ovsrec_bridge *br_cfg = node->data; struct bridge *br = bridge_lookup(br_cfg->name); if (!br) { bridge_create(br_cfg); } } shash_destroy(&new_br); } /* Configures 'netdev' based on the "options" column in 'iface_cfg'. * Returns 0 if successful, otherwise a positive errno value. */ static int iface_set_netdev_config(const struct ovsrec_interface *iface_cfg, struct netdev *netdev, char **errp) { return netdev_set_config(netdev, &iface_cfg->options, errp); } /* Opens a network device for 'if_cfg' and configures it. Adds the network * device to br->ofproto and stores the OpenFlow port number in '*ofp_portp'. * * If successful, returns 0 and stores the network device in '*netdevp'. On * failure, returns a positive errno value and stores NULL in '*netdevp'. */ static int iface_do_create(const struct bridge *br, const struct ovsrec_interface *iface_cfg, const struct ovsrec_port *port_cfg, ofp_port_t *ofp_portp, struct netdev **netdevp, char **errp) { struct netdev *netdev = NULL; int error; const char *type; if (netdev_is_reserved_name(iface_cfg->name)) { VLOG_WARN("could not create interface %s, name is reserved", iface_cfg->name); error = EINVAL; goto error; } type = ofproto_port_open_type(br->cfg->datapath_type, iface_get_type(iface_cfg, br->cfg)); error = netdev_open(iface_cfg->name, type, &netdev); if (error) { VLOG_WARN_BUF(errp, "could not open network device %s (%s)", iface_cfg->name, ovs_strerror(error)); goto error; } error = iface_set_netdev_config(iface_cfg, netdev, errp); if (error) { goto error; } *ofp_portp = iface_pick_ofport(iface_cfg); error = ofproto_port_add(br->ofproto, netdev, ofp_portp); if (error) { goto error; } VLOG_INFO("bridge %s: added interface %s on port %d", br->name, iface_cfg->name, *ofp_portp); if (port_cfg->vlan_mode && !strcmp(port_cfg->vlan_mode, "splinter")) { netdev_turn_flags_on(netdev, NETDEV_UP, NULL); } *netdevp = netdev; return 0; error: *netdevp = NULL; netdev_close(netdev); return error; } /* Creates a new iface on 'br' based on 'if_cfg'. The new iface has OpenFlow * port number 'ofp_port'. If ofp_port is OFPP_NONE, an OpenFlow port is * automatically allocated for the iface. Takes ownership of and * deallocates 'if_cfg'. * * Return true if an iface is successfully created, false otherwise. */ static bool iface_create(struct bridge *br, const struct ovsrec_interface *iface_cfg, const struct ovsrec_port *port_cfg) { struct netdev *netdev; struct iface *iface; ofp_port_t ofp_port; struct port *port; char *errp = NULL; int error; /* Do the bits that can fail up front. */ ovs_assert(!iface_lookup(br, iface_cfg->name)); error = iface_do_create(br, iface_cfg, port_cfg, &ofp_port, &netdev, &errp); if (error) { iface_clear_db_record(iface_cfg, errp); free(errp); return false; } /* Get or create the port structure. */ port = port_lookup(br, port_cfg->name); if (!port) { port = port_create(br, port_cfg); } /* Create the iface structure. */ iface = xzalloc(sizeof *iface); list_push_back(&port->ifaces, &iface->port_elem); hmap_insert(&br->iface_by_name, &iface->name_node, hash_string(iface_cfg->name, 0)); iface->port = port; iface->name = xstrdup(iface_cfg->name); iface->ofp_port = ofp_port; iface->netdev = netdev; iface->type = iface_get_type(iface_cfg, br->cfg); iface->netdev_type = ofproto_port_open_type(br->cfg->datapath_type, iface->type); iface->cfg = iface_cfg; hmap_insert(&br->ifaces, &iface->ofp_port_node, hash_ofp_port(ofp_port)); /* Populate initial status in database. */ iface_refresh_stats(iface); iface_refresh_netdev_status(iface); /* Add bond fake iface if necessary. */ if (port_is_bond_fake_iface(port)) { struct ofproto_port ofproto_port; if (ofproto_port_query_by_name(br->ofproto, port->name, &ofproto_port)) { struct netdev *netdev; int error; error = netdev_open(port->name, "internal", &netdev); if (!error) { ofp_port_t fake_ofp_port = OFPP_NONE; ofproto_port_add(br->ofproto, netdev, &fake_ofp_port); netdev_close(netdev); } else { VLOG_WARN("could not open network device %s (%s)", port->name, ovs_strerror(error)); } } else { /* Already exists, nothing to do. */ ofproto_port_destroy(&ofproto_port); } } return true; } /* Set forward BPDU option. */ static void bridge_configure_forward_bpdu(struct bridge *br) { ofproto_set_forward_bpdu(br->ofproto, smap_get_bool(&br->cfg->other_config, "forward-bpdu", false)); } /* Set MAC learning table configuration for 'br'. */ static void bridge_configure_mac_table(struct bridge *br) { const char *idle_time_str; int idle_time; const char *mac_table_size_str; int mac_table_size; idle_time_str = smap_get(&br->cfg->other_config, "mac-aging-time"); idle_time = (idle_time_str && atoi(idle_time_str) ? atoi(idle_time_str) : MAC_ENTRY_DEFAULT_IDLE_TIME); mac_table_size_str = smap_get(&br->cfg->other_config, "mac-table-size"); mac_table_size = (mac_table_size_str && atoi(mac_table_size_str) ? atoi(mac_table_size_str) : MAC_DEFAULT_MAX); ofproto_set_mac_table_config(br->ofproto, idle_time, mac_table_size); } /* Set multicast snooping table configuration for 'br'. */ static void bridge_configure_mcast_snooping(struct bridge *br) { if (!br->cfg->mcast_snooping_enable) { ofproto_set_mcast_snooping(br->ofproto, NULL); } else { struct port *port; struct ofproto_mcast_snooping_settings br_s; const char *idle_time_str; const char *max_entries_str; idle_time_str = smap_get(&br->cfg->other_config, "mcast-snooping-aging-time"); br_s.idle_time = (idle_time_str && atoi(idle_time_str) ? atoi(idle_time_str) : MCAST_ENTRY_DEFAULT_IDLE_TIME); max_entries_str = smap_get(&br->cfg->other_config, "mcast-snooping-table-size"); br_s.max_entries = (max_entries_str && atoi(max_entries_str) ? atoi(max_entries_str) : MCAST_DEFAULT_MAX_ENTRIES); br_s.flood_unreg = !smap_get_bool(&br->cfg->other_config, "mcast-snooping-disable-flood-unregistered", false); /* Configure multicast snooping on the bridge */ if (ofproto_set_mcast_snooping(br->ofproto, &br_s)) { VLOG_ERR("bridge %s: could not enable multicast snooping", br->name); return; } HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct ofproto_mcast_snooping_port_settings port_s; port_s.flood = smap_get_bool(&port->cfg->other_config, "mcast-snooping-flood", false); port_s.flood_reports = smap_get_bool(&port->cfg->other_config, "mcast-snooping-flood-reports", false); if (ofproto_port_set_mcast_snooping(br->ofproto, port, &port_s)) { VLOG_ERR("port %s: could not configure mcast snooping", port->name); } } } } static void find_local_hw_addr(const struct bridge *br, struct eth_addr *ea, const struct port *fake_br, struct iface **hw_addr_iface) { struct hmapx mirror_output_ports; struct port *port; bool found_addr = false; int error; int i; /* Mirror output ports don't participate in picking the local hardware * address. ofproto can't help us find out whether a given port is a * mirror output because we haven't configured mirrors yet, so we need to * accumulate them ourselves. */ hmapx_init(&mirror_output_ports); for (i = 0; i < br->cfg->n_mirrors; i++) { struct ovsrec_mirror *m = br->cfg->mirrors[i]; if (m->output_port) { hmapx_add(&mirror_output_ports, m->output_port); } } /* Otherwise choose the minimum non-local MAC address among all of the * interfaces. */ HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct eth_addr iface_ea; struct iface *candidate; struct iface *iface; /* Mirror output ports don't participate. */ if (hmapx_contains(&mirror_output_ports, port->cfg)) { continue; } /* Choose the MAC address to represent the port. */ iface = NULL; if (port->cfg->mac && eth_addr_from_string(port->cfg->mac, &iface_ea)) { /* Find the interface with this Ethernet address (if any) so that * we can provide the correct devname to the caller. */ LIST_FOR_EACH (candidate, port_elem, &port->ifaces) { struct eth_addr candidate_ea; if (!netdev_get_etheraddr(candidate->netdev, &candidate_ea) && eth_addr_equals(iface_ea, candidate_ea)) { iface = candidate; } } } else { /* Choose the interface whose MAC address will represent the port. * The Linux kernel bonding code always chooses the MAC address of * the first slave added to a bond, and the Fedora networking * scripts always add slaves to a bond in alphabetical order, so * for compatibility we choose the interface with the name that is * first in alphabetical order. */ LIST_FOR_EACH (candidate, port_elem, &port->ifaces) { if (!iface || strcmp(candidate->name, iface->name) < 0) { iface = candidate; } } /* The local port doesn't count (since we're trying to choose its * MAC address anyway). */ if (iface->ofp_port == OFPP_LOCAL) { continue; } /* For fake bridges we only choose from ports with the same tag */ if (fake_br && fake_br->cfg && fake_br->cfg->tag) { if (!port->cfg->tag) { continue; } if (*port->cfg->tag != *fake_br->cfg->tag) { continue; } } /* Grab MAC. */ error = netdev_get_etheraddr(iface->netdev, &iface_ea); if (error) { continue; } } /* Compare against our current choice. */ if (!eth_addr_is_multicast(iface_ea) && !eth_addr_is_local(iface_ea) && !eth_addr_is_reserved(iface_ea) && !eth_addr_is_zero(iface_ea) && (!found_addr || eth_addr_compare_3way(iface_ea, *ea) < 0)) { *ea = iface_ea; *hw_addr_iface = iface; found_addr = true; } } if (!found_addr) { *ea = br->default_ea; *hw_addr_iface = NULL; } hmapx_destroy(&mirror_output_ports); } static void bridge_pick_local_hw_addr(struct bridge *br, struct eth_addr *ea, struct iface **hw_addr_iface) { const char *hwaddr; *hw_addr_iface = NULL; /* Did the user request a particular MAC? */ hwaddr = smap_get(&br->cfg->other_config, "hwaddr"); if (hwaddr && eth_addr_from_string(hwaddr, ea)) { if (eth_addr_is_multicast(*ea)) { VLOG_ERR("bridge %s: cannot set MAC address to multicast " "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(*ea)); } else if (eth_addr_is_zero(*ea)) { VLOG_ERR("bridge %s: cannot set MAC address to zero", br->name); } else { return; } } /* Find a local hw address */ find_local_hw_addr(br, ea, NULL, hw_addr_iface); } /* Choose and returns the datapath ID for bridge 'br' given that the bridge * Ethernet address is 'bridge_ea'. If 'bridge_ea' is the Ethernet address of * an interface on 'br', then that interface must be passed in as * 'hw_addr_iface'; if 'bridge_ea' was derived some other way, then * 'hw_addr_iface' must be passed in as a null pointer. */ static uint64_t bridge_pick_datapath_id(struct bridge *br, const struct eth_addr bridge_ea, struct iface *hw_addr_iface) { /* * The procedure for choosing a bridge MAC address will, in the most * ordinary case, also choose a unique MAC that we can use as a datapath * ID. In some special cases, though, multiple bridges will end up with * the same MAC address. This is OK for the bridges, but it will confuse * the OpenFlow controller, because each datapath needs a unique datapath * ID. * * Datapath IDs must be unique. It is also very desirable that they be * stable from one run to the next, so that policy set on a datapath * "sticks". */ const char *datapath_id; uint64_t dpid; datapath_id = smap_get(&br->cfg->other_config, "datapath-id"); if (datapath_id && dpid_from_string(datapath_id, &dpid)) { return dpid; } if (!hw_addr_iface) { /* * A purely internal bridge, that is, one that has no non-virtual * network devices on it at all, is difficult because it has no * natural unique identifier at all. * * When the host is a XenServer, we handle this case by hashing the * host's UUID with the name of the bridge. Names of bridges are * persistent across XenServer reboots, although they can be reused if * an internal network is destroyed and then a new one is later * created, so this is fairly effective. * * When the host is not a XenServer, we punt by using a random MAC * address on each run. */ const char *host_uuid = xenserver_get_host_uuid(); if (host_uuid) { char *combined = xasprintf("%s,%s", host_uuid, br->name); dpid = dpid_from_hash(combined, strlen(combined)); free(combined); return dpid; } } return eth_addr_to_uint64(bridge_ea); } static uint64_t dpid_from_hash(const void *data, size_t n) { union { uint8_t bytes[SHA1_DIGEST_SIZE]; struct eth_addr ea; } hash; sha1_bytes(data, n, hash.bytes); eth_addr_mark_random(&hash.ea); return eth_addr_to_uint64(hash.ea); } static void iface_refresh_netdev_status(struct iface *iface) { struct smap smap; enum netdev_features current; enum netdev_flags flags; const char *link_state; struct eth_addr mac; int64_t bps, mtu_64, ifindex64, link_resets; int mtu, error; if (iface_is_synthetic(iface)) { return; } if (iface->change_seq == netdev_get_change_seq(iface->netdev) && !status_txn_try_again) { return; } iface->change_seq = netdev_get_change_seq(iface->netdev); smap_init(&smap); if (!netdev_get_status(iface->netdev, &smap)) { ovsrec_interface_set_status(iface->cfg, &smap); } else { ovsrec_interface_set_status(iface->cfg, NULL); } smap_destroy(&smap); error = netdev_get_flags(iface->netdev, &flags); if (!error) { const char *state = flags & NETDEV_UP ? "up" : "down"; ovsrec_interface_set_admin_state(iface->cfg, state); } else { ovsrec_interface_set_admin_state(iface->cfg, NULL); } link_state = netdev_get_carrier(iface->netdev) ? "up" : "down"; ovsrec_interface_set_link_state(iface->cfg, link_state); link_resets = netdev_get_carrier_resets(iface->netdev); ovsrec_interface_set_link_resets(iface->cfg, &link_resets, 1); error = netdev_get_features(iface->netdev, ¤t, NULL, NULL, NULL); bps = !error ? netdev_features_to_bps(current, 0) : 0; if (bps) { ovsrec_interface_set_duplex(iface->cfg, netdev_features_is_full_duplex(current) ? "full" : "half"); ovsrec_interface_set_link_speed(iface->cfg, &bps, 1); } else { ovsrec_interface_set_duplex(iface->cfg, NULL); ovsrec_interface_set_link_speed(iface->cfg, NULL, 0); } error = netdev_get_mtu(iface->netdev, &mtu); if (!error) { mtu_64 = mtu; ovsrec_interface_set_mtu(iface->cfg, &mtu_64, 1); } else { ovsrec_interface_set_mtu(iface->cfg, NULL, 0); } error = netdev_get_etheraddr(iface->netdev, &mac); if (!error) { char mac_string[32]; sprintf(mac_string, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); ovsrec_interface_set_mac_in_use(iface->cfg, mac_string); } else { ovsrec_interface_set_mac_in_use(iface->cfg, NULL); } /* The netdev may return a negative number (such as -EOPNOTSUPP) * if there is no valid ifindex number. */ ifindex64 = netdev_get_ifindex(iface->netdev); if (ifindex64 < 0) { ifindex64 = 0; } ovsrec_interface_set_ifindex(iface->cfg, &ifindex64, 1); } static void iface_refresh_ofproto_status(struct iface *iface) { int current; if (iface_is_synthetic(iface)) { return; } current = ofproto_port_is_lacp_current(iface->port->bridge->ofproto, iface->ofp_port); if (current >= 0) { bool bl = current; ovsrec_interface_set_lacp_current(iface->cfg, &bl, 1); } else { ovsrec_interface_set_lacp_current(iface->cfg, NULL, 0); } if (ofproto_port_cfm_status_changed(iface->port->bridge->ofproto, iface->ofp_port) || status_txn_try_again) { iface_refresh_cfm_stats(iface); } if (ofproto_port_bfd_status_changed(iface->port->bridge->ofproto, iface->ofp_port) || status_txn_try_again) { struct smap smap; smap_init(&smap); ofproto_port_get_bfd_status(iface->port->bridge->ofproto, iface->ofp_port, &smap); ovsrec_interface_set_bfd_status(iface->cfg, &smap); smap_destroy(&smap); } } /* Writes 'iface''s CFM statistics to the database. 'iface' must not be * synthetic. */ static void iface_refresh_cfm_stats(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; struct cfm_status status; int error; error = ofproto_port_get_cfm_status(iface->port->bridge->ofproto, iface->ofp_port, &status); if (error > 0) { ovsrec_interface_set_cfm_fault(cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); ovsrec_interface_set_cfm_flap_count(cfg, NULL, 0); ovsrec_interface_set_cfm_health(cfg, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0); } else { const char *reasons[CFM_FAULT_N_REASONS]; int64_t cfm_health = status.health; int64_t cfm_flap_count = status.flap_count; bool faulted = status.faults != 0; size_t i, j; ovsrec_interface_set_cfm_fault(cfg, &faulted, 1); j = 0; for (i = 0; i < CFM_FAULT_N_REASONS; i++) { int reason = 1 << i; if (status.faults & reason) { reasons[j++] = cfm_fault_reason_to_str(reason); } } ovsrec_interface_set_cfm_fault_status(cfg, reasons, j); ovsrec_interface_set_cfm_flap_count(cfg, &cfm_flap_count, 1); if (status.remote_opstate >= 0) { const char *remote_opstate = status.remote_opstate ? "up" : "down"; ovsrec_interface_set_cfm_remote_opstate(cfg, remote_opstate); } else { ovsrec_interface_set_cfm_remote_opstate(cfg, NULL); } ovsrec_interface_set_cfm_remote_mpids(cfg, (const int64_t *)status.rmps, status.n_rmps); if (cfm_health >= 0) { ovsrec_interface_set_cfm_health(cfg, &cfm_health, 1); } else { ovsrec_interface_set_cfm_health(cfg, NULL, 0); } free(status.rmps); } } static void iface_refresh_stats(struct iface *iface) { #define IFACE_STATS \ IFACE_STAT(rx_packets, "rx_packets") \ IFACE_STAT(tx_packets, "tx_packets") \ IFACE_STAT(rx_bytes, "rx_bytes") \ IFACE_STAT(tx_bytes, "tx_bytes") \ IFACE_STAT(rx_dropped, "rx_dropped") \ IFACE_STAT(tx_dropped, "tx_dropped") \ IFACE_STAT(rx_errors, "rx_errors") \ IFACE_STAT(tx_errors, "tx_errors") \ IFACE_STAT(rx_frame_errors, "rx_frame_err") \ IFACE_STAT(rx_over_errors, "rx_over_err") \ IFACE_STAT(rx_crc_errors, "rx_crc_err") \ IFACE_STAT(collisions, "collisions") #define IFACE_STAT(MEMBER, NAME) + 1 enum { N_IFACE_STATS = IFACE_STATS }; #undef IFACE_STAT int64_t values[N_IFACE_STATS]; const char *keys[N_IFACE_STATS]; int n; struct netdev_stats stats; if (iface_is_synthetic(iface)) { return; } /* Intentionally ignore return value, since errors will set 'stats' to * all-1s, and we will deal with that correctly below. */ netdev_get_stats(iface->netdev, &stats); /* Copy statistics into keys[] and values[]. */ n = 0; #define IFACE_STAT(MEMBER, NAME) \ if (stats.MEMBER != UINT64_MAX) { \ keys[n] = NAME; \ values[n] = stats.MEMBER; \ n++; \ } IFACE_STATS; #undef IFACE_STAT ovs_assert(n <= N_IFACE_STATS); ovsrec_interface_set_statistics(iface->cfg, keys, values, n); #undef IFACE_STATS } static void br_refresh_datapath_info(struct bridge *br) { const char *version; version = (br->ofproto && br->ofproto->ofproto_class->get_datapath_version ? br->ofproto->ofproto_class->get_datapath_version(br->ofproto) : NULL); ovsrec_bridge_set_datapath_version(br->cfg, version ? version : ""); } static void br_refresh_stp_status(struct bridge *br) { struct smap smap = SMAP_INITIALIZER(&smap); struct ofproto *ofproto = br->ofproto; struct ofproto_stp_status status; if (ofproto_get_stp_status(ofproto, &status)) { return; } if (!status.enabled) { ovsrec_bridge_set_status(br->cfg, NULL); return; } smap_add_format(&smap, "stp_bridge_id", STP_ID_FMT, STP_ID_ARGS(status.bridge_id)); smap_add_format(&smap, "stp_designated_root", STP_ID_FMT, STP_ID_ARGS(status.designated_root)); smap_add_format(&smap, "stp_root_path_cost", "%d", status.root_path_cost); ovsrec_bridge_set_status(br->cfg, &smap); smap_destroy(&smap); } static void port_refresh_stp_status(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_stp_status status; struct smap smap; if (port_is_synthetic(port)) { return; } /* STP doesn't currently support bonds. */ if (!list_is_singleton(&port->ifaces)) { ovsrec_port_set_status(port->cfg, NULL); return; } iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_stp_status(ofproto, iface->ofp_port, &status)) { return; } if (!status.enabled) { ovsrec_port_set_status(port->cfg, NULL); return; } /* Set Status column. */ smap_init(&smap); smap_add_format(&smap, "stp_port_id", STP_PORT_ID_FMT, status.port_id); smap_add(&smap, "stp_state", stp_state_name(status.state)); smap_add_format(&smap, "stp_sec_in_state", "%u", status.sec_in_state); smap_add(&smap, "stp_role", stp_role_name(status.role)); ovsrec_port_set_status(port->cfg, &smap); smap_destroy(&smap); } static void port_refresh_stp_stats(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_stp_stats stats; const char *keys[3]; int64_t int_values[3]; if (port_is_synthetic(port)) { return; } /* STP doesn't currently support bonds. */ if (!list_is_singleton(&port->ifaces)) { return; } iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_stp_stats(ofproto, iface->ofp_port, &stats)) { return; } if (!stats.enabled) { ovsrec_port_set_statistics(port->cfg, NULL, NULL, 0); return; } /* Set Statistics column. */ keys[0] = "stp_tx_count"; int_values[0] = stats.tx_count; keys[1] = "stp_rx_count"; int_values[1] = stats.rx_count; keys[2] = "stp_error_count"; int_values[2] = stats.error_count; ovsrec_port_set_statistics(port->cfg, keys, int_values, ARRAY_SIZE(int_values)); } static void br_refresh_rstp_status(struct bridge *br) { struct smap smap = SMAP_INITIALIZER(&smap); struct ofproto *ofproto = br->ofproto; struct ofproto_rstp_status status; if (ofproto_get_rstp_status(ofproto, &status)) { return; } if (!status.enabled) { ovsrec_bridge_set_rstp_status(br->cfg, NULL); return; } smap_add_format(&smap, "rstp_bridge_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.bridge_id)); smap_add_format(&smap, "rstp_root_path_cost", "%"PRIu32, status.root_path_cost); smap_add_format(&smap, "rstp_root_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.root_id)); smap_add_format(&smap, "rstp_designated_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.designated_id)); smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT, status.designated_port_id); smap_add_format(&smap, "rstp_bridge_port_id", RSTP_PORT_ID_FMT, status.bridge_port_id); ovsrec_bridge_set_rstp_status(br->cfg, &smap); smap_destroy(&smap); } static void port_refresh_rstp_status(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; struct iface *iface; struct ofproto_port_rstp_status status; const char *keys[4]; int64_t int_values[4]; struct smap smap; if (port_is_synthetic(port)) { return; } /* RSTP doesn't currently support bonds. */ if (!list_is_singleton(&port->ifaces)) { ovsrec_port_set_rstp_status(port->cfg, NULL); return; } iface = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); if (ofproto_port_get_rstp_status(ofproto, iface->ofp_port, &status)) { return; } if (!status.enabled) { ovsrec_port_set_rstp_status(port->cfg, NULL); ovsrec_port_set_rstp_statistics(port->cfg, NULL, NULL, 0); return; } /* Set Status column. */ smap_init(&smap); smap_add_format(&smap, "rstp_port_id", RSTP_PORT_ID_FMT, status.port_id); smap_add_format(&smap, "rstp_port_role", "%s", rstp_port_role_name(status.role)); smap_add_format(&smap, "rstp_port_state", "%s", rstp_state_name(status.state)); smap_add_format(&smap, "rstp_designated_bridge_id", RSTP_ID_FMT, RSTP_ID_ARGS(status.designated_bridge_id)); smap_add_format(&smap, "rstp_designated_port_id", RSTP_PORT_ID_FMT, status.designated_port_id); smap_add_format(&smap, "rstp_designated_path_cost", "%"PRIu32, status.designated_path_cost); ovsrec_port_set_rstp_status(port->cfg, &smap); smap_destroy(&smap); /* Set Statistics column. */ keys[0] = "rstp_tx_count"; int_values[0] = status.tx_count; keys[1] = "rstp_rx_count"; int_values[1] = status.rx_count; keys[2] = "rstp_uptime"; int_values[2] = status.uptime; keys[3] = "rstp_error_count"; int_values[3] = status.error_count; ovsrec_port_set_rstp_statistics(port->cfg, keys, int_values, ARRAY_SIZE(int_values)); } static void port_refresh_bond_status(struct port *port, bool force_update) { struct eth_addr mac; /* Return if port is not a bond */ if (list_is_singleton(&port->ifaces)) { return; } if (bond_get_changed_active_slave(port->name, &mac, force_update)) { struct ds mac_s; ds_init(&mac_s); ds_put_format(&mac_s, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac)); ovsrec_port_set_bond_active_slave(port->cfg, ds_cstr(&mac_s)); ds_destroy(&mac_s); } } static bool enable_system_stats(const struct ovsrec_open_vswitch *cfg) { return smap_get_bool(&cfg->other_config, "enable-statistics", false); } static void reconfigure_system_stats(const struct ovsrec_open_vswitch *cfg) { bool enable = enable_system_stats(cfg); system_stats_enable(enable); if (!enable) { ovsrec_open_vswitch_set_statistics(cfg, NULL); } } static void run_system_stats(void) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl); struct smap *stats; stats = system_stats_run(); if (stats && cfg) { struct ovsdb_idl_txn *txn; struct ovsdb_datum datum; txn = ovsdb_idl_txn_create(idl); ovsdb_datum_from_smap(&datum, stats); ovsdb_idl_txn_write(&cfg->header_, &ovsrec_open_vswitch_col_statistics, &datum); ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); free(stats); } } static const char * ofp12_controller_role_to_str(enum ofp12_controller_role role) { switch (role) { case OFPCR12_ROLE_EQUAL: return "other"; case OFPCR12_ROLE_MASTER: return "master"; case OFPCR12_ROLE_SLAVE: return "slave"; case OFPCR12_ROLE_NOCHANGE: default: return "*** INVALID ROLE ***"; } } static void refresh_controller_status(void) { struct bridge *br; struct shash info; const struct ovsrec_controller *cfg; shash_init(&info); /* Accumulate status for controllers on all bridges. */ HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_get_ofproto_controller_info(br->ofproto, &info); } /* Update each controller in the database with current status. */ OVSREC_CONTROLLER_FOR_EACH(cfg, idl) { struct ofproto_controller_info *cinfo = shash_find_data(&info, cfg->target); if (cinfo) { ovsrec_controller_set_is_connected(cfg, cinfo->is_connected); ovsrec_controller_set_role(cfg, ofp12_controller_role_to_str( cinfo->role)); ovsrec_controller_set_status(cfg, &cinfo->pairs); } else { ovsrec_controller_set_is_connected(cfg, false); ovsrec_controller_set_role(cfg, NULL); ovsrec_controller_set_status(cfg, NULL); } } ofproto_free_ofproto_controller_info(&info); } /* Update interface and mirror statistics if necessary. */ static void run_stats_update(void) { const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(idl); int stats_interval; if (!cfg) { return; } /* Statistics update interval should always be greater than or equal to * 5000 ms. */ stats_interval = MAX(smap_get_int(&cfg->other_config, "stats-update-interval", 5000), 5000); if (stats_timer_interval != stats_interval) { stats_timer_interval = stats_interval; stats_timer = LLONG_MIN; } if (time_msec() >= stats_timer) { enum ovsdb_idl_txn_status status; /* Rate limit the update. Do not start a new update if the * previous one is not done. */ if (!stats_txn) { struct bridge *br; stats_txn = ovsdb_idl_txn_create(idl); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; struct mirror *m; HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_refresh_stats(iface); } port_refresh_stp_stats(port); } HMAP_FOR_EACH (m, hmap_node, &br->mirrors) { mirror_refresh_stats(m); } } refresh_controller_status(); } status = ovsdb_idl_txn_commit(stats_txn); if (status != TXN_INCOMPLETE) { stats_timer = time_msec() + stats_timer_interval; ovsdb_idl_txn_destroy(stats_txn); stats_txn = NULL; } } } static void stats_update_wait(void) { /* If the 'stats_txn' is non-null (transaction incomplete), waits for the * transaction to complete. Otherwise, waits for the 'stats_timer'. */ if (stats_txn) { ovsdb_idl_txn_wait(stats_txn); } else { poll_timer_wait_until(stats_timer); } } /* Update bridge/port/interface status if necessary. */ static void run_status_update(void) { if (!status_txn) { uint64_t seq; /* Rate limit the update. Do not start a new update if the * previous one is not done. */ seq = seq_read(connectivity_seq_get()); if (seq != connectivity_seqno || status_txn_try_again) { struct bridge *br; connectivity_seqno = seq; status_txn = ovsdb_idl_txn_create(idl); HMAP_FOR_EACH (br, node, &all_bridges) { struct port *port; br_refresh_stp_status(br); br_refresh_rstp_status(br); br_refresh_datapath_info(br); HMAP_FOR_EACH (port, hmap_node, &br->ports) { struct iface *iface; port_refresh_stp_status(port); port_refresh_rstp_status(port); port_refresh_bond_status(port, status_txn_try_again); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { iface_refresh_netdev_status(iface); iface_refresh_ofproto_status(iface); } } } } } /* Commit the transaction and get the status. If the transaction finishes, * then destroy the transaction. Otherwise, keep it so that we can check * progress the next time that this function is called. */ if (status_txn) { enum ovsdb_idl_txn_status status; status = ovsdb_idl_txn_commit(status_txn); if (status != TXN_INCOMPLETE) { ovsdb_idl_txn_destroy(status_txn); status_txn = NULL; /* Sets the 'status_txn_try_again' if the transaction fails. */ if (status == TXN_SUCCESS || status == TXN_UNCHANGED) { status_txn_try_again = false; } else { status_txn_try_again = true; } } } /* Refresh AA port status if necessary. */ if (time_msec() >= aa_refresh_timer) { struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { if (bridge_aa_need_refresh(br)) { struct ovsdb_idl_txn *txn; txn = ovsdb_idl_txn_create(idl); bridge_aa_refresh_queued(br); ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); } } aa_refresh_timer = time_msec() + AA_REFRESH_INTERVAL; } } static void status_update_wait(void) { /* If the 'status_txn' is non-null (transaction incomplete), waits for the * transaction to complete. If the status update to database needs to be * run again (transaction fails), registers a timeout in * 'STATUS_CHECK_AGAIN_MSEC'. Otherwise, waits on the global connectivity * sequence number. */ if (status_txn) { ovsdb_idl_txn_wait(status_txn); } else if (status_txn_try_again) { poll_timer_wait_until(time_msec() + STATUS_CHECK_AGAIN_MSEC); } else { seq_wait(connectivity_seq_get(), connectivity_seqno); } } static void bridge_run__(void) { struct bridge *br; struct sset types; const char *type; /* Let each datapath type do the work that it needs to do. */ sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_run(type); } sset_destroy(&types); /* Let each bridge do the work that it needs to do. */ HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_run(br->ofproto); } } void bridge_run(void) { static struct ovsrec_open_vswitch null_cfg; const struct ovsrec_open_vswitch *cfg; bool vlan_splinters_changed; ovsrec_open_vswitch_init(&null_cfg); ovsdb_idl_run(idl); if_notifier_run(); if (ovsdb_idl_is_lock_contended(idl)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); struct bridge *br, *next_br; VLOG_ERR_RL(&rl, "another ovs-vswitchd process is running, " "disabling this process (pid %ld) until it goes away", (long int) getpid()); HMAP_FOR_EACH_SAFE (br, next_br, node, &all_bridges) { bridge_destroy(br, false); } /* Since we will not be running system_stats_run() in this process * with the current situation of multiple ovs-vswitchd daemons, * disable system stats collection. */ system_stats_enable(false); return; } else if (!ovsdb_idl_has_lock(idl) || !ovsdb_idl_has_ever_connected(idl)) { /* Returns if not holding the lock or not done retrieving db * contents. */ return; } cfg = ovsrec_open_vswitch_first(idl); /* Initialize the ofproto library. This only needs to run once, but * it must be done after the configuration is set. If the * initialization has already occurred, bridge_init_ofproto() * returns immediately. */ bridge_init_ofproto(cfg); /* Once the value of flow-restore-wait is false, we no longer should * check its value from the database. */ if (cfg && ofproto_get_flow_restore_wait()) { ofproto_set_flow_restore_wait(smap_get_bool(&cfg->other_config, "flow-restore-wait", false)); } bridge_run__(); /* Re-configure SSL. We do this on every trip through the main loop, * instead of just when the database changes, because the contents of the * key and certificate files can change without the database changing. * * We do this before bridge_reconfigure() because that function might * initiate SSL connections and thus requires SSL to be configured. */ if (cfg && cfg->ssl) { const struct ovsrec_ssl *ssl = cfg->ssl; stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate); stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert); } /* If VLAN splinters are in use, then we need to reconfigure if VLAN * usage has changed. */ vlan_splinters_changed = false; if (vlan_splinters_enabled_anywhere) { struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { if (ofproto_has_vlan_usage_changed(br->ofproto)) { vlan_splinters_changed = true; break; } } } if (ovsdb_idl_get_seqno(idl) != idl_seqno || vlan_splinters_changed || if_notifier_changed(ifnotifier)) { struct ovsdb_idl_txn *txn; idl_seqno = ovsdb_idl_get_seqno(idl); txn = ovsdb_idl_txn_create(idl); bridge_reconfigure(cfg ? cfg : &null_cfg); if (cfg) { ovsrec_open_vswitch_set_cur_cfg(cfg, cfg->next_cfg); discover_types(cfg); } /* If we are completing our initial configuration for this run * of ovs-vswitchd, then keep the transaction around to monitor * it for completion. */ if (initial_config_done) { /* Always sets the 'status_txn_try_again' to check again, * in case that this transaction fails. */ status_txn_try_again = true; ovsdb_idl_txn_commit(txn); ovsdb_idl_txn_destroy(txn); } else { initial_config_done = true; daemonize_txn = txn; } } if (daemonize_txn) { enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(daemonize_txn); if (status != TXN_INCOMPLETE) { ovsdb_idl_txn_destroy(daemonize_txn); daemonize_txn = NULL; /* ovs-vswitchd has completed initialization, so allow the * process that forked us to exit successfully. */ daemonize_complete(); vlog_enable_async(); VLOG_INFO_ONCE("%s (Open vSwitch) %s", program_name, VERSION); } } run_stats_update(); run_status_update(); run_system_stats(); } void bridge_wait(void) { struct sset types; const char *type; ovsdb_idl_wait(idl); if (daemonize_txn) { ovsdb_idl_txn_wait(daemonize_txn); } if_notifier_wait(); sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_wait(type); } sset_destroy(&types); if (!hmap_is_empty(&all_bridges)) { struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_wait(br->ofproto); } stats_update_wait(); status_update_wait(); } system_stats_wait(); } /* Adds some memory usage statistics for bridges into 'usage', for use with * memory_report(). */ void bridge_get_memory_usage(struct simap *usage) { struct bridge *br; struct sset types; const char *type; sset_init(&types); ofproto_enumerate_types(&types); SSET_FOR_EACH (type, &types) { ofproto_type_get_memory_usage(type, usage); } sset_destroy(&types); HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_get_memory_usage(br->ofproto, usage); } } /* QoS unixctl user interface functions. */ struct qos_unixctl_show_cbdata { struct ds *ds; struct iface *iface; }; static void qos_unixctl_show_queue(unsigned int queue_id, const struct smap *details, struct iface *iface, struct ds *ds) { struct netdev_queue_stats stats; struct smap_node *node; int error; ds_put_cstr(ds, "\n"); if (queue_id) { ds_put_format(ds, "Queue %u:\n", queue_id); } else { ds_put_cstr(ds, "Default:\n"); } SMAP_FOR_EACH (node, details) { ds_put_format(ds, "\t%s: %s\n", node->key, node->value); } error = netdev_get_queue_stats(iface->netdev, queue_id, &stats); if (!error) { if (stats.tx_packets != UINT64_MAX) { ds_put_format(ds, "\ttx_packets: %"PRIu64"\n", stats.tx_packets); } if (stats.tx_bytes != UINT64_MAX) { ds_put_format(ds, "\ttx_bytes: %"PRIu64"\n", stats.tx_bytes); } if (stats.tx_errors != UINT64_MAX) { ds_put_format(ds, "\ttx_errors: %"PRIu64"\n", stats.tx_errors); } } else { ds_put_format(ds, "\tFailed to get statistics for queue %u: %s", queue_id, ovs_strerror(error)); } } static void qos_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct smap smap = SMAP_INITIALIZER(&smap); struct iface *iface; const char *type; struct smap_node *node; int error; iface = iface_find(argv[1]); if (!iface) { unixctl_command_reply_error(conn, "no such interface"); return; } error = netdev_get_qos(iface->netdev, &type, &smap); if (!error) { if (*type != '\0') { struct netdev_queue_dump dump; struct smap details; unsigned int queue_id; ds_put_format(&ds, "QoS: %s %s\n", iface->name, type); SMAP_FOR_EACH (node, &smap) { ds_put_format(&ds, "%s: %s\n", node->key, node->value); } smap_init(&details); NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) { qos_unixctl_show_queue(queue_id, &details, iface, &ds); } smap_destroy(&details); unixctl_command_reply(conn, ds_cstr(&ds)); } else { ds_put_format(&ds, "QoS not configured on %s\n", iface->name); unixctl_command_reply_error(conn, ds_cstr(&ds)); } } else { ds_put_format(&ds, "%s: failed to retrieve QOS configuration (%s)\n", iface->name, ovs_strerror(error)); unixctl_command_reply_error(conn, ds_cstr(&ds)); } smap_destroy(&smap); ds_destroy(&ds); } /* Bridge reconfiguration functions. */ static void bridge_create(const struct ovsrec_bridge *br_cfg) { struct bridge *br; ovs_assert(!bridge_lookup(br_cfg->name)); br = xzalloc(sizeof *br); br->name = xstrdup(br_cfg->name); br->type = xstrdup(ofproto_normalize_type(br_cfg->datapath_type)); br->cfg = br_cfg; /* Derive the default Ethernet address from the bridge's UUID. This should * be unique and it will be stable between ovs-vswitchd runs. */ memcpy(&br->default_ea, &br_cfg->header_.uuid, ETH_ADDR_LEN); eth_addr_mark_random(&br->default_ea); hmap_init(&br->ports); hmap_init(&br->ifaces); hmap_init(&br->iface_by_name); hmap_init(&br->mirrors); hmap_init(&br->mappings); hmap_insert(&all_bridges, &br->node, hash_string(br->name, 0)); } static void bridge_destroy(struct bridge *br, bool del) { if (br) { struct mirror *mirror, *next_mirror; struct port *port, *next_port; HMAP_FOR_EACH_SAFE (port, next_port, hmap_node, &br->ports) { port_destroy(port); } HMAP_FOR_EACH_SAFE (mirror, next_mirror, hmap_node, &br->mirrors) { mirror_destroy(mirror); } hmap_remove(&all_bridges, &br->node); ofproto_destroy(br->ofproto, del); hmap_destroy(&br->ifaces); hmap_destroy(&br->ports); hmap_destroy(&br->iface_by_name); hmap_destroy(&br->mirrors); hmap_destroy(&br->mappings); free(br->name); free(br->type); free(br); } } static struct bridge * bridge_lookup(const char *name) { struct bridge *br; HMAP_FOR_EACH_WITH_HASH (br, node, hash_string(name, 0), &all_bridges) { if (!strcmp(br->name, name)) { return br; } } return NULL; } /* Handle requests for a listing of all flows known by the OpenFlow * stack, including those normally hidden. */ static void bridge_unixctl_dump_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; struct ds results; br = bridge_lookup(argv[1]); if (!br) { unixctl_command_reply_error(conn, "Unknown bridge"); return; } ds_init(&results); ofproto_get_all_flows(br->ofproto, &results); unixctl_command_reply(conn, ds_cstr(&results)); ds_destroy(&results); } /* "bridge/reconnect [BRIDGE]": makes BRIDGE drop all of its controller * connections and reconnect. If BRIDGE is not specified, then all bridges * drop their controller connections and reconnect. */ static void bridge_unixctl_reconnect(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct bridge *br; if (argc > 1) { br = bridge_lookup(argv[1]); if (!br) { unixctl_command_reply_error(conn, "Unknown bridge"); return; } ofproto_reconnect_controllers(br->ofproto); } else { HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_reconnect_controllers(br->ofproto); } } unixctl_command_reply(conn, NULL); } static size_t bridge_get_controllers(const struct bridge *br, struct ovsrec_controller ***controllersp) { struct ovsrec_controller **controllers; size_t n_controllers; controllers = br->cfg->controller; n_controllers = br->cfg->n_controller; if (n_controllers == 1 && !strcmp(controllers[0]->target, "none")) { controllers = NULL; n_controllers = 0; } if (controllersp) { *controllersp = controllers; } return n_controllers; } static void bridge_collect_wanted_ports(struct bridge *br, const unsigned long int *splinter_vlans, struct shash *wanted_ports) { size_t i; shash_init(wanted_ports); for (i = 0; i < br->cfg->n_ports; i++) { const char *name = br->cfg->ports[i]->name; if (!shash_add_once(wanted_ports, name, br->cfg->ports[i])) { VLOG_WARN("bridge %s: %s specified twice as bridge port", br->name, name); } } if (bridge_get_controllers(br, NULL) && !shash_find(wanted_ports, br->name)) { VLOG_WARN("bridge %s: no port named %s, synthesizing one", br->name, br->name); ovsrec_interface_init(&br->synth_local_iface); ovsrec_port_init(&br->synth_local_port); br->synth_local_port.interfaces = &br->synth_local_ifacep; br->synth_local_port.n_interfaces = 1; br->synth_local_port.name = br->name; br->synth_local_iface.name = br->name; br->synth_local_iface.type = "internal"; br->synth_local_ifacep = &br->synth_local_iface; shash_add(wanted_ports, br->name, &br->synth_local_port); } if (splinter_vlans) { add_vlan_splinter_ports(br, splinter_vlans, wanted_ports); } } /* Deletes "struct port"s and "struct iface"s under 'br' which aren't * consistent with 'br->cfg'. Updates 'br->if_cfg_queue' with interfaces which * 'br' needs to complete its configuration. */ static void bridge_del_ports(struct bridge *br, const struct shash *wanted_ports) { struct shash_node *port_node; struct port *port, *next; /* Get rid of deleted ports. * Get rid of deleted interfaces on ports that still exist. */ HMAP_FOR_EACH_SAFE (port, next, hmap_node, &br->ports) { port->cfg = shash_find_data(wanted_ports, port->name); if (!port->cfg) { port_destroy(port); } else { port_del_ifaces(port); } } /* Update iface->cfg and iface->type in interfaces that still exist. */ SHASH_FOR_EACH (port_node, wanted_ports) { const struct ovsrec_port *port = port_node->data; size_t i; for (i = 0; i < port->n_interfaces; i++) { const struct ovsrec_interface *cfg = port->interfaces[i]; struct iface *iface = iface_lookup(br, cfg->name); const char *type = iface_get_type(cfg, br->cfg); const char *dp_type = br->cfg->datapath_type; const char *netdev_type = ofproto_port_open_type(dp_type, type); if (iface) { iface->cfg = cfg; iface->type = type; iface->netdev_type = netdev_type; } else if (!strcmp(type, "null")) { VLOG_WARN_ONCE("%s: The null interface type is deprecated and" " may be removed in February 2013. Please email" " dev@openvswitch.org with concerns.", cfg->name); } else { /* We will add new interfaces later. */ } } } } /* Initializes 'oc' appropriately as a management service controller for * 'br'. * * The caller must free oc->target when it is no longer needed. */ static void bridge_ofproto_controller_for_mgmt(const struct bridge *br, struct ofproto_controller *oc) { oc->target = xasprintf("punix:%s/%s.mgmt", ovs_rundir(), br->name); oc->max_backoff = 0; oc->probe_interval = 60; oc->band = OFPROTO_OUT_OF_BAND; oc->rate_limit = 0; oc->burst_limit = 0; oc->enable_async_msgs = true; oc->dscp = 0; } /* Converts ovsrec_controller 'c' into an ofproto_controller in 'oc'. */ static void bridge_ofproto_controller_from_ovsrec(const struct ovsrec_controller *c, struct ofproto_controller *oc) { int dscp; oc->target = c->target; oc->max_backoff = c->max_backoff ? *c->max_backoff / 1000 : 8; oc->probe_interval = c->inactivity_probe ? *c->inactivity_probe / 1000 : 5; oc->band = (!c->connection_mode || !strcmp(c->connection_mode, "in-band") ? OFPROTO_IN_BAND : OFPROTO_OUT_OF_BAND); oc->rate_limit = c->controller_rate_limit ? *c->controller_rate_limit : 0; oc->burst_limit = (c->controller_burst_limit ? *c->controller_burst_limit : 0); oc->enable_async_msgs = (!c->enable_async_messages || *c->enable_async_messages); dscp = smap_get_int(&c->other_config, "dscp", DSCP_DEFAULT); if (dscp < 0 || dscp > 63) { dscp = DSCP_DEFAULT; } oc->dscp = dscp; } /* Configures the IP stack for 'br''s local interface properly according to the * configuration in 'c'. */ static void bridge_configure_local_iface_netdev(struct bridge *br, struct ovsrec_controller *c) { struct netdev *netdev; struct in_addr mask, gateway; struct iface *local_iface; struct in_addr ip; /* If there's no local interface or no IP address, give up. */ local_iface = iface_from_ofp_port(br, OFPP_LOCAL); if (!local_iface || !c->local_ip || !inet_pton(AF_INET, c->local_ip, &ip)) { return; } /* Bring up the local interface. */ netdev = local_iface->netdev; netdev_turn_flags_on(netdev, NETDEV_UP, NULL); /* Configure the IP address and netmask. */ if (!c->local_netmask || !inet_pton(AF_INET, c->local_netmask, &mask) || !mask.s_addr) { mask.s_addr = guess_netmask(ip.s_addr); } if (!netdev_set_in4(netdev, ip, mask)) { VLOG_INFO("bridge %s: configured IP address "IP_FMT", netmask "IP_FMT, br->name, IP_ARGS(ip.s_addr), IP_ARGS(mask.s_addr)); } /* Configure the default gateway. */ if (c->local_gateway && inet_pton(AF_INET, c->local_gateway, &gateway) && gateway.s_addr) { if (!netdev_add_router(netdev, gateway)) { VLOG_INFO("bridge %s: configured gateway "IP_FMT, br->name, IP_ARGS(gateway.s_addr)); } } } /* Returns true if 'a' and 'b' are the same except that any number of slashes * in either string are treated as equal to any number of slashes in the other, * e.g. "x///y" is equal to "x/y". * * Also, if 'b_stoplen' bytes from 'b' are found to be equal to corresponding * bytes from 'a', the function considers this success. Specify 'b_stoplen' as * SIZE_MAX to compare all of 'a' to all of 'b' rather than just a prefix of * 'b' against a prefix of 'a'. */ static bool equal_pathnames(const char *a, const char *b, size_t b_stoplen) { const char *b_start = b; for (;;) { if (b - b_start >= b_stoplen) { return true; } else if (*a != *b) { return false; } else if (*a == '/') { a += strspn(a, "/"); b += strspn(b, "/"); } else if (*a == '\0') { return true; } else { a++; b++; } } } static void bridge_configure_remotes(struct bridge *br, const struct sockaddr_in *managers, size_t n_managers) { bool disable_in_band; struct ovsrec_controller **controllers; size_t n_controllers; enum ofproto_fail_mode fail_mode; struct ofproto_controller *ocs; size_t n_ocs; size_t i; /* Check if we should disable in-band control on this bridge. */ disable_in_band = smap_get_bool(&br->cfg->other_config, "disable-in-band", false); /* Set OpenFlow queue ID for in-band control. */ ofproto_set_in_band_queue(br->ofproto, smap_get_int(&br->cfg->other_config, "in-band-queue", -1)); if (disable_in_band) { ofproto_set_extra_in_band_remotes(br->ofproto, NULL, 0); } else { ofproto_set_extra_in_band_remotes(br->ofproto, managers, n_managers); } n_controllers = (ofproto_get_flow_restore_wait() ? 0 : bridge_get_controllers(br, &controllers)); ocs = xmalloc((n_controllers + 1) * sizeof *ocs); n_ocs = 0; bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]); for (i = 0; i < n_controllers; i++) { struct ovsrec_controller *c = controllers[i]; if (!strncmp(c->target, "punix:", 6) || !strncmp(c->target, "unix:", 5)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); char *whitelist; if (!strncmp(c->target, "unix:", 5)) { /* Connect to a listening socket */ whitelist = xasprintf("unix:%s/", ovs_rundir()); if (strchr(c->target, '/') && !equal_pathnames(c->target, whitelist, strlen(whitelist))) { /* Absolute path specified, but not in ovs_rundir */ VLOG_ERR_RL(&rl, "bridge %s: Not connecting to socket " "controller \"%s\" due to possibility for " "remote exploit. Instead, specify socket " "in whitelisted \"%s\" or connect to " "\"unix:%s/%s.mgmt\" (which is always " "available without special configuration).", br->name, c->target, whitelist, ovs_rundir(), br->name); free(whitelist); continue; } } else { whitelist = xasprintf("punix:%s/%s.", ovs_rundir(), br->name); if (!equal_pathnames(c->target, whitelist, strlen(whitelist)) || strchr(c->target + strlen(whitelist), '/')) { /* Prevent remote ovsdb-server users from accessing * arbitrary Unix domain sockets and overwriting arbitrary * local files. */ VLOG_ERR_RL(&rl, "bridge %s: Not adding Unix domain socket " "controller \"%s\" due to possibility of " "overwriting local files. Instead, specify " "path in whitelisted format \"%s*\" or " "connect to \"unix:%s/%s.mgmt\" (which is " "always available without special " "configuration).", br->name, c->target, whitelist, ovs_rundir(), br->name); free(whitelist); continue; } } free(whitelist); } bridge_configure_local_iface_netdev(br, c); bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]); if (disable_in_band) { ocs[n_ocs].band = OFPROTO_OUT_OF_BAND; } n_ocs++; } ofproto_set_controllers(br->ofproto, ocs, n_ocs, bridge_get_allowed_versions(br)); free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */ free(ocs); /* Set the fail-mode. */ fail_mode = !br->cfg->fail_mode || !strcmp(br->cfg->fail_mode, "standalone") ? OFPROTO_FAIL_STANDALONE : OFPROTO_FAIL_SECURE; ofproto_set_fail_mode(br->ofproto, fail_mode); /* Configure OpenFlow controller connection snooping. */ if (!ofproto_has_snoops(br->ofproto)) { struct sset snoops; sset_init(&snoops); sset_add_and_free(&snoops, xasprintf("punix:%s/%s.snoop", ovs_rundir(), br->name)); ofproto_set_snoops(br->ofproto, &snoops); sset_destroy(&snoops); } } static void bridge_configure_tables(struct bridge *br) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); int n_tables; int i, j, k; n_tables = ofproto_get_n_tables(br->ofproto); j = 0; for (i = 0; i < n_tables; i++) { struct ofproto_table_settings s; bool use_default_prefixes = true; s.name = NULL; s.max_flows = UINT_MAX; s.groups = NULL; s.enable_eviction = false; s.n_groups = 0; s.n_prefix_fields = 0; memset(s.prefix_fields, ~0, sizeof(s.prefix_fields)); if (j < br->cfg->n_flow_tables && i == br->cfg->key_flow_tables[j]) { struct ovsrec_flow_table *cfg = br->cfg->value_flow_tables[j++]; s.name = cfg->name; if (cfg->n_flow_limit && *cfg->flow_limit < UINT_MAX) { s.max_flows = *cfg->flow_limit; } s.enable_eviction = (cfg->overflow_policy && !strcmp(cfg->overflow_policy, "evict")); if (cfg->n_groups) { s.groups = xmalloc(cfg->n_groups * sizeof *s.groups); for (k = 0; k < cfg->n_groups; k++) { const char *string = cfg->groups[k]; char *msg; msg = mf_parse_subfield__(&s.groups[k], &string); if (msg) { VLOG_WARN_RL(&rl, "bridge %s table %d: error parsing " "'groups' (%s)", br->name, i, msg); free(msg); } else if (*string) { VLOG_WARN_RL(&rl, "bridge %s table %d: 'groups' " "element '%s' contains trailing garbage", br->name, i, cfg->groups[k]); } else { s.n_groups++; } } } /* Prefix lookup fields. */ s.n_prefix_fields = 0; for (k = 0; k < cfg->n_prefixes; k++) { const char *name = cfg->prefixes[k]; const struct mf_field *mf; if (strcmp(name, "none") == 0) { use_default_prefixes = false; s.n_prefix_fields = 0; break; } mf = mf_from_name(name); if (!mf) { VLOG_WARN("bridge %s: 'prefixes' with unknown field: %s", br->name, name); continue; } if (mf->flow_be32ofs < 0 || mf->n_bits % 32) { VLOG_WARN("bridge %s: 'prefixes' with incompatible field: " "%s", br->name, name); continue; } if (s.n_prefix_fields >= ARRAY_SIZE(s.prefix_fields)) { VLOG_WARN("bridge %s: 'prefixes' with too many fields, " "field not used: %s", br->name, name); continue; } use_default_prefixes = false; s.prefix_fields[s.n_prefix_fields++] = mf->id; } } if (use_default_prefixes) { /* Use default values. */ s.n_prefix_fields = ARRAY_SIZE(default_prefix_fields); memcpy(s.prefix_fields, default_prefix_fields, sizeof default_prefix_fields); } else { int k; struct ds ds = DS_EMPTY_INITIALIZER; for (k = 0; k < s.n_prefix_fields; k++) { if (k) { ds_put_char(&ds, ','); } ds_put_cstr(&ds, mf_from_id(s.prefix_fields[k])->name); } if (s.n_prefix_fields == 0) { ds_put_cstr(&ds, "none"); } VLOG_INFO("bridge %s table %d: Prefix lookup with: %s.", br->name, i, ds_cstr(&ds)); ds_destroy(&ds); } ofproto_configure_table(br->ofproto, i, &s); free(s.groups); } for (; j < br->cfg->n_flow_tables; j++) { VLOG_WARN_RL(&rl, "bridge %s: ignoring configuration for flow table " "%"PRId64" not supported by this datapath", br->name, br->cfg->key_flow_tables[j]); } } static void bridge_configure_dp_desc(struct bridge *br) { ofproto_set_dp_desc(br->ofproto, smap_get(&br->cfg->other_config, "dp-desc")); } static struct aa_mapping * bridge_aa_mapping_find(struct bridge *br, const int64_t isid) { struct aa_mapping *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, hash_bytes(&isid, sizeof isid, 0), &br->mappings) { if (isid == m->isid) { return m; } } return NULL; } static struct aa_mapping * bridge_aa_mapping_create(struct bridge *br, const int64_t isid, const int64_t vlan) { struct aa_mapping *m; m = xzalloc(sizeof *m); m->bridge = br; m->isid = isid; m->vlan = vlan; m->br_name = xstrdup(br->name); hmap_insert(&br->mappings, &m->hmap_node, hash_bytes(&isid, sizeof isid, 0)); return m; } static void bridge_aa_mapping_destroy(struct aa_mapping *m) { if (m) { struct bridge *br = m->bridge; if (br->ofproto) { ofproto_aa_mapping_unregister(br->ofproto, m); } hmap_remove(&br->mappings, &m->hmap_node); if (m->br_name) { free(m->br_name); } free(m); } } static bool bridge_aa_mapping_configure(struct aa_mapping *m) { struct aa_mapping_settings s; s.isid = m->isid; s.vlan = m->vlan; /* Configure. */ ofproto_aa_mapping_register(m->bridge->ofproto, m, &s); return true; } static void bridge_configure_aa(struct bridge *br) { const struct ovsdb_datum *mc; struct ovsrec_autoattach *auto_attach = br->cfg->auto_attach; struct aa_settings aa_s; struct aa_mapping *m, *next; size_t i; if (!auto_attach) { ofproto_set_aa(br->ofproto, NULL, NULL); return; } memset(&aa_s, 0, sizeof aa_s); aa_s.system_description = auto_attach->system_description; aa_s.system_name = auto_attach->system_name; ofproto_set_aa(br->ofproto, NULL, &aa_s); mc = ovsrec_autoattach_get_mappings(auto_attach, OVSDB_TYPE_INTEGER, OVSDB_TYPE_INTEGER); HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mappings) { union ovsdb_atom atom; atom.integer = m->isid; if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_INTEGER) == UINT_MAX) { VLOG_INFO("Deleting isid=%"PRIu32", vlan=%"PRIu16, m->isid, m->vlan); bridge_aa_mapping_destroy(m); } } /* Add new mappings and reconfigure existing ones. */ for (i = 0; i < auto_attach->n_mappings; ++i) { struct aa_mapping *m = bridge_aa_mapping_find(br, auto_attach->key_mappings[i]); if (!m) { VLOG_INFO("Adding isid=%"PRId64", vlan=%"PRId64, auto_attach->key_mappings[i], auto_attach->value_mappings[i]); m = bridge_aa_mapping_create(br, auto_attach->key_mappings[i], auto_attach->value_mappings[i]); if (!bridge_aa_mapping_configure(m)) { bridge_aa_mapping_destroy(m); } } } } static bool bridge_aa_need_refresh(struct bridge *br) { return ofproto_aa_vlan_get_queue_size(br->ofproto) > 0; } static void bridge_aa_update_trunks(struct port *port, struct bridge_aa_vlan *m) { int64_t *trunks = NULL; unsigned int i = 0; bool found = false, reconfigure = false; for (i = 0; i < port->cfg->n_trunks; i++) { if (port->cfg->trunks[i] == m->vlan) { found = true; break; } } switch (m->oper) { case BRIDGE_AA_VLAN_OPER_ADD: if (!found) { trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks + 1)); for (i = 0; i < port->cfg->n_trunks; i++) { trunks[i] = port->cfg->trunks[i]; } trunks[i++] = m->vlan; reconfigure = true; } break; case BRIDGE_AA_VLAN_OPER_REMOVE: if (found) { unsigned int j = 0; trunks = xmalloc(sizeof *trunks * (port->cfg->n_trunks - 1)); for (i = 0; i < port->cfg->n_trunks; i++) { if (port->cfg->trunks[i] != m->vlan) { trunks[j++] = port->cfg->trunks[i]; } } i = j; reconfigure = true; } break; case BRIDGE_AA_VLAN_OPER_UNDEF: default: VLOG_WARN("unrecognized operation %u", m->oper); break; } if (reconfigure) { /* VLAN switching under trunk mode cause the trunk port to switch all * VLANs, see ovs-vswitchd.conf.db */ if (i == 0) { static char *vlan_mode_access = "access"; ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_access); } if (i == 1) { static char *vlan_mode_trunk = "trunk"; ovsrec_port_set_vlan_mode(port->cfg, vlan_mode_trunk); } ovsrec_port_set_trunks(port->cfg, trunks, i); /* Force reconfigure of the port. */ port_configure(port); } free(trunks); } static void bridge_aa_refresh_queued(struct bridge *br) { struct ovs_list *list = xmalloc(sizeof *list); struct bridge_aa_vlan *node, *next; list_init(list); ofproto_aa_vlan_get_queued(br->ofproto, list); LIST_FOR_EACH_SAFE (node, next, list_node, list) { struct port *port; VLOG_INFO("ifname=%s, vlan=%u, oper=%u", node->port_name, node->vlan, node->oper); port = port_lookup(br, node->port_name); if (port) { bridge_aa_update_trunks(port, node); } list_remove(&node->list_node); free(node->port_name); free(node); } free(list); } /* Port functions. */ static struct port * port_create(struct bridge *br, const struct ovsrec_port *cfg) { struct port *port; port = xzalloc(sizeof *port); port->bridge = br; port->name = xstrdup(cfg->name); port->cfg = cfg; list_init(&port->ifaces); hmap_insert(&br->ports, &port->hmap_node, hash_string(port->name, 0)); return port; } /* Deletes interfaces from 'port' that are no longer configured for it. */ static void port_del_ifaces(struct port *port) { struct iface *iface, *next; struct sset new_ifaces; size_t i; /* Collect list of new interfaces. */ sset_init(&new_ifaces); for (i = 0; i < port->cfg->n_interfaces; i++) { const char *name = port->cfg->interfaces[i]->name; const char *type = port->cfg->interfaces[i]->type; if (strcmp(type, "null")) { sset_add(&new_ifaces, name); } } /* Get rid of deleted interfaces. */ LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) { if (!sset_contains(&new_ifaces, iface->name)) { iface_destroy(iface); } } sset_destroy(&new_ifaces); } static void port_destroy(struct port *port) { if (port) { struct bridge *br = port->bridge; struct iface *iface, *next; if (br->ofproto) { ofproto_bundle_unregister(br->ofproto, port); } LIST_FOR_EACH_SAFE (iface, next, port_elem, &port->ifaces) { iface_destroy__(iface); } hmap_remove(&br->ports, &port->hmap_node); free(port->name); free(port); } } static struct port * port_lookup(const struct bridge *br, const char *name) { struct port *port; HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_string(name, 0), &br->ports) { if (!strcmp(port->name, name)) { return port; } } return NULL; } static bool enable_lacp(struct port *port, bool *activep) { if (!port->cfg->lacp) { /* XXX when LACP implementation has been sufficiently tested, enable by * default and make active on bonded ports. */ return false; } else if (!strcmp(port->cfg->lacp, "off")) { return false; } else if (!strcmp(port->cfg->lacp, "active")) { *activep = true; return true; } else if (!strcmp(port->cfg->lacp, "passive")) { *activep = false; return true; } else { VLOG_WARN("port %s: unknown LACP mode %s", port->name, port->cfg->lacp); return false; } } static struct lacp_settings * port_configure_lacp(struct port *port, struct lacp_settings *s) { const char *lacp_time, *system_id; int priority; if (!enable_lacp(port, &s->active)) { return NULL; } s->name = port->name; system_id = smap_get(&port->cfg->other_config, "lacp-system-id"); if (system_id) { if (!ovs_scan(system_id, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(s->id))) { VLOG_WARN("port %s: LACP system ID (%s) must be an Ethernet" " address.", port->name, system_id); return NULL; } } else { s->id = port->bridge->ea; } if (eth_addr_is_zero(s->id)) { VLOG_WARN("port %s: Invalid zero LACP system ID.", port->name); return NULL; } /* Prefer bondable links if unspecified. */ priority = smap_get_int(&port->cfg->other_config, "lacp-system-priority", 0); s->priority = (priority > 0 && priority <= UINT16_MAX ? priority : UINT16_MAX - !list_is_short(&port->ifaces)); lacp_time = smap_get(&port->cfg->other_config, "lacp-time"); s->fast = lacp_time && !strcasecmp(lacp_time, "fast"); s->fallback_ab_cfg = smap_get_bool(&port->cfg->other_config, "lacp-fallback-ab", false); return s; } static void iface_configure_lacp(struct iface *iface, struct lacp_slave_settings *s) { int priority, portid, key; portid = smap_get_int(&iface->cfg->other_config, "lacp-port-id", 0); priority = smap_get_int(&iface->cfg->other_config, "lacp-port-priority", 0); key = smap_get_int(&iface->cfg->other_config, "lacp-aggregation-key", 0); if (portid <= 0 || portid > UINT16_MAX) { portid = ofp_to_u16(iface->ofp_port); } if (priority <= 0 || priority > UINT16_MAX) { priority = UINT16_MAX; } if (key < 0 || key > UINT16_MAX) { key = 0; } s->name = iface->name; s->id = portid; s->priority = priority; s->key = key; } static void port_configure_bond(struct port *port, struct bond_settings *s) { const char *detect_s; struct iface *iface; const char *mac_s; int miimon_interval; s->name = port->name; s->balance = BM_AB; if (port->cfg->bond_mode) { if (!bond_mode_from_string(&s->balance, port->cfg->bond_mode)) { VLOG_WARN("port %s: unknown bond_mode %s, defaulting to %s", port->name, port->cfg->bond_mode, bond_mode_to_string(s->balance)); } } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); /* XXX: Post version 1.5.*, the default bond_mode changed from SLB to * active-backup. At some point we should remove this warning. */ VLOG_WARN_RL(&rl, "port %s: Using the default bond_mode %s. Note that" " in previous versions, the default bond_mode was" " balance-slb", port->name, bond_mode_to_string(s->balance)); } if (s->balance == BM_SLB && port->bridge->cfg->n_flood_vlans) { VLOG_WARN("port %s: SLB bonds are incompatible with flood_vlans, " "please use another bond type or disable flood_vlans", port->name); } miimon_interval = smap_get_int(&port->cfg->other_config, "bond-miimon-interval", 0); if (miimon_interval <= 0) { miimon_interval = 200; } detect_s = smap_get(&port->cfg->other_config, "bond-detect-mode"); if (!detect_s || !strcmp(detect_s, "carrier")) { miimon_interval = 0; } else if (strcmp(detect_s, "miimon")) { VLOG_WARN("port %s: unsupported bond-detect-mode %s, " "defaulting to carrier", port->name, detect_s); miimon_interval = 0; } s->up_delay = MAX(0, port->cfg->bond_updelay); s->down_delay = MAX(0, port->cfg->bond_downdelay); s->basis = smap_get_int(&port->cfg->other_config, "bond-hash-basis", 0); s->rebalance_interval = smap_get_int(&port->cfg->other_config, "bond-rebalance-interval", 10000); if (s->rebalance_interval && s->rebalance_interval < 1000) { s->rebalance_interval = 1000; } s->lacp_fallback_ab_cfg = smap_get_bool(&port->cfg->other_config, "lacp-fallback-ab", false); LIST_FOR_EACH (iface, port_elem, &port->ifaces) { netdev_set_miimon_interval(iface->netdev, miimon_interval); } mac_s = port->cfg->bond_active_slave; if (!mac_s || !ovs_scan(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(s->active_slave_mac))) { /* OVSDB did not store the last active interface */ s->active_slave_mac = eth_addr_zero; } } /* Returns true if 'port' is synthetic, that is, if we constructed it locally * instead of obtaining it from the database. */ static bool port_is_synthetic(const struct port *port) { return ovsdb_idl_row_is_synthetic(&port->cfg->header_); } /* Interface functions. */ static bool iface_is_internal(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br) { /* The local port and "internal" ports are always "internal". */ return !strcmp(iface->type, "internal") || !strcmp(iface->name, br->name); } /* Returns the correct network device type for interface 'iface' in bridge * 'br'. */ static const char * iface_get_type(const struct ovsrec_interface *iface, const struct ovsrec_bridge *br) { const char *type; /* The local port always has type "internal". Other ports take * their type from the database and default to "system" if none is * specified. */ if (iface_is_internal(iface, br)) { type = "internal"; } else { type = iface->type[0] ? iface->type : "system"; } return type; } static void iface_destroy__(struct iface *iface) { if (iface) { struct port *port = iface->port; struct bridge *br = port->bridge; VLOG_INFO("bridge %s: deleted interface %s on port %d", br->name, iface->name, iface->ofp_port); if (br->ofproto && iface->ofp_port != OFPP_NONE) { ofproto_port_unregister(br->ofproto, iface->ofp_port); } if (iface->ofp_port != OFPP_NONE) { hmap_remove(&br->ifaces, &iface->ofp_port_node); } list_remove(&iface->port_elem); hmap_remove(&br->iface_by_name, &iface->name_node); /* The user is changing configuration here, so netdev_remove needs to be * used as opposed to netdev_close */ netdev_remove(iface->netdev); free(iface->name); free(iface); } } static void iface_destroy(struct iface *iface) { if (iface) { struct port *port = iface->port; iface_destroy__(iface); if (list_is_empty(&port->ifaces)) { port_destroy(port); } } } static struct iface * iface_lookup(const struct bridge *br, const char *name) { struct iface *iface; HMAP_FOR_EACH_WITH_HASH (iface, name_node, hash_string(name, 0), &br->iface_by_name) { if (!strcmp(iface->name, name)) { return iface; } } return NULL; } static struct iface * iface_find(const char *name) { const struct bridge *br; HMAP_FOR_EACH (br, node, &all_bridges) { struct iface *iface = iface_lookup(br, name); if (iface) { return iface; } } return NULL; } static struct iface * iface_from_ofp_port(const struct bridge *br, ofp_port_t ofp_port) { struct iface *iface; HMAP_FOR_EACH_IN_BUCKET (iface, ofp_port_node, hash_ofp_port(ofp_port), &br->ifaces) { if (iface->ofp_port == ofp_port) { return iface; } } return NULL; } /* Set Ethernet address of 'iface', if one is specified in the configuration * file. */ static void iface_set_mac(const struct bridge *br, const struct port *port, struct iface *iface) { struct eth_addr ea, *mac = NULL; struct iface *hw_addr_iface; if (strcmp(iface->type, "internal")) { return; } if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, &ea)) { mac = &ea; } else if (port->cfg->fake_bridge) { /* Fake bridge and no MAC set in the configuration. Pick a local one. */ find_local_hw_addr(br, &ea, port, &hw_addr_iface); mac = &ea; } if (mac) { if (iface->ofp_port == OFPP_LOCAL) { VLOG_ERR("interface %s: ignoring mac in Interface record " "(use Bridge record to set local port's mac)", iface->name); } else if (eth_addr_is_multicast(*mac)) { VLOG_ERR("interface %s: cannot set MAC to multicast address", iface->name); } else { int error = netdev_set_etheraddr(iface->netdev, *mac); if (error) { VLOG_ERR("interface %s: setting MAC failed (%s)", iface->name, ovs_strerror(error)); } } } } /* Sets the ofport column of 'if_cfg' to 'ofport'. */ static void iface_set_ofport(const struct ovsrec_interface *if_cfg, ofp_port_t ofport) { if (if_cfg && !ovsdb_idl_row_is_synthetic(&if_cfg->header_)) { int64_t port = ofport == OFPP_NONE ? -1 : ofp_to_u16(ofport); ovsrec_interface_set_ofport(if_cfg, &port, 1); } } /* Clears all of the fields in 'if_cfg' that indicate interface status, and * sets the "ofport" field to -1. * * This is appropriate when 'if_cfg''s interface cannot be created or is * otherwise invalid. */ static void iface_clear_db_record(const struct ovsrec_interface *if_cfg, char *errp) { if (!ovsdb_idl_row_is_synthetic(&if_cfg->header_)) { iface_set_ofport(if_cfg, OFPP_NONE); ovsrec_interface_set_error(if_cfg, errp); ovsrec_interface_set_status(if_cfg, NULL); ovsrec_interface_set_admin_state(if_cfg, NULL); ovsrec_interface_set_duplex(if_cfg, NULL); ovsrec_interface_set_link_speed(if_cfg, NULL, 0); ovsrec_interface_set_link_state(if_cfg, NULL); ovsrec_interface_set_mac_in_use(if_cfg, NULL); ovsrec_interface_set_mtu(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault(if_cfg, NULL, 0); ovsrec_interface_set_cfm_fault_status(if_cfg, NULL, 0); ovsrec_interface_set_cfm_remote_mpids(if_cfg, NULL, 0); ovsrec_interface_set_lacp_current(if_cfg, NULL, 0); ovsrec_interface_set_statistics(if_cfg, NULL, NULL, 0); ovsrec_interface_set_ifindex(if_cfg, NULL, 0); } } static bool queue_ids_include(const struct ovsdb_datum *queues, int64_t target) { union ovsdb_atom atom; atom.integer = target; return ovsdb_datum_find_key(queues, &atom, OVSDB_TYPE_INTEGER) != UINT_MAX; } static void iface_configure_qos(struct iface *iface, const struct ovsrec_qos *qos) { struct ofpbuf queues_buf; ofpbuf_init(&queues_buf, 0); if (!qos || qos->type[0] == '\0') { netdev_set_qos(iface->netdev, NULL, NULL); } else { const struct ovsdb_datum *queues; struct netdev_queue_dump dump; unsigned int queue_id; struct smap details; bool queue_zero; size_t i; /* Configure top-level Qos for 'iface'. */ netdev_set_qos(iface->netdev, qos->type, &qos->other_config); /* Deconfigure queues that were deleted. */ queues = ovsrec_qos_get_queues(qos, OVSDB_TYPE_INTEGER, OVSDB_TYPE_UUID); smap_init(&details); NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &dump, iface->netdev) { if (!queue_ids_include(queues, queue_id)) { netdev_delete_queue(iface->netdev, queue_id); } } smap_destroy(&details); /* Configure queues for 'iface'. */ queue_zero = false; for (i = 0; i < qos->n_queues; i++) { const struct ovsrec_queue *queue = qos->value_queues[i]; unsigned int queue_id = qos->key_queues[i]; if (queue_id == 0) { queue_zero = true; } if (queue->n_dscp == 1) { struct ofproto_port_queue *port_queue; port_queue = ofpbuf_put_uninit(&queues_buf, sizeof *port_queue); port_queue->queue = queue_id; port_queue->dscp = queue->dscp[0]; } netdev_set_queue(iface->netdev, queue_id, &queue->other_config); } if (!queue_zero) { struct smap details; smap_init(&details); netdev_set_queue(iface->netdev, 0, &details); smap_destroy(&details); } } if (iface->ofp_port != OFPP_NONE) { const struct ofproto_port_queue *port_queues = queues_buf.data; size_t n_queues = queues_buf.size / sizeof *port_queues; ofproto_port_set_queues(iface->port->bridge->ofproto, iface->ofp_port, port_queues, n_queues); } netdev_set_policing(iface->netdev, MIN(UINT32_MAX, iface->cfg->ingress_policing_rate), MIN(UINT32_MAX, iface->cfg->ingress_policing_burst)); ofpbuf_uninit(&queues_buf); } static void iface_configure_cfm(struct iface *iface) { const struct ovsrec_interface *cfg = iface->cfg; const char *opstate_str; const char *cfm_ccm_vlan; struct cfm_settings s; struct smap netdev_args; if (!cfg->n_cfm_mpid) { ofproto_port_clear_cfm(iface->port->bridge->ofproto, iface->ofp_port); return; } s.check_tnl_key = false; smap_init(&netdev_args); if (!netdev_get_config(iface->netdev, &netdev_args)) { const char *key = smap_get(&netdev_args, "key"); const char *in_key = smap_get(&netdev_args, "in_key"); s.check_tnl_key = (key && !strcmp(key, "flow")) || (in_key && !strcmp(in_key, "flow")); } smap_destroy(&netdev_args); s.mpid = *cfg->cfm_mpid; s.interval = smap_get_int(&iface->cfg->other_config, "cfm_interval", 0); cfm_ccm_vlan = smap_get(&iface->cfg->other_config, "cfm_ccm_vlan"); s.ccm_pcp = smap_get_int(&iface->cfg->other_config, "cfm_ccm_pcp", 0); if (s.interval <= 0) { s.interval = 1000; } if (!cfm_ccm_vlan) { s.ccm_vlan = 0; } else if (!strcasecmp("random", cfm_ccm_vlan)) { s.ccm_vlan = CFM_RANDOM_VLAN; } else { s.ccm_vlan = atoi(cfm_ccm_vlan); if (s.ccm_vlan == CFM_RANDOM_VLAN) { s.ccm_vlan = 0; } } s.extended = smap_get_bool(&iface->cfg->other_config, "cfm_extended", false); s.demand = smap_get_bool(&iface->cfg->other_config, "cfm_demand", false); opstate_str = smap_get(&iface->cfg->other_config, "cfm_opstate"); s.opup = !opstate_str || !strcasecmp("up", opstate_str); ofproto_port_set_cfm(iface->port->bridge->ofproto, iface->ofp_port, &s); } /* Returns true if 'iface' is synthetic, that is, if we constructed it locally * instead of obtaining it from the database. */ static bool iface_is_synthetic(const struct iface *iface) { return ovsdb_idl_row_is_synthetic(&iface->cfg->header_); } static ofp_port_t iface_validate_ofport__(size_t n, int64_t *ofport) { return (n && *ofport >= 1 && *ofport < ofp_to_u16(OFPP_MAX) ? u16_to_ofp(*ofport) : OFPP_NONE); } static ofp_port_t iface_get_requested_ofp_port(const struct ovsrec_interface *cfg) { return iface_validate_ofport__(cfg->n_ofport_request, cfg->ofport_request); } static ofp_port_t iface_pick_ofport(const struct ovsrec_interface *cfg) { ofp_port_t requested_ofport = iface_get_requested_ofp_port(cfg); return (requested_ofport != OFPP_NONE ? requested_ofport : iface_validate_ofport__(cfg->n_ofport, cfg->ofport)); } /* Port mirroring. */ static struct mirror * mirror_find_by_uuid(struct bridge *br, const struct uuid *uuid) { struct mirror *m; HMAP_FOR_EACH_IN_BUCKET (m, hmap_node, uuid_hash(uuid), &br->mirrors) { if (uuid_equals(uuid, &m->uuid)) { return m; } } return NULL; } static void bridge_configure_mirrors(struct bridge *br) { const struct ovsdb_datum *mc; unsigned long *flood_vlans; struct mirror *m, *next; size_t i; /* Get rid of deleted mirrors. */ mc = ovsrec_bridge_get_mirrors(br->cfg, OVSDB_TYPE_UUID); HMAP_FOR_EACH_SAFE (m, next, hmap_node, &br->mirrors) { union ovsdb_atom atom; atom.uuid = m->uuid; if (ovsdb_datum_find_key(mc, &atom, OVSDB_TYPE_UUID) == UINT_MAX) { mirror_destroy(m); } } /* Add new mirrors and reconfigure existing ones. */ for (i = 0; i < br->cfg->n_mirrors; i++) { const struct ovsrec_mirror *cfg = br->cfg->mirrors[i]; struct mirror *m = mirror_find_by_uuid(br, &cfg->header_.uuid); if (!m) { m = mirror_create(br, cfg); } m->cfg = cfg; if (!mirror_configure(m)) { mirror_destroy(m); } } /* Update flooded vlans (for RSPAN). */ flood_vlans = vlan_bitmap_from_array(br->cfg->flood_vlans, br->cfg->n_flood_vlans); ofproto_set_flood_vlans(br->ofproto, flood_vlans); bitmap_free(flood_vlans); } static struct mirror * mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg) { struct mirror *m; m = xzalloc(sizeof *m); m->uuid = cfg->header_.uuid; hmap_insert(&br->mirrors, &m->hmap_node, uuid_hash(&m->uuid)); m->bridge = br; m->name = xstrdup(cfg->name); return m; } static void mirror_destroy(struct mirror *m) { if (m) { struct bridge *br = m->bridge; if (br->ofproto) { ofproto_mirror_unregister(br->ofproto, m); } hmap_remove(&br->mirrors, &m->hmap_node); free(m->name); free(m); } } static void mirror_collect_ports(struct mirror *m, struct ovsrec_port **in_ports, int n_in_ports, void ***out_portsp, size_t *n_out_portsp) { void **out_ports = xmalloc(n_in_ports * sizeof *out_ports); size_t n_out_ports = 0; size_t i; for (i = 0; i < n_in_ports; i++) { const char *name = in_ports[i]->name; struct port *port = port_lookup(m->bridge, name); if (port) { out_ports[n_out_ports++] = port; } else { VLOG_WARN("bridge %s: mirror %s cannot match on nonexistent " "port %s", m->bridge->name, m->name, name); } } *out_portsp = out_ports; *n_out_portsp = n_out_ports; } static bool mirror_configure(struct mirror *m) { const struct ovsrec_mirror *cfg = m->cfg; struct ofproto_mirror_settings s; /* Set name. */ if (strcmp(cfg->name, m->name)) { free(m->name); m->name = xstrdup(cfg->name); } s.name = m->name; /* Get output port or VLAN. */ if (cfg->output_port) { s.out_bundle = port_lookup(m->bridge, cfg->output_port->name); if (!s.out_bundle) { VLOG_ERR("bridge %s: mirror %s outputs to port not on bridge", m->bridge->name, m->name); return false; } s.out_vlan = UINT16_MAX; if (cfg->output_vlan) { VLOG_ERR("bridge %s: mirror %s specifies both output port and " "output vlan; ignoring output vlan", m->bridge->name, m->name); } } else if (cfg->output_vlan) { /* The database should prevent invalid VLAN values. */ s.out_bundle = NULL; s.out_vlan = *cfg->output_vlan; } else { VLOG_ERR("bridge %s: mirror %s does not specify output; ignoring", m->bridge->name, m->name); return false; } /* Get port selection. */ if (cfg->select_all) { size_t n_ports = hmap_count(&m->bridge->ports); void **ports = xmalloc(n_ports * sizeof *ports); struct port *port; size_t i; i = 0; HMAP_FOR_EACH (port, hmap_node, &m->bridge->ports) { ports[i++] = port; } s.srcs = ports; s.n_srcs = n_ports; s.dsts = ports; s.n_dsts = n_ports; } else { /* Get ports, dropping ports that don't exist. * The IDL ensures that there are no duplicates. */ mirror_collect_ports(m, cfg->select_src_port, cfg->n_select_src_port, &s.srcs, &s.n_srcs); mirror_collect_ports(m, cfg->select_dst_port, cfg->n_select_dst_port, &s.dsts, &s.n_dsts); } /* Get VLAN selection. */ s.src_vlans = vlan_bitmap_from_array(cfg->select_vlan, cfg->n_select_vlan); /* Configure. */ ofproto_mirror_register(m->bridge->ofproto, m, &s); /* Clean up. */ if (s.srcs != s.dsts) { free(s.dsts); } free(s.srcs); free(s.src_vlans); return true; } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ static struct ovsrec_port **recs; static size_t n_recs, allocated_recs; /* Adds 'rec' to a list of recs that have to be destroyed when the VLAN * splinters are reconfigured. */ static void register_rec(struct ovsrec_port *rec) { if (n_recs >= allocated_recs) { recs = x2nrealloc(recs, &allocated_recs, sizeof *recs); } recs[n_recs++] = rec; } /* Frees all of the ports registered with register_reg(). */ static void free_registered_recs(void) { size_t i; for (i = 0; i < n_recs; i++) { struct ovsrec_port *port = recs[i]; size_t j; for (j = 0; j < port->n_interfaces; j++) { struct ovsrec_interface *iface = port->interfaces[j]; free(iface->name); free(iface); } smap_destroy(&port->other_config); free(port->interfaces); free(port->name); free(port->tag); free(port); } n_recs = 0; } /* Returns true if VLAN splinters are enabled on 'iface_cfg', false * otherwise. */ static bool vlan_splinters_is_enabled(const struct ovsrec_interface *iface_cfg) { return smap_get_bool(&iface_cfg->other_config, "enable-vlan-splinters", false); } /* Figures out the set of VLANs that are in use for the purpose of VLAN * splinters. * * If VLAN splinters are enabled on at least one interface and any VLANs are in * use, returns a 4096-bit bitmap with a 1-bit for each in-use VLAN (bits 0 and * 4095 will not be set). The caller is responsible for freeing the bitmap, * with free(). * * If VLANs splinters are not enabled on any interface or if no VLANs are in * use, returns NULL. * * Updates 'vlan_splinters_enabled_anywhere'. */ static unsigned long int * collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg) { unsigned long int *splinter_vlans; struct sset splinter_ifaces; const char *real_dev_name; struct shash *real_devs; struct shash_node *node; struct bridge *br; size_t i; /* Free space allocated for synthesized ports and interfaces, since we're * in the process of reconstructing all of them. */ free_registered_recs(); splinter_vlans = bitmap_allocate(4096); sset_init(&splinter_ifaces); vlan_splinters_enabled_anywhere = false; for (i = 0; i < ovs_cfg->n_bridges; i++) { struct ovsrec_bridge *br_cfg = ovs_cfg->bridges[i]; size_t j; for (j = 0; j < br_cfg->n_ports; j++) { struct ovsrec_port *port_cfg = br_cfg->ports[j]; int k; for (k = 0; k < port_cfg->n_interfaces; k++) { struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; if (vlan_splinters_is_enabled(iface_cfg)) { vlan_splinters_enabled_anywhere = true; sset_add(&splinter_ifaces, iface_cfg->name); vlan_bitmap_from_array__(port_cfg->trunks, port_cfg->n_trunks, splinter_vlans); } } if (port_cfg->tag && *port_cfg->tag > 0 && *port_cfg->tag < 4095) { bitmap_set1(splinter_vlans, *port_cfg->tag); } } } if (!vlan_splinters_enabled_anywhere) { free(splinter_vlans); sset_destroy(&splinter_ifaces); return NULL; } HMAP_FOR_EACH (br, node, &all_bridges) { if (br->ofproto) { ofproto_get_vlan_usage(br->ofproto, splinter_vlans); } } /* Don't allow VLANs 0 or 4095 to be splintered. VLAN 0 should appear on * the real device. VLAN 4095 is reserved and Linux doesn't allow a VLAN * device to be created for it. */ bitmap_set0(splinter_vlans, 0); bitmap_set0(splinter_vlans, 4095); /* Delete all VLAN devices that we don't need. */ vlandev_refresh(); real_devs = vlandev_get_real_devs(); SHASH_FOR_EACH (node, real_devs) { const struct vlan_real_dev *real_dev = node->data; const struct vlan_dev *vlan_dev; bool real_dev_has_splinters; real_dev_has_splinters = sset_contains(&splinter_ifaces, real_dev->name); HMAP_FOR_EACH (vlan_dev, hmap_node, &real_dev->vlan_devs) { if (!real_dev_has_splinters || !bitmap_is_set(splinter_vlans, vlan_dev->vid)) { struct netdev *netdev; if (!netdev_open(vlan_dev->name, "system", &netdev)) { if (!netdev_get_in4(netdev, NULL, NULL) || !netdev_get_in6(netdev, NULL)) { /* It has an IP address configured, so we don't own * it. Don't delete it. */ } else { vlandev_del(vlan_dev->name); } netdev_close(netdev); } } } } /* Add all VLAN devices that we need. */ SSET_FOR_EACH (real_dev_name, &splinter_ifaces) { int vid; BITMAP_FOR_EACH_1 (vid, 4096, splinter_vlans) { if (!vlandev_get_name(real_dev_name, vid)) { vlandev_add(real_dev_name, vid); } } } vlandev_refresh(); sset_destroy(&splinter_ifaces); if (bitmap_scan(splinter_vlans, 1, 0, 4096) >= 4096) { free(splinter_vlans); return NULL; } return splinter_vlans; } /* Pushes the configure of VLAN splinter port 'port' (e.g. eth0.9) down to * ofproto. */ static void configure_splinter_port(struct port *port) { struct ofproto *ofproto = port->bridge->ofproto; ofp_port_t realdev_ofp_port; const char *realdev_name; struct iface *vlandev, *realdev; ofproto_bundle_unregister(port->bridge->ofproto, port); vlandev = CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem); realdev_name = smap_get(&port->cfg->other_config, "realdev"); realdev = iface_lookup(port->bridge, realdev_name); realdev_ofp_port = realdev ? realdev->ofp_port : 0; ofproto_port_set_realdev(ofproto, vlandev->ofp_port, realdev_ofp_port, *port->cfg->tag); } static struct ovsrec_port * synthesize_splinter_port(const char *real_dev_name, const char *vlan_dev_name, int vid) { struct ovsrec_interface *iface; struct ovsrec_port *port; iface = xmalloc(sizeof *iface); ovsrec_interface_init(iface); iface->name = xstrdup(vlan_dev_name); iface->type = "system"; port = xmalloc(sizeof *port); ovsrec_port_init(port); port->interfaces = xmemdup(&iface, sizeof iface); port->n_interfaces = 1; port->name = xstrdup(vlan_dev_name); port->vlan_mode = "splinter"; port->tag = xmalloc(sizeof *port->tag); *port->tag = vid; smap_add(&port->other_config, "realdev", real_dev_name); register_rec(port); return port; } /* For each interface with 'br' that has VLAN splinters enabled, adds a * corresponding ovsrec_port to 'ports' for each splinter VLAN marked with a * 1-bit in the 'splinter_vlans' bitmap. */ static void add_vlan_splinter_ports(struct bridge *br, const unsigned long int *splinter_vlans, struct shash *ports) { size_t i; /* We iterate through 'br->cfg->ports' instead of 'ports' here because * we're modifying 'ports'. */ for (i = 0; i < br->cfg->n_ports; i++) { const char *name = br->cfg->ports[i]->name; struct ovsrec_port *port_cfg = shash_find_data(ports, name); size_t j; for (j = 0; j < port_cfg->n_interfaces; j++) { struct ovsrec_interface *iface_cfg = port_cfg->interfaces[j]; if (vlan_splinters_is_enabled(iface_cfg)) { const char *real_dev_name; uint16_t vid; real_dev_name = iface_cfg->name; BITMAP_FOR_EACH_1 (vid, 4096, splinter_vlans) { const char *vlan_dev_name; vlan_dev_name = vlandev_get_name(real_dev_name, vid); if (vlan_dev_name && !shash_find(ports, vlan_dev_name)) { shash_add(ports, vlan_dev_name, synthesize_splinter_port( real_dev_name, vlan_dev_name, vid)); } } } } } } static void mirror_refresh_stats(struct mirror *m) { struct ofproto *ofproto = m->bridge->ofproto; uint64_t tx_packets, tx_bytes; const char *keys[2]; int64_t values[2]; size_t stat_cnt = 0; if (ofproto_mirror_get_stats(ofproto, m, &tx_packets, &tx_bytes)) { ovsrec_mirror_set_statistics(m->cfg, NULL, NULL, 0); return; } if (tx_packets != UINT64_MAX) { keys[stat_cnt] = "tx_packets"; values[stat_cnt] = tx_packets; stat_cnt++; } if (tx_bytes != UINT64_MAX) { keys[stat_cnt] = "tx_bytes"; values[stat_cnt] = tx_bytes; stat_cnt++; } ovsrec_mirror_set_statistics(m->cfg, keys, values, stat_cnt); } /* * Add registered netdev and dpif types to ovsdb to allow external * applications to query the capabilities of the Open vSwitch instance * running on the node. */ static void discover_types(const struct ovsrec_open_vswitch *cfg) { struct sset types; /* Datapath types. */ sset_init(&types); dp_enumerate_types(&types); const char **datapath_types = sset_array(&types); ovsrec_open_vswitch_set_datapath_types(cfg, datapath_types, sset_count(&types)); free(datapath_types); sset_destroy(&types); /* Port types. */ sset_init(&types); netdev_enumerate_types(&types); const char **iface_types = sset_array(&types); ovsrec_open_vswitch_set_iface_types(cfg, iface_types, sset_count(&types)); free(iface_types); sset_destroy(&types); } openvswitch-2.5.9/vswitchd/PaxHeaders.82075/vswitch.ovsschema0000644000000000000000000000013213534540071021100 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801424.177848973 openvswitch-2.5.9/vswitchd/vswitch.ovsschema0000644000175000017500000005404513534540071022576 0ustar00jpettitjpettit00000000000000{"name": "Open_vSwitch", "version": "7.12.1", "cksum": "2211824403 22535", "tables": { "Open_vSwitch": { "columns": { "bridges": { "type": {"key": {"type": "uuid", "refTable": "Bridge"}, "min": 0, "max": "unlimited"}}, "manager_options": { "type": {"key": {"type": "uuid", "refTable": "Manager"}, "min": 0, "max": "unlimited"}}, "ssl": { "type": {"key": {"type": "uuid", "refTable": "SSL"}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "next_cfg": { "type": "integer"}, "cur_cfg": { "type": "integer"}, "statistics": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "ovs_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "db_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "system_type": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "system_version": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "datapath_types": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}, "iface_types": { "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}}, "isRoot": true, "maxRows": 1}, "Bridge": { "columns": { "name": { "type": "string", "mutable": false}, "datapath_type": { "type": "string"}, "datapath_version": { "type": "string"}, "datapath_id": { "type": {"key": "string", "min": 0, "max": 1}, "ephemeral": true}, "stp_enable": { "type": "boolean"}, "rstp_enable": { "type": "boolean"}, "mcast_snooping_enable": { "type": "boolean"}, "ports": { "type": {"key": {"type": "uuid", "refTable": "Port"}, "min": 0, "max": "unlimited"}}, "mirrors": { "type": {"key": {"type": "uuid", "refTable": "Mirror"}, "min": 0, "max": "unlimited"}}, "netflow": { "type": {"key": {"type": "uuid", "refTable": "NetFlow"}, "min": 0, "max": 1}}, "sflow": { "type": {"key": {"type": "uuid", "refTable": "sFlow"}, "min": 0, "max": 1}}, "ipfix": { "type": {"key": {"type": "uuid", "refTable": "IPFIX"}, "min": 0, "max": 1}}, "controller": { "type": {"key": {"type": "uuid", "refTable": "Controller"}, "min": 0, "max": "unlimited"}}, "protocols": { "type": {"key": {"type": "string", "enum": ["set", ["OpenFlow10", "OpenFlow11", "OpenFlow12", "OpenFlow13", "OpenFlow14", "OpenFlow15"]]}, "min": 0, "max": "unlimited"}}, "fail_mode": { "type": {"key": {"type": "string", "enum": ["set", ["standalone", "secure"]]}, "min": 0, "max": 1}}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "flood_vlans": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "flow_tables": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 254}, "value": {"type": "uuid", "refTable": "Flow_Table"}, "min": 0, "max": "unlimited"}}, "auto_attach": { "type": {"key": {"type": "uuid", "refTable": "AutoAttach"}, "min": 0, "max": 1}}}, "indexes": [["name"]]}, "Port": { "columns": { "name": { "type": "string", "mutable": false}, "interfaces": { "type": {"key": {"type": "uuid", "refTable": "Interface"}, "min": 1, "max": "unlimited"}}, "trunks": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "tag": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 1}}, "vlan_mode": { "type": {"key": {"type": "string", "enum": ["set", ["trunk", "access", "native-tagged", "native-untagged"]]}, "min": 0, "max": 1}}, "qos": { "type": {"key": {"type": "uuid", "refTable": "QoS"}, "min": 0, "max": 1}}, "mac": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "bond_mode": { "type": {"key": {"type": "string", "enum": ["set", ["balance-tcp", "balance-slb", "active-backup"]]}, "min": 0, "max": 1}}, "lacp": { "type": {"key": {"type": "string", "enum": ["set", ["active", "passive", "off"]]}, "min": 0, "max": 1}}, "bond_updelay": { "type": "integer"}, "bond_downdelay": { "type": "integer"}, "bond_active_slave": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "bond_fake_iface": { "type": "boolean"}, "fake_bridge": { "type": "boolean"}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "rstp_statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "indexes": [["name"]]}, "Interface": { "columns": { "name": { "type": "string", "mutable": false}, "type": { "type": "string"}, "options": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ingress_policing_rate": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "ingress_policing_burst": { "type": {"key": {"type": "integer", "minInteger": 0}}}, "mac_in_use": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}, "ephemeral": true}, "mac": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "ifindex": { "type": { "key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}, "ephemeral": true}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ofport": { "type": {"key": "integer", "min": 0, "max": 1}}, "ofport_request": { "type": { "key": {"type": "integer", "minInteger": 1, "maxInteger": 65279}, "min": 0, "max": 1}}, "bfd": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "bfd_status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "cfm_mpid": { "type": { "key": {"type": "integer"}, "min": 0, "max": 1}}, "cfm_remote_mpids": { "type": { "key": {"type": "integer"}, "min": 0, "max": "unlimited"}, "ephemeral": true}, "cfm_flap_count": { "type": { "key": {"type": "integer"}, "min": 0, "max": 1}}, "cfm_fault": { "type": { "key": { "type": "boolean"}, "min": 0, "max": 1}, "ephemeral": true}, "cfm_fault_status": { "type": { "key": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "cfm_remote_opstate": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "cfm_health": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 100}, "min": 0, "max": 1}, "ephemeral": true}, "lacp_current": { "type": {"key": {"type": "boolean"}, "min": 0, "max": 1}, "ephemeral": true}, "lldp": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}, "admin_state": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "link_state": { "type": {"key": {"type": "string", "enum": ["set", ["up", "down"]]}, "min": 0, "max": 1}, "ephemeral": true}, "link_resets": { "type": {"key": {"type": "integer"}, "min": 0, "max": 1}, "ephemeral": true}, "link_speed": { "type": {"key": "integer", "min": 0, "max": 1}, "ephemeral": true}, "duplex": { "type": {"key": {"type": "string", "enum": ["set", ["half", "full"]]}, "min": 0, "max": 1}, "ephemeral": true}, "mtu": { "type": {"key": "integer", "min": 0, "max": 1}, "ephemeral": true}, "error": { "type": {"key": "string", "min": 0, "max": 1}}}, "indexes": [["name"]]}, "Flow_Table": { "columns": { "name": { "type": {"key": "string", "min": 0, "max": 1}}, "flow_limit": { "type": {"key": {"type": "integer", "minInteger": 0}, "min": 0, "max": 1}}, "overflow_policy": { "type": {"key": {"type": "string", "enum": ["set", ["refuse", "evict"]]}, "min": 0, "max": 1}}, "groups": { "type": {"key": "string", "min": 0, "max": "unlimited"}}, "prefixes": { "type": {"key": "string", "min": 0, "max": 3}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "QoS": { "columns": { "type": { "type": "string"}, "queues": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "value": {"type": "uuid", "refTable": "Queue"}, "min": 0, "max": "unlimited"}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Queue": { "columns": { "dscp": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 63}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true}, "Mirror": { "columns": { "name": { "type": "string"}, "select_all": { "type": "boolean"}, "select_src_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "select_dst_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": "unlimited"}}, "select_vlan": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": 4096}}, "output_port": { "type": {"key": {"type": "uuid", "refTable": "Port", "refType": "weak"}, "min": 0, "max": 1}}, "output_vlan": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4095}, "min": 0, "max": 1}}, "statistics": { "type": {"key": "string", "value": "integer", "min": 0, "max": "unlimited"}, "ephemeral": true}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "NetFlow": { "columns": { "targets": { "type": {"key": {"type": "string"}, "min": 1, "max": "unlimited"}}, "engine_type": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 255}, "min": 0, "max": 1}}, "engine_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 255}, "min": 0, "max": 1}}, "add_id_to_interface": { "type": "boolean"}, "active_timeout": { "type": {"key": {"type": "integer", "minInteger": -1}}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "sFlow": { "columns": { "targets": { "type": {"key": "string", "min": 1, "max": "unlimited"}}, "sampling": { "type": {"key": "integer", "min": 0, "max": 1}}, "polling": { "type": {"key": "integer", "min": 0, "max": 1}}, "header": { "type": {"key": "integer", "min": 0, "max": 1}}, "agent": { "type": {"key": "string", "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "IPFIX": { "columns": { "targets": { "type": {"key": "string", "min": 0, "max": "unlimited"}}, "sampling": { "type": {"key": {"type": "integer", "minInteger": 1, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "obs_domain_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "obs_point_id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "cache_active_timeout": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4200}, "min": 0, "max": 1}}, "cache_max_flows": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}}, "Flow_Sample_Collector_Set": { "columns": { "id": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 4294967295}, "min": 1, "max": 1}}, "bridge": { "type": {"key": {"type": "uuid", "refTable": "Bridge"}, "min": 1, "max": 1}}, "ipfix": { "type": {"key": {"type": "uuid", "refTable": "IPFIX"}, "min": 0, "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "isRoot": true, "indexes": [["id", "bridge"]]}, "Controller": { "columns": { "target": { "type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "connection_mode": { "type": {"key": {"type": "string", "enum": ["set", ["in-band", "out-of-band"]]}, "min": 0, "max": 1}}, "local_ip": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "local_netmask": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "local_gateway": { "type": {"key": {"type": "string"}, "min": 0, "max": 1}}, "enable_async_messages": { "type": {"key": {"type": "boolean"}, "min": 0, "max": 1}}, "controller_rate_limit": { "type": {"key": {"type": "integer", "minInteger": 100}, "min": 0, "max": 1}}, "controller_burst_limit": { "type": {"key": {"type": "integer", "minInteger": 25}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "role": { "type": {"key": {"type": "string", "enum": ["set", ["other", "master", "slave"]]}, "min": 0, "max": 1}, "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}}, "Manager": { "columns": { "target": { "type": "string"}, "max_backoff": { "type": {"key": {"type": "integer", "minInteger": 1000}, "min": 0, "max": 1}}, "inactivity_probe": { "type": {"key": "integer", "min": 0, "max": 1}}, "connection_mode": { "type": {"key": {"type": "string", "enum": ["set", ["in-band", "out-of-band"]]}, "min": 0, "max": 1}}, "other_config": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "is_connected": { "type": "boolean", "ephemeral": true}, "status": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}, "ephemeral": true}}, "indexes": [["target"]]}, "SSL": { "columns": { "private_key": { "type": "string"}, "certificate": { "type": "string"}, "ca_cert": { "type": "string"}, "bootstrap_ca_cert": { "type": "boolean"}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, "maxRows": 1}, "AutoAttach": { "columns": { "system_name": { "type": "string"}, "system_description": { "type": "string"}, "mappings": { "type": {"key": {"type": "integer", "minInteger": 0, "maxInteger": 16777215}, "value": {"type": "integer", "minInteger": 0, "maxInteger": 4095}, "min": 0, "max": "unlimited"}}}}}} openvswitch-2.5.9/vswitchd/PaxHeaders.82075/bridge.h0000644000000000000000000000013213534540071017104 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801425.201856523 openvswitch-2.5.9/vswitchd/bridge.h0000644000175000017500000000156413534540071020600 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VSWITCHD_BRIDGE_H #define VSWITCHD_BRIDGE_H 1 struct simap; void bridge_init(const char *remote); void bridge_exit(void); void bridge_run(void); void bridge_wait(void); void bridge_get_memory_usage(struct simap *usage); #endif /* bridge.h */ openvswitch-2.5.9/vswitchd/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017636 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801424.625852276 openvswitch-2.5.9/vswitchd/automake.mk0000644000175000017500000000424513534540071021331 0ustar00jpettitjpettit00000000000000sbin_PROGRAMS += vswitchd/ovs-vswitchd man_MANS += vswitchd/ovs-vswitchd.8 DISTCLEANFILES += \ vswitchd/ovs-vswitchd.8 vswitchd_ovs_vswitchd_SOURCES = \ vswitchd/bridge.c \ vswitchd/bridge.h \ vswitchd/ovs-vswitchd.c \ vswitchd/system-stats.c \ vswitchd/system-stats.h \ vswitchd/xenserver.c \ vswitchd/xenserver.h vswitchd_ovs_vswitchd_LDADD = \ ofproto/libofproto.la \ lib/libsflow.la \ lib/libopenvswitch.la vswitchd_ovs_vswitchd_LDFLAGS = $(AM_LDFLAGS) $(DPDK_vswitchd_LDFLAGS) EXTRA_DIST += vswitchd/INTERNALS MAN_ROOTS += vswitchd/ovs-vswitchd.8.in # vswitch schema and IDL EXTRA_DIST += vswitchd/vswitch.ovsschema pkgdata_DATA += vswitchd/vswitch.ovsschema # vswitch E-R diagram # # If "python" or "dot" is not available, then we do not add graphical diagram # to the documentation. if HAVE_PYTHON if HAVE_DOT vswitchd/vswitch.gv: ovsdb/ovsdb-dot.in vswitchd/vswitch.ovsschema $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/vswitchd/vswitch.ovsschema > $@ vswitchd/vswitch.pic: vswitchd/vswitch.gv ovsdb/dot2pic $(AM_V_GEN)(dot -T plain < vswitchd/vswitch.gv | $(PERL) $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \ mv $@.tmp $@ VSWITCH_PIC = vswitchd/vswitch.pic VSWITCH_DOT_DIAGRAM_ARG = --er-diagram=$(VSWITCH_PIC) DISTCLEANFILES += vswitchd/vswitch.gv vswitchd/vswitch.pic endif endif # vswitch schema documentation EXTRA_DIST += vswitchd/vswitch.xml DISTCLEANFILES += vswitchd/ovs-vswitchd.conf.db.5 man_MANS += vswitchd/ovs-vswitchd.conf.db.5 vswitchd/ovs-vswitchd.conf.db.5: \ ovsdb/ovsdb-doc vswitchd/vswitch.xml vswitchd/vswitch.ovsschema \ $(VSWITCH_PIC) $(AM_V_GEN)$(OVSDB_DOC) \ $(VSWITCH_DOT_DIAGRAM_ARG) \ --version=$(VERSION) \ $(srcdir)/vswitchd/vswitch.ovsschema \ $(srcdir)/vswitchd/vswitch.xml > $@.tmp && \ mv $@.tmp $@ # Version checking for vswitch.ovsschema. ALL_LOCAL += vswitchd/vswitch.ovsschema.stamp vswitchd/vswitch.ovsschema.stamp: vswitchd/vswitch.ovsschema $(srcdir)/build-aux/cksum-schema-check $? $@ CLEANFILES += vswitchd/vswitch.ovsschema.stamp # Clean up generated files from older OVS versions. (This is important so that # #include "vswitch-idl.h" doesn't get the wrong copy.) CLEANFILES += vswitchd/vswitch-idl.c vswitchd/vswitch-idl.h openvswitch-2.5.9/vswitchd/PaxHeaders.82075/xenserver.h0000644000000000000000000000013213534540071017671 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801425.205856551 openvswitch-2.5.9/vswitchd/xenserver.h0000644000175000017500000000133113534540071021355 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VSWITCHD_XENSERVER_H #define VSWITCHD_XENSERVER_H 1 const char *xenserver_get_host_uuid(void); #endif /* xenserver.h */ openvswitch-2.5.9/vswitchd/PaxHeaders.82075/system-stats.h0000644000000000000000000000013213534540071020330 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801425.205856551 openvswitch-2.5.9/vswitchd/system-stats.h0000644000175000017500000000150213534540071022014 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef VSWITCHD_SYSTEM_STATS #define VSWITCHD_SYSTEM_STATS 1 #include void system_stats_enable(bool enable); struct smap *system_stats_run(void); void system_stats_wait(void); #endif /* vswitchd/system-stats.h */ openvswitch-2.5.9/vswitchd/PaxHeaders.82075/xenserver.c0000644000000000000000000000013213534540071017664 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.141686605 30 ctime=1567801425.205856551 openvswitch-2.5.9/vswitchd/xenserver.c0000644000175000017500000000443613534540071021361 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "xenserver.h" #include #include #include #include #include #include #include "dynamic-string.h" #include "process.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(xenserver); /* If running on a XenServer, the XenServer host UUID as a 36-character string, * otherwise null. */ static char *host_uuid; static void read_host_uuid(void) { static const char filename[] = "/etc/xensource-inventory"; char line[128]; FILE *file; file = fopen(filename, "r"); if (!file) { if (errno == ENOENT) { VLOG_DBG("not running on a XenServer"); } else { VLOG_INFO("%s: open: %s", filename, ovs_strerror(errno)); } return; } while (fgets(line, sizeof line, file)) { static const char leader[] = "INSTALLATION_UUID='"; const int leader_len = strlen(leader); const int uuid_len = 36; static const char trailer[] = "'\n"; const int trailer_len = strlen(trailer); if (strlen(line) == leader_len + uuid_len + trailer_len && !memcmp(line, leader, leader_len) && !memcmp(line + leader_len + uuid_len, trailer, trailer_len)) { host_uuid = xmemdup0(line + leader_len, uuid_len); VLOG_INFO("running on XenServer, host-uuid %s", host_uuid); fclose(file); return; } } fclose(file); VLOG_ERR("%s: INSTALLATION_UUID not found", filename); } const char * xenserver_get_host_uuid(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, read_host_uuid); return host_uuid; } openvswitch-2.5.9/vswitchd/PaxHeaders.82075/ovs-vswitchd.8.in0000644000000000000000000000013213534540071020635 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801423.777846025 openvswitch-2.5.9/vswitchd/ovs-vswitchd.8.in0000644000175000017500000003252113534540071022326 0ustar00jpettitjpettit00000000000000.\" -*- nroff -*- .de IQ . br . ns . IP "\\$1" .. .TH ovs\-vswitchd 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual" .\" This program's name: .ds PN ovs\-vswitchd . .SH NAME ovs\-vswitchd \- Open vSwitch daemon . .SH SYNOPSIS \fBovs\-vswitchd \fR[\fIdatabase\fR] . .SH DESCRIPTION A daemon that manages and controls any number of Open vSwitch switches on the local machine. .PP The \fIdatabase\fR argument specifies how \fBovs\-vswitchd\fR connects to \fBovsdb\-server\fR. The default is \fBunix:@RUNDIR@/db.sock\fR. The following forms are accepted: .so ovsdb/remote-active.man .so ovsdb/remote-passive.man .PP \fBovs\-vswitchd\fR retrieves its configuration from \fIdatabase\fR at startup. It sets up Open vSwitch datapaths and then operates switching across each bridge described in its configuration files. As the database changes, \fBovs\-vswitchd\fR automatically updates its configuration to match. .PP \fBovs\-vswitchd\fR switches may be configured with any of the following features: . .IP \(bu L2 switching with MAC learning. . .IP \(bu NIC bonding with automatic fail-over and source MAC-based TX load balancing ("SLB"). . .IP \(bu 802.1Q VLAN support. . .IP \(bu Port mirroring, with optional VLAN tagging. . .IP \(bu NetFlow v5 flow logging. . .IP \(bu sFlow(R) monitoring. . .IP \(bu Connectivity to an external OpenFlow controller, such as NOX. . .PP Only a single instance of \fBovs\-vswitchd\fR is intended to run at a time. A single \fBovs\-vswitchd\fR can manage any number of switch instances, up to the maximum number of supported Open vSwitch datapaths. .PP \fBovs\-vswitchd\fR does all the necessary management of Open vSwitch datapaths itself. Thus, external tools, such \fBovs\-dpctl\fR(8), are not needed for managing datapaths in conjunction with \fBovs\-vswitchd\fR, and their use to modify datapaths when \fBovs\-vswitchd\fR is running can interfere with its operation. (\fBovs\-dpctl\fR may still be useful for diagnostics.) .PP An Open vSwitch datapath kernel module must be loaded for \fBovs\-vswitchd\fR to be useful. Please refer to the \fBINSTALL.Linux\fR file included in the Open vSwitch distribution for instructions on how to build and load the Open vSwitch kernel module. .PP .SH OPTIONS .IP "\fB\-\-mlockall\fR" Causes \fBovs\-vswitchd\fR to call the \fBmlockall()\fR function, to attempt to lock all of its process memory into physical RAM, preventing the kernel from paging any of its memory to disk. This helps to avoid networking interruptions due to system memory pressure. .IP Some systems do not support \fBmlockall()\fR at all, and other systems only allow privileged users, such as the superuser, to use it. \fBovs\-vswitchd\fR emits a log message if \fBmlockall()\fR is unavailable or unsuccessful. . .SS "DPDK Options" .IP "\fB\-\-dpdk\fR" Initialize \fBovs\-vswitchd\fR DPDK datapath. Refer to INSTALL.DPDK for details. .SS "Daemon Options" .ds DD \ \fBovs\-vswitchd\fR detaches only after it has connected to the \ database, retrieved the initial configuration, and set up that \ configuration. .so lib/daemon.man .SS "Service Options" .so lib/service.man .SS "Public Key Infrastructure Options" .so lib/ssl.man .so lib/ssl-bootstrap.man .SS "Logging Options" .so lib/vlog.man .SS "Other Options" .so lib/unixctl.man .so lib/common.man . .SH "RUNTIME MANAGEMENT COMMANDS" \fBovs\-appctl\fR(8) can send commands to a running \fBovs\-vswitchd\fR process. The currently supported commands are described below. The command descriptions assume an understanding of how to configure Open vSwitch. .SS "GENERAL COMMANDS" .IP "\fBexit\fR" Causes \fBovs\-vswitchd\fR to gracefully terminate. .IP "\fBqos/show\fR \fIinterface\fR" Queries the kernel for Quality of Service configuration and statistics associated with the given \fIinterface\fR. .IP "\fBbfd/show\fR [\fIinterface\fR]" Displays detailed information about Bidirectional Forwarding Detection configured on \fIinterface\fR. If \fIinterface\fR is not specified, then displays detailed information about all interfaces with BFD enabled. .IP "\fBbfd/set-forwarding\fR [\fIinterface\fR] \fIstatus\fR" Force the fault status of the BFD module on \fIinterface\fR (or all interfaces if none is given) to be \fIstatus\fR. \fIstatus\fR can be "true", "false", or "normal" which reverts to the standard behavior. .IP "\fBcfm/show\fR [\fIinterface\fR]" Displays detailed information about Connectivity Fault Management configured on \fIinterface\fR. If \fIinterface\fR is not specified, then displays detailed information about all interfaces with CFM enabled. .IP "\fBcfm/set-fault\fR [\fIinterface\fR] \fIstatus\fR" Force the fault status of the CFM module on \fIinterface\fR (or all interfaces if none is given) to be \fIstatus\fR. \fIstatus\fR can be "true", "false", or "normal" which reverts to the standard behavior. .IP "\fBstp/tcn\fR [\fIbridge\fR]" Forces a topology change event on \fIbridge\fR if it's running STP. This may cause it to send Topology Change Notifications to its peers and flush its MAC table.. If no \fIbridge\fR is given, forces a topology change event on all bridges. .SS "BRIDGE COMMANDS" These commands manage bridges. .IP "\fBfdb/flush\fR [\fIbridge\fR]" Flushes \fIbridge\fR MAC address learning table, or all learning tables if no \fIbridge\fR is given. .IP "\fBfdb/show\fR \fIbridge\fR" Lists each MAC address/VLAN pair learned by the specified \fIbridge\fR, along with the port on which it was learned and the age of the entry, in seconds. .IP "\fBmdb/flush\fR [\fIbridge\fR]" Flushes \fIbridge\fR multicast snooping table, or all snooping tables if no \fIbridge\fR is given. .IP "\fBmdb/show\fR \fIbridge\fR" Lists each multicast group/VLAN pair learned by the specified \fIbridge\fR, along with the port on which it was learned and the age of the entry, in seconds. .IP "\fBbridge/reconnect\fR [\fIbridge\fR]" Makes \fIbridge\fR drop all of its OpenFlow controller connections and reconnect. If \fIbridge\fR is not specified, then all bridges drop their controller connections and reconnect. .IP This command might be useful for debugging OpenFlow controller issues. . .IP "\fBbridge/dump\-flows\fR \fIbridge\fR" Lists all flows in \fIbridge\fR, including those normally hidden to commands such as \fBovs\-ofctl dump\-flows\fR. Flows set up by mechanisms such as in-band control and fail-open are hidden from the controller since it is not allowed to modify or override them. .SS "BOND COMMANDS" These commands manage bonded ports on an Open vSwitch's bridges. To understand some of these commands, it is important to understand a detail of the bonding implementation called ``source load balancing'' (SLB). Instead of directly assigning Ethernet source addresses to slaves, the bonding implementation computes a function that maps an 48-bit Ethernet source addresses into an 8-bit value (a ``MAC hash'' value). All of the Ethernet addresses that map to a single 8-bit value are then assigned to a single slave. .IP "\fBbond/list\fR" Lists all of the bonds, and their slaves, on each bridge. . .IP "\fBbond/show\fR [\fIport\fR]" Lists all of the bond-specific information (updelay, downdelay, time until the next rebalance) about the given bonded \fIport\fR, or all bonded ports if no \fIport\fR is given. Also lists information about each slave: whether it is enabled or disabled, the time to completion of an updelay or downdelay if one is in progress, whether it is the active slave, the hashes assigned to the slave. Any LACP information related to this bond may be found using the \fBlacp/show\fR command. . .IP "\fBbond/migrate\fR \fIport\fR \fIhash\fR \fIslave\fR" Only valid for SLB bonds. Assigns a given MAC hash to a new slave. \fIport\fR specifies the bond port, \fIhash\fR the MAC hash to be migrated (as a decimal number between 0 and 255), and \fIslave\fR the new slave to be assigned. .IP The reassignment is not permanent: rebalancing or fail-over will cause the MAC hash to be shifted to a new slave in the usual manner. .IP A MAC hash cannot be migrated to a disabled slave. .IP "\fBbond/set\-active\-slave\fR \fIport\fR \fIslave\fR" Sets \fIslave\fR as the active slave on \fIport\fR. \fIslave\fR must currently be enabled. .IP The setting is not permanent: a new active slave will be selected if \fIslave\fR becomes disabled. .IP "\fBbond/enable\-slave\fR \fIport\fR \fIslave\fR" .IQ "\fBbond/disable\-slave\fR \fIport\fR \fIslave\fR" Enables (or disables) \fIslave\fR on the given bond \fIport\fR, skipping any updelay (or downdelay). .IP This setting is not permanent: it persists only until the carrier status of \fIslave\fR changes. .IP "\fBbond/hash\fR \fImac\fR [\fIvlan\fR] [\fIbasis\fR]" Returns the hash value which would be used for \fImac\fR with \fIvlan\fR and \fIbasis\fR if specified. . .IP "\fBlacp/show\fR [\fIport\fR]" Lists all of the LACP related information about the given \fIport\fR: active or passive, aggregation key, system id, and system priority. Also lists information about each slave: whether it is enabled or disabled, whether it is attached or detached, port id and priority, actor information, and partner information. If \fIport\fR is not specified, then displays detailed information about all interfaces with CFM enabled. .SS "DPCTL DATAPATH DEBUGGING COMMANDS" The primary way to configure \fBovs\-vswitchd\fR is through the Open vSwitch database, e.g. using \fBovs\-vsctl\fR(8). These commands provide a debugging interface for managing datapaths. They implement the same features (and syntax) as \fBovs\-dpctl\fR(8). Unlike \fBovs\-dpctl\fR(8), these commands work with datapaths that are integrated into \fBovs\-vswitchd\fR (e.g. the \fBnetdev\fR datapath type). .PP . .ds DX \fBdpctl/\fR .de DO \\$2 \\$1 \\$3 .. .so lib/dpctl.man . .SS "DPIF-NETDEV COMMANDS" These commands are used to expose internal information (mostly statistics) about the ``dpif-netdev'' userspace datapath. If there is only one datapath (as is often the case, unless \fBdpctl/\fR commands are used), the \fIdp\fR argument can be omitted. .IP "\fBdpif-netdev/pmd-stats-show\fR [\fIdp\fR]" Shows performance statistics for each pmd thread of the datapath \fIdp\fR. The special thread ``main'' sums up the statistics of every non pmd thread. The sum of ``emc hits'', ``masked hits'' and ``miss'' is the number of packets received by the datapath. Cycles are counted using the TSC or similar facilities (when available on the platform). To reset these counters use \fBdpif-netdev/pmd-stats-clear\fR. The duration of one cycle depends on the measuring infrastructure. .IP "\fBdpif-netdev/pmd-stats-clear\fR [\fIdp\fR]" Resets to zero the per pmd thread performance numbers shown by the \fBdpif-netdev/pmd-stats-show\fR command. It will NOT reset datapath or bridge statistics, only the values shown by the above command. .IP "\fBdpif-netdev/pmd-rxq-show\fR [\fIdp\fR]" For each pmd thread of the datapath \fIdp\fR shows list of queue-ids with port names, which this thread polls. . .so ofproto/ofproto-dpif-unixctl.man .so ofproto/ofproto-unixctl.man .so lib/vlog-unixctl.man .so lib/memory-unixctl.man .so lib/coverage-unixctl.man .so ofproto/ofproto-tnl-unixctl.man . .SH "OPENFLOW IMPLEMENTATION" . .PP This section documents aspects of OpenFlow for which the OpenFlow specification requires documentation. . .SS "Packet buffering." The OpenFlow specification, version 1.2, says: . .IP Switches that implement buffering are expected to expose, through documentation, both the amount of available buffering, and the length of time before buffers may be reused. . .PP Open vSwitch maintains a separate set of 256 packet buffers for each OpenFlow connection. Any given packet buffer is preserved until it is referenced by an \fBOFPT_FLOW_MOD\fR or \fBOFPT_PACKET_OUT\fR request or for 5 seconds, whichever comes first. . .SH "LIMITS" . .PP We believe these limits to be accurate as of this writing. These limits assume the use of the Linux kernel datapath. . .IP \(bu \fBovs\-vswitchd\fR started through \fBovs\-ctl\fR(8) provides a limit of 65535 file descriptors. The limits on the number of bridges and ports is decided by the availability of file descriptors. With the Linux kernel datapath, creation of a single bridge consumes three file descriptors and adding a port consumes "n-handler-threads" file descriptors per bridge port. Performance will degrade beyond 1,024 ports per bridge due to fixed hash table sizing. Other platforms may have different limitations. . .IP \(bu 2,048 MAC learning entries per bridge, by default. (This is configurable via \fBother\-config:mac\-table\-size\fR in the \fBBridge\fR table. See \fBovs\-vswitchd.conf.db\fR(5) for details.) . .IP \(bu Kernel flows are limited only by memory available to the kernel. Performance will degrade beyond 1,048,576 kernel flows per bridge with a 32-bit kernel, beyond 262,144 with a 64-bit kernel. (\fBovs\-vswitchd\fR should never install anywhere near that many flows.) . .IP \(bu OpenFlow flows are limited only by available memory. Performance is linear in the number of unique wildcard patterns. That is, an OpenFlow table that contains many flows that all match on the same fields in the same way has a constant-time lookup, but a table that contains many flows that match on different fields requires lookup time linear in the number of flows. . .IP \(bu 255 ports per bridge participating in 802.1D Spanning Tree Protocol. . .IP \(bu 32 mirrors per bridge. . .IP \(bu 15 bytes for the name of a port. (This is a Linux kernel limitation.) . .SH "SEE ALSO" .BR ovs\-appctl (8), .BR ovsdb\-server (1), \fBINSTALL.Linux\fR in the Open vSwitch distribution. openvswitch-2.5.9/vswitchd/PaxHeaders.82075/INTERNALS0000644000000000000000000000013213534540071016761 xustar0030 mtime=1567801401.957685253 30 atime=1567801402.137686576 30 ctime=1567801424.177848973 openvswitch-2.5.9/vswitchd/INTERNALS0000644000175000017500000002677613534540071020471 0ustar00jpettitjpettit00000000000000 ======================== ovs-vswitchd Internals ======================== This document describes some of the internals of the ovs-vswitchd process. It is not complete. It tends to be updated on demand, so if you have questions about the vswitchd implementation, ask them and perhaps we'll add some appropriate documentation here. Most of the ovs-vswitchd implementation is in vswitchd/bridge.c, so code references below should be assumed to refer to that file except as otherwise specified. Bonding ======= Bonding allows two or more interfaces (the "slaves") to share network traffic. From a high-level point of view, bonded interfaces act like a single port, but they have the bandwidth of multiple network devices, e.g. two 1 GB physical interfaces act like a single 2 GB interface. Bonds also increase robustness: the bonded port does not go down as long as at least one of its slaves is up. In vswitchd, a bond always has at least two slaves (and may have more). If a configuration error, etc. would cause a bond to have only one slave, the port becomes an ordinary port, not a bonded port, and none of the special features of bonded ports described in this section apply. There are many forms of bonding of which ovs-vswitchd implements only a few. The most complex bond ovs-vswitchd implements is called "source load balancing" or SLB bonding. SLB bonding divides traffic among the slaves based on the Ethernet source address. This is useful only if the traffic over the bond has multiple Ethernet source addresses, for example if network traffic from multiple VMs are multiplexed over the bond. Enabling and Disabling Slaves ----------------------------- When a bond is created, a slave is initially enabled or disabled based on whether carrier is detected on the NIC (see iface_create()). After that, a slave is disabled if its carrier goes down for a period of time longer than the downdelay, and it is enabled if carrier comes up for longer than the updelay (see bond_link_status_update()). There is one exception where the updelay is skipped: if no slaves at all are currently enabled, then the first slave on which carrier comes up is enabled immediately. The updelay should be set to a time longer than the STP forwarding delay of the physical switch to which the bond port is connected (if STP is enabled on that switch). Otherwise, the slave will be enabled, and load may be shifted to it, before the physical switch starts forwarding packets on that port, which can cause some data to be "blackholed" for a time. The exception for a single enabled slave does not cause any problem in this regard because when no slaves are enabled all output packets are blackholed anyway. When a slave becomes disabled, the vswitch immediately chooses a new output port for traffic that was destined for that slave (see bond_enable_slave()). It also sends a "gratuitous learning packet", specifically a RARP, on the bond port (on the newly chosen slave) for each MAC address that the vswitch has learned on a port other than the bond (see bond_send_learning_packets()), to teach the physical switch that the new slave should be used in place of the one that is now disabled. (This behavior probably makes sense only for a vswitch that has only one port (the bond) connected to a physical switch; vswitchd should probably provide a way to disable or configure it in other scenarios.) Bond Packet Input ----------------- Bonding accepts unicast packets on any bond slave. This can occasionally cause packet duplication for the first few packets sent to a given MAC, if the physical switch attached to the bond is flooding packets to that MAC because it has not yet learned the correct slave for that MAC. Bonding only accepts multicast (and broadcast) packets on a single bond slave (the "active slave") at any given time. Multicast packets received on other slaves are dropped. Otherwise, every multicast packet would be duplicated, once for every bond slave, because the physical switch attached to the bond will flood those packets. Bonding also drops received packets when the vswitch has learned that the packet's MAC is on a port other than the bond port itself. This is because it is likely that the vswitch itself sent the packet out the bond port on a different slave and is now receiving the packet back. This occurs when the packet is multicast or the physical switch has not yet learned the MAC and is flooding it. However, the vswitch makes an exception to this rule for broadcast ARP replies, which indicate that the MAC has moved to another switch, probably due to VM migration. (ARP replies are normally unicast, so this exception does not match normal ARP replies. It will match the learning packets sent on bond fail-over.) The active slave is simply the first slave to be enabled after the bond is created (see bond_choose_active_iface()). If the active slave is disabled, then a new active slave is chosen among the slaves that remain active. Currently due to the way that configuration works, this tends to be the remaining slave whose interface name is first alphabetically, but this is by no means guaranteed. Bond Packet Output ------------------ When a packet is sent out a bond port, the bond slave actually used is selected based on the packet's source MAC and VLAN tag (see choose_output_iface()). In particular, the source MAC and VLAN tag are hashed into one of 256 values, and that value is looked up in a hash table (the "bond hash") kept in the "bond_hash" member of struct port. The hash table entry identifies a bond slave. If no bond slave has yet been chosen for that hash table entry, vswitchd chooses one arbitrarily. Every 10 seconds, vswitchd rebalances the bond slaves (see bond_rebalance_port()). To rebalance, vswitchd examines the statistics for the number of bytes transmitted by each slave over approximately the past minute, with data sent more recently weighted more heavily than data sent less recently. It considers each of the slaves in order from most-loaded to least-loaded. If highly loaded slave H is significantly more heavily loaded than the least-loaded slave L, and slave H carries at least two hashes, then vswitchd shifts one of H's hashes to L. However, vswitchd will only shift a hash from H to L if it will decrease the ratio of the load between H and L by at least 0.1. Currently, "significantly more loaded" means that H must carry at least 1 Mbps more traffic, and that traffic must be at least 3% greater than L's. Bond Balance Modes ------------------ Each bond balancing mode has different considerations, described below. LACP Bonding ------------ LACP bonding requires the remote switch to implement LACP, but it is otherwise very simple in that, after LACP negotiation is complete, there is no need for special handling of received packets. Several of the physical switches that support LACP block all traffic for ports that are configured to use LACP, until LACP is negotiated with the host. When configuring a LACP bond on a OVS host (eg: XenServer), this means that there will be an interruption of the network connectivity between the time the ports on the physical switch and the bond on the OVS host are configured. The interruption may be relatively long, if different people are responsible for managing the switches and the OVS host. Such network connectivity failure can be avoided if LACP can be configured on the OVS host before configuring the physical switch, and having the OVS host fall back to a bond mode (active-backup) till the physical switch LACP configuration is complete. An option "lacp-fallback-ab" exists to provide such behavior on openvswitch. Active Backup Bonding --------------------- Active Backup bonds send all traffic out one "active" slave until that slave becomes unavailable. Since they are significantly less complicated than SLB bonds, they are preferred when LACP is not an option. Additionally, they are the only bond mode which supports attaching each slave to a different upstream switch. SLB Bonding ----------- SLB bonding allows a limited form of load balancing without the remote switch's knowledge or cooperation. The basics of SLB are simple. SLB assigns each source MAC+VLAN pair to a link and transmits all packets from that MAC+VLAN through that link. Learning in the remote switch causes it to send packets to that MAC+VLAN through the same link. SLB bonding has the following complications: 0. When the remote switch has not learned the MAC for the destination of a unicast packet and hence floods the packet to all of the links on the SLB bond, Open vSwitch will forward duplicate packets, one per link, to each other switch port. Open vSwitch does not solve this problem. 1. When the remote switch receives a multicast or broadcast packet from a port not on the SLB bond, it will forward it to all of the links in the SLB bond. This would cause packet duplication if not handled specially. Open vSwitch avoids packet duplication by accepting multicast and broadcast packets on only the active slave, and dropping multicast and broadcast packets on all other slaves. 2. When Open vSwitch forwards a multicast or broadcast packet to a link in the SLB bond other than the active slave, the remote switch will forward it to all of the other links in the SLB bond, including the active slave. Without special handling, this would mean that Open vSwitch would forward a second copy of the packet to each switch port (other than the bond), including the port that originated the packet. Open vSwitch deals with this case by dropping packets received on any SLB bonded link that have a source MAC+VLAN that has been learned on any other port. (This means that SLB as implemented in Open vSwitch relies critically on MAC learning. Notably, SLB is incompatible with the "flood_vlans" feature.) 3. Suppose that a MAC+VLAN moves to an SLB bond from another port (e.g. when a VM is migrated from this hypervisor to a different one). Without additional special handling, Open vSwitch will not notice until the MAC learning entry expires, up to 60 seconds later as a consequence of rule #2. Open vSwitch avoids a 60-second delay by listening for gratuitous ARPs, which VMs commonly emit upon migration. As an exception to rule #2, a gratuitous ARP received on an SLB bond is not dropped and updates the MAC learning table in the usual way. (If a move does not trigger a gratuitous ARP, or if the gratuitous ARP is lost in the network, then a 60-second delay still occurs.) 4. Suppose that a MAC+VLAN moves from an SLB bond to another port (e.g. when a VM is migrated from a different hypervisor to this one), that the MAC+VLAN emits a gratuitous ARP, and that Open vSwitch forwards that gratuitous ARP to a link in the SLB bond other than the active slave. The remote switch will forward the gratuitous ARP to all of the other links in the SLB bond, including the active slave. Without additional special handling, this would mean that Open vSwitch would learn that the MAC+VLAN was located on the SLB bond, as a consequence of rule #3. Open vSwitch avoids this problem by "locking" the MAC learning table entry for a MAC+VLAN from which a gratuitous ARP was received from a non-SLB bond port. For 5 seconds, a locked MAC learning table entry will not be updated based on a gratuitous ARP received on a SLB bond. openvswitch-2.5.9/vswitchd/PaxHeaders.82075/system-stats.c0000644000000000000000000000013213534540071020323 xustar0030 mtime=1567801401.961685283 30 atime=1567801402.137686576 30 ctime=1567801425.201856523 openvswitch-2.5.9/vswitchd/system-stats.c0000644000175000017500000004337513534540071022025 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "system-stats.h" #include #include #include #if HAVE_MNTENT_H #include #endif #include #include #include #if HAVE_SYS_STATVFS_H #include #endif #include #include "daemon.h" #include "dirs.h" #include "dynamic-string.h" #include "json.h" #include "latch.h" #include "ofpbuf.h" #include "ovs-thread.h" #include "poll-loop.h" #include "shash.h" #include "smap.h" #include "timeval.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(system_stats); /* #ifdefs make it a pain to maintain code: you have to try to build both ways. * Thus, this file tries to compile as much of the code as possible regardless * of the target, by writing "if (LINUX)" instead of "#ifdef __linux__" where * this is possible. */ #ifdef __linux__ #define LINUX 1 #include #else #define LINUX 0 #endif static void get_cpu_cores(struct smap *stats) { long int n_cores = count_cpu_cores(); if (n_cores > 0) { smap_add_format(stats, "cpu", "%ld", n_cores); } } static void get_load_average(struct smap *stats OVS_UNUSED) { #if HAVE_GETLOADAVG double loadavg[3]; if (getloadavg(loadavg, 3) == 3) { smap_add_format(stats, "load_average", "%.2f,%.2f,%.2f", loadavg[0], loadavg[1], loadavg[2]); } #endif } static unsigned int get_page_size(void) { static unsigned int cached; if (!cached) { #ifndef _WIN32 long int value = sysconf(_SC_PAGESIZE); #else long int value; SYSTEM_INFO sysinfo; GetSystemInfo(&sysinfo); value = sysinfo.dwPageSize; #endif if (value >= 0) { cached = value; } } return cached; } static void get_memory_stats(struct smap *stats) { if (!LINUX) { unsigned int pagesize = get_page_size(); #ifdef _SC_PHYS_PAGES long int phys_pages = sysconf(_SC_PHYS_PAGES); #else long int phys_pages = 0; #endif #ifdef _SC_AVPHYS_PAGES long int avphys_pages = sysconf(_SC_AVPHYS_PAGES); #else long int avphys_pages = 0; #endif int mem_total, mem_used; #ifndef _WIN32 if (pagesize <= 0 || phys_pages <= 0 || avphys_pages <= 0) { return; } mem_total = phys_pages * (pagesize / 1024); mem_used = (phys_pages - avphys_pages) * (pagesize / 1024); #else MEMORYSTATUS memory_status; GlobalMemoryStatus(&memory_status); mem_total = memory_status.dwTotalPhys; mem_used = memory_status.dwTotalPhys - memory_status.dwAvailPhys; #endif smap_add_format(stats, "memory", "%d,%d", mem_total, mem_used); } else { static const char file_name[] = "/proc/meminfo"; int mem_used, mem_cache, swap_used; int mem_free = 0; int buffers = 0; int cached = 0; int swap_free = 0; int mem_total = 0; int swap_total = 0; struct shash dict; char line[128]; FILE *stream; stream = fopen(file_name, "r"); if (!stream) { VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); return; } shash_init(&dict); shash_add(&dict, "MemTotal", &mem_total); shash_add(&dict, "MemFree", &mem_free); shash_add(&dict, "Buffers", &buffers); shash_add(&dict, "Cached", &cached); shash_add(&dict, "SwapTotal", &swap_total); shash_add(&dict, "SwapFree", &swap_free); while (fgets(line, sizeof line, stream)) { char key[16]; int value; if (ovs_scan(line, "%15[^:]: %u", key, &value)) { int *valuep = shash_find_data(&dict, key); if (valuep) { *valuep = value; } } } fclose(stream); shash_destroy(&dict); mem_used = mem_total - mem_free; mem_cache = buffers + cached; swap_used = swap_total - swap_free; smap_add_format(stats, "memory", "%d,%d,%d,%d,%d", mem_total, mem_used, mem_cache, swap_total, swap_used); } } /* Returns the time at which the system booted, as the number of milliseconds * since the epoch, or 0 if the time of boot cannot be determined. */ static long long int get_boot_time(void) { static long long int cache_expiration = LLONG_MIN; static long long int boot_time; ovs_assert(LINUX); if (time_msec() >= cache_expiration) { static const char stat_file[] = "/proc/stat"; char line[128]; FILE *stream; cache_expiration = time_msec() + 5 * 1000; stream = fopen(stat_file, "r"); if (!stream) { VLOG_ERR_ONCE("%s: open failed (%s)", stat_file, ovs_strerror(errno)); return boot_time; } while (fgets(line, sizeof line, stream)) { long long int btime; if (ovs_scan(line, "btime %lld", &btime)) { boot_time = btime * 1000; goto done; } } VLOG_ERR_ONCE("%s: btime not found", stat_file); done: fclose(stream); } return boot_time; } static unsigned long long int ticks_to_ms(unsigned long long int ticks) { ovs_assert(LINUX); #ifndef USER_HZ #define USER_HZ 100 #endif #if USER_HZ == 100 /* Common case. */ return ticks * (1000 / USER_HZ); #else /* Alpha and some other architectures. */ double factor = 1000.0 / USER_HZ; return ticks * factor + 0.5; #endif } struct raw_process_info { unsigned long int vsz; /* Virtual size, in kB. */ unsigned long int rss; /* Resident set size, in kB. */ long long int uptime; /* ms since started. */ long long int cputime; /* ms of CPU used during 'uptime'. */ pid_t ppid; /* Parent. */ char name[18]; /* Name (surrounded by parentheses). */ }; static bool get_raw_process_info(pid_t pid, struct raw_process_info *raw) { unsigned long long int vsize, rss, start_time, utime, stime; long long int start_msec; unsigned long ppid; char file_name[128]; FILE *stream; int n; ovs_assert(LINUX); sprintf(file_name, "/proc/%lu/stat", (unsigned long int) pid); stream = fopen(file_name, "r"); if (!stream) { VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); return false; } n = fscanf(stream, "%*d " /* (1. pid) */ "%17s " /* 2. process name */ "%*c " /* (3. state) */ "%lu " /* 4. ppid */ "%*d " /* (5. pgid) */ "%*d " /* (6. sid) */ "%*d " /* (7. tty_nr) */ "%*d " /* (8. tty_pgrp) */ "%*u " /* (9. flags) */ "%*u " /* (10. min_flt) */ "%*u " /* (11. cmin_flt) */ "%*u " /* (12. maj_flt) */ "%*u " /* (13. cmaj_flt) */ "%llu " /* 14. utime */ "%llu " /* 15. stime */ "%*d " /* (16. cutime) */ "%*d " /* (17. cstime) */ "%*d " /* (18. priority) */ "%*d " /* (19. nice) */ "%*d " /* (20. num_threads) */ "%*d " /* (21. always 0) */ "%llu " /* 22. start_time */ "%llu " /* 23. vsize */ "%llu " /* 24. rss */ #if 0 /* These are here for documentation but #if'd out to save * actually parsing them from the stream for no benefit. */ "%*lu " /* (25. rsslim) */ "%*lu " /* (26. start_code) */ "%*lu " /* (27. end_code) */ "%*lu " /* (28. start_stack) */ "%*lu " /* (29. esp) */ "%*lu " /* (30. eip) */ "%*lu " /* (31. pending signals) */ "%*lu " /* (32. blocked signals) */ "%*lu " /* (33. ignored signals) */ "%*lu " /* (34. caught signals) */ "%*lu " /* (35. whcan) */ "%*lu " /* (36. always 0) */ "%*lu " /* (37. always 0) */ "%*d " /* (38. exit_signal) */ "%*d " /* (39. task_cpu) */ "%*u " /* (40. rt_priority) */ "%*u " /* (41. policy) */ "%*llu " /* (42. blkio_ticks) */ "%*lu " /* (43. gtime) */ "%*ld" /* (44. cgtime) */ #endif , raw->name, &ppid, &utime, &stime, &start_time, &vsize, &rss); fclose(stream); if (n != 7) { VLOG_ERR_ONCE("%s: fscanf failed", file_name); return false; } start_msec = get_boot_time() + ticks_to_ms(start_time); raw->vsz = vsize / 1024; raw->rss = rss * (getpagesize() / 1024); raw->uptime = time_wall_msec() - start_msec; raw->cputime = ticks_to_ms(utime + stime); raw->ppid = ppid; return true; } static int count_crashes(pid_t pid) { char file_name[128]; const char *paren; char line[128]; int crashes = 0; FILE *stream; ovs_assert(LINUX); sprintf(file_name, "/proc/%lu/cmdline", (unsigned long int) pid); stream = fopen(file_name, "r"); if (!stream) { VLOG_WARN_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); goto exit; } if (!fgets(line, sizeof line, stream)) { VLOG_WARN_ONCE("%s: read failed (%s)", file_name, feof(stream) ? "end of file" : ovs_strerror(errno)); goto exit_close; } paren = strchr(line, '('); if (paren) { int x; if (ovs_scan(paren + 1, "%d", &x)) { crashes = x; } } exit_close: fclose(stream); exit: return crashes; } struct process_info { unsigned long int vsz; /* Virtual size, in kB. */ unsigned long int rss; /* Resident set size, in kB. */ long long int booted; /* ms since monitor started. */ int crashes; /* # of crashes (usually 0). */ long long int uptime; /* ms since last (re)started by monitor. */ long long int cputime; /* ms of CPU used during 'uptime'. */ }; static bool get_process_info(pid_t pid, struct process_info *pinfo) { struct raw_process_info child; ovs_assert(LINUX); if (!get_raw_process_info(pid, &child)) { return false; } pinfo->vsz = child.vsz; pinfo->rss = child.rss; pinfo->booted = child.uptime; pinfo->crashes = 0; pinfo->uptime = child.uptime; pinfo->cputime = child.cputime; if (child.ppid) { struct raw_process_info parent; get_raw_process_info(child.ppid, &parent); if (!strcmp(child.name, parent.name)) { pinfo->booted = parent.uptime; pinfo->crashes = count_crashes(child.ppid); } } return true; } static void get_process_stats(struct smap *stats) { #ifndef _WIN32 struct dirent *de; DIR *dir; dir = opendir(ovs_rundir()); if (!dir) { VLOG_ERR_ONCE("%s: open failed (%s)", ovs_rundir(), ovs_strerror(errno)); return; } while ((de = readdir(dir)) != NULL) { struct process_info pinfo; char *file_name; char *extension; char *key; pid_t pid; #ifdef _DIRENT_HAVE_D_TYPE if (de->d_type != DT_UNKNOWN && de->d_type != DT_REG) { continue; } #endif extension = strrchr(de->d_name, '.'); if (!extension || strcmp(extension, ".pid")) { continue; } file_name = xasprintf("%s/%s", ovs_rundir(), de->d_name); pid = read_pidfile(file_name); free(file_name); if (pid < 0) { continue; } key = xasprintf("process_%.*s", (int) (extension - de->d_name), de->d_name); if (!smap_get(stats, key)) { if (LINUX && get_process_info(pid, &pinfo)) { smap_add_format(stats, key, "%lu,%lu,%lld,%d,%lld,%lld", pinfo.vsz, pinfo.rss, pinfo.cputime, pinfo.crashes, pinfo.booted, pinfo.uptime); } else { smap_add(stats, key, ""); } } free(key); } closedir(dir); #endif /* _WIN32 */ } static void get_filesys_stats(struct smap *stats OVS_UNUSED) { #if HAVE_GETMNTENT_R && HAVE_STATVFS static const char file_name[] = "/etc/mtab"; struct mntent mntent; struct mntent *me; char buf[4096]; FILE *stream; struct ds s; stream = setmntent(file_name, "r"); if (!stream) { VLOG_ERR_ONCE("%s: open failed (%s)", file_name, ovs_strerror(errno)); return; } ds_init(&s); while ((me = getmntent_r(stream, &mntent, buf, sizeof buf)) != NULL) { unsigned long long int total, free; struct statvfs vfs; char *p; /* Skip non-local and read-only filesystems. */ if (strncmp(me->mnt_fsname, "/dev", 4) || !strstr(me->mnt_opts, "rw")) { continue; } /* Given the mount point we can stat the file system. */ if (statvfs(me->mnt_dir, &vfs) && vfs.f_flag & ST_RDONLY) { /* That's odd... */ continue; } /* Now format the data. */ if (s.length) { ds_put_char(&s, ' '); } for (p = me->mnt_dir; *p != '\0'; p++) { ds_put_char(&s, *p == ' ' || *p == ',' ? '_' : *p); } total = (unsigned long long int) vfs.f_frsize * vfs.f_blocks / 1024; free = (unsigned long long int) vfs.f_frsize * vfs.f_bfree / 1024; ds_put_format(&s, ",%llu,%llu", total, total - free); } endmntent(stream); if (s.length) { smap_add(stats, "file_systems", ds_cstr(&s)); } ds_destroy(&s); #endif /* HAVE_GETMNTENT_R && HAVE_STATVFS */ } #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */ static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static struct latch latch OVS_GUARDED_BY(mutex); static bool enabled; static bool started OVS_GUARDED_BY(mutex); static struct smap *system_stats OVS_GUARDED_BY(mutex); OVS_NO_RETURN static void *system_stats_thread_func(void *); static void discard_stats(void); /* Enables or disables system stats collection, according to 'enable'. */ void system_stats_enable(bool enable) { if (enabled != enable) { ovs_mutex_lock(&mutex); if (enable) { if (!started) { ovs_thread_create("system_stats", system_stats_thread_func, NULL); latch_init(&latch); started = true; } discard_stats(); xpthread_cond_signal(&cond); } enabled = enable; ovs_mutex_unlock(&mutex); } } /* Tries to obtain a new snapshot of system stats every SYSTEM_STATS_INTERVAL * milliseconds. * * When a new snapshot is available (which only occurs if system stats are * enabled), returns it as an smap owned by the caller. The caller must use * both smap_destroy() and free() to completely free the returned data. * * When no new snapshot is available, returns NULL. */ struct smap * system_stats_run(void) { struct smap *stats = NULL; ovs_mutex_lock(&mutex); if (system_stats) { latch_poll(&latch); if (enabled) { stats = system_stats; system_stats = NULL; } else { discard_stats(); } } ovs_mutex_unlock(&mutex); return stats; } /* Causes poll_block() to wake up when system_stats_run() needs to be * called. */ void system_stats_wait(void) { if (enabled) { latch_wait(&latch); } } static void discard_stats(void) OVS_REQUIRES(mutex) { if (system_stats) { smap_destroy(system_stats); free(system_stats); system_stats = NULL; } } static void * system_stats_thread_func(void *arg OVS_UNUSED) { pthread_detach(pthread_self()); for (;;) { long long int next_refresh; struct smap *stats; ovs_mutex_lock(&mutex); while (!enabled) { ovs_mutex_cond_wait(&cond, &mutex); } ovs_mutex_unlock(&mutex); stats = xmalloc(sizeof *stats); smap_init(stats); get_cpu_cores(stats); get_load_average(stats); get_memory_stats(stats); get_process_stats(stats); get_filesys_stats(stats); ovs_mutex_lock(&mutex); discard_stats(); system_stats = stats; latch_set(&latch); ovs_mutex_unlock(&mutex); next_refresh = time_msec() + SYSTEM_STATS_INTERVAL; do { poll_timer_wait_until(next_refresh); poll_block(); } while (time_msec() < next_refresh); } } openvswitch-2.5.9/vswitchd/PaxHeaders.82075/vswitch.xml0000644000000000000000000000013213534540071017710 xustar0030 mtime=1567801401.965685311 30 atime=1567801402.137686576 30 ctime=1567801424.177848973 openvswitch-2.5.9/vswitchd/vswitch.xml0000644000175000017500000060671113534540071021411 0ustar00jpettitjpettit00000000000000

    A database with this schema holds the configuration for one Open vSwitch daemon. The top-level configuration for the daemon is the table, which must have exactly one record. Records in other tables are significant only when they can be reached directly or indirectly from the table. Records that are not reachable from the table are automatically deleted from the database, except for records in a few distinguished ``root set'' tables.

    Common Columns

    Most tables contain two special columns, named other_config and external_ids. These columns have the same form and purpose each place that they appear, so we describe them here to save space later.

    other_config: map of string-string pairs

    Key-value pairs for configuring rarely used features. Supported keys, along with the forms taken by their values, are documented individually for each table.

    A few tables do not have other_config columns because no key-value pairs have yet been defined for them.

    external_ids: map of string-string pairs
    Key-value pairs for use by external frameworks that integrate with Open vSwitch, rather than by Open vSwitch itself. System integrators should either use the Open vSwitch development mailing list to coordinate on common key-value definitions, or choose key names that are likely to be unique. In some cases, where key-value pairs have been defined that are likely to be widely useful, they are documented individually for each table.
    Configuration for an Open vSwitch daemon. There must be exactly one record in the table. Set of bridges managed by the daemon. SSL used globally by the daemon. A unique identifier for the Open vSwitch's physical host. The form of the identifier depends on the type of the host. On a Citrix XenServer, this will likely be the same as . The Citrix XenServer universally unique identifier for the physical host as displayed by xe host-list.

    Interval for updating statistics to the database, in milliseconds. This option will affect the update of the statistics column in the following tables: Port, Interface , Mirror.

    Default value is 5000 ms.

    Getting statistics more frequently can be achieved via OpenFlow.

    When ovs-vswitchd starts up, it has an empty flow table and therefore it handles all arriving packets in its default fashion according to its configuration, by dropping them or sending them to an OpenFlow controller or switching them as a standalone switch. This behavior is ordinarily desirable. However, if ovs-vswitchd is restarting as part of a ``hot-upgrade,'' then this leads to a relatively long period during which packets are mishandled.

    This option allows for improvement. When ovs-vswitchd starts with this value set as true, it will neither flush or expire previously set datapath flows nor will it send and receive any packets to or from the datapath. When this value is later set to false, ovs-vswitchd will start receiving packets from the datapath and re-setup the flows.

    Thus, with this option, the procedure for a hot-upgrade of ovs-vswitchd becomes roughly the following:

    1. Stop ovs-vswitchd.
    2. Set to true.
    3. Start ovs-vswitchd.
    4. Use ovs-ofctl (or some other program, such as an OpenFlow controller) to restore the OpenFlow flow table to the desired state.
    5. Set to false (or remove it entirely from the database).

    The ovs-ctl's ``restart'' and ``force-reload-kmod'' functions use the above config option during hot upgrades.

    The maximum number of flows allowed in the datapath flow table. Internally OVS will choose a flow limit which will likely be lower than this number, based on real time network conditions. Tweaking this value is discouraged unless you know exactly what you're doing.

    The default is 200000.

    The maximum time (in ms) that idle flows will remain cached in the datapath. Internally OVS will check the validity and activity for datapath flows regularly and may expire flows quicker than this number, based on real time network conditions. Tweaking this value is discouraged unless you know exactly what you're doing.

    The default is 10000.

    Specifies the maximum number of rx queues to be created for each dpdk interface. If not specified or specified to 0, one rx queue will be created for each dpdk interface by default.

    Specifies CPU mask for setting the cpu affinity of PMD (Poll Mode Driver) threads. Value should be in the form of hex string, similar to the dpdk EAL '-c COREMASK' option input or the 'taskset' mask input.

    The lowest order bit corresponds to the first CPU core. A set bit means the corresponding core is available and a pmd thread will be created and pinned to it. If the input does not cover all cores, those uncovered cores are considered not set.

    If not specified, one pmd thread will be created for each numa node and pinned to any available core on the numa node by default.

    Specifies the number of threads for software datapaths to use for handling new flows. The default the number of online CPU cores minus the number of revalidators.

    This configuration is per datapath. If you have more than one software datapath (e.g. some system bridges and some netdev bridges), then the total number of threads is n-handler-threads times the number of software datapaths.

    Specifies the number of threads for software datapaths to use for revalidating flows in the datapath. Typically, there is a direct correlation between the number of revalidator threads, and the number of flows allowed in the datapath. The default is the number of cpu cores divided by four plus one. If n-handler-threads is set, the default changes to the number of cpu cores minus the number of handler threads.

    This configuration is per datapath. If you have more than one software datapath (e.g. some system bridges and some netdev bridges), then the total number of threads is n-handler-threads times the number of software datapaths.

    Sequence number for client to increment. When a client modifies any part of the database configuration and wishes to wait for Open vSwitch to finish applying the changes, it may increment this sequence number. Sequence number that Open vSwitch sets to the current value of after it finishes applying a set of configuration changes.

    The statistics column contains key-value pairs that report statistics about a system running an Open vSwitch. These are updated periodically (currently, every 5 seconds). Key-value pairs that cannot be determined or that do not apply to a platform are omitted.

    Statistics are disabled by default to avoid overhead in the common case when statistics gathering is not useful. Set this value to true to enable populating the column or to false to explicitly disable it.

    Number of CPU processors, threads, or cores currently online and available to the operating system on which Open vSwitch is running, as an integer. This may be less than the number installed, if some are not online or if they are not available to the operating system.

    Open vSwitch userspace processes are not multithreaded, but the Linux kernel-based datapath is.

    A comma-separated list of three floating-point numbers, representing the system load average over the last 1, 5, and 15 minutes, respectively.

    A comma-separated list of integers, each of which represents a quantity of memory in kilobytes that describes the operating system on which Open vSwitch is running. In respective order, these values are:

    1. Total amount of RAM allocated to the OS.
    2. RAM allocated to the OS that is in use.
    3. RAM that can be flushed out to disk or otherwise discarded if that space is needed for another purpose. This number is necessarily less than or equal to the previous value.
    4. Total disk space allocated for swap.
    5. Swap space currently in use.

    On Linux, all five values can be determined and are included. On other operating systems, only the first two values can be determined, so the list will only have two values.

    One such key-value pair, with NAME replaced by a process name, will exist for each running Open vSwitch daemon process, with name replaced by the daemon's name (e.g. process_ovs-vswitchd). The value is a comma-separated list of integers. The integers represent the following, with memory measured in kilobytes and durations in milliseconds:

    1. The process's virtual memory size.
    2. The process's resident set size.
    3. The amount of user and system CPU time consumed by the process.
    4. The number of times that the process has crashed and been automatically restarted by the monitor.
    5. The duration since the process was started.
    6. The duration for which the process has been running.

    The interpretation of some of these values depends on whether the process was started with the . If it was not, then the crash count will always be 0 and the two durations will always be the same. If was given, then the crash count may be positive; if it is, the latter duration is the amount of time since the most recent crash and restart.

    There will be one key-value pair for each file in Open vSwitch's ``run directory'' (usually /var/run/openvswitch) whose name ends in .pid, whose contents are a process ID, and which is locked by a running process. The name is taken from the pidfile's name.

    Currently Open vSwitch is only able to obtain all of the above detail on Linux systems. On other systems, the same key-value pairs will be present but the values will always be the empty string.

    A space-separated list of information on local, writable file systems. Each item in the list describes one file system and consists in turn of a comma-separated list of the following:

    1. Mount point, e.g. / or /var/log. Any spaces or commas in the mount point are replaced by underscores.
    2. Total size, in kilobytes, as an integer.
    3. Amount of storage in use, in kilobytes, as an integer.

    This key-value pair is omitted if there are no local, writable file systems or if Open vSwitch cannot obtain the needed information.

    These columns report the types and versions of the hardware and software running Open vSwitch. We recommend in general that software should test whether specific features are supported instead of relying on version number checks. These values are primarily intended for reporting to human administrators.

    The Open vSwitch version number, e.g. 1.1.0.

    The database schema version number in the form major.minor.tweak, e.g. 1.2.3. Whenever the database schema is changed in a non-backward compatible way (e.g. deleting a column or a table), major is incremented. When the database schema is changed in a backward compatible way (e.g. adding a new column), minor is incremented. When the database schema is changed cosmetically (e.g. reindenting its syntax), tweak is incremented.

    The schema version is part of the database schema, so it can also be retrieved by fetching the schema using the Open vSwitch database protocol.

    An identifier for the type of system on top of which Open vSwitch runs, e.g. XenServer or KVM.

    System integrators are responsible for choosing and setting an appropriate value for this column.

    The version of the system identified by , e.g. 5.6.100-39265p on XenServer 5.6.100 build 39265.

    System integrators are responsible for choosing and setting an appropriate value for this column.

    These columns report capabilities of the Open vSwitch instance.

    This column reports the different dpifs registered with the system. These are the values that this instance supports in the column of the table.

    This column reports the different netdevs registered with the system. These are the values that this instance supports in the column of the table.

    These columns primarily configure the Open vSwitch database (ovsdb-server), not the Open vSwitch switch (ovs-vswitchd). The OVSDB database also uses the settings.

    The Open vSwitch switch does read the database configuration to determine remote IP addresses to which in-band control should apply.

    Database clients to which the Open vSwitch database server should connect or to which it should listen, along with options for how these connection should be configured. See the table for more information.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a bridge within an .

    A record represents an Ethernet switch with one or more ``ports,'' which are the records pointed to by the 's column.

    Bridge identifier. Should be alphanumeric and no more than about 8 bytes long. Must be unique among the names of ports, interfaces, and bridges on a host. Ports included in the bridge. Port mirroring configuration. NetFlow configuration. sFlow(R) configuration. IPFIX configuration.

    VLAN IDs of VLANs on which MAC address learning should be disabled, so that packets are flooded instead of being sent to specific ports that are believed to contain packets' destination MACs. This should ordinarily be used to disable MAC learning on VLANs used for mirroring (RSPAN VLANs). It may also be useful for debugging.

    SLB bonding (see the column in the table) is incompatible with flood_vlans. Consider using another bonding mode or a different type of mirror instead.

    Auto Attach configuration.

    OpenFlow controller set. If unset, then no OpenFlow controllers will be used.

    If there are primary controllers, removing all of them clears the flow table. If there are no primary controllers, adding one also clears the flow table. Other changes to the set of controllers, such as adding or removing a service controller, adding another primary controller to supplement an existing primary controller, or removing only one of two primary controllers, have no effect on the flow table.

    Configuration for OpenFlow tables. Each pair maps from an OpenFlow table ID to configuration for that table.

    When a controller is configured, it is, ordinarily, responsible for setting up all flows on the switch. Thus, if the connection to the controller fails, no new network connections can be set up. If the connection to the controller stays down long enough, no packets can pass through the switch at all. This setting determines the switch's response to such a situation. It may be set to one of the following:

    standalone
    If no message is received from the controller for three times the inactivity probe interval (see ), then Open vSwitch will take over responsibility for setting up flows. In this mode, Open vSwitch causes the bridge to act like an ordinary MAC-learning switch. Open vSwitch will continue to retry connecting to the controller in the background and, when the connection succeeds, it will discontinue its standalone behavior.
    secure
    Open vSwitch will not set up flows on its own when the controller connection fails or when no controllers are defined. The bridge will continue to retry connecting to any defined controllers forever.

    The default is standalone if the value is unset, but future versions of Open vSwitch may change the default.

    The standalone mode can create forwarding loops on a bridge that has more than one uplink port unless STP is enabled. To avoid loops on such a bridge, configure secure mode or enable STP (see ).

    When more than one controller is configured, is considered only when none of the configured controllers can be contacted.

    Changing when no primary controllers are configured clears the flow table.

    Reports the OpenFlow datapath ID in use. Exactly 16 hex digits. (Setting this column has no useful effect. Set instead.)

    Reports the version number of the Open vSwitch datapath in use. This allows management software to detect and report discrepancies between Open vSwitch userspace and datapath versions. (The column in the reports the Open vSwitch userspace version.) The version reported depends on the datapath in use:

    • When the kernel module included in the Open vSwitch source tree is used, this column reports the Open vSwitch version from which the module was taken.
    • When the kernel module that is part of the upstream Linux kernel is used, this column reports <unknown>.
    • When the datapath is built into the ovs-vswitchd binary, this column reports <built-in>. A built-in datapath is by definition the same version as the rest of the Open VSwitch userspace.
    • Other datapaths (such as the Hyper-V kernel datapath) currently report <unknown>.

    A version discrepancy between ovs-vswitchd and the datapath in use is not normally cause for alarm. The Open vSwitch kernel datapaths for Linux and Hyper-V, in particular, are designed for maximum inter-version compatibility: any userspace version works with with any kernel version. Some reasons do exist to insist on particular user/kernel pairings. First, newer kernel versions add new features, that can only be used by new-enough userspace, e.g. VXLAN tunneling requires certain minimal userspace and kernel versions. Second, as an extension to the first reason, some newer kernel versions add new features for enhancing performance that only new-enough userspace versions can take advantage of.

    Exactly 16 hex digits to set the OpenFlow datapath ID to a specific value. May not be all-zero. Human readable description of datapath. It it a maximum 256 byte-long free-form string to describe the datapath for debugging purposes, e.g. switch3 in room 3120. If set to true, disable in-band control on the bridge regardless of controller and manager settings. A queue ID as a nonnegative integer. This sets the OpenFlow queue ID that will be used by flows set up by in-band control on this bridge. If unset, or if the port used by an in-band control flow does not have QoS configured, or if the port does not have a queue with the specified ID, the default queue is used instead.

    List of OpenFlow protocols that may be used when negotiating a connection with a controller. OpenFlow 1.0, 1.1, 1.2, and 1.3 are enabled by default if this column is empty.

    OpenFlow 1.4 is not enabled by default because its implementation is missing features.

    OpenFlow 1.5 has the same risks as OpenFlow 1.4, but it is even more experimental because the OpenFlow 1.5 specification is still under development and thus subject to change. Pass --enable-of15 to ovs-vswitchd to allow OpenFlow 1.5 to be enabled.

    The IEEE 802.1D Spanning Tree Protocol (STP) is a network protocol that ensures loop-free topologies. It allows redundant links to be included in the network to provide automatic backup paths if the active links fails.

    These settings configure the slower-to-converge but still widely supported version of Spanning Tree Protocol, sometimes known as 802.1D-1998. Open vSwitch also supports the newer Rapid Spanning Tree Protocol (RSTP), documented later in the section titled Rapid Spanning Tree Configuration.

    Enable spanning tree on the bridge. By default, STP is disabled on bridges. Bond, internal, and mirror ports are not supported and will not participate in the spanning tree.

    STP and RSTP are mutually exclusive. If both are enabled, RSTP will be used.

    The bridge's STP identifier (the lower 48 bits of the bridge-id) in the form xx:xx:xx:xx:xx:xx. By default, the identifier is the MAC address of the bridge. The bridge's relative priority value for determining the root bridge (the upper 16 bits of the bridge-id). A bridge with the lowest bridge-id is elected the root. By default, the priority is 0x8000. The interval between transmissions of hello messages by designated ports, in seconds. By default the hello interval is 2 seconds. The maximum age of the information transmitted by the bridge when it is the root bridge, in seconds. By default, the maximum age is 20 seconds. The delay to wait between transitioning root and designated ports to forwarding, in seconds. By default, the forwarding delay is 15 seconds.

    The maximum number of seconds to retain a multicast snooping entry for which no packets have been seen. The default is currently 300 seconds (5 minutes). The value, if specified, is forced into a reasonable range, currently 15 to 3600 seconds.

    The maximum number of multicast snooping addresses to learn. The default is currently 2048. The value, if specified, is forced into a reasonable range, currently 10 to 1,000,000.

    If set to false, unregistered multicast packets are forwarded to all ports. If set to true, unregistered multicast packets are forwarded to ports connected to multicast routers.

    These key-value pairs report the status of 802.1D-1998. They are present only if STP is enabled (via the column).

    The bridge ID used in spanning tree advertisements, in the form xxxx.yyyyyyyyyyyy where the xs are the STP priority, the ys are the STP system ID, and each x and y is a hex digit. The designated root for this spanning tree, in the same form as . If this bridge is the root, this will have the same value as , otherwise it will differ. The path cost of reaching the designated bridge. A lower number is better. The value is 0 if this bridge is the root, otherwise it is higher.

    Rapid Spanning Tree Protocol (RSTP), like STP, is a network protocol that ensures loop-free topologies. RSTP superseded STP with the publication of 802.1D-2004. Compared to STP, RSTP converges more quickly and recovers more quickly from failures.

    Enable Rapid Spanning Tree on the bridge. By default, RSTP is disabled on bridges. Bond, internal, and mirror ports are not supported and will not participate in the spanning tree.

    STP and RSTP are mutually exclusive. If both are enabled, RSTP will be used.

    The bridge's RSTP address (the lower 48 bits of the bridge-id) in the form xx:xx:xx:xx:xx:xx. By default, the address is the MAC address of the bridge. The bridge's relative priority value for determining the root bridge (the upper 16 bits of the bridge-id). A bridge with the lowest bridge-id is elected the root. By default, the priority is 0x8000 (32768). This value needs to be a multiple of 4096, otherwise it's rounded to the nearest inferior one. The Ageing Time parameter for the Bridge. The default value is 300 seconds. The Force Protocol Version parameter for the Bridge. This can take the value 0 (STP Compatibility mode) or 2 (the default, normal operation). The maximum age of the information transmitted by the Bridge when it is the Root Bridge. The default value is 20. The delay used by STP Bridges to transition Root and Designated Ports to Forwarding. The default value is 15. The Transmit Hold Count used by the Port Transmit state machine to limit transmission rate. The default value is 6.

    These key-value pairs report the status of 802.1D-2004. They are present only if RSTP is enabled (via the column).

    The bridge ID used in rapid spanning tree advertisements, in the form x.yyy.zzzzzzzzzzzz where x is the RSTP priority, the ys are a locally assigned system ID extension, the zs are the STP system ID, and each x, y, or z is a hex digit. The root of this spanning tree, in the same form as . If this bridge is the root, this will have the same value as , otherwise it will differ. The path cost of reaching the root. A lower number is better. The value is 0 if this bridge is the root, otherwise it is higher. The RSTP designated ID, in the same form as . The RSTP designated port ID, as a 4-digit hex number. The RSTP bridge port ID, as a 4-digit hex number.
    Multicast snooping (RFC 4541) monitors the Internet Group Management Protocol (IGMP) and Multicast Listener Discovery traffic between hosts and multicast routers. The switch uses what IGMP and MLD snooping learns to forward multicast traffic only to interfaces that are connected to interested receivers. Currently it supports IGMPv1, IGMPv2, IGMPv3, MLDv1 and MLDv2 protocols. Enable multicast snooping on the bridge. For now, the default is disabled. Name of datapath provider. The kernel datapath has type system. The userspace datapath has type netdev. A manager may refer to the column of the table for a list of the types accepted by this Open vSwitch instance. A unique identifier of the bridge. On Citrix XenServer this will commonly be the same as . Semicolon-delimited set of universally unique identifier(s) for the network with which this bridge is associated on a Citrix XenServer host. The network identifiers are RFC 4122 UUIDs as displayed by, e.g., xe network-list. An Ethernet address in the form xx:xx:xx:xx:xx:xx to set the hardware address of the local port and influence the datapath ID.

    Controls forwarding of BPDUs and other network control frames when NORMAL action is invoked. When this option is false or unset, frames with reserved Ethernet addresses (see table below) will not be forwarded. When this option is true, such frames will not be treated specially.

    The above general rule has the following exceptions:

    • If STP is enabled on the bridge (see the column in the table), the bridge processes all received STP packets and never passes them to OpenFlow or forwards them. This is true even if STP is disabled on an individual port.
    • If LLDP is enabled on an interface (see the column in the table), the interface processes received LLDP packets and never passes them to OpenFlow or forwards them.

    Set this option to true if the Open vSwitch bridge connects different Ethernet networks and is not configured to participate in STP.

    This option affects packets with the following destination MAC addresses:

    01:80:c2:00:00:00
    IEEE 802.1D Spanning Tree Protocol (STP).
    01:80:c2:00:00:01
    IEEE Pause frame.
    01:80:c2:00:00:0x
    Other reserved protocols.
    00:e0:2b:00:00:00
    Extreme Discovery Protocol (EDP).
    00:e0:2b:00:00:04 and 00:e0:2b:00:00:06
    Ethernet Automatic Protection Switching (EAPS).
    01:00:0c:cc:cc:cc
    Cisco Discovery Protocol (CDP), VLAN Trunking Protocol (VTP), Dynamic Trunking Protocol (DTP), Port Aggregation Protocol (PAgP), and others.
    01:00:0c:cc:cc:cd
    Cisco Shared Spanning Tree Protocol PVSTP+.
    01:00:0c:cd:cd:cd
    Cisco STP Uplink Fast.
    01:00:0c:00:00:00
    Cisco Inter Switch Link.
    01:00:0c:cc:cc:cx
    Cisco CFM.

    The maximum number of seconds to retain a MAC learning entry for which no packets have been seen. The default is currently 300 seconds (5 minutes). The value, if specified, is forced into a reasonable range, currently 15 to 3600 seconds.

    A short MAC aging time allows a network to more quickly detect that a host is no longer connected to a switch port. However, it also makes it more likely that packets will be flooded unnecessarily, when they are addressed to a connected host that rarely transmits packets. To reduce the incidence of unnecessary flooding, use a MAC aging time longer than the maximum interval at which a host will ordinarily transmit packets.

    The maximum number of MAC addresses to learn. The default is currently 2048. The value, if specified, is forced into a reasonable range, currently 10 to 1,000,000.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A port within a .

    Most commonly, a port has exactly one ``interface,'' pointed to by its column. Such a port logically corresponds to a port on a physical Ethernet switch. A port with more than one interface is a ``bonded port'' (see ).

    Some properties that one might think as belonging to a port are actually part of the port's members.

    Port name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the interface name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host. The port's interfaces. If there is more than one, this is a bonded Port.

    Bridge ports support the following types of VLAN configuration:

    trunk

    A trunk port carries packets on one or more specified VLANs specified in the column (often, on every VLAN). A packet that ingresses on a trunk port is in the VLAN specified in its 802.1Q header, or VLAN 0 if the packet has no 802.1Q header. A packet that egresses through a trunk port will have an 802.1Q header if it has a nonzero VLAN ID.

    Any packet that ingresses on a trunk port tagged with a VLAN that the port does not trunk is dropped.

    access

    An access port carries packets on exactly one VLAN specified in the column. Packets egressing on an access port have no 802.1Q header.

    Any packet with an 802.1Q header with a nonzero VLAN ID that ingresses on an access port is dropped, regardless of whether the VLAN ID in the header is the access port's VLAN ID.

    native-tagged
    A native-tagged port resembles a trunk port, with the exception that a packet without an 802.1Q header that ingresses on a native-tagged port is in the ``native VLAN'' (specified in the column).
    native-untagged
    A native-untagged port resembles a native-tagged port, with the exception that a packet that egresses on a native-untagged port in the native VLAN will not have an 802.1Q header.

    A packet will only egress through bridge ports that carry the VLAN of the packet, as described by the rules above.

    The VLAN mode of the port, as described above. When this column is empty, a default mode is selected as follows:

    • If contains a value, the port is an access port. The column should be empty.
    • Otherwise, the port is a trunk port. The column value is honored if it is present.

    For an access port, the port's implicitly tagged VLAN. For a native-tagged or native-untagged port, the port's native VLAN. Must be empty if this is a trunk port.

    For a trunk, native-tagged, or native-untagged port, the 802.1Q VLAN or VLANs that this port trunks; if it is empty, then the port trunks all VLANs. Must be empty if this is an access port.

    A native-tagged or native-untagged port always trunks its native VLAN, regardless of whether includes that VLAN.

    An 802.1Q header contains two important pieces of information: a VLAN ID and a priority. A frame with a zero VLAN ID, called a ``priority-tagged'' frame, is supposed to be treated the same way as a frame without an 802.1Q header at all (except for the priority).

    However, some network elements ignore any frame that has 802.1Q header at all, even when the VLAN ID is zero. Therefore, by default Open vSwitch does not output priority-tagged frames, instead omitting the 802.1Q header entirely if the VLAN ID is zero. Set this key to true to enable priority-tagged frames on a port.

    Regardless of this setting, Open vSwitch omits the 802.1Q header on output if both the VLAN ID and priority would be zero.

    All frames output to native-tagged ports have a nonzero VLAN ID, so this setting is not meaningful on native-tagged ports.

    A port that has more than one interface is a ``bonded port.'' Bonding allows for load balancing and fail-over.

    The following types of bonding will work with any kind of upstream switch. On the upstream switch, do not configure the interfaces as a bond:

    balance-slb
    Balances flows among slaves based on source MAC address and output VLAN, with periodic rebalancing as traffic patterns change.
    active-backup
    Assigns all flows to one slave, failing over to a backup slave when the active slave is disabled. This is the only bonding mode in which interfaces may be plugged into different upstream switches.

    The following modes require the upstream switch to support 802.3ad with successful LACP negotiation. If LACP negotiation fails and other-config:lacp-fallback-ab is true, then active-backup mode is used:

    balance-tcp
    Balances flows among slaves based on L3 and L4 protocol information such as IP addresses and TCP/UDP ports.

    These columns apply only to bonded ports. Their values are otherwise ignored.

    The type of bonding used for a bonded port. Defaults to active-backup if unset.

    An integer hashed along with flows when choosing output slaves in load balanced bonds. When changed, all flows will be assigned different hash values possibly causing slave selection decisions to change. Does not affect bonding modes which do not employ load balancing such as active-backup.

    An important part of link bonding is detecting that links are down so that they may be disabled. These settings determine how Open vSwitch detects link failure.

    The means used to detect link failures. Defaults to carrier which uses each interface's carrier to detect failures. When set to miimon, will check for failures by polling each interface's MII. The interval, in milliseconds, between successive attempts to poll each interface's MII. Relevant only when is miimon.

    The number of milliseconds for which the link must stay up on an interface before the interface is considered to be up. Specify 0 to enable the interface immediately.

    This setting is honored only when at least one bonded interface is already enabled. When no interfaces are enabled, then the first bond interface to come up is enabled immediately.

    The number of milliseconds for which the link must stay down on an interface before the interface is considered to be down. Specify 0 to disable the interface immediately.

    LACP, the Link Aggregation Control Protocol, is an IEEE standard that allows switches to automatically detect that they are connected by multiple links and aggregate across those links. These settings control LACP behavior.

    Configures LACP on this port. LACP allows directly connected switches to negotiate which links may be bonded. LACP may be enabled on non-bonded ports for the benefit of any switches they may be connected to. active ports are allowed to initiate LACP negotiations. passive ports are allowed to participate in LACP negotiations initiated by a remote switch, but not allowed to initiate such negotiations themselves. If LACP is enabled on a port whose partner switch does not support LACP, the bond will be disabled, unless other-config:lacp-fallback-ab is set to true. Defaults to off if unset. The LACP system ID of this . The system ID of a LACP bond is used to identify itself to its partners. Must be a nonzero MAC address. Defaults to the bridge Ethernet address if unset. The LACP system priority of this . In LACP negotiations, link status decisions are made by the system with the numerically lower priority.

    The LACP timing which should be used on this . By default slow is used. When configured to be fast LACP heartbeats are requested at a rate of once per second causing connectivity problems to be detected more quickly. In slow mode, heartbeats are requested at a rate of once every 30 seconds.

    Determines the behavior of openvswitch bond in LACP mode. If the partner switch does not support LACP, setting this option to true allows openvswitch to fallback to active-backup. If the option is set to false, the bond will be disabled. In both the cases, once the partner switch is configured to LACP mode, the bond will use LACP.

    These settings control behavior when a bond is in balance-slb or balance-tcp mode.

    For a load balanced bonded port, the number of milliseconds between successive attempts to rebalance the bond, that is, to move flows from one interface on the bond to another in an attempt to keep usage of each interface roughly equal. If zero, load balancing is disabled on the bond (link failure still cause flows to move). If less than 1000ms, the rebalance interval will be 1000ms.
    For a bonded port, whether to create a fake internal interface with the name of the port. Use only for compatibility with legacy software that requires this.

    The configuration here is only meaningful, and the status is only populated, when 802.1D-1998 Spanning Tree Protocol is enabled on the port's with its column.

    When STP is enabled on a bridge, it is enabled by default on all of the bridge's ports except bond, internal, and mirror ports (which do not work with STP). If this column's value is false, STP is disabled on the port. The port number used for the lower 8 bits of the port-id. By default, the numbers will be assigned automatically. If any port's number is manually configured on a bridge, then they must all be. The port's relative priority value for determining the root port (the upper 8 bits of the port-id). A port with a lower port-id will be chosen as the root port. By default, the priority is 0x80. Spanning tree path cost for the port. A lower number indicates a faster link. By default, the cost is based on the maximum speed of the link. The port ID used in spanning tree advertisements for this port, as 4 hex digits. Configuring the port ID is described in the stp-port-num and stp-port-priority keys of the other_config section earlier. STP state of the port. The amount of time this port has been in the current STP state, in seconds. STP role of the port.

    The configuration here is only meaningful, and the status and statistics are only populated, when 802.1D-1998 Spanning Tree Protocol is enabled on the port's with its column.

    When RSTP is enabled on a bridge, it is enabled by default on all of the bridge's ports except bond, internal, and mirror ports (which do not work with RSTP). If this column's value is false, RSTP is disabled on the port. The port's relative priority value for determining the root port, in multiples of 16. By default, the port priority is 0x80 (128). Any value in the lower 4 bits is rounded off. The significant upper 4 bits become the upper 4 bits of the port-id. A port with the lowest port-id is elected as the root. The local RSTP port number, used as the lower 12 bits of the port-id. By default the port numbers are assigned automatically, and typically may not correspond to the OpenFlow port numbers. A port with the lowest port-id is elected as the root. The port path cost. The Port's contribution, when it is the Root Port, to the Root Path Cost for the Bridge. By default the cost is automatically calculated from the port's speed. The admin edge port parameter for the Port. Default is false. The auto edge port parameter for the Port. Default is true.

    The mcheck port parameter for the Port. Default is false. May be set to force the Port Protocol Migration state machine to transmit RST BPDUs for a MigrateTime period, to test whether all STP Bridges on the attached LAN have been removed and the Port can continue to transmit RSTP BPDUs. Setting mcheck has no effect if the Bridge is operating in STP Compatibility mode.

    Changing the value from true to false has no effect, but needs to be done if this behavior is to be triggered again by subsequently changing the value from false to true.

    The port ID used in spanning tree advertisements for this port, as 4 hex digits. Configuring the port ID is described in the rstp-port-num and rstp-port-priority keys of the other_config section earlier. RSTP role of the port. RSTP state of the port. The port's RSTP designated bridge ID, in the same form as in the table. The port's RSTP designated port ID, as 4 hex digits. The port's RSTP designated path cost. Lower is better. Number of RSTP BPDUs transmitted through this port. Number of valid RSTP BPDUs received by this port. Number of invalid RSTP BPDUs received by this port. The duration covered by the other RSTP statistics, in seconds.

    If set to true, multicast packets (except Reports) are unconditionally forwarded to the specific port.

    If set to true, multicast Reports are unconditionally forwarded to the specific port.

    Quality of Service configuration for this port. The MAC address to use for this port for the purpose of choosing the bridge's MAC address. This column does not necessarily reflect the port's actual MAC address, nor will setting it change the port's actual MAC address. Does this port represent a sub-bridge for its tagged VLAN within the Bridge? See ovs-vsctl(8) for more information. External IDs for a fake bridge (see the column) are defined by prefixing a key with fake-bridge-, e.g. fake-bridge-xs-network-uuids.

    If set to true, the port will be removed when ovs-ctl start --delete-transient-ports is used.

    For a bonded port, record the mac address of the current active slave.

    Key-value pairs that report port statistics. The update period is controlled by in the Open_vSwitch table.

    Number of STP BPDUs sent on this port by the spanning tree library. Number of STP BPDUs received on this port and accepted by the spanning tree library. Number of bad STP BPDUs received on this port. Bad BPDUs include runt packets and those with an unexpected protocol ID.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.
    An interface within a . Interface name. Should be alphanumeric and no more than about 8 bytes long. May be the same as the port name, for non-bonded ports. Must otherwise be unique among the names of ports, interfaces, and bridges on a host. A positive interface index as defined for SNMP MIB-II in RFCs 1213 and 2863, if the interface has one, otherwise 0. The ifindex is useful for seamless integration with protocols such as SNMP and sFlow. The MAC address in use by this interface.

    Ethernet address to set for this interface. If unset then the default MAC address is used:

    • For the local interface, the default is the lowest-numbered MAC address among the other bridge ports, either the value of the in its record, if set, or its actual MAC (for bonded ports, the MAC of its slave whose name is first in alphabetical order). Internal ports and bridge ports that are used as port mirroring destinations (see the table) are ignored.
    • For other internal interfaces, the default MAC is randomly generated.
    • External interfaces typically have a MAC address associated with their hardware.

    Some interfaces may not have a software-controllable MAC address.

    If the configuration of the port failed, as indicated by -1 in , Open vSwitch sets this column to an error description in human readable form. Otherwise, Open vSwitch clears this column.

    When a client adds a new interface, Open vSwitch chooses an OpenFlow port number for the new port. If the client that adds the port fills in , then Open vSwitch tries to use its value as the OpenFlow port number. Otherwise, or if the requested port number is already in use or cannot be used for another reason, Open vSwitch automatically assigns a free port number. Regardless of how the port number was obtained, Open vSwitch then reports in the port number actually assigned.

    Open vSwitch limits the port numbers that it automatically assigns to the range 1 through 32,767, inclusive. Controllers therefore have free use of ports 32,768 and up.

    OpenFlow port number for this interface. Open vSwitch sets this column's value, so other clients should treat it as read-only.

    The OpenFlow ``local'' port (OFPP_LOCAL) is 65,534. The other valid port numbers are in the range 1 to 65,279, inclusive. Value -1 indicates an error adding the interface.

    Requested OpenFlow port number for this interface.

    A client should ideally set this column's value in the same database transaction that it uses to create the interface. Open vSwitch version 2.1 and later will honor a later request for a specific port number, althuogh it might confuse some controllers: OpenFlow does not have a way to announce a port number change, so Open vSwitch represents it over OpenFlow as a port deletion followed immediately by a port addition.

    If is set or changed to some other port's automatically assigned port number, Open vSwitch chooses a new port number for the latter port.

    The interface type. The types supported by a particular instance of Open vSwitch are listed in the column in the table. The following types are defined:

    system
    An ordinary network device, e.g. eth0 on Linux. Sometimes referred to as ``external interfaces'' since they are generally connected to hardware external to that on which the Open vSwitch is running. The empty string is a synonym for system.
    internal
    A simulated network device that sends and receives traffic. An internal interface whose is the same as its bridge's is called the ``local interface.'' It does not make sense to bond an internal interface, so the terms ``port'' and ``interface'' are often used imprecisely for internal interfaces.
    tap
    A TUN/TAP device managed by Open vSwitch.
    geneve
    An Ethernet over Geneve (http://tools.ietf.org/html/draft-ietf-nvo3-geneve-00) IPv4 tunnel. A description of how to match and set Geneve options can be found in the ovs-ofctl manual page.
    gre
    An Ethernet over RFC 2890 Generic Routing Encapsulation over IPv4 tunnel.
    ipsec_gre
    An Ethernet over RFC 2890 Generic Routing Encapsulation over IPv4 IPsec tunnel.
    vxlan

    An Ethernet tunnel over the UDP-based VXLAN protocol described in RFC 7348.

    Open vSwitch uses UDP destination port 4789. The source port used for VXLAN traffic varies on a per-flow basis and is in the ephemeral port range.

    lisp

    A layer 3 tunnel over the experimental, UDP-based Locator/ID Separation Protocol (RFC 6830).

    Only IPv4 and IPv6 packets are supported by the protocol, and they are sent and received without an Ethernet header. Traffic to/from LISP ports is expected to be configured explicitly, and the ports are not intended to participate in learning based switching. As such, they are always excluded from packet flooding.

    stt
    The Stateless TCP Tunnel (STT) is particularly useful when tunnel endpoints are in end-systems, as it utilizes the capabilities of standard network interface cards to improve performance. STT utilizes a TCP-like header inside the IP header. It is stateless, i.e., there is no TCP connection state of any kind associated with the tunnel. The TCP-like header is used to leverage the capabilities of existing network interface cards, but should not be interpreted as implying any sort of connection state between endpoints. Since the STT protocol does not engage in the usual TCP 3-way handshake, so it will have difficulty traversing stateful firewalls. The protocol is documented at http://www.ietf.org/archive/id/draft-davie-stt-06.txt All traffic uses a default destination port of 7471. STT is only available in kernel datapath on kernel 3.5 or newer.
    patch
    A pair of virtual devices that act as a patch cable.
    null
    An ignored interface. Deprecated and slated for removal in February 2013.

    These options apply to interfaces with of geneve, gre, ipsec_gre, vxlan, lisp and stt.

    Each tunnel must be uniquely identified by the combination of , , , and . If two ports are defined that are the same except one has an optional identifier and the other does not, the more specific one is matched first. is considered more specific than if a port defines one and another port defines the other.

    Required. The remote tunnel endpoint, one of:

    • An IPv4 address (not a DNS name), e.g. 192.168.0.123. Only unicast endpoints are supported.
    • The word flow. The tunnel accepts packets from any remote tunnel endpoint. To process only packets from a specific remote tunnel endpoint, the flow entries may match on the tun_src field. When sending packets to a remote_ip=flow tunnel, the flow actions must explicitly set the tun_dst field to the IP address of the desired remote tunnel endpoint, e.g. with a set_field action.

    The remote tunnel endpoint for any packet received from a tunnel is available in the tun_src field for matching in the flow table.

    Optional. The tunnel destination IP that received packets must match. Default is to match all addresses. If specified, may be one of:

    • An IPv4 address (not a DNS name), e.g. 192.168.12.3.
    • The word flow. The tunnel accepts packets sent to any of the local IP addresses of the system running OVS. To process only packets sent to a specific IP address, the flow entries may match on the tun_dst field. When sending packets to a local_ip=flow tunnel, the flow actions may explicitly set the tun_src field to the desired IP address, e.g. with a set_field action. However, while routing the tunneled packet out, the local system may override the specified address with the local IP address configured for the outgoing system interface.

      This option is valid only for tunnels also configured with the remote_ip=flow option.

    The tunnel destination IP address for any packet received from a tunnel is available in the tun_dst field for matching in the flow table.

    Optional. The key that received packets must contain, one of:

    • 0. The tunnel receives packets with no key or with a key of 0. This is equivalent to specifying no at all.
    • A positive 24-bit (for Geneve, VXLAN, and LISP), 32-bit (for GRE) or 64-bit (for STT) number. The tunnel receives only packets with the specified key.
    • The word flow. The tunnel accepts packets with any key. The key will be placed in the tun_id field for matching in the flow table. The ovs-ofctl manual page contains additional information about matching fields in OpenFlow flows.

    Optional. The key to be set on outgoing packets, one of:

    • 0. Packets sent through the tunnel will have no key. This is equivalent to specifying no at all.
    • A positive 24-bit (for Geneve, VXLAN and LISP), 32-bit (for GRE) or 64-bit (for STT) number. Packets sent through the tunnel will have the specified key.
    • The word flow. Packets sent through the tunnel will have the key set using the set_tunnel Nicira OpenFlow vendor extension (0 is used in the absence of an action). The ovs-ofctl manual page contains additional information about the Nicira OpenFlow vendor extensions.
    Optional. Shorthand to set in_key and out_key at the same time. Optional. The value of the ToS bits to be set on the encapsulating packet. ToS is interpreted as DSCP and ECN bits, ECN part must be zero. It may also be the word inherit, in which case the ToS will be copied from the inner packet if it is IPv4 or IPv6 (otherwise it will be 0). The ECN fields are always inherited. Default is 0. Optional. The TTL to be set on the encapsulating packet. It may also be the word inherit, in which case the TTL will be copied from the inner packet if it is IPv4 or IPv6 (otherwise it will be the system default, typically 64). Default is the system default TTL. Optional. If enabled, the Don't Fragment bit will be set on tunnel outer headers to allow path MTU discovery. Default is enabled; set to false to disable.

    Optional. Comma separated list of optional VXLAN extensions to enable. The following extensions are supported:

    • gbp: VXLAN-GBP allows to transport the group policy context of a packet across the VXLAN tunnel to other network peers. See the field description of tun_gbp_id and tun_gbp_flags in ovs-ofctl(8) for additional information. (https://tools.ietf.org/html/draft-smith-vxlan-group-policy)

    gre, ipsec_gre, geneve, and vxlan interfaces support these options.

    Optional. Compute encapsulation header (either GRE or UDP) checksums on outgoing packets. Default is disabled, set to true to enable. Checksums present on incoming packets will be validated regardless of this setting.

    When using the upstream Linux kernel module, computation of checksums for geneve and vxlan requires Linux kernel version 4.0 or higher. gre supports checksums for all versions of Open vSwitch that support GRE. The out of tree kernel module distributed as part of OVS can compute all tunnel checksums on any kernel version that it is compatible with.

    This option is supported for ipsec_gre, but not useful because GRE checksums are weaker than, and redundant with, IPsec payload authentication.

    Only ipsec_gre interfaces support these options.

    Required for certificate authentication. A string containing the peer's certificate in PEM format. Additionally the host's certificate must be specified with the certificate option. Required for certificate authentication. The name of a PEM file containing a certificate that will be presented to the peer during authentication. Optional for certificate authentication. The name of a PEM file containing the private key associated with certificate. If certificate contains the private key, this option may be omitted. Required for pre-shared key authentication. Specifies a pre-shared key for authentication that must be identical on both sides of the tunnel.

    Only patch interfaces support these options.

    The of the for the other side of the patch. The named 's own peer option must specify this 's name. That is, the two patch interfaces must have reversed and peer values.

    Status information about interfaces attached to bridges, updated every 5 seconds. Not all interfaces have all of these properties; virtual interfaces don't have a link speed, for example. Non-applicable columns will have empty values.

    The administrative state of the physical network link.

    The observed state of the physical network link. This is ordinarily the link's carrier status. If the interface's is a bond configured for miimon monitoring, it is instead the network link's miimon status.

    The number of times Open vSwitch has observed the of this change.

    The negotiated speed of the physical network link. Valid values are positive integers greater than 0.

    The duplex mode of the physical network link.

    The MTU (maximum transmission unit); i.e. the largest amount of data that can fit into a single Ethernet frame. The standard Ethernet MTU is 1500 bytes. Some physical media and many kinds of virtual interfaces can be configured with higher MTUs.

    This column will be empty for an interface that does not have an MTU as, for example, some kinds of tunnels do not.

    Boolean value indicating LACP status for this interface. If true, this interface has current LACP information about its LACP partner. This information may be used to monitor the health of interfaces in a LACP enabled port. This column will be empty if LACP is not enabled. Key-value pairs that report port status. Supported status values are -dependent; some interfaces may not have a valid , for example. The name of the device driver controlling the network adapter. The version string of the device driver controlling the network adapter. The version string of the network adapter's firmware, if available. The source IP address used for an IPv4 tunnel end-point, such as gre. Egress interface for tunnels. Currently only relevant for tunnels on Linux systems, this column will show the name of the interface which is responsible for routing traffic destined for the configured . This could be an internal interface such as a bridge port. Whether carrier is detected on .

    Key-value pairs that report interface statistics. The current implementation updates these counters periodically. The update period is controlled by in the Open_vSwitch table. Future implementations may update them when an interface is created, when they are queried (e.g. using an OVSDB select operation), and just before an interface is deleted due to virtual interface hot-unplug or VM shutdown, and perhaps at other times, but not on any regular periodic basis.

    These are the same statistics reported by OpenFlow in its struct ofp_port_stats structure. If an interface does not support a given statistic, then that pair is omitted.

    Number of received packets. Number of received bytes. Number of transmitted packets. Number of transmitted bytes. Number of packets dropped by RX. Number of frame alignment errors. Number of packets with RX overrun. Number of CRC errors. Total number of receive errors, greater than or equal to the sum of the above. Number of packets dropped by TX. Number of collisions. Total number of transmit errors, greater than or equal to the sum of the above.

    These settings control ingress policing for packets received on this interface. On a physical interface, this limits the rate at which traffic is allowed into the system from the outside; on a virtual interface (one connected to a virtual machine), this limits the rate at which the VM is able to transmit.

    Policing is a simple form of quality-of-service that simply drops packets received in excess of the configured rate. Due to its simplicity, policing is usually less accurate and less effective than egress QoS (which is configured using the and tables).

    Policing is currently implemented only on Linux. The Linux implementation uses a simple ``token bucket'' approach:

    • The size of the bucket corresponds to . Initially the bucket is full.
    • Whenever a packet is received, its size (converted to tokens) is compared to the number of tokens currently in the bucket. If the required number of tokens are available, they are removed and the packet is forwarded. Otherwise, the packet is dropped.
    • Whenever it is not full, the bucket is refilled with tokens at the rate specified by .

    Policing interacts badly with some network protocols, and especially with fragmented IP packets. Suppose that there is enough network activity to keep the bucket nearly empty all the time. Then this token bucket algorithm will forward a single packet every so often, with the period depending on packet size and on the configured rate. All of the fragments of an IP packets are normally transmitted back-to-back, as a group. In such a situation, therefore, only one of these fragments will be forwarded and the rest will be dropped. IP does not provide any way for the intended recipient to ask for only the remaining fragments. In such a case there are two likely possibilities for what will happen next: either all of the fragments will eventually be retransmitted (as TCP will do), in which case the same problem will recur, or the sender will not realize that its packet has been dropped and data will simply be lost (as some UDP-based protocols will do). Either way, it is possible that no forward progress will ever occur.

    Maximum rate for data received on this interface, in kbps. Data received faster than this rate is dropped. Set to 0 (the default) to disable policing.

    Maximum burst size for data received on this interface, in kb. The default burst size if set to 0 is 8000 kbit. This value has no effect if is 0.

    Specifying a larger burst size lets the algorithm be more forgiving, which is important for protocols like TCP that react severely to dropped packets. The burst size should be at least the size of the interface's MTU. Specifying a value that is numerically at least as large as 80% of helps TCP come closer to achieving the full rate.

    BFD, defined in RFC 5880 and RFC 5881, allows point-to-point detection of connectivity failures by occasional transmission of BFD control messages. Open vSwitch implements BFD to serve as a more popular and standards compliant alternative to CFM.

    BFD operates by regularly transmitting BFD control messages at a rate negotiated independently in each direction. Each endpoint specifies the rate at which it expects to receive control messages, and the rate at which it is willing to transmit them. Open vSwitch uses a detection multiplier of three, meaning that an endpoint signals a connectivity fault if three consecutive BFD control messages fail to arrive. In the case of a unidirectional connectivity issue, the system not receiving BFD control messages signals the problem to its peer in the messages it transmits.

    The Open vSwitch implementation of BFD aims to comply faithfully with RFC 5880 requirements. Open vSwitch does not implement the optional Authentication or ``Echo Mode'' features.

    A controller sets up key-value pairs in the column to enable and configure BFD.

    True to enable BFD on this . If not specified, BFD will not be enabled by default. The shortest interval, in milliseconds, at which this BFD session offers to receive BFD control messages. The remote endpoint may choose to send messages at a slower rate. Defaults to 1000. The shortest interval, in milliseconds, at which this BFD session is willing to transmit BFD control messages. Messages will actually be transmitted at a slower rate if the remote endpoint is not willing to receive as quickly as specified. Defaults to 100. An alternate receive interval, in milliseconds, that must be greater than or equal to . The implementation switches from to when there is no obvious incoming data traffic at the interface, to reduce the CPU and bandwidth cost of monitoring an idle interface. This feature may be disabled by setting a value of 0. This feature is reset whenever or changes. When true, traffic received on the is used to indicate the capability of packet I/O. BFD control packets are still transmitted and received. At least one BFD control packet must be received every 100 * amount of time. Otherwise, even if traffic are received, the will be false. Set to true to notify the remote endpoint that traffic should not be forwarded to this system for some reason other than a connectivty failure on the interface being monitored. The typical underlying reason is ``concatenated path down,'' that is, that connectivity beyond the local system is down. Defaults to false. Set to true to make BFD accept only control messages with a tunnel key of zero. By default, BFD accepts control messages with any tunnel key. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used as source for transmitted BFD packets. The default is the mac address of the BFD enabled interface. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used as destination for transmitted BFD packets. The default is 00:23:20:00:00:01. Set to an Ethernet address in the form xx:xx:xx:xx:xx:xx to set the MAC used for checking the destination of received BFD packets. Packets with different destination MAC will not be considered as BFD packets. If not specified the destination MAC address of received BFD packets are not checked. Set to an IPv4 address to set the IP address used as source for transmitted BFD packets. The default is 169.254.1.1. Set to an IPv4 address to set the IP address used as destination for transmitted BFD packets. The default is 169.254.1.0.

    The switch sets key-value pairs in the column to report the status of BFD on this interface. When BFD is not enabled, with , the switch clears all key-value pairs from .

    Reports the state of the BFD session. The BFD session is fully healthy and negotiated if UP. Reports whether the BFD session believes this may be used to forward traffic. Typically this means the local session is signaling UP, and the remote system isn't signaling a problem such as concatenated path down. A diagnostic code specifying the local system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Reports the state of the remote endpoint's BFD session. A diagnostic code specifying the remote system's reason for the last change in session state. The error messages are defined in section 4.1 of [RFC 5880]. Counts the number of flaps since start. A flap is considered as a change of the value.

    802.1ag Connectivity Fault Management (CFM) allows a group of Maintenance Points (MPs) called a Maintenance Association (MA) to detect connectivity problems with each other. MPs within a MA should have complete and exclusive interconnectivity. This is verified by occasionally broadcasting Continuity Check Messages (CCMs) at a configurable transmission interval.

    According to the 802.1ag specification, each Maintenance Point should be configured out-of-band with a list of Remote Maintenance Points it should have connectivity to. Open vSwitch differs from the specification in this area. It simply assumes the link is faulted if no Remote Maintenance Points are reachable, and considers it not faulted otherwise.

    When operating over tunnels which have no in_key, or an in_key of flow. CFM will only accept CCMs with a tunnel key of zero.

    A Maintenance Point ID (MPID) uniquely identifies each endpoint within a Maintenance Association. The MPID is used to identify this endpoint to other Maintenance Points in the MA. Each end of a link being monitored should have a different MPID. Must be configured to enable CFM on this .

    According to the 802.1ag specification, MPIDs can only range between [1, 8191]. However, extended mode (see ) supports eight byte MPIDs.

    Counts the number of cfm fault flapps since boot. A flap is considered to be a change of the value.

    Indicates a connectivity fault triggered by an inability to receive heartbeats from any remote endpoint. When a fault is triggered on s participating in bonds, they will be disabled.

    Faults can be triggered for several reasons. Most importantly they are triggered when no CCMs are received for a period of 3.5 times the transmission interval. Faults are also triggered when any CCMs indicate that a Remote Maintenance Point is not receiving CCMs but able to send them. Finally, a fault is triggered if a CCM is received which indicates unexpected configuration. Notably, this case arises when a CCM is received which advertises the local MPID.

    Indicates a CFM fault was triggered due to a lack of CCMs received on the . Indicates a CFM fault was triggered due to the reception of a CCM with the RDI bit flagged. Endpoints set the RDI bit in their CCMs when they are not receiving CCMs themselves. This typically indicates a unidirectional connectivity failure. Indicates a CFM fault was triggered due to the reception of a CCM with a MAID other than the one Open vSwitch uses. CFM broadcasts are tagged with an identification number in addition to the MPID called the MAID. Open vSwitch only supports receiving CCM broadcasts tagged with the MAID it uses internally. Indicates a CFM fault was triggered due to the reception of a CCM advertising the same MPID configured in the column of this . This may indicate a loop in the network. Indicates a CFM fault was triggered because the CFM module received CCMs from more remote endpoints than it can keep track of. Indicates a CFM fault was manually triggered by an administrator using an ovs-appctl command. Indicates a CFM fault was triggered due to the reception of a CCM frame having an invalid interval.

    When in extended mode, indicates the operational state of the remote endpoint as either up or down. See .

    Indicates the health of the interface as a percentage of CCM frames received over 21 s. The health of an interface is undefined if it is communicating with more than one . It reduces if healthy heartbeats are not received at the expected rate, and gradually improves as healthy heartbeats are received at the desired rate. Every 21 s, the health of the interface is refreshed.

    As mentioned above, the faults can be triggered for several reasons. The link health will deteriorate even if heartbeats are received but they are reported to be unhealthy. An unhealthy heartbeat in this context is a heartbeat for which either some fault is set or is out of sequence. The interface health can be 100 only on receiving healthy heartbeats at the desired rate.

    When CFM is properly configured, Open vSwitch will occasionally receive CCM broadcasts. These broadcasts contain the MPID of the sending Maintenance Point. The list of MPIDs from which this is receiving broadcasts from is regularly collected and written to this column.

    The interval, in milliseconds, between transmissions of CFM heartbeats. Three missed heartbeat receptions indicate a connectivity fault.

    In standard operation only intervals of 3, 10, 100, 1,000, 10,000, 60,000, or 600,000 ms are supported. Other values will be rounded down to the nearest value on the list. Extended mode (see ) supports any interval up to 65,535 ms. In either mode, the default is 1000 ms.

    We do not recommend using intervals less than 100 ms.

    When true, the CFM module operates in extended mode. This causes it to use a nonstandard destination address to avoid conflicting with compliant implementations which may be running concurrently on the network. Furthermore, extended mode increases the accuracy of the cfm_interval configuration parameter by breaking wire compatibility with 802.1ag compliant implementations. And extended mode allows eight byte MPIDs. Defaults to false.

    When true, and is true, the CFM module operates in demand mode. When in demand mode, traffic received on the is used to indicate liveness. CCMs are still transmitted and received. At least one CCM must be received every 100 * amount of time. Otherwise, even if traffic are received, the CFM module will raise the connectivity fault.

    Demand mode has a couple of caveats:

    • To ensure that ovs-vswitchd has enough time to pull statistics from the datapath, the fault detection interval is set to 3.5 * MAX(, 500) ms.
    • To avoid ambiguity, demand mode disables itself when there are multiple remote maintenance points.
    • If the is heavily congested, CCMs containing the status may be dropped causing changes in the operational state to be delayed. Similarly, if CCMs containing the RDI bit are not received, unidirectional link failures may not be detected.

    When down, the CFM module marks all CCMs it generates as operationally down without triggering a fault. This allows remote maintenance points to choose not to forward traffic to the on which this CFM module is running. Currently, in Open vSwitch, the opdown bit of CCMs affects s participating in bonds, and the bundle OpenFlow action. This setting is ignored when CFM is not in extended mode. Defaults to up. When set, the CFM module will apply a VLAN tag to all CCMs it generates with the given value. May be the string random in which case each CCM will be tagged with a different randomly generated VLAN. When set, the CFM module will apply a VLAN tag to all CCMs it generates with the given PCP value, the VLAN ID of the tag is governed by the value of . If is unset, a VLAN ID of zero is used.
    The LACP port ID of this . Port IDs are used in LACP negotiations to identify individual ports participating in a bond. The LACP port priority of this . In LACP negotiations s with numerically lower priorities are preferred for aggregation. The LACP aggregation key of this . s with different aggregation keys may not be active within a given at the same time.

    These key-value pairs specifically apply to an interface that represents a virtual Ethernet interface connected to a virtual machine. These key-value pairs should not be present for other types of interfaces. Keys whose names end in -uuid have values that uniquely identify the entity in question. For a Citrix XenServer hypervisor, these values are UUIDs in RFC 4122 format. Other hypervisors may use other formats.

    The MAC address programmed into the ``virtual hardware'' for this interface, in the form xx:xx:xx:xx:xx:xx. For Citrix XenServer, this is the value of the MAC field in the VIF record for this interface. A system-unique identifier for the interface. On XenServer, this will commonly be the same as .

    Hypervisors may sometimes have more than one interface associated with a given , only one of which is actually in use at a given time. For example, in some circumstances XenServer has both a ``tap'' and a ``vif'' interface for a single , but only uses one of them at a time. A hypervisor that behaves this way must mark the currently in use interface active and the others inactive. A hypervisor that never has more than one interface for a given may mark that interface active or omit entirely.

    During VM migration, a given might transiently be marked active on two different hypervisors. That is, active means that this is the active instance within a single hypervisor, not in a broader scope. There is one exception: some hypervisors support ``migration'' from a given hypervisor to itself (most often for test purposes). During such a ``migration,'' two instances of a single might both be briefly marked active on a single hypervisor.

    The virtual interface associated with this interface. The virtual network to which this interface is attached. The VM to which this interface belongs. On XenServer, this will be the same as . The VM to which this interface belongs.

    The ``VLAN splinters'' feature increases Open vSwitch compatibility with buggy network drivers in old versions of Linux that do not properly support VLANs when VLAN devices are not used, at some cost in memory and performance.

    When VLAN splinters are enabled on a particular interface, Open vSwitch creates a VLAN device for each in-use VLAN. For sending traffic tagged with a VLAN on the interface, it substitutes the VLAN device. Traffic received on the VLAN device is treated as if it had been received on the interface on the particular VLAN.

    VLAN splinters consider a VLAN to be in use if:

    • The VLAN is the value in any record.
    • The VLAN is listed within the column of the record of an interface on which VLAN splinters are enabled. An empty does not influence the in-use VLANs: creating 4,096 VLAN devices is impractical because it will exceed the current 1,024 port per datapath limit.
    • An OpenFlow flow within any bridge matches the VLAN.

    The same set of in-use VLANs applies to every interface on which VLAN splinters are enabled. That is, the set is not chosen separately for each interface but selected once as the union of all in-use VLANs based on the rules above.

    It does not make sense to enable VLAN splinters on an interface for an access port, or on an interface that is not a physical port.

    VLAN splinters are deprecated. When broken device drivers are no longer in widespread use, we will delete this feature.

    Set to true to enable VLAN splinters on this interface. Defaults to false.

    VLAN splinters increase kernel and userspace memory overhead, so do not use them unless they are needed.

    VLAN splinters do not support 802.1p priority tags. Received priorities will appear to be 0, regardless of their actual values, and priorities on transmitted packets will also be cleared to 0.

    Auto Attach configuration for a particular interface.

    True to enable LLDP on this . If not specified, LLDP will be disabled by default.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a particular OpenFlow table.

    The table's name. Set this column to change the name that controllers will receive when they request table statistics, e.g. ovs-ofctl dump-tables. The name does not affect switch behavior.

    Open vSwitch supports limiting the number of flows that may be installed in a flow table, via the column. When adding a flow would exceed this limit, by default Open vSwitch reports an error, but there are two ways to configure Open vSwitch to instead delete (``evict'') a flow to make room for the new one:

    • Set the column to evict.
    • Send an OpenFlow 1.4+ ``table mod request'' to enable eviction for the flow table (e.g. ovs-ofctl -O OpenFlow14 mod-table br0 0 evict to enable eviction on flow table 0 of bridge br0).

    When a flow must be evicted due to overflow, the flow to evict is chosen through an approximation of the following algorithm. This algorithm is used regardless of how eviction was enabled:

    1. Divide the flows in the table into groups based on the values of the fields or subfields specified in the column, so that all of the flows in a given group have the same values for those fields. If a flow does not specify a given field, that field's value is treated as 0. If is empty, then all of the flows in the flow table are treated as a single group.
    2. Consider the flows in the largest group, that is, the group that contains the greatest number of flows. If two or more groups all have the same largest number of flows, consider the flows in all of those groups.
    3. If the flows under consideration have different importance values, eliminate from consideration any flows except those with the lowest importance. (``Importance,'' a 16-bit integer value attached to each flow, was introduced in OpenFlow 1.4. Flows inserted with older versions of OpenFlow always have an importance of 0.)
    4. Among the flows under consideration, choose the flow that expires soonest for eviction.

    The eviction process only considers flows that have an idle timeout or a hard timeout. That is, eviction never deletes permanent flows. (Permanent flows do count against .)

    If set, limits the number of flows that may be added to the table. Open vSwitch may limit the number of flows in a table for other reasons, e.g. due to hardware limitations or for resource availability or performance reasons.

    Controls the switch's behavior when an OpenFlow flow table modification request would add flows in excess of . The supported values are:

    refuse
    Refuse to add the flow or flows. This is also the default policy when is unset.
    evict
    Delete a flow chosen according to the algorithm described above.

    When is evict, this controls how flows are chosen for eviction when the flow table would otherwise exceed flows. Its value is a set of NXM fields or sub-fields, each of which takes one of the forms field[] or field[start..end], e.g. NXM_OF_IN_PORT[]. Please see nicira-ext.h for a complete list of NXM field names.

    Open vSwitch ignores any invalid or unknown field specifications.

    When eviction is not enabled, via or an OpenFlow 1.4+ ``table mod,'' this column has no effect.

    This string set specifies which fields should be used for address prefix tracking. Prefix tracking allows the classifier to skip rules with longer than necessary prefixes, resulting in better wildcarding for datapath flows.

    Prefix tracking may be beneficial when a flow table contains matches on IP address fields with different prefix lengths. For example, when a flow table contains IP address matches on both full addresses and proper prefixes, the full address matches will typically cause the datapath flow to un-wildcard the whole address field (depending on flow entry priorities). In this case each packet with a different address gets handed to the userspace for flow processing and generates its own datapath flow. With prefix tracking enabled for the address field in question packets with addresses matching shorter prefixes would generate datapath flows where the irrelevant address bits are wildcarded, allowing the same datapath flow to handle all the packets within the prefix in question. In this case many userspace upcalls can be avoided and the overall performance can be better.

    This is a performance optimization only, so packets will receive the same treatment with or without prefix tracking.

    The supported fields are: tun_id, tun_src, tun_dst, nw_src, nw_dst (or aliases ip_src and ip_dst), ipv6_src, and ipv6_dst. (Using this feature for tun_id would only make sense if the tunnel IDs have prefix structure similar to IP addresses.)

    By default, the prefixes=ip_dst,ip_src are used on each flow table. This instructs the flow classifier to track the IP destination and source addresses used by the rules in this specific flow table.

    The keyword none is recognized as an explicit override of the default values, causing no prefix fields to be tracked.

    To set the prefix fields, the flow table record needs to exist:

    ovs-vsctl set Bridge br0 flow_tables:0=@N1 -- --id=@N1 create Flow_Table name=table0
    Creates a flow table record for the OpenFlow table number 0.
    ovs-vsctl set Flow_Table table0 prefixes=ip_dst,ip_src
    Enables prefix tracking for IP source and destination address fields.

    There is a maximum number of fields that can be enabled for any one flow table. Currently this limit is 3.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Quality of Service (QoS) configuration for each Port that references it.

    The type of QoS to implement. The currently defined types are listed below:

    linux-htb
    Linux ``hierarchy token bucket'' classifier. See tc-htb(8) (also at http://linux.die.net/man/8/tc-htb) and the HTB manual (http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm) for information on how this classifier works and how to configure it.
    linux-hfsc
    Linux "Hierarchical Fair Service Curve" classifier. See http://linux-ip.net/articles/hfsc.en/ for information on how this classifier works.
    linux-sfq
    Linux ``Stochastic Fairness Queueing'' classifier. See tc-sfq(8) (also at http://linux.die.net/man/8/tc-sfq) for information on how this classifier works.
    linux-codel
    Linux ``Controlled Delay'' classifier. See tc-codel(8) (also at http://man7.org/linux/man-pages/man8/tc-codel.8.html) for information on how this classifier works.
    linux-fq_codel
    Linux ``Fair Queuing with Controlled Delay'' classifier. See tc-fq_codel(8) (also at http://man7.org/linux/man-pages/man8/tc-fq_codel.8.html) for information on how this classifier works.

    A map from queue numbers to records. The supported range of queue numbers depend on . The queue numbers are the same as the queue_id used in OpenFlow in struct ofp_action_enqueue and other structures.

    Queue 0 is the ``default queue.'' It is used by OpenFlow output actions when no specific queue has been set. When no configuration for queue 0 is present, it is automatically configured as if a record with empty and columns had been specified. (Before version 1.6, Open vSwitch would leave queue 0 unconfigured in this case. With some queuing disciplines, this dropped all packets destined for the default queue.)

    The linux-htb and linux-hfsc classes support the following key-value pair:

    Maximum rate shared by all queued traffic, in bit/s. Optional. If not specified, for physical interfaces, the default is the link rate. For other interfaces or if the link rate cannot be determined, the default is currently 100 Mbps.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A configuration for a port output queue, used in configuring Quality of Service (QoS) features. May be referenced by column in table.

    If set, Open vSwitch will mark all traffic egressing this with the given DSCP bits. Traffic egressing the default is only marked if it was explicitly selected as the at the time the packet was output. If unset, the DSCP bits of traffic egressing this will remain unchanged.

    linux-htb may use queue_ids less than 61440. It has the following key-value pairs defined.

    Minimum guaranteed bandwidth, in bit/s. Maximum allowed bandwidth, in bit/s. Optional. If specified, the queue's rate will not be allowed to exceed the specified value, even if excess bandwidth is available. If unspecified, defaults to no limit. Burst size, in bits. This is the maximum amount of ``credits'' that a queue can accumulate while it is idle. Optional. Details of the linux-htb implementation require a minimum burst size, so a too-small burst will be silently ignored. A queue with a smaller priority will receive all the excess bandwidth that it can use before a queue with a larger value receives any. Specific priority values are unimportant; only relative ordering matters. Defaults to 0 if unspecified.

    linux-hfsc may use queue_ids less than 61440. It has the following key-value pairs defined.

    Minimum guaranteed bandwidth, in bit/s. Maximum allowed bandwidth, in bit/s. Optional. If specified, the queue's rate will not be allowed to exceed the specified value, even if excess bandwidth is available. If unspecified, defaults to no limit.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A port mirror within a .

    A port mirror configures a bridge to send selected frames to special ``mirrored'' ports, in addition to their normal destinations. Mirroring traffic may also be referred to as SPAN or RSPAN, depending on how the mirrored traffic is sent.

    When a packet enters an Open vSwitch bridge, it becomes eligible for mirroring based on its ingress port and VLAN. As the packet travels through the flow tables, each time it is output to a port, it becomes eligible for mirroring based on the egress port and VLAN. In Open vSwitch 2.5 and later, mirroring occurs just after a packet first becomes eligible, using the packet as it exists at that point; in Open vSwitch 2.4 and earlier, mirroring occurs only after a packet has traversed all the flow tables, using the original packet as it entered the bridge. This makes a difference only when the flow table modifies the packet: in Open vSwitch 2.4, the modifications are never visible to mirrors, whereas in Open vSwitch 2.5 and later modifications made before the first output that makes it eligible for mirroring to a particular destination are visible.

    A packet that enters an Open vSwitch bridge is mirrored to a particular destination only once, even if it is eligible for multiple reasons. For example, a packet would be mirrored to a particular only once, even if it is selected for mirroring to that port by and in the same or different records.

    Arbitrary identifier for the .

    To be selected for mirroring, a given packet must enter or leave the bridge through a selected port and it must also be in one of the selected VLANs.

    If true, every packet arriving or departing on any port is selected for mirroring. Ports on which departing packets are selected for mirroring. Ports on which arriving packets are selected for mirroring. VLANs on which packets are selected for mirroring. An empty set selects packets on all VLANs.

    These columns are mutually exclusive. Exactly one of them must be nonempty.

    Output port for selected packets, if nonempty.

    Specifying a port for mirror output reserves that port exclusively for mirroring. No frames other than those selected for mirroring via this column will be forwarded to the port, and any frames received on the port will be discarded.

    The output port may be any kind of port supported by Open vSwitch. It may be, for example, a physical port (sometimes called SPAN) or a GRE tunnel.

    Output VLAN for selected packets, if nonempty.

    The frames will be sent out all ports that trunk , as well as any ports with implicit VLAN . When a mirrored frame is sent out a trunk port, the frame's VLAN tag will be set to , replacing any existing tag; when it is sent out an implicit VLAN port, the frame will not be tagged. This type of mirroring is sometimes called RSPAN.

    See the documentation for in the table for a list of destination MAC addresses which will not be mirrored to a VLAN to avoid confusing switches that interpret the protocols that they represent.

    Please note: Mirroring to a VLAN can disrupt a network that contains unmanaged switches. Consider an unmanaged physical switch with two ports: port 1, connected to an end host, and port 2, connected to an Open vSwitch configured to mirror received packets into VLAN 123 on port 2. Suppose that the end host sends a packet on port 1 that the physical switch forwards to port 2. The Open vSwitch forwards this packet to its destination and then reflects it back on port 2 in VLAN 123. This reflected packet causes the unmanaged physical switch to replace the MAC learning table entry, which correctly pointed to port 1, with one that incorrectly points to port 2. Afterward, the physical switch will direct packets destined for the end host to the Open vSwitch on port 2, instead of to the end host on port 1, disrupting connectivity. If mirroring to a VLAN is desired in this scenario, then the physical switch must be replaced by one that learns Ethernet addresses on a per-VLAN basis. In addition, learning should be disabled on the VLAN containing mirrored traffic. If this is not done then intermediate switches will learn the MAC address of each end host from the mirrored traffic. If packets being sent to that end host are also mirrored, then they will be dropped since the switch will attempt to send them out the input port. Disabling learning for the VLAN will cause the switch to correctly send the packet out all ports configured for that VLAN. If Open vSwitch is being used as an intermediate switch, learning can be disabled by adding the mirrored VLAN to in the appropriate table or tables.

    Mirroring to a GRE tunnel has fewer caveats than mirroring to a VLAN and should generally be preferred.

    Key-value pairs that report mirror statistics. The update period is controlled by in the Open_vSwitch table.

    Number of packets transmitted through this mirror. Number of bytes transmitted through this mirror.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    An OpenFlow controller.

    Open vSwitch supports two kinds of OpenFlow controllers:

    Primary controllers

    This is the kind of controller envisioned by the OpenFlow 1.0 specification. Usually, a primary controller implements a network policy by taking charge of the switch's flow table.

    Open vSwitch initiates and maintains persistent connections to primary controllers, retrying the connection each time it fails or drops. The column in the table applies to primary controllers.

    Open vSwitch permits a bridge to have any number of primary controllers. When multiple controllers are configured, Open vSwitch connects to all of them simultaneously. Because OpenFlow 1.0 does not specify how multiple controllers coordinate in interacting with a single switch, more than one primary controller should be specified only if the controllers are themselves designed to coordinate with each other. (The Nicira-defined NXT_ROLE OpenFlow vendor extension may be useful for this.)

    Service controllers

    These kinds of OpenFlow controller connections are intended for occasional support and maintenance use, e.g. with ovs-ofctl. Usually a service controller connects only briefly to inspect or modify some of a switch's state.

    Open vSwitch listens for incoming connections from service controllers. The service controllers initiate and, if necessary, maintain the connections from their end. The column in the table does not apply to service controllers.

    Open vSwitch supports configuring any number of service controllers.

    The determines the type of controller.

    Connection method for controller.

    The following connection methods are currently supported for primary controllers:

    ssl:ip[:port]

    The specified SSL port on the host at the given ip, which must be expressed as an IP address (not a DNS name). The column in the table must point to a valid SSL configuration when this form is used.

    If port is not specified, it defaults to 6653.

    SSL support is an optional feature that is not always built as part of Open vSwitch.

    tcp:ip[:port]

    The specified TCP port on the host at the given ip, which must be expressed as an IP address (not a DNS name), where ip can be IPv4 or IPv6 address. If ip is an IPv6 address, wrap it in square brackets, e.g. tcp:[::1]:6653.

    If port is not specified, it defaults to 6653.

    The following connection methods are currently supported for service controllers:

    pssl:[port][:ip]

    Listens for SSL connections on the specified TCP port. If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address (either IPv4 or IPv6). If ip is an IPv6 address, wrap it in square brackets, e.g. pssl:6653:[::1].

    If port is not specified, it defaults to 6653. If ip is not specified then it listens only on IPv4 (but not IPv6) addresses. The column in the table must point to a valid SSL configuration when this form is used.

    If port is not specified, it currently to 6653.

    SSL support is an optional feature that is not always built as part of Open vSwitch.

    ptcp:[port][:ip]

    Listens for connections on the specified TCP port. If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address (either IPv4 or IPv6). If ip is an IPv6 address, wrap it in square brackets, e.g. ptcp:6653:[::1]. If ip is not specified then it listens only on IPv4 addresses.

    If port is not specified, it defaults to 6653.

    When multiple controllers are configured for a single bridge, the values must be unique. Duplicate values yield unspecified results.

    If it is specified, this setting must be one of the following strings that describes how Open vSwitch contacts this OpenFlow controller over the network:

    in-band
    In this mode, this controller's OpenFlow traffic travels over the bridge associated with the controller. With this setting, Open vSwitch allows traffic to and from the controller regardless of the contents of the OpenFlow flow table. (Otherwise, Open vSwitch would never be able to connect to the controller, because it did not have a flow to enable it.) This is the most common connection mode because it is not necessary to maintain two independent networks.
    out-of-band
    In this mode, OpenFlow traffic uses a control network separate from the bridge associated with this controller, that is, the bridge does not use any of its own network devices to communicate with the controller. The control network must be configured separately, before or after ovs-vswitchd is started.

    If not specified, the default is implementation-specific.

    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to controller before sending an inactivity probe message. If Open vSwitch does not communicate with the controller for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, Open vSwitch assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes.

    OpenFlow switches send certain messages to controllers spontanenously, that is, not in response to any request from the controller. These messages are called ``asynchronous messages.'' These columns allow asynchronous messages to be limited or disabled to ensure the best use of network resources.

    The OpenFlow protocol enables asynchronous messages at time of connection establishment, which means that a controller can receive asynchronous messages, potentially many of them, even if it turns them off immediately after connecting. Set this column to false to change Open vSwitch behavior to disable, by default, all asynchronous messages. The controller can use the NXT_SET_ASYNC_CONFIG Nicira extension to OpenFlow to turn on any messages that it does want to receive, if any.

    A switch can forward packets to a controller over the OpenFlow protocol. Forwarding packets this way at too high a rate can overwhelm a controller, frustrate use of the OpenFlow connection for other purposes, increase the latency of flow setup, and use an unreasonable amount of bandwidth. Therefore, Open vSwitch supports limiting the rate of packet forwarding to a controller.

    There are two main reasons in OpenFlow for a packet to be sent to a controller: either the packet ``misses'' in the flow table, that is, there is no matching flow, or a flow table action says to send the packet to the controller. Open vSwitch limits the rate of each kind of packet separately at the configured rate. Therefore, the actual rate that packets are sent to the controller can be up to twice the configured rate, when packets are sent for both reasons.

    This feature is specific to forwarding packets over an OpenFlow connection. It is not general-purpose QoS. See the table for quality of service configuration, and in the table for ingress policing configuration.

    The maximum rate at which the switch will forward packets to the OpenFlow controller, in packets per second. If no value is specified, rate limiting is disabled.

    When a high rate triggers rate-limiting, Open vSwitch queues packets to the controller for each port and transmits them to the controller at the configured rate. This value limits the number of queued packets. Ports on a bridge share the packet queue fairly.

    This value has no effect unless is configured. The current default when this value is not specified is one-quarter of , meaning that queuing can delay forwarding a packet to the controller by up to 250 ms.

    These values report the effects of rate limiting. Their values are relative to establishment of the most recent OpenFlow connection, or since rate limiting was enabled, whichever happened more recently. Each consists of two values, one with TYPE replaced by miss for rate limiting flow table misses, and the other with TYPE replaced by action for rate limiting packets sent by OpenFlow actions.

    These statistics are reported only when controller rate limiting is enabled.

    Number of packets sent directly to the controller, without queuing, because the rate did not exceed the configured maximum. Number of packets added to the queue to send later. Number of packets added to the queue that were later dropped due to overflow. This value is less than or equal to . Number of packets currently queued. The other statistics increase monotonically, but this one fluctuates between 0 and the as conditions change.

    These values are considered only in in-band control mode (see ).

    When multiple controllers are configured on a single bridge, there should be only one set of unique values in these columns. If different values are set for these columns in different controllers, the effect is unspecified.

    The IP address to configure on the local port, e.g. 192.168.0.123. If this value is unset, then and are ignored. The IP netmask to configure on the local port, e.g. 255.255.255.0. If is set but this value is unset, then the default is chosen based on whether the IP address is class A, B, or C. The IP address of the gateway to configure on the local port, as a string, e.g. 192.168.0.1. Leave this column unset if this network has no gateway.
    true if currently connected to this controller, false otherwise.

    The level of authority this controller has on the associated bridge. Possible values are:

    other
    Allows the controller access to all OpenFlow features.
    master
    Equivalent to other, except that there may be at most one master controller at a time. When a controller configures itself as master, any existing master is demoted to the slave role.
    slave
    Allows the controller read-only access to OpenFlow features. Attempts to modify the flow table will be rejected with an error. Slave controllers do not receive OFPT_PACKET_IN or OFPT_FLOW_REMOVED messages, but they do receive OFPT_PORT_STATUS messages.
    A human-readable description of the last error on the connection to the controller; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the controller:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this controller last successfully connected to the switch (in seconds). Value is empty if controller has never successfully connected. The amount of time since this controller last disconnected from the switch (in seconds). Value is empty if controller has never disconnected.

    Additional configuration for a connection between the controller and the Open vSwitch.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the controller and the Open vSwitch. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for a database connection to an Open vSwitch database (OVSDB) client.

    This table primarily configures the Open vSwitch database (ovsdb-server), not the Open vSwitch switch (ovs-vswitchd). The switch does read the table to determine what connections should be treated as in-band.

    The Open vSwitch database server can initiate and maintain active connections to remote clients. It can also listen for database connections.

    Connection method for managers.

    The following connection methods are currently supported:

    ssl:ip[:port]

    The specified SSL port on the host at the given ip, which must be expressed as an IP address (not a DNS name). The column in the table must point to a valid SSL configuration when this form is used.

    If port is not specified, it defaults to 6640.

    SSL support is an optional feature that is not always built as part of Open vSwitch.

    tcp:ip[:port]

    The specified TCP port on the host at the given ip, which must be expressed as an IP address (not a DNS name), where ip can be IPv4 or IPv6 address. If ip is an IPv6 address, wrap it in square brackets, e.g. tcp:[::1]:6640.

    If port is not specified, it defaults to 6640.

    pssl:[port][:ip]

    Listens for SSL connections on the specified TCP port. Specify 0 for port to have the kernel automatically choose an available port. If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address (either IPv4 or IPv6 address). If ip is an IPv6 address, wrap in square brackets, e.g. pssl:6640:[::1]. If ip is not specified then it listens only on IPv4 (but not IPv6) addresses. The column in the table must point to a valid SSL configuration when this form is used.

    If port is not specified, it defaults to 6640.

    SSL support is an optional feature that is not always built as part of Open vSwitch.

    ptcp:[port][:ip]

    Listens for connections on the specified TCP port. Specify 0 for port to have the kernel automatically choose an available port. If ip, which must be expressed as an IP address (not a DNS name), is specified, then connections are restricted to the specified local IP address (either IPv4 or IPv6 address). If ip is an IPv6 address, wrap it in square brackets, e.g. ptcp:6640:[::1]. If ip is not specified then it listens only on IPv4 addresses.

    If port is not specified, it defaults to 6640.

    When multiple managers are configured, the values must be unique. Duplicate values yield unspecified results.

    If it is specified, this setting must be one of the following strings that describes how Open vSwitch contacts this OVSDB client over the network:

    in-band
    In this mode, this connection's traffic travels over a bridge managed by Open vSwitch. With this setting, Open vSwitch allows traffic to and from the client regardless of the contents of the OpenFlow flow table. (Otherwise, Open vSwitch would never be able to connect to the client, because it did not have a flow to enable it.) This is the most common connection mode because it is not necessary to maintain two independent networks.
    out-of-band
    In this mode, the client's traffic uses a control network separate from that managed by Open vSwitch, that is, Open vSwitch does not use any of its own network devices to communicate with the client. The control network must be configured separately, before or after ovs-vswitchd is started.

    If not specified, the default is implementation-specific.

    Maximum number of milliseconds to wait between connection attempts. Default is implementation-specific. Maximum number of milliseconds of idle time on connection to the client before sending an inactivity probe message. If Open vSwitch does not communicate with the client for the specified number of seconds, it will send a probe. If a response is not received for the same additional amount of time, Open vSwitch assumes the connection has been broken and attempts to reconnect. Default is implementation-specific. A value of 0 disables inactivity probes. true if currently connected to this manager, false otherwise. A human-readable description of the last error on the connection to the manager; i.e. strerror(errno). This key will exist only if an error has occurred.

    The state of the connection to the manager:

    VOID
    Connection is disabled.
    BACKOFF
    Attempting to reconnect at an increasing period.
    CONNECTING
    Attempting to connect.
    ACTIVE
    Connected, remote host responsive.
    IDLE
    Connection is idle. Waiting for response to keep-alive.

    These values may change in the future. They are provided only for human consumption.

    The amount of time since this manager last successfully connected to the database (in seconds). Value is empty if manager has never successfully connected. The amount of time since this manager last disconnected from the database (in seconds). Value is empty if manager has never disconnected. Space-separated list of the names of OVSDB locks that the connection holds. Omitted if the connection does not hold any locks. Space-separated list of the names of OVSDB locks that the connection is currently waiting to acquire. Omitted if the connection is not waiting for any locks. Space-separated list of the names of OVSDB locks that the connection has had stolen by another OVSDB client. Omitted if no locks have been stolen from this connection.

    When specifies a connection method that listens for inbound connections (e.g. ptcp: or pssl:) and more than one connection is actually active, the value is the number of active connections. Otherwise, this key-value pair is omitted.

    When multiple connections are active, status columns and key-value pairs (other than this one) report the status of one arbitrarily chosen connection.

    When is ptcp: or pssl:, this is the TCP port on which the OVSDB server is listening. (This is is particularly useful when specifies a port of 0, allowing the kernel to choose any available port.)

    Additional configuration for a connection between the manager and the Open vSwitch Database.

    The Differentiated Service Code Point (DSCP) is specified using 6 bits in the Type of Service (TOS) field in the IP header. DSCP provides a mechanism to classify the network traffic and provide Quality of Service (QoS) on IP networks. The DSCP value specified here is used when establishing the connection between the manager and the Open vSwitch. If no value is specified, a default value of 48 is chosen. Valid DSCP values must be in the range 0 to 63.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.
    A NetFlow target. NetFlow is a protocol that exports a number of details about terminating IP flows, such as the principals involved and duration. NetFlow targets in the form ip:port. The ip must be specified numerically, not as a DNS name. Engine ID to use in NetFlow messages. Defaults to datapath index if not specified. Engine type to use in NetFlow messages. Defaults to datapath index if not specified.

    The interval at which NetFlow records are sent for flows that are still active, in seconds. A value of 0 requests the default timeout (currently 600 seconds); a value of -1 disables active timeouts.

    The NetFlow passive timeout, for flows that become inactive, is not configurable. It will vary depending on the Open vSwitch version, the forms and contents of the OpenFlow flow tables, CPU and memory usage, and network activity. A typical passive timeout is about a second.

    If this column's value is false, the ingress and egress interface fields of NetFlow flow records are derived from OpenFlow port numbers. When it is true, the 7 most significant bits of these fields will be replaced by the least significant 7 bits of the engine id. This is useful because many NetFlow collectors do not expect multiple switches to be sending messages from the same host, so they do not store the engine information which could be used to disambiguate the traffic.

    When this option is enabled, a maximum of 508 ports are supported.

    The overall purpose of these columns is described under Common Columns at the beginning of this document.
    SSL configuration for an Open_vSwitch. Name of a PEM file containing the private key used as the switch's identity for SSL connections to the controller. Name of a PEM file containing a certificate, signed by the certificate authority (CA) used by the controller and manager, that certifies the switch's private key, identifying a trustworthy switch. Name of a PEM file containing the CA certificate used to verify that the switch is connected to a trustworthy controller. If set to true, then Open vSwitch will attempt to obtain the CA certificate from the controller on its first SSL connection and save it to the named PEM file. If it is successful, it will immediately drop the connection and reconnect, and from then on all SSL connections must be authenticated by a certificate signed by the CA certificate thus obtained. This option exposes the SSL connection to a man-in-the-middle attack obtaining the initial CA certificate. It may still be useful for bootstrapping. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A set of sFlow(R) targets. sFlow is a protocol for remote monitoring of switches.

    Name of the network device whose IP address should be reported as the ``agent address'' to collectors. If not specified, the agent device is figured from the first target address and the routing table. If the routing table does not contain a route to the target, the IP address defaults to the in the collector's . If an agent IP address cannot be determined any of these ways, sFlow is disabled. Number of bytes of a sampled packet to send to the collector. If not specified, the default is 128 bytes. Polling rate in seconds to send port statistics to the collector. If not specified, defaults to 30 seconds. Rate at which packets should be sampled and sent to the collector. If not specified, defaults to 400, which means one out of 400 packets, on average, will be sent to the collector. sFlow targets in the form ip:port. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Configuration for sending packets to IPFIX collectors.

    IPFIX is a protocol that exports a number of details about flows. The IPFIX implementation in Open vSwitch samples packets at a configurable rate, extracts flow information from those packets, optionally caches and aggregates the flow information, and sends the result to one or more collectors.

    IPFIX in Open vSwitch can be configured two different ways:

    • With per-bridge sampling, Open vSwitch performs IPFIX sampling automatically on all packets that pass through a bridge. To configure per-bridge sampling, create an record and point a table's column to it. The table is not used for per-bridge sampling.
    • With flow-based sampling, sample actions in the OpenFlow flow table drive IPFIX sampling. See ovs-ofctl(8) for a description of the sample action.

      Flow-based sampling also requires database configuration: create a record that describes the IPFIX configuration and a record that points to the whose flow table holds the sample actions and to record. The in the table is not used for flow-based sampling.

    IPFIX target collectors in the form ip:port. The maximum period in seconds for which an IPFIX flow record is cached and aggregated before being sent. If not specified, defaults to 0. If 0, caching is disabled. The maximum number of IPFIX flow records that can be cached at a time. If not specified, defaults to 0. If 0, caching is disabled.

    These values affect only per-bridge sampling. See above for a description of the differences between per-bridge and flow-based sampling.

    The rate at which packets should be sampled and sent to each target collector. If not specified, defaults to 400, which means one out of 400 packets, on average, will be sent to each target collector. The IPFIX Observation Domain ID sent in each IPFIX packet. If not specified, defaults to 0. The IPFIX Observation Point ID sent in each IPFIX flow record. If not specified, defaults to 0.

    Set to true to enable sampling and reporting tunnel header 7-tuples in IPFIX flow records. Tunnel sampling is disabled by default.

    The following enterprise entities report the sampled tunnel info:

    tunnelType:

    ID: 891, and enterprise ID 6876 (VMware).

    type: unsigned 8-bit integer.

    data type semantics: identifier.

    description: Identifier of the layer 2 network overlay network encapsulation type: 0x01 VxLAN, 0x02 GRE, 0x03 LISP, 0x05 IPsec+GRE, 0x07 GENEVE.

    tunnelKey:

    ID: 892, and enterprise ID 6876 (VMware).

    type: variable-length octetarray.

    data type semantics: identifier.

    description: Key which is used for identifying an individual traffic flow within a VxLAN (24-bit VNI), GENEVE (24-bit VNI), GRE (32-bit key), or LISP (24-bit instance ID) tunnel. The key is encoded in this octetarray as a 3-, 4-, or 8-byte integer ID in network byte order.

    tunnelSourceIPv4Address:

    ID: 893, and enterprise ID 6876 (VMware).

    type: unsigned 32-bit integer.

    data type semantics: identifier.

    description: The IPv4 source address in the tunnel IP packet header.

    tunnelDestinationIPv4Address:

    ID: 894, and enterprise ID 6876 (VMware).

    type: unsigned 32-bit integer.

    data type semantics: identifier.

    description: The IPv4 destination address in the tunnel IP packet header.

    tunnelProtocolIdentifier:

    ID: 895, and enterprise ID 6876 (VMware).

    type: unsigned 8-bit integer.

    data type semantics: identifier.

    description: The value of the protocol number in the tunnel IP packet header. The protocol number identifies the tunnel IP packet payload type.

    tunnelSourceTransportPort:

    ID: 896, and enterprise ID 6876 (VMware).

    type: unsigned 16-bit integer.

    data type semantics: identifier.

    description: The source port identifier in the tunnel transport header. For the transport protocols UDP, TCP, and SCTP, this is the source port number given in the respective header.

    tunnelDestinationTransportPort:

    ID: 897, and enterprise ID 6876 (VMware).

    type: unsigned 16-bit integer.

    data type semantics: identifier.

    description: The destination port identifier in the tunnel transport header. For the transport protocols UDP, TCP, and SCTP, this is the destination port number given in the respective header.

    By default, Open vSwitch samples and reports flows at bridge port input in IPFIX flow records. Set this column to false to disable input sampling. By default, Open vSwitch samples and reports flows at bridge port output in IPFIX flow records. Set this column to false to disable output sampling.
    The overall purpose of these columns is described under Common Columns at the beginning of this document.

    A set of IPFIX collectors of packet samples generated by OpenFlow sample actions. This table is used only for IPFIX flow-based sampling, not for per-bridge sampling (see the table for a description of the two forms).

    The ID of this collector set, unique among the bridge's collector sets, to be used as the collector_set_id in OpenFlow sample actions. The bridge into which OpenFlow sample actions can be added to send packet samples to this set of IPFIX collectors. Configuration of the set of IPFIX collectors to send one flow record per sampled packet to. The overall purpose of these columns is described under Common Columns at the beginning of this document.

    Auto Attach configuration within a bridge. The IETF Auto-Attach SPBM draft standard describes a compact method of using IEEE 802.1AB Link Layer Discovery Protocol (LLDP) together with a IEEE 802.1aq Shortest Path Bridging (SPB) network to automatically attach network devices to individual services in a SPB network. The intent here is to allow network applications and devices using OVS to be able to easily take advantage of features offered by industry standard SPB networks.

    Auto Attach (AA) uses LLDP to communicate between a directly connected Auto Attach Client (AAC) and Auto Attach Server (AAS). The LLDP protocol is extended to add two new Type-Length-Value tuples (TLVs). The first new TLV supports the ongoing discovery of directly connected AA correspondents. Auto Attach operates by regularly transmitting AA discovery TLVs between the AA client and AA server. By exchanging these discovery messages, both the AAC and AAS learn the system name and system description of their peer. In the OVS context, OVS operates as the AA client and the AA server resides on a switch at the edge of the SPB network.

    Once AA discovery has been completed the AAC then uses the second new TLV to deliver identifier mappings from the AAC to the AAS. A primary feature of Auto Attach is to facilitate the mapping of VLANs defined outside the SPB network onto service ids (ISIDs) defined within the SPM network. By doing so individual external VLANs can be mapped onto specific SPB network services. These VLAN id to ISID mappings can be configured and managed locally using new options added to the ovs-vsctl command.

    The Auto Attach OVS feature does not provide a full implementation of the LLDP protocol. Support for the mandatory TLVs as defined by the LLDP standard and support for the AA TLV extensions is provided. LLDP protocol support in OVS can be enabled or disabled on a port by port basis. LLDP support is disabled by default.

    The system_name string is exported in LLDP messages. It should uniquely identify the bridge in the network. The system_description string is exported in LLDP messages. It should describe the type of software and hardware. A mapping from SPB network Individual Service Identifier (ISID) to VLAN id.
    openvswitch-2.5.9/PaxHeaders.82075/INSTALL.XenServer.md0000644000000000000000000000013213534540071017214 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.681845316 openvswitch-2.5.9/INSTALL.XenServer.md0000644000175000017500000002043213534540071020703 0ustar00jpettitjpettit00000000000000How to Install Open vSwitch on Citrix XenServer =============================================== This document describes how to build and install Open vSwitch on a Citrix XenServer host. If you want to install Open vSwitch on a generic Linux or BSD host, see [INSTALL.md] instead. Open vSwitch should work with XenServer 5.6.100 and later. However, Open vSwitch requires Python 2.7 or later, so using Open vSwitch with XenServer 6.5 or earlier requires installing Python 2.7. Building Open vSwitch for XenServer ----------------------------------- You may build from an Open vSwitch distribution tarball or from an Open vSwitch Git tree. The recommended build environment to build RPMs for Citrix XenServer is the DDK VM available from Citrix. 1. If you are building from an Open vSwitch Git tree, then you will need to first create a distribution tarball by running `./boot.sh; ./configure; make dist` in the Git tree. You cannot run this in the DDK VM, because it lacks tools that are necessary to bootstrap the Open vSwitch distribution. Instead, you must run this on a machine that has the tools listed in [INSTALL.md] as prerequisites for building from a Git tree. 2. Copy the distribution tarball into /usr/src/redhat/SOURCES inside the DDK VM. 3. In the DDK VM, unpack the distribution tarball into a temporary directory and "cd" into the root of the distribution tarball. 4. To build Open vSwitch userspace, run: `rpmbuild -bb xenserver/openvswitch-xen.spec` This produces three RPMs in /usr/src/redhat/RPMS/i386: "openvswitch", "openvswitch-modules-xen", and "openvswitch-debuginfo". The above command automatically runs the Open vSwitch unit tests. To disable the unit tests, run: `rpmbuild -bb --without check xenserver/openvswitch-xen.spec` Build Parameters ---------------- openvswitch-xen.spec needs to know a number of pieces of information about the XenServer kernel. Usually, it can figure these out for itself, but if it does not do it correctly then you can specify them yourself as parameters to the build. Thus, the final "rpmbuild" step above can be elaborated as: ``` VERSION= KERNEL_NAME= KERNEL_VERSION= KERNEL_FLAVOR= rpmbuild \ -D "openvswitch_version $VERSION" \ -D "kernel_name $KERNEL_NAME" \ -D "kernel_version $KERNEL_VERSION" \ -D "kernel_flavor $KERNEL_FLAVOR" \ -bb xenserver/openvswitch-xen.spec ``` where: `` is the version number that appears in the name of the Open vSwitch tarball, e.g. 0.90.0. `` is the name of the XenServer kernel package, e.g. kernel-xen or kernel-NAME-xen, without the "kernel-" prefix. `` is the output of: rpm -q --queryformat "%{Version}-%{Release}" , e.g. 2.6.32.12-0.7.1.xs5.6.100.323.170596, where is the name of the -devel package corresponding to . `` is either "xen" or "kdump". The "xen" flavor is the main running kernel flavor and the "kdump" flavor is the crashdump kernel flavor. Commonly, one would specify "xen" here. For XenServer 6.5 or above, the kernel version naming no longer contains KERNEL_FLAVOR. In fact, only providing the `uname -r` output is enough. So, the final "rpmbuild" step changes to: ``` KERNEL_UNAME=<`uname -r` output> rpmbuild \ -D "kenel_uname $KERNEL_UNAME" \ -bb xenserver/openvswitch-xen.spec ``` Installing Open vSwitch for XenServer ------------------------------------- To install Open vSwitch on a XenServer host, or to upgrade to a newer version, copy the "openvswitch" and "openvswitch-modules-xen" RPMs to that host with "scp", then install them with "rpm -U", e.g.: ``` scp openvswitch-$VERSION-1.i386.rpm \ openvswitch-modules-xen-$XEN_KERNEL_VERSION-$VERSION-1.i386.rpm \ root@: (At this point you will have to enter 's root password.) ssh root@ (At this point you will have to enter 's root password again.) rpm -U openvswitch-$VERSION-1.i386.rpm \ openvswitch-modules-xen-$XEN_KERNEL_VERSION-$VERSION-1.i386.rpm ``` To uninstall Open vSwitch from a XenServer host, remove the packages: `ssh root@` (At this point you will have to enter 's root password again.) `rpm -e openvswitch openvswitch-modules-xen-$XEN_KERNEL_VERSION` After installing or uninstalling Open vSwitch, the XenServer should be rebooted as soon as possible. Open vSwitch Boot Sequence on XenServer --------------------------------------- When Open vSwitch is installed on XenServer, its startup script /etc/init.d/openvswitch runs early in boot. It does roughly the following: * Loads the OVS kernel module, openvswitch. * Starts ovsdb-server, the OVS configuration database. * XenServer expects there to be no bridges configured at startup, but the OVS configuration database likely still has bridges configured from before reboot. To match XenServer expectations, the startup script deletes all configured bridges from the database. * Starts ovs-vswitchd, the OVS switching daemon. At this point in the boot process, then, there are no Open vSwitch bridges, even though all of the Open vSwitch daemons are running. Later on in boot, /etc/init.d/management-interface (part of XenServer, not Open vSwitch) creates the bridge for the XAPI management interface by invoking /opt/xensource/libexec/interface-reconfigure. Normally this program consults XAPI's database to obtain information about how to configure the bridge, but XAPI is not running yet[*] so it instead consults /var/xapi/network.dbcache, which is a cached copy of the most recent network configuration. [*] Even if XAPI were running, if this XenServer node is a pool slave then the query would have to consult the master, which requires network access, which begs the question of how to configure the management interface. XAPI starts later on in the boot process. XAPI can then create other bridges on demand using /opt/xensource/libexec/interface-reconfigure. Now that XAPI is running, that program consults XAPI directly instead of reading the cache. As part of its own startup, XAPI invokes the Open vSwitch XAPI plugin script /etc/xapi.d/openvswitch-cfg-update passing the "update" command. The plugin script does roughly the following: * Calls /opt/xensource/libexec/interface-reconfigure with the "rewrite" command, to ensure that the network cache is up-to-date. * Queries the Open vSwitch manager setting (named "vswitch_controller") from the XAPI database for the XenServer pool. * If XAPI and OVS are configured for different managers, or if OVS is configured for a manager but XAPI is not, runs "ovs-vsctl emer-reset" to bring the Open vSwitch configuration to a known state. One effect of emer-reset is to deconfigure any manager from the OVS database. * If XAPI is configured for a manager, configures the OVS manager to match with "ovs-vsctl set-manager". Notes ----- * The Open vSwitch boot sequence only configures an OVS configuration database manager. There is no way to directly configure an OpenFlow controller on XenServer and, as a consequence of the step above that deletes all of the bridges at boot time, controller configuration only persists until XenServer reboot. The configuration database manager can, however, configure controllers for bridges. See the BUGS section of ovs-testcontroller(8) for more information on this topic. * The Open vSwitch startup script automatically adds a firewall rule to allow GRE traffic. This rule is needed for the XenServer feature called "Cross-Host Internal Networks" (CHIN) that uses GRE. If a user configures tunnels other than GRE (ex: Geneve, VXLAN, LISP), they will have to either manually add a iptables firewall rule to allow the tunnel traffic or add it through a startup script (Please refer to the "enable-protocol" command in the ovs-ctl(8) manpage). Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. [INSTALL.md]:INSTALL.md openvswitch-2.5.9/PaxHeaders.82075/TODO.md0000644000000000000000000000013213534540071014733 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.697845435 openvswitch-2.5.9/TODO.md0000644000175000017500000002526213534540071016430 0ustar00jpettitjpettit00000000000000Open vSwitch Project Ideas ========================== This file lists a number of project ideas for Open vSwitch. The ideas here overlap somewhat with those in the [OPENFLOW-1.1+.md] file. Programming Project Ideas ========================= Each of these projects would ideally result in a patch or a short series of them posted to ovs-dev. Please read [CONTRIBUTING.md] and [CodingStyle.md] in the top of the source tree before you begin work. The [OPENFLOW-1.1+.md] file also has an introduction to how OpenFlow is implemented in Open vSwitch. It is also a good idea to look around the source tree for related code, and back through the Git history for commits on related subjects, to allow you to follow existing patterns and conventions. Meters ------ Open vSwitch has OpenFlow protocol support for meters, but it does not have an implementation in the kernel or userspace datapaths. An implementation was proposed some time ago (I recommend looking for the discussion in the ovs-dev mailing list archives), but for a few different reasons it was not accepted. Some of those reasons apply only to a kernel implementation of meters. At the time, a userspace implementation wasn't as interesting, because the userspace switch did not perform at a production speed, but with the advent of multithreaded forwarding and, now, DPDK support, userspace-only meters would be a great way to get started. Improve SSL/TLS Security ------------------------ Open vSwitch allows some weak ciphers to be used for its secure connections. Security audits often suggest that the project remove those ciphers, but there's not a clean way to modify the acceptable ciphers. At the very least, the cipher list should be audited, but it would be nice to make it configurable. Open vSwitch does not insist on perfect forward security via ephemeral Diffie-Hellman key exchange when it establishes an SSL/TLS connection. Given the wiretapping revelations over the last year, it seems wise to turn this on. (This would probably amount to finding the right OpenSSL function to call or just reducing the acceptable ciphers further.) These changes might have backward-compatibility implications; one would have to test the behavior of the reduced cipher list OVS against older versions. Bash Command Completion ----------------------- ovs-vsctl and other programs would be easier to use if bash command completion (with ``tab'', etc.) were supported. Alex Wang is leading a team for this project. Auxiliary Connections --------------------- Auxiliary connections are a feature of OpenFlow 1.3 and later that allow OpenFlow messages to be carried over datagram channels such as UDP or DTLS. One place to start would be to implement a datagram abstraction library for OVS analogous to the ``stream'' library that already abstracts TCP, SSL, and other stream protocols. Basic OpenFlow 1.4 support -------------------------- Some basic support for OpenFlow 1.4 is missing and needs to be implemented. These can be found by looking through lib/ofp-util.c for mentions of OFP14_VERSION followed by a call to OVS_NOT_REACHED (which aborts the program). OpenFlow 1.4: Flow monitoring ----------------------------- OpenFlow 1.4 introduces OFPMP_FLOW_MONITOR for notifying a controller of changes to selected flow tables. This feature is based on NXST_FLOW_MONITOR that is already part of Open vSwitch, so to implement this feature would be to extend that code to handle the OpenFlow 1.4 wire protocol. OpenFlow 1.3 also includes this feature as a ONF-defined extension, so ideally OVS would support that too. OpenFlow 1.4 Role Status Message -------------------------------- OpenFlow 1.4 section 7.4.4 ``Controller Role Status Message'' defines a new message sent by a switch to notify the controller that its role (whether it is a master or a slave) has changed. OVS should implement this. OpenFlow 1.3 also includes this feature as a ONF-defined extension, so ideally OVS would support that too. OpenFlow 1.4 Vacancy Events --------------------------- OpenFlow 1.4 section 7.4.5 ``Table Status Message'' defines a new message sent by a switch to notify the controller that a flow table is close to filling up (or that it is no longer close to filling up). OVS should implement this. OpenFlow 1.3 also includes this feature as a ONF-defined extension, so ideally OVS would support that too. OpenFlow 1.4 Group and Meter Change Notification ------------------------------------------------ OpenFlow 1.4 adds a feature whereby a controller can ask the switch to send it copies of messages that change groups and meters. (This is only useful in the presence of multiple controllers.) OVS should implement this. OpenFlow 1.3 also includes this feature as a ONF-defined extension, so ideally OVS would support that too. Testing Project Ideas ===================== Each of these projects would ideally result in confirmation that features work or bug reports explaining how they do not. Please sent bug reports to dev at openvswitch.org, with as many details as you have. ONF Plugfest Results Analysis ----------------------------- Ben Pfaff has a collection of files reporting Open vSwitch conformance to OpenFlow 1.3 provided by one of the vendors at the ONF plugfest last year. Some of the reported failures have been fixed, some of the other failures probably result from differing interpretations of OpenFlow 1.3, and others are probably genuine bugs in Open vSwitch. Open vSwitch has also improved in the meantime. Ben can provide the results, privately, to some person or team who wishes to check them out and try to pick out the genuine bugs. OpenFlow Fuzzer --------------- Build a ``fuzzer'' for the OpenFlow protocol (or use an existing one, if there is one) and run it against the Open vSwitch implementation. One could also build a fuzzer for the OSVDB protocol. Ryu Certification Tests Analysis -------------------------------- The Ryu controller comes with a suite of ``certification tests'' that check the correctness of a switch's implementation of various OpenFlow 1.3 features. The INSTALL file in the OVS source tree has a section that explains how to easily run these tests against an OVS source tree. Run the tests and figure out whether any tests fail but should pass. (Some tests fail and should fail because OVS does not implement the particular feature; for example, OVS does not implement PBB encapsulation, so related tests fail.) OFTest Results Analysis ----------------------- OFTest is a test suite for OpenFlow 1.0 compliance. The INSTALL file in the OVS source tree has a section that explains how to easily run these tests against an OVS source tree. Run the tests and figure out whether any tests fail but should pass, and ideally why. OFTest is not particularly well vetted--in the past, at least, some tests have failed against OVS due to bugs in OFTest, not in OVS--so some care is warranted. Documentation Project Ideas =========================== Each of these projects would ideally result in creating some new documentation for users. Some documentation might be suitable to accompany Open vSwitch as part of its source tree most likely either in plain text or ``nroff'' (manpage) format. OpenFlow Basics Tutorial ------------------------ Open vSwitch has a tutorial that covers its advanced features, but it does not have a basic tutorial. There are several tutorials on the Internet already, so a new tutorial would have to distinguish itself in some way. One way would be to use the Open vSwitch ``sandbox'' environment already used in the advanced tutorial. The sandbox does not require any real network or even supervisor privilege on the machine where it runs, and thus it is easy to use with hardly any up-front setup, so it is a gentle way to get started. FlowVisor via patch ports ------------------------- FlowVisor is a proxy that sits between OpenFlow controllers and a switch. It divides up switch resources, allowing each controller to control a ``slice'' of the network. For example, it can break up a network based on VLAN, allowing different controllers to handle packets with different VLANs. It seems that Open vSwitch has features that allow it to implement at least simple forms of FlowVisor control without any need for FlowVisor. Consider an Open vSwitch instance with three bridges. Bridge br0 has physical ports eth0 and eth1. Bridge v9 has no physical ports, but it has two ``patch ports'' that connect it to br0. Bridge v11 has the same setup. Flows in br0 match packets received on vlan 9, strip the vlan header, and direct them to the appropriate patch port leading to v9. Additional flows in br0 match packets received from v9, attach a VLAN 9 tag to them, and direct them out eth0 or eth1 as appropriate. Other flows in br0 treat packets on VLAN 11 similarly. Controllers attached to bridge v9 or v11 may thus work as if they had full control of a network. It seems to me that this is a good example of the power of OpenFlow and Open vSwitch. The point of this project is to explain how to do this, with detailed examples, in case someone finds it handy and to open eyes toward the generality of Open vSwitch usefulness. ``Cookbooks'' ------------- The Open vSwitch website has a few ``cookbook'' entries that describe how to use Open vSwitch in a few scenarios. There are only a few of these and all of them are dated. It would be a good idea to come up with ideas for some more and write them. These could be added to the Open vSwitch website or the source tree or somewhere else. Demos ----- Record a demo of Open vSwitch functionality in use (or something else relevant) and post it to youtube or another video site so that we can link to it from openvswitch.org. How to contribute ================= If you plan to contribute code for a feature, please let everyone know on ovs-dev before you start work. This will help avoid duplicating work. Please consider the following: * Testing. Please test your code. * Unit tests. Please consider writing some. The tests directory has many examples that you can use as a starting point. * ovs-ofctl. If you add a feature that is useful for some ovs-ofctl command then you should add support for it there. * Documentation. If you add a user-visible feature, then you should document it in the appropriate manpage and mention it in NEWS as well. * Coding style (see the [CodingStyle.md] file at the top of the source tree). * The patch submission guidelines (see [CONTRIBUTING.md]). I recommend using "git send-email", which automatically follows a lot of those guidelines. Bug Reporting ============= Please report problems to bugs@openvswitch.org. Local Variables: mode: text End: [OPENFLOW-1.1+.md]:OPENFLOW-1.1+.md [CONTRIBUTING.md]:CONTRIBUTING.md [CodingStyle.md]:CodingStyle.md openvswitch-2.5.9/PaxHeaders.82075/OPENFLOW-1.1+.md0000644000000000000000000000013213534540071015767 xustar0030 mtime=1567801401.189679614 30 atime=1567801402.041685871 30 ctime=1567801423.685845346 openvswitch-2.5.9/OPENFLOW-1.1+.md0000644000175000017500000002503313534540071017460 0ustar00jpettitjpettit00000000000000OpenFlow 1.1+ support in Open vSwitch ===================================== Open vSwitch support for OpenFlow 1.1 and beyond is a work in progress. This file describes the work still to be done. The Plan -------- OpenFlow version support is not a build-time option. A single build of Open vSwitch must be able to handle all supported versions of OpenFlow. Ideally, even at runtime it should be able to support all protocol versions at the same time on different OpenFlow bridges (and perhaps even on the same bridge). At the same time, it would be a shame to litter the core of the OVS code with lots of ugly code concerned with the details of various OpenFlow protocol versions. The primary approach to compatibility is to abstract most of the details of the differences from the core code, by adding a protocol layer that translates between OF1.x and a slightly higher-level abstract representation. The core of this approach is the many struct ofputil_* structures in lib/ofp-util.h. As a consequence of this approach, OVS cannot use OpenFlow protocol definitions that closely resemble those in the OpenFlow specification, because openflow.h in different versions of the OpenFlow specification defines the same identifier with different values. Instead, openflow-common.h contains definitions that are common to all the specifications and separate protocol version-specific headers contain protocol-specific definitions renamed so as not to conflict, e.g. OFPAT10_ENQUEUE and OFPAT11_ENQUEUE for the OpenFlow 1.0 and 1.1 values for OFPAT_ENQUEUE. Generally, in cases of conflict, the protocol layer will define a more abstract OFPUTIL_* or struct ofputil_*. Here are the current approaches in a few tricky areas: * Port numbering. OpenFlow 1.0 has 16-bit port numbers and later OpenFlow versions have 32-bit port numbers. For now, OVS support for later protocol versions requires all port numbers to fall into the 16-bit range, translating the reserved OFPP_* port numbers. * Actions. OpenFlow 1.0 and later versions have very different ideas of actions. OVS reconciles by translating all the versions' actions (and instructions) to and from a common internal representation. OpenFlow 1.1 ------------ The list of remaining work items for OpenFlow 1.1 is below. It is probably incomplete. * Match and set double-tagged VLANs (QinQ). This requires kernel work for reasonable performance. [optional for OF1.1+] * VLANs tagged with 88a8 Ethertype. This requires kernel work for reasonable performance. [required for OF1.1+] OpenFlow 1.2 ------------ OpenFlow 1.2 support requires OpenFlow 1.1 as a prerequisite. All the additional work specific to Openflow 1.2 are complete. (This is based on the change log at the end of the OF1.2 spec. I didn't compare the specs carefully yet.) OpenFlow 1.3 ------------ OpenFlow 1.3 support requires OpenFlow 1.2 as a prerequisite, plus the following additional work. (This is based on the change log at the end of the OF1.3 spec, reusing most of the section titles directly. I didn't compare the specs carefully yet.) * Add support for multipart requests. Currently we always report OFPBRC_MULTIPART_BUFFER_OVERFLOW. [optional for OF1.3+] * IPv6 extension header handling support. Fully implementing this requires kernel support. This likely will take some careful and probably time-consuming design work. The actual coding, once that is all done, is probably 2 or 3 days work. [optional for OF1.3+] * Per-flow meters. OpenFlow protocol support is now implemented. Support for the special OFPM_SLOWPATH and OFPM_CONTROLLER meters is missing. Support for the software switch is under review. [optional for OF1.3+] * Auxiliary connections. An implementation in generic code might be a week's worth of work. The value of an implementation in generic code is questionable, though, since much of the benefit of axuiliary connections is supposed to be to take advantage of hardware support. (We could make the kernel module somehow send packets across the auxiliary connections directly, for some kind of "hardware" support, if we judged it useful enough.) [optional for OF1.3+] * Provider Backbone Bridge tagging. I don't plan to implement this (but we'd accept an implementation). [optional for OF1.3+] * On-demand flow counters. I think this might be a real optimization in some cases for the software switch. [optional for OF1.3+] OpenFlow 1.4 & ONF Extensions for 1.3.X Pack1 --------------------------------------------- The following features are both defined as a set of ONF Extensions for 1.3 and integrated in 1.4. When defined as an ONF Extension for 1.3, the feature is using the Experimenter mechanism with the ONF Experimenter ID. When defined integrated in 1.4, the feature use the standard OpenFlow structures (for example defined in openflow-1.4.h). The two definitions for each feature are independant and can exist in parallel in OVS. * Flow entry notifications This seems to be modelled after OVS's NXST_FLOW_MONITOR. (Simon Horman is working on this.) [EXT-187] [optional for OF1.4+] * Role Status Already implemented as a 1.4 feature. [EXT-191] [required for OF1.4+] * Flow entry eviction OVS has flow eviction functionality. table_mod OFPTC_EVICTION, flow_mod 'importance', and table_desc ofp_table_mod_prop_eviction need to be implemented. [EXT-192-e] [optional for OF1.4+] * Vacancy events [EXT-192-v] [optional for OF1.4+] * Bundle Transactional modification. OpenFlow 1.4 requires to support flow_mods and port_mods in a bundle if bundle is supported. (Not related to OVS's 'ofbundle' stuff.) Implemented as an OpenFlow 1.4 feature. Only flow_mods and port_mods are supported in a bundle. If the bundle includes port mods, it may not specify the OFPBF_ATOMIC flag. Nevertheless, port mods and flow mods in a bundle are always applied in order and consecutive flow mods between port mods are made available to lookups atomically. [EXT-230] [optional for OF1.4+] * Table synchronisation Probably not so useful to the software switch. [EXT-232] [optional for OF1.4+] * Group and Meter change notifications [EXT-235] [optional for OF1.4+] * Bad flow entry priority error Probably not so useful to the software switch. [EXT-236] [optional for OF1.4+] * Set async config error [EXT-237] [optional for OF1.4+] * PBB UCA header field See comment on Provider Backbone Bridge in section about OpenFlow 1.3. [EXT-256] [optional for OF1.4+] * Multipart timeout error [EXT-264] [required for OF1.4+] OpenFlow 1.4 only ----------------- Those features are those only available in OpenFlow 1.4, other OpenFlow 1.4 features are listed in the previous section. * More extensible wire protocol Many on-wire structures got TLVs. Already implemented: port desc properties, port mod properties, port stats properties, table mod properties, queue stats, unified property errors. Remaining required: set-async, queue desc Remaining optional: table desc, table-status [EXT-262] [required for OF1.4+] * More descriptive reasons for packet-in Distinguish OFPR_APPLY_ACTION, OFPR_ACTION_SET, OFPR_GROUP, OFPR_PACKET_OUT. NO_MATCH was renamed to OFPR_TABLE_MISS. (OFPR_ACTION_SET and OFPR_GROUP are now supported) [EXT-136] [required for OF1.4+] * Optical port properties [EXT-154] [optional for OF1.4+] OpenFlow 1.5 & ONF Extensions for 1.3.X Pack2 --------------------------------------------- The following features are both defined as a set of ONF Extensions for 1.3 and integrated in 1.5. Note that this list is not definitive as those are not yet published. When defined as an ONF Extension for 1.3, the feature is using the Experimenter mechanism with the ONF Experimenter ID. When defined integrated in 1.5, the feature use the standard OpenFlow structures (for example defined in openflow-1.5.h). The two definitions for each feature are independant and can exist in parallel in OVS. * Time scheduled bundles [EXT-340] [optional for OF1.5+] OpenFlow 1.5 only ----------------- Those features are those only available in OpenFlow 1.5, other OpenFlow 1.5 features are listed in the previous section. Note that this list is not definitive as OpenFlow 1.5 is not yet published. * Egress Tables [EXT-306] [optional for OF1.5+] * Packet Type aware pipeline Prototype for OVS was done during specification. [EXT-112] [optional for OF1.5+] * Extensible Flow Entry Statistics [EXT-334] [required for OF1.5+] * Flow Entry Statistics Trigger [EXT-335] [optional for OF1.5+] * Controller connection status Prototype for OVS was done during specification. [EXT-454] [optional for OF1.5+] * Meter action [EXT-379] [required for OF1.5+ if metering is supported] * Enable setting all pipeline fields in packet-out Prototype for OVS was done during specification. [EXT-427] [required for OF1.5+] * Port properties for pipeline fields Prototype for OVS was done during specification. [EXT-388] [optional for OF1.5+] * Port property for recirculation Prototype for OVS was done during specification. [EXT-399] [optional for OF1.5+] General ----- * ovs-ofctl(8) often lists as Nicira extensions features that later OpenFlow versions support in standard ways. How to contribute ----------------- If you plan to contribute code for a feature, please let everyone know on ovs-dev before you start work. This will help avoid duplicating work. Please consider the following: * Testing. Please test your code. * Unit tests. Please consider writing some. The tests directory has many examples that you can use as a starting point. * ovs-ofctl. If you add a feature that is useful for some ovs-ofctl command then you should add support for it there. * Documentation. If you add a user-visible feature, then you should document it in the appropriate manpage and mention it in NEWS as well. * Coding style (see the [CodingStyle.md] file at the top of the source tree). * The patch submission guidelines (see [CONTRIBUTING.md]). I recommend using "git send-email", which automatically follows a lot of those guidelines. Bug Reporting ------------- Please report problems to bugs@openvswitch.org. [CONTRIBUTING.md]:CONTRIBUTING.md [CodingStyle.md]:CodingStyle.md openvswitch-2.5.9/PaxHeaders.82075/CodingStyle.md0000644000000000000000000000013213534540071016412 xustar0030 mtime=1567801401.181679555 30 atime=1567801402.037685841 30 ctime=1567801423.657845141 openvswitch-2.5.9/CodingStyle.md0000644000175000017500000004644213534540071020112 0ustar00jpettitjpettit00000000000000Open vSwitch Coding Style ========================= This file describes the coding style used in most C files in the Open vSwitch distribution. However, Linux kernel code datapath directory follows the Linux kernel's established coding conventions. For the Windows kernel datapath code, use the coding style described in datapath-windows/CodingStyle. The following GNU indent options approximate this style: -npro -bad -bap -bbb -br -blf -brs -cdw -ce -fca -cli0 -npcs -i4 -l79 \ -lc79 -nbfda -nut -saf -sai -saw -sbi4 -sc -sob -st -ncdb -pi4 -cs -bs \ -di1 -lp -il0 -hnl ## BASICS Limit lines to 79 characters. Use form feeds (control+L) to divide long source files into logical pieces. A form feed should appear as the only character on a line. Do not use tabs for indentation. Avoid trailing spaces on lines. ## NAMING - Use names that explain the purpose of a function or object. - Use underscores to separate words in an identifier: multi_word_name. - Use lowercase for most names. Use uppercase for macros, macro parameters, and members of enumerations. - Give arrays names that are plural. - Pick a unique name prefix (ending with an underscore) for each module, and apply that prefix to all of that module's externally visible names. Names of macro parameters, struct and union members, and parameters in function prototypes are not considered externally visible for this purpose. - Do not use names that begin with _. If you need a name for "internal use only", use __ as a suffix instead of a prefix. - Avoid negative names: "found" is a better name than "not_found". - In names, a "size" is a count of bytes, a "length" is a count of characters. A buffer has size, but a string has length. The length of a string does not include the null terminator, but the size of the buffer that contains the string does. ## COMMENTS Comments should be written as full sentences that start with a capital letter and end with a period. Put two spaces between sentences. Write block comments as shown below. You may put the /* and */ on the same line as comment text if you prefer. /* * We redirect stderr to /dev/null because we often want to remove all * traffic control configuration on a port so its in a known state. If * this done when there is no such configuration, tc complains, so we just * always ignore it. */ Each function and each variable declared outside a function, and each struct, union, and typedef declaration should be preceded by a comment. See FUNCTION DEFINITIONS below for function comment guidelines. Each struct and union member should each have an inline comment that explains its meaning. structs and unions with many members should be additionally divided into logical groups of members by block comments, e.g.: /* An event that will wake the following call to poll_block(). */ struct poll_waiter { /* Set when the waiter is created. */ struct ovs_list node; /* Element in global waiters list. */ int fd; /* File descriptor. */ short int events; /* Events to wait for (POLLIN, POLLOUT). */ poll_fd_func *function; /* Callback function, if any, or null. */ void *aux; /* Argument to callback function. */ struct backtrace *backtrace; /* Event that created waiter, or null. */ /* Set only when poll_block() is called. */ struct pollfd *pollfd; /* Pointer to element of the pollfds array (null if added from a callback). */ }; Use XXX or FIXME comments to mark code that needs work. Don't use `//` comments. Don't comment out or #if 0 out code. Just remove it. The code that was there will still be in version control history. ## FUNCTIONS Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. Before each function definition, write a comment that describes the function's purpose, including each parameter, the return value, and side effects. References to argument names should be given in single-quotes, e.g. 'arg'. The comment should not include the function name, nor need it follow any formal structure. The comment does not need to describe how a function does its work, unless this information is needed to use the function correctly (this is often better done with comments *inside* the function). Simple static functions do not need a comment. Within a file, non-static functions should come first, in the order that they are declared in the header file, followed by static functions. Static functions should be in one or more separate pages (separated by form feed characters) in logical groups. A commonly useful way to divide groups is by "level", with high-level functions first, followed by groups of progressively lower-level functions. This makes it easy for the program's reader to see the top-down structure by reading from top to bottom. All function declarations and definitions should include a prototype. Empty parentheses, e.g. "int foo();", do not include a prototype (they state that the function's parameters are unknown); write "void" in parentheses instead, e.g. "int foo(void);". Prototypes for static functions should either all go at the top of the file, separated into groups by blank lines, or they should appear at the top of each page of functions. Don't comment individual prototypes, but a comment on each group of prototypes is often appropriate. In the absence of good reasons for another order, the following parameter order is preferred. One notable exception is that data parameters and their corresponding size parameters should be paired. 1. The primary object being manipulated, if any (equivalent to the "this" pointer in C++). 2. Input-only parameters. 3. Input/output parameters. 4. Output-only parameters. 5. Status parameter. Example: ``` /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value * is a bitmap of "enum ofp_port_features" bits, in host byte order. * Returns 0 if successful, otherwise a positive errno value. On failure, * all of the passed-in values are set to 0. */ int netdev_get_features(struct netdev *netdev, uint32_t *current, uint32_t *advertised, uint32_t *supported, uint32_t *peer) { ... } ``` Functions that destroy an instance of a dynamically-allocated type should accept and ignore a null pointer argument. Code that calls such a function (including the C standard library function free()) should omit a null-pointer check. We find that this usually makes code easier to read. Functions in .c files should not normally be marked "inline", because it does not usually help code generation and it does suppress compilers warnings about unused functions. (Functions defined in .h usually should be marked inline.) ## FUNCTION PROTOTYPES Put the return type and function name on the same line in a function prototype: static const struct option_class *get_option_class(int code); Omit parameter names from function prototypes when the names do not give useful information, e.g.: int netdev_get_mtu(const struct netdev *, int *mtup); ## STATEMENTS Indent each level of code with 4 spaces. Use BSD-style brace placement: if (a()) { b(); d(); } Put a space between "if", "while", "for", etc. and the expressions that follow them. Enclose single statements in braces: if (a > b) { return a; } else { return b; } Use comments and blank lines to divide long functions into logical groups of statements. Avoid assignments inside "if" and "while" conditions. Do not put gratuitous parentheses around the expression in a return statement, that is, write "return 0;" and not "return(0);" Write only one statement per line. Indent "switch" statements like this: switch (conn->state) { case S_RECV: error = run_connection_input(conn); break; case S_PROCESS: error = 0; break; case S_SEND: error = run_connection_output(conn); break; default: OVS_NOT_REACHED(); } "switch" statements with very short, uniform cases may use an abbreviated style: switch (code) { case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; case 204: return "No Content"; default: return "Unknown"; } Use "for (;;)" to write an infinite loop. In an if/else construct where one branch is the "normal" or "common" case and the other branch is the "uncommon" or "error" case, put the common case after the "if", not the "else". This is a form of documentation. It also places the most important code in sequential order without forcing the reader to visually skip past less important details. (Some compilers also assume that the "if" branch is the more common case, so this can be a real form of optimization as well.) ## RETURN VALUES For functions that return a success or failure indication, prefer one of the following return value conventions: * An "int" where 0 indicates success and a positive errno value indicates a reason for failure. * A "bool" where true indicates success and false indicates failure. ## MACROS Don't define an object-like macro if an enum can be used instead. Don't define a function-like macro if a "static inline" function can be used instead. If a macro's definition contains multiple statements, enclose them with "do { ... } while (0)" to allow them to work properly in all syntactic circumstances. Do use macros to eliminate the need to update different parts of a single file in parallel, e.g. a list of enums and an array that gives the name of each enum. For example: /* Logging importance levels. */ #define VLOG_LEVELS \ VLOG_LEVEL(EMER, LOG_ALERT) \ VLOG_LEVEL(ERR, LOG_ERR) \ VLOG_LEVEL(WARN, LOG_WARNING) \ VLOG_LEVEL(INFO, LOG_NOTICE) \ VLOG_LEVEL(DBG, LOG_DEBUG) enum vlog_level { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) VLL_##NAME, VLOG_LEVELS #undef VLOG_LEVEL VLL_N_LEVELS }; /* Name for each logging level. */ static const char *level_names[VLL_N_LEVELS] = { #define VLOG_LEVEL(NAME, SYSLOG_LEVEL) #NAME, VLOG_LEVELS #undef VLOG_LEVEL }; ## THREAD SAFETY ANNOTATIONS Use the macros in lib/compiler.h to annotate locking requirements. For example: static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER; void function_require_plain_mutex(void) OVS_REQUIRES(mutex); void function_require_rwlock(void) OVS_REQ_RDLOCK(rwlock); Pass lock objects, not their addresses, to the annotation macros. (Thus we have OVS_REQUIRES(mutex) above, not OVS_REQUIRES(&mutex).) ## SOURCE FILES Each source file should state its license in a comment at the very top, followed by a comment explaining the purpose of the code that is in that file. The comment should explain how the code in the file relates to code in other files. The goal is to allow a programmer to quickly figure out where a given module fits into the larger system. The first non-comment line in a .c source file should be: #include `#include` directives should appear in the following order: 1. `#include ` 2. The module's own headers, if any. Including this before any other header (besides ) ensures that the module's header file is self-contained (see HEADER FILES) below. 3. Standard C library headers and other system headers, preferably in alphabetical order. (Occasionally one encounters a set of system headers that must be included in a particular order, in which case that order must take precedence.) 4. Open vSwitch headers, in alphabetical order. Use "", not <>, to specify Open vSwitch header names. ## HEADER FILES Each header file should start with its license, as described under SOURCE FILES above, followed by a "header guard" to make the header file idempotent, like so: #ifndef NETDEV_H #define NETDEV_H 1 ... #endif /* netdev.h */ Header files should be self-contained; that is, they should #include whatever additional headers are required, without requiring the client to #include them for it. Don't define the members of a struct or union in a header file, unless client code is actually intended to access them directly or if the definition is otherwise actually needed (e.g. inline functions defined in the header need them). Similarly, don't #include a header file just for the declaration of a struct or union tag (e.g. just for "struct ;"). Just declare the tag yourself. This reduces the number of header file dependencies. ## TYPES Use typedefs sparingly. Code is clearer if the actual type is visible at the point of declaration. Do not, in general, declare a typedef for a struct, union, or enum. Do not declare a typedef for a pointer type, because this can be very confusing to the reader. A function type is a good use for a typedef because it can clarify code. The type should be a function type, not a pointer-to-function type. That way, the typedef name can be used to declare function prototypes. (It cannot be used for function definitions, because that is explicitly prohibited by C89 and C99.) You may assume that "char" is exactly 8 bits and that "int" and "long" are at least 32 bits. Don't assume that "long" is big enough to hold a pointer. If you need to cast a pointer to an integer, use "intptr_t" or "uintptr_t" from . Use the int_t and uint_t types from for exact-width integer types. Use the PRId, PRIu, and PRIx macros from for formatting them with printf() and related functions. For compatibility with antique printf() implementations: - Instead of "%zu", use "%"PRIuSIZE. - Instead of "%td", use "%"PRIdPTR. - Instead of "%ju", use "%"PRIuMAX. Other variants exist for different radixes. For example, use "%"PRIxSIZE instead of "%zx" or "%x" instead of "%hhx". Also, instead of "%hhd", use "%d". Be cautious substituting "%u", "%x", and "%o" for the corresponding versions with "hh": cast the argument to unsigned char if necessary, because printf("%hhu", -1) prints 255 but printf("%u", -1) prints 4294967295. Use bit-fields sparingly. Do not use bit-fields for layout of network protocol fields or in other circumstances where the exact format is important. Declare bit-fields to be signed or unsigned integer types or _Bool (aka bool). Do *not* declare bit-fields of type "int": C99 allows these to be either signed or unsigned according to the compiler's whim. (A 1-bit bit-field of type "int" may have a range of -1...0!) Try to order structure members such that they pack well on a system with 2-byte "short", 4-byte "int", and 4- or 8-byte "long" and pointer types. Prefer clear organization over size optimization unless you are convinced there is a size or speed benefit. Pointer declarators bind to the variable name, not the type name. Write "int *x", not "int* x" and definitely not "int * x". ## EXPRESSIONS Put one space on each side of infix binary and ternary operators: * / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= Avoid comma operators. Do not put any white space around postfix, prefix, or grouping operators: () [] -> . ! ~ ++ -- + - * & Exception 1: Put a space after (but not before) the "sizeof" keyword. Exception 2: Put a space between the () used in a cast and the expression whose type is cast: (void *) 0. Break long lines before the ternary operators ? and :, rather than after them, e.g. return (out_port != VIGP_CONTROL_PATH ? alpheus_output_port(dp, skb, out_port) : alpheus_output_control(dp, skb, fwd_save_skb(skb), VIGR_ACTION)); Do not parenthesize the operands of && and || unless operator precedence makes it necessary, or unless the operands are themselves expressions that use && and ||. Thus: if (!isdigit((unsigned char)s[0]) || !isdigit((unsigned char)s[1]) || !isdigit((unsigned char)s[2])) { printf("string %s does not start with 3-digit code\n", s); } but if (rule && (!best || rule->priority > best->priority)) { best = rule; } Do parenthesize a subexpression that must be split across more than one line, e.g.: *idxp = ((l1_idx << PORT_ARRAY_L1_SHIFT) | (l2_idx << PORT_ARRAY_L2_SHIFT) | (l3_idx << PORT_ARRAY_L3_SHIFT)); Try to avoid casts. Don't cast the return value of malloc(). The "sizeof" operator is unique among C operators in that it accepts two very different kinds of operands: an expression or a type. In general, prefer to specify an expression, e.g. "int *x = xmalloc(sizeof *x);". When the operand of sizeof is an expression, there is no need to parenthesize that operand, and please don't. Use the ARRAY_SIZE macro from lib/util.h to calculate the number of elements in an array. When using a relational operator like "<" or "==", put an expression or variable argument on the left and a constant argument on the right, e.g. "x == 0", *not* "0 == x". ## BLANK LINES Put one blank line between top-level definitions of functions and global variables. ## C DIALECT Most C99 features are OK because they are widely implemented: * Flexible array members (e.g. struct { int foo[]; }). * "static inline" functions (but no other forms of "inline", for which GCC and C99 have differing interpretations). * "long long" * and . * bool and , but don't assume that bool or _Bool can only take on the values 0 or 1, because this behavior can't be simulated on C89 compilers. Also, don't assume that a conversion to bool or _Bool follows C99 semantics. I.e. use "(bool)(some_value != 0)" rather than "(bool)some_value". The latter might produce unexpected results on non-C99 environments. For example, if bool is implemented as a typedef of char and some_value = 0x10000000. * Designated initializers (e.g. "struct foo foo = {.a = 1};" and "int a[] = {[2] = 5};"). * Mixing of declarations and code within a block. Please use this judiciously; keep declarations nicely grouped together in the beginning of a block if possible. * Use of declarations in iteration statements (e.g. "for (int i = 0; i < 10; i++)"). * Use of a trailing comma in an enum declaration (e.g. "enum { x = 1, };"). As a matter of style, avoid // comments. Avoid using GCC or Clang extensions unless you also add a fallback for other compilers. You can, however, use C99 features or GCC extensions also supported by Clang in code that compiles only on GNU/Linux (such as lib/netdev-linux.c), because GCC is the system compiler there. ## PYTHON When introducing new Python code, try to follow Python's [PEP 8](http://www.python.org/dev/peps/pep-0008/) style. Consider running the `pep8` or `flake8` tool against your code to find issues. openvswitch-2.5.9/PaxHeaders.82075/INSTALL.Docker.md0000644000000000000000000000013213534540071016502 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.037685841 30 ctime=1567801423.665845199 openvswitch-2.5.9/INSTALL.Docker.md0000644000175000017500000002112113534540071020165 0ustar00jpettitjpettit00000000000000How to Use Open vSwitch with Docker ==================================== This document describes how to use Open vSwitch with Docker 1.9.0 or later. This document assumes that you installed Open vSwitch by following [INSTALL.md] or by using the distribution packages such as .deb or .rpm. Consult www.docker.com for instructions on how to install Docker. Docker 1.9.0 comes with support for multi-host networking. Integration of Docker networking and Open vSwitch can be achieved via Open vSwitch virtual network (OVN). Setup ===== For multi-host networking with OVN and Docker, Docker has to be started with a destributed key-value store. For e.g., if you decide to use consul as your distributed key-value store, and your host IP address is $HOST_IP, start your Docker daemon with: ``` docker daemon --cluster-store=consul://127.0.0.1:8500 \ --cluster-advertise=$HOST_IP:0 ``` OVN provides network virtualization to containers. OVN's integration with Docker currently works in two modes - the "underlay" mode or the "overlay" mode. In the "underlay" mode, OVN requires a OpenStack setup to provide container networking. In this mode, one can create logical networks and can have containers running inside VMs, standalone VMs (without having any containers running inside them) and physical machines connected to the same logical network. This is a multi-tenant, multi-host solution. In the "overlay" mode, OVN can create a logical network amongst containers running on multiple hosts. This is a single-tenant (extendable to multi-tenants depending on the security characteristics of the workloads), multi-host solution. In this mode, you do not need a pre-created OpenStack setup. For both the modes to work, a user has to install and start Open vSwitch in each VM/host that he plans to run his containers. The "overlay" mode ================== OVN in "overlay" mode needs a minimum Open vSwitch version of 2.5. * Start the central components. OVN architecture has a central component which stores your networking intent in a database. On one of your machines, with an IP Address of $CENTRAL_IP, where you have installed and started Open vSwitch, you will need to start some central components. Begin by making ovsdb-server listen on a TCP port by running: ``` ovs-appctl -t ovsdb-server ovsdb-server/add-remote ptcp:6640 ``` Start ovn-northd daemon. This daemon translates networking intent from Docker stored in the OVN_Northbound database to logical flows in OVN_Southbound database. ``` /usr/share/openvswitch/scripts/ovn-ctl start_northd ``` * One time setup. On each host, where you plan to spawn your containers, you will need to run the following command once. (You need to run it again if your OVS database gets cleared. It is harmless to run it again in any case.) $LOCAL_IP in the below command is the IP address via which other hosts can reach this host. This acts as your local tunnel endpoint. $ENCAP_TYPE is the type of tunnel that you would like to use for overlay networking. The options are "geneve" or "stt". (Please note that your kernel should have support for your chosen $ENCAP_TYPE. Both geneve and stt are part of the Open vSwitch kernel module that is compiled from this repo. If you use the Open vSwitch kernel module from upstream Linux, you will need a minumum kernel version of 3.18 for geneve. There is no stt support in upstream Linux. You can verify whether you have the support in your kernel by doing a "lsmod | grep $ENCAP_TYPE".) ``` ovs-vsctl set Open_vSwitch . external_ids:ovn-remote="tcp:$CENTRAL_IP:6640" \ external_ids:ovn-encap-ip=$LOCAL_IP external_ids:ovn-encap-type="$ENCAP_TYPE" ``` And finally, start the ovn-controller. (You need to run the below command on every boot) ``` /usr/share/openvswitch/scripts/ovn-ctl start_controller ``` * Start the Open vSwitch network driver. By default Docker uses Linux bridge for networking. But it has support for external drivers. To use Open vSwitch instead of the Linux bridge, you will need to start the Open vSwitch driver. The Open vSwitch driver uses the Python's flask module to listen to Docker's networking api calls. So, if your host does not have Python's flask module, install it with: ``` easy_install -U pip pip install Flask ``` Start the Open vSwitch driver on every host where you plan to create your containers. ``` ovn-docker-overlay-driver --detach ``` Docker has inbuilt primitives that closely match OVN's logical switches and logical port concepts. Please consult Docker's documentation for all the possible commands. Here are some examples. * Create your logical switch. To create a logical switch with name 'foo', on subnet '192.168.1.0/24' run: ``` NID=`docker network create -d openvswitch --subnet=192.168.1.0/24 foo` ``` * List your logical switches. ``` docker network ls ``` You can also look at this logical switch in OVN's northbound database by running the following command. ``` ovn-nbctl --db=tcp:$CENTRAL_IP:6640 lswitch-list ``` * Docker creates your logical port and attaches it to the logical network in a single step. For e.g., to attach a logical port to network 'foo' inside cotainer busybox, run: ``` docker run -itd --net=foo --name=busybox busybox ``` * List all your logical ports. Docker currently does not have a CLI command to list all your logical ports. But you can look at them in the OVN database, by running: ``` ovn-nbctl --db=tcp:$CENTRAL_IP:6640 lport-list $NID ``` * You can also create a logical port and attach it to a running container. ``` docker network create -d openvswitch --subnet=192.168.2.0/24 bar docker network connect bar busybox ``` You can delete your logical port and detach it from a running container by running: ``` docker network disconnect bar busybox ``` * You can delete your logical switch by running: ``` docker network rm bar ``` The "underlay" mode =================== This mode requires that you have a OpenStack setup pre-installed with OVN providing the underlay networking. * One time setup. A OpenStack tenant creates a VM with a single network interface (or multiple) that belongs to management logical networks. The tenant needs to fetch the port-id associated with the interface via which he plans to send the container traffic inside the spawned VM. This can be obtained by running the below command to fetch the 'id' associated with the VM. ``` nova list ``` and then by running: ``` neutron port-list --device_id=$id ``` Inside the VM, download the OpenStack RC file that contains the tenant information (henceforth referred to as 'openrc.sh'). Edit the file and add the previously obtained port-id information to the file by appending the following line: export OS_VIF_ID=$port_id. After this edit, the file will look something like: ``` #!/bin/bash export OS_AUTH_URL=http://10.33.75.122:5000/v2.0 export OS_TENANT_ID=fab106b215d943c3bad519492278443d export OS_TENANT_NAME="demo" export OS_USERNAME="demo" export OS_VIF_ID=e798c371-85f4-4f2d-ad65-d09dd1d3c1c9 ``` * Create the Open vSwitch bridge. If your VM has one ethernet interface (e.g.: 'eth0'), you will need to add that device as a port to an Open vSwitch bridge 'breth0' and move its IP address and route related information to that bridge. (If it has multiple network interfaces, you will need to create and attach an Open vSwitch bridge for the interface via which you plan to send your container traffic.) If you use DHCP to obtain an IP address, then you should kill the DHCP client that was listening on the physical Ethernet interface (e.g. eth0) and start one listening on the Open vSwitch bridge (e.g. breth0). Depending on your VM, you can make the above step persistent across reboots. For e.g.:, if your VM is Debian/Ubuntu, you can read [openvswitch-switch.README.Debian]. If your VM is RHEL based, you can read [README.RHEL] * Start the Open vSwitch network driver. The Open vSwitch driver uses the Python's flask module to listen to Docker's networking api calls. The driver also uses OpenStack's python-neutronclient libraries. So, if your host does not have Python's flask module or python-neutronclient install them with: ``` easy_install -U pip pip install python-neutronclient pip install Flask ``` Source the openrc file. e.g.: ```` . ./openrc.sh ``` Start the network driver and provide your OpenStack tenant password when prompted. ``` ovn-docker-underlay-driver --bridge breth0 --detach ``` From here-on you can use the same Docker commands as described in the section 'The "overlay" mode'. Please read 'man ovn-architecture' to understand OVN's architecture in detail. [INSTALL.md]: INSTALL.md [openvswitch-switch.README.Debian]: debian/openvswitch-switch.README.Debian [README.RHEL]: rhel/README.RHEL openvswitch-2.5.9/PaxHeaders.82075/build-aux0000644000000000000000000000013213534540120015454 xustar0030 mtime=1567801424.633852336 30 atime=1567801425.625859648 30 ctime=1567801424.633852336 openvswitch-2.5.9/build-aux/0000755000175000017500000000000013534540120017217 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/build-aux/PaxHeaders.82075/check-structs0000644000000000000000000000013213534540071020243 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801424.105848443 openvswitch-2.5.9/build-aux/check-structs0000755000175000017500000002125613534540071021742 0ustar00jpettitjpettit00000000000000#! /usr/bin/python import os.path import sys import re macros = {} anyWarnings = False types = {} types['char'] = {"size": 1, "alignment": 1} types['uint8_t'] = {"size": 1, "alignment": 1} types['ovs_be16'] = {"size": 2, "alignment": 2} types['ovs_be32'] = {"size": 4, "alignment": 4} types['ovs_be64'] = {"size": 8, "alignment": 8} types['ovs_32aligned_be64'] = {"size": 8, "alignment": 4} types['struct eth_addr'] = {"size": 6, "alignment": 1} token = None line = "" idRe = "[a-zA-Z_][a-zA-Z_0-9]*" tokenRe = "#?" + idRe + "|[0-9]+|." includeRe = re.compile(r'\s*#include\s+<(openflow/[^#]+)>') includePath = '' inComment = False inDirective = False inputStack = [] def getToken(): global token global line global inComment global inDirective global inputFile global fileName while True: line = line.lstrip() if line != "": if line.startswith("/*"): inComment = True line = line[2:] elif inComment: commentEnd = line.find("*/") if commentEnd < 0: line = "" else: inComment = False line = line[commentEnd + 2:] else: match = re.match(tokenRe, line) token = match.group(0) line = line[len(token):] if token.startswith('#'): inDirective = True elif token in macros and not inDirective: line = macros[token] + line continue return True elif inDirective: token = "$" inDirective = False return True else: global lineNumber while True: line = inputFile.readline() lineNumber += 1 while line.endswith("\\\n"): line = line[:-2] + inputFile.readline() lineNumber += 1 match = includeRe.match(line) if match: inputStack.append((fileName, inputFile, lineNumber)) inputFile = open(includePath + match.group(1)) lineNumber = 0 continue if line == "": if inputStack: fileName, inputFile, lineNumber = inputStack.pop() continue if token == None: fatal("unexpected end of input") token = None return False break def fatal(msg): sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg)) sys.exit(1) def warn(msg): global anyWarnings anyWarnings = True sys.stderr.write("%s:%d: warning: %s\n" % (fileName, lineNumber, msg)) def skipDirective(): getToken() while token != '$': getToken() def isId(s): return re.match(idRe + "$", s) != None def forceId(): if not isId(token): fatal("identifier expected") def forceInteger(): if not re.match('[0-9]+$', token): fatal("integer expected") def match(t): if token == t: getToken() return True else: return False def forceMatch(t): if not match(t): fatal("%s expected" % t) def parseTaggedName(): assert token in ('struct', 'union') name = token getToken() forceId() name = "%s %s" % (name, token) getToken() return name def parseTypeName(): if token in ('struct', 'union'): name = parseTaggedName() elif isId(token): name = token getToken() else: fatal("type name expected") if name in types: return name else: fatal("unknown type \"%s\"" % name) def parseStruct(): isStruct = token == 'struct' structName = parseTaggedName() if token == ";": return ofs = size = 0 alignment = 4 # ARM has minimum 32-bit alignment forceMatch('{') while not match('}'): typeName = parseTypeName() typeSize = types[typeName]['size'] typeAlignment = types[typeName]['alignment'] forceId() memberName = token getToken() if match('['): if token == ']': count = 0 else: forceInteger() count = int(token) getToken() forceMatch(']') else: count = 1 nBytes = typeSize * count if isStruct: if ofs % typeAlignment: shortage = typeAlignment - (ofs % typeAlignment) warn("%s member %s is %d bytes short of %d-byte alignment" % (structName, memberName, shortage, typeAlignment)) size += shortage ofs += shortage size += nBytes ofs += nBytes else: if nBytes > size: size = nBytes if typeAlignment > alignment: alignment = typeAlignment forceMatch(';') if size % alignment: shortage = alignment - (size % alignment) if (structName == "struct ofp10_packet_in" and shortage == 2 and memberName == 'data' and count == 0): # This is intentional pass else: warn("%s needs %d bytes of tail padding" % (structName, shortage)) size += shortage types[structName] = {"size": size, "alignment": alignment} return structName def checkStructs(): if len(sys.argv) < 2: sys.stderr.write("at least one non-option argument required; " "use --help for help") sys.exit(1) if '--help' in sys.argv: argv0 = os.path.basename(sys.argv[0]) print '''\ %(argv0)s, for checking struct and struct member alignment usage: %(argv0)s -Ipath HEADER [HEADER]... This program reads the header files specified on the command line and verifies that all struct members are aligned on natural boundaries without any need for the compiler to add additional padding. It also verifies that each struct's size is a multiple of 32 bits (because some ABIs for ARM require all structs to be a multiple of 32 bits), or 64 bits if the struct has any 64-bit members, again without the compiler adding additional padding. Finally, it checks struct size assertions using OFP_ASSERT. This program is specialized for reading Open vSwitch's OpenFlow header files. It will not work on arbitrary header files without extensions.\ ''' % {"argv0": argv0} sys.exit(0) global fileName for fileName in sys.argv[1:]: if fileName.startswith('-I'): global includePath includePath = fileName[2:] if not includePath.endswith('/'): includePath += '/' continue global inputFile global lineNumber inputFile = open(fileName) lineNumber = 0 lastStruct = None while getToken(): if token in ("#ifdef", "#ifndef", "#include", "#endif", "#elif", "#else"): skipDirective() elif token == "#define": getToken() name = token if line.startswith('('): skipDirective() else: definition = "" getToken() while token != '$': definition += token getToken() macros[name] = definition elif token == "enum": while token != ';': getToken() elif token in ('struct', 'union'): lastStruct = parseStruct() elif match('OFP_ASSERT') or match('BOOST_STATIC_ASSERT'): forceMatch('(') forceMatch('sizeof') forceMatch('(') typeName = parseTypeName() if typeName != lastStruct: warn("checking size of %s but %s was most recently defined" % (typeName, lastStruct)) forceMatch(')') forceMatch('=') forceMatch('=') forceInteger() size = int(token) getToken() forceMatch(')') if types[typeName]['size'] != size: warn("%s is %d bytes long but declared as %d" % ( typeName, types[typeName]['size'], size)) else: fatal("parse error") inputFile.close() if anyWarnings: sys.exit(1) if __name__ == '__main__': checkStructs() openvswitch-2.5.9/build-aux/PaxHeaders.82075/xml2nroff0000644000000000000000000000013213534540071017376 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.721845611 openvswitch-2.5.9/build-aux/xml2nroff0000755000175000017500000001015613534540071021072 0ustar00jpettitjpettit00000000000000#! /usr/bin/python # Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from datetime import date import getopt import os import sys import xml.dom.minidom from build.nroff import * argv0 = sys.argv[0] def usage(): print """\ %(argv0)s: XML to nroff converter Converts the XML format supplied as input into an nroff-formatted manpage. usage: %(argv0)s [OPTIONS] INPUT.XML [VAR=VALUE]... where INPUT.XML is a manpage in an OVS-specific XML format. Each VAR, when enclosed by "@"s in the input, is replaced by its corresponding VALUE, with characters &<>"' in VALUE escaped. The following options are also available: --version=VERSION use VERSION to display on document footer -h, --help display this help message\ """ % {'argv0': argv0} sys.exit(0) def manpage_to_nroff(xml_file, subst, version=None): f = open(xml_file) input = [] for line in f: for k, v in subst.iteritems(): line = line.replace(k, v) input += [line] doc = xml.dom.minidom.parseString(''.join(input)).documentElement d = date.fromtimestamp(os.stat(xml_file).st_mtime) if version == None: version = "UNKNOWN" program = doc.attributes['program'].nodeValue title = doc.attributes['title'].nodeValue section = doc.attributes['section'].nodeValue # Putting '\" p as the first line tells "man" that the manpage # needs to be preprocessed by "pic". s = r''''\" p .\" -*- nroff -*- .TH "%s" %s "%s" "Open vSwitch %s" "Open vSwitch Manual" .fp 5 L CR \\" Make fixed-width font available as \\fL. .de TQ . br . ns . TP "\\$1" .. .de ST . PP . RS -0.15in . I "\\$1" . RE .. ''' % (text_to_nroff(program), text_to_nroff(section), text_to_nroff(title), text_to_nroff(version)) s += block_xml_to_nroff(doc.childNodes) + "\n" return s def usage(): print """\ %(argv0)s: converts XML in a somewhat HTML-like format to nroff usage: %(argv0)s [OPTIONS] XML where XML is documentation in a somewhat HTML-like XML format. The manpage, in nroff "man" format, is output on stdout. The following options are also available: --version=VERSION use VERSION to display on document footer -h, --help display this help message\ """ % {'argv0': argv0} sys.exit(0) if __name__ == "__main__": try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['version=', 'help']) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (argv0, geo.msg)) sys.exit(1) er_diagram = None title = None version = None for key, value in options: if key == '--version': version = value elif key in ['-h', '--help']: usage() else: sys.exit(0) if len(args) < 1: sys.stderr.write("%s: exactly 1 non-option arguments required " "(use --help for help)\n" % argv0) sys.exit(1) subst = {} for s in args[1:]: var, value = s.split('=', 1) value = value.replace('&', '&') value = value.replace('<', '<') value = value.replace('>', '>') value = value.replace('"', '"') value = value.replace("'", ''') subst['@%s@' % var] = value try: s = manpage_to_nroff(args[0], subst, version) except error.Error, e: sys.stderr.write("%s: %s\n" % (argv0, e.msg)) sys.exit(1) for line in s.splitlines(): line = line.strip() if line: print line # Local variables: # mode: python # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-odp-netlink-h0000644000000000000000000000013213534540071021602 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801424.105848443 openvswitch-2.5.9/build-aux/extract-odp-netlink-h0000755000175000017500000000247213534540071023300 0ustar00jpettitjpettit00000000000000# This is a "sed" script that transforms into a # form that is suitable for inclusion within the Open vSwitch tree on # both Linux and non-Linux systems. # Add a header warning that this is a generated file. It might save somebody # some frustration (maybe even me!). 1i\ /* -*- mode: c; buffer-read-only: t -*- */\ /* Generated automatically from -- do not modify! */\ \ \ # Avoid using reserved names in header guards. s/_LINUX_OPENVSWITCH_H/ODP_NETLINK_H/ # Include platform extensions header file on Win32. $i\ #ifdef _WIN32\ #include "OvsDpInterfaceExt.h"\ #endif\ # Use OVS's own struct eth_addr instead of a 6-byte char array. s,,"openvswitch/types.h", s,#.*,, s/__u8[[:space:]]*\([a-zA-Z0-9_]*\)[[:space:]]*\[[[:space:]]*ETH_ALEN[[:space:]]*\]/struct eth_addr \1/ # Transform most Linux-specific __u types into C99 uint_t types, # and most Linux-specific __be into Open vSwitch ovs_be, # and use the appropriate userspace header. s/__u32/uint32_t/g s/__u16/uint16_t/g s/__u8/uint8_t/g s/__be32/ovs_be32/g s/__be16/ovs_be16/g # Transform 64-bit Linux-specific types into Open vSwitch specialized # types for 64-bit numbers that might only be aligned on a 32-bit # boundary. s/__u64/ovs_32aligned_u64/g s/__be64/ovs_32aligned_be64/g openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-ofp-msgs0000644000000000000000000000013213534540071020664 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.801846201 openvswitch-2.5.9/build-aux/extract-ofp-msgs0000755000175000017500000003237313534540071022365 0ustar00jpettitjpettit00000000000000#! /usr/bin/python import sys import os.path import re line = "" # Maps from user-friendly version number to its protocol encoding. VERSION = {"1.0": 0x01, "1.1": 0x02, "1.2": 0x03, "1.3": 0x04, "1.4": 0x05, "1.5": 0x06} NX_VENDOR_ID = 0x00002320 ONF_VENDOR_ID = 0x4f4e4600 OFPT_VENDOR = 4 OFPT10_STATS_REQUEST = 16 OFPT10_STATS_REPLY = 17 OFPT11_STATS_REQUEST = 18 OFPT11_STATS_REPLY = 19 OFPST_VENDOR = 0xffff def decode_version_range(range): if range in VERSION: return (VERSION[range], VERSION[range]) elif range.endswith('+'): return (VERSION[range[:-1]], max(VERSION.values())) elif range == '': return (0x01, 0xff) else: a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups() return (VERSION[a], VERSION[b]) def get_line(): global line global line_number line = input_file.readline() line_number += 1 if line == "": fatal("unexpected end of input") n_errors = 0 def error(msg): global n_errors sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) n_errors += 1 def fatal(msg): error(msg) sys.exit(1) def usage(): argv0 = os.path.basename(sys.argv[0]) print '''\ %(argv0)s, for extracting OpenFlow message types from header files usage: %(argv0)s INPUT OUTPUT where INPUT is the name of the input header file and OUTPUT is the output file name. Despite OUTPUT, the output is written to stdout, and the OUTPUT argument only controls #line directives in the output.\ ''' % {"argv0": argv0} sys.exit(0) def make_sizeof(s): m = re.match(r'(.*) up to (.*)', s) if m: struct, member = m.groups() return "offsetof(%s, %s)" % (struct, member) else: return "sizeof(%s)" % s def extract_ofp_msgs(output_file_name): raw_types = [] all_hdrs = {} all_raws = {} all_raws_order = [] while True: get_line() if re.match('enum ofpraw', line): break while True: get_line() first_line_number = line_number here = '%s:%d' % (file_name, line_number) if (line.startswith('/*') or line.startswith(' *') or not line or line.isspace()): continue elif re.match('}', line): break if not line.lstrip().startswith('/*'): fatal("unexpected syntax between ofpraw types") comment = line.lstrip()[2:].strip() while not comment.endswith('*/'): get_line() if line.startswith('/*') or not line or line.isspace(): fatal("unexpected syntax within message") comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') comment = comment[:-2].rstrip() m = re.match(r'([A-Z]+) ([-.+\d]+|) \((\d+)\): ([^.]+)\.$', comment) if not m: fatal("unexpected syntax between messages") type_, versions, number, contents = m.groups() number = int(number) get_line() m = re.match('\s+(?:OFPRAW_%s)(\d*)_([A-Z0-9_]+),?$' % type_, line) if not m: fatal("syntax error expecting OFPRAW_ enum") vinfix, name = m.groups() rawname = 'OFPRAW_%s%s_%s' % (type_, vinfix, name) min_version, max_version = decode_version_range(versions) human_name = '%s_%s' % (type_, name) if type_.endswith('ST'): if rawname.endswith('_REQUEST'): human_name = human_name[:-8] + " request" elif rawname.endswith('_REPLY'): human_name = human_name[:-6] + " reply" else: fatal("%s messages are statistics but %s doesn't end " "in _REQUEST or _REPLY" % (type_, rawname)) these_hdrs = [] for version in range(min_version, max_version + 1): if type_ == 'OFPT': if number == OFPT_VENDOR: fatal("OFPT (%d) is used for vendor extensions" % number) elif (version == VERSION["1.0"] and (number == OFPT10_STATS_REQUEST or number == OFPT10_STATS_REPLY)): fatal("OFPT 1.0 (%d) is used for stats messages" % number) elif (version != VERSION["1.0"] and (number == OFPT11_STATS_REQUEST or number == OFPT11_STATS_REPLY)): fatal("OFPT 1.1+ (%d) is used for stats messages" % number) hdrs = (version, number, 0, 0, 0) elif type_ == 'OFPST' and name.endswith('_REQUEST'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REQUEST, number, 0, 0) else: hdrs = (version, OFPT11_STATS_REQUEST, number, 0, 0) elif type_ == 'OFPST' and name.endswith('_REPLY'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REPLY, number, 0, 0) else: hdrs = (version, OFPT11_STATS_REPLY, number, 0, 0) elif type_ == 'ONF': hdrs = (version, OFPT_VENDOR, 0, ONF_VENDOR_ID, number) elif type_ == 'ONFST' and name.endswith('_REQUEST'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR, ONF_VENDOR_ID, number) else: hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR, ONF_VENDOR_ID, number) elif type_ == 'ONFST' and name.endswith('_REPLY'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR, ONF_VENDOR_ID, number) else: hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR, ONF_VENDOR_ID, number) elif type_ == 'NXT': hdrs = (version, OFPT_VENDOR, 0, NX_VENDOR_ID, number) elif type_ == 'NXST' and name.endswith('_REQUEST'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REQUEST, OFPST_VENDOR, NX_VENDOR_ID, number) else: hdrs = (version, OFPT11_STATS_REQUEST, OFPST_VENDOR, NX_VENDOR_ID, number) elif type_ == 'NXST' and name.endswith('_REPLY'): if version == VERSION["1.0"]: hdrs = (version, OFPT10_STATS_REPLY, OFPST_VENDOR, NX_VENDOR_ID, number) else: hdrs = (version, OFPT11_STATS_REPLY, OFPST_VENDOR, NX_VENDOR_ID, number) else: fatal("type '%s' unknown" % type_) if hdrs in all_hdrs: error("Duplicate message definition for %s." % str(hdrs)) sys.stderr.write("%s: Here is the location " "of the previous definition.\n" % (all_hdrs[hdrs])) all_hdrs[hdrs] = here these_hdrs.append(hdrs) extra_multiple = '0' if contents == 'void': min_body = '0' else: min_body_elem = [] for c in [s.strip() for s in contents.split(",")]: if c.endswith('[]'): if extra_multiple == '0': extra_multiple = make_sizeof(c[:-2]) else: error("Cannot have multiple [] elements") else: min_body_elem.append(c) if min_body_elem: min_body = " + ".join([make_sizeof(s) for s in min_body_elem]) else: if extra_multiple == '0': error("Must specify contents (use 'void' if empty)") min_body = 0 if rawname in all_raws: fatal("%s: Duplicate name" % rawname) all_raws[rawname] = {"hdrs": these_hdrs, "min_version": min_version, "max_version": max_version, "min_body": min_body, "extra_multiple": extra_multiple, "type": type_, "human_name": human_name, "line": first_line_number} all_raws_order.append(rawname) continue while True: get_line() if re.match('enum ofptype', line): break all_types = [] while True: get_line() if re.match(r'\s*/?\*', line) or line.isspace(): continue elif re.match('}', line): break if not re.match(r'\s*OFPTYPE_.*/\*', line): fatal("unexpected syntax between OFPTYPE_ definitions") syntax = line.strip() while not syntax.endswith('*/'): get_line() if not line.strip().startswith('*'): fatal("unexpected syntax within OFPTYPE_ definition") syntax += ' %s' % line.strip().lstrip('* \t') syntax = syntax.strip() m = re.match(r'(OFPTYPE_[A-Z0-9_]+),\s*/\* (.*) \*/', syntax) if not m: fatal("syntax error in OFPTYPE_ definition") ofptype, raws_ = m.groups() raws = [s.rstrip('.') for s in raws_.split()] for raw in raws: if not re.match('OFPRAW_[A-Z0-9_]+$', raw): fatal("%s: invalid OFPRAW_* name syntax" % raw) if raw not in all_raws: fatal("%s: not a declared OFPRAW_* name" % raw) if "ofptype" in all_raws[raw]: fatal("%s: already part of %s" % (raw, all_raws[raw]["ofptype"])) all_raws[raw]["ofptype"] = ofptype all_types.append(all_raws[raws[0]]["human_name"]) input_file.close() if n_errors: sys.exit(1) output = [] output.append("/* Generated automatically; do not modify! " "-*- buffer-read-only: t -*- */") output.append("") for raw in all_raws_order: r = all_raws[raw] output.append("static struct raw_instance %s_instances[] = {" % raw.lower()) for hdrs in r['hdrs']: output.append(" { {0, NULL}, {%d, %d, %d, 0x%x, %d}, %s, 0 }," % (hdrs + (raw,))) output.append("};") output.append("") output.append("static struct raw_info raw_infos[] = {") for raw in all_raws_order: r = all_raws[raw] if "ofptype" not in r: error("%s: no defined OFPTYPE_" % raw) continue output.append(" {") output.append(" %s_instances," % raw.lower()) output.append(" %d, %d," % (r["min_version"], r["max_version"])) output.append("#line %s \"%s\"" % (r["line"], file_name)) output.append(" %s," % r["min_body"]) output.append("#line %s \"%s\"" % (r["line"], file_name)) output.append(" %s," % r["extra_multiple"]) output.append("#line %s \"%s\"" % (len(output) + 2, output_file_name)) output.append(" %s," % r["ofptype"]) output.append(" \"%s\"," % r["human_name"]) output.append(" },") if r['type'].endswith("ST"): for hdrs in r['hdrs']: op_hdrs = list(hdrs) if hdrs[0] == VERSION["1.0"]: if hdrs[1] == OFPT10_STATS_REQUEST: op_hdrs[1] = OFPT10_STATS_REPLY elif hdrs[1] == OFPT10_STATS_REPLY: op_hdrs[1] = OFPT10_STATS_REQUEST else: assert False else: if hdrs[1] == OFPT11_STATS_REQUEST: op_hdrs[1] = OFPT11_STATS_REPLY elif hdrs[1] == OFPT11_STATS_REPLY: op_hdrs[1] = OFPT11_STATS_REQUEST else: assert False if tuple(op_hdrs) not in all_hdrs: if r["human_name"].endswith("request"): fatal("%s has no corresponding reply" % r["human_name"]) else: fatal("%s has no corresponding request" % r["human_name"]) output.append("};") output.append(""); output.append("static const char *type_names[] = {"); for t in all_types: output.append(" \"%s\"," % t) output.append("};") if n_errors: sys.exit(1) return output if __name__ == '__main__': if '--help' in sys.argv: usage() elif len(sys.argv) != 3: sys.stderr.write("exactly two non-option arguments required; " "use --help for help\n") sys.exit(1) else: global file_name global input_file global line_number file_name = sys.argv[1] input_file = open(file_name) line_number = 0 for line in extract_ofp_msgs(sys.argv[2]): print line openvswitch-2.5.9/build-aux/PaxHeaders.82075/sodepends.pl0000644000000000000000000000013213534540071020057 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.717845583 openvswitch-2.5.9/build-aux/sodepends.pl0000644000175000017500000000357613534540071021560 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008, 2011 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; use Getopt::Long; our ($exit_code) = 0; our (@include_dirs); Getopt::Long::Configure ("bundling"); GetOptions("I|include=s" => \@include_dirs) or exit(1); @include_dirs = ('.') if !@include_dirs; sub find_file { my ($name) = @_; foreach my $dir (@include_dirs, '.') { my $file = "$dir/$name"; if (stat($file)) { return $file; } } print STDERR "$name not found in: ", join(' ', @include_dirs), "\n"; $exit_code = 1; return; } print "# Generated automatically -- do not modify! -*- buffer-read-only: t -*-\n"; for my $toplevel (sort(@ARGV)) { # Skip names that don't end in .in. next if $toplevel !~ /\.in$/; # Open file. my ($fn) = find_file($toplevel); next if !defined($fn); if (!open(OUTER, '<', $fn)) { print "$fn: open: $!\n"; $exit_code = 1; next; } my (@dependencies); OUTER: while () { if (my ($name) = /^\.so (\S+)$/) { push(@dependencies, $name) if find_file($name); } } close(OUTER); my ($output) = $toplevel; $output =~ s/\.in//; print "\n$output:"; print " \\\n\t$_" foreach $toplevel, sort(@dependencies); print "\n"; print "$_:\n" foreach $toplevel, sort(@dependencies); } exit $exit_code; openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-ofp-errors0000644000000000000000000000013213534540071021227 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.801846201 openvswitch-2.5.9/build-aux/extract-ofp-errors0000755000175000017500000003277713534540071022740 0ustar00jpettitjpettit00000000000000#! /usr/bin/python import sys import os.path import re macros = {} # Map from OpenFlow version number to version ID used in ofp_header. version_map = {"1.0": 0x01, "1.1": 0x02, "1.2": 0x03, "1.3": 0x04, "1.4": 0x05, "1.5": 0x06} version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) token = None line = "" idRe = "[a-zA-Z_][a-zA-Z_0-9]*" tokenRe = "#?" + idRe + "|[0-9]+|." inComment = False inDirective = False def open_file(fn): global fileName global inputFile global lineNumber fileName = fn inputFile = open(fileName) lineNumber = 0 def tryGetLine(): global inputFile global line global lineNumber line = inputFile.readline() lineNumber += 1 return line != "" def getLine(): if not tryGetLine(): fatal("unexpected end of input") def getToken(): global token global line global inComment global inDirective while True: line = line.lstrip() if line != "": if line.startswith("/*"): inComment = True line = line[2:] elif inComment: commentEnd = line.find("*/") if commentEnd < 0: line = "" else: inComment = False line = line[commentEnd + 2:] else: match = re.match(tokenRe, line) token = match.group(0) line = line[len(token):] if token.startswith('#'): inDirective = True elif token in macros and not inDirective: line = macros[token] + line continue return True elif inDirective: token = "$" inDirective = False return True else: global lineNumber line = inputFile.readline() lineNumber += 1 while line.endswith("\\\n"): line = line[:-2] + inputFile.readline() lineNumber += 1 if line == "": if token == None: fatal("unexpected end of input") token = None return False n_errors = 0 def error(msg): global n_errors sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg)) n_errors += 1 def fatal(msg): error(msg) sys.exit(1) def skipDirective(): getToken() while token != '$': getToken() def isId(s): return re.match(idRe + "$", s) != None def forceId(): if not isId(token): fatal("identifier expected") def forceInteger(): if not re.match('[0-9]+$', token): fatal("integer expected") def match(t): if token == t: getToken() return True else: return False def forceMatch(t): if not match(t): fatal("%s expected" % t) def parseTaggedName(): assert token in ('struct', 'union') name = token getToken() forceId() name = "%s %s" % (name, token) getToken() return name def print_enum(tag, constants, storage_class): print (""" %(storage_class)sconst char * %(tag)s_to_string(uint16_t value) { switch (value) {\ """ % {"tag": tag, "bufferlen": len(tag) + 32, "storage_class": storage_class}) for constant in constants: print (" case %s: return \"%s\";" % (constant, constant)) print ("""\ } return NULL; }\ """ % {"tag": tag}) def usage(): argv0 = os.path.basename(sys.argv[0]) print ('''\ %(argv0)s, for extracting OpenFlow error codes from header files usage: %(argv0)s ERROR_HEADER VENDOR_HEADER This program reads VENDOR_HEADER to obtain OpenFlow vendor (aka experimenter IDs), then ERROR_HEADER to obtain OpenFlow error number. It outputs a C source file for translating OpenFlow error codes into strings. ERROR_HEADER should point to lib/ofp-errors.h. VENDOR_HEADER should point to include/openflow/openflow-common.h. The output is suitable for use as lib/ofp-errors.inc.\ ''' % {"argv0": argv0}) sys.exit(0) def extract_vendor_ids(fn): global vendor_map vendor_map = {} vendor_loc = {} open_file(fn) while tryGetLine(): m = re.match(r'#define\s+([A-Z0-9_]+)_VENDOR_ID\s+(0x[0-9a-fA-F]+|[0-9]+)', line) if not m: continue name = m.group(1) id_ = int(m.group(2), 0) if name in vendor_map: error("%s: duplicate definition of vendor" % name) sys.stderr.write("%s: Here is the location of the previous " "definition.\n" % vendor_loc[name]) sys.exit(1) vendor_map[name] = id_ vendor_loc[name] = "%s:%d" % (fileName, lineNumber) if not vendor_map: fatal("%s: no vendor definitions found" % fn) inputFile.close() vendor_reverse_map = {} for name, id_ in vendor_map.items(): if id_ in vendor_reverse_map: fatal("%s: duplicate vendor id for vendors %s and %s" % (id_, vendor_reverse_map[id_], name)) vendor_reverse_map[id_] = name def extract_ofp_errors(fn): error_types = {} comments = [] names = [] domain = {} reverse = {} for domain_name in version_map.values(): domain[domain_name] = {} reverse[domain_name] = {} n_errors = 0 expected_errors = {} open_file(fn) while True: getLine() if re.match('enum ofperr', line): break while True: getLine() if line.startswith('/*') or not line or line.isspace(): continue elif re.match('}', line): break if not line.lstrip().startswith('/*'): fatal("unexpected syntax between errors") comment = line.lstrip()[2:].strip() while not comment.endswith('*/'): getLine() if line.startswith('/*') or not line or line.isspace(): fatal("unexpected syntax within error") comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') comment = comment[:-2].rstrip() m = re.match('Expected: (.*)\.$', comment) if m: expected_errors[m.group(1)] = (fileName, lineNumber) continue m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment) if not m: fatal("unexpected syntax between errors") dsts, comment = m.groups() getLine() m = re.match('\s+(?:OFPERR_([A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,', line) if not m: fatal("syntax error expecting enum value") enum = m.group(1) if enum in names: fatal("%s specified twice" % enum) comments.append(re.sub('\[[^]]*\]', '', comment)) names.append(enum) for dst in dsts.split(', '): m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?\((\d+)(?:,(\d+))?\)$', dst) if not m: fatal("%r: syntax error in destination" % dst) vendor_name = m.group(1) version1_name = m.group(2) version2_name = m.group(3) type_ = int(m.group(4)) if m.group(5): code = int(m.group(5)) else: code = None if vendor_name not in vendor_map: fatal("%s: unknown vendor" % vendor_name) vendor = vendor_map[vendor_name] if version1_name not in version_map: fatal("%s: unknown OpenFlow version" % version1_name) v1 = version_map[version1_name] if version2_name is None: v2 = v1 elif version2_name == "+": v2 = max(version_map.values()) elif version2_name[1:] not in version_map: fatal("%s: unknown OpenFlow version" % version2_name[1:]) else: v2 = version_map[version2_name[1:]] if v2 < v1: fatal("%s%s: %s precedes %s" % (version1_name, version2_name, version2_name, version1_name)) if vendor == vendor_map['OF']: # All standard OpenFlow errors have a type and a code. if code is None: fatal("%s: %s domain requires code" % (dst, vendor_name)) elif vendor == vendor_map['NX']: # Before OpenFlow 1.2, OVS used a Nicira extension to # define errors that included a type and a code. # # In OpenFlow 1.2 and later, Nicira extension errors # are defined using the OpenFlow experimenter error # mechanism that includes a type but not a code. if v1 < version_map['1.2'] or v2 < version_map['1.2']: if code is None: fatal("%s: NX1.0 and NX1.1 domains require code" % (dst, vendor_name)) if v1 >= version_map['1.2'] or v2 >= version_map['1.2']: if code is not None: fatal("%s: NX1.2+ domains do not have codes" % dst) else: # Experimenter extension error for OF1.2+ only. if v1 < version_map['1.2']: fatal("%s: %s domain not supported before OF1.2" % (dst, vendor_name)) if code is not None: fatal("%s: %s domains do not have codes" % (dst, vendor_name)) if code is None: code = 0 for version in range(v1, v2 + 1): domain[version].setdefault(vendor, {}) domain[version][vendor].setdefault(type_, {}) if code in domain[version][vendor][type_]: msg = "%#x,%d,%d in OF%s means both %s and %s" % ( vendor, type_, code, version_reverse_map[version], domain[version][vendor][type_][code][0], enum) if msg in expected_errors: del expected_errors[msg] else: error("%s: %s." % (dst, msg)) sys.stderr.write("%s:%d: %s: Here is the location " "of the previous definition.\n" % (domain[version][vendor][type_][code][1], domain[version][vendor][type_][code][2], dst)) else: domain[version][vendor][type_][code] = (enum, fileName, lineNumber) assert enum not in reverse[version] reverse[version][enum] = (vendor, type_, code) inputFile.close() for fn, ln in expected_errors.values(): sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln)) n_errors += 1 if n_errors: sys.exit(1) print ("""\ /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ #define OFPERR_N_ERRORS %d struct ofperr_domain { const char *name; uint8_t version; enum ofperr (*decode)(uint32_t vendor, uint16_t type, uint16_t code); struct triplet errors[OFPERR_N_ERRORS]; }; static const char *error_names[OFPERR_N_ERRORS] = { %s }; static const char *error_comments[OFPERR_N_ERRORS] = { %s };\ """ % (len(names), '\n'.join(' "%s",' % name for name in names), '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment) for comment in comments))) def output_domain(map, name, description, version): print (""" static enum ofperr %s_decode(uint32_t vendor, uint16_t type, uint16_t code) { switch (((uint64_t) vendor << 32) | (uint32_t) (type << 16) | code) {""" % name) found = set() for enum in names: if enum not in map: continue vendor, type_, code = map[enum] value = (vendor << 32) | (type_ << 16) | code if value in found: continue found.add(value) if vendor: vendor_s = "(%#xULL << 32) | " % vendor else: vendor_s = "" print (" case %s(uint32_t) (%d << 16) | %d:" % (vendor_s, type_, code)) print (" return OFPERR_%s;" % enum) print ("""\ } return 0; }""") print (""" static const struct ofperr_domain %s = { "%s", %d, %s_decode, {""" % (name, description, version, name)) for enum in names: if enum in map: vendor, type_, code = map[enum] if code == None: code = -1 print " { %#8x, %2d, %3d }, /* %s */" % (vendor, type_, code, enum) else: print (" { -1, -1, -1 }, /* %s */" % enum) print ("""\ }, };""") for version_name, id_ in version_map.items(): var = 'ofperr_of' + re.sub('[^A-Za-z0-9_]', '', version_name) description = "OpenFlow %s" % version_name output_domain(reverse[id_], var, description, id_) if __name__ == '__main__': if '--help' in sys.argv: usage() elif len(sys.argv) != 3: sys.stderr.write("exactly two non-options arguments required; " "use --help for help\n") sys.exit(1) else: extract_vendor_ids(sys.argv[2]) extract_ofp_errors(sys.argv[1]) openvswitch-2.5.9/build-aux/PaxHeaders.82075/config.guess0000644000000000000000000000013113534540101020044 xustar0029 mtime=1567801409.20173858 30 atime=1567801410.925751285 30 ctime=1567801424.629852305 openvswitch-2.5.9/build-aux/config.guess0000755000175000017500000012637313534540101021552 0ustar00jpettitjpettit00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-24' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > "$dummy.c" ; for c in cc gcc c89 c99 ; do if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "$UNAME_SYSTEM" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval "$set_cc_for_build" cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" # If ldd exists, use it to detect musl libc. if command -v ldd >/dev/null && \ ldd --version 2>&1 | grep -q ^musl then LIBC=musl fi ;; esac # Note: order is significant - the case branches are not exclusive. case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ "/sbin/$sysctl" 2>/dev/null || \ "/usr/sbin/$sysctl" 2>/dev/null || \ echo unknown)` case "$UNAME_MACHINE_ARCH" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine="${arch}${endian}"-unknown ;; *) machine="$UNAME_MACHINE_ARCH"-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "$UNAME_MACHINE_ARCH" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval "$set_cc_for_build" if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "$UNAME_MACHINE_ARCH" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "$UNAME_VERSION" in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "$machine-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" exit ;; *:MidnightBSD:*:*) echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" exit ;; *:ekkoBSD:*:*) echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" exit ;; *:SolidBSD:*:*) echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:MirBSD:*:*) echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" exit ;; *:Sortix:*:*) echo "$UNAME_MACHINE"-unknown-sortix exit ;; *:Redox:*:*) echo "$UNAME_MACHINE"-unknown-redox exit ;; mips:OSF1:*.*) echo mips-dec-osf1 exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo "$UNAME_MACHINE"-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix"$UNAME_RELEASE" exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval "$set_cc_for_build" SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos"$UNAME_RELEASE" exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos"$UNAME_RELEASE" ;; sun4) echo sparc-sun-sunos"$UNAME_RELEASE" ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos"$UNAME_RELEASE" exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint"$UNAME_RELEASE" exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint"$UNAME_RELEASE" exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint"$UNAME_RELEASE" exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint"$UNAME_RELEASE" exit ;; m68k:machten:*:*) echo m68k-apple-machten"$UNAME_RELEASE" exit ;; powerpc:machten:*:*) echo powerpc-apple-machten"$UNAME_RELEASE" exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix"$UNAME_RELEASE" exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix"$UNAME_RELEASE" exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos"$UNAME_RELEASE" exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] then if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ [ "$TARGET_BINARY_INTERFACE"x = x ] then echo m88k-dg-dgux"$UNAME_RELEASE" else echo m88k-dg-dguxbcs"$UNAME_RELEASE" fi else echo i586-dg-dgux"$UNAME_RELEASE" fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" fi echo "$IBM_ARCH"-ibm-aix"$IBM_REV" exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` case "$UNAME_MACHINE" in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "$sc_cpu_version" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "$sc_kernel_bits" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "$HP_ARCH" = "" ]; then eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ "$HP_ARCH" = hppa2.0w ] then eval "$set_cc_for_build" # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo "$HP_ARCH"-hp-hpux"$HPUX_REV" exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo "$UNAME_MACHINE"-unknown-osf1mk else echo "$UNAME_MACHINE"-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi"$UNAME_RELEASE" exit ;; *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; i*:CYGWIN*:*) echo "$UNAME_MACHINE"-pc-cygwin exit ;; *:MINGW64*:*) echo "$UNAME_MACHINE"-pc-mingw64 exit ;; *:MINGW*:*) echo "$UNAME_MACHINE"-pc-mingw32 exit ;; *:MSYS*:*) echo "$UNAME_MACHINE"-pc-msys exit ;; i*:PW*:*) echo "$UNAME_MACHINE"-pc-pw32 exit ;; *:Interix*:*) case "$UNAME_MACHINE" in x86) echo i586-pc-interix"$UNAME_RELEASE" exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix"$UNAME_RELEASE" exit ;; IA64) echo ia64-unknown-interix"$UNAME_RELEASE" exit ;; esac ;; i*:UWIN*:*) echo "$UNAME_MACHINE"-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" exit ;; *:GNU:*:*) # the GNU system echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; i*86:Minix:*:*) echo "$UNAME_MACHINE"-pc-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) eval "$set_cc_for_build" if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi else echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf fi fi exit ;; avr32*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; cris:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; crisv32:Linux:*:*) echo "$UNAME_MACHINE"-axis-linux-"$LIBC" exit ;; e2k:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; frv:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; hexagon:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:Linux:*:*) echo "$UNAME_MACHINE"-pc-linux-"$LIBC" exit ;; ia64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; k1om:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m32r*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; m68*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval "$set_cc_for_build" sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } ;; mips64el:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-"$LIBC" exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; padre:Linux:*:*) echo sparc-unknown-linux-"$LIBC" exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-"$LIBC" exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; *) echo hppa-unknown-linux-"$LIBC" ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-"$LIBC" exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-"$LIBC" exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-"$LIBC" exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-"$LIBC" exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" exit ;; sh64*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sh*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; tile*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; vax:Linux:*:*) echo "$UNAME_MACHINE"-dec-linux-"$LIBC" exit ;; x86_64:Linux:*:*) if objdump -f /bin/sh | grep -q elf32-x86-64; then echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32 else echo "$UNAME_MACHINE"-pc-linux-"$LIBC" fi exit ;; xtensa*:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo "$UNAME_MACHINE"-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo "$UNAME_MACHINE"-unknown-stop exit ;; i*86:atheos:*:*) echo "$UNAME_MACHINE"-unknown-atheos exit ;; i*86:syllable:*:*) echo "$UNAME_MACHINE"-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos"$UNAME_RELEASE" exit ;; i*86:*DOS:*:*) echo "$UNAME_MACHINE"-pc-msdosdjgpp exit ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" else echo "$UNAME_MACHINE"-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos"$UNAME_RELEASE" exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos"$UNAME_RELEASE" exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos"$UNAME_RELEASE" exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos"$UNAME_RELEASE" exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv"$UNAME_RELEASE" exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo "$UNAME_MACHINE"-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo "$UNAME_MACHINE"-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux"$UNAME_RELEASE" exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv"$UNAME_RELEASE" else echo mips-unknown-sysv"$UNAME_RELEASE" fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux"$UNAME_RELEASE" exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux"$UNAME_RELEASE" exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux"$UNAME_RELEASE" exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux"$UNAME_RELEASE" exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux"$UNAME_RELEASE" exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux"$UNAME_RELEASE" exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux"$UNAME_RELEASE" exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Rhapsody:*:*) echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval "$set_cc_for_build" if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk"$UNAME_RELEASE" exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk"$UNAME_RELEASE" exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk"$UNAME_RELEASE" exit ;; NSV-*:NONSTOP_KERNEL:*:*) echo nsv-tandem-nsk"$UNAME_RELEASE" exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk"$UNAME_RELEASE" exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo "$UNAME_MACHINE"-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux"$UNAME_RELEASE" exit ;; *:DragonFly:*:*) echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "$UNAME_MACHINE" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" exit ;; i*86:rdos:*:*) echo "$UNAME_MACHINE"-pc-rdos exit ;; i*86:AROS:*:*) echo "$UNAME_MACHINE"-pc-aros exit ;; x86_64:VMkernel:*:*) echo "$UNAME_MACHINE"-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac echo "$0: unable to guess system type" >&2 case "$UNAME_MACHINE:$UNAME_SYSTEM" in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/missing0000644000000000000000000000013113534540101017123 xustar0029 mtime=1567801409.20573861 30 atime=1567801409.609741588 30 ctime=1567801424.633852336 openvswitch-2.5.9/build-aux/missing0000755000175000017500000001533013534540101020617 0ustar00jpettitjpettit00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2013-10-28.13; # UTC # Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/install-sh0000644000000000000000000000013013534540101017527 xustar0029 mtime=1567801409.20573861 29 atime=1567801409.20573861 30 ctime=1567801424.633852336 openvswitch-2.5.9/build-aux/install-sh0000755000175000017500000003546313534540101021235 0ustar00jpettitjpettit00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2014-09-12.12; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # $RANDOM is not portable (e.g. dash); use it when possible to # lower collision chance tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # As "mkdir -p" follows symlinks and we work in /tmp possibly; so # create the $tmpdir first (and fail if unsuccessful) to make sure # that nobody tries to guess the $tmpdir name. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/thread-safety-blacklist0000644000000000000000000000013213534540071022167 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.785846083 openvswitch-2.5.9/build-aux/thread-safety-blacklist0000644000175000017500000000214313534540071023655 0ustar00jpettitjpettit00000000000000\basctime( \bbasename( \bcatgets( \bcrypt( \bctermid( \bctime( \bdbm_clearerr( \bdbm_close( \bdbm_delete( \bdbm_error( \bdbm_fetch( \bdbm_firstkey( \bdbm_nextkey( \bdbm_open( \bdbm_store( \bdirname( \bdlerror( \bdrand48( \becvt( \bencrypt( \bendgrent( \bendpwent( \bendutxent( \bfcvt( \bftw( \bgcvt( \bgetc_unlocked( \bgetchar_unlocked( \bgetdate( \bgetgrent( \bgetgrgid( \bgetgrnam( \bgethostbyaddr( \bgethostbyname( \bgethostent( \bgetlogin( \bgetmntent( \bgetnetbyaddr( \bgetnetbyname( \bgetnetent( \bgetprotobyname( \bgetprotobynumber( \bgetprotoent( \bgetpwent( \bgetpwnam( \bgetpwuid( \bgetservbyname( \bgetservbyport( \bgetservent( \bgetutxent( \bgetutxid( \bgetutxline( \bgmtime( \bhcreate( \bhdestroy( \bhsearch( \binet_ntoa( \bl64a( \blgamma( \blgammaf( \blgammal( \blocaleconv( \blocaltime( \blrand48( \bmrand48( \bnftw( \bnl_langinfo( \bptsname( \bputc_unlocked( \bputchar_unlocked( \bputenv( \bpututxline( \brand( \bsetenv( \bsetgrent( \bsetkey( \bsetpwent( \bsetutxent( \bsigprocmask( \bstrerror( \bstrsignal( \bstrtok( \bsystem( \btmpnam( \bttyname( \bunsetenv( \bwcrtomb( \bwcsrtombs( \bwcstombs( \bwctomb( openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-ofp-actions0000644000000000000000000000013213534540071021353 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.797846172 openvswitch-2.5.9/build-aux/extract-ofp-actions0000755000175000017500000003261313534540071023051 0ustar00jpettitjpettit00000000000000#! /usr/bin/python import sys import os.path import re OFP_ACTION_ALIGN = 8 # Map from OpenFlow version number to version ID used in ofp_header. version_map = {"1.0": 0x01, "1.1": 0x02, "1.2": 0x03, "1.3": 0x04, "1.4": 0x05, "1.5": 0x06} version_reverse_map = dict((v, k) for (k, v) in version_map.iteritems()) # Map from vendor name to the length of the action header. vendor_map = {"OF": (0x00000000, 4), "ONF": (0x4f4e4600, 10), "NX": (0x00002320, 10)} # Basic types used in action arguments. types = {} types['uint8_t'] = {"size": 1, "align": 1, "ntoh": None, "hton": None} types['ovs_be16'] = {"size": 2, "align": 2, "ntoh": "ntohs", "hton": "htons"} types['ovs_be32'] = {"size": 4, "align": 4, "ntoh": "ntohl", "hton": "htonl"} types['ovs_be64'] = {"size": 8, "align": 8, "ntoh": "ntohll", "hton": "htonll"} types['uint16_t'] = {"size": 2, "align": 2, "ntoh": None, "hton": None} types['uint32_t'] = {"size": 4, "align": 4, "ntoh": None, "hton": None} types['uint64_t'] = {"size": 8, "align": 8, "ntoh": None, "hton": None} line = "" arg_structs = set() def round_up(x, y): return (x + (y - 1)) / y * y def open_file(fn): global file_name global input_file global line_number file_name = fn input_file = open(file_name) line_number = 0 def get_line(): global input_file global line global line_number line = input_file.readline() line_number += 1 if line == "": fatal("unexpected end of input") return line n_errors = 0 def error(msg): global n_errors sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) n_errors += 1 def fatal(msg): error(msg) sys.exit(1) def usage(): argv0 = os.path.basename(sys.argv[0]) print ('''\ %(argv0)s, for extracting OpenFlow action data usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions] This program reads ofp-actions.c to obtain information about OpenFlow actions. With --prototypes, it outputs on stdout a set of prototypes to #include early in ofp-actions.c. With --definitions, it outputs on stdout a set of definitions to #include late in ofp-actions.c OFP_ACTIONS.C should point to lib/ofp-actions.c.\ ''' % {"argv0": argv0}) sys.exit(0) def extract_ofp_actions(fn, definitions): error_types = {} comments = [] names = [] domain = {} for code, size in vendor_map.values(): domain[code] = {} enums = {} n_errors = 0 open_file(fn) while True: get_line() if re.match('enum ofp_raw_action_type {', line): break while True: get_line() if line.startswith('/*') or not line or line.isspace(): continue elif re.match('}', line): break if not line.lstrip().startswith('/*'): fatal("unexpected syntax between actions") comment = line.lstrip()[2:].strip() while not comment.endswith('*/'): get_line() if line.startswith('/*') or not line or line.isspace(): fatal("unexpected syntax within action") comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n') comment = re.sub('\[[^]]*\]', '', comment) comment = comment[:-2].rstrip() m = re.match('([^:]+):\s+(.*)$', comment) if not m: fatal("unexpected syntax between actions") dsts = m.group(1) argtype = m.group(2).strip().replace('.', '', 1) get_line() m = re.match(r'\s+(([A-Z]+)_RAW([0-9]*)_([A-Z0-9_]+)),?', line) if not m: fatal("syntax error expecting enum value") enum = m.group(1) if enum in names: fatal("%s specified twice" % enum) names.append(enum) for dst in dsts.split(', '): m = re.match(r'([A-Z]+)([0-9.]+)(\+|-[0-9.]+)?(?:\((\d+)\))(?: is deprecated \(([^)]+)\))?$', dst) if not m: fatal("%r: syntax error in destination" % dst) vendor_name = m.group(1) version1_name = m.group(2) version2_name = m.group(3) type_ = int(m.group(4)) deprecation = m.group(5) if vendor_name not in vendor_map: fatal("%s: unknown vendor" % vendor_name) vendor = vendor_map[vendor_name][0] if version1_name not in version_map: fatal("%s: unknown OpenFlow version" % version1_name) v1 = version_map[version1_name] if version2_name is None: v2 = v1 elif version2_name == "+": v2 = max(version_map.values()) elif version2_name[1:] not in version_map: fatal("%s: unknown OpenFlow version" % version2_name[1:]) else: v2 = version_map[version2_name[1:]] if v2 < v1: fatal("%s%s: %s precedes %s" % (version1_name, version2_name, version2_name, version1_name)) for version in range(v1, v2 + 1): domain[vendor].setdefault(type_, {}) if version in domain[vendor][type_]: v = domain[vendor][type_][version] msg = "%#x,%d in OF%s means both %s and %s" % ( vendor, type_, version_reverse_map[version], v["enum"], enum) error("%s: %s." % (dst, msg)) sys.stderr.write("%s:%d: %s: Here is the location " "of the previous definition.\n" % (v["file_name"], v["line_number"], dst)) n_errors += 1 else: header_len = vendor_map[vendor_name][1] base_argtype = argtype.replace(', ..', '', 1) if base_argtype in types: arg_align = types[base_argtype]['align'] arg_len = types[base_argtype]['size'] arg_ofs = round_up(header_len, arg_align) min_length = round_up(arg_ofs + arg_len, OFP_ACTION_ALIGN) elif base_argtype == 'void': min_length = round_up(header_len, OFP_ACTION_ALIGN) arg_len = 0 arg_ofs = 0 elif re.match(r'struct [a-zA-Z0-9_]+$', base_argtype): min_length = 'sizeof(%s)' % base_argtype arg_structs.add(base_argtype) arg_len = 0 arg_ofs = 0 # should also emit OFP_ACTION_ALIGN assertion else: fatal("bad argument type %s" % argtype) ellipsis = argtype != base_argtype if ellipsis: max_length = '65536 - OFP_ACTION_ALIGN' else: max_length = min_length info = {"enum": enum, # 0 "deprecation": deprecation, # 1 "file_name": file_name, # 2 "line_number": line_number, # 3 "min_length": min_length, # 4 "max_length": max_length, # 5 "arg_ofs": arg_ofs, # 6 "arg_len": arg_len, # 7 "base_argtype": base_argtype, # 8 "version": version, # 9 "type": type_} # 10 domain[vendor][type_][version] = info enums.setdefault(enum, []) enums[enum].append(info) input_file.close() if n_errors: sys.exit(1) print """\ /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */ """ if definitions: print "/* Verify that structs used as actions are reasonable sizes. */" for s in sorted(arg_structs): print "BUILD_ASSERT_DECL(sizeof(%s) %% OFP_ACTION_ALIGN == 0);" % s print "\nstatic struct ofpact_raw_instance all_raw_instances[] = {" for vendor in domain: for type_ in domain[vendor]: for version in domain[vendor][type_]: d = domain[vendor][type_][version] print " { { 0x%08x, %2d, 0x%02x }, " % ( vendor, type_, version) print " %s," % d["enum"] print " HMAP_NODE_NULL_INITIALIZER," print " HMAP_NODE_NULL_INITIALIZER," print " %s," % d["min_length"] print " %s," % d["max_length"] print " %s," % d["arg_ofs"] print " %s," % d["arg_len"] print " \"%s\"," % re.sub('_RAW[0-9]*', '', d["enum"], 1) if d["deprecation"]: print " \"%s\"," % re.sub(r'(["\\])', r'\\\1', d["deprecation"]) else: print " NULL," print " }," print "};"; for versions in enums.values(): need_ofp_version = False for v in versions: assert v["arg_len"] == versions[0]["arg_len"] assert v["base_argtype"] == versions[0]["base_argtype"] if (v["min_length"] != versions[0]["min_length"] or v["arg_ofs"] != versions[0]["arg_ofs"] or v["type"] != versions[0]["type"]): need_ofp_version = True base_argtype = versions[0]["base_argtype"] decl = "static inline " if base_argtype.startswith('struct'): decl += "%s *" %base_argtype else: decl += "void" decl += "\nput_%s(struct ofpbuf *openflow" % versions[0]["enum"].replace('_RAW', '', 1) if need_ofp_version: decl += ", enum ofp_version version" if base_argtype != 'void' and not base_argtype.startswith('struct'): decl += ", %s arg" % base_argtype decl += ")" if definitions: decl += "{\n" decl += " " if base_argtype.startswith('struct'): decl += "return " decl += "ofpact_put_raw(openflow, " if need_ofp_version: decl += "version" else: decl += "%s" % versions[0]["version"] decl += ", %s, " % versions[0]["enum"] if base_argtype.startswith('struct') or base_argtype == 'void': decl += "0" else: ntoh = types[base_argtype]['ntoh'] if ntoh: decl += "%s(arg)" % ntoh else: decl += "arg" decl += ");\n" decl += "}" else: decl += ";" print decl print if definitions: print """\ static enum ofperr ofpact_decode(const struct ofp_action_header *a, enum ofp_raw_action_type raw, enum ofp_version version, uint64_t arg, struct ofpbuf *out) { switch (raw) {\ """ for versions in enums.values(): enum = versions[0]["enum"] print " case %s:" % enum base_argtype = versions[0]["base_argtype"] if base_argtype == 'void': print " return decode_%s(out);" % enum else: if base_argtype.startswith('struct'): arg = "ALIGNED_CAST(const %s *, a)" % base_argtype else: hton = types[base_argtype]['hton'] if hton: arg = "%s(arg)" % hton else: arg = "arg" print " return decode_%s(%s, version, out);" % (enum, arg) print print """\ default: OVS_NOT_REACHED(); } }\ """ else: for versions in enums.values(): enum = versions[0]["enum"] prototype = "static enum ofperr decode_%s(" % enum base_argtype = versions[0]["base_argtype"] if base_argtype != 'void': if base_argtype.startswith('struct'): prototype += "const %s *, enum ofp_version, " % base_argtype else: prototype += "%s, enum ofp_version, " % base_argtype prototype += "struct ofpbuf *);" print prototype print """ static enum ofperr ofpact_decode(const struct ofp_action_header *, enum ofp_raw_action_type raw, enum ofp_version version, uint64_t arg, struct ofpbuf *out); """ if __name__ == '__main__': if '--help' in sys.argv: usage() elif len(sys.argv) != 3: sys.stderr.write("exactly two arguments required; " "use --help for help\n") sys.exit(1) elif sys.argv[2] == '--prototypes': extract_ofp_actions(sys.argv[1], False) elif sys.argv[2] == '--definitions': extract_ofp_actions(sys.argv[1], True) else: sys.stderr.write("invalid arguments; use --help for help\n") sys.exit(1) openvswitch-2.5.9/build-aux/PaxHeaders.82075/config.sub0000644000000000000000000000013113534540101017507 xustar0029 mtime=1567801409.20173858 30 atime=1567801410.913751197 30 ctime=1567801424.629852305 openvswitch-2.5.9/build-aux/config.sub0000755000175000017500000010645013534540101021207 0ustar00jpettitjpettit00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. timestamp='2018-02-22' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo "$1" | sed 's/-[^-]*$//'` if [ "$basic_machine" != "$1" ] then os=`echo "$1" | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-pc os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2*) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsv-tandem) basic_machine=nsv-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh5el) basic_machine=sh5le-unknown ;; simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; x64) basic_machine=x86_64-pc ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases that might get confused # with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) -es1800*) os=-ose ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ | -midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -xray | -os68k* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4*) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. case $basic_machine in arm*) os=-eabi ;; *) os=-elf ;; esac ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac echo "$basic_machine$os" exit # Local variables: # eval: (add-hook 'write-file-functions 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/soexpand.pl0000644000000000000000000000013213534540071017714 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.721845611 openvswitch-2.5.9/build-aux/soexpand.pl0000644000175000017500000000217713534540071021411 0ustar00jpettitjpettit00000000000000# Copyright (c) 2008 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; use Getopt::Long; my ($exit_code) = 0; my (@include_dirs); Getopt::Long::Configure ("bundling"); GetOptions("I|include=s" => \@include_dirs) or exit(1); @include_dirs = ('.') if !@include_dirs; OUTER: while () { if (my ($name) = /^\.so (\S+)$/) { foreach my $dir (@include_dirs, '.') { if (open(INNER, "$dir/$name")) { while () { print $_; } close(INNER); next OUTER; } } print STDERR "$name not found in: ", join(' ', @include_dirs), "\n"; $exit_code = 1; } print $_; } exit $exit_code; openvswitch-2.5.9/build-aux/PaxHeaders.82075/ltmain.sh0000644000000000000000000000013213534540075017362 xustar0030 mtime=1567801405.717712914 30 atime=1567801416.521792535 30 ctime=1567801424.633852336 openvswitch-2.5.9/build-aux/ltmain.sh0000644000175000017500000117147413534540075021067 0ustar00jpettitjpettit00000000000000#! /bin/sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2014-01-03.01 # libtool (GNU libtool) 2.4.6 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.4.6 Debian-2.4.6-2" package_revision=2.4.6 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2015-01-20.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # Copyright (C) 2004-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # As a special exception to the GNU General Public License, if you distribute # this file as part of a program or library that is built using GNU Libtool, # you may include this file under the same distribution terms that you use # for the rest of that program. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some retarded systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # We should try to minimise forks, especially on Windows where they are # unreasonably slow, so skip the feature probes when bash or zsh are # being used: if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then : ${_G_HAVE_ARITH_OP="yes"} : ${_G_HAVE_XSI_OPS="yes"} # The += operator was introduced in bash 3.1 case $BASH_VERSION in [12].* | 3.0 | 3.0*) ;; *) : ${_G_HAVE_PLUSEQ_OP="yes"} ;; esac fi # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # useable or anything else if it does not work. test -z "$_G_HAVE_PLUSEQ_OP" \ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ && _G_HAVE_PLUSEQ_OP=yes if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1+=\\ \$func_quote_for_eval_result" }' else func_append_quoted () { $debug_cmd func_quote_for_eval "$2" eval "$1=\$$1\\ \$func_quote_for_eval_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_for_eval ARG... # -------------------------- # Aesthetically quote ARGs to be evaled later. # This function returns two values: # i) func_quote_for_eval_result # double-quoted, suitable for a subsequent eval # ii) func_quote_for_eval_unquoted_result # has all characters that are still active within double # quotes backslashified. func_quote_for_eval () { $debug_cmd func_quote_for_eval_unquoted_result= func_quote_for_eval_result= while test 0 -lt $#; do case $1 in *[\\\`\"\$]*) _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; *) _G_unquoted_arg=$1 ;; esac if test -n "$func_quote_for_eval_unquoted_result"; then func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" else func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" fi case $_G_unquoted_arg in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_quoted_arg=\"$_G_unquoted_arg\" ;; *) _G_quoted_arg=$_G_unquoted_arg ;; esac if test -n "$func_quote_for_eval_result"; then func_append func_quote_for_eval_result " $_G_quoted_arg" else func_append func_quote_for_eval_result "$_G_quoted_arg" fi shift done } # func_quote_for_expand ARG # ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { $debug_cmd case $1 in *[\\\`\"]*) _G_arg=`$ECHO "$1" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) _G_arg=$1 ;; esac case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") _G_arg=\"$_G_arg\" ;; esac func_quote_for_expand_result=$_G_arg } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_for_expand "$_G_cmd" eval "func_notquiet $func_quote_for_expand_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # Set a version string for this script. scriptversion=2014-01-07.03; # UTC # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # Copyright (C) 2010-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Please report bugs or propose patches to gary@gnu.org. ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# warranty; '. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # to the main code. A hook is just a named list of of function, that can # be run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of functions called by FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It is assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook funcions.n" ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do eval $_G_hook '"$@"' # store returned options list back into positional # parameters for next 'cmd' execution. eval _G_hook_result=\$${_G_hook}_result eval set dummy "$_G_hook_result"; shift done func_quote_for_eval ${1+"$@"} func_run_hooks_result=$func_quote_for_eval_result } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list in your hook function, remove any # options that you action, and then pass back the remaining unprocessed # options in '_result', escaped suitably for # 'eval'. Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # func_quote_for_eval ${1+"$@"} # my_options_prep_result=$func_quote_for_eval_result # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # # Note that for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # ;; # *) set dummy "$_G_opt" "$*"; shift; break ;; # esac # done # # func_quote_for_eval ${1+"$@"} # my_silent_option_result=$func_quote_for_eval_result # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # # func_quote_for_eval ${1+"$@"} # my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # # You'll alse need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd func_options_prep ${1+"$@"} eval func_parse_options \ ${func_options_prep_result+"$func_options_prep_result"} eval func_validate_options \ ${func_parse_options_result+"$func_parse_options_result"} eval func_run_hooks func_options \ ${func_validate_options_result+"$func_validate_options_result"} # save modified positional parameters for caller func_options_result=$func_run_hooks_result } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propogate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before # returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} # save modified positional parameters for caller func_options_prep_result=$func_run_hooks_result } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd func_parse_options_result= # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} # Adjust func_parse_options positional parameters to match eval set dummy "$func_run_hooks_result"; shift # Break out of the loop if we already parsed every option. test $# -gt 0 || break _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) test $# = 0 && func_missing_arg $_G_opt && break case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} func_parse_options_result=$func_quote_for_eval_result } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE # save modified positional parameters for caller func_validate_options_result=$func_run_hooks_result } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables after # splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} test "x$func_split_equals_lhs" = "x$1" \ && func_split_equals_rhs= }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /(C)/!b go :more /\./!{ N s|\n# | | b more } :go /^# Written by /,/# warranty; / { s|^# || s|^# *$|| s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| p } /^# Written by / { s|^# || p } /^warranty; /q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.4.6' # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { $debug_cmd $warning_func ${1+"$@"} } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --mode=MODE use operation mode MODE --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.4.6-2 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func__fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_preserve_dup_deps=false opt_quiet=false nonopt= preserve_args= # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Pass back the list of options. func_quote_for_eval ${1+"$@"} libtool_options_prep_result=$func_quote_for_eval_result } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $_G_opt" exit_cmd=exit break ;; esac shift ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; esac done # save modified positional parameters for caller func_quote_for_eval ${1+"$@"} libtool_parse_options_result=$func_quote_for_eval_result } func_add_hook func_parse_options libtool_parse_options # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote_for_eval ${1+"$@"} libtool_validate_options_result=$func_quote_for_eval_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, cygwin, or some other w32 environment. Relies on a correctly # configured wine environment available, with the winepath program in $build's # $PATH. Assumes ARG has no leading or trailing path separator characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" func_append install_prog "$func_quote_for_eval_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" func_append install_prog " $func_quote_for_eval_result" if test -n "$arg2"; then func_quote_for_eval "$arg2" fi func_append install_shared_prog " $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_for_eval "$install_override_mode" func_append install_shared_prog " -m $func_quote_for_eval_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=\"$qECHO\" fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot|--sysroot) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) func_append new_inherited_linker_flags " $arg" ;; esac continue ;; -multi_module) single_module=$wl-multi_module continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "'-no-install' is ignored for $host" func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -os2dllname) prev=os2dllname continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; =*) func_stripname '=' '' "$dir" dir=$lt_sysroot$func_stripname_result ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: # -64, -mips[0-9] enable 64-bit mode for the SGI compiler # -r[0-9][0-9]* specify processor for the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler # +DA*, +DD* enable 64-bit mode for the HP compiler # -q* compiler args for the IBM compiler # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang # -fsanitize=* Clang/GCC memory and address sanitizer -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ -specs=*|-fsanitize=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; -Z*) if test os2 = "`expr $host : '.*\(os2\)'`"; then # OS/2 uses -Zxxx to specify OS/2-specific options compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case $arg in -Zlinker | -Zstack) prev=xcompiler ;; esac continue else # Otherwise treat like 'Some other compiler flag' below func_quote_for_eval "$arg" arg=$func_quote_for_eval_result fi ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; *.$objext) # A standard object. func_append objs " $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result test none = "$pic_object" || { # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object } # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. func_append deplibs " $arg" func_append old_deplibs " $arg" continue ;; *.la) # A libtool-controlled library. func_resolve_sysroot "$arg" if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= else func_append deplibs " $func_resolve_sysroot_result" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg=$func_quote_for_eval_result ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the '$prevarg' option requires an argument" if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname=$func_basename_result libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" # Definition is injected by LT_CONFIG during libtool generation. func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" func_dirname "$output" "/" "" output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append libs " $deplib" done if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; esac func_append pre_post_deps " $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs=$tmp_deplibs fi if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass"; then libs=$deplibs deplibs= fi if test prog = "$linkmode"; then case $pass in dlopen) libs=$dlfiles ;; dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= func_resolve_sysroot "$lib" case $lib in *.la) func_source "$func_resolve_sysroot_result" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do func_basename "$deplib" deplib_base=$func_basename_result case " $weak_libs " in *" $deplib_base "*) ;; *) func_append deplibs " $deplib" ;; esac done done libs=$dlprefiles fi if test dlopen = "$pass"; then # Collect dlpreopened libraries save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= found=false case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -l*) if test lib != "$linkmode" && test prog != "$linkmode"; then func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib=$searchdir/lib$name$search_ext if test -f "$lib"; then if test .la = "$search_ext"; then found=: else found=false fi break 2 fi done done if $found; then # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll=$l done if test "X$ll" = "X$old_library"; then # only static version available found=false func_dirname "$lib" "" "." ladir=$func_dirname_result lib=$ladir/$old_library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi else # deplib doesn't seem to be a libtool library if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi ;; # -l *.ltframework) if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) func_append xrpath " $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) func_resolve_sysroot "$deplib" lib=$func_resolve_sysroot_result ;; *.$libext) if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=: fi ;; pass_all) valid_a_lib=: ;; esac if $valid_a_lib; then echo $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." fi ;; esac continue ;; prog) if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test conv = "$pass"; then deplibs="$deplib $deplibs" elif test prog = "$linkmode"; then if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append newdlfiles " $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=: continue ;; esac # case $deplib $found || test -f "$lib" \ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir=$func_dirname_result dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` if test lib,link = "$linkmode,$pass" || test prog,scan = "$linkmode,$pass" || { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" func_append old_convenience " $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done elif test prog != "$linkmode" && test lib != "$linkmode"; then func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= if test -n "$old_library" && { test yes = "$prefer_static_libs" || test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do linklib=$l done fi if test -z "$linklib"; then func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. if test dlopen = "$pass"; then test -z "$libdir" \ && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || test yes != "$dlopen_support" || test no = "$build_libtool_libs" then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. func_append dlprefiles " $lib $dependency_libs" else func_append newdlfiles " $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir=$ladir fi ;; esac func_basename "$lib" laname=$func_basename_result # Find the relevant object directory and library name. if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library '$lib' was moved." dir=$ladir absdir=$abs_ladir libdir=$abs_ladir else dir=$lt_sysroot$libdir absdir=$lt_sysroot$libdir fi test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir=$ladir absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else dir=$ladir/$objdir absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test dlpreopen = "$pass"; then if test -z "$libdir" && test prog = "$linkmode"; then func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both # static and shared are present. Therefore, ensure we extract # symbols from the import library if a shared library is present # (otherwise, the dlopen module name will be incorrect). We do # this by putting the import library name into $newdlprefiles. # We recover the dlopen module name by 'saving' the la file # name in a special purpose variable, and (later) extracting the # dlname from the la file. if test -n "$dlname"; then func_tr_sh "$dir/$linklib" eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" func_append newdlprefiles " $dir/$linklib" else func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" fi ;; * ) # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then func_append newdlprefiles " $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ func_append dlpreconveniencelibs " $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then func_append newdlprefiles " $dir/$dlname" else func_append newdlprefiles " $dir/$linklib" fi ;; esac fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" linkalldeplibs=false if test no != "$link_all_deplibs" || test -z "$library_names" || test no = "$build_libtool_libs"; then linkalldeplibs=: fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; esac # Need to link against all dependency_libs? if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done # for deplib continue fi # $linkmode = prog... if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && { { test no = "$prefer_static_libs" || test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi # $linkmode,$pass = prog,link... if $alldeplibs && { test pass_all = "$deplibs_check_method" || { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule=$dlpremoduletest break fi done if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test lib = "$linkmode" && test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) func_append compile_rpath " $absdir" ;; esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result versuffix=-$major ;; esac eval soname=\"$soname_spec\" else soname=$realname fi # Make a new name for the extract_expsyms_cmds to use soroot=$soname func_basename "$soroot" soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test no = "$hardcode_direct"; then add=$dir/$linklib case $host in *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else add=$dir/$old_library fi elif test -n "$old_library"; then add=$dir/$old_library fi fi esac elif test no = "$hardcode_minus_L"; then case $host in *-*-sunos*) add_shlibpath=$dir ;; esac add_dir=-L$dir add=-l$name elif test no = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; relink) if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$dir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name elif test yes = "$hardcode_shlibpath_var"; then add_shlibpath=$dir add=-l$name else lib_linked=no fi ;; *) lib_linked=no ;; esac if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test yes != "$hardcode_direct" && test yes != "$hardcode_minus_L" && test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac fi fi fi if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test yes = "$hardcode_direct" && test no = "$hardcode_direct_absolute"; then add=$libdir/$linklib elif test yes = "$hardcode_minus_L"; then add_dir=-L$libdir add=-l$name elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac add=-l$name elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib"; then add=$inst_prefix_dir$libdir/$linklib else add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) func_append add_dir " -L$inst_prefix_dir$libdir" ;; esac fi add=-l$name fi if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test unsupported != "$hardcode_direct"; then test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test yes = "$build_libtool_libs"; then # Not a shared library if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test lib = "$linkmode"; then if test -n "$dependency_libs" && { test yes != "$hardcode_into_libs" || test yes = "$build_old_libs" || test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) func_append xrpath " $temp_xrpath";; esac;; *) func_append temp_deplibs " $libdir";; esac done dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" case $deplib in -L*) func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; esac fi func_append tmp_libs " $func_resolve_sysroot_result" done if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result func_dirname "$deplib" "" "." dir=$func_dirname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of '$dir'" absdir=$dir fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names"; then for tmp in $deplibrary_names; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl"; then depdepl=$absdir/$objdir/$depdepl darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) path=-L$absdir/$objdir ;; esac else eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "'$deplib' seems to be moved" path=-L$absdir fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test link = "$pass"; then if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs=$newdependency_libs if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test dlopen != "$pass"; then test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) func_append lib_search_path " $dir" ;; esac done newlib_search_path= } if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" else vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) func_append tmp_libs " $deplib" ;; esac ;; *) func_append tmp_libs " $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Add Sun CC postdeps if required: test CXX = "$tagname" && { case $host_os in linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; solaris*) func_cc_basename "$CC" case $func_cc_basename_result in CC* | sunCC*) func_suncc_cstd_abi if test no != "$suncc_use_cstd_abi"; then func_append postdeps ' -library=Cstd -library=Crun' fi ;; esac ;; esac } # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i= ;; esac if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass if test prog = "$linkmode"; then dlfiles=$newdlfiles fi if test prog = "$linkmode" || test lib = "$linkmode"; then dlprefiles=$newdlprefiles fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs=$output func_append objs "$old_deplibs" ;; lib) # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test no = "$module" \ && func_fatal_help "libtool library '$output' must begin with 'lib'" if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test pass_all != "$deplibs_check_method"; then func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" func_append libobjs " $objs" fi fi test no = "$dlself" \ || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test 1 -lt "$#" \ && func_warning "ignoring multiple '-rpath's for a libtool library" install_libdir=$1 oldlibs= if test -z "$rpath"; then if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift IFS=$save_ifs test -n "$7" && \ func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major=$1 number_minor=$2 number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # that has an extra 1 added just for fun # case $version_type in # correct linux to gnu/linux during the next big refactor darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_revision ;; freebsd-aout|qnx|sunos) current=$number_major revision=$number_minor age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age=$number_minor revision=$number_minor lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) current=$1 revision=$2 age=$3 ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT '$current' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION '$revision' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE '$age' must be a nonnegative integer" func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE '$age' is greater than the current interface number '$current'" func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" # On Darwin other compilers case $CC in nagfor*) verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" ;; *) verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; esac ;; freebsd-aout) major=.$current versuffix=.$current.$revision ;; freebsd-elf) func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; irix | nonstopux) if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring_prefix$major.$iface:$verstring done # Before this point, $major must not contain '.'. major=.$major versuffix=$major.$revision ;; linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=.$current.$age.$revision verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring=$verstring:$iface.0 done # Make executables depend on our current version. func_append verstring ":$current.0" ;; qnx) major=.$current versuffix=.$current ;; sco) major=.$current versuffix=.$current ;; sunos) major=.$current versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result versuffix=-$major ;; *) func_fatal_configuration "unknown library version type '$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring=0.0 ;; esac if test no = "$need_version"; then versuffix= else versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided if test yes,no = "$avoid_version,$need_version"; then major= versuffix= verstring= fi # Check to see if the archive will have undefined symbols. if test yes = "$allow_undefined"; then if test unsupported = "$allow_undefined_flag"; then if test yes = "$build_old_libs"; then func_warning "undefined symbols not allowed in $host shared libraries; building static only" build_libtool_libs=no else func_fatal_error "can't build $host shared library unless -no-undefined is specified" fi fi else # Don't allow undefined symbols. allow_undefined_flag=$no_undefined_flag fi fi func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" test " " = "$libobjs" && libobjs= if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi func_append removelist " $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do func_replace_sysroot "$libdir" func_append temp_xrpath " -R$func_replace_sysroot_result" case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) func_append dlfiles " $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) func_append dlprefiles " $lib" ;; esac done if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework func_append deplibs " System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release= versuffix= major= newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` $nocaseglob else potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` fi for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" a_deplib= ;; esac fi if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" a_deplib= break 2 fi done done fi if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. func_append newdeplibs " $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` if test yes = "$allow_libtool_libs_with_static_runtimes"; then for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." fi echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes ;; esac ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac if test yes = "$droppeddeps"; then if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" echo "*** a static module, that should work as long as the dlopening" echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." echo "*** 'nm' from GNU binutils and a full rebuild may help." fi if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else echo "*** The inter-library dependencies that have been dropped here will be" echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." if test no = "$build_old_libs"; then oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test yes = "$build_libtool_libs"; then # Remove $wl instances when linking with ld. # FIXME: should test the right _cmds variable. case $archive_cmds in *\$LD\ *) wl= ;; esac if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath=$finalize_rpath test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append dep_rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath=$finalize_shlibpath test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname=$realname fi if test -z "$dlname"; then dlname=$soname fi lib=$output_objdir/$realname linknames= for link do func_append linknames " $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) try_normal_branch=yes eval cmd=\"$cmd1\" func_len " $cmd" len=$func_len_result ;; *) try_normal_branch=no ;; esac if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then func_show_eval "$cmd" 'exit $?' skipped_export=false elif test -n "$nm_file_list_spec"; then func_basename "$output" output_la=$func_basename_result save_libobjs=$libobjs save_output=$output output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" func_verbose "creating $NM input file list: $output" for obj in $save_libobjs; do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > "$output" eval cmd=\"$cmd1\" func_show_eval "$cmd" 'exit $?' output=$save_output libobjs=$save_libobjs skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS=$save_ifs if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) func_append tmp_deplibs " $test_deplib" ;; esac done deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output func_basename "$output" output_la=$func_basename_result # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done echo ')' >> $output func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi for obj do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" >> $output done func_append delfiles " $output" func_to_tool_file "$output" output=$firstobj\"$file_list_spec$func_to_tool_file_result\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" else # All subsequent reloadable object files will link in # the last one created. reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" else output= fi ${skipped_export-false} && { func_verbose "generating symbol list for '$libname.la'" export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols=$export_symbols test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter func_append delfiles " $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi } libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append libobjs " $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs=$IFS; IFS='~' for cmd in $cmds; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS=$save_ifs # Restore the uninstalled library and exit if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. dlname=$soname fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ func_warning "'-version-info' is ignored for objects" test -n "$release" && \ func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj=$output ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # if reload_cmds runs $LD directly, get rid of -Wl from # whole_archive_flag_spec and hope we can get by with turning comma # into space. case $reload_cmds in *\$LD[\ \$]*) wl= ;; esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # If we're not building shared, we need to use non_pic_objs test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS } if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "'-version-info' is ignored for programs" test -n "$release" && \ func_warning "'-release' is ignored for programs" $preload \ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) func_append compile_command " $wl-bind_at_load" func_append finalize_command " $wl-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) func_append new_libs " -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) func_append new_libs " $deplib" ;; esac ;; *) func_append new_libs " $deplib" ;; esac done compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" func_append finalize_command " $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) func_append finalize_rpath " $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) func_append perm_rpath " $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) func_append dllsearchpath ":$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath=$rpath rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" func_append rpath " $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) func_append finalize_perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath=$rpath if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. wrappers_required=false ;; *cygwin* | *mingw* ) test yes = "$build_libtool_libs" || wrappers_required=false ;; *) if test no = "$need_relink" || test yes != "$build_libtool_libs"; then wrappers_required=false fi ;; esac $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Delete the generated files. if test -f "$output_objdir/${outputname}S.$objext"; then func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do func_append rpath "$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do func_append rpath "$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test yes = "$no_install"; then # We don't need to create a wrapper script. link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi exit $EXIT_SUCCESS fi case $hardcode_action,$fast_install in relink,*) # Fast installation is not supported link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath func_warning "this platform does not like uninstalled shared libraries" func_warning "'$output' will be relinked during installation" ;; *,yes) link_command=$finalize_var$compile_command$finalize_rpath relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` ;; *,no) link_command=$compile_var$compile_command$compile_rpath relink_command=$finalize_var$finalize_command$finalize_rpath ;; *,needless) link_command=$finalize_var$compile_command$finalize_rpath relink_command= ;; esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' if test -n "$postlink_cmds"; then func_to_tool_file "$output_objdir/$outputname" postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` func_execute_cmds "$postlink_cmds" 'exit $?' fi # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource=$output_path/$objdir/lt-$output_name.c cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do case $build_libtool_libs in convenience) oldobjs="$libobjs_save $symfileobj" addlibs=$convenience build_libtool_libs=no ;; module) oldobjs=$libobjs_save addlibs=$old_convenience build_libtool_libs=no ;; *) oldobjs="$old_deplibs $non_pic_objects" $preload && test -f "$symfileobj" \ && func_append oldobjs " $symfileobj" addlibs=$old_convenience ;; esac if test -n "$addlibs"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs func_append oldobjs " $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles func_append oldobjs " $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else echo "copying selected object files to avoid basename conflicts..." gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" func_append oldobjs " $gentop/$newobj" ;; *) func_append oldobjs " $obj" ;; esac done fi func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds elif test -n "$archiver_list_spec"; then func_verbose "using command file archive linking..." for obj in $oldobjs do func_to_tool_file "$obj" $ECHO "$func_to_tool_file_result" done > $output_objdir/$libname.libcmd func_to_tool_file "$output_objdir/$libname.libcmd" oldobjs=" $archiver_list_spec$func_to_tool_file_result" cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test yes = "$installed"; then if test -z "$install_libdir"; then break fi output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name=$func_basename_result func_resolve_sysroot "$deplib" eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) func_stripname -L '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -L$func_replace_sysroot_result" ;; -R*) func_stripname -R '' "$deplib" func_replace_sysroot "$func_stripname_result" func_append newdependency_libs " -R$func_replace_sysroot_result" ;; *) func_append newdependency_libs " $deplib" ;; esac done dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name=$func_basename_result eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin # In fact, it would be nice if we could use this code for all target # systems that can't hard-code library paths into their executables # and that have no shared library path variable independent of PATH, # but it turns out we can't easily determine that from inspecting # libtool variables, so we have to hard-code the OSs to which it # applies here; at the moment, that means platforms that use the PE # object format with DLL files. See the long comment at the top of # tests/bindir.at for full details. tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname fi ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } if test link = "$opt_mode" || test relink = "$opt_mode"; then func_mode_link ${1+"$@"} fi # func_mode_uninstall arg... func_mode_uninstall () { $debug_cmd RM=$nonopt files= rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic for arg do case $arg in -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= for file in $files; do func_dirname "$file" "" "." dir=$func_dirname_result if test . = "$dir"; then odir=$objdir else odir=$dir/$objdir fi func_basename "$file" name=$func_basename_result test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif $rmforce; then continue fi rmfiles=$file case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do func_append rmfiles " $odir/$n" done test -n "$old_library" && func_append rmfiles " $odir/$old_library" case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; esac test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe func_append rmfiles " $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result func_append rmfiles " $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles func_append rmfiles " $odir/$name $odir/${name}S.$objext" if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi if test "X$noexename" != "X$name"; then func_append rmfiles " $odir/lt-$noexename.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then func_mode_uninstall ${1+"$@"} fi test -z "$opt_mode" && { help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-odp-netlink-windows-dp-h0000644000000000000000000000013213534540071023673 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801424.477851186 openvswitch-2.5.9/build-aux/extract-odp-netlink-windows-dp-h0000755000175000017500000000147413534540071025372 0ustar00jpettitjpettit00000000000000# This is a "sed" script that transforms into a # form that is suitable for inclusion within the Open vSwitch tree on # windows system. The transformed header file can be included by windows # driver modules. # Add a header warning that this is a generated file. 1i\ /* -*- mode: c; buffer-read-only: t -*- */\ /* Generated automatically from -- do not modify! */\ \ \ # Avoid using reserved names in header guards. s/_LINUX_OPENVSWITCH_H/__OVS_DP_INTERFACE_H_/ # and use the appropriate userspace header. s,,"Types.h", # Add ETH_ADDR_LEN macro to avoid including userspace packet.h s,#include ,\n#ifndef ETH_ADDR_LEN \ #define ETH_ADDR_LEN 6 \n#endif, # Use OVS's own ETH_ADDR_LEN instead of Linux-specific ETH_ALEN. s/ETH_ALEN/ETH_ADDR_LEN/ openvswitch-2.5.9/build-aux/PaxHeaders.82075/cksum-schema-check0000644000000000000000000000013213534540071021114 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.717845583 openvswitch-2.5.9/build-aux/cksum-schema-check0000755000175000017500000000074413534540071022612 0ustar00jpettitjpettit00000000000000#!/bin/sh schema=$1 stamp=$2 sum=`sed '/cksum/d' $schema | cksum` expected=`sed -n 's/.*"cksum": "\(.*\)".*/\1/p' $schema` if test "X$sum" = "X$expected"; then touch $stamp else ln=`sed -n '/"cksum":/=' $schema` echo >&2 "$schema:$ln: The checksum \"$sum\" was calculated from the schema file and does not match cksum field in the schema file - you should probably update the version number and the checksum in the schema file with the value listed here." exit 1 fi openvswitch-2.5.9/build-aux/PaxHeaders.82075/cccl0000644000000000000000000000013213534540071016365 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.713845553 openvswitch-2.5.9/build-aux/cccl0000644000175000017500000001101213534540071020046 0ustar00jpettitjpettit00000000000000#!/bin/sh # cccl # Wrapper around MS's cl.exe and link.exe to make them act more like # Unix cc and ld # # Copyright (C) 2000-2003 Geoffrey Wossum (gwossum@acm.org) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # usage() { cat <. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: openvswitch-2.5.9/build-aux/PaxHeaders.82075/extract-ofp-fields0000644000000000000000000000013213534540071021161 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.797846172 openvswitch-2.5.9/build-aux/extract-ofp-fields0000755000175000017500000004051213534540071022654 0ustar00jpettitjpettit00000000000000#! /usr/bin/python import sys import os.path import re line = "" # Maps from user-friendly version number to its protocol encoding. VERSION = {"1.0": 0x01, "1.1": 0x02, "1.2": 0x03, "1.3": 0x04, "1.4": 0x05, "1.5": 0x06} TYPES = {"u8": (1, False), "be16": (2, False), "be32": (4, False), "MAC": (6, False), "be64": (8, False), "be128": (16, False), "tunnelMD": (124, True)} FORMATTING = {"decimal": ("MFS_DECIMAL", 1, 8), "hexadecimal": ("MFS_HEXADECIMAL", 1, 127), "ct state": ("MFS_CT_STATE", 4, 4), "Ethernet": ("MFS_ETHERNET", 6, 6), "IPv4": ("MFS_IPV4", 4, 4), "IPv6": ("MFS_IPV6", 16, 16), "OpenFlow 1.0 port": ("MFS_OFP_PORT", 2, 2), "OpenFlow 1.1+ port": ("MFS_OFP_PORT_OXM", 4, 4), "frag": ("MFS_FRAG", 1, 1), "tunnel flags": ("MFS_TNL_FLAGS", 2, 2), "TCP flags": ("MFS_TCP_FLAGS", 2, 2)} PREREQS = {"none": "MFP_NONE", "ARP": "MFP_ARP", "VLAN VID": "MFP_VLAN_VID", "IPv4": "MFP_IPV4", "IPv6": "MFP_IPV6", "IPv4/IPv6": "MFP_IP_ANY", "MPLS": "MFP_MPLS", "TCP": "MFP_TCP", "UDP": "MFP_UDP", "SCTP": "MFP_SCTP", "ICMPv4": "MFP_ICMPV4", "ICMPv6": "MFP_ICMPV6", "ND": "MFP_ND", "ND solicit": "MFP_ND_SOLICIT", "ND advert": "MFP_ND_ADVERT"} # Maps a name prefix into an (experimenter ID, class) pair, so: # # - Standard OXM classes are written as (0, ) # # - Experimenter OXM classes are written as (, 0xffff) # # If a name matches more than one prefix, the longest one is used. OXM_CLASSES = {"NXM_OF_": (0, 0x0000), "NXM_NX_": (0, 0x0001), "OXM_OF_": (0, 0x8000), "OXM_OF_PKT_REG": (0, 0x8001), "ONFOXM_ET_": (0x4f4e4600, 0xffff), # This is the experimenter OXM class for Nicira, which is the # one that OVS would be using instead of NXM_OF_ and NXM_NX_ # if OVS didn't have those grandfathered in. It is currently # used only to test support for experimenter OXM, since there # are barely any real uses of experimenter OXM in the wild. "NXOXM_ET_": (0x00002320, 0xffff)} def oxm_name_to_class(name): prefix = '' class_ = None for p, c in OXM_CLASSES.items(): if name.startswith(p) and len(p) > len(prefix): prefix = p class_ = c return class_ def decode_version_range(range): if range in VERSION: return (VERSION[range], VERSION[range]) elif range.endswith('+'): return (VERSION[range[:-1]], max(VERSION.values())) else: a, b = re.match(r'^([^-]+)-([^-]+)$', range).groups() return (VERSION[a], VERSION[b]) def get_line(): global line global line_number line = input_file.readline() line_number += 1 if line == "": fatal("unexpected end of input") n_errors = 0 def error(msg): global n_errors sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg)) n_errors += 1 def fatal(msg): error(msg) sys.exit(1) def usage(): argv0 = os.path.basename(sys.argv[0]) print('''\ %(argv0)s, for extracting OpenFlow field properties from meta-flow.h usage: %(argv0)s INPUT [--meta-flow | --nx-match] where INPUT points to lib/meta-flow.h in the source directory. Depending on the option given, the output written to stdout is intended to be saved either as lib/meta-flow.inc or lib/nx-match.inc for the respective C file to #include.\ ''' % {"argv0": argv0}) sys.exit(0) def make_sizeof(s): m = re.match(r'(.*) up to (.*)', s) if m: struct, member = m.groups() return "offsetof(%s, %s)" % (struct, member) else: return "sizeof(%s)" % s def parse_oxms(s, prefix, n_bytes): if s == 'none': return () return tuple(parse_oxm(s2.strip(), prefix, n_bytes) for s2 in s.split(',')) match_types = dict() def parse_oxm(s, prefix, n_bytes): global match_types m = re.match('([A-Z0-9_]+)\(([0-9]+)\) since(?: OF(1\.[0-9]+) and)? v([12]\.[0-9]+)$', s) if not m: fatal("%s: syntax error parsing %s" % (s, prefix)) name, oxm_type, of_version, ovs_version = m.groups() class_ = oxm_name_to_class(name) if class_ is None: fatal("unknown OXM class for %s" % name) oxm_vendor, oxm_class = class_ if class_ in match_types: if oxm_type in match_types[class_]: fatal("duplicate match type for %s (conflicts with %s)" % (name, match_types[class_][oxm_type])) else: match_types[class_] = dict() match_types[class_][oxm_type] = name # Normally the oxm_length is the size of the field, but for experimenter # OXMs oxm_length also includes the 4-byte experimenter ID. oxm_length = n_bytes if oxm_class == 0xffff: oxm_length += 4 header = ("NXM_HEADER(0x%x,0x%x,%s,0,%d)" % (oxm_vendor, oxm_class, oxm_type, oxm_length)) if of_version: if of_version not in VERSION: fatal("%s: unknown OpenFlow version %s" % (name, of_version)) of_version_nr = VERSION[of_version] if of_version_nr < VERSION['1.2']: fatal("%s: claimed version %s predates OXM" % (name, of_version)) else: of_version_nr = 0 return (header, name, of_version_nr, ovs_version) def parse_field(mff, comment): f = {'mff': mff} # First line of comment is the field name. m = re.match(r'"([^"]+)"(?:\s+\(aka "([^"]+)"\))?(?:\s+\(.*\))?\.', comment[0]) if not m: fatal("%s lacks field name" % mff) f['name'], f['extra_name'] = m.groups() # Find the last blank line the comment. The field definitions # start after that. blank = None for i in range(len(comment)): if not comment[i]: blank = i if not blank: fatal("%s: missing blank line in comment" % mff) d = {} for key in ("Type", "Maskable", "Formatting", "Prerequisites", "Access", "Prefix lookup member", "OXM", "NXM", "OF1.0", "OF1.1"): d[key] = None for fline in comment[blank + 1:]: m = re.match(r'([^:]+):\s+(.*)\.$', fline) if not m: fatal("%s: syntax error parsing key-value pair as part of %s" % (fline, mff)) key, value = m.groups() if key not in d: fatal("%s: unknown key" % key) elif key == 'Code point': d[key] += [value] elif d[key] is not None: fatal("%s: duplicate key" % key) d[key] = value for key, value in d.items(): if not value and key not in ("OF1.0", "OF1.1", "Prefix lookup member", "Notes"): fatal("%s: missing %s" % (mff, key)) m = re.match(r'([a-zA-Z0-9]+)(?: \(low ([0-9]+) bits\))?$', d['Type']) if not m: fatal("%s: syntax error in type" % mff) type_ = m.group(1) if type_ not in TYPES: fatal("%s: unknown type %s" % (mff, d['Type'])) f['n_bytes'] = TYPES[type_][0] if m.group(2): f['n_bits'] = int(m.group(2)) if f['n_bits'] > f['n_bytes'] * 8: fatal("%s: more bits (%d) than field size (%d)" % (mff, f['n_bits'], 8 * f['n_bytes'])) else: f['n_bits'] = 8 * f['n_bytes'] f['variable'] = TYPES[type_][1] if d['Maskable'] == 'no': f['mask'] = 'MFM_NONE' elif d['Maskable'] == 'bitwise': f['mask'] = 'MFM_FULLY' else: fatal("%s: unknown maskable %s" % (mff, d['Maskable'])) fmt = FORMATTING.get(d['Formatting']) if not fmt: fatal("%s: unknown format %s" % (mff, d['Formatting'])) if f['n_bytes'] < fmt[1] or f['n_bytes'] > fmt[2]: fatal("%s: %d-byte field can't be formatted as %s" % (mff, f['n_bytes'], d['Formatting'])) f['string'] = fmt[0] f['prereqs'] = PREREQS.get(d['Prerequisites']) if not f['prereqs']: fatal("%s: unknown prerequisites %s" % (mff, d['Prerequisites'])) if d['Access'] == 'read-only': f['writable'] = False elif d['Access'] == 'read/write': f['writable'] = True else: fatal("%s: unknown access %s" % (mff, d['Access'])) f['OF1.0'] = d['OF1.0'] if not d['OF1.0'] in (None, 'exact match', 'CIDR mask'): fatal("%s: unknown OF1.0 match type %s" % (mff, d['OF1.0'])) f['OF1.1'] = d['OF1.1'] if not d['OF1.1'] in (None, 'exact match', 'bitwise mask'): fatal("%s: unknown OF1.1 match type %s" % (mff, d['OF1.1'])) f['OXM'] = (parse_oxms(d['OXM'], 'OXM', f['n_bytes']) + parse_oxms(d['NXM'], 'NXM', f['n_bytes'])) f['prefix'] = d["Prefix lookup member"] return f def protocols_to_c(protocols): if protocols == set(['of10', 'of11', 'oxm']): return 'OFPUTIL_P_ANY' elif protocols == set(['of11', 'oxm']): return 'OFPUTIL_P_NXM_OF11_UP' elif protocols == set(['oxm']): return 'OFPUTIL_P_NXM_OXM_ANY' elif protocols == set([]): return 'OFPUTIL_P_NONE' else: assert False def make_meta_flow(fields): output = [] for f in fields: output += ["{"] output += [" %s," % f['mff']] if f['extra_name']: output += [" \"%s\", \"%s\"," % (f['name'], f['extra_name'])] else: output += [" \"%s\", NULL," % f['name']] if f['variable']: variable = 'true' else: variable = 'false' output += [" %d, %d, %s," % (f['n_bytes'], f['n_bits'], variable)] if f['writable']: rw = 'true' else: rw = 'false' output += [" %s, %s, %s, %s," % (f['mask'], f['string'], f['prereqs'], rw)] oxm = f['OXM'] of10 = f['OF1.0'] of11 = f['OF1.1'] if f['mff'] in ('MFF_DL_VLAN', 'MFF_DL_VLAN_PCP'): # MFF_DL_VLAN and MFF_DL_VLAN_PCP don't exactly correspond to # OF1.1, nor do they have NXM or OXM assignments, but their # meanings can be expressed in every protocol, which is the goal of # this member. protocols = set(["of10", "of11", "oxm"]) else: protocols = set([]) if of10: protocols |= set(["of10"]) if of11: protocols |= set(["of11"]) if oxm: protocols |= set(["oxm"]) if f['mask'] == 'MFM_FULLY': cidr_protocols = protocols.copy() bitwise_protocols = protocols.copy() if of10 == 'exact match': bitwise_protocols -= set(['of10']) cidr_protocols -= set(['of10']) elif of10 == 'CIDR mask': bitwise_protocols -= set(['of10']) else: assert of10 is None if of11 == 'exact match': bitwise_protocols -= set(['of11']) cidr_protocols -= set(['of11']) else: assert of11 in (None, 'bitwise mask') else: assert f['mask'] == 'MFM_NONE' cidr_protocols = set([]) bitwise_protocols = set([]) output += [" %s," % protocols_to_c(protocols)] output += [" %s," % protocols_to_c(cidr_protocols)] output += [" %s," % protocols_to_c(bitwise_protocols)] if f['prefix']: output += [" FLOW_U32OFS(%s)," % f['prefix']] else: output += [" -1, /* not usable for prefix lookup */"] output += ["},"] return output def make_nx_match(fields): output = [] print("static struct nxm_field_index all_nxm_fields[] = {") for f in fields: # Sort by OpenFlow version number (nx-match.c depends on this). for oxm in sorted(f['OXM'], key=lambda x: x[2]): print("""{ .nf = { %s, %d, "%s", %s } },""" % ( oxm[0], oxm[2], oxm[1], f['mff'])) print("};") return output def extract_ofp_fields(mode): global line fields = [] while True: get_line() if re.match('enum.*mf_field_id', line): break while True: get_line() first_line_number = line_number here = '%s:%d' % (file_name, line_number) if (line.startswith('/*') or line.startswith(' *') or line.startswith('#') or not line or line.isspace()): continue elif re.match('}', line) or re.match('\s+MFF_N_IDS', line): break # Parse the comment preceding an MFF_ constant into 'comment', # one line to an array element. line = line.strip() if not line.startswith('/*'): fatal("unexpected syntax between fields") line = line[1:] comment = [] end = False while not end: line = line.strip() if line.startswith('*/'): get_line() break if not line.startswith('*'): fatal("unexpected syntax within field") line = line[1:] if line.startswith(' '): line = line[1:] if line.startswith(' ') and comment: continuation = True line = line.lstrip() else: continuation = False if line.endswith('*/'): line = line[:-2].rstrip() end = True else: end = False if continuation: comment[-1] += " " + line else: comment += [line] get_line() # Drop blank lines at each end of comment. while comment and not comment[0]: comment = comment[1:] while comment and not comment[-1]: comment = comment[:-1] # Parse the MFF_ constant(s). mffs = [] while True: m = re.match('\s+(MFF_[A-Z0-9_]+),?\s?$', line) if not m: break mffs += [m.group(1)] get_line() if not mffs: fatal("unexpected syntax looking for MFF_ constants") if len(mffs) > 1 or '' in comment[0]: for mff in mffs: # Extract trailing integer. m = re.match('.*[^0-9]([0-9]+)$', mff) if not m: fatal("%s lacks numeric suffix in register group" % mff) n = m.group(1) # Search-and-replace within the comment, # and drop lines that have for x != n. instance = [] for x in comment: y = x.replace('', n) if re.search('<[0-9]+>', y): if ('<%s>' % n) not in y: continue y = re.sub('<[0-9]+>', '', y) instance += [y.strip()] fields += [parse_field(mff, instance)] else: fields += [parse_field(mffs[0], comment)] continue input_file.close() if n_errors: sys.exit(1) print("""\ /* Generated automatically; do not modify! "-*- buffer-read-only: t -*- */ """) if mode == '--meta-flow': output = make_meta_flow(fields) elif mode == '--nx-match': output = make_nx_match(fields) else: assert False return output if __name__ == '__main__': if '--help' in sys.argv: usage() elif len(sys.argv) != 3: sys.stderr.write("exactly two arguments required; " "use --help for help\n") sys.exit(1) elif sys.argv[2] in ('--meta-flow', '--nx-match'): global file_name global input_file global line_number file_name = sys.argv[1] input_file = open(file_name) line_number = 0 for oline in extract_ofp_fields(sys.argv[2]): print(oline) else: sys.stderr.write("invalid arguments; use --help for help\n") sys.exit(1) openvswitch-2.5.9/build-aux/PaxHeaders.82075/dist-docs0000644000000000000000000000013213534540071017352 xustar0030 mtime=1567801401.197679672 30 atime=1567801402.045685899 30 ctime=1567801423.717845583 openvswitch-2.5.9/build-aux/dist-docs0000755000175000017500000000710313534540071021044 0ustar00jpettitjpettit00000000000000#! /bin/sh set -e # Check command line. if test ! -d "$1" || test $# -lt 2; then cat <&2 "$0: $1 not found in \$PATH, please install and try again" exit 1 } search_path man search_path markdown search_path ps2pdf # Create dist-docs directory. distdir=dist-docs abs_distdir=`pwd`/dist-docs rm -rf $distdir mkdir $distdir # Install manpages. make install-man mandir="$abs_distdir"/man (cd $distdir && mv `find man -type f` . && rm -rf man) manpages=`cd $distdir && echo *` # Start writing index.html. exec 3>$distdir/index.html cat >&3 < Open vSwitch $VERSION Documentation

    Open vSwitch $VERSION Documentation

    Documents

    EOF # Add top-level documentation to index.html, giving it .txt extensions so # that the webserver doesn't serve it as Markdown and make your web browser # try to invoke some kind of external helper you don't have installed. # # Also translate documentation to HTML. for file do title=`head -1 "$srcdir/$file"` dir=$distdir/`dirname $file`; test -d "$dir" || mkdir "$dir" cp "$srcdir/$file" "$distdir/$file.txt" (cat < $file (Open vSwitch $VERSION) EOF markdown "$distdir/$file.txt" echo "") > "$distdir/$file.html" cat < EOF done >&3 # Add header for manpages to index.html. cat >&3 <

    Manpages

    $file $title HTML, plain text
    EOF # Add manpages to index.html, translating them into PDF, HTML, and plain text. # The HTML is just slightly marked up from the plain text version; although # groff supports better HTML output, on my system some of the OVS manpages # cause the groff HTML output engine to segfault (!). (cd $distdir for manpage in $manpages; do man -l -Tps $manpage | ps2pdf - > $manpage.pdf GROFF_NO_SGR=1 man -l -Tutf8 $manpage | sed 's/.//g' > $manpage.txt (echo '
    '
          GROFF_NO_SGR=1 man -l -Tutf8 $manpage | sed '
    s/&/&/g
    s//>/g
    s,\(.\)\1,\1,g
    s,_\(.\),\1,g'
          echo '
    ' ) > $manpage.html name=`echo $manpage | sed 's/\.\([0-9]\)$/(\1)/'` echo " " done ) >&3 cat >&3 < EOF # Create CSS style file. cat >$distdir/style.css <<'EOF' div { vertical-align:top; } p { vertical-align:baseline; } a { text-decoration: none; font-weight: 700; } a:hover { color:#444; } a:visited { color:#447099; } a:link { color:#447099; } body { font-family: Arial,Helvetica,sans-serif; font-size: 14px; line-height: 1.5em; color: #444; background-color:#f5f5f5; } EOF openvswitch-2.5.9/build-aux/PaxHeaders.82075/compile0000644000000000000000000000013013534540101017101 xustar0029 mtime=1567801409.19773855 29 atime=1567801409.19773855 30 ctime=1567801424.629852305 openvswitch-2.5.9/build-aux/compile0000755000175000017500000001624513534540101020604 0ustar00jpettitjpettit00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: openvswitch-2.5.9/PaxHeaders.82075/appveyor.yml0000644000000000000000000000013213534540071016234 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.713845553 openvswitch-2.5.9/appveyor.yml0000644000175000017500000000271313534540071017725 0ustar00jpettitjpettit00000000000000version: 1.0.{build} branches: only: - master clone_folder: C:\openvswitch init: - ps: >- mkdir C:\pthreads-win32 mkdir C:\ovs-build-downloads $source = "ftp://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip" $destination = "C:\pthreads-win32\pthreads-win32.zip" Invoke-WebRequest $source -OutFile $destination $source = "https://slproweb.com/download/Win32OpenSSL-1_0_2d.exe" $destination = "C:\ovs-build-downloads\Win32OpenSSL-1_0_2d.exe" Invoke-WebRequest $source -OutFile $destination cd C:\pthreads-win32 7z x C:\pthreads-win32\pthreads-win32.zip cd C:\ovs-build-downloads .\Win32OpenSSL-1_0_2d.exe /silent /verysilent /sp- /suppressmsgboxes Start-Sleep -s 30 cd C:\openvswitch build_script: - '"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\VsDevCmd"' - C:\MinGW\msys\1.0\bin\bash -lc "echo \"C:/MinGW /mingw\" > /etc/fstab" - C:\MinGW\msys\1.0\bin\bash -lc "cp /c/pthreads-win32/Pre-built.2/dll/x86/*.dll /c/openvswitch/." - C:\MinGW\msys\1.0\bin\bash -lc "mv /bin/link.exe /bin/link_copy.exe" - C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && ./boot.sh" - C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && ./configure CC=build-aux/cccl LD=\"`which link`\" LIBS=\"-lws2_32 -liphlpapi\" --with-pthread=C:/pthreads-win32/Pre-built.2 --with-openssl=C:/OpenSSL-Win32 --with-vstudiotarget=\"Debug\"" - C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && make" openvswitch-2.5.9/PaxHeaders.82075/ofproto0000644000000000000000000000013013534540121015251 xustar0029 mtime=1567801425.06185549 30 atime=1567801425.625859648 29 ctime=1567801425.06185549 openvswitch-2.5.9/ofproto/0000755000175000017500000000000013534540121017016 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/ofproto/PaxHeaders.82075/tunnel.c0000644000000000000000000000013013534540071017003 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.057855461 openvswitch-2.5.9/ofproto/tunnel.c0000644000175000017500000005661513534540071020510 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "tunnel.h" #include #include "byte-order.h" #include "connectivity.h" #include "csum.h" #include "dpif.h" #include "dynamic-string.h" #include "fat-rwlock.h" #include "hash.h" #include "hmap.h" #include "netdev.h" #include "odp-util.h" #include "ofpbuf.h" #include "packets.h" #include "route-table.h" #include "seq.h" #include "smap.h" #include "socket-util.h" #include "tnl-ports.h" #include "tunnel.h" #include "openvswitch/vlog.h" #include "unaligned.h" #include "ofproto-dpif.h" VLOG_DEFINE_THIS_MODULE(tunnel); /* skb mark used for IPsec tunnel packets */ #define IPSEC_MARK 1 struct tnl_match { ovs_be64 in_key; struct in6_addr ipv6_src; struct in6_addr ipv6_dst; odp_port_t odp_port; uint32_t pkt_mark; bool in_key_flow; bool ip_src_flow; bool ip_dst_flow; }; struct tnl_port { struct hmap_node ofport_node; struct hmap_node match_node; const struct ofport_dpif *ofport; uint64_t change_seq; struct netdev *netdev; struct tnl_match match; }; static struct fat_rwlock rwlock; /* Tunnel matches. * * This module maps packets received over tunnel protocols to vports. The * tunnel protocol and, for some protocols, tunnel-specific information (e.g., * for VXLAN, the UDP destination port number) are always use as part of the * mapping. Which other fields are used for the mapping depends on the vports * themselves (the parenthesized notations refer to "struct tnl_match" fields): * * - in_key: A vport may match a specific tunnel ID (in_key_flow == false) * or arrange for the tunnel ID to be matched as tunnel.tun_id in the * OpenFlow flow (in_key_flow == true). * * - ip_dst: A vport may match a specific destination IP address * (ip_dst_flow == false) or arrange for the destination IP to be matched * as tunnel.ip_dst in the OpenFlow flow (ip_dst_flow == true). * * - ip_src: A vport may match a specific IP source address (ip_src_flow == * false, ip_src != 0), wildcard all source addresses (ip_src_flow == * false, ip_src == 0), or arrange for the IP source address to be * handled in the OpenFlow flow table (ip_src_flow == true). * * Thus, there are 2 * 2 * 3 == 12 possible ways a vport can match against a * tunnel packet. We number the possibilities for each field in increasing * order as listed in each bullet above. We order the 12 overall combinations * in lexicographic order considering in_key first, then ip_dst, then * ip_src. */ #define N_MATCH_TYPES (2 * 2 * 3) /* The three possibilities (see above) for vport ip_src matches. */ enum ip_src_type { IP_SRC_CFG, /* ip_src must equal configured address. */ IP_SRC_ANY, /* Any ip_src is acceptable. */ IP_SRC_FLOW /* ip_src is handled in flow table. */ }; /* Each hmap contains "struct tnl_port"s. * The index is a combination of how each of the fields listed under "Tunnel * matches" above matches, see the final paragraph for ordering. */ static struct hmap *tnl_match_maps[N_MATCH_TYPES] OVS_GUARDED_BY(rwlock); static struct hmap **tnl_match_map(const struct tnl_match *); static struct hmap ofport_map__ = HMAP_INITIALIZER(&ofport_map__); static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60); static struct tnl_port *tnl_find(const struct flow *) OVS_REQ_RDLOCK(rwlock); static struct tnl_port *tnl_find_exact(struct tnl_match *, struct hmap *) OVS_REQ_RDLOCK(rwlock); static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *) OVS_REQ_RDLOCK(rwlock); static uint32_t tnl_hash(struct tnl_match *); static void tnl_match_fmt(const struct tnl_match *, struct ds *); static char *tnl_port_fmt(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock); static void tnl_port_mod_log(const struct tnl_port *, const char *action) OVS_REQ_RDLOCK(rwlock); static const char *tnl_port_get_name(const struct tnl_port *) OVS_REQ_RDLOCK(rwlock); static void tnl_port_del__(const struct ofport_dpif *) OVS_REQ_WRLOCK(rwlock); void ofproto_tunnel_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { fat_rwlock_init(&rwlock); ovsthread_once_done(&once); } } static bool tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev, odp_port_t odp_port, bool warn, bool native_tnl, const char name[]) OVS_REQ_WRLOCK(rwlock) { const struct netdev_tunnel_config *cfg; struct tnl_port *existing_port; struct tnl_port *tnl_port; struct hmap **map; cfg = netdev_get_tunnel_config(netdev); ovs_assert(cfg); tnl_port = xzalloc(sizeof *tnl_port); tnl_port->ofport = ofport; tnl_port->netdev = netdev_ref(netdev); tnl_port->change_seq = netdev_get_change_seq(tnl_port->netdev); tnl_port->match.in_key = cfg->in_key; tnl_port->match.ipv6_src = cfg->ipv6_src; tnl_port->match.ipv6_dst = cfg->ipv6_dst; tnl_port->match.ip_src_flow = cfg->ip_src_flow; tnl_port->match.ip_dst_flow = cfg->ip_dst_flow; tnl_port->match.pkt_mark = cfg->ipsec ? IPSEC_MARK : 0; tnl_port->match.in_key_flow = cfg->in_key_flow; tnl_port->match.odp_port = odp_port; map = tnl_match_map(&tnl_port->match); existing_port = tnl_find_exact(&tnl_port->match, *map); if (existing_port) { if (warn) { struct ds ds = DS_EMPTY_INITIALIZER; tnl_match_fmt(&tnl_port->match, &ds); VLOG_WARN("%s: attempting to add tunnel port with same config as " "port '%s' (%s)", tnl_port_get_name(tnl_port), tnl_port_get_name(existing_port), ds_cstr(&ds)); ds_destroy(&ds); } netdev_close(tnl_port->netdev); free(tnl_port); return false; } hmap_insert(ofport_map, &tnl_port->ofport_node, hash_pointer(ofport, 0)); if (!*map) { *map = xmalloc(sizeof **map); hmap_init(*map); } hmap_insert(*map, &tnl_port->match_node, tnl_hash(&tnl_port->match)); tnl_port_mod_log(tnl_port, "adding"); if (native_tnl) { tnl_port_map_insert(odp_port, cfg->dst_port, name); } return true; } /* Adds 'ofport' to the module with datapath port number 'odp_port'. 'ofport's * must be added before they can be used by the module. 'ofport' must be a * tunnel. * * Returns 0 if successful, otherwise a positive errno value. */ int tnl_port_add(const struct ofport_dpif *ofport, const struct netdev *netdev, odp_port_t odp_port, bool native_tnl, const char name[]) OVS_EXCLUDED(rwlock) { bool ok; fat_rwlock_wrlock(&rwlock); ok = tnl_port_add__(ofport, netdev, odp_port, true, native_tnl, name); fat_rwlock_unlock(&rwlock); return ok ? 0 : EEXIST; } /* Checks if the tunnel represented by 'ofport' reconfiguration due to changes * in its netdev_tunnel_config. If it does, returns true. Otherwise, returns * false. 'ofport' and 'odp_port' should be the same as would be passed to * tnl_port_add(). */ bool tnl_port_reconfigure(const struct ofport_dpif *ofport, const struct netdev *netdev, odp_port_t odp_port, bool native_tnl, const char name[]) OVS_EXCLUDED(rwlock) { struct tnl_port *tnl_port; bool changed = false; fat_rwlock_wrlock(&rwlock); tnl_port = tnl_find_ofport(ofport); if (!tnl_port) { changed = tnl_port_add__(ofport, netdev, odp_port, false, native_tnl, name); } else if (tnl_port->netdev != netdev || tnl_port->match.odp_port != odp_port || tnl_port->change_seq != netdev_get_change_seq(tnl_port->netdev)) { VLOG_DBG("reconfiguring %s", tnl_port_get_name(tnl_port)); tnl_port_del__(ofport); tnl_port_add__(ofport, netdev, odp_port, true, native_tnl, name); changed = true; } fat_rwlock_unlock(&rwlock); return changed; } static void tnl_port_del__(const struct ofport_dpif *ofport) OVS_REQ_WRLOCK(rwlock) { struct tnl_port *tnl_port; if (!ofport) { return; } tnl_port = tnl_find_ofport(ofport); if (tnl_port) { const struct netdev_tunnel_config *cfg = netdev_get_tunnel_config(tnl_port->netdev); struct hmap **map; tnl_port_map_delete(cfg->dst_port); tnl_port_mod_log(tnl_port, "removing"); map = tnl_match_map(&tnl_port->match); hmap_remove(*map, &tnl_port->match_node); if (hmap_is_empty(*map)) { hmap_destroy(*map); free(*map); *map = NULL; } hmap_remove(ofport_map, &tnl_port->ofport_node); netdev_close(tnl_port->netdev); free(tnl_port); } } /* Removes 'ofport' from the module. */ void tnl_port_del(const struct ofport_dpif *ofport) OVS_EXCLUDED(rwlock) { fat_rwlock_wrlock(&rwlock); tnl_port_del__(ofport); fat_rwlock_unlock(&rwlock); } /* Looks in the table of tunnels for a tunnel matching the metadata in 'flow'. * Returns the 'ofport' corresponding to the new in_port, or a null pointer if * none is found. * * Callers should verify that 'flow' needs to be received by calling * tnl_port_should_receive() before this function. */ const struct ofport_dpif * tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock) { char *pre_flow_str = NULL; const struct ofport_dpif *ofport; struct tnl_port *tnl_port; fat_rwlock_rdlock(&rwlock); tnl_port = tnl_find(flow); ofport = tnl_port ? tnl_port->ofport : NULL; if (!tnl_port) { char *flow_str = flow_to_string(flow); VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", flow_str); free(flow_str); goto out; } if (!VLOG_DROP_DBG(&dbg_rl)) { pre_flow_str = flow_to_string(flow); } if (pre_flow_str) { char *post_flow_str = flow_to_string(flow); char *tnl_str = tnl_port_fmt(tnl_port); VLOG_DBG("flow received\n" "%s" " pre: %s\n" "post: %s", tnl_str, pre_flow_str, post_flow_str); free(tnl_str); free(pre_flow_str); free(post_flow_str); } out: fat_rwlock_unlock(&rwlock); return ofport; } /* Should be called at the beginning of action translation to initialize * wildcards and perform any actions based on receiving on tunnel port. * * Returns false if the packet must be dropped. */ bool tnl_process_ecn(struct flow *flow) { if (!tnl_port_should_receive(flow)) { return true; } if (is_ip_any(flow) && (flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) { if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) { VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE" " but is not ECN capable"); return false; } /* Set the ECN CE value in the tunneled packet. */ flow->nw_tos |= IP_ECN_CE; } flow->pkt_mark &= ~IPSEC_MARK; return true; } void tnl_wc_init(struct flow *flow, struct flow_wildcards *wc) { if (tnl_port_should_receive(flow)) { wc->masks.tunnel.tun_id = OVS_BE64_MAX; if (flow->tunnel.ip_dst) { wc->masks.tunnel.ip_src = OVS_BE32_MAX; wc->masks.tunnel.ip_dst = OVS_BE32_MAX; } else { wc->masks.tunnel.ipv6_src = in6addr_exact; wc->masks.tunnel.ipv6_dst = in6addr_exact; } wc->masks.tunnel.flags = (FLOW_TNL_F_DONT_FRAGMENT | FLOW_TNL_F_CSUM | FLOW_TNL_F_KEY); wc->masks.tunnel.ip_tos = UINT8_MAX; wc->masks.tunnel.ip_ttl = UINT8_MAX; /* The tp_src and tp_dst members in flow_tnl are set to be always * wildcarded, not to unwildcard them here. */ wc->masks.tunnel.tp_src = 0; wc->masks.tunnel.tp_dst = 0; memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark); if (is_ip_any(flow) && (flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) { wc->masks.nw_tos |= IP_ECN_MASK; } } } /* Given that 'flow' should be output to the ofport corresponding to * 'tnl_port', updates 'flow''s tunnel headers and returns the actual datapath * port that the output should happen on. May return ODPP_NONE if the output * shouldn't occur. */ odp_port_t tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow, struct flow_wildcards *wc) OVS_EXCLUDED(rwlock) { const struct netdev_tunnel_config *cfg; struct tnl_port *tnl_port; char *pre_flow_str = NULL; odp_port_t out_port; fat_rwlock_rdlock(&rwlock); tnl_port = tnl_find_ofport(ofport); out_port = tnl_port ? tnl_port->match.odp_port : ODPP_NONE; if (!tnl_port) { goto out; } cfg = netdev_get_tunnel_config(tnl_port->netdev); ovs_assert(cfg); if (!VLOG_DROP_DBG(&dbg_rl)) { pre_flow_str = flow_to_string(flow); } if (!cfg->ip_src_flow) { flow->tunnel.ip_src = in6_addr_get_mapped_ipv4(&tnl_port->match.ipv6_src); if (!flow->tunnel.ip_src) { flow->tunnel.ipv6_src = tnl_port->match.ipv6_src; } } if (!cfg->ip_dst_flow) { flow->tunnel.ip_dst = in6_addr_get_mapped_ipv4(&tnl_port->match.ipv6_dst); if (!flow->tunnel.ip_dst) { flow->tunnel.ipv6_dst = tnl_port->match.ipv6_dst; } } if (ipv6_addr_is_set(&flow->tunnel.ipv6_dst) || ipv6_addr_is_set(&flow->tunnel.ipv6_src)) { out_port = ODPP_NONE; VLOG_WARN_RL(&rl, "port (%s): IPv6 tunnel endpoint is not supported", netdev_get_name(tnl_port->netdev)); goto out; } flow->tunnel.tp_dst = cfg->dst_port; flow->pkt_mark |= tnl_port->match.pkt_mark; wc->masks.pkt_mark |= tnl_port->match.pkt_mark; if (!cfg->out_key_flow) { flow->tunnel.tun_id = cfg->out_key; } if (cfg->ttl_inherit && is_ip_any(flow)) { wc->masks.nw_ttl = 0xff; flow->tunnel.ip_ttl = flow->nw_ttl; } else { flow->tunnel.ip_ttl = cfg->ttl; } if (cfg->tos_inherit && is_ip_any(flow)) { wc->masks.nw_tos |= IP_DSCP_MASK; flow->tunnel.ip_tos = flow->nw_tos & IP_DSCP_MASK; } else { flow->tunnel.ip_tos = cfg->tos; } /* ECN fields are always inherited. */ if (is_ip_any(flow)) { wc->masks.nw_tos |= IP_ECN_MASK; if ((flow->nw_tos & IP_ECN_MASK) == IP_ECN_CE) { flow->tunnel.ip_tos |= IP_ECN_ECT_0; } else { flow->tunnel.ip_tos |= flow->nw_tos & IP_ECN_MASK; } } flow->tunnel.flags |= (cfg->dont_fragment ? FLOW_TNL_F_DONT_FRAGMENT : 0) | (cfg->csum ? FLOW_TNL_F_CSUM : 0) | (cfg->out_key_present ? FLOW_TNL_F_KEY : 0); if (pre_flow_str) { char *post_flow_str = flow_to_string(flow); char *tnl_str = tnl_port_fmt(tnl_port); VLOG_DBG("flow sent\n" "%s" " pre: %s\n" "post: %s", tnl_str, pre_flow_str, post_flow_str); free(tnl_str); free(pre_flow_str); free(post_flow_str); } out: fat_rwlock_unlock(&rwlock); return out_port; } static uint32_t tnl_hash(struct tnl_match *match) { BUILD_ASSERT_DECL(sizeof *match % sizeof(uint32_t) == 0); return hash_words((uint32_t *) match, sizeof *match / sizeof(uint32_t), 0); } static struct tnl_port * tnl_find_ofport(const struct ofport_dpif *ofport) OVS_REQ_RDLOCK(rwlock) { struct tnl_port *tnl_port; HMAP_FOR_EACH_IN_BUCKET (tnl_port, ofport_node, hash_pointer(ofport, 0), ofport_map) { if (tnl_port->ofport == ofport) { return tnl_port; } } return NULL; } static struct tnl_port * tnl_find_exact(struct tnl_match *match, struct hmap *map) OVS_REQ_RDLOCK(rwlock) { if (map) { struct tnl_port *tnl_port; HMAP_FOR_EACH_WITH_HASH (tnl_port, match_node, tnl_hash(match), map) { if (!memcmp(match, &tnl_port->match, sizeof *match)) { return tnl_port; } } } return NULL; } /* Returns the tnl_port that is the best match for the tunnel data in 'flow', * or NULL if no tnl_port matches 'flow'. */ static struct tnl_port * tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) { enum ip_src_type ip_src; int in_key_flow; int ip_dst_flow; int i; i = 0; for (in_key_flow = 0; in_key_flow < 2; in_key_flow++) { for (ip_dst_flow = 0; ip_dst_flow < 2; ip_dst_flow++) { for (ip_src = 0; ip_src < 3; ip_src++) { struct hmap *map = tnl_match_maps[i]; if (map) { struct tnl_port *tnl_port; struct tnl_match match; memset(&match, 0, sizeof match); /* The apparent mix-up of 'ip_dst' and 'ip_src' below is * correct, because "struct tnl_match" is expressed in * terms of packets being sent out, but we are using it * here as a description of how to treat received * packets. */ match.in_key = in_key_flow ? 0 : flow->tunnel.tun_id; if (ip_src == IP_SRC_CFG) { match.ipv6_src = flow_tnl_dst(&flow->tunnel); } if (!ip_dst_flow) { match.ipv6_dst = flow_tnl_src(&flow->tunnel); } match.odp_port = flow->in_port.odp_port; match.pkt_mark = flow->pkt_mark; match.in_key_flow = in_key_flow; match.ip_dst_flow = ip_dst_flow; match.ip_src_flow = ip_src == IP_SRC_FLOW; tnl_port = tnl_find_exact(&match, map); if (tnl_port) { return tnl_port; } } i++; } } } return NULL; } /* Returns a pointer to the 'tnl_match_maps' element corresponding to 'm''s * matching criteria. */ static struct hmap ** tnl_match_map(const struct tnl_match *m) { enum ip_src_type ip_src; ip_src = (m->ip_src_flow ? IP_SRC_FLOW : ipv6_addr_is_set(&m->ipv6_src) ? IP_SRC_CFG : IP_SRC_ANY); return &tnl_match_maps[6 * m->in_key_flow + 3 * m->ip_dst_flow + ip_src]; } static void tnl_match_fmt(const struct tnl_match *match, struct ds *ds) OVS_REQ_RDLOCK(rwlock) { if (!match->ip_dst_flow) { ipv6_format_mapped(&match->ipv6_src, ds); ds_put_cstr(ds, "->"); ipv6_format_mapped(&match->ipv6_dst, ds); } else if (!match->ip_src_flow) { ipv6_format_mapped(&match->ipv6_src, ds); ds_put_cstr(ds, "->flow"); } else { ds_put_cstr(ds, "flow->flow"); } if (match->in_key_flow) { ds_put_cstr(ds, ", key=flow"); } else { ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); } ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port); ds_put_format(ds, ", pkt mark=%"PRIu32, match->pkt_mark); } static void tnl_port_mod_log(const struct tnl_port *tnl_port, const char *action) OVS_REQ_RDLOCK(rwlock) { if (VLOG_IS_DBG_ENABLED()) { struct ds ds = DS_EMPTY_INITIALIZER; tnl_match_fmt(&tnl_port->match, &ds); VLOG_INFO("%s tunnel port %s (%s)", action, tnl_port_get_name(tnl_port), ds_cstr(&ds)); ds_destroy(&ds); } } static char * tnl_port_fmt(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) { const struct netdev_tunnel_config *cfg = netdev_get_tunnel_config(tnl_port->netdev); struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "port %"PRIu32": %s (%s: ", tnl_port->match.odp_port, tnl_port_get_name(tnl_port), netdev_get_type(tnl_port->netdev)); tnl_match_fmt(&tnl_port->match, &ds); if (cfg->out_key != cfg->in_key || cfg->out_key_present != cfg->in_key_present || cfg->out_key_flow != cfg->in_key_flow) { ds_put_cstr(&ds, ", out_key="); if (!cfg->out_key_present) { ds_put_cstr(&ds, "none"); } else if (cfg->out_key_flow) { ds_put_cstr(&ds, "flow"); } else { ds_put_format(&ds, "%#"PRIx64, ntohll(cfg->out_key)); } } if (cfg->ttl_inherit) { ds_put_cstr(&ds, ", ttl=inherit"); } else { ds_put_format(&ds, ", ttl=%"PRIu8, cfg->ttl); } if (cfg->tos_inherit) { ds_put_cstr(&ds, ", tos=inherit"); } else if (cfg->tos) { ds_put_format(&ds, ", tos=%#"PRIx8, cfg->tos); } if (!cfg->dont_fragment) { ds_put_cstr(&ds, ", df=false"); } if (cfg->csum) { ds_put_cstr(&ds, ", csum=true"); } ds_put_cstr(&ds, ")\n"); return ds_steal_cstr(&ds); } static const char * tnl_port_get_name(const struct tnl_port *tnl_port) OVS_REQ_RDLOCK(rwlock) { return netdev_get_name(tnl_port->netdev); } int tnl_port_build_header(const struct ofport_dpif *ofport, const struct flow *tnl_flow, const struct eth_addr dmac, const struct eth_addr smac, const struct in6_addr * ipv6_src, struct ovs_action_push_tnl *data) { struct tnl_port *tnl_port; struct eth_header *eth; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; void *l3; int res; ovs_be32 ip_src; fat_rwlock_rdlock(&rwlock); tnl_port = tnl_find_ofport(ofport); ovs_assert(tnl_port); ip_src = in6_addr_get_mapped_ipv4(ipv6_src); /* Build Ethernet and IP headers. */ memset(data->header, 0, sizeof data->header); eth = (struct eth_header *)data->header; eth->eth_dst = dmac; eth->eth_src = smac; eth->eth_type = ip_src ? htons(ETH_TYPE_IP) : htons(ETH_TYPE_IPV6); l3 = (eth + 1); if (ip_src) { ip = (struct ip_header *) l3; ip->ip_ihl_ver = IP_IHL_VER(5, 4); ip->ip_tos = tnl_flow->tunnel.ip_tos; ip->ip_ttl = tnl_flow->tunnel.ip_ttl; ip->ip_frag_off = (tnl_flow->tunnel.flags & FLOW_TNL_F_DONT_FRAGMENT) ? htons(IP_DF) : 0; put_16aligned_be32(&ip->ip_src, ip_src); put_16aligned_be32(&ip->ip_dst, tnl_flow->tunnel.ip_dst); } else { ip6 = (struct ovs_16aligned_ip6_hdr *) l3; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = tnl_flow->tunnel.ip_ttl; /* next header, plen - at netdev_build_header? */ memcpy(&ip6->ip6_src, ipv6_src, sizeof(ovs_be32[4])); memcpy(&ip6->ip6_dst, &tnl_flow->tunnel.ipv6_dst, sizeof(ovs_be32[4])); } res = netdev_build_header(tnl_port->netdev, data, tnl_flow); if (ip_src) { ip->ip_csum = csum(ip, sizeof *ip); } fat_rwlock_unlock(&rwlock); return res; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-mirror.h0000644000000000000000000000013113534540071021424 xustar0030 mtime=1567801401.629682844 29 atime=1567801402.10568634 30 ctime=1567801425.041855343 openvswitch-2.5.9/ofproto/ofproto-dpif-mirror.h0000644000175000017500000000412313534540071023113 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_MIRROR_H #define OFPROTO_DPIF_MIRROR_H 1 #include #include "util.h" #define MAX_MIRRORS 32 typedef uint32_t mirror_mask_t; struct ofproto_dpif; struct ofbundle; struct mbridge *mbridge_create(void); struct mbridge *mbridge_ref(const struct mbridge *); void mbridge_unref(struct mbridge *); bool mbridge_has_mirrors(struct mbridge *); bool mbridge_need_revalidate(struct mbridge *); void mbridge_register_bundle(struct mbridge *, struct ofbundle *); void mbridge_unregister_bundle(struct mbridge *, struct ofbundle *); mirror_mask_t mirror_bundle_out(struct mbridge *, struct ofbundle *); mirror_mask_t mirror_bundle_src(struct mbridge *, struct ofbundle *); mirror_mask_t mirror_bundle_dst(struct mbridge *, struct ofbundle *); int mirror_set(struct mbridge *, void *aux, const char *name, struct ofbundle **srcs, size_t n_srcs, struct ofbundle **dsts, size_t n_dsts, unsigned long *src_vlans, struct ofbundle *out_bundle, uint16_t out_vlan); void mirror_destroy(struct mbridge *, void *aux); int mirror_get_stats(struct mbridge *, void *aux, uint64_t *packets, uint64_t *bytes); void mirror_update_stats(struct mbridge*, mirror_mask_t, uint64_t packets, uint64_t bytes); bool mirror_get(struct mbridge *, int index, const unsigned long **vlans, mirror_mask_t *dup_mirrors, struct ofbundle **out, int *out_vlan); #endif /* ofproto-dpif-mirror.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-upcall.c0000644000000000000000000000013113534540071021365 xustar0030 mtime=1567801401.637682902 29 atime=1567801402.10968637 30 ctime=1567801425.049855401 openvswitch-2.5.9/ofproto/ofproto-dpif-upcall.c0000644000175000017500000025564313534540071023073 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto-dpif-upcall.h" #include #include #include #include "connmgr.h" #include "coverage.h" #include "cmap.h" #include "dpif.h" #include "dynamic-string.h" #include "fail-open.h" #include "guarded-list.h" #include "latch.h" #include "list.h" #include "netlink.h" #include "ofpbuf.h" #include "ofproto-dpif-ipfix.h" #include "ofproto-dpif-sflow.h" #include "ofproto-dpif-xlate.h" #include "ovs-rcu.h" #include "packets.h" #include "poll-loop.h" #include "seq.h" #include "unixctl.h" #include "openvswitch/vlog.h" #define MAX_QUEUE_LENGTH 512 #define UPCALL_MAX_BATCH 64 #define REVALIDATE_MAX_BATCH 50 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_upcall); COVERAGE_DEFINE(dumped_duplicate_flow); COVERAGE_DEFINE(dumped_new_flow); COVERAGE_DEFINE(handler_duplicate_upcall); COVERAGE_DEFINE(upcall_ukey_contention); COVERAGE_DEFINE(revalidate_missed_dp_flow); /* A thread that reads upcalls from dpif, forwards each upcall's packet, * and possibly sets up a kernel flow as a cache. */ struct handler { struct udpif *udpif; /* Parent udpif. */ pthread_t thread; /* Thread ID. */ uint32_t handler_id; /* Handler id. */ }; /* In the absence of a multiple-writer multiple-reader datastructure for * storing ukeys, we use a large number of cmaps, each with its own lock for * writing. */ #define N_UMAPS 512 /* per udpif. */ struct umap { struct ovs_mutex mutex; /* Take for writing to the following. */ struct cmap cmap; /* Datapath flow keys. */ }; /* A thread that processes datapath flows, updates OpenFlow statistics, and * updates or removes them if necessary. */ struct revalidator { struct udpif *udpif; /* Parent udpif. */ pthread_t thread; /* Thread ID. */ unsigned int id; /* ovsthread_id_self(). */ }; /* An upcall handler for ofproto_dpif. * * udpif keeps records of two kind of logically separate units: * * upcall handling * --------------- * * - An array of 'struct handler's for upcall handling and flow * installation. * * flow revalidation * ----------------- * * - Revalidation threads which read the datapath flow table and maintains * them. */ struct udpif { struct ovs_list list_node; /* In all_udpifs list. */ struct dpif *dpif; /* Datapath handle. */ struct dpif_backer *backer; /* Opaque dpif_backer pointer. */ struct handler *handlers; /* Upcall handlers. */ size_t n_handlers; struct revalidator *revalidators; /* Flow revalidators. */ size_t n_revalidators; struct latch exit_latch; /* Tells child threads to exit. */ /* Revalidation. */ struct seq *reval_seq; /* Incremented to force revalidation. */ bool reval_exit; /* Set by leader on 'exit_latch. */ struct ovs_barrier reval_barrier; /* Barrier used by revalidators. */ struct dpif_flow_dump *dump; /* DPIF flow dump state. */ long long int dump_duration; /* Duration of the last flow dump. */ struct seq *dump_seq; /* Increments each dump iteration. */ atomic_bool enable_ufid; /* If true, skip dumping flow attrs. */ /* These variables provide a mechanism for the main thread to pause * all revalidation without having to completely shut the threads down. * 'pause_latch' is shared between the main thread and the lead * revalidator thread, so when it is desirable to halt revalidation, the * main thread will set the latch. 'pause' and 'pause_barrier' are shared * by revalidator threads. The lead revalidator will set 'pause' when it * observes the latch has been set, and this will cause all revalidator * threads to wait on 'pause_barrier' at the beginning of the next * revalidation round. */ bool pause; /* Set by leader on 'pause_latch. */ struct latch pause_latch; /* Set to force revalidators pause. */ struct ovs_barrier pause_barrier; /* Barrier used to pause all */ /* revalidators by main thread. */ /* There are 'N_UMAPS' maps containing 'struct udpif_key' elements. * * During the flow dump phase, revalidators insert into these with a random * distribution. During the garbage collection phase, each revalidator * takes care of garbage collecting a slice of these maps. */ struct umap *ukeys; /* Datapath flow statistics. */ unsigned int max_n_flows; unsigned int avg_n_flows; /* Following fields are accessed and modified by different threads. */ atomic_uint flow_limit; /* Datapath flow hard limit. */ /* n_flows_mutex prevents multiple threads updating these concurrently. */ atomic_uint n_flows; /* Number of flows in the datapath. */ atomic_llong n_flows_timestamp; /* Last time n_flows was updated. */ struct ovs_mutex n_flows_mutex; /* Following fields are accessed and modified only from the main thread. */ struct unixctl_conn **conns; /* Connections waiting on dump_seq. */ uint64_t conn_seq; /* Corresponds to 'dump_seq' when conns[n_conns-1] was stored. */ size_t n_conns; /* Number of connections waiting. */ }; enum upcall_type { BAD_UPCALL, /* Some kind of bug somewhere. */ MISS_UPCALL, /* A flow miss. */ SFLOW_UPCALL, /* sFlow sample. */ FLOW_SAMPLE_UPCALL, /* Per-flow sampling. */ IPFIX_UPCALL /* Per-bridge sampling. */ }; enum reval_result { UKEY_KEEP, UKEY_DELETE, UKEY_MODIFY }; struct upcall { struct ofproto_dpif *ofproto; /* Parent ofproto. */ const struct recirc_id_node *recirc; /* Recirculation context. */ bool have_recirc_ref; /* Reference held on recirc ctx? */ /* The flow and packet are only required to be constant when using * dpif-netdev. If a modification is absolutely necessary, a const cast * may be used with other datapaths. */ const struct flow *flow; /* Parsed representation of the packet. */ const ovs_u128 *ufid; /* Unique identifier for 'flow'. */ unsigned pmd_id; /* Datapath poll mode driver id. */ const struct dp_packet *packet; /* Packet associated with this upcall. */ ofp_port_t in_port; /* OpenFlow in port, or OFPP_NONE. */ uint16_t mru; /* If !0, Maximum receive unit of fragmented IP packet */ enum dpif_upcall_type type; /* Datapath type of the upcall. */ const struct nlattr *userdata; /* Userdata for DPIF_UC_ACTION Upcalls. */ const struct nlattr *actions; /* Flow actions in DPIF_UC_ACTION Upcalls. */ bool xout_initialized; /* True if 'xout' must be uninitialized. */ struct xlate_out xout; /* Result of xlate_actions(). */ struct ofpbuf odp_actions; /* Datapath actions from xlate_actions(). */ struct flow_wildcards wc; /* Dependencies that megaflow must match. */ struct ofpbuf put_actions; /* Actions 'put' in the fastpath. */ struct dpif_ipfix *ipfix; /* IPFIX pointer or NULL. */ struct dpif_sflow *sflow; /* SFlow pointer or NULL. */ bool vsp_adjusted; /* 'packet' and 'flow' were adjusted for VLAN splinters if true. */ struct udpif_key *ukey; /* Revalidator flow cache. */ bool ukey_persists; /* Set true to keep 'ukey' beyond the lifetime of this upcall. */ uint64_t dump_seq; /* udpif->dump_seq at translation time. */ uint64_t reval_seq; /* udpif->reval_seq at translation time. */ /* Not used by the upcall callback interface. */ const struct nlattr *key; /* Datapath flow key. */ size_t key_len; /* Datapath flow key length. */ const struct nlattr *out_tun_key; /* Datapath output tunnel key. */ uint64_t odp_actions_stub[1024 / 8]; /* Stub for odp_actions. */ }; /* 'udpif_key's are responsible for tracking the little bit of state udpif * needs to do flow expiration which can't be pulled directly from the * datapath. They may be created by any handler or revalidator thread at any * time, and read by any revalidator during the dump phase. They are however * each owned by a single revalidator which takes care of destroying them * during the garbage-collection phase. * * The mutex within the ukey protects some members of the ukey. The ukey * itself is protected by RCU and is held within a umap in the parent udpif. * Adding or removing a ukey from a umap is only safe when holding the * corresponding umap lock. */ struct udpif_key { struct cmap_node cmap_node; /* In parent revalidator 'ukeys' map. */ /* These elements are read only once created, and therefore aren't * protected by a mutex. */ const struct nlattr *key; /* Datapath flow key. */ size_t key_len; /* Length of 'key'. */ const struct nlattr *mask; /* Datapath flow mask. */ size_t mask_len; /* Length of 'mask'. */ ovs_u128 ufid; /* Unique flow identifier. */ bool ufid_present; /* True if 'ufid' is in datapath. */ uint32_t hash; /* Pre-computed hash for 'key'. */ unsigned pmd_id; /* Datapath poll mode driver id. */ struct ovs_mutex mutex; /* Guards the following. */ struct dpif_flow_stats stats OVS_GUARDED; /* Last known stats.*/ long long int created OVS_GUARDED; /* Estimate of creation time. */ uint64_t dump_seq OVS_GUARDED; /* Tracks udpif->dump_seq. */ uint64_t reval_seq OVS_GUARDED; /* Tracks udpif->reval_seq. */ bool flow_exists OVS_GUARDED; /* Ensures flows are only deleted once. */ /* Datapath flow actions as nlattrs. Protected by RCU. Read with * ukey_get_actions(), and write with ukey_set_actions(). */ OVSRCU_TYPE(struct ofpbuf *) actions; struct xlate_cache *xcache OVS_GUARDED; /* Cache for xlate entries that * are affected by this ukey. * Used for stats and learning.*/ union { struct odputil_keybuf buf; struct nlattr nla; } keybuf, maskbuf; uint32_t key_recirc_id; /* Non-zero if reference is held by the ukey. */ struct recirc_refs recircs; /* Action recirc IDs with references held. */ }; /* Datapath operation with optional ukey attached. */ struct ukey_op { struct udpif_key *ukey; struct dpif_flow_stats stats; /* Stats for 'op'. */ struct dpif_op dop; /* Flow operation. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static struct ovs_list all_udpifs = OVS_LIST_INITIALIZER(&all_udpifs); static size_t recv_upcalls(struct handler *); static int process_upcall(struct udpif *, struct upcall *, struct ofpbuf *odp_actions, struct flow_wildcards *); static void handle_upcalls(struct udpif *, struct upcall *, size_t n_upcalls); static void udpif_stop_threads(struct udpif *); static void udpif_start_threads(struct udpif *, size_t n_handlers, size_t n_revalidators); static void udpif_pause_revalidators(struct udpif *); static void udpif_resume_revalidators(struct udpif *); static void *udpif_upcall_handler(void *); static void *udpif_revalidator(void *); static unsigned long udpif_get_n_flows(struct udpif *); static void revalidate(struct revalidator *); static void revalidator_pause(struct revalidator *); static void revalidator_sweep(struct revalidator *); static void revalidator_purge(struct revalidator *); static void upcall_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); static void upcall_unixctl_disable_megaflows(struct unixctl_conn *, int argc, const char *argv[], void *aux); static void upcall_unixctl_enable_megaflows(struct unixctl_conn *, int argc, const char *argv[], void *aux); static void upcall_unixctl_disable_ufid(struct unixctl_conn *, int argc, const char *argv[], void *aux); static void upcall_unixctl_enable_ufid(struct unixctl_conn *, int argc, const char *argv[], void *aux); static void upcall_unixctl_set_flow_limit(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); static void upcall_unixctl_purge(struct unixctl_conn *conn, int argc, const char *argv[], void *aux); static struct udpif_key *ukey_create_from_upcall(struct upcall *, struct flow_wildcards *); static int ukey_create_from_dpif_flow(const struct udpif *, const struct dpif_flow *, struct udpif_key **); static void ukey_get_actions(struct udpif_key *, const struct nlattr **actions, size_t *size); static bool ukey_install_start(struct udpif *, struct udpif_key *ukey); static bool ukey_install_finish(struct udpif_key *ukey, int error); static bool ukey_install(struct udpif *udpif, struct udpif_key *ukey); static struct udpif_key *ukey_lookup(struct udpif *udpif, const ovs_u128 *ufid); static int ukey_acquire(struct udpif *, const struct dpif_flow *, struct udpif_key **result, int *error); static void ukey_delete__(struct udpif_key *); static void ukey_delete(struct umap *, struct udpif_key *); static enum upcall_type classify_upcall(enum dpif_upcall_type type, const struct nlattr *userdata); static int upcall_receive(struct upcall *, const struct dpif_backer *, const struct dp_packet *packet, enum dpif_upcall_type, const struct nlattr *userdata, const struct flow *, const unsigned int mru, const ovs_u128 *ufid, const unsigned pmd_id); static void upcall_uninit(struct upcall *); static upcall_callback upcall_cb; static dp_purge_callback dp_purge_cb; static atomic_bool enable_megaflows = ATOMIC_VAR_INIT(true); static atomic_bool enable_ufid = ATOMIC_VAR_INIT(true); void udpif_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { unixctl_command_register("upcall/show", "", 0, 0, upcall_unixctl_show, NULL); unixctl_command_register("upcall/disable-megaflows", "", 0, 0, upcall_unixctl_disable_megaflows, NULL); unixctl_command_register("upcall/enable-megaflows", "", 0, 0, upcall_unixctl_enable_megaflows, NULL); unixctl_command_register("upcall/disable-ufid", "", 0, 0, upcall_unixctl_disable_ufid, NULL); unixctl_command_register("upcall/enable-ufid", "", 0, 0, upcall_unixctl_enable_ufid, NULL); unixctl_command_register("upcall/set-flow-limit", "", 1, 1, upcall_unixctl_set_flow_limit, NULL); unixctl_command_register("revalidator/wait", "", 0, 0, upcall_unixctl_dump_wait, NULL); unixctl_command_register("revalidator/purge", "", 0, 0, upcall_unixctl_purge, NULL); ovsthread_once_done(&once); } } struct udpif * udpif_create(struct dpif_backer *backer, struct dpif *dpif) { struct udpif *udpif = xzalloc(sizeof *udpif); udpif->dpif = dpif; udpif->backer = backer; atomic_init(&udpif->flow_limit, MIN(ofproto_flow_limit, 10000)); udpif->reval_seq = seq_create(); udpif->dump_seq = seq_create(); latch_init(&udpif->exit_latch); latch_init(&udpif->pause_latch); list_push_back(&all_udpifs, &udpif->list_node); atomic_init(&udpif->enable_ufid, false); atomic_init(&udpif->n_flows, 0); atomic_init(&udpif->n_flows_timestamp, LLONG_MIN); ovs_mutex_init(&udpif->n_flows_mutex); udpif->ukeys = xmalloc(N_UMAPS * sizeof *udpif->ukeys); for (int i = 0; i < N_UMAPS; i++) { cmap_init(&udpif->ukeys[i].cmap); ovs_mutex_init(&udpif->ukeys[i].mutex); } dpif_register_upcall_cb(dpif, upcall_cb, udpif); dpif_register_dp_purge_cb(dpif, dp_purge_cb, udpif); return udpif; } void udpif_run(struct udpif *udpif) { if (udpif->conns && udpif->conn_seq != seq_read(udpif->dump_seq)) { int i; for (i = 0; i < udpif->n_conns; i++) { unixctl_command_reply(udpif->conns[i], NULL); } free(udpif->conns); udpif->conns = NULL; udpif->n_conns = 0; } } void udpif_destroy(struct udpif *udpif) { udpif_stop_threads(udpif); dpif_register_dp_purge_cb(udpif->dpif, NULL, udpif); dpif_register_upcall_cb(udpif->dpif, NULL, udpif); for (int i = 0; i < N_UMAPS; i++) { cmap_destroy(&udpif->ukeys[i].cmap); ovs_mutex_destroy(&udpif->ukeys[i].mutex); } free(udpif->ukeys); udpif->ukeys = NULL; list_remove(&udpif->list_node); latch_destroy(&udpif->exit_latch); latch_destroy(&udpif->pause_latch); seq_destroy(udpif->reval_seq); seq_destroy(udpif->dump_seq); ovs_mutex_destroy(&udpif->n_flows_mutex); free(udpif); } /* Stops the handler and revalidator threads, must be enclosed in * ovsrcu quiescent state unless when destroying udpif. */ static void udpif_stop_threads(struct udpif *udpif) { if (udpif && (udpif->n_handlers != 0 || udpif->n_revalidators != 0)) { size_t i; latch_set(&udpif->exit_latch); for (i = 0; i < udpif->n_handlers; i++) { struct handler *handler = &udpif->handlers[i]; xpthread_join(handler->thread, NULL); } for (i = 0; i < udpif->n_revalidators; i++) { xpthread_join(udpif->revalidators[i].thread, NULL); } dpif_disable_upcall(udpif->dpif); for (i = 0; i < udpif->n_revalidators; i++) { struct revalidator *revalidator = &udpif->revalidators[i]; /* Delete ukeys, and delete all flows from the datapath to prevent * double-counting stats. */ revalidator_purge(revalidator); } latch_poll(&udpif->exit_latch); ovs_barrier_destroy(&udpif->reval_barrier); ovs_barrier_destroy(&udpif->pause_barrier); free(udpif->revalidators); udpif->revalidators = NULL; udpif->n_revalidators = 0; free(udpif->handlers); udpif->handlers = NULL; udpif->n_handlers = 0; } } /* Starts the handler and revalidator threads, must be enclosed in * ovsrcu quiescent state. */ static void udpif_start_threads(struct udpif *udpif, size_t n_handlers, size_t n_revalidators) { if (udpif && n_handlers && n_revalidators) { size_t i; bool enable_ufid; udpif->n_handlers = n_handlers; udpif->n_revalidators = n_revalidators; udpif->handlers = xzalloc(udpif->n_handlers * sizeof *udpif->handlers); for (i = 0; i < udpif->n_handlers; i++) { struct handler *handler = &udpif->handlers[i]; handler->udpif = udpif; handler->handler_id = i; handler->thread = ovs_thread_create( "handler", udpif_upcall_handler, handler); } enable_ufid = ofproto_dpif_get_enable_ufid(udpif->backer); atomic_init(&udpif->enable_ufid, enable_ufid); dpif_enable_upcall(udpif->dpif); ovs_barrier_init(&udpif->reval_barrier, udpif->n_revalidators); ovs_barrier_init(&udpif->pause_barrier, udpif->n_revalidators + 1); udpif->reval_exit = false; udpif->pause = false; udpif->revalidators = xzalloc(udpif->n_revalidators * sizeof *udpif->revalidators); for (i = 0; i < udpif->n_revalidators; i++) { struct revalidator *revalidator = &udpif->revalidators[i]; revalidator->udpif = udpif; revalidator->thread = ovs_thread_create( "revalidator", udpif_revalidator, revalidator); } } } /* Pauses all revalidators. Should only be called by the main thread. * When function returns, all revalidators are paused and will proceed * only after udpif_resume_revalidators() is called. */ static void udpif_pause_revalidators(struct udpif *udpif) { if (ofproto_dpif_backer_enabled(udpif->backer)) { latch_set(&udpif->pause_latch); ovs_barrier_block(&udpif->pause_barrier); } } /* Resumes the pausing of revalidators. Should only be called by the * main thread. */ static void udpif_resume_revalidators(struct udpif *udpif) { if (ofproto_dpif_backer_enabled(udpif->backer)) { latch_poll(&udpif->pause_latch); ovs_barrier_block(&udpif->pause_barrier); } } /* Tells 'udpif' how many threads it should use to handle upcalls. * 'n_handlers' and 'n_revalidators' can never be zero. 'udpif''s * datapath handle must have packet reception enabled before starting * threads. */ void udpif_set_threads(struct udpif *udpif, size_t n_handlers, size_t n_revalidators) { ovs_assert(udpif); ovs_assert(n_handlers && n_revalidators); ovsrcu_quiesce_start(); if (udpif->n_handlers != n_handlers || udpif->n_revalidators != n_revalidators) { udpif_stop_threads(udpif); } if (!udpif->handlers && !udpif->revalidators) { int error; error = dpif_handlers_set(udpif->dpif, n_handlers); if (error) { VLOG_ERR("failed to configure handlers in dpif %s: %s", dpif_name(udpif->dpif), ovs_strerror(error)); return; } udpif_start_threads(udpif, n_handlers, n_revalidators); } ovsrcu_quiesce_end(); } /* Waits for all ongoing upcall translations to complete. This ensures that * there are no transient references to any removed ofprotos (or other * objects). In particular, this should be called after an ofproto is removed * (e.g. via xlate_remove_ofproto()) but before it is destroyed. */ void udpif_synchronize(struct udpif *udpif) { /* This is stronger than necessary. It would be sufficient to ensure * (somehow) that each handler and revalidator thread had passed through * its main loop once. */ size_t n_handlers = udpif->n_handlers; size_t n_revalidators = udpif->n_revalidators; ovsrcu_quiesce_start(); udpif_stop_threads(udpif); udpif_start_threads(udpif, n_handlers, n_revalidators); ovsrcu_quiesce_end(); } /* Notifies 'udpif' that something changed which may render previous * xlate_actions() results invalid. */ void udpif_revalidate(struct udpif *udpif) { seq_change(udpif->reval_seq); } /* Returns a seq which increments every time 'udpif' pulls stats from the * datapath. Callers can use this to get a sense of when might be a good time * to do periodic work which relies on relatively up to date statistics. */ struct seq * udpif_dump_seq(struct udpif *udpif) { return udpif->dump_seq; } void udpif_get_memory_usage(struct udpif *udpif, struct simap *usage) { size_t i; simap_increase(usage, "handlers", udpif->n_handlers); simap_increase(usage, "revalidators", udpif->n_revalidators); for (i = 0; i < N_UMAPS; i++) { simap_increase(usage, "udpif keys", cmap_count(&udpif->ukeys[i].cmap)); } } /* Remove flows from a single datapath. */ void udpif_flush(struct udpif *udpif) { size_t n_handlers, n_revalidators; n_handlers = udpif->n_handlers; n_revalidators = udpif->n_revalidators; ovsrcu_quiesce_start(); udpif_stop_threads(udpif); dpif_flow_flush(udpif->dpif); udpif_start_threads(udpif, n_handlers, n_revalidators); ovsrcu_quiesce_end(); } /* Removes all flows from all datapaths. */ static void udpif_flush_all_datapaths(void) { struct udpif *udpif; LIST_FOR_EACH (udpif, list_node, &all_udpifs) { udpif_flush(udpif); } } static bool udpif_use_ufid(struct udpif *udpif) { bool enable; atomic_read_relaxed(&enable_ufid, &enable); return enable && ofproto_dpif_get_enable_ufid(udpif->backer); } static unsigned long udpif_get_n_flows(struct udpif *udpif) { long long int time, now; unsigned long flow_count; now = time_msec(); atomic_read_relaxed(&udpif->n_flows_timestamp, &time); if (time < now - 100 && !ovs_mutex_trylock(&udpif->n_flows_mutex)) { struct dpif_dp_stats stats; atomic_store_relaxed(&udpif->n_flows_timestamp, now); dpif_get_dp_stats(udpif->dpif, &stats); flow_count = stats.n_flows; atomic_store_relaxed(&udpif->n_flows, flow_count); ovs_mutex_unlock(&udpif->n_flows_mutex); } else { atomic_read_relaxed(&udpif->n_flows, &flow_count); } return flow_count; } /* The upcall handler thread tries to read a batch of UPCALL_MAX_BATCH * upcalls from dpif, processes the batch and installs corresponding flows * in dpif. */ static void * udpif_upcall_handler(void *arg) { struct handler *handler = arg; struct udpif *udpif = handler->udpif; while (!latch_is_set(&handler->udpif->exit_latch)) { if (recv_upcalls(handler)) { poll_immediate_wake(); } else { dpif_recv_wait(udpif->dpif, handler->handler_id); latch_wait(&udpif->exit_latch); } poll_block(); } return NULL; } static size_t recv_upcalls(struct handler *handler) { struct udpif *udpif = handler->udpif; uint64_t recv_stubs[UPCALL_MAX_BATCH][512 / 8]; struct ofpbuf recv_bufs[UPCALL_MAX_BATCH]; struct dpif_upcall dupcalls[UPCALL_MAX_BATCH]; struct upcall upcalls[UPCALL_MAX_BATCH]; struct flow flows[UPCALL_MAX_BATCH]; size_t n_upcalls, i; n_upcalls = 0; while (n_upcalls < UPCALL_MAX_BATCH) { struct ofpbuf *recv_buf = &recv_bufs[n_upcalls]; struct dpif_upcall *dupcall = &dupcalls[n_upcalls]; struct upcall *upcall = &upcalls[n_upcalls]; struct flow *flow = &flows[n_upcalls]; unsigned int mru; int error; ofpbuf_use_stub(recv_buf, recv_stubs[n_upcalls], sizeof recv_stubs[n_upcalls]); if (dpif_recv(udpif->dpif, handler->handler_id, dupcall, recv_buf)) { ofpbuf_uninit(recv_buf); break; } if (odp_flow_key_to_flow(dupcall->key, dupcall->key_len, flow) == ODP_FIT_ERROR) { goto free_dupcall; } if (dupcall->mru) { mru = nl_attr_get_u16(dupcall->mru); } else { mru = 0; } error = upcall_receive(upcall, udpif->backer, &dupcall->packet, dupcall->type, dupcall->userdata, flow, mru, &dupcall->ufid, PMD_ID_NULL); if (error) { if (error == ENODEV) { /* Received packet on datapath port for which we couldn't * associate an ofproto. This can happen if a port is removed * while traffic is being received. Print a rate-limited * message in case it happens frequently. */ dpif_flow_put(udpif->dpif, DPIF_FP_CREATE, dupcall->key, dupcall->key_len, NULL, 0, NULL, 0, &dupcall->ufid, PMD_ID_NULL, NULL); VLOG_INFO_RL(&rl, "received packet on unassociated datapath " "port %"PRIu32, flow->in_port.odp_port); } goto free_dupcall; } upcall->key = dupcall->key; upcall->key_len = dupcall->key_len; upcall->ufid = &dupcall->ufid; upcall->out_tun_key = dupcall->out_tun_key; upcall->actions = dupcall->actions; if (vsp_adjust_flow(upcall->ofproto, flow, &dupcall->packet)) { upcall->vsp_adjusted = true; } pkt_metadata_from_flow(&dupcall->packet.md, flow); flow_extract(&dupcall->packet, flow); error = process_upcall(udpif, upcall, &upcall->odp_actions, &upcall->wc); if (error) { goto cleanup; } n_upcalls++; continue; cleanup: upcall_uninit(upcall); free_dupcall: dp_packet_uninit(&dupcall->packet); ofpbuf_uninit(recv_buf); } if (n_upcalls) { handle_upcalls(handler->udpif, upcalls, n_upcalls); for (i = 0; i < n_upcalls; i++) { dp_packet_uninit(&dupcalls[i].packet); ofpbuf_uninit(&recv_bufs[i]); upcall_uninit(&upcalls[i]); } } return n_upcalls; } static void * udpif_revalidator(void *arg) { /* Used by all revalidators. */ struct revalidator *revalidator = arg; struct udpif *udpif = revalidator->udpif; bool leader = revalidator == &udpif->revalidators[0]; /* Used only by the leader. */ long long int start_time = 0; uint64_t last_reval_seq = 0; size_t n_flows = 0; revalidator->id = ovsthread_id_self(); for (;;) { if (leader) { uint64_t reval_seq; recirc_run(); /* Recirculation cleanup. */ reval_seq = seq_read(udpif->reval_seq); last_reval_seq = reval_seq; n_flows = udpif_get_n_flows(udpif); udpif->max_n_flows = MAX(n_flows, udpif->max_n_flows); udpif->avg_n_flows = (udpif->avg_n_flows + n_flows) / 2; /* Only the leader checks the pause latch to prevent a race where * some threads think it's false and proceed to block on * reval_barrier and others think it's true and block indefinitely * on the pause_barrier */ udpif->pause = latch_is_set(&udpif->pause_latch); /* Only the leader checks the exit latch to prevent a race where * some threads think it's true and exit and others think it's * false and block indefinitely on the reval_barrier */ udpif->reval_exit = latch_is_set(&udpif->exit_latch); start_time = time_msec(); if (!udpif->reval_exit) { bool terse_dump; terse_dump = udpif_use_ufid(udpif); udpif->dump = dpif_flow_dump_create(udpif->dpif, terse_dump); } } /* Wait for the leader to start the flow dump. */ ovs_barrier_block(&udpif->reval_barrier); if (udpif->pause) { revalidator_pause(revalidator); } if (udpif->reval_exit) { break; } revalidate(revalidator); /* Wait for all flows to have been dumped before we garbage collect. */ ovs_barrier_block(&udpif->reval_barrier); revalidator_sweep(revalidator); /* Wait for all revalidators to finish garbage collection. */ ovs_barrier_block(&udpif->reval_barrier); if (leader) { unsigned int flow_limit; long long int duration; atomic_read_relaxed(&udpif->flow_limit, &flow_limit); dpif_flow_dump_destroy(udpif->dump); seq_change(udpif->dump_seq); duration = MAX(time_msec() - start_time, 1); udpif->dump_duration = duration; if (duration > 2000) { flow_limit /= duration / 1000; } else if (duration > 1300) { flow_limit = flow_limit * 3 / 4; } else if (duration < 1000 && n_flows > 2000 && flow_limit < n_flows * 1000 / duration) { flow_limit += 1000; } flow_limit = MIN(ofproto_flow_limit, MAX(flow_limit, 1000)); atomic_store_relaxed(&udpif->flow_limit, flow_limit); if (duration > 2000) { VLOG_INFO("Spent an unreasonably long %lldms dumping flows", duration); } poll_timer_wait_until(start_time + MIN(ofproto_max_idle, 500)); seq_wait(udpif->reval_seq, last_reval_seq); latch_wait(&udpif->exit_latch); latch_wait(&udpif->pause_latch); poll_block(); } } return NULL; } static enum upcall_type classify_upcall(enum dpif_upcall_type type, const struct nlattr *userdata) { union user_action_cookie cookie; size_t userdata_len; /* First look at the upcall type. */ switch (type) { case DPIF_UC_ACTION: break; case DPIF_UC_MISS: return MISS_UPCALL; case DPIF_N_UC_TYPES: default: VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, type); return BAD_UPCALL; } /* "action" upcalls need a closer look. */ if (!userdata) { VLOG_WARN_RL(&rl, "action upcall missing cookie"); return BAD_UPCALL; } userdata_len = nl_attr_get_size(userdata); if (userdata_len < sizeof cookie.type || userdata_len > sizeof cookie) { VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %"PRIuSIZE, userdata_len); return BAD_UPCALL; } memset(&cookie, 0, sizeof cookie); memcpy(&cookie, nl_attr_get(userdata), userdata_len); if (userdata_len == MAX(8, sizeof cookie.sflow) && cookie.type == USER_ACTION_COOKIE_SFLOW) { return SFLOW_UPCALL; } else if (userdata_len == MAX(8, sizeof cookie.slow_path) && cookie.type == USER_ACTION_COOKIE_SLOW_PATH) { return MISS_UPCALL; } else if (userdata_len == MAX(8, sizeof cookie.flow_sample) && cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) { return FLOW_SAMPLE_UPCALL; } else if (userdata_len == MAX(8, sizeof cookie.ipfix) && cookie.type == USER_ACTION_COOKIE_IPFIX) { return IPFIX_UPCALL; } else { VLOG_WARN_RL(&rl, "invalid user cookie of type %"PRIu16 " and size %"PRIuSIZE, cookie.type, userdata_len); return BAD_UPCALL; } } /* Calculates slow path actions for 'xout'. 'buf' must statically be * initialized with at least 128 bytes of space. */ static void compose_slow_path(struct udpif *udpif, struct xlate_out *xout, const struct flow *flow, odp_port_t odp_in_port, struct ofpbuf *buf) { union user_action_cookie cookie; odp_port_t port; uint32_t pid; memset(&cookie, 0, sizeof cookie); cookie.type = USER_ACTION_COOKIE_SLOW_PATH; cookie.slow_path.unused = 0; cookie.slow_path.reason = xout->slow; port = xout->slow & (SLOW_CFM | SLOW_BFD | SLOW_LACP | SLOW_STP) ? ODPP_NONE : odp_in_port; pid = dpif_port_get_pid(udpif->dpif, port, flow_hash_5tuple(flow, 0)); odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, ODPP_NONE, false, buf); } /* If there is no error, the upcall must be destroyed with upcall_uninit() * before quiescing, as the referred objects are guaranteed to exist only * until the calling thread quiesces. Otherwise, do not call upcall_uninit() * since the 'upcall->put_actions' remains uninitialized. */ static int upcall_receive(struct upcall *upcall, const struct dpif_backer *backer, const struct dp_packet *packet, enum dpif_upcall_type type, const struct nlattr *userdata, const struct flow *flow, const unsigned int mru, const ovs_u128 *ufid, const unsigned pmd_id) { int error; error = xlate_lookup(backer, flow, &upcall->ofproto, &upcall->ipfix, &upcall->sflow, NULL, &upcall->in_port); if (error) { return error; } upcall->recirc = NULL; upcall->have_recirc_ref = false; upcall->flow = flow; upcall->packet = packet; upcall->ufid = ufid; upcall->pmd_id = pmd_id; upcall->type = type; upcall->userdata = userdata; ofpbuf_use_stub(&upcall->odp_actions, upcall->odp_actions_stub, sizeof upcall->odp_actions_stub); ofpbuf_init(&upcall->put_actions, 0); upcall->xout_initialized = false; upcall->vsp_adjusted = false; upcall->ukey_persists = false; upcall->ukey = NULL; upcall->key = NULL; upcall->key_len = 0; upcall->mru = mru; upcall->out_tun_key = NULL; upcall->actions = NULL; return 0; } static void upcall_xlate(struct udpif *udpif, struct upcall *upcall, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { struct dpif_flow_stats stats; struct xlate_in xin; stats.n_packets = 1; stats.n_bytes = dp_packet_size(upcall->packet); stats.used = time_msec(); stats.tcp_flags = ntohs(upcall->flow->tcp_flags); xlate_in_init(&xin, upcall->ofproto, upcall->flow, upcall->in_port, NULL, stats.tcp_flags, upcall->packet, wc, odp_actions); if (upcall->type == DPIF_UC_MISS) { xin.resubmit_stats = &stats; if (xin.recirc) { /* We may install a datapath flow only if we get a reference to the * recirculation context (otherwise we could have recirculation * upcalls using recirculation ID for which no context can be * found). We may still execute the flow's actions even if we * don't install the flow. */ upcall->recirc = xin.recirc; upcall->have_recirc_ref = recirc_id_node_try_ref_rcu(xin.recirc); } } else { /* For non-miss upcalls, we are either executing actions (one of which * is an userspace action) for an upcall, in which case the stats have * already been taken care of, or there's a flow in the datapath which * this packet was accounted to. Presumably the revalidators will deal * with pushing its stats eventually. */ } upcall->dump_seq = seq_read(udpif->dump_seq); upcall->reval_seq = seq_read(udpif->reval_seq); xlate_actions(&xin, &upcall->xout); if (wc) { /* Convert the input port wildcard from OFP to ODP format. There's no * real way to do this for arbitrary bitmasks since the numbering spaces * aren't the same. However, flow translation always exact matches the * whole thing, so we can do the same here. */ WC_MASK_FIELD(wc, in_port.odp_port); } upcall->xout_initialized = true; /* Special case for fail-open mode. * * If we are in fail-open mode, but we are connected to a controller too, * then we should send the packet up to the controller in the hope that it * will try to set up a flow and thereby allow us to exit fail-open. * * See the top-level comment in fail-open.c for more information. * * Copy packets before they are modified by execution. */ if (upcall->xout.fail_open) { const struct dp_packet *packet = upcall->packet; struct ofproto_packet_in *pin; pin = xmalloc(sizeof *pin); pin->up.packet = xmemdup(dp_packet_data(packet), dp_packet_size(packet)); pin->up.packet_len = dp_packet_size(packet); pin->up.reason = OFPR_NO_MATCH; pin->up.table_id = 0; pin->up.cookie = OVS_BE64_MAX; flow_get_metadata(upcall->flow, &pin->up.flow_metadata); pin->send_len = 0; /* Not used for flow table misses. */ pin->miss_type = OFPROTO_PACKET_IN_NO_MISS; ofproto_dpif_send_packet_in(upcall->ofproto, pin); } if (!upcall->xout.slow) { ofpbuf_use_const(&upcall->put_actions, odp_actions->data, odp_actions->size); } else { /* upcall->put_actions already initialized by upcall_receive(). */ compose_slow_path(udpif, &upcall->xout, upcall->flow, upcall->flow->in_port.odp_port, &upcall->put_actions); } /* This function is also called for slow-pathed flows. As we are only * going to create new datapath flows for actual datapath misses, there is * no point in creating a ukey otherwise. */ if (upcall->type == DPIF_UC_MISS) { upcall->ukey = ukey_create_from_upcall(upcall, wc); } } static void upcall_uninit(struct upcall *upcall) { if (upcall) { if (upcall->xout_initialized) { xlate_out_uninit(&upcall->xout); } ofpbuf_uninit(&upcall->odp_actions); ofpbuf_uninit(&upcall->put_actions); if (upcall->ukey) { if (!upcall->ukey_persists) { ukey_delete__(upcall->ukey); } } else if (upcall->have_recirc_ref) { /* The reference was transferred to the ukey if one was created. */ recirc_id_node_unref(upcall->recirc); } } } static int upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufid, unsigned pmd_id, enum dpif_upcall_type type, const struct nlattr *userdata, struct ofpbuf *actions, struct flow_wildcards *wc, struct ofpbuf *put_actions, void *aux) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); struct udpif *udpif = aux; unsigned int flow_limit; struct upcall upcall; bool megaflow; int error; atomic_read_relaxed(&enable_megaflows, &megaflow); atomic_read_relaxed(&udpif->flow_limit, &flow_limit); error = upcall_receive(&upcall, udpif->backer, packet, type, userdata, flow, 0, ufid, pmd_id); if (error) { return error; } error = process_upcall(udpif, &upcall, actions, wc); if (error) { goto out; } if (upcall.xout.slow && put_actions) { ofpbuf_put(put_actions, upcall.put_actions.data, upcall.put_actions.size); } if (OVS_UNLIKELY(!megaflow && wc)) { flow_wildcards_init_for_packet(wc, flow); } if (udpif_get_n_flows(udpif) >= flow_limit) { VLOG_WARN_RL(&rl, "upcall_cb failure: datapath flow limit reached"); error = ENOSPC; goto out; } /* Prevent miss flow installation if the key has recirculation ID but we * were not able to get a reference on it. */ if (type == DPIF_UC_MISS && upcall.recirc && !upcall.have_recirc_ref) { VLOG_WARN_RL(&rl, "upcall_cb failure: no reference for recirc flow"); error = ENOSPC; goto out; } if (upcall.ukey && !ukey_install(udpif, upcall.ukey)) { VLOG_WARN_RL(&rl, "upcall_cb failure: ukey installation fails"); error = ENOSPC; } out: if (!error) { upcall.ukey_persists = true; } upcall_uninit(&upcall); return error; } static int process_upcall(struct udpif *udpif, struct upcall *upcall, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { const struct nlattr *userdata = upcall->userdata; const struct dp_packet *packet = upcall->packet; const struct flow *flow = upcall->flow; switch (classify_upcall(upcall->type, userdata)) { case MISS_UPCALL: upcall_xlate(udpif, upcall, odp_actions, wc); return 0; case SFLOW_UPCALL: if (upcall->sflow) { union user_action_cookie cookie; const struct nlattr *actions; size_t actions_len = 0; struct dpif_sflow_actions sflow_actions; memset(&sflow_actions, 0, sizeof sflow_actions); memset(&cookie, 0, sizeof cookie); memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.sflow); if (upcall->actions) { /* Actions were passed up from datapath. */ actions = nl_attr_get(upcall->actions); actions_len = nl_attr_get_size(upcall->actions); if (actions && actions_len) { dpif_sflow_read_actions(flow, actions, actions_len, &sflow_actions); } } if (actions_len == 0) { /* Lookup actions in userspace cache. */ struct udpif_key *ukey = ukey_lookup(udpif, upcall->ufid); if (ukey) { ukey_get_actions(ukey, &actions, &actions_len); dpif_sflow_read_actions(flow, actions, actions_len, &sflow_actions); } } dpif_sflow_received(upcall->sflow, packet, flow, flow->in_port.odp_port, &cookie, actions_len > 0 ? &sflow_actions : NULL); } break; case IPFIX_UPCALL: if (upcall->ipfix) { union user_action_cookie cookie; struct flow_tnl output_tunnel_key; memset(&cookie, 0, sizeof cookie); memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.ipfix); if (upcall->out_tun_key) { odp_tun_key_from_attr(upcall->out_tun_key, false, &output_tunnel_key); } dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow, flow->in_port.odp_port, cookie.ipfix.output_odp_port, upcall->out_tun_key ? &output_tunnel_key : NULL); } break; case FLOW_SAMPLE_UPCALL: if (upcall->ipfix) { union user_action_cookie cookie; memset(&cookie, 0, sizeof cookie); memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.flow_sample); /* The flow reflects exactly the contents of the packet. * Sample the packet using it. */ dpif_ipfix_flow_sample(upcall->ipfix, packet, flow, cookie.flow_sample.collector_set_id, cookie.flow_sample.probability, cookie.flow_sample.obs_domain_id, cookie.flow_sample.obs_point_id); } break; case BAD_UPCALL: break; } return EAGAIN; } static void handle_upcalls(struct udpif *udpif, struct upcall *upcalls, size_t n_upcalls) { struct dpif_op *opsp[UPCALL_MAX_BATCH * 2]; struct ukey_op ops[UPCALL_MAX_BATCH * 2]; unsigned int flow_limit; size_t n_ops, n_opsp, i; bool may_put; atomic_read_relaxed(&udpif->flow_limit, &flow_limit); may_put = udpif_get_n_flows(udpif) < flow_limit; /* Handle the packets individually in order of arrival. * * - For SLOW_CFM, SLOW_LACP, SLOW_STP, and SLOW_BFD, translation is what * processes received packets for these protocols. * * - For SLOW_CONTROLLER, translation sends the packet to the OpenFlow * controller. * * The loop fills 'ops' with an array of operations to execute in the * datapath. */ n_ops = 0; for (i = 0; i < n_upcalls; i++) { struct upcall *upcall = &upcalls[i]; const struct dp_packet *packet = upcall->packet; struct ukey_op *op; if (upcall->vsp_adjusted) { /* This packet was received on a VLAN splinter port. We added a * VLAN to the packet to make the packet resemble the flow, but the * actions were composed assuming that the packet contained no * VLAN. So, we must remove the VLAN header from the packet before * trying to execute the actions. */ if (upcall->odp_actions.size) { eth_pop_vlan(CONST_CAST(struct dp_packet *, upcall->packet)); } /* Remove the flow vlan tags inserted by vlan splinter logic * to ensure megaflow masks generated match the data path flow. */ CONST_CAST(struct flow *, upcall->flow)->vlan_tci = 0; } /* Do not install a flow into the datapath if: * * - The datapath already has too many flows. * * - We received this packet via some flow installed in the kernel * already. * * - Upcall was a recirculation but we do not have a reference to * to the recirculation ID. */ if (may_put && upcall->type == DPIF_UC_MISS && (!upcall->recirc || upcall->have_recirc_ref)) { struct udpif_key *ukey = upcall->ukey; upcall->ukey_persists = true; op = &ops[n_ops++]; op->ukey = ukey; op->dop.type = DPIF_OP_FLOW_PUT; op->dop.u.flow_put.flags = DPIF_FP_CREATE; op->dop.u.flow_put.key = ukey->key; op->dop.u.flow_put.key_len = ukey->key_len; op->dop.u.flow_put.mask = ukey->mask; op->dop.u.flow_put.mask_len = ukey->mask_len; op->dop.u.flow_put.ufid = upcall->ufid; op->dop.u.flow_put.stats = NULL; ukey_get_actions(ukey, &op->dop.u.flow_put.actions, &op->dop.u.flow_put.actions_len); } if (upcall->odp_actions.size) { op = &ops[n_ops++]; op->ukey = NULL; op->dop.type = DPIF_OP_EXECUTE; op->dop.u.execute.packet = CONST_CAST(struct dp_packet *, packet); odp_key_to_pkt_metadata(upcall->key, upcall->key_len, &op->dop.u.execute.packet->md); op->dop.u.execute.actions = upcall->odp_actions.data; op->dop.u.execute.actions_len = upcall->odp_actions.size; op->dop.u.execute.needs_help = (upcall->xout.slow & SLOW_ACTION) != 0; op->dop.u.execute.probe = false; op->dop.u.execute.mtu = upcall->mru; } } /* Execute batch. * * We install ukeys before installing the flows, locking them for exclusive * access by this thread for the period of installation. This ensures that * other threads won't attempt to delete the flows as we are creating them. */ n_opsp = 0; for (i = 0; i < n_ops; i++) { struct udpif_key *ukey = ops[i].ukey; if (ukey) { /* If we can't install the ukey, don't install the flow. */ if (!ukey_install_start(udpif, ukey)) { ukey_delete__(ukey); ops[i].ukey = NULL; continue; } } opsp[n_opsp++] = &ops[i].dop; } dpif_operate(udpif->dpif, opsp, n_opsp); for (i = 0; i < n_ops; i++) { if (ops[i].ukey) { ukey_install_finish(ops[i].ukey, ops[i].dop.error); } } } static uint32_t get_ufid_hash(const ovs_u128 *ufid) { return ufid->u32[0]; } static struct udpif_key * ukey_lookup(struct udpif *udpif, const ovs_u128 *ufid) { struct udpif_key *ukey; int idx = get_ufid_hash(ufid) % N_UMAPS; struct cmap *cmap = &udpif->ukeys[idx].cmap; CMAP_FOR_EACH_WITH_HASH (ukey, cmap_node, get_ufid_hash(ufid), cmap) { if (ovs_u128_equals(&ukey->ufid, ufid)) { return ukey; } } return NULL; } /* Provides safe lockless access of RCU protected 'ukey->actions'. Callers may * alternatively access the field directly if they take 'ukey->mutex'. */ static void ukey_get_actions(struct udpif_key *ukey, const struct nlattr **actions, size_t *size) { const struct ofpbuf *buf = ovsrcu_get(struct ofpbuf *, &ukey->actions); *actions = buf->data; *size = buf->size; } static void ukey_set_actions(struct udpif_key *ukey, const struct ofpbuf *actions) { ovsrcu_postpone(ofpbuf_delete, ovsrcu_get_protected(struct ofpbuf *, &ukey->actions)); ovsrcu_set(&ukey->actions, ofpbuf_clone(actions)); } static struct udpif_key * ukey_create__(const struct nlattr *key, size_t key_len, const struct nlattr *mask, size_t mask_len, bool ufid_present, const ovs_u128 *ufid, const unsigned pmd_id, const struct ofpbuf *actions, uint64_t dump_seq, uint64_t reval_seq, long long int used, uint32_t key_recirc_id, struct xlate_out *xout) OVS_NO_THREAD_SAFETY_ANALYSIS { struct udpif_key *ukey = xmalloc(sizeof *ukey); memcpy(&ukey->keybuf, key, key_len); ukey->key = &ukey->keybuf.nla; ukey->key_len = key_len; memcpy(&ukey->maskbuf, mask, mask_len); ukey->mask = &ukey->maskbuf.nla; ukey->mask_len = mask_len; ukey->ufid_present = ufid_present; ukey->ufid = *ufid; ukey->pmd_id = pmd_id; ukey->hash = get_ufid_hash(&ukey->ufid); ovsrcu_init(&ukey->actions, NULL); ukey_set_actions(ukey, actions); ovs_mutex_init(&ukey->mutex); ukey->dump_seq = dump_seq; ukey->reval_seq = reval_seq; ukey->flow_exists = false; ukey->created = time_msec(); memset(&ukey->stats, 0, sizeof ukey->stats); ukey->stats.used = used; ukey->xcache = NULL; ukey->key_recirc_id = key_recirc_id; recirc_refs_init(&ukey->recircs); if (xout) { /* Take ownership of the action recirc id references. */ recirc_refs_swap(&ukey->recircs, &xout->recircs); } return ukey; } static struct udpif_key * ukey_create_from_upcall(struct upcall *upcall, struct flow_wildcards *wc) { struct odputil_keybuf keystub, maskstub; struct ofpbuf keybuf, maskbuf; bool megaflow; struct odp_flow_key_parms odp_parms = { .flow = upcall->flow, .mask = wc ? &wc->masks : NULL, }; odp_parms.support = ofproto_dpif_get_support(upcall->ofproto)->odp; if (upcall->key_len) { ofpbuf_use_const(&keybuf, upcall->key, upcall->key_len); } else { /* dpif-netdev doesn't provide a netlink-formatted flow key in the * upcall, so convert the upcall's flow here. */ ofpbuf_use_stack(&keybuf, &keystub, sizeof keystub); odp_parms.odp_in_port = upcall->flow->in_port.odp_port; odp_flow_key_from_flow(&odp_parms, &keybuf); } atomic_read_relaxed(&enable_megaflows, &megaflow); ofpbuf_use_stack(&maskbuf, &maskstub, sizeof maskstub); if (megaflow && wc) { odp_parms.odp_in_port = wc->masks.in_port.odp_port; odp_parms.key_buf = &keybuf; odp_flow_key_from_mask(&odp_parms, &maskbuf); } return ukey_create__(keybuf.data, keybuf.size, maskbuf.data, maskbuf.size, true, upcall->ufid, upcall->pmd_id, &upcall->put_actions, upcall->dump_seq, upcall->reval_seq, 0, upcall->have_recirc_ref ? upcall->recirc->id : 0, &upcall->xout); } static int ukey_create_from_dpif_flow(const struct udpif *udpif, const struct dpif_flow *flow, struct udpif_key **ukey) { struct dpif_flow full_flow; struct ofpbuf actions; uint64_t dump_seq, reval_seq; uint64_t stub[DPIF_FLOW_BUFSIZE / 8]; const struct nlattr *a; unsigned int left; if (!flow->key_len || !flow->actions_len) { struct ofpbuf buf; int err; /* If the key or actions were not provided by the datapath, fetch the * full flow. */ ofpbuf_use_stack(&buf, &stub, sizeof stub); err = dpif_flow_get(udpif->dpif, flow->key, flow->key_len, flow->ufid_present ? &flow->ufid : NULL, flow->pmd_id, &buf, &full_flow); if (err) { return err; } flow = &full_flow; } /* Check the flow actions for recirculation action. As recirculation * relies on OVS userspace internal state, we need to delete all old * datapath flows with either a non-zero recirc_id in the key, or any * recirculation actions upon OVS restart. */ NL_ATTR_FOR_EACH (a, left, flow->key, flow->key_len) { if (nl_attr_type(a) == OVS_KEY_ATTR_RECIRC_ID && nl_attr_get_u32(a) != 0) { return EINVAL; } } NL_ATTR_FOR_EACH (a, left, flow->actions, flow->actions_len) { if (nl_attr_type(a) == OVS_ACTION_ATTR_RECIRC) { return EINVAL; } } dump_seq = seq_read(udpif->dump_seq); reval_seq = seq_read(udpif->reval_seq) - 1; /* Ensure revalidation. */ ofpbuf_use_const(&actions, &flow->actions, flow->actions_len); *ukey = ukey_create__(flow->key, flow->key_len, flow->mask, flow->mask_len, flow->ufid_present, &flow->ufid, flow->pmd_id, &actions, dump_seq, reval_seq, flow->stats.used, 0, NULL); return 0; } /* Attempts to insert a ukey into the shared ukey maps. * * On success, returns true, installs the ukey and returns it in a locked * state. Otherwise, returns false. */ static bool ukey_install_start(struct udpif *udpif, struct udpif_key *new_ukey) OVS_TRY_LOCK(true, new_ukey->mutex) { struct umap *umap; struct udpif_key *old_ukey; uint32_t idx; bool locked = false; idx = new_ukey->hash % N_UMAPS; umap = &udpif->ukeys[idx]; ovs_mutex_lock(&umap->mutex); old_ukey = ukey_lookup(udpif, &new_ukey->ufid); if (old_ukey) { /* Uncommon case: A ukey is already installed with the same UFID. */ if (old_ukey->key_len == new_ukey->key_len && !memcmp(old_ukey->key, new_ukey->key, new_ukey->key_len)) { COVERAGE_INC(handler_duplicate_upcall); } else { struct ds ds = DS_EMPTY_INITIALIZER; odp_format_ufid(&old_ukey->ufid, &ds); ds_put_cstr(&ds, " "); odp_flow_key_format(old_ukey->key, old_ukey->key_len, &ds); ds_put_cstr(&ds, "\n"); odp_format_ufid(&new_ukey->ufid, &ds); ds_put_cstr(&ds, " "); odp_flow_key_format(new_ukey->key, new_ukey->key_len, &ds); VLOG_WARN_RL(&rl, "Conflicting ukey for flows:\n%s", ds_cstr(&ds)); ds_destroy(&ds); } } else { ovs_mutex_lock(&new_ukey->mutex); cmap_insert(&umap->cmap, &new_ukey->cmap_node, new_ukey->hash); locked = true; } ovs_mutex_unlock(&umap->mutex); return locked; } static void ukey_install_finish__(struct udpif_key *ukey) OVS_REQUIRES(ukey->mutex) { ukey->flow_exists = true; } static bool ukey_install_finish(struct udpif_key *ukey, int error) OVS_RELEASES(ukey->mutex) { if (!error) { ukey_install_finish__(ukey); } ovs_mutex_unlock(&ukey->mutex); return !error; } static bool ukey_install(struct udpif *udpif, struct udpif_key *ukey) { /* The usual way to keep 'ukey->flow_exists' in sync with the datapath is * to call ukey_install_start(), install the corresponding datapath flow, * then call ukey_install_finish(). The netdev interface using upcall_cb() * doesn't provide a function to separately finish the flow installation, * so we perform the operations together here. * * This is fine currently, as revalidator threads will only delete this * ukey during revalidator_sweep() and only if the dump_seq is mismatched. * It is unlikely for a revalidator thread to advance dump_seq and reach * the next GC phase between ukey creation and flow installation. */ return ukey_install_start(udpif, ukey) && ukey_install_finish(ukey, 0); } /* Searches for a ukey in 'udpif->ukeys' that matches 'flow' and attempts to * lock the ukey. If the ukey does not exist, create it. * * Returns 0 on success, setting *result to the matching ukey and returning it * in a locked state. Otherwise, returns an errno and clears *result. EBUSY * indicates that another thread is handling this flow. Other errors indicate * an unexpected condition creating a new ukey. * * *error is an output parameter provided to appease the threadsafety analyser, * and its value matches the return value. */ static int ukey_acquire(struct udpif *udpif, const struct dpif_flow *flow, struct udpif_key **result, int *error) OVS_TRY_LOCK(0, (*result)->mutex) { struct udpif_key *ukey; int retval; ukey = ukey_lookup(udpif, &flow->ufid); if (ukey) { retval = ovs_mutex_trylock(&ukey->mutex); } else { /* Usually we try to avoid installing flows from revalidator threads, * because locking on a umap may cause handler threads to block. * However there are certain cases, like when ovs-vswitchd is * restarted, where it is desirable to handle flows that exist in the * datapath gracefully (ie, don't just clear the datapath). */ bool install; retval = ukey_create_from_dpif_flow(udpif, flow, &ukey); if (retval) { goto done; } install = ukey_install_start(udpif, ukey); if (install) { ukey_install_finish__(ukey); retval = 0; } else { ukey_delete__(ukey); retval = EBUSY; } } done: *error = retval; if (retval) { *result = NULL; } else { *result = ukey; } return retval; } static void ukey_delete__(struct udpif_key *ukey) OVS_NO_THREAD_SAFETY_ANALYSIS { if (ukey) { if (ukey->key_recirc_id) { recirc_free_id(ukey->key_recirc_id); } recirc_refs_unref(&ukey->recircs); xlate_cache_delete(ukey->xcache); ofpbuf_delete(ovsrcu_get(struct ofpbuf *, &ukey->actions)); ovs_mutex_destroy(&ukey->mutex); free(ukey); } } static void ukey_delete(struct umap *umap, struct udpif_key *ukey) OVS_REQUIRES(umap->mutex) { cmap_remove(&umap->cmap, &ukey->cmap_node, ukey->hash); ovsrcu_postpone(ukey_delete__, ukey); } static bool should_revalidate(const struct udpif *udpif, uint64_t packets, long long int used) { long long int metric, now, duration; if (udpif->dump_duration < 200) { /* We are likely to handle full revalidation for the flows. */ return true; } /* Calculate the mean time between seeing these packets. If this * exceeds the threshold, then delete the flow rather than performing * costly revalidation for flows that aren't being hit frequently. * * This is targeted at situations where the dump_duration is high (~1s), * and revalidation is triggered by a call to udpif_revalidate(). In * these situations, revalidation of all flows causes fluctuations in the * flow_limit due to the interaction with the dump_duration and max_idle. * This tends to result in deletion of low-throughput flows anyway, so * skip the revalidation and just delete those flows. */ packets = MAX(packets, 1); now = MAX(used, time_msec()); duration = now - used; metric = duration / packets; if (metric < 200) { /* The flow is receiving more than ~5pps, so keep it. */ return true; } return false; } /* Verifies that the datapath actions of 'ukey' are still correct, and pushes * 'stats' for it. * * Returns a recommended action for 'ukey', options include: * UKEY_DELETE The ukey should be deleted. * UKEY_KEEP The ukey is fine as is. * UKEY_MODIFY The ukey's actions should be changed but is otherwise * fine. Callers should change the actions to those found * in the caller supplied 'odp_actions' buffer. The * recirculation references can be found in 'recircs' and * must be handled by the caller. * * If the result is UKEY_MODIFY, then references to all recirc_ids used by the * new flow will be held within 'recircs' (which may be none). * * The caller is responsible for both initializing 'recircs' prior this call, * and ensuring any references are eventually freed. */ static enum reval_result revalidate_ukey(struct udpif *udpif, struct udpif_key *ukey, const struct dpif_flow_stats *stats, struct ofpbuf *odp_actions, uint64_t reval_seq, struct recirc_refs *recircs) OVS_REQUIRES(ukey->mutex) { struct xlate_out xout, *xoutp; struct netflow *netflow; struct ofproto_dpif *ofproto; struct dpif_flow_stats push; struct flow flow; struct flow_wildcards dp_mask, wc; enum reval_result result; ofp_port_t ofp_in_port; struct xlate_in xin; long long int last_used; int error; bool need_revalidate; result = UKEY_DELETE; xoutp = NULL; netflow = NULL; ofpbuf_clear(odp_actions); need_revalidate = (ukey->reval_seq != reval_seq); last_used = ukey->stats.used; push.used = stats->used; push.tcp_flags = stats->tcp_flags; push.n_packets = (stats->n_packets > ukey->stats.n_packets ? stats->n_packets - ukey->stats.n_packets : 0); push.n_bytes = (stats->n_bytes > ukey->stats.n_bytes ? stats->n_bytes - ukey->stats.n_bytes : 0); if (need_revalidate && last_used && !should_revalidate(udpif, push.n_packets, last_used)) { goto exit; } /* We will push the stats, so update the ukey stats cache. */ ukey->stats = *stats; if (!push.n_packets && !need_revalidate) { result = UKEY_KEEP; goto exit; } if (ukey->xcache && !need_revalidate) { xlate_push_stats(ukey->xcache, &push); result = UKEY_KEEP; goto exit; } if (odp_flow_key_to_flow(ukey->key, ukey->key_len, &flow) == ODP_FIT_ERROR) { goto exit; } error = xlate_lookup(udpif->backer, &flow, &ofproto, NULL, NULL, &netflow, &ofp_in_port); if (error) { goto exit; } if (need_revalidate) { xlate_cache_clear(ukey->xcache); } if (!ukey->xcache) { ukey->xcache = xlate_cache_new(); } xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, push.tcp_flags, NULL, need_revalidate ? &wc : NULL, odp_actions); if (push.n_packets) { xin.resubmit_stats = &push; xin.may_learn = true; } xin.xcache = ukey->xcache; xlate_actions(&xin, &xout); xoutp = &xout; if (!need_revalidate) { result = UKEY_KEEP; goto exit; } if (xout.slow) { ofpbuf_clear(odp_actions); compose_slow_path(udpif, &xout, &flow, flow.in_port.odp_port, odp_actions); } if (odp_flow_key_to_mask(ukey->mask, ukey->mask_len, ukey->key, ukey->key_len, &dp_mask, &flow) == ODP_FIT_ERROR) { goto exit; } /* Do not modify if any bit is wildcarded by the installed datapath flow, * but not the newly revalidated wildcard mask (wc), i.e., if revalidation * tells that the datapath flow is now too generic and must be narrowed * down. Note that we do not know if the datapath has ignored any of the * wildcarded bits, so we may be overtly conservative here. */ if (flow_wildcards_has_extra(&dp_mask, &wc)) { goto exit; } if (!ofpbuf_equal(odp_actions, ovsrcu_get(struct ofpbuf *, &ukey->actions))) { /* The datapath mask was OK, but the actions seem to have changed. * Let's modify it in place. */ result = UKEY_MODIFY; /* Transfer recirc action ID references to the caller. */ recirc_refs_swap(recircs, &xoutp->recircs); goto exit; } result = UKEY_KEEP; exit: if (result != UKEY_DELETE) { ukey->reval_seq = reval_seq; } if (netflow && result == UKEY_DELETE) { netflow_flow_clear(netflow, &flow); } xlate_out_uninit(xoutp); return result; } static void delete_op_init__(struct udpif *udpif, struct ukey_op *op, const struct dpif_flow *flow) { op->ukey = NULL; op->dop.type = DPIF_OP_FLOW_DEL; op->dop.u.flow_del.key = flow->key; op->dop.u.flow_del.key_len = flow->key_len; op->dop.u.flow_del.ufid = flow->ufid_present ? &flow->ufid : NULL; op->dop.u.flow_del.pmd_id = flow->pmd_id; op->dop.u.flow_del.stats = &op->stats; op->dop.u.flow_del.terse = udpif_use_ufid(udpif); } static void delete_op_init(struct udpif *udpif, struct ukey_op *op, struct udpif_key *ukey) { op->ukey = ukey; op->dop.type = DPIF_OP_FLOW_DEL; op->dop.u.flow_del.key = ukey->key; op->dop.u.flow_del.key_len = ukey->key_len; op->dop.u.flow_del.ufid = ukey->ufid_present ? &ukey->ufid : NULL; op->dop.u.flow_del.pmd_id = ukey->pmd_id; op->dop.u.flow_del.stats = &op->stats; op->dop.u.flow_del.terse = udpif_use_ufid(udpif); } static void modify_op_init(struct ukey_op *op, struct udpif_key *ukey) { op->ukey = ukey; op->dop.type = DPIF_OP_FLOW_PUT; op->dop.u.flow_put.flags = DPIF_FP_MODIFY; op->dop.u.flow_put.key = ukey->key; op->dop.u.flow_put.key_len = ukey->key_len; op->dop.u.flow_put.mask = ukey->mask; op->dop.u.flow_put.mask_len = ukey->mask_len; op->dop.u.flow_put.ufid = ukey->ufid_present ? &ukey->ufid : NULL; op->dop.u.flow_put.pmd_id = ukey->pmd_id; op->dop.u.flow_put.stats = NULL; ukey_get_actions(ukey, &op->dop.u.flow_put.actions, &op->dop.u.flow_put.actions_len); } static void push_ukey_ops__(struct udpif *udpif, struct ukey_op *ops, size_t n_ops) { struct dpif_op *opsp[REVALIDATE_MAX_BATCH]; size_t i; ovs_assert(n_ops <= REVALIDATE_MAX_BATCH); for (i = 0; i < n_ops; i++) { opsp[i] = &ops[i].dop; } dpif_operate(udpif->dpif, opsp, n_ops); for (i = 0; i < n_ops; i++) { struct ukey_op *op = &ops[i]; struct dpif_flow_stats *push, *stats, push_buf; stats = op->dop.u.flow_del.stats; push = &push_buf; if (op->dop.type != DPIF_OP_FLOW_DEL) { /* Only deleted flows need their stats pushed. */ continue; } if (op->dop.error) { /* flow_del error, 'stats' is unusable. */ continue; } if (op->ukey) { ovs_mutex_lock(&op->ukey->mutex); push->used = MAX(stats->used, op->ukey->stats.used); push->tcp_flags = stats->tcp_flags | op->ukey->stats.tcp_flags; push->n_packets = stats->n_packets - op->ukey->stats.n_packets; push->n_bytes = stats->n_bytes - op->ukey->stats.n_bytes; ovs_mutex_unlock(&op->ukey->mutex); } else { push = stats; } if (push->n_packets || netflow_exists()) { const struct nlattr *key = op->dop.u.flow_del.key; size_t key_len = op->dop.u.flow_del.key_len; struct ofproto_dpif *ofproto; struct netflow *netflow; ofp_port_t ofp_in_port; struct flow flow; int error; if (op->ukey) { ovs_mutex_lock(&op->ukey->mutex); if (op->ukey->xcache) { xlate_push_stats(op->ukey->xcache, push); ovs_mutex_unlock(&op->ukey->mutex); continue; } ovs_mutex_unlock(&op->ukey->mutex); key = op->ukey->key; key_len = op->ukey->key_len; } if (odp_flow_key_to_flow(key, key_len, &flow) == ODP_FIT_ERROR) { continue; } error = xlate_lookup(udpif->backer, &flow, &ofproto, NULL, NULL, &netflow, &ofp_in_port); if (!error) { struct xlate_in xin; xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, push->tcp_flags, NULL, NULL, NULL); xin.resubmit_stats = push->n_packets ? push : NULL; xin.may_learn = push->n_packets > 0; xlate_actions_for_side_effects(&xin); if (netflow) { netflow_flow_clear(netflow, &flow); } } } } } static void push_ukey_ops(struct udpif *udpif, struct umap *umap, struct ukey_op *ops, size_t n_ops) { int i; push_ukey_ops__(udpif, ops, n_ops); ovs_mutex_lock(&umap->mutex); for (i = 0; i < n_ops; i++) { if (ops[i].dop.type == DPIF_OP_FLOW_DEL) { ukey_delete(umap, ops[i].ukey); } } ovs_mutex_unlock(&umap->mutex); } static void log_unexpected_flow(const struct dpif_flow *flow, int error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 60); struct ds ds = DS_EMPTY_INITIALIZER; ds_put_format(&ds, "Failed to acquire udpif_key corresponding to " "unexpected flow (%s): ", ovs_strerror(error)); odp_format_ufid(&flow->ufid, &ds); VLOG_WARN_RL(&rl, "%s", ds_cstr(&ds)); ds_destroy(&ds); } static void reval_op_init(struct ukey_op *op, enum reval_result result, struct udpif *udpif, struct udpif_key *ukey, struct recirc_refs *recircs, struct ofpbuf *odp_actions) { if (result == UKEY_DELETE) { delete_op_init(udpif, op, ukey); } else if (result == UKEY_MODIFY) { /* Store the new recircs. */ recirc_refs_swap(&ukey->recircs, recircs); /* Release old recircs. */ recirc_refs_unref(recircs); /* ukey->key_recirc_id remains, as the key is the same as before. */ ukey_set_actions(ukey, odp_actions); modify_op_init(op, ukey); } } static void revalidate(struct revalidator *revalidator) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub); struct udpif *udpif = revalidator->udpif; struct dpif_flow_dump_thread *dump_thread; uint64_t dump_seq, reval_seq; unsigned int flow_limit; dump_seq = seq_read(udpif->dump_seq); reval_seq = seq_read(udpif->reval_seq); atomic_read_relaxed(&udpif->flow_limit, &flow_limit); dump_thread = dpif_flow_dump_thread_create(udpif->dump); for (;;) { struct ukey_op ops[REVALIDATE_MAX_BATCH]; int n_ops = 0; struct dpif_flow flows[REVALIDATE_MAX_BATCH]; const struct dpif_flow *f; int n_dumped; long long int max_idle; long long int now; size_t n_dp_flows; bool kill_them_all; n_dumped = dpif_flow_dump_next(dump_thread, flows, ARRAY_SIZE(flows)); if (!n_dumped) { break; } now = time_msec(); /* In normal operation we want to keep flows around until they have * been idle for 'ofproto_max_idle' milliseconds. However: * * - If the number of datapath flows climbs above 'flow_limit', * drop that down to 100 ms to try to bring the flows down to * the limit. * * - If the number of datapath flows climbs above twice * 'flow_limit', delete all the datapath flows as an emergency * measure. (We reassess this condition for the next batch of * datapath flows, so we will recover before all the flows are * gone.) */ n_dp_flows = udpif_get_n_flows(udpif); kill_them_all = n_dp_flows > flow_limit * 2; max_idle = n_dp_flows > flow_limit ? 100 : ofproto_max_idle; for (f = flows; f < &flows[n_dumped]; f++) { long long int used = f->stats.used; struct recirc_refs recircs = RECIRC_REFS_EMPTY_INITIALIZER; enum reval_result result; struct udpif_key *ukey; bool already_dumped; int error; if (ukey_acquire(udpif, f, &ukey, &error)) { if (error == EBUSY) { /* Another thread is processing this flow, so don't bother * processing it.*/ COVERAGE_INC(upcall_ukey_contention); } else { log_unexpected_flow(f, error); if (error != ENOENT) { delete_op_init__(udpif, &ops[n_ops++], f); } } continue; } already_dumped = ukey->dump_seq == dump_seq; if (already_dumped) { /* The flow has already been handled during this flow dump * operation. Skip it. */ if (ukey->xcache) { COVERAGE_INC(dumped_duplicate_flow); } else { COVERAGE_INC(dumped_new_flow); } ovs_mutex_unlock(&ukey->mutex); continue; } if (!used) { used = ukey->created; } if (kill_them_all || (used && used < now - max_idle)) { result = UKEY_DELETE; } else { result = revalidate_ukey(udpif, ukey, &f->stats, &odp_actions, reval_seq, &recircs); } ukey->dump_seq = dump_seq; ukey->flow_exists = result != UKEY_DELETE; if (result != UKEY_KEEP) { /* Takes ownership of 'recircs'. */ reval_op_init(&ops[n_ops++], result, udpif, ukey, &recircs, &odp_actions); } ovs_mutex_unlock(&ukey->mutex); } if (n_ops) { push_ukey_ops__(udpif, ops, n_ops); } ovsrcu_quiesce(); } dpif_flow_dump_thread_destroy(dump_thread); ofpbuf_uninit(&odp_actions); } /* Pauses the 'revalidator', can only proceed after main thread * calls udpif_resume_revalidators(). */ static void revalidator_pause(struct revalidator *revalidator) { /* The first block is for sync'ing the pause with main thread. */ ovs_barrier_block(&revalidator->udpif->pause_barrier); /* The second block is for pausing until main thread resumes. */ ovs_barrier_block(&revalidator->udpif->pause_barrier); } static void revalidator_sweep__(struct revalidator *revalidator, bool purge) { struct udpif *udpif; uint64_t dump_seq, reval_seq; int slice; udpif = revalidator->udpif; dump_seq = seq_read(udpif->dump_seq); reval_seq = seq_read(udpif->reval_seq); slice = revalidator - udpif->revalidators; ovs_assert(slice < udpif->n_revalidators); for (int i = slice; i < N_UMAPS; i += udpif->n_revalidators) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub); struct ukey_op ops[REVALIDATE_MAX_BATCH]; struct udpif_key *ukey; struct umap *umap = &udpif->ukeys[i]; size_t n_ops = 0; CMAP_FOR_EACH(ukey, cmap_node, &umap->cmap) { bool flow_exists, seq_mismatch; struct recirc_refs recircs = RECIRC_REFS_EMPTY_INITIALIZER; enum reval_result result; /* Handler threads could be holding a ukey lock while it installs a * new flow, so don't hang around waiting for access to it. */ if (ovs_mutex_trylock(&ukey->mutex)) { continue; } flow_exists = ukey->flow_exists; seq_mismatch = (ukey->dump_seq != dump_seq && ukey->reval_seq != reval_seq); if (purge) { result = UKEY_DELETE; } else if (!seq_mismatch) { result = UKEY_KEEP; } else { struct dpif_flow_stats stats; COVERAGE_INC(revalidate_missed_dp_flow); memset(&stats, 0, sizeof stats); result = revalidate_ukey(udpif, ukey, &stats, &odp_actions, reval_seq, &recircs); } if (flow_exists && result != UKEY_KEEP) { /* Takes ownership of 'recircs'. */ reval_op_init(&ops[n_ops++], result, udpif, ukey, &recircs, &odp_actions); } ovs_mutex_unlock(&ukey->mutex); if (n_ops == REVALIDATE_MAX_BATCH) { push_ukey_ops(udpif, umap, ops, n_ops); n_ops = 0; } if (!flow_exists) { ovs_mutex_lock(&umap->mutex); ukey_delete(umap, ukey); ovs_mutex_unlock(&umap->mutex); } } if (n_ops) { push_ukey_ops(udpif, umap, ops, n_ops); } ofpbuf_uninit(&odp_actions); ovsrcu_quiesce(); } } static void revalidator_sweep(struct revalidator *revalidator) { revalidator_sweep__(revalidator, false); } static void revalidator_purge(struct revalidator *revalidator) { revalidator_sweep__(revalidator, true); } /* In reaction to dpif purge, purges all 'ukey's with same 'pmd_id'. */ static void dp_purge_cb(void *aux, unsigned pmd_id) { struct udpif *udpif = aux; size_t i; udpif_pause_revalidators(udpif); for (i = 0; i < N_UMAPS; i++) { struct ukey_op ops[REVALIDATE_MAX_BATCH]; struct udpif_key *ukey; struct umap *umap = &udpif->ukeys[i]; size_t n_ops = 0; CMAP_FOR_EACH(ukey, cmap_node, &umap->cmap) { if (ukey->pmd_id == pmd_id) { delete_op_init(udpif, &ops[n_ops++], ukey); if (n_ops == REVALIDATE_MAX_BATCH) { push_ukey_ops(udpif, umap, ops, n_ops); n_ops = 0; } } } if (n_ops) { push_ukey_ops(udpif, umap, ops, n_ops); } ovsrcu_quiesce(); } udpif_resume_revalidators(udpif); } static void upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct udpif *udpif; LIST_FOR_EACH (udpif, list_node, &all_udpifs) { unsigned int flow_limit; bool ufid_enabled; size_t i; atomic_read_relaxed(&udpif->flow_limit, &flow_limit); ufid_enabled = udpif_use_ufid(udpif); ds_put_format(&ds, "%s:\n", dpif_name(udpif->dpif)); ds_put_format(&ds, "\tflows : (current %lu)" " (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif), udpif->avg_n_flows, udpif->max_n_flows, flow_limit); ds_put_format(&ds, "\tdump duration : %lldms\n", udpif->dump_duration); ds_put_format(&ds, "\tufid enabled : "); if (ufid_enabled) { ds_put_format(&ds, "true\n"); } else { ds_put_format(&ds, "false\n"); } ds_put_char(&ds, '\n'); for (i = 0; i < n_revalidators; i++) { struct revalidator *revalidator = &udpif->revalidators[i]; int j, elements = 0; for (j = i; j < N_UMAPS; j += n_revalidators) { elements += cmap_count(&udpif->ukeys[j].cmap); } ds_put_format(&ds, "\t%u: (keys %d)\n", revalidator->id, elements); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } /* Disable using the megaflows. * * This command is only needed for advanced debugging, so it's not * documented in the man page. */ static void upcall_unixctl_disable_megaflows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { atomic_store_relaxed(&enable_megaflows, false); udpif_flush_all_datapaths(); unixctl_command_reply(conn, "megaflows disabled"); } /* Re-enable using megaflows. * * This command is only needed for advanced debugging, so it's not * documented in the man page. */ static void upcall_unixctl_enable_megaflows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { atomic_store_relaxed(&enable_megaflows, true); udpif_flush_all_datapaths(); unixctl_command_reply(conn, "megaflows enabled"); } /* Disable skipping flow attributes during flow dump. * * This command is only needed for advanced debugging, so it's not * documented in the man page. */ static void upcall_unixctl_disable_ufid(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { atomic_store_relaxed(&enable_ufid, false); unixctl_command_reply(conn, "Datapath dumping tersely using UFID disabled"); } /* Re-enable skipping flow attributes during flow dump. * * This command is only needed for advanced debugging, so it's not documented * in the man page. */ static void upcall_unixctl_enable_ufid(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { atomic_store_relaxed(&enable_ufid, true); unixctl_command_reply(conn, "Datapath dumping tersely using UFID enabled " "for supported datapaths"); } /* Set the flow limit. * * This command is only needed for advanced debugging, so it's not * documented in the man page. */ static void upcall_unixctl_set_flow_limit(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct udpif *udpif; unsigned int flow_limit = atoi(argv[1]); LIST_FOR_EACH (udpif, list_node, &all_udpifs) { atomic_store_relaxed(&udpif->flow_limit, flow_limit); } ds_put_format(&ds, "set flow_limit to %u\n", flow_limit); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void upcall_unixctl_dump_wait(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { if (list_is_singleton(&all_udpifs)) { struct udpif *udpif = NULL; size_t len; udpif = OBJECT_CONTAINING(list_front(&all_udpifs), udpif, list_node); len = (udpif->n_conns + 1) * sizeof *udpif->conns; udpif->conn_seq = seq_read(udpif->dump_seq); udpif->conns = xrealloc(udpif->conns, len); udpif->conns[udpif->n_conns++] = conn; } else { unixctl_command_reply_error(conn, "can't wait on multiple udpifs."); } } static void upcall_unixctl_purge(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct udpif *udpif; LIST_FOR_EACH (udpif, list_node, &all_udpifs) { int n; for (n = 0; n < udpif->n_revalidators; n++) { revalidator_purge(&udpif->revalidators[n]); } } unixctl_command_reply(conn, ""); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/libofproto.pc.in0000644000000000000000000000013113534540071020443 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801424.645852423 openvswitch-2.5.9/ofproto/libofproto.pc.in0000644000175000017500000000035613534540071022136 0ustar00jpettitjpettit00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libofproto Description: OpenFlow library of Open vSwitch Version: @VERSION@ Libs: -L${libdir} -lofproto Libs.private: @LIBS@ Cflags: -I${includedir} openvswitch-2.5.9/ofproto/PaxHeaders.82075/connmgr.c0000644000000000000000000000013113534540071017142 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.017855166 openvswitch-2.5.9/ofproto/connmgr.c0000644000175000017500000023224213534540071020636 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "connmgr.h" #include #include #include "coverage.h" #include "dynamic-string.h" #include "fail-open.h" #include "in-band.h" #include "odp-util.h" #include "ofp-actions.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto-provider.h" #include "pinsched.h" #include "poll-loop.h" #include "pktbuf.h" #include "rconn.h" #include "shash.h" #include "simap.h" #include "stream.h" #include "timeval.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "bundles.h" VLOG_DEFINE_THIS_MODULE(connmgr); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); /* An OpenFlow connection. * * * Thread-safety * ============= * * 'ofproto_mutex' must be held whenever an ofconn is created or destroyed or, * more or less equivalently, whenever an ofconn is added to or removed from a * connmgr. 'ofproto_mutex' doesn't protect the data inside the ofconn, except * as specifically noted below. */ struct ofconn { /* Configuration that persists from one connection to the next. */ struct ovs_list node; /* In struct connmgr's "all_conns" list. */ struct hmap_node hmap_node; /* In struct connmgr's "controllers" map. */ struct connmgr *connmgr; /* Connection's manager. */ struct rconn *rconn; /* OpenFlow connection. */ enum ofconn_type type; /* Type. */ enum ofproto_band band; /* In-band or out-of-band? */ bool enable_async_msgs; /* Initially enable async messages? */ /* State that should be cleared from one connection to the next. */ /* OpenFlow state. */ enum ofp12_controller_role role; /* Role. */ enum ofputil_protocol protocol; /* Current protocol variant. */ enum nx_packet_in_format packet_in_format; /* OFPT_PACKET_IN format. */ /* OFPT_PACKET_IN related data. */ struct rconn_packet_counter *packet_in_counter; /* # queued on 'rconn'. */ #define N_SCHEDULERS 2 struct pinsched *schedulers[N_SCHEDULERS]; struct pktbuf *pktbuf; /* OpenFlow packet buffers. */ int miss_send_len; /* Bytes to send of buffered packets. */ uint16_t controller_id; /* Connection controller ID. */ /* Number of OpenFlow messages queued on 'rconn' as replies to OpenFlow * requests, and the maximum number before we stop reading OpenFlow * requests. */ #define OFCONN_REPLY_MAX 100 struct rconn_packet_counter *reply_counter; /* Asynchronous message configuration in each possible roles. * * A 1-bit enables sending an asynchronous message for one possible reason * that the message might be generated, a 0-bit disables it. */ uint32_t master_async_config[OAM_N_TYPES]; /* master, other */ uint32_t slave_async_config[OAM_N_TYPES]; /* slave */ /* Flow table operation logging. */ int n_add, n_delete, n_modify; /* Number of unreported ops of each kind. */ long long int first_op, last_op; /* Range of times for unreported ops. */ long long int next_op_report; /* Time to report ops, or LLONG_MAX. */ long long int op_backoff; /* Earliest time to report ops again. */ /* Flow monitors (e.g. NXST_FLOW_MONITOR). */ /* Configuration. Contains "struct ofmonitor"s. */ struct hmap monitors OVS_GUARDED_BY(ofproto_mutex); /* Flow control. * * When too many flow monitor notifications back up in the transmit buffer, * we pause the transmission of further notifications. These members track * the flow control state. * * When notifications are flowing, 'monitor_paused' is 0. When * notifications are paused, 'monitor_paused' is the value of * 'monitor_seqno' at the point we paused. * * 'monitor_counter' counts the OpenFlow messages and bytes currently in * flight. This value growing too large triggers pausing. */ uint64_t monitor_paused OVS_GUARDED_BY(ofproto_mutex); struct rconn_packet_counter *monitor_counter OVS_GUARDED_BY(ofproto_mutex); /* State of monitors for a single ongoing flow_mod. * * 'updates' is a list of "struct ofpbuf"s that contain * NXST_FLOW_MONITOR_REPLY messages representing the changes made by the * current flow_mod. * * When 'updates' is nonempty, 'sent_abbrev_update' is true if 'updates' * contains an update event of type NXFME_ABBREV and false otherwise.. */ struct ovs_list updates OVS_GUARDED_BY(ofproto_mutex); bool sent_abbrev_update OVS_GUARDED_BY(ofproto_mutex); /* Active bundles. Contains "struct ofp_bundle"s. */ struct hmap bundles; }; static struct ofconn *ofconn_create(struct connmgr *, struct rconn *, enum ofconn_type, bool enable_async_msgs) OVS_REQUIRES(ofproto_mutex); static void ofconn_destroy(struct ofconn *) OVS_REQUIRES(ofproto_mutex); static void ofconn_flush(struct ofconn *) OVS_REQUIRES(ofproto_mutex); static void ofconn_reconfigure(struct ofconn *, const struct ofproto_controller *); static void ofconn_run(struct ofconn *, void (*handle_openflow)(struct ofconn *, const struct ofpbuf *ofp_msg)); static void ofconn_wait(struct ofconn *); static void ofconn_log_flow_mods(struct ofconn *); static const char *ofconn_get_target(const struct ofconn *); static char *ofconn_make_name(const struct connmgr *, const char *target); static void ofconn_set_rate_limit(struct ofconn *, int rate, int burst); static void ofconn_send(const struct ofconn *, struct ofpbuf *, struct rconn_packet_counter *); static void do_send_packet_ins(struct ofconn *, struct ovs_list *txq); /* A listener for incoming OpenFlow "service" connections. */ struct ofservice { struct hmap_node node; /* In struct connmgr's "services" hmap. */ struct pvconn *pvconn; /* OpenFlow connection listener. */ /* These are not used by ofservice directly. They are settings for * accepted "struct ofconn"s from the pvconn. */ int probe_interval; /* Max idle time before probing, in seconds. */ int rate_limit; /* Max packet-in rate in packets per second. */ int burst_limit; /* Limit on accumulating packet credits. */ bool enable_async_msgs; /* Initially enable async messages? */ uint8_t dscp; /* DSCP Value for controller connection */ uint32_t allowed_versions; /* OpenFlow protocol versions that may * be negotiated for a session. */ }; static void ofservice_reconfigure(struct ofservice *, const struct ofproto_controller *); static int ofservice_create(struct connmgr *mgr, const char *target, uint32_t allowed_versions, uint8_t dscp); static void ofservice_destroy(struct connmgr *, struct ofservice *); static struct ofservice *ofservice_lookup(struct connmgr *, const char *target); /* Connection manager for an OpenFlow switch. */ struct connmgr { struct ofproto *ofproto; char *name; char *local_port_name; /* OpenFlow connections. */ struct hmap controllers; /* All OFCONN_PRIMARY controllers. */ struct ovs_list all_conns; /* All controllers. */ uint64_t master_election_id; /* monotonically increasing sequence number * for master election */ bool master_election_id_defined; /* OpenFlow listeners. */ struct hmap services; /* Contains "struct ofservice"s. */ struct pvconn **snoops; size_t n_snoops; /* Fail open. */ struct fail_open *fail_open; enum ofproto_fail_mode fail_mode; /* In-band control. */ struct in_band *in_band; struct sockaddr_in *extra_in_band_remotes; size_t n_extra_remotes; int in_band_queue; }; static void update_in_band_remotes(struct connmgr *); static void add_snooper(struct connmgr *, struct vconn *); static void ofmonitor_run(struct connmgr *); static void ofmonitor_wait(struct connmgr *); /* Creates and returns a new connection manager owned by 'ofproto'. 'name' is * a name for the ofproto suitable for using in log messages. * 'local_port_name' is the name of the local port (OFPP_LOCAL) within * 'ofproto'. */ struct connmgr * connmgr_create(struct ofproto *ofproto, const char *name, const char *local_port_name) { struct connmgr *mgr; mgr = xmalloc(sizeof *mgr); mgr->ofproto = ofproto; mgr->name = xstrdup(name); mgr->local_port_name = xstrdup(local_port_name); hmap_init(&mgr->controllers); list_init(&mgr->all_conns); mgr->master_election_id = 0; mgr->master_election_id_defined = false; hmap_init(&mgr->services); mgr->snoops = NULL; mgr->n_snoops = 0; mgr->fail_open = NULL; mgr->fail_mode = OFPROTO_FAIL_SECURE; mgr->in_band = NULL; mgr->extra_in_band_remotes = NULL; mgr->n_extra_remotes = 0; mgr->in_band_queue = -1; return mgr; } /* Frees 'mgr' and all of its resources. */ void connmgr_destroy(struct connmgr *mgr) { struct ofservice *ofservice, *next_ofservice; struct ofconn *ofconn, *next_ofconn; size_t i; if (!mgr) { return; } ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) { ofconn_destroy(ofconn); } ovs_mutex_unlock(&ofproto_mutex); hmap_destroy(&mgr->controllers); HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &mgr->services) { ofservice_destroy(mgr, ofservice); } hmap_destroy(&mgr->services); for (i = 0; i < mgr->n_snoops; i++) { pvconn_close(mgr->snoops[i]); } free(mgr->snoops); fail_open_destroy(mgr->fail_open); mgr->fail_open = NULL; in_band_destroy(mgr->in_band); mgr->in_band = NULL; free(mgr->extra_in_band_remotes); free(mgr->name); free(mgr->local_port_name); free(mgr); } /* Does all of the periodic maintenance required by 'mgr'. Calls * 'handle_openflow' for each message received on an OpenFlow connection, * passing along the OpenFlow connection itself and the message that was sent. * 'handle_openflow' must not modify or free the message. */ void connmgr_run(struct connmgr *mgr, void (*handle_openflow)(struct ofconn *, const struct ofpbuf *ofp_msg)) OVS_EXCLUDED(ofproto_mutex) { struct ofconn *ofconn, *next_ofconn; struct ofservice *ofservice; size_t i; if (mgr->in_band) { if (!in_band_run(mgr->in_band)) { in_band_destroy(mgr->in_band); mgr->in_band = NULL; } } LIST_FOR_EACH_SAFE (ofconn, next_ofconn, node, &mgr->all_conns) { ofconn_run(ofconn, handle_openflow); } ofmonitor_run(mgr); /* Fail-open maintenance. Do this after processing the ofconns since * fail-open checks the status of the controller rconn. */ if (mgr->fail_open) { fail_open_run(mgr->fail_open); } HMAP_FOR_EACH (ofservice, node, &mgr->services) { struct vconn *vconn; int retval; retval = pvconn_accept(ofservice->pvconn, &vconn); if (!retval) { struct rconn *rconn; char *name; /* Passing default value for creation of the rconn */ rconn = rconn_create(ofservice->probe_interval, 0, ofservice->dscp, vconn_get_allowed_versions(vconn)); name = ofconn_make_name(mgr, vconn_get_name(vconn)); rconn_connect_unreliably(rconn, vconn, name); free(name); ovs_mutex_lock(&ofproto_mutex); ofconn = ofconn_create(mgr, rconn, OFCONN_SERVICE, ofservice->enable_async_msgs); ovs_mutex_unlock(&ofproto_mutex); ofconn_set_rate_limit(ofconn, ofservice->rate_limit, ofservice->burst_limit); } else if (retval != EAGAIN) { VLOG_WARN_RL(&rl, "accept failed (%s)", ovs_strerror(retval)); } } for (i = 0; i < mgr->n_snoops; i++) { struct vconn *vconn; int retval; retval = pvconn_accept(mgr->snoops[i], &vconn); if (!retval) { add_snooper(mgr, vconn); } else if (retval != EAGAIN) { VLOG_WARN_RL(&rl, "accept failed (%s)", ovs_strerror(retval)); } } } /* Causes the poll loop to wake up when connmgr_run() needs to run. */ void connmgr_wait(struct connmgr *mgr) { struct ofservice *ofservice; struct ofconn *ofconn; size_t i; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { ofconn_wait(ofconn); } ofmonitor_wait(mgr); if (mgr->in_band) { in_band_wait(mgr->in_band); } if (mgr->fail_open) { fail_open_wait(mgr->fail_open); } HMAP_FOR_EACH (ofservice, node, &mgr->services) { pvconn_wait(ofservice->pvconn); } for (i = 0; i < mgr->n_snoops; i++) { pvconn_wait(mgr->snoops[i]); } } /* Adds some memory usage statistics for 'mgr' into 'usage', for use with * memory_report(). */ void connmgr_get_memory_usage(const struct connmgr *mgr, struct simap *usage) { const struct ofconn *ofconn; unsigned int packets = 0; unsigned int ofconns = 0; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { int i; ofconns++; packets += rconn_count_txqlen(ofconn->rconn); for (i = 0; i < N_SCHEDULERS; i++) { struct pinsched_stats stats; pinsched_get_stats(ofconn->schedulers[i], &stats); packets += stats.n_queued; } packets += pktbuf_count_packets(ofconn->pktbuf); } simap_increase(usage, "ofconns", ofconns); simap_increase(usage, "packets", packets); } /* Returns the ofproto that owns 'ofconn''s connmgr. */ struct ofproto * ofconn_get_ofproto(const struct ofconn *ofconn) { return ofconn->connmgr->ofproto; } /* OpenFlow configuration. */ static void add_controller(struct connmgr *, const char *target, uint8_t dscp, uint32_t allowed_versions) OVS_REQUIRES(ofproto_mutex); static struct ofconn *find_controller_by_target(struct connmgr *, const char *target); static void update_fail_open(struct connmgr *) OVS_EXCLUDED(ofproto_mutex); static int set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp, const struct sset *); /* Returns true if 'mgr' has any configured primary controllers. * * Service controllers do not count, but configured primary controllers do * count whether or not they are currently connected. */ bool connmgr_has_controllers(const struct connmgr *mgr) { return !hmap_is_empty(&mgr->controllers); } /* Initializes 'info' and populates it with information about each configured * primary controller. The keys in 'info' are the controllers' targets; the * data values are corresponding "struct ofproto_controller_info". * * The caller owns 'info' and everything in it and should free it when it is no * longer needed. */ void connmgr_get_controller_info(struct connmgr *mgr, struct shash *info) { const struct ofconn *ofconn; HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { const struct rconn *rconn = ofconn->rconn; const char *target = rconn_get_target(rconn); if (!shash_find(info, target)) { struct ofproto_controller_info *cinfo = xmalloc(sizeof *cinfo); time_t now = time_now(); time_t last_connection = rconn_get_last_connection(rconn); time_t last_disconnect = rconn_get_last_disconnect(rconn); int last_error = rconn_get_last_error(rconn); int i; shash_add(info, target, cinfo); cinfo->is_connected = rconn_is_connected(rconn); cinfo->role = ofconn->role; smap_init(&cinfo->pairs); if (last_error) { smap_add(&cinfo->pairs, "last_error", ovs_retval_to_string(last_error)); } smap_add(&cinfo->pairs, "state", rconn_get_state(rconn)); if (last_connection != TIME_MIN) { smap_add_format(&cinfo->pairs, "sec_since_connect", "%ld", (long int) (now - last_connection)); } if (last_disconnect != TIME_MIN) { smap_add_format(&cinfo->pairs, "sec_since_disconnect", "%ld", (long int) (now - last_disconnect)); } for (i = 0; i < N_SCHEDULERS; i++) { if (ofconn->schedulers[i]) { const char *name = i == 0 ? "miss" : "action"; struct pinsched_stats stats; pinsched_get_stats(ofconn->schedulers[i], &stats); smap_add_nocopy(&cinfo->pairs, xasprintf("packet-in-%s-backlog", name), xasprintf("%u", stats.n_queued)); smap_add_nocopy(&cinfo->pairs, xasprintf("packet-in-%s-bypassed", name), xasprintf("%llu", stats.n_normal)); smap_add_nocopy(&cinfo->pairs, xasprintf("packet-in-%s-queued", name), xasprintf("%llu", stats.n_limited)); smap_add_nocopy(&cinfo->pairs, xasprintf("packet-in-%s-dropped", name), xasprintf("%llu", stats.n_queue_dropped)); } } } } } void connmgr_free_controller_info(struct shash *info) { struct shash_node *node; SHASH_FOR_EACH (node, info) { struct ofproto_controller_info *cinfo = node->data; smap_destroy(&cinfo->pairs); free(cinfo); } shash_destroy(info); } /* Changes 'mgr''s set of controllers to the 'n_controllers' controllers in * 'controllers'. */ void connmgr_set_controllers(struct connmgr *mgr, const struct ofproto_controller *controllers, size_t n_controllers, uint32_t allowed_versions) OVS_EXCLUDED(ofproto_mutex) { bool had_controllers = connmgr_has_controllers(mgr); struct shash new_controllers; struct ofconn *ofconn, *next_ofconn; struct ofservice *ofservice, *next_ofservice; size_t i; /* Required to add and remove ofconns. This could probably be narrowed to * cover a smaller amount of code, if that yielded some benefit. */ ovs_mutex_lock(&ofproto_mutex); /* Create newly configured controllers and services. * Create a name to ofproto_controller mapping in 'new_controllers'. */ shash_init(&new_controllers); for (i = 0; i < n_controllers; i++) { const struct ofproto_controller *c = &controllers[i]; if (!vconn_verify_name(c->target)) { bool add = false; ofconn = find_controller_by_target(mgr, c->target); if (!ofconn) { VLOG_INFO("%s: added primary controller \"%s\"", mgr->name, c->target); add = true; } else if (rconn_get_allowed_versions(ofconn->rconn) != allowed_versions) { VLOG_INFO("%s: re-added primary controller \"%s\"", mgr->name, c->target); add = true; ofconn_destroy(ofconn); } if (add) { add_controller(mgr, c->target, c->dscp, allowed_versions); } } else if (!pvconn_verify_name(c->target)) { bool add = false; ofservice = ofservice_lookup(mgr, c->target); if (!ofservice) { VLOG_INFO("%s: added service controller \"%s\"", mgr->name, c->target); add = true; } else if (ofservice->allowed_versions != allowed_versions) { VLOG_INFO("%s: re-added service controller \"%s\"", mgr->name, c->target); ofservice_destroy(mgr, ofservice); add = true; } if (add) { ofservice_create(mgr, c->target, allowed_versions, c->dscp); } } else { VLOG_WARN_RL(&rl, "%s: unsupported controller \"%s\"", mgr->name, c->target); continue; } shash_add_once(&new_controllers, c->target, &controllers[i]); } /* Delete controllers that are no longer configured. * Update configuration of all now-existing controllers. */ HMAP_FOR_EACH_SAFE (ofconn, next_ofconn, hmap_node, &mgr->controllers) { const char *target = ofconn_get_target(ofconn); struct ofproto_controller *c; c = shash_find_data(&new_controllers, target); if (!c) { VLOG_INFO("%s: removed primary controller \"%s\"", mgr->name, target); ofconn_destroy(ofconn); } else { ofconn_reconfigure(ofconn, c); } } /* Delete services that are no longer configured. * Update configuration of all now-existing services. */ HMAP_FOR_EACH_SAFE (ofservice, next_ofservice, node, &mgr->services) { const char *target = pvconn_get_name(ofservice->pvconn); struct ofproto_controller *c; c = shash_find_data(&new_controllers, target); if (!c) { VLOG_INFO("%s: removed service controller \"%s\"", mgr->name, target); ofservice_destroy(mgr, ofservice); } else { ofservice_reconfigure(ofservice, c); } } shash_destroy(&new_controllers); ovs_mutex_unlock(&ofproto_mutex); update_in_band_remotes(mgr); update_fail_open(mgr); if (had_controllers != connmgr_has_controllers(mgr)) { ofproto_flush_flows(mgr->ofproto); } } /* Drops the connections between 'mgr' and all of its primary and secondary * controllers, forcing them to reconnect. */ void connmgr_reconnect(const struct connmgr *mgr) { struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { rconn_reconnect(ofconn->rconn); } } /* Sets the "snoops" for 'mgr' to the pvconn targets listed in 'snoops'. * * A "snoop" is a pvconn to which every OpenFlow message to or from the most * important controller on 'mgr' is mirrored. */ int connmgr_set_snoops(struct connmgr *mgr, const struct sset *snoops) { return set_pvconns(&mgr->snoops, &mgr->n_snoops, snoops); } /* Adds each of the snoops currently configured on 'mgr' to 'snoops'. */ void connmgr_get_snoops(const struct connmgr *mgr, struct sset *snoops) { size_t i; for (i = 0; i < mgr->n_snoops; i++) { sset_add(snoops, pvconn_get_name(mgr->snoops[i])); } } /* Returns true if 'mgr' has at least one snoop, false if it has none. */ bool connmgr_has_snoops(const struct connmgr *mgr) { return mgr->n_snoops > 0; } /* Creates a new controller for 'target' in 'mgr'. update_controller() needs * to be called later to finish the new ofconn's configuration. */ static void add_controller(struct connmgr *mgr, const char *target, uint8_t dscp, uint32_t allowed_versions) OVS_REQUIRES(ofproto_mutex) { char *name = ofconn_make_name(mgr, target); struct ofconn *ofconn; ofconn = ofconn_create(mgr, rconn_create(5, 8, dscp, allowed_versions), OFCONN_PRIMARY, true); ofconn->pktbuf = pktbuf_create(); rconn_connect(ofconn->rconn, target, name); hmap_insert(&mgr->controllers, &ofconn->hmap_node, hash_string(target, 0)); free(name); } static struct ofconn * find_controller_by_target(struct connmgr *mgr, const char *target) { struct ofconn *ofconn; HMAP_FOR_EACH_WITH_HASH (ofconn, hmap_node, hash_string(target, 0), &mgr->controllers) { if (!strcmp(ofconn_get_target(ofconn), target)) { return ofconn; } } return NULL; } static void update_in_band_remotes(struct connmgr *mgr) { struct sockaddr_in *addrs; size_t max_addrs, n_addrs; struct ofconn *ofconn; size_t i; /* Allocate enough memory for as many remotes as we could possibly have. */ max_addrs = mgr->n_extra_remotes + hmap_count(&mgr->controllers); addrs = xmalloc(max_addrs * sizeof *addrs); n_addrs = 0; /* Add all the remotes. */ HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { const char *target = rconn_get_target(ofconn->rconn); union { struct sockaddr_storage ss; struct sockaddr_in in; } sa; if (ofconn->band == OFPROTO_IN_BAND && stream_parse_target_with_default_port(target, OFP_PORT, &sa.ss) && sa.ss.ss_family == AF_INET) { addrs[n_addrs++] = sa.in; } } for (i = 0; i < mgr->n_extra_remotes; i++) { addrs[n_addrs++] = mgr->extra_in_band_remotes[i]; } /* Create or update or destroy in-band. */ if (n_addrs) { if (!mgr->in_band) { in_band_create(mgr->ofproto, mgr->local_port_name, &mgr->in_band); } } else { /* in_band_run() needs a chance to delete any existing in-band flows. * We will destroy mgr->in_band after it's done with that. */ } if (mgr->in_band) { in_band_set_queue(mgr->in_band, mgr->in_band_queue); in_band_set_remotes(mgr->in_band, addrs, n_addrs); } /* Clean up. */ free(addrs); } static void update_fail_open(struct connmgr *mgr) OVS_EXCLUDED(ofproto_mutex) { if (connmgr_has_controllers(mgr) && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) { if (!mgr->fail_open) { mgr->fail_open = fail_open_create(mgr->ofproto, mgr); } } else { fail_open_destroy(mgr->fail_open); mgr->fail_open = NULL; } } static int set_pvconns(struct pvconn ***pvconnsp, size_t *n_pvconnsp, const struct sset *sset) { struct pvconn **pvconns = *pvconnsp; size_t n_pvconns = *n_pvconnsp; const char *name; int retval = 0; size_t i; for (i = 0; i < n_pvconns; i++) { pvconn_close(pvconns[i]); } free(pvconns); pvconns = xmalloc(sset_count(sset) * sizeof *pvconns); n_pvconns = 0; SSET_FOR_EACH (name, sset) { struct pvconn *pvconn; int error; error = pvconn_open(name, 0, 0, &pvconn); if (!error) { pvconns[n_pvconns++] = pvconn; } else { VLOG_ERR("failed to listen on %s: %s", name, ovs_strerror(error)); if (!retval) { retval = error; } } } *pvconnsp = pvconns; *n_pvconnsp = n_pvconns; return retval; } /* Returns a "preference level" for snooping 'ofconn'. A higher return value * means that 'ofconn' is more interesting for monitoring than a lower return * value. */ static int snoop_preference(const struct ofconn *ofconn) { switch (ofconn->role) { case OFPCR12_ROLE_MASTER: return 3; case OFPCR12_ROLE_EQUAL: return 2; case OFPCR12_ROLE_SLAVE: return 1; case OFPCR12_ROLE_NOCHANGE: default: /* Shouldn't happen. */ return 0; } } /* One of 'mgr''s "snoop" pvconns has accepted a new connection on 'vconn'. * Connects this vconn to a controller. */ static void add_snooper(struct connmgr *mgr, struct vconn *vconn) { struct ofconn *ofconn, *best; /* Pick a controller for monitoring. */ best = NULL; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofconn->type == OFCONN_PRIMARY && (!best || snoop_preference(ofconn) > snoop_preference(best))) { best = ofconn; } } if (best) { rconn_add_monitor(best->rconn, vconn); } else { VLOG_INFO_RL(&rl, "no controller connection to snoop"); vconn_close(vconn); } } /* Public ofconn functions. */ /* Returns the connection type, either OFCONN_PRIMARY or OFCONN_SERVICE. */ enum ofconn_type ofconn_get_type(const struct ofconn *ofconn) { return ofconn->type; } /* If a master election id is defined, stores it into '*idp' and returns * true. Otherwise, stores UINT64_MAX into '*idp' and returns false. */ bool ofconn_get_master_election_id(const struct ofconn *ofconn, uint64_t *idp) { *idp = (ofconn->connmgr->master_election_id_defined ? ofconn->connmgr->master_election_id : UINT64_MAX); return ofconn->connmgr->master_election_id_defined; } /* Sets the master election id. * * Returns true if successful, false if the id is stale */ bool ofconn_set_master_election_id(struct ofconn *ofconn, uint64_t id) { if (ofconn->connmgr->master_election_id_defined && /* Unsigned difference interpreted as a two's complement signed * value */ (int64_t)(id - ofconn->connmgr->master_election_id) < 0) { return false; } ofconn->connmgr->master_election_id = id; ofconn->connmgr->master_election_id_defined = true; return true; } /* Returns the role configured for 'ofconn'. * * The default role, if no other role has been set, is OFPCR12_ROLE_EQUAL. */ enum ofp12_controller_role ofconn_get_role(const struct ofconn *ofconn) { return ofconn->role; } void ofconn_send_role_status(struct ofconn *ofconn, uint32_t role, uint8_t reason) { struct ofputil_role_status status; struct ofpbuf *buf; status.reason = reason; status.role = role; ofconn_get_master_election_id(ofconn, &status.generation_id); buf = ofputil_encode_role_status(&status, ofconn_get_protocol(ofconn)); if (buf) { ofconn_send(ofconn, buf, NULL); } } /* Changes 'ofconn''s role to 'role'. If 'role' is OFPCR12_ROLE_MASTER then * any existing master is demoted to a slave. */ void ofconn_set_role(struct ofconn *ofconn, enum ofp12_controller_role role) { if (role != ofconn->role && role == OFPCR12_ROLE_MASTER) { struct ofconn *other; LIST_FOR_EACH (other, node, &ofconn->connmgr->all_conns) { if (other->role == OFPCR12_ROLE_MASTER) { other->role = OFPCR12_ROLE_SLAVE; ofconn_send_role_status(other, OFPCR12_ROLE_SLAVE, OFPCRR_MASTER_REQUEST); } } } ofconn->role = role; } void ofconn_set_invalid_ttl_to_controller(struct ofconn *ofconn, bool enable) { uint32_t bit = 1u << OFPR_INVALID_TTL; if (enable) { ofconn->master_async_config[OAM_PACKET_IN] |= bit; } else { ofconn->master_async_config[OAM_PACKET_IN] &= ~bit; } } bool ofconn_get_invalid_ttl_to_controller(struct ofconn *ofconn) { uint32_t bit = 1u << OFPR_INVALID_TTL; return (ofconn->master_async_config[OAM_PACKET_IN] & bit) != 0; } /* Returns the currently configured protocol for 'ofconn', one of OFPUTIL_P_*. * * Returns OFPUTIL_P_NONE, which is not a valid protocol, if 'ofconn' hasn't * completed version negotiation. This can't happen if at least one OpenFlow * message, other than OFPT_HELLO, has been received on the connection (such as * in ofproto.c's message handling code), since version negotiation is a * prerequisite for starting to receive messages. This means that * OFPUTIL_P_NONE is a special case that most callers need not worry about. */ enum ofputil_protocol ofconn_get_protocol(const struct ofconn *ofconn) { if (ofconn->protocol == OFPUTIL_P_NONE && rconn_is_connected(ofconn->rconn)) { int version = rconn_get_version(ofconn->rconn); if (version > 0) { ofconn_set_protocol(CONST_CAST(struct ofconn *, ofconn), ofputil_protocol_from_ofp_version(version)); } } return ofconn->protocol; } /* Sets the protocol for 'ofconn' to 'protocol' (one of OFPUTIL_P_*). * * (This doesn't actually send anything to accomplish this. Presumably the * caller already did that.) */ void ofconn_set_protocol(struct ofconn *ofconn, enum ofputil_protocol protocol) { ofconn->protocol = protocol; if (!(protocol & OFPUTIL_P_OF14_UP)) { uint32_t *master = ofconn->master_async_config; uint32_t *slave = ofconn->slave_async_config; /* OFPR_ACTION_SET is not supported before OF1.4 */ master[OAM_PACKET_IN] &= ~(1u << OFPR_ACTION_SET); slave [OAM_PACKET_IN] &= ~(1u << OFPR_ACTION_SET); /* OFPR_GROUP is not supported before OF1.4 */ master[OAM_PACKET_IN] &= ~(1u << OFPR_GROUP); slave [OAM_PACKET_IN] &= ~(1u << OFPR_GROUP); /* OFPR_PACKET_OUT is not supported before OF1.4 */ master[OAM_PACKET_IN] &= ~(1u << OFPR_PACKET_OUT); slave [OAM_PACKET_IN] &= ~(1u << OFPR_PACKET_OUT); /* OFPRR_GROUP_DELETE is not supported before OF1.4 */ master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_GROUP_DELETE); slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_GROUP_DELETE); /* OFPRR_METER_DELETE is not supported before OF1.4 */ master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_METER_DELETE); slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_METER_DELETE); /* OFPRR_EVICTION is not supported before OF1.4 */ master[OAM_FLOW_REMOVED] &= ~(1u << OFPRR_EVICTION); slave [OAM_FLOW_REMOVED] &= ~(1u << OFPRR_EVICTION); } } /* Returns the currently configured packet in format for 'ofconn', one of * NXPIF_*. * * The default, if no other format has been set, is NXPIF_OPENFLOW10. */ enum nx_packet_in_format ofconn_get_packet_in_format(struct ofconn *ofconn) { return ofconn->packet_in_format; } /* Sets the packet in format for 'ofconn' to 'packet_in_format' (one of * NXPIF_*). */ void ofconn_set_packet_in_format(struct ofconn *ofconn, enum nx_packet_in_format packet_in_format) { ofconn->packet_in_format = packet_in_format; } /* Sets the controller connection ID for 'ofconn' to 'controller_id'. * * The connection controller ID is used for OFPP_CONTROLLER and * NXAST_CONTROLLER actions. See "struct nx_action_controller" for details. */ void ofconn_set_controller_id(struct ofconn *ofconn, uint16_t controller_id) { ofconn->controller_id = controller_id; } /* Returns the default miss send length for 'ofconn'. */ int ofconn_get_miss_send_len(const struct ofconn *ofconn) { return ofconn->miss_send_len; } /* Sets the default miss send length for 'ofconn' to 'miss_send_len'. */ void ofconn_set_miss_send_len(struct ofconn *ofconn, int miss_send_len) { ofconn->miss_send_len = miss_send_len; } void ofconn_set_async_config(struct ofconn *ofconn, const uint32_t master_masks[OAM_N_TYPES], const uint32_t slave_masks[OAM_N_TYPES]) { size_t size = sizeof ofconn->master_async_config; memcpy(ofconn->master_async_config, master_masks, size); memcpy(ofconn->slave_async_config, slave_masks, size); } void ofconn_get_async_config(struct ofconn *ofconn, uint32_t *master_masks, uint32_t *slave_masks) { size_t size = sizeof ofconn->master_async_config; /* Make sure we know the protocol version and the async_config * masks are properly updated by calling ofconn_get_protocol() */ if (OFPUTIL_P_NONE == ofconn_get_protocol(ofconn)){ OVS_NOT_REACHED(); } memcpy(master_masks, ofconn->master_async_config, size); memcpy(slave_masks, ofconn->slave_async_config, size); } /* Sends 'msg' on 'ofconn', accounting it as a reply. (If there is a * sufficient number of OpenFlow replies in-flight on a single ofconn, then the * connmgr will stop accepting new OpenFlow requests on that ofconn until the * controller has accepted some of the replies.) */ void ofconn_send_reply(const struct ofconn *ofconn, struct ofpbuf *msg) { ofconn_send(ofconn, msg, ofconn->reply_counter); } /* Sends each of the messages in list 'replies' on 'ofconn' in order, * accounting them as replies. */ void ofconn_send_replies(const struct ofconn *ofconn, struct ovs_list *replies) { struct ofpbuf *reply; LIST_FOR_EACH_POP (reply, list_node, replies) { ofconn_send_reply(ofconn, reply); } } /* Sends 'error' on 'ofconn', as a reply to 'request'. Only at most the * first 64 bytes of 'request' are used. */ void ofconn_send_error(const struct ofconn *ofconn, const struct ofp_header *request, enum ofperr error) { static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(10, 10); struct ofpbuf *reply; reply = ofperr_encode_reply(error, request); if (!VLOG_DROP_INFO(&err_rl)) { const char *type_name; size_t request_len; enum ofpraw raw; request_len = ntohs(request->length); type_name = (!ofpraw_decode_partial(&raw, request, MIN(64, request_len)) ? ofpraw_get_name(raw) : "invalid"); VLOG_INFO("%s: sending %s error reply to %s message", rconn_get_name(ofconn->rconn), ofperr_to_string(error), type_name); } ofconn_send_reply(ofconn, reply); } /* Same as pktbuf_retrieve(), using the pktbuf owned by 'ofconn'. */ enum ofperr ofconn_pktbuf_retrieve(struct ofconn *ofconn, uint32_t id, struct dp_packet **bufferp, ofp_port_t *in_port) { return pktbuf_retrieve(ofconn->pktbuf, id, bufferp, in_port); } /* Reports that a flow_mod operation of the type specified by 'command' was * successfully executed by 'ofconn', so that the connmgr can log it. */ void ofconn_report_flow_mod(struct ofconn *ofconn, enum ofp_flow_mod_command command) { long long int now; switch (command) { case OFPFC_ADD: ofconn->n_add++; break; case OFPFC_MODIFY: case OFPFC_MODIFY_STRICT: ofconn->n_modify++; break; case OFPFC_DELETE: case OFPFC_DELETE_STRICT: ofconn->n_delete++; break; } now = time_msec(); if (ofconn->next_op_report == LLONG_MAX) { ofconn->first_op = now; ofconn->next_op_report = MAX(now + 10 * 1000, ofconn->op_backoff); ofconn->op_backoff = ofconn->next_op_report + 60 * 1000; } ofconn->last_op = now; } /* OpenFlow 1.4 bundles. */ static inline uint32_t bundle_hash(uint32_t id) { return hash_int(id, 0); } struct ofp_bundle * ofconn_get_bundle(struct ofconn *ofconn, uint32_t id) { struct ofp_bundle *bundle; HMAP_FOR_EACH_IN_BUCKET(bundle, node, bundle_hash(id), &ofconn->bundles) { if (bundle->id == id) { return bundle; } } return NULL; } enum ofperr ofconn_insert_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle) { /* XXX: Check the limit of open bundles */ hmap_insert(&ofconn->bundles, &bundle->node, bundle_hash(bundle->id)); return 0; } enum ofperr ofconn_remove_bundle(struct ofconn *ofconn, struct ofp_bundle *bundle) { hmap_remove(&ofconn->bundles, &bundle->node); return 0; } static void bundle_remove_all(struct ofconn *ofconn) { struct ofp_bundle *b, *next; HMAP_FOR_EACH_SAFE (b, next, node, &ofconn->bundles) { ofp_bundle_remove__(ofconn, b, false); } } /* Private ofconn functions. */ static const char * ofconn_get_target(const struct ofconn *ofconn) { return rconn_get_target(ofconn->rconn); } static struct ofconn * ofconn_create(struct connmgr *mgr, struct rconn *rconn, enum ofconn_type type, bool enable_async_msgs) { struct ofconn *ofconn; ofconn = xzalloc(sizeof *ofconn); ofconn->connmgr = mgr; list_push_back(&mgr->all_conns, &ofconn->node); ofconn->rconn = rconn; ofconn->type = type; ofconn->enable_async_msgs = enable_async_msgs; hmap_init(&ofconn->monitors); list_init(&ofconn->updates); hmap_init(&ofconn->bundles); ofconn_flush(ofconn); return ofconn; } /* Clears all of the state in 'ofconn' that should not persist from one * connection to the next. */ static void ofconn_flush(struct ofconn *ofconn) OVS_REQUIRES(ofproto_mutex) { struct ofmonitor *monitor, *next_monitor; int i; ofconn_log_flow_mods(ofconn); ofconn->role = OFPCR12_ROLE_EQUAL; ofconn_set_protocol(ofconn, OFPUTIL_P_NONE); ofconn->packet_in_format = NXPIF_OPENFLOW10; rconn_packet_counter_destroy(ofconn->packet_in_counter); ofconn->packet_in_counter = rconn_packet_counter_create(); for (i = 0; i < N_SCHEDULERS; i++) { if (ofconn->schedulers[i]) { int rate, burst; pinsched_get_limits(ofconn->schedulers[i], &rate, &burst); pinsched_destroy(ofconn->schedulers[i]); ofconn->schedulers[i] = pinsched_create(rate, burst); } } if (ofconn->pktbuf) { pktbuf_destroy(ofconn->pktbuf); ofconn->pktbuf = pktbuf_create(); } ofconn->miss_send_len = (ofconn->type == OFCONN_PRIMARY ? OFP_DEFAULT_MISS_SEND_LEN : 0); ofconn->controller_id = 0; rconn_packet_counter_destroy(ofconn->reply_counter); ofconn->reply_counter = rconn_packet_counter_create(); if (ofconn->enable_async_msgs) { uint32_t *master = ofconn->master_async_config; uint32_t *slave = ofconn->slave_async_config; /* "master" and "other" roles get all asynchronous messages by default, * except that the controller needs to enable nonstandard "packet-in" * reasons itself. */ master[OAM_PACKET_IN] = ((1u << OFPR_NO_MATCH) | (1u << OFPR_ACTION) | (1u << OFPR_ACTION_SET) | (1u << OFPR_GROUP) | (1u << OFPR_PACKET_OUT)); master[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) | (1u << OFPPR_DELETE) | (1u << OFPPR_MODIFY)); master[OAM_FLOW_REMOVED] = ((1u << OFPRR_IDLE_TIMEOUT) | (1u << OFPRR_HARD_TIMEOUT) | (1u << OFPRR_DELETE) | (1u << OFPRR_GROUP_DELETE) | (1u << OFPRR_METER_DELETE) | (1u << OFPRR_EVICTION)); master[OAM_ROLE_STATUS] = 0; master[OAM_TABLE_STATUS] = 0; master[OAM_REQUESTFORWARD] = 0; /* "slave" role gets port status updates by default. */ slave[OAM_PACKET_IN] = 0; slave[OAM_PORT_STATUS] = ((1u << OFPPR_ADD) | (1u << OFPPR_DELETE) | (1u << OFPPR_MODIFY)); slave[OAM_FLOW_REMOVED] = 0; slave[OAM_ROLE_STATUS] = 0; slave[OAM_TABLE_STATUS] = 0; slave[OAM_REQUESTFORWARD] = 0; } else { memset(ofconn->master_async_config, 0, sizeof ofconn->master_async_config); memset(ofconn->slave_async_config, 0, sizeof ofconn->slave_async_config); } ofconn->n_add = ofconn->n_delete = ofconn->n_modify = 0; ofconn->first_op = ofconn->last_op = LLONG_MIN; ofconn->next_op_report = LLONG_MAX; ofconn->op_backoff = LLONG_MIN; HMAP_FOR_EACH_SAFE (monitor, next_monitor, ofconn_node, &ofconn->monitors) { ofmonitor_destroy(monitor); } rconn_packet_counter_destroy(ofconn->monitor_counter); ofconn->monitor_counter = rconn_packet_counter_create(); ofpbuf_list_delete(&ofconn->updates); /* ...but it should be empty. */ } static void ofconn_destroy(struct ofconn *ofconn) OVS_REQUIRES(ofproto_mutex) { ofconn_flush(ofconn); if (ofconn->type == OFCONN_PRIMARY) { hmap_remove(&ofconn->connmgr->controllers, &ofconn->hmap_node); } bundle_remove_all(ofconn); hmap_destroy(&ofconn->bundles); hmap_destroy(&ofconn->monitors); list_remove(&ofconn->node); rconn_destroy(ofconn->rconn); rconn_packet_counter_destroy(ofconn->packet_in_counter); rconn_packet_counter_destroy(ofconn->reply_counter); pktbuf_destroy(ofconn->pktbuf); rconn_packet_counter_destroy(ofconn->monitor_counter); free(ofconn); } /* Reconfigures 'ofconn' to match 'c'. 'ofconn' and 'c' must have the same * target. */ static void ofconn_reconfigure(struct ofconn *ofconn, const struct ofproto_controller *c) { int probe_interval; ofconn->band = c->band; ofconn->enable_async_msgs = c->enable_async_msgs; rconn_set_max_backoff(ofconn->rconn, c->max_backoff); probe_interval = c->probe_interval ? MAX(c->probe_interval, 5) : 0; rconn_set_probe_interval(ofconn->rconn, probe_interval); ofconn_set_rate_limit(ofconn, c->rate_limit, c->burst_limit); /* If dscp value changed reconnect. */ if (c->dscp != rconn_get_dscp(ofconn->rconn)) { rconn_set_dscp(ofconn->rconn, c->dscp); rconn_reconnect(ofconn->rconn); } } /* Returns true if it makes sense for 'ofconn' to receive and process OpenFlow * messages. */ static bool ofconn_may_recv(const struct ofconn *ofconn) { int count = rconn_packet_counter_n_packets(ofconn->reply_counter); return count < OFCONN_REPLY_MAX; } static void ofconn_run(struct ofconn *ofconn, void (*handle_openflow)(struct ofconn *, const struct ofpbuf *ofp_msg)) { struct connmgr *mgr = ofconn->connmgr; size_t i; for (i = 0; i < N_SCHEDULERS; i++) { struct ovs_list txq; pinsched_run(ofconn->schedulers[i], &txq); do_send_packet_ins(ofconn, &txq); } rconn_run(ofconn->rconn); /* Limit the number of iterations to avoid starving other tasks. */ for (i = 0; i < 50 && ofconn_may_recv(ofconn); i++) { struct ofpbuf *of_msg = rconn_recv(ofconn->rconn); if (!of_msg) { break; } if (mgr->fail_open) { fail_open_maybe_recover(mgr->fail_open); } handle_openflow(ofconn, of_msg); ofpbuf_delete(of_msg); } if (time_msec() >= ofconn->next_op_report) { ofconn_log_flow_mods(ofconn); } ovs_mutex_lock(&ofproto_mutex); if (!rconn_is_alive(ofconn->rconn)) { ofconn_destroy(ofconn); } else if (!rconn_is_connected(ofconn->rconn)) { ofconn_flush(ofconn); } ovs_mutex_unlock(&ofproto_mutex); } static void ofconn_wait(struct ofconn *ofconn) { int i; for (i = 0; i < N_SCHEDULERS; i++) { pinsched_wait(ofconn->schedulers[i]); } rconn_run_wait(ofconn->rconn); if (ofconn_may_recv(ofconn)) { rconn_recv_wait(ofconn->rconn); } if (ofconn->next_op_report != LLONG_MAX) { poll_timer_wait_until(ofconn->next_op_report); } } static void ofconn_log_flow_mods(struct ofconn *ofconn) { int n_flow_mods = ofconn->n_add + ofconn->n_delete + ofconn->n_modify; if (n_flow_mods) { long long int ago = (time_msec() - ofconn->first_op) / 1000; long long int interval = (ofconn->last_op - ofconn->first_op) / 1000; struct ds s; ds_init(&s); ds_put_format(&s, "%d flow_mods ", n_flow_mods); if (interval == ago) { ds_put_format(&s, "in the last %lld s", ago); } else if (interval) { ds_put_format(&s, "in the %lld s starting %lld s ago", interval, ago); } else { ds_put_format(&s, "%lld s ago", ago); } ds_put_cstr(&s, " ("); if (ofconn->n_add) { ds_put_format(&s, "%d adds, ", ofconn->n_add); } if (ofconn->n_delete) { ds_put_format(&s, "%d deletes, ", ofconn->n_delete); } if (ofconn->n_modify) { ds_put_format(&s, "%d modifications, ", ofconn->n_modify); } s.length -= 2; ds_put_char(&s, ')'); VLOG_INFO("%s: %s", rconn_get_name(ofconn->rconn), ds_cstr(&s)); ds_destroy(&s); ofconn->n_add = ofconn->n_delete = ofconn->n_modify = 0; } ofconn->next_op_report = LLONG_MAX; } /* Returns true if 'ofconn' should receive asynchronous messages of the given * OAM_* 'type' and 'reason', which should be a OFPR_* value for OAM_PACKET_IN, * a OFPPR_* value for OAM_PORT_STATUS, or an OFPRR_* value for * OAM_FLOW_REMOVED. Returns false if the message should not be sent on * 'ofconn'. */ static bool ofconn_receives_async_msg(const struct ofconn *ofconn, enum ofputil_async_msg_type type, unsigned int reason) { const uint32_t *async_config; ovs_assert(reason < 32); ovs_assert((unsigned int) type < OAM_N_TYPES); if (ofconn_get_protocol(ofconn) == OFPUTIL_P_NONE || !rconn_is_connected(ofconn->rconn)) { return false; } /* Keep the following code in sync with the documentation in the * "Asynchronous Messages" section in DESIGN. */ if (ofconn->type == OFCONN_SERVICE && !ofconn->miss_send_len) { /* Service connections don't get asynchronous messages unless they have * explicitly asked for them by setting a nonzero miss send length. */ return false; } async_config = (ofconn->role == OFPCR12_ROLE_SLAVE ? ofconn->slave_async_config : ofconn->master_async_config); if (!(async_config[type] & (1u << reason))) { return false; } return true; } /* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the * packet rather than to send the packet to the controller. * * This function returns false to indicate the packet should be dropped if * the controller action was the result of the default table-miss behaviour * and the controller is using OpenFlow1.3+. * * Otherwise true is returned to indicate the packet should be forwarded to * the controller */ static bool ofconn_wants_packet_in_on_miss(struct ofconn *ofconn, const struct ofproto_packet_in *pin) { if (pin->miss_type == OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW) { enum ofputil_protocol protocol = ofconn_get_protocol(ofconn); if (protocol != OFPUTIL_P_NONE && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION && (ofproto_table_get_miss_config(ofconn->connmgr->ofproto, pin->up.table_id) == OFPUTIL_TABLE_MISS_DEFAULT)) { return false; } } return true; } /* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the * packet rather than to send the packet to the controller. * * This function returns true to indicate that a packet_in message * for a "table-miss" should be sent to at least one controller. * That is there is at least one controller with controller_id 0 * which connected using an OpenFlow version earlier than OpenFlow1.3. * * False otherwise. * * This logic assumes that "table-miss" packet_in messages * are always sent to controller_id 0. */ bool connmgr_wants_packet_in_on_miss(struct connmgr *mgr) OVS_EXCLUDED(ofproto_mutex) { struct ofconn *ofconn; ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { enum ofputil_protocol protocol = ofconn_get_protocol(ofconn); if (ofconn->controller_id == 0 && (protocol == OFPUTIL_P_NONE || ofputil_protocol_to_ofp_version(protocol) < OFP13_VERSION)) { ovs_mutex_unlock(&ofproto_mutex); return true; } } ovs_mutex_unlock(&ofproto_mutex); return false; } /* Returns a human-readable name for an OpenFlow connection between 'mgr' and * 'target', suitable for use in log messages for identifying the connection. * * The name is dynamically allocated. The caller should free it (with free()) * when it is no longer needed. */ static char * ofconn_make_name(const struct connmgr *mgr, const char *target) { return xasprintf("%s<->%s", mgr->name, target); } static void ofconn_set_rate_limit(struct ofconn *ofconn, int rate, int burst) { int i; for (i = 0; i < N_SCHEDULERS; i++) { struct pinsched **s = &ofconn->schedulers[i]; if (rate > 0) { if (!*s) { *s = pinsched_create(rate, burst); } else { pinsched_set_limits(*s, rate, burst); } } else { pinsched_destroy(*s); *s = NULL; } } } static void ofconn_send(const struct ofconn *ofconn, struct ofpbuf *msg, struct rconn_packet_counter *counter) { ofpmsg_update_length(msg); rconn_send(ofconn->rconn, msg, counter); } /* Sending asynchronous messages. */ static void schedule_packet_in(struct ofconn *, struct ofproto_packet_in, enum ofp_packet_in_reason wire_reason); /* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate * controllers managed by 'mgr'. For messages caused by a controller * OFPT_PORT_MOD, specify 'source' as the controller connection that sent the * request; otherwise, specify 'source' as NULL. */ void connmgr_send_port_status(struct connmgr *mgr, struct ofconn *source, const struct ofputil_phy_port *pp, uint8_t reason) { /* XXX Should limit the number of queued port status change messages. */ struct ofputil_port_status ps; struct ofconn *ofconn; ps.reason = reason; ps.desc = *pp; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) { struct ofpbuf *msg; /* Before 1.5, OpenFlow specified that OFPT_PORT_MOD should not * generate OFPT_PORT_STATUS messages. That requirement was a * relic of how OpenFlow originally supported a single controller, * so that one could expect the controller to already know the * changes it had made. * * EXT-338 changes OpenFlow 1.5 OFPT_PORT_MOD to send * OFPT_PORT_STATUS messages to every controller. This is * obviously more useful in the multi-controller case. We could * always implement it that way in OVS, but that would risk * confusing controllers that are intended for single-controller * use only. (Imagine a controller that generates an OFPT_PORT_MOD * in response to any OFPT_PORT_STATUS!) * * So this compromises: for OpenFlow 1.4 and earlier, it generates * OFPT_PORT_STATUS for OFPT_PORT_MOD, but not back to the * originating controller. In a single-controller environment, in * particular, this means that it will never generate * OFPT_PORT_STATUS for OFPT_PORT_MOD at all. */ if (ofconn == source && rconn_get_version(ofconn->rconn) < OFP15_VERSION) { continue; } msg = ofputil_encode_port_status(&ps, ofconn_get_protocol(ofconn)); ofconn_send(ofconn, msg, NULL); } } } /* Sends an OFPT_REQUESTFORWARD message with 'request' and 'reason' to * appropriate controllers managed by 'mgr'. For messages caused by a * controller OFPT_GROUP_MOD and OFPT_METER_MOD, specify 'source' as the * controller connection that sent the request; otherwise, specify 'source' * as NULL. */ void connmgr_send_requestforward(struct connmgr *mgr, const struct ofconn *source, const struct ofputil_requestforward *rf) { struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofconn_receives_async_msg(ofconn, OAM_REQUESTFORWARD, rf->reason) && rconn_get_version(ofconn->rconn) >= OFP14_VERSION && ofconn != source) { enum ofputil_protocol protocol = ofconn_get_protocol(ofconn); ofconn_send(ofconn, ofputil_encode_requestforward(rf, protocol), NULL); } } } /* Sends an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message based on 'fr' to * appropriate controllers managed by 'mgr'. */ void connmgr_send_flow_removed(struct connmgr *mgr, const struct ofputil_flow_removed *fr) { struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofconn_receives_async_msg(ofconn, OAM_FLOW_REMOVED, fr->reason)) { struct ofpbuf *msg; /* Account flow expirations as replies to OpenFlow requests. That * works because preventing OpenFlow requests from being processed * also prevents new flows from being added (and expiring). (It * also prevents processing OpenFlow requests that would not add * new flows, so it is imperfect.) */ msg = ofputil_encode_flow_removed(fr, ofconn_get_protocol(ofconn)); ofconn_send_reply(ofconn, msg); } } } /* Normally a send-to-controller action uses reason OFPR_ACTION. However, in * OpenFlow 1.3 and later, packet_ins generated by a send-to-controller action * in a "table-miss" flow (one with priority 0 and completely wildcarded) are * sent as OFPR_NO_MATCH. This function returns the reason that should * actually be sent on 'ofconn' for 'pin'. */ static enum ofp_packet_in_reason wire_reason(struct ofconn *ofconn, const struct ofproto_packet_in *pin) { enum ofputil_protocol protocol = ofconn_get_protocol(ofconn); if (pin->miss_type == OFPROTO_PACKET_IN_MISS_FLOW && pin->up.reason == OFPR_ACTION && protocol != OFPUTIL_P_NONE && ofputil_protocol_to_ofp_version(protocol) >= OFP13_VERSION) { return OFPR_NO_MATCH; } switch (pin->up.reason) { case OFPR_ACTION_SET: case OFPR_GROUP: case OFPR_PACKET_OUT: if (!(protocol & OFPUTIL_P_OF14_UP)) { /* Only supported in OF1.4+ */ return OFPR_ACTION; } /* Fall through. */ case OFPR_NO_MATCH: case OFPR_ACTION: case OFPR_INVALID_TTL: case OFPR_N_REASONS: default: return pin->up.reason; } } /* Given 'pin', sends an OFPT_PACKET_IN message to each OpenFlow controller as * necessary according to their individual configurations. * * The caller doesn't need to fill in pin->buffer_id or pin->total_len. */ void connmgr_send_packet_in(struct connmgr *mgr, const struct ofproto_packet_in *pin) { struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { enum ofp_packet_in_reason reason = wire_reason(ofconn, pin); if (ofconn_wants_packet_in_on_miss(ofconn, pin) && ofconn_receives_async_msg(ofconn, OAM_PACKET_IN, reason) && ofconn->controller_id == pin->controller_id) { schedule_packet_in(ofconn, *pin, reason); } } } static void do_send_packet_ins(struct ofconn *ofconn, struct ovs_list *txq) { struct ofpbuf *pin; LIST_FOR_EACH_POP (pin, list_node, txq) { if (rconn_send_with_limit(ofconn->rconn, pin, ofconn->packet_in_counter, 100) == EAGAIN) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); VLOG_INFO_RL(&rl, "%s: dropping packet-in due to queue overflow", rconn_get_name(ofconn->rconn)); } } } /* Takes 'pin', composes an OpenFlow packet-in message from it, and passes it * to 'ofconn''s packet scheduler for sending. */ static void schedule_packet_in(struct ofconn *ofconn, struct ofproto_packet_in pin, enum ofp_packet_in_reason wire_reason) { struct connmgr *mgr = ofconn->connmgr; uint16_t controller_max_len; struct ovs_list txq; pin.up.total_len = pin.up.packet_len; pin.up.reason = wire_reason; if (pin.up.reason == OFPR_ACTION) { controller_max_len = pin.send_len; /* max_len */ } else { controller_max_len = ofconn->miss_send_len; } /* Get OpenFlow buffer_id. * For OpenFlow 1.2+, OFPCML_NO_BUFFER (== UINT16_MAX) specifies * unbuffered. This behaviour doesn't violate prior versions, too. */ if (controller_max_len == UINT16_MAX) { pin.up.buffer_id = UINT32_MAX; } else if (mgr->fail_open && fail_open_is_active(mgr->fail_open)) { pin.up.buffer_id = pktbuf_get_null(); } else if (!ofconn->pktbuf) { pin.up.buffer_id = UINT32_MAX; } else { pin.up.buffer_id = pktbuf_save(ofconn->pktbuf, pin.up.packet, pin.up.packet_len, pin.up.flow_metadata.flow.in_port.ofp_port); } /* Figure out how much of the packet to send. * If not buffered, send the entire packet. Otherwise, depending on * the reason of packet-in, send what requested by the controller. */ if (pin.up.buffer_id != UINT32_MAX && controller_max_len < pin.up.packet_len) { pin.up.packet_len = controller_max_len; } /* Make OFPT_PACKET_IN and hand over to packet scheduler. */ pinsched_send(ofconn->schedulers[pin.up.reason == OFPR_NO_MATCH ? 0 : 1], pin.up.flow_metadata.flow.in_port.ofp_port, ofputil_encode_packet_in(&pin.up, ofconn_get_protocol(ofconn), ofconn->packet_in_format), &txq); do_send_packet_ins(ofconn, &txq); } /* Fail-open settings. */ /* Returns the failure handling mode (OFPROTO_FAIL_SECURE or * OFPROTO_FAIL_STANDALONE) for 'mgr'. */ enum ofproto_fail_mode connmgr_get_fail_mode(const struct connmgr *mgr) { return mgr->fail_mode; } /* Sets the failure handling mode for 'mgr' to 'fail_mode' (either * OFPROTO_FAIL_SECURE or OFPROTO_FAIL_STANDALONE). */ void connmgr_set_fail_mode(struct connmgr *mgr, enum ofproto_fail_mode fail_mode) { if (mgr->fail_mode != fail_mode) { mgr->fail_mode = fail_mode; update_fail_open(mgr); if (!connmgr_has_controllers(mgr)) { ofproto_flush_flows(mgr->ofproto); } } } /* Fail-open implementation. */ /* Returns the longest probe interval among the primary controllers configured * on 'mgr'. Returns 0 if there are no primary controllers. */ int connmgr_get_max_probe_interval(const struct connmgr *mgr) { const struct ofconn *ofconn; int max_probe_interval; max_probe_interval = 0; HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { int probe_interval = rconn_get_probe_interval(ofconn->rconn); max_probe_interval = MAX(max_probe_interval, probe_interval); } return max_probe_interval; } /* Returns the number of seconds for which all of 'mgr's primary controllers * have been disconnected. Returns 0 if 'mgr' has no primary controllers. */ int connmgr_failure_duration(const struct connmgr *mgr) { const struct ofconn *ofconn; int min_failure_duration; if (!connmgr_has_controllers(mgr)) { return 0; } min_failure_duration = INT_MAX; HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { int failure_duration = rconn_failure_duration(ofconn->rconn); min_failure_duration = MIN(min_failure_duration, failure_duration); } return min_failure_duration; } /* Returns true if at least one primary controller is connected (regardless of * whether those controllers are believed to have authenticated and accepted * this switch), false if none of them are connected. */ bool connmgr_is_any_controller_connected(const struct connmgr *mgr) { const struct ofconn *ofconn; HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { if (rconn_is_connected(ofconn->rconn)) { return true; } } return false; } /* Returns true if at least one primary controller is believed to have * authenticated and accepted this switch, false otherwise. */ bool connmgr_is_any_controller_admitted(const struct connmgr *mgr) { const struct ofconn *ofconn; HMAP_FOR_EACH (ofconn, hmap_node, &mgr->controllers) { if (rconn_is_admitted(ofconn->rconn)) { return true; } } return false; } /* In-band configuration. */ static bool any_extras_changed(const struct connmgr *, const struct sockaddr_in *extras, size_t n); /* Sets the 'n' TCP port addresses in 'extras' as ones to which 'mgr''s * in-band control should guarantee access, in the same way that in-band * control guarantees access to OpenFlow controllers. */ void connmgr_set_extra_in_band_remotes(struct connmgr *mgr, const struct sockaddr_in *extras, size_t n) { if (!any_extras_changed(mgr, extras, n)) { return; } free(mgr->extra_in_band_remotes); mgr->n_extra_remotes = n; mgr->extra_in_band_remotes = xmemdup(extras, n * sizeof *extras); update_in_band_remotes(mgr); } /* Sets the OpenFlow queue used by flows set up by in-band control on * 'mgr' to 'queue_id'. If 'queue_id' is negative, then in-band control * flows will use the default queue. */ void connmgr_set_in_band_queue(struct connmgr *mgr, int queue_id) { if (queue_id != mgr->in_band_queue) { mgr->in_band_queue = queue_id; update_in_band_remotes(mgr); } } static bool any_extras_changed(const struct connmgr *mgr, const struct sockaddr_in *extras, size_t n) { size_t i; if (n != mgr->n_extra_remotes) { return true; } for (i = 0; i < n; i++) { const struct sockaddr_in *old = &mgr->extra_in_band_remotes[i]; const struct sockaddr_in *new = &extras[i]; if (old->sin_addr.s_addr != new->sin_addr.s_addr || old->sin_port != new->sin_port) { return true; } } return false; } /* In-band implementation. */ bool connmgr_has_in_band(struct connmgr *mgr) { return mgr->in_band != NULL; } /* Fail-open and in-band implementation. */ /* Called by 'ofproto' after all flows have been flushed, to allow fail-open * and standalone mode to re-create their flows. * * In-band control has more sophisticated code that manages flows itself. */ void connmgr_flushed(struct connmgr *mgr) OVS_EXCLUDED(ofproto_mutex) { if (mgr->fail_open) { fail_open_flushed(mgr->fail_open); } /* If there are no controllers and we're in standalone mode, set up a flow * that matches every packet and directs them to OFPP_NORMAL (which goes to * us). Otherwise, the switch is in secure mode and we won't pass any * traffic until a controller has been defined and it tells us to do so. */ if (!connmgr_has_controllers(mgr) && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) { struct ofpbuf ofpacts; struct match match; ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE); ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; ofpact_pad(&ofpacts); match_init_catchall(&match); ofproto_add_flow(mgr->ofproto, &match, 0, ofpacts.data, ofpacts.size); ofpbuf_uninit(&ofpacts); } } /* Returns the number of hidden rules created by the in-band and fail-open * implementations in table 0. (Subtracting this count from the number of * rules in the table 0 classifier, as maintained in struct oftable, yields * the number of flows that OVS should report via OpenFlow for table 0.) */ int connmgr_count_hidden_rules(const struct connmgr *mgr) { int n_hidden = 0; if (mgr->in_band) { n_hidden += in_band_count_rules(mgr->in_band); } if (mgr->fail_open) { n_hidden += fail_open_count_rules(mgr->fail_open); } return n_hidden; } /* Creates a new ofservice for 'target' in 'mgr'. Returns 0 if successful, * otherwise a positive errno value. * * ofservice_reconfigure() must be called to fully configure the new * ofservice. */ static int ofservice_create(struct connmgr *mgr, const char *target, uint32_t allowed_versions, uint8_t dscp) { struct ofservice *ofservice; struct pvconn *pvconn; int error; error = pvconn_open(target, allowed_versions, dscp, &pvconn); if (error) { return error; } ofservice = xzalloc(sizeof *ofservice); hmap_insert(&mgr->services, &ofservice->node, hash_string(target, 0)); ofservice->pvconn = pvconn; ofservice->allowed_versions = allowed_versions; return 0; } static void ofservice_destroy(struct connmgr *mgr, struct ofservice *ofservice) { hmap_remove(&mgr->services, &ofservice->node); pvconn_close(ofservice->pvconn); free(ofservice); } static void ofservice_reconfigure(struct ofservice *ofservice, const struct ofproto_controller *c) { ofservice->probe_interval = c->probe_interval; ofservice->rate_limit = c->rate_limit; ofservice->burst_limit = c->burst_limit; ofservice->enable_async_msgs = c->enable_async_msgs; ofservice->dscp = c->dscp; } /* Finds and returns the ofservice within 'mgr' that has the given * 'target', or a null pointer if none exists. */ static struct ofservice * ofservice_lookup(struct connmgr *mgr, const char *target) { struct ofservice *ofservice; HMAP_FOR_EACH_WITH_HASH (ofservice, node, hash_string(target, 0), &mgr->services) { if (!strcmp(pvconn_get_name(ofservice->pvconn), target)) { return ofservice; } } return NULL; } /* Flow monitors (NXST_FLOW_MONITOR). */ /* A counter incremented when something significant happens to an OpenFlow * rule. * * - When a rule is added, its 'add_seqno' and 'modify_seqno' are set to * the current value (which is then incremented). * * - When a rule is modified, its 'modify_seqno' is set to the current * value (which is then incremented). * * Thus, by comparing an old value of monitor_seqno against a rule's * 'add_seqno', one can tell whether the rule was added before or after the old * value was read, and similarly for 'modify_seqno'. * * 32 bits should normally be sufficient (and would be nice, to save space in * each rule) but then we'd have to have some special cases for wraparound. * * We initialize monitor_seqno to 1 to allow 0 to be used as an invalid * value. */ static uint64_t monitor_seqno = 1; COVERAGE_DEFINE(ofmonitor_pause); COVERAGE_DEFINE(ofmonitor_resume); enum ofperr ofmonitor_create(const struct ofputil_flow_monitor_request *request, struct ofconn *ofconn, struct ofmonitor **monitorp) OVS_REQUIRES(ofproto_mutex) { struct ofmonitor *m; *monitorp = NULL; m = ofmonitor_lookup(ofconn, request->id); if (m) { return OFPERR_OFPMOFC_MONITOR_EXISTS; } m = xmalloc(sizeof *m); m->ofconn = ofconn; hmap_insert(&ofconn->monitors, &m->ofconn_node, hash_int(request->id, 0)); m->id = request->id; m->flags = request->flags; m->out_port = request->out_port; m->table_id = request->table_id; minimatch_init(&m->match, &request->match); *monitorp = m; return 0; } struct ofmonitor * ofmonitor_lookup(struct ofconn *ofconn, uint32_t id) OVS_REQUIRES(ofproto_mutex) { struct ofmonitor *m; HMAP_FOR_EACH_IN_BUCKET (m, ofconn_node, hash_int(id, 0), &ofconn->monitors) { if (m->id == id) { return m; } } return NULL; } void ofmonitor_destroy(struct ofmonitor *m) OVS_REQUIRES(ofproto_mutex) { if (m) { minimatch_destroy(&m->match); hmap_remove(&m->ofconn->monitors, &m->ofconn_node); free(m); } } void ofmonitor_report(struct connmgr *mgr, struct rule *rule, enum nx_flow_update_event event, enum ofp_flow_removed_reason reason, const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid, const struct rule_actions *old_actions) OVS_REQUIRES(ofproto_mutex) { enum nx_flow_monitor_flags update; struct ofconn *ofconn; if (rule_is_hidden(rule)) { return; } switch (event) { case NXFME_ADDED: update = NXFMF_ADD; rule->add_seqno = rule->modify_seqno = monitor_seqno++; break; case NXFME_DELETED: update = NXFMF_DELETE; break; case NXFME_MODIFIED: update = NXFMF_MODIFY; rule->modify_seqno = monitor_seqno++; break; default: case NXFME_ABBREV: OVS_NOT_REACHED(); } LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { enum nx_flow_monitor_flags flags = 0; struct ofmonitor *m; if (ofconn->monitor_paused) { /* Only send NXFME_DELETED notifications for flows that were added * before we paused. */ if (event != NXFME_DELETED || rule->add_seqno > ofconn->monitor_paused) { continue; } } HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) { if (m->flags & update && (m->table_id == 0xff || m->table_id == rule->table_id) && (ofproto_rule_has_out_port(rule, m->out_port) || (old_actions && ofpacts_output_to_port(old_actions->ofpacts, old_actions->ofpacts_len, m->out_port))) && cls_rule_is_loose_match(&rule->cr, &m->match)) { flags |= m->flags; } } if (flags) { if (list_is_empty(&ofconn->updates)) { ofputil_start_flow_update(&ofconn->updates); ofconn->sent_abbrev_update = false; } if (flags & NXFMF_OWN || ofconn != abbrev_ofconn || ofconn->monitor_paused) { struct ofputil_flow_update fu; struct match match; fu.event = event; fu.reason = event == NXFME_DELETED ? reason : 0; fu.table_id = rule->table_id; fu.cookie = rule->flow_cookie; minimatch_expand(&rule->cr.match, &match); fu.match = &match; fu.priority = rule->cr.priority; ovs_mutex_lock(&rule->mutex); fu.idle_timeout = rule->idle_timeout; fu.hard_timeout = rule->hard_timeout; ovs_mutex_unlock(&rule->mutex); if (flags & NXFMF_ACTIONS) { const struct rule_actions *actions = rule_get_actions(rule); fu.ofpacts = actions->ofpacts; fu.ofpacts_len = actions->ofpacts_len; } else { fu.ofpacts = NULL; fu.ofpacts_len = 0; } ofputil_append_flow_update(&fu, &ofconn->updates); } else if (!ofconn->sent_abbrev_update) { struct ofputil_flow_update fu; fu.event = NXFME_ABBREV; fu.xid = abbrev_xid; ofputil_append_flow_update(&fu, &ofconn->updates); ofconn->sent_abbrev_update = true; } } } } void ofmonitor_flush(struct connmgr *mgr) OVS_REQUIRES(ofproto_mutex) { struct ofconn *ofconn; LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { struct rconn_packet_counter *counter = ofconn->monitor_counter; struct ofpbuf *msg; LIST_FOR_EACH_POP (msg, list_node, &ofconn->updates) { ofconn_send(ofconn, msg, counter); } if (!ofconn->monitor_paused && rconn_packet_counter_n_bytes(counter) > 128 * 1024) { struct ofpbuf *pause; COVERAGE_INC(ofmonitor_pause); ofconn->monitor_paused = monitor_seqno++; pause = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_PAUSED, OFP10_VERSION, htonl(0), 0); ofconn_send(ofconn, pause, counter); } } } static void ofmonitor_resume(struct ofconn *ofconn) OVS_REQUIRES(ofproto_mutex) { struct rule_collection rules; struct ofpbuf *resumed; struct ofmonitor *m; struct ovs_list msgs; rule_collection_init(&rules); HMAP_FOR_EACH (m, ofconn_node, &ofconn->monitors) { ofmonitor_collect_resume_rules(m, ofconn->monitor_paused, &rules); } list_init(&msgs); ofmonitor_compose_refresh_updates(&rules, &msgs); resumed = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION, htonl(0), 0); list_push_back(&msgs, &resumed->list_node); ofconn_send_replies(ofconn, &msgs); ofconn->monitor_paused = 0; } static bool ofmonitor_may_resume(const struct ofconn *ofconn) OVS_REQUIRES(ofproto_mutex) { return (ofconn->monitor_paused != 0 && !rconn_packet_counter_n_packets(ofconn->monitor_counter)); } static void ofmonitor_run(struct connmgr *mgr) { struct ofconn *ofconn; ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofmonitor_may_resume(ofconn)) { COVERAGE_INC(ofmonitor_resume); ofmonitor_resume(ofconn); } } ovs_mutex_unlock(&ofproto_mutex); } static void ofmonitor_wait(struct connmgr *mgr) { struct ofconn *ofconn; ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH (ofconn, node, &mgr->all_conns) { if (ofmonitor_may_resume(ofconn)) { poll_immediate_wake(); } } ovs_mutex_unlock(&ofproto_mutex); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-upcall.h0000644000000000000000000000013113534540071021372 xustar0030 mtime=1567801401.637682902 29 atime=1567801402.10968637 30 ctime=1567801425.049855401 openvswitch-2.5.9/ofproto/ofproto-dpif-upcall.h0000644000175000017500000000270013534540071023060 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_UPCALL_H #define OFPROTO_DPIF_UPCALL_H #include struct dpif; struct dpif_backer; struct dpif_upcall; struct ofpbuf; struct seq; struct simap; /* Udif is responsible for retrieving upcalls from the kernel and processing * them. Additionally, it's responsible for maintaining the datapath flow * table. */ void udpif_init(void); struct udpif *udpif_create(struct dpif_backer *, struct dpif *); void udpif_run(struct udpif *udpif); void udpif_set_threads(struct udpif *, size_t n_handlers, size_t n_revalidators); void udpif_synchronize(struct udpif *); void udpif_destroy(struct udpif *); void udpif_revalidate(struct udpif *); void udpif_get_memory_usage(struct udpif *, struct simap *usage); struct seq *udpif_dump_seq(struct udpif *); void udpif_flush(struct udpif *); #endif /* ofproto-dpif-upcall.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/names.c0000644000000000000000000000013113534540071016602 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801425.025855226 openvswitch-2.5.9/ofproto/names.c0000644000175000017500000000217613534540071020277 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto/ofproto.h" #include "dpif.h" /* This function is in a separate file because it is the only ofproto function * required by ovs-ofctl, which allows ovs-ofctl to avoid linking in extra * baggage. */ /* Parses 'ofproto_name', which is of the form [type@]name into component * pieces that are suitable for passing to ofproto_create(). The caller must * free 'name' and 'type'. */ void ofproto_parse_name(const char *ofproto_name, char **name, char **type) { dp_parse_name(ofproto_name, name, type); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ipfix-entities.def0000644000000000000000000000013113534540117020755 xustar0030 mtime=1567801423.545844314 30 atime=1567801423.477843813 29 ctime=1567801425.06185549 openvswitch-2.5.9/ofproto/ipfix-entities.def0000644000175000017500000005327013534540117022453 0ustar00jpettitjpettit00000000000000/* IPFIX entities. */ #ifndef IPFIX_ENTITY #define IPFIX_ENTITY(ENUM, ID, SIZE, NAME) #endif IPFIX_ENTITY(OCTET_DELTA_COUNT, 1, 8, octetDeltaCount) IPFIX_ENTITY(PACKET_DELTA_COUNT, 2, 8, packetDeltaCount) IPFIX_ENTITY(DELTA_FLOW_COUNT, 3, 8, deltaFlowCount) IPFIX_ENTITY(PROTOCOL_IDENTIFIER, 4, 1, protocolIdentifier) IPFIX_ENTITY(IP_CLASS_OF_SERVICE, 5, 1, ipClassOfService) IPFIX_ENTITY(TCP_CONTROL_BITS, 6, 1, tcpControlBits) IPFIX_ENTITY(SOURCE_TRANSPORT_PORT, 7, 2, sourceTransportPort) IPFIX_ENTITY(SOURCE_IPV4_ADDRESS, 8, 4, sourceIPv4Address) IPFIX_ENTITY(SOURCE_IPV4_PREFIX_LENGTH, 9, 1, sourceIPv4PrefixLength) IPFIX_ENTITY(INGRESS_INTERFACE, 10, 4, ingressInterface) IPFIX_ENTITY(DESTINATION_TRANSPORT_PORT, 11, 2, destinationTransportPort) IPFIX_ENTITY(DESTINATION_IPV4_ADDRESS, 12, 4, destinationIPv4Address) IPFIX_ENTITY(DESTINATION_IPV4_PREFIX_LENGTH, 13, 1, destinationIPv4PrefixLength) IPFIX_ENTITY(EGRESS_INTERFACE, 14, 4, egressInterface) IPFIX_ENTITY(IP_NEXT_HOP_IPV4_ADDRESS, 15, 4, ipNextHopIPv4Address) IPFIX_ENTITY(BGP_SOURCE_AS_NUMBER, 16, 4, bgpSourceAsNumber) IPFIX_ENTITY(BGP_DESTINATION_AS_NUMBER, 17, 4, bgpDestinationAsNumber) IPFIX_ENTITY(BGP_NEXT_HOP_IPV4_ADDRESS, 18, 4, bgpNextHopIPv4Address) IPFIX_ENTITY(POST_MCAST_PACKET_DELTA_COUNT, 19, 8, postMCastPacketDeltaCount) IPFIX_ENTITY(POST_MCAST_OCTET_DELTA_COUNT, 20, 8, postMCastOctetDeltaCount) IPFIX_ENTITY(FLOW_END_SYS_UP_TIME, 21, 4, flowEndSysUpTime) IPFIX_ENTITY(FLOW_START_SYS_UP_TIME, 22, 4, flowStartSysUpTime) IPFIX_ENTITY(POST_OCTET_DELTA_COUNT, 23, 8, postOctetDeltaCount) IPFIX_ENTITY(POST_PACKET_DELTA_COUNT, 24, 8, postPacketDeltaCount) IPFIX_ENTITY(MINIMUM_IP_TOTAL_LENGTH, 25, 8, minimumIpTotalLength) IPFIX_ENTITY(MAXIMUM_IP_TOTAL_LENGTH, 26, 8, maximumIpTotalLength) IPFIX_ENTITY(SOURCE_IPV6_ADDRESS, 27, 16, sourceIPv6Address) IPFIX_ENTITY(DESTINATION_IPV6_ADDRESS, 28, 16, destinationIPv6Address) IPFIX_ENTITY(SOURCE_IPV6_PREFIX_LENGTH, 29, 1, sourceIPv6PrefixLength) IPFIX_ENTITY(DESTINATION_IPV6_PREFIX_LENGTH, 30, 1, destinationIPv6PrefixLength) IPFIX_ENTITY(FLOW_LABEL_IPV6, 31, 4, flowLabelIPv6) IPFIX_ENTITY(ICMP_TYPE_CODE_IPV4, 32, 2, icmpTypeCodeIPv4) IPFIX_ENTITY(IGMP_TYPE, 33, 1, igmpType) IPFIX_ENTITY(FLOW_ACTIVE_TIMEOUT, 36, 2, flowActiveTimeout) IPFIX_ENTITY(FLOW_IDLE_TIMEOUT, 37, 2, flowIdleTimeout) IPFIX_ENTITY(EXPORTED_OCTET_TOTAL_COUNT, 40, 8, exportedOctetTotalCount) IPFIX_ENTITY(EXPORTED_MESSAGE_TOTAL_COUNT, 41, 8, exportedMessageTotalCount) IPFIX_ENTITY(EXPORTED_FLOW_RECORD_TOTAL_COUNT, 42, 8, exportedFlowRecordTotalCount) IPFIX_ENTITY(SOURCE_IPV4_PREFIX, 44, 4, sourceIPv4Prefix) IPFIX_ENTITY(DESTINATION_IPV4_PREFIX, 45, 4, destinationIPv4Prefix) IPFIX_ENTITY(MPLS_TOP_LABEL_TYPE, 46, 1, mplsTopLabelType) IPFIX_ENTITY(MPLS_TOP_LABEL_IPV4_ADDRESS, 47, 4, mplsTopLabelIPv4Address) IPFIX_ENTITY(MINIMUM_TTL, 52, 1, minimumTTL) IPFIX_ENTITY(MAXIMUM_TTL, 53, 1, maximumTTL) IPFIX_ENTITY(FRAGMENT_IDENTIFICATION, 54, 4, fragmentIdentification) IPFIX_ENTITY(POST_IP_CLASS_OF_SERVICE, 55, 1, postIpClassOfService) IPFIX_ENTITY(SOURCE_MAC_ADDRESS, 56, 6, sourceMacAddress) IPFIX_ENTITY(POST_DESTINATION_MAC_ADDRESS, 57, 6, postDestinationMacAddress) IPFIX_ENTITY(VLAN_ID, 58, 2, vlanId) IPFIX_ENTITY(POST_VLAN_ID, 59, 2, postVlanId) IPFIX_ENTITY(IP_VERSION, 60, 1, ipVersion) IPFIX_ENTITY(FLOW_DIRECTION, 61, 1, flowDirection) IPFIX_ENTITY(IP_NEXT_HOP_IPV6_ADDRESS, 62, 16, ipNextHopIPv6Address) IPFIX_ENTITY(BGP_NEXT_HOP_IPV6_ADDRESS, 63, 16, bgpNextHopIPv6Address) IPFIX_ENTITY(IPV6_EXTENSION_HEADERS, 64, 4, ipv6ExtensionHeaders) IPFIX_ENTITY(MPLS_TOP_LABEL_STACK_SECTION, 70, 0, mplsTopLabelStackSection) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION2, 71, 0, mplsLabelStackSection2) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION3, 72, 0, mplsLabelStackSection3) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION4, 73, 0, mplsLabelStackSection4) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION5, 74, 0, mplsLabelStackSection5) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION6, 75, 0, mplsLabelStackSection6) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION7, 76, 0, mplsLabelStackSection7) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION8, 77, 0, mplsLabelStackSection8) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION9, 78, 0, mplsLabelStackSection9) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION10, 79, 0, mplsLabelStackSection10) IPFIX_ENTITY(DESTINATION_MAC_ADDRESS, 80, 6, destinationMacAddress) IPFIX_ENTITY(POST_SOURCE_MAC_ADDRESS, 81, 6, postSourceMacAddress) IPFIX_ENTITY(INTERFACE_NAME, 82, 0, interfaceName) IPFIX_ENTITY(INTERFACE_DESCRIPTION, 83, 0, interfaceDescription) IPFIX_ENTITY(OCTET_TOTAL_COUNT, 85, 8, octetTotalCount) IPFIX_ENTITY(PACKET_TOTAL_COUNT, 86, 8, packetTotalCount) IPFIX_ENTITY(FRAGMENT_OFFSET, 88, 2, fragmentOffset) IPFIX_ENTITY(MPLS_VPN_ROUTE_DISTINGUISHER, 90, 0, mplsVpnRouteDistinguisher) IPFIX_ENTITY(MPLS_TOP_LABEL_PREFIX_LENGTH, 91, 1, mplsTopLabelPrefixLength) IPFIX_ENTITY(APPLICATION_DESCRIPTION, 94, 0, applicationDescription) IPFIX_ENTITY(APPLICATION_ID, 95, 0, applicationId) IPFIX_ENTITY(APPLICATION_NAME, 96, 0, applicationName) IPFIX_ENTITY(POST_IP_DIFF_SERV_CODE_POINT, 98, 1, postIpDiffServCodePoint) IPFIX_ENTITY(MULTICAST_REPLICATION_FACTOR, 99, 4, multicastReplicationFactor) IPFIX_ENTITY(CLASSIFICATION_ENGINE_ID, 101, 1, classificationEngineId) IPFIX_ENTITY(BGP_NEXT_ADJACENT_AS_NUMBER, 128, 4, bgpNextAdjacentAsNumber) IPFIX_ENTITY(BGP_PREV_ADJACENT_AS_NUMBER, 129, 4, bgpPrevAdjacentAsNumber) IPFIX_ENTITY(EXPORTER_IPV4_ADDRESS, 130, 4, exporterIPv4Address) IPFIX_ENTITY(EXPORTER_IPV6_ADDRESS, 131, 16, exporterIPv6Address) IPFIX_ENTITY(DROPPED_OCTET_DELTA_COUNT, 132, 8, droppedOctetDeltaCount) IPFIX_ENTITY(DROPPED_PACKET_DELTA_COUNT, 133, 8, droppedPacketDeltaCount) IPFIX_ENTITY(DROPPED_OCTET_TOTAL_COUNT, 134, 8, droppedOctetTotalCount) IPFIX_ENTITY(DROPPED_PACKET_TOTAL_COUNT, 135, 8, droppedPacketTotalCount) IPFIX_ENTITY(FLOW_END_REASON, 136, 1, flowEndReason) IPFIX_ENTITY(COMMON_PROPERTIES_ID, 137, 8, commonPropertiesId) IPFIX_ENTITY(OBSERVATION_POINT_ID, 138, 4, observationPointId) IPFIX_ENTITY(ICMP_TYPE_CODE_IPV6, 139, 2, icmpTypeCodeIPv6) IPFIX_ENTITY(MPLS_TOP_LABEL_IPV6_ADDRESS, 140, 16, mplsTopLabelIPv6Address) IPFIX_ENTITY(LINE_CARD_ID, 141, 4, lineCardId) IPFIX_ENTITY(PORT_ID, 142, 4, portId) IPFIX_ENTITY(METERING_PROCESS_ID, 143, 4, meteringProcessId) IPFIX_ENTITY(EXPORTING_PROCESS_ID, 144, 4, exportingProcessId) IPFIX_ENTITY(TEMPLATE_ID, 145, 2, templateId) IPFIX_ENTITY(WLAN_CHANNEL_ID, 146, 1, wlanChannelId) IPFIX_ENTITY(WLAN_SSID, 147, 0, wlanSSID) IPFIX_ENTITY(FLOW_ID, 148, 8, flowId) IPFIX_ENTITY(OBSERVATION_DOMAIN_ID, 149, 4, observationDomainId) IPFIX_ENTITY(FLOW_START_SECONDS, 150, 4, flowStartSeconds) IPFIX_ENTITY(FLOW_END_SECONDS, 151, 4, flowEndSeconds) IPFIX_ENTITY(FLOW_START_MILLISECONDS, 152, 8, flowStartMilliseconds) IPFIX_ENTITY(FLOW_END_MILLISECONDS, 153, 8, flowEndMilliseconds) IPFIX_ENTITY(FLOW_START_MICROSECONDS, 154, 8, flowStartMicroseconds) IPFIX_ENTITY(FLOW_END_MICROSECONDS, 155, 8, flowEndMicroseconds) IPFIX_ENTITY(FLOW_START_NANOSECONDS, 156, 8, flowStartNanoseconds) IPFIX_ENTITY(FLOW_END_NANOSECONDS, 157, 8, flowEndNanoseconds) IPFIX_ENTITY(FLOW_START_DELTA_MICROSECONDS, 158, 4, flowStartDeltaMicroseconds) IPFIX_ENTITY(FLOW_END_DELTA_MICROSECONDS, 159, 4, flowEndDeltaMicroseconds) IPFIX_ENTITY(SYSTEM_INIT_TIME_MILLISECONDS, 160, 8, systemInitTimeMilliseconds) IPFIX_ENTITY(FLOW_DURATION_MILLISECONDS, 161, 4, flowDurationMilliseconds) IPFIX_ENTITY(FLOW_DURATION_MICROSECONDS, 162, 4, flowDurationMicroseconds) IPFIX_ENTITY(OBSERVED_FLOW_TOTAL_COUNT, 163, 8, observedFlowTotalCount) IPFIX_ENTITY(IGNORED_PACKET_TOTAL_COUNT, 164, 8, ignoredPacketTotalCount) IPFIX_ENTITY(IGNORED_OCTET_TOTAL_COUNT, 165, 8, ignoredOctetTotalCount) IPFIX_ENTITY(NOT_SENT_FLOW_TOTAL_COUNT, 166, 8, notSentFlowTotalCount) IPFIX_ENTITY(NOT_SENT_PACKET_TOTAL_COUNT, 167, 8, notSentPacketTotalCount) IPFIX_ENTITY(NOT_SENT_OCTET_TOTAL_COUNT, 168, 8, notSentOctetTotalCount) IPFIX_ENTITY(DESTINATION_IPV6_PREFIX, 169, 16, destinationIPv6Prefix) IPFIX_ENTITY(SOURCE_IPV6_PREFIX, 170, 16, sourceIPv6Prefix) IPFIX_ENTITY(POST_OCTET_TOTAL_COUNT, 171, 8, postOctetTotalCount) IPFIX_ENTITY(POST_PACKET_TOTAL_COUNT, 172, 8, postPacketTotalCount) IPFIX_ENTITY(FLOW_KEY_INDICATOR, 173, 8, flowKeyIndicator) IPFIX_ENTITY(POST_MCAST_PACKET_TOTAL_COUNT, 174, 8, postMCastPacketTotalCount) IPFIX_ENTITY(POST_MCAST_OCTET_TOTAL_COUNT, 175, 8, postMCastOctetTotalCount) IPFIX_ENTITY(ICMP_TYPE_IPV4, 176, 1, icmpTypeIPv4) IPFIX_ENTITY(ICMP_CODE_IPV4, 177, 1, icmpCodeIPv4) IPFIX_ENTITY(ICMP_TYPE_IPV6, 178, 1, icmpTypeIPv6) IPFIX_ENTITY(ICMP_CODE_IPV6, 179, 1, icmpCodeIPv6) IPFIX_ENTITY(UDP_SOURCE_PORT, 180, 2, udpSourcePort) IPFIX_ENTITY(UDP_DESTINATION_PORT, 181, 2, udpDestinationPort) IPFIX_ENTITY(TCP_SOURCE_PORT, 182, 2, tcpSourcePort) IPFIX_ENTITY(TCP_DESTINATION_PORT, 183, 2, tcpDestinationPort) IPFIX_ENTITY(TCP_SEQUENCE_NUMBER, 184, 4, tcpSequenceNumber) IPFIX_ENTITY(TCP_ACKNOWLEDGEMENT_NUMBER, 185, 4, tcpAcknowledgementNumber) IPFIX_ENTITY(TCP_WINDOW_SIZE, 186, 2, tcpWindowSize) IPFIX_ENTITY(TCP_URGENT_POINTER, 187, 2, tcpUrgentPointer) IPFIX_ENTITY(TCP_HEADER_LENGTH, 188, 1, tcpHeaderLength) IPFIX_ENTITY(IP_HEADER_LENGTH, 189, 1, ipHeaderLength) IPFIX_ENTITY(TOTAL_LENGTH_IPV4, 190, 2, totalLengthIPv4) IPFIX_ENTITY(PAYLOAD_LENGTH_IPV6, 191, 2, payloadLengthIPv6) IPFIX_ENTITY(IP_TTL, 192, 1, ipTTL) IPFIX_ENTITY(NEXT_HEADER_IPV6, 193, 1, nextHeaderIPv6) IPFIX_ENTITY(MPLS_PAYLOAD_LENGTH, 194, 4, mplsPayloadLength) IPFIX_ENTITY(IP_DIFF_SERV_CODE_POINT, 195, 1, ipDiffServCodePoint) IPFIX_ENTITY(IP_PRECEDENCE, 196, 1, ipPrecedence) IPFIX_ENTITY(FRAGMENT_FLAGS, 197, 1, fragmentFlags) IPFIX_ENTITY(OCTET_DELTA_SUM_OF_SQUARES, 198, 8, octetDeltaSumOfSquares) IPFIX_ENTITY(OCTET_TOTAL_SUM_OF_SQUARES, 199, 8, octetTotalSumOfSquares) IPFIX_ENTITY(MPLS_TOP_LABEL_TTL, 200, 1, mplsTopLabelTTL) IPFIX_ENTITY(MPLS_LABEL_STACK_LENGTH, 201, 4, mplsLabelStackLength) IPFIX_ENTITY(MPLS_LABEL_STACK_DEPTH, 202, 4, mplsLabelStackDepth) IPFIX_ENTITY(MPLS_TOP_LABEL_EXP, 203, 1, mplsTopLabelExp) IPFIX_ENTITY(IP_PAYLOAD_LENGTH, 204, 4, ipPayloadLength) IPFIX_ENTITY(UDP_MESSAGE_LENGTH, 205, 2, udpMessageLength) IPFIX_ENTITY(IS_MULTICAST, 206, 1, isMulticast) IPFIX_ENTITY(IPV4_IHL, 207, 1, ipv4IHL) IPFIX_ENTITY(IPV4_OPTIONS, 208, 4, ipv4Options) IPFIX_ENTITY(TCP_OPTIONS, 209, 8, tcpOptions) IPFIX_ENTITY(PADDING_OCTETS, 210, 0, paddingOctets) IPFIX_ENTITY(COLLECTOR_IPV4_ADDRESS, 211, 4, collectorIPv4Address) IPFIX_ENTITY(COLLECTOR_IPV6_ADDRESS, 212, 16, collectorIPv6Address) IPFIX_ENTITY(EXPORT_INTERFACE, 213, 4, exportInterface) IPFIX_ENTITY(EXPORT_PROTOCOL_VERSION, 214, 1, exportProtocolVersion) IPFIX_ENTITY(EXPORT_TRANSPORT_PROTOCOL, 215, 1, exportTransportProtocol) IPFIX_ENTITY(COLLECTOR_TRANSPORT_PORT, 216, 2, collectorTransportPort) IPFIX_ENTITY(EXPORTER_TRANSPORT_PORT, 217, 2, exporterTransportPort) IPFIX_ENTITY(TCP_SYN_TOTAL_COUNT, 218, 8, tcpSynTotalCount) IPFIX_ENTITY(TCP_FIN_TOTAL_COUNT, 219, 8, tcpFinTotalCount) IPFIX_ENTITY(TCP_RST_TOTAL_COUNT, 220, 8, tcpRstTotalCount) IPFIX_ENTITY(TCP_PSH_TOTAL_COUNT, 221, 8, tcpPshTotalCount) IPFIX_ENTITY(TCP_ACK_TOTAL_COUNT, 222, 8, tcpAckTotalCount) IPFIX_ENTITY(TCP_URG_TOTAL_COUNT, 223, 8, tcpUrgTotalCount) IPFIX_ENTITY(IP_TOTAL_LENGTH, 224, 8, ipTotalLength) IPFIX_ENTITY(POST_NATSOURCE_IPV4_ADDRESS, 225, 4, postNATSourceIPv4Address) IPFIX_ENTITY(POST_NATDESTINATION_IPV4_ADDRESS, 226, 4, postNATDestinationIPv4Address) IPFIX_ENTITY(POST_NAPTSOURCE_TRANSPORT_PORT, 227, 2, postNAPTSourceTransportPort) IPFIX_ENTITY(POST_NAPTDESTINATION_TRANSPORT_PORT, 228, 2, postNAPTDestinationTransportPort) IPFIX_ENTITY(NAT_ORIGINATING_ADDRESS_REALM, 229, 1, natOriginatingAddressRealm) IPFIX_ENTITY(NAT_EVENT, 230, 1, natEvent) IPFIX_ENTITY(INITIATOR_OCTETS, 231, 8, initiatorOctets) IPFIX_ENTITY(RESPONDER_OCTETS, 232, 8, responderOctets) IPFIX_ENTITY(FIREWALL_EVENT, 233, 1, firewallEvent) IPFIX_ENTITY(INGRESS_VRFID, 234, 4, ingressVRFID) IPFIX_ENTITY(EGRESS_VRFID, 235, 4, egressVRFID) IPFIX_ENTITY(V_RFNAME, 236, 0, VRFname) IPFIX_ENTITY(POST_MPLS_TOP_LABEL_EXP, 237, 1, postMplsTopLabelExp) IPFIX_ENTITY(TCP_WINDOW_SCALE, 238, 2, tcpWindowScale) IPFIX_ENTITY(BIFLOW_DIRECTION, 239, 1, biflowDirection) IPFIX_ENTITY(ETHERNET_HEADER_LENGTH, 240, 1, ethernetHeaderLength) IPFIX_ENTITY(ETHERNET_PAYLOAD_LENGTH, 241, 2, ethernetPayloadLength) IPFIX_ENTITY(ETHERNET_TOTAL_LENGTH, 242, 2, ethernetTotalLength) IPFIX_ENTITY(DOT1Q_VLAN_ID, 243, 2, dot1qVlanId) IPFIX_ENTITY(DOT1Q_PRIORITY, 244, 1, dot1qPriority) IPFIX_ENTITY(DOT1Q_CUSTOMER_VLAN_ID, 245, 2, dot1qCustomerVlanId) IPFIX_ENTITY(DOT1Q_CUSTOMER_PRIORITY, 246, 1, dot1qCustomerPriority) IPFIX_ENTITY(METRO_EVC_ID, 247, 0, metroEvcId) IPFIX_ENTITY(METRO_EVC_TYPE, 248, 1, metroEvcType) IPFIX_ENTITY(PSEUDO_WIRE_ID, 249, 4, pseudoWireId) IPFIX_ENTITY(PSEUDO_WIRE_TYPE, 250, 2, pseudoWireType) IPFIX_ENTITY(PSEUDO_WIRE_CONTROL_WORD, 251, 4, pseudoWireControlWord) IPFIX_ENTITY(INGRESS_PHYSICAL_INTERFACE, 252, 4, ingressPhysicalInterface) IPFIX_ENTITY(EGRESS_PHYSICAL_INTERFACE, 253, 4, egressPhysicalInterface) IPFIX_ENTITY(POST_DOT1Q_VLAN_ID, 254, 2, postDot1qVlanId) IPFIX_ENTITY(POST_DOT1Q_CUSTOMER_VLAN_ID, 255, 2, postDot1qCustomerVlanId) IPFIX_ENTITY(ETHERNET_TYPE, 256, 2, ethernetType) IPFIX_ENTITY(POST_IP_PRECEDENCE, 257, 1, postIpPrecedence) IPFIX_ENTITY(COLLECTION_TIME_MILLISECONDS, 258, 8, collectionTimeMilliseconds) IPFIX_ENTITY(EXPORT_SCTP_STREAM_ID, 259, 2, exportSctpStreamId) IPFIX_ENTITY(MAX_EXPORT_SECONDS, 260, 4, maxExportSeconds) IPFIX_ENTITY(MAX_FLOW_END_SECONDS, 261, 4, maxFlowEndSeconds) IPFIX_ENTITY(MESSAGE_MD5_CHECKSUM, 262, 0, messageMD5Checksum) IPFIX_ENTITY(MESSAGE_SCOPE, 263, 1, messageScope) IPFIX_ENTITY(MIN_EXPORT_SECONDS, 264, 4, minExportSeconds) IPFIX_ENTITY(MIN_FLOW_START_SECONDS, 265, 4, minFlowStartSeconds) IPFIX_ENTITY(OPAQUE_OCTETS, 266, 0, opaqueOctets) IPFIX_ENTITY(SESSION_SCOPE, 267, 1, sessionScope) IPFIX_ENTITY(MAX_FLOW_END_MICROSECONDS, 268, 8, maxFlowEndMicroseconds) IPFIX_ENTITY(MAX_FLOW_END_MILLISECONDS, 269, 8, maxFlowEndMilliseconds) IPFIX_ENTITY(MAX_FLOW_END_NANOSECONDS, 270, 8, maxFlowEndNanoseconds) IPFIX_ENTITY(MIN_FLOW_START_MICROSECONDS, 271, 8, minFlowStartMicroseconds) IPFIX_ENTITY(MIN_FLOW_START_MILLISECONDS, 272, 8, minFlowStartMilliseconds) IPFIX_ENTITY(MIN_FLOW_START_NANOSECONDS, 273, 8, minFlowStartNanoseconds) IPFIX_ENTITY(COLLECTOR_CERTIFICATE, 274, 0, collectorCertificate) IPFIX_ENTITY(EXPORTER_CERTIFICATE, 275, 0, exporterCertificate) IPFIX_ENTITY(DATA_RECORDS_RELIABILITY, 276, 1, dataRecordsReliability) IPFIX_ENTITY(OBSERVATION_POINT_TYPE, 277, 1, observationPointType) IPFIX_ENTITY(CONNECTION_COUNT_NEW, 278, 4, connectionCountNew) IPFIX_ENTITY(CONNECTION_SUM_DURATION, 279, 8, connectionSumDuration) IPFIX_ENTITY(CONNECTION_TRANSACTION_ID, 280, 8, connectionTransactionId) IPFIX_ENTITY(POST_NATSOURCE_IPV6_ADDRESS, 281, 16, postNATSourceIPv6Address) IPFIX_ENTITY(POST_NATDESTINATION_IPV6_ADDRESS, 282, 16, postNATDestinationIPv6Address) IPFIX_ENTITY(NAT_POOL_ID, 283, 4, natPoolId) IPFIX_ENTITY(NAT_POOL_NAME, 284, 0, natPoolName) IPFIX_ENTITY(ANONYMIZATION_FLAGS, 285, 2, anonymizationFlags) IPFIX_ENTITY(ANONYMIZATION_TECHNIQUE, 286, 2, anonymizationTechnique) IPFIX_ENTITY(INFORMATION_ELEMENT_INDEX, 287, 2, informationElementIndex) IPFIX_ENTITY(P2P_TECHNOLOGY, 288, 0, p2pTechnology) IPFIX_ENTITY(TUNNEL_TECHNOLOGY, 289, 0, tunnelTechnology) IPFIX_ENTITY(ENCRYPTED_TECHNOLOGY, 290, 0, encryptedTechnology) IPFIX_ENTITY(BASIC_LIST, 291, 0, basicList) IPFIX_ENTITY(SUB_TEMPLATE_LIST, 292, 0, subTemplateList) IPFIX_ENTITY(SUB_TEMPLATE_MULTI_LIST, 293, 0, subTemplateMultiList) IPFIX_ENTITY(BGP_VALIDITY_STATE, 294, 1, bgpValidityState) IPFIX_ENTITY(I_PSEC_SPI, 295, 4, IPSecSPI) IPFIX_ENTITY(GRE_KEY, 296, 4, greKey) IPFIX_ENTITY(NAT_TYPE, 297, 1, natType) IPFIX_ENTITY(INITIATOR_PACKETS, 298, 8, initiatorPackets) IPFIX_ENTITY(RESPONDER_PACKETS, 299, 8, responderPackets) IPFIX_ENTITY(OBSERVATION_DOMAIN_NAME, 300, 0, observationDomainName) IPFIX_ENTITY(SELECTION_SEQUENCE_ID, 301, 8, selectionSequenceId) IPFIX_ENTITY(SELECTOR_ID, 302, 8, selectorId) IPFIX_ENTITY(INFORMATION_ELEMENT_ID, 303, 2, informationElementId) IPFIX_ENTITY(SELECTOR_ALGORITHM, 304, 2, selectorAlgorithm) IPFIX_ENTITY(SAMPLING_PACKET_INTERVAL, 305, 4, samplingPacketInterval) IPFIX_ENTITY(SAMPLING_PACKET_SPACE, 306, 4, samplingPacketSpace) IPFIX_ENTITY(SAMPLING_TIME_INTERVAL, 307, 4, samplingTimeInterval) IPFIX_ENTITY(SAMPLING_TIME_SPACE, 308, 4, samplingTimeSpace) IPFIX_ENTITY(SAMPLING_SIZE, 309, 4, samplingSize) IPFIX_ENTITY(SAMPLING_POPULATION, 310, 4, samplingPopulation) IPFIX_ENTITY(SAMPLING_PROBABILITY, 311, 8, samplingProbability) IPFIX_ENTITY(DATA_LINK_FRAME_SIZE, 312, 2, dataLinkFrameSize) IPFIX_ENTITY(IP_HEADER_PACKET_SECTION, 313, 0, ipHeaderPacketSection) IPFIX_ENTITY(IP_PAYLOAD_PACKET_SECTION, 314, 0, ipPayloadPacketSection) IPFIX_ENTITY(DATA_LINK_FRAME_SECTION, 315, 0, dataLinkFrameSection) IPFIX_ENTITY(MPLS_LABEL_STACK_SECTION, 316, 0, mplsLabelStackSection) IPFIX_ENTITY(MPLS_PAYLOAD_PACKET_SECTION, 317, 0, mplsPayloadPacketSection) IPFIX_ENTITY(SELECTOR_ID_TOTAL_PKTS_OBSERVED, 318, 8, selectorIdTotalPktsObserved) IPFIX_ENTITY(SELECTOR_ID_TOTAL_PKTS_SELECTED, 319, 8, selectorIdTotalPktsSelected) IPFIX_ENTITY(ABSOLUTE_ERROR, 320, 8, absoluteError) IPFIX_ENTITY(RELATIVE_ERROR, 321, 8, relativeError) IPFIX_ENTITY(OBSERVATION_TIME_SECONDS, 322, 4, observationTimeSeconds) IPFIX_ENTITY(OBSERVATION_TIME_MILLISECONDS, 323, 8, observationTimeMilliseconds) IPFIX_ENTITY(OBSERVATION_TIME_MICROSECONDS, 324, 8, observationTimeMicroseconds) IPFIX_ENTITY(OBSERVATION_TIME_NANOSECONDS, 325, 8, observationTimeNanoseconds) IPFIX_ENTITY(DIGEST_HASH_VALUE, 326, 8, digestHashValue) IPFIX_ENTITY(HASH_IPPAYLOAD_OFFSET, 327, 8, hashIPPayloadOffset) IPFIX_ENTITY(HASH_IPPAYLOAD_SIZE, 328, 8, hashIPPayloadSize) IPFIX_ENTITY(HASH_OUTPUT_RANGE_MIN, 329, 8, hashOutputRangeMin) IPFIX_ENTITY(HASH_OUTPUT_RANGE_MAX, 330, 8, hashOutputRangeMax) IPFIX_ENTITY(HASH_SELECTED_RANGE_MIN, 331, 8, hashSelectedRangeMin) IPFIX_ENTITY(HASH_SELECTED_RANGE_MAX, 332, 8, hashSelectedRangeMax) IPFIX_ENTITY(HASH_DIGEST_OUTPUT, 333, 1, hashDigestOutput) IPFIX_ENTITY(HASH_INITIALISER_VALUE, 334, 8, hashInitialiserValue) IPFIX_ENTITY(SELECTOR_NAME, 335, 0, selectorName) IPFIX_ENTITY(UPPER_CILIMIT, 336, 8, upperCILimit) IPFIX_ENTITY(LOWER_CILIMIT, 337, 8, lowerCILimit) IPFIX_ENTITY(CONFIDENCE_LEVEL, 338, 8, confidenceLevel) IPFIX_ENTITY(INFORMATION_ELEMENT_DATA_TYPE, 339, 1, informationElementDataType) IPFIX_ENTITY(INFORMATION_ELEMENT_DESCRIPTION, 340, 0, informationElementDescription) IPFIX_ENTITY(INFORMATION_ELEMENT_NAME, 341, 0, informationElementName) IPFIX_ENTITY(INFORMATION_ELEMENT_RANGE_BEGIN, 342, 8, informationElementRangeBegin) IPFIX_ENTITY(INFORMATION_ELEMENT_RANGE_END, 343, 8, informationElementRangeEnd) IPFIX_ENTITY(INFORMATION_ELEMENT_SEMANTICS, 344, 1, informationElementSemantics) IPFIX_ENTITY(INFORMATION_ELEMENT_UNITS, 345, 2, informationElementUnits) IPFIX_ENTITY(PRIVATE_ENTERPRISE_NUMBER, 346, 4, privateEnterpriseNumber) IPFIX_ENTITY(VIRTUAL_STATION_INTERFACE_ID, 347, 0, virtualStationInterfaceId) IPFIX_ENTITY(VIRTUAL_STATION_INTERFACE_NAME, 348, 0, virtualStationInterfaceName) IPFIX_ENTITY(VIRTUAL_STATION_UUID, 349, 0, virtualStationUUID) IPFIX_ENTITY(VIRTUAL_STATION_NAME, 350, 0, virtualStationName) IPFIX_ENTITY(LAYER2_SEGMENT_ID, 351, 8, layer2SegmentId) IPFIX_ENTITY(LAYER2_OCTET_DELTA_COUNT, 352, 8, layer2OctetDeltaCount) IPFIX_ENTITY(LAYER2_OCTET_TOTAL_COUNT, 353, 8, layer2OctetTotalCount) IPFIX_ENTITY(INGRESS_UNICAST_PACKET_TOTAL_COUNT, 354, 8, ingressUnicastPacketTotalCount) IPFIX_ENTITY(INGRESS_MULTICAST_PACKET_TOTAL_COUNT, 355, 8, ingressMulticastPacketTotalCount) IPFIX_ENTITY(INGRESS_BROADCAST_PACKET_TOTAL_COUNT, 356, 8, ingressBroadcastPacketTotalCount) IPFIX_ENTITY(EGRESS_UNICAST_PACKET_TOTAL_COUNT, 357, 8, egressUnicastPacketTotalCount) IPFIX_ENTITY(EGRESS_BROADCAST_PACKET_TOTAL_COUNT, 358, 8, egressBroadcastPacketTotalCount) IPFIX_ENTITY(MONITORING_INTERVAL_START_MILLI_SECONDS, 359, 8, monitoringIntervalStartMilliSeconds) IPFIX_ENTITY(MONITORING_INTERVAL_END_MILLI_SECONDS, 360, 8, monitoringIntervalEndMilliSeconds) IPFIX_ENTITY(PORT_RANGE_START, 361, 2, portRangeStart) IPFIX_ENTITY(PORT_RANGE_END, 362, 2, portRangeEnd) IPFIX_ENTITY(PORT_RANGE_STEP_SIZE, 363, 2, portRangeStepSize) IPFIX_ENTITY(PORT_RANGE_NUM_PORTS, 364, 2, portRangeNumPorts) IPFIX_ENTITY(STA_MAC_ADDRESS, 365, 6, staMacAddress) IPFIX_ENTITY(STA_IPV4_ADDRESS, 366, 4, staIPv4Address) IPFIX_ENTITY(WTP_MAC_ADDRESS, 367, 6, wtpMacAddress) IPFIX_ENTITY(INGRESS_INTERFACE_TYPE, 368, 4, ingressInterfaceType) IPFIX_ENTITY(EGRESS_INTERFACE_TYPE, 369, 4, egressInterfaceType) IPFIX_ENTITY(RTP_SEQUENCE_NUMBER, 370, 2, rtpSequenceNumber) IPFIX_ENTITY(USER_NAME, 371, 0, userName) IPFIX_ENTITY(APPLICATION_CATEGORY_NAME, 372, 0, applicationCategoryName) IPFIX_ENTITY(APPLICATION_SUB_CATEGORY_NAME, 373, 0, applicationSubCategoryName) IPFIX_ENTITY(APPLICATION_GROUP_NAME, 374, 0, applicationGroupName) IPFIX_ENTITY(ORIGINAL_FLOWS_PRESENT, 375, 8, originalFlowsPresent) IPFIX_ENTITY(ORIGINAL_FLOWS_INITIATED, 376, 8, originalFlowsInitiated) IPFIX_ENTITY(ORIGINAL_FLOWS_COMPLETED, 377, 8, originalFlowsCompleted) IPFIX_ENTITY(DISTINCT_COUNT_OF_SOURCE_IPADDRESS, 378, 8, distinctCountOfSourceIPAddress) IPFIX_ENTITY(DISTINCT_COUNT_OF_DESTINATION_IPADDRESS, 379, 8, distinctCountOfDestinationIPAddress) IPFIX_ENTITY(DISTINCT_COUNT_OF_SOURCE_IPV4_ADDRESS, 380, 4, distinctCountOfSourceIPv4Address) IPFIX_ENTITY(DISTINCT_COUNT_OF_DESTINATION_IPV4_ADDRESS, 381, 4, distinctCountOfDestinationIPv4Address) IPFIX_ENTITY(DISTINCT_COUNT_OF_SOURCE_IPV6_ADDRESS, 382, 8, distinctCountOfSourceIPv6Address) IPFIX_ENTITY(DISTINCT_COUNT_OF_DESTINATION_IPV6_ADDRESS, 383, 8, distinctCountOfDestinationIPv6Address) IPFIX_ENTITY(VALUE_DISTRIBUTION_METHOD, 384, 1, valueDistributionMethod) IPFIX_ENTITY(RFC3550_JITTER_MEAN_MILLISECONDS, 385, 4, rfc3550JitterMeanMilliseconds) IPFIX_ENTITY(RFC3550_JITTER_MEAN_MICROSECONDS, 386, 4, rfc3550JitterMeanMicroseconds) IPFIX_ENTITY(RFC3550_JITTER_MEAN_NANOSECONDS, 387, 4, rfc3550JitterMeanNanoseconds) #undef IPFIX_ENTITY openvswitch-2.5.9/ofproto/PaxHeaders.82075/in-band.h0000644000000000000000000000013113534540071017014 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.025855226 openvswitch-2.5.9/ofproto/in-band.h0000644000175000017500000000253113534540071020504 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef IN_BAND_H #define IN_BAND_H 1 #include #include #include #include #include "flow.h" struct flow; struct in_band; struct nlattr; struct ofpbuf; struct ofproto; int in_band_create(struct ofproto *, const char *local_name, struct in_band **); void in_band_destroy(struct in_band *); void in_band_set_queue(struct in_band *, int queue_id); void in_band_set_remotes(struct in_band *, const struct sockaddr_in *, size_t n); bool in_band_run(struct in_band *); void in_band_wait(struct in_band *); bool in_band_must_output_to_local_port(const struct flow *); int in_band_count_rules(const struct in_band *); #endif /* in-band.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/in-band.c0000644000000000000000000000013113534540071017007 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.025855226 openvswitch-2.5.9/ofproto/in-band.c0000644000175000017500000004065313534540071020506 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "in-band.h" #include #include #include #include #include #include #include #include "classifier.h" #include "dhcp.h" #include "flow.h" #include "netdev.h" #include "netlink.h" #include "odp-util.h" #include "ofp-actions.h" #include "ofproto.h" #include "ofpbuf.h" #include "ofproto-provider.h" #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "timeval.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(in_band); /* Priorities used in classifier for in-band rules. These values are higher * than any that may be set with OpenFlow, and "18" kind of looks like "IB". * The ordering of priorities is not important because all of the rules set up * by in-band control have the same action. The only reason to use more than * one priority is to make the kind of flow easier to see during debugging. */ enum { /* One set per bridge. */ IBR_FROM_LOCAL_DHCP = 180000, /* (a) From local port, DHCP. */ IBR_TO_LOCAL_ARP, /* (b) To local port, ARP. */ IBR_FROM_LOCAL_ARP, /* (c) From local port, ARP. */ /* One set per unique next-hop MAC. */ IBR_TO_NEXT_HOP_ARP, /* (d) To remote MAC, ARP. */ IBR_FROM_NEXT_HOP_ARP, /* (e) From remote MAC, ARP. */ /* One set per unique remote IP address. */ IBR_TO_REMOTE_ARP, /* (f) To remote IP, ARP. */ IBR_FROM_REMOTE_ARP, /* (g) From remote IP, ARP. */ /* One set per unique remote (IP,port) pair. */ IBR_TO_REMOTE_TCP, /* (h) To remote IP, TCP port. */ IBR_FROM_REMOTE_TCP /* (i) From remote IP, TCP port. */ }; /* Track one remote IP and next hop information. */ struct in_band_remote { struct sockaddr_in remote_addr; /* IP address, in network byte order. */ struct eth_addr remote_mac; /* Next-hop MAC, all-zeros if unknown. */ struct eth_addr last_remote_mac; /* Previous nonzero next-hop MAC. */ struct netdev *remote_netdev; /* Device to send to next-hop MAC. */ }; /* What to do to an in_band_rule. */ enum in_band_op { ADD, /* Add the rule to ofproto's flow table. */ DEL /* Delete the rule from ofproto's flow table. */ }; /* A rule to add to or delete from ofproto's flow table. */ struct in_band_rule { struct hmap_node hmap_node; /* In struct in_band's "rules" hmap. */ struct match match; int priority; enum in_band_op op; }; struct in_band { struct ofproto *ofproto; int queue_id; /* Remote information. */ time_t next_remote_refresh; /* Refresh timer. */ struct in_band_remote *remotes; size_t n_remotes; /* Local information. */ time_t next_local_refresh; /* Refresh timer. */ struct eth_addr local_mac; /* Current MAC. */ struct netdev *local_netdev; /* Local port's network device. */ /* Flow tracking. */ struct hmap rules; /* Contains "struct in_band_rule"s. */ }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); static int refresh_remote(struct in_band *ib, struct in_band_remote *r) { struct in_addr next_hop_inaddr; char *next_hop_dev; int retval; /* Find the next-hop IP address. */ r->remote_mac = eth_addr_zero; retval = netdev_get_next_hop(ib->local_netdev, &r->remote_addr.sin_addr, &next_hop_inaddr, &next_hop_dev); if (retval) { VLOG_WARN("%s: cannot find route for controller ("IP_FMT"): %s", ib->ofproto->name, IP_ARGS(r->remote_addr.sin_addr.s_addr), ovs_strerror(retval)); return 1; } if (!next_hop_inaddr.s_addr) { next_hop_inaddr = r->remote_addr.sin_addr; } /* Open the next-hop network device. */ if (!r->remote_netdev || strcmp(netdev_get_name(r->remote_netdev), next_hop_dev)) { netdev_close(r->remote_netdev); retval = netdev_open(next_hop_dev, "system", &r->remote_netdev); if (retval) { VLOG_WARN_RL(&rl, "%s: cannot open netdev %s (next hop " "to controller "IP_FMT"): %s", ib->ofproto->name, next_hop_dev, IP_ARGS(r->remote_addr.sin_addr.s_addr), ovs_strerror(retval)); free(next_hop_dev); return 1; } } free(next_hop_dev); /* Look up the MAC address of the next-hop IP address. */ retval = netdev_arp_lookup(r->remote_netdev, next_hop_inaddr.s_addr, &r->remote_mac); if (retval) { VLOG_DBG_RL(&rl, "%s: cannot look up remote MAC address ("IP_FMT"): %s", ib->ofproto->name, IP_ARGS(next_hop_inaddr.s_addr), ovs_strerror(retval)); } /* If we don't have a MAC address, then refresh quickly, since we probably * will get a MAC address soon (via ARP). Otherwise, we can afford to wait * a little while. */ return eth_addr_is_zero(r->remote_mac) ? 1 : 10; } static bool refresh_remotes(struct in_band *ib) { struct in_band_remote *r; bool any_changes; if (time_now() < ib->next_remote_refresh) { return false; } any_changes = false; ib->next_remote_refresh = TIME_MAX; for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { struct eth_addr old_remote_mac; time_t next_refresh; /* Save old MAC. */ old_remote_mac = r->remote_mac; /* Refresh remote information. */ next_refresh = refresh_remote(ib, r) + time_now(); ib->next_remote_refresh = MIN(ib->next_remote_refresh, next_refresh); /* If the MAC changed, log the changes. */ if (!eth_addr_equals(r->remote_mac, old_remote_mac)) { any_changes = true; if (!eth_addr_is_zero(r->remote_mac) && !eth_addr_equals(r->last_remote_mac, r->remote_mac)) { VLOG_DBG("%s: remote MAC address changed from "ETH_ADDR_FMT " to "ETH_ADDR_FMT, ib->ofproto->name, ETH_ADDR_ARGS(r->last_remote_mac), ETH_ADDR_ARGS(r->remote_mac)); r->last_remote_mac = r->remote_mac; } } } return any_changes; } /* Refreshes the MAC address of the local port into ib->local_mac, if it is due * for a refresh. Returns true if anything changed, otherwise false. */ static bool refresh_local(struct in_band *ib) { struct eth_addr ea; time_t now; now = time_now(); if (now < ib->next_local_refresh) { return false; } ib->next_local_refresh = now + 1; if (netdev_get_etheraddr(ib->local_netdev, &ea) || eth_addr_equals(ea, ib->local_mac)) { return false; } ib->local_mac = ea; return true; } /* Returns true if packets in 'flow' should be directed to the local port. * (This keeps the flow table from preventing DHCP replies from being seen by * the local port.) */ bool in_band_must_output_to_local_port(const struct flow *flow) { return (flow->dl_type == htons(ETH_TYPE_IP) && flow->nw_proto == IPPROTO_UDP && flow->tp_src == htons(DHCP_SERVER_PORT) && flow->tp_dst == htons(DHCP_CLIENT_PORT)); } /* Returns the number of in-band rules currently installed in the flow * table. */ int in_band_count_rules(const struct in_band *ib) { return hmap_count(&ib->rules); } static void add_rule(struct in_band *ib, const struct match *match, int priority) { uint32_t hash = match_hash(match, 0); struct in_band_rule *rule; HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &ib->rules) { if (match_equal(&rule->match, match)) { rule->op = ADD; return; } } rule = xmalloc(sizeof *rule); rule->match = *match; rule->priority = priority; rule->op = ADD; hmap_insert(&ib->rules, &rule->hmap_node, hash); } static void update_rules(struct in_band *ib) { struct in_band_rule *ib_rule; struct in_band_remote *r; struct match match; /* Mark all the existing rules for deletion. (Afterward we will re-add any * rules that are still valid.) */ HMAP_FOR_EACH (ib_rule, hmap_node, &ib->rules) { ib_rule->op = DEL; } if (ib->n_remotes && !eth_addr_is_zero(ib->local_mac)) { /* (a) Allow DHCP requests sent from the local port. */ match_init_catchall(&match); match_set_in_port(&match, OFPP_LOCAL); match_set_dl_type(&match, htons(ETH_TYPE_IP)); match_set_dl_src(&match, ib->local_mac); match_set_nw_proto(&match, IPPROTO_UDP); match_set_tp_src(&match, htons(DHCP_CLIENT_PORT)); match_set_tp_dst(&match, htons(DHCP_SERVER_PORT)); add_rule(ib, &match, IBR_FROM_LOCAL_DHCP); /* (b) Allow ARP replies to the local port's MAC address. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_dl_dst(&match, ib->local_mac); match_set_nw_proto(&match, ARP_OP_REPLY); add_rule(ib, &match, IBR_TO_LOCAL_ARP); /* (c) Allow ARP requests from the local port's MAC address. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_dl_src(&match, ib->local_mac); match_set_nw_proto(&match, ARP_OP_REQUEST); add_rule(ib, &match, IBR_FROM_LOCAL_ARP); } for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { if (eth_addr_is_zero(r->remote_mac)) { continue; } /* (d) Allow ARP replies to the next hop's MAC address. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_dl_dst(&match, r->remote_mac); match_set_nw_proto(&match, ARP_OP_REPLY); add_rule(ib, &match, IBR_TO_NEXT_HOP_ARP); /* (e) Allow ARP requests from the next hop's MAC address. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_dl_src(&match, r->remote_mac); match_set_nw_proto(&match, ARP_OP_REQUEST); add_rule(ib, &match, IBR_FROM_NEXT_HOP_ARP); } for (r = ib->remotes; r < &ib->remotes[ib->n_remotes]; r++) { const struct sockaddr_in *a = &r->remote_addr; /* (f) Allow ARP replies containing the remote's IP address as a * target. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_nw_proto(&match, ARP_OP_REPLY); match_set_nw_dst(&match, a->sin_addr.s_addr); add_rule(ib, &match, IBR_TO_REMOTE_ARP); /* (g) Allow ARP requests containing the remote's IP address as a * source. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_ARP)); match_set_nw_proto(&match, ARP_OP_REQUEST); match_set_nw_src(&match, a->sin_addr.s_addr); add_rule(ib, &match, IBR_FROM_REMOTE_ARP); /* (h) Allow TCP traffic to the remote's IP and port. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_IP)); match_set_nw_proto(&match, IPPROTO_TCP); match_set_nw_dst(&match, a->sin_addr.s_addr); match_set_tp_dst(&match, a->sin_port); add_rule(ib, &match, IBR_TO_REMOTE_TCP); /* (i) Allow TCP traffic from the remote's IP and port. */ match_init_catchall(&match); match_set_dl_type(&match, htons(ETH_TYPE_IP)); match_set_nw_proto(&match, IPPROTO_TCP); match_set_nw_src(&match, a->sin_addr.s_addr); match_set_tp_src(&match, a->sin_port); add_rule(ib, &match, IBR_FROM_REMOTE_TCP); } } /* Updates the OpenFlow flow table for the current state of in-band control. * Returns true ordinarily. Returns false if no remotes are configured on 'ib' * and 'ib' doesn't have any rules left to remove from the OpenFlow flow * table. Thus, a false return value means that the caller can destroy 'ib' * without leaving extra flows hanging around in the flow table. */ bool in_band_run(struct in_band *ib) { uint64_t ofpacts_stub[128 / 8]; struct ofpbuf ofpacts; struct in_band_rule *rule, *next; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); if (ib->queue_id >= 0) { ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id; } ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; refresh_local(ib); refresh_remotes(ib); update_rules(ib); HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) { switch (rule->op) { case ADD: ofproto_add_flow(ib->ofproto, &rule->match, rule->priority, ofpacts.data, ofpacts.size); break; case DEL: ofproto_delete_flow(ib->ofproto, &rule->match, rule->priority); hmap_remove(&ib->rules, &rule->hmap_node); free(rule); break; } } ofpbuf_uninit(&ofpacts); return ib->n_remotes || !hmap_is_empty(&ib->rules); } void in_band_wait(struct in_band *in_band) { long long int wakeup = MIN(in_band->next_remote_refresh, in_band->next_local_refresh); poll_timer_wait_until(wakeup * 1000); } int in_band_create(struct ofproto *ofproto, const char *local_name, struct in_band **in_bandp) { struct in_band *in_band; struct netdev *local_netdev; int error; *in_bandp = NULL; error = netdev_open(local_name, "internal", &local_netdev); if (error) { VLOG_ERR("%s: failed to initialize in-band control: cannot open " "datapath local port %s (%s)", ofproto->name, local_name, ovs_strerror(error)); return error; } in_band = xzalloc(sizeof *in_band); in_band->ofproto = ofproto; in_band->queue_id = -1; in_band->next_remote_refresh = TIME_MIN; in_band->next_local_refresh = TIME_MIN; in_band->local_netdev = local_netdev; hmap_init(&in_band->rules); *in_bandp = in_band; return 0; } void in_band_destroy(struct in_band *ib) { if (ib) { struct in_band_rule *rule, *next; HMAP_FOR_EACH_SAFE (rule, next, hmap_node, &ib->rules) { hmap_remove(&ib->rules, &rule->hmap_node); free(rule); } hmap_destroy(&ib->rules); in_band_set_remotes(ib, NULL, 0); netdev_close(ib->local_netdev); free(ib); } } static bool any_addresses_changed(struct in_band *ib, const struct sockaddr_in *addresses, size_t n) { size_t i; if (n != ib->n_remotes) { return true; } for (i = 0; i < n; i++) { const struct sockaddr_in *old = &ib->remotes[i].remote_addr; const struct sockaddr_in *new = &addresses[i]; if (old->sin_addr.s_addr != new->sin_addr.s_addr || old->sin_port != new->sin_port) { return true; } } return false; } void in_band_set_remotes(struct in_band *ib, const struct sockaddr_in *addresses, size_t n) { size_t i; if (!any_addresses_changed(ib, addresses, n)) { return; } /* Clear old remotes. */ for (i = 0; i < ib->n_remotes; i++) { netdev_close(ib->remotes[i].remote_netdev); } free(ib->remotes); /* Set up new remotes. */ ib->remotes = n ? xzalloc(n * sizeof *ib->remotes) : NULL; ib->n_remotes = n; for (i = 0; i < n; i++) { ib->remotes[i].remote_addr = addresses[i]; } /* Force refresh in next call to in_band_run(). */ ib->next_remote_refresh = TIME_MIN; } /* Sets the OpenFlow queue used by flows set up by 'ib' to 'queue_id'. If * 'queue_id' is negative, 'ib' will not set any queue (which is also the * default). */ void in_band_set_queue(struct in_band *ib, int queue_id) { ib->queue_id = queue_id; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-tnl-unixctl.man0000644000000000000000000000012713534540071022004 xustar0029 mtime=1567801401.65368302 28 atime=1567801402.1136864 30 ctime=1567801423.753845848 openvswitch-2.5.9/ofproto/ofproto-tnl-unixctl.man0000644000175000017500000000225513534540071023472 0ustar00jpettitjpettit00000000000000.SS "OPENVSWITCH TUNNELING COMMANDS" These commands query and modify OVS tunnel components. Ref to README-native-tunneling.md for more info. . .IP "\fBovs/route/add ipv4_address/plen output_bridge [GW]\fR" Adds ipv4_address/plen route to vswitchd routing table. output_bridge needs to be OVS bridge name. This command is useful if OVS cached routes does not look right. . .IP "\fBovs/route/show\fR" Print all routes in OVS routing table, This includes routes cached from system routing table and user configured routes. . .IP "\fBovs/route/del ipv4_address/plen\fR" Delete ipv4_address/plen route from OVS routing table. . .IP "\fBtnl/neigh/show\fR" .IP "\fBtnl/arp/show\fR" OVS builds ARP cache by snooping are messages. This command shows ARP cache table. . .IP "\fBtnl/neigh/set \fIbridge ip mac\fR" .IP "\fBtnl/arp/set \fIbridge ip mac\fR" Adds or modifies an ARP cache entry in \fIbridge\fR, mapping \fIip\fR to \fImac\fR. . .IP "\fBtnl/neigh/flush\fR" .IP "\fBtnl/arp/flush\fR" Flush ARP table. . .IP "\fBtnl/egress_port_range [num1] [num2]\fR" Set range for UDP source port used for UDP based Tunnels. For example VxLAN. If case of zero arguments this command prints current range in use. openvswitch-2.5.9/ofproto/PaxHeaders.82075/collectors.h0000644000000000000000000000013113534540071017655 xustar0030 mtime=1567801401.617682756 29 atime=1567801402.10568634 30 ctime=1567801425.017855166 openvswitch-2.5.9/ofproto/collectors.h0000644000175000017500000000202013534540071021336 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2011 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef COLLECTORS_H #define COLLECTORS_H 1 #include #include struct collectors; struct sset; int collectors_create(const struct sset *targets, uint16_t default_port, struct collectors **); void collectors_destroy(struct collectors *); void collectors_send(const struct collectors *, const void *, size_t); int collectors_count(const struct collectors *); #endif /* collectors.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/tunnel.h0000644000000000000000000000012713534540071017016 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 29 ctime=1567801425.06185549 openvswitch-2.5.9/ofproto/tunnel.h0000644000175000017500000000411713534540071020503 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TUNNEL_H #define TUNNEL_H 1 #include #include #include "flow.h" /* Tunnel port emulation layer. * * These functions emulate tunnel virtual ports based on the outer * header information from the kernel. */ struct ovs_action_push_tnl; struct ofport_dpif; struct netdev; void ofproto_tunnel_init(void); bool tnl_port_reconfigure(const struct ofport_dpif *, const struct netdev *, odp_port_t, bool native_tnl, const char name[]); int tnl_port_add(const struct ofport_dpif *, const struct netdev *, odp_port_t odp_port, bool native_tnl, const char name[]); void tnl_port_del(const struct ofport_dpif *); const struct ofport_dpif *tnl_port_receive(const struct flow *); void tnl_wc_init(struct flow *, struct flow_wildcards *); bool tnl_process_ecn(struct flow *); odp_port_t tnl_port_send(const struct ofport_dpif *, struct flow *, struct flow_wildcards *wc); /* Returns true if 'flow' should be submitted to tnl_port_receive(). */ static inline bool tnl_port_should_receive(const struct flow *flow) { return flow_tnl_dst_is_set(&flow->tunnel); } int tnl_port_build_header(const struct ofport_dpif *ofport, const struct flow *tnl_flow, const struct eth_addr dmac, const struct eth_addr smac, const struct in6_addr *ipv6_src, struct ovs_action_push_tnl *data); #endif /* tunnel.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/fail-open.c0000644000000000000000000000013113534540071017351 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.021855195 openvswitch-2.5.9/ofproto/fail-open.c0000644000175000017500000002151613534540071021045 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "fail-open.h" #include #include #include "classifier.h" #include "connmgr.h" #include "flow.h" #include "mac-learning.h" #include "odp-util.h" #include "ofpbuf.h" #include "ofp-actions.h" #include "ofp-util.h" #include "ofproto.h" #include "ofproto-provider.h" #include "pktbuf.h" #include "dp-packet.h" #include "poll-loop.h" #include "rconn.h" #include "timeval.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(fail_open); /* * Fail-open mode. * * In fail-open mode, the switch detects when the controller cannot be * contacted or when the controller is dropping switch connections because the * switch does not pass its admission control policy. In those situations the * switch sets up flows itself using the "normal" action. * * There is a little subtlety to implementation, to properly handle the case * where the controller allows switch connections but drops them a few seconds * later for admission control reasons. Because of this case, we don't want to * just stop setting up flows when we connect to the controller: if we did, * then new flow setup and existing flows would stop during the duration of * connection to the controller, and thus the whole network would go down for * that period of time. * * So, instead, we add some special cases when we are connected to a * controller, but not yet sure that it has admitted us: * * - We set up flows immediately ourselves, but simultaneously send out an * OFPT_PACKET_IN to the controller. We put a special bogus buffer-id in * these OFPT_PACKET_IN messages so that duplicate packets don't get sent * out to the network when the controller replies. * * - We also send out OFPT_PACKET_IN messages for totally bogus packets * every so often, in case no real new flows are arriving in the network. * * - We don't flush the flow table at the time we connect, because this * could cause network stuttering in a switch with lots of flows or very * high-bandwidth flows by suddenly throwing lots of packets down to * userspace. */ struct fail_open { struct ofproto *ofproto; struct connmgr *connmgr; int last_disconn_secs; long long int next_bogus_packet_in; struct rconn_packet_counter *bogus_packet_counter; bool fail_open_active; }; static void fail_open_recover(struct fail_open *); /* Returns the number of seconds of disconnection after which fail-open mode * should activate. */ static int trigger_duration(const struct fail_open *fo) { if (!connmgr_has_controllers(fo->connmgr)) { /* Shouldn't ever arrive here, but if we do, never fail open. */ return INT_MAX; } else { /* Otherwise, every controller must have a chance to send an * inactivity probe and reconnect before we fail open, so take the * maximum probe interval and multiply by 3: * * - The first interval is the idle time before sending an inactivity * probe. * * - The second interval is the time allowed for a response to the * inactivity probe. * * - The third interval is the time allowed to reconnect after no * response is received. */ return connmgr_get_max_probe_interval(fo->connmgr) * 3; } } /* Returns true if 'fo' is currently in fail-open mode, otherwise false. */ bool fail_open_is_active(const struct fail_open *fo) { return fo->last_disconn_secs != 0; } static void send_bogus_packet_ins(struct fail_open *fo) { struct ofproto_packet_in pin; struct eth_addr mac; struct dp_packet b; dp_packet_init(&b, 128); eth_addr_nicira_random(&mac); compose_rarp(&b, mac); memset(&pin, 0, sizeof pin); pin.up.packet = dp_packet_data(&b); pin.up.packet_len = dp_packet_size(&b); pin.up.reason = OFPR_NO_MATCH; match_init_catchall(&pin.up.flow_metadata); match_set_in_port(&pin.up.flow_metadata, OFPP_LOCAL); pin.send_len = dp_packet_size(&b); pin.miss_type = OFPROTO_PACKET_IN_NO_MISS; connmgr_send_packet_in(fo->connmgr, &pin); dp_packet_uninit(&b); } /* Enter fail-open mode if we should be in it. */ void fail_open_run(struct fail_open *fo) { int disconn_secs = connmgr_failure_duration(fo->connmgr); /* Enter fail-open mode if 'fo' is not in it but should be. */ if (disconn_secs >= trigger_duration(fo)) { if (!fail_open_is_active(fo)) { VLOG_WARN("Could not connect to controller (or switch failed " "controller's post-connection admission control " "policy) for %d seconds, failing open", disconn_secs); fo->last_disconn_secs = disconn_secs; /* Flush all OpenFlow and datapath flows. We will set up our * fail-open rule from fail_open_flushed() when * ofproto_flush_flows() calls back to us. */ ofproto_flush_flows(fo->ofproto); } else if (disconn_secs > fo->last_disconn_secs + 60) { VLOG_INFO("Still in fail-open mode after %d seconds disconnected " "from controller", disconn_secs); fo->last_disconn_secs = disconn_secs; } } /* Schedule a bogus packet-in if we're connected and in fail-open. */ if (fail_open_is_active(fo)) { if (connmgr_is_any_controller_connected(fo->connmgr)) { bool expired = time_msec() >= fo->next_bogus_packet_in; if (expired) { send_bogus_packet_ins(fo); } if (expired || fo->next_bogus_packet_in == LLONG_MAX) { fo->next_bogus_packet_in = time_msec() + 2000; } } else { fo->next_bogus_packet_in = LLONG_MAX; } } } /* If 'fo' is currently in fail-open mode and its rconn has connected to the * controller, exits fail open mode. */ void fail_open_maybe_recover(struct fail_open *fo) OVS_EXCLUDED(ofproto_mutex) { if (fail_open_is_active(fo) && connmgr_is_any_controller_admitted(fo->connmgr)) { fail_open_recover(fo); } } static void fail_open_recover(struct fail_open *fo) OVS_EXCLUDED(ofproto_mutex) { struct match match; VLOG_WARN("No longer in fail-open mode"); fo->last_disconn_secs = 0; fo->next_bogus_packet_in = LLONG_MAX; match_init_catchall(&match); ofproto_delete_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY); } void fail_open_wait(struct fail_open *fo) { if (fo->next_bogus_packet_in != LLONG_MAX) { poll_timer_wait_until(fo->next_bogus_packet_in); } } void fail_open_flushed(struct fail_open *fo) OVS_EXCLUDED(ofproto_mutex) { int disconn_secs = connmgr_failure_duration(fo->connmgr); bool open = disconn_secs >= trigger_duration(fo); if (open) { struct ofpbuf ofpacts; struct match match; /* Set up a flow that matches every packet and directs them to * OFPP_NORMAL. */ ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE); ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; ofpact_pad(&ofpacts); match_init_catchall(&match); ofproto_add_flow(fo->ofproto, &match, FAIL_OPEN_PRIORITY, ofpacts.data, ofpacts.size); ofpbuf_uninit(&ofpacts); } fo->fail_open_active = open; } /* Returns the number of fail-open rules currently installed in the flow * table. */ int fail_open_count_rules(const struct fail_open *fo) { return fo->fail_open_active != 0; } /* Creates and returns a new struct fail_open for 'ofproto' and 'mgr'. */ struct fail_open * fail_open_create(struct ofproto *ofproto, struct connmgr *mgr) { struct fail_open *fo = xmalloc(sizeof *fo); fo->ofproto = ofproto; fo->connmgr = mgr; fo->last_disconn_secs = 0; fo->next_bogus_packet_in = LLONG_MAX; fo->bogus_packet_counter = rconn_packet_counter_create(); fo->fail_open_active = false; return fo; } /* Destroys 'fo'. */ void fail_open_destroy(struct fail_open *fo) OVS_EXCLUDED(ofproto_mutex) { if (fo) { if (fail_open_is_active(fo)) { fail_open_recover(fo); } /* We don't own fo->connmgr. */ rconn_packet_counter_destroy(fo->bogus_packet_counter); free(fo); } } openvswitch-2.5.9/ofproto/PaxHeaders.82075/collectors.c0000644000000000000000000000013113534540071017650 xustar0030 mtime=1567801401.617682756 29 atime=1567801402.10568634 30 ctime=1567801425.017855166 openvswitch-2.5.9/ofproto/collectors.c0000644000175000017500000000715613534540071021350 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "collectors.h" #include #include #include #include #include "socket-util.h" #include "sset.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(collectors); struct collectors { int *fds; /* Sockets. */ size_t n_fds; /* Number of sockets. */ }; /* Opens the targets specified in 'targets' for sending UDP packets. This is * useful for e.g. sending NetFlow or sFlow packets. Returns 0 if successful, * otherwise a positive errno value if opening at least one collector failed. * * Each target in 'targets' should be a string in the format "[:]". * may be omitted if 'default_port' is nonzero, in which case it * defaults to 'default_port'. * * '*collectorsp' is set to a null pointer if no targets were successfully * added, otherwise to a new collectors object if at least one was successfully * added. Thus, even on a failure return, it is possible that '*collectorsp' * is nonnull, and even on a successful return, it is possible that * '*collectorsp' is null, if 'target's is an empty sset. */ int collectors_create(const struct sset *targets, uint16_t default_port, struct collectors **collectorsp) { struct collectors *c; const char *name; int retval = 0; c = xmalloc(sizeof *c); c->fds = xmalloc(sizeof *c->fds * sset_count(targets)); c->n_fds = 0; SSET_FOR_EACH (name, targets) { int error; int fd; error = inet_open_active(SOCK_DGRAM, name, default_port, NULL, &fd, 0); if (fd >= 0) { c->fds[c->n_fds++] = fd; } else { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "couldn't open connection to collector %s (%s)", name, ovs_strerror(error)); if (!retval) { retval = error; } } } if (c->n_fds) { *collectorsp = c; } else { collectors_destroy(c); *collectorsp = NULL; } return retval; } /* Destroys 'c'. */ void collectors_destroy(struct collectors *c) { if (c) { size_t i; for (i = 0; i < c->n_fds; i++) { closesocket(c->fds[i]); } free(c->fds); free(c); } } /* Sends the 'n'-byte 'payload' to each of the collectors in 'c'. */ void collectors_send(const struct collectors *c, const void *payload, size_t n) { if (c) { size_t i; for (i = 0; i < c->n_fds; i++) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); if (send(c->fds[i], payload, n, 0) == -1) { char *s = describe_fd(c->fds[i]); VLOG_WARN_RL(&rl, "%s: sending to collector failed (%s)", s, ovs_strerror(errno)); free(s); } } } } int collectors_count(const struct collectors *c) { return c ? c->n_fds : 0; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/pktbuf.h0000644000000000000000000000013013534540071016776 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.057855461 openvswitch-2.5.9/ofproto/pktbuf.h0000644000175000017500000000240013534540071020462 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PKTBUF_H #define PKTBUF_H 1 #include #include #include "ofp-errors.h" struct pktbuf; struct dp_packet; int pktbuf_capacity(void); struct pktbuf *pktbuf_create(void); void pktbuf_destroy(struct pktbuf *); uint32_t pktbuf_save(struct pktbuf *, const void *buffer, size_t buffer_size, ofp_port_t in_port); uint32_t pktbuf_get_null(void); enum ofperr pktbuf_retrieve(struct pktbuf *, uint32_t id, struct dp_packet **bufferp, ofp_port_t *in_port); void pktbuf_discard(struct pktbuf *, uint32_t id); unsigned int pktbuf_count_packets(const struct pktbuf *); #endif /* pktbuf.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/netflow.c0000644000000000000000000000013113534540071017155 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801425.025855226 openvswitch-2.5.9/ofproto/netflow.c0000644000175000017500000003531213534540071020650 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "netflow.h" #include #include #include #include #include "byte-order.h" #include "collectors.h" #include "dpif.h" #include "flow.h" #include "lib/netflow.h" #include "ofpbuf.h" #include "ofproto.h" #include "ofproto/netflow.h" #include "packets.h" #include "poll-loop.h" #include "socket-util.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(netflow); struct netflow { uint8_t engine_type; /* Value of engine_type to use. */ uint8_t engine_id; /* Value of engine_id to use. */ long long int boot_time; /* Time when netflow_create() was called. */ struct collectors *collectors; /* NetFlow collectors. */ bool add_id_to_iface; /* Put the 7 least significiant bits of * 'engine_id' into the most significant * bits of the interface fields. */ uint32_t netflow_cnt; /* Flow sequence number for NetFlow. */ struct ofpbuf packet; /* NetFlow packet being accumulated. */ long long int active_timeout; /* Timeout for flows that are still active. */ long long int next_timeout; /* Next scheduled active timeout. */ long long int reconfig_time; /* When we reconfigured the timeouts. */ struct hmap flows; /* Contains 'netflow_flows'. */ struct ovs_refcount ref_cnt; }; struct netflow_flow { struct hmap_node hmap_node; long long int last_expired; /* Time this flow last timed out. */ long long int created; /* Time flow was created since time out. */ ofp_port_t output_iface; /* Output interface index. */ uint16_t tcp_flags; /* Bitwise-OR of all TCP flags seen. */ ofp_port_t in_port; /* Input port. */ ovs_be32 nw_src; /* IPv4 source address. */ ovs_be32 nw_dst; /* IPv4 destination address. */ uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */ uint8_t nw_proto; /* IP protocol. */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */ ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port. */ uint64_t packet_count; /* Packets from subrules. */ uint64_t byte_count; /* Bytes from subrules. */ long long int used; /* Last-used time (0 if never used). */ }; static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; static atomic_count netflow_count = ATOMIC_COUNT_INIT(0); static struct netflow_flow *netflow_flow_lookup(const struct netflow *, const struct flow *) OVS_REQUIRES(mutex); static uint32_t netflow_flow_hash(const struct flow *); static void netflow_expire__(struct netflow *, struct netflow_flow *) OVS_REQUIRES(mutex); static void netflow_run__(struct netflow *) OVS_REQUIRES(mutex); void netflow_mask_wc(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type != htons(ETH_TYPE_IP)) { return; } memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); flow_unwildcard_tp_ports(flow, wc); wc->masks.nw_tos |= IP_DSCP_MASK; } static void gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow, uint32_t packet_count, uint32_t byte_count) OVS_REQUIRES(mutex) { struct netflow_v5_header *nf_hdr; struct netflow_v5_record *nf_rec; if (!nf->packet.size) { struct timespec now; time_wall_timespec(&now); nf_hdr = ofpbuf_put_zeros(&nf->packet, sizeof *nf_hdr); nf_hdr->version = htons(NETFLOW_V5_VERSION); nf_hdr->count = htons(0); nf_hdr->sysuptime = htonl(time_msec() - nf->boot_time); nf_hdr->unix_secs = htonl(now.tv_sec); nf_hdr->unix_nsecs = htonl(now.tv_nsec); nf_hdr->engine_type = nf->engine_type; nf_hdr->engine_id = nf->engine_id; nf_hdr->sampling_interval = htons(0); } nf_hdr = nf->packet.data; nf_hdr->count = htons(ntohs(nf_hdr->count) + 1); nf_hdr->flow_seq = htonl(nf->netflow_cnt++); nf_rec = ofpbuf_put_zeros(&nf->packet, sizeof *nf_rec); nf_rec->src_addr = nf_flow->nw_src; nf_rec->dst_addr = nf_flow->nw_dst; nf_rec->nexthop = htonl(0); if (nf->add_id_to_iface) { uint16_t iface = (nf->engine_id & 0x7f) << 9; nf_rec->input = htons(iface | (ofp_to_u16(nf_flow->in_port) & 0x1ff)); nf_rec->output = htons(iface | (ofp_to_u16(nf_flow->output_iface) & 0x1ff)); } else { nf_rec->input = htons(ofp_to_u16(nf_flow->in_port)); nf_rec->output = htons(ofp_to_u16(nf_flow->output_iface)); } nf_rec->packet_count = htonl(packet_count); nf_rec->byte_count = htonl(byte_count); nf_rec->init_time = htonl(nf_flow->created - nf->boot_time); nf_rec->used_time = htonl(MAX(nf_flow->created, nf_flow->used) - nf->boot_time); if (nf_flow->nw_proto == IPPROTO_ICMP) { /* In NetFlow, the ICMP type and code are concatenated and * placed in the 'dst_port' field. */ uint8_t type = ntohs(nf_flow->tp_src); uint8_t code = ntohs(nf_flow->tp_dst); nf_rec->src_port = htons(0); nf_rec->dst_port = htons((type << 8) | code); } else { nf_rec->src_port = nf_flow->tp_src; nf_rec->dst_port = nf_flow->tp_dst; } nf_rec->tcp_flags = (uint8_t) nf_flow->tcp_flags; nf_rec->ip_proto = nf_flow->nw_proto; nf_rec->ip_tos = nf_flow->nw_tos & IP_DSCP_MASK; /* NetFlow messages are limited to 30 records. */ if (ntohs(nf_hdr->count) >= 30) { netflow_run__(nf); } } void netflow_flow_update(struct netflow *nf, const struct flow *flow, ofp_port_t output_iface, const struct dpif_flow_stats *stats) OVS_EXCLUDED(mutex) { struct netflow_flow *nf_flow; long long int used; /* NetFlow only reports on IP packets. */ if (flow->dl_type != htons(ETH_TYPE_IP)) { return; } ovs_mutex_lock(&mutex); nf_flow = netflow_flow_lookup(nf, flow); if (!nf_flow) { nf_flow = xzalloc(sizeof *nf_flow); nf_flow->in_port = flow->in_port.ofp_port; nf_flow->nw_src = flow->nw_src; nf_flow->nw_dst = flow->nw_dst; nf_flow->nw_tos = flow->nw_tos; nf_flow->nw_proto = flow->nw_proto; nf_flow->tp_src = flow->tp_src; nf_flow->tp_dst = flow->tp_dst; nf_flow->created = stats->used; nf_flow->output_iface = output_iface; hmap_insert(&nf->flows, &nf_flow->hmap_node, netflow_flow_hash(flow)); } if (nf_flow->output_iface != output_iface) { netflow_expire__(nf, nf_flow); nf_flow->created = stats->used; nf_flow->output_iface = output_iface; } nf_flow->packet_count += stats->n_packets; nf_flow->byte_count += stats->n_bytes; nf_flow->tcp_flags |= stats->tcp_flags; used = MAX(nf_flow->used, stats->used); if (nf_flow->used != used) { nf_flow->used = used; if (!nf->active_timeout || !nf_flow->last_expired || nf->reconfig_time > nf_flow->last_expired) { /* Keep the time updated to prevent a flood of expiration in * the future. */ nf_flow->last_expired = time_msec(); } } ovs_mutex_unlock(&mutex); } static void netflow_expire__(struct netflow *nf, struct netflow_flow *nf_flow) OVS_REQUIRES(mutex) { uint64_t pkts, bytes; pkts = nf_flow->packet_count; bytes = nf_flow->byte_count; nf_flow->last_expired += nf->active_timeout; if (pkts == 0) { return; } if ((bytes >> 32) <= 175) { /* NetFlow v5 records are limited to 32-bit counters. If we've wrapped * a counter, send as multiple records so we don't lose track of any * traffic. We try to evenly distribute the packet and byte counters, * so that the bytes-per-packet lengths don't look wonky across the * records. */ while (bytes) { int n_recs = (bytes + UINT32_MAX - 1) / UINT32_MAX; uint32_t pkt_count = pkts / n_recs; uint32_t byte_count = bytes / n_recs; gen_netflow_rec(nf, nf_flow, pkt_count, byte_count); pkts -= pkt_count; bytes -= byte_count; } } else { /* In 600 seconds, a 10GbE link can theoretically transmit 75 * 10**10 * == 175 * 2**32 bytes. The byte counter is bigger than that, so it's * probably a bug--for example, the netdev code uses UINT64_MAX to * report "unknown value", and perhaps that has leaked through to here. * * We wouldn't want to hit the loop above in this case, because it * would try to send up to UINT32_MAX netflow records, which would take * a long time. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "impossible byte counter %"PRIu64, bytes); } /* Update flow tracking data. */ nf_flow->packet_count = 0; nf_flow->byte_count = 0; nf_flow->tcp_flags = 0; } void netflow_flow_clear(struct netflow *nf, const struct flow *flow) OVS_EXCLUDED(mutex) { struct netflow_flow *nf_flow; ovs_mutex_lock(&mutex); nf_flow = netflow_flow_lookup(nf, flow); if (nf_flow) { netflow_expire__(nf, nf_flow); hmap_remove(&nf->flows, &nf_flow->hmap_node); free(nf_flow); } ovs_mutex_unlock(&mutex); } /* Returns true if it's time to send out a round of NetFlow active timeouts, * false otherwise. */ static void netflow_run__(struct netflow *nf) OVS_REQUIRES(mutex) { long long int now = time_msec(); struct netflow_flow *nf_flow, *next; if (nf->packet.size) { collectors_send(nf->collectors, nf->packet.data, nf->packet.size); nf->packet.size = 0; } if (!nf->active_timeout || now < nf->next_timeout) { return; } nf->next_timeout = now + 1000; HMAP_FOR_EACH_SAFE (nf_flow, next, hmap_node, &nf->flows) { if (now > nf_flow->last_expired + nf->active_timeout) { bool idle = nf_flow->used < nf_flow->last_expired; netflow_expire__(nf, nf_flow); if (idle) { /* If the netflow_flow hasn't been used in a while, it's * possible the upper layer lost track of it. */ hmap_remove(&nf->flows, &nf_flow->hmap_node); free(nf_flow); } } } } void netflow_run(struct netflow *nf) { ovs_mutex_lock(&mutex); netflow_run__(nf); ovs_mutex_unlock(&mutex); } void netflow_wait(struct netflow *nf) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (nf->active_timeout) { poll_timer_wait_until(nf->next_timeout); } if (nf->packet.size) { poll_immediate_wake(); } ovs_mutex_unlock(&mutex); } int netflow_set_options(struct netflow *nf, const struct netflow_options *nf_options) OVS_EXCLUDED(mutex) { int error = 0; long long int old_timeout; ovs_mutex_lock(&mutex); nf->engine_type = nf_options->engine_type; nf->engine_id = nf_options->engine_id; nf->add_id_to_iface = nf_options->add_id_to_iface; collectors_destroy(nf->collectors); collectors_create(&nf_options->collectors, 0, &nf->collectors); old_timeout = nf->active_timeout; if (nf_options->active_timeout >= 0) { nf->active_timeout = nf_options->active_timeout; } else { nf->active_timeout = NF_ACTIVE_TIMEOUT_DEFAULT; } nf->active_timeout *= 1000; if (old_timeout != nf->active_timeout) { nf->reconfig_time = time_msec(); nf->next_timeout = time_msec(); } ovs_mutex_unlock(&mutex); return error; } struct netflow * netflow_create(void) { struct netflow *nf = xzalloc(sizeof *nf); nf->engine_type = 0; nf->engine_id = 0; nf->boot_time = time_msec(); nf->collectors = NULL; nf->add_id_to_iface = false; nf->netflow_cnt = 0; hmap_init(&nf->flows); ovs_refcount_init(&nf->ref_cnt); ofpbuf_init(&nf->packet, 1500); atomic_count_inc(&netflow_count); return nf; } struct netflow * netflow_ref(const struct netflow *nf_) { struct netflow *nf = CONST_CAST(struct netflow *, nf_); if (nf) { ovs_refcount_ref(&nf->ref_cnt); } return nf; } void netflow_unref(struct netflow *nf) { if (nf && ovs_refcount_unref_relaxed(&nf->ref_cnt) == 1) { atomic_count_dec(&netflow_count); collectors_destroy(nf->collectors); ofpbuf_uninit(&nf->packet); free(nf); } } /* Returns true if there exist any netflow objects, false otherwise. * Callers must cope with transient false positives, i.e., there is no tight * synchronization with the count and the actual existence of netflow objects. */ bool netflow_exists(void) { return atomic_count_get(&netflow_count) > 0; } /* Helpers. */ static struct netflow_flow * netflow_flow_lookup(const struct netflow *nf, const struct flow *flow) OVS_REQUIRES(mutex) { struct netflow_flow *nf_flow; HMAP_FOR_EACH_WITH_HASH (nf_flow, hmap_node, netflow_flow_hash(flow), &nf->flows) { if (flow->in_port.ofp_port == nf_flow->in_port && flow->nw_src == nf_flow->nw_src && flow->nw_dst == nf_flow->nw_dst && flow->nw_tos == nf_flow->nw_tos && flow->nw_proto == nf_flow->nw_proto && flow->tp_src == nf_flow->tp_src && flow->tp_dst == nf_flow->tp_dst) { return nf_flow; } } return NULL; } static uint32_t netflow_flow_hash(const struct flow *flow) { uint32_t hash = 0; hash = hash_add(hash, (OVS_FORCE uint32_t) flow->in_port.ofp_port); hash = hash_add(hash, ntohl(flow->nw_src)); hash = hash_add(hash, ntohl(flow->nw_dst)); hash = hash_add(hash, flow->nw_tos); hash = hash_add(hash, flow->nw_proto); hash = hash_add(hash, ntohs(flow->tp_src)); hash = hash_add(hash, ntohs(flow->tp_dst)); return hash_finish(hash, 28); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/fail-open.h0000644000000000000000000000013113534540071017356 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.021855195 openvswitch-2.5.9/ofproto/fail-open.h0000644000175000017500000000350413534540071021047 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FAIL_OPEN_H #define FAIL_OPEN_H 1 #include #include #include "ofproto-provider.h" struct connmgr; struct fail_open; struct ofproto; /* Priority of the rule added by the fail-open subsystem when a switch enters * fail-open mode. This priority value uniquely identifies a fail-open flow * (OpenFlow priorities max out at 65535 and nothing else in Open vSwitch * creates flows with this priority). And "f0" is mnemonic for "fail open"! */ #define FAIL_OPEN_PRIORITY 0xf0f0f0 /* Returns true if 'rule' is one created by the "fail open" logic, false * otherwise. */ static inline bool is_fail_open_rule(const struct rule *rule) { return rule->cr.priority == FAIL_OPEN_PRIORITY; } struct fail_open *fail_open_create(struct ofproto *, struct connmgr *); void fail_open_destroy(struct fail_open *) OVS_EXCLUDED(ofproto_mutex); void fail_open_wait(struct fail_open *); bool fail_open_is_active(const struct fail_open *); void fail_open_run(struct fail_open *); void fail_open_maybe_recover(struct fail_open *) OVS_EXCLUDED(ofproto_mutex); void fail_open_flushed(struct fail_open *) OVS_EXCLUDED(ofproto_mutex); int fail_open_count_rules(const struct fail_open *); #endif /* fail-open.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-monitor.h0000644000000000000000000000013113534540071021601 xustar0030 mtime=1567801401.633682874 29 atime=1567801402.10568634 30 ctime=1567801425.041855343 openvswitch-2.5.9/ofproto/ofproto-dpif-monitor.h0000644000175000017500000000216213534540071023271 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_MONITOR_H #define OFPROTO_DPIF_MONITOR_H 1 #include #include "openflow/openflow.h" #include "packets.h" struct bfd; struct cfm; struct lldp; struct ofport_dpif; void ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *); void ofproto_dpif_monitor_port_update(const struct ofport_dpif *, struct bfd *, struct cfm *, struct lldp *, const struct eth_addr *); #endif /* ofproto-dpif-monitor.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-sflow.h0000644000000000000000000000013113534540071021244 xustar0030 mtime=1567801401.637682902 29 atime=1567801402.10968637 30 ctime=1567801425.049855401 openvswitch-2.5.9/ofproto/ofproto-dpif-sflow.h0000644000175000017500000000605513534540071022741 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010 InMon Corp. * Copyright (c) 2009, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_SFLOW_H #define OFPROTO_DPIF_SFLOW_H 1 #include #include "svec.h" #include "lib/odp-util.h" struct dpif; struct dpif_upcall; struct flow; struct ofproto_sflow_options; struct ofport; /* When we have the actions for a sampled packet that * will go to just one output, then this structure is * populated by parsing them. Only fields relevant to * the sFlow export are extracted. */ struct dpif_sflow_actions { odp_port_t out_port; /* ODP output port. */ uint32_t encap_depth; /* Count layers of tunnel-encap. */ struct flow_tnl tunnel; /* Egress tunnel push/set. */ uint8_t tunnel_ipproto; /* Tunnel push action can set ipproto. */ bool tunnel_err; /* Tunnel actions parse failure. */ /* Using host-byte order for the mpls stack here to match the expectations of the sFlow library. Also the ordering is reversed, so that the entry at offset 0 is the bottom of the stack. */ uint32_t mpls_lse[FLOW_MAX_MPLS_LABELS]; /* Out stack in host byte order. */ uint32_t mpls_stack_depth; /* Out stack depth. */ bool mpls_err; /* MPLS actions parse failure. */ }; struct dpif_sflow *dpif_sflow_create(void); struct dpif_sflow *dpif_sflow_ref(const struct dpif_sflow *); void dpif_sflow_unref(struct dpif_sflow *); uint32_t dpif_sflow_get_probability(const struct dpif_sflow *); void dpif_sflow_set_options(struct dpif_sflow *, const struct ofproto_sflow_options *); void dpif_sflow_clear(struct dpif_sflow *); bool dpif_sflow_is_enabled(const struct dpif_sflow *); void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport, odp_port_t odp_port); void dpif_sflow_del_port(struct dpif_sflow *, odp_port_t odp_port); void dpif_sflow_run(struct dpif_sflow *); void dpif_sflow_wait(struct dpif_sflow *); void dpif_sflow_read_actions(const struct flow *, const struct nlattr *actions, size_t actions_len, struct dpif_sflow_actions *); void dpif_sflow_received(struct dpif_sflow *, const struct dp_packet *, const struct flow *, odp_port_t odp_port, const union user_action_cookie *, const struct dpif_sflow_actions *); int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *, odp_port_t odp_port); #endif /* ofproto/ofproto-dpif-sflow.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto.h0000644000000000000000000000013013534540071017173 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.029855254 openvswitch-2.5.9/ofproto/ofproto.h0000644000175000017500000005032613534540071020671 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_H #define OFPROTO_H 1 #include #include #include #include #include #include "cfm.h" #include "classifier.h" #include "flow.h" #include "meta-flow.h" #include "netflow.h" #include "rstp.h" #include "smap.h" #include "sset.h" #include "stp.h" #include "lacp.h" #ifdef __cplusplus extern "C" { #endif struct bfd_cfg; struct cfm_settings; struct cls_rule; struct netdev; struct netdev_stats; struct ofport; struct ofproto; struct shash; struct simap; struct smap; struct netdev_stats; struct ovs_list; struct lldp_status; struct aa_settings; struct aa_mapping_settings; /* Needed for the lock annotations. */ extern struct ovs_mutex ofproto_mutex; struct ofproto_controller_info { bool is_connected; enum ofp12_controller_role role; struct smap pairs; }; struct ofproto_sflow_options { struct sset targets; uint32_t sampling_rate; uint32_t polling_interval; uint32_t header_len; uint32_t sub_id; char *agent_device; char *control_ip; }; struct ofproto_ipfix_bridge_exporter_options { struct sset targets; uint32_t sampling_rate; uint32_t obs_domain_id; /* Bridge-wide Observation Domain ID. */ uint32_t obs_point_id; /* Bridge-wide Observation Point ID. */ uint32_t cache_active_timeout; uint32_t cache_max_flows; bool enable_tunnel_sampling; bool enable_input_sampling; bool enable_output_sampling; }; struct ofproto_ipfix_flow_exporter_options { uint32_t collector_set_id; struct sset targets; uint32_t cache_active_timeout; uint32_t cache_max_flows; }; struct ofproto_rstp_status { bool enabled; /* If false, ignore other members. */ rstp_identifier root_id; rstp_identifier bridge_id; rstp_identifier designated_id; uint32_t root_path_cost; uint16_t designated_port_id; uint16_t bridge_port_id; }; struct ofproto_rstp_settings { rstp_identifier address; uint16_t priority; uint32_t ageing_time; enum rstp_force_protocol_version force_protocol_version; uint16_t bridge_forward_delay; uint16_t bridge_max_age; uint16_t transmit_hold_count; }; struct ofproto_port_rstp_status { bool enabled; /* If false, ignore other members. */ uint16_t port_id; enum rstp_port_role role; enum rstp_state state; rstp_identifier designated_bridge_id; uint16_t designated_port_id; uint32_t designated_path_cost; int tx_count; /* Number of BPDUs transmitted. */ int rx_count; /* Number of valid BPDUs received. */ int error_count; /* Number of bad BPDUs received. */ int uptime; }; struct ofproto_port_rstp_settings { bool enable; uint16_t port_num; /* In the range 1-4095, inclusive. */ uint8_t priority; uint32_t path_cost; bool admin_edge_port; bool auto_edge; bool mcheck; uint8_t admin_p2p_mac_state; bool admin_port_state; }; struct ofproto_stp_settings { stp_identifier system_id; uint16_t priority; uint16_t hello_time; uint16_t max_age; uint16_t fwd_delay; }; struct ofproto_stp_status { bool enabled; /* If false, ignore other members. */ stp_identifier bridge_id; stp_identifier designated_root; int root_path_cost; }; struct ofproto_port_stp_settings { bool enable; uint8_t port_num; /* In the range 1-255, inclusive. */ uint8_t priority; uint16_t path_cost; }; struct ofproto_port_stp_status { bool enabled; /* If false, ignore other members. */ int port_id; enum stp_state state; unsigned int sec_in_state; enum stp_role role; }; struct ofproto_port_stp_stats { bool enabled; /* If false, ignore other members. */ int tx_count; /* Number of BPDUs transmitted. */ int rx_count; /* Number of valid BPDUs received. */ int error_count; /* Number of bad BPDUs received. */ }; struct ofproto_port_queue { uint32_t queue; /* Queue ID. */ uint8_t dscp; /* DSCP bits (e.g. [0, 63]). */ }; struct ofproto_mcast_snooping_settings { bool flood_unreg; /* If true, flood unregistered packets to all all ports. If false, send only to ports connected to multicast routers. */ unsigned int idle_time; /* Entry is removed after the idle time * in seconds. */ unsigned int max_entries; /* Size of the multicast snooping table. */ }; struct ofproto_mcast_snooping_port_settings { bool flood; /* If true, flood multicast traffic */ bool flood_reports; /* If true, flood Reports traffic */ }; /* How the switch should act if the controller cannot be contacted. */ enum ofproto_fail_mode { OFPROTO_FAIL_SECURE, /* Preserve flow table. */ OFPROTO_FAIL_STANDALONE /* Act as a standalone switch. */ }; enum ofproto_band { OFPROTO_IN_BAND, /* In-band connection to controller. */ OFPROTO_OUT_OF_BAND /* Out-of-band connection to controller. */ }; struct ofproto_controller { char *target; /* e.g. "tcp:127.0.0.1" */ int max_backoff; /* Maximum reconnection backoff, in seconds. */ int probe_interval; /* Max idle time before probing, in seconds. */ enum ofproto_band band; /* In-band or out-of-band? */ bool enable_async_msgs; /* Initially enable asynchronous messages? */ /* OpenFlow packet-in rate-limiting. */ int rate_limit; /* Max packet-in rate in packets per second. */ int burst_limit; /* Limit on accumulating packet credits. */ uint8_t dscp; /* DSCP value for controller connection. */ }; void ofproto_enumerate_types(struct sset *types); const char *ofproto_normalize_type(const char *); int ofproto_enumerate_names(const char *type, struct sset *names); void ofproto_parse_name(const char *name, char **dp_name, char **dp_type); /* An interface hint element, which is used by ofproto_init() to * describe the caller's understanding of the startup state. */ struct iface_hint { char *br_name; /* Name of owning bridge. */ char *br_type; /* Type of owning bridge. */ ofp_port_t ofp_port; /* OpenFlow port number. */ }; void ofproto_init(const struct shash *iface_hints); int ofproto_type_run(const char *datapath_type); void ofproto_type_wait(const char *datapath_type); int ofproto_create(const char *datapath, const char *datapath_type, struct ofproto **ofprotop); void ofproto_destroy(struct ofproto *, bool del); int ofproto_delete(const char *name, const char *type); int ofproto_run(struct ofproto *); void ofproto_wait(struct ofproto *); bool ofproto_is_alive(const struct ofproto *); void ofproto_get_memory_usage(const struct ofproto *, struct simap *); void ofproto_type_get_memory_usage(const char *datapath_type, struct simap *); /* A port within an OpenFlow switch. * * 'name' and 'type' are suitable for passing to netdev_open(). */ struct ofproto_port { char *name; /* Network device name, e.g. "eth0". */ char *type; /* Network device type, e.g. "system". */ ofp_port_t ofp_port; /* OpenFlow port number. */ }; void ofproto_port_clone(struct ofproto_port *, const struct ofproto_port *); void ofproto_port_destroy(struct ofproto_port *); struct ofproto_port_dump { const struct ofproto *ofproto; int error; void *state; }; void ofproto_port_dump_start(struct ofproto_port_dump *, const struct ofproto *); bool ofproto_port_dump_next(struct ofproto_port_dump *, struct ofproto_port *); int ofproto_port_dump_done(struct ofproto_port_dump *); /* Iterates through each OFPROTO_PORT in OFPROTO, using DUMP as state. * * Arguments all have pointer type. * * If you break out of the loop, then you need to free the dump structure by * hand using ofproto_port_dump_done(). */ #define OFPROTO_PORT_FOR_EACH(OFPROTO_PORT, DUMP, OFPROTO) \ for (ofproto_port_dump_start(DUMP, OFPROTO); \ (ofproto_port_dump_next(DUMP, OFPROTO_PORT) \ ? true \ : (ofproto_port_dump_done(DUMP), false)); \ ) #define OFPROTO_FLOW_LIMIT_DEFAULT 200000 #define OFPROTO_MAX_IDLE_DEFAULT 10000 /* ms */ const char *ofproto_port_open_type(const char *datapath_type, const char *port_type); int ofproto_port_add(struct ofproto *, struct netdev *, ofp_port_t *ofp_portp); int ofproto_port_del(struct ofproto *, ofp_port_t ofp_port); int ofproto_port_get_stats(const struct ofport *, struct netdev_stats *stats); int ofproto_port_query_by_name(const struct ofproto *, const char *devname, struct ofproto_port *); /* Top-level configuration. */ uint64_t ofproto_get_datapath_id(const struct ofproto *); void ofproto_set_datapath_id(struct ofproto *, uint64_t datapath_id); void ofproto_set_controllers(struct ofproto *, const struct ofproto_controller *, size_t n, uint32_t allowed_versions); void ofproto_set_fail_mode(struct ofproto *, enum ofproto_fail_mode fail_mode); void ofproto_reconnect_controllers(struct ofproto *); void ofproto_set_extra_in_band_remotes(struct ofproto *, const struct sockaddr_in *, size_t n); void ofproto_set_in_band_queue(struct ofproto *, int queue_id); void ofproto_set_flow_limit(unsigned limit); void ofproto_set_max_idle(unsigned max_idle); void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu); void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time, size_t max_entries); int ofproto_set_mcast_snooping(struct ofproto *ofproto, const struct ofproto_mcast_snooping_settings *s); int ofproto_port_set_mcast_snooping(struct ofproto *ofproto, void *aux, const struct ofproto_mcast_snooping_port_settings *s); void ofproto_set_threads(int n_handlers, int n_revalidators); void ofproto_set_n_dpdk_rxqs(int n_rxqs); void ofproto_set_cpu_mask(const char *cmask); void ofproto_set_dp_desc(struct ofproto *, const char *dp_desc); int ofproto_set_snoops(struct ofproto *, const struct sset *snoops); int ofproto_set_netflow(struct ofproto *, const struct netflow_options *nf_options); int ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *); int ofproto_set_ipfix(struct ofproto *, const struct ofproto_ipfix_bridge_exporter_options *, const struct ofproto_ipfix_flow_exporter_options *, size_t); void ofproto_set_flow_restore_wait(bool flow_restore_wait_db); bool ofproto_get_flow_restore_wait(void); int ofproto_set_stp(struct ofproto *, const struct ofproto_stp_settings *); int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *); int ofproto_set_rstp(struct ofproto *, const struct ofproto_rstp_settings *); int ofproto_get_rstp_status(struct ofproto *, struct ofproto_rstp_status *); /* Configuration of ports. */ void ofproto_port_unregister(struct ofproto *, ofp_port_t ofp_port); void ofproto_port_clear_cfm(struct ofproto *, ofp_port_t ofp_port); void ofproto_port_set_cfm(struct ofproto *, ofp_port_t ofp_port, const struct cfm_settings *); void ofproto_port_set_bfd(struct ofproto *, ofp_port_t ofp_port, const struct smap *cfg); bool ofproto_port_bfd_status_changed(struct ofproto *, ofp_port_t ofp_port); int ofproto_port_get_bfd_status(struct ofproto *, ofp_port_t ofp_port, struct smap *); int ofproto_port_is_lacp_current(struct ofproto *, ofp_port_t ofp_port); int ofproto_port_get_lacp_stats(const struct ofport *, struct lacp_slave_stats *); int ofproto_port_set_stp(struct ofproto *, ofp_port_t ofp_port, const struct ofproto_port_stp_settings *); int ofproto_port_get_stp_status(struct ofproto *, ofp_port_t ofp_port, struct ofproto_port_stp_status *); int ofproto_port_get_stp_stats(struct ofproto *, ofp_port_t ofp_port, struct ofproto_port_stp_stats *); int ofproto_port_set_queues(struct ofproto *, ofp_port_t ofp_port, const struct ofproto_port_queue *, size_t n_queues); int ofproto_port_get_rstp_status(struct ofproto *, ofp_port_t ofp_port, struct ofproto_port_rstp_status *); int ofproto_port_set_rstp(struct ofproto *, ofp_port_t ofp_port, const struct ofproto_port_rstp_settings *); /* The behaviour of the port regarding VLAN handling */ enum port_vlan_mode { /* This port is an access port. 'vlan' is the VLAN ID. 'trunks' is * ignored. */ PORT_VLAN_ACCESS, /* This port is a trunk. 'trunks' is the set of trunks. 'vlan' is * ignored. */ PORT_VLAN_TRUNK, /* Untagged incoming packets are part of 'vlan', as are incoming packets * tagged with 'vlan'. Outgoing packets tagged with 'vlan' stay tagged. * Other VLANs in 'trunks' are trunked. */ PORT_VLAN_NATIVE_TAGGED, /* Untagged incoming packets are part of 'vlan', as are incoming packets * tagged with 'vlan'. Outgoing packets tagged with 'vlan' are untagged. * Other VLANs in 'trunks' are trunked. */ PORT_VLAN_NATIVE_UNTAGGED }; /* Configuration of bundles. */ struct ofproto_bundle_settings { char *name; /* For use in log messages. */ ofp_port_t *slaves; /* OpenFlow port numbers for slaves. */ size_t n_slaves; enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */ int vlan; /* VLAN VID, except for PORT_VLAN_TRUNK. */ unsigned long *trunks; /* vlan_bitmap, except for PORT_VLAN_ACCESS. */ bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */ struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */ struct lacp_settings *lacp; /* Nonnull to enable LACP. */ struct lacp_slave_settings *lacp_slaves; /* Array of n_slaves elements. */ /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device * drivers in old versions of Linux that do not properly support VLANs when * VLAN devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ ofp_port_t realdev_ofp_port;/* OpenFlow port number of real device. */ }; int ofproto_bundle_register(struct ofproto *, void *aux, const struct ofproto_bundle_settings *); int ofproto_bundle_unregister(struct ofproto *, void *aux); /* Configuration of mirrors. */ struct ofproto_mirror_settings { /* Name for log messages. */ char *name; /* Bundles that select packets for mirroring upon ingress. */ void **srcs; /* A set of registered ofbundle handles. */ size_t n_srcs; /* Bundles that select packets for mirroring upon egress. */ void **dsts; /* A set of registered ofbundle handles. */ size_t n_dsts; /* VLANs of packets to select for mirroring. */ unsigned long *src_vlans; /* vlan_bitmap, NULL selects all VLANs. */ /* Output (mutually exclusive). */ void *out_bundle; /* A registered ofbundle handle or NULL. */ uint16_t out_vlan; /* Output VLAN, only if out_bundle is NULL. */ }; int ofproto_mirror_register(struct ofproto *, void *aux, const struct ofproto_mirror_settings *); int ofproto_mirror_unregister(struct ofproto *, void *aux); int ofproto_mirror_get_stats(struct ofproto *, void *aux, uint64_t *packets, uint64_t *bytes); void ofproto_port_set_lldp(struct ofproto *ofproto, ofp_port_t ofp_port, const struct smap *cfg); int ofproto_set_aa(struct ofproto *ofproto, void *aux, const struct aa_settings *s); int ofproto_aa_mapping_register(struct ofproto *ofproto, void *aux, const struct aa_mapping_settings *s); int ofproto_aa_mapping_unregister(struct ofproto *ofproto, void *aux); int ofproto_aa_vlan_get_queued(struct ofproto *ofproto, struct ovs_list *list); unsigned int ofproto_aa_vlan_get_queue_size(struct ofproto *ofproto); int ofproto_set_flood_vlans(struct ofproto *, unsigned long *flood_vlans); bool ofproto_is_mirror_output_bundle(const struct ofproto *, void *aux); /* Configuration of OpenFlow tables. */ struct ofproto_table_settings { char *name; /* Name exported via OpenFlow or NULL. */ unsigned int max_flows; /* Maximum number of flows or UINT_MAX. */ /* These members, together with OpenFlow OFPT_TABLE_MOD, determine the * handling of an attempt to add a flow that would cause the table to have * more than 'max_flows' flows: * * - If 'enable_eviction' is false and OFPT_TABLE_MOD does not enable * eviction, overflows will be rejected with an error. * * - If 'enable_eviction' is true or OFPT_TABLE_MOD enables eviction, an * overflow will cause a flow to be removed. The flow to be removed * is chosen to give fairness among groups distinguished by different * values for the 'n_groups' subfields within 'groups'. */ bool enable_eviction; struct mf_subfield *groups; size_t n_groups; /* * Fields for which prefix trie lookup is maintained. */ unsigned int n_prefix_fields; enum mf_field_id prefix_fields[CLS_MAX_TRIES]; }; extern const enum mf_field_id default_prefix_fields[2]; BUILD_ASSERT_DECL(ARRAY_SIZE(default_prefix_fields) <= CLS_MAX_TRIES); int ofproto_get_n_tables(const struct ofproto *); uint8_t ofproto_get_n_visible_tables(const struct ofproto *); void ofproto_configure_table(struct ofproto *, int table_id, const struct ofproto_table_settings *); /* Configuration querying. */ bool ofproto_has_snoops(const struct ofproto *); void ofproto_get_snoops(const struct ofproto *, struct sset *); void ofproto_get_all_flows(struct ofproto *p, struct ds *); void ofproto_get_netflow_ids(const struct ofproto *, uint8_t *engine_type, uint8_t *engine_id); void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *); void ofproto_free_ofproto_controller_info(struct shash *); bool ofproto_port_cfm_status_changed(struct ofproto *, ofp_port_t ofp_port); int ofproto_port_get_cfm_status(const struct ofproto *, ofp_port_t ofp_port, struct cfm_status *); /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ void ofproto_get_vlan_usage(struct ofproto *, unsigned long int *vlan_bitmap); bool ofproto_has_vlan_usage_changed(const struct ofproto *); int ofproto_port_set_realdev(struct ofproto *, ofp_port_t vlandev_ofp_port, ofp_port_t realdev_ofp_port, int vid); /* Table configuration */ enum ofputil_table_miss ofproto_table_get_miss_config(const struct ofproto *, uint8_t table_id); #ifdef __cplusplus } #endif #endif /* ofproto.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/bundles.c0000644000000000000000000000013113534540071017133 xustar0030 mtime=1567801401.617682756 30 atime=1567801402.101686312 29 ctime=1567801425.06185549 openvswitch-2.5.9/ofproto/bundles.c0000644000175000017500000001013113534540071020616 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Alexandru Copot , with support from IXIA. * Copyright (c) 2013, 2014 Daniel Baluta * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "coverage.h" #include "fail-open.h" #include "in-band.h" #include "odp-util.h" #include "ofp-actions.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto-provider.h" #include "pinsched.h" #include "poll-loop.h" #include "pktbuf.h" #include "rconn.h" #include "shash.h" #include "simap.h" #include "stream.h" #include "timeval.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "bundles.h" VLOG_DEFINE_THIS_MODULE(bundles); static struct ofp_bundle * ofp_bundle_create(uint32_t id, uint16_t flags) { struct ofp_bundle *bundle; bundle = xmalloc(sizeof(*bundle)); bundle->id = id; bundle->flags = flags; bundle->state = BS_OPEN; list_init(&bundle->msg_list); return bundle; } void ofp_bundle_remove__(struct ofconn *ofconn, struct ofp_bundle *bundle, bool success) { struct ofp_bundle_entry *msg; LIST_FOR_EACH_POP (msg, node, &bundle->msg_list) { if (success && msg->type == OFPTYPE_FLOW_MOD) { /* Tell connmgr about successful flow mods. */ ofconn_report_flow_mod(ofconn, msg->ofm.fm.command); } ofp_bundle_entry_free(msg); } ofconn_remove_bundle(ofconn, bundle); free(bundle); } enum ofperr ofp_bundle_open(struct ofconn *ofconn, uint32_t id, uint16_t flags) { struct ofp_bundle *bundle; enum ofperr error; bundle = ofconn_get_bundle(ofconn, id); if (bundle) { VLOG_INFO("Bundle %x already exists.", id); ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_ID; } bundle = ofp_bundle_create(id, flags); error = ofconn_insert_bundle(ofconn, bundle); if (error) { free(bundle); } return error; } enum ofperr ofp_bundle_close(struct ofconn *ofconn, uint32_t id, uint16_t flags) { struct ofp_bundle *bundle; bundle = ofconn_get_bundle(ofconn, id); if (!bundle) { return OFPERR_OFPBFC_BAD_ID; } if (bundle->state == BS_CLOSED) { ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BUNDLE_CLOSED; } if (bundle->flags != flags) { ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_FLAGS; } bundle->state = BS_CLOSED; return 0; } enum ofperr ofp_bundle_discard(struct ofconn *ofconn, uint32_t id) { struct ofp_bundle *bundle; bundle = ofconn_get_bundle(ofconn, id); if (!bundle) { return OFPERR_OFPBFC_BAD_ID; } ofp_bundle_remove__(ofconn, bundle, false); return 0; } enum ofperr ofp_bundle_add_message(struct ofconn *ofconn, uint32_t id, uint16_t flags, struct ofp_bundle_entry *bmsg) { struct ofp_bundle *bundle; bundle = ofconn_get_bundle(ofconn, id); if (!bundle) { enum ofperr error; bundle = ofp_bundle_create(id, flags); error = ofconn_insert_bundle(ofconn, bundle); if (error) { free(bundle); return error; } } else if (bundle->state == BS_CLOSED) { ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BUNDLE_CLOSED; } else if (flags != bundle->flags) { ofp_bundle_remove__(ofconn, bundle, false); return OFPERR_OFPBFC_BAD_FLAGS; } list_push_back(&bundle->msg_list, &bmsg->node); return 0; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ipfix-enterprise-entities.def0000644000000000000000000000013113534540071023132 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801423.805846231 openvswitch-2.5.9/ofproto/ipfix-enterprise-entities.def0000644000175000017500000000166613534540071024632 0ustar00jpettitjpettit00000000000000/* IPFIX enterprise entities. */ #ifndef IPFIX_ENTERPRISE_ENTITY #define IPFIX_ENTERPRISE_ENTITY(ENUM, ID, SIZE, NAME, ENTERPRISE) #endif #define IPFIX_ENTERPRISE_VMWARE 6876 IPFIX_ENTERPRISE_ENTITY(TUNNEL_TYPE, 891, 1, tunnelType, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_KEY, 892, 0, tunnelKey, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_SOURCE_IPV4_ADDRESS, 893, 4, tunnelSourceIPv4Address, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_IPV4_ADDRESS, 894, 4, tunnelDestinationIPv4Address, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_PROTOCOL_IDENTIFIER, 895, 1, tunnelProtocolIdentifier, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_SOURCE_TRANSPORT_PORT, 896, 2, tunnelSourceTransportPort, IPFIX_ENTERPRISE_VMWARE) IPFIX_ENTERPRISE_ENTITY(TUNNEL_DESTINATION_TRANSPORT_PORT, 897, 2, tunnelDestinationTransportPort, IPFIX_ENTERPRISE_VMWARE) #undef IPFIX_ENTERPRISE_ENTITY openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-xlate.c0000644000000000000000000000013113534540071021222 xustar0030 mtime=1567801401.641682932 29 atime=1567801402.10968637 30 ctime=1567801425.049855401 openvswitch-2.5.9/ofproto/ofproto-dpif-xlate.c0000644000175000017500000057651013534540071022727 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto/ofproto-dpif-xlate.h" #include #include #include #include #include #include "tnl-neigh-cache.h" #include "bfd.h" #include "bitmap.h" #include "bond.h" #include "bundle.h" #include "byte-order.h" #include "cfm.h" #include "connmgr.h" #include "coverage.h" #include "dp-packet.h" #include "dpif.h" #include "dynamic-string.h" #include "in-band.h" #include "lacp.h" #include "learn.h" #include "list.h" #include "ovs-lldp.h" #include "mac-learning.h" #include "mcast-snooping.h" #include "meta-flow.h" #include "multipath.h" #include "netdev-vport.h" #include "netlink.h" #include "nx-match.h" #include "odp-execute.h" #include "ofp-actions.h" #include "ofproto/ofproto-dpif-ipfix.h" #include "ofproto/ofproto-dpif-mirror.h" #include "ofproto/ofproto-dpif-monitor.h" #include "ofproto/ofproto-dpif-sflow.h" #include "ofproto/ofproto-dpif.h" #include "ofproto/ofproto-provider.h" #include "packets.h" #include "ovs-router.h" #include "tnl-ports.h" #include "tunnel.h" #include "openvswitch/vlog.h" COVERAGE_DEFINE(xlate_actions); COVERAGE_DEFINE(xlate_actions_oversize); COVERAGE_DEFINE(xlate_actions_too_many_output); VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); /* Maximum depth of flow table recursion (due to resubmit actions) in a * flow translation. */ #define MAX_RESUBMIT_RECURSION 64 #define MAX_INTERNAL_RESUBMITS 1 /* Max resbmits allowed using rules in internal table. */ /* Maximum number of resubmit actions in a flow translation, whether they are * recursive or not. */ #define MAX_RESUBMITS (MAX_RESUBMIT_RECURSION * MAX_RESUBMIT_RECURSION) struct xbridge { struct hmap_node hmap_node; /* Node in global 'xbridges' map. */ struct ofproto_dpif *ofproto; /* Key in global 'xbridges' map. */ struct ovs_list xbundles; /* Owned xbundles. */ struct hmap xports; /* Indexed by ofp_port. */ char *name; /* Name used in log messages. */ struct dpif *dpif; /* Datapath interface. */ struct mac_learning *ml; /* Mac learning handle. */ struct mcast_snooping *ms; /* Multicast Snooping handle. */ struct mbridge *mbridge; /* Mirroring. */ struct dpif_sflow *sflow; /* SFlow handle, or null. */ struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */ struct netflow *netflow; /* Netflow handle, or null. */ struct stp *stp; /* STP or null if disabled. */ struct rstp *rstp; /* RSTP or null if disabled. */ bool has_in_band; /* Bridge has in band control? */ bool forward_bpdu; /* Bridge forwards STP BPDUs? */ /* Datapath feature support. */ struct dpif_backer_support support; }; struct xbundle { struct hmap_node hmap_node; /* In global 'xbundles' map. */ struct ofbundle *ofbundle; /* Key in global 'xbundles' map. */ struct ovs_list list_node; /* In parent 'xbridges' list. */ struct xbridge *xbridge; /* Parent xbridge. */ struct ovs_list xports; /* Contains "struct xport"s. */ char *name; /* Name used in log messages. */ struct bond *bond; /* Nonnull iff more than one port. */ struct lacp *lacp; /* LACP handle or null. */ enum port_vlan_mode vlan_mode; /* VLAN mode. */ int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */ unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1. * NULL if all VLANs are trunked. */ bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */ bool floodable; /* No port has OFPUTIL_PC_NO_FLOOD set? */ }; struct xport { struct hmap_node hmap_node; /* Node in global 'xports' map. */ struct ofport_dpif *ofport; /* Key in global 'xports map. */ struct hmap_node ofp_node; /* Node in parent xbridge 'xports' map. */ ofp_port_t ofp_port; /* Key in parent xbridge 'xports' map. */ odp_port_t odp_port; /* Datapath port number or ODPP_NONE. */ struct ovs_list bundle_node; /* In parent xbundle (if it exists). */ struct xbundle *xbundle; /* Parent xbundle or null. */ struct netdev *netdev; /* 'ofport''s netdev. */ struct xbridge *xbridge; /* Parent bridge. */ struct xport *peer; /* Patch port peer or null. */ enum ofputil_port_config config; /* OpenFlow port configuration. */ enum ofputil_port_state state; /* OpenFlow port state. */ int stp_port_no; /* STP port number or -1 if not in use. */ struct rstp_port *rstp_port; /* RSTP port or null. */ struct hmap skb_priorities; /* Map of 'skb_priority_to_dscp's. */ bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* Is a tunnel port. */ struct cfm *cfm; /* CFM handle or null. */ struct bfd *bfd; /* BFD handle or null. */ struct lldp *lldp; /* LLDP handle or null. */ }; struct xlate_ctx { struct xlate_in *xin; struct xlate_out *xout; const struct xbridge *xbridge; /* Flow tables version at the beginning of the translation. */ cls_version_t tables_version; /* Flow at the last commit. */ struct flow base_flow; /* Tunnel IP destination address as received. This is stored separately * as the base_flow.tunnel is cleared on init to reflect the datapath * behavior. Used to make sure not to send tunneled output to ourselves, * which might lead to an infinite loop. This could happen easily * if a tunnel is marked as 'ip_remote=flow', and the flow does not * actually set the tun_dst field. */ struct in6_addr orig_tunnel_ipv6_dst; /* Stack for the push and pop actions. Each stack element is of type * "union mf_subvalue". */ struct ofpbuf stack; /* The rule that we are currently translating, or NULL. */ struct rule_dpif *rule; /* Flow translation populates this with wildcards relevant in translation. * When 'xin->wc' is nonnull, this is the same pointer. When 'xin->wc' is * null, this is a pointer to a temporary buffer. */ struct flow_wildcards *wc; /* Output buffer for datapath actions. When 'xin->odp_actions' is nonnull, * this is the same pointer. When 'xin->odp_actions' is null, this points * to a scratch ofpbuf. This allows code to add actions to * 'ctx->odp_actions' without worrying about whether the caller really * wants actions. */ struct ofpbuf *odp_actions; /* Resubmit statistics, via xlate_table_action(). */ int recurse; /* Current resubmit nesting depth. */ int resubmits; /* Total number of resubmits. */ bool in_group; /* Currently translating ofgroup, if true. */ bool in_action_set; /* Currently translating action_set, if true. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ ovs_be64 rule_cookie; /* Cookie of the rule being translated. */ uint32_t orig_skb_priority; /* Priority when packet arrived. */ uint32_t sflow_n_outputs; /* Number of output ports. */ odp_port_t sflow_odp_port; /* Output port for composing sFlow action. */ ofp_port_t nf_output_iface; /* Output interface index for NetFlow. */ bool exit; /* No further actions should be processed. */ mirror_mask_t mirrors; /* Bitmap of associated mirrors. */ /* These are used for non-bond recirculation. The recirculation IDs are * stored in xout and must be associated with a datapath flow (ukey), * otherwise they will be freed when the xout is uninitialized. * * * Steps in Recirculation Translation * ================================== * * At some point during translation, the code recognizes the need for * recirculation. For example, recirculation is necessary when, after * popping the last MPLS label, an action or a match tries to examine or * modify a field that has been newly revealed following the MPLS label. * * The simplest part of the work to be done is to commit existing changes to * the packet, which produces datapath actions corresponding to the changes, * and after this, add an OVS_ACTION_ATTR_RECIRC datapath action. * * The main problem here is preserving state. When the datapath executes * OVS_ACTION_ATTR_RECIRC, it will upcall to userspace to get a translation * for the post-recirculation actions. At this point userspace has to * resume the translation where it left off, which means that it has to * execute the following: * * - The action that prompted recirculation, and any actions following * it within the same flow. * * - If the action that prompted recirculation was invoked within a * NXAST_RESUBMIT, then any actions following the resubmit. These * "resubmit"s can be nested, so this has to go all the way up the * control stack. * * - The OpenFlow 1.1+ action set. * * State that actions and flow table lookups can depend on, such as the * following, must also be preserved: * * - Metadata fields (input port, registers, OF1.1+ metadata, ...). * * - Action set, stack * * - The table ID and cookie of the flow being translated at each level * of the control stack (since OFPAT_CONTROLLER actions send these to * the controller). * * Translation allows for the control of this state preservation via these * members. When a need for recirculation is identified, the translation * process: * * 1. Sets 'recirc_action_offset' to the current size of 'action_set'. The * action set is part of what needs to be preserved, so this allows the * action set and the additional state to share the 'action_set' buffer. * Later steps can tell that setup for recirculation is in progress from * the nonnegative value of 'recirc_action_offset'. * * 2. Sets 'exit' to true to tell later steps that we're exiting from the * translation process. * * 3. Adds an OFPACT_UNROLL_XLATE action to 'action_set'. This action * holds the current table ID and cookie so that they can be restored * during a post-recirculation upcall translation. * * 4. Adds the action that prompted recirculation and any actions following * it within the same flow to 'action_set', so that they can be executed * during a post-recirculation upcall translation. * * 5. Returns. * * 6. The action that prompted recirculation might be nested in a stack of * nested "resubmit"s that have actions remaining. Each of these notices * that we're exiting (from 'exit') and that recirculation setup is in * progress (from 'recirc_action_offset') and responds by adding more * OFPACT_UNROLL_XLATE actions to 'action_set', as necessary, and any * actions that were yet unprocessed. * * The caller stores all the state produced by this process associated with * the recirculation ID. For post-recirculation upcall translation, the * caller passes it back in for the new translation to execute. The * process yielded a set of ofpacts that can be translated directly, so it * is not much of a special case at that point. */ int recirc_action_offset; /* Offset in 'action_set' to actions to be * executed after recirculation, or -1. */ int last_unroll_offset; /* Offset in 'action_set' to the latest unroll * action, or -1. */ /* True if a packet was but is no longer MPLS (due to an MPLS pop action). * This is a trigger for recirculation in cases where translating an action * or looking up a flow requires access to the fields of the packet after * the MPLS label stack that was originally present. */ bool was_mpls; /* True if conntrack has been performed on this packet during processing * on the current bridge. This is used to determine whether conntrack * state from the datapath should be honored after recirculation. */ bool conntracked; /* OpenFlow 1.1+ action set. * * 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS. * When translation is otherwise complete, ofpacts_execute_action_set() * converts it to a set of "struct ofpact"s that can be translated into * datapath actions. */ bool action_set_has_group; /* Action set contains OFPACT_GROUP? */ struct ofpbuf action_set; /* Action set. */ enum xlate_error error; /* Translation failed. */ }; const char *xlate_strerror(enum xlate_error error) { switch (error) { case XLATE_OK: return "OK"; case XLATE_BRIDGE_NOT_FOUND: return "Bridge not found"; case XLATE_RECURSION_TOO_DEEP: return "Recursion too deep"; case XLATE_TOO_MANY_RESUBMITS: return "Too many resubmits"; case XLATE_STACK_TOO_DEEP: return "Stack too deep"; case XLATE_NO_RECIRCULATION_CONTEXT: return "No recirculation context"; case XLATE_RECIRCULATION_CONFLICT: return "Recirculation conflict"; case XLATE_TOO_MANY_MPLS_LABELS: return "Too many MPLS labels"; } return "Unknown error"; } static void xlate_action_set(struct xlate_ctx *ctx); static void xlate_commit_actions(struct xlate_ctx *ctx); static void ctx_trigger_recirculation(struct xlate_ctx *ctx) { ctx->exit = true; ctx->recirc_action_offset = ctx->action_set.size; } static bool ctx_first_recirculation_action(const struct xlate_ctx *ctx) { return ctx->recirc_action_offset == ctx->action_set.size; } static inline bool exit_recirculates(const struct xlate_ctx *ctx) { /* When recirculating the 'recirc_action_offset' has a non-negative value. */ return ctx->recirc_action_offset >= 0; } static void compose_recirculate_action(struct xlate_ctx *ctx); /* A controller may use OFPP_NONE as the ingress port to indicate that * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for * when an input bundle is needed for validation (e.g., mirroring or * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have * any 'port' structs, so care must be taken when dealing with it. */ static struct xbundle ofpp_none_bundle = { .name = "OFPP_NONE", .vlan_mode = PORT_VLAN_TRUNK }; /* Node in 'xport''s 'skb_priorities' map. Used to maintain a map from * 'priority' (the datapath's term for QoS queue) to the dscp bits which all * traffic egressing the 'ofport' with that priority should be marked with. */ struct skb_priority_to_dscp { struct hmap_node hmap_node; /* Node in 'ofport_dpif''s 'skb_priorities'. */ uint32_t skb_priority; /* Priority of this queue (see struct flow). */ uint8_t dscp; /* DSCP bits to mark outgoing traffic with. */ }; enum xc_type { XC_RULE, XC_BOND, XC_NETDEV, XC_NETFLOW, XC_MIRROR, XC_LEARN, XC_NORMAL, XC_FIN_TIMEOUT, XC_GROUP, XC_TNL_NEIGH, }; /* xlate_cache entries hold enough information to perform the side effects of * xlate_actions() for a rule, without needing to perform rule translation * from scratch. The primary usage of these is to submit statistics to objects * that a flow relates to, although they may be used for other effects as well * (for instance, refreshing hard timeouts for learned flows). */ struct xc_entry { enum xc_type type; union { struct rule_dpif *rule; struct { struct netdev *tx; struct netdev *rx; struct bfd *bfd; } dev; struct { struct netflow *netflow; struct flow *flow; ofp_port_t iface; } nf; struct { struct mbridge *mbridge; mirror_mask_t mirrors; } mirror; struct { struct bond *bond; struct flow *flow; uint16_t vid; } bond; struct { struct ofproto_dpif *ofproto; struct ofputil_flow_mod *fm; struct ofpbuf *ofpacts; } learn; struct { struct ofproto_dpif *ofproto; struct flow *flow; int vlan; } normal; struct { struct rule_dpif *rule; uint16_t idle; uint16_t hard; } fin; struct { struct group_dpif *group; struct ofputil_bucket *bucket; } group; struct { char br_name[IFNAMSIZ]; struct in6_addr d_ipv6; } tnl_neigh_cache; } u; }; #define XC_ENTRY_FOR_EACH(entry, entries, xcache) \ entries = xcache->entries; \ for (entry = ofpbuf_try_pull(&entries, sizeof *entry); \ entry; \ entry = ofpbuf_try_pull(&entries, sizeof *entry)) struct xlate_cache { struct ofpbuf entries; }; /* Xlate config contains hash maps of all bridges, bundles and ports. * Xcfgp contains the pointer to the current xlate configuration. * When the main thread needs to change the configuration, it copies xcfgp to * new_xcfg and edits new_xcfg. This enables the use of RCU locking which * does not block handler and revalidator threads. */ struct xlate_cfg { struct hmap xbridges; struct hmap xbundles; struct hmap xports; }; static OVSRCU_TYPE(struct xlate_cfg *) xcfgp = OVSRCU_INITIALIZER(NULL); static struct xlate_cfg *new_xcfg = NULL; static bool may_receive(const struct xport *, struct xlate_ctx *); static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len, struct xlate_ctx *); static void xlate_normal(struct xlate_ctx *); static inline void xlate_report(struct xlate_ctx *, const char *, ...) OVS_PRINTF_FORMAT(2, 3); static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port, uint8_t table_id, bool may_packet_in, bool honor_table_miss); static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn); static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid); static void output_normal(struct xlate_ctx *, const struct xbundle *, uint16_t vlan); /* Optional bond recirculation parameter to compose_output_action(). */ struct xlate_bond_recirc { uint32_t recirc_id; /* !0 Use recirculation instead of output. */ uint8_t hash_alg; /* !0 Compute hash for recirc before. */ uint32_t hash_basis; /* Compute hash for recirc before. */ }; static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port, const struct xlate_bond_recirc *xr); static struct xbridge *xbridge_lookup(struct xlate_cfg *, const struct ofproto_dpif *); static struct xbundle *xbundle_lookup(struct xlate_cfg *, const struct ofbundle *); static struct xport *xport_lookup(struct xlate_cfg *, const struct ofport_dpif *); static struct xport *get_ofp_port(const struct xbridge *, ofp_port_t ofp_port); static struct skb_priority_to_dscp *get_skb_priority(const struct xport *, uint32_t skb_priority); static void clear_skb_priorities(struct xport *); static size_t count_skb_priorities(const struct xport *); static bool dscp_from_skb_priority(const struct xport *, uint32_t skb_priority, uint8_t *dscp); static struct xc_entry *xlate_cache_add_entry(struct xlate_cache *xc, enum xc_type type); static void xlate_xbridge_init(struct xlate_cfg *, struct xbridge *); static void xlate_xbundle_init(struct xlate_cfg *, struct xbundle *); static void xlate_xport_init(struct xlate_cfg *, struct xport *); static void xlate_xbridge_set(struct xbridge *, struct dpif *, const struct mac_learning *, struct stp *, struct rstp *, const struct mcast_snooping *, const struct mbridge *, const struct dpif_sflow *, const struct dpif_ipfix *, const struct netflow *, bool forward_bpdu, bool has_in_band, const struct dpif_backer_support *); static void xlate_xbundle_set(struct xbundle *xbundle, enum port_vlan_mode vlan_mode, int vlan, unsigned long *trunks, bool use_priority_tags, const struct bond *bond, const struct lacp *lacp, bool floodable); static void xlate_xport_set(struct xport *xport, odp_port_t odp_port, const struct netdev *netdev, const struct cfm *cfm, const struct bfd *bfd, const struct lldp *lldp, int stp_port_no, const struct rstp_port *rstp_port, enum ofputil_port_config config, enum ofputil_port_state state, bool is_tunnel, bool may_enable); static void xlate_xbridge_remove(struct xlate_cfg *, struct xbridge *); static void xlate_xbundle_remove(struct xlate_cfg *, struct xbundle *); static void xlate_xport_remove(struct xlate_cfg *, struct xport *); static void xlate_xbridge_copy(struct xbridge *); static void xlate_xbundle_copy(struct xbridge *, struct xbundle *); static void xlate_xport_copy(struct xbridge *, struct xbundle *, struct xport *); static void xlate_xcfg_free(struct xlate_cfg *); static inline void xlate_report(struct xlate_ctx *ctx, const char *format, ...) { if (OVS_UNLIKELY(ctx->xin->report_hook)) { va_list args; va_start(args, format); ctx->xin->report_hook(ctx->xin, ctx->recurse, format, args); va_end(args); } } static struct vlog_rate_limit error_report_rl = VLOG_RATE_LIMIT_INIT(1, 5); #define XLATE_REPORT_ERROR(CTX, ...) \ do { \ if (OVS_UNLIKELY((CTX)->xin->report_hook)) { \ xlate_report(CTX, __VA_ARGS__); \ } else { \ VLOG_ERR_RL(&error_report_rl, __VA_ARGS__); \ } \ } while (0) static inline void xlate_report_actions(struct xlate_ctx *ctx, const char *title, const struct ofpact *ofpacts, size_t ofpacts_len) { if (OVS_UNLIKELY(ctx->xin->report_hook)) { struct ds s = DS_EMPTY_INITIALIZER; ofpacts_format(ofpacts, ofpacts_len, &s); xlate_report(ctx, "%s: %s", title, ds_cstr(&s)); ds_destroy(&s); } } static void xlate_xbridge_init(struct xlate_cfg *xcfg, struct xbridge *xbridge) { list_init(&xbridge->xbundles); hmap_init(&xbridge->xports); hmap_insert(&xcfg->xbridges, &xbridge->hmap_node, hash_pointer(xbridge->ofproto, 0)); } static void xlate_xbundle_init(struct xlate_cfg *xcfg, struct xbundle *xbundle) { list_init(&xbundle->xports); list_insert(&xbundle->xbridge->xbundles, &xbundle->list_node); hmap_insert(&xcfg->xbundles, &xbundle->hmap_node, hash_pointer(xbundle->ofbundle, 0)); } static void xlate_xport_init(struct xlate_cfg *xcfg, struct xport *xport) { hmap_init(&xport->skb_priorities); hmap_insert(&xcfg->xports, &xport->hmap_node, hash_pointer(xport->ofport, 0)); hmap_insert(&xport->xbridge->xports, &xport->ofp_node, hash_ofp_port(xport->ofp_port)); } static void xlate_xbridge_set(struct xbridge *xbridge, struct dpif *dpif, const struct mac_learning *ml, struct stp *stp, struct rstp *rstp, const struct mcast_snooping *ms, const struct mbridge *mbridge, const struct dpif_sflow *sflow, const struct dpif_ipfix *ipfix, const struct netflow *netflow, bool forward_bpdu, bool has_in_band, const struct dpif_backer_support *support) { if (xbridge->ml != ml) { mac_learning_unref(xbridge->ml); xbridge->ml = mac_learning_ref(ml); } if (xbridge->ms != ms) { mcast_snooping_unref(xbridge->ms); xbridge->ms = mcast_snooping_ref(ms); } if (xbridge->mbridge != mbridge) { mbridge_unref(xbridge->mbridge); xbridge->mbridge = mbridge_ref(mbridge); } if (xbridge->sflow != sflow) { dpif_sflow_unref(xbridge->sflow); xbridge->sflow = dpif_sflow_ref(sflow); } if (xbridge->ipfix != ipfix) { dpif_ipfix_unref(xbridge->ipfix); xbridge->ipfix = dpif_ipfix_ref(ipfix); } if (xbridge->stp != stp) { stp_unref(xbridge->stp); xbridge->stp = stp_ref(stp); } if (xbridge->rstp != rstp) { rstp_unref(xbridge->rstp); xbridge->rstp = rstp_ref(rstp); } if (xbridge->netflow != netflow) { netflow_unref(xbridge->netflow); xbridge->netflow = netflow_ref(netflow); } xbridge->dpif = dpif; xbridge->forward_bpdu = forward_bpdu; xbridge->has_in_band = has_in_band; xbridge->support = *support; } static void xlate_xbundle_set(struct xbundle *xbundle, enum port_vlan_mode vlan_mode, int vlan, unsigned long *trunks, bool use_priority_tags, const struct bond *bond, const struct lacp *lacp, bool floodable) { ovs_assert(xbundle->xbridge); xbundle->vlan_mode = vlan_mode; xbundle->vlan = vlan; xbundle->trunks = trunks; xbundle->use_priority_tags = use_priority_tags; xbundle->floodable = floodable; if (xbundle->bond != bond) { bond_unref(xbundle->bond); xbundle->bond = bond_ref(bond); } if (xbundle->lacp != lacp) { lacp_unref(xbundle->lacp); xbundle->lacp = lacp_ref(lacp); } } static void xlate_xport_set(struct xport *xport, odp_port_t odp_port, const struct netdev *netdev, const struct cfm *cfm, const struct bfd *bfd, const struct lldp *lldp, int stp_port_no, const struct rstp_port* rstp_port, enum ofputil_port_config config, enum ofputil_port_state state, bool is_tunnel, bool may_enable) { xport->config = config; xport->state = state; xport->stp_port_no = stp_port_no; xport->is_tunnel = is_tunnel; xport->may_enable = may_enable; xport->odp_port = odp_port; if (xport->rstp_port != rstp_port) { rstp_port_unref(xport->rstp_port); xport->rstp_port = rstp_port_ref(rstp_port); } if (xport->cfm != cfm) { cfm_unref(xport->cfm); xport->cfm = cfm_ref(cfm); } if (xport->bfd != bfd) { bfd_unref(xport->bfd); xport->bfd = bfd_ref(bfd); } if (xport->lldp != lldp) { lldp_unref(xport->lldp); xport->lldp = lldp_ref(lldp); } if (xport->netdev != netdev) { netdev_close(xport->netdev); xport->netdev = netdev_ref(netdev); } } static void xlate_xbridge_copy(struct xbridge *xbridge) { struct xbundle *xbundle; struct xport *xport; struct xbridge *new_xbridge = xzalloc(sizeof *xbridge); new_xbridge->ofproto = xbridge->ofproto; new_xbridge->name = xstrdup(xbridge->name); xlate_xbridge_init(new_xcfg, new_xbridge); xlate_xbridge_set(new_xbridge, xbridge->dpif, xbridge->ml, xbridge->stp, xbridge->rstp, xbridge->ms, xbridge->mbridge, xbridge->sflow, xbridge->ipfix, xbridge->netflow, xbridge->forward_bpdu, xbridge->has_in_band, &xbridge->support); LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) { xlate_xbundle_copy(new_xbridge, xbundle); } /* Copy xports which are not part of a xbundle */ HMAP_FOR_EACH (xport, ofp_node, &xbridge->xports) { if (!xport->xbundle) { xlate_xport_copy(new_xbridge, NULL, xport); } } } static void xlate_xbundle_copy(struct xbridge *xbridge, struct xbundle *xbundle) { struct xport *xport; struct xbundle *new_xbundle = xzalloc(sizeof *xbundle); new_xbundle->ofbundle = xbundle->ofbundle; new_xbundle->xbridge = xbridge; new_xbundle->name = xstrdup(xbundle->name); xlate_xbundle_init(new_xcfg, new_xbundle); xlate_xbundle_set(new_xbundle, xbundle->vlan_mode, xbundle->vlan, xbundle->trunks, xbundle->use_priority_tags, xbundle->bond, xbundle->lacp, xbundle->floodable); LIST_FOR_EACH (xport, bundle_node, &xbundle->xports) { xlate_xport_copy(xbridge, new_xbundle, xport); } } static void xlate_xport_copy(struct xbridge *xbridge, struct xbundle *xbundle, struct xport *xport) { struct skb_priority_to_dscp *pdscp, *new_pdscp; struct xport *new_xport = xzalloc(sizeof *xport); new_xport->ofport = xport->ofport; new_xport->ofp_port = xport->ofp_port; new_xport->xbridge = xbridge; xlate_xport_init(new_xcfg, new_xport); xlate_xport_set(new_xport, xport->odp_port, xport->netdev, xport->cfm, xport->bfd, xport->lldp, xport->stp_port_no, xport->rstp_port, xport->config, xport->state, xport->is_tunnel, xport->may_enable); if (xport->peer) { struct xport *peer = xport_lookup(new_xcfg, xport->peer->ofport); if (peer) { new_xport->peer = peer; new_xport->peer->peer = new_xport; } } if (xbundle) { new_xport->xbundle = xbundle; list_insert(&new_xport->xbundle->xports, &new_xport->bundle_node); } HMAP_FOR_EACH (pdscp, hmap_node, &xport->skb_priorities) { new_pdscp = xmalloc(sizeof *pdscp); new_pdscp->skb_priority = pdscp->skb_priority; new_pdscp->dscp = pdscp->dscp; hmap_insert(&new_xport->skb_priorities, &new_pdscp->hmap_node, hash_int(new_pdscp->skb_priority, 0)); } } /* Sets the current xlate configuration to new_xcfg and frees the old xlate * configuration in xcfgp. * * This needs to be called after editing the xlate configuration. * * Functions that edit the new xlate configuration are * xlate__set and xlate__remove. * * A sample workflow: * * xlate_txn_start(); * ... * edit_xlate_configuration(); * ... * xlate_txn_commit(); */ void xlate_txn_commit(void) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); ovsrcu_set(&xcfgp, new_xcfg); ovsrcu_synchronize(); xlate_xcfg_free(xcfg); new_xcfg = NULL; } /* Copies the current xlate configuration in xcfgp to new_xcfg. * * This needs to be called prior to editing the xlate configuration. */ void xlate_txn_start(void) { struct xbridge *xbridge; struct xlate_cfg *xcfg; ovs_assert(!new_xcfg); new_xcfg = xmalloc(sizeof *new_xcfg); hmap_init(&new_xcfg->xbridges); hmap_init(&new_xcfg->xbundles); hmap_init(&new_xcfg->xports); xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); if (!xcfg) { return; } HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) { xlate_xbridge_copy(xbridge); } } static void xlate_xcfg_free(struct xlate_cfg *xcfg) { struct xbridge *xbridge, *next_xbridge; if (!xcfg) { return; } HMAP_FOR_EACH_SAFE (xbridge, next_xbridge, hmap_node, &xcfg->xbridges) { xlate_xbridge_remove(xcfg, xbridge); } hmap_destroy(&xcfg->xbridges); hmap_destroy(&xcfg->xbundles); hmap_destroy(&xcfg->xports); free(xcfg); } void xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, struct dpif *dpif, const struct mac_learning *ml, struct stp *stp, struct rstp *rstp, const struct mcast_snooping *ms, const struct mbridge *mbridge, const struct dpif_sflow *sflow, const struct dpif_ipfix *ipfix, const struct netflow *netflow, bool forward_bpdu, bool has_in_band, const struct dpif_backer_support *support) { struct xbridge *xbridge; ovs_assert(new_xcfg); xbridge = xbridge_lookup(new_xcfg, ofproto); if (!xbridge) { xbridge = xzalloc(sizeof *xbridge); xbridge->ofproto = ofproto; xlate_xbridge_init(new_xcfg, xbridge); } free(xbridge->name); xbridge->name = xstrdup(name); xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, ipfix, netflow, forward_bpdu, has_in_band, support); } static void xlate_xbridge_remove(struct xlate_cfg *xcfg, struct xbridge *xbridge) { struct xbundle *xbundle, *next_xbundle; struct xport *xport, *next_xport; if (!xbridge) { return; } HMAP_FOR_EACH_SAFE (xport, next_xport, ofp_node, &xbridge->xports) { xlate_xport_remove(xcfg, xport); } LIST_FOR_EACH_SAFE (xbundle, next_xbundle, list_node, &xbridge->xbundles) { xlate_xbundle_remove(xcfg, xbundle); } hmap_remove(&xcfg->xbridges, &xbridge->hmap_node); mac_learning_unref(xbridge->ml); mcast_snooping_unref(xbridge->ms); mbridge_unref(xbridge->mbridge); dpif_sflow_unref(xbridge->sflow); dpif_ipfix_unref(xbridge->ipfix); netflow_unref(xbridge->netflow); stp_unref(xbridge->stp); rstp_unref(xbridge->rstp); hmap_destroy(&xbridge->xports); free(xbridge->name); free(xbridge); } void xlate_remove_ofproto(struct ofproto_dpif *ofproto) { struct xbridge *xbridge; ovs_assert(new_xcfg); xbridge = xbridge_lookup(new_xcfg, ofproto); xlate_xbridge_remove(new_xcfg, xbridge); } void xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, const char *name, enum port_vlan_mode vlan_mode, int vlan, unsigned long *trunks, bool use_priority_tags, const struct bond *bond, const struct lacp *lacp, bool floodable) { struct xbundle *xbundle; ovs_assert(new_xcfg); xbundle = xbundle_lookup(new_xcfg, ofbundle); if (!xbundle) { xbundle = xzalloc(sizeof *xbundle); xbundle->ofbundle = ofbundle; xbundle->xbridge = xbridge_lookup(new_xcfg, ofproto); xlate_xbundle_init(new_xcfg, xbundle); } free(xbundle->name); xbundle->name = xstrdup(name); xlate_xbundle_set(xbundle, vlan_mode, vlan, trunks, use_priority_tags, bond, lacp, floodable); } static void xlate_xbundle_remove(struct xlate_cfg *xcfg, struct xbundle *xbundle) { struct xport *xport; if (!xbundle) { return; } LIST_FOR_EACH_POP (xport, bundle_node, &xbundle->xports) { xport->xbundle = NULL; } hmap_remove(&xcfg->xbundles, &xbundle->hmap_node); list_remove(&xbundle->list_node); bond_unref(xbundle->bond); lacp_unref(xbundle->lacp); free(xbundle->name); free(xbundle); } void xlate_bundle_remove(struct ofbundle *ofbundle) { struct xbundle *xbundle; ovs_assert(new_xcfg); xbundle = xbundle_lookup(new_xcfg, ofbundle); xlate_xbundle_remove(new_xcfg, xbundle); } void xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, struct ofport_dpif *ofport, ofp_port_t ofp_port, odp_port_t odp_port, const struct netdev *netdev, const struct cfm *cfm, const struct bfd *bfd, const struct lldp *lldp, struct ofport_dpif *peer, int stp_port_no, const struct rstp_port *rstp_port, const struct ofproto_port_queue *qdscp_list, size_t n_qdscp, enum ofputil_port_config config, enum ofputil_port_state state, bool is_tunnel, bool may_enable) { size_t i; struct xport *xport; ovs_assert(new_xcfg); xport = xport_lookup(new_xcfg, ofport); if (!xport) { xport = xzalloc(sizeof *xport); xport->ofport = ofport; xport->xbridge = xbridge_lookup(new_xcfg, ofproto); xport->ofp_port = ofp_port; xlate_xport_init(new_xcfg, xport); } ovs_assert(xport->ofp_port == ofp_port); xlate_xport_set(xport, odp_port, netdev, cfm, bfd, lldp, stp_port_no, rstp_port, config, state, is_tunnel, may_enable); if (xport->peer) { xport->peer->peer = NULL; } xport->peer = xport_lookup(new_xcfg, peer); if (xport->peer) { xport->peer->peer = xport; } if (xport->xbundle) { list_remove(&xport->bundle_node); } xport->xbundle = xbundle_lookup(new_xcfg, ofbundle); if (xport->xbundle) { list_insert(&xport->xbundle->xports, &xport->bundle_node); } clear_skb_priorities(xport); for (i = 0; i < n_qdscp; i++) { struct skb_priority_to_dscp *pdscp; uint32_t skb_priority; if (dpif_queue_to_priority(xport->xbridge->dpif, qdscp_list[i].queue, &skb_priority)) { continue; } pdscp = xmalloc(sizeof *pdscp); pdscp->skb_priority = skb_priority; pdscp->dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK; hmap_insert(&xport->skb_priorities, &pdscp->hmap_node, hash_int(pdscp->skb_priority, 0)); } } static void xlate_xport_remove(struct xlate_cfg *xcfg, struct xport *xport) { if (!xport) { return; } if (xport->peer) { xport->peer->peer = NULL; xport->peer = NULL; } if (xport->xbundle) { list_remove(&xport->bundle_node); } clear_skb_priorities(xport); hmap_destroy(&xport->skb_priorities); hmap_remove(&xcfg->xports, &xport->hmap_node); hmap_remove(&xport->xbridge->xports, &xport->ofp_node); netdev_close(xport->netdev); rstp_port_unref(xport->rstp_port); cfm_unref(xport->cfm); bfd_unref(xport->bfd); lldp_unref(xport->lldp); free(xport); } void xlate_ofport_remove(struct ofport_dpif *ofport) { struct xport *xport; ovs_assert(new_xcfg); xport = xport_lookup(new_xcfg, ofport); xlate_xport_remove(new_xcfg, xport); } static struct ofproto_dpif * xlate_lookup_ofproto_(const struct dpif_backer *backer, const struct flow *flow, ofp_port_t *ofp_in_port, const struct xport **xportp) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); const struct xport *xport; xport = xport_lookup(xcfg, tnl_port_should_receive(flow) ? tnl_port_receive(flow) : odp_port_to_ofport(backer, flow->in_port.odp_port)); if (OVS_UNLIKELY(!xport)) { return NULL; } *xportp = xport; if (ofp_in_port) { *ofp_in_port = xport->ofp_port; } return xport->xbridge->ofproto; } /* Given a datapath and flow metadata ('backer', and 'flow' respectively) * returns the corresponding struct ofproto_dpif and OpenFlow port number. */ struct ofproto_dpif * xlate_lookup_ofproto(const struct dpif_backer *backer, const struct flow *flow, ofp_port_t *ofp_in_port) { const struct xport *xport; return xlate_lookup_ofproto_(backer, flow, ofp_in_port, &xport); } /* Given a datapath and flow metadata ('backer', and 'flow' respectively), * optionally populates 'ofproto' with the ofproto_dpif, 'ofp_in_port' with the * openflow in_port, and 'ipfix', 'sflow', and 'netflow' with the appropriate * handles for those protocols if they're enabled. Caller may use the returned * pointers until quiescing, for longer term use additional references must * be taken. * * Returns 0 if successful, ENODEV if the parsed flow has no associated ofproto. */ int xlate_lookup(const struct dpif_backer *backer, const struct flow *flow, struct ofproto_dpif **ofprotop, struct dpif_ipfix **ipfix, struct dpif_sflow **sflow, struct netflow **netflow, ofp_port_t *ofp_in_port) { struct ofproto_dpif *ofproto; const struct xport *xport; ofproto = xlate_lookup_ofproto_(backer, flow, ofp_in_port, &xport); if (!ofproto) { return ENODEV; } if (ofprotop) { *ofprotop = ofproto; } if (ipfix) { *ipfix = xport ? xport->xbridge->ipfix : NULL; } if (sflow) { *sflow = xport ? xport->xbridge->sflow : NULL; } if (netflow) { *netflow = xport ? xport->xbridge->netflow : NULL; } return 0; } static struct xbridge * xbridge_lookup(struct xlate_cfg *xcfg, const struct ofproto_dpif *ofproto) { struct hmap *xbridges; struct xbridge *xbridge; if (!ofproto || !xcfg) { return NULL; } xbridges = &xcfg->xbridges; HMAP_FOR_EACH_IN_BUCKET (xbridge, hmap_node, hash_pointer(ofproto, 0), xbridges) { if (xbridge->ofproto == ofproto) { return xbridge; } } return NULL; } static struct xbundle * xbundle_lookup(struct xlate_cfg *xcfg, const struct ofbundle *ofbundle) { struct hmap *xbundles; struct xbundle *xbundle; if (!ofbundle || !xcfg) { return NULL; } xbundles = &xcfg->xbundles; HMAP_FOR_EACH_IN_BUCKET (xbundle, hmap_node, hash_pointer(ofbundle, 0), xbundles) { if (xbundle->ofbundle == ofbundle) { return xbundle; } } return NULL; } static struct xport * xport_lookup(struct xlate_cfg *xcfg, const struct ofport_dpif *ofport) { struct hmap *xports; struct xport *xport; if (!ofport || !xcfg) { return NULL; } xports = &xcfg->xports; HMAP_FOR_EACH_IN_BUCKET (xport, hmap_node, hash_pointer(ofport, 0), xports) { if (xport->ofport == ofport) { return xport; } } return NULL; } static struct stp_port * xport_get_stp_port(const struct xport *xport) { return xport->xbridge->stp && xport->stp_port_no != -1 ? stp_get_port(xport->xbridge->stp, xport->stp_port_no) : NULL; } static bool xport_stp_learn_state(const struct xport *xport) { struct stp_port *sp = xport_get_stp_port(xport); return sp ? stp_learn_in_state(stp_port_get_state(sp)) : true; } static bool xport_stp_forward_state(const struct xport *xport) { struct stp_port *sp = xport_get_stp_port(xport); return sp ? stp_forward_in_state(stp_port_get_state(sp)) : true; } static bool xport_stp_should_forward_bpdu(const struct xport *xport) { struct stp_port *sp = xport_get_stp_port(xport); return stp_should_forward_bpdu(sp ? stp_port_get_state(sp) : STP_DISABLED); } /* Returns true if STP should process 'flow'. Sets fields in 'wc' that * were used to make the determination.*/ static bool stp_should_process_flow(const struct flow *flow, struct flow_wildcards *wc) { /* is_stp() also checks dl_type, but dl_type is always set in 'wc'. */ memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); return is_stp(flow); } static void stp_process_packet(const struct xport *xport, const struct dp_packet *packet) { struct stp_port *sp = xport_get_stp_port(xport); struct dp_packet payload = *packet; struct eth_header *eth = dp_packet_data(&payload); /* Sink packets on ports that have STP disabled when the bridge has * STP enabled. */ if (!sp || stp_port_get_state(sp) == STP_DISABLED) { return; } /* Trim off padding on payload. */ if (dp_packet_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) { dp_packet_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN); } if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload)); } } static enum rstp_state xport_get_rstp_port_state(const struct xport *xport) { return xport->rstp_port ? rstp_port_get_state(xport->rstp_port) : RSTP_DISABLED; } static bool xport_rstp_learn_state(const struct xport *xport) { return xport->xbridge->rstp && xport->rstp_port ? rstp_learn_in_state(xport_get_rstp_port_state(xport)) : true; } static bool xport_rstp_forward_state(const struct xport *xport) { return xport->xbridge->rstp && xport->rstp_port ? rstp_forward_in_state(xport_get_rstp_port_state(xport)) : true; } static bool xport_rstp_should_manage_bpdu(const struct xport *xport) { return rstp_should_manage_bpdu(xport_get_rstp_port_state(xport)); } static void rstp_process_packet(const struct xport *xport, const struct dp_packet *packet) { struct dp_packet payload = *packet; struct eth_header *eth = dp_packet_data(&payload); /* Sink packets on ports that have no RSTP. */ if (!xport->rstp_port) { return; } /* Trim off padding on payload. */ if (dp_packet_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) { dp_packet_set_size(&payload, ntohs(eth->eth_type) + ETH_HEADER_LEN); } if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload), dp_packet_size(&payload)); } } static struct xport * get_ofp_port(const struct xbridge *xbridge, ofp_port_t ofp_port) { struct xport *xport; HMAP_FOR_EACH_IN_BUCKET (xport, ofp_node, hash_ofp_port(ofp_port), &xbridge->xports) { if (xport->ofp_port == ofp_port) { return xport; } } return NULL; } static odp_port_t ofp_port_to_odp_port(const struct xbridge *xbridge, ofp_port_t ofp_port) { const struct xport *xport = get_ofp_port(xbridge, ofp_port); return xport ? xport->odp_port : ODPP_NONE; } static bool odp_port_is_alive(const struct xlate_ctx *ctx, ofp_port_t ofp_port) { struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port); return xport && xport->may_enable; } static struct ofputil_bucket * group_first_live_bucket(const struct xlate_ctx *, const struct group_dpif *, int depth); static bool group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth) { struct group_dpif *group; if (group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group)) { struct ofputil_bucket *bucket; bucket = group_first_live_bucket(ctx, group, depth); group_dpif_unref(group); return bucket != NULL; } return false; } #define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */ static bool bucket_is_alive(const struct xlate_ctx *ctx, struct ofputil_bucket *bucket, int depth) { if (depth >= MAX_LIVENESS_RECURSION) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "bucket chaining exceeded %d links", MAX_LIVENESS_RECURSION); return false; } return (!ofputil_bucket_has_liveness(bucket) || (bucket->watch_port != OFPP_ANY && odp_port_is_alive(ctx, bucket->watch_port)) || (bucket->watch_group != OFPG_ANY && group_is_alive(ctx, bucket->watch_group, depth + 1))); } static struct ofputil_bucket * group_first_live_bucket(const struct xlate_ctx *ctx, const struct group_dpif *group, int depth) { struct ofputil_bucket *bucket; const struct ovs_list *buckets; group_dpif_get_buckets(group, &buckets); LIST_FOR_EACH (bucket, list_node, buckets) { if (bucket_is_alive(ctx, bucket, depth)) { return bucket; } } return NULL; } static struct ofputil_bucket * group_best_live_bucket(const struct xlate_ctx *ctx, const struct group_dpif *group, uint32_t basis) { struct ofputil_bucket *best_bucket = NULL; uint32_t best_score = 0; struct ofputil_bucket *bucket; const struct ovs_list *buckets; group_dpif_get_buckets(group, &buckets); LIST_FOR_EACH (bucket, list_node, buckets) { if (bucket_is_alive(ctx, bucket, 0)) { uint32_t score = (hash_int(bucket->bucket_id, basis) & 0xffff) * bucket->weight; if (score >= best_score) { best_bucket = bucket; best_score = score; } } } return best_bucket; } static bool xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan) { return (bundle->vlan_mode != PORT_VLAN_ACCESS && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan))); } static bool xbundle_includes_vlan(const struct xbundle *xbundle, uint16_t vlan) { return vlan == xbundle->vlan || xbundle_trunks_vlan(xbundle, vlan); } static mirror_mask_t xbundle_mirror_out(const struct xbridge *xbridge, struct xbundle *xbundle) { return xbundle != &ofpp_none_bundle ? mirror_bundle_out(xbridge->mbridge, xbundle->ofbundle) : 0; } static mirror_mask_t xbundle_mirror_src(const struct xbridge *xbridge, struct xbundle *xbundle) { return xbundle != &ofpp_none_bundle ? mirror_bundle_src(xbridge->mbridge, xbundle->ofbundle) : 0; } static mirror_mask_t xbundle_mirror_dst(const struct xbridge *xbridge, struct xbundle *xbundle) { return xbundle != &ofpp_none_bundle ? mirror_bundle_dst(xbridge->mbridge, xbundle->ofbundle) : 0; } static struct xbundle * lookup_input_bundle(const struct xbridge *xbridge, ofp_port_t in_port, bool warn, struct xport **in_xportp) { struct xport *xport; /* Find the port and bundle for the received packet. */ xport = get_ofp_port(xbridge, in_port); if (in_xportp) { *in_xportp = xport; } if (xport && xport->xbundle) { return xport->xbundle; } /* Special-case OFPP_NONE (OF1.0) and OFPP_CONTROLLER (OF1.1+), * which a controller may use as the ingress port for traffic that * it is sourcing. */ if (in_port == OFPP_CONTROLLER || in_port == OFPP_NONE) { return &ofpp_none_bundle; } /* Odd. A few possible reasons here: * * - We deleted a port but there are still a few packets queued up * from it. * * - Someone externally added a port (e.g. "ovs-dpctl add-if") that * we don't know about. * * - The ofproto client didn't configure the port as part of a bundle. * This is particularly likely to happen if a packet was received on the * port after it was created, but before the client had a chance to * configure its bundle. */ if (warn) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown " "port %"PRIu16, xbridge->name, in_port); } return NULL; } /* Mirrors the packet represented by 'ctx' to appropriate mirror destinations, * given the packet is ingressing or egressing on 'xbundle', which has ingress * or egress (as appropriate) mirrors 'mirrors'. */ static void mirror_packet(struct xlate_ctx *ctx, struct xbundle *xbundle, mirror_mask_t mirrors) { /* Figure out what VLAN the packet is in (because mirrors can select * packets on basis of VLAN). */ bool warn = ctx->xin->packet != NULL; uint16_t vid = vlan_tci_to_vid(ctx->xin->flow.vlan_tci); if (!input_vid_is_valid(vid, xbundle, warn)) { return; } uint16_t vlan = input_vid_to_vlan(xbundle, vid); const struct xbridge *xbridge = ctx->xbridge; /* Don't mirror to destinations that we've already mirrored to. */ mirrors &= ~ctx->mirrors; if (!mirrors) { return; } if (ctx->xin->resubmit_stats) { mirror_update_stats(xbridge->mbridge, mirrors, ctx->xin->resubmit_stats->n_packets, ctx->xin->resubmit_stats->n_bytes); } if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_MIRROR); entry->u.mirror.mbridge = mbridge_ref(xbridge->mbridge); entry->u.mirror.mirrors = mirrors; } /* 'mirrors' is a bit-mask of candidates for mirroring. Iterate as long as * some candidates remain. */ while (mirrors) { const unsigned long *vlans; mirror_mask_t dup_mirrors; struct ofbundle *out; int out_vlan; /* Get the details of the mirror represented by the rightmost 1-bit. */ bool has_mirror = mirror_get(xbridge->mbridge, raw_ctz(mirrors), &vlans, &dup_mirrors, &out, &out_vlan); ovs_assert(has_mirror); /* If this mirror selects on the basis of VLAN, and it does not select * 'vlan', then discard this mirror and go on to the next one. */ if (vlans) { ctx->wc->masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK); } if (vlans && !bitmap_is_set(vlans, vlan)) { mirrors = zero_rightmost_1bit(mirrors); continue; } /* Record the mirror, and the mirrors that output to the same * destination, so that we don't mirror to them again. This must be * done now to ensure that output_normal(), below, doesn't recursively * output to the same mirrors. */ ctx->mirrors |= dup_mirrors; /* Send the packet to the mirror. */ if (out) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xbundle *out_xbundle = xbundle_lookup(xcfg, out); if (out_xbundle) { output_normal(ctx, out_xbundle, vlan); } } else if (vlan != out_vlan && !eth_addr_is_reserved(ctx->xin->flow.dl_dst)) { struct xbundle *xbundle; LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) { if (xbundle_includes_vlan(xbundle, out_vlan) && !xbundle_mirror_out(xbridge, xbundle)) { output_normal(ctx, xbundle, out_vlan); } } } /* output_normal() could have recursively output (to different * mirrors), so make sure that we don't send duplicates. */ mirrors &= ~ctx->mirrors; } } static void mirror_ingress_packet(struct xlate_ctx *ctx) { if (mbridge_has_mirrors(ctx->xbridge->mbridge)) { bool warn = ctx->xin->packet != NULL; struct xbundle *xbundle = lookup_input_bundle( ctx->xbridge, ctx->xin->flow.in_port.ofp_port, warn, NULL); if (xbundle) { mirror_packet(ctx, xbundle, xbundle_mirror_src(ctx->xbridge, xbundle)); } } } /* Given 'vid', the VID obtained from the 802.1Q header that was received as * part of a packet (specify 0 if there was no 802.1Q header), and 'in_xbundle', * the bundle on which the packet was received, returns the VLAN to which the * packet belongs. * * Both 'vid' and the return value are in the range 0...4095. */ static uint16_t input_vid_to_vlan(const struct xbundle *in_xbundle, uint16_t vid) { switch (in_xbundle->vlan_mode) { case PORT_VLAN_ACCESS: return in_xbundle->vlan; break; case PORT_VLAN_TRUNK: return vid; case PORT_VLAN_NATIVE_UNTAGGED: case PORT_VLAN_NATIVE_TAGGED: return vid ? vid : in_xbundle->vlan; default: OVS_NOT_REACHED(); } } /* Checks whether a packet with the given 'vid' may ingress on 'in_xbundle'. * If so, returns true. Otherwise, returns false and, if 'warn' is true, logs * a warning. * * 'vid' should be the VID obtained from the 802.1Q header that was received as * part of a packet (specify 0 if there was no 802.1Q header), in the range * 0...4095. */ static bool input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn) { /* Allow any VID on the OFPP_NONE port. */ if (in_xbundle == &ofpp_none_bundle) { return true; } switch (in_xbundle->vlan_mode) { case PORT_VLAN_ACCESS: if (vid) { if (warn) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" tagged " "packet received on port %s configured as VLAN " "%"PRIu16" access port", vid, in_xbundle->name, in_xbundle->vlan); } return false; } return true; case PORT_VLAN_NATIVE_UNTAGGED: case PORT_VLAN_NATIVE_TAGGED: if (!vid) { /* Port must always carry its native VLAN. */ return true; } /* Fall through. */ case PORT_VLAN_TRUNK: if (!xbundle_includes_vlan(in_xbundle, vid)) { if (warn) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" packet " "received on port %s not configured for trunking " "VLAN %"PRIu16, vid, in_xbundle->name, vid); } return false; } return true; default: OVS_NOT_REACHED(); } } /* Given 'vlan', the VLAN that a packet belongs to, and * 'out_xbundle', a bundle on which the packet is to be output, returns the VID * that should be included in the 802.1Q header. (If the return value is 0, * then the 802.1Q header should only be included in the packet if there is a * nonzero PCP.) * * Both 'vlan' and the return value are in the range 0...4095. */ static uint16_t output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan) { switch (out_xbundle->vlan_mode) { case PORT_VLAN_ACCESS: return 0; case PORT_VLAN_TRUNK: case PORT_VLAN_NATIVE_TAGGED: return vlan; case PORT_VLAN_NATIVE_UNTAGGED: return vlan == out_xbundle->vlan ? 0 : vlan; default: OVS_NOT_REACHED(); } } static void output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle, uint16_t vlan) { ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci; uint16_t vid; ovs_be16 tci, old_tci; struct xport *xport; struct xlate_bond_recirc xr; bool use_recirc = false; vid = output_vlan_to_vid(out_xbundle, vlan); if (list_is_empty(&out_xbundle->xports)) { /* Partially configured bundle with no slaves. Drop the packet. */ return; } else if (!out_xbundle->bond) { xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport, bundle_node); } else { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct flow_wildcards *wc = ctx->wc; struct ofport_dpif *ofport; if (ctx->xbridge->support.odp.recirc && bond_may_recirc(out_xbundle->bond, NULL, NULL)) { /* To avoid unnecessary locking, bond_may_recirc() is first * called outside of the 'rwlock'. After acquiring the lock, * bond_update_post_recirc_rules() will check again to make * sure bond configuration has not been changed. * * In case recirculation is not actually in use, 'xr.recirc_id' * will be set to '0', Since a valid 'recirc_id' can * not be zero. */ bond_update_post_recirc_rules(out_xbundle->bond, &xr.recirc_id, &xr.hash_basis); if (xr.recirc_id) { /* Use recirculation instead of output. */ use_recirc = true; xr.hash_alg = OVS_HASH_ALG_L4; /* Recirculation does not require unmasking hash fields. */ wc = NULL; } } ofport = bond_choose_output_slave(out_xbundle->bond, &ctx->xin->flow, wc, vid); xport = xport_lookup(xcfg, ofport); if (!xport) { /* No slaves enabled, so drop packet. */ return; } /* If use_recirc is set, the main thread will handle stats * accounting for this bond. */ if (!use_recirc) { if (ctx->xin->resubmit_stats) { bond_account(out_xbundle->bond, &ctx->xin->flow, vid, ctx->xin->resubmit_stats->n_bytes); } if (ctx->xin->xcache) { struct xc_entry *entry; struct flow *flow; flow = &ctx->xin->flow; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_BOND); entry->u.bond.bond = bond_ref(out_xbundle->bond); entry->u.bond.flow = xmemdup(flow, sizeof *flow); entry->u.bond.vid = vid; } } } old_tci = *flow_tci; tci = htons(vid); if (tci || out_xbundle->use_priority_tags) { tci |= *flow_tci & htons(VLAN_PCP_MASK); if (tci) { tci |= htons(VLAN_CFI); } } *flow_tci = tci; compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL); *flow_tci = old_tci; } /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to * indicate this; newer upstream kernels use gratuitous ARP requests. */ static bool is_gratuitous_arp(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type != htons(ETH_TYPE_ARP)) { return false; } memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); if (!eth_addr_is_broadcast(flow->dl_dst)) { return false; } memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); if (flow->nw_proto == ARP_OP_REPLY) { return true; } else if (flow->nw_proto == ARP_OP_REQUEST) { memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); return flow->nw_src == flow->nw_dst; } else { return false; } } /* Determines whether packets in 'flow' within 'xbridge' should be forwarded or * dropped. Returns true if they may be forwarded, false if they should be * dropped. * * 'in_port' must be the xport that corresponds to flow->in_port. * 'in_port' must be part of a bundle (e.g. in_port->bundle must be nonnull). * * 'vlan' must be the VLAN that corresponds to flow->vlan_tci on 'in_port', as * returned by input_vid_to_vlan(). It must be a valid VLAN for 'in_port', as * checked by input_vid_is_valid(). * * May also add tags to '*tags', although the current implementation only does * so in one special case. */ static bool is_admissible(struct xlate_ctx *ctx, struct xport *in_port, uint16_t vlan) { struct xbundle *in_xbundle = in_port->xbundle; const struct xbridge *xbridge = ctx->xbridge; struct flow *flow = &ctx->xin->flow; /* Drop frames for reserved multicast addresses * only if forward_bpdu option is absent. */ if (!xbridge->forward_bpdu && eth_addr_is_reserved(flow->dl_dst)) { xlate_report(ctx, "packet has reserved destination MAC, dropping"); return false; } if (in_xbundle->bond) { struct mac_entry *mac; switch (bond_check_admissibility(in_xbundle->bond, in_port->ofport, flow->dl_dst)) { case BV_ACCEPT: break; case BV_DROP: xlate_report(ctx, "bonding refused admissibility, dropping"); return false; case BV_DROP_IF_MOVED: ovs_rwlock_rdlock(&xbridge->ml->rwlock); mac = mac_learning_lookup(xbridge->ml, flow->dl_src, vlan); if (mac && mac_entry_get_port(xbridge->ml, mac) != in_xbundle->ofbundle && (!is_gratuitous_arp(flow, ctx->wc) || mac_entry_is_grat_arp_locked(mac))) { ovs_rwlock_unlock(&xbridge->ml->rwlock); xlate_report(ctx, "SLB bond thinks this packet looped back, " "dropping"); return false; } ovs_rwlock_unlock(&xbridge->ml->rwlock); break; } } return true; } /* Checks whether a MAC learning update is necessary for MAC learning table * 'ml' given that a packet matching 'flow' was received on 'in_xbundle' in * 'vlan'. * * Most packets processed through the MAC learning table do not actually * change it in any way. This function requires only a read lock on the MAC * learning table, so it is much cheaper in this common case. * * Keep the code here synchronized with that in update_learning_table__() * below. */ static bool is_mac_learning_update_needed(const struct mac_learning *ml, const struct flow *flow, struct flow_wildcards *wc, int vlan, struct xbundle *in_xbundle) OVS_REQ_RDLOCK(ml->rwlock) { struct mac_entry *mac; if (!mac_learning_may_learn(ml, flow->dl_src, vlan)) { return false; } mac = mac_learning_lookup(ml, flow->dl_src, vlan); if (!mac || mac_entry_age(ml, mac)) { return true; } if (is_gratuitous_arp(flow, wc)) { /* We don't want to learn from gratuitous ARP packets that are * reflected back over bond slaves so we lock the learning table. */ if (!in_xbundle->bond) { return true; } else if (mac_entry_is_grat_arp_locked(mac)) { return false; } } return mac_entry_get_port(ml, mac) != in_xbundle->ofbundle; } /* Updates MAC learning table 'ml' given that a packet matching 'flow' was * received on 'in_xbundle' in 'vlan'. * * This code repeats all the checks in is_mac_learning_update_needed() because * the lock was released between there and here and thus the MAC learning state * could have changed. * * Keep the code here synchronized with that in is_mac_learning_update_needed() * above. */ static void update_learning_table__(const struct xbridge *xbridge, const struct flow *flow, struct flow_wildcards *wc, int vlan, struct xbundle *in_xbundle) OVS_REQ_WRLOCK(xbridge->ml->rwlock) { struct mac_entry *mac; if (!mac_learning_may_learn(xbridge->ml, flow->dl_src, vlan)) { return; } mac = mac_learning_insert(xbridge->ml, flow->dl_src, vlan); if (is_gratuitous_arp(flow, wc)) { /* We don't want to learn from gratuitous ARP packets that are * reflected back over bond slaves so we lock the learning table. */ if (!in_xbundle->bond) { mac_entry_set_grat_arp_lock(mac); } else if (mac_entry_is_grat_arp_locked(mac)) { return; } } if (mac_entry_get_port(xbridge->ml, mac) != in_xbundle->ofbundle) { /* The log messages here could actually be useful in debugging, * so keep the rate limit relatively high. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is " "on port %s in VLAN %d", xbridge->name, ETH_ADDR_ARGS(flow->dl_src), in_xbundle->name, vlan); mac_entry_set_port(xbridge->ml, mac, in_xbundle->ofbundle); } } static void update_learning_table(const struct xbridge *xbridge, const struct flow *flow, struct flow_wildcards *wc, int vlan, struct xbundle *in_xbundle) { bool need_update; /* Don't learn the OFPP_NONE port. */ if (in_xbundle == &ofpp_none_bundle) { return; } /* First try the common case: no change to MAC learning table. */ ovs_rwlock_rdlock(&xbridge->ml->rwlock); need_update = is_mac_learning_update_needed(xbridge->ml, flow, wc, vlan, in_xbundle); ovs_rwlock_unlock(&xbridge->ml->rwlock); if (need_update) { /* Slow path: MAC learning table might need an update. */ ovs_rwlock_wrlock(&xbridge->ml->rwlock); update_learning_table__(xbridge, flow, wc, vlan, in_xbundle); ovs_rwlock_unlock(&xbridge->ml->rwlock); } } /* Updates multicast snooping table 'ms' given that a packet matching 'flow' * was received on 'in_xbundle' in 'vlan' and is either Report or Query. */ static void update_mcast_snooping_table4__(const struct xbridge *xbridge, const struct flow *flow, struct mcast_snooping *ms, int vlan, struct xbundle *in_xbundle, const struct dp_packet *packet) OVS_REQ_WRLOCK(ms->rwlock) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30); int count; ovs_be32 ip4 = flow->igmp_group_ip4; switch (ntohs(flow->tp_src)) { case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPV2_HOST_MEMBERSHIP_REPORT: if (mcast_snooping_add_group4(ms, ip4, vlan, in_xbundle->ofbundle)) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping learned that " IP_FMT" is on port %s in VLAN %d", xbridge->name, IP_ARGS(ip4), in_xbundle->name, vlan); } break; case IGMP_HOST_LEAVE_MESSAGE: if (mcast_snooping_leave_group4(ms, ip4, vlan, in_xbundle->ofbundle)) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping leaving " IP_FMT" is on port %s in VLAN %d", xbridge->name, IP_ARGS(ip4), in_xbundle->name, vlan); } break; case IGMP_HOST_MEMBERSHIP_QUERY: if (flow->nw_src && mcast_snooping_add_mrouter(ms, vlan, in_xbundle->ofbundle)) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query from " IP_FMT" is on port %s in VLAN %d", xbridge->name, IP_ARGS(flow->nw_src), in_xbundle->name, vlan); } break; case IGMPV3_HOST_MEMBERSHIP_REPORT: if ((count = mcast_snooping_add_report(ms, packet, vlan, in_xbundle->ofbundle))) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d " "addresses on port %s in VLAN %d", xbridge->name, count, in_xbundle->name, vlan); } break; } } static void update_mcast_snooping_table6__(const struct xbridge *xbridge, const struct flow *flow, struct mcast_snooping *ms, int vlan, struct xbundle *in_xbundle, const struct dp_packet *packet) OVS_REQ_WRLOCK(ms->rwlock) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30); int count; switch (ntohs(flow->tp_src)) { case MLD_QUERY: if (!ipv6_addr_equals(&flow->ipv6_src, &in6addr_any) && mcast_snooping_add_mrouter(ms, vlan, in_xbundle->ofbundle)) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query on port %s" "in VLAN %d", xbridge->name, in_xbundle->name, vlan); } break; case MLD_REPORT: case MLD_DONE: case MLD2_REPORT: count = mcast_snooping_add_mld(ms, packet, vlan, in_xbundle->ofbundle); if (count) { VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d " "addresses on port %s in VLAN %d", xbridge->name, count, in_xbundle->name, vlan); } break; } } /* Updates multicast snooping table 'ms' given that a packet matching 'flow' * was received on 'in_xbundle' in 'vlan'. */ static void update_mcast_snooping_table(const struct xbridge *xbridge, const struct flow *flow, int vlan, struct xbundle *in_xbundle, const struct dp_packet *packet) { struct mcast_snooping *ms = xbridge->ms; struct xlate_cfg *xcfg; struct xbundle *mcast_xbundle; struct mcast_port_bundle *fport; /* Don't learn the OFPP_NONE port. */ if (in_xbundle == &ofpp_none_bundle) { return; } /* Don't learn from flood ports */ mcast_xbundle = NULL; ovs_rwlock_wrlock(&ms->rwlock); xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); LIST_FOR_EACH(fport, node, &ms->fport_list) { mcast_xbundle = xbundle_lookup(xcfg, fport->port); if (mcast_xbundle == in_xbundle) { break; } } if (!mcast_xbundle || mcast_xbundle != in_xbundle) { if (flow->dl_type == htons(ETH_TYPE_IP)) { update_mcast_snooping_table4__(xbridge, flow, ms, vlan, in_xbundle, packet); } else { update_mcast_snooping_table6__(xbridge, flow, ms, vlan, in_xbundle, packet); } } ovs_rwlock_unlock(&ms->rwlock); } /* send the packet to ports having the multicast group learned */ static void xlate_normal_mcast_send_group(struct xlate_ctx *ctx, struct mcast_snooping *ms OVS_UNUSED, struct mcast_group *grp, struct xbundle *in_xbundle, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct xlate_cfg *xcfg; struct mcast_group_bundle *b; struct xbundle *mcast_xbundle; xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) { mcast_xbundle = xbundle_lookup(xcfg, b->port); if (mcast_xbundle && mcast_xbundle != in_xbundle) { xlate_report(ctx, "forwarding to mcast group port"); output_normal(ctx, mcast_xbundle, vlan); } else if (!mcast_xbundle) { xlate_report(ctx, "mcast group port is unknown, dropping"); } else { xlate_report(ctx, "mcast group port is input port, dropping"); } } } /* send the packet to ports connected to multicast routers */ static void xlate_normal_mcast_send_mrouters(struct xlate_ctx *ctx, struct mcast_snooping *ms, struct xbundle *in_xbundle, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct xlate_cfg *xcfg; struct mcast_mrouter_bundle *mrouter; struct xbundle *mcast_xbundle; xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); LIST_FOR_EACH(mrouter, mrouter_node, &ms->mrouter_lru) { mcast_xbundle = xbundle_lookup(xcfg, mrouter->port); if (mcast_xbundle && mcast_xbundle != in_xbundle && mrouter->vlan == vlan) { xlate_report(ctx, "forwarding to mcast router port"); output_normal(ctx, mcast_xbundle, vlan); } else if (!mcast_xbundle) { xlate_report(ctx, "mcast router port is unknown, dropping"); } else if (mrouter->vlan != vlan) { xlate_report(ctx, "mcast router is on another vlan, dropping"); } else { xlate_report(ctx, "mcast router port is input port, dropping"); } } } /* send the packet to ports flagged to be flooded */ static void xlate_normal_mcast_send_fports(struct xlate_ctx *ctx, struct mcast_snooping *ms, struct xbundle *in_xbundle, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct xlate_cfg *xcfg; struct mcast_port_bundle *fport; struct xbundle *mcast_xbundle; xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); LIST_FOR_EACH(fport, node, &ms->fport_list) { mcast_xbundle = xbundle_lookup(xcfg, fport->port); if (mcast_xbundle && mcast_xbundle != in_xbundle) { xlate_report(ctx, "forwarding to mcast flood port"); output_normal(ctx, mcast_xbundle, vlan); } else if (!mcast_xbundle) { xlate_report(ctx, "mcast flood port is unknown, dropping"); } else { xlate_report(ctx, "mcast flood port is input port, dropping"); } } } /* forward the Reports to configured ports */ static void xlate_normal_mcast_send_rports(struct xlate_ctx *ctx, struct mcast_snooping *ms, struct xbundle *in_xbundle, uint16_t vlan) OVS_REQ_RDLOCK(ms->rwlock) { struct xlate_cfg *xcfg; struct mcast_port_bundle *rport; struct xbundle *mcast_xbundle; xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); LIST_FOR_EACH(rport, node, &ms->rport_list) { mcast_xbundle = xbundle_lookup(xcfg, rport->port); if (mcast_xbundle && mcast_xbundle != in_xbundle) { xlate_report(ctx, "forwarding Report to mcast flagged port"); output_normal(ctx, mcast_xbundle, vlan); } else if (!mcast_xbundle) { xlate_report(ctx, "mcast port is unknown, dropping the Report"); } else { xlate_report(ctx, "mcast port is input port, dropping the Report"); } } } static void xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle, uint16_t vlan) { struct xbundle *xbundle; LIST_FOR_EACH (xbundle, list_node, &ctx->xbridge->xbundles) { if (xbundle != in_xbundle && xbundle_includes_vlan(xbundle, vlan) && xbundle->floodable && !xbundle_mirror_out(ctx->xbridge, xbundle)) { output_normal(ctx, xbundle, vlan); } } ctx->nf_output_iface = NF_OUT_FLOOD; } static bool is_ip_local_multicast(const struct flow *flow, struct flow_wildcards *wc) { if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); return ip_is_local_multicast(flow->nw_dst); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst); return ipv6_is_all_hosts(&flow->ipv6_dst); } else { return false; } } static void xlate_normal(struct xlate_ctx *ctx) { struct flow_wildcards *wc = ctx->wc; struct flow *flow = &ctx->xin->flow; struct xbundle *in_xbundle; struct xport *in_port; struct mac_entry *mac; void *mac_port; uint16_t vlan; uint16_t vid; memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src); memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); in_xbundle = lookup_input_bundle(ctx->xbridge, flow->in_port.ofp_port, ctx->xin->packet != NULL, &in_port); if (!in_xbundle) { xlate_report(ctx, "no input bundle, dropping"); return; } /* Drop malformed frames. */ if (flow->dl_type == htons(ETH_TYPE_VLAN) && !(flow->vlan_tci & htons(VLAN_CFI))) { if (ctx->xin->packet != NULL) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial " "VLAN tag received on port %s", ctx->xbridge->name, in_xbundle->name); } xlate_report(ctx, "partial VLAN tag, dropping"); return; } /* Drop frames on bundles reserved for mirroring. */ if (xbundle_mirror_out(ctx->xbridge, in_xbundle)) { if (ctx->xin->packet != NULL) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port " "%s, which is reserved exclusively for mirroring", ctx->xbridge->name, in_xbundle->name); } xlate_report(ctx, "input port is mirror output port, dropping"); return; } /* Check VLAN. */ vid = vlan_tci_to_vid(flow->vlan_tci); if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) { xlate_report(ctx, "disallowed VLAN VID for this input port, dropping"); return; } vlan = input_vid_to_vlan(in_xbundle, vid); /* Check other admissibility requirements. */ if (in_port && !is_admissible(ctx, in_port, vlan)) { return; } /* Learn source MAC. */ if (ctx->xin->may_learn) { update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle); } if (ctx->xin->xcache) { struct xc_entry *entry; /* Save enough info to update mac learning table later. */ entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NORMAL); entry->u.normal.ofproto = ctx->xbridge->ofproto; entry->u.normal.flow = xmemdup(flow, sizeof *flow); entry->u.normal.vlan = vlan; } /* Determine output bundle. */ if (mcast_snooping_enabled(ctx->xbridge->ms) && !eth_addr_is_broadcast(flow->dl_dst) && eth_addr_is_multicast(flow->dl_dst) && is_ip_any(flow)) { struct mcast_snooping *ms = ctx->xbridge->ms; struct mcast_group *grp = NULL; if (is_igmp(flow, wc)) { /* * IGMP packets need to take the slow path, in order to be * processed for mdb updates. That will prevent expires * firing off even after hosts have sent reports. */ ctx->xout->slow |= SLOW_ACTION; memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); if (mcast_snooping_is_membership(flow->tp_src) || mcast_snooping_is_query(flow->tp_src)) { if (ctx->xin->may_learn && ctx->xin->packet) { update_mcast_snooping_table(ctx->xbridge, flow, vlan, in_xbundle, ctx->xin->packet); } } if (mcast_snooping_is_membership(flow->tp_src)) { ovs_rwlock_rdlock(&ms->rwlock); xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan); /* RFC4541: section 2.1.1, item 1: A snooping switch should * forward IGMP Membership Reports only to those ports where * multicast routers are attached. Alternatively stated: a * snooping switch should not forward IGMP Membership Reports * to ports on which only hosts are attached. * An administrative control may be provided to override this * restriction, allowing the report messages to be flooded to * other ports. */ xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan); ovs_rwlock_unlock(&ms->rwlock); } else { xlate_report(ctx, "multicast traffic, flooding"); xlate_normal_flood(ctx, in_xbundle, vlan); } return; } else if (is_mld(flow, wc)) { ctx->xout->slow |= SLOW_ACTION; if (ctx->xin->may_learn && ctx->xin->packet) { update_mcast_snooping_table(ctx->xbridge, flow, vlan, in_xbundle, ctx->xin->packet); } if (is_mld_report(flow, wc)) { ovs_rwlock_rdlock(&ms->rwlock); xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan); xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan); ovs_rwlock_unlock(&ms->rwlock); } else { xlate_report(ctx, "MLD query, flooding"); xlate_normal_flood(ctx, in_xbundle, vlan); } } else { if (is_ip_local_multicast(flow, wc)) { /* RFC4541: section 2.1.2, item 2: Packets with a dst IP * address in the 224.0.0.x range which are not IGMP must * be forwarded on all ports */ xlate_report(ctx, "RFC4541: section 2.1.2, item 2, flooding"); xlate_normal_flood(ctx, in_xbundle, vlan); return; } } /* forwarding to group base ports */ ovs_rwlock_rdlock(&ms->rwlock); if (flow->dl_type == htons(ETH_TYPE_IP)) { grp = mcast_snooping_lookup4(ms, flow->nw_dst, vlan); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { grp = mcast_snooping_lookup(ms, &flow->ipv6_dst, vlan); } if (grp) { xlate_normal_mcast_send_group(ctx, ms, grp, in_xbundle, vlan); xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan); xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan); } else { if (mcast_snooping_flood_unreg(ms)) { xlate_report(ctx, "unregistered multicast, flooding"); xlate_normal_flood(ctx, in_xbundle, vlan); } else { xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan); xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan); } } ovs_rwlock_unlock(&ms->rwlock); } else { ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock); mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan); mac_port = mac ? mac_entry_get_port(ctx->xbridge->ml, mac) : NULL; ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock); if (mac_port) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xbundle *mac_xbundle = xbundle_lookup(xcfg, mac_port); if (mac_xbundle && mac_xbundle != in_xbundle) { xlate_report(ctx, "forwarding to learned port"); output_normal(ctx, mac_xbundle, vlan); } else if (!mac_xbundle) { xlate_report(ctx, "learned port is unknown, dropping"); } else { xlate_report(ctx, "learned port is input port, dropping"); } } else { xlate_report(ctx, "no learned MAC for destination, flooding"); xlate_normal_flood(ctx, in_xbundle, vlan); } } } /* Appends a "sample" action for sFlow or IPFIX to 'ctx->odp_actions'. The * 'probability' is the number of packets out of UINT32_MAX to sample. The * 'cookie' (of length 'cookie_size' bytes) is passed back in the callback for * each sampled packet. 'tunnel_out_port', if not ODPP_NONE, is added as the * OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute. If 'include_actions', an * OVS_USERSPACE_ATTR_ACTIONS attribute is added. */ static size_t compose_sample_action(struct xlate_ctx *ctx, const uint32_t probability, const union user_action_cookie *cookie, const size_t cookie_size, const odp_port_t tunnel_out_port, bool include_actions) { size_t sample_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_SAMPLE); nl_msg_put_u32(ctx->odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability); size_t actions_offset = nl_msg_start_nested(ctx->odp_actions, OVS_SAMPLE_ATTR_ACTIONS); odp_port_t odp_port = ofp_port_to_odp_port( ctx->xbridge, ctx->xin->flow.in_port.ofp_port); uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port, flow_hash_5tuple(&ctx->xin->flow, 0)); int cookie_offset = odp_put_userspace_action(pid, cookie, cookie_size, tunnel_out_port, include_actions, ctx->odp_actions); nl_msg_end_nested(ctx->odp_actions, actions_offset); nl_msg_end_nested(ctx->odp_actions, sample_offset); return cookie_offset; } /* If sFLow is not enabled, returns 0 without doing anything. * * If sFlow is enabled, appends a template "sample" action to the ODP actions * in 'ctx'. This action is a template because some of the information needed * to fill it out is not available until flow translation is complete. In this * case, this functions returns an offset, which is always nonzero, to pass * later to fix_sflow_action() to fill in the rest of the template. */ static size_t compose_sflow_action(struct xlate_ctx *ctx) { struct dpif_sflow *sflow = ctx->xbridge->sflow; if (!sflow || ctx->xin->flow.in_port.ofp_port == OFPP_NONE) { return 0; } union user_action_cookie cookie; memset(&cookie, 0, sizeof cookie); cookie.type = USER_ACTION_COOKIE_SFLOW; return compose_sample_action(ctx, dpif_sflow_get_probability(sflow), &cookie, sizeof cookie.sflow, ODPP_NONE, true); } /* If IPFIX is enabled, this appends a "sample" action to implement IPFIX to * 'ctx->odp_actions'. */ static void compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port) { struct dpif_ipfix *ipfix = ctx->xbridge->ipfix; odp_port_t tunnel_out_port = ODPP_NONE; if (!ipfix || ctx->xin->flow.in_port.ofp_port == OFPP_NONE) { return; } /* For input case, output_odp_port is ODPP_NONE, which is an invalid port * number. */ if (output_odp_port == ODPP_NONE && !dpif_ipfix_get_bridge_exporter_input_sampling(ipfix)) { return; } /* For output case, output_odp_port is valid*/ if (output_odp_port != ODPP_NONE) { if (!dpif_ipfix_get_bridge_exporter_output_sampling(ipfix)) { return; } /* If tunnel sampling is enabled, put an additional option attribute: * OVS_USERSPACE_ATTR_TUNNEL_OUT_PORT */ if (dpif_ipfix_get_bridge_exporter_tunnel_sampling(ipfix) && dpif_ipfix_get_tunnel_port(ipfix, output_odp_port) ) { tunnel_out_port = output_odp_port; } } union user_action_cookie cookie; memset(&cookie, 0, sizeof cookie); cookie.ipfix.type = USER_ACTION_COOKIE_IPFIX; cookie.ipfix.output_odp_port = output_odp_port; compose_sample_action(ctx, dpif_ipfix_get_bridge_exporter_probability(ipfix), &cookie, sizeof cookie.ipfix, tunnel_out_port, false); } /* Fix "sample" action according to data collected while composing ODP actions, * as described in compose_sflow_action(). * * 'user_cookie_offset' must be the offset returned by add_sflow_action(). */ static void fix_sflow_action(struct xlate_ctx *ctx, unsigned int user_cookie_offset) { const struct flow *base = &ctx->base_flow; union user_action_cookie *cookie; cookie = ofpbuf_at(ctx->odp_actions, user_cookie_offset, sizeof cookie->sflow); ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW); cookie->type = USER_ACTION_COOKIE_SFLOW; cookie->sflow.vlan_tci = base->vlan_tci; /* See http://www.sflow.org/sflow_version_5.txt (search for "Input/output * port information") for the interpretation of cookie->output. */ switch (ctx->sflow_n_outputs) { case 0: /* 0x40000000 | 256 means "packet dropped for unknown reason". */ cookie->sflow.output = 0x40000000 | 256; break; case 1: cookie->sflow.output = dpif_sflow_odp_port_to_ifindex( ctx->xbridge->sflow, ctx->sflow_odp_port); if (cookie->sflow.output) { break; } /* Fall through. */ default: /* 0x80000000 means "multiple output ports. */ cookie->sflow.output = 0x80000000 | ctx->sflow_n_outputs; break; } } static bool process_special(struct xlate_ctx *ctx, const struct xport *xport) { const struct flow *flow = &ctx->xin->flow; struct flow_wildcards *wc = ctx->wc; const struct xbridge *xbridge = ctx->xbridge; const struct dp_packet *packet = ctx->xin->packet; enum slow_path_reason slow; if (!xport) { slow = 0; } else if (xport->cfm && cfm_should_process_flow(xport->cfm, flow, wc)) { if (packet) { cfm_process_heartbeat(xport->cfm, packet); } slow = SLOW_CFM; } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) { if (packet) { bfd_process_packet(xport->bfd, flow, packet); /* If POLL received, immediately sends FINAL back. */ if (bfd_should_send_packet(xport->bfd)) { ofproto_dpif_monitor_port_send_soon(xport->ofport); } } slow = SLOW_BFD; } else if (xport->xbundle && xport->xbundle->lacp && flow->dl_type == htons(ETH_TYPE_LACP)) { if (packet) { lacp_process_packet(xport->xbundle->lacp, xport->ofport, packet); } slow = SLOW_LACP; } else if ((xbridge->stp || xbridge->rstp) && stp_should_process_flow(flow, wc)) { if (packet) { xbridge->stp ? stp_process_packet(xport, packet) : rstp_process_packet(xport, packet); } slow = SLOW_STP; } else if (xport->lldp && lldp_should_process_flow(xport->lldp, flow)) { if (packet) { lldp_process_packet(xport->lldp, packet); } slow = SLOW_LLDP; } else { slow = 0; } if (slow) { ctx->xout->slow |= slow; return true; } else { return false; } } static int tnl_route_lookup_flow(const struct flow *oflow, struct in6_addr *ip, struct xport **out_port) { char out_dev[IFNAMSIZ]; struct xbridge *xbridge; struct xlate_cfg *xcfg; struct in6_addr gw; struct in6_addr dst; dst = flow_tnl_dst(&oflow->tunnel); if (!ovs_router_lookup(&dst, out_dev, &gw)) { return -ENOENT; } if (ipv6_addr_is_set(&gw) && (!IN6_IS_ADDR_V4MAPPED(&gw) || in6_addr_get_mapped_ipv4(&gw))) { *ip = gw; } else { *ip = dst; } xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); ovs_assert(xcfg); HMAP_FOR_EACH (xbridge, hmap_node, &xcfg->xbridges) { if (!strncmp(xbridge->name, out_dev, IFNAMSIZ)) { struct xport *port; HMAP_FOR_EACH (port, ofp_node, &xbridge->xports) { if (!strncmp(netdev_get_name(port->netdev), out_dev, IFNAMSIZ)) { *out_port = port; return 0; } } } } return -ENOENT; } static int compose_table_xlate(struct xlate_ctx *ctx, const struct xport *out_dev, struct dp_packet *packet) { struct xbridge *xbridge = out_dev->xbridge; struct ofpact_output output; struct flow flow; ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output); flow_extract(packet, &flow); flow.in_port.ofp_port = out_dev->ofp_port; output.port = OFPP_TABLE; output.max_len = 0; return ofproto_dpif_execute_actions__(xbridge->ofproto, &flow, NULL, &output.ofpact, sizeof output, ctx->recurse, ctx->resubmits, packet); } static void tnl_send_nd_request(struct xlate_ctx *ctx, const struct xport *out_dev, const struct eth_addr eth_src, struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst) { struct dp_packet packet; dp_packet_init(&packet, 0); compose_nd(&packet, eth_src, ipv6_src, ipv6_dst); compose_table_xlate(ctx, out_dev, &packet); dp_packet_uninit(&packet); } static void tnl_send_arp_request(struct xlate_ctx *ctx, const struct xport *out_dev, const struct eth_addr eth_src, ovs_be32 ip_src, ovs_be32 ip_dst) { struct dp_packet packet; dp_packet_init(&packet, 0); compose_arp(&packet, ARP_OP_REQUEST, eth_src, eth_addr_zero, true, ip_src, ip_dst); compose_table_xlate(ctx, out_dev, &packet); dp_packet_uninit(&packet); } static int build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport, const struct flow *flow, odp_port_t tunnel_odp_port) { struct ovs_action_push_tnl tnl_push_data; struct xport *out_dev = NULL; ovs_be32 s_ip = 0, d_ip = 0; struct in6_addr s_ip6 = in6addr_any; struct in6_addr d_ip6 = in6addr_any; struct eth_addr smac; struct eth_addr dmac; int err; char buf_sip6[INET6_ADDRSTRLEN]; char buf_dip6[INET6_ADDRSTRLEN]; err = tnl_route_lookup_flow(flow, &d_ip6, &out_dev); if (err) { xlate_report(ctx, "native tunnel routing failed"); return err; } xlate_report(ctx, "tunneling to %s via %s", ipv6_string_mapped(buf_dip6, &d_ip6), netdev_get_name(out_dev->netdev)); /* Use mac addr of bridge port of the peer. */ err = netdev_get_etheraddr(out_dev->netdev, &smac); if (err) { xlate_report(ctx, "tunnel output device lacks Ethernet address"); return err; } d_ip = in6_addr_get_mapped_ipv4(&d_ip6); if (d_ip) { err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL); if (err) { xlate_report(ctx, "tunnel output device lacks IPv4 address"); return err; } in6_addr_set_mapped_ipv4(&s_ip6, s_ip); } else { err = netdev_get_in6(out_dev->netdev, &s_ip6); if (err) { xlate_report(ctx, "tunnel output device lacks IPv6 address"); return err; } } err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &dmac); if (err) { xlate_report(ctx, "neighbor cache miss for %s on bridge %s, " "sending %s request", buf_dip6, out_dev->xbridge->name, d_ip ? "ARP" : "ND"); if (d_ip) { tnl_send_arp_request(ctx, out_dev, smac, s_ip, d_ip); } else { tnl_send_nd_request(ctx, out_dev, smac, &s_ip6, &d_ip6); } return err; } if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_TNL_NEIGH); ovs_strlcpy(entry->u.tnl_neigh_cache.br_name, out_dev->xbridge->name, sizeof entry->u.tnl_neigh_cache.br_name); entry->u.tnl_neigh_cache.d_ipv6 = d_ip6; } xlate_report(ctx, "tunneling from "ETH_ADDR_FMT" %s" " to "ETH_ADDR_FMT" %s", ETH_ADDR_ARGS(smac), ipv6_string_mapped(buf_sip6, &s_ip6), ETH_ADDR_ARGS(dmac), buf_dip6); err = tnl_port_build_header(xport->ofport, flow, dmac, smac, &s_ip6, &tnl_push_data); if (err) { return err; } tnl_push_data.tnl_port = odp_to_u32(tunnel_odp_port); tnl_push_data.out_port = odp_to_u32(out_dev->odp_port); odp_put_tnl_push_action(ctx->odp_actions, &tnl_push_data); return 0; } static void xlate_commit_actions(struct xlate_ctx *ctx) { bool use_masked = ctx->xbridge->support.masked_set_action; ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc, use_masked); } static void clear_conntrack(struct flow *flow) { flow->ct_state = 0; flow->ct_zone = 0; flow->ct_mark = 0; memset(&flow->ct_label, 0, sizeof flow->ct_label); } static void compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, const struct xlate_bond_recirc *xr, bool check_stp) { const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port); struct flow_wildcards *wc = ctx->wc; struct flow *flow = &ctx->xin->flow; struct flow_tnl flow_tnl; ovs_be16 flow_vlan_tci; uint32_t flow_pkt_mark; uint8_t flow_nw_tos; odp_port_t out_port, odp_port; bool tnl_push_pop_send = false; uint8_t dscp; /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); memset(&flow_tnl, 0, sizeof flow_tnl); if (!xport) { xlate_report(ctx, "Nonexistent output port"); return; } else if (xport->config & OFPUTIL_PC_NO_FWD) { xlate_report(ctx, "OFPPC_NO_FWD set, skipping output"); return; } else if (check_stp) { if (is_stp(&ctx->base_flow)) { if (!xport_stp_should_forward_bpdu(xport) && !xport_rstp_should_manage_bpdu(xport)) { if (ctx->xbridge->stp != NULL) { xlate_report(ctx, "STP not in listening state, " "skipping bpdu output"); } else if (ctx->xbridge->rstp != NULL) { xlate_report(ctx, "RSTP not managing BPDU in this state, " "skipping bpdu output"); } return; } } else if ((xport->cfm && cfm_should_process_flow(xport->cfm, flow, wc)) || (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc))) { /* Pass; STP should not block link health detection. */ } else if (!xport_stp_forward_state(xport) || !xport_rstp_forward_state(xport)) { if (ctx->xbridge->stp != NULL) { xlate_report(ctx, "STP not in forwarding state, " "skipping output"); } else if (ctx->xbridge->rstp != NULL) { xlate_report(ctx, "RSTP not in forwarding state, " "skipping output"); } return; } } if (xport->peer) { const struct xport *peer = xport->peer; struct flow old_flow = ctx->xin->flow; bool old_conntrack = ctx->conntracked; bool old_was_mpls = ctx->was_mpls; cls_version_t old_version = ctx->tables_version; struct ofpbuf old_stack = ctx->stack; union mf_subvalue new_stack[1024 / sizeof(union mf_subvalue)]; struct ofpbuf old_action_set = ctx->action_set; uint64_t actset_stub[1024 / 8]; ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack); ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub); flow->in_port.ofp_port = peer->ofp_port; flow->metadata = htonll(0); memset(&flow->tunnel, 0, sizeof flow->tunnel); memset(flow->regs, 0, sizeof flow->regs); flow->actset_output = OFPP_UNSET; ctx->conntracked = false; clear_conntrack(flow); /* When the patch port points to a different bridge, then the mirrors * for that bridge clearly apply independently to the packet, so we * reset the mirror bitmap to zero and then restore it after the packet * returns. * * When the patch port points to the same bridge, this is more of a * design decision: can mirrors be re-applied to the packet after it * re-enters the bridge, or should we treat that as doubly mirroring a * single packet? The former may be cleaner, since it respects the * model in which a patch port is like a physical cable plugged from * one switch port to another, but the latter may be less surprising to * users. We take the latter choice, for now at least. (To use the * former choice, hard-code 'independent_mirrors' to "true".) */ mirror_mask_t old_mirrors = ctx->mirrors; bool independent_mirrors = peer->xbridge != ctx->xbridge; if (independent_mirrors) { ctx->mirrors = 0; } ctx->xbridge = peer->xbridge; /* The bridge is now known so obtain its table version. */ ctx->tables_version = ofproto_dpif_get_tables_version(ctx->xbridge->ofproto); if (!process_special(ctx, peer) && may_receive(peer, ctx)) { if (xport_stp_forward_state(peer) && xport_rstp_forward_state(peer)) { xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); if (ctx->action_set.size) { /* Translate action set only if not dropping the packet and * not recirculating. */ if (!exit_recirculates(ctx)) { xlate_action_set(ctx); } } /* Check if need to recirculate. */ if (exit_recirculates(ctx)) { compose_recirculate_action(ctx); } } else { /* Forwarding is disabled by STP and RSTP. Let OFPP_NORMAL and * the learning action look at the packet, then drop it. */ struct flow old_base_flow = ctx->base_flow; size_t old_size = ctx->odp_actions->size; mirror_mask_t old_mirrors2 = ctx->mirrors; xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); ctx->mirrors = old_mirrors2; ctx->base_flow = old_base_flow; ctx->odp_actions->size = old_size; /* Undo changes that may have been done for recirculation. */ if (exit_recirculates(ctx)) { ctx->action_set.size = ctx->recirc_action_offset; ctx->recirc_action_offset = -1; ctx->last_unroll_offset = -1; } } } if (independent_mirrors) { ctx->mirrors = old_mirrors; } ctx->xin->flow = old_flow; ctx->xbridge = xport->xbridge; ofpbuf_uninit(&ctx->action_set); ctx->action_set = old_action_set; ofpbuf_uninit(&ctx->stack); ctx->stack = old_stack; /* Restore calling bridge's lookup version. */ ctx->tables_version = old_version; /* The peer bridge popping MPLS should have no effect on the original * bridge. */ ctx->was_mpls = old_was_mpls; /* The peer bridge's conntrack execution should have no effect on the * original bridge. */ ctx->conntracked = old_conntrack; /* The fact that the peer bridge exits (for any reason) does not mean * that the original bridge should exit. Specifically, if the peer * bridge recirculates (which typically modifies the packet), the * original bridge must continue processing with the original, not the * recirculated packet! */ ctx->exit = false; /* Peer bridge errors do not propagate back. */ ctx->error = XLATE_OK; if (ctx->xin->resubmit_stats) { netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats); netdev_vport_inc_rx(peer->netdev, ctx->xin->resubmit_stats); if (peer->bfd) { bfd_account_rx(peer->bfd, ctx->xin->resubmit_stats); } } if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV); entry->u.dev.tx = netdev_ref(xport->netdev); entry->u.dev.rx = netdev_ref(peer->netdev); entry->u.dev.bfd = bfd_ref(peer->bfd); } return; } flow_vlan_tci = flow->vlan_tci; flow_pkt_mark = flow->pkt_mark; flow_nw_tos = flow->nw_tos; if (count_skb_priorities(xport)) { memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) { wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= dscp; } } if (xport->is_tunnel) { struct in6_addr dst; /* Save tunnel metadata so that changes made due to * the Logical (tunnel) Port are not visible for any further * matches, while explicit set actions on tunnel metadata are. */ flow_tnl = flow->tunnel; odp_port = tnl_port_send(xport->ofport, flow, ctx->wc); if (odp_port == ODPP_NONE) { xlate_report(ctx, "Tunneling decided against output"); goto out; /* restore flow_nw_tos */ } dst = flow_tnl_dst(&flow->tunnel); if (ipv6_addr_equals(&dst, &ctx->orig_tunnel_ipv6_dst)) { xlate_report(ctx, "Not tunneling to our own address"); goto out; /* restore flow_nw_tos */ } if (ctx->xin->resubmit_stats) { netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats); } if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV); entry->u.dev.tx = netdev_ref(xport->netdev); } out_port = odp_port; if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) { xlate_report(ctx, "output to native tunnel"); tnl_push_pop_send = true; } else { xlate_report(ctx, "output to kernel tunnel"); commit_odp_tunnel_action(flow, &ctx->base_flow, ctx->odp_actions); flow->tunnel = flow_tnl; /* Restore tunnel metadata */ } } else { odp_port = xport->odp_port; out_port = odp_port; if (ofproto_has_vlan_splinters(ctx->xbridge->ofproto)) { ofp_port_t vlandev_port; wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, ofp_port, flow->vlan_tci); if (vlandev_port != ofp_port) { out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port); flow->vlan_tci = htons(0); } } } if (out_port != ODPP_NONE) { xlate_commit_actions(ctx); if (xr) { struct ovs_action_hash *act_hash; /* Hash action. */ act_hash = nl_msg_put_unspec_uninit(ctx->odp_actions, OVS_ACTION_ATTR_HASH, sizeof *act_hash); act_hash->hash_alg = xr->hash_alg; act_hash->hash_basis = xr->hash_basis; /* Recirc action. */ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_RECIRC, xr->recirc_id); } else { if (tnl_push_pop_send) { build_tunnel_send(ctx, xport, flow, odp_port); flow->tunnel = flow_tnl; /* Restore tunnel metadata */ } else { odp_port_t odp_tnl_port = ODPP_NONE; /* XXX: Write better Filter for tunnel port. We can use inport * int tunnel-port flow to avoid these checks completely. */ if (ofp_port == OFPP_LOCAL && ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) { odp_tnl_port = tnl_port_map_lookup(flow, wc); } if (odp_tnl_port != ODPP_NONE) { nl_msg_put_odp_port(ctx->odp_actions, OVS_ACTION_ATTR_TUNNEL_POP, odp_tnl_port); } else { /* Tunnel push-pop action is not compatible with * IPFIX action. */ compose_ipfix_action(ctx, out_port); nl_msg_put_odp_port(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port); } } } ctx->sflow_odp_port = odp_port; ctx->sflow_n_outputs++; ctx->nf_output_iface = ofp_port; } if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) { mirror_packet(ctx, xport->xbundle, xbundle_mirror_dst(xport->xbundle->xbridge, xport->xbundle)); } out: /* Restore flow */ flow->vlan_tci = flow_vlan_tci; flow->pkt_mark = flow_pkt_mark; flow->nw_tos = flow_nw_tos; } static void compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port, const struct xlate_bond_recirc *xr) { compose_output_action__(ctx, ofp_port, xr, true); } static void xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule) { struct rule_dpif *old_rule = ctx->rule; ovs_be64 old_cookie = ctx->rule_cookie; const struct rule_actions *actions; if (ctx->xin->resubmit_stats) { rule_dpif_credit_stats(rule, ctx->xin->resubmit_stats); } ctx->resubmits++; ctx->recurse++; ctx->rule = rule; ctx->rule_cookie = rule_dpif_get_flow_cookie(rule); actions = rule_dpif_get_actions(rule); do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx); ctx->rule_cookie = old_cookie; ctx->rule = old_rule; ctx->recurse--; } static bool xlate_resubmit_resource_check(struct xlate_ctx *ctx) { if (ctx->recurse >= MAX_RESUBMIT_RECURSION + MAX_INTERNAL_RESUBMITS) { XLATE_REPORT_ERROR(ctx, "resubmit actions recursed over %d times", MAX_RESUBMIT_RECURSION); ctx->error = XLATE_RECURSION_TOO_DEEP; } else if (ctx->resubmits >= MAX_RESUBMITS + MAX_INTERNAL_RESUBMITS) { XLATE_REPORT_ERROR(ctx, "over %d resubmit actions", MAX_RESUBMITS); ctx->error = XLATE_TOO_MANY_RESUBMITS; } else if (ctx->odp_actions->size > UINT16_MAX) { XLATE_REPORT_ERROR(ctx, "resubmits yielded over 64 kB of actions"); /* NOT an error, as we'll be slow-pathing the flow in this case? */ ctx->exit = true; /* XXX: translation still terminated! */ } else if (ctx->stack.size >= 65536) { XLATE_REPORT_ERROR(ctx, "resubmits yielded over 64 kB of stack"); ctx->error = XLATE_STACK_TOO_DEEP; } else { return true; } return false; } static void xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id, bool may_packet_in, bool honor_table_miss) { /* Check if we need to recirculate before matching in a table. */ if (ctx->was_mpls) { ctx_trigger_recirculation(ctx); return; } if (xlate_resubmit_resource_check(ctx)) { uint8_t old_table_id = ctx->table_id; struct rule_dpif *rule; ctx->table_id = table_id; rule = rule_dpif_lookup_from_table(ctx->xbridge->ofproto, ctx->tables_version, &ctx->xin->flow, ctx->wc, ctx->xin->resubmit_stats, &ctx->table_id, in_port, may_packet_in, honor_table_miss); if (OVS_UNLIKELY(ctx->xin->resubmit_hook)) { ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse + 1); } if (rule) { /* Fill in the cache entry here instead of xlate_recursively * to make the reference counting more explicit. We take a * reference in the lookups above if we are going to cache the * rule. */ if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_RULE); entry->u.rule = rule; rule_dpif_ref(rule); } xlate_recursively(ctx, rule); } ctx->table_id = old_table_id; return; } } static void xlate_group_stats(struct xlate_ctx *ctx, struct group_dpif *group, struct ofputil_bucket *bucket) { if (ctx->xin->resubmit_stats) { group_dpif_credit_stats(group, bucket, ctx->xin->resubmit_stats); } if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_GROUP); entry->u.group.group = group_dpif_ref(group); entry->u.group.bucket = bucket; } } static void xlate_group_bucket(struct xlate_ctx *ctx, struct ofputil_bucket *bucket) { uint64_t action_list_stub[1024 / 8]; struct ofpbuf action_list, action_set; struct flow old_flow = ctx->xin->flow; bool old_was_mpls = ctx->was_mpls; ofpbuf_use_const(&action_set, bucket->ofpacts, bucket->ofpacts_len); ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub); ofpacts_execute_action_set(&action_list, &action_set); ctx->recurse++; do_xlate_actions(action_list.data, action_list.size, ctx); ctx->recurse--; ofpbuf_uninit(&action_set); ofpbuf_uninit(&action_list); /* Check if need to recirculate. */ if (exit_recirculates(ctx)) { compose_recirculate_action(ctx); } /* Roll back flow to previous state. * This is equivalent to cloning the packet for each bucket. * * As a side effect any subsequently applied actions will * also effectively be applied to a clone of the packet taken * just before applying the all or indirect group. * * Note that group buckets are action sets, hence they cannot modify the * main action set. Also any stack actions are ignored when executing an * action set, so group buckets cannot change the stack either. * However, we do allow resubmit actions in group buckets, which could * break the above assumptions. It is up to the controller to not mess up * with the action_set and stack in the tables resubmitted to from * group buckets. */ ctx->xin->flow = old_flow; /* The group bucket popping MPLS should have no effect after bucket * execution. */ ctx->was_mpls = old_was_mpls; /* The fact that the group bucket exits (for any reason) does not mean that * the translation after the group action should exit. Specifically, if * the group bucket recirculates (which typically modifies the packet), the * actions after the group action must continue processing with the * original, not the recirculated packet! */ ctx->exit = false; } static void xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group) { struct ofputil_bucket *bucket; const struct ovs_list *buckets; group_dpif_get_buckets(group, &buckets); LIST_FOR_EACH (bucket, list_node, buckets) { xlate_group_bucket(ctx, bucket); } xlate_group_stats(ctx, group, NULL); } static void xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group) { struct ofputil_bucket *bucket; bucket = group_first_live_bucket(ctx, group, 0); if (bucket) { xlate_group_bucket(ctx, bucket); xlate_group_stats(ctx, group, bucket); } } static void xlate_default_select_group(struct xlate_ctx *ctx, struct group_dpif *group) { struct flow_wildcards *wc = ctx->wc; struct ofputil_bucket *bucket; uint32_t basis; basis = flow_hash_symmetric_l4(&ctx->xin->flow, 0); flow_mask_hash_fields(&ctx->xin->flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4); bucket = group_best_live_bucket(ctx, group, basis); if (bucket) { xlate_group_bucket(ctx, bucket); xlate_group_stats(ctx, group, bucket); } } static void xlate_hash_fields_select_group(struct xlate_ctx *ctx, struct group_dpif *group) { struct mf_bitmap hash_fields = MF_BITMAP_INITIALIZER; const struct field_array *fields; struct ofputil_bucket *bucket; uint32_t basis; int i; fields = group_dpif_get_fields(group); basis = hash_uint64(group_dpif_get_selection_method_param(group)); /* Determine which fields to hash */ for (i = 0; i < MFF_N_IDS; i++) { if (bitmap_is_set(fields->used.bm, i)) { const struct mf_field *mf; /* If the field is already present in 'hash_fields' then * this loop has already checked that it and its pre-requisites * are present in the flow and its pre-requisites have * already been added to 'hash_fields'. There is nothing more * to do here and as an optimisation the loop can continue. */ if (bitmap_is_set(hash_fields.bm, i)) { continue; } mf = mf_from_id(i); /* Only hash a field if it and its pre-requisites are present * in the flow. */ if (!mf_are_prereqs_ok(mf, &ctx->xin->flow)) { continue; } /* Hash both the field and its pre-requisites */ mf_bitmap_set_field_and_prereqs(mf, &hash_fields); } } /* Hash the fields */ for (i = 0; i < MFF_N_IDS; i++) { if (bitmap_is_set(hash_fields.bm, i)) { const struct mf_field *mf = mf_from_id(i); union mf_value value; int j; mf_get_value(mf, &ctx->xin->flow, &value); /* This seems inefficient but so does apply_mask() */ for (j = 0; j < mf->n_bytes; j++) { ((uint8_t *) &value)[j] &= ((uint8_t *) &fields->value[i])[j]; } basis = hash_bytes(&value, mf->n_bytes, basis); /* For tunnels, hash in whether the field is present. */ if (mf_is_tun_metadata(mf)) { basis = hash_boolean(mf_is_set(mf, &ctx->xin->flow), basis); } mf_mask_field(mf, &ctx->wc->masks); } } bucket = group_best_live_bucket(ctx, group, basis); if (bucket) { xlate_group_bucket(ctx, bucket); xlate_group_stats(ctx, group, bucket); } } static void xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) { const char *selection_method = group_dpif_get_selection_method(group); if (selection_method[0] == '\0') { xlate_default_select_group(ctx, group); } else if (!strcasecmp("hash", selection_method)) { xlate_hash_fields_select_group(ctx, group); } else { /* Parsing of groups should ensure this never happens */ OVS_NOT_REACHED(); } } static void xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) { bool was_in_group = ctx->in_group; ctx->in_group = true; switch (group_dpif_get_type(group)) { case OFPGT11_ALL: case OFPGT11_INDIRECT: xlate_all_group(ctx, group); break; case OFPGT11_SELECT: xlate_select_group(ctx, group); break; case OFPGT11_FF: xlate_ff_group(ctx, group); break; default: OVS_NOT_REACHED(); } group_dpif_unref(group); ctx->in_group = was_in_group; } static bool xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id) { if (xlate_resubmit_resource_check(ctx)) { struct group_dpif *group; bool got_group; got_group = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group); if (got_group) { xlate_group_action__(ctx, group); } else { return true; } } return false; } static void xlate_ofpact_resubmit(struct xlate_ctx *ctx, const struct ofpact_resubmit *resubmit) { ofp_port_t in_port; uint8_t table_id; bool may_packet_in = false; bool honor_table_miss = false; if (ctx->rule && rule_dpif_is_internal(ctx->rule)) { /* Still allow missed packets to be sent to the controller * if resubmitting from an internal table. */ may_packet_in = true; honor_table_miss = true; } in_port = resubmit->in_port; if (in_port == OFPP_IN_PORT) { in_port = ctx->xin->flow.in_port.ofp_port; } table_id = resubmit->table_id; if (table_id == 255) { table_id = ctx->table_id; } xlate_table_action(ctx, in_port, table_id, may_packet_in, honor_table_miss); } static void flood_packets(struct xlate_ctx *ctx, bool all) { const struct xport *xport; HMAP_FOR_EACH (xport, ofp_node, &ctx->xbridge->xports) { if (xport->ofp_port == ctx->xin->flow.in_port.ofp_port) { continue; } if (all) { compose_output_action__(ctx, xport->ofp_port, NULL, false); } else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) { compose_output_action(ctx, xport->ofp_port, NULL); } } ctx->nf_output_iface = NF_OUT_FLOOD; } static void execute_controller_action(struct xlate_ctx *ctx, int len, enum ofp_packet_in_reason reason, uint16_t controller_id) { struct ofproto_packet_in *pin; struct dp_packet *packet; ctx->xout->slow |= SLOW_CONTROLLER; xlate_commit_actions(ctx); if (!ctx->xin->packet) { return; } packet = dp_packet_clone(ctx->xin->packet); odp_execute_actions(NULL, &packet, 1, false, ctx->odp_actions->data, ctx->odp_actions->size, NULL); pin = xmalloc(sizeof *pin); pin->up.packet_len = dp_packet_size(packet); pin->up.packet = dp_packet_steal_data(packet); pin->up.reason = reason; pin->up.table_id = ctx->table_id; pin->up.cookie = ctx->rule_cookie; flow_get_metadata(&ctx->xin->flow, &pin->up.flow_metadata); pin->controller_id = controller_id; pin->send_len = len; /* If a rule is a table-miss rule then this is * a table-miss handled by a table-miss rule. * * Else, if rule is internal and has a controller action, * the later being implied by the rule being processed here, * then this is a table-miss handled without a table-miss rule. * * Otherwise this is not a table-miss. */ pin->miss_type = OFPROTO_PACKET_IN_NO_MISS; if (ctx->rule) { if (rule_dpif_is_table_miss(ctx->rule)) { pin->miss_type = OFPROTO_PACKET_IN_MISS_FLOW; } else if (rule_dpif_is_internal(ctx->rule)) { pin->miss_type = OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW; } } ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin); dp_packet_delete(packet); } static void compose_recirculate_action__(struct xlate_ctx *ctx, uint8_t table) { struct recirc_metadata md; uint32_t id; recirc_metadata_from_flow(&md, &ctx->xin->flow); ovs_assert(ctx->recirc_action_offset >= 0); struct recirc_state state = { .table_id = table, .ofproto = ctx->xbridge->ofproto, .metadata = md, .stack = &ctx->stack, .mirrors = ctx->mirrors, .conntracked = ctx->conntracked, .action_set_len = ctx->recirc_action_offset, .ofpacts_len = ctx->action_set.size, .ofpacts = ctx->action_set.data, }; /* Allocate a unique recirc id for the given metadata state in the * flow. An existing id, with a new reference to the corresponding * recirculation context, will be returned if possible. * The life-cycle of this recirc id is managed by associating it * with the udpif key ('ukey') created for each new datapath flow. */ id = recirc_alloc_id_ctx(&state); if (!id) { XLATE_REPORT_ERROR(ctx, "Failed to allocate recirculation id"); ctx->error = XLATE_NO_RECIRCULATION_CONTEXT; return; } recirc_refs_add(&ctx->xout->recircs, id); nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_RECIRC, id); /* Undo changes done by recirculation. */ ctx->action_set.size = ctx->recirc_action_offset; ctx->recirc_action_offset = -1; ctx->last_unroll_offset = -1; } /* Called only when ctx->recirc_action_offset is set. */ static void compose_recirculate_action(struct xlate_ctx *ctx) { xlate_commit_actions(ctx); compose_recirculate_action__(ctx, 0); } /* Fork the pipeline here. The current packet will continue processing the * current action list. A clone of the current packet will recirculate, skip * the remainder of the current action list and asynchronously resume pipeline * processing in 'table' with the current metadata and action set. */ static void compose_recirculate_and_fork(struct xlate_ctx *ctx, uint8_t table) { ctx->recirc_action_offset = ctx->action_set.size; compose_recirculate_action__(ctx, table); } static void compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls) { struct flow *flow = &ctx->xin->flow; int n; ovs_assert(eth_type_mpls(mpls->ethertype)); n = flow_count_mpls_labels(flow, ctx->wc); if (!n) { xlate_commit_actions(ctx); } else if (n >= FLOW_MAX_MPLS_LABELS) { if (ctx->xin->packet != NULL) { XLATE_REPORT_ERROR(ctx, "bridge %s: dropping packet on which an " "MPLS push action can't be performed as it would " "have more MPLS LSEs than the %d supported.", ctx->xbridge->name, FLOW_MAX_MPLS_LABELS); } ctx->error = XLATE_TOO_MANY_MPLS_LABELS; return; } /* Update flow's MPLS stack, and clear L3/4 fields to mark them invalid. */ flow_push_mpls(flow, n, mpls->ethertype, ctx->wc, true); } static void compose_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type) { struct flow *flow = &ctx->xin->flow; int n = flow_count_mpls_labels(flow, ctx->wc); if (flow_pop_mpls(flow, n, eth_type, ctx->wc)) { if (ctx->xbridge->support.odp.recirc) { ctx->was_mpls = true; } } else if (n >= FLOW_MAX_MPLS_LABELS) { if (ctx->xin->packet != NULL) { XLATE_REPORT_ERROR(ctx, "bridge %s: dropping packet on which an " "MPLS pop action can't be performed as it has " "more MPLS LSEs than the %d supported.", ctx->xbridge->name, FLOW_MAX_MPLS_LABELS); } ctx->error = XLATE_TOO_MANY_MPLS_LABELS; ofpbuf_clear(ctx->odp_actions); } } static bool compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) { struct flow *flow = &ctx->xin->flow; if (!is_ip_any(flow)) { return false; } ctx->wc->masks.nw_ttl = 0xff; if (flow->nw_ttl > 1) { flow->nw_ttl--; return false; } else { size_t i; for (i = 0; i < ids->n_controllers; i++) { execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, ids->cnt_ids[i]); } /* Stop processing for current table. */ return true; } } static void compose_set_mpls_label_action(struct xlate_ctx *ctx, ovs_be32 label) { if (eth_type_mpls(ctx->xin->flow.dl_type)) { ctx->wc->masks.mpls_lse[0] |= htonl(MPLS_LABEL_MASK); set_mpls_lse_label(&ctx->xin->flow.mpls_lse[0], label); } } static void compose_set_mpls_tc_action(struct xlate_ctx *ctx, uint8_t tc) { if (eth_type_mpls(ctx->xin->flow.dl_type)) { ctx->wc->masks.mpls_lse[0] |= htonl(MPLS_TC_MASK); set_mpls_lse_tc(&ctx->xin->flow.mpls_lse[0], tc); } } static void compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl) { if (eth_type_mpls(ctx->xin->flow.dl_type)) { ctx->wc->masks.mpls_lse[0] |= htonl(MPLS_TTL_MASK); set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse[0], ttl); } } static bool compose_dec_mpls_ttl_action(struct xlate_ctx *ctx) { struct flow *flow = &ctx->xin->flow; if (eth_type_mpls(flow->dl_type)) { uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse[0]); ctx->wc->masks.mpls_lse[0] |= htonl(MPLS_TTL_MASK); if (ttl > 1) { ttl--; set_mpls_lse_ttl(&flow->mpls_lse[0], ttl); return false; } else { execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0); } } /* Stop processing for current table. */ return true; } static void xlate_output_action(struct xlate_ctx *ctx, ofp_port_t port, uint16_t max_len, bool may_packet_in) { ofp_port_t prev_nf_output_iface = ctx->nf_output_iface; ctx->nf_output_iface = NF_OUT_DROP; switch (port) { case OFPP_IN_PORT: compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL); break; case OFPP_TABLE: xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, 0, may_packet_in, true); break; case OFPP_NORMAL: xlate_normal(ctx); break; case OFPP_FLOOD: flood_packets(ctx, false); break; case OFPP_ALL: flood_packets(ctx, true); break; case OFPP_CONTROLLER: execute_controller_action(ctx, max_len, (ctx->in_group ? OFPR_GROUP : ctx->in_action_set ? OFPR_ACTION_SET : OFPR_ACTION), 0); break; case OFPP_NONE: break; case OFPP_LOCAL: default: if (port != ctx->xin->flow.in_port.ofp_port) { compose_output_action(ctx, port, NULL); } else { xlate_report(ctx, "skipping output to input port"); } break; } if (prev_nf_output_iface == NF_OUT_FLOOD) { ctx->nf_output_iface = NF_OUT_FLOOD; } else if (ctx->nf_output_iface == NF_OUT_DROP) { ctx->nf_output_iface = prev_nf_output_iface; } else if (prev_nf_output_iface != NF_OUT_DROP && ctx->nf_output_iface != NF_OUT_FLOOD) { ctx->nf_output_iface = NF_OUT_MULTI; } } static void xlate_output_reg_action(struct xlate_ctx *ctx, const struct ofpact_output_reg *or) { uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow); if (port <= UINT16_MAX) { union mf_subvalue value; memset(&value, 0xff, sizeof value); mf_write_subfield_flow(&or->src, &value, &ctx->wc->masks); xlate_output_action(ctx, u16_to_ofp(port), or->max_len, false); } } static void xlate_enqueue_action(struct xlate_ctx *ctx, const struct ofpact_enqueue *enqueue) { ofp_port_t ofp_port = enqueue->port; uint32_t queue_id = enqueue->queue; uint32_t flow_priority, priority; int error; /* Translate queue to priority. */ error = dpif_queue_to_priority(ctx->xbridge->dpif, queue_id, &priority); if (error) { /* Fall back to ordinary output action. */ xlate_output_action(ctx, enqueue->port, 0, false); return; } /* Check output port. */ if (ofp_port == OFPP_IN_PORT) { ofp_port = ctx->xin->flow.in_port.ofp_port; } else if (ofp_port == ctx->xin->flow.in_port.ofp_port) { return; } /* Add datapath actions. */ flow_priority = ctx->xin->flow.skb_priority; ctx->xin->flow.skb_priority = priority; compose_output_action(ctx, ofp_port, NULL); ctx->xin->flow.skb_priority = flow_priority; /* Update NetFlow output port. */ if (ctx->nf_output_iface == NF_OUT_DROP) { ctx->nf_output_iface = ofp_port; } else if (ctx->nf_output_iface != NF_OUT_FLOOD) { ctx->nf_output_iface = NF_OUT_MULTI; } } static void xlate_set_queue_action(struct xlate_ctx *ctx, uint32_t queue_id) { uint32_t skb_priority; if (!dpif_queue_to_priority(ctx->xbridge->dpif, queue_id, &skb_priority)) { ctx->xin->flow.skb_priority = skb_priority; } else { /* Couldn't translate queue to a priority. Nothing to do. A warning * has already been logged. */ } } static bool slave_enabled_cb(ofp_port_t ofp_port, void *xbridge_) { const struct xbridge *xbridge = xbridge_; struct xport *port; switch (ofp_port) { case OFPP_IN_PORT: case OFPP_TABLE: case OFPP_NORMAL: case OFPP_FLOOD: case OFPP_ALL: case OFPP_NONE: return true; case OFPP_CONTROLLER: /* Not supported by the bundle action. */ return false; default: port = get_ofp_port(xbridge, ofp_port); return port ? port->may_enable : false; } } static void xlate_bundle_action(struct xlate_ctx *ctx, const struct ofpact_bundle *bundle) { ofp_port_t port; port = bundle_execute(bundle, &ctx->xin->flow, ctx->wc, slave_enabled_cb, CONST_CAST(struct xbridge *, ctx->xbridge)); if (bundle->dst.field) { nxm_reg_load(&bundle->dst, ofp_to_u16(port), &ctx->xin->flow, ctx->wc); } else { xlate_output_action(ctx, port, 0, false); } } static void xlate_learn_action__(struct xlate_ctx *ctx, const struct ofpact_learn *learn, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { learn_execute(learn, &ctx->xin->flow, fm, ofpacts); if (ctx->xin->may_learn) { ofproto_dpif_flow_mod(ctx->xbridge->ofproto, fm); } } static void xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn) { learn_mask(learn, ctx->wc); if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_LEARN); entry->u.learn.ofproto = ctx->xbridge->ofproto; entry->u.learn.fm = xmalloc(sizeof *entry->u.learn.fm); entry->u.learn.ofpacts = ofpbuf_new(64); xlate_learn_action__(ctx, learn, entry->u.learn.fm, entry->u.learn.ofpacts); } else if (ctx->xin->may_learn) { uint64_t ofpacts_stub[1024 / 8]; struct ofputil_flow_mod fm; struct ofpbuf ofpacts; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); xlate_learn_action__(ctx, learn, &fm, &ofpacts); ofpbuf_uninit(&ofpacts); } } static void xlate_fin_timeout__(struct rule_dpif *rule, uint16_t tcp_flags, uint16_t idle_timeout, uint16_t hard_timeout) { if (tcp_flags & (TCP_FIN | TCP_RST)) { rule_dpif_reduce_timeouts(rule, idle_timeout, hard_timeout); } } static void xlate_fin_timeout(struct xlate_ctx *ctx, const struct ofpact_fin_timeout *oft) { if (ctx->rule) { xlate_fin_timeout__(ctx->rule, ctx->xin->tcp_flags, oft->fin_idle_timeout, oft->fin_hard_timeout); if (ctx->xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx->xin->xcache, XC_FIN_TIMEOUT); /* XC_RULE already holds a reference on the rule, none is taken * here. */ entry->u.fin.rule = ctx->rule; entry->u.fin.idle = oft->fin_idle_timeout; entry->u.fin.hard = oft->fin_hard_timeout; } } } static void xlate_sample_action(struct xlate_ctx *ctx, const struct ofpact_sample *os) { /* Scale the probability from 16-bit to 32-bit while representing * the same percentage. */ uint32_t probability = (os->probability << 16) | os->probability; if (!ctx->xbridge->support.variable_length_userdata) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_ERR_RL(&rl, "ignoring NXAST_SAMPLE action because datapath " "lacks support (needs Linux 3.10+ or kernel module from " "OVS 1.11+)"); return; } xlate_commit_actions(ctx); union user_action_cookie cookie; memset(&cookie, 0, sizeof cookie); cookie.flow_sample.type = USER_ACTION_COOKIE_FLOW_SAMPLE; cookie.flow_sample.probability = os->probability; cookie.flow_sample.collector_set_id = os->collector_set_id; cookie.flow_sample.obs_domain_id = os->obs_domain_id; cookie.flow_sample.obs_point_id = os->obs_point_id; compose_sample_action(ctx, probability, &cookie, sizeof cookie.flow_sample, ODPP_NONE, false); } static bool may_receive(const struct xport *xport, struct xlate_ctx *ctx) { if (xport->config & (is_stp(&ctx->xin->flow) ? OFPUTIL_PC_NO_RECV_STP : OFPUTIL_PC_NO_RECV)) { return false; } /* Only drop packets here if both forwarding and learning are * disabled. If just learning is enabled, we need to have * OFPP_NORMAL and the learning action have a look at the packet * before we can drop it. */ if ((!xport_stp_forward_state(xport) && !xport_stp_learn_state(xport)) || (!xport_rstp_forward_state(xport) && !xport_rstp_learn_state(xport))) { return false; } return true; } static void xlate_write_actions(struct xlate_ctx *ctx, const struct ofpact *a) { const struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a); size_t on_len = ofpact_nest_get_action_len(on); const struct ofpact *inner; /* Maintain actset_output depending on the contents of the action set: * * - OFPP_UNSET, if there is no "output" action. * * - The output port, if there is an "output" action and no "group" * action. * * - OFPP_UNSET, if there is a "group" action. */ if (!ctx->action_set_has_group) { OFPACT_FOR_EACH (inner, on->actions, on_len) { if (inner->type == OFPACT_OUTPUT) { ctx->xin->flow.actset_output = ofpact_get_OUTPUT(inner)->port; } else if (inner->type == OFPACT_GROUP) { ctx->xin->flow.actset_output = OFPP_UNSET; ctx->action_set_has_group = true; break; } } } ofpbuf_put(&ctx->action_set, on->actions, on_len); ofpact_pad(&ctx->action_set); } static void xlate_action_set(struct xlate_ctx *ctx) { uint64_t action_list_stub[1024 / 64]; struct ofpbuf action_list; ctx->in_action_set = true; ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub); ofpacts_execute_action_set(&action_list, &ctx->action_set); /* Clear the action set, as it is not needed any more. */ ofpbuf_clear(&ctx->action_set); do_xlate_actions(action_list.data, action_list.size, ctx); ctx->in_action_set = false; ofpbuf_uninit(&action_list); } static void recirc_put_unroll_xlate(struct xlate_ctx *ctx) { struct ofpact_unroll_xlate *unroll; unroll = ctx->last_unroll_offset < 0 ? NULL : ALIGNED_CAST(struct ofpact_unroll_xlate *, (char *)ctx->action_set.data + ctx->last_unroll_offset); /* Restore the table_id and rule cookie for a potential PACKET * IN if needed. */ if (!unroll || (ctx->table_id != unroll->rule_table_id || ctx->rule_cookie != unroll->rule_cookie)) { ctx->last_unroll_offset = ctx->action_set.size; unroll = ofpact_put_UNROLL_XLATE(&ctx->action_set); unroll->rule_table_id = ctx->table_id; unroll->rule_cookie = ctx->rule_cookie; } } /* Copy remaining actions to the action_set to be executed after recirculation. * UNROLL_XLATE action is inserted, if not already done so, before actions that * may depend on the current table ID or flow cookie. */ static void recirc_unroll_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct xlate_ctx *ctx) { const struct ofpact *a; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { switch (a->type) { case OFPACT_OUTPUT_REG: case OFPACT_GROUP: case OFPACT_OUTPUT: case OFPACT_CONTROLLER: case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: /* These actions may generate asynchronous messages, which include * table ID and flow cookie information. */ recirc_put_unroll_xlate(ctx); break; case OFPACT_RESUBMIT: if (ofpact_get_RESUBMIT(a)->table_id == 0xff) { /* This resubmit action is relative to the current table, so we * need to track what table that is.*/ recirc_put_unroll_xlate(ctx); } break; case OFPACT_SET_TUNNEL: case OFPACT_REG_MOVE: case OFPACT_SET_FIELD: case OFPACT_STACK_PUSH: case OFPACT_STACK_POP: case OFPACT_LEARN: case OFPACT_WRITE_METADATA: case OFPACT_GOTO_TABLE: case OFPACT_ENQUEUE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: case OFPACT_SET_IPV4_DST: case OFPACT_SET_IP_DSCP: case OFPACT_SET_IP_ECN: case OFPACT_SET_IP_TTL: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: case OFPACT_PUSH_MPLS: case OFPACT_POP_MPLS: case OFPACT_SET_MPLS_LABEL: case OFPACT_SET_MPLS_TC: case OFPACT_SET_MPLS_TTL: case OFPACT_MULTIPATH: case OFPACT_BUNDLE: case OFPACT_EXIT: case OFPACT_UNROLL_XLATE: case OFPACT_FIN_TIMEOUT: case OFPACT_CLEAR_ACTIONS: case OFPACT_WRITE_ACTIONS: case OFPACT_METER: case OFPACT_SAMPLE: case OFPACT_DEBUG_RECIRC: case OFPACT_CT: /* These may not generate PACKET INs. */ break; case OFPACT_NOTE: case OFPACT_CONJUNCTION: /* These need not be copied for restoration. */ continue; } /* Copy the action over. */ ofpbuf_put(&ctx->action_set, a, OFPACT_ALIGN(a->len)); } } #define CHECK_MPLS_RECIRCULATION() \ if (ctx->was_mpls) { \ ctx_trigger_recirculation(ctx); \ break; \ } #define CHECK_MPLS_RECIRCULATION_IF(COND) \ if (COND) { \ CHECK_MPLS_RECIRCULATION(); \ } static void put_ct_mark(const struct flow *flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { struct { uint32_t key; uint32_t mask; } odp_attr; odp_attr.key = flow->ct_mark & wc->masks.ct_mark; odp_attr.mask = wc->masks.ct_mark; if (odp_attr.mask) { nl_msg_put_unspec(odp_actions, OVS_CT_ATTR_MARK, &odp_attr, sizeof(odp_attr)); } } static void put_ct_label(const struct flow *flow, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { if (!ovs_u128_is_zero(&wc->masks.ct_label)) { struct { ovs_u128 key; ovs_u128 mask; } *odp_ct_label; odp_ct_label = nl_msg_put_unspec_uninit(odp_actions, OVS_CT_ATTR_LABELS, sizeof(*odp_ct_label)); odp_ct_label->key = ovs_u128_and(flow->ct_label, wc->masks.ct_label); odp_ct_label->mask = wc->masks.ct_label; } } static void put_ct_helper(struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc) { if (ofc->alg) { if (ofc->alg == IPPORT_FTP) { nl_msg_put_string(odp_actions, OVS_CT_ATTR_HELPER, "ftp"); } else { VLOG_WARN("Cannot serialize ct_helper %d\n", ofc->alg); } } } static void compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc) { ovs_u128 old_ct_label = ctx->xin->flow.ct_label; ovs_u128 old_ct_label_mask = ctx->wc->masks.ct_label; uint32_t old_ct_mark = ctx->xin->flow.ct_mark; uint32_t old_ct_mark_mask = ctx->wc->masks.ct_mark; size_t ct_offset; uint16_t zone; /* Ensure that any prior actions are applied before composing the new * conntrack action. */ xlate_commit_actions(ctx); /* Process nested actions first, to populate the key. */ ctx->wc->masks.ct_mark = 0; ctx->wc->masks.ct_label.u64.hi = ctx->wc->masks.ct_label.u64.lo = 0; do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx); if (ofc->zone_src.field) { zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow); } else { zone = ofc->zone_imm; } ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT); if (ofc->flags & NX_CT_F_COMMIT) { nl_msg_put_flag(ctx->odp_actions, OVS_CT_ATTR_COMMIT); } nl_msg_put_u16(ctx->odp_actions, OVS_CT_ATTR_ZONE, zone); put_ct_mark(&ctx->xin->flow, ctx->odp_actions, ctx->wc); put_ct_label(&ctx->xin->flow, ctx->odp_actions, ctx->wc); put_ct_helper(ctx->odp_actions, ofc); nl_msg_end_nested(ctx->odp_actions, ct_offset); /* Restore the original ct fields in the key. These should only be exposed * after recirculation to another table. */ ctx->xin->flow.ct_mark = old_ct_mark; ctx->wc->masks.ct_mark = old_ct_mark_mask; ctx->xin->flow.ct_label = old_ct_label; ctx->wc->masks.ct_label = old_ct_label_mask; if (ofc->recirc_table == NX_CT_RECIRC_NONE) { /* If we do not recirculate as part of this action, hide the results of * connection tracking from subsequent recirculations. */ ctx->conntracked = false; } else { /* Use ct_* fields from datapath during recirculation upcall. */ ctx->conntracked = true; compose_recirculate_and_fork(ctx, ofc->recirc_table); ctx->conntracked = false; } } static void do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct xlate_ctx *ctx) { struct flow_wildcards *wc = ctx->wc; struct flow *flow = &ctx->xin->flow; const struct ofpact *a; if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) { tnl_neigh_snoop(flow, wc, ctx->xbridge->name); } /* dl_type already in the mask, not set below. */ OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { struct ofpact_controller *controller; const struct ofpact_metadata *metadata; const struct ofpact_set_field *set_field; const struct mf_field *mf; if (ctx->error) { break; } if (ctx->exit) { /* Check if need to store the remaining actions for later * execution. */ if (exit_recirculates(ctx)) { recirc_unroll_actions(a, OFPACT_ALIGN(ofpacts_len - ((uint8_t *)a - (uint8_t *)ofpacts)), ctx); } break; } switch (a->type) { case OFPACT_OUTPUT: xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port, ofpact_get_OUTPUT(a)->max_len, true); break; case OFPACT_GROUP: if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) { /* Group could not be found. */ return; } break; case OFPACT_CONTROLLER: controller = ofpact_get_CONTROLLER(a); execute_controller_action(ctx, controller->max_len, controller->reason, controller->controller_id); break; case OFPACT_ENQUEUE: memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a)); break; case OFPACT_SET_VLAN_VID: wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); if (flow->vlan_tci & htons(VLAN_CFI) || ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) { flow->vlan_tci &= ~htons(VLAN_VID_MASK); flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid) | htons(VLAN_CFI)); } break; case OFPACT_SET_VLAN_PCP: wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI); if (flow->vlan_tci & htons(VLAN_CFI) || ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) { flow->vlan_tci &= ~htons(VLAN_PCP_MASK); flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI); } break; case OFPACT_STRIP_VLAN: memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); flow->vlan_tci = htons(0); break; case OFPACT_PUSH_VLAN: /* XXX 802.1AD(QinQ) */ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); flow->vlan_tci = htons(VLAN_CFI); break; case OFPACT_SET_ETH_SRC: WC_MASK_FIELD(wc, dl_src); flow->dl_src = ofpact_get_SET_ETH_SRC(a)->mac; break; case OFPACT_SET_ETH_DST: WC_MASK_FIELD(wc, dl_dst); flow->dl_dst = ofpact_get_SET_ETH_DST(a)->mac; break; case OFPACT_SET_IPV4_SRC: CHECK_MPLS_RECIRCULATION(); if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4; } break; case OFPACT_SET_IPV4_DST: CHECK_MPLS_RECIRCULATION(); if (flow->dl_type == htons(ETH_TYPE_IP)) { memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4; } break; case OFPACT_SET_IP_DSCP: CHECK_MPLS_RECIRCULATION(); if (is_ip_any(flow)) { wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp; } break; case OFPACT_SET_IP_ECN: CHECK_MPLS_RECIRCULATION(); if (is_ip_any(flow)) { wc->masks.nw_tos |= IP_ECN_MASK; flow->nw_tos &= ~IP_ECN_MASK; flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn; } break; case OFPACT_SET_IP_TTL: CHECK_MPLS_RECIRCULATION(); if (is_ip_any(flow)) { wc->masks.nw_ttl = 0xff; flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl; } break; case OFPACT_SET_L4_SRC_PORT: CHECK_MPLS_RECIRCULATION(); if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); } break; case OFPACT_SET_L4_DST_PORT: CHECK_MPLS_RECIRCULATION(); if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port); } break; case OFPACT_RESUBMIT: /* Recirculation complicates resubmit. There are two cases: * * - If mpls_pop has been executed, then the flow table lookup * as part of resubmit might depend on fields that can only * be obtained via recirculation, so the resubmit itself * triggers recirculation and we need to make sure that the * resubmit is executed again after recirculation. * Therefore, in this case we trigger recirculation and let * the code following this "switch" append the resubmit to * the post-recirculation actions. * * - Otherwise, some action in the flow entry found by resubmit * might trigger recirculation. If that happens, then we do * not want to execute the resubmit again after * recirculation, so we want to skip back to the head of the * loop to avoid that, only adding any actions that follow * the resubmit to the post-recirculation actions. */ if (ctx->was_mpls) { ctx_trigger_recirculation(ctx); break; } xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a)); continue; case OFPACT_SET_TUNNEL: flow->tunnel.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id); break; case OFPACT_SET_QUEUE: memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id); break; case OFPACT_POP_QUEUE: memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); flow->skb_priority = ctx->orig_skb_priority; break; case OFPACT_REG_MOVE: CHECK_MPLS_RECIRCULATION_IF( mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->dst.field) || mf_is_l3_or_higher(ofpact_get_REG_MOVE(a)->src.field)); nxm_execute_reg_move(ofpact_get_REG_MOVE(a), flow, wc); break; case OFPACT_SET_FIELD: CHECK_MPLS_RECIRCULATION_IF( mf_is_l3_or_higher(ofpact_get_SET_FIELD(a)->field)); set_field = ofpact_get_SET_FIELD(a); mf = set_field->field; /* Set field action only ever overwrites packet's outermost * applicable header fields. Do nothing if no header exists. */ if (mf->id == MFF_VLAN_VID) { wc->masks.vlan_tci |= htons(VLAN_CFI); if (!(flow->vlan_tci & htons(VLAN_CFI))) { break; } } else if ((mf->id == MFF_MPLS_LABEL || mf->id == MFF_MPLS_TC) /* 'dl_type' is already unwildcarded. */ && !eth_type_mpls(flow->dl_type)) { break; } /* A flow may wildcard nw_frag. Do nothing if setting a transport * header field on a packet that does not have them. */ mf_mask_field_and_prereqs__(mf, &set_field->mask, wc); if (mf_are_prereqs_ok(mf, flow)) { mf_set_flow_value_masked(mf, &set_field->value, &set_field->mask, flow); } break; case OFPACT_STACK_PUSH: CHECK_MPLS_RECIRCULATION_IF( mf_is_l3_or_higher(ofpact_get_STACK_PUSH(a)->subfield.field)); nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc, &ctx->stack); break; case OFPACT_STACK_POP: CHECK_MPLS_RECIRCULATION_IF( mf_is_l3_or_higher(ofpact_get_STACK_POP(a)->subfield.field)); nxm_execute_stack_pop(ofpact_get_STACK_POP(a), flow, wc, &ctx->stack); break; case OFPACT_PUSH_MPLS: /* Recirculate if it is an IP packet with a zero ttl. This may * indicate that the packet was previously MPLS and an MPLS pop * action converted it to IP. In this case recirculating should * reveal the IP TTL which is used as the basis for a new MPLS * LSE. */ CHECK_MPLS_RECIRCULATION_IF( !flow_count_mpls_labels(flow, wc) && flow->nw_ttl == 0 && is_ip_any(flow)); compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)); break; case OFPACT_POP_MPLS: CHECK_MPLS_RECIRCULATION(); compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype); break; case OFPACT_SET_MPLS_LABEL: CHECK_MPLS_RECIRCULATION(); compose_set_mpls_label_action( ctx, ofpact_get_SET_MPLS_LABEL(a)->label); break; case OFPACT_SET_MPLS_TC: CHECK_MPLS_RECIRCULATION(); compose_set_mpls_tc_action(ctx, ofpact_get_SET_MPLS_TC(a)->tc); break; case OFPACT_SET_MPLS_TTL: CHECK_MPLS_RECIRCULATION(); compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl); break; case OFPACT_DEC_MPLS_TTL: CHECK_MPLS_RECIRCULATION(); if (compose_dec_mpls_ttl_action(ctx)) { return; } break; case OFPACT_DEC_TTL: CHECK_MPLS_RECIRCULATION(); wc->masks.nw_ttl = 0xff; if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) { return; } break; case OFPACT_NOTE: /* Nothing to do. */ break; case OFPACT_MULTIPATH: CHECK_MPLS_RECIRCULATION(); multipath_execute(ofpact_get_MULTIPATH(a), flow, wc); break; case OFPACT_BUNDLE: CHECK_MPLS_RECIRCULATION(); xlate_bundle_action(ctx, ofpact_get_BUNDLE(a)); break; case OFPACT_OUTPUT_REG: xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a)); break; case OFPACT_LEARN: CHECK_MPLS_RECIRCULATION(); xlate_learn_action(ctx, ofpact_get_LEARN(a)); break; case OFPACT_CONJUNCTION: { /* A flow with a "conjunction" action represents part of a special * kind of "set membership match". Such a flow should not actually * get executed, but it could via, say, a "packet-out", even though * that wouldn't be useful. Log it to help debugging. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_INFO_RL(&rl, "executing no-op conjunction action"); break; } case OFPACT_EXIT: ctx->exit = true; break; case OFPACT_UNROLL_XLATE: { struct ofpact_unroll_xlate *unroll = ofpact_get_UNROLL_XLATE(a); /* Restore translation context data that was stored earlier. */ ctx->table_id = unroll->rule_table_id; ctx->rule_cookie = unroll->rule_cookie; break; } case OFPACT_FIN_TIMEOUT: CHECK_MPLS_RECIRCULATION(); memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a)); break; case OFPACT_CLEAR_ACTIONS: ofpbuf_clear(&ctx->action_set); ctx->xin->flow.actset_output = OFPP_UNSET; ctx->action_set_has_group = false; break; case OFPACT_WRITE_ACTIONS: xlate_write_actions(ctx, a); break; case OFPACT_WRITE_METADATA: metadata = ofpact_get_WRITE_METADATA(a); flow->metadata &= ~metadata->mask; flow->metadata |= metadata->metadata & metadata->mask; break; case OFPACT_METER: /* Not implemented yet. */ break; case OFPACT_GOTO_TABLE: { struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a); /* Allow ctx->table_id == TBL_INTERNAL, which will be greater * than ogt->table_id. This is to allow goto_table actions that * triggered recirculation: ctx->table_id will be TBL_INTERNAL * after recirculation. */ ovs_assert(ctx->table_id == TBL_INTERNAL || ctx->table_id < ogt->table_id); xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, ogt->table_id, true, true); break; } case OFPACT_SAMPLE: xlate_sample_action(ctx, ofpact_get_SAMPLE(a)); break; case OFPACT_CT: CHECK_MPLS_RECIRCULATION(); compose_conntrack_action(ctx, ofpact_get_CT(a)); break; case OFPACT_DEBUG_RECIRC: ctx_trigger_recirculation(ctx); a = ofpact_next(a); break; } /* Check if need to store this and the remaining actions for later * execution. */ if (!ctx->error && ctx->exit && ctx_first_recirculation_action(ctx)) { recirc_unroll_actions(a, OFPACT_ALIGN(ofpacts_len - ((uint8_t *)a - (uint8_t *)ofpacts)), ctx); break; } } } void xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto, const struct flow *flow, ofp_port_t in_port, struct rule_dpif *rule, uint16_t tcp_flags, const struct dp_packet *packet, struct flow_wildcards *wc, struct ofpbuf *odp_actions) { xin->ofproto = ofproto; xin->flow = *flow; xin->flow.in_port.ofp_port = in_port; xin->flow.actset_output = OFPP_UNSET; xin->packet = packet; xin->may_learn = packet != NULL; xin->rule = rule; xin->xcache = NULL; xin->ofpacts = NULL; xin->ofpacts_len = 0; xin->tcp_flags = tcp_flags; xin->resubmit_hook = NULL; xin->report_hook = NULL; xin->resubmit_stats = NULL; xin->recurse = 0; xin->resubmits = 0; xin->wc = wc; xin->odp_actions = odp_actions; /* Do recirc lookup. */ xin->recirc = flow->recirc_id ? recirc_id_node_find(flow->recirc_id) : NULL; } void xlate_out_uninit(struct xlate_out *xout) { if (xout) { recirc_refs_unref(&xout->recircs); } } /* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts' * into datapath actions, using 'ctx', and discards the datapath actions. */ void xlate_actions_for_side_effects(struct xlate_in *xin) { struct xlate_out xout; enum xlate_error error; error = xlate_actions(xin, &xout); if (error) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "xlate_actions failed (%s)!", xlate_strerror(error)); } xlate_out_uninit(&xout); } static struct skb_priority_to_dscp * get_skb_priority(const struct xport *xport, uint32_t skb_priority) { struct skb_priority_to_dscp *pdscp; uint32_t hash; hash = hash_int(skb_priority, 0); HMAP_FOR_EACH_IN_BUCKET (pdscp, hmap_node, hash, &xport->skb_priorities) { if (pdscp->skb_priority == skb_priority) { return pdscp; } } return NULL; } static bool dscp_from_skb_priority(const struct xport *xport, uint32_t skb_priority, uint8_t *dscp) { struct skb_priority_to_dscp *pdscp = get_skb_priority(xport, skb_priority); *dscp = pdscp ? pdscp->dscp : 0; return pdscp != NULL; } static size_t count_skb_priorities(const struct xport *xport) { return hmap_count(&xport->skb_priorities); } static void clear_skb_priorities(struct xport *xport) { struct skb_priority_to_dscp *pdscp, *next; HMAP_FOR_EACH_SAFE (pdscp, next, hmap_node, &xport->skb_priorities) { hmap_remove(&xport->skb_priorities, &pdscp->hmap_node); free(pdscp); } } static bool actions_output_to_local_port(const struct xlate_ctx *ctx) { odp_port_t local_odp_port = ofp_port_to_odp_port(ctx->xbridge, OFPP_LOCAL); const struct nlattr *a; unsigned int left; NL_ATTR_FOR_EACH_UNSAFE (a, left, ctx->odp_actions->data, ctx->odp_actions->size) { if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT && nl_attr_get_odp_port(a) == local_odp_port) { return true; } } return false; } #if defined(__linux__) /* Returns the maximum number of packets that the Linux kernel is willing to * queue up internally to certain kinds of software-implemented ports, or the * default (and rarely modified) value if it cannot be determined. */ static int netdev_max_backlog(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int max_backlog = 1000; /* The normal default value. */ if (ovsthread_once_start(&once)) { static const char filename[] = "/proc/sys/net/core/netdev_max_backlog"; FILE *stream; int n; stream = fopen(filename, "r"); if (!stream) { VLOG_INFO("%s: open failed (%s)", filename, ovs_strerror(errno)); } else { if (fscanf(stream, "%d", &n) != 1) { VLOG_WARN("%s: read error", filename); } else if (n <= 100) { VLOG_WARN("%s: unexpectedly small value %d", filename, n); } else { max_backlog = n; } fclose(stream); } ovsthread_once_done(&once); VLOG_DBG("%s: using %d max_backlog", filename, max_backlog); } return max_backlog; } /* Counts and returns the number of OVS_ACTION_ATTR_OUTPUT actions in * 'odp_actions'. */ static int count_output_actions(const struct ofpbuf *odp_actions) { const struct nlattr *a; size_t left; int n = 0; NL_ATTR_FOR_EACH_UNSAFE (a, left, odp_actions->data, odp_actions->size) { if (a->nla_type == OVS_ACTION_ATTR_OUTPUT) { n++; } } return n; } #endif /* defined(__linux__) */ /* Returns true if 'odp_actions' contains more output actions than the datapath * can reliably handle in one go. On Linux, this is the value of the * net.core.netdev_max_backlog sysctl, which limits the maximum number of * packets that the kernel is willing to queue up for processing while the * datapath is processing a set of actions. */ static bool too_many_output_actions(const struct ofpbuf *odp_actions OVS_UNUSED) { #ifdef __linux__ return (odp_actions->size / NL_A_U32_SIZE > netdev_max_backlog() && count_output_actions(odp_actions) > netdev_max_backlog()); #else /* OSes other than Linux might have similar limits, but we don't know how * to determine them.*/ return false; #endif } static void xlate_wc_init(struct xlate_ctx *ctx) { flow_wildcards_init_catchall(ctx->wc); /* Some fields we consider to always be examined. */ WC_MASK_FIELD(ctx->wc, in_port); WC_MASK_FIELD(ctx->wc, dl_type); if (is_ip_any(&ctx->xin->flow)) { WC_MASK_FIELD_MASK(ctx->wc, nw_frag, FLOW_NW_FRAG_MASK); } if (ctx->xbridge->support.odp.recirc) { /* Always exactly match recirc_id when datapath supports * recirculation. */ WC_MASK_FIELD(ctx->wc, recirc_id); } if (ctx->xbridge->netflow) { netflow_mask_wc(&ctx->xin->flow, ctx->wc); } tnl_wc_init(&ctx->xin->flow, ctx->wc); } static void xlate_wc_finish(struct xlate_ctx *ctx) { /* Clear the metadata and register wildcard masks, because we won't * use non-header fields as part of the cache. */ flow_wildcards_clear_non_packet_fields(ctx->wc); /* ICMPv4 and ICMPv6 have 8-bit "type" and "code" fields. struct flow * uses the low 8 bits of the 16-bit tp_src and tp_dst members to * represent these fields. The datapath interface, on the other hand, * represents them with just 8 bits each. This means that if the high * 8 bits of the masks for these fields somehow become set, then they * will get chopped off by a round trip through the datapath, and * revalidation will spot that as an inconsistency and delete the flow. * Avoid the problem here by making sure that only the low 8 bits of * either field can be unwildcarded for ICMP. */ if (is_icmpv4(&ctx->xin->flow, NULL) || is_icmpv6(&ctx->xin->flow, NULL)) { ctx->wc->masks.tp_src &= htons(UINT8_MAX); ctx->wc->masks.tp_dst &= htons(UINT8_MAX); } /* VLAN_TCI CFI bit must be matched if any of the TCI is matched. */ if (ctx->wc->masks.vlan_tci) { ctx->wc->masks.vlan_tci |= htons(VLAN_CFI); } /* The classifier might return masks that match on tp_src and tp_dst even * for later fragments. This happens because there might be flows that * match on tp_src or tp_dst without matching on the frag bits, because * it is not a prerequisite for OpenFlow. Since it is a prerequisite for * datapath flows and since tp_src and tp_dst are always going to be 0, * wildcard the fields here. */ if (ctx->xin->flow.nw_frag & FLOW_NW_FRAG_LATER) { ctx->wc->masks.tp_src = 0; ctx->wc->masks.tp_dst = 0; } } /* Translates the flow, actions, or rule in 'xin' into datapath actions in * 'xout'. * The caller must take responsibility for eventually freeing 'xout', with * xlate_out_uninit(). * Returns 'XLATE_OK' if translation was successful. In case of an error an * empty set of actions will be returned in 'xin->odp_actions' (if non-NULL), * so that most callers may ignore the return value and transparently install a * drop flow when the translation fails. */ enum xlate_error xlate_actions(struct xlate_in *xin, struct xlate_out *xout) { *xout = (struct xlate_out) { .slow = 0, .fail_open = false, .recircs = RECIRC_REFS_EMPTY_INITIALIZER, }; struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xbridge *xbridge = xbridge_lookup(xcfg, xin->ofproto); if (!xbridge) { return XLATE_BRIDGE_NOT_FOUND; } struct flow *flow = &xin->flow; union mf_subvalue stack_stub[1024 / sizeof(union mf_subvalue)]; uint64_t action_set_stub[1024 / 8]; uint64_t actions_stub[256 / 8]; struct ofpbuf scratch_actions = OFPBUF_STUB_INITIALIZER(actions_stub); struct xlate_ctx ctx = { .xin = xin, .xout = xout, .base_flow = *flow, .orig_tunnel_ipv6_dst = flow_tnl_dst(&flow->tunnel), .xbridge = xbridge, .stack = OFPBUF_STUB_INITIALIZER(stack_stub), .rule = xin->rule, .wc = (xin->wc ? xin->wc : &(struct flow_wildcards) { .masks = { .dl_type = 0 } }), .odp_actions = xin->odp_actions ? xin->odp_actions : &scratch_actions, .recurse = xin->recurse, .resubmits = xin->resubmits, .in_group = false, .in_action_set = false, .table_id = 0, .rule_cookie = OVS_BE64_MAX, .orig_skb_priority = flow->skb_priority, .sflow_n_outputs = 0, .sflow_odp_port = 0, .nf_output_iface = NF_OUT_DROP, .exit = false, .error = XLATE_OK, .mirrors = 0, .recirc_action_offset = -1, .last_unroll_offset = -1, .was_mpls = false, .conntracked = false, .action_set_has_group = false, .action_set = OFPBUF_STUB_INITIALIZER(action_set_stub), }; /* 'base_flow' reflects the packet as it came in, but we need it to reflect * the packet as the datapath will treat it for output actions: * * - Our datapath doesn't retain tunneling information without us * re-setting it, so clear the tunnel data. * * - For VLAN splinters, a higher layer may pretend that the packet * came in on 'flow->in_port.ofp_port' with 'flow->vlan_tci' * attached, because that's how we want to treat it from an OpenFlow * perspective. But from the datapath's perspective it actually came * in on a VLAN device without any VLAN attached. So here we put the * datapath's view of the VLAN information in 'base_flow' to ensure * correct treatment. */ memset(&ctx.base_flow.tunnel, 0, sizeof ctx.base_flow.tunnel); if (flow->in_port.ofp_port != vsp_realdev_to_vlandev(xbridge->ofproto, flow->in_port.ofp_port, flow->vlan_tci)) { ctx.base_flow.vlan_tci = 0; } ofpbuf_reserve(ctx.odp_actions, NL_A_U32_SIZE); xlate_wc_init(&ctx); COVERAGE_INC(xlate_actions); if (xin->recirc) { const struct recirc_state *state = &xin->recirc->state; xlate_report(&ctx, "Restoring state post-recirculation:"); if (xin->ofpacts_len > 0 || ctx.rule) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); const char *conflict = xin->ofpacts_len ? "actions" : "rule"; VLOG_WARN_RL(&rl, "Recirculation conflict (%s)!", conflict); xlate_report(&ctx, "- Recirculation conflict (%s)!", conflict); ctx.error = XLATE_RECIRCULATION_CONFLICT; goto exit; } /* Set the bridge for post-recirculation processing if needed. */ if (ctx.xbridge->ofproto != state->ofproto) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); const struct xbridge *new_bridge = xbridge_lookup(xcfg, state->ofproto); if (OVS_UNLIKELY(!new_bridge)) { /* Drop the packet if the bridge cannot be found. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "Recirculation bridge no longer exists."); xlate_report(&ctx, "- Recirculation bridge no longer exists."); ctx.error = XLATE_BRIDGE_NOT_FOUND; goto exit; } ctx.xbridge = new_bridge; } /* Set the post-recirculation table id. Note: A table lookup is done * only if there are no post-recirculation actions. */ ctx.table_id = state->table_id; xlate_report(&ctx, "- Resuming from table %"PRIu8, ctx.table_id); ctx.conntracked = state->conntracked; if (!state->conntracked) { clear_conntrack(flow); } /* Restore pipeline metadata. May change flow's in_port and other * metadata to the values that existed when recirculation was * triggered. */ recirc_metadata_to_flow(&state->metadata, flow); /* Restore stack, if any. */ if (state->stack) { ofpbuf_put(&ctx.stack, state->stack->data, state->stack->size); } /* Restore mirror state. */ ctx.mirrors = state->mirrors; /* Restore action set, if any. */ if (state->action_set_len) { const struct ofpact *a; xlate_report_actions(&ctx, "- Restoring action set", state->ofpacts, state->action_set_len); ofpbuf_put(&ctx.action_set, state->ofpacts, state->action_set_len); OFPACT_FOR_EACH(a, state->ofpacts, state->action_set_len) { if (a->type == OFPACT_GROUP) { ctx.action_set_has_group = true; break; } } } /* Restore recirculation actions. If there are no actions, processing * will start with a lookup in the table set above. */ if (state->ofpacts_len > state->action_set_len) { xin->ofpacts_len = state->ofpacts_len - state->action_set_len; xin->ofpacts = state->ofpacts + state->action_set_len / sizeof *state->ofpacts; xlate_report_actions(&ctx, "- Restoring actions", xin->ofpacts, xin->ofpacts_len); } } else if (OVS_UNLIKELY(flow->recirc_id)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&rl, "Recirculation context not found for ID %"PRIx32, flow->recirc_id); ctx.error = XLATE_NO_RECIRCULATION_CONTEXT; goto exit; } /* The bridge is now known so obtain its table version. */ ctx.tables_version = ofproto_dpif_get_tables_version(ctx.xbridge->ofproto); if (!xin->ofpacts && !ctx.rule) { ctx.rule = rule_dpif_lookup_from_table( ctx.xbridge->ofproto, ctx.tables_version, flow, ctx.wc, ctx.xin->resubmit_stats, &ctx.table_id, flow->in_port.ofp_port, true, true); if (ctx.xin->resubmit_stats) { rule_dpif_credit_stats(ctx.rule, ctx.xin->resubmit_stats); } if (ctx.xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx.xin->xcache, XC_RULE); entry->u.rule = ctx.rule; rule_dpif_ref(ctx.rule); } if (OVS_UNLIKELY(ctx.xin->resubmit_hook)) { ctx.xin->resubmit_hook(ctx.xin, ctx.rule, 0); } } xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule); /* Get the proximate input port of the packet. (If xin->recirc, * flow->in_port is the ultimate input port of the packet.) */ struct xport *in_port = get_ofp_port(xbridge, ctx.base_flow.in_port.ofp_port); /* Tunnel stats only for non-recirculated packets. */ if (!xin->recirc && in_port && in_port->is_tunnel) { if (ctx.xin->resubmit_stats) { netdev_vport_inc_rx(in_port->netdev, ctx.xin->resubmit_stats); if (in_port->bfd) { bfd_account_rx(in_port->bfd, ctx.xin->resubmit_stats); } } if (ctx.xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx.xin->xcache, XC_NETDEV); entry->u.dev.rx = netdev_ref(in_port->netdev); entry->u.dev.bfd = bfd_ref(in_port->bfd); } } if (!xin->recirc && process_special(&ctx, in_port)) { /* process_special() did all the processing for this packet. * * We do not perform special processing on recirculated packets, as * recirculated packets are not really received by the bridge.*/ } else if (in_port && in_port->xbundle && xbundle_mirror_out(xbridge, in_port->xbundle)) { if (ctx.xin->packet != NULL) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port " "%s, which is reserved exclusively for mirroring", ctx.xbridge->name, in_port->xbundle->name); } } else { /* Sampling is done only for packets really received by the bridge. */ unsigned int user_cookie_offset = 0; if (!xin->recirc) { user_cookie_offset = compose_sflow_action(&ctx); compose_ipfix_action(&ctx, ODPP_NONE); } size_t sample_actions_len = ctx.odp_actions->size; if (tnl_process_ecn(flow) && (!in_port || may_receive(in_port, &ctx))) { const struct ofpact *ofpacts; size_t ofpacts_len; if (xin->ofpacts) { ofpacts = xin->ofpacts; ofpacts_len = xin->ofpacts_len; } else if (ctx.rule) { const struct rule_actions *actions = rule_dpif_get_actions(ctx.rule); ofpacts = actions->ofpacts; ofpacts_len = actions->ofpacts_len; ctx.rule_cookie = rule_dpif_get_flow_cookie(ctx.rule); } else { OVS_NOT_REACHED(); } mirror_ingress_packet(&ctx); do_xlate_actions(ofpacts, ofpacts_len, &ctx); if (ctx.error) { goto exit; } /* We've let OFPP_NORMAL and the learning action look at the * packet, so drop it now if forwarding is disabled. */ if (in_port && (!xport_stp_forward_state(in_port) || !xport_rstp_forward_state(in_port))) { /* Drop all actions added by do_xlate_actions() above. */ ctx.odp_actions->size = sample_actions_len; /* Undo changes that may have been done for recirculation. */ if (exit_recirculates(&ctx)) { ctx.action_set.size = ctx.recirc_action_offset; ctx.recirc_action_offset = -1; ctx.last_unroll_offset = -1; } } else if (ctx.action_set.size) { /* Translate action set only if not dropping the packet and * not recirculating. */ if (!exit_recirculates(&ctx)) { xlate_action_set(&ctx); } } /* Check if need to recirculate. */ if (exit_recirculates(&ctx)) { compose_recirculate_action(&ctx); } } /* Output only fully processed packets. */ if (!exit_recirculates(&ctx) && xbridge->has_in_band && in_band_must_output_to_local_port(flow) && !actions_output_to_local_port(&ctx)) { compose_output_action(&ctx, OFPP_LOCAL, NULL); } if (user_cookie_offset) { fix_sflow_action(&ctx, user_cookie_offset); } } if (nl_attr_oversized(ctx.odp_actions->size)) { /* These datapath actions are too big for a Netlink attribute, so we * can't hand them to the kernel directly. dpif_execute() can execute * them one by one with help, so just mark the result as SLOW_ACTION to * prevent the flow from being installed. */ COVERAGE_INC(xlate_actions_oversize); ctx.xout->slow |= SLOW_ACTION; } else if (too_many_output_actions(ctx.odp_actions)) { COVERAGE_INC(xlate_actions_too_many_output); ctx.xout->slow |= SLOW_ACTION; } /* Do netflow only for packets really received by the bridge and not sent * to the controller. We consider packets sent to the controller to be * part of the control plane rather than the data plane. */ if (!xin->recirc && xbridge->netflow && !(xout->slow & SLOW_CONTROLLER)) { if (ctx.xin->resubmit_stats) { netflow_flow_update(xbridge->netflow, flow, ctx.nf_output_iface, ctx.xin->resubmit_stats); } if (ctx.xin->xcache) { struct xc_entry *entry; entry = xlate_cache_add_entry(ctx.xin->xcache, XC_NETFLOW); entry->u.nf.netflow = netflow_ref(xbridge->netflow); entry->u.nf.flow = xmemdup(flow, sizeof *flow); entry->u.nf.iface = ctx.nf_output_iface; } } xlate_wc_finish(&ctx); exit: ofpbuf_uninit(&ctx.stack); ofpbuf_uninit(&ctx.action_set); ofpbuf_uninit(&scratch_actions); /* Make sure we return a "drop flow" in case of an error. */ if (ctx.error) { xout->slow = 0; if (xin->odp_actions) { ofpbuf_clear(xin->odp_actions); } } return ctx.error; } /* Sends 'packet' out 'ofport'. * May modify 'packet'. * Returns 0 if successful, otherwise a positive errno value. */ int xlate_send_packet(const struct ofport_dpif *ofport, struct dp_packet *packet) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xport *xport; struct ofpact_output output; struct flow flow; ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output); /* Use OFPP_NONE as the in_port to avoid special packet processing. */ flow_extract(packet, &flow); flow.in_port.ofp_port = OFPP_NONE; xport = xport_lookup(xcfg, ofport); if (!xport) { return EINVAL; } output.port = xport->ofp_port; output.max_len = 0; return ofproto_dpif_execute_actions(xport->xbridge->ofproto, &flow, NULL, &output.ofpact, sizeof output, packet); } struct xlate_cache * xlate_cache_new(void) { struct xlate_cache *xcache = xmalloc(sizeof *xcache); ofpbuf_init(&xcache->entries, 512); return xcache; } static struct xc_entry * xlate_cache_add_entry(struct xlate_cache *xcache, enum xc_type type) { struct xc_entry *entry; entry = ofpbuf_put_zeros(&xcache->entries, sizeof *entry); entry->type = type; return entry; } static void xlate_cache_netdev(struct xc_entry *entry, const struct dpif_flow_stats *stats) { if (entry->u.dev.tx) { netdev_vport_inc_tx(entry->u.dev.tx, stats); } if (entry->u.dev.rx) { netdev_vport_inc_rx(entry->u.dev.rx, stats); } if (entry->u.dev.bfd) { bfd_account_rx(entry->u.dev.bfd, stats); } } static void xlate_cache_normal(struct ofproto_dpif *ofproto, struct flow *flow, int vlan) { struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp); struct xbridge *xbridge; struct xbundle *xbundle; struct flow_wildcards wc; xbridge = xbridge_lookup(xcfg, ofproto); if (!xbridge) { return; } xbundle = lookup_input_bundle(xbridge, flow->in_port.ofp_port, false, NULL); if (!xbundle) { return; } update_learning_table(xbridge, flow, &wc, vlan, xbundle); } /* Push stats and perform side effects of flow translation. */ void xlate_push_stats(struct xlate_cache *xcache, const struct dpif_flow_stats *stats) { struct xc_entry *entry; struct ofpbuf entries = xcache->entries; struct eth_addr dmac; if (!stats->n_packets) { return; } XC_ENTRY_FOR_EACH (entry, entries, xcache) { switch (entry->type) { case XC_RULE: rule_dpif_credit_stats(entry->u.rule, stats); break; case XC_BOND: bond_account(entry->u.bond.bond, entry->u.bond.flow, entry->u.bond.vid, stats->n_bytes); break; case XC_NETDEV: xlate_cache_netdev(entry, stats); break; case XC_NETFLOW: netflow_flow_update(entry->u.nf.netflow, entry->u.nf.flow, entry->u.nf.iface, stats); break; case XC_MIRROR: mirror_update_stats(entry->u.mirror.mbridge, entry->u.mirror.mirrors, stats->n_packets, stats->n_bytes); break; case XC_LEARN: ofproto_dpif_flow_mod(entry->u.learn.ofproto, entry->u.learn.fm); break; case XC_NORMAL: xlate_cache_normal(entry->u.normal.ofproto, entry->u.normal.flow, entry->u.normal.vlan); break; case XC_FIN_TIMEOUT: xlate_fin_timeout__(entry->u.fin.rule, stats->tcp_flags, entry->u.fin.idle, entry->u.fin.hard); break; case XC_GROUP: group_dpif_credit_stats(entry->u.group.group, entry->u.group.bucket, stats); break; case XC_TNL_NEIGH: /* Lookup neighbor to avoid timeout. */ tnl_neigh_lookup(entry->u.tnl_neigh_cache.br_name, &entry->u.tnl_neigh_cache.d_ipv6, &dmac); break; default: OVS_NOT_REACHED(); } } } static void xlate_dev_unref(struct xc_entry *entry) { if (entry->u.dev.tx) { netdev_close(entry->u.dev.tx); } if (entry->u.dev.rx) { netdev_close(entry->u.dev.rx); } if (entry->u.dev.bfd) { bfd_unref(entry->u.dev.bfd); } } static void xlate_cache_clear_netflow(struct netflow *netflow, struct flow *flow) { netflow_flow_clear(netflow, flow); netflow_unref(netflow); free(flow); } void xlate_cache_clear(struct xlate_cache *xcache) { struct xc_entry *entry; struct ofpbuf entries; if (!xcache) { return; } XC_ENTRY_FOR_EACH (entry, entries, xcache) { switch (entry->type) { case XC_RULE: rule_dpif_unref(entry->u.rule); break; case XC_BOND: free(entry->u.bond.flow); bond_unref(entry->u.bond.bond); break; case XC_NETDEV: xlate_dev_unref(entry); break; case XC_NETFLOW: xlate_cache_clear_netflow(entry->u.nf.netflow, entry->u.nf.flow); break; case XC_MIRROR: mbridge_unref(entry->u.mirror.mbridge); break; case XC_LEARN: free(entry->u.learn.fm); ofpbuf_delete(entry->u.learn.ofpacts); break; case XC_NORMAL: free(entry->u.normal.flow); break; case XC_FIN_TIMEOUT: /* 'u.fin.rule' is always already held as a XC_RULE, which * has already released it's reference above. */ break; case XC_GROUP: group_dpif_unref(entry->u.group.group); break; case XC_TNL_NEIGH: break; default: OVS_NOT_REACHED(); } } ofpbuf_clear(&xcache->entries); } void xlate_cache_delete(struct xlate_cache *xcache) { xlate_cache_clear(xcache); ofpbuf_uninit(&xcache->entries); free(xcache); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071017473 xustar0030 mtime=1567801401.617682756 30 atime=1567801402.101686312 30 ctime=1567801424.609852158 openvswitch-2.5.9/ofproto/automake.mk0000644000175000017500000000511713534540071021165 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. lib_LTLIBRARIES += ofproto/libofproto.la ofproto_libofproto_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -Wl,--version-script=$(top_builddir)/ofproto/libofproto.sym \ $(AM_LDFLAGS) ofproto_libofproto_la_SOURCES = \ ofproto/bond.c \ ofproto/bond.h \ ofproto/collectors.c \ ofproto/collectors.h \ ofproto/connmgr.c \ ofproto/connmgr.h \ ofproto/fail-open.c \ ofproto/fail-open.h \ ofproto/in-band.c \ ofproto/in-band.h \ ofproto/names.c \ ofproto/netflow.c \ ofproto/netflow.h \ ofproto/ofproto.c \ ofproto/ofproto.h \ ofproto/ofproto-dpif.c \ ofproto/ofproto-dpif.h \ ofproto/ofproto-dpif-ipfix.c \ ofproto/ofproto-dpif-ipfix.h \ ofproto/ofproto-dpif-mirror.c \ ofproto/ofproto-dpif-mirror.h \ ofproto/ofproto-dpif-monitor.c \ ofproto/ofproto-dpif-monitor.h \ ofproto/ofproto-dpif-rid.c \ ofproto/ofproto-dpif-rid.h \ ofproto/ofproto-dpif-sflow.c \ ofproto/ofproto-dpif-sflow.h \ ofproto/ofproto-dpif-upcall.c \ ofproto/ofproto-dpif-upcall.h \ ofproto/ofproto-dpif-xlate.c \ ofproto/ofproto-dpif-xlate.h \ ofproto/ofproto-provider.h \ ofproto/pktbuf.c \ ofproto/pktbuf.h \ ofproto/pinsched.c \ ofproto/pinsched.h \ ofproto/tunnel.c \ ofproto/tunnel.h \ ofproto/bundles.c \ ofproto/bundles.h ofproto_libofproto_la_CPPFLAGS = $(AM_CPPFLAGS) ofproto_libofproto_la_CFLAGS = $(AM_CFLAGS) ofproto_libofproto_la_LIBADD = lib/libsflow.la if WIN32 ofproto_libofproto_la_LIBADD += ${PTHREAD_LIBS} endif pkgconfig_DATA += \ $(srcdir)/ofproto/libofproto.pc # Distribute this generated file in order not to require Python at # build time if ofproto/ipfix.xml is not modified. ofproto_libofproto_la_SOURCES += ofproto/ipfix-entities.def BUILT_SOURCES += ofproto/ipfix-entities.def CLEANFILES += ofproto/ipfix-entities.def MAN_FRAGMENTS += ofproto/ofproto-unixctl.man ofproto/ofproto-dpif-unixctl.man \ ofproto/ofproto-tnl-unixctl.man # IPFIX entity definition macros generation from IANA's XML definition. EXTRA_DIST += ofproto/ipfix.xml dist_noinst_SCRIPTS = ofproto/ipfix-gen-entities ofproto/ipfix-entities.def: ofproto/ipfix.xml ofproto/ipfix-gen-entities $(AM_V_GEN)$(run_python) $(srcdir)/ofproto/ipfix-gen-entities $< > $@.tmp && \ mv $@.tmp $@ # IPFIX enterprise entity definition macros. EXTRA_DIST += ofproto/ipfix-enterprise-entities.def openvswitch-2.5.9/ofproto/PaxHeaders.82075/ipfix-gen-entities0000644000000000000000000000013013534540071020765 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 29 ctime=1567801423.65384511 openvswitch-2.5.9/ofproto/ipfix-gen-entities0000755000175000017500000001022713534540071022462 0ustar00jpettitjpettit00000000000000#! /usr/bin/env python # # Copyright (C) 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. import getopt import re import sys import xml.sax import xml.sax.handler class IpfixEntityHandler(xml.sax.handler.ContentHandler): RECORD_FIELDS = ['name', 'dataType', 'elementId', 'status'] # Cf. RFC 5101, Section 6. DATA_TYPE_SIZE = { 'unsigned8': 1, 'unsigned16': 2, 'unsigned32': 4, 'unsigned64': 8, 'signed8': 1, 'signed16': 2, 'signed32': 4, 'signed64': 8, 'float32': 4, 'float64': 8, 'boolean': 1, # Not clear. 'macAddress': 6, 'octetArray': 0, # Not clear. 'string': 0, # Not clear. 'dateTimeSeconds': 4, 'dateTimeMilliseconds': 8, 'dateTimeMicroseconds': 8, 'dateTimeNanoseconds': 8, 'ipv4Address': 4, 'ipv6Address': 16, } def __init__(self): self.current_field_name = None self.current_field_value = [] self.current_record = dict() def startDocument(self): print """\ /* IPFIX entities. */ #ifndef IPFIX_ENTITY #define IPFIX_ENTITY(ENUM, ID, SIZE, NAME) #endif """ def endDocument(self): print """ #undef IPFIX_ENTITY""" def startElement(self, name, attrs): if name in self.RECORD_FIELDS: self.current_field_name = name else: self.current_field_name = None self.current_field_value = [] @staticmethod def camelcase_to_uppercase(s): return re.sub('(.)([A-Z]+)', r'\1_\2', s).upper() def endElement(self, name): if self.current_field_name is not None: self.current_record[self.current_field_name] = ''.join( self.current_field_value).strip() elif (name == 'record' and self.current_record.get('status') == 'current' and 'dataType' in self.current_record): self.current_record['enumName'] = self.camelcase_to_uppercase( self.current_record['name']) self.current_record['dataTypeSize'] = self.DATA_TYPE_SIZE.get( self.current_record['dataType'], 0) print 'IPFIX_ENTITY(%(enumName)s, %(elementId)s, ' \ '%(dataTypeSize)i, %(name)s)' % self.current_record self.current_record.clear() def characters(self, content): if self.current_field_name is not None: self.current_field_value.append(content) def print_ipfix_entity_macros(xml_file): xml.sax.parse(xml_file, IpfixEntityHandler()) def usage(name): print """\ %(name)s: IPFIX entity definition generator Prints C macros defining IPFIX entities from the standard IANA file at usage: %(name)s [OPTIONS] XML where XML is the standard IANA XML file defining IPFIX entities The following options are also available: -h, --help display this help message -V, --version display version information\ """ % {'name': name} sys.exit(0) if __name__ == '__main__': # try: try: options, args = getopt.gnu_getopt(sys.argv[1:], 'hV', ['help', 'version']) except getopt.GetoptError, geo: sys.stderr.write('%s: %s\n' % (sys.argv[0], geo.msg)) sys.exit(1) for key, value in options: if key in ['-h', '--help']: usage() elif key in ['-V', '--version']: print 'ipfix-gen-entities (Open vSwitch)' else: sys.exit(0) if len(args) != 1: sys.stderr.write('%s: exactly 1 non-option arguments required ' '(use --help for help)\n' % sys.argv[0]) sys.exit(1) print_ipfix_entity_macros(args[0]) # except Exception, e: # sys.stderr.write('%s: %s\n' % (sys.argv[0], e)) # sys.exit(1) # Local variables: # mode: python # End: openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-rid.c0000644000000000000000000000013113534540071020663 xustar0030 mtime=1567801401.633682874 29 atime=1567801402.10568634 30 ctime=1567801425.045855373 openvswitch-2.5.9/ofproto/ofproto-dpif-rid.c0000644000175000017500000002745513534540071022367 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofpbuf.h" #include "ofproto-dpif.h" #include "ofproto-dpif-rid.h" #include "ofproto-provider.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofproto_dpif_rid); static struct ovs_mutex mutex; static struct cmap id_map; static struct cmap metadata_map; static struct ovs_list expiring OVS_GUARDED_BY(mutex); static struct ovs_list expired OVS_GUARDED_BY(mutex); static uint32_t next_id OVS_GUARDED_BY(mutex); /* Possible next free id. */ #define RECIRC_POOL_STATIC_IDS 1024 static void recirc_id_node_free(struct recirc_id_node *); void recirc_init(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; if (ovsthread_once_start(&once)) { ovs_mutex_init(&mutex); ovs_mutex_lock(&mutex); next_id = 1; /* 0 is not a valid ID. */ cmap_init(&id_map); cmap_init(&metadata_map); list_init(&expiring); list_init(&expired); ovs_mutex_unlock(&mutex); ovsthread_once_done(&once); } } /* This should be called by the revalidator once at each round (every 500ms or * more). */ void recirc_run(void) { static long long int last = 0; long long int now = time_msec(); /* Do maintenance at most 4 times / sec. */ ovs_mutex_lock(&mutex); if (now - last > 250) { struct recirc_id_node *node; last = now; /* Nodes in 'expiring' and 'expired' lists have the refcount of zero, * which means that while they can still be found (by id), no new * references can be taken on them. We have removed the entry from the * 'metadata_map', at the time when refcount reached zero, causing any * new translations to allocate a new ID. This allows the expiring * entry to be safely deleted while any sudden new use of the similar * recirculation will safely start using a new recirculation ID. When * the refcount gets to zero, the node is also added to the 'expiring' * list. At any time after that the nodes in the 'expiring' list can * be moved to the 'expired' list, from which they are deleted at least * 250ms afterwards. */ /* Delete the expired. These have been lingering for at least 250 ms, * which should be enough for any ongoing recirculations to be * finished. */ LIST_FOR_EACH_POP (node, exp_node, &expired) { cmap_remove(&id_map, &node->id_node, node->id); ovsrcu_postpone(recirc_id_node_free, node); } if (!list_is_empty(&expiring)) { /* 'expired' is now empty, move nodes in 'expiring' to it. */ list_splice(&expired, list_front(&expiring), &expiring); } } ovs_mutex_unlock(&mutex); } /* We use the id as the hash value, which works due to cmap internal rehashing. * We also only insert nodes with unique IDs, so all possible hash collisions * remain internal to the cmap. */ static struct recirc_id_node * recirc_find__(uint32_t id) OVS_REQUIRES(mutex) { struct cmap_node *node = cmap_find_protected(&id_map, id); return node ? CONTAINER_OF(node, struct recirc_id_node, id_node) : NULL; } /* Lockless RCU protected lookup. If node is needed accross RCU quiescent * state, caller should copy the contents. */ const struct recirc_id_node * recirc_id_node_find(uint32_t id) { const struct cmap_node *node = cmap_find(&id_map, id); return node ? CONTAINER_OF(node, const struct recirc_id_node, id_node) : NULL; } static uint32_t recirc_metadata_hash(const struct recirc_state *state) { uint32_t hash; hash = hash_pointer(state->ofproto, 0); hash = hash_int(state->table_id, hash); if (flow_tnl_dst_is_set(state->metadata.tunnel)) { /* We may leave remainder bytes unhashed, but that is unlikely as * the tunnel is not in the datapath format. */ hash = hash_words64((const uint64_t *) state->metadata.tunnel, flow_tnl_size(state->metadata.tunnel) / sizeof(uint64_t), hash); } hash = hash_boolean(state->conntracked, hash); hash = hash_words64((const uint64_t *) &state->metadata.metadata, (sizeof state->metadata - sizeof state->metadata.tunnel) / sizeof(uint64_t), hash); if (state->stack && state->stack->size != 0) { hash = hash_words64((const uint64_t *) state->stack->data, state->stack->size / sizeof(uint64_t), hash); } hash = hash_int(state->mirrors, hash); hash = hash_int(state->action_set_len, hash); if (state->ofpacts_len) { hash = hash_words64(ALIGNED_CAST(const uint64_t *, state->ofpacts), state->ofpacts_len / sizeof(uint64_t), hash); } return hash; } static bool recirc_metadata_equal(const struct recirc_state *a, const struct recirc_state *b) { return (a->table_id == b->table_id && a->ofproto == b->ofproto && flow_tnl_equal(a->metadata.tunnel, b->metadata.tunnel) && !memcmp(&a->metadata.metadata, &b->metadata.metadata, sizeof a->metadata - sizeof a->metadata.tunnel) && (((!a->stack || !a->stack->size) && (!b->stack || !b->stack->size)) || (a->stack && b->stack && ofpbuf_equal(a->stack, b->stack))) && a->mirrors == b->mirrors && a->conntracked == b->conntracked && a->action_set_len == b->action_set_len && ofpacts_equal(a->ofpacts, a->ofpacts_len, b->ofpacts, b->ofpacts_len)); } /* Lockless RCU protected lookup. If node is needed accross RCU quiescent * state, caller should take a reference. */ static struct recirc_id_node * recirc_find_equal(const struct recirc_state *target, uint32_t hash) { struct recirc_id_node *node; CMAP_FOR_EACH_WITH_HASH (node, metadata_node, hash, &metadata_map) { if (recirc_metadata_equal(&node->state, target)) { return node; } } return NULL; } static struct recirc_id_node * recirc_ref_equal(const struct recirc_state *target, uint32_t hash) { struct recirc_id_node *node; do { node = recirc_find_equal(target, hash); /* Try again if the node was released before we get the reference. */ } while (node && !ovs_refcount_try_ref_rcu(&node->refcount)); return node; } static void recirc_state_clone(struct recirc_state *new, const struct recirc_state *old, struct flow_tnl *tunnel) { *new = *old; flow_tnl_copy__(tunnel, old->metadata.tunnel); new->metadata.tunnel = tunnel; if (new->stack) { new->stack = new->stack->size ? ofpbuf_clone(new->stack) : NULL; } if (new->ofpacts) { new->ofpacts = (new->ofpacts_len ? xmemdup(new->ofpacts, new->ofpacts_len) : NULL); } } static void recirc_state_free(struct recirc_state *state) { ofpbuf_delete(state->stack); free(state->ofpacts); } /* Allocate a unique recirculation id for the given set of flow metadata. * The ID space is 2^^32, so there should never be a situation in which all * the IDs are used up. We loop until we find a free one. * hash is recomputed if it is passed in as 0. */ static struct recirc_id_node * recirc_alloc_id__(const struct recirc_state *state, uint32_t hash) { ovs_assert(state->action_set_len <= state->ofpacts_len); struct recirc_id_node *node = xzalloc(sizeof *node); node->hash = hash; ovs_refcount_init(&node->refcount); recirc_state_clone(CONST_CAST(struct recirc_state *, &node->state), state, &node->state_metadata_tunnel); ovs_mutex_lock(&mutex); for (;;) { /* Claim the next ID. The ID space should be sparse enough for the allocation to succeed at the first try. We do skip the first RECIRC_POOL_STATIC_IDS IDs on the later rounds, though, as some of the initial allocations may be for long term uses (like bonds). */ node->id = next_id++; if (OVS_UNLIKELY(!node->id)) { next_id = RECIRC_POOL_STATIC_IDS + 1; node->id = next_id++; } /* Find if the id is free. */ if (OVS_LIKELY(!recirc_find__(node->id))) { break; } } cmap_insert(&id_map, &node->id_node, node->id); cmap_insert(&metadata_map, &node->metadata_node, node->hash); ovs_mutex_unlock(&mutex); return node; } /* Look up an existing ID for the given flow's metadata and optional actions. */ uint32_t recirc_find_id(const struct recirc_state *target) { uint32_t hash = recirc_metadata_hash(target); struct recirc_id_node *node = recirc_find_equal(target, hash); return node ? node->id : 0; } /* Allocate a unique recirculation id for the given set of flow metadata and optional actions. */ uint32_t recirc_alloc_id_ctx(const struct recirc_state *state) { uint32_t hash = recirc_metadata_hash(state); struct recirc_id_node *node = recirc_ref_equal(state, hash); if (!node) { node = recirc_alloc_id__(state, hash); } return node->id; } /* Allocate a unique recirculation id. */ uint32_t recirc_alloc_id(struct ofproto_dpif *ofproto) { struct flow_tnl tunnel; tunnel.ip_dst = htonl(0); tunnel.ipv6_dst = in6addr_any; struct recirc_state state = { .table_id = TBL_INTERNAL, .ofproto = ofproto, .metadata = { .tunnel = &tunnel, .in_port = OFPP_NONE }, }; return recirc_alloc_id__(&state, recirc_metadata_hash(&state))->id; } static void recirc_id_node_free(struct recirc_id_node *node) { recirc_state_free(CONST_CAST(struct recirc_state *, &node->state)); free(node); } void recirc_id_node_unref(const struct recirc_id_node *node_) OVS_EXCLUDED(mutex) { struct recirc_id_node *node = CONST_CAST(struct recirc_id_node *, node_); if (node && ovs_refcount_unref(&node->refcount) == 1) { ovs_mutex_lock(&mutex); /* Prevent re-use of this node by removing the node from 'metadata_map' */ cmap_remove(&metadata_map, &node->metadata_node, node->hash); /* We keep the node in the 'id_map' so that it can be found as long * as it lingers, and add it to the 'expiring' list. */ list_insert(&expiring, &node->exp_node); ovs_mutex_unlock(&mutex); } } void recirc_free_id(uint32_t id) { const struct recirc_id_node *node; node = recirc_id_node_find(id); if (node) { recirc_id_node_unref(node); } else { VLOG_ERR("Freeing nonexistent recirculation ID: %"PRIu32, id); } } /* Called when 'ofproto' is destructed. Checks for and clears any * recirc_id leak. * No other thread may have access to the 'ofproto' being destructed. * All related datapath flows must be deleted before calling this. */ void recirc_free_ofproto(struct ofproto_dpif *ofproto, const char *ofproto_name) { struct recirc_id_node *n; CMAP_FOR_EACH (n, metadata_node, &metadata_map) { if (n->state.ofproto == ofproto) { VLOG_ERR("recirc_id %"PRIu32 " left allocated when ofproto (%s)" " is destructed", n->id, ofproto_name); } } } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto.c0000644000000000000000000000013013534540071017166 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.029855254 openvswitch-2.5.9/ofproto/ofproto.c0000644000175000017500000075642713534540071020703 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009-2015 Nicira, Inc. * Copyright (c) 2010 Jean Tourrilhes - HP-Labs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto.h" #include #include #include #include #include #include "bitmap.h" #include "byte-order.h" #include "classifier.h" #include "connectivity.h" #include "connmgr.h" #include "coverage.h" #include "dynamic-string.h" #include "hash.h" #include "hmap.h" #include "meta-flow.h" #include "netdev.h" #include "nx-match.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-msgs.h" #include "ofp-print.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto-provider.h" #include "openflow/nicira-ext.h" #include "openflow/openflow.h" #include "ovs-rcu.h" #include "dp-packet.h" #include "packets.h" #include "pinsched.h" #include "pktbuf.h" #include "poll-loop.h" #include "random.h" #include "seq.h" #include "shash.h" #include "simap.h" #include "smap.h" #include "sset.h" #include "timeval.h" #include "tun-metadata.h" #include "unaligned.h" #include "unixctl.h" #include "openvswitch/vlog.h" #include "bundles.h" VLOG_DEFINE_THIS_MODULE(ofproto); COVERAGE_DEFINE(ofproto_flush); COVERAGE_DEFINE(ofproto_packet_out); COVERAGE_DEFINE(ofproto_queue_req); COVERAGE_DEFINE(ofproto_recv_openflow); COVERAGE_DEFINE(ofproto_reinit_ports); COVERAGE_DEFINE(ofproto_update_port); /* Default fields to use for prefix tries in each flow table, unless something * else is configured. */ const enum mf_field_id default_prefix_fields[2] = { MFF_IPV4_DST, MFF_IPV4_SRC }; /* oftable. */ static void oftable_init(struct oftable *); static void oftable_destroy(struct oftable *); static void oftable_set_name(struct oftable *, const char *name); static enum ofperr evict_rules_from_table(struct oftable *) OVS_REQUIRES(ofproto_mutex); static void oftable_configure_eviction(struct oftable *, unsigned int eviction, const struct mf_subfield *fields, size_t n_fields) OVS_REQUIRES(ofproto_mutex); /* This is the only combination of OpenFlow eviction flags that OVS supports: a * combination of OF1.4+ importance, the remaining lifetime of the flow, and * fairness based on user-specified fields. */ #define OFPROTO_EVICTION_FLAGS \ (OFPTMPEF14_OTHER | OFPTMPEF14_IMPORTANCE | OFPTMPEF14_LIFETIME) /* A set of rules within a single OpenFlow table (oftable) that have the same * values for the oftable's eviction_fields. A rule to be evicted, when one is * needed, is taken from the eviction group that contains the greatest number * of rules. * * An oftable owns any number of eviction groups, each of which contains any * number of rules. * * Membership in an eviction group is imprecise, based on the hash of the * oftable's eviction_fields (in the eviction_group's id_node.hash member). * That is, if two rules have different eviction_fields, but those * eviction_fields hash to the same value, then they will belong to the same * eviction_group anyway. * * (When eviction is not enabled on an oftable, we don't track any eviction * groups, to save time and space.) */ struct eviction_group { struct hmap_node id_node; /* In oftable's "eviction_groups_by_id". */ struct heap_node size_node; /* In oftable's "eviction_groups_by_size". */ struct heap rules; /* Contains "struct rule"s. */ }; static bool choose_rule_to_evict(struct oftable *table, struct rule **rulep) OVS_REQUIRES(ofproto_mutex); static uint64_t rule_eviction_priority(struct ofproto *ofproto, struct rule *) OVS_REQUIRES(ofproto_mutex); static void eviction_group_add_rule(struct rule *) OVS_REQUIRES(ofproto_mutex); static void eviction_group_remove_rule(struct rule *) OVS_REQUIRES(ofproto_mutex); /* Criteria that flow_mod and other operations use for selecting rules on * which to operate. */ struct rule_criteria { /* An OpenFlow table or 255 for all tables. */ uint8_t table_id; /* OpenFlow matching criteria. Interpreted different in "loose" way by * collect_rules_loose() and "strict" way by collect_rules_strict(), as * defined in the OpenFlow spec. */ struct cls_rule cr; cls_version_t version; /* Matching criteria for the OpenFlow cookie. Consider a bit B in a rule's * cookie and the corresponding bits C in 'cookie' and M in 'cookie_mask'. * The rule will not be selected if M is 1 and B != C. */ ovs_be64 cookie; ovs_be64 cookie_mask; /* Selection based on actions within a rule: * * If out_port != OFPP_ANY, selects only rules that output to out_port. * If out_group != OFPG_ALL, select only rules that output to out_group. */ ofp_port_t out_port; uint32_t out_group; /* If true, collects only rules that are modifiable. */ bool include_hidden; bool include_readonly; }; static void rule_criteria_init(struct rule_criteria *, uint8_t table_id, const struct match *match, int priority, cls_version_t version, ovs_be64 cookie, ovs_be64 cookie_mask, ofp_port_t out_port, uint32_t out_group); static void rule_criteria_require_rw(struct rule_criteria *, bool can_write_readonly); static void rule_criteria_destroy(struct rule_criteria *); static enum ofperr collect_rules_loose(struct ofproto *, const struct rule_criteria *, struct rule_collection *); /* A packet that needs to be passed to rule_execute(). * * (We can't do this immediately from ofopgroup_complete() because that holds * ofproto_mutex, which rule_execute() needs released.) */ struct rule_execute { struct ovs_list list_node; /* In struct ofproto's "rule_executes" list. */ struct rule *rule; /* Owns a reference to the rule. */ ofp_port_t in_port; struct dp_packet *packet; /* Owns the packet. */ }; static void run_rule_executes(struct ofproto *) OVS_EXCLUDED(ofproto_mutex); static void destroy_rule_executes(struct ofproto *); struct learned_cookie { union { /* In struct ofproto's 'learned_cookies' hmap. */ struct hmap_node hmap_node OVS_GUARDED_BY(ofproto_mutex); /* In 'dead_cookies' list when removed from hmap. */ struct ovs_list list_node; } u; /* Key. */ ovs_be64 cookie OVS_GUARDED_BY(ofproto_mutex); uint8_t table_id OVS_GUARDED_BY(ofproto_mutex); /* Number of references from "learn" actions. * * When this drops to 0, all of the flows in 'table_id' with the specified * 'cookie' are deleted. */ int n OVS_GUARDED_BY(ofproto_mutex); }; static const struct ofpact_learn *next_learn_with_delete( const struct rule_actions *, const struct ofpact_learn *start); static void learned_cookies_inc(struct ofproto *, const struct rule_actions *) OVS_REQUIRES(ofproto_mutex); static void learned_cookies_dec(struct ofproto *, const struct rule_actions *, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex); static void learned_cookies_flush(struct ofproto *, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex); /* ofport. */ static void ofport_destroy__(struct ofport *) OVS_EXCLUDED(ofproto_mutex); static void ofport_destroy(struct ofport *, bool del); static int update_port(struct ofproto *, const char *devname); static int init_ports(struct ofproto *); static void reinit_ports(struct ofproto *); static long long int ofport_get_usage(const struct ofproto *, ofp_port_t ofp_port); static void ofport_set_usage(struct ofproto *, ofp_port_t ofp_port, long long int last_used); static void ofport_remove_usage(struct ofproto *, ofp_port_t ofp_port); /* Ofport usage. * * Keeps track of the currently used and recently used ofport values and is * used to prevent immediate recycling of ofport values. */ struct ofport_usage { struct hmap_node hmap_node; /* In struct ofproto's "ofport_usage" hmap. */ ofp_port_t ofp_port; /* OpenFlow port number. */ long long int last_used; /* Last time the 'ofp_port' was used. LLONG_MAX represents in-use ofports. */ }; /* rule. */ static void ofproto_rule_send_removed(struct rule *) OVS_EXCLUDED(ofproto_mutex); static bool rule_is_readonly(const struct rule *); static void ofproto_rule_insert__(struct ofproto *, struct rule *) OVS_REQUIRES(ofproto_mutex); static void ofproto_rule_remove__(struct ofproto *, struct rule *) OVS_REQUIRES(ofproto_mutex); /* The source of a flow_mod request, in the code that processes flow_mods. * * A flow table modification request can be generated externally, via OpenFlow, * or internally through a function call. This structure indicates the source * of an OpenFlow-generated flow_mod. For an internal flow_mod, it isn't * meaningful and thus supplied as NULL. */ struct flow_mod_requester { struct ofconn *ofconn; /* Connection on which flow_mod arrived. */ const struct ofp_header *request; }; /* OpenFlow. */ static enum ofperr replace_rule_create(struct ofproto *, struct ofputil_flow_mod *, struct cls_rule *cr, uint8_t table_id, struct rule *old_rule, struct rule **new_rule) OVS_REQUIRES(ofproto_mutex); static void replace_rule_start(struct ofproto *, cls_version_t version, struct rule *old_rule, struct rule *new_rule, struct cls_conjunction *, size_t n_conjs) OVS_REQUIRES(ofproto_mutex); static void replace_rule_revert(struct ofproto *, struct rule *old_rule, struct rule *new_rule) OVS_REQUIRES(ofproto_mutex); static void replace_rule_finish(struct ofproto *, struct ofputil_flow_mod *, const struct flow_mod_requester *, struct rule *old_rule, struct rule *new_rule, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex); static void delete_flows__(struct rule_collection *, enum ofp_flow_removed_reason, const struct flow_mod_requester *) OVS_REQUIRES(ofproto_mutex); static void send_buffered_packet(const struct flow_mod_requester *, uint32_t buffer_id, struct rule *) OVS_REQUIRES(ofproto_mutex); static bool ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id) OVS_REQ_RDLOCK(ofproto->groups_rwlock); static bool ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) OVS_EXCLUDED(ofproto->groups_rwlock); static enum ofperr add_group(struct ofproto *, const struct ofputil_group_mod *); static void handle_openflow(struct ofconn *, const struct ofpbuf *); static enum ofperr ofproto_flow_mod_start(struct ofproto *, struct ofproto_flow_mod *) OVS_REQUIRES(ofproto_mutex); static void ofproto_flow_mod_finish(struct ofproto *, struct ofproto_flow_mod *, const struct flow_mod_requester *) OVS_REQUIRES(ofproto_mutex); static enum ofperr handle_flow_mod__(struct ofproto *, struct ofproto_flow_mod *, const struct flow_mod_requester *) OVS_EXCLUDED(ofproto_mutex); static void calc_duration(long long int start, long long int now, uint32_t *sec, uint32_t *nsec); /* ofproto. */ static uint64_t pick_datapath_id(const struct ofproto *); static uint64_t pick_fallback_dpid(void); static void ofproto_destroy__(struct ofproto *); static void update_mtu(struct ofproto *, struct ofport *); static void meter_delete(struct ofproto *, uint32_t first, uint32_t last); static void meter_insert_rule(struct rule *); /* unixctl. */ static void ofproto_unixctl_init(void); /* All registered ofproto classes, in probe order. */ static const struct ofproto_class **ofproto_classes; static size_t n_ofproto_classes; static size_t allocated_ofproto_classes; /* Global lock that protects all flow table operations. */ struct ovs_mutex ofproto_mutex = OVS_MUTEX_INITIALIZER; unsigned ofproto_flow_limit = OFPROTO_FLOW_LIMIT_DEFAULT; unsigned ofproto_max_idle = OFPROTO_MAX_IDLE_DEFAULT; size_t n_handlers, n_revalidators; size_t n_dpdk_rxqs; char *pmd_cpu_mask; /* Map from datapath name to struct ofproto, for use by unixctl commands. */ static struct hmap all_ofprotos = HMAP_INITIALIZER(&all_ofprotos); /* Initial mappings of port to OpenFlow number mappings. */ static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); /* The default value of true waits for flow restore. */ static bool flow_restore_wait = true; /* Must be called to initialize the ofproto library. * * The caller may pass in 'iface_hints', which contains an shash of * "iface_hint" elements indexed by the interface's name. The provider * may use these hints to describe the startup configuration in order to * reinitialize its state. The caller owns the provided data, so a * provider will make copies of anything required. An ofproto provider * will remove any existing state that is not described by the hint, and * may choose to remove it all. */ void ofproto_init(const struct shash *iface_hints) { struct shash_node *node; size_t i; ofproto_class_register(&ofproto_dpif_class); /* Make a local copy, since we don't own 'iface_hints' elements. */ SHASH_FOR_EACH(node, iface_hints) { const struct iface_hint *orig_hint = node->data; struct iface_hint *new_hint = xmalloc(sizeof *new_hint); const char *br_type = ofproto_normalize_type(orig_hint->br_type); new_hint->br_name = xstrdup(orig_hint->br_name); new_hint->br_type = xstrdup(br_type); new_hint->ofp_port = orig_hint->ofp_port; shash_add(&init_ofp_ports, node->name, new_hint); } for (i = 0; i < n_ofproto_classes; i++) { ofproto_classes[i]->init(&init_ofp_ports); } ofproto_unixctl_init(); } /* 'type' should be a normalized datapath type, as returned by * ofproto_normalize_type(). Returns the corresponding ofproto_class * structure, or a null pointer if there is none registered for 'type'. */ static const struct ofproto_class * ofproto_class_find__(const char *type) { size_t i; for (i = 0; i < n_ofproto_classes; i++) { const struct ofproto_class *class = ofproto_classes[i]; struct sset types; bool found; sset_init(&types); class->enumerate_types(&types); found = sset_contains(&types, type); sset_destroy(&types); if (found) { return class; } } VLOG_WARN("unknown datapath type %s", type); return NULL; } /* Registers a new ofproto class. After successful registration, new ofprotos * of that type can be created using ofproto_create(). */ int ofproto_class_register(const struct ofproto_class *new_class) { size_t i; for (i = 0; i < n_ofproto_classes; i++) { if (ofproto_classes[i] == new_class) { return EEXIST; } } if (n_ofproto_classes >= allocated_ofproto_classes) { ofproto_classes = x2nrealloc(ofproto_classes, &allocated_ofproto_classes, sizeof *ofproto_classes); } ofproto_classes[n_ofproto_classes++] = new_class; return 0; } /* Unregisters a datapath provider. 'type' must have been previously * registered and not currently be in use by any ofprotos. After * unregistration new datapaths of that type cannot be opened using * ofproto_create(). */ int ofproto_class_unregister(const struct ofproto_class *class) { size_t i; for (i = 0; i < n_ofproto_classes; i++) { if (ofproto_classes[i] == class) { for (i++; i < n_ofproto_classes; i++) { ofproto_classes[i - 1] = ofproto_classes[i]; } n_ofproto_classes--; return 0; } } VLOG_WARN("attempted to unregister an ofproto class that is not " "registered"); return EAFNOSUPPORT; } /* Clears 'types' and enumerates all registered ofproto types into it. The * caller must first initialize the sset. */ void ofproto_enumerate_types(struct sset *types) { size_t i; sset_clear(types); for (i = 0; i < n_ofproto_classes; i++) { ofproto_classes[i]->enumerate_types(types); } } /* Returns the fully spelled out name for the given ofproto 'type'. * * Normalized type string can be compared with strcmp(). Unnormalized type * string might be the same even if they have different spellings. */ const char * ofproto_normalize_type(const char *type) { return type && type[0] ? type : "system"; } /* Clears 'names' and enumerates the names of all known created ofprotos with * the given 'type'. The caller must first initialize the sset. Returns 0 if * successful, otherwise a positive errno value. * * Some kinds of datapaths might not be practically enumerable. This is not * considered an error. */ int ofproto_enumerate_names(const char *type, struct sset *names) { const struct ofproto_class *class = ofproto_class_find__(type); return class ? class->enumerate_names(type, names) : EAFNOSUPPORT; } static void ofproto_bump_tables_version(struct ofproto *ofproto) { ++ofproto->tables_version; ofproto->ofproto_class->set_tables_version(ofproto, ofproto->tables_version); } int ofproto_create(const char *datapath_name, const char *datapath_type, struct ofproto **ofprotop) { const struct ofproto_class *class; struct ofproto *ofproto; int error; int i; *ofprotop = NULL; datapath_type = ofproto_normalize_type(datapath_type); class = ofproto_class_find__(datapath_type); if (!class) { VLOG_WARN("could not create datapath %s of unknown type %s", datapath_name, datapath_type); return EAFNOSUPPORT; } ofproto = class->alloc(); if (!ofproto) { VLOG_ERR("failed to allocate datapath %s of type %s", datapath_name, datapath_type); return ENOMEM; } /* Initialize. */ ovs_mutex_lock(&ofproto_mutex); memset(ofproto, 0, sizeof *ofproto); ofproto->ofproto_class = class; ofproto->name = xstrdup(datapath_name); ofproto->type = xstrdup(datapath_type); hmap_insert(&all_ofprotos, &ofproto->hmap_node, hash_string(ofproto->name, 0)); ofproto->datapath_id = 0; ofproto->forward_bpdu = false; ofproto->fallback_dpid = pick_fallback_dpid(); ofproto->mfr_desc = NULL; ofproto->hw_desc = NULL; ofproto->sw_desc = NULL; ofproto->serial_desc = NULL; ofproto->dp_desc = NULL; ofproto->frag_handling = OFPC_FRAG_NORMAL; hmap_init(&ofproto->ports); hmap_init(&ofproto->ofport_usage); shash_init(&ofproto->port_by_name); simap_init(&ofproto->ofp_requests); ofproto->max_ports = ofp_to_u16(OFPP_MAX); ofproto->eviction_group_timer = LLONG_MIN; ofproto->tables = NULL; ofproto->n_tables = 0; ofproto->tables_version = CLS_MIN_VERSION; hindex_init(&ofproto->cookies); hmap_init(&ofproto->learned_cookies); list_init(&ofproto->expirable); ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name); guarded_list_init(&ofproto->rule_executes); ofproto->vlan_bitmap = NULL; ofproto->vlans_changed = false; ofproto->min_mtu = INT_MAX; ovs_rwlock_init(&ofproto->groups_rwlock); hmap_init(&ofproto->groups); ovs_mutex_unlock(&ofproto_mutex); ofproto->ogf.types = 0xf; ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS | OFPGFC_SELECT_WEIGHT; for (i = 0; i < 4; i++) { ofproto->ogf.max_groups[i] = OFPG_MAX; ofproto->ogf.ofpacts[i] = (UINT64_C(1) << N_OFPACTS) - 1; } tun_metadata_init(); error = ofproto->ofproto_class->construct(ofproto); if (error) { VLOG_ERR("failed to open datapath %s: %s", datapath_name, ovs_strerror(error)); connmgr_destroy(ofproto->connmgr); ofproto_destroy__(ofproto); return error; } /* Check that hidden tables, if any, are at the end. */ ovs_assert(ofproto->n_tables); for (i = 0; i + 1 < ofproto->n_tables; i++) { enum oftable_flags flags = ofproto->tables[i].flags; enum oftable_flags next_flags = ofproto->tables[i + 1].flags; ovs_assert(!(flags & OFTABLE_HIDDEN) || next_flags & OFTABLE_HIDDEN); } ofproto->datapath_id = pick_datapath_id(ofproto); init_ports(ofproto); /* Initialize meters table. */ if (ofproto->ofproto_class->meter_get_features) { ofproto->ofproto_class->meter_get_features(ofproto, &ofproto->meter_features); } else { memset(&ofproto->meter_features, 0, sizeof ofproto->meter_features); } ofproto->meters = xzalloc((ofproto->meter_features.max_meters + 1) * sizeof(struct meter *)); /* Set the initial tables version. */ ofproto_bump_tables_version(ofproto); *ofprotop = ofproto; return 0; } /* Must be called (only) by an ofproto implementation in its constructor * function. See the large comment on 'construct' in struct ofproto_class for * details. */ void ofproto_init_tables(struct ofproto *ofproto, int n_tables) { struct oftable *table; ovs_assert(!ofproto->n_tables); ovs_assert(n_tables >= 1 && n_tables <= 255); ofproto->n_tables = n_tables; ofproto->tables = xmalloc(n_tables * sizeof *ofproto->tables); OFPROTO_FOR_EACH_TABLE (table, ofproto) { oftable_init(table); } } /* To be optionally called (only) by an ofproto implementation in its * constructor function. See the large comment on 'construct' in struct * ofproto_class for details. * * Sets the maximum number of ports to 'max_ports'. The ofproto generic layer * will then ensure that actions passed into the ofproto implementation will * not refer to OpenFlow ports numbered 'max_ports' or higher. If this * function is not called, there will be no such restriction. * * Reserved ports numbered OFPP_MAX and higher are special and not subject to * the 'max_ports' restriction. */ void ofproto_init_max_ports(struct ofproto *ofproto, uint16_t max_ports) { ovs_assert(max_ports <= ofp_to_u16(OFPP_MAX)); ofproto->max_ports = max_ports; } uint64_t ofproto_get_datapath_id(const struct ofproto *ofproto) { return ofproto->datapath_id; } void ofproto_set_datapath_id(struct ofproto *p, uint64_t datapath_id) { uint64_t old_dpid = p->datapath_id; p->datapath_id = datapath_id ? datapath_id : pick_datapath_id(p); if (p->datapath_id != old_dpid) { /* Force all active connections to reconnect, since there is no way to * notify a controller that the datapath ID has changed. */ ofproto_reconnect_controllers(p); } } void ofproto_set_controllers(struct ofproto *p, const struct ofproto_controller *controllers, size_t n_controllers, uint32_t allowed_versions) { connmgr_set_controllers(p->connmgr, controllers, n_controllers, allowed_versions); } void ofproto_set_fail_mode(struct ofproto *p, enum ofproto_fail_mode fail_mode) { connmgr_set_fail_mode(p->connmgr, fail_mode); } /* Drops the connections between 'ofproto' and all of its controllers, forcing * them to reconnect. */ void ofproto_reconnect_controllers(struct ofproto *ofproto) { connmgr_reconnect(ofproto->connmgr); } /* Sets the 'n' TCP port addresses in 'extras' as ones to which 'ofproto''s * in-band control should guarantee access, in the same way that in-band * control guarantees access to OpenFlow controllers. */ void ofproto_set_extra_in_band_remotes(struct ofproto *ofproto, const struct sockaddr_in *extras, size_t n) { connmgr_set_extra_in_band_remotes(ofproto->connmgr, extras, n); } /* Sets the OpenFlow queue used by flows set up by in-band control on * 'ofproto' to 'queue_id'. If 'queue_id' is negative, then in-band control * flows will use the default queue. */ void ofproto_set_in_band_queue(struct ofproto *ofproto, int queue_id) { connmgr_set_in_band_queue(ofproto->connmgr, queue_id); } /* Sets the number of flows at which eviction from the kernel flow table * will occur. */ void ofproto_set_flow_limit(unsigned limit) { ofproto_flow_limit = limit; } /* Sets the maximum idle time for flows in the datapath before they are * expired. */ void ofproto_set_max_idle(unsigned max_idle) { ofproto_max_idle = max_idle; } /* If forward_bpdu is true, the NORMAL action will forward frames with * reserved (e.g. STP) destination Ethernet addresses. if forward_bpdu is false, * the NORMAL action will drop these frames. */ void ofproto_set_forward_bpdu(struct ofproto *ofproto, bool forward_bpdu) { bool old_val = ofproto->forward_bpdu; ofproto->forward_bpdu = forward_bpdu; if (old_val != ofproto->forward_bpdu) { if (ofproto->ofproto_class->forward_bpdu_changed) { ofproto->ofproto_class->forward_bpdu_changed(ofproto); } } } /* Sets the MAC aging timeout for the OFPP_NORMAL action on 'ofproto' to * 'idle_time', in seconds, and the maximum number of MAC table entries to * 'max_entries'. */ void ofproto_set_mac_table_config(struct ofproto *ofproto, unsigned idle_time, size_t max_entries) { if (ofproto->ofproto_class->set_mac_table_config) { ofproto->ofproto_class->set_mac_table_config(ofproto, idle_time, max_entries); } } /* Multicast snooping configuration. */ /* Configures multicast snooping on 'ofproto' using the settings * defined in 's'. If 's' is NULL, disables multicast snooping. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_set_mcast_snooping(struct ofproto *ofproto, const struct ofproto_mcast_snooping_settings *s) { return (ofproto->ofproto_class->set_mcast_snooping ? ofproto->ofproto_class->set_mcast_snooping(ofproto, s) : EOPNOTSUPP); } /* Configures multicast snooping flood settings on 'ofp_port' of 'ofproto'. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_set_mcast_snooping(struct ofproto *ofproto, void *aux, const struct ofproto_mcast_snooping_port_settings *s) { return (ofproto->ofproto_class->set_mcast_snooping_port ? ofproto->ofproto_class->set_mcast_snooping_port(ofproto, aux, s) : EOPNOTSUPP); } void ofproto_set_n_dpdk_rxqs(int n_rxqs) { n_dpdk_rxqs = MAX(n_rxqs, 0); } void ofproto_set_cpu_mask(const char *cmask) { free(pmd_cpu_mask); pmd_cpu_mask = cmask ? xstrdup(cmask) : NULL; } void ofproto_set_threads(int n_handlers_, int n_revalidators_) { int threads = MAX(count_cpu_cores(), 2); n_revalidators = MAX(n_revalidators_, 0); n_handlers = MAX(n_handlers_, 0); if (!n_revalidators) { n_revalidators = n_handlers ? MAX(threads - (int) n_handlers, 1) : threads / 4 + 1; } if (!n_handlers) { n_handlers = MAX(threads - (int) n_revalidators, 1); } } void ofproto_set_dp_desc(struct ofproto *p, const char *dp_desc) { free(p->dp_desc); p->dp_desc = dp_desc ? xstrdup(dp_desc) : NULL; } int ofproto_set_snoops(struct ofproto *ofproto, const struct sset *snoops) { return connmgr_set_snoops(ofproto->connmgr, snoops); } int ofproto_set_netflow(struct ofproto *ofproto, const struct netflow_options *nf_options) { if (nf_options && sset_is_empty(&nf_options->collectors)) { nf_options = NULL; } if (ofproto->ofproto_class->set_netflow) { return ofproto->ofproto_class->set_netflow(ofproto, nf_options); } else { return nf_options ? EOPNOTSUPP : 0; } } int ofproto_set_sflow(struct ofproto *ofproto, const struct ofproto_sflow_options *oso) { if (oso && sset_is_empty(&oso->targets)) { oso = NULL; } if (ofproto->ofproto_class->set_sflow) { return ofproto->ofproto_class->set_sflow(ofproto, oso); } else { return oso ? EOPNOTSUPP : 0; } } int ofproto_set_ipfix(struct ofproto *ofproto, const struct ofproto_ipfix_bridge_exporter_options *bo, const struct ofproto_ipfix_flow_exporter_options *fo, size_t n_fo) { if (ofproto->ofproto_class->set_ipfix) { return ofproto->ofproto_class->set_ipfix(ofproto, bo, fo, n_fo); } else { return (bo || fo) ? EOPNOTSUPP : 0; } } void ofproto_set_flow_restore_wait(bool flow_restore_wait_db) { flow_restore_wait = flow_restore_wait_db; } bool ofproto_get_flow_restore_wait(void) { return flow_restore_wait; } /* Spanning Tree Protocol (STP) configuration. */ /* Configures STP on 'ofproto' using the settings defined in 's'. If * 's' is NULL, disables STP. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_set_stp(struct ofproto *ofproto, const struct ofproto_stp_settings *s) { return (ofproto->ofproto_class->set_stp ? ofproto->ofproto_class->set_stp(ofproto, s) : EOPNOTSUPP); } /* Retrieves STP status of 'ofproto' and stores it in 's'. If the * 'enabled' member of 's' is false, then the other members are not * meaningful. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_get_stp_status(struct ofproto *ofproto, struct ofproto_stp_status *s) { return (ofproto->ofproto_class->get_stp_status ? ofproto->ofproto_class->get_stp_status(ofproto, s) : EOPNOTSUPP); } /* Configures STP on 'ofp_port' of 'ofproto' using the settings defined * in 's'. The caller is responsible for assigning STP port numbers * (using the 'port_num' member in the range of 1 through 255, inclusive) * and ensuring there are no duplicates. If the 's' is NULL, then STP * is disabled on the port. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_set_stp(struct ofproto *ofproto, ofp_port_t ofp_port, const struct ofproto_port_stp_settings *s) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot configure STP on nonexistent port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } return (ofproto->ofproto_class->set_stp_port ? ofproto->ofproto_class->set_stp_port(ofport, s) : EOPNOTSUPP); } /* Retrieves STP port status of 'ofp_port' on 'ofproto' and stores it in * 's'. If the 'enabled' member in 's' is false, then the other members * are not meaningful. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_get_stp_status(struct ofproto *ofproto, ofp_port_t ofp_port, struct ofproto_port_stp_status *s) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN_RL(&rl, "%s: cannot get STP status on nonexistent " "port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } return (ofproto->ofproto_class->get_stp_port_status ? ofproto->ofproto_class->get_stp_port_status(ofport, s) : EOPNOTSUPP); } /* Retrieves STP port statistics of 'ofp_port' on 'ofproto' and stores it in * 's'. If the 'enabled' member in 's' is false, then the other members * are not meaningful. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_get_stp_stats(struct ofproto *ofproto, ofp_port_t ofp_port, struct ofproto_port_stp_stats *s) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN_RL(&rl, "%s: cannot get STP stats on nonexistent " "port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } return (ofproto->ofproto_class->get_stp_port_stats ? ofproto->ofproto_class->get_stp_port_stats(ofport, s) : EOPNOTSUPP); } /* Rapid Spanning Tree Protocol (RSTP) configuration. */ /* Configures RSTP on 'ofproto' using the settings defined in 's'. If * 's' is NULL, disables RSTP. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_set_rstp(struct ofproto *ofproto, const struct ofproto_rstp_settings *s) { if (!ofproto->ofproto_class->set_rstp) { return EOPNOTSUPP; } ofproto->ofproto_class->set_rstp(ofproto, s); return 0; } /* Retrieves RSTP status of 'ofproto' and stores it in 's'. If the * 'enabled' member of 's' is false, then the other members are not * meaningful. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_get_rstp_status(struct ofproto *ofproto, struct ofproto_rstp_status *s) { if (!ofproto->ofproto_class->get_rstp_status) { return EOPNOTSUPP; } ofproto->ofproto_class->get_rstp_status(ofproto, s); return 0; } /* Configures RSTP on 'ofp_port' of 'ofproto' using the settings defined * in 's'. The caller is responsible for assigning RSTP port numbers * (using the 'port_num' member in the range of 1 through 255, inclusive) * and ensuring there are no duplicates. If the 's' is NULL, then RSTP * is disabled on the port. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_set_rstp(struct ofproto *ofproto, ofp_port_t ofp_port, const struct ofproto_port_rstp_settings *s) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot configure RSTP on nonexistent port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } if (!ofproto->ofproto_class->set_rstp_port) { return EOPNOTSUPP; } ofproto->ofproto_class->set_rstp_port(ofport, s); return 0; } /* Retrieves RSTP port status of 'ofp_port' on 'ofproto' and stores it in * 's'. If the 'enabled' member in 's' is false, then the other members * are not meaningful. * * Returns 0 if successful, otherwise a positive errno value.*/ int ofproto_port_get_rstp_status(struct ofproto *ofproto, ofp_port_t ofp_port, struct ofproto_port_rstp_status *s) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN_RL(&rl, "%s: cannot get RSTP status on nonexistent " "port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } if (!ofproto->ofproto_class->get_rstp_port_status) { return EOPNOTSUPP; } ofproto->ofproto_class->get_rstp_port_status(ofport, s); return 0; } /* Queue DSCP configuration. */ /* Registers meta-data associated with the 'n_qdscp' Qualities of Service * 'queues' attached to 'ofport'. This data is not intended to be sufficient * to implement QoS. Instead, it is used to implement features which require * knowledge of what queues exist on a port, and some basic information about * them. * * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_port_set_queues(struct ofproto *ofproto, ofp_port_t ofp_port, const struct ofproto_port_queue *queues, size_t n_queues) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot set queues on nonexistent port %"PRIu16, ofproto->name, ofp_port); return ENODEV; } return (ofproto->ofproto_class->set_queues ? ofproto->ofproto_class->set_queues(ofport, queues, n_queues) : EOPNOTSUPP); } /* LLDP configuration. */ void ofproto_port_set_lldp(struct ofproto *ofproto, ofp_port_t ofp_port, const struct smap *cfg) { struct ofport *ofport; int error; ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot configure LLDP on nonexistent port %"PRIu16, ofproto->name, ofp_port); return; } error = (ofproto->ofproto_class->set_lldp ? ofproto->ofproto_class->set_lldp(ofport, cfg) : EOPNOTSUPP); if (error) { VLOG_WARN("%s: lldp configuration on port %"PRIu16" (%s) failed (%s)", ofproto->name, ofp_port, netdev_get_name(ofport->netdev), ovs_strerror(error)); } } int ofproto_set_aa(struct ofproto *ofproto, void *aux OVS_UNUSED, const struct aa_settings *s) { if (!ofproto->ofproto_class->set_aa) { return EOPNOTSUPP; } ofproto->ofproto_class->set_aa(ofproto, s); return 0; } int ofproto_aa_mapping_register(struct ofproto *ofproto, void *aux, const struct aa_mapping_settings *s) { if (!ofproto->ofproto_class->aa_mapping_set) { return EOPNOTSUPP; } ofproto->ofproto_class->aa_mapping_set(ofproto, aux, s); return 0; } int ofproto_aa_mapping_unregister(struct ofproto *ofproto, void *aux) { if (!ofproto->ofproto_class->aa_mapping_unset) { return EOPNOTSUPP; } ofproto->ofproto_class->aa_mapping_unset(ofproto, aux); return 0; } int ofproto_aa_vlan_get_queued(struct ofproto *ofproto, struct ovs_list *list) { if (!ofproto->ofproto_class->aa_vlan_get_queued) { return EOPNOTSUPP; } ofproto->ofproto_class->aa_vlan_get_queued(ofproto, list); return 0; } unsigned int ofproto_aa_vlan_get_queue_size(struct ofproto *ofproto) { if (!ofproto->ofproto_class->aa_vlan_get_queue_size) { return EOPNOTSUPP; } return ofproto->ofproto_class->aa_vlan_get_queue_size(ofproto); } /* Connectivity Fault Management configuration. */ /* Clears the CFM configuration from 'ofp_port' on 'ofproto'. */ void ofproto_port_clear_cfm(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); if (ofport && ofproto->ofproto_class->set_cfm) { ofproto->ofproto_class->set_cfm(ofport, NULL); } } /* Configures connectivity fault management on 'ofp_port' in 'ofproto'. Takes * basic configuration from the configuration members in 'cfm', and the remote * maintenance point ID from remote_mpid. Ignores the statistics members of * 'cfm'. * * This function has no effect if 'ofproto' does not have a port 'ofp_port'. */ void ofproto_port_set_cfm(struct ofproto *ofproto, ofp_port_t ofp_port, const struct cfm_settings *s) { struct ofport *ofport; int error; ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot configure CFM on nonexistent port %"PRIu16, ofproto->name, ofp_port); return; } /* XXX: For configuration simplicity, we only support one remote_mpid * outside of the CFM module. It's not clear if this is the correct long * term solution or not. */ error = (ofproto->ofproto_class->set_cfm ? ofproto->ofproto_class->set_cfm(ofport, s) : EOPNOTSUPP); if (error) { VLOG_WARN("%s: CFM configuration on port %"PRIu16" (%s) failed (%s)", ofproto->name, ofp_port, netdev_get_name(ofport->netdev), ovs_strerror(error)); } } /* Configures BFD on 'ofp_port' in 'ofproto'. This function has no effect if * 'ofproto' does not have a port 'ofp_port'. */ void ofproto_port_set_bfd(struct ofproto *ofproto, ofp_port_t ofp_port, const struct smap *cfg) { struct ofport *ofport; int error; ofport = ofproto_get_port(ofproto, ofp_port); if (!ofport) { VLOG_WARN("%s: cannot configure bfd on nonexistent port %"PRIu16, ofproto->name, ofp_port); return; } error = (ofproto->ofproto_class->set_bfd ? ofproto->ofproto_class->set_bfd(ofport, cfg) : EOPNOTSUPP); if (error) { VLOG_WARN("%s: bfd configuration on port %"PRIu16" (%s) failed (%s)", ofproto->name, ofp_port, netdev_get_name(ofport->netdev), ovs_strerror(error)); } } /* Checks the status change of BFD on 'ofport'. * * Returns true if 'ofproto_class' does not support 'bfd_status_changed'. */ bool ofproto_port_bfd_status_changed(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); return (ofport && ofproto->ofproto_class->bfd_status_changed ? ofproto->ofproto_class->bfd_status_changed(ofport) : true); } /* Populates 'status' with the status of BFD on 'ofport'. Returns 0 on * success. Returns a positive errno otherwise. Has no effect if 'ofp_port' * is not an OpenFlow port in 'ofproto'. * * The caller must provide and own '*status'. */ int ofproto_port_get_bfd_status(struct ofproto *ofproto, ofp_port_t ofp_port, struct smap *status) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); return (ofport && ofproto->ofproto_class->get_bfd_status ? ofproto->ofproto_class->get_bfd_status(ofport, status) : EOPNOTSUPP); } /* Checks the status of LACP negotiation for 'ofp_port' within ofproto. * Returns 1 if LACP partner information for 'ofp_port' is up-to-date, * 0 if LACP partner information is not current (generally indicating a * connectivity problem), or -1 if LACP is not enabled on 'ofp_port'. */ int ofproto_port_is_lacp_current(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); return (ofport && ofproto->ofproto_class->port_is_lacp_current ? ofproto->ofproto_class->port_is_lacp_current(ofport) : -1); } int ofproto_port_get_lacp_stats(const struct ofport *port, struct lacp_slave_stats *stats) { struct ofproto *ofproto = port->ofproto; int error; if (ofproto->ofproto_class->port_get_lacp_stats) { error = ofproto->ofproto_class->port_get_lacp_stats(port, stats); } else { error = EOPNOTSUPP; } return error; } /* Bundles. */ /* Registers a "bundle" associated with client data pointer 'aux' in 'ofproto'. * A bundle is the same concept as a Port in OVSDB, that is, it consists of one * or more "slave" devices (Interfaces, in OVSDB) along with a VLAN * configuration plus, if there is more than one slave, a bonding * configuration. * * If 'aux' is already registered then this function updates its configuration * to 's'. Otherwise, this function registers a new bundle. * * Bundles only affect the NXAST_AUTOPATH action and output to the OFPP_NORMAL * port. */ int ofproto_bundle_register(struct ofproto *ofproto, void *aux, const struct ofproto_bundle_settings *s) { return (ofproto->ofproto_class->bundle_set ? ofproto->ofproto_class->bundle_set(ofproto, aux, s) : EOPNOTSUPP); } /* Unregisters the bundle registered on 'ofproto' with auxiliary data 'aux'. * If no such bundle has been registered, this has no effect. */ int ofproto_bundle_unregister(struct ofproto *ofproto, void *aux) { return ofproto_bundle_register(ofproto, aux, NULL); } /* Registers a mirror associated with client data pointer 'aux' in 'ofproto'. * If 'aux' is already registered then this function updates its configuration * to 's'. Otherwise, this function registers a new mirror. */ int ofproto_mirror_register(struct ofproto *ofproto, void *aux, const struct ofproto_mirror_settings *s) { return (ofproto->ofproto_class->mirror_set ? ofproto->ofproto_class->mirror_set(ofproto, aux, s) : EOPNOTSUPP); } /* Unregisters the mirror registered on 'ofproto' with auxiliary data 'aux'. * If no mirror has been registered, this has no effect. */ int ofproto_mirror_unregister(struct ofproto *ofproto, void *aux) { return ofproto_mirror_register(ofproto, aux, NULL); } /* Retrieves statistics from mirror associated with client data pointer * 'aux' in 'ofproto'. Stores packet and byte counts in 'packets' and * 'bytes', respectively. If a particular counters is not supported, * the appropriate argument is set to UINT64_MAX. */ int ofproto_mirror_get_stats(struct ofproto *ofproto, void *aux, uint64_t *packets, uint64_t *bytes) { if (!ofproto->ofproto_class->mirror_get_stats) { *packets = *bytes = UINT64_MAX; return EOPNOTSUPP; } return ofproto->ofproto_class->mirror_get_stats(ofproto, aux, packets, bytes); } /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs on * which all packets are flooded, instead of using MAC learning. If * 'flood_vlans' is NULL, then MAC learning applies to all VLANs. * * Flood VLANs affect only the treatment of packets output to the OFPP_NORMAL * port. */ int ofproto_set_flood_vlans(struct ofproto *ofproto, unsigned long *flood_vlans) { return (ofproto->ofproto_class->set_flood_vlans ? ofproto->ofproto_class->set_flood_vlans(ofproto, flood_vlans) : EOPNOTSUPP); } /* Returns true if 'aux' is a registered bundle that is currently in use as the * output for a mirror. */ bool ofproto_is_mirror_output_bundle(const struct ofproto *ofproto, void *aux) { return (ofproto->ofproto_class->is_mirror_output_bundle ? ofproto->ofproto_class->is_mirror_output_bundle(ofproto, aux) : false); } /* Configuration of OpenFlow tables. */ /* Returns the number of OpenFlow tables in 'ofproto'. */ int ofproto_get_n_tables(const struct ofproto *ofproto) { return ofproto->n_tables; } /* Returns the number of Controller visible OpenFlow tables * in 'ofproto'. This number will exclude Hidden tables. * This funtion's return value should be less or equal to that of * ofproto_get_n_tables() . */ uint8_t ofproto_get_n_visible_tables(const struct ofproto *ofproto) { uint8_t n = ofproto->n_tables; /* Count only non-hidden tables in the number of tables. (Hidden tables, * if present, are always at the end.) */ while(n && (ofproto->tables[n - 1].flags & OFTABLE_HIDDEN)) { n--; } return n; } /* Configures the OpenFlow table in 'ofproto' with id 'table_id' with the * settings from 's'. 'table_id' must be in the range 0 through the number of * OpenFlow tables in 'ofproto' minus 1, inclusive. * * For read-only tables, only the name may be configured. */ void ofproto_configure_table(struct ofproto *ofproto, int table_id, const struct ofproto_table_settings *s) { struct oftable *table; ovs_assert(table_id >= 0 && table_id < ofproto->n_tables); table = &ofproto->tables[table_id]; oftable_set_name(table, s->name); if (table->flags & OFTABLE_READONLY) { return; } if (classifier_set_prefix_fields(&table->cls, s->prefix_fields, s->n_prefix_fields)) { /* XXX: Trigger revalidation. */ } ovs_mutex_lock(&ofproto_mutex); unsigned int new_eviction = (s->enable_eviction ? table->eviction | EVICTION_CLIENT : table->eviction & ~EVICTION_CLIENT); oftable_configure_eviction(table, new_eviction, s->groups, s->n_groups); table->max_flows = s->max_flows; evict_rules_from_table(table); ovs_mutex_unlock(&ofproto_mutex); } bool ofproto_has_snoops(const struct ofproto *ofproto) { return connmgr_has_snoops(ofproto->connmgr); } void ofproto_get_snoops(const struct ofproto *ofproto, struct sset *snoops) { connmgr_get_snoops(ofproto->connmgr, snoops); } /* Deletes 'rule' from 'ofproto'. * * Within an ofproto implementation, this function allows an ofproto * implementation to destroy any rules that remain when its ->destruct() * function is called. This function is not suitable for use elsewhere in an * ofproto implementation. * * This function implements steps 4.4 and 4.5 in the section titled "Rule Life * Cycle" in ofproto-provider.h. */ void ofproto_rule_delete(struct ofproto *ofproto, struct rule *rule) OVS_EXCLUDED(ofproto_mutex) { /* This skips the ofmonitor and flow-removed notifications because the * switch is being deleted and any OpenFlow channels have been or soon will * be killed. */ ovs_mutex_lock(&ofproto_mutex); if (!rule->removed) { /* Make sure there is no postponed removal of the rule. */ ovs_assert(cls_rule_visible_in_version(&rule->cr, CLS_MAX_VERSION)); if (!classifier_remove(&rule->ofproto->tables[rule->table_id].cls, &rule->cr)) { OVS_NOT_REACHED(); } ofproto_rule_remove__(rule->ofproto, rule); ofproto->ofproto_class->rule_delete(rule); ofproto_rule_unref(rule); } ovs_mutex_unlock(&ofproto_mutex); } static void ofproto_flush__(struct ofproto *ofproto) OVS_EXCLUDED(ofproto_mutex) { struct oftable *table; /* This will flush all datapath flows. */ if (ofproto->ofproto_class->flush) { ofproto->ofproto_class->flush(ofproto); } /* XXX: There is a small race window here, where new datapath flows can be * created by upcall handlers based on the existing flow table. We can not * call ofproto class flush while holding 'ofproto_mutex' to prevent this, * as then we could deadlock on syncing with the handler threads waiting on * the same mutex. */ ovs_mutex_lock(&ofproto_mutex); OFPROTO_FOR_EACH_TABLE (table, ofproto) { struct rule_collection rules; struct rule *rule; if (table->flags & OFTABLE_HIDDEN) { continue; } rule_collection_init(&rules); CLS_FOR_EACH (rule, cr, &table->cls) { rule_collection_add(&rules, rule); } delete_flows__(&rules, OFPRR_DELETE, NULL); } /* XXX: Concurrent handler threads may insert new learned flows based on * learn actions of the now deleted flows right after we release * 'ofproto_mutex'. */ ovs_mutex_unlock(&ofproto_mutex); } static void delete_group(struct ofproto *ofproto, uint32_t group_id); static void ofproto_destroy__(struct ofproto *ofproto) OVS_EXCLUDED(ofproto_mutex) { struct oftable *table; destroy_rule_executes(ofproto); guarded_list_destroy(&ofproto->rule_executes); ovs_rwlock_destroy(&ofproto->groups_rwlock); hmap_destroy(&ofproto->groups); hmap_remove(&all_ofprotos, &ofproto->hmap_node); free(ofproto->name); free(ofproto->type); free(ofproto->mfr_desc); free(ofproto->hw_desc); free(ofproto->sw_desc); free(ofproto->serial_desc); free(ofproto->dp_desc); hmap_destroy(&ofproto->ports); hmap_destroy(&ofproto->ofport_usage); shash_destroy(&ofproto->port_by_name); simap_destroy(&ofproto->ofp_requests); OFPROTO_FOR_EACH_TABLE (table, ofproto) { oftable_destroy(table); } free(ofproto->tables); ovs_assert(hindex_is_empty(&ofproto->cookies)); hindex_destroy(&ofproto->cookies); ovs_assert(hmap_is_empty(&ofproto->learned_cookies)); hmap_destroy(&ofproto->learned_cookies); free(ofproto->vlan_bitmap); ofproto->ofproto_class->dealloc(ofproto); } /* Destroying rules is doubly deferred, must have 'ofproto' around for them. * - 1st we defer the removal of the rules from the classifier * - 2nd we defer the actual destruction of the rules. */ static void ofproto_destroy_defer__(struct ofproto *ofproto) OVS_EXCLUDED(ofproto_mutex) { ovsrcu_postpone(ofproto_destroy__, ofproto); } void ofproto_destroy(struct ofproto *p, bool del) OVS_EXCLUDED(ofproto_mutex) { struct ofport *ofport, *next_ofport; struct ofport_usage *usage, *next_usage; if (!p) { return; } if (p->meters) { meter_delete(p, 1, p->meter_features.max_meters); p->meter_features.max_meters = 0; free(p->meters); p->meters = NULL; } ofproto_flush__(p); HMAP_FOR_EACH_SAFE (ofport, next_ofport, hmap_node, &p->ports) { ofport_destroy(ofport, del); } HMAP_FOR_EACH_SAFE (usage, next_usage, hmap_node, &p->ofport_usage) { hmap_remove(&p->ofport_usage, &usage->hmap_node); free(usage); } p->ofproto_class->destruct(p); /* We should not postpone this because it involves deleting a listening * socket which we may want to reopen soon. 'connmgr' should not be used * by other threads */ connmgr_destroy(p->connmgr); /* Destroying rules is deferred, must have 'ofproto' around for them. */ ovsrcu_postpone(ofproto_destroy_defer__, p); } /* Destroys the datapath with the respective 'name' and 'type'. With the Linux * kernel datapath, for example, this destroys the datapath in the kernel, and * with the netdev-based datapath, it tears down the data structures that * represent the datapath. * * The datapath should not be currently open as an ofproto. */ int ofproto_delete(const char *name, const char *type) { const struct ofproto_class *class = ofproto_class_find__(type); return (!class ? EAFNOSUPPORT : !class->del ? EACCES : class->del(type, name)); } static void process_port_change(struct ofproto *ofproto, int error, char *devname) { if (error == ENOBUFS) { reinit_ports(ofproto); } else if (!error) { update_port(ofproto, devname); free(devname); } } int ofproto_type_run(const char *datapath_type) { const struct ofproto_class *class; int error; datapath_type = ofproto_normalize_type(datapath_type); class = ofproto_class_find__(datapath_type); error = class->type_run ? class->type_run(datapath_type) : 0; if (error && error != EAGAIN) { VLOG_ERR_RL(&rl, "%s: type_run failed (%s)", datapath_type, ovs_strerror(error)); } return error; } void ofproto_type_wait(const char *datapath_type) { const struct ofproto_class *class; datapath_type = ofproto_normalize_type(datapath_type); class = ofproto_class_find__(datapath_type); if (class->type_wait) { class->type_wait(datapath_type); } } int ofproto_run(struct ofproto *p) { int error; uint64_t new_seq; error = p->ofproto_class->run(p); if (error && error != EAGAIN) { VLOG_ERR_RL(&rl, "%s: run failed (%s)", p->name, ovs_strerror(error)); } run_rule_executes(p); /* Restore the eviction group heap invariant occasionally. */ if (p->eviction_group_timer < time_msec()) { size_t i; p->eviction_group_timer = time_msec() + 1000; for (i = 0; i < p->n_tables; i++) { struct oftable *table = &p->tables[i]; struct eviction_group *evg; struct rule *rule; if (!table->eviction) { continue; } if (table->n_flows > 100000) { static struct vlog_rate_limit count_rl = VLOG_RATE_LIMIT_INIT(1, 1); VLOG_WARN_RL(&count_rl, "Table %"PRIuSIZE" has an excessive" " number of rules: %d", i, table->n_flows); } ovs_mutex_lock(&ofproto_mutex); CLS_FOR_EACH (rule, cr, &table->cls) { if (rule->idle_timeout || rule->hard_timeout) { if (!rule->eviction_group) { eviction_group_add_rule(rule); } else { heap_raw_change(&rule->evg_node, rule_eviction_priority(p, rule)); } } } HEAP_FOR_EACH (evg, size_node, &table->eviction_groups_by_size) { heap_rebuild(&evg->rules); } ovs_mutex_unlock(&ofproto_mutex); } } if (p->ofproto_class->port_poll) { char *devname; while ((error = p->ofproto_class->port_poll(p, &devname)) != EAGAIN) { process_port_change(p, error, devname); } } new_seq = seq_read(connectivity_seq_get()); if (new_seq != p->change_seq) { struct sset devnames; const char *devname; struct ofport *ofport; /* Update OpenFlow port status for any port whose netdev has changed. * * Refreshing a given 'ofport' can cause an arbitrary ofport to be * destroyed, so it's not safe to update ports directly from the * HMAP_FOR_EACH loop, or even to use HMAP_FOR_EACH_SAFE. Instead, we * need this two-phase approach. */ sset_init(&devnames); HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { uint64_t port_change_seq; port_change_seq = netdev_get_change_seq(ofport->netdev); if (ofport->change_seq != port_change_seq) { ofport->change_seq = port_change_seq; sset_add(&devnames, netdev_get_name(ofport->netdev)); } } SSET_FOR_EACH (devname, &devnames) { update_port(p, devname); } sset_destroy(&devnames); p->change_seq = new_seq; } connmgr_run(p->connmgr, handle_openflow); return error; } void ofproto_wait(struct ofproto *p) { p->ofproto_class->wait(p); if (p->ofproto_class->port_poll_wait) { p->ofproto_class->port_poll_wait(p); } seq_wait(connectivity_seq_get(), p->change_seq); connmgr_wait(p->connmgr); } bool ofproto_is_alive(const struct ofproto *p) { return connmgr_has_controllers(p->connmgr); } /* Adds some memory usage statistics for 'ofproto' into 'usage', for use with * memory_report(). */ void ofproto_get_memory_usage(const struct ofproto *ofproto, struct simap *usage) { const struct oftable *table; unsigned int n_rules; simap_increase(usage, "ports", hmap_count(&ofproto->ports)); n_rules = 0; OFPROTO_FOR_EACH_TABLE (table, ofproto) { n_rules += table->n_flows; } simap_increase(usage, "rules", n_rules); if (ofproto->ofproto_class->get_memory_usage) { ofproto->ofproto_class->get_memory_usage(ofproto, usage); } connmgr_get_memory_usage(ofproto->connmgr, usage); } void ofproto_type_get_memory_usage(const char *datapath_type, struct simap *usage) { const struct ofproto_class *class; datapath_type = ofproto_normalize_type(datapath_type); class = ofproto_class_find__(datapath_type); if (class && class->type_get_memory_usage) { class->type_get_memory_usage(datapath_type, usage); } } void ofproto_get_ofproto_controller_info(const struct ofproto *ofproto, struct shash *info) { connmgr_get_controller_info(ofproto->connmgr, info); } void ofproto_free_ofproto_controller_info(struct shash *info) { connmgr_free_controller_info(info); } /* Makes a deep copy of 'old' into 'port'. */ void ofproto_port_clone(struct ofproto_port *port, const struct ofproto_port *old) { port->name = xstrdup(old->name); port->type = xstrdup(old->type); port->ofp_port = old->ofp_port; } /* Frees memory allocated to members of 'ofproto_port'. * * Do not call this function on an ofproto_port obtained from * ofproto_port_dump_next(): that function retains ownership of the data in the * ofproto_port. */ void ofproto_port_destroy(struct ofproto_port *ofproto_port) { free(ofproto_port->name); free(ofproto_port->type); } /* Initializes 'dump' to begin dumping the ports in an ofproto. * * This function provides no status indication. An error status for the entire * dump operation is provided when it is completed by calling * ofproto_port_dump_done(). */ void ofproto_port_dump_start(struct ofproto_port_dump *dump, const struct ofproto *ofproto) { dump->ofproto = ofproto; dump->error = ofproto->ofproto_class->port_dump_start(ofproto, &dump->state); } /* Attempts to retrieve another port from 'dump', which must have been created * with ofproto_port_dump_start(). On success, stores a new ofproto_port into * 'port' and returns true. On failure, returns false. * * Failure might indicate an actual error or merely that the last port has been * dumped. An error status for the entire dump operation is provided when it * is completed by calling ofproto_port_dump_done(). * * The ofproto owns the data stored in 'port'. It will remain valid until at * least the next time 'dump' is passed to ofproto_port_dump_next() or * ofproto_port_dump_done(). */ bool ofproto_port_dump_next(struct ofproto_port_dump *dump, struct ofproto_port *port) { const struct ofproto *ofproto = dump->ofproto; if (dump->error) { return false; } dump->error = ofproto->ofproto_class->port_dump_next(ofproto, dump->state, port); if (dump->error) { ofproto->ofproto_class->port_dump_done(ofproto, dump->state); return false; } return true; } /* Completes port table dump operation 'dump', which must have been created * with ofproto_port_dump_start(). Returns 0 if the dump operation was * error-free, otherwise a positive errno value describing the problem. */ int ofproto_port_dump_done(struct ofproto_port_dump *dump) { const struct ofproto *ofproto = dump->ofproto; if (!dump->error) { dump->error = ofproto->ofproto_class->port_dump_done(ofproto, dump->state); } return dump->error == EOF ? 0 : dump->error; } /* Returns the type to pass to netdev_open() when a datapath of type * 'datapath_type' has a port of type 'port_type', for a few special * cases when a netdev type differs from a port type. For example, when * using the userspace datapath, a port of type "internal" needs to be * opened as "tap". * * Returns either 'type' itself or a string literal, which must not be * freed. */ const char * ofproto_port_open_type(const char *datapath_type, const char *port_type) { const struct ofproto_class *class; datapath_type = ofproto_normalize_type(datapath_type); class = ofproto_class_find__(datapath_type); if (!class) { return port_type; } return (class->port_open_type ? class->port_open_type(datapath_type, port_type) : port_type); } /* Attempts to add 'netdev' as a port on 'ofproto'. If 'ofp_portp' is * non-null and '*ofp_portp' is not OFPP_NONE, attempts to use that as * the port's OpenFlow port number. * * If successful, returns 0 and sets '*ofp_portp' to the new port's * OpenFlow port number (if 'ofp_portp' is non-null). On failure, * returns a positive errno value and sets '*ofp_portp' to OFPP_NONE (if * 'ofp_portp' is non-null). */ int ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev, ofp_port_t *ofp_portp) { ofp_port_t ofp_port = ofp_portp ? *ofp_portp : OFPP_NONE; int error; error = ofproto->ofproto_class->port_add(ofproto, netdev); if (!error) { const char *netdev_name = netdev_get_name(netdev); simap_put(&ofproto->ofp_requests, netdev_name, ofp_to_u16(ofp_port)); error = update_port(ofproto, netdev_name); } if (ofp_portp) { *ofp_portp = OFPP_NONE; if (!error) { struct ofproto_port ofproto_port; error = ofproto_port_query_by_name(ofproto, netdev_get_name(netdev), &ofproto_port); if (!error) { *ofp_portp = ofproto_port.ofp_port; ofproto_port_destroy(&ofproto_port); } } } return error; } /* Looks up a port named 'devname' in 'ofproto'. On success, returns 0 and * initializes '*port' appropriately; on failure, returns a positive errno * value. * * The caller owns the data in 'ofproto_port' and must free it with * ofproto_port_destroy() when it is no longer needed. */ int ofproto_port_query_by_name(const struct ofproto *ofproto, const char *devname, struct ofproto_port *port) { int error; error = ofproto->ofproto_class->port_query_by_name(ofproto, devname, port); if (error) { memset(port, 0, sizeof *port); } return error; } /* Deletes port number 'ofp_port' from the datapath for 'ofproto'. * Returns 0 if successful, otherwise a positive errno. */ int ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); const char *name = ofport ? netdev_get_name(ofport->netdev) : ""; struct simap_node *ofp_request_node; int error; ofp_request_node = simap_find(&ofproto->ofp_requests, name); if (ofp_request_node) { simap_delete(&ofproto->ofp_requests, ofp_request_node); } error = ofproto->ofproto_class->port_del(ofproto, ofp_port); if (!error && ofport) { /* 'name' is the netdev's name and update_port() is going to close the * netdev. Just in case update_port() refers to 'name' after it * destroys 'ofport', make a copy of it around the update_port() * call. */ char *devname = xstrdup(name); update_port(ofproto, devname); free(devname); } return error; } static void flow_mod_init(struct ofputil_flow_mod *fm, const struct match *match, int priority, const struct ofpact *ofpacts, size_t ofpacts_len, enum ofp_flow_mod_command command) { memset(fm, 0, sizeof *fm); fm->match = *match; fm->priority = priority; fm->cookie = 0; fm->new_cookie = 0; fm->modify_cookie = false; fm->table_id = 0; fm->command = command; fm->idle_timeout = 0; fm->hard_timeout = 0; fm->importance = 0; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_ANY; fm->out_group = OFPG_ANY; fm->flags = 0; fm->ofpacts = CONST_CAST(struct ofpact *, ofpacts); fm->ofpacts_len = ofpacts_len; fm->delete_reason = OFPRR_DELETE; } static int simple_flow_mod(struct ofproto *ofproto, const struct match *match, int priority, const struct ofpact *ofpacts, size_t ofpacts_len, enum ofp_flow_mod_command command) { struct ofproto_flow_mod ofm; flow_mod_init(&ofm.fm, match, priority, ofpacts, ofpacts_len, command); return handle_flow_mod__(ofproto, &ofm, NULL); } /* Adds a flow to OpenFlow flow table 0 in 'p' that matches 'cls_rule' and * performs the 'n_actions' actions in 'actions'. The new flow will not * timeout. * * If cls_rule->priority is in the range of priorities supported by OpenFlow * (0...65535, inclusive) then the flow will be visible to OpenFlow * controllers; otherwise, it will be hidden. * * The caller retains ownership of 'cls_rule' and 'ofpacts'. * * This is a helper function for in-band control and fail-open. */ void ofproto_add_flow(struct ofproto *ofproto, const struct match *match, int priority, const struct ofpact *ofpacts, size_t ofpacts_len) OVS_EXCLUDED(ofproto_mutex) { const struct rule *rule; bool must_add; /* First do a cheap check whether the rule we're looking for already exists * with the actions that we want. If it does, then we're done. */ rule = rule_from_cls_rule(classifier_find_match_exactly( &ofproto->tables[0].cls, match, priority, CLS_MAX_VERSION)); if (rule) { const struct rule_actions *actions = rule_get_actions(rule); must_add = !ofpacts_equal(actions->ofpacts, actions->ofpacts_len, ofpacts, ofpacts_len); } else { must_add = true; } /* If there's no such rule or the rule doesn't have the actions we want, * fall back to a executing a full flow mod. We can't optimize this at * all because we didn't take enough locks above to ensure that the flow * table didn't already change beneath us. */ if (must_add) { simple_flow_mod(ofproto, match, priority, ofpacts, ofpacts_len, OFPFC_MODIFY_STRICT); } } /* Executes the flow modification specified in 'fm'. Returns 0 on success, or * an OFPERR_* OpenFlow error code on failure. * * This is a helper function for in-band control and fail-open and the "learn" * action. */ enum ofperr ofproto_flow_mod(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_EXCLUDED(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; /* Optimize for the most common case of a repeated learn action. * If an identical flow already exists we only need to update its * 'modified' time. */ if (fm->command == OFPFC_MODIFY_STRICT && fm->table_id != OFPTT_ALL && !(fm->flags & OFPUTIL_FF_RESET_COUNTS)) { struct oftable *table = &ofproto->tables[fm->table_id]; struct rule *rule; bool done = false; rule = rule_from_cls_rule(classifier_find_match_exactly( &table->cls, &fm->match, fm->priority, CLS_MAX_VERSION)); if (rule) { /* Reading many of the rule fields and writing on 'modified' * requires the rule->mutex. Also, rule->actions may change * if rule->mutex is not held. */ const struct rule_actions *actions; ovs_mutex_lock(&rule->mutex); actions = rule_get_actions(rule); if (rule->idle_timeout == fm->idle_timeout && rule->hard_timeout == fm->hard_timeout && rule->importance == fm->importance && rule->flags == (fm->flags & OFPUTIL_FF_STATE) && (!fm->modify_cookie || (fm->new_cookie == rule->flow_cookie)) && ofpacts_equal(fm->ofpacts, fm->ofpacts_len, actions->ofpacts, actions->ofpacts_len)) { /* Rule already exists and need not change, only update the modified timestamp. */ rule->modified = time_msec(); done = true; } ovs_mutex_unlock(&rule->mutex); } if (done) { return 0; } } return handle_flow_mod__(ofproto, ofm, NULL); } /* Searches for a rule with matching criteria exactly equal to 'target' in * ofproto's table 0 and, if it finds one, deletes it. * * This is a helper function for in-band control and fail-open. */ void ofproto_delete_flow(struct ofproto *ofproto, const struct match *target, int priority) OVS_EXCLUDED(ofproto_mutex) { struct classifier *cls = &ofproto->tables[0].cls; struct rule *rule; /* First do a cheap check whether the rule we're looking for has already * been deleted. If so, then we're done. */ rule = rule_from_cls_rule(classifier_find_match_exactly( cls, target, priority, CLS_MAX_VERSION)); if (!rule) { return; } /* Execute a flow mod. We can't optimize this at all because we didn't * take enough locks above to ensure that the flow table didn't already * change beneath us. */ simple_flow_mod(ofproto, target, priority, NULL, 0, OFPFC_DELETE_STRICT); } /* Delete all of the flows from all of ofproto's flow tables, then reintroduce * the flows required by in-band control and fail-open. */ void ofproto_flush_flows(struct ofproto *ofproto) { COVERAGE_INC(ofproto_flush); ofproto_flush__(ofproto); connmgr_flushed(ofproto->connmgr); } static void reinit_ports(struct ofproto *p) { struct ofproto_port_dump dump; struct sset devnames; struct ofport *ofport; struct ofproto_port ofproto_port; const char *devname; COVERAGE_INC(ofproto_reinit_ports); sset_init(&devnames); HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { sset_add(&devnames, netdev_get_name(ofport->netdev)); } OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) { sset_add(&devnames, ofproto_port.name); } SSET_FOR_EACH (devname, &devnames) { update_port(p, devname); } sset_destroy(&devnames); } static ofp_port_t alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name) { uint16_t port_idx; port_idx = simap_get(&ofproto->ofp_requests, netdev_name); port_idx = port_idx ? port_idx : UINT16_MAX; if (port_idx >= ofproto->max_ports || ofport_get_usage(ofproto, u16_to_ofp(port_idx)) == LLONG_MAX) { uint16_t lru_ofport = 0, end_port_no = ofproto->alloc_port_no; long long int last_used_at, lru = LLONG_MAX; /* Search for a free OpenFlow port number. We try not to * immediately reuse them to prevent problems due to old * flows. * * We limit the automatically assigned port numbers to the lower half * of the port range, to reserve the upper half for assignment by * controllers. */ for (;;) { if (++ofproto->alloc_port_no >= MIN(ofproto->max_ports, 32768)) { ofproto->alloc_port_no = 1; } last_used_at = ofport_get_usage(ofproto, u16_to_ofp(ofproto->alloc_port_no)); if (!last_used_at) { port_idx = ofproto->alloc_port_no; break; } else if ( last_used_at < time_msec() - 60*60*1000) { /* If the port with ofport 'ofproto->alloc_port_no' was deleted * more than an hour ago, consider it usable. */ ofport_remove_usage(ofproto, u16_to_ofp(ofproto->alloc_port_no)); port_idx = ofproto->alloc_port_no; break; } else if (last_used_at < lru) { lru = last_used_at; lru_ofport = ofproto->alloc_port_no; } if (ofproto->alloc_port_no == end_port_no) { if (lru_ofport) { port_idx = lru_ofport; break; } return OFPP_NONE; } } } ofport_set_usage(ofproto, u16_to_ofp(port_idx), LLONG_MAX); return u16_to_ofp(port_idx); } static void dealloc_ofp_port(struct ofproto *ofproto, ofp_port_t ofp_port) { if (ofp_to_u16(ofp_port) < ofproto->max_ports) { ofport_set_usage(ofproto, ofp_port, time_msec()); } } /* Opens and returns a netdev for 'ofproto_port' in 'ofproto', or a null * pointer if the netdev cannot be opened. On success, also fills in * '*pp'. */ static struct netdev * ofport_open(struct ofproto *ofproto, struct ofproto_port *ofproto_port, struct ofputil_phy_port *pp) { enum netdev_flags flags; struct netdev *netdev; int error; error = netdev_open(ofproto_port->name, ofproto_port->type, &netdev); if (error) { VLOG_WARN_RL(&rl, "%s: ignoring port %s (%"PRIu16") because netdev %s " "cannot be opened (%s)", ofproto->name, ofproto_port->name, ofproto_port->ofp_port, ofproto_port->name, ovs_strerror(error)); return NULL; } if (ofproto_port->ofp_port == OFPP_NONE) { if (!strcmp(ofproto->name, ofproto_port->name)) { ofproto_port->ofp_port = OFPP_LOCAL; } else { ofproto_port->ofp_port = alloc_ofp_port(ofproto, ofproto_port->name); } } pp->port_no = ofproto_port->ofp_port; netdev_get_etheraddr(netdev, &pp->hw_addr); ovs_strlcpy(pp->name, ofproto_port->name, sizeof pp->name); netdev_get_flags(netdev, &flags); pp->config = flags & NETDEV_UP ? 0 : OFPUTIL_PC_PORT_DOWN; pp->state = netdev_get_carrier(netdev) ? 0 : OFPUTIL_PS_LINK_DOWN; netdev_get_features(netdev, &pp->curr, &pp->advertised, &pp->supported, &pp->peer); pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000; pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000; return netdev; } /* Returns true if most fields of 'a' and 'b' are equal. Differences in name, * port number, and 'config' bits other than OFPUTIL_PC_PORT_DOWN are * disregarded. */ static bool ofport_equal(const struct ofputil_phy_port *a, const struct ofputil_phy_port *b) { return (eth_addr_equals(a->hw_addr, b->hw_addr) && a->state == b->state && !((a->config ^ b->config) & OFPUTIL_PC_PORT_DOWN) && a->curr == b->curr && a->advertised == b->advertised && a->supported == b->supported && a->peer == b->peer && a->curr_speed == b->curr_speed && a->max_speed == b->max_speed); } /* Adds an ofport to 'p' initialized based on the given 'netdev' and 'opp'. * The caller must ensure that 'p' does not have a conflicting ofport (that is, * one with the same name or port number). */ static int ofport_install(struct ofproto *p, struct netdev *netdev, const struct ofputil_phy_port *pp) { const char *netdev_name = netdev_get_name(netdev); struct ofport *ofport; int error; /* Create ofport. */ ofport = p->ofproto_class->port_alloc(); if (!ofport) { error = ENOMEM; goto error; } ofport->ofproto = p; ofport->netdev = netdev; ofport->change_seq = netdev_get_change_seq(netdev); ofport->pp = *pp; ofport->ofp_port = pp->port_no; ofport->created = time_msec(); /* Add port to 'p'. */ hmap_insert(&p->ports, &ofport->hmap_node, hash_ofp_port(ofport->ofp_port)); shash_add(&p->port_by_name, netdev_name, ofport); update_mtu(p, ofport); /* Let the ofproto_class initialize its private data. */ error = p->ofproto_class->port_construct(ofport); if (error) { goto error; } connmgr_send_port_status(p->connmgr, NULL, pp, OFPPR_ADD); return 0; error: VLOG_WARN_RL(&rl, "%s: could not add port %s (%s)", p->name, netdev_name, ovs_strerror(error)); if (ofport) { ofport_destroy__(ofport); } else { netdev_close(netdev); } return error; } /* Removes 'ofport' from 'p' and destroys it. */ static void ofport_remove(struct ofport *ofport) { connmgr_send_port_status(ofport->ofproto->connmgr, NULL, &ofport->pp, OFPPR_DELETE); ofport_destroy(ofport, true); } /* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and * destroys it. */ static void ofport_remove_with_name(struct ofproto *ofproto, const char *name) { struct ofport *port = shash_find_data(&ofproto->port_by_name, name); if (port) { ofport_remove(port); } } /* Updates 'port' with new 'pp' description. * * Does not handle a name or port number change. The caller must implement * such a change as a delete followed by an add. */ static void ofport_modified(struct ofport *port, struct ofputil_phy_port *pp) { port->pp.hw_addr = pp->hw_addr; port->pp.config = ((port->pp.config & ~OFPUTIL_PC_PORT_DOWN) | (pp->config & OFPUTIL_PC_PORT_DOWN)); port->pp.state = ((port->pp.state & ~OFPUTIL_PS_LINK_DOWN) | (pp->state & OFPUTIL_PS_LINK_DOWN)); port->pp.curr = pp->curr; port->pp.advertised = pp->advertised; port->pp.supported = pp->supported; port->pp.peer = pp->peer; port->pp.curr_speed = pp->curr_speed; port->pp.max_speed = pp->max_speed; connmgr_send_port_status(port->ofproto->connmgr, NULL, &port->pp, OFPPR_MODIFY); } /* Update OpenFlow 'state' in 'port' and notify controller. */ void ofproto_port_set_state(struct ofport *port, enum ofputil_port_state state) { if (port->pp.state != state) { port->pp.state = state; connmgr_send_port_status(port->ofproto->connmgr, NULL, &port->pp, OFPPR_MODIFY); } } void ofproto_port_unregister(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *port = ofproto_get_port(ofproto, ofp_port); if (port) { if (port->ofproto->ofproto_class->set_realdev) { port->ofproto->ofproto_class->set_realdev(port, 0, 0); } if (port->ofproto->ofproto_class->set_stp_port) { port->ofproto->ofproto_class->set_stp_port(port, NULL); } if (port->ofproto->ofproto_class->set_rstp_port) { port->ofproto->ofproto_class->set_rstp_port(port, NULL); } if (port->ofproto->ofproto_class->set_cfm) { port->ofproto->ofproto_class->set_cfm(port, NULL); } if (port->ofproto->ofproto_class->bundle_remove) { port->ofproto->ofproto_class->bundle_remove(port); } } } static void ofport_destroy__(struct ofport *port) { struct ofproto *ofproto = port->ofproto; const char *name = netdev_get_name(port->netdev); hmap_remove(&ofproto->ports, &port->hmap_node); shash_delete(&ofproto->port_by_name, shash_find(&ofproto->port_by_name, name)); netdev_close(port->netdev); ofproto->ofproto_class->port_dealloc(port); } static void ofport_destroy(struct ofport *port, bool del) { if (port) { dealloc_ofp_port(port->ofproto, port->ofp_port); port->ofproto->ofproto_class->port_destruct(port, del); ofport_destroy__(port); } } struct ofport * ofproto_get_port(const struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *port; HMAP_FOR_EACH_IN_BUCKET (port, hmap_node, hash_ofp_port(ofp_port), &ofproto->ports) { if (port->ofp_port == ofp_port) { return port; } } return NULL; } static long long int ofport_get_usage(const struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport_usage *usage; HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port), &ofproto->ofport_usage) { if (usage->ofp_port == ofp_port) { return usage->last_used; } } return 0; } static void ofport_set_usage(struct ofproto *ofproto, ofp_port_t ofp_port, long long int last_used) { struct ofport_usage *usage; HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port), &ofproto->ofport_usage) { if (usage->ofp_port == ofp_port) { usage->last_used = last_used; return; } } ovs_assert(last_used == LLONG_MAX); usage = xmalloc(sizeof *usage); usage->ofp_port = ofp_port; usage->last_used = last_used; hmap_insert(&ofproto->ofport_usage, &usage->hmap_node, hash_ofp_port(ofp_port)); } static void ofport_remove_usage(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport_usage *usage; HMAP_FOR_EACH_IN_BUCKET (usage, hmap_node, hash_ofp_port(ofp_port), &ofproto->ofport_usage) { if (usage->ofp_port == ofp_port) { hmap_remove(&ofproto->ofport_usage, &usage->hmap_node); free(usage); break; } } } int ofproto_port_get_stats(const struct ofport *port, struct netdev_stats *stats) { struct ofproto *ofproto = port->ofproto; int error; if (ofproto->ofproto_class->port_get_stats) { error = ofproto->ofproto_class->port_get_stats(port, stats); } else { error = EOPNOTSUPP; } return error; } static int update_port(struct ofproto *ofproto, const char *name) { struct ofproto_port ofproto_port; struct ofputil_phy_port pp; struct netdev *netdev; struct ofport *port; int error = 0; COVERAGE_INC(ofproto_update_port); /* Fetch 'name''s location and properties from the datapath. */ netdev = (!ofproto_port_query_by_name(ofproto, name, &ofproto_port) ? ofport_open(ofproto, &ofproto_port, &pp) : NULL); if (netdev) { port = ofproto_get_port(ofproto, ofproto_port.ofp_port); if (port && !strcmp(netdev_get_name(port->netdev), name)) { struct netdev *old_netdev = port->netdev; /* 'name' hasn't changed location. Any properties changed? */ if (!ofport_equal(&port->pp, &pp)) { ofport_modified(port, &pp); } update_mtu(ofproto, port); /* Install the newly opened netdev in case it has changed. * Don't close the old netdev yet in case port_modified has to * remove a retained reference to it.*/ port->netdev = netdev; port->change_seq = netdev_get_change_seq(netdev); if (port->ofproto->ofproto_class->port_modified) { port->ofproto->ofproto_class->port_modified(port); } netdev_close(old_netdev); } else { /* If 'port' is nonnull then its name differs from 'name' and thus * we should delete it. If we think there's a port named 'name' * then its port number must be wrong now so delete it too. */ if (port) { ofport_remove(port); } ofport_remove_with_name(ofproto, name); error = ofport_install(ofproto, netdev, &pp); } } else { /* Any port named 'name' is gone now. */ ofport_remove_with_name(ofproto, name); } ofproto_port_destroy(&ofproto_port); return error; } static int init_ports(struct ofproto *p) { struct ofproto_port_dump dump; struct ofproto_port ofproto_port; struct shash_node *node, *next; OFPROTO_PORT_FOR_EACH (&ofproto_port, &dump, p) { const char *name = ofproto_port.name; if (shash_find(&p->port_by_name, name)) { VLOG_WARN_RL(&rl, "%s: ignoring duplicate device %s in datapath", p->name, name); } else { struct ofputil_phy_port pp; struct netdev *netdev; /* Check if an OpenFlow port number had been requested. */ node = shash_find(&init_ofp_ports, name); if (node) { const struct iface_hint *iface_hint = node->data; simap_put(&p->ofp_requests, name, ofp_to_u16(iface_hint->ofp_port)); } netdev = ofport_open(p, &ofproto_port, &pp); if (netdev) { ofport_install(p, netdev, &pp); if (ofp_to_u16(ofproto_port.ofp_port) < p->max_ports) { p->alloc_port_no = MAX(p->alloc_port_no, ofp_to_u16(ofproto_port.ofp_port)); } } } } SHASH_FOR_EACH_SAFE(node, next, &init_ofp_ports) { struct iface_hint *iface_hint = node->data; if (!strcmp(iface_hint->br_name, p->name)) { free(iface_hint->br_name); free(iface_hint->br_type); free(iface_hint); shash_delete(&init_ofp_ports, node); } } return 0; } /* Find the minimum MTU of all non-datapath devices attached to 'p'. * Returns ETH_PAYLOAD_MAX or the minimum of the ports. */ static int find_min_mtu(struct ofproto *p) { struct ofport *ofport; int mtu = 0; HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { struct netdev *netdev = ofport->netdev; int dev_mtu; /* Skip any internal ports, since that's what we're trying to * set. */ if (!strcmp(netdev_get_type(netdev), "internal")) { continue; } if (netdev_get_mtu(netdev, &dev_mtu)) { continue; } if (!mtu || dev_mtu < mtu) { mtu = dev_mtu; } } return mtu ? mtu: ETH_PAYLOAD_MAX; } /* Update MTU of all datapath devices on 'p' to the minimum of the * non-datapath ports in event of 'port' added or changed. */ static void update_mtu(struct ofproto *p, struct ofport *port) { struct ofport *ofport; struct netdev *netdev = port->netdev; int dev_mtu, old_min; if (netdev_get_mtu(netdev, &dev_mtu)) { port->mtu = 0; return; } if (!strcmp(netdev_get_type(port->netdev), "internal")) { if (dev_mtu > p->min_mtu) { if (!netdev_set_mtu(port->netdev, p->min_mtu)) { dev_mtu = p->min_mtu; } } port->mtu = dev_mtu; return; } /* For non-internal port find new min mtu. */ old_min = p->min_mtu; port->mtu = dev_mtu; p->min_mtu = find_min_mtu(p); if (p->min_mtu == old_min) { return; } HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { struct netdev *netdev = ofport->netdev; if (!strcmp(netdev_get_type(netdev), "internal")) { if (!netdev_set_mtu(netdev, p->min_mtu)) { ofport->mtu = p->min_mtu; } } } } static void ofproto_rule_destroy__(struct rule *rule) OVS_NO_THREAD_SAFETY_ANALYSIS { cls_rule_destroy(CONST_CAST(struct cls_rule *, &rule->cr)); rule_actions_destroy(rule_get_actions(rule)); ovs_mutex_destroy(&rule->mutex); rule->ofproto->ofproto_class->rule_dealloc(rule); } static void rule_destroy_cb(struct rule *rule) OVS_NO_THREAD_SAFETY_ANALYSIS { /* Send rule removed if needed. */ if (rule->flags & OFPUTIL_FF_SEND_FLOW_REM && rule->removed_reason != OVS_OFPRR_NONE && !rule_is_hidden(rule)) { ofproto_rule_send_removed(rule); } rule->ofproto->ofproto_class->rule_destruct(rule); ofproto_rule_destroy__(rule); } void ofproto_rule_ref(struct rule *rule) { if (rule) { ovs_refcount_ref(&rule->ref_count); } } bool ofproto_rule_try_ref(struct rule *rule) { if (rule) { return ovs_refcount_try_ref_rcu(&rule->ref_count); } return false; } /* Decrements 'rule''s ref_count and schedules 'rule' to be destroyed if the * ref_count reaches 0. * * Use of RCU allows short term use (between RCU quiescent periods) without * keeping a reference. A reference must be taken if the rule needs to * stay around accross the RCU quiescent periods. */ void ofproto_rule_unref(struct rule *rule) { if (rule && ovs_refcount_unref_relaxed(&rule->ref_count) == 1) { ovsrcu_postpone(rule_destroy_cb, rule); } } static void remove_rule_rcu__(struct rule *rule) OVS_REQUIRES(ofproto_mutex) { struct ofproto *ofproto = rule->ofproto; struct oftable *table = &ofproto->tables[rule->table_id]; ovs_assert(!cls_rule_visible_in_version(&rule->cr, CLS_MAX_VERSION)); if (!classifier_remove(&table->cls, &rule->cr)) { OVS_NOT_REACHED(); } ofproto->ofproto_class->rule_delete(rule); ofproto_rule_unref(rule); } static void remove_rule_rcu(struct rule *rule) OVS_EXCLUDED(ofproto_mutex) { ovs_mutex_lock(&ofproto_mutex); remove_rule_rcu__(rule); ovs_mutex_unlock(&ofproto_mutex); } /* Removes and deletes rules from a NULL-terminated array of rule pointers. */ static void remove_rules_rcu(struct rule **rules) OVS_EXCLUDED(ofproto_mutex) { struct rule **orig_rules = rules; if (*rules) { struct ofproto *ofproto = rules[0]->ofproto; unsigned long tables[BITMAP_N_LONGS(256)]; struct rule *rule; size_t table_id; memset(tables, 0, sizeof tables); ovs_mutex_lock(&ofproto_mutex); while ((rule = *rules++)) { /* Defer once for each new table. This defers the subtable cleanup * until later, so that when removing large number of flows the * operation is faster. */ if (!bitmap_is_set(tables, rule->table_id)) { struct classifier *cls = &ofproto->tables[rule->table_id].cls; bitmap_set1(tables, rule->table_id); classifier_defer(cls); } remove_rule_rcu__(rule); } BITMAP_FOR_EACH_1(table_id, 256, tables) { struct classifier *cls = &ofproto->tables[table_id].cls; classifier_publish(cls); } ovs_mutex_unlock(&ofproto_mutex); } free(orig_rules); } void ofproto_group_ref(struct ofgroup *group) { if (group) { ovs_refcount_ref(&group->ref_count); } } void ofproto_group_unref(struct ofgroup *group) { if (group && ovs_refcount_unref(&group->ref_count) == 1) { group->ofproto->ofproto_class->group_destruct(group); ofputil_bucket_list_destroy(&group->buckets); group->ofproto->ofproto_class->group_dealloc(group); } } static uint32_t get_provider_meter_id(const struct ofproto *, uint32_t of_meter_id); /* Creates and returns a new 'struct rule_actions', whose actions are a copy * of from the 'ofpacts_len' bytes of 'ofpacts'. */ const struct rule_actions * rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len) { struct rule_actions *actions; actions = xmalloc(sizeof *actions + ofpacts_len); actions->ofpacts_len = ofpacts_len; actions->has_meter = ofpacts_get_meter(ofpacts, ofpacts_len) != 0; memcpy(actions->ofpacts, ofpacts, ofpacts_len); actions->has_learn_with_delete = (next_learn_with_delete(actions, NULL) != NULL); return actions; } /* Free the actions after the RCU quiescent period is reached. */ void rule_actions_destroy(const struct rule_actions *actions) { if (actions) { ovsrcu_postpone(free, CONST_CAST(struct rule_actions *, actions)); } } /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */ bool ofproto_rule_has_out_port(const struct rule *rule, ofp_port_t port) OVS_REQUIRES(ofproto_mutex) { if (port == OFPP_ANY) { return true; } else { const struct rule_actions *actions = rule_get_actions(rule); return ofpacts_output_to_port(actions->ofpacts, actions->ofpacts_len, port); } } /* Returns true if 'rule' has group and equals group_id. */ static bool ofproto_rule_has_out_group(const struct rule *rule, uint32_t group_id) OVS_REQUIRES(ofproto_mutex) { if (group_id == OFPG_ANY) { return true; } else { const struct rule_actions *actions = rule_get_actions(rule); return ofpacts_output_to_group(actions->ofpacts, actions->ofpacts_len, group_id); } } static void rule_execute_destroy(struct rule_execute *e) { ofproto_rule_unref(e->rule); list_remove(&e->list_node); free(e); } /* Executes all "rule_execute" operations queued up in ofproto->rule_executes, * by passing them to the ofproto provider. */ static void run_rule_executes(struct ofproto *ofproto) OVS_EXCLUDED(ofproto_mutex) { struct rule_execute *e, *next; struct ovs_list executes; guarded_list_pop_all(&ofproto->rule_executes, &executes); LIST_FOR_EACH_SAFE (e, next, list_node, &executes) { struct flow flow; flow_extract(e->packet, &flow); flow.in_port.ofp_port = e->in_port; ofproto->ofproto_class->rule_execute(e->rule, &flow, e->packet); rule_execute_destroy(e); } } /* Destroys and discards all "rule_execute" operations queued up in * ofproto->rule_executes. */ static void destroy_rule_executes(struct ofproto *ofproto) { struct rule_execute *e, *next; struct ovs_list executes; guarded_list_pop_all(&ofproto->rule_executes, &executes); LIST_FOR_EACH_SAFE (e, next, list_node, &executes) { dp_packet_delete(e->packet); rule_execute_destroy(e); } } static bool rule_is_readonly(const struct rule *rule) { const struct oftable *table = &rule->ofproto->tables[rule->table_id]; return (table->flags & OFTABLE_READONLY) != 0; } static uint32_t hash_learned_cookie(ovs_be64 cookie_, uint8_t table_id) { uint64_t cookie = (OVS_FORCE uint64_t) cookie_; return hash_3words(cookie, cookie >> 32, table_id); } static void learned_cookies_update_one__(struct ofproto *ofproto, const struct ofpact_learn *learn, int delta, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex) { uint32_t hash = hash_learned_cookie(learn->cookie, learn->table_id); struct learned_cookie *c; HMAP_FOR_EACH_WITH_HASH (c, u.hmap_node, hash, &ofproto->learned_cookies) { if (c->cookie == learn->cookie && c->table_id == learn->table_id) { c->n += delta; ovs_assert(c->n >= 0); if (!c->n) { hmap_remove(&ofproto->learned_cookies, &c->u.hmap_node); list_push_back(dead_cookies, &c->u.list_node); } return; } } ovs_assert(delta > 0); c = xmalloc(sizeof *c); hmap_insert(&ofproto->learned_cookies, &c->u.hmap_node, hash); c->cookie = learn->cookie; c->table_id = learn->table_id; c->n = delta; } static const struct ofpact_learn * next_learn_with_delete(const struct rule_actions *actions, const struct ofpact_learn *start) { const struct ofpact *pos; for (pos = start ? ofpact_next(&start->ofpact) : actions->ofpacts; pos < ofpact_end(actions->ofpacts, actions->ofpacts_len); pos = ofpact_next(pos)) { if (pos->type == OFPACT_LEARN) { const struct ofpact_learn *learn = ofpact_get_LEARN(pos); if (learn->flags & NX_LEARN_F_DELETE_LEARNED) { return learn; } } } return NULL; } static void learned_cookies_update__(struct ofproto *ofproto, const struct rule_actions *actions, int delta, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex) { if (actions->has_learn_with_delete) { const struct ofpact_learn *learn; for (learn = next_learn_with_delete(actions, NULL); learn; learn = next_learn_with_delete(actions, learn)) { learned_cookies_update_one__(ofproto, learn, delta, dead_cookies); } } } static void learned_cookies_inc(struct ofproto *ofproto, const struct rule_actions *actions) OVS_REQUIRES(ofproto_mutex) { learned_cookies_update__(ofproto, actions, +1, NULL); } static void learned_cookies_dec(struct ofproto *ofproto, const struct rule_actions *actions, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex) { learned_cookies_update__(ofproto, actions, -1, dead_cookies); } static void learned_cookies_flush(struct ofproto *ofproto, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex) { struct learned_cookie *c; LIST_FOR_EACH_POP (c, u.list_node, dead_cookies) { struct rule_criteria criteria; struct rule_collection rules; struct match match; match_init_catchall(&match); rule_criteria_init(&criteria, c->table_id, &match, 0, CLS_MAX_VERSION, c->cookie, OVS_BE64_MAX, OFPP_ANY, OFPG_ANY); rule_criteria_require_rw(&criteria, false); collect_rules_loose(ofproto, &criteria, &rules); rule_criteria_destroy(&criteria); delete_flows__(&rules, OFPRR_DELETE, NULL); free(c); } } static enum ofperr handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh) { ofconn_send_reply(ofconn, make_echo_reply(oh)); return 0; } static void query_tables(struct ofproto *ofproto, struct ofputil_table_features **featuresp, struct ofputil_table_stats **statsp) { struct mf_bitmap rw_fields = oxm_writable_fields(); struct mf_bitmap match = oxm_matchable_fields(); struct mf_bitmap mask = oxm_maskable_fields(); struct ofputil_table_features *features; struct ofputil_table_stats *stats; int i; features = *featuresp = xcalloc(ofproto->n_tables, sizeof *features); for (i = 0; i < ofproto->n_tables; i++) { struct ofputil_table_features *f = &features[i]; f->table_id = i; sprintf(f->name, "table%d", i); f->metadata_match = OVS_BE64_MAX; f->metadata_write = OVS_BE64_MAX; atomic_read_relaxed(&ofproto->tables[i].miss_config, &f->miss_config); f->max_entries = 1000000; bool more_tables = false; for (int j = i + 1; j < ofproto->n_tables; j++) { if (!(ofproto->tables[j].flags & OFTABLE_HIDDEN)) { bitmap_set1(f->nonmiss.next, j); more_tables = true; } } f->nonmiss.instructions = (1u << N_OVS_INSTRUCTIONS) - 1; if (!more_tables) { f->nonmiss.instructions &= ~(1u << OVSINST_OFPIT11_GOTO_TABLE); } f->nonmiss.write.ofpacts = (UINT64_C(1) << N_OFPACTS) - 1; f->nonmiss.write.set_fields = rw_fields; f->nonmiss.apply = f->nonmiss.write; f->miss = f->nonmiss; f->match = match; f->mask = mask; f->wildcard = match; } if (statsp) { stats = *statsp = xcalloc(ofproto->n_tables, sizeof *stats); for (i = 0; i < ofproto->n_tables; i++) { struct ofputil_table_stats *s = &stats[i]; s->table_id = i; s->active_count = ofproto->tables[i].n_flows; if (i == 0) { s->active_count -= connmgr_count_hidden_rules( ofproto->connmgr); } } } else { stats = NULL; } ofproto->ofproto_class->query_tables(ofproto, features, stats); for (i = 0; i < ofproto->n_tables; i++) { const struct oftable *table = &ofproto->tables[i]; struct ofputil_table_features *f = &features[i]; if (table->name) { ovs_strzcpy(f->name, table->name, sizeof f->name); } if (table->max_flows < f->max_entries) { f->max_entries = table->max_flows; } } } static void query_switch_features(struct ofproto *ofproto, bool *arp_match_ip, uint64_t *ofpacts) { struct ofputil_table_features *features, *f; *arp_match_ip = false; *ofpacts = 0; query_tables(ofproto, &features, NULL); for (f = features; f < &features[ofproto->n_tables]; f++) { *ofpacts |= f->nonmiss.apply.ofpacts | f->miss.apply.ofpacts; if (bitmap_is_set(f->match.bm, MFF_ARP_SPA) || bitmap_is_set(f->match.bm, MFF_ARP_TPA)) { *arp_match_ip = true; } } free(features); /* Sanity check. */ ovs_assert(*ofpacts & (UINT64_C(1) << OFPACT_OUTPUT)); } static enum ofperr handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_switch_features features; struct ofport *port; bool arp_match_ip; struct ofpbuf *b; query_switch_features(ofproto, &arp_match_ip, &features.ofpacts); features.datapath_id = ofproto->datapath_id; features.n_buffers = pktbuf_capacity(); features.n_tables = ofproto_get_n_visible_tables(ofproto); features.capabilities = (OFPUTIL_C_FLOW_STATS | OFPUTIL_C_TABLE_STATS | OFPUTIL_C_PORT_STATS | OFPUTIL_C_QUEUE_STATS | OFPUTIL_C_GROUP_STATS | OFPUTIL_C_BUNDLES); if (arp_match_ip) { features.capabilities |= OFPUTIL_C_ARP_MATCH_IP; } /* FIXME: Fill in proper features.auxiliary_id for auxiliary connections */ features.auxiliary_id = 0; b = ofputil_encode_switch_features(&features, ofconn_get_protocol(ofconn), oh->xid); HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { ofputil_put_switch_features_port(&port->pp, b); } ofconn_send_reply(ofconn, b); return 0; } static enum ofperr handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofp_switch_config *osc; enum ofp_config_flags flags; struct ofpbuf *buf; /* Send reply. */ buf = ofpraw_alloc_reply(OFPRAW_OFPT_GET_CONFIG_REPLY, oh, 0); osc = ofpbuf_put_uninit(buf, sizeof *osc); flags = ofproto->frag_handling; /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OF 1.3 */ if (oh->version < OFP13_VERSION && ofconn_get_invalid_ttl_to_controller(ofconn)) { flags |= OFPC_INVALID_TTL_TO_CONTROLLER; } osc->flags = htons(flags); osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn)); ofconn_send_reply(ofconn, buf); return 0; } static enum ofperr handle_set_config(struct ofconn *ofconn, const struct ofp_header *oh) { const struct ofp_switch_config *osc = ofpmsg_body(oh); struct ofproto *ofproto = ofconn_get_ofproto(ofconn); uint16_t flags = ntohs(osc->flags); if (ofconn_get_type(ofconn) != OFCONN_PRIMARY || ofconn_get_role(ofconn) != OFPCR12_ROLE_SLAVE) { enum ofp_config_flags cur = ofproto->frag_handling; enum ofp_config_flags next = flags & OFPC_FRAG_MASK; ovs_assert((cur & OFPC_FRAG_MASK) == cur); if (cur != next) { if (ofproto->ofproto_class->set_frag_handling(ofproto, next)) { ofproto->frag_handling = next; } else { VLOG_WARN_RL(&rl, "%s: unsupported fragment handling mode %s", ofproto->name, ofputil_frag_handling_to_string(next)); } } } /* OFPC_INVALID_TTL_TO_CONTROLLER is deprecated in OF 1.3 */ ofconn_set_invalid_ttl_to_controller(ofconn, (oh->version < OFP13_VERSION && flags & OFPC_INVALID_TTL_TO_CONTROLLER)); ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len)); return 0; } /* Checks whether 'ofconn' is a slave controller. If so, returns an OpenFlow * error message code for the caller to propagate upward. Otherwise, returns * 0. * * The log message mentions 'msg_type'. */ static enum ofperr reject_slave_controller(struct ofconn *ofconn) { if (ofconn_get_type(ofconn) == OFCONN_PRIMARY && ofconn_get_role(ofconn) == OFPCR12_ROLE_SLAVE) { return OFPERR_OFPBRC_IS_SLAVE; } else { return 0; } } /* Checks that the 'ofpacts_len' bytes of action in 'ofpacts' are appropriate * for 'ofproto': * * - If they use a meter, then 'ofproto' has that meter configured. * * - If they use any groups, then 'ofproto' has that group configured. * * Returns 0 if successful, otherwise an OpenFlow error. */ enum ofperr ofproto_check_ofpacts(struct ofproto *ofproto, const struct ofpact ofpacts[], size_t ofpacts_len) { const struct ofpact *a; uint32_t mid; mid = ofpacts_get_meter(ofpacts, ofpacts_len); if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) { return OFPERR_OFPMMFC_INVALID_METER; } OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) { if (a->type == OFPACT_GROUP && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) { return OFPERR_OFPBAC_BAD_OUT_GROUP; } } return 0; } static enum ofperr handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *p = ofconn_get_ofproto(ofconn); struct ofputil_packet_out po; struct dp_packet *payload; uint64_t ofpacts_stub[1024 / 8]; struct ofpbuf ofpacts; struct flow flow; enum ofperr error; COVERAGE_INC(ofproto_packet_out); error = reject_slave_controller(ofconn); if (error) { goto exit; } /* Decode message. */ ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); error = ofputil_decode_packet_out(&po, oh, &ofpacts); if (error) { goto exit_free_ofpacts; } if (ofp_to_u16(po.in_port) >= p->max_ports && ofp_to_u16(po.in_port) < ofp_to_u16(OFPP_MAX)) { error = OFPERR_OFPBRC_BAD_PORT; goto exit_free_ofpacts; } /* Get payload. */ if (po.buffer_id != UINT32_MAX) { error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL); if (error || !payload) { goto exit_free_ofpacts; } } else { /* Ensure that the L3 header is 32-bit aligned. */ payload = dp_packet_clone_data_with_headroom(po.packet, po.packet_len, 2); } /* Verify actions against packet, then send packet if successful. */ flow_extract(payload, &flow); flow.in_port.ofp_port = po.in_port; /* Check actions like for flow mods. We pass a 'table_id' of 0 to * ofproto_check_consistency(), which isn't strictly correct because these * actions aren't in any table. This is OK as 'table_id' is only used to * check instructions (e.g., goto-table), which can't appear on the action * list of a packet-out. */ error = ofpacts_check_consistency(po.ofpacts, po.ofpacts_len, &flow, u16_to_ofp(p->max_ports), 0, p->n_tables, ofconn_get_protocol(ofconn)); if (!error) { error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len); if (!error) { error = p->ofproto_class->packet_out(p, payload, &flow, po.ofpacts, po.ofpacts_len); } } dp_packet_delete(payload); exit_free_ofpacts: ofpbuf_uninit(&ofpacts); exit: return error; } static void update_port_config(struct ofconn *ofconn, struct ofport *port, enum ofputil_port_config config, enum ofputil_port_config mask) { enum ofputil_port_config toggle = (config ^ port->pp.config) & mask; if (toggle & OFPUTIL_PC_PORT_DOWN && (config & OFPUTIL_PC_PORT_DOWN ? netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL) : netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL))) { /* We tried to bring the port up or down, but it failed, so don't * update the "down" bit. */ toggle &= ~OFPUTIL_PC_PORT_DOWN; } if (toggle) { enum ofputil_port_config old_config = port->pp.config; port->pp.config ^= toggle; port->ofproto->ofproto_class->port_reconfigured(port, old_config); connmgr_send_port_status(port->ofproto->connmgr, ofconn, &port->pp, OFPPR_MODIFY); } } static enum ofperr port_mod_start(struct ofconn *ofconn, struct ofputil_port_mod *pm, struct ofport **port) { struct ofproto *p = ofconn_get_ofproto(ofconn); *port = ofproto_get_port(p, pm->port_no); if (!*port) { return OFPERR_OFPPMFC_BAD_PORT; } if (!eth_addr_equals((*port)->pp.hw_addr, pm->hw_addr)) { return OFPERR_OFPPMFC_BAD_HW_ADDR; } return 0; } static void port_mod_finish(struct ofconn *ofconn, struct ofputil_port_mod *pm, struct ofport *port) { update_port_config(ofconn, port, pm->config, pm->mask); if (pm->advertise) { netdev_set_advertisements(port->netdev, pm->advertise); } } static enum ofperr handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofputil_port_mod pm; struct ofport *port; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_port_mod(oh, &pm, false); if (error) { return error; } error = port_mod_start(ofconn, &pm, &port); if (!error) { port_mod_finish(ofconn, &pm, port); } return error; } static enum ofperr handle_desc_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { static const char *default_mfr_desc = "Nicira, Inc."; static const char *default_hw_desc = "Open vSwitch"; static const char *default_sw_desc = VERSION; static const char *default_serial_desc = "None"; static const char *default_dp_desc = "None"; struct ofproto *p = ofconn_get_ofproto(ofconn); struct ofp_desc_stats *ods; struct ofpbuf *msg; msg = ofpraw_alloc_stats_reply(request, 0); ods = ofpbuf_put_zeros(msg, sizeof *ods); ovs_strlcpy(ods->mfr_desc, p->mfr_desc ? p->mfr_desc : default_mfr_desc, sizeof ods->mfr_desc); ovs_strlcpy(ods->hw_desc, p->hw_desc ? p->hw_desc : default_hw_desc, sizeof ods->hw_desc); ovs_strlcpy(ods->sw_desc, p->sw_desc ? p->sw_desc : default_sw_desc, sizeof ods->sw_desc); ovs_strlcpy(ods->serial_num, p->serial_desc ? p->serial_desc : default_serial_desc, sizeof ods->serial_num); ovs_strlcpy(ods->dp_desc, p->dp_desc ? p->dp_desc : default_dp_desc, sizeof ods->dp_desc); ofconn_send_reply(ofconn, msg); return 0; } static enum ofperr handle_table_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_table_features *features; struct ofputil_table_stats *stats; struct ofpbuf *reply; size_t i; query_tables(ofproto, &features, &stats); reply = ofputil_encode_table_stats_reply(request); for (i = 0; i < ofproto->n_tables; i++) { if (!(ofproto->tables[i].flags & OFTABLE_HIDDEN)) { ofputil_append_table_stats_reply(reply, &stats[i], &features[i]); } } ofconn_send_reply(ofconn, reply); free(features); free(stats); return 0; } static enum ofperr handle_table_features_request(struct ofconn *ofconn, const struct ofp_header *request) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_table_features *features; struct ovs_list replies; struct ofpbuf msg; size_t i; ofpbuf_use_const(&msg, request, ntohs(request->length)); ofpraw_pull_assert(&msg); if (msg.size || ofpmp_more(request)) { return OFPERR_OFPTFFC_EPERM; } query_tables(ofproto, &features, NULL); ofpmp_init(&replies, request); for (i = 0; i < ofproto->n_tables; i++) { if (!(ofproto->tables[i].flags & OFTABLE_HIDDEN)) { ofputil_append_table_features_reply(&features[i], &replies); } } ofconn_send_replies(ofconn, &replies); free(features); return 0; } static void query_table_desc__(struct ofputil_table_desc *td, struct ofproto *ofproto, uint8_t table_id) { unsigned int count = ofproto->tables[table_id].n_flows; unsigned int max_flows = ofproto->tables[table_id].max_flows; td->table_id = table_id; td->eviction = (ofproto->tables[table_id].eviction & EVICTION_OPENFLOW ? OFPUTIL_TABLE_EVICTION_ON : OFPUTIL_TABLE_EVICTION_OFF); td->eviction_flags = OFPROTO_EVICTION_FLAGS; td->vacancy = (ofproto->tables[table_id].vacancy_enabled ? OFPUTIL_TABLE_VACANCY_ON : OFPUTIL_TABLE_VACANCY_OFF); td->table_vacancy.vacancy_down = ofproto->tables[table_id].vacancy_down; td->table_vacancy.vacancy_up = ofproto->tables[table_id].vacancy_up; td->table_vacancy.vacancy = max_flows ? (count * 100) / max_flows : 0; } /* This function queries the database for dumping table-desc. */ static void query_tables_desc(struct ofproto *ofproto, struct ofputil_table_desc **descp) { struct ofputil_table_desc *table_desc; size_t i; table_desc = *descp = xcalloc(ofproto->n_tables, sizeof *table_desc); for (i = 0; i < ofproto->n_tables; i++) { struct ofputil_table_desc *td = &table_desc[i]; query_table_desc__(td, ofproto, i); } } /* Function to handle dump-table-desc request. */ static enum ofperr handle_table_desc_request(struct ofconn *ofconn, const struct ofp_header *request) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_table_desc *table_desc; struct ovs_list replies; size_t i; query_tables_desc(ofproto, &table_desc); ofpmp_init(&replies, request); for (i = 0; i < ofproto->n_tables; i++) { if (!(ofproto->tables[i].flags & OFTABLE_HIDDEN)) { ofputil_append_table_desc_reply(&table_desc[i], &replies, request->version); } } ofconn_send_replies(ofconn, &replies); free(table_desc); return 0; } static void append_port_stat(struct ofport *port, struct ovs_list *replies) { struct ofputil_port_stats ops = { .port_no = port->pp.port_no }; calc_duration(port->created, time_msec(), &ops.duration_sec, &ops.duration_nsec); /* Intentionally ignore return value, since errors will set * 'stats' to all-1s, which is correct for OpenFlow, and * netdev_get_stats() will log errors. */ ofproto_port_get_stats(port, &ops.stats); ofputil_append_port_stat(replies, &ops); } static void handle_port_request(struct ofconn *ofconn, const struct ofp_header *request, ofp_port_t port_no, void (*cb)(struct ofport *, struct ovs_list *replies)) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofport *port; struct ovs_list replies; ofpmp_init(&replies, request); if (port_no != OFPP_ANY) { port = ofproto_get_port(ofproto, port_no); if (port) { cb(port, &replies); } } else { HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { cb(port, &replies); } } ofconn_send_replies(ofconn, &replies); } static enum ofperr handle_port_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { ofp_port_t port_no; enum ofperr error; error = ofputil_decode_port_stats_request(request, &port_no); if (!error) { handle_port_request(ofconn, request, port_no, append_port_stat); } return error; } static void append_port_desc(struct ofport *port, struct ovs_list *replies) { ofputil_append_port_desc_stats_reply(&port->pp, replies); } static enum ofperr handle_port_desc_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { ofp_port_t port_no; enum ofperr error; error = ofputil_decode_port_desc_stats_request(request, &port_no); if (!error) { handle_port_request(ofconn, request, port_no, append_port_desc); } return error; } static uint32_t hash_cookie(ovs_be64 cookie) { return hash_uint64((OVS_FORCE uint64_t)cookie); } static void cookies_insert(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { hindex_insert(&ofproto->cookies, &rule->cookie_node, hash_cookie(rule->flow_cookie)); } static void cookies_remove(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { hindex_remove(&ofproto->cookies, &rule->cookie_node); } static void calc_duration(long long int start, long long int now, uint32_t *sec, uint32_t *nsec) { long long int msecs = now - start; *sec = msecs / 1000; *nsec = (msecs % 1000) * (1000 * 1000); } /* Checks whether 'table_id' is 0xff or a valid table ID in 'ofproto'. Returns * true if 'table_id' is OK, false otherwise. */ static bool check_table_id(const struct ofproto *ofproto, uint8_t table_id) { return table_id == OFPTT_ALL || table_id < ofproto->n_tables; } static struct oftable * next_visible_table(const struct ofproto *ofproto, uint8_t table_id) { struct oftable *table; for (table = &ofproto->tables[table_id]; table < &ofproto->tables[ofproto->n_tables]; table++) { if (!(table->flags & OFTABLE_HIDDEN)) { return table; } } return NULL; } static struct oftable * first_matching_table(const struct ofproto *ofproto, uint8_t table_id) { if (table_id == 0xff) { return next_visible_table(ofproto, 0); } else if (table_id < ofproto->n_tables) { return &ofproto->tables[table_id]; } else { return NULL; } } static struct oftable * next_matching_table(const struct ofproto *ofproto, const struct oftable *table, uint8_t table_id) { return (table_id == 0xff ? next_visible_table(ofproto, (table - ofproto->tables) + 1) : NULL); } /* Assigns TABLE to each oftable, in turn, that matches TABLE_ID in OFPROTO: * * - If TABLE_ID is 0xff, this iterates over every classifier table in * OFPROTO, skipping tables marked OFTABLE_HIDDEN. * * - If TABLE_ID is the number of a table in OFPROTO, then the loop iterates * only once, for that table. (This can be used to access tables marked * OFTABLE_HIDDEN.) * * - Otherwise, TABLE_ID isn't valid for OFPROTO, so the loop won't be * entered at all. (Perhaps you should have validated TABLE_ID with * check_table_id().) * * All parameters are evaluated multiple times. */ #define FOR_EACH_MATCHING_TABLE(TABLE, TABLE_ID, OFPROTO) \ for ((TABLE) = first_matching_table(OFPROTO, TABLE_ID); \ (TABLE) != NULL; \ (TABLE) = next_matching_table(OFPROTO, TABLE, TABLE_ID)) /* Initializes 'criteria' in a straightforward way based on the other * parameters. * * By default, the criteria include flows that are read-only, on the assumption * that the collected flows won't be modified. Call rule_criteria_require_rw() * if flows will be modified. * * For "loose" matching, the 'priority' parameter is unimportant and may be * supplied as 0. */ static void rule_criteria_init(struct rule_criteria *criteria, uint8_t table_id, const struct match *match, int priority, cls_version_t version, ovs_be64 cookie, ovs_be64 cookie_mask, ofp_port_t out_port, uint32_t out_group) { criteria->table_id = table_id; cls_rule_init(&criteria->cr, match, priority); criteria->version = version; criteria->cookie = cookie; criteria->cookie_mask = cookie_mask; criteria->out_port = out_port; criteria->out_group = out_group; /* We ordinarily want to skip hidden rules, but there has to be a way for * code internal to OVS to modify and delete them, so if the criteria * specify a priority that can only be for a hidden flow, then allow hidden * rules to be selected. (This doesn't allow OpenFlow clients to meddle * with hidden flows because OpenFlow uses only a 16-bit field to specify * priority.) */ criteria->include_hidden = priority > UINT16_MAX; /* We assume that the criteria are being used to collect flows for reading * but not modification. Thus, we should collect read-only flows. */ criteria->include_readonly = true; } /* By default, criteria initialized by rule_criteria_init() will match flows * that are read-only, on the assumption that the collected flows won't be * modified. Call this function to match only flows that are be modifiable. * * Specify 'can_write_readonly' as false in ordinary circumstances, true if the * caller has special privileges that allow it to modify even "read-only" * flows. */ static void rule_criteria_require_rw(struct rule_criteria *criteria, bool can_write_readonly) { criteria->include_readonly = can_write_readonly; } static void rule_criteria_destroy(struct rule_criteria *criteria) { cls_rule_destroy(&criteria->cr); } void rule_collection_init(struct rule_collection *rules) { rules->rules = rules->stub; rules->n = 0; rules->capacity = ARRAY_SIZE(rules->stub); } void rule_collection_add(struct rule_collection *rules, struct rule *rule) { if (rules->n >= rules->capacity) { size_t old_size, new_size; old_size = rules->capacity * sizeof *rules->rules; rules->capacity *= 2; new_size = rules->capacity * sizeof *rules->rules; if (rules->rules == rules->stub) { rules->rules = xmalloc(new_size); memcpy(rules->rules, rules->stub, old_size); } else { rules->rules = xrealloc(rules->rules, new_size); } } rules->rules[rules->n++] = rule; } void rule_collection_ref(struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { size_t i; for (i = 0; i < rules->n; i++) { ofproto_rule_ref(rules->rules[i]); } } void rule_collection_unref(struct rule_collection *rules) { size_t i; for (i = 0; i < rules->n; i++) { ofproto_rule_unref(rules->rules[i]); } } /* Returns a NULL-terminated array of rule pointers, * destroys 'rules'. */ static struct rule ** rule_collection_detach(struct rule_collection *rules) { struct rule **rule_array; rule_collection_add(rules, NULL); if (rules->rules == rules->stub) { rules->rules = xmemdup(rules->rules, rules->n * sizeof *rules->rules); } rule_array = rules->rules; rule_collection_init(rules); return rule_array; } void rule_collection_destroy(struct rule_collection *rules) { if (rules->rules != rules->stub) { free(rules->rules); } /* Make repeated destruction harmless. */ rule_collection_init(rules); } /* Schedules postponed removal of rules, destroys 'rules'. */ static void rule_collection_remove_postponed(struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { if (rules->n > 0) { if (rules->n == 1) { ovsrcu_postpone(remove_rule_rcu, rules->rules[0]); } else { ovsrcu_postpone(remove_rules_rcu, rule_collection_detach(rules)); } } } /* Checks whether 'rule' matches 'c' and, if so, adds it to 'rules'. This * function verifies most of the criteria in 'c' itself, but the caller must * check 'c->cr' itself. * * Rules that have already been marked for removal are not collected. * * Increments '*n_readonly' if 'rule' wasn't added because it's read-only (and * 'c' only includes modifiable rules). */ static void collect_rule(struct rule *rule, const struct rule_criteria *c, struct rule_collection *rules, size_t *n_readonly) OVS_REQUIRES(ofproto_mutex) { if ((c->table_id == rule->table_id || c->table_id == 0xff) && ofproto_rule_has_out_port(rule, c->out_port) && ofproto_rule_has_out_group(rule, c->out_group) && !((rule->flow_cookie ^ c->cookie) & c->cookie_mask) && (!rule_is_hidden(rule) || c->include_hidden) && cls_rule_visible_in_version(&rule->cr, c->version)) { /* Rule matches all the criteria... */ if (!rule_is_readonly(rule) || c->include_readonly) { /* ...add it. */ rule_collection_add(rules, rule); } else { /* ...except it's read-only. */ ++*n_readonly; } } } /* Searches 'ofproto' for rules that match the criteria in 'criteria'. Matches * on classifiers rules are done in the "loose" way required for OpenFlow * OFPFC_MODIFY and OFPFC_DELETE requests. Puts the selected rules on list * 'rules'. * * Returns 0 on success, otherwise an OpenFlow error code. */ static enum ofperr collect_rules_loose(struct ofproto *ofproto, const struct rule_criteria *criteria, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { struct oftable *table; enum ofperr error = 0; size_t n_readonly = 0; rule_collection_init(rules); if (!check_table_id(ofproto, criteria->table_id)) { error = OFPERR_OFPBRC_BAD_TABLE_ID; goto exit; } if (criteria->cookie_mask == OVS_BE64_MAX) { struct rule *rule; HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(criteria->cookie), &ofproto->cookies) { if (cls_rule_is_loose_match(&rule->cr, &criteria->cr.match)) { collect_rule(rule, criteria, rules, &n_readonly); } } } else { FOR_EACH_MATCHING_TABLE (table, criteria->table_id, ofproto) { struct rule *rule; CLS_FOR_EACH_TARGET (rule, cr, &table->cls, &criteria->cr, criteria->version) { collect_rule(rule, criteria, rules, &n_readonly); } } } exit: if (!error && !rules->n && n_readonly) { /* We didn't find any rules to modify. We did find some read-only * rules that we're not allowed to modify, so report that. */ error = OFPERR_OFPBRC_EPERM; } if (error) { rule_collection_destroy(rules); } return error; } /* Searches 'ofproto' for rules that match the criteria in 'criteria'. Matches * on classifiers rules are done in the "strict" way required for OpenFlow * OFPFC_MODIFY_STRICT and OFPFC_DELETE_STRICT requests. Puts the selected * rules on list 'rules'. * * Returns 0 on success, otherwise an OpenFlow error code. */ static enum ofperr collect_rules_strict(struct ofproto *ofproto, const struct rule_criteria *criteria, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { struct oftable *table; size_t n_readonly = 0; enum ofperr error = 0; rule_collection_init(rules); if (!check_table_id(ofproto, criteria->table_id)) { error = OFPERR_OFPBRC_BAD_TABLE_ID; goto exit; } if (criteria->cookie_mask == OVS_BE64_MAX) { struct rule *rule; HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node, hash_cookie(criteria->cookie), &ofproto->cookies) { if (cls_rule_equal(&rule->cr, &criteria->cr)) { collect_rule(rule, criteria, rules, &n_readonly); } } } else { FOR_EACH_MATCHING_TABLE (table, criteria->table_id, ofproto) { struct rule *rule; rule = rule_from_cls_rule(classifier_find_rule_exactly( &table->cls, &criteria->cr, criteria->version)); if (rule) { collect_rule(rule, criteria, rules, &n_readonly); } } } exit: if (!error && !rules->n && n_readonly) { /* We didn't find any rules to modify. We did find some read-only * rules that we're not allowed to modify, so report that. */ error = OFPERR_OFPBRC_EPERM; } if (error) { rule_collection_destroy(rules); } return error; } /* Returns 'age_ms' (a duration in milliseconds), converted to seconds and * forced into the range of a uint16_t. */ static int age_secs(long long int age_ms) { return (age_ms < 0 ? 0 : age_ms >= UINT16_MAX * 1000 ? UINT16_MAX : (unsigned int) age_ms / 1000); } static enum ofperr handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *request) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_flow_stats_request fsr; struct rule_criteria criteria; struct rule_collection rules; struct ovs_list replies; enum ofperr error; size_t i; error = ofputil_decode_flow_stats_request(&fsr, request); if (error) { return error; } rule_criteria_init(&criteria, fsr.table_id, &fsr.match, 0, CLS_MAX_VERSION, fsr.cookie, fsr.cookie_mask, fsr.out_port, fsr.out_group); ovs_mutex_lock(&ofproto_mutex); error = collect_rules_loose(ofproto, &criteria, &rules); rule_criteria_destroy(&criteria); if (!error) { rule_collection_ref(&rules); } ovs_mutex_unlock(&ofproto_mutex); if (error) { return error; } ofpmp_init(&replies, request); for (i = 0; i < rules.n; i++) { struct rule *rule = rules.rules[i]; long long int now = time_msec(); struct ofputil_flow_stats fs; long long int created, used, modified; const struct rule_actions *actions; enum ofputil_flow_mod_flags flags; ovs_mutex_lock(&rule->mutex); fs.cookie = rule->flow_cookie; fs.idle_timeout = rule->idle_timeout; fs.hard_timeout = rule->hard_timeout; fs.importance = rule->importance; created = rule->created; modified = rule->modified; actions = rule_get_actions(rule); flags = rule->flags; ovs_mutex_unlock(&rule->mutex); ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count, &fs.byte_count, &used); minimatch_expand(&rule->cr.match, &fs.match); fs.table_id = rule->table_id; calc_duration(created, now, &fs.duration_sec, &fs.duration_nsec); fs.priority = rule->cr.priority; fs.idle_age = age_secs(now - used); fs.hard_age = age_secs(now - modified); fs.ofpacts = actions->ofpacts; fs.ofpacts_len = actions->ofpacts_len; fs.flags = flags; ofputil_append_flow_stats_reply(&fs, &replies); } rule_collection_unref(&rules); rule_collection_destroy(&rules); ofconn_send_replies(ofconn, &replies); return 0; } static void flow_stats_ds(struct rule *rule, struct ds *results) { uint64_t packet_count, byte_count; const struct rule_actions *actions; long long int created, used; rule->ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count, &used); ovs_mutex_lock(&rule->mutex); actions = rule_get_actions(rule); created = rule->created; ovs_mutex_unlock(&rule->mutex); if (rule->table_id != 0) { ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id); } ds_put_format(results, "duration=%llds, ", (time_msec() - created) / 1000); ds_put_format(results, "n_packets=%"PRIu64", ", packet_count); ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count); cls_rule_format(&rule->cr, results); ds_put_char(results, ','); ds_put_cstr(results, "actions="); ofpacts_format(actions->ofpacts, actions->ofpacts_len, results); ds_put_cstr(results, "\n"); } /* Adds a pretty-printed description of all flows to 'results', including * hidden flows (e.g., set up by in-band control). */ void ofproto_get_all_flows(struct ofproto *p, struct ds *results) { struct oftable *table; OFPROTO_FOR_EACH_TABLE (table, p) { struct rule *rule; CLS_FOR_EACH (rule, cr, &table->cls) { flow_stats_ds(rule, results); } } } /* Obtains the NetFlow engine type and engine ID for 'ofproto' into * '*engine_type' and '*engine_id', respectively. */ void ofproto_get_netflow_ids(const struct ofproto *ofproto, uint8_t *engine_type, uint8_t *engine_id) { ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id); } /* Checks the status change of CFM on 'ofport'. * * Returns true if 'ofproto_class' does not support 'cfm_status_changed'. */ bool ofproto_port_cfm_status_changed(struct ofproto *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); return (ofport && ofproto->ofproto_class->cfm_status_changed ? ofproto->ofproto_class->cfm_status_changed(ofport) : true); } /* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. * Returns 0 if the port's CFM status was successfully stored into * '*status'. Returns positive errno if the port did not have CFM * configured. * * The caller must provide and own '*status', and must free 'status->rmps'. * '*status' is indeterminate if the return value is non-zero. */ int ofproto_port_get_cfm_status(const struct ofproto *ofproto, ofp_port_t ofp_port, struct cfm_status *status) { struct ofport *ofport = ofproto_get_port(ofproto, ofp_port); return (ofport && ofproto->ofproto_class->get_cfm_status ? ofproto->ofproto_class->get_cfm_status(ofport, status) : EOPNOTSUPP); } static enum ofperr handle_aggregate_stats_request(struct ofconn *ofconn, const struct ofp_header *oh) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_flow_stats_request request; struct ofputil_aggregate_stats stats; bool unknown_packets, unknown_bytes; struct rule_criteria criteria; struct rule_collection rules; struct ofpbuf *reply; enum ofperr error; size_t i; error = ofputil_decode_flow_stats_request(&request, oh); if (error) { return error; } rule_criteria_init(&criteria, request.table_id, &request.match, 0, CLS_MAX_VERSION, request.cookie, request.cookie_mask, request.out_port, request.out_group); ovs_mutex_lock(&ofproto_mutex); error = collect_rules_loose(ofproto, &criteria, &rules); rule_criteria_destroy(&criteria); if (!error) { rule_collection_ref(&rules); } ovs_mutex_unlock(&ofproto_mutex); if (error) { return error; } memset(&stats, 0, sizeof stats); unknown_packets = unknown_bytes = false; for (i = 0; i < rules.n; i++) { struct rule *rule = rules.rules[i]; uint64_t packet_count; uint64_t byte_count; long long int used; ofproto->ofproto_class->rule_get_stats(rule, &packet_count, &byte_count, &used); if (packet_count == UINT64_MAX) { unknown_packets = true; } else { stats.packet_count += packet_count; } if (byte_count == UINT64_MAX) { unknown_bytes = true; } else { stats.byte_count += byte_count; } stats.flow_count++; } if (unknown_packets) { stats.packet_count = UINT64_MAX; } if (unknown_bytes) { stats.byte_count = UINT64_MAX; } rule_collection_unref(&rules); rule_collection_destroy(&rules); reply = ofputil_encode_aggregate_stats_reply(&stats, oh); ofconn_send_reply(ofconn, reply); return 0; } struct queue_stats_cbdata { struct ofport *ofport; struct ovs_list replies; long long int now; }; static void put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id, const struct netdev_queue_stats *stats) { struct ofputil_queue_stats oqs; oqs.port_no = cbdata->ofport->pp.port_no; oqs.queue_id = queue_id; oqs.tx_bytes = stats->tx_bytes; oqs.tx_packets = stats->tx_packets; oqs.tx_errors = stats->tx_errors; if (stats->created != LLONG_MIN) { calc_duration(stats->created, cbdata->now, &oqs.duration_sec, &oqs.duration_nsec); } else { oqs.duration_sec = oqs.duration_nsec = UINT32_MAX; } ofputil_append_queue_stat(&cbdata->replies, &oqs); } static void handle_queue_stats_dump_cb(uint32_t queue_id, struct netdev_queue_stats *stats, void *cbdata_) { struct queue_stats_cbdata *cbdata = cbdata_; put_queue_stats(cbdata, queue_id, stats); } static enum ofperr handle_queue_stats_for_port(struct ofport *port, uint32_t queue_id, struct queue_stats_cbdata *cbdata) { cbdata->ofport = port; if (queue_id == OFPQ_ALL) { netdev_dump_queue_stats(port->netdev, handle_queue_stats_dump_cb, cbdata); } else { struct netdev_queue_stats stats; if (!netdev_get_queue_stats(port->netdev, queue_id, &stats)) { put_queue_stats(cbdata, queue_id, &stats); } else { return OFPERR_OFPQOFC_BAD_QUEUE; } } return 0; } static enum ofperr handle_queue_stats_request(struct ofconn *ofconn, const struct ofp_header *rq) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct queue_stats_cbdata cbdata; struct ofport *port; enum ofperr error; struct ofputil_queue_stats_request oqsr; COVERAGE_INC(ofproto_queue_req); ofpmp_init(&cbdata.replies, rq); cbdata.now = time_msec(); error = ofputil_decode_queue_stats_request(rq, &oqsr); if (error) { return error; } if (oqsr.port_no == OFPP_ANY) { error = OFPERR_OFPQOFC_BAD_QUEUE; HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { if (!handle_queue_stats_for_port(port, oqsr.queue_id, &cbdata)) { error = 0; } } } else { port = ofproto_get_port(ofproto, oqsr.port_no); error = (port ? handle_queue_stats_for_port(port, oqsr.queue_id, &cbdata) : OFPERR_OFPQOFC_BAD_PORT); } if (!error) { ofconn_send_replies(ofconn, &cbdata.replies); } else { ofpbuf_list_delete(&cbdata.replies); } return error; } static enum ofperr evict_rules_from_table(struct oftable *table) OVS_REQUIRES(ofproto_mutex) { enum ofperr error = 0; struct rule_collection rules; unsigned int count = table->n_flows; unsigned int max_flows = table->max_flows; rule_collection_init(&rules); while (count-- > max_flows) { struct rule *rule; if (!choose_rule_to_evict(table, &rule)) { error = OFPERR_OFPFMFC_TABLE_FULL; break; } else { eviction_group_remove_rule(rule); rule_collection_add(&rules, rule); } } delete_flows__(&rules, OFPRR_EVICTION, NULL); return error; } static void get_conjunctions(const struct ofputil_flow_mod *fm, struct cls_conjunction **conjsp, size_t *n_conjsp) OVS_REQUIRES(ofproto_mutex) { struct cls_conjunction *conjs = NULL; int n_conjs = 0; const struct ofpact *ofpact; OFPACT_FOR_EACH (ofpact, fm->ofpacts, fm->ofpacts_len) { if (ofpact->type == OFPACT_CONJUNCTION) { n_conjs++; } else if (ofpact->type != OFPACT_NOTE) { /* "conjunction" may appear with "note" actions but not with any * other type of actions. */ ovs_assert(!n_conjs); break; } } if (n_conjs) { int i = 0; conjs = xzalloc(n_conjs * sizeof *conjs); OFPACT_FOR_EACH (ofpact, fm->ofpacts, fm->ofpacts_len) { if (ofpact->type == OFPACT_CONJUNCTION) { struct ofpact_conjunction *oc = ofpact_get_CONJUNCTION(ofpact); conjs[i].clause = oc->clause; conjs[i].n_clauses = oc->n_clauses; conjs[i].id = oc->id; i++; } } } *conjsp = conjs; *n_conjsp = n_conjs; } /* Implements OFPFC_ADD and the cases for OFPFC_MODIFY and OFPFC_MODIFY_STRICT * in which no matching flow already exists in the flow table. * * Adds the flow specified by 'fm', to the ofproto's flow table. Returns 0 on * success, or an OpenFlow error code on failure. * * On successful return the caller must complete the operation either by * calling add_flow_finish(), or add_flow_revert() if the operation needs to * be reverted. * * The caller retains ownership of 'fm->ofpacts'. */ static enum ofperr add_flow_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule **old_rule = &ofm->old_rules.stub[0]; struct rule **new_rule = &ofm->new_rules.stub[0]; struct oftable *table; struct cls_rule cr; struct rule *rule; uint8_t table_id; struct cls_conjunction *conjs; size_t n_conjs; enum ofperr error; if (!check_table_id(ofproto, fm->table_id)) { error = OFPERR_OFPBRC_BAD_TABLE_ID; return error; } /* Pick table. */ if (fm->table_id == 0xff) { if (ofproto->ofproto_class->rule_choose_table) { error = ofproto->ofproto_class->rule_choose_table(ofproto, &fm->match, &table_id); if (error) { return error; } ovs_assert(table_id < ofproto->n_tables); } else { table_id = 0; } } else if (fm->table_id < ofproto->n_tables) { table_id = fm->table_id; } else { return OFPERR_OFPBRC_BAD_TABLE_ID; } table = &ofproto->tables[table_id]; if (table->flags & OFTABLE_READONLY && !(fm->flags & OFPUTIL_FF_NO_READONLY)) { return OFPERR_OFPBRC_EPERM; } if (!(fm->flags & OFPUTIL_FF_HIDDEN_FIELDS) && !match_has_default_hidden_fields(&fm->match)) { VLOG_WARN_RL(&rl, "%s: (add_flow) only internal flows can set " "non-default values to hidden fields", ofproto->name); return OFPERR_OFPBRC_EPERM; } cls_rule_init(&cr, &fm->match, fm->priority); /* Check for the existence of an identical rule. * This will not return rules earlier marked for removal. */ rule = rule_from_cls_rule(classifier_find_rule_exactly(&table->cls, &cr, ofm->version)); *old_rule = rule; if (!rule) { /* Check for overlap, if requested. */ if (fm->flags & OFPUTIL_FF_CHECK_OVERLAP && classifier_rule_overlaps(&table->cls, &cr, ofm->version)) { cls_rule_destroy(&cr); return OFPERR_OFPFMFC_OVERLAP; } /* If necessary, evict an existing rule to clear out space. */ if (table->n_flows >= table->max_flows) { if (!choose_rule_to_evict(table, &rule)) { error = OFPERR_OFPFMFC_TABLE_FULL; cls_rule_destroy(&cr); return error; } eviction_group_remove_rule(rule); /* Marks '*old_rule' as an evicted rule rather than replaced rule. */ fm->delete_reason = OFPRR_EVICTION; *old_rule = rule; } } else { fm->modify_cookie = true; } /* Allocate new rule. */ error = replace_rule_create(ofproto, fm, &cr, table - ofproto->tables, rule, new_rule); if (error) { return error; } get_conjunctions(fm, &conjs, &n_conjs); replace_rule_start(ofproto, ofm->version, rule, *new_rule, conjs, n_conjs); free(conjs); return 0; } /* Revert the effects of add_flow_start(). */ static void add_flow_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule *old_rule = ofm->old_rules.stub[0]; struct rule *new_rule = ofm->new_rules.stub[0]; if (old_rule && fm->delete_reason == OFPRR_EVICTION) { /* Revert the eviction. */ eviction_group_add_rule(old_rule); } replace_rule_revert(ofproto, old_rule, new_rule); } /* To be called after version bump. */ static void add_flow_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule *old_rule = ofm->old_rules.stub[0]; struct rule *new_rule = ofm->new_rules.stub[0]; struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies); replace_rule_finish(ofproto, fm, req, old_rule, new_rule, &dead_cookies); learned_cookies_flush(ofproto, &dead_cookies); if (old_rule) { ovsrcu_postpone(remove_rule_rcu, old_rule); } else { if (minimask_get_vid_mask(new_rule->cr.match.mask) == VLAN_VID_MASK) { if (ofproto->vlan_bitmap) { uint16_t vid = miniflow_get_vid(new_rule->cr.match.flow); if (!bitmap_is_set(ofproto->vlan_bitmap, vid)) { bitmap_set1(ofproto->vlan_bitmap, vid); ofproto->vlans_changed = true; } } else { ofproto->vlans_changed = true; } } ofmonitor_report(ofproto->connmgr, new_rule, NXFME_ADDED, 0, req ? req->ofconn : NULL, req ? req->request->xid : 0, NULL); } send_buffered_packet(req, fm->buffer_id, new_rule); } /* OFPFC_MODIFY and OFPFC_MODIFY_STRICT. */ /* Create a new rule based on attributes in 'fm', match in 'cr', 'table_id', * and 'old_rule'. Note that the rule is NOT inserted into a any data * structures yet. Takes ownership of 'cr'. */ static enum ofperr replace_rule_create(struct ofproto *ofproto, struct ofputil_flow_mod *fm, struct cls_rule *cr, uint8_t table_id, struct rule *old_rule, struct rule **new_rule) { struct rule *rule; enum ofperr error; /* Allocate new rule. */ rule = ofproto->ofproto_class->rule_alloc(); if (!rule) { cls_rule_destroy(cr); VLOG_WARN_RL(&rl, "%s: failed to allocate a rule.", ofproto->name); return OFPERR_OFPFMFC_UNKNOWN; } /* Initialize base state. */ *CONST_CAST(struct ofproto **, &rule->ofproto) = ofproto; cls_rule_move(CONST_CAST(struct cls_rule *, &rule->cr), cr); ovs_refcount_init(&rule->ref_count); rule->flow_cookie = fm->new_cookie; rule->created = rule->modified = time_msec(); ovs_mutex_init(&rule->mutex); ovs_mutex_lock(&rule->mutex); rule->idle_timeout = fm->idle_timeout; rule->hard_timeout = fm->hard_timeout; *CONST_CAST(uint16_t *, &rule->importance) = fm->importance; rule->removed_reason = OVS_OFPRR_NONE; *CONST_CAST(uint8_t *, &rule->table_id) = table_id; rule->flags = fm->flags & OFPUTIL_FF_STATE; *CONST_CAST(const struct rule_actions **, &rule->actions) = rule_actions_create(fm->ofpacts, fm->ofpacts_len); list_init(&rule->meter_list_node); rule->eviction_group = NULL; list_init(&rule->expirable); rule->monitor_flags = 0; rule->add_seqno = 0; rule->modify_seqno = 0; /* Copy values from old rule for modify semantics. */ if (old_rule && fm->delete_reason != OFPRR_EVICTION) { bool change_cookie = (fm->modify_cookie && fm->new_cookie != OVS_BE64_MAX && fm->new_cookie != old_rule->flow_cookie); ovs_mutex_lock(&old_rule->mutex); if (fm->command != OFPFC_ADD) { rule->idle_timeout = old_rule->idle_timeout; rule->hard_timeout = old_rule->hard_timeout; *CONST_CAST(uint16_t *, &rule->importance) = old_rule->importance; rule->flags = old_rule->flags; rule->created = old_rule->created; } if (!change_cookie) { rule->flow_cookie = old_rule->flow_cookie; } ovs_mutex_unlock(&old_rule->mutex); } ovs_mutex_unlock(&rule->mutex); /* Construct rule, initializing derived state. */ error = ofproto->ofproto_class->rule_construct(rule); if (error) { ofproto_rule_destroy__(rule); return error; } rule->removed = true; /* Not yet in ofproto data structures. */ *new_rule = rule; return 0; } static void replace_rule_start(struct ofproto *ofproto, cls_version_t version, struct rule *old_rule, struct rule *new_rule, struct cls_conjunction *conjs, size_t n_conjs) { struct oftable *table = &ofproto->tables[new_rule->table_id]; /* 'old_rule' may be either an evicted rule or replaced rule. */ if (old_rule) { /* Mark the old rule for removal in the next version. */ cls_rule_make_invisible_in_version(&old_rule->cr, version); } else { table->n_flows++; } /* Insert flow to the classifier, so that later flow_mods may relate * to it. This is reversible, in case later errors require this to * be reverted. */ ofproto_rule_insert__(ofproto, new_rule); /* Make the new rule visible for classifier lookups only from the next * version. */ classifier_insert(&table->cls, &new_rule->cr, version, conjs, n_conjs); } static void replace_rule_revert(struct ofproto *ofproto, struct rule *old_rule, struct rule *new_rule) { struct oftable *table = &ofproto->tables[new_rule->table_id]; if (old_rule) { /* Restore the original visibility of the old rule. */ cls_rule_restore_visibility(&old_rule->cr); } else { /* Restore table's rule count. */ table->n_flows--; } /* Remove the new rule immediately. It was never visible to lookups. */ if (!classifier_remove(&table->cls, &new_rule->cr)) { OVS_NOT_REACHED(); } ofproto_rule_remove__(ofproto, new_rule); /* The rule was not inserted to the ofproto provider, so we can * release it without deleting it from the ofproto provider. */ ofproto_rule_unref(new_rule); } /* Adds the 'new_rule', replacing the 'old_rule'. */ static void replace_rule_finish(struct ofproto *ofproto, struct ofputil_flow_mod *fm, const struct flow_mod_requester *req, struct rule *old_rule, struct rule *new_rule, struct ovs_list *dead_cookies) OVS_REQUIRES(ofproto_mutex) { bool forward_counts = !(fm->flags & OFPUTIL_FF_RESET_COUNTS); struct rule *replaced_rule; replaced_rule = fm->delete_reason != OFPRR_EVICTION ? old_rule : NULL; /* Insert the new flow to the ofproto provider. A non-NULL 'replaced_rule' * is a duplicate rule the 'new_rule' is replacing. The provider should * link the packet and byte counts from the old rule to the new one if * 'forward_counts' is 'true'. The 'replaced_rule' will be deleted right * after this call. */ ofproto->ofproto_class->rule_insert(new_rule, replaced_rule, forward_counts); learned_cookies_inc(ofproto, rule_get_actions(new_rule)); if (old_rule) { const struct rule_actions *old_actions = rule_get_actions(old_rule); /* Remove the old rule from data structures. Removal from the * classifier and the deletion of the rule is RCU postponed by the * caller. */ ofproto_rule_remove__(ofproto, old_rule); learned_cookies_dec(ofproto, old_actions, dead_cookies); if (replaced_rule) { enum nx_flow_update_event event = fm->command == OFPFC_ADD ? NXFME_ADDED : NXFME_MODIFIED; bool change_cookie = (fm->modify_cookie && fm->new_cookie != OVS_BE64_MAX && fm->new_cookie != old_rule->flow_cookie); bool change_actions = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len, old_actions->ofpacts, old_actions->ofpacts_len); if (event != NXFME_MODIFIED || change_actions || change_cookie) { ofmonitor_report(ofproto->connmgr, new_rule, event, 0, req ? req->ofconn : NULL, req ? req->request->xid : 0, change_actions ? old_actions : NULL); } } else { /* XXX: This is slight duplication with delete_flows_finish__() */ old_rule->removed_reason = OFPRR_EVICTION; ofmonitor_report(ofproto->connmgr, old_rule, NXFME_DELETED, OFPRR_EVICTION, req ? req->ofconn : NULL, req ? req->request->xid : 0, NULL); } } } static enum ofperr modify_flows_start__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; struct rule_collection *new_rules = &ofm->new_rules; enum ofperr error; rule_collection_init(new_rules); if (old_rules->n > 0) { struct cls_conjunction *conjs; size_t n_conjs; size_t i; /* Create a new 'modified' rule for each old rule. */ for (i = 0; i < old_rules->n; i++) { struct rule *old_rule = old_rules->rules[i]; struct rule *new_rule; struct cls_rule cr; cls_rule_clone(&cr, &old_rule->cr); error = replace_rule_create(ofproto, fm, &cr, old_rule->table_id, old_rule, &new_rule); if (!error) { rule_collection_add(new_rules, new_rule); } else { rule_collection_unref(new_rules); rule_collection_destroy(new_rules); return error; } } ovs_assert(new_rules->n == old_rules->n); get_conjunctions(fm, &conjs, &n_conjs); for (i = 0; i < old_rules->n; i++) { replace_rule_start(ofproto, ofm->version, old_rules->rules[i], new_rules->rules[i], conjs, n_conjs); } free(conjs); } else if (!(fm->cookie_mask != htonll(0) || fm->new_cookie == OVS_BE64_MAX)) { /* No match, add a new flow. */ error = add_flow_start(ofproto, ofm); if (!error) { ovs_assert(fm->delete_reason == OFPRR_EVICTION || !old_rules->rules[0]); } new_rules->n = 1; } else { error = 0; } return error; } /* Implements OFPFC_MODIFY. Returns 0 on success or an OpenFlow error code on * failure. * * 'ofconn' is used to retrieve the packet buffer specified in fm->buffer_id, * if any. */ static enum ofperr modify_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; struct rule_criteria criteria; enum ofperr error; rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, CLS_MAX_VERSION, fm->cookie, fm->cookie_mask, OFPP_ANY, OFPG_ANY); rule_criteria_require_rw(&criteria, (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); error = collect_rules_loose(ofproto, &criteria, old_rules); rule_criteria_destroy(&criteria); if (!error) { error = modify_flows_start__(ofproto, ofm); } if (error) { rule_collection_destroy(old_rules); } return error; } static void modify_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct rule_collection *old_rules = &ofm->old_rules; struct rule_collection *new_rules = &ofm->new_rules; /* Old rules were not changed yet, only need to revert new rules. */ if (old_rules->n == 0 && new_rules->n == 1) { add_flow_revert(ofproto, ofm); } else if (old_rules->n > 0) { for (size_t i = 0; i < old_rules->n; i++) { replace_rule_revert(ofproto, old_rules->rules[i], new_rules->rules[i]); } rule_collection_destroy(new_rules); rule_collection_destroy(old_rules); } } static void modify_flows_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; struct rule_collection *new_rules = &ofm->new_rules; if (old_rules->n == 0 && new_rules->n == 1) { add_flow_finish(ofproto, ofm, req); } else if (old_rules->n > 0) { struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies); ovs_assert(new_rules->n == old_rules->n); for (size_t i = 0; i < old_rules->n; i++) { replace_rule_finish(ofproto, fm, req, old_rules->rules[i], new_rules->rules[i], &dead_cookies); } learned_cookies_flush(ofproto, &dead_cookies); rule_collection_remove_postponed(old_rules); send_buffered_packet(req, fm->buffer_id, new_rules->rules[0]); rule_collection_destroy(new_rules); } } /* Implements OFPFC_MODIFY_STRICT. Returns 0 on success or an OpenFlow error * code on failure. */ static enum ofperr modify_flow_start_strict(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *old_rules = &ofm->old_rules; struct rule_criteria criteria; enum ofperr error; rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority, CLS_MAX_VERSION, fm->cookie, fm->cookie_mask, OFPP_ANY, OFPG_ANY); rule_criteria_require_rw(&criteria, (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); error = collect_rules_strict(ofproto, &criteria, old_rules); rule_criteria_destroy(&criteria); if (!error) { /* collect_rules_strict() can return max 1 rule. */ error = modify_flows_start__(ofproto, ofm); } if (error) { rule_collection_destroy(old_rules); } return error; } /* OFPFC_DELETE implementation. */ static void delete_flows_start__(struct ofproto *ofproto, cls_version_t version, const struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { for (size_t i = 0; i < rules->n; i++) { struct rule *rule = rules->rules[i]; struct oftable *table = &ofproto->tables[rule->table_id]; table->n_flows--; cls_rule_make_invisible_in_version(&rule->cr, version); } } static void delete_flows_finish__(struct ofproto *ofproto, struct rule_collection *rules, enum ofp_flow_removed_reason reason, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { if (rules->n) { struct ovs_list dead_cookies = OVS_LIST_INITIALIZER(&dead_cookies); for (size_t i = 0; i < rules->n; i++) { struct rule *rule = rules->rules[i]; /* This value will be used to send the flow removed message right * before the rule is actually destroyed. */ rule->removed_reason = reason; ofmonitor_report(ofproto->connmgr, rule, NXFME_DELETED, reason, req ? req->ofconn : NULL, req ? req->request->xid : 0, NULL); ofproto_rule_remove__(ofproto, rule); learned_cookies_dec(ofproto, rule_get_actions(rule), &dead_cookies); } rule_collection_remove_postponed(rules); learned_cookies_flush(ofproto, &dead_cookies); } } /* Deletes the rules listed in 'rules'. * The deleted rules will become invisible to the lookups in the next version. * Destroys 'rules'. */ static void delete_flows__(struct rule_collection *rules, enum ofp_flow_removed_reason reason, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { if (rules->n) { struct ofproto *ofproto = rules->rules[0]->ofproto; delete_flows_start__(ofproto, ofproto->tables_version + 1, rules); ofproto_bump_tables_version(ofproto); delete_flows_finish__(ofproto, rules, reason, req); ofmonitor_flush(ofproto->connmgr); } } /* Implements OFPFC_DELETE. */ static enum ofperr delete_flows_start_loose(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { const struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *rules = &ofm->old_rules; struct rule_criteria criteria; enum ofperr error; rule_criteria_init(&criteria, fm->table_id, &fm->match, 0, CLS_MAX_VERSION, fm->cookie, fm->cookie_mask, fm->out_port, fm->out_group); rule_criteria_require_rw(&criteria, (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); error = collect_rules_loose(ofproto, &criteria, rules); rule_criteria_destroy(&criteria); if (!error) { delete_flows_start__(ofproto, ofm->version, rules); } return error; } static void delete_flows_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { struct rule_collection *rules = &ofm->old_rules; for (size_t i = 0; i < rules->n; i++) { struct rule *rule = rules->rules[i]; struct oftable *table = &ofproto->tables[rule->table_id]; /* Restore table's rule count. */ table->n_flows++; /* Restore the original visibility of the rule. */ cls_rule_restore_visibility(&rule->cr); } rule_collection_destroy(rules); } static void delete_flows_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { delete_flows_finish__(ofproto, &ofm->old_rules, ofm->fm.delete_reason, req); } /* Implements OFPFC_DELETE_STRICT. */ static enum ofperr delete_flow_start_strict(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { const struct ofputil_flow_mod *fm = &ofm->fm; struct rule_collection *rules = &ofm->old_rules; struct rule_criteria criteria; enum ofperr error; rule_criteria_init(&criteria, fm->table_id, &fm->match, fm->priority, CLS_MAX_VERSION, fm->cookie, fm->cookie_mask, fm->out_port, fm->out_group); rule_criteria_require_rw(&criteria, (fm->flags & OFPUTIL_FF_NO_READONLY) != 0); error = collect_rules_strict(ofproto, &criteria, rules); rule_criteria_destroy(&criteria); if (!error) { delete_flows_start__(ofproto, ofm->version, rules); } return error; } /* This may only be called by rule_destroy_cb()! */ static void ofproto_rule_send_removed(struct rule *rule) OVS_EXCLUDED(ofproto_mutex) { struct ofputil_flow_removed fr; long long int used; minimatch_expand(&rule->cr.match, &fr.match); fr.priority = rule->cr.priority; ovs_mutex_lock(&ofproto_mutex); fr.cookie = rule->flow_cookie; fr.reason = rule->removed_reason; fr.table_id = rule->table_id; calc_duration(rule->created, time_msec(), &fr.duration_sec, &fr.duration_nsec); ovs_mutex_lock(&rule->mutex); fr.idle_timeout = rule->idle_timeout; fr.hard_timeout = rule->hard_timeout; ovs_mutex_unlock(&rule->mutex); rule->ofproto->ofproto_class->rule_get_stats(rule, &fr.packet_count, &fr.byte_count, &used); connmgr_send_flow_removed(rule->ofproto->connmgr, &fr); ovs_mutex_unlock(&ofproto_mutex); } /* Sends an OpenFlow "flow removed" message with the given 'reason' (either * OFPRR_HARD_TIMEOUT or OFPRR_IDLE_TIMEOUT), and then removes 'rule' from its * ofproto. * * ofproto implementation ->run() functions should use this function to expire * OpenFlow flows. */ void ofproto_rule_expire(struct rule *rule, uint8_t reason) OVS_REQUIRES(ofproto_mutex) { struct rule_collection rules; rules.rules = rules.stub; rules.n = 1; rules.stub[0] = rule; delete_flows__(&rules, reason, NULL); } /* Reduces '*timeout' to no more than 'max'. A value of zero in either case * means "infinite". */ static void reduce_timeout(uint16_t max, uint16_t *timeout) { if (max && (!*timeout || *timeout > max)) { *timeout = max; } } /* If 'idle_timeout' is nonzero, and 'rule' has no idle timeout or an idle * timeout greater than 'idle_timeout', lowers 'rule''s idle timeout to * 'idle_timeout' seconds. Similarly for 'hard_timeout'. * * Suitable for implementing OFPACT_FIN_TIMEOUT. */ void ofproto_rule_reduce_timeouts(struct rule *rule, uint16_t idle_timeout, uint16_t hard_timeout) OVS_EXCLUDED(ofproto_mutex, rule->mutex) { if (!idle_timeout && !hard_timeout) { return; } ovs_mutex_lock(&ofproto_mutex); if (list_is_empty(&rule->expirable)) { list_insert(&rule->ofproto->expirable, &rule->expirable); } ovs_mutex_unlock(&ofproto_mutex); ovs_mutex_lock(&rule->mutex); reduce_timeout(idle_timeout, &rule->idle_timeout); reduce_timeout(hard_timeout, &rule->hard_timeout); ovs_mutex_unlock(&rule->mutex); } static enum ofperr handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofproto_flow_mod ofm; uint64_t ofpacts_stub[1024 / 8]; struct ofpbuf ofpacts; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { goto exit; } ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); error = ofputil_decode_flow_mod(&ofm.fm, oh, ofconn_get_protocol(ofconn), &ofpacts, u16_to_ofp(ofproto->max_ports), ofproto->n_tables); if (!error) { error = ofproto_check_ofpacts(ofproto, ofm.fm.ofpacts, ofm.fm.ofpacts_len); } if (!error) { struct flow_mod_requester req; req.ofconn = ofconn; req.request = oh; error = handle_flow_mod__(ofproto, &ofm, &req); } if (error) { goto exit_free_ofpacts; } ofconn_report_flow_mod(ofconn, ofm.fm.command); exit_free_ofpacts: ofpbuf_uninit(&ofpacts); exit: return error; } static enum ofperr handle_flow_mod__(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req) OVS_EXCLUDED(ofproto_mutex) { enum ofperr error; ovs_mutex_lock(&ofproto_mutex); ofm->version = ofproto->tables_version + 1; error = ofproto_flow_mod_start(ofproto, ofm); if (!error) { ofproto_bump_tables_version(ofproto); ofproto_flow_mod_finish(ofproto, ofm, req); } ofmonitor_flush(ofproto->connmgr); ovs_mutex_unlock(&ofproto_mutex); run_rule_executes(ofproto); return error; } static enum ofperr handle_role_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofputil_role_request request; struct ofputil_role_request reply; struct ofpbuf *buf; enum ofperr error; error = ofputil_decode_role_message(oh, &request); if (error) { return error; } if (request.role != OFPCR12_ROLE_NOCHANGE) { if (request.role != OFPCR12_ROLE_EQUAL && request.have_generation_id && !ofconn_set_master_election_id(ofconn, request.generation_id)) { return OFPERR_OFPRRFC_STALE; } ofconn_set_role(ofconn, request.role); } reply.role = ofconn_get_role(ofconn); reply.have_generation_id = ofconn_get_master_election_id( ofconn, &reply.generation_id); buf = ofputil_encode_role_reply(oh, &reply); ofconn_send_reply(ofconn, buf); return 0; } static enum ofperr handle_nxt_flow_mod_table_id(struct ofconn *ofconn, const struct ofp_header *oh) { const struct nx_flow_mod_table_id *msg = ofpmsg_body(oh); enum ofputil_protocol cur, next; cur = ofconn_get_protocol(ofconn); next = ofputil_protocol_set_tid(cur, msg->set != 0); ofconn_set_protocol(ofconn, next); return 0; } static enum ofperr handle_nxt_set_flow_format(struct ofconn *ofconn, const struct ofp_header *oh) { const struct nx_set_flow_format *msg = ofpmsg_body(oh); enum ofputil_protocol cur, next; enum ofputil_protocol next_base; next_base = ofputil_nx_flow_format_to_protocol(ntohl(msg->format)); if (!next_base) { return OFPERR_OFPBRC_EPERM; } cur = ofconn_get_protocol(ofconn); next = ofputil_protocol_set_base(cur, next_base); ofconn_set_protocol(ofconn, next); return 0; } static enum ofperr handle_nxt_set_packet_in_format(struct ofconn *ofconn, const struct ofp_header *oh) { const struct nx_set_packet_in_format *msg = ofpmsg_body(oh); uint32_t format; format = ntohl(msg->format); if (format != NXPIF_OPENFLOW10 && format != NXPIF_NXM) { return OFPERR_OFPBRC_EPERM; } ofconn_set_packet_in_format(ofconn, format); return 0; } static enum ofperr handle_nxt_set_async_config(struct ofconn *ofconn, const struct ofp_header *oh) { enum ofperr error; uint32_t master[OAM_N_TYPES] = {0}; uint32_t slave[OAM_N_TYPES] = {0}; error = ofputil_decode_set_async_config(oh, master, slave, false); if (error) { return error; } ofconn_set_async_config(ofconn, master, slave); if (ofconn_get_type(ofconn) == OFCONN_SERVICE && !ofconn_get_miss_send_len(ofconn)) { ofconn_set_miss_send_len(ofconn, OFP_DEFAULT_MISS_SEND_LEN); } return 0; } static enum ofperr handle_nxt_get_async_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofpbuf *buf; uint32_t master[OAM_N_TYPES]; uint32_t slave[OAM_N_TYPES]; ofconn_get_async_config(ofconn, master, slave); buf = ofputil_encode_get_async_config(oh, master, slave); ofconn_send_reply(ofconn, buf); return 0; } static enum ofperr handle_nxt_set_controller_id(struct ofconn *ofconn, const struct ofp_header *oh) { const struct nx_controller_id *nci = ofpmsg_body(oh); if (!is_all_zeros(nci->zero, sizeof nci->zero)) { return OFPERR_NXBRC_MUST_BE_ZERO; } ofconn_set_controller_id(ofconn, ntohs(nci->controller_id)); return 0; } static enum ofperr handle_barrier_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofpbuf *buf; buf = ofpraw_alloc_reply((oh->version == OFP10_VERSION ? OFPRAW_OFPT10_BARRIER_REPLY : OFPRAW_OFPT11_BARRIER_REPLY), oh, 0); ofconn_send_reply(ofconn, buf); return 0; } static void ofproto_compose_flow_refresh_update(const struct rule *rule, enum nx_flow_monitor_flags flags, struct ovs_list *msgs) OVS_REQUIRES(ofproto_mutex) { const struct rule_actions *actions; struct ofputil_flow_update fu; struct match match; fu.event = (flags & (NXFMF_INITIAL | NXFMF_ADD) ? NXFME_ADDED : NXFME_MODIFIED); fu.reason = 0; ovs_mutex_lock(&rule->mutex); fu.idle_timeout = rule->idle_timeout; fu.hard_timeout = rule->hard_timeout; ovs_mutex_unlock(&rule->mutex); fu.table_id = rule->table_id; fu.cookie = rule->flow_cookie; minimatch_expand(&rule->cr.match, &match); fu.match = &match; fu.priority = rule->cr.priority; actions = flags & NXFMF_ACTIONS ? rule_get_actions(rule) : NULL; fu.ofpacts = actions ? actions->ofpacts : NULL; fu.ofpacts_len = actions ? actions->ofpacts_len : 0; if (list_is_empty(msgs)) { ofputil_start_flow_update(msgs); } ofputil_append_flow_update(&fu, msgs); } void ofmonitor_compose_refresh_updates(struct rule_collection *rules, struct ovs_list *msgs) OVS_REQUIRES(ofproto_mutex) { size_t i; for (i = 0; i < rules->n; i++) { struct rule *rule = rules->rules[i]; enum nx_flow_monitor_flags flags = rule->monitor_flags; rule->monitor_flags = 0; ofproto_compose_flow_refresh_update(rule, flags, msgs); } } static void ofproto_collect_ofmonitor_refresh_rule(const struct ofmonitor *m, struct rule *rule, uint64_t seqno, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { enum nx_flow_monitor_flags update; if (rule_is_hidden(rule)) { return; } if (!ofproto_rule_has_out_port(rule, m->out_port)) { return; } if (seqno) { if (rule->add_seqno > seqno) { update = NXFMF_ADD | NXFMF_MODIFY; } else if (rule->modify_seqno > seqno) { update = NXFMF_MODIFY; } else { return; } if (!(m->flags & update)) { return; } } else { update = NXFMF_INITIAL; } if (!rule->monitor_flags) { rule_collection_add(rules, rule); } rule->monitor_flags |= update | (m->flags & NXFMF_ACTIONS); } static void ofproto_collect_ofmonitor_refresh_rules(const struct ofmonitor *m, uint64_t seqno, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { const struct ofproto *ofproto = ofconn_get_ofproto(m->ofconn); const struct oftable *table; struct cls_rule target; cls_rule_init_from_minimatch(&target, &m->match, 0); FOR_EACH_MATCHING_TABLE (table, m->table_id, ofproto) { struct rule *rule; CLS_FOR_EACH_TARGET (rule, cr, &table->cls, &target, CLS_MAX_VERSION) { ofproto_collect_ofmonitor_refresh_rule(m, rule, seqno, rules); } } cls_rule_destroy(&target); } static void ofproto_collect_ofmonitor_initial_rules(struct ofmonitor *m, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { if (m->flags & NXFMF_INITIAL) { ofproto_collect_ofmonitor_refresh_rules(m, 0, rules); } } void ofmonitor_collect_resume_rules(struct ofmonitor *m, uint64_t seqno, struct rule_collection *rules) OVS_REQUIRES(ofproto_mutex) { ofproto_collect_ofmonitor_refresh_rules(m, seqno, rules); } static enum ofperr flow_monitor_delete(struct ofconn *ofconn, uint32_t id) OVS_REQUIRES(ofproto_mutex) { struct ofmonitor *m; enum ofperr error; m = ofmonitor_lookup(ofconn, id); if (m) { ofmonitor_destroy(m); error = 0; } else { error = OFPERR_OFPMOFC_UNKNOWN_MONITOR; } return error; } static enum ofperr handle_flow_monitor_request(struct ofconn *ofconn, const struct ofp_header *oh) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofmonitor **monitors; size_t n_monitors, allocated_monitors; struct rule_collection rules; struct ovs_list replies; enum ofperr error; struct ofpbuf b; size_t i; ofpbuf_use_const(&b, oh, ntohs(oh->length)); monitors = NULL; n_monitors = allocated_monitors = 0; ovs_mutex_lock(&ofproto_mutex); for (;;) { struct ofputil_flow_monitor_request request; struct ofmonitor *m; int retval; retval = ofputil_decode_flow_monitor_request(&request, &b); if (retval == EOF) { break; } else if (retval) { error = retval; goto error; } if (request.table_id != 0xff && request.table_id >= ofproto->n_tables) { error = OFPERR_OFPBRC_BAD_TABLE_ID; goto error; } error = ofmonitor_create(&request, ofconn, &m); if (error) { goto error; } if (n_monitors >= allocated_monitors) { monitors = x2nrealloc(monitors, &allocated_monitors, sizeof *monitors); } monitors[n_monitors++] = m; } rule_collection_init(&rules); for (i = 0; i < n_monitors; i++) { ofproto_collect_ofmonitor_initial_rules(monitors[i], &rules); } ofpmp_init(&replies, oh); ofmonitor_compose_refresh_updates(&rules, &replies); ovs_mutex_unlock(&ofproto_mutex); rule_collection_destroy(&rules); ofconn_send_replies(ofconn, &replies); free(monitors); return 0; error: for (i = 0; i < n_monitors; i++) { ofmonitor_destroy(monitors[i]); } free(monitors); ovs_mutex_unlock(&ofproto_mutex); return error; } static enum ofperr handle_flow_monitor_cancel(struct ofconn *ofconn, const struct ofp_header *oh) OVS_EXCLUDED(ofproto_mutex) { enum ofperr error; uint32_t id; id = ofputil_decode_flow_monitor_cancel(oh); ovs_mutex_lock(&ofproto_mutex); error = flow_monitor_delete(ofconn, id); ovs_mutex_unlock(&ofproto_mutex); return error; } /* Meters implementation. * * Meter table entry, indexed by the OpenFlow meter_id. * 'created' is used to compute the duration for meter stats. * 'list rules' is needed so that we can delete the dependent rules when the * meter table entry is deleted. * 'provider_meter_id' is for the provider's private use. */ struct meter { long long int created; /* Time created. */ struct ovs_list rules; /* List of "struct rule_dpif"s. */ ofproto_meter_id provider_meter_id; uint16_t flags; /* Meter flags. */ uint16_t n_bands; /* Number of meter bands. */ struct ofputil_meter_band *bands; }; /* * This is used in instruction validation at flow set-up time, * as flows may not use non-existing meters. * Return value of UINT32_MAX signifies an invalid meter. */ static uint32_t get_provider_meter_id(const struct ofproto *ofproto, uint32_t of_meter_id) { if (of_meter_id && of_meter_id <= ofproto->meter_features.max_meters) { const struct meter *meter = ofproto->meters[of_meter_id]; if (meter) { return meter->provider_meter_id.uint32; } } return UINT32_MAX; } /* Finds the meter invoked by 'rule''s actions and adds 'rule' to the meter's * list of rules. */ static void meter_insert_rule(struct rule *rule) { const struct rule_actions *a = rule_get_actions(rule); uint32_t meter_id = ofpacts_get_meter(a->ofpacts, a->ofpacts_len); struct meter *meter = rule->ofproto->meters[meter_id]; list_insert(&meter->rules, &rule->meter_list_node); } static void meter_update(struct meter *meter, const struct ofputil_meter_config *config) { free(meter->bands); meter->flags = config->flags; meter->n_bands = config->n_bands; meter->bands = xmemdup(config->bands, config->n_bands * sizeof *meter->bands); } static struct meter * meter_create(const struct ofputil_meter_config *config, ofproto_meter_id provider_meter_id) { struct meter *meter; meter = xzalloc(sizeof *meter); meter->provider_meter_id = provider_meter_id; meter->created = time_msec(); list_init(&meter->rules); meter_update(meter, config); return meter; } static void meter_delete(struct ofproto *ofproto, uint32_t first, uint32_t last) OVS_REQUIRES(ofproto_mutex) { uint32_t mid; for (mid = first; mid <= last; ++mid) { struct meter *meter = ofproto->meters[mid]; if (meter) { ofproto->meters[mid] = NULL; ofproto->ofproto_class->meter_del(ofproto, meter->provider_meter_id); free(meter->bands); free(meter); } } } static enum ofperr handle_add_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm) { ofproto_meter_id provider_meter_id = { UINT32_MAX }; struct meter **meterp = &ofproto->meters[mm->meter.meter_id]; enum ofperr error; if (*meterp) { return OFPERR_OFPMMFC_METER_EXISTS; } error = ofproto->ofproto_class->meter_set(ofproto, &provider_meter_id, &mm->meter); if (!error) { ovs_assert(provider_meter_id.uint32 != UINT32_MAX); *meterp = meter_create(&mm->meter, provider_meter_id); } return error; } static enum ofperr handle_modify_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm) { struct meter *meter = ofproto->meters[mm->meter.meter_id]; enum ofperr error; uint32_t provider_meter_id; if (!meter) { return OFPERR_OFPMMFC_UNKNOWN_METER; } provider_meter_id = meter->provider_meter_id.uint32; error = ofproto->ofproto_class->meter_set(ofproto, &meter->provider_meter_id, &mm->meter); ovs_assert(meter->provider_meter_id.uint32 == provider_meter_id); if (!error) { meter_update(meter, &mm->meter); } return error; } static enum ofperr handle_delete_meter(struct ofconn *ofconn, struct ofputil_meter_mod *mm) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); uint32_t meter_id = mm->meter.meter_id; struct rule_collection rules; enum ofperr error = 0; uint32_t first, last; if (meter_id == OFPM13_ALL) { first = 1; last = ofproto->meter_features.max_meters; } else { if (!meter_id || meter_id > ofproto->meter_features.max_meters) { return 0; } first = last = meter_id; } /* First delete the rules that use this meter. If any of those rules are * currently being modified, postpone the whole operation until later. */ rule_collection_init(&rules); ovs_mutex_lock(&ofproto_mutex); for (meter_id = first; meter_id <= last; ++meter_id) { struct meter *meter = ofproto->meters[meter_id]; if (meter && !list_is_empty(&meter->rules)) { struct rule *rule; LIST_FOR_EACH (rule, meter_list_node, &meter->rules) { rule_collection_add(&rules, rule); } } } delete_flows__(&rules, OFPRR_METER_DELETE, NULL); /* Delete the meters. */ meter_delete(ofproto, first, last); ovs_mutex_unlock(&ofproto_mutex); return error; } static enum ofperr handle_meter_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_meter_mod mm; uint64_t bands_stub[256 / 8]; struct ofpbuf bands; uint32_t meter_id; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub); error = ofputil_decode_meter_mod(oh, &mm, &bands); if (error) { goto exit_free_bands; } meter_id = mm.meter.meter_id; if (mm.command != OFPMC13_DELETE) { /* Fails also when meters are not implemented by the provider. */ if (meter_id == 0 || meter_id > OFPM13_MAX) { error = OFPERR_OFPMMFC_INVALID_METER; goto exit_free_bands; } else if (meter_id > ofproto->meter_features.max_meters) { error = OFPERR_OFPMMFC_OUT_OF_METERS; goto exit_free_bands; } if (mm.meter.n_bands > ofproto->meter_features.max_bands) { error = OFPERR_OFPMMFC_OUT_OF_BANDS; goto exit_free_bands; } } switch (mm.command) { case OFPMC13_ADD: error = handle_add_meter(ofproto, &mm); break; case OFPMC13_MODIFY: error = handle_modify_meter(ofproto, &mm); break; case OFPMC13_DELETE: error = handle_delete_meter(ofconn, &mm); break; default: error = OFPERR_OFPMMFC_BAD_COMMAND; break; } if (!error) { struct ofputil_requestforward rf; rf.xid = oh->xid; rf.reason = OFPRFR_METER_MOD; rf.meter_mod = &mm; connmgr_send_requestforward(ofproto->connmgr, ofconn, &rf); } exit_free_bands: ofpbuf_uninit(&bands); return error; } static enum ofperr handle_meter_features_request(struct ofconn *ofconn, const struct ofp_header *request) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_meter_features features; struct ofpbuf *b; if (ofproto->ofproto_class->meter_get_features) { ofproto->ofproto_class->meter_get_features(ofproto, &features); } else { memset(&features, 0, sizeof features); } b = ofputil_encode_meter_features_reply(&features, request); ofconn_send_reply(ofconn, b); return 0; } static enum ofperr handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request, enum ofptype type) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ovs_list replies; uint64_t bands_stub[256 / 8]; struct ofpbuf bands; uint32_t meter_id, first, last; ofputil_decode_meter_request(request, &meter_id); if (meter_id == OFPM13_ALL) { first = 1; last = ofproto->meter_features.max_meters; } else { if (!meter_id || meter_id > ofproto->meter_features.max_meters || !ofproto->meters[meter_id]) { return OFPERR_OFPMMFC_UNKNOWN_METER; } first = last = meter_id; } ofpbuf_use_stub(&bands, bands_stub, sizeof bands_stub); ofpmp_init(&replies, request); for (meter_id = first; meter_id <= last; ++meter_id) { struct meter *meter = ofproto->meters[meter_id]; if (!meter) { continue; /* Skip non-existing meters. */ } if (type == OFPTYPE_METER_STATS_REQUEST) { struct ofputil_meter_stats stats; stats.meter_id = meter_id; /* Provider sets the packet and byte counts, we do the rest. */ stats.flow_count = list_size(&meter->rules); calc_duration(meter->created, time_msec(), &stats.duration_sec, &stats.duration_nsec); stats.n_bands = meter->n_bands; ofpbuf_clear(&bands); stats.bands = ofpbuf_put_uninit(&bands, meter->n_bands * sizeof *stats.bands); if (!ofproto->ofproto_class->meter_get(ofproto, meter->provider_meter_id, &stats)) { ofputil_append_meter_stats(&replies, &stats); } } else { /* type == OFPTYPE_METER_CONFIG_REQUEST */ struct ofputil_meter_config config; config.meter_id = meter_id; config.flags = meter->flags; config.n_bands = meter->n_bands; config.bands = meter->bands; ofputil_append_meter_config(&replies, &config); } } ofconn_send_replies(ofconn, &replies); ofpbuf_uninit(&bands); return 0; } static bool ofproto_group_lookup__(const struct ofproto *ofproto, uint32_t group_id, struct ofgroup **group) OVS_REQ_RDLOCK(ofproto->groups_rwlock) { HMAP_FOR_EACH_IN_BUCKET (*group, hmap_node, hash_int(group_id, 0), &ofproto->groups) { if ((*group)->group_id == group_id) { return true; } } return false; } /* If the group exists, this function increments the groups's reference count. * * Make sure to call ofproto_group_unref() after no longer needing to maintain * a reference to the group. */ bool ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id, struct ofgroup **group) { bool found; ovs_rwlock_rdlock(&ofproto->groups_rwlock); found = ofproto_group_lookup__(ofproto, group_id, group); if (found) { ofproto_group_ref(*group); } ovs_rwlock_unlock(&ofproto->groups_rwlock); return found; } static bool ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id) OVS_REQ_RDLOCK(ofproto->groups_rwlock) { struct ofgroup *grp; HMAP_FOR_EACH_IN_BUCKET (grp, hmap_node, hash_int(group_id, 0), &ofproto->groups) { if (grp->group_id == group_id) { return true; } } return false; } static bool ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id) OVS_EXCLUDED(ofproto->groups_rwlock) { bool exists; ovs_rwlock_rdlock(&ofproto->groups_rwlock); exists = ofproto_group_exists__(ofproto, group_id); ovs_rwlock_unlock(&ofproto->groups_rwlock); return exists; } static uint32_t group_get_ref_count(struct ofgroup *group) OVS_EXCLUDED(ofproto_mutex) { struct ofproto *ofproto = CONST_CAST(struct ofproto *, group->ofproto); struct rule_criteria criteria; struct rule_collection rules; struct match match; enum ofperr error; uint32_t count; match_init_catchall(&match); rule_criteria_init(&criteria, 0xff, &match, 0, CLS_MAX_VERSION, htonll(0), htonll(0), OFPP_ANY, group->group_id); ovs_mutex_lock(&ofproto_mutex); error = collect_rules_loose(ofproto, &criteria, &rules); ovs_mutex_unlock(&ofproto_mutex); rule_criteria_destroy(&criteria); count = !error && rules.n < UINT32_MAX ? rules.n : UINT32_MAX; rule_collection_destroy(&rules); return count; } static void append_group_stats(struct ofgroup *group, struct ovs_list *replies) { struct ofputil_group_stats ogs; const struct ofproto *ofproto = group->ofproto; long long int now = time_msec(); int error; ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats); /* Provider sets the packet and byte counts, we do the rest. */ ogs.ref_count = group_get_ref_count(group); ogs.n_buckets = group->n_buckets; error = (ofproto->ofproto_class->group_get_stats ? ofproto->ofproto_class->group_get_stats(group, &ogs) : EOPNOTSUPP); if (error) { ogs.packet_count = UINT64_MAX; ogs.byte_count = UINT64_MAX; memset(ogs.bucket_stats, 0xff, ogs.n_buckets * sizeof *ogs.bucket_stats); } ogs.group_id = group->group_id; calc_duration(group->created, now, &ogs.duration_sec, &ogs.duration_nsec); ofputil_append_group_stats(replies, &ogs); free(ogs.bucket_stats); } static void handle_group_request(struct ofconn *ofconn, const struct ofp_header *request, uint32_t group_id, void (*cb)(struct ofgroup *, struct ovs_list *replies)) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofgroup *group; struct ovs_list replies; ofpmp_init(&replies, request); if (group_id == OFPG_ALL) { ovs_rwlock_rdlock(&ofproto->groups_rwlock); HMAP_FOR_EACH (group, hmap_node, &ofproto->groups) { cb(group, &replies); } ovs_rwlock_unlock(&ofproto->groups_rwlock); } else { if (ofproto_group_lookup(ofproto, group_id, &group)) { cb(group, &replies); ofproto_group_unref(group); } } ofconn_send_replies(ofconn, &replies); } static enum ofperr handle_group_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { uint32_t group_id; enum ofperr error; error = ofputil_decode_group_stats_request(request, &group_id); if (error) { return error; } handle_group_request(ofconn, request, group_id, append_group_stats); return 0; } static void append_group_desc(struct ofgroup *group, struct ovs_list *replies) { struct ofputil_group_desc gds; gds.group_id = group->group_id; gds.type = group->type; gds.props = group->props; ofputil_append_group_desc_reply(&gds, &group->buckets, replies); } static enum ofperr handle_group_desc_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { handle_group_request(ofconn, request, ofputil_decode_group_desc_request(request), append_group_desc); return 0; } static enum ofperr handle_group_features_stats_request(struct ofconn *ofconn, const struct ofp_header *request) { struct ofproto *p = ofconn_get_ofproto(ofconn); struct ofpbuf *msg; msg = ofputil_encode_group_features_reply(&p->ogf, request); if (msg) { ofconn_send_reply(ofconn, msg); } return 0; } static enum ofperr handle_queue_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *p = ofconn_get_ofproto(ofconn); struct netdev_queue_dump queue_dump; struct ofport *ofport; unsigned int queue_id; struct ofpbuf *reply; struct smap details; ofp_port_t request; enum ofperr error; error = ofputil_decode_queue_get_config_request(oh, &request); if (error) { return error; } ofport = ofproto_get_port(p, request); if (!ofport) { return OFPERR_OFPQOFC_BAD_PORT; } reply = ofputil_encode_queue_get_config_reply(oh); smap_init(&details); NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &queue_dump, ofport->netdev) { struct ofputil_queue_config queue; /* None of the existing queues have compatible properties, so we * hard-code omitting min_rate and max_rate. */ queue.queue_id = queue_id; queue.min_rate = UINT16_MAX; queue.max_rate = UINT16_MAX; ofputil_append_queue_get_config_reply(reply, &queue); } smap_destroy(&details); ofconn_send_reply(ofconn, reply); return 0; } static enum ofperr init_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm, struct ofgroup **ofgroup) { enum ofperr error; const long long int now = time_msec(); if (gm->group_id > OFPG_MAX) { return OFPERR_OFPGMFC_INVALID_GROUP; } if (gm->type > OFPGT11_FF) { return OFPERR_OFPGMFC_BAD_TYPE; } *ofgroup = ofproto->ofproto_class->group_alloc(); if (!*ofgroup) { VLOG_WARN_RL(&rl, "%s: failed to allocate group", ofproto->name); return OFPERR_OFPGMFC_OUT_OF_GROUPS; } (*ofgroup)->ofproto = ofproto; *CONST_CAST(uint32_t *, &((*ofgroup)->group_id)) = gm->group_id; *CONST_CAST(enum ofp11_group_type *, &(*ofgroup)->type) = gm->type; *CONST_CAST(long long int *, &((*ofgroup)->created)) = now; *CONST_CAST(long long int *, &((*ofgroup)->modified)) = now; ovs_refcount_init(&(*ofgroup)->ref_count); list_init(&(*ofgroup)->buckets); ofputil_bucket_clone_list(&(*ofgroup)->buckets, &gm->buckets, NULL); *CONST_CAST(uint32_t *, &(*ofgroup)->n_buckets) = list_size(&(*ofgroup)->buckets); memcpy(CONST_CAST(struct ofputil_group_props *, &(*ofgroup)->props), &gm->props, sizeof (struct ofputil_group_props)); /* Construct called BEFORE any locks are held. */ error = ofproto->ofproto_class->group_construct(*ofgroup); if (error) { ofputil_bucket_list_destroy(&(*ofgroup)->buckets); ofproto->ofproto_class->group_dealloc(*ofgroup); } return error; } /* Implements the OFPGC11_ADD operation specified by 'gm', adding a group to * 'ofproto''s group table. Returns 0 on success or an OpenFlow error code on * failure. */ static enum ofperr add_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) { struct ofgroup *ofgroup; enum ofperr error; /* Allocate new group and initialize it. */ error = init_group(ofproto, gm, &ofgroup); if (error) { return error; } /* We wrlock as late as possible to minimize the time we jam any other * threads: No visible state changes before acquiring the lock. */ ovs_rwlock_wrlock(&ofproto->groups_rwlock); if (ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) { error = OFPERR_OFPGMFC_OUT_OF_GROUPS; goto unlock_out; } if (ofproto_group_exists__(ofproto, gm->group_id)) { error = OFPERR_OFPGMFC_GROUP_EXISTS; goto unlock_out; } if (!error) { /* Insert new group. */ hmap_insert(&ofproto->groups, &ofgroup->hmap_node, hash_int(ofgroup->group_id, 0)); ofproto->n_groups[ofgroup->type]++; ovs_rwlock_unlock(&ofproto->groups_rwlock); return error; } unlock_out: ovs_rwlock_unlock(&ofproto->groups_rwlock); ofproto->ofproto_class->group_destruct(ofgroup); ofputil_bucket_list_destroy(&ofgroup->buckets); ofproto->ofproto_class->group_dealloc(ofgroup); return error; } /* Adds all of the buckets from 'ofgroup' to 'new_ofgroup'. The buckets * already in 'new_ofgroup' will be placed just after the (copy of the) bucket * in 'ofgroup' with bucket ID 'command_bucket_id'. Special * 'command_bucket_id' values OFPG15_BUCKET_FIRST and OFPG15_BUCKET_LAST are * also honored. */ static enum ofperr copy_buckets_for_insert_bucket(const struct ofgroup *ofgroup, struct ofgroup *new_ofgroup, uint32_t command_bucket_id) { struct ofputil_bucket *last = NULL; if (command_bucket_id <= OFPG15_BUCKET_MAX) { /* Check here to ensure that a bucket corresponding to * command_bucket_id exists in the old bucket list. * * The subsequent search of below of new_ofgroup covers * both buckets in the old bucket list and buckets added * by the insert buckets group mod message this function processes. */ if (!ofputil_bucket_find(&ofgroup->buckets, command_bucket_id)) { return OFPERR_OFPGMFC_UNKNOWN_BUCKET; } if (!list_is_empty(&new_ofgroup->buckets)) { last = ofputil_bucket_list_back(&new_ofgroup->buckets); } } ofputil_bucket_clone_list(&new_ofgroup->buckets, &ofgroup->buckets, NULL); if (ofputil_bucket_check_duplicate_id(&new_ofgroup->buckets)) { VLOG_INFO_RL(&rl, "Duplicate bucket id"); return OFPERR_OFPGMFC_BUCKET_EXISTS; } /* Rearrange list according to command_bucket_id */ if (command_bucket_id == OFPG15_BUCKET_LAST) { if (!list_is_empty(&ofgroup->buckets)) { struct ofputil_bucket *new_first; const struct ofputil_bucket *first; first = ofputil_bucket_list_front(&ofgroup->buckets); new_first = ofputil_bucket_find(&new_ofgroup->buckets, first->bucket_id); list_splice(new_ofgroup->buckets.next, &new_first->list_node, &new_ofgroup->buckets); } } else if (command_bucket_id <= OFPG15_BUCKET_MAX && last) { struct ofputil_bucket *after; /* Presence of bucket is checked above so after should never be NULL */ after = ofputil_bucket_find(&new_ofgroup->buckets, command_bucket_id); list_splice(after->list_node.next, new_ofgroup->buckets.next, last->list_node.next); } return 0; } /* Appends all of the a copy of all the buckets from 'ofgroup' to 'new_ofgroup' * with the exception of the bucket whose bucket id is 'command_bucket_id'. * Special 'command_bucket_id' values OFPG15_BUCKET_FIRST, OFPG15_BUCKET_LAST * and OFPG15_BUCKET_ALL are also honored. */ static enum ofperr copy_buckets_for_remove_bucket(const struct ofgroup *ofgroup, struct ofgroup *new_ofgroup, uint32_t command_bucket_id) { const struct ofputil_bucket *skip = NULL; if (command_bucket_id == OFPG15_BUCKET_ALL) { return 0; } if (command_bucket_id == OFPG15_BUCKET_FIRST) { if (!list_is_empty(&ofgroup->buckets)) { skip = ofputil_bucket_list_front(&ofgroup->buckets); } } else if (command_bucket_id == OFPG15_BUCKET_LAST) { if (!list_is_empty(&ofgroup->buckets)) { skip = ofputil_bucket_list_back(&ofgroup->buckets); } } else { skip = ofputil_bucket_find(&ofgroup->buckets, command_bucket_id); if (!skip) { return OFPERR_OFPGMFC_UNKNOWN_BUCKET; } } ofputil_bucket_clone_list(&new_ofgroup->buckets, &ofgroup->buckets, skip); return 0; } /* Implements OFPGC11_MODIFY, OFPGC15_INSERT_BUCKET and * OFPGC15_REMOVE_BUCKET. Returns 0 on success or an OpenFlow error code * on failure. * * Note that the group is re-created and then replaces the old group in * ofproto's ofgroup hash map. Thus, the group is never altered while users of * the xlate module hold a pointer to the group. */ static enum ofperr modify_group(struct ofproto *ofproto, const struct ofputil_group_mod *gm) { struct ofgroup *ofgroup, *new_ofgroup, *retiring; enum ofperr error; error = init_group(ofproto, gm, &new_ofgroup); if (error) { return error; } retiring = new_ofgroup; ovs_rwlock_wrlock(&ofproto->groups_rwlock); if (!ofproto_group_lookup__(ofproto, gm->group_id, &ofgroup)) { error = OFPERR_OFPGMFC_UNKNOWN_GROUP; goto out; } /* Ofproto's group write lock is held now. */ if (ofgroup->type != gm->type && ofproto->n_groups[gm->type] >= ofproto->ogf.max_groups[gm->type]) { error = OFPERR_OFPGMFC_OUT_OF_GROUPS; goto out; } /* Manipulate bucket list for bucket commands */ if (gm->command == OFPGC15_INSERT_BUCKET) { error = copy_buckets_for_insert_bucket(ofgroup, new_ofgroup, gm->command_bucket_id); } else if (gm->command == OFPGC15_REMOVE_BUCKET) { error = copy_buckets_for_remove_bucket(ofgroup, new_ofgroup, gm->command_bucket_id); } if (error) { goto out; } /* The group creation time does not change during modification. */ *CONST_CAST(long long int *, &(new_ofgroup->created)) = ofgroup->created; *CONST_CAST(long long int *, &(new_ofgroup->modified)) = time_msec(); error = ofproto->ofproto_class->group_modify(new_ofgroup); if (error) { goto out; } retiring = ofgroup; /* Replace ofgroup in ofproto's groups hash map with new_ofgroup. */ hmap_remove(&ofproto->groups, &ofgroup->hmap_node); hmap_insert(&ofproto->groups, &new_ofgroup->hmap_node, hash_int(new_ofgroup->group_id, 0)); if (ofgroup->type != new_ofgroup->type) { ofproto->n_groups[ofgroup->type]--; ofproto->n_groups[new_ofgroup->type]++; } out: ofproto_group_unref(retiring); ovs_rwlock_unlock(&ofproto->groups_rwlock); return error; } static void delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup) OVS_RELEASES(ofproto->groups_rwlock) { struct match match; struct ofproto_flow_mod ofm; /* Delete all flow entries containing this group in a group action */ match_init_catchall(&match); flow_mod_init(&ofm.fm, &match, 0, NULL, 0, OFPFC_DELETE); ofm.fm.delete_reason = OFPRR_GROUP_DELETE; ofm.fm.out_group = ofgroup->group_id; ofm.fm.table_id = OFPTT_ALL; handle_flow_mod__(ofproto, &ofm, NULL); hmap_remove(&ofproto->groups, &ofgroup->hmap_node); /* No-one can find this group any more. */ ofproto->n_groups[ofgroup->type]--; ovs_rwlock_unlock(&ofproto->groups_rwlock); ofproto_group_unref(ofgroup); } /* Implements OFPGC11_DELETE. */ static void delete_group(struct ofproto *ofproto, uint32_t group_id) { struct ofgroup *ofgroup; ovs_rwlock_wrlock(&ofproto->groups_rwlock); if (group_id == OFPG_ALL) { for (;;) { struct hmap_node *node = hmap_first(&ofproto->groups); if (!node) { break; } ofgroup = CONTAINER_OF(node, struct ofgroup, hmap_node); delete_group__(ofproto, ofgroup); /* Lock for each node separately, so that we will not jam the * other threads for too long time. */ ovs_rwlock_wrlock(&ofproto->groups_rwlock); } } else { HMAP_FOR_EACH_IN_BUCKET (ofgroup, hmap_node, hash_int(group_id, 0), &ofproto->groups) { if (ofgroup->group_id == group_id) { delete_group__(ofproto, ofgroup); return; } } } ovs_rwlock_unlock(&ofproto->groups_rwlock); } /* Delete all groups from 'ofproto'. * * This is intended for use within an ofproto provider's 'destruct' * function. */ void ofproto_group_delete_all(struct ofproto *ofproto) { delete_group(ofproto, OFPG_ALL); } static enum ofperr handle_group_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_group_mod gm; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_group_mod(oh, &gm); if (error) { return error; } switch (gm.command) { case OFPGC11_ADD: error = add_group(ofproto, &gm); break; case OFPGC11_MODIFY: error = modify_group(ofproto, &gm); break; case OFPGC11_DELETE: delete_group(ofproto, gm.group_id); error = 0; break; case OFPGC15_INSERT_BUCKET: error = modify_group(ofproto, &gm); break; case OFPGC15_REMOVE_BUCKET: error = modify_group(ofproto, &gm); break; default: if (gm.command > OFPGC11_DELETE) { VLOG_INFO_RL(&rl, "%s: Invalid group_mod command type %d", ofproto->name, gm.command); } error = OFPERR_OFPGMFC_BAD_COMMAND; } if (!error) { struct ofputil_requestforward rf; rf.xid = oh->xid; rf.reason = OFPRFR_GROUP_MOD; rf.group_mod = &gm; connmgr_send_requestforward(ofproto->connmgr, ofconn, &rf); } ofputil_bucket_list_destroy(&gm.buckets); return error; } enum ofputil_table_miss ofproto_table_get_miss_config(const struct ofproto *ofproto, uint8_t table_id) { enum ofputil_table_miss miss; atomic_read_relaxed(&ofproto->tables[table_id].miss_config, &miss); return miss; } static void table_mod__(struct oftable *oftable, const struct ofputil_table_mod *tm) { if (tm->miss == OFPUTIL_TABLE_MISS_DEFAULT) { /* This is how an OFPT_TABLE_MOD decodes if it doesn't specify any * table-miss configuration (because the protocol used doesn't have * such a concept), so there's nothing to do. */ } else { atomic_store_relaxed(&oftable->miss_config, tm->miss); } unsigned int new_eviction = oftable->eviction; if (tm->eviction == OFPUTIL_TABLE_EVICTION_ON) { new_eviction |= EVICTION_OPENFLOW; } else if (tm->eviction == OFPUTIL_TABLE_EVICTION_OFF) { new_eviction &= ~EVICTION_OPENFLOW; } if (new_eviction != oftable->eviction) { ovs_mutex_lock(&ofproto_mutex); oftable_configure_eviction(oftable, new_eviction, oftable->eviction_fields, oftable->n_eviction_fields); ovs_mutex_unlock(&ofproto_mutex); } if (tm->vacancy != OFPUTIL_TABLE_VACANCY_DEFAULT) { ovs_mutex_lock(&ofproto_mutex); oftable->vacancy_enabled = (tm->vacancy == OFPUTIL_TABLE_VACANCY_ON ? OFPTC14_VACANCY_EVENTS : 0); oftable->vacancy_down = tm->table_vacancy.vacancy_down; oftable->vacancy_up = tm->table_vacancy.vacancy_up; ovs_mutex_unlock(&ofproto_mutex); } } static enum ofperr table_mod(struct ofproto *ofproto, const struct ofputil_table_mod *tm) { if (!check_table_id(ofproto, tm->table_id)) { return OFPERR_OFPTMFC_BAD_TABLE; } /* Don't allow the eviction flags to be changed (except to the only fixed * value that OVS supports). OF1.4 says this is normal: "The * OFPTMPT_EVICTION property usually cannot be modified using a * OFP_TABLE_MOD request, because the eviction mechanism is switch * defined". */ if (tm->eviction_flags != UINT32_MAX && tm->eviction_flags != OFPROTO_EVICTION_FLAGS) { return OFPERR_OFPTMFC_BAD_CONFIG; } if (tm->table_id == OFPTT_ALL) { struct oftable *oftable; OFPROTO_FOR_EACH_TABLE (oftable, ofproto) { if (!(oftable->flags & (OFTABLE_HIDDEN | OFTABLE_READONLY))) { table_mod__(oftable, tm); } } } else { struct oftable *oftable = &ofproto->tables[tm->table_id]; if (oftable->flags & OFTABLE_READONLY) { return OFPERR_OFPTMFC_EPERM; } table_mod__(oftable, tm); } return 0; } static enum ofperr handle_table_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_table_mod tm; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_table_mod(oh, &tm); if (error) { return error; } return table_mod(ofproto, &tm); } static enum ofperr ofproto_flow_mod_start(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { switch (ofm->fm.command) { case OFPFC_ADD: return add_flow_start(ofproto, ofm); /* , &be->old_rules.stub[0], &be->new_rules.stub[0]); */ case OFPFC_MODIFY: return modify_flows_start_loose(ofproto, ofm); case OFPFC_MODIFY_STRICT: return modify_flow_start_strict(ofproto, ofm); case OFPFC_DELETE: return delete_flows_start_loose(ofproto, ofm); case OFPFC_DELETE_STRICT: return delete_flow_start_strict(ofproto, ofm); } return OFPERR_OFPFMFC_BAD_COMMAND; } static void ofproto_flow_mod_revert(struct ofproto *ofproto, struct ofproto_flow_mod *ofm) OVS_REQUIRES(ofproto_mutex) { switch (ofm->fm.command) { case OFPFC_ADD: add_flow_revert(ofproto, ofm); break; case OFPFC_MODIFY: case OFPFC_MODIFY_STRICT: modify_flows_revert(ofproto, ofm); break; case OFPFC_DELETE: case OFPFC_DELETE_STRICT: delete_flows_revert(ofproto, ofm); break; default: break; } } static void ofproto_flow_mod_finish(struct ofproto *ofproto, struct ofproto_flow_mod *ofm, const struct flow_mod_requester *req) OVS_REQUIRES(ofproto_mutex) { switch (ofm->fm.command) { case OFPFC_ADD: add_flow_finish(ofproto, ofm, req); break; case OFPFC_MODIFY: case OFPFC_MODIFY_STRICT: modify_flows_finish(ofproto, ofm, req); break; case OFPFC_DELETE: case OFPFC_DELETE_STRICT: delete_flows_finish(ofproto, ofm, req); break; default: break; } } /* Commit phases (all while locking ofproto_mutex): * * 1. Begin: Gather resources and make changes visible in the next version. * - Mark affected rules for removal in the next version. * - Create new replacement rules, make visible in the next * version. * - Do not send any events or notifications. * * 2. Revert: Fail if any errors are found. After this point no errors are * possible. No visible changes were made, so rollback is minimal (remove * added invisible rules, restore visibility of rules marked for removal). * * 3. Finish: Make the changes visible for lookups. Insert replacement rules to * the ofproto provider. Remove replaced and deleted rules from ofproto data * structures, and Schedule postponed removal of deleted rules from the * classifier. Send notifications, buffered packets, etc. */ static enum ofperr do_bundle_commit(struct ofconn *ofconn, uint32_t id, uint16_t flags) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); cls_version_t version = ofproto->tables_version + 1; struct ofp_bundle *bundle; struct ofp_bundle_entry *be; enum ofperr error; bundle = ofconn_get_bundle(ofconn, id); if (!bundle) { return OFPERR_OFPBFC_BAD_ID; } if (bundle->flags != flags) { error = OFPERR_OFPBFC_BAD_FLAGS; } else { bool prev_is_port_mod = false; error = 0; ovs_mutex_lock(&ofproto_mutex); /* 1. Begin. */ LIST_FOR_EACH (be, node, &bundle->msg_list) { if (be->type == OFPTYPE_PORT_MOD) { /* Our port mods are not atomic. */ if (flags & OFPBF_ATOMIC) { error = OFPERR_OFPBFC_MSG_FAILED; } else { prev_is_port_mod = true; error = port_mod_start(ofconn, &be->opm.pm, &be->opm.port); } } else if (be->type == OFPTYPE_FLOW_MOD) { /* Flow mods between port mods are applied as a single * version, but the versions are published only after * we know the commit is successful. */ if (prev_is_port_mod) { ++version; } prev_is_port_mod = false; /* Store the version in which the changes should take * effect. */ be->ofm.version = version; error = ofproto_flow_mod_start(ofproto, &be->ofm); } else { OVS_NOT_REACHED(); } if (error) { break; } } if (error) { /* Send error referring to the original message. */ if (error) { ofconn_send_error(ofconn, be->ofp_msg, error); error = OFPERR_OFPBFC_MSG_FAILED; } /* 2. Revert. Undo all the changes made above. */ LIST_FOR_EACH_REVERSE_CONTINUE(be, node, &bundle->msg_list) { if (be->type == OFPTYPE_FLOW_MOD) { ofproto_flow_mod_revert(ofproto, &be->ofm); } /* Nothing needs to be reverted for a port mod. */ } } else { /* 4. Finish. */ LIST_FOR_EACH (be, node, &bundle->msg_list) { if (be->type == OFPTYPE_FLOW_MOD) { struct flow_mod_requester req = { ofconn, be->ofp_msg }; /* Bump the lookup version to the one of the current * message. This makes all the changes in the bundle at * this version visible to lookups at once. */ if (ofproto->tables_version < be->ofm.version) { ofproto->tables_version = be->ofm.version; ofproto->ofproto_class->set_tables_version( ofproto, ofproto->tables_version); } ofproto_flow_mod_finish(ofproto, &be->ofm, &req); } else if (be->type == OFPTYPE_PORT_MOD) { /* Perform the actual port mod. This is not atomic, i.e., * the effects will be immediately seen by upcall * processing regardless of the lookup version. It should * be noted that port configuration changes can originate * also from OVSDB changes asynchronously to all upcall * processing. */ port_mod_finish(ofconn, &be->opm.pm, be->opm.port); } } } ofmonitor_flush(ofproto->connmgr); ovs_mutex_unlock(&ofproto_mutex); run_rule_executes(ofproto); } /* The bundle is discarded regardless the outcome. */ ofp_bundle_remove__(ofconn, bundle, !error); return error; } static enum ofperr handle_bundle_control(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofputil_bundle_ctrl_msg bctrl; struct ofputil_bundle_ctrl_msg reply; struct ofpbuf *buf; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_bundle_ctrl(oh, &bctrl); if (error) { return error; } reply.flags = 0; reply.bundle_id = bctrl.bundle_id; switch (bctrl.type) { case OFPBCT_OPEN_REQUEST: error = ofp_bundle_open(ofconn, bctrl.bundle_id, bctrl.flags); reply.type = OFPBCT_OPEN_REPLY; break; case OFPBCT_CLOSE_REQUEST: error = ofp_bundle_close(ofconn, bctrl.bundle_id, bctrl.flags); reply.type = OFPBCT_CLOSE_REPLY; break; case OFPBCT_COMMIT_REQUEST: error = do_bundle_commit(ofconn, bctrl.bundle_id, bctrl.flags); reply.type = OFPBCT_COMMIT_REPLY; break; case OFPBCT_DISCARD_REQUEST: error = ofp_bundle_discard(ofconn, bctrl.bundle_id); reply.type = OFPBCT_DISCARD_REPLY; break; case OFPBCT_OPEN_REPLY: case OFPBCT_CLOSE_REPLY: case OFPBCT_COMMIT_REPLY: case OFPBCT_DISCARD_REPLY: return OFPERR_OFPBFC_BAD_TYPE; break; } if (!error) { buf = ofputil_encode_bundle_ctrl_reply(oh, &reply); ofconn_send_reply(ofconn, buf); } return error; } static enum ofperr handle_bundle_add(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); enum ofperr error; struct ofputil_bundle_add_msg badd; struct ofp_bundle_entry *bmsg; enum ofptype type; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_bundle_add(oh, &badd, &type); if (error) { return error; } bmsg = ofp_bundle_entry_alloc(type, badd.msg); if (type == OFPTYPE_PORT_MOD) { error = ofputil_decode_port_mod(badd.msg, &bmsg->opm.pm, false); } else if (type == OFPTYPE_FLOW_MOD) { struct ofpbuf ofpacts; uint64_t ofpacts_stub[1024 / 8]; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); error = ofputil_decode_flow_mod(&bmsg->ofm.fm, badd.msg, ofconn_get_protocol(ofconn), &ofpacts, u16_to_ofp(ofproto->max_ports), ofproto->n_tables); /* Move actions to heap. */ bmsg->ofm.fm.ofpacts = ofpbuf_steal_data(&ofpacts); if (!error && bmsg->ofm.fm.ofpacts_len) { error = ofproto_check_ofpacts(ofproto, bmsg->ofm.fm.ofpacts, bmsg->ofm.fm.ofpacts_len); } } else { OVS_NOT_REACHED(); } if (!error) { error = ofp_bundle_add_message(ofconn, badd.bundle_id, badd.flags, bmsg); } if (error) { ofp_bundle_entry_free(bmsg); } return error; } static enum ofperr handle_tlv_table_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofputil_tlv_table_mod ttm; enum ofperr error; error = reject_slave_controller(ofconn); if (error) { return error; } error = ofputil_decode_tlv_table_mod(oh, &ttm); if (error) { return error; } error = tun_metadata_table_mod(&ttm); ofputil_uninit_tlv_table(&ttm.mappings); return error; } static enum ofperr handle_tlv_table_request(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofputil_tlv_table_reply ttr; struct ofpbuf *b; tun_metadata_table_request(&ttr); b = ofputil_encode_tlv_table_reply(oh, &ttr); ofputil_uninit_tlv_table(&ttr.mappings); ofconn_send_reply(ofconn, b); return 0; } static enum ofperr handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg) OVS_EXCLUDED(ofproto_mutex) { const struct ofp_header *oh = msg->data; enum ofptype type; enum ofperr error; error = ofptype_decode(&type, oh); if (error) { return error; } if (oh->version >= OFP13_VERSION && ofpmsg_is_stat_request(oh) && ofpmp_more(oh)) { /* We have no buffer implementation for multipart requests. * Report overflow for requests which consists of multiple * messages. */ return OFPERR_OFPBRC_MULTIPART_BUFFER_OVERFLOW; } switch (type) { /* OpenFlow requests. */ case OFPTYPE_ECHO_REQUEST: return handle_echo_request(ofconn, oh); case OFPTYPE_FEATURES_REQUEST: return handle_features_request(ofconn, oh); case OFPTYPE_GET_CONFIG_REQUEST: return handle_get_config_request(ofconn, oh); case OFPTYPE_SET_CONFIG: return handle_set_config(ofconn, oh); case OFPTYPE_PACKET_OUT: return handle_packet_out(ofconn, oh); case OFPTYPE_PORT_MOD: return handle_port_mod(ofconn, oh); case OFPTYPE_FLOW_MOD: return handle_flow_mod(ofconn, oh); case OFPTYPE_GROUP_MOD: return handle_group_mod(ofconn, oh); case OFPTYPE_TABLE_MOD: return handle_table_mod(ofconn, oh); case OFPTYPE_METER_MOD: return handle_meter_mod(ofconn, oh); case OFPTYPE_BARRIER_REQUEST: return handle_barrier_request(ofconn, oh); case OFPTYPE_ROLE_REQUEST: return handle_role_request(ofconn, oh); /* OpenFlow replies. */ case OFPTYPE_ECHO_REPLY: return 0; /* Nicira extension requests. */ case OFPTYPE_FLOW_MOD_TABLE_ID: return handle_nxt_flow_mod_table_id(ofconn, oh); case OFPTYPE_SET_FLOW_FORMAT: return handle_nxt_set_flow_format(ofconn, oh); case OFPTYPE_SET_PACKET_IN_FORMAT: return handle_nxt_set_packet_in_format(ofconn, oh); case OFPTYPE_SET_CONTROLLER_ID: return handle_nxt_set_controller_id(ofconn, oh); case OFPTYPE_FLOW_AGE: /* Nothing to do. */ return 0; case OFPTYPE_FLOW_MONITOR_CANCEL: return handle_flow_monitor_cancel(ofconn, oh); case OFPTYPE_SET_ASYNC_CONFIG: return handle_nxt_set_async_config(ofconn, oh); case OFPTYPE_GET_ASYNC_REQUEST: return handle_nxt_get_async_request(ofconn, oh); /* Statistics requests. */ case OFPTYPE_DESC_STATS_REQUEST: return handle_desc_stats_request(ofconn, oh); case OFPTYPE_FLOW_STATS_REQUEST: return handle_flow_stats_request(ofconn, oh); case OFPTYPE_AGGREGATE_STATS_REQUEST: return handle_aggregate_stats_request(ofconn, oh); case OFPTYPE_TABLE_STATS_REQUEST: return handle_table_stats_request(ofconn, oh); case OFPTYPE_TABLE_FEATURES_STATS_REQUEST: return handle_table_features_request(ofconn, oh); case OFPTYPE_TABLE_DESC_REQUEST: return handle_table_desc_request(ofconn, oh); case OFPTYPE_PORT_STATS_REQUEST: return handle_port_stats_request(ofconn, oh); case OFPTYPE_QUEUE_STATS_REQUEST: return handle_queue_stats_request(ofconn, oh); case OFPTYPE_PORT_DESC_STATS_REQUEST: return handle_port_desc_stats_request(ofconn, oh); case OFPTYPE_FLOW_MONITOR_STATS_REQUEST: return handle_flow_monitor_request(ofconn, oh); case OFPTYPE_METER_STATS_REQUEST: case OFPTYPE_METER_CONFIG_STATS_REQUEST: return handle_meter_request(ofconn, oh, type); case OFPTYPE_METER_FEATURES_STATS_REQUEST: return handle_meter_features_request(ofconn, oh); case OFPTYPE_GROUP_STATS_REQUEST: return handle_group_stats_request(ofconn, oh); case OFPTYPE_GROUP_DESC_STATS_REQUEST: return handle_group_desc_stats_request(ofconn, oh); case OFPTYPE_GROUP_FEATURES_STATS_REQUEST: return handle_group_features_stats_request(ofconn, oh); case OFPTYPE_QUEUE_GET_CONFIG_REQUEST: return handle_queue_get_config_request(ofconn, oh); case OFPTYPE_BUNDLE_CONTROL: return handle_bundle_control(ofconn, oh); case OFPTYPE_BUNDLE_ADD_MESSAGE: return handle_bundle_add(ofconn, oh); case OFPTYPE_NXT_TLV_TABLE_MOD: return handle_tlv_table_mod(ofconn, oh); case OFPTYPE_NXT_TLV_TABLE_REQUEST: return handle_tlv_table_request(ofconn, oh); case OFPTYPE_HELLO: case OFPTYPE_ERROR: case OFPTYPE_FEATURES_REPLY: case OFPTYPE_GET_CONFIG_REPLY: case OFPTYPE_PACKET_IN: case OFPTYPE_FLOW_REMOVED: case OFPTYPE_PORT_STATUS: case OFPTYPE_BARRIER_REPLY: case OFPTYPE_QUEUE_GET_CONFIG_REPLY: case OFPTYPE_DESC_STATS_REPLY: case OFPTYPE_FLOW_STATS_REPLY: case OFPTYPE_QUEUE_STATS_REPLY: case OFPTYPE_PORT_STATS_REPLY: case OFPTYPE_TABLE_STATS_REPLY: case OFPTYPE_AGGREGATE_STATS_REPLY: case OFPTYPE_PORT_DESC_STATS_REPLY: case OFPTYPE_ROLE_REPLY: case OFPTYPE_FLOW_MONITOR_PAUSED: case OFPTYPE_FLOW_MONITOR_RESUMED: case OFPTYPE_FLOW_MONITOR_STATS_REPLY: case OFPTYPE_GET_ASYNC_REPLY: case OFPTYPE_GROUP_STATS_REPLY: case OFPTYPE_GROUP_DESC_STATS_REPLY: case OFPTYPE_GROUP_FEATURES_STATS_REPLY: case OFPTYPE_METER_STATS_REPLY: case OFPTYPE_METER_CONFIG_STATS_REPLY: case OFPTYPE_METER_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_FEATURES_STATS_REPLY: case OFPTYPE_TABLE_DESC_REPLY: case OFPTYPE_ROLE_STATUS: case OFPTYPE_REQUESTFORWARD: case OFPTYPE_NXT_TLV_TABLE_REPLY: default: if (ofpmsg_is_stat_request(oh)) { return OFPERR_OFPBRC_BAD_STAT; } else { return OFPERR_OFPBRC_BAD_TYPE; } } } static void handle_openflow(struct ofconn *ofconn, const struct ofpbuf *ofp_msg) OVS_EXCLUDED(ofproto_mutex) { enum ofperr error = handle_openflow__(ofconn, ofp_msg); if (error) { ofconn_send_error(ofconn, ofp_msg->data, error); } COVERAGE_INC(ofproto_recv_openflow); } /* Asynchronous operations. */ static void send_buffered_packet(const struct flow_mod_requester *req, uint32_t buffer_id, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { if (req && req->ofconn && buffer_id != UINT32_MAX) { struct ofproto *ofproto = ofconn_get_ofproto(req->ofconn); struct dp_packet *packet; ofp_port_t in_port; enum ofperr error; error = ofconn_pktbuf_retrieve(req->ofconn, buffer_id, &packet, &in_port); if (packet) { struct rule_execute *re; ofproto_rule_ref(rule); re = xmalloc(sizeof *re); re->rule = rule; re->in_port = in_port; re->packet = packet; if (!guarded_list_push_back(&ofproto->rule_executes, &re->list_node, 1024)) { ofproto_rule_unref(rule); dp_packet_delete(re->packet); free(re); } } else { ofconn_send_error(req->ofconn, req->request, error); } } } static uint64_t pick_datapath_id(const struct ofproto *ofproto) { const struct ofport *port; port = ofproto_get_port(ofproto, OFPP_LOCAL); if (port) { struct eth_addr ea; int error; error = netdev_get_etheraddr(port->netdev, &ea); if (!error) { return eth_addr_to_uint64(ea); } VLOG_WARN("%s: could not get MAC address for %s (%s)", ofproto->name, netdev_get_name(port->netdev), ovs_strerror(error)); } return ofproto->fallback_dpid; } static uint64_t pick_fallback_dpid(void) { struct eth_addr ea; eth_addr_nicira_random(&ea); return eth_addr_to_uint64(ea); } /* Table overflow policy. */ /* Chooses and updates 'rulep' with a rule to evict from 'table'. Sets 'rulep' * to NULL if the table is not configured to evict rules or if the table * contains no evictable rules. (Rules with a readlock on their evict rwlock, * or with no timeouts are not evictable.) */ static bool choose_rule_to_evict(struct oftable *table, struct rule **rulep) OVS_REQUIRES(ofproto_mutex) { struct eviction_group *evg; *rulep = NULL; if (!table->eviction) { return false; } /* In the common case, the outer and inner loops here will each be entered * exactly once: * * - The inner loop normally "return"s in its first iteration. If the * eviction group has any evictable rules, then it always returns in * some iteration. * * - The outer loop only iterates more than once if the largest eviction * group has no evictable rules. * * - The outer loop can exit only if table's 'max_flows' is all filled up * by unevictable rules. */ HEAP_FOR_EACH (evg, size_node, &table->eviction_groups_by_size) { struct rule *rule; HEAP_FOR_EACH (rule, evg_node, &evg->rules) { *rulep = rule; return true; } } return false; } /* Eviction groups. */ /* Returns the priority to use for an eviction_group that contains 'n_rules' * rules. The priority contains low-order random bits to ensure that eviction * groups with the same number of rules are prioritized randomly. */ static uint32_t eviction_group_priority(size_t n_rules) { uint16_t size = MIN(UINT16_MAX, n_rules); return (size << 16) | random_uint16(); } /* Updates 'evg', an eviction_group within 'table', following a change that * adds or removes rules in 'evg'. */ static void eviction_group_resized(struct oftable *table, struct eviction_group *evg) OVS_REQUIRES(ofproto_mutex) { heap_change(&table->eviction_groups_by_size, &evg->size_node, eviction_group_priority(heap_count(&evg->rules))); } /* Destroys 'evg', an eviction_group within 'table': * * - Removes all the rules, if any, from 'evg'. (It doesn't destroy the * rules themselves, just removes them from the eviction group.) * * - Removes 'evg' from 'table'. * * - Frees 'evg'. */ static void eviction_group_destroy(struct oftable *table, struct eviction_group *evg) OVS_REQUIRES(ofproto_mutex) { while (!heap_is_empty(&evg->rules)) { struct rule *rule; rule = CONTAINER_OF(heap_pop(&evg->rules), struct rule, evg_node); rule->eviction_group = NULL; } hmap_remove(&table->eviction_groups_by_id, &evg->id_node); heap_remove(&table->eviction_groups_by_size, &evg->size_node); heap_destroy(&evg->rules); free(evg); } /* Removes 'rule' from its eviction group, if any. */ static void eviction_group_remove_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex) { if (rule->eviction_group) { struct oftable *table = &rule->ofproto->tables[rule->table_id]; struct eviction_group *evg = rule->eviction_group; rule->eviction_group = NULL; heap_remove(&evg->rules, &rule->evg_node); if (heap_is_empty(&evg->rules)) { eviction_group_destroy(table, evg); } else { eviction_group_resized(table, evg); } } } /* Hashes the 'rule''s values for the eviction_fields of 'rule''s table, and * returns the hash value. */ static uint32_t eviction_group_hash_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex) { struct oftable *table = &rule->ofproto->tables[rule->table_id]; const struct mf_subfield *sf; struct flow flow; uint32_t hash; hash = table->eviction_group_id_basis; miniflow_expand(rule->cr.match.flow, &flow); for (sf = table->eviction_fields; sf < &table->eviction_fields[table->n_eviction_fields]; sf++) { if (mf_are_prereqs_ok(sf->field, &flow)) { union mf_value value; mf_get_value(sf->field, &flow, &value); if (sf->ofs) { bitwise_zero(&value, sf->field->n_bytes, 0, sf->ofs); } if (sf->ofs + sf->n_bits < sf->field->n_bytes * 8) { unsigned int start = sf->ofs + sf->n_bits; bitwise_zero(&value, sf->field->n_bytes, start, sf->field->n_bytes * 8 - start); } hash = hash_bytes(&value, sf->field->n_bytes, hash); } else { hash = hash_int(hash, 0); } } return hash; } /* Returns an eviction group within 'table' with the given 'id', creating one * if necessary. */ static struct eviction_group * eviction_group_find(struct oftable *table, uint32_t id) OVS_REQUIRES(ofproto_mutex) { struct eviction_group *evg; HMAP_FOR_EACH_WITH_HASH (evg, id_node, id, &table->eviction_groups_by_id) { return evg; } evg = xmalloc(sizeof *evg); hmap_insert(&table->eviction_groups_by_id, &evg->id_node, id); heap_insert(&table->eviction_groups_by_size, &evg->size_node, eviction_group_priority(0)); heap_init(&evg->rules); return evg; } /* Returns an eviction priority for 'rule'. The return value should be * interpreted so that higher priorities make a rule a more attractive * candidate for eviction. */ static uint64_t rule_eviction_priority(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { /* Calculate absolute time when this flow will expire. If it will never * expire, then return 0 to make it unevictable. */ long long int expiration = LLONG_MAX; if (rule->hard_timeout) { /* 'modified' needs protection even when we hold 'ofproto_mutex'. */ ovs_mutex_lock(&rule->mutex); long long int modified = rule->modified; ovs_mutex_unlock(&rule->mutex); expiration = modified + rule->hard_timeout * 1000; } if (rule->idle_timeout) { uint64_t packets, bytes; long long int used; long long int idle_expiration; ofproto->ofproto_class->rule_get_stats(rule, &packets, &bytes, &used); idle_expiration = used + rule->idle_timeout * 1000; expiration = MIN(expiration, idle_expiration); } if (expiration == LLONG_MAX) { return 0; } /* Calculate the time of expiration as a number of (approximate) seconds * after program startup. * * This should work OK for program runs that last UINT32_MAX seconds or * less. Therefore, please restart OVS at least once every 136 years. */ uint32_t expiration_ofs = (expiration >> 10) - (time_boot_msec() >> 10); /* Combine expiration time with OpenFlow "importance" to form a single * priority value. We want flows with relatively low "importance" to be * evicted before even considering expiration time, so put "importance" in * the most significant bits and expiration time in the least significant * bits. * * Small 'priority' should be evicted before those with large 'priority'. * The caller expects the opposite convention (a large return value being * more attractive for eviction) so we invert it before returning. */ uint64_t priority = ((uint64_t) rule->importance << 32) + expiration_ofs; return UINT64_MAX - priority; } /* Adds 'rule' to an appropriate eviction group for its oftable's * configuration. Does nothing if 'rule''s oftable doesn't have eviction * enabled, or if 'rule' is a permanent rule (one that will never expire on its * own). * * The caller must ensure that 'rule' is not already in an eviction group. */ static void eviction_group_add_rule(struct rule *rule) OVS_REQUIRES(ofproto_mutex) { struct ofproto *ofproto = rule->ofproto; struct oftable *table = &ofproto->tables[rule->table_id]; bool has_timeout; /* Timeouts may be modified only when holding 'ofproto_mutex'. We have it * so no additional protection is needed. */ has_timeout = rule->hard_timeout || rule->idle_timeout; if (table->eviction && has_timeout) { struct eviction_group *evg; evg = eviction_group_find(table, eviction_group_hash_rule(rule)); rule->eviction_group = evg; heap_insert(&evg->rules, &rule->evg_node, rule_eviction_priority(ofproto, rule)); eviction_group_resized(table, evg); } } /* oftables. */ /* Initializes 'table'. */ static void oftable_init(struct oftable *table) { memset(table, 0, sizeof *table); classifier_init(&table->cls, flow_segment_u64s); table->max_flows = UINT_MAX; table->n_flows = 0; hmap_init(&table->eviction_groups_by_id); heap_init(&table->eviction_groups_by_size); atomic_init(&table->miss_config, OFPUTIL_TABLE_MISS_DEFAULT); classifier_set_prefix_fields(&table->cls, default_prefix_fields, ARRAY_SIZE(default_prefix_fields)); atomic_init(&table->n_matched, 0); atomic_init(&table->n_missed, 0); } /* Destroys 'table', including its classifier and eviction groups. * * The caller is responsible for freeing 'table' itself. */ static void oftable_destroy(struct oftable *table) { ovs_assert(classifier_is_empty(&table->cls)); ovs_mutex_lock(&ofproto_mutex); oftable_configure_eviction(table, 0, NULL, 0); ovs_mutex_unlock(&ofproto_mutex); hmap_destroy(&table->eviction_groups_by_id); heap_destroy(&table->eviction_groups_by_size); classifier_destroy(&table->cls); free(table->name); } /* Changes the name of 'table' to 'name'. If 'name' is NULL or the empty * string, then 'table' will use its default name. * * This only affects the name exposed for a table exposed through the OpenFlow * OFPST_TABLE (as printed by "ovs-ofctl dump-tables"). */ static void oftable_set_name(struct oftable *table, const char *name) { if (name && name[0]) { int len = strnlen(name, OFP_MAX_TABLE_NAME_LEN); if (!table->name || strncmp(name, table->name, len)) { free(table->name); table->name = xmemdup0(name, len); } } else { free(table->name); table->name = NULL; } } /* oftables support a choice of two policies when adding a rule would cause the * number of flows in the table to exceed the configured maximum number: either * they can refuse to add the new flow or they can evict some existing flow. * This function configures the latter policy on 'table', with fairness based * on the values of the 'n_fields' fields specified in 'fields'. (Specifying * 'n_fields' as 0 disables fairness.) */ static void oftable_configure_eviction(struct oftable *table, unsigned int eviction, const struct mf_subfield *fields, size_t n_fields) OVS_REQUIRES(ofproto_mutex) { struct rule *rule; if ((table->eviction != 0) == (eviction != 0) && n_fields == table->n_eviction_fields && (!n_fields || !memcmp(fields, table->eviction_fields, n_fields * sizeof *fields))) { /* The set of eviction fields did not change. If 'eviction' changed, * it remains nonzero, so that we can just update table->eviction * without fussing with the eviction groups. */ table->eviction = eviction; return; } /* Destroy existing eviction groups, then destroy and recreate data * structures to recover memory. */ struct eviction_group *evg, *next; HMAP_FOR_EACH_SAFE (evg, next, id_node, &table->eviction_groups_by_id) { eviction_group_destroy(table, evg); } hmap_destroy(&table->eviction_groups_by_id); hmap_init(&table->eviction_groups_by_id); heap_destroy(&table->eviction_groups_by_size); heap_init(&table->eviction_groups_by_size); /* Replace eviction groups by the new ones, if there is a change. Free the * old fields only after allocating the new ones, because 'fields == * table->eviction_fields' is possible. */ struct mf_subfield *old_fields = table->eviction_fields; table->n_eviction_fields = n_fields; table->eviction_fields = (fields ? xmemdup(fields, n_fields * sizeof *fields) : NULL); free(old_fields); /* Add the new eviction groups, if enabled. */ table->eviction = eviction; if (table->eviction) { table->eviction_group_id_basis = random_uint32(); CLS_FOR_EACH (rule, cr, &table->cls) { eviction_group_add_rule(rule); } } } /* Inserts 'rule' from the ofproto data structures BEFORE caller has inserted * it to the classifier. */ static void ofproto_rule_insert__(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { const struct rule_actions *actions = rule_get_actions(rule); ovs_assert(rule->removed); if (rule->hard_timeout || rule->idle_timeout) { list_insert(&ofproto->expirable, &rule->expirable); } cookies_insert(ofproto, rule); eviction_group_add_rule(rule); if (actions->has_meter) { meter_insert_rule(rule); } rule->removed = false; } /* Removes 'rule' from the ofproto data structures. Caller may have deferred * the removal from the classifier. */ static void ofproto_rule_remove__(struct ofproto *ofproto, struct rule *rule) OVS_REQUIRES(ofproto_mutex) { ovs_assert(!rule->removed); cookies_remove(ofproto, rule); eviction_group_remove_rule(rule); if (!list_is_empty(&rule->expirable)) { list_remove(&rule->expirable); } if (!list_is_empty(&rule->meter_list_node)) { list_remove(&rule->meter_list_node); list_init(&rule->meter_list_node); } rule->removed = true; } /* unixctl commands. */ struct ofproto * ofproto_lookup(const char *name) { struct ofproto *ofproto; HMAP_FOR_EACH_WITH_HASH (ofproto, hmap_node, hash_string(name, 0), &all_ofprotos) { if (!strcmp(ofproto->name, name)) { return ofproto; } } return NULL; } static void ofproto_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ofproto *ofproto; struct ds results; ds_init(&results); HMAP_FOR_EACH (ofproto, hmap_node, &all_ofprotos) { ds_put_format(&results, "%s\n", ofproto->name); } unixctl_command_reply(conn, ds_cstr(&results)); ds_destroy(&results); } static void ofproto_unixctl_init(void) { static bool registered; if (registered) { return; } registered = true; unixctl_command_register("ofproto/list", "", 0, 0, ofproto_unixctl_list, NULL); } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ /* Sets a 1-bit in the 4096-bit 'vlan_bitmap' for each VLAN ID that is matched * (exactly) by an OpenFlow rule in 'ofproto'. */ void ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap) { struct match match; struct cls_rule target; const struct oftable *oftable; match_init_catchall(&match); match_set_vlan_vid_masked(&match, htons(VLAN_CFI), htons(VLAN_CFI)); cls_rule_init(&target, &match, 0); free(ofproto->vlan_bitmap); ofproto->vlan_bitmap = bitmap_allocate(4096); ofproto->vlans_changed = false; OFPROTO_FOR_EACH_TABLE (oftable, ofproto) { struct rule *rule; CLS_FOR_EACH_TARGET (rule, cr, &oftable->cls, &target, CLS_MAX_VERSION) { if (minimask_get_vid_mask(rule->cr.match.mask) == VLAN_VID_MASK) { uint16_t vid = miniflow_get_vid(rule->cr.match.flow); bitmap_set1(vlan_bitmap, vid); bitmap_set1(ofproto->vlan_bitmap, vid); } } } cls_rule_destroy(&target); } /* Returns true if new VLANs have come into use by the flow table since the * last call to ofproto_get_vlan_usage(). * * We don't track when old VLANs stop being used. */ bool ofproto_has_vlan_usage_changed(const struct ofproto *ofproto) { return ofproto->vlans_changed; } /* Configures a VLAN splinter binding between the ports identified by OpenFlow * port numbers 'vlandev_ofp_port' and 'realdev_ofp_port'. If * 'realdev_ofp_port' is nonzero, then the VLAN device is enslaved to the real * device as a VLAN splinter for VLAN ID 'vid'. If 'realdev_ofp_port' is zero, * then the VLAN device is un-enslaved. */ int ofproto_port_set_realdev(struct ofproto *ofproto, ofp_port_t vlandev_ofp_port, ofp_port_t realdev_ofp_port, int vid) { struct ofport *ofport; int error; ovs_assert(vlandev_ofp_port != realdev_ofp_port); ofport = ofproto_get_port(ofproto, vlandev_ofp_port); if (!ofport) { VLOG_WARN("%s: cannot set realdev on nonexistent port %"PRIu16, ofproto->name, vlandev_ofp_port); return EINVAL; } if (!ofproto->ofproto_class->set_realdev) { if (!vlandev_ofp_port) { return 0; } VLOG_WARN("%s: vlan splinters not supported", ofproto->name); return EOPNOTSUPP; } error = ofproto->ofproto_class->set_realdev(ofport, realdev_ofp_port, vid); if (error) { VLOG_WARN("%s: setting realdev on port %"PRIu16" (%s) failed (%s)", ofproto->name, vlandev_ofp_port, netdev_get_name(ofport->netdev), ovs_strerror(error)); } return error; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/bundles.h0000644000000000000000000000013113534540071017140 xustar0030 mtime=1567801401.617682756 30 atime=1567801402.101686312 29 ctime=1567801425.06185549 openvswitch-2.5.9/ofproto/bundles.h0000644000175000017500000000557513534540071020643 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2013, 2014 Alexandru Copot , with support from IXIA. * Copyright (c) 2013, 2014 Daniel Baluta * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BUNDLES_H #define BUNDLES_H 1 #include #include "connmgr.h" #include "ofp-msgs.h" #include "ofp-util.h" #include "ofproto-provider.h" #include "util.h" #ifdef __cplusplus extern "C" { #endif struct ofp_bundle_entry { struct ovs_list node; enum ofptype type; /* OFPTYPE_FLOW_MOD or OFPTYPE_PORT_MOD. */ union { struct ofproto_flow_mod ofm; /* ofm.fm.ofpacts must be malloced. */ struct ofproto_port_mod opm; }; /* OpenFlow header and some of the message contents for error reporting. */ struct ofp_header ofp_msg[DIV_ROUND_UP(64, sizeof(struct ofp_header))]; }; enum bundle_state { BS_OPEN, BS_CLOSED }; struct ofp_bundle { struct hmap_node node; /* In struct ofconn's "bundles" hmap. */ uint32_t id; uint16_t flags; enum bundle_state state; /* List of 'struct bundle_message's */ struct ovs_list msg_list; }; static inline struct ofp_bundle_entry *ofp_bundle_entry_alloc( enum ofptype type, const struct ofp_header *oh); static inline void ofp_bundle_entry_free(struct ofp_bundle_entry *); enum ofperr ofp_bundle_open(struct ofconn *, uint32_t id, uint16_t flags); enum ofperr ofp_bundle_close(struct ofconn *, uint32_t id, uint16_t flags); enum ofperr ofp_bundle_discard(struct ofconn *, uint32_t id); enum ofperr ofp_bundle_add_message(struct ofconn *, uint32_t id, uint16_t flags, struct ofp_bundle_entry *); void ofp_bundle_remove__(struct ofconn *, struct ofp_bundle *, bool success); static inline struct ofp_bundle_entry * ofp_bundle_entry_alloc(enum ofptype type, const struct ofp_header *oh) { struct ofp_bundle_entry *entry = xmalloc(sizeof *entry); entry->type = type; /* Max 64 bytes for error reporting. */ memcpy(entry->ofp_msg, oh, MIN(ntohs(oh->length), sizeof entry->ofp_msg)); return entry; } static inline void ofp_bundle_entry_free(struct ofp_bundle_entry *entry) { if (entry) { if (entry->type == OFPTYPE_FLOW_MOD) { free(entry->ofm.fm.ofpacts); } free(entry); } } #ifdef __cplusplus } #endif #endif openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-sflow.c0000644000000000000000000000013113534540071021237 xustar0030 mtime=1567801401.637682902 29 atime=1567801402.10968637 30 ctime=1567801425.045855373 openvswitch-2.5.9/ofproto/ofproto-dpif-sflow.c0000644000175000017500000012706213534540071022736 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * Copyright (c) 2009 InMon Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto-dpif-sflow.h" #include #include #include #include #include #include "collectors.h" #include "compiler.h" #include "dpif.h" #include "hash.h" #include "hmap.h" #include "netdev.h" #include "netlink.h" #include "ofpbuf.h" #include "ofproto.h" #include "packets.h" #include "poll-loop.h" #include "ovs-router.h" #include "route-table.h" #include "sflow_api.h" #include "socket-util.h" #include "timeval.h" #include "openvswitch/vlog.h" #include "lib/odp-util.h" #include "lib/unaligned.h" #include "ofproto-provider.h" #include "lacp.h" VLOG_DEFINE_THIS_MODULE(sflow); static struct ovs_mutex mutex; /* This global var is used to determine which sFlow sub-agent should send the datapath counters. */ #define SFLOW_GC_SUBID_UNCLAIMED (uint32_t)-1 static uint32_t sflow_global_counters_subid = SFLOW_GC_SUBID_UNCLAIMED; /* * The enum dpif_sflow_tunnel_type is to declare the types supported */ enum dpif_sflow_tunnel_type { DPIF_SFLOW_TUNNEL_UNKNOWN = 0, DPIF_SFLOW_TUNNEL_VXLAN, DPIF_SFLOW_TUNNEL_GRE, DPIF_SFLOW_TUNNEL_LISP, DPIF_SFLOW_TUNNEL_IPSEC_GRE, DPIF_SFLOW_TUNNEL_GENEVE }; struct dpif_sflow_port { struct hmap_node hmap_node; /* In struct dpif_sflow's "ports" hmap. */ SFLDataSource_instance dsi; /* sFlow library's notion of port number. */ struct ofport *ofport; /* To retrive port stats. */ odp_port_t odp_port; enum dpif_sflow_tunnel_type tunnel_type; }; struct dpif_sflow { struct collectors *collectors; SFLAgent *sflow_agent; struct ofproto_sflow_options *options; time_t next_tick; size_t n_flood, n_all; struct hmap ports; /* Contains "struct dpif_sflow_port"s. */ uint32_t probability; struct ovs_refcount ref_cnt; }; static void dpif_sflow_del_port__(struct dpif_sflow *, struct dpif_sflow_port *); #define RECEIVER_INDEX 1 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static bool nullable_string_is_equal(const char *a, const char *b) { return a ? b && !strcmp(a, b) : !b; } static bool ofproto_sflow_options_equal(const struct ofproto_sflow_options *a, const struct ofproto_sflow_options *b) { return (sset_equals(&a->targets, &b->targets) && a->sampling_rate == b->sampling_rate && a->polling_interval == b->polling_interval && a->header_len == b->header_len && a->sub_id == b->sub_id && nullable_string_is_equal(a->agent_device, b->agent_device) && nullable_string_is_equal(a->control_ip, b->control_ip)); } static struct ofproto_sflow_options * ofproto_sflow_options_clone(const struct ofproto_sflow_options *old) { struct ofproto_sflow_options *new = xmemdup(old, sizeof *old); sset_clone(&new->targets, &old->targets); new->agent_device = old->agent_device ? xstrdup(old->agent_device) : NULL; new->control_ip = old->control_ip ? xstrdup(old->control_ip) : NULL; return new; } static void ofproto_sflow_options_destroy(struct ofproto_sflow_options *options) { if (options) { sset_destroy(&options->targets); free(options->agent_device); free(options->control_ip); free(options); } } /* sFlow library callback to allocate memory. */ static void * sflow_agent_alloc_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, size_t bytes) { return xzalloc(bytes); } /* sFlow library callback to free memory. */ static int sflow_agent_free_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, void *obj) { free(obj); return 0; } /* sFlow library callback to report error. */ static void sflow_agent_error_cb(void *magic OVS_UNUSED, SFLAgent *agent OVS_UNUSED, char *msg) { VLOG_WARN("sFlow agent error: %s", msg); } /* sFlow library callback to send datagram. */ static void sflow_agent_send_packet_cb(void *ds_, SFLAgent *agent OVS_UNUSED, SFLReceiver *receiver OVS_UNUSED, u_char *pkt, uint32_t pktLen) { struct dpif_sflow *ds = ds_; collectors_send(ds->collectors, pkt, pktLen); } static struct dpif_sflow_port * dpif_sflow_find_port(const struct dpif_sflow *ds, odp_port_t odp_port) OVS_REQUIRES(mutex) { struct dpif_sflow_port *dsp; HMAP_FOR_EACH_IN_BUCKET (dsp, hmap_node, hash_odp_port(odp_port), &ds->ports) { if (dsp->odp_port == odp_port) { return dsp; } } return NULL; } /* Call to get the datapath stats. Modeled after the dpctl utility. * * It might be more efficient for this module to be given a handle it can use * to get these stats more efficiently, but this is only going to be called * once every 20-30 seconds. Return number of datapaths found (normally expect * 1). */ static int sflow_get_dp_stats(struct dpif_sflow *ds OVS_UNUSED, struct dpif_dp_stats *dp_totals) { struct sset types; const char *type; int count = 0; memset(dp_totals, 0, sizeof *dp_totals); sset_init(&types); dp_enumerate_types(&types); SSET_FOR_EACH (type, &types) { struct sset names; const char *name; sset_init(&names); if (dp_enumerate_names(type, &names) == 0) { SSET_FOR_EACH (name, &names) { struct dpif *dpif; if (dpif_open(name, type, &dpif) == 0) { struct dpif_dp_stats dp_stats; if (dpif_get_dp_stats(dpif, &dp_stats) == 0) { count++; dp_totals->n_hit += dp_stats.n_hit; dp_totals->n_missed += dp_stats.n_missed; dp_totals->n_lost += dp_stats.n_lost; dp_totals->n_flows += dp_stats.n_flows; dp_totals->n_mask_hit += dp_stats.n_mask_hit; dp_totals->n_masks += dp_stats.n_masks; } dpif_close(dpif); } } sset_destroy(&names); } } sset_destroy(&types); return count; } /* If there are multiple bridges defined then we need some minimal artibration to decide which one should send the global counters. This function allows each sub-agent to ask if he should do it or not. */ static bool sflow_global_counters_subid_test(uint32_t subid) OVS_REQUIRES(mutex) { if (sflow_global_counters_subid == SFLOW_GC_SUBID_UNCLAIMED) { /* The role is up for grabs. */ sflow_global_counters_subid = subid; } return (sflow_global_counters_subid == subid); } static void sflow_global_counters_subid_clear(uint32_t subid) OVS_REQUIRES(mutex) { if (sflow_global_counters_subid == subid) { /* The sub-agent that was sending global counters is going away, so reset to allow another to take over. */ sflow_global_counters_subid = SFLOW_GC_SUBID_UNCLAIMED; } } static void sflow_agent_get_global_counters(void *ds_, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) OVS_REQUIRES(mutex) { struct dpif_sflow *ds = ds_; SFLCounters_sample_element dp_elem, res_elem; struct dpif_dp_stats dp_totals; struct rusage usage; if (!sflow_global_counters_subid_test(poller->agent->subId)) { /* Another sub-agent is currently responsible for this. */ return; } /* datapath stats */ if (sflow_get_dp_stats(ds, &dp_totals)) { dp_elem.tag = SFLCOUNTERS_OVSDP; dp_elem.counterBlock.ovsdp.n_hit = dp_totals.n_hit; dp_elem.counterBlock.ovsdp.n_missed = dp_totals.n_missed; dp_elem.counterBlock.ovsdp.n_lost = dp_totals.n_lost; dp_elem.counterBlock.ovsdp.n_mask_hit = dp_totals.n_mask_hit; dp_elem.counterBlock.ovsdp.n_flows = dp_totals.n_flows; dp_elem.counterBlock.ovsdp.n_masks = dp_totals.n_masks; SFLADD_ELEMENT(cs, &dp_elem); } /* resource usage */ getrusage(RUSAGE_SELF, &usage); res_elem.tag = SFLCOUNTERS_APP_RESOURCES; res_elem.counterBlock.appResources.user_time = timeval_to_msec(&usage.ru_utime); res_elem.counterBlock.appResources.system_time = timeval_to_msec(&usage.ru_stime); res_elem.counterBlock.appResources.mem_used = (usage.ru_maxrss * 1024); SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.mem_max); SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.fd_open); SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.fd_max); SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.conn_open); SFL_UNDEF_GAUGE(res_elem.counterBlock.appResources.conn_max); SFLADD_ELEMENT(cs, &res_elem); sfl_poller_writeCountersSample(poller, cs); } static void sflow_agent_get_counters(void *ds_, SFLPoller *poller, SFL_COUNTERS_SAMPLE_TYPE *cs) OVS_REQUIRES(mutex) { struct dpif_sflow *ds = ds_; SFLCounters_sample_element elem, lacp_elem, of_elem, name_elem; enum netdev_features current; struct dpif_sflow_port *dsp; SFLIf_counters *counters; struct netdev_stats stats; enum netdev_flags flags; struct lacp_slave_stats lacp_stats; const char *ifName; dsp = dpif_sflow_find_port(ds, u32_to_odp(poller->bridgePort)); if (!dsp) { return; } elem.tag = SFLCOUNTERS_GENERIC; counters = &elem.counterBlock.generic; counters->ifIndex = SFL_DS_INDEX(poller->dsi); counters->ifType = 6; if (!netdev_get_features(dsp->ofport->netdev, ¤t, NULL, NULL, NULL)) { /* The values of ifDirection come from MAU MIB (RFC 2668): 0 = unknown, 1 = full-duplex, 2 = half-duplex, 3 = in, 4=out */ counters->ifSpeed = netdev_features_to_bps(current, 0); counters->ifDirection = (netdev_features_is_full_duplex(current) ? 1 : 2); } else { counters->ifSpeed = 100000000; counters->ifDirection = 0; } if (!netdev_get_flags(dsp->ofport->netdev, &flags) && flags & NETDEV_UP) { counters->ifStatus = 1; /* ifAdminStatus up. */ if (netdev_get_carrier(dsp->ofport->netdev)) { counters->ifStatus |= 2; /* ifOperStatus us. */ } } else { counters->ifStatus = 0; /* Down. */ } /* XXX 1. Is the multicast counter filled in? 2. Does the multicast counter include broadcasts? 3. Does the rx_packets counter include multicasts/broadcasts? */ ofproto_port_get_stats(dsp->ofport, &stats); counters->ifInOctets = stats.rx_bytes; counters->ifInUcastPkts = stats.rx_packets; counters->ifInMulticastPkts = stats.multicast; counters->ifInBroadcastPkts = -1; counters->ifInDiscards = stats.rx_dropped; counters->ifInErrors = stats.rx_errors; counters->ifInUnknownProtos = -1; counters->ifOutOctets = stats.tx_bytes; counters->ifOutUcastPkts = stats.tx_packets; counters->ifOutMulticastPkts = -1; counters->ifOutBroadcastPkts = -1; counters->ifOutDiscards = stats.tx_dropped; counters->ifOutErrors = stats.tx_errors; counters->ifPromiscuousMode = 0; SFLADD_ELEMENT(cs, &elem); /* Include LACP counters and identifiers if this port is part of a LAG. */ if (ofproto_port_get_lacp_stats(dsp->ofport, &lacp_stats) == 0) { memset(&lacp_elem, 0, sizeof lacp_elem); lacp_elem.tag = SFLCOUNTERS_LACP; lacp_elem.counterBlock.lacp.actorSystemID = lacp_stats.dot3adAggPortActorSystemID; lacp_elem.counterBlock.lacp.partnerSystemID = lacp_stats.dot3adAggPortPartnerOperSystemID; lacp_elem.counterBlock.lacp.attachedAggID = lacp_stats.dot3adAggPortAttachedAggID; lacp_elem.counterBlock.lacp.portState.v.actorAdmin = lacp_stats.dot3adAggPortActorAdminState; lacp_elem.counterBlock.lacp.portState.v.actorOper = lacp_stats.dot3adAggPortActorOperState; lacp_elem.counterBlock.lacp.portState.v.partnerAdmin = lacp_stats.dot3adAggPortPartnerAdminState; lacp_elem.counterBlock.lacp.portState.v.partnerOper = lacp_stats.dot3adAggPortPartnerOperState; lacp_elem.counterBlock.lacp.LACPDUsRx = lacp_stats.dot3adAggPortStatsLACPDUsRx; SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsRx); SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsRx); SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.unknownRx); lacp_elem.counterBlock.lacp.illegalRx = lacp_stats.dot3adAggPortStatsIllegalRx; lacp_elem.counterBlock.lacp.LACPDUsTx = lacp_stats.dot3adAggPortStatsLACPDUsTx; SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerPDUsTx); SFL_UNDEF_COUNTER(lacp_elem.counterBlock.lacp.markerResponsePDUsTx); SFLADD_ELEMENT(cs, &lacp_elem); } /* Include Port name. */ if ((ifName = netdev_get_name(dsp->ofport->netdev)) != NULL) { memset(&name_elem, 0, sizeof name_elem); name_elem.tag = SFLCOUNTERS_PORTNAME; name_elem.counterBlock.portName.portName.str = (char *)ifName; name_elem.counterBlock.portName.portName.len = strlen(ifName); SFLADD_ELEMENT(cs, &name_elem); } /* Include OpenFlow DPID and openflow port number. */ memset(&of_elem, 0, sizeof of_elem); of_elem.tag = SFLCOUNTERS_OPENFLOWPORT; of_elem.counterBlock.ofPort.datapath_id = ofproto_get_datapath_id(dsp->ofport->ofproto); of_elem.counterBlock.ofPort.port_no = (OVS_FORCE uint32_t)dsp->ofport->ofp_port; SFLADD_ELEMENT(cs, &of_elem); sfl_poller_writeCountersSample(poller, cs); } /* Obtains an address to use for the local sFlow agent and stores it into * '*agent_addr'. Returns true if successful, false on failure. * * The sFlow agent address should be a local IP address that is persistent and * reachable over the network, if possible. The IP address associated with * 'agent_device' is used if it has one, and otherwise 'control_ip', the IP * address used to talk to the controller. If the agent device is not * specified then it is figured out by taking a look at the routing table based * on 'targets'. */ static bool sflow_choose_agent_address(const char *agent_device, const struct sset *targets, const char *control_ip, SFLAddress *agent_addr) { const char *target; struct in_addr in4; memset(agent_addr, 0, sizeof *agent_addr); agent_addr->type = SFLADDRESSTYPE_IP_V4; if (agent_device) { if (!netdev_get_in4_by_name(agent_device, &in4)) { goto success; } } SSET_FOR_EACH (target, targets) { union { struct sockaddr_storage ss; struct sockaddr_in sin; } sa; char name[IFNAMSIZ]; if (inet_parse_active(target, SFL_DEFAULT_COLLECTOR_PORT, &sa.ss) && sa.ss.ss_family == AF_INET) { ovs_be32 gw; if (ovs_router_lookup4(sa.sin.sin_addr.s_addr, name, &gw) && !netdev_get_in4_by_name(name, &in4)) { goto success; } } } if (control_ip && !lookup_ip(control_ip, &in4)) { goto success; } VLOG_ERR("could not determine IP address for sFlow agent"); return false; success: agent_addr->address.ip_v4.addr = (OVS_FORCE uint32_t) in4.s_addr; return true; } static void dpif_sflow_clear__(struct dpif_sflow *ds) OVS_REQUIRES(mutex) { if (ds->sflow_agent) { sflow_global_counters_subid_clear(ds->sflow_agent->subId); sfl_agent_release(ds->sflow_agent); free(ds->sflow_agent); ds->sflow_agent = NULL; } collectors_destroy(ds->collectors); ds->collectors = NULL; ofproto_sflow_options_destroy(ds->options); ds->options = NULL; /* Turn off sampling to save CPU cycles. */ ds->probability = 0; } void dpif_sflow_clear(struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); dpif_sflow_clear__(ds); ovs_mutex_unlock(&mutex); } bool dpif_sflow_is_enabled(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { bool enabled; ovs_mutex_lock(&mutex); enabled = ds->collectors != NULL; ovs_mutex_unlock(&mutex); return enabled; } struct dpif_sflow * dpif_sflow_create(void) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; struct dpif_sflow *ds; if (ovsthread_once_start(&once)) { ovs_mutex_init_recursive(&mutex); ovsthread_once_done(&once); } ds = xcalloc(1, sizeof *ds); ds->next_tick = time_now() + 1; hmap_init(&ds->ports); ds->probability = 0; ovs_refcount_init(&ds->ref_cnt); return ds; } struct dpif_sflow * dpif_sflow_ref(const struct dpif_sflow *ds_) { struct dpif_sflow *ds = CONST_CAST(struct dpif_sflow *, ds_); if (ds) { ovs_refcount_ref(&ds->ref_cnt); } return ds; } /* 32-bit fraction of packets to sample with. A value of 0 samples no packets, * a value of %UINT32_MAX samples all packets and intermediate values sample * intermediate fractions of packets. */ uint32_t dpif_sflow_get_probability(const struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { uint32_t probability; ovs_mutex_lock(&mutex); probability = ds->probability; ovs_mutex_unlock(&mutex); return probability; } void dpif_sflow_unref(struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { if (ds && ovs_refcount_unref_relaxed(&ds->ref_cnt) == 1) { struct dpif_sflow_port *dsp, *next; dpif_sflow_clear(ds); HMAP_FOR_EACH_SAFE (dsp, next, hmap_node, &ds->ports) { dpif_sflow_del_port__(ds, dsp); } hmap_destroy(&ds->ports); free(ds); } } static void dpif_sflow_add_poller(struct dpif_sflow *ds, struct dpif_sflow_port *dsp) OVS_REQUIRES(mutex) { SFLPoller *poller = sfl_agent_addPoller(ds->sflow_agent, &dsp->dsi, ds, sflow_agent_get_counters); sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval); sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX); sfl_poller_set_bridgePort(poller, odp_to_u32(dsp->odp_port)); } static enum dpif_sflow_tunnel_type dpif_sflow_tunnel_type(struct ofport *ofport) { const char *type = netdev_get_type(ofport->netdev); if (type) { if (strcmp(type, "gre") == 0) { return DPIF_SFLOW_TUNNEL_GRE; } else if (strcmp(type, "ipsec_gre") == 0) { return DPIF_SFLOW_TUNNEL_IPSEC_GRE; } else if (strcmp(type, "vxlan") == 0) { return DPIF_SFLOW_TUNNEL_VXLAN; } else if (strcmp(type, "lisp") == 0) { return DPIF_SFLOW_TUNNEL_LISP; } else if (strcmp(type, "geneve") == 0) { return DPIF_SFLOW_TUNNEL_GENEVE; } } return DPIF_SFLOW_TUNNEL_UNKNOWN; } static uint8_t dpif_sflow_tunnel_proto(enum dpif_sflow_tunnel_type tunnel_type) { /* Default to 0 (IPPROTO_IP), meaning "unknown". */ uint8_t ipproto = 0; switch(tunnel_type) { case DPIF_SFLOW_TUNNEL_GRE: ipproto = IPPROTO_GRE; break; case DPIF_SFLOW_TUNNEL_IPSEC_GRE: ipproto = IPPROTO_ESP; break; case DPIF_SFLOW_TUNNEL_VXLAN: case DPIF_SFLOW_TUNNEL_LISP: case DPIF_SFLOW_TUNNEL_GENEVE: ipproto = IPPROTO_UDP; case DPIF_SFLOW_TUNNEL_UNKNOWN: break; } return ipproto; } void dpif_sflow_add_port(struct dpif_sflow *ds, struct ofport *ofport, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_sflow_port *dsp; int ifindex; enum dpif_sflow_tunnel_type tunnel_type; ovs_mutex_lock(&mutex); dpif_sflow_del_port(ds, odp_port); tunnel_type = dpif_sflow_tunnel_type(ofport); ifindex = netdev_get_ifindex(ofport->netdev); if (ifindex <= 0 && tunnel_type == DPIF_SFLOW_TUNNEL_UNKNOWN) { /* Not an ifindex port, and not a tunnel port either * so do not add a cross-reference to it here. */ goto out; } /* Add to table of ports. */ dsp = xmalloc(sizeof *dsp); dsp->ofport = ofport; dsp->odp_port = odp_port; dsp->tunnel_type = tunnel_type; hmap_insert(&ds->ports, &dsp->hmap_node, hash_odp_port(odp_port)); if (ifindex > 0) { /* Add poller for ports that have ifindex. */ SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, ifindex, 0); if (ds->sflow_agent) { dpif_sflow_add_poller(ds, dsp); } } else { /* Record "ifindex unknown" for the others */ SFL_DS_SET(dsp->dsi, SFL_DSCLASS_IFINDEX, 0, 0); } out: ovs_mutex_unlock(&mutex); } static void dpif_sflow_del_port__(struct dpif_sflow *ds, struct dpif_sflow_port *dsp) OVS_REQUIRES(mutex) { if (ds->sflow_agent && SFL_DS_INDEX(dsp->dsi)) { sfl_agent_removePoller(ds->sflow_agent, &dsp->dsi); sfl_agent_removeSampler(ds->sflow_agent, &dsp->dsi); } hmap_remove(&ds->ports, &dsp->hmap_node); free(dsp); } void dpif_sflow_del_port(struct dpif_sflow *ds, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_sflow_port *dsp; ovs_mutex_lock(&mutex); dsp = dpif_sflow_find_port(ds, odp_port); if (dsp) { dpif_sflow_del_port__(ds, dsp); } ovs_mutex_unlock(&mutex); } void dpif_sflow_set_options(struct dpif_sflow *ds, const struct ofproto_sflow_options *options) OVS_EXCLUDED(mutex) { struct dpif_sflow_port *dsp; bool options_changed; SFLReceiver *receiver; SFLAddress agentIP; time_t now; SFLDataSource_instance dsi; uint32_t dsIndex; SFLSampler *sampler; SFLPoller *poller; ovs_mutex_lock(&mutex); if (sset_is_empty(&options->targets) || !options->sampling_rate) { /* No point in doing any work if there are no targets or nothing to * sample. */ dpif_sflow_clear__(ds); goto out; } options_changed = (!ds->options || !ofproto_sflow_options_equal(options, ds->options)); /* Configure collectors if options have changed or if we're shortchanged in * collectors (which indicates that opening one or more of the configured * collectors failed, so that we should retry). */ if (options_changed || collectors_count(ds->collectors) < sset_count(&options->targets)) { collectors_destroy(ds->collectors); collectors_create(&options->targets, SFL_DEFAULT_COLLECTOR_PORT, &ds->collectors); if (ds->collectors == NULL) { VLOG_WARN_RL(&rl, "no collectors could be initialized, " "sFlow disabled"); dpif_sflow_clear__(ds); goto out; } } /* Choose agent IP address and agent device (if not yet setup) */ if (!sflow_choose_agent_address(options->agent_device, &options->targets, options->control_ip, &agentIP)) { dpif_sflow_clear__(ds); goto out; } /* Avoid reconfiguring if options didn't change. */ if (!options_changed) { goto out; } ofproto_sflow_options_destroy(ds->options); ds->options = ofproto_sflow_options_clone(options); /* Create agent. */ VLOG_INFO("creating sFlow agent %d", options->sub_id); if (ds->sflow_agent) { sflow_global_counters_subid_clear(ds->sflow_agent->subId); sfl_agent_release(ds->sflow_agent); } ds->sflow_agent = xcalloc(1, sizeof *ds->sflow_agent); now = time_wall(); sfl_agent_init(ds->sflow_agent, &agentIP, options->sub_id, now, /* Boot time. */ now, /* Current time. */ ds, /* Pointer supplied to callbacks. */ sflow_agent_alloc_cb, sflow_agent_free_cb, sflow_agent_error_cb, sflow_agent_send_packet_cb); receiver = sfl_agent_addReceiver(ds->sflow_agent); sfl_receiver_set_sFlowRcvrOwner(receiver, "Open vSwitch sFlow"); sfl_receiver_set_sFlowRcvrTimeout(receiver, 0xffffffff); /* Set the sampling_rate down in the datapath. */ ds->probability = MAX(1, UINT32_MAX / ds->options->sampling_rate); /* Add a single sampler for the bridge. This appears as a PHYSICAL_ENTITY because it is associated with the hypervisor, and interacts with the server hardware directly. The sub_id is used to distinguish this sampler from others on other bridges within the same agent. */ dsIndex = 1000 + options->sub_id; SFL_DS_SET(dsi, SFL_DSCLASS_PHYSICAL_ENTITY, dsIndex, 0); sampler = sfl_agent_addSampler(ds->sflow_agent, &dsi); sfl_sampler_set_sFlowFsPacketSamplingRate(sampler, ds->options->sampling_rate); sfl_sampler_set_sFlowFsMaximumHeaderSize(sampler, ds->options->header_len); sfl_sampler_set_sFlowFsReceiver(sampler, RECEIVER_INDEX); /* Add a counter poller for the bridge so we can use it to send global counters such as datapath cache hit/miss stats. */ poller = sfl_agent_addPoller(ds->sflow_agent, &dsi, ds, sflow_agent_get_global_counters); sfl_poller_set_sFlowCpInterval(poller, ds->options->polling_interval); sfl_poller_set_sFlowCpReceiver(poller, RECEIVER_INDEX); /* Add pollers for the currently known ifindex-ports */ HMAP_FOR_EACH (dsp, hmap_node, &ds->ports) { if (SFL_DS_INDEX(dsp->dsi)) { dpif_sflow_add_poller(ds, dsp); } } out: ovs_mutex_unlock(&mutex); } int dpif_sflow_odp_port_to_ifindex(const struct dpif_sflow *ds, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_sflow_port *dsp; int ret; ovs_mutex_lock(&mutex); dsp = dpif_sflow_find_port(ds, odp_port); ret = dsp ? SFL_DS_INDEX(dsp->dsi) : 0; ovs_mutex_unlock(&mutex); return ret; } static void dpif_sflow_tunnel_v4(uint8_t tunnel_ipproto, const struct flow_tnl *tunnel, SFLSampled_ipv4 *ipv4) { ipv4->protocol = tunnel_ipproto; ipv4->tos = tunnel->ip_tos; ipv4->src_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_src; ipv4->dst_ip.addr = (OVS_FORCE uint32_t) tunnel->ip_dst; ipv4->src_port = (OVS_FORCE uint16_t) tunnel->tp_src; ipv4->dst_port = (OVS_FORCE uint16_t) tunnel->tp_dst; } static void dpif_sflow_push_mpls_lse(struct dpif_sflow_actions *sflow_actions, ovs_be32 lse) { if (sflow_actions->mpls_stack_depth >= FLOW_MAX_MPLS_LABELS) { sflow_actions->mpls_err = true; return; } /* Record the new lse in host-byte-order. */ /* BOS flag will be fixed later when we send stack to sFlow library. */ sflow_actions->mpls_lse[sflow_actions->mpls_stack_depth++] = ntohl(lse); } static void dpif_sflow_pop_mpls_lse(struct dpif_sflow_actions *sflow_actions) { if (sflow_actions->mpls_stack_depth == 0) { sflow_actions->mpls_err = true; return; } sflow_actions->mpls_stack_depth--; } static void dpif_sflow_set_mpls(struct dpif_sflow_actions *sflow_actions, const struct ovs_key_mpls *mpls_key, int n) { int ii; if (n > FLOW_MAX_MPLS_LABELS) { sflow_actions->mpls_err = true; return; } for (ii = 0; ii < n; ii++) { /* Reverse stack order, and use host-byte-order for each lse. */ sflow_actions->mpls_lse[n - ii - 1] = ntohl(mpls_key[ii].mpls_lse); } sflow_actions->mpls_stack_depth = n; } static void sflow_read_tnl_push_action(const struct nlattr *attr, struct dpif_sflow_actions *sflow_actions) { /* Modeled on lib/odp-util.c: format_odp_tnl_push_header */ const struct ovs_action_push_tnl *data = nl_attr_get(attr); const struct eth_header *eth = ALIGNED_CAST(const struct eth_header *, data->header); const struct ip_header *ip = ALIGNED_CAST(const struct ip_header *, eth + 1); sflow_actions->out_port = u32_to_odp(data->out_port); /* Ethernet. */ /* TODO: SFlow does not currently define a MAC-in-MAC * encapsulation structure. We could use an extension * structure to report this. */ /* IPv4 */ /* Cannot assume alignment so just use memcpy. */ sflow_actions->tunnel.ip_src = get_16aligned_be32(&ip->ip_src); sflow_actions->tunnel.ip_dst = get_16aligned_be32(&ip->ip_dst); sflow_actions->tunnel.ip_tos = ip->ip_tos; sflow_actions->tunnel.ip_ttl = ip->ip_ttl; /* The tnl_push action can supply the ip_protocol too. */ sflow_actions->tunnel_ipproto = ip->ip_proto; /* Layer 4 */ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN || data->tnl_type == OVS_VPORT_TYPE_GENEVE) { const struct udp_header *udp = (const struct udp_header *) (ip + 1); sflow_actions->tunnel.tp_src = udp->udp_src; sflow_actions->tunnel.tp_dst = udp->udp_dst; if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) { const struct vxlanhdr *vxh = (const struct vxlanhdr *) (udp + 1); uint64_t tun_id = ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8; sflow_actions->tunnel.tun_id = htonll(tun_id); } else { const struct genevehdr *gnh = (const struct genevehdr *) (udp + 1); uint64_t tun_id = ntohl(get_16aligned_be32(&gnh->vni)) >> 8; sflow_actions->tunnel.tun_id = htonll(tun_id); } } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) { const void *l4 = ip + 1; const struct gre_base_hdr *greh = (const struct gre_base_hdr *) l4; ovs_16aligned_be32 *options = (ovs_16aligned_be32 *)(greh + 1); if (greh->flags & htons(GRE_CSUM)) { options++; } if (greh->flags & htons(GRE_KEY)) { uint64_t tun_id = ntohl(get_16aligned_be32(options)); sflow_actions->tunnel.tun_id = htonll(tun_id); } } } static void sflow_read_set_action(const struct nlattr *attr, struct dpif_sflow_actions *sflow_actions) { enum ovs_key_attr type = nl_attr_type(attr); switch (type) { case OVS_KEY_ATTR_ENCAP: if (++sflow_actions->encap_depth > 1) { /* Do not handle multi-encap for now. */ sflow_actions->tunnel_err = true; } else { dpif_sflow_read_actions(NULL, nl_attr_get(attr), nl_attr_get_size(attr), sflow_actions); } break; case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_DP_HASH: case OVS_KEY_ATTR_RECIRC_ID: break; case OVS_KEY_ATTR_TUNNEL: { if (++sflow_actions->encap_depth > 1) { /* Do not handle multi-encap for now. */ sflow_actions->tunnel_err = true; } else { if (odp_tun_key_from_attr(attr, false, &sflow_actions->tunnel) == ODP_FIT_ERROR) { /* Tunnel parsing error. */ sflow_actions->tunnel_err = true; } } break; } case OVS_KEY_ATTR_IN_PORT: case OVS_KEY_ATTR_ETHERNET: case OVS_KEY_ATTR_VLAN: break; case OVS_KEY_ATTR_MPLS: { const struct ovs_key_mpls *mpls_key = nl_attr_get(attr); size_t size = nl_attr_get_size(attr); dpif_sflow_set_mpls(sflow_actions, mpls_key, size / sizeof *mpls_key); break; } case OVS_KEY_ATTR_ETHERTYPE: case OVS_KEY_ATTR_IPV4: if (sflow_actions->encap_depth == 1) { const struct ovs_key_ipv4 *key = nl_attr_get(attr); if (key->ipv4_src) { sflow_actions->tunnel.ip_src = key->ipv4_src; } if (key->ipv4_dst) { sflow_actions->tunnel.ip_dst = key->ipv4_dst; } if (key->ipv4_proto) { sflow_actions->tunnel_ipproto = key->ipv4_proto; } if (key->ipv4_tos) { sflow_actions->tunnel.ip_tos = key->ipv4_tos; } if (key->ipv4_ttl) { sflow_actions->tunnel.ip_tos = key->ipv4_ttl; } } break; case OVS_KEY_ATTR_IPV6: /* TODO: parse IPv6 encap. */ break; /* These have the same structure and format. */ case OVS_KEY_ATTR_TCP: case OVS_KEY_ATTR_UDP: case OVS_KEY_ATTR_SCTP: if (sflow_actions->encap_depth == 1) { const struct ovs_key_tcp *key = nl_attr_get(attr); if (key->tcp_src) { sflow_actions->tunnel.tp_src = key->tcp_src; } if (key->tcp_dst) { sflow_actions->tunnel.tp_dst = key->tcp_dst; } } break; case OVS_KEY_ATTR_TCP_FLAGS: case OVS_KEY_ATTR_ICMP: case OVS_KEY_ATTR_ICMPV6: case OVS_KEY_ATTR_ARP: case OVS_KEY_ATTR_ND: case OVS_KEY_ATTR_CT_STATE: case OVS_KEY_ATTR_CT_ZONE: case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_LABELS: case OVS_KEY_ATTR_UNSPEC: case __OVS_KEY_ATTR_MAX: default: break; } } static void dpif_sflow_capture_input_mpls(const struct flow *flow, struct dpif_sflow_actions *sflow_actions) { if (eth_type_mpls(flow->dl_type)) { int depth = 0; int ii; ovs_be32 lse; /* Calculate depth by detecting BOS. */ for (ii = 0; ii < FLOW_MAX_MPLS_LABELS; ii++) { lse = flow->mpls_lse[ii]; depth++; if (lse & htonl(MPLS_BOS_MASK)) { break; } } /* Capture stack, reversing stack order, and * using host-byte-order for each lse. BOS flag * is ignored for now. It is set later when * the output stack is encoded. */ for (ii = 0; ii < depth; ii++) { lse = flow->mpls_lse[ii]; sflow_actions->mpls_lse[depth - ii - 1] = ntohl(lse); } sflow_actions->mpls_stack_depth = depth; } } void dpif_sflow_read_actions(const struct flow *flow, const struct nlattr *actions, size_t actions_len, struct dpif_sflow_actions *sflow_actions) { const struct nlattr *a; unsigned int left; if (actions_len == 0) { /* Packet dropped.*/ return; } if (flow != NULL) { /* Make sure the MPLS output stack * is seeded with the input stack. */ dpif_sflow_capture_input_mpls(flow, sflow_actions); /* XXX when 802.1AD(QinQ) is supported then * we can do the same with VLAN stacks here */ } NL_ATTR_FOR_EACH (a, left, actions, actions_len) { enum ovs_action_attr type = nl_attr_type(a); switch (type) { case OVS_ACTION_ATTR_OUTPUT: /* Capture the output port in case we need it * to get the output tunnel type. */ sflow_actions->out_port = u32_to_odp(nl_attr_get_u32(a)); break; case OVS_ACTION_ATTR_TUNNEL_POP: /* XXX: Do not handle this for now. It's not clear * if we should start with encap_depth == 1 when we * see an input tunnel, or if we should assume * that the input tunnel was always "popped" if it * was presented to us decoded in flow->tunnel? * * If we do handle this it might look like this, * as we clear the captured tunnel info and decrement * the encap_depth: * * memset(&sflow_actions->tunnel, 0, sizeof struct flow_tnl); * sflow_actions->tunnel_ipproto = 0; * --sflow_actions->encap_depth; * * but for now just disable the tunnel annotation: */ sflow_actions->tunnel_err = true; break; case OVS_ACTION_ATTR_TUNNEL_PUSH: /* XXX: This actions appears to come with it's own * OUTPUT action, so should it be regarded as having * an implicit "pop" following it too? Put another * way, would two tnl_push() actions in succession * result in a packet with two layers of encap? */ if (++sflow_actions->encap_depth > 1) { /* Do not handle multi-encap for now. */ sflow_actions->tunnel_err = true; } else { sflow_read_tnl_push_action(a, sflow_actions); } break; case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_CT: break; case OVS_ACTION_ATTR_SET_MASKED: /* TODO: apply mask. XXX: Are we likely to see this? */ break; case OVS_ACTION_ATTR_SET: sflow_read_set_action(nl_attr_get(a), sflow_actions); break; case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_POP_VLAN: /* TODO: 802.1AD(QinQ) is not supported by OVS (yet), so do not * construct a VLAN-stack. The sFlow user-action cookie already * captures the egress VLAN ID so there is nothing more to do here. */ break; case OVS_ACTION_ATTR_PUSH_MPLS: { const struct ovs_action_push_mpls *mpls = nl_attr_get(a); if (mpls) { dpif_sflow_push_mpls_lse(sflow_actions, mpls->mpls_lse); } break; } case OVS_ACTION_ATTR_POP_MPLS: { dpif_sflow_pop_mpls_lse(sflow_actions); break; } case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: break; } } } static void dpif_sflow_encode_mpls_stack(SFLLabelStack *stack, uint32_t *mpls_lse_buf, const struct dpif_sflow_actions *sflow_actions) { /* Put the MPLS stack back into "packet header" order, * and make sure the BOS flag is set correctly on the last * one. Each lse is still in host-byte-order. */ int ii; uint32_t lse; stack->depth = sflow_actions->mpls_stack_depth; stack->stack = mpls_lse_buf; for (ii = 0; ii < stack->depth; ii++) { lse = sflow_actions->mpls_lse[stack->depth - ii - 1]; stack->stack[ii] = (lse & ~MPLS_BOS_MASK); } stack->stack[stack->depth - 1] |= MPLS_BOS_MASK; } /* Extract the output port count from the user action cookie. * See http://sflow.org/sflow_version_5.txt "Input/Output port information" */ static uint32_t dpif_sflow_cookie_num_outputs(const union user_action_cookie *cookie) { uint32_t format = cookie->sflow.output & 0xC0000000; uint32_t port_n = cookie->sflow.output & 0x3FFFFFFF; if (format == 0) { return port_n ? 1 : 0; } else if (format == 0x80000000) { return port_n; } return 0; } void dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet, const struct flow *flow, odp_port_t odp_in_port, const union user_action_cookie *cookie, const struct dpif_sflow_actions *sflow_actions) OVS_EXCLUDED(mutex) { SFL_FLOW_SAMPLE_TYPE fs; SFLFlow_sample_element hdrElem; SFLSampled_header *header; SFLFlow_sample_element switchElem; uint8_t tnlInProto, tnlOutProto; SFLFlow_sample_element tnlInElem, tnlOutElem; SFLFlow_sample_element vniInElem, vniOutElem; SFLFlow_sample_element mplsElem; uint32_t mpls_lse_buf[FLOW_MAX_MPLS_LABELS]; SFLSampler *sampler; struct dpif_sflow_port *in_dsp; struct dpif_sflow_port *out_dsp; ovs_be16 vlan_tci; ovs_mutex_lock(&mutex); sampler = ds->sflow_agent->samplers; if (!sampler) { goto out; } /* Build a flow sample. */ memset(&fs, 0, sizeof fs); /* Look up the input ifIndex if this port has one. Otherwise just * leave it as 0 (meaning 'unknown') and continue. */ in_dsp = dpif_sflow_find_port(ds, odp_in_port); if (in_dsp) { fs.input = SFL_DS_INDEX(in_dsp->dsi); } /* Make the assumption that the random number generator in the datapath converges * to the configured mean, and just increment the samplePool by the configured * sampling rate every time. */ sampler->samplePool += sfl_sampler_get_sFlowFsPacketSamplingRate(sampler); /* Sampled header. */ memset(&hdrElem, 0, sizeof hdrElem); hdrElem.tag = SFLFLOW_HEADER; header = &hdrElem.flowType.header; header->header_protocol = SFLHEADER_ETHERNET_ISO8023; /* The frame_length should include the Ethernet FCS (4 bytes), * but it has already been stripped, so we need to add 4 here. */ header->frame_length = dp_packet_size(packet) + 4; /* Ethernet FCS stripped off. */ header->stripped = 4; header->header_length = MIN(dp_packet_size(packet), sampler->sFlowFsMaximumHeaderSize); header->header_bytes = dp_packet_data(packet); /* Add extended switch element. */ memset(&switchElem, 0, sizeof(switchElem)); switchElem.tag = SFLFLOW_EX_SWITCH; switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow->vlan_tci); switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow->vlan_tci); /* Retrieve data from user_action_cookie. */ vlan_tci = cookie->sflow.vlan_tci; switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(vlan_tci); switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(vlan_tci); fs.output = cookie->sflow.output; /* Input tunnel. */ if (flow->tunnel.ip_dst) { memset(&tnlInElem, 0, sizeof(tnlInElem)); tnlInElem.tag = SFLFLOW_EX_IPV4_TUNNEL_INGRESS; tnlInProto = in_dsp ? dpif_sflow_tunnel_proto(in_dsp->tunnel_type) : 0; dpif_sflow_tunnel_v4(tnlInProto, &flow->tunnel, &tnlInElem.flowType.ipv4); SFLADD_ELEMENT(&fs, &tnlInElem); if (flow->tunnel.tun_id) { memset(&vniInElem, 0, sizeof(vniInElem)); vniInElem.tag = SFLFLOW_EX_VNI_INGRESS; vniInElem.flowType.tunnel_vni.vni = ntohll(flow->tunnel.tun_id); SFLADD_ELEMENT(&fs, &vniInElem); } } /* Output tunnel. */ if (sflow_actions && sflow_actions->encap_depth == 1 && !sflow_actions->tunnel_err && dpif_sflow_cookie_num_outputs(cookie) == 1) { tnlOutProto = sflow_actions->tunnel_ipproto; if (tnlOutProto == 0) { /* Try to infer the ip-protocol from the output port. */ if (sflow_actions->out_port != ODPP_NONE) { out_dsp = dpif_sflow_find_port(ds, sflow_actions->out_port); if (out_dsp) { tnlOutProto = dpif_sflow_tunnel_proto(out_dsp->tunnel_type); } } } memset(&tnlOutElem, 0, sizeof(tnlOutElem)); tnlOutElem.tag = SFLFLOW_EX_IPV4_TUNNEL_EGRESS; dpif_sflow_tunnel_v4(tnlOutProto, &sflow_actions->tunnel, &tnlOutElem.flowType.ipv4); SFLADD_ELEMENT(&fs, &tnlOutElem); if (sflow_actions->tunnel.tun_id) { memset(&vniOutElem, 0, sizeof(vniOutElem)); vniOutElem.tag = SFLFLOW_EX_VNI_EGRESS; vniOutElem.flowType.tunnel_vni.vni = ntohll(sflow_actions->tunnel.tun_id); SFLADD_ELEMENT(&fs, &vniOutElem); } } /* MPLS output label stack. */ if (sflow_actions && sflow_actions->mpls_stack_depth > 0 && !sflow_actions->mpls_err && dpif_sflow_cookie_num_outputs(cookie) == 1) { memset(&mplsElem, 0, sizeof(mplsElem)); mplsElem.tag = SFLFLOW_EX_MPLS; dpif_sflow_encode_mpls_stack(&mplsElem.flowType.mpls.out_stack, mpls_lse_buf, sflow_actions); SFLADD_ELEMENT(&fs, &mplsElem); } /* Submit the flow sample to be encoded into the next datagram. */ SFLADD_ELEMENT(&fs, &hdrElem); SFLADD_ELEMENT(&fs, &switchElem); sfl_sampler_writeFlowSample(sampler, &fs); out: ovs_mutex_unlock(&mutex); } void dpif_sflow_run(struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (ds->collectors != NULL) { time_t now = time_now(); route_table_run(); if (now >= ds->next_tick) { sfl_agent_tick(ds->sflow_agent, time_wall()); ds->next_tick = now + 1; } } ovs_mutex_unlock(&mutex); } void dpif_sflow_wait(struct dpif_sflow *ds) OVS_EXCLUDED(mutex) { ovs_mutex_lock(&mutex); if (ds->collectors != NULL) { poll_timer_wait_until(ds->next_tick * 1000LL); } ovs_mutex_unlock(&mutex); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/pktbuf.c0000644000000000000000000000013013534540071016771 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.053855431 openvswitch-2.5.9/ofproto/pktbuf.c0000644000175000017500000001713313534540071020466 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "pktbuf.h" #include #include #include "coverage.h" #include "ofp-util.h" #include "dp-packet.h" #include "timeval.h" #include "util.h" #include "openvswitch/vconn.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(pktbuf); COVERAGE_DEFINE(pktbuf_buffer_unknown); COVERAGE_DEFINE(pktbuf_null_cookie); COVERAGE_DEFINE(pktbuf_retrieved); COVERAGE_DEFINE(pktbuf_reuse_error); /* Buffers are identified by a 32-bit opaque ID. We divide the ID * into a buffer number (low bits) and a cookie (high bits). The buffer number * is an index into an array of buffers. The cookie distinguishes between * different packets that have occupied a single buffer. Thus, the more * buffers we have, the lower-quality the cookie... */ #define PKTBUF_BITS 8 #define PKTBUF_MASK (PKTBUF_CNT - 1) #define PKTBUF_CNT (1u << PKTBUF_BITS) #define COOKIE_BITS (32 - PKTBUF_BITS) #define COOKIE_MAX ((1u << COOKIE_BITS) - 1) #define OVERWRITE_MSECS 5000 struct packet { struct dp_packet *buffer; uint32_t cookie; long long int timeout; ofp_port_t in_port; }; struct pktbuf { struct packet packets[PKTBUF_CNT]; unsigned int buffer_idx; unsigned int null_idx; }; int pktbuf_capacity(void) { return PKTBUF_CNT; } struct pktbuf * pktbuf_create(void) { return xzalloc(sizeof *pktbuf_create()); } void pktbuf_destroy(struct pktbuf *pb) { if (pb) { size_t i; for (i = 0; i < PKTBUF_CNT; i++) { dp_packet_delete(pb->packets[i].buffer); } free(pb); } } static unsigned int make_id(unsigned int buffer_idx, unsigned int cookie) { return buffer_idx | (cookie << PKTBUF_BITS); } /* Attempts to allocate an OpenFlow packet buffer id within 'pb'. The packet * buffer will store a copy of 'buffer_size' bytes in 'buffer' and the port * number 'in_port', which should be the OpenFlow port number on which 'buffer' * was received. * * If successful, returns the packet buffer id (a number other than * UINT32_MAX). pktbuf_retrieve() can later be used to retrieve the buffer and * its input port number (buffers do expire after a time, so this is not * guaranteed to be true forever). On failure, returns UINT32_MAX. * * The caller retains ownership of 'buffer'. */ uint32_t pktbuf_save(struct pktbuf *pb, const void *buffer, size_t buffer_size, ofp_port_t in_port) { struct packet *p = &pb->packets[pb->buffer_idx]; pb->buffer_idx = (pb->buffer_idx + 1) & PKTBUF_MASK; if (p->buffer) { if (time_msec() < p->timeout) { return UINT32_MAX; } dp_packet_delete(p->buffer); } /* Don't use maximum cookie value since all-1-bits ID is special. */ if (++p->cookie >= COOKIE_MAX) { p->cookie = 0; } /* Use 2 bytes of headroom to 32-bit align the L3 header. */ p->buffer = dp_packet_clone_data_with_headroom(buffer, buffer_size, 2); p->timeout = time_msec() + OVERWRITE_MSECS; p->in_port = in_port; return make_id(p - pb->packets, p->cookie); } /* * Allocates and returns a "null" packet buffer id. The returned packet buffer * id is considered valid by pktbuf_retrieve(), but it is not associated with * actual buffered data. * * This function is always successful. * * This is useful in one special case: with the current OpenFlow design, the * "fail-open" code cannot always know whether a connection to a controller is * actually valid until it receives a OFPT_PACKET_OUT or OFPT_FLOW_MOD request, * but at that point the packet in question has already been forwarded (since * we are still in "fail-open" mode). If the packet was buffered in the usual * way, then the OFPT_PACKET_OUT or OFPT_FLOW_MOD would cause a duplicate * packet in the network. Null packet buffer ids identify such a packet that * has already been forwarded, so that Open vSwitch can quietly ignore the * request to re-send it. (After that happens, the switch exits fail-open * mode.) * * See the top-level comment in fail-open.c for an overview. */ uint32_t pktbuf_get_null(void) { return make_id(0, COOKIE_MAX); } /* Attempts to retrieve a saved packet with the given 'id' from 'pb'. Returns * 0 if successful, otherwise an OpenFlow error code. * * On success, ordinarily stores the buffered packet in '*bufferp' and the * OpenFlow port number on which the packet was received in '*in_port'. The * caller becomes responsible for freeing the buffer. However, if 'id' * identifies a "null" packet buffer (created with pktbuf_get_null()), stores * NULL in '*bufferp' and OFPP_NONE in '*in_port'. * * 'in_port' may be NULL if the input port is not of interest. * * The L3 header of a returned packet will be 32-bit aligned. * * On failure, stores NULL in in '*bufferp' and UINT16_MAX in '*in_port'. */ enum ofperr pktbuf_retrieve(struct pktbuf *pb, uint32_t id, struct dp_packet **bufferp, ofp_port_t *in_port) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 20); struct packet *p; enum ofperr error; if (id == UINT32_MAX) { error = 0; goto error; } if (!pb) { VLOG_WARN_RL(&rl, "attempt to send buffered packet via connection " "without buffers"); error = OFPERR_OFPBRC_BUFFER_UNKNOWN; goto error; } p = &pb->packets[id & PKTBUF_MASK]; if (p->cookie == id >> PKTBUF_BITS) { struct dp_packet *buffer = p->buffer; if (buffer) { *bufferp = buffer; if (in_port) { *in_port = p->in_port; } p->buffer = NULL; COVERAGE_INC(pktbuf_retrieved); return 0; } else { COVERAGE_INC(pktbuf_reuse_error); VLOG_WARN_RL(&rl, "attempt to reuse buffer %08"PRIx32, id); error = OFPERR_OFPBRC_BUFFER_EMPTY; } } else if (id >> PKTBUF_BITS != COOKIE_MAX) { COVERAGE_INC(pktbuf_buffer_unknown); VLOG_WARN_RL(&rl, "cookie mismatch: %08"PRIx32" != %08"PRIx32, id, (id & PKTBUF_MASK) | (p->cookie << PKTBUF_BITS)); error = OFPERR_OFPBRC_BUFFER_UNKNOWN; } else { COVERAGE_INC(pktbuf_null_cookie); VLOG_INFO_RL(&rl, "Received null cookie %08"PRIx32" (this is normal " "if the switch was recently in fail-open mode)", id); error = 0; } error: *bufferp = NULL; if (in_port) { *in_port = OFPP_NONE; } return error; } void pktbuf_discard(struct pktbuf *pb, uint32_t id) { struct packet *p = &pb->packets[id & PKTBUF_MASK]; if (p->cookie == id >> PKTBUF_BITS) { dp_packet_delete(p->buffer); p->buffer = NULL; } } /* Returns the number of packets buffered in 'pb'. Returns 0 if 'pb' is * null. */ unsigned int pktbuf_count_packets(const struct pktbuf *pb) { int n = 0; if (pb) { int i; for (i = 0; i < PKTBUF_CNT; i++) { if (pb->packets[i].buffer) { n++; } } } return n; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/bond.h0000644000000000000000000000013213534540071016427 xustar0030 mtime=1567801401.617682756 30 atime=1567801402.101686312 30 ctime=1567801425.017855166 openvswitch-2.5.9/ofproto/bond.h0000644000175000017500000001120713534540071020116 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef BOND_H #define BOND_H 1 #include #include #include "ofproto-provider.h" #include "packets.h" struct flow; struct netdev; struct ofpbuf; struct ofproto_dpif; enum lacp_status; /* How flows are balanced among bond slaves. */ enum bond_mode { BM_TCP, /* Transport Layer Load Balance. */ BM_SLB, /* Source Load Balance. */ BM_AB /* Active Backup. */ }; bool bond_mode_from_string(enum bond_mode *, const char *); const char *bond_mode_to_string(enum bond_mode); /* Configuration for a bond as a whole. */ struct bond_settings { char *name; /* Bond's name, for log messages. */ uint32_t basis; /* Flow hashing basis. */ /* Balancing configuration. */ enum bond_mode balance; int rebalance_interval; /* Milliseconds between rebalances. Zero to disable rebalancing. */ /* Link status detection. */ int up_delay; /* ms before enabling an up slave. */ int down_delay; /* ms before disabling a down slave. */ bool lacp_fallback_ab_cfg; /* Fallback to active-backup on LACP failure. */ struct eth_addr active_slave_mac; /* The MAC address of the interface that was active during the last ovs run. */ }; /* Program startup. */ void bond_init(void); /* Basics. */ struct bond *bond_create(const struct bond_settings *, struct ofproto_dpif *ofproto); void bond_unref(struct bond *); struct bond *bond_ref(const struct bond *); bool bond_reconfigure(struct bond *, const struct bond_settings *); void bond_slave_register(struct bond *, void *slave_, ofp_port_t ofport, struct netdev *); void bond_slave_set_netdev(struct bond *, void *slave_, struct netdev *); void bond_slave_unregister(struct bond *, const void *slave); bool bond_run(struct bond *, enum lacp_status); void bond_wait(struct bond *); void bond_slave_set_may_enable(struct bond *, void *slave_, bool may_enable); /* Special MAC learning support for SLB bonding. */ bool bond_should_send_learning_packets(struct bond *); struct dp_packet *bond_compose_learning_packet(struct bond *, const struct eth_addr eth_src, uint16_t vlan, void **port_aux); bool bond_get_changed_active_slave(const char *name, struct eth_addr *mac, bool force); /* Packet processing. */ enum bond_verdict { BV_ACCEPT, /* Accept this packet. */ BV_DROP, /* Drop this packet. */ BV_DROP_IF_MOVED /* Drop if we've learned a different port. */ }; enum bond_verdict bond_check_admissibility(struct bond *, const void *slave_, const struct eth_addr dst); void *bond_choose_output_slave(struct bond *, const struct flow *, struct flow_wildcards *, uint16_t vlan); /* Rebalancing. */ void bond_account(struct bond *, const struct flow *, uint16_t vlan, uint64_t n_bytes); void bond_rebalance(struct bond *); /* Recirculation * * Only balance_tcp mode uses recirculation. * * When recirculation is used, each bond port is assigned with a unique * recirc_id. The output action to the bond port will be replaced by * a Hash action, followed by a RECIRC action. * * ... actions= ... HASH(hash(L4)), RECIRC(recirc_id) .... * * On handling first output packet, 256 post recirculation flows are installed: * * recirc_id=, dp_hash=<[0..255]>/0xff, actions: output * * Bond module pulls stats from those post recirculation rules. If rebalancing * is needed, those rules are updated with new output actions. */ void bond_update_post_recirc_rules(struct bond *, uint32_t *recirc_id, uint32_t *hash_basis); bool bond_may_recirc(const struct bond *, uint32_t *recirc_id, uint32_t *hash_bias); #endif /* bond.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-monitor.c0000644000000000000000000000013113534540071021574 xustar0030 mtime=1567801401.629682844 29 atime=1567801402.10568634 30 ctime=1567801425.041855343 openvswitch-2.5.9/ofproto/ofproto-dpif-monitor.c0000644000175000017500000003003213534540071023261 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto-dpif-monitor.h" #include #include "bfd.h" #include "cfm.h" #include "dp-packet.h" #include "guarded-list.h" #include "hash.h" #include "heap.h" #include "hmap.h" #include "latch.h" #include "ofpbuf.h" #include "ofproto-dpif.h" #include "ovs-lldp.h" #include "ovs-thread.h" #include "poll-loop.h" #include "seq.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofproto_dpif_monitor); /* Converts the time in millisecond to heap priority. */ #define MSEC_TO_PRIO(TIME) (LLONG_MAX - (TIME)) /* Converts the heap priority to time in millisecond. */ #define PRIO_TO_MSEC(PRIO) (LLONG_MAX - (PRIO)) /* Monitored port. It owns references to ofport, bfd, cfm, and lldp structs. */ struct mport { struct hmap_node hmap_node; /* In monitor_hmap. */ struct heap_node heap_node; /* In monitor_heap. */ const struct ofport_dpif *ofport; /* The corresponding ofport. */ struct cfm *cfm; /* Reference to cfm. */ struct bfd *bfd; /* Reference to bfd. */ struct lldp *lldp; /* Reference to lldp. */ struct eth_addr hw_addr; /* Hardware address. */ }; /* Entry of the 'send_soon' list. Contains the pointer to the * 'ofport_dpif'. Note, the pointed object is not protected, so * users should always use the mport_find() to convert it to 'mport'. */ struct send_soon_entry { struct ovs_list list_node; /* In send_soon. */ const struct ofport_dpif *ofport; }; /* hmap that contains "struct mport"s. */ static struct hmap monitor_hmap = HMAP_INITIALIZER(&monitor_hmap); /* heap for ordering mport based on bfd/cfm wakeup time. */ static struct heap monitor_heap; /* guarded-list for storing the mports that need to send bfd/cfm control * packet soon. */ static struct guarded_list send_soon = GUARDED_OVS_LIST_INITIALIZER(&send_soon); /* The monitor thread id. */ static pthread_t monitor_tid; /* True if the monitor thread is running. */ static bool monitor_running; static struct latch monitor_exit_latch; static struct ovs_mutex monitor_mutex = OVS_MUTEX_INITIALIZER; static void *monitor_main(void *); static void monitor_check_send_soon(struct dp_packet *); static void monitor_run(void); static void monitor_mport_run(struct mport *, struct dp_packet *); static void mport_register(const struct ofport_dpif *, struct bfd *, struct cfm *, struct lldp *, const struct eth_addr *) OVS_REQUIRES(monitor_mutex); static void mport_unregister(const struct ofport_dpif *) OVS_REQUIRES(monitor_mutex); static void mport_update(struct mport *, struct bfd *, struct cfm *, struct lldp *, const struct eth_addr *) OVS_REQUIRES(monitor_mutex); static struct mport *mport_find(const struct ofport_dpif *) OVS_REQUIRES(monitor_mutex); /* Tries finding and returning the 'mport' from the monitor_hmap. * If there is no such 'mport', returns NULL. */ static struct mport * mport_find(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex) { struct mport *node; HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash_pointer(ofport, 0), &monitor_hmap) { if (node->ofport == ofport) { return node; } } return NULL; } /* Creates a new mport and inserts it into monitor_hmap and monitor_heap, * if it doesn't exist. Otherwise, just updates its fields. */ static void mport_register(const struct ofport_dpif *ofport, struct bfd *bfd, struct cfm *cfm, struct lldp *lldp, const struct eth_addr *hw_addr) OVS_REQUIRES(monitor_mutex) { struct mport *mport = mport_find(ofport); if (!mport) { mport = xzalloc(sizeof *mport); mport->ofport = ofport; hmap_insert(&monitor_hmap, &mport->hmap_node, hash_pointer(ofport, 0)); heap_insert(&monitor_heap, &mport->heap_node, 0); } mport_update(mport, bfd, cfm, lldp, hw_addr); } /* Removes mport from monitor_hmap and monitor_heap and frees it. */ static void mport_unregister(const struct ofport_dpif *ofport) OVS_REQUIRES(monitor_mutex) { struct mport *mport = mport_find(ofport); if (mport) { mport_update(mport, NULL, NULL, NULL, NULL); hmap_remove(&monitor_hmap, &mport->hmap_node); heap_remove(&monitor_heap, &mport->heap_node); free(mport); } } /* Updates the fields of an existing mport struct. */ static void mport_update(struct mport *mport, struct bfd *bfd, struct cfm *cfm, struct lldp *lldp, const struct eth_addr *hw_addr) OVS_REQUIRES(monitor_mutex) { ovs_assert(mport); if (mport->cfm != cfm) { cfm_unref(mport->cfm); mport->cfm = cfm_ref(cfm); } if (mport->bfd != bfd) { bfd_unref(mport->bfd); mport->bfd = bfd_ref(bfd); } if (mport->lldp != lldp) { lldp_unref(mport->lldp); mport->lldp = lldp_ref(lldp); } if (hw_addr && !eth_addr_equals(mport->hw_addr, *hw_addr)) { mport->hw_addr = *hw_addr; } /* If bfd/cfm/lldp is added or reconfigured, move the mport on top of the heap * so that the monitor thread can run the mport next time it wakes up. */ if (mport->bfd || mport->cfm || mport->lldp) { heap_change(&monitor_heap, &mport->heap_node, LLONG_MAX); } } /* The 'main' function for the monitor thread. */ static void * monitor_main(void * args OVS_UNUSED) { VLOG_INFO("monitor thread created"); while (!latch_is_set(&monitor_exit_latch)) { monitor_run(); latch_wait(&monitor_exit_latch); poll_block(); } VLOG_INFO("monitor thread terminated"); return NULL; } /* The monitor thread should wake up this often to ensure that newly added or * reconfigured monitoring ports are run in a timely manner. */ #define MONITOR_INTERVAL_MSEC 100 /* Checks the 'send_soon' list and the heap for mports that have timed * out bfd/cfm sessions. */ static void monitor_run(void) { uint32_t stub[512 / 4]; long long int prio_now; struct dp_packet packet; dp_packet_use_stub(&packet, stub, sizeof stub); ovs_mutex_lock(&monitor_mutex); /* The monitor_check_send_soon() needs to be run twice. The first * time is for preventing the same 'mport' from being processed twice * (i.e. once from heap, the other from the 'send_soon' array). * The second run is to cover the case when the control packet is sent * via patch port and the other end needs to send back immediately. */ monitor_check_send_soon(&packet); prio_now = MSEC_TO_PRIO(time_msec()); /* Peeks the top of heap and checks if we should run this mport. */ while (!heap_is_empty(&monitor_heap) && heap_max(&monitor_heap)->priority >= prio_now) { struct mport *mport; mport = CONTAINER_OF(heap_max(&monitor_heap), struct mport, heap_node); monitor_mport_run(mport, &packet); } monitor_check_send_soon(&packet); /* Waits on the earliest next wakeup time. */ if (!heap_is_empty(&monitor_heap)) { long long int next_timeout, next_mport_wakeup; next_timeout = time_msec() + MONITOR_INTERVAL_MSEC; next_mport_wakeup = PRIO_TO_MSEC(heap_max(&monitor_heap)->priority); poll_timer_wait_until(MIN(next_timeout, next_mport_wakeup)); } ovs_mutex_unlock(&monitor_mutex); dp_packet_uninit(&packet); } /* Checks the 'send_soon' list for any mport that needs to send cfm/bfd * control packet immediately, and calls monitor_mport_run(). */ static void monitor_check_send_soon(struct dp_packet *packet) OVS_REQUIRES(monitor_mutex) { while (!guarded_list_is_empty(&send_soon)) { struct send_soon_entry *entry; struct mport *mport; entry = CONTAINER_OF(guarded_list_pop_front(&send_soon), struct send_soon_entry, list_node); mport = mport_find(entry->ofport); if (mport) { monitor_mport_run(mport, packet); } free(entry); } } /* Checks the sending of control packet on 'mport'. Sends the control * packet if needed. Executes bfd and cfm periodic functions (run, wait) * on 'mport'. And changes the location of 'mport' in heap based on next * timeout. */ static void monitor_mport_run(struct mport *mport, struct dp_packet *packet) OVS_REQUIRES(monitor_mutex) { long long int next_wake_time; long long int bfd_wake_time = LLONG_MAX; long long int cfm_wake_time = LLONG_MAX; long long int lldp_wake_time = LLONG_MAX; if (mport->cfm && cfm_should_send_ccm(mport->cfm)) { dp_packet_clear(packet); cfm_compose_ccm(mport->cfm, packet, mport->hw_addr); ofproto_dpif_send_packet(mport->ofport, packet); } if (mport->bfd && bfd_should_send_packet(mport->bfd)) { dp_packet_clear(packet); bfd_put_packet(mport->bfd, packet, mport->hw_addr); ofproto_dpif_send_packet(mport->ofport, packet); } if (mport->lldp && lldp_should_send_packet(mport->lldp)) { dp_packet_clear(packet); lldp_put_packet(mport->lldp, packet, mport->hw_addr); ofproto_dpif_send_packet(mport->ofport, packet); } if (mport->cfm) { cfm_run(mport->cfm); cfm_wake_time = cfm_wait(mport->cfm); } if (mport->bfd) { bfd_run(mport->bfd); bfd_wake_time = bfd_wait(mport->bfd); } if (mport->lldp) { lldp_wake_time = lldp_wait(mport->lldp); } /* Computes the next wakeup time for this mport. */ next_wake_time = MIN(bfd_wake_time, cfm_wake_time); next_wake_time = MIN(next_wake_time, lldp_wake_time); heap_change(&monitor_heap, &mport->heap_node, MSEC_TO_PRIO(next_wake_time)); } /* Creates the mport in monitor module if either bfd or cfm * is configured. Otherwise, deletes the mport. * Also checks whether the monitor thread should be started * or terminated. */ void ofproto_dpif_monitor_port_update(const struct ofport_dpif *ofport, struct bfd *bfd, struct cfm *cfm, struct lldp *lldp, const struct eth_addr *hw_addr) { ovs_mutex_lock(&monitor_mutex); if (!cfm && !bfd && !lldp) { mport_unregister(ofport); } else { mport_register(ofport, bfd, cfm, lldp, hw_addr); } ovs_mutex_unlock(&monitor_mutex); /* If the monitor thread is not running and the hmap * is not empty, starts it. If it is and the hmap is empty, * terminates it. */ if (!monitor_running && !hmap_is_empty(&monitor_hmap)) { latch_init(&monitor_exit_latch); monitor_tid = ovs_thread_create("monitor", monitor_main, NULL); monitor_running = true; } else if (monitor_running && hmap_is_empty(&monitor_hmap)) { latch_set(&monitor_exit_latch); xpthread_join(monitor_tid, NULL); latch_destroy(&monitor_exit_latch); monitor_running = false; } } /* Registers the 'ofport' in the 'send_soon' list. We cannot directly * insert the corresponding mport to the 'send_soon' list, since the * 'send_soon' list is not updated when the mport is removed. * * Reader of the 'send_soon' list is responsible for freeing the entry. */ void ofproto_dpif_monitor_port_send_soon(const struct ofport_dpif *ofport) { struct send_soon_entry *entry = xzalloc(sizeof *entry); entry->ofport = ofport; guarded_list_push_back(&send_soon, &entry->list_node, SIZE_MAX); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-rid.h0000644000000000000000000000013113534540071020670 xustar0030 mtime=1567801401.633682874 29 atime=1567801402.10968637 30 ctime=1567801425.045855373 openvswitch-2.5.9/ofproto/ofproto-dpif-rid.h0000644000175000017500000002152013534540071022357 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_RID_H #define OFPROTO_DPIF_RID_H #include #include #include "cmap.h" #include "list.h" #include "ofp-actions.h" #include "ofproto-dpif-mirror.h" #include "ovs-thread.h" struct ofproto_dpif; struct rule; /* * Recirculation * ============= * * Recirculation is a technique to allow a frame to re-enter the datapath * packet processing path to achieve more flexible packet processing, such as * modifying header fields after MPLS POP action and selecting a slave port for * bond ports. * * Data path and user space interface * ----------------------------------- * * Recirculation uses two uint32_t fields, recirc_id and dp_hash, and a RECIRC * action. recirc_id is used to select the next packet processing steps among * multiple instances of recirculation. When a packet initially enters the * datapath it is assigned with recirc_id 0, which indicates no recirculation. * Recirc_ids are managed by the user space, opaque to the datapath. * * On the other hand, dp_hash can only be computed by the datapath, opaque to * the user space, as the datapath is free to choose the hashing algorithm * without informing user space about it. The dp_hash value should be * wildcarded for newly received packets. HASH action specifies whether the * hash is computed, and if computed, how many fields are to be included in the * hash computation. The computed hash value is stored into the dp_hash field * prior to recirculation. * * The RECIRC action sets the recirc_id field and then reprocesses the packet * as if it was received again on the same input port. RECIRC action works * like a function call; actions listed after the RECIRC action will be * executed after recirculation. RECIRC action can be nested, but datapath * implementation limits the number of nested recirculations to prevent * unreasonable nesting depth or infinite loop. * * User space recirculation context * --------------------------------- * * Recirculation is usually hidden from the OpenFlow controllers. Action * translation code deduces when recirculation is necessary and issues a * datapath recirculation action. All OpenFlow actions to be performed after * recirculation are derived from the OpenFlow pipeline and are stored with the * recirculation ID. When the OpenFlow tables are changed in a way affecting * the recirculation flows, new recirculation ID with new metadata and actions * is allocated and the old one is timed out. * * Recirculation ID pool * ---------------------- * * Recirculation ID needs to be unique for all datapaths. Recirculation ID * pool keeps track of recirculation ids and stores OpenFlow pipeline * translation context so that flow processing may continue after * recirculation. * * A Recirculation ID can be any uint32_t value, except for that the value 0 is * reserved for 'no recirculation' case. * * Thread-safety * -------------- * * All APIs are thread safe. */ /* Metadata for restoring pipeline context after recirculation. Helpers * are inlined below to keep them together with the definition for easier * updates. */ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); struct recirc_metadata { /* Metadata in struct flow. */ const struct flow_tnl *tunnel; /* Encapsulating tunnel parameters. */ ovs_be64 metadata; /* OpenFlow Metadata. */ uint64_t regs[FLOW_N_XREGS]; /* Registers. */ ofp_port_t in_port; /* Incoming port. */ ofp_port_t actset_output; /* Output port in action set. */ }; static inline void recirc_metadata_from_flow(struct recirc_metadata *md, const struct flow *flow) { memset(md, 0, sizeof *md); md->tunnel = &flow->tunnel; md->metadata = flow->metadata; memcpy(md->regs, flow->regs, sizeof md->regs); md->in_port = flow->in_port.ofp_port; md->actset_output = flow->actset_output; } static inline void recirc_metadata_to_flow(const struct recirc_metadata *md, struct flow *flow) { if (md->tunnel && flow_tnl_dst_is_set(md->tunnel)) { flow->tunnel = *md->tunnel; } else { memset(&flow->tunnel, 0, sizeof flow->tunnel); } flow->metadata = md->metadata; memcpy(flow->regs, md->regs, sizeof flow->regs); flow->in_port.ofp_port = md->in_port; flow->actset_output = md->actset_output; } /* State that flow translation can save, to restore when recirculation * occurs. */ struct recirc_state { /* Initial table for post-recirculation processing. */ uint8_t table_id; /* Pipeline context for post-recirculation processing. */ struct ofproto_dpif *ofproto; /* Post-recirculation bridge. */ struct recirc_metadata metadata; /* Flow metadata. */ struct ofpbuf *stack; /* Stack if any. */ mirror_mask_t mirrors; /* Mirrors already output. */ bool conntracked; /* Conntrack occurred prior to recirc. */ /* Actions to be translated on recirculation. */ uint32_t action_set_len; /* How much of 'ofpacts' consists of an * action set? */ uint32_t ofpacts_len; /* Size of 'ofpacts', in bytes. */ struct ofpact *ofpacts; /* Sequence of "struct ofpacts". */ }; /* This maps a recirculation ID to saved state that flow translation can * restore when recirculation occurs. */ struct recirc_id_node { /* Index data. */ struct ovs_list exp_node OVS_GUARDED; struct cmap_node id_node; struct cmap_node metadata_node; uint32_t id; uint32_t hash; struct ovs_refcount refcount; /* Saved state. * * This state should not be modified after inserting a node in the pool, * hence the 'const' to emphasize that. */ const struct recirc_state state; /* Storage for tunnel metadata. */ struct flow_tnl state_metadata_tunnel; }; void recirc_init(void); /* This is only used for bonds and will go away when bonds implementation is * updated to use this mechanism instead of internal rules. */ uint32_t recirc_alloc_id(struct ofproto_dpif *); uint32_t recirc_alloc_id_ctx(const struct recirc_state *); uint32_t recirc_find_id(const struct recirc_state *); void recirc_free_id(uint32_t recirc_id); void recirc_free_ofproto(struct ofproto_dpif *, const char *ofproto_name); const struct recirc_id_node *recirc_id_node_find(uint32_t recirc_id); static inline bool recirc_id_node_try_ref_rcu(const struct recirc_id_node *n_) { struct recirc_id_node *node = CONST_CAST(struct recirc_id_node *, n_); return node ? ovs_refcount_try_ref_rcu(&node->refcount) : false; } void recirc_id_node_unref(const struct recirc_id_node *); void recirc_run(void); /* Recirculation IDs on which references are held. */ struct recirc_refs { unsigned n_recircs; union { uint32_t recirc[2]; /* When n_recircs == 1 or 2 */ uint32_t *recircs; /* When 'n_recircs' > 2 */ }; }; #define RECIRC_REFS_EMPTY_INITIALIZER ((struct recirc_refs) \ { 0, { { 0, 0 } } }) /* Helpers to abstract the recirculation union away. */ static inline void recirc_refs_init(struct recirc_refs *rr) { *rr = RECIRC_REFS_EMPTY_INITIALIZER; } static inline void recirc_refs_add(struct recirc_refs *rr, uint32_t id) { if (OVS_LIKELY(rr->n_recircs < ARRAY_SIZE(rr->recirc))) { rr->recirc[rr->n_recircs++] = id; } else { if (rr->n_recircs == ARRAY_SIZE(rr->recirc)) { uint32_t *recircs = xmalloc(sizeof rr->recirc + sizeof id); memcpy(recircs, rr->recirc, sizeof rr->recirc); rr->recircs = recircs; } else { rr->recircs = xrealloc(rr->recircs, (rr->n_recircs + 1) * sizeof id); } rr->recircs[rr->n_recircs++] = id; } } static inline void recirc_refs_swap(struct recirc_refs *a, struct recirc_refs *b) { struct recirc_refs tmp; tmp = *a; *a = *b; *b = tmp; } static inline void recirc_refs_unref(struct recirc_refs *rr) { if (OVS_LIKELY(rr->n_recircs <= ARRAY_SIZE(rr->recirc))) { for (int i = 0; i < rr->n_recircs; i++) { recirc_free_id(rr->recirc[i]); } } else { for (int i = 0; i < rr->n_recircs; i++) { recirc_free_id(rr->recircs[i]); } free(rr->recircs); } rr->n_recircs = 0; } #endif openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-ipfix.h0000644000000000000000000000013113534540071021231 xustar0030 mtime=1567801401.629682844 29 atime=1567801402.10568634 30 ctime=1567801425.037855313 openvswitch-2.5.9/ofproto/ofproto-dpif-ipfix.h0000644000175000017500000000440113534540071022717 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_IPFIX_H #define OFPROTO_DPIF_IPFIX_H 1 #include #include #include #include "lib/odp-util.h" struct flow; struct dp_packet; struct ofproto_ipfix_bridge_exporter_options; struct ofproto_ipfix_flow_exporter_options; struct flow_tnl; struct ofport; struct dpif_ipfix *dpif_ipfix_create(void); struct dpif_ipfix *dpif_ipfix_ref(const struct dpif_ipfix *); void dpif_ipfix_unref(struct dpif_ipfix *); void dpif_ipfix_add_tunnel_port(struct dpif_ipfix *, struct ofport *, odp_port_t); void dpif_ipfix_del_tunnel_port(struct dpif_ipfix *, odp_port_t); uint32_t dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *); bool dpif_ipfix_get_bridge_exporter_tunnel_sampling(const struct dpif_ipfix *); bool dpif_ipfix_get_bridge_exporter_input_sampling(const struct dpif_ipfix *); bool dpif_ipfix_get_bridge_exporter_output_sampling(const struct dpif_ipfix *); bool dpif_ipfix_get_tunnel_port(const struct dpif_ipfix *, odp_port_t); void dpif_ipfix_set_options( struct dpif_ipfix *, const struct ofproto_ipfix_bridge_exporter_options *, const struct ofproto_ipfix_flow_exporter_options *, size_t); void dpif_ipfix_bridge_sample(struct dpif_ipfix *, const struct dp_packet *, const struct flow *, odp_port_t, odp_port_t, const struct flow_tnl *); void dpif_ipfix_flow_sample(struct dpif_ipfix *, const struct dp_packet *, const struct flow *, uint32_t, uint16_t, uint32_t, uint32_t); void dpif_ipfix_run(struct dpif_ipfix *); void dpif_ipfix_wait(struct dpif_ipfix *); #endif /* ofproto/ofproto-dpif-ipfix.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-provider.h0000644000000000000000000000013013534540071021023 xustar0029 mtime=1567801401.65368302 29 atime=1567801402.10968637 30 ctime=1567801425.053855431 openvswitch-2.5.9/ofproto/ofproto-provider.h0000644000175000017500000024020013534540071022511 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_OFPROTO_PROVIDER_H #define OFPROTO_OFPROTO_PROVIDER_H 1 /* Definitions for use within ofproto. * * * Thread-safety * ============= * * Lots of ofproto data structures are only accessed from a single thread. * Those data structures are generally not thread-safe. * * The ofproto-dpif ofproto implementation accesses the flow table from * multiple threads, including modifying the flow table from multiple threads * via the "learn" action, so the flow table and various structures that index * it have been made thread-safe. Refer to comments on individual data * structures for details. */ #include "cfm.h" #include "classifier.h" #include "guarded-list.h" #include "heap.h" #include "hindex.h" #include "list.h" #include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofproto/ofproto.h" #include "ovs-atomic.h" #include "ovs-rcu.h" #include "ovs-thread.h" #include "shash.h" #include "simap.h" #include "timeval.h" struct match; struct ofputil_flow_mod; struct bfd_cfg; struct meter; struct ofoperation; extern struct ovs_mutex ofproto_mutex; /* An OpenFlow switch. * * With few exceptions, ofproto implementations may look at these fields but * should not modify them. */ struct ofproto { struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */ const struct ofproto_class *ofproto_class; char *type; /* Datapath type. */ char *name; /* Datapath name. */ /* Settings. */ uint64_t fallback_dpid; /* Datapath ID if no better choice found. */ uint64_t datapath_id; /* Datapath ID. */ bool forward_bpdu; /* Option to allow forwarding of BPDU frames * when NORMAL action is invoked. */ char *mfr_desc; /* Manufacturer (NULL for default). */ char *hw_desc; /* Hardware (NULL for default). */ char *sw_desc; /* Software version (NULL for default). */ char *serial_desc; /* Serial number (NULL for default). */ char *dp_desc; /* Datapath description (NULL for default). */ enum ofp_config_flags frag_handling; /* One of OFPC_*. */ /* Datapath. */ struct hmap ports; /* Contains "struct ofport"s. */ struct shash port_by_name; struct simap ofp_requests; /* OpenFlow port number requests. */ uint16_t alloc_port_no; /* Last allocated OpenFlow port number. */ uint16_t max_ports; /* Max possible OpenFlow port num, plus one. */ struct hmap ofport_usage; /* Map ofport to last used time. */ uint64_t change_seq; /* Change sequence for netdev status. */ /* Flow tables. */ long long int eviction_group_timer; /* For rate limited reheapification. */ struct oftable *tables; int n_tables; cls_version_t tables_version; /* Controls which rules are visible to * table lookups. */ /* Rules indexed on their cookie values, in all flow tables. */ struct hindex cookies OVS_GUARDED_BY(ofproto_mutex); struct hmap learned_cookies OVS_GUARDED_BY(ofproto_mutex); /* List of expirable flows, in all flow tables. */ struct ovs_list expirable OVS_GUARDED_BY(ofproto_mutex); /* Meter table. * OpenFlow meters start at 1. To avoid confusion we leave the first * pointer in the array un-used, and index directly with the OpenFlow * meter_id. */ struct ofputil_meter_features meter_features; struct meter **meters; /* 'meter_features.max_meter' + 1 pointers. */ /* OpenFlow connections. */ struct connmgr *connmgr; /* Delayed rule executions. * * We delay calls to ->ofproto_class->rule_execute() past releasing * ofproto_mutex during a flow_mod, because otherwise a "learn" action * triggered by the executing the packet would try to recursively modify * the flow table and reacquire the global lock. */ struct guarded_list rule_executes; /* Contains "struct rule_execute"s. */ /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device * drivers in old versions of Linux that do not properly support VLANs when * VLAN devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ unsigned long int *vlan_bitmap; /* 4096-bit bitmap of in-use VLANs. */ bool vlans_changed; /* True if new VLANs are in use. */ int min_mtu; /* Current MTU of non-internal ports. */ /* Groups. */ struct ovs_rwlock groups_rwlock; struct hmap groups OVS_GUARDED; /* Contains "struct ofgroup"s. */ uint32_t n_groups[4] OVS_GUARDED; /* # of existing groups of each type. */ struct ofputil_group_features ogf; }; void ofproto_init_tables(struct ofproto *, int n_tables); void ofproto_init_max_ports(struct ofproto *, uint16_t max_ports); struct ofproto *ofproto_lookup(const char *name); struct ofport *ofproto_get_port(const struct ofproto *, ofp_port_t ofp_port); /* An OpenFlow port within a "struct ofproto". * * The port's name is netdev_get_name(port->netdev). * * With few exceptions, ofproto implementations may look at these fields but * should not modify them. */ struct ofport { struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */ struct ofproto *ofproto; /* The ofproto that contains this port. */ struct netdev *netdev; struct ofputil_phy_port pp; ofp_port_t ofp_port; /* OpenFlow port number. */ uint64_t change_seq; long long int created; /* Time created, in msec. */ int mtu; }; void ofproto_port_set_state(struct ofport *, enum ofputil_port_state); /* OpenFlow table flags: * * - "Hidden" tables are not included in OpenFlow operations that operate on * "all tables". For example, a request for flow stats on all tables will * omit flows in hidden tables, table stats requests will omit the table * entirely, and the switch features reply will not count the hidden table. * * However, operations that specifically name the particular table still * operate on it. For example, flow_mods and flow stats requests on a * hidden table work. * * To avoid gaps in table IDs (which have unclear validity in OpenFlow), * hidden tables must be the highest-numbered tables that a provider * implements. * * - "Read-only" tables can't be changed through OpenFlow operations. (At * the moment all flow table operations go effectively through OpenFlow, so * this means that read-only tables can't be changed at all after the * read-only flag is set.) * * The generic ofproto layer never sets these flags. An ofproto provider can * set them if it is appropriate. */ enum oftable_flags { OFTABLE_HIDDEN = 1 << 0, /* Hide from most OpenFlow operations. */ OFTABLE_READONLY = 1 << 1 /* Don't allow OpenFlow controller to change this table. */ }; /* A flow table within a "struct ofproto". * * * Thread-safety * ============= * * Adding or removing rules requires holding ofproto_mutex. * * Rules in 'cls' are RCU protected. For extended access to a rule, try * incrementing its ref_count with ofproto_rule_try_ref(), or * ofproto_rule_ref(), if the rule is still known to be in 'cls'. A rule * will be freed using ovsrcu_postpone() once its 'ref_count' reaches zero. * * Modifying a rule requires the rule's own mutex. * * Freeing a rule requires ofproto_mutex. After removing the rule from the * classifier, release a ref_count from the rule ('cls''s reference to the * rule). * * Refer to the thread-safety notes on struct rule for more information.*/ struct oftable { enum oftable_flags flags; struct classifier cls; /* Contains "struct rule"s. */ char *name; /* Table name exposed via OpenFlow, or NULL. */ /* Maximum number of flows or UINT_MAX if there is no limit besides any * limit imposed by resource limitations. */ unsigned int max_flows; /* Current number of flows, not counting temporary duplicates nor deferred * deletions. */ unsigned int n_flows; /* These members determine the handling of an attempt to add a flow that * would cause the table to have more than 'max_flows' flows. * * If 'eviction_fields' is NULL, overflows will be rejected with an error. * * If 'eviction_fields' is nonnull (regardless of whether n_eviction_fields * is nonzero), an overflow will cause a flow to be removed. The flow to * be removed is chosen to give fairness among groups distinguished by * different values for the subfields within 'groups'. */ struct mf_subfield *eviction_fields; size_t n_eviction_fields; /* Eviction groups. * * When a flow is added that would cause the table to have more than * 'max_flows' flows, and 'eviction_fields' is nonnull, these groups are * used to decide which rule to evict: the rule is chosen from the eviction * group that contains the greatest number of rules.*/ uint32_t eviction_group_id_basis; struct hmap eviction_groups_by_id; struct heap eviction_groups_by_size; /* Flow table miss handling configuration. */ ATOMIC(enum ofputil_table_miss) miss_config; /* Eviction is enabled if either the client (vswitchd) enables it or an * OpenFlow controller enables it; thus, a nonzero value indicates that * eviction is enabled. */ #define EVICTION_CLIENT (1 << 0) /* Set to 1 if client enables eviction. */ #define EVICTION_OPENFLOW (1 << 1) /* Set to 1 if OpenFlow enables eviction. */ unsigned int eviction; /* If true, vacancy events are enabled; otherwise they are disabled. */ bool vacancy_enabled; /* Non-zero values for vacancy_up and vacancy_down indicates that vacancy * is enabled by table-mod, else these values are set to zero when * vacancy is disabled */ uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */ uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */ atomic_ulong n_matched; atomic_ulong n_missed; }; /* Assigns TABLE to each oftable, in turn, in OFPROTO. * * All parameters are evaluated multiple times. */ #define OFPROTO_FOR_EACH_TABLE(TABLE, OFPROTO) \ for ((TABLE) = (OFPROTO)->tables; \ (TABLE) < &(OFPROTO)->tables[(OFPROTO)->n_tables]; \ (TABLE)++) /* An OpenFlow flow within a "struct ofproto". * * With few exceptions, ofproto implementations may look at these fields but * should not modify them. * * * Thread-safety * ============= * * Except near the beginning or ending of its lifespan, rule 'rule' belongs to * the classifier rule->ofproto->tables[rule->table_id].cls. The text below * calls this classifier 'cls'. * * Motivation * ---------- * * The thread safety rules described here for "struct rule" are motivated by * two goals: * * - Prevent threads that read members of "struct rule" from reading bad * data due to changes by some thread concurrently modifying those * members. * * - Prevent two threads making changes to members of a given "struct rule" * from interfering with each other. * * * Rules * ----- * * A rule 'rule' may be accessed without a risk of being freed by a thread * until the thread quiesces (i.e., rules are RCU protected and destructed * using ovsrcu_postpone()). Code that needs to hold onto a rule for a * while should increment 'rule->ref_count' either with ofproto_rule_ref() * (if 'ofproto_mutex' is held), or with ofproto_rule_try_ref() (when some * other thread might remove the rule from 'cls'). ofproto_rule_try_ref() * will fail if the rule has already been scheduled for destruction. * * 'rule->ref_count' protects 'rule' from being freed. It doesn't protect the * rule from being deleted from 'cls' (that's 'ofproto_mutex') and it doesn't * protect members of 'rule' from modification (that's 'rule->mutex'). * * 'rule->mutex' protects the members of 'rule' from modification. It doesn't * protect the rule from being deleted from 'cls' (that's 'ofproto_mutex') and * it doesn't prevent the rule from being freed (that's 'rule->ref_count'). * * Regarding thread safety, the members of a rule fall into the following * categories: * * - Immutable. These members are marked 'const'. * * - Members that may be safely read or written only by code holding * ofproto_mutex. These are marked OVS_GUARDED_BY(ofproto_mutex). * * - Members that may be safely read only by code holding ofproto_mutex or * 'rule->mutex', and safely written only by coding holding ofproto_mutex * AND 'rule->mutex'. These are marked OVS_GUARDED. */ struct rule { /* Where this rule resides in an OpenFlow switch. * * These are immutable once the rule is constructed, hence 'const'. */ struct ofproto *const ofproto; /* The ofproto that contains this rule. */ const struct cls_rule cr; /* In owning ofproto's classifier. */ const uint8_t table_id; /* Index in ofproto's 'tables' array. */ bool removed; /* Rule has been removed from the ofproto * data structures. */ /* Protects members marked OVS_GUARDED. * Readers only need to hold this mutex. * Writers must hold both this mutex AND ofproto_mutex. * By implication writers can read *without* taking this mutex while they * hold ofproto_mutex. */ struct ovs_mutex mutex OVS_ACQ_AFTER(ofproto_mutex); /* Number of references. * The classifier owns one reference. * Any thread trying to keep a rule from being freed should hold its own * reference. */ struct ovs_refcount ref_count; /* A "flow cookie" is the OpenFlow name for a 64-bit value associated with * a flow.. */ ovs_be64 flow_cookie OVS_GUARDED; struct hindex_node cookie_node OVS_GUARDED_BY(ofproto_mutex); enum ofputil_flow_mod_flags flags OVS_GUARDED; /* Timeouts. */ uint16_t hard_timeout OVS_GUARDED; /* In seconds from ->modified. */ uint16_t idle_timeout OVS_GUARDED; /* In seconds from ->used. */ /* Eviction precedence. */ const uint16_t importance; /* Removal reason for sending flow removed message. * Used only if 'flags' has OFPUTIL_FF_SEND_FLOW_REM set and if the * value is not OVS_OFPRR_NONE. */ uint8_t removed_reason; /* Eviction groups (see comment on struct eviction_group for explanation) . * * 'eviction_group' is this rule's eviction group, or NULL if it is not in * any eviction group. When 'eviction_group' is nonnull, 'evg_node' is in * the ->eviction_group->rules hmap. */ struct eviction_group *eviction_group OVS_GUARDED_BY(ofproto_mutex); struct heap_node evg_node OVS_GUARDED_BY(ofproto_mutex); /* OpenFlow actions. See struct rule_actions for more thread-safety * notes. */ const struct rule_actions * const actions; /* In owning meter's 'rules' list. An empty list if there is no meter. */ struct ovs_list meter_list_node OVS_GUARDED_BY(ofproto_mutex); /* Flow monitors (e.g. for NXST_FLOW_MONITOR, related to struct ofmonitor). * * 'add_seqno' is the sequence number when this rule was created. * 'modify_seqno' is the sequence number when this rule was last modified. * See 'monitor_seqno' in connmgr.c for more information. */ enum nx_flow_monitor_flags monitor_flags OVS_GUARDED_BY(ofproto_mutex); uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex); uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex); /* Optimisation for flow expiry. In ofproto's 'expirable' list if this * rule is expirable, otherwise empty. */ struct ovs_list expirable OVS_GUARDED_BY(ofproto_mutex); /* Times. Last so that they are more likely close to the stats managed * by the provider. */ long long int created OVS_GUARDED; /* Creation time. */ /* Must hold 'mutex' for both read/write, 'ofproto_mutex' not needed. */ long long int modified OVS_GUARDED; /* Time of last modification. */ }; void ofproto_rule_ref(struct rule *); bool ofproto_rule_try_ref(struct rule *); void ofproto_rule_unref(struct rule *); static inline const struct rule_actions * rule_get_actions(const struct rule *); static inline bool rule_is_table_miss(const struct rule *); static inline bool rule_is_hidden(const struct rule *); /* A set of actions within a "struct rule". * * * Thread-safety * ============= * * A struct rule_actions may be accessed without a risk of being freed by * code that holds 'rule->mutex' (where 'rule' is the rule for which * 'rule->actions == actions') or during the RCU active period. * * All members are immutable: they do not change during the rule's * lifetime. */ struct rule_actions { /* Flags. * * 'has_meter' is true if 'ofpacts' contains an OFPACT_METER action. * * 'has_learn_with_delete' is true if 'ofpacts' contains an OFPACT_LEARN * action whose flags include NX_LEARN_F_DELETE_LEARNED. */ bool has_meter; bool has_learn_with_delete; /* Actions. */ uint32_t ofpacts_len; /* Size of 'ofpacts', in bytes. */ struct ofpact ofpacts[]; /* Sequence of "struct ofpacts". */ }; BUILD_ASSERT_DECL(offsetof(struct rule_actions, ofpacts) % OFPACT_ALIGNTO == 0); const struct rule_actions *rule_actions_create(const struct ofpact *, size_t); void rule_actions_destroy(const struct rule_actions *); bool ofproto_rule_has_out_port(const struct rule *, ofp_port_t port) OVS_REQUIRES(ofproto_mutex); /* A set of rules to which an OpenFlow operation applies. */ struct rule_collection { struct rule **rules; /* The rules. */ size_t n; /* Number of rules collected. */ size_t capacity; /* Number of rules that will fit in 'rules'. */ struct rule *stub[64]; /* Preallocated rules to avoid malloc(). */ }; void rule_collection_init(struct rule_collection *); void rule_collection_add(struct rule_collection *, struct rule *); void rule_collection_ref(struct rule_collection *) OVS_REQUIRES(ofproto_mutex); void rule_collection_unref(struct rule_collection *); void rule_collection_destroy(struct rule_collection *); /* Limits the number of flows allowed in the datapath. Only affects the * ofproto-dpif implementation. */ extern unsigned ofproto_flow_limit; /* Maximum idle time (in ms) for flows to be cached in the datapath. * Revalidators may expire flows more quickly than the configured value based * on system load and other factors. This variable is subject to change. */ extern unsigned ofproto_max_idle; /* Number of upcall handler and revalidator threads. Only affects the * ofproto-dpif implementation. */ extern size_t n_handlers, n_revalidators; /* Number of rx queues to be created for each dpdk interface. */ extern size_t n_dpdk_rxqs; /* Cpu mask for pmd threads. */ extern char *pmd_cpu_mask; static inline struct rule *rule_from_cls_rule(const struct cls_rule *); void ofproto_rule_expire(struct rule *rule, uint8_t reason) OVS_REQUIRES(ofproto_mutex); void ofproto_rule_delete(struct ofproto *, struct rule *) OVS_EXCLUDED(ofproto_mutex); void ofproto_rule_reduce_timeouts(struct rule *rule, uint16_t idle_timeout, uint16_t hard_timeout) OVS_EXCLUDED(ofproto_mutex); /* A group within a "struct ofproto". * * With few exceptions, ofproto implementations may look at these fields but * should not modify them. */ struct ofgroup { struct hmap_node hmap_node OVS_GUARDED; /* In ofproto's "groups" hmap. */ /* Number of references. * * This is needed to keep track of references to the group in the xlate * module. * * If the main thread removes the group from an ofproto, we need to * guarantee that the group remains accessible to users of * xlate_group_actions and the xlate_cache, as the xlate_cache will not be * cleaned up until the corresponding datapath flows are revalidated. */ struct ovs_refcount ref_count; /* No lock is needed to protect the fields below since they are not * modified after construction. */ const struct ofproto *ofproto; /* The ofproto that contains this group. */ const uint32_t group_id; const enum ofp11_group_type type; /* One of OFPGT_*. */ const long long int created; /* Creation time. */ const long long int modified; /* Time of last modification. */ struct ovs_list buckets; /* Contains "struct ofputil_bucket"s. */ const uint32_t n_buckets; const struct ofputil_group_props props; }; bool ofproto_group_lookup(const struct ofproto *ofproto, uint32_t group_id, struct ofgroup **group); void ofproto_group_ref(struct ofgroup *); void ofproto_group_unref(struct ofgroup *); void ofproto_group_delete_all(struct ofproto *); /* ofproto class structure, to be defined by each ofproto implementation. * * * Data Structures * =============== * * These functions work primarily with four different kinds of data * structures: * * - "struct ofproto", which represents an OpenFlow switch. * * - "struct ofport", which represents a port within an ofproto. * * - "struct rule", which represents an OpenFlow flow within an ofproto. * * - "struct ofgroup", which represents an OpenFlow 1.1+ group within an * ofproto. * * Each of these data structures contains all of the implementation-independent * generic state for the respective concept, called the "base" state. None of * them contains any extra space for ofproto implementations to use. Instead, * each implementation is expected to declare its own data structure that * contains an instance of the generic data structure plus additional * implementation-specific members, called the "derived" state. The * implementation can use casts or (preferably) the CONTAINER_OF macro to * obtain access to derived state given only a pointer to the embedded generic * data structure. * * * Life Cycle * ========== * * Four stylized functions accompany each of these data structures: * * "alloc" "construct" "destruct" "dealloc" * ------------ ---------------- --------------- -------------- * ofproto ->alloc ->construct ->destruct ->dealloc * ofport ->port_alloc ->port_construct ->port_destruct ->port_dealloc * rule ->rule_alloc ->rule_construct ->rule_destruct ->rule_dealloc * group ->group_alloc ->group_construct ->group_destruct ->group_dealloc * * "ofproto", "ofport", and "group" have this exact life cycle. The "rule" * data structure also follow this life cycle with some additional elaborations * described under "Rule Life Cycle" below. * * Any instance of a given data structure goes through the following life * cycle: * * 1. The client calls the "alloc" function to obtain raw memory. If "alloc" * fails, skip all the other steps. * * 2. The client initializes all of the data structure's base state. If this * fails, skip to step 7. * * 3. The client calls the "construct" function. The implementation * initializes derived state. It may refer to the already-initialized * base state. If "construct" fails, skip to step 6. * * 4. The data structure is now initialized and in use. * * 5. When the data structure is no longer needed, the client calls the * "destruct" function. The implementation uninitializes derived state. * The base state has not been uninitialized yet, so the implementation * may still refer to it. * * 6. The client uninitializes all of the data structure's base state. * * 7. The client calls the "dealloc" to free the raw memory. The * implementation must not refer to base or derived state in the data * structure, because it has already been uninitialized. * * Each "alloc" function allocates and returns a new instance of the respective * data structure. The "alloc" function is not given any information about the * use of the new data structure, so it cannot perform much initialization. * Its purpose is just to ensure that the new data structure has enough room * for base and derived state. It may return a null pointer if memory is not * available, in which case none of the other functions is called. * * Each "construct" function initializes derived state in its respective data * structure. When "construct" is called, all of the base state has already * been initialized, so the "construct" function may refer to it. The * "construct" function is allowed to fail, in which case the client calls the * "dealloc" function (but not the "destruct" function). * * Each "destruct" function uninitializes and frees derived state in its * respective data structure. When "destruct" is called, the base state has * not yet been uninitialized, so the "destruct" function may refer to it. The * "destruct" function is not allowed to fail. * * Each "dealloc" function frees raw memory that was allocated by the * "alloc" function. The memory's base and derived members might not have ever * been initialized (but if "construct" returned successfully, then it has been * "destruct"ed already). The "dealloc" function is not allowed to fail. * * * Conventions * =========== * * Most of these functions return 0 if they are successful or a positive error * code on failure. Depending on the function, valid error codes are either * errno values or OFPERR_* OpenFlow error codes. * * Most of these functions are expected to execute synchronously, that is, to * block as necessary to obtain a result. Thus, these functions may return * EAGAIN (or EWOULDBLOCK or EINPROGRESS) only where the function descriptions * explicitly say those errors are a possibility. We may relax this * requirement in the future if and when we encounter performance problems. */ struct ofproto_class { /* ## ----------------- ## */ /* ## Factory Functions ## */ /* ## ----------------- ## */ /* Initializes provider. The caller may pass in 'iface_hints', * which contains an shash of "struct iface_hint" elements indexed * by the interface's name. The provider may use these hints to * describe the startup configuration in order to reinitialize its * state. The caller owns the provided data, so a provider must * make copies of anything required. An ofproto provider must * remove any existing state that is not described by the hint, and * may choose to remove it all. */ void (*init)(const struct shash *iface_hints); /* Enumerates the types of all supported ofproto types into 'types'. The * caller has already initialized 'types'. The implementation should add * its own types to 'types' but not remove any existing ones, because other * ofproto classes might already have added names to it. */ void (*enumerate_types)(struct sset *types); /* Enumerates the names of all existing datapath of the specified 'type' * into 'names' 'all_dps'. The caller has already initialized 'names' as * an empty sset. * * 'type' is one of the types enumerated by ->enumerate_types(). * * Returns 0 if successful, otherwise a positive errno value. */ int (*enumerate_names)(const char *type, struct sset *names); /* Deletes the datapath with the specified 'type' and 'name'. The caller * should have closed any open ofproto with this 'type' and 'name'; this * function is allowed to fail if that is not the case. * * 'type' is one of the types enumerated by ->enumerate_types(). * 'name' is one of the names enumerated by ->enumerate_names() for 'type'. * * Returns 0 if successful, otherwise a positive errno value. */ int (*del)(const char *type, const char *name); /* Returns the type to pass to netdev_open() when a datapath of type * 'datapath_type' has a port of type 'port_type', for a few special * cases when a netdev type differs from a port type. For example, * when using the userspace datapath, a port of type "internal" * needs to be opened as "tap". * * Returns either 'type' itself or a string literal, which must not * be freed. */ const char *(*port_open_type)(const char *datapath_type, const char *port_type); /* ## ------------------------ ## */ /* ## Top-Level type Functions ## */ /* ## ------------------------ ## */ /* Performs any periodic activity required on ofprotos of type * 'type'. * * An ofproto provider may implement it or not, depending on whether * it needs type-level maintenance. * * Returns 0 if successful, otherwise a positive errno value. */ int (*type_run)(const char *type); /* Causes the poll loop to wake up when a type 'type''s 'run' * function needs to be called, e.g. by calling the timer or fd * waiting functions in poll-loop.h. * * An ofproto provider may implement it or not, depending on whether * it needs type-level maintenance. */ void (*type_wait)(const char *type); /* ## --------------------------- ## */ /* ## Top-Level ofproto Functions ## */ /* ## --------------------------- ## */ /* Life-cycle functions for an "ofproto" (see "Life Cycle" above). * * * Construction * ============ * * ->construct() should not modify any base members of the ofproto. The * client will initialize the ofproto's 'ports' and 'tables' members after * construction is complete. * * When ->construct() is called, the client does not yet know how many flow * tables the datapath supports, so ofproto->n_tables will be 0 and * ofproto->tables will be NULL. ->construct() should call * ofproto_init_tables() to allocate and initialize ofproto->n_tables and * ofproto->tables. Each flow table will be initially empty, so * ->construct() should delete flows from the underlying datapath, if * necessary, rather than populating the tables. * * If the ofproto knows the maximum port number that the datapath can have, * then it can call ofproto_init_max_ports(). If it does so, then the * client will ensure that the actions it allows to be used through * OpenFlow do not refer to ports above that maximum number. * * Only one ofproto instance needs to be supported for any given datapath. * If a datapath is already open as part of one "ofproto", then another * attempt to "construct" the same datapath as part of another ofproto is * allowed to fail with an error. * * ->construct() returns 0 if successful, otherwise a positive errno * value. * * * Destruction * =========== * * ->destruct() must also destroy all remaining rules in the ofproto's * tables, by passing each remaining rule to ofproto_rule_delete(), then * destroy all remaining groups by calling ofproto_group_delete_all(). * * The client will destroy the flow tables themselves after ->destruct() * returns. */ struct ofproto *(*alloc)(void); int (*construct)(struct ofproto *ofproto); void (*destruct)(struct ofproto *ofproto); void (*dealloc)(struct ofproto *ofproto); /* Performs any periodic activity required by 'ofproto'. It should: * * - Call connmgr_send_packet_in() for each received packet that missed * in the OpenFlow flow table or that had a OFPP_CONTROLLER output * action. * * - Call ofproto_rule_expire() for each OpenFlow flow that has reached * its hard_timeout or idle_timeout, to expire the flow. * * Returns 0 if successful, otherwise a positive errno value. */ int (*run)(struct ofproto *ofproto); /* Causes the poll loop to wake up when 'ofproto''s 'run' function needs to * be called, e.g. by calling the timer or fd waiting functions in * poll-loop.h. */ void (*wait)(struct ofproto *ofproto); /* Adds some memory usage statistics for the implementation of 'ofproto' * into 'usage', for use with memory_report(). * * This function is optional. */ void (*get_memory_usage)(const struct ofproto *ofproto, struct simap *usage); /* Adds some memory usage statistics for the implementation of 'type' * into 'usage', for use with memory_report(). * * This function is optional. */ void (*type_get_memory_usage)(const char *type, struct simap *usage); /* Every "struct rule" in 'ofproto' is about to be deleted, one by one. * This function may prepare for that, for example by clearing state in * advance. It should *not* actually delete any "struct rule"s from * 'ofproto', only prepare for it. * * This function is optional; it's really just for optimization in case * it's cheaper to delete all the flows from your hardware in a single pass * than to do it one by one. */ void (*flush)(struct ofproto *ofproto); /* Helper for the OpenFlow OFPT_TABLE_FEATURES request. * * The 'features' array contains 'ofproto->n_tables' elements. Each * element is initialized as: * * - 'table_id' to the array index. * * - 'name' to "table#" where # is the table ID. * * - 'metadata_match' and 'metadata_write' to OVS_BE64_MAX. * * - 'config' to the table miss configuration. * * - 'max_entries' to 1,000,000. * * - Both 'nonmiss' and 'miss' to: * * * 'next' to all 1-bits for all later tables. * * * 'instructions' to all instructions. * * * 'write' and 'apply' both to: * * - 'ofpacts': All actions. * * - 'set_fields': All fields. * * - 'match', 'mask', and 'wildcard' to all fields. * * If 'stats' is nonnull, it also contains 'ofproto->n_tables' elements. * Each element is initialized as: * * - 'table_id' to the array index. * * - 'active_count' to the 'n_flows' of struct ofproto for the table. * * - 'lookup_count' and 'matched_count' to 0. * * The implementation should update any members in each element for which * it has better values: * * - Any member of 'features' to better describe the implementation's * capabilities. * * - 'lookup_count' to the number of packets looked up in this flow table * so far. * * - 'matched_count' to the number of packets looked up in this flow * table so far that matched one of the flow entries. */ void (*query_tables)(struct ofproto *ofproto, struct ofputil_table_features *features, struct ofputil_table_stats *stats); /* Sets the current tables version the provider should use for classifier * lookups. */ void (*set_tables_version)(struct ofproto *ofproto, cls_version_t version); /* ## ---------------- ## */ /* ## ofport Functions ## */ /* ## ---------------- ## */ /* Life-cycle functions for a "struct ofport" (see "Life Cycle" above). * * ->port_construct() should not modify any base members of the ofport. * An ofproto implementation should use the 'ofp_port' member of * "struct ofport" as the OpenFlow port number. * * ofports are managed by the base ofproto code. The ofproto * implementation should only create and destroy them in response to calls * to these functions. The base ofproto code will create and destroy * ofports in the following situations: * * - Just after the ->construct() function is called, the base ofproto * iterates over all of the implementation's ports, using * ->port_dump_start() and related functions, and constructs an ofport * for each dumped port. * * - If ->port_poll() reports that a specific port has changed, then the * base ofproto will query that port with ->port_query_by_name() and * construct or destruct ofports as necessary to reflect the updated * set of ports. * * - If ->port_poll() returns ENOBUFS to report an unspecified port set * change, then the base ofproto will iterate over all of the * implementation's ports, in the same way as at ofproto * initialization, and construct and destruct ofports to reflect all of * the changes. * * - On graceful shutdown, the base ofproto code will destruct all * the ports. * * ->port_construct() returns 0 if successful, otherwise a positive errno * value. * * * ->port_destruct() * ================= * * ->port_destruct() takes a 'del' parameter. If the provider implements * the datapath itself (e.g. dpif-netdev, it can ignore 'del'. On the * other hand, if the provider is an interface to an external datapath * (e.g. to a kernel module that implement the datapath) then 'del' should * influence destruction behavior in the following way: * * - If 'del' is true, it should remove the port from the underlying * implementation. This is the common case. * * - If 'del' is false, it should leave the port in the underlying * implementation. This is used when Open vSwitch is performing a * graceful shutdown and ensures that, across Open vSwitch restarts, * the underlying ports are not removed and recreated. That makes an * important difference for, e.g., "internal" ports that have * configured IP addresses; without this distinction, the IP address * and other configured state for the port is lost. */ struct ofport *(*port_alloc)(void); int (*port_construct)(struct ofport *ofport); void (*port_destruct)(struct ofport *ofport, bool del); void (*port_dealloc)(struct ofport *ofport); /* Called after 'ofport->netdev' is replaced by a new netdev object. If * the ofproto implementation uses the ofport's netdev internally, then it * should switch to using the new one. The old one has been closed. * * An ofproto implementation that doesn't need to do anything in this * function may use a null pointer. */ void (*port_modified)(struct ofport *ofport); /* Called after an OpenFlow request changes a port's configuration. * 'ofport->pp.config' contains the new configuration. 'old_config' * contains the previous configuration. * * The caller implements OFPUTIL_PC_PORT_DOWN using netdev functions to * turn NETDEV_UP on and off, so this function doesn't have to do anything * for that bit (and it won't be called if that is the only bit that * changes). */ void (*port_reconfigured)(struct ofport *ofport, enum ofputil_port_config old_config); /* Looks up a port named 'devname' in 'ofproto'. On success, returns 0 and * initializes '*port' appropriately. Otherwise, returns a positive errno * value. * * The caller owns the data in 'port' and must free it with * ofproto_port_destroy() when it is no longer needed. */ int (*port_query_by_name)(const struct ofproto *ofproto, const char *devname, struct ofproto_port *port); /* Attempts to add 'netdev' as a port on 'ofproto'. Returns 0 if * successful, otherwise a positive errno value. The caller should * inform the implementation of the OpenFlow port through the * ->port_construct() method. * * It doesn't matter whether the new port will be returned by a later call * to ->port_poll(); the implementation may do whatever is more * convenient. */ int (*port_add)(struct ofproto *ofproto, struct netdev *netdev); /* Deletes port number 'ofp_port' from the datapath for 'ofproto'. Returns * 0 if successful, otherwise a positive errno value. * * It doesn't matter whether the new port will be returned by a later call * to ->port_poll(); the implementation may do whatever is more * convenient. */ int (*port_del)(struct ofproto *ofproto, ofp_port_t ofp_port); /* Get port stats */ int (*port_get_stats)(const struct ofport *port, struct netdev_stats *stats); /* Port iteration functions. * * The client might not be entirely in control of the ports within an * ofproto. Some hardware implementations, for example, might have a fixed * set of ports in a datapath. For this reason, the client needs a way to * iterate through all the ports that are actually in a datapath. These * functions provide that functionality. * * The 'state' pointer provides the implementation a place to * keep track of its position. Its format is opaque to the caller. * * The ofproto provider retains ownership of the data that it stores into * ->port_dump_next()'s 'port' argument. The data must remain valid until * at least the next call to ->port_dump_next() or ->port_dump_done() for * 'state'. The caller will not modify or free it. * * Details * ======= * * ->port_dump_start() attempts to begin dumping the ports in 'ofproto'. * On success, it should return 0 and initialize '*statep' with any data * needed for iteration. On failure, returns a positive errno value, and * the client will not call ->port_dump_next() or ->port_dump_done(). * * ->port_dump_next() attempts to retrieve another port from 'ofproto' for * 'state'. If there is another port, it should store the port's * information into 'port' and return 0. It should return EOF if all ports * have already been iterated. Otherwise, on error, it should return a * positive errno value. This function will not be called again once it * returns nonzero once for a given iteration (but the 'port_dump_done' * function will be called afterward). * * ->port_dump_done() allows the implementation to release resources used * for iteration. The caller might decide to stop iteration in the middle * by calling this function before ->port_dump_next() returns nonzero. * * Usage Example * ============= * * int error; * void *state; * * error = ofproto->ofproto_class->port_dump_start(ofproto, &state); * if (!error) { * for (;;) { * struct ofproto_port port; * * error = ofproto->ofproto_class->port_dump_next( * ofproto, state, &port); * if (error) { * break; * } * // Do something with 'port' here (without modifying or freeing * // any of its data). * } * ofproto->ofproto_class->port_dump_done(ofproto, state); * } * // 'error' is now EOF (success) or a positive errno value (failure). */ int (*port_dump_start)(const struct ofproto *ofproto, void **statep); int (*port_dump_next)(const struct ofproto *ofproto, void *state, struct ofproto_port *port); int (*port_dump_done)(const struct ofproto *ofproto, void *state); /* Polls for changes in the set of ports in 'ofproto'. If the set of ports * in 'ofproto' has changed, then this function should do one of the * following: * * - Preferably: store the name of the device that was added to or deleted * from 'ofproto' in '*devnamep' and return 0. The caller is responsible * for freeing '*devnamep' (with free()) when it no longer needs it. * * - Alternatively: return ENOBUFS, without indicating the device that was * added or deleted. * * Occasional 'false positives', in which the function returns 0 while * indicating a device that was not actually added or deleted or returns * ENOBUFS without any change, are acceptable. * * The purpose of 'port_poll' is to let 'ofproto' know about changes made * externally to the 'ofproto' object, e.g. by a system administrator via * ovs-dpctl. Therefore, it's OK, and even preferable, for port_poll() to * not report changes made through calls to 'port_add' or 'port_del' on the * same 'ofproto' object. (But it's OK for it to report them too, just * slightly less efficient.) * * If the set of ports in 'ofproto' has not changed, returns EAGAIN. May * also return other positive errno values to indicate that something has * gone wrong. * * If the set of ports in a datapath is fixed, or if the only way that the * set of ports in a datapath can change is through ->port_add() and * ->port_del(), then this function may be a null pointer. */ int (*port_poll)(const struct ofproto *ofproto, char **devnamep); /* Arranges for the poll loop to wake up when ->port_poll() will return a * value other than EAGAIN. * * If the set of ports in a datapath is fixed, or if the only way that the * set of ports in a datapath can change is through ->port_add() and * ->port_del(), or if the poll loop will always wake up anyway when * ->port_poll() will return a value other than EAGAIN, then this function * may be a null pointer. */ void (*port_poll_wait)(const struct ofproto *ofproto); /* Checks the status of LACP negotiation for 'port'. Returns 1 if LACP * partner information for 'port' is up-to-date, 0 if LACP partner * information is not current (generally indicating a connectivity * problem), or -1 if LACP is not enabled on 'port'. * * This function may be a null pointer if the ofproto implementation does * not support LACP. */ int (*port_is_lacp_current)(const struct ofport *port); /* Get LACP port stats. Returns -1 if LACP is not enabled on 'port'. * * This function may be a null pointer if the ofproto implementation does * not support LACP. */ int (*port_get_lacp_stats)(const struct ofport *port, struct lacp_slave_stats *stats); /* ## ----------------------- ## */ /* ## OpenFlow Rule Functions ## */ /* ## ----------------------- ## */ /* Chooses an appropriate table for 'match' within 'ofproto'. On * success, stores the table ID into '*table_idp' and returns 0. On * failure, returns an OpenFlow error code. * * The choice of table should be a function of 'match' and 'ofproto''s * datapath capabilities. It should not depend on the flows already in * 'ofproto''s flow tables. Failure implies that an OpenFlow rule with * 'match' as its matching condition can never be inserted into 'ofproto', * even starting from an empty flow table. * * If multiple tables are candidates for inserting the flow, the function * should choose one arbitrarily (but deterministically). * * If this function is NULL then table 0 is always chosen. */ enum ofperr (*rule_choose_table)(const struct ofproto *ofproto, const struct match *match, uint8_t *table_idp); /* Life-cycle functions for a "struct rule". * * * Rule Life Cycle * =============== * * The life cycle of a struct rule is an elaboration of the basic life * cycle described above under "Life Cycle". * * After a rule is successfully constructed, it is then inserted. If * insertion is successful, then before it is later destructed, it is * deleted. * * You can think of a rule as having the following extra steps inserted * between "Life Cycle" steps 4 and 5: * * 4.1. The client inserts the rule into the flow table, making it * visible in flow table lookups. * * 4.2. The client calls "rule_insert" to insert the flow. The * implementation attempts to install the flow in the underlying * hardware and returns an error code indicate success or failure. * On failure, go to step 5. * * 4.3. The rule is now installed in the flow table. Eventually it will * be deleted. * * 4.4. The client removes the rule from the flow table. It is no longer * visible in flow table lookups. * * 4.5. The client calls "rule_delete". The implementation uninstalls * the flow from the underlying hardware. Deletion is not allowed * to fail. * * * Construction * ============ * * When ->rule_construct() is called, 'rule' is a new rule that is not yet * inserted into a flow table. ->rule_construct() should initialize enough * of the rule's derived state for 'rule' to be suitable for inserting into * a flow table. ->rule_construct() should not modify any base members of * struct rule. * * If ->rule_construct() fails (as indicated by returning a nonzero * OpenFlow error code), the ofproto base code will uninitialize and * deallocate 'rule'. See "Rule Life Cycle" above for more details. * * ->rule_construct() must also: * * - Validate that the datapath supports the matching rule in 'rule->cr' * datapath. For example, if the rule's table does not support * registers, then it is an error if 'rule->cr' does not wildcard all * registers. * * - Validate that the datapath can correctly implement 'rule->ofpacts'. * * After a successful construction the rest of the rule life cycle calls * may not fail, so ->rule_construct() must also make sure that the rule * can be inserted in to the datapath. * * * Insertion * ========= * * Following successful construction, the ofproto base case inserts 'rule' * into its flow table, then it calls ->rule_insert(). ->rule_insert() * must add the new rule to the datapath flow table and return only after * this is complete. The 'new_rule' may be a duplicate of an 'old_rule'. * In this case the 'old_rule' is non-null, and the implementation should * forward rule statistics counts from the 'old_rule' to the 'new_rule' if * 'forward_counts' is 'true', 'used' timestamp is always forwarded. This * may not fail. * * * Deletion * ======== * * The ofproto base code removes 'rule' from its flow table before it calls * ->rule_delete(). ->rule_delete() must remove 'rule' from the datapath * flow table and return only after this has completed successfully. * * Rule deletion must not fail. * * * Destruction * =========== * * ->rule_destruct() must uninitialize derived state. * * Rule destruction must not fail. */ struct rule *(*rule_alloc)(void); enum ofperr (*rule_construct)(struct rule *rule) /* OVS_REQUIRES(ofproto_mutex) */; void (*rule_insert)(struct rule *rule, struct rule *old_rule, bool forward_counts) /* OVS_REQUIRES(ofproto_mutex) */; void (*rule_delete)(struct rule *rule) /* OVS_REQUIRES(ofproto_mutex) */; void (*rule_destruct)(struct rule *rule); void (*rule_dealloc)(struct rule *rule); /* Obtains statistics for 'rule', storing the number of packets that have * matched it in '*packet_count' and the number of bytes in those packets * in '*byte_count'. UINT64_MAX indicates that the packet count or byte * count is unknown. */ void (*rule_get_stats)(struct rule *rule, uint64_t *packet_count, uint64_t *byte_count, long long int *used) /* OVS_EXCLUDED(ofproto_mutex) */; /* Applies the actions in 'rule' to 'packet'. (This implements sending * buffered packets for OpenFlow OFPT_FLOW_MOD commands.) * * Takes ownership of 'packet' (so it should eventually free it, with * ofpbuf_delete()). * * 'flow' reflects the flow information for 'packet'. All of the * information in 'flow' is extracted from 'packet', except for * flow->tunnel and flow->in_port, which are assigned the correct values * for the incoming packet. The register values are zeroed. 'packet''s * header pointers and offsets (e.g. packet->l3) are appropriately * initialized. packet->l3 is aligned on a 32-bit boundary. * * The implementation should add the statistics for 'packet' into 'rule'. * * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow, struct dp_packet *packet); /* Changes the OpenFlow IP fragment handling policy to 'frag_handling', * which takes one of the following values, with the corresponding * meanings: * * - OFPC_FRAG_NORMAL: The switch should treat IP fragments the same way * as other packets, omitting TCP and UDP port numbers (always setting * them to 0). * * - OFPC_FRAG_DROP: The switch should drop all IP fragments without * passing them through the flow table. * * - OFPC_FRAG_REASM: The switch should reassemble IP fragments before * passing packets through the flow table. * * - OFPC_FRAG_NX_MATCH (a Nicira extension): Similar to OFPC_FRAG_NORMAL, * except that TCP and UDP port numbers should be included in fragments * with offset 0. * * Implementations are not required to support every mode. * OFPC_FRAG_NORMAL is the default mode when an ofproto is created. * * At the time of the call to ->set_frag_handling(), the current mode is * available in 'ofproto->frag_handling'. ->set_frag_handling() returns * true if the requested mode was set, false if it is not supported. * * Upon successful return, the caller changes 'ofproto->frag_handling' to * reflect the new mode. */ bool (*set_frag_handling)(struct ofproto *ofproto, enum ofp_config_flags frag_handling); /* Implements the OpenFlow OFPT_PACKET_OUT command. The datapath should * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'. * * The caller retains ownership of 'packet' and of 'ofpacts', so * ->packet_out() should not modify or free them. * * This function must validate that it can correctly implement 'ofpacts'. * If not, then it should return an OpenFlow error code. * * 'flow' reflects the flow information for 'packet'. All of the * information in 'flow' is extracted from 'packet', except for * flow->in_port (see below). flow->tunnel and its register values are * zeroed. * * flow->in_port comes from the OpenFlow OFPT_PACKET_OUT message. The * implementation should reject invalid flow->in_port values by returning * OFPERR_OFPBRC_BAD_PORT. (If the implementation called * ofproto_init_max_ports(), then the client will reject these ports * itself.) For consistency, the implementation should consider valid for * flow->in_port any value that could possibly be seen in a packet that it * passes to connmgr_send_packet_in(). Ideally, even an implementation * that never generates packet-ins (e.g. due to hardware limitations) * should still allow flow->in_port values for every possible physical port * and OFPP_LOCAL. The only virtual ports (those above OFPP_MAX) that the * caller will ever pass in as flow->in_port, other than OFPP_LOCAL, are * OFPP_NONE and OFPP_CONTROLLER. The implementation should allow both of * these, treating each of them as packets generated by the controller as * opposed to packets originating from some switch port. * * (Ordinarily the only effect of flow->in_port is on output actions that * involve the input port, such as actions that output to OFPP_IN_PORT, * OFPP_FLOOD, or OFPP_ALL. flow->in_port can also affect Nicira extension * "resubmit" actions.) * * 'packet' is not matched against the OpenFlow flow table, so its * statistics should not be included in OpenFlow flow statistics. * * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr (*packet_out)(struct ofproto *ofproto, struct dp_packet *packet, const struct flow *flow, const struct ofpact *ofpacts, size_t ofpacts_len); /* ## ------------------------- ## */ /* ## OFPP_NORMAL configuration ## */ /* ## ------------------------- ## */ /* Configures NetFlow on 'ofproto' according to the options in * 'netflow_options', or turns off NetFlow if 'netflow_options' is NULL. * * EOPNOTSUPP as a return value indicates that 'ofproto' does not support * NetFlow, as does a null pointer. */ int (*set_netflow)(struct ofproto *ofproto, const struct netflow_options *netflow_options); void (*get_netflow_ids)(const struct ofproto *ofproto, uint8_t *engine_type, uint8_t *engine_id); /* Configures sFlow on 'ofproto' according to the options in * 'sflow_options', or turns off sFlow if 'sflow_options' is NULL. * * EOPNOTSUPP as a return value indicates that 'ofproto' does not support * sFlow, as does a null pointer. */ int (*set_sflow)(struct ofproto *ofproto, const struct ofproto_sflow_options *sflow_options); /* Configures IPFIX on 'ofproto' according to the options in * 'bridge_exporter_options' and the 'flow_exporters_options' * array, or turns off IPFIX if 'bridge_exporter_options' and * 'flow_exporters_options' is NULL. * * EOPNOTSUPP as a return value indicates that 'ofproto' does not support * IPFIX, as does a null pointer. */ int (*set_ipfix)( struct ofproto *ofproto, const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options, const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options, size_t n_flow_exporters_options); /* Configures connectivity fault management on 'ofport'. * * If 'cfm_settings' is nonnull, configures CFM according to its members. * * If 'cfm_settings' is null, removes any connectivity fault management * configuration from 'ofport'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support CFM, as does a null pointer. */ int (*set_cfm)(struct ofport *ofport, const struct cfm_settings *s); /* Checks the status change of CFM on 'ofport'. Returns true if * there is status change since last call or if CFM is not specified. */ bool (*cfm_status_changed)(struct ofport *ofport); /* Populates 'smap' with the status of CFM on 'ofport'. Returns 0 on * success, or a positive errno. EOPNOTSUPP as a return value indicates * that this ofproto_class does not support CFM, as does a null pointer. * * The caller must provide and own '*status', and it must free the array * returned in 'status->rmps'. '*status' is indeterminate if the return * value is non-zero. */ int (*get_cfm_status)(const struct ofport *ofport, struct cfm_status *status); /* Configures LLDP on 'ofport'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support LLDP, as does a null pointer. */ int (*set_lldp)(struct ofport *ofport, const struct smap *cfg); /* Checks the status of LLDP configured on 'ofport'. Returns true if the * port's LLDP status was successfully stored into '*status'. Returns * false if the port did not have LLDP configured, in which case '*status' * is indeterminate. * * The caller must provide and own '*status'. '*status' is indeterminate * if the return value is non-zero. */ bool (*get_lldp_status)(const struct ofport *ofport, struct lldp_status *status); /* Configures Auto Attach. * * If 's' is nonnull, configures Auto Attach according to its members. * * If 's' is null, removes any Auto Attach configuration. */ int (*set_aa)(struct ofproto *ofproto, const struct aa_settings *s); /* If 's' is nonnull, this function registers a mapping associated with * client data pointer 'aux' in 'ofproto'. If 'aux' is already registered * then this function updates its configuration to 's'. Otherwise, this * function registers a new mapping. * * An implementation that does not support mapping at all may set * it to NULL or return EOPNOTSUPP. An implementation that supports * only a subset of the functionality should implement what it can * and return 0. */ int (*aa_mapping_set)(struct ofproto *ofproto, void *aux, const struct aa_mapping_settings *s); /* If 's' is nonnull, this function unregisters a mapping associated with * client data pointer 'aux' in 'ofproto'. If 'aux' is already registered * then this function updates its configuration to 's'. Otherwise, this * function unregisters a new mapping. * * An implementation that does not support mapping at all may set * it to NULL or return EOPNOTSUPP. An implementation that supports * only a subset of the functionality should implement what it can * and return 0. */ int (*aa_mapping_unset)(struct ofproto *ofproto, void *aux); /* * Returns the a list of AutoAttach VLAN operations. When Auto Attach is * enabled, the VLAN associated with an I-SID/VLAN mapping is first * negotiated with an Auto Attach Server. Once an I-SID VLAN mapping * becomes active, the corresponding VLAN needs to be communicated to the * bridge in order to add the VLAN to the trunk port linking the Auto * Attach Client (in this case openvswitch) and the Auto Attach Server. * * The list entries are of type "struct bridge_aa_vlan". Each entry * specifies the operation (add or remove), the interface on which to * execute the operation and the VLAN. */ int (*aa_vlan_get_queued)(struct ofproto *ofproto, struct ovs_list *list); /* * Returns the current number of entries in the list of VLAN operations * in the Auto Attach Client (see previous function description * aa_vlan_get_queued). Returns 0 if Auto Attach is disabled. */ unsigned int (*aa_vlan_get_queue_size)(struct ofproto *ofproto); /* Configures BFD on 'ofport'. * * If 'cfg' is NULL, or 'cfg' does not contain the key value pair * "enable=true", removes BFD from 'ofport'. Otherwise, configures BFD * according to 'cfg'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support BFD, as does a null pointer. */ int (*set_bfd)(struct ofport *ofport, const struct smap *cfg); /* Checks the status change of BFD on 'ofport'. Returns true if there * is status change since last call or if BFD is not specified. */ bool (*bfd_status_changed)(struct ofport *ofport); /* Populates 'smap' with the status of BFD on 'ofport'. Returns 0 on * success, or a positive errno. EOPNOTSUPP as a return value indicates * that this ofproto_class does not support BFD, as does a null pointer. */ int (*get_bfd_status)(struct ofport *ofport, struct smap *smap); /* Configures spanning tree protocol (STP) on 'ofproto' using the * settings defined in 's'. * * If 's' is nonnull, configures STP according to its members. * * If 's' is null, removes any STP configuration from 'ofproto'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ int (*set_stp)(struct ofproto *ofproto, const struct ofproto_stp_settings *s); /* Retrieves state of spanning tree protocol (STP) on 'ofproto'. * * Stores STP state for 'ofproto' in 's'. If the 'enabled' member * is false, the other member values are not meaningful. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ int (*get_stp_status)(struct ofproto *ofproto, struct ofproto_stp_status *s); /* Configures spanning tree protocol (STP) on 'ofport' using the * settings defined in 's'. * * If 's' is nonnull, configures STP according to its members. The * caller is responsible for assigning STP port numbers (using the * 'port_num' member in the range of 1 through 255, inclusive) and * ensuring there are no duplicates. * * If 's' is null, removes any STP configuration from 'ofport'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ int (*set_stp_port)(struct ofport *ofport, const struct ofproto_port_stp_settings *s); /* Retrieves spanning tree protocol (STP) port status of 'ofport'. * * Stores STP state for 'ofport' in 's'. If the 'enabled' member is * false, the other member values are not meaningful. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ int (*get_stp_port_status)(struct ofport *ofport, struct ofproto_port_stp_status *s); /* Retrieves spanning tree protocol (STP) port statistics of 'ofport'. * * Stores STP state for 'ofport' in 's'. If the 'enabled' member is * false, the other member values are not meaningful. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ int (*get_stp_port_stats)(struct ofport *ofport, struct ofproto_port_stp_stats *s); /* Configures Rapid Spanning Tree Protocol (RSTP) on 'ofproto' using the * settings defined in 's'. * * If 's' is nonnull, configures RSTP according to its members. * * If 's' is null, removes any RSTP configuration from 'ofproto'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support RSTP, as does a null pointer. */ void (*set_rstp)(struct ofproto *ofproto, const struct ofproto_rstp_settings *s); /* Retrieves state of Rapid Spanning Tree Protocol (RSTP) on 'ofproto'. * * Stores RSTP state for 'ofproto' in 's'. If the 'enabled' member * is false, the other member values are not meaningful. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support RSTP, as does a null pointer. */ void (*get_rstp_status)(struct ofproto *ofproto, struct ofproto_rstp_status *s); /* Configures Rapid Spanning Tree Protocol (RSTP) on 'ofport' using the * settings defined in 's'. * * If 's' is nonnull, configures RSTP according to its members. The * caller is responsible for assigning RSTP port numbers (using the * 'port_num' member in the range of 1 through 255, inclusive) and * ensuring there are no duplicates. * * If 's' is null, removes any RSTP configuration from 'ofport'. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support STP, as does a null pointer. */ void (*set_rstp_port)(struct ofport *ofport, const struct ofproto_port_rstp_settings *s); /* Retrieves Rapid Spanning Tree Protocol (RSTP) port status of 'ofport'. * * Stores RSTP state for 'ofport' in 's'. If the 'enabled' member is * false, the other member values are not meaningful. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support RSTP, as does a null pointer. */ void (*get_rstp_port_status)(struct ofport *ofport, struct ofproto_port_rstp_status *s); /* Registers meta-data associated with the 'n_qdscp' Qualities of Service * 'queues' attached to 'ofport'. This data is not intended to be * sufficient to implement QoS. Instead, providers may use this * information to implement features which require knowledge of what queues * exist on a port, and some basic information about them. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support QoS, as does a null pointer. */ int (*set_queues)(struct ofport *ofport, const struct ofproto_port_queue *queues, size_t n_qdscp); /* If 's' is nonnull, this function registers a "bundle" associated with * client data pointer 'aux' in 'ofproto'. A bundle is the same concept as * a Port in OVSDB, that is, it consists of one or more "slave" devices * (Interfaces, in OVSDB) along with VLAN and LACP configuration and, if * there is more than one slave, a bonding configuration. If 'aux' is * already registered then this function updates its configuration to 's'. * Otherwise, this function registers a new bundle. * * If 's' is NULL, this function unregisters the bundle registered on * 'ofproto' associated with client data pointer 'aux'. If no such bundle * has been registered, this has no effect. * * This function affects only the behavior of the NXAST_AUTOPATH action and * output to the OFPP_NORMAL port. An implementation that does not support * it at all may set it to NULL or return EOPNOTSUPP. An implementation * that supports only a subset of the functionality should implement what * it can and return 0. */ int (*bundle_set)(struct ofproto *ofproto, void *aux, const struct ofproto_bundle_settings *s); /* If 'port' is part of any bundle, removes it from that bundle. If the * bundle now has no ports, deletes the bundle. If the bundle now has only * one port, deconfigures the bundle's bonding configuration. */ void (*bundle_remove)(struct ofport *ofport); /* If 's' is nonnull, this function registers a mirror associated with * client data pointer 'aux' in 'ofproto'. A mirror is the same concept as * a Mirror in OVSDB. If 'aux' is already registered then this function * updates its configuration to 's'. Otherwise, this function registers a * new mirror. * * If 's' is NULL, this function unregisters the mirror registered on * 'ofproto' associated with client data pointer 'aux'. If no such mirror * has been registered, this has no effect. * * An implementation that does not support mirroring at all may set * it to NULL or return EOPNOTSUPP. An implementation that supports * only a subset of the functionality should implement what it can * and return 0. */ int (*mirror_set)(struct ofproto *ofproto, void *aux, const struct ofproto_mirror_settings *s); /* Retrieves statistics from mirror associated with client data * pointer 'aux' in 'ofproto'. Stores packet and byte counts in * 'packets' and 'bytes', respectively. If a particular counter is * not supported, the appropriate argument is set to UINT64_MAX. * * EOPNOTSUPP as a return value indicates that this ofproto_class does not * support retrieving mirror statistics. */ int (*mirror_get_stats)(struct ofproto *ofproto, void *aux, uint64_t *packets, uint64_t *bytes); /* Configures the VLANs whose bits are set to 1 in 'flood_vlans' as VLANs * on which all packets are flooded, instead of using MAC learning. If * 'flood_vlans' is NULL, then MAC learning applies to all VLANs. * * This function affects only the behavior of the OFPP_NORMAL action. An * implementation that does not support it may set it to NULL or return * EOPNOTSUPP. */ int (*set_flood_vlans)(struct ofproto *ofproto, unsigned long *flood_vlans); /* Returns true if 'aux' is a registered bundle that is currently in use as * the output for a mirror. */ bool (*is_mirror_output_bundle)(const struct ofproto *ofproto, void *aux); /* When the configuration option of forward_bpdu changes, this function * will be invoked. */ void (*forward_bpdu_changed)(struct ofproto *ofproto); /* Sets the MAC aging timeout for the OFPP_NORMAL action to 'idle_time', in * seconds, and the maximum number of MAC table entries to * 'max_entries'. * * An implementation that doesn't support configuring these features may * set this function to NULL or implement it as a no-op. */ void (*set_mac_table_config)(struct ofproto *ofproto, unsigned int idle_time, size_t max_entries); /* Configures multicast snooping on 'ofport' using the settings * defined in 's'. * * If 's' is nonnull, this function updates multicast snooping * configuration to 's' in 'ofproto'. * * If 's' is NULL, this function disables multicast snooping * on 'ofproto'. * * An implementation that does not support multicast snooping may set * it to NULL or return EOPNOTSUPP. */ int (*set_mcast_snooping)(struct ofproto *ofproto, const struct ofproto_mcast_snooping_settings *s); /* Configures multicast snooping port's flood setting on 'ofproto'. * * If 's' is nonnull, this function updates multicast snooping * configuration to 's' in 'ofproto'. * * If 's' is NULL, this function doesn't change anything. * * An implementation that does not support multicast snooping may set * it to NULL or return EOPNOTSUPP. */ int (*set_mcast_snooping_port)(struct ofproto *ofproto_, void *aux, const struct ofproto_mcast_snooping_port_settings *s); /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ /* If 'realdev_ofp_port' is nonzero, then this function configures 'ofport' * as a VLAN splinter port for VLAN 'vid', associated with the real device * that has OpenFlow port number 'realdev_ofp_port'. * * If 'realdev_ofp_port' is zero, then this function deconfigures 'ofport' * as a VLAN splinter port. * * This function should be NULL if an implementation does not support it. */ int (*set_realdev)(struct ofport *ofport, ofp_port_t realdev_ofp_port, int vid); /* ## ------------------------ ## */ /* ## OpenFlow meter functions ## */ /* ## ------------------------ ## */ /* These functions should be NULL if an implementation does not support * them. They must be all null or all non-null.. */ /* Initializes 'features' to describe the metering features supported by * 'ofproto'. */ void (*meter_get_features)(const struct ofproto *ofproto, struct ofputil_meter_features *features); /* If '*id' is UINT32_MAX, adds a new meter with the given 'config'. On * success the function must store a provider meter ID other than * UINT32_MAX in '*id'. All further references to the meter will be made * with the returned provider meter id rather than the OpenFlow meter id. * The caller does not try to interpret the provider meter id, giving the * implementation the freedom to either use the OpenFlow meter_id value * provided in the meter configuration, or any other value suitable for the * implementation. * * If '*id' is a value other than UINT32_MAX, modifies the existing meter * with that meter provider ID to have configuration 'config', while * leaving '*id' unchanged. On failure, the existing meter configuration * is left intact. */ enum ofperr (*meter_set)(struct ofproto *ofproto, ofproto_meter_id *id, const struct ofputil_meter_config *config); /* Gets the meter and meter band packet and byte counts for maximum of * 'stats->n_bands' bands for the meter with provider ID 'id' within * 'ofproto'. The caller fills in the other stats values. The band stats * are copied to memory at 'stats->bands' provided by the caller. The * number of returned band stats is returned in 'stats->n_bands'. */ enum ofperr (*meter_get)(const struct ofproto *ofproto, ofproto_meter_id id, struct ofputil_meter_stats *stats); /* Deletes a meter, making the 'ofproto_meter_id' invalid for any * further calls. */ void (*meter_del)(struct ofproto *, ofproto_meter_id); /* ## -------------------- ## */ /* ## OpenFlow 1.1+ groups ## */ /* ## -------------------- ## */ struct ofgroup *(*group_alloc)(void); enum ofperr (*group_construct)(struct ofgroup *); void (*group_destruct)(struct ofgroup *); void (*group_dealloc)(struct ofgroup *); enum ofperr (*group_modify)(struct ofgroup *); enum ofperr (*group_get_stats)(const struct ofgroup *, struct ofputil_group_stats *); /* ## --------------------- ## */ /* ## Datapath information ## */ /* ## --------------------- ## */ /* Retrieve the version string of the datapath. The version * string can be NULL if it can not be determined. * * The version retuned is read only. The caller should not * free it. * * This function should be NULL if an implementation does not support it. */ const char *(*get_datapath_version)(const struct ofproto *); }; extern const struct ofproto_class ofproto_dpif_class; int ofproto_class_register(const struct ofproto_class *); int ofproto_class_unregister(const struct ofproto_class *); /* flow_mod with execution context. */ struct ofproto_flow_mod { struct ofputil_flow_mod fm; cls_version_t version; /* Version in which changes take * effect. */ struct rule_collection old_rules; /* Affected rules. */ struct rule_collection new_rules; /* Replacement rules. */ }; /* port_mod with execution context. */ struct ofproto_port_mod { struct ofputil_port_mod pm; struct ofport *port; /* Affected port. */ }; enum ofperr ofproto_flow_mod(struct ofproto *, struct ofproto_flow_mod *) OVS_EXCLUDED(ofproto_mutex); void ofproto_add_flow(struct ofproto *, const struct match *, int priority, const struct ofpact *ofpacts, size_t ofpacts_len) OVS_EXCLUDED(ofproto_mutex); void ofproto_delete_flow(struct ofproto *, const struct match *, int priority) OVS_EXCLUDED(ofproto_mutex); void ofproto_flush_flows(struct ofproto *); enum ofperr ofproto_check_ofpacts(struct ofproto *, const struct ofpact ofpacts[], size_t ofpacts_len); static inline const struct rule_actions * rule_get_actions(const struct rule *rule) { return rule->actions; } /* Returns true if 'rule' is an OpenFlow 1.3 "table-miss" rule, false * otherwise. * * ("Table-miss" rules are special because a packet_in generated through one * uses OFPR_NO_MATCH as its reason, whereas packet_ins generated by any other * rule use OFPR_ACTION.) */ static inline bool rule_is_table_miss(const struct rule *rule) { return rule->cr.priority == 0 && cls_rule_is_catchall(&rule->cr); } /* Returns true if 'rule' should be hidden from the controller. * * Rules with priority higher than UINT16_MAX are set up by ofproto itself * (e.g. by in-band control) and are intentionally hidden from the * controller. */ static inline bool rule_is_hidden(const struct rule *rule) { return rule->cr.priority > UINT16_MAX; } static inline struct rule * rule_from_cls_rule(const struct cls_rule *cls_rule) { return cls_rule ? CONTAINER_OF(cls_rule, struct rule, cr) : NULL; } #endif /* ofproto/ofproto-provider.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-xlate.h0000644000000000000000000000013113534540071021227 xustar0030 mtime=1567801401.641682932 29 atime=1567801402.10968637 30 ctime=1567801425.053855431 openvswitch-2.5.9/ofproto/ofproto-dpif-xlate.h0000644000175000017500000002163013534540071022720 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_XLATE_H #define OFPROTO_DPIF_XLATE_H 1 #include "dp-packet.h" #include "flow.h" #include "meta-flow.h" #include "odp-util.h" #include "ofpbuf.h" #include "ofproto-dpif-mirror.h" #include "ofproto-dpif-rid.h" #include "ofproto-dpif.h" #include "ofproto.h" #include "stp.h" #include "ovs-lldp.h" struct bfd; struct bond; struct dpif; struct lacp; struct dpif_ipfix; struct dpif_sflow; struct mac_learning; struct mcast_snooping; struct xlate_cache; struct xlate_out { enum slow_path_reason slow; /* 0 if fast path may be used. */ bool fail_open; /* Initial rule is fail open? */ struct recirc_refs recircs; /* Recirc action IDs on which references are * held. */ }; struct xlate_in { struct ofproto_dpif *ofproto; /* Flow to which the OpenFlow actions apply. xlate_actions() will modify * this flow when actions change header fields. */ struct flow flow; /* The packet corresponding to 'flow', or a null pointer if we are * revalidating without a packet to refer to. */ const struct dp_packet *packet; /* Should OFPP_NORMAL update the MAC learning table? Should "learn" * actions update the flow table? * * We want to update these tables if we are actually processing a packet, * or if we are accounting for packets that the datapath has processed, but * not if we are just revalidating. */ bool may_learn; /* The rule initiating translation or NULL. If both 'rule' and 'ofpacts' * are NULL, xlate_actions() will do the initial rule lookup itself. */ struct rule_dpif *rule; /* The actions to translate. If 'rule' is not NULL, these may be NULL. */ const struct ofpact *ofpacts; size_t ofpacts_len; /* Union of the set of TCP flags seen so far in this flow. (Used only by * NXAST_FIN_TIMEOUT. Set to zero to avoid updating updating rules' * timeouts.) */ uint16_t tcp_flags; /* If nonnull, flow translation calls this function just before executing a * resubmit or OFPP_TABLE action. In addition, disables logging of traces * when the recursion depth is exceeded. * * 'rule' is the rule being submitted into. It will be null if the * resubmit or OFPP_TABLE action didn't find a matching rule. * * 'recurse' is the resubmit recursion depth at time of invocation. * * This is normally null so the client has to set it manually after * calling xlate_in_init(). */ void (*resubmit_hook)(struct xlate_in *, struct rule_dpif *rule, int recurse); /* If nonnull, flow translation calls this function to report some * significant decision, e.g. to explain why OFPP_NORMAL translation * dropped a packet. 'recurse' is the resubmit recursion depth at time of * invocation. */ void (*report_hook)(struct xlate_in *, int recurse, const char *format, va_list args); /* If nonnull, flow translation credits the specified statistics to each * rule reached through a resubmit or OFPP_TABLE action. * * This is normally null so the client has to set it manually after * calling xlate_in_init(). */ const struct dpif_flow_stats *resubmit_stats; /* Recursion and resubmission levels carried over from a pre-existing * translation of a related flow. An example of when this can occur is * the translation of an ARP packet that was generated as the result of * outputting to a tunnel port. In this case, the original flow going to * the tunnel is the related flow. Since the two flows are different, they * should not use the same xlate_ctx structure. However, we still need * limit the maximum recursion across the entire translation. * * These fields are normally set to zero, so the client has to set them * manually after calling xlate_in_init(). In that case, they should be * copied from the same-named fields in the related flow's xlate_ctx. */ int recurse; int resubmits; /* If nonnull, flow translation populates this cache with references to all * modules that are affected by translation. This 'xlate_cache' may be * passed to xlate_push_stats() to perform the same function as * xlate_actions() without the full cost of translation. * * This is normally null so the client has to set it manually after * calling xlate_in_init(). */ struct xlate_cache *xcache; /* If nonnull, flow translation puts the resulting datapath actions in this * buffer. If null, flow translation will not produce datapath actions. */ struct ofpbuf *odp_actions; /* If nonnull, flow translation populates this with wildcards relevant in * translation. Any fields that were used to calculate the action are set, * to allow caching and kernel wildcarding to work. For example, if the * flow lookup involved performing the "normal" action on IPv4 and ARP * packets, 'wc' would have the 'in_port' (always set), 'dl_type' (flow * match), 'vlan_tci' (normal action), and 'dl_dst' (normal action) fields * set. */ struct flow_wildcards *wc; /* The recirculation context related to this translation, as returned by * xlate_lookup. */ const struct recirc_id_node *recirc; }; void xlate_ofproto_set(struct ofproto_dpif *, const char *name, struct dpif *, const struct mac_learning *, struct stp *, struct rstp *, const struct mcast_snooping *, const struct mbridge *, const struct dpif_sflow *, const struct dpif_ipfix *, const struct netflow *, bool forward_bpdu, bool has_in_band, const struct dpif_backer_support *support); void xlate_remove_ofproto(struct ofproto_dpif *); void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *, const char *name, enum port_vlan_mode, int vlan, unsigned long *trunks, bool use_priority_tags, const struct bond *, const struct lacp *, bool floodable); void xlate_bundle_remove(struct ofbundle *); void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *, struct ofport_dpif *, ofp_port_t, odp_port_t, const struct netdev *, const struct cfm *, const struct bfd *, const struct lldp *, struct ofport_dpif *peer, int stp_port_no, const struct rstp_port *rstp_port, const struct ofproto_port_queue *qdscp, size_t n_qdscp, enum ofputil_port_config, enum ofputil_port_state, bool is_tunnel, bool may_enable); void xlate_ofport_remove(struct ofport_dpif *); struct ofproto_dpif * xlate_lookup_ofproto(const struct dpif_backer *, const struct flow *, ofp_port_t *ofp_in_port); int xlate_lookup(const struct dpif_backer *, const struct flow *, struct ofproto_dpif **, struct dpif_ipfix **, struct dpif_sflow **, struct netflow **, ofp_port_t *ofp_in_port); enum xlate_error { XLATE_OK = 0, XLATE_BRIDGE_NOT_FOUND, XLATE_RECURSION_TOO_DEEP, XLATE_TOO_MANY_RESUBMITS, XLATE_STACK_TOO_DEEP, XLATE_NO_RECIRCULATION_CONTEXT, XLATE_RECIRCULATION_CONFLICT, XLATE_TOO_MANY_MPLS_LABELS, }; const char *xlate_strerror(enum xlate_error error); enum xlate_error xlate_actions(struct xlate_in *, struct xlate_out *); void xlate_in_init(struct xlate_in *, struct ofproto_dpif *, const struct flow *, ofp_port_t in_port, struct rule_dpif *, uint16_t tcp_flags, const struct dp_packet *packet, struct flow_wildcards *, struct ofpbuf *odp_actions); void xlate_out_uninit(struct xlate_out *); void xlate_actions_for_side_effects(struct xlate_in *); int xlate_send_packet(const struct ofport_dpif *, struct dp_packet *); struct xlate_cache *xlate_cache_new(void); void xlate_push_stats(struct xlate_cache *, const struct dpif_flow_stats *); void xlate_cache_clear(struct xlate_cache *); void xlate_cache_delete(struct xlate_cache *); void xlate_txn_start(void); void xlate_txn_commit(void); #endif /* ofproto-dpif-xlate.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/pinsched.h0000644000000000000000000000013013534540071017300 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.057855461 openvswitch-2.5.9/ofproto/pinsched.h0000644000175000017500000000324413534540071020773 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PINSCHED_H #define PINSCHED_H_H 1 #include #include "flow.h" struct ovs_list; struct ofpbuf; struct pinsched *pinsched_create(int rate_limit, int burst_limit); void pinsched_get_limits(const struct pinsched *, int *rate_limit, int *burst_limit); void pinsched_set_limits(struct pinsched *, int rate_limit, int burst_limit); void pinsched_destroy(struct pinsched *); void pinsched_send(struct pinsched *, ofp_port_t port_no, struct ofpbuf *, struct ovs_list *txq); void pinsched_run(struct pinsched *, struct ovs_list *txq); void pinsched_wait(struct pinsched *); struct pinsched_stats { unsigned int n_queued; /* # currently queued to send. */ unsigned long long n_normal; /* # txed w/o rate limit queuing. */ unsigned long long n_limited; /* # queued for rate limiting. */ unsigned long long n_queue_dropped; /* # dropped due to queue overflow. */ }; void pinsched_get_stats(const struct pinsched *, struct pinsched_stats *); #endif /* pinsched.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif.h0000644000000000000000000000013113534540071020114 xustar0030 mtime=1567801401.649682991 29 atime=1567801402.10968637 30 ctime=1567801425.033855284 openvswitch-2.5.9/ofproto/ofproto-dpif.h0000644000175000017500000002173113534540071021607 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_DPIF_H #define OFPROTO_DPIF_H 1 #include #include "fail-open.h" #include "hmapx.h" #include "odp-util.h" #include "ofp-util.h" #include "ovs-thread.h" #include "ofproto-provider.h" #include "timer.h" #include "util.h" #include "ovs-thread.h" /* Priority for internal rules created to handle recirculation */ #define RECIRC_RULE_PRIORITY 20 union user_action_cookie; struct dpif_flow_stats; struct ofproto; struct ofproto_dpif; struct ofproto_packet_in; struct ofport_dpif; struct dpif_backer; struct OVS_LOCKABLE rule_dpif; struct OVS_LOCKABLE group_dpif; /* Number of implemented OpenFlow tables. */ enum { N_TABLES = 255 }; enum { TBL_INTERNAL = N_TABLES - 1 }; /* Used for internal hidden rules. */ BUILD_ASSERT_DECL(N_TABLES >= 2 && N_TABLES <= 255); /* Ofproto-dpif -- DPIF based ofproto implementation. * * Ofproto-dpif provides an ofproto implementation for those platforms which * implement the netdev and dpif interface defined in netdev.h and dpif.h. The * most important of which is the Linux Kernel Module (dpif-linux), but * alternatives are supported such as a userspace only implementation * (dpif-netdev), and a dummy implementation used for unit testing. * * Ofproto-dpif is divided into three major chunks. * * - ofproto-dpif.c * The main ofproto-dpif module is responsible for implementing the * provider interface, installing and removing datapath flows, maintaining * packet statistics, running protocols (BFD, LACP, STP, etc), and * configuring relevant submodules. * * - ofproto-dpif-upcall.c * Ofproto-dpif-upcall is responsible for retrieving upcalls from the kernel, * processing miss upcalls, and handing more complex ones up to the main * ofproto-dpif module. Miss upcall processing boils down to figuring out * what each packet's actions are, executing them (i.e. asking the kernel to * forward it), and handing it up to ofproto-dpif to decided whether or not * to install a kernel flow. * * - ofproto-dpif-xlate.c * Ofproto-dpif-xlate is responsible for translating OpenFlow actions into * datapath actions. */ /* Stores the various features which the corresponding backer supports. */ struct dpif_backer_support { /* True if the datapath supports variable-length * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. * False if the datapath supports only 8-byte (or shorter) userdata. */ bool variable_length_userdata; /* True if the datapath supports masked data in OVS_ACTION_ATTR_SET * actions. */ bool masked_set_action; /* True if the datapath supports tnl_push and pop actions. */ bool tnl_push_pop; /* True if the datapath supports OVS_FLOW_ATTR_UFID. */ bool ufid; /* Each member represents support for related OVS_KEY_ATTR_* fields. */ struct odp_support odp; }; bool ofproto_dpif_get_enable_ufid(const struct dpif_backer *backer); struct dpif_backer_support *ofproto_dpif_get_support(const struct ofproto_dpif *); cls_version_t ofproto_dpif_get_tables_version(struct ofproto_dpif *); struct rule_dpif *rule_dpif_lookup_from_table(struct ofproto_dpif *, cls_version_t, struct flow *, struct flow_wildcards *, const struct dpif_flow_stats *, uint8_t *table_id, ofp_port_t in_port, bool may_packet_in, bool honor_table_miss); static inline void rule_dpif_ref(struct rule_dpif *); static inline void rule_dpif_unref(struct rule_dpif *); void rule_dpif_credit_stats(struct rule_dpif *rule , const struct dpif_flow_stats *); static inline bool rule_dpif_is_fail_open(const struct rule_dpif *); static inline bool rule_dpif_is_table_miss(const struct rule_dpif *); static inline bool rule_dpif_is_internal(const struct rule_dpif *); uint8_t rule_dpif_get_table(const struct rule_dpif *); bool table_is_internal(uint8_t table_id); const struct rule_actions *rule_dpif_get_actions(const struct rule_dpif *); void rule_set_recirc_id(struct rule *, uint32_t id); ovs_be64 rule_dpif_get_flow_cookie(const struct rule_dpif *rule); void rule_dpif_reduce_timeouts(struct rule_dpif *rule, uint16_t idle_timeout, uint16_t hard_timeout); void group_dpif_credit_stats(struct group_dpif *, struct ofputil_bucket *, const struct dpif_flow_stats *); bool group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id, struct group_dpif **group); void group_dpif_get_buckets(const struct group_dpif *group, const struct ovs_list **buckets); enum ofp11_group_type group_dpif_get_type(const struct group_dpif *group); const char *group_dpif_get_selection_method(const struct group_dpif *group); uint64_t group_dpif_get_selection_method_param(const struct group_dpif *group); const struct field_array *group_dpif_get_fields(const struct group_dpif *group); bool ofproto_has_vlan_splinters(const struct ofproto_dpif *); ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *, ofp_port_t realdev_ofp_port, ovs_be16 vlan_tci); bool vsp_adjust_flow(const struct ofproto_dpif *, struct flow *, struct dp_packet *packet); int ofproto_dpif_execute_actions(struct ofproto_dpif *, const struct flow *, struct rule_dpif *, const struct ofpact *, size_t ofpacts_len, struct dp_packet *); int ofproto_dpif_execute_actions__(struct ofproto_dpif *, const struct flow *, struct rule_dpif *, const struct ofpact *, size_t ofpacts_len, int recurse, int resubmits, struct dp_packet *); void ofproto_dpif_send_packet_in(struct ofproto_dpif *, struct ofproto_packet_in *); bool ofproto_dpif_wants_packet_in_on_miss(struct ofproto_dpif *); int ofproto_dpif_send_packet(const struct ofport_dpif *, struct dp_packet *); void ofproto_dpif_flow_mod(struct ofproto_dpif *, const struct ofputil_flow_mod *); struct rule_dpif *ofproto_dpif_refresh_rule(struct rule_dpif *); struct ofport_dpif *odp_port_to_ofport(const struct dpif_backer *, odp_port_t); struct ofport_dpif *ofp_port_to_ofport(const struct ofproto_dpif *, ofp_port_t); bool ofproto_dpif_backer_enabled(struct dpif_backer* backer); int ofproto_dpif_add_internal_flow(struct ofproto_dpif *, const struct match *, int priority, uint16_t idle_timeout, const struct ofpbuf *ofpacts, struct rule **rulep); int ofproto_dpif_delete_internal_flow(struct ofproto_dpif *, struct match *, int priority); /* struct rule_dpif has struct rule as it's first member. */ #define RULE_CAST(RULE) ((struct rule *)RULE) #define GROUP_CAST(GROUP) ((struct ofgroup *)GROUP) static inline struct group_dpif* group_dpif_ref(struct group_dpif *group) { if (group) { ofproto_group_ref(GROUP_CAST(group)); } return group; } static inline void group_dpif_unref(struct group_dpif *group) { if (group) { ofproto_group_unref(GROUP_CAST(group)); } } static inline void rule_dpif_ref(struct rule_dpif *rule) { if (rule) { ofproto_rule_ref(RULE_CAST(rule)); } } static inline void rule_dpif_unref(struct rule_dpif *rule) { if (rule) { ofproto_rule_unref(RULE_CAST(rule)); } } static inline bool rule_dpif_is_fail_open(const struct rule_dpif *rule) { return is_fail_open_rule(RULE_CAST(rule)); } static inline bool rule_dpif_is_table_miss(const struct rule_dpif *rule) { return rule_is_table_miss(RULE_CAST(rule)); } /* Returns true if 'rule' is an internal rule, false otherwise. */ static inline bool rule_dpif_is_internal(const struct rule_dpif *rule) { return RULE_CAST(rule)->table_id == TBL_INTERNAL; } #undef RULE_CAST bool ovs_native_tunneling_is_on(struct ofproto_dpif *ofproto); #endif /* ofproto-dpif.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/libofproto.sym.in0000644000000000000000000000013113534540071020651 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801424.645852423 openvswitch-2.5.9/ofproto/libofproto.sym.in0000644000175000017500000000006013534540071022334 0ustar00jpettitjpettit00000000000000libofproto_@LT_CURRENT@ { global: *; }; openvswitch-2.5.9/ofproto/PaxHeaders.82075/netflow.h0000644000000000000000000000013113534540071017162 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801425.029855254 openvswitch-2.5.9/ofproto/netflow.h0000644000175000017500000000362513534540071020657 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef OFPROTO_NETFLOW_H #define OFPROTO_NETFLOW_H 1 #include #include "flow.h" #include "sset.h" /* Default active timeout interval, in seconds. * * (The active timeout interval is the interval at which NetFlow records are * sent for flows that do not expire, so that such flows are still * accounted.) */ #define NF_ACTIVE_TIMEOUT_DEFAULT 600 struct netflow_options { struct sset collectors; uint8_t engine_type; uint8_t engine_id; int active_timeout; bool add_id_to_iface; }; #define NF_OUT_FLOOD OFP_PORT_C(UINT16_MAX) #define NF_OUT_MULTI OFP_PORT_C(UINT16_MAX - 1) #define NF_OUT_DROP OFP_PORT_C(UINT16_MAX - 2) struct netflow *netflow_create(void); struct netflow *netflow_ref(const struct netflow *); void netflow_unref(struct netflow *); bool netflow_exists(void); int netflow_set_options(struct netflow *, const struct netflow_options *); void netflow_run(struct netflow *); void netflow_wait(struct netflow *); void netflow_mask_wc(const struct flow *, struct flow_wildcards *); void netflow_flow_clear(struct netflow *, const struct flow *); void netflow_flow_update(struct netflow *nf, const struct flow *flow, ofp_port_t output_iface, const struct dpif_flow_stats *); #endif /* netflow.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/connmgr.h0000644000000000000000000000013113534540071017147 xustar0030 mtime=1567801401.621682785 29 atime=1567801402.10568634 30 ctime=1567801425.021855195 openvswitch-2.5.9/ofproto/connmgr.h0000644000175000017500000002231513534540071020641 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CONNMGR_H #define CONNMGR_H 1 #include "classifier.h" #include "hmap.h" #include "list.h" #include "match.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofproto.h" #include "ofproto-provider.h" #include "openflow/nicira-ext.h" #include "openvswitch/types.h" struct nlattr; struct ofconn; struct rule; struct simap; struct sset; /* ofproto supports two kinds of OpenFlow connections: * * - "Primary" connections to ordinary OpenFlow controllers. ofproto * maintains persistent connections to these controllers and by default * sends them asynchronous messages such as packet-ins. * * - "Service" connections, e.g. from ovs-ofctl. When these connections * drop, it is the other side's responsibility to reconnect them if * necessary. ofproto does not send them asynchronous messages by default. * * Currently, active (tcp, ssl, unix) connections are always "primary" * connections and passive (ptcp, pssl, punix) connections are always "service" * connections. There is no inherent reason for this, but it reflects the * common case. */ enum ofconn_type { OFCONN_PRIMARY, /* An ordinary OpenFlow controller. */ OFCONN_SERVICE /* A service connection, e.g. "ovs-ofctl". */ }; enum ofproto_packet_in_miss_type { /* Not generated by a flow miss or table-miss flow. */ OFPROTO_PACKET_IN_NO_MISS, /* The packet_in was generated directly by a table-miss flow, that is, a * flow with priority 0 that wildcards all fields. See OF1.3.3 section * 5.4. * * (Our interpretation of "directly" is "not via groups". Packet_ins * generated by table-miss flows via groups use * OFPROTO_PACKET_IN_NO_MISS.) */ OFPROTO_PACKET_IN_MISS_FLOW, /* The packet-in was generated directly by a table-miss, but not a * table-miss flow. That is, it was generated by the OpenFlow 1.0, 1.1, or * 1.2 table-miss behavior. */ OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW, }; /* A packet_in, with extra members to assist in queuing and routing it. */ struct ofproto_packet_in { struct ofputil_packet_in up; struct ovs_list list_node; /* For queuing. */ uint16_t controller_id; /* Controller ID to send to. */ int send_len; /* Length that the action requested sending. */ enum ofproto_packet_in_miss_type miss_type; }; /* Basics. */ struct connmgr *connmgr_create(struct ofproto *ofproto, const char *dpif_name, const char *local_name); void connmgr_destroy(struct connmgr *); void connmgr_run(struct connmgr *, void (*handle_openflow)(struct ofconn *, const struct ofpbuf *ofp_msg)); void connmgr_wait(struct connmgr *); void connmgr_get_memory_usage(const struct connmgr *, struct simap *usage); struct ofproto *ofconn_get_ofproto(const struct ofconn *); void connmgr_retry(struct connmgr *); /* OpenFlow configuration. */ bool connmgr_has_controllers(const struct connmgr *); void connmgr_get_controller_info(struct connmgr *, struct shash *); void connmgr_free_controller_info(struct shash *); void connmgr_set_controllers(struct connmgr *, const struct ofproto_controller[], size_t n, uint32_t allowed_versions); void connmgr_reconnect(const struct connmgr *); int connmgr_set_snoops(struct connmgr *, const struct sset *snoops); bool connmgr_has_snoops(const struct connmgr *); void connmgr_get_snoops(const struct connmgr *, struct sset *snoops); /* Individual connections to OpenFlow controllers. */ enum ofconn_type ofconn_get_type(const struct ofconn *); bool ofconn_get_master_election_id(const struct ofconn *, uint64_t *idp); bool ofconn_set_master_election_id(struct ofconn *, uint64_t); enum ofp12_controller_role ofconn_get_role(const struct ofconn *); void ofconn_set_role(struct ofconn *, enum ofp12_controller_role); enum ofputil_protocol ofconn_get_protocol(const struct ofconn *); void ofconn_set_protocol(struct ofconn *, enum ofputil_protocol); enum nx_packet_in_format ofconn_get_packet_in_format(struct ofconn *); void ofconn_set_packet_in_format(struct ofconn *, enum nx_packet_in_format); void ofconn_set_controller_id(struct ofconn *, uint16_t controller_id); void ofconn_set_invalid_ttl_to_controller(struct ofconn *, bool); bool ofconn_get_invalid_ttl_to_controller(struct ofconn *); int ofconn_get_miss_send_len(const struct ofconn *); void ofconn_set_miss_send_len(struct ofconn *, int miss_send_len); void ofconn_set_async_config(struct ofconn *, const uint32_t master_masks[OAM_N_TYPES], const uint32_t slave_masks[OAM_N_TYPES]); void ofconn_get_async_config(struct ofconn *, uint32_t *master_masks, uint32_t *slave_masks); void ofconn_send_reply(const struct ofconn *, struct ofpbuf *); void ofconn_send_replies(const struct ofconn *, struct ovs_list *); void ofconn_send_error(const struct ofconn *, const struct ofp_header *request, enum ofperr); enum ofperr ofconn_pktbuf_retrieve(struct ofconn *, uint32_t id, struct dp_packet **bufferp, ofp_port_t *in_port); struct ofp_bundle; struct ofp_bundle *ofconn_get_bundle(struct ofconn *, uint32_t id); enum ofperr ofconn_insert_bundle(struct ofconn *, struct ofp_bundle *); enum ofperr ofconn_remove_bundle(struct ofconn *, struct ofp_bundle *); /* Logging flow_mod summaries. */ void ofconn_report_flow_mod(struct ofconn *, enum ofp_flow_mod_command); /* Sending asynchronous messages. */ bool connmgr_wants_packet_in_on_miss(struct connmgr *mgr); void connmgr_send_port_status(struct connmgr *, struct ofconn *source, const struct ofputil_phy_port *, uint8_t reason); void connmgr_send_flow_removed(struct connmgr *, const struct ofputil_flow_removed *); void connmgr_send_packet_in(struct connmgr *, const struct ofproto_packet_in *); void ofconn_send_role_status(struct ofconn *ofconn, uint32_t role, uint8_t reason); void connmgr_send_requestforward(struct connmgr *, const struct ofconn *source, const struct ofputil_requestforward *); /* Fail-open settings. */ enum ofproto_fail_mode connmgr_get_fail_mode(const struct connmgr *); void connmgr_set_fail_mode(struct connmgr *, enum ofproto_fail_mode); /* Fail-open implementation. */ int connmgr_get_max_probe_interval(const struct connmgr *); bool connmgr_is_any_controller_connected(const struct connmgr *); bool connmgr_is_any_controller_admitted(const struct connmgr *); int connmgr_failure_duration(const struct connmgr *); /* In-band configuration. */ void connmgr_set_extra_in_band_remotes(struct connmgr *, const struct sockaddr_in *, size_t); void connmgr_set_in_band_queue(struct connmgr *, int queue_id); /* In-band implementation. */ bool connmgr_has_in_band(struct connmgr *); /* Fail-open and in-band implementation. */ void connmgr_flushed(struct connmgr *); int connmgr_count_hidden_rules(const struct connmgr *); /* A flow monitor managed by NXST_FLOW_MONITOR and related requests. */ struct ofmonitor { struct ofconn *ofconn; /* Owning 'ofconn'. */ struct hmap_node ofconn_node; /* In ofconn's 'monitors' hmap. */ uint32_t id; enum nx_flow_monitor_flags flags; /* Matching. */ ofp_port_t out_port; uint8_t table_id; struct minimatch match; }; struct ofputil_flow_monitor_request; enum ofperr ofmonitor_create(const struct ofputil_flow_monitor_request *, struct ofconn *, struct ofmonitor **) OVS_REQUIRES(ofproto_mutex); struct ofmonitor *ofmonitor_lookup(struct ofconn *, uint32_t id) OVS_REQUIRES(ofproto_mutex); void ofmonitor_destroy(struct ofmonitor *) OVS_REQUIRES(ofproto_mutex); void ofmonitor_report(struct connmgr *, struct rule *, enum nx_flow_update_event, enum ofp_flow_removed_reason, const struct ofconn *abbrev_ofconn, ovs_be32 abbrev_xid, const struct rule_actions *old_actions) OVS_REQUIRES(ofproto_mutex); void ofmonitor_flush(struct connmgr *) OVS_REQUIRES(ofproto_mutex); struct rule_collection; void ofmonitor_collect_resume_rules(struct ofmonitor *, uint64_t seqno, struct rule_collection *) OVS_REQUIRES(ofproto_mutex); void ofmonitor_compose_refresh_updates(struct rule_collection *rules, struct ovs_list *msgs) OVS_REQUIRES(ofproto_mutex); #endif /* connmgr.h */ openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-unixctl.man0000644000000000000000000000013113534540071022124 xustar0030 mtime=1567801401.637682902 29 atime=1567801402.10968637 30 ctime=1567801423.753845848 openvswitch-2.5.9/ofproto/ofproto-dpif-unixctl.man0000644000175000017500000000303613534540071023615 0ustar00jpettitjpettit00000000000000.SS "DATAPATH DEBUGGING COMMANDS" These commands query and modify datapaths. They are are similar to \fBovs\-dpctl\fR(8) commands. \fBdpif/show\fR has the additional functionality, beyond \fBdpctl/show\fR of printing OpenFlow port numbers. The other commands are redundant and will be removed in a future release. . .IP "\fBdpif/dump\-dps\fR" Prints the name of each configured datapath on a separate line. . .IP "\fBdpif/show\fR" Prints a summary of configured datapaths, including statistics and a list of connected ports. The port information includes the OpenFlow port number, datapath port number, and the type. (The local port is identified as OpenFlow port 65534.) . .IP "\fBdpif/dump\-flows\fR [\fB\-m\fR] \fIdp\fR" Prints to the console all flow entries in datapath \fIdp\fR's flow table. Without \fB\-m\fR, output omits match fields that a flow wildcards entirely; with \fB\-m\fR output includes all wildcarded fields. .IP This command is primarily useful for debugging Open vSwitch. The flow table entries that it displays are not OpenFlow flow entries. Instead, they are different and considerably simpler flows maintained by the datapath module. If you wish to see the OpenFlow flow entries, use \fBovs\-ofctl dump\-flows\fR. . .IP "\fBdpif/del\-flows \fIdp\fR" Deletes all flow entries from datapath \fIdp\fR's flow table and underlying datapath implementation (e.g., kernel datapath module). .IP This command is primarily useful for debugging Open vSwitch. As discussed in \fBdpif/dump\-flows\fR, these entries are not OpenFlow flow entries. openvswitch-2.5.9/ofproto/PaxHeaders.82075/ipfix.xml0000644000000000000000000000013113534540071017174 xustar0030 mtime=1567801401.625682815 29 atime=1567801402.10568634 30 ctime=1567801423.801846201 openvswitch-2.5.9/ofproto/ipfix.xml0000644000175000017500000115075013534540071020674 0ustar00jpettitjpettit00000000000000 IP Flow Information Export (IPFIX) Entities 2007-05-10 2012-12-05 For the IPFIX schema, please see . IPFIX Information Elements Expert Review Primary expert - Nevil Brownlee and Secondary expert - Juergen Quittek Values 0-127: NFv9-compatible Reserved 0 current octetDeltaCount unsigned64 flowCounter deltaCounter 1 data current The number of octets since the previous report (if any) in incoming packets for this Flow at the Observation Point. The number of octets includes IP header(s) and IP payload. octets packetDeltaCount unsigned64 flowCounter deltaCounter 2 data current The number of incoming packets since the previous report (if any) for this Flow at the Observation Point. packets deltaFlowCount unsigned64 deltaCounter 3 current The conservative count of Original Flows contributing to this Aggregated Flow; may be distributed via any of the methods expressed by the valueDistributionMethod Information Element. protocolIdentifier unsigned8 ipHeader identifier 4 all current The value of the protocol number in the IP packet header. The protocol number identifies the IP packet payload type. Protocol numbers are defined in the IANA Protocol Numbers registry. In Internet Protocol version 4 (IPv4), this is carried in the Protocol field. In Internet Protocol version 6 (IPv6), this is carried in the Next Header field in the last extension header of the packet. See for the specification of the IPv4 protocol field. See for the specification of the IPv6 protocol field. See the list of protocol numbers assigned by IANA at . ipClassOfService unsigned8 ipHeader identifier 5 all current For IPv4 packets, this is the value of the TOS field in the IPv4 packet header. For IPv6 packets, this is the value of the Traffic Class field in the IPv6 packet header. See (Section 5.3.2) and for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. tcpControlBits unsigned8 minMax flags 6 all current TCP control bits observed for packets of this Flow. The information is encoded in a set of bit fields. For each TCP control bit, there is a bit in this set. A bit is set to 1 if any observed packet of this Flow has the corresponding TCP control bit set to 1. A value of 0 for a bit indicates that the corresponding bit was not set in any of the observed packets of this Flow. 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | Reserved | URG | ACK | PSH | RST | SYN | FIN | +-----+-----+-----+-----+-----+-----+-----+-----+ Reserved: Reserved for future use by TCP. Must be zero. URG: Urgent Pointer field significant ACK: Acknowledgment field significant PSH: Push Function RST: Reset the connection SYN: Synchronize sequence numbers FIN: No more data from sender See for the definition of the TCP control bits in the TCP header. sourceTransportPort unsigned16 transportHeader identifier 7 all current The source port identifier in the transport header. For the transport protocols UDP, TCP, and SCTP, this is the source port number given in the respective header. This field MAY also be used for future transport protocols that have 16-bit source port identifiers. See for the definition of the UDP source port field. See for the definition of the TCP source port field. See for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at . sourceIPv4Address ipv4Address ipHeader identifier 8 all current The IPv4 source address in the IP packet header. See for the definition of the IPv4 source address field. sourceIPv4PrefixLength unsigned8 ipHeader 9 option current The number of contiguous bits that are relevant in the sourceIPv4Prefix Information Element. bits 0-32 ingressInterface unsigned32 scope identifier 10 all current The index of the IP interface where packets of this Flow are being received. The value matches the value of managed object 'ifIndex' as defined in RFC 2863. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in RFC 2863. See for the definition of the ifIndex object. destinationTransportPort unsigned16 transportHeader identifier 11 all current The destination port identifier in the transport header. For the transport protocols UDP, TCP, and SCTP, this is the destination port number given in the respective header. This field MAY also be used for future transport protocols that have 16-bit destination port identifiers. See for the definition of the UDP destination port field. See for the definition of the TCP destination port field. See for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at . destinationIPv4Address ipv4Address ipHeader identifier 12 all current The IPv4 destination address in the IP packet header. See for the definition of the IPv4 destination address field. destinationIPv4PrefixLength unsigned8 ipHeader 13 option current The number of contiguous bits that are relevant in the destinationIPv4Prefix Information Element. bits 0-32 egressInterface unsigned32 scope identifier 14 all current The index of the IP interface where packets of this Flow are being sent. The value matches the value of managed object 'ifIndex' as defined in RFC 2863. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in RFC 2863. See for the definition of the ifIndex object. ipNextHopIPv4Address ipv4Address derived identifier 15 data current The IPv4 address of the next IPv4 hop. bgpSourceAsNumber unsigned32 derived identifier 16 all current The autonomous system (AS) number of the source IP address. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0. See for a description of BGP-4, and see for the definition of the AS number. bgpDestinationAsNumber unsigned32 derived identifier 17 all current The autonomous system (AS) number of the destination IP address. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0. See for a description of BGP-4, and see for the definition of the AS number. bgpNextHopIPv4Address ipv4Address derived identifier 18 all current The IPv4 address of the next (adjacent) BGP hop. See for a description of BGP-4. postMCastPacketDeltaCount unsigned64 flowCounter deltaCounter 19 data current The number of outgoing multicast packets since the previous report (if any) sent for packets of this Flow by a multicast daemon within the Observation Domain. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. packets postMCastOctetDeltaCount unsigned64 flowCounter deltaCounter 20 data current The number of octets since the previous report (if any) in outgoing multicast packets sent for packets of this Flow by a multicast daemon within the Observation Domain. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. The number of octets includes IP header(s) and IP payload. octets flowEndSysUpTime unsigned32 timestamp 21 data current The relative timestamp of the last packet of this Flow. It indicates the number of milliseconds since the last (re-)initialization of the IPFIX Device (sysUpTime). milliseconds flowStartSysUpTime unsigned32 timestamp 22 data current The relative timestamp of the first packet of this Flow. It indicates the number of milliseconds since the last (re-)initialization of the IPFIX Device (sysUpTime). milliseconds postOctetDeltaCount unsigned64 flowCounter deltaCounter 23 data current The definition of this Information Element is identical to the definition of Information Element 'octetDeltaCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. octets postPacketDeltaCount unsigned64 flowCounter deltaCounter 24 data current The definition of this Information Element is identical to the definition of Information Element 'packetDeltaCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. packets minimumIpTotalLength unsigned64 minMax 25 all current Length of the smallest packet observed for this Flow. The packet length includes the IP header(s) length and the IP payload length. octets See for the specification of the IPv4 total length. See for the specification of the IPv6 payload length. See for the specification of the IPv6 jumbo payload length. maximumIpTotalLength unsigned64 minMax 26 all current Length of the largest packet observed for this Flow. The packet length includes the IP header(s) length and the IP payload length. octets See for the specification of the IPv4 total length. See for the specification of the IPv6 payload length. See for the specification of the IPv6 jumbo payload length. sourceIPv6Address ipv6Address ipHeader identifier 27 all current The IPv6 source address in the IP packet header. See for the definition of the Source Address field in the IPv6 header. destinationIPv6Address ipv6Address ipHeader identifier 28 all current The IPv6 destination address in the IP packet header. See for the definition of the Destination Address field in the IPv6 header. sourceIPv6PrefixLength unsigned8 ipHeader 29 option current The number of contiguous bits that are relevant in the sourceIPv6Prefix Information Element. bits 0-128 destinationIPv6PrefixLength unsigned8 ipHeader 30 option current The number of contiguous bits that are relevant in the destinationIPv6Prefix Information Element. bits 0-128 flowLabelIPv6 unsigned32 ipHeader identifier 31 all current The value of the IPv6 Flow Label field in the IP packet header. See for the definition of the Flow Label field in the IPv6 packet header. icmpTypeCodeIPv4 unsigned16 transportHeader identifier 32 all current Type and Code of the IPv4 ICMP message. The combination of both values is reported as (ICMP type * 256) + ICMP code. See for the definition of the IPv4 ICMP type and code fields. igmpType unsigned8 transportHeader identifier 33 all current The type field of the IGMP message. See for the definition of the IGMP type field. 34-35 flowActiveTimeout unsigned16 misc 36 all current The number of seconds after which an active Flow is timed out anyway, even if there is still a continuous flow of packets. seconds flowIdleTimeout unsigned16 misc 37 all current A Flow is considered to be timed out if no packets belonging to the Flow have been observed for the number of seconds specified by this field. seconds 38-39 exportedOctetTotalCount unsigned64 processCounter totalCounter 40 data current The total number of octets that the Exporting Process has sent since the Exporting Process (re-)initialization to a particular Collecting Process. The value of this Information Element is calculated by summing up the IPFIX Message Header length values of all IPFIX Messages that were successfully sent to the Collecting Process. The reported number excludes octets in the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of octets sent to this Collecting Process. octets exportedMessageTotalCount unsigned64 processCounter totalCounter 41 data current The total number of IPFIX Messages that the Exporting Process has sent since the Exporting Process (re-)initialization to a particular Collecting Process. The reported number excludes the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of IPFIX Messages sent to this Collecting Process. messages exportedFlowRecordTotalCount unsigned64 processCounter totalCounter 42 data current The total number of Flow Records that the Exporting Process has sent as Data Records since the Exporting Process (re-)initialization to a particular Collecting Process. The reported number excludes Flow Records in the IPFIX Message that carries the counter value. If this Information Element is sent to a particular Collecting Process, then by default it specifies the number of Flow Records sent to this process. flows 43 sourceIPv4Prefix ipv4Address ipHeader 44 data current IPv4 source address prefix. destinationIPv4Prefix ipv4Address ipHeader 45 data current IPv4 destination address prefix. mplsTopLabelType unsigned8 derived identifier 46 data current This field identifies the control protocol that allocated the top-of-stack label. Values for this field are listed in the MPLS label type registry. See See for the MPLS label structure. See for the association of MPLS labels with Virtual Private Networks (VPNs). See for BGP and BGP routing. See for Label Distribution Protocol (LDP). See the list of MPLS label types assigned by IANA at . mplsTopLabelIPv4Address ipv4Address derived identifier 47 data current The IPv4 address of the system that the MPLS top label will cause this Flow to be forwarded to. See for the association between MPLS labels and IP addresses. 48-51 minimumTTL unsigned8 minMax 52 data current Minimum TTL value observed for any packet in this Flow. hops See for the definition of the IPv4 Time to Live field. See for the definition of the IPv6 Hop Limit field. maximumTTL unsigned8 minMax 53 data current Maximum TTL value observed for any packet in this Flow. hops See for the definition of the IPv4 Time to Live field. See for the definition of the IPv6 Hop Limit field. fragmentIdentification unsigned32 ipHeader identifier 54 data current The value of the Identification field in the IPv4 packet header or in the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header. See for the definition of the IPv4 Identification field. See for the definition of the Identification field in the IPv6 Fragment header. postIpClassOfService unsigned8 ipHeader identifier 55 all current The definition of this Information Element is identical to the definition of Information Element 'ipClassOfService', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. See for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. See for the definition of middleboxes. sourceMacAddress macAddress subIpHeader identifier 56 data current The IEEE 802 source MAC address field. See IEEE.802-3.2002. postDestinationMacAddress macAddress subIpHeader identifier 57 data current The definition of this Information Element is identical to the definition of Information Element 'destinationMacAddress', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. See IEEE.802-3.2002. vlanId unsigned16 subIpHeader identifier 58 data current Virtual LAN identifier associated with ingress interface. For dot1q vlans, see 243 dot1qVlanId. See IEEE.802-1Q.2003. postVlanId unsigned16 subIpHeader identifier 59 data current Virtual LAN identifier associated with egress interface. For postdot1q vlans, see 254 postDot1qVlanId. See IEEE.802-1Q.2003. ipVersion unsigned8 ipHeader identifier 60 all current The IP version field in the IP packet header. See for the definition of the version field in the IPv4 packet header. See for the definition of the version field in the IPv6 packet header. Additional information on defined version numbers can be found at . flowDirection unsigned8 misc identifier 61 data current The direction of the Flow observed at the Observation Point. There are only two values defined. 0x00: ingress flow 0x01: egress flow ipNextHopIPv6Address ipv6Address derived identifier 62 data current The IPv6 address of the next IPv6 hop. bgpNextHopIPv6Address ipv6Address derived identifier 63 all current The IPv6 address of the next (adjacent) BGP hop. See for a description of BGP-4. ipv6ExtensionHeaders unsigned32 minMax flags 64 all current IPv6 extension headers observed in packets of this Flow. The information is encoded in a set of bit fields. For each IPv6 option header, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding IPv6 extension header. Otherwise, if no observed packet of this Flow contained the respective IPv6 extension header, the value of the corresponding bit is 0. 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | DST | HOP | Res | UNK |FRA0 | RH |FRA1 | Res | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 8 9 10 11 12 13 14 15 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | MOB | ESP | AH | PAY | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 16 17 18 19 20 21 22 23 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 24 25 26 27 28 29 30 31 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | Reserved | +-----+-----+-----+-----+-----+-----+-----+-----+ Bit IPv6 Option Description 0, DST 60 Destination option header 1, HOP 0 Hop-by-hop option header 2, Res Reserved 3, UNK Unknown Layer 4 header (compressed, encrypted, not supported) 4, FRA0 44 Fragment header - first fragment 5, RH 43 Routing header 6, FRA1 44 Fragmentation header - not first fragment 7, Res Reserved 8 to 11 Reserved 12, MOB 135 IPv6 mobility [RFC3775] 13, ESP 50 Encrypted security payload 14, AH 51 Authentication Header 15, PAY 108 Payload compression header 16 to 31 Reserved See for the general definition of IPv6 extension headers and for the specification of the hop-by-hop options header, the routing header, the fragment header, and the destination options header. See for the specification of the authentication header. See for the specification of the encapsulating security payload. The diagram provided in is incorrect. The diagram in this registry is taken from Errata 1738. See 65-69 mplsTopLabelStackSection octetArray subIpHeader identifier 70 all current The Label, Exp, and S fields from the top MPLS label stack entry, i.e., from the last label that was pushed. The size of this Information Element is 3 octets. 0 1 2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Label | Exp |S| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Label: Label Value, 20 bits Exp: Experimental Use, 3 bits S: Bottom of Stack, 1 bit See . mplsLabelStackSection2 octetArray subIpHeader identifier 71 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsTopLabelStackSection. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection3 octetArray subIpHeader identifier 72 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection2. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection4 octetArray subIpHeader identifier 73 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection3. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection5 octetArray subIpHeader identifier 74 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection4. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection6 octetArray subIpHeader identifier 75 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection5. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection7 octetArray subIpHeader identifier 76 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection6. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection8 octetArray subIpHeader identifier 77 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection7. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection9 octetArray subIpHeader identifier 78 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection8. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . mplsLabelStackSection10 octetArray subIpHeader identifier 79 all current The Label, Exp, and S fields from the label stack entry that was pushed immediately before the label stack entry that would be reported by mplsLabelStackSection9. See the definition of mplsTopLabelStackSection for further details. The size of this Information Element is 3 octets. See . destinationMacAddress macAddress subIpHeader identifier 80 data current The IEEE 802 destination MAC address field. See IEEE.802-3.2002. postSourceMacAddress macAddress subIpHeader identifier 81 data current The definition of this Information Element is identical to the definition of Information Element 'sourceMacAddress', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. See IEEE.802-3.2002. interfaceName string 82 current A short name uniquely describing an interface, eg "Eth1/0". See for the definition of the ifName object. interfaceDescription string 83 current The description of an interface, eg "FastEthernet 1/0" or "ISP connection". See for the definition of the ifDescr object. 84 octetTotalCount unsigned64 flowCounter totalCounter 85 all current The total number of octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload. octets packetTotalCount unsigned64 flowCounter totalCounter 86 all current The total number of incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets 87 fragmentOffset unsigned16 ipHeader identifier 88 all current The value of the IP fragment offset field in the IPv4 packet header or the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header. See for the specification of the fragment offset in the IPv4 header. See for the specification of the fragment offset in the IPv6 Fragment header. 89 mplsVpnRouteDistinguisher octetArray derived identifier 90 all current The value of the VPN route distinguisher of a corresponding entry in a VPN routing and forwarding table. Route distinguisher ensures that the same address can be used in several different MPLS VPNs and that it is possible for BGP to carry several completely different routes to that address, one for each VPN. According to RFC 4364, the size of mplsVpnRouteDistinguisher is 8 octets. However, in RFC 4382 an octet string with flexible length was chosen for representing a VPN route distinguisher by object MplsL3VpnRouteDistinguisher. This choice was made in order to be open to future changes of the size. This idea was adopted when choosing octetArray as abstract data type for this Information Element. The maximum length of this Information Element is 256 octets. See for the specification of the route distinguisher. See for the specification of the MPLS/BGP Layer 3 Virtual Private Network (VPN) Management Information Base. mplsTopLabelPrefixLength unsigned8 identifier 91 current The prefix length of the subnet of the mplsTopLabelIPv4Address that the MPLS top label will cause the Flow to be forwarded to. bits 0-32 See for the association between MPLS labels and prefix lengths. 92-93 applicationDescription string 94 current Specifies the description of an application. applicationId octetArray identifier 95 current Specifies an Application ID per . See section 4 of for the applicationId Information Element Specification. applicationName string 96 current Specifies the name of an application. 97 postIpDiffServCodePoint unsigned8 identifier 98 current The definition of this Information Element is identical to the definition of Information Element 'ipDiffServCodePoint', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. 0-63 See for the definition of the Differentiated Services Field. See section 5.3.2 of and for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. See the IPFIX Information Model for the 'ipDiffServCodePoint' specification. multicastReplicationFactor unsigned32 quantity 99 current The amount of multicast replication that's applied to a traffic stream. See for the specification of reserved IPv4 multicast addresses. See for the specification of reserved IPv6 multicast addresses. 100 classificationEngineId unsigned8 identifier 101 current A unique identifier for the engine that determined the Selector ID. Thus, the Classification Engine ID defines the context for the Selector ID. The Classification Engine can be considered a specific registry for application assignments. Values for this field are listed in the Classification Engine IDs registry. See 102-127 bgpNextAdjacentAsNumber unsigned32 derived identifier 128 all current The autonomous system (AS) number of the first AS in the AS path to the destination IP address. The path is deduced by looking up the destination IP address of the Flow in the BGP routing information base. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0. See for a description of BGP-4, and see for the definition of the AS number. bgpPrevAdjacentAsNumber unsigned32 derived identifier 129 all current The autonomous system (AS) number of the last AS in the AS path from the source IP address. The path is deduced by looking up the source IP address of the Flow in the BGP routing information base. If AS path information for this Flow is only available as an unordered AS set (and not as an ordered AS sequence), then the value of this Information Element is 0. In case of BGP asymmetry, the bgpPrevAdjacentAsNumber might not be able to report the correct value. See for a description of BGP-4, and see for the definition of the AS number. exporterIPv4Address ipv4Address config identifier 130 all current The IPv4 address used by the Exporting Process. This is used by the Collector to identify the Exporter in cases where the identity of the Exporter may have been obscured by the use of a proxy. exporterIPv6Address ipv6Address config identifier 131 all current The IPv6 address used by the Exporting Process. This is used by the Collector to identify the Exporter in cases where the identity of the Exporter may have been obscured by the use of a proxy. droppedOctetDeltaCount unsigned64 flowCounter deltaCounter 132 data current The number of octets since the previous report (if any) in packets of this Flow dropped by packet treatment. The number of octets includes IP header(s) and IP payload. octets droppedPacketDeltaCount unsigned64 flowCounter deltaCounter 133 data current The number of packets since the previous report (if any) of this Flow dropped by packet treatment. packets droppedOctetTotalCount unsigned64 flowCounter totalCounter 134 data current The total number of octets in packets of this Flow dropped by packet treatment since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload. octets droppedPacketTotalCount unsigned64 flowCounter totalCounter 135 data current The number of packets of this Flow dropped by packet treatment since the Metering Process (re-)initialization for this Observation Point. packets flowEndReason unsigned8 misc identifier 136 data current The reason for Flow termination. The range of values includes the following: 0x01: idle timeout The Flow was terminated because it was considered to be idle. 0x02: active timeout The Flow was terminated for reporting purposes while it was still active, for example, after the maximum lifetime of unreported Flows was reached. 0x03: end of Flow detected The Flow was terminated because the Metering Process detected signals indicating the end of the Flow, for example, the TCP FIN flag. 0x04: forced end The Flow was terminated because of some external event, for example, a shutdown of the Metering Process initiated by a network management application. 0x05: lack of resources The Flow was terminated because of lack of resources available to the Metering Process and/or the Exporting Process. commonPropertiesId unsigned64 scope identifier 137 option current An identifier of a set of common properties that is unique per Observation Domain and Transport Session. Typically, this Information Element is used to link to information reported in separate Data Records. observationPointId unsigned32 scope identifier 138 option current An identifier of an Observation Point that is unique per Observation Domain. It is RECOMMENDED that this identifier is also unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. icmpTypeCodeIPv6 unsigned16 transportHeader identifier 139 all current Type and Code of the IPv6 ICMP message. The combination of both values is reported as (ICMP type * 256) + ICMP code. See for the definition of the IPv6 ICMP type and code fields. mplsTopLabelIPv6Address ipv6Address derived identifier 140 data current The IPv6 address of the system that the MPLS top label will cause this Flow to be forwarded to. See for the association between MPLS labels and IP addresses. lineCardId unsigned32 scope identifier 141 option current An identifier of a line card that is unique per IPFIX Device hosting an Observation Point. Typically, this Information Element is used for limiting the scope of other Information Elements. portId unsigned32 scope identifier 142 option current An identifier of a line port that is unique per IPFIX Device hosting an Observation Point. Typically, this Information Element is used for limiting the scope of other Information Elements. meteringProcessId unsigned32 scope identifier 143 option current An identifier of a Metering Process that is unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that process identifiers are typically assigned dynamically. The Metering Process may be re-started with a different ID. exportingProcessId unsigned32 scope identifier 144 option current An identifier of an Exporting Process that is unique per IPFIX Device. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that process identifiers are typically assigned dynamically. The Exporting Process may be re-started with a different ID. templateId unsigned16 scope identifier 145 option current An identifier of a Template that is locally unique within a combination of a Transport session and an Observation Domain. Template IDs 0-255 are reserved for Template Sets, Options Template Sets, and other reserved Sets yet to be created. Template IDs of Data Sets are numbered from 256 to 65535. Typically, this Information Element is used for limiting the scope of other Information Elements. Note that after a re-start of the Exporting Process Template identifiers may be re-assigned. wlanChannelId unsigned8 subIpHeader identifier 146 data current The identifier of the 802.11 (Wi-Fi) channel used. See IEEE.802-11.1999. wlanSSID string subIpHeader 147 data current The Service Set IDentifier (SSID) identifying an 802.11 (Wi-Fi) network used. According to IEEE.802-11.1999, the SSID is encoded into a string of up to 32 characters. See IEEE.802-11.1999. flowId unsigned64 scope identifier 148 option current An identifier of a Flow that is unique within an Observation Domain. This Information Element can be used to distinguish between different Flows if Flow Keys such as IP addresses and port numbers are not reported or are reported in separate records. observationDomainId unsigned32 scope identifier 149 option current An identifier of an Observation Domain that is locally unique to an Exporting Process. The Exporting Process uses the Observation Domain ID to uniquely identify to the Collecting Process the Observation Domain where Flows were metered. It is RECOMMENDED that this identifier is also unique per IPFIX Device. A value of 0 indicates that no specific Observation Domain is identified by this Information Element. Typically, this Information Element is used for limiting the scope of other Information Elements. flowStartSeconds dateTimeSeconds timestamp 150 data current The absolute timestamp of the first packet of this Flow. seconds flowEndSeconds dateTimeSeconds timestamp 151 data current The absolute timestamp of the last packet of this Flow. seconds flowStartMilliseconds dateTimeMilliseconds timestamp 152 data current The absolute timestamp of the first packet of this Flow. milliseconds flowEndMilliseconds dateTimeMilliseconds timestamp 153 data current The absolute timestamp of the last packet of this Flow. milliseconds flowStartMicroseconds dateTimeMicroseconds timestamp 154 data current The absolute timestamp of the first packet of this Flow. microseconds flowEndMicroseconds dateTimeMicroseconds timestamp 155 data current The absolute timestamp of the last packet of this Flow. microseconds flowStartNanoseconds dateTimeNanoseconds timestamp 156 data current The absolute timestamp of the first packet of this Flow. nanoseconds flowEndNanoseconds dateTimeNanoseconds timestamp 157 data current The absolute timestamp of the last packet of this Flow. nanoseconds flowStartDeltaMicroseconds unsigned32 timestamp 158 data current This is a relative timestamp only valid within the scope of a single IPFIX Message. It contains the negative time offset of the first observed packet of this Flow relative to the export time specified in the IPFIX Message Header. microseconds See the IPFIX protocol specification for the definition of the IPFIX Message Header. flowEndDeltaMicroseconds unsigned32 timestamp 159 data current This is a relative timestamp only valid within the scope of a single IPFIX Message. It contains the negative time offset of the last observed packet of this Flow relative to the export time specified in the IPFIX Message Header. microseconds See the IPFIX protocol specification for the definition of the IPFIX Message Header. systemInitTimeMilliseconds dateTimeMilliseconds timestamp 160 data current The absolute timestamp of the last (re-)initialization of the IPFIX Device. milliseconds flowDurationMilliseconds unsigned32 misc 161 data current The difference in time between the first observed packet of this Flow and the last observed packet of this Flow. milliseconds flowDurationMicroseconds unsigned32 misc 162 data current The difference in time between the first observed packet of this Flow and the last observed packet of this Flow. microseconds observedFlowTotalCount unsigned64 processCounter totalCounter 163 data current The total number of Flows observed in the Observation Domain since the Metering Process (re-)initialization for this Observation Point. flows ignoredPacketTotalCount unsigned64 processCounter totalCounter 164 data current The total number of observed IP packets that the Metering Process did not process since the (re-)initialization of the Metering Process. packets ignoredOctetTotalCount unsigned64 processCounter totalCounter 165 data current The total number of octets in observed IP packets (including the IP header) that the Metering Process did not process since the (re-)initialization of the Metering Process. octets notSentFlowTotalCount unsigned64 processCounter totalCounter 166 data current The total number of Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies. flows notSentPacketTotalCount unsigned64 processCounter totalCounter 167 data current The total number of packets in Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies. packets notSentOctetTotalCount unsigned64 processCounter totalCounter 168 data current The total number of octets in packets in Flow Records that were generated by the Metering Process and dropped by the Metering Process or by the Exporting Process instead of being sent to the Collecting Process. There are several potential reasons for this including resource shortage and special Flow export policies. octets destinationIPv6Prefix ipv6Address ipHeader 169 data current IPv6 destination address prefix. sourceIPv6Prefix ipv6Address ipHeader 170 data current IPv6 source address prefix. postOctetTotalCount unsigned64 flowCounter totalCounter 171 all current The definition of this Information Element is identical to the definition of Information Element 'octetTotalCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. octets postPacketTotalCount unsigned64 flowCounter totalCounter 172 all current The definition of this Information Element is identical to the definition of Information Element 'packetTotalCount', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. packets flowKeyIndicator unsigned64 config flags 173 all current This set of bit fields is used for marking the Information Elements of a Data Record that serve as Flow Key. Each bit represents an Information Element in the Data Record with the n-th bit representing the n-th Information Element. A bit set to value 1 indicates that the corresponding Information Element is a Flow Key of the reported Flow. A bit set to value 0 indicates that this is not the case. If the Data Record contains more than 64 Information Elements, the corresponding Template SHOULD be designed such that all Flow Keys are among the first 64 Information Elements, because the flowKeyIndicator only contains 64 bits. If the Data Record contains less than 64 Information Elements, then the bits in the flowKeyIndicator for which no corresponding Information Element exists MUST have the value 0. postMCastPacketTotalCount unsigned64 flowCounter totalCounter 174 data current The total number of outgoing multicast packets sent for packets of this Flow by a multicast daemon within the Observation Domain since the Metering Process (re-)initialization. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. packets postMCastOctetTotalCount unsigned64 flowCounter totalCounter 175 data current The total number of octets in outgoing multicast packets sent for packets of this Flow by a multicast daemon in the Observation Domain since the Metering Process (re-)initialization. This property cannot necessarily be observed at the Observation Point, but may be retrieved by other means. The number of octets includes IP header(s) and IP payload. octets icmpTypeIPv4 unsigned8 transportHeader identifier 176 all current Type of the IPv4 ICMP message. See for the definition of the IPv4 ICMP type field. icmpCodeIPv4 unsigned8 transportHeader identifier 177 all current Code of the IPv4 ICMP message. See for the definition of the IPv4 ICMP code field. icmpTypeIPv6 unsigned8 transportHeader identifier 178 all current Type of the IPv6 ICMP message. See for the definition of the IPv6 ICMP type field. icmpCodeIPv6 unsigned8 transportHeader identifier 179 all current Code of the IPv6 ICMP message. See for the definition of the IPv6 ICMP code field. udpSourcePort unsigned16 transportHeader identifier 180 all current The source port identifier in the UDP header. See for the definition of the UDP source port field. Additional information on defined UDP port numbers can be found at . udpDestinationPort unsigned16 transportHeader identifier 181 all current The destination port identifier in the UDP header. See for the definition of the UDP destination port field. Additional information on defined UDP port numbers can be found at . tcpSourcePort unsigned16 transportHeader identifier 182 all current The source port identifier in the TCP header. See for the definition of the TCP source port field. Additional information on defined TCP port numbers can be found at . tcpDestinationPort unsigned16 transportHeader identifier 183 all current The destination port identifier in the TCP header. See for the definition of the TCP destination port field. Additional information on defined TCP port numbers can be found at . tcpSequenceNumber unsigned32 transportHeader 184 all current The sequence number in the TCP header. See for the definition of the TCP sequence number. tcpAcknowledgementNumber unsigned32 transportHeader 185 all current The acknowledgement number in the TCP header. See for the definition of the TCP acknowledgement number. tcpWindowSize unsigned16 transportHeader 186 all current The window field in the TCP header. If the TCP window scale is supported, then TCP window scale must be known to fully interpret the value of this information. See for the definition of the TCP window field. See for the definition of the TCP window scale. tcpUrgentPointer unsigned16 transportHeader 187 all current The urgent pointer in the TCP header. See for the definition of the TCP urgent pointer. tcpHeaderLength unsigned8 transportHeader 188 all current The length of the TCP header. Note that the value of this Information Element is different from the value of the Data Offset field in the TCP header. The Data Offset field indicates the length of the TCP header in units of 4 octets. This Information Elements specifies the length of the TCP header in units of octets. octets See for the definition of the TCP header. ipHeaderLength unsigned8 ipHeader 189 all current The length of the IP header. For IPv6, the value of this Information Element is 40. octets See for the definition of the IPv4 header. See for the definition of the IPv6 header. totalLengthIPv4 unsigned16 ipHeader 190 all current The total length of the IPv4 packet. octets See for the specification of the IPv4 total length. payloadLengthIPv6 unsigned16 ipHeader 191 all current This Information Element reports the value of the Payload Length field in the IPv6 header. Note that IPv6 extension headers belong to the payload. Also note that in case of a jumbo payload option the value of the Payload Length field in the IPv6 header is zero and so will be the value reported by this Information Element. octets See for the specification of the IPv6 payload length. See for the specification of the IPv6 jumbo payload option. ipTTL unsigned8 ipHeader 192 all current For IPv4, the value of the Information Element matches the value of the Time to Live (TTL) field in the IPv4 packet header. For IPv6, the value of the Information Element matches the value of the Hop Limit field in the IPv6 packet header. hops See for the definition of the IPv4 Time to Live field. See for the definition of the IPv6 Hop Limit field. nextHeaderIPv6 unsigned8 ipHeader 193 all current The value of the Next Header field of the IPv6 header. The value identifies the type of the following IPv6 extension header or of the following IP payload. Valid values are defined in the IANA Protocol Numbers registry. See for the definition of the IPv6 Next Header field. See the list of protocol numbers assigned by IANA at . mplsPayloadLength unsigned32 subIpHeader 194 all current The size of the MPLS packet without the label stack. octets See for the specification of MPLS packets. See for the specification of the MPLS label stack. ipDiffServCodePoint unsigned8 ipHeader identifier 195 all current The value of a Differentiated Services Code Point (DSCP) encoded in the Differentiated Services field. The Differentiated Services field spans the most significant 6 bits of the IPv4 TOS field or the IPv6 Traffic Class field, respectively. This Information Element encodes only the 6 bits of the Differentiated Services field. Therefore, its value may range from 0 to 63. 0-63 See for the definition of the Differentiated Services field. See (Section 5.3.2) and for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. ipPrecedence unsigned8 ipHeader identifier 196 all current The value of the IP Precedence. The IP Precedence value is encoded in the first 3 bits of the IPv4 TOS field or the IPv6 Traffic Class field, respectively. This Information Element encodes only these 3 bits. Therefore, its value may range from 0 to 7. 0-7 See (Section 5.3.3) and for the definition of the IP Precedence. See (Section 5.3.2) and for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. fragmentFlags unsigned8 ipHeader flags 197 all current Fragmentation properties indicated by flags in the IPv4 packet header or the IPv6 Fragment header, respectively. Bit 0: (RS) Reserved. The value of this bit MUST be 0 until specified otherwise. Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. Corresponds to the value of the DF flag in the IPv4 header. Will always be 0 for IPv6 unless a "don't fragment" feature is introduced to IPv6. Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. Corresponds to the MF flag in the IPv4 header or to the M flag in the IPv6 Fragment header, respectively. The value is 0 for IPv6 if there is no fragment header. Bits 3-7: (DC) Don't Care. The values of these bits are irrelevant. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | R | D | M | D | D | D | D | D | | S | F | F | C | C | C | C | C | +---+---+---+---+---+---+---+---+ See for the specification of the IPv4 fragment flags. See for the specification of the IPv6 Fragment header. octetDeltaSumOfSquares unsigned64 flowCounter 198 data current The sum of the squared numbers of octets per incoming packet since the previous report (if any) for this Flow at the Observation Point. The number of octets includes IP header(s) and IP payload. octetTotalSumOfSquares unsigned64 flowCounter 199 all current The total sum of the squared numbers of octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes IP header(s) and IP payload. octets mplsTopLabelTTL unsigned8 subIpHeader 200 all current The TTL field from the top MPLS label stack entry, i.e., the last label that was pushed. hops See for the specification of the TTL field. mplsLabelStackLength unsigned32 subIpHeader 201 all current The length of the MPLS label stack in units of octets. octets See for the specification of the MPLS label stack. mplsLabelStackDepth unsigned32 subIpHeader 202 all current The number of labels in the MPLS label stack. label stack entries See for the specification of the MPLS label stack. mplsTopLabelExp unsigned8 subIpHeader flags 203 all current The Exp field from the top MPLS label stack entry, i.e., the last label that was pushed. Bits 0-4: Don't Care, value is irrelevant. Bits 5-7: MPLS Exp field. 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | don't care | Exp | +---+---+---+---+---+---+---+---+ See for the specification of the Exp field. See for usage of the Exp field. ipPayloadLength unsigned32 derived 204 all current The effective length of the IP payload. For IPv4 packets, the value of this Information Element is the difference between the total length of the IPv4 packet (as reported by Information Element totalLengthIPv4) and the length of the IPv4 header (as reported by Information Element headerLengthIPv4). For IPv6, the value of the Payload Length field in the IPv6 header is reported except in the case that the value of this field is zero and that there is a valid jumbo payload option. In this case, the value of the Jumbo Payload Length field in the jumbo payload option is reported. octets See for the specification of IPv4 packets. See for the specification of the IPv6 payload length. See for the specification of the IPv6 jumbo payload length. udpMessageLength unsigned16 transportHeader 205 all current The value of the Length field in the UDP header. octets See for the specification of the UDP header. isMulticast unsigned8 ipHeader flags 206 data current If the IP destination address is not a reserved multicast address, then the value of all bits of the octet (including the reserved ones) is zero. The first bit of this octet is set to 1 if the Version field of the IP header has the value 4 and if the Destination Address field contains a reserved multicast address in the range from 224.0.0.0 to 239.255.255.255. Otherwise, this bit is set to 0. The second and third bits of this octet are reserved for future use. The remaining bits of the octet are only set to values other than zero if the IP Destination Address is a reserved IPv6 multicast address. Then the fourth bit of the octet is set to the value of the T flag in the IPv6 multicast address and the remaining four bits are set to the value of the scope field in the IPv6 multicast address. 0 1 2 3 4 5 6 7 +------+------+------+------+------+------+------+------+ | IPv6 multicast scope | T | RES. | RES. | MCv4 | +------+------+------+------+------+------+------+------+ Bits 0-3: set to value of multicast scope if IPv6 multicast Bit 4: set to value of T flag, if IPv6 multicast Bits 5-6: reserved for future use Bit 7: set to 1 if IPv4 multicast See for the specification of reserved IPv4 multicast addresses. See for the specification of reserved IPv6 multicast addresses and the definition of the T flag and the IPv6 multicast scope. The diagram provided in is incorrect. The diagram in this registry is taken from Errata 1736. See ipv4IHL unsigned8 ipHeader 207 all current The value of the Internet Header Length (IHL) field in the IPv4 header. It specifies the length of the header in units of 4 octets. Please note that its unit is different from most of the other Information Elements reporting length values. 4 octets See for the specification of the IPv4 header. ipv4Options unsigned32 minMax flags 208 all current IPv4 options in packets of this Flow. The information is encoded in a set of bit fields. For each valid IPv4 option type, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding IPv4 option type. Otherwise, if no observed packet of this Flow contained the respective IPv4 option type, the value of the corresponding bit is 0. The list of valid IPv4 options is maintained by IANA. Note that for identifying an option not just the 5-bit Option Number, but all 8 bits of the Option Type need to match one of the IPv4 options specified at http://www.iana.org/assignments/ip-parameters. Options are mapped to bits according to their option numbers. Option number X is mapped to bit X. The mapping is illustrated by the figure below. 0 1 2 3 4 5 6 7 +------+------+------+------+------+------+------+------+ ... | RR |CIPSO |E-SEC | TS | LSR | SEC | NOP | EOOL | +------+------+------+------+------+------+------+------+ 8 9 10 11 12 13 14 15 +------+------+------+------+------+------+------+------+ ... |ENCODE| VISA | FINN | MTUR | MTUP | ZSU | SSR | SID | ... +------+------+------+------+------+------+------+------+ 16 17 18 19 20 21 22 23 +------+------+------+------+------+------+------+------+ ... | DPS |NSAPA | SDB |RTRALT|ADDEXT| TR | EIP |IMITD | ... +------+------+------+------+------+------+------+------+ 24 25 26 27 28 29 30 31 +------+------+------+------+------+------+------+------+ | | EXP | to be assigned by IANA | QS | UMP | ... +------+------+------+------+------+------+------+------+ Type Option Bit Value Name Reference ---+-----+-------+------------------------------------ 0 7 RR Record Route, RFC 791 1 134 CIPSO Commercial Security 2 133 E-SEC Extended Security, RFC 1108 3 68 TS Time Stamp, RFC 791 4 131 LSR Loose Source Route, RFC791 5 130 SEC Security, RFC 1108 6 1 NOP No Operation, RFC 791 7 0 EOOL End of Options List, RFC 791 8 15 ENCODE 9 142 VISA Experimental Access Control 10 205 FINN Experimental Flow Control 11 12 MTUR (obsoleted) MTU Reply, RFC 1191 12 11 MTUP (obsoleted) MTU Probe, RFC 1191 13 10 ZSU Experimental Measurement 14 137 SSR Strict Source Route, RFC 791 15 136 SID Stream ID, RFC 791 16 151 DPS Dynamic Packet State 17 150 NSAPA NSAP Address 18 149 SDB Selective Directed Broadcast 19 147 ADDEXT Address Extension 20 148 RTRALT Router Alert, RFC 2113 21 82 TR Traceroute, RFC 3193 22 145 EIP Extended Internet Protocol, RFC 1385 23 144 IMITD IMI Traffic Descriptor 25 30 EXP RFC3692-style Experiment 25 94 EXP RFC3692-style Experiment 25 158 EXP RFC3692-style Experiment 25 222 EXP RFC3692-style Experiment 30 25 QS Quick-Start 31 152 UMP Upstream Multicast Pkt. ... ... ... Further options numbers may be assigned by IANA See for the definition of IPv4 options. See the list of IPv4 option numbers assigned by IANA at . The diagram provided in is incorrect. The diagram in this registry is taken from Errata 1737. See tcpOptions unsigned64 minMax flags 209 all current TCP options in packets of this Flow. The information is encoded in a set of bit fields. For each TCP option, there is a bit in this set. The bit is set to 1 if any observed packet of this Flow contains the corresponding TCP option. Otherwise, if no observed packet of this Flow contained the respective TCP option, the value of the corresponding bit is 0. Options are mapped to bits according to their option numbers. Option number X is mapped to bit X. TCP option numbers are maintained by IANA. 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ... +-----+-----+-----+-----+-----+-----+-----+-----+ 8 9 10 11 12 13 14 15 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |... +-----+-----+-----+-----+-----+-----+-----+-----+ 16 17 18 19 20 21 22 23 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |... +-----+-----+-----+-----+-----+-----+-----+-----+ . . . 56 57 58 59 60 61 62 63 +-----+-----+-----+-----+-----+-----+-----+-----+ ... | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | +-----+-----+-----+-----+-----+-----+-----+-----+ See for the definition of TCP options. See the list of TCP option numbers assigned by IANA at . The diagram provided in is incorrect. The diagram in this registry is taken from Errata 1739. See paddingOctets octetArray padding 210 option current The value of this Information Element is always a sequence of 0x00 values. collectorIPv4Address ipv4Address config identifier 211 all current An IPv4 address to which the Exporting Process sends Flow information. collectorIPv6Address ipv6Address config identifier 212 all current An IPv6 address to which the Exporting Process sends Flow information. exportInterface unsigned32 config identifier 213 all current The index of the interface from which IPFIX Messages sent by the Exporting Process to a Collector leave the IPFIX Device. The value matches the value of managed object 'ifIndex' as defined in RFC 2863. Note that ifIndex values are not assigned statically to an interface and that the interfaces may be renumbered every time the device's management system is re-initialized, as specified in RFC 2863. See for the definition of the ifIndex object. exportProtocolVersion unsigned8 config identifier 214 all current The protocol version used by the Exporting Process for sending Flow information. The protocol version is given by the value of the Version Number field in the Message Header. The protocol version is 10 for IPFIX and 9 for NetFlow version 9. A value of 0 indicates that no export protocol is in use. See the IPFIX protocol specification for the definition of the IPFIX Message Header. See for the definition of the NetFlow version 9 message header. exportTransportProtocol unsigned8 config identifier 215 all current The value of the protocol number used by the Exporting Process for sending Flow information. The protocol number identifies the IP packet payload type. Protocol numbers are defined in the IANA Protocol Numbers registry. In Internet Protocol version 4 (IPv4), this is carried in the Protocol field. In Internet Protocol version 6 (IPv6), this is carried in the Next Header field in the last extension header of the packet. See for the specification of the IPv4 protocol field. See for the specification of the IPv6 protocol field. See the list of protocol numbers assigned by IANA at . collectorTransportPort unsigned16 config identifier 216 all current The destination port identifier to which the Exporting Process sends Flow information. For the transport protocols UDP, TCP, and SCTP, this is the destination port number. This field MAY also be used for future transport protocols that have 16-bit source port identifiers. See for the definition of the UDP destination port field. See for the definition of the TCP destination port field. See for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at . exporterTransportPort unsigned16 config identifier 217 all current The source port identifier from which the Exporting Process sends Flow information. For the transport protocols UDP, TCP, and SCTP, this is the source port number. This field MAY also be used for future transport protocols that have 16-bit source port identifiers. This field may be useful for distinguishing multiple Exporting Processes that use the same IP address. See for the definition of the UDP source port field. See for the definition of the TCP source port field. See for the definition of SCTP. Additional information on defined UDP and TCP port numbers can be found at . tcpSynTotalCount unsigned64 flowCounter totalCounter 218 data current The total number of packets of this Flow with TCP "Synchronize sequence numbers" (SYN) flag set. packets See for the definition of the TCP SYN flag. tcpFinTotalCount unsigned64 flowCounter totalCounter 219 data current The total number of packets of this Flow with TCP "No more data from sender" (FIN) flag set. packets See for the definition of the TCP FIN flag. tcpRstTotalCount unsigned64 flowCounter totalCounter 220 data current The total number of packets of this Flow with TCP "Reset the connection" (RST) flag set. packets See for the definition of the TCP RST flag. tcpPshTotalCount unsigned64 flowCounter totalCounter 221 data current The total number of packets of this Flow with TCP "Push Function" (PSH) flag set. packets See for the definition of the TCP PSH flag. tcpAckTotalCount unsigned64 flowCounter totalCounter 222 data current The total number of packets of this Flow with TCP "Acknowledgment field significant" (ACK) flag set. packets See for the definition of the TCP ACK flag. tcpUrgTotalCount unsigned64 flowCounter totalCounter 223 data current The total number of packets of this Flow with TCP "Urgent Pointer field significant" (URG) flag set. packets See for the definition of the TCP URG flag. ipTotalLength unsigned64 ipHeader 224 all current The total length of the IP packet. octets See for the specification of the IPv4 total length. See for the specification of the IPv6 payload length. See for the specification of the IPv6 jumbo payload length. postNATSourceIPv4Address ipv4Address identifier 225 current The definition of this Information Element is identical to the definition of Information Element 'sourceIPv4Address', except that it reports a modified value caused by a NAT middlebox function after the packet passed the Observation Point. See for the definition of the IPv4 source address field. See for the definition of NAT. See for the definition of middleboxes. postNATDestinationIPv4Address ipv4Address identifier 226 current The definition of this Information Element is identical to the definition of Information Element 'destinationIPv4Address', except that it reports a modified value caused by a NAT middlebox function after the packet passed the Observation Point. See for the definition of the IPv4 destination address field. See for the definition of NAT. See for the definition of middleboxes. postNAPTSourceTransportPort unsigned16 identifier 227 current The definition of this Information Element is identical to the definition of Information Element 'sourceTransportPort', except that it reports a modified value caused by a Network Address Port Translation (NAPT) middlebox function after the packet passed the Observation Point. See for the definition of the UDP source port field. See for the definition of the TCP source port field. See for the definition of SCTP. See for the definition of NAPT. See for the definition of middleboxes. Additional information on defined UDP and TCP port numbers can be found at http://www.iana.org/assignments/port-numbers. postNAPTDestinationTransportPort unsigned16 identifier 228 current The definition of this Information Element is identical to the definition of Information Element 'destinationTransportPort', except that it reports a modified value caused by a Network Address Port Translation (NAPT) middlebox function after the packet passed the Observation Point. See for the definition of the UDP source port field. See for the definition of the TCP source port field. See for the definition of SCTP. See for the definition of NAPT. See for the definition of middleboxes. Additional information on defined UDP and TCP port numbers can be found at http://www.iana.org/assignments/port-numbers. natOriginatingAddressRealm unsigned8 flags 229 current Indicates whether the session was created because traffic originated in the private or public address realm. postNATSourceIPv4Address, postNATDestinationIPv4Address, postNAPTSourceTransportPort, and postNAPTDestinationTransportPort are qualified with the address realm in perspective. The allowed values are: Private: 1 Public: 2 See for the definition of NAT. natEvent unsigned8 230 current Indicates a NAT event. The allowed values are: 1 - Create event. 2 - Delete event. 3 - Pool exhausted. A Create event is generated when a NAT translation is created, whether dynamically or statically. A Delete event is generated when a NAT translation is deleted. See for the definition of NAT. initiatorOctets unsigned64 231 current The total number of layer 4 payload bytes in a flow from the initiator. The initiator is the device which triggered the session creation, and remains the same for the life of the session. octets See #298, initiatorPackets. responderOctets unsigned64 232 current The total number of layer 4 payload bytes in a flow from the responder. The responder is the device which replies to the initiator, and remains the same for the life of the session. octets See #299, responderPackets. firewallEvent unsigned8 233 current Indicates a firewall event. The allowed values are: 0 - Ignore (invalid) 1 - Flow Created 2 - Flow Deleted 3 - Flow Denied 4 - Flow Alert 5 - Flow Update ingressVRFID unsigned32 234 current An unique identifier of the VRFname where the packets of this flow are being received. This identifier is unique per Metering Process egressVRFID unsigned32 235 current An unique identifier of the VRFname where the packets of this flow are being sent. This identifier is unique per Metering Process VRFname string 236 current The name of a VPN Routing and Forwarding table (VRF). See for the definition of VRF. postMplsTopLabelExp unsigned8 subIpHeader flags 237 all current The definition of this Information Element is identical to the definition of Information Element 'mplsTopLabelExp', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. See for the specification of the Exp field. See for usage of the Exp field. tcpWindowScale unsigned16 transportHeader 238 all current The scale of the window field in the TCP header. See for the definition of the TCP window scale. biflowDirection unsigned8 misc identifier 239 all current A description of the direction assignment method used to assign the Biflow Source and Destination. This Information Element MAY be present in a Flow Data Record, or applied to all flows exported from an Exporting Process or Observation Domain using IPFIX Options. If this Information Element is not present in a Flow Record or associated with a Biflow via scope, it is assumed that the configuration of the direction assignment method is done out-of-band. Note that when using IPFIX Options to apply this Information Element to all flows within an Observation Domain or from an Exporting Process, the Option SHOULD be sent reliably. If reliable transport is not available (i.e., when using UDP), this Information Element SHOULD appear in each Flow Record. This field may take the following values: +-------+------------------+----------------------------------------+ | Value | Name | Description | +-------+------------------+----------------------------------------+ | 0x00 | arbitrary | Direction was assigned arbitrarily. | | 0x01 | initiator | The Biflow Source is the flow | | | | initiator, as determined by the | | | | Metering Process' best effort to | | | | detect the initiator. | | 0x02 | reverseInitiator | The Biflow Destination is the flow | | | | initiator, as determined by the | | | | Metering Process' best effort to | | | | detect the initiator. This value is | | | | provided for the convenience of | | | | Exporting Processes to revise an | | | | initiator estimate without re-encoding | | | | the Biflow Record. | | 0x03 | perimeter | The Biflow Source is the endpoint | | | | outside of a defined perimeter. The | | | | perimeter's definition is implicit in | | | | the set of Biflow Source and Biflow | | | | Destination addresses exported in the | | | | Biflow Records. | +-------+------------------+----------------------------------------+ ethernetHeaderLength unsigned8 identifier 240 current The difference between the length of an Ethernet frame (minus the FCS) and the length of its MAC Client Data section (including any padding) as defined in section 3.1 of [IEEE.802-3.2005]. It does not include the Preamble, SFD and Extension field lengths. octets [IEEE.802-3.2005] ethernetPayloadLength unsigned16 identifier 241 current The length of the MAC Client Data section (including any padding) of a frame as defined in section 3.1 of [IEEE.802-3.2005]. octets [IEEE.802-3.2005] ethernetTotalLength unsigned16 identifier 242 current The total length of the Ethernet frame (excluding the Preamble, SFD, Extension and FCS fields) as described in section 3.1 of [IEEE.802-3.2005]. octets [IEEE.802-3.2005] dot1qVlanId unsigned16 identifier 243 current The value of the 12-bit VLAN Identifier portion of the Tag Control Information field of an Ethernet frame as described in section 3.5.5 of [IEEE.802-3.2005]. The structure and semantics within the Tag Control Information field are defined in IEEE P802.1Q. In case of a QinQ frame, it represents the outer tag's VLAN identifier and in case of an IEEE 802.1ad frame it represents the Service VLAN identifier in the S-TAG Tag Control Information (TCI) field as described in [IEEE.802-1ad.2005]. octets [IEEE.802-3.2005] dot1qPriority unsigned8 identifier 244 current The value of the 3-bit User Priority portion of the Tag Control Information field of an Ethernet frame as described in section 3.5.5 of [IEEE.802-3.2005]. The structure and semantics within the Tag Control Information field are defined in IEEE P802.1Q. In case of a QinQ frame, it represents the outer tag's 3-bit Class of Service (CoS) identifier and in case of an IEEE 802.1ad frame it represents the 3-bit Priority Code Point (PCP) portion of the S-TAG Tag Control Information (TCI) field as described in [IEEE.802-1ad.2005]. [IEEE.802-3.2005] [IEEE.802-1ad.2005] dot1qCustomerVlanId unsigned16 identifier 245 current In case of a QinQ frame, it represents the inner tag's (*) VLAN identifier and in case of an IEEE 802.1ad frame it represents the Customer VLAN identifier in the C-TAG Tag Control Information (TCI) field as described in [IEEE.802-1ad.2005]. (*) Note: the 801.2Q tag directly following the outer one. [IEEE.802-1ad.2005] [IEEE.802-1Q.2003] dot1qCustomerPriority unsigned8 identifier 246 current In case of a QinQ frame, it represents the inner tag's (*) Class of Service (CoS) identifier and in case of an IEEE 802.1ad frame it represents the 3-bit Priority Code Point (PCP) portion of the C-TAG Tag Control Information (TCI) field as described in [IEEE.802-1ad.2005]. (*) Note: the 801.2Q tag directly following the outer one. [IEEE.802-1ad.2005] [IEEE.802-1Q.2003] metroEvcId string 247 current The EVC Service Attribute which uniquely identifies the Ethernet Virtual Connection (EVC) within a Metro Ethernet Network, as defined in section 6.2 of MEF 10.1. The MetroEVCID is encoded in a string of up to 100 characters. MEF 10.1 (Ethernet Services Attributes Phase 2) MEF16 (Ethernet Local Management Interface) metroEvcType unsigned8 identifier 248 current The 3-bit EVC Service Attribute which identifies the type of service provided by an EVC. MEF 10.1 (Ethernet Services Attributes Phase 2) MEF16 (Ethernet Local Management Interface) pseudoWireId unsigned32 identifier 249 current A 32-bit non-zero connection identifier, which together with the pseudoWireType, identifies the Pseudo Wire (PW) as defined in RFC 4447 [RFC4447]. See for pseudowire definitions. pseudoWireType unsigned16 identifier 250 current The value of this information element identifies the type of MPLS Pseudo Wire (PW) as defined in RFC 4446. See for the pseudowire type definition, and http://www.iana.org/assignments/pwe3-parameters for the IANA Pseudowire Types Registry. pseudoWireControlWord unsigned32 identifier 251 current The 32-bit Preferred Pseudo Wire (PW) MPLS Control Word as defined in Section 3 of . See for the Pseudo Wire Control Word definition. ingressPhysicalInterface unsigned32 identifier 252 current The index of a networking device's physical interface (example, a switch port) where packets of this flow are being received. See for the definition of the ifIndex object. egressPhysicalInterface unsigned32 identifier 253 current The index of a networking device's physical interface (example, a switch port) where packets of this flow are being sent. See for the definition of the ifIndex object. postDot1qVlanId unsigned16 identifier 254 current The definition of this Information Element is identical to the definition of Information Element 'dot1qVlanId', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. [IEEE.802-3.2005] [IEEE.802-1ad.2005] postDot1qCustomerVlanId unsigned16 identifier 255 current The definition of this Information Element is identical to the definition of Information Element 'dot1qCustomerVlanId', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. [IEEE.802-1ad.2005] [IEEE.802-1Q.2003] ethernetType unsigned16 identifier 256 current The Ethernet type field of an Ethernet frame that identifies the MAC client protocol carried in the payload as defined in paragraph 1.4.349 of [IEEE.802-3.2005]. [IEEE.802-3.2005] Ethertype registry available at http://standards.ieee.org/regauth/ethertype/eth.txt postIpPrecedence unsigned8 identifier 257 current The definition of this Information Element is identical to the definition of Information Element 'ipPrecedence', except that it reports a potentially modified value caused by a middlebox function after the packet passed the Observation Point. 0-7 See (Section 5.3.3) and for the definition of the IP Precedence. See (Section 5.3.2) and for the definition of the IPv4 TOS field. See for the definition of the IPv6 Traffic Class field. collectionTimeMilliseconds dateTimeMilliseconds 258 current The absolute timestamp at which the data within the scope containing this Information Element was received by a Collecting Process. This Information Element SHOULD be bound to its containing IPFIX Message via IPFIX Options and the messageScope Information Element, as defined below. exportSctpStreamId unsigned16 identifier 259 current The value of the SCTP Stream Identifier used by the Exporting Process for exporting IPFIX Message data. This is carried in the Stream Identifier field of the header of the SCTP DATA chunk containing the IPFIX Message(s). maxExportSeconds dateTimeSeconds 260 current The absolute Export Time of the latest IPFIX Message within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. seconds maxFlowEndSeconds dateTimeSeconds 261 current The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the second if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. seconds messageMD5Checksum octetArray 262 current The MD5 checksum of the IPFIX Message containing this record. This Information Element SHOULD be bound to its containing IPFIX Message via an options record and the messageScope Information Element, as defined below, and SHOULD appear only once in a given IPFIX Message. To calculate the value of this Information Element, first buffer the containing IPFIX Message, setting the value of this Information Element to all zeroes. Then calculate the MD5 checksum of the resulting buffer as defined in [RFC1321], place the resulting value in this Information Element, and export the buffered message. This Information Element is intended as a simple checksum only; therefore collision resistance and algorithm agility are not required, and MD5 is an appropriate message digest. This Information Element has a fixed length of 16 octets. messageScope unsigned8 263 current The presence of this Information Element as scope in an Options Template signifies that the options described by the Template apply to the IPFIX Message that contains them. It is defined for general purpose message scoping of options, and proposed specifically to allow the attachment a checksum to a message via IPFIX Options. The value of this Information Element MUST be written as 0 by the File Writer or Exporting Process. The value of this Information Element MUST be ignored by the File Reader or the Collecting Process. minExportSeconds dateTimeSeconds 264 current The absolute Export Time of the earliest IPFIX Message within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. seconds minFlowStartSeconds dateTimeSeconds 265 current The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the second if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. seconds opaqueOctets octetArray 266 current This Information Element is used to encapsulate non- IPFIX data into an IPFIX Message stream, for the purpose of allowing a non-IPFIX data processor to store a data stream inline within an IPFIX File. A Collecting Process or File Writer MUST NOT try to interpret this binary data. This Information Element differs from paddingOctets as its contents are meaningful in some non-IPFIX context, while the contents of paddingOctets MUST be 0x00 and are intended only for Information Element alignment. sessionScope unsigned8 267 current The presence of this Information Element as scope in an Options Template signifies that the options described by the Template apply to the IPFIX Transport Session that contains them. Note that as all options are implicitly scoped to Transport Session and Observation Domain, this Information Element is equivalent to a "null" scope. It is defined for general purpose session scoping of options, and proposed specifically to allow the attachment of time window to an IPFIX File via IPFIX Options. The value of this Information Element MUST be written as 0 by the File Writer or Exporting Process. The value of this Information Element MUST be ignored by the File Reader or the Collecting Process. maxFlowEndMicroseconds dateTimeMicroseconds 268 current The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the microsecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with microsecond- precision (or better) timestamp Information Elements. microseconds maxFlowEndMilliseconds dateTimeMilliseconds 269 current The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element, rounded up to the millisecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with millisecond- precision (or better) timestamp Information Elements. milliseconds maxFlowEndNanoseconds dateTimeNanoseconds 270 current The latest absolute timestamp of the last packet within any Flow within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via IPFIX Options and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with nanosecond-precision timestamp Information Elements. nanoseconds minFlowStartMicroseconds dateTimeMicroseconds 271 current The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the microsecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with microsecond- precision (or better) timestamp Information Elements. microseconds minFlowStartMilliseconds dateTimeMilliseconds 272 current The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element, rounded down to the millisecond if necessary. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with millisecond- precision (or better) timestamp Information Elements. milliseconds minFlowStartNanoseconds dateTimeNanoseconds 273 current The earliest absolute timestamp of the first packet within any Flow within the scope containing this Information Element. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element. This Information Element SHOULD be used only in Transport Sessions containing Flow Records with nanosecond-precision timestamp Information Elements. nanoseconds collectorCertificate octetArray 274 current The full X.509 certificate, encoded in ASN.1 DER format, used by the Collector when IPFIX Messages were transmitted using TLS or DTLS. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element, or to its containing IPFIX Message via an options record and the messageScope Information Element. exporterCertificate octetArray 275 current The full X.509 certificate, encoded in ASN.1 DER format, used by the Collector when IPFIX Messages were transmitted using TLS or DTLS. This Information Element SHOULD be bound to its containing IPFIX Transport Session via an options record and the sessionScope Information Element, or to its containing IPFIX Message via an options record and the messageScope Information Element. dataRecordsReliability boolean identifier 276 current The export reliability of Data Records, within this SCTP stream, for the element(s) in the Options Template scope. A typical example of an element for which the export reliability will be reported is the templateID, as specified in the Data Records Reliability Options Template. A value of 'True' means that the Exporting Process MUST send any Data Records associated with the element(s) reliably within this SCTP stream. A value of 'False' means that the Exporting Process MAY send any Data Records associated with the element(s) unreliably within this SCTP stream. observationPointType unsigned8 identifier 277 current Type of observation point. Values assigned to date are: 1. Physical port 2. Port channel 3. Vlan. connectionCountNew unsigned32 deltaCounter 278 current This information element counts the number of TCP or UDP connections which were opened during the observation period. The observation period may be specified by the flow start and end timestamps. connectionSumDuration unsigned64 279 current This information element aggregates the total time in seconds for all of the TCP or UDP connections which were in use during the observation period. For example if there are 5 concurrent connections each for 10 seconds, the value would be 50 s. connectionTransactionId unsigned64 identifier 280 current This information element identifies a transaction within a connection. A transaction is a meaningful exchange of application data between two network devices or a client and server. A transactionId is assigned the first time a flow is reported, so that later reports for the same flow will have the same transactionId. A different transactionId is used for each transaction within a TCP or UDP connection. The identifiers need not be sequential. postNATSourceIPv6Address ipv6Address 281 current The definition of this Information Element is identical to the definition of Information Element 'sourceIPv6Address', except that it reports a modified value caused by a NAT64 middlebox function after the packet passed the Observation Point. See [RFC2460] for the definition of the Source Address field in the IPv6 header. See [RFC3234] for the definition of middleboxes. See http://tools.ietf.org/html/draft-ietf-behave-v6v4-xlate-stateful-12 for nat64 specification. postNATDestinationIPv6Address ipv6Address 282 current The definition of this Information Element is identical to the definition of Information Element 'destinationIPv6Address', except that it reports a modified value caused by a NAT64 middlebox function after the packet passed the Observation Point. See [RFC2460] for the definition of the Destination Address field in the IPv6 header. See [RFC3234] for the definition of middleboxes. See http://tools.ietf.org/html/draft-ietf-behave-v6v4-xlate-stateful-12 for nat64 specification. natPoolId unsigned32 identifier 283 current Locally unique identifier of a NAT pool. natPoolName string 284 current The name of a NAT pool identified by a natPoolID. anonymizationFlags unsigned16 flags 285 current A flag word describing specialized modifications to the anonymization policy in effect for the anonymization technique applied to a referenced Information Element within a referenced Template. When flags are clear (0), the normal policy (as described by anonymizationTechnique) applies without modification. MSB 14 13 12 11 10 9 8 7 6 5 4 3 2 1 LSB +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | Reserved |LOR|PmA| SC | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ anonymizationFlags IE +--------+----------+-----------------------------------------------+ | bit(s) | name | description | | (LSB = | | | | 0) | | | +--------+----------+-----------------------------------------------+ | 0-1 | SC | Stability Class: see the Stability Class | | | | table below, and section Section 5.1. | | 2 | PmA | Perimeter Anonymization: when set (1), | | | | source- Information Elements as described in | | | | [RFC5103] are interpreted as external | | | | addresses, and destination- Information | | | | Elements as described in [RFC5103] are | | | | interpreted as internal addresses, for the | | | | purposes of associating | | | | anonymizationTechnique to Information | | | | Elements only; see Section 7.2.2 for details. | | | | This bit MUST NOT be set when associated with | | | | a non-endpoint (i.e., source- or | | | | destination-) Information Element. SHOULD be | | | | consistent within a record (i.e., if a | | | | source- Information Element has this flag | | | | set, the corresponding destination- element | | | | SHOULD have this flag set, and vice-versa.) | | 3 | LOR | Low-Order Unchanged: when set (1), the | | | | low-order bits of the anonymized Information | | | | Element contain real data. This modification | | | | is intended for the anonymization of | | | | network-level addresses while leaving | | | | host-level addresses intact in order to | | | | preserve host level-structure, which could | | | | otherwise be used to reverse anonymization. | | | | MUST NOT be set when associated with a | | | | truncation-based anonymizationTechnique. | | 4-15 | Reserved | Reserved for future use: SHOULD be cleared | | | | (0) by the Exporting Process and MUST be | | | | ignored by the Collecting Process. | +--------+----------+-----------------------------------------------+ The Stability Class portion of this flags word describes the stability class of the anonymization technique applied to a referenced Information Element within a referenced Template. Stability classes refer to the stability of the parameters of the anonymization technique, and therefore the comparability of the mapping between the real and anonymized values over time. This determines which anonymized datasets may be compared with each other. Values are as follows: +-----+-----+-------------------------------------------------------+ | Bit | Bit | Description | | 1 | 0 | | +-----+-----+-------------------------------------------------------+ | 0 | 0 | Undefined: the Exporting Process makes no | | | | representation as to how stable the mapping is, or | | | | over what time period values of this field will | | | | remain comparable; while the Collecting Process MAY | | | | assume Session level stability, Session level | | | | stability is not guaranteed. Processes SHOULD assume | | | | this is the case in the absence of stability class | | | | information; this is the default stability class. | | 0 | 1 | Session: the Exporting Process will ensure that the | | | | parameters of the anonymization technique are stable | | | | during the Transport Session. All the values of the | | | | described Information Element for each Record | | | | described by the referenced Template within the | | | | Transport Session are comparable. The Exporting | | | | Process SHOULD endeavour to ensure at least this | | | | stability class. | | 1 | 0 | Exporter-Collector Pair: the Exporting Process will | | | | ensure that the parameters of the anonymization | | | | technique are stable across Transport Sessions over | | | | time with the given Collecting Process, but may use | | | | different parameters for different Collecting | | | | Processes. Data exported to different Collecting | | | | Processes are not comparable. | | 1 | 1 | Stable: the Exporting Process will ensure that the | | | | parameters of the anonymization technique are stable | | | | across Transport Sessions over time, regardless of | | | | the Collecting Process to which it is sent. | +-----+-----+-------------------------------------------------------+ anonymizationTechnique unsigned16 identifier 286 current A description of the anonymization technique applied to a referenced Information Element within a referenced Template. Each technique may be applicable only to certain Information Elements and recommended only for certain Infomation Elements; these restrictions are noted in the table below. +-------+---------------------------+-----------------+-------------+ | Value | Description | Applicable to | Recommended | | | | | for | +-------+---------------------------+-----------------+-------------+ | 0 | Undefined: the Exporting | all | all | | | Process makes no | | | | | representation as to | | | | | whether the defined field | | | | | is anonymized or not. | | | | | While the Collecting | | | | | Process MAY assume that | | | | | the field is not | | | | | anonymized, it is not | | | | | guaranteed not to be. | | | | | This is the default | | | | | anonymization technique. | | | | 1 | None: the values exported | all | all | | | are real. | | | | 2 | Precision | all | all | | | Degradation/Truncation: | | | | | the values exported are | | | | | anonymized using simple | | | | | precision degradation or | | | | | truncation. The new | | | | | precision or number of | | | | | truncated bits is | | | | | implicit in the exported | | | | | data, and can be deduced | | | | | by the Collecting | | | | | Process. | | | | 3 | Binning: the values | all | all | | | exported are anonymized | | | | | into bins. | | | | 4 | Enumeration: the values | all | timestamps | | | exported are anonymized | | | | | by enumeration. | | | | 5 | Permutation: the values | all | identifiers | | | exported are anonymized | | | | | by permutation. | | | | 6 | Structured Permutation: | addresses | | | | the values exported are | | | | | anonymized by | | | | | permutation, preserving | | | | | bit-level structure as | | | | | appropriate; this | | | | | represents | | | | | prefix-preserving IP | | | | | address anonymization or | | | | | structured MAC address | | | | | anonymization. | | | | 7 | Reverse Truncation: the | addresses | | | | values exported are | | | | | anonymized using reverse | | | | | truncation. The number | | | | | of truncated bits is | | | | | implicit in the exported | | | | | data, and can be deduced | | | | | by the Collecting | | | | | Process. | | | | 8 | Noise: the values | non-identifiers | counters | | | exported are anonymized | | | | | by adding random noise to | | | | | each value. | | | | 9 | Offset: the values | all | timestamps | | | exported are anonymized | | | | | by adding a single offset | | | | | to all values. | | | +-------+---------------------------+-----------------+-------------+ informationElementIndex unsigned16 identifier 287 current A zero-based index of an Information Element referenced by informationElementId within a Template referenced by templateId; used to disambiguate scope for templates containing multiple identical Information Elements. p2pTechnology string 288 current Specifies if the Application ID is based on peer-to-peer technology. Possible values are: { "yes", "y", 1 }, { "no", "n", 2 } and { "unassigned", "u", 0 }. tunnelTechnology string 289 current Specifies if the Application ID is used as a tunnel technology. Possible values are: { "yes", "y", 1 }, { "no", "n", 2 } and { "unassigned", "u", 0 }. encryptedTechnology string 290 current Specifies if the Application ID is an encrypted networking protocol. Possible values are: { "yes", "y", 1 }, { "no", "n", 2 } and { "unassigned", "u", 0 }. basicList basicList list 291 current Specifies a generic Information Element with a basicList abstract data type. For example, a list of port numbers, a list of interface indexes, etc. subTemplateList subTemplateList list 292 current Specifies a generic Information Element with a subTemplateList abstract data type. subTemplateMultiList subTemplateMultiList list 293 current Specifies a generic Information Element with a subTemplateMultiList abstract data type. bgpValidityState unsigned8 identifier 294 current This element describes the "validity state" of the BGP route correspondent source or destination IP address. If the "validity state" for this Flow is only available, then the value of this Information Element is 255. See for a description of BGP-4, for the definition of "validity states" and for the encoding of those "validity states". IPSecSPI unsigned32 identifier 295 current IPSec Security Parameters Index (SPI). 0x0-0xFFFFFFFF See for the definition of SPI. greKey unsigned32 identifier 296 current GRE key, which is used for identifying an individual traffic flow within a tunnel. 0x0-0xFFFFFFFF See for the definition of GRE and the GRE Key. natType unsigned8 identifier 297 current The type of NAT treatment: 0 unknown 1 NAT44 translated 2 NAT64 translated 3 NAT46 translated 4 IPv4-->IPv4 (no NAT) 5 NAT66 translated 6 IPv6-->IPv6 (no NAT) See for the definition of NAT. See for the definition of NAT44. See for the definition of NAT64. See for the definition of NAT46. See for the definition of NAT66. See for the definition of IPv4. See for the definition of IPv6. initiatorPackets unsigned64 identifier 298 current The total number of layer 4 packets in a flow from the initiator. The initiator is the device which triggered the session creation, and remains the same for the life of the session. packets See #231, initiatorOctets. responderPackets unsigned64 identifier 299 current The total number of layer 4 packets in a flow from the responder. The responder is the device which replies to the initiator, and remains the same for the life of the session. packets See #232, responderOctets. observationDomainName string 300 current The name of an observation domain identified by an observationDomainId. See #149, observationDomainId. selectionSequenceId unsigned64 identifier 301 current From all the packets observed at an Observation Point, a subset of the packets is selected by a sequence of one or more Selectors. The selectionSequenceId is a unique value per Observation Domain, specifying the Observation Point and the sequence of Selectors through which the packets are selected. selectorId unsigned64 identifier 302 current The Selector ID is the unique ID identifying a Primitive Selector. Each Primitive Selector must have a unique ID in the Observation Domain. informationElementId unsigned16 identifier 303 current This Information Element contains the ID of another Information Element. selectorAlgorithm unsigned16 identifier 304 current This Information Element identifies the packet selection methods (e.g., Filtering, Sampling) that are applied by the Selection Process. Most of these methods have parameters. Further Information Elements are needed to fully specify packet selection with these methods and all their parameters. The methods listed below are defined in [RFC5475]. For their parameters, Information Elements are defined in the information model document. The names of these Information Elements are listed for each method identifier. Further method identifiers may be added to the list below. It might be necessary to define new Information Elements to specify their parameters. The selectorAlgorithm registry is maintained by IANA. New assignments for the registry will be administered by IANA, and are subject to Expert Review [RFC5226]. The registry can be updated when specifications of the new method(s) and any new Information Elements are provided. The group of experts must double check the selectorAlgorithm definitions and Information Elements with already defined selectorAlgorithms and Information Elements for completeness, accuracy, and redundancy. Those experts will initially be drawn from the Working Group Chairs and document editors of the IPFIX and PSAMP Working Groups. The following packet selection methods identifiers are defined here: http://www.iana.org/assignments/psamp-parameters/psamp-parameters.xhtml There is a broad variety of possible parameters that could be used for Property match Filtering (5) but currently there are no agreed parameters specified. samplingPacketInterval unsigned32 quantity 305 current This Information Element specifies the number of packets that are consecutively sampled. A value of 100 means that 100 consecutive packets are sampled. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector. packets samplingPacketSpace unsigned32 quantity 306 current This Information Element specifies the number of packets between two "samplingPacketInterval"s. A value of 100 means that the next interval starts 100 packets (which are not sampled) after the current "samplingPacketInterval" is over. For example, this Information Element may be used to describe the configuration of a systematic count-based Sampling Selector. packets samplingTimeInterval unsigned32 quantity 307 current This Information Element specifies the time interval in microseconds during which all arriving packets are sampled. For example, this Information Element may be used to describe the configuration of a systematic time-based Sampling Selector. microseconds samplingTimeSpace unsigned32 quantity 308 current This Information Element specifies the time interval in microseconds between two "samplingTimeInterval"s. A value of 100 means that the next interval starts 100 microseconds (during which no packets are sampled) after the current "samplingTimeInterval" is over. For example, this Information Element may used to describe the configuration of a systematic time-based Sampling Selector. microseconds samplingSize unsigned32 quantity 309 current This Information Element specifies the number of elements taken from the parent Population for random Sampling methods. For example, this Information Element may be used to describe the configuration of a random n-out-of-N Sampling Selector. packets samplingPopulation unsigned32 quantity 310 current This Information Element specifies the number of elements in the parent Population for random Sampling methods. For example, this Information Element may be used to describe the configuration of a random n-out-of-N Sampling Selector. packets samplingProbability float64 quantity 311 current This Information Element specifies the probability that a packet is sampled, expressed as a value between 0 and 1. The probability is equal for every packet. A value of 0 means no packet was sampled since the probability is 0. For example, this Information Element may be used to describe the configuration of a uniform probabilistic Sampling Selector. dataLinkFrameSize unsigned16 312 current This Information Element specifies the length of the selected data link frame. The data link layer is defined in [ISO/IEC 7498-1:1994]. ipHeaderPacketSection octetArray 313 current This Information Element, which may have a variable length, carries a series of octets from the start of the IP header of a sampled packet. With sufficient length, this element also reports octets from the IP payload, subject to [RFC2804]. See the Security Considerations section. The size of the exported section may be constrained due to limitations in the IPFIX protocol. The data for this field MUST NOT be padded. ipPayloadPacketSection octetArray 314 current This Information Element, which may have a variable length, carries a series of octets from the start of the IP payload of a sampled packet. The IPv4 payload is that part of the packet that follows the IPv4 header and any options, which [RFC0791] refers to as "data" or "data octets". For example, see the examples in [RFC0791], APPENDIX A. The IPv6 payload is the rest of the packet following the 40 octet IPv6 header. Note that any extension headers present are considered part of the payload. See [RFC2460] for the IPv6 specification. The size of the exported section may be constrained due to limitations in the IPFIX protocol. The data for this field MUST NOT be padded. dataLinkFrameSection octetArray 315 current This Information Element carries n octets from the data link frame of a selected frame, starting sectionOffset octets into the frame. The sectionObservedOctets expresses how much data was observed, while the remainder is padding. When the sectionObservedOctets field corresponding to this Information Element exists, this Information Element MAY have a fixed length and MAY be padded, or MAY have a variable length. When the sectionObservedOctets field corresponding to this Information Element does not exist, this Information Element SHOULD have a variable length and MUST NOT be padded. In this case, the size of the exported section may be constrained due to limitations in the IPFIX protocol. Further Information Elements, i.e., dataLinkFrameType and dataLinkFrameSize are needed to specify the data link type and the size of the data link frame of this Information Element. A set of these Information Elements MAY be contained in a structured data type, as expressed in . Or a set of these Information Elements MAY be contained in one Flow Record as shown in Appendix C of . The data link layer is defined in [ISO/IEC 7498-1:1994]. mplsLabelStackSection octetArray 316 current This Information Element, which may have a variable length, carries the first n octets from the MPLS label stack of a sampled packet. With sufficient length, this element also reports octets from the MPLS payload, subject to [RFC2804]. See the Security Considerations section. See [RFC3031] for the specification of MPLS packets. See [RFC3032] for the specification of the MPLS label stack. The size of the exported section may be constrained due to limitations in the IPFIX protocol. The data for this field MUST NOT be padded. mplsPayloadPacketSection octetArray 317 current This Information Element, which may have a variable length, carries the first n octets from the MPLS payload of a sampled packet, being data that follows immediately after the MPLS label stack. See [RFC3031] for the specification of MPLS packets. See [RFC3032] for the specification of the MPLS label stack. The size of the exported section may be constrained due to limitations in the IPFIX protocol. The data for this field MUST NOT be padded. selectorIdTotalPktsObserved unsigned64 totalCounter 318 current This Information Element specifies the total number of packets observed by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. packets selectorIdTotalPktsSelected unsigned64 totalCounter 319 current This Information Element specifies the total number of packets selected by a Selector, for a specific value of SelectorId. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. packets absoluteError float64 quantity 320 current This Information Element specifies the maximum possible measurement error of the reported value for a given Information Element. The absoluteError has the same unit as the Information Element with which it is associated. The real value of the metric can differ by absoluteError (positive or negative) from the measured value. This Information Element provides only the error for measured values. If an Information Element contains an estimated value (from Sampling), the confidence boundaries and confidence level have to be provided instead, using the upperCILimit, lowerCILimit, and confidenceLevel Information Elements. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. The units of the Information Element for which the error is specified. relativeError float64 quantity 321 current This Information Element specifies the maximum possible positive or negative error ratio for the reported value for a given Information Element as percentage of the measured value. The real value of the metric can differ by relativeError percent (positive or negative) from the measured value. This Information Element provides only the error for measured values. If an Information Element contains an estimated value (from Sampling), the confidence boundaries and confidence level have to be provided instead, using the upperCILimit, lowerCILimit, and confidenceLevel Information Elements. This Information Element should be used in an Options Template scoped to the observation to which it refers. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. observationTimeSeconds dateTimeSeconds quantity 322 current This Information Element specifies the absolute time in seconds of an observation. seconds observationTimeMilliseconds dateTimeMilliseconds quantity 323 current This Information Element specifies the absolute time in milliseconds of an observation. milliseconds observationTimeMicroseconds dateTimeMicroseconds quantity 324 current This Information Element specifies the absolute time in microseconds of an observation. microseconds observationTimeNanoseconds dateTimeNanoseconds quantity 325 current This Information Element specifies the absolute time in nanoseconds of an observation. nanoseconds digestHashValue unsigned64 quantity 326 current This Information Element specifies the value from the digest hash function. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashIPPayloadOffset unsigned64 quantity 327 current This Information Element specifies the IP payload offset used by a Hash-based Selection Selector. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashIPPayloadSize unsigned64 quantity 328 current This Information Element specifies the IP payload size used by a Hash-based Selection Selector. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashOutputRangeMin unsigned64 quantity 329 current This Information Element specifies the value for the beginning of a hash function's potential output range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashOutputRangeMax unsigned64 quantity 330 current This Information Element specifies the value for the end of a hash function's potential output range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashSelectedRangeMin unsigned64 quantity 331 current This Information Element specifies the value for the beginning of a hash function's selected range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashSelectedRangeMax unsigned64 quantity 332 current This Information Element specifies the value for the end of a hash function's selected range. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashDigestOutput boolean quantity 333 current This Information Element contains a boolean value that is TRUE if the output from this hash Selector has been configured to be included in the packet report as a packet digest, else FALSE. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. hashInitialiserValue unsigned64 quantity 334 current This Information Element specifies the initialiser value to the hash function. See also Sections 6.2, 3.8 and 7.1 of [RFC5475]. selectorName string 335 current The name of a selector identified by a selectorID. Globally unique per Metering Process. upperCILimit float64 quantity 336 current This Information Element specifies the upper limit of a confidence interval. It is used to provide an accuracy statement for an estimated value. The confidence limits define the range in which the real value is assumed to be with a certain probability p. Confidence limits always need to be associated with a confidence level that defines this probability p. Please note that a confidence interval only provides a probability that the real value lies within the limits. That means the real value can lie outside the confidence limits. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together. lowerCILimit float64 quantity 337 current This Information Element specifies the lower limit of a confidence interval. For further information, see the description of upperCILimit. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together. confidenceLevel float64 quantity 338 current This Information Element specifies the confidence level. It is used to provide an accuracy statement for estimated values. The confidence level provides the probability p with which the real value lies within a given range. A confidence level always needs to be associated with confidence limits that define the range in which the real value is assumed to be. The upperCILimit, lowerCILimit, and confidenceLevel Information Elements should all be used in an Options Template scoped to the observation to which they refer. See Section 3.4.2.1 of the IPFIX protocol document [RFC5101]. Note that the upperCILimit, lowerCILimit, and confidenceLevel are all required to specify confidence, and should be disregarded unless all three are specified together. informationElementDataType unsigned8 339 current A description of the abstract data type of an IPFIX information element.These are taken from the abstract data types defined in section 3.1 of the IPFIX Information Model [RFC5102]; see that section for more information on the types described in the informationElementDataType sub-registry. These types are registered in the IANA IPFIX Information Element Data Type subregistry. This subregistry is intended to assign numbers for type names, not to provide a mechanism for adding data types to the IPFIX Protocol, and as such requires a Standards Action [RFC5226] to modify. informationElementDescription string 340 current A UTF-8 [RFC3629] encoded Unicode string containing a human-readable description of an Information Element. The content of the informationElementDescription MAY be annotated with one or more language tags [RFC4646], encoded in-line [RFC2482] within the UTF-8 string, in order to specify the language in which the description is written. Description text in multiple languages MAY tag each section with its own language tag; in this case, the description information in each language SHOULD have equivalent meaning. In the absence of any language tag, the "i-default" [RFC2277] language SHOULD be assumed. See the Security Considerations section for notes on string handling for Information Element type records. informationElementName string 341 current A UTF-8 [RFC3629] encoded Unicode string containing the name of an Information Element, intended as a simple identifier. See the Security Considerations section for notes on string handling for Information Element type records informationElementRangeBegin unsigned64 quantity 342 current Contains the inclusive low end of the range of acceptable values for an Information Element. informationElementRangeEnd unsigned64 quantity 343 current Contains the inclusive high end of the range of acceptable values for an Information Element. informationElementSemantics unsigned8 344 current A description of the semantics of an IPFIX Information Element. These are taken from the data type semantics defined in section 3.2 of the IPFIX Information Model [RFC5102]; see that section for more information on the types defined in the informationElementSemantics sub-registry. This field may take the values in Table ; the special value 0x00 (default) is used to note that no semantics apply to the field; it cannot be manipulated by a Collecting Process or File Reader that does not understand it a priori. These semantics are registered in the IANA IPFIX Information Element Semantics subregistry. This subregistry is intended to assign numbers for semantics names, not to provide a mechanism for adding semantics to the IPFIX Protocol, and as such requires a Standards Action [RFC5226] to modify. informationElementUnits unsigned16 345 current A description of the units of an IPFIX Information Element. These correspond to the units implicitly defined in the Information Element definitions in section 5 of the IPFIX Information Model [RFC5102]; see that section for more information on the types described in the informationElementsUnits sub-registry. This field may take the values in Table 3 below; the special value 0x00 (none) is used to note that the field is unitless. These types are registered in the IANA IPFIX Information Element Units subregistry; new types may be added on a First Come First Served [RFC5226] basis. privateEnterpriseNumber unsigned32 identifier 346 current A private enterprise number, as assigned by IANA. Within the context of an Information Element Type record, this element can be used along with the informationElementId element to scope properties to a specific Information Element. To export type information about an IANA-assigned Information Element, set the privateEnterpriseNumber to 0, or do not export the privateEnterpriseNumber in the type record. To export type information about an enterprise-specific Information Element, export the enterprise number in privateEnterpriseNumber, and export the Information Element number with the Enterprise bit cleared in informationElementId. The Enterprise bit in the associated informationElementId Information Element MUST be ignored by the Collecting Process. virtualStationInterfaceId octetArray identifier 347 current Instance Identifier of the interface to a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host. See IEEE 802.1Qbg for the definition of Virtual Station Interface ID. virtualStationInterfaceName string 348 current Name of the interface to a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host. See IEEE 802.1Qbg for the definition of Virtual Station Interface. virtualStationUUID octetArray identifier 349 current Unique Identifier of a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host. See IEEE 802.1Qbg for the definition of Virtual Station. virtualStationName string 350 current Name of a Virtual Station. A Virtual Station is an end station instance: it can be a virtual machine or a physical host. See IEEE 802.1Qbg for the definition of Virtual Station. layer2SegmentId unsigned64 identifier 351 current Identifier of a layer 2 network segment in an overlay network. The most significant byte identifies the layer 2 network overlay network encapsulation type: 0x00 reserved 0x01 VxLAN 0x02 NVGRE The three lowest significant bytes hold the value of the layer 2 overlay network segment identifier. For example: - a 24 bit segment ID VXLAN Network Identifier (VNI) - a 24 bit Tenant Network Identifier (TNI) for NVGRE See VxLAN RFC at http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-00 See NVGRE RFC at http://tools.ietf.org/html/draft-sridharan-virtualization-nvgre-00 layer2OctetDeltaCount unsigned64 deltaCounter 352 current The number of layer 2 octets since the previous report (if any) in incoming packets for this Flow at the Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. # memo: layer 2 version of octetDeltaCount (field #1) octets layer2OctetTotalCount unsigned64 totalCounter 353 current The total number of layer 2 octets in incoming packets for this Flow at the Observation Point since the Metering Process (re-)initialization for this Observation Point. The number of octets includes layer 2 header(s) and layer 2 payload. # memo: layer 2 version of octetTotalCount (field #85) octets ingressUnicastPacketTotalCount unsigned64 totalCounter 354 current The total number of incoming unicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets ingressMulticastPacketTotalCount unsigned64 totalCounter 355 current The total number of incoming multicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets ingressBroadcastPacketTotalCount unsigned64 totalCounter 356 current The total number of incoming broadcast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets egressUnicastPacketTotalCount unsigned64 totalCounter 357 current The total number of incoming unicast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets egressBroadcastPacketTotalCount unsigned64 totalCounter 358 current The total number of incoming broadcast packets metered at the Observation Point since the Metering Process (re-)initialization for this Observation Point. packets monitoringIntervalStartMilliSeconds dateTimeMilliseconds 359 current The absolute timestamp at which the monitoring interval started. A Monitoring interval is the period of time during which the Metering Process is running. milliseconds monitoringIntervalEndMilliSeconds dateTimeMilliseconds 360 current The absolute timestamp at which the monitoring interval ended. A Monitoring interval is the period of time during which the Metering Process is running. milliseconds portRangeStart unsigned16 identifier 361 current The port number identifying the start of a range of ports. A value of zero indicates that the range start is not specified, ie the range is defined in some other way. Additional information on defined TCP port numbers can be found at http://www.iana.org/assignments/service-names-port-numbers. portRangeEnd unsigned16 identifier 362 current The port number identifying the end of a range of ports. A value of zero indicates that the range end is not specified, ie the range is defined in some other way. Additional information on defined TCP port numbers can be found at http://www.iana.org/assignments/service-names-port-numbers. portRangeStepSize unsigned16 identifier 363 current The step size in a port range. The default step size is 1, which indicates contiguous ports. A value of zero indicates that the step size is not specified, ie the range is defined in some other way. portRangeNumPorts unsigned16 identifier 364 current The number of ports in a port range. A value of zero indicates that the number of ports is not specified, ie the range is defined in some other way. staMacAddress macAddress identifier 365 current The IEEE 802 MAC address of a wireless station (STA). See section 1.4 of RFC5415 for the definition of STA. staIPv4Address ipv4Address identifier 366 current The IPv4 address of a wireless station (STA). See section 1.4 of RFC5415 for the definition of STA. wtpMacAddress macAddress identifier 367 current The IEEE 802 MAC address of a wireless access point (WTP). See section 1.4 of RFC5415 for the definition of WTP. ingressInterfaceType unsigned32 identifier 368 current The type of interface where packets of this Flow are being received. The value matches the value of managed object 'ifType' as defined in http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib egressInterfaceType unsigned32 identifier 369 current The type of interface where packets of this Flow are being sent. The value matches the value of managed object 'ifType' as defined in http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib rtpSequenceNumber unsigned16 370 current The RTP sequence number per RFC3550. userName string 371 current User name associated with the flow. applicationCategoryName string 372 current An attribute that provides a first level categorization for each Application ID. applicationSubCategoryName string 373 current An attribute that provides a second level categorization for each Application ID. applicationGroupName string 374 current An attribute that groups multiple Application IDs that belong to the same networking application. originalFlowsPresent unsigned64 deltaCounter 375 current The non-conservative count of Original Flows contributing to this Aggregated Flow. Non-conservative counts need not sum to the original count on re-aggregation. originalFlowsInitiated unsigned64 deltaCounter 376 current The conservative count of Original Flows whose first packet is represented within this Aggregated Flow. Conservative counts must sum to the original count on re-aggregation. originalFlowsCompleted unsigned64 deltaCounter 377 current The conservative count of Original Flows whose last packet is represented within this Aggregated Flow. Conservative counts must sum to the original count on re-aggregation. distinctCountOfSourceIPAddress unsigned64 totalCounter 378 current The count of distinct source IP address values for Original Flows contributing to this Aggregated Flow, without regard to IP version. This Information Element is preferred to the IP-version-specific counters, unless it is important to separate the counts by version. distinctCountOfDestinationIPAddress unsigned64 totalCounter 379 current The count of distinct destination IP address values for Original Flows contributing to this Aggregated Flow, without regard to IP version. This Information Element is preferred to the version-specific counters below, unless it is important to separate the counts by version. distinctCountOfSourceIPv4Address unsigned32 totalCounter 380 current The count of distinct source IPv4 address values for Original Flows contributing to this Aggregated Flow. distinctCountOfDestinationIPv4Address unsigned32 totalCounter 381 current The count of distinct destination IPv4 address values for Original Flows contributing to this Aggregated Flow. distinctCountOfSourceIPv6Address unsigned64 totalCounter 382 current The count of distinct source IPv6 address values for Original Flows contributing to this Aggregated Flow. distinctCountOfDestinationIPv6Address unsigned64 totalCounter 383 current The count of distinct destination IPv6 address values for Original Flows contributing to this Aggregated Flow. valueDistributionMethod unsigned8 384 current A description of the method used to distribute the counters from Contributing Flows into the Aggregated Flow records described by an associated scope, generally a Template. The method is deemed to apply to all the non-key Information Elements in the referenced scope for which value distribution is a valid operation; if the originalFlowsInitiated and/or originalFlowsCompleted Information Elements appear in the Template, they are not subject to this distribution method, as they each infer their own distribution method. This is intended to be a complete set of possible value distribution methods; it is encoded as follows: +-------+-----------------------------------------------------------+ | Value | Description | +-------+-----------------------------------------------------------+ | 0 | Unspecified: The counters for an Original Flow are | | | explicitly not distributed according to any other method | | | defined for this Information Element; use for arbitrary | | | distribution, or distribution algorithms not described by | | | any other codepoint. | | | --------------------------------------------------------- | | | | | 1 | Start Interval: The counters for an Original Flow are | | | added to the counters of the appropriate Aggregated Flow | | | containing the start time of the Original Flow. This | | | should be assumed the default if value distribution | | | information is not available at a Collecting Process for | | | an Aggregated Flow. | | | --------------------------------------------------------- | | | | | 2 | End Interval: The counters for an Original Flow are added | | | to the counters of the appropriate Aggregated Flow | | | containing the end time of the Original Flow. | | | --------------------------------------------------------- | | | | | 3 | Mid Interval: The counters for an Original Flow are added | | | to the counters of a single appropriate Aggregated Flow | | | containing some timestamp between start and end time of | | | the Original Flow. | | | --------------------------------------------------------- | | | | | 4 | Simple Uniform Distribution: Each counter for an Original | | | Flow is divided by the number of time intervals the | | | Original Flow covers (i.e., of appropriate Aggregated | | | Flows sharing the same Flow Key), and this number is | | | added to each corresponding counter in each Aggregated | | | Flow. | | | --------------------------------------------------------- | | | | | 5 | Proportional Uniform Distribution: Each counter for an | | | Original Flow is divided by the number of time units the | | | Original Flow covers, to derive a mean count rate. This | | | mean count rate is then multiplied by the number of time | | | units in the intersection of the duration of the Original | | | Flow and the time interval of each Aggregated Flow. This | | | is like simple uniform distribution, but accounts for the | | | fractional portions of a time interval covered by an | | | Original Flow in the first and last time interval. | | | --------------------------------------------------------- | | | | | 6 | Simulated Process: Each counter of the Original Flow is | | | distributed among the intervals of the Aggregated Flows | | | according to some function the Intermediate Aggregation | | | Process uses based upon properties of Flows presumed to | | | be like the Original Flow. This is essentially an | | | assertion that the Intermediate Aggregation Process has | | | no direct packet timing information but is nevertheless | | | not using one of the other simpler distribution methods. | | | The Intermediate Aggregation Process specifically makes | | | no assertion as to the correctness of the simulation. | | | --------------------------------------------------------- | | | | | 7 | Direct: The Intermediate Aggregation Process has access | | | to the original packet timings from the packets making up | | | the Original Flow, and uses these to distribute or | | | recalculate the counters. | +-------+-----------------------------------------------------------+ rfc3550JitterMeanMilliseconds unsigned32 quantity 385 current Interarrival jitter as defined in section 6.4.1 of , measured in milliseconds. milliseconds rfc3550JitterMeanMicroseconds unsigned32 quantity 386 current Interarrival jitter as defined in section 6.4.1 of , measured in microseconds. microseconds rfc3550JitterMeanNanoseconds unsigned32 quantity 387 current Interarrival jitter as defined in section 6.4.1 of , measured in nanoseconds. nanoseconds 388-32767 IPFIX MPLS label type (Value 46) Expert Review Primary expert - Nevil Brownlee and Secondary expert - Juergen Quittek 1 TE-MIDPT: Any TE tunnel mid-point or tail label 2 Pseudowire: Any PWE3 or Cisco AToM based label 3 VPN: Any label associated with VPN 4 BGP: Any label associated with BGP or BGP routing 5 LDP: Any label associated with dynamically assigned labels using LDP 6-255 Unassigned Classification Engine IDs (Value 101) Expert Review Primary expert - Nevil Brownlee and Secondary expert - Juergen Quittek 0 Invalid. 1 IANA-L3: The Assigned Internet Protocol Number (layer 3 (L3)) is exported in the Selector ID. See http://www.iana.org/assignments/protocol-numbers. 1 2 PANA-L3: Proprietary layer 3 definition. An enterprise can export its own layer 3 protocol numbers. The Selector ID has a global significance for all devices from the same enterprise. 1 3 IANA-L4: The IANA layer 4 (L4) well-known port number is exported in the Selector ID. See [http://www.iana.org/assignments/service-names-port-numbers]. Note: as an IPFIX flow is unidirectional, it contains the destination port in a flow from the client to the server. 2 4 PANA-L4: Proprietary layer 4 definition. An enterprise can export its own layer 4 port numbers. The Selector ID has global significance for devices from the same enterprise. Example: IPFIX had the port 4739 pre-assigned in the IETF draft for years. While waiting for the RFC and its associated IANA registration, the Selector ID 4739 was used with this PANA-L4. 2 5 Reserved 6 USER-Defined: The Selector ID represents applications defined by the user (using CLI, GUI, etc.) based on the methods described in section 2. The Selector ID has a local significance per device. 3 7 Reserved 8 Reserved 9 Reserved 10 Reserved 11 Reserved 12 PANA-L2: Proprietary layer 2 (L2) definition. An enterprise can export its own layer 2 identifiers. The Selector ID represents the enterprise's unique global layer 2 applications. The Selector ID has a global significance for all devices from the same enterprise. Examples include Cisco Subnetwork Access Protocol (SNAP). 5 13 PANA-L7: Proprietary layer 7 definition. The Selector ID represents the enterprise's unique global ID for the layer 7 applications. The Selector ID has a global significance for all devices from the same enterprise. This Classification Engine Id is used when the application registry is owned by the Exporter manufacturer (referred to as the "enterprise" in this document). 3 14 Reserved 15 Reserved 16 Reserved 17 Reserved 18 ETHERTYPE: The Selector ID represents the well- known Ethertype. See http://standards.ieee.org/develop/regauth/ethertype/eth.txt. Note that the Ethertype is usually expressed in hexadecimal. However, the corresponding decimal value is used in this Selector ID. 2 19 LLC: The Selector ID represents the well-known IEEE 802.2 Link Layer Control (LLC) Destination Service Access Point (DSAP). See http://standards.ieee.org/develop/regauth/llc/public.html. Note that LLC DSAP is usually expressed in hexadecimal. However, the corresponding decimal value is used in this Selector ID. 1 20 PANA-L7-PEN: Proprietary layer 7 definition, including a Private Enterprise Number (PEN) [http://www.iana.org/assignments/enterprise-numbers] to identify that the application registry being used is not owned by the Exporter manufacturer or to identify the original enterprise in the case of a mediator or 3rd party device. The Selector ID represents the enterprise unique global ID for the layer 7 applications. The Selector ID has a global significance for all devices from the same enterprise. 3 IPFIX Version Numbers Standards Action 0 Reserved 1-8 Reserved (historic) 9 Cisco Systems NetFlow Version 9 (historic) 10 IPFIX as documented in RFC5101 11-65535 Unassigned IPFIX Set IDs Standards Action 0-1 Not used (historic) 2 Template Set 3 Option Template Set 4-255 Unassigned 256-65535 Reserved for Data Sets IPFIX Information Element Data Types Standards Action 0 octetArray 1 unsigned8 2 unsigned16 3 unsigned32 4 unsigned64 5 signed8 6 signed16 7 signed32 8 signed64 9 float32 10 float64 11 boolean 12 macAddress 13 string 14 dateTimeSeconds 15 dateTimeMilliseconds 16 dateTimeMicroseconds 17 dateTimeNanoseconds 18 ipv4Address 19 ipv6Address 20 basicList 21 subTemplateList 22 subTemplateMultiList 23-255 Unassigned IPFIX Information Element Semantics Standards Action 0 default 1 quantity 2 totalCounter 3 deltaCounter 4 identifier 5 flags 6 list 7-255 Unassigned IPFIX Information Element Units Expert Review 0 none 1 bits 2 octets 3 packets 4 flows 5 seconds 6 milliseconds 7 microseconds 8 nanoseconds 9 4-octet words for IPv4 header length 10 messages for reliability reporting 11 hops for TTL 12 entries for MPLS label stack 13-65535 Unassigned IPFIX Structured Data Types Semantics Standards Action 0x00 noneOf The "noneOf" structured data type semantic specifies that none of the elements are actual properties of the Data Record. 0x01 exactlyOneOf The "exactlyOneOf" structured data type semantic specifies that only a single element from the structured data is an actual property of the Data Record. This is equivalent to a logical XOR operation. 0x02 oneOrMoreOf The "oneOrMoreOf" structured data type semantic specifies that one or more elements from the list in the structured data are actual properties of the Data Record. This is equivalent to a logical OR operation. 0x03 allOf The "allOf" structured data type semantic specifies that all of the list elements from the structured data are actual properties of the Data Record. 0x04 ordered The "ordered" structured data type semantic specifies that elements from the list in the structured data are ordered. 0x05-0xFE unassigned 0xFF undefined The "undefined" structured data type semantic specifies that the semantic of the list elements is not specified and that, if a semantic exists, then it is up to the Collecting Process to draw its own conclusions. The "undefined" structured data type semantic is the default structured data type semantic. ipfix-iana at cisco.com mailto:ipfix-iana&cisco.com 2012-09-20 openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-unixctl.man0000644000000000000000000000012713534540071021211 xustar0029 mtime=1567801401.65368302 28 atime=1567801402.1136864 30 ctime=1567801423.753845848 openvswitch-2.5.9/ofproto/ofproto-unixctl.man0000644000175000017500000001270213534540071022675 0ustar00jpettitjpettit00000000000000.SS "OFPROTO COMMANDS" These commands manage the core OpenFlow switch implementation (called \fBofproto\fR). . .IP "\fBofproto/list\fR" Lists the names of the running ofproto instances. These are the names that may be used on \fBofproto/trace\fR. . .IP "\fBofproto/trace\fR [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \fIpacket\fR]" .IQ "\fBofproto/trace\fR \fIbridge\fR \fIbr_flow\fR [\fB\-generate \fR| \fIpacket\fR]" .IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \fIpacket\fR] \fIactions\fR" .IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] \fIbridge\fR \fIbr_flow\fR [\fB\-generate \fR| \fIpacket\fR] \fIactions\fR" Traces the path of an imaginary packet through \fIswitch\fR and reports the path that it took. The initial treatment of the packet varies based on the command: . .RS .IP \(bu \fBofproto/trace\fR looks the packet up in the OpenFlow flow table, as if the packet had arrived on an OpenFlow port. . .IP \(bu \fBofproto/trace\-packet\-out\fR applies the specified OpenFlow \fIactions\fR, as if the packet, flow, and actions had been specified in an OpenFlow ``packet-out'' request. .RE . .IP The packet's headers (e.g. source and destination) and metadata (e.g. input port), together called its ``flow,'' are usually all that matter for the purpose of tracing a packet. You can specify the flow in the following ways: . .RS .IP "\fIdpname\fR \fIodp_flow\fR" \fIodp_flow\fR is a flow in the form printed by \fBovs\-dpctl\fR(8)'s \fBdump\-flows\fR command. If all of your bridges have the same type, which is the common case, then you can omit \fIdpname\fR, but if you have bridges of different types (say, both \fBovs-netdev\fR and \fBovs-system\fR), then you need to specify a \fIdpname\fR to disambiguate. . .IP "\fIbridge\fR \fIbr_flow\fR" \fIbr_flow\fR is a flow in the form similar to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR command. (This is not an OpenFlow flow: besides other differences, it never contains wildcards.) \fIbridge\fR names of the bridge through which \fIbr_flow\fR should be traced. .RE . .IP Most commonly, one specifies only a flow, using one of the forms above, but sometimes one might need to specify an actual packet instead of just a flow: . .RS .IP "Side effects." Some actions have side effects. For example, the \fBnormal\fR action can update the MAC learning table, and the \fBlearn\fR action can change OpenFlow tables. The trace commands only perform side effects when a packet is specified. If you want side effects to take place, then you must supply a packet. . .IP (Output actions are obviously side effects too, but the trace commands never execute them, even when one specifies a packet.) . .IP "Incomplete information." Most of the time, Open vSwitch can figure out everything about the path of a packet using just the flow, but in some special circumstances it needs to look at parts of the packet that are not included in the flow. When this is the case, and you do not supply a packet, then a trace command will tell you it needs a packet. .RE . .IP If you wish to include a packet as part of a trace operation, there are two ways to do it: . .RS .IP \fB\-generate\fR This option, added to one of the ways to specify a flow already described, causes Open vSwitch to internally generate a packet with the flow described and then to use that packet. If your goal is to execute side effects, then \fB\-generate\fR is the easiest way to do it, but \fB\-generate\fR is not a good way to fill in incomplete information, because it generates packets based on only the flow information, which means that the packets really do not have any more information than the flow. . .IP \fIpacket\fR This form supplies an explicit \fIpacket\fR as a sequence of hex digits. An Ethernet frame is at least 14 bytes long, so there must be at least 28 hex digits. Obviously, it is inconvenient to type in the hex digits by hand, so the \fBovs\-pcap\fR(1) and \fBovs\-tcpundump\fR(1) utilities provide easier ways. .IP With this form, packet headers are extracted directly from \fIpacket\fR, so the \fIodp_flow\fR or \fIbr_flow\fR should specify only metadata. The metadata can be: .RS .IP \fIskb_priority\fR Packet QoS priority. .IP \fIpkt_mark\fR Mark of the packet. .IP \fIct_state\fR Connection state of the packet. .IP \fIct_zone\fR Connection tracking zone for packet. .IP \fIct_mark\fR Connection mark of the packet. .IP \fIct_label\fR Connection label of the packet. .IP \fItun_id\fR The tunnel ID on which the packet arrived. .IP \fIin_port\fR The port on which the packet arrived. .RE .RE . .IP The in_port value is kernel datapath port number for the first format and OpenFlow port number for the second format. The numbering of these two types of port usually differs and there is no relationship. . .IP \fBofproto\-trace\-packet\-out\fR accepts an additional \fB\-consistent\fR option. With this option specified, the command rejects \fIactions\fR that are inconsistent with the specified packet. (An example of an inconsistency is attempting to strip the VLAN tag from a packet that does not have a VLAN tag.) Open vSwitch ignores most forms of inconsistency in OpenFlow 1.0 and rejects inconsistencies in later versions of OpenFlow. The option is necessary because the command does not ordinarily imply a particular OpenFlow version. One exception is that, when \fIactions\fR includes an action that only OpenFlow 1.1 and later supports (such as \fBpush_vlan\fR), \fB\-consistent\fR is automatically enabled. openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-mirror.c0000644000000000000000000000013113534540071021417 xustar0030 mtime=1567801401.629682844 29 atime=1567801402.10568634 30 ctime=1567801425.037855313 openvswitch-2.5.9/ofproto/ofproto-dpif-mirror.c0000644000175000017500000003324113534540071023111 0ustar00jpettitjpettit00000000000000/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto-dpif-mirror.h" #include #include "hmap.h" #include "hmapx.h" #include "ofproto.h" #include "vlan-bitmap.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofproto_dpif_mirror); #define MIRROR_MASK_C(X) UINT32_C(X) BUILD_ASSERT_DECL(sizeof(mirror_mask_t) * CHAR_BIT >= MAX_MIRRORS); struct mbridge { struct mirror *mirrors[MAX_MIRRORS]; struct hmap mbundles; bool need_revalidate; bool has_mirrors; struct ovs_refcount ref_cnt; }; struct mbundle { struct hmap_node hmap_node; /* In parent 'mbridge' map. */ struct ofbundle *ofbundle; mirror_mask_t src_mirrors; /* Mirrors triggered when packet received. */ mirror_mask_t dst_mirrors; /* Mirrors triggered when packet sent. */ mirror_mask_t mirror_out; /* Mirrors that output to this mbundle. */ }; struct mirror { struct mbridge *mbridge; /* Owning ofproto. */ size_t idx; /* In ofproto's "mirrors" array. */ void *aux; /* Key supplied by ofproto's client. */ /* Selection criteria. */ struct hmapx srcs; /* Contains "struct mbundle*"s. */ struct hmapx dsts; /* Contains "struct mbundle*"s. */ unsigned long *vlans; /* Bitmap of chosen VLANs, NULL selects all. */ /* Output (exactly one of out == NULL and out_vlan == -1 is true). */ struct mbundle *out; /* Output port or NULL. */ int out_vlan; /* Output VLAN or -1. */ mirror_mask_t dup_mirrors; /* Bitmap of mirrors with the same output. */ /* Counters. */ int64_t packet_count; /* Number of packets sent. */ int64_t byte_count; /* Number of bytes sent. */ }; static struct mirror *mirror_lookup(struct mbridge *, void *aux); static struct mbundle *mbundle_lookup(const struct mbridge *, struct ofbundle *); static void mbundle_lookup_multiple(const struct mbridge *, struct ofbundle **, size_t n_bundles, struct hmapx *mbundles); static int mirror_scan(struct mbridge *); static void mirror_update_dups(struct mbridge *); struct mbridge * mbridge_create(void) { struct mbridge *mbridge; mbridge = xzalloc(sizeof *mbridge); ovs_refcount_init(&mbridge->ref_cnt); hmap_init(&mbridge->mbundles); return mbridge; } struct mbridge * mbridge_ref(const struct mbridge *mbridge_) { struct mbridge *mbridge = CONST_CAST(struct mbridge *, mbridge_); if (mbridge) { ovs_refcount_ref(&mbridge->ref_cnt); } return mbridge; } void mbridge_unref(struct mbridge *mbridge) { struct mbundle *mbundle, *next; size_t i; if (!mbridge) { return; } if (ovs_refcount_unref(&mbridge->ref_cnt) == 1) { for (i = 0; i < MAX_MIRRORS; i++) { if (mbridge->mirrors[i]) { mirror_destroy(mbridge, mbridge->mirrors[i]->aux); } } HMAP_FOR_EACH_SAFE (mbundle, next, hmap_node, &mbridge->mbundles) { mbridge_unregister_bundle(mbridge, mbundle->ofbundle); } hmap_destroy(&mbridge->mbundles); free(mbridge); } } bool mbridge_has_mirrors(struct mbridge *mbridge) { return mbridge ? mbridge->has_mirrors : false; } /* Returns true if configurations changes in 'mbridge''s mirrors require * revalidation, and resets the revalidation flag to false. */ bool mbridge_need_revalidate(struct mbridge *mbridge) { bool need_revalidate = mbridge->need_revalidate; mbridge->need_revalidate = false; return need_revalidate; } void mbridge_register_bundle(struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle; mbundle = xzalloc(sizeof *mbundle); mbundle->ofbundle = ofbundle; hmap_insert(&mbridge->mbundles, &mbundle->hmap_node, hash_pointer(ofbundle, 0)); } void mbridge_unregister_bundle(struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle); size_t i; if (!mbundle) { return; } for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *m = mbridge->mirrors[i]; if (m) { if (m->out == mbundle) { mirror_destroy(mbridge, m->aux); } else if (hmapx_find_and_delete(&m->srcs, mbundle) || hmapx_find_and_delete(&m->dsts, mbundle)) { mbridge->need_revalidate = true; } } } hmap_remove(&mbridge->mbundles, &mbundle->hmap_node); free(mbundle); } mirror_mask_t mirror_bundle_out(struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle); return mbundle ? mbundle->mirror_out : 0; } mirror_mask_t mirror_bundle_src(struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle); return mbundle ? mbundle->src_mirrors : 0; } mirror_mask_t mirror_bundle_dst(struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundle); return mbundle ? mbundle->dst_mirrors : 0; } int mirror_set(struct mbridge *mbridge, void *aux, const char *name, struct ofbundle **srcs, size_t n_srcs, struct ofbundle **dsts, size_t n_dsts, unsigned long *src_vlans, struct ofbundle *out_bundle, uint16_t out_vlan) { struct mbundle *mbundle, *out; mirror_mask_t mirror_bit; struct mirror *mirror; struct hmapx srcs_map; /* Contains "struct ofbundle *"s. */ struct hmapx dsts_map; /* Contains "struct ofbundle *"s. */ mirror = mirror_lookup(mbridge, aux); if (!mirror) { int idx; idx = mirror_scan(mbridge); if (idx < 0) { VLOG_WARN("maximum of %d port mirrors reached, cannot create %s", MAX_MIRRORS, name); return EFBIG; } mirror = mbridge->mirrors[idx] = xzalloc(sizeof *mirror); mirror->mbridge = mbridge; mirror->idx = idx; mirror->aux = aux; mirror->out_vlan = -1; } /* Get the new configuration. */ if (out_bundle) { out = mbundle_lookup(mbridge, out_bundle); if (!out) { mirror_destroy(mbridge, mirror->aux); return EINVAL; } out_vlan = -1; } else { out = NULL; } mbundle_lookup_multiple(mbridge, srcs, n_srcs, &srcs_map); mbundle_lookup_multiple(mbridge, dsts, n_dsts, &dsts_map); /* If the configuration has not changed, do nothing. */ if (hmapx_equals(&srcs_map, &mirror->srcs) && hmapx_equals(&dsts_map, &mirror->dsts) && vlan_bitmap_equal(mirror->vlans, src_vlans) && mirror->out == out && mirror->out_vlan == out_vlan) { hmapx_destroy(&srcs_map); hmapx_destroy(&dsts_map); return 0; } hmapx_swap(&srcs_map, &mirror->srcs); hmapx_destroy(&srcs_map); hmapx_swap(&dsts_map, &mirror->dsts); hmapx_destroy(&dsts_map); free(mirror->vlans); mirror->vlans = vlan_bitmap_clone(src_vlans); mirror->out = out; mirror->out_vlan = out_vlan; /* Update mbundles. */ mirror_bit = MIRROR_MASK_C(1) << mirror->idx; HMAP_FOR_EACH (mbundle, hmap_node, &mirror->mbridge->mbundles) { if (hmapx_contains(&mirror->srcs, mbundle)) { mbundle->src_mirrors |= mirror_bit; } else { mbundle->src_mirrors &= ~mirror_bit; } if (hmapx_contains(&mirror->dsts, mbundle)) { mbundle->dst_mirrors |= mirror_bit; } else { mbundle->dst_mirrors &= ~mirror_bit; } if (mirror->out == mbundle) { mbundle->mirror_out |= mirror_bit; } else { mbundle->mirror_out &= ~mirror_bit; } } mbridge->has_mirrors = true; mirror_update_dups(mbridge); return 0; } void mirror_destroy(struct mbridge *mbridge, void *aux) { struct mirror *mirror = mirror_lookup(mbridge, aux); mirror_mask_t mirror_bit; struct mbundle *mbundle; int i; if (!mirror) { return; } mirror_bit = MIRROR_MASK_C(1) << mirror->idx; HMAP_FOR_EACH (mbundle, hmap_node, &mbridge->mbundles) { mbundle->src_mirrors &= ~mirror_bit; mbundle->dst_mirrors &= ~mirror_bit; mbundle->mirror_out &= ~mirror_bit; } hmapx_destroy(&mirror->srcs); hmapx_destroy(&mirror->dsts); free(mirror->vlans); mbridge->mirrors[mirror->idx] = NULL; free(mirror); mirror_update_dups(mbridge); mbridge->has_mirrors = false; for (i = 0; i < MAX_MIRRORS; i++) { if (mbridge->mirrors[i]) { mbridge->has_mirrors = true; break; } } } int mirror_get_stats(struct mbridge *mbridge, void *aux, uint64_t *packets, uint64_t *bytes) { struct mirror *mirror = mirror_lookup(mbridge, aux); if (!mirror) { *packets = *bytes = UINT64_MAX; return 0; } *packets = mirror->packet_count; *bytes = mirror->byte_count; return 0; } void mirror_update_stats(struct mbridge *mbridge, mirror_mask_t mirrors, uint64_t packets, uint64_t bytes) { if (!mbridge || !mirrors) { return; } for (; mirrors; mirrors = zero_rightmost_1bit(mirrors)) { struct mirror *m; m = mbridge->mirrors[raw_ctz(mirrors)]; if (!m) { /* In normal circumstances 'm' will not be NULL. However, * if mirrors are reconfigured, we can temporarily get out * of sync in facet_revalidate(). We could "correct" the * mirror list before reaching here, but doing that would * not properly account the traffic stats we've currently * accumulated for previous mirror configuration. */ continue; } m->packet_count += packets; m->byte_count += bytes; } } /* Retrieves the mirror numbered 'index' in 'mbridge'. Returns true if such a * mirror exists, false otherwise. * * If successful, '*vlans' receives the mirror's VLAN membership information, * either a null pointer if the mirror includes all VLANs or a 4096-bit bitmap * in which a 1-bit indicates that the mirror includes a particular VLAN, * '*dup_mirrors' receives a bitmap of mirrors whose output duplicates mirror * 'index', '*out' receives the output ofbundle (if any), and '*out_vlan' * receives the output VLAN (if any). */ bool mirror_get(struct mbridge *mbridge, int index, const unsigned long **vlans, mirror_mask_t *dup_mirrors, struct ofbundle **out, int *out_vlan) { struct mirror *mirror; if (!mbridge) { return false; } mirror = mbridge->mirrors[index]; if (!mirror) { return false; } *vlans = mirror->vlans; *dup_mirrors = mirror->dup_mirrors; *out = mirror->out ? mirror->out->ofbundle : NULL; *out_vlan = mirror->out_vlan; return true; } /* Helpers. */ static struct mbundle * mbundle_lookup(const struct mbridge *mbridge, struct ofbundle *ofbundle) { struct mbundle *mbundle; HMAP_FOR_EACH_IN_BUCKET (mbundle, hmap_node, hash_pointer(ofbundle, 0), &mbridge->mbundles) { if (mbundle->ofbundle == ofbundle) { return mbundle; } } return NULL; } /* Looks up each of the 'n_ofbundlees' pointers in 'ofbundlees' as mbundles and * adds the ones that are found to 'mbundles'. */ static void mbundle_lookup_multiple(const struct mbridge *mbridge, struct ofbundle **ofbundles, size_t n_ofbundles, struct hmapx *mbundles) { size_t i; hmapx_init(mbundles); for (i = 0; i < n_ofbundles; i++) { struct mbundle *mbundle = mbundle_lookup(mbridge, ofbundles[i]); if (mbundle) { hmapx_add(mbundles, mbundle); } } } static int mirror_scan(struct mbridge *mbridge) { int idx; for (idx = 0; idx < MAX_MIRRORS; idx++) { if (!mbridge->mirrors[idx]) { return idx; } } return -1; } static struct mirror * mirror_lookup(struct mbridge *mbridge, void *aux) { int i; for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *mirror = mbridge->mirrors[i]; if (mirror && mirror->aux == aux) { return mirror; } } return NULL; } /* Update the 'dup_mirrors' member of each of the mirrors in 'ofproto'. */ static void mirror_update_dups(struct mbridge *mbridge) { int i; for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *m = mbridge->mirrors[i]; if (m) { m->dup_mirrors = MIRROR_MASK_C(1) << i; } } for (i = 0; i < MAX_MIRRORS; i++) { struct mirror *m1 = mbridge->mirrors[i]; int j; if (!m1) { continue; } for (j = i + 1; j < MAX_MIRRORS; j++) { struct mirror *m2 = mbridge->mirrors[j]; if (m2 && m1->out == m2->out && m1->out_vlan == m2->out_vlan) { m1->dup_mirrors |= MIRROR_MASK_C(1) << j; m2->dup_mirrors |= m1->dup_mirrors; } } } } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif-ipfix.c0000644000000000000000000000013113534540071021224 xustar0030 mtime=1567801401.629682844 29 atime=1567801402.10568634 30 ctime=1567801425.033855284 openvswitch-2.5.9/ofproto/ofproto-dpif-ipfix.c0000644000175000017500000020014113534540071022711 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto-dpif-ipfix.h" #include #include "byte-order.h" #include "collectors.h" #include "flow.h" #include "hash.h" #include "hmap.h" #include "list.h" #include "ofpbuf.h" #include "ofproto.h" #include "ofproto-dpif.h" #include "dp-packet.h" #include "packets.h" #include "poll-loop.h" #include "sset.h" #include "util.h" #include "timeval.h" #include "util.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ipfix); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER; /* Cf. IETF RFC 5101 Section 10.3.4. */ #define IPFIX_DEFAULT_COLLECTOR_PORT 4739 /* Cf. IETF RFC 5881 Setion 8. */ #define BFD_CONTROL_DEST_PORT 3784 #define BFD_ECHO_DEST_PORT 3785 /* The standard layer2SegmentId (ID 351) element is included in vDS to send * the VxLAN tunnel's VNI. It is 64-bit long, the most significant byte is * used to indicate the type of tunnel (0x01 = VxLAN, 0x02 = GRE) and the three * least significant bytes hold the value of the layer 2 overlay network * segment identifier: a 24-bit VxLAN tunnel's VNI or a 24-bit GRE tunnel's * TNI. This is not compatible with STT, as implemented in OVS, as * its tunnel IDs is 64-bit. * * Two new enterprise information elements are defined which are similar to * laryerSegmentId but support 64-bit IDs: * tunnelType (ID 891) and tunnelKey (ID 892). * * The enum dpif_ipfix_tunnel_type is to declare the types supported in the * tunnelType element. * The number of ipfix tunnel types includes two reserverd types: 0x04 and 0x06. */ enum dpif_ipfix_tunnel_type { DPIF_IPFIX_TUNNEL_UNKNOWN = 0x00, DPIF_IPFIX_TUNNEL_VXLAN = 0x01, DPIF_IPFIX_TUNNEL_GRE = 0x02, DPIF_IPFIX_TUNNEL_LISP = 0x03, DPIF_IPFIX_TUNNEL_STT = 0x04, DPIF_IPFIX_TUNNEL_IPSEC_GRE = 0x05, DPIF_IPFIX_TUNNEL_GENEVE = 0x07, NUM_DPIF_IPFIX_TUNNEL }; struct dpif_ipfix_port { struct hmap_node hmap_node; /* In struct dpif_ipfix's "tunnel_ports" hmap. */ struct ofport *ofport; /* To retrieve port stats. */ odp_port_t odp_port; enum dpif_ipfix_tunnel_type tunnel_type; uint8_t tunnel_key_length; }; struct dpif_ipfix_exporter { struct collectors *collectors; uint32_t seq_number; time_t last_template_set_time; struct hmap cache_flow_key_map; /* ipfix_flow_cache_entry. */ struct ovs_list cache_flow_start_timestamp_list; /* ipfix_flow_cache_entry. */ uint32_t cache_active_timeout; /* In seconds. */ uint32_t cache_max_flows; }; struct dpif_ipfix_bridge_exporter { struct dpif_ipfix_exporter exporter; struct ofproto_ipfix_bridge_exporter_options *options; uint32_t probability; }; struct dpif_ipfix_flow_exporter { struct dpif_ipfix_exporter exporter; struct ofproto_ipfix_flow_exporter_options *options; }; struct dpif_ipfix_flow_exporter_map_node { struct hmap_node node; struct dpif_ipfix_flow_exporter exporter; }; struct dpif_ipfix { struct dpif_ipfix_bridge_exporter bridge_exporter; struct hmap flow_exporter_map; /* dpif_ipfix_flow_exporter_map_node. */ struct hmap tunnel_ports; /* Contains "struct dpif_ipfix_port"s. * It makes tunnel port lookups faster in * sampling upcalls. */ struct ovs_refcount ref_cnt; }; #define IPFIX_VERSION 0x000a /* When using UDP, IPFIX Template Records must be re-sent regularly. * The standard default interval is 10 minutes (600 seconds). * Cf. IETF RFC 5101 Section 10.3.6. */ #define IPFIX_TEMPLATE_INTERVAL 600 /* Cf. IETF RFC 5101 Section 3.1. */ OVS_PACKED( struct ipfix_header { ovs_be16 version; /* IPFIX_VERSION. */ ovs_be16 length; /* Length in bytes including this header. */ ovs_be32 export_time; /* Seconds since the epoch. */ ovs_be32 seq_number; /* Message sequence number. */ ovs_be32 obs_domain_id; /* Observation Domain ID. */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_header) == 16); #define IPFIX_SET_ID_TEMPLATE 2 #define IPFIX_SET_ID_OPTION_TEMPLATE 3 /* Cf. IETF RFC 5101 Section 3.3.2. */ OVS_PACKED( struct ipfix_set_header { ovs_be16 set_id; /* IPFIX_SET_ID_* or valid template ID for Data Sets. */ ovs_be16 length; /* Length of the set in bytes including header. */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_set_header) == 4); /* Alternatives for templates at each layer. A template is defined by * a combination of one value for each layer. */ enum ipfix_proto_l2 { IPFIX_PROTO_L2_ETH = 0, /* No VLAN. */ IPFIX_PROTO_L2_VLAN, NUM_IPFIX_PROTO_L2 }; enum ipfix_proto_l3 { IPFIX_PROTO_L3_UNKNOWN = 0, IPFIX_PROTO_L3_IPV4, IPFIX_PROTO_L3_IPV6, NUM_IPFIX_PROTO_L3 }; enum ipfix_proto_l4 { IPFIX_PROTO_L4_UNKNOWN = 0, IPFIX_PROTO_L4_TCP_UDP_SCTP, IPFIX_PROTO_L4_ICMP, NUM_IPFIX_PROTO_L4 }; enum ipfix_proto_tunnel { IPFIX_PROTO_NOT_TUNNELED = 0, IPFIX_PROTO_TUNNELED, /* Support gre, lisp and vxlan. */ NUM_IPFIX_PROTO_TUNNEL }; /* Any Template ID > 255 is usable for Template Records. */ #define IPFIX_TEMPLATE_ID_MIN 256 /* Cf. IETF RFC 5101 Section 3.4.1. */ OVS_PACKED( struct ipfix_template_record_header { ovs_be16 template_id; ovs_be16 field_count; }); BUILD_ASSERT_DECL(sizeof(struct ipfix_template_record_header) == 4); enum ipfix_entity_id { /* standard IPFIX elements */ #define IPFIX_ENTITY(ENUM, ID, SIZE, NAME) IPFIX_ENTITY_ID_##ENUM = ID, #include "ofproto/ipfix-entities.def" /* non-standard IPFIX elements */ #define IPFIX_SET_ENTERPRISE(v) (((v) | 0x8000)) #define IPFIX_ENTERPRISE_ENTITY(ENUM, ID, SIZE, NAME, ENTERPRISE) \ IPFIX_ENTITY_ID_##ENUM = IPFIX_SET_ENTERPRISE(ID), #include "ofproto/ipfix-enterprise-entities.def" }; enum ipfix_entity_size { /* standard IPFIX elements */ #define IPFIX_ENTITY(ENUM, ID, SIZE, NAME) IPFIX_ENTITY_SIZE_##ENUM = SIZE, #include "ofproto/ipfix-entities.def" /* non-standard IPFIX elements */ #define IPFIX_ENTERPRISE_ENTITY(ENUM, ID, SIZE, NAME, ENTERPRISE) \ IPFIX_ENTITY_SIZE_##ENUM = SIZE, #include "ofproto/ipfix-enterprise-entities.def" }; enum ipfix_entity_enterprise { /* standard IPFIX elements */ #define IPFIX_ENTITY(ENUM, ID, SIZE, NAME) IPFIX_ENTITY_ENTERPRISE_##ENUM = 0, #include "ofproto/ipfix-entities.def" /* non-standard IPFIX elements */ #define IPFIX_ENTERPRISE_ENTITY(ENUM, ID, SIZE, NAME, ENTERPRISE) \ IPFIX_ENTITY_ENTERPRISE_##ENUM = ENTERPRISE, #include "ofproto/ipfix-enterprise-entities.def" }; OVS_PACKED( struct ipfix_template_field_specifier { ovs_be16 element_id; /* IPFIX_ENTITY_ID_*. */ ovs_be16 field_length; /* Length of the field's value, in bytes. * For Variable-Length element, it should be 65535. */ ovs_be32 enterprise; /* Enterprise number */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_template_field_specifier) == 8); /* Cf. IETF RFC 5102 Section 5.11.6. */ enum ipfix_flow_direction { INGRESS_FLOW = 0x00, EGRESS_FLOW = 0x01 }; /* Part of data record flow key for common metadata and Ethernet entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_common { ovs_be32 observation_point_id; /* OBSERVATION_POINT_ID */ uint8_t flow_direction; /* FLOW_DIRECTION */ struct eth_addr source_mac_address; /* SOURCE_MAC_ADDRESS */ struct eth_addr destination_mac_address; /* DESTINATION_MAC_ADDRESS */ ovs_be16 ethernet_type; /* ETHERNET_TYPE */ uint8_t ethernet_header_length; /* ETHERNET_HEADER_LENGTH */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_common) == 20); /* Part of data record flow key for VLAN entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_vlan { ovs_be16 vlan_id; /* VLAN_ID */ ovs_be16 dot1q_vlan_id; /* DOT1Q_VLAN_ID */ uint8_t dot1q_priority; /* DOT1Q_PRIORITY */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_vlan) == 5); /* Part of data record flow key for IP entities. */ /* XXX: Replace IP_TTL with MINIMUM_TTL and MAXIMUM_TTL? */ OVS_PACKED( struct ipfix_data_record_flow_key_ip { uint8_t ip_version; /* IP_VERSION */ uint8_t ip_ttl; /* IP_TTL */ uint8_t protocol_identifier; /* PROTOCOL_IDENTIFIER */ uint8_t ip_diff_serv_code_point; /* IP_DIFF_SERV_CODE_POINT */ uint8_t ip_precedence; /* IP_PRECEDENCE */ uint8_t ip_class_of_service; /* IP_CLASS_OF_SERVICE */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ip) == 6); /* Part of data record flow key for IPv4 entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_ipv4 { ovs_be32 source_ipv4_address; /* SOURCE_IPV4_ADDRESS */ ovs_be32 destination_ipv4_address; /* DESTINATION_IPV4_ADDRESS */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ipv4) == 8); /* Part of data record flow key for IPv6 entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_ipv6 { uint8_t source_ipv6_address[16]; /* SOURCE_IPV6_ADDRESS */ uint8_t destination_ipv6_address[16]; /* DESTINATION_IPV6_ADDRESS */ ovs_be32 flow_label_ipv6; /* FLOW_LABEL_IPV6 */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ipv6) == 36); /* Part of data record flow key for TCP/UDP/SCTP entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_transport { ovs_be16 source_transport_port; /* SOURCE_TRANSPORT_PORT */ ovs_be16 destination_transport_port; /* DESTINATION_TRANSPORT_PORT */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_transport) == 4); /* Part of data record flow key for ICMP entities. */ OVS_PACKED( struct ipfix_data_record_flow_key_icmp { uint8_t icmp_type; /* ICMP_TYPE_IPV4 / ICMP_TYPE_IPV6 */ uint8_t icmp_code; /* ICMP_CODE_IPV4 / ICMP_CODE_IPV6 */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_icmp) == 2); /* For the tunnel type that is on the top of IPSec, the protocol identifier * of the upper tunnel type is used. */ static uint8_t tunnel_protocol[NUM_DPIF_IPFIX_TUNNEL] = { 0, /* reserved */ IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_VXLAN */ IPPROTO_GRE, /* DPIF_IPFIX_TUNNEL_GRE */ IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_LISP*/ IPPROTO_TCP, /* DPIF_IPFIX_TUNNEL_STT*/ IPPROTO_GRE, /* DPIF_IPFIX_TUNNEL_IPSEC_GRE */ 0 , /* reserved */ IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GENEVE*/ }; OVS_PACKED( struct ipfix_data_record_flow_key_tunnel { ovs_be32 tunnel_source_ipv4_address; /* TUNNEL_SOURCE_IPV4_ADDRESS */ ovs_be32 tunnel_destination_ipv4_address; /* TUNNEL_DESTINATION_IPV4_ADDRESS */ uint8_t tunnel_protocol_identifier; /* TUNNEL_PROTOCOL_IDENTIFIER */ ovs_be16 tunnel_source_transport_port; /* TUNNEL_SOURCE_TRANSPORT_PORT */ ovs_be16 tunnel_destination_transport_port; /* TUNNEL_DESTINATION_TRANSPORT_PORT */ uint8_t tunnel_type; /* TUNNEL_TYPE */ uint8_t tunnel_key_length; /* length of TUNNEL_KEY */ uint8_t tunnel_key[]; /* data of TUNNEL_KEY */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_tunnel) == 15); /* Cf. IETF RFC 5102 Section 5.11.3. */ enum ipfix_flow_end_reason { IDLE_TIMEOUT = 0x01, ACTIVE_TIMEOUT = 0x02, END_OF_FLOW_DETECTED = 0x03, FORCED_END = 0x04, LACK_OF_RESOURCES = 0x05 }; /* Part of data record for common aggregated elements. */ OVS_PACKED( struct ipfix_data_record_aggregated_common { ovs_be32 flow_start_delta_microseconds; /* FLOW_START_DELTA_MICROSECONDS */ ovs_be32 flow_end_delta_microseconds; /* FLOW_END_DELTA_MICROSECONDS */ ovs_be64 packet_delta_count; /* PACKET_DELTA_COUNT */ ovs_be64 layer2_octet_delta_count; /* LAYER2_OCTET_DELTA_COUNT */ uint8_t flow_end_reason; /* FLOW_END_REASON */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_common) == 25); /* Part of data record for IP aggregated elements. */ OVS_PACKED( struct ipfix_data_record_aggregated_ip { ovs_be64 octet_delta_count; /* OCTET_DELTA_COUNT */ ovs_be64 octet_delta_sum_of_squares; /* OCTET_DELTA_SUM_OF_SQUARES */ ovs_be64 minimum_ip_total_length; /* MINIMUM_IP_TOTAL_LENGTH */ ovs_be64 maximum_ip_total_length; /* MAXIMUM_IP_TOTAL_LENGTH */ }); BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 32); /* * support tunnel key for: * VxLAN: 24-bit VIN, * GRE: 32-bit key, * LISP: 24-bit instance ID * STT: 64-bit key */ #define MAX_TUNNEL_KEY_LEN 8 #define MAX_FLOW_KEY_LEN \ (sizeof(struct ipfix_data_record_flow_key_common) \ + sizeof(struct ipfix_data_record_flow_key_vlan) \ + sizeof(struct ipfix_data_record_flow_key_ip) \ + MAX(sizeof(struct ipfix_data_record_flow_key_ipv4), \ sizeof(struct ipfix_data_record_flow_key_ipv6)) \ + MAX(sizeof(struct ipfix_data_record_flow_key_icmp), \ sizeof(struct ipfix_data_record_flow_key_transport)) \ + sizeof(struct ipfix_data_record_flow_key_tunnel) \ + MAX_TUNNEL_KEY_LEN) #define MAX_DATA_RECORD_LEN \ (MAX_FLOW_KEY_LEN \ + sizeof(struct ipfix_data_record_aggregated_common) \ + sizeof(struct ipfix_data_record_aggregated_ip)) /* Max length of a data set. To simplify the implementation, each * data record is sent in a separate data set, so each data set * contains at most one data record. */ #define MAX_DATA_SET_LEN \ (sizeof(struct ipfix_set_header) \ + MAX_DATA_RECORD_LEN) /* Max length of an IPFIX message. Arbitrarily set to accommodate low * MTU. */ #define MAX_MESSAGE_LEN 1024 /* Cache structures. */ /* Flow key. */ struct ipfix_flow_key { uint32_t obs_domain_id; uint16_t template_id; size_t flow_key_msg_part_size; uint64_t flow_key_msg_part[DIV_ROUND_UP(MAX_FLOW_KEY_LEN, 8)]; }; /* Flow cache entry. */ struct ipfix_flow_cache_entry { struct hmap_node flow_key_map_node; struct ovs_list cache_flow_start_timestamp_list_node; struct ipfix_flow_key flow_key; /* Common aggregated elements. */ uint64_t flow_start_timestamp_usec; uint64_t flow_end_timestamp_usec; uint64_t packet_delta_count; uint64_t layer2_octet_delta_count; uint64_t octet_delta_count; uint64_t octet_delta_sum_of_squares; /* 0 if not IP. */ uint16_t minimum_ip_total_length; /* 0 if not IP. */ uint16_t maximum_ip_total_length; /* 0 if not IP. */ }; static void dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *, bool, const uint64_t, const uint32_t); static void get_export_time_now(uint64_t *, uint32_t *); static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool); static bool ofproto_ipfix_bridge_exporter_options_equal( const struct ofproto_ipfix_bridge_exporter_options *a, const struct ofproto_ipfix_bridge_exporter_options *b) { return (a->obs_domain_id == b->obs_domain_id && a->obs_point_id == b->obs_point_id && a->sampling_rate == b->sampling_rate && a->cache_active_timeout == b->cache_active_timeout && a->cache_max_flows == b->cache_max_flows && a->enable_tunnel_sampling == b->enable_tunnel_sampling && a->enable_input_sampling == b->enable_input_sampling && a->enable_output_sampling == b->enable_output_sampling && sset_equals(&a->targets, &b->targets)); } static struct ofproto_ipfix_bridge_exporter_options * ofproto_ipfix_bridge_exporter_options_clone( const struct ofproto_ipfix_bridge_exporter_options *old) { struct ofproto_ipfix_bridge_exporter_options *new = xmemdup(old, sizeof *old); sset_clone(&new->targets, &old->targets); return new; } static void ofproto_ipfix_bridge_exporter_options_destroy( struct ofproto_ipfix_bridge_exporter_options *options) { if (options) { sset_destroy(&options->targets); free(options); } } static bool ofproto_ipfix_flow_exporter_options_equal( const struct ofproto_ipfix_flow_exporter_options *a, const struct ofproto_ipfix_flow_exporter_options *b) { return (a->collector_set_id == b->collector_set_id && a->cache_active_timeout == b->cache_active_timeout && a->cache_max_flows == b->cache_max_flows && sset_equals(&a->targets, &b->targets)); } static struct ofproto_ipfix_flow_exporter_options * ofproto_ipfix_flow_exporter_options_clone( const struct ofproto_ipfix_flow_exporter_options *old) { struct ofproto_ipfix_flow_exporter_options *new = xmemdup(old, sizeof *old); sset_clone(&new->targets, &old->targets); return new; } static void ofproto_ipfix_flow_exporter_options_destroy( struct ofproto_ipfix_flow_exporter_options *options) { if (options) { sset_destroy(&options->targets); free(options); } } static void dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter) { exporter->collectors = NULL; exporter->seq_number = 1; exporter->last_template_set_time = 0; hmap_init(&exporter->cache_flow_key_map); list_init(&exporter->cache_flow_start_timestamp_list); exporter->cache_active_timeout = 0; exporter->cache_max_flows = 0; } static void dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter) { /* Flush the cache with flow end reason "forced end." */ dpif_ipfix_cache_expire_now(exporter, true); collectors_destroy(exporter->collectors); exporter->collectors = NULL; exporter->seq_number = 1; exporter->last_template_set_time = 0; exporter->cache_active_timeout = 0; exporter->cache_max_flows = 0; } static void dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter) { dpif_ipfix_exporter_clear(exporter); hmap_destroy(&exporter->cache_flow_key_map); } static bool dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter, const struct sset *targets, const uint32_t cache_active_timeout, const uint32_t cache_max_flows) { collectors_destroy(exporter->collectors); collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT, &exporter->collectors); if (exporter->collectors == NULL) { VLOG_WARN_RL(&rl, "no collectors could be initialized, " "IPFIX exporter disabled"); dpif_ipfix_exporter_clear(exporter); return false; } exporter->cache_active_timeout = cache_active_timeout; exporter->cache_max_flows = cache_max_flows; return true; } static struct dpif_ipfix_port * dpif_ipfix_find_port(const struct dpif_ipfix *di, odp_port_t odp_port) OVS_REQUIRES(mutex) { struct dpif_ipfix_port *dip; HMAP_FOR_EACH_IN_BUCKET (dip, hmap_node, hash_odp_port(odp_port), &di->tunnel_ports) { if (dip->odp_port == odp_port) { return dip; } } return NULL; } static void dpif_ipfix_del_port(struct dpif_ipfix *di, struct dpif_ipfix_port *dip) OVS_REQUIRES(mutex) { hmap_remove(&di->tunnel_ports, &dip->hmap_node); free(dip); } void dpif_ipfix_add_tunnel_port(struct dpif_ipfix *di, struct ofport *ofport, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_ipfix_port *dip; const char *type; ovs_mutex_lock(&mutex); dip = dpif_ipfix_find_port(di, odp_port); if (dip) { dpif_ipfix_del_port(di, dip); } type = netdev_get_type(ofport->netdev); if (type == NULL) { goto out; } /* Add to table of tunnel ports. */ dip = xmalloc(sizeof *dip); dip->ofport = ofport; dip->odp_port = odp_port; if (strcmp(type, "gre") == 0) { /* 32-bit key gre */ dip->tunnel_type = DPIF_IPFIX_TUNNEL_GRE; dip->tunnel_key_length = 4; } else if (strcmp(type, "ipsec_gre") == 0) { /* 32-bit key ipsec_gre */ dip->tunnel_type = DPIF_IPFIX_TUNNEL_IPSEC_GRE; dip->tunnel_key_length = 4; } else if (strcmp(type, "vxlan") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_VXLAN; dip->tunnel_key_length = 3; } else if (strcmp(type, "lisp") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_LISP; dip->tunnel_key_length = 3; } else if (strcmp(type, "geneve") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_GENEVE; dip->tunnel_key_length = 3; } else if (strcmp(type, "stt") == 0) { dip->tunnel_type = DPIF_IPFIX_TUNNEL_STT; dip->tunnel_key_length = 8; } else { free(dip); goto out; } hmap_insert(&di->tunnel_ports, &dip->hmap_node, hash_odp_port(odp_port)); out: ovs_mutex_unlock(&mutex); } void dpif_ipfix_del_tunnel_port(struct dpif_ipfix *di, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_ipfix_port *dip; ovs_mutex_lock(&mutex); dip = dpif_ipfix_find_port(di, odp_port); if (dip) { dpif_ipfix_del_port(di, dip); } ovs_mutex_unlock(&mutex); } bool dpif_ipfix_get_tunnel_port(const struct dpif_ipfix *di, odp_port_t odp_port) OVS_EXCLUDED(mutex) { struct dpif_ipfix_port *dip; ovs_mutex_lock(&mutex); dip = dpif_ipfix_find_port(di, odp_port); ovs_mutex_unlock(&mutex); return dip != NULL; } static void dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter) { dpif_ipfix_exporter_init(&exporter->exporter); exporter->options = NULL; exporter->probability = 0; } static void dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter) { dpif_ipfix_exporter_clear(&exporter->exporter); ofproto_ipfix_bridge_exporter_options_destroy(exporter->options); exporter->options = NULL; exporter->probability = 0; } static void dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter) { dpif_ipfix_bridge_exporter_clear(exporter); dpif_ipfix_exporter_destroy(&exporter->exporter); } static void dpif_ipfix_bridge_exporter_set_options( struct dpif_ipfix_bridge_exporter *exporter, const struct ofproto_ipfix_bridge_exporter_options *options) { bool options_changed; if (!options || sset_is_empty(&options->targets)) { /* No point in doing any work if there are no targets. */ dpif_ipfix_bridge_exporter_clear(exporter); return; } options_changed = ( !exporter->options || !ofproto_ipfix_bridge_exporter_options_equal( options, exporter->options)); /* Configure collectors if options have changed or if we're * shortchanged in collectors (which indicates that opening one or * more of the configured collectors failed, so that we should * retry). */ if (options_changed || collectors_count(exporter->exporter.collectors) < sset_count(&options->targets)) { if (!dpif_ipfix_exporter_set_options( &exporter->exporter, &options->targets, options->cache_active_timeout, options->cache_max_flows)) { return; } } /* Avoid reconfiguring if options didn't change. */ if (!options_changed) { return; } ofproto_ipfix_bridge_exporter_options_destroy(exporter->options); exporter->options = ofproto_ipfix_bridge_exporter_options_clone(options); exporter->probability = MAX(1, UINT32_MAX / exporter->options->sampling_rate); /* Run over the cache as some entries might have expired after * changing the timeouts. */ dpif_ipfix_cache_expire_now(&exporter->exporter, false); } static struct dpif_ipfix_flow_exporter_map_node* dpif_ipfix_find_flow_exporter_map_node( const struct dpif_ipfix *di, const uint32_t collector_set_id) OVS_REQUIRES(mutex) { struct dpif_ipfix_flow_exporter_map_node *exporter_node; HMAP_FOR_EACH_WITH_HASH (exporter_node, node, hash_int(collector_set_id, 0), &di->flow_exporter_map) { if (exporter_node->exporter.options->collector_set_id == collector_set_id) { return exporter_node; } } return NULL; } static void dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter) { dpif_ipfix_exporter_init(&exporter->exporter); exporter->options = NULL; } static void dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter) { dpif_ipfix_exporter_clear(&exporter->exporter); ofproto_ipfix_flow_exporter_options_destroy(exporter->options); exporter->options = NULL; } static void dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter) { dpif_ipfix_flow_exporter_clear(exporter); dpif_ipfix_exporter_destroy(&exporter->exporter); } static bool dpif_ipfix_flow_exporter_set_options( struct dpif_ipfix_flow_exporter *exporter, const struct ofproto_ipfix_flow_exporter_options *options) { bool options_changed; if (sset_is_empty(&options->targets)) { /* No point in doing any work if there are no targets. */ dpif_ipfix_flow_exporter_clear(exporter); return true; } options_changed = ( !exporter->options || !ofproto_ipfix_flow_exporter_options_equal( options, exporter->options)); /* Configure collectors if options have changed or if we're * shortchanged in collectors (which indicates that opening one or * more of the configured collectors failed, so that we should * retry). */ if (options_changed || collectors_count(exporter->exporter.collectors) < sset_count(&options->targets)) { if (!dpif_ipfix_exporter_set_options( &exporter->exporter, &options->targets, options->cache_active_timeout, options->cache_max_flows)) { return false; } } /* Avoid reconfiguring if options didn't change. */ if (!options_changed) { return true; } ofproto_ipfix_flow_exporter_options_destroy(exporter->options); exporter->options = ofproto_ipfix_flow_exporter_options_clone(options); /* Run over the cache as some entries might have expired after * changing the timeouts. */ dpif_ipfix_cache_expire_now(&exporter->exporter, false); return true; } static void remove_flow_exporter(struct dpif_ipfix *di, struct dpif_ipfix_flow_exporter_map_node *node) { hmap_remove(&di->flow_exporter_map, &node->node); dpif_ipfix_flow_exporter_destroy(&node->exporter); free(node); } void dpif_ipfix_set_options( struct dpif_ipfix *di, const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options, const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options, size_t n_flow_exporters_options) OVS_EXCLUDED(mutex) { int i; struct ofproto_ipfix_flow_exporter_options *options; struct dpif_ipfix_flow_exporter_map_node *node, *next; ovs_mutex_lock(&mutex); dpif_ipfix_bridge_exporter_set_options(&di->bridge_exporter, bridge_exporter_options); /* Add new flow exporters and update current flow exporters. */ options = (struct ofproto_ipfix_flow_exporter_options *) flow_exporters_options; for (i = 0; i < n_flow_exporters_options; i++) { node = dpif_ipfix_find_flow_exporter_map_node( di, options->collector_set_id); if (!node) { node = xzalloc(sizeof *node); dpif_ipfix_flow_exporter_init(&node->exporter); hmap_insert(&di->flow_exporter_map, &node->node, hash_int(options->collector_set_id, 0)); } if (!dpif_ipfix_flow_exporter_set_options(&node->exporter, options)) { remove_flow_exporter(di, node); } options++; } /* Remove dropped flow exporters, if any needs to be removed. */ HMAP_FOR_EACH_SAFE (node, next, node, &di->flow_exporter_map) { /* This is slow but doesn't take any extra memory, and * this table is not supposed to contain many rows anyway. */ options = (struct ofproto_ipfix_flow_exporter_options *) flow_exporters_options; for (i = 0; i < n_flow_exporters_options; i++) { if (node->exporter.options->collector_set_id == options->collector_set_id) { break; } options++; } if (i == n_flow_exporters_options) { // Not found. remove_flow_exporter(di, node); } } ovs_mutex_unlock(&mutex); } struct dpif_ipfix * dpif_ipfix_create(void) { struct dpif_ipfix *di; di = xzalloc(sizeof *di); dpif_ipfix_bridge_exporter_init(&di->bridge_exporter); hmap_init(&di->flow_exporter_map); hmap_init(&di->tunnel_ports); ovs_refcount_init(&di->ref_cnt); return di; } struct dpif_ipfix * dpif_ipfix_ref(const struct dpif_ipfix *di_) { struct dpif_ipfix *di = CONST_CAST(struct dpif_ipfix *, di_); if (di) { ovs_refcount_ref(&di->ref_cnt); } return di; } uint32_t dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { uint32_t ret; ovs_mutex_lock(&mutex); ret = di->bridge_exporter.probability; ovs_mutex_unlock(&mutex); return ret; } bool dpif_ipfix_get_bridge_exporter_input_sampling(const struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { bool ret = false; ovs_mutex_lock(&mutex); if (di->bridge_exporter.options) { ret = di->bridge_exporter.options->enable_input_sampling; } ovs_mutex_unlock(&mutex); return ret; } bool dpif_ipfix_get_bridge_exporter_output_sampling(const struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { bool ret = false; ovs_mutex_lock(&mutex); if (di->bridge_exporter.options) { ret = di->bridge_exporter.options->enable_output_sampling; } ovs_mutex_unlock(&mutex); return ret; } bool dpif_ipfix_get_bridge_exporter_tunnel_sampling(const struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { bool ret = false; ovs_mutex_lock(&mutex); if (di->bridge_exporter.options) { ret = di->bridge_exporter.options->enable_tunnel_sampling; } ovs_mutex_unlock(&mutex); return ret; } static void dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex) { struct dpif_ipfix_flow_exporter_map_node *exp_node, *exp_next; struct dpif_ipfix_port *dip, *next; dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter); HMAP_FOR_EACH_SAFE (exp_node, exp_next, node, &di->flow_exporter_map) { hmap_remove(&di->flow_exporter_map, &exp_node->node); dpif_ipfix_flow_exporter_destroy(&exp_node->exporter); free(exp_node); } HMAP_FOR_EACH_SAFE (dip, next, hmap_node, &di->tunnel_ports) { dpif_ipfix_del_port(di, dip); } } void dpif_ipfix_unref(struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { if (di && ovs_refcount_unref_relaxed(&di->ref_cnt) == 1) { ovs_mutex_lock(&mutex); dpif_ipfix_clear(di); dpif_ipfix_bridge_exporter_destroy(&di->bridge_exporter); hmap_destroy(&di->flow_exporter_map); hmap_destroy(&di->tunnel_ports); free(di); ovs_mutex_unlock(&mutex); } } static void ipfix_init_header(uint32_t export_time_sec, uint32_t seq_number, uint32_t obs_domain_id, struct dp_packet *msg) { struct ipfix_header *hdr; hdr = dp_packet_put_zeros(msg, sizeof *hdr); hdr->version = htons(IPFIX_VERSION); hdr->length = htons(sizeof *hdr); /* Updated in ipfix_send_msg. */ hdr->export_time = htonl(export_time_sec); hdr->seq_number = htonl(seq_number); hdr->obs_domain_id = htonl(obs_domain_id); } static void ipfix_send_msg(const struct collectors *collectors, struct dp_packet *msg) { struct ipfix_header *hdr; /* Adjust the length in the header. */ hdr = dp_packet_data(msg); hdr->length = htons(dp_packet_size(msg)); collectors_send(collectors, dp_packet_data(msg), dp_packet_size(msg)); dp_packet_set_size(msg, 0); } static uint16_t ipfix_get_template_id(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3, enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel) { uint16_t template_id; template_id = l2; template_id = template_id * NUM_IPFIX_PROTO_L3 + l3; template_id = template_id * NUM_IPFIX_PROTO_L4 + l4; template_id = template_id * NUM_IPFIX_PROTO_TUNNEL + tunnel; return IPFIX_TEMPLATE_ID_MIN + template_id; } static void ipfix_define_template_entity(enum ipfix_entity_id id, enum ipfix_entity_size size, enum ipfix_entity_enterprise enterprise, struct dp_packet *msg) { struct ipfix_template_field_specifier *field; size_t field_size; if (enterprise) { field_size = sizeof *field; } else { /* No enterprise number */ field_size = sizeof *field - sizeof(ovs_be32); } field = dp_packet_put_zeros(msg, field_size); field->element_id = htons(id); if (size) { field->field_length = htons(size); } else { /* RFC 5101, Section 7. Variable-Length Information Element */ field->field_length = OVS_BE16_MAX; } if (enterprise) { field->enterprise = htonl(enterprise); } } static uint16_t ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3, enum ipfix_proto_l4 l4, enum ipfix_proto_tunnel tunnel, struct dp_packet *msg) { uint16_t count = 0; #define DEF(ID) \ { \ ipfix_define_template_entity(IPFIX_ENTITY_ID_##ID, \ IPFIX_ENTITY_SIZE_##ID, \ IPFIX_ENTITY_ENTERPRISE_##ID, msg); \ count++; \ } /* 1. Flow key. */ DEF(OBSERVATION_POINT_ID); DEF(FLOW_DIRECTION); /* Common Ethernet entities. */ DEF(SOURCE_MAC_ADDRESS); DEF(DESTINATION_MAC_ADDRESS); DEF(ETHERNET_TYPE); DEF(ETHERNET_HEADER_LENGTH); if (l2 == IPFIX_PROTO_L2_VLAN) { DEF(VLAN_ID); DEF(DOT1Q_VLAN_ID); DEF(DOT1Q_PRIORITY); } if (l3 != IPFIX_PROTO_L3_UNKNOWN) { DEF(IP_VERSION); DEF(IP_TTL); DEF(PROTOCOL_IDENTIFIER); DEF(IP_DIFF_SERV_CODE_POINT); DEF(IP_PRECEDENCE); DEF(IP_CLASS_OF_SERVICE); if (l3 == IPFIX_PROTO_L3_IPV4) { DEF(SOURCE_IPV4_ADDRESS); DEF(DESTINATION_IPV4_ADDRESS); if (l4 == IPFIX_PROTO_L4_TCP_UDP_SCTP) { DEF(SOURCE_TRANSPORT_PORT); DEF(DESTINATION_TRANSPORT_PORT); } else if (l4 == IPFIX_PROTO_L4_ICMP) { DEF(ICMP_TYPE_IPV4); DEF(ICMP_CODE_IPV4); } } else { /* l3 == IPFIX_PROTO_L3_IPV6 */ DEF(SOURCE_IPV6_ADDRESS); DEF(DESTINATION_IPV6_ADDRESS); DEF(FLOW_LABEL_IPV6); if (l4 == IPFIX_PROTO_L4_TCP_UDP_SCTP) { DEF(SOURCE_TRANSPORT_PORT); DEF(DESTINATION_TRANSPORT_PORT); } else if (l4 == IPFIX_PROTO_L4_ICMP) { DEF(ICMP_TYPE_IPV6); DEF(ICMP_CODE_IPV6); } } } if (tunnel != IPFIX_PROTO_NOT_TUNNELED) { DEF(TUNNEL_SOURCE_IPV4_ADDRESS); DEF(TUNNEL_DESTINATION_IPV4_ADDRESS); DEF(TUNNEL_PROTOCOL_IDENTIFIER); DEF(TUNNEL_SOURCE_TRANSPORT_PORT); DEF(TUNNEL_DESTINATION_TRANSPORT_PORT); DEF(TUNNEL_TYPE); DEF(TUNNEL_KEY); } /* 2. Flow aggregated data. */ DEF(FLOW_START_DELTA_MICROSECONDS); DEF(FLOW_END_DELTA_MICROSECONDS); DEF(PACKET_DELTA_COUNT); DEF(LAYER2_OCTET_DELTA_COUNT); DEF(FLOW_END_REASON); if (l3 != IPFIX_PROTO_L3_UNKNOWN) { DEF(OCTET_DELTA_COUNT); DEF(OCTET_DELTA_SUM_OF_SQUARES); DEF(MINIMUM_IP_TOTAL_LENGTH); DEF(MAXIMUM_IP_TOTAL_LENGTH); } #undef DEF return count; } static void ipfix_init_template_msg(void *msg_stub, uint32_t export_time_sec, uint32_t seq_number, uint32_t obs_domain_id, struct dp_packet *msg, size_t *set_hdr_offset) { struct ipfix_set_header *set_hdr; dp_packet_use_stub(msg, msg_stub, sizeof msg_stub); ipfix_init_header(export_time_sec, seq_number, obs_domain_id, msg); *set_hdr_offset = dp_packet_size(msg); /* Add a Template Set. */ set_hdr = dp_packet_put_zeros(msg, sizeof *set_hdr); set_hdr->set_id = htons(IPFIX_SET_ID_TEMPLATE); } static void ipfix_send_template_msg(const struct collectors *collectors, struct dp_packet *msg, size_t set_hdr_offset) { struct ipfix_set_header *set_hdr; /* Send template message. */ set_hdr = (struct ipfix_set_header*) ((uint8_t*)dp_packet_data(msg) + set_hdr_offset); set_hdr->length = htons(dp_packet_size(msg) - set_hdr_offset); ipfix_send_msg(collectors, msg); dp_packet_uninit(msg); } static void ipfix_send_template_msgs(struct dpif_ipfix_exporter *exporter, uint32_t export_time_sec, uint32_t obs_domain_id) { uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)]; struct dp_packet msg; size_t set_hdr_offset, tmpl_hdr_offset; struct ipfix_template_record_header *tmpl_hdr; uint16_t field_count; enum ipfix_proto_l2 l2; enum ipfix_proto_l3 l3; enum ipfix_proto_l4 l4; enum ipfix_proto_tunnel tunnel; ipfix_init_template_msg(msg_stub, export_time_sec, exporter->seq_number, obs_domain_id, &msg, &set_hdr_offset); /* Define one template for each possible combination of * protocols. */ for (l2 = 0; l2 < NUM_IPFIX_PROTO_L2; l2++) { for (l3 = 0; l3 < NUM_IPFIX_PROTO_L3; l3++) { for (l4 = 0; l4 < NUM_IPFIX_PROTO_L4; l4++) { if (l3 == IPFIX_PROTO_L3_UNKNOWN && l4 != IPFIX_PROTO_L4_UNKNOWN) { continue; } for (tunnel = 0; tunnel < NUM_IPFIX_PROTO_TUNNEL; tunnel++) { /* When the size of the template packet reaches * MAX_MESSAGE_LEN(1024), send it out. * And then reinitialize the msg to construct a new * packet for the following templates. */ if (dp_packet_size(&msg) >= MAX_MESSAGE_LEN) { /* Send template message. */ ipfix_send_template_msg(exporter->collectors, &msg, set_hdr_offset); /* Reinitialize the template msg. */ ipfix_init_template_msg(msg_stub, export_time_sec, exporter->seq_number, obs_domain_id, &msg, &set_hdr_offset); } tmpl_hdr_offset = dp_packet_size(&msg); tmpl_hdr = dp_packet_put_zeros(&msg, sizeof *tmpl_hdr); tmpl_hdr->template_id = htons( ipfix_get_template_id(l2, l3, l4, tunnel)); field_count = ipfix_define_template_fields(l2, l3, l4, tunnel, &msg); tmpl_hdr = (struct ipfix_template_record_header*) ((uint8_t*)dp_packet_data(&msg) + tmpl_hdr_offset); tmpl_hdr->field_count = htons(field_count); } } } } /* Send template message. */ ipfix_send_template_msg(exporter->collectors, &msg, set_hdr_offset); /* XXX: Add Options Template Sets, at least to define a Flow Keys * Option Template. */ } static inline uint32_t ipfix_hash_flow_key(const struct ipfix_flow_key *flow_key, uint32_t basis) { uint32_t hash; hash = hash_int(flow_key->obs_domain_id, basis); hash = hash_int(flow_key->template_id, hash); hash = hash_bytes(flow_key->flow_key_msg_part, flow_key->flow_key_msg_part_size, hash); return hash; } static bool ipfix_flow_key_equal(const struct ipfix_flow_key *a, const struct ipfix_flow_key *b) { /* The template ID determines the flow key size, so not need to * compare it. */ return (a->obs_domain_id == b->obs_domain_id && a->template_id == b->template_id && memcmp(a->flow_key_msg_part, b->flow_key_msg_part, a->flow_key_msg_part_size) == 0); } static struct ipfix_flow_cache_entry* ipfix_cache_find_entry(const struct dpif_ipfix_exporter *exporter, const struct ipfix_flow_key *flow_key) { struct ipfix_flow_cache_entry *entry; HMAP_FOR_EACH_WITH_HASH (entry, flow_key_map_node, ipfix_hash_flow_key(flow_key, 0), &exporter->cache_flow_key_map) { if (ipfix_flow_key_equal(&entry->flow_key, flow_key)) { return entry; } } return NULL; } static bool ipfix_cache_next_timeout_msec(const struct dpif_ipfix_exporter *exporter, long long int *next_timeout_msec) { struct ipfix_flow_cache_entry *entry; LIST_FOR_EACH (entry, cache_flow_start_timestamp_list_node, &exporter->cache_flow_start_timestamp_list) { *next_timeout_msec = entry->flow_start_timestamp_usec / 1000LL + 1000LL * exporter->cache_active_timeout; return true; } return false; } static void ipfix_cache_aggregate_entries(struct ipfix_flow_cache_entry *from_entry, struct ipfix_flow_cache_entry *to_entry) { uint64_t *to_start, *to_end, *from_start, *from_end; uint16_t *to_min_len, *to_max_len, *from_min_len, *from_max_len; to_start = &to_entry->flow_start_timestamp_usec; to_end = &to_entry->flow_end_timestamp_usec; from_start = &from_entry->flow_start_timestamp_usec; from_end = &from_entry->flow_end_timestamp_usec; if (*to_start > *from_start) { *to_start = *from_start; } if (*to_end < *from_end) { *to_end = *from_end; } to_entry->packet_delta_count += from_entry->packet_delta_count; to_entry->layer2_octet_delta_count += from_entry->layer2_octet_delta_count; to_entry->octet_delta_count += from_entry->octet_delta_count; to_entry->octet_delta_sum_of_squares += from_entry->octet_delta_sum_of_squares; to_min_len = &to_entry->minimum_ip_total_length; to_max_len = &to_entry->maximum_ip_total_length; from_min_len = &from_entry->minimum_ip_total_length; from_max_len = &from_entry->maximum_ip_total_length; if (!*to_min_len || (*from_min_len && *to_min_len > *from_min_len)) { *to_min_len = *from_min_len; } if (*to_max_len < *from_max_len) { *to_max_len = *from_max_len; } } /* Add an entry into a flow cache. The entry is either aggregated into * an existing entry with the same flow key and free()d, or it is * inserted into the cache. */ static void ipfix_cache_update(struct dpif_ipfix_exporter *exporter, struct ipfix_flow_cache_entry *entry) { struct ipfix_flow_cache_entry *old_entry; old_entry = ipfix_cache_find_entry(exporter, &entry->flow_key); if (old_entry == NULL) { hmap_insert(&exporter->cache_flow_key_map, &entry->flow_key_map_node, ipfix_hash_flow_key(&entry->flow_key, 0)); /* As the latest entry added into the cache, it should * logically have the highest flow_start_timestamp_usec, so * append it at the tail. */ list_push_back(&exporter->cache_flow_start_timestamp_list, &entry->cache_flow_start_timestamp_list_node); /* Enforce exporter->cache_max_flows limit. */ if (hmap_count(&exporter->cache_flow_key_map) > exporter->cache_max_flows) { dpif_ipfix_cache_expire_now(exporter, false); } } else { ipfix_cache_aggregate_entries(entry, old_entry); free(entry); } } static void ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry, const struct dp_packet *packet, const struct flow *flow, uint64_t packet_delta_count, uint32_t obs_domain_id, uint32_t obs_point_id, odp_port_t output_odp_port, const struct dpif_ipfix_port *tunnel_port, const struct flow_tnl *tunnel_key) { struct ipfix_flow_key *flow_key; struct dp_packet msg; enum ipfix_proto_l2 l2; enum ipfix_proto_l3 l3; enum ipfix_proto_l4 l4; enum ipfix_proto_tunnel tunnel = IPFIX_PROTO_NOT_TUNNELED; uint8_t ethernet_header_length; uint16_t ethernet_total_length; flow_key = &entry->flow_key; dp_packet_use_stub(&msg, flow_key->flow_key_msg_part, sizeof flow_key->flow_key_msg_part); /* Choose the right template ID matching the protocols in the * sampled packet. */ l2 = (flow->vlan_tci == 0) ? IPFIX_PROTO_L2_ETH : IPFIX_PROTO_L2_VLAN; switch(ntohs(flow->dl_type)) { case ETH_TYPE_IP: l3 = IPFIX_PROTO_L3_IPV4; switch(flow->nw_proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_SCTP: l4 = IPFIX_PROTO_L4_TCP_UDP_SCTP; break; case IPPROTO_ICMP: l4 = IPFIX_PROTO_L4_ICMP; break; default: l4 = IPFIX_PROTO_L4_UNKNOWN; } break; case ETH_TYPE_IPV6: l3 = IPFIX_PROTO_L3_IPV6; switch(flow->nw_proto) { case IPPROTO_TCP: case IPPROTO_UDP: case IPPROTO_SCTP: l4 = IPFIX_PROTO_L4_TCP_UDP_SCTP; break; case IPPROTO_ICMPV6: l4 = IPFIX_PROTO_L4_ICMP; break; default: l4 = IPFIX_PROTO_L4_UNKNOWN; } break; default: l3 = IPFIX_PROTO_L3_UNKNOWN; l4 = IPFIX_PROTO_L4_UNKNOWN; } if (tunnel_port && tunnel_key) { tunnel = IPFIX_PROTO_TUNNELED; } flow_key->obs_domain_id = obs_domain_id; flow_key->template_id = ipfix_get_template_id(l2, l3, l4, tunnel); /* The fields defined in the ipfix_data_record_* structs and sent * below must match exactly the templates defined in * ipfix_define_template_fields. */ ethernet_header_length = (l2 == IPFIX_PROTO_L2_VLAN) ? VLAN_ETH_HEADER_LEN : ETH_HEADER_LEN; ethernet_total_length = dp_packet_size(packet); /* Common Ethernet entities. */ { struct ipfix_data_record_flow_key_common *data_common; data_common = dp_packet_put_zeros(&msg, sizeof *data_common); data_common->observation_point_id = htonl(obs_point_id); data_common->flow_direction = (output_odp_port == ODPP_NONE) ? INGRESS_FLOW : EGRESS_FLOW; data_common->source_mac_address = flow->dl_src; data_common->destination_mac_address = flow->dl_dst; data_common->ethernet_type = flow->dl_type; data_common->ethernet_header_length = ethernet_header_length; } if (l2 == IPFIX_PROTO_L2_VLAN) { struct ipfix_data_record_flow_key_vlan *data_vlan; uint16_t vlan_id = vlan_tci_to_vid(flow->vlan_tci); uint8_t priority = vlan_tci_to_pcp(flow->vlan_tci); data_vlan = dp_packet_put_zeros(&msg, sizeof *data_vlan); data_vlan->vlan_id = htons(vlan_id); data_vlan->dot1q_vlan_id = htons(vlan_id); data_vlan->dot1q_priority = priority; } if (l3 != IPFIX_PROTO_L3_UNKNOWN) { struct ipfix_data_record_flow_key_ip *data_ip; data_ip = dp_packet_put_zeros(&msg, sizeof *data_ip); data_ip->ip_version = (l3 == IPFIX_PROTO_L3_IPV4) ? 4 : 6; data_ip->ip_ttl = flow->nw_ttl; data_ip->protocol_identifier = flow->nw_proto; data_ip->ip_diff_serv_code_point = flow->nw_tos >> 2; data_ip->ip_precedence = flow->nw_tos >> 5; data_ip->ip_class_of_service = flow->nw_tos; if (l3 == IPFIX_PROTO_L3_IPV4) { struct ipfix_data_record_flow_key_ipv4 *data_ipv4; data_ipv4 = dp_packet_put_zeros(&msg, sizeof *data_ipv4); data_ipv4->source_ipv4_address = flow->nw_src; data_ipv4->destination_ipv4_address = flow->nw_dst; } else { /* l3 == IPFIX_PROTO_L3_IPV6 */ struct ipfix_data_record_flow_key_ipv6 *data_ipv6; data_ipv6 = dp_packet_put_zeros(&msg, sizeof *data_ipv6); memcpy(data_ipv6->source_ipv6_address, &flow->ipv6_src, sizeof flow->ipv6_src); memcpy(data_ipv6->destination_ipv6_address, &flow->ipv6_dst, sizeof flow->ipv6_dst); data_ipv6->flow_label_ipv6 = flow->ipv6_label; } } if (l4 == IPFIX_PROTO_L4_TCP_UDP_SCTP) { struct ipfix_data_record_flow_key_transport *data_transport; data_transport = dp_packet_put_zeros(&msg, sizeof *data_transport); data_transport->source_transport_port = flow->tp_src; data_transport->destination_transport_port = flow->tp_dst; } else if (l4 == IPFIX_PROTO_L4_ICMP) { struct ipfix_data_record_flow_key_icmp *data_icmp; data_icmp = dp_packet_put_zeros(&msg, sizeof *data_icmp); data_icmp->icmp_type = ntohs(flow->tp_src) & 0xff; data_icmp->icmp_code = ntohs(flow->tp_dst) & 0xff; } if (tunnel == IPFIX_PROTO_TUNNELED) { struct ipfix_data_record_flow_key_tunnel *data_tunnel; const uint8_t *tun_id; data_tunnel = dp_packet_put_zeros(&msg, sizeof *data_tunnel + tunnel_port->tunnel_key_length); data_tunnel->tunnel_source_ipv4_address = tunnel_key->ip_src; data_tunnel->tunnel_destination_ipv4_address = tunnel_key->ip_dst; /* The tunnel_protocol_identifier is from tunnel_proto array, which * contains protocol_identifiers of each tunnel type. * For the tunnel type on the top of IPSec, which uses the protocol * identifier of the upper tunnel type is used, the tcp_src and tcp_dst * are decided based on the protocol identifiers. * E.g: * The protocol identifier of DPIF_IPFIX_TUNNEL_IPSEC_GRE is IPPROTO_GRE, * and both tp_src and tp_dst are zero. */ data_tunnel->tunnel_protocol_identifier = tunnel_protocol[tunnel_port->tunnel_type]; data_tunnel->tunnel_source_transport_port = tunnel_key->tp_src; data_tunnel->tunnel_destination_transport_port = tunnel_key->tp_dst; data_tunnel->tunnel_type = tunnel_port->tunnel_type; data_tunnel->tunnel_key_length = tunnel_port->tunnel_key_length; /* tun_id is in network order, and tunnel key is in low bits. */ tun_id = (const uint8_t *) &tunnel_key->tun_id; memcpy(data_tunnel->tunnel_key, &tun_id[8 - tunnel_port->tunnel_key_length], tunnel_port->tunnel_key_length); } flow_key->flow_key_msg_part_size = dp_packet_size(&msg); { struct timeval now; uint64_t layer2_octet_delta_count; /* Calculate the total matched octet count by considering as * an approximation that all matched packets have the same * length. */ layer2_octet_delta_count = packet_delta_count * ethernet_total_length; xgettimeofday(&now); entry->flow_end_timestamp_usec = now.tv_usec + 1000000LL * now.tv_sec; entry->flow_start_timestamp_usec = entry->flow_end_timestamp_usec; entry->packet_delta_count = packet_delta_count; entry->layer2_octet_delta_count = layer2_octet_delta_count; } if (l3 != IPFIX_PROTO_L3_UNKNOWN) { uint16_t ip_total_length = ethernet_total_length - ethernet_header_length; uint64_t octet_delta_count; /* Calculate the total matched octet count by considering as * an approximation that all matched packets have the same * length. */ octet_delta_count = packet_delta_count * ip_total_length; entry->octet_delta_count = octet_delta_count; entry->octet_delta_sum_of_squares = octet_delta_count * ip_total_length; entry->minimum_ip_total_length = ip_total_length; entry->maximum_ip_total_length = ip_total_length; } else { entry->octet_delta_sum_of_squares = 0; entry->minimum_ip_total_length = 0; entry->maximum_ip_total_length = 0; } } /* Send each single data record in its own data set, to simplify the * implementation by avoiding having to group record by template ID * before sending. */ static void ipfix_put_data_set(uint32_t export_time_sec, struct ipfix_flow_cache_entry *entry, enum ipfix_flow_end_reason flow_end_reason, struct dp_packet *msg) { size_t set_hdr_offset; struct ipfix_set_header *set_hdr; set_hdr_offset = dp_packet_size(msg); /* Put a Data Set. */ set_hdr = dp_packet_put_zeros(msg, sizeof *set_hdr); set_hdr->set_id = htons(entry->flow_key.template_id); /* Copy the flow key part of the data record. */ dp_packet_put(msg, entry->flow_key.flow_key_msg_part, entry->flow_key.flow_key_msg_part_size); /* Put the non-key part of the data record. */ { struct ipfix_data_record_aggregated_common *data_aggregated_common; uint64_t export_time_usec, flow_start_delta_usec, flow_end_delta_usec; /* Calculate the negative deltas relative to the export time * in seconds sent in the header, not the exact export * time. */ export_time_usec = 1000000LL * export_time_sec; flow_start_delta_usec = export_time_usec - entry->flow_start_timestamp_usec; flow_end_delta_usec = export_time_usec - entry->flow_end_timestamp_usec; data_aggregated_common = dp_packet_put_zeros( msg, sizeof *data_aggregated_common); data_aggregated_common->flow_start_delta_microseconds = htonl( flow_start_delta_usec); data_aggregated_common->flow_end_delta_microseconds = htonl( flow_end_delta_usec); data_aggregated_common->packet_delta_count = htonll( entry->packet_delta_count); data_aggregated_common->layer2_octet_delta_count = htonll( entry->layer2_octet_delta_count); data_aggregated_common->flow_end_reason = flow_end_reason; } if (entry->octet_delta_sum_of_squares) { /* IP packet. */ struct ipfix_data_record_aggregated_ip *data_aggregated_ip; data_aggregated_ip = dp_packet_put_zeros( msg, sizeof *data_aggregated_ip); data_aggregated_ip->octet_delta_count = htonll( entry->octet_delta_count); data_aggregated_ip->octet_delta_sum_of_squares = htonll( entry->octet_delta_sum_of_squares); data_aggregated_ip->minimum_ip_total_length = htonll( entry->minimum_ip_total_length); data_aggregated_ip->maximum_ip_total_length = htonll( entry->maximum_ip_total_length); } set_hdr = (struct ipfix_set_header*)((uint8_t*)dp_packet_data(msg) + set_hdr_offset); set_hdr->length = htons(dp_packet_size(msg) - set_hdr_offset); } /* Send an IPFIX message with a single data record. */ static void ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, uint32_t export_time_sec, struct ipfix_flow_cache_entry *entry, enum ipfix_flow_end_reason flow_end_reason) { uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)]; struct dp_packet msg; dp_packet_use_stub(&msg, msg_stub, sizeof msg_stub); ipfix_init_header(export_time_sec, exporter->seq_number++, entry->flow_key.obs_domain_id, &msg); ipfix_put_data_set(export_time_sec, entry, flow_end_reason, &msg); ipfix_send_msg(exporter->collectors, &msg); dp_packet_uninit(&msg); } static void dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter, const struct dp_packet *packet, const struct flow *flow, uint64_t packet_delta_count, uint32_t obs_domain_id, uint32_t obs_point_id, odp_port_t output_odp_port, const struct dpif_ipfix_port *tunnel_port, const struct flow_tnl *tunnel_key) { struct ipfix_flow_cache_entry *entry; /* Create a flow cache entry from the sample. */ entry = xmalloc(sizeof *entry); ipfix_cache_entry_init(entry, packet, flow, packet_delta_count, obs_domain_id, obs_point_id, output_odp_port, tunnel_port, tunnel_key); ipfix_cache_update(exporter, entry); } static bool bridge_exporter_enabled(struct dpif_ipfix *di) { return di->bridge_exporter.probability > 0; } void dpif_ipfix_bridge_sample(struct dpif_ipfix *di, const struct dp_packet *packet, const struct flow *flow, odp_port_t input_odp_port, odp_port_t output_odp_port, const struct flow_tnl *output_tunnel_key) OVS_EXCLUDED(mutex) { uint64_t packet_delta_count; const struct flow_tnl *tunnel_key = NULL; struct dpif_ipfix_port * tunnel_port = NULL; ovs_mutex_lock(&mutex); if (!bridge_exporter_enabled(di)) { ovs_mutex_unlock(&mutex); return; } /* Skip BFD packets: * Bidirectional Forwarding Detection(BFD) packets are for monitoring * the tunnel link status and consumed by ovs itself. No need to * smaple them. * CF IETF RFC 5881, BFD control packet is the UDP packet with * destination port 3784, and BFD echo packet is the UDP packet with * destination port 3785. */ if (is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP && (flow->tp_dst == htons(BFD_CONTROL_DEST_PORT) || flow->tp_dst == htons(BFD_ECHO_DEST_PORT))) { ovs_mutex_unlock(&mutex); return; } /* Use the sampling probability as an approximation of the number * of matched packets. */ packet_delta_count = UINT32_MAX / di->bridge_exporter.probability; if (di->bridge_exporter.options->enable_tunnel_sampling) { if (output_odp_port == ODPP_NONE && flow->tunnel.ip_dst) { /* Input tunnel. */ tunnel_key = &flow->tunnel; tunnel_port = dpif_ipfix_find_port(di, input_odp_port); } if (output_odp_port != ODPP_NONE && output_tunnel_key) { /* Output tunnel, output_tunnel_key must be valid. */ tunnel_key = output_tunnel_key; tunnel_port = dpif_ipfix_find_port(di, output_odp_port); } } dpif_ipfix_sample(&di->bridge_exporter.exporter, packet, flow, packet_delta_count, di->bridge_exporter.options->obs_domain_id, di->bridge_exporter.options->obs_point_id, output_odp_port, tunnel_port, tunnel_key); ovs_mutex_unlock(&mutex); } void dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet, const struct flow *flow, uint32_t collector_set_id, uint16_t probability, uint32_t obs_domain_id, uint32_t obs_point_id) OVS_EXCLUDED(mutex) { struct dpif_ipfix_flow_exporter_map_node *node; /* Use the sampling probability as an approximation of the number * of matched packets. */ uint64_t packet_delta_count = USHRT_MAX / probability; ovs_mutex_lock(&mutex); node = dpif_ipfix_find_flow_exporter_map_node(di, collector_set_id); if (node) { dpif_ipfix_sample(&node->exporter.exporter, packet, flow, packet_delta_count, obs_domain_id, obs_point_id, ODPP_NONE, NULL, NULL); } ovs_mutex_unlock(&mutex); } static void dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter, bool forced_end, const uint64_t export_time_usec, const uint32_t export_time_sec) { struct ipfix_flow_cache_entry *entry, *next_entry; uint64_t max_flow_start_timestamp_usec; bool template_msg_sent = false; enum ipfix_flow_end_reason flow_end_reason; if (list_is_empty(&exporter->cache_flow_start_timestamp_list)) { return; } max_flow_start_timestamp_usec = export_time_usec - 1000000LL * exporter->cache_active_timeout; LIST_FOR_EACH_SAFE (entry, next_entry, cache_flow_start_timestamp_list_node, &exporter->cache_flow_start_timestamp_list) { if (forced_end) { flow_end_reason = FORCED_END; } else if (entry->flow_start_timestamp_usec <= max_flow_start_timestamp_usec) { flow_end_reason = ACTIVE_TIMEOUT; } else if (hmap_count(&exporter->cache_flow_key_map) > exporter->cache_max_flows) { /* Enforce exporter->cache_max_flows. */ flow_end_reason = LACK_OF_RESOURCES; } else { /* Remaining flows haven't expired yet. */ break; } list_remove(&entry->cache_flow_start_timestamp_list_node); hmap_remove(&exporter->cache_flow_key_map, &entry->flow_key_map_node); if (!template_msg_sent && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL) <= export_time_sec) { ipfix_send_template_msgs(exporter, export_time_sec, entry->flow_key.obs_domain_id); exporter->last_template_set_time = export_time_sec; template_msg_sent = true; } /* XXX: Group multiple data records for the same obs domain id * into the same message. */ ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason); free(entry); } } static void get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec) { struct timeval export_time; xgettimeofday(&export_time); *export_time_usec = export_time.tv_usec + 1000000LL * export_time.tv_sec; /* The IPFIX start and end deltas are negative deltas relative to * the export time, so set the export time 1 second off to * calculate those deltas. */ if (export_time.tv_usec == 0) { *export_time_sec = export_time.tv_sec; } else { *export_time_sec = export_time.tv_sec + 1; } } static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter, bool forced_end) { uint64_t export_time_usec; uint32_t export_time_sec; get_export_time_now(&export_time_usec, &export_time_sec); dpif_ipfix_cache_expire(exporter, forced_end, export_time_usec, export_time_sec); } void dpif_ipfix_run(struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { uint64_t export_time_usec; uint32_t export_time_sec; struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node; ovs_mutex_lock(&mutex); get_export_time_now(&export_time_usec, &export_time_sec); if (bridge_exporter_enabled(di)) { dpif_ipfix_cache_expire( &di->bridge_exporter.exporter, false, export_time_usec, export_time_sec); } HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) { dpif_ipfix_cache_expire( &flow_exporter_node->exporter.exporter, false, export_time_usec, export_time_sec); } ovs_mutex_unlock(&mutex); } void dpif_ipfix_wait(struct dpif_ipfix *di) OVS_EXCLUDED(mutex) { long long int next_timeout_msec = LLONG_MAX; struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node; ovs_mutex_lock(&mutex); if (bridge_exporter_enabled(di)) { if (ipfix_cache_next_timeout_msec( &di->bridge_exporter.exporter, &next_timeout_msec)) { poll_timer_wait_until(next_timeout_msec); } } HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) { if (ipfix_cache_next_timeout_msec( &flow_exporter_node->exporter.exporter, &next_timeout_msec)) { poll_timer_wait_until(next_timeout_msec); } } ovs_mutex_unlock(&mutex); } openvswitch-2.5.9/ofproto/PaxHeaders.82075/bond.c0000644000000000000000000000013213534540071016422 xustar0030 mtime=1567801401.617682756 30 atime=1567801402.101686312 30 ctime=1567801425.013855136 openvswitch-2.5.9/ofproto/bond.c0000755000175000017500000016562413534540071020131 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "bond.h" #include #include #include #include #include "ofp-util.h" #include "ofp-actions.h" #include "ofpbuf.h" #include "ofproto/ofproto-provider.h" #include "ofproto/ofproto-dpif.h" #include "ofproto/ofproto-dpif-rid.h" #include "connectivity.h" #include "coverage.h" #include "dynamic-string.h" #include "flow.h" #include "hmap.h" #include "lacp.h" #include "list.h" #include "netdev.h" #include "odp-util.h" #include "ofpbuf.h" #include "packets.h" #include "dp-packet.h" #include "poll-loop.h" #include "seq.h" #include "match.h" #include "shash.h" #include "timeval.h" #include "unixctl.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(bond); static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER; static struct hmap all_bonds__ = HMAP_INITIALIZER(&all_bonds__); static struct hmap *const all_bonds OVS_GUARDED_BY(rwlock) = &all_bonds__; /* Bit-mask for hashing a flow down to a bucket. */ #define BOND_MASK 0xff #define BOND_BUCKETS (BOND_MASK + 1) /* A hash bucket for mapping a flow to a slave. * "struct bond" has an array of BOND_BUCKETS of these. */ struct bond_entry { struct bond_slave *slave; /* Assigned slave, NULL if unassigned. */ uint64_t tx_bytes /* Count of bytes recently transmitted. */ OVS_GUARDED_BY(rwlock); struct ovs_list list_node; /* In bond_slave's 'entries' list. */ /* Recirculation. * * 'pr_rule' is the post-recirculation rule for this entry. * 'pr_tx_bytes' is the most recently seen statistics for 'pr_rule', which * is used to determine delta (applied to 'tx_bytes' above.) */ struct rule *pr_rule; uint64_t pr_tx_bytes OVS_GUARDED_BY(rwlock); }; /* A bond slave, that is, one of the links comprising a bond. */ struct bond_slave { struct hmap_node hmap_node; /* In struct bond's slaves hmap. */ struct ovs_list list_node; /* In struct bond's enabled_slaves list. */ struct bond *bond; /* The bond that contains this slave. */ void *aux; /* Client-provided handle for this slave. */ struct netdev *netdev; /* Network device, owned by the client. */ uint64_t change_seq; /* Tracks changes in 'netdev'. */ ofp_port_t ofp_port; /* OpenFlow port number. */ char *name; /* Name (a copy of netdev_get_name(netdev)). */ /* Link status. */ long long delay_expires; /* Time after which 'enabled' may change. */ bool enabled; /* May be chosen for flows? */ bool may_enable; /* Client considers this slave bondable. */ /* Rebalancing info. Used only by bond_rebalance(). */ struct ovs_list bal_node; /* In bond_rebalance()'s 'bals' list. */ struct ovs_list entries; /* 'struct bond_entry's assigned here. */ uint64_t tx_bytes; /* Sum across 'tx_bytes' of entries. */ }; /* A bond, that is, a set of network devices grouped to improve performance or * robustness. */ struct bond { struct hmap_node hmap_node; /* In 'all_bonds' hmap. */ char *name; /* Name provided by client. */ struct ofproto_dpif *ofproto; /* The bridge this bond belongs to. */ /* Slaves. */ struct hmap slaves; /* Enabled slaves. * * Any reader or writer of 'enabled_slaves' must hold 'mutex'. * (To prevent the bond_slave from disappearing they must also hold * 'rwlock'.) */ struct ovs_mutex mutex OVS_ACQ_AFTER(rwlock); struct ovs_list enabled_slaves OVS_GUARDED; /* Contains struct bond_slaves. */ /* Bonding info. */ enum bond_mode balance; /* Balancing mode, one of BM_*. */ struct bond_slave *active_slave; int updelay, downdelay; /* Delay before slave goes up/down, in ms. */ enum lacp_status lacp_status; /* Status of LACP negotiations. */ bool bond_revalidate; /* True if flows need revalidation. */ uint32_t basis; /* Basis for flow hash function. */ /* SLB specific bonding info. */ struct bond_entry *hash; /* An array of BOND_BUCKETS elements. */ int rebalance_interval; /* Interval between rebalances, in ms. */ long long int next_rebalance; /* Next rebalancing time. */ bool send_learning_packets; uint32_t recirc_id; /* Non zero if recirculation can be used.*/ struct hmap pr_rule_ops; /* Helps to maintain post recirculation rules.*/ /* Store active slave to OVSDB. */ bool active_slave_changed; /* Set to true whenever the bond changes active slave. It will be reset to false after it is stored into OVSDB */ /* Interface name may not be persistent across an OS reboot, use * MAC address for identifing the active slave */ struct eth_addr active_slave_mac; /* The MAC address of the active interface. */ /* Legacy compatibility. */ bool lacp_fallback_ab; /* Fallback to active-backup on LACP failure. */ struct ovs_refcount ref_cnt; }; /* What to do with an bond_recirc_rule. */ enum bond_op { ADD, /* Add the rule to ofproto's flow table. */ DEL, /* Delete the rule from the ofproto's flow table. */ }; /* A rule to add to or delete from ofproto's internal flow table. */ struct bond_pr_rule_op { struct hmap_node hmap_node; struct match match; ofp_port_t out_ofport; enum bond_op op; struct rule **pr_rule; }; static void bond_entry_reset(struct bond *) OVS_REQ_WRLOCK(rwlock); static struct bond_slave *bond_slave_lookup(struct bond *, const void *slave_) OVS_REQ_RDLOCK(rwlock); static void bond_enable_slave(struct bond_slave *, bool enable) OVS_REQ_WRLOCK(rwlock); static void bond_link_status_update(struct bond_slave *) OVS_REQ_WRLOCK(rwlock); static void bond_choose_active_slave(struct bond *) OVS_REQ_WRLOCK(rwlock); static unsigned int bond_hash_src(const struct eth_addr mac, uint16_t vlan, uint32_t basis); static unsigned int bond_hash_tcp(const struct flow *, uint16_t vlan, uint32_t basis); static struct bond_entry *lookup_bond_entry(const struct bond *, const struct flow *, uint16_t vlan) OVS_REQ_RDLOCK(rwlock); static struct bond_slave *get_enabled_slave(struct bond *) OVS_REQ_RDLOCK(rwlock); static struct bond_slave *choose_output_slave(const struct bond *, const struct flow *, struct flow_wildcards *, uint16_t vlan) OVS_REQ_RDLOCK(rwlock); static void update_recirc_rules__(struct bond *bond); static bool bond_is_falling_back_to_ab(const struct bond *); /* Attempts to parse 's' as the name of a bond balancing mode. If successful, * stores the mode in '*balance' and returns true. Otherwise returns false * without modifying '*balance'. */ bool bond_mode_from_string(enum bond_mode *balance, const char *s) { if (!strcmp(s, bond_mode_to_string(BM_TCP))) { *balance = BM_TCP; } else if (!strcmp(s, bond_mode_to_string(BM_SLB))) { *balance = BM_SLB; } else if (!strcmp(s, bond_mode_to_string(BM_AB))) { *balance = BM_AB; } else { return false; } return true; } /* Returns a string representing 'balance'. */ const char * bond_mode_to_string(enum bond_mode balance) { switch (balance) { case BM_TCP: return "balance-tcp"; case BM_SLB: return "balance-slb"; case BM_AB: return "active-backup"; } OVS_NOT_REACHED(); } /* Creates and returns a new bond whose configuration is initially taken from * 's'. * * The caller should register each slave on the new bond by calling * bond_slave_register(). */ struct bond * bond_create(const struct bond_settings *s, struct ofproto_dpif *ofproto) { struct bond *bond; bond = xzalloc(sizeof *bond); bond->ofproto = ofproto; hmap_init(&bond->slaves); list_init(&bond->enabled_slaves); ovs_mutex_init(&bond->mutex); ovs_refcount_init(&bond->ref_cnt); bond->recirc_id = 0; hmap_init(&bond->pr_rule_ops); bond->active_slave_mac = eth_addr_zero; bond->active_slave_changed = false; bond_reconfigure(bond, s); return bond; } struct bond * bond_ref(const struct bond *bond_) { struct bond *bond = CONST_CAST(struct bond *, bond_); if (bond) { ovs_refcount_ref(&bond->ref_cnt); } return bond; } /* Frees 'bond'. */ void bond_unref(struct bond *bond) { struct bond_slave *slave, *next_slave; if (!bond || ovs_refcount_unref_relaxed(&bond->ref_cnt) != 1) { return; } ovs_rwlock_wrlock(&rwlock); hmap_remove(all_bonds, &bond->hmap_node); ovs_rwlock_unlock(&rwlock); HMAP_FOR_EACH_SAFE (slave, next_slave, hmap_node, &bond->slaves) { hmap_remove(&bond->slaves, &slave->hmap_node); /* Client owns 'slave->netdev'. */ free(slave->name); free(slave); } hmap_destroy(&bond->slaves); ovs_mutex_destroy(&bond->mutex); /* Free bond resources. Remove existing post recirc rules. */ if (bond->recirc_id) { recirc_free_id(bond->recirc_id); bond->recirc_id = 0; } free(bond->hash); bond->hash = NULL; update_recirc_rules__(bond); hmap_destroy(&bond->pr_rule_ops); free(bond->name); free(bond); } static void add_pr_rule(struct bond *bond, const struct match *match, ofp_port_t out_ofport, struct rule **rule) { uint32_t hash = match_hash(match, 0); struct bond_pr_rule_op *pr_op; HMAP_FOR_EACH_WITH_HASH(pr_op, hmap_node, hash, &bond->pr_rule_ops) { if (match_equal(&pr_op->match, match)) { pr_op->op = ADD; pr_op->out_ofport = out_ofport; pr_op->pr_rule = rule; return; } } pr_op = xmalloc(sizeof *pr_op); pr_op->match = *match; pr_op->op = ADD; pr_op->out_ofport = out_ofport; pr_op->pr_rule = rule; hmap_insert(&bond->pr_rule_ops, &pr_op->hmap_node, hash); } /* This function should almost never be called directly. * 'update_recirc_rules()' should be called instead. Since * this function modifies 'bond->pr_rule_ops', it is only * safe when 'rwlock' is held. * * However, when the 'bond' is the only reference in the system, * calling this function avoid acquiring lock only to satisfy * lock annotation. Currently, only 'bond_unref()' calls * this function directly. */ static void update_recirc_rules__(struct bond *bond) { struct match match; struct bond_pr_rule_op *pr_op, *next_op; uint64_t ofpacts_stub[128 / 8]; struct ofpbuf ofpacts; int i; ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); HMAP_FOR_EACH(pr_op, hmap_node, &bond->pr_rule_ops) { pr_op->op = DEL; } if (bond->hash && bond->recirc_id) { for (i = 0; i < BOND_BUCKETS; i++) { struct bond_slave *slave = bond->hash[i].slave; if (slave) { match_init_catchall(&match); match_set_recirc_id(&match, bond->recirc_id); match_set_dp_hash_masked(&match, i, BOND_MASK); add_pr_rule(bond, &match, slave->ofp_port, &bond->hash[i].pr_rule); } } } HMAP_FOR_EACH_SAFE(pr_op, next_op, hmap_node, &bond->pr_rule_ops) { int error; switch (pr_op->op) { case ADD: ofpbuf_clear(&ofpacts); ofpact_put_OUTPUT(&ofpacts)->port = pr_op->out_ofport; error = ofproto_dpif_add_internal_flow(bond->ofproto, &pr_op->match, RECIRC_RULE_PRIORITY, 0, &ofpacts, pr_op->pr_rule); if (error) { char *err_s = match_to_string(&pr_op->match, RECIRC_RULE_PRIORITY); VLOG_ERR("failed to add post recirculation flow %s", err_s); free(err_s); } break; case DEL: error = ofproto_dpif_delete_internal_flow(bond->ofproto, &pr_op->match, RECIRC_RULE_PRIORITY); if (error) { char *err_s = match_to_string(&pr_op->match, RECIRC_RULE_PRIORITY); VLOG_ERR("failed to remove post recirculation flow %s", err_s); free(err_s); } hmap_remove(&bond->pr_rule_ops, &pr_op->hmap_node); if (bond->hash) { *pr_op->pr_rule = NULL; } free(pr_op); break; } } ofpbuf_uninit(&ofpacts); } static void update_recirc_rules(struct bond *bond) OVS_REQ_RDLOCK(rwlock) { update_recirc_rules__(bond); } /* Updates 'bond''s overall configuration to 's'. * * The caller should register each slave on 'bond' by calling * bond_slave_register(). This is optional if none of the slaves' * configuration has changed. In any case it can't hurt. * * Returns true if the configuration has changed in such a way that requires * flow revalidation. * */ bool bond_reconfigure(struct bond *bond, const struct bond_settings *s) { bool revalidate = false; ovs_rwlock_wrlock(&rwlock); if (!bond->name || strcmp(bond->name, s->name)) { if (bond->name) { hmap_remove(all_bonds, &bond->hmap_node); free(bond->name); } bond->name = xstrdup(s->name); hmap_insert(all_bonds, &bond->hmap_node, hash_string(bond->name, 0)); } bond->updelay = s->up_delay; bond->downdelay = s->down_delay; if (bond->lacp_fallback_ab != s->lacp_fallback_ab_cfg) { bond->lacp_fallback_ab = s->lacp_fallback_ab_cfg; revalidate = true; } if (bond->rebalance_interval != s->rebalance_interval) { bond->rebalance_interval = s->rebalance_interval; revalidate = true; } if (bond->balance != s->balance) { bond->balance = s->balance; revalidate = true; } if (bond->basis != s->basis) { bond->basis = s->basis; revalidate = true; } if (bond->bond_revalidate) { revalidate = true; bond->bond_revalidate = false; } if (bond->balance != BM_AB) { if (!bond->recirc_id) { bond->recirc_id = recirc_alloc_id(bond->ofproto); } } else if (bond->recirc_id) { recirc_free_id(bond->recirc_id); bond->recirc_id = 0; } if (bond->balance == BM_AB || !bond->hash || revalidate) { bond_entry_reset(bond); } ovs_rwlock_unlock(&rwlock); return revalidate; } static struct bond_slave * bond_find_slave_by_mac(const struct bond *bond, const struct eth_addr mac) { struct bond_slave *slave; /* Find the last active slave */ HMAP_FOR_EACH(slave, hmap_node, &bond->slaves) { struct eth_addr slave_mac; if (netdev_get_etheraddr(slave->netdev, &slave_mac)) { continue; } if (eth_addr_equals(slave_mac, mac)) { return slave; } } return NULL; } static void bond_active_slave_changed(struct bond *bond) { if (bond->active_slave) { struct eth_addr mac; netdev_get_etheraddr(bond->active_slave->netdev, &mac); bond->active_slave_mac = mac; } else { bond->active_slave_mac = eth_addr_zero; } bond->active_slave_changed = true; seq_change(connectivity_seq_get()); } static void bond_slave_set_netdev__(struct bond_slave *slave, struct netdev *netdev) OVS_REQ_WRLOCK(rwlock) { if (slave->netdev != netdev) { slave->netdev = netdev; slave->change_seq = 0; } } /* Registers 'slave_' as a slave of 'bond'. The 'slave_' pointer is an * arbitrary client-provided pointer that uniquely identifies a slave within a * bond. If 'slave_' already exists within 'bond' then this function * reconfigures the existing slave. * * 'netdev' must be the network device that 'slave_' represents. It is owned * by the client, so the client must not close it before either unregistering * 'slave_' or destroying 'bond'. */ void bond_slave_register(struct bond *bond, void *slave_, ofp_port_t ofport, struct netdev *netdev) { struct bond_slave *slave; ovs_rwlock_wrlock(&rwlock); slave = bond_slave_lookup(bond, slave_); if (!slave) { slave = xzalloc(sizeof *slave); hmap_insert(&bond->slaves, &slave->hmap_node, hash_pointer(slave_, 0)); slave->bond = bond; slave->aux = slave_; slave->ofp_port = ofport; slave->delay_expires = LLONG_MAX; slave->name = xstrdup(netdev_get_name(netdev)); bond->bond_revalidate = true; slave->enabled = false; bond_enable_slave(slave, netdev_get_carrier(netdev)); } bond_slave_set_netdev__(slave, netdev); free(slave->name); slave->name = xstrdup(netdev_get_name(netdev)); ovs_rwlock_unlock(&rwlock); } /* Updates the network device to be used with 'slave_' to 'netdev'. * * This is useful if the caller closes and re-opens the network device * registered with bond_slave_register() but doesn't need to change anything * else. */ void bond_slave_set_netdev(struct bond *bond, void *slave_, struct netdev *netdev) { struct bond_slave *slave; ovs_rwlock_wrlock(&rwlock); slave = bond_slave_lookup(bond, slave_); if (slave) { bond_slave_set_netdev__(slave, netdev); } ovs_rwlock_unlock(&rwlock); } /* Unregisters 'slave_' from 'bond'. If 'bond' does not contain such a slave * then this function has no effect. * * Unregistering a slave invalidates all flows. */ void bond_slave_unregister(struct bond *bond, const void *slave_) { struct bond_slave *slave; bool del_active; ovs_rwlock_wrlock(&rwlock); slave = bond_slave_lookup(bond, slave_); if (!slave) { goto out; } bond->bond_revalidate = true; bond_enable_slave(slave, false); del_active = bond->active_slave == slave; if (bond->hash) { struct bond_entry *e; for (e = bond->hash; e <= &bond->hash[BOND_MASK]; e++) { if (e->slave == slave) { e->slave = NULL; } } } free(slave->name); hmap_remove(&bond->slaves, &slave->hmap_node); /* Client owns 'slave->netdev'. */ free(slave); if (del_active) { bond_choose_active_slave(bond); bond->send_learning_packets = true; } out: ovs_rwlock_unlock(&rwlock); } /* Should be called on each slave in 'bond' before bond_run() to indicate * whether or not 'slave_' may be enabled. This function is intended to allow * other protocols to have some impact on bonding decisions. For example LACP * or high level link monitoring protocols may decide that a given slave should * not be able to send traffic. */ void bond_slave_set_may_enable(struct bond *bond, void *slave_, bool may_enable) { ovs_rwlock_wrlock(&rwlock); bond_slave_lookup(bond, slave_)->may_enable = may_enable; ovs_rwlock_unlock(&rwlock); } /* Performs periodic maintenance on 'bond'. * * Returns true if the caller should revalidate its flows. * * The caller should check bond_should_send_learning_packets() afterward. */ bool bond_run(struct bond *bond, enum lacp_status lacp_status) { struct bond_slave *slave; bool revalidate; ovs_rwlock_wrlock(&rwlock); if (bond->lacp_status != lacp_status) { bond->lacp_status = lacp_status; bond->bond_revalidate = true; /* Change in LACP status can affect whether the bond is falling back to * active-backup. Make sure to create or destroy buckets if * necessary. */ if (bond_is_falling_back_to_ab(bond) || !bond->hash) { bond_entry_reset(bond); } } /* Enable slaves based on link status and LACP feedback. */ HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { bond_link_status_update(slave); slave->change_seq = seq_read(connectivity_seq_get()); } if (!bond->active_slave || !bond->active_slave->enabled) { bond_choose_active_slave(bond); } revalidate = bond->bond_revalidate; bond->bond_revalidate = false; ovs_rwlock_unlock(&rwlock); return revalidate; } /* Causes poll_block() to wake up when 'bond' needs something to be done. */ void bond_wait(struct bond *bond) { struct bond_slave *slave; ovs_rwlock_rdlock(&rwlock); HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (slave->delay_expires != LLONG_MAX) { poll_timer_wait_until(slave->delay_expires); } seq_wait(connectivity_seq_get(), slave->change_seq); } if (bond->bond_revalidate) { poll_immediate_wake(); } ovs_rwlock_unlock(&rwlock); /* We don't wait for bond->next_rebalance because rebalancing can only run * at a flow account checkpoint. ofproto does checkpointing on its own * schedule and bond_rebalance() gets called afterward, so we'd just be * waking up for no purpose. */ } /* MAC learning table interaction. */ static bool may_send_learning_packets(const struct bond *bond) { return ((bond->lacp_status == LACP_DISABLED && (bond->balance == BM_SLB || bond->balance == BM_AB)) || (bond->lacp_fallback_ab && bond->lacp_status == LACP_CONFIGURED)) && bond->active_slave; } /* Returns true if 'bond' needs the client to send out packets to assist with * MAC learning on 'bond'. If this function returns true, then the client * should iterate through its MAC learning table for the bridge on which 'bond' * is located. For each MAC that has been learned on a port other than 'bond', * it should call bond_compose_learning_packet(). * * This function will only return true if 'bond' is in SLB or active-backup * mode and LACP is not negotiated. Otherwise sending learning packets isn't * necessary. * * Calling this function resets the state that it checks. */ bool bond_should_send_learning_packets(struct bond *bond) { bool send; ovs_rwlock_wrlock(&rwlock); send = bond->send_learning_packets && may_send_learning_packets(bond); bond->send_learning_packets = false; ovs_rwlock_unlock(&rwlock); return send; } /* Sends a gratuitous learning packet on 'bond' from 'eth_src' on 'vlan'. * * See bond_should_send_learning_packets() for description of usage. The * caller should send the composed packet on the port associated with * port_aux and takes ownership of the returned ofpbuf. */ struct dp_packet * bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src, uint16_t vlan, void **port_aux) { struct bond_slave *slave; struct dp_packet *packet; struct flow flow; ovs_rwlock_rdlock(&rwlock); ovs_assert(may_send_learning_packets(bond)); memset(&flow, 0, sizeof flow); flow.dl_src = eth_src; slave = choose_output_slave(bond, &flow, NULL, vlan); packet = dp_packet_new(0); compose_rarp(packet, eth_src); if (vlan) { eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan)); } *port_aux = slave->aux; ovs_rwlock_unlock(&rwlock); return packet; } static bool bond_is_falling_back_to_ab(const struct bond *bond) { return (bond->lacp_fallback_ab && (bond->balance == BM_SLB || bond->balance == BM_TCP) && bond->lacp_status == LACP_CONFIGURED); } /* Checks whether a packet that arrived on 'slave_' within 'bond', with an * Ethernet destination address of 'eth_dst', should be admitted. * * The return value is one of the following: * * - BV_ACCEPT: Admit the packet. * * - BV_DROP: Drop the packet. * * - BV_DROP_IF_MOVED: Consult the MAC learning table for the packet's * Ethernet source address and VLAN. If there is none, or if the packet * is on the learned port, then admit the packet. If a different port has * been learned, however, drop the packet (and do not use it for MAC * learning). */ enum bond_verdict bond_check_admissibility(struct bond *bond, const void *slave_, const struct eth_addr eth_dst) { enum bond_verdict verdict = BV_DROP; struct bond_slave *slave; ovs_rwlock_rdlock(&rwlock); slave = bond_slave_lookup(bond, slave_); if (!slave) { goto out; } /* LACP bonds have very loose admissibility restrictions because we can * assume the remote switch is aware of the bond and will "do the right * thing". However, as a precaution we drop packets on disabled slaves * because no correctly implemented partner switch should be sending * packets to them. * * If LACP is configured, but LACP negotiations have been unsuccessful, we * drop all incoming traffic except if lacp_fallback_ab is enabled. */ switch (bond->lacp_status) { case LACP_NEGOTIATED: verdict = slave->enabled ? BV_ACCEPT : BV_DROP; goto out; case LACP_CONFIGURED: if (!bond->lacp_fallback_ab) { goto out; } case LACP_DISABLED: break; } /* Drop all multicast packets on inactive slaves. */ if (eth_addr_is_multicast(eth_dst)) { if (bond->active_slave != slave) { goto out; } } switch (bond->balance) { case BM_TCP: /* TCP balanced bonds require successful LACP negotiations. Based on the * above check, LACP is off or lacp_fallback_ab is true on this bond. * If lacp_fallback_ab is true fall through to BM_AB case else, we * drop all incoming traffic. */ if (!bond->lacp_fallback_ab) { goto out; } case BM_AB: /* Drop all packets which arrive on backup slaves. This is similar to * how Linux bonding handles active-backup bonds. */ if (bond->active_slave != slave) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_DBG_RL(&rl, "active-backup bond received packet on backup" " slave (%s) destined for " ETH_ADDR_FMT, slave->name, ETH_ADDR_ARGS(eth_dst)); goto out; } verdict = BV_ACCEPT; goto out; case BM_SLB: /* Drop all packets for which we have learned a different input port, * because we probably sent the packet on one slave and got it back on * the other. Gratuitous ARP packets are an exception to this rule: * the host has moved to another switch. The exception to the * exception is if we locked the learning table to avoid reflections on * bond slaves. */ verdict = BV_DROP_IF_MOVED; goto out; } OVS_NOT_REACHED(); out: ovs_rwlock_unlock(&rwlock); return verdict; } /* Returns the slave (registered on 'bond' by bond_slave_register()) to which * a packet with the given 'flow' and 'vlan' should be forwarded. Returns * NULL if the packet should be dropped because no slaves are enabled. * * 'vlan' is not necessarily the same as 'flow->vlan_tci'. First, 'vlan' * should be a VID only (i.e. excluding the PCP bits). Second, * 'flow->vlan_tci' is the VLAN TCI that appeared on the packet (so it will be * nonzero only for trunk ports), whereas 'vlan' is the logical VLAN that the * packet belongs to (so for an access port it will be the access port's VLAN). * * If 'wc' is non-NULL, bitwise-OR's 'wc' with the set of bits that were * significant in the selection. At some point earlier, 'wc' should * have been initialized (e.g., by flow_wildcards_init_catchall()). */ void * bond_choose_output_slave(struct bond *bond, const struct flow *flow, struct flow_wildcards *wc, uint16_t vlan) { struct bond_slave *slave; void *aux; ovs_rwlock_rdlock(&rwlock); slave = choose_output_slave(bond, flow, wc, vlan); aux = slave ? slave->aux : NULL; ovs_rwlock_unlock(&rwlock); return aux; } /* Recirculation. */ static void bond_entry_account(struct bond_entry *entry, uint64_t rule_tx_bytes) OVS_REQ_WRLOCK(rwlock) { if (entry->slave) { uint64_t delta; delta = rule_tx_bytes - entry->pr_tx_bytes; entry->tx_bytes += delta; entry->pr_tx_bytes = rule_tx_bytes; } } /* Maintain bond stats using post recirculation rule byte counters.*/ static void bond_recirculation_account(struct bond *bond) OVS_REQ_WRLOCK(rwlock) { int i; for (i=0; i<=BOND_MASK; i++) { struct bond_entry *entry = &bond->hash[i]; struct rule *rule = entry->pr_rule; if (rule) { uint64_t n_packets OVS_UNUSED; long long int used OVS_UNUSED; uint64_t n_bytes; rule->ofproto->ofproto_class->rule_get_stats( rule, &n_packets, &n_bytes, &used); bond_entry_account(entry, n_bytes); } } } bool bond_may_recirc(const struct bond *bond, uint32_t *recirc_id, uint32_t *hash_bias) { bool may_recirc = (bond->balance == BM_TCP && bond->recirc_id && !bond_is_falling_back_to_ab(bond)); if (recirc_id) { *recirc_id = may_recirc ? bond->recirc_id : 0; } if (hash_bias) { *hash_bias = may_recirc ? bond->basis : 0; } return may_recirc; } static void bond_update_post_recirc_rules__(struct bond* bond, const bool force) OVS_REQ_WRLOCK(rwlock) { struct bond_entry *e; bool update_rules = force; /* Always update rules if caller forces it. */ /* Make sure all bond entries are populated */ for (e = bond->hash; e <= &bond->hash[BOND_MASK]; e++) { if (!e->slave || !e->slave->enabled) { update_rules = true; e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves), struct bond_slave, hmap_node); if (!e->slave->enabled) { e->slave = bond->active_slave; } } } if (update_rules) { update_recirc_rules(bond); } } void bond_update_post_recirc_rules(struct bond *bond, uint32_t *recirc_id, uint32_t *hash_basis) { ovs_rwlock_wrlock(&rwlock); if (bond_may_recirc(bond, recirc_id, hash_basis)) { bond_update_post_recirc_rules__(bond, false); } ovs_rwlock_unlock(&rwlock); } /* Rebalancing. */ static bool bond_is_balanced(const struct bond *bond) OVS_REQ_RDLOCK(rwlock) { return bond->rebalance_interval && (bond->balance == BM_SLB || bond->balance == BM_TCP) && !(bond->lacp_fallback_ab && bond->lacp_status == LACP_CONFIGURED); } /* Notifies 'bond' that 'n_bytes' bytes were sent in 'flow' within 'vlan'. */ void bond_account(struct bond *bond, const struct flow *flow, uint16_t vlan, uint64_t n_bytes) { ovs_rwlock_wrlock(&rwlock); if (bond_is_balanced(bond)) { lookup_bond_entry(bond, flow, vlan)->tx_bytes += n_bytes; } ovs_rwlock_unlock(&rwlock); } static struct bond_slave * bond_slave_from_bal_node(struct ovs_list *bal) OVS_REQ_RDLOCK(rwlock) { return CONTAINER_OF(bal, struct bond_slave, bal_node); } static void log_bals(struct bond *bond, const struct ovs_list *bals) OVS_REQ_RDLOCK(rwlock) { if (VLOG_IS_DBG_ENABLED()) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bond_slave *slave; LIST_FOR_EACH (slave, bal_node, bals) { if (ds.length) { ds_put_char(&ds, ','); } ds_put_format(&ds, " %s %"PRIu64"kB", slave->name, slave->tx_bytes / 1024); if (!slave->enabled) { ds_put_cstr(&ds, " (disabled)"); } if (!list_is_empty(&slave->entries)) { struct bond_entry *e; ds_put_cstr(&ds, " ("); LIST_FOR_EACH (e, list_node, &slave->entries) { if (&e->list_node != list_front(&slave->entries)) { ds_put_cstr(&ds, " + "); } ds_put_format(&ds, "h%"PRIdPTR": %"PRIu64"kB", e - bond->hash, e->tx_bytes / 1024); } ds_put_cstr(&ds, ")"); } } VLOG_DBG("bond %s:%s", bond->name, ds_cstr(&ds)); ds_destroy(&ds); } } /* Shifts 'hash' from its current slave to 'to'. */ static void bond_shift_load(struct bond_entry *hash, struct bond_slave *to) OVS_REQ_WRLOCK(rwlock) { struct bond_slave *from = hash->slave; struct bond *bond = from->bond; uint64_t delta = hash->tx_bytes; VLOG_INFO("bond %s: shift %"PRIu64"kB of load (with hash %"PRIdPTR") " "from %s to %s (now carrying %"PRIu64"kB and " "%"PRIu64"kB load, respectively)", bond->name, delta / 1024, hash - bond->hash, from->name, to->name, (from->tx_bytes - delta) / 1024, (to->tx_bytes + delta) / 1024); /* Shift load away from 'from' to 'to'. */ from->tx_bytes -= delta; to->tx_bytes += delta; /* Arrange for flows to be revalidated. */ hash->slave = to; bond->bond_revalidate = true; } /* Picks and returns a bond_entry to migrate from 'from' (the most heavily * loaded bond slave) to a bond slave that has 'to_tx_bytes' bytes of load, * given that doing so must decrease the ratio of the load on the two slaves by * at least 0.1. Returns NULL if there is no appropriate entry. * * The list of entries isn't sorted. I don't know of a reason to prefer to * shift away small hashes or large hashes. */ static struct bond_entry * choose_entry_to_migrate(const struct bond_slave *from, uint64_t to_tx_bytes) OVS_REQ_WRLOCK(rwlock) { struct bond_entry *e; if (list_is_short(&from->entries)) { /* 'from' carries no more than one MAC hash, so shifting load away from * it would be pointless. */ return NULL; } LIST_FOR_EACH (e, list_node, &from->entries) { uint64_t delta = e->tx_bytes; /* The amount to rebalance. */ uint64_t ideal_tx_bytes = (from->tx_bytes + to_tx_bytes)/2; /* Note, the ideal traffic is the mid point * between 'from' and 'to'. This value does * not change by rebalancing. */ uint64_t new_low; /* The lower bandwidth between 'to' and 'from' after rebalancing. */ new_low = MIN(from->tx_bytes - delta, to_tx_bytes + delta); if ((new_low > to_tx_bytes) && (new_low - to_tx_bytes >= (ideal_tx_bytes - to_tx_bytes) / 10)) { /* Only rebalance if the new 'low' is closer to to the mid point, * and the improvement exceeds 10% of current traffic * deviation from the ideal split. * * The improvement on the 'high' side is always the same as the * 'low' side. Thus consider 'low' side is sufficient. */ return e; } } return NULL; } /* Inserts 'slave' into 'bals' so that descending order of 'tx_bytes' is * maintained. */ static void insert_bal(struct ovs_list *bals, struct bond_slave *slave) { struct bond_slave *pos; LIST_FOR_EACH (pos, bal_node, bals) { if (slave->tx_bytes > pos->tx_bytes) { break; } } list_insert(&pos->bal_node, &slave->bal_node); } /* Removes 'slave' from its current list and then inserts it into 'bals' so * that descending order of 'tx_bytes' is maintained. */ static void reinsert_bal(struct ovs_list *bals, struct bond_slave *slave) { list_remove(&slave->bal_node); insert_bal(bals, slave); } /* If 'bond' needs rebalancing, does so. * * The caller should have called bond_account() for each active flow, or in case * of recirculation is used, have called bond_recirculation_account(bond), * to ensure that flow data is consistently accounted at this point. */ void bond_rebalance(struct bond *bond) { struct bond_slave *slave; struct bond_entry *e; struct ovs_list bals; bool rebalanced = false; bool use_recirc; ovs_rwlock_wrlock(&rwlock); if (!bond_is_balanced(bond) || time_msec() < bond->next_rebalance) { goto done; } bond->next_rebalance = time_msec() + bond->rebalance_interval; use_recirc = ofproto_dpif_get_support(bond->ofproto)->odp.recirc && bond_may_recirc(bond, NULL, NULL); if (use_recirc) { bond_recirculation_account(bond); } /* Add each bond_entry to its slave's 'entries' list. * Compute each slave's tx_bytes as the sum of its entries' tx_bytes. */ HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { slave->tx_bytes = 0; list_init(&slave->entries); } for (e = &bond->hash[0]; e <= &bond->hash[BOND_MASK]; e++) { if (e->slave && e->tx_bytes) { e->slave->tx_bytes += e->tx_bytes; list_push_back(&e->slave->entries, &e->list_node); } } /* Add enabled slaves to 'bals' in descending order of tx_bytes. * * XXX This is O(n**2) in the number of slaves but it could be O(n lg n) * with a proper list sort algorithm. */ list_init(&bals); HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (slave->enabled) { insert_bal(&bals, slave); } } log_bals(bond, &bals); /* Shift load from the most-loaded slaves to the least-loaded slaves. */ while (!list_is_short(&bals)) { struct bond_slave *from = bond_slave_from_bal_node(list_front(&bals)); struct bond_slave *to = bond_slave_from_bal_node(list_back(&bals)); uint64_t overload; overload = from->tx_bytes - to->tx_bytes; if (overload < to->tx_bytes >> 5 || overload < 100000) { /* The extra load on 'from' (and all less-loaded slaves), compared * to that of 'to' (the least-loaded slave), is less than ~3%, or * it is less than ~1Mbps. No point in rebalancing. */ break; } /* 'from' is carrying significantly more load than 'to'. Pick a hash * to move from 'from' to 'to'. */ e = choose_entry_to_migrate(from, to->tx_bytes); if (e) { bond_shift_load(e, to); /* Delete element from from->entries. * * We don't add the element to to->hashes. That would only allow * 'e' to be migrated to another slave in this rebalancing run, and * there is no point in doing that. */ list_remove(&e->list_node); /* Re-sort 'bals'. */ reinsert_bal(&bals, from); reinsert_bal(&bals, to); rebalanced = true; } else { /* Can't usefully migrate anything away from 'from'. * Don't reconsider it. */ list_remove(&from->bal_node); } } /* Implement exponentially weighted moving average. A weight of 1/2 causes * historical data to decay to <1% in 7 rebalancing runs. 1,000,000 bytes * take 20 rebalancing runs to decay to 0 and get deleted entirely. */ for (e = &bond->hash[0]; e <= &bond->hash[BOND_MASK]; e++) { e->tx_bytes /= 2; } if (use_recirc && rebalanced) { bond_update_post_recirc_rules__(bond,true); } done: ovs_rwlock_unlock(&rwlock); } /* Bonding unixctl user interface functions. */ static struct bond * bond_find(const char *name) OVS_REQ_RDLOCK(rwlock) { struct bond *bond; HMAP_FOR_EACH_WITH_HASH (bond, hmap_node, hash_string(name, 0), all_bonds) { if (!strcmp(bond->name, name)) { return bond; } } return NULL; } static struct bond_slave * bond_lookup_slave(struct bond *bond, const char *slave_name) { struct bond_slave *slave; HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (!strcmp(slave->name, slave_name)) { return slave; } } return NULL; } static void bond_unixctl_list(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct bond *bond; ds_put_cstr(&ds, "bond\ttype\trecircID\tslaves\n"); ovs_rwlock_rdlock(&rwlock); HMAP_FOR_EACH (bond, hmap_node, all_bonds) { const struct bond_slave *slave; size_t i; ds_put_format(&ds, "%s\t%s\t%d\t", bond->name, bond_mode_to_string(bond->balance), bond->recirc_id); i = 0; HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (i++ > 0) { ds_put_cstr(&ds, ", "); } ds_put_cstr(&ds, slave->name); } ds_put_char(&ds, '\n'); } ovs_rwlock_unlock(&rwlock); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void bond_print_details(struct ds *ds, const struct bond *bond) OVS_REQ_RDLOCK(rwlock) { struct shash slave_shash = SHASH_INITIALIZER(&slave_shash); const struct shash_node **sorted_slaves = NULL; const struct bond_slave *slave; bool may_recirc; uint32_t recirc_id; int i; ds_put_format(ds, "---- %s ----\n", bond->name); ds_put_format(ds, "bond_mode: %s\n", bond_mode_to_string(bond->balance)); may_recirc = bond_may_recirc(bond, &recirc_id, NULL); ds_put_format(ds, "bond may use recirculation: %s, Recirc-ID : %d\n", may_recirc ? "yes" : "no", may_recirc ? recirc_id: -1); ds_put_format(ds, "bond-hash-basis: %"PRIu32"\n", bond->basis); ds_put_format(ds, "updelay: %d ms\n", bond->updelay); ds_put_format(ds, "downdelay: %d ms\n", bond->downdelay); if (bond_is_balanced(bond)) { ds_put_format(ds, "next rebalance: %lld ms\n", bond->next_rebalance - time_msec()); } ds_put_cstr(ds, "lacp_status: "); switch (bond->lacp_status) { case LACP_NEGOTIATED: ds_put_cstr(ds, "negotiated\n"); break; case LACP_CONFIGURED: ds_put_cstr(ds, "configured\n"); break; case LACP_DISABLED: ds_put_cstr(ds, "off\n"); break; default: ds_put_cstr(ds, "\n"); break; } ds_put_cstr(ds, "active slave mac: "); ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(bond->active_slave_mac)); slave = bond_find_slave_by_mac(bond, bond->active_slave_mac); ds_put_format(ds,"(%s)\n", slave ? slave->name : "none"); HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { shash_add(&slave_shash, slave->name, slave); } sorted_slaves = shash_sort(&slave_shash); for (i = 0; i < shash_count(&slave_shash); i++) { struct bond_entry *be; slave = sorted_slaves[i]->data; /* Basic info. */ ds_put_format(ds, "\nslave %s: %s\n", slave->name, slave->enabled ? "enabled" : "disabled"); if (slave == bond->active_slave) { ds_put_cstr(ds, "\tactive slave\n"); } if (slave->delay_expires != LLONG_MAX) { ds_put_format(ds, "\t%s expires in %lld ms\n", slave->enabled ? "downdelay" : "updelay", slave->delay_expires - time_msec()); } ds_put_format(ds, "\tmay_enable: %s\n", slave->may_enable ? "true" : "false"); if (!bond_is_balanced(bond)) { continue; } /* Hashes. */ for (be = bond->hash; be <= &bond->hash[BOND_MASK]; be++) { int hash = be - bond->hash; uint64_t be_tx_k; if (be->slave != slave) { continue; } be_tx_k = be->tx_bytes / 1024; if (be_tx_k) { ds_put_format(ds, "\thash %d: %"PRIu64" kB load\n", hash, be_tx_k); } /* XXX How can we list the MACs assigned to hashes of SLB bonds? */ } } shash_destroy(&slave_shash); free(sorted_slaves); ds_put_cstr(ds, "\n"); } static void bond_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; ovs_rwlock_rdlock(&rwlock); if (argc > 1) { const struct bond *bond = bond_find(argv[1]); if (!bond) { unixctl_command_reply_error(conn, "no such bond"); goto out; } bond_print_details(&ds, bond); } else { const struct bond *bond; HMAP_FOR_EACH (bond, hmap_node, all_bonds) { bond_print_details(&ds, bond); } } unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); out: ovs_rwlock_unlock(&rwlock); } static void bond_unixctl_migrate(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { const char *bond_s = argv[1]; const char *hash_s = argv[2]; const char *slave_s = argv[3]; struct bond *bond; struct bond_slave *slave; struct bond_entry *entry; int hash; ovs_rwlock_wrlock(&rwlock); bond = bond_find(bond_s); if (!bond) { unixctl_command_reply_error(conn, "no such bond"); goto out; } if (bond->balance != BM_SLB) { unixctl_command_reply_error(conn, "not an SLB bond"); goto out; } if (strspn(hash_s, "0123456789") == strlen(hash_s)) { hash = atoi(hash_s) & BOND_MASK; } else { unixctl_command_reply_error(conn, "bad hash"); goto out; } slave = bond_lookup_slave(bond, slave_s); if (!slave) { unixctl_command_reply_error(conn, "no such slave"); goto out; } if (!slave->enabled) { unixctl_command_reply_error(conn, "cannot migrate to disabled slave"); goto out; } entry = &bond->hash[hash]; bond->bond_revalidate = true; entry->slave = slave; unixctl_command_reply(conn, "migrated"); out: ovs_rwlock_unlock(&rwlock); } static void bond_unixctl_set_active_slave(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { const char *bond_s = argv[1]; const char *slave_s = argv[2]; struct bond *bond; struct bond_slave *slave; ovs_rwlock_wrlock(&rwlock); bond = bond_find(bond_s); if (!bond) { unixctl_command_reply_error(conn, "no such bond"); goto out; } slave = bond_lookup_slave(bond, slave_s); if (!slave) { unixctl_command_reply_error(conn, "no such slave"); goto out; } if (!slave->enabled) { unixctl_command_reply_error(conn, "cannot make disabled slave active"); goto out; } if (bond->active_slave != slave) { bond->bond_revalidate = true; bond->active_slave = slave; VLOG_INFO("bond %s: active interface is now %s", bond->name, slave->name); bond->send_learning_packets = true; unixctl_command_reply(conn, "done"); bond_active_slave_changed(bond); } else { unixctl_command_reply(conn, "no change"); } out: ovs_rwlock_unlock(&rwlock); } static void enable_slave(struct unixctl_conn *conn, const char *argv[], bool enable) { const char *bond_s = argv[1]; const char *slave_s = argv[2]; struct bond *bond; struct bond_slave *slave; ovs_rwlock_wrlock(&rwlock); bond = bond_find(bond_s); if (!bond) { unixctl_command_reply_error(conn, "no such bond"); goto out; } slave = bond_lookup_slave(bond, slave_s); if (!slave) { unixctl_command_reply_error(conn, "no such slave"); goto out; } bond_enable_slave(slave, enable); unixctl_command_reply(conn, enable ? "enabled" : "disabled"); out: ovs_rwlock_unlock(&rwlock); } static void bond_unixctl_enable_slave(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { enable_slave(conn, argv, true); } static void bond_unixctl_disable_slave(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { enable_slave(conn, argv, false); } static void bond_unixctl_hash(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { const char *mac_s = argv[1]; const char *vlan_s = argc > 2 ? argv[2] : NULL; const char *basis_s = argc > 3 ? argv[3] : NULL; struct eth_addr mac; uint8_t hash; char *hash_cstr; unsigned int vlan; uint32_t basis; if (vlan_s) { if (!ovs_scan(vlan_s, "%u", &vlan)) { unixctl_command_reply_error(conn, "invalid vlan"); return; } } else { vlan = 0; } if (basis_s) { if (!ovs_scan(basis_s, "%"SCNu32, &basis)) { unixctl_command_reply_error(conn, "invalid basis"); return; } } else { basis = 0; } if (ovs_scan(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) { hash = bond_hash_src(mac, vlan, basis) & BOND_MASK; hash_cstr = xasprintf("%u", hash); unixctl_command_reply(conn, hash_cstr); free(hash_cstr); } else { unixctl_command_reply_error(conn, "invalid mac"); } } void bond_init(void) { unixctl_command_register("bond/list", "", 0, 0, bond_unixctl_list, NULL); unixctl_command_register("bond/show", "[port]", 0, 1, bond_unixctl_show, NULL); unixctl_command_register("bond/migrate", "port hash slave", 3, 3, bond_unixctl_migrate, NULL); unixctl_command_register("bond/set-active-slave", "port slave", 2, 2, bond_unixctl_set_active_slave, NULL); unixctl_command_register("bond/enable-slave", "port slave", 2, 2, bond_unixctl_enable_slave, NULL); unixctl_command_register("bond/disable-slave", "port slave", 2, 2, bond_unixctl_disable_slave, NULL); unixctl_command_register("bond/hash", "mac [vlan] [basis]", 1, 3, bond_unixctl_hash, NULL); } static void bond_entry_reset(struct bond *bond) { if (bond->balance != BM_AB && !bond_is_falling_back_to_ab(bond)) { size_t hash_len = BOND_BUCKETS * sizeof *bond->hash; if (!bond->hash) { bond->hash = xmalloc(hash_len); } memset(bond->hash, 0, hash_len); bond->next_rebalance = time_msec() + bond->rebalance_interval; } else { free(bond->hash); bond->hash = NULL; /* Remove existing post recirc rules. */ update_recirc_rules(bond); } } static struct bond_slave * bond_slave_lookup(struct bond *bond, const void *slave_) { struct bond_slave *slave; HMAP_FOR_EACH_IN_BUCKET (slave, hmap_node, hash_pointer(slave_, 0), &bond->slaves) { if (slave->aux == slave_) { return slave; } } return NULL; } static void bond_enable_slave(struct bond_slave *slave, bool enable) { slave->delay_expires = LLONG_MAX; if (enable != slave->enabled) { slave->bond->bond_revalidate = true; slave->enabled = enable; ovs_mutex_lock(&slave->bond->mutex); if (enable) { list_insert(&slave->bond->enabled_slaves, &slave->list_node); } else { list_remove(&slave->list_node); } ovs_mutex_unlock(&slave->bond->mutex); VLOG_INFO("interface %s: %s", slave->name, slave->enabled ? "enabled" : "disabled"); } } static void bond_link_status_update(struct bond_slave *slave) { struct bond *bond = slave->bond; bool up; up = netdev_get_carrier(slave->netdev) && slave->may_enable; if ((up == slave->enabled) != (slave->delay_expires == LLONG_MAX)) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); VLOG_INFO_RL(&rl, "interface %s: link state %s", slave->name, up ? "up" : "down"); if (up == slave->enabled) { slave->delay_expires = LLONG_MAX; VLOG_INFO_RL(&rl, "interface %s: will not be %s", slave->name, up ? "disabled" : "enabled"); } else { int delay = (bond->lacp_status != LACP_DISABLED ? 0 : up ? bond->updelay : bond->downdelay); slave->delay_expires = time_msec() + delay; if (delay) { VLOG_INFO_RL(&rl, "interface %s: will be %s if it stays %s " "for %d ms", slave->name, up ? "enabled" : "disabled", up ? "up" : "down", delay); } } } if (time_msec() >= slave->delay_expires) { bond_enable_slave(slave, up); } } static unsigned int bond_hash_src(const struct eth_addr mac, uint16_t vlan, uint32_t basis) { return hash_mac(mac, vlan, basis); } static unsigned int bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis) { struct flow hash_flow = *flow; hash_flow.vlan_tci = htons(vlan); /* The symmetric quality of this hash function is not required, but * flow_hash_symmetric_l4 already exists, and is sufficient for our * purposes, so we use it out of convenience. */ return flow_hash_symmetric_l4(&hash_flow, basis); } static unsigned int bond_hash(const struct bond *bond, const struct flow *flow, uint16_t vlan) { ovs_assert(bond->balance == BM_TCP || bond->balance == BM_SLB); return (bond->balance == BM_TCP ? bond_hash_tcp(flow, vlan, bond->basis) : bond_hash_src(flow->dl_src, vlan, bond->basis)); } static struct bond_entry * lookup_bond_entry(const struct bond *bond, const struct flow *flow, uint16_t vlan) { return &bond->hash[bond_hash(bond, flow, vlan) & BOND_MASK]; } /* Selects and returns an enabled slave from the 'enabled_slaves' list * in a round-robin fashion. If the 'enabled_slaves' list is empty, * returns NULL. */ static struct bond_slave * get_enabled_slave(struct bond *bond) { struct ovs_list *node; ovs_mutex_lock(&bond->mutex); if (list_is_empty(&bond->enabled_slaves)) { ovs_mutex_unlock(&bond->mutex); return NULL; } node = list_pop_front(&bond->enabled_slaves); list_push_back(&bond->enabled_slaves, node); ovs_mutex_unlock(&bond->mutex); return CONTAINER_OF(node, struct bond_slave, list_node); } static struct bond_slave * choose_output_slave(const struct bond *bond, const struct flow *flow, struct flow_wildcards *wc, uint16_t vlan) { struct bond_entry *e; int balance; balance = bond->balance; if (bond->lacp_status == LACP_CONFIGURED) { /* LACP has been configured on this bond but negotiations were * unsuccussful. If lacp_fallback_ab is enabled use active- * backup mode else drop all traffic. */ if (!bond->lacp_fallback_ab) { return NULL; } balance = BM_AB; } switch (balance) { case BM_AB: return bond->active_slave; case BM_TCP: if (bond->lacp_status != LACP_NEGOTIATED) { /* Must have LACP negotiations for TCP balanced bonds. */ return NULL; } if (wc) { flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4); } /* Fall Through. */ case BM_SLB: if (wc) { flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_ETH_SRC); } e = lookup_bond_entry(bond, flow, vlan); if (!e->slave || !e->slave->enabled) { e->slave = get_enabled_slave(CONST_CAST(struct bond*, bond)); } return e->slave; default: OVS_NOT_REACHED(); } } static struct bond_slave * bond_choose_slave(const struct bond *bond) { struct bond_slave *slave, *best; /* Find the last active slave. */ slave = bond_find_slave_by_mac(bond, bond->active_slave_mac); if (slave && slave->enabled) { return slave; } /* Find an enabled slave. */ HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (slave->enabled) { return slave; } } /* All interfaces are disabled. Find an interface that will be enabled * after its updelay expires. */ best = NULL; HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) { if (slave->delay_expires != LLONG_MAX && slave->may_enable && (!best || slave->delay_expires < best->delay_expires)) { best = slave; } } return best; } static void bond_choose_active_slave(struct bond *bond) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); struct bond_slave *old_active_slave = bond->active_slave; bond->active_slave = bond_choose_slave(bond); if (bond->active_slave) { if (bond->active_slave->enabled) { VLOG_INFO_RL(&rl, "bond %s: active interface is now %s", bond->name, bond->active_slave->name); } else { VLOG_INFO_RL(&rl, "bond %s: active interface is now %s, skipping " "remaining %lld ms updelay (since no interface was " "enabled)", bond->name, bond->active_slave->name, bond->active_slave->delay_expires - time_msec()); bond_enable_slave(bond->active_slave, true); } bond->send_learning_packets = true; if (bond->active_slave != old_active_slave) { bond_active_slave_changed(bond); } } else if (old_active_slave) { bond_active_slave_changed(bond); VLOG_INFO_RL(&rl, "bond %s: all interfaces disabled", bond->name); } } /* * Return true if bond has unstored active slave change. * If return true, 'mac' will store the bond's current active slave's * MAC address. */ bool bond_get_changed_active_slave(const char *name, struct eth_addr *mac, bool force) { struct bond *bond; ovs_rwlock_wrlock(&rwlock); bond = bond_find(name); if (bond) { if (bond->active_slave_changed || force) { *mac = bond->active_slave_mac; bond->active_slave_changed = false; ovs_rwlock_unlock(&rwlock); return true; } } ovs_rwlock_unlock(&rwlock); return false; } openvswitch-2.5.9/ofproto/PaxHeaders.82075/pinsched.c0000644000000000000000000000013013534540071017273 xustar0030 mtime=1567801401.673683167 28 atime=1567801402.1136864 30 ctime=1567801425.057855461 openvswitch-2.5.9/ofproto/pinsched.c0000644000175000017500000002066613534540071020775 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "pinsched.h" #include #include #include #include #include #include "flow.h" #include "hash.h" #include "hmap.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "random.h" #include "rconn.h" #include "sat-math.h" #include "timeval.h" #include "openvswitch/token-bucket.h" #include "openvswitch/vconn.h" struct pinqueue { struct hmap_node node; /* In struct pinsched's 'queues' hmap. */ ofp_port_t port_no; /* Port number. */ struct ovs_list packets; /* Contains "struct ofpbuf"s. */ int n; /* Number of packets in 'packets'. */ }; struct pinsched { struct token_bucket token_bucket; /* One queue per physical port. */ struct hmap queues; /* Contains "struct pinqueue"s. */ unsigned int n_queued; /* Sum over queues[*].n. */ struct pinqueue *next_txq; /* Next pinqueue check in round-robin. */ /* Statistics reporting. */ unsigned long long n_normal; /* # txed w/o rate limit queuing. */ unsigned long long n_limited; /* # queued for rate limiting. */ unsigned long long n_queue_dropped; /* # dropped due to queue overflow. */ }; static void advance_txq(struct pinsched *ps) { struct hmap_node *next; next = (ps->next_txq ? hmap_next(&ps->queues, &ps->next_txq->node) : hmap_first(&ps->queues)); ps->next_txq = next ? CONTAINER_OF(next, struct pinqueue, node) : NULL; } static struct ofpbuf * dequeue_packet(struct pinsched *ps, struct pinqueue *q) { struct ofpbuf *packet = ofpbuf_from_list(list_pop_front(&q->packets)); q->n--; ps->n_queued--; return packet; } static void adjust_limits(int *rate_limit, int *burst_limit) { if (*rate_limit <= 0) { *rate_limit = 1000; } if (*burst_limit <= 0) { *burst_limit = *rate_limit / 4; } if (*burst_limit < 1) { *burst_limit = 1; } } /* Destroys 'q' and removes it from 'ps''s set of queues. * (The caller must ensure that 'q' is empty.) */ static void pinqueue_destroy(struct pinsched *ps, struct pinqueue *q) { if (ps->next_txq == q) { advance_txq(ps); if (ps->next_txq == q) { ps->next_txq = NULL; } } hmap_remove(&ps->queues, &q->node); free(q); } static struct pinqueue * pinqueue_get(struct pinsched *ps, ofp_port_t port_no) { uint32_t hash = hash_ofp_port(port_no); struct pinqueue *q; HMAP_FOR_EACH_IN_BUCKET (q, node, hash, &ps->queues) { if (port_no == q->port_no) { return q; } } q = xmalloc(sizeof *q); hmap_insert(&ps->queues, &q->node, hash); q->port_no = port_no; list_init(&q->packets); q->n = 0; return q; } /* Drop a packet from the longest queue in 'ps'. */ static void drop_packet(struct pinsched *ps) { struct pinqueue *longest; /* Queue currently selected as longest. */ int n_longest = 0; /* # of queues of same length as 'longest'. */ struct pinqueue *q; ps->n_queue_dropped++; longest = NULL; HMAP_FOR_EACH (q, node, &ps->queues) { if (!longest || longest->n < q->n) { longest = q; n_longest = 1; } else if (longest->n == q->n) { n_longest++; /* Randomly select one of the longest queues, with a uniform * distribution (Knuth algorithm 3.4.2R). */ if (!random_range(n_longest)) { longest = q; } } } /* FIXME: do we want to pop the tail instead? */ ofpbuf_delete(dequeue_packet(ps, longest)); if (longest->n == 0) { pinqueue_destroy(ps, longest); } } /* Remove and return the next packet to transmit (in round-robin order). */ static struct ofpbuf * get_tx_packet(struct pinsched *ps) { struct ofpbuf *packet; struct pinqueue *q; if (!ps->next_txq) { advance_txq(ps); } q = ps->next_txq; packet = dequeue_packet(ps, q); advance_txq(ps); if (q->n == 0) { pinqueue_destroy(ps, q); } return packet; } /* Attempts to remove enough tokens from 'ps' to transmit a packet. Returns * true if successful, false otherwise. (In the latter case no tokens are * removed.) */ static bool get_token(struct pinsched *ps) { return token_bucket_withdraw(&ps->token_bucket, 1000); } void pinsched_send(struct pinsched *ps, ofp_port_t port_no, struct ofpbuf *packet, struct ovs_list *txq) { list_init(txq); if (!ps) { list_push_back(txq, &packet->list_node); } else if (!ps->n_queued && get_token(ps)) { /* In the common case where we are not constrained by the rate limit, * let the packet take the normal path. */ ps->n_normal++; list_push_back(txq, &packet->list_node); } else { /* Otherwise queue it up for the periodic callback to drain out. */ struct pinqueue *q; /* We might be called with a buffer obtained from dpif_recv() that has * much more allocated space than actual content most of the time. * Since we're going to store the packet for some time, free up that * otherwise wasted space. */ ofpbuf_trim(packet); if (ps->n_queued * 1000 >= ps->token_bucket.burst) { drop_packet(ps); } q = pinqueue_get(ps, port_no); list_push_back(&q->packets, &packet->list_node); q->n++; ps->n_queued++; ps->n_limited++; } } void pinsched_run(struct pinsched *ps, struct ovs_list *txq) { list_init(txq); if (ps) { int i; /* Drain some packets out of the bucket if possible, but limit the * number of iterations to allow other code to get work done too. */ for (i = 0; ps->n_queued && get_token(ps) && i < 50; i++) { struct ofpbuf *packet = get_tx_packet(ps); list_push_back(txq, &packet->list_node); } } } void pinsched_wait(struct pinsched *ps) { if (ps && ps->n_queued) { token_bucket_wait(&ps->token_bucket, 1000); } } /* Creates and returns a scheduler for sending packet-in messages. */ struct pinsched * pinsched_create(int rate_limit, int burst_limit) { struct pinsched *ps; ps = xzalloc(sizeof *ps); adjust_limits(&rate_limit, &burst_limit); token_bucket_init(&ps->token_bucket, rate_limit, sat_mul(burst_limit, 1000)); hmap_init(&ps->queues); ps->n_queued = 0; ps->next_txq = NULL; ps->n_normal = 0; ps->n_limited = 0; ps->n_queue_dropped = 0; return ps; } void pinsched_destroy(struct pinsched *ps) { if (ps) { struct pinqueue *q, *next; HMAP_FOR_EACH_SAFE (q, next, node, &ps->queues) { hmap_remove(&ps->queues, &q->node); ofpbuf_list_delete(&q->packets); free(q); } hmap_destroy(&ps->queues); free(ps); } } void pinsched_get_limits(const struct pinsched *ps, int *rate_limit, int *burst_limit) { *rate_limit = ps->token_bucket.rate; *burst_limit = ps->token_bucket.burst / 1000; } void pinsched_set_limits(struct pinsched *ps, int rate_limit, int burst_limit) { adjust_limits(&rate_limit, &burst_limit); token_bucket_set(&ps->token_bucket, rate_limit, sat_mul(burst_limit, 1000)); while (ps->n_queued > burst_limit) { drop_packet(ps); } } /* Retrieves statistics for 'ps'. The statistics will be all zero if 'ps' is * null. */ void pinsched_get_stats(const struct pinsched *ps, struct pinsched_stats *stats) { if (ps) { stats->n_queued = ps->n_queued; stats->n_normal = ps->n_normal; stats->n_limited = ps->n_limited; stats->n_queue_dropped = ps->n_queue_dropped; } else { memset(stats, 0, sizeof *stats); } } openvswitch-2.5.9/ofproto/PaxHeaders.82075/ofproto-dpif.c0000644000000000000000000000013113534540071020107 xustar0030 mtime=1567801401.649682991 29 atime=1567801402.10968637 30 ctime=1567801425.033855284 openvswitch-2.5.9/ofproto/ofproto-dpif.c0000644000175000017500000054732513534540071021616 0ustar00jpettitjpettit00000000000000/* * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "ofproto/ofproto-dpif.h" #include "ofproto/ofproto-provider.h" #include #include "bfd.h" #include "bond.h" #include "bundle.h" #include "byte-order.h" #include "connectivity.h" #include "connmgr.h" #include "coverage.h" #include "cfm.h" #include "ovs-lldp.h" #include "dpif.h" #include "dynamic-string.h" #include "fail-open.h" #include "guarded-list.h" #include "hmapx.h" #include "lacp.h" #include "learn.h" #include "mac-learning.h" #include "mcast-snooping.h" #include "meta-flow.h" #include "multipath.h" #include "netdev-vport.h" #include "netdev.h" #include "netlink.h" #include "nx-match.h" #include "odp-util.h" #include "odp-execute.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofp-actions.h" #include "ofp-parse.h" #include "ofp-print.h" #include "ofproto-dpif-ipfix.h" #include "ofproto-dpif-mirror.h" #include "ofproto-dpif-monitor.h" #include "ofproto-dpif-rid.h" #include "ofproto-dpif-sflow.h" #include "ofproto-dpif-upcall.h" #include "ofproto-dpif-xlate.h" #include "poll-loop.h" #include "ovs-rcu.h" #include "ovs-router.h" #include "seq.h" #include "simap.h" #include "smap.h" #include "timer.h" #include "tunnel.h" #include "unaligned.h" #include "unixctl.h" #include "vlan-bitmap.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(ofproto_dpif); COVERAGE_DEFINE(ofproto_dpif_expired); COVERAGE_DEFINE(packet_in_overflow); struct flow_miss; struct rule_dpif { struct rule up; /* These statistics: * * - Do include packets and bytes from datapath flows which have not * recently been processed by a revalidator. */ struct ovs_mutex stats_mutex; struct dpif_flow_stats stats OVS_GUARDED; /* In non-NULL, will point to a new rule (for which a reference is held) to * which all the stats updates should be forwarded. This exists only * transitionally when flows are replaced. * * Protected by stats_mutex. If both 'rule->stats_mutex' and * 'rule->new_rule->stats_mutex' must be held together, acquire them in that * order, */ struct rule_dpif *new_rule OVS_GUARDED; bool forward_counts OVS_GUARDED; /* Forward counts? 'used' time will be * forwarded in all cases. */ /* If non-zero then the recirculation id that has * been allocated for use with this rule. * The recirculation id and associated internal flow should * be freed when the rule is freed */ uint32_t recirc_id; }; /* RULE_CAST() depends on this. */ BUILD_ASSERT_DECL(offsetof(struct rule_dpif, up) == 0); static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes, long long int *used); static struct rule_dpif *rule_dpif_cast(const struct rule *); static void rule_expire(struct rule_dpif *); struct group_dpif { struct ofgroup up; /* These statistics: * * - Do include packets and bytes from datapath flows which have not * recently been processed by a revalidator. */ struct ovs_mutex stats_mutex; uint64_t packet_count OVS_GUARDED; /* Number of packets received. */ uint64_t byte_count OVS_GUARDED; /* Number of bytes received. */ }; struct ofbundle { struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */ struct ofproto_dpif *ofproto; /* Owning ofproto. */ void *aux; /* Key supplied by ofproto's client. */ char *name; /* Identifier for log messages. */ /* Configuration. */ struct ovs_list ports; /* Contains "struct ofport"s. */ enum port_vlan_mode vlan_mode; /* VLAN mode */ int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */ unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1. * NULL if all VLANs are trunked. */ struct lacp *lacp; /* LACP if LACP is enabled, otherwise NULL. */ struct bond *bond; /* Nonnull iff more than one port. */ bool use_priority_tags; /* Use 802.1p tag for frames in VLAN 0? */ /* Status. */ bool floodable; /* True if no port has OFPUTIL_PC_NO_FLOOD set. */ }; static void bundle_remove(struct ofport *); static void bundle_update(struct ofbundle *); static void bundle_destroy(struct ofbundle *); static void bundle_del_port(struct ofport_dpif *); static void bundle_run(struct ofbundle *); static void bundle_wait(struct ofbundle *); static void bundle_flush_macs(struct ofbundle *, bool); static void bundle_move(struct ofbundle *, struct ofbundle *); static void stp_run(struct ofproto_dpif *ofproto); static void stp_wait(struct ofproto_dpif *ofproto); static int set_stp_port(struct ofport *, const struct ofproto_port_stp_settings *); static void rstp_run(struct ofproto_dpif *ofproto); static void set_rstp_port(struct ofport *, const struct ofproto_port_rstp_settings *); struct ofport_dpif { struct hmap_node odp_port_node; /* In dpif_backer's "odp_to_ofport_map". */ struct ofport up; odp_port_t odp_port; struct ofbundle *bundle; /* Bundle that contains this port, if any. */ struct ovs_list bundle_node;/* In struct ofbundle's "ports" list. */ struct cfm *cfm; /* Connectivity Fault Management, if any. */ struct bfd *bfd; /* BFD, if any. */ struct lldp *lldp; /* lldp, if any. */ bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* This port is a tunnel. */ bool is_layer3; /* This is a layer 3 port. */ long long int carrier_seq; /* Carrier status changes. */ struct ofport_dpif *peer; /* Peer if patch port. */ /* Spanning tree. */ struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */ enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ long long int stp_state_entered; /* Rapid Spanning Tree. */ struct rstp_port *rstp_port; /* Rapid Spanning Tree Protocol, if any. */ enum rstp_state rstp_state; /* Always RSTP_DISABLED if RSTP not in use. */ /* Queue to DSCP mapping. */ struct ofproto_port_queue *qdscp; size_t n_qdscp; /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device * drivers in old versions of Linux that do not properly support VLANs when * VLAN devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ ofp_port_t realdev_ofp_port; int vlandev_vid; }; /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ struct vlan_splinter { struct hmap_node realdev_vid_node; struct hmap_node vlandev_node; ofp_port_t realdev_ofp_port; ofp_port_t vlandev_ofp_port; int vid; }; static void vsp_remove(struct ofport_dpif *); static void vsp_add(struct ofport_dpif *, ofp_port_t realdev_ofp_port, int vid); static odp_port_t ofp_port_to_odp_port(const struct ofproto_dpif *, ofp_port_t); static ofp_port_t odp_port_to_ofp_port(const struct ofproto_dpif *, odp_port_t); static struct ofport_dpif * ofport_dpif_cast(const struct ofport *ofport) { return ofport ? CONTAINER_OF(ofport, struct ofport_dpif, up) : NULL; } static void port_run(struct ofport_dpif *); static int set_bfd(struct ofport *, const struct smap *); static int set_cfm(struct ofport *, const struct cfm_settings *); static int set_lldp(struct ofport *ofport_, const struct smap *cfg); static void ofport_update_peer(struct ofport_dpif *); /* Reasons that we might need to revalidate every datapath flow, and * corresponding coverage counters. * * A value of 0 means that there is no need to revalidate. * * It would be nice to have some cleaner way to integrate with coverage * counters, but with only a few reasons I guess this is good enough for * now. */ enum revalidate_reason { REV_RECONFIGURE = 1, /* Switch configuration changed. */ REV_STP, /* Spanning tree protocol port status change. */ REV_RSTP, /* RSTP port status change. */ REV_BOND, /* Bonding changed. */ REV_PORT_TOGGLED, /* Port enabled or disabled by CFM, LACP, ...*/ REV_FLOW_TABLE, /* Flow table changed. */ REV_MAC_LEARNING, /* Mac learning changed. */ REV_MCAST_SNOOPING, /* Multicast snooping changed. */ }; COVERAGE_DEFINE(rev_reconfigure); COVERAGE_DEFINE(rev_stp); COVERAGE_DEFINE(rev_rstp); COVERAGE_DEFINE(rev_bond); COVERAGE_DEFINE(rev_port_toggled); COVERAGE_DEFINE(rev_flow_table); COVERAGE_DEFINE(rev_mac_learning); COVERAGE_DEFINE(rev_mcast_snooping); /* All datapaths of a given type share a single dpif backer instance. */ struct dpif_backer { char *type; int refcount; struct dpif *dpif; struct udpif *udpif; struct ovs_rwlock odp_to_ofport_lock; struct hmap odp_to_ofport_map OVS_GUARDED; /* Contains "struct ofport"s. */ struct simap tnl_backers; /* Set of dpif ports backing tunnels. */ enum revalidate_reason need_revalidate; /* Revalidate all flows. */ bool recv_set_enable; /* Enables or disables receiving packets. */ /* Version string of the datapath stored in OVSDB. */ char *dp_version_string; /* Datapath feature support. */ struct dpif_backer_support support; struct atomic_count tnl_count; }; /* All existing ofproto_backer instances, indexed by ofproto->up.type. */ static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers); struct ofproto_dpif { struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */ struct ofproto up; struct dpif_backer *backer; ATOMIC(cls_version_t) tables_version; /* For classifier lookups. */ uint64_t dump_seq; /* Last read of udpif_dump_seq(). */ /* Special OpenFlow rules. */ struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */ struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */ struct rule_dpif *drop_frags_rule; /* Used in OFPC_FRAG_DROP mode. */ /* Bridging. */ struct netflow *netflow; struct dpif_sflow *sflow; struct dpif_ipfix *ipfix; struct hmap bundles; /* Contains "struct ofbundle"s. */ struct mac_learning *ml; struct mcast_snooping *ms; bool has_bonded_bundles; bool lacp_enabled; struct mbridge *mbridge; struct ovs_mutex stats_mutex; struct netdev_stats stats OVS_GUARDED; /* To account packets generated and * consumed in userspace. */ /* Spanning tree. */ struct stp *stp; long long int stp_last_tick; /* Rapid Spanning Tree. */ struct rstp *rstp; long long int rstp_last_tick; /* VLAN splinters. */ struct ovs_mutex vsp_mutex; struct hmap realdev_vid_map OVS_GUARDED; /* (realdev,vid) -> vlandev. */ struct hmap vlandev_map OVS_GUARDED; /* vlandev -> (realdev,vid). */ /* Ports. */ struct sset ports; /* Set of standard port names. */ struct sset ghost_ports; /* Ports with no datapath port. */ struct sset port_poll_set; /* Queued names for port_poll() reply. */ int port_poll_errno; /* Last errno for port_poll() reply. */ uint64_t change_seq; /* Connectivity status changes. */ /* Work queues. */ struct guarded_list pins; /* Contains "struct ofputil_packet_in"s. */ struct seq *pins_seq; /* For notifying 'pins' reception. */ uint64_t pins_seqno; }; /* All existing ofproto_dpif instances, indexed by ->up.name. */ static struct hmap all_ofproto_dpifs = HMAP_INITIALIZER(&all_ofproto_dpifs); static bool ofproto_use_tnl_push_pop = true; static void ofproto_unixctl_init(void); static inline struct ofproto_dpif * ofproto_dpif_cast(const struct ofproto *ofproto) { ovs_assert(ofproto->ofproto_class == &ofproto_dpif_class); return CONTAINER_OF(ofproto, struct ofproto_dpif, up); } bool ofproto_dpif_get_enable_ufid(const struct dpif_backer *backer) { return backer->support.ufid; } struct dpif_backer_support * ofproto_dpif_get_support(const struct ofproto_dpif *ofproto) { return &ofproto->backer->support; } static void ofproto_trace(struct ofproto_dpif *, struct flow *, const struct dp_packet *packet, const struct ofpact[], size_t ofpacts_len, struct ds *); /* Global variables. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); /* Initial mappings of port to bridge mappings. */ static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports); /* Executes 'fm'. The caller retains ownership of 'fm' and everything in * it. */ void ofproto_dpif_flow_mod(struct ofproto_dpif *ofproto, const struct ofputil_flow_mod *fm) { struct ofproto_flow_mod ofm; /* Multiple threads may do this for the same 'fm' at the same time. * Allocate ofproto_flow_mod with execution context from stack. * * Note: This copy could be avoided by making ofproto_flow_mod more * complex, but that may not be desireable, and a learn action is not that * fast to begin with. */ ofm.fm = *fm; ofproto_flow_mod(&ofproto->up, &ofm); } /* Appends 'pin' to the queue of "packet ins" to be sent to the controller. * Takes ownership of 'pin' and pin->packet. */ void ofproto_dpif_send_packet_in(struct ofproto_dpif *ofproto, struct ofproto_packet_in *pin) { if (!guarded_list_push_back(&ofproto->pins, &pin->list_node, 1024)) { COVERAGE_INC(packet_in_overflow); free(CONST_CAST(void *, pin->up.packet)); free(pin); } /* Wakes up main thread for packet-in I/O. */ seq_change(ofproto->pins_seq); } /* The default "table-miss" behaviour for OpenFlow1.3+ is to drop the * packet rather than to send the packet to the controller. * * This function returns false to indicate that a packet_in message * for a "table-miss" should be sent to at least one controller. * False otherwise. */ bool ofproto_dpif_wants_packet_in_on_miss(struct ofproto_dpif *ofproto) { return connmgr_wants_packet_in_on_miss(ofproto->up.connmgr); } /* Factory functions. */ static void init(const struct shash *iface_hints) { struct shash_node *node; /* Make a local copy, since we don't own 'iface_hints' elements. */ SHASH_FOR_EACH(node, iface_hints) { const struct iface_hint *orig_hint = node->data; struct iface_hint *new_hint = xmalloc(sizeof *new_hint); new_hint->br_name = xstrdup(orig_hint->br_name); new_hint->br_type = xstrdup(orig_hint->br_type); new_hint->ofp_port = orig_hint->ofp_port; shash_add(&init_ofp_ports, node->name, new_hint); } ofproto_unixctl_init(); udpif_init(); } static void enumerate_types(struct sset *types) { dp_enumerate_types(types); } static int enumerate_names(const char *type, struct sset *names) { struct ofproto_dpif *ofproto; sset_clear(names); HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (strcmp(type, ofproto->up.type)) { continue; } sset_add(names, ofproto->up.name); } return 0; } static int del(const char *type, const char *name) { struct dpif *dpif; int error; error = dpif_open(name, type, &dpif); if (!error) { error = dpif_delete(dpif); dpif_close(dpif); } return error; } static const char * port_open_type(const char *datapath_type, const char *port_type) { return dpif_port_open_type(datapath_type, port_type); } /* Type functions. */ static void process_dpif_port_changes(struct dpif_backer *); static void process_dpif_all_ports_changed(struct dpif_backer *); static void process_dpif_port_change(struct dpif_backer *, const char *devname); static void process_dpif_port_error(struct dpif_backer *, int error); static struct ofproto_dpif * lookup_ofproto_dpif_by_port_name(const char *name) { struct ofproto_dpif *ofproto; HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (sset_contains(&ofproto->ports, name)) { return ofproto; } } return NULL; } bool ofproto_dpif_backer_enabled(struct dpif_backer* backer) { return backer->recv_set_enable; } static int type_run(const char *type) { struct dpif_backer *backer; backer = shash_find_data(&all_dpif_backers, type); if (!backer) { /* This is not necessarily a problem, since backers are only * created on demand. */ return 0; } if (dpif_run(backer->dpif)) { backer->need_revalidate = REV_RECONFIGURE; } udpif_run(backer->udpif); /* If vswitchd started with other_config:flow_restore_wait set as "true", * and the configuration has now changed to "false", enable receiving * packets from the datapath. */ if (!backer->recv_set_enable && !ofproto_get_flow_restore_wait()) { int error; backer->recv_set_enable = true; error = dpif_recv_set(backer->dpif, backer->recv_set_enable); if (error) { VLOG_ERR("Failed to enable receiving packets in dpif."); return error; } dpif_flow_flush(backer->dpif); backer->need_revalidate = REV_RECONFIGURE; } if (backer->recv_set_enable) { udpif_set_threads(backer->udpif, n_handlers, n_revalidators); } dpif_poll_threads_set(backer->dpif, n_dpdk_rxqs, pmd_cpu_mask); if (backer->need_revalidate) { struct ofproto_dpif *ofproto; struct simap_node *node; struct simap tmp_backers; /* Handle tunnel garbage collection. */ simap_init(&tmp_backers); simap_swap(&backer->tnl_backers, &tmp_backers); HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { struct ofport_dpif *iter; if (backer != ofproto->backer) { continue; } HMAP_FOR_EACH (iter, up.hmap_node, &ofproto->up.ports) { char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dp_port; if (!iter->is_tunnel) { continue; } dp_port = netdev_vport_get_dpif_port(iter->up.netdev, namebuf, sizeof namebuf); node = simap_find(&tmp_backers, dp_port); if (node) { simap_put(&backer->tnl_backers, dp_port, node->data); simap_delete(&tmp_backers, node); node = simap_find(&backer->tnl_backers, dp_port); } else { node = simap_find(&backer->tnl_backers, dp_port); if (!node) { odp_port_t odp_port = ODPP_NONE; if (!dpif_port_add(backer->dpif, iter->up.netdev, &odp_port)) { simap_put(&backer->tnl_backers, dp_port, odp_to_u32(odp_port)); node = simap_find(&backer->tnl_backers, dp_port); } } } iter->odp_port = node ? u32_to_odp(node->data) : ODPP_NONE; if (tnl_port_reconfigure(iter, iter->up.netdev, iter->odp_port, ovs_native_tunneling_is_on(ofproto), dp_port)) { backer->need_revalidate = REV_RECONFIGURE; } } } SIMAP_FOR_EACH (node, &tmp_backers) { dpif_port_del(backer->dpif, u32_to_odp(node->data)); } simap_destroy(&tmp_backers); switch (backer->need_revalidate) { case REV_RECONFIGURE: COVERAGE_INC(rev_reconfigure); break; case REV_STP: COVERAGE_INC(rev_stp); break; case REV_RSTP: COVERAGE_INC(rev_rstp); break; case REV_BOND: COVERAGE_INC(rev_bond); break; case REV_PORT_TOGGLED: COVERAGE_INC(rev_port_toggled); break; case REV_FLOW_TABLE: COVERAGE_INC(rev_flow_table); break; case REV_MAC_LEARNING: COVERAGE_INC(rev_mac_learning); break; case REV_MCAST_SNOOPING: COVERAGE_INC(rev_mcast_snooping); break; } backer->need_revalidate = 0; HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { struct ofport_dpif *ofport; struct ofbundle *bundle; if (ofproto->backer != backer) { continue; } xlate_txn_start(); xlate_ofproto_set(ofproto, ofproto->up.name, ofproto->backer->dpif, ofproto->ml, ofproto->stp, ofproto->rstp, ofproto->ms, ofproto->mbridge, ofproto->sflow, ofproto->ipfix, ofproto->netflow, ofproto->up.forward_bpdu, connmgr_has_in_band(ofproto->up.connmgr), &ofproto->backer->support); HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { xlate_bundle_set(ofproto, bundle, bundle->name, bundle->vlan_mode, bundle->vlan, bundle->trunks, bundle->use_priority_tags, bundle->bond, bundle->lacp, bundle->floodable); } HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { int stp_port = ofport->stp_port ? stp_port_no(ofport->stp_port) : -1; xlate_ofport_set(ofproto, ofport->bundle, ofport, ofport->up.ofp_port, ofport->odp_port, ofport->up.netdev, ofport->cfm, ofport->bfd, ofport->lldp, ofport->peer, stp_port, ofport->rstp_port, ofport->qdscp, ofport->n_qdscp, ofport->up.pp.config, ofport->up.pp.state, ofport->is_tunnel, ofport->may_enable); } xlate_txn_commit(); } udpif_revalidate(backer->udpif); } process_dpif_port_changes(backer); return 0; } /* Check for and handle port changes in 'backer''s dpif. */ static void process_dpif_port_changes(struct dpif_backer *backer) { for (;;) { char *devname; int error; error = dpif_port_poll(backer->dpif, &devname); switch (error) { case EAGAIN: return; case ENOBUFS: process_dpif_all_ports_changed(backer); break; case 0: process_dpif_port_change(backer, devname); free(devname); break; default: process_dpif_port_error(backer, error); break; } } } static void process_dpif_all_ports_changed(struct dpif_backer *backer) { struct ofproto_dpif *ofproto; struct dpif_port dpif_port; struct dpif_port_dump dump; struct sset devnames; const char *devname; sset_init(&devnames); HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (ofproto->backer == backer) { struct ofport *ofport; HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { sset_add(&devnames, netdev_get_name(ofport->netdev)); } } } DPIF_PORT_FOR_EACH (&dpif_port, &dump, backer->dpif) { sset_add(&devnames, dpif_port.name); } SSET_FOR_EACH (devname, &devnames) { process_dpif_port_change(backer, devname); } sset_destroy(&devnames); } static void process_dpif_port_change(struct dpif_backer *backer, const char *devname) { struct ofproto_dpif *ofproto; struct dpif_port port; /* Don't report on the datapath's device. */ if (!strcmp(devname, dpif_base_name(backer->dpif))) { return; } HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (simap_contains(&ofproto->backer->tnl_backers, devname)) { return; } } ofproto = lookup_ofproto_dpif_by_port_name(devname); if (dpif_port_query_by_name(backer->dpif, devname, &port)) { /* The port was removed. If we know the datapath, * report it through poll_set(). If we don't, it may be * notifying us of a removal we initiated, so ignore it. * If there's a pending ENOBUFS, let it stand, since * everything will be reevaluated. */ if (ofproto && ofproto->port_poll_errno != ENOBUFS) { sset_add(&ofproto->port_poll_set, devname); ofproto->port_poll_errno = 0; } } else if (!ofproto) { /* The port was added, but we don't know with which * ofproto we should associate it. Delete it. */ dpif_port_del(backer->dpif, port.port_no); } else { struct ofport_dpif *ofport; ofport = ofport_dpif_cast(shash_find_data( &ofproto->up.port_by_name, devname)); if (ofport && ofport->odp_port != port.port_no && !odp_port_to_ofport(backer, port.port_no)) { /* 'ofport''s datapath port number has changed from * 'ofport->odp_port' to 'port.port_no'. Update our internal data * structures to match. */ ovs_rwlock_wrlock(&backer->odp_to_ofport_lock); hmap_remove(&backer->odp_to_ofport_map, &ofport->odp_port_node); ofport->odp_port = port.port_no; hmap_insert(&backer->odp_to_ofport_map, &ofport->odp_port_node, hash_odp_port(port.port_no)); ovs_rwlock_unlock(&backer->odp_to_ofport_lock); backer->need_revalidate = REV_RECONFIGURE; } } dpif_port_destroy(&port); } /* Propagate 'error' to all ofprotos based on 'backer'. */ static void process_dpif_port_error(struct dpif_backer *backer, int error) { struct ofproto_dpif *ofproto; HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (ofproto->backer == backer) { sset_clear(&ofproto->port_poll_set); ofproto->port_poll_errno = error; } } } static void type_wait(const char *type) { struct dpif_backer *backer; backer = shash_find_data(&all_dpif_backers, type); if (!backer) { /* This is not necessarily a problem, since backers are only * created on demand. */ return; } dpif_wait(backer->dpif); } /* Basic life-cycle. */ static int add_internal_flows(struct ofproto_dpif *); static struct ofproto * alloc(void) { struct ofproto_dpif *ofproto = xzalloc(sizeof *ofproto); return &ofproto->up; } static void dealloc(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); free(ofproto); } static void close_dpif_backer(struct dpif_backer *backer) { ovs_assert(backer->refcount > 0); if (--backer->refcount) { return; } udpif_destroy(backer->udpif); simap_destroy(&backer->tnl_backers); ovs_rwlock_destroy(&backer->odp_to_ofport_lock); hmap_destroy(&backer->odp_to_ofport_map); shash_find_and_delete(&all_dpif_backers, backer->type); free(backer->type); free(backer->dp_version_string); dpif_close(backer->dpif); free(backer); } /* Datapath port slated for removal from datapath. */ struct odp_garbage { struct ovs_list list_node; odp_port_t odp_port; }; static bool check_variable_length_userdata(struct dpif_backer *backer); static void check_support(struct dpif_backer *backer); static int open_dpif_backer(const char *type, struct dpif_backer **backerp) { struct dpif_backer *backer; struct dpif_port_dump port_dump; struct dpif_port port; struct shash_node *node; struct ovs_list garbage_list; struct odp_garbage *garbage; struct sset names; char *backer_name; const char *name; int error; recirc_init(); backer = shash_find_data(&all_dpif_backers, type); if (backer) { backer->refcount++; *backerp = backer; return 0; } backer_name = xasprintf("ovs-%s", type); /* Remove any existing datapaths, since we assume we're the only * userspace controlling the datapath. */ sset_init(&names); dp_enumerate_names(type, &names); SSET_FOR_EACH(name, &names) { struct dpif *old_dpif; /* Don't remove our backer if it exists. */ if (!strcmp(name, backer_name)) { continue; } if (dpif_open(name, type, &old_dpif)) { VLOG_WARN("couldn't open old datapath %s to remove it", name); } else { dpif_delete(old_dpif); dpif_close(old_dpif); } } sset_destroy(&names); backer = xmalloc(sizeof *backer); error = dpif_create_and_open(backer_name, type, &backer->dpif); free(backer_name); if (error) { VLOG_ERR("failed to open datapath of type %s: %s", type, ovs_strerror(error)); free(backer); return error; } backer->udpif = udpif_create(backer, backer->dpif); backer->type = xstrdup(type); backer->refcount = 1; hmap_init(&backer->odp_to_ofport_map); ovs_rwlock_init(&backer->odp_to_ofport_lock); backer->need_revalidate = 0; simap_init(&backer->tnl_backers); backer->recv_set_enable = !ofproto_get_flow_restore_wait(); *backerp = backer; if (backer->recv_set_enable) { dpif_flow_flush(backer->dpif); } /* Loop through the ports already on the datapath and remove any * that we don't need anymore. */ list_init(&garbage_list); dpif_port_dump_start(&port_dump, backer->dpif); while (dpif_port_dump_next(&port_dump, &port)) { node = shash_find(&init_ofp_ports, port.name); if (!node && strcmp(port.name, dpif_base_name(backer->dpif))) { garbage = xmalloc(sizeof *garbage); garbage->odp_port = port.port_no; list_push_front(&garbage_list, &garbage->list_node); } } dpif_port_dump_done(&port_dump); LIST_FOR_EACH_POP (garbage, list_node, &garbage_list) { dpif_port_del(backer->dpif, garbage->odp_port); free(garbage); } shash_add(&all_dpif_backers, type, backer); check_support(backer); atomic_count_init(&backer->tnl_count, 0); error = dpif_recv_set(backer->dpif, backer->recv_set_enable); if (error) { VLOG_ERR("failed to listen on datapath of type %s: %s", type, ovs_strerror(error)); close_dpif_backer(backer); return error; } if (backer->recv_set_enable) { udpif_set_threads(backer->udpif, n_handlers, n_revalidators); } /* This check fails if performed before udpif threads have been set, * as the kernel module checks that the 'pid' in userspace action * is non-zero. */ backer->support.variable_length_userdata = check_variable_length_userdata(backer); backer->dp_version_string = dpif_get_dp_version(backer->dpif); return error; } bool ovs_native_tunneling_is_on(struct ofproto_dpif *ofproto) { return ofproto_use_tnl_push_pop && ofproto->backer->support.tnl_push_pop && atomic_count_get(&ofproto->backer->tnl_count); } /* Tests whether 'backer''s datapath supports recirculation. Only newer * datapaths support OVS_KEY_ATTR_RECIRC_ID in keys. We need to disable some * features on older datapaths that don't support this feature. * * Returns false if 'backer' definitely does not support recirculation, true if * it seems to support recirculation or if at least the error we get is * ambiguous. */ static bool check_recirc(struct dpif_backer *backer) { struct flow flow; struct odputil_keybuf keybuf; struct ofpbuf key; bool enable_recirc; struct odp_flow_key_parms odp_parms = { .flow = &flow, .support = { .recirc = true, }, }; memset(&flow, 0, sizeof flow); flow.recirc_id = 1; flow.dp_hash = 1; ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&odp_parms, &key); enable_recirc = dpif_probe_feature(backer->dpif, "recirculation", &key, NULL); if (enable_recirc) { VLOG_INFO("%s: Datapath supports recirculation", dpif_name(backer->dpif)); } else { VLOG_INFO("%s: Datapath does not support recirculation", dpif_name(backer->dpif)); } return enable_recirc; } /* Tests whether 'dpif' supports unique flow ids. We can skip serializing * some flow attributes for datapaths that support this feature. * * Returns true if 'dpif' supports UFID for flow operations. * Returns false if 'dpif' does not support UFID. */ static bool check_ufid(struct dpif_backer *backer) { struct flow flow; struct odputil_keybuf keybuf; struct ofpbuf key; ovs_u128 ufid; bool enable_ufid; struct odp_flow_key_parms odp_parms = { .flow = &flow, }; memset(&flow, 0, sizeof flow); flow.dl_type = htons(0x1234); ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&odp_parms, &key); dpif_flow_hash(backer->dpif, key.data, key.size, &ufid); enable_ufid = dpif_probe_feature(backer->dpif, "UFID", &key, &ufid); if (enable_ufid) { VLOG_INFO("%s: Datapath supports unique flow ids", dpif_name(backer->dpif)); } else { VLOG_INFO("%s: Datapath does not support unique flow ids", dpif_name(backer->dpif)); } return enable_ufid; } /* Tests whether 'backer''s datapath supports variable-length * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. We need * to disable some features on older datapaths that don't support this * feature. * * Returns false if 'backer' definitely does not support variable-length * userdata, true if it seems to support them or if at least the error we get * is ambiguous. */ static bool check_variable_length_userdata(struct dpif_backer *backer) { struct eth_header *eth; struct ofpbuf actions; struct dpif_execute execute; struct dp_packet packet; size_t start; int error; /* Compose a userspace action that will cause an ERANGE error on older * datapaths that don't support variable-length userdata. * * We really test for using userdata longer than 8 bytes, but older * datapaths accepted these, silently truncating the userdata to 8 bytes. * The same older datapaths rejected userdata shorter than 8 bytes, so we * test for that instead as a proxy for longer userdata support. */ ofpbuf_init(&actions, 64); start = nl_msg_start_nested(&actions, OVS_ACTION_ATTR_USERSPACE); nl_msg_put_u32(&actions, OVS_USERSPACE_ATTR_PID, dpif_port_get_pid(backer->dpif, ODPP_NONE, 0)); nl_msg_put_unspec_zero(&actions, OVS_USERSPACE_ATTR_USERDATA, 4); nl_msg_end_nested(&actions, start); /* Compose a dummy ethernet packet. */ dp_packet_init(&packet, ETH_HEADER_LEN); eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN); eth->eth_type = htons(0x1234); /* Execute the actions. On older datapaths this fails with ERANGE, on * newer datapaths it succeeds. */ execute.actions = actions.data; execute.actions_len = actions.size; execute.packet = &packet; execute.needs_help = false; execute.probe = true; execute.mtu = 0; error = dpif_execute(backer->dpif, &execute); dp_packet_uninit(&packet); ofpbuf_uninit(&actions); switch (error) { case 0: return true; case ERANGE: /* Variable-length userdata is not supported. */ VLOG_WARN("%s: datapath does not support variable-length userdata " "feature (needs Linux 3.10+ or kernel module from OVS " "1..11+). The NXAST_SAMPLE action will be ignored.", dpif_name(backer->dpif)); return false; default: /* Something odd happened. We're not sure whether variable-length * userdata is supported. Default to "yes". */ VLOG_WARN("%s: variable-length userdata feature probe failed (%s)", dpif_name(backer->dpif), ovs_strerror(error)); return true; } } /* Tests the MPLS label stack depth supported by 'backer''s datapath. * * Returns the number of elements in a struct flow's mpls_lse field * if the datapath supports at least that many entries in an * MPLS label stack. * Otherwise returns the number of MPLS push actions supported by * the datapath. */ static size_t check_max_mpls_depth(struct dpif_backer *backer) { struct flow flow; int n; for (n = 0; n < FLOW_MAX_MPLS_LABELS; n++) { struct odputil_keybuf keybuf; struct ofpbuf key; struct odp_flow_key_parms odp_parms = { .flow = &flow, }; memset(&flow, 0, sizeof flow); flow.dl_type = htons(ETH_TYPE_MPLS); flow_set_mpls_bos(&flow, n, 1); ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&odp_parms, &key); if (!dpif_probe_feature(backer->dpif, "MPLS", &key, NULL)) { break; } } VLOG_INFO("%s: MPLS label stack length probed as %d", dpif_name(backer->dpif), n); return n; } /* Tests whether 'backer''s datapath supports masked data in * OVS_ACTION_ATTR_SET actions. We need to disable some features on older * datapaths that don't support this feature. */ static bool check_masked_set_action(struct dpif_backer *backer) { struct eth_header *eth; struct ofpbuf actions; struct dpif_execute execute; struct dp_packet packet; int error; struct ovs_key_ethernet key, mask; /* Compose a set action that will cause an EINVAL error on older * datapaths that don't support masked set actions. * Avoid using a full mask, as it could be translated to a non-masked * set action instead. */ ofpbuf_init(&actions, 64); memset(&key, 0x53, sizeof key); memset(&mask, 0x7f, sizeof mask); commit_masked_set_action(&actions, OVS_KEY_ATTR_ETHERNET, &key, &mask, sizeof key); /* Compose a dummy ethernet packet. */ dp_packet_init(&packet, ETH_HEADER_LEN); eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN); eth->eth_type = htons(0x1234); /* Execute the actions. On older datapaths this fails with EINVAL, on * newer datapaths it succeeds. */ execute.actions = actions.data; execute.actions_len = actions.size; execute.packet = &packet; execute.needs_help = false; execute.probe = true; execute.mtu = 0; error = dpif_execute(backer->dpif, &execute); dp_packet_uninit(&packet); ofpbuf_uninit(&actions); if (error) { /* Masked set action is not supported. */ VLOG_INFO("%s: datapath does not support masked set action feature.", dpif_name(backer->dpif)); } return !error; } #define CHECK_FEATURE__(NAME, FIELD) \ static bool \ check_##NAME(struct dpif_backer *backer) \ { \ struct flow flow; \ struct odputil_keybuf keybuf; \ struct ofpbuf key; \ bool enable; \ struct odp_flow_key_parms odp_parms = { \ .flow = &flow, \ .support = { \ .NAME = true, \ }, \ }; \ \ memset(&flow, 0, sizeof flow); \ flow.FIELD = 1; \ \ ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); \ odp_flow_key_from_flow(&odp_parms, &key); \ enable = dpif_probe_feature(backer->dpif, #NAME, &key, NULL); \ \ if (enable) { \ VLOG_INFO("%s: Datapath supports "#NAME, dpif_name(backer->dpif)); \ } else { \ VLOG_INFO("%s: Datapath does not support "#NAME, \ dpif_name(backer->dpif)); \ } \ \ return enable; \ } #define CHECK_FEATURE(FIELD) CHECK_FEATURE__(FIELD, FIELD) CHECK_FEATURE(ct_state) CHECK_FEATURE(ct_zone) CHECK_FEATURE(ct_mark) CHECK_FEATURE__(ct_label, ct_label.u64.lo) #undef CHECK_FEATURE #undef CHECK_FEATURE__ static void check_support(struct dpif_backer *backer) { /* This feature needs to be tested after udpif threads are set. */ backer->support.variable_length_userdata = false; backer->support.odp.recirc = check_recirc(backer); backer->support.odp.max_mpls_depth = check_max_mpls_depth(backer); backer->support.masked_set_action = check_masked_set_action(backer); backer->support.ufid = check_ufid(backer); backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif); backer->support.odp.ct_state = check_ct_state(backer); backer->support.odp.ct_zone = check_ct_zone(backer); backer->support.odp.ct_mark = check_ct_mark(backer); backer->support.odp.ct_label = check_ct_label(backer); } static int construct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct shash_node *node, *next; int error; /* Tunnel module can get used right after the udpif threads are running. */ ofproto_tunnel_init(); error = open_dpif_backer(ofproto->up.type, &ofproto->backer); if (error) { return error; } atomic_init(&ofproto->tables_version, CLS_MIN_VERSION); ofproto->netflow = NULL; ofproto->sflow = NULL; ofproto->ipfix = NULL; ofproto->stp = NULL; ofproto->rstp = NULL; ofproto->dump_seq = 0; hmap_init(&ofproto->bundles); ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME); ofproto->ms = NULL; ofproto->mbridge = mbridge_create(); ofproto->has_bonded_bundles = false; ofproto->lacp_enabled = false; ovs_mutex_init_adaptive(&ofproto->stats_mutex); ovs_mutex_init(&ofproto->vsp_mutex); guarded_list_init(&ofproto->pins); hmap_init(&ofproto->vlandev_map); hmap_init(&ofproto->realdev_vid_map); sset_init(&ofproto->ports); sset_init(&ofproto->ghost_ports); sset_init(&ofproto->port_poll_set); ofproto->port_poll_errno = 0; ofproto->change_seq = 0; ofproto->pins_seq = seq_create(); ofproto->pins_seqno = seq_read(ofproto->pins_seq); SHASH_FOR_EACH_SAFE (node, next, &init_ofp_ports) { struct iface_hint *iface_hint = node->data; if (!strcmp(iface_hint->br_name, ofproto->up.name)) { /* Check if the datapath already has this port. */ if (dpif_port_exists(ofproto->backer->dpif, node->name)) { sset_add(&ofproto->ports, node->name); } free(iface_hint->br_name); free(iface_hint->br_type); free(iface_hint); shash_delete(&init_ofp_ports, node); } } hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node, hash_string(ofproto->up.name, 0)); memset(&ofproto->stats, 0, sizeof ofproto->stats); ofproto_init_tables(ofproto_, N_TABLES); error = add_internal_flows(ofproto); ofproto->up.tables[TBL_INTERNAL].flags = OFTABLE_HIDDEN | OFTABLE_READONLY; return error; } static int add_internal_miss_flow(struct ofproto_dpif *ofproto, int id, const struct ofpbuf *ofpacts, struct rule_dpif **rulep) { struct match match; int error; struct rule *rule; match_init_catchall(&match); match_set_reg(&match, 0, id); error = ofproto_dpif_add_internal_flow(ofproto, &match, 0, 0, ofpacts, &rule); *rulep = error ? NULL : rule_dpif_cast(rule); return error; } static int add_internal_flows(struct ofproto_dpif *ofproto) { struct ofpact_controller *controller; uint64_t ofpacts_stub[128 / 8]; struct ofpbuf ofpacts; struct rule *unused_rulep OVS_UNUSED; struct match match; int error; int id; ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); id = 1; controller = ofpact_put_CONTROLLER(&ofpacts); controller->max_len = UINT16_MAX; controller->controller_id = 0; controller->reason = OFPR_NO_MATCH; ofpact_pad(&ofpacts); error = add_internal_miss_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule); if (error) { return error; } ofpbuf_clear(&ofpacts); error = add_internal_miss_flow(ofproto, id++, &ofpacts, &ofproto->no_packet_in_rule); if (error) { return error; } error = add_internal_miss_flow(ofproto, id++, &ofpacts, &ofproto->drop_frags_rule); if (error) { return error; } /* Drop any run away non-recirc rule lookups. Recirc_id has to be * zero when reaching this rule. * * (priority=2), recirc_id=0, actions=drop */ ofpbuf_clear(&ofpacts); match_init_catchall(&match); match_set_recirc_id(&match, 0); error = ofproto_dpif_add_internal_flow(ofproto, &match, 2, 0, &ofpacts, &unused_rulep); return error; } static void destruct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofproto_packet_in *pin; struct rule_dpif *rule; struct oftable *table; struct ovs_list pins; ofproto->backer->need_revalidate = REV_RECONFIGURE; xlate_txn_start(); xlate_remove_ofproto(ofproto); xlate_txn_commit(); /* Ensure that the upcall processing threads have no remaining references * to the ofproto or anything in it. */ udpif_synchronize(ofproto->backer->udpif); hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node); OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { CLS_FOR_EACH (rule, up.cr, &table->cls) { ofproto_rule_delete(&ofproto->up, &rule->up); } } ofproto_group_delete_all(&ofproto->up); guarded_list_pop_all(&ofproto->pins, &pins); LIST_FOR_EACH_POP (pin, list_node, &pins) { free(CONST_CAST(void *, pin->up.packet)); free(pin); } guarded_list_destroy(&ofproto->pins); recirc_free_ofproto(ofproto, ofproto->up.name); mbridge_unref(ofproto->mbridge); netflow_unref(ofproto->netflow); dpif_sflow_unref(ofproto->sflow); dpif_ipfix_unref(ofproto->ipfix); hmap_destroy(&ofproto->bundles); mac_learning_unref(ofproto->ml); mcast_snooping_unref(ofproto->ms); stp_unref(ofproto->stp); rstp_unref(ofproto->rstp); hmap_destroy(&ofproto->vlandev_map); hmap_destroy(&ofproto->realdev_vid_map); sset_destroy(&ofproto->ports); sset_destroy(&ofproto->ghost_ports); sset_destroy(&ofproto->port_poll_set); ovs_mutex_destroy(&ofproto->stats_mutex); ovs_mutex_destroy(&ofproto->vsp_mutex); seq_destroy(ofproto->pins_seq); close_dpif_backer(ofproto->backer); } static int run(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); uint64_t new_seq, new_dump_seq; if (mbridge_need_revalidate(ofproto->mbridge)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); mcast_snooping_mdb_flush(ofproto->ms); } /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during * flow restore. Even though nothing is processed during flow restore, * all queued 'pins' will be handled immediately when flow restore * completes. */ ofproto->pins_seqno = seq_read(ofproto->pins_seq); /* Do not perform any periodic activity required by 'ofproto' while * waiting for flow restore to complete. */ if (!ofproto_get_flow_restore_wait()) { struct ofproto_packet_in *pin; struct ovs_list pins; guarded_list_pop_all(&ofproto->pins, &pins); LIST_FOR_EACH_POP (pin, list_node, &pins) { connmgr_send_packet_in(ofproto->up.connmgr, pin); free(CONST_CAST(void *, pin->up.packet)); free(pin); } } if (ofproto->netflow) { netflow_run(ofproto->netflow); } if (ofproto->sflow) { dpif_sflow_run(ofproto->sflow); } if (ofproto->ipfix) { dpif_ipfix_run(ofproto->ipfix); } new_seq = seq_read(connectivity_seq_get()); if (ofproto->change_seq != new_seq) { struct ofport_dpif *ofport; HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { port_run(ofport); } ofproto->change_seq = new_seq; } if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) { struct ofbundle *bundle; HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { bundle_run(bundle); } } stp_run(ofproto); rstp_run(ofproto); ovs_rwlock_wrlock(&ofproto->ml->rwlock); if (mac_learning_run(ofproto->ml)) { ofproto->backer->need_revalidate = REV_MAC_LEARNING; } ovs_rwlock_unlock(&ofproto->ml->rwlock); if (mcast_snooping_run(ofproto->ms)) { ofproto->backer->need_revalidate = REV_MCAST_SNOOPING; } new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif)); if (ofproto->dump_seq != new_dump_seq) { struct rule *rule, *next_rule; /* We know stats are relatively fresh, so now is a good time to do some * periodic work. */ ofproto->dump_seq = new_dump_seq; /* Expire OpenFlow flows whose idle_timeout or hard_timeout * has passed. */ ovs_mutex_lock(&ofproto_mutex); LIST_FOR_EACH_SAFE (rule, next_rule, expirable, &ofproto->up.expirable) { rule_expire(rule_dpif_cast(rule)); } ovs_mutex_unlock(&ofproto_mutex); /* All outstanding data in existing flows has been accounted, so it's a * good time to do bond rebalancing. */ if (ofproto->has_bonded_bundles) { struct ofbundle *bundle; HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { if (bundle->bond) { bond_rebalance(bundle->bond); } } } } return 0; } static void wait(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (ofproto_get_flow_restore_wait()) { return; } if (ofproto->sflow) { dpif_sflow_wait(ofproto->sflow); } if (ofproto->ipfix) { dpif_ipfix_wait(ofproto->ipfix); } if (ofproto->lacp_enabled || ofproto->has_bonded_bundles) { struct ofbundle *bundle; HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { bundle_wait(bundle); } } if (ofproto->netflow) { netflow_wait(ofproto->netflow); } ovs_rwlock_rdlock(&ofproto->ml->rwlock); mac_learning_wait(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); mcast_snooping_wait(ofproto->ms); stp_wait(ofproto); if (ofproto->backer->need_revalidate) { poll_immediate_wake(); } seq_wait(udpif_dump_seq(ofproto->backer->udpif), ofproto->dump_seq); seq_wait(ofproto->pins_seq, ofproto->pins_seqno); } static void type_get_memory_usage(const char *type, struct simap *usage) { struct dpif_backer *backer; backer = shash_find_data(&all_dpif_backers, type); if (backer) { udpif_get_memory_usage(backer->udpif, usage); } } static void flush(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_backer *backer = ofproto->backer; if (backer) { udpif_flush(backer->udpif); } } static void query_tables(struct ofproto *ofproto, struct ofputil_table_features *features, struct ofputil_table_stats *stats) { strcpy(features->name, "classifier"); if (stats) { int i; for (i = 0; i < ofproto->n_tables; i++) { unsigned long missed, matched; atomic_read_relaxed(&ofproto->tables[i].n_matched, &matched); atomic_read_relaxed(&ofproto->tables[i].n_missed, &missed); stats[i].matched_count = matched; stats[i].lookup_count = matched + missed; } } } static void set_tables_version(struct ofproto *ofproto_, cls_version_t version) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); /* Use memory_order_release to signify that any prior memory accesses can * not be reordered to happen after this atomic store. This makes sure the * new version is properly set up when the readers can read this 'version' * value. */ atomic_store_explicit(&ofproto->tables_version, version, memory_order_release); } static struct ofport * port_alloc(void) { struct ofport_dpif *port = xzalloc(sizeof *port); return &port->up; } static void port_dealloc(struct ofport *port_) { struct ofport_dpif *port = ofport_dpif_cast(port_); free(port); } static int port_construct(struct ofport *port_) { struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); const struct netdev *netdev = port->up.netdev; char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dp_port_name; struct dpif_port dpif_port; int error; ofproto->backer->need_revalidate = REV_RECONFIGURE; port->bundle = NULL; port->cfm = NULL; port->bfd = NULL; port->lldp = NULL; port->may_enable = false; port->stp_port = NULL; port->stp_state = STP_DISABLED; port->rstp_port = NULL; port->rstp_state = RSTP_DISABLED; port->is_tunnel = false; port->peer = NULL; port->qdscp = NULL; port->n_qdscp = 0; port->realdev_ofp_port = 0; port->vlandev_vid = 0; port->carrier_seq = netdev_get_carrier_resets(netdev); port->is_layer3 = netdev_vport_is_layer3(netdev); if (netdev_vport_is_patch(netdev)) { /* By bailing out here, we don't submit the port to the sFlow module * to be considered for counter polling export. This is correct * because the patch port represents an interface that sFlow considers * to be "internal" to the switch as a whole, and therefore not a * candidate for counter polling. */ port->odp_port = ODPP_NONE; ofport_update_peer(port); return 0; } dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); error = dpif_port_query_by_name(ofproto->backer->dpif, dp_port_name, &dpif_port); if (error) { return error; } port->odp_port = dpif_port.port_no; if (netdev_get_tunnel_config(netdev)) { atomic_count_inc(&ofproto->backer->tnl_count); error = tnl_port_add(port, port->up.netdev, port->odp_port, ovs_native_tunneling_is_on(ofproto), dp_port_name); if (error) { atomic_count_dec(&ofproto->backer->tnl_count); dpif_port_destroy(&dpif_port); return error; } port->is_tunnel = true; if (ofproto->ipfix) { dpif_ipfix_add_tunnel_port(ofproto->ipfix, port_, port->odp_port); } } else { /* Sanity-check that a mapping doesn't already exist. This * shouldn't happen for non-tunnel ports. */ if (odp_port_to_ofp_port(ofproto, port->odp_port) != OFPP_NONE) { VLOG_ERR("port %s already has an OpenFlow port number", dpif_port.name); dpif_port_destroy(&dpif_port); return EBUSY; } ovs_rwlock_wrlock(&ofproto->backer->odp_to_ofport_lock); hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node, hash_odp_port(port->odp_port)); ovs_rwlock_unlock(&ofproto->backer->odp_to_ofport_lock); } dpif_port_destroy(&dpif_port); if (ofproto->sflow) { dpif_sflow_add_port(ofproto->sflow, port_, port->odp_port); } return 0; } static void port_destruct(struct ofport *port_, bool del) { struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); const char *devname = netdev_get_name(port->up.netdev); char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dp_port_name; ofproto->backer->need_revalidate = REV_RECONFIGURE; xlate_txn_start(); xlate_ofport_remove(port); xlate_txn_commit(); dp_port_name = netdev_vport_get_dpif_port(port->up.netdev, namebuf, sizeof namebuf); if (del && dpif_port_exists(ofproto->backer->dpif, dp_port_name)) { /* The underlying device is still there, so delete it. This * happens when the ofproto is being destroyed, since the caller * assumes that removal of attached ports will happen as part of * destruction. */ if (!port->is_tunnel) { dpif_port_del(ofproto->backer->dpif, port->odp_port); } } if (port->peer) { port->peer->peer = NULL; port->peer = NULL; } if (port->odp_port != ODPP_NONE && !port->is_tunnel) { ovs_rwlock_wrlock(&ofproto->backer->odp_to_ofport_lock); hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node); ovs_rwlock_unlock(&ofproto->backer->odp_to_ofport_lock); } if (port->is_tunnel) { atomic_count_dec(&ofproto->backer->tnl_count); } if (port->is_tunnel && ofproto->ipfix) { dpif_ipfix_del_tunnel_port(ofproto->ipfix, port->odp_port); } tnl_port_del(port); sset_find_and_delete(&ofproto->ports, devname); sset_find_and_delete(&ofproto->ghost_ports, devname); bundle_remove(port_); set_cfm(port_, NULL); set_bfd(port_, NULL); set_lldp(port_, NULL); if (port->stp_port) { stp_port_disable(port->stp_port); } set_rstp_port(port_, NULL); if (ofproto->sflow) { dpif_sflow_del_port(ofproto->sflow, port->odp_port); } free(port->qdscp); } static void port_modified(struct ofport *port_) { struct ofport_dpif *port = ofport_dpif_cast(port_); char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dp_port_name; struct netdev *netdev = port->up.netdev; if (port->bundle && port->bundle->bond) { bond_slave_set_netdev(port->bundle->bond, port, netdev); } if (port->cfm) { cfm_set_netdev(port->cfm, netdev); } if (port->bfd) { bfd_set_netdev(port->bfd, netdev); } ofproto_dpif_monitor_port_update(port, port->bfd, port->cfm, port->lldp, &port->up.pp.hw_addr); dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); if (port->is_tunnel) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); if (tnl_port_reconfigure(port, netdev, port->odp_port, ovs_native_tunneling_is_on(ofproto), dp_port_name)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } } ofport_update_peer(port); } static void port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config) { struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); enum ofputil_port_config changed = old_config ^ port->up.pp.config; if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP | OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD | OFPUTIL_PC_NO_PACKET_IN)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) { bundle_update(port->bundle); } } } static int set_sflow(struct ofproto *ofproto_, const struct ofproto_sflow_options *sflow_options) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_sflow *ds = ofproto->sflow; if (sflow_options) { uint32_t old_probability = ds ? dpif_sflow_get_probability(ds) : 0; if (!ds) { struct ofport_dpif *ofport; ds = ofproto->sflow = dpif_sflow_create(); HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port); } } dpif_sflow_set_options(ds, sflow_options); if (dpif_sflow_get_probability(ds) != old_probability) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } } else { if (ds) { dpif_sflow_unref(ds); ofproto->backer->need_revalidate = REV_RECONFIGURE; ofproto->sflow = NULL; } } return 0; } static int set_ipfix( struct ofproto *ofproto_, const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options, const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options, size_t n_flow_exporters_options) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_ipfix *di = ofproto->ipfix; bool has_options = bridge_exporter_options || flow_exporters_options; bool new_di = false; if (has_options && !di) { di = ofproto->ipfix = dpif_ipfix_create(); new_di = true; } if (di) { /* Call set_options in any case to cleanly flush the flow * caches in the last exporters that are to be destroyed. */ dpif_ipfix_set_options( di, bridge_exporter_options, flow_exporters_options, n_flow_exporters_options); /* Add tunnel ports only when a new ipfix created */ if (new_di == true) { struct ofport_dpif *ofport; HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) { if (ofport->is_tunnel == true) { dpif_ipfix_add_tunnel_port(di, &ofport->up, ofport->odp_port); } } } if (!has_options) { dpif_ipfix_unref(di); ofproto->ipfix = NULL; } } return 0; } static int set_cfm(struct ofport *ofport_, const struct cfm_settings *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct cfm *old = ofport->cfm; int error = 0; if (s) { if (!ofport->cfm) { ofport->cfm = cfm_create(ofport->up.netdev); } if (cfm_configure(ofport->cfm, s)) { error = 0; goto out; } error = EINVAL; } cfm_unref(ofport->cfm); ofport->cfm = NULL; out: if (ofport->cfm != old) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } ofproto_dpif_monitor_port_update(ofport, ofport->bfd, ofport->cfm, ofport->lldp, &ofport->up.pp.hw_addr); return error; } static bool cfm_status_changed(struct ofport *ofport_) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); return ofport->cfm ? cfm_check_status_change(ofport->cfm) : true; } static int get_cfm_status(const struct ofport *ofport_, struct cfm_status *status) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); int ret = 0; if (ofport->cfm) { cfm_get_status(ofport->cfm, status); } else { ret = ENOENT; } return ret; } static int set_bfd(struct ofport *ofport_, const struct smap *cfg) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto); struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct bfd *old; old = ofport->bfd; ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), cfg, ofport->up.netdev); if (ofport->bfd != old) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } ofproto_dpif_monitor_port_update(ofport, ofport->bfd, ofport->cfm, ofport->lldp, &ofport->up.pp.hw_addr); return 0; } static bool bfd_status_changed(struct ofport *ofport_) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); return ofport->bfd ? bfd_check_status_change(ofport->bfd) : true; } static int get_bfd_status(struct ofport *ofport_, struct smap *smap) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); int ret = 0; if (ofport->bfd) { bfd_get_status(ofport->bfd, smap); } else { ret = ENOENT; } return ret; } static int set_lldp(struct ofport *ofport_, const struct smap *cfg) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); int error = 0; if (cfg) { if (!ofport->lldp) { struct ofproto_dpif *ofproto; ofproto = ofproto_dpif_cast(ofport->up.ofproto); ofproto->backer->need_revalidate = REV_RECONFIGURE; ofport->lldp = lldp_create(ofport->up.netdev, ofport_->mtu, cfg); } if (!lldp_configure(ofport->lldp, cfg)) { error = EINVAL; } } if (error) { lldp_unref(ofport->lldp); ofport->lldp = NULL; } ofproto_dpif_monitor_port_update(ofport, ofport->bfd, ofport->cfm, ofport->lldp, &ofport->up.pp.hw_addr); return error; } static bool get_lldp_status(const struct ofport *ofport_, struct lldp_status *status OVS_UNUSED) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); return ofport->lldp ? true : false; } static int set_aa(struct ofproto *ofproto OVS_UNUSED, const struct aa_settings *s) { return aa_configure(s); } static int aa_mapping_set(struct ofproto *ofproto_ OVS_UNUSED, void *aux, const struct aa_mapping_settings *s) { return aa_mapping_register(aux, s); } static int aa_mapping_unset(struct ofproto *ofproto OVS_UNUSED, void *aux) { return aa_mapping_unregister(aux); } static int aa_vlan_get_queued(struct ofproto *ofproto OVS_UNUSED, struct ovs_list *list) { return aa_get_vlan_queued(list); } static unsigned int aa_vlan_get_queue_size(struct ofproto *ofproto OVS_UNUSED) { return aa_get_vlan_queue_size(); } /* Spanning Tree. */ /* Called while rstp_mutex is held. */ static void rstp_send_bpdu_cb(struct dp_packet *pkt, void *ofport_, void *ofproto_) { struct ofproto_dpif *ofproto = ofproto_; struct ofport_dpif *ofport = ofport_; struct eth_header *eth = dp_packet_l2(pkt); netdev_get_etheraddr(ofport->up.netdev, ð->eth_src); if (eth_addr_is_zero(eth->eth_src)) { VLOG_WARN_RL(&rl, "%s port %d: cannot send RSTP BPDU on a port which " "does not have a configured source MAC address.", ofproto->up.name, ofp_to_u16(ofport->up.ofp_port)); } else { ofproto_dpif_send_packet(ofport, pkt); } dp_packet_delete(pkt); } static void send_bpdu_cb(struct dp_packet *pkt, int port_num, void *ofproto_) { struct ofproto_dpif *ofproto = ofproto_; struct stp_port *sp = stp_get_port(ofproto->stp, port_num); struct ofport_dpif *ofport; ofport = stp_port_get_aux(sp); if (!ofport) { VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d", ofproto->up.name, port_num); } else { struct eth_header *eth = dp_packet_l2(pkt); netdev_get_etheraddr(ofport->up.netdev, ð->eth_src); if (eth_addr_is_zero(eth->eth_src)) { VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " "with unknown MAC", ofproto->up.name, port_num); } else { ofproto_dpif_send_packet(ofport, pkt); } } dp_packet_delete(pkt); } /* Configure RSTP on 'ofproto_' using the settings defined in 's'. */ static void set_rstp(struct ofproto *ofproto_, const struct ofproto_rstp_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); /* Only revalidate flows if the configuration changed. */ if (!s != !ofproto->rstp) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } if (s) { if (!ofproto->rstp) { ofproto->rstp = rstp_create(ofproto_->name, s->address, rstp_send_bpdu_cb, ofproto); ofproto->rstp_last_tick = time_msec(); } rstp_set_bridge_address(ofproto->rstp, s->address); rstp_set_bridge_priority(ofproto->rstp, s->priority); rstp_set_bridge_ageing_time(ofproto->rstp, s->ageing_time); rstp_set_bridge_force_protocol_version(ofproto->rstp, s->force_protocol_version); rstp_set_bridge_max_age(ofproto->rstp, s->bridge_max_age); rstp_set_bridge_forward_delay(ofproto->rstp, s->bridge_forward_delay); rstp_set_bridge_transmit_hold_count(ofproto->rstp, s->transmit_hold_count); } else { struct ofport *ofport; HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { set_rstp_port(ofport, NULL); } rstp_unref(ofproto->rstp); ofproto->rstp = NULL; } } static void get_rstp_status(struct ofproto *ofproto_, struct ofproto_rstp_status *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (ofproto->rstp) { s->enabled = true; s->root_id = rstp_get_root_id(ofproto->rstp); s->bridge_id = rstp_get_bridge_id(ofproto->rstp); s->designated_id = rstp_get_designated_id(ofproto->rstp); s->root_path_cost = rstp_get_root_path_cost(ofproto->rstp); s->designated_port_id = rstp_get_designated_port_id(ofproto->rstp); s->bridge_port_id = rstp_get_bridge_port_id(ofproto->rstp); } else { s->enabled = false; } } static void update_rstp_port_state(struct ofport_dpif *ofport) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); enum rstp_state state; /* Figure out new state. */ state = ofport->rstp_port ? rstp_port_get_state(ofport->rstp_port) : RSTP_DISABLED; /* Update state. */ if (ofport->rstp_state != state) { enum ofputil_port_state of_state; bool fwd_change; VLOG_DBG("port %s: RSTP state changed from %s to %s", netdev_get_name(ofport->up.netdev), rstp_state_name(ofport->rstp_state), rstp_state_name(state)); if (rstp_learn_in_state(ofport->rstp_state) != rstp_learn_in_state(state)) { /* XXX: Learning action flows should also be flushed. */ if (ofport->bundle) { if (!rstp_shift_root_learned_address(ofproto->rstp) || rstp_get_old_root_aux(ofproto->rstp) != ofport) { bundle_flush_macs(ofport->bundle, false); } } } fwd_change = rstp_forward_in_state(ofport->rstp_state) != rstp_forward_in_state(state); ofproto->backer->need_revalidate = REV_RSTP; ofport->rstp_state = state; if (fwd_change && ofport->bundle) { bundle_update(ofport->bundle); } /* Update the RSTP state bits in the OpenFlow port description. */ of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK; of_state |= (state == RSTP_LEARNING ? OFPUTIL_PS_STP_LEARN : state == RSTP_FORWARDING ? OFPUTIL_PS_STP_FORWARD : state == RSTP_DISCARDING ? OFPUTIL_PS_STP_LISTEN : 0); ofproto_port_set_state(&ofport->up, of_state); } } static void rstp_run(struct ofproto_dpif *ofproto) { if (ofproto->rstp) { long long int now = time_msec(); long long int elapsed = now - ofproto->rstp_last_tick; struct rstp_port *rp; struct ofport_dpif *ofport; /* Every second, decrease the values of the timers. */ if (elapsed >= 1000) { rstp_tick_timers(ofproto->rstp); ofproto->rstp_last_tick = now; } rp = NULL; while ((ofport = rstp_get_next_changed_port_aux(ofproto->rstp, &rp))) { update_rstp_port_state(ofport); } rp = NULL; ofport = NULL; /* FIXME: This check should be done on-event (i.e., when setting * p->fdb_flush) and not periodically. */ while ((ofport = rstp_check_and_reset_fdb_flush(ofproto->rstp, &rp))) { if (!rstp_shift_root_learned_address(ofproto->rstp) || rstp_get_old_root_aux(ofproto->rstp) != ofport) { bundle_flush_macs(ofport->bundle, false); } } if (rstp_shift_root_learned_address(ofproto->rstp)) { struct ofport_dpif *old_root_aux = (struct ofport_dpif *)rstp_get_old_root_aux(ofproto->rstp); struct ofport_dpif *new_root_aux = (struct ofport_dpif *)rstp_get_new_root_aux(ofproto->rstp); if (old_root_aux != NULL && new_root_aux != NULL) { bundle_move(old_root_aux->bundle, new_root_aux->bundle); rstp_reset_root_changed(ofproto->rstp); } } } } /* Configures STP on 'ofproto_' using the settings defined in 's'. */ static int set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); /* Only revalidate flows if the configuration changed. */ if (!s != !ofproto->stp) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } if (s) { if (!ofproto->stp) { ofproto->stp = stp_create(ofproto_->name, s->system_id, send_bpdu_cb, ofproto); ofproto->stp_last_tick = time_msec(); } stp_set_bridge_id(ofproto->stp, s->system_id); stp_set_bridge_priority(ofproto->stp, s->priority); stp_set_hello_time(ofproto->stp, s->hello_time); stp_set_max_age(ofproto->stp, s->max_age); stp_set_forward_delay(ofproto->stp, s->fwd_delay); } else { struct ofport *ofport; HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { set_stp_port(ofport, NULL); } stp_unref(ofproto->stp); ofproto->stp = NULL; } return 0; } static int get_stp_status(struct ofproto *ofproto_, struct ofproto_stp_status *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (ofproto->stp) { s->enabled = true; s->bridge_id = stp_get_bridge_id(ofproto->stp); s->designated_root = stp_get_designated_root(ofproto->stp); s->root_path_cost = stp_get_root_path_cost(ofproto->stp); } else { s->enabled = false; } return 0; } static void update_stp_port_state(struct ofport_dpif *ofport) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); enum stp_state state; /* Figure out new state. */ state = ofport->stp_port ? stp_port_get_state(ofport->stp_port) : STP_DISABLED; /* Update state. */ if (ofport->stp_state != state) { enum ofputil_port_state of_state; bool fwd_change; VLOG_DBG("port %s: STP state changed from %s to %s", netdev_get_name(ofport->up.netdev), stp_state_name(ofport->stp_state), stp_state_name(state)); if (stp_learn_in_state(ofport->stp_state) != stp_learn_in_state(state)) { /* xxx Learning action flows should also be flushed. */ ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); mcast_snooping_mdb_flush(ofproto->ms); } fwd_change = stp_forward_in_state(ofport->stp_state) != stp_forward_in_state(state); ofproto->backer->need_revalidate = REV_STP; ofport->stp_state = state; ofport->stp_state_entered = time_msec(); if (fwd_change && ofport->bundle) { bundle_update(ofport->bundle); } /* Update the STP state bits in the OpenFlow port description. */ of_state = ofport->up.pp.state & ~OFPUTIL_PS_STP_MASK; of_state |= (state == STP_LISTENING ? OFPUTIL_PS_STP_LISTEN : state == STP_LEARNING ? OFPUTIL_PS_STP_LEARN : state == STP_FORWARDING ? OFPUTIL_PS_STP_FORWARD : state == STP_BLOCKING ? OFPUTIL_PS_STP_BLOCK : 0); ofproto_port_set_state(&ofport->up, of_state); } } /* Configures STP on 'ofport_' using the settings defined in 's'. The * caller is responsible for assigning STP port numbers and ensuring * there are no duplicates. */ static int set_stp_port(struct ofport *ofport_, const struct ofproto_port_stp_settings *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct stp_port *sp = ofport->stp_port; if (!s || !s->enable) { if (sp) { ofport->stp_port = NULL; stp_port_disable(sp); update_stp_port_state(ofport); } return 0; } else if (sp && stp_port_no(sp) != s->port_num && ofport == stp_port_get_aux(sp)) { /* The port-id changed, so disable the old one if it's not * already in use by another port. */ stp_port_disable(sp); } sp = ofport->stp_port = stp_get_port(ofproto->stp, s->port_num); /* Set name before enabling the port so that debugging messages can print * the name. */ stp_port_set_name(sp, netdev_get_name(ofport->up.netdev)); stp_port_enable(sp); stp_port_set_aux(sp, ofport); stp_port_set_priority(sp, s->priority); stp_port_set_path_cost(sp, s->path_cost); update_stp_port_state(ofport); return 0; } static int get_stp_port_status(struct ofport *ofport_, struct ofproto_port_stp_status *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct stp_port *sp = ofport->stp_port; if (!ofproto->stp || !sp) { s->enabled = false; return 0; } s->enabled = true; s->port_id = stp_port_get_id(sp); s->state = stp_port_get_state(sp); s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000; s->role = stp_port_get_role(sp); return 0; } static int get_stp_port_stats(struct ofport *ofport_, struct ofproto_port_stp_stats *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct stp_port *sp = ofport->stp_port; if (!ofproto->stp || !sp) { s->enabled = false; return 0; } s->enabled = true; stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count); return 0; } static void stp_run(struct ofproto_dpif *ofproto) { if (ofproto->stp) { long long int now = time_msec(); long long int elapsed = now - ofproto->stp_last_tick; struct stp_port *sp; if (elapsed > 0) { stp_tick(ofproto->stp, MIN(INT_MAX, elapsed)); ofproto->stp_last_tick = now; } while (stp_get_changed_port(ofproto->stp, &sp)) { struct ofport_dpif *ofport = stp_port_get_aux(sp); if (ofport) { update_stp_port_state(ofport); } } if (stp_check_and_reset_fdb_flush(ofproto->stp)) { ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); mcast_snooping_mdb_flush(ofproto->ms); } } } static void stp_wait(struct ofproto_dpif *ofproto) { if (ofproto->stp) { poll_timer_wait(1000); } } /* Configures RSTP on 'ofport_' using the settings defined in 's'. The * caller is responsible for assigning RSTP port numbers and ensuring * there are no duplicates. */ static void set_rstp_port(struct ofport *ofport_, const struct ofproto_port_rstp_settings *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct rstp_port *rp = ofport->rstp_port; if (!s || !s->enable) { if (rp) { rstp_port_set_aux(rp, NULL); rstp_port_set_state(rp, RSTP_DISABLED); rstp_port_set_mac_operational(rp, false); ofport->rstp_port = NULL; rstp_port_unref(rp); update_rstp_port_state(ofport); } return; } /* Check if need to add a new port. */ if (!rp) { rp = ofport->rstp_port = rstp_add_port(ofproto->rstp); } rstp_port_set(rp, s->port_num, s->priority, s->path_cost, s->admin_edge_port, s->auto_edge, s->admin_p2p_mac_state, s->admin_port_state, s->mcheck, ofport); update_rstp_port_state(ofport); /* Synchronize operational status. */ rstp_port_set_mac_operational(rp, ofport->may_enable); } static void get_rstp_port_status(struct ofport *ofport_, struct ofproto_port_rstp_status *s) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); struct rstp_port *rp = ofport->rstp_port; if (!ofproto->rstp || !rp) { s->enabled = false; return; } s->enabled = true; rstp_port_get_status(rp, &s->port_id, &s->state, &s->role, &s->designated_bridge_id, &s->designated_port_id, &s->designated_path_cost, &s->tx_count, &s->rx_count, &s->error_count, &s->uptime); } static int set_queues(struct ofport *ofport_, const struct ofproto_port_queue *qdscp, size_t n_qdscp) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); if (ofport->n_qdscp != n_qdscp || (n_qdscp && memcmp(ofport->qdscp, qdscp, n_qdscp * sizeof *qdscp))) { ofproto->backer->need_revalidate = REV_RECONFIGURE; free(ofport->qdscp); ofport->qdscp = n_qdscp ? xmemdup(qdscp, n_qdscp * sizeof *qdscp) : NULL; ofport->n_qdscp = n_qdscp; } return 0; } /* Bundles. */ /* Expires all MAC learning entries associated with 'bundle' and forces its * ofproto to revalidate every flow. * * Normally MAC learning entries are removed only from the ofproto associated * with 'bundle', but if 'all_ofprotos' is true, then the MAC learning entries * are removed from every ofproto. When patch ports and SLB bonds are in use * and a VM migration happens and the gratuitous ARPs are somehow lost, this * avoids a MAC_ENTRY_IDLE_TIME delay before the migrated VM can communicate * with the host from which it migrated. */ static void bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos) { struct ofproto_dpif *ofproto = bundle->ofproto; struct mac_learning *ml = ofproto->ml; struct mac_entry *mac, *next_mac; ofproto->backer->need_revalidate = REV_RECONFIGURE; ovs_rwlock_wrlock(&ml->rwlock); LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) { if (mac_entry_get_port(ml, mac) == bundle) { if (all_ofprotos) { struct ofproto_dpif *o; HMAP_FOR_EACH (o, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (o != ofproto) { struct mac_entry *e; ovs_rwlock_wrlock(&o->ml->rwlock); e = mac_learning_lookup(o->ml, mac->mac, mac->vlan); if (e) { mac_learning_expire(o->ml, e); } ovs_rwlock_unlock(&o->ml->rwlock); } } } mac_learning_expire(ml, mac); } } ovs_rwlock_unlock(&ml->rwlock); } static void bundle_move(struct ofbundle *old, struct ofbundle *new) { struct ofproto_dpif *ofproto = old->ofproto; struct mac_learning *ml = ofproto->ml; struct mac_entry *mac, *next_mac; ovs_assert(new->ofproto == old->ofproto); ofproto->backer->need_revalidate = REV_RECONFIGURE; ovs_rwlock_wrlock(&ml->rwlock); LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) { if (mac_entry_get_port(ml, mac) == old) { mac_entry_set_port(ml, mac, new); } } ovs_rwlock_unlock(&ml->rwlock); } static struct ofbundle * bundle_lookup(const struct ofproto_dpif *ofproto, void *aux) { struct ofbundle *bundle; HMAP_FOR_EACH_IN_BUCKET (bundle, hmap_node, hash_pointer(aux, 0), &ofproto->bundles) { if (bundle->aux == aux) { return bundle; } } return NULL; } static void bundle_update(struct ofbundle *bundle) { struct ofport_dpif *port; bundle->floodable = true; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD || port->is_layer3 || (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state)) || (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) { bundle->floodable = false; break; } } } static void bundle_del_port(struct ofport_dpif *port) { struct ofbundle *bundle = port->bundle; bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE; list_remove(&port->bundle_node); port->bundle = NULL; if (bundle->lacp) { lacp_slave_unregister(bundle->lacp, port); } if (bundle->bond) { bond_slave_unregister(bundle->bond, port); } bundle_update(bundle); } static bool bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port, struct lacp_slave_settings *lacp) { struct ofport_dpif *port; port = ofp_port_to_ofport(bundle->ofproto, ofp_port); if (!port) { return false; } if (port->bundle != bundle) { bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE; if (port->bundle) { bundle_remove(&port->up); } port->bundle = bundle; list_push_back(&bundle->ports, &port->bundle_node); if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD || port->is_layer3 || (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state)) || (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) { bundle->floodable = false; } } if (lacp) { bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE; lacp_slave_register(bundle->lacp, port, lacp); } return true; } static void bundle_destroy(struct ofbundle *bundle) { struct ofproto_dpif *ofproto; struct ofport_dpif *port, *next_port; if (!bundle) { return; } ofproto = bundle->ofproto; mbridge_unregister_bundle(ofproto->mbridge, bundle); xlate_txn_start(); xlate_bundle_remove(bundle); xlate_txn_commit(); LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) { bundle_del_port(port); } bundle_flush_macs(bundle, true); hmap_remove(&ofproto->bundles, &bundle->hmap_node); free(bundle->name); free(bundle->trunks); lacp_unref(bundle->lacp); bond_unref(bundle->bond); free(bundle); } static int bundle_set(struct ofproto *ofproto_, void *aux, const struct ofproto_bundle_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); bool need_flush = false; struct ofport_dpif *port; struct ofbundle *bundle; unsigned long *trunks; int vlan; size_t i; bool ok; if (!s) { bundle_destroy(bundle_lookup(ofproto, aux)); return 0; } ovs_assert(s->n_slaves == 1 || s->bond != NULL); ovs_assert((s->lacp != NULL) == (s->lacp_slaves != NULL)); bundle = bundle_lookup(ofproto, aux); if (!bundle) { bundle = xmalloc(sizeof *bundle); bundle->ofproto = ofproto; hmap_insert(&ofproto->bundles, &bundle->hmap_node, hash_pointer(aux, 0)); bundle->aux = aux; bundle->name = NULL; list_init(&bundle->ports); bundle->vlan_mode = PORT_VLAN_TRUNK; bundle->vlan = -1; bundle->trunks = NULL; bundle->use_priority_tags = s->use_priority_tags; bundle->lacp = NULL; bundle->bond = NULL; bundle->floodable = true; mbridge_register_bundle(ofproto->mbridge, bundle); } if (!bundle->name || strcmp(s->name, bundle->name)) { free(bundle->name); bundle->name = xstrdup(s->name); } /* LACP. */ if (s->lacp) { ofproto->lacp_enabled = true; if (!bundle->lacp) { ofproto->backer->need_revalidate = REV_RECONFIGURE; bundle->lacp = lacp_create(); } lacp_configure(bundle->lacp, s->lacp); } else { lacp_unref(bundle->lacp); bundle->lacp = NULL; } /* Update set of ports. */ ok = true; for (i = 0; i < s->n_slaves; i++) { if (!bundle_add_port(bundle, s->slaves[i], s->lacp ? &s->lacp_slaves[i] : NULL)) { ok = false; } } if (!ok || list_size(&bundle->ports) != s->n_slaves) { struct ofport_dpif *next_port; LIST_FOR_EACH_SAFE (port, next_port, bundle_node, &bundle->ports) { for (i = 0; i < s->n_slaves; i++) { if (s->slaves[i] == port->up.ofp_port) { goto found; } } bundle_del_port(port); found: ; } } ovs_assert(list_size(&bundle->ports) <= s->n_slaves); if (list_is_empty(&bundle->ports)) { bundle_destroy(bundle); return EINVAL; } /* Set VLAN tagging mode */ if (s->vlan_mode != bundle->vlan_mode || s->use_priority_tags != bundle->use_priority_tags) { bundle->vlan_mode = s->vlan_mode; bundle->use_priority_tags = s->use_priority_tags; need_flush = true; } /* Set VLAN tag. */ vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1 : s->vlan >= 0 && s->vlan <= 4095 ? s->vlan : 0); if (vlan != bundle->vlan) { bundle->vlan = vlan; need_flush = true; } /* Get trunked VLANs. */ switch (s->vlan_mode) { case PORT_VLAN_ACCESS: trunks = NULL; break; case PORT_VLAN_TRUNK: trunks = CONST_CAST(unsigned long *, s->trunks); break; case PORT_VLAN_NATIVE_UNTAGGED: case PORT_VLAN_NATIVE_TAGGED: if (vlan != 0 && (!s->trunks || !bitmap_is_set(s->trunks, vlan) || bitmap_is_set(s->trunks, 0))) { /* Force trunking the native VLAN and prohibit trunking VLAN 0. */ if (s->trunks) { trunks = bitmap_clone(s->trunks, 4096); } else { trunks = bitmap_allocate1(4096); } bitmap_set1(trunks, vlan); bitmap_set0(trunks, 0); } else { trunks = CONST_CAST(unsigned long *, s->trunks); } break; default: OVS_NOT_REACHED(); } if (!vlan_bitmap_equal(trunks, bundle->trunks)) { free(bundle->trunks); if (trunks == s->trunks) { bundle->trunks = vlan_bitmap_clone(trunks); } else { bundle->trunks = trunks; trunks = NULL; } need_flush = true; } if (trunks != s->trunks) { free(trunks); } /* Bonding. */ if (!list_is_short(&bundle->ports)) { bundle->ofproto->has_bonded_bundles = true; if (bundle->bond) { if (bond_reconfigure(bundle->bond, s->bond)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } } else { bundle->bond = bond_create(s->bond, ofproto); ofproto->backer->need_revalidate = REV_RECONFIGURE; } LIST_FOR_EACH (port, bundle_node, &bundle->ports) { bond_slave_register(bundle->bond, port, port->up.ofp_port, port->up.netdev); } } else { bond_unref(bundle->bond); bundle->bond = NULL; } /* If we changed something that would affect MAC learning, un-learn * everything on this port and force flow revalidation. */ if (need_flush) { bundle_flush_macs(bundle, false); } return 0; } static void bundle_remove(struct ofport *port_) { struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofbundle *bundle = port->bundle; if (bundle) { bundle_del_port(port); if (list_is_empty(&bundle->ports)) { bundle_destroy(bundle); } else if (list_is_short(&bundle->ports)) { bond_unref(bundle->bond); bundle->bond = NULL; } } } static void send_pdu_cb(void *port_, const void *pdu, size_t pdu_size) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 10); struct ofport_dpif *port = port_; struct eth_addr ea; int error; error = netdev_get_etheraddr(port->up.netdev, &ea); if (!error) { struct dp_packet packet; void *packet_pdu; dp_packet_init(&packet, 0); packet_pdu = eth_compose(&packet, eth_addr_lacp, ea, ETH_TYPE_LACP, pdu_size); memcpy(packet_pdu, pdu, pdu_size); ofproto_dpif_send_packet(port, &packet); dp_packet_uninit(&packet); } else { VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " "%s (%s)", port->bundle->name, netdev_get_name(port->up.netdev), ovs_strerror(error)); } } static void bundle_send_learning_packets(struct ofbundle *bundle) { struct ofproto_dpif *ofproto = bundle->ofproto; int error, n_packets, n_errors; struct mac_entry *e; struct pkt_list { struct ovs_list list_node; struct ofport_dpif *port; struct dp_packet *pkt; } *pkt_node; struct ovs_list packets; list_init(&packets); ovs_rwlock_rdlock(&ofproto->ml->rwlock); LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) { if (mac_entry_get_port(ofproto->ml, e) != bundle) { pkt_node = xmalloc(sizeof *pkt_node); pkt_node->pkt = bond_compose_learning_packet(bundle->bond, e->mac, e->vlan, (void **)&pkt_node->port); list_push_back(&packets, &pkt_node->list_node); } } ovs_rwlock_unlock(&ofproto->ml->rwlock); error = n_packets = n_errors = 0; LIST_FOR_EACH_POP (pkt_node, list_node, &packets) { int ret; ret = ofproto_dpif_send_packet(pkt_node->port, pkt_node->pkt); dp_packet_delete(pkt_node->pkt); free(pkt_node); if (ret) { error = ret; n_errors++; } n_packets++; } if (n_errors) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bond %s: %d errors sending %d gratuitous learning " "packets, last error was: %s", bundle->name, n_errors, n_packets, ovs_strerror(error)); } else { VLOG_DBG("bond %s: sent %d gratuitous learning packets", bundle->name, n_packets); } } static void bundle_run(struct ofbundle *bundle) { if (bundle->lacp) { lacp_run(bundle->lacp, send_pdu_cb); } if (bundle->bond) { struct ofport_dpif *port; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { bond_slave_set_may_enable(bundle->bond, port, port->may_enable); } if (bond_run(bundle->bond, lacp_status(bundle->lacp))) { bundle->ofproto->backer->need_revalidate = REV_BOND; } if (bond_should_send_learning_packets(bundle->bond)) { bundle_send_learning_packets(bundle); } } } static void bundle_wait(struct ofbundle *bundle) { if (bundle->lacp) { lacp_wait(bundle->lacp); } if (bundle->bond) { bond_wait(bundle->bond); } } /* Mirrors. */ static int mirror_set__(struct ofproto *ofproto_, void *aux, const struct ofproto_mirror_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofbundle **srcs, **dsts; int error; size_t i; if (!s) { mirror_destroy(ofproto->mbridge, aux); return 0; } srcs = xmalloc(s->n_srcs * sizeof *srcs); dsts = xmalloc(s->n_dsts * sizeof *dsts); for (i = 0; i < s->n_srcs; i++) { srcs[i] = bundle_lookup(ofproto, s->srcs[i]); } for (i = 0; i < s->n_dsts; i++) { dsts[i] = bundle_lookup(ofproto, s->dsts[i]); } error = mirror_set(ofproto->mbridge, aux, s->name, srcs, s->n_srcs, dsts, s->n_dsts, s->src_vlans, bundle_lookup(ofproto, s->out_bundle), s->out_vlan); free(srcs); free(dsts); return error; } static int mirror_get_stats__(struct ofproto *ofproto, void *aux, uint64_t *packets, uint64_t *bytes) { return mirror_get_stats(ofproto_dpif_cast(ofproto)->mbridge, aux, packets, bytes); } static int set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); ovs_rwlock_wrlock(&ofproto->ml->rwlock); if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) { mac_learning_flush(ofproto->ml); } ovs_rwlock_unlock(&ofproto->ml->rwlock); return 0; } static bool is_mirror_output_bundle(const struct ofproto *ofproto_, void *aux) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofbundle *bundle = bundle_lookup(ofproto, aux); return bundle && mirror_bundle_out(ofproto->mbridge, bundle) != 0; } static void forward_bpdu_changed(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); ofproto->backer->need_revalidate = REV_RECONFIGURE; } static void set_mac_table_config(struct ofproto *ofproto_, unsigned int idle_time, size_t max_entries) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_set_idle_time(ofproto->ml, idle_time); mac_learning_set_max_entries(ofproto->ml, max_entries); ovs_rwlock_unlock(&ofproto->ml->rwlock); } /* Configures multicast snooping on 'ofport' using the settings * defined in 's'. */ static int set_mcast_snooping(struct ofproto *ofproto_, const struct ofproto_mcast_snooping_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); /* Only revalidate flows if the configuration changed. */ if (!s != !ofproto->ms) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } if (s) { if (!ofproto->ms) { ofproto->ms = mcast_snooping_create(); } ovs_rwlock_wrlock(&ofproto->ms->rwlock); mcast_snooping_set_idle_time(ofproto->ms, s->idle_time); mcast_snooping_set_max_entries(ofproto->ms, s->max_entries); if (mcast_snooping_set_flood_unreg(ofproto->ms, s->flood_unreg)) { ofproto->backer->need_revalidate = REV_RECONFIGURE; } ovs_rwlock_unlock(&ofproto->ms->rwlock); } else { mcast_snooping_unref(ofproto->ms); ofproto->ms = NULL; } return 0; } /* Configures multicast snooping port's flood settings on 'ofproto'. */ static int set_mcast_snooping_port(struct ofproto *ofproto_, void *aux, const struct ofproto_mcast_snooping_port_settings *s) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofbundle *bundle = bundle_lookup(ofproto, aux); if (ofproto->ms && s) { ovs_rwlock_wrlock(&ofproto->ms->rwlock); mcast_snooping_set_port_flood(ofproto->ms, bundle, s->flood); mcast_snooping_set_port_flood_reports(ofproto->ms, bundle, s->flood_reports); ovs_rwlock_unlock(&ofproto->ms->rwlock); } return 0; } /* Ports. */ struct ofport_dpif * ofp_port_to_ofport(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port) { struct ofport *ofport = ofproto_get_port(&ofproto->up, ofp_port); return ofport ? ofport_dpif_cast(ofport) : NULL; } static void ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto, struct ofproto_port *ofproto_port, struct dpif_port *dpif_port) { ofproto_port->name = dpif_port->name; ofproto_port->type = dpif_port->type; ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no); } static void ofport_update_peer(struct ofport_dpif *ofport) { const struct ofproto_dpif *ofproto; struct dpif_backer *backer; char *peer_name; if (!netdev_vport_is_patch(ofport->up.netdev)) { return; } backer = ofproto_dpif_cast(ofport->up.ofproto)->backer; backer->need_revalidate = REV_RECONFIGURE; if (ofport->peer) { ofport->peer->peer = NULL; ofport->peer = NULL; } peer_name = netdev_vport_patch_peer(ofport->up.netdev); if (!peer_name) { return; } HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { struct ofport *peer_ofport; struct ofport_dpif *peer; char *peer_peer; if (ofproto->backer != backer) { continue; } peer_ofport = shash_find_data(&ofproto->up.port_by_name, peer_name); if (!peer_ofport) { continue; } peer = ofport_dpif_cast(peer_ofport); peer_peer = netdev_vport_patch_peer(peer->up.netdev); if (peer_peer && !strcmp(netdev_get_name(ofport->up.netdev), peer_peer)) { ofport->peer = peer; ofport->peer->peer = ofport; } free(peer_peer); break; } free(peer_name); } static void port_run(struct ofport_dpif *ofport) { long long int carrier_seq = netdev_get_carrier_resets(ofport->up.netdev); bool carrier_changed = carrier_seq != ofport->carrier_seq; bool enable = netdev_get_carrier(ofport->up.netdev); bool cfm_enable = false; bool bfd_enable = false; ofport->carrier_seq = carrier_seq; if (ofport->cfm) { int cfm_opup = cfm_get_opup(ofport->cfm); cfm_enable = !cfm_get_fault(ofport->cfm); if (cfm_opup >= 0) { cfm_enable = cfm_enable && cfm_opup; } } if (ofport->bfd) { bfd_enable = bfd_forwarding(ofport->bfd); } if (ofport->bfd || ofport->cfm) { enable = enable && (cfm_enable || bfd_enable); } if (ofport->bundle) { enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport); if (carrier_changed) { lacp_slave_carrier_changed(ofport->bundle->lacp, ofport); } } if (ofport->may_enable != enable) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); ofproto->backer->need_revalidate = REV_PORT_TOGGLED; if (ofport->rstp_port) { rstp_port_set_mac_operational(ofport->rstp_port, enable); } } ofport->may_enable = enable; } static int port_query_by_name(const struct ofproto *ofproto_, const char *devname, struct ofproto_port *ofproto_port) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_port dpif_port; int error; if (sset_contains(&ofproto->ghost_ports, devname)) { const char *type = netdev_get_type_from_name(devname); /* We may be called before ofproto->up.port_by_name is populated with * the appropriate ofport. For this reason, we must get the name and * type from the netdev layer directly. */ if (type) { const struct ofport *ofport; ofport = shash_find_data(&ofproto->up.port_by_name, devname); ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE; ofproto_port->name = xstrdup(devname); ofproto_port->type = xstrdup(type); return 0; } return ENODEV; } if (!sset_contains(&ofproto->ports, devname)) { return ENODEV; } error = dpif_port_query_by_name(ofproto->backer->dpif, devname, &dpif_port); if (!error) { ofproto_port_from_dpif_port(ofproto, ofproto_port, &dpif_port); } return error; } static int port_add(struct ofproto *ofproto_, struct netdev *netdev) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); const char *devname = netdev_get_name(netdev); char namebuf[NETDEV_VPORT_NAME_BUFSIZE]; const char *dp_port_name; if (netdev_vport_is_patch(netdev)) { sset_add(&ofproto->ghost_ports, netdev_get_name(netdev)); return 0; } dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf); if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) { odp_port_t port_no = ODPP_NONE; int error; error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no); if (error) { return error; } if (netdev_get_tunnel_config(netdev)) { simap_put(&ofproto->backer->tnl_backers, dp_port_name, odp_to_u32(port_no)); } } if (netdev_get_tunnel_config(netdev)) { sset_add(&ofproto->ghost_ports, devname); } else { sset_add(&ofproto->ports, devname); } return 0; } static int port_del(struct ofproto *ofproto_, ofp_port_t ofp_port) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofport_dpif *ofport = ofp_port_to_ofport(ofproto, ofp_port); int error = 0; if (!ofport) { return 0; } sset_find_and_delete(&ofproto->ghost_ports, netdev_get_name(ofport->up.netdev)); ofproto->backer->need_revalidate = REV_RECONFIGURE; if (!ofport->is_tunnel && !netdev_vport_is_patch(ofport->up.netdev)) { error = dpif_port_del(ofproto->backer->dpif, ofport->odp_port); if (!error) { /* The caller is going to close ofport->up.netdev. If this is a * bonded port, then the bond is using that netdev, so remove it * from the bond. The client will need to reconfigure everything * after deleting ports, so then the slave will get re-added. */ bundle_remove(&ofport->up); } } return error; } static int port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); int error; error = netdev_get_stats(ofport->up.netdev, stats); if (!error && ofport_->ofp_port == OFPP_LOCAL) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); ovs_mutex_lock(&ofproto->stats_mutex); /* ofproto->stats.tx_packets represents packets that we created * internally and sent to some port (e.g. packets sent with * ofproto_dpif_send_packet()). Account for them as if they had * come from OFPP_LOCAL and got forwarded. */ if (stats->rx_packets != UINT64_MAX) { stats->rx_packets += ofproto->stats.tx_packets; } if (stats->rx_bytes != UINT64_MAX) { stats->rx_bytes += ofproto->stats.tx_bytes; } /* ofproto->stats.rx_packets represents packets that were received on * some port and we processed internally and dropped (e.g. STP). * Account for them as if they had been forwarded to OFPP_LOCAL. */ if (stats->tx_packets != UINT64_MAX) { stats->tx_packets += ofproto->stats.rx_packets; } if (stats->tx_bytes != UINT64_MAX) { stats->tx_bytes += ofproto->stats.rx_bytes; } ovs_mutex_unlock(&ofproto->stats_mutex); } return error; } static int port_get_lacp_stats(const struct ofport *ofport_, struct lacp_slave_stats *stats) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); if (ofport->bundle && ofport->bundle->lacp) { if (lacp_get_slave_stats(ofport->bundle->lacp, ofport, stats)) { return 0; } } return -1; } struct port_dump_state { uint32_t bucket; uint32_t offset; bool ghost; struct ofproto_port port; bool has_port; }; static int port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep) { *statep = xzalloc(sizeof(struct port_dump_state)); return 0; } static int port_dump_next(const struct ofproto *ofproto_, void *state_, struct ofproto_port *port) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct port_dump_state *state = state_; const struct sset *sset; struct sset_node *node; if (state->has_port) { ofproto_port_destroy(&state->port); state->has_port = false; } sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports; while ((node = sset_at_position(sset, &state->bucket, &state->offset))) { int error; error = port_query_by_name(ofproto_, node->name, &state->port); if (!error) { *port = state->port; state->has_port = true; return 0; } else if (error != ENODEV) { return error; } } if (!state->ghost) { state->ghost = true; state->bucket = 0; state->offset = 0; return port_dump_next(ofproto_, state_, port); } return EOF; } static int port_dump_done(const struct ofproto *ofproto_ OVS_UNUSED, void *state_) { struct port_dump_state *state = state_; if (state->has_port) { ofproto_port_destroy(&state->port); } free(state); return 0; } static int port_poll(const struct ofproto *ofproto_, char **devnamep) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (ofproto->port_poll_errno) { int error = ofproto->port_poll_errno; ofproto->port_poll_errno = 0; return error; } if (sset_is_empty(&ofproto->port_poll_set)) { return EAGAIN; } *devnamep = sset_pop(&ofproto->port_poll_set); return 0; } static void port_poll_wait(const struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); dpif_port_poll_wait(ofproto->backer->dpif); } static int port_is_lacp_current(const struct ofport *ofport_) { const struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); return (ofport->bundle && ofport->bundle->lacp ? lacp_slave_is_current(ofport->bundle->lacp, ofport) : -1); } /* If 'rule' is an OpenFlow rule, that has expired according to OpenFlow rules, * then delete it entirely. */ static void rule_expire(struct rule_dpif *rule) OVS_REQUIRES(ofproto_mutex) { uint16_t hard_timeout, idle_timeout; long long int now = time_msec(); int reason = -1; hard_timeout = rule->up.hard_timeout; idle_timeout = rule->up.idle_timeout; /* Has 'rule' expired? */ if (hard_timeout) { long long int modified; ovs_mutex_lock(&rule->up.mutex); modified = rule->up.modified; ovs_mutex_unlock(&rule->up.mutex); if (now > modified + hard_timeout * 1000) { reason = OFPRR_HARD_TIMEOUT; } } if (reason < 0 && idle_timeout) { long long int used; ovs_mutex_lock(&rule->stats_mutex); used = rule->stats.used; ovs_mutex_unlock(&rule->stats_mutex); if (now > used + idle_timeout * 1000) { reason = OFPRR_IDLE_TIMEOUT; } } if (reason >= 0) { COVERAGE_INC(ofproto_dpif_expired); ofproto_rule_expire(&rule->up, reason); } } int ofproto_dpif_execute_actions__(struct ofproto_dpif *ofproto, const struct flow *flow, struct rule_dpif *rule, const struct ofpact *ofpacts, size_t ofpacts_len, int recurse, int resubmits, struct dp_packet *packet) { struct dpif_flow_stats stats; struct xlate_out xout; struct xlate_in xin; ofp_port_t in_port; struct dpif_execute execute; int error; ovs_assert((rule != NULL) != (ofpacts != NULL)); dpif_flow_stats_extract(flow, packet, time_msec(), &stats); if (rule) { rule_dpif_credit_stats(rule, &stats); } uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions = OFPBUF_STUB_INITIALIZER(odp_actions_stub); xlate_in_init(&xin, ofproto, flow, flow->in_port.ofp_port, rule, stats.tcp_flags, packet, NULL, &odp_actions); xin.ofpacts = ofpacts; xin.ofpacts_len = ofpacts_len; xin.resubmit_stats = &stats; xin.recurse = recurse; xin.resubmits = resubmits; if (xlate_actions(&xin, &xout) != XLATE_OK) { error = EINVAL; goto out; } execute.actions = odp_actions.data; execute.actions_len = odp_actions.size; pkt_metadata_from_flow(&packet->md, flow); execute.packet = packet; execute.needs_help = (xout.slow & SLOW_ACTION) != 0; execute.probe = false; execute.mtu = 0; /* Fix up in_port. */ in_port = flow->in_port.ofp_port; if (in_port == OFPP_NONE) { in_port = OFPP_LOCAL; } execute.packet->md.in_port.odp_port = ofp_port_to_odp_port(ofproto, in_port); error = dpif_execute(ofproto->backer->dpif, &execute); out: xlate_out_uninit(&xout); ofpbuf_uninit(&odp_actions); return error; } /* Executes, within 'ofproto', the actions in 'rule' or 'ofpacts' on 'packet'. * 'flow' must reflect the data in 'packet'. */ int ofproto_dpif_execute_actions(struct ofproto_dpif *ofproto, const struct flow *flow, struct rule_dpif *rule, const struct ofpact *ofpacts, size_t ofpacts_len, struct dp_packet *packet) { return ofproto_dpif_execute_actions__(ofproto, flow, rule, ofpacts, ofpacts_len, 0, 0, packet); } static void rule_dpif_credit_stats__(struct rule_dpif *rule, const struct dpif_flow_stats *stats, bool credit_counts) OVS_REQUIRES(rule->stats_mutex) { if (credit_counts) { rule->stats.n_packets += stats->n_packets; rule->stats.n_bytes += stats->n_bytes; } rule->stats.used = MAX(rule->stats.used, stats->used); } void rule_dpif_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats) { ovs_mutex_lock(&rule->stats_mutex); if (OVS_UNLIKELY(rule->new_rule)) { ovs_mutex_lock(&rule->new_rule->stats_mutex); rule_dpif_credit_stats__(rule->new_rule, stats, rule->forward_counts); ovs_mutex_unlock(&rule->new_rule->stats_mutex); } else { rule_dpif_credit_stats__(rule, stats, true); } ovs_mutex_unlock(&rule->stats_mutex); } ovs_be64 rule_dpif_get_flow_cookie(const struct rule_dpif *rule) OVS_REQUIRES(rule->up.mutex) { return rule->up.flow_cookie; } void rule_dpif_reduce_timeouts(struct rule_dpif *rule, uint16_t idle_timeout, uint16_t hard_timeout) { ofproto_rule_reduce_timeouts(&rule->up, idle_timeout, hard_timeout); } /* Returns 'rule''s actions. The returned actions are RCU-protected, and can * be read until the calling thread quiesces. */ const struct rule_actions * rule_dpif_get_actions(const struct rule_dpif *rule) { return rule_get_actions(&rule->up); } /* Sets 'rule''s recirculation id. */ static void rule_dpif_set_recirc_id(struct rule_dpif *rule, uint32_t id) OVS_REQUIRES(rule->up.mutex) { ovs_assert(!rule->recirc_id || rule->recirc_id == id); if (rule->recirc_id == id) { /* Release the new reference to the same id. */ recirc_free_id(id); } else { rule->recirc_id = id; } } /* Sets 'rule''s recirculation id. */ void rule_set_recirc_id(struct rule *rule_, uint32_t id) { struct rule_dpif *rule = rule_dpif_cast(rule_); ovs_mutex_lock(&rule->up.mutex); rule_dpif_set_recirc_id(rule, id); ovs_mutex_unlock(&rule->up.mutex); } cls_version_t ofproto_dpif_get_tables_version(struct ofproto_dpif *ofproto OVS_UNUSED) { cls_version_t version; /* Use memory_order_acquire to signify that any following memory accesses * can not be reordered to happen before this atomic read. This makes sure * all following reads relate to this or a newer version, but never to an * older version. */ atomic_read_explicit(&ofproto->tables_version, &version, memory_order_acquire); return version; } /* The returned rule (if any) is valid at least until the next RCU quiescent * period. If the rule needs to stay around longer, the caller should take * a reference. * * 'flow' is non-const to allow for temporary modifications during the lookup. * Any changes are restored before returning. */ static struct rule_dpif * rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, cls_version_t version, uint8_t table_id, struct flow *flow, struct flow_wildcards *wc) { struct classifier *cls = &ofproto->up.tables[table_id].cls; return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version, flow, wc))); } /* Look up 'flow' in 'ofproto''s classifier version 'version', starting from * table '*table_id'. Returns the rule that was found, which may be one of the * special rules according to packet miss hadling. If 'may_packet_in' is * false, returning of the miss_rule (which issues packet ins for the * controller) is avoided. Updates 'wc', if nonnull, to reflect the fields * that were used during the lookup. * * If 'honor_table_miss' is true, the first lookup occurs in '*table_id', but * if none is found then the table miss configuration for that table is * honored, which can result in additional lookups in other OpenFlow tables. * In this case the function updates '*table_id' to reflect the final OpenFlow * table that was searched. * * If 'honor_table_miss' is false, then only one table lookup occurs, in * '*table_id'. * * The rule is returned in '*rule', which is valid at least until the next * RCU quiescent period. If the '*rule' needs to stay around longer, the * caller must take a reference. * * 'in_port' allows the lookup to take place as if the in port had the value * 'in_port'. This is needed for resubmit action support. * * 'flow' is non-const to allow for temporary modifications during the lookup. * Any changes are restored before returning. */ struct rule_dpif * rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, cls_version_t version, struct flow *flow, struct flow_wildcards *wc, const struct dpif_flow_stats *stats, uint8_t *table_id, ofp_port_t in_port, bool may_packet_in, bool honor_table_miss) { ovs_be16 old_tp_src = flow->tp_src, old_tp_dst = flow->tp_dst; ofp_port_t old_in_port = flow->in_port.ofp_port; enum ofputil_table_miss miss_config; struct rule_dpif *rule; uint8_t next_id; /* We always unwildcard nw_frag (for IP), so they * need not be unwildcarded here. */ if (flow->nw_frag & FLOW_NW_FRAG_ANY && ofproto->up.frag_handling != OFPC_FRAG_NX_MATCH) { if (ofproto->up.frag_handling == OFPC_FRAG_NORMAL) { /* We must pretend that transport ports are unavailable. */ flow->tp_src = htons(0); flow->tp_dst = htons(0); } else { /* Must be OFPC_FRAG_DROP (we don't have OFPC_FRAG_REASM). * Use the drop_frags_rule (which cannot disappear). */ rule = ofproto->drop_frags_rule; if (stats) { struct oftable *tbl = &ofproto->up.tables[*table_id]; unsigned long orig; atomic_add_relaxed(&tbl->n_matched, stats->n_packets, &orig); } return rule; } } /* Look up a flow with 'in_port' as the input port. Then restore the * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will * have surprising behavior). */ flow->in_port.ofp_port = in_port; /* Our current implementation depends on n_tables == N_TABLES, and * TBL_INTERNAL being the last table. */ BUILD_ASSERT_DECL(N_TABLES == TBL_INTERNAL + 1); miss_config = OFPUTIL_TABLE_MISS_CONTINUE; for (next_id = *table_id; next_id < ofproto->up.n_tables; next_id++, next_id += (next_id == TBL_INTERNAL)) { *table_id = next_id; rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc); if (stats) { struct oftable *tbl = &ofproto->up.tables[next_id]; unsigned long orig; atomic_add_relaxed(rule ? &tbl->n_matched : &tbl->n_missed, stats->n_packets, &orig); } if (rule) { goto out; /* Match. */ } if (honor_table_miss) { miss_config = ofproto_table_get_miss_config(&ofproto->up, *table_id); if (miss_config == OFPUTIL_TABLE_MISS_CONTINUE) { continue; } } break; } /* Miss. */ rule = ofproto->no_packet_in_rule; if (may_packet_in) { if (miss_config == OFPUTIL_TABLE_MISS_CONTINUE || miss_config == OFPUTIL_TABLE_MISS_CONTROLLER) { struct ofport_dpif *port; port = ofp_port_to_ofport(ofproto, old_in_port); if (!port) { VLOG_WARN_RL(&rl, "packet-in on unknown OpenFlow port %"PRIu16, old_in_port); } else if (!(port->up.pp.config & OFPUTIL_PC_NO_PACKET_IN)) { rule = ofproto->miss_rule; } } else if (miss_config == OFPUTIL_TABLE_MISS_DEFAULT && connmgr_wants_packet_in_on_miss(ofproto->up.connmgr)) { rule = ofproto->miss_rule; } } out: /* Restore port numbers, as they may have been modified above. */ flow->tp_src = old_tp_src; flow->tp_dst = old_tp_dst; /* Restore the old in port. */ flow->in_port.ofp_port = old_in_port; return rule; } static void complete_operation(struct rule_dpif *rule) OVS_REQUIRES(ofproto_mutex) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); ofproto->backer->need_revalidate = REV_FLOW_TABLE; } static struct rule_dpif *rule_dpif_cast(const struct rule *rule) { return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL; } static struct rule * rule_alloc(void) { struct rule_dpif *rule = xzalloc(sizeof *rule); return &rule->up; } static void rule_dealloc(struct rule *rule_) { struct rule_dpif *rule = rule_dpif_cast(rule_); free(rule); } static enum ofperr check_mask(struct ofproto_dpif *ofproto, const struct miniflow *flow) { const struct odp_support *support; uint16_t ct_state, ct_zone; ovs_u128 ct_label; uint32_t ct_mark; support = &ofproto_dpif_get_support(ofproto)->odp; ct_state = MINIFLOW_GET_U16(flow, ct_state); if (support->ct_state && support->ct_zone && support->ct_mark && support->ct_label) { return ct_state & CS_UNSUPPORTED_MASK ? OFPERR_OFPBMC_BAD_MASK : 0; } ct_zone = MINIFLOW_GET_U16(flow, ct_zone); ct_mark = MINIFLOW_GET_U32(flow, ct_mark); ct_label = MINIFLOW_GET_U128(flow, ct_label); if ((ct_state && !support->ct_state) || (ct_state & CS_UNSUPPORTED_MASK) || (ct_zone && !support->ct_zone) || (ct_mark && !support->ct_mark) || (!ovs_u128_is_zero(&ct_label) && !support->ct_label)) { return OFPERR_OFPBMC_BAD_MASK; } return 0; } static enum ofperr check_actions(const struct ofproto_dpif *ofproto, const struct rule_actions *const actions) { const struct ofpact *ofpact; OFPACT_FOR_EACH (ofpact, actions->ofpacts, actions->ofpacts_len) { const struct odp_support *support; const struct ofpact_conntrack *ct; const struct ofpact *a; if (ofpact->type != OFPACT_CT) { continue; } ct = CONTAINER_OF(ofpact, struct ofpact_conntrack, ofpact); support = &ofproto_dpif_get_support(ofproto)->odp; if (!support->ct_state) { return OFPERR_OFPBAC_BAD_TYPE; } if ((ct->zone_imm || ct->zone_src.field) && !support->ct_zone) { return OFPERR_OFPBAC_BAD_ARGUMENT; } OFPACT_FOR_EACH(a, ct->actions, ofpact_ct_get_action_len(ct)) { const struct mf_field *dst = ofpact_get_mf_dst(a); if (dst && ((dst->id == MFF_CT_MARK && !support->ct_mark) || (dst->id == MFF_CT_LABEL && !support->ct_label))) { return OFPERR_OFPBAC_BAD_SET_ARGUMENT; } } } return 0; } static enum ofperr rule_check(struct rule *rule) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->ofproto); enum ofperr err; err = check_mask(ofproto, &rule->cr.match.mask->masks); if (err) { return err; } return check_actions(ofproto, rule->actions); } static enum ofperr rule_construct(struct rule *rule_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rule_dpif *rule = rule_dpif_cast(rule_); int error; error = rule_check(rule_); if (error) { return error; } ovs_mutex_init_adaptive(&rule->stats_mutex); rule->stats.n_packets = 0; rule->stats.n_bytes = 0; rule->stats.used = rule->up.modified; rule->recirc_id = 0; rule->new_rule = NULL; rule->forward_counts = false; return 0; } static void rule_insert(struct rule *rule_, struct rule *old_rule_, bool forward_counts) OVS_REQUIRES(ofproto_mutex) { struct rule_dpif *rule = rule_dpif_cast(rule_); if (old_rule_) { struct rule_dpif *old_rule = rule_dpif_cast(old_rule_); ovs_assert(!old_rule->new_rule); /* Take a reference to the new rule, and refer all stats updates from * the old rule to the new rule. */ rule_dpif_ref(rule); ovs_mutex_lock(&old_rule->stats_mutex); ovs_mutex_lock(&rule->stats_mutex); old_rule->new_rule = rule; /* Forward future stats. */ old_rule->forward_counts = forward_counts; if (forward_counts) { rule->stats = old_rule->stats; /* Transfer stats to the new * rule. */ } else { /* Used timestamp must be forwarded whenever a rule is modified. */ rule->stats.used = old_rule->stats.used; } ovs_mutex_unlock(&rule->stats_mutex); ovs_mutex_unlock(&old_rule->stats_mutex); } complete_operation(rule); } static void rule_delete(struct rule *rule_) OVS_REQUIRES(ofproto_mutex) { struct rule_dpif *rule = rule_dpif_cast(rule_); complete_operation(rule); } static void rule_destruct(struct rule *rule_) OVS_NO_THREAD_SAFETY_ANALYSIS { struct rule_dpif *rule = rule_dpif_cast(rule_); ovs_mutex_destroy(&rule->stats_mutex); /* Release reference to the new rule, if any. */ if (rule->new_rule) { rule_dpif_unref(rule->new_rule); } if (rule->recirc_id) { recirc_free_id(rule->recirc_id); } } static void rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes, long long int *used) { struct rule_dpif *rule = rule_dpif_cast(rule_); ovs_mutex_lock(&rule->stats_mutex); if (OVS_UNLIKELY(rule->new_rule)) { rule_get_stats(&rule->new_rule->up, packets, bytes, used); } else { *packets = rule->stats.n_packets; *bytes = rule->stats.n_bytes; *used = rule->stats.used; } ovs_mutex_unlock(&rule->stats_mutex); } static void rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow, struct dp_packet *packet) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); ofproto_dpif_execute_actions(ofproto, flow, rule, NULL, 0, packet); } static enum ofperr rule_execute(struct rule *rule, const struct flow *flow, struct dp_packet *packet) { rule_dpif_execute(rule_dpif_cast(rule), flow, packet); dp_packet_delete(packet); return 0; } static struct group_dpif *group_dpif_cast(const struct ofgroup *group) { return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL; } static struct ofgroup * group_alloc(void) { struct group_dpif *group = xzalloc(sizeof *group); return &group->up; } static void group_dealloc(struct ofgroup *group_) { struct group_dpif *group = group_dpif_cast(group_); free(group); } static void group_construct_stats(struct group_dpif *group) OVS_REQUIRES(group->stats_mutex) { struct ofputil_bucket *bucket; const struct ovs_list *buckets; group->packet_count = 0; group->byte_count = 0; group_dpif_get_buckets(group, &buckets); LIST_FOR_EACH (bucket, list_node, buckets) { bucket->stats.packet_count = 0; bucket->stats.byte_count = 0; } } void group_dpif_credit_stats(struct group_dpif *group, struct ofputil_bucket *bucket, const struct dpif_flow_stats *stats) { ovs_mutex_lock(&group->stats_mutex); group->packet_count += stats->n_packets; group->byte_count += stats->n_bytes; if (bucket) { bucket->stats.packet_count += stats->n_packets; bucket->stats.byte_count += stats->n_bytes; } else { /* Credit to all buckets */ const struct ovs_list *buckets; group_dpif_get_buckets(group, &buckets); LIST_FOR_EACH (bucket, list_node, buckets) { bucket->stats.packet_count += stats->n_packets; bucket->stats.byte_count += stats->n_bytes; } } ovs_mutex_unlock(&group->stats_mutex); } static enum ofperr group_construct(struct ofgroup *group_) { struct group_dpif *group = group_dpif_cast(group_); ovs_mutex_init_adaptive(&group->stats_mutex); ovs_mutex_lock(&group->stats_mutex); group_construct_stats(group); ovs_mutex_unlock(&group->stats_mutex); return 0; } static void group_destruct(struct ofgroup *group_) { struct group_dpif *group = group_dpif_cast(group_); ovs_mutex_destroy(&group->stats_mutex); } static enum ofperr group_modify(struct ofgroup *group_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(group_->ofproto); ofproto->backer->need_revalidate = REV_FLOW_TABLE; return 0; } static enum ofperr group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs) { struct group_dpif *group = group_dpif_cast(group_); struct ofputil_bucket *bucket; const struct ovs_list *buckets; struct bucket_counter *bucket_stats; ovs_mutex_lock(&group->stats_mutex); ogs->packet_count = group->packet_count; ogs->byte_count = group->byte_count; group_dpif_get_buckets(group, &buckets); bucket_stats = ogs->bucket_stats; LIST_FOR_EACH (bucket, list_node, buckets) { bucket_stats->packet_count = bucket->stats.packet_count; bucket_stats->byte_count = bucket->stats.byte_count; bucket_stats++; } ovs_mutex_unlock(&group->stats_mutex); return 0; } /* If the group exists, this function increments the groups's reference count. * * Make sure to call group_dpif_unref() after no longer needing to maintain * a reference to the group. */ bool group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id, struct group_dpif **group) { struct ofgroup *ofgroup; bool found; found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup); *group = found ? group_dpif_cast(ofgroup) : NULL; return found; } void group_dpif_get_buckets(const struct group_dpif *group, const struct ovs_list **buckets) { *buckets = &group->up.buckets; } enum ofp11_group_type group_dpif_get_type(const struct group_dpif *group) { return group->up.type; } const char * group_dpif_get_selection_method(const struct group_dpif *group) { return group->up.props.selection_method; } /* Sends 'packet' out 'ofport'. * May modify 'packet'. * Returns 0 if successful, otherwise a positive errno value. */ int ofproto_dpif_send_packet(const struct ofport_dpif *ofport, struct dp_packet *packet) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); int error; error = xlate_send_packet(ofport, packet); ovs_mutex_lock(&ofproto->stats_mutex); ofproto->stats.tx_packets++; ofproto->stats.tx_bytes += dp_packet_size(packet); ovs_mutex_unlock(&ofproto->stats_mutex); return error; } uint64_t group_dpif_get_selection_method_param(const struct group_dpif *group) { return group->up.props.selection_method_param; } const struct field_array * group_dpif_get_fields(const struct group_dpif *group) { return &group->up.props.fields; } /* Return the version string of the datapath that backs up * this 'ofproto'. */ static const char * get_datapath_version(const struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); return ofproto->backer->dp_version_string; } static bool set_frag_handling(struct ofproto *ofproto_, enum ofp_config_flags frag_handling) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (frag_handling != OFPC_FRAG_REASM) { ofproto->backer->need_revalidate = REV_RECONFIGURE; return true; } else { return false; } } static enum ofperr packet_out(struct ofproto *ofproto_, struct dp_packet *packet, const struct flow *flow, const struct ofpact *ofpacts, size_t ofpacts_len) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); ofproto_dpif_execute_actions(ofproto, flow, NULL, ofpacts, ofpacts_len, packet); return 0; } /* NetFlow. */ static int set_netflow(struct ofproto *ofproto_, const struct netflow_options *netflow_options) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); if (netflow_options) { if (!ofproto->netflow) { ofproto->netflow = netflow_create(); ofproto->backer->need_revalidate = REV_RECONFIGURE; } return netflow_set_options(ofproto->netflow, netflow_options); } else if (ofproto->netflow) { ofproto->backer->need_revalidate = REV_RECONFIGURE; netflow_unref(ofproto->netflow); ofproto->netflow = NULL; } return 0; } static void get_netflow_ids(const struct ofproto *ofproto_, uint8_t *engine_type, uint8_t *engine_id) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id); } static struct ofproto_dpif * ofproto_dpif_lookup(const char *name) { struct ofproto_dpif *ofproto; HMAP_FOR_EACH_WITH_HASH (ofproto, all_ofproto_dpifs_node, hash_string(name, 0), &all_ofproto_dpifs) { if (!strcmp(ofproto->up.name, name)) { return ofproto; } } return NULL; } static void ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct ofproto_dpif *ofproto; if (argc > 1) { ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply_error(conn, "no such bridge"); return; } ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); } else { HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { ovs_rwlock_wrlock(&ofproto->ml->rwlock); mac_learning_flush(ofproto->ml); ovs_rwlock_unlock(&ofproto->ml->rwlock); } } unixctl_command_reply(conn, "table successfully flushed"); } static void ofproto_unixctl_mcast_snooping_flush(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct ofproto_dpif *ofproto; if (argc > 1) { ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply_error(conn, "no such bridge"); return; } if (!mcast_snooping_enabled(ofproto->ms)) { unixctl_command_reply_error(conn, "multicast snooping is disabled"); return; } mcast_snooping_mdb_flush(ofproto->ms); } else { HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { if (!mcast_snooping_enabled(ofproto->ms)) { continue; } mcast_snooping_mdb_flush(ofproto->ms); } } unixctl_command_reply(conn, "table successfully flushed"); } static struct ofport_dpif * ofbundle_get_a_port(const struct ofbundle *bundle) { return CONTAINER_OF(list_front(&bundle->ports), struct ofport_dpif, bundle_node); } static void ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct ofproto_dpif *ofproto; const struct mac_entry *e; ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply_error(conn, "no such bridge"); return; } ds_put_cstr(&ds, " port VLAN MAC Age\n"); ovs_rwlock_rdlock(&ofproto->ml->rwlock); LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) { struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e); char name[OFP_MAX_PORT_NAME_LEN]; ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, name, sizeof name); ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" %3d\n", name, e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(ofproto->ml, e)); } ovs_rwlock_unlock(&ofproto->ml->rwlock); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct ofproto_dpif *ofproto; const struct ofbundle *bundle; const struct mcast_group *grp; struct mcast_group_bundle *b; struct mcast_mrouter_bundle *mrouter; ofproto = ofproto_dpif_lookup(argv[1]); if (!ofproto) { unixctl_command_reply_error(conn, "no such bridge"); return; } if (!mcast_snooping_enabled(ofproto->ms)) { unixctl_command_reply_error(conn, "multicast snooping is disabled"); return; } ds_put_cstr(&ds, " port VLAN GROUP Age\n"); ovs_rwlock_rdlock(&ofproto->ms->rwlock); LIST_FOR_EACH (grp, group_node, &ofproto->ms->group_lru) { LIST_FOR_EACH(b, bundle_node, &grp->bundle_lru) { char name[OFP_MAX_PORT_NAME_LEN]; bundle = b->port; ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, name, sizeof name); ds_put_format(&ds, "%5s %4d ", name, grp->vlan); ipv6_format_mapped(&grp->addr, &ds); ds_put_format(&ds, " %3d\n", mcast_bundle_age(ofproto->ms, b)); } } /* ports connected to multicast routers */ LIST_FOR_EACH(mrouter, mrouter_node, &ofproto->ms->mrouter_lru) { char name[OFP_MAX_PORT_NAME_LEN]; bundle = mrouter->port; ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port, name, sizeof name); ds_put_format(&ds, "%5s %4d querier %3d\n", name, mrouter->vlan, mcast_mrouter_age(ofproto->ms, mrouter)); } ovs_rwlock_unlock(&ofproto->ms->rwlock); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } struct trace_ctx { struct xlate_out xout; struct xlate_in xin; const struct flow *key; struct flow flow; struct ds *result; struct flow_wildcards wc; struct ofpbuf odp_actions; }; static void trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule) { const struct rule_actions *actions; ovs_be64 cookie; ds_put_char_multiple(result, '\t', level); if (!rule) { ds_put_cstr(result, "No match\n"); return; } ovs_mutex_lock(&rule->up.mutex); cookie = rule->up.flow_cookie; ovs_mutex_unlock(&rule->up.mutex); ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ", rule ? rule->up.table_id : 0, ntohll(cookie)); cls_rule_format(&rule->up.cr, result); ds_put_char(result, '\n'); actions = rule_dpif_get_actions(rule); ds_put_char_multiple(result, '\t', level); ds_put_cstr(result, "OpenFlow actions="); ofpacts_format(actions->ofpacts, actions->ofpacts_len, result); ds_put_char(result, '\n'); } static void trace_format_flow(struct ds *result, int level, const char *title, struct trace_ctx *trace) { ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s: ", title); /* Do not report unchanged flows for resubmits. */ if ((level > 0 && flow_equal(&trace->xin.flow, &trace->flow)) || (level == 0 && flow_equal(&trace->xin.flow, trace->key))) { ds_put_cstr(result, "unchanged"); } else { flow_format(result, &trace->xin.flow); trace->flow = trace->xin.flow; } ds_put_char(result, '\n'); } static void trace_format_regs(struct ds *result, int level, const char *title, struct trace_ctx *trace) { size_t i; ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s:", title); for (i = 0; i < FLOW_N_REGS; i++) { ds_put_format(result, " reg%"PRIuSIZE"=0x%"PRIx32, i, trace->flow.regs[i]); } ds_put_char(result, '\n'); } static void trace_format_odp(struct ds *result, int level, const char *title, struct trace_ctx *trace) { struct ofpbuf *odp_actions = &trace->odp_actions; ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s: ", title); format_odp_actions(result, odp_actions->data, odp_actions->size); ds_put_char(result, '\n'); } static void trace_format_megaflow(struct ds *result, int level, const char *title, struct trace_ctx *trace) { struct match match; ds_put_char_multiple(result, '\t', level); ds_put_format(result, "%s: ", title); match_init(&match, trace->key, &trace->wc); match_format(&match, result, OFP_DEFAULT_PRIORITY); ds_put_char(result, '\n'); } static void trace_report(struct xlate_in *, int recurse, const char *format, ...) OVS_PRINTF_FORMAT(3, 4); static void trace_report_valist(struct xlate_in *, int recurse, const char *format, va_list args) OVS_PRINTF_FORMAT(3, 0); static void trace_resubmit(struct xlate_in *xin, struct rule_dpif *rule, int recurse) { struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin); struct ds *result = trace->result; if (!recurse) { if (rule == xin->ofproto->miss_rule) { trace_report(xin, recurse, "No match, flow generates \"packet in\"s."); } else if (rule == xin->ofproto->no_packet_in_rule) { trace_report(xin, recurse, "No match, packets dropped because " "OFPPC_NO_PACKET_IN is set on in_port."); } else if (rule == xin->ofproto->drop_frags_rule) { trace_report(xin, recurse, "Packets dropped because they are IP " "fragments and the fragment handling mode is " "\"drop\"."); } } ds_put_char(result, '\n'); if (recurse) { trace_format_flow(result, recurse, "Resubmitted flow", trace); trace_format_regs(result, recurse, "Resubmitted regs", trace); trace_format_odp(result, recurse, "Resubmitted odp", trace); trace_format_megaflow(result, recurse, "Resubmitted megaflow", trace); } trace_format_rule(result, recurse, rule); } static void trace_report_valist(struct xlate_in *xin, int recurse, const char *format, va_list args) { struct trace_ctx *trace = CONTAINER_OF(xin, struct trace_ctx, xin); struct ds *result = trace->result; ds_put_char_multiple(result, '\t', recurse); ds_put_format_valist(result, format, args); ds_put_char(result, '\n'); } static void trace_report(struct xlate_in *xin, int recurse, const char *format, ...) { va_list args; va_start(args, format); trace_report_valist(xin, recurse, format, args); va_end(args); } /* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following * forms are supported: * * - [dpname] odp_flow [-generate | packet] * - bridge br_flow [-generate | packet] * * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure * returns a nonnull malloced error message. */ static char * OVS_WARN_UNUSED_RESULT parse_flow_and_packet(int argc, const char *argv[], struct ofproto_dpif **ofprotop, struct flow *flow, struct dp_packet **packetp) { const struct dpif_backer *backer = NULL; const char *error = NULL; char *m_err = NULL; struct simap port_names = SIMAP_INITIALIZER(&port_names); struct dp_packet *packet; struct ofpbuf odp_key; struct ofpbuf odp_mask; ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); /* Handle "-generate" or a hex string as the last argument. */ if (!strcmp(argv[argc - 1], "-generate")) { packet = dp_packet_new(0); argc--; } else { error = eth_from_hex(argv[argc - 1], &packet); if (!error) { argc--; } else if (argc == 4) { /* The 3-argument form must end in "-generate' or a hex string. */ goto exit; } error = NULL; } /* odp_flow can have its in_port specified as a name instead of port no. * We do not yet know whether a given flow is a odp_flow or a br_flow. * But, to know whether a flow is odp_flow through odp_flow_from_string(), * we need to create a simap of name to port no. */ if (argc == 3) { const char *dp_type; if (!strncmp(argv[1], "ovs-", 4)) { dp_type = argv[1] + 4; } else { dp_type = argv[1]; } backer = shash_find_data(&all_dpif_backers, dp_type); } else if (argc == 2) { struct shash_node *node; if (shash_count(&all_dpif_backers) == 1) { node = shash_first(&all_dpif_backers); backer = node->data; } } else { error = "Syntax error"; goto exit; } if (backer && backer->dpif) { struct dpif_port dpif_port; struct dpif_port_dump port_dump; DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, backer->dpif) { simap_put(&port_names, dpif_port.name, odp_to_u32(dpif_port.port_no)); } } /* Parse the flow and determine whether a datapath or * bridge is specified. If function odp_flow_key_from_string() * returns 0, the flow is a odp_flow. If function * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */ if (!odp_flow_from_string(argv[argc - 1], &port_names, &odp_key, &odp_mask)) { if (!backer) { error = "Cannot find the datapath"; goto exit; } if (odp_flow_key_to_flow(odp_key.data, odp_key.size, flow) == ODP_FIT_ERROR) { error = "Failed to parse datapath flow key"; goto exit; } *ofprotop = xlate_lookup_ofproto(backer, flow, &flow->in_port.ofp_port); if (*ofprotop == NULL) { error = "Invalid datapath flow"; goto exit; } vsp_adjust_flow(*ofprotop, flow, NULL); } else { char *err = parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL); if (err) { m_err = xasprintf("Bad openflow flow syntax: %s", err); free(err); goto exit; } else { if (argc != 3) { error = "Must specify bridge name"; goto exit; } *ofprotop = ofproto_dpif_lookup(argv[1]); if (!*ofprotop) { error = "Unknown bridge name"; goto exit; } } } /* Generate a packet, if requested. */ if (packet) { if (!dp_packet_size(packet)) { flow_compose(packet, flow); } else { /* Use the metadata from the flow and the packet argument * to reconstruct the flow. */ pkt_metadata_from_flow(&packet->md, flow); flow_extract(packet, flow); } } exit: if (error && !m_err) { m_err = xstrdup(error); } if (m_err) { dp_packet_delete(packet); packet = NULL; } *packetp = packet; ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); simap_destroy(&port_names); return m_err; } static void ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { struct ofproto_dpif *ofproto; struct dp_packet *packet; char *error; struct flow flow; error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); if (!error) { struct ds result; ds_init(&result); ofproto_trace(ofproto, &flow, packet, NULL, 0, &result); unixctl_command_reply(conn, ds_cstr(&result)); ds_destroy(&result); dp_packet_delete(packet); } else { unixctl_command_reply_error(conn, error); free(error); } } static void ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { enum ofputil_protocol usable_protocols; struct ofproto_dpif *ofproto; bool enforce_consistency; struct ofpbuf ofpacts; struct dp_packet *packet; struct ds result; struct flow flow; uint16_t in_port; /* Three kinds of error return values! */ enum ofperr retval; char *error; packet = NULL; ds_init(&result); ofpbuf_init(&ofpacts, 0); /* Parse actions. */ error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols); if (error) { unixctl_command_reply_error(conn, error); free(error); goto exit; } /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of * consistency between the flow and the actions. With -consistent, we * enforce consistency even for a flow supported in OpenFlow 1.0. */ if (!strcmp(argv[1], "-consistent")) { enforce_consistency = true; argv++; argc--; } else { enforce_consistency = false; } error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet); if (error) { unixctl_command_reply_error(conn, error); free(error); goto exit; } /* Do the same checks as handle_packet_out() in ofproto.c. * * We pass a 'table_id' of 0 to ofpacts_check(), which isn't * strictly correct because these actions aren't in any table, but it's OK * because it 'table_id' is used only to check goto_table instructions, but * packet-outs take a list of actions and therefore it can't include * instructions. * * We skip the "meter" check here because meter is an instruction, not an * action, and thus cannot appear in ofpacts. */ in_port = ofp_to_u16(flow.in_port.ofp_port); if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) { unixctl_command_reply_error(conn, "invalid in_port"); goto exit; } if (enforce_consistency) { retval = ofpacts_check_consistency(ofpacts.data, ofpacts.size, &flow, u16_to_ofp(ofproto->up.max_ports), 0, ofproto->up.n_tables, usable_protocols); } else { retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow, u16_to_ofp(ofproto->up.max_ports), 0, ofproto->up.n_tables, &usable_protocols); } if (!retval) { retval = ofproto_check_ofpacts(&ofproto->up, ofpacts.data, ofpacts.size); } if (retval) { ds_clear(&result); ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval)); unixctl_command_reply_error(conn, ds_cstr(&result)); goto exit; } ofproto_trace(ofproto, &flow, packet, ofpacts.data, ofpacts.size, &result); unixctl_command_reply(conn, ds_cstr(&result)); exit: ds_destroy(&result); dp_packet_delete(packet); ofpbuf_uninit(&ofpacts); } /* Implements a "trace" through 'ofproto''s flow table, appending a textual * description of the results to 'ds'. * * The trace follows a packet with the specified 'flow' through the flow * table. 'packet' may be nonnull to trace an actual packet, with consequent * side effects (if it is nonnull then its flow must be 'flow'). * * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to * trace, otherwise the actions are determined by a flow table lookup. */ static void ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow, const struct dp_packet *packet, const struct ofpact ofpacts[], size_t ofpacts_len, struct ds *ds) { struct trace_ctx trace; enum xlate_error error; ds_put_format(ds, "Bridge: %s\n", ofproto->up.name); ds_put_cstr(ds, "Flow: "); flow_format(ds, flow); ds_put_char(ds, '\n'); ofpbuf_init(&trace.odp_actions, 0); trace.result = ds; trace.key = flow; /* Original flow key, used for megaflow. */ trace.flow = *flow; /* May be modified by actions. */ xlate_in_init(&trace.xin, ofproto, flow, flow->in_port.ofp_port, NULL, ntohs(flow->tcp_flags), packet, &trace.wc, &trace.odp_actions); trace.xin.ofpacts = ofpacts; trace.xin.ofpacts_len = ofpacts_len; trace.xin.resubmit_hook = trace_resubmit; trace.xin.report_hook = trace_report_valist; error = xlate_actions(&trace.xin, &trace.xout); ds_put_char(ds, '\n'); trace.xin.flow.actset_output = 0; trace_format_flow(ds, 0, "Final flow", &trace); trace_format_megaflow(ds, 0, "Megaflow", &trace); ds_put_cstr(ds, "Datapath actions: "); format_odp_actions(ds, trace.odp_actions.data, trace.odp_actions.size); if (error != XLATE_OK) { ds_put_format(ds, "\nTranslation failed (%s), packet is dropped.\n", xlate_strerror(error)); } else if (trace.xout.slow) { enum slow_path_reason slow; ds_put_cstr(ds, "\nThis flow is handled by the userspace " "slow path because it:"); slow = trace.xout.slow; while (slow) { enum slow_path_reason bit = rightmost_1bit(slow); ds_put_format(ds, "\n\t- %s.", slow_path_reason_to_explanation(bit)); slow &= ~bit; } } xlate_out_uninit(&trace.xout); ofpbuf_uninit(&trace.odp_actions); } /* Store the current ofprotos in 'ofproto_shash'. Returns a sorted list * of the 'ofproto_shash' nodes. It is the responsibility of the caller * to destroy 'ofproto_shash' and free the returned value. */ static const struct shash_node ** get_ofprotos(struct shash *ofproto_shash) { const struct ofproto_dpif *ofproto; HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { char *name = xasprintf("%s@%s", ofproto->up.type, ofproto->up.name); shash_add_nocopy(ofproto_shash, name, ofproto); } return shash_sort(ofproto_shash); } static void ofproto_unixctl_dpif_dump_dps(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; struct shash ofproto_shash; const struct shash_node **sorted_ofprotos; int i; shash_init(&ofproto_shash); sorted_ofprotos = get_ofprotos(&ofproto_shash); for (i = 0; i < shash_count(&ofproto_shash); i++) { const struct shash_node *node = sorted_ofprotos[i]; ds_put_format(&ds, "%s\n", node->name); } shash_destroy(&ofproto_shash); free(sorted_ofprotos); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void dpif_show_backer(const struct dpif_backer *backer, struct ds *ds) { const struct shash_node **ofprotos; struct dpif_dp_stats dp_stats; struct shash ofproto_shash; size_t i; dpif_get_dp_stats(backer->dpif, &dp_stats); ds_put_format(ds, "%s: hit:%"PRIu64" missed:%"PRIu64"\n", dpif_name(backer->dpif), dp_stats.n_hit, dp_stats.n_missed); shash_init(&ofproto_shash); ofprotos = get_ofprotos(&ofproto_shash); for (i = 0; i < shash_count(&ofproto_shash); i++) { struct ofproto_dpif *ofproto = ofprotos[i]->data; const struct shash_node **ports; size_t j; if (ofproto->backer != backer) { continue; } ds_put_format(ds, "\t%s:\n", ofproto->up.name); ports = shash_sort(&ofproto->up.port_by_name); for (j = 0; j < shash_count(&ofproto->up.port_by_name); j++) { const struct shash_node *node = ports[j]; struct ofport *ofport = node->data; struct smap config; odp_port_t odp_port; ds_put_format(ds, "\t\t%s %u/", netdev_get_name(ofport->netdev), ofport->ofp_port); odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port); if (odp_port != ODPP_NONE) { ds_put_format(ds, "%"PRIu32":", odp_port); } else { ds_put_cstr(ds, "none:"); } ds_put_format(ds, " (%s", netdev_get_type(ofport->netdev)); smap_init(&config); if (!netdev_get_config(ofport->netdev, &config)) { const struct smap_node **nodes; size_t i; nodes = smap_sort(&config); for (i = 0; i < smap_count(&config); i++) { const struct smap_node *node = nodes[i]; ds_put_format(ds, "%c %s=%s", i ? ',' : ':', node->key, node->value); } free(nodes); } smap_destroy(&config); ds_put_char(ds, ')'); ds_put_char(ds, '\n'); } free(ports); } shash_destroy(&ofproto_shash); free(ofprotos); } static void ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED) { struct ds ds = DS_EMPTY_INITIALIZER; const struct shash_node **backers; int i; backers = shash_sort(&all_dpif_backers); for (i = 0; i < shash_count(&all_dpif_backers); i++) { dpif_show_backer(backers[i]->data, &ds); } free(backers); unixctl_command_reply(conn, ds_cstr(&ds)); ds_destroy(&ds); } static void ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { const struct ofproto_dpif *ofproto; struct ds ds = DS_EMPTY_INITIALIZER; bool verbosity = false; struct dpif_port dpif_port; struct dpif_port_dump port_dump; struct hmap portno_names; struct dpif_flow_dump *flow_dump; struct dpif_flow_dump_thread *flow_dump_thread; struct dpif_flow f; int error; ofproto = ofproto_dpif_lookup(argv[argc - 1]); if (!ofproto) { unixctl_command_reply_error(conn, "no such bridge"); return; } if (argc > 2 && !strcmp(argv[1], "-m")) { verbosity = true; } hmap_init(&portno_names); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, ofproto->backer->dpif) { odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name); } ds_init(&ds); flow_dump = dpif_flow_dump_create(ofproto->backer->dpif, false); flow_dump_thread = dpif_flow_dump_thread_create(flow_dump); while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) { struct flow flow; if (odp_flow_key_to_flow(f.key, f.key_len, &flow) == ODP_FIT_ERROR || xlate_lookup_ofproto(ofproto->backer, &flow, NULL) != ofproto) { continue; } if (verbosity) { odp_format_ufid(&f.ufid, &ds); ds_put_cstr(&ds, " "); } odp_flow_format(f.key, f.key_len, f.mask, f.mask_len, &portno_names, &ds, verbosity); ds_put_cstr(&ds, ", "); dpif_flow_stats_format(&f.stats, &ds); ds_put_cstr(&ds, ", actions:"); format_odp_actions(&ds, f.actions, f.actions_len); ds_put_char(&ds, '\n'); } dpif_flow_dump_thread_destroy(flow_dump_thread); error = dpif_flow_dump_destroy(flow_dump); if (error) { ds_clear(&ds); ds_put_format(&ds, "dpif/dump_flows failed: %s", ovs_strerror(errno)); unixctl_command_reply_error(conn, ds_cstr(&ds)); } else { unixctl_command_reply(conn, ds_cstr(&ds)); } odp_portno_names_destroy(&portno_names); hmap_destroy(&portno_names); ds_destroy(&ds); } static void ofproto_revalidate_all_backers(void) { const struct shash_node **backers; int i; backers = shash_sort(&all_dpif_backers); for (i = 0; i < shash_count(&all_dpif_backers); i++) { struct dpif_backer *backer = backers[i]->data; backer->need_revalidate = REV_RECONFIGURE; } free(backers); } static void disable_tnl_push_pop(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, const char *argv[], void *aux OVS_UNUSED) { if (!strcasecmp(argv[1], "off")) { ofproto_use_tnl_push_pop = false; unixctl_command_reply(conn, "Tunnel push-pop off"); ofproto_revalidate_all_backers(); } else if (!strcasecmp(argv[1], "on")) { ofproto_use_tnl_push_pop = true; unixctl_command_reply(conn, "Tunnel push-pop on"); ofproto_revalidate_all_backers(); } else { unixctl_command_reply_error(conn, "Invalid argument"); } } static void ofproto_unixctl_init(void) { static bool registered; if (registered) { return; } registered = true; unixctl_command_register( "ofproto/trace", "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]", 1, 3, ofproto_unixctl_trace, NULL); unixctl_command_register( "ofproto/trace-packet-out", "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions", 2, 6, ofproto_unixctl_trace_actions, NULL); unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, ofproto_unixctl_fdb_show, NULL); unixctl_command_register("mdb/flush", "[bridge]", 0, 1, ofproto_unixctl_mcast_snooping_flush, NULL); unixctl_command_register("mdb/show", "bridge", 1, 1, ofproto_unixctl_mcast_snooping_show, NULL); unixctl_command_register("dpif/dump-dps", "", 0, 0, ofproto_unixctl_dpif_dump_dps, NULL); unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show, NULL); unixctl_command_register("dpif/dump-flows", "[-m] bridge", 1, 2, ofproto_unixctl_dpif_dump_flows, NULL); unixctl_command_register("ofproto/tnl-push-pop", "[on]|[off]", 1, 1, disable_tnl_push_pop, NULL); } /* Returns true if 'table' is the table used for internal rules, * false otherwise. */ bool table_is_internal(uint8_t table_id) { return table_id == TBL_INTERNAL; } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) * * This is deprecated. It is only for compatibility with broken device drivers * in old versions of Linux that do not properly support VLANs when VLAN * devices are not used. When broken device drivers are no longer in * widespread use, we will delete these interfaces. */ static int set_realdev(struct ofport *ofport_, ofp_port_t realdev_ofp_port, int vid) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport_->ofproto); struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); if (realdev_ofp_port == ofport->realdev_ofp_port && vid == ofport->vlandev_vid) { return 0; } ofproto->backer->need_revalidate = REV_RECONFIGURE; if (ofport->realdev_ofp_port) { vsp_remove(ofport); } if (realdev_ofp_port && ofport->bundle) { /* vlandevs are enslaved to their realdevs, so they are not allowed to * themselves be part of a bundle. */ bundle_set(ofport_->ofproto, ofport->bundle, NULL); } ofport->realdev_ofp_port = realdev_ofp_port; ofport->vlandev_vid = vid; if (realdev_ofp_port) { vsp_add(ofport, realdev_ofp_port, vid); } return 0; } static uint32_t hash_realdev_vid(ofp_port_t realdev_ofp_port, int vid) { return hash_2words(ofp_to_u16(realdev_ofp_port), vid); } bool ofproto_has_vlan_splinters(const struct ofproto_dpif *ofproto) OVS_EXCLUDED(ofproto->vsp_mutex) { /* hmap_is_empty is thread safe. */ return !hmap_is_empty(&ofproto->realdev_vid_map); } static ofp_port_t vsp_realdev_to_vlandev__(const struct ofproto_dpif *ofproto, ofp_port_t realdev_ofp_port, ovs_be16 vlan_tci) OVS_REQUIRES(ofproto->vsp_mutex) { if (!hmap_is_empty(&ofproto->realdev_vid_map)) { int vid = vlan_tci_to_vid(vlan_tci); const struct vlan_splinter *vsp; HMAP_FOR_EACH_WITH_HASH (vsp, realdev_vid_node, hash_realdev_vid(realdev_ofp_port, vid), &ofproto->realdev_vid_map) { if (vsp->realdev_ofp_port == realdev_ofp_port && vsp->vid == vid) { return vsp->vlandev_ofp_port; } } } return realdev_ofp_port; } /* Returns the OFP port number of the Linux VLAN device that corresponds to * 'vlan_tci' on the network device with port number 'realdev_ofp_port' in * 'struct ofport_dpif'. For example, given 'realdev_ofp_port' of eth0 and * 'vlan_tci' 9, it would return the port number of eth0.9. * * Unless VLAN splinters are enabled for port 'realdev_ofp_port', this * function just returns its 'realdev_ofp_port' argument. */ ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *ofproto, ofp_port_t realdev_ofp_port, ovs_be16 vlan_tci) OVS_EXCLUDED(ofproto->vsp_mutex) { ofp_port_t ret; /* hmap_is_empty is thread safe, see if we can return immediately. */ if (hmap_is_empty(&ofproto->realdev_vid_map)) { return realdev_ofp_port; } ovs_mutex_lock(&ofproto->vsp_mutex); ret = vsp_realdev_to_vlandev__(ofproto, realdev_ofp_port, vlan_tci); ovs_mutex_unlock(&ofproto->vsp_mutex); return ret; } static struct vlan_splinter * vlandev_find(const struct ofproto_dpif *ofproto, ofp_port_t vlandev_ofp_port) { struct vlan_splinter *vsp; HMAP_FOR_EACH_WITH_HASH (vsp, vlandev_node, hash_ofp_port(vlandev_ofp_port), &ofproto->vlandev_map) { if (vsp->vlandev_ofp_port == vlandev_ofp_port) { return vsp; } } return NULL; } /* Returns the OpenFlow port number of the "real" device underlying the Linux * VLAN device with OpenFlow port number 'vlandev_ofp_port' and stores the * VLAN VID of the Linux VLAN device in '*vid'. For example, given * 'vlandev_ofp_port' of eth0.9, it would return the OpenFlow port number of * eth0 and store 9 in '*vid'. * * Returns 0 and does not modify '*vid' if 'vlandev_ofp_port' is not a Linux * VLAN device. Unless VLAN splinters are enabled, this is what this function * always does.*/ static ofp_port_t vsp_vlandev_to_realdev(const struct ofproto_dpif *ofproto, ofp_port_t vlandev_ofp_port, int *vid) OVS_REQUIRES(ofproto->vsp_mutex) { if (!hmap_is_empty(&ofproto->vlandev_map)) { const struct vlan_splinter *vsp; vsp = vlandev_find(ofproto, vlandev_ofp_port); if (vsp) { if (vid) { *vid = vsp->vid; } return vsp->realdev_ofp_port; } } return 0; } /* Given 'flow', a flow representing a packet received on 'ofproto', checks * whether 'flow->in_port' represents a Linux VLAN device. If so, changes * 'flow->in_port' to the "real" device backing the VLAN device, sets * 'flow->vlan_tci' to the VLAN VID, and returns true. Optionally pushes the * appropriate VLAN on 'packet' if provided. Otherwise (which is always the * case unless VLAN splinters are enabled), returns false without making any * changes. */ bool vsp_adjust_flow(const struct ofproto_dpif *ofproto, struct flow *flow, struct dp_packet *packet) OVS_EXCLUDED(ofproto->vsp_mutex) { ofp_port_t realdev; int vid; /* hmap_is_empty is thread safe. */ if (hmap_is_empty(&ofproto->vlandev_map)) { return false; } ovs_mutex_lock(&ofproto->vsp_mutex); realdev = vsp_vlandev_to_realdev(ofproto, flow->in_port.ofp_port, &vid); ovs_mutex_unlock(&ofproto->vsp_mutex); if (!realdev) { return false; } /* Cause the flow to be processed as if it came in on the real device with * the VLAN device's VLAN ID. */ flow->in_port.ofp_port = realdev; flow->vlan_tci = htons((vid & VLAN_VID_MASK) | VLAN_CFI); if (packet) { /* Make the packet resemble the flow, so that it gets sent to an * OpenFlow controller properly, so that it looks correct for sFlow, * and so that flow_extract() will get the correct vlan_tci if it is * called on 'packet'. */ eth_push_vlan(packet, htons(ETH_TYPE_VLAN), flow->vlan_tci); } return true; } static void vsp_remove(struct ofport_dpif *port) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); struct vlan_splinter *vsp; ovs_mutex_lock(&ofproto->vsp_mutex); vsp = vlandev_find(ofproto, port->up.ofp_port); if (vsp) { hmap_remove(&ofproto->vlandev_map, &vsp->vlandev_node); hmap_remove(&ofproto->realdev_vid_map, &vsp->realdev_vid_node); free(vsp); port->realdev_ofp_port = 0; } else { VLOG_ERR("missing vlan device record"); } ovs_mutex_unlock(&ofproto->vsp_mutex); } static void vsp_add(struct ofport_dpif *port, ofp_port_t realdev_ofp_port, int vid) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); ovs_mutex_lock(&ofproto->vsp_mutex); if (!vsp_vlandev_to_realdev(ofproto, port->up.ofp_port, NULL) && (vsp_realdev_to_vlandev__(ofproto, realdev_ofp_port, htons(vid)) == realdev_ofp_port)) { struct vlan_splinter *vsp; vsp = xmalloc(sizeof *vsp); vsp->realdev_ofp_port = realdev_ofp_port; vsp->vlandev_ofp_port = port->up.ofp_port; vsp->vid = vid; port->realdev_ofp_port = realdev_ofp_port; hmap_insert(&ofproto->vlandev_map, &vsp->vlandev_node, hash_ofp_port(port->up.ofp_port)); hmap_insert(&ofproto->realdev_vid_map, &vsp->realdev_vid_node, hash_realdev_vid(realdev_ofp_port, vid)); } else { VLOG_ERR("duplicate vlan device record"); } ovs_mutex_unlock(&ofproto->vsp_mutex); } static odp_port_t ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port) { const struct ofport_dpif *ofport = ofp_port_to_ofport(ofproto, ofp_port); return ofport ? ofport->odp_port : ODPP_NONE; } struct ofport_dpif * odp_port_to_ofport(const struct dpif_backer *backer, odp_port_t odp_port) { struct ofport_dpif *port; ovs_rwlock_rdlock(&backer->odp_to_ofport_lock); HMAP_FOR_EACH_IN_BUCKET (port, odp_port_node, hash_odp_port(odp_port), &backer->odp_to_ofport_map) { if (port->odp_port == odp_port) { ovs_rwlock_unlock(&backer->odp_to_ofport_lock); return port; } } ovs_rwlock_unlock(&backer->odp_to_ofport_lock); return NULL; } static ofp_port_t odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port) { struct ofport_dpif *port; port = odp_port_to_ofport(ofproto->backer, odp_port); if (port && &ofproto->up == port->up.ofproto) { return port->up.ofp_port; } else { return OFPP_NONE; } } int ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto, const struct match *match, int priority, uint16_t idle_timeout, const struct ofpbuf *ofpacts, struct rule **rulep) { struct ofproto_flow_mod ofm; struct rule_dpif *rule; int error; ofm.fm.match = *match; ofm.fm.priority = priority; ofm.fm.new_cookie = htonll(0); ofm.fm.cookie = htonll(0); ofm.fm.cookie_mask = htonll(0); ofm.fm.modify_cookie = false; ofm.fm.table_id = TBL_INTERNAL; ofm.fm.command = OFPFC_ADD; ofm.fm.idle_timeout = idle_timeout; ofm.fm.hard_timeout = 0; ofm.fm.importance = 0; ofm.fm.buffer_id = 0; ofm.fm.out_port = 0; ofm.fm.flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY; ofm.fm.ofpacts = ofpacts->data; ofm.fm.ofpacts_len = ofpacts->size; ofm.fm.delete_reason = OVS_OFPRR_NONE; error = ofproto_flow_mod(&ofproto->up, &ofm); if (error) { VLOG_ERR_RL(&rl, "failed to add internal flow (%s)", ofperr_to_string(error)); *rulep = NULL; return error; } rule = rule_dpif_lookup_in_table(ofproto, ofproto_dpif_get_tables_version(ofproto), TBL_INTERNAL, &ofm.fm.match.flow, &ofm.fm.match.wc); if (rule) { *rulep = &rule->up; } else { OVS_NOT_REACHED(); } return 0; } int ofproto_dpif_delete_internal_flow(struct ofproto_dpif *ofproto, struct match *match, int priority) { struct ofproto_flow_mod ofm; int error; ofm.fm.match = *match; ofm.fm.priority = priority; ofm.fm.new_cookie = htonll(0); ofm.fm.cookie = htonll(0); ofm.fm.cookie_mask = htonll(0); ofm.fm.modify_cookie = false; ofm.fm.table_id = TBL_INTERNAL; ofm.fm.out_port = OFPP_ANY; ofm.fm.out_group = OFPG_ANY; ofm.fm.flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY; ofm.fm.command = OFPFC_DELETE_STRICT; error = ofproto_flow_mod(&ofproto->up, &ofm); if (error) { VLOG_ERR_RL(&rl, "failed to delete internal flow (%s)", ofperr_to_string(error)); return error; } return 0; } const struct ofproto_class ofproto_dpif_class = { init, enumerate_types, enumerate_names, del, port_open_type, type_run, type_wait, alloc, construct, destruct, dealloc, run, wait, NULL, /* get_memory_usage. */ type_get_memory_usage, flush, query_tables, set_tables_version, port_alloc, port_construct, port_destruct, port_dealloc, port_modified, port_reconfigured, port_query_by_name, port_add, port_del, port_get_stats, port_dump_start, port_dump_next, port_dump_done, port_poll, port_poll_wait, port_is_lacp_current, port_get_lacp_stats, NULL, /* rule_choose_table */ rule_alloc, rule_construct, rule_insert, rule_delete, rule_destruct, rule_dealloc, rule_get_stats, rule_execute, set_frag_handling, packet_out, set_netflow, get_netflow_ids, set_sflow, set_ipfix, set_cfm, cfm_status_changed, get_cfm_status, set_lldp, get_lldp_status, set_aa, aa_mapping_set, aa_mapping_unset, aa_vlan_get_queued, aa_vlan_get_queue_size, set_bfd, bfd_status_changed, get_bfd_status, set_stp, get_stp_status, set_stp_port, get_stp_port_status, get_stp_port_stats, set_rstp, get_rstp_status, set_rstp_port, get_rstp_port_status, set_queues, bundle_set, bundle_remove, mirror_set__, mirror_get_stats__, set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, set_mac_table_config, set_mcast_snooping, set_mcast_snooping_port, set_realdev, NULL, /* meter_get_features */ NULL, /* meter_set */ NULL, /* meter_get */ NULL, /* meter_del */ group_alloc, /* group_alloc */ group_construct, /* group_construct */ group_destruct, /* group_destruct */ group_dealloc, /* group_dealloc */ group_modify, /* group_modify */ group_get_stats, /* group_get_stats */ get_datapath_version, /* get_datapath_version */ }; openvswitch-2.5.9/PaxHeaders.82075/INSTALL.Windows.md0000644000000000000000000000013213534540071016725 xustar0030 mtime=1567801401.185679583 30 atime=1567801402.041685871 30 ctime=1567801423.685845346 openvswitch-2.5.9/INSTALL.Windows.md0000644000175000017500000005070213534540071020417 0ustar00jpettitjpettit00000000000000How to Build the Kernel module & userspace daemons for Windows ============================================================== Autoconf, Automake and Visual C++: --------------------------------- Open vSwitch on Linux uses autoconf and automake for generating Makefiles. It will be useful to maintain the same build system while compiling on Windows too. One approach is to compile Open vSwitch in a MinGW environment that contains autoconf and automake utilities and then use Visual C++ as a compiler and linker. The following explains the steps in some detail. * Install Mingw on a Windows machine by following the instructions at: http://www.mingw.org/wiki/Getting_Started This should install mingw at C:\Mingw and msys at C:\Mingw\msys. Add "C:\MinGW\bin" and "C:\Mingw\msys\1.0\bin" to PATH environment variable of Windows. You can either use the MinGW installer or the command line utility 'mingw-get' to install both the base packages and additional packages like automake and autoconf(version 2.68). Also make sure that /mingw mount point exists. If its not, please add/create the following entry in /etc/fstab - 'C:/MinGW /mingw'. * Install the latest Python 2.x from python.org and verify that its path is part of Windows' PATH environment variable. * You will need at least Visual Studio 2013 (update 4) to compile userspace binaries. In addition to that, if you want to compile the kernel module you will also need to install Windows Driver Kit (WDK) 8.1 Update. It is important to get the Visual Studio related environment variables and to have the $PATH inside the bash to point to the proper compiler and linker. One easy way to achieve this for VS2013 is to get into the "VS2013 x86 Native Tools Command Prompt" (in a default installation of Visual Studio 2013 this can be found under the following location: C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts) and through it enter into the bash shell available from msys by typing 'bash --login'. There is support for generating 64 bit binaries too. To compile under x64, open the "VS2013 x64 Native Tools Command Prompt" (if your current running OS is 64 bit) or "VS2013 x64 Cross Tools Command Prompt" (if your current running OS is not 64 bit) instead of opening its x86 variant. This will point the compiler and the linker to their 64 bit equivalent. If after the above step, a 'which link' inside MSYS's bash says, "/bin/link.exe", rename /bin/link.exe to something else so that the Visual studio's linker is used. You should also see a 'which sort' report "/bin/sort.exe". * For pthread support, install the library, dll and includes of pthreads-win32 project from ftp://sourceware.org/pub/pthreads-win32/prebuilt-dll-2-9-1-release to a directory (e.g.: C:/pthread). You should add the pthread-win32's dll path (e.g.: C:\pthread\dll\x86) to the Windows' PATH environment variable. * Get the Open vSwitch sources from either cloning the repo using git or from a distribution tar ball. * If you pulled the sources directly from an Open vSwitch Git tree, run boot.sh in the top source directory: % ./boot.sh * In the top source directory, configure the package by running the configure script. You should provide some configure options to choose the right compiler, linker, libraries, Open vSwitch component installation directories, etc. For example, % ./configure CC=./build-aux/cccl LD="`which link`" \ LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" By default, the above enables compiler optimization for fast code. For default compiler optimization, pass the "--with-debug" configure option. * Run make for the ported executables in the top source directory, e.g.: % make For faster compilation, you can pass the '-j' argument to make. For example, to run 4 jobs simultaneously, run 'make -j4'. Note: MSYS 1.0.18 has a bug that causes parallel make to hang. You can overcome this by downgrading to MSYS 1.0.17. A simple way to downgrade is to exit all MinGW sessions and then run the command 'mingw-get upgrade msys-core-bin=1.0.17-1' from MSVC developers command prompt. * To run all the unit tests in Open vSwitch, one at a time: % make check To run all the unit tests in Open vSwitch, up to 8 in parallel: % make check TESTSUITEFLAGS="-j8" * To install all the compiled executables on the local machine, run: % make install The above command will install the Open vSwitch executables in C:/openvswitch. You can add 'C:\openvswitch\usr\bin' and 'C:\openvswitch\usr\sbin' to Windows' PATH environment variable for easy access. OpenSSL, Open vSwitch and Visual C++ ------------------------------------ To get SSL support for Open vSwitch on Windows, do the following: * Install OpenSSL for Windows as suggested at http://www.openssl.org/related/binaries.html. The link as of this writing suggests to download it from http://slproweb.com/products/Win32OpenSSL.html Note down the directory where OpenSSL is installed (e.g.: C:/OpenSSL-Win32). * While configuring the package, specify the OpenSSL directory path. For example, % ./configure CC=./build-aux/cccl LD="`which link`" \ LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" --enable-ssl --with-openssl="C:/OpenSSL-Win32" * Run make for the ported executables. Building the Kernel datapath module ----------------------------------- * We directly use the Visual Studio 2013 IDE to compile the kernel datapath. You can open the extensions.sln file in the IDE and build the solution. * The kernel datapath can be compiled from command line as well. The top level 'make' will invoke building the kernel datapath, if the '--with-vstudiotarget' argument is specified while configuring the package. For example, % ./configure CC=./build-aux/cccl LD="`which link`" \ LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \ --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \ --with-pthread="C:/pthread" --enable-ssl \ --with-openssl="C:/OpenSSL-Win32" --with-vstudiotarget="" Possible values for "" are: "Debug" and "Release" Installing the Kernel module ---------------------------- Once you have built the solution, you can copy the following files to the target Hyper-V machines. ./datapath-windows/x64/Win8.1Debug/package/ovsext.inf ./datapath-windows/x64/Win8.1Debug/package/OVSExt.sys ./datapath-windows/x64/Win8.1Debug/package/ovsext.cat ./datapath-windows/misc/install.cmd ./datapath-windows/misc/uninstall.cmd The above path assumes that the kernel module has been built using Windows DDK 8.1 in Debug mode. Change the path appropriately, if a different WDK has been used. Steps to install the module --------------------------- 01> Run ./uninstall.cmd to remove the old extension. 02> Run ./install.cmd to insert the new one. For this to work you will have to turn on TESTSIGNING boot option or 'Disable Driver Signature Enforcement' during boot. The following commands can be used: % bcdedit /set LOADOPTIONS DISABLE_INTEGRITY_CHECKS % bcdedit /set TESTSIGNING ON % bcdedit /set nointegritychecks ON Note: you may have to restart the machine for the settings to take effect. 03> In the Virtual Switch Manager configuration you can enable the Open vSwitch Extension on an existing switch or create a new switch. If you are using an existing switch, make sure to enable the "Allow Management OS" option for VXLAN to work (covered later). The command to create a new switch named 'OVS-Extended-Switch' using a physical NIC named 'Ethernet 1' is: % New-VMSwitch "OVS-Extended-Switch" -AllowManagementOS $true \ -NetAdapterName "Ethernet 1" Note: you can obtain the list of physical NICs on the host using 'Get-NetAdapter' command. 04> In the properties of any switch, you should should now see "Open vSwitch Extension" under 'Extensions'. Click the check box to enable the extension. An alternative way to do the same is to run the following command: % Enable-VMSwitchExtension "Open vSwitch Extension" OVS-Extended-Switch Note: If you enabled the extension using the command line, a delay of a few seconds has been observed for the change to be reflected in the UI. This is not a bug in Open vSwitch. Steps to run the user processes & configure ports ------------------------------------------------- The following steps assume that you have installed the Open vSwitch utilities in the local machine via 'make install'. 01> Create the database. % ovsdb-tool create C:\openvswitch\etc\openvswitch\conf.db \ C:\openvswitch\usr\share\openvswitch\vswitch.ovsschema 02> Start the ovsdb-server and initialize the database. % ovsdb-server -vfile:info --remote=punix:db.sock --log-file --pidfile \ --detach % ovs-vsctl --no-wait init If you would like to terminate the started ovsdb-server, run: % ovs-appctl -t ovsdb-server exit (Note that the logfile is created at C:/openvswitch/var/log/openvswitch/) 03> Start ovs-vswitchd. % ovs-vswitchd -vfile:info --log-file --pidfile --detach If you would like to terminate the started ovs-vswitchd, run: % ovs-appctl exit (Note that the logfile is created at C:/openvswitch/var/log/openvswitch/) 04> Create integration bridge & pif bridge % ovs-vsctl add-br br-int % ovs-vsctl add-br br-pif NOTE: There's a known bug that running the ovs-vsctl command does not terminate. This is generally solved by having ovs-vswitchd running. If you face the issue despite that, hit Ctrl-C to terminate ovs-vsctl and check the output to see if your command succeeded. NOTE: There's a known bug that the ports added to OVSDB via ovs-vsctl don't get to the kernel datapath immediately, ie. they don't show up in the output of "ovs-dpctl show" even though they show up in output of "ovs-vsctl show". In order to workaround this issue, restart ovs-vswitchd. (You can terminate ovs-vswitchd by running 'ovs-appctl exit'.) 05> Dump the ports in the kernel datapath % ovs-dpctl show * Sample output is as follows: % ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 2: br-pif (internal) <<< internal port on 'br-pif' bridge port 1: br-int (internal) <<< internal port on 'br-int' bridge 06> Dump the ports in the OVSDB % ovs-vsctl show * Sample output is as follows: % ovs-vsctl show a56ec7b5-5b1f-49ec-a795-79f6eb63228b Bridge br-pif Port br-pif Interface br-pif type: internal Bridge br-int Port br-int Interface br-int type: internal 07> Add the physical NIC and the internal port to br-pif. In OVS for Hyper-V, we use the name of the adapter on top of which the Hyper-V virtual switch was created, as a special name to refer to the physical NICs connected to the Hyper-V switch. I.e. let us suppose we created the Hyper-V virtual switch on top of the adapter named 'Ethernet0'. In OVS for Hyper-V, we use that name('Ethernet0') as a special name to refer to that adapter. Note: Currently, we assume that the Hyper-V switch on which OVS extension is enabled has a single physical NIC connected to it. Internal port is the virtual adapter created on the Hyper-V switch using the 'AllowManagementOS' setting. This has already been setup while creating the switch using the instructions above. In OVS for Hyper-V, we use a the name of that specific adapter as a special name to refer to that adapter. By default it is created under the following rule "vEthernet ()". As a whole example, if we issue the following in a powershell console: PS C:\package\binaries> Get-NetAdapter | select Name,MacAddress,InterfaceDescription Name MacAddress InterfaceDescription ---- ---------- -------------------- Ethernet1 00-0C-29-94-05-65 Intel(R) PRO/1000 MT Network Connection vEthernet (external) 00-0C-29-94-05-5B Hyper-V Virtual Ethernet Adapter #2 Ethernet0 00-0C-29-94-05-5B Intel(R) PRO/1000 MT Network Connection #2 PS C:\package\binaries> Get-VMSwitch Name SwitchType NetAdapterInterfaceDescription ---- ---------- ------------------------------ external External Intel(R) PRO/1000 MT Network Connection #2 We can see that we have a switch(external) created upon adapter name 'Ethernet0' with an internal port under name 'vEthernet (external)'. Thus resulting into the following ovs-vsctl commands % ovs-vsctl add-port br-pif Ethernet0 % ovs-vsctl add-port br-pif "vEthernet (external)" * Dumping the ports should show the additional ports that were just added. Sample output shows up as follows: % ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 4: vEthernet (external) (internal) <<< 'AllowManagementOS' adapter on Hyper-V switch port 2: br-pif (internal) port 1: br-int (internal) port 3: Ethernet0 <<< Physical NIC % ovs-vsctl show a56ec7b5-5b1f-49ec-a795-79f6eb63228b Bridge br-pif Port "vEthernet (external)" Interface "vEthernet (external)" Port br-pif Interface br-pif type: internal Port "Ethernet0" Interface "Ethernet0" Bridge br-int Port br-int Interface br-int type: internal 08> Add the VIFs to br-int Adding VIFs to openvswitch is a two step procedure. The first step is to assign a 'OVS port name' which is a unique name across all VIFs on this Hyper-V. The next step is to add the VIF to the ovsdb using its 'OVS port name' as key. 08a> Assign a unique 'OVS port name' to the VIF Note that the VIF needs to have been disconnected from the Hyper-V switch before assigning a 'OVS port name' to it. In the example below, we assign a 'OVS port name' called 'ovs-port-a' to a VIF on a VM by name 'VM1'. By using index 0 for '$vnic', the first VIF of the VM is being addressed. After assigning the name 'ovs-port-a', the VIF is connected back to the Hyper-V switch with name 'OVS-HV-Switch', which is assumed to be the Hyper-V switch with OVS extension enabled. Eg: % import-module .\datapath-windows\misc\OVS.psm1 % $vnic = Get-VMNetworkAdapter % Disconnect-VMNetworkAdapter -VMNetworkAdapter $vnic[0] % $vnic[0] | Set-VMNetworkAdapterOVSPort -OVSPortName ovs-port-a % Connect-VMNetworkAdapter -VMNetworkAdapter $vnic[0] \ -SwitchName OVS-Extended-Switch 08b> Add the VIFs to br-int in ovsdb Eg: % ovs-vsctl add-port br-int ovs-port-a 09> Verify the status % ovs-dpctl show system@ovs-system: lookups: hit:0 missed:0 lost:0 flows: 0 port 4: vEthernet (external) (internal) port 5: ovs-port-a port 2: br-pif (internal) port 1: br-int (internal port 3: Ethernet0 % ovs-vsctl show 4cd86499-74df-48bd-a64d-8d115b12a9f2 Bridge br-pif Port "vEthernet (external)" Interface "vEthernet (external)" Port "Ethernet0" Interface "Ethernet0" Port br-pif Interface br-pif type: internal Bridge br-int Port br-int Interface br-int type: internal Port "ovs-port-a" Interface "ovs-port-a" Steps to configure patch ports and switch VLAN tagging ------------------------------------------------------ The Windows Open vSwitch implementation support VLAN tagging in the switch. Switch VLAN tagging along with patch ports between 'br-int' and 'br-pif' is used to configure VLAN tagging functionality between two VMs on different Hyper-Vs. The following examples demonstrate how it can be done: 01> Add a patch port from br-int to br-pif % ovs-vsctl add-port br-int patch-to-pif % ovs-vsctl set interface patch-to-pif type=patch \ options:peer=patch-to-int 02> Add a patch port from br-pif to br-int % ovs-vsctl add-port br-pif patch-to-int % ovs-vsctl set interface patch-to-int type=patch \ options:peer=patch-to-pif 03> Re-Add the VIF ports with the VLAN tag % ovs-vsctl add-port br-int ovs-port-a tag=900 % ovs-vsctl add-port br-int ovs-port-b tag=900 Steps to add tunnels -------------------------- The Windows Open vSwitch implementation support VXLAN and STT tunnels. To add tunnels, the following steps serve as examples. Note that, any patch ports created between br-int and br-pif MUST be beleted prior to adding tunnels. 01> Add the tunnel port between 172.168.201.101 <-> 172.168.201.102 % ovs-vsctl add-port br-int tun-1 % ovs-vsctl set Interface tun-1 type=port-type % ovs-vsctl set Interface tun-1 options:local_ip=172.168.201.101 % ovs-vsctl set Interface tun-1 options:remote_ip=172.168.201.102 % ovs-vsctl set Interface tun-1 options:in_key=flow % ovs-vsctl set Interface tun-1 options:out_key=flow 02> Add the tunnel port between 172.168.201.101 <-> 172.168.201.105 % ovs-vsctl add-port br-int tun-2 % ovs-vsctl set Interface tun-2 type=port-type % ovs-vsctl set Interface tun-2 options:local_ip=172.168.201.102 % ovs-vsctl set Interface tun-2 options:remote_ip=172.168.201.105 % ovs-vsctl set Interface tun-2 options:in_key=flow % ovs-vsctl set Interface tun-2 options:out_key=flow Where port-type is the string stt or vxlan Requirements ------------ * We require that you don't disable the "Allow management operating system to share this network adapter" under 'Virtual Switch Properties' > 'Connection type: External network', in the HyperV virtual network switch configuration. * Checksum Offloads While there is some support for checksum/segmentation offloads in software, this is still a work in progress. Till the support is complete we recommend disabling TX/RX offloads for both the VM's as well as the HyperV. Windows Services ---------------- Open vSwitch daemons come with support to run as a Windows service. The instructions here assume that you have installed the Open vSwitch utilities and daemons via 'make install'. The commands shown here can be run from MSYS bash or Windows command prompt. * Create the database. % ovsdb-tool create C:/openvswitch/etc/openvswitch/conf.db \ "C:/openvswitch/usr/share/openvswitch/vswitch.ovsschema" * Create the ovsdb-server service and start it. % sc create ovsdb-server binpath="C:/openvswitch/usr/sbin/ovsdb-server.exe C:/openvswitch/etc/openvswitch/conf.db -vfile:info --log-file --pidfile --remote=punix:db.sock --service --service-monitor" One of the common issues with creating a Windows service is with mungled paths. You can make sure that the correct path has been registered with the Windows services manager by running: % sc qc ovsdb-server Start the service. % sc start ovsdb-server Check that the service is healthy by running: % sc query ovsdb-server * Initialize the database. % ovs-vsctl --no-wait init * Create the ovs-vswitchd service and start it. % sc create ovs-vswitchd binpath="C:/openvswitch/usr/sbin/ovs-vswitchd.exe --pidfile -vfile:info --log-file --service --service-monitor" % sc start ovs-vswitchd Check that the service is healthy by running: % sc query ovs-vswitchd * To stop and delete the services, run: % sc stop ovs-vswitchd % sc stop ovsdb-server % sc delete ovs-vswitchd % sc delete ovsdb-server Windows autobuild service ------------------------- AppVeyor (appveyor.com) provides a free Windows autobuild service for opensource projects. Open vSwitch has integration with AppVeyor for continuous build. A developer can build test his changes for Windows by logging into appveyor.com using a github account, creating a new project by linking it to his development repository in github and triggering a new build. TODO ---- * Investigate the working of sFlow on Windows and re-enable the unit tests. * Investigate and add the feature to provide QOS. * Sign the driver & create an MSI for installing the different OpenvSwitch components on windows. openvswitch-2.5.9/PaxHeaders.82075/rhel0000644000000000000000000000013213534540120014514 xustar0030 mtime=1567801424.209849209 30 atime=1567801425.625859648 30 ctime=1567801424.209849209 openvswitch-2.5.9/rhel/0000755000175000017500000000000013534540120016257 5ustar00jpettitjpettit00000000000000openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_ovn-controller.service0000644000000000000000000000013013534540071026034 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20584918 openvswitch-2.5.9/rhel/usr_lib_systemd_system_ovn-controller.service0000644000175000017500000000142413534540071027525 0ustar00jpettitjpettit00000000000000# # You may override the following variables to customize ovn-controller behavior: # # OVS_DB - Set this variable to the location of the ovsdb server that is # serving the Open_vSwitch database for the local ovs-vswitchd. # See the manpage for ovn-controller for more details on the # format for the db location. # [Unit] Description=OVN controller daemon After=syslog.target Requires=openvswitch.service After=openvswitch.service [Service] Type=simple Environment=OVS_RUNDIR=%t/openvswitch Environment=OVS_DB=unix:%t/openvswitch/db.sock ExecStart=/usr/bin/ovn-controller -vconsole:emer -vsyslog:err -vfile:info \ --log-file=/var/log/openvswitch/ovn-controller.log \ --no-chdir --pidfile=${OVS_RUNDIR}/ovn-controller.pid ${OVS_DB} openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-kmod-rhel6.spec.in0000644000000000000000000000013113534540071022375 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.193849091 openvswitch-2.5.9/rhel/openvswitch-kmod-rhel6.spec.in0000644000175000017500000000430713534540071024070 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch kernel modules on Red Hat Enterprise # Linux 6. # Copyright (C) 2011, 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. %define oname openvswitch Name: %{oname}-kmod Version: @VERSION@ Release: 1%{?dist} Summary: Open vSwitch kernel module Group: System/Kernel License: GPLv2 URL: http://openvswitch.org/ Source0: %{oname}-%{version}.tar.gz Source1: %{oname}-kmod.files BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: %kernel_module_package_buildreqs # Without this we get an empty openvswitch-debuginfo package (whose name # conflicts with the openvswitch-debuginfo package for OVS userspace). %undefine _enable_debug_packages # Use -D 'kversion 2.6.32-131.6.1.el6.x86_64' to build package # for specified kernel version. %{?kversion:%define kernel_version %kversion} # Use -D 'kflavors default debug kdump' to build packages for # specified kernel variants. %{!?kflavors:%define kflavors default} %kernel_module_package -n %{oname} -f %{SOURCE1} %kflavors %description Open vSwitch Linux kernel module. %prep %setup -n %{oname}-%{version} cat > %{oname}.conf << EOF override %{oname} * extra/%{oname} override %{oname} * weak-updates/%{oname} EOF %build for flavor in %flavors_to_build; do mkdir _$flavor (cd _$flavor && ../configure --with-linux="%{kernel_source $flavor}") %{__make} -C _$flavor/datapath/linux %{?_smp_mflags} done %install export INSTALL_MOD_PATH=$RPM_BUILD_ROOT export INSTALL_MOD_DIR=extra/%{oname} for flavor in %flavors_to_build ; do make -C %{kernel_source $flavor} modules_install \ M="`pwd`"/_$flavor/datapath/linux # Cleanup unnecessary kernel-generated module dependency files. find $INSTALL_MOD_PATH/lib/modules -iname 'modules.*' -exec rm {} \; done install -d %{buildroot}%{_sysconfdir}/depmod.d/ install -m 644 %{oname}.conf %{buildroot}%{_sysconfdir}/depmod.d/ %clean rm -rf $RPM_BUILD_ROOT openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-kmod-rhel6.spec0000644000000000000000000000013213534540117021772 xustar0030 mtime=1567801423.409843311 30 atime=1567801423.405843283 30 ctime=1567801424.193849091 openvswitch-2.5.9/rhel/openvswitch-kmod-rhel6.spec0000644000175000017500000000441613534540117023465 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch kernel modules on Red Hat Enterprise # Linux 6. # Copyright (C) 2011, 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. %define oname openvswitch Name: %{oname}-kmod Version: 2.5.9 Release: 1%{?dist} Summary: Open vSwitch kernel module Group: System/Kernel License: GPLv2 URL: http://openvswitch.org/ Source0: %{oname}-%{version}.tar.gz Source1: %{oname}-kmod.files BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: %kernel_module_package_buildreqs # Without this we get an empty openvswitch-debuginfo package (whose name # conflicts with the openvswitch-debuginfo package for OVS userspace). %undefine _enable_debug_packages # Use -D 'kversion 2.6.32-131.6.1.el6.x86_64' to build package # for specified kernel version. %{?kversion:%define kernel_version %kversion} # Use -D 'kflavors default debug kdump' to build packages for # specified kernel variants. %{!?kflavors:%define kflavors default} %kernel_module_package -n %{oname} -f %{SOURCE1} %kflavors %description Open vSwitch Linux kernel module. %prep %setup -n %{oname}-%{version} cat > %{oname}.conf << EOF override %{oname} * extra/%{oname} override %{oname} * weak-updates/%{oname} EOF %build for flavor in %flavors_to_build; do mkdir _$flavor (cd _$flavor && ../configure --with-linux="%{kernel_source $flavor}") %{__make} -C _$flavor/datapath/linux %{?_smp_mflags} done %install export INSTALL_MOD_PATH=$RPM_BUILD_ROOT export INSTALL_MOD_DIR=extra/%{oname} for flavor in %flavors_to_build ; do make -C %{kernel_source $flavor} modules_install \ M="`pwd`"/_$flavor/datapath/linux # Cleanup unnecessary kernel-generated module dependency files. find $INSTALL_MOD_PATH/lib/modules -iname 'modules.*' -exec rm {} \; done install -d %{buildroot}%{_sysconfdir}/depmod.d/ install -m 644 %{oname}.conf %{buildroot}%{_sysconfdir}/depmod.d/ %clean rm -rf $RPM_BUILD_ROOT openvswitch-2.5.9/rhel/PaxHeaders.82075/etc_sysconfig_network-scripts_ifdown-ovs0000644000000000000000000000013113534540071024770 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.189849061 openvswitch-2.5.9/rhel/etc_sysconfig_network-scripts_ifdown-ovs0000755000175000017500000000356113534540071026467 0ustar00jpettitjpettit00000000000000#!/bin/bash # Copyright (c) 2011 Alexey I. Froloff. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. . /etc/init.d/functions cd /etc/sysconfig/network-scripts . ./network-functions [ -f ../network ] && . ../network CONFIG=${1} TIMEOUT=10 source_config . /etc/sysconfig/network OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-${REAL_DEVICETYPE}" if [ ! -x ${OTHERSCRIPT} ]; then OTHERSCRIPT="/etc/sysconfig/network-scripts/ifdown-eth" fi SERVICE_UNIT=/usr/lib/systemd/system/ovsdb-server.service if [ -f $SERVICE_UNIT ] && [ -x /usr/bin/systemctl ]; then if ! systemctl --quiet is-active ovsdb-server.service; then systemctl start ovsdb-server.service fi else if [ ! -f /var/lock/subsys/openvswitch ]; then /sbin/service openvswitch start fi fi case "$TYPE" in OVSBridge|OVSUserBridge) ${OTHERSCRIPT} ${CONFIG} $2 retval=$? ovs-vsctl -t ${TIMEOUT} -- --if-exists del-br "$DEVICE" ;; OVSPort|OVSIntPort|OVSBond) ${OTHERSCRIPT} ${CONFIG} $2 retval=$? ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; OVSPatchPort|OVSTunnel) ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; OVSDPDKPort|OVSDPDKRPort|OVSDPDKVhostPort|OVSDPDKVhostUserPort|OVSDPDKBond) ovs-vsctl -t ${TIMEOUT} -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" ;; *) echo $"Invalid OVS interface type $TYPE" exit 1 ;; esac exit $retval openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_ovn-northd.service0000644000000000000000000000013113534540071025150 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.209849209 openvswitch-2.5.9/rhel/usr_lib_systemd_system_ovn-northd.service0000644000175000017500000000054613534540071026644 0ustar00jpettitjpettit00000000000000[Unit] Description=OVN northd management daemon After=syslog.target Requires=openvswitch.service After=openvswitch.service [Service] Type=oneshot RemainAfterExit=yes Environment=OVS_RUNDIR=%t/openvswitch OVS_DBDIR=/var/lib/openvswitch ExecStart=/usr/share/openvswitch/scripts/ovn-ctl start_northd ExecStop=/usr/share/openvswitch/scripts/ovn-ctl stop_northd openvswitch-2.5.9/rhel/PaxHeaders.82075/etc_logrotate.d_openvswitch0000644000000000000000000000013113534540071022226 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.185849033 openvswitch-2.5.9/rhel/etc_logrotate.d_openvswitch0000644000175000017500000000111113534540071023707 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. /var/log/openvswitch/*.log { daily compress sharedscripts missingok postrotate # Tell Open vSwitch daemons to reopen their log files for pidfile in `cd /var/run/openvswitch && echo *.pid`; do ovs-appctl -t "${pidfile%%.pid}" vlog/reopen done endscript } openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-kmod-fedora.spec.in0000644000000000000000000000013113534540071022615 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.197849121 openvswitch-2.5.9/rhel/openvswitch-kmod-fedora.spec.in0000644000175000017500000000354713534540071024315 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2015 Nicira Networks, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. #%define kernel 3.1.5-1.fc16.x86_64 #define kernel %{kernel_source} %{?kversion:%define kernel %kversion} Name: openvswitch-kmod Summary: Open vSwitch Kernel Modules Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: OpenSource Security Ralf Spenneberg Version: @VERSION@ # The entire source code is ASL 2.0 except datapath/ which is GPLv2 License: GPLv2 Release: 1%{?dist} Source: openvswitch-%{version}.tar.gz #Source1: openvswitch-init Buildroot: /tmp/openvswitch-xen-rpm %description Open vSwitch provides standard network bridging functions augmented with support for the OpenFlow protocol for remote per-flow control of traffic. This package contains the kernel modules. %prep %setup -q -n openvswitch-%{version} %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{kernel}/build --enable-ssl make %{_smp_mflags} -C datapath/linux %install rm -rf $RPM_BUILD_ROOT make -C datapath/linux modules_install install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{kernel}/kernel/extra/openvswitch find datapath/linux -name *.ko -exec install -m 755 \{\} $RPM_BUILD_ROOT/lib/modules/%{kernel}/kernel/extra/openvswitch \; %clean rm -rf $RPM_BUILD_ROOT %preun %post # Ensure that modprobe will find our modules. depmod %{kernel} %files %defattr(-,root,root) /lib/modules/%{kernel}/kernel/extra/openvswitch/*.ko %changelog * Wed Sep 21 2011 Kyle Mestery - Updated for F15 * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-dkms.spec0000644000000000000000000000013213534540117020760 xustar0030 mtime=1567801423.397843223 30 atime=1567801423.397843223 30 ctime=1567801424.189849061 openvswitch-2.5.9/rhel/openvswitch-dkms.spec0000644000175000017500000000530613534540117022452 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch kernel modules using DKMS. # # Copyright (C) 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. %define oname openvswitch Name: %{oname}-dkms Version: 2.5.9 Release: 1%{?dist} Summary: Open vSwitch kernel module Group: System/Kernel License: GPLv2 URL: http://openvswitch.org/ Source: %{oname}-%{version}.tar.gz Requires: autoconf, gcc, make Requires(post): dkms Requires(preun): dkms BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) # Without this we get an empty openvswitch-debuginfo package (whose name # conflicts with the openvswitch-debuginfo package for OVS userspace). %undefine _enable_debug_packages %description Open vSwitch Linux kernel module. %prep %setup -n %{oname}-%{version} cat > %{oname}.conf << EOF override %{oname} * extra/%{oname} override %{oname} * weak-updates/%{oname} EOF %build # for running the '%{__make} -C datapath print-build-modules' below. ./configure %install %{__rm} -rf %{buildroot} # Kernel module sources install for dkms %{__mkdir_p} %{buildroot}%{_usrsrc}/%{oname}-%{version}/ %{__cp} -r * %{buildroot}%{_usrsrc}/%{oname}-%{version}/ # check we can get kernel module names %{__make} -C datapath print-build-modules # Prepare dkms.conf cat > %{buildroot}%{_usrsrc}/%{oname}-%{version}/dkms.conf << EOF MODULES=( `%{__make} -C datapath print-build-modules | grep -v make` ) PACKAGE_NAME="openvswitch" PACKAGE_VERSION="%{version}-%{release}" MAKE="./configure --with-linux='\${kernel_source_dir}' && make -C datapath/linux" for __idx in \${!MODULES[@]}; do BUILT_MODULE_NAME[__idx]=\${MODULES[__idx]} BUILT_MODULE_LOCATION[__idx]=datapath/linux/ DEST_MODULE_LOCATION[__idx]=/kernel/drivers/net/openvswitch/ done AUTOINSTALL=yes EOF install -d %{buildroot}%{_sysconfdir}/depmod.d/ install -m 644 %{oname}.conf %{buildroot}%{_sysconfdir}/depmod.d/ %post # Add to DKMS registry isadded=`dkms status -m "%{oname}" -v "%{version}"` if [ "x${isadded}" = "x" ] ; then dkms add -m "%{oname}" -v "%{version}" || : fi dkms build -m "%{oname}" -v "%{version}" || : dkms install -m "%{oname}" -v "%{version}" --force || : %preun # Remove all versions from DKMS registry dkms remove -m "%{oname}" -v "%{version}" --all || : %clean %{__rm} -rf %{buildroot} %files %defattr(-,root,root) %{_usrsrc}/%{oname}-%{version}/ /etc/depmod.d/openvswitch.conf openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_ovn-controller-vtep.service0000644000000000000000000000013113534540071027011 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.209849209 openvswitch-2.5.9/rhel/usr_lib_systemd_system_ovn-controller-vtep.service0000644000175000017500000000217113534540071030501 0ustar00jpettitjpettit00000000000000# # You may override the following variables to customize ovn-controller-vtep # behavior: # # OVN_DB - Set this variable to the location of the ovsdb server that is # serving the OVN_Southbound database. See the manpage for # ovn-controller-vtep for more details on the format for the db # location. # # VTEP_DB - Set this variable to the location of the ovsdb server that is # serving the hardware_vtep database. See the manpage for # ovn-controller-vtep for more details on the format for the db # location. # [Unit] Description=OVN VTEP gateway controller daemon After=syslog.target Requires=openvswitch.service After=openvswitch.service [Service] Type=simple Environment=OVS_RUNDIR=%t/openvswitch Environment=OVN_DB=unix:%t/openvswitch/db.sock Environment=VTEP_DB=unix:%t/openvswitch/db.sock ExecStart=/usr/bin/ovn-controller-vtep -vconsole:emer -vsyslog:err -vfile:info \ --log-file=/var/log/openvswitch/ovn-controller-vtep.log \ --no-chdir --pidfile=${OVS_RUNDIR}/ovn-controller-vtep.pid \ --ovnsb-db=${OVN_DB} --vtep-db=${VTEP_DB} openvswitch-2.5.9/rhel/PaxHeaders.82075/automake.mk0000644000000000000000000000013213534540071016735 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.185849033 openvswitch-2.5.9/rhel/automake.mk0000644000175000017500000000417413534540071020431 0ustar00jpettitjpettit00000000000000# Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. EXTRA_DIST += \ rhel/README.RHEL \ rhel/automake.mk \ rhel/etc_init.d_openvswitch \ rhel/etc_logrotate.d_openvswitch \ rhel/etc_sysconfig_network-scripts_ifdown-ovs \ rhel/etc_sysconfig_network-scripts_ifup-ovs \ rhel/openvswitch-dkms.spec \ rhel/openvswitch-dkms.spec.in \ rhel/openvswitch-kmod-rhel6.spec \ rhel/openvswitch-kmod-rhel6.spec.in \ rhel/openvswitch-kmod.files \ rhel/openvswitch-kmod-fedora.spec \ rhel/openvswitch-kmod-fedora.spec.in \ rhel/openvswitch.spec \ rhel/openvswitch.spec.in \ rhel/openvswitch-fedora.spec \ rhel/openvswitch-fedora.spec.in \ rhel/usr_share_openvswitch_scripts_sysconfig.template \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ rhel/usr_lib_systemd_system_openvswitch.service \ rhel/usr_lib_systemd_system_ovsdb-server.service \ rhel/usr_lib_systemd_system_ovs-vswitchd.service \ rhel/usr_lib_systemd_system_ovn-controller.service \ rhel/usr_lib_systemd_system_ovn-controller-vtep.service \ rhel/usr_lib_systemd_system_ovn-northd.service update_rhel_spec = \ $(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \ < $(srcdir)/rhel/$(@F).in > $(@F).tmp || exit 1; \ if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi $(srcdir)/rhel/openvswitch-dkms.spec: rhel/openvswitch-dkms.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-kmod-rhel6.spec: rhel/openvswitch-kmod-rhel6.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-kmod-fedora.spec: rhel/openvswitch-kmod-fedora.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch.spec: rhel/openvswitch.spec.in $(top_builddir)/config.status $(update_rhel_spec) $(srcdir)/rhel/openvswitch-fedora.spec: rhel/openvswitch-fedora.spec.in $(top_builddir)/config.status $(update_rhel_spec) openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_ovsdb-server.service0000644000000000000000000000013013534540071025472 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20584918 openvswitch-2.5.9/rhel/usr_lib_systemd_system_ovsdb-server.service0000644000175000017500000000117213534540071027163 0ustar00jpettitjpettit00000000000000[Unit] Description=Open vSwitch Database Unit After=syslog.target network-pre.target Before=network.target network.service ReloadPropagatedFrom=openvswitch.service PartOf=openvswitch.service [Service] Type=forking EnvironmentFile=-/etc/sysconfig/openvswitch ExecStart=/usr/share/openvswitch/scripts/ovs-ctl \ --no-ovs-vswitchd --no-monitor --system-id=random \ start $OPTIONS ExecStop=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd stop ExecReload=/usr/share/openvswitch/scripts/ovs-ctl --no-ovs-vswitchd \ --no-monitor restart $OPTIONS RuntimeDirectory=openvswitch RuntimeDirectoryMode=0755 openvswitch-2.5.9/rhel/PaxHeaders.82075/etc_sysconfig_network-scripts_ifup-ovs0000644000000000000000000000013113534540071024445 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.189849061 openvswitch-2.5.9/rhel/etc_sysconfig_network-scripts_ifup-ovs0000755000175000017500000001334413534540071026144 0ustar00jpettitjpettit00000000000000#!/bin/bash # Copyright (c) 2011 Alexey I. Froloff. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. . /etc/init.d/functions cd /etc/sysconfig/network-scripts . ./network-functions [ -f ../network ] && . ../network CONFIG=${1} TIMEOUT=10 need_config ${CONFIG} source_config OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-${REAL_DEVICETYPE}" if [ ! -x ${OTHERSCRIPT} ]; then OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth" fi check_recursion () { [ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do [ "$_r" = "$1" ] && return 1 done return 0 } ifup_ovs_bridge () { if ovs-vsctl br-exists "${OVS_BRIDGE}"; then :; else /sbin/ifup "${OVS_BRIDGE}" fi } if [ -z "${UPPEDSTACK}" ]; then UPPEDSTACK="${DEVICE}" fi [ -n "${OVSREQUIRES}" ] && for _i in ${OVSREQUIRES}; do if ( check_recursion "$_i" ); then UPPEDSTACK="${UPPEDSTACK} $_i" /sbin/ifup "$_i" fi done SERVICE_UNIT=/usr/lib/systemd/system/openvswitch.service if [ -f $SERVICE_UNIT ] && [ -x /usr/bin/systemctl ]; then if ! systemctl --quiet is-active openvswitch.service; then systemctl start openvswitch.service fi else if [ ! -f /var/lock/subsys/openvswitch ]; then /sbin/service openvswitch start fi fi case "$TYPE" in OVSBridge|OVSUserBridge) # If bridge already exists and is up, it has been configured through # other cases like OVSPort, OVSIntPort and OVSBond. If it is down or # it does not exist, create it. It is possible for a bridge to exist # because it remained in the OVSDB for some reason, but it won't be up. if [ "${TYPE}" = "OVSUserBridge" ]; then DATAPATH="netdev" fi if check_device_down "${DEVICE}"; then ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS \ ${OVS_EXTRA+-- $OVS_EXTRA} \ ${STP+-- set bridge "$DEVICE" stp_enable="${STP}"} \ ${DATAPATH+-- set bridge "$DEVICE" datapath_type="$DATAPATH"} else OVSBRIDGECONFIGURED="yes" fi # If MACADDR is provided in the interface configuration file, # we need to set it using ovs-vsctl; setting it with the "ip" # command in ifup-eth does not make the change persistent. if [ -n "$MACADDR" ]; then ovs-vsctl -t ${TIMEOUT} -- set bridge "$DEVICE" \ other-config:hwaddr="$MACADDR" fi # When dhcp is enabled, the assumption is that there will be a port to # attach (otherwise, we can't reach out for dhcp). So, we do not # configure the bridge through rhel's ifup infrastructure unless # it is being configured after the port has been configured. # The "OVSINTF" is set only after the port is configured. if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then case " ${OVSDHCPINTERFACES} " in *" ${OVSINTF} "*) BOOTPROTO=dhcp ${OTHERSCRIPT} ${CONFIG} ;; esac fi # When dhcp is not enabled, it is possible that someone may want # a standalone bridge (i.e it may not have any ports). Configure it. if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ] && \ [ "${OVSBRIDGECONFIGURED}" != "yes" ]; then ${OTHERSCRIPT} ${CONFIG} fi exit 0 ;; OVSPort) ifup_ovs_bridge ${OTHERSCRIPT} ${CONFIG} ${2} # The port might be already in the database but not yet # in the datapath. So, remove the stale interface first. ovs-vsctl -t ${TIMEOUT} \ -- --if-exists del-port "$OVS_BRIDGE" "$DEVICE" \ -- add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE" ;; OVSIntPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA} if [ -n "${OVSDHCPINTERFACES}" ]; then for _iface in ${OVSDHCPINTERFACES}; do /sbin/ifup ${_iface} done fi BOOTPROTO="${OVSBOOTPROTO}" ${OTHERSCRIPT} ${CONFIG} ${2} ;; OVSBond) ifup_ovs_bridge for _iface in $BOND_IFACES; do /sbin/ifup ${_iface} done ovs-vsctl -t ${TIMEOUT} -- --may-exist add-bond "$OVS_BRIDGE" "$DEVICE" ${BOND_IFACES} $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE" ;; OVSTunnel) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=$OVS_TUNNEL_TYPE $OVS_TUNNEL_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSPatchPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=patch options:peer="${OVS_PATCH_PEER}" ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDKPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=dpdk ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDKRPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=dpdkr ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDVhostPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=dpdkvhost ${OVS_EXTRA+-- $OVS_EXTRA} ;; OVSDPDKVhostUserPort) ifup_ovs_bridge ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=dpdkvhostuser ${OVS_EXTRA+-- $OVS_EXTRA} ;; *) echo $"Invalid OVS interface type $TYPE" exit 1 ;; esac openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch.spec0000644000000000000000000000012613534540117020027 xustar0028 mtime=1567801423.4218434 28 atime=1567801423.4218434 30 ctime=1567801424.197849121 openvswitch-2.5.9/rhel/openvswitch.spec0000644000175000017500000001302513534540117021513 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch on Red Hat Enterprise Linux. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch.spec Name: openvswitch Summary: Open vSwitch daemon/database/utilities Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: Nicira, Inc. Version: 2.5.9 License: ASL 2.0 Release: 1 Source: openvswitch-%{version}.tar.gz Buildroot: /tmp/openvswitch-rpm Requires: logrotate, python >= 2.7 BuildRequires: openssl-devel %bcond_without check %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %prep %setup -q %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --enable-ssl make %{_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT rhel_cp() { base=$1 mode=$2 dst=$RPM_BUILD_ROOT/$(echo $base | sed 's,_,/,g') install -D -m $mode rhel/$base $dst } rhel_cp etc_init.d_openvswitch 0755 rhel_cp etc_logrotate.d_openvswitch 0644 rhel_cp etc_sysconfig_network-scripts_ifup-ovs 0755 rhel_cp etc_sysconfig_network-scripts_ifdown-ovs 0755 rhel_cp usr_share_openvswitch_scripts_sysconfig.template 0644 # Get rid of stuff we don't want to make RPM happy. rm \ $RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \ $RPM_BUILD_ROOT/usr/bin/ovs-test \ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \ $RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8 \ $RPM_BUILD_ROOT/usr/bin/ovn-* \ $RPM_BUILD_ROOT/usr/share/man/man?/ovn-* \ $RPM_BUILD_ROOT/usr/share/openvswitch/ovn-* \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/ovn-* (cd "$RPM_BUILD_ROOT" && rm -rf usr/lib) (cd "$RPM_BUILD_ROOT" && rm -rf usr/include) install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %post # Create default or update existing /etc/sysconfig/openvswitch. SYSCONFIG=/etc/sysconfig/openvswitch TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template if [ ! -e $SYSCONFIG ]; then cp $TEMPLATE $SYSCONFIG else for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE) do if ! grep $var $SYSCONFIG >/dev/null 2>&1; then echo >> $SYSCONFIG sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG fi done fi # Ensure all required services are set to run /sbin/chkconfig --add openvswitch /sbin/chkconfig openvswitch on %preun if [ "$1" = "0" ]; then # $1 = 0 for uninstall /sbin/service openvswitch stop /sbin/chkconfig --del openvswitch fi %postun if [ "$1" = "0" ]; then # $1 = 0 for uninstall rm -f /etc/openvswitch/conf.db rm -f /etc/sysconfig/openvswitch rm -f /etc/openvswitch/vswitchd.cacert fi exit 0 %files %defattr(-,root,root) %dir /etc/openvswitch /etc/bash_completion.d/ovs-appctl-bashcomp.bash /etc/bash_completion.d/ovs-vsctl-bashcomp.bash /etc/init.d/openvswitch %config(noreplace) /etc/logrotate.d/openvswitch /etc/sysconfig/network-scripts/ifup-ovs /etc/sysconfig/network-scripts/ifdown-ovs /usr/bin/ovs-appctl /usr/bin/ovs-benchmark /usr/bin/ovs-dpctl /usr/bin/ovs-dpctl-top /usr/bin/ovs-docker /usr/bin/ovs-ofctl /usr/bin/ovs-parse-backtrace /usr/bin/ovs-pcap /usr/bin/ovs-pki /usr/bin/ovs-tcpundump /usr/bin/ovs-vlan-test /usr/bin/ovs-vsctl /usr/bin/ovsdb-client /usr/bin/ovsdb-tool /usr/bin/vtep-ctl /usr/sbin/ovs-bugtool /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server /usr/share/man/man1/ovs-benchmark.1.gz /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man1/ovsdb-client.1.gz /usr/share/man/man1/ovsdb-server.1.gz /usr/share/man/man1/ovsdb-tool.1.gz /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz /usr/share/man/man5/vtep.5.gz /usr/share/man/man8/ovs-appctl.8.gz /usr/share/man/man8/ovs-bugtool.8.gz /usr/share/man/man8/ovs-ctl.8.gz /usr/share/man/man8/ovs-dpctl.8.gz /usr/share/man/man8/ovs-dpctl-top.8.gz /usr/share/man/man8/ovs-ofctl.8.gz /usr/share/man/man8/ovs-parse-backtrace.8.gz /usr/share/man/man8/ovs-pki.8.gz /usr/share/man/man8/ovs-vlan-test.8.gz /usr/share/man/man8/ovs-vsctl.8.gz /usr/share/man/man8/ovs-vswitchd.8.gz /usr/share/man/man8/vtep-ctl.8.gz /usr/share/openvswitch/bugtool-plugins/ /usr/share/openvswitch/python/ /usr/share/openvswitch/scripts/ovs-bugtool-* /usr/share/openvswitch/scripts/ovs-check-dead-ifs /usr/share/openvswitch/scripts/ovs-ctl /usr/share/openvswitch/scripts/ovs-lib /usr/share/openvswitch/scripts/ovs-save /usr/share/openvswitch/scripts/ovs-vtep /usr/share/openvswitch/scripts/sysconfig.template /usr/share/openvswitch/vswitch.ovsschema /usr/share/openvswitch/vtep.ovsschema %doc COPYING DESIGN.md INSTALL.SSL.md NOTICE README.md WHY-OVS.md FAQ.md NEWS %doc INSTALL.DPDK.md rhel/README.RHEL README-native-tunneling.md /var/lib/openvswitch /var/log/openvswitch openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_openvswitch.service0000644000000000000000000000013013534540071025422 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20584918 openvswitch-2.5.9/rhel/usr_lib_systemd_system_openvswitch.service0000644000175000017500000000047513534540071027120 0ustar00jpettitjpettit00000000000000[Unit] Description=Open vSwitch Before=network.target network.service After=network-pre.target PartOf=network.target BindsTo=ovsdb-server.service BindsTo=ovs-vswitchd.service [Service] Type=oneshot ExecStart=/bin/true ExecReload=/bin/true ExecStop=/bin/true RemainAfterExit=yes [Install] WantedBy=multi-user.target openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-dkms.spec.in0000644000000000000000000000013113534540071021363 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.189849061 openvswitch-2.5.9/rhel/openvswitch-dkms.spec.in0000644000175000017500000000517713534540071023064 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch kernel modules using DKMS. # # Copyright (C) 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. %define oname openvswitch Name: %{oname}-dkms Version: @VERSION@ Release: 1%{?dist} Summary: Open vSwitch kernel module Group: System/Kernel License: GPLv2 URL: http://openvswitch.org/ Source: %{oname}-%{version}.tar.gz Requires: autoconf, gcc, make Requires(post): dkms Requires(preun): dkms BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) # Without this we get an empty openvswitch-debuginfo package (whose name # conflicts with the openvswitch-debuginfo package for OVS userspace). %undefine _enable_debug_packages %description Open vSwitch Linux kernel module. %prep %setup -n %{oname}-%{version} cat > %{oname}.conf << EOF override %{oname} * extra/%{oname} override %{oname} * weak-updates/%{oname} EOF %build # for running the '%{__make} -C datapath print-build-modules' below. ./configure %install %{__rm} -rf %{buildroot} # Kernel module sources install for dkms %{__mkdir_p} %{buildroot}%{_usrsrc}/%{oname}-%{version}/ %{__cp} -r * %{buildroot}%{_usrsrc}/%{oname}-%{version}/ # check we can get kernel module names %{__make} -C datapath print-build-modules # Prepare dkms.conf cat > %{buildroot}%{_usrsrc}/%{oname}-%{version}/dkms.conf << EOF MODULES=( `%{__make} -C datapath print-build-modules | grep -v make` ) PACKAGE_NAME="openvswitch" PACKAGE_VERSION="%{version}-%{release}" MAKE="./configure --with-linux='\${kernel_source_dir}' && make -C datapath/linux" for __idx in \${!MODULES[@]}; do BUILT_MODULE_NAME[__idx]=\${MODULES[__idx]} BUILT_MODULE_LOCATION[__idx]=datapath/linux/ DEST_MODULE_LOCATION[__idx]=/kernel/drivers/net/openvswitch/ done AUTOINSTALL=yes EOF install -d %{buildroot}%{_sysconfdir}/depmod.d/ install -m 644 %{oname}.conf %{buildroot}%{_sysconfdir}/depmod.d/ %post # Add to DKMS registry isadded=`dkms status -m "%{oname}" -v "%{version}"` if [ "x${isadded}" = "x" ] ; then dkms add -m "%{oname}" -v "%{version}" || : fi dkms build -m "%{oname}" -v "%{version}" || : dkms install -m "%{oname}" -v "%{version}" --force || : %preun # Remove all versions from DKMS registry dkms remove -m "%{oname}" -v "%{version}" --all || : %clean %{__rm} -rf %{buildroot} %files %defattr(-,root,root) %{_usrsrc}/%{oname}-%{version}/ /etc/depmod.d/openvswitch.conf openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-kmod.files0000644000000000000000000000013113534540071021122 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.193849091 openvswitch-2.5.9/rhel/openvswitch-kmod.files0000644000175000017500000000011613534540071022607 0ustar00jpettitjpettit00000000000000%defattr(644,root,root,755) /lib/modules/%2-%1 /etc/depmod.d/openvswitch.conf openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-fedora.spec0000644000000000000000000000013213534540117021262 xustar0030 mtime=1567801423.433843488 30 atime=1567801423.429843459 30 ctime=1567801424.197849121 openvswitch-2.5.9/rhel/openvswitch-fedora.spec0000644000175000017500000003043013534540117022750 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2013, 2014, 2015 Nicira Networks, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch-fedora.spec #%define kernel 2.6.40.4-5.fc15.x86_64 # If libcap-ng isn't available and there is no need for running OVS # as regular user, specify the '--without libcapng' %bcond_without libcapng # To enable DPDK support, specify '--with dpdk' when building %bcond_with dpdk # Enable PIE, bz#955181 %global _hardened_build 1 # some distros (e.g: RHEL-7) don't define _rundir macro yet # Fedora 15 onwards uses /run as _rundir %if 0%{!?_rundir:1} %define _rundir /run %endif Name: openvswitch Summary: Open vSwitch Group: System Environment/Daemons URL: http://www.openvswitch.org/ Version: 2.5.9 # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL # datapath/ is GPLv2 (although not built into any of the binary packages) License: ASL 2.0 and LGPLv2+ and SISSL Release: 1%{?dist} Source: http://openvswitch.org/releases/%{name}-%{version}.tar.gz BuildRequires: autoconf automake libtool BuildRequires: systemd-units openssl openssl-devel BuildRequires: python python-twisted-core python-zope-interface PyQt4 BuildRequires: desktop-file-utils BuildRequires: groff graphviz BuildRequires: checkpolicy, selinux-policy-devel # make check dependencies BuildRequires: procps-ng %if %{with libcapng} BuildRequires: libcap-ng libcap-ng-devel %endif %if %{with dpdk} BuildRequires: dpdk-devel >= 2.2.0 Provides: %{name}-dpdk = %{version}-%{release} %endif Requires: openssl iproute module-init-tools #Upstream kernel commit 4f647e0a3c37b8d5086214128614a136064110c3 #Requires: kernel >= 3.15.0-0 Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units Obsoletes: openvswitch-controller <= 0:2.1.0-1 %bcond_without check %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %package selinux-policy Summary: Open vSwitch SELinux policy License: ASL 2.0 BuildArch: noarch Requires: selinux-policy-targeted %description selinux-policy Tailored Open vSwitch SELinux policy %package -n python-openvswitch Summary: Open vSwitch python bindings License: ASL 2.0 BuildArch: noarch Requires: python %description -n python-openvswitch Python bindings for the Open vSwitch database %package test Summary: Open vSwitch testing utilities License: ASL 2.0 BuildArch: noarch Requires: python-openvswitch = %{version}-%{release} Requires: python python-twisted-core python-twisted-web %description test Utilities that are useful to diagnose performance and connectivity issues in Open vSwitch setup. %package devel Summary: Open vSwitch OpenFlow development package (library, headers) License: ASL 2.0 Provides: openvswitch-static = %{version}-%{release} %description devel This provides static library, libopenswitch.a and the openvswitch header files needed to build an external application. %package ovn Summary: Open vSwitch - Open Virtual Network support License: ASL 2.0 Requires: openvswitch %description ovn OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. %prep %setup -q %build %configure \ %if %{with libcapng} --enable-libcapng \ %else --disable-libcapng \ %endif %if %{with dpdk} --with-dpdk=$(dirname %{_datadir}/dpdk/*/.config) \ %endif --enable-ssl \ --with-pkidir=%{_sharedstatedir}/openvswitch/pki make %{?_smp_mflags} cd selinux make -f %{_datadir}/selinux/devel/Makefile %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT install -d -m 0755 $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch install -p -D -m 0644 \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/openvswitch for service in openvswitch ovsdb-server ovs-vswitchd \ ovn-controller ovn-controller-vtep ovn-northd; do install -p -D -m 0644 \ rhel/usr_lib_systemd_system_${service}.service \ $RPM_BUILD_ROOT%{_unitdir}/${service}.service done install -m 0755 rhel/etc_init.d_openvswitch \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/openvswitch.init install -p -D -m 0644 rhel/etc_logrotate.d_openvswitch \ $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/openvswitch install -m 0644 vswitchd/vswitch.ovsschema \ $RPM_BUILD_ROOT/%{_datadir}/openvswitch/vswitch.ovsschema install -d -m 0755 $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifdown-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifup-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifup-ovs install -d -m 0755 $RPM_BUILD_ROOT%{python_sitelib} mv $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/* \ $RPM_BUILD_ROOT%{python_sitelib} rmdir $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/ install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch touch $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch/conf.db touch $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch/system-id.conf install -p -m 644 -D selinux/openvswitch-custom.pp \ $RPM_BUILD_ROOT%{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp # remove unpackaged files rm -f $RPM_BUILD_ROOT%{_bindir}/ovs-benchmark \ $RPM_BUILD_ROOT%{_bindir}/ovs-parse-backtrace \ $RPM_BUILD_ROOT%{_bindir}/ovs-pcap \ $RPM_BUILD_ROOT%{_bindir}/ovs-tcpundump \ $RPM_BUILD_ROOT%{_sbindir}/ovs-vlan-bug-workaround \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-benchmark.1 \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-pcap.1 \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-tcpundump.1 \ $RPM_BUILD_ROOT%{_mandir}/man8/ovs-vlan-bug-workaround.8 \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs-save %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %preun %if 0%{?systemd_preun:1} %systemd_preun %{name}.service %else if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable %{name}.service >/dev/null 2>&1 || : /bin/systemctl stop %{name}.service >/dev/null 2>&1 || : fi %endif %preun ovn %if 0%{?systemd_preun:1} %systemd_preun ovn-controller.service %systemd_preun ovn-controller-vtep.service %systemd_preun ovn-northd.service %else if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || : fi %endif %post %if 0%{?systemd_post:1} %systemd_post %{name}.service %else # Package install, not upgrade if [ $1 -eq 1 ]; then /bin/systemctl daemon-reload >dev/null || : fi %endif %post ovn %if 0%{?systemd_post:1} %systemd_post ovn-controller.service %systemd_post ovn-controller-vtep.service %systemd_post ovn-northd.service %else # Package install, not upgrade if [ $1 -eq 1 ]; then /bin/systemctl daemon-reload >dev/null || : fi %endif %post selinux-policy /usr/sbin/semodule -i %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp &> /dev/null || : %postun %if 0%{?systemd_postun:1} %systemd_postun %{name}.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif %postun ovn %if 0%{?systemd_postun_with_restart:1} %systemd_postun_with_restart ovn-controller.service %systemd_postun_with_restart ovn-controller-vtep.service %systemd_postun_with_restart ovn-northd.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ "$1" -ge "1" ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || : fi %endif %postun selinux-policy if [ $1 -eq 0 ] ; then /usr/sbin/semodule -r openvswitch-custom &> /dev/null || : fi %files selinux-policy %defattr(-,root,root) %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp %files -n python-openvswitch %{python_sitelib}/ovs %doc COPYING %files test %{_bindir}/ovs-test %{_bindir}/ovs-vlan-test %{_bindir}/ovs-l3ping %{_mandir}/man8/ovs-test.8* %{_mandir}/man8/ovs-vlan-test.8* %{_mandir}/man8/ovs-l3ping.8* %{python_sitelib}/ovstest %files devel %{_libdir}/*.a %{_libdir}/*.la %{_libdir}/pkgconfig/*.pc %{_includedir}/openvswitch/* %{_includedir}/openflow/* %files %defattr(-,root,root) %{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash %{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash %dir %{_sysconfdir}/openvswitch %config %ghost %{_sysconfdir}/openvswitch/conf.db %config %ghost %{_sysconfdir}/openvswitch/system-id.conf %config(noreplace) %{_sysconfdir}/sysconfig/openvswitch %config(noreplace) %{_sysconfdir}/logrotate.d/openvswitch %{_unitdir}/openvswitch.service %{_unitdir}/ovsdb-server.service %{_unitdir}/ovs-vswitchd.service %{_datadir}/openvswitch/scripts/openvswitch.init %{_sysconfdir}/sysconfig/network-scripts/ifup-ovs %{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs %{_datadir}/openvswitch/bugtool-plugins/ %{_datadir}/openvswitch/scripts/ovs-bugtool-* %{_datadir}/openvswitch/scripts/ovs-check-dead-ifs %{_datadir}/openvswitch/scripts/ovs-lib %{_datadir}/openvswitch/scripts/ovs-vtep %{_datadir}/openvswitch/scripts/ovs-ctl %config %{_datadir}/openvswitch/vswitch.ovsschema %config %{_datadir}/openvswitch/vtep.ovsschema %{_bindir}/ovs-appctl %{_bindir}/ovs-docker %{_bindir}/ovs-dpctl %{_bindir}/ovs-dpctl-top %{_bindir}/ovs-ofctl %{_bindir}/ovs-vsctl %{_bindir}/ovsdb-client %{_bindir}/ovsdb-tool %{_bindir}/ovs-testcontroller %{_bindir}/ovs-pki %{_bindir}/vtep-ctl %{_sbindir}/ovs-bugtool %{_sbindir}/ovs-vswitchd %{_sbindir}/ovsdb-server %{_mandir}/man1/ovsdb-client.1* %{_mandir}/man1/ovsdb-server.1* %{_mandir}/man1/ovsdb-tool.1* %{_mandir}/man5/ovs-vswitchd.conf.db.5* %{_mandir}/man5/vtep.5* %{_mandir}/man8/vtep-ctl.8* %{_mandir}/man8/ovs-appctl.8* %{_mandir}/man8/ovs-bugtool.8* %{_mandir}/man8/ovs-ctl.8* %{_mandir}/man8/ovs-dpctl.8* %{_mandir}/man8/ovs-dpctl-top.8* %{_mandir}/man8/ovs-ofctl.8* %{_mandir}/man8/ovs-pki.8* %{_mandir}/man8/ovs-vsctl.8* %{_mandir}/man8/ovs-vswitchd.8* %{_mandir}/man8/ovs-parse-backtrace.8* %{_mandir}/man8/ovs-testcontroller.8* %doc COPYING DESIGN.md INSTALL.SSL.md NOTICE README.md WHY-OVS.md %doc FAQ.md NEWS INSTALL.DPDK.md rhel/README.RHEL /var/lib/openvswitch /var/log/openvswitch %ghost %attr(755,root,root) %{_rundir}/openvswitch %files ovn %{_bindir}/ovn-controller %{_bindir}/ovn-controller-vtep %{_bindir}/ovn-docker-overlay-driver %{_bindir}/ovn-docker-underlay-driver %{_bindir}/ovn-nbctl %{_bindir}/ovn-northd %{_bindir}/ovn-sbctl %{_datadir}/openvswitch/scripts/ovn-ctl %{_mandir}/man8/ovs-testcontroller.8* %{_mandir}/man5/ovn-nb.5* %{_mandir}/man5/ovn-sb.5* %{_mandir}/man7/ovn-architecture.7* %{_mandir}/man8/ovn-controller.8* %{_mandir}/man8/ovn-controller-vtep.8* %{_mandir}/man8/ovn-ctl.8* %{_mandir}/man8/ovn-nbctl.8* %{_mandir}/man8/ovn-northd.8* %{_mandir}/man8/ovn-sbctl.8* %config %{_datadir}/openvswitch/ovn-nb.ovsschema %config %{_datadir}/openvswitch/ovn-sb.ovsschema %{_unitdir}/ovn-controller.service %{_unitdir}/ovn-controller-vtep.service %{_unitdir}/ovn-northd.service %changelog * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_lib_systemd_system_ovs-vswitchd.service0000644000000000000000000000013013534540071025511 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20584918 openvswitch-2.5.9/rhel/usr_lib_systemd_system_ovs-vswitchd.service0000644000175000017500000000127413534540071027205 0ustar00jpettitjpettit00000000000000[Unit] Description=Open vSwitch Forwarding Unit After=ovsdb-server.service network-pre.target Before=network.target network.service Requires=ovsdb-server.service ReloadPropagatedFrom=ovsdb-server.service AssertPathIsReadWrite=/var/run/openvswitch/db.sock PartOf=openvswitch.service [Service] Type=forking EnvironmentFile=-/etc/sysconfig/openvswitch ExecStart=/usr/share/openvswitch/scripts/ovs-ctl \ --no-ovsdb-server --no-monitor --system-id=random \ start $OPTIONS ExecStop=/usr/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server stop ExecReload=/usr/share/openvswitch/scripts/ovs-ctl --no-ovsdb-server \ --no-monitor --system-id=random \ restart $OPTIONS openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch.spec.in0000644000000000000000000000013113534540071020427 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.197849121 openvswitch-2.5.9/rhel/openvswitch.spec.in0000644000175000017500000001271613534540071022125 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch on Red Hat Enterprise Linux. # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch.spec Name: openvswitch Summary: Open vSwitch daemon/database/utilities Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: Nicira, Inc. Version: @VERSION@ License: ASL 2.0 Release: 1 Source: openvswitch-%{version}.tar.gz Buildroot: /tmp/openvswitch-rpm Requires: logrotate, python >= 2.7 BuildRequires: openssl-devel %bcond_without check %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %prep %setup -q %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --enable-ssl make %{_smp_mflags} %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT rhel_cp() { base=$1 mode=$2 dst=$RPM_BUILD_ROOT/$(echo $base | sed 's,_,/,g') install -D -m $mode rhel/$base $dst } rhel_cp etc_init.d_openvswitch 0755 rhel_cp etc_logrotate.d_openvswitch 0644 rhel_cp etc_sysconfig_network-scripts_ifup-ovs 0755 rhel_cp etc_sysconfig_network-scripts_ifdown-ovs 0755 rhel_cp usr_share_openvswitch_scripts_sysconfig.template 0644 # Get rid of stuff we don't want to make RPM happy. rm \ $RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \ $RPM_BUILD_ROOT/usr/bin/ovs-test \ $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \ $RPM_BUILD_ROOT/usr/sbin/ovs-vlan-bug-workaround \ $RPM_BUILD_ROOT/usr/share/man/man8/ovs-vlan-bug-workaround.8 \ $RPM_BUILD_ROOT/usr/bin/ovn-* \ $RPM_BUILD_ROOT/usr/share/man/man?/ovn-* \ $RPM_BUILD_ROOT/usr/share/openvswitch/ovn-* \ $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/ovn-* (cd "$RPM_BUILD_ROOT" && rm -rf usr/lib) (cd "$RPM_BUILD_ROOT" && rm -rf usr/include) install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %post # Create default or update existing /etc/sysconfig/openvswitch. SYSCONFIG=/etc/sysconfig/openvswitch TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template if [ ! -e $SYSCONFIG ]; then cp $TEMPLATE $SYSCONFIG else for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE) do if ! grep $var $SYSCONFIG >/dev/null 2>&1; then echo >> $SYSCONFIG sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG fi done fi # Ensure all required services are set to run /sbin/chkconfig --add openvswitch /sbin/chkconfig openvswitch on %preun if [ "$1" = "0" ]; then # $1 = 0 for uninstall /sbin/service openvswitch stop /sbin/chkconfig --del openvswitch fi %postun if [ "$1" = "0" ]; then # $1 = 0 for uninstall rm -f /etc/openvswitch/conf.db rm -f /etc/sysconfig/openvswitch rm -f /etc/openvswitch/vswitchd.cacert fi exit 0 %files %defattr(-,root,root) %dir /etc/openvswitch /etc/bash_completion.d/ovs-appctl-bashcomp.bash /etc/bash_completion.d/ovs-vsctl-bashcomp.bash /etc/init.d/openvswitch %config(noreplace) /etc/logrotate.d/openvswitch /etc/sysconfig/network-scripts/ifup-ovs /etc/sysconfig/network-scripts/ifdown-ovs /usr/bin/ovs-appctl /usr/bin/ovs-benchmark /usr/bin/ovs-dpctl /usr/bin/ovs-dpctl-top /usr/bin/ovs-docker /usr/bin/ovs-ofctl /usr/bin/ovs-parse-backtrace /usr/bin/ovs-pcap /usr/bin/ovs-pki /usr/bin/ovs-tcpundump /usr/bin/ovs-vlan-test /usr/bin/ovs-vsctl /usr/bin/ovsdb-client /usr/bin/ovsdb-tool /usr/bin/vtep-ctl /usr/sbin/ovs-bugtool /usr/sbin/ovs-vswitchd /usr/sbin/ovsdb-server /usr/share/man/man1/ovs-benchmark.1.gz /usr/share/man/man1/ovs-pcap.1.gz /usr/share/man/man1/ovs-tcpundump.1.gz /usr/share/man/man1/ovsdb-client.1.gz /usr/share/man/man1/ovsdb-server.1.gz /usr/share/man/man1/ovsdb-tool.1.gz /usr/share/man/man5/ovs-vswitchd.conf.db.5.gz /usr/share/man/man5/vtep.5.gz /usr/share/man/man8/ovs-appctl.8.gz /usr/share/man/man8/ovs-bugtool.8.gz /usr/share/man/man8/ovs-ctl.8.gz /usr/share/man/man8/ovs-dpctl.8.gz /usr/share/man/man8/ovs-dpctl-top.8.gz /usr/share/man/man8/ovs-ofctl.8.gz /usr/share/man/man8/ovs-parse-backtrace.8.gz /usr/share/man/man8/ovs-pki.8.gz /usr/share/man/man8/ovs-vlan-test.8.gz /usr/share/man/man8/ovs-vsctl.8.gz /usr/share/man/man8/ovs-vswitchd.8.gz /usr/share/man/man8/vtep-ctl.8.gz /usr/share/openvswitch/bugtool-plugins/ /usr/share/openvswitch/python/ /usr/share/openvswitch/scripts/ovs-bugtool-* /usr/share/openvswitch/scripts/ovs-check-dead-ifs /usr/share/openvswitch/scripts/ovs-ctl /usr/share/openvswitch/scripts/ovs-lib /usr/share/openvswitch/scripts/ovs-save /usr/share/openvswitch/scripts/ovs-vtep /usr/share/openvswitch/scripts/sysconfig.template /usr/share/openvswitch/vswitch.ovsschema /usr/share/openvswitch/vtep.ovsschema %doc COPYING DESIGN.md INSTALL.SSL.md NOTICE README.md WHY-OVS.md FAQ.md NEWS %doc INSTALL.DPDK.md rhel/README.RHEL README-native-tunneling.md /var/lib/openvswitch /var/log/openvswitch openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-kmod-fedora.spec0000644000000000000000000000013213534540117022212 xustar0030 mtime=1567801423.413843341 30 atime=1567801423.413843341 30 ctime=1567801424.193849091 openvswitch-2.5.9/rhel/openvswitch-kmod-fedora.spec0000644000175000017500000000365613534540117023712 0ustar00jpettitjpettit00000000000000# Generated automatically -- do not modify! -*- buffer-read-only: t -*- # Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2015 Nicira Networks, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. #%define kernel 3.1.5-1.fc16.x86_64 #define kernel %{kernel_source} %{?kversion:%define kernel %kversion} Name: openvswitch-kmod Summary: Open vSwitch Kernel Modules Group: System Environment/Daemons URL: http://www.openvswitch.org/ Vendor: OpenSource Security Ralf Spenneberg Version: 2.5.9 # The entire source code is ASL 2.0 except datapath/ which is GPLv2 License: GPLv2 Release: 1%{?dist} Source: openvswitch-%{version}.tar.gz #Source1: openvswitch-init Buildroot: /tmp/openvswitch-xen-rpm %description Open vSwitch provides standard network bridging functions augmented with support for the OpenFlow protocol for remote per-flow control of traffic. This package contains the kernel modules. %prep %setup -q -n openvswitch-%{version} %build ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{kernel}/build --enable-ssl make %{_smp_mflags} -C datapath/linux %install rm -rf $RPM_BUILD_ROOT make -C datapath/linux modules_install install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{kernel}/kernel/extra/openvswitch find datapath/linux -name *.ko -exec install -m 755 \{\} $RPM_BUILD_ROOT/lib/modules/%{kernel}/kernel/extra/openvswitch \; %clean rm -rf $RPM_BUILD_ROOT %preun %post # Ensure that modprobe will find our modules. depmod %{kernel} %files %defattr(-,root,root) /lib/modules/%{kernel}/kernel/extra/openvswitch/*.ko %changelog * Wed Sep 21 2011 Kyle Mestery - Updated for F15 * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_share_openvswitch_scripts_systemd_sysconfig.template0000644000000000000000000000013013534540071030360 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20184915 openvswitch-2.5.9/rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template0000644000175000017500000000123613534540071032052 0ustar00jpettitjpettit00000000000000### Configuration options for openvswitch # # Enable core files: # --force-corefiles=yes # # Set "nice" priority at which to run ovsdb-server: # --ovsdb-server-priority=-10 # # Set "nice" priority at which to run ovsdb-vswitchd: # --ovs-vswitchd-priority=-10 # # Pass or not --mlockall option to ovs-vswitchd. # This option should be set to "yes" or "no". The default is "yes". # Enabling this option can avoid networking interruptions due to # system memory pressure in extraordinary situations, such as multiple # concurrent VM import operations. # --mlockall=yes # # Use valgrind: # --ovs-vswitchd-wrapper=valgrind # --ovsdb-server-wrapper=valgrind # OPTIONS="" openvswitch-2.5.9/rhel/PaxHeaders.82075/usr_share_openvswitch_scripts_sysconfig.template0000644000000000000000000000013013534540071026610 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20184915 openvswitch-2.5.9/rhel/usr_share_openvswitch_scripts_sysconfig.template0000644000175000017500000000155213534540071030303 0ustar00jpettitjpettit00000000000000### Configuration options for openvswitch # Copyright (C) 2009, 2010, 2011 Nicira, Inc. # FORCE_COREFILES: If 'yes' then core files will be enabled. # FORCE_COREFILES=yes # OVSDB_SERVER_PRIORITY: "nice" priority at which to run ovsdb-server. # # OVSDB_SERVER_PRIORITY=-10 # VSWITCHD_PRIORITY: "nice" priority at which to run ovs-vswitchd. # VSWITCHD_PRIORITY=-10 # VSWITCHD_MLOCKALL: Whether to pass ovs-vswitchd the --mlockall option. # This option should be set to "yes" or "no". The default is "yes". # Enabling this option can avoid networking interruptions due to # system memory pressure in extraordinary situations, such as multiple # concurrent VM import operations. # VSWITCHD_MLOCKALL=yes # OVS_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, # a suitable place to specify --ovs-vswitchd-wrapper=valgrind. # OVS_CTL_OPTS= openvswitch-2.5.9/rhel/PaxHeaders.82075/README.RHEL0000644000000000000000000000013213534540071016207 xustar0030 mtime=1567801401.841684402 30 atime=1567801402.125686488 30 ctime=1567801424.185849033 openvswitch-2.5.9/rhel/README.RHEL0000644000175000017500000001346413534540071017705 0ustar00jpettitjpettit00000000000000Red Hat network scripts integration ----------------------------------- The RPM packages for Open vSwitch provide some integration with Red Hat's network scripts. Using this integration is optional. To use the integration for a Open vSwitch bridge or interface named , create or edit /etc/sysconfig/network-scripts/ifcfg-. This is a shell script that consists of a series of VARIABLE=VALUE assignments. The following OVS-specific variable names are supported: - DEVICETYPE: Always set to "ovs". - TYPE: If this is "OVSBridge", then this file represents an OVS bridge named . Otherwise, it represents a port on an OVS bridge and TYPE must have one of the following values: * "OVSPort", if is a physical port (e.g. eth0) or virtual port (e.g. vif1.0). * "OVSIntPort", if is an internal port (e.g. a tagged VLAN). * "OVSBond", if is an OVS bond. * "OVSTunnel", if is an OVS tunnel. * "OVSPatchPort", if is a patch port Additionally the following DPDK port types may be available, depends on OVS build- and runtime configuration: * "OVSDPDKPort", if is a physical DPDK NIC port (name must start with "dpdk" and end with portid, eg "dpdk0") * "OVSDPDKRPort", if is a DPDK ring port (name must start with dpdkr and end with portid, eg "dpdkr0") * "OVSDPDKVhostPort" if is a DPDK vhost-cuse port * "OVSDPDKVhostUserPort" if is a DPDK vhost-user port - OVS_BRIDGE: If TYPE is anything other than "OVSBridge", set to the name of the OVS bridge to which the port should be attached. - OVS_OPTIONS: Optionally, extra options to set in the "Port" table when adding the port to the bridge, as a sequence of column[:key]=value options. For example, "tag=100" to make the port an access port for VLAN 100. See the documentation of "add-port" in ovs-vsctl(8) for syntax and the section on the Port table in ovs-vswitchd.conf.db(5) for available options. - OVS_EXTRA: Optionally, additional ovs-vsctl commands, separated by "--" (double dash). - BOND_IFACES: For "OVSBond" interfaces, a list of physical interfaces to bond together. - OVS_TUNNEL_TYPE: For "OVSTunnel" interfaces, the type of the tunnel. For example, "gre", "vxlan", etc. - OVS_TUNNEL_OPTIONS: For "OVSTunnel" interfaces, this field should be used to specify the tunnel options like remote_ip, key, etc. - OVS_PATCH_PEER: For "OVSPatchPort" devices, this field specifies the patch's peer on the other bridge. Note ---- * "ifdown" on a bridge will not bring individual ports on the bridge down. "ifup" on a bridge will not add ports to the bridge. This behavior should be compatible with standard bridges (with TYPE=Bridge). * If 'ifup' on an interface is called multiple times, one can see "RTNETLINK answers: File exists" printed on the console. This comes from ifup-eth trying to add zeroconf route multiple times and is harmless. Examples -------- Standalone bridge: ==> ifcfg-ovsbridge0 <== DEVICE=ovsbridge0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBridge BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no Enable DHCP on the bridge: * Needs OVSBOOTPROTO instead of BOOTPROTO. * All the interfaces that can reach the DHCP server as a space separated list in OVSDHCPINTERFACES. DEVICE=ovsbridge0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBridge OVSBOOTPROTO="dhcp" OVSDHCPINTERFACES="eth0" HOTPLUG=no Adding Internal Port to ovsbridge0: ==> ifcfg-intbr0 <== DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 HOTPLUG=no Internal Port with fixed IP address: DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no Internal Port with DHCP: * Needs OVSBOOTPROTO or BOOTPROTO. * All the interfaces that can reach the DHCP server as a space separated list in OVSDHCPINTERFACES. DEVICE=intbr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort OVS_BRIDGE=ovsbridge0 OVSBOOTPROTO="dhcp" OVSDHCPINTERFACES="eth0" HOTPLUG=no Adding physical eth0 to ovsbridge0 described above: ==> ifcfg-eth0 <== DEVICE=eth0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPort OVS_BRIDGE=ovsbridge0 BOOTPROTO=none HOTPLUG=no Tagged VLAN interface on top of ovsbridge0: ==> ifcfg-vlan100 <== DEVICE=vlan100 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSIntPort BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 OVS_BRIDGE=ovsbridge0 OVS_OPTIONS="tag=100" OVS_EXTRA="set Interface $DEVICE external-ids:iface-id=$(hostname -s)-$DEVICE-vif" HOTPLUG=no Bonding: ==> ifcfg-bond0 <== DEVICE=bond0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSBond OVS_BRIDGE=ovsbridge0 BOOTPROTO=none BOND_IFACES="gige-1b-0 gige-1b-1 gige-21-0 gige-21-1" OVS_OPTIONS="bond_mode=balance-tcp lacp=active" HOTPLUG=no ==> ifcfg-gige-* <== DEVICE=gige-* ONBOOT=yes HOTPLUG=no An Open vSwitch Tunnel: ==> ifcfg-gre0 <== DEVICE=ovs-gre0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSTunnel OVS_BRIDGE=ovsbridge0 OVS_TUNNEL_TYPE=gre OVS_TUNNEL_OPTIONS="options:remote_ip=A.B.C.D" Patch Ports: ==> ifcfg-patch-ovs-0 <== DEVICE=patch-ovs-0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPatchPort OVS_BRIDGE=ovsbridge0 OVS_PATCH_PEER=patch-ovs-1 ==> ifcfg-patch-ovs-1 <== DEVICE=patch-ovs-1 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSPatchPort OVS_BRIDGE=ovsbridge1 OVS_PATCH_PEER=patch-ovs-0 User bridge: ==> ifcfg-obr0 <== DEVICE=obr0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSUserBridge BOOTPROTO=static IPADDR=A.B.C.D NETMASK=X.Y.Z.0 HOTPLUG=no DPDK NIC port: ==> ifcfg-dpdk0 <== DPDK vhost-user port: DEVICE=dpdk0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSDPDKPort OVS_BRIDGE=obr0 ==> ifcfg-vhu0 <== DEVICE=vhu0 ONBOOT=yes DEVICETYPE=ovs TYPE=OVSDPDKVhostUserPort OVS_BRIDGE=obr0 Reporting Bugs -------------- Please report problems to bugs@openvswitch.org. openvswitch-2.5.9/rhel/PaxHeaders.82075/openvswitch-fedora.spec.in0000644000000000000000000000013013534540071021664 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 29 ctime=1567801424.20184915 openvswitch-2.5.9/rhel/openvswitch-fedora.spec.in0000644000175000017500000003032113534540071023353 0ustar00jpettitjpettit00000000000000# Spec file for Open vSwitch. # Copyright (C) 2009, 2010, 2013, 2014, 2015 Nicira Networks, Inc. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without warranty of any kind. # # If tests have to be skipped while building, specify the '--without check' # option. For example: # rpmbuild -bb --without check rhel/openvswitch-fedora.spec #%define kernel 2.6.40.4-5.fc15.x86_64 # If libcap-ng isn't available and there is no need for running OVS # as regular user, specify the '--without libcapng' %bcond_without libcapng # To enable DPDK support, specify '--with dpdk' when building %bcond_with dpdk # Enable PIE, bz#955181 %global _hardened_build 1 # some distros (e.g: RHEL-7) don't define _rundir macro yet # Fedora 15 onwards uses /run as _rundir %if 0%{!?_rundir:1} %define _rundir /run %endif Name: openvswitch Summary: Open vSwitch Group: System Environment/Daemons URL: http://www.openvswitch.org/ Version: @VERSION@ # Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the # lib/sflow*.[ch] files are SISSL # datapath/ is GPLv2 (although not built into any of the binary packages) License: ASL 2.0 and LGPLv2+ and SISSL Release: 1%{?dist} Source: http://openvswitch.org/releases/%{name}-%{version}.tar.gz BuildRequires: autoconf automake libtool BuildRequires: systemd-units openssl openssl-devel BuildRequires: python python-twisted-core python-zope-interface PyQt4 BuildRequires: desktop-file-utils BuildRequires: groff graphviz BuildRequires: checkpolicy, selinux-policy-devel # make check dependencies BuildRequires: procps-ng %if %{with libcapng} BuildRequires: libcap-ng libcap-ng-devel %endif %if %{with dpdk} BuildRequires: dpdk-devel >= 2.2.0 Provides: %{name}-dpdk = %{version}-%{release} %endif Requires: openssl iproute module-init-tools #Upstream kernel commit 4f647e0a3c37b8d5086214128614a136064110c3 #Requires: kernel >= 3.15.0-0 Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units Obsoletes: openvswitch-controller <= 0:2.1.0-1 %bcond_without check %description Open vSwitch provides standard network bridging functions and support for the OpenFlow protocol for remote per-flow control of traffic. %package selinux-policy Summary: Open vSwitch SELinux policy License: ASL 2.0 BuildArch: noarch Requires: selinux-policy-targeted %description selinux-policy Tailored Open vSwitch SELinux policy %package -n python-openvswitch Summary: Open vSwitch python bindings License: ASL 2.0 BuildArch: noarch Requires: python %description -n python-openvswitch Python bindings for the Open vSwitch database %package test Summary: Open vSwitch testing utilities License: ASL 2.0 BuildArch: noarch Requires: python-openvswitch = %{version}-%{release} Requires: python python-twisted-core python-twisted-web %description test Utilities that are useful to diagnose performance and connectivity issues in Open vSwitch setup. %package devel Summary: Open vSwitch OpenFlow development package (library, headers) License: ASL 2.0 Provides: openvswitch-static = %{version}-%{release} %description devel This provides static library, libopenswitch.a and the openvswitch header files needed to build an external application. %package ovn Summary: Open vSwitch - Open Virtual Network support License: ASL 2.0 Requires: openvswitch %description ovn OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. %prep %setup -q %build %configure \ %if %{with libcapng} --enable-libcapng \ %else --disable-libcapng \ %endif %if %{with dpdk} --with-dpdk=$(dirname %{_datadir}/dpdk/*/.config) \ %endif --enable-ssl \ --with-pkidir=%{_sharedstatedir}/openvswitch/pki make %{?_smp_mflags} cd selinux make -f %{_datadir}/selinux/devel/Makefile %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT install -d -m 0755 $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch install -p -D -m 0644 \ rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/openvswitch for service in openvswitch ovsdb-server ovs-vswitchd \ ovn-controller ovn-controller-vtep ovn-northd; do install -p -D -m 0644 \ rhel/usr_lib_systemd_system_${service}.service \ $RPM_BUILD_ROOT%{_unitdir}/${service}.service done install -m 0755 rhel/etc_init.d_openvswitch \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/openvswitch.init install -p -D -m 0644 rhel/etc_logrotate.d_openvswitch \ $RPM_BUILD_ROOT/%{_sysconfdir}/logrotate.d/openvswitch install -m 0644 vswitchd/vswitch.ovsschema \ $RPM_BUILD_ROOT/%{_datadir}/openvswitch/vswitch.ovsschema install -d -m 0755 $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifdown-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs install -p -m 0755 rhel/etc_sysconfig_network-scripts_ifup-ovs \ $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/network-scripts/ifup-ovs install -d -m 0755 $RPM_BUILD_ROOT%{python_sitelib} mv $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/* \ $RPM_BUILD_ROOT%{python_sitelib} rmdir $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/ install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch touch $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch/conf.db touch $RPM_BUILD_ROOT%{_sysconfdir}/openvswitch/system-id.conf install -p -m 644 -D selinux/openvswitch-custom.pp \ $RPM_BUILD_ROOT%{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp # remove unpackaged files rm -f $RPM_BUILD_ROOT%{_bindir}/ovs-benchmark \ $RPM_BUILD_ROOT%{_bindir}/ovs-parse-backtrace \ $RPM_BUILD_ROOT%{_bindir}/ovs-pcap \ $RPM_BUILD_ROOT%{_bindir}/ovs-tcpundump \ $RPM_BUILD_ROOT%{_sbindir}/ovs-vlan-bug-workaround \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-benchmark.1 \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-pcap.1 \ $RPM_BUILD_ROOT%{_mandir}/man1/ovs-tcpundump.1 \ $RPM_BUILD_ROOT%{_mandir}/man8/ovs-vlan-bug-workaround.8 \ $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs-save %check %if %{with check} if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :; else cat tests/testsuite.log exit 1 fi %endif %clean rm -rf $RPM_BUILD_ROOT %preun %if 0%{?systemd_preun:1} %systemd_preun %{name}.service %else if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable %{name}.service >/dev/null 2>&1 || : /bin/systemctl stop %{name}.service >/dev/null 2>&1 || : fi %endif %preun ovn %if 0%{?systemd_preun:1} %systemd_preun ovn-controller.service %systemd_preun ovn-controller-vtep.service %systemd_preun ovn-northd.service %else if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || : /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || : fi %endif %post %if 0%{?systemd_post:1} %systemd_post %{name}.service %else # Package install, not upgrade if [ $1 -eq 1 ]; then /bin/systemctl daemon-reload >dev/null || : fi %endif %post ovn %if 0%{?systemd_post:1} %systemd_post ovn-controller.service %systemd_post ovn-controller-vtep.service %systemd_post ovn-northd.service %else # Package install, not upgrade if [ $1 -eq 1 ]; then /bin/systemctl daemon-reload >dev/null || : fi %endif %post selinux-policy /usr/sbin/semodule -i %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp &> /dev/null || : %postun %if 0%{?systemd_postun:1} %systemd_postun %{name}.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : %endif %postun ovn %if 0%{?systemd_postun_with_restart:1} %systemd_postun_with_restart ovn-controller.service %systemd_postun_with_restart ovn-controller-vtep.service %systemd_postun_with_restart ovn-northd.service %else /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ "$1" -ge "1" ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || : /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || : /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || : fi %endif %postun selinux-policy if [ $1 -eq 0 ] ; then /usr/sbin/semodule -r openvswitch-custom &> /dev/null || : fi %files selinux-policy %defattr(-,root,root) %{_datadir}/selinux/packages/%{name}/openvswitch-custom.pp %files -n python-openvswitch %{python_sitelib}/ovs %doc COPYING %files test %{_bindir}/ovs-test %{_bindir}/ovs-vlan-test %{_bindir}/ovs-l3ping %{_mandir}/man8/ovs-test.8* %{_mandir}/man8/ovs-vlan-test.8* %{_mandir}/man8/ovs-l3ping.8* %{python_sitelib}/ovstest %files devel %{_libdir}/*.a %{_libdir}/*.la %{_libdir}/pkgconfig/*.pc %{_includedir}/openvswitch/* %{_includedir}/openflow/* %files %defattr(-,root,root) %{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash %{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash %dir %{_sysconfdir}/openvswitch %config %ghost %{_sysconfdir}/openvswitch/conf.db %config %ghost %{_sysconfdir}/openvswitch/system-id.conf %config(noreplace) %{_sysconfdir}/sysconfig/openvswitch %config(noreplace) %{_sysconfdir}/logrotate.d/openvswitch %{_unitdir}/openvswitch.service %{_unitdir}/ovsdb-server.service %{_unitdir}/ovs-vswitchd.service %{_datadir}/openvswitch/scripts/openvswitch.init %{_sysconfdir}/sysconfig/network-scripts/ifup-ovs %{_sysconfdir}/sysconfig/network-scripts/ifdown-ovs %{_datadir}/openvswitch/bugtool-plugins/ %{_datadir}/openvswitch/scripts/ovs-bugtool-* %{_datadir}/openvswitch/scripts/ovs-check-dead-ifs %{_datadir}/openvswitch/scripts/ovs-lib %{_datadir}/openvswitch/scripts/ovs-vtep %{_datadir}/openvswitch/scripts/ovs-ctl %config %{_datadir}/openvswitch/vswitch.ovsschema %config %{_datadir}/openvswitch/vtep.ovsschema %{_bindir}/ovs-appctl %{_bindir}/ovs-docker %{_bindir}/ovs-dpctl %{_bindir}/ovs-dpctl-top %{_bindir}/ovs-ofctl %{_bindir}/ovs-vsctl %{_bindir}/ovsdb-client %{_bindir}/ovsdb-tool %{_bindir}/ovs-testcontroller %{_bindir}/ovs-pki %{_bindir}/vtep-ctl %{_sbindir}/ovs-bugtool %{_sbindir}/ovs-vswitchd %{_sbindir}/ovsdb-server %{_mandir}/man1/ovsdb-client.1* %{_mandir}/man1/ovsdb-server.1* %{_mandir}/man1/ovsdb-tool.1* %{_mandir}/man5/ovs-vswitchd.conf.db.5* %{_mandir}/man5/vtep.5* %{_mandir}/man8/vtep-ctl.8* %{_mandir}/man8/ovs-appctl.8* %{_mandir}/man8/ovs-bugtool.8* %{_mandir}/man8/ovs-ctl.8* %{_mandir}/man8/ovs-dpctl.8* %{_mandir}/man8/ovs-dpctl-top.8* %{_mandir}/man8/ovs-ofctl.8* %{_mandir}/man8/ovs-pki.8* %{_mandir}/man8/ovs-vsctl.8* %{_mandir}/man8/ovs-vswitchd.8* %{_mandir}/man8/ovs-parse-backtrace.8* %{_mandir}/man8/ovs-testcontroller.8* %doc COPYING DESIGN.md INSTALL.SSL.md NOTICE README.md WHY-OVS.md %doc FAQ.md NEWS INSTALL.DPDK.md rhel/README.RHEL /var/lib/openvswitch /var/log/openvswitch %ghost %attr(755,root,root) %{_rundir}/openvswitch %files ovn %{_bindir}/ovn-controller %{_bindir}/ovn-controller-vtep %{_bindir}/ovn-docker-overlay-driver %{_bindir}/ovn-docker-underlay-driver %{_bindir}/ovn-nbctl %{_bindir}/ovn-northd %{_bindir}/ovn-sbctl %{_datadir}/openvswitch/scripts/ovn-ctl %{_mandir}/man8/ovs-testcontroller.8* %{_mandir}/man5/ovn-nb.5* %{_mandir}/man5/ovn-sb.5* %{_mandir}/man7/ovn-architecture.7* %{_mandir}/man8/ovn-controller.8* %{_mandir}/man8/ovn-controller-vtep.8* %{_mandir}/man8/ovn-ctl.8* %{_mandir}/man8/ovn-nbctl.8* %{_mandir}/man8/ovn-northd.8* %{_mandir}/man8/ovn-sbctl.8* %config %{_datadir}/openvswitch/ovn-nb.ovsschema %config %{_datadir}/openvswitch/ovn-sb.ovsschema %{_unitdir}/ovn-controller.service %{_unitdir}/ovn-controller-vtep.service %{_unitdir}/ovn-northd.service %changelog * Wed Jan 12 2011 Ralf Spenneberg - First build on F14 openvswitch-2.5.9/rhel/PaxHeaders.82075/etc_init.d_openvswitch0000644000000000000000000000013113534540071021171 xustar0029 mtime=1567801401.84568443 30 atime=1567801402.125686488 30 ctime=1567801424.185849033 openvswitch-2.5.9/rhel/etc_init.d_openvswitch0000755000175000017500000000450613534540071022670 0ustar00jpettitjpettit00000000000000#!/bin/sh # # openvswitch # # chkconfig: 2345 09 91 # description: Manage Open vSwitch kernel modules and user-space daemons # Copyright (C) 2009, 2010, 2011, 2013 Nicira, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ### BEGIN INIT INFO # Provides: openvswitch # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Open vSwitch switch ### END INIT INFO . /usr/share/openvswitch/scripts/ovs-lib || exit 1 test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch start () { set ovs_ctl ${1-start} set "$@" --system-id=random if test X"$FORCE_COREFILES" != X; then set "$@" --force-corefiles="$FORCE_COREFILES" fi if test X"$OVSDB_SERVER_PRIORITY" != X; then set "$@" --ovsdb-server-priority="$OVSDB_SERVER_PRIORITY" fi if test X"$VSWITCHD_PRIORITY" != X; then set "$@" --ovs-vswitchd-priority="$VSWITCHD_PRIORITY" fi if test X"$VSWITCHD_MLOCKALL" != X; then set "$@" --mlockall="$VSWITCHD_MLOCKALL" fi set "$@" $OVS_CTL_OPTS "$@" touch /var/lock/subsys/openvswitch } stop () { ovs_ctl stop rm -f /var/lock/subsys/openvswitch } restart () { if [ "$1" = "--save-flows=yes" ]; then start restart else stop start fi } case $1 in start) start ;; stop) stop ;; restart) shift restart "$@" ;; reload|force-reload) # Nothing to do. ;; status) ovs_ctl status exit $? ;; version) ovs_ctl version ;; force-reload-kmod) start force-reload-kmod ;; help) printf "$0 [start|stop|restart|reload|force-reload|status|version|force-reload-kmod]\n" ;; *) printf "Unknown command: $1\n" exit 1 ;; esac openvswitch-2.5.9/PaxHeaders.82075/configure0000644000000000000000000000013213534540100015541 xustar0030 mtime=1567801408.149730828 30 atime=1567801409.469740555 30 ctime=1567801423.633844963 openvswitch-2.5.9/configure0000755000175000017500000250244413534540100017245 0ustar00jpettitjpettit00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for openvswitch 2.5.9. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: bugs@openvswitch.org about your system, including any $0: error possibly output before this message. Then install $0: a modern shell, or manually run the script under such a $0: shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='openvswitch' PACKAGE_TARNAME='openvswitch' PACKAGE_VERSION='2.5.9' PACKAGE_STRING='openvswitch 2.5.9' PACKAGE_BUGREPORT='bugs@openvswitch.org' PACKAGE_URL='' ac_unique_file="datapath/datapath.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS OVS_LDFLAGS OVS_CFLAGS DPDK_NETDEV_FALSE DPDK_NETDEV_TRUE DPDK_vswitchd_LDFLAGS LINUX_ENABLED_FALSE LINUX_ENABLED_TRUE KBUILD KARCH SPARSE CGCCFLAGS SPARSEFLAGS HAVE_WNO_UNUSED_PARAMETER_FALSE HAVE_WNO_UNUSED_PARAMETER_TRUE HAVE_WNO_UNUSED_FALSE HAVE_WNO_UNUSED_TRUE WARNING_FLAGS NEXT_AS_FIRST_DIRECTIVE_STRING_H NEXT_STRING_H NEXT_AS_FIRST_DIRECTIVE_STDIO_H NEXT_STDIO_H PRAGMA_COLUMNS PRAGMA_SYSTEM_HEADER INCLUDE_NEXT_AS_FIRST_DIRECTIVE INCLUDE_NEXT LINUX_FALSE LINUX_TRUE HAVE_POSIX_AIO_FALSE HAVE_POSIX_AIO_TRUE GNU_MAKE_FALSE GNU_MAKE_TRUE HAVE_GROFF_FALSE HAVE_GROFF_TRUE DBDIR RUNDIR PKIDIR HAVE_IF_DL_FALSE HAVE_IF_DL_TRUE HAVE_IF_PACKET_FALSE HAVE_IF_PACKET_TRUE HAVE_DOT_FALSE HAVE_DOT_TRUE HAVE_PYTHON_FALSE HAVE_PYTHON_TRUE PYTHON HAVE_PYTHON LOGDIR CAPNG_LDADD HAVE_LIBCAPNG_FALSE HAVE_LIBCAPNG_TRUE HAVE_LIBCAPNG HAVE_OPENSSL_FALSE HAVE_OPENSSL_TRUE HAVE_OPENSSL SSL_LDFLAGS SSL_LIBS SSL_INCLUDES PKG_CONFIG HAVE_NETLINK_FALSE HAVE_NETLINK_TRUE NDEBUG_FALSE NDEBUG_TRUE VSTUDIO_DDK_FALSE VSTUDIO_DDK_TRUE VSTUDIO_CONFIG MSVC_CFLAGS PTHREAD_LIBS PTHREAD_LDFLAGS PTHREAD_INCLUDES PTHREAD_WIN32_DIR_DLL PTHREAD_WIN32_DIR_DLL_WIN_FORM WIN32_FALSE WIN32_TRUE MSVC64_LDFLAGS ESX_FALSE ESX_TRUE LT_AGE LT_REVISION LT_CURRENT LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL AUTOM4TE PERL EGREP FGREP GREP CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL SPARSE_EXTRA_INCLUDES' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_largefile enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock with_pthread with_debug with_vstudiotarget enable_coverage enable_ndebug enable_ssl with_openssl enable_libcapng with_logdir with_pkidir with_rundir with_dbdir enable_Werror with_linux with_linux_source with_l26 with_l26_source with_dpdk ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP PERL LT_SYS_LIBRARY_PATH KARCH' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures openvswitch 2.5.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/openvswitch] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of openvswitch 2.5.9:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --disable-largefile omit support for large files --enable-shared[=PKGS] build shared libraries [default=no] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-coverage Enable gcov coverage tool. --enable-ndebug Disable debugging features for max performance --disable-ssl Disable OpenSSL support --disable-libcapng Disable Linux capability support --enable-Werror Add -Werror to CFLAGS Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-pthread=DIR root of the pthread-win32 directory --with-debug Build without compiler optimizations --with-vstudiotarget=target_type Target type: Debug/Release --with-openssl=DIR root of the OpenSSL directory --with-logdir=DIR directory used for logs [[LOCALSTATEDIR/log/PACKAGE]] --with-pkidir=DIR PKI hierarchy directory [[LOCALSTATEDIR/lib/openvswitch/pki]] --with-rundir=DIR directory used for pidfiles [[LOCALSTATEDIR/run/openvswitch]] --with-dbdir=DIR directory used for conf.db [[SYSCONFDIR/PACKAGE]] --with-linux=/path/to/linux Specify the Linux kernel build directory --with-linux-source=/path/to/linux-source Specify the Linux kernel source directory (usually figured out automatically from build directory) --with-dpdk=/path/to/dpdk Specify the DPDK build directory Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor PERL path to Perl interpreter LT_SYS_LIBRARY_PATH User-defined run-time library search path. KARCH Kernel Architecture String Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF openvswitch configure 2.5.9 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ----------------------------------- ## ## Report this to bugs@openvswitch.org ## ## ----------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } if eval \${$4+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by openvswitch $as_me 2.5.9, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi as_fn_append ac_header_list " stdio.h" as_fn_append ac_header_list " string.h" # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in build-aux "$srcdir"/build-aux; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. ac_config_headers="$ac_config_headers config.h" ac_config_commands="$ac_config_commands tests/atconfig" am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='openvswitch' VERSION='2.5.9' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a pax tar archive" >&5 $as_echo_n "checking how to create a pax tar archive... " >&6; } # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_pax-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do { echo "$as_me:$LINENO: $_am_tar --version" >&5 ($_am_tar --version) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && break done am__tar="$_am_tar --format=posix -chf - "'"$$tardir"' am__tar_="$_am_tar --format=posix -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x pax -w "$$tardir"' am__tar_='pax -L -x pax -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H pax -L' am__tar_='find "$tardir" -print | cpio -o -H pax -L' am__untar='cpio -i -H pax -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_pax}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } rm -rf conftest.dir if test -s conftest.tar; then { echo "$as_me:$LINENO: $am__untar &5 ($am__untar &5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { echo "$as_me:$LINENO: cat conftest.dir/file" >&5 (cat conftest.dir/file) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir if ${am_cv_prog_tar_pax+:} false; then : $as_echo_n "(cached) " >&6 else am_cv_prog_tar_pax=$_am_tool fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_pax" >&5 $as_echo "$am_cv_prog_tar_pax" >&6; } # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" # Extract the first word of "perl", so it can be a program name with args. set dummy perl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PERL+:} false; then : $as_echo_n "(cached) " >&6 else case $PERL in [\\/]* | ?:[\\/]*) ac_cv_path_PERL="$PERL" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_PERL" && ac_cv_path_PERL="no" ;; esac fi PERL=$ac_cv_path_PERL if test -n "$PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 $as_echo "$PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "$PERL" = no; then as_fn_error $? "Perl interpreter not found in $PATH or $PERL." "$LINENO" 5 fi AUTOM4TE=${AUTOM4TE-"${am_missing_run}autom4te"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" if test "x$ac_cv_header_minix_config_h" = xyes; then : MINIX=yes else MINIX= fi if test "$MINIX" = yes; then $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h $as_echo "#define _MINIX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } if ${ac_cv_safe_to_define___extensions__+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_define___extensions__=yes else ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } test $ac_cv_safe_to_define___extensions__ = yes && $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h $as_echo "#define _ALL_SOURCE 1" >>confdefs.h $as_echo "#define _GNU_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6' macro_revision='2.4.6' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done # Set options # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=no fi enable_dlopen=no enable_win32_dll=no # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) link_all_deplibs=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi link_all_deplibs=no else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC ac_config_commands="$ac_config_commands libtool" # Only expand once: # The following explanation may help to understand the use of the # version number fields: current, revision, and age. # # Consider that there are three possible kinds of reactions from # users of your library to changes in a shared library: # # 1. Programs using the previous version may use the new version as drop-in # replacement, and programs using the new version can also work with the # previous one. In other words, no recompiling nor relinking is needed. # In short, there are no changes to any symbols, no symbols removed, # and no symbols added. In this case, bump revision only, don't touch # current nor age. # # 2. Programs using the previous version may use the new version as drop-in # replacement, but programs using the new version may use APIs not # present in the previous one. In other words, new symbols have been # added and a program linking against the new version may fail with # "unresolved symbols." If linking against the old version at runtime: # set revision to 0, bump current and age. # # 3. Programs may need to be changed, recompiled, relinked in order to use # the new version. This is the case when symbols have been modified or # deleted. Bump current, set revision and age to 0. LT_CURRENT=1 LT_REVISION=0 LT_AGE=0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5 $as_echo_n "checking for library containing pow... " >&6; } if ${ac_cv_search_pow+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pow (); int main () { return pow (); ; return 0; } _ACEOF for ac_lib in '' m; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_pow=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_pow+:} false; then : break fi done if ${ac_cv_search_pow+:} false; then : else ac_cv_search_pow=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5 $as_echo "$ac_cv_search_pow" >&6; } ac_res=$ac_cv_search_pow if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing timer_create" >&5 $as_echo_n "checking for library containing timer_create... " >&6; } if ${ac_cv_search_timer_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char timer_create (); int main () { return timer_create (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_timer_create=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_timer_create+:} false; then : break fi done if ${ac_cv_search_timer_create+:} false; then : else ac_cv_search_timer_create=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_timer_create" >&5 $as_echo "$ac_cv_search_timer_create" >&6; } ac_res=$ac_cv_search_timer_create if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5 $as_echo_n "checking for library containing pthread_create... " >&6; } if ${ac_cv_search_pthread_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_create (); int main () { return pthread_create (); ; return 0; } _ACEOF for ac_lib in '' pthread; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_pthread_create=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_pthread_create+:} false; then : break fi done if ${ac_cv_search_pthread_create+:} false; then : else ac_cv_search_pthread_create=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5 $as_echo "$ac_cv_search_pthread_create" >&6; } ac_res=$ac_cv_search_pthread_create if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" if test "x$ac_cv_have_decl_strerror_r" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_STRERROR_R $ac_have_decl _ACEOF for ac_func in strerror_r do : ac_fn_c_check_func "$LINENO" "strerror_r" "ac_cv_func_strerror_r" if test "x$ac_cv_func_strerror_r" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRERROR_R 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5 $as_echo_n "checking whether strerror_r returns char *... " >&6; } if ${ac_cv_func_strerror_r_char_p+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_func_strerror_r_char_p=no if test $ac_cv_have_decl_strerror_r = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); char *p = strerror_r (0, buf, sizeof buf); return !p || x; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_func_strerror_r_char_p=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else # strerror_r is not declared. Choose between # systems that have relatively inaccessible declarations for the # function. BeOS and DEC UNIX 4.0 fall in this category, but the # former has a strerror_r that returns char*, while the latter # has a strerror_r that returns `int'. # This test should segfault on the DEC system. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default extern char *strerror_r (); int main () { char buf[100]; char x = *strerror_r (0, buf, sizeof buf); return ! isalpha (x); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_strerror_r_char_p=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5 $as_echo "$ac_cv_func_strerror_r_char_p" >&6; } if test $ac_cv_func_strerror_r_char_p = yes; then $as_echo "#define STRERROR_R_CHAR_P 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "vmware.h" "ac_cv_header_vmware_h" "$ac_includes_default" if test "x$ac_cv_header_vmware_h" = xyes; then : ESX=yes else ESX=no fi if test "$ESX" = yes; then ESX_TRUE= ESX_FALSE='#' else ESX_TRUE='#' ESX_FALSE= fi if test "$ESX" = yes; then $as_echo "#define ESX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for MSVC x64 compiler" >&5 $as_echo_n "checking for MSVC x64 compiler... " >&6; } if ${cl_cv_x64+:} false; then : $as_echo_n "(cached) " >&6 else if (cl) 2>&1 | grep 'x64' >/dev/null 2>&1; then cl_cv_x64=yes MSVC64_LDFLAGS=" /MACHINE:X64 " else cl_cv_x64=no MSVC64_LDFLAGS="" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cl_cv_x64" >&5 $as_echo "$cl_cv_x64" >&6; } ac_fn_c_check_header_mongrel "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default" if test "x$ac_cv_header_windows_h" = xyes; then : WIN32=yes else WIN32=no fi if test "$WIN32" = yes; then WIN32_TRUE= WIN32_FALSE='#' else WIN32_TRUE='#' WIN32_FALSE= fi if test "$WIN32" = yes; then # Check whether --with-pthread was given. if test "${with_pthread+set}" = set; then : withval=$with_pthread; case "$withval" in "" | y | ye | yes | n | no) as_fn_error $? "Invalid --with-pthread value" "$LINENO" 5 ;; *) if (cl) 2>&1 | grep 'x64' >/dev/null 2>&1; then cl_cv_x64=yes else cl_cv_x64=no fi if test "$cl_cv_x64" = yes; then PTHREAD_WIN32_DIR=$withval/lib/x64 PTHREAD_WIN32_DIR_DLL=/$(echo ${withval} | ${SED} -e 's/://')/dll/x64 PTHREAD_WIN32_DIR_DLL_WIN_FORM=$withval/dll/x64 else PTHREAD_WIN32_DIR=$withval/lib/x86 PTHREAD_WIN32_DIR_DLL=/$(echo ${withval} | ${SED} -e 's/://')/dll/x86 PTHREAD_WIN32_DIR_DLL_WIN_FORM=$withval/dll/x86 fi PTHREAD_INCLUDES=-I$withval/include PTHREAD_LDFLAGS=-L$PTHREAD_WIN32_DIR PTHREAD_LIBS="-lpthreadVC2" ;; esac else as_fn_error $? "pthread directory not specified" "$LINENO" 5 fi # Check whether --with-debug was given. if test "${with_debug+set}" = set; then : withval=$with_debug; MSVC_CFLAGS="-O0" else MSVC_CFLAGS="-O2" fi $as_echo "#define WIN32 1" >>confdefs.h fi # Check whether --with-vstudiotarget was given. if test "${with_vstudiotarget+set}" = set; then : withval=$with_vstudiotarget; case "$withval" in "Release") ;; "Debug") ;; *) as_fn_error $? "No valid Visual Studio configuration found" "$LINENO" 5 ;; esac VSTUDIO_CONFIG=$withval else VSTUDIO_CONFIG= fi $as_echo "#define VSTUDIO_DDK 1" >>confdefs.h if test -n "$VSTUDIO_CONFIG"; then VSTUDIO_DDK_TRUE= VSTUDIO_DDK_FALSE='#' else VSTUDIO_DDK_TRUE='#' VSTUDIO_DDK_FALSE= fi # Check whether --enable-coverage was given. if test "${enable_coverage+set}" = set; then : enableval=$enable_coverage; case "${enableval}" in (yes) coverage=true ;; (no) coverage=false ;; (*) as_fn_error $? "bad value ${enableval} for --enable-coverage" "$LINENO" 5 ;; esac else coverage=false fi if $coverage; then # Autoconf by default puts "-g -O2" in CFLAGS. We need to remove the -O2 # option for coverage to be useful. This does it without otherwise # interfering with anything that the user might have put there. old_CFLAGS=$CFLAGS CFLAGS= for option in $old_CFLAGS; do case $option in (-O2) ;; (*) CFLAGS="$CFLAGS $option" ;; esac done OVS_CFLAGS="$OVS_CFLAGS --coverage" OVS_LDFLAGS="$OVS_LDFLAGS --coverage" fi # Check whether --enable-ndebug was given. if test "${enable_ndebug+set}" = set; then : enableval=$enable_ndebug; case "${enableval}" in (yes) ndebug=true ;; (no) ndebug=false ;; (*) as_fn_error $? "bad value ${enableval} for --enable-ndebug" "$LINENO" 5 ;; esac else ndebug=false fi if test x$ndebug = xtrue; then NDEBUG_TRUE= NDEBUG_FALSE='#' else NDEBUG_TRUE='#' NDEBUG_FALSE= fi ac_fn_c_check_header_compile "$LINENO" "linux/netlink.h" "ac_cv_header_linux_netlink_h" "#include " if test "x$ac_cv_header_linux_netlink_h" = xyes; then : HAVE_NETLINK=yes else HAVE_NETLINK=no fi if test "$HAVE_NETLINK" = yes; then HAVE_NETLINK_TRUE= HAVE_NETLINK_FALSE='#' else HAVE_NETLINK_TRUE='#' HAVE_NETLINK_FALSE= fi if test "$HAVE_NETLINK" = yes; then $as_echo "#define HAVE_NETLINK 1" >>confdefs.h fi # Check whether --enable-ssl was given. if test "${enable_ssl+set}" = set; then : enableval=$enable_ssl; case "${enableval}" in (yes) ssl=true ;; (no) ssl=false ;; (*) as_fn_error $? "bad value ${enableval} for --enable-ssl" "$LINENO" 5 ;; esac else ssl=check fi if test "$ssl" != false; then found=false # Check whether --with-openssl was given. if test "${with_openssl+set}" = set; then : withval=$with_openssl; case "$withval" in "" | y | ye | yes | n | no) as_fn_error $? "Invalid --with-openssl value" "$LINENO" 5 ;; *) ssldirs="$withval" ;; esac else # if pkg-config is installed and openssl has installed a .pc file, # then use that information and don't search ssldirs # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$PKG_CONFIG" != x""; then SSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null` if test $? = 0; then SSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null` SSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null` found=true fi fi # no such luck; use some default ssldirs if ! $found; then ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr" fi fi # note that we #include , so the OpenSSL headers have to be in # an 'openssl' subdirectory if ! $found; then SSL_INCLUDES= for ssldir in $ssldirs; do { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $ssldir" >&5 $as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; } if test -f "$ssldir/include/openssl/ssl.h"; then SSL_INCLUDES="-I$ssldir/include" SSL_LDFLAGS="-L$ssldir/lib" if test "$WIN32" = "yes"; then SSL_LIBS="-lssleay32 -llibeay32" else SSL_LIBS="-lssl -lcrypto" fi found=true { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } break else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi done # if the file wasn't found, well, go ahead and try the link anyway -- maybe # it will just work! fi # try the preprocessor and linker with our new flags, # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling and linking against OpenSSL works" >&5 $as_echo_n "checking whether compiling and linking against OpenSSL works... " >&6; } echo "Trying link with SSL_LDFLAGS=$SSL_LDFLAGS;" \ "SSL_LIBS=$SSL_LIBS; SSL_INCLUDES=$SSL_INCLUDES" >&5 save_LIBS="$LIBS" save_LDFLAGS="$LDFLAGS" save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS $SSL_LDFLAGS" LIBS="$SSL_LIBS $LIBS" CPPFLAGS="$SSL_INCLUDES $CPPFLAGS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { SSL_CTX *ctx=NULL;SSL_new(ctx) ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } HAVE_OPENSSL=yes else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } HAVE_OPENSSL=no if test "$ssl" = check; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot find openssl: $SSL_PKG_ERRORS OpenFlow connections over SSL will not be supported. (You may use --disable-ssl to suppress this warning.)" >&5 $as_echo "$as_me: WARNING: Cannot find openssl: $SSL_PKG_ERRORS OpenFlow connections over SSL will not be supported. (You may use --disable-ssl to suppress this warning.)" >&2;} else as_fn_error $? "Cannot find openssl (use --disable-ssl to configure without SSL support)" "$LINENO" 5 fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" else HAVE_OPENSSL=no fi if test "$HAVE_OPENSSL" = yes; then HAVE_OPENSSL_TRUE= HAVE_OPENSSL_FALSE='#' else HAVE_OPENSSL_TRUE='#' HAVE_OPENSSL_FALSE= fi if test "$HAVE_OPENSSL" = yes; then $as_echo "#define HAVE_OPENSSL 1" >>confdefs.h fi # Check whether --enable-libcapng was given. if test "${enable_libcapng+set}" = set; then : enableval=$enable_libcapng; case "${enableval}" in (yes) libcapng=true ;; (no) libcapng=false ;; (*) as_fn_error $? "bad value ${enableval} for --enable-libcapng" "$LINENO" 5 ;; esac else libcapng=check fi if test "$libcapng" != false; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for capng_clear in -lcap-ng" >&5 $as_echo_n "checking for capng_clear in -lcap-ng... " >&6; } if ${ac_cv_lib_cap_ng_capng_clear+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcap-ng $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char capng_clear (); int main () { return capng_clear (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cap_ng_capng_clear=yes else ac_cv_lib_cap_ng_capng_clear=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_ng_capng_clear" >&5 $as_echo "$ac_cv_lib_cap_ng_capng_clear" >&6; } if test "x$ac_cv_lib_cap_ng_capng_clear" = xyes; then : HAVE_LIBCAPNG=yes fi if test "$HAVE_LIBCAPNG" != yes; then if test "$libcapng" = true ; then as_fn_error $? "libcap-ng support requested, but not found" "$LINENO" 5 fi if test "$libcapng" = check ; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find libcap-ng. --user option will not be supported on Linux. (you may use --disable-libcapng to suppress this warning). " >&5 $as_echo "$as_me: WARNING: cannot find libcap-ng. --user option will not be supported on Linux. (you may use --disable-libcapng to suppress this warning). " >&2;} fi fi fi if test "$HAVE_LIBCAPNG" = yes; then HAVE_LIBCAPNG_TRUE= HAVE_LIBCAPNG_FALSE='#' else HAVE_LIBCAPNG_TRUE='#' HAVE_LIBCAPNG_FALSE= fi if test "$HAVE_LIBCAPNG" = yes; then $as_echo "#define HAVE_LIBCAPNG 1" >>confdefs.h CAPNG_LDADD="-lcap-ng" fi # Check whether --with-logdir was given. if test "${with_logdir+set}" = set; then : withval=$with_logdir; LOGDIR=$withval else LOGDIR='${localstatedir}/log/${PACKAGE}' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python 2.x for x >= 7" >&5 $as_echo_n "checking for Python 2.x for x >= 7... " >&6; } if ${ovs_cv_python+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$PYTHON"; then ovs_cv_python=$PYTHON else ovs_cv_python=no for binary in python python2.7; do ovs_save_IFS=$IFS; IFS=$PATH_SEPARATOR for dir in $PATH; do IFS=$ovs_save_IFS test -z "$dir" && dir=. if test -x "$dir"/"$binary" && "$dir"/"$binary" -c 'import sys if sys.hexversion >= 0x02070000 and sys.hexversion < 0x03000000: sys.exit(0) else: sys.exit(1)'; then ovs_cv_python=$dir/$binary break 2 fi done done fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_python" >&5 $as_echo "$ovs_cv_python" >&6; } PYTHON=${PYTHON-"${am_missing_run}python"} if test $ovs_cv_python != no; then PYTHON=$ovs_cv_python HAVE_PYTHON=yes else HAVE_PYTHON=no fi if test "$HAVE_PYTHON" = yes; then HAVE_PYTHON_TRUE= HAVE_PYTHON_FALSE='#' else HAVE_PYTHON_TRUE='#' HAVE_PYTHON_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dot" >&5 $as_echo_n "checking for dot... " >&6; } if ${ovs_cv_dot+:} false; then : $as_echo_n "(cached) " >&6 else if (dot -V) 2>&1 | grep '^dot - [gG]raphviz version' >/dev/null 2>&1; then ovs_cv_dot=yes else ovs_cv_dot=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_dot" >&5 $as_echo "$ovs_cv_dot" >&6; } if test "$ovs_cv_dot" = yes; then HAVE_DOT_TRUE= HAVE_DOT_FALSE='#' else HAVE_DOT_TRUE='#' HAVE_DOT_FALSE= fi ac_fn_c_check_header_mongrel "$LINENO" "net/if_packet.h" "ac_cv_header_net_if_packet_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_packet_h" = xyes; then : HAVE_IF_PACKET=yes else HAVE_IF_PACKET=no fi if test "$HAVE_IF_PACKET" = yes; then HAVE_IF_PACKET_TRUE= HAVE_IF_PACKET_FALSE='#' else HAVE_IF_PACKET_TRUE='#' HAVE_IF_PACKET_FALSE= fi if test "$HAVE_IF_PACKET" = yes; then $as_echo "#define HAVE_IF_PACKET 1" >>confdefs.h fi ac_fn_c_check_header_mongrel "$LINENO" "net/if_dl.h" "ac_cv_header_net_if_dl_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_dl_h" = xyes; then : HAVE_IF_DL=yes else HAVE_IF_DL=no fi if test "$HAVE_IF_DL" = yes; then HAVE_IF_DL_TRUE= HAVE_IF_DL_FALSE='#' else HAVE_IF_DL_TRUE='#' HAVE_IF_DL_FALSE= fi if test "$HAVE_IF_DL" = yes; then $as_echo "#define HAVE_IF_DL 1" >>confdefs.h # On these platforms we use libpcap to access network devices. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pcap_open_live" >&5 $as_echo_n "checking for library containing pcap_open_live... " >&6; } if ${ac_cv_search_pcap_open_live+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pcap_open_live (); int main () { return pcap_open_live (); ; return 0; } _ACEOF for ac_lib in '' pcap; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_pcap_open_live=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_pcap_open_live+:} false; then : break fi done if ${ac_cv_search_pcap_open_live+:} false; then : else ac_cv_search_pcap_open_live=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pcap_open_live" >&5 $as_echo "$ac_cv_search_pcap_open_live" >&6; } ac_res=$ac_cv_search_pcap_open_live if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strtok_r macro segfaults on some inputs" >&5 $as_echo_n "checking whether strtok_r macro segfaults on some inputs... " >&6; } if ${ovs_cv_strtok_r_bug+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ovs_cv_strtok_r_bug=yes else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 /* Assume bug is present, because relatively minor changes in compiler settings (e.g. optimization level) can make it crop up. */ return 1; #else char string[] = ":::"; char *save_ptr = (char *) 0xc0ffee; char *token1, *token2; token1 = strtok_r(string, ":", &save_ptr); token2 = strtok_r(NULL, ":", &save_ptr); freopen ("/dev/null", "w", stdout); printf ("%s %s\n", token1, token2); return 0; #endif ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ovs_cv_strtok_r_bug=no else ovs_cv_strtok_r_bug=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_strtok_r_bug" >&5 $as_echo "$ovs_cv_strtok_r_bug" >&6; } if test $ovs_cv_strtok_r_bug = yes; then $as_echo "#define HAVE_STRTOK_R_BUG 1" >>confdefs.h fi ac_fn_c_check_decl "$LINENO" "sys_siglist" "ac_cv_have_decl_sys_siglist" "#include " if test "x$ac_cv_have_decl_sys_siglist" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_SYS_SIGLIST $ac_have_decl _ACEOF ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "#include " if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 _ACEOF fi ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimensec" "ac_cv_member_struct_stat_st_mtimensec" "#include " if test "x$ac_cv_member_struct_stat_st_mtimensec" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 _ACEOF fi ac_fn_c_check_member "$LINENO" "struct ifreq" "ifr_flagshigh" "ac_cv_member_struct_ifreq_ifr_flagshigh" "#include " if test "x$ac_cv_member_struct_ifreq_ifr_flagshigh" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_IFREQ_IFR_FLAGSHIGH 1 _ACEOF fi for ac_func in mlockall strnlen getloadavg statvfs getmntent_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in mntent.h sys/statvfs.h linux/types.h linux/if_ether.h stdatomic.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in net/if_mib.h do : ac_fn_c_check_header_compile "$LINENO" "net/if_mib.h" "ac_cv_header_net_if_mib_h" "#include #include " if test "x$ac_cv_header_net_if_mib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_NET_IF_MIB_H 1 _ACEOF fi done # Check whether --with-pkidir was given. if test "${with_pkidir+set}" = set; then : withval=$with_pkidir; PKIDIR=$withval else PKIDIR='${localstatedir}/lib/openvswitch/pki' fi # Check whether --with-rundir was given. if test "${with_rundir+set}" = set; then : withval=$with_rundir; RUNDIR=$withval else RUNDIR='${localstatedir}/run/openvswitch' fi # Check whether --with-dbdir was given. if test "${with_dbdir+set}" = set; then : withval=$with_dbdir; DBDIR=$withval else DBDIR='${sysconfdir}/${PACKAGE}' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing backtrace" >&5 $as_echo_n "checking for library containing backtrace... " >&6; } if ${ac_cv_search_backtrace+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char backtrace (); int main () { return backtrace (); ; return 0; } _ACEOF for ac_lib in '' execinfo ubacktrace; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_backtrace=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_backtrace+:} false; then : break fi done if ${ac_cv_search_backtrace+:} false; then : else ac_cv_search_backtrace=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_backtrace" >&5 $as_echo "$ac_cv_search_backtrace" >&6; } ac_res=$ac_cv_search_backtrace if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_BACKTRACE 1" >>confdefs.h fi for ac_header in linux/perf_event.h do : ac_fn_c_check_header_mongrel "$LINENO" "linux/perf_event.h" "ac_cv_header_linux_perf_event_h" "$ac_includes_default" if test "x$ac_cv_header_linux_perf_event_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_PERF_EVENT_H 1 _ACEOF fi done for ac_header in valgrind/valgrind.h do : ac_fn_c_check_header_mongrel "$LINENO" "valgrind/valgrind.h" "ac_cv_header_valgrind_valgrind_h" "$ac_includes_default" if test "x$ac_cv_header_valgrind_valgrind_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VALGRIND_VALGRIND_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for connect in -lsocket" >&5 $as_echo_n "checking for connect in -lsocket... " >&6; } if ${ac_cv_lib_socket_connect+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char connect (); int main () { return connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_connect=yes else ac_cv_lib_socket_connect=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_connect" >&5 $as_echo "$ac_cv_lib_socket_connect" >&6; } if test "x$ac_cv_lib_socket_connect" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 $as_echo_n "checking for library containing gethostbyname... " >&6; } if ${ac_cv_search_gethostbyname+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gethostbyname (); int main () { return gethostbyname (); ; return 0; } _ACEOF for ac_lib in '' resolv; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_gethostbyname=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_gethostbyname+:} false; then : break fi done if ${ac_cv_search_gethostbyname+:} false; then : else ac_cv_search_gethostbyname=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 $as_echo "$ac_cv_search_gethostbyname" >&6; } ac_res=$ac_cv_search_gethostbyname if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking XenServer release" >&5 $as_echo_n "checking XenServer release... " >&6; } if ${ovs_cv_xsversion+:} false; then : $as_echo_n "(cached) " >&6 else if test -e /etc/redhat-release; then ovs_cv_xsversion=`sed -n 's/^XenServer DDK release \([^-]*\)-.*/\1/p' /etc/redhat-release` fi if test -z "$ovs_cv_xsversion"; then ovs_cv_xsversion=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_xsversion" >&5 $as_echo "$ovs_cv_xsversion" >&6; } case $ovs_cv_xsversion in none) ;; [1-9][0-9]* | [6-9]* | 5.[7-9]* | 5.6.[1-9][0-9][0-9][0-9]* | 5.6.[2-9][0-9][0-9]* | 5.6.1[0-9][0-9]) ;; *) as_fn_error $? "This appears to be XenServer $ovs_cv_xsversion, but only XenServer 5.6.100 or later is supported. (If you are really using a supported version of XenServer, you may override this error message by specifying 'ovs_cv_xsversion=5.6.100' on the \"configure\" command line.)" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for groff" >&5 $as_echo_n "checking for groff... " >&6; } if ${ovs_cv_groff+:} false; then : $as_echo_n "(cached) " >&6 else if (groff -v) >/dev/null 2>&1; then ovs_cv_groff=yes else ovs_cv_groff=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_groff" >&5 $as_echo "$ovs_cv_groff" >&6; } if test "$ovs_cv_groff" = yes; then HAVE_GROFF_TRUE= HAVE_GROFF_FALSE='#' else HAVE_GROFF_TRUE='#' HAVE_GROFF_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} is GNU make" >&5 $as_echo_n "checking whether ${MAKE-make} is GNU make... " >&6; } if ${ovs_cv_gnu_make+:} false; then : $as_echo_n "(cached) " >&6 else rm -f conftest.out $as_echo "$as_me:$LINENO: invoking ${MAKE-make} --version:" >&5 2>&1 ${MAKE-make} --version >conftest.out 2>&1 cat conftest.out >&5 2>&1 result=`cat conftest.out` rm -f conftest.mk conftest.out case $result in # ( GNU*) ovs_cv_gnu_make=yes ;; # ( *) ovs_cv_gnu_make=no ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_gnu_make" >&5 $as_echo "$ovs_cv_gnu_make" >&6; } if test $ovs_cv_gnu_make = yes; then GNU_MAKE_TRUE= GNU_MAKE_FALSE='#' else GNU_MAKE_TRUE='#' GNU_MAKE_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC has that supports thread_local" >&5 $as_echo_n "checking whether $CC has that supports thread_local... " >&6; } if ${ovs_cv_thread_local+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static thread_local int var; int main () { return var; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ovs_cv_thread_local=yes else ovs_cv_thread_local=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_thread_local" >&5 $as_echo "$ovs_cv_thread_local" >&6; } if test $ovs_cv_thread_local = yes; then $as_echo "#define HAVE_THREAD_LOCAL 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports __thread" >&5 $as_echo_n "checking whether $CC supports __thread... " >&6; } if ${ovs_cv___thread+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ static __thread int var; int main () { return var; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ovs_cv___thread=yes else ovs_cv___thread=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv___thread" >&5 $as_echo "$ovs_cv___thread" >&6; } if test $ovs_cv___thread = yes; then $as_echo "#define HAVE___THREAD 1" >>confdefs.h fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __atomic_load_8" >&5 $as_echo_n "checking for library containing __atomic_load_8... " >&6; } if ${ac_cv_search___atomic_load_8+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __atomic_load_8 (); int main () { return __atomic_load_8 (); ; return 0; } _ACEOF for ac_lib in '' atomic; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search___atomic_load_8=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search___atomic_load_8+:} false; then : break fi done if ${ac_cv_search___atomic_load_8+:} false; then : else ac_cv_search___atomic_load_8=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___atomic_load_8" >&5 $as_echo "$ac_cv_search___atomic_load_8" >&6; } ac_res=$ac_cv_search___atomic_load_8 if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports GCC 4.0+ atomic built-ins" >&5 $as_echo_n "checking whether $CC supports GCC 4.0+ atomic built-ins... " >&6; } if ${ovs_cv_gcc4_atomics+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #define ovs_assert(expr) if (!(expr)) abort(); #define TEST_ATOMIC_TYPE(TYPE) \ { \ TYPE x = 1; \ TYPE orig; \ \ __sync_synchronize(); \ ovs_assert(x == 1); \ \ __sync_synchronize(); \ x = 3; \ __sync_synchronize(); \ ovs_assert(x == 3); \ \ orig = __sync_fetch_and_add(&x, 1); \ ovs_assert(orig == 3); \ __sync_synchronize(); \ ovs_assert(x == 4); \ \ orig = __sync_fetch_and_sub(&x, 2); \ ovs_assert(orig == 4); \ __sync_synchronize(); \ ovs_assert(x == 2); \ \ orig = __sync_fetch_and_or(&x, 6); \ ovs_assert(orig == 2); \ __sync_synchronize(); \ ovs_assert(x == 6); \ \ orig = __sync_fetch_and_and(&x, 10); \ ovs_assert(orig == 6); \ __sync_synchronize(); \ ovs_assert(x == 2); \ \ orig = __sync_fetch_and_xor(&x, 10); \ ovs_assert(orig == 2); \ __sync_synchronize(); \ ovs_assert(x == 8); \ } int main () { TEST_ATOMIC_TYPE(char); TEST_ATOMIC_TYPE(unsigned char); TEST_ATOMIC_TYPE(signed char); TEST_ATOMIC_TYPE(short); TEST_ATOMIC_TYPE(unsigned short); TEST_ATOMIC_TYPE(int); TEST_ATOMIC_TYPE(unsigned int); TEST_ATOMIC_TYPE(long int); TEST_ATOMIC_TYPE(unsigned long int); TEST_ATOMIC_TYPE(long long int); TEST_ATOMIC_TYPE(unsigned long long int); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ovs_cv_gcc4_atomics=yes else ovs_cv_gcc4_atomics=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_gcc4_atomics" >&5 $as_echo "$ovs_cv_gcc4_atomics" >&6; } if test $ovs_cv_gcc4_atomics = yes; then $as_echo "#define HAVE_GCC4_ATOMICS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking value of __atomic_always_lock_free(1)" >&5 $as_echo_n "checking value of __atomic_always_lock_free(1)... " >&6; } if ${ovs_cv_atomic_always_lock_free_1+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "__atomic_always_lock_free(1, 0)" "ovs_cv_atomic_always_lock_free_1" ""; then : else ovs_cv_atomic_always_lock_free_1=unsupported fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_atomic_always_lock_free_1" >&5 $as_echo "$ovs_cv_atomic_always_lock_free_1" >&6; } if test ovs_cv_atomic_always_lock_free_1 != unsupported; then cat >>confdefs.h <<_ACEOF #define ATOMIC_ALWAYS_LOCK_FREE_1B $ovs_cv_atomic_always_lock_free_1 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking value of __atomic_always_lock_free(2)" >&5 $as_echo_n "checking value of __atomic_always_lock_free(2)... " >&6; } if ${ovs_cv_atomic_always_lock_free_2+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "__atomic_always_lock_free(2, 0)" "ovs_cv_atomic_always_lock_free_2" ""; then : else ovs_cv_atomic_always_lock_free_2=unsupported fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_atomic_always_lock_free_2" >&5 $as_echo "$ovs_cv_atomic_always_lock_free_2" >&6; } if test ovs_cv_atomic_always_lock_free_2 != unsupported; then cat >>confdefs.h <<_ACEOF #define ATOMIC_ALWAYS_LOCK_FREE_2B $ovs_cv_atomic_always_lock_free_2 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking value of __atomic_always_lock_free(4)" >&5 $as_echo_n "checking value of __atomic_always_lock_free(4)... " >&6; } if ${ovs_cv_atomic_always_lock_free_4+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "__atomic_always_lock_free(4, 0)" "ovs_cv_atomic_always_lock_free_4" ""; then : else ovs_cv_atomic_always_lock_free_4=unsupported fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_atomic_always_lock_free_4" >&5 $as_echo "$ovs_cv_atomic_always_lock_free_4" >&6; } if test ovs_cv_atomic_always_lock_free_4 != unsupported; then cat >>confdefs.h <<_ACEOF #define ATOMIC_ALWAYS_LOCK_FREE_4B $ovs_cv_atomic_always_lock_free_4 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking value of __atomic_always_lock_free(8)" >&5 $as_echo_n "checking value of __atomic_always_lock_free(8)... " >&6; } if ${ovs_cv_atomic_always_lock_free_8+:} false; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "__atomic_always_lock_free(8, 0)" "ovs_cv_atomic_always_lock_free_8" ""; then : else ovs_cv_atomic_always_lock_free_8=unsupported fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_atomic_always_lock_free_8" >&5 $as_echo "$ovs_cv_atomic_always_lock_free_8" >&6; } if test ovs_cv_atomic_always_lock_free_8 != unsupported; then cat >>confdefs.h <<_ACEOF #define ATOMIC_ALWAYS_LOCK_FREE_8B $ovs_cv_atomic_always_lock_free_8 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing aio_write" >&5 $as_echo_n "checking for library containing aio_write... " >&6; } if ${ac_cv_search_aio_write+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char aio_write (); int main () { return aio_write (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_aio_write=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_aio_write+:} false; then : break fi done if ${ac_cv_search_aio_write+:} false; then : else ac_cv_search_aio_write=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_aio_write" >&5 $as_echo "$ac_cv_search_aio_write" >&6; } ac_res=$ac_cv_search_aio_write if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi if test "$ac_cv_search_aio_write" != no; then HAVE_POSIX_AIO_TRUE= HAVE_POSIX_AIO_FALSE='#' else HAVE_POSIX_AIO_TRUE='#' HAVE_POSIX_AIO_FALSE= fi for ac_func in pthread_set_name_np do : ac_fn_c_check_func "$LINENO" "pthread_set_name_np" "ac_cv_func_pthread_set_name_np" if test "x$ac_cv_func_pthread_set_name_np" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTHREAD_SET_NAME_NP 1 _ACEOF fi done if test $ac_cv_func_pthread_set_name_np != yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_setname_np() variant" >&5 $as_echo_n "checking for pthread_setname_np() variant... " >&6; } if ${ovs_cv_pthread_setname_np+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_setname_np(pthread_self(), "name"); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ovs_cv_pthread_setname_np=glibc else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { pthread_setname_np(pthread_self(), "%s", "name"); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ovs_cv_pthread_setname_np=netbsd else ovs_cv_pthread_setname_np=none fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_pthread_setname_np" >&5 $as_echo "$ovs_cv_pthread_setname_np" >&6; } case $ovs_cv_pthread_setname_np in # ( glibc) $as_echo "#define HAVE_GLIBC_PTHREAD_SETNAME_NP 1" >>confdefs.h ;; # ( netbsd) $as_echo "#define HAVE_NETBSD_PTHREAD_SETNAME_NP 1" >>confdefs.h ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __linux__ is defined" >&5 $as_echo_n "checking whether __linux__ is defined... " >&6; } if ${ovs_cv_linux+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ enum { LINUX = __linux__}; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ovs_cv_linux=true else ovs_cv_linux=false fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_linux" >&5 $as_echo "$ovs_cv_linux" >&6; } if $ovs_cv_linux; then LINUX_TRUE= LINUX_FALSE='#' else LINUX_TRUE='#' LINUX_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working posix_memalign" >&5 $as_echo_n "checking for working posix_memalign... " >&6; } if ${ax_cv_func_posix_memalign_works+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ax_cv_func_posix_memalign_works=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { void *buffer; /* Some versions of glibc incorrectly perform the alignment check on * the size word. */ exit (posix_memalign (&buffer, sizeof(void *), 123) != 0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ax_cv_func_posix_memalign_works=yes else ax_cv_func_posix_memalign_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_func_posix_memalign_works" >&5 $as_echo "$ax_cv_func_posix_memalign_works" >&6; } if test "$ax_cv_func_posix_memalign_works" = "yes" ; then $as_echo "#define HAVE_POSIX_MEMALIGN 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the preprocessor supports include_next" >&5 $as_echo_n "checking whether the preprocessor supports include_next... " >&6; } if ${gl_cv_have_include_next+:} false; then : $as_echo_n "(cached) " >&6 else rm -rf conftestd1a conftestd1b conftestd2 mkdir conftestd1a conftestd1b conftestd2 cat < conftestd1a/conftest.h #define DEFINED_IN_CONFTESTD1 #include_next #ifdef DEFINED_IN_CONFTESTD2 int foo; #else #error "include_next doesn't work" #endif EOF cat < conftestd1b/conftest.h #define DEFINED_IN_CONFTESTD1 #include #include_next #ifdef DEFINED_IN_CONFTESTD2 int foo; #else #error "include_next doesn't work" #endif EOF cat < conftestd2/conftest.h #ifndef DEFINED_IN_CONFTESTD1 #error "include_next test doesn't work" #endif #define DEFINED_IN_CONFTESTD2 EOF gl_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1b -Iconftestd2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_compile "$LINENO"; then : gl_cv_have_include_next=yes else CPPFLAGS="$gl_save_CPPFLAGS -Iconftestd1a -Iconftestd2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_compile "$LINENO"; then : gl_cv_have_include_next=buggy else gl_cv_have_include_next=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CPPFLAGS="$gl_save_CPPFLAGS" rm -rf conftestd1a conftestd1b conftestd2 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_have_include_next" >&5 $as_echo "$gl_cv_have_include_next" >&6; } PRAGMA_SYSTEM_HEADER= if test $gl_cv_have_include_next = yes; then INCLUDE_NEXT=include_next INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next if test -n "$GCC"; then PRAGMA_SYSTEM_HEADER='#pragma GCC system_header' fi else if test $gl_cv_have_include_next = buggy; then INCLUDE_NEXT=include INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include_next else INCLUDE_NEXT=include INCLUDE_NEXT_AS_FIRST_DIRECTIVE=include fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether system header files limit the line length" >&5 $as_echo_n "checking whether system header files limit the line length... " >&6; } if ${gl_cv_pragma_columns+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __TANDEM choke me #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "choke me" >/dev/null 2>&1; then : gl_cv_pragma_columns=yes else gl_cv_pragma_columns=no fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_pragma_columns" >&5 $as_echo "$gl_cv_pragma_columns" >&6; } if test $gl_cv_pragma_columns = yes; then PRAGMA_COLUMNS="#pragma COLUMNS 10000" else PRAGMA_COLUMNS= fi for ac_header in $ac_header_list do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test $gl_cv_have_include_next = yes; then gl_cv_next_stdio_h='<'stdio.h'>' else { $as_echo "$as_me:${as_lineno-$LINENO}: checking absolute name of " >&5 $as_echo_n "checking absolute name of ... " >&6; } if ${gl_cv_next_stdio_h+:} false; then : $as_echo_n "(cached) " >&6 else if test $ac_cv_header_stdio_h = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF case "$host_os" in aix*) gl_absname_cpp="$ac_cpp -C" ;; *) gl_absname_cpp="$ac_cpp" ;; esac case "$host_os" in mingw*) gl_dirsep_regex='[/\\]' ;; *) gl_dirsep_regex='\/' ;; esac gl_make_literal_regex_sed='s,[]$^\\.*/[],\\&,g' gl_header_literal_regex=`echo 'stdio.h' \ | sed -e "$gl_make_literal_regex_sed"` gl_absolute_header_sed="/${gl_dirsep_regex}${gl_header_literal_regex}/"'{ s/.*"\(.*'"${gl_dirsep_regex}${gl_header_literal_regex}"'\)".*/\1/ s|^/[^/]|//&| p q }' gl_cv_absolute_stdio_h=`(eval "$gl_absname_cpp conftest.$ac_ext") 2>&5 | sed -n "$gl_absolute_header_sed"` gl_header=$gl_cv_absolute_stdio_h gl_cv_next_stdio_h='"'$gl_header'"' else gl_cv_next_stdio_h='<'stdio.h'>' fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_next_stdio_h" >&5 $as_echo "$gl_cv_next_stdio_h" >&6; } fi NEXT_STDIO_H=$gl_cv_next_stdio_h if test $gl_cv_have_include_next = yes || test $gl_cv_have_include_next = buggy; then # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include_next' gl_next_as_first_directive='<'stdio.h'>' else # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include' gl_next_as_first_directive=$gl_cv_next_stdio_h fi NEXT_AS_FIRST_DIRECTIVE_STDIO_H=$gl_next_as_first_directive if test $gl_cv_have_include_next = yes; then gl_cv_next_string_h='<'string.h'>' else { $as_echo "$as_me:${as_lineno-$LINENO}: checking absolute name of " >&5 $as_echo_n "checking absolute name of ... " >&6; } if ${gl_cv_next_string_h+:} false; then : $as_echo_n "(cached) " >&6 else if test $ac_cv_header_string_h = yes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF case "$host_os" in aix*) gl_absname_cpp="$ac_cpp -C" ;; *) gl_absname_cpp="$ac_cpp" ;; esac case "$host_os" in mingw*) gl_dirsep_regex='[/\\]' ;; *) gl_dirsep_regex='\/' ;; esac gl_make_literal_regex_sed='s,[]$^\\.*/[],\\&,g' gl_header_literal_regex=`echo 'string.h' \ | sed -e "$gl_make_literal_regex_sed"` gl_absolute_header_sed="/${gl_dirsep_regex}${gl_header_literal_regex}/"'{ s/.*"\(.*'"${gl_dirsep_regex}${gl_header_literal_regex}"'\)".*/\1/ s|^/[^/]|//&| p q }' gl_cv_absolute_string_h=`(eval "$gl_absname_cpp conftest.$ac_ext") 2>&5 | sed -n "$gl_absolute_header_sed"` gl_header=$gl_cv_absolute_string_h gl_cv_next_string_h='"'$gl_header'"' else gl_cv_next_string_h='<'string.h'>' fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_next_string_h" >&5 $as_echo "$gl_cv_next_string_h" >&6; } fi NEXT_STRING_H=$gl_cv_next_string_h if test $gl_cv_have_include_next = yes || test $gl_cv_have_include_next = buggy; then # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include_next' gl_next_as_first_directive='<'string.h'>' else # INCLUDE_NEXT_AS_FIRST_DIRECTIVE='include' gl_next_as_first_directive=$gl_cv_next_string_h fi NEXT_AS_FIRST_DIRECTIVE_STRING_H=$gl_next_as_first_directive ac_config_files="$ac_config_files lib/stdio.h lib/string.h ovsdb/libovsdb.sym ofproto/libofproto.sym lib/libsflow.sym lib/libopenvswitch.sym ovn/lib/libovn.sym vtep/libvtep.sym" WERROR= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Werror" >&5 $as_echo_n "checking whether $CC accepts -Werror... " >&6; } if ${ovs_cv__Werror+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Werror=no; else ovs_cv__Werror=yes; fi else ovs_cv__Werror=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Werror" >&5 $as_echo "$ovs_cv__Werror" >&6; } if test $ovs_cv__Werror = yes; then WERROR=-Werror else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wall" >&5 $as_echo_n "checking whether $CC accepts -Wall... " >&6; } if ${ovs_cv__Wall+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wall=no; else ovs_cv__Wall=yes; fi else ovs_cv__Wall=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wall" >&5 $as_echo "$ovs_cv__Wall" >&6; } if test $ovs_cv__Wall = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wall" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wextra" >&5 $as_echo_n "checking whether $CC accepts -Wextra... " >&6; } if ${ovs_cv__Wextra+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wextra" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wextra=no; else ovs_cv__Wextra=yes; fi else ovs_cv__Wextra=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wextra" >&5 $as_echo "$ovs_cv__Wextra" >&6; } if test $ovs_cv__Wextra = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wextra" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wno-sign-compare" >&5 $as_echo_n "checking whether $CC accepts -Wno-sign-compare... " >&6; } if ${ovs_cv__Wno_sign_compare+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wno-sign-compare" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wno_sign_compare=no; else ovs_cv__Wno_sign_compare=yes; fi else ovs_cv__Wno_sign_compare=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wno_sign_compare" >&5 $as_echo "$ovs_cv__Wno_sign_compare" >&6; } if test $ovs_cv__Wno_sign_compare = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wno-sign-compare" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wpointer-arith" >&5 $as_echo_n "checking whether $CC accepts -Wpointer-arith... " >&6; } if ${ovs_cv__Wpointer_arith+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wpointer-arith" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wpointer_arith=no; else ovs_cv__Wpointer_arith=yes; fi else ovs_cv__Wpointer_arith=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wpointer_arith" >&5 $as_echo "$ovs_cv__Wpointer_arith" >&6; } if test $ovs_cv__Wpointer_arith = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wpointer-arith" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wformat-security" >&5 $as_echo_n "checking whether $CC accepts -Wformat-security... " >&6; } if ${ovs_cv__Wformat_security+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wformat-security" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wformat_security=no; else ovs_cv__Wformat_security=yes; fi else ovs_cv__Wformat_security=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wformat_security" >&5 $as_echo "$ovs_cv__Wformat_security" >&6; } if test $ovs_cv__Wformat_security = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wformat-security" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wswitch-enum" >&5 $as_echo_n "checking whether $CC accepts -Wswitch-enum... " >&6; } if ${ovs_cv__Wswitch_enum+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wswitch-enum" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wswitch_enum=no; else ovs_cv__Wswitch_enum=yes; fi else ovs_cv__Wswitch_enum=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wswitch_enum" >&5 $as_echo "$ovs_cv__Wswitch_enum" >&6; } if test $ovs_cv__Wswitch_enum = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wswitch-enum" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wunused-parameter" >&5 $as_echo_n "checking whether $CC accepts -Wunused-parameter... " >&6; } if ${ovs_cv__Wunused_parameter+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wunused-parameter" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wunused_parameter=no; else ovs_cv__Wunused_parameter=yes; fi else ovs_cv__Wunused_parameter=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wunused_parameter" >&5 $as_echo "$ovs_cv__Wunused_parameter" >&6; } if test $ovs_cv__Wunused_parameter = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wunused-parameter" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wbad-function-cast" >&5 $as_echo_n "checking whether $CC accepts -Wbad-function-cast... " >&6; } if ${ovs_cv__Wbad_function_cast+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wbad-function-cast" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wbad_function_cast=no; else ovs_cv__Wbad_function_cast=yes; fi else ovs_cv__Wbad_function_cast=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wbad_function_cast" >&5 $as_echo "$ovs_cv__Wbad_function_cast" >&6; } if test $ovs_cv__Wbad_function_cast = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wbad-function-cast" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wcast-align" >&5 $as_echo_n "checking whether $CC accepts -Wcast-align... " >&6; } if ${ovs_cv__Wcast_align+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wcast-align" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wcast_align=no; else ovs_cv__Wcast_align=yes; fi else ovs_cv__Wcast_align=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wcast_align" >&5 $as_echo "$ovs_cv__Wcast_align" >&6; } if test $ovs_cv__Wcast_align = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wcast-align" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wstrict-prototypes" >&5 $as_echo_n "checking whether $CC accepts -Wstrict-prototypes... " >&6; } if ${ovs_cv__Wstrict_prototypes+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wstrict-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wstrict_prototypes=no; else ovs_cv__Wstrict_prototypes=yes; fi else ovs_cv__Wstrict_prototypes=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wstrict_prototypes" >&5 $as_echo "$ovs_cv__Wstrict_prototypes" >&6; } if test $ovs_cv__Wstrict_prototypes = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wstrict-prototypes" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wold-style-definition" >&5 $as_echo_n "checking whether $CC accepts -Wold-style-definition... " >&6; } if ${ovs_cv__Wold_style_definition+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wold-style-definition" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wold_style_definition=no; else ovs_cv__Wold_style_definition=yes; fi else ovs_cv__Wold_style_definition=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wold_style_definition" >&5 $as_echo "$ovs_cv__Wold_style_definition" >&6; } if test $ovs_cv__Wold_style_definition = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wold-style-definition" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wmissing-prototypes" >&5 $as_echo_n "checking whether $CC accepts -Wmissing-prototypes... " >&6; } if ${ovs_cv__Wmissing_prototypes+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wmissing-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wmissing_prototypes=no; else ovs_cv__Wmissing_prototypes=yes; fi else ovs_cv__Wmissing_prototypes=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wmissing_prototypes" >&5 $as_echo "$ovs_cv__Wmissing_prototypes" >&6; } if test $ovs_cv__Wmissing_prototypes = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wmissing-prototypes" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wmissing-field-initializers" >&5 $as_echo_n "checking whether $CC accepts -Wmissing-field-initializers... " >&6; } if ${ovs_cv__Wmissing_field_initializers+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wmissing-field-initializers" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wmissing_field_initializers=no; else ovs_cv__Wmissing_field_initializers=yes; fi else ovs_cv__Wmissing_field_initializers=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wmissing_field_initializers" >&5 $as_echo "$ovs_cv__Wmissing_field_initializers" >&6; } if test $ovs_cv__Wmissing_field_initializers = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wmissing-field-initializers" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wthread-safety" >&5 $as_echo_n "checking whether $CC accepts -Wthread-safety... " >&6; } if ${ovs_cv__Wthread_safety+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wthread-safety" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wthread_safety=no; else ovs_cv__Wthread_safety=yes; fi else ovs_cv__Wthread_safety=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wthread_safety" >&5 $as_echo "$ovs_cv__Wthread_safety" >&6; } if test $ovs_cv__Wthread_safety = yes; then WARNING_FLAGS="$WARNING_FLAGS -Wthread-safety" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -fno-strict-aliasing" >&5 $as_echo_n "checking whether $CC accepts -fno-strict-aliasing... " >&6; } if ${ovs_cv__fno_strict_aliasing+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -fno-strict-aliasing" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__fno_strict_aliasing=no; else ovs_cv__fno_strict_aliasing=yes; fi else ovs_cv__fno_strict_aliasing=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__fno_strict_aliasing" >&5 $as_echo "$ovs_cv__fno_strict_aliasing" >&6; } if test $ovs_cv__fno_strict_aliasing = yes; then WARNING_FLAGS="$WARNING_FLAGS -fno-strict-aliasing" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Qunused-arguments" >&5 $as_echo_n "checking whether $CC accepts -Qunused-arguments... " >&6; } if ${ovs_cv__Qunused_arguments+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Qunused-arguments" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Qunused_arguments=no; else ovs_cv__Qunused_arguments=yes; fi else ovs_cv__Qunused_arguments=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Qunused_arguments" >&5 $as_echo "$ovs_cv__Qunused_arguments" >&6; } if test $ovs_cv__Qunused_arguments = yes; then WARNING_FLAGS="$WARNING_FLAGS -Qunused-arguments" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wno-unused" >&5 $as_echo_n "checking whether $CC accepts -Wno-unused... " >&6; } if ${ovs_cv__Wno_unused+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wno-unused" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wno_unused=no; else ovs_cv__Wno_unused=yes; fi else ovs_cv__Wno_unused=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wno_unused" >&5 $as_echo "$ovs_cv__Wno_unused" >&6; } if test $ovs_cv__Wno_unused = yes; then ovs_have_cc_option=yes else ovs_have_cc_option=no fi if test $ovs_have_cc_option = yes; then HAVE_WNO_UNUSED_TRUE= HAVE_WNO_UNUSED_FALSE='#' else HAVE_WNO_UNUSED_TRUE='#' HAVE_WNO_UNUSED_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -Wno-unused-parameter" >&5 $as_echo_n "checking whether $CC accepts -Wno-unused-parameter... " >&6; } if ${ovs_cv__Wno_unused_parameter+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -Wno-unused-parameter" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__Wno_unused_parameter=no; else ovs_cv__Wno_unused_parameter=yes; fi else ovs_cv__Wno_unused_parameter=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__Wno_unused_parameter" >&5 $as_echo "$ovs_cv__Wno_unused_parameter" >&6; } if test $ovs_cv__Wno_unused_parameter = yes; then ovs_have_cc_option=yes else ovs_have_cc_option=no fi if test $ovs_have_cc_option = yes; then HAVE_WNO_UNUSED_PARAMETER_TRUE= HAVE_WNO_UNUSED_PARAMETER_FALSE='#' else HAVE_WNO_UNUSED_PARAMETER_TRUE='#' HAVE_WNO_UNUSED_PARAMETER_FALSE= fi # Check whether --enable-Werror was given. if test "${enable_Werror+set}" = set; then : enableval=$enable_Werror; else enable_Werror=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking target hint for cgcc" >&5 $as_echo_n "checking target hint for cgcc... " >&6; } if ${ac_cv_sparse_target+:} false; then : $as_echo_n "(cached) " >&6 else case `$CC -dumpmachine 2>/dev/null` in #( i?86-* | athlon-*) : ac_cv_sparse_target=x86 ;; #( x86_64-*) : ac_cv_sparse_target=x86_64 ;; #( *) : ac_cv_sparse_target=other ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sparse_target" >&5 $as_echo "$ac_cv_sparse_target" >&6; } case $ac_cv_sparse_target in #( x86) : SPARSEFLAGS= CGCCFLAGS=-target=i86 ;; #( x86_64) : SPARSEFLAGS=-m64 CGCCFLAGS=-target=x86_64 ;; #( *) : SPARSEFLAGS= CGCCFLAGS= ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} has GNU make \$(if) extension" >&5 $as_echo_n "checking whether ${MAKE-make} has GNU make \$(if) extension... " >&6; } if ${ovs_cv_gnu_make_if+:} false; then : $as_echo_n "(cached) " >&6 else cat <<'EOF' > conftest.mk conftest.out: echo $(if x,y,z) > conftest.out .PHONY: all EOF rm -f conftest.out $as_echo "$as_me:$LINENO: invoking ${MAKE-make} -f conftest.mk all:" >&5 2>&1 ${MAKE-make} -f conftest.mk conftest.out >&5 2>&1 $as_echo "$as_me:$LINENO: conftest.out contains:" >&5 2>&1 cat conftest.out >&5 2>&1 result=`cat conftest.out` rm -f conftest.mk conftest.out if test "X$result" = "Xy"; then ovs_cv_gnu_make_if=yes else ovs_cv_gnu_make_if=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv_gnu_make_if" >&5 $as_echo "$ovs_cv_gnu_make_if" >&6; } SPARSE_EXTRA_INCLUDES=`$CC -v -E - &1 >/dev/null | sed -n -e '/^#include.*search.*starts.*here:/,/^End.*of.*search.*list\./s/^ \(.*\)/-I \1/p' |grep -v /usr/lib | grep -x -v '\-I /usr/include' | tr \\\n ' ' ` : ${SPARSE=sparse} # Check whether --with-linux was given. if test "${with_linux+set}" = set; then : withval=$with_linux; fi # Check whether --with-linux-source was given. if test "${with_linux_source+set}" = set; then : withval=$with_linux_source; fi # Deprecated equivalents to --with-linux, --with-linux-source. # Check whether --with-l26 was given. if test "${with_l26+set}" = set; then : withval=$with_l26; fi # Check whether --with-l26-source was given. if test "${with_l26_source+set}" = set; then : withval=$with_l26_source; fi if test X"$with_linux" != X; then KBUILD=$with_linux elif test X"$with_l26" != X; then KBUILD=$with_l26 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-l26 is deprecated, please use --with-linux instead" >&5 $as_echo "$as_me: WARNING: --with-l26 is deprecated, please use --with-linux instead" >&2;} else KBUILD= fi if test X"$KBUILD" != X; then if test X"$with_linux_source" != X; then KSRC=$with_linux_source elif test X"$with_l26_source" != X; then KSRC=$with_l26_source { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-l26-source is deprecated, please use --with-linux-source instead" >&5 $as_echo "$as_me: WARNING: --with-l26-source is deprecated, please use --with-linux-source instead" >&2;} else KSRC= fi elif test X"$with_linux_source" != X || test X"$with_l26_source" != X; then as_fn_error $? "Linux source directory may not be specified without Linux build directory" "$LINENO" 5 fi if test -n "$KBUILD"; then KBUILD=`eval echo "$KBUILD"` case $KBUILD in /*) ;; *) KBUILD=`pwd`/$KBUILD ;; esac # The build directory is what the user provided. # Make sure that it exists. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux build directory" >&5 $as_echo_n "checking for Linux build directory... " >&6; } if test -d "$KBUILD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $KBUILD" >&5 $as_echo "$KBUILD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } as_fn_error $? "source dir $KBUILD doesn't exist" "$LINENO" 5 fi # Debian breaks kernel headers into "source" header and "build" headers. # We want the source headers, but $KBUILD gives us the "build" headers. # Use heuristics to find the source headers. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux source directory" >&5 $as_echo_n "checking for Linux source directory... " >&6; } if test -n "$KSRC"; then KSRC=`eval echo "$KSRC"` case $KSRC in /*) ;; *) KSRC=`pwd`/$KSRC ;; esac if test ! -e $KSRC/include/linux/kernel.h; then as_fn_error $? "$KSRC is not a kernel source directory" "$LINENO" 5 fi else KSRC=$KBUILD if test ! -e $KSRC/include/linux/kernel.h; then # Debian kernel build Makefiles tend to include a line of the form: # MAKEARGS := -C /usr/src/linux-headers-3.2.0-1-common O=/usr/src/linux-headers-3.2.0-1-486 # First try to extract the source directory from this line. KSRC=`sed -n 's/.*-C \([^ ]*\).*/\1/p' "$KBUILD"/Makefile` if test ! -e "$KSRC"/include/linux/kernel.h; then # Didn't work. Fall back to name-based heuristics that used to work. case `echo "$KBUILD" | sed 's,/*$,,'` in # ( */build) KSRC=`echo "$KBUILD" | sed 's,/build/*$,/source,'` ;; # ( *) KSRC=`(cd $KBUILD && pwd -P) | sed 's,-[^-]*$,-common,'` ;; esac fi fi if test ! -e "$KSRC"/include/linux/kernel.h; then as_fn_error $? "cannot find source directory (please use --with-linux-source)" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $KSRC" >&5 $as_echo "$KSRC" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for kernel version" >&5 $as_echo_n "checking for kernel version... " >&6; } version=`sed -n 's/^VERSION = //p' "$KSRC/Makefile"` patchlevel=`sed -n 's/^PATCHLEVEL = //p' "$KSRC/Makefile"` sublevel=`sed -n 's/^SUBLEVEL = //p' "$KSRC/Makefile"` if test X"$version" = X || test X"$patchlevel" = X; then as_fn_error $? "cannot determine kernel version" "$LINENO" 5 elif test X"$sublevel" = X; then kversion=$version.$patchlevel else kversion=$version.$patchlevel.$sublevel fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $kversion" >&5 $as_echo "$kversion" >&6; } if test "$version" -ge 4; then if test "$version" = 4 && test "$patchlevel" -le 3; then : # Linux 4.x else as_fn_error $? "Linux kernel in $KBUILD is version $kversion, but version newer than 4.3.x is not supported (please refer to the FAQ for advice)" "$LINENO" 5 fi elif test "$version" = 3; then : # Linux 3.x else if test "$version" -le 1 || test "$patchlevel" -le 5 || test "$sublevel" -le 31; then as_fn_error $? "Linux kernel in $KBUILD is version $kversion, but version 2.6.32 or later is required" "$LINENO" 5 else : # Linux 2.6.x fi fi if (test ! -e "$KBUILD"/include/linux/version.h && \ test ! -e "$KBUILD"/include/generated/uapi/linux/version.h)|| \ (test ! -e "$KBUILD"/include/linux/autoconf.h && \ test ! -e "$KBUILD"/include/generated/autoconf.h); then as_fn_error $? "Linux kernel source in $KBUILD is not configured" "$LINENO" 5 fi rm -f datapath/linux/kcompat.h.new mkdir -p datapath/linux : > datapath/linux/kcompat.h.new echo '#include #ifndef RHEL_RELEASE_CODE #define RHEL_RELEASE_CODE 0 #define RHEL_RELEASE_VERSION(a, b) 0 #endif' >> datapath/linux/kcompat.h.new { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether src_err, matches in $KSRC/arch/x86/include/asm/checksum_32.h" >&5 $as_echo_n "checking whether src_err, matches in $KSRC/arch/x86/include/asm/checksum_32.h... " >&6; } if test -f $KSRC/arch/x86/include/asm/checksum_32.h; then grep 'src_err,' $KSRC/arch/x86/include/asm/checksum_32.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CSUM_COPY_DBG 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ipv6_dst_lookup.*net matches in $KSRC/include/net/addrconf.h" >&5 $as_echo_n "checking whether ipv6_dst_lookup.*net matches in $KSRC/include/net/addrconf.h... " >&6; } if test -f $KSRC/include/net/addrconf.h; then grep 'ipv6_dst_lookup.*net' $KSRC/include/net/addrconf.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IPV6_DST_LOOKUP_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ipv6_stub matches in $KSRC/include/net/addrconf.h" >&5 $as_echo_n "checking whether ipv6_stub matches in $KSRC/include/net/addrconf.h... " >&6; } if test -f $KSRC/include/net/addrconf.h; then grep 'ipv6_stub' $KSRC/include/net/addrconf.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IPV6_STUB 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ERR_CAST matches in $KSRC/include/linux/err.h" >&5 $as_echo_n "checking whether ERR_CAST matches in $KSRC/include/linux/err.h... " >&6; } if test -f $KSRC/include/linux/err.h; then grep 'ERR_CAST' $KSRC/include/linux/err.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_ERR_CAST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether IS_ERR_OR_NULL matches in $KSRC/include/linux/err.h" >&5 $as_echo_n "checking whether IS_ERR_OR_NULL matches in $KSRC/include/linux/err.h... " >&6; } if test -f $KSRC/include/linux/err.h; then grep 'IS_ERR_OR_NULL' $KSRC/include/linux/err.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IS_ERR_OR_NULL 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether eth_hw_addr_random matches in $KSRC/include/linux/etherdevice.h" >&5 $as_echo_n "checking whether eth_hw_addr_random matches in $KSRC/include/linux/etherdevice.h... " >&6; } if test -f $KSRC/include/linux/etherdevice.h; then grep 'eth_hw_addr_random' $KSRC/include/linux/etherdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_ETH_HW_ADDR_RANDOM 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ether_addr_copy matches in $KSRC/include/linux/etherdevice.h" >&5 $as_echo_n "checking whether ether_addr_copy matches in $KSRC/include/linux/etherdevice.h... " >&6; } if test -f $KSRC/include/linux/etherdevice.h; then grep 'ether_addr_copy' $KSRC/include/linux/etherdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_ETHER_ADDR_COPY 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether IFLA_GENEVE_TOS matches in $KSRC/include/uapi/linux/if_link.h" >&5 $as_echo_n "checking whether IFLA_GENEVE_TOS matches in $KSRC/include/uapi/linux/if_link.h... " >&6; } if test -f $KSRC/include/uapi/linux/if_link.h; then grep 'IFLA_GENEVE_TOS' $KSRC/include/uapi/linux/if_link.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IFLA_GENEVE_TOS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rtnl_link_stats64 matches in $KSRC/include/uapi/linux/if_link.h" >&5 $as_echo_n "checking whether rtnl_link_stats64 matches in $KSRC/include/uapi/linux/if_link.h... " >&6; } if test -f $KSRC/include/uapi/linux/if_link.h; then grep 'rtnl_link_stats64' $KSRC/include/uapi/linux/if_link.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RTNL_LINK_STATS64 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rtnl_link_stats64 matches in $KSRC/include/linux/if_link.h" >&5 $as_echo_n "checking whether rtnl_link_stats64 matches in $KSRC/include/linux/if_link.h... " >&6; } if test -f $KSRC/include/linux/if_link.h; then grep 'rtnl_link_stats64' $KSRC/include/linux/if_link.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RTNL_LINK_STATS64 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vlan_set_encap_proto matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether vlan_set_encap_proto matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep 'vlan_set_encap_proto' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VLAN_SET_ENCAP_PROTO 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vlan_hwaccel_push_inside matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether vlan_hwaccel_push_inside matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep 'vlan_hwaccel_push_inside' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VLAN_HWACCEL_PUSH_INSIDE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ipv4_is_multicast matches in $KSRC/include/linux/in.h" >&5 $as_echo_n "checking whether ipv4_is_multicast matches in $KSRC/include/linux/in.h... " >&6; } if test -f $KSRC/include/linux/in.h; then grep 'ipv4_is_multicast' $KSRC/include/linux/in.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IPV4_IS_MULTICAST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether proto_ports_offset matches in $KSRC/include/linux/in.h" >&5 $as_echo_n "checking whether proto_ports_offset matches in $KSRC/include/linux/in.h... " >&6; } if test -f $KSRC/include/linux/in.h; then grep 'proto_ports_offset' $KSRC/include/linux/in.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PROTO_PORTS_OFFSET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __ip_select_ident.*dst_entry matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether __ip_select_ident.*dst_entry matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep '__ip_select_ident.*dst_entry' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_SELECT_IDENT_USING_DST_ENTRY 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __ip_select_ident.*net matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether __ip_select_ident.*net matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep '__ip_select_ident.*net' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_SELECT_IDENT_USING_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_get_local_port_range.*net matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether inet_get_local_port_range.*net matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep 'inet_get_local_port_range.*net' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ip_is_fragment matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether ip_is_fragment matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep 'ip_is_fragment' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_IS_FRAGMENT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ip_do_fragment.*net matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether ip_do_fragment.*net matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep 'ip_do_fragment.*net' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_DO_FRAGMENT_TAKES_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ip_skb_dst_mtu matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether ip_skb_dst_mtu matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep 'ip_skb_dst_mtu' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_SKB_DST_MTU 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether IPSKB_FRAG_PMTU matches in $KSRC/include/net/ip.h" >&5 $as_echo_n "checking whether IPSKB_FRAG_PMTU matches in $KSRC/include/net/ip.h... " >&6; } if test -f $KSRC/include/net/ip.h; then grep 'IPSKB_FRAG_PMTU' $KSRC/include/net/ip.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CORRECT_MRU_HANDLING 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __ip_tunnel_change_mtu matches in $KSRC/include/net/ip_tunnels.h" >&5 $as_echo_n "checking whether __ip_tunnel_change_mtu matches in $KSRC/include/net/ip_tunnels.h... " >&6; } if test -f $KSRC/include/net/ip_tunnels.h; then grep '__ip_tunnel_change_mtu' $KSRC/include/net/ip_tunnels.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___IP_TUNNEL_CHANGE_MTU 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether hashfn.*const matches in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether hashfn.*const matches in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then grep 'hashfn.*const' $KSRC/include/net/inet_frag.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAGS_CONST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether last_in matches in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether last_in matches in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then grep 'last_in' $KSRC/include/net/inet_frag.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAGS_LAST_IN 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_frag_evicting matches in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether inet_frag_evicting matches in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then grep 'inet_frag_evicting' $KSRC/include/net/inet_frag.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAG_EVICTING 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_frags has member frags_work in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether inet_frags has member frags_work in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then awk '/inet_frags.{/,/^}/' $KSRC/include/net/inet_frag.h 2>/dev/null | grep 'frags_work' >/dev/null status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAGS_WITH_FRAGS_WORK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_frags has member rwlock in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether inet_frags has member rwlock in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then awk '/inet_frags.{/,/^}/' $KSRC/include/net/inet_frag.h 2>/dev/null | grep 'rwlock' >/dev/null status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAGS_WITH_RWLOCK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_frag_queue has member list_evictor in $KSRC/include/net/inet_frag.h" >&5 $as_echo_n "checking whether inet_frag_queue has member list_evictor in $KSRC/include/net/inet_frag.h... " >&6; } if test -f $KSRC/include/net/inet_frag.h; then awk '/inet_frag_queue.{/,/^}/' $KSRC/include/net/inet_frag.h 2>/dev/null | grep 'list_evictor' >/dev/null status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INET_FRAG_QUEUE_WITH_LIST_EVICTOR 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vif matches in $KSRC/include/net/inetpeer.h" >&5 $as_echo_n "checking whether vif matches in $KSRC/include/net/inetpeer.h... " >&6; } if test -f $KSRC/include/net/inetpeer.h; then grep 'vif' $KSRC/include/net/inetpeer.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INETPEER_VIF_SUPPORT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether metadata_dst matches in $KSRC/include/net/dst_metadata.h" >&5 $as_echo_n "checking whether metadata_dst matches in $KSRC/include/net/dst_metadata.h... " >&6; } if test -f $KSRC/include/net/dst_metadata.h; then grep 'metadata_dst' $KSRC/include/net/dst_metadata.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_METADATA_DST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sock_create_kern.*net matches in $KSRC/include/linux/net.h" >&5 $as_echo_n "checking whether sock_create_kern.*net matches in $KSRC/include/linux/net.h... " >&6; } if test -f $KSRC/include/linux/net.h; then grep 'sock_create_kern.*net' $KSRC/include/linux/net.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SOCK_CREATE_KERN_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_disable_lro matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether dev_disable_lro matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'dev_disable_lro' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DEV_DISABLE_LRO 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_get_stats matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether dev_get_stats matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'dev_get_stats' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DEV_GET_STATS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_get_stats64 matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether dev_get_stats64 matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'dev_get_stats64' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DEV_GET_STATS64 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_get_by_index_rcu matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether dev_get_by_index_rcu matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'dev_get_by_index_rcu' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DEV_GET_BY_INDEX_RCU 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dev_recursion_level matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether dev_recursion_level matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'dev_recursion_level' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DEV_RECURSION_LEVEL 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __skb_gso_segment matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether __skb_gso_segment matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep '__skb_gso_segment' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___SKB_GSO_SEGMENT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether can_checksum_protocol matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether can_checksum_protocol matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'can_checksum_protocol' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CAN_CHECKSUM_PROTOCOL 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ndo_get_iflink matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether ndo_get_iflink matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'ndo_get_iflink' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NDO_GET_IFLINK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether netdev_features_t matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether netdev_features_t matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'netdev_features_t' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NETDEV_FEATURES_T 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pcpu_sw_netstats matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether pcpu_sw_netstats matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'pcpu_sw_netstats' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PCPU_SW_NETSTATS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether netdev_rx_handler_register matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether netdev_rx_handler_register matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'netdev_rx_handler_register' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NETDEV_RX_HANDLER_REGISTER 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether net_device_extended matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether net_device_extended matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'net_device_extended' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NET_DEVICE_EXTENDED 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rx_handler_func_t.*pskb matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether rx_handler_func_t.*pskb matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'rx_handler_func_t.*pskb' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RX_HANDLER_PSKB 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether netif_needs_gso.*net_device matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether netif_needs_gso.*net_device matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'netif_needs_gso.*net_device' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NETIF_NEEDS_GSO_NETDEV 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether udp_offload matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether udp_offload matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'udp_offload' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_OFFLOAD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether udp_offload.*uoff matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether udp_offload.*uoff matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'udp_offload.*uoff' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_OFFLOAD_ARG_UOFF 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether gro_remcsum matches in $KSRC/include/linux/netdevice.h" >&5 $as_echo_n "checking whether gro_remcsum matches in $KSRC/include/linux/netdevice.h... " >&6; } if test -f $KSRC/include/linux/netdevice.h; then grep 'gro_remcsum' $KSRC/include/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GRO_REMCSUM 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_hook_state matches in $KSRC/include/linux/netfilter.h" >&5 $as_echo_n "checking whether nf_hook_state matches in $KSRC/include/linux/netfilter.h... " >&6; } if test -f $KSRC/include/linux/netfilter.h; then grep 'nf_hook_state' $KSRC/include/linux/netfilter.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_HOOK_STATE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_register_net_hook matches in $KSRC/include/linux/netfilter.h" >&5 $as_echo_n "checking whether nf_register_net_hook matches in $KSRC/include/linux/netfilter.h... " >&6; } if test -f $KSRC/include/linux/netfilter.h; then grep 'nf_register_net_hook' $KSRC/include/linux/netfilter.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_REGISTER_NET_HOOK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_hookfn.*nf_hook_ops matches in $KSRC/include/linux/netfilter.h" >&5 $as_echo_n "checking whether nf_hookfn.*nf_hook_ops matches in $KSRC/include/linux/netfilter.h... " >&6; } if test -f $KSRC/include/linux/netfilter.h; then grep 'nf_hookfn.*nf_hook_ops' $KSRC/include/linux/netfilter.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_HOOKFN_ARG_OPS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_ipv6_ops has member fragment.*sock in $KSRC/include/linux/netfilter_ipv6.h" >&5 $as_echo_n "checking whether nf_ipv6_ops has member fragment.*sock in $KSRC/include/linux/netfilter_ipv6.h... " >&6; } if test -f $KSRC/include/linux/netfilter_ipv6.h; then awk '/nf_ipv6_ops.{/,/^}/' $KSRC/include/linux/netfilter_ipv6.h 2>/dev/null | grep 'fragment.*sock' >/dev/null status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_IPV6_OPS_FRAGMENT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tmpl_alloc.*conntrack_zone matches in $KSRC/include/net/netfilter/nf_conntrack.h" >&5 $as_echo_n "checking whether tmpl_alloc.*conntrack_zone matches in $KSRC/include/net/netfilter/nf_conntrack.h... " >&6; } if test -f $KSRC/include/net/netfilter/nf_conntrack.h; then grep 'tmpl_alloc.*conntrack_zone' $KSRC/include/net/netfilter/nf_conntrack.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_CT_TMPL_ALLOC_TAKES_STRUCT_ZONE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_ct_zone_init matches in $KSRC/include/net/netfilter/nf_conntrack_zones.h" >&5 $as_echo_n "checking whether nf_ct_zone_init matches in $KSRC/include/net/netfilter/nf_conntrack_zones.h... " >&6; } if test -f $KSRC/include/net/netfilter/nf_conntrack_zones.h; then grep 'nf_ct_zone_init' $KSRC/include/net/netfilter/nf_conntrack_zones.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_CT_ZONE_INIT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_connlabels_get matches in $KSRC/include/net/netfilter/nf_conntrack_labels.h" >&5 $as_echo_n "checking whether nf_connlabels_get matches in $KSRC/include/net/netfilter/nf_conntrack_labels.h... " >&6; } if test -f $KSRC/include/net/netfilter/nf_conntrack_labels.h; then grep 'nf_connlabels_get' $KSRC/include/net/netfilter/nf_conntrack_labels.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_CONNLABELS_GET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_ct_frag6_consume_orig matches in $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h" >&5 $as_echo_n "checking whether nf_ct_frag6_consume_orig matches in $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h... " >&6; } if test -f $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h; then grep 'nf_ct_frag6_consume_orig' $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_CT_FRAG6_CONSUME_ORIG 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nf_ct_frag6_output matches in $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h" >&5 $as_echo_n "checking whether nf_ct_frag6_output matches in $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h... " >&6; } if test -f $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h; then grep 'nf_ct_frag6_output' $KSRC/include/net/netfilter/ipv6/nf_defrag_ipv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NF_CT_FRAG6_OUTPUT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether prandom_u32 matches in $KSRC/include/linux/random.h" >&5 $as_echo_n "checking whether prandom_u32 matches in $KSRC/include/linux/random.h... " >&6; } if test -f $KSRC/include/linux/random.h; then grep 'prandom_u32' $KSRC/include/linux/random.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PRANDOM_U32 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether prandom_u32_max matches in $KSRC/include/linux/random.h" >&5 $as_echo_n "checking whether prandom_u32_max matches in $KSRC/include/linux/random.h... " >&6; } if test -f $KSRC/include/linux/random.h; then grep 'prandom_u32_max' $KSRC/include/linux/random.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PRANDOM_U32_MAX 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether get_link_net matches in $KSRC/include/net/rtnetlink.h" >&5 $as_echo_n "checking whether get_link_net matches in $KSRC/include/net/rtnetlink.h... " >&6; } if test -f $KSRC/include/net/rtnetlink.h; then grep 'get_link_net' $KSRC/include/net/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GET_LINK_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether name_assign_type matches in $KSRC/include/net/rtnetlink.h" >&5 $as_echo_n "checking whether name_assign_type matches in $KSRC/include/net/rtnetlink.h... " >&6; } if test -f $KSRC/include/net/rtnetlink.h; then grep 'name_assign_type' $KSRC/include/net/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NAME_ASSIGN_TYPE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rtnl_create_link.*src_net matches in $KSRC/include/net/rtnetlink.h" >&5 $as_echo_n "checking whether rtnl_create_link.*src_net matches in $KSRC/include/net/rtnetlink.h... " >&6; } if test -f $KSRC/include/net/rtnetlink.h; then grep 'rtnl_create_link.*src_net' $KSRC/include/net/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RTNL_CREATE_LINK_SRC_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether possible_net_t matches in $KSRC/include/net/net_namespace.h" >&5 $as_echo_n "checking whether possible_net_t matches in $KSRC/include/net/net_namespace.h... " >&6; } if test -f $KSRC/include/net/net_namespace.h; then grep 'possible_net_t' $KSRC/include/net/net_namespace.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_POSSIBLE_NET_T 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rcu_read_lock_held matches in $KSRC/include/linux/rcupdate.h" >&5 $as_echo_n "checking whether rcu_read_lock_held matches in $KSRC/include/linux/rcupdate.h... " >&6; } if test -f $KSRC/include/linux/rcupdate.h; then grep 'rcu_read_lock_held' $KSRC/include/linux/rcupdate.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RCU_READ_LOCK_HELD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rcu_read_lock_held matches in $KSRC/include/linux/rtnetlink.h" >&5 $as_echo_n "checking whether rcu_read_lock_held matches in $KSRC/include/linux/rtnetlink.h... " >&6; } if test -f $KSRC/include/linux/rtnetlink.h; then grep 'rcu_read_lock_held' $KSRC/include/linux/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RCU_READ_LOCK_HELD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rcu_read_lock_held matches in $KSRC/include/linux/rtnetlink.h" >&5 $as_echo_n "checking whether rcu_read_lock_held matches in $KSRC/include/linux/rtnetlink.h... " >&6; } if test -f $KSRC/include/linux/rtnetlink.h; then grep 'rcu_read_lock_held' $KSRC/include/linux/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RCU_READ_LOCK_HELD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lockdep_rtnl_is_held matches in $KSRC/include/linux/rtnetlink.h" >&5 $as_echo_n "checking whether lockdep_rtnl_is_held matches in $KSRC/include/linux/rtnetlink.h... " >&6; } if test -f $KSRC/include/linux/rtnetlink.h; then grep 'lockdep_rtnl_is_held' $KSRC/include/linux/rtnetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_LOCKDEP_RTNL_IS_HELD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi # Check for the proto_data_valid member in struct sk_buff. The [^@] # is necessary because some versions of this header remove the # member but retain the kerneldoc comment that describes it (which # starts with @). The brackets must be doubled because of m4 # quoting rules. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether [^@]proto_data_valid matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether [^@]proto_data_valid matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep '[^@]proto_data_valid' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PROTO_DATA_VALID 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_checksum_start_offset matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_checksum_start_offset matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_checksum_start_offset' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_CHECKSUM_START_OFFSET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inner_protocol matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether inner_protocol matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'inner_protocol' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INNER_PROTOCOL 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inner_mac_header matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether inner_mac_header matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'inner_mac_header' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INNER_MAC_HEADER 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inner_network_header matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether inner_network_header matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'inner_network_header' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_INNER_NETWORK_HEADER 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether kfree_skb_list matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether kfree_skb_list matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'kfree_skb_list' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_KFREE_SKB_LIST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rxhash matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether rxhash matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'rxhash' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RXHASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether u16.*rxhash matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether u16.*rxhash matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'u16.*rxhash' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_U16_RXHASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_dst( matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_dst( matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_dst(' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_DST_ACCESSOR_FUNCS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_copy_from_linear_data_offset matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_copy_from_linear_data_offset matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_copy_from_linear_data_offset' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_COPY_FROM_LINEAR_DATA_OFFSET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_reset_tail_pointer matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_reset_tail_pointer matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_reset_tail_pointer' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_RESET_TAIL_POINTER 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_cow_head matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_cow_head matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_cow_head' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_COW_HEAD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_transport_header matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_transport_header matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_transport_header' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKBUFF_HEADER_HELPERS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether icmp6_hdr matches in $KSRC/include/linux/icmpv6.h" >&5 $as_echo_n "checking whether icmp6_hdr matches in $KSRC/include/linux/icmpv6.h... " >&6; } if test -f $KSRC/include/linux/icmpv6.h; then grep 'icmp6_hdr' $KSRC/include/linux/icmpv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_ICMP6_HDR 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_warn_if_lro matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_warn_if_lro matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_warn_if_lro' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_WARN_LRO 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether consume_skb matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether consume_skb matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'consume_skb' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CONSUME_SKB 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_frag_page matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_frag_page matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_frag_page' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_FRAG_PAGE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_has_frag_list matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_has_frag_list matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_has_frag_list' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_HAS_FRAG_LIST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __skb_fill_page_desc matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether __skb_fill_page_desc matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep '__skb_fill_page_desc' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___SKB_FILL_PAGE_DESC 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_reset_mac_len matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_reset_mac_len matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_reset_mac_len' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_RESET_MAC_LEN 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_unclone matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_unclone matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_unclone' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_UNCLONE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_orphan_frags matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_orphan_frags matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_orphan_frags' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_ORPHAN_FRAGS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_get_hash( matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_get_hash( matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_get_hash(' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_GET_HASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_clear_hash matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_clear_hash matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_clear_hash' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_CLEAR_HASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether int.skb_zerocopy( matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether int.skb_zerocopy( matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'int.skb_zerocopy(' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_ZEROCOPY 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether u8.*l4_rxhash matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether u8.*l4_rxhash matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'u8.*l4_rxhash' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_L4_RXHASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_ensure_writable matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_ensure_writable matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_ensure_writable' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_ENSURE_WRITABLE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_vlan_pop matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_vlan_pop matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_vlan_pop' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_VLAN_POP 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether skb_vlan_push matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether skb_vlan_push matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'skb_vlan_push' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SKB_VLAN_PUSH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether bool matches in $KSRC/include/linux/types.h" >&5 $as_echo_n "checking whether bool matches in $KSRC/include/linux/types.h... " >&6; } if test -f $KSRC/include/linux/types.h; then grep 'bool' $KSRC/include/linux/types.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_BOOL_TYPE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __wsum matches in $KSRC/include/linux/types.h" >&5 $as_echo_n "checking whether __wsum matches in $KSRC/include/linux/types.h... " >&6; } if test -f $KSRC/include/linux/types.h; then grep '__wsum' $KSRC/include/linux/types.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CSUM_TYPES 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __wsum matches in $KSRC/include/uapi/linux/types.h" >&5 $as_echo_n "checking whether __wsum matches in $KSRC/include/uapi/linux/types.h... " >&6; } if test -f $KSRC/include/uapi/linux/types.h; then grep '__wsum' $KSRC/include/uapi/linux/types.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CSUM_TYPES 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether csum_replace4 matches in $KSRC/include/net/checksum.h" >&5 $as_echo_n "checking whether csum_replace4 matches in $KSRC/include/net/checksum.h... " >&6; } if test -f $KSRC/include/net/checksum.h; then grep 'csum_replace4' $KSRC/include/net/checksum.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CSUM_REPLACE4 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether csum_unfold matches in $KSRC/include/net/checksum.h" >&5 $as_echo_n "checking whether csum_unfold matches in $KSRC/include/net/checksum.h... " >&6; } if test -f $KSRC/include/net/checksum.h; then grep 'csum_unfold' $KSRC/include/net/checksum.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_CSUM_UNFOLD 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dst_discard_sk matches in $KSRC/include/net/dst.h" >&5 $as_echo_n "checking whether dst_discard_sk matches in $KSRC/include/net/dst.h... " >&6; } if test -f $KSRC/include/net/dst.h; then grep 'dst_discard_sk' $KSRC/include/net/dst.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_DST_DISCARD_SK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __skb_dst_copy matches in $KSRC/include/net/dst.h" >&5 $as_echo_n "checking whether __skb_dst_copy matches in $KSRC/include/net/dst.h... " >&6; } if test -f $KSRC/include/net/dst.h; then grep '__skb_dst_copy' $KSRC/include/net/dst.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___SKB_DST_COPY 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether genl_has_listeners matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether genl_has_listeners matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'genl_has_listeners' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENL_HAS_LISTENERS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mcgrp_offset matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether mcgrp_offset matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'mcgrp_offset' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_MCGRP_OFFSET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether parallel_ops matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether parallel_ops matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'parallel_ops' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_PARALLEL_OPS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether genlmsg_new_unicast matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether genlmsg_new_unicast matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'genlmsg_new_unicast' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENLMSG_NEW_UNICAST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether netlink_has_listeners(net->genl_sock matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether netlink_has_listeners(net->genl_sock matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'netlink_has_listeners(net->genl_sock' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENL_HAS_LISTENERS_TAKES_NET 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether genlmsg_parse matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether genlmsg_parse matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'genlmsg_parse' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENLMSG_PARSE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether genl_notify.*family matches in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether genl_notify.*family matches in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then grep 'genl_notify.*family' $KSRC/include/net/genetlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENL_NOTIFY_TAKES_FAMILY 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether genl_multicast_group has member id in $KSRC/include/net/genetlink.h" >&5 $as_echo_n "checking whether genl_multicast_group has member id in $KSRC/include/net/genetlink.h... " >&6; } if test -f $KSRC/include/net/genetlink.h; then awk '/genl_multicast_group.{/,/^}/' $KSRC/include/net/genetlink.h 2>/dev/null | grep 'id' >/dev/null status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENL_MULTICAST_GROUP_WITH_ID 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether geneve_hdr matches in $KSRC/include/net/geneve.h" >&5 $as_echo_n "checking whether geneve_hdr matches in $KSRC/include/net/geneve.h... " >&6; } if test -f $KSRC/include/net/geneve.h; then grep 'geneve_hdr' $KSRC/include/net/geneve.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GENEVE_HDR 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether gre_cisco_register matches in $KSRC/include/net/gre.h" >&5 $as_echo_n "checking whether gre_cisco_register matches in $KSRC/include/net/gre.h... " >&6; } if test -f $KSRC/include/net/gre.h; then grep 'gre_cisco_register' $KSRC/include/net/gre.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GRE_CISCO_REGISTER 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether gre_handle_offloads matches in $KSRC/include/net/gre.h" >&5 $as_echo_n "checking whether gre_handle_offloads matches in $KSRC/include/net/gre.h... " >&6; } if test -f $KSRC/include/net/gre.h; then grep 'gre_handle_offloads' $KSRC/include/net/gre.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_GRE_HANDLE_OFFLOADS 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether IP6_FH_F_SKIP_RH matches in $KSRC/include/net/ipv6.h" >&5 $as_echo_n "checking whether IP6_FH_F_SKIP_RH matches in $KSRC/include/net/ipv6.h... " >&6; } if test -f $KSRC/include/net/ipv6.h; then grep 'IP6_FH_F_SKIP_RH' $KSRC/include/net/ipv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP6_FH_F_SKIP_RH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ip6_local_out_sk matches in $KSRC/include/net/ipv6.h" >&5 $as_echo_n "checking whether ip6_local_out_sk matches in $KSRC/include/net/ipv6.h... " >&6; } if test -f $KSRC/include/net/ipv6.h; then grep 'ip6_local_out_sk' $KSRC/include/net/ipv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP6_LOCAL_OUT_SK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __ipv6_addr_jhash matches in $KSRC/include/net/ipv6.h" >&5 $as_echo_n "checking whether __ipv6_addr_jhash matches in $KSRC/include/net/ipv6.h... " >&6; } if test -f $KSRC/include/net/ipv6.h; then grep '__ipv6_addr_jhash' $KSRC/include/net/ipv6.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___IPV6_ADDR_JHASH 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether rt6i.*u.dst matches in $KSRC/include/net/ip6_fib.h" >&5 $as_echo_n "checking whether rt6i.*u.dst matches in $KSRC/include/net/ip6_fib.h... " >&6; } if test -f $KSRC/include/net/ip6_fib.h; then grep 'rt6i.*u.dst' $KSRC/include/net/ip6_fib.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RT6INFO_DST_UNION 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ip6_frag.*sock matches in $KSRC/include/net/ip6_route.h" >&5 $as_echo_n "checking whether ip6_frag.*sock matches in $KSRC/include/net/ip6_route.h... " >&6; } if test -f $KSRC/include/net/ip6_route.h; then grep 'ip6_frag.*sock' $KSRC/include/net/ip6_route.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IP_FRAGMENT_TAKES_SOCK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_get_be16 matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_get_be16 matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_get_be16' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_GET_BE16 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_put_be16 matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_put_be16 matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_put_be16' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_PUT_BE16 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_put_be32 matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_put_be32 matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_put_be32' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_PUT_BE32 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_put_be64 matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_put_be64 matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_put_be64' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_PUT_BE64 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_put_in_addr matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_put_in_addr matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_put_in_addr' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_PUT_IN_ADDR 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_find_nested matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_find_nested matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_find_nested' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_FIND_NESTED 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether nla_is_last matches in $KSRC/include/net/netlink.h" >&5 $as_echo_n "checking whether nla_is_last matches in $KSRC/include/net/netlink.h... " >&6; } if test -f $KSRC/include/net/netlink.h; then grep 'nla_is_last' $KSRC/include/net/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NLA_IS_LAST 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether void.*netlink_set_err matches in $KSRC/include/linux/netlink.h" >&5 $as_echo_n "checking whether void.*netlink_set_err matches in $KSRC/include/linux/netlink.h... " >&6; } if test -f $KSRC/include/linux/netlink.h; then grep 'void.*netlink_set_err' $KSRC/include/linux/netlink.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VOID_NETLINK_SET_ERR 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sctp_compute_cksum matches in $KSRC/include/net/sctp/checksum.h" >&5 $as_echo_n "checking whether sctp_compute_cksum matches in $KSRC/include/net/sctp/checksum.h... " >&6; } if test -f $KSRC/include/net/sctp/checksum.h; then grep 'sctp_compute_cksum' $KSRC/include/net/sctp/checksum.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_SCTP_COMPUTE_CKSUM 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ADD_ALL_VLANS_CMD matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether ADD_ALL_VLANS_CMD matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep 'ADD_ALL_VLANS_CMD' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VLAN_BUG_WORKAROUND 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vlan_insert_tag_set_proto matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether vlan_insert_tag_set_proto matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep 'vlan_insert_tag_set_proto' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VLAN_INSERT_TAG_SET_PROTO 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __vlan_insert_tag matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether __vlan_insert_tag matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep '__vlan_insert_tag' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE___VLAN_INSERT_TAG 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether vlan_get_protocol matches in $KSRC/include/linux/if_vlan.h" >&5 $as_echo_n "checking whether vlan_get_protocol matches in $KSRC/include/linux/if_vlan.h... " >&6; } if test -f $KSRC/include/linux/if_vlan.h; then grep 'vlan_get_protocol' $KSRC/include/linux/if_vlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VLAN_GET_PROTOCOL 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether u64_stats_fetch_begin_irq matches in $KSRC/include/linux/u64_stats_sync.h" >&5 $as_echo_n "checking whether u64_stats_fetch_begin_irq matches in $KSRC/include/linux/u64_stats_sync.h... " >&6; } if test -f $KSRC/include/linux/u64_stats_sync.h; then grep 'u64_stats_fetch_begin_irq' $KSRC/include/linux/u64_stats_sync.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_U64_STATS_FETCH_BEGIN_IRQ 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether openvswitch_handle_frame_hook matches in $KSRC/include/linux/openvswitch.h" >&5 $as_echo_n "checking whether openvswitch_handle_frame_hook matches in $KSRC/include/linux/openvswitch.h... " >&6; } if test -f $KSRC/include/linux/openvswitch.h; then grep 'openvswitch_handle_frame_hook' $KSRC/include/linux/openvswitch.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RHEL_OVS_HOOK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct vxlan_metadata matches in $KSRC/include/net/vxlan.h" >&5 $as_echo_n "checking whether struct vxlan_metadata matches in $KSRC/include/net/vxlan.h... " >&6; } if test -f $KSRC/include/net/vxlan.h; then grep 'struct vxlan_metadata' $KSRC/include/net/vxlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VXLAN_METADATA 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether VXLAN_HF_RCO matches in $KSRC/include/net/vxlan.h" >&5 $as_echo_n "checking whether VXLAN_HF_RCO matches in $KSRC/include/net/vxlan.h... " >&6; } if test -f $KSRC/include/net/vxlan.h; then grep 'VXLAN_HF_RCO' $KSRC/include/net/vxlan.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_VXLAN_HF_RCO 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether udp_flow_src_port matches in $KSRC/include/net/udp.h" >&5 $as_echo_n "checking whether udp_flow_src_port matches in $KSRC/include/net/udp.h... " >&6; } if test -f $KSRC/include/net/udp.h; then grep 'udp_flow_src_port' $KSRC/include/net/udp.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether inet_get_local_port_range(net matches in $KSRC/include/net/udp.h" >&5 $as_echo_n "checking whether inet_get_local_port_range(net matches in $KSRC/include/net/udp.h... " >&6; } if test -f $KSRC/include/net/udp.h; then grep 'inet_get_local_port_range(net' $KSRC/include/net/udp.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_FLOW_SRC_PORT 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether udp_v4_check matches in $KSRC/include/net/udp.h" >&5 $as_echo_n "checking whether udp_v4_check matches in $KSRC/include/net/udp.h... " >&6; } if test -f $KSRC/include/net/udp.h; then grep 'udp_v4_check' $KSRC/include/net/udp.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_V4_CHECK 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether udp_tunnel_gro_complete matches in $KSRC/include/net/udp_tunnel.h" >&5 $as_echo_n "checking whether udp_tunnel_gro_complete matches in $KSRC/include/net/udp_tunnel.h... " >&6; } if test -f $KSRC/include/net/udp_tunnel.h; then grep 'udp_tunnel_gro_complete' $KSRC/include/net/udp_tunnel.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_TUNNEL_GRO_COMPLETE 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ipv6_v6only matches in $KSRC/include/net/udp_tunnel.h" >&5 $as_echo_n "checking whether ipv6_v6only matches in $KSRC/include/net/udp_tunnel.h... " >&6; } if test -f $KSRC/include/net/udp_tunnel.h; then grep 'ipv6_v6only' $KSRC/include/net/udp_tunnel.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_UDP_TUNNEL_IPV6 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ignore_df matches in $KSRC/include/linux/skbuff.h" >&5 $as_echo_n "checking whether ignore_df matches in $KSRC/include/linux/skbuff.h... " >&6; } if test -f $KSRC/include/linux/skbuff.h; then grep 'ignore_df' $KSRC/include/linux/skbuff.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_IGNORE_DF_RENAME 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NET_NAME_UNKNOWN matches in $KSRC/include/uapi/linux/netdevice.h" >&5 $as_echo_n "checking whether NET_NAME_UNKNOWN matches in $KSRC/include/uapi/linux/netdevice.h... " >&6; } if test -f $KSRC/include/uapi/linux/netdevice.h; then grep 'NET_NAME_UNKNOWN' $KSRC/include/uapi/linux/netdevice.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_NET_NAME_UNKNOWN 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether el6 matches in $KSRC/include/linux/utsrelease.h" >&5 $as_echo_n "checking whether el6 matches in $KSRC/include/linux/utsrelease.h... " >&6; } if test -f $KSRC/include/linux/utsrelease.h; then grep 'el6' $KSRC/include/linux/utsrelease.h >/dev/null 2>&1 status=$? case $status in 0) { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } echo '#define HAVE_RHEL6_PER_CPU 1' >> datapath/linux/kcompat.h.new ;; 1) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; *) as_fn_error $? "grep exited with status $status" "$LINENO" 5 ;; esac else { $as_echo "$as_me:${as_lineno-$LINENO}: result: file not found" >&5 $as_echo "file not found" >&6; } fi if test "$version" = 4; then echo '#define OVS_FRAGMENT_BACKPORT 1' >> datapath/linux/kcompat.h.new elif test "$version" = 3 && test "$patchlevel" -ge 10; then echo '#define OVS_FRAGMENT_BACKPORT 1' >> datapath/linux/kcompat.h.new fi if cmp -s datapath/linux/kcompat.h.new \ datapath/linux/kcompat.h >/dev/null 2>&1; then rm datapath/linux/kcompat.h.new else mv datapath/linux/kcompat.h.new datapath/linux/kcompat.h fi fi if test -n "$KBUILD"; then LINUX_ENABLED_TRUE= LINUX_ENABLED_FALSE='#' else LINUX_ENABLED_TRUE='#' LINUX_ENABLED_FALSE= fi # Check whether --with-dpdk was given. if test "${with_dpdk+set}" = set; then : withval=$with_dpdk; fi if test X"$with_dpdk" != X; then RTE_SDK=$with_dpdk DPDK_INCLUDE=$RTE_SDK/include DPDK_LIB_DIR=$RTE_SDK/lib DPDK_LIB="-ldpdk" DPDK_EXTRA_LIB="" RTE_SDK_FULL=`readlink -f $RTE_SDK` cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$RTE_SDK_FULL/include/rte_config.h> #if !RTE_LIBRTE_VHOST_USER #error #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else $as_echo "#define VHOST_CUSE 1" >>confdefs.h DPDK_EXTRA_LIB="-lfuse" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ovs_save_CFLAGS="$CFLAGS" ovs_save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -L$DPDK_LIB_DIR" CFLAGS="$CFLAGS -I$DPDK_INCLUDE" # On some systems we have to add -ldl to link with dpdk # # This code, at first, tries to link without -ldl (""), # then adds it and tries again. # Before each attempt the search cache must be unset, # otherwise autoconf will stick with the old result found=false save_LIBS=$LIBS for extras in "" "-ldl"; do LIBS="$DPDK_LIB $extras $save_LIBS $DPDK_EXTRA_LIB" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { int rte_argc; char ** rte_argv; rte_eal_init(rte_argc, rte_argv); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : found=true fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if $found; then break fi done if $found; then :; else as_fn_error $? "cannot link with dpdk" "$LINENO" 5 fi CFLAGS="$ovs_save_CFLAGS" LDFLAGS="$ovs_save_LDFLAGS" OVS_LDFLAGS="$OVS_LDFLAGS -L$DPDK_LIB_DIR" OVS_CFLAGS="$OVS_CFLAGS -I$DPDK_INCLUDE" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -mssse3" >&5 $as_echo_n "checking whether $CC accepts -mssse3... " >&6; } if ${ovs_cv__mssse3+:} false; then : $as_echo_n "(cached) " >&6 else ovs_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR -mssse3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if test -s conftest.err && grep "unrecognized option" conftest.err; then ovs_cv__mssse3=no; else ovs_cv__mssse3=yes; fi else ovs_cv__mssse3=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$ovs_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ovs_cv__mssse3" >&5 $as_echo "$ovs_cv__mssse3" >&6; } if test $ovs_cv__mssse3 = yes; then WARNING_FLAGS="$WARNING_FLAGS -mssse3" else : fi # DPDK pmd drivers are not linked unless --whole-archive is used. # # This happens because the rest of the DPDK code doesn't use any symbol in # the pmd driver objects, and the drivers register themselves using an # __attribute__((constructor)) function. # # These options are specified inside a single -Wl directive to prevent # autotools from reordering them. DPDK_vswitchd_LDFLAGS=-Wl,--whole-archive,$DPDK_LIB,--no-whole-archive $as_echo "#define DPDK_NETDEV 1" >>confdefs.h else RTE_SDK= fi if test -n "$RTE_SDK"; then DPDK_NETDEV_TRUE= DPDK_NETDEV_FALSE='#' else DPDK_NETDEV_TRUE='#' DPDK_NETDEV_FALSE= fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ _Pragma("message(\"Checking for pragma message\")") int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define HAVE_PRAGMA_MESSAGE 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_config_files="$ac_config_files Makefile" ac_config_files="$ac_config_files datapath/Makefile" ac_config_files="$ac_config_files datapath/linux/Kbuild" ac_config_files="$ac_config_files datapath/linux/Makefile" ac_config_files="$ac_config_files datapath/linux/Makefile.main" ac_config_files="$ac_config_files tests/atlocal" ac_config_files="$ac_config_files lib/libopenvswitch.pc" ac_config_files="$ac_config_files lib/libsflow.pc" ac_config_files="$ac_config_files ofproto/libofproto.pc" ac_config_files="$ac_config_files ovsdb/libovsdb.pc" ac_config_files="$ac_config_files include/openvswitch/version.h" ac_config_commands="$ac_config_commands include/openflow/openflow.h.stamp" ac_config_commands="$ac_config_commands utilities/bugtool/dummy" ac_config_commands="$ac_config_commands ovn/dummy" ac_config_commands="$ac_config_commands ovn/utilities/dummy" # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ESX_TRUE}" && test -z "${ESX_FALSE}"; then as_fn_error $? "conditional \"ESX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then as_fn_error $? "conditional \"WIN32\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VSTUDIO_DDK_TRUE}" && test -z "${VSTUDIO_DDK_FALSE}"; then as_fn_error $? "conditional \"VSTUDIO_DDK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NDEBUG_TRUE}" && test -z "${NDEBUG_FALSE}"; then as_fn_error $? "conditional \"NDEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_NETLINK_TRUE}" && test -z "${HAVE_NETLINK_FALSE}"; then as_fn_error $? "conditional \"HAVE_NETLINK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_OPENSSL_TRUE}" && test -z "${HAVE_OPENSSL_FALSE}"; then as_fn_error $? "conditional \"HAVE_OPENSSL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_LIBCAPNG_TRUE}" && test -z "${HAVE_LIBCAPNG_FALSE}"; then as_fn_error $? "conditional \"HAVE_LIBCAPNG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_PYTHON_TRUE}" && test -z "${HAVE_PYTHON_FALSE}"; then as_fn_error $? "conditional \"HAVE_PYTHON\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_DOT_TRUE}" && test -z "${HAVE_DOT_FALSE}"; then as_fn_error $? "conditional \"HAVE_DOT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_IF_PACKET_TRUE}" && test -z "${HAVE_IF_PACKET_FALSE}"; then as_fn_error $? "conditional \"HAVE_IF_PACKET\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_IF_DL_TRUE}" && test -z "${HAVE_IF_DL_FALSE}"; then as_fn_error $? "conditional \"HAVE_IF_DL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_GROFF_TRUE}" && test -z "${HAVE_GROFF_FALSE}"; then as_fn_error $? "conditional \"HAVE_GROFF\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${GNU_MAKE_TRUE}" && test -z "${GNU_MAKE_FALSE}"; then as_fn_error $? "conditional \"GNU_MAKE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_POSIX_AIO_TRUE}" && test -z "${HAVE_POSIX_AIO_FALSE}"; then as_fn_error $? "conditional \"HAVE_POSIX_AIO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LINUX_TRUE}" && test -z "${LINUX_FALSE}"; then as_fn_error $? "conditional \"LINUX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_WNO_UNUSED_TRUE}" && test -z "${HAVE_WNO_UNUSED_FALSE}"; then as_fn_error $? "conditional \"HAVE_WNO_UNUSED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_WNO_UNUSED_PARAMETER_TRUE}" && test -z "${HAVE_WNO_UNUSED_PARAMETER_FALSE}"; then as_fn_error $? "conditional \"HAVE_WNO_UNUSED_PARAMETER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test "X$enable_Werror" = Xyes; then OVS_CFLAGS="$OVS_CFLAGS -Werror" fi if test $ovs_cv_gnu_make_if = yes; then CC='$(if $(C),env REAL_CC="'"$CC"'" CHECK="$(SPARSE) -I $(top_srcdir)/include/sparse $(SPARSEFLAGS) $(SPARSE_EXTRA_INCLUDES) " cgcc $(CGCCFLAGS),'"$CC"')' fi if test -z "${LINUX_ENABLED_TRUE}" && test -z "${LINUX_ENABLED_FALSE}"; then as_fn_error $? "conditional \"LINUX_ENABLED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DPDK_NETDEV_TRUE}" && test -z "${DPDK_NETDEV_FALSE}"; then as_fn_error $? "conditional \"DPDK_NETDEV\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by openvswitch $as_me 2.5.9, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ openvswitch config.status 2.5.9 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "tests/atconfig") CONFIG_COMMANDS="$CONFIG_COMMANDS tests/atconfig" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "lib/stdio.h") CONFIG_FILES="$CONFIG_FILES lib/stdio.h" ;; "lib/string.h") CONFIG_FILES="$CONFIG_FILES lib/string.h" ;; "ovsdb/libovsdb.sym") CONFIG_FILES="$CONFIG_FILES ovsdb/libovsdb.sym" ;; "ofproto/libofproto.sym") CONFIG_FILES="$CONFIG_FILES ofproto/libofproto.sym" ;; "lib/libsflow.sym") CONFIG_FILES="$CONFIG_FILES lib/libsflow.sym" ;; "lib/libopenvswitch.sym") CONFIG_FILES="$CONFIG_FILES lib/libopenvswitch.sym" ;; "ovn/lib/libovn.sym") CONFIG_FILES="$CONFIG_FILES ovn/lib/libovn.sym" ;; "vtep/libvtep.sym") CONFIG_FILES="$CONFIG_FILES vtep/libvtep.sym" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "datapath/Makefile") CONFIG_FILES="$CONFIG_FILES datapath/Makefile" ;; "datapath/linux/Kbuild") CONFIG_FILES="$CONFIG_FILES datapath/linux/Kbuild" ;; "datapath/linux/Makefile") CONFIG_FILES="$CONFIG_FILES datapath/linux/Makefile" ;; "datapath/linux/Makefile.main") CONFIG_FILES="$CONFIG_FILES datapath/linux/Makefile.main" ;; "tests/atlocal") CONFIG_FILES="$CONFIG_FILES tests/atlocal" ;; "lib/libopenvswitch.pc") CONFIG_FILES="$CONFIG_FILES lib/libopenvswitch.pc" ;; "lib/libsflow.pc") CONFIG_FILES="$CONFIG_FILES lib/libsflow.pc" ;; "ofproto/libofproto.pc") CONFIG_FILES="$CONFIG_FILES ofproto/libofproto.pc" ;; "ovsdb/libovsdb.pc") CONFIG_FILES="$CONFIG_FILES ovsdb/libovsdb.pc" ;; "include/openvswitch/version.h") CONFIG_FILES="$CONFIG_FILES include/openvswitch/version.h" ;; "include/openflow/openflow.h.stamp") CONFIG_COMMANDS="$CONFIG_COMMANDS include/openflow/openflow.h.stamp" ;; "utilities/bugtool/dummy") CONFIG_COMMANDS="$CONFIG_COMMANDS utilities/bugtool/dummy" ;; "ovn/dummy") CONFIG_COMMANDS="$CONFIG_COMMANDS ovn/dummy" ;; "ovn/utilities/dummy") CONFIG_COMMANDS="$CONFIG_COMMANDS ovn/utilities/dummy" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "tests/atconfig":C) cat >tests/atconfig </dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # The names of the tagged configurations supported by this script. available_tags='' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ;; "utilities/bugtool/dummy":C) : ;; "ovn/dummy":C) : ;; "ovn/utilities/dummy":C) : ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi openvswitch-2.5.9/PaxHeaders.82075/Vagrantfile0000644000000000000000000000013213534540071016031 xustar0030 mtime=1567801401.193679642 30 atime=1567801402.045685899 30 ctime=1567801423.785846083 openvswitch-2.5.9/Vagrantfile0000644000175000017500000000404413534540071017521 0ustar00jpettitjpettit00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.require_version ">=1.7.0" $bootstrap_fedora = <
    $namePDF, HTML, plain text